        SUBT    > <wini>arm.FileSwitch.FSUtils3 - Count + Common bits

 [ hascount
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                    W i l d c a r d   C o u n t a l l
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; CountEntry
; ==========

; In    r1 -> path to count
;       r3 = bitset for count
;       r4,r5 = optional time (start)
;       r6,r7 = optional time (end)

; Out   r2 = sizeof path counted
;       r3 = number of files counted

CountEntry NewSwiEntry "r0-r1, r4-r10",utils
                                        ; CountWildObject may destroy all regs
                                        ; Extra large frame used in util
 [ debugcount
 DLINE "FSControl_Count: options '",cc
 TST r3, #util_confirm
 SWINE XOS_WriteI+"C"
 TST r3, #util_recurse
 SWINE XOS_WriteI+"R"
 TST r3, #util_verbose
 SWINE XOS_WriteI+"V"
 DREG r3,"' "
 ]
        BL      Util_CommonStart
        BVS     %FA99 ; SwiExit - nothing allocated

        BL      CountWildObject

        BL      Util_FreeFileStrings_R7 ; Catch errors from these on SwiExit
                                        ; 'Cos it's nice to print ...
        BL      SFreePassedFilenameAndSpecial ; Ditto

        LDR     r14, util_bitset
        TST     r14, #util_printok
        BEQ     %FT95                   ; [skip printing]

        LDR     r0, util_nfiles
        ADR     r1, fsw_space_counted
        BL      Util_FilesDone          ; Say '65 files counted, 12345 bytes'

        LDRVC   r0, util_totalsize
        BLVC    Util_PrintFullSize

        BLVS    CopyError

95      LDRVC   r2, util_totalsize
        LDRVC   r3, util_nfiles

99      SwiExit


fsw_space_counted DCB   " counted, ", 0
                  ALIGN

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; CountWildObject
; ===============

; src * : CountObject on all that match
; src - : CountObject given name

; Out   Only r7, wp preserved

CountWildObject ENTRY

 [ debugcount
 DLINE "CountWildObject"
 ]
        LDR     r1, [r7, #copy_leafname] ; If not wild, do single count
        BL      Util_CheckWildName
        BNE     %FT90                   ; Single count

; ...................... Wild object spec for count ..........................

        MOV     r9, r1                  ; Remember current leafname

        MOV     r14, #0                 
        STR     r14, util_direntry      ; Start at the beginning of dir
        STR     r14, [r7, #copy_leafname] ; 'Free' leafname !

10 ; Loop over names, match against wildcard
   ; fullname explicitly made and freed

        BL      STestEscape             ; Util grade, not copied
        BLVS    CopyError
        BLVC    Util_ReadDirEntry       ; Get a name. Uses + updates util_diren
        BVS     %FT60                   ; Put old leafname back, exit

        CMP     r3, #0                  ; No name xferred ?
        BEQ     %FT60                   ; Means eod

        ADR     r4, util_objectname     ; What we read just now
        STR     r4, [r7, #copy_leafname] ; Use as source leafname if matches
        BL      Util_TryMatch
        BNE     %BT10                   ; Loop - try another name

        MOV     r0, r7                  ; Create fullname of each object
        BL      Util_AppendLeafToDir    ; that matched wildcard

        Push    r9                      ; Preserve wild pattern round call
        BLVC    CountObject
        Pull    r9

        LDR     r2, [r7, #copy_name]    ; Deallocate new fullname always
        BL      SFreeArea               ; Accumulate V from Count/Free
        BVC     %BT10                   ; Loop - try another name
   
60      STR     r9, [r7, #copy_leafname] ; Put old leafname back
        EXIT


; .......... Single object count. Must read object info ourselves ..........

; NB. Don't call CountObject - if counting dir, just recurse one level unless
; punter wants more than that

90      MOV     r0, r7                  ; Create fullname of object
        BL      Util_AppendLeafToDir    ; from existing (non-wild) leafname
        EXIT    VS

        BL      Util_SingleObjectSetup  ; Error if not found
        BVS     %FT95                   ; Deallocate error
        BHI     %FT96                   ; Is it a dir ?
        BLEQ    CountFile               ; If it's a file

95      LDR     r2, [r7, #copy_name]    ; Deallocate fullname
        BL      SFreeArea               ; Accumulate V from Count/Free
        EXIT


96      BL      CountDirectory
        B       %BT95

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; CountObject
; ===========
;
; Count object, recursing as necessary/required
; Dirname set up for this level
; Leafname already set up after possible wildcard match
; Fullname = dirname + leafname set up

; In    r7 = source file desc^

; Out   r0-r5 corrupted by calls
;       VC: object counted, skipped by user, date, or dir & ~recurse
;       VS: failed in count, or aborted

CountObject ENTRY

 [ debugcount
 DLINE "CountObject"
 ]
        LDR     r14, util_objecttype    ; What is it ?
        TEQ     r14, #object_directory
        BEQ     %FT50

; Count existing file

        BL      CountFile
        EXIT


50 ; It's a directory. Are we recursing ?

        LDR     r14, util_bitset        ; Recurse set ?
        TST     r14, #util_recurse
        BEQ     %FT70

; Count this dir, recursing

        BL      CountDirectory          ; dir & recurse
        EXIT


70      TST     r14, #util_verbose      ; dir & ~recurse. No need to INC skippd
        EXIT    EQ

        MOV     r0, r7                  ; src
        BL      Util_PrintName
        addr    r0, fsw_space_is_a_dir, VC
        SWIVC   XOS_Write0
        BLVS    CopyError
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; CountFile
; =========
;
; Count single file (which exists)
; Fullname set up in desc^.name; info read

; In    r7 = file desc^, file exists

; Out   r0, r5 corrupt
;       VC: file counted, or skipped
;       VS: failed to count file, or aborted

CountFile ENTRY

 [ debugcount
 DLINE "CountFile"
 ]
        BL      CountFile_Confirm       ; Ask the punter
        EXIT    VS
        BLNE    Util_IncrementSkipped
        EXIT    NE                      ; Don't count this file

; Add size to total

        BLVC    CountFile_Verbose        ; Tell them copy action is complete
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; CountDirectory
; ==============

; Count whole directory contents then count the directory itself
; Fullname of dir set up in desc^.name; info read

; In    r7 = dir desc^, directory exists

; Out   r0-r5 corrupt
;       VC: directory counted, or skipped
;       VS: failed to count directory, or aborted

CountDirectory ENTRY

 [ debugcount
 DLINE "CountDirectory"
 ]
        BL      CountDirectory_ConfirmAll
        EXIT    VS
        BLNE    Util_IncrementSkipped
        EXIT    NE

; Count contents of dir, saving quite a bit of info on stack

        LDR     r1, util_totalsize      ; How much so far this level
        Push    r1

        LDR     r0, [r7, #copy_dirprefix] ; Push names
        LDR     r1, [r7, #copy_leafname]
        LDR     r2, [r7, #copy_name]
        LDR     r3, util_direntry       ; Push position in dir
        LDR     r4, util_bitset         ; Push flags
        LDR     r5, util_attr           ; Push locked state
        LDR     r14, util_nskipped
        Push    "r0-r5, r14"

        LDR     r14, [r7, #copy_name]   ; Use fullname as dirprefix
        STR     r14, [r7, #copy_dirprefix]
        addr    r14, fsw_wildstar       ; Leafname := '*'
        STR     r14, [r7, #copy_leafname]
        MOV     r14, #0
        STR     r14, util_nskipped      ; Nothing skipped at this level
        STR     r14, util_totalsize     ; Nothing counted at this level

        BL      CountWildObject

        Pull    "r0-r5, r14"            ; Restore names, locked state (to r5)
        STR     r0, [r7, #copy_dirprefix]
        STR     r1, [r7, #copy_leafname]
        STR     r2, [r7, #copy_name]
        STR     r3, util_direntry
        STR     r4, util_bitset

        LDR     r0, util_nskipped       ; Remember nskipped at that level
        STR     r14, util_nskipped

        LDR     r14, util_totalsize     ; Remember size copied at that level
        STR     r14, util_length

        Pull    r1                      ; Restore size copied at this level
        ADD     r1, r1, r14             ; Add here because CountDir_Verbose
        STR     r1, util_totalsize      ; mustn't do it (verbose problems)

        BLVC    CountDirectory_Verbose  ; Tell them count dir complete
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; CountFile_Confirm
; =================

; If dates allow count, maybe ask punter if he wants to do it

; Out   VS: error
;       VC, EQ -> do it
;       VC, NE -> don't do it

CountFile_Confirm ENTRY "r0, r1"

        BL      Util_DateRange          ; See if dates allow it
        EXIT    NE

        addr    r0, fsw_file_space

10      LDR     r14, util_bitset        ; Entered from below
        TST     r14, #util_confirm
        EXIT    EQ

        SWI     XOS_WriteS
        DCB     "Count ", 0
        ALIGN
        SWIVC   XOS_Write0              ; Second string
        MOVVC   r0, r7                  ; src
        BLVC    Util_PrintName

        BLVC    Util_GetConfirm
        BLVS    CopyError
        EXIT

; .............................................................................
;
; CountDirectory_ConfirmAll
; =========================

; Out   VS: error
;       VC, EQ -> do it
;       VC, NE -> don't do it

CountDirectory_ConfirmAll ALTENTRY

        addr    r0, fsw_contents_of_dir_space
        B       %BT10

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; CountFile_Verbose
; =================

; Counted the file, so print copied info if being verbose. nfiles++

; Out   VS: error

CountFile_Verbose ENTRY "r0, r9"

        LDR     r9, util_length         ; Always increment size copied
        LDR     r14, util_totalsize
        ADD     r14, r14, r9
        STR     r14, util_totalsize

        BL      Util_IncrementFiles_TestVerbose
        EXIT    EQ                      ; EQ -> ~verbose

        addr    r0, fsw_File_space      ; 'File '

30      SWI     XOS_Write0
        MOVVC   r0, r7                  ; object_name
        BLVC    Util_PrintName
        addr    r0, fsw_space_counted, VC ; ' counted, '
        SWIVC   XOS_Write0

        LDRVC   r0, util_length
        BLVC    Util_PrintSize          ; 'xxx bytes', NL

        BLVS    CopyError
        EXIT

; .............................................................................
;
; CountDirectory_Verbose
; ======================

; Counted the directory; always print info

; Out   VS: error

CountDirectory_Verbose ALTENTRY

        BL      Util_IncrementDir_TestVerbose ; Ignore verbose flag here !!!

        LDR     r14, util_bitset        ; If ~print then exit (must have added
        TST     r14, #util_printok      ; totalsize in CountDirectory). Don't
        EXIT    EQ                      ; change without knowing why this is.

        addr    r0, fsw_Directory_space ; 'Directory '
        B       %BT30

 ] ; hascount

 [ hasutil
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                   Common   routines   for   utilities
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Common startup - save registers + initialise. Process first name

; In    r3    = bits for utility
;       r4,r5 = start time
;       r6,r7 = end time
;       Only have utilslocalframe unless called from copy

; Out   r7 -> util_block

Util_CommonStart ENTRY

        TST     r3, #util_printok       ; Can't be verbose or ask for confirm
                                        ; if printing not allowed
        BICEQ   r3, r3, #util_confirm + util_verbose + util_promptchange

        TST     r3, #util_structureonly ; Force recurse if 'T' option
        ORRNE   r3, r3, #util_recurse

        TST     r3, #util_promptchange  ; If prompting then reduce n disc swaps
        BICNE   r3, r3, #util_peekdest

        TST     r3, #util_confirm       ; Remember top level confirmation
        ORRNE   r3, r3, #util_topconfirm
        STR     r3, util_bitset

        AND     r5, r5, #&FF            ; Nobble time bits to 5 byte range
        AND     r7, r7, #&FF
        ADR     r14, util_times         ; Start then end - global to the copy
        STMIA   r14, {r4-r7}

        MOV     r14, #0
        STR     r14, util_ndir
        STR     r14, util_nfiles
        STR     r14, util_nskipped
        STR     r14, util_totalsize

; Set up the first name in PassedFilename/SpecialField - common to all three

        BL      TransNameSetFSAndPolice ; Molest the string
        EXIT    VS                      ; Nothing allocated

        BL      Util_MakeFileStrings

        BLVS    SFreePassedFilenameAndSpecial ; Deallocate if error

 [ debugutil
 EXIT VS
 DLINE "Setting up r7 util block"
 ]
        ADRVC   r7, util_block
        STRVC   r0, [r7, #copy_dirprefix]
        STRVC   r1, [r7, #copy_leafname]
        STRVC   r2, [r7, #copy_special]
        STRVC   r3, [r7, #copy_fscb]
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Prints '1 file' or 'xxx files' as appropriate

; In    r0 =  number of files
;       r1 -> string to print afterwards

; Out   VS: r0 -> error
;       VC: r0 corrupt

Util_FilesDone ENTRY "r1-r3"

        MOV     r3, r0
        SUB     sp, sp, #32
        MOV     r1, sp
        MOV     r2, #32
        SWI     XOS_ConvertCardinal4
        SWIVC   XOS_Write0
        ADD     sp, sp, #32
        ADRVC   r0, fsw_space_file      ; ' file'
        SWIVC   XOS_Write0
        EXIT    VS

        CMP     r3, #1
        SWINE   XOS_WriteI+"s"

        LDRVC   r0, [sp]                ; Print postfix string
        SWIVC   XOS_Write0
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Print length as (xxxx bytes or xxxx Kbytes or xxxx Mbytes), nl

; In    r0 = size in bytes

; Out   VS: r0 -> error, not copied
;       VC: r0 corrupt

Util_PrintSize ENTRY "r1-r3"

        SUB     sp, sp, #32
        MOV     r1, sp
        MOV     r2, #32
        SWI     XOS_ConvertFileSize
        SWIVC   XOS_Write0

90      SWIVC   XOS_NewLine
        ADD     sp, sp, #32
        EXIT

; .............................................................................
; Print length as xxxxxxxxxx bytes

; In    r0 = size in bytes

; Out   VS: r0 -> error
;       VC: r0 corrupt

Util_PrintFullSize ALTENTRY

        SUB     sp, sp, #32
        MOV     r3, r0
        MOV     r1, sp
        MOV     r2, #32
        SWI     XOS_ConvertCardinal4
        SWIVC   XOS_Write0
        ADRVC   r0, fsw_space_byte
        SWIVC   XOS_Write0
        BVS     %BA90
        CMP     r3, #1                  ; VClear. Unpluralisationism rules ok
        SWINE   XOS_WriteI+"s"
        B       %BA90


fsw_space_file DCB " file", 0
fsw_space_byte DCB " byte", 0

        ALIGN

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; Util_CheckWildName
; ==================

; Test leafname for wildness

; In    r1 -> leafname

; Out   EQ: wild spec
;       NE: not wild

Util_CheckWildName ENTRY "r1"

 [ debugutil
 DSTRING r1,"Util_CheckWildName: "
 ]
01      LDRB    r14, [r1], #1
        CMP     r14, #space             ; LO term (never space in name -> NE)
        EXIT    LS
        CMP     r14, #"*"
        CMPNE   r14, #"#"
        EXIT    EQ                      ; Either EQ -> wild or NE -> not wild
        B       %BT01

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; Util_MakeFileStrings
; ====================

; Separate a policed name into dirprefix and leafname strings in heap

; Out   VC: r0 -> dirprefix string (new, derived from last policed name)
;           r1 -> leafname string (new, ditto)
;           r2 -> special string (SpecialField)
;           r3 -> fscb (fsforthisop)
;       VS: error set

Util_MakeFileStrings ROUT

 [ debugutil
 DLINE "Util_MakeFileStrings"
 ]
        MOV     r0, #0                  ; No storage allocated yet
        MOV     r1, #0

        ENTRY   "r0, r1"                ; Resultis 0 for strings

        LDR     r0, strippedfilenameptr
        LDR     r1, leafnameptr

        SUBS    r3, r1, r0              ; Are they the same ? (ie no dirprefix)
        BEQ     %FT10                   ; If so, leave dirprefix result = 0

; Would -1 to remove '.' separator, but we also need +1 for terminator

        BL      SMustGetArea            ; Can't SGetString here 'cos unterm'd
        EXIT    VS                      ; Nothing to deallocate yet

        STR     r2, [sp]
05      LDRB    r14, [r0], #1           ; Copy dirprefix
        CMP     r0, r1
        MOVEQ   r14, #0
        STRB    r14, [r2], #1
        BNE     %BT05

10      LDRB    r0, [r1], #0            ; Null leafname ?
        CMP     r0, #0
        ADREQ   r1, %FT66               ; Make it '@' if so
        ADD     r0, sp, #4*1            ; Result leafname
        BL      SNewString

 [ debugutil
 BVS %FT99
 LDR     r1,[sp]
 DSTRING r1,"Copied: Dirprefix ",cc
 LDR     r1,[sp, #4]
 DSTRING r1,", leafname ",cc
 LDR     r1,SpecialField
 DSTRING r1,", special "
99
 ]
        LDRVC   r2, SpecialField        ; Already copied, so don't bother to
        LDRVC   r3, fsforthisop         ; make a new one nowadays !

        LDRVS   r2, [sp]                ; Free dirprefix if error in
        BLVS    SFreeArea               ; allocating leafname string
        EXIT                            ; FreeArea accumulates V

66
        DCB     "@", 0
        ALIGN

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Don't need to catch errors in here; we'll get 'em on the way out !

; In fact, no error must come from these!

Util_FreeFileStrings_R7 ENTRY "r1-r2"

 [ debugutil
 DLINE "Freeing r7 leaf,dir"
 ]
        MOV     r1, r7

10      LDR     r2, [r1, #copy_leafname]
        BL      SFreeArea

        LDR     r2, [r1, #copy_dirprefix]
        BL      SFreeArea
        EXITS

; .............................................................................

Util_FreeFileStrings_R8 ALTENTRY

 [ debugcopy ; Not used by wipe
 DLINE "Freeing r8 leaf,dir"
 ]
        MOV     r1, r8
        B       %BT10

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; Util_AppendLeafToDir
; ====================

; Create fullname in record given dirprefix and leafname

; In    r0 -> record

Util_AppendLeafToDir ENTRY "r1-r4"

 [ debugutil
 DLINE "Util_AppendLeafToDir"
 ]
        LDR     r1, [r0, #copy_leafname] ; Can't have null leafname

01      MOV     r4, r1                  ; Remember for below
        BL      strlen
 [ debugutil
 DSTRING r1,"leafname ",cc
 DREG r3,", nchars "
 ]

        LDR     r1, [r0, #copy_dirprefix] ; Can have null dirprefix
 [ debugutil
 DSTRING r1, "dirprefix ",cc
 CMP r1, #0
 BEQ %FT50
 Push r3
 BL strlen
 DREG r3,", nchars "
 Pull r3
 ]
        CMP     r1, #0
        BEQ     %FT50

        ADD     r2, r3, #2              ; '.' + \0
        BL      strlen
        ADD     r3, r3, r2              ; Allocate dir + leaf + sep + term
        BL      SMustGetArea
        STRVC   r2, [r0, #copy_name]
        MOVVC   r1, r2
        LDRVC   r2, [r0, #copy_dirprefix]
        BLVC    strcpy
        ADRVC   r2, %FT96
        BLVC    strcat
        MOVVC   r2, r4                  ; fujj for *copy fred.jim bert.*
        BLVC    strcat
 [ debugutil
 DSTRING r1,"Dir+Name = "
 ]
        EXIT

50 ; Null dirprefix, so just copy leafname as fullname

        ADD     r3, r3, #1              ; \0
        BL      SMustGetArea            ; Allocate leaf + 1
        STRVC   r2, [r0, #copy_name]
        MOVVC   r1, r2
        MOVVC   r2, r4
        BLVC    strcpy
 [ debugutil
 DSTRING r1,"<NullDir>+Name = "
 ]
        EXIT

96
        DCB     ".", 0
        ALIGN

; .............................................................................
; In    r0 -> record
;       r1 = leafname to append

Util_AppendGivenLeafToDir ALTENTRY

        B       %BT01

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; Util_PrintName
; ==============

; Print filename in the form 'filesystem#special:dirprefix.leafname'

; In    r0 -> record

; Out   VC: r0 corrupt
;       VS: r0 -> error block

Util_PrintName ENTRY "r1-r3"

        MOV     r1, r0                  ; Save record^ (saves STRVS r0,[sp])

        LDR     r2, [r1, #copy_special]
        LDRB    r3, [r2]                ; Is it null ?
        TEQ     r3, #0

        LDR     r0, [r1, #copy_fscb]    ; Print fs name if ~currentfs
        LDREQ   r14, currentfs          ; or special ~null
        CMPEQ   r14, r0
        BEQ     %FT60

        ADD     r0, r0, #fscb_name
        SWI     XOS_Write0
        EXIT    VS

        TEQ     r3, #0                  ; Optionally print special field
        BEQ     %FT50

        SWI     XOS_WriteI+"#"
        MOVVC   r0, r2
        SWIVC   XOS_Write0

50      SWIVC   XOS_WriteI+":"
        EXIT    VS

60      LDR     r0, [r1, #copy_name]    ; Rest of filename
        LDRB    r14, [r0]
        TEQ     r14, #"@"               ; Skip over '@.' if present
        LDREQB  r14, [r0, #1]
        TEQEQ   r14, #"."
        ADDEQ   r0, r0, #2
        SWI     XOS_Write0
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; Util_ReadDirEntry
; =================

; Read one name from directory specified by r7 record

; In    util_direntry -> dir entry to read

; Out   util_direntry -> next dir entry to read
;       r3 = number of names read. 0 if at eod
;       Info + name in util_load .. util_objectname

Util_ReadDirEntry ENTRY "r0-r2, r4-r6, fscb" ; FSFunc only preserves r6 up

        LDR     r4, util_direntry
 [ debugutil
 DREG r4, "Util_ReadDirEntry from dir entry ",cc,Integer
 ]
        TST     r4, #(1 :SHL: 31)       ; Already at end ? (-ve)
        MOVNE   r3, #0
        BNE     %FT50

        LDMIB   r7, {r6, fscb}          ; Read special + fscb, skipping name
        LDR     r1, [r7, #copy_dirprefix]
        CMP     r1, #0                  ; Might be zero
        ADREQ   r1, %FT66
 [ debugutil
 DSTRING r1,", dirprefix "
 ]
        MOV     r0, #fsfunc_ReadDirEntriesInfo
        ADR     r2, util_load
        MOV     r3, #1                  ; Just one at a time, please
        MOV     r5, #util_objblksize
        BL      CallFSFunc_Given
        EXIT    VS

50      CMP     r3, #0                  ; If no name read, must be at eod
        MOVEQ   r4, #-1
 [ debugutil
 DREG r3,,cc,Integer
 DREG r4, " name(s) read, next dir entry ",,Integer
 ]
        STR     r4, util_direntry
        EXIT
66
        DCB     "@", 0                  ; Use CSD if dirprefix zero
        ALIGN

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Get file info into util block as if it had been read using Util_ReadDirEntry

; Out   EQ: Object is a file
;       HI: Object is a directory
;       VS: fulkup or not found

Util_SingleObjectSetup ENTRY

        MOV     r0, #fsfile_ReadInfo
        BL      CallFSFile_Given_R7
        EXIT    VS

        CMP     r0, #object_file        ; VClear. Flags for result
        STRHS   r0, util_objecttype
        ADRHS   r14, fileblock_load     ; Copy all object info if file or dir
        LDMHSIA r14, {r2-r5}
        ADRHS   r14, util_load
        STMHSIA r14, {r2-r5}
        LDRLO   r1, [r7, #copy_name]
        BLLO    SetMagicFileNotFound    ; Else give error
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; In    r4 -> object name
;       r9 -> wild pattern

; Out   EQ -> matched

Util_TryMatch ENTRY

        MOV     r0, r9                  ; Source pattern
        BL      TryWildMatch            ; Corrupts r0,r4
        BLNE    Util_IncrementSkipped   ; EQ -> matched, so copy
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; Util_SpecialDir
; ===============

; In    r1 -> pathname to check

; Out   EQ: name is special
;       NE: or not as the case may be
;       VClear always

Util_SpecialDir ENTRY "r1"

        LDRB    r14, [r1]               ; Is it exactly 'x' where x = special ?
        CMP     r14, #"$"               ; Root ?
        CMPNE   r14, #"&"               ; URD ?
        CMPNE   r14, #"@"               ; CSD ?
        CMPNE   r14, #"%"               ; LIB ?
        CMPNE   r14, #"\"               ; PSD ?
        LDREQB  r14, [r1, #1]
        CMPEQ   r14, #0                 ; followed by terminator
        EXIT    EQ

        LDRB    r14, [r1], #1           ; Is it a drive spec ?
        CMP     r14, #":"
        EXIT    NE                      ; [not special]

10      LDRB    r14, [r1], #1
        CMP     r14, #0
        EXIT    EQ                      ; [yes, it's a drive spec]
        CMP     r14, #"."
        BNE     %BT10

        LDRB    r14, [r1]               ; Optional $ then terminator ?
        CMP     r14, #"$"
        LDREQB  r14, [r1, #1]
        CMPEQ   r14, #0
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; Util_GetConfirm
; ===============

; Ask for confirmation of action without killing buffer, may be done from exec

; A(bandon): Skip object and all others at this level
; Q(uiet): Do object, turn off confirm at this level and below
; Y(es): Do object
; Other: Skip object

; ESCAPE aborts from global operation and gives error

; Out   VS: r0 -> error block
;       VC, EQ: confirmed
;       VC, NE: not confirmed

Util_GetConfirm ENTRY "r0-r2"

        MOV     r0, #15                 ; Flush current input buffer
        MOV     r1, #1
        SWI     XOS_Byte

        SWI     XOS_WriteS
        DCB     " (Y/N/Quiet/Abandon) ? ", 0
        ALIGN
        SWIVC   XOS_Confirm
        BVS     %FT99
        BCS     %FT95
        UpperCase r0, r14

        CMP     r0, #"A"                ; Abort this level ?
        BNE     %FT10
        MOVS    r14, #-1                ; NE -> skip (and everything else)
        STR     r14, util_direntry
        B       %FT50

10      CMP     r0, #"Q"                ; Stop prompting and get on with it ?
        LDREQ   r14, util_bitset
        BICEQ   r14, r14, #util_confirm
        STREQ   r14, util_bitset
        BEQ     %FT50                   ; EQ -> do it

        CMP     r0, #"Y"                ; EQ -> do it
        MOVNE   r0, #"N"                ; NE -> skip (and say 'N' too)

50      SWI     XOS_WriteC              ; Tell him what he pressed
        SWIVC   XOS_NewLine             ; Flags fall out from compare if lucky

99      STRVS   r0, [sp]
        EXIT


95      SWI     XOS_WriteS
        DCB     "Aborted", LF,CR, 0
        ALIGN
        BL      SAckEscape
        B       %BT99

; .............................................................................
; A simpler version

Util_GetConfirmYN ALTENTRY

        MOV     r0, #15                 ; Flush current input buffer
        MOV     r1, #1
        SWI     XOS_Byte

        SWI     XOS_WriteS
        DCB     " (Y/N) ? ", 0
        ALIGN
        SWIVC   XOS_Confirm
        BVS     %BT99
        BCS     %BT95
        UpperCase r0, r14

        CMP     r0, #"Y"                ; EQ -> do it
        MOVNE   r0, #"N"                ; NE -> skip (and say 'N' too)

        SWI     XOS_WriteC              ; Tell him what he pressed
        SWIVC   XOS_NewLine             ; Flags fall out from compare if lucky
        B       %BT99                   ; Cheap exit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; Util_DateRange
; ==============

; Check file datestamp against limits (inclusive). Silly on directories !

; Out   EQ -> ok, do it (whatever it is you propose to do !)
;       NE -> don't do it

Util_DateRange ENTRY "r0-r5"

        LDR     r14, util_bitset        ; Ok if no datelimit set
        TST     r14, #util_datelimit
        EXIT    EQ                      ; Do it

        ADR     r14, util_load          ; Read load/exec
        LDMIA   r14, {r0, r1}           ; r0 = load, r1 = exec
        CMN     r0, #&00100000          ; Is it dated ? CSet if so
 [ debugutil
 BCS %FT00
 DLINE "source file undated"
00
 ]
        BCC     %FT20                   ; Always ok if not dated
        AND     r0, r0, #&FF
 [ debugutil
 DREG r0,"source timestamp is ",cc,Byte
 DREG r1
 ]

        ADR     r14, util_starttime     ; Read limits
        LDMIA   r14, {r2-r5}            ; r3, r5 already masked (ex/0ld/ex/0ld)

; NB. Date comparison must be done VERY CAREFULLY. Merci beaucoup to TMD

; SUBS   t1, dl0, dl1
; MOVNE  t1, #Z_bit
; SBCS   t2, dh0, dh1
; TEQEQP t1, pc       ; If equal here, set equality on that of low words

        SUBS    r5, r0, r5              ; After end date ?
        MOVNE   r5, #Z_bit              ; lo words first
        SBCS    r4, r1, r4              ; hi words next
        TEQEQP  r5, pc
 [ debugutil
 BLS %FT00
 DLINE "source newer than end date"
00
 ]
        EXIT    HI                      ; NE -> no go. Watch out if change HS

        SUBS    r3, r0, r3              ; After or equal to start date ?
        MOVNE   r3, #Z_bit              ; lo words first
        SBCS    r2, r1, r2              ; hi words next
        TEQEQP  r3, pc
 [ debugutil
 BLO %FT00
 DLINE "source date in range"
00
 ]
        BHS     %FT20

 [ debugutil
 DLINE "source older than first date"
 ]
        CMP     pc, #0                  ; NE -> no copy. VClear
        EXIT

20      CMP     r0, r0                  ; EQ -> copy. VClear
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Out   EQ: Not verbose
;       NE: Verbose

Util_IncrementFiles_TestVerbose ENTRY

        LDR     r14, util_nfiles        ; Another one bites the dust
        ADD     r14, r14, #1
        STR     r14, util_nfiles

50      LDR     r14, util_bitset
        TST     r14, #util_verbose
        EXIT

; .............................................................................

Util_IncrementDir_TestVerbose ALTENTRY

        LDR     r14, util_ndir          ; Another one bites the dust
        ADD     r14, r14, #1
        STR     r14, util_ndir

        B       %BT50

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Must preserve flags

Util_IncrementSkipped ENTRY

 [ debugutil
 DLINE "Skipping object"
 ]
        LDR     r14, util_nskipped
        ADD     r14, r14, #1
        STR     r14, util_nskipped
        EXITS

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Must preserve flags

Util_DecrementDirEntry ENTRY

        LDR     r14, util_direntry
 [ debugutil
 DREG r14, "Util_DecrementDirEntry from ",cc
 ]
        CMP     r14, #-1
        SUBNE   r14, r14, #1
        STRNE   r14, util_direntry
 [ debugutil
 DREG r14, " to "
 ]
        EXITS

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Out   r0 = result (if any)

CallFSFile_Given_R7 ENTRY "r1, r6, fscb"

        LDMIA   r7, {r1, r6, fscb}      ; copy_name, copy_special, copy_fscb
        BL      CallFSFile_Given
        EXIT

 ] ; hasutil

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Out   VC: Escape clear
;       VS: r0 -> error, Escape acknowledged

STestEscape ENTRY

        SWI     XOS_ReadEscapeState
        EXIT    CC

; .............................................................................
; Out   VS, r0 -> error, not copied

SAckEscape ENTRY

        MOV     r0, #&7E                ; Acknowledge ESCAPE
        SWI     XOS_Byte                ; May give error to override Escape
        BLVC    SetErrorEscape          ; Util grade, not copied
        EXIT


        LTORG

        LNK     $fileprefix.FSControl
