| Index | Manual | Download | Source | Macros | Embedded word Case ins.
Sitemap | Links | Forum | Tests | Projects

# dirswinc.htm
Data
Data
Procedures
AddUnderscores
Arg2Gui
ByDate
ByName
ByVolume
DirVolume
EraseOlderFile
Error
ExcludeDir
LoadAndCompareScans
LoadNewer
LoadOlder
MainCon
OneLine2Dir
ParseToBuffer
ReadFile_ini
Scan
SetMaxDaysAgo
SetToday
StoreFileName
StoreIndex
StoreWideChar

This is a CON module of EuroTool program EuroDirs for Windows.

             EUROASM CPU=x64, SIMD=yes, Unicode=no
    dirswinc PROGRAM Format=COFF,Width=64
              %DROPMACRO *
              INCLUDEHEAD1 argument.htm
              INCLUDE1 winabi.htm, cpuext64.htm, wins.htm, winscon.htm, winf64.htm, winsfile.htm
              INCLUDE1 memory64.htm, sort64.htm, string64.htm, status32.htm, time.htm,

↑ Data

    SCAN_INDEX  STRUC     ; A structure which describes one scaned directory.
    .Ptr         D DWORD  ; Relative pointer to the directory name in UTF-8. Related to [NamesInMemory].
    .Size        D DWORD  ; Size of the directory name in bytes.
    .Volume      D QWORD  ; Total amount of bytes in all files in this directory.
                ENDSTRUC SCAN_INDEX
    CP_UTF8     EQU 65001
    [.rodata]   ; Text constants.
    Ext_names   DU ".names",0              ; The extension of temporary names file.
    Ext_index   DU ".index",0              ; The extension of temporary index file.
    Ext_scan    DU ".scan",0               ; The extension of scan file.
    DateMaskW   DU "????.??.??_??.??",0    ; File date mask in unichars.
    Root        DU "C:\",0                 ; Initialization of Dir.
    EuroDirs::  DB "EuroDirs",0
    Version::   DB " version %^Date",0
    Help::      DB 0xEF,0xBB,0xBF       ; BOM UTF-8.
                DB "; EuroDirs default configuration:",13,10
                DB "/ScanNow=false     ; Do not scan if /SN=false, compare scans instead.",13,10
                DB "/ScanDir="".""     ; Directory for scan files.",13,10
                DB "/TimeZone=0        ; Time zone of scan files in hours. /TZ=0 for UTC.",13,10
                DB "/LeaveTemporary=no ; Do not erase *.name and *.index files (for debugging).",13,10
                DB "/MaxDays=35        ; Older scan files will be deleted when /SN=true.",13,10
                DB "/InputFile=        ; Old scan file to compare. Default is the oldest in ScanDir.",13,10
                DB "/OutputFile=       ; New scan file to compare. Default is the newest in ScanDir.",13,10
                DB "/ExcludeDirs=      ; Do not scan dirs beginning with it. Default is empty.",13,10
                DB "/IncludeDirs=      ; Scan those dirs only. Default is scan all.",13,10
                DB "/ViewDirs=Inc      ; Ins|Inc|Unch|Dec|Del - select one of 5 views.",13,10
                DB 0
    Comparing   DB 13,10,"Comparing directories from two scan files:",13,10,0
    Report1::   DB " new created directories,",0
    Report2::   DB " increased directories,",0
    Report3::   DB " directories which did not change,",0
    Report4::   DB " decreased directories,",0
    Report5::   DB " deleted directories.",0
    [.data]     ; Text variables.
    TodayW      DU "yyyy.mm.dd_hh.mm",0    ; Today's date during the scan in unichars.
    MaxDaysAgoW DU "yyyy.mm.dd_hh.mm",0    ; Today - ArgMaxDays in unichars.
    TodayA      DB "yyyy.mm.dd_hh.mm",0    ; Today's date during the scan in bytes.
    MaxDaysAgoA DB "yyyy.mm.dd_hh.mm",0    ; Today - ArgMaxDays in bytes.
    Disk        DB "B"                     ; Disk letters when ArgIncludeDirs is not specified.
    [.bss]      ; Working memory variables.
    SystemTime            DS SYSTEMTIME  ; A time structure.
    FileTime              DS FILETIME    ; A time structure.
    ScanIndex             DS SCAN_INDEX  ; A directory index.
    File_ini              DS FILE64 ; Configuration FILE64.
    File_names            DS FILE64 ; Temporary FILE64 for directory names (UTF-8).
    File_index            DS FILE64 ; Temporary FILE64 for directory indexes.
    File_scan             DS FILE64 ; Scan FILE64 (UTF-8), alphabetically sorted.
    File_scanOlder::      DS FILE64 ; Older scan FILE64 to compare.
    File_scanNewer::      DS FILE64 ; Newer scan FILE64 to compare.
    Dir::                 D 1K * UNICHAR ; The working directory which is scanned. UTF-16.
    DirUTF8::             D 4K * BYTE    ; The working directory translated to UTF-8.
    InputFileNameUTF16::  D 264 * UNICHAR ; Input arguments translated to UTF-16.
    OutputFileNameUTF16:: D 264 * UNICHAR
    ScanDirUTF16::        D 264 * UNICHAR
    IncludeDirsUTF16::    D  2K * UNICHAR
    ExcludeDirsUTF16::    D  2K * UNICHAR
    Total                 D QWORD   ; Total size of all files and subdirectories.
    NamesInMemory         D QWORD   ; Pointer to the contents of File_names mapped in memory.
    NamesSize             D QWORD   ; Size of the contents of File_names mapped in memory.
    IndexInMemory         D QWORD   ; Pointer to the contents of File_index mapped in memory.
    IndexEndInMemory      D QWORD   ; Size of the contents of File_index mapped in memory.
    IndexNumber           D 24*B    ; Number of subdirectories.
    IncludedDirsBuffer    D QWORD   ; BUFFER for the parsed directories of ArgIncludedDirs.
    IncludedDirsPtr       D QWORD   ; Pointer to IncludedDirsBuffer contents.
    IncludedDirsEnd       D QWORD   ; Pointer to the end of Includ edDirsBuffer contents.
    ExcludedDirsBuffer    D QWORD   ; BUFFER for the parsed directories of ArgExcludedDirs.
    ExcludedDirsPtr       D QWORD   ; Pointer to ExcludedDirsBuffer contents.
    ExcludedDirsEnd       D QWORD   ; Pointer to the end of ExcludedDirsBuffer contents.
    ScanDirPtr            D QWORD   ; Pointer to ScanDirBuffer contents.
    ScanDirEnd            D QWORD   ; Pointer to the end of ScanDirBuffer contents.
    ScanDirNr             D QWORD   ; Number of scans in ScanDir.
    OlderPtr              D QWORD   ; Pointer to the old scan mapped in memory, variable.
    OlderInMemory         D QWORD   ; Pointer to the beginning of old scan mapped in memory.
    OlderEndInMemory      D QWORD   ; Pointer to the end of old scan mapped in memory.
    NewerPtr              D QWORD   ; Pointer to the new scan mapped in memory, variable.
    NewerInMemory         D QWORD   ; Pointer to the beginning of new scan mapped in memory.
    NewerEndInMemory      D QWORD   ; Pointer to the end of new scan mapped in memory.
    ScanDirBuffer         D QWORD   ; BUFFER for the dates of old scans
    Buffers::             ; Array of buffers with SCAN_INDEXes.
    Buffer1               D QWORD   ; Pointer to BUFFER with SCAN_INDEXes of inserted dirs.
    Buffer2               D QWORD   ; Pointer to BUFFER with SCAN_INDEXes of increased dirs.
    Buffer3               D QWORD   ; Pointer to BUFFER with SCAN_INDEXes of unchanged dirs.
    Buffer4               D QWORD   ; Pointer to BUFFER with SCAN_INDEXes of decreased dirs.
    Buffer5               D QWORD   ; Pointer to BUFFER with SCAN_INDEXes of deleted dirs.
    BufferSelected::      D QWORD   ; One of Buffer1..Buffer5 selected by a button or /ViewDirs.
    MaxLineNr::           D DWORD   ; Number of rows in the selected buffer.
    ArgNr                 D DWORD   ; Binary number of the current cmd-line argument.
    IndexPtr              D DWORD   ; On the fly pointer in File_index.
    FatDate               D WORD    ; Used in SetMaxDaysAgo.
    FatTime               D WORD    ; Used in SetMaxDaysAgo.
    Number1W::            D 40*BYTE ; Decimal number (UTF-16) of inserted dirs in Unichars.
    Number2W::            D 40*BYTE ; Decimal number (UTF-16) of increased dirs in Unichars.
    Number3W::            D 40*BYTE ; Decimal number (UTF-16) of unchanged dirs in Unichars.
    Number4W::            D 40*BYTE ; Decimal number (UTF-16) of decreased dirs in Unichars.
    Number5W::            D 40*BYTE ; Decimal number (UTF-16) of deleted dirs in Unichars.
    Number1A:             D 20*BYTE ; Decimal number (ANSI) of inserted dirs in bytes.
    Number2A:             D 20*BYTE ; Decimal number (ANSI) of increased dirs in bytes.
    Number3A:             D 20*BYTE ; Decimal number (ANSI) of unchanged dirs in bytes.
    Number4A:             D 20*BYTE ; Decimal number (ANSI) of decreased dirs in bytes.
    Number5A:             D 20*BYTE ; Decimal number (ANSI) of deleted dirs in bytes.
    NumberA               D 20*BYTE ; A working space for numbers. Zero terminated, e. g. "1234567",0.
    Number_A              D 26*BYTE ; Number same as NumberA but with separators '_', e. g. "1_234_567",0.
    [.text]

