unit MaskUtils;

interface

Type
  DWord = LongWord;
  BOOL = LongBool;

const
  PathDelim  = '\';
  DriveDelim = ':';
  PathSep    = ';';

  user32   = 'user32.dll';
  kernel32 = 'kernel32.dll';
  shell32  = 'shell32.dll';
  MAX_PATH = 260;
  {$EXTERNALSYM MAX_PATH}
  FILE_ATTRIBUTE_DIRECTORY            = $00000010;
  {$EXTERNALSYM FILE_ATTRIBUTE_DIRECTORY}

  faReadOnly  = $00000001 platform;
  faHidden    = $00000002 platform;
  faSysFile   = $00000004 platform;
  faVolumeID  = $00000008 platform;
  faDirectory = $00000010;
  faArchive   = $00000020 platform;
  faSymLink   = $00000040 platform;
  faAnyFile   = $0000003F;
  INVALID_HANDLE_VALUE = DWORD(-1);
  {$EXTERNALSYM INVALID_HANDLE_VALUE}

Type
  TMbcsByteType = (mbSingleByte, mbLeadByte, mbTrailByte);
  TSysLocale = packed record
    DefaultLCID: Integer;
    PriLangID: Integer;
    SubLangID: Integer;
    FarEast: Boolean;
    MiddleEast: Boolean;
  end;
  TFileName = type string;

  PFileTime = ^TFileTime;
  _FILETIME = record
    dwLowDateTime: DWORD;
    dwHighDateTime: DWORD;
  end;
  {$EXTERNALSYM _FILETIME}
  TFileTime = _FILETIME;
  FILETIME = _FILETIME;
  {$EXTERNALSYM FILETIME}

  _WIN32_FIND_DATAA = record
    dwFileAttributes: DWORD;
    ftCreationTime: TFileTime;
    ftLastAccessTime: TFileTime;
    ftLastWriteTime: TFileTime;
    nFileSizeHigh: DWORD;
    nFileSizeLow: DWORD;
    dwReserved0: DWORD;
    dwReserved1: DWORD;
    cFileName: array[0..MAX_PATH - 1] of AnsiChar;
    cAlternateFileName: array[0..13] of AnsiChar;
  end;
  TWin32FindDataA = _WIN32_FIND_DATAA;
 // TWin32FindDataW = _WIN32_FIND_DATAW;
  TWin32FindData = TWin32FindDataA;


  TSearchRec = record
    Time: Integer;
    Size: Integer;
    Attr: Integer;
    Name: TFileName;
    ExcludeAttr: Integer;
    FindHandle: THandle  platform;
    FindData: TWin32FindData  platform;
  end;

    HWND = type LongWord;
  {$EXTERNALSYM HWND}

    PSystemTime = ^TSystemTime;
  _SYSTEMTIME = record
    wYear: Word;
    wMonth: Word;
    wDayOfWeek: Word;
    wDay: Word;
    wHour: Word;
    wMinute: Word;
    wSecond: Word;
    wMilliseconds: Word;
  end;
  {$EXTERNALSYM _SYSTEMTIME}
  TSystemTime = _SYSTEMTIME;
  SYSTEMTIME = _SYSTEMTIME;
  {$EXTERNALSYM SYSTEMTIME}

    PSecurityAttributes = ^TSecurityAttributes;
  _SECURITY_ATTRIBUTES = record
    nLength: DWORD;
    lpSecurityDescriptor: Pointer;
    bInheritHandle: BOOL;
  end;
  {$EXTERNALSYM _SECURITY_ATTRIBUTES}
  TSecurityAttributes = _SECURITY_ATTRIBUTES;
  SECURITY_ATTRIBUTES = _SECURITY_ATTRIBUTES;
  {$EXTERNALSYM SECURITY_ATTRIBUTES}

  LongRec = packed record
    case Integer of
      0: (Lo, Hi: Word);
      1: (Words: array [0..1] of Word);
      2: (Bytes: array [0..3] of Byte);
  end;


