Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Search.c
/* |
File: Search.c |
Description:IndexedSearch and the PBCatSearch compatibility function. |
Author: JL |
Copyright: Copyright: © 1992-1999 by Apple Computer, Inc. |
all rights reserved. |
Disclaimer: 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. |
Change History (most recent first): |
6/25/99 Updated for Metrowerks Codewarror Pro 2.1(KG) |
*/ |
#include <Types.h> |
#include <Gestalt.h> |
#include <Timer.h> |
#include <Errors.h> |
#include <Memory.h> |
#include <Files.h> |
#include <TextUtils.h> |
#define __COMPILINGMOREFILES |
#include "MoreFiles.h" |
#include "MoreFilesExtras.h" |
#include "Search.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_ALIGN_SUPPORTED |
#pragma options align=mac68k |
#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_ALIGN_SUPPORTED |
#pragma options align=reset |
#endif |
typedef struct SearchPositionRec SearchPositionRec; |
typedef SearchPositionRec *SearchPositionRecPtr; |
/* |
** ExtendedTMTask is a TMTask record extended to hold the timer flag. |
*/ |
#if PRAGMA_ALIGN_SUPPORTED |
#pragma options align=mac68k |
#endif |
struct ExtendedTMTask |
{ |
TMTask theTask; |
Boolean stopSearch; /* the Time Mgr task will set stopSearch to */ |
/* true when the timer expires */ |
}; |
#if PRAGMA_ALIGN_SUPPORTED |
#pragma options align=reset |
#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 GENERATINGCFM |
static pascal void TimeOutTask(TMTaskPtr tmTaskPtr); |
#else |
static pascal TMTaskPtr GetTMTaskPtr(void); |
static void TimeOutTask(void); |
#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 */ |
*searchStackSize = InlineGetHandleSize((Handle)searchStack); |
} |
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 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 |
#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 ) |
{ |
searchStackSize = InlineGetHandleSize((Handle)searchStack); |
/* 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)); |
DisposeRoutineDescriptor(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 ); |
} |
/*****************************************************************************/ |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-03-13