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_STRUCT_ALIGN
70 #pragma options align=mac68k
71 #elif PRAGMA_STRUCT_PACKPUSH
73 #elif PRAGMA_STRUCT_PACK
76 struct SearchPositionRec
78 long initialize
; /* Goofy checksum of volume information used to make */
79 /* sure we're resuming a search on the same volume. */
80 unsigned short stackDepth
; /* Current depth on searchStack. */
81 short priv
[11]; /* For future use... */
83 #if PRAGMA_STRUCT_ALIGN
84 #pragma options align=reset
85 #elif PRAGMA_STRUCT_PACKPUSH
87 #elif PRAGMA_STRUCT_PACK
90 typedef struct SearchPositionRec SearchPositionRec
;
91 typedef SearchPositionRec
*SearchPositionRecPtr
;
95 ** ExtendedTMTask is a TMTask record extended to hold the timer flag.
97 #if PRAGMA_STRUCT_ALIGN
98 #pragma options align=mac68k
99 #elif PRAGMA_STRUCT_PACKPUSH
100 #pragma pack(push, 2)
101 #elif PRAGMA_STRUCT_PACK
104 struct ExtendedTMTask
107 Boolean stopSearch
; /* the Time Mgr task will set stopSearch to */
108 /* true when the timer expires */
110 #if PRAGMA_STRUCT_ALIGN
111 #pragma options align=reset
112 #elif PRAGMA_STRUCT_PACKPUSH
114 #elif PRAGMA_STRUCT_PACK
117 typedef struct ExtendedTMTask ExtendedTMTask
;
118 typedef ExtendedTMTask
*ExtendedTMTaskPtr
;
120 /*****************************************************************************/
122 static OSErr
CheckVol(ConstStr255Param pathname
,
127 static OSErr
CheckStack(unsigned short stackDepth
,
128 LevelRecHandle searchStack
,
129 Size
*searchStackSize
);
131 static OSErr
VerifyUserPB(CSParamPtr userPB
,
132 Boolean
*includeFiles
,
133 Boolean
*includeDirs
,
134 Boolean
*includeNames
);
136 static Boolean
IsSubString(ConstStr255Param aStringPtr
,
137 ConstStr255Param subStringPtr
);
139 static Boolean
CompareMasked(const long *data1
,
142 short longsToCompare
);
144 static void CheckForMatches(CInfoPBPtr cPB
,
146 const Str63 matchName
,
147 Boolean includeFiles
,
148 Boolean includeDirs
);
150 #if __WANTPASCALELIMINATION
155 static pascal void TimeOutTask(TMTaskPtr tmTaskPtr
);
160 static pascal void TimeOutTask(TMTaskPtr tmTaskPtr
);
164 static pascal TMTaskPtr
GetTMTaskPtr(void);
166 static void TimeOutTask(void);
171 #if __WANTPASCALELIMINATION
175 static long GetDirModDate(short vRefNum
,
178 /*****************************************************************************/
181 ** CheckVol gets the volume's real vRefNum and builds a volID. The volID
182 ** is used to help insure that calls to resume searching with IndexedSearch
183 ** are to the same volume as the last call to IndexedSearch.
185 static OSErr
CheckVol(ConstStr255Param pathname
,
193 error
= GetVolumeInfoNoName(pathname
, vRefNum
, &pb
);
194 if ( error
== noErr
)
196 /* Return the real vRefNum */
197 *realVRefNum
= pb
.volumeParam
.ioVRefNum
;
199 /* Add together a bunch of things that aren't supposed to change on */
200 /* a mounted volume that's being searched and that should come up with */
201 /* a fairly unique number */
202 *volID
= pb
.volumeParam
.ioVCrDate
+
203 pb
.volumeParam
.ioVRefNum
+
204 pb
.volumeParam
.ioVNmAlBlks
+
205 pb
.volumeParam
.ioVAlBlkSiz
+
206 pb
.volumeParam
.ioVFSID
;
211 /*****************************************************************************/
214 ** CheckStack checks the size of the search stack (array) to see if there's
215 ** room to push another LevelRec. If not, CheckStack grows the stack by
216 ** another kAdditionalLevelRecs elements.
218 static OSErr
CheckStack(unsigned short stackDepth
,
219 LevelRecHandle searchStack
,
220 Size
*searchStackSize
)
224 if ( (*searchStackSize
/ sizeof(LevelRec
)) == (stackDepth
+ 1) )
226 /* Time to grow stack */
227 SetHandleSize((Handle
)searchStack
, *searchStackSize
+ (kAdditionalLevelRecs
* sizeof(LevelRec
)));
228 result
= MemError(); /* should be noErr */
230 *searchStackSize
= GetHandleSize((Handle
)searchStack
);
232 *searchStackSize
= InlineGetHandleSize((Handle
)searchStack
);
243 /*****************************************************************************/
246 ** VerifyUserPB makes sure the parameter block passed to IndexedSearch has
247 ** valid parameters. By making this check once, we don't have to worry about
248 ** things like NULL pointers, strings being too long, etc.
249 ** VerifyUserPB also determines if the search includes files and/or
250 ** directories, and determines if a full or partial name search was requested.
252 static OSErr
VerifyUserPB(CSParamPtr userPB
,
253 Boolean
*includeFiles
,
254 Boolean
*includeDirs
,
255 Boolean
*includeNames
)
257 CInfoPBPtr searchInfo1
;
258 CInfoPBPtr searchInfo2
;
260 searchInfo1
= userPB
->ioSearchInfo1
;
261 searchInfo2
= userPB
->ioSearchInfo2
;
263 /* ioMatchPtr cannot be NULL */
264 if ( userPB
->ioMatchPtr
== NULL
)
269 /* ioSearchInfo1 cannot be NULL */
270 if ( searchInfo1
== NULL
)
275 /* If any bits except partialName, fullName, or negate are set, then */
276 /* ioSearchInfo2 cannot be NULL because information in ioSearchInfo2 is required */
277 if ( ((userPB
->ioSearchBits
& ~(fsSBPartialName
| fsSBFullName
| fsSBNegate
)) != 0) &&
278 ( searchInfo2
== NULL
))
283 *includeFiles
= false;
284 *includeDirs
= false;
285 *includeNames
= false;
287 if ( (userPB
->ioSearchBits
& (fsSBPartialName
| fsSBFullName
)) != 0 )
289 /* If any kind of name matching is requested, then ioNamePtr in */
290 /* ioSearchInfo1 cannot be NULL or a zero-length string */
291 if ( (searchInfo1
->hFileInfo
.ioNamePtr
== NULL
) ||
292 (searchInfo1
->hFileInfo
.ioNamePtr
[0] == 0) ||
293 (searchInfo1
->hFileInfo
.ioNamePtr
[0] > (sizeof(Str63
) - 1)) )
298 *includeNames
= true;
301 if ( (userPB
->ioSearchBits
& fsSBFlAttrib
) != 0 )
303 /* The only attributes you can search on are the directory flag */
304 /* and the locked flag. */
305 if ( (searchInfo2
->hFileInfo
.ioFlAttrib
& ~(ioDirMask
| 0x01)) != 0 )
310 /* interested in the directory bit? */
311 if ( (searchInfo2
->hFileInfo
.ioFlAttrib
& ioDirMask
) != 0 )
313 /* yes, so do they want just directories or just files? */
314 if ( (searchInfo1
->hFileInfo
.ioFlAttrib
& ioDirMask
) != 0 )
320 *includeFiles
= true;
325 /* no interest in directory bit - get both files and directories */
327 *includeFiles
= true;
332 /* no attribute checking - get both files and directories */
334 *includeFiles
= true;
337 /* If directories are included in the search, */
338 /* then the locked attribute cannot be requested. */
340 ((userPB
->ioSearchBits
& fsSBFlAttrib
) != 0) &&
341 ((searchInfo2
->hFileInfo
.ioFlAttrib
& 0x01) != 0) )
346 /* If files are included in the search, then there cannot be */
347 /* a search on the number of files. */
348 if ( *includeFiles
&&
349 ((userPB
->ioSearchBits
& fsSBDrNmFls
) != 0) )
354 /* If directories are included in the search, then there cannot */
355 /* be a search on file lengths. */
357 ((userPB
->ioSearchBits
& (fsSBFlLgLen
| fsSBFlPyLen
| fsSBFlRLgLen
| fsSBFlRPyLen
)) != 0) )
368 /*****************************************************************************/
371 ** IsSubString checks to see if a string is a substring of another string.
372 ** Both input strings have already been converted to all uppercase using
373 ** UprString (the same non-international call the File Manager uses).
375 static Boolean
IsSubString(ConstStr255Param aStringPtr
,
376 ConstStr255Param subStringPtr
)
378 short strLength
; /* length of string */
379 short subStrLength
; /* length of subString */
380 Boolean found
; /* result of test */
381 short index
; /* current index into string */
384 strLength
= aStringPtr
[0];
385 subStrLength
= subStringPtr
[0];
387 if ( subStrLength
<= strLength
)
389 register short count
; /* search counter */
390 register short strIndex
; /* running index into string */
391 register short subStrIndex
; /* running index into subString */
393 /* start looking at first character */
396 /* continue looking until remaining string is shorter than substring */
397 count
= strLength
- subStrLength
+ 1;
401 strIndex
= index
; /* start string index at index */
402 subStrIndex
= 1; /* start subString index at 1 */
404 while ( !found
&& (aStringPtr
[strIndex
] == subStringPtr
[subStrIndex
]) )
406 if ( subStrIndex
== subStrLength
)
408 /* all characters in subString were found */
413 /* check next character of substring against next character of string */
421 /* start substring search again at next string character */
425 } while ( count
!= 0 && (!found
) );
431 /*****************************************************************************/
434 ** CompareMasked does a bitwise comparison with mask on 1 or more longs.
435 ** data1 and data2 are first exclusive-ORed together resulting with bits set
436 ** where they are different. That value is then ANDed with the mask resulting
437 ** with bits set if the test fails. true is returned if the tests pass.
439 static Boolean
CompareMasked(const long *data1
,
442 short longsToCompare
)
444 Boolean result
= true;
446 while ( (longsToCompare
!= 0) && (result
== true) )
448 /* (*data1 ^ *data2) = bits that are different, so... */
449 /* ((*data1 ^ *data2) & *mask) = bits that are different that we're interested in */
451 if ( ((*data1
^ *data2
) & *mask
) != 0 )
463 /*****************************************************************************/
466 ** Check for matches compares the search criteria in userPB to the file
467 ** system object in cPB. If there's a match, then the information in cPB is
468 ** is added to the match array and the actual match count is incremented.
470 static void CheckForMatches(CInfoPBPtr cPB
,
472 const Str63 matchName
,
473 Boolean includeFiles
,
477 CInfoPBPtr searchInfo1
;
478 CInfoPBPtr searchInfo2
;
479 Str63 itemName
; /* copy of object's name for partial name matching */
482 foundMatch
= false; /* default to no match */
484 searchBits
= userPB
->ioSearchBits
;
485 searchInfo1
= userPB
->ioSearchInfo1
;
486 searchInfo2
= userPB
->ioSearchInfo2
;
488 /* Into the if statements that go on forever... */
490 if ( (cPB
->hFileInfo
.ioFlAttrib
& ioDirMask
) == 0 )
505 if ( (searchBits
& fsSBPartialName
) != 0 )
507 if ( (cPB
->hFileInfo
.ioNamePtr
[0] > 0) &&
508 (cPB
->hFileInfo
.ioNamePtr
[0] <= (sizeof(Str63
) - 1)) )
510 /* Make uppercase copy of object name */
511 BlockMoveData(cPB
->hFileInfo
.ioNamePtr
,
513 cPB
->hFileInfo
.ioNamePtr
[0] + 1);
514 /* Use the same non-international call the File Manager uses */
515 UpperString(itemName
, true);
523 if ( !IsSubString(itemName
, matchName
) )
527 else if ( searchBits
== fsSBPartialName
)
529 /* optimize for name matching only since it is most common way to search */
535 if ( (searchBits
& fsSBFullName
) != 0 )
537 /* Use the same non-international call the File Manager uses */
538 if ( !EqualString(cPB
->hFileInfo
.ioNamePtr
, matchName
, false, true) )
542 else if ( searchBits
== fsSBFullName
)
544 /* optimize for name matching only since it is most common way to search */
549 if ( (searchBits
& fsSBFlParID
) != 0 )
551 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlParID
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlParID
)) ||
552 ((unsigned long)(cPB
->hFileInfo
.ioFlParID
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlParID
)) )
558 if ( (searchBits
& fsSBFlAttrib
) != 0 )
560 if ( ((cPB
->hFileInfo
.ioFlAttrib
^ searchInfo1
->hFileInfo
.ioFlAttrib
) &
561 searchInfo2
->hFileInfo
.ioFlAttrib
) != 0 )
567 if ( (searchBits
& fsSBDrNmFls
) != 0 )
569 if ( ((unsigned long)(cPB
->dirInfo
.ioDrNmFls
) < (unsigned long)(searchInfo1
->dirInfo
.ioDrNmFls
)) ||
570 ((unsigned long)(cPB
->dirInfo
.ioDrNmFls
) > (unsigned long)(searchInfo2
->dirInfo
.ioDrNmFls
)) )
576 if ( (searchBits
& fsSBFlFndrInfo
) != 0 ) /* fsSBFlFndrInfo is same as fsSBDrUsrWds */
578 if ( !CompareMasked((long *)&(cPB
->hFileInfo
.ioFlFndrInfo
),
579 (long *)&(searchInfo1
->hFileInfo
.ioFlFndrInfo
),
580 (long *)&(searchInfo2
->hFileInfo
.ioFlFndrInfo
),
581 sizeof(FInfo
) / sizeof(long)) )
587 if ( (searchBits
& fsSBFlXFndrInfo
) != 0 ) /* fsSBFlXFndrInfo is same as fsSBDrFndrInfo */
589 if ( !CompareMasked((long *)&(cPB
->hFileInfo
.ioFlXFndrInfo
),
590 (long *)&(searchInfo1
->hFileInfo
.ioFlXFndrInfo
),
591 (long *)&(searchInfo2
->hFileInfo
.ioFlXFndrInfo
),
592 sizeof(FXInfo
) / sizeof(long)) )
598 if ( (searchBits
& fsSBFlLgLen
) != 0 )
600 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlLgLen
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlLgLen
)) ||
601 ((unsigned long)(cPB
->hFileInfo
.ioFlLgLen
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlLgLen
)) )
607 if ( (searchBits
& fsSBFlPyLen
) != 0 )
609 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlPyLen
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlPyLen
)) ||
610 ((unsigned long)(cPB
->hFileInfo
.ioFlPyLen
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlPyLen
)) )
616 if ( (searchBits
& fsSBFlRLgLen
) != 0 )
618 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlRLgLen
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlRLgLen
)) ||
619 ((unsigned long)(cPB
->hFileInfo
.ioFlRLgLen
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlRLgLen
)) )
625 if ( (searchBits
& fsSBFlRPyLen
) != 0 )
627 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlRPyLen
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlRPyLen
)) ||
628 ((unsigned long)(cPB
->hFileInfo
.ioFlRPyLen
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlRPyLen
)) )
634 if ( (searchBits
& fsSBFlCrDat
) != 0 ) /* fsSBFlCrDat is same as fsSBDrCrDat */
636 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlCrDat
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlCrDat
)) ||
637 ((unsigned long)(cPB
->hFileInfo
.ioFlCrDat
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlCrDat
)) )
643 if ( (searchBits
& fsSBFlMdDat
) != 0 ) /* fsSBFlMdDat is same as fsSBDrMdDat */
645 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlMdDat
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlMdDat
)) ||
646 ((unsigned long)(cPB
->hFileInfo
.ioFlMdDat
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlMdDat
)) )
652 if ( (searchBits
& fsSBFlBkDat
) != 0 ) /* fsSBFlBkDat is same as fsSBDrBkDat */
654 if ( ((unsigned long)(cPB
->hFileInfo
.ioFlBkDat
) < (unsigned long)(searchInfo1
->hFileInfo
.ioFlBkDat
)) ||
655 ((unsigned long)(cPB
->hFileInfo
.ioFlBkDat
) > (unsigned long)(searchInfo2
->hFileInfo
.ioFlBkDat
)) )
661 /* Hey, we passed all of the tests! */
666 /* foundMatch is false if code jumps to Failed */
668 /* Do we reverse our findings? */
669 if ( (searchBits
& fsSBNegate
) != 0 )
671 foundMatch
= !foundMatch
; /* matches are not, not matches are */
677 /* Move the match into the match buffer */
678 userPB
->ioMatchPtr
[userPB
->ioActMatchCount
].vRefNum
= cPB
->hFileInfo
.ioVRefNum
;
679 userPB
->ioMatchPtr
[userPB
->ioActMatchCount
].parID
= cPB
->hFileInfo
.ioFlParID
;
680 if ( cPB
->hFileInfo
.ioNamePtr
[0] > 63 )
682 cPB
->hFileInfo
.ioNamePtr
[0] = 63;
684 BlockMoveData(cPB
->hFileInfo
.ioNamePtr
,
685 userPB
->ioMatchPtr
[userPB
->ioActMatchCount
].name
,
686 cPB
->hFileInfo
.ioNamePtr
[0] + 1);
688 /* increment the actual count */
689 ++(userPB
->ioActMatchCount
);
693 /*****************************************************************************/
696 ** TimeOutTask is executed when the timer goes off. It simply sets the
697 ** stopSearch field to true. After each object is found and possibly added
698 ** to the matches buffer, stopSearch is checked to see if the search should
702 #if __WANTPASCALELIMINATION
706 static pascal void TimeOutTask(TMTaskPtr tmTaskPtr
)
708 ((ExtendedTMTaskPtr
)tmTaskPtr
)->stopSearch
= true;
713 static pascal void TimeOutTask(TMTaskPtr tmTaskPtr
)
715 ((ExtendedTMTaskPtr
)tmTaskPtr
)->stopSearch
= true;
720 static pascal TMTaskPtr
GetTMTaskPtr(void)
721 ONEWORDINLINE(0x2e89); /* MOVE.L A1,(SP) */
723 static void TimeOutTask(void)
725 ((ExtendedTMTaskPtr
)GetTMTaskPtr())->stopSearch
= true;
730 #if __WANTPASCALELIMINATION
734 /*****************************************************************************/
737 ** GetDirModDate returns the modification date of a directory. If there is
738 ** an error getting the modification date, -1 is returned to indicate
739 ** something went wrong.
741 static long GetDirModDate(short vRefNum
,
748 /* Protection against File Sharing problem */
750 pb
.dirInfo
.ioNamePtr
= tempName
;
751 pb
.dirInfo
.ioVRefNum
= vRefNum
;
752 pb
.dirInfo
.ioDrDirID
= dirID
;
753 pb
.dirInfo
.ioFDirIndex
= -1; /* use ioDrDirID */
755 if ( PBGetCatInfoSync(&pb
) == noErr
)
757 modDate
= pb
.dirInfo
.ioDrMdDat
;
767 /*****************************************************************************/
769 pascal OSErr
IndexedSearch(CSParamPtr pb
,
772 static LevelRecHandle searchStack
= NULL
; /* static handle to LevelRec stack */
773 static Size searchStackSize
= 0; /* size of static handle */
774 SearchPositionRecPtr catPosition
;
777 ExtendedTMTask timerTask
;
783 Boolean includeFiles
;
785 Boolean includeNames
;
788 timerTask
.stopSearch
= false; /* don't stop yet! */
790 /* If request has a timeout, install a Time Manager task. */
791 if ( pb
->ioSearchTime
!= 0 )
794 #if defined(UNIVERSAL_INTERFACES_VERSION) && (UNIVERSAL_INTERFACES_VERSION >= 0x0340)
795 timerTask
.theTask
.tmAddr
= NewTimerUPP(TimeOutTask
);
797 timerTask
.theTask
.tmAddr
= NewTimerProc(TimeOutTask
);
799 InsTime((QElemPtr
)&(timerTask
.theTask
));
800 PrimeTime((QElemPtr
)&(timerTask
.theTask
), pb
->ioSearchTime
);
803 /* Check the parameter block passed for things that we don't want to assume */
804 /* are OK later in the code. For example, make sure pointers to data structures */
805 /* and buffers are not NULL. And while we're in there, see if the request */
806 /* specified searching for files, directories, or both, and see if the search */
807 /* was by full or partial name. */
808 result
= VerifyUserPB(pb
, &includeFiles
, &includeDirs
, &includeNames
);
809 if ( result
== noErr
)
811 pb
->ioActMatchCount
= 0; /* no matches yet */
815 /* The search includes seach by full or partial name. */
816 /* Make an upper case copy of the match string to pass to */
817 /* CheckForMatches. */
818 BlockMoveData(pb
->ioSearchInfo1
->hFileInfo
.ioNamePtr
,
820 pb
->ioSearchInfo1
->hFileInfo
.ioNamePtr
[0] + 1);
821 /* Use the same non-international call the File Manager uses */
822 UpperString(upperName
, true);
825 /* Prevent casting to my type throughout code */
826 catPosition
= (SearchPositionRecPtr
)&pb
->ioCatPosition
;
828 /* Create searchStack first time called */
829 if ( searchStack
== NULL
)
831 searchStack
= (LevelRecHandle
)NewHandle(kAdditionalLevelRecs
* sizeof(LevelRec
));
834 /* Make sure searchStack really exists */
835 if ( searchStack
!= NULL
)
838 searchStackSize
= GetHandleSize((Handle
)searchStack
);
840 searchStackSize
= InlineGetHandleSize((Handle
)searchStack
);
843 /* See if the search is a new search or a resumed search. */
844 if ( catPosition
->initialize
== 0 )
848 /* Get the real vRefNum and fill in catPosition->initialize. */
849 result
= CheckVol(pb
->ioNamePtr
, pb
->ioVRefNum
, &realVRefNum
, &catPosition
->initialize
);
850 if ( result
== noErr
)
852 /* clear searchStack */
853 catPosition
->stackDepth
= 0;
855 /* use dirID parameter passed and... */
856 index
= -1; /* start with the passed directory itself! */
861 /* We're resuming a search. */
863 /* Get the real vRefNum and make sure catPosition->initialize is valid. */
864 result
= CheckVol(pb
->ioNamePtr
, pb
->ioVRefNum
, &realVRefNum
, &tempLong
);
865 if ( result
== noErr
)
867 /* Make sure the resumed search is to the same volume! */
868 if ( catPosition
->initialize
== tempLong
)
870 /* For resume, catPosition->stackDepth > 0 */
871 if ( catPosition
->stackDepth
> 0 )
873 /* Position catPosition->stackDepth to access last saved level */
874 --(catPosition
->stackDepth
);
876 /* Get the dirID and index for the next item */
877 dirID
= (*searchStack
)[catPosition
->stackDepth
].dirID
;
878 index
= (*searchStack
)[catPosition
->stackDepth
].index
;
880 /* Check the dir's mod date against the saved mode date on our "stack" */
881 modDate
= GetDirModDate(realVRefNum
, dirID
);
882 if ( modDate
!= (*searchStack
)[catPosition
->stackDepth
].dirModDate
)
884 result
= catChangedErr
;
889 /* Invalid catPosition record was passed */
895 /* The volume is not the same */
896 result
= catChangedErr
;
901 if ( result
== noErr
)
903 /* ioNamePtr and ioVRefNum only need to be set up once. */
904 cPB
.hFileInfo
.ioNamePtr
= itemName
;
905 cPB
.hFileInfo
.ioVRefNum
= realVRefNum
;
908 ** Here's the loop that:
909 ** Finds the next item on the volume.
910 ** If noErr, calls the code to check for matches and add matches
911 ** to the match buffer.
912 ** Sets up dirID and index for to find the next item on the volume.
914 ** The looping ends when:
915 ** (a) an unexpected error is returned by PBGetCatInfo. All that
916 ** is expected is noErr and fnfErr (after the last item in a
917 ** directory is found).
918 ** (b) the caller specified a timeout and our Time Manager task
920 ** (c) the number of matches requested by the caller has been found.
921 ** (d) the last item on the volume was found.
925 /* get the next item */
926 cPB
.hFileInfo
.ioFDirIndex
= index
;
927 cPB
.hFileInfo
.ioDirID
= dirID
;
928 result
= PBGetCatInfoSync(&cPB
);
931 if ( result
== noErr
)
933 /* We found something */
935 CheckForMatches(&cPB
, pb
, upperName
, includeFiles
, includeDirs
);
938 if ( (cPB
.dirInfo
.ioFlAttrib
& ioDirMask
) != 0 )
940 /* It's a directory */
942 result
= CheckStack(catPosition
->stackDepth
, searchStack
, &searchStackSize
);
943 if ( result
== noErr
)
945 /* Save the current state on the searchStack */
946 /* when we come back, this is where we'll start */
947 (*searchStack
)[catPosition
->stackDepth
].dirID
= dirID
;
948 (*searchStack
)[catPosition
->stackDepth
].index
= index
;
949 (*searchStack
)[catPosition
->stackDepth
].dirModDate
= GetDirModDate(realVRefNum
, dirID
);
951 /* position catPosition->stackDepth for next saved level */
952 ++(catPosition
->stackDepth
);
954 /* The next item to get is the 1st item in the child directory */
955 dirID
= cPB
.dirInfo
.ioDrDirID
;
959 /* else do nothing for files */
963 /* End of directory found (or we had some error and that */
964 /* means we have to drop out of this directory). */
965 /* Restore last thing put on stack and */
966 /* see if we need to continue or quit. */
967 if ( catPosition
->stackDepth
> 0 )
969 /* position catPosition->stackDepth to access last saved level */
970 --(catPosition
->stackDepth
);
972 dirID
= (*searchStack
)[catPosition
->stackDepth
].dirID
;
973 index
= (*searchStack
)[catPosition
->stackDepth
].index
;
975 /* Check the dir's mod date against the saved mode date on our "stack" */
976 modDate
= GetDirModDate(realVRefNum
, dirID
);
977 if ( modDate
!= (*searchStack
)[catPosition
->stackDepth
].dirModDate
)
979 result
= catChangedErr
;
983 /* Going back to ancestor directory. */
984 /* Clear error so we can continue. */
990 /* We hit the bottom of the stack, so we'll let the */
991 /* the eofErr drop us out of the loop. */
998 /* Special case for index == -1; that means that we're starting */
999 /* a new search and so the first item to check is the directory */
1001 if ( result
== noErr
)
1003 /* We found something */
1005 CheckForMatches(&cPB
, pb
, upperName
, includeFiles
, includeDirs
);
1007 /* Now, set the index to 1 and then we're ready to look inside */
1008 /* the passed directory. */
1012 } while ( (!timerTask
.stopSearch
) && /* timer hasn't fired */
1013 (result
== noErr
) && /* no unexpected errors */
1014 (pb
->ioReqMatchCount
> pb
->ioActMatchCount
) ); /* we haven't found our limit */
1016 /* Did we drop out of the loop because of timeout or */
1017 /* ioReqMatchCount was found? */
1018 if ( result
== noErr
)
1020 result
= CheckStack(catPosition
->stackDepth
, searchStack
, &searchStackSize
);
1021 if ( result
== noErr
)
1023 /* Either there was a timeout or ioReqMatchCount was reached. */
1024 /* Save the dirID and index for the next time we're called. */
1026 (*searchStack
)[catPosition
->stackDepth
].dirID
= dirID
;
1027 (*searchStack
)[catPosition
->stackDepth
].index
= index
;
1028 (*searchStack
)[catPosition
->stackDepth
].dirModDate
= GetDirModDate(realVRefNum
, dirID
);
1030 /* position catPosition->stackDepth for next saved level */
1032 ++(catPosition
->stackDepth
);
1039 /* searchStack Handle could not be allocated */
1040 result
= memFullErr
;
1044 if ( pb
->ioSearchTime
!= 0 )
1046 /* Stop Time Manager task here if it was installed */
1047 RmvTime((QElemPtr
)&(timerTask
.theTask
));
1048 DisposeTimerUPP(timerTask
.theTask
.tmAddr
);
1054 /*****************************************************************************/
1056 pascal OSErr
PBCatSearchSyncCompat(CSParamPtr paramBlock
)
1058 static Boolean fullExtFSDispatchingtested
= false;
1059 static Boolean hasFullExtFSDispatching
= false;
1061 Boolean supportsCatSearch
;
1063 GetVolParmsInfoBuffer volParmsInfo
;
1068 /* See if File Manager will pass CatSearch requests to external file systems */
1069 /* we'll store the results in a static variable so we don't have to call Gestalt */
1070 /* everytime we're called. */
1071 if ( !fullExtFSDispatchingtested
)
1073 fullExtFSDispatchingtested
= true;
1074 if ( Gestalt(gestaltFSAttr
, &response
) == noErr
)
1076 hasFullExtFSDispatching
= ((response
& (1L << gestaltFullExtFSDispatching
)) != 0);
1080 /* CatSearch is a per volume attribute, so we have to check each time we're */
1081 /* called to see if it is available on the volume specified. */
1082 supportsCatSearch
= false;
1083 if ( hasFullExtFSDispatching
)
1085 infoSize
= sizeof(GetVolParmsInfoBuffer
);
1086 result
= HGetVolParms(paramBlock
->ioNamePtr
, paramBlock
->ioVRefNum
,
1087 &volParmsInfo
, &infoSize
);
1088 if ( result
== noErr
)
1090 supportsCatSearch
= hasCatSearch(volParmsInfo
);
1094 /* noErr or paramErr is OK here. */
1095 /* paramErr just means that GetVolParms isn't supported by this volume */
1096 if ( (result
== noErr
) || (result
== paramErr
) )
1098 if ( supportsCatSearch
)
1100 /* Volume supports CatSearch so use it. */
1101 /* CatSearch is faster than an indexed search. */
1102 result
= PBCatSearchSync(paramBlock
);
1106 /* Volume doesn't support CatSearch so */
1107 /* search using IndexedSearch from root directory. */
1108 result
= IndexedSearch(paramBlock
, fsRtDirID
);
1115 /*****************************************************************************/
1117 pascal OSErr
NameFileSearch(ConstStr255Param volName
,
1119 ConstStr255Param fileName
,
1122 long *actMatchCount
,
1126 CInfoPBRec searchInfo1
, searchInfo2
;
1129 static CatPositionRec catPosition
;
1130 static short lastVRefNum
= 0;
1132 /* get the real volume reference number */
1133 error
= DetermineVRefNum(volName
, vRefNum
, &vRefNum
);
1134 if ( error
!= noErr
)
1137 pb
.csParam
.ioNamePtr
= NULL
;
1138 pb
.csParam
.ioVRefNum
= vRefNum
;
1139 pb
.csParam
.ioMatchPtr
= matches
;
1140 pb
.csParam
.ioReqMatchCount
= reqMatchCount
;
1141 if ( partial
) /* tell CatSearch what we're looking for: */
1143 pb
.csParam
.ioSearchBits
= fsSBPartialName
+ fsSBFlAttrib
; /* partial name file matches or */
1147 pb
.csParam
.ioSearchBits
= fsSBFullName
+ fsSBFlAttrib
; /* full name file matches */
1149 pb
.csParam
.ioSearchInfo1
= &searchInfo1
;
1150 pb
.csParam
.ioSearchInfo2
= &searchInfo2
;
1151 pb
.csParam
.ioSearchTime
= 0;
1152 if ( (newSearch
) || /* If caller specified new search */
1153 (lastVRefNum
!= vRefNum
) ) /* or if last search was to another volume, */
1155 catPosition
.initialize
= 0; /* then search from beginning of catalog */
1157 pb
.csParam
.ioCatPosition
= catPosition
;
1158 pb
.csParam
.ioOptBuffer
= GetTempBuffer(0x00004000, &pb
.csParam
.ioOptBufSize
);
1160 /* search for fileName */
1161 searchInfo1
.hFileInfo
.ioNamePtr
= (StringPtr
)fileName
;
1162 searchInfo2
.hFileInfo
.ioNamePtr
= NULL
;
1164 /* only match files (not directories) */
1165 searchInfo1
.hFileInfo
.ioFlAttrib
= 0x00;
1166 searchInfo2
.hFileInfo
.ioFlAttrib
= ioDirMask
;
1168 error
= PBCatSearchSyncCompat((CSParamPtr
)&pb
);
1170 if ( (error
== noErr
) || /* If no errors or the end of catalog was */
1171 (error
== eofErr
) ) /* found, then the call was successful so */
1173 *actMatchCount
= pb
.csParam
.ioActMatchCount
; /* return the match count */
1177 *actMatchCount
= 0; /* else no matches found */
1180 if ( (error
== noErr
) || /* If no errors */
1181 (error
== catChangedErr
) ) /* or there was a change in the catalog */
1183 catPosition
= pb
.csParam
.ioCatPosition
;
1184 lastVRefNum
= vRefNum
;
1185 /* we can probably start the next search where we stopped this time */
1189 catPosition
.initialize
= 0;
1190 /* start the next search from beginning of catalog */
1193 if ( pb
.csParam
.ioOptBuffer
!= NULL
)
1195 DisposePtr(pb
.csParam
.ioOptBuffer
);
1201 /*****************************************************************************/
1203 pascal OSErr
CreatorTypeFileSearch(ConstStr255Param volName
,
1209 long *actMatchCount
,
1212 CInfoPBRec searchInfo1
, searchInfo2
;
1215 static CatPositionRec catPosition
;
1216 static short lastVRefNum
= 0;
1218 /* get the real volume reference number */
1219 error
= DetermineVRefNum(volName
, vRefNum
, &vRefNum
);
1220 if ( error
!= noErr
)
1223 pb
.csParam
.ioNamePtr
= NULL
;
1224 pb
.csParam
.ioVRefNum
= vRefNum
;
1225 pb
.csParam
.ioMatchPtr
= matches
;
1226 pb
.csParam
.ioReqMatchCount
= reqMatchCount
;
1227 pb
.csParam
.ioSearchBits
= fsSBFlAttrib
+ fsSBFlFndrInfo
; /* Looking for finder info file matches */
1228 pb
.csParam
.ioSearchInfo1
= &searchInfo1
;
1229 pb
.csParam
.ioSearchInfo2
= &searchInfo2
;
1230 pb
.csParam
.ioSearchTime
= 0;
1231 if ( (newSearch
) || /* If caller specified new search */
1232 (lastVRefNum
!= vRefNum
) ) /* or if last search was to another volume, */
1234 catPosition
.initialize
= 0; /* then search from beginning of catalog */
1236 pb
.csParam
.ioCatPosition
= catPosition
;
1237 pb
.csParam
.ioOptBuffer
= GetTempBuffer(0x00004000, &pb
.csParam
.ioOptBufSize
);
1240 searchInfo1
.hFileInfo
.ioNamePtr
= NULL
;
1241 searchInfo2
.hFileInfo
.ioNamePtr
= NULL
;
1243 /* only match files (not directories) */
1244 searchInfo1
.hFileInfo
.ioFlAttrib
= 0x00;
1245 searchInfo2
.hFileInfo
.ioFlAttrib
= ioDirMask
;
1247 /* search for creator; if creator = 0x00000000, ignore creator */
1248 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdCreator
= creator
;
1249 if ( creator
== (OSType
)0x00000000 )
1251 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdCreator
= (OSType
)0x00000000;
1255 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdCreator
= (OSType
)0xffffffff;
1258 /* search for fileType; if fileType = 0x00000000, ignore fileType */
1259 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdType
= fileType
;
1260 if ( fileType
== (OSType
)0x00000000 )
1262 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdType
= (OSType
)0x00000000;
1266 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdType
= (OSType
)0xffffffff;
1269 /* zero all other FInfo fields */
1270 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdFlags
= 0;
1271 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdLocation
.v
= 0;
1272 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdLocation
.h
= 0;
1273 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdFldr
= 0;
1275 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdFlags
= 0;
1276 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdLocation
.v
= 0;
1277 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdLocation
.h
= 0;
1278 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdFldr
= 0;
1280 error
= PBCatSearchSyncCompat((CSParamPtr
)&pb
);
1282 if ( (error
== noErr
) || /* If no errors or the end of catalog was */
1283 (error
== eofErr
) ) /* found, then the call was successful so */
1285 *actMatchCount
= pb
.csParam
.ioActMatchCount
; /* return the match count */
1289 *actMatchCount
= 0; /* else no matches found */
1292 if ( (error
== noErr
) || /* If no errors */
1293 (error
== catChangedErr
) ) /* or there was a change in the catalog */
1295 catPosition
= pb
.csParam
.ioCatPosition
;
1296 lastVRefNum
= vRefNum
;
1297 /* we can probably start the next search where we stopped this time */
1301 catPosition
.initialize
= 0;
1302 /* start the next search from beginning of catalog */
1305 if ( pb
.csParam
.ioOptBuffer
!= NULL
)
1307 DisposePtr(pb
.csParam
.ioOptBuffer
);
1313 /*****************************************************************************/