--- /dev/null
+/*
+ File: IterateDirectory.c
+
+ Contains: File Manager directory iterator routines.
+
+ Version: MoreFiles
+
+ Copyright: © 1995-2001 by 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. 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.
+
+ File Ownership:
+
+ DRI: Apple Macintosh Developer Technical Support
+
+ Other Contact: Apple Macintosh Developer Technical Support
+ <http://developer.apple.com/bugreporter/>
+
+ Technology: DTS Sample Code
+
+ Writers:
+
+ (JL) Jim Luther
+
+ Change History (most recent first):
+
+ <2> 2/7/01 JL Added standard header. Updated names of includes.
+ <1> 12/06/99 JL MoreFiles 1.5.
+*/
+
+#include <MacTypes.h>
+#include <MacErrors.h>
+#include <Files.h>
+
+#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_STRUCT_ALIGN
+#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_STRUCT_ALIGN
+#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 & kioFlAttribDirMask) != 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) );
+}
+
+/*****************************************************************************/