supermario/base/SuperMarioProj.1994-02-09/OS/HFS/Extensions/CatSearch.a

1015 lines
39 KiB
Plaintext
Raw Normal View History

2019-06-29 15:17:50 +00:00
;
; 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 (doesnt 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, dont 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