unit uBafVarModule;

// this code is under the BAF fair use license (BFUL) - https://bafbal.de/index.php?title=Bful
// module for vars, text, csv...
// https://bafbal.de/index.php?title=Modul_VAR_neu

interface

uses System.Math, System.SysUtils, System.Classes, uBafTypes, uBafInterpreter,
  System.Contnrs, Winapi.Windows, System.StrUtils, FMX.Dialogs, System.Zip,
  System.Hash, uStringIniFile, System.RegularExpressions, System.NetEncoding,
  AbZipper, uBafCrypt;


type
  TBafNumInfo = (niIsInt, niIsCurr, niIntLen, niIntMin, niIntMax, niDateMin,
      niDateMax, niNumBetween, niDateBetween);

  TBafColorInfo = (ciDeadlineYR);

  TBafVarModule = class(TBafInterpreterCustomModule)
  protected   // Text
    FTextList: TObjectList;
    FKatList: TObjectList;
    procedure SetText(AIndex: integer);
    procedure SetTextN(AIndex: integer);
    procedure SaveText(AIndex: integer);
    procedure OpenText(AIndex: integer);
    procedure TextProperties(AIndex: integer);
    procedure TextSet(AIndex: integer);
    procedure TextCompose(AIndex: integer);
    procedure TextDissect(AIndex: integer);
    procedure TextSort(AIndex: integer);
    procedure TextLoop(AIndex: integer);
    procedure TextAct(AIndex: integer);
    procedure TvlAdd(AIndex: integer);
    procedure ClearText(AIndex: integer);
    function GetText(AParam: string): string;
    function GetTextLine(AParams: TStrings): string;
  protected   // CSV
    FCsvList, FCsvHeaderList, FCsvZeileList: TObjectList;
    function GetCsvList(AIndex: integer): TStringList;
    function GetCsvHeaderList(AIndex: integer): TStringList;
    function GetCsvZeileList(AIndex: integer): TStringList;
    procedure CsvOpen(AIndex: integer);
    procedure CsvLine(AIndex: integer);
    procedure CsvCheck(AIndex: integer);
    function GetCsvValue(AParams: TStrings): string;
    function GetCsvLine(AParams: TStrings): string;
  protected  // Ini
    FIniList: TObjectList;
    procedure SetIniValue(AIndex: integer);
    procedure IniLoadSave(AIndex: integer; ASave: boolean);
    function GetIniText(AParam: string): string;
    function GetIniValue(AParams: TStrings): string;
  protected   // Sperated Line
    FSepLineList: TObjectList;
    FSepLineValue: TStringList;
    function GetSepLineList(AIndex: integer): TStringList;
    procedure SepLine(AIndex: integer);
    function GetSepLine(AParam: string): string;
  protected   // Vars und Code
    FCode: TStringList;
    FNamedVars: TStringList;
    FCmdList: TObjectList;
    procedure SetVar(AList: TStringList; AOnlyIfEmpty: boolean);
    procedure AddVar(AList: TStringList);
    function GetVar(AList: TStringList; AParams: TStrings): string;
    function GetVariable(AName: string): string; override;
    procedure SetVariable(AName: string; const Value: string); override;
    function GetRightOfDef(AName: string): TBafRight; override;
    procedure LogVars(AList: TStringList);
    function GetCode(AClear: boolean): string; override;
    procedure SetCmd(AIndex: integer);
    procedure ClearCmd(AIndex: integer);
    function GetCmdList(AIndex: integer): TStringList;
    function GetProc(AIndex: integer): string; override;
    procedure ClearCmds;
  protected // Rights
    FRightDefs: TStringList;
    procedure RightsDef;
    function GetUserId(AParams: TStrings): string;
  protected    // Funktionen
    function Int2Time(AParam: string): string;
    function RemoveCrLf(AParams: TStrings): string;
    procedure Exec;
    procedure ExecAsync;
    procedure Procdir;
    procedure Forcedir;
    procedure BatchExec;
    procedure DoOpenFile;
    procedure Save2Clipboard;
    procedure FileOperation;
    procedure BafFileSearch;
    procedure BafFileWait;
    procedure PasteCsv;
    procedure Loop;
    procedure CheckString;
    procedure BafZipCreate;
    function GetNumInfos(AInfo: TBafNumInfo; AParams: TStrings): string;
    function GetIntMinMax(AInfo: TBafNumInfo; ANum, ANum2: string): string;
    function GetDateMinMaxRel(AInfo: TBafNumInfo; ANum, ANum2: string): string;
    function NewGuidFormat(AText: string): string;
    function StringCopy(AParams: TStrings): string;
    function GetLookup(AParams: TStrings): string;
    function GetInversLookup(AParams: TStrings): string;
    function StringInclude(AParams: TStrings): string;
    function GetChar(AParams: TStrings): string;
    function BafBase64(AParams: TStrings): string;
    function IncDec(ADec: boolean; AParams: TStrings): string;
    function ICalc(AParams: TStrings): string;
    function BafPos(AParams: TStrings): string;
    function BafRandom(AParams: TStrings): string;
    function ReplaceString(AParams: TStrings): string;
    function StringOperation(AParams: TStrings): string;
    function StringExtract(AParams: TStrings): string;
    function BafTrim(AParams: TStrings): string;
    function BafXmlReplace(AParams: TStrings): string;
    function ValueTranslate(AParams: TStrings): string;
    function ValueTranslateValueList(AParams: TStrings): string;
    function ValueTranslateRootDB(AParams: TStrings): string;
    function BafXmlReReplace(AParams: TStrings): string;
    function TextCompare(AParams: TStrings): string;
    function GetGuid(AParams: TStrings): string;
    function FunctionExecute(AParams: TStrings): string;
    function GetHash(AParams: TStrings): string;
    function NumFormat(AParams: TStrings): string;
    function NullValue(AParams: TStrings): string;
    function OnlyChar(AParams: TStrings): string;
    function Pad(AParams: TStrings): string;
    function FormatDataTime(AParams: TStrings; ADefault: string): string;
    function LowNoSpace(AText: string): string;
    function GetClrDeadline(AInfo: TBafColorInfo; AParams: TStrings): string;
    function Convert(AParams: TStrings): string;
    function IncDate(AParams: TStrings): string;
    function IncTime(AParams: TStrings): string;
    function GetDateFormated(AParams: TStrings): string;
    function FileDialog(AParams: TStrings): string;
    function FileInfo(AParams: TStrings): string;
    function IsAdmin(AParams: TStrings; ARight: boolean): string;
    function BCalc(AParams: TStrings): string;
    function EmptyV(ANotEmpty: boolean; AParams: TStrings): string;
    function BFmt(AParams: TStrings): string;
    function CCalc(AParams: TStrings): string;
    function CFmt(AParams: TStrings): string;
    function GetCurr(AParams: TStrings): string;
    function OnlyNum(AParams: TStrings): string;
    function InOperation(AParams: TStrings; AIgnoreCase, AVariable: boolean): string;
    function GetBetween(AInfo: TBafNumInfo; AParams: TStrings): string;
    function TextHasLine(AParams: TStrings): string;
    function BafREgex(AParams: TStrings): string;
    function BafIsDate(AParams: TStrings): string;
    function BafAdresssKorrelation(AParams: TStrings): string;
    function BafDateCompare(AParams: TStrings): string;
  protected
    function GetJSON(AParams: TStrings): string;
    function BafJsonObjectParse(AJson, APath: string;
        AIgnoreNameCase: boolean = false; APathSep: Char = '.'): string;
  public
    constructor Create; override;
    destructor Destroy; override;
    function InterpretLine(AExecInter: TBafCustomInterpreter): boolean; override;
    function ReplaceFunction(ACommand: string; AParams: TStrings; var AResult: string): boolean; override;
    function GetTextStringList(AIndex: integer): TStringList; override;
    function GetKatStringList(AIndex: integer): TStringList; override;
    function GetIni(AIndex: integer): TStringIniFile; override;
    function BafLevenshtein(AString1, AString2: string): integer;
  end;

implementation

{ TBafVarModule }

uses uBafDataCache, uOsStuff, dmMain, uBafComboHelper;

procedure TBafVarModule.AddVar(AList: TStringList);
var
  LName, LValue, LRightName, LTyp: string;
  LRight: TBafRight;


  procedure lokMonth;
  var
    LDate: TDate;
    LYear, LMonth, LDay: word;
    LDiff, LMonth2: integer;
  begin
    LDate := StrToDateTimeDef(AList.Values[LName], 0);
    DecodeDate(LDate, LYear, LMonth, LDay);
    LDiff := StrToIntDef(LValue, 0);
    LMonth2 := LMonth + LDiff;
    while LMonth2 > 12 do begin
      LMonth2 := LMonth2 - 12;
      inc(LYear);
    end;
    while LMonth2 < 1 do begin
      LMonth2 := LMonth2 + 12;
      dec(LYear);
    end;
    LDate := EncodeDate(LYear, LMonth2, LDay);
    AList.Values[LName] := FormatDateTime('dd.mm.yyyy', LDate);
  end; // procedure lokMonth

begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LRightName := FindParamStringLower('r', 'frm');
    LName := FindParamStringReplaced('n', '');
    if LRightName = 'frm' then
      LRight := brWrite
    else
      LRight := FInter.GetRightOfDef(LRightName);
    if LRight = brWrite then begin
      LValue := FindParamStringReplaced('z', '');
      LTyp := FindParamStringReplacedLower('y', '');
      if LTyp = 'int' then
        AList.Values[LName] := IntToStr(StrToIntDef(AList.Values[LName], 0)
            + StrToIntDef(LValue, 0))
      else if LTyp = 'curr' then
        AList.Values[LName] := CurrToStr(StrToCurrDef(AList.Values[LName], 0)
            + StrToCurrDef(LValue, 0))
      else if LTyp = 'date' then
        AList.Values[LName] := FormatDateTime('dd.mm.yyyy',
            StrToDateTimeDef(AList.Values[LName], 0) + StrToFloatDef(LValue, 0))
      else if LTyp = 'month' then
        lokMonth
      else if LTyp = 'datetime' then begin
        AList.Values[LName] := FormatDateTime('dd.mm.yyyy hh:mm:ss',
            StrToDateTimeDef(AList.Values[LName], 0) + StrToFloatDef(LValue, 0))
        end
      else
        AList.Values[LName] := AList.Values[LName] + LValue;
    end;
  end;
end;

function TBafVarModule.BafAdresssKorrelation(AParams: TStrings): string;
var
  LAnz, LDist, LLength: integer;
  LString1, LString2, LTyp: string;
  LProzTotal, LProz: single;
begin
  LAnz := 3;
  LProzTotal := 0;
  while AParams.Count >= LAnz do begin
    LTyp := AnsiLowerCase(AParams[LAnz - 3]);
    LString1 := AParams[LAnz - 2];
    LString2 := AParams[LAnz - 1];
    if LTyp = 'minlen' then begin
      LLength := System.Math.Min(Length(LString1), Length(LString2));
      LString1 := copy(LString1, 1, LLength);
      LString2 := copy(LString2, 1, LLength);
    end;
    if (LString1 = '') or (LString2 = '') then begin
      LDist := 1;
      LProz := 15;
    end
    else begin
      LDist := BafLevenshtein(LString1, LString2);
      LProz := 0;
      if LDist > 0 then
        LProz := 200 * LDist / (Length(LString1) + Length(LString2));
    end;
    LProzTotal := LProzTotal + LProz;
    LAnz := LAnz + 3;
  end;
  result := FormatFloat('0.00', 100 - LProzTotal);
end;

function TBafVarModule.BafBase64(AParams: TStrings): string;
var
  s0, s1: string;
  LInStream: TStream;
  LOutStream: TStringStream;
begin
  if AParams.Count > 1 then begin
    s0 := Trim(AnsiLowerCase(AParams[0]));
    s1 := AParams[1];
    if s0 = 'e' then
      result := TNetEncoding.Base64String.Encode(s1)
    else if s0 = 'ef' then begin
      LInStream := TFileStream.Create(s1, fmOpenRead);
      try
        LOutStream := TStringStream.Create;
        try
          TNetEncoding.Base64String.Encode(LInStream, LOutStream);
          result := LOutStream.DataString;
        finally
          LOutStream.Free;
        end;
      finally
        LInStream.Free;
      end;
    end
    else if s0 = 'd' then
      result := TNetEncoding.Base64String.Decode(s1);
  end
  else
    FInter.DoLog('E', '$BASE64 - Number of Params less 2');
end;

function TBafVarModule.BafDateCompare(AParams: TStrings): string;
var
  d1, d2, LDiff: TDate;
  LOp: string;
begin
  if AParams.Count > 1 then begin
    d1 := StrToDateDef(AParams[0], 0);
    d2 := StrToDateDef(AParams[1], 0);
    LDiff := d2 - d1;
    if AParams.Count > 2 then
      LOp := AParams[2];
    if LOp = '<' then
      result := IfThen(d1 < d2, 'Y', 'N')
    else if LOp = '<=' then
      result := IfThen(d1 <= d2, 'Y', 'N')
    else if LOp = '>' then
      result := IfThen(d1 > d2, 'Y', 'N')
    else if LOp = '>=' then
      result := IfThen(d1 >= d2, 'Y', 'N')

    else
      result := FormatFloat('0', LDiff);
  end
  else
    FInter.DoLog('E', '$DATECOMP - Number of Params less 2');
end;

procedure TBafVarModule.BafFileSearch;
var
  LText: TStringList;
  LNum, LResult, LAttr: integer;
  LSearchRec: TSearchRec;
  LFileName, LAttributes: string;
  LAll: boolean;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LNum := FindParamIntegerReplaced('n', 1);
    LText := GetTextStringList(LNum);
    LFileName := FindParamStringReplaced('fn', '');
    LAll := FindParamBooleanReplaced('all', false);
    LAttributes := FindParamStringReplacedLower('y', 'AnyFile');
    if LAttributes = 'AnyFile' then
      LAttr := faAnyFile
    else if LAttributes = 'ReadOnly' then
      LAttr := faReadOnly
    else if LAttributes = 'Hidden' then
      LAttr := faHidden
    else if LAttributes = 'SysFile' then
      LAttr := faSysFile
    else if LAttributes = 'VolumeID' then
      LAttr := faVolumeID
    else if LAttributes = 'Directory' then
      LAttr := faDirectory
    else if LAttributes = 'Archive' then
      LAttr := faArchive
    else LAttr := StrToIntDef(LAttributes, faAnyFile);
    if FindParamBooleanReplaced('clr', true) then
      LText.Clear;
    LResult := FindFirst(LFileName, LAttr, LSearchRec);
    try
       while LResult = 0 do begin
         if LAll then
           LText.Add(Format('%s;%s;%d;%d', [LSearchRec.Name,
               FormatDateTime(BAFDATETIMEFMT, (LSearchRec.TimeStamp)),
               LSearchRec.Size, LSearchRec.Attr]))
         else
           LText.Add(LSearchRec.Name);
         LResult := FindNext(LSearchRec);
       end;
    finally
      System.SysUtils.FindClose(LSearchRec);
    end;
  end;
end;

procedure TBafVarModule.BafFileWait;
var
  LText: TStringList;
  LNum, LResult, LAttr, LSleep, LTimeout: integer;
  LSearchRec: TSearchRec;
  LFileName, LAttributes, LCmd, LCmdTo: string;
  LAll, LGefunden: boolean;

  procedure lokParams;
  begin
    LGefunden := false;
    LFileName := FindParamStringReplaced('fn', '');
    LAttributes := FindParamStringReplacedLower('y', 'AnyFile');
    if LAttributes = 'AnyFile' then
      LAttr := faAnyFile
    else if LAttributes = 'ReadOnly' then
      LAttr := faReadOnly
    else if LAttributes = 'Hidden' then
      LAttr := faHidden
    else if LAttributes = 'SysFile' then
      LAttr := faSysFile
    else if LAttributes = 'VolumeID' then
      LAttr := faVolumeID
    else if LAttributes = 'Directory' then
      LAttr := faDirectory
    else if LAttributes = 'Archive' then
      LAttr := faArchive
    else LAttr := StrToIntDef(LAttributes, faAnyFile);
    LSleep := FindParamIntegerReplaced('sleep', 1000);
    LTimeout := FindParamIntegerReplaced('to', 15000);
    LCmd := FindParamStringReplaced('cmd', '');
    LCmdTo := FindParamStringReplaced('cmdto', '');
  end; // procedure lokParams

  function lokSearch: boolean;
  begin
    result := false;
    LResult := FindFirst(LFileName, LAttr, LSearchRec);
    try
       while LResult = 0 do begin
         result := true;
         LResult := FindNext(LSearchRec);
       end;
    finally
      System.SysUtils.FindClose(LSearchRec);
    end;
  end; // function lokSearch

