// Copyright (c) 1993-2005 "Компания БЭСТ". Все права сохраняются.

// Функции осуществляющие поддержку PLUG INS
#include "s_public.ch"
#include "inkey.ch"
#include "my.ch"
#include "plugins.ch"
#include "fileio.ch"
STATIC aSystemCode := {""}
STATIC AExpFile := {}
STATIC aExpFunction := {}
STATIC aExpInterface := {}
STATIC nErrorStr := 0
//--------------------------------------------------------------------------//
 FUNCTION IsPlugIns(cProDir,cDbfDir,aFilePlugIns,nGetDir,cGlobalTask,lDelFrm)
//--------------------------------------------------------------------------//
// Проверяет признак наличия PLUG INS и
// возвращает массив aFilePlugIns по ссылке
// cProDir - путь к директории PRO
// cDbfDir - путь к директории DBASE
// nGetDir - статус определяет поиск исполняемых файлов
//           ALL_PLUG_IN        - все в cProDir
//           ALL_DBF_PLUG_IN    - все cProDir и cDbfDir
//           ALL_PLUG_IN_EXE    - все cProDir для запуска (по умолчанию)
//           ALL_PLUG_IN_PRINT  - все cProDir для печати
//           TASK_PLUG_IN_EXE   - все cProDir с учетом АРМ'а для запуска
//           TASK_PLUG_IN_PRINT - все cProDir с учетом АРМ'а для печати
//           TASK_PLUG_IN       - все cProDir с учетом АРМ'а для запуска и печати
// cGlobalTask - GlobalTask (по умолчанию GlobalTask)
// lDelFrm   - если .T., то не учитываются *.frm, по умолчанию .F.

