2 ** Apple Macintosh Developer Technical Support
4 ** IndexedSearch and the PBCatSearch compatibility function.
6 ** by Jim Luther, Apple Developer Technical Support Emeritus
10 ** Copyright © 1992-1998 Apple Computer, Inc.
11 ** All rights reserved.
13 ** You may incorporate this sample code into your applications without
14 ** restriction, though the sample code has been provided "AS IS" and the
15 ** responsibility for its operation is 100% yours. However, what you are
16 ** not permitted to do is to redistribute the source as "DSC Sample Code"
17 ** after having made changes. If you're going to re-distribute the source,
18 ** we require that you make it clear in the source that the code was
19 ** descended from Apple Sample Code, but that you've made changes.
28 #include <TextUtils.h>
30 #define __COMPILINGMOREFILES
36 /*****************************************************************************/
40 /* Number of LevelRecs to add each time the searchStack is grown */
41 /* 20 levels is probably more than reasonable for most volumes. */
42 /* If more are needed, they are allocated 20 levels at a time. */
43 kAdditionalLevelRecs
= 20
46 /*****************************************************************************/
49 ** LevelRecs are used to store the directory ID and index whenever
50 ** IndexedSearch needs to either scan a sub-directory, or return control
51 ** to the caller because the call has timed out or the number of
52 ** matches requested has been found. LevelRecs are stored in an array
57 long dirModDate
; /* for detecting most (but not all) catalog changes */
61 typedef struct LevelRec LevelRec
;
62 typedef LevelRec
*LevelRecPtr
, **LevelRecHandle
;
66 ** SearchPositionRec is my version of a CatPositionRec. It holds the
67 ** information I need to resuming searching.
69 #if PRAGMA_ALIGN_SUPPORTED
70 #pragma options align=mac68k
72 struct SearchPositionRec
74 long initialize
; /* Goofy checksum of volume information used to make */
75 /* sure we're resuming a search on the same volume. */
76 unsigned short stackDepth
; /* Current depth on searchStack. */
77 short priv
[11]; /* For future use... */
79 #if PRAGMA_ALIGN_SUPPORTED
80 #pragma options align=reset
82 typedef struct SearchPositionRec SearchPositionRec
;
83 typedef SearchPositionRec
*SearchPositionRecPtr
;
87 ** ExtendedTMTask is a TMTask record extended to hold the timer flag.
89 #if PRAGMA_ALIGN_SUPPORTED
90 #pragma options align=mac68k
95 Boolean stopSearch
; /* the Time Mgr task will set stopSearch to */
96 /* true when the timer expires */
98 #if PRAGMA_ALIGN_SUPPORTED
99 #pragma options align=reset
101 typedef struct ExtendedTMTask ExtendedTMTask
;
102 typedef ExtendedTMTask
*ExtendedTMTaskPtr
;
104 /*****************************************************************************/
106 static OSErr
CheckVol(ConstStr255Param pathname
,
111 static OSErr
CheckStack(unsigned short stackDepth
,
112 LevelRecHandle searchStack
,
113 Size
*searchStackSize
);
115 static OSErr
VerifyUserPB(CSParamPtr userPB
,
116 Boolean
*includeFiles
,
117 Boolean
*includeDirs
,
118 Boolean
*includeNames
);
120 static Boolean
IsSubString(ConstStr255Param aStringPtr
,
121 ConstStr255Param subStringPtr
);
123 static Boolean
CompareMasked(const long *data1
,
126 short longsToCompare
);
128 static void CheckForMatches(CInfoPBPtr cPB
,
130 const Str63 matchName
,
131 Boolean includeFiles
,
132 Boolean includeDirs
);
134 #if __WANTPASCALELIMINATION
140 static pascal void TimeOutTask(TMTaskPtr tmTaskPtr
);
144 static pascal TMTaskPtr
GetTMTaskPtr(void);
146 static void TimeOutTask(void);
150 #if __WANTPASCALELIMINATION
154 static long GetDirModDate(short vRefNum
,
157 /*****************************************************************************/
160 ** CheckVol gets the volume's real vRefNum and builds a volID. The volID
161 ** is used to help insure that calls to resume searching with IndexedSearch
162 ** are to the same volume as the last call to IndexedSearch.
164 static OSErr
CheckVol(ConstStr255Param pathname
,
172 error
= GetVolumeInfoNoName(pathname
, vRefNum
, &pb
);
173 if ( error
== noErr
)
175 /* Return the real vRefNum */
176 *realVRefNum
= pb
.volumeParam
.ioVRefNum
;
178 /* Add together a bunch of things that aren't supposed to change on */
179 /* a mounted volume that's being searched and that should come up with */
180 /* a fairly unique number */
181 *volID
= pb
.volumeParam
.ioVCrDate
+
182 pb
.volumeParam
.ioVRefNum
+
183 pb
.volumeParam
.ioVNmAlBlks
+
184 pb
.volumeParam
.ioVAlBlkSiz
+
185 pb
.volumeParam
.ioVFSID
;
190 /*****************************************************************************/
193 ** CheckStack checks the size of the search stack (array) to see if there's
194 ** room to push another LevelRec. If not, CheckStack grows the stack by
195 ** another kAdditionalLevelRecs elements.
197 static OSErr
CheckStack(unsigned short stackDepth
,
198 LevelRecHandle searchStack
,
199 Size
*searchStackSize
)
203 if ( (*searchStackSize
/ sizeof(LevelRec
)) == (stackDepth
+ 1) )
205 /* Time to grow stack */
206 SetHandleSize((Handle
)searchStack
, *searchStackSize
+ (kAdditionalLevelRecs
* sizeof(LevelRec
)));
207 result
= MemError(); /* should be noErr */
208 *searchStackSize
= InlineGetHandleSize((Handle
)searchStack
);
218 /*****************************************************************************/
221 ** VerifyUserPB makes sure the parameter block passed to IndexedSearch has
222 ** valid parameters. By making this check once, we don't have to worry about
223 ** things like NULL pointers, strings being too long, etc.
224 ** VerifyUserPB also determines if the search includes files and/or
225 ** directories, and determines if a full or partial name search was requested.
227 static OSErr
VerifyUserPB(CSParamPtr userPB
,
228 Boolean
*includeFiles
,
229 Boolean
*includeDirs
,
230 Boolean
*includeNames
)
232 CInfoPBPtr searchInfo1
;
233 CInfoPBPtr searchInfo2
;
235 searchInfo1
= userPB
->ioSearchInfo1
;
236 searchInfo2
= userPB
->ioSearchInfo2
;
238 /* ioMatchPtr cannot be NULL */
239 if ( userPB
->ioMatchPtr
== NULL
)
244 /* ioSearchInfo1 cannot be NULL */
245 if ( searchInfo1
== NULL
)
250 /* If any bits except partialName, fullName, or negate are set, then */
251 /* ioSearchInfo2 cannot be NULL because information in ioSearchInfo2 is required */
252 if ( ((userPB
->ioSearchBits
& ~(fsSBPartialName
| fsSBFullName
| fsSBNegate
)) != 0) &&
253 ( searchInfo2
== NULL
))
258 *includeFiles
= false;
259 *includeDirs
= false;
260 *includeNames
= false;
262 if ( (userPB
->ioSearchBits
& (fsSBPartialName
| fsSBFullName
)) != 0 )
264 /* If any kind of name matching is requested, then ioNamePtr in */
265 /* ioSearchInfo1 cannot be NULL or a zero-length string */
266 if ( (searchInfo1
->hFileInfo
.ioNamePtr
== NULL
) ||
267 (searchInfo1
->hFileInfo
.ioNamePtr
[0] == 0) ||
268 (searchInfo1
->hFileInfo
.ioNamePtr
[0] > (sizeof(Str63
) - 1)) )
273 *includeNames
= true;
276 if ( (userPB
->ioSearchBits
& fsSBFlAttrib
) != 0 )
278 /* The only attributes you can search on are the directory flag */
279 /* and the locked flag. */
280 if ( (searchInfo2
->hFileInfo
.ioFlAttrib
& ~(ioDirMask
| 0x01)) != 0 )
285 /* interested in the directory bit? */
286 if ( (searchInfo2
->hFileInfo
.ioFlAttrib
& ioDirMask
) != 0 )
288 /* yes, so do they want just directories or just files? */
289 if ( (searchInfo1
->hFileInfo
.ioFlAttrib
& ioDirMask
) != 0 )
295 *includeFiles
= true;
300 /* no interest in directory bit - get both files and directories */
302 *includeFiles
= true;
307 /* no attribute checking - get both files and directories */
309 *includeFiles
= true;
312 /* If directories are included in the search, */
313 /* then the locked attribute cannot be requested. */
315 ((userPB
->ioSearchBits
& fsSBFlAttrib
) != 0) &&
316 ((searchInfo2
->hFileInfo
.ioFlAttrib
& 0x01) != 0) )
321 /* If files are included in the search, then there cannot be */
322 /* a search on the number of files. */
323 if ( *includeFiles
&&
324 ((userPB
->ioSearchBits
& fsSBDrNmFls
) != 0) )
329 /* If directories are included in the search, then there cannot */
330 /* be a search on file lengths. */
332 ((userPB
->ioSearchBits
& (fsSBFlLgLen
| fsSBFlPyLen
| fsSBFlRLgLen
| fsSBFlRPyLen
)) != 0) )
343 /*****************************************************************************/
346 ** IsSubString checks to see if a string is a substring of another string.
347 ** Both input strings have already been converted to all uppercase using
348 ** UprString (the same non-international call the File Manager uses).
350 static Boolean
IsSubString(ConstStr255Param aStringPtr
,
351 ConstStr255Param subStringPtr
)
353 short strLength
; /* length of string */
354 short subStrLength
; /* length of subString */
355 Boolean found
; /* result of test */
356 short index
; /* current index into string */
359 strLength
= aStringPtr
[0];
360 subStrLength
= subStringPtr
[0];
362 if ( subStrLength
<= strLength
)
364 register short count
; /* search counter */
365 register short strIndex
; /* running index into string */
366 register short subStrIndex
; /* running index into subString */
368 /* start looking at first character */
371 /* continue looking until remaining string is shorter than substring */
372 count
= strLength
- subStrLength
+ 1;
376 strIndex
= index
; /* start string index at index */
377 subStrIndex
= 1; /* start subString index at 1 */
379 while ( !found
&& (aStringPtr
[strIndex
] == subStringPtr
[subStrIndex
]) )
381 if ( subStrIndex
== subStrLength
)
383 /* all characters in subString were found */
388 /* check next character of substring against next character of string */
396 /* start substring search again at next string character */
400 } while ( count
!= 0 && (!found
) );
406 /*****************************************************************************/
409 ** CompareMasked does a bitwise comparison with mask on 1 or more longs.
410 ** data1 and data2 are first exclusive-ORed together resulting with bits set
411 ** where they are different. That value is then ANDed with the mask resulting
412 ** with bits set if the test fails. true is returned if the tests pass.
414 static Boolean
CompareMasked(const long *data1
,
417 short longsToCompare
)
419 Boolean result
= true;
421 while ( (longsToCompare
!= 0) && (result
== true) )
423 /* (*data1 ^ *data2) = bits that are different, so... */
424 /* ((*data1 ^ *data2) & *mask) = bits that are different that we're interested in */
426 if ( ((*data1
^ *data2
) & *mask
) != 0 )
438 /*****************************************************************************/
441 ** Check for matches compares the search criteria in userPB to the file
442 ** system object in cPB. If there's a match, then the information in cPB is
443 ** is added to the match array and the actual match count is incremented.
445 static void CheckForMatches(CInfoPBPtr cPB
,
447 const Str63 matchName
,
448 Boolean includeFiles
,
452 CInfoPBPtr searchInfo1
;
453 CInfoPBPtr searchInfo2
;
454 Str63 itemName
; /* copy of object's name for partial name matching */
457 foundMatch
= false; /* default to no match */
459 searchBits
= userPB
->ioSearchBits
;
460 searchInfo1
= userPB
->ioSearchInfo1
;
461 searchInfo2
= userPB
->ioSearchInfo2
;
463 /* Into the if statements that go on forever... */
465 if ( (cPB
->hFileInfo
.ioFlAttrib
& ioDirMask
) == 0 )
480 if ( (searchBits
& fsSBPartialName
) != 0 )
482 if ( (cPB
->hFileInfo
.ioNamePtr
[0] > 0) &&
483 (cPB
->hFileInfo
.ioNamePtr
[0] <= (sizeof(Str63
) - 1)) )
485 /* Make uppercase copy of object name */
486 BlockMoveData(cPB
->hFileInfo
.ioNamePtr
,
488 cPB
->hFileInfo
.ioNamePtr
[0] + 1);
489 /* Use the same non-international call the File Manager uses */
490 UpperString(itemName
, true);
498 if ( !IsSubString(itemName
, matchName
) )
502 else if ( searchBits
== fsSBPartialName
)
504 /* optimize for name matching only since it is most common way to search */
510 if ( (searchBits
& fsSBFullName
) != 0 )
512 /* Use the same non-international call the File Manager uses */
513 if ( !EqualString(cPB
->hFileInfo
.ioNamePtr
, matchName
, false, true) )
517 else if ( searchBits
== fsSBFullName
)
519 /* optimize for name matching only since it is most common way to search */
524 if ( (searchBits
& fsSBFlParID
) != 0 )
526 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlParID
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlParID
)) ||
527 ((unsigned long)(cPB
->hFileInfo
.ioFlParID
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlParID
)) )
533 if ( (searchBits
& fsSBFlAttrib
) != 0 )
535 if ( ((cPB
->hFileInfo
.ioFlAttrib
^ searchInfo1
->hFileInfo
.ioFlAttrib
) &
536 searchInfo2
->hFileInfo
.ioFlAttrib
) != 0 )
542 if ( (searchBits
& fsSBDrNmFls
) != 0 )
544 if ( ((unsigned long)(cPB
->dirInfo
.ioDrNmFls
) < (unsigned long)(searchInfo1
->dirInfo
.ioDrNmFls
)) ||
545 ((unsigned long)(cPB
->dirInfo
.ioDrNmFls
) > (unsigned long)(searchInfo2
->dirInfo
.ioDrNmFls
)) )
551 if ( (searchBits
& fsSBFlFndrInfo
) != 0 ) /* fsSBFlFndrInfo is same as fsSBDrUsrWds */
553 if ( !CompareMasked((long *)&(cPB
->hFileInfo
.ioFlFndrInfo
),
554 (long *)&(searchInfo1
->hFileInfo
.ioFlFndrInfo
),
555 (long *)&(searchInfo2
->hFileInfo
.ioFlFndrInfo
),
556 sizeof(FInfo
) / sizeof(long)) )
562 if ( (searchBits
& fsSBFlXFndrInfo
) != 0 ) /* fsSBFlXFndrInfo is same as fsSBDrFndrInfo */
564 if ( !CompareMasked((long *)&(cPB
->hFileInfo
.ioFlXFndrInfo
),
565 (long *)&(searchInfo1
->hFileInfo
.ioFlXFndrInfo
),
566 (long *)&(searchInfo2
->hFileInfo
.ioFlXFndrInfo
),
567 sizeof(FXInfo
) / sizeof(long)) )
573 if ( (searchBits
& fsSBFlLgLen
) != 0 )
575 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlLgLen
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlLgLen
)) ||
576 ((unsigned long)(cPB
->hFileInfo
.ioFlLgLen
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlLgLen
)) )
582 if ( (searchBits
& fsSBFlPyLen
) != 0 )
584 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlPyLen
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlPyLen
)) ||
585 ((unsigned long)(cPB
->hFileInfo
.ioFlPyLen
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlPyLen
)) )
591 if ( (searchBits
& fsSBFlRLgLen
) != 0 )
593 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlRLgLen
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlRLgLen
)) ||
594 ((unsigned long)(cPB
->hFileInfo
.ioFlRLgLen
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlRLgLen
)) )
600 if ( (searchBits
& fsSBFlRPyLen
) != 0 )
602 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlRPyLen
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlRPyLen
)) ||
603 ((unsigned long)(cPB
->hFileInfo
.ioFlRPyLen
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlRPyLen
)) )
609 if ( (searchBits
& fsSBFlCrDat
) != 0 ) /* fsSBFlCrDat is same as fsSBDrCrDat */
611 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlCrDat
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlCrDat
)) ||
612 ((unsigned long)(cPB
->hFileInfo
.ioFlCrDat
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlCrDat
)) )
618 if ( (searchBits
& fsSBFlMdDat
) != 0 ) /* fsSBFlMdDat is same as fsSBDrMdDat */
620 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlMdDat
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlMdDat
)) ||
621 ((unsigned long)(cPB
->hFileInfo
.ioFlMdDat
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlMdDat
)) )
627 if ( (searchBits
& fsSBFlBkDat
) != 0 ) /* fsSBFlBkDat is same as fsSBDrBkDat */
629 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlBkDat
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlBkDat
)) ||
630 ((unsigned long)(cPB
->hFileInfo
.ioFlBkDat
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlBkDat
)) )
636 /* Hey, we passed all of the tests! */
641 /* foundMatch is false if code jumps to Failed */
643 /* Do we reverse our findings? */
644 if ( (searchBits
& fsSBNegate
) != 0 )
646 foundMatch
= !foundMatch
; /* matches are not, not matches are */
652 /* Move the match into the match buffer */
653 userPB
->ioMatchPtr
[userPB
->ioActMatchCount
].vRefNum
= cPB
->hFileInfo
.ioVRefNum
;
654 userPB
->ioMatchPtr
[userPB
->ioActMatchCount
].parID
= cPB
->hFileInfo
.ioFlParID
;
655 if ( cPB
->hFileInfo
.ioNamePtr
[0] > 63 )
657 cPB
->hFileInfo
.ioNamePtr
[0] = 63;
659 BlockMoveData(cPB
->hFileInfo
.ioNamePtr
,
660 userPB
->ioMatchPtr
[userPB
->ioActMatchCount
].name
,
661 cPB
->hFileInfo
.ioNamePtr
[0] + 1);
663 /* increment the actual count */
664 ++(userPB
->ioActMatchCount
);
668 /*****************************************************************************/
671 ** TimeOutTask is executed when the timer goes off. It simply sets the
672 ** stopSearch field to true. After each object is found and possibly added
673 ** to the matches buffer, stopSearch is checked to see if the search should
677 #if __WANTPASCALELIMINATION
683 static pascal void TimeOutTask(TMTaskPtr tmTaskPtr
)
685 ((ExtendedTMTaskPtr
)tmTaskPtr
)->stopSearch
= true;
690 static pascal TMTaskPtr
GetTMTaskPtr(void)
691 ONEWORDINLINE(0x2e89); /* MOVE.L A1,(SP) */
693 static void TimeOutTask(void)
695 ((ExtendedTMTaskPtr
)GetTMTaskPtr())->stopSearch
= true;
700 #if __WANTPASCALELIMINATION
704 /*****************************************************************************/
707 ** GetDirModDate returns the modification date of a directory. If there is
708 ** an error getting the modification date, -1 is returned to indicate
709 ** something went wrong.
711 static long GetDirModDate(short vRefNum
,
718 /* Protection against File Sharing problem */
720 pb
.dirInfo
.ioNamePtr
= tempName
;
721 pb
.dirInfo
.ioVRefNum
= vRefNum
;
722 pb
.dirInfo
.ioDrDirID
= dirID
;
723 pb
.dirInfo
.ioFDirIndex
= -1; /* use ioDrDirID */
725 if ( PBGetCatInfoSync(&pb
) == noErr
)
727 modDate
= pb
.dirInfo
.ioDrMdDat
;
737 /*****************************************************************************/
739 pascal OSErr
IndexedSearch(CSParamPtr pb
,
742 static LevelRecHandle searchStack
= NULL
; /* static handle to LevelRec stack */
743 static Size searchStackSize
= 0; /* size of static handle */
744 SearchPositionRecPtr catPosition
;
747 ExtendedTMTask timerTask
;
753 Boolean includeFiles
;
755 Boolean includeNames
;
758 timerTask
.stopSearch
= false; /* don't stop yet! */
760 /* If request has a timeout, install a Time Manager task. */
761 if ( pb
->ioSearchTime
!= 0 )
764 timerTask
.theTask
.tmAddr
= NewTimerProc(TimeOutTask
);
765 InsTime((QElemPtr
)&(timerTask
.theTask
));
766 PrimeTime((QElemPtr
)&(timerTask
.theTask
), pb
->ioSearchTime
);
769 /* Check the parameter block passed for things that we don't want to assume */
770 /* are OK later in the code. For example, make sure pointers to data structures */
771 /* and buffers are not NULL. And while we're in there, see if the request */
772 /* specified searching for files, directories, or both, and see if the search */
773 /* was by full or partial name. */
774 result
= VerifyUserPB(pb
, &includeFiles
, &includeDirs
, &includeNames
);
775 if ( result
== noErr
)
777 pb
->ioActMatchCount
= 0; /* no matches yet */
781 /* The search includes seach by full or partial name. */
782 /* Make an upper case copy of the match string to pass to */
783 /* CheckForMatches. */
784 BlockMoveData(pb
->ioSearchInfo1
->hFileInfo
.ioNamePtr
,
786 pb
->ioSearchInfo1
->hFileInfo
.ioNamePtr
[0] + 1);
787 /* Use the same non-international call the File Manager uses */
788 UpperString(upperName
, true);
791 /* Prevent casting to my type throughout code */
792 catPosition
= (SearchPositionRecPtr
)&pb
->ioCatPosition
;
794 /* Create searchStack first time called */
795 if ( searchStack
== NULL
)
797 searchStack
= (LevelRecHandle
)NewHandle(kAdditionalLevelRecs
* sizeof(LevelRec
));
800 /* Make sure searchStack really exists */
801 if ( searchStack
!= NULL
)
803 searchStackSize
= InlineGetHandleSize((Handle
)searchStack
);
805 /* See if the search is a new search or a resumed search. */
806 if ( catPosition
->initialize
== 0 )
810 /* Get the real vRefNum and fill in catPosition->initialize. */
811 result
= CheckVol(pb
->ioNamePtr
, pb
->ioVRefNum
, &realVRefNum
, &catPosition
->initialize
);
812 if ( result
== noErr
)
814 /* clear searchStack */
815 catPosition
->stackDepth
= 0;
817 /* use dirID parameter passed and... */
818 index
= -1; /* start with the passed directory itself! */
823 /* We're resuming a search. */
825 /* Get the real vRefNum and make sure catPosition->initialize is valid. */
826 result
= CheckVol(pb
->ioNamePtr
, pb
->ioVRefNum
, &realVRefNum
, &tempLong
);
827 if ( result
== noErr
)
829 /* Make sure the resumed search is to the same volume! */
830 if ( catPosition
->initialize
== tempLong
)
832 /* For resume, catPosition->stackDepth > 0 */
833 if ( catPosition
->stackDepth
> 0 )
835 /* Position catPosition->stackDepth to access last saved level */
836 --(catPosition
->stackDepth
);
838 /* Get the dirID and index for the next item */
839 dirID
= (*searchStack
)[catPosition
->stackDepth
].dirID
;
840 index
= (*searchStack
)[catPosition
->stackDepth
].index
;
842 /* Check the dir's mod date against the saved mode date on our "stack" */
843 modDate
= GetDirModDate(realVRefNum
, dirID
);
844 if ( modDate
!= (*searchStack
)[catPosition
->stackDepth
].dirModDate
)
846 result
= catChangedErr
;
851 /* Invalid catPosition record was passed */
857 /* The volume is not the same */
858 result
= catChangedErr
;
863 if ( result
== noErr
)
865 /* ioNamePtr and ioVRefNum only need to be set up once. */
866 cPB
.hFileInfo
.ioNamePtr
= itemName
;
867 cPB
.hFileInfo
.ioVRefNum
= realVRefNum
;
870 ** Here's the loop that:
871 ** Finds the next item on the volume.
872 ** If noErr, calls the code to check for matches and add matches
873 ** to the match buffer.
874 ** Sets up dirID and index for to find the next item on the volume.
876 ** The looping ends when:
877 ** (a) an unexpected error is returned by PBGetCatInfo. All that
878 ** is expected is noErr and fnfErr (after the last item in a
879 ** directory is found).
880 ** (b) the caller specified a timeout and our Time Manager task
882 ** (c) the number of matches requested by the caller has been found.
883 ** (d) the last item on the volume was found.
887 /* get the next item */
888 cPB
.hFileInfo
.ioFDirIndex
= index
;
889 cPB
.hFileInfo
.ioDirID
= dirID
;
890 result
= PBGetCatInfoSync(&cPB
);
893 if ( result
== noErr
)
895 /* We found something */
897 CheckForMatches(&cPB
, pb
, upperName
, includeFiles
, includeDirs
);
900 if ( (cPB
.dirInfo
.ioFlAttrib
& ioDirMask
) != 0 )
902 /* It's a directory */
904 result
= CheckStack(catPosition
->stackDepth
, searchStack
, &searchStackSize
);
905 if ( result
== noErr
)
907 /* Save the current state on the searchStack */
908 /* when we come back, this is where we'll start */
909 (*searchStack
)[catPosition
->stackDepth
].dirID
= dirID
;
910 (*searchStack
)[catPosition
->stackDepth
].index
= index
;
911 (*searchStack
)[catPosition
->stackDepth
].dirModDate
= GetDirModDate(realVRefNum
, dirID
);
913 /* position catPosition->stackDepth for next saved level */
914 ++(catPosition
->stackDepth
);
916 /* The next item to get is the 1st item in the child directory */
917 dirID
= cPB
.dirInfo
.ioDrDirID
;
921 /* else do nothing for files */
925 /* End of directory found (or we had some error and that */
926 /* means we have to drop out of this directory). */
927 /* Restore last thing put on stack and */
928 /* see if we need to continue or quit. */
929 if ( catPosition
->stackDepth
> 0 )
931 /* position catPosition->stackDepth to access last saved level */
932 --(catPosition
->stackDepth
);
934 dirID
= (*searchStack
)[catPosition
->stackDepth
].dirID
;
935 index
= (*searchStack
)[catPosition
->stackDepth
].index
;
937 /* Check the dir's mod date against the saved mode date on our "stack" */
938 modDate
= GetDirModDate(realVRefNum
, dirID
);
939 if ( modDate
!= (*searchStack
)[catPosition
->stackDepth
].dirModDate
)
941 result
= catChangedErr
;
945 /* Going back to ancestor directory. */
946 /* Clear error so we can continue. */
952 /* We hit the bottom of the stack, so we'll let the */
953 /* the eofErr drop us out of the loop. */
960 /* Special case for index == -1; that means that we're starting */
961 /* a new search and so the first item to check is the directory */
963 if ( result
== noErr
)
965 /* We found something */
967 CheckForMatches(&cPB
, pb
, upperName
, includeFiles
, includeDirs
);
969 /* Now, set the index to 1 and then we're ready to look inside */
970 /* the passed directory. */
974 } while ( (!timerTask
.stopSearch
) && /* timer hasn't fired */
975 (result
== noErr
) && /* no unexpected errors */
976 (pb
->ioReqMatchCount
> pb
->ioActMatchCount
) ); /* we haven't found our limit */
978 /* Did we drop out of the loop because of timeout or */
979 /* ioReqMatchCount was found? */
980 if ( result
== noErr
)
982 result
= CheckStack(catPosition
->stackDepth
, searchStack
, &searchStackSize
);
983 if ( result
== noErr
)
985 /* Either there was a timeout or ioReqMatchCount was reached. */
986 /* Save the dirID and index for the next time we're called. */
988 (*searchStack
)[catPosition
->stackDepth
].dirID
= dirID
;
989 (*searchStack
)[catPosition
->stackDepth
].index
= index
;
990 (*searchStack
)[catPosition
->stackDepth
].dirModDate
= GetDirModDate(realVRefNum
, dirID
);
992 /* position catPosition->stackDepth for next saved level */
994 ++(catPosition
->stackDepth
);
1001 /* searchStack Handle could not be allocated */
1002 result
= memFullErr
;
1006 if ( pb
->ioSearchTime
!= 0 )
1008 /* Stop Time Manager task here if it was installed */
1009 RmvTime((QElemPtr
)&(timerTask
.theTask
));
1010 DisposeRoutineDescriptor(timerTask
.theTask
.tmAddr
);
1016 /*****************************************************************************/
1018 pascal OSErr
PBCatSearchSyncCompat(CSParamPtr paramBlock
)
1020 static Boolean fullExtFSDispatchingtested
= false;
1021 static Boolean hasFullExtFSDispatching
= false;
1023 Boolean supportsCatSearch
;
1025 GetVolParmsInfoBuffer volParmsInfo
;
1030 /* See if File Manager will pass CatSearch requests to external file systems */
1031 /* we'll store the results in a static variable so we don't have to call Gestalt */
1032 /* everytime we're called. */
1033 if ( !fullExtFSDispatchingtested
)
1035 fullExtFSDispatchingtested
= true;
1036 if ( Gestalt(gestaltFSAttr
, &response
) == noErr
)
1038 hasFullExtFSDispatching
= ((response
& (1L << gestaltFullExtFSDispatching
)) != 0);
1042 /* CatSearch is a per volume attribute, so we have to check each time we're */
1043 /* called to see if it is available on the volume specified. */
1044 supportsCatSearch
= false;
1045 if ( hasFullExtFSDispatching
)
1047 infoSize
= sizeof(GetVolParmsInfoBuffer
);
1048 result
= HGetVolParms(paramBlock
->ioNamePtr
, paramBlock
->ioVRefNum
,
1049 &volParmsInfo
, &infoSize
);
1050 if ( result
== noErr
)
1052 supportsCatSearch
= hasCatSearch(volParmsInfo
);
1056 /* noErr or paramErr is OK here. */
1057 /* paramErr just means that GetVolParms isn't supported by this volume */
1058 if ( (result
== noErr
) || (result
== paramErr
) )
1060 if ( supportsCatSearch
)
1062 /* Volume supports CatSearch so use it. */
1063 /* CatSearch is faster than an indexed search. */
1064 result
= PBCatSearchSync(paramBlock
);
1068 /* Volume doesn't support CatSearch so */
1069 /* search using IndexedSearch from root directory. */
1070 result
= IndexedSearch(paramBlock
, fsRtDirID
);
1077 /*****************************************************************************/
1079 pascal OSErr
NameFileSearch(ConstStr255Param volName
,
1081 ConstStr255Param fileName
,
1084 long *actMatchCount
,
1088 CInfoPBRec searchInfo1
, searchInfo2
;
1091 static CatPositionRec catPosition
;
1092 static short lastVRefNum
= 0;
1094 /* get the real volume reference number */
1095 error
= DetermineVRefNum(volName
, vRefNum
, &vRefNum
);
1096 if ( error
!= noErr
)
1099 pb
.csParam
.ioNamePtr
= NULL
;
1100 pb
.csParam
.ioVRefNum
= vRefNum
;
1101 pb
.csParam
.ioMatchPtr
= matches
;
1102 pb
.csParam
.ioReqMatchCount
= reqMatchCount
;
1103 if ( partial
) /* tell CatSearch what we're looking for: */
1105 pb
.csParam
.ioSearchBits
= fsSBPartialName
+ fsSBFlAttrib
; /* partial name file matches or */
1109 pb
.csParam
.ioSearchBits
= fsSBFullName
+ fsSBFlAttrib
; /* full name file matches */
1111 pb
.csParam
.ioSearchInfo1
= &searchInfo1
;
1112 pb
.csParam
.ioSearchInfo2
= &searchInfo2
;
1113 pb
.csParam
.ioSearchTime
= 0;
1114 if ( (newSearch
) || /* If caller specified new search */
1115 (lastVRefNum
!= vRefNum
) ) /* or if last search was to another volume, */
1117 catPosition
.initialize
= 0; /* then search from beginning of catalog */
1119 pb
.csParam
.ioCatPosition
= catPosition
;
1120 pb
.csParam
.ioOptBuffer
= GetTempBuffer(0x00004000, &pb
.csParam
.ioOptBufSize
);
1122 /* search for fileName */
1123 searchInfo1
.hFileInfo
.ioNamePtr
= (StringPtr
)fileName
;
1124 searchInfo2
.hFileInfo
.ioNamePtr
= NULL
;
1126 /* only match files (not directories) */
1127 searchInfo1
.hFileInfo
.ioFlAttrib
= 0x00;
1128 searchInfo2
.hFileInfo
.ioFlAttrib
= ioDirMask
;
1130 error
= PBCatSearchSyncCompat((CSParamPtr
)&pb
);
1132 if ( (error
== noErr
) || /* If no errors or the end of catalog was */
1133 (error
== eofErr
) ) /* found, then the call was successful so */
1135 *actMatchCount
= pb
.csParam
.ioActMatchCount
; /* return the match count */
1139 *actMatchCount
= 0; /* else no matches found */
1142 if ( (error
== noErr
) || /* If no errors */
1143 (error
== catChangedErr
) ) /* or there was a change in the catalog */
1145 catPosition
= pb
.csParam
.ioCatPosition
;
1146 lastVRefNum
= vRefNum
;
1147 /* we can probably start the next search where we stopped this time */
1151 catPosition
.initialize
= 0;
1152 /* start the next search from beginning of catalog */
1155 if ( pb
.csParam
.ioOptBuffer
!= NULL
)
1157 DisposePtr(pb
.csParam
.ioOptBuffer
);
1163 /*****************************************************************************/
1165 pascal OSErr
CreatorTypeFileSearch(ConstStr255Param volName
,
1171 long *actMatchCount
,
1174 CInfoPBRec searchInfo1
, searchInfo2
;
1177 static CatPositionRec catPosition
;
1178 static short lastVRefNum
= 0;
1180 /* get the real volume reference number */
1181 error
= DetermineVRefNum(volName
, vRefNum
, &vRefNum
);
1182 if ( error
!= noErr
)
1185 pb
.csParam
.ioNamePtr
= NULL
;
1186 pb
.csParam
.ioVRefNum
= vRefNum
;
1187 pb
.csParam
.ioMatchPtr
= matches
;
1188 pb
.csParam
.ioReqMatchCount
= reqMatchCount
;
1189 pb
.csParam
.ioSearchBits
= fsSBFlAttrib
+ fsSBFlFndrInfo
; /* Looking for finder info file matches */
1190 pb
.csParam
.ioSearchInfo1
= &searchInfo1
;
1191 pb
.csParam
.ioSearchInfo2
= &searchInfo2
;
1192 pb
.csParam
.ioSearchTime
= 0;
1193 if ( (newSearch
) || /* If caller specified new search */
1194 (lastVRefNum
!= vRefNum
) ) /* or if last search was to another volume, */
1196 catPosition
.initialize
= 0; /* then search from beginning of catalog */
1198 pb
.csParam
.ioCatPosition
= catPosition
;
1199 pb
.csParam
.ioOptBuffer
= GetTempBuffer(0x00004000, &pb
.csParam
.ioOptBufSize
);
1202 searchInfo1
.hFileInfo
.ioNamePtr
= NULL
;
1203 searchInfo2
.hFileInfo
.ioNamePtr
= NULL
;
1205 /* only match files (not directories) */
1206 searchInfo1
.hFileInfo
.ioFlAttrib
= 0x00;
1207 searchInfo2
.hFileInfo
.ioFlAttrib
= ioDirMask
;
1209 /* search for creator; if creator = 0x00000000, ignore creator */
1210 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdCreator
= creator
;
1211 if ( creator
== (OSType
)0x00000000 )
1213 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdCreator
= (OSType
)0x00000000;
1217 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdCreator
= (OSType
)0xffffffff;
1220 /* search for fileType; if fileType = 0x00000000, ignore fileType */
1221 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdType
= fileType
;
1222 if ( fileType
== (OSType
)0x00000000 )
1224 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdType
= (OSType
)0x00000000;
1228 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdType
= (OSType
)0xffffffff;
1231 /* zero all other FInfo fields */
1232 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdFlags
= 0;
1233 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdLocation
.v
= 0;
1234 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdLocation
.h
= 0;
1235 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdFldr
= 0;
1237 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdFlags
= 0;
1238 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdLocation
.v
= 0;
1239 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdLocation
.h
= 0;
1240 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdFldr
= 0;
1242 error
= PBCatSearchSyncCompat((CSParamPtr
)&pb
);
1244 if ( (error
== noErr
) || /* If no errors or the end of catalog was */
1245 (error
== eofErr
) ) /* found, then the call was successful so */
1247 *actMatchCount
= pb
.csParam
.ioActMatchCount
; /* return the match count */
1251 *actMatchCount
= 0; /* else no matches found */
1254 if ( (error
== noErr
) || /* If no errors */
1255 (error
== catChangedErr
) ) /* or there was a change in the catalog */
1257 catPosition
= pb
.csParam
.ioCatPosition
;
1258 lastVRefNum
= vRefNum
;
1259 /* we can probably start the next search where we stopped this time */
1263 catPosition
.initialize
= 0;
1264 /* start the next search from beginning of catalog */
1267 if ( pb
.csParam
.ioOptBuffer
!= NULL
)
1269 DisposePtr(pb
.csParam
.ioOptBuffer
);
1275 /*****************************************************************************/