{$A+,B-,C+,D+,E-,F-,G+,H+,I+,J+,K-,L+,M-,N+,O+,P+,Q-,R-,S-,T-,U-,V+,W-,X+,Y+,Z1}

{$MINSTACKSIZE $00004000}

{$MAXSTACKSIZE $00100000}

{$IMAGEBASE $00400000}

{.$APPTYPE GUI}
{$I define.pas}
unit ExeLogu;

interface
uses Classes, SysUtils{v0.39}, WinUtl, PropUtl{/v0.39}{v0.43},UtlType{/v0.43}
  {v0.44}, MMSystem{/v0.44}{v0.61}, Windows, DLLUtl{/v0.61}, SyncObjs
  ;

{v0.39}
const
  LogExt = '.LOG';
  DefLogDir = 'LOG';
{/v0.39}
{v0.43}
  LogSection = 'LOG';{for INI file}
{/v0.43}
type
  TExeLogLog = procedure(const msg:string) of object;

  {v0.62}
  TTodoWithFile = (twfDelete, twfMove);
  {/v0.62}

  { v0.62 reorganized a little bit according to apache LogLevel,
  TLogEvents = (
     leEmerg, // emerg Emergencies - system is unusable. "Child cannot open lock file. Exiting"
     leAlert,  // alert Action must be taken immediately. "getpwuid: couldn't determine user name from uid"
     leCrit,  // crit  Critical Conditions. "socket: Failed to get a socket, exiting child"
     leError, //error  Error conditions. "Premature end of script headers"
     leWarn,  // warn Warning conditions. "child process 1234 did not exit, sending another SIGHUP"
     leNotice, // notice  Normal but significant condition. "httpd: caught SIGBUS, attempting to dump core in ..."
     leInfo,  // info  Informational. "Server seems busy, (you may need to increase StartServers, or Min/MaxSpareServers)..."

     leDebug  // debug  Debug-level messages "Opening config file ..."
   ); }

  TLogEvents = (
    leFatalError,  { real disaster happened }
    leError,       { some real program error occurred (unexpected, that should not happen) }
    leWarning,     { some expectable problem arised (disk full,...) }
    leAction,      { some important not very often occuring program process started/ended }
    leInfo,        { default event, information about anything happening, used if Log() method }

    leUserWarning, { user tried to do something what he should not, gui window appeared }
    leUserInfo,    { user is informed that some action started, is going on or ended,
                     gui window appeared}
    leUserConfirm, { user is aked to do some decision, gui window appeared }
    leDebug       { debugging purposes (a lot of logs) }
  );

  (*/v0.62
  TLogEvents = (
    leInfo,   { default event, information about anything happening, used if Log() method }
    leAction, { some important not very often occuring program process started/ended }
    leDebug,  { debugging purposes }
    leUserWarning, { user tried to do something what he should not, gui window appeared }
    leUserInfo,    { user is informed that some action started, is going on or ended,
                     gui window appeared}
    leUserConfirm,  { user is aked to do some decision, gui window appeared }
    leWarning,     { some expectable problem arised (disk full,...) }
    leError,       { some real program error occurred (unexpected, that should not happen) }
    leFatalError   { real disaster happened }
  );
  *)
  {v0.60}
  TFileNameTimeKind = (
     ftkNormal,
     ftkYear,       // name of the log file will end by current year, e.g. LogFile2002.log
     ftkMonth,      // name of the log file will end by current month, e.g. LogFile200204.log
     ftkDay,        // name of the log file will end by current date, e.g. LogFile20020418.log
     ftkHour        // name of the log file will end by current date+hour, e.g. LogFile2002041819.log
  );
  {/v0.60}

  TLogEvent = set of TLogEvents;

  TExeLog = class(TComponent)
  private
    FLogDir: string;
      { Directory for log files (if not absolute path, then relative to exedir
        is considered), default = LOG. Used if FFileName does not contain absolute
        path }
    {v0.39}
    FDefExt: string;
      { Appended if AFileName has no extension. = .LOG }
    {/v0.39}
    FFileName: string;
      { Name of the log file. If none specified then ExeName.Log is used.
        If does not contain absolute path then placed to FLogDir. }{logu}
    FText: text;
      { the text file handle }
    FActive: boolean;
      { is the log file opened }
    FAutoOpen: boolean;
      { should the log file be opened upon program start? }
    FOverWrite: boolean;
      { should erase eventually existing log file upon opening? }
    //FLogEvent: TLogEvent;
      { last log event logged }
    FAutoFlushInterval: integer;
      { how often the log should be flushed to disk (in seconds), not
        flushed at all if = 0 (flushing can take place only during Log call),
        if = 1, then flushed every log call (useful for debug crash) }
    FLastFlushTime: TDateTime;
      { last time when the file was flushed to disk }
    FOnLog: TExeLogLog;
    {v0.26}
    FAssigningOnLog: boolean;
    {/v0.26}
    {v0.39}
    FLogToSysLog: boolean;
      { Log not only to itself, but also to program's main log file?
        (if self is not the main log file) }
    {/v0.39}
    {v0.60}
    FFileNameTimeKind: TFileNameTimeKind;
    FOpenDate: TDateTime;
      { when the file was open (what corresponding file name
        it is using) }
    FBaseName: string;
      { if defined, then used to create time dependent filenames }
    {/v0.60}
    {v0.62}
    FAutoDeleteFilesOlderThan: integer;
       { if set to non zero, then upon closing log file all time dependent
         log files older than specified number of days will be deleted }
    {/v0.62}
    {v0.44}
    FNoHead: boolean;
    FNoFoot: boolean;
    FDateTimeInLine: TDateTimeParts;
    FCritSec: TCriticalSection;
    FFileSuff: string;// optional suffix to add after base name of the file,
    // before the eventual date part. Should always start and end with _
    // if date part used in the file name

    procedure Start;
    procedure Stop;

    function GetDateTimeSec: string;
    {/v0.44}
    {v0.60}
    procedure SetFileNameTimeKind(AFileNameTimeKind: TFileNameTimeKind);
    procedure SetOpenDate(AOpenDate: TDateTime);
    { Called from Destroy, checks if old log files should be deleted and
      deletes them. }
    procedure CheckAutoDelete;
    procedure SetBaseName(const ABaseName: string);
    {/v0.62}
    procedure Lock;
    procedure Unlock;
    procedure SetFileSuff(const AFileSuff: string);
  protected
    procedure SetActive(OnOff:boolean);
    procedure SetFileName(const AFileName: string);
    {v0.60}
    function GetFileName: string;
    {/v0.60}
    {v0.26}
    procedure SetOnLog(AExeLogLog: TExeLogLog);
    {/v0.26}
    {v0.39}
    procedure SetLogDir(const ADir: string);
    {/v0.39}
    function GetExpandedLogPath: string;
  public
    class function GetTimeFileStr(AFileNameTimeKind: TFileNameTimeKind;
      ADateTime: TDateTime): string;
      { returns substring used for creation of time dependent file name;
        used to compare if filename should be changed }
    class function GetTimeFileName(const AFileName: string; const AFileSuff: string;
       AFileNameTimeKind: TFileNameTimeKind; ATime: TDateTime): string;
    {/v0.60}
    {v0.62}
    class function ParseTimeFileName(const AFileName: string;
      var ABaseName: string; var AFileNameTimeKind: TFileNameTimeKind;
      var ADateTime: TDateTime): boolean;


    constructor Create(AOwner: TComponent; const AFileName: string);reintroduce;
      { if AFileName = '' then ExeName.LOG will be used; Created not Active!!! }
    destructor Destroy;override;

    { Thread safe methods }
    procedure Log(const Msg: string);
    {v0.39}
    procedure LogErr(const Msg: string);
    {/v0.39}
    procedure LogEvent(ALogEvent: TLogEvent; const Msg: string);
    {/ Thread safe methods }

    {v0.60}
    function TimeFileName(ADateTime: TDateTime): string;
      { what is the name of log file with data from given time }
    procedure FlushLog;
    {/v0.60}

    property Active: boolean read FActive write SetActive;
    property AutoOpen: boolean read FAutoOpen write FAutoOpen;
    property FileName: string read {v0.60}GetFileName{/v0.60 FFileName} write SetFileName;
    {v0.39 moved to published}{/v0.39
    property OverWrite: boolean read FOverWrite write FOverWrite;}
    {v0.42}
    {/v0.42
    property AutoFlushInterval: integer read FAutoFlushInterval write FAutoFlushInterval;}
    property OnLog: TExeLogLog read FOnLog write {v0.26}SetOnLog{/v0.26 FOnLog};
    {v0.60}
    property OpenDate: TDateTime read FOpenDate write SetOpenDate;
    property BaseName: string read FBaseName write {v0.62}SetBaseName{/v0.62 FBaseName};
    property FileSuff: string read FFileSuff write SetFileSuff;
    {/v0.60
    property LogDir: string read FLogDir write SetLogDir;
    }
  {v0.39}
  {v0.62}
    property ExpandedLogPath: string read GetExpandedLogPath;
  class procedure HandleTimeFileNameFiles(
    What: TTodoWithFile;
    const ASourceDir: string;
    const ABaseName: string;
    const AExt: string;
    AOlderThanXDays: integer;
    const ADestDir: string);

    { Find files from specified directory ADir, that has given ABaseName,
      given extension AExt, that's file name part (without path and extension)
      end with a date/time part specifying from what date/time period the file
      contain data. Delete those files that are older then specified number
      of days - AOlderThanXDays. }
    class procedure DeleteTimeFileNameFiles(
      const ADir: string;
      const ABaseName: string;
      const AExt: string;
      AOlderThanXDays: integer);
    class procedure MoveTimeFileNameFiles(const ASourceDir: string;
      const ABaseName: string;
      const AExt: string;
      AOlderThanXDays: integer;
      const ADestDir: string);
    {/v0.62}
    class function ExpandLogDir(const ADir: string): string;
  published
    property LogToSysLog: boolean read FLogToSysLog write FLogToSysLog;
    property OverWrite: boolean read FOverWrite write FOverWrite;
  {/v0.39}
  {v0.42}
    property AutoFlushInterval: integer read FAutoFlushInterval write FAutoFlushInterval;
  {/v0.42}
  {v0.44}
    property NoHead: boolean read FNoHead write FNoHead;
    property NoFoot: boolean read FNoFoot write FNoFoot;
    property DateTimeInLine: TDateTimeParts read FDateTimeInLine write FDateTimeInLine;
  {/v0.44}
  {v0.60}
    property LogDir: string read FLogDir write SetLogDir;
    property FileNameTimeKind: TFileNameTimeKind read FFileNameTimeKind write SetFileNameTimeKind;
  {/v0.60}
  {v0.62}
    property AutoDeleteFilesOlderThan: integer read FAutoDeleteFilesOlderThan write FAutoDeleteFilesOlderThan;
  {/v0.62}
  end;

