4 Contains: IndexedSearch and the PBCatSearch compatibility function.
8 Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved.
10 You may incorporate this sample code into your applications without
11 restriction, though the sample code has been provided "AS IS" and the
12 responsibility for its operation is 100% yours. However, what you are
13 not permitted to do is to redistribute the source as "DSC Sample Code"
14 after having made changes. If you're going to re-distribute the source,
15 we require that you make it clear in the source that the code was
16 descended from Apple Sample Code, but that you've made changes.
22 Other Contact: Apple Macintosh Developer Technical Support
23 <http://developer.apple.com/bugreporter/>
25 Technology: DTS Sample Code
31 Change History (most recent first):
33 <2> 2/7/01 JL Added standard header. Updated names of includes. Updated
34 various routines to use new calling convention of the
35 MoreFilesExtras accessor functions. Added TARGET_API_MAC_CARBON
36 conditional checks around TimeOutTask.
37 <1> 12/06/99 JL MoreFiles 1.5.
43 #include <MacErrors.h>
44 #include <MacMemory.h>
46 #include <TextUtils.h>
48 #define __COMPILINGMOREFILES
50 #include "MoreFiles.h"
51 #include "MoreFilesExtras.h"
55 /*****************************************************************************/
59 /* Number of LevelRecs to add each time the searchStack is grown */
60 /* 20 levels is probably more than reasonable for most volumes. */
61 /* If more are needed, they are allocated 20 levels at a time. */
62 kAdditionalLevelRecs
= 20
65 /*****************************************************************************/
68 ** LevelRecs are used to store the directory ID and index whenever
69 ** IndexedSearch needs to either scan a sub-directory, or return control
70 ** to the caller because the call has timed out or the number of
71 ** matches requested has been found. LevelRecs are stored in an array
76 long dirModDate
; /* for detecting most (but not all) catalog changes */
80 typedef struct LevelRec LevelRec
;
81 typedef LevelRec
*LevelRecPtr
, **LevelRecHandle
;
85 ** SearchPositionRec is my version of a CatPositionRec. It holds the
86 ** information I need to resuming searching.
88 #if PRAGMA_STRUCT_ALIGN
89 #pragma options align=mac68k
91 struct SearchPositionRec
93 long initialize
; /* Goofy checksum of volume information used to make */
94 /* sure we're resuming a search on the same volume. */
95 unsigned short stackDepth
; /* Current depth on searchStack. */
96 short priv
[11]; /* For future use... */
98 #if PRAGMA_STRUCT_ALIGN
99 #pragma options align=reset
101 typedef struct SearchPositionRec SearchPositionRec
;
102 typedef SearchPositionRec
*SearchPositionRecPtr
;
106 ** ExtendedTMTask is a TMTask record extended to hold the timer flag.
108 #if PRAGMA_STRUCT_ALIGN
109 #pragma options align=mac68k
111 struct ExtendedTMTask
114 Boolean stopSearch
; /* the Time Mgr task will set stopSearch to */
115 /* true when the timer expires */
117 #if PRAGMA_STRUCT_ALIGN
118 #pragma options align=reset
120 typedef struct ExtendedTMTask ExtendedTMTask
;
121 typedef ExtendedTMTask
*ExtendedTMTaskPtr
;
123 /*****************************************************************************/
125 static OSErr
CheckVol(ConstStr255Param pathname
,
130 static OSErr
CheckStack(unsigned short stackDepth
,
131 LevelRecHandle searchStack
,
132 Size
*searchStackSize
);
134 static OSErr
VerifyUserPB(CSParamPtr userPB
,
135 Boolean
*includeFiles
,
136 Boolean
*includeDirs
,
137 Boolean
*includeNames
);
139 static Boolean
IsSubString(ConstStr255Param aStringPtr
,
140 ConstStr255Param subStringPtr
);
142 static Boolean
CompareMasked(const long *data1
,
145 short longsToCompare
);
147 static void CheckForMatches(CInfoPBPtr cPB
,
149 const Str63 matchName
,
150 Boolean includeFiles
,
151 Boolean includeDirs
);
153 #if __WANTPASCALELIMINATION
157 #if TARGET_RT_MAC_CFM || TARGET_API_MAC_CARBON
159 static pascal void TimeOutTask(TMTaskPtr tmTaskPtr
);
163 static pascal TMTaskPtr
GetTMTaskPtr(void);
165 static void TimeOutTask(void);
169 #if __WANTPASCALELIMINATION
173 static long GetDirModDate(short vRefNum
,
176 /*****************************************************************************/
179 ** CheckVol gets the volume's real vRefNum and builds a volID. The volID
180 ** is used to help insure that calls to resume searching with IndexedSearch
181 ** are to the same volume as the last call to IndexedSearch.
183 static OSErr
CheckVol(ConstStr255Param pathname
,
191 error
= GetVolumeInfoNoName(pathname
, vRefNum
, &pb
);
192 if ( error
== noErr
)
194 /* Return the real vRefNum */
195 *realVRefNum
= pb
.volumeParam
.ioVRefNum
;
197 /* Add together a bunch of things that aren't supposed to change on */
198 /* a mounted volume that's being searched and that should come up with */
199 /* a fairly unique number */
200 *volID
= pb
.volumeParam
.ioVCrDate
+
201 pb
.volumeParam
.ioVRefNum
+
202 pb
.volumeParam
.ioVNmAlBlks
+
203 pb
.volumeParam
.ioVAlBlkSiz
+
204 pb
.volumeParam
.ioVFSID
;
209 /*****************************************************************************/
212 ** CheckStack checks the size of the search stack (array) to see if there's
213 ** room to push another LevelRec. If not, CheckStack grows the stack by
214 ** another kAdditionalLevelRecs elements.
216 static OSErr
CheckStack(unsigned short stackDepth
,
217 LevelRecHandle searchStack
,
218 Size
*searchStackSize
)
222 if ( (*searchStackSize
/ sizeof(LevelRec
)) == (stackDepth
+ 1) )
224 /* Time to grow stack */
225 SetHandleSize((Handle
)searchStack
, *searchStackSize
+ (kAdditionalLevelRecs
* sizeof(LevelRec
)));
226 result
= MemError(); /* should be noErr */
227 *searchStackSize
= GetHandleSize((Handle
)searchStack
);
237 /*****************************************************************************/
240 ** VerifyUserPB makes sure the parameter block passed to IndexedSearch has
241 ** valid parameters. By making this check once, we don't have to worry about
242 ** things like NULL pointers, strings being too long, etc.
243 ** VerifyUserPB also determines if the search includes files and/or
244 ** directories, and determines if a full or partial name search was requested.
246 static OSErr
VerifyUserPB(CSParamPtr userPB
,
247 Boolean
*includeFiles
,
248 Boolean
*includeDirs
,
249 Boolean
*includeNames
)
251 CInfoPBPtr searchInfo1
;
252 CInfoPBPtr searchInfo2
;
254 searchInfo1
= userPB
->ioSearchInfo1
;
255 searchInfo2
= userPB
->ioSearchInfo2
;
257 /* ioMatchPtr cannot be NULL */
258 if ( userPB
->ioMatchPtr
== NULL
)
263 /* ioSearchInfo1 cannot be NULL */
264 if ( searchInfo1
== NULL
)
269 /* If any bits except partialName, fullName, or negate are set, then */
270 /* ioSearchInfo2 cannot be NULL because information in ioSearchInfo2 is required */
271 if ( ((userPB
->ioSearchBits
& ~(fsSBPartialName
| fsSBFullName
| fsSBNegate
)) != 0) &&
272 ( searchInfo2
== NULL
))
277 *includeFiles
= false;
278 *includeDirs
= false;
279 *includeNames
= false;
281 if ( (userPB
->ioSearchBits
& (fsSBPartialName
| fsSBFullName
)) != 0 )
283 /* If any kind of name matching is requested, then ioNamePtr in */
284 /* ioSearchInfo1 cannot be NULL or a zero-length string */
285 if ( (searchInfo1
->hFileInfo
.ioNamePtr
== NULL
) ||
286 (searchInfo1
->hFileInfo
.ioNamePtr
[0] == 0) ||
287 (searchInfo1
->hFileInfo
.ioNamePtr
[0] > (sizeof(Str63
) - 1)) )
292 *includeNames
= true;
295 if ( (userPB
->ioSearchBits
& fsSBFlAttrib
) != 0 )
297 /* The only attributes you can search on are the directory flag */
298 /* and the locked flag. */
299 if ( (searchInfo2
->hFileInfo
.ioFlAttrib
& ~(kioFlAttribDirMask
| kioFlAttribLockedMask
)) != 0 )
304 /* interested in the directory bit? */
305 if ( (searchInfo2
->hFileInfo
.ioFlAttrib
& kioFlAttribDirMask
) != 0 )
307 /* yes, so do they want just directories or just files? */
308 if ( (searchInfo1
->hFileInfo
.ioFlAttrib
& kioFlAttribDirMask
) != 0 )
314 *includeFiles
= true;
319 /* no interest in directory bit - get both files and directories */
321 *includeFiles
= true;
326 /* no attribute checking - get both files and directories */
328 *includeFiles
= true;
331 /* If directories are included in the search, */
332 /* then the locked attribute cannot be requested. */
334 ((userPB
->ioSearchBits
& fsSBFlAttrib
) != 0) &&
335 ((searchInfo2
->hFileInfo
.ioFlAttrib
& kioFlAttribLockedMask
) != 0) )
340 /* If files are included in the search, then there cannot be */
341 /* a search on the number of files. */
342 if ( *includeFiles
&&
343 ((userPB
->ioSearchBits
& fsSBDrNmFls
) != 0) )
348 /* If directories are included in the search, then there cannot */
349 /* be a search on file lengths. */
351 ((userPB
->ioSearchBits
& (fsSBFlLgLen
| fsSBFlPyLen
| fsSBFlRLgLen
| fsSBFlRPyLen
)) != 0) )
362 /*****************************************************************************/
365 ** IsSubString checks to see if a string is a substring of another string.
366 ** Both input strings have already been converted to all uppercase using
367 ** UprString (the same non-international call the File Manager uses).
369 static Boolean
IsSubString(ConstStr255Param aStringPtr
,
370 ConstStr255Param subStringPtr
)
372 short strLength
; /* length of string */
373 short subStrLength
; /* length of subString */
374 Boolean found
; /* result of test */
375 short index
; /* current index into string */
378 strLength
= aStringPtr
[0];
379 subStrLength
= subStringPtr
[0];
381 if ( subStrLength
<= strLength
)
383 register short count
; /* search counter */
384 register short strIndex
; /* running index into string */
385 register short subStrIndex
; /* running index into subString */
387 /* start looking at first character */
390 /* continue looking until remaining string is shorter than substring */
391 count
= strLength
- subStrLength
+ 1;
395 strIndex
= index
; /* start string index at index */
396 subStrIndex
= 1; /* start subString index at 1 */
398 while ( !found
&& (aStringPtr
[strIndex
] == subStringPtr
[subStrIndex
]) )
400 if ( subStrIndex
== subStrLength
)
402 /* all characters in subString were found */
407 /* check next character of substring against next character of string */
415 /* start substring search again at next string character */
419 } while ( count
!= 0 && (!found
) );
425 /*****************************************************************************/
428 ** CompareMasked does a bitwise comparison with mask on 1 or more longs.
429 ** data1 and data2 are first exclusive-ORed together resulting with bits set
430 ** where they are different. That value is then ANDed with the mask resulting
431 ** with bits set if the test fails. true is returned if the tests pass.
433 static Boolean
CompareMasked(const long *data1
,
436 short longsToCompare
)
438 Boolean result
= true;
440 while ( (longsToCompare
!= 0) && (result
== true) )
442 /* (*data1 ^ *data2) = bits that are different, so... */
443 /* ((*data1 ^ *data2) & *mask) = bits that are different that we're interested in */
445 if ( ((*data1
^ *data2
) & *mask
) != 0 )
457 /*****************************************************************************/
460 ** Check for matches compares the search criteria in userPB to the file
461 ** system object in cPB. If there's a match, then the information in cPB is
462 ** is added to the match array and the actual match count is incremented.
464 static void CheckForMatches(CInfoPBPtr cPB
,
466 const Str63 matchName
,
467 Boolean includeFiles
,
471 CInfoPBPtr searchInfo1
;
472 CInfoPBPtr searchInfo2
;
473 Str63 itemName
; /* copy of object's name for partial name matching */
476 foundMatch
= false; /* default to no match */
478 searchBits
= userPB
->ioSearchBits
;
479 searchInfo1
= userPB
->ioSearchInfo1
;
480 searchInfo2
= userPB
->ioSearchInfo2
;
482 /* Into the if statements that go on forever... */
484 if ( (cPB
->hFileInfo
.ioFlAttrib
& kioFlAttribDirMask
) == 0 )
499 if ( (searchBits
& fsSBPartialName
) != 0 )
501 if ( (cPB
->hFileInfo
.ioNamePtr
[0] > 0) &&
502 (cPB
->hFileInfo
.ioNamePtr
[0] <= (sizeof(Str63
) - 1)) )
504 /* Make uppercase copy of object name */
505 BlockMoveData(cPB
->hFileInfo
.ioNamePtr
,
507 cPB
->hFileInfo
.ioNamePtr
[0] + 1);
508 /* Use the same non-international call the File Manager uses */
509 UpperString(itemName
, true);
517 if ( !IsSubString(itemName
, matchName
) )
521 else if ( searchBits
== fsSBPartialName
)
523 /* optimize for name matching only since it is most common way to search */
529 if ( (searchBits
& fsSBFullName
) != 0 )
531 /* Use the same non-international call the File Manager uses */
532 if ( !EqualString(cPB
->hFileInfo
.ioNamePtr
, matchName
, false, true) )
536 else if ( searchBits
== fsSBFullName
)
538 /* optimize for name matching only since it is most common way to search */
543 if ( (searchBits
& fsSBFlParID
) != 0 )
545 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlParID
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlParID
)) ||
546 ((unsigned long)(cPB
->hFileInfo
.ioFlParID
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlParID
)) )
552 if ( (searchBits
& fsSBFlAttrib
) != 0 )
554 if ( ((cPB
->hFileInfo
.ioFlAttrib
^ searchInfo1
->hFileInfo
.ioFlAttrib
) &
555 searchInfo2
->hFileInfo
.ioFlAttrib
) != 0 )
561 if ( (searchBits
& fsSBDrNmFls
) != 0 )
563 if ( ((unsigned long)(cPB
->dirInfo
.ioDrNmFls
) < (unsigned long)(searchInfo1
->dirInfo
.ioDrNmFls
)) ||
564 ((unsigned long)(cPB
->dirInfo
.ioDrNmFls
) > (unsigned long)(searchInfo2
->dirInfo
.ioDrNmFls
)) )
570 if ( (searchBits
& fsSBFlFndrInfo
) != 0 ) /* fsSBFlFndrInfo is same as fsSBDrUsrWds */
572 if ( !CompareMasked((long *)&(cPB
->hFileInfo
.ioFlFndrInfo
),
573 (long *)&(searchInfo1
->hFileInfo
.ioFlFndrInfo
),
574 (long *)&(searchInfo2
->hFileInfo
.ioFlFndrInfo
),
575 sizeof(FInfo
) / sizeof(long)) )
581 if ( (searchBits
& fsSBFlXFndrInfo
) != 0 ) /* fsSBFlXFndrInfo is same as fsSBDrFndrInfo */
583 if ( !CompareMasked((long *)&(cPB
->hFileInfo
.ioFlXFndrInfo
),
584 (long *)&(searchInfo1
->hFileInfo
.ioFlXFndrInfo
),
585 (long *)&(searchInfo2
->hFileInfo
.ioFlXFndrInfo
),
586 sizeof(FXInfo
) / sizeof(long)) )
592 if ( (searchBits
& fsSBFlLgLen
) != 0 )
594 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlLgLen
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlLgLen
)) ||
595 ((unsigned long)(cPB
->hFileInfo
.ioFlLgLen
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlLgLen
)) )
601 if ( (searchBits
& fsSBFlPyLen
) != 0 )
603 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlPyLen
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlPyLen
)) ||
604 ((unsigned long)(cPB
->hFileInfo
.ioFlPyLen
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlPyLen
)) )
610 if ( (searchBits
& fsSBFlRLgLen
) != 0 )
612 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlRLgLen
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlRLgLen
)) ||
613 ((unsigned long)(cPB
->hFileInfo
.ioFlRLgLen
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlRLgLen
)) )
619 if ( (searchBits
& fsSBFlRPyLen
) != 0 )
621 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlRPyLen
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlRPyLen
)) ||
622 ((unsigned long)(cPB
->hFileInfo
.ioFlRPyLen
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlRPyLen
)) )
628 if ( (searchBits
& fsSBFlCrDat
) != 0 ) /* fsSBFlCrDat is same as fsSBDrCrDat */
630 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlCrDat
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlCrDat
)) ||
631 ((unsigned long)(cPB
->hFileInfo
.ioFlCrDat
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlCrDat
)) )
637 if ( (searchBits
& fsSBFlMdDat
) != 0 ) /* fsSBFlMdDat is same as fsSBDrMdDat */
639 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlMdDat
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlMdDat
)) ||
640 ((unsigned long)(cPB
->hFileInfo
.ioFlMdDat
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlMdDat
)) )
646 if ( (searchBits
& fsSBFlBkDat
) != 0 ) /* fsSBFlBkDat is same as fsSBDrBkDat */
648 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlBkDat
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlBkDat
)) ||
649 ((unsigned long)(cPB
->hFileInfo
.ioFlBkDat
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlBkDat
)) )
655 /* Hey, we passed all of the tests! */
660 /* foundMatch is false if code jumps to Failed */
662 /* Do we reverse our findings? */
663 if ( (searchBits
& fsSBNegate
) != 0 )
665 foundMatch
= !foundMatch
; /* matches are not, not matches are */
671 /* Move the match into the match buffer */
672 userPB
->ioMatchPtr
[userPB
->ioActMatchCount
].vRefNum
= cPB
->hFileInfo
.ioVRefNum
;
673 userPB
->ioMatchPtr
[userPB
->ioActMatchCount
].parID
= cPB
->hFileInfo
.ioFlParID
;
674 if ( cPB
->hFileInfo
.ioNamePtr
[0] > 63 )
676 cPB
->hFileInfo
.ioNamePtr
[0] = 63;
678 BlockMoveData(cPB
->hFileInfo
.ioNamePtr
,
679 userPB
->ioMatchPtr
[userPB
->ioActMatchCount
].name
,
680 cPB
->hFileInfo
.ioNamePtr
[0] + 1);
682 /* increment the actual count */
683 ++(userPB
->ioActMatchCount
);
687 /*****************************************************************************/
690 ** TimeOutTask is executed when the timer goes off. It simply sets the
691 ** stopSearch field to true. After each object is found and possibly added
692 ** to the matches buffer, stopSearch is checked to see if the search should
696 #if __WANTPASCALELIMINATION
700 #if TARGET_RT_MAC_CFM || TARGET_API_MAC_CARBON
702 static pascal void TimeOutTask(TMTaskPtr tmTaskPtr
)
704 ((ExtendedTMTaskPtr
)tmTaskPtr
)->stopSearch
= true;
709 static pascal TMTaskPtr
GetTMTaskPtr(void)
710 ONEWORDINLINE(0x2e89); /* MOVE.L A1,(SP) */
712 static void TimeOutTask(void)
714 ((ExtendedTMTaskPtr
)GetTMTaskPtr())->stopSearch
= true;
719 #if __WANTPASCALELIMINATION
723 /*****************************************************************************/
726 ** GetDirModDate returns the modification date of a directory. If there is
727 ** an error getting the modification date, -1 is returned to indicate
728 ** something went wrong.
730 static long GetDirModDate(short vRefNum
,
737 /* Protection against File Sharing problem */
739 pb
.dirInfo
.ioNamePtr
= tempName
;
740 pb
.dirInfo
.ioVRefNum
= vRefNum
;
741 pb
.dirInfo
.ioDrDirID
= dirID
;
742 pb
.dirInfo
.ioFDirIndex
= -1; /* use ioDrDirID */
744 if ( PBGetCatInfoSync(&pb
) == noErr
)
746 modDate
= pb
.dirInfo
.ioDrMdDat
;
756 /*****************************************************************************/
758 pascal OSErr
IndexedSearch(CSParamPtr pb
,
761 static LevelRecHandle searchStack
= NULL
; /* static handle to LevelRec stack */
762 static Size searchStackSize
= 0; /* size of static handle */
763 SearchPositionRecPtr catPosition
;
766 ExtendedTMTask timerTask
;
772 Boolean includeFiles
;
774 Boolean includeNames
;
777 timerTask
.stopSearch
= false; /* don't stop yet! */
779 /* If request has a timeout, install a Time Manager task. */
780 if ( pb
->ioSearchTime
!= 0 )
783 timerTask
.theTask
.tmAddr
= NewTimerUPP(TimeOutTask
);
784 InsTime((QElemPtr
)&(timerTask
.theTask
));
785 PrimeTime((QElemPtr
)&(timerTask
.theTask
), pb
->ioSearchTime
);
788 /* Check the parameter block passed for things that we don't want to assume */
789 /* are OK later in the code. For example, make sure pointers to data structures */
790 /* and buffers are not NULL. And while we're in there, see if the request */
791 /* specified searching for files, directories, or both, and see if the search */
792 /* was by full or partial name. */
793 result
= VerifyUserPB(pb
, &includeFiles
, &includeDirs
, &includeNames
);
794 if ( result
== noErr
)
796 pb
->ioActMatchCount
= 0; /* no matches yet */
800 /* The search includes seach by full or partial name. */
801 /* Make an upper case copy of the match string to pass to */
802 /* CheckForMatches. */
803 BlockMoveData(pb
->ioSearchInfo1
->hFileInfo
.ioNamePtr
,
805 pb
->ioSearchInfo1
->hFileInfo
.ioNamePtr
[0] + 1);
806 /* Use the same non-international call the File Manager uses */
807 UpperString(upperName
, true);
810 /* Prevent casting to my type throughout code */
811 catPosition
= (SearchPositionRecPtr
)&pb
->ioCatPosition
;
813 /* Create searchStack first time called */
814 if ( searchStack
== NULL
)
816 searchStack
= (LevelRecHandle
)NewHandle(kAdditionalLevelRecs
* sizeof(LevelRec
));
819 /* Make sure searchStack really exists */
820 if ( searchStack
!= NULL
)
822 searchStackSize
= GetHandleSize((Handle
)searchStack
);
824 /* See if the search is a new search or a resumed search. */
825 if ( catPosition
->initialize
== 0 )
829 /* Get the real vRefNum and fill in catPosition->initialize. */
830 result
= CheckVol(pb
->ioNamePtr
, pb
->ioVRefNum
, &realVRefNum
, &catPosition
->initialize
);
831 if ( result
== noErr
)
833 /* clear searchStack */
834 catPosition
->stackDepth
= 0;
836 /* use dirID parameter passed and... */
837 index
= -1; /* start with the passed directory itself! */
842 /* We're resuming a search. */
844 /* Get the real vRefNum and make sure catPosition->initialize is valid. */
845 result
= CheckVol(pb
->ioNamePtr
, pb
->ioVRefNum
, &realVRefNum
, &tempLong
);
846 if ( result
== noErr
)
848 /* Make sure the resumed search is to the same volume! */
849 if ( catPosition
->initialize
== tempLong
)
851 /* For resume, catPosition->stackDepth > 0 */
852 if ( catPosition
->stackDepth
> 0 )
854 /* Position catPosition->stackDepth to access last saved level */
855 --(catPosition
->stackDepth
);
857 /* Get the dirID and index for the next item */
858 dirID
= (*searchStack
)[catPosition
->stackDepth
].dirID
;
859 index
= (*searchStack
)[catPosition
->stackDepth
].index
;
861 /* Check the dir's mod date against the saved mode date on our "stack" */
862 modDate
= GetDirModDate(realVRefNum
, dirID
);
863 if ( modDate
!= (*searchStack
)[catPosition
->stackDepth
].dirModDate
)
865 result
= catChangedErr
;
870 /* Invalid catPosition record was passed */
876 /* The volume is not the same */
877 result
= catChangedErr
;
882 if ( result
== noErr
)
884 /* ioNamePtr and ioVRefNum only need to be set up once. */
885 cPB
.hFileInfo
.ioNamePtr
= itemName
;
886 cPB
.hFileInfo
.ioVRefNum
= realVRefNum
;
889 ** Here's the loop that:
890 ** Finds the next item on the volume.
891 ** If noErr, calls the code to check for matches and add matches
892 ** to the match buffer.
893 ** Sets up dirID and index for to find the next item on the volume.
895 ** The looping ends when:
896 ** (a) an unexpected error is returned by PBGetCatInfo. All that
897 ** is expected is noErr and fnfErr (after the last item in a
898 ** directory is found).
899 ** (b) the caller specified a timeout and our Time Manager task
901 ** (c) the number of matches requested by the caller has been found.
902 ** (d) the last item on the volume was found.
906 /* get the next item */
907 cPB
.hFileInfo
.ioFDirIndex
= index
;
908 cPB
.hFileInfo
.ioDirID
= dirID
;
909 result
= PBGetCatInfoSync(&cPB
);
912 if ( result
== noErr
)
914 /* We found something */
916 CheckForMatches(&cPB
, pb
, upperName
, includeFiles
, includeDirs
);
919 if ( (cPB
.dirInfo
.ioFlAttrib
& kioFlAttribDirMask
) != 0 )
921 /* It's a directory */
923 result
= CheckStack(catPosition
->stackDepth
, searchStack
, &searchStackSize
);
924 if ( result
== noErr
)
926 /* Save the current state on the searchStack */
927 /* when we come back, this is where we'll start */
928 (*searchStack
)[catPosition
->stackDepth
].dirID
= dirID
;
929 (*searchStack
)[catPosition
->stackDepth
].index
= index
;
930 (*searchStack
)[catPosition
->stackDepth
].dirModDate
= GetDirModDate(realVRefNum
, dirID
);
932 /* position catPosition->stackDepth for next saved level */
933 ++(catPosition
->stackDepth
);
935 /* The next item to get is the 1st item in the child directory */
936 dirID
= cPB
.dirInfo
.ioDrDirID
;
940 /* else do nothing for files */
944 /* End of directory found (or we had some error and that */
945 /* means we have to drop out of this directory). */
946 /* Restore last thing put on stack and */
947 /* see if we need to continue or quit. */
948 if ( catPosition
->stackDepth
> 0 )
950 /* position catPosition->stackDepth to access last saved level */
951 --(catPosition
->stackDepth
);
953 dirID
= (*searchStack
)[catPosition
->stackDepth
].dirID
;
954 index
= (*searchStack
)[catPosition
->stackDepth
].index
;
956 /* Check the dir's mod date against the saved mode date on our "stack" */
957 modDate
= GetDirModDate(realVRefNum
, dirID
);
958 if ( modDate
!= (*searchStack
)[catPosition
->stackDepth
].dirModDate
)
960 result
= catChangedErr
;
964 /* Going back to ancestor directory. */
965 /* Clear error so we can continue. */
971 /* We hit the bottom of the stack, so we'll let the */
972 /* the eofErr drop us out of the loop. */
979 /* Special case for index == -1; that means that we're starting */
980 /* a new search and so the first item to check is the directory */
982 if ( result
== noErr
)
984 /* We found something */
986 CheckForMatches(&cPB
, pb
, upperName
, includeFiles
, includeDirs
);
988 /* Now, set the index to 1 and then we're ready to look inside */
989 /* the passed directory. */
993 } while ( (!timerTask
.stopSearch
) && /* timer hasn't fired */
994 (result
== noErr
) && /* no unexpected errors */
995 (pb
->ioReqMatchCount
> pb
->ioActMatchCount
) ); /* we haven't found our limit */
997 /* Did we drop out of the loop because of timeout or */
998 /* ioReqMatchCount was found? */
999 if ( result
== noErr
)
1001 result
= CheckStack(catPosition
->stackDepth
, searchStack
, &searchStackSize
);
1002 if ( result
== noErr
)
1004 /* Either there was a timeout or ioReqMatchCount was reached. */
1005 /* Save the dirID and index for the next time we're called. */
1007 (*searchStack
)[catPosition
->stackDepth
].dirID
= dirID
;
1008 (*searchStack
)[catPosition
->stackDepth
].index
= index
;
1009 (*searchStack
)[catPosition
->stackDepth
].dirModDate
= GetDirModDate(realVRefNum
, dirID
);
1011 /* position catPosition->stackDepth for next saved level */
1013 ++(catPosition
->stackDepth
);
1020 /* searchStack Handle could not be allocated */
1021 result
= memFullErr
;
1025 if ( pb
->ioSearchTime
!= 0 )
1027 /* Stop Time Manager task here if it was installed */
1028 RmvTime((QElemPtr
)&(timerTask
.theTask
));
1029 DisposeTimerUPP(timerTask
.theTask
.tmAddr
);
1035 /*****************************************************************************/
1037 pascal OSErr
PBCatSearchSyncCompat(CSParamPtr paramBlock
)
1040 Boolean supportsCatSearch
;
1041 GetVolParmsInfoBuffer volParmsInfo
;
1043 #if !__MACOSSEVENORLATER
1044 static Boolean fullExtFSDispatchingtested
= false;
1045 static Boolean hasFullExtFSDispatching
= false;
1051 #if !__MACOSSEVENORLATER
1052 /* See if File Manager will pass CatSearch requests to external file systems */
1053 /* we'll store the results in a static variable so we don't have to call Gestalt */
1054 /* everytime we're called. (System 7.0 and later always do this) */
1055 if ( !fullExtFSDispatchingtested
)
1057 fullExtFSDispatchingtested
= true;
1058 if ( Gestalt(gestaltFSAttr
, &response
) == noErr
)
1060 hasFullExtFSDispatching
= ((response
& (1L << gestaltFullExtFSDispatching
)) != 0);
1065 /* CatSearch is a per volume attribute, so we have to check each time we're */
1066 /* called to see if it is available on the volume specified. */
1067 supportsCatSearch
= false;
1068 #if !__MACOSSEVENORLATER
1069 if ( hasFullExtFSDispatching
)
1072 infoSize
= sizeof(GetVolParmsInfoBuffer
);
1073 result
= HGetVolParms(paramBlock
->ioNamePtr
, paramBlock
->ioVRefNum
,
1074 &volParmsInfo
, &infoSize
);
1075 if ( result
== noErr
)
1077 supportsCatSearch
= hasCatSearch(&volParmsInfo
);
1081 /* noErr or paramErr is OK here. */
1082 /* paramErr just means that GetVolParms isn't supported by this volume */
1083 if ( (result
== noErr
) || (result
== paramErr
) )
1085 if ( supportsCatSearch
)
1087 /* Volume supports CatSearch so use it. */
1088 /* CatSearch is faster than an indexed search. */
1089 result
= PBCatSearchSync(paramBlock
);
1093 /* Volume doesn't support CatSearch so */
1094 /* search using IndexedSearch from root directory. */
1095 result
= IndexedSearch(paramBlock
, fsRtDirID
);
1102 /*****************************************************************************/
1104 pascal OSErr
NameFileSearch(ConstStr255Param volName
,
1106 ConstStr255Param fileName
,
1109 long *actMatchCount
,
1113 CInfoPBRec searchInfo1
, searchInfo2
;
1116 static CatPositionRec catPosition
;
1117 static short lastVRefNum
= 0;
1119 /* get the real volume reference number */
1120 error
= DetermineVRefNum(volName
, vRefNum
, &vRefNum
);
1121 if ( error
!= noErr
)
1124 pb
.csParam
.ioNamePtr
= NULL
;
1125 pb
.csParam
.ioVRefNum
= vRefNum
;
1126 pb
.csParam
.ioMatchPtr
= matches
;
1127 pb
.csParam
.ioReqMatchCount
= reqMatchCount
;
1128 if ( partial
) /* tell CatSearch what we're looking for: */
1130 pb
.csParam
.ioSearchBits
= fsSBPartialName
+ fsSBFlAttrib
; /* partial name file matches or */
1134 pb
.csParam
.ioSearchBits
= fsSBFullName
+ fsSBFlAttrib
; /* full name file matches */
1136 pb
.csParam
.ioSearchInfo1
= &searchInfo1
;
1137 pb
.csParam
.ioSearchInfo2
= &searchInfo2
;
1138 pb
.csParam
.ioSearchTime
= 0;
1139 if ( (newSearch
) || /* If caller specified new search */
1140 (lastVRefNum
!= vRefNum
) ) /* or if last search was to another volume, */
1142 catPosition
.initialize
= 0; /* then search from beginning of catalog */
1144 pb
.csParam
.ioCatPosition
= catPosition
;
1145 pb
.csParam
.ioOptBuffer
= GetTempBuffer(0x00004000, &pb
.csParam
.ioOptBufSize
);
1147 /* search for fileName */
1148 searchInfo1
.hFileInfo
.ioNamePtr
= (StringPtr
)fileName
;
1149 searchInfo2
.hFileInfo
.ioNamePtr
= NULL
;
1151 /* only match files (not directories) */
1152 searchInfo1
.hFileInfo
.ioFlAttrib
= 0x00;
1153 searchInfo2
.hFileInfo
.ioFlAttrib
= kioFlAttribDirMask
;
1155 error
= PBCatSearchSyncCompat((CSParamPtr
)&pb
);
1157 if ( (error
== noErr
) || /* If no errors or the end of catalog was */
1158 (error
== eofErr
) ) /* found, then the call was successful so */
1160 *actMatchCount
= pb
.csParam
.ioActMatchCount
; /* return the match count */
1164 *actMatchCount
= 0; /* else no matches found */
1167 if ( (error
== noErr
) || /* If no errors */
1168 (error
== catChangedErr
) ) /* or there was a change in the catalog */
1170 catPosition
= pb
.csParam
.ioCatPosition
;
1171 lastVRefNum
= vRefNum
;
1172 /* we can probably start the next search where we stopped this time */
1176 catPosition
.initialize
= 0;
1177 /* start the next search from beginning of catalog */
1180 if ( pb
.csParam
.ioOptBuffer
!= NULL
)
1182 DisposePtr(pb
.csParam
.ioOptBuffer
);
1188 /*****************************************************************************/
1190 pascal OSErr
CreatorTypeFileSearch(ConstStr255Param volName
,
1196 long *actMatchCount
,
1199 CInfoPBRec searchInfo1
, searchInfo2
;
1202 static CatPositionRec catPosition
;
1203 static short lastVRefNum
= 0;
1205 /* get the real volume reference number */
1206 error
= DetermineVRefNum(volName
, vRefNum
, &vRefNum
);
1207 if ( error
!= noErr
)
1210 pb
.csParam
.ioNamePtr
= NULL
;
1211 pb
.csParam
.ioVRefNum
= vRefNum
;
1212 pb
.csParam
.ioMatchPtr
= matches
;
1213 pb
.csParam
.ioReqMatchCount
= reqMatchCount
;
1214 pb
.csParam
.ioSearchBits
= fsSBFlAttrib
+ fsSBFlFndrInfo
; /* Looking for finder info file matches */
1215 pb
.csParam
.ioSearchInfo1
= &searchInfo1
;
1216 pb
.csParam
.ioSearchInfo2
= &searchInfo2
;
1217 pb
.csParam
.ioSearchTime
= 0;
1218 if ( (newSearch
) || /* If caller specified new search */
1219 (lastVRefNum
!= vRefNum
) ) /* or if last search was to another volume, */
1221 catPosition
.initialize
= 0; /* then search from beginning of catalog */
1223 pb
.csParam
.ioCatPosition
= catPosition
;
1224 pb
.csParam
.ioOptBuffer
= GetTempBuffer(0x00004000, &pb
.csParam
.ioOptBufSize
);
1227 searchInfo1
.hFileInfo
.ioNamePtr
= NULL
;
1228 searchInfo2
.hFileInfo
.ioNamePtr
= NULL
;
1230 /* only match files (not directories) */
1231 searchInfo1
.hFileInfo
.ioFlAttrib
= 0x00;
1232 searchInfo2
.hFileInfo
.ioFlAttrib
= kioFlAttribDirMask
;
1234 /* search for creator; if creator = 0x00000000, ignore creator */
1235 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdCreator
= creator
;
1236 if ( creator
== (OSType
)0x00000000 )
1238 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdCreator
= (OSType
)0x00000000;
1242 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdCreator
= (OSType
)0xffffffff;
1245 /* search for fileType; if fileType = 0x00000000, ignore fileType */
1246 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdType
= fileType
;
1247 if ( fileType
== (OSType
)0x00000000 )
1249 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdType
= (OSType
)0x00000000;
1253 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdType
= (OSType
)0xffffffff;
1256 /* zero all other FInfo fields */
1257 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdFlags
= 0;
1258 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdLocation
.v
= 0;
1259 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdLocation
.h
= 0;
1260 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdFldr
= 0;
1262 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdFlags
= 0;
1263 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdLocation
.v
= 0;
1264 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdLocation
.h
= 0;
1265 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdFldr
= 0;
1267 error
= PBCatSearchSyncCompat((CSParamPtr
)&pb
);
1269 if ( (error
== noErr
) || /* If no errors or the end of catalog was */
1270 (error
== eofErr
) ) /* found, then the call was successful so */
1272 *actMatchCount
= pb
.csParam
.ioActMatchCount
; /* return the match count */
1276 *actMatchCount
= 0; /* else no matches found */
1279 if ( (error
== noErr
) || /* If no errors */
1280 (error
== catChangedErr
) ) /* or there was a change in the catalog */
1282 catPosition
= pb
.csParam
.ioCatPosition
;
1283 lastVRefNum
= vRefNum
;
1284 /* we can probably start the next search where we stopped this time */
1288 catPosition
.initialize
= 0;
1289 /* start the next search from beginning of catalog */
1292 if ( pb
.csParam
.ioOptBuffer
!= NULL
)
1294 DisposePtr(pb
.csParam
.ioOptBuffer
);
1300 /*****************************************************************************/