begin
  if FindParamBooleanReplaced('cnd', true) then begin
    lokParams;
    LGefunden := lokSearch;
    while not LGefunden and (LTimeout > 0) do begin
      Sleep(LSleep);
      LTimeOut := LTimeout - LSleep;
      LGefunden := lokSearch;
    end;
    if LGefunden and (LCmd <> '') then
      TBafInterpreterLevel.ExecInNewLevel(LCmd, FExecInter, FInter);
    if not LGefunden and (LCmdTo <> '') then
      TBafInterpreterLevel.ExecInNewLevel(LCmdTo, FExecInter, FInter);
  end;
// procedure TBafVarModule.BafFileWait
end;

function TBafVarModule.BafIsDate(AParams: TStrings): string;
var
  LText, LOp: string;
  LDate: TDateTime;
begin
  if AParams.Count > 0 then begin
    LText := AParams[0];
    if AParams.Count > 1 then
      LOp := AnsiLowerCase(AParams[1])
    else
      LOp := 'date';
    if LOp = 'date' then begin
      LDate := StrToDateDef(LText, -1234);
      result := IfThen(LDate <> -1234, 'Y', 'N');
    end
    else if LOp = 'datetime' then begin
      LDate := StrToDateTimeDef(LText, -1234);
      result := IfThen(LDate <> -1234, 'Y', 'N');
    end;
  end
  else
    FInter.DoLog('E', '$ISDATE - Number of Params less 1');
end;

function TBafVarModule.BafJsonObjectParse(AJson, APath: string;
  AIgnoreNameCase: boolean; APathSep: Char): string;
var
  i, LLevel, LLvlObject, LLvlArray, LLvlObject2: integer;
  LName, LValue, LParsePath: string;
  LAbort, LInQuote, LInValue, LInObject: boolean;

  procedure lokInit;
  begin
    APath := APathSep + APath;
    LLevel := 0;
    LLvlObject := -1;
    LLvlArray := 0;
    LInQuote := false;
    LInValue := false;
    LAbort := false;
    LInObject := false;
    LLvlObject2 := 0;
  end; // procedure lokInit

  procedure lokReducePath;
  var
    i: integer;
  begin
    for i := Length(LParsePath) downto 1 do begin
      if LParsePath[i] = APathSep then begin
        Delete(LParsePath, i, MaxInt);
        exit;
      end;
    end;
  end; // procedure lokReducePath

  procedure lokStopValue;
  begin
    LInValue := false;
    if AIgnoreNameCase then begin
      if AnsiCompareText(LParsePath + APathSep + LName, APath) = 0 then begin
        result := LValue;
        LAbort := true;
      end;
    end  // if AIgnoreNameCase
    else begin
      if LParsePath + APathSep + LName = APath then begin
        result := LValue;
        LAbort := true;
      end;
    end; // else AIgnoreNameCase
    LName := '';
  end; // procedure lokStartValue

  procedure lokCheckCharIntern;
  begin
    case AJson[i] of
      '"': LInQuote := not LInQuote;
      '{': if not LInQuote then begin
             inc(LLvlObject);
             if LLvlObject > 0 then
               LParsePath := LParsePath + APathSep + LName;
             if AIgnoreNameCase then begin
               if AnsiCompareText(LParsePath, APath) = 0 then
                 LInObject := true;
             end
             else begin
               if LParsePath = APath then
                 LInObject := true;
             end;
             LInValue := false;
             LName := '';
           end;
      '}': if not LInQuote then begin
             dec(LLvlObject);
             lokStopValue;
             lokReducePath;
           end;
      '[': if not LInQuote then begin
             inc(LLvlArray);
             LValue := LValue + '[';
           end;
      ':': if not LInQuote then begin
             LInValue := true;
             LValue := '';
           end;
      ',': if not LInQuote then
             lokStopValue;
      ' ': if LInQuote then begin
             if LInValue then
               LValue := LValue + AJson[i]
             else
               LName := LName + AJson[i];
           end;
      else begin
        if LInValue then
          LValue := LValue + AJson[i]
        else if LInQuote then
          LName := LName + AJson[i];
      end;
    end; // case
  end; // procedure lokCheckCharIntern

  procedure lokCheckChar;
  begin
    if not LInObject then begin
      if LLvlArray = 0 then
        lokCheckCharIntern
      else begin
        if AJson[i] = ']' then
          dec(LLvlArray);
        LValue := LValue + AJson[i];
        if LLvlArray = 0 then
          lokStopValue;
      end;
    end
    else begin  // if not LInObject
      case AJson[i] of
        '"': LInQuote := not LInQuote;
        '{': if not LInQuote then
               inc(LLvlObject2);
        '}': if not LInQuote then
               dec(LLvlObject2);
      end; // case AJson[i] of
      LValue := LValue + AJson[i];
      if LLvlObject2 < 0 then begin
        result := '{' + LValue;
        LAbort := true;
      end;
    end;  // else if not LInObject
  end; // procedure lokCheckChar

begin
  lokInit;
  for i := 1 to Length(AJson) do begin
    lokCheckChar;
    if LAbort then
      Break;
  end;
// function TBafVarModule.BafJsonObjectParse
end;

function TBafVarModule.BafLevenshtein(AString1, AString2: string): integer;
var
  d: array of array of Integer;
  n, m, i, j, costo: Integer;
  c1, c2: char;

  function lokMinimum(a, b, c: Integer): Integer;
  begin
    result := a;
    if b < result then
      result := b;
    if c < result then
      result := c;
  end; // function lokMinimum

begin
  try
    AString1 := Trim(AString1);
    AString2 := Trim(AString2);
    n := Length(AString1);
    m := Length(AString2);
    if (n = 0) then begin
        Result := m;
        Exit;
    end;
    if m = 0 then begin
        Result := n;
        Exit;
    end;
    SetLength(d, n + 1, m + 1);
    for i := 0 to n do
      d[i, 0] := i;
    for j := 0 to m do
      d[0, j] := j;
    for i := 1 to n do begin
      c1 := AString1[i];
      for j := 1 to m do begin
        c2 := AString2[j];
        if c1 = c2 then costo := 0 else costo := 1;
        d[i, j] := lokMinimum(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + costo);
      end;
    end;
    result := d[n, m];
  except
    on E: Exception do
    begin
      FInter.DoLog('E', 'BafLevenshtein - Error: ' + E.Message);
      result := -1;
    end;
  end;
end;

function TBafVarModule.BafPos(AParams: TStrings): string;
// 0 - String to search
// 1 - String to seachr in
// 2 - Options
var
  s0, s1: string;
begin
  if AParams.Count > 1 then begin
    s0 := AParams[0];
    s1 := AParams[1];
    if AParams.Count > 2 then begin
      if AnsiCompareText(AParams[2], 'ic') = 0 then begin
        s0 := AnsiUpperCase(s0);
        s1 := AnsiUpperCase(s1);
      end;
    end;
    result := IntToStr(Pos(s0, s1));
  end
  else
    FInter.DoLog('E', '$POS - Number of Params less 2');
end;

function TBafVarModule.BafRandom(AParams: TStrings): string;
var
  LTyp, LValueMin, LValueMax: string;
  LIntMin, LIntMax, LTextNum: integer;
  LDateMin, LDateMax: TDate;
  LCurrMin, LCurrMax: currency;
  LText: TStringList;
  LHelper: TBafComboHelper;
begin
  if AParams.Count > 0 then begin
    LTyp := AnsiLowerCase(AParams[0]);
    if AParams.Count > 1 then
      LValueMin := AParams[1];
    if AParams.Count > 2 then
      LValueMax := AParams[2];

    if LTyp = 'int' then begin
      LIntMin := StrToIntDef(LValueMin, 0);
      LIntMax := StrToIntDef(LValueMax, 100);
      result := IntToStr(random(1 + LIntMax - LIntMin) + LIntMin);
    end
    else if LTyp = 'date' then begin
      LDateMin := StrToDateDef(LValueMin, now - 7);
      LDateMax := StrToDateDef(LValueMax, now + 7);
      result := FormatDateTime(BAFDATEFMT, (random(1
          + round(LDateMax) - round(LDateMin)) + round(LDateMin)));
    end
    else if LTyp = 'curr' then begin
      LCurrMin := StrToCurrDef(LValueMin, 0);
      LCurrMax := StrToCurrDef(LValueMax, 100);
      result := FormatFloat('0.00', (LCurrMax - LCurrMin) * random + LCurrMin);
    end
    else if LTyp = 'curr4' then begin
      LCurrMin := StrToCurrDef(LValueMin, 0);
      LCurrMax := StrToCurrDef(LValueMax, 100);
      result := CurrToStr((LCurrMax - LCurrMin) * random + LCurrMin);
    end
    else if copy(LTyp, 1, 4) = 'text' then begin
      LTextNum := StrToIntDef(copy(LTyp, 5, MaxInt), 1);
      LText := GetTextStringList(LTextNum);
      result := LText[random(LText.Count)];
    end
    else if LTyp = 'ld' then begin
      if LValueMin <> '' then begin
        LHelper := gvBafDataCache.Lookup[LValueMin];
        result := LHelper.GetGuidFromIndex(random(LHelper.Count));
      end;
    end
    else if LTyp = 'ldv' then begin
      if LValueMin <> '' then begin
        LHelper := gvBafDataCache.Lookup[LValueMin];
        result := LHelper.Strings[random(LHelper.Count)];
      end;
    end
    else if LTyp = 'ls' then begin
      if LValueMin <> '' then begin
        LHelper := gvBafDataCache.Special[LValueMin];
        result := LHelper.GetGuidFromIndex(random(LHelper.Count));
      end;
    end
    else if LTyp = 'lsv' then begin
      if LValueMin <> '' then begin
        LHelper := gvBafDataCache.Special[LValueMin];
        result := LHelper.Strings[random(LHelper.Count)];
      end;
    end;

  end;
// function TBafVarModule.BafRandom
end;

function TBafVarModule.BafREgex(AParams: TStrings): string;
begin
  if AParams.Count > 1 then begin
    result := IfThen(TRegEx.IsMatch(Trim(AParams[0]), Trim(AParams[1])), 'Y', 'N');
  end
  else
    FInter.DoLog('E', '$REGEX - Number of Params less 2');
end;

function TBafVarModule.BafTrim(AParams: TStrings): string;
var
  LTrim: string;
begin
  if AParams.Count > 0 then begin
    if AParams.Count > 1 then
      LTrim := AnsiUpperCase(AParams[1]);
    case (LTrim + ' ')[1] of
      'L': result := TrimLeft(AParams[0]);
      'R': result := TrimRight(AParams[0]);
    else
      result := Trim(AParams[0]);
    end;
  end
  else
    FInter.DoLog('E', '$TRIM - Number of Params less 1');
end;

function TBafVarModule.BafXmlReplace(AParams: TStrings): string;
var
  i: integer;
  s: string;
begin
  s := AParams[0];
  result := '';
  for i := 1 to Length(s) do begin
    case s[i] of
      '&': result := result + '&amp;';
      '<': result := result + '&lt;';
      '>': result := result + '&gt;';
      '"': result := result + '&quot;';
      Chr(39): result := result + '&apos;';
      else
        result := result + s[i];
    end;
  end;
end;

function TBafVarModule.BafXmlReReplace(AParams: TStrings): string;
var
  i: integer;
  s: string;
begin
  s := AParams[0];
  result := '';
  i := 1;
  while i <= Length(s) do begin
    if s[i] = '&' then begin
      if copy(s, i, 5) = '&amp;' then begin
        result := result + '&';
        inc(i, 4);
      end
      else if copy(s, i, 4) = '&lt;' then begin
        result := result + '<';
        inc(i, 3);
      end
      else if copy(s, i, 4) = '&gt;' then begin
        result := result + '>';
        inc(i, 3);
      end
      else if copy(s, i, 6) = '&quot;' then begin
        result := result + '"';
        inc(i, 5);
      end
      else if copy(s, i, 6) = '&apos;' then begin
        result := result + Chr(39);
        inc(i, 5);
      end
      else if copy(s, i, 8) = '&#x20AC;' then begin
        result := result + '?';
        inc(i, 7);
      end
      else if copy(s, i, 7) = '&#x142;' then begin
        result := result + '?';
        inc(i, 6);
      end
      else
        result := result + s[i];
    end
    else if CharInSet(s[i], ['Ã', 'Â'])  then begin
      if copy(s, i, 8) = 'Ã&#x153;' then begin
        result := result + 'Ü';
        inc(i, 7);
      end
      else if copy(s, i, 7) = 'Â&#x81;' then begin
        result := result + 'ü';
        inc(i, 6);
      end





      else
        result := result + s[i];
    end
    else
      result := result + s[i];
    inc(i);
  end;
end;

procedure TBafVarModule.BafZipCreate;
var
  LZipFile: TAbZipper;
  LFileName, LPassword, LDir: string;
  i, LCount, LList: integer;
  LFileList: TStringList;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LZipFile := TAbZipper.Create(nil);
    try
      LDir := FindParamStringReplaced('dir', '');
      LFileName := FindParamStringReplaced('fn', '');
      if LFileName <> '' then begin
        if FindParamBooleanReplaced('pwc', false) then
          LPassword := BafDecrypt(FindParamStringReplaced('pw', ''))
        else
          LPassword := FindParamStringReplaced('pw', '');
        if Trim(LPassword) <> '' then
          LZipFile.Password := LPassword;
        if LDir <> '' then
          LZipFile.BaseDirectory := LDir;
        LZipFile.FileName := LFileName;
        try
          LList := FindParamIntegerReplaced('list', 0);
          if LList > 0 then begin
            LFileList  := GetTextStringList(LList);
            for i := 0 to LFileList.Count - 1 do begin
              LFileName := LFileList[i];
              if FileExists(LFileName) then
                LZipFile.AddFiles(LFileName, 0);
            end;
          end
          else begin
            LCount := FindParamIntegerReplaced('cnt', 0);
            for i := 1 to LCount do begin
              LFileName := FindParamStringReplaced('fn' + IntToStr(i), '');
              if FileExists(LFileName)
                  or FileExists(IncludeTrailingPathDelimiter(LDir) + LFileName) then
                LZipFile.AddFiles(LFileName, 0);
            end;
          end;
        finally
          LZipFile.Save;
          LZipFile.CloseArchive;
        end;
      end
      else
        FInter.DoLog('E', '#zip_create - no FileName (fn)');
    finally
      LZipFile.Free;
    end;
  end;
end;

procedure TBafVarModule.BatchExec;
// Speichert den Text n in batch.bat und führt diese aus
var
  LFileName: string;
  LIndex: integer;
  LText: TStringList;
begin
  LFileName := FInter.NeedInfo('usrroot', '') + 'batch.bat';
  LIndex := FindParamInteger('n', 0);
  LText := GetTextStringList(LIndex);
  LText.SaveToFile(LFileName);
  BafOpenFile(LFileName);
  if FindParamBooleanReplaced('clr', true) then
    LText.Clear;
end;

function TBafVarModule.BCalc(AParams: TStrings): string;
// 0 - operator
// 1 - value 1
// 2 - value 2
// optional 3 and more: further value
var
  LOp: string;
  LResult: boolean;
  i: integer;
begin
  if AParams.Count > 2 then begin
    LOp := AnsiLowerCase(AParams[0]);
    LResult := false;
    if LOp = 'and' then begin
      LResult := true;
      for i := 1 to AParams.Count - 1 do begin
        if not BafIsYesChar(AParams[i]) then begin
          LResult := false;
          Break;
        end;
      end;
    end
    else if LOp = 'or' then begin
      LResult := false;
      for i := 1 to AParams.Count - 1 do begin
        if BafIsYesChar(AParams[i]) then begin
          LResult := true;
          Break;
        end;
      end;
    end
    else if LOp = 'xor' then
      LResult := BafIsYesChar(AParams[1]) xor BafIsYesChar(AParams[2]);
    result := IfThen(LResult, BAFYESCHAR, BAFNOCHAR);
  end
  else
    FInter.DoLog('E', '$BCALC - number of params too low');
end;

function TBafVarModule.BFmt(AParams: TStrings): string;
// 0 - boolean value
// 1 - result Y
// 2 - result N
var
  LResult: boolean;
begin
  LResult := false;
  if AParams.Count > 0 then
    LResult := BafIsYesChar((AParams[0] + ' ')[1]);
  if LResult then begin
    if AParams.Count > 1 then
      result := AParams[1]
    else
      result := BAFYESCHAR;
  end
  else begin
    if AParams.Count > 2 then
      result := AParams[2]
    else
      result := BAFNOCHAR;
  end;
end;

function TBafVarModule.CCalc(AParams: TStrings): string;
// 0 - operator
// 1 - number 1
// 2 - numer 2
// optional 3 and more: further numbers
var
  LOp: string;
  LResult: currency;
  i: integer;