MainCon
  The main procedure executed both in console and in graphic mode.
  It reads arguments and then it decides by /ScanNow=. If true, it scans all directories, writes a scan file to /ScanDir and terminates.
  When /ScanNow=false (default), EuroDirs opens a graphic window and the user can browse directories, change the /ViewDirs mode or change older and newer scan files.
Calls
  ReadFile_ini, ArgParse, Scan, LoadAndCompareScans.

    MainCon:: PROC
        StdOutput EuroDirs, Version, Eol=yes, Unicode=off
        ; Try to load arguments from the configuration file "%AppData%\eurotool\eurodirs.ini".
        WinABI GetEnvironmentVariableW,=U'AppData',Dir,SIZE# Dir,Unicode=yes
        TEST RAX
        JZ .10:
        FileAssign File_ini,Dir,=U'\eurotool\eurodirs.ini',Unicode=yes
        FileExists? File_ini
        JNC .13:
        FileMkDir File_ini
        JC .10:
        LEA RDI,[Help]                     ; Online help and default configuration.
        GetLength$ RDI, Unicode=no
        FileStore File_ini, RDI,RCX
        JNC .13:
    .10:StdOutput =U'Configuration "',File_ini.Name,=U'" could not be saved.',Eol=yes, Console=yes, Unicode=yes
        JMP .20:
    .13:CALL ReadFile_ini
    .20:; And finally read arguments from the command-line.
        INC [ArgNr]                        ; The next argument.
        GetArg [ArgNr],Unicode=yes ; RSI,RCX is the line with one argument in UTF-16, e. g. /ScanDir="C:\Eurodirs"
        JC .30:                            ; When they are no more arguments.
        SHR RCX,1
        LEA RDI,[DirUTF8]
        WinABI WideCharToMultiByte,CP_UTF8,0,RSI,RCX,RDI,SIZE#DirUTF8/2,0,0
        MOV RSI,RDI
        GetLengthUTF8 RDI
        CALL ArgParse::  ; Use ArgParse to translate UTF-8 string RSI,RCX to a public symbol Arg***.
        JNC .20:
        LEA RSI,[Help+3]
        StdOutput ErrorMessage::,RSI,Unicode=no  ; On error write help and exit.
        TerminateProgram 8
    .30:; Create buffers for SCAN_INDEX objects.
        BufferCreate Size=16K
        JC ErrorBuffer
        MOV [ScanDirBuffer],RAX
        BufferCreate Size=4M
        JC ErrorBuffer
        MOV [Buffer1],RAX
        BufferCreate Size=4M
        JC ErrorBuffer
        MOV [Buffer2],RAX
        BufferCreate Size=8M
        JC ErrorBuffer
        MOV [Buffer3],RAX
        BufferCreate Size=4M
        JC ErrorBuffer
        MOV [Buffer4],RAX
        BufferCreate Size=4M
        JC ErrorBuffer
        MOV [Buffer5],RAX
        CALL Arg2Gui
        JSt [Status::],ArgScanNow,.80:     ; Should the computer be scanned now?
        CALL LoadAndCompareScans:          ; Compare older and newer scans, fill the five buffers.
        JMP MainGui::
    .80:CALL Scan:                         ; Compute and save the Scan and then terminate.
    .Terminate::
        FileClose File_scanOlder, File_scanNewer
        BufferDestroy [ScanDirBuffer],[Buffer1],[Buffer2],[Buffer3],[Buffer4],[Buffer5]
        TerminateProgram 0
      ENDP MainCon::

Scan

  Procedure Scan is called when an argument /ScanNow=yes was detected. EuroDirs is usually called with this argument from TaskScheduler daily or weekly and it creates a scan file "eurodirs_YYYY.MM.DD_HH.MM.scan" in the directory specified by /ScanDir=