const
  ExeLog: TExeLog = nil;

{v0.40}
{ Use to log every nonsense to track down a source of a bug. Turn it on to
do really the logging by .ini variable UseDebugLog=1. Other logs then copy
everything to debug.log too. }
procedure DebLog(const msg: string);
{/v0.40}

implementation

{v0.43}
const
  UseDebugLog: boolean = false;
  FDebLog: TExeLog = nil;
{/v0.43}

procedure TExeLog.Start;
begin
  if OpenDate = 0 then
    OpenDate := Now;
  AssignFile(FText, FileName);
  try
    if FileExists(FileName) then begin
      if FOverWrite then
        Rewrite(FText)
      else
        System.Append(FText);
    end else begin
      Rewrite(FText);
    end;
  {$IFDEF IGNOREFAILEDLOG}
  except
  {$ELSE}
  finally
  {$ENDIF}
  end;
  {v0.44}
  if not FNoHead then
  {/v0.44}
  begin
    Writeln(FText, '');
    Writeln(FText, '*** Log Started ' + FormatDateTime('',Now) + ' ***');
    Writeln(FText, '');
  end;
  FActive := true;
end;

procedure TExeLog.Stop;
begin
  FActive := false;
  if not FNoFoot then
  {/v0.44}
  begin
    Writeln(FText, '');
    Writeln(FText, '*** Log Stopped ' + FormatDateTime('',Now) + ' ***');
    Writeln(FText, '');
  end;
  CloseFile(FText);
  OpenDate := 0;
