• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/ap/gpl/amule/wxWidgets-2.8.12/src/mac/classic/morefile/
1/*
2	File:		Search.c
3
4	Contains:	IndexedSearch and the PBCatSearch compatibility function.
5
6	Version:	MoreFiles
7
8	Copyright:	� 1992-2001 by Apple Computer, Inc., all rights reserved.
9
10	You may incorporate this sample code into your applications without
11	restriction, though the sample code has been provided "AS IS" and the
12	responsibility for its operation is 100% yours.  However, what you are
13	not permitted to do is to redistribute the source as "DSC Sample Code"
14	after having made changes. If you're going to re-distribute the source,
15	we require that you make it clear in the source that the code was
16	descended from Apple Sample Code, but that you've made changes.
17
18	File Ownership:
19
20		DRI:				Jim Luther
21
22		Other Contact:		Apple Macintosh Developer Technical Support
23							<http://developer.apple.com/bugreporter/>
24
25		Technology:			DTS Sample Code
26
27	Writers:
28
29		(JL)	Jim Luther
30
31	Change History (most recent first):
32
33		 <2>	  2/7/01	JL		Added standard header. Updated names of includes. Updated
34									various routines to use new calling convention of the
35									MoreFilesExtras accessor functions. Added TARGET_API_MAC_CARBON
36									conditional checks around TimeOutTask.
37		<1>		12/06/99	JL		MoreFiles 1.5.
38*/
39
40#include <MacTypes.h>
41#include <Gestalt.h>
42#include <Timer.h>
43#include <MacErrors.h>
44#include <MacMemory.h>
45#include <Files.h>
46#include <TextUtils.h>
47
48#define	__COMPILINGMOREFILES
49
50#include "MoreFiles.h"
51#include "MoreFilesExtras.h"
52
53#include "Search.h"
54
55/*****************************************************************************/
56
57enum
58{
59	/* Number of LevelRecs to add each time the searchStack is grown */
60	/* 20 levels is probably more than reasonable for most volumes. */
61	/* If more are needed, they are allocated 20 levels at a time. */
62	kAdditionalLevelRecs = 20
63};
64
65/*****************************************************************************/
66
67/*
68**	LevelRecs are used to store the directory ID and index whenever
69**	IndexedSearch needs to either scan a sub-directory, or return control
70**	to the caller because the call has timed out or the number of
71**	matches requested has been found. LevelRecs are stored in an array
72**	used as a stack.
73*/
74struct	LevelRec
75{
76	long	dirModDate;	/* for detecting most (but not all) catalog changes */
77	long	dirID;
78	short	index;
79};
80typedef struct LevelRec LevelRec;
81typedef LevelRec *LevelRecPtr, **LevelRecHandle;
82
83
84/*
85**	SearchPositionRec is my version of a CatPositionRec. It holds the
86**	information I need to resuming searching.
87*/
88#if PRAGMA_STRUCT_ALIGN
89#pragma options align=mac68k
90#endif
91struct SearchPositionRec
92{
93	long			initialize;		/* Goofy checksum of volume information used to make */
94									/* sure we're resuming a search on the same volume. */
95	unsigned short	stackDepth;		/* Current depth on searchStack. */
96	short			priv[11];		/* For future use... */
97};
98#if PRAGMA_STRUCT_ALIGN
99#pragma options align=reset
100#endif
101typedef struct SearchPositionRec SearchPositionRec;
102typedef SearchPositionRec *SearchPositionRecPtr;
103
104
105/*
106**	ExtendedTMTask is a TMTask record extended to hold the timer flag.
107*/
108#if PRAGMA_STRUCT_ALIGN
109#pragma options align=mac68k
110#endif
111struct ExtendedTMTask
112{
113	TMTask			theTask;
114	Boolean			stopSearch;		/* the Time Mgr task will set stopSearch to */
115									/* true when the timer expires */
116};
117#if PRAGMA_STRUCT_ALIGN
118#pragma options align=reset
119#endif
120typedef struct ExtendedTMTask ExtendedTMTask;
121typedef ExtendedTMTask *ExtendedTMTaskPtr;
122
123/*****************************************************************************/
124
125static	OSErr	CheckVol(ConstStr255Param pathname,
126						 short vRefNum,
127						 short *realVRefNum,
128						 long *volID);
129
130static	OSErr	CheckStack(unsigned short stackDepth,
131						   LevelRecHandle searchStack,
132						   Size *searchStackSize);
133
134static	OSErr	VerifyUserPB(CSParamPtr userPB,
135							 Boolean *includeFiles,
136							 Boolean *includeDirs,
137							 Boolean *includeNames);
138
139static	Boolean	IsSubString(ConstStr255Param aStringPtr,
140							ConstStr255Param subStringPtr);
141
142static	Boolean	CompareMasked(const long *data1,
143							  const long *data2,
144							  const long *mask,
145							  short longsToCompare);
146
147static	void	CheckForMatches(CInfoPBPtr cPB,
148								CSParamPtr userPB,
149								const Str63 matchName,
150								Boolean includeFiles,
151								Boolean includeDirs);
152
153#if	__WANTPASCALELIMINATION
154#undef	pascal
155#endif
156
157#if TARGET_RT_MAC_CFM || TARGET_API_MAC_CARBON
158
159static	pascal	void	TimeOutTask(TMTaskPtr tmTaskPtr);
160
161#else
162
163static	pascal	TMTaskPtr	GetTMTaskPtr(void);
164
165static	void	TimeOutTask(void);
166
167#endif
168
169#if	__WANTPASCALELIMINATION
170#define	pascal
171#endif
172
173static	long	GetDirModDate(short vRefNum,
174							  long dirID);
175
176/*****************************************************************************/
177
178/*
179**	CheckVol gets the volume's real vRefNum and builds a volID. The volID
180**	is used to help insure that calls to resume searching with IndexedSearch
181**	are to the same volume as the last call to IndexedSearch.
182*/
183static	OSErr	CheckVol(ConstStr255Param pathname,
184						 short vRefNum,
185						 short *realVRefNum,
186						 long *volID)
187{
188	HParamBlockRec pb;
189	OSErr error;
190
191	error = GetVolumeInfoNoName(pathname, vRefNum, &pb);
192	if ( error == noErr )
193	{
194		/* Return the real vRefNum */
195		*realVRefNum = pb.volumeParam.ioVRefNum;
196
197		/* Add together a bunch of things that aren't supposed to change on */
198		/* a mounted volume that's being searched and that should come up with */
199		/* a fairly unique number */
200		*volID = pb.volumeParam.ioVCrDate +
201				 pb.volumeParam.ioVRefNum +
202				 pb.volumeParam.ioVNmAlBlks +
203				 pb.volumeParam.ioVAlBlkSiz +
204				 pb.volumeParam.ioVFSID;
205	}
206	return ( error );
207}
208
209/*****************************************************************************/
210
211/*
212**	CheckStack checks the size of the search stack (array) to see if there's
213**	room to push another LevelRec. If not, CheckStack grows the stack by
214**	another kAdditionalLevelRecs elements.
215*/
216static	OSErr	CheckStack(unsigned short stackDepth,
217						   LevelRecHandle searchStack,
218						   Size *searchStackSize)
219{
220	OSErr	result;
221
222	if ( (*searchStackSize / sizeof(LevelRec)) == (stackDepth + 1) )
223	{
224		/* Time to grow stack */
225		SetHandleSize((Handle)searchStack, *searchStackSize + (kAdditionalLevelRecs * sizeof(LevelRec)));
226		result = MemError();	/* should be noErr */
227		*searchStackSize = GetHandleSize((Handle)searchStack);
228	}
229	else
230	{
231		result = noErr;
232	}
233
234	return ( result );
235}
236
237/*****************************************************************************/
238
239/*
240**	VerifyUserPB makes sure the parameter block passed to IndexedSearch has
241**	valid parameters. By making this check once, we don't have to worry about
242**	things like NULL pointers, strings being too long, etc.
243**	VerifyUserPB also determines if the search includes files and/or
244**	directories, and determines if a full or partial name search was requested.
245*/
246static	OSErr	VerifyUserPB(CSParamPtr userPB,
247							 Boolean *includeFiles,
248							 Boolean *includeDirs,
249							 Boolean *includeNames)
250{
251	CInfoPBPtr	searchInfo1;
252	CInfoPBPtr	searchInfo2;
253
254	searchInfo1 = userPB->ioSearchInfo1;
255	searchInfo2 = userPB->ioSearchInfo2;
256
257	/* ioMatchPtr cannot be NULL */
258	if ( userPB->ioMatchPtr == NULL )
259	{
260		goto ParamErrExit;
261	}
262
263	/* ioSearchInfo1 cannot be NULL */
264	if ( searchInfo1 == NULL )
265	{
266		goto ParamErrExit;
267	}
268
269	/* If any bits except partialName, fullName, or negate are set, then */
270	/* ioSearchInfo2 cannot be NULL because information in ioSearchInfo2 is required  */
271	if ( ((userPB->ioSearchBits & ~(fsSBPartialName | fsSBFullName | fsSBNegate)) != 0) &&
272		 ( searchInfo2 == NULL ))
273	{
274		goto ParamErrExit;
275	}
276
277	*includeFiles = false;
278	*includeDirs = false;
279	*includeNames = false;
280
281	if ( (userPB->ioSearchBits & (fsSBPartialName | fsSBFullName)) != 0 )
282	{
283		/* If any kind of name matching is requested, then ioNamePtr in */
284		/* ioSearchInfo1 cannot be NULL or a zero-length string */
285		if ( (searchInfo1->hFileInfo.ioNamePtr == NULL) ||
286			 (searchInfo1->hFileInfo.ioNamePtr[0] == 0) ||
287			 (searchInfo1->hFileInfo.ioNamePtr[0] > (sizeof(Str63) - 1)) )
288		{
289			goto ParamErrExit;
290		}
291
292		*includeNames = true;
293	}
294
295	if ( (userPB->ioSearchBits & fsSBFlAttrib) != 0 )
296	{
297		/* The only attributes you can search on are the directory flag */
298		/* and the locked flag. */
299		if ( (searchInfo2->hFileInfo.ioFlAttrib & ~(kioFlAttribDirMask | kioFlAttribLockedMask)) != 0 )
300		{
301			goto ParamErrExit;
302		}
303
304		/* interested in the directory bit? */
305		if ( (searchInfo2->hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 )
306		{
307			/* yes, so do they want just directories or just files? */
308			if ( (searchInfo1->hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 )
309			{
310				*includeDirs = true;
311			}
312			else
313			{
314				*includeFiles = true;
315			}
316		}
317		else
318		{
319			/* no interest in directory bit - get both files and directories */
320			*includeDirs = true;
321			*includeFiles = true;
322		}
323	}
324	else
325	{
326		/* no attribute checking - get both files and directories */
327		*includeDirs = true;
328		*includeFiles = true;
329	}
330
331	/* If directories are included in the search, */
332	/* then the locked attribute cannot be requested. */
333	if ( *includeDirs &&
334		 ((userPB->ioSearchBits & fsSBFlAttrib) != 0) &&
335		 ((searchInfo2->hFileInfo.ioFlAttrib & kioFlAttribLockedMask) != 0) )
336	{
337		goto ParamErrExit;
338	}
339
340	/* If files are included in the search, then there cannot be */
341	/* a search on the number of files. */
342	if ( *includeFiles &&
343		 ((userPB->ioSearchBits & fsSBDrNmFls) != 0) )
344	{
345		goto ParamErrExit;
346	}
347
348	/* If directories are included in the search, then there cannot */
349	/* be a search on file lengths. */
350	if ( *includeDirs &&
351		 ((userPB->ioSearchBits & (fsSBFlLgLen | fsSBFlPyLen | fsSBFlRLgLen | fsSBFlRPyLen)) != 0) )
352	{
353		goto ParamErrExit;
354	}
355
356	return ( noErr );
357
358ParamErrExit:
359	return ( paramErr );
360}
361
362/*****************************************************************************/
363
364/*
365**	IsSubString checks to see if a string is a substring of another string.
366**	Both input strings have already been converted to all uppercase using
367**	UprString (the same non-international call the File Manager uses).
368*/
369static	Boolean	IsSubString(ConstStr255Param aStringPtr,
370							ConstStr255Param subStringPtr)
371{
372	short	strLength;		/* length of string */
373	short	subStrLength;	/* length of subString */
374	Boolean	found;			/* result of test */
375	short	index;			/* current index into string */
376
377	found = false;
378	strLength = aStringPtr[0];
379	subStrLength = subStringPtr[0];
380
381	if ( subStrLength <= strLength)
382	{
383		register short	count;			/* search counter */
384		register short	strIndex;		/* running index into string */
385		register short	subStrIndex;	/* running index into subString */
386
387		/* start looking at first character */
388		index = 1;
389
390		/* continue looking until remaining string is shorter than substring */
391		count = strLength - subStrLength + 1;
392
393		do
394		{
395			strIndex = index;	/* start string index at index */
396			subStrIndex = 1;	/* start subString index at 1 */
397
398			while ( !found && (aStringPtr[strIndex] == subStringPtr[subStrIndex]) )
399			{
400				if ( subStrIndex == subStrLength )
401				{
402					/* all characters in subString were found */
403					found = true;
404				}
405				else
406				{
407					/* check next character of substring against next character of string */
408					++subStrIndex;
409					++strIndex;
410				}
411			}
412
413			if ( !found )
414			{
415				/* start substring search again at next string character */
416				++index;
417				--count;
418			}
419		} while ( count != 0 && (!found) );
420	}
421
422	return ( found );
423}
424
425/*****************************************************************************/
426
427/*
428**	CompareMasked does a bitwise comparison with mask on 1 or more longs.
429**	data1 and data2 are first exclusive-ORed together resulting with bits set
430**	where they are different. That value is then ANDed with the mask resulting
431**	with bits set if the test fails. true is returned if the tests pass.
432*/
433static	Boolean	CompareMasked(const long *data1,
434							  const long *data2,
435							  const long *mask,
436							  short longsToCompare)
437{
438	Boolean	result = true;
439
440	while ( (longsToCompare != 0) && (result == true) )
441	{
442		/* (*data1 ^ *data2) = bits that are different, so... */
443		/* ((*data1 ^ *data2) & *mask) = bits that are different that we're interested in */
444
445		if ( ((*data1 ^ *data2) & *mask) != 0 )
446			result = false;
447
448		++data1;
449		++data2;
450		++mask;
451		--longsToCompare;
452	}
453
454	return ( result );
455}
456
457/*****************************************************************************/
458
459/*
460**	Check for matches compares the search criteria in userPB to the file
461**	system object in cPB. If there's a match, then the information in cPB is
462**	is added to the match array and the actual match count is incremented.
463*/
464static	void	CheckForMatches(CInfoPBPtr cPB,
465								CSParamPtr userPB,
466								const Str63 matchName,
467								Boolean includeFiles,
468								Boolean includeDirs)
469{
470	long		searchBits;
471	CInfoPBPtr	searchInfo1;
472	CInfoPBPtr	searchInfo2;
473	Str63		itemName;		/* copy of object's name for partial name matching */
474	Boolean		foundMatch;
475
476	foundMatch = false;			/* default to no match */
477
478	searchBits = userPB->ioSearchBits;
479	searchInfo1 = userPB->ioSearchInfo1;
480	searchInfo2 = userPB->ioSearchInfo2;
481
482	/* Into the if statements that go on forever... */
483
484	if ( (cPB->hFileInfo.ioFlAttrib & kioFlAttribDirMask) == 0 )
485	{
486		if (!includeFiles)
487		{
488			goto Failed;
489		}
490	}
491	else
492	{
493		if (!includeDirs)
494		{
495			goto Failed;
496		}
497	}
498
499	if ( (searchBits & fsSBPartialName) != 0 )
500	{
501		if ( (cPB->hFileInfo.ioNamePtr[0] > 0) &&
502			 (cPB->hFileInfo.ioNamePtr[0] <= (sizeof(Str63) - 1)) )
503		{
504			/* Make uppercase copy of object name */
505			BlockMoveData(cPB->hFileInfo.ioNamePtr,
506							itemName,
507							cPB->hFileInfo.ioNamePtr[0] + 1);
508			/* Use the same non-international call the File Manager uses */
509			UpperString(itemName, true);
510		}
511		else
512		{
513			goto Failed;
514		}
515
516		{
517			if ( !IsSubString(itemName, matchName) )
518			{
519				goto Failed;
520			}
521			else if ( searchBits == fsSBPartialName )
522			{
523				/* optimize for name matching only since it is most common way to search */
524				goto Hit;
525			}
526		}
527	}
528
529	if ( (searchBits & fsSBFullName) != 0 )
530	{
531		/* Use the same non-international call the File Manager uses */
532		if ( !EqualString(cPB->hFileInfo.ioNamePtr, matchName, false, true) )
533		{
534			goto Failed;
535		}
536		else if ( searchBits == fsSBFullName )
537		{
538			/* optimize for name matching only since it is most common way to search */
539			goto Hit;
540		}
541	}
542
543	if ( (searchBits & fsSBFlParID) != 0 )
544	{
545		if ( ((unsigned long)(cPB->hFileInfo.ioFlParID) < (unsigned long)(searchInfo1->hFileInfo.ioFlParID)) ||
546			 ((unsigned long)(cPB->hFileInfo.ioFlParID) > (unsigned long)(searchInfo2->hFileInfo.ioFlParID)) )
547		{
548			goto Failed;
549		}
550	}
551
552	if ( (searchBits & fsSBFlAttrib) != 0 )
553	{
554		if ( ((cPB->hFileInfo.ioFlAttrib ^ searchInfo1->hFileInfo.ioFlAttrib) &
555			  searchInfo2->hFileInfo.ioFlAttrib) != 0 )
556		{
557			goto Failed;
558		}
559	}
560
561	if ( (searchBits & fsSBDrNmFls) != 0 )
562	{
563		if ( ((unsigned long)(cPB->dirInfo.ioDrNmFls) < (unsigned long)(searchInfo1->dirInfo.ioDrNmFls)) ||
564			 ((unsigned long)(cPB->dirInfo.ioDrNmFls) > (unsigned long)(searchInfo2->dirInfo.ioDrNmFls)) )
565		{
566			goto Failed;
567		}
568	}
569
570	if ( (searchBits & fsSBFlFndrInfo) != 0 )	/* fsSBFlFndrInfo is same as fsSBDrUsrWds */
571	{
572		if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlFndrInfo),
573							(long *)&(searchInfo1->hFileInfo.ioFlFndrInfo),
574							(long *)&(searchInfo2->hFileInfo.ioFlFndrInfo),
575							sizeof(FInfo) / sizeof(long)) )
576		{
577			goto Failed;
578		}
579	}
580
581	if ( (searchBits & fsSBFlXFndrInfo) != 0 )	/* fsSBFlXFndrInfo is same as fsSBDrFndrInfo */
582	{
583		if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlXFndrInfo),
584							(long *)&(searchInfo1->hFileInfo.ioFlXFndrInfo),
585							(long *)&(searchInfo2->hFileInfo.ioFlXFndrInfo),
586							sizeof(FXInfo) / sizeof(long)) )
587		{
588			goto Failed;
589		}
590	}
591
592	if ( (searchBits & fsSBFlLgLen) != 0 )
593	{
594		if ( ((unsigned long)(cPB->hFileInfo.ioFlLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlLgLen)) ||
595			 ((unsigned long)(cPB->hFileInfo.ioFlLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlLgLen)) )
596		{
597			goto Failed;
598		}
599	}
600
601	if ( (searchBits & fsSBFlPyLen) != 0 )
602	{
603		if ( ((unsigned long)(cPB->hFileInfo.ioFlPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlPyLen)) ||
604			 ((unsigned long)(cPB->hFileInfo.ioFlPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlPyLen)) )
605		{
606			goto Failed;
607		}
608	}
609
610	if ( (searchBits & fsSBFlRLgLen) != 0 )
611	{
612		if ( ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRLgLen)) ||
613			 ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRLgLen)) )
614		{
615			goto Failed;
616		}
617	}
618
619	if ( (searchBits & fsSBFlRPyLen) != 0 )
620	{
621		if ( ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRPyLen)) ||
622			 ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRPyLen)) )
623		{
624			goto Failed;
625		}
626	}
627
628	if ( (searchBits & fsSBFlCrDat) != 0 )	/* fsSBFlCrDat is same as fsSBDrCrDat */
629	{
630		if ( ((unsigned long)(cPB->hFileInfo.ioFlCrDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlCrDat)) ||
631			 ((unsigned long)(cPB->hFileInfo.ioFlCrDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlCrDat)) )
632		{
633			goto Failed;
634		}
635	}
636
637	if ( (searchBits & fsSBFlMdDat) != 0 )	/* fsSBFlMdDat is same as fsSBDrMdDat */
638	{
639		if ( ((unsigned long)(cPB->hFileInfo.ioFlMdDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlMdDat)) ||
640			 ((unsigned long)(cPB->hFileInfo.ioFlMdDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlMdDat)) )
641		{
642			goto Failed;
643		}
644	}
645
646	if ( (searchBits & fsSBFlBkDat) != 0 )	/* fsSBFlBkDat is same as fsSBDrBkDat */
647	{
648		if ( ((unsigned long)(cPB->hFileInfo.ioFlBkDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlBkDat)) ||
649			 ((unsigned long)(cPB->hFileInfo.ioFlBkDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlBkDat)) )
650		{
651			goto Failed;
652		}
653	}
654
655	/* Hey, we passed all of the tests! */
656
657Hit:
658	foundMatch = true;
659
660/* foundMatch is false if code jumps to Failed */
661Failed:
662	/* Do we reverse our findings? */
663	if ( (searchBits & fsSBNegate) != 0 )
664	{
665		foundMatch = !foundMatch;	/* matches are not, not matches are */
666	}
667
668	if ( foundMatch )
669	{
670
671		/* Move the match into the match buffer */
672		userPB->ioMatchPtr[userPB->ioActMatchCount].vRefNum = cPB->hFileInfo.ioVRefNum;
673		userPB->ioMatchPtr[userPB->ioActMatchCount].parID = cPB->hFileInfo.ioFlParID;
674		if ( cPB->hFileInfo.ioNamePtr[0] > 63 )
675		{
676			cPB->hFileInfo.ioNamePtr[0] = 63;
677		}
678		BlockMoveData(cPB->hFileInfo.ioNamePtr,
679					  userPB->ioMatchPtr[userPB->ioActMatchCount].name,
680					  cPB->hFileInfo.ioNamePtr[0] + 1);
681
682		/* increment the actual count */
683		++(userPB->ioActMatchCount);
684	}
685}
686
687/*****************************************************************************/
688
689/*
690**	TimeOutTask is executed when the timer goes off. It simply sets the
691**	stopSearch field to true. After each object is found and possibly added
692**	to the matches buffer, stopSearch is checked to see if the search should
693**	continue.
694*/
695
696#if	__WANTPASCALELIMINATION
697#undef	pascal
698#endif
699
700#if TARGET_RT_MAC_CFM || TARGET_API_MAC_CARBON
701
702static	pascal	void	TimeOutTask(TMTaskPtr tmTaskPtr)
703{
704	((ExtendedTMTaskPtr)tmTaskPtr)->stopSearch = true;
705}
706
707#else
708
709static	pascal	TMTaskPtr	GetTMTaskPtr(void)
710	ONEWORDINLINE(0x2e89);	/* MOVE.L A1,(SP) */
711
712static	void	TimeOutTask(void)
713{
714	((ExtendedTMTaskPtr)GetTMTaskPtr())->stopSearch = true;
715}
716
717#endif
718
719#if	__WANTPASCALELIMINATION
720#define	pascal
721#endif
722
723/*****************************************************************************/
724
725/*
726**	GetDirModDate returns the modification date of a directory. If there is
727**	an error getting the modification date, -1 is returned to indicate
728**	something went wrong.
729*/
730static	long	GetDirModDate(short vRefNum,
731							  long dirID)
732{
733	CInfoPBRec pb;
734	Str31 tempName;
735	long modDate;
736
737	/* Protection against File Sharing problem */
738	tempName[0] = 0;
739	pb.dirInfo.ioNamePtr = tempName;
740	pb.dirInfo.ioVRefNum = vRefNum;
741	pb.dirInfo.ioDrDirID = dirID;
742	pb.dirInfo.ioFDirIndex = -1;	/* use ioDrDirID */
743
744	if ( PBGetCatInfoSync(&pb) == noErr )
745	{
746		modDate = pb.dirInfo.ioDrMdDat;
747	}
748	else
749	{
750		modDate = -1;
751	}
752
753	return ( modDate );
754}
755
756/*****************************************************************************/
757
758pascal	OSErr	IndexedSearch(CSParamPtr pb,
759							  long dirID)
760{
761	static LevelRecHandle	searchStack = NULL;		/* static handle to LevelRec stack */
762	static Size				searchStackSize = 0;	/* size of static handle */
763	SearchPositionRecPtr	catPosition;
764	long					modDate;
765	short					index = -1 ;
766	ExtendedTMTask			timerTask;
767	OSErr					result;
768	short					realVRefNum;
769	Str63					itemName;
770	CInfoPBRec				cPB;
771	long					tempLong;
772	Boolean					includeFiles;
773	Boolean					includeDirs;
774	Boolean					includeNames;
775	Str63					upperName;
776
777	timerTask.stopSearch = false;	/* don't stop yet! */
778
779	/* If request has a timeout, install a Time Manager task. */
780	if ( pb->ioSearchTime != 0 )
781	{
782		/* Start timer */
783		timerTask.theTask.tmAddr = NewTimerUPP(TimeOutTask);
784		InsTime((QElemPtr)&(timerTask.theTask));
785		PrimeTime((QElemPtr)&(timerTask.theTask), pb->ioSearchTime);
786	}
787
788	/* Check the parameter block passed for things that we don't want to assume */
789	/* are OK later in the code. For example, make sure pointers to data structures */
790	/* and buffers are not NULL.  And while we're in there, see if the request */
791	/* specified searching for files, directories, or both, and see if the search */
792	/* was by full or partial name. */
793	result = VerifyUserPB(pb, &includeFiles, &includeDirs, &includeNames);
794	if ( result == noErr )
795	{
796		pb->ioActMatchCount = 0;	/* no matches yet */
797
798		if ( includeNames )
799		{
800			/* The search includes seach by full or partial name. */
801			/* Make an upper case copy of the match string to pass to */
802			/* CheckForMatches. */
803			BlockMoveData(pb->ioSearchInfo1->hFileInfo.ioNamePtr,
804							upperName,
805							pb->ioSearchInfo1->hFileInfo.ioNamePtr[0] + 1);
806			/* Use the same non-international call the File Manager uses */
807			UpperString(upperName, true);
808		}
809
810		/* Prevent casting to my type throughout code */
811		catPosition = (SearchPositionRecPtr)&pb->ioCatPosition;
812
813		/* Create searchStack first time called */
814		if ( searchStack == NULL )
815		{
816			searchStack = (LevelRecHandle)NewHandle(kAdditionalLevelRecs * sizeof(LevelRec));
817		}
818
819		/* Make sure searchStack really exists */
820		if ( searchStack != NULL )
821		{
822			searchStackSize = GetHandleSize((Handle)searchStack);
823
824			/* See if the search is a new search or a resumed search. */
825			if ( catPosition->initialize == 0 )
826			{
827				/* New search. */
828
829				/* Get the real vRefNum and fill in catPosition->initialize. */
830				result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &catPosition->initialize);
831				if ( result == noErr )
832				{
833					/* clear searchStack */
834					catPosition->stackDepth = 0;
835
836					/* use dirID parameter passed and... */
837					index = -1;	/* start with the passed directory itself! */
838				}
839			}
840			else
841			{
842				/* We're resuming a search. */
843
844				/* Get the real vRefNum and make sure catPosition->initialize is valid. */
845				result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &tempLong);
846				if ( result == noErr )
847				{
848					/* Make sure the resumed search is to the same volume! */
849					if ( catPosition->initialize == tempLong )
850					{
851						/* For resume, catPosition->stackDepth > 0 */
852						if ( catPosition->stackDepth > 0 )
853						{
854							/* Position catPosition->stackDepth to access last saved level */
855							--(catPosition->stackDepth);
856
857							/* Get the dirID and index for the next item */
858							dirID = (*searchStack)[catPosition->stackDepth].dirID;
859							index = (*searchStack)[catPosition->stackDepth].index;
860
861							/* Check the dir's mod date against the saved mode date on our "stack" */
862							modDate = GetDirModDate(realVRefNum, dirID);
863							if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate )
864							{
865								result = catChangedErr;
866							}
867						}
868						else
869						{
870							/* Invalid catPosition record was passed */
871							result = paramErr;
872						}
873					}
874					else
875					{
876						/* The volume is not the same */
877						result = catChangedErr;
878					}
879				}
880			}
881
882			if ( result == noErr )
883			{
884				/* ioNamePtr and ioVRefNum only need to be set up once. */
885				cPB.hFileInfo.ioNamePtr = itemName;
886				cPB.hFileInfo.ioVRefNum = realVRefNum;
887
888				/*
889				**	Here's the loop that:
890				**		Finds the next item on the volume.
891				**		If noErr, calls the code to check for matches and add matches
892				**			to the match buffer.
893				**		Sets up dirID and index for to find the next item on the volume.
894				**
895				**	The looping ends when:
896				**		(a) an unexpected error is returned by PBGetCatInfo. All that
897				**			is expected is noErr and fnfErr (after the last item in a
898				**			directory is found).
899				**		(b) the caller specified a timeout and our Time Manager task
900				**			has fired.
901				**		(c) the number of matches requested by the caller has been found.
902				**		(d) the last item on the volume was found.
903				*/
904				do
905				{
906					/* get the next item */
907					cPB.hFileInfo.ioFDirIndex = index;
908					cPB.hFileInfo.ioDirID = dirID;
909					result = PBGetCatInfoSync(&cPB);
910					if ( index != -1 )
911					{
912						if ( result == noErr )
913						{
914							/* We found something */
915
916							CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
917
918							++index;
919							if ( (cPB.dirInfo.ioFlAttrib & kioFlAttribDirMask) != 0 )
920							{
921								/* It's a directory */
922
923								result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
924								if ( result == noErr )
925								{
926									/* Save the current state on the searchStack */
927									/* when we come back, this is where we'll start */
928									(*searchStack)[catPosition->stackDepth].dirID = dirID;
929									(*searchStack)[catPosition->stackDepth].index = index;
930									(*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
931
932									/* position catPosition->stackDepth for next saved level */
933									++(catPosition->stackDepth);
934
935									/* The next item to get is the 1st item in the child directory */
936									dirID = cPB.dirInfo.ioDrDirID;
937									index = 1;
938								}
939							}
940							/* else do nothing for files */
941						}
942						else
943						{
944							/* End of directory found (or we had some error and that */
945							/* means we have to drop out of this directory). */
946							/* Restore last thing put on stack and */
947							/* see if we need to continue or quit. */
948							if ( catPosition->stackDepth > 0 )
949							{
950								/* position catPosition->stackDepth to access last saved level */
951								--(catPosition->stackDepth);
952
953								dirID = (*searchStack)[catPosition->stackDepth].dirID;
954								index = (*searchStack)[catPosition->stackDepth].index;
955
956								/* Check the dir's mod date against the saved mode date on our "stack" */
957								modDate = GetDirModDate(realVRefNum, dirID);
958								if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate )
959								{
960									result = catChangedErr;
961								}
962								else
963								{
964									/* Going back to ancestor directory. */
965									/* Clear error so we can continue. */
966									result = noErr;
967								}
968							}
969							else
970							{
971								/* We hit the bottom of the stack, so we'll let the */
972								/* the eofErr drop us out of the loop. */
973								result = eofErr;
974							}
975						}
976					}
977					else
978					{
979						/* Special case for index == -1; that means that we're starting */
980						/* a new search and so the first item to check is the directory */
981						/* passed to us. */
982						if ( result == noErr )
983						{
984							/* We found something */
985
986							CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
987
988							/* Now, set the index to 1 and then we're ready to look inside */
989							/* the passed directory. */
990							index = 1;
991						}
992					}
993				} while ( (!timerTask.stopSearch) &&	/* timer hasn't fired */
994						  (result == noErr) &&			/* no unexpected errors */
995						  (pb->ioReqMatchCount > pb->ioActMatchCount) ); /* we haven't found our limit */
996
997				/* Did we drop out of the loop because of timeout or */
998				/* ioReqMatchCount was found? */
999				if ( result == noErr )
1000				{
1001					result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
1002					if ( result == noErr )
1003					{
1004						/* Either there was a timeout or ioReqMatchCount was reached. */
1005						/* Save the dirID and index for the next time we're called. */
1006
1007						(*searchStack)[catPosition->stackDepth].dirID = dirID;
1008						(*searchStack)[catPosition->stackDepth].index = index;
1009						(*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
1010
1011						/* position catPosition->stackDepth for next saved level */
1012
1013						++(catPosition->stackDepth);
1014					}
1015				}
1016			}
1017		}
1018		else
1019		{
1020			/* searchStack Handle could not be allocated */
1021			result = memFullErr;
1022		}
1023	}
1024
1025	if ( pb->ioSearchTime != 0 )
1026	{
1027		/* Stop Time Manager task here if it was installed */
1028		RmvTime((QElemPtr)&(timerTask.theTask));
1029		DisposeTimerUPP(timerTask.theTask.tmAddr);
1030	}
1031
1032	return ( result );
1033}
1034
1035/*****************************************************************************/
1036
1037pascal OSErr PBCatSearchSyncCompat(CSParamPtr paramBlock)
1038{
1039	OSErr 					result;
1040	Boolean					supportsCatSearch;
1041	GetVolParmsInfoBuffer	volParmsInfo;
1042	long					infoSize;
1043#if !__MACOSSEVENORLATER
1044	static Boolean			fullExtFSDispatchingtested = false;
1045	static Boolean			hasFullExtFSDispatching = false;
1046	long					response;
1047#endif
1048
1049	result = noErr;
1050
1051#if !__MACOSSEVENORLATER
1052	/* See if File Manager will pass CatSearch requests to external file systems */
1053	/* we'll store the results in a static variable so we don't have to call Gestalt */
1054	/* everytime we're called. (System 7.0 and later always do this) */
1055	if ( !fullExtFSDispatchingtested )
1056	{
1057		fullExtFSDispatchingtested = true;
1058		if ( Gestalt(gestaltFSAttr, &response) == noErr )
1059		{
1060			hasFullExtFSDispatching = ((response & (1L << gestaltFullExtFSDispatching)) != 0);
1061		}
1062	}
1063#endif
1064
1065	/* CatSearch is a per volume attribute, so we have to check each time we're */
1066	/* called to see if it is available on the volume specified. */
1067	supportsCatSearch = false;
1068#if !__MACOSSEVENORLATER
1069	if ( hasFullExtFSDispatching )
1070#endif
1071	{
1072		infoSize = sizeof(GetVolParmsInfoBuffer);
1073		result = HGetVolParms(paramBlock->ioNamePtr, paramBlock->ioVRefNum,
1074							&volParmsInfo, &infoSize);
1075		if ( result == noErr )
1076		{
1077			supportsCatSearch = hasCatSearch(&volParmsInfo);
1078		}
1079	}
1080
1081	/* noErr or paramErr is OK here. */
1082	/* paramErr just means that GetVolParms isn't supported by this volume */
1083	if ( (result == noErr) || (result == paramErr) )
1084	{
1085		if ( supportsCatSearch )
1086		{
1087			/* Volume supports CatSearch so use it. */
1088			/* CatSearch is faster than an indexed search. */
1089			result = PBCatSearchSync(paramBlock);
1090		}
1091		else
1092		{
1093			/* Volume doesn't support CatSearch so */
1094			/* search using IndexedSearch from root directory. */
1095			result = IndexedSearch(paramBlock, fsRtDirID);
1096		}
1097	}
1098
1099	return ( result );
1100}
1101
1102/*****************************************************************************/
1103
1104pascal	OSErr	NameFileSearch(ConstStr255Param volName,
1105							   short vRefNum,
1106							   ConstStr255Param fileName,
1107							   FSSpecPtr matches,
1108							   long reqMatchCount,
1109							   long *actMatchCount,
1110							   Boolean newSearch,
1111							   Boolean partial)
1112{
1113	CInfoPBRec		searchInfo1, searchInfo2;
1114	HParamBlockRec	pb;
1115	OSErr			error;
1116	static CatPositionRec catPosition;
1117	static short	lastVRefNum = 0;
1118
1119	/* get the real volume reference number */
1120	error = DetermineVRefNum(volName, vRefNum, &vRefNum);
1121	if ( error != noErr )
1122		return ( error );
1123
1124	pb.csParam.ioNamePtr = NULL;
1125	pb.csParam.ioVRefNum = vRefNum;
1126	pb.csParam.ioMatchPtr = matches;
1127	pb.csParam.ioReqMatchCount = reqMatchCount;
1128	if ( partial )	/* tell CatSearch what we're looking for: */
1129	{
1130		pb.csParam.ioSearchBits = fsSBPartialName + fsSBFlAttrib;	/* partial name file matches or */
1131	}
1132	else
1133	{
1134		pb.csParam.ioSearchBits =  fsSBFullName + fsSBFlAttrib;		/* full name file matches */
1135	}
1136	pb.csParam.ioSearchInfo1 = &searchInfo1;
1137	pb.csParam.ioSearchInfo2 = &searchInfo2;
1138	pb.csParam.ioSearchTime = 0;
1139	if ( (newSearch) ||				/* If caller specified new search */
1140		 (lastVRefNum != vRefNum) )	/* or if last search was to another volume, */
1141	{
1142		catPosition.initialize = 0;	/* then search from beginning of catalog */
1143	}
1144	pb.csParam.ioCatPosition = catPosition;
1145	pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize);
1146
1147	/* search for fileName */
1148	searchInfo1.hFileInfo.ioNamePtr = (StringPtr)fileName;
1149	searchInfo2.hFileInfo.ioNamePtr = NULL;
1150
1151	/* only match files (not directories) */
1152	searchInfo1.hFileInfo.ioFlAttrib = 0x00;
1153	searchInfo2.hFileInfo.ioFlAttrib = kioFlAttribDirMask;
1154
1155	error = PBCatSearchSyncCompat((CSParamPtr)&pb);
1156
1157	if ( (error == noErr) ||							/* If no errors or the end of catalog was */
1158		 (error == eofErr) )							/* found, then the call was successful so */
1159	{
1160		*actMatchCount = pb.csParam.ioActMatchCount;	/* return the match count */
1161	}
1162	else
1163	{
1164		*actMatchCount = 0;							/* else no matches found */
1165	}
1166
1167	if ( (error == noErr) ||						/* If no errors */
1168		 (error == catChangedErr) )					/* or there was a change in the catalog */
1169	{
1170		catPosition = pb.csParam.ioCatPosition;
1171		lastVRefNum = vRefNum;
1172			/* we can probably start the next search where we stopped this time */
1173	}
1174	else
1175	{
1176		catPosition.initialize = 0;
1177			/* start the next search from beginning of catalog */
1178	}
1179
1180	if ( pb.csParam.ioOptBuffer != NULL )
1181	{
1182		DisposePtr(pb.csParam.ioOptBuffer);
1183	}
1184
1185	return ( error );
1186}
1187
1188/*****************************************************************************/
1189
1190pascal	OSErr	CreatorTypeFileSearch(ConstStr255Param volName,
1191									  short vRefNum,
1192									  OSType creator,
1193									  OSType fileType,
1194									  FSSpecPtr matches,
1195									  long reqMatchCount,
1196									  long *actMatchCount,
1197									  Boolean newSearch)
1198{
1199	CInfoPBRec		searchInfo1, searchInfo2;
1200	HParamBlockRec	pb;
1201	OSErr			error;
1202	static CatPositionRec catPosition;
1203	static short	lastVRefNum = 0;
1204
1205	/* get the real volume reference number */
1206	error = DetermineVRefNum(volName, vRefNum, &vRefNum);
1207	if ( error != noErr )
1208		return ( error );
1209
1210	pb.csParam.ioNamePtr = NULL;
1211	pb.csParam.ioVRefNum = vRefNum;
1212	pb.csParam.ioMatchPtr = matches;
1213	pb.csParam.ioReqMatchCount = reqMatchCount;
1214	pb.csParam.ioSearchBits = fsSBFlAttrib + fsSBFlFndrInfo;	/* Looking for finder info file matches */
1215	pb.csParam.ioSearchInfo1 = &searchInfo1;
1216	pb.csParam.ioSearchInfo2 = &searchInfo2;
1217	pb.csParam.ioSearchTime = 0;
1218	if ( (newSearch) ||				/* If caller specified new search */
1219		 (lastVRefNum != vRefNum) )	/* or if last search was to another volume, */
1220	{
1221		catPosition.initialize = 0;	/* then search from beginning of catalog */
1222	}
1223	pb.csParam.ioCatPosition = catPosition;
1224	pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize);
1225
1226	/* no fileName */
1227	searchInfo1.hFileInfo.ioNamePtr = NULL;
1228	searchInfo2.hFileInfo.ioNamePtr = NULL;
1229
1230	/* only match files (not directories) */
1231	searchInfo1.hFileInfo.ioFlAttrib = 0x00;
1232	searchInfo2.hFileInfo.ioFlAttrib = kioFlAttribDirMask;
1233
1234	/* search for creator; if creator = 0x00000000, ignore creator */
1235	searchInfo1.hFileInfo.ioFlFndrInfo.fdCreator = creator;
1236	if ( creator == (OSType)0x00000000 )
1237	{
1238		searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (OSType)0x00000000;
1239	}
1240	else
1241	{
1242		searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (OSType)0xffffffff;
1243	}
1244
1245	/* search for fileType; if fileType = 0x00000000, ignore fileType */
1246	searchInfo1.hFileInfo.ioFlFndrInfo.fdType = fileType;
1247	if ( fileType == (OSType)0x00000000 )
1248	{
1249		searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (OSType)0x00000000;
1250	}
1251	else
1252	{
1253		searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (OSType)0xffffffff;
1254	}
1255
1256	/* zero all other FInfo fields */
1257	searchInfo1.hFileInfo.ioFlFndrInfo.fdFlags = 0;
1258	searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
1259	searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
1260	searchInfo1.hFileInfo.ioFlFndrInfo.fdFldr = 0;
1261
1262	searchInfo2.hFileInfo.ioFlFndrInfo.fdFlags = 0;
1263	searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
1264	searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
1265	searchInfo2.hFileInfo.ioFlFndrInfo.fdFldr = 0;
1266
1267	error = PBCatSearchSyncCompat((CSParamPtr)&pb);
1268
1269	if ( (error == noErr) ||							/* If no errors or the end of catalog was */
1270		 (error == eofErr) )							/* found, then the call was successful so */
1271	{
1272		*actMatchCount = pb.csParam.ioActMatchCount;	/* return the match count */
1273	}
1274	else
1275	{
1276		*actMatchCount = 0;							/* else no matches found */
1277	}
1278
1279	if ( (error == noErr) ||						/* If no errors */
1280		 (error == catChangedErr) )					/* or there was a change in the catalog */
1281	{
1282		catPosition = pb.csParam.ioCatPosition;
1283		lastVRefNum = vRefNum;
1284			/* we can probably start the next search where we stopped this time */
1285	}
1286	else
1287	{
1288		catPosition.initialize = 0;
1289			/* start the next search from beginning of catalog */
1290	}
1291
1292	if ( pb.csParam.ioOptBuffer != NULL )
1293	{
1294		DisposePtr(pb.csParam.ioOptBuffer);
1295	}
1296
1297	return ( error );
1298}
1299
1300/*****************************************************************************/
1301