mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-29 20:49:19 +00:00
1015 lines
39 KiB
Plaintext
1015 lines
39 KiB
Plaintext
|
;
|
|||
|
; File: CatSearch.a
|
|||
|
;
|
|||
|
; Contains: High speed catalog search
|
|||
|
;
|
|||
|
; Written by: Dave N. Feldman
|
|||
|
;
|
|||
|
; Copyright: © 1988-1992 by Apple Computer, Inc., all rights reserved.
|
|||
|
;
|
|||
|
; Change History (most recent first):
|
|||
|
;
|
|||
|
; <SM1> 4/15/92 kc Removed the "ROM" prefix from the RomBind routines.
|
|||
|
; • Pre-SuperMario comments follow •
|
|||
|
; <19> 9/13/91 JSM Cleanup header.
|
|||
|
; <18> 2/25/91 dnf dho, #83584: Change the name of maskNotFileNames to
|
|||
|
; maskNotSearchInfo2, since that what it really does. Update the
|
|||
|
; comment in the #paramErr rules section to note the new bits
|
|||
|
; that count here.
|
|||
|
; <17> 2/12/91 dnf gbm, #breaksTheAUXExternalFileSystem: Make sure CatSearch
|
|||
|
; clears A3 (WDCB pointer) when exiting, since we never identify
|
|||
|
; working directories.
|
|||
|
; <16> 11/28/90 dnf Put a comment in front of <14>
|
|||
|
; <15> 11/28/90 dnf Put a comment in front of <14>
|
|||
|
; <14> 11/27/90 dnf (with dba) Fix use of fsSBNegate when ioFlAttrib specifies only
|
|||
|
; files or directories.
|
|||
|
; <13> 11/6/90 dnf (with dba) Don't crush the timerInstalled flag. Make sure that
|
|||
|
; long string target names (>31 characters) don't get searched.
|
|||
|
; <12> 10/30/90 dnf (with dba) Start the timer way early for more accurate timing.
|
|||
|
; Fix early error exit cases where a3 wasn't set up.
|
|||
|
; <11> 8/28/90 dnf Rename ioSpecBits, ioSpec1 and ioSpec2 to ioSearchBits,
|
|||
|
; ioSearchInfo1 and ioSearchInfo2
|
|||
|
; <10> 7/30/90 dnf Convert to linked patch; change rom reference names and use
|
|||
|
; jsrRom macro.
|
|||
|
; <9> 7/2/90 dnf Fix typo in branch from FlXFndrInfoIsDir case.
|
|||
|
; <8> 6/2/90 dnf Use proper offsets for both file and directory search cases on
|
|||
|
; finder info and dates
|
|||
|
; <7> 4/17/90 dnf Fix register trashing bug in StartTimer
|
|||
|
; <6> 3/16/90 dnf Return FSSpecs in ioMatchPtr instead of MBufEntries
|
|||
|
; <5> 2/26/90 dnf Change ioQuant from a catalog record count to an amount of time
|
|||
|
; to let the call run (and rename it ioSearchTime)
|
|||
|
; <4> 2/8/90 dnf Test d0 before exiting CheckCriteria
|
|||
|
; <3> 2/4/90 dnf Added negate bit; fixed ioFlAttrib bug; Changed to include
|
|||
|
; StandardEqu.d
|
|||
|
; <2> 1/9/90 dnf Add support for search on parent ID, implement CheckCSPB
|
|||
|
; routine.
|
|||
|
; <1.5> 9/7/89 dnf Added support for CatPosition
|
|||
|
; <1.4> 8/26/89 dnf Get minimum buffer off of stack
|
|||
|
; <1.3> 7/31/89 dnf Bug fixes, input from code review
|
|||
|
; <1.2> 7/6/89 dnf Added read buffer support and offline check
|
|||
|
; <1.1> 6/1/89 GGD Added include of HFS70Equ.a so that it would assemble.
|
|||
|
; <1.0> 5/30/89 dnf Integrate CatSearch, FileID's and Desktop Database Mgr into one
|
|||
|
; ptch
|
|||
|
; 1/23/89 dnf Modified for new param block and search criteria
|
|||
|
; 12/20/88 dnf Broke off BTPScan into separate file
|
|||
|
; 11/2/88 dnf New Today.
|
|||
|
;
|
|||
|
; External
|
|||
|
; Routine: CMCatSearch - Searches catalog for files according to search criteria
|
|||
|
;
|
|||
|
; Internal GetReadBuffer - Allocates a read buffer
|
|||
|
; Subroutines: MbufInsert - Inserts a match record into match buffer
|
|||
|
; UpperCaseIt - Convert a CName to its canonical form for comparison
|
|||
|
; CompareMasked - Masked bitwise comparison of a range of bytes
|
|||
|
; CompareRange - See if a value falls within a specified range
|
|||
|
; CheckCriteria - Checks current CNode against all specified search criteria
|
|||
|
;
|
|||
|
; TimerRoutine - timer task completion routine
|
|||
|
; StartTimer - installs a timer task
|
|||
|
; StopTimer - removes a timer task
|
|||
|
;
|
|||
|
|
|||
|
|
|||
|
print push
|
|||
|
PRINT OFF
|
|||
|
LOAD 'StandardEqu.d'
|
|||
|
INCLUDE 'CatSrchPriv.a'
|
|||
|
INCLUDE 'BTScanPriv.a' ; for the NoMoreReads bit
|
|||
|
include 'LinkedPatchMacros.a'
|
|||
|
include 'FileMgrPrivate.a'
|
|||
|
PRINT pop
|
|||
|
|
|||
|
import BTIPScan, BTEndPScan, BTGetPhys
|
|||
|
|
|||
|
;__________________________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: GetReadBuffer
|
|||
|
;
|
|||
|
; Function: Allocates a read buffer
|
|||
|
;
|
|||
|
; Input: A3.L - CatSearch state record pointer
|
|||
|
; A5.L - user pb pointer
|
|||
|
;
|
|||
|
; Output: A1.L - address of buffer
|
|||
|
; D3.L - length (in bytes) of buffer
|
|||
|
;
|
|||
|
; The deal - If the ioRBufPtr field in the param block points to a suitable buffer
|
|||
|
; that buffer will be used in the call. If not, CatSearch will get a small (512-byte or so)
|
|||
|
; buffer off the A6 stack. A buffer is suitable if it is bigger than the one CatSearch
|
|||
|
; would get off the stack.
|
|||
|
;__________________________________________________________________________________
|
|||
|
|
|||
|
GetReadBuffer: proc
|
|||
|
import bufAddr, bufLength
|
|||
|
MOVE.L ioOptBuffer(A5), D3 ; get the user's buffer address <1.2>
|
|||
|
BEQ.S @getOurOwn ; nil? Use our own buffer then <1.2>
|
|||
|
MOVE.L ioOptBufSize(A5), D3 ; get the user's buffer length <1.2>
|
|||
|
CMP.L #CSMinBufferSize, D3 ; bigger than ours? <1.2>
|
|||
|
BLE.S @getOurOwn ; if not, we'll use ours <1.2>
|
|||
|
|
|||
|
; the user's buffer is cool; go ahead and use it <1.2>
|
|||
|
MOVE.L ioOptBuffer(A5), A1 ; move buffer address to output register <1.2>
|
|||
|
BRA.S @GRBExit ; <1.2>
|
|||
|
|
|||
|
; the user's buffer is uncool. Get our own off the A6 stack. <1.4>
|
|||
|
@getOurOwn: ; <1.2>
|
|||
|
MOVE.L #CSMinBufferSize, D3 ; length of read buffer <1.4>
|
|||
|
SUBA.L D3, A6 ; get memory off the stack <1.4>
|
|||
|
MOVEA.L A6, A1 ; address of read buffer <1.4>
|
|||
|
BSET.B #bufferOnStack, CSSR.flags(A3) ; remember so we can deallocate <1.4>
|
|||
|
@GRBExit:
|
|||
|
RTS
|
|||
|
_CSDebug 'GetReadBuffer', 0
|
|||
|
endproc
|
|||
|
|
|||
|
|
|||
|
;
|
|||
|
; How the timer works here:
|
|||
|
;
|
|||
|
; Before reading any data from the catalog, we start a timer running.
|
|||
|
; When the timer expires the timer completion routine will set the timerFired bit
|
|||
|
; in the CatSearch state record. This is passed along to the BTree scanner in the
|
|||
|
; "noMoreReads" bit, telling the scanner to continue to deliver records until it
|
|||
|
; needs to hit the disk again.
|
|||
|
;
|
|||
|
; The effect is that CatSearch will process records until the timer goes off,
|
|||
|
; at which point it will finish the records it's got in memory and exit
|
|||
|
;
|
|||
|
|
|||
|
;__________________________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: TimerRoutine
|
|||
|
;
|
|||
|
; Function: Gets run when the timer goes off
|
|||
|
; Sets the "noMoreReads" bit in the PScan flags
|
|||
|
;
|
|||
|
; Input: A1 - csTimeTask record (a.k.a CatSearch state record pointer)
|
|||
|
;
|
|||
|
; Output: A1 - trashed
|
|||
|
;
|
|||
|
; Note that since csTimeTask is first in the CSSR, a1 can be used like a pointer
|
|||
|
; to the CSSR.
|
|||
|
;__________________________________________________________________________________
|
|||
|
|
|||
|
TimerRoutine: proc
|
|||
|
bset.b #timerFired, CSSR.flags(a1) ; boom! <12>
|
|||
|
rts
|
|||
|
endproc
|
|||
|
|
|||
|
;__________________________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: StartTimer
|
|||
|
;
|
|||
|
; Function: Checks to see if we need to run a timer during this search
|
|||
|
; If so, starts the timer
|
|||
|
;
|
|||
|
; Input: A3.L - CatSearch state record pointer
|
|||
|
; D0.L - Time count (time manager form) or zero for no timer
|
|||
|
;
|
|||
|
; Output: A3.L - CatSearch state record pointer
|
|||
|
;
|
|||
|
; If the time count is zero, no timer task is started. If a timer task is started
|
|||
|
; the "timerWasInstalled" bit is set in CSSR.flags.
|
|||
|
;__________________________________________________________________________________
|
|||
|
|
|||
|
StartTimerRegs reg d0-d1/a0-a1
|
|||
|
|
|||
|
StartTimer: proc
|
|||
|
tst.l d0 ; do we need to start a timer?
|
|||
|
beq.s @StartTimerExit ; if count = 0, no.
|
|||
|
|
|||
|
movem.l StartTimerRegs, -(sp)
|
|||
|
move.l d0, d1 ; save time value around _InsTime
|
|||
|
bset.b #timerWasInstalled, CSSR.flags(a3) ; note that we're doing this
|
|||
|
move.l a3, a0 ; a0 = ptr (our time task record)
|
|||
|
lea.l TimerRoutine, a1 ; a1 = ptr(our timer completion routine)
|
|||
|
move.l a1, tmAddr(a0) ; point to our completion routine
|
|||
|
_InsTime ; put our task in the queue
|
|||
|
move.l d1, d0 ; restore time value
|
|||
|
_PrimeTime ; start the timer ticking
|
|||
|
movem.l (sp)+, StartTimerRegs
|
|||
|
|
|||
|
@StartTimerExit:
|
|||
|
rts
|
|||
|
endproc
|
|||
|
|
|||
|
;__________________________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: StopTimer
|
|||
|
;
|
|||
|
; Function: Checks to see if we installed a time manager task
|
|||
|
; If so, removes it.
|
|||
|
;
|
|||
|
; Input: A3.L - CatSearch state record pointer
|
|||
|
;
|
|||
|
; Output: A3.L - CatSearch state record pointer
|
|||
|
;
|
|||
|
;__________________________________________________________________________________
|
|||
|
|
|||
|
StopTimerRegs reg d0/a0
|
|||
|
StopTimer: proc
|
|||
|
|
|||
|
btst.b #timerWasInstalled, CSSR.flags(a3) ; did we use a timer this search?
|
|||
|
beq.s @noTimerUsed
|
|||
|
|
|||
|
movem.l StopTimerRegs, -(sp)
|
|||
|
move.l a3, a0 ; a0 = ptr (our time task record)
|
|||
|
_RmvTime ; remove the task from the queue
|
|||
|
movem.l (sp)+, StopTimerRegs
|
|||
|
|
|||
|
@noTimerUsed:
|
|||
|
rts
|
|||
|
endproc
|
|||
|
|
|||
|
;__________________________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: MBufInsert
|
|||
|
;
|
|||
|
; Function: Convert the current record into an FSSpec and insert it
|
|||
|
; into the Match Buffer
|
|||
|
; Increments nextMBuf pointer in CSSR
|
|||
|
;
|
|||
|
; Input: A0.L - ptr(catalog key)
|
|||
|
; A3.L - ptr(CatSearch state record)
|
|||
|
;
|
|||
|
; Output:
|
|||
|
; A0.L - trash
|
|||
|
; A1.L - trash
|
|||
|
; D0.L - trash
|
|||
|
;__________________________________________________________________________________
|
|||
|
MBufInsert: proc
|
|||
|
MOVE.L CSSR.PSRPtr(A3), A1 ; A1 = ptr(Pscan state record)
|
|||
|
MOVE.L PSR.fcbptr(A1), A1 ; A1 = ptr(fcb of btree file we're scanning)
|
|||
|
MOVE.L fcbVPtr(A1), A1 ; A1 = ptr(vcb of search volume)
|
|||
|
MOVE.W vcbVRefNum(A1), D0 ; D0 = vRefNum of search volume
|
|||
|
MOVE.L CSSR.nextMBuf(A3), A1 ; A1 = ptr(next empty MBuf entry)
|
|||
|
MOVE.W D0, (A1)+ ; copy vRefNum into FSSpec
|
|||
|
MOVE.L ckrParID(A0), (A1)+ ; copy ParID from key
|
|||
|
MOVEQ.L #0, D0 ; clear D0 to clear high bytes
|
|||
|
LEA.L ckrCName(A0), A0 ; A0 = ptr(str31 containing CName)
|
|||
|
MOVE.B (A0), D0 ; get length of CName
|
|||
|
; don't add one for the length byte 'cause we're DBRAing <1.3>
|
|||
|
@MoveCName:
|
|||
|
MOVE.B (A0)+, (A1)+ ; move the CName, byte by byte
|
|||
|
DBRA D0, @MoveCName ; (this is still faster than _BlockMove)
|
|||
|
MOVE.L CSSR.nextMBuf(A3), A1 ; A1 = ptr(just filled MBuf entry)
|
|||
|
ADDA.W #FSSpec.size, A1 ; A1 = ptr(MBuf entry after the one we just filled)
|
|||
|
MOVE.L A1, CSSR.nextMBuf(A3) ; remember position for next time
|
|||
|
RTS
|
|||
|
_CSDebug 'MBufInsert', 0
|
|||
|
|
|||
|
; _AssumeEQ ckrParID+4, ckrCName ; put this in. ••
|
|||
|
endproc
|
|||
|
|
|||
|
;__________________________________________________________________________________
|
|||
|
;
|
|||
|
; UpperCaseIt
|
|||
|
;
|
|||
|
; Function: Convert the current cName (from the key to its catalog record)
|
|||
|
; into a canonical form for comparison. Store the result in the CatSearch
|
|||
|
; state record. Zero flag up if current cName has length of zero.
|
|||
|
;
|
|||
|
; Register
|
|||
|
; Usage: A0.L - ptr(key)
|
|||
|
; A3.L - ptr(CatSearch state record)
|
|||
|
; A4.L, A5.L - scratch
|
|||
|
;
|
|||
|
; D5.L - ioSearchBits
|
|||
|
;
|
|||
|
; Output: zero flag high if current cName is empty
|
|||
|
; D0.L - D4.L are trashed
|
|||
|
;__________________________________________________________________________________
|
|||
|
UpperCaseIt: proc
|
|||
|
MOVE.L A0, D2 ; save address regs into trashable D regs
|
|||
|
MOVE.L A4, D3
|
|||
|
MOVE.L A5, D4
|
|||
|
MOVEQ.L #0, D0 ; clear D0 to clear byte 1 (for DBRA)
|
|||
|
MOVE.B ckrCName(A0), D0 ; D0.W = len(current cName)
|
|||
|
BEQ.S @failUCI ; null cNames fail
|
|||
|
MOVE.W D0, D1 ; keep another copy in D1
|
|||
|
|
|||
|
; Don't add one for the length byte, 'cause we're DBRAing
|
|||
|
LEA.L ckrCName(A0), A4 ; A4 = ptr(current cName)
|
|||
|
LEA.L CSSR.copyCur(A3), A5 ; A5 = ptr(copy of current cName)
|
|||
|
@block
|
|||
|
MOVE.B (A4)+, (A5)+ ; move a byte
|
|||
|
DBRA D0, @block ; until we're done
|
|||
|
|
|||
|
MOVE.W D1, D0 ; D0.W = len(copy of current cName)
|
|||
|
LEA.L CSSR.copyCur+1(A3), A0 ; A0 = ptr(1st char of copy of cName)
|
|||
|
_FSUprString ; uppercase, strip diacriticals
|
|||
|
MOVEQ.L #1, D0 ; just something to clear the zero flag
|
|||
|
|
|||
|
; Zero flag is cool regardless of how we got here
|
|||
|
@failUCI
|
|||
|
MOVEA.L D2, A0 ; restore A regs from D regs
|
|||
|
MOVEA.L D3, A4
|
|||
|
MOVEA.L D4, A5
|
|||
|
RTS
|
|||
|
_CSDebug 'UpperCaseIt', 0
|
|||
|
endproc
|
|||
|
|
|||
|
;__________________________________________________________________________________
|
|||
|
;
|
|||
|
; CompareMasked
|
|||
|
;
|
|||
|
; Function: Do a bitwise comparison with mask. The source and data are exclusive-or'ed
|
|||
|
; together, yielding 1's where they differ. That value is and'ed with the
|
|||
|
; comparison mask, leaving a word with 1's where there are significant
|
|||
|
; mismatches. This technique allows the caller to set target bits only where
|
|||
|
; the mask bits are 1, and leaving the rest undefined. If all long words
|
|||
|
; pass, then the zero flag is set on return.
|
|||
|
;
|
|||
|
; Register
|
|||
|
; Usage: A0.L - ptr(source data) (what you're checking out)
|
|||
|
; A4.L - ptr(target data) (what you're looking for)
|
|||
|
; A5.L - ptr(comparison mask) (what bits you care about)
|
|||
|
;
|
|||
|
; D0.W - comparison count - 1 (note: we step by longwords)
|
|||
|
; D1.L - trashed
|
|||
|
; D2.L - trashed
|
|||
|
;
|
|||
|
; Output: zero flag high if hit
|
|||
|
;
|
|||
|
; Note: This routine steps on the register usage of CheckCriteria by using A0 as an input.
|
|||
|
;__________________________________________________________________________________
|
|||
|
CompareMasked: proc
|
|||
|
MOVE.L (A0)+, D1 ; D1 = long word of source data
|
|||
|
MOVE.L (A4)+, D2 ; D2 = long of target data
|
|||
|
EOR.L D2, D1 ; D1 bits are on where source and data differ
|
|||
|
AND.L (A5)+, D1 ; D1 bits are on where we cared about mismatches
|
|||
|
DBNE D0, CompareMasked ; keep going as long as the bytes match
|
|||
|
RTS
|
|||
|
_CSDebug 'CompareMasked', 0
|
|||
|
endproc
|
|||
|
|
|||
|
;__________________________________________________________________________________
|
|||
|
;
|
|||
|
; CompareRange
|
|||
|
;
|
|||
|
; Function: Compare to see if ioSearchInfo1[D1].L <= D0.L <= ioSearchInfo2[D1].L
|
|||
|
; Return carry clear if yes, carry set if not.
|
|||
|
;
|
|||
|
; Register
|
|||
|
; Usage: A0.L - ptr(ioSearchInfo1) (low end of range)
|
|||
|
; A2.L - ptr(ioSearchInfo2) (high end of range)
|
|||
|
;
|
|||
|
; D0.L - current value
|
|||
|
; D1.W - offset in CInfoPBRec to use for value
|
|||
|
;
|
|||
|
; Output: zero flag high if D0 is in range, low if not
|
|||
|
;
|
|||
|
;__________________________________________________________________________________
|
|||
|
|
|||
|
CompareRange: proc
|
|||
|
CMP.L (A0, D1.W), D0 ; is D0 >= low end of range?
|
|||
|
BLO.S @failRange ; if (D0 < value in ioSearchInfo1), fail
|
|||
|
CMP.L (A2, D1.W), D0 ; is D0 <= high end of range?
|
|||
|
BLS.S @succeedRange ; if (D0 <= value in ioSearchInfo2), succeed
|
|||
|
@failRange:
|
|||
|
MOVEQ.L #1, D0 ; clear the zero flag
|
|||
|
RTS
|
|||
|
@succeedRange:
|
|||
|
MOVEQ.L #0, D0 ; set the zero flag
|
|||
|
RTS
|
|||
|
_CSDebug 'CompareRange', 0
|
|||
|
endproc
|
|||
|
|
|||
|
;__________________________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: CheckCriteria
|
|||
|
;
|
|||
|
; Function: Checks current record against match criteria.
|
|||
|
; Partial and full name matches are the optimized cases
|
|||
|
;
|
|||
|
; Input: A0.L - ptr(key)
|
|||
|
; A1.L - ptr(data record)
|
|||
|
; A3.L - ptr(CatSearch state record)
|
|||
|
; A5.L - ptr(user parameter block)
|
|||
|
; D5.L - ioSearchBits
|
|||
|
;
|
|||
|
; Output: A0.L - ptr(key)
|
|||
|
; A1.L, A2.L - trash
|
|||
|
; A3.L, A4.L, A5.L - preserved
|
|||
|
; D0, D1, D2, D3, D4 - trash
|
|||
|
; D5, D6, D7 - preserved
|
|||
|
; zero flag set if match, clear otherwise
|
|||
|
;__________________________________________________________________________________
|
|||
|
CheckCriteria: proc
|
|||
|
MOVE.L A0, -(A6)
|
|||
|
|
|||
|
CMP.B #cmFilCN, cdrType(A1) ; is this a file record? <14>
|
|||
|
BEQ.S @IsFile ; <14>
|
|||
|
CMP.B #cmDirCN, cdrType(A1) ; is this a directory record? <14>
|
|||
|
BNE @notSearchable ; we only consider files and directories <14>
|
|||
|
BCLR.B #isFile, CSSR.flags(A3) ; indicate that current record is a dir <14>
|
|||
|
BTST.B #inclDirs, CSSR.flags(A3) ; do criteria include directories? <14>
|
|||
|
BEQ @failed ; if not, we're done already <14>
|
|||
|
BRA.S @LookAtIt ; if so, continue with criteria <14>
|
|||
|
|
|||
|
@IsFile:
|
|||
|
BSET.B #isFile, CSSR.flags(A3) ; indicate that current record is a file
|
|||
|
BTST.B #inclFiles, CSSR.flags(A3) ; do criteria include files?
|
|||
|
BEQ @failed ; if not, we're done already
|
|||
|
|
|||
|
@LookAtIt:
|
|||
|
BTST.L #fsSBPartialName, D5 ; is name substring included?
|
|||
|
BEQ.S @FullName ; go check the next thing (Exact cName match)
|
|||
|
|
|||
|
; This is the 'name substring' match
|
|||
|
BSR UpperCaseIt ; make an uppercase copy of the current cName
|
|||
|
BEQ @failed ; zero flag indicates current cName is empty
|
|||
|
|
|||
|
; Register use during substring compare
|
|||
|
; A0 - ptr(current cName) D1 - starting point limit in current cName
|
|||
|
; A1 - ptr(substring) D2 - length of substring
|
|||
|
; A2 - scratch for DBRA D3 - next starting point in current cName
|
|||
|
; A3 - scratch for DBRA D4 - scratch for DBNE
|
|||
|
|
|||
|
MOVEM.L A0/A1/A3, -(A6) ; save regs around string compare loop
|
|||
|
LEA.L CSSR.copyCur(A3), A0 ; A0 = ptr(current cName)
|
|||
|
LEA.L CSSR.copyTarg(A3), A1 ; A1 = ptr(target substring)
|
|||
|
MOVE.B (A0), D1 ; D1 = len(current cName)
|
|||
|
MOVEQ.L #0, D2 ; make D2 clean for DBRA
|
|||
|
MOVE.B (A1)+, D2 ; D2 = len(target substring) (A1 = ptr(1st char))
|
|||
|
SUB.B D2, D1 ; D1 = -1 + last possible starting point for a match
|
|||
|
BMI.S @failPartial ; if that's < 0, we couldn't possibly match
|
|||
|
|
|||
|
SUBQ.L #1, D2 ; adjust D2 for DBRA
|
|||
|
ADDQ.B #1, D1 ; D1 = last possible starting point for a match
|
|||
|
MOVEQ.L #1, D3 ; initialize "next starting point" value (past length byte)
|
|||
|
|
|||
|
@outer:
|
|||
|
LEA.L (A0, D3), A2 ; A2 = ptr(place to look for match)
|
|||
|
MOVEA.L A1, A3 ; A3 = ptr(1st char in substring)
|
|||
|
MOVE.W D2, D4 ; D4 counts the DBRA
|
|||
|
@loop:
|
|||
|
CMPM.B (A2)+, (A3)+ ; compare char by char
|
|||
|
DBNE D4, @loop ; and keep going as long as they're the same
|
|||
|
BEQ.S @hitPartial ; all the way without a miss = a hit
|
|||
|
|
|||
|
@again:
|
|||
|
ADDQ.B #1, D3 ; up the starting point
|
|||
|
CMP.B D1, D3 ; is starting point beyond last possible start?
|
|||
|
BLS.S @outer ; as long as it's not, check another starting point
|
|||
|
|
|||
|
; if we fell through to here, we missed on all starting points
|
|||
|
@failPartial
|
|||
|
MOVEM.L (A6)+, A0/A1/A3 ; restore regs saved around substring compare
|
|||
|
BRA @failed
|
|||
|
|
|||
|
@hitPartial
|
|||
|
MOVEM.L (A6)+, A0/A1/A3 ; restore regs saved around substring compare
|
|||
|
MOVEQ.L #maskPartialName, D0 ; D0 = ioSearchBits long w/fsSBPartialName set
|
|||
|
CMP.L D0, D5 ; was PartialName the only criteria?
|
|||
|
BEQ @hit ; then leave now
|
|||
|
|
|||
|
@FullName: ; This is the 'full cName' match
|
|||
|
BTST.L #fsSBFullName, D5 ; is full name included?
|
|||
|
BEQ.S @FlParID ; go check the next thing (file parent)
|
|||
|
|
|||
|
BSR UpperCaseIt ; make an uppercase copy of the current cName
|
|||
|
MOVE.L A4, D1 ; save A4 around string compare
|
|||
|
LEA.L CSSR.copyCur(A3), A2 ; A2 = ptr(current cName)
|
|||
|
LEA.L CSSR.copyTarg(A3), A4 ; A4 = ptr(target string)
|
|||
|
MOVE.B (A2), D0 ; D0 = length of current CName (+ length byte for DBRA)
|
|||
|
@FullNameLoop:
|
|||
|
CMPM.B (A2)+, (A4)+ ; compare a byte
|
|||
|
DBNE D0, @FullNameLoop
|
|||
|
BNE.S @failedFull ; did we end on a missed character?
|
|||
|
|
|||
|
; If we fell through to here, we hit
|
|||
|
MOVEA.L D1, A4 ; restore reg saved around string compare
|
|||
|
MOVEQ.L #maskFullName, D0 ; D0 = ioSearchBits long w/fsSBFullName set
|
|||
|
CMP.L D0, D5 ; was Fullname the only criteria?
|
|||
|
BEQ @hit ; then leave now
|
|||
|
BRA.S @FlParID ; or else keep going with the next criteria
|
|||
|
|
|||
|
@failedFull:
|
|||
|
MOVEA.L D1, A4 ; restore reg saved around string compare
|
|||
|
BRA @failed
|
|||
|
|
|||
|
@FlParID: ; This is the parent ID check
|
|||
|
MOVE.L ckrParID(A0), D0 ; D0 = parent ID of current cNode (grabbed before we change A0)
|
|||
|
|
|||
|
; from here down,
|
|||
|
; A0 - points to ioSearchInfo1
|
|||
|
; A2 - points to ioSearchInfo2
|
|||
|
MOVEA.L ioSearchInfo1(A5), A0 ; A0 = ptr(CInfoPBRec with values & low end of ranges)
|
|||
|
MOVEA.L ioSearchInfo2(A5), A2 ; A2 = ptr(CInfoPBRec with masks & high end of ranges)
|
|||
|
|
|||
|
BTST.L #fsSBFlParID, D5 ; is parent ID included?
|
|||
|
BEQ.S @FlAttrib ; go check the next thing (file attributes)
|
|||
|
MOVEQ.L #ioFlParID, D1 ; D1 = offset into CInfoPBRec to parent ID
|
|||
|
BSR CompareRange
|
|||
|
BNE @failed
|
|||
|
|
|||
|
@FlAttrib: ; This is the file attributes check
|
|||
|
BTST.L #fsSBFlAttrib, D5 ; are file attributes included?
|
|||
|
BEQ.S @DrNmFls ; go check the next thing (files/directory)
|
|||
|
MOVE.B filFlags(A1), D0 ; D0 = file flags (bit 0 = locked is the only one)
|
|||
|
MOVE.B ioFlAttrib(A0), D1 ; D1 = user's target for file attrib bits
|
|||
|
EOR.B D1, D0 ; D0 bits on where source and target differ
|
|||
|
AND.B CSSR.attribMask(A3), D0 ; D0 bits on where we cared about mismatches
|
|||
|
BNE @failed
|
|||
|
|
|||
|
@DrNmFls:
|
|||
|
BTST.L #fsSBDrNmFls, D5 ; is # files per directory included?
|
|||
|
BEQ.S @FlFndrInfo ; go check the next thing (finder info)
|
|||
|
MOVE.W dirVal(A1), D0 ; D0 = # files in current directory
|
|||
|
CMP.W ioDrNmFls(A0), D0 ; is D0 >= low end of range?
|
|||
|
BCS @failed ; if (D0 < # files in this directory), fail
|
|||
|
CMP.W ioDrNmFls(A2), D0 ; is D0 <= high end of range?
|
|||
|
BLS.S @FlFndrInfo ; if (D0 <= # files), continue with next criteria
|
|||
|
BRA @failed ; D0 too high; fail
|
|||
|
|
|||
|
@FlFndrInfo:
|
|||
|
BTST.L #fsSBFlFndrInfo, D5 ; is finder info included?
|
|||
|
BEQ.S @FlXFndrInfo ; go check the next thing (extra finder info)
|
|||
|
MOVEM.L A0/A4/A5, -(A6) ; save A0/A4/A5 around test
|
|||
|
MOVEQ.L #(FlFndrInfoLen/4-1), D0; D0 = DBRA count for # of finder info bytes
|
|||
|
LEA.L ioFlUsrWds(A0), A4 ; A4 = ptr(target user words)
|
|||
|
LEA.L ioFlUsrWds(A2), A5 ; A5 = ptr(mask for user words)
|
|||
|
BTST.B #isFile, CSSR.flags(A3) ; are we looking at a file record?
|
|||
|
BEQ.S @FlFndrInfoIsDir ; if not, go get the right offset for a directory
|
|||
|
LEA.L filUsrWds(A1), A0 ; A0 = ptr(source data)
|
|||
|
BRA.S @1
|
|||
|
@FlFndrInfoIsDir:
|
|||
|
LEA.L dirUsrInfo(A1), A0 ; A0 = ptr(source data)
|
|||
|
@1:
|
|||
|
BSR CompareMasked ; see if they match
|
|||
|
MOVEM.L (A6)+, A0/A4/A5 ; restore A0/A4/A5
|
|||
|
BNE @failed
|
|||
|
|
|||
|
@FlXFndrInfo:
|
|||
|
BTST.L #fsSBFlXFndrInfo, D5 ; is extra finder info included?
|
|||
|
BEQ.S @FlLgLen ; go check the next thing (file logical length)
|
|||
|
MOVEM.L A0/A4/A5, -(A6) ; save A0/A4/A5 around test
|
|||
|
MOVEQ.L #(FlXFndrInfoLen/4-1), D0; D0 = DBRA count for # of Xfinder info bytes
|
|||
|
LEA.L ioFlxFndrInfo(A0), A4 ; A4 = ptr(target Xfinder info)
|
|||
|
LEA.L ioFlxFndrInfo(A2), A5 ; A5 = ptr(mask Xfinder words)
|
|||
|
BTST.B #isFile, CSSR.flags(A3) ; are we looking at a file record?
|
|||
|
BEQ.S @FlXFndrInfoIsDir ; if not, go get the right offset for a directory
|
|||
|
LEA.L filFndrInfo(A1), A0 ; A0 = ptr(source data)
|
|||
|
BRA.S @2
|
|||
|
@FlXFndrInfoIsDir:
|
|||
|
LEA.L dirFndrInfo(A1), A0 ; A0 = ptr(source data)
|
|||
|
@2:
|
|||
|
BSR CompareMasked ; see if they match
|
|||
|
MOVEM.L (A6)+, A0/A4/A5 ; restore A0/A4/A5
|
|||
|
BNE @failed
|
|||
|
|
|||
|
@FlLgLen:
|
|||
|
BTST.L #fsSBFlLgLen, D5 ; is logical length included?
|
|||
|
BEQ.S @FlPyLen ; go check the next thing (physical length)
|
|||
|
MOVE.L filLgLen(A1), D0 ; D0 = logical length of current file
|
|||
|
MOVEQ.L #ioFlLgLen, D1 ; D1 = offset into CInfoPBRec to find logical length
|
|||
|
BSR CompareRange
|
|||
|
BNE @failed
|
|||
|
|
|||
|
@FlPyLen:
|
|||
|
BTST.L #fsSBFlPyLen, D5 ; is physical length included?
|
|||
|
BEQ.S @FlRLgLen ; go check the next thing (resource logical length)
|
|||
|
MOVE.L filPyLen(A1), D0 ; D0 = physical length of current file
|
|||
|
MOVEQ.L #ioFlPyLen, D1 ; D1 = offset into CInfoPBRec to find physical length
|
|||
|
BSR CompareRange
|
|||
|
BNE @failed
|
|||
|
|
|||
|
@FlRLgLen:
|
|||
|
BTST.L #fsSBFlRLgLen, D5 ; is resource logical length included?
|
|||
|
BEQ.S @FlRPyLen ; go check the next thing (resource physical length)
|
|||
|
MOVE.L filRLgLen(A1), D0 ; D0 = resource logical length of current file
|
|||
|
MOVEQ.L #ioFlRLgLen, D1 ; D1 = offset into CInfoPBRec to find resource logical length
|
|||
|
BSR CompareRange
|
|||
|
BNE.S @failed
|
|||
|
|
|||
|
@FlRPyLen:
|
|||
|
BTST.L #fsSBFlRPyLen, D5 ; is resource physical length included?
|
|||
|
BEQ.S @FlCrDat ; go check the next thing (file create date)
|
|||
|
MOVE.L filRPyLen(A1), D0 ; D0 = resource physical length of current file
|
|||
|
MOVEQ.L #ioFlRPyLen, D1 ; D1 = offset into CInfoPBRec to find resource physical length
|
|||
|
BSR CompareRange
|
|||
|
BNE.S @failed
|
|||
|
|
|||
|
@FlCrDat:
|
|||
|
BTST.L #fsSBFlCrDat, D5 ; is creation date included?
|
|||
|
BEQ.S @FlMdDat ; go check the next thing (file modification date)
|
|||
|
BTST.B #isFile, CSSR.flags(A3) ; are we looking at a file record?
|
|||
|
BEQ.S @FlCrDatIsDir ; if not, go get the right offset for a directory
|
|||
|
MOVE.L filCrDat(A1), D0 ; D0 = creation date of current cNode
|
|||
|
BRA.S @3
|
|||
|
@FlCrDatIsDir:
|
|||
|
MOVE.L dirCrDat(A1), D0 ; D0 = creation date of current cNode
|
|||
|
@3:
|
|||
|
MOVEQ.L #ioFlCrDat, D1 ; D1 = offset into CInfoPBRec to find creation date
|
|||
|
BSR CompareRange
|
|||
|
BNE.S @failed
|
|||
|
|
|||
|
@FlMdDat:
|
|||
|
BTST.L #fsSBFlMdDat, D5 ; is modification date included?
|
|||
|
BEQ.S @FlBkDat ; go check the next thing (file backup date)
|
|||
|
BTST.B #isFile, CSSR.flags(A3) ; are we looking at a file record?
|
|||
|
BEQ.S @FlMdDatIsDir ; if not, go get the right offset for a directory
|
|||
|
MOVE.L filMdDat(A1), D0 ; D0 = modification date of current cNode
|
|||
|
BRA.S @4
|
|||
|
@FlMdDatIsDir:
|
|||
|
MOVE.L dirMdDat(A1), D0 ; D0 = modification date of the current cNode
|
|||
|
@4:
|
|||
|
MOVEQ.L #ioFlMdDat, D1 ; D1 = offset into CInfoPBRec to find modification date
|
|||
|
BSR CompareRange
|
|||
|
BNE.S @failed
|
|||
|
|
|||
|
@FlBkDat:
|
|||
|
BTST.L #fsSBFlBkDat, D5 ; is backup date included?
|
|||
|
BEQ.S @hit ; that's the last one, so we hit
|
|||
|
BTST.B #isFile, CSSR.flags(A3) ; are we looking at a file record?
|
|||
|
BEQ.S @FlBkDatIsDir ; if not, go get the right offset for a directory
|
|||
|
MOVE.L filBkDat(A1), D0 ; D0 = backup date of current cNode
|
|||
|
BRA.S @5
|
|||
|
@FlBkDatIsDir:
|
|||
|
MOVE.L dirBkDat(A1), D0 ; D0 = backup date of current cNode
|
|||
|
@5:
|
|||
|
MOVEQ.L #ioFlBkDat, D1 ; D1 = offset into CInfoPBRec to find backup date
|
|||
|
BSR CompareRange
|
|||
|
BEQ.S @hit
|
|||
|
|
|||
|
@failed:
|
|||
|
MOVEQ.L #1, D0 ; indicate failure
|
|||
|
BRA.S @CheckNegate
|
|||
|
@hit:
|
|||
|
MOVEQ.L #0, D0 ; indicate success
|
|||
|
|
|||
|
@checkNegate:
|
|||
|
btst.l #fsSBNegate, d5 ; do we want everything that doesn't match?
|
|||
|
beq.s @CompareExit ; no; the result stands
|
|||
|
|
|||
|
eor.b #1, d0 ; reverse the outcome
|
|||
|
bra.s @compareExit
|
|||
|
|
|||
|
@notSearchable: ; not a file or directory record
|
|||
|
moveq.l #1, d0 ; indicate failure
|
|||
|
|
|||
|
@CompareExit:
|
|||
|
tst.w d0 ; set Z to indicate success
|
|||
|
MOVEA.L (A6)+, A0
|
|||
|
RTS
|
|||
|
_CSDebug 'CheckCriteria', 0
|
|||
|
endproc
|
|||
|
|
|||
|
;__________________________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: VerifyCSPB
|
|||
|
;
|
|||
|
; Function: Verify that a param block contains legal values
|
|||
|
;
|
|||
|
; Input: A0.L - parameter block pointer
|
|||
|
;
|
|||
|
; Register Usage (during call):
|
|||
|
; A0.L - pointer to user's param block D0.L - scratch
|
|||
|
; A1.L - pointer to ioSearchInfo1 D1.L - scratch
|
|||
|
; D2.B - copy of user's ioFlAttrib mask
|
|||
|
; D3.L - cssr.flags byte
|
|||
|
; A4.L - pointer to ioSearchInfo2
|
|||
|
; A5.L - pointer to user's param block D5.L - ioSearchBits
|
|||
|
;
|
|||
|
; Output:
|
|||
|
; d0.W - result code (noErr, paramErr)
|
|||
|
; d2.b - copy of user's ioFlAttrib mask, ioDirFlg masked
|
|||
|
; d3.b - cssr.flags with inclFiles, inclDirs and inclNames set correctly
|
|||
|
;
|
|||
|
;__________________________________________________________________________________
|
|||
|
VerifyCSPBRegs reg A1/A4-A5/D1/D5
|
|||
|
VerifyCSPB: proc export
|
|||
|
|
|||
|
movem.l VerifyCSPBRegs, -(sp)
|
|||
|
MOVE.L a0, a5 ; a5 = ptr(user's pb)
|
|||
|
|
|||
|
; look for conditions that return paramErr
|
|||
|
TST.L ioMatchPtr(A5) ; Nil mBuf? (P1)
|
|||
|
BEQ @ParamErrExit ; is a param err
|
|||
|
TST.L ioSearchInfo1(A5) ; Nil ioSearchInfo1? (P2)
|
|||
|
BEQ @ParamErrExit ; is a param err
|
|||
|
MOVE.L ioSearchBits(A5), D5 ; D5 = spec mask
|
|||
|
MOVE.L D5, D1 ; D1 = scratch copy of spec mask
|
|||
|
ANDI.L #maskNotSearchInfo2, D1 ; any criteria that requires ioSearchInfo2? (P3)
|
|||
|
BEQ.S @checkNamesIncluded ; if not, we don't worry about ioSearchInfo2
|
|||
|
TST.L ioSearchInfo2(A5) ; Nil ioSearchInfo2?
|
|||
|
BEQ @ParamErrExit ; is a param err
|
|||
|
|
|||
|
@checkNamesIncluded:
|
|||
|
; If the search includes names, make a note of it and check to make sure that the name
|
|||
|
; is non-nil and non-zero length.
|
|||
|
clr.b d3 ; d3 will become cssr.flags byte
|
|||
|
BTST.L #fsSBPartialName, D5 ; does search includes partial name?
|
|||
|
BNE.S @cNameCheck ; then go check it
|
|||
|
BTST.L #fsSBFullName, D5 ; does search includes full name?
|
|||
|
BEQ.S @checkSpecBits ; no? then don't need to copy name
|
|||
|
|
|||
|
@cNameCheck: ; make sure name is not nil or zero length
|
|||
|
MOVEA.L ioSearchInfo1(A5), A1 ; A1 = ptr(ioSearchInfo1)
|
|||
|
MOVEA.L ioFileName(A1), A1 ; A1 = ptr(ioName)
|
|||
|
MOVE.L A1, D2 ; nil ioNamePtr? (P4)
|
|||
|
BEQ @ParamErrExit ; is a param err
|
|||
|
MOVEQ.L #0, D0 ; clear D0 to clear high bytes
|
|||
|
MOVE.B (A1), D0 ; D0 = len(target cName)
|
|||
|
BEQ @ParamErrExit ; zero length target name is a param err(P5)
|
|||
|
BSET.L #inclNames, d3 ; note that the search includes names
|
|||
|
|
|||
|
@checkSpecBits:
|
|||
|
MOVEA.L ioSearchInfo1(A5), A1 ; A1 = ptr(ioSearchInfo1)
|
|||
|
MOVEA.L ioSearchInfo2(A5), A4 ; A4 = ptr(ioSearchInfo2)
|
|||
|
BTST.L #fsSBFlAttrib, D5 ; are file attributes included in the search?
|
|||
|
BEQ.S @includeBoth ; if not, search is on both files and directories
|
|||
|
|
|||
|
move.b ioFlAttrib(a4), d2 ; d2.b = user's attribute mask
|
|||
|
MOVE.B #maskBadAttribs, D0 ; D0 = illegal attribute bits on (P6)
|
|||
|
AND.B ioFlAttrib(A4), D0 ; are there any illegal attribute bits included?
|
|||
|
BNE @ParamErrExit ; then it's a param err
|
|||
|
BTST.L #ioDirFlg, d2 ; is the mask bit for directories on?
|
|||
|
BEQ.S @includeBoth ; no, which means to include both
|
|||
|
BTST.B #ioDirFlg, ioFlAttrib(A1) ; what's the desired value for the directory bit?
|
|||
|
BEQ.S @justFiles ; caller wants it off, i.e. just files
|
|||
|
BSET.L #inclDirs, d3 ; caller wants it on, i.e. just directories
|
|||
|
BRA.S @clearMaskBit ; we're done deciding about files vs. directories
|
|||
|
|
|||
|
@includeBoth:
|
|||
|
BSET.L #inclDirs, d3 ; the search includes directories
|
|||
|
@justFiles:
|
|||
|
BSET.L #inclFiles, d3 ; the search includes files
|
|||
|
|
|||
|
@clearMaskBit
|
|||
|
BCLR.L #ioDirFlg, d2 ; clear the dir bit; it isn't actually on disk
|
|||
|
|
|||
|
@P7: ; (P7)
|
|||
|
BTST.L #inclDirs, d3 ; are directories included in the search?
|
|||
|
BEQ.S @P8 ; if not, don't worry about rule 7
|
|||
|
BTST.L #fsSBFlAttrib, D5 ; does the search include file attributes?
|
|||
|
BEQ.S @P8 ; if not, don't worry about rule 7
|
|||
|
BTST.B #ioLockFlg, ioFlAttrib(A4) ; did caller care about the locked bit?
|
|||
|
BNE.S @ParamErrExit ; locked attribute is meaningless on a directory
|
|||
|
|
|||
|
@P8: ; (P8)
|
|||
|
BTST.L #fsSBDrNmFls, D5 ; is #files per directory included?
|
|||
|
BEQ.S @P9 ; then can't conflict with files
|
|||
|
BTST.L #inclFiles, d3 ; does the search include files?
|
|||
|
BNE.S @ParamErrExit ; then that's a param error
|
|||
|
|
|||
|
@P9: ; (P9-P12)
|
|||
|
BTST.L #inclDirs, d3 ; are directories included in the search?
|
|||
|
BEQ.S @OKExit ; if not, don't worry about rules 9-12
|
|||
|
MOVE.L D5, D0 ; make a copy of ioSearchBits
|
|||
|
ANDI.L #maskLengthBits, D0 ; kill all but the bits specifying some sort of length
|
|||
|
BNE.S @ParamErrExit ; any length spec is meaningless in a directory
|
|||
|
|
|||
|
@OKExit:
|
|||
|
moveq.l #noErr, d0
|
|||
|
bra.s @Exit
|
|||
|
|
|||
|
@ParamErrExit:
|
|||
|
move.w #paramErr, d0
|
|||
|
|
|||
|
@Exit:
|
|||
|
movem.l (sp)+, VerifyCSPBRegs
|
|||
|
rts
|
|||
|
|
|||
|
endproc
|
|||
|
|
|||
|
; Here are the parameter checking rules for PBCatSearch: <1.3>
|
|||
|
; The labels at the left also appear in the code which checks that rule.
|
|||
|
|
|||
|
; You get a paramErr if
|
|||
|
; P1 ioMBufPtr is nil
|
|||
|
; P2 ioSearchInfo1 is nil
|
|||
|
; P3 (ioSearchBits includes not(fsSBPartialName or fsSBFullName or fsSBNegate)) and ioSearchInfo2 is nil;
|
|||
|
; i.e. A selected search bit requires the presence of ioSearchInfo2
|
|||
|
;
|
|||
|
; Then these,
|
|||
|
; ioSearchBits rules: You get a paramErr if
|
|||
|
; P4 fsSBPartialName
|
|||
|
; and (ioSearchInfo1->ioNamePtr = (nil or empty string))
|
|||
|
; P5 fsSBFullName (same as above)
|
|||
|
; P6 fsSBFlAttrib and some bit other than 0 (locked) or 4 (directory) returns paramErr
|
|||
|
; P7 search includes directories and
|
|||
|
; (ioSearchBits includes fsSBFlAttrib) and (ioSearchInfo2^.ioFlAttrib bit 0 = 1)
|
|||
|
; return paramErr
|
|||
|
; (i.e. "locked directory" makes no sense)
|
|||
|
; P8 fsSBDrNmFls and files returns paramErr
|
|||
|
; P9 fsSBFlLgLen and directories returns paramErr
|
|||
|
; P10 fsSBFlPyLen (same as above)
|
|||
|
; P11 fsSBFlRLgLen (same as above)
|
|||
|
; P12 fsSBFlRPyLen (same as above)
|
|||
|
;
|
|||
|
; A search includes (files only) if
|
|||
|
; ioSearchBits includes fsSBFlAttrib
|
|||
|
; - and -
|
|||
|
; ioSearchInfo1^.ioFlAttrib bit 4 = 0 i.e. explicitly looking for non-directories
|
|||
|
; - and -
|
|||
|
; ioSearchInfo2^.ioFlAttrib bit 4 = 1
|
|||
|
;
|
|||
|
; A search includes (directories only) if
|
|||
|
; ioSearchBits includes fsSBFlAttrib
|
|||
|
; - and -
|
|||
|
; ioSearchInfo1^.ioFlAttrib bit 4 = 1 i.e. explicitly looking for directories
|
|||
|
; - and -
|
|||
|
; ioSearchInfo2^.ioFlAttrib bit 4 = 1
|
|||
|
|
|||
|
; A search includes both if
|
|||
|
; ioSearchBits includes fsSBFlAttrib
|
|||
|
; - and -
|
|||
|
; ioSearchInfo2^.ioFlAttrib bit 4 = 0 i.e. don't care about the directory bit
|
|||
|
;
|
|||
|
; - OR -
|
|||
|
; ioSearchBits doesn't include fsSBFlAttrib i.e. don't care about attributes at all
|
|||
|
|
|||
|
|
|||
|
;__________________________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: CheckCSPB
|
|||
|
;
|
|||
|
; Function: Check that a param block contains legal values
|
|||
|
; This is a wrapper of VerifyCSPB that is set up as an
|
|||
|
; unqueued a060 call. Its main purpose in life is to let
|
|||
|
; external file systems get a quick parameter block check
|
|||
|
; on a CatSearch call.
|
|||
|
;
|
|||
|
; (Unqueued a060 call means that this is pretty much a register-based
|
|||
|
; OS trap. )
|
|||
|
;
|
|||
|
; Input: A0.L - parameter block pointer
|
|||
|
;
|
|||
|
; Output: D0.W - noErr or paramErr
|
|||
|
;__________________________________________________________________________________
|
|||
|
CheckCSPBRegs reg d2/d3 ; the output registers from VerifyCSPB
|
|||
|
CheckCSPB proc export
|
|||
|
|
|||
|
movem.l CheckCSPBRegs, -(sp)
|
|||
|
bsr VerifyCSPB
|
|||
|
movem.l (sp)+, CheckCSPBRegs
|
|||
|
rts
|
|||
|
endproc
|
|||
|
|
|||
|
;__________________________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: CMCatSearch
|
|||
|
;
|
|||
|
; Function: Locates files by search criteria
|
|||
|
;
|
|||
|
; Input: A0.L - parameter block pointer
|
|||
|
;
|
|||
|
; Register Usage (during call):
|
|||
|
; A0.L - scratch D0.L - scratch
|
|||
|
; A1.L - scratch D1.L - scratch
|
|||
|
; A2.L - scratch D2.L - scratch
|
|||
|
; A3.L - pointer to CatSearch State Record (CSSR) D3.L - scratch
|
|||
|
; A4.L - pointer to PScan State Record (PSR) D4.L - scratch
|
|||
|
; A5.L - pointer to user's param block D5.L - ioSearchBits
|
|||
|
; A6.L - standard file system stack
|
|||
|
; D7.L - ioActMatchCount
|
|||
|
;
|
|||
|
; Output: D0.W - result code (noErr, extFSErr, ioErr, nsvErr, catChanged, paramErr)
|
|||
|
; A0.L, A1.L - trash
|
|||
|
; D0.L, D1.L, D2.L - trash
|
|||
|
;__________________________________________________________________________________
|
|||
|
CMCatSearch: proc export
|
|||
|
|
|||
|
jsrRom FSQUEUE ; queue up the request
|
|||
|
SUBA.W #CSSR.size, A6 ; allocate a CSSR on A6 moved here <1.3>
|
|||
|
CLR.B CSSR.flags(A6) ; clear the flags moved here <1.3>
|
|||
|
MOVEA.L A6, A3 ; move CSSR ptr to a safe register <12>
|
|||
|
MOVEA.L A0, A5 ; move userPB ptr to a safe register <12>
|
|||
|
|
|||
|
move.l ioSearchTime(a5), d0 ; get max search time (time mgr count) <12>
|
|||
|
bsr StartTimer ; start a timer, if needed, right away <12>
|
|||
|
|
|||
|
jsrRom DTRMV3 ; find vol using ioNamePtr & ioVRefNum (D023/A234 trashed)
|
|||
|
MOVEA.L A6, A3 ; reload pointer to CSSR (doesn’t hurt CCs) <12>
|
|||
|
BNE @Exit ; (DtrmV3 puts VCBPtr in A2)
|
|||
|
MOVE.W vcbSigWord(A2), D0 ; Check out the volume signature
|
|||
|
CMP.W #SigWord, D0 ; Is this an MFS disk? •• use TFSVCBCk
|
|||
|
BNE.S @ItsHFS ; Keep going if not
|
|||
|
MOVE.W #wrgVolTypErr, D0 ; Tell the silly caller
|
|||
|
BRA @Exit ; and cruise
|
|||
|
|
|||
|
@ItsHFS:
|
|||
|
jsrRom EXTOFFLINCK ; Is this volume on-line and one of ours? <1.2>
|
|||
|
BEQ.S @VolumeIsCool ; Keep going if so <1.2>
|
|||
|
BRA @Exit ; Go home if not (with extFSErr or volOffLinErr in D0) <1.2>
|
|||
|
|
|||
|
@VolumeIsCool:
|
|||
|
|
|||
|
MOVE.L ioSearchBits(A5), D5 ; D5 = user's spec mask
|
|||
|
MOVEQ.L #0, D7 ; no matches yet
|
|||
|
MOVE.L D7, ioActMatchCount(A5) ; set PB to initial value
|
|||
|
|
|||
|
MOVE.L ioMatchPtr(A5), CSSR.nextMBuf(A3) ; 1st MBuf entry is at the beginning of the buffer
|
|||
|
|
|||
|
; a0 = cspb
|
|||
|
bsr VerifyCSPB ; d2 = ioFlAttrib, d3 = cssr.flags
|
|||
|
bne @Exit ; no? ->
|
|||
|
move.b d2, cssr.attribMask(a3) ; save copy of user's attrib bits
|
|||
|
or.b d3, cssr.flags(a3) ; add flags, keeping timer bit <13>
|
|||
|
|
|||
|
; look for conditions that don't return paramErr, but are reason to quit now
|
|||
|
TST.L ioReqMatchCount(A5) ; max 0 matches?
|
|||
|
BEQ @LeaveEarly ; just quit now; no error
|
|||
|
|
|||
|
; name setup - if the search includes any of the file name bits we need to make an
|
|||
|
; uppercase copy of the target file name.
|
|||
|
BTST.L #inclNames, d3 ; does the search include names?
|
|||
|
BEQ.S @go ; if not, we're ready to roll
|
|||
|
MOVEA.L ioSearchInfo1(A5), A0 ; A0 = ptr(ioSearchInfo1)
|
|||
|
MOVEA.L ioFileName(A0), A0 ; A0 = ptr(ioName)
|
|||
|
MOVEQ.L #0, D0 ; clear D0 to clear high bytes
|
|||
|
MOVE.B (A0), D0 ; D0 = len(target cName)
|
|||
|
CMPI.B #31, D0 ; is length > max hfs length? <14>
|
|||
|
BHI.S @NoWayName ; then we do this little optimization <14>
|
|||
|
ADDQ.B #1, D0 ; D0 = len(target cName) + length byte
|
|||
|
LEA.L CSSR.copyTarg(A3), A1 ; A1 = ptr(copy of target cName)
|
|||
|
_BlockMove ; make the copy
|
|||
|
MOVEQ.L #0, D0 ; clear D0 to clear high bytes
|
|||
|
MOVEA.L A1, A0 ; A0 = ptr(length byte of copy of target cName)
|
|||
|
MOVE.B (A0)+, D0 ; D0 = len(target cName)
|
|||
|
_FSUprString ; uppercase, strip diacriticals
|
|||
|
bra.s @go ; <14>
|
|||
|
|
|||
|
; <14>
|
|||
|
; Since we can't copy extra-long names into our private buffer (to let them fail against
|
|||
|
; all the names on this Str31'd hfs volume) we'll psych out the parameter right here.
|
|||
|
@NoWayName:
|
|||
|
moveq.l #eofErr, d0 ; in case we need this
|
|||
|
btst.l #fsSBNegate, D5 ; do we match everything or nothing?
|
|||
|
beq.s @Exit ; if no negation, match nothing
|
|||
|
moveq.l #0, D5 ; if negation, clear criteria so as to match everything
|
|||
|
|
|||
|
; the parameters are cool, so start searching
|
|||
|
@go:
|
|||
|
MOVE.W vcbCTRef(A2), D0 ; catalog btree refnum
|
|||
|
BSR GetReadBuffer ; A1 = ptr(buffer), D3 = length, cssr.bufferOnStack set
|
|||
|
LEA.L ioCatPosition(A5), A0 ; A0 = ptr(CatPosition record in param block) <dnf 1.5>
|
|||
|
|
|||
|
BSR BTIPScan ; A4 is now ptr(PScan state record), 1st record is read
|
|||
|
BNE.S @EndScan ; BTIPScan returns a regular OSErr
|
|||
|
MOVE.L A4, CSSR.PSRPtr(A3) ; save a copy of the PScan state record
|
|||
|
|
|||
|
@loop:
|
|||
|
BSR CheckCriteria ; do we have a match?
|
|||
|
BNE.S @continue ; Z=1 indicates a hit
|
|||
|
|
|||
|
BSR MBufInsert ; yes, so add entry to match buffer
|
|||
|
ADDQ.L #1, D7 ; tally this match
|
|||
|
MOVE.L D7, ioActMatchCount(A5) ; update PB, so async users can watch
|
|||
|
CMP.L ioReqMatchCount(A5), D7 ; hit user match limit?
|
|||
|
BHS.S @LimitExit
|
|||
|
|
|||
|
@continue:
|
|||
|
btst #timerFired, CSSR.flags(a3) ; did the timer fire? <12>
|
|||
|
bz.s @noTimerYet ; no, don’t bug Mr. Scanner <12>
|
|||
|
bset #noMoreReads, PSR.flags(a4) ; yes, tell him not to hit the disk again <12>
|
|||
|
@noTimerYet
|
|||
|
|
|||
|
BSR BTGetPhys ; on to the next record
|
|||
|
BNE.S @EndScan ; either a real error, or just EOF
|
|||
|
BRA.S @loop
|
|||
|
|
|||
|
; Exit code
|
|||
|
; There are 5 paths out of CatSearch
|
|||
|
; 1) LeaveEarly - set result to noErr, don't clean up PScan
|
|||
|
; 2) ParamErrExit - set result to paramErr, don't clean up PScan
|
|||
|
; 3) LimitExit - set result to noErr, and clean up the PScan
|
|||
|
; 4) EndScan - don't touch the result, and clean up the PScan
|
|||
|
; 5) Exit - don't touch the result code, don't clean up the PScan.
|
|||
|
|
|||
|
@LeaveEarly:
|
|||
|
MOVEQ.L #noErr, D0 ; No error
|
|||
|
BRA.S @Exit ; No PScan cleanup
|
|||
|
|
|||
|
@ParamErrExit:
|
|||
|
MOVE.W #paramErr, D0
|
|||
|
BRA.S @Exit
|
|||
|
|
|||
|
@LimitExit:
|
|||
|
MOVEQ.L #noErr, D0 ; user limit condition implies noErr
|
|||
|
|
|||
|
@EndScan:
|
|||
|
CMP.W #userCanceledErr, d0 ; is this because noMoreReads was set?
|
|||
|
BNE.S @errorIsReal ; if not, this must be a real error
|
|||
|
MOVEQ.L #noErr, d0 ; if so, replace the signal error with noErr
|
|||
|
|
|||
|
@errorIsReal:
|
|||
|
LEA.L ioCatPosition(A5), A0 ; A0 = ptr(CatPosition record in param block) <dnf 1.5>
|
|||
|
BSR BTEndPScan ; clean up PScan (update CatPosition record)
|
|||
|
|
|||
|
@Exit: ; Pass the error code on and exit
|
|||
|
BSR StopTimer ; Stop the timer if we started one <12>
|
|||
|
BTST.B #bufferOnStack, CSSR.flags(A3) ; do we have our buffer on the stack?
|
|||
|
BEQ.S @notBufferOnStack ; if not, don't deallocate one
|
|||
|
ADDA.L #CSMinBufferSize, A6 ; deallocate read buffer
|
|||
|
@notBufferOnStack:
|
|||
|
ADDA.W #CSSR.size, A6 ; deallocate CSSR
|
|||
|
suba.l a3, a3 ; clear WDCB pointer for external file systems <17>
|
|||
|
jmpRom CMDDONE
|
|||
|
|
|||
|
_CSDebugRts 'CMCatSearch', 0
|
|||
|
endproc
|
|||
|
end
|