LOCAL cPathPlugIns := cProDir+'PLUGINS\'
LOCAL cPathDbfPlugIns := cDbfDir
LOCAL bDelPPO := {|aFile| "PPO.DBF" $ UPPER(aFile[1]) .OR. "PPO.CDX" $ UPPER(aFile[1]) .OR. "PPO.FPT" $ UPPER(aFile[1]).OR. ".SCC" $ UPPER(aFile[1]).OR. ".HRB" $ UPPER(aFile[1]) }
LOCAL bRun,bDel := IF(!EMPTY(lDelFrm),{|aFile| ".FRM" $ UPPER(aFile[1]) .OR. EVAL(bDelPPO,aFile) },bDelPPO)
IF(EMPTY(nGetDir),nGetDir := ALL_PLUG_IN_EXE,NIL)
IF(EMPTY(cGlobalTask),cGlobalTask := GlobalTask,NIL)
aFilePlugIns := {}
IF nGetDir = ALL_PLUG_IN
  AEVAL(AllDirFile(cProDir+'PLUGINS\',"*.*",bDel,bRun),{|x| AADD(aFilePlugIns,cProDir+'PLUGINS\'+x) })
ELSEIF nGetDir = ALL_DBF_PLUG_IN
  AEVAL(AllDirFile(cProDir+'PLUGINS\',"*.*",bDel,bRun),{|x| AADD(aFilePlugIns,cProDir+'PLUGINS\'+x) })
  AEVAL(AllDirFile(cDbfDir,"*.scr",bDel,bRun),{|x| AADD(aFilePlugIns,cDbfDir+x) })
ELSEIF nGetDir = ALL_PLUG_IN_EXE
  AEVAL(AllDirFile(cProDir+'PLUGINS\EXTENSNS\',"*.*",bDel,bRun),{|x| AADD(aFilePlugIns,cProDir+'PLUGINS\EXTENSNS\'+x) })
ELSEIF nGetDir = ALL_PLUG_IN_PRINT
  AEVAL(AllDirFile(cProDir+'PLUGINS\PRINT\',"*.*",bDel,bRun),{|x| AADD(aFilePlugIns,cProDir+'PLUGINS\PRINT\'+x) })
ELSEIF nGetDir = TASK_PLUG_IN_EXE
  AEVAL(AllDirFile(cProDir+'PLUGINS\EXTENSNS\'+GetAllArm(NIL,EXENAME())[6],"*.*",bDel,bRun),{|x| AADD(aFilePlugIns,cProDir+'PLUGINS\EXTENSNS\'+GetAllArm(NIL,EXENAME())[6]+x) })
ELSEIF nGetDir = TASK_PLUG_IN_PRINT
  AEVAL(AllDirFile(cProDir+'PLUGINS\PRINT\'+GetAllArm(NIL,EXENAME())[6],"*.*",bDel,bRun),{|x| AADD(aFilePlugIns,cProDir+'PLUGINS\PRINT\'+GetAllArm(NIL,EXENAME())[6]+x) })
ELSEIF nGetDir = TASK_PLUG_IN
  AEVAL(AllDirFile(cProDir+'PLUGINS\EXTENSNS\',"*.*",{|aFile,cOnlyPath| IF(bDel != NIL,EVAL(bDel,aFile,cOnlyPath),.T.) .OR. !EMPTY(cOnlyPath) },bRun),{|x| AADD(aFilePlugIns,cProDir+'PLUGINS\EXTENSNS\'+x) })
  AEVAL(AllDirFile(cProDir+'PLUGINS\EXTENSNS\'+GetAllArm(NIL,EXENAME())[6],"*.*",bDel,bRun),{|x| AADD(aFilePlugIns,cProDir+'PLUGINS\EXTENSNS\'+GetAllArm(NIL,EXENAME())[6]+x) })
ENDIF

RETURN !EMPTY(aFilePlugIns)
//--------------------------------------------------------------------------//
STATIC FUNCTION IsValidPlugIns(cFile)
//--------------------------------------------------------------------------//
// Проверка корректности файла PlugIns'а
LOCAL lValid := .T.
LOCAL oErr, aError := {}
LOCAL BErr := ErrorBlock({|oErr| Break(oErr)})
LOCAL aStrExpFile := {},aStrExpFunc  := {}
IF !(VALTYPE(cFile)=="C".AND.LEN(cFile)>4.AND.UPPER(RIGHT(cFile,4))==".HRB")
   BEGIN SEQUENCE
     GenCodePlugIn(cFile,@aStrExpFile,@aStrExpFunc,@aError)
     CompCodePlugIn(cFile,@aStrExpFile,@aStrExpFunc)
   RECOVER USING oErr
     lValid := .F.
   END SEQUENCE
ENDIF
ErrorBlock(BErr)
RETURN lValid .AND. EMPTY(aError)

STATIC PROCEDURE WriteLog(cText, lNoLine)
 LOCAL nHandle
 Local cFile :="C:\best4log.txt"


 IF FILE(cFile)
   nHandle := FOpen(cFile, FO_WRITE+FO_SHARED )
 ELSE
   nHandle := FCreate( cFile, FC_NORMAL )
 ENDIF


   FSeek( nHandle, 0 , FS_END )
   IF ValType(cText) != "C"
     cText:=""
   ENDIF
   IF lNoLine == NIL
    cText := ProcName(1)             + "(" + ;
           LTrim(Str(ProcLine(1))) + ")" + ;
           ProcName(2)             + "(" + ;
           LTrim(Str(ProcLine(2))) + ")" + ;
           cText
    ENDIF

   FWrite( nHandle,cText + CHR(13)+CHR(10) )


   IF ! FClose( nHandle )
   ENDIF



RETURN
//--------------------------------------------------------------------------//
           PROCEDURE UpgradePlugIns(cNewProDir,cNewDbfDir,cBakProDir,cBakDbfDir)
//--------------------------------------------------------------------------//
// Выполняет UPGRADE исполняемых файлов с созданием новых директорий
LOCAL cGetCopy,cSetCopy
LOCAL aFilePlugIns
LOCAL bGetFile := {|_cGetCopy,_cSetCopy|;
  cGetCopy := _cGetCopy,cSetCopy := _cSetCopy,;
  WriteLog(cGetCopy),;
  AEVAL(aFilePlugIns,{|x,n|WriteLog(cGetCopy), IF(x != NIL .AND. cGetCopy $ x,(IF(IsValidPlugIns(x).AND.UPPER(x[1])=="U",PlugInsCopy(x,cSetCopy+TOKEN(x,":\")),NIL),aFilePlugIns[n] := NIL),NIL) });
                   }
LOCAL aPathArm := {}, aArm := {}

IsPlugIns(cBakProDir,cBakDbfDir,@aFilePlugIns,ALL_DBF_PLUG_IN,GlobalTask,.T.)
AEVAL(GetAllArm(),{|x| IF(!EMPTY(x[6]) .AND. ASCAN(aArm,x[6]) = 0,AADD(aArm,x[6]),NIL) })
AEVAL(aArm,{|x| AADD(aPathArm,{cBakDbfDir+x,cNewProDir+"PLUGINS\PRINT\"+x}) })
AEVAL(aArm,{|x| AADD(aPathArm,{cBakProDir+"PLUGINS\PRINT\"+x,cNewProDir+"PLUGINS\PRINT\"+x}) })
AEVAL(aArm,{|x| AADD(aPathArm,{cBakProDir+"PLUGINS\EXTENSNS\"+x,cNewProDir+"PLUGINS\EXTENSNS\"+x}) })
AADD(aPathArm,{cBakDbfDir+"INI\",cNewProDir+"PLUGINS\PRINT\"})
// Lexx
AADD(aPathArm,{cBakProDir+"PLUGINS\PRINT\",   cNewProDir+"PLUGINS\PRINT\"})
AADD(aPathArm,{cBakProDir+"PLUGINS\EXTENSNS\",cNewProDir+"PLUGINS\EXTENSNS\"})

AEVAL(aArm,{|x|  MakeDir(cNewProDir+"PLUGINS\PRINT\"+x),MakeDir(cNewProDir+"PLUGINS\EXTENSNS\"+x) })
AEVAL(aPathArm,{|x| EVAL(bGetFile,x[1],x[2]) })
RETURN
//--------------------------------------------------------------------------//
        PROCEDURE PlugInsCopy( cOldFile, cNewFile )
//--------------------------------------------------------------------------//
// Копирование
LOCAL BErr := ErrorBlock({|oErr| Break(oErr)})
  IF FILE(cOldFile)
    IF FILE(cNewFile)
        IF  ALLTRIM(UPPER(cOldFile))==ALLTRIM(UPPER(cNewFile)).AND.;
            !YesOrNo({;
                        "Каталог PLUGINS уже содержит файл "+SUBSTR(ALLTRIM(cNewFile),RAT("\",ALLTRIM(cNewFile))+1),;
                        "Заменить имеющийся файл",;
                        "(дата изменения "+DTOC(FILEDATE(cOldFile))+FILETIME(cOldFile)+")",;
                        "новым файлом?",;
                        "(дата изменения "+DTOC(FILEDATE(cNewFile))+FILETIME(cNewFile)+")";
                    })

            RETURN
        ENDIF
      FERASE(cNewFile)
    ENDIF

    BEGIN SEQUENCE
      COPY FILE (cOldFile) TO (cNewFile)
    RECOVER USING oErr

    END SEQUENCE

  ENDIF
  ErrorBlock(BErr)
RETURN

/*
//--------------------------------------------------------------------------//
                     FUNCTION MakeDir(cPath)
//--------------------------------------------------------------------------//
// Создаю диpектоpий (последовательно по кусочку)
  LOCAL lRez := .T., cTmpDir := '', nPos, nRez
  cPath:=LOWER(cPath)
  IF LEFT( cPath, 1 ) == '\'
    cTmpDir := '\'
    cPath   := SUBSTR( cPath, 2 )
  ENDIF
  do while (nPos := AT( '\', cPath )) <> 0
    cTmpDir += SUBSTR( cPath, 1, nPos-1 )
    cPath   := SUBSTR( cPath, nPos+1  )
    nRez := DIRMAKE(LOWER(cTmpDir) )
    IF nRez  <> 0 .AND. nRez <> -5
      lRez := .F.
      EXIT
    ENDIF
    cTmpDir += '\'
  enddo
  IF( lRez .AND. !EMPTY(cPath), lRez := .F., )
RETURN(lRez)
*/

//--------------------------------------------------------------------------//
               PROCEDURE CompilePlugIns()
//--------------------------------------------------------------------------//
// Выполняет компиляцию PlugIns
LOCAL aFilePlugIns
BEGIN SEQUENCE
IF IsPlugIns(_PRO_PATH,LoadPath(),@aFilePlugIns,TASK_PLUG_IN,GlobalTask)
  AEVAL(aFilePlugIns,{|cFile| FileEval(cFile,,.T.) })
ENDIF
END SEQUENCE
RETURN
//--------------------------------------------------------------------------//
      FUNCTION IsRunPlugIns(cObject,cEvent)
//--------------------------------------------------------------------------//
// Проверяет существует ли PlugIns для данного объекта
RETURN RunPlugIns(cObject,cEvent,,,,,,,,,,,.T.)
//--------------------------------------------------------------------------//
      FUNCTION RunPlugIns(cObject,cEvent,xParam1,xParam2,xParam3,xParam4,xParam5,xParam6,xParam7,xParam8,xParam9,xParam10,lNoRun)
//--------------------------------------------------------------------------//
// Выполняет запуск PlugIns
// cObject - код объекта
// cEvent  - код события
// xParam? - параметры передаваемые в вызываемые функции
//
// INTERFACE OBJECT АРМ EVENT ВХОД BIND 12.1
// INTERFACE OBJECT xxxxxx EVENT xxxxxx BIND xxxxxxx
//
// Объекты    События          Передаваемые параметры
//────────────────────────────────────────────────────────────────────────────
// АРМ
//  +       ВХОД
//  +       ВЫХОД
//  +       ФОРМИРОВАНИЕ_МЕНЮ
//                            МАССИВ МЕНЮ
// РЕЕСТР ДОКУМЕТОВ
// +        ВХОД
//                            КОД ФИЛЬТРАЦИИ
//                            МАССИВ ДЛЯ ЗАГОЛОВА
//                            МАССИВ ДЛЯ ПРОРИСОВКИ КОЛОНОК
// +        ВЫХОД
// +        НАЖАТИЕ_КЛАВИШИ
//                            КОД НАЖАТОЙ КЛАВИШИ
// +        ПЕЧАТЬ
//                           ОРИГИНАЛЬНЫЙ ОБРАБОТЧИК ПЕЧАТИ
//
// ДОКУМЕНТ
// +        ВХОД
//                           РЕЖИМ РАБОТЫ ДОКУМЕНТА
//                           ФЛАГ ЗАВЕРШЕНИЯ ВВОДА
// +        ВЫХОД_ПЕРЕД_СОХРАНЕНИЕМ
//                           РЕЖИМ РАБОТЫ ДОКУМЕНТА
//                           ФЛАГ ИЗМЕНЕНИЯ ДАННЫХ
//                           КЛАВИША ВЫХОДА
//                           обработчик должен возвратить логическое значение
//                           разрешающее (.T.) или запрещеющее (.F.)
//                           выход из редактирования
// +        ВЫХОД_ПОСЛЕ_СОХРАНЕНИЯ
//                           РЕЖИМ РАБОТЫ ДОКУМЕНТА
// +        ПЕЧАТЬ
//                           ОРИГИНАЛЬНЫЙ ОБРАБОТЧИК ПЕЧАТИ
// +        ВЫХОД_ИЗ_ПОЛЯ
//                           ОРИГИНАЛЬНЫЙ ОБРАБОТЧИК ИЗМЕНЕНИЯ
//                           ЗНАЧЕНИЕ
//                           GET ОБЪЕКТ
//                           НОМЕР РЕДАКТИРУЕМОГО ЭЛЕМЕНТА
//                           ПРИЗНАК ПЕРВОЙ ПРОРИСОВКИ ДОКУМЕНТА
//                           обработчик должет возвратить значение для прорисовки
// +        ВЫХОД_ИЗ_ПОЛЯ
//                           ОРИГИНАЛЬНЫЙ ОБРАБОТЧИК ИЗМЕНЕНИЯ
//                           GET ОБЪЕКТ
//                           НОМЕР РЕДАКТИРУЕМОГО ЭЛЕМЕНТА
//                           обработчик должет возвратить признак разрешения выхода
//
// ОТЧЕТ
// +        ВХОД
//                           ИМЯ ФАЙЛА
// +        ФОРМИРОВАНИЕ
//                           ОРИГИНАЛЬНЫЙ ОБРАБОТЧИК ВЫВОДА
//                           СТРОКА ДЛЯ ВЫВОДА
//                           следует выполнить вывод строки посредством
//                           вызова оригинального обработчика спередачей ему
//                           обработанной строки
// +        ВЫХОД
//                           ИМЯ ФАЙЛА
// РАСЧЕТ
//          НАЧАЛО
//          РАБОТА
//          ЗАВЕРШЕНИЕ
// BROWSE
// +        ВХОД
// +        ВЫХОД
// +        СМЕЩЕНИЕ
//───────────────────────────────────────────────────────────────────────────
LOCAL nPoz, aPozNil := {NIL,NIL,NIL}, cCode, xRet := .F.

PRIVATE lFromMaker:=.T.

nPoz := ASCAN(aExpInterface,{|x| x[1] == cObject .OR. (x[1] = NIL .AND. (aPozNil[1] := x[2],.F.)) })
IF nPoz != 0
  aPozNil[1] := aExpInterface[nPoz,2]
ENDIF
IF aPozNil[1] != NIL
  nPoz := ASCAN(aPozNil[1],{|x| x[1] == cEvent .OR. (x[1] = NIL .AND. (aPozNil[2] := x[2],.F.)) })
ENDIF
IF nPoz != 0
  aPozNil[2] := aPozNil[1][nPoz,2]
ENDIF
IF aPozNil[2] != NIL
  IF cObject = "АРМ"
    cCode := GetAllArm(NIL,EXENAME())[7]
  ENDIF
  nPoz := ASCAN(aPozNil[2],{|x| x[1] == HELP_CODE .OR. x[1] == GetPlugIn() .OR. x[1] == cCode .OR. (x[1] = NIL .AND. (aPozNil[3] := x[2],.F.)) })
ENDIF
IF nPoz != 0
  aPozNil[3] := aPozNil[2][nPoz,2]
ENDIF
IF aPozNil[3] != NIL
  IF !EMPTY(lNoRun)
    xRet := .T.
  ELSE
    AEVAL(aPozNil[3],{|x| xRet := EvalFun(,@xParam1,@xParam2,@xParam3,@xParam4,@xParam5,@xParam6,@xParam7,@xParam8,@xParam9,@xParam10,x) })
  ENDIF
ENDIF

RETURN xRet
//--------------------------------------------------------------------------//
              FUNCTION GetPlugIn()
//--------------------------------------------------------------------------//
// Возвращает код текущего PlugIn'а
RETURN ATAIL(aSystemCode)
//--------------------------------------------------------------------------//
              PROCEDURE PushPlugIn(cCode)
//--------------------------------------------------------------------------//
// Устанавливает код текщего PlugIn'а
AADD(aSystemCode,cCode)
RETURN
//--------------------------------------------------------------------------//
              PROCEDURE PopPlugIn()
//--------------------------------------------------------------------------//
// Устанавливает код текщего PlugIn'а
ASIZE(aSystemCode,LEN(aSystemCode)-1)
RETURN
//--------------------------------------------------------------------------//
                            FUNCTION EvalFun
//--------------------------------------------------------------------------//
// Выполнение пользовательских функций
  PARAMETERS cNameFunction, cPar1, cPar2, cPar3, cPar4, cPar5,;
                                      cPar6, cPar7, cPar8, cPar9, cPar10,nF
#ifndef __HARBOUR__

  MEMVAR xRet,nF,nI,cNameFunction, cPar1, cPar2, cPar3, cPar4, cPar5,;
                                      cPar6, cPar7, cPar8, cPar9, cPar10,;
                                      cParam
#endif

  PRIVATE xRet:=0, nI,cParam

  IF(nF = NIL,(cNameFunction := UPPER(cNameFunction),nF:= ASCAN( aExpFunction, {|x|x[1]==cNameFunction})),NIL)
  IF nF == 0
    SayError( {'Функция: '+cNameFunction,'Не определена!'})
  ELSE
    // PARAMETERS
    FOR nI := 1 TO LEN(aExpFunction[nF, 2])
      cParam := aExpFunction[nF, 2][nI]
      IF !EMPTY(cParam)
        PRIVATE &cParam
        &cParam := &('CPAR'+LTRIM(STR(nI,2)))
      ENDIF
    NEXT
     // ОБЯВЛЕНИЕ PRIVATE переменных
     FOR nI := 1 TO LEN( aExpFunction[nF, 5] )
       cParam := aExpFunction[nF, 5][nI]
       PRIVATE &cParam
     NEXT
    AEval(aExpFunction[nF, 3], {|x| nI := x[1], MemVar->xRet := Eval(x[2],x[3],x[4],x[5])})
  ENDIF
RETURN xRet

//--------------------------------------------------------------------------//
FUNCTION EvalWhile( bFor, aWork )
//--------------------------------------------------------------------------//
// Выполнение пользовательских циклов
  LOCAL oErr
  MEMVAR xRet
  PRIVATE xRet := 0
  BEGIN SEQUENCE
    DO WHILE EVAL(bFor)
      AEval(aWork, {|x| nErrorStr := x[1], MemVar->xRet := Eval(x[2],x[3],x[4],x[5])})
    ENDDO

  Recover USING oErr
     IF ValType(oErr) == 'O'
       SayError( "Ошибка: "+ oerr:descriptio + ;
               " при выполнении строки: "+ ;
               ;
              NToC(nErrorStr) + " !!!")
      ENDIF
  END SEQUENCE
RETURN MemVar->xRet

//--------------------------------------------------------------------------//
FUNCTION EvalIfElse( bLogic, aWork, aNoWork )
//--------------------------------------------------------------------------//
// Выполнение пользовательских IF ELSE ENDIF
  LOCAL aMyWork
  MEMVAR xRet
  PRIVATE xRet := 0
  BEGIN SEQUENCE
    aMyWork := IF(EVAL(bLogic),aWork, aNoWork)
    AEval(aMyWork, {|x| nErrorStr := x[1], MemVar->xRet := Eval(x[2],x[3],x[4],x[5])})
  RECOVER
  END SEQUENCE
RETURN MemVar->xRet
//--------------------------------------------------------------------------//
FUNCTION ArrayIndex(aArray,aPos)
//--------------------------------------------------------------------------//
  LOCAL aRet
  aRet := aArray
  AEVAL( aPos, {|x| aRet:= aRet[x] } )
RETURN aRet
//--------------------------------------------------------------------------//


/*******************************
STATIC FUNCTION OpenPPO( cPath )
//--------------------------------------------------------------------------//
// Открытие файла PPO или его создание
  LOCAL nSelect := SELECT(), lOpen := .T.
  FIELD FILE_NAME IN PPO
  IF SELECT('PPO') == 0
    IF !FILE( cPath+'PPO.DBF')
      dbCreate( cPath+'PPO.DBF', { { 'FILE_NAME', 'C',100,0 }, ;
                                   { 'FILE_DATE', 'C',  8,0 }, ;
                                   { 'FILE_TIME', 'C',  8,0 }, ;
                                   { 'FILE_EVAL', 'M', 10,0 }, ;
                                   { 'FUN_EVAL',  'M', 10,0 }  ;
                                 };
       )
       NetUseExc( 'PPO', cPath+'PPO' )
       INDEX ON FILE_NAME TAG NAME TO (cPath+'PPO.CDX')
       USE
    ENDIF
    NetUse( 'PPO', cPath+'PPO' )
    SELECT(nSelect)
    lOpen := .F.
  ENDIF
RETURN lOpen
***********************************/




//--------------------------------------------------------------------------//
STATIC PROCEDURE NameFunct( aFunc,  xT )
//--------------------------------------------------------------------------//
// Замена пользовательских функций на внутренние
  LOCAL i,j,nLen, nMax
  IF !('FUNCTION' $ UPPER(xT))
    FOR i := 1 TO LEN( aFunc )
       nLen := LEN(aFunc[i][1]+'(')
       nMax := NUMAT( aFunc[i][1]+'(', UPPER(xT) )
       FOR j := 1 TO nMax
         xT := STUFF( xT, ;
                      ATNUM( aFunc[i][1]+'(', UPPER(xT), 1 ),;
                      nLen, 'EvalFun("'+aFunc[i][1]+'",' )
       NEXT
    NEXT
  ENDIF
RETURN

//--------------------------------------------------------------------------//
FUNCTION Interface( xT)
//--------------------------------------------------------------------------//
// Привязка фукций к событиям
  LOCAL aInterface[3],i, cParam,cKey
  FOR i := 2 TO 6 STEP 2
    cParam := UPPER(TOKEN(xT,[,()"' ]+CHR(0)+CHR(10)+CHR(13),i))
    cKey   := UPPER(TOKEN(xT,[,()"' ]+CHR(0)+CHR(10)+CHR(13),i+1))
    IF     cParam == 'OBJECT';       aInterface[1] :=cKey
    ELSEIF cParam == 'EVENT'  ;      aInterface[2] :=cKey
    ELSEIF cParam == 'BIND'   ;      aInterface[3] :=cKey
    ENDIF
  NEXT
RETURN aInterface

//--------------------------------------------------------------------------//
PROCEDURE AddInterface(aInterface,nFunc)
//--------------------------------------------------------------------------//
  LOCAL i,j,k
  IF !EMPTY( aInterface ) .AND. !EMPTY(aInterface[1])
    IF (i := ASCAN( aExpInterface, {|x|aInterface[1]==x[1]} )) == 0
      AADD( aExpInterface, {aInterface[1],{}} )
      i := LEN(aExpInterface)
    ENDIF
    IF (j := ASCAN( aExpInterface[i,2], {|x|aInterface[2]==x[1]} )) == 0
      AADD( aExpInterface[i,2], {aInterface[2],{}} )
      j := LEN(aExpInterface[i,2])
    ENDIF
    IF(k := ASCAN( aExpInterface[i,2][j,2], {|x|aInterface[3]==x[1]} )) == 0
      AADD( aExpInterface[i,2][j,2], {aInterface[3],{}} )
      k := LEN(aExpInterface[i,2][j,2])
    ENDIF
    IF ASCAN( aExpInterface[i,2][j,2][k,2], nFunc ) == 0
      AADD( aExpInterface[i,2][j,2][k,2], nFunc )
    ENDIF

  ENDIF
RETURN

//--------------------------------------------------------------------------//
 Function FileEval(cFile, aX, lNoEval)
//--------------------------------------------------------------------------//
// Выполняет построчно файл для использования в формуле типа
// FileEval("имя файла",{массив передаваемых параметров для доступа через aPars})
// Можно использовать aVars для инициализации внутреннего массива
// lNoEval - если .T., то только компиляция
   Local BErr
   Local xT, nNo, cStr, lFC := .F., cParam
   LOCAL nFunction := 0, lYesFunction := .F.,aTmp,nJ
   LOCAL nWhile:=0,aPos:={},nIf:=0
   LOCAL lOpenPPO := .T., aError := {}
   LOCAL aStrExpFile := {},;
         aStrExpFunc  := {},;
         oErr
   LOCAL cTrap
   LOCAL uPath, uName, uExt

   hb_FNameSplit( cFile, @uPath, @uName, @uExt )
   uExt := Lower(Alltrim(uExt))
   if uExt == '.js'.or. uExt == '.vbs'
      Return ""
   endif





   BErr:= ErrorBlock({|oErr| Break(oErr)})
   cTrap:=TRAPSHIFT("")


//   FIELD FILE_DATE, FILE_TIME, FILE_EVAL, FILE_NAME, FUN_EVAL IN PPO

   MEMVAR xRet
   Private xRet:=0, aPars
   PRIVATE _User_Code
   m->_User_Code := _USER_CODE

   IF TYPE('aGlobVars') = 'U'
      PUBLIC aGlobVars
      m-> aGlobVars := ARRAY(100)
   ENDIF

   MemVar->aPars := aX
   Begin Sequence
     AEVAL({;
       _PRO_PATH+'PLUGINS\EXTENSNS\'+cFile,;
       _PRO_PATH+'PLUGINS\PRINT\'+cFile,;
       _PRO_PATH+'PLUGINS\EXTENSNS\'+GetAllArm(NIL,EXENAME())[6]+cFile,;
       _PRO_PATH+'PLUGINS\PRINT\'+GetAllArm(NIL,EXENAME())[6]+cFile,;
       UPPER(IF(TYPE("PATH_DBS") = "C",MemVar->PATH_DBS,LoadPath()+"INI\")) + cFile;
              },;
            {|x|;
            IF(FILE(x),cFile := x,NIL);
              };
            )
     IF (nNo := AScan(aExpFile, {|x| x[1] == cFile })) == 0
       lFC := .F.
/*******************
       lOpenPPO := OpenPPO( _PRO_PATH+"PLUGINS\" )
       IF PPO->( dbSEEK( cFile ) ) .AND. FILE_DATE == DTOS(FILEDATE( cFile )) .AND. ;
          FILE_TIME == FILETIME( cFile ).AND. ValType(FUN_EVAL)=="A" .AND. LEN(FUN_EVAL)>2
         aStrExpFile := { cFile, FILE_EVAL }
         aStrExpFunc  := FUN_EVAL
       ELSE
************************/
         // Разбор PlugIns
         GenCodePlugIn(cFile,@aStrExpFile,@aStrExpFunc,@aError)
         IF !EMPTY(aError)
           SayError(aError)
         ENDIF
//       ENDIF
       IF EMPTY(aError)
          // Компиляция PlugIns
          CompCodePlugIn(cFile,@aStrExpFile,@aStrExpFunc)
          nNo := Len(aExpFile)
       ENDIF
     ENDIF

     lFC := .T.

     IF EMPTY(lNoEval).AND.EMPTY(aError)
       // ОБЯВЛЕНИЕ PRIVATE переменных
       FOR nJ := 1 TO LEN(aExpFile[nNo,3])
         cParam := aExpFile[nNo,3][nJ]
         PRIVATE &cParam
       NEXT
       // ВЫПОЛНЕНИЕ
       AEval(aExpFile[nNo, 2], {|x| nErrorStr := x[1], MemVar->xRet := Eval(x[2],x[3],x[4],x[5])})
     ENDIF

    Recover USING oErr

     IF ValType(oErr) == 'O'

       SayError(If(lFC,;
               "Ошибка при выполнении строки: ",;
               "Синтаксическая ошибка в строке: ") +;
              NToC(nErrorStr) + " файла: " + cFile + " !!!" + oErr:description	)
      ENDIF
    End Sequence

   ErrorBlock(BErr)

   // Закроем файл PPO
//   IF !lOpenPPO ;     PPO->( dbCloseArea() ) ;   ENDIF

   TRAPSHIFT(cTrap)

 Return IF(ValType(MemVar->xRet) = "A","",(MemVar->xRet))

//--------------------------------------------------------------------------//
STATIC FUNCTION StrToBlock(  aStrExpFile )
//--------------------------------------------------------------------------//
// Преобразование сивмольных блоков
  LOCAL cParam := '',aMyExpFile :={},;
        cParam3,;
        aParam4,;
        aParam5,nJ



  FOR nJ := 1 TO LEN(aStrExpFile)
    nErrorStr := aStrExpFile[nJ,1]
    cParam := aStrExpFile[nJ,2]
    cParam3 :=aStrExpFile[nJ,3]


    IF( EMPTY(cParam3),, cParam3 := &(cParam3) )
    aParam4 :=IF(ValType(aStrExpFile[nJ,4])=='A',;
                        StrToBlock(aStrExpFile[nJ,4]),;
                        )
    aParam5 :=IF(ValType(aStrExpFile[nJ,5])=='A',;
                        StrToBlock(aStrExpFile[nJ,5]),;
                        )
    AADD( aMyExpFile, { nErrorStr,;
                      &(cParam),;
                      cParam3,;
                      aParam4,;
                      aParam5;
                    };
        )
  NEXT
RETURN aMyExpFile


//--------------------------------------------------------------------------//
     PROCEDURE GenCodePlugIn(cFile,aStrExpFile,aStrExpFunc,aError,cStr)
//--------------------------------------------------------------------------//
// Выполняет разбор текста без компиляции в массивы
// aStrExpFile,aStrExpFunc
LOCAL xT, nNo,  cParam
LOCAL nFunction := 0, lYesFunction := .F.,aTmp,nJ
LOCAL nWhile:=0,aPos:={},nIf:=0,cTok:=""
  IF ValType(cFile) == "C"
    cStr  := MemoRead(cFile)
  ElseIF ValType(cStr)<> "C"
    cStr:=""
  ENDIF
  IF Len(cStr) > 0
                        //    FILE   BLOCK  PRIVATE
       AAdd(aStrExpFile, {   cFile, {},    {}     })
       nNo := Len(aStrExpFile)
       aPos:={}

       TokenInit(@cStr, Chr(13) + Chr(10), 2)
       nErrorStr := 1
       While !TokenEnd()
         IF Empty(xT := ALLTRIM(TokenNext(@cStr))) .Or. Left(xT, 2) == "//"
         ELSE
           DO WHILE  RIGHT(RTRIM(xT),1) == ';' .AND. !EMPTY(cTok:=ALLTRIM(TokenNext(@cStr)))
             xT := ALLTRIM(xT)+cTok
           ENDDO

          NameFunct( aStrExpFunc,  @xT )
          NameFunct( aExpFunction, @xT )

          IF  UPPER(LTRIM(xT)) = "PRIVATE"
            aTmp := LineToArray( ALLTRIM(SUBSTR(LTRIM(xT),9)), ',' )
            FOR nj := 1 TO LEN(aTmp)
             AADD( IF( lYesFunction,;
                       aStrExpFunc[nFunction,5],;
                       aStrExpFile[nNo,3];
                     ), ALLTRIM(aTmp[nJ]) ;
                  )
            NEXT

          ELSEIF 'ENDIF' $ UPPER( xT )
             nIf--
             IF nIf < 0 ; AADD(aError,'Есть ENDIF - нет IF(...) в стр. '+STR(nErrorStr,5,0)) ; ENDIF
             ASIZE( aPos, LEN(aPos)-2)

          ELSEIF  UPPER(TOKEN(xT,[,()"' ]+CHR(0)+CHR(10)+CHR(13)+CHR(9),1) ) == 'IF' .AND.;
                  TOKENSEP(.T.) <> '('
             nIf++
             cParam := "{||" + ;
                       ALLTRIM(SUBSTR(xT, AT('IF',UPPER(xT))+2)) +;
                       "}"

             AADD( IF( lYesFunction,;
                       ArrayIndex(aStrExpFunc[nFunction,3],aPos),;
                       ArrayIndex(aStrExpFile[nNo,2],aPos)),;
                   {nErrorStr,;
                    "{|bFor, aWork,aNoWork| EvalIfElse( bFor, aWork,aNoWork ) }",;
                    cParam,{},{};
                   } )

             AADD( aPos, LEN(IF( lYesFunction,;
                       ArrayIndex(aStrExpFunc[nFunction,3],aPos),;
                       ArrayIndex(aStrExpFile[nNo,2],aPos);
                     )) )
             AADD( aPos, 4)

          ELSEIF 'ELSE'  $ UPPER( xT )
            aPos[LEN(aPos)] := 5
            IF nIf < 1 ; AADD(aError,'Есть ELSE - нет IF(...) в стр. '+STR(nErrorStr,5,0)) ; ENDIF

          ELSEIF 'WHILE' $ UPPER( xT )
             nWhile++
             cParam := "{||" + ;
                       ALLTRIM(SUBSTR(xT, AT('WHILE',UPPER(xT))+5)) +;
                       "}"
             AADD( IF( lYesFunction,;
                       ArrayIndex(aStrExpFunc[nFunction,3],aPos),;
                       ArrayIndex(aStrExpFile[nNo,2],aPos);
                     ),;
                   {nErrorStr,"{|bFor, aWork| EvalWhile( bFor, aWork ) }",;
                      cParam,{}, } )

             AADD( aPos, LEN(IF( lYesFunction,;
                       ArrayIndex(aStrExpFunc[nFunction,3],aPos),;
                       ArrayIndex(aStrExpFile[nNo,2],aPos);
                     )) )
             AADD( aPos, 4)

          ELSEIF 'ENDDO' $ UPPER( xT )
            nWhile--
            ASIZE( aPos, LEN(aPos)-2)
            IF nWhile < 0 ;  AADD(aError,'Лишний ENDDO в стр. '+STR(nErrorStr,5,0)) ;  ENDIF

          ELSEIF UPPER( xT ) = 'FUNCTION'
            lYesFunction := .T.
            nFunction := AT('(',xT)+1
            AADD( aStrExpFunc, { UPPER(TOKEN(xT,[,()"' ]+CHR(0)+CHR(10)+CHR(13)+CHR(9),2)),;
                                  LineToArray(SUBSTR(xT,nFunction,AT(')',xT)-nFunction),','),{},{},{} } )
            nFunction := LEN(aStrExpFunc)

          ELSEIF UPPER( xT ) = 'INTERFACE' .AND. lYesFunction
            AADD( aStrExpFunc[nFunction][4], Interface(xT) )

          ELSEIF UPPER( xT ) = 'RETURN'
            lYesFunction := .F.
          ELSE
            AADD( IF( lYesFunction,;
                       ArrayIndex(aStrExpFunc[nFunction,3], aPos),;
                       ArrayIndex(aStrExpFile[nNo,2],           aPos) ;
                    ),;
                  {nErrorStr,"{||" + xT + "}",,,})
          ENDIF
         ENDIF
         nErrorStr++
       EndDo


/*****************
       // сброс в файл
       IF SELECT("PPO") != 0
         IF !(PPO->( dbSEEK( cFile ) ))
           PPO->(dbAppend())
         ENDIF
         IF PPO->( RecLock() )
           PPO->FILE_NAME := cFile
           PPO->FILE_DATE := DTOS(FILEDATE( cFile ))
           PPO->FILE_TIME := FILETIME( cFile )

           #ifdef _ALASKA
           PPO->FILE_EVAL := Var2Bin(aStrExpFile[nNo])
           PPO->FUN_EVAL  := Var2Bin(aStrExpFunc)
           #else
           PPO->FILE_EVAL := aStrExpFile[nNo]
           PPO->FUN_EVAL  := aStrExpFunc
           #endif
           PPO->( dbUnlock() )
         ENDIF
       ENDIF
*************************/
  ELSE
    AADD(aError,"Нет файла или пустой файл:" + cFile)
  ENDIF

RETURN
//--------------------------------------------------------------------------//
              PROCEDURE CompCodePlugIn(cFile,aStrExpFile,aStrExpFunc)
//--------------------------------------------------------------------------//
// Выполняет компиляцию по готовым массивам сформированных строк
LOCAL nNo := Len(aStrExpFile)

IF !EMPTY(nNo)
  AAdd( aExpFile,    {;
                         cFile, ;
                         StrToBlock( aStrExpFile[nNo,2] ),;
                         aStrExpFile[nNo,3];
                      };
       )

  AEVAL( aStrExpFunc,{|x,xi|;
     AADD(aExpFunction,{x[1],x[2],STRToBlock(x[3]),x[4],x[5]}),;
     IF( ValType(x[4])=='A',;
        AEVAL(x[4],{|y|AddInterface(y,LEN(aExpFunction))}),;
        NIL;
        );
                      } )
ENDIF
RETURN

//----------------------------------------------------------------------------//
                     FUNCTION AllDirFile(cPath,cMaska,bDel,bRun,cOnlyPath)
//----------------------------------------------------------------------------//
// Возвращает массив имен файлов по маске вместе с поддиректриями
// cPath - основной путь
// cMaska  - маска
// bDel - блок кода - ему передается массив инф. о файле и если он возвратит .T.
//        то информация из результирующего массива убирается
//        и вторым параметром путь
// bRun - блок кода выполняемы на каждом чтении имени файла
// cOnlyPath - используется только для внутрених целей (при рекурсии)
STATIC cSPath
LOCAL aFile := DIRECTORY(cPath+IF(cOnlyPath != NIL,cOnlyPath,"")+cMaska,"D")
LOCAL aDirFile := {},aDelFile := {}
IF EMPTY(cOnlyPath)
  cSPath := cPath
ENDIF
AEVAL(aFile,{|_aFile,n|_aFile[1]:=UPPER(_aFile[1]),;
             IF(bRun != NIL,EVAL(bRun,cPath+_aFile[1]),NIL),;
             IF(_aFile[5] = "D".AND. _aFile[1] != "." .AND. _aFile[1] != "..",;
               (;
                AEVAL(AllDirFile(cPath+IF(cOnlyPath != NIL,cOnlyPath,""),cMaska,bDel,bRun,_aFile[1]+"\"),{|_aFile| AADD(aDirFile,_aFile) }),;
                AADD(aDelFile,n),aFile[n] := NIL;
                ),;
               IF(_aFile[5] = "D".OR.(bDel != NIL .AND. EVAL(bDel,_aFile,UPPER(cPath+IF(cOnlyPath != NIL,cOnlyPath,"")),UPPER(IF(cOnlyPath != NIL,cOnlyPath,"")))),(AADD(aDelFile,n),aFile[n] := NIL),NIL);
               );
               })
ASORT(aDelFile,,,{|x,y| x > y})
AEVAL(aDelFile,{|n| ADEL(aFile,n) })
ASIZE(aFile,LEN(aFile)-LEN(aDelFile))
IF !EMPTY(cOnlyPath)
  AEVAL(aFile,{|_aFile,n| aFile[n] := SUBSTR(cPath,LEN(cSPath)+1)+cOnlyPath+_aFile[1] })
ELSE
  AEVAL(aFile,{|_aFile,n| aFile[n] := _aFile[1] })
ENDIF
AEVAL(aDirFile,{|_aFile,n| AADD(aFile,_aFile),aDirFile[n] := NIL })
RETURN aFile

******************
FUNCTION DoNewGet(nStr,nCol,bBlock,cName,cSh,cCol,bWhen,bValid,bReader)
******************
LOCAL o
o:= GETNEW(nStr,nCol,bBlock,cName,cSh,cCol)
o:postblock:=bValid
o:preblock:=bWhen
o:reader:=bReader

RETURN o



#ifdef __HARBOUR__
FUNCTION FormulPars(cFormul, aPars)
RETURN FormulParsBlock(cFormul, aPars) //в FileEval сокращенно употребляют
#endif






/*****************************************************************
 FUNCTION: FormulParsBlock(cFormul, aPars)
 Назначение.........Преобразование строки символов в блок кода
                    cFormul - преобразуемая строка
                    aPars   - массив параметров для блока кода
**************************************/
Function FormulParsBlock(cFormul, aPars)
   Local cPar := "{|"
   AEval(aPars, {|x, i| cPar += x + If(i == Len(aPars), "|", ",")})
Return (&(cPar + AllTrim(cFormul) + "}"))

/*******************************
*   F_DBUNLOCK () --> NIL
*
*/
FUNCTION  F_DBUNLOCK()
RETURN DBUNLOCK()  // from  func F_DBUNLOCK

/*******************************
*   F_SETRELATION () --> NIL
*
*/
FUNCTION  F_SETRELATION(cAliasD,cRelat)
  //  SET RELATION TO &cRelat INTO (cAlias)
RETURN DBSetRelation(cAliasD,&("{|| "+cRelat+"}"),cRelat)  // from  func F_SETRELATION

/*******************************
*   F_CLEARRELATION () --> NIL
*
*/
FUNCTION  F_CLEARRELATION()
    SET RELATION TO
RETURN NIL  // from  func F_CLEARRELATION

******************
FUNCTION GetArrEl(aMas,aInd)
******************
LOCAL nRes:=aMas,i
FOR i:=1 TO LEN(aInd)
   nRes:=nRes[aInd[i]]
NEXT

RETURN nRes

// 11.02.2004 - Титов В.
// Выполняется строка cStrIn, передаются параметры aX
// Сделано для того чтобы Evalить Memo текст сразу без сброса в PPO.
******************************
FUNCTION EvalStr(cStrIn,aX,aErrorHeader)
******************************
   Local BErr := ErrorBlock({|oErr| Break(oErr)})
   Local xT, nNo, cStr, lFC := .F., cParam
   LOCAL nFunction := 0, lYesFunction := .F.,aTmp,nJ
   LOCAL nWhile:=0,aPos:={},nIf:=0
   LOCAL lOpenPPO := .T., aError := {}
   LOCAL aStrExpFile := {},;
         aStrExpFunc  := {},;
         oErr,lOk:=.F.
   Local cMemoStr := ""
   Local aErrMess := {}
	Local i := 1,nLen
	Local lResult := .T.
	 Local lPrivate := .F.

   MEMVAR xRet
   Private xRet:=0, aPars
   PRIVATE _User_Code
   m->_User_Code := _USER_CODE
   IF TYPE('aGlobVars') = 'U'
      PUBLIC aGlobVars
      m-> aGlobVars := ARRAY(100)
   ENDIF

   MemVar->aPars := aX


Begin sequence
IF aErrorHeader !=Nil
	IF ValType(aErrorHeader) = "C"
		aErrorHeader := {aErrorHeader}
	ENDIF
ENDIF

IF ValType(cStrIn) = "M"
	i := 1
	nLen := mlCount(cStrIn,254)
	While (i <= nLen)
		cMemoStr:= cMemoStr+ALLTRIM(MemoLine(cStrIn,254,i))+IIF(i<nLen,chr(13)+chr(10),"")
		i := i+1
	ENDDO
	cStrIn := cMemoStr
ENDIF


         // Разбор PlugIns
         GenCodePlugIn(,@aStrExpFile,@aStrExpFunc,@aError,cStrIn)
         IF !EMPTY(aError)
           SayError(aError)
         ENDIF


       // Компиляция PlugIns
       CompCodePlugIn(,@aStrExpFile,@aStrExpFunc)
       nNo := Len(aExpFile)




     lFC := .T.
	lPrivate := .F.

//     IF EMPTY(lNoEval)
       // ОБЯВЛЕНИЕ PRIVATE переменных
	lPrivate := .T.
       FOR nJ := 1 TO LEN(aExpFile[nNo,3])
         cParam := aExpFile[nNo,3][nJ]
         PRIVATE &cParam
       NEXT
	lPrivate := .F.
       // ВЫПОЛНЕНИЕ
       AEval(aExpFile[nNo, 2], {|x| nErrorStr := x[1], MemVar->xRet := Eval(x[2],x[3],x[4],x[5])})
  //   ENDIF

    Recover USING oErr

     IF ValType(oErr) == 'O'
	lResult := .F.
	aErrMess := aErrorHeader
	AADD(aErrMess,(IF(lPrivate,"Ошибка при определении PRIVATE переменной!"+ALLTRIM(cParam),If(lFC,;
               "Ошибка при выполнении строки: ",;
               "Синтаксическая ошибка в строке: ") +;
              NToC(nErrorStr) + " !!!")))

       SayError(aErrMess)
      ENDIF
    End Sequence
   ErrorBlock(BErr)
RETURN lResult