begin
  if AParams.Count > 2 then begin
    LOp := AParams[0] + '2';
    LResult := 0;
    for i := 1 to AParams.Count - 1 do
      AParams[i] := StringReplace(AParams[i], '.', ',', []);
    if LOp[1] = '+' then begin
      for i := 1 to AParams.Count - 1 do
        LResult := LResult + StrToCurr(AParams[i]);
    end
    else if LOp[1] = '*' then begin
      LResult := 1;
      for i := 1 to AParams.Count - 1 do
        LResult := LResult * StrToCurr(AParams[i]);
    end
    else if LOp[1] = '-' then begin
      LResult := StrToCurr(AParams[1]);
      for i := 2 to AParams.Count - 1 do
        LResult := LResult - StrToCurr(AParams[i]);
    end
    else if LOp[1] = '/' then begin
      LResult := StrToCurr(AParams[1]);
      for i := 2 to AParams.Count - 1 do
        LResult := LResult / StrToCurr(AParams[i]);
    end
    else if LOp[1] = '%' then begin
      LResult := 100 * StrToCurr(AParams[1]);
      for i := 2 to AParams.Count - 1 do
        LResult := LResult / StrToCurr(AParams[i]);
    end
    else if (LOp[1] = 'B') or (LOp[1] = 'b') then begin
      LResult := StrToCurr(AParams[1]);
      LResult := LResult * ((100 + StrToCurr(AParams[2])) / 100);
    end
    else if (LOp[1] = 'E') or (LOp[1] = 'e') then begin
      LResult := StrToCurr(AParams[1]);
      LResult := LResult - (100 * LResult / (100 + StrToCurr(AParams[2])));
    end
    else if (LOp[1] = 'M') or (LOp[1] = 'm') then begin
      LResult := StrToCurr(AParams[1]);
      LResult := LResult * (StrToCurr(AParams[2]) / 100);
    end
    else if (LOp[1] = 'N') or (LOp[1] = 'n') then begin
      LResult := StrToCurr(AParams[1]);
      LResult := 100 * LResult / (100 + StrToCurr(AParams[2]));
    end;
    if LOp[2] = '4' then
      result := FormatCurr('0.0000', LResult)
    else
      result := FormatCurr('0.00', LResult);
  end
  else
    FInter.DoLog('E', '$ICALC - number of params too low');
end;

function TBafVarModule.CFmt(AParams: TStrings): string;
var
  LResult: currency;
  LFormat: string;
  LHideNull: boolean;
begin
  LHideNull := false;
  LResult := 0;
  LFormat := '0.00';
  if AParams.Count > 0 then
    LResult := StrToCurrDef(AParams[0], 0);
  if AParams.Count > 1 then
    LFormat := AParams[1];
  if AParams.Count > 2 then
    LHideNull := UpperCase(Trim(AParams[2])) = 'HN';
  if LHideNull and (AParams[0] = '') then
    result := ''
  else
    result := FormatCurr(LFormat, LResult);
end;

procedure TBafVarModule.CsvCheck(AIndex: integer);
var
  LText, LHeaderText: TStringList;
  LTyp, LResultText: string;
  LResult: boolean;

  procedure lokCheckHeader;
  var
    LCount, i: integer;
    s, LSep: string;
  begin
    LResult := true;
    LResultText := '';
    LSep := FindParamStringReplaced('sep', ';') + ' ';
    LHeaderText.Delimiter := LSep[1];
    LHeaderText.StrictDelimiter := true;
    LHeaderText.DelimitedText := AnsiLowerCase(LText[0]);

    LCount := FindParamIntegerReplaced('cnt', 0);
    for i := 1 to LCount do begin
      s := FindParamStringReplacedLower('h' + IntToStr(i), '');
      if LHeaderText.IndexOf(s) = -1 then begin
        LResult := false;
        LResultText := LResultText + s + LSep[1];
      end;
    end;
  end; // procedure lokCheckHeader

begin
  LText := GetCsvList(AIndex);
  LHeaderText := GetCsvHeaderList(AIndex);
  LTyp := FindParamStringReplacedLower('', 'header');
  if LTyp = 'header' then
    lokCheckHeader

  ;
  FExecInter.SetVarOrValue('n', IfThen(LResult, BAFYESCHAR, BAFNOCHAR));
  FExecInter.SetVarOrValue('res', LResultText);

// procedure TBafVarModule.CsvCheck
end;

procedure TBafVarModule.CheckString;
var
  LValue, LTyp, LSuc, LMsg, LMessage: string;
  LSuccess: boolean;
  LNum: integer;

  procedure lokTtDeRp(AFirstChar: Char);
  var
    i: integer;
    s, t: string;
  begin
    if Length(LValue) <> 9 then begin
      LMessage := Format('Länge %d Zeichen - ', [Length(LValue)]);
      LSuccess := false;
    end;
    if (Length(LValue) > 0) and (LValue[1] <> AFirstChar) then begin
      LMessage := LMessage + Format('Erstes Zeichen nicht "%s" - ', [AFirstChar]);
      LSuccess := false;
    end;
    s := '';
    t := '';
    for i := 1 to Length(LValue) do begin
      if (LValue[i] = AnsiLowerCase(LValue[i])) and not CharInSet(LValue[i], ['0'..'9']) then begin
        s := s + ', ' + LValue[i];
        LSuccess := false;
      end
      else if not CharInSet(LValue[i], ['0'..'9', 'C', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
          'N', 'P', 'R', 'T', 'V', 'W', 'X', 'Y', 'Z']) then begin
        t := t + ', ' + LValue[i];
        LSuccess := false;
      end;
    end;
    if Length(s) > 0 then
      LMessage := LMessage + 'Zeichen in Kleinschreibung: ' + copy(s, 3, MaxInt) + ' - ';
    if Length(t) > 0 then
      LMessage := LMessage + 'Unzulässige Zeichen: ' + copy(t, 3, MaxInt) + ' - ';
  end; // procedure lokTtDeRp

begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LValue := FindParamStringReplaced('z', '');
    LTyp := FindParamStringReplacedLower('y', '');
    LSuc := FindParamStringReplaced('suc', '');
    LMsg := FindParamStringReplaced('msg', '');
    LSuccess := true;
    if LTyp = 'tt_de_rp' then
      lokTtDeRp('C')
    else if LTyp = 'tt_de_pa' then
      lokTtDeRp('L')
    ;
    LNum := StrToIntDef(LSuc, -1);
    if LNum > 0 then
      FExecInter.Values[LNum] := IfThen(LSuccess, BAFYESCHAR, BAFNOCHAR)
    else if LSuc <> '' then
      FInter.Variable[LSuc] := IfThen(LSuccess, BAFYESCHAR, BAFNOCHAR);
    LNum := StrToIntDef(LMsg, -1);
    if LNum > 0 then
      FExecInter.Values[LNum] := LMessage
    else if LSuc <> '' then
      FInter.Variable[LMsg] := LMessage;
  end;
end;

procedure TBafVarModule.ClearCmd(AIndex: integer);
begin
  GetCmdList(AIndex).Clear;
  if Trim(FExecInter.LineP) <> '' then
    GetCmdList(AIndex).Add(FExecInter.LineP);
end;

procedure TBafVarModule.ClearCmds;
begin
  FCmdList.Clear;
end;

procedure TBafVarModule.ClearText(AIndex: integer);
var
  s: string;
begin
  GetTextStringList(AIndex).Clear;
  s := FExecInter.ReplaceFunctions(FExecInter.LineP);
  s := FExecInter.ReplaceFunctions(s);
  if Trim(s) <> '' then
    GetTextStringList(AIndex).Add(s);
end;

function TBafVarModule.Convert(AParams: TStrings): string;
// 1 - Konvertierungsfunktion
// 2 - Konvertierungsdaten
var
  LFunction: string;
begin
  LFunction := AnsiLowerCase(AParams[0]);
  if LFunction = 'isodate' then
    result := BafConvert(cfIsoDate, AParams[1])
  else if LFunction = 'isodatetime' then
    result := BafConvert(cfIsoDateTime, AParams[1])
  else if LFunction = 'truefalse' then
    result := BafConvert(cfTrueFalse, AParams[1])
  else if LFunction = 'pointfloat' then
    result := BafConvert(cfPointFloat, AParams[1])
  else if LFunction = 'urlcode' then
    result := BafConvert(cfUrlEncode, AParams[1])
  else if LFunction = 'utf8' then
    result := BafConvert(cfUtf8, AParams[1])
  else
    result := AParams[1];
end;

constructor TBafVarModule.Create;
begin
  inherited;
  FTextList := TObjectList.Create(true);
  FKatList := TObjectList.Create(true);
  FCsvList := TObjectList.Create(true);
  FIniList := TObjectList.Create(true);
  FCsvHeaderList := TObjectList.Create(true);
  FCsvZeileList := TObjectList.Create(true);
  FSepLineList := TObjectList.Create(true);
  FSepLineValue := TStringList.Create;
  FNamedVars := TStringList.Create;
  FCode := TStringList.Create;
  FCmdList := TObjectList.Create(true);
  FRightDefs := TStringList.Create;
end;

procedure TBafVarModule.CsvLine(AIndex: integer);
var
  i, LRow, LFirst, LRowCount, LMax: integer;
  LBafConName, LSep, LEachRow: string;
  LEachRowTrans, LNoException, LTrim: boolean;
  LText, LHeaderText, LZeileText: TStringList;
begin
  LText := GetCsvList(AIndex);
  LHeaderText := GetCsvHeaderList(AIndex);
  LZeileText := GetCsvZeileList(AIndex);
  LSep := FindParamStringReplaced('sep', ';') + ' ';
  LZeileText.Delimiter := LSep[1];
  LZeileText.StrictDelimiter := true;
  LHeaderText.Delimiter := LSep[1];
  LMax := FindParamIntegerReplaced('m', MaxInt);
  LEachRow := FindParamStringReplaced('er', '');
  LEachRowTrans := FindParamBooleanReplaced('ert', false);
  LNoException := FindParamBooleanReplaced('nex', false);
  LBafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
  LTrim := FindParamBooleanReplaced('trim', false);
  if (LEachRow <> '') and (LText.Count > 0) then begin
    LRowCount := 0;
    LHeaderText.Clear;
    LFirst := 0;
    if FindParamBoolean('hhr', false) then begin
      LFirst := 1;
      LHeaderText.StrictDelimiter := true;
      LHeaderText.DelimitedText := AnsiLowerCase(LText[0]);
    end;

    for LRow := LFirst to LText.Count - 1 do begin
      LZeileText.DelimitedText := LText[LRow];
      if LTrim then begin
        for i := 0 to LZeileText.Count - 1 do
          LZeileText[i] := Trim(LZeileText[i]);
      end;
      FExecInter.EachRow(LBafConName, LEachRow, '#csvline', LEachRowTrans,
          LNoException);
      inc(LRowCount);
      if LRowCount >= LMax then begin
        FInter.DoLog('I', Format('#csvline, Max (%d) reached, execution aborted', [LMax]));
        Break;
      end;
    end;
  end;
// procedure TBafVarModule.CsvLine
end;

procedure TBafVarModule.CsvOpen(AIndex: integer);
var
  LFileName, s: string;
  i, LTextNum: integer;
  LInQuote: boolean;
  LText, LHeaderText: TStringList;
  LEncoding: TEncoding;