var
  LeadBytes: set of Char = [];
  SysLocale: TSysLocale;


function DirectoryExists(const Directory: string): Boolean;
function FindFirst(const Path: string; Attr: Integer;
  var F: TSearchRec): Integer;
function FindNext(var F: TSearchRec): Integer;
function StrSatisfy( const S, Mask: String ): Boolean;
{$EXTERNALSYM FreeConsole}
function FreeConsole: BOOL; stdcall;
{$EXTERNALSYM CharLowerBuff}
function CharLowerBuff(lpsz: PChar; cchLength: DWORD): DWORD; stdcall;

function DeleteFile(lpFileName: PChar): BOOL; stdcall;
{$EXTERNALSYM DeleteFile}
function SHGetSpecialFolderPath(hwndOwner: HWND; lpszPath: PChar;
  nFolder: Integer; fCreate: BOOL): BOOL; stdcall;
{$EXTERNALSYM SHGetSpecialFolderPath}
function MoveFile(lpExistingFileName, lpNewFileName: PChar): BOOL; stdcall;
{$EXTERNALSYM MoveFile}
function ForceDirectories(Dir: string): Boolean;
function FindFirstFile(lpFileName: PChar; var lpFindFileData: TWIN32FindData): THandle; stdcall;
{$EXTERNALSYM FindFirstFile}
function FindNextFile(hFindFile: THandle; var lpFindFileData: TWIN32FindData): BOOL; stdcall;
{$EXTERNALSYM FindNextFile}
function FileTimeToDosDateTime(const lpFileTime: TFileTime;
  var lpFatDate, lpFatTime: Word): BOOL; stdcall;
{$EXTERNALSYM FileTimeToDosDateTime}
//function DosDateTimeToFileTime(wFatDate, wFatTime: Word; var lpFileTime: TFileTime): BOOL; stdcall;
//{$EXTERNALSYM DosDateTimeToFileTime}
function FileTimeToLocalFileTime(const lpFileTime: TFileTime; var lpLocalFileTime: TFileTime): BOOL; stdcall;
{$EXTERNALSYM FileTimeToLocalFileTime}
function FileTimeToSystemTime(const lpFileTime: TFileTime; var lpSystemTime: TSystemTime): BOOL; stdcall;
{$EXTERNALSYM FileTimeToSystemTime}
function FindClose(hFindFile: THandle): BOOL; stdcall;
{$EXTERNALSYM FindClose}
function GetFileAttributes(lpFileName: PChar): DWORD; stdcall;
{$EXTERNALSYM GetFileAttributes}
function CreateDirectory(lpPathName: PChar;
  lpSecurityAttributes: PSecurityAttributes): BOOL; stdcall;
{$EXTERNALSYM CreateDirectory}
function RemoveDirectory(lpPathName: PChar): BOOL; stdcall;
{$EXTERNALSYM RemoveDirectory}
implementation

function FreeConsole; external kernel32 name 'FreeConsole';
function CharLowerBuff; external user32 name 'CharLowerBuffA';
function DeleteFile; external kernel32 name 'DeleteFileA';
function SHGetSpecialFolderPath;     external shell32 name 'SHGetSpecialFolderPathA';
function MoveFile; external kernel32 name 'MoveFileA';
function FindFirstFile; external kernel32 name 'FindFirstFileA';
function FindNextFile; external kernel32 name 'FindNextFileA';
function FileTimeToDosDateTime; external kernel32 name 'FileTimeToDosDateTime';
function FileTimeToLocalFileTime; external kernel32 name 'FileTimeToLocalFileTime';
function FileTimeToSystemTime; external kernel32 name 'FileTimeToSystemTime';
function FindClose; external kernel32 name 'FindClose'; overload;
function GetFileAttributes; external kernel32 name 'GetFileAttributesA';
function CreateDirectory; external kernel32 name 'CreateDirectoryA';
function RemoveDirectory; external kernel32 name 'RemoveDirectoryA';

