Logo Search packages:      
Sourcecode: xaos version File versions  Download package

mfsearch.cpp

/*
**    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 */
#if defined(UNIVERSAL_INTERFACES_VERSION) && (UNIVERSAL_INTERFACES_VERSION >= 0x0340)
            timerTask.theTask.tmAddr = NewTimerUPP(TimeOutTask);
#else
            timerTask.theTask.tmAddr = NewTimerProc(TimeOutTask);
#endif
            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 );
}

/*****************************************************************************/

Generated by  Doxygen 1.6.0   Back to index