end;

procedure TExeLog.SetActive(OnOff:boolean);
begin
  Lock;
  try
    if OnOff <> FActive then begin
      if OnOff then begin
        Start;
      end else begin
        Stop;
      end;
    end;
  finally
    Unlock;
  end;
end;

procedure TExeLog.SetFileName(const AFileName: string);
var wasa:boolean;
begin
  if AFileName = '' then
    exit;
  if AFileName <> {v0.60}FileName{/v0.60 FFileName} then begin
    {v0.39}
    if ExtractFileExt(AFileName) = '' then
      FFileName := ChangeFileExt(AFileName, FDefExt)
    else
      FFileName := AFileName;
    if ExtractFilePath(FFileName) = '' then begin
      WinUtl.CheckDirExists(ExpandedLogPath);
      FFileName := ExpandedLogPath{FLogDir} + FFileName;
    end;
    {/v0.39}
    wasa := Active;
    if wasa then
      Active := false;
    FFileName := ExpandFileName({v0.39}FFileName{/v0.39 AFileName});
    {v0.62}
    if FileNameTimeKind <> ftkNormal then
      BaseName := FFileName;
    {/v0.62}
    if wasa then
      Active := true;
  end;
end;


procedure TExeLog.SetLogDir(const ADir: string);
var
  wasa: boolean;
  d: string;
