/* ** IterateDirectory: File Manager directory iterator routines. ** ** by Jim Luther ** ** File: IterateDirectory.c ** ** Copyright © 1995-1998 Jim Luther and 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. ** ** IterateDirectory is designed to drop into the MoreFiles sample code ** library I wrote while in Apple Developer Technical Support */ #include #include #include #define __COMPILINGMOREFILES #include "MoreFilesExtras.h" #include "IterateDirectory.h" /* ** Type definitions */ /* The IterateGlobals structure is used to minimize the amount of ** stack space used when recursively calling IterateDirectoryLevel ** and to hold global information that might be needed at any time. */ #if PRAGMA_ALIGN_SUPPORTED #pragma options align=mac68k #endif struct IterateGlobals { IterateFilterProcPtr iterateFilter; /* pointer to IterateFilterProc */ CInfoPBRec cPB; /* the parameter block used for PBGetCatInfo calls */ Str63 itemName; /* the name of the current item */ OSErr result; /* temporary holder of results - saves 2 bytes of stack each level */ Boolean quitFlag; /* set to true if filter wants to kill interation */ unsigned short maxLevels; /* Maximum levels to iterate through */ unsigned short currentLevel; /* The current level IterateLevel is on */ void *yourDataPtr; /* A pointer to caller data the filter may need to access */ }; #if PRAGMA_ALIGN_SUPPORTED #pragma options align=reset #endif typedef struct IterateGlobals IterateGlobals; typedef IterateGlobals *IterateGlobalsPtr; /*****************************************************************************/ /* Static Prototype */ static void IterateDirectoryLevel(long dirID, IterateGlobals *theGlobals); /*****************************************************************************/ /* ** Functions */ static void IterateDirectoryLevel(long dirID, IterateGlobals *theGlobals) { if ( (theGlobals->maxLevels == 0) || /* if maxLevels is zero, we aren't checking levels */ (theGlobals->currentLevel < theGlobals->maxLevels) ) /* if currentLevel < maxLevels, look at this level */ { short index = 1; ++theGlobals->currentLevel; /* go to next level */ do { /* Isn't C great... What I'd give for a "WITH theGlobals DO" about now... */ /* Get next source item at the current directory level */ theGlobals->cPB.dirInfo.ioFDirIndex = index; theGlobals->cPB.dirInfo.ioDrDirID = dirID; theGlobals->result = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->cPB); if ( theGlobals->result == noErr ) { /* Call the IterateFilterProc */ CallIterateFilterProc(theGlobals->iterateFilter, &theGlobals->cPB, &theGlobals->quitFlag, theGlobals->yourDataPtr); /* Is it a directory? */ if ( (theGlobals->cPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 ) { /* We have a directory */ if ( !theGlobals->quitFlag ) { /* Dive again if the IterateFilterProc didn't say "quit" */ IterateDirectoryLevel(theGlobals->cPB.dirInfo.ioDrDirID, theGlobals); } } } ++index; /* prepare to get next item */ } while ( (theGlobals->result == noErr) && (!theGlobals->quitFlag) ); /* time to fall back a level? */ if ( (theGlobals->result == fnfErr) || /* fnfErr is OK - it only means we hit the end of this level */ (theGlobals->result == afpAccessDenied) ) /* afpAccessDenied is OK, too - it only means we cannot see inside a directory */ { theGlobals->result = noErr; } --theGlobals->currentLevel; /* return to previous level as we leave */ } } /*****************************************************************************/ pascal OSErr IterateDirectory(short vRefNum, long dirID, ConstStr255Param name, unsigned short maxLevels, IterateFilterProcPtr iterateFilter, void *yourDataPtr) { IterateGlobals theGlobals; OSErr result; long theDirID; short theVRefNum; Boolean isDirectory; /* Make sure there is a IterateFilter */ if ( iterateFilter != NULL ) { /* Get the real directory ID and make sure it is a directory */ result = GetDirectoryID(vRefNum, dirID, name, &theDirID, &isDirectory); if ( result == noErr ) { if ( isDirectory == true ) { /* Get the real vRefNum */ result = DetermineVRefNum(name, vRefNum, &theVRefNum); if ( result == noErr ) { /* Set up the globals we need to access from the recursive routine. */ theGlobals.iterateFilter = iterateFilter; theGlobals.cPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName; theGlobals.cPB.hFileInfo.ioVRefNum = theVRefNum; theGlobals.itemName[0] = 0; theGlobals.result = noErr; theGlobals.quitFlag = false; theGlobals.maxLevels = maxLevels; theGlobals.currentLevel = 0; /* start at level 0 */ theGlobals.yourDataPtr = yourDataPtr; /* Here we go into recursion land... */ IterateDirectoryLevel(theDirID, &theGlobals); result = theGlobals.result; /* set the result */ } } else { result = dirNFErr; /* a file was passed instead of a directory */ } } } else { result = paramErr; /* iterateFilter was NULL */ } return ( result ); } /*****************************************************************************/ pascal OSErr FSpIterateDirectory(const FSSpec *spec, unsigned short maxLevels, IterateFilterProcPtr iterateFilter, void *yourDataPtr) { return ( IterateDirectory(spec->vRefNum, spec->parID, spec->name, maxLevels, iterateFilter, yourDataPtr) ); } /*****************************************************************************/