]> git.saurik.com Git - wxWidgets.git/commitdiff
wxMac merge
authorStefan Csomor <csomor@advancedconcepts.ch>
Thu, 10 Aug 2000 07:21:41 +0000 (07:21 +0000)
committerStefan Csomor <csomor@advancedconcepts.ch>
Thu, 10 Aug 2000 07:21:41 +0000 (07:21 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@8001 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

src/mac/morefile/mfsearch.cpp [new file with mode: 0644]
src/mac/morefile/mfsearch.h [new file with mode: 0644]

diff --git a/src/mac/morefile/mfsearch.cpp b/src/mac/morefile/mfsearch.cpp
new file mode 100644 (file)
index 0000000..8bdba27
--- /dev/null
@@ -0,0 +1,1309 @@
+/*
+**     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 );
+}
+
+/*****************************************************************************/
diff --git a/src/mac/morefile/mfsearch.h b/src/mac/morefile/mfsearch.h
new file mode 100644 (file)
index 0000000..6bd1fdf
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+**     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__ */