2 ** IterateDirectory: File Manager directory iterator routines.
6 ** File: IterateDirectory.c
8 ** Copyright © 1995-1998 Jim Luther and Apple Computer, Inc.
9 ** All rights reserved.
11 ** You may incorporate this sample code into your applications without
12 ** restriction, though the sample code has been provided "AS IS" and the
13 ** responsibility for its operation is 100% yours.
15 ** IterateDirectory is designed to drop into the MoreFiles sample code
16 ** library I wrote while in Apple Developer Technical Support
23 #define __COMPILINGMOREFILES
32 /* The IterateGlobals structure is used to minimize the amount of
33 ** stack space used when recursively calling IterateDirectoryLevel
34 ** and to hold global information that might be needed at any time.
36 #if PRAGMA_ALIGN_SUPPORTED
37 #pragma options align=mac68k
41 IterateFilterProcPtr iterateFilter
; /* pointer to IterateFilterProc */
42 CInfoPBRec cPB
; /* the parameter block used for PBGetCatInfo calls */
43 Str63 itemName
; /* the name of the current item */
44 OSErr result
; /* temporary holder of results - saves 2 bytes of stack each level */
45 Boolean quitFlag
; /* set to true if filter wants to kill interation */
46 unsigned short maxLevels
; /* Maximum levels to iterate through */
47 unsigned short currentLevel
; /* The current level IterateLevel is on */
48 void *yourDataPtr
; /* A pointer to caller data the filter may need to access */
50 #if PRAGMA_ALIGN_SUPPORTED
51 #pragma options align=reset
54 typedef struct IterateGlobals IterateGlobals
;
55 typedef IterateGlobals
*IterateGlobalsPtr
;
57 /*****************************************************************************/
59 /* Static Prototype */
61 static void IterateDirectoryLevel(long dirID
,
62 IterateGlobals
*theGlobals
);
64 /*****************************************************************************/
70 static void IterateDirectoryLevel(long dirID
,
71 IterateGlobals
*theGlobals
)
73 if ( (theGlobals
->maxLevels
== 0) || /* if maxLevels is zero, we aren't checking levels */
74 (theGlobals
->currentLevel
< theGlobals
->maxLevels
) ) /* if currentLevel < maxLevels, look at this level */
78 ++theGlobals
->currentLevel
; /* go to next level */
81 { /* Isn't C great... What I'd give for a "WITH theGlobals DO" about now... */
83 /* Get next source item at the current directory level */
85 theGlobals
->cPB
.dirInfo
.ioFDirIndex
= index
;
86 theGlobals
->cPB
.dirInfo
.ioDrDirID
= dirID
;
87 theGlobals
->result
= PBGetCatInfoSync((CInfoPBPtr
)&theGlobals
->cPB
);
89 if ( theGlobals
->result
== noErr
)
91 /* Call the IterateFilterProc */
92 CallIterateFilterProc(theGlobals
->iterateFilter
, &theGlobals
->cPB
, &theGlobals
->quitFlag
, theGlobals
->yourDataPtr
);
94 /* Is it a directory? */
95 if ( (theGlobals
->cPB
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0 )
97 /* We have a directory */
98 if ( !theGlobals
->quitFlag
)
100 /* Dive again if the IterateFilterProc didn't say "quit" */
101 IterateDirectoryLevel(theGlobals
->cPB
.dirInfo
.ioDrDirID
, theGlobals
);
106 ++index
; /* prepare to get next item */
107 } while ( (theGlobals
->result
== noErr
) && (!theGlobals
->quitFlag
) ); /* time to fall back a level? */
109 if ( (theGlobals
->result
== fnfErr
) || /* fnfErr is OK - it only means we hit the end of this level */
110 (theGlobals
->result
== afpAccessDenied
) ) /* afpAccessDenied is OK, too - it only means we cannot see inside a directory */
112 theGlobals
->result
= noErr
;
115 --theGlobals
->currentLevel
; /* return to previous level as we leave */
119 /*****************************************************************************/
121 pascal OSErr
IterateDirectory(short vRefNum
,
123 ConstStr255Param name
,
124 unsigned short maxLevels
,
125 IterateFilterProcPtr iterateFilter
,
128 IterateGlobals theGlobals
;
134 /* Make sure there is a IterateFilter */
135 if ( iterateFilter
!= NULL
)
137 /* Get the real directory ID and make sure it is a directory */
138 result
= GetDirectoryID(vRefNum
, dirID
, name
, &theDirID
, &isDirectory
);
139 if ( result
== noErr
)
141 if ( isDirectory
== true )
143 /* Get the real vRefNum */
144 result
= DetermineVRefNum(name
, vRefNum
, &theVRefNum
);
145 if ( result
== noErr
)
147 /* Set up the globals we need to access from the recursive routine. */
148 theGlobals
.iterateFilter
= iterateFilter
;
149 theGlobals
.cPB
.hFileInfo
.ioNamePtr
= (StringPtr
)&theGlobals
.itemName
;
150 theGlobals
.cPB
.hFileInfo
.ioVRefNum
= theVRefNum
;
151 theGlobals
.itemName
[0] = 0;
152 theGlobals
.result
= noErr
;
153 theGlobals
.quitFlag
= false;
154 theGlobals
.maxLevels
= maxLevels
;
155 theGlobals
.currentLevel
= 0; /* start at level 0 */
156 theGlobals
.yourDataPtr
= yourDataPtr
;
158 /* Here we go into recursion land... */
159 IterateDirectoryLevel(theDirID
, &theGlobals
);
161 result
= theGlobals
.result
; /* set the result */
166 result
= dirNFErr
; /* a file was passed instead of a directory */
172 result
= paramErr
; /* iterateFilter was NULL */
178 /*****************************************************************************/
180 pascal OSErr
FSpIterateDirectory(const FSSpec
*spec
,
181 unsigned short maxLevels
,
182 IterateFilterProcPtr iterateFilter
,
185 return ( IterateDirectory(spec
->vRefNum
, spec
->parID
, spec
->name
,
186 maxLevels
, iterateFilter
, yourDataPtr
) );
189 /*****************************************************************************/