Called by
  MainCon.

    Scan: PROC                            ; Scan now.
        CALL SetToday
        CALL SetMaxDaysAgo
        FileAssign File_scan,ScanDirUTF16::,DateMaskW,Ext_scan,Unicode=yes
        FileEach File_scan,EraseOlderFile ; Erase files older than ArgMaxDays.
        ; Open temporary files with extensions .names, .index, .scan.
        LEA RBX,[File_names]
        FileAssign RBX,ScanDirUTF16,TodayW,Ext_names,Unicode=yes
        FileCreate RBX
        JC ErrorFile:
        LEA RBX,[File_index]
        FileAssign RBX,ScanDirUTF16,TodayW,Ext_index,Unicode=yes
        FileCreate RBX
        JC ErrorFile:
        LEA RBX,[File_scan]
        FileAssign RBX,ScanDirUTF16,TodayW,Ext_scan,Unicode=yes
        FileCreate RBX
        JC ErrorFile:
        ; Prepare excluded directories.
        BufferCreate Size=4K
        MOV [ExcludedDirsBuffer],RAX
        LEA RSI,[ExcludeDirsUTF16]
        CALL ParseToBuffer
        BufferRetrieve [ExcludedDirsBuffer]
        LEA RDX,[RSI+RCX]
        MOV [ExcludedDirsPtr],RSI
        MOV [ExcludedDirsEnd],RDX
        ; Scan included dirs (if not empty) or all directories otherwise. Omit excluded dirs.
        LEA RSI,[IncludeDirsUTF16]
        CMPW [RSI],0
        JZ .20:                  ; Included directories are not specified; jump to scan all.
        BufferCreate Size=4K
        MOV [IncludedDirsBuffer],RAX
        CALL ParseToBuffer
        BufferRetrieve [IncludedDirsBuffer]
        LEA RDX,[RSI+RCX]
        MOV [IncludedDirsPtr],RSI
        MOV [IncludedDirsEnd],RDX
    .10:MOV RSI,[IncludedDirsPtr]; Scan the next directory from IncludedDirs.
        CMP RSI,[IncludedDirsEnd]
        JNB .40:
        LODSQ                    ; 1st QWORD is a size, 2nd QWORD is a pointer to a directory name.
        MOV ECX,EAX
        LODSQ
        MOV [IncludedDirsPtr],RSI
        MOV RSI,RAX
        LEA RDI,[Dir]
        JRCXZ .10:
        REP MOVSW                ; Included directory will be put to Dir.
        XOR EAX,EAX
        STOSD
        CALL ExcludeDir          ; Skip if Dir is one of the excluded directories.
        JZ .10:
        LEA RDI,[Dir]
        StdOutput =U"Scanning directory """,RDI,=U""", please wait...",Eol=yes,Console=yes,Unicode=yes
        CALL DirVolume           ; The main recursive procedure will scan Dir and all its subdirectories.
        JZ .10:                  ; ZF=1 when the directory is excluded.
        ADD [Total],RAX          ; Otherwise RAX is the total volume of Dir.
        CALL StoreIndex          ; Write Dir to File_names and File_index.
        JMP .10:
    .20:; IncludedDirs are empty, scan directories from all disks.
        MOV RAX,[Root]           ; DU "C:\",0
        LEA RDI,[Dir]
        MOV [RDI],RAX
    .30:MOV AL,[Disk]            ; DB "B"
        INC AL
        CMP AL,'Z'
        JA .40:
        LEA RDI,[Dir]
        MOV [Disk],AL             ; Rewrite "C:\" to "D:\" etc.
        MOV [RDI],AL
        WinABI GetDriveTypeW,RDI
        CMP AL,DRIVE_FIXED
        JNE .30:
        CALL ExcludeDir
        JZ .30:
        LEA RDI,[Dir]
        StdOutput =U"Scanning directory """,RDI,=U"""",Eol=yes,Console=yes,Unicode=yes
        CALL DirVolume           ; The main recursive procedure will scan Dir and all its subdirectories.
        JZ .30:                  ; ZF if Dir does not exist or is excluded.
        ADD [Total],RAX          ; Volume of the Dir.
        CALL StoreIndex
        JMP .30:                 ; Go to the next disk.
    .40:MOV RBX,[IncludedDirsBuffer]
        BufferDestroy RBX
        MOV RBX,[ExcludedDirsBuffer]
        BufferDestroy RBX
        FileClose File_index,File_names  ; DirVolume was now scanned and the result put into File_names and File_index.
        LEA RBX,[File_names]
        FileMapOpen RBX
        JC ErrorMap:
        MOV [NamesInMemory],RSI
        MOV [NamesSize],RAX
        LEA RBX,[File_index]
        FileMapCreate RBX
        JC ErrorMap:
        MOV [IndexInMemory],RDI
        ADD RDI,RAX
        MOV [IndexEndInMemory],RDI
        SHR RAX,4                ; SIZE# SCAN_INDEX = 16.
        MOV [IndexNumber],RAX    ; Number of scanned directories.
        LEA RDI,[NumberA]
        MOV RBX,RDI
        StoD RDI
        MOVB [RDI],0
        StdOutput =B'Sorting ',RBX,=B' directories...',Unicode=off,Eol=yes
        LEA RDX,[ByName:]
        MOV RSI,[IndexInMemory]
        MOV RAX,[IndexNumber]
        ShellSort RSI,RAX,SIZE#SCAN_INDEX,RDX
        StdOutput =B'Writing scan "', Unicode=no
        StdOutput File_scan.Name, =U'"', Unicode=yes, Console=yes, Eol=yes
    .60:CMP RSI,[IndexEndInMemory]
        JAE .80:
        MOV RAX,[RSI+SCAN_INDEX.Volume]
        LEA RDI,[NumberA]
        MOV RBX,RDI
        StoD RDI
        SUB RDI,RBX
        MOV EDX,[RSI+SCAN_INDEX.Ptr]
        MOV ECX,[RSI+SCAN_INDEX.Size]
        ADD RDX,[NamesInMemory]
        FileWrite File_scan,RDX,RCX,=B(9),1,RBX,RDI,=B(10),1 ; DirNameInUTF8, HT, DirVolume, LF.
        LEA RBX,[File_scan]
        JC ErrorWrite
        ADD RSI,SIZE# SCAN_INDEX
        JMP .60:
    .80:FileClose File_names, File_index, File_scan
        JSt [Status::],ArgLeaveTemporary,.90:
        FileDelete File_names, File_index
    .90:StdOutput =B"Scan terminated.", Unicode=off, Eol=yes
        RET
      ENDP Scan:

DirVolume
  This is a recursive procedure which will scan a single directory.
  The directory name in Dir may or may be not terminated with backslash. It is zero terminated. The procedure will reuse Dir and temporarilly append file names to it to find the file size.
Input
  Dir contains name of the examined folder, encoded in unichars.
Output
  ZF=0, RAX=total data volume of Dir's files and subdirectories.
Error
  ZF=1, RAX=0 if the directory does not exist or if it is excluded.
Called by
  Scan.
Calls
  StoreIndex, ExcludeDir.
Clobbers
  RCX,RDX,RSI,RDI,R8,R11

    DirVolume: PROC                                 ; Recursively called procedure which scans one directory.
        XOR EAX,EAX
        PUSH RBX,R12,RBP,RAX
         SUB RSP,SIZE# WIN32_FIND_DATAW
         MOV RBP,RSP                             ; RBP: ^WIN32_FIND_DATAW.
         LEA RDI,[Dir]
         MOV ECX,SIZE# Dir
         XOR EAX,EAX
         REPNE SCASW                             ; Search for the terminating zero.
         SUB RDI,2
         MOV AX,'\'
         CMP [RDI-2],AX
         JE .10:
         STOSW                                   ; Make the folder name always backslash terminated.
    .10: MOV R12,RDI                             ; R12: behind the last \ in Dir.
         MOV EAX,'*'
         STOSD
         LEA RCX,[Dir]
         WinABI FindFirstFileW,RCX,RBP
         MOV RBX,RAX                             ; RBX=FindHandle.
         CMP RAX,INVALID_HANDLE_VALUE
         JE .90:
         MOVW [R12],0                            ; Erase the asterix.
    .20: JSt [RBP+WIN32_FIND_DATAW.FileAttributes],FILE_ATTRIBUTE_DIRECTORY,.40:
         ;  The name is a file.
         MOV EAX,[RBP+WIN32_FIND_DATAW.FileSizeLow]
         MOV EDX,[RBP+WIN32_FIND_DATAW.FileSizeHigh]
         SAL RDX,32
         ADD RAX,RDX
         ADD [RBP+SIZE# WIN32_FIND_DATAW],RAX
    .30: WinABI FindNextFileW,RBX,RBP
         TEST RAX,RAX
         JNZ .20:
         JMP .90:
    .40: ;  The name is a directory.
         LEA RSI,[RBP+WIN32_FIND_DATAW.FileName] ; ReturnRAX (Total) on machine stack.
         MOV EAX,'.'
         CMP [RSI],EAX
         JE .30:                                 ; Ignore the directory "."
         SHL EAX,16
         MOV AL,'.'
         CMP [RSI],EAX
         JNE .50:
         CMPW [RSI+4],0
         JE .30:                                 ; Ignore the directory ".."
    .50: GetLength$ RSI, Unicode=yes
         MOV RDI,R12
         REP MOVSB
         SUB RAX,RAX
         STOSW                                   ; Let Dir be the new directory.
         CALL ExcludeDir:
         JNZ .60:
         MOVW [R12],0
         JMP .30:
    .60: CALL DirVolume                          ; Recursive call itself.
         ADD [RBP+SIZE# WIN32_FIND_DATAW],RAX    ; Returned RAX (Total) on machine stack.
         CALL StoreIndex:
         JMP .30:
    .90: MOVW [R12],0
         WinABI FindClose,RBX,Fastmode=enabled
         ADD RSP,SIZE# WIN32_FIND_DATAW
         CMP RBX,INVALID_HANDLE_VALUE
        POP RAX,RBP,R12,RBX
        RET
      ENDP DirVolume:

StoreIndex
  StoreIndex writes the directory name from Dir to the File_names and its index to File_index.
  The name in Dir is encoded in UTF-16.
  The name written to File_names will be encoded in UTF-8. It is not zero-terminated.
Input
  RAX=the volume of files and subdirectories in bytes.
Output
  File_names and File_index are incremented.
Clobbers
  RAX,RDX,RSI,RDI,R8,R11
Called by
  Scan and DirVolume.

    StoreIndex: PROC
        MOV [ScanIndex.Volume],RAX
        LEA RSI,[Dir]
        LEA RDI,[DirUTF8]
        XOR EAX,EAX
    .20:LODSW
        CMP AX,0
        JE .80:
        CMP AX,0xD800
        JB .50:
        CMP AX,0xE000
        JAE .50:
        SUB AX,0xD800                      ; High surrogate character.
        SHL EAX,10
        MOV EDX,EAX
        LODSW
        CMP AX,0
        JE .80:
        SUB AX,0xDC00
        JNA .40:
        CMP AX,0x03FF
        JA .40:
        ADD EAX,EDX
        EncodeUTF8                         ; Use macro EncodeUTF8.
        XOR EAX,EAX
        JMP .20:
    .40:MOV EAX,'?'
    .50:EncodeUTF8
        JMP .20:
    .80:LEA RDX,[DirUTF8]
        MOV R8,RDI
        SUB R8,RDX
        MOV [ScanIndex.Size],R8D
        MOV EAX,[IndexPtr]
        MOV [ScanIndex.Ptr],EAX
        ADD [IndexPtr],R8D
        MOV R11,RBX
        LEA RBX,[File_names]
        FileWrite RBX,RDX,R8               ; Write the directory name (UTF8) to the File_names.
        JC ErrorWrite:
        LEA RBX,[File_index]
        FileWrite RBX,ScanIndex,SIZE#SCAN_INDEX ; Write Index to the nxdFile.
        JC ErrorWrite:
        MOV RBX,R11
        RET
      ENDP StoreIndex:

LoadAndCompareScans

  Global procedure LoadAndCompareScans checks arguments.
  If /ScanDir is empty it fills it with ..
  If /InputFile is empty (default), it assigns it with the oldest scan from /ScanDir.
  If /OutputFile is empty (default), it assigns it with the newest scan from /ScanDir.
  If the name of oldest scan is greater then the name of newest scan, it will swap them.
  If less than two scans are in /ScanDir, LoadAndCompareScans ends with CF=1.

  By default it selects the oldest and the newest scan in ScanDir.

  The procedure fills five buffers of ScanFiles indexes with differences between those two scans.

Input
  -
Output
  CF=0, Buffer1,Buffer2,Buffer3,Buffer4,Buffer5 are loaded with the differences and sorted.
Error
  CF=1 less than two scans were found.
Called by
  DirVolume.

    LoadAndCompareScans:: PROC
        CALL Arg2Gui
        ; Clear buffers from previous run of LoadAndCompareScans procedure.
        BufferClear [ScanDirBuffer],[Buffer1],[Buffer2],[Buffer3],[Buffer4],[Buffer5]
        ; Load dates of all scans which are in ScanDirUTF16 into a ScanDirBuffer.
        FileAssign File_scan,ScanDirUTF16,DateMaskW,Ext_scan,Unicode=yes
        FileEach File_scan,StoreFileName ; Store the date from file names to ScanDirBuffer.
        FileClose File_scan
        BufferRetrieve [ScanDirBuffer]
        CMP ECX,2*16*2                   ; At least two scans are required.
        JAE .20:
        StdOutput =B"At least two scan files are required in /ScanDir=""",Unicode=no
        StdOutput ScanDirUTF16,=U"""",Unicode=yes,Console=yes,Eol=yes
        STC
        JMP .90:
    .20:MOV RAX,RCX
        ADD RCX,RSI
        SHR RAX,4+1                      ; Each item in ScanDirBuffer has 16*2 bytes.
        MOV [ScanDirPtr],RSI
        MOV [ScanDirEnd],RCX
        MOV [ScanDirNr],RAX
        ShellSort RSI, RAX, 2*16, ByDate
        FileClose File_scanOlder,File_scanNewer ; Close files, if opened by previous comparison.
        ; If InputFileNameUTF16 is specified, it will be used as File_scanOlder.
        LEA RBX,[File_scanOlder]
        LEA RDI,[InputFileNameUTF16]
        MOV RDX,RDI
        CMPW [RDI],0
        JNE .25:
        MOV RSI,[ScanDirPtr]
        MOV ECX,16*2
        REP MOVSB
        SUB EAX,EAX
        STOSW
        FileAssign RBX,ScanDirUTF16::,RDX,Ext_scan,Unicode=yes ; The first (oldest) scan.
        JMP .30:
    .25:FileAssign RBX,RDX,Unicode=yes
    .30:FileMapOpen RBX
        JC ErrorMap:
        TEST RAX
        JZ ErrorZero:
        MOV [OlderPtr],RSI
        MOV [OlderInMemory],RSI
        ADD RAX,RSI
        MOV [OlderEndInMemory],RAX
        ; If OutputFileNameUTF16 is specified, it will be used as File_scanNewer.
        LEA RBX,[File_scanNewer]
        LEA RDI,[OutputFileNameUTF16]
        MOV RDX,RDI
        CMPW [RDI],0
        JNE .35:
        MOV RSI,[ScanDirEnd]
        SUB RSI,16*2
        MOV ECX,16*2
        REP MOVSB
        SUB EAX,EAX
        STOSW
        FileAssign RBX,ScanDirUTF16::,RDX,Ext_scan,Unicode=yes ; The last (newest) scan.
        JMP .40:
    .35:FileAssign RBX,RDX,Unicode=yes
    .40:FileMapOpen RBX
        JC ErrorMap:
        TEST RAX
        JZ ErrorZero:
        MOV [NewerPtr],RSI
        MOV [NewerInMemory],RSI
        ADD RAX,RSI
        MOV [NewerEndInMemory],RAX
        ; Check if newer > older.
        RstSt [Status::],ArgSwappedDates
        LEA RSI,[File_scanNewer.Name]
        LEA RDI,[File_scanOlder.Name]
        GetLength$ RDI,Unicode=yes
        MOV EDX,ECX
        GetLength$ RSI,Unicode=yes
        CMP ECX,EDX
        JNE .45:
        SHR ECX,1
        REPE CMPSW
        JGE .45:
        SetSt [Status::],ArgSwappedDates
    .45:; Comparing two scan files starts.
        StdOutput Comparing,Unicode=no
        StdOutput =U"Old file: ",File_scanOlder.Name,Eol=yes,Console=yes, Unicode=yes
        StdOutput =U"New file: ",File_scanNewer.Name,Eol=yes,Console=yes, Unicode=yes
        JNSt [Status::],ArgSwappedDates,.50:
        StdOutput =B"Warning: old scan is younger than the new scan.",Eol=yes,Unicode=no
    .50:; Start to compare File_scanOlder and File_scanNewer.
    .LoadBoth:CALL LoadNewer: ; Output: R8=Volume,RBP=RSI=Pointer to a dir-name,RCX=R12=Size or -1 when at EOF.
    .LoadOld: CALL LoadOlder: ; Output: R9=Volume,RBX=RDI=Pointer to a dir-name,RDX=Size or -1 when at EOF.
    .Compare: CMP RCX,-1
        JNE .55:
        ; File_scanNewer is over or higher. Keep loading File_scanOlder, store it to Buffer5.
        CMP RDX,-1
        JE .EndCompare:         ; Both file are at end.
        JMP .NewIsHigher:
    .55:CMP RDX,-1
        JE .OldIsHigher: ; File_scanOlder is over or higher. Keep loading File_scanNewer, store it to Buffer1.
        CMP ECX,EDX
        JBE .60:
        MOV ECX,EDX
    .60:REPE CMPSB       ; Compare at the matching size.
        MOV RDI,RBX
        JB .OldIsHigher:
        JA .NewIsHigher:
        CMP R12,RDX      ; When names match, look at their sizes.
        JB .OldIsHigher:
        JA .NewIsHigher:
    ; NoneIsHigher; Directory names are equal.
    ; ScanIndex goes to Buffer2,3 or 4.  SCAN_INDEX.Ptr is relative to [NewerInMemory].
        SUB RBP,[NewerInMemory]
        MOV [ScanIndex.Ptr],EBP
        MOV [ScanIndex.Size],EDX
        SUB R8,R9        ; Volume difference is in R8.
        MOV [ScanIndex.Volume],R8
        JNE .75:
        MOV RBX,[Buffer3]
        MOV [ScanIndex.Volume],R9
        JMP .80:
    .75:MOV RBX,[Buffer4]
        JB .80:
        MOV RBX,[Buffer2]
    .80:BufferStore RBX,ScanIndex,SIZE# ScanIndex
        JC ErrorBuffer
        JMP .LoadBoth:
    .NewIsHigher:  ; Line retrieved from File_scanNewer is higher or it already ended.
    ; Keep the line (in R8,RBP,R12) and store the index of line retrieved from File_scanOlder (R9,RBX=RDI,RDX).
    ; ScanIndex goes to Buffer5, SCAN_INDEX.Ptr is relative to [OlderInMemory].
        SUB RBX,[OlderInMemory]
        MOV [ScanIndex.Ptr],EBX
        MOV [ScanIndex.Size],EDX
        NEG R9
        MOV [ScanIndex.Volume],R9
        MOV RBX,[Buffer5]
        BufferStore RBX,ScanIndex,SIZE# ScanIndex
        JC ErrorBuffer
        MOV RSI,RBP
        MOV RCX,R12
        JMP .LoadOld:
    .OldIsHigher:; Line retrieved from File_scanOlder is higher or it already ended.
    ; Keep the line (in R9,RBX=RDI,RDX) and store the index of line retrieved from File_scanNewer (R8,RBP=RSI,R12=RCX).
    ; ScanIndex goes to Buffer1, SCAN_INDEX.Ptr is relative to [NewerInMemory],
        SUB RBP,[NewerInMemory]
        MOV [ScanIndex.Ptr],EBP
        MOV [ScanIndex.Size],R12D
        MOV [ScanIndex.Volume],R8
        MOV R10,RBX
        MOV RBX,[Buffer1]
        BufferStore RBX,ScanIndex,SIZE# ScanIndex
        JC ErrorBuffer
        MOV RBX,R10
        CALL LoadNewer:
        JMP .Compare:
    .EndCompare:
    Nr  %FOR 1..5                           ; Set the lengths of five buffers to NumberxA and NumberxW.
           BufferRetrieve [Buffer%Nr]
              MOV EAX,ECX
              SHR EAX,4
              LEA RDI,[Number%Nr{}A]
              MOV RSI,RDI
              StoD RDI
              XOR EAX,EAX
              STOSB
              LEA RDI,[Number%Nr{}W]
       .8%Nr: LODSB
              STOSW
              CMP AL,0
              JNE .8%Nr:
        %ENDFOR Nr
        SetSt [Status::],ArgDescending
        MOV R8,[NewerInMemory]
        BufferRetrieve [Buffer1]
        SHR ECX,4                        ; Let RCX=number of SCAN_INDEX records (16 bytes each).
        ShellSort RSI,RCX,16,ByVolume    ; ArgDescending, R8=NewerInMemory
        BufferRetrieve [Buffer2]
        SHR ECX,4                        ; Let RCX=number of SCAN_INDEX records (16 bytes each).
        ShellSort RSI,RCX,16,ByVolume    ; ArgDescending, R8=NewerInMemory
        BufferRetrieve [Buffer3]
        SHR ECX,4                        ; Let RCX=number of SCAN_INDEX records (16 bytes each).
        ShellSort RSI,RCX,16,ByVolume    ; ArgDescending, R8=NewerInMemory
        RstSt [Status::],ArgDescending
        BufferRetrieve [Buffer4]
        SHR ECX,4                        ; Let RCX=number of SCAN_INDEX records (16 bytes each).
        ShellSort RSI,RCX,16,ByVolume    ; ArgAscending, R8=NewerInMemory
        MOV R8,[OlderInMemory]
        BufferRetrieve [Buffer5]
        SHR ECX,4                        ; Let RCX=number of SCAN_INDEX records (16 bytes each).
        ShellSort RSI,RCX,16,ByVolume    ; ArgAscending, R8=OlderInMemory
        StdOutput EuroDirs, =B" has found", Eol=yes, Unicode=no
    Nr  %FOR 1..5
          StdOutput Number%Nr{}A, Report%Nr, Eol=yes, Unicode=no
        %ENDFOR Nr
        CLC
    .90:RET
       ENDP LoadAndCompareScans

Arg2Gui
  Copy arguments Arg** from UTF-8 to UTF-16 used by WinGUI module.

    Arg2Gui PROC   ; Convert string arguments Arg*** from UTF-8 to ***UTF16.
        LEA RSI,[ArgScanDir::]
        LEA RDI,[ScanDirUTF16]
        WinABI MultiByteToWideChar,CP_UTF8,0,RSI,-1,RDI,SIZE#ScanDirUTF16/2
        LEA RSI,[ArgInputFile::]
        LEA RDI,[InputFileNameUTF16]
        WinABI MultiByteToWideChar,CP_UTF8,0,RSI,-1,RDI,SIZE#InputFileNameUTF16/2
        LEA RSI,[ArgOutputFile::]
        LEA RDI,[OutputFileNameUTF16]
        WinABI MultiByteToWideChar,CP_UTF8,0,RSI,-1,RDI,SIZE#OutputFileNameUTF16/2
        LEA RSI,[ArgIncludeDirs::]
        LEA RDI,[IncludeDirsUTF16]
        WinABI MultiByteToWideChar,CP_UTF8,0,RSI,-1,RDI,SIZE#IncludeDirsUTF16/2
        LEA RSI,[ArgExcludeDirs::]
        LEA RDI,[ExcludeDirsUTF16]
        WinABI MultiByteToWideChar,CP_UTF8,0,RSI,-1,RDI,SIZE#ExcludeDirsUTF16/2
        ; Check /ScanDir=
        LEA RDI,[ScanDirUTF16]
        CMPW [RDI],0
        JNE .10:
        MOV EAX,'.'                      ; /ScanDir= was not specified, use ".".
        MOV [RDI],EAX
    .10:XOR ECX,ECX
        XOR EAX,EAX
        DEC RCX
        REPNE SCASW
        MOV AX,'\'
        SUB RDI,2
        CMP [RDI-2],AX
        JE .90:
        STOSD                            ; Terminate the scan directory with backslash.
    .90:RET
     ENDP Arg2Gui

StoreFileName
  Procedure StoreFileName is called as a callback from FileEach macro in LoadAndCompareScans.
  It stores the 16*unichar string such as "2025.09.06_09.59" from the scan file name to ScanDirBuffer.
Input
  RDI=^WIN32_FIND_DATAW
containing the name such as eurodirs_2025.09.06_09.59.scan.
ScanDirBuffer must be created.
Output
  CF=0, 16 UNICHARs are stored to ScanDirBuffer.
Error
  ErrorBuffer
Called by
  LoadAndCompareScans.

    StoreFileName PROC
       LEA RSI,[RDI+WIN32_FIND_DATAW.FileName] ; Let RSI point to unichar "2024.09.06_09.59".
       BufferStore [ScanDirBuffer],RSI,16*2
       JC ErrorBuffer
       RET
      ENDP StoreFileName

OneLine2Dir

  Construct a line from the [BufferSelected] into Dir. The line looks like `dir_size directory\name\in\UTF16`, it is encoded in UTF-16 and zero terminated.
  When the input line number RBX is 0, an empty line is returned (CF=1. RCX=0).
  When the input line number RBX is 1, first line is returned (CF=0. RCX>0).
  When the input line number RBX is 2, second line is returned etc.
  When the input line number RBX is bigger than the [MaxLineNr], an empty line is returned (CF=1. RCX=0).

Input
  RBX= line number (0,1,2,,,).
  [ArgViewDirs::]= selected view (1..5 alias ArgViewDirsIns..ArgViewDirsDel).
Output
  RSI=Dir is filled with the line containing the differential size and name in WIDE encoding.
  RCX= is size of the output line in bytes.
Calls
  StoreWideChar, AddUnderscores
Called by
  LoadAndCompareScans
Clobbers
  RAX,RDX

    OneLine2Dir:: PROC
        PUSH RBX,RDI
         MOV EAX,[ArgViewDirs::]
         MOV RDX,[Buffers+8*RAX-8]
         BufferRetrieve RDX    ; Load the buffer contents to RSI,RCX.
         ADD RCX,RSI           ; The end of buffer contents.
         SAL EBX,4             ; Each SCAN_INDEX in the buffer contents has size=16.
         JZ .10:
         LEA RBX,[RBX+RSI-SIZE# SCAN_INDEX] ; Let RBX point at the selected SCAN_INDEX.
         CMP RBX,RCX           ; Is it out of contents?
         JB .20:
    .10: XOR ECX,ECX
         LEA RSI,[Dir]
         STC
         JMP .90:              ; Abort when the requested line number exceeded the buffer and return CF=1 and empty line.
    .20: MOV RDX,[NewerInMemory]
         CMP AL,ArgViewDirsDel
         JNE .30:
         MOV RDX,[OlderInMemory] ; Select the mapping of File_scanOlder or File_scanNewer.
    .30: MOV RAX,[RBX+SCAN_INDEX.Volume]
         LEA RSI,[NumberA]
         StoD RSI,Size=20      ; Store the directory volume as a decimal ANSI
         CALL AddUnderscores   ;  and complete it with separators.
         LEA RSI,[Number_A]
         CMPB [ArgViewDirs::],ArgViewDirsDel
         JNE .50:
         CMPB [RSI],'0'
         JE .50:
         INC RSI               ; Skip the minus sign when viewing deleted direcotires.
    .50: LEA RDI,[Dir]
         CMPB [ArgViewDirs::],ArgViewDirsInc
         JNE .60:
         MOV AL,'+'            ; Add a plus sign when viewing incremented directories.
         JMP .70:
    .60: LODSB                 ; Convert the number from ANSI to WIDE.
         CMP AL,0
         JE .80:
    .70: STOSW
         JMP .60:
    .80: MOV ECX,[RBX+SCAN_INDEX.Size]
         MOV EAX,[RBX+SCAN_INDEX.Ptr]
         ADD RDX,RAX           ; Let RDX point to the directory name.
         DecodeUTF8 RDX,StoreWideChar,Size=RCX,Width=16
         MOV RCX,RDI           ; At the end of directory name.
         MOVW [RDI],0          ; Zero terminate.
         LEA RSI,[Dir]
         SUB RCX,RSI           ; CF=0, RSI,RCX is the target line.
    .90:POP RDI,RBX
        RET
      ENDP OneLine2Dir

AddUnderscores

  Complete an ASCII decadic number with underscores `_` per each three digits.

Input
  RSI= is pointer to the beginning of the number.
  RDI= is pointer at the end of the number.
Output
  Number_A is filled with the number and separators, space and zero terminated.
Called by
  LoadAndCompareScans
Clobbers
  RAX,RCX,RDI

    AddUnderscores PROC
       PUSH RDX
        SUB RDI,RSI
        MOV ECX,3
        MOV EAX,EDI
        XOR EDX,EDX
        LEA RDI,[Number_A]
        DIV RCX
        CMP EDX,0
        JNE .20:
        REP MOVSB
        DEC EAX
        JMP .40:
    .20:CMP EDX,1
        JNE .30:
        MOVSB
        CMPB [RSI-1],'-'
        JNE .40:
        XCHG EAX,EDX
        DEC EDX
        JS .80:
        JMP .60:
    .30:MOVSW
    .40:XCHG EAX,EDX
    .50:DEC RDX
        JS .80:
        MOV AL,'_'
        STOSB
    .60:MOV CL,3
        REP MOVSB
        JMP .50:
    .80:MOV AX,' '
        STOSW
       POP RDX
       RET
     ENDP AddUnderscores

StoreWideChar
  Callback procedure StoreWideChar from DecodeUTF8.
Called by
  ReadFile_ini, OneLine2Dir.
Input
  AX=a character to write.
  RDI=position to write.
Output
  RDI=incremented by 2.

    StoreWideChar:: PROC
      STOSW
      RET
     ENDP StoreWideChar:

LoadOlder
  Load one directory name and volume from File_scanOlder mapped at OlderInMemory..OlderEndInMemory.
  Each record has variable size: `/dir_name 0x09 dir_volume_decimal 0x0A`
Input
  OlderPtr is pointer to one record,
  OlderEndInMemory points to the end of records.
Output
  R9=volume,
  RBX=RDI=pointer to a directory name,
  RDX=directory name size or -1 when at EOF.
  OlderPtr is updated.
Preserves
  R8,RCX,RSI,RBP
Clobbers
  RAX,R10,R11

    LoadOlder PROC
        MOV R10,RCX                     ; Save preserved RCX.
        MOV RDI,[OlderPtr]
        MOV RCX,[OlderEndInMemory]
        MOV RBX,RDI
        MOV RDX,RDI
        SUB RCX,RDI
        JNA .90:
        MOV AL,9
        REPNE SCASB
        MOV R11,RSI
        MOV RSI,RDI
        DEC RDI                         ; Go at HT.
        SUB RDI,RDX
        XCHG RDI,RDX
        LodD RSI
        MOV R9,RAX
        INC RSI                         ; Skip LF.
        MOV [OlderPtr],RSI
        MOV RSI,R11
        MOV RCX,R10                     ; Restore preserved RCX.
        RET                             ; Normal end.
    .90:MOV RDX,-1
        MOV RCX,R10                     ; Restore preserved RCX.
        RET                             ; End at EOF.
      ENDP LoadOlder

LoadNewer
  Load one directory name and volume from File_scanNewer mapped at NewerInMemory..NewerEndInMemory.
  Each record has variable size: `/dir_name 0x09 dir_volume_decimal 0x0A`
Input
  NewerPtr is pointer to one record,
  NewerEndInMemory points to the end of records.
Output
  R8=volume,
  RBP=RSI=pointer to a directory name,
  RCX=R12=directory name size or -1 when at EOF.
  NewerPtr is updated.
Preserves
  R9,RBX,RDX,RDI
Clobbers
  RAX,R10

    LoadNewer PROC
        MOV R10,RDI                     ; Save preserved RDI.
        MOV RDI,[NewerPtr]
        MOV RCX,[NewerEndInMemory]
        MOV RBP,RDI
        SUB RCX,RDI
        JNA .90:
        MOV AL,9
        REPNE SCASB
        MOV RSI,RDI
        DEC RDI
        SUB RDI,RBP
        MOV ECX,EDI
        LodD RSI
        MOV R8,RAX
        INC RSI                         ; Skip LF.
        MOV [NewerPtr],RSI
        MOV RDI,R10                     ; Restore preserved RDI.
        MOV RSI,RBP                     ; Normal end.
        MOV R12,RCX
        RET                             ; Normal end.
    .90:MOV RDI,R10                     ; Restore preserved RDI.
        MOV RCX,-1
        MOV R12,RCX
        RET                             ; End at EOF.
      ENDP LoadNewer

ExcludeDir
  Procedure ExcludeDir returns ZF=1 when the directory name in Dir is it the ArgExcludeDirs.
Called by
  Scan.
Input
  ExcludeDirsPtr, ExcludeDirsEnd are pointers to an array of 2*QWORDs.
Clobbers
  RAX,RCX,RDX,RSI,RDI

    ExcludeDir PROC
        MOV RSI,[ExcludedDirsPtr]
    .10:CMP RSI,[ExcludedDirsEnd]
        JAE .90:
        LODSQ
        MOV RCX,RAX
        LODSQ
        JRCXZ .10:
        XCHG RSI,RAX
        LEA RDI,[Dir]
        REPE CMPSW
        XCHG RAX,RSI
        JNE .10:
        RET        ; ZF=1.
    .90:CMP ESI,1
        RET        ; ZF=0.
       ENDP ExcludeDir

ParseToBuffer
  Procedure ParseToBuffer will parse a multiple semicolon-separated pathes from RSI and for each path it stores two QWORDs to the buffer: length of dir in unichars and pointer.
Called by
  Scan.
Input
  RAX= output empty BUFFER.
  RSI= pointer to IncludeDirsUTF16 or ExcludeDirsUTF16.

    ParseToBuffer PROC
        MOV RBX,RAX
    .10:MOV RDX,RSI
    .20:LODSW
        CMP AX,0
        JE .50:
        CMP AX,';'            ; ; is the folder names separator.
        JNE .20:
        CALL .50:             ; Store to buffer and continue.
        JMP .10:
    .50: ; Store size RSI-2-RDX and pointer RDX to the buffer RBX.
        LEA RAX,[RSI-2]
        SUB RAX,RDX
        SHR RAX,1             ; Convert bytes to unichars.
        JZ .90:
        PUSH RAX              ; Save folder name size on stack.
          MOV RAX,RSP
          BufferStore RBX,RAX,8 ; Dir name size in unichars.
        POP RAX
        JC ErrorBuffer
        PUSH RDX              ; Save pointer to folder name on stack.
          MOV RAX,RSP
          BufferStore RBX,RAX,8 ; Dir ptr.
        POP RAX
        JC ErrorBuffer
    .90:RET
       ENDP ParseToBuffer

ReadFile_ini
  Procedure ReadFile_ini reads lines from File_ini in UTF-8 and parses its each line into configuration variables Arg*** in UTF-8, too.
  The File_ini.Name is in UTF-16. Procedure writes information about the file name and whether it was found.
Called by
  MainCon.
Input
  File_ini is assigned with the name.
Clobbers
  RAX,RCX,RDX,RSI,RDI

    ReadFile_ini PROC
        SetSt [Status::],ArgFromFile       ; Tell ArgParse that arguments may not begin with / or -.
        StdOutput =B"Configuration """,Unicode=no
        StdOutput File_ini.Name,Console=yes,Unicode=yes
        FileStreamOpen File_ini,BufSize=4K
        JNC .10:
        StdOutput =B""" was not found.",Eol=yes,Unicode=no
        JMP .90:
    .10:FileStreamReadLn File_ini
        JBE .80:
        MOV ECX,EAX
        ; The first line may begin with BOM.
        MOV AX,[RSI]
        CMPW AX,0xBBEF   ; UTF-8 BOM?
        JNE .50:
        ADD RSI,3        ; Skip the BOM.
        SUB ECX,3
        JB .80:
        JMP .50:
    .30:FileStreamReadLn File_ini
        JBE .80:
        MOV ECX,EAX
    .50:CALL ArgParse::
        JNC .30:
        LEA RSI,[Help+3]
        StdOutput RSI,Unicode=no
        TerminateProgram 8
    .80:FileClose File_ini
        StdOutput =B""" was accepted.",Eol=yes,Unicode=off
    .90:RstSt [Status::],ArgFromFile
        RET
      ENDP ReadFile_ini