begin
  d := ExpandLogDir(ADir);
  if d <> ExpandLogDir(FLogDir) then begin
    if WinUtl.CheckDirExists(d) then begin
      wasa := Active;
      try
        Active := false;
        FLogDir := ADir;
      finally
        Active := wasa;
      end;
    end;
  end;
end;

class function TExeLog.ExpandLogDir(const ADir: string): string;
begin
  if ADir = '' then begin
    Result := ExtractFilePath(GetModuleName(HInstance)) + DefLogDir;
  end else begin
    if ExtractFilePath(ADir) = '' then begin
      Result := ExtractFilePath(GetModuleName(HInstance)) + ADir;
    end else begin
      Result := ADir;
    end;
  end;
  Result := AddBackSlash(Result);
end;

function TExeLog.GetExpandedLogPath: string;
begin
  Result := ExpandLogDir(FLogDir);

end;

function GetSecName(const AFileName: string): string;
begin
  if AFileName = '' then begin             {dllutl}
    Result := {v0.61}GetModuleName(HInstance{GetModuleHandle(nil))});
      {/v0.61 paramstr(0)}
  end else begin
    Result := AFileName;
  end;
  Result := ChangeFileExt(ExtractFileName(Result), '') + 'Log';
end;
{/v0.39}

constructor TExeLog.Create(AOwner: TComponent; const AFileName: string);
begin
  inherited Create(AOwner);
  {v0.39}
  FCritSec := TCriticalSection.Create;
  FDefExt := LogExt;
  LogDir := '';
  if Name = '' then
    Name := GetSecName(AFileName);
  {v0.62}
  if AFileName = '' then begin
    { new defaults for application log }
    DateTimeInLine := [dtSecond, dtMinute, dtHour];
    FileNameTimeKind := ftkDay;
    FAutoDeleteFilesOlderThan := 365;
  end;
  {/v0.62}
  {v0.76}
  {/v0.76}
  ClassReadWriteIniFile(Self, 2, '', true);
  {/v0.39}
  if AFileName = '' then begin
    {v0.62 so that it uses Log dir from ini file}
    SetFileName(ChangeFileExt(ExtractFileName(GetModuleName(HInstance)), FDefExt));
    {/v0.62
    SetFileName(ChangeFileExt(GetModuleName(HInstance), FDefExt));
    }
  end {v0.39} else begin
    SetFileName(AFileName);
  end{/v0.39};
end;

destructor TExeLog.Destroy;
begin
  Lock;
  try
    {v0.62}
    CheckAutoDelete;
    {/v0.62}
    Active := false;
    ClassReadWriteIniFile(Self, 2, '', false);
  finally
    Unlock;
  end;
  FCritSec.Free;
  inherited Destroy;