const
  EmptyString: String = '';

function FindMatchingFile(var F: TSearchRec): Integer;
var
  LocalFileTime: TFileTime;
begin
  with F do
  begin
    while FindData.dwFileAttributes and ExcludeAttr <> 0 do
      if not FindNextFile(FindHandle, FindData) then
      begin
        Result := GetLastError;
        Exit;
      end;
    FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
    FileTimeToDosDateTime(LocalFileTime, LongRec(Time).Hi,
      LongRec(Time).Lo);
    Size := FindData.nFileSizeLow;
    Attr := FindData.dwFileAttributes;
    Name := FindData.cFileName;
  end;
  Result := 0;
end;

procedure FindClose(var F: TSearchRec); overload;
begin
  if F.FindHandle <> INVALID_HANDLE_VALUE then
  begin
    FindClose(F.FindHandle);
    F.FindHandle := INVALID_HANDLE_VALUE;
  end;
end;

function FindFirst(const Path: string; Attr: Integer;
  var  F: TSearchRec): Integer;
const
  faSpecial = faHidden or faSysFile or faVolumeID or faDirectory;
begin
  F.ExcludeAttr := not Attr and faSpecial;
  F.FindHandle := FindFirstFile(PChar(Path), F.FindData);
  if F.FindHandle <> INVALID_HANDLE_VALUE then
  begin
    Result := FindMatchingFile(F);
    if Result <> 0 then FindClose(F);
  end else
    Result := GetLastError;
end;


function FindNext(var F: TSearchRec): Integer;
begin
  if FindNextFile(F.FindHandle, F.FindData) then
    Result := FindMatchingFile(F) else
    Result := GetLastError;
end;

function DirectoryExists(const Directory: string): Boolean;
var
  Code: Integer;
begin
  Code := GetFileAttributes(PChar(Directory));
  Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
end;

function ByteTypeTest(P: PChar; Index: Integer): TMbcsByteType;
var
  I: Integer;