ByName
  Procedure ByName is a callback function to sort by directory name.
Input
  RSI,RDI point to SCAN_INDEX records to compare.
Clobbers
  RAX,RDX,RSI,RDI,R09,R10,R12
Called by
  Scan.

    ByName: PROC
        MOV R8,RCX              ; Save RCX,RSI,RDI.
        MOV R9,RSI
        MOV R10,RDI
        MOV EDX,[RDI+SCAN_INDEX.Size]
        MOV ECX,[RSI+SCAN_INDEX.Size]
        CMP ECX,EDX
        JBE .10:
        MOV ECX,EDX              ; Compare dirnames by the shorter of both sizes.
    .10:MOV ESI,[RSI+SCAN_INDEX.Ptr]
        MOV EDI,[RDI+SCAN_INDEX.Ptr]
        ADD RSI,[NamesInMemory]
        ADD RDI,[NamesInMemory]
        DEC ECX
        XCHG RSI,RDI             ; Ascending.
        REPE CMPSB
        JA .90:
        JB .50:
        CMP EDX,[R9+SCAN_INDEX.Size] ; When the dirnames are the same, compare by dirname size.
        JAE .90
    .50:MOV RAX,[R9+SCAN_INDEX.Ptr]  ; Records are in wrong order. Swap them.
        MOV RDX,[R9+SCAN_INDEX.Volume]
        XCHG RAX,[R10+SCAN_INDEX.Ptr]
        XCHG RDX,[R10+SCAN_INDEX.Volume]
        MOV [R9+SCAN_INDEX.Ptr],RAX
        MOV [R9+SCAN_INDEX.Volume],RDX
    .90:MOV RCX,R8               ; Restore RCX.
        RET
      ENDP ByName:

ByDate
  Procedure ByDate is a callback function to sort a table of DateMaskW records.
Input
  RSI,RDI point to DateMaskW records to compare.
Clobbers
  RAX,RDX,RSI,RDI
Called by
  Scan.

    ByDate: PROC
        XCHG RSI,RDI
        PUSH RCX,RSI,RDI
         MOV ECX,SIZE# DateMaskW /2
         REPE CMPSW
        POP RDI,RSI,RCX
        JAE .90:
        MOV  RAX,[RSI+00]
        MOV  RDX,[RSI+08]
        XCHG [RDI+00],RAX
        XCHG [RDI+08],RDX
        MOV  [RSI+00],RAX
        MOV  [RSI+08],RDX
        MOV  RAX,[RSI+16]
        MOV  RDX,[RSI+24]
        XCHG [RDI+16],RAX
        XCHG [RDI+24],RDX
        MOV  [RSI+16],RAX
        MOV  [RSI+24],RDX
    .90:RET
       ENDP ByDate

ByVolume
  Procedure ByVolume is a callback function to sort a table of SCAN_INDEX records by volume and then by directory name.
Input
  RSI,RDI point to SCAN_INDEX records to compare.
  R8 contains [NewerInMemory] or [OlderInMemory].
  ArgDescending specifies the sort direction.
