home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #19 / NN_1992_19.iso / spool / comp / sys / mac / programm / 14906 < prev    next >
Encoding:
Text File  |  1992-09-02  |  17.9 KB  |  636 lines

  1. Path: sparky!uunet!gatech!darwin.sura.net!wupost!micro-heart-of-gold.mit.edu!bu.edu!Shiva.COM!world!aep
  2. From: aep@world.std.com (Andrew E Page)
  3. Newsgroups: comp.sys.mac.programmer
  4. Subject: Re: Directory Scan Revisited dirscan.c (C Code)
  5. Message-ID: <Btyt08.JLL@world.std.com>
  6. Date: 2 Sep 92 19:04:54 GMT
  7. References: <BtwK89.oI@world.std.com>
  8. Organization: The World Public Access UNIX, Brookline, MA
  9. Lines: 625
  10.  
  11. Here is the directory scanning code.  Written under MPW 3.2.
  12.  
  13. --------CUT HERE------------------------------------------------CUT HERE------
  14.  
  15. #ifdef USE_PRECOMPILE
  16. #pragma load ":Headers:dirscanh.hpc"
  17. #endif
  18.  
  19. #ifndef __ERRORS__
  20. #include <Errors.h>
  21. #endif
  22. #ifndef __FILES__
  23. #include <Files.h>
  24. #endif
  25. #ifndef __MEMORY__
  26. #include <Memory.h>
  27. #endif
  28. #ifndef __TOOLUTILS__
  29. #include <ToolUtils.h>
  30. #endif
  31. #ifndef __DIRSCAN__
  32. #include <dirscanh.h>
  33. #endif
  34.  
  35. #ifdef PRECOMPILE
  36. #undef PRECOMPILE
  37. #pragma dump ":Headers:dirscanh.hpc"
  38. #endif
  39.  
  40. /*
  41.  
  42.   dirscan.c by Andrew E. Page.    Macintosh Directory Scanning Code
  43.   The code operates as follows. 
  44.  
  45.   At the start of the scan StartScan accepts the directory in
  46.   which to start.  It initializes all the structures, starting
  47.   the scan in the directory at index 1, which is the first file
  48.   in the given directory.  For each call to GetNextScanFile the
  49.   file information for the current index is fetched, and the index
  50.   is incremented by one.  The entry is checked to see if it is
  51.   a directory.    If so the current state of the scan, the current
  52.   directory being scanned and the index for the next file, is pushed
  53.   onto a 'stack', and a recursive call to GetNextScanFile is made.    
  54.  
  55.  
  56.   If PBGetCatInfo returns a "File Not Found" error (-43 fnfErr) 
  57.   occurs during the scan the state of the stack is checked.  If
  58.   the stack is 'empty' (n_stackEntries == 0) then the scan is
  59.   at the top of the directory tree and has accessed the last
  60.   file, and the tree is exhasted.  Any other error should cause
  61.   the termination of the scan (this depends on how the code is
  62.   being used at a higher level however).
  63.   If the stack is not empty then the last entry is 'popped' off
  64.   of the stack and scanning resumes at that level.
  65.  
  66.  
  67. Example:
  68.  
  69. Consider the following directory tree.    With the vRefNum for <startdir>
  70. passed to StartScan, the files would be returned in the following
  71. order.    
  72.  
  73. file1a file1b file3a file3b file2a file2b file1c file4a
  74.  
  75.                      <startdir>
  76.                          |
  77.                          |
  78.                          |
  79.   file1a    file1b     <dir1a>   file1c    <dir1b>
  80.                         |                   |
  81.                         |                   |
  82.                         |                   |
  83.               <dir2a> file2a file2b        |
  84.                  |                           |
  85.                  |                           |
  86.                  |                           |
  87.            file3a file3b                   |
  88.                                            |
  89.                                            |
  90.                                         file4a
  91.                     
  92.  
  93.  
  94. */
  95.  
  96.  
  97. typedef struct {
  98.   long    dirID ; // the reference for the directory
  99.   short index ;  // what index it stopped scanning at
  100.   } StackEntry_t ;
  101.  
  102. typedef struct {
  103.   /*
  104.   * these fields provide
  105.   * control over the 'stack'.  Which
  106.   * saves the state of the scan from one
  107.   * level to the next.    
  108.   */
  109.   StackEntry_t    **entries ;       // container for the entries
  110.   long            n_stackEntries ;  // number of entries currently in stack
  111.   long            maxStackEntries ; // maximum entries stack can hold
  112.   long            level ;           // directory level we've decended to
  113.  
  114.   /*
  115.   * Storage space for the Parameter Block record.  This allows
  116.   * us to recycle certain fields without having to constantely
  117.   * reinitialize them.    
  118.   */
  119.   CInfoPBRec    F ;               // CInfo Rec ref IM IV-125
  120.  
  121.   /*
  122.   * filtering parameters
  123.   */
  124.   short       doFiltering ;       // is filtering necessary?
  125.   int          numTypes ;          // number of OSTypes to check for
  126.   OSType      *TypeList ;          // List of the types to check
  127.  
  128.   pascal Boolean (*filter)(FileParam *f) ; // filter function to be called for each file
  129.  
  130.   } ScanControl_t ;
  131.  
  132. /*
  133. *  We start off with a stack with a capacity for 32 entries.  Allowing
  134. *  us to descend 32 levels without having to reallocate any more memory.
  135. *  That should be sufficient for most applications.  However, more
  136. *  memory will be allocated if need be.  Each allocation will only
  137. *  require 192 additional bytes.
  138. *
  139. *  Season to taste here, just so long as STACK_BLOCK_INCREMENT >= 1
  140. */
  141. #define STACK_BLOCK_INCREMENT 32
  142.  
  143. /*
  144. * This is an internal function, hence the static qualifier. 
  145. * This provides the "push" or store function of a standard LIFO(Last In First Out)
  146. * queue.  Otherwise called a 'stack'.
  147. *
  148. *    Each time that it is called it stores the entry, in our case the directory
  149. * ID where the scan is 'branching' from, and the index in that directory where the
  150. * branch is taken, stores them in the container, and then increments the stack
  151. * pointer by one to make room for the next entry.  This differs slightly from the
  152. * nature of the 68XXX stack in as much as for a 68XXX stack the pointer is
  153. * predecremented to make space for the new entry.
  154. *
  155. *     Entries are retrieved from this stack with the PopFromStack function.    
  156. */
  157. static PushToStack(ScanControl_t *SC, long dirID, short index)
  158. {
  159.  
  160.   if( SC->n_stackEntries == SC->maxStackEntries )
  161.     {
  162.     /*
  163.     * Make room for more entries
  164.     */
  165.     SC->maxStackEntries += STACK_BLOCK_INCREMENT ;
  166.     SetHandleSize((Handle)SC->entries, sizeof(StackEntry_t) * SC->maxStackEntries) ;
  167.     }
  168.     
  169.   /*
  170.   * put the entry on the stack
  171.   */
  172.  
  173.   (*(SC->entries))[SC->n_stackEntries].dirID = dirID ;
  174.   (*(SC->entries))[SC->n_stackEntries].index = index ;
  175.  
  176.   SC->n_stackEntries += 1 ;
  177.   SC->level += 1 ;
  178.  
  179. } /* end of PushToStack */
  180.  
  181. /*
  182. * This is an internal function, hence the static qualifier. 
  183. * This provides the "pop" or retrieve function of a standard LIFO(Last In First Out)
  184. * queue.  Otherwise called a 'stack'.
  185. *
  186. *  Each time that it is called it restores the state of the scan from where
  187. * it had 'branched' in descending to another directory level.  It starts by
  188. * decrementing the 'stack pointer' so that it points at the last entry that was
  189. * placed on the stack by the PushToStack function.    
  190. */
  191. static PopFromStack(ScanControl_t *SC, long *dirID, short *index)
  192. {
  193.  
  194.   SC->n_stackEntries -= 1 ; 
  195.   SC->level -= 1 ;
  196.  
  197.   *index = (*(SC->entries))[SC->n_stackEntries].index ;
  198.   *dirID = (*(SC->entries))[SC->n_stackEntries].dirID ;
  199.  
  200. } /* end of PopFromStack */
  201.  
  202. #ifndef FALSE
  203. #define FALSE 0
  204. #define TRUE 1
  205. #endif
  206.  
  207. /*
  208. * This internal function determines whether or not this is a type
  209. * of file that we are looking for.    It first checks the files 'type'
  210. * against the list of types provided by the user, or simply passes it
  211. * on if the numTypes was specified as -1.
  212. *  If it succeeds in passing that check it then executes the 'filter' function
  213. * if supplied by the caller.
  214. */
  215. static Boolean doFilter(ScanControl_t *SC, FileParam *f)
  216. {
  217. int i ;
  218. short isType = FALSE ;
  219.  
  220.   /*
  221.   * check against types
  222.   */
  223.  
  224.   if( SC->numTypes != -1 )
  225.     {
  226.     for( i = 0 ; i < SC->numTypes ; i++ )
  227.       {
  228.       if( SC->TypeList[i] == f->ioFlFndrInfo.fdType )
  229.         {
  230.         isType = TRUE ;
  231.         break ;
  232.         }
  233.       }
  234.     /*
  235.     * Is this one of the file types that we are looking for?
  236.     * if not then exclude it from the list. 
  237.     */
  238.     if( !isType )
  239.       return TRUE ;
  240.     }
  241.  
  242.   /*
  243.   * Perform any additional filtering with the
  244.   * filter proc specified by the caller.
  245.   */
  246.   if( SC->filter )
  247.     return (*SC->filter)(f) ;
  248.  
  249.  
  250.   /*
  251.   * Otherwise if we've reached this point
  252.   * we do not want this filtered from the list
  253.   */
  254.   return FALSE ;
  255.  
  256. }
  257.  
  258.  
  259. /*
  260. *  This is essentially the core starting routine.  It will also be used
  261. * when starting a scan with a WDVRef instead of a ioDirID.    
  262. *
  263. *  Initializes all the fields necessary, setting up the Parameter
  264. * block for subsequent calls to GetNextDirScanFile. 
  265. *
  266. *  Sets up the Stack's controlling data structures, initializing the stack
  267. * pointer to 0 and allocating an initial block of entries.    
  268. *
  269. *  Does some pre-filter checks to optimize for the case where no filtering
  270. * is required.    It copies the TypeList to ensure that the data remains
  271. * valid if this function is being called from a routine where the TypeList
  272. * may reside in the stack frame that may be deallocated before calls to
  273. * GetNextScanFile.    
  274. *
  275. */
  276. Ptr StartDirScanDirID(short volRef, long dirID, int numTypes, OSType *TypeList, pascal Boolean (*filter)(FileParam *f))
  277. {
  278. ScanControl_t *SC ;
  279. StackEntry_t **entries ;
  280. OSType *theTypes ;
  281. long len ;
  282.  
  283.   /*
  284.   * Setup the control structure(s)
  285.   * At each allocation check to ensure that we
  286.   * got the space asked for.  If not, deallocate any
  287.   * previous allocated memory and return a 0L, indictating
  288.   * that the setup failed.    
  289.   */
  290.  
  291.   /*
  292.   * Check to see if the caller has provided a type list.
  293.   * This will be the case if numTypes is > 0.
  294.   * If so copy the list to ensure that it remains valid.
  295.   */
  296.   if( numTypes > 0 )
  297.     {
  298.     /*
  299.     * Make space for the copy of the TypeList.    
  300.     */
  301.     theTypes = (OSType *)NewPtr(len = numTypes * sizeof(OSType)) ; // ref IM I-75
  302.     if( theTypes == 0L )
  303.       return 0L ;
  304.     /*
  305.     * copy the scan types    
  306.     */
  307.     BlockMove(TypeList, theTypes, len) ; // ref IM II-44
  308.     }
  309.   else
  310.     theTypes = 0L ;
  311.     
  312.   /*
  313.   * Allocate space for the stack.  Start off with STACK_BLOCK_INCREMENT
  314.   * entries.  More can be added later if necessary. 
  315.   */
  316.   entries = (StackEntry_t **)NewHandle(sizeof(StackEntry_t) * STACK_BLOCK_INCREMENT) ; // ref IM I-76
  317.   if( entries == 0L )
  318.     {
  319.     DisposPtr((Ptr)theTypes) ; // ref IM I-75
  320.     return 0L ;
  321.     }
  322.  
  323.   /*
  324.   * Allocate space for the overal control structure that
  325.   * will be passed back to the caller.    
  326.   */
  327.   SC = (ScanControl_t *)NewPtr(sizeof(ScanControl_t)) ; // ref IM I-75
  328.   if( SC == 0L )
  329.     {
  330.     DisposPtr((Ptr)theTypes) ; // ref IM I-75
  331.     DisposHandle((Handle)entries) ;
  332.     return 0L ;
  333.     }
  334.     
  335.   /*
  336.   * Setup the filtering
  337.   */
  338.  
  339.   if( (numTypes > 0) || (filter != 0L) )
  340.     {
  341.     SC->doFiltering = TRUE ;
  342.     SC->numTypes = numTypes ;
  343.     SC->TypeList = theTypes ;
  344.     SC->filter = filter ;
  345.     }
  346.   else
  347.     {
  348.     SC->TypeList = 0L ;
  349.     SC->doFiltering = FALSE ;
  350.     }
  351.     
  352.   /*
  353.   * initialize the current
  354.   * state of the scan
  355.   */
  356.  
  357.   SC->entries = entries ;
  358.   SC->n_stackEntries = 0 ;
  359.   SC->maxStackEntries = STACK_BLOCK_INCREMENT ;
  360.   SC->level = 0 ;
  361.  
  362.   /*
  363.   * Setup the initial PB Rec  ref IM IV-125
  364.   */
  365.  
  366.   SC->F.hFileInfo.ioCompletion = 0L ; // always initialize this field!
  367.   SC->F.hFileInfo.ioFlParID = dirID ; // Parent dirtory to start scan in.
  368.   SC->F.hFileInfo.ioVRefNum = volRef ;// true volume reference number
  369.   SC->F.hFileInfo.ioFDirIndex = 0 ;   // initial scanning index.  (will be incremented for each call)
  370.  
  371.   return (Ptr)SC ;
  372.  
  373. } /* end of StartDirScanDirID */
  374.  
  375. /*
  376. *  This call allows us to start off with a WDVRef that may have been
  377. * returned from SFGetFile.    It returns in volRef the true volume
  378. * reference for the volume where the working directory passed in
  379. * vRef resides. 
  380. */
  381. Ptr StartDirScanWDVRef(short vRef, short *volRef, int numTypes, OSType *TypeList, pascal Boolean (*filter)(FileParam *f))
  382. {
  383. OSErr err ;
  384. WDPBRec wd ;
  385.  
  386.   /*
  387.   * Setup the the wd parameter block
  388.   */
  389.  
  390.   wd.ioCompletion = 0L ;
  391.   wd.ioNamePtr = 0L ;
  392.   wd.ioVRefNum = vRef ;
  393.   wd.ioWDIndex = 0 ;
  394.  
  395.   /*
  396.   * obtain the WD info
  397.   */
  398.  
  399.   err = PBGetWDInfo(&wd, FALSE) ;
  400.   if( err )
  401.     return 0L ;
  402.     
  403.   *volRef = wd.ioWDVRefNum ; /* the true volume reference number */
  404.  
  405.   /*
  406.   * use StartDirScanDirID to initialize the scan.
  407.   */
  408.   return StartDirScanDirID(wd.ioWDVRefNum, wd.ioWDDirID, numTypes, TypeList, filter) ;
  409.     
  410. } /* end of StartDirScan */
  411.  
  412.  
  413. /*
  414. * This function is essentially the core of the system.    Each call to this function
  415. * will produce a file, or an fnfErr when the scan is completed.  Any other error is 
  416. * an indication of something else being terribly wrong.
  417. *
  418. *  At each call the function will do the following:
  419. *
  420. *     1.  Increment the index of the file that is currently pointing at.
  421. *
  422. *     2.  Get the file's catalog information with PBGetCatInfo.
  423. *
  424. *     3.  If the file is a directory it will save the state of the scan on the
  425. *         internal stack, and make a recursive call to itself to the next level. 
  426. *
  427. *     4.  If PBGetCatInfo returns with a File Not Find Error (fnfErr -43), it pops
  428. *         the last entry off of the stack, restoring the state of the scan and makes
  429. *         a recursive call to itself to return to the previous level.  If the stack
  430. *         at this point is empty it returns the fnfErr to the caller indicating that
  431. *         the scan has completed.
  432. *
  433. *     5.  If the file entry is not a directory it checks it against the types and
  434. *         filter function specified by the caller.  If it is rejected by the filter, 
  435. *         (when it returns TRUE) GetNextDirScanFile repeats steps 1 through 4.
  436. */
  437. OSErr GetNextDirScanFile(Ptr scanPtr, char *fName, long *dirID, long *level)
  438. {
  439. ScanControl_t *SC = (ScanControl_t *)scanPtr ;
  440. OSErr err ;
  441. short flag = FALSE ;
  442. long parID = SC->F.hFileInfo.ioFlParID ;
  443.  
  444.   SC->F.hFileInfo.ioNamePtr = fName ;
  445.  
  446.   do {
  447.     SC->F.hFileInfo.ioFDirIndex++ ;
  448.     SC->F.hFileInfo.ioDirID = parID ;
  449.     err = PBGetCatInfo(&SC->F, FALSE) ;
  450.     if( err )
  451.       {
  452.       /*
  453.       * Anything other than a fnfErr, or a noErr is an inidcation
  454.       * of some catastrophic failure and that the scan cannot procede.
  455.       */
  456.       if( err != fnfErr )
  457.         return err ;
  458.         
  459.       /*
  460.       * if there is an fnfErr and there are no more stack entries, this means
  461.       * that the tree that is being searched has been exhausted.  Returning
  462.       * the fnfErr at this point should terminate the scan by the caller.
  463.       */
  464.       if( SC->n_stackEntries == 0 )
  465.         return err ;
  466.     
  467.       /*
  468.       * if it is a fnfErr, and entries remain in the stack, we have exhausted
  469.       * the file entries at this level and need to return to the previous level
  470.       * and resuming scanning where  we left off.  To do this we Pop the previous
  471.       * state of the scan  off of the stack, and make a recursive call to GetNextDirScanFile.
  472.       */
  473.       PopFromStack(SC, &SC->F.hFileInfo.ioFlParID, &SC->F.hFileInfo.ioFDirIndex) ;
  474.             
  475.       return GetNextDirScanFile((Ptr)SC, fName, dirID, level) ;
  476.       }
  477.  
  478.     if( SC->F.hFileInfo.ioFlAttrib & 0x10 ) // check for directory flag
  479.       {
  480.       /*
  481.       * Save the state of the scan
  482.       * at the current level.
  483.       */
  484.       PushToStack(SC, SC->F.dirInfo.ioDrParID, SC->F.hFileInfo.ioFDirIndex) ;
  485.     
  486.       /*
  487.       * Setup the the state of the next level to be
  488.       * the directory that we just found.  Starting at the
  489.       * first file index.
  490.       */
  491.       SC->F.hFileInfo.ioFlParID = SC->F.dirInfo.ioDrDirID ;
  492.       SC->F.hFileInfo.ioFDirIndex = 0 ;
  493.     
  494.       return GetNextDirScanFile((Ptr)SC, fName, dirID, level) ;
  495.       }
  496.  
  497.     /*
  498.     * if necessary check to see if we should filter
  499.     * out this file entry.    
  500.     */
  501.     
  502.     if( SC->doFiltering )
  503.       flag = doFilter(SC, (FileParam *)&SC->F) ;
  504.     } while( flag ) ; // if file is to be filtered, try another one.
  505.     
  506.   /*
  507.   * Set the directory ID parameter
  508.   */
  509.   *dirID = SC->F.hFileInfo.ioFlParID ;
  510.  
  511.   /*
  512.   * if the caller wants the level of this file set it.
  513.   */
  514.   if( level )
  515.     *level = SC->level ;
  516.     
  517.   return err ;
  518.  
  519. } /* end of GetNextDirScanFile */
  520.  
  521.  
  522. /*
  523. * This terminates the scan.  What is actually does is release
  524. * all the memory that was allocated.  Since no new working
  525. * directories were created there is no need to call PBCloseWD.
  526. */
  527. OSErr StopDirScan(Ptr scanPtr)
  528. {
  529. ScanControl_t *SC = (ScanControl_t *)scanPtr ;
  530.  
  531.   if( SC->TypeList )
  532.     DisposPtr((Ptr)SC->TypeList) ;
  533.  
  534.   DisposHandle((Handle)SC->entries) ;
  535.   DisposPtr(scanPtr) ;
  536.  
  537.   /*
  538.   * Although it is unlikely that we will have comitted
  539.   * any errors provide this check to the caller.
  540.   */
  541.   return MemError() ;
  542. }
  543.  
  544. /*
  545. *
  546. * This function will generate a full pathname to
  547. * the file identified by its name, volume, and directory ID.
  548. * The result is a handle that contains the full pathname of the
  549. * file and is sized to fit that pathname exactly.  It contains
  550. * No length byte or null terminator.  The length of the string can
  551. * be determined with GetHandleSize. 
  552. *
  553. *
  554. * The function operates as a loop.    At each interation through the
  555. * the loop it gets the catalog name for the entry.    Each time
  556. * making the parent directory for that entry the target for the
  557. * next cycle.  This proceeds until it hits the root directory
  558. * is reached, which has no logical parent, and thus PBGetCatInfo
  559. * returned a file not found error(-43 fnfErr).
  560. *
  561. *     We use a handle since this allows us to use the
  562. * Munger string manipulator.
  563. *
  564. */
  565.  
  566. Handle FullPathNameID(char *fName, int vRef, long dirID)
  567. {
  568. Handle theString = NewHandle(0) ; /* container for the string */
  569. CInfoPBRec F ;
  570. short err ;
  571. char colon = ':' ; /* just in case you want to put it into a stand alone code resource */
  572. char string[64] ;
  573.  
  574.   /*
  575.   * For safety sake(to ensure that our string contains enough space for 
  576.   * subsequent entries found, and to make sure that we don't overwrite
  577.   * the input string, we copy the input fileName to our own storage.
  578.   */
  579.  
  580.   BlockMove(fName, string, fName[0]+1) ; // ref IM II-44
  581.  
  582.   /*
  583.   * Initialize the PB fields
  584.   */
  585.  
  586.   F.hFileInfo.ioNamePtr = string ;    /* pascal string for the name, and also subsequent storage on calls */
  587.   F.hFileInfo.ioVRefNum = vRef ;  /* volume reference (Gotten from SFGetFile perhaps */
  588.   F.hFileInfo.ioCompletion = 0L ; /* initialize this field or get a Serious BOMB */
  589.   F.hFileInfo.ioFDirIndex = 0 ;   /* for the first call only.  Get CatInfo based on ioNamePtr, ioVRefNum, and dirID */
  590.   F.hFileInfo.ioDirID = dirID ;   /* directory containing this file */
  591.  
  592.   while( !(err = PBGetCatInfo(&F,0)) )
  593.     {
  594.     /*
  595.     * Insert the directory level into the front of the string
  596.     */
  597.     Munger(theString, 0, 0L, 0, &string[1], string[0]) ; // ref IM I-468
  598.  
  599.     /*
  600.     * Insert a colon in front of the Directory Entry
  601.     */
  602.     Munger(theString, 0, 0L, 0, &colon, 1) ; // ref IM I-468
  603.  
  604.     /*
  605.     * We now wish to get the information about
  606.     * the directory that contains this file/directory.
  607.     * in short we are moving one level UP in the heirarchy
  608.     */
  609.     F.dirInfo.ioDrDirID = F.dirInfo.ioDrParID ; /* get the ioDrDirID of the PARENT directory (the next level up) */
  610.     
  611.     /*
  612.     * Setting this field to < 0 will
  613.     * cause PBGetCatInfo to get the catalog
  614.     * information based on the VRef and dirID
  615.     * that we've placed in F.dirInfo.ioDrDirID,
  616.     * and return the name of the entry in the
  617.     * pointer to the string in ioNamePtr.
  618.     */
  619.     F.hFileInfo.ioFDirIndex = -1 ;
  620.     }
  621.     
  622.   /*
  623.   * The last entry will be the Volume name which should not have a colon in front of it
  624.   */
  625.     
  626.   Munger(theString, 0, 0L, 1, string, 0) ;
  627.  
  628.   return theString ;
  629. }
  630.  
  631.  
  632. -- 
  633. Andrew E. Page CTO(Warrior Poet)|   Decision and Effort The Archer and Arrow
  634. DSP Ironworks                   |     The difference between what we are
  635. Macintosh and DSP Technology    |           and what we want to be.
  636.