]> git.saurik.com Git - wxWidgets.git/blob - src/mac/morefile/mfsearch.cpp
set more event object fields for lbox dbl click events
[wxWidgets.git] / src / mac / morefile / mfsearch.cpp
1 /*
2 ** Apple Macintosh Developer Technical Support
3 **
4 ** IndexedSearch and the PBCatSearch compatibility function.
5 **
6 ** by Jim Luther, Apple Developer Technical Support Emeritus
7 **
8 ** File: Search.c
9 **
10 ** Copyright © 1992-1998 Apple Computer, Inc.
11 ** All rights reserved.
12 **
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.
20 */
21
22 #include <Types.h>
23 #include <Gestalt.h>
24 #include <Timer.h>
25 #include <Errors.h>
26 #include <Memory.h>
27 #include <Files.h>
28 #include <TextUtils.h>
29
30 #define __COMPILINGMOREFILES
31
32 #include "morefile.h"
33 #include "moreextr.h"
34 #include "mfsearch.h"
35
36 /*****************************************************************************/
37
38 enum
39 {
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
44 };
45
46 /*****************************************************************************/
47
48 /*
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
53 ** used as a stack.
54 */
55 struct LevelRec
56 {
57 long dirModDate; /* for detecting most (but not all) catalog changes */
58 long dirID;
59 short index;
60 };
61 typedef struct LevelRec LevelRec;
62 typedef LevelRec *LevelRecPtr, **LevelRecHandle;
63
64
65 /*
66 ** SearchPositionRec is my version of a CatPositionRec. It holds the
67 ** information I need to resuming searching.
68 */
69 #if PRAGMA_STRUCT_ALIGN
70 #pragma options align=mac68k
71 #elif PRAGMA_STRUCT_PACKPUSH
72 #pragma pack(push, 2)
73 #elif PRAGMA_STRUCT_PACK
74 #pragma pack(2)
75 #endif
76 struct SearchPositionRec
77 {
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... */
82 };
83 #if PRAGMA_STRUCT_ALIGN
84 #pragma options align=reset
85 #elif PRAGMA_STRUCT_PACKPUSH
86 #pragma pack(pop)
87 #elif PRAGMA_STRUCT_PACK
88 #pragma pack()
89 #endif
90 typedef struct SearchPositionRec SearchPositionRec;
91 typedef SearchPositionRec *SearchPositionRecPtr;
92
93
94 /*
95 ** ExtendedTMTask is a TMTask record extended to hold the timer flag.
96 */
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
102 #pragma pack(2)
103 #endif
104 struct ExtendedTMTask
105 {
106 TMTask theTask;
107 Boolean stopSearch; /* the Time Mgr task will set stopSearch to */
108 /* true when the timer expires */
109 };
110 #if PRAGMA_STRUCT_ALIGN
111 #pragma options align=reset
112 #elif PRAGMA_STRUCT_PACKPUSH
113 #pragma pack(pop)
114 #elif PRAGMA_STRUCT_PACK
115 #pragma pack()
116 #endif
117 typedef struct ExtendedTMTask ExtendedTMTask;
118 typedef ExtendedTMTask *ExtendedTMTaskPtr;
119
120 /*****************************************************************************/
121
122 static OSErr CheckVol(ConstStr255Param pathname,
123 short vRefNum,
124 short *realVRefNum,
125 long *volID);
126
127 static OSErr CheckStack(unsigned short stackDepth,
128 LevelRecHandle searchStack,
129 Size *searchStackSize);
130
131 static OSErr VerifyUserPB(CSParamPtr userPB,
132 Boolean *includeFiles,
133 Boolean *includeDirs,
134 Boolean *includeNames);
135
136 static Boolean IsSubString(ConstStr255Param aStringPtr,
137 ConstStr255Param subStringPtr);
138
139 static Boolean CompareMasked(const long *data1,
140 const long *data2,
141 const long *mask,
142 short longsToCompare);
143
144 static void CheckForMatches(CInfoPBPtr cPB,
145 CSParamPtr userPB,
146 const Str63 matchName,
147 Boolean includeFiles,
148 Boolean includeDirs);
149
150 #if __WANTPASCALELIMINATION
151 #undef pascal
152 #endif
153
154 #if TARGET_CARBON
155 static pascal void TimeOutTask(TMTaskPtr tmTaskPtr);
156
157 #else
158 #if GENERATINGCFM
159
160 static pascal void TimeOutTask(TMTaskPtr tmTaskPtr);
161
162 #else
163
164 static pascal TMTaskPtr GetTMTaskPtr(void);
165
166 static void TimeOutTask(void);
167
168 #endif
169 #endif
170
171 #if __WANTPASCALELIMINATION
172 #define pascal
173 #endif
174
175 static long GetDirModDate(short vRefNum,
176 long dirID);
177
178 /*****************************************************************************/
179
180 /*
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.
184 */
185 static OSErr CheckVol(ConstStr255Param pathname,
186 short vRefNum,
187 short *realVRefNum,
188 long *volID)
189 {
190 HParamBlockRec pb;
191 OSErr error;
192
193 error = GetVolumeInfoNoName(pathname, vRefNum, &pb);
194 if ( error == noErr )
195 {
196 /* Return the real vRefNum */
197 *realVRefNum = pb.volumeParam.ioVRefNum;
198
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;
207 }
208 return ( error );
209 }
210
211 /*****************************************************************************/
212
213 /*
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.
217 */
218 static OSErr CheckStack(unsigned short stackDepth,
219 LevelRecHandle searchStack,
220 Size *searchStackSize)
221 {
222 OSErr result;
223
224 if ( (*searchStackSize / sizeof(LevelRec)) == (stackDepth + 1) )
225 {
226 /* Time to grow stack */
227 SetHandleSize((Handle)searchStack, *searchStackSize + (kAdditionalLevelRecs * sizeof(LevelRec)));
228 result = MemError(); /* should be noErr */
229 #if TARGET_CARBON
230 *searchStackSize = GetHandleSize((Handle)searchStack);
231 #else
232 *searchStackSize = InlineGetHandleSize((Handle)searchStack);
233 #endif
234 }
235 else
236 {
237 result = noErr;
238 }
239
240 return ( result );
241 }
242
243 /*****************************************************************************/
244
245 /*
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.
251 */
252 static OSErr VerifyUserPB(CSParamPtr userPB,
253 Boolean *includeFiles,
254 Boolean *includeDirs,
255 Boolean *includeNames)
256 {
257 CInfoPBPtr searchInfo1;
258 CInfoPBPtr searchInfo2;
259
260 searchInfo1 = userPB->ioSearchInfo1;
261 searchInfo2 = userPB->ioSearchInfo2;
262
263 /* ioMatchPtr cannot be NULL */
264 if ( userPB->ioMatchPtr == NULL )
265 {
266 goto ParamErrExit;
267 }
268
269 /* ioSearchInfo1 cannot be NULL */
270 if ( searchInfo1 == NULL )
271 {
272 goto ParamErrExit;
273 }
274
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 ))
279 {
280 goto ParamErrExit;
281 }
282
283 *includeFiles = false;
284 *includeDirs = false;
285 *includeNames = false;
286
287 if ( (userPB->ioSearchBits & (fsSBPartialName | fsSBFullName)) != 0 )
288 {
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)) )
294 {
295 goto ParamErrExit;
296 }
297
298 *includeNames = true;
299 }
300
301 if ( (userPB->ioSearchBits & fsSBFlAttrib) != 0 )
302 {
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 )
306 {
307 goto ParamErrExit;
308 }
309
310 /* interested in the directory bit? */
311 if ( (searchInfo2->hFileInfo.ioFlAttrib & ioDirMask) != 0 )
312 {
313 /* yes, so do they want just directories or just files? */
314 if ( (searchInfo1->hFileInfo.ioFlAttrib & ioDirMask) != 0 )
315 {
316 *includeDirs = true;
317 }
318 else
319 {
320 *includeFiles = true;
321 }
322 }
323 else
324 {
325 /* no interest in directory bit - get both files and directories */
326 *includeDirs = true;
327 *includeFiles = true;
328 }
329 }
330 else
331 {
332 /* no attribute checking - get both files and directories */
333 *includeDirs = true;
334 *includeFiles = true;
335 }
336
337 /* If directories are included in the search, */
338 /* then the locked attribute cannot be requested. */
339 if ( *includeDirs &&
340 ((userPB->ioSearchBits & fsSBFlAttrib) != 0) &&
341 ((searchInfo2->hFileInfo.ioFlAttrib & 0x01) != 0) )
342 {
343 goto ParamErrExit;
344 }
345
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) )
350 {
351 goto ParamErrExit;
352 }
353
354 /* If directories are included in the search, then there cannot */
355 /* be a search on file lengths. */
356 if ( *includeDirs &&
357 ((userPB->ioSearchBits & (fsSBFlLgLen | fsSBFlPyLen | fsSBFlRLgLen | fsSBFlRPyLen)) != 0) )
358 {
359 goto ParamErrExit;
360 }
361
362 return ( noErr );
363
364 ParamErrExit:
365 return ( paramErr );
366 }
367
368 /*****************************************************************************/
369
370 /*
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).
374 */
375 static Boolean IsSubString(ConstStr255Param aStringPtr,
376 ConstStr255Param subStringPtr)
377 {
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 */
382
383 found = false;
384 strLength = aStringPtr[0];
385 subStrLength = subStringPtr[0];
386
387 if ( subStrLength <= strLength)
388 {
389 register short count; /* search counter */
390 register short strIndex; /* running index into string */
391 register short subStrIndex; /* running index into subString */
392
393 /* start looking at first character */
394 index = 1;
395
396 /* continue looking until remaining string is shorter than substring */
397 count = strLength - subStrLength + 1;
398
399 do
400 {
401 strIndex = index; /* start string index at index */
402 subStrIndex = 1; /* start subString index at 1 */
403
404 while ( !found && (aStringPtr[strIndex] == subStringPtr[subStrIndex]) )
405 {
406 if ( subStrIndex == subStrLength )
407 {
408 /* all characters in subString were found */
409 found = true;
410 }
411 else
412 {
413 /* check next character of substring against next character of string */
414 ++subStrIndex;
415 ++strIndex;
416 }
417 }
418
419 if ( !found )
420 {
421 /* start substring search again at next string character */
422 ++index;
423 --count;
424 }
425 } while ( count != 0 && (!found) );
426 }
427
428 return ( found );
429 }
430
431 /*****************************************************************************/
432
433 /*
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.
438 */
439 static Boolean CompareMasked(const long *data1,
440 const long *data2,
441 const long *mask,
442 short longsToCompare)
443 {
444 Boolean result = true;
445
446 while ( (longsToCompare != 0) && (result == true) )
447 {
448 /* (*data1 ^ *data2) = bits that are different, so... */
449 /* ((*data1 ^ *data2) & *mask) = bits that are different that we're interested in */
450
451 if ( ((*data1 ^ *data2) & *mask) != 0 )
452 result = false;
453
454 ++data1;
455 ++data2;
456 ++mask;
457 --longsToCompare;
458 }
459
460 return ( result );
461 }
462
463 /*****************************************************************************/
464
465 /*
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.
469 */
470 static void CheckForMatches(CInfoPBPtr cPB,
471 CSParamPtr userPB,
472 const Str63 matchName,
473 Boolean includeFiles,
474 Boolean includeDirs)
475 {
476 long searchBits;
477 CInfoPBPtr searchInfo1;
478 CInfoPBPtr searchInfo2;
479 Str63 itemName; /* copy of object's name for partial name matching */
480 Boolean foundMatch;
481
482 foundMatch = false; /* default to no match */
483
484 searchBits = userPB->ioSearchBits;
485 searchInfo1 = userPB->ioSearchInfo1;
486 searchInfo2 = userPB->ioSearchInfo2;
487
488 /* Into the if statements that go on forever... */
489
490 if ( (cPB->hFileInfo.ioFlAttrib & ioDirMask) == 0 )
491 {
492 if (!includeFiles)
493 {
494 goto Failed;
495 }
496 }
497 else
498 {
499 if (!includeDirs)
500 {
501 goto Failed;
502 }
503 }
504
505 if ( (searchBits & fsSBPartialName) != 0 )
506 {
507 if ( (cPB->hFileInfo.ioNamePtr[0] > 0) &&
508 (cPB->hFileInfo.ioNamePtr[0] <= (sizeof(Str63) - 1)) )
509 {
510 /* Make uppercase copy of object name */
511 BlockMoveData(cPB->hFileInfo.ioNamePtr,
512 itemName,
513 cPB->hFileInfo.ioNamePtr[0] + 1);
514 /* Use the same non-international call the File Manager uses */
515 UpperString(itemName, true);
516 }
517 else
518 {
519 goto Failed;
520 }
521
522 {
523 if ( !IsSubString(itemName, matchName) )
524 {
525 goto Failed;
526 }
527 else if ( searchBits == fsSBPartialName )
528 {
529 /* optimize for name matching only since it is most common way to search */
530 goto Hit;
531 }
532 }
533 }
534
535 if ( (searchBits & fsSBFullName) != 0 )
536 {
537 /* Use the same non-international call the File Manager uses */
538 if ( !EqualString(cPB->hFileInfo.ioNamePtr, matchName, false, true) )
539 {
540 goto Failed;
541 }
542 else if ( searchBits == fsSBFullName )
543 {
544 /* optimize for name matching only since it is most common way to search */
545 goto Hit;
546 }
547 }
548
549 if ( (searchBits & fsSBFlParID) != 0 )
550 {
551 if ( ((unsigned long)(cPB->hFileInfo.ioFlParID) < (unsigned long)(searchInfo1->hFileInfo.ioFlParID)) ||
552 ((unsigned long)(cPB->hFileInfo.ioFlParID) > (unsigned long)(searchInfo2->hFileInfo.ioFlParID)) )
553 {
554 goto Failed;
555 }
556 }
557
558 if ( (searchBits & fsSBFlAttrib) != 0 )
559 {
560 if ( ((cPB->hFileInfo.ioFlAttrib ^ searchInfo1->hFileInfo.ioFlAttrib) &
561 searchInfo2->hFileInfo.ioFlAttrib) != 0 )
562 {
563 goto Failed;
564 }
565 }
566
567 if ( (searchBits & fsSBDrNmFls) != 0 )
568 {
569 if ( ((unsigned long)(cPB->dirInfo.ioDrNmFls) < (unsigned long)(searchInfo1->dirInfo.ioDrNmFls)) ||
570 ((unsigned long)(cPB->dirInfo.ioDrNmFls) > (unsigned long)(searchInfo2->dirInfo.ioDrNmFls)) )
571 {
572 goto Failed;
573 }
574 }
575
576 if ( (searchBits & fsSBFlFndrInfo) != 0 ) /* fsSBFlFndrInfo is same as fsSBDrUsrWds */
577 {
578 if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlFndrInfo),
579 (long *)&(searchInfo1->hFileInfo.ioFlFndrInfo),
580 (long *)&(searchInfo2->hFileInfo.ioFlFndrInfo),
581 sizeof(FInfo) / sizeof(long)) )
582 {
583 goto Failed;
584 }
585 }
586
587 if ( (searchBits & fsSBFlXFndrInfo) != 0 ) /* fsSBFlXFndrInfo is same as fsSBDrFndrInfo */
588 {
589 if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlXFndrInfo),
590 (long *)&(searchInfo1->hFileInfo.ioFlXFndrInfo),
591 (long *)&(searchInfo2->hFileInfo.ioFlXFndrInfo),
592 sizeof(FXInfo) / sizeof(long)) )
593 {
594 goto Failed;
595 }
596 }
597
598 if ( (searchBits & fsSBFlLgLen) != 0 )
599 {
600 if ( ((unsigned long)(cPB->hFileInfo.ioFlLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlLgLen)) ||
601 ((unsigned long)(cPB->hFileInfo.ioFlLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlLgLen)) )
602 {
603 goto Failed;
604 }
605 }
606
607 if ( (searchBits & fsSBFlPyLen) != 0 )
608 {
609 if ( ((unsigned long)(cPB->hFileInfo.ioFlPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlPyLen)) ||
610 ((unsigned long)(cPB->hFileInfo.ioFlPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlPyLen)) )
611 {
612 goto Failed;
613 }
614 }
615
616 if ( (searchBits & fsSBFlRLgLen) != 0 )
617 {
618 if ( ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRLgLen)) ||
619 ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRLgLen)) )
620 {
621 goto Failed;
622 }
623 }
624
625 if ( (searchBits & fsSBFlRPyLen) != 0 )
626 {
627 if ( ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRPyLen)) ||
628 ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRPyLen)) )
629 {
630 goto Failed;
631 }
632 }
633
634 if ( (searchBits & fsSBFlCrDat) != 0 ) /* fsSBFlCrDat is same as fsSBDrCrDat */
635 {
636 if ( ((unsigned long)(cPB->hFileInfo.ioFlCrDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlCrDat)) ||
637 ((unsigned long)(cPB->hFileInfo.ioFlCrDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlCrDat)) )
638 {
639 goto Failed;
640 }
641 }
642
643 if ( (searchBits & fsSBFlMdDat) != 0 ) /* fsSBFlMdDat is same as fsSBDrMdDat */
644 {
645 if ( ((unsigned long)(cPB->hFileInfo.ioFlMdDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlMdDat)) ||
646 ((unsigned long)(cPB->hFileInfo.ioFlMdDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlMdDat)) )
647 {
648 goto Failed;
649 }
650 }
651
652 if ( (searchBits & fsSBFlBkDat) != 0 ) /* fsSBFlBkDat is same as fsSBDrBkDat */
653 {
654 if ( ((unsigned long)(cPB->hFileInfo.ioFlBkDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlBkDat)) ||
655 ((unsigned long)(cPB->hFileInfo.ioFlBkDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlBkDat)) )
656 {
657 goto Failed;
658 }
659 }
660
661 /* Hey, we passed all of the tests! */
662
663 Hit:
664 foundMatch = true;
665
666 /* foundMatch is false if code jumps to Failed */
667 Failed:
668 /* Do we reverse our findings? */
669 if ( (searchBits & fsSBNegate) != 0 )
670 {
671 foundMatch = !foundMatch; /* matches are not, not matches are */
672 }
673
674 if ( foundMatch )
675 {
676
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 )
681 {
682 cPB->hFileInfo.ioNamePtr[0] = 63;
683 }
684 BlockMoveData(cPB->hFileInfo.ioNamePtr,
685 userPB->ioMatchPtr[userPB->ioActMatchCount].name,
686 cPB->hFileInfo.ioNamePtr[0] + 1);
687
688 /* increment the actual count */
689 ++(userPB->ioActMatchCount);
690 }
691 }
692
693 /*****************************************************************************/
694
695 /*
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
699 ** continue.
700 */
701
702 #if __WANTPASCALELIMINATION
703 #undef pascal
704 #endif
705 #if TARGET_CARBON
706 static pascal void TimeOutTask(TMTaskPtr tmTaskPtr)
707 {
708 ((ExtendedTMTaskPtr)tmTaskPtr)->stopSearch = true;
709 }
710 #else
711 #if GENERATINGCFM
712
713 static pascal void TimeOutTask(TMTaskPtr tmTaskPtr)
714 {
715 ((ExtendedTMTaskPtr)tmTaskPtr)->stopSearch = true;
716 }
717
718 #else
719
720 static pascal TMTaskPtr GetTMTaskPtr(void)
721 ONEWORDINLINE(0x2e89); /* MOVE.L A1,(SP) */
722
723 static void TimeOutTask(void)
724 {
725 ((ExtendedTMTaskPtr)GetTMTaskPtr())->stopSearch = true;
726 }
727 #endif
728 #endif
729
730 #if __WANTPASCALELIMINATION
731 #define pascal
732 #endif
733
734 /*****************************************************************************/
735
736 /*
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.
740 */
741 static long GetDirModDate(short vRefNum,
742 long dirID)
743 {
744 CInfoPBRec pb;
745 Str31 tempName;
746 long modDate;
747
748 /* Protection against File Sharing problem */
749 tempName[0] = 0;
750 pb.dirInfo.ioNamePtr = tempName;
751 pb.dirInfo.ioVRefNum = vRefNum;
752 pb.dirInfo.ioDrDirID = dirID;
753 pb.dirInfo.ioFDirIndex = -1; /* use ioDrDirID */
754
755 if ( PBGetCatInfoSync(&pb) == noErr )
756 {
757 modDate = pb.dirInfo.ioDrMdDat;
758 }
759 else
760 {
761 modDate = -1;
762 }
763
764 return ( modDate );
765 }
766
767 /*****************************************************************************/
768
769 pascal OSErr IndexedSearch(CSParamPtr pb,
770 long dirID)
771 {
772 static LevelRecHandle searchStack = NULL; /* static handle to LevelRec stack */
773 static Size searchStackSize = 0; /* size of static handle */
774 SearchPositionRecPtr catPosition;
775 long modDate;
776 short index;
777 ExtendedTMTask timerTask;
778 OSErr result;
779 short realVRefNum;
780 Str63 itemName;
781 CInfoPBRec cPB;
782 long tempLong;
783 Boolean includeFiles;
784 Boolean includeDirs;
785 Boolean includeNames;
786 Str63 upperName;
787
788 timerTask.stopSearch = false; /* don't stop yet! */
789
790 /* If request has a timeout, install a Time Manager task. */
791 if ( pb->ioSearchTime != 0 )
792 {
793 /* Start timer */
794 timerTask.theTask.tmAddr = NewTimerProc(TimeOutTask);
795 InsTime((QElemPtr)&(timerTask.theTask));
796 PrimeTime((QElemPtr)&(timerTask.theTask), pb->ioSearchTime);
797 }
798
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 )
806 {
807 pb->ioActMatchCount = 0; /* no matches yet */
808
809 if ( includeNames )
810 {
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,
815 upperName,
816 pb->ioSearchInfo1->hFileInfo.ioNamePtr[0] + 1);
817 /* Use the same non-international call the File Manager uses */
818 UpperString(upperName, true);
819 }
820
821 /* Prevent casting to my type throughout code */
822 catPosition = (SearchPositionRecPtr)&pb->ioCatPosition;
823
824 /* Create searchStack first time called */
825 if ( searchStack == NULL )
826 {
827 searchStack = (LevelRecHandle)NewHandle(kAdditionalLevelRecs * sizeof(LevelRec));
828 }
829
830 /* Make sure searchStack really exists */
831 if ( searchStack != NULL )
832 {
833 #if TARGET_CARBON
834 searchStackSize = GetHandleSize((Handle)searchStack);
835 #else
836 searchStackSize = InlineGetHandleSize((Handle)searchStack);
837 #endif
838
839 /* See if the search is a new search or a resumed search. */
840 if ( catPosition->initialize == 0 )
841 {
842 /* New search. */
843
844 /* Get the real vRefNum and fill in catPosition->initialize. */
845 result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &catPosition->initialize);
846 if ( result == noErr )
847 {
848 /* clear searchStack */
849 catPosition->stackDepth = 0;
850
851 /* use dirID parameter passed and... */
852 index = -1; /* start with the passed directory itself! */
853 }
854 }
855 else
856 {
857 /* We're resuming a search. */
858
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 )
862 {
863 /* Make sure the resumed search is to the same volume! */
864 if ( catPosition->initialize == tempLong )
865 {
866 /* For resume, catPosition->stackDepth > 0 */
867 if ( catPosition->stackDepth > 0 )
868 {
869 /* Position catPosition->stackDepth to access last saved level */
870 --(catPosition->stackDepth);
871
872 /* Get the dirID and index for the next item */
873 dirID = (*searchStack)[catPosition->stackDepth].dirID;
874 index = (*searchStack)[catPosition->stackDepth].index;
875
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 )
879 {
880 result = catChangedErr;
881 }
882 }
883 else
884 {
885 /* Invalid catPosition record was passed */
886 result = paramErr;
887 }
888 }
889 else
890 {
891 /* The volume is not the same */
892 result = catChangedErr;
893 }
894 }
895 }
896
897 if ( result == noErr )
898 {
899 /* ioNamePtr and ioVRefNum only need to be set up once. */
900 cPB.hFileInfo.ioNamePtr = itemName;
901 cPB.hFileInfo.ioVRefNum = realVRefNum;
902
903 /*
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.
909 **
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
915 ** has fired.
916 ** (c) the number of matches requested by the caller has been found.
917 ** (d) the last item on the volume was found.
918 */
919 do
920 {
921 /* get the next item */
922 cPB.hFileInfo.ioFDirIndex = index;
923 cPB.hFileInfo.ioDirID = dirID;
924 result = PBGetCatInfoSync(&cPB);
925 if ( index != -1 )
926 {
927 if ( result == noErr )
928 {
929 /* We found something */
930
931 CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
932
933 ++index;
934 if ( (cPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
935 {
936 /* It's a directory */
937
938 result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
939 if ( result == noErr )
940 {
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);
946
947 /* position catPosition->stackDepth for next saved level */
948 ++(catPosition->stackDepth);
949
950 /* The next item to get is the 1st item in the child directory */
951 dirID = cPB.dirInfo.ioDrDirID;
952 index = 1;
953 }
954 }
955 /* else do nothing for files */
956 }
957 else
958 {
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 )
964 {
965 /* position catPosition->stackDepth to access last saved level */
966 --(catPosition->stackDepth);
967
968 dirID = (*searchStack)[catPosition->stackDepth].dirID;
969 index = (*searchStack)[catPosition->stackDepth].index;
970
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 )
974 {
975 result = catChangedErr;
976 }
977 else
978 {
979 /* Going back to ancestor directory. */
980 /* Clear error so we can continue. */
981 result = noErr;
982 }
983 }
984 else
985 {
986 /* We hit the bottom of the stack, so we'll let the */
987 /* the eofErr drop us out of the loop. */
988 result = eofErr;
989 }
990 }
991 }
992 else
993 {
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 */
996 /* passed to us. */
997 if ( result == noErr )
998 {
999 /* We found something */
1000
1001 CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
1002
1003 /* Now, set the index to 1 and then we're ready to look inside */
1004 /* the passed directory. */
1005 index = 1;
1006 }
1007 }
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 */
1011
1012 /* Did we drop out of the loop because of timeout or */
1013 /* ioReqMatchCount was found? */
1014 if ( result == noErr )
1015 {
1016 result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
1017 if ( result == noErr )
1018 {
1019 /* Either there was a timeout or ioReqMatchCount was reached. */
1020 /* Save the dirID and index for the next time we're called. */
1021
1022 (*searchStack)[catPosition->stackDepth].dirID = dirID;
1023 (*searchStack)[catPosition->stackDepth].index = index;
1024 (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
1025
1026 /* position catPosition->stackDepth for next saved level */
1027
1028 ++(catPosition->stackDepth);
1029 }
1030 }
1031 }
1032 }
1033 else
1034 {
1035 /* searchStack Handle could not be allocated */
1036 result = memFullErr;
1037 }
1038 }
1039
1040 if ( pb->ioSearchTime != 0 )
1041 {
1042 /* Stop Time Manager task here if it was installed */
1043 RmvTime((QElemPtr)&(timerTask.theTask));
1044 DisposeTimerUPP(timerTask.theTask.tmAddr);
1045 }
1046
1047 return ( result );
1048 }
1049
1050 /*****************************************************************************/
1051
1052 pascal OSErr PBCatSearchSyncCompat(CSParamPtr paramBlock)
1053 {
1054 static Boolean fullExtFSDispatchingtested = false;
1055 static Boolean hasFullExtFSDispatching = false;
1056 OSErr result;
1057 Boolean supportsCatSearch;
1058 long response;
1059 GetVolParmsInfoBuffer volParmsInfo;
1060 long infoSize;
1061
1062 result = noErr;
1063
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 )
1068 {
1069 fullExtFSDispatchingtested = true;
1070 if ( Gestalt(gestaltFSAttr, &response) == noErr )
1071 {
1072 hasFullExtFSDispatching = ((response & (1L << gestaltFullExtFSDispatching)) != 0);
1073 }
1074 }
1075
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 )
1080 {
1081 infoSize = sizeof(GetVolParmsInfoBuffer);
1082 result = HGetVolParms(paramBlock->ioNamePtr, paramBlock->ioVRefNum,
1083 &volParmsInfo, &infoSize);
1084 if ( result == noErr )
1085 {
1086 supportsCatSearch = hasCatSearch(volParmsInfo);
1087 }
1088 }
1089
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) )
1093 {
1094 if ( supportsCatSearch )
1095 {
1096 /* Volume supports CatSearch so use it. */
1097 /* CatSearch is faster than an indexed search. */
1098 result = PBCatSearchSync(paramBlock);
1099 }
1100 else
1101 {
1102 /* Volume doesn't support CatSearch so */
1103 /* search using IndexedSearch from root directory. */
1104 result = IndexedSearch(paramBlock, fsRtDirID);
1105 }
1106 }
1107
1108 return ( result );
1109 }
1110
1111 /*****************************************************************************/
1112
1113 pascal OSErr NameFileSearch(ConstStr255Param volName,
1114 short vRefNum,
1115 ConstStr255Param fileName,
1116 FSSpecPtr matches,
1117 long reqMatchCount,
1118 long *actMatchCount,
1119 Boolean newSearch,
1120 Boolean partial)
1121 {
1122 CInfoPBRec searchInfo1, searchInfo2;
1123 HParamBlockRec pb;
1124 OSErr error;
1125 static CatPositionRec catPosition;
1126 static short lastVRefNum = 0;
1127
1128 /* get the real volume reference number */
1129 error = DetermineVRefNum(volName, vRefNum, &vRefNum);
1130 if ( error != noErr )
1131 return ( error );
1132
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: */
1138 {
1139 pb.csParam.ioSearchBits = fsSBPartialName + fsSBFlAttrib; /* partial name file matches or */
1140 }
1141 else
1142 {
1143 pb.csParam.ioSearchBits = fsSBFullName + fsSBFlAttrib; /* full name file matches */
1144 }
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, */
1150 {
1151 catPosition.initialize = 0; /* then search from beginning of catalog */
1152 }
1153 pb.csParam.ioCatPosition = catPosition;
1154 pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize);
1155
1156 /* search for fileName */
1157 searchInfo1.hFileInfo.ioNamePtr = (StringPtr)fileName;
1158 searchInfo2.hFileInfo.ioNamePtr = NULL;
1159
1160 /* only match files (not directories) */
1161 searchInfo1.hFileInfo.ioFlAttrib = 0x00;
1162 searchInfo2.hFileInfo.ioFlAttrib = ioDirMask;
1163
1164 error = PBCatSearchSyncCompat((CSParamPtr)&pb);
1165
1166 if ( (error == noErr) || /* If no errors or the end of catalog was */
1167 (error == eofErr) ) /* found, then the call was successful so */
1168 {
1169 *actMatchCount = pb.csParam.ioActMatchCount; /* return the match count */
1170 }
1171 else
1172 {
1173 *actMatchCount = 0; /* else no matches found */
1174 }
1175
1176 if ( (error == noErr) || /* If no errors */
1177 (error == catChangedErr) ) /* or there was a change in the catalog */
1178 {
1179 catPosition = pb.csParam.ioCatPosition;
1180 lastVRefNum = vRefNum;
1181 /* we can probably start the next search where we stopped this time */
1182 }
1183 else
1184 {
1185 catPosition.initialize = 0;
1186 /* start the next search from beginning of catalog */
1187 }
1188
1189 if ( pb.csParam.ioOptBuffer != NULL )
1190 {
1191 DisposePtr(pb.csParam.ioOptBuffer);
1192 }
1193
1194 return ( error );
1195 }
1196
1197 /*****************************************************************************/
1198
1199 pascal OSErr CreatorTypeFileSearch(ConstStr255Param volName,
1200 short vRefNum,
1201 OSType creator,
1202 OSType fileType,
1203 FSSpecPtr matches,
1204 long reqMatchCount,
1205 long *actMatchCount,
1206 Boolean newSearch)
1207 {
1208 CInfoPBRec searchInfo1, searchInfo2;
1209 HParamBlockRec pb;
1210 OSErr error;
1211 static CatPositionRec catPosition;
1212 static short lastVRefNum = 0;
1213
1214 /* get the real volume reference number */
1215 error = DetermineVRefNum(volName, vRefNum, &vRefNum);
1216 if ( error != noErr )
1217 return ( error );
1218
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, */
1229 {
1230 catPosition.initialize = 0; /* then search from beginning of catalog */
1231 }
1232 pb.csParam.ioCatPosition = catPosition;
1233 pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize);
1234
1235 /* no fileName */
1236 searchInfo1.hFileInfo.ioNamePtr = NULL;
1237 searchInfo2.hFileInfo.ioNamePtr = NULL;
1238
1239 /* only match files (not directories) */
1240 searchInfo1.hFileInfo.ioFlAttrib = 0x00;
1241 searchInfo2.hFileInfo.ioFlAttrib = ioDirMask;
1242
1243 /* search for creator; if creator = 0x00000000, ignore creator */
1244 searchInfo1.hFileInfo.ioFlFndrInfo.fdCreator = creator;
1245 if ( creator == (OSType)0x00000000 )
1246 {
1247 searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (OSType)0x00000000;
1248 }
1249 else
1250 {
1251 searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (OSType)0xffffffff;
1252 }
1253
1254 /* search for fileType; if fileType = 0x00000000, ignore fileType */
1255 searchInfo1.hFileInfo.ioFlFndrInfo.fdType = fileType;
1256 if ( fileType == (OSType)0x00000000 )
1257 {
1258 searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (OSType)0x00000000;
1259 }
1260 else
1261 {
1262 searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (OSType)0xffffffff;
1263 }
1264
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;
1270
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;
1275
1276 error = PBCatSearchSyncCompat((CSParamPtr)&pb);
1277
1278 if ( (error == noErr) || /* If no errors or the end of catalog was */
1279 (error == eofErr) ) /* found, then the call was successful so */
1280 {
1281 *actMatchCount = pb.csParam.ioActMatchCount; /* return the match count */
1282 }
1283 else
1284 {
1285 *actMatchCount = 0; /* else no matches found */
1286 }
1287
1288 if ( (error == noErr) || /* If no errors */
1289 (error == catChangedErr) ) /* or there was a change in the catalog */
1290 {
1291 catPosition = pb.csParam.ioCatPosition;
1292 lastVRefNum = vRefNum;
1293 /* we can probably start the next search where we stopped this time */
1294 }
1295 else
1296 {
1297 catPosition.initialize = 0;
1298 /* start the next search from beginning of catalog */
1299 }
1300
1301 if ( pb.csParam.ioOptBuffer != NULL )
1302 {
1303 DisposePtr(pb.csParam.ioOptBuffer);
1304 }
1305
1306 return ( error );
1307 }
1308
1309 /*****************************************************************************/