--- /dev/null
+/*
+** Apple Macintosh Developer Technical Support
+**
+** IndexedSearch and the PBCatSearch compatibility function.
+**
+** by Jim Luther, Apple Developer Technical Support Emeritus
+**
+** File: Search.c
+**
+** Copyright © 1992-1998 Apple Computer, Inc.
+** All rights reserved.
+**
+** You may incorporate this sample code into your applications without
+** restriction, though the sample code has been provided "AS IS" and the
+** responsibility for its operation is 100% yours. However, what you are
+** not permitted to do is to redistribute the source as "DSC Sample Code"
+** after having made changes. If you're going to re-distribute the source,
+** we require that you make it clear in the source that the code was
+** descended from Apple Sample Code, but that you've made changes.
+*/
+
+#include <Types.h>
+#include <Gestalt.h>
+#include <Timer.h>
+#include <Errors.h>
+#include <Memory.h>
+#include <Files.h>
+#include <TextUtils.h>
+
+#define __COMPILINGMOREFILES
+
+#include "morefile.h"
+#include "moreextr.h"
+#include "mfsearch.h"
+
+/*****************************************************************************/
+
+enum
+{
+ /* Number of LevelRecs to add each time the searchStack is grown */
+ /* 20 levels is probably more than reasonable for most volumes. */
+ /* If more are needed, they are allocated 20 levels at a time. */
+ kAdditionalLevelRecs = 20
+};
+
+/*****************************************************************************/
+
+/*
+** LevelRecs are used to store the directory ID and index whenever
+** IndexedSearch needs to either scan a sub-directory, or return control
+** to the caller because the call has timed out or the number of
+** matches requested has been found. LevelRecs are stored in an array
+** used as a stack.
+*/
+struct LevelRec
+{
+ long dirModDate; /* for detecting most (but not all) catalog changes */
+ long dirID;
+ short index;
+};
+typedef struct LevelRec LevelRec;
+typedef LevelRec *LevelRecPtr, **LevelRecHandle;
+
+
+/*
+** SearchPositionRec is my version of a CatPositionRec. It holds the
+** information I need to resuming searching.
+*/
+#if PRAGMA_STRUCT_ALIGN
+ #pragma options align=mac68k
+#elif PRAGMA_STRUCT_PACKPUSH
+ #pragma pack(push, 2)
+#elif PRAGMA_STRUCT_PACK
+ #pragma pack(2)
+#endif
+struct SearchPositionRec
+{
+ long initialize; /* Goofy checksum of volume information used to make */
+ /* sure we're resuming a search on the same volume. */
+ unsigned short stackDepth; /* Current depth on searchStack. */
+ short priv[11]; /* For future use... */
+};
+#if PRAGMA_STRUCT_ALIGN
+ #pragma options align=reset
+#elif PRAGMA_STRUCT_PACKPUSH
+ #pragma pack(pop)
+#elif PRAGMA_STRUCT_PACK
+ #pragma pack()
+#endif
+typedef struct SearchPositionRec SearchPositionRec;
+typedef SearchPositionRec *SearchPositionRecPtr;
+
+
+/*
+** ExtendedTMTask is a TMTask record extended to hold the timer flag.
+*/
+#if PRAGMA_STRUCT_ALIGN
+ #pragma options align=mac68k
+#elif PRAGMA_STRUCT_PACKPUSH
+ #pragma pack(push, 2)
+#elif PRAGMA_STRUCT_PACK
+ #pragma pack(2)
+#endif
+struct ExtendedTMTask
+{
+ TMTask theTask;
+ Boolean stopSearch; /* the Time Mgr task will set stopSearch to */
+ /* true when the timer expires */
+};
+#if PRAGMA_STRUCT_ALIGN
+ #pragma options align=reset
+#elif PRAGMA_STRUCT_PACKPUSH
+ #pragma pack(pop)
+#elif PRAGMA_STRUCT_PACK
+ #pragma pack()
+#endif
+typedef struct ExtendedTMTask ExtendedTMTask;
+typedef ExtendedTMTask *ExtendedTMTaskPtr;
+
+/*****************************************************************************/
+
+static OSErr CheckVol(ConstStr255Param pathname,
+ short vRefNum,
+ short *realVRefNum,
+ long *volID);
+
+static OSErr CheckStack(unsigned short stackDepth,
+ LevelRecHandle searchStack,
+ Size *searchStackSize);
+
+static OSErr VerifyUserPB(CSParamPtr userPB,
+ Boolean *includeFiles,
+ Boolean *includeDirs,
+ Boolean *includeNames);
+
+static Boolean IsSubString(ConstStr255Param aStringPtr,
+ ConstStr255Param subStringPtr);
+
+static Boolean CompareMasked(const long *data1,
+ const long *data2,
+ const long *mask,
+ short longsToCompare);
+
+static void CheckForMatches(CInfoPBPtr cPB,
+ CSParamPtr userPB,
+ const Str63 matchName,
+ Boolean includeFiles,
+ Boolean includeDirs);
+
+#if __WANTPASCALELIMINATION
+#undef pascal
+#endif
+
+#if TARGET_CARBON
+static pascal void TimeOutTask(TMTaskPtr tmTaskPtr);
+
+#else
+#if GENERATINGCFM
+
+static pascal void TimeOutTask(TMTaskPtr tmTaskPtr);
+
+#else
+
+static pascal TMTaskPtr GetTMTaskPtr(void);
+
+static void TimeOutTask(void);
+
+#endif
+#endif
+
+#if __WANTPASCALELIMINATION
+#define pascal
+#endif
+
+static long GetDirModDate(short vRefNum,
+ long dirID);
+
+/*****************************************************************************/
+
+/*
+** CheckVol gets the volume's real vRefNum and builds a volID. The volID
+** is used to help insure that calls to resume searching with IndexedSearch
+** are to the same volume as the last call to IndexedSearch.
+*/
+static OSErr CheckVol(ConstStr255Param pathname,
+ short vRefNum,
+ short *realVRefNum,
+ long *volID)
+{
+ HParamBlockRec pb;
+ OSErr error;
+
+ error = GetVolumeInfoNoName(pathname, vRefNum, &pb);
+ if ( error == noErr )
+ {
+ /* Return the real vRefNum */
+ *realVRefNum = pb.volumeParam.ioVRefNum;
+
+ /* Add together a bunch of things that aren't supposed to change on */
+ /* a mounted volume that's being searched and that should come up with */
+ /* a fairly unique number */
+ *volID = pb.volumeParam.ioVCrDate +
+ pb.volumeParam.ioVRefNum +
+ pb.volumeParam.ioVNmAlBlks +
+ pb.volumeParam.ioVAlBlkSiz +
+ pb.volumeParam.ioVFSID;
+ }
+ return ( error );
+}
+
+/*****************************************************************************/
+
+/*
+** CheckStack checks the size of the search stack (array) to see if there's
+** room to push another LevelRec. If not, CheckStack grows the stack by
+** another kAdditionalLevelRecs elements.
+*/
+static OSErr CheckStack(unsigned short stackDepth,
+ LevelRecHandle searchStack,
+ Size *searchStackSize)
+{
+ OSErr result;
+
+ if ( (*searchStackSize / sizeof(LevelRec)) == (stackDepth + 1) )
+ {
+ /* Time to grow stack */
+ SetHandleSize((Handle)searchStack, *searchStackSize + (kAdditionalLevelRecs * sizeof(LevelRec)));
+ result = MemError(); /* should be noErr */
+#if TARGET_CARBON
+ *searchStackSize = GetHandleSize((Handle)searchStack);
+#else
+ *searchStackSize = InlineGetHandleSize((Handle)searchStack);
+#endif
+ }
+ else
+ {
+ result = noErr;
+ }
+
+ return ( result );
+}
+
+/*****************************************************************************/
+
+/*
+** VerifyUserPB makes sure the parameter block passed to IndexedSearch has
+** valid parameters. By making this check once, we don't have to worry about
+** things like NULL pointers, strings being too long, etc.
+** VerifyUserPB also determines if the search includes files and/or
+** directories, and determines if a full or partial name search was requested.
+*/
+static OSErr VerifyUserPB(CSParamPtr userPB,
+ Boolean *includeFiles,
+ Boolean *includeDirs,
+ Boolean *includeNames)
+{
+ CInfoPBPtr searchInfo1;
+ CInfoPBPtr searchInfo2;
+
+ searchInfo1 = userPB->ioSearchInfo1;
+ searchInfo2 = userPB->ioSearchInfo2;
+
+ /* ioMatchPtr cannot be NULL */
+ if ( userPB->ioMatchPtr == NULL )
+ {
+ goto ParamErrExit;
+ }
+
+ /* ioSearchInfo1 cannot be NULL */
+ if ( searchInfo1 == NULL )
+ {
+ goto ParamErrExit;
+ }
+
+ /* If any bits except partialName, fullName, or negate are set, then */
+ /* ioSearchInfo2 cannot be NULL because information in ioSearchInfo2 is required */
+ if ( ((userPB->ioSearchBits & ~(fsSBPartialName | fsSBFullName | fsSBNegate)) != 0) &&
+ ( searchInfo2 == NULL ))
+ {
+ goto ParamErrExit;
+ }
+
+ *includeFiles = false;
+ *includeDirs = false;
+ *includeNames = false;
+
+ if ( (userPB->ioSearchBits & (fsSBPartialName | fsSBFullName)) != 0 )
+ {
+ /* If any kind of name matching is requested, then ioNamePtr in */
+ /* ioSearchInfo1 cannot be NULL or a zero-length string */
+ if ( (searchInfo1->hFileInfo.ioNamePtr == NULL) ||
+ (searchInfo1->hFileInfo.ioNamePtr[0] == 0) ||
+ (searchInfo1->hFileInfo.ioNamePtr[0] > (sizeof(Str63) - 1)) )
+ {
+ goto ParamErrExit;
+ }
+
+ *includeNames = true;
+ }
+
+ if ( (userPB->ioSearchBits & fsSBFlAttrib) != 0 )
+ {
+ /* The only attributes you can search on are the directory flag */
+ /* and the locked flag. */
+ if ( (searchInfo2->hFileInfo.ioFlAttrib & ~(ioDirMask | 0x01)) != 0 )
+ {
+ goto ParamErrExit;
+ }
+
+ /* interested in the directory bit? */
+ if ( (searchInfo2->hFileInfo.ioFlAttrib & ioDirMask) != 0 )
+ {
+ /* yes, so do they want just directories or just files? */
+ if ( (searchInfo1->hFileInfo.ioFlAttrib & ioDirMask) != 0 )
+ {
+ *includeDirs = true;
+ }
+ else
+ {
+ *includeFiles = true;
+ }
+ }
+ else
+ {
+ /* no interest in directory bit - get both files and directories */
+ *includeDirs = true;
+ *includeFiles = true;
+ }
+ }
+ else
+ {
+ /* no attribute checking - get both files and directories */
+ *includeDirs = true;
+ *includeFiles = true;
+ }
+
+ /* If directories are included in the search, */
+ /* then the locked attribute cannot be requested. */
+ if ( *includeDirs &&
+ ((userPB->ioSearchBits & fsSBFlAttrib) != 0) &&
+ ((searchInfo2->hFileInfo.ioFlAttrib & 0x01) != 0) )
+ {
+ goto ParamErrExit;
+ }
+
+ /* If files are included in the search, then there cannot be */
+ /* a search on the number of files. */
+ if ( *includeFiles &&
+ ((userPB->ioSearchBits & fsSBDrNmFls) != 0) )
+ {
+ goto ParamErrExit;
+ }
+
+ /* If directories are included in the search, then there cannot */
+ /* be a search on file lengths. */
+ if ( *includeDirs &&
+ ((userPB->ioSearchBits & (fsSBFlLgLen | fsSBFlPyLen | fsSBFlRLgLen | fsSBFlRPyLen)) != 0) )
+ {
+ goto ParamErrExit;
+ }
+
+ return ( noErr );
+
+ParamErrExit:
+ return ( paramErr );
+}
+
+/*****************************************************************************/
+
+/*
+** IsSubString checks to see if a string is a substring of another string.
+** Both input strings have already been converted to all uppercase using
+** UprString (the same non-international call the File Manager uses).
+*/
+static Boolean IsSubString(ConstStr255Param aStringPtr,
+ ConstStr255Param subStringPtr)
+{
+ short strLength; /* length of string */
+ short subStrLength; /* length of subString */
+ Boolean found; /* result of test */
+ short index; /* current index into string */
+
+ found = false;
+ strLength = aStringPtr[0];
+ subStrLength = subStringPtr[0];
+
+ if ( subStrLength <= strLength)
+ {
+ register short count; /* search counter */
+ register short strIndex; /* running index into string */
+ register short subStrIndex; /* running index into subString */
+
+ /* start looking at first character */
+ index = 1;
+
+ /* continue looking until remaining string is shorter than substring */
+ count = strLength - subStrLength + 1;
+
+ do
+ {
+ strIndex = index; /* start string index at index */
+ subStrIndex = 1; /* start subString index at 1 */
+
+ while ( !found && (aStringPtr[strIndex] == subStringPtr[subStrIndex]) )
+ {
+ if ( subStrIndex == subStrLength )
+ {
+ /* all characters in subString were found */
+ found = true;
+ }
+ else
+ {
+ /* check next character of substring against next character of string */
+ ++subStrIndex;
+ ++strIndex;
+ }
+ }
+
+ if ( !found )
+ {
+ /* start substring search again at next string character */
+ ++index;
+ --count;
+ }
+ } while ( count != 0 && (!found) );
+ }
+
+ return ( found );
+}
+
+/*****************************************************************************/
+
+/*
+** CompareMasked does a bitwise comparison with mask on 1 or more longs.
+** data1 and data2 are first exclusive-ORed together resulting with bits set
+** where they are different. That value is then ANDed with the mask resulting
+** with bits set if the test fails. true is returned if the tests pass.
+*/
+static Boolean CompareMasked(const long *data1,
+ const long *data2,
+ const long *mask,
+ short longsToCompare)
+{
+ Boolean result = true;
+
+ while ( (longsToCompare != 0) && (result == true) )
+ {
+ /* (*data1 ^ *data2) = bits that are different, so... */
+ /* ((*data1 ^ *data2) & *mask) = bits that are different that we're interested in */
+
+ if ( ((*data1 ^ *data2) & *mask) != 0 )
+ result = false;
+
+ ++data1;
+ ++data2;
+ ++mask;
+ --longsToCompare;
+ }
+
+ return ( result );
+}
+
+/*****************************************************************************/
+
+/*
+** Check for matches compares the search criteria in userPB to the file
+** system object in cPB. If there's a match, then the information in cPB is
+** is added to the match array and the actual match count is incremented.
+*/
+static void CheckForMatches(CInfoPBPtr cPB,
+ CSParamPtr userPB,
+ const Str63 matchName,
+ Boolean includeFiles,
+ Boolean includeDirs)
+{
+ long searchBits;
+ CInfoPBPtr searchInfo1;
+ CInfoPBPtr searchInfo2;
+ Str63 itemName; /* copy of object's name for partial name matching */
+ Boolean foundMatch;
+
+ foundMatch = false; /* default to no match */
+
+ searchBits = userPB->ioSearchBits;
+ searchInfo1 = userPB->ioSearchInfo1;
+ searchInfo2 = userPB->ioSearchInfo2;
+
+ /* Into the if statements that go on forever... */
+
+ if ( (cPB->hFileInfo.ioFlAttrib & ioDirMask) == 0 )
+ {
+ if (!includeFiles)
+ {
+ goto Failed;
+ }
+ }
+ else
+ {
+ if (!includeDirs)
+ {
+ goto Failed;
+ }
+ }
+
+ if ( (searchBits & fsSBPartialName) != 0 )
+ {
+ if ( (cPB->hFileInfo.ioNamePtr[0] > 0) &&
+ (cPB->hFileInfo.ioNamePtr[0] <= (sizeof(Str63) - 1)) )
+ {
+ /* Make uppercase copy of object name */
+ BlockMoveData(cPB->hFileInfo.ioNamePtr,
+ itemName,
+ cPB->hFileInfo.ioNamePtr[0] + 1);
+ /* Use the same non-international call the File Manager uses */
+ UpperString(itemName, true);
+ }
+ else
+ {
+ goto Failed;
+ }
+
+ {
+ if ( !IsSubString(itemName, matchName) )
+ {
+ goto Failed;
+ }
+ else if ( searchBits == fsSBPartialName )
+ {
+ /* optimize for name matching only since it is most common way to search */
+ goto Hit;
+ }
+ }
+ }
+
+ if ( (searchBits & fsSBFullName) != 0 )
+ {
+ /* Use the same non-international call the File Manager uses */
+ if ( !EqualString(cPB->hFileInfo.ioNamePtr, matchName, false, true) )
+ {
+ goto Failed;
+ }
+ else if ( searchBits == fsSBFullName )
+ {
+ /* optimize for name matching only since it is most common way to search */
+ goto Hit;
+ }
+ }
+
+ if ( (searchBits & fsSBFlParID) != 0 )
+ {
+ if ( ((unsigned long)(cPB->hFileInfo.ioFlParID) < (unsigned long)(searchInfo1->hFileInfo.ioFlParID)) ||
+ ((unsigned long)(cPB->hFileInfo.ioFlParID) > (unsigned long)(searchInfo2->hFileInfo.ioFlParID)) )
+ {
+ goto Failed;
+ }
+ }
+
+ if ( (searchBits & fsSBFlAttrib) != 0 )
+ {
+ if ( ((cPB->hFileInfo.ioFlAttrib ^ searchInfo1->hFileInfo.ioFlAttrib) &
+ searchInfo2->hFileInfo.ioFlAttrib) != 0 )
+ {
+ goto Failed;
+ }
+ }
+
+ if ( (searchBits & fsSBDrNmFls) != 0 )
+ {
+ if ( ((unsigned long)(cPB->dirInfo.ioDrNmFls) < (unsigned long)(searchInfo1->dirInfo.ioDrNmFls)) ||
+ ((unsigned long)(cPB->dirInfo.ioDrNmFls) > (unsigned long)(searchInfo2->dirInfo.ioDrNmFls)) )
+ {
+ goto Failed;
+ }
+ }
+
+ if ( (searchBits & fsSBFlFndrInfo) != 0 ) /* fsSBFlFndrInfo is same as fsSBDrUsrWds */
+ {
+ if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlFndrInfo),
+ (long *)&(searchInfo1->hFileInfo.ioFlFndrInfo),
+ (long *)&(searchInfo2->hFileInfo.ioFlFndrInfo),
+ sizeof(FInfo) / sizeof(long)) )
+ {
+ goto Failed;
+ }
+ }
+
+ if ( (searchBits & fsSBFlXFndrInfo) != 0 ) /* fsSBFlXFndrInfo is same as fsSBDrFndrInfo */
+ {
+ if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlXFndrInfo),
+ (long *)&(searchInfo1->hFileInfo.ioFlXFndrInfo),
+ (long *)&(searchInfo2->hFileInfo.ioFlXFndrInfo),
+ sizeof(FXInfo) / sizeof(long)) )
+ {
+ goto Failed;
+ }
+ }
+
+ if ( (searchBits & fsSBFlLgLen) != 0 )
+ {
+ if ( ((unsigned long)(cPB->hFileInfo.ioFlLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlLgLen)) ||
+ ((unsigned long)(cPB->hFileInfo.ioFlLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlLgLen)) )
+ {
+ goto Failed;
+ }
+ }
+
+ if ( (searchBits & fsSBFlPyLen) != 0 )
+ {
+ if ( ((unsigned long)(cPB->hFileInfo.ioFlPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlPyLen)) ||
+ ((unsigned long)(cPB->hFileInfo.ioFlPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlPyLen)) )
+ {
+ goto Failed;
+ }
+ }
+
+ if ( (searchBits & fsSBFlRLgLen) != 0 )
+ {
+ if ( ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRLgLen)) ||
+ ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRLgLen)) )
+ {
+ goto Failed;
+ }
+ }
+
+ if ( (searchBits & fsSBFlRPyLen) != 0 )
+ {
+ if ( ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRPyLen)) ||
+ ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRPyLen)) )
+ {
+ goto Failed;
+ }
+ }
+
+ if ( (searchBits & fsSBFlCrDat) != 0 ) /* fsSBFlCrDat is same as fsSBDrCrDat */
+ {
+ if ( ((unsigned long)(cPB->hFileInfo.ioFlCrDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlCrDat)) ||
+ ((unsigned long)(cPB->hFileInfo.ioFlCrDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlCrDat)) )
+ {
+ goto Failed;
+ }
+ }
+
+ if ( (searchBits & fsSBFlMdDat) != 0 ) /* fsSBFlMdDat is same as fsSBDrMdDat */
+ {
+ if ( ((unsigned long)(cPB->hFileInfo.ioFlMdDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlMdDat)) ||
+ ((unsigned long)(cPB->hFileInfo.ioFlMdDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlMdDat)) )
+ {
+ goto Failed;
+ }
+ }
+
+ if ( (searchBits & fsSBFlBkDat) != 0 ) /* fsSBFlBkDat is same as fsSBDrBkDat */
+ {
+ if ( ((unsigned long)(cPB->hFileInfo.ioFlBkDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlBkDat)) ||
+ ((unsigned long)(cPB->hFileInfo.ioFlBkDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlBkDat)) )
+ {
+ goto Failed;
+ }
+ }
+
+ /* Hey, we passed all of the tests! */
+
+Hit:
+ foundMatch = true;
+
+/* foundMatch is false if code jumps to Failed */
+Failed:
+ /* Do we reverse our findings? */
+ if ( (searchBits & fsSBNegate) != 0 )
+ {
+ foundMatch = !foundMatch; /* matches are not, not matches are */
+ }
+
+ if ( foundMatch )
+ {
+
+ /* Move the match into the match buffer */
+ userPB->ioMatchPtr[userPB->ioActMatchCount].vRefNum = cPB->hFileInfo.ioVRefNum;
+ userPB->ioMatchPtr[userPB->ioActMatchCount].parID = cPB->hFileInfo.ioFlParID;
+ if ( cPB->hFileInfo.ioNamePtr[0] > 63 )
+ {
+ cPB->hFileInfo.ioNamePtr[0] = 63;
+ }
+ BlockMoveData(cPB->hFileInfo.ioNamePtr,
+ userPB->ioMatchPtr[userPB->ioActMatchCount].name,
+ cPB->hFileInfo.ioNamePtr[0] + 1);
+
+ /* increment the actual count */
+ ++(userPB->ioActMatchCount);
+ }
+}
+
+/*****************************************************************************/
+
+/*
+** TimeOutTask is executed when the timer goes off. It simply sets the
+** stopSearch field to true. After each object is found and possibly added
+** to the matches buffer, stopSearch is checked to see if the search should
+** continue.
+*/
+
+#if __WANTPASCALELIMINATION
+#undef pascal
+#endif
+#if TARGET_CARBON
+static pascal void TimeOutTask(TMTaskPtr tmTaskPtr)
+{
+ ((ExtendedTMTaskPtr)tmTaskPtr)->stopSearch = true;
+}
+#else
+#if GENERATINGCFM
+
+static pascal void TimeOutTask(TMTaskPtr tmTaskPtr)
+{
+ ((ExtendedTMTaskPtr)tmTaskPtr)->stopSearch = true;
+}
+
+#else
+
+static pascal TMTaskPtr GetTMTaskPtr(void)
+ ONEWORDINLINE(0x2e89); /* MOVE.L A1,(SP) */
+
+static void TimeOutTask(void)
+{
+ ((ExtendedTMTaskPtr)GetTMTaskPtr())->stopSearch = true;
+}
+#endif
+#endif
+
+#if __WANTPASCALELIMINATION
+#define pascal
+#endif
+
+/*****************************************************************************/
+
+/*
+** GetDirModDate returns the modification date of a directory. If there is
+** an error getting the modification date, -1 is returned to indicate
+** something went wrong.
+*/
+static long GetDirModDate(short vRefNum,
+ long dirID)
+{
+ CInfoPBRec pb;
+ Str31 tempName;
+ long modDate;
+
+ /* Protection against File Sharing problem */
+ tempName[0] = 0;
+ pb.dirInfo.ioNamePtr = tempName;
+ pb.dirInfo.ioVRefNum = vRefNum;
+ pb.dirInfo.ioDrDirID = dirID;
+ pb.dirInfo.ioFDirIndex = -1; /* use ioDrDirID */
+
+ if ( PBGetCatInfoSync(&pb) == noErr )
+ {
+ modDate = pb.dirInfo.ioDrMdDat;
+ }
+ else
+ {
+ modDate = -1;
+ }
+
+ return ( modDate );
+}
+
+/*****************************************************************************/
+
+pascal OSErr IndexedSearch(CSParamPtr pb,
+ long dirID)
+{
+ static LevelRecHandle searchStack = NULL; /* static handle to LevelRec stack */
+ static Size searchStackSize = 0; /* size of static handle */
+ SearchPositionRecPtr catPosition;
+ long modDate;
+ short index;
+ ExtendedTMTask timerTask;
+ OSErr result;
+ short realVRefNum;
+ Str63 itemName;
+ CInfoPBRec cPB;
+ long tempLong;
+ Boolean includeFiles;
+ Boolean includeDirs;
+ Boolean includeNames;
+ Str63 upperName;
+
+ timerTask.stopSearch = false; /* don't stop yet! */
+
+ /* If request has a timeout, install a Time Manager task. */
+ if ( pb->ioSearchTime != 0 )
+ {
+ /* Start timer */
+ timerTask.theTask.tmAddr = NewTimerProc(TimeOutTask);
+ InsTime((QElemPtr)&(timerTask.theTask));
+ PrimeTime((QElemPtr)&(timerTask.theTask), pb->ioSearchTime);
+ }
+
+ /* Check the parameter block passed for things that we don't want to assume */
+ /* are OK later in the code. For example, make sure pointers to data structures */
+ /* and buffers are not NULL. And while we're in there, see if the request */
+ /* specified searching for files, directories, or both, and see if the search */
+ /* was by full or partial name. */
+ result = VerifyUserPB(pb, &includeFiles, &includeDirs, &includeNames);
+ if ( result == noErr )
+ {
+ pb->ioActMatchCount = 0; /* no matches yet */
+
+ if ( includeNames )
+ {
+ /* The search includes seach by full or partial name. */
+ /* Make an upper case copy of the match string to pass to */
+ /* CheckForMatches. */
+ BlockMoveData(pb->ioSearchInfo1->hFileInfo.ioNamePtr,
+ upperName,
+ pb->ioSearchInfo1->hFileInfo.ioNamePtr[0] + 1);
+ /* Use the same non-international call the File Manager uses */
+ UpperString(upperName, true);
+ }
+
+ /* Prevent casting to my type throughout code */
+ catPosition = (SearchPositionRecPtr)&pb->ioCatPosition;
+
+ /* Create searchStack first time called */
+ if ( searchStack == NULL )
+ {
+ searchStack = (LevelRecHandle)NewHandle(kAdditionalLevelRecs * sizeof(LevelRec));
+ }
+
+ /* Make sure searchStack really exists */
+ if ( searchStack != NULL )
+ {
+#if TARGET_CARBON
+ searchStackSize = GetHandleSize((Handle)searchStack);
+#else
+ searchStackSize = InlineGetHandleSize((Handle)searchStack);
+#endif
+
+ /* See if the search is a new search or a resumed search. */
+ if ( catPosition->initialize == 0 )
+ {
+ /* New search. */
+
+ /* Get the real vRefNum and fill in catPosition->initialize. */
+ result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &catPosition->initialize);
+ if ( result == noErr )
+ {
+ /* clear searchStack */
+ catPosition->stackDepth = 0;
+
+ /* use dirID parameter passed and... */
+ index = -1; /* start with the passed directory itself! */
+ }
+ }
+ else
+ {
+ /* We're resuming a search. */
+
+ /* Get the real vRefNum and make sure catPosition->initialize is valid. */
+ result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &tempLong);
+ if ( result == noErr )
+ {
+ /* Make sure the resumed search is to the same volume! */
+ if ( catPosition->initialize == tempLong )
+ {
+ /* For resume, catPosition->stackDepth > 0 */
+ if ( catPosition->stackDepth > 0 )
+ {
+ /* Position catPosition->stackDepth to access last saved level */
+ --(catPosition->stackDepth);
+
+ /* Get the dirID and index for the next item */
+ dirID = (*searchStack)[catPosition->stackDepth].dirID;
+ index = (*searchStack)[catPosition->stackDepth].index;
+
+ /* Check the dir's mod date against the saved mode date on our "stack" */
+ modDate = GetDirModDate(realVRefNum, dirID);
+ if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate )
+ {
+ result = catChangedErr;
+ }
+ }
+ else
+ {
+ /* Invalid catPosition record was passed */
+ result = paramErr;
+ }
+ }
+ else
+ {
+ /* The volume is not the same */
+ result = catChangedErr;
+ }
+ }
+ }
+
+ if ( result == noErr )
+ {
+ /* ioNamePtr and ioVRefNum only need to be set up once. */
+ cPB.hFileInfo.ioNamePtr = itemName;
+ cPB.hFileInfo.ioVRefNum = realVRefNum;
+
+ /*
+ ** Here's the loop that:
+ ** Finds the next item on the volume.
+ ** If noErr, calls the code to check for matches and add matches
+ ** to the match buffer.
+ ** Sets up dirID and index for to find the next item on the volume.
+ **
+ ** The looping ends when:
+ ** (a) an unexpected error is returned by PBGetCatInfo. All that
+ ** is expected is noErr and fnfErr (after the last item in a
+ ** directory is found).
+ ** (b) the caller specified a timeout and our Time Manager task
+ ** has fired.
+ ** (c) the number of matches requested by the caller has been found.
+ ** (d) the last item on the volume was found.
+ */
+ do
+ {
+ /* get the next item */
+ cPB.hFileInfo.ioFDirIndex = index;
+ cPB.hFileInfo.ioDirID = dirID;
+ result = PBGetCatInfoSync(&cPB);
+ if ( index != -1 )
+ {
+ if ( result == noErr )
+ {
+ /* We found something */
+
+ CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
+
+ ++index;
+ if ( (cPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
+ {
+ /* It's a directory */
+
+ result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
+ if ( result == noErr )
+ {
+ /* Save the current state on the searchStack */
+ /* when we come back, this is where we'll start */
+ (*searchStack)[catPosition->stackDepth].dirID = dirID;
+ (*searchStack)[catPosition->stackDepth].index = index;
+ (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
+
+ /* position catPosition->stackDepth for next saved level */
+ ++(catPosition->stackDepth);
+
+ /* The next item to get is the 1st item in the child directory */
+ dirID = cPB.dirInfo.ioDrDirID;
+ index = 1;
+ }
+ }
+ /* else do nothing for files */
+ }
+ else
+ {
+ /* End of directory found (or we had some error and that */
+ /* means we have to drop out of this directory). */
+ /* Restore last thing put on stack and */
+ /* see if we need to continue or quit. */
+ if ( catPosition->stackDepth > 0 )
+ {
+ /* position catPosition->stackDepth to access last saved level */
+ --(catPosition->stackDepth);
+
+ dirID = (*searchStack)[catPosition->stackDepth].dirID;
+ index = (*searchStack)[catPosition->stackDepth].index;
+
+ /* Check the dir's mod date against the saved mode date on our "stack" */
+ modDate = GetDirModDate(realVRefNum, dirID);
+ if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate )
+ {
+ result = catChangedErr;
+ }
+ else
+ {
+ /* Going back to ancestor directory. */
+ /* Clear error so we can continue. */
+ result = noErr;
+ }
+ }
+ else
+ {
+ /* We hit the bottom of the stack, so we'll let the */
+ /* the eofErr drop us out of the loop. */
+ result = eofErr;
+ }
+ }
+ }
+ else
+ {
+ /* Special case for index == -1; that means that we're starting */
+ /* a new search and so the first item to check is the directory */
+ /* passed to us. */
+ if ( result == noErr )
+ {
+ /* We found something */
+
+ CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
+
+ /* Now, set the index to 1 and then we're ready to look inside */
+ /* the passed directory. */
+ index = 1;
+ }
+ }
+ } while ( (!timerTask.stopSearch) && /* timer hasn't fired */
+ (result == noErr) && /* no unexpected errors */
+ (pb->ioReqMatchCount > pb->ioActMatchCount) ); /* we haven't found our limit */
+
+ /* Did we drop out of the loop because of timeout or */
+ /* ioReqMatchCount was found? */
+ if ( result == noErr )
+ {
+ result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
+ if ( result == noErr )
+ {
+ /* Either there was a timeout or ioReqMatchCount was reached. */
+ /* Save the dirID and index for the next time we're called. */
+
+ (*searchStack)[catPosition->stackDepth].dirID = dirID;
+ (*searchStack)[catPosition->stackDepth].index = index;
+ (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
+
+ /* position catPosition->stackDepth for next saved level */
+
+ ++(catPosition->stackDepth);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* searchStack Handle could not be allocated */
+ result = memFullErr;
+ }
+ }
+
+ if ( pb->ioSearchTime != 0 )
+ {
+ /* Stop Time Manager task here if it was installed */
+ RmvTime((QElemPtr)&(timerTask.theTask));
+ DisposeTimerUPP(timerTask.theTask.tmAddr);
+ }
+
+ return ( result );
+}
+
+/*****************************************************************************/
+
+pascal OSErr PBCatSearchSyncCompat(CSParamPtr paramBlock)
+{
+ static Boolean fullExtFSDispatchingtested = false;
+ static Boolean hasFullExtFSDispatching = false;
+ OSErr result;
+ Boolean supportsCatSearch;
+ long response;
+ GetVolParmsInfoBuffer volParmsInfo;
+ long infoSize;
+
+ result = noErr;
+
+ /* See if File Manager will pass CatSearch requests to external file systems */
+ /* we'll store the results in a static variable so we don't have to call Gestalt */
+ /* everytime we're called. */
+ if ( !fullExtFSDispatchingtested )
+ {
+ fullExtFSDispatchingtested = true;
+ if ( Gestalt(gestaltFSAttr, &response) == noErr )
+ {
+ hasFullExtFSDispatching = ((response & (1L << gestaltFullExtFSDispatching)) != 0);
+ }
+ }
+
+ /* CatSearch is a per volume attribute, so we have to check each time we're */
+ /* called to see if it is available on the volume specified. */
+ supportsCatSearch = false;
+ if ( hasFullExtFSDispatching )
+ {
+ infoSize = sizeof(GetVolParmsInfoBuffer);
+ result = HGetVolParms(paramBlock->ioNamePtr, paramBlock->ioVRefNum,
+ &volParmsInfo, &infoSize);
+ if ( result == noErr )
+ {
+ supportsCatSearch = hasCatSearch(volParmsInfo);
+ }
+ }
+
+ /* noErr or paramErr is OK here. */
+ /* paramErr just means that GetVolParms isn't supported by this volume */
+ if ( (result == noErr) || (result == paramErr) )
+ {
+ if ( supportsCatSearch )
+ {
+ /* Volume supports CatSearch so use it. */
+ /* CatSearch is faster than an indexed search. */
+ result = PBCatSearchSync(paramBlock);
+ }
+ else
+ {
+ /* Volume doesn't support CatSearch so */
+ /* search using IndexedSearch from root directory. */
+ result = IndexedSearch(paramBlock, fsRtDirID);
+ }
+ }
+
+ return ( result );
+}
+
+/*****************************************************************************/
+
+pascal OSErr NameFileSearch(ConstStr255Param volName,
+ short vRefNum,
+ ConstStr255Param fileName,
+ FSSpecPtr matches,
+ long reqMatchCount,
+ long *actMatchCount,
+ Boolean newSearch,
+ Boolean partial)
+{
+ CInfoPBRec searchInfo1, searchInfo2;
+ HParamBlockRec pb;
+ OSErr error;
+ static CatPositionRec catPosition;
+ static short lastVRefNum = 0;
+
+ /* get the real volume reference number */
+ error = DetermineVRefNum(volName, vRefNum, &vRefNum);
+ if ( error != noErr )
+ return ( error );
+
+ pb.csParam.ioNamePtr = NULL;
+ pb.csParam.ioVRefNum = vRefNum;
+ pb.csParam.ioMatchPtr = matches;
+ pb.csParam.ioReqMatchCount = reqMatchCount;
+ if ( partial ) /* tell CatSearch what we're looking for: */
+ {
+ pb.csParam.ioSearchBits = fsSBPartialName + fsSBFlAttrib; /* partial name file matches or */
+ }
+ else
+ {
+ pb.csParam.ioSearchBits = fsSBFullName + fsSBFlAttrib; /* full name file matches */
+ }
+ pb.csParam.ioSearchInfo1 = &searchInfo1;
+ pb.csParam.ioSearchInfo2 = &searchInfo2;
+ pb.csParam.ioSearchTime = 0;
+ if ( (newSearch) || /* If caller specified new search */
+ (lastVRefNum != vRefNum) ) /* or if last search was to another volume, */
+ {
+ catPosition.initialize = 0; /* then search from beginning of catalog */
+ }
+ pb.csParam.ioCatPosition = catPosition;
+ pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize);
+
+ /* search for fileName */
+ searchInfo1.hFileInfo.ioNamePtr = (StringPtr)fileName;
+ searchInfo2.hFileInfo.ioNamePtr = NULL;
+
+ /* only match files (not directories) */
+ searchInfo1.hFileInfo.ioFlAttrib = 0x00;
+ searchInfo2.hFileInfo.ioFlAttrib = ioDirMask;
+
+ error = PBCatSearchSyncCompat((CSParamPtr)&pb);
+
+ if ( (error == noErr) || /* If no errors or the end of catalog was */
+ (error == eofErr) ) /* found, then the call was successful so */
+ {
+ *actMatchCount = pb.csParam.ioActMatchCount; /* return the match count */
+ }
+ else
+ {
+ *actMatchCount = 0; /* else no matches found */
+ }
+
+ if ( (error == noErr) || /* If no errors */
+ (error == catChangedErr) ) /* or there was a change in the catalog */
+ {
+ catPosition = pb.csParam.ioCatPosition;
+ lastVRefNum = vRefNum;
+ /* we can probably start the next search where we stopped this time */
+ }
+ else
+ {
+ catPosition.initialize = 0;
+ /* start the next search from beginning of catalog */
+ }
+
+ if ( pb.csParam.ioOptBuffer != NULL )
+ {
+ DisposePtr(pb.csParam.ioOptBuffer);
+ }
+
+ return ( error );
+}
+
+/*****************************************************************************/
+
+pascal OSErr CreatorTypeFileSearch(ConstStr255Param volName,
+ short vRefNum,
+ OSType creator,
+ OSType fileType,
+ FSSpecPtr matches,
+ long reqMatchCount,
+ long *actMatchCount,
+ Boolean newSearch)
+{
+ CInfoPBRec searchInfo1, searchInfo2;
+ HParamBlockRec pb;
+ OSErr error;
+ static CatPositionRec catPosition;
+ static short lastVRefNum = 0;
+
+ /* get the real volume reference number */
+ error = DetermineVRefNum(volName, vRefNum, &vRefNum);
+ if ( error != noErr )
+ return ( error );
+
+ pb.csParam.ioNamePtr = NULL;
+ pb.csParam.ioVRefNum = vRefNum;
+ pb.csParam.ioMatchPtr = matches;
+ pb.csParam.ioReqMatchCount = reqMatchCount;
+ pb.csParam.ioSearchBits = fsSBFlAttrib + fsSBFlFndrInfo; /* Looking for finder info file matches */
+ pb.csParam.ioSearchInfo1 = &searchInfo1;
+ pb.csParam.ioSearchInfo2 = &searchInfo2;
+ pb.csParam.ioSearchTime = 0;
+ if ( (newSearch) || /* If caller specified new search */
+ (lastVRefNum != vRefNum) ) /* or if last search was to another volume, */
+ {
+ catPosition.initialize = 0; /* then search from beginning of catalog */
+ }
+ pb.csParam.ioCatPosition = catPosition;
+ pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize);
+
+ /* no fileName */
+ searchInfo1.hFileInfo.ioNamePtr = NULL;
+ searchInfo2.hFileInfo.ioNamePtr = NULL;
+
+ /* only match files (not directories) */
+ searchInfo1.hFileInfo.ioFlAttrib = 0x00;
+ searchInfo2.hFileInfo.ioFlAttrib = ioDirMask;
+
+ /* search for creator; if creator = 0x00000000, ignore creator */
+ searchInfo1.hFileInfo.ioFlFndrInfo.fdCreator = creator;
+ if ( creator == (OSType)0x00000000 )
+ {
+ searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (OSType)0x00000000;
+ }
+ else
+ {
+ searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (OSType)0xffffffff;
+ }
+
+ /* search for fileType; if fileType = 0x00000000, ignore fileType */
+ searchInfo1.hFileInfo.ioFlFndrInfo.fdType = fileType;
+ if ( fileType == (OSType)0x00000000 )
+ {
+ searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (OSType)0x00000000;
+ }
+ else
+ {
+ searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (OSType)0xffffffff;
+ }
+
+ /* zero all other FInfo fields */
+ searchInfo1.hFileInfo.ioFlFndrInfo.fdFlags = 0;
+ searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
+ searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
+ searchInfo1.hFileInfo.ioFlFndrInfo.fdFldr = 0;
+
+ searchInfo2.hFileInfo.ioFlFndrInfo.fdFlags = 0;
+ searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
+ searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
+ searchInfo2.hFileInfo.ioFlFndrInfo.fdFldr = 0;
+
+ error = PBCatSearchSyncCompat((CSParamPtr)&pb);
+
+ if ( (error == noErr) || /* If no errors or the end of catalog was */
+ (error == eofErr) ) /* found, then the call was successful so */
+ {
+ *actMatchCount = pb.csParam.ioActMatchCount; /* return the match count */
+ }
+ else
+ {
+ *actMatchCount = 0; /* else no matches found */
+ }
+
+ if ( (error == noErr) || /* If no errors */
+ (error == catChangedErr) ) /* or there was a change in the catalog */
+ {
+ catPosition = pb.csParam.ioCatPosition;
+ lastVRefNum = vRefNum;
+ /* we can probably start the next search where we stopped this time */
+ }
+ else
+ {
+ catPosition.initialize = 0;
+ /* start the next search from beginning of catalog */
+ }
+
+ if ( pb.csParam.ioOptBuffer != NULL )
+ {
+ DisposePtr(pb.csParam.ioOptBuffer);
+ }
+
+ return ( error );
+}
+
+/*****************************************************************************/
--- /dev/null
+/*
+** Apple Macintosh Developer Technical Support
+**
+** IndexedSearch and the PBCatSearch compatibility function.
+**
+** by Jim Luther, Apple Developer Technical Support Emeritus
+**
+** File: Search.h
+**
+** Copyright © 1992-1998 Apple Computer, Inc.
+** All rights reserved.
+**
+** You may incorporate this sample code into your applications without
+** restriction, though the sample code has been provided "AS IS" and the
+** responsibility for its operation is 100% yours. However, what you are
+** not permitted to do is to redistribute the source as "DSC Sample Code"
+** after having made changes. If you're going to re-distribute the source,
+** we require that you make it clear in the source that the code was
+** descended from Apple Sample Code, but that you've made changes.
+*/
+
+#ifndef __SEARCH__
+#define __SEARCH__
+
+#include <Types.h>
+#include <Files.h>
+
+#include "optim.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************************/
+
+pascal OSErr IndexedSearch(CSParamPtr pb,
+ long dirID);
+/* ¦ Search in and below a directory.
+ The IndexedSearch function performs an indexed search in and below the
+ specified directory using the same parameters (in pb) as is passed to
+ PBCatSearch. See Inside Macintosh: Files for a description of the
+ parameter block.
+
+ pb input: A CSParamPtr record specifying the volume to search
+ and the search criteria.
+ output: Fields in the parameter block are returned indicating
+ the number of matches found, the matches, and if the
+ search ended with noErr, the CatPosition record that
+ lets you resume a search where the last search left
+ off.
+ dirID input: The directory to search. If fsRtDirID is passed,
+ the entire volume is searched.
+
+ Note: If you use a high-level debugger and use ioSearchTime to limit
+ the length of time to run the search, you'll want to step over
+ calls to IndexedSearch because it installs a Time Manager task.
+ Most high-level debuggers don't deal gracefully with interrupt
+ driven code.
+
+ Result Codes
+ noErr 0 No error
+ nsvErr -35 Volume not found
+ ioErr -36 I/O error
+ eofErr -39 End of catalog found (this is normal!)
+ paramErr -50 Parameter block has invalid parameters
+ (see source for VerifyUserPB) or
+ invalid catPosition record was passed
+ extFSErr -58 External file system error - no file
+ system claimed this call.
+ memFullErr -108 Memory could not be allocated in heap
+ catChangedErr -1304 Catalog has changed and catalog position
+ record may be invalid
+
+ __________
+
+ See also: PBCatSearch, PBCatSearchSyncCompat
+*/
+
+/*****************************************************************************/
+
+pascal OSErr PBCatSearchSyncCompat(CSParamPtr paramBlock);
+/* ¦ Search a volume using PBCatSearch or IndexedSearch.
+ The PBCatSearchSyncCompat function uses PBCatSearch (if available) or
+ IndexedSearch (if PBCatSearch is not available) to search a volume
+ using a set of search criteria that you specify. It builds a list of all
+ files or directories that meet your specifications.
+
+ pb input: A CSParamPtr record specifying the volume to search
+ and the search criteria.
+ output: Fields in the parameter block are returned indicating
+ the number of matches found, the matches, and if the
+ search ended with noErr, the CatPosition record that
+ lets you resume a search where the last search left
+ off.
+
+ Note: If you use a high-level debugger and use ioSearchTime to limit
+ the length of time to run the search, you'll want to step over
+ calls to PBCatSearchSyncCompat because it calls IndexedSearch
+ which installs a Time Manager task. Most high-level debuggers
+ don't deal gracefully with interrupt driven code.
+
+ Result Codes
+ noErr 0 No error
+ nsvErr -35 Volume not found
+ ioErr -36 I/O error
+ eofErr -39 End of catalog found (this is normal!)
+ paramErr -50 Parameter block has invalid parameters
+ (see source for VerifyUserPB) or
+ invalid catPosition record was passed
+ extFSErr -58 External file system error - no file
+ system claimed this call.
+ memFullErr -108 Memory could not be allocated in heap
+ catChangedErr -1304 Catalog has changed and catalog position
+ record may be invalid
+ afpCatalogChanged -5037 Catalog has changed and search cannot
+ be resumed
+
+ __________
+
+ See also: PBCatSearch, IndexedSearch
+*/
+
+/*****************************************************************************/
+
+pascal OSErr NameFileSearch(ConstStr255Param volName,
+ short vRefNum,
+ ConstStr255Param fileName,
+ FSSpecPtr matches,
+ long reqMatchCount,
+ long *actMatchCount,
+ Boolean newSearch,
+ Boolean partial);
+/* ¦ Search for files by file name with PBCatSearch.
+ The NameFileSearch function searches for files with a specific file
+ name on a volume that supports PBCatSearch.
+ Note: A result of catChangedErr means the catalog has changed between
+ searches, but the search can be continued with the possiblity that you
+ may miss some matches or get duplicate matches. For all other results
+ (except for noErr), the search cannot be continued.
+
+ volName input: A pointer to the name of a mounted volume
+ or nil.
+ vRefNum input: Volume specification.
+ fileName input: The name of the file to search for.
+ matches input: Pointer to array of FSSpec where the match list is
+ returned.
+ reqMatchCount input: Maximum number of matches to return (the number of
+ elements in the matches array).
+ actMatchCount output: The number of matches actually returned.
+ newSearch input: If true, start a new search. If false and if
+ vRefNum is the same as the last call to
+ NameFileSearch, then start searching at the
+ position where the last search left off.
+ partial input: If the partial parameter is false, then only files
+ that exactly match fileName will be found. If the
+ partial parameter is true, then all file names that
+ contain fileName will be found.
+
+ Result Codes
+ noErr 0 No error
+ nsvErr -35 Volume not found
+ ioErr -36 I/O error
+ eofErr -39 End of catalog found (this is normal!)
+ paramErr -50 Parameter block has invalid parameters
+ (see source for VerifyUserPB) or
+ invalid catPosition record was passed
+ extFSErr -58 External file system error - no file
+ system claimed this call.
+ memFullErr -108 Memory could not be allocated in heap
+ catChangedErr -1304 Catalog has changed and catalog position
+ record may be invalid
+ afpCatalogChanged -5037 Catalog has changed and search cannot
+ be resumed
+
+ __________
+
+ Also see: CreatorTypeFileSearch
+*/
+
+/*****************************************************************************/
+
+pascal OSErr CreatorTypeFileSearch(ConstStr255Param volName,
+ short vRefNum,
+ OSType creator,
+ OSType fileType,
+ FSSpecPtr matches,
+ long reqMatchCount,
+ long *actMatchCount,
+ Boolean newSearch);
+/* ¦ Search for files by creator/fileType with PBCatSearch.
+ The CreatorTypeFileSearch function searches for files with a specific
+ creator or fileType on a volume that supports PBCatSearch.
+ Note: A result of catChangedErr means the catalog has changed between
+ searches, but the search can be continued with the possiblity that you
+ may miss some matches or get duplicate matches. For all other results
+ (except for noErr), the search cannot be continued.
+
+ volName input: A pointer to the name of a mounted volume
+ or nil.
+ vRefNum input: Volume specification.
+ creator input: The creator type of the file to search for.
+ To ignore the creator type, pass 0x00000000 in
+ this field.
+ fileType input: The file type of the file to search for.
+ To ignore the file type, pass 0x00000000 in
+ this field.
+ matches input: Pointer to array of FSSpec where the match list is
+ returned.
+ reqMatchCount input: Maximum number of matches to return (the number of
+ elements in the matches array).
+ actMatchCount output: The number of matches actually returned.
+ newSearch input: If true, start a new search. If false and if
+ vRefNum is the same as the last call to
+ CreatorTypeFileSearch, then start searching at the
+ position where the last search left off.
+
+ Result Codes
+ noErr 0 No error
+ nsvErr -35 Volume not found
+ ioErr -36 I/O error
+ eofErr -39 End of catalog found (this is normal!)
+ paramErr -50 Parameter block has invalid parameters
+ (see source for VerifyUserPB) or
+ invalid catPosition record was passed
+ extFSErr -58 External file system error - no file
+ system claimed this call.
+ memFullErr -108 Memory could not be allocated in heap
+ catChangedErr -1304 Catalog has changed and catalog position
+ record may be invalid
+ afpCatalogChanged -5037 Catalog has changed and search cannot
+ be resumed
+
+ __________
+
+ Also see: NameFileSearch
+*/
+
+/*****************************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#include "optimend.h"
+
+#endif /* __SEARCH__ */