begin
  LText := GetCsvList(AIndex);
  LFileName := FindParamStringReplaced('fn', '');
  LTextNum := FindParamIntegerReplaced('txt', 0);
  LEncoding := BafGetEncoding(FindParamStringReplaced('e', ''));
  if FileExists(LFileName) then
    LText.LoadFromFile(LFileName, LEncoding)
  else if LTextNum > 0 then
    LText.Text := GetTextStringList(LTextNum).Text
  else
    FInter.DoLog('E', '#csv_open - Datei existiert nicht: ' + LFileName);

  if FindParamBoolean('ber', false) then begin    // replace line breaks in fields through blanks
    s := LText.Text;
    LInQuote := false;
    for i := 1 to Length(s) do begin
      if s[i] = '"' then
        LInQuote := not LInQuote;
      if LInQuote and CharInSet(s[i], [#13, #10]) then
        s[i] := ' ';
    end;
    LText.Text := s;
  end;

  LHeaderText := GetCsvHeaderList(AIndex);
  if FindParamBoolean('hhr', false) then begin
    LHeaderText.StrictDelimiter := true;
    LHeaderText.DelimitedText := AnsiLowerCase(LText[0]);
  end;

end;

destructor TBafVarModule.Destroy;
begin
  FreeandNil(FRightDefs);
  FreeandNil(FCmdList);
  FreeandNil(FCode);
  FreeandNil(FNamedVars);
  FreeandNil(FSepLineList);
  FreeandNil(FSepLineValue);
  FreeandNil(FCsvZeileList);
  FreeandNil(FCsvHeaderList);
  FreeandNil(FIniList);
  FreeandNil(FCsvList);
  FreeandNil(FKatList);
  FreeandNil(FTextList);
  inherited;
end;

procedure TBafVarModule.DoOpenFile;
var
  LFileName: string;
begin
  LFileName := FindParamStringReplaced('fn', '');
  if LFileName = '' then
    FInter.DoLog('E', '#openfile, fn: ' + LFileName)
  else
    BafOpenFile(LFileName);
end;

function TBafVarModule.EmptyV(ANotEmpty: boolean; AParams: TStrings): string;
var
  LValue: string;
begin
  if AParams.Count > 0 then begin
    LValue := FInter.Variable[AParams[0]];
    if ANotEmpty then
      result := IfThen(Trim(LValue) = '', BAFNOCHAR, BAFYESCHAR)
    else
      result := IfThen(Trim(LValue) = '', BAFYESCHAR, BAFNOCHAR);
  end
  else
    FInter.DoLog('E', '$EMPTYVAR(n) / $NEMPTYVAR(n) - number of params low');
end;

procedure TBafVarModule.Exec;
var
  LCmd, LFinally, LExcept: string;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LCmd := FindParamStringReplaced('cmd', '');
    LFinally := FindParamStringReplaced('fin', '');
    LExcept := FindParamStringReplaced('ex', '');
    if LExcept <> '' then begin
      try
        TBafInterpreterLevel.ExecInNewLevel(LCmd, FExecInter, FInter);
      except
        TBafInterpreterLevel.ExecInNewLevel(LExcept, FExecInter, FInter);
        raise;
      end;
    end
    else if LFinally <> '' then begin
      try
        TBafInterpreterLevel.ExecInNewLevel(LCmd, FExecInter, FInter);
      finally
        TBafInterpreterLevel.ExecInNewLevel(LFinally, FExecInter, FInter);
      end;
    end
    else
      TBafInterpreterLevel.ExecInNewLevel(LCmd, FExecInter, FInter);
  end;
end;

procedure TBafVarModule.ExecAsync;
begin
  FInter.CommandOnTimer('#exec ' + FExecInter.LineP);
end;

function TBafVarModule.GetBetween(AInfo: TBafNumInfo; AParams: TStrings): string;
var
  LValue, LValue1, LValue2: double;
  i: integer;

  function lokGetValue(AIndex: integer): double;
  begin
    result := 0;
    case AInfo of
      niNumBetween: result := StrToFloatDef(AParams[AIndex], 0);
      niDateBetween: result := StrToDateTimeDef(AParams[AIndex], 0);
    end;
  end; // function lokGetValue

begin
  if AParams.Count > 2 then begin
    LValue := lokGetValue(0);
    LValue1 := lokGetValue(1);
    LValue2 := lokGetValue(2);
    result := IfThen((LValue >= LValue1) and (LValue <= LValue2),
        BAFYESCHAR, BAFNOCHAR);
    if result = BAFYESCHAR then
      exit;
    for i := 3 to AParams.Count -1 do begin
      LValue1 := lokGetValue(i);
      if LValue = LValue1 then begin
        result := BAFYESCHAR;
        exit;
      end;
    end;
  end
  else begin
    case AInfo of
      niNumBetween: FInter.DoLog('E', '$NUMBETWEEN - less then 3 parameters');
      niDateBetween: FInter.DoLog('E', '$DATEBETWEEN - less then 3 parameters');
    end;
  end;
end;

function TBafVarModule.GetChar(AParams: TStrings): string;
var
  s: string;
begin
  result := '';
  if AParams.Count > 0 then begin
    s := AnsiLowerCase(AParams[0]);
    if (s = 'crlf') or (s = 'lfcr') then
      result := #13#10
    else if s = 'cr' then
      result := #13
    else if s = 'cr' then
      result := #10
    else if s = 'lf' then
      result := #13
    else if s = 'tab' then
      result := #9
    else if s = 'quote' then
      result := #39
    else if s = 'dquote' then
      result := #34
    else if (s = 'space') or (s = 'leer') then
      result := ' '
    else if (s = 'comma') or (s = 'komma') then
      result := ','
    else if (s = 'questionmark') or (s = 'qmark') or (s = 'frage')
        or (s = 'fragezeichen') then
      result := '?'
    else if (s = 'bro') or (s = 'bracket_open')  then
      result := '('
    else if (s = 'brc') or (s = 'bracket_close')  then
      result := ')'
    else if s = 'dollar' then
      result := '$'

    else
      result := Chr(StrToIntDef(s, 0));
  end;
end;

function TBafVarModule.GetClrDeadline(AInfo: TBafColorInfo; AParams: TStrings): string;
// 0 - ceadline
// 1 - date to compare with deadline
// 2 - days before deadline "yellow"
var
  LDate, LDeadline: TDate;
  LGelb: integer;
  s, LFarbeGelb, LFarbeRot: string;
begin
  if AParams.Count < 3 then
    result := 'aqua'
  else begin
    LDeadline := StrToDateDef(AParams[0], 0);
    s := Trim(AnsiUpperCase(AParams[1]));
    if (Length(s) = 1) and CharInSet(s[1], ['H', 'T']) then
      LDate := trunc(now)
    else if (Length(s) = 1) and CharInSet(s[1], ['M']) then
      LDate := trunc(now) + 1
    else if (Length(s) = 1) and CharInSet(s[1], ['G', 'Y']) then
      LDate := trunc(now) - 1
    else
      LDate := StrToDateDef(s, 0);
    LGelb := StrToIntDef(AParams[2], 7);
    case AInfo of
      ciDeadlineYR: begin
        LFarbeGelb := 'yellow';
        LFarbeRot := 'red';
      end;
    end;
    result := '';
    if LDate >= LDeadline then
      result := LFarbeRot
    else if LDate >= (LDeadline - LGelb) then
      result := LFarbeGelb;
  end;
end;

function TBafVarModule.GetCode(AClear: boolean): string;
begin
  result := StringReplace(FCode.Text, #13#10, '   ', [rfReplaceAll]);
  if AClear then
    FCode.Clear;
end;

function TBafVarModule.GetCsvHeaderList(AIndex: integer): TStringList;
begin
  while AIndex > FCsvHeaderList.Count do
    FCsvHeaderList.Add(TStringList.Create);
  result := (FCsvHeaderList[AIndex - 1] as TStringList);
end;

function TBafVarModule.GetCsvLine(AParams: TStrings): string;
//1. number of csv file
//2. name of the line
var
  LText, LZeile, LHeader: TStringList;
  ix, LCol, LFrom, LLength: integer;
  LName: string;
begin
  if AParams.Count > 1 then begin
    ix := StrToIntDef(AParams[0], -1);
    if ix >= 0 then begin
      result := '';
      LText := GetCsvList(ix);
      LName := AnsiLowerCase(AParams[1]);
      LZeile := GetCsvZeileList(ix);
      LHeader := GetCsvHeaderList(ix);
      if LName = 'header' then begin
        if Assigned(LHeader) then
          result := LHeader.DelimitedText;
      end
      else begin
        if Assigned(LZeile) then
          result := LZeile.DelimitedText;
      end;
    end
    else
      FInter.DoLog('E', '$CSV_LINE(ix, n) - ix missing or lower 0');
  end
  else
    FInter.DoLog('E', '$CSV_LINE(ix, n) - number of params low');
end;

function TBafVarModule.GetCsvList(AIndex: integer): TStringList;
begin
  while AIndex > FCsvList.Count do
    FCsvList.Add(TStringList.Create);
  result := (FCsvList[AIndex - 1] as TStringList);
end;

function TBafVarModule.GetCsvValue(AParams: TStrings): string;
//1. number of csv file
//2. Index or Name of column
//3. optional: vfromon
//4. optional: Length
var
  LZeile, LHeader: TStringList;
  ix, LCol, LFrom, LLength: integer;
begin
  if AParams.Count > 1 then begin
    ix := StrToIntDef(AParams[0], -1);
    if ix >= 0 then begin
      result := '';
      LZeile := GetCsvZeileList(ix);
      LHeader := GetCsvHeaderList(ix);
      LCol := StrToIntDef(AParams[1], -1);
      if LCol = -1 then begin
        LCol := LHeader.IndexOf(AnsiLowerCase(Trim(AParams[1])));
        if (LCol < LZeile.Count) and (LCol >= 0) then
          result := LZeile[LCol]
        else
          FInter.DoLog('E', '$CSV(ix, n) - Column n not found: ' + AParams[1]);
      end
      else begin
        if LCol < LZeile.Count then
          result := LZeile[LCol]
        else
          FInter.DoLog('E', '$CSV(ix, n) - n must be less then the number of columns');
      end;
      LFrom := -1;
      LLength := MaxInt;
      if AParams.Count > 2 then
        LFrom := StrToIntDef(AParams[2], -1);
      if AParams.Count > 3 then
        LLength := StrToIntDef(AParams[3], MaxInt);
      if LFrom > 0 then
        result := copy(result, LFrom, LLength);
    end
    else
      FInter.DoLog('E', '$CSV(ix, n) - ix missing or lower 0');
  end
  else
    FInter.DoLog('E', '$CSV(ix, n) - number of params low');
end;

function TBafVarModule.GetCsvZeileList(AIndex: integer): TStringList;
begin
  while AIndex > FCsvZeileList.Count do
    FCsvZeileList.Add(TStringList.Create);
  result := (FCsvZeileList[AIndex - 1] as TStringList);
end;

function TBafVarModule.GetCurr(AParams: TStrings): string;
begin
  if AParams.Count > 0 then
    result := AParams[0];
  if AParams.Count > 1 then
    result := result + '.' + AParams[1];
end;

function TBafVarModule.GetDateFormated(AParams: TStrings): string;
var
  LDate: TDateTime;
begin
  if AParams.Count > 1 then begin
    LDate := StrToDateTimeDef(AParams[0], 0);
    if (AParams.Count > 2)   and (AnsiUpperCase(AParams[2]) = 'HN')
        and (LDate <= 1) then
      result := ''
    else
      result := FormatDateTime(AParams[1], LDate);
  end
  else
    FInter.DoLog('E', '$DFMT(date, fmt) - number of params low');
end;

function TBafVarModule.GetDateMinMaxRel(AInfo: TBafNumInfo; ANum, ANum2: string): string;
// compares the date in ANum with the current date, added with Num2
var
  LDate, LDate2: TDateTime;
begin
  result := '';
  LDate := StrToDateTimeDef(ANum, 0);
  LDate2 := now + StrToIntDef(ANum2, 0);
  case AInfo of
    niDateMin: result := IfThen(LDate < LDate2, BAFNOCHAR, BAFYESCHAR);
    niDateMax: result := IfThen(LDate > LDate2, BAFNOCHAR, BAFYESCHAR);
  end;
end;

function TBafVarModule.GetGuid(AParams: TStrings): string;
begin
  result := BafGetGuid;
  if AParams.Count > 0 then
    FInter.Variable[AParams[0]] := result;
end;

function TBafVarModule.GetHash(AParams: TStrings): string;
var
  LValue, LHash: string;
begin
  if AParams.Count > 1 then begin
    LValue := AParams[0];
    LHash := AnsiUpperCase(APArams[1]);
    if (LHash = 'SHA512') then
      result := THashSHA2.GetHashString(LValue, THashSHA2.TSHA2Version.SHA512)
    else if (LHash = 'SHA512_256') then
      result := THashSHA2.GetHashString(LValue, THashSHA2.TSHA2Version.SHA512_256)
    else if (LHash = 'SHA512_224') then
      result := THashSHA2.GetHashString(LValue, THashSHA2.TSHA2Version.SHA512_224)
    else if (LHash = 'SHA384') then
      result := THashSHA2.GetHashString(LValue, THashSHA2.TSHA2Version.SHA384)
    else if (LHash = 'SHA256') then
      result := THashSHA2.GetHashString(LValue, THashSHA2.TSHA2Version.SHA256)
    else if (LHash = 'SHA224') then
      result := THashSHA2.GetHashString(LValue, THashSHA2.TSHA2Version.SHA224)
    else if (LHash = 'SHA1') then
      result := THashSHA1.GetHashString(LValue)
    else if (LHash = 'MD5') then
      result := THashMD5.GetHashString(LValue);
  end;
end;

function TBafVarModule.GetIni(AIndex: integer): TStringIniFile;
begin
  while AIndex > FIniList.Count do
    FIniList.Add(TStringIniFile.Create(''));
  result := (FIniList[AIndex - 1] as TStringIniFile);
end;

function TBafVarModule.GetIniText(AParam: string): string;
var
  ix: integer;
begin
  ix := StrToIntDef(AParam, -1);
  if ix >= 0 then
    result := GetIni(ix).AsString
  else if AnsiCompareText(AParam, 'root') = 0 then
    result := dataMain.Ini.AsString
  else if AnsiCompareText(AParam, 'usr') = 0 then
    result := dataMain.UserIni.AsString
  else
    FInter.DoLog('E', '$INITEXT(ix) - ix incorrect');
end;

function TBafVarModule.GetIniValue(AParams: TStrings): string;
// 0 - Name or Index of the ini
// 1 - item
// 2 - optional: section; default values
// 3 - optional: default value
var
  ix: integer;
  LIniName: string;
  LIni: TStringIniFile;

  function lokVal(AIndex: integer; LDefault: string): string;
  begin
    if AParams.Count > AIndex then
      result := AParams[AIndex]
    else
      result := LDefault;
    if result = 'db!' then
      result := dataMain.DBName;
  end;

begin
  if AParams.Count > 1 then begin
    LIni := nil;
    LIniName := AnsiLowerCase(AParams[0]);
    if LIniName = 'usr' then
      LIni := dataMain.UserIni
    else if LIniName = 'root' then
      LIni := dataMain.Ini
    else begin
      ix := StrToIntDef(LIniName, -1);
      if ix >= 0 then
        LIni := GetIni(ix);
    end;
    if Assigned(LIni) then
      result := LIni.ReadString(lokVal(2, 'values'), AParams[1], lokVal(3, ''))
    else
      FInter.DoLog('E', '$INI(ix, item, [sec], [default]) - ini not found');
  end
  else
    FInter.DoLog('E', '$INI(ix, item, [sec], [default]) - count of params less 2');
end;

function TBafVarModule.GetIntMinMax(AInfo: TBafNumInfo; ANum, ANum2: string): string;
var
  LInt, LInt2: integer;
begin
  result := '';
  LInt := StrToIntDef(ANum, MaxInt);
  LInt2 := StrToIntDef(ANum2, MaxInt);
  case AInfo of
    niIntMin: result := IfThen(LInt < LInt2, BAFNOCHAR, BAFYESCHAR);
    niIntMax: result := IfThen(LInt > LInt2, BAFNOCHAR, BAFYESCHAR);
  end;
end;

function TBafVarModule.GetInversLookup(AParams: TStrings): string;
// Params
// 0 - name of the looup
// 1 - value
// 2 - default result
var
  LHelper: TBafComboHelper;
begin
  if AParams.Count > 1 then begin
    LHelper := gvBafDataCache.Lookup[AParams[0]];
    result := LHelper.GetGuidFromCaption(AParams[1]);
    if (result = '') and (AParams.Count > 2) then
      result := AParams[2];
  end
  else
    FInter.DoLog('E', '$INVLOOKUP(name, value, default) - number of params less 2');
end;

function TBafVarModule.GetJSON(AParams: TStrings): string;
var
  LVar, LPath, LText: string;
  LIgnoreCase: boolean;
  LSep: Char;
begin
  if AParams.Count > 1 then begin
    LPath := AParams[1];
    LVar := AParams[0];
    LText := FInter.Variable[LVar];
    LIgnoreCase := false;
    LSep := '.';
    if AParams.Count > 2 then
      LIgnoreCase := BafIsYesChar(AParams[2]);
    if AParams.Count > 3 then
      LSep := (AParams[2] + '.')[1];
    result := BafJsonObjectParse(LText, LPath, LIgnoreCase, LSep);
  end
  else
    FInter.DoLog('E', '$JSON, number of params less 2');
end;

function TBafVarModule.GetKatStringList(AIndex: integer): TStringList;
var
  LList: TStringList;
begin
  while AIndex > FKatList.Count do begin
    LList := TStringList.Create;
    LList.OwnsObjects := true;
    LList.Sorted := true;
    LList.Duplicates := dupError;
    FKatList.Add(LList);
  end;
  result := (FKatList[AIndex - 1] as TStringList);
end;

function TBafVarModule.GetLookup(AParams: TStrings): string;
// Params
// 0 - name of the looup
// 1 - value
// 2 - default result
var
  LHelper: TBafComboHelper;
begin
  if AParams.Count > 1 then begin
    LHelper := gvBafDataCache.Lookup[AParams[0]];
    result := LHelper.GetCaptionGuid(AParams[1]);
    if (result = '') and (AParams.Count > 2) then
      result := AParams[2];
  end
  else
    FInter.DoLog('E', '$LOOKUP(name, value, default) - number of params less 2');
end;

function TBafVarModule.GetNumInfos(AInfo: TBafNumInfo; AParams: TStrings): string;
var
  LInt: integer;
  LCurr: currency;
begin
  result := '';
  LInt := StrToIntDef(AParams[0], MaxInt);
  LCurr := StrToCurrDef(AParams[0], MaxInt);
  case AInfo of
    niIsInt: result := IfThen(LInt = MaxInt, BAFNOCHAR, BAFYESCHAR);
    niIsCurr: result := IfThen(LCurr = MaxInt, BAFNOCHAR, BAFYESCHAR);
    niIntLen: begin
      if AParams[0] = '' then
        result := '0'
      else if LInt = MaxInt then
        result := '-1'
      else begin
        if (AParams.Count > 1) and (AnsiCompareText(AParams[1], 'sl') = 0) then
          result := IntToStr(Length(AParams[0]))
        else
          result := IntToStr(Length(IntToStr(LInt)));
      end;
    end; // case niIntLen
  end; // case
end;

function TBafVarModule.GetProc(AIndex: integer): string;
begin
  result := GetCmdList(AIndex).Text;
end;

function TBafVarModule.GetRightOfDef(AName: string): TBafRight;
var
  s: string;
begin
  result := brNone;
  if gvBafDataCache.RightIsAdmin then
    result := brWrite
  else begin
    s := FRightDefs.Values[AName] + ' ';
    case s[1] of
      'r': result := brRead;
      'w': result := brWrite;
    end;
  end;
end;

function TBafVarModule.GetCmdList(AIndex: integer): TStringList;
begin
  while AIndex > FCmdList.Count do
    FCmdList.Add(TStringList.Create);
  result := (FCmdList[AIndex - 1] as TStringList);
end;

function TBafVarModule.GetSepLine(AParam: string): string;
var
  ix: integer;
begin
  ix := StrToIntDef(AParam, -1);
  if ix >= 0 then begin
    while FSepLineValue.Count <= ix do
      FSepLineValue.Add('');
    result := FSepLineValue[ix];
  end
  else
    FInter.DoLog('E', '$SEPLINE(ix) - ix missed or below 0');
end;

function TBafVarModule.GetSepLineList(AIndex: integer): TStringList;
begin
  while AIndex > FSepLineList.Count do
    FSepLineList.Add(TStringList.Create);
  result := (FSepLineList[AIndex - 1] as TStringList);
end;

function TBafVarModule.GetTextStringList(AIndex: integer): TStringList;
begin
  while AIndex > FTextList.Count do
    FTextList.Add(TStringList.Create);
  result := (FTextList[AIndex - 1] as TStringList);
end;

function TBafVarModule.GetText(AParam: string): string;
var
  ix: integer;
begin
  ix := StrToIntDef(AParam, -1);
  if ix >= 0 then
    result := GetTextStringList(ix).Text
  else
    FInter.DoLog('E', '$TEXT(ix) - ix missing or less 0');
end;

function TBafVarModule.GetTextLine(AParams: TStrings): string;
var
  LText: TStringList;
  LParam, LSep: string;
  ix, LIndex: integer;
begin
  if AParams.Count = 0 then
    LParam := '1'
  else
    LParam := AParams[0];
  ix := StrToIntDef(LParam, 1);
  LText := GetTextStringList(ix);
  if AParams.Count > 1 then begin
    LParam := AnsiLowerCase(AParams[1]);
    if (LParam = 'cnt') or (LParam = 'count') then
      result := IntToStr(LText.Count)
    else if LParam = 'last' then begin
      if LText.Count = 0 then
        result := ''
      else
        result := LText[LText.Count - 1]
    end
    else if LParam = 'ix' then begin
      if AParams.Count > 2 then begin
        LIndex := LText.IndexOf(AParams[2]);
        if LIndex = -1 then
          result := ''
        else
          result := IntToStr(LIndex);
      end;
    end
    else if (LParam = 'as_line') or (LParam = 'asline') then begin
      if AParams.Count > 2 then
        LSep := AParams[2];
      result := StringReplace(LText.Text, #13#10, LSep, [rfReplaceAll]);
    end
    else begin
      ix := StrToIntDef(LParam, -1);
      if (ix = -1) or (ix >= LText.Count) then
        result := ''
      else
        result := LText[ix];
    end;
  end
  else
    result := LText.Text;
end;

function TBafVarModule.GetUserId(AParams: TStrings): string;
var
  LParam: string;
begin
  if AParams.Count > 0 then begin
    LParam := AnsiLowerCase(AParams[0]);
    if LParam = 'userguid' then
      result := dataMain.UserGuid
    else if LParam = 'userid' then
      result := dataMain.UserId;
  end;
  if result = '' then
    result := dataMain.GetDefaultUserIdent;
end;

function TBafVarModule.GetVar(AList: TStringList; AParams: TStrings): string;
var
  ix: integer;
  LInc, LVarName: string;
begin
  LVarName := AnsiLowerCase(AParams[0]);
  if Trim(LVarName) = '' then begin
    FInter.DoLog('E', '$VAR(name) - name ist leer');
    exit;
  end;
  ix := StrToIntDef(LVarName, -1234567);
  if ix = -1234567 then begin
    if AParams.Count > 1 then
    LInc := AnsiLowerCase(AParams[1]);
    if LInc = 'ib' then
      AList.Values[LVarName] := IntToStr(StrToIntDef(AList.Values[LVarName], 0) + 1);
    if LInc = 'db' then
      AList.Values[LVarName] := IntToStr(StrToIntDef(AList.Values[LVarName], 0) - 1);
    result := AList.Values[LVarName];
    if LInc = 'ia' then
      AList.Values[LVarName] := IntToStr(StrToIntDef(AList.Values[LVarName], 0) + 1);
    if LInc = 'da' then
      AList.Values[LVarName] := IntToStr(StrToIntDef(AList.Values[LVarName], 0) - 1);
  end
  else
    FInter.DoLog('E', '$VAR(name) - name is numeric');
end;

function TBafVarModule.GetVariable(AName: string): string;
begin
  result := FNamedVars.Values[AName];
end;

function TBafVarModule.ICalc(AParams: TStrings): string;
// 0 - operator
// 1 - number 1
// 2 - numer 2
// optional 3 and more: further numbers
var
  LOp: string;
  LResult: int64;
  i: integer;
begin
  if AParams.Count > 2 then begin
    LOp := AnsiLowerCase(AParams[0]);
    LResult := 0;
    if LOp = '+' then begin
      for i := 1 to AParams.Count - 1 do
        LResult := LResult + StrToInt64(AParams[i]);
    end
    else if LOp = '-' then
      LResult := StrToInt64(AParams[1]) - StrToInt64(AParams[2])
    else if LOp = '*' then
      LResult := StrToInt64(AParams[1]) * StrToInt64(AParams[2])
    else if LOp = 'div' then
      LResult := StrToInt64(AParams[1]) div StrToInt64(AParams[2])
    else if LOp = 'mod' then
      LResult := StrToInt64(AParams[1]) mod StrToInt64(AParams[2]);
    result := IntToStr(LResult);
  end
  else
    FInter.DoLog('E', '$ICALC - number of params too low');
end;

function TBafVarModule.IncDate(AParams: TStrings): string;
// 0 - date
// 1 - optional: number of days to add; default 1
var
  LDate: TDate;
  LAdd: integer;
begin
  LDate := Trunc(StrToDateTimeDef(AParams[0], 0));
  LAdd := 1;
  if AParams.Count > 1 then
    LAdd := StrToIntDef(AParams[1], 1);
  result := FormatDateTime('dd.mm.yyyy', LDate + LAdd);
end;

function TBafVarModule.IncDec(ADec: boolean; AParams: TStrings): string;
var
  LVal1, LVal2: integer;
begin
  LVal1 := StrToIntDef(AParams[0], 0);
  if AParams.Count > 1 then
    LVal2 := StrToIntDef(AParams[1], 1)
  else
    LVal2 := 1;
  if ADec then
    result := IntToStr(LVal1 - LVal2)
  else
    result := IntToStr(LVal1 + LVal2);
end;

function TBafVarModule.IncTime(AParams: TStrings): string;
// 0 - datetime
// 1 - optional: number of seconds to add; default 1
var
  LDate: TDateTime;
  LAdd: integer;
begin
  LDate := StrToDateTimeDef(AParams[0], 0);
  LAdd := 1;
  if AParams.Count > 1 then
    LAdd := StrToIntDef(AParams[1], 1);
  result := FormatDateTime('dd.mm.yyyy hh:mm:ss', LDate + LAdd / (24 * 3600));
end;

procedure TBafVarModule.IniLoadSave(AIndex: integer; ASave: boolean);
var
  LIni: TStringIniFile;
  fn: string;
  sl: TStringList;
begin
  fn := FindParamStringReplaced('fn', '');
  LIni := GetIni(AIndex);
  sl := TStringList.Create;
  try
    if ASave then begin
      sl.Text := LIni.AsString;
      sl.SaveToFile(fn);
    end
    else begin
      sl.LoadFromFile(fn);
      LIni.AsString := sl.Text;
    end;
  finally
    sl.Free;
  end;
end;

function TBafVarModule.InOperation(AParams: TStrings; AIgnoreCase, AVariable: boolean): string;
var
  LValue, LVarName: string;
  i: integer;
begin
  result := BAFNOCHAR;
  if AParams.Count > 1 then begin
    if AVariable then begin
      LVarName := Trim(AParams[0]);
      if LVarName = '' then
        LValue := ''
      else
        LValue := FNamedVars.Values[LVarName];
    end
    else
      LValue := AParams[0];
    for i := 1 to AParams.Count - 1 do begin
      if (AIgnoreCase and (AnsiCompareText(LValue, AParams[i]) = 0))
          or (not AIgnoreCase and (AnsiCompareStr(LValue, AParams[i]) = 0)) then begin
        result := BAFYESCHAR;
        Exit;
      end;
    end;
  end;
end;

function TBafVarModule.Int2Time(AParam: string): string;
// converts 1130 to 11:30
begin
  result := AParam;
  if Length(AParam) = 3 then
    result := '0' + AParam;
  if Length(result) = 4 then
    result := copy(result, 1, 2) + ':' + copy(result, 3, 2);
end;

function TBafVarModule.RemoveCrLf(AParams: TStrings): string;
var
  sl: TStringList;
  i: integer;
  LIns: string;
begin
  if AParams.Count > 1 then
    LIns := AParams[1];
  result := '';
  if AParams.Count > 0 then begin
    sl := TStringList.Create;
    try
      sl.Text := AParams[0];
      for i := 0 to sl.Count - 1 do
        result := result + TrimRight(sl[i]) + LIns;
    finally
      sl.Free;
    end;
  end;
end;

procedure TBafVarModule.LogVars(AList: TStringList);
var
  i: integer;
begin
  FInter.DebugLog('----------------------------------------');
  for i := 0 to AList.Count - 1 do
    FInter.DebugLog(AList[i]);
  FInter.DebugLog('----------------------------------------');
end;

procedure TBafVarModule.Loop;
var
  LRow, LLoopFrom, LLoopTo: integer;
  LBafConName, LEachRow, LName: string;
  LEachRowTrans, LNoException: boolean;
begin
  LLoopFrom := FindParamIntegerReplaced(FExecInter.LineP, 'lf', 0);
  LLoopTo := FindParamIntegerReplaced(FExecInter.LineP, 'lt', 0);
  LEachRow := FindParamString('ern', '');
  if LEachRow = '' then
    LEachRow := FindParamStringReplaced('er', '');
  LEachRowTrans := FindParamBoolean('ert', false);
  LNoException := FindParamBooleanReplaced('nex', false);
  LBafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
  LName := FindParamStringReplaced('n', '');
  if LLoopTo < LLoopFrom then begin
    for LRow := LLoopFrom downto LLoopTo do begin
      if LName <> '' then
        FInter.Variable[LName] := IntToStr(LRow);    // write loop var in a var
      FExecInter.EachRow(LBafConName, LEachRow, '#loop', LEachRowTrans, LNoException);
      if FInter.ProgressAborted then begin
        FInter.DoLog('I', '#loop, loop by user aborted');
        exit;
      end;
    end;
  end // if LLoopTo < LLoopFrom
  else begin
    for LRow := LLoopFrom to LLoopTo do begin
      if LName <> '' then
        FInter.Variable[LName] := IntToStr(LRow);    // write loop var in a var
      FExecInter.EachRow(LBafConName, LEachRow, '#loop', LEachRowTrans, LNoException);
      if FInter.ProgressAborted then begin
        FInter.DoLog('I', '#loop, loop by user aborted');
        exit;
      end;
    end;
  end; // else LLoopTo < LLoopFrom
end;

function TBafVarModule.LowNoSpace(AText: string): string;
begin
  result := AnsiLowerCase(AText);
  result := StringReplace(result, ' ', '_', [rfReplaceAll]);
end;

function TBafVarModule.NewGuidFormat(AText: string): string;
begin
  result := StringReplace(AText, '{', '', []);
  result := StringReplace(result, '}', '', []);
end;

function TBafVarModule.NullValue(AParams: TStrings): string;
begin
  result := '';
  if AParams.Count > 0 then
    result := AParams[0];
  if (Trim(result) = '') and (AParams.Count > 1) then
    result := AParams[1];
end;

function TBafVarModule.NumFormat(AParams: TStrings): string;
var
  LType, LNum, LDefault: string;
begin
  if AParams.Count > 1 then begin
    LType := AnsiLowerCase(AParams[0]);
    LNum := AParams[1];
    if LNum <> '' then begin
      if LType = 'int' then
        result := Trim(FormatFloat('# ### ### ###', StrToIntDef(LNum, -1)))
      else if LType = 'curr' then
        result := Trim(FormatFloat('### ### ### ### ### ##0.00', StrToCurrDef(LNum, -1)))
      else if LType = 'curr4' then
        result := Trim(FormatFloat('### ### ### ### ### ##0.0000', StrToCurrDef(LNum, -1)))
      else
        result := 'no valid type';
    end;
  end
  else
    FInter.DoLog('E', '$NUMFMT - Number of Params less 2');
end;

function TBafVarModule.OnlyChar(AParams: TStrings): string;
// removes all not char chars out of result
// 0 - string
// 1 - type
var
  LType, LValue: string;
  i: integer;

  function lokReplaceUmlaut(AChar: Char): string;
  begin
    case AChar of
      'ä': result := 'ae';
      'ö': result := 'oe';
      'ü': result := 'ue';
      'Ä': result := 'Ae';
      'Ö': result := 'Oe';
      'Ü': result := 'Ue';
      'ß': result := 'ss';
      else
        result := AChar;
    end;
  end; // function lokReplaceUmlaut

begin
  if AParams.Count > 0 then begin
    LValue := AParams[0];
    if AParams.Count > 1 then
      LType := AnsiLowerCase(AParams[1]);
    for i := 1 to Length(LValue) do begin
      if LType = '' then begin
        if CharInSet(LValue[i], ['a'..'z', 'A'..'Z', 'ä', 'ö', 'ü', 'Ä','Ö', 'Ü', 'ß']) then
          result := result + LValue[i];
      end
      else if LType = 'charnum' then begin
        if CharInSet(LValue[i], ['0'..'9', 'a'..'z', 'A'..'Z', 'ä', 'ö', 'ü', 'Ä','Ö', 'Ü', 'ß']) then
          result := result + LValue[i];
      end
      else if (LType = 'low') or (LType = 'lower')  then begin
        if CharInSet(LValue[i], ['a'..'z', 'A'..'Z', 'ä', 'ö', 'ü', 'Ä','Ö', 'Ü', 'ß']) then
          result := result + AnsiLowerCase(LValue[i]);
      end
      else if (LType = 'upp') or (LType = 'upper')  then begin
        if CharInSet(LValue[i], ['a'..'z', 'A'..'Z', 'ä', 'ö', 'ü', 'Ä','Ö', 'Ü', 'ß']) then
          result := result + AnsiUpperCase(LValue[i]);
      end
      else if (LType = 'charnumlow') or (LType = 'charnumlower')  then begin
        if CharInSet(LValue[i], ['0'..'9', 'a'..'z', 'A'..'Z', 'ä', 'ö', 'ü', 'Ä','Ö', 'Ü', 'ß']) then
          result := result + AnsiLowerCase(LValue[i]);
      end
      else if (LType = 'charnumupp') or (LType = 'charnumupper')  then begin
        if CharInSet(LValue[i], ['0'..'9', 'a'..'z', 'A'..'Z', 'ä', 'ö', 'ü', 'Ä','Ö', 'Ü', 'ß']) then
          result := result + AnsiUpperCase(LValue[i]);
      end;
      if LType = 'uml' then begin
        if CharInSet(LValue[i], ['a'..'z', 'A'..'Z', 'ä', 'ö', 'ü', 'Ä','Ö', 'Ü', 'ß']) then
          result := result + lokReplaceUmlaut(LValue[i]);
      end
      else if LType = 'umlcharnum' then begin
        if CharInSet(LValue[i], ['0'..'9', 'a'..'z', 'A'..'Z', 'ä', 'ö', 'ü', 'Ä','Ö', 'Ü', 'ß']) then
          result := result + lokReplaceUmlaut(LValue[i]);
      end
      else if (LType = 'umllow') or (LType = 'umllower')  then begin
        if CharInSet(LValue[i], ['a'..'z', 'A'..'Z', 'ä', 'ö', 'ü', 'Ä','Ö', 'Ü', 'ß']) then
          result := result + AnsiLowerCase(lokReplaceUmlaut(LValue[i]));
      end
      else if (LType = 'umlupp') or (LType = 'umlupper')  then begin
        if CharInSet(LValue[i], ['a'..'z', 'A'..'Z', 'ä', 'ö', 'ü', 'Ä','Ö', 'Ü', 'ß']) then
          result := result + AnsiUpperCase(lokReplaceUmlaut(LValue[i]));
      end
      else if (LType = 'umlcharnumlow') or (LType = 'umlcharnumlower')  then begin
        if CharInSet(LValue[i], ['0'..'9', 'a'..'z', 'A'..'Z', 'ä', 'ö', 'ü', 'Ä','Ö', 'Ü', 'ß']) then
          result := result + AnsiLowerCase(lokReplaceUmlaut(LValue[i]));
      end
      else if (LType = 'umlcharnumupp') or (LType = 'umlcharnumupper')  then begin
        if CharInSet(LValue[i], ['0'..'9', 'a'..'z', 'A'..'Z', 'ä', 'ö', 'ü', 'Ä','Ö', 'Ü', 'ß']) then
          result := result + AnsiUpperCase(lokReplaceUmlaut(LValue[i]));
      end
      else if (LType = 'no191') or (LType = 'not191') then begin
        if Ord(LValue[i]) <> 191 then
          result := result + LValue[i];
      end


      ;

    end;
  end
  else
    FInter.DoLog('E', '$ONLYCHAR - needs a parameter');
end;

function TBafVarModule.OnlyNum(AParams: TStrings): string;
var
  i: integer;
  s, LMode: string;
begin
  if AParams.Count > 0 then begin
    s := AParams[0];
    if AParams.Count > 1 then
      LMode := AnsiLowerCase(AParams[1])
    else
      LMode := 'numkomma';

    if (LMode = 'flnnc') then begin
      for i := Length(s) downto 1 do begin
        if not CharInSet(s[i], ['0'..'9', '.']) then begin
          result := copy(s, i + 1, MaxInt);
          Break;
        end;
      end;
    end
    else begin
      for i := 1 to Length(s) do begin
        if (LMode = 'num') and CharInSet(s[i], ['0'..'9']) then
          result := result + s[i]
        else if (LMode = 'numkomma') and CharInSet(s[i], ['0'..'9', ',']) then
          result := result + s[i]
        else if (LMode = 'numpoint') and CharInSet(s[i], ['0'..'9', '.']) then
          result := result + s[i]
        else if (LMode = 'ufnnc') then begin
          if CharInSet(s[i], ['0'..'9', '.']) then
            result := result + s[i]
          else
            Break;
        end;
      end;
    end;
  end;
end;

procedure TBafVarModule.OpenText(AIndex: integer);
var
  LFileName: string;
  LText: TStringList;
  LEncoding: TEncoding;
begin
  LText := GetTextStringList(AIndex);
  LFileName := FindParamStringReplaced('fn', '');
  LEncoding := BafGetEncoding(FindParamStringReplaced('e', ''));
  if FileExists(LFileName) then
    LText.LoadFromFile(LFileName, LEncoding)
  else
    FInter.DoLog('E', '#opentext - file doesn''t exist: ' + LFileName);
end;

function TBafVarModule.FileDialog(AParams: TStrings): string;
var
  LDialog: TOpenDialog;
begin
  result := 'abort';
  if (AParams.Count > 0) and (AnsiUpperCase(AParams[0]) = 'SAVE') then
    LDialog := TSaveDialog.Create(nil)
  else
    LDialog := TOpenDialog.Create(nil);
  try
    if (AParams.Count > 1) then
      LDialog.Filter := AParams[1];
    if (AParams.Count > 2) then
      LDialog.DefaultExt := AParams[2];
    if LDialog.Execute then
      result := LDialog.FileName;
  finally
    LDialog.Free;
  end;
end;

function TBafVarModule.FileInfo(AParams: TStrings): string;
// 0 - FileName
// 1 - Typ
var
  LFileName, LTyp: string;
  LFileAge: TDateTime;

  function lokFileSize(const AFileName: string): Int64;
  var
    LFindData: TWin32FindData;
    LHandle: THandle;
  begin
    LHandle := FindFirstFile(PChar(AFileName), LFindData);
    if LHandle = INVALID_HANDLE_VALUE then
      Result := 0
    else begin
      try
        Result := LFindData.nFileSizeHigh;
        Result := Result shl 32;
        Result := Result + LFindData.nFileSizeLow;
      finally
        FindClose(LHandle);
      end;
    end;
  end; // function lokFileSize

  function lokFileSizeAuto(ASize: integer): string;
  begin
    if ASize < 1024 then
      result := IntToStr(ASize) + ' Byte'
    else if ASize < 1024 * 1024 then
      result := FormatFloat('0.00', ASize / 1024) + ' kB'
    else if ASize < 1024 * 1024 * 1024 then
      result := FormatFloat('0.00', ASize / (1024 * 1024)) + ' MB'
    else
      result := FormatFloat('0.00', ASize / (1024 * 1024 * 1024)) + ' GB'
  end;

begin
  if AParams.Count > 1 then begin
    LFileName := AParams[0];
    LTyp := AnsiLowerCase(AParams[1]);
    if LTyp = 'size' then
      result := IntToStr(lokFileSize(LFileName))
    else if LTyp = 'size_point' then
      result := FormatFloat('###,###,###,##0', lokFileSize(LFileName))
    else if LTyp = 'size_auto' then
      result := lokFileSizeAuto( lokFileSize(LFileName))
    else if LTyp = 'size_kb' then
      result := FormatFloat('###,###,###,##0',
          System.Math.Max(1, lokFileSize(LFileName)) / 1024)  + ' kB'
    else if LTyp = 'date' then begin
      if FileAge(LFileName, LFileAge) then
        result := FormatDateTime('dd.mm.yyyy', LFileAge);
    end
    else if LTyp = 'date_yyyy' then begin
      if FileAge(LFileName, LFileAge) then
        result := FormatDateTime('yyyymmdd', LFileAge);
    end
    else if LTyp = 'datetime' then begin
      if FileAge(LFileName, LFileAge) then
        result := FormatDateTime('dd.mm.yyyy hh:mm:ss', LFileAge);
    end
    else if LTyp = 'datetime_yyyy' then begin
      if FileAge(LFileName, LFileAge) then
        result := FormatDateTime('yyyymmddhhmmss', LFileAge);
    end
    else if LTyp = 'exists' then
      result := IfThen(FileExists(LFileName), 'Y', 'N');
  end
  else
    FInter.DoLog('E', '$FILEINFO needs at least two Parameters');
// function TBafVarModule.FileInfo
end;

procedure TBafVarModule.FileOperation;
var
  LOp: string;
  LResult: boolean;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LOp := FindParamStringReplacedLower('y', '');
    LResult := false;
    if (LOp = 'cd') or (LOp = 'createdir') then
      LResult := CreateDir(FindParamStringReplaced('fn', ''))
    else if (LOp = 'rd') or (LOp = 'removedir') then
      LResult := RemoveDir(FindParamStringReplaced('fn', ''))
    else if (LOp = 'df') or (LOp = 'deletefile') then
      LResult := System.SysUtils.DeleteFile(FindParamStringReplaced('fn', ''))
    else if (LOp = 'rf') or (LOp = 'renamefile') then
      LResult := RenameFile(FindParamStringReplaced('on', ''),
          FindParamStringReplaced('fn', ''))
    else if (LOp = 'cf') or (LOp = 'copyfile') or (LOp = 'filecopy') then
      LResult := CopyFile(PWideChar(FindParamStringReplaced('on', '')),
          PWideChar(FindParamStringReplaced('fn', '')), false)
    else if (LOp = 'fd') or (LOp = 'forcedirectories') then
      LResult := ForceDirectories(FindParamStringReplaced('fn', ''))
    ;
    // result to value or variable
    FExecInter.SetVarOrValue('n', IfThen(LResult, 'Y', 'N'));
  end;
// procedure TBafVarModule.FileOperation
end;

procedure TBafVarModule.Forcedir;
var
  LDir: string;
begin
  LDir := FindParamStringReplaced('n', '');
  if Trim(LDir) <> '' then
    ForceDirectories(LDir)
  else
    FInter.DoLog('W', 'ForceDir not executed, dir name empty');
end;

function TBafVarModule.FormatDataTime(AParams: TStrings; ADefault: string): string;
begin
  if (AParams.Count = 0) or (AParams[0] = '') then
    result := FormatDateTime(ADefault, now)
  else
    result := FormatDateTime(AParams[0], now);
end;

function TBafVarModule.FunctionExecute(AParams: TStrings): string;
var
  LCmd, LVar: string;
begin
  if AParams.Count > 0 then begin
    LCmd := AParams[0];
    TBafInterpreterLevel.ExecInNewLevel(LCmd, FExecInter, FInter);
    if AParams.Count > 1 then
      LVar := AParams[1]
    else
      LVar := 'result';
    result := FInter.Variable[LVar];
  end;
end;

function TBafVarModule.Pad(AParams: TStrings): string;
// 0 - string
// 1 - length
// 2 - L or R
// 3 - char(s)
// optional 4 - MaxLength
var
  LFolge: string;
  LLaenge, LLength, LMaxLength: integer;
  LLinks: boolean;
begin
  if AParams.Count > 3 then begin
    result := AParams[0];
    LFolge := AParams[3];
    if LFolge = '' then
      LFolge := ' ';
    if  AParams.Count > 4 then begin
      LMaxLength := StrToIntDef(AParams[4], -1);
      result := copy(result, 1, LMaxLength);
    end;
    LLaenge := StrToIntDef(AParams[1], 0);
    LLinks := (UpperCase(AParams[2] + ' ')[1] = 'L');
    LLength := Length(result);
    while LLength < LLaenge do begin
      if LLinks then
        result := copy(LFolge, 1, LLaenge - LLength) + result
      else
        result := result + copy(LFolge, 1, LLaenge - LLength);
      LLength := Length(result);
    end;
  end
  else
    FInter.DoLog('E', '$PAD needs 4 parameters');
end;

procedure TBafVarModule.PasteCsv;
type
  TBafPasteMode = (pmSingle, pmMulti);
var
  LBafConName, LSep, LZeile, s, LEachRow, LFirstRow: string;
  sl, slZeile: TStringList;
  i, p, LMax, LRowCount, LMultiRowStart, LFrom: integer;
  LHasHeaderRow, LEachRowTrans, LNoException: boolean;
  LMode: TBafPasteMode;
  LExit, LTrim: boolean;

  procedure lokZerlegen;
  begin
    p := Pos(LSep, LZeile);
    while p > 0 do begin
      s := copy(LZeile, 1, p - 1);
      Delete(LZeile, 1, p);
      if LTrim then
        slZeile.Add(Trim(s))
      else
        slZeile.Add(s);
      p := Pos(LSep, LZeile);
    end;
  end; // procedure lokZerlegen

  procedure lokExecRow;
  begin
    FExecInter.EachRow(LBafConName, LEachRow, '#csv_paste', LEachRowTrans, LNoException);
    inc(LRowCount);
    if LRowCount >= LMax then begin
      FInter.DoLog('I', Format('#csv_paste, Max (%d) reached, loop aborted', [LMax]));
      LExit := true;
      exit;
    end;
    if FInter.ProgressAborted then begin
      FInter.DoLog('I', '#csv_paste, loop by user aborted');
      LExit := true;
      exit;
    end;
  end; // procedure lokExecRow

  procedure lokMulti;
  var
    i: integer;
  begin
    for i := LMultiRowStart to slZeile.Count - 1 do begin
      if (slZeile[i] <> '') and not LExit then begin
        slZeile[LMultiRowStart] := slZeile[i];
        lokExecRow;
      end;
    end;
  end; // procedure lokMulti

  procedure lokParams;
  begin
    LSep := FindParamStringReplaced('sep', #9);
    LHasHeaderRow := FindParamBooleanReplaced('hhr', false);
    s := FindParamStringLower('y', 's');
    if s = 'm' then
      LMode := pmMulti
    else
      LMode := pmSingle;
    LMax := FindParamInteger('m', MaxInt);
    LEachRow := FindParamString('ern', '');
    if LEachRow = '' then
      LEachRow := FindParamStringReplaced('er', '');
    LEachRowTrans := FindParamBoolean('ert', false);
    LNoException := FindParamBooleanReplaced('nex', false);
    LBafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
    LMultiRowStart := FindParamInteger('mrs', MaxInt);
    LTrim := FindParamBooleanReplaced('trim', false);
    LFirstRow := Trim(FindParamStringReplaced('fr', ''));
  end; // procedure lokParams

begin
  lokParams;

  slZeile := FSepLineValue;
  sl := TStringList.Create;
  try
    sl.Text := Clipboard.AsText;
    LExit := false;
    if sl.Count > 0 then begin
      if (LFirstRow <> '') and (Trim(sl[0]) <> LFirstRow) then
        FInter.DoLog('E', 'Die erste Datenzeile muss folgenden Wert haben: ' + LFirstRow)
      else begin
        LRowCount := 0;
        LFrom := IfThen(LHasHeaderRow, 1, 0);
        if (LFirstRow <> '') then
          inc(LFrom);
        for i := LFrom to sl.Count - 1 do begin
          slZeile.Clear;
          LZeile := sl[i] + LSep;
          lokZerlegen;
          case LMode of
            pmSingle: lokExecRow;
            pmMulti: lokMulti;
          end;
          if LExit then
            exit;
        end;
      end;
    end; // if sl.Count > 0
  finally
    sl.Free;
  end;
// procedure TBafVarModule.PasteCsv
end;

procedure TBafVarModule.Procdir;
var
  LRec: TSearchRec;
  LRes: integer;
  LDirName, LFileName, LDone, LError, LCommand, LName, LVarDone: string;
  LDoDone: boolean;
begin
  LDirName := IncludeTrailingPathDelimiter(Trim(FindParamStringReplaced('dn', '')));
  if LDirName = '' then
    Finter.DoLog('E', '#procdir - param dn empty');
  LFileName := Trim(FindParamStringReplaced('fn', '*'));
  LDone := Trim(FindParamStringReplaced('done', ''));
  LError := Trim(FindParamStringReplaced('error', ''));
  LName := FindParamStringReplaced('n', 'dir_proc');
  LVarDone := FindParamStringReplaced('ndone', '');
  LCommand := FindParamString('cmd', '');
  LRes := FindFirst(LDirName + LFileName, faAnyFile - faDirectory, LRec);
  try
    while LRes = 0 do begin
      LDoDone := true;
      if LVarDone <> '' then
        FNamedVars.Values[LVarDone] := 'Y';
      FNamedVars.Values[LName] := LDirName + LRec.Name;
      TBafInterpreterLevel.ExecInNewLevel(LCommand, FExecInter, FInter);
      if LVarDone <> '' then
        LDoDone := BafIsYesChar( FNamedVars.Values[LVarDone]);
      if DirectoryExists(LDone) and LDoDone then        // all executed move to done
        RenameFile(LDirName + LRec.Name, IncludeTrailingPathDelimiter(LDone) + LRec.Name)
      else if DirectoryExists(LError) and (UpperCase(Trim(FNamedVars.Values[LVarDone])) = 'E') then
        RenameFile(LDirName + LRec.Name, IncludeTrailingPathDelimiter(LError) + LRec.Name);
      LRes := FindNext(LRec);
    end;
  finally
    System.SysUtils.FindClose(LRec);
  end;
end;

function TBafVarModule.ReplaceString(AParams: TStrings): string;
var
  s: string;
  LFlags: TReplaceFlags;
begin
  if AParams.Count > 2 then begin
    LFlags := [];
    if AParams.Count > 3 then begin
      s := AnsiLowerCase(AParams[3]);
      if Pos('i', s) > 0 then
        LFlags := LFlags + [rfIgnoreCase];
      if Pos('a', s) > 0 then
        LFlags := LFlags + [rfReplaceAll];
    end;
    result := StringReplace(AParams[0], AParams[1], AParams[2], LFlags);
  end
  else
    FInter.DoLog('E', '$STREP number of params to low');
end;

procedure TBafVarModule.RightsDef;
var
  i: integer;
  LName, LRight, LLine: string;
begin
  LName := FindParamStringLower('n', 'frm');
  LRight := '';
  for i := 0 to gvBafDataCache.UserGroups.Count - 1 do begin
    LLine := FindParamStringLower('r_' + gvBafDataCache.UserGroups[i], '');
    if LRight = '' then
      LRight := LLine
    else if (LRight = 'r') and (LLine = 'w') then
      LRight := 'w';
    if LRight = 'w' then
      Break;
  end;
  FRightDefs.Values[LName] := LRight;
end;

function TBafVarModule.ReplaceFunction(ACommand: string; AParams: TStrings;
  var AResult: string): boolean;
begin
  inherited;
  result := true;
  if ACommand = '$TEXT' then AResult := GetTextLine(AParams)
  else if ACommand = '$TEXTTR' then AResult := TrimRight(GetText(AParams[0]))
  else if ACommand = '$VAR' then AResult := GetVar(FNamedVars, AParams)
  else if ACommand = '$GVAR' then AResult := GetVar(gvGlobalVars, AParams)
  else if ACommand = '$CSV' then AResult := GetCsvValue(AParams)
  else if ACommand = '$CSV_LINE' then AResult := GetCsvLine(AParams)
  else if ACommand = '$SEPLINE' then AResult := GetSepLine(AParams[0])

  // sys and ini
  else if ACommand = '$PROG' then AResult := FInter.NeedInfo('prog', '')
  else if ACommand = '$RIGHTUSERID' then AResult := FInter.NeedInfo('rightusrid', '')
  else if ACommand = '$USERID' then AResult := FInter.NeedInfo('usrid', AParams[0])
  else if ACommand = '$INI' then AResult := GetIniValue(AParams)
  else if ACommand = '$ADM' then AResult := IsAdmin(AParams, false)
  else if ACommand = '$RADM' then AResult := IsAdmin(AParams, true)
  else if ACommand = '$INITEXT' then AResult := GetIniText(AParams[0])

  // strings
  else if ACommand = '$BASE64' then AResult := BafBase64(AParams)
  else if ACommand = '$CHR' then AResult := GetChar(AParams)
  else if ACommand = '$COPY' then AResult := StringCopy(AParams)
  else if ACommand = '$CLIPBOARD' then AResult := Clipboard.AsText
  else if ACommand = '$CONVERT' then AResult := Convert(AParams)
  else if ACommand = '$EXEC' then AResult := FunctionExecute(AParams)
  else if ACommand = '$GUID' then AResult := GetGuid(AParams)
  else if ACommand = '$HASH' then AResult := GetHash(AParams)
  else if ACommand = '$INVLOOKUP' then AResult := GetInversLookup(AParams)
  else if ACommand = '$INCLUDE' then AResult := StringInclude(AParams)
  else if ACommand = '$LOOKUP' then AResult := GetLookup(AParams)
  else if ACommand = '$LOW' then AResult := AnsiLowerCase(AParams[0])
  else if ACommand = '$LOWNOSPACE' then AResult := LowNoSpace(AParams[0])
  else if ACommand = '$NOCRLF' then AResult := RemoveCrLf(AParams)
  else if ACommand = '$NUMFMT' then AResult := NumFormat(AParams)
  else if ACommand = '$NVL' then AResult := NullValue(AParams)
  else if ACommand = '$ONLYCHAR' then AResult := OnlyChar(AParams)
  else if ACommand = '$PAD' then AResult := Pad(AParams)
  else if ACommand = '$SUBSTR' then AResult := StringCopy(AParams)
  else if ACommand = '$STREP' then AResult := ReplaceString(AParams)
  else if ACommand = '$STROP' then AResult := StringOperation(AParams)
  else if ACommand = '$STREXTR' then AResult := StringExtract(AParams)
  else if ACommand = '$TEXTCOMPARE' then AResult := TextCompare(AParams)
  else if ACommand = '$TRIM' then AResult := BafTrim(AParams)
  else if ACommand = '$UPP' then AResult := AnsiUpperCase(AParams[0])
  else if ACommand = '$VT' then AResult := ValueTranslate(AParams)
  else if ACommand = '$VTVL' then AResult := ValueTranslateValueList(AParams)
  else if ACommand = '$VTRDB' then AResult := ValueTranslateRootDB(AParams)
  else if ACommand = '$XR' then AResult := BafXmlReplace(AParams)
  else if ACommand = '$XRR' then AResult := BafXmlReReplace(AParams)


  // integer functions, returns an integer number
  else if ACommand = '$ADRKORR' then AResult := BafAdresssKorrelation(AParams)
  else if ACommand = '$DATECOMP' then AResult := BafDateCompare(AParams)
  else if ACommand = '$DEC' then AResult := IncDec(true, AParams)
  else if ACommand = '$ICALC' then AResult := ICalc(AParams)
  else if ACommand = '$INC' then AResult := IncDec(false, AParams)
  else if ACommand = '$INTLEN' then AResult := GetNumInfos(niIntLen, AParams)
  else if ACommand = '$LEN' then AResult := IntToStr(Length(AParams[0]))
  else if ACommand = '$LEVENSHTEIN' then AResult := IntToStr(BafLevenshtein(AParams[0], AParams[1]))
  else if ACommand = '$POS' then AResult := BafPos(AParams)
  else if ACommand = '$RANDOM' then AResult := BafRandom(AParams)



  // currency functions, retuns a currency
  else if ACommand = '$CCALC' then AResult := CCalc(AParams)
  else if ACommand = '$CFMT' then AResult := CFmt(AParams)
  else if ACommand = '$CURR' then AResult := GetCurr(AParams)
  else if ACommand = '$ONLYNUM' then AResult := OnlyNum(AParams)

  // date functions, retuns a date or a time
  else if ACommand = '$DFMT' then AResult := GetDateFormated(AParams)
  else if ACommand = '$INCDATE' then AResult := IncDate(AParams)
  else if ACommand = '$INCSEC' then AResult := IncTime(AParams)
  else if ACommand = '$INT2TIME' then AResult := Int2Time(AParams[0])
  else if ACommand = '$NOW' then AResult := FormatDateTime('dd.mm.yyyy hh:mm:ss', now)
  else if ACommand = '$NOWFMT' then AResult := FormatDateTime(AParams[0], now)
  else if ACommand = '$NOWMIN' then AResult := FormatDateTime('dd.mm.yyyy hh:mm', now)
  else if ACommand = '$TODAY' then AResult := FormatDataTime(AParams, 'dd.mm.yyyy')

  // boolean functions, retuns Y or N
  else if ACommand = '$BCALC' then AResult := BCalc(AParams)
  else if ACommand = '$BFMT' then AResult := BFmt(AParams)
  else if ACommand = '$BOOL' then AResult := IfThen(FExecInter.ParseBoolStatement(AParams[0]), BAFYESCHAR, BAFNOCHAR)
  else if ACommand = '$EMPTY' then AResult := IfThen(Trim(AParams[0]) = '', BAFYESCHAR, BAFNOCHAR)
  else if ACommand = '$EMPTYVAR' then AResult := EmptyV(false, AParams)
  else if ACommand = '$ISCURR' then AResult := GetNumInfos(niIsCurr, AParams)
  else if ACommand = '$ISDATE' then AResult := BafIsDate(AParams)
  else if ACommand = '$ISINT' then AResult := GetNumInfos(niIsInt, AParams)
  else if ACommand = '$IN' then AResult := InOperation(AParams, false, false)
  else if ACommand = '$INIC' then AResult := InOperation(AParams, true, false)
  else if ACommand = '$INVAR' then AResult := InOperation(AParams, false, true)
  else if ACommand = '$INICVAR' then AResult := InOperation(AParams, true, true)
  else if ACommand = '$INTMAX' then AResult := GetIntMinMax(niIntMax, AParams[0], AParams[1])
  else if ACommand = '$INTMIN' then AResult := GetIntMinMax(niIntMin, AParams[0], AParams[1])
  else if ACommand = '$NEMPTY' then AResult := IfThen(Trim(AParams[0]) = '', BAFNOCHAR, BAFYESCHAR)
  else if ACommand = '$NEMPTYVAR' then AResult := EmptyV(true, AParams)
  else if ACommand = '$NUMBETWEEN' then AResult := GetBetween(niNumBetween, AParams)
  else if ACommand = '$DATEBETWEEN' then AResult := GetBetween(niDateBetween, AParams)
  else if ACommand = '$DATEMINREL' then AResult := GetDateMinMaxRel(niDateMin, AParams[0], AParams[1])
  else if ACommand = '$DATEMAXREL' then AResult := GetDateMinMaxRel(niDateMax, AParams[0], AParams[1])
  else if ACommand = '$NOT' then AResult := IfThen(BafIsYesChar(AParams[0]), BAFNOCHAR, BAFYESCHAR)
  else if ACommand = '$REGEX' then AResult := BafRegex(AParams)
  else if ACommand = '$TEXT_HASLINE' then AResult := TextHasLine(AParams)
  else if ACommand = '$VIBAN' then AResult := IfThen(BafIsValidIban(AParams[0]), BAFYESCHAR, BAFNOCHAR)

  // files and directories
  else if ACommand = '$DIR' then AResult := BafGetDir(AParams[0])
  else if ACommand = '$FILEDIALOG' then AResult := FileDialog(AParams)
  else if ACommand = '$FILEINFO' then AResult := FileInfo(AParams)



  // colors
  else if ACommand = '$CLRDEADLINE_YR' then AResult := GetClrDeadline(ciDeadlineYR, AParams)


  // JSON and XML
  else if ACommand = '$JSON' then AResult := GetJSON(AParams)

  else result := false;
// function TBafVarModule.ReplaceFunction
end;

procedure TBafVarModule.Save2Clipboard;
begin
  Clipboard.AsText := FindParamStringReplaced('z', '');
end;

procedure TBafVarModule.SaveText(AIndex: integer);
var
  fn: string;
  LSort, LWriteBom, LBomAlt: boolean;
  LText, sl: TStringList;
  i: integer;
  LEncoding: TEncoding;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LText := GetTextStringList(AIndex);
    fn := FindParamStringReplaced('fn', '');
    LEncoding := BafGetEncoding(FindParamStringReplaced('e', ''));
    LWriteBom := FindParamBooleanReplaced('bom', true);
    LSort := FindParamBoolean('sort', false);
    if LSort then begin
      sl := TStringList.Create;
      try
        sl.Sorted := true;
        sl.Duplicates := dupIgnore;
        for i := 0 to LText.Count - 1 do
          sl.Add(LText[i]);
        sl.WriteBOM := LWriteBom;
        sl.SaveToFile(fn, LEncoding);
      finally
        sl.Free;
      end;
    end
    else begin
      LBomAlt := LText.WriteBOM;
      LText.WriteBOM := LWriteBom;
      LText.SaveToFile(fn, LEncoding);
      LText.WriteBOM := LBomAlt;
    end;
    if FindParamBooleanReplaced('o', false) then
      BafOpenFile(fn);
  end;
end;

procedure TBafVarModule.SepLine(AIndex: integer);
var
  LRow, LRowCount, LMax: integer;
  LBafConName, LSep, LEachRow: string;
  LEachRowTrans, LNoException, LNotNull: boolean;
  LText: TStringList;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LText := GetSepLineList(AIndex);
    LSep := FindParamStringReplaced('sep', ';') + ' ';
    LText.Delimiter := LSep[1];
    LText.StrictDelimiter := true;
    LText.DelimitedText := FindParamStringReplaced('z', '');
    LMax := FindParamIntegerReplaced('m', MaxInt);
    LEachRow := FindParamString('ern', '');
    if LEachRow = '' then
      LEachRow := FindParamStringReplaced('er', '');
    LEachRowTrans := FindParamBooleanReplaced('ert', false);
    LNoException := FindParamBooleanReplaced('nex', false);
    LBafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
    LNotNull := FindParamBooleanReplaced('nn', false);
    if (LEachRow <> '') and (LText.Count > 0) then begin
      LRowCount := 0;
      for LRow := 0 to LText.Count - 1 do begin
        while FSepLineValue.Count <= AIndex do
          FSepLineValue.Add('');
        FSepLineValue[AIndex] := Trim(LText[LRow]);
        if (FSepLineValue[AIndex] <> '') or not LNotNull then begin
          FExecInter.EachRow(LBafConName, LEachRow, '#sepline', LEachRowTrans, LNoException);
          inc(LRowCount);
          if LRowCount >= LMax then begin
            FInter.DoLog('I', Format('#sepline, Max (%d) reached, loop aborted', [LMax]));
            Break;
          end;
          if FInter.ProgressAborted then begin
            FInter.DoLog('I', '#sepline, loop by user aborted');
            Break;
          end;
        end;
      end; // for LRow := 0 to LText.Count - 1
    end; // if (LEachRow <> '') and (LText.Count > 0)
  end; // if FindParamBooleanReplaced('cnd',
// procedure SepLine.SepLine
end;

procedure TBafVarModule.SetCmd(AIndex: integer);
begin
  GetCmdList(AIndex).Add(FExecInter.LineP);
end;

procedure TBafVarModule.SetIniValue(AIndex: integer);
var
  LIni: TStringIniFile;
  s: string;
begin
  s := FindParamStringLower('n', '');
  if (s = 'usr') or (s = 'user') then
    LIni := dataMain.UserIni
  else
    LIni := GetIni(AIndex);
  LIni.WriteString(FindParamStringReplaced('sec', 'values'),
      FindParamStringReplaced('i', 'value'),
      FindParamStringReplaced('z', ''))
end;

procedure TBafVarModule.SetText(AIndex: integer);
var
  s: string;
begin
  s := FExecInter.ReplaceFunctions(FExecInter.LineP);
  s := FExecInter.ReplaceFunctions(s);
  GetTextStringList(AIndex).Add(s);
end;

procedure TBafVarModule.SetTextN(AIndex: integer);
begin
  GetTextStringList(AIndex).Add(FExecInter.LineP);
end;

procedure TBafVarModule.SetVar(AList: TStringList; AOnlyIfEmpty: boolean);
var
  LName, LValue, LRightName: string;
  LRight: TBafRight;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LRightName := FindParamStringLower('r', 'frm');
    LName := FindParamStringReplaced('n', '');
    if LRightName = 'frm' then
      LRight := brWrite
    else
      LRight := FInter.GetRightOfDef(LRightName);
    case LRight of
      brNone: LValue := FindParamStringReplaced('zn', '');
      brRead: LValue := FindParamStringReplaced('zr', '');
    else
      LValue := FindParamStringReplaced('z', '');
    end;
    if FindParamBooleanReplaced('rf', false) then
      LValue := FExecInter.ReplaceFunctions(LValue);
    if LValue = '' then
      LValue := FindParamStringReplaced('ie', '');
    if (AOnlyIfEmpty = false) or (Trim(AList.Values[LName]) = '') then
      AList.Values[LName] := LValue;
  end;
end;

procedure TBafVarModule.SetVariable(AName: string; const Value: string);
begin
  inherited;
  FNamedVars.Values[AName] := Value;
end;

function TBafVarModule.StringCopy(AParams: TStrings): string;
var
  s: string;
  LVon, LCount: integer;
begin
  result := '';
  if AParams.Count > 2 then begin
    s := AParams[0];
    LVon := StrToIntDef(AParams[1], 1);
    LCount := StrToIntDef(AParams[2], MaxInt);
    result := copy(s, LVon, LCount);
  end
  else if AParams.Count = 2 then begin
    s := AParams[0];
    LVon := StrToIntDef(AParams[1], 1);
    result := copy(s, LVon, MaxInt);
  end;
end;

function TBafVarModule.StringExtract(AParams: TStrings): string;
var
  LText, LType: string;

  function lokFirst(AParamNum: integer; AFrom, AInclude: boolean): string;
  var
    LPos: integer;
    LSearch: string;
  begin
    if AParams.Count > AParamNum then begin
      LSearch := AParams[AParamNum];
      LPos := Pos(LSearch, LText);
      if AFrom then begin
        if LPos > 0 then
          result := copy(LText, LPos
              + IfThen(AInclude, 0, Length(LSearch)), MaxInt)
        else
          result := '';
      end
      else begin
        if LPos > 0 then
          result := copy(LText, 1, LPos - 1
              + IfThen(not AInclude, 0, Length(LSearch)))
        else
          result := LText;
      end;
    end
    else
      FInter.DoLog('E', '$STREXTR - number of params too low for ' + LType);
  end; // function lokFirst

  function LastPos(ASearch, AText: string): integer;
  var
    i, LLength: integer;
  begin
    result := 0;
    LLength := Length(ASearch);
    for i := Length(AText) downto 1 do begin
      if ASearch = copy(AText, i, LLength) then begin
        result := i;
        exit;
      end;
    end;
  end; // function LastPos

  function lokLast(AParamNum: integer; AFrom, AInclude: boolean): string;
  var
    LPos: integer;
    LSearch: string;
  begin
    if AParams.Count > AParamNum then begin
      LSearch := AParams[AParamNum];
      LPos := LastPos(LSearch, LText);
      if AFrom then begin
        if LPos > 0 then
          result := copy(LText, LPos
              + IfThen(AInclude, 0, Length(LSearch)), MaxInt)
        else
          result := '';
      end
      else begin
        if LPos > 0 then
          result := copy(LText, 1, LPos - 1
              + IfThen(not AInclude, 0, Length(LSearch)))
        else
          result := LText;
      end;
    end
    else
      FInter.DoLog('E', '$STREXTR - number of params too low for ' + LType);
  end; // function lokLast

begin
  if AParams.Count > 2 then begin
    LText := AParams[0];
    LType := Trim(AnsiLowerCase(AParams[1]));
    if LType = 'ffi' then
      result := lokFirst(2, true, true)
    else if LType = 'ffe' then
      result := lokFirst(2, true, false)
    else if LType = 'tfi' then
      result := lokFirst(2, false, true)
    else if LType = 'tfe' then
      result := lokFirst(2, false, false)

    else if LType = 'fli' then
      result := lokLast(2, true, true)
    else if LType = 'fle' then
      result := lokLast(2, true, false)
    else if LType = 'tli' then
      result := lokLast(2, false, true)
    else if LType = 'tle' then
      result := lokLast(2, false, false)


    ;
  end
  else
    FInter.DoLog('E', '$STREXTR - number of params too low');
// function TBafVarModule.StringExtract
end;

function TBafVarModule.StringInclude(AParams: TStrings): string;
var
  LType, LText, LInc: string;
begin
  if AParams.Count > 2 then begin
    LType := Trim(AnsiLowerCase(AParams[0]));
    LText := AParams[1];
    LInc := AParams[2];

    result := LText;
    if (LType = 'trail')  or (LType = 'trailing') then begin
      if not (LInc = copy(LText, length(LText) - length(LInc) + 1, length(LInc))) then
        result := LText + LInc
    end
    else if (LType = 'trailci')  or (LType = 'trailingci')  or (LType = 'trailingcaseinsensitive') then begin
      if AnsiCompareText(LInc, copy(LText, length(LText) - length(LInc) + 1, length(LInc))) <> 0 then
        result := LText + LInc
    end
    else if (LType = 'lead')  or (LType = 'leading') then begin
      if not (LInc = copy(LText, 1, length(LInc))) then
        result := LInc + LText
    end
    else if (LType = 'leadci')  or (LType = 'leadingci')  or (LType = 'leadingcaseinsensitive') then begin
      if AnsiCompareText(LInc, copy(LText, 1, length(LInc))) <> 0 then
        result := LInc + LText
    end
    ;
  end
  else
    FInter.DoLog('E', '$INCLUDE - number of params less 3');
end;

function TBafVarModule.StringOperation(AParams: TStrings): string;
var
  LText, LType: string;
begin
  if AParams.Count > 1 then begin
    LType := Trim(AnsiLowerCase(AParams[0]));
    LText := AParams[1];
    if LType = 'trim'
      then result := Trim(LText)
    else if LType = 'trimleft'
      then result := TrimLeft(LText)
    else if LType = 'trimright'
      then result := TrimRight(LText)
    else if LType = 'quote'
      then result := AnsiQuotedStr(LText, Chr(39))
    else if LType = 'unquote'
      then result := AnsiDequotedStr(LText, Chr(39))
    else if LType = 'dquote'
      then result := AnsiQuotedStr(LText, '"')
    else if LType = 'undquote'
      then result := AnsiDequotedStr(LText, '"')
    else if LType = 'ex_filepath'
      then result := ExtractFilePath(LText)
    else if LType = 'ex_filedir'
      then result := ExtractFileDir(LText)
    else if LType = 'ex_filedrive'
      then result := ExtractFileDrive(LText)
    else if LType = 'ex_filename'
      then result := ExtractFileName(LText)
    else if LType = 'ex_fileext'
      then result := ExtractFileExt(LText)


    ;
  end
  else
    FInter.DoLog('E', '$STROP - number of params less 2');
end;

function TBafVarModule.TextCompare(AParams: TStrings): string;
var
  LText0, LText1: string;
begin
  if AParams.Count > 1 then begin
    LText0 := Trim(GetTextStringList(StrToIntDef(AParams[0], 1)).Text);
    LText1 := Trim(GetTextStringList(StrToIntDef(AParams[1], 1)).Text);
    result := IfThen(LText0 = LText1, 'Y', 'N');
  end;
end;

procedure TBafVarModule.TextCompose(AIndex: integer);
var
  LText: TStringList;
  i, ix: integer;
  LName, LSep, LResult: string;
  LLastSep: boolean;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LText := GetTextStringList(AIndex);
    LSep := FindParamStringReplaced('sep', '');
    LLastSep := FindParamBooleanReplaced('ls', false);
    for i := 0 to LText.Count - 1 do
      LResult := LResult + LText[i]
          + IfThen(LLastSep or (i < (LText.Count - 1)), LSep, '');
    FExecInter.SetVarOrValue('n', LResult);
  end;
end;

procedure TBafVarModule.TextDissect(AIndex: integer);
var
  LText: TStringList;
  p: integer;
  LValue, LSep: string;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LText := GetTextStringList(AIndex);
    LValue := FindParamStringReplaced('z', '');
    LSep := FindParamStringReplaced('sep', '');
    if FindParamBooleanReplaced('ls', false) then
      LValue := LValue + LSep;
    p := Pos(LSep, LValue);
    while p > 0 do begin
      LText.Add(copy(LValue, 1, p - 1));
      Delete(LValue, 1, p - 1 + Length(LSep));
      p := Pos(LSep, LValue);
    end;
  end;
end;

function TBafVarModule.TextHasLine(AParams: TStrings): string;
// 0 - Num of Text
// 1 - Value to check
// 2 - Yes
// 3 - No
var
  LText: TStringList;
  LYes, LNo: string;
begin
  if AParams.Count > 1 then begin
    LText := GetTextStringList(StrToIntDef(AParams[0], 1));
    LYes := 'Y';
    LNo := 'N';
    if AParams.Count > 2 then
      LYes := AParams[2];
    if AParams.Count > 3 then
      LNo := AParams[3];
    result := IfThen(LText.IndexOf(AParams[1]) >= 0, LYes, LNo);
  end
  else
    FInter.DoLog('E', '$TEXT_HASLINE - number of params too low');
end;

procedure TBafVarModule.TextLoop(AIndex: integer);
var
  LText: TStringList;
  i, LNum, LMax, LRowCount: integer;
  LName, LEachRow, LBafConName: string;
  LEachRowTrans, LNoException: boolean;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LMax := FindParamIntegerReplaced('m', MaxInt);
    LEachRow := FindParamString('ern', '');
    if LEachRow = '' then
      LEachRow := FindParamStringReplaced('er', '');
    LEachRowTrans := FindParamBooleanReplaced('ert', false);
    LNoException := FindParamBooleanReplaced('nex', false);
    LBafConName := FindParamStringReplacedLower('db', DC_DEFAULT);
    LName := FindParamStringReplaced('n', '');
    LNum := StrToIntDef(LName, -1);
    LText := GetTextStringList(AIndex);
    LRowCount := 0;
    for i := 0 to LText.Count - 1 do begin
      if LNum > 0 then
        FExecInter.Values[LNum] := IntToStr(i)
      else if LName <> '' then
        FInter.Variable[LName] := IntToStr(i);

      FExecInter.EachRow(LBafConName, LEachRow, FExecInter.LineF, LEachRowTrans, LNoException);
      inc(LRowCount);
      if LRowCount >= LMax then begin
        FInter.DoLog('I', Format(FExecInter.LineF + ', Max (%d) reached, execution aborted', [LMax]));
        Break;
      end;
      if FInter.ProgressAborted then begin
        FInter.DoLog('I', FExecInter.LineF + ', loop by user aborted');
        Break;
      end;
    end;
  end;
// procedure TBafVarModule.TextLoop
end;

procedure TBafVarModule.TextAct(AIndex: integer);
var
  LText: TStringList;
  LCurrent, LNew: integer;
  LTyp, LLine: string;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LText := GetTextStringList(AIndex);
    LTyp := FindParamStringReplacedLower('y', '');
    if LTyp = 'move' then begin
      LCurrent := FindParamIntegerReplaced('cu', 0);
      LNew := FindParamIntegerReplaced('ne', 0);
      LText.Move(LCurrent, LNew);
    end
    else if LTyp = 'delete' then begin
      LCurrent := FindParamIntegerReplaced('cu', 0);
      LText.Delete(LCurrent);
    end
    else if LTyp = 'insert' then begin
      LCurrent := FindParamIntegerReplaced('cu', 0);
      LLine := FindParamStringReplaced('c', '');
      LText.Insert(LCurrent, LLine);
    end
  end;
// procedure TBafVarModule.TextAct
end;

procedure TBafVarModule.TextProperties(AIndex: integer);
var
  LText: TStringList;
  s: string;
begin
  LText := GetTextStringList(AIndex);
  LText.Sorted := FindParamBooleanReplaced('srt', false);
  s := FindParamStringReplacedLower('dup', '');
  if s = 'ign' then
    LText.Duplicates := dupIgnore
  else if s = 'acc' then
    LText.Duplicates := dupAccept
  else if s = 'err' then
    LText.Duplicates := dupError;
end;

procedure TBafVarModule.TextSet(AIndex: integer);
var
  LText: TStringList;
  s, LValue: string;
  ix: integer;
begin
  LText := GetTextStringList(AIndex);
  s := FindParamStringReplacedLower('i', '');
  LValue := FindParamStringReplaced('z', '');
  if s = 'all' then
    LText.Text := LValue
  else begin
    if s = 'last' then
      ix := LText.Count - 1
    else
      ix := StrToIntDef(s, -1);
    if (ix = -1) or (ix >= LText.Count) then
      FInter.DoLog('W', '#text_set - ix out of range')
    else
      LText[ix] := LValue;
  end;
end;

procedure TBafVarModule.TextSort(AIndex: integer);
var
  LText, sl: TStringList;
  LType: string;
  i: integer;
begin
  LText := GetTextStringList(AIndex);
  LType := FindParamStringReplacedLower('y', '');
  if LType = 'inv' then begin
    sl := TStringList.Create;
    try
      for i := LText.Count - 1 downto 0 do
        sl.Add(LText[i]);
      LText.Text := sl.Text;
    finally
      sl.Free;
    end;
  end
  else
    LText.Sort;
end;

procedure TBafVarModule.TvlAdd(AIndex: integer);
var
  LText: TStringList;
  LTyp, LName, LValue: string;
begin
  if FindParamBooleanReplaced('cnd', true) then begin
    LText := GetTextStringList(AIndex);
    LTyp := FindParamStringReplacedLower('y', '');
    LName := FindParamStringReplaced('n', '');
    LValue := FindParamStringReplaced('z', '');
    if LTyp = 'int' then
      LText.Values[LName] := IntToStr(StrToIntDef(LText.Values[LName], 0)
          + StrToIntDef(LValue, 0))
    else if LTyp = 'curr' then
      LText.Values[LName] := CurrToStr(StrToCurrDef(LText.Values[LName], 0)
          + StrToCurrDef(LValue, 0))
    else if LTyp = 'date' then
      LText.Values[LName] := FormatDateTime('dd.mm.yyyy',
          StrToDateTimeDef(LText.Values[LName], 0) + StrToFloatDef(LValue, 0))
    else if LTyp = 'datetime' then begin
      LText.Values[LName] := FormatDateTime('dd.mm.yyyy hh:mm:ss',
          StrToDateTimeDef(LText.Values[LName], 0) + StrToFloatDef(LValue, 0))
      end
    else
      LText.Values[LName] := LText.Values[LName] + LValue;


  end;
end;

function TBafVarModule.ValueTranslate(AParams: TStrings): string;
// 0 - Value
// 1 - else-result
// 2 - compare 1
// 3 - result 1
var
  LValue, LCompare, LCompareResult, LElseResult: string;
  LPointer: integer;
begin
  if AParams.Count > 1 then begin
    LValue := AParams[0];
    LElseResult := AParams[1];
    LPointer := 2;
    while AParams.Count > (LPointer + 1) do begin
      LCompare := AParams[LPointer];
      LCompareResult := AParams[LPointer + 1];
      LPointer := LPointer + 2;
      if LValue = LCompare then begin
        result := LCompareResult;
        exit;
      end;
    end;
    result := LElseResult;
  end
  else
    FInter.DoLog('E', '$VT - number of params too low');
end;

function TBafVarModule.ValueTranslateRootDB(AParams: TStrings): string;
// 0 - item
// 1 - Value of item 1
// 2 - Result 1
// 3 - Value of item 2
// 4 - Result 2
// ...
var
  LIni: TStringIniFile;
  LValue, LValue2: string;
  ix: integer;
begin
  if AParams.Count > 2 then begin
    LIni := dataMain.Ini;
    LValue := AnsiLowerCase(LIni.ReadString(dataMain.DBName, AParams[0], ''));
    if LValue <> '' then begin
      ix := 1;
      while (ix + 1) < AParams.Count do begin
        LValue2 := AnsiLowerCase(AParams[ix]);
        if LValue = LValue2 then begin
          result := AParams[ix + 1];
          break;
        end;
        inc(ix, 2);
      end; // while ix < AParams.Count
    end; // if LValue <> ''
  end
  else
    FInter.DoLog('E', '$VTRDB(item, value1, result1...) - count of params less 3');
// function TBafVarModule.ValueTranslateRootDB
end;

function TBafVarModule.ValueTranslateValueList(AParams: TStrings): string;
// 0 - Value
// 1 - else-result
// 2 - Number of Text
var
  LValue: string;
  ix: integer;
  sl: TStringList;
begin
  if AParams.Count > 2 then begin
    LValue := AParams[0];
    result := AParams[1];
    ix := StrToIntDef(AParams[2], -1);
    if ix >= 0 then begin
      sl := GetTextStringList(ix);
      ix := sl.IndexOfName(LValue);
      if ix >= 0 then
        result := sl.ValueFromIndex[ix];
    end
    else
      FInter.DoLog('E', '$VTVL(ix) - id of text not numeric or less 0');
  end
  else
    FInter.DoLog('E', '$VTVL - number of params too low');
end;

function TBafVarModule.InterpretLine(AExecInter: TBafCustomInterpreter): boolean;
var
  LNum: integer;
  LInter: TBafCustomInterpreter;
begin
  LInter := FExecInter;
  try
    FExecInter := AExecInter;

    result := true;
    if FExecInter.LineF = '#text_clearall' then FTextList.Clear                              // clear all texts
    else if FExecInter.LineF = '#textclearall' then FTextList.Clear
    else if FExecInter.LineF = '#kat_clearall' then FKatList.Clear
    else if BafIsNumberedFunk(FExecInter.LineF, '#text', LNum) then SetText(LNum)            // add a line to the text
    else if BafIsNumberedFunk(FExecInter.LineF, '#textn', LNum) then SetTextN(LNum)
    else if BafIsNumberedFunk(FExecInter.LineF, '#text_clear', LNum) then ClearText(LNum)    // clears one text
    else if BafIsNumberedFunk(FExecInter.LineF, '#textclear', LNum) then ClearText(LNum)
    else if BafIsNumberedFunk(FExecInter.LineF, '#text_save', LNum) then SaveText(LNum)      // saves the text to a file
    else if BafIsNumberedFunk(FExecInter.LineF, '#textsave', LNum) then SaveText(LNum)
    else if BafIsNumberedFunk(FExecInter.LineF, '#text_open', LNum) then OpenText(LNum)      // opens the text from a file
    else if BafIsNumberedFunk(FExecInter.LineF, '#textopen', LNum) then OpenText(LNum)
    else if BafIsNumberedFunk(FExecInter.LineF, '#text_props', LNum) then TextProperties(LNum)
    else if BafIsNumberedFunk(FExecInter.LineF, '#text_set', LNum) then TextSet(LNum)
    else if BafIsNumberedFunk(FExecInter.LineF, '#text_compose', LNum) then TextCompose(LNum)
    else if BafIsNumberedFunk(FExecInter.LineF, '#text_dissect', LNum) then TextDissect(LNum)
    else if BafIsNumberedFunk(FExecInter.LineF, '#text_sort', LNum) then TextSort(LNum)
    else if BafIsNumberedFunk(FExecInter.LineF, '#text_loop', LNum) then TextLoop(LNum)
    else if BafIsNumberedFunk(FExecInter.LineF, '#text_act', LNum) then TextAct(LNum)
    else if BafIsNumberedFunk(FExecInter.LineF, '#tvl_add', LNum) then TvlAdd(LNum)

    else if BafIsNumberedFunk(FExecInter.LineF, '#ini_val', LNum) then SetIniValue(LNum)
    else if BafIsNumberedFunk(FExecInter.LineF, '#ini_load', LNum) then IniLoadSave(LNum, false)
    else if BafIsNumberedFunk(FExecInter.LineF, '#ini_save', LNum) then IniLoadSave(LNum, true)


    else if BafIsNumberedFunk(FExecInter.LineF, '#cmd', LNum) then SetCmd(LNum)
    else if BafIsNumberedFunk(FExecInter.LineF, '#cmd_clear', LNum) then ClearCmd(LNum)
    else if BafIsNumberedFunk(FExecInter.LineF, '#cmdclear', LNum) then ClearCmd(LNum)
    else if FExecInter.LineF = '#cmd_clearall' then ClearCmds
    else if FExecInter.LineF = '#cmdclearall' then ClearCmds
    else if FExecInter.LineF = '#code' then FCode.Add(FExecInter.LineP)                 // adds a code line
    else if FExecInter.LineF = '#setvar' then SetVar(FNamedVars, false)
    else if FExecInter.LineF = '#var_set' then SetVar(FNamedVars, false)                            // sets a variable
    else if FExecInter.LineF = '#var_clearall' then FNamedVars.Clear                    // clears all variables
    else if FExecInter.LineF = '#setvarempty' then SetVar(FNamedVars, true)
    else if FExecInter.LineF = '#var_setempty' then SetVar(FNamedVars, true)                        // sets a variable if empty
    else if FExecInter.LineF = '#var_add' then AddVar(FNamedVars)                                   // adds to a variable
    else if FExecInter.LineF = '#var_log' then LogVars(FNamedVars)                                  // writes all variables to the log
    else if FExecInter.LineF = '#gvar_set' then SetVar(gvGlobalVars, false)                            // sets a global variable
    else if FExecInter.LineF = '#gvar_setempty' then SetVar(gvGlobalVars, true)                        // sets a global variable if empty
    else if FExecInter.LineF = '#gvar_clearall' then gvGlobalVars.Clear                    // clears all global variables
    else if FExecInter.LineF = '#gvar_add' then AddVar(gvGlobalVars)                                   // adds to a global variable
    else if FExecInter.LineF = '#gvar_log' then LogVars(gvGlobalVars)                                  // writes all global variables to the log
    else if BafIsNumberedFunk(FExecInter.LineF, '#sepline', LNum) then SepLine(LNum)    // converts a string in a sparated list

    else if FExecInter.LineF = '#rights' then RightsDef
    else if FExecInter.LineF = '#rights_clear' then FRightDefs.Clear

    else if BafIsNumberedFunk(FExecInter.LineF, '#csv_open', LNum) then CsvOpen(LNum)   // opens a csv file
    else if BafIsNumberedFunk(FExecInter.LineF, '#csv_line', LNum) then CsvLine(LNum)   // executes a command for each line of the csv file
    else if BafIsNumberedFunk(FExecInter.LineF, '#csv_check', LNum) then CsvCheck(LNum)   // Checks a CSV
    else if FExecInter.LineF = '#csv_paste' then PasteCsv                               // execute csv data in the clipboard

    else if FExecInter.LineF = '#exec' then Exec                                        // executes a command
    else if FExecInter.LineF = '#exec_async' then ExecAsync                             // executes a command
    else if FExecInter.LineF = '#dir_proc' then Procdir                                 // processes all files in a directory
    else if FExecInter.LineF = '#dir_force' then Forcedir                               // forces a dir
    else if FExecInter.LineF = '#batchexec' then BatchExec                              // converts a text into a batch and executes it
    else if FExecInter.LineF = '#openfile' then DoOpenFile
    else if FExecInter.LineF = '#file_open' then DoOpenFile                             // WIN-Api OpenFile
    else if FExecInter.LineF = '#clipboard' then Save2Clipboard                         // copy data to the clipboard
    else if FExecInter.LineF = '#file_op' then FileOperation                            // files and dirs
    else if FExecInter.LineF = '#file_search' then BafFileSearch                        // searches files
    else if FExecInter.LineF = '#file_wait' then BafFileWait                            // waits until a file is found
    else if FExecInter.LineF = '#zip_create' then BafZipCreate
//    else if FExecInter.LineF = '#zip_folder' then BafZipFolder
//    else if FExecInter.LineF = '#zip_extract' then BafZipExtract

    else if FExecInter.LineF = '#loop' then Loop                                        // processes a loop
    else if FExecInter.LineF = '#exit' then FExecInter.ExecutionBreak                   // exits the code

    else if FExecInter.LineF = '#check_string' then CheckString

    else result := false;
  finally
    FExecInter := LInter;
  end;
end;

function TBafVarModule.IsAdmin(AParams: TStrings; ARight: boolean): string;
var
  AResult: boolean;
begin
  if ARight then
    AResult := gvBafDataCache.RightIsAdmin
  else
    AResult := gvBafDataCache.IsAdmin;
  if AResult then begin
    if AParams.Count > 0 then
      result := AParams[0]
    else
      result := BAFYESCHAR
  end
  else begin
    if AParams.Count > 1 then
      result := AParams[1]
    else
      result := BAFNOCHAR
  end;
end;

end.