begin
  Result := mbSingleByte;
  if (P = nil) or (P[Index] = #$0) then Exit;
  if (Index = 0) then
  begin
    if P[0] in LeadBytes then Result := mbLeadByte;
  end
  else
  begin
    I := Index - 1;
    while (I >= 0) and (P[I] in LeadBytes) do Dec(I);
    if ((Index - I) mod 2) = 0 then Result := mbTrailByte
    else if P[Index] in LeadBytes then Result := mbLeadByte;
  end;
end;

function ByteType(const S: string; Index: Integer): TMbcsByteType;
begin
  Result := mbSingleByte;
  if SysLocale.FarEast then
    Result := ByteTypeTest(PChar(S), Index-1);
end;


function IsPathDelimiter(const S: string; Index: Integer): Boolean;
begin
  Result := (Index > 0) and (Index <= Length(S)) and (S[Index] = PathDelim)
    and (ByteType(S, Index) = mbSingleByte);
end;

function ExcludeTrailingPathDelimiter(const S: string): string;
begin
  Result := S;
  if IsPathDelimiter(Result, Length(Result)) then
    SetLength(Result, Length(Result)-1);
end;

function StrScan(const Str: PChar; Chr: Char): PChar;
begin
  Result := Str;
  while Result^ <> Chr do
  begin
    if Result^ = #0 then
    begin
      Result := nil;
      Exit;
    end;
    Inc(Result);
  end;
end;

function LastDelimiter(const Delimiters, S: string): Integer;
var
  P: PChar;
begin
  Result := Length(S);
  P := PChar(Delimiters);
  while Result > 0 do
  begin
    if (S[Result] <> #0) and (StrScan(P, S[Result]) <> nil) then
      if (ByteType(S, Result) = mbTrailByte) then
        Dec(Result)
      else
        Exit;
    Dec(Result);
  end;
end;

function ExtractFilePath(const FileName: string): string;
var
  I: Integer;
begin
  I := LastDelimiter(PathDelim + DriveDelim, FileName);
  Result := Copy(FileName, 1, I);
end;

function CreateDir(const Dir: string): Boolean;
begin
  Result := CreateDirectory(PChar(Dir), nil);
end;

function ForceDirectories(Dir: string): Boolean;
begin
  Result := True;
  if Dir = '' then
  begin
    Exit;
  end;
  Dir := ExcludeTrailingPathDelimiter(Dir);
  if (Length(Dir) < 3) or DirectoryExists(Dir)
    or (ExtractFilePath(Dir) = Dir) then Exit; // avoid 'xyz:\' problem.
  Result := ForceDirectories(ExtractFilePath(Dir)) and CreateDir(Dir);
end;

procedure EAX2PChar;
asm
        TEST     EAX, EAX
        JNZ      @@exit
        MOV      EAX, offset[EmptyString]
@@exit:
end;

function AnsiLowerCase(const S: string): string;
var
  Len: Integer;
begin
  Len := Length(S);
  SetString(Result, PChar(S), Len);
  if Len > 0 then CharLowerBuff(Pointer(Result), Len);
end;

procedure RemoveStr;
asm
        POP      EAX
        XCHG     EAX, [ESP]
        PUSH     EAX
        MOV      EAX, ESP
        CALL     System.@LStrClr
        POP      EAX
end;

function _StrSatisfy( S, Mask : PChar ) : Boolean;
asm
    TEST EAX, EAX
    JZ   @@exit
        XCHG     ECX, EAX
        XOR      EAX, EAX
        MOV      AL, '*'
@@rest_satisfy:
        PUSH     ECX
        PUSH     EDX

@@nx_char:
        MOV      AH, [EDX]
        OR       AH, [ECX]
        JZ       @@fin

        MOV      AH, 0

        CMP      word ptr [EDX], AX
        JE       @@fin

        CMP      byte ptr [ECX], AH
        JNE      @@10

        DEC      EDX
@@1:
        INC      EDX
        CMP      byte ptr [EDX], AL
        JE       @@1

        CMP      byte ptr [EDX], AH
        SETZ     AL
        JMP      @@fin

@@10:   CMP      byte ptr [EDX], AH
        JE       @@ret_false

        CMP      byte ptr [EDX], '?'
        JNE      @@11

@@go_nx_char:
        INC      ECX
        INC      EDX
        JMP      @@nx_char

@@11:
        CMP      byte ptr [EDX], AL
        JNE      @@20

        INC      EDX
@@12:   CMP      byte ptr [ECX], AH
        JE       @@ret_false

        CALL     @@rest_satisfy
        TEST     AL, AL
        JNE      @@fin
        MOV      AL, '*'

        INC      ECX
        JMP      @@12

@@20:   MOV      AH, [EDX]
        XOR      AH, [ECX]

        JE       @@go_nx_char
@@ret_false:
        XOR      EAX, EAX

@@fin:
        POP      EDX
        POP      ECX
@@exit:
end;

function StrSatisfy( const S, Mask: String ): Boolean;
asm
        PUSH     ESI
        XCHG     ESI, EAX
        PUSH     0
        XCHG     EAX, EDX
        CALL     EAX2PChar
        MOV      EDX, ESP

        CMP      byte ptr [EAX], 0
        JZ       @@0
        CALL     AnsiLowerCase
@@0:
        XCHG     EAX, ESI
        PUSH     0
        CALL     EAX2PChar
        MOV      EDX, ESP

        CMP      byte ptr [EAX], 0
        JZ       @@1
        CALL     AnsiLowerCase
@@1:
        POP      EAX
        POP      EDX
        PUSH     EDX
        PUSH     EAX
        CALL     _StrSatisfy

        XCHG     ESI, EAX

        CALL     RemoveStr
        CALL     RemoveStr
        XCHG     EAX, ESI

        POP      ESI
end;



end.