Clobbers
  RAX,RDX,RSI,RDI

    ByVolume:PROC
        JSt [Status::],ArgDescending,.10:
        XCHG RSI,RDI
    .10:MOV RAX,[RSI+SCAN_INDEX.Volume]
        MOV RDX,[RDI+SCAN_INDEX.Volume]
        CMP RDX,RAX
        CLC
        JG .90:
        MOV R9,RCX
        MOV R10,RSI
        MOV R11,RDI
        XCHG RSI,RDI
        JL .60:
        MOV ECX,[RSI+SCAN_INDEX.Size]
        MOV ESI,[RSI+SCAN_INDEX.Ptr]
        MOV EDX,[RDI+SCAN_INDEX.Size]
        MOV EDI,[RDI+SCAN_INDEX.Ptr]
        ADD RSI,R8
        ADD RDI,R8
        CMP ECX,EDX
        JBE .20:
        MOV ECX,EDX
    .20:REPE CMPSB
        JA .80:
        JB .50:
        CMP EDX,[R10+SCAN_INDEX.Size]
        JAE .80:
    .50:MOV RAX,[R10+SCAN_INDEX.Volume]
        MOV RDX,[R11+SCAN_INDEX.Volume]
    .60:MOV [R11+SCAN_INDEX.Volume],RAX
        MOV [R10+SCAN_INDEX.Volume],RDX
        MOV RAX,[R10+SCAN_INDEX.Ptr]
        MOV RDX,[R11+SCAN_INDEX.Ptr]
        MOV [R11+SCAN_INDEX.Ptr],RAX
        MOV [R10+SCAN_INDEX.Ptr],RDX
        STC
    .80:MOV RCX,R9
    .90:RET
     ENDP ByVolume:

SetToday
  Procedure SetToday will set a nominal local time of a scan
Called by
  MainCon.
Clobbers
  RAX,RBX,RCX,RSI,RDI

    SetToday PROC
        LEA RBX,[SystemTime]
        WinABI GetLocalTime,RBX
        LEA RDI,[TodayA]
        MOVZXW EAX,[RBX+SYSTEMTIME.wYear]
        StoD RDI,Size=4,Signed=no,Align=right,LeadingZeroes=yes
        MOV AL,'.'
        STOSB
        MOVZXW EAX,[RBX+SYSTEMTIME.wMonth]
        StoD RDI,Size=2,Signed=no,Align=right,LeadingZeroes=yes
        MOV AL,'.'
        STOSB
        MOVZXW EAX,[RBX+SYSTEMTIME.wDay]
        StoD RDI,Size=2,Signed=no,Align=right,LeadingZeroes=yes
        MOV AL,'_'
        STOSB
        MOVZXW EAX,[RBX+SYSTEMTIME.wHour]
        StoD RDI,Size=2,Signed=no,Align=right,LeadingZeroes=yes
        MOV AL,'.'
        STOSB
        MOVZXW EAX,[RBX+SYSTEMTIME.wMinute]
        StoD RDI,Size=2,Signed=no,Align=right,LeadingZeroes=yes
        XOR EAX,EAX
        STOSB
        LEA RSI,[TodayA]
        LEA RDI,[TodayW]
        MOV ECX,SIZE# TodayA
    .50:LODSB
        STOSW
        LOOP .50:
        RET
      ENDP SetToday

