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 timerTask
.theTask
.tmAddr
= NewTimerProc(TimeOutTask
);
795 InsTime((QElemPtr
)&(timerTask
.theTask
));
796 PrimeTime((QElemPtr
)&(timerTask
.theTask
), pb
->ioSearchTime
);
799 /* Check the parameter block passed for things that we don't want to assume */
800 /* are OK later in the code. For example, make sure pointers to data structures */
801 /* and buffers are not NULL. And while we're in there, see if the request */
802 /* specified searching for files, directories, or both, and see if the search */
803 /* was by full or partial name. */
804 result
= VerifyUserPB(pb
, &includeFiles
, &includeDirs
, &includeNames
);
805 if ( result
== noErr
)
807 pb
->ioActMatchCount
= 0; /* no matches yet */
811 /* The search includes seach by full or partial name. */
812 /* Make an upper case copy of the match string to pass to */
813 /* CheckForMatches. */
814 BlockMoveData(pb
->ioSearchInfo1
->hFileInfo
.ioNamePtr
,
816 pb
->ioSearchInfo1
->hFileInfo
.ioNamePtr
[0] + 1);
817 /* Use the same non-international call the File Manager uses */
818 UpperString(upperName
, true);
821 /* Prevent casting to my type throughout code */
822 catPosition
= (SearchPositionRecPtr
)&pb
->ioCatPosition
;
824 /* Create searchStack first time called */
825 if ( searchStack
== NULL
)
827 searchStack
= (LevelRecHandle
)NewHandle(kAdditionalLevelRecs
* sizeof(LevelRec
));
830 /* Make sure searchStack really exists */
831 if ( searchStack
!= NULL
)
834 searchStackSize
= GetHandleSize((Handle
)searchStack
);
836 searchStackSize
= InlineGetHandleSize((Handle
)searchStack
);
839 /* See if the search is a new search or a resumed search. */
840 if ( catPosition
->initialize
== 0 )
844 /* Get the real vRefNum and fill in catPosition->initialize. */
845 result
= CheckVol(pb
->ioNamePtr
, pb
->ioVRefNum
, &realVRefNum
, &catPosition
->initialize
);
846 if ( result
== noErr
)
848 /* clear searchStack */
849 catPosition
->stackDepth
= 0;
851 /* use dirID parameter passed and... */
852 index
= -1; /* start with the passed directory itself! */
857 /* We're resuming a search. */
859 /* Get the real vRefNum and make sure catPosition->initialize is valid. */
860 result
= CheckVol(pb
->ioNamePtr
, pb
->ioVRefNum
, &realVRefNum
, &tempLong
);
861 if ( result
== noErr
)
863 /* Make sure the resumed search is to the same volume! */
864 if ( catPosition
->initialize
== tempLong
)
866 /* For resume, catPosition->stackDepth > 0 */
867 if ( catPosition
->stackDepth
> 0 )
869 /* Position catPosition->stackDepth to access last saved level */
870 --(catPosition
->stackDepth
);
872 /* Get the dirID and index for the next item */
873 dirID
= (*searchStack
)[catPosition
->stackDepth
].dirID
;
874 index
= (*searchStack
)[catPosition
->stackDepth
].index
;
876 /* Check the dir's mod date against the saved mode date on our "stack" */
877 modDate
= GetDirModDate(realVRefNum
, dirID
);
878 if ( modDate
!= (*searchStack
)[catPosition
->stackDepth
].dirModDate
)
880 result
= catChangedErr
;
885 /* Invalid catPosition record was passed */
891 /* The volume is not the same */
892 result
= catChangedErr
;
897 if ( result
== noErr
)
899 /* ioNamePtr and ioVRefNum only need to be set up once. */
900 cPB
.hFileInfo
.ioNamePtr
= itemName
;
901 cPB
.hFileInfo
.ioVRefNum
= realVRefNum
;
904 ** Here's the loop that:
905 ** Finds the next item on the volume.
906 ** If noErr, calls the code to check for matches and add matches
907 ** to the match buffer.
908 ** Sets up dirID and index for to find the next item on the volume.
910 ** The looping ends when:
911 ** (a) an unexpected error is returned by PBGetCatInfo. All that
912 ** is expected is noErr and fnfErr (after the last item in a
913 ** directory is found).
914 ** (b) the caller specified a timeout and our Time Manager task
916 ** (c) the number of matches requested by the caller has been found.
917 ** (d) the last item on the volume was found.
921 /* get the next item */
922 cPB
.hFileInfo
.ioFDirIndex
= index
;
923 cPB
.hFileInfo
.ioDirID
= dirID
;
924 result
= PBGetCatInfoSync(&cPB
);
927 if ( result
== noErr
)
929 /* We found something */
931 CheckForMatches(&cPB
, pb
, upperName
, includeFiles
, includeDirs
);
934 if ( (cPB
.dirInfo
.ioFlAttrib
& ioDirMask
) != 0 )
936 /* It's a directory */
938 result
= CheckStack(catPosition
->stackDepth
, searchStack
, &searchStackSize
);
939 if ( result
== noErr
)
941 /* Save the current state on the searchStack */
942 /* when we come back, this is where we'll start */
943 (*searchStack
)[catPosition
->stackDepth
].dirID
= dirID
;
944 (*searchStack
)[catPosition
->stackDepth
].index
= index
;
945 (*searchStack
)[catPosition
->stackDepth
].dirModDate
= GetDirModDate(realVRefNum
, dirID
);
947 /* position catPosition->stackDepth for next saved level */
948 ++(catPosition
->stackDepth
);
950 /* The next item to get is the 1st item in the child directory */
951 dirID
= cPB
.dirInfo
.ioDrDirID
;
955 /* else do nothing for files */
959 /* End of directory found (or we had some error and that */
960 /* means we have to drop out of this directory). */
961 /* Restore last thing put on stack and */
962 /* see if we need to continue or quit. */
963 if ( catPosition
->stackDepth
> 0 )
965 /* position catPosition->stackDepth to access last saved level */
966 --(catPosition
->stackDepth
);
968 dirID
= (*searchStack
)[catPosition
->stackDepth
].dirID
;
969 index
= (*searchStack
)[catPosition
->stackDepth
].index
;
971 /* Check the dir's mod date against the saved mode date on our "stack" */
972 modDate
= GetDirModDate(realVRefNum
, dirID
);
973 if ( modDate
!= (*searchStack
)[catPosition
->stackDepth
].dirModDate
)
975 result
= catChangedErr
;
979 /* Going back to ancestor directory. */
980 /* Clear error so we can continue. */
986 /* We hit the bottom of the stack, so we'll let the */
987 /* the eofErr drop us out of the loop. */
994 /* Special case for index == -1; that means that we're starting */
995 /* a new search and so the first item to check is the directory */
997 if ( result
== noErr
)
999 /* We found something */
1001 CheckForMatches(&cPB
, pb
, upperName
, includeFiles
, includeDirs
);
1003 /* Now, set the index to 1 and then we're ready to look inside */
1004 /* the passed directory. */
1008 } while ( (!timerTask
.stopSearch
) && /* timer hasn't fired */
1009 (result
== noErr
) && /* no unexpected errors */
1010 (pb
->ioReqMatchCount
> pb
->ioActMatchCount
) ); /* we haven't found our limit */
1012 /* Did we drop out of the loop because of timeout or */
1013 /* ioReqMatchCount was found? */
1014 if ( result
== noErr
)
1016 result
= CheckStack(catPosition
->stackDepth
, searchStack
, &searchStackSize
);
1017 if ( result
== noErr
)
1019 /* Either there was a timeout or ioReqMatchCount was reached. */
1020 /* Save the dirID and index for the next time we're called. */
1022 (*searchStack
)[catPosition
->stackDepth
].dirID
= dirID
;
1023 (*searchStack
)[catPosition
->stackDepth
].index
= index
;
1024 (*searchStack
)[catPosition
->stackDepth
].dirModDate
= GetDirModDate(realVRefNum
, dirID
);
1026 /* position catPosition->stackDepth for next saved level */
1028 ++(catPosition
->stackDepth
);
1035 /* searchStack Handle could not be allocated */
1036 result
= memFullErr
;
1040 if ( pb
->ioSearchTime
!= 0 )
1042 /* Stop Time Manager task here if it was installed */
1043 RmvTime((QElemPtr
)&(timerTask
.theTask
));
1044 DisposeTimerUPP(timerTask
.theTask
.tmAddr
);
1050 /*****************************************************************************/
1052 pascal OSErr
PBCatSearchSyncCompat(CSParamPtr paramBlock
)
1054 static Boolean fullExtFSDispatchingtested
= false;
1055 static Boolean hasFullExtFSDispatching
= false;
1057 Boolean supportsCatSearch
;
1059 GetVolParmsInfoBuffer volParmsInfo
;
1064 /* See if File Manager will pass CatSearch requests to external file systems */
1065 /* we'll store the results in a static variable so we don't have to call Gestalt */
1066 /* everytime we're called. */
1067 if ( !fullExtFSDispatchingtested
)
1069 fullExtFSDispatchingtested
= true;
1070 if ( Gestalt(gestaltFSAttr
, &response
) == noErr
)
1072 hasFullExtFSDispatching
= ((response
& (1L << gestaltFullExtFSDispatching
)) != 0);
1076 /* CatSearch is a per volume attribute, so we have to check each time we're */
1077 /* called to see if it is available on the volume specified. */
1078 supportsCatSearch
= false;
1079 if ( hasFullExtFSDispatching
)
1081 infoSize
= sizeof(GetVolParmsInfoBuffer
);
1082 result
= HGetVolParms(paramBlock
->ioNamePtr
, paramBlock
->ioVRefNum
,
1083 &volParmsInfo
, &infoSize
);
1084 if ( result
== noErr
)
1086 supportsCatSearch
= hasCatSearch(volParmsInfo
);
1090 /* noErr or paramErr is OK here. */
1091 /* paramErr just means that GetVolParms isn't supported by this volume */
1092 if ( (result
== noErr
) || (result
== paramErr
) )
1094 if ( supportsCatSearch
)
1096 /* Volume supports CatSearch so use it. */
1097 /* CatSearch is faster than an indexed search. */
1098 result
= PBCatSearchSync(paramBlock
);
1102 /* Volume doesn't support CatSearch so */
1103 /* search using IndexedSearch from root directory. */
1104 result
= IndexedSearch(paramBlock
, fsRtDirID
);
1111 /*****************************************************************************/
1113 pascal OSErr
NameFileSearch(ConstStr255Param volName
,
1115 ConstStr255Param fileName
,
1118 long *actMatchCount
,
1122 CInfoPBRec searchInfo1
, searchInfo2
;
1125 static CatPositionRec catPosition
;
1126 static short lastVRefNum
= 0;
1128 /* get the real volume reference number */
1129 error
= DetermineVRefNum(volName
, vRefNum
, &vRefNum
);
1130 if ( error
!= noErr
)
1133 pb
.csParam
.ioNamePtr
= NULL
;
1134 pb
.csParam
.ioVRefNum
= vRefNum
;
1135 pb
.csParam
.ioMatchPtr
= matches
;
1136 pb
.csParam
.ioReqMatchCount
= reqMatchCount
;
1137 if ( partial
) /* tell CatSearch what we're looking for: */
1139 pb
.csParam
.ioSearchBits
= fsSBPartialName
+ fsSBFlAttrib
; /* partial name file matches or */
1143 pb
.csParam
.ioSearchBits
= fsSBFullName
+ fsSBFlAttrib
; /* full name file matches */
1145 pb
.csParam
.ioSearchInfo1
= &searchInfo1
;
1146 pb
.csParam
.ioSearchInfo2
= &searchInfo2
;
1147 pb
.csParam
.ioSearchTime
= 0;
1148 if ( (newSearch
) || /* If caller specified new search */
1149 (lastVRefNum
!= vRefNum
) ) /* or if last search was to another volume, */
1151 catPosition
.initialize
= 0; /* then search from beginning of catalog */
1153 pb
.csParam
.ioCatPosition
= catPosition
;
1154 pb
.csParam
.ioOptBuffer
= GetTempBuffer(0x00004000, &pb
.csParam
.ioOptBufSize
);
1156 /* search for fileName */
1157 searchInfo1
.hFileInfo
.ioNamePtr
= (StringPtr
)fileName
;
1158 searchInfo2
.hFileInfo
.ioNamePtr
= NULL
;
1160 /* only match files (not directories) */
1161 searchInfo1
.hFileInfo
.ioFlAttrib
= 0x00;
1162 searchInfo2
.hFileInfo
.ioFlAttrib
= ioDirMask
;
1164 error
= PBCatSearchSyncCompat((CSParamPtr
)&pb
);
1166 if ( (error
== noErr
) || /* If no errors or the end of catalog was */
1167 (error
== eofErr
) ) /* found, then the call was successful so */
1169 *actMatchCount
= pb
.csParam
.ioActMatchCount
; /* return the match count */
1173 *actMatchCount
= 0; /* else no matches found */
1176 if ( (error
== noErr
) || /* If no errors */
1177 (error
== catChangedErr
) ) /* or there was a change in the catalog */
1179 catPosition
= pb
.csParam
.ioCatPosition
;
1180 lastVRefNum
= vRefNum
;
1181 /* we can probably start the next search where we stopped this time */
1185 catPosition
.initialize
= 0;
1186 /* start the next search from beginning of catalog */
1189 if ( pb
.csParam
.ioOptBuffer
!= NULL
)
1191 DisposePtr(pb
.csParam
.ioOptBuffer
);
1197 /*****************************************************************************/
1199 pascal OSErr
CreatorTypeFileSearch(ConstStr255Param volName
,
1205 long *actMatchCount
,
1208 CInfoPBRec searchInfo1
, searchInfo2
;
1211 static CatPositionRec catPosition
;
1212 static short lastVRefNum
= 0;
1214 /* get the real volume reference number */
1215 error
= DetermineVRefNum(volName
, vRefNum
, &vRefNum
);
1216 if ( error
!= noErr
)
1219 pb
.csParam
.ioNamePtr
= NULL
;
1220 pb
.csParam
.ioVRefNum
= vRefNum
;
1221 pb
.csParam
.ioMatchPtr
= matches
;
1222 pb
.csParam
.ioReqMatchCount
= reqMatchCount
;
1223 pb
.csParam
.ioSearchBits
= fsSBFlAttrib
+ fsSBFlFndrInfo
; /* Looking for finder info file matches */
1224 pb
.csParam
.ioSearchInfo1
= &searchInfo1
;
1225 pb
.csParam
.ioSearchInfo2
= &searchInfo2
;
1226 pb
.csParam
.ioSearchTime
= 0;
1227 if ( (newSearch
) || /* If caller specified new search */
1228 (lastVRefNum
!= vRefNum
) ) /* or if last search was to another volume, */
1230 catPosition
.initialize
= 0; /* then search from beginning of catalog */
1232 pb
.csParam
.ioCatPosition
= catPosition
;
1233 pb
.csParam
.ioOptBuffer
= GetTempBuffer(0x00004000, &pb
.csParam
.ioOptBufSize
);
1236 searchInfo1
.hFileInfo
.ioNamePtr
= NULL
;
1237 searchInfo2
.hFileInfo
.ioNamePtr
= NULL
;
1239 /* only match files (not directories) */
1240 searchInfo1
.hFileInfo
.ioFlAttrib
= 0x00;
1241 searchInfo2
.hFileInfo
.ioFlAttrib
= ioDirMask
;
1243 /* search for creator; if creator = 0x00000000, ignore creator */
1244 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdCreator
= creator
;
1245 if ( creator
== (OSType
)0x00000000 )
1247 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdCreator
= (OSType
)0x00000000;
1251 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdCreator
= (OSType
)0xffffffff;
1254 /* search for fileType; if fileType = 0x00000000, ignore fileType */
1255 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdType
= fileType
;
1256 if ( fileType
== (OSType
)0x00000000 )
1258 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdType
= (OSType
)0x00000000;
1262 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdType
= (OSType
)0xffffffff;
1265 /* zero all other FInfo fields */
1266 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdFlags
= 0;
1267 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdLocation
.v
= 0;
1268 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdLocation
.h
= 0;
1269 searchInfo1
.hFileInfo
.ioFlFndrInfo
.fdFldr
= 0;
1271 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdFlags
= 0;
1272 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdLocation
.v
= 0;
1273 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdLocation
.h
= 0;
1274 searchInfo2
.hFileInfo
.ioFlFndrInfo
.fdFldr
= 0;
1276 error
= PBCatSearchSyncCompat((CSParamPtr
)&pb
);
1278 if ( (error
== noErr
) || /* If no errors or the end of catalog was */
1279 (error
== eofErr
) ) /* found, then the call was successful so */
1281 *actMatchCount
= pb
.csParam
.ioActMatchCount
; /* return the match count */
1285 *actMatchCount
= 0; /* else no matches found */
1288 if ( (error
== noErr
) || /* If no errors */
1289 (error
== catChangedErr
) ) /* or there was a change in the catalog */
1291 catPosition
= pb
.csParam
.ioCatPosition
;
1292 lastVRefNum
= vRefNum
;
1293 /* we can probably start the next search where we stopped this time */
1297 catPosition
.initialize
= 0;
1298 /* start the next search from beginning of catalog */
1301 if ( pb
.csParam
.ioOptBuffer
!= NULL
)
1303 DisposePtr(pb
.csParam
.ioOptBuffer
);
1309 /*****************************************************************************/