end;

procedure TExeLog.Log(const Msg: string);
begin
  if not Active then
    exit;
  LogEvent([leInfo], Msg);
end;

procedure TExeLog.LogErr(const Msg: string);
begin
  if not Active then
    exit;
  LogEvent([leError], Msg);
end;

procedure TExeLog.SetOnLog(AExeLogLog: TExeLogLog);
begin
{  if AExeLogLog <> FOnLog then
    exit;}
  Lock;
  try
    fAssigningOnLog := true;
    try
      FOnLog := AExeLogLog;
    finally
      fAssigningOnLog := false;
    end;
  finally
    Unlock;
  end;
end;

{v0.44}
function TExeLog.GetDateTimeSec: string;
var
  i: TDateTimePart;
  d: TDateTime;
  dt: array[TDateTimePart] of word;
  ms: integer;

  function IntToString(i:integer; len:integer):string;
  begin
    str(i, result);
    while length(result) < len do
      result := '0' + result;
  end;

begin
  Result := '';
  if DateTimeInLine <> [] then begin
    d := Now;
    //ms := (TimeGetTime mod 1000);
    DecodeDate(d, dt[dtYear], dt[dtMonth], dt[dtDay]);
    DecodeTime(d, dt[dtHour], dt[dtMinute], dt[dtSecond], dt[dtMilisecond]);
    ms := dt[dtMilisecond];//???
    for i := high(TDateTimePart) downto low(TDateTimePart) do begin
      if i in DateTimeInLine then begin
        case i of
          dtYear: Result := Result + IntToStr(dt[i]) + '-';
          dtMonth, dtDay: Result := Result + IntToString(dt[i],2) + '-';
          {dtDay: Result := Result := IntToString(dt[i],2) + '-';}
          dtHour,dtMinute: Result := Result + IntToString(dt[i],2) + ':';
          dtSecond: Result := Result + IntToString(dt[i],2);
          dtMilisecond: Result := Result + '.' + IntToString(ms,3);
        end;
      end;
    end;
    Result := Result + ' ';
  end;
end;
{/v0.44}
{v0.62}
class function TExeLog.ParseTimeFileName(const AFileName: string; var ABaseName: string; var AFileNameTimeKind: TFileNameTimeKind;
  var ADateTime: TDateTime): boolean;
var
  b: string;
  ds: string;
  i: integer;
  y,m,d: word;
begin
  Result := false;
  b := ChangeFileExt(ExtractFilename(AFileName), '');
  ds := '';
  for i := length(b) downto 1 do begin
    if b[i] in ['0'..'9'] then begin
      ds := b[i] + ds;
    end else
      break;
  end;
  if ds = '' then begin
    ABaseName := b;
    AFileNameTimeKind := ftkNormal;
    ADateTime := Now;
  end else begin
    case length(ds) of
      4: begin // yyyy
        ADateTime := IndexStrToDateTime(ds + '1231235959');
        AFileNameTimeKind := ftkYear;
      end;
      6: begin // yyyymm
        ADateTime := IndexStrToDateTime(ds + '01');
        DecodeDate(ADateTime, y, m, d);
        d := DaysInMonth(y, m);
        ADateTime := EncodeDate(y, m, d) + EncodeTime(23, 59, 59, 0);
        AFileNameTimeKind := ftkMonth;
      end;
      8: begin // yyyymmdd
        ADateTime := IndexStrToDateTime(ds + '235959');
        AFileNameTimeKind := ftkDay;
      end;
      10: begin // yyyymmddhh
        ADateTime := IndexStrToDateTime(ds + '5959');
        AFileNameTimeKind := ftkHour;
      end;
    else
      exit;
    end;
    ABaseName := copy(b, 1, length(b) - length(ds));
  end;
  Result := true;
end;
{/v0.62}

{v0.60}
class function TExeLog.GetTimeFileStr(AFileNameTimeKind: TFileNameTimeKind;
  ADateTime: TDateTime): string;
  var s: string;
begin
  Result := '';
  s := '';
  case AFileNameTimeKind of
    ftkHour: s := 'yyyymmddhh';
    ftkDay: s := 'yyyymmdd';
    ftkMonth: s := 'yyyymm';
    ftkYear: s := 'yyyy';
  else
    exit;
  end;
  Result := FormatDateTime(s, ADateTime);
end;

class function TExeLog.GetTimeFileName(const AFileName: string; const AFileSuff: string;
  AFileNameTimeKind: TFileNameTimeKind; ATime: TDateTime): string;
var s: string;
begin
  if AFileNameTimeKind = ftkNormal then begin
    Result := AFileName + AFileSuff;
  end else begin
    s := GetTimeFileStr(AFileNameTimeKind, ATime);
    Result :=
      ExtractFilePath(AFileName) +
      ChangeFileExt(ExtractFileName(AFileName), '') + AFileSuff +
      s +
      ExtractFileExt(AFileName);
  end;
end;

function TExeLog.TimeFileName(ADateTime: TDateTime): string;
begin
  if BaseName <> '' then begin
    Result := ExpandedLogPath + GetTimeFileName(BaseName, FileSuff, FileNameTimeKind, ADateTime) + FDefExt;
  end else begin
    Result := GetTimeFileName(FFileName, FileSuff, FileNameTimeKind, ADateTime);
  end;
end;

function TExeLog.GetFileName: string;
begin
  if BaseName <> '' then begin
    Result := ExpandedLogPath + GetTimeFileName(BaseName, FileSuff, FileNameTimeKind, OpenDate) + FDefExt;
  end else begin
    Result := GetTimeFileName(FFileName, FileSuff, FileNameTimeKind, Date);
  end;
end;

procedure TExeLog.SetFileNameTimeKind(AFileNameTimeKind: TFileNameTimeKind);
var
  wasa: boolean;
begin
  if FFileNameTimeKind = AFileNameTimeKind then
    exit;
  wasa := Active;
  try
    Active := false;
    {v0.62}
    if AFileNameTimeKind <> ftkNormal then begin
      BaseName := FileName;
    end;
    {/v0.62}
    FFileNameTimeKind := AFileNameTimeKind;
  finally
    Active := wasa;
  end;
end;

procedure TExeLog.SetOpenDate(AOpenDate: TDateTime);
begin
  FOpenDate := AOpenDate;
end;

procedure TExeLog.FlushLog;
begin
  if not Active then
    exit;
  Flush(FText);
  FLastFlushTime := Now;
end;
{/v0.60}
procedure TExeLog.LogEvent(ALogEvent: TLogEvent; const Msg: string);
{v0.39}
var s: string;
{/v0.39}
begin
  Lock;
  try
    if not FActive then
      exit;
    if FileNameTimeKind <> ftkNormal then begin
      if GetTimeFileStr(FileNameTimeKind, OpenDate) <>
        GetTimeFileStr(FileNameTimeKind, Now) then
      begin
        // close the log file and open it with new file name (time dependent)
        Stop;//Active := false;
        Start;//Active := true;
      end;
    end;
    //FLogEvent := ALogEvent;
    s := Msg;
    if (leError in ALogEvent) then
      s := 'ERR: ' + Msg;
    s := GetDateTimeSec + s;
    IoResult;
    Writeln(FText, s);
    if (not FAssigningOnLog) and Assigned(FOnLog) then
      FOnLog(s);
    if FLogToSysLog and (Self <> ExeLog) and (ExeLog <> nil) and (Self <> FDebLog) then
      ExeLog.LogEvent(ALogEvent, s);
    if UseDebugLog and (Self <> FDebLog) then
      DebLog(s);
    if FAutoFlushInterval <> 0 then begin
      if (FAutoFlushInterval = 1) or
        ((Now - FLastFlushTime) * 24 * 3600 > FAutoFlushInterval) then
      begin
        FlushLog;
      end;
    end;
  finally
    Unlock;
  end;
end;


{v0.40}
{v0.43}{/v0.43 const
  FDebLog: TExeLog = nil;}

{v0.62}
procedure TExeLog.SetBaseName(const ABaseName: string);
begin
  FBaseName := ChangeFileExt(ExtractFileName(ABaseName), '');
  while (length(FBaseName) > 0) and (FBaseName[length(FBaseName)] in ['0'..'9']) do begin
    SetLength(FBaseName, length(FBaseName) - 1);
  end;
end;

procedure TExeLog.SetFileSuff(const AFileSuff: string);
var wasAc: boolean;
begin
  if FFileSuff = AFileSuff then
    exit;
  wasAc := Active;
  if wasAc then
    Active := false;
  FFileSuff := AFileSuff;
  if wasAc then
    Active := true;
end;

class procedure TExeLog.HandleTimeFileNameFiles(
  What: TTodoWithFile;
  const ASourceDir: string;
  const ABaseName: string;
  const AExt: string;
  AOlderThanXDays: integer;
  const ADestDir: string);
var
  dd, fd: TDateTime;
  dir, bn: string;
  ftk: TFileNameTimeKind;
  sr: TSearchRec;
  res: integer;
begin
  if AOlderThanXDays <= 0 then
    exit;
  try
    dd := Now - AOlderThanXDays;
    dir := AddBackSlash(ASourceDir);
    res := SysUtils.FindFirst(dir + ABaseName + '*' + AExt, faAnyFile, sr);
    try
      while res = 0 do begin
        if ParseTimeFileName(sr.Name, bn, ftk, fd) and (ftk <> ftkNormal) then begin
          if fd <= dd then begin
            case What of
              twfDelete:  SysUtils.DeleteFile(dir + sr.Name);
              twfMove: begin
                if WinUtl.CheckDirExists(ADestDir) then begin
                  if Windows.CopyFile(PChar(dir + sr.Name), PChar(ADestDir), false) then
                    SysUtils.DeleteFile(dir + sr.Name);
                end;
              end;
            end;
          end;
        end;
        res := SysUtils.FindNext(sr);
      end;
    finally
      SysUtils.FindClose(sr);
    end;
  except // ignore eventual errors (date/time conversion mostly) completely for now
  end;
end;


class procedure TExeLog.MoveTimeFileNameFiles(const ASourceDir: string;
  const ABaseName: string;
  const AExt: string;
  AOlderThanXDays: integer;
  const ADestDir: string);
begin
  HandleTimeFileNameFiles(twfMove,
    ASourceDir, ABaseName, AExt, AOlderThanXDays, ADestDir);
end;

class procedure TExeLog.DeleteTimeFileNameFiles(
  const ADir: string;
  const ABaseName: string;
  const AExt: string;
  AOlderThanXDays: integer);
begin
  HandleTimeFileNameFiles(twfDelete,
    ADir, ABaseName, AExt, AOlderThanXDays, '');
end;

procedure TExeLog.CheckAutoDelete;
begin
  if FileNameTimeKind = ftkNormal then
    exit;
  DeleteTimeFileNameFiles(FLogDir, BaseName, FDefExt, FAutoDeleteFilesOlderThan);
end;

procedure TExeLog.Lock;
begin
  FCritSec.Enter;
end;
procedure TExeLog.Unlock;
begin
  FCritSec.Leave;
end;

{/v0.62}

procedure DebLog(const msg: string);
begin
  if not UseDebugLog then
    exit;
  if FDebLog = nil then begin
    FDebLog := TExeLog.Create(nil, 'DebLog');
    FDebLog.Active := true;
  end;
  FDebLog.Log(msg);
end;
{/v0.40}

initialization
  ExeLog := TExeLog.Create(nil, '');
  {ExeLog.OverWrite := true;}
  try
    {$IFNDEF NOAUTOEXELOG}
    ExeLog.Active := true;
    {$ENDIF}
  except
  end;
  {v0.43}
  {$IFNDEF NODEBLOG}
  ConfigReadWriteValue(nil, rwRead, LogSection, 'UseDebugLog' , @UseDebugLog, ptByte);
  {$ENDIF}
  {/v0.43}
finalization
  ExeLog.Free;
  {v0.40}
  FDebLog.Free;
  {/v0.40}
  {v0.43}
  {$IFNDEF NODEBLOG}
  ConfigReadWriteValue(nil, rwWrite, LogSection, 'UseDebugLog' , @UseDebugLog, ptByte);
  {$ENDIF}
  {/v0.43}
end.