SetMaxDaysAgo
  Procedure SetMaxDaysAgo will set a current local time stored by SetToday into TodayA and TodayW minus /MaxDays= into string variables MaxDaysAgoA and MaxDaysAgoW.
Called by
  MainCon.
Clobbers
  RAX,RBX,RCX,RSI,RDI

    SetMaxDaysAgo PROC
        LEA RSI,[TodayA]
        LEA RDI,[MaxDaysAgoA]
        MOV ECX,SIZE# MaxDaysAgoA
        REP MOVSB
        LEA RBX,[FileTime]
        WinABI GetSystemTimeAsFileTime,RBX
        MOV RBX,[RBX]  ; RBX is the number of 100-nanosecond intervals since January 1, 1601.
        MOV RAX,24*60*60*10_000_000
        MOV ECX,[ArgMaxDays::]
        MUL RCX
        SUB RBX,RAX
        LEA RDI,[FileTime]
        MOV [RDI],RBX
        WinABI FileTimeToDosDateTime,RDI,FatDate,FatTime
        MOV AX,[FatDate]
        SHR AX,9
        ADD AX,1980
        LEA RDI,[MaxDaysAgoA]
        StoD RDI,Size=4,Signed=no,Align=right,LeadingZeroes=yes
        MOV AX,[FatDate]
        AND AX,01E0h
        SHR AX,5
        INC RDI
        StoD RDI,Size=2,Signed=no,Align=right,LeadingZeroes=yes
        MOV AX,[FatDate]
        AND AX,001Fh
        INC RDI
        StoD RDI,Size=2,Signed=no,Align=right,LeadingZeroes=yes
        LEA RSI,[MaxDaysAgoA]
        LEA RDI,[MaxDaysAgoW]
        MOV ECX,SIZE# MaxDaysAgoA
    .50:LODSB
        STOSW
        LOOP .50:
        RET
      ENDP SetMaxDaysAgo

EraseOlderFile
  Procedure EraseOld is called as a callback from FileEach
Called by
  Scan.
Input
  RBX=^FILE64 with the name.
  RDI=^WIN32_FIND_DATAW
Clobbers
  RAX,RCX,RSI,RDI

    EraseOlderFile: PROC
        LEA RSI,[RDI+WIN32_FIND_DATAW.FileName+18]
        LEA RDI,[MaxDaysAgoW]
        MOV ECX,16
        REPE CMPSW                ; Compare datetime encoded in the file name.
        JAE .90:
        LEA RAX,[RBX+FILE64.Name]
        StdOutput =U"Old scan """,RAX,=U""" was deleted.",Eol=yes,Console=yes,Unicode=yes
        FileDelete RBX
    .90:RET
      ENDP EraseOlderFile:

Error
  Simplified error handling will abort the program with errorlevel 8.
Input
  RBX=^FILE64 which caused the error.
Called by
  Scan

    ErrorZero: ; RBX=^FILE64
         StdOutput =B'Error: zero size of the file "',Unicode=no
          LEA RAX,[RBX+FILE64.Name]
          JMP Write
    ErrorMap:  ; RBX=^FILE64
          StdOutput =B'Error: memory-mapping the file "',Unicode=no
          LEA RAX,[RBX+FILE64.Name]
          JMP Write
    ErrorFile: ; RBX=^FILE64
          StdOutput =B'Error: could not open the file "',Unicode=no
          LEA RAX,[RBX+FILE64.Name]
          JMP Write
    ErrorWrite:  ; RBX=^FILE64
          StdOutput =B'Error writing to the file "',Unicode=no
          LEA RAX,[RBX+FILE64.Name]
    Write:StdOutput RAX,Eol=no,Console=yes,Unicode=yes
          StdOutput =B'".', Unicode=no, Eol=yes
          JMPS .Terminate:
    ErrorBuffer:
          StdOutput =B"Error allocating memory.",Eol=yes,Unicode=no
    .Terminate:
          FileClose File_scan, File_index, File_names
          BufferDestroy [ScanDirBuffer],[Buffer1],[Buffer2],[Buffer3],[Buffer4],[Buffer5]
          TerminateProgram 8
          TerminateProgram 8

        ENDPROGRAM dirswinc

▲Back to the top▲