home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 308.lha / treewalk / treewalk.c < prev    next >
C/C++ Source or Header  |  1980-12-05  |  6KB  |  234 lines

  1. /*
  2.  * treewalk.c - generic tree walking routine for AmigaDOS trees.
  3.  *
  4.  *    Copyright (C) 1989  Mike Meyer
  5.  *
  6.  *    This program is free software; you can redistribute it and/or modify
  7.  *    it under the terms of the GNU General Public License as published by
  8.  *    the Free Software Foundation; either version 1, or any later version.
  9.  *
  10.  *    This program is distributed in the hope that it will be useful,
  11.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  *    GNU General Public License for more details.
  14.  *
  15.  *    You should have received a copy of the GNU General Public License
  16.  *    along with this program; if not, write to the Free Software
  17.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  */
  19.  
  20. #include <exec/types.h>
  21. #include <libraries/dosextens.h>
  22. #include <proto/dos.h>
  23. #include <proto/exec.h>
  24. #include "treewalk.h"
  25.  
  26. /*
  27.  * We keep a stack of locks on directories yet to be visited. Stacknode is the
  28.  * used for this, containing nothing but the pointer down the stack, and
  29.  * the current lock.
  30.  */
  31. struct stacknode {
  32.     struct stacknode    *sn_next ;
  33.     long            sn_lock ;
  34.     } *stacktop = NULL ;
  35.  
  36. /*
  37.  * Stacktop is global so that these two functions can work cleanly (bleah).
  38.  * Push returns the top value on the stack while removing it from the stack.
  39.  * Pop puts a new value on the stack, and returns FALSE if it fails.
  40.  */
  41.  
  42. static long
  43. pop(void) {
  44.     long out ;
  45.     struct stacknode *old ;
  46.  
  47.     if (!stacktop) return NULL ;
  48.     old = stacktop ;
  49.     stacktop = old->sn_next ;
  50.     out = old->sn_lock ;
  51.     FreeMem(old, sizeof(struct stacknode)) ;
  52.     return out ;
  53.     }
  54.  
  55. static int
  56. push(long new) {
  57.     struct stacknode *top ;
  58.  
  59.     if ((top = (struct stacknode *) AllocMem(sizeof(struct stacknode), 0)) == NULL) {
  60.         UnLock(new) ;    /* As we're about to throw it out... */
  61.         return FALSE ;
  62.         }
  63.     top->sn_lock = new ;
  64.     top->sn_next = stacktop ;
  65.     stacktop = top ;
  66.     return TRUE ;
  67.     }
  68.  
  69. int
  70. treewalk(long root, int (*userfunc)(long, struct FileInfoBlock *), int flags) {
  71.     register long            mylock, childlock ;
  72.     register struct FileInfoBlock    *fib ;
  73.     register int            stat ;
  74.     int                visit, scan ;
  75.  
  76.     if ((fib = AllocMem(sizeof(struct FileInfoBlock), 0)) == NULL)
  77.         return FALSE ;
  78.  
  79.     /*
  80.      * Set up the lock & stack as apropos for this pass. If we
  81.      * are doing only a preorder walk, then we always visit & scan
  82.      * simultaneously, so we just set them and forget it. postorder
  83.      * walks will reset them as needed.
  84.      */
  85.     mylock = DupLock(root) ;
  86.     if (!Examine(mylock, fib)        /* Can't examine */
  87.     || fib->fib_DirEntryType < 0) {    /* Not a directory */
  88.         UnLock(mylock) ;
  89.         FreeMem(fib, sizeof(*fib)) ;
  90.         return FALSE ;
  91.         }
  92.     if (flags & TREE_POST)
  93.         if (push(mylock)) mylock = root  ;
  94.         else {
  95.             UnLock(mylock) ;
  96.             FreeMem(fib, sizeof(*fib)) ;
  97.             return FALSE ;
  98.             }
  99.     visit = scan = flags & TREE_PRE ;
  100.  
  101.     do {
  102.         /*
  103.          * We use root as a "mark" that the following node has been
  104.          * read for directories, but not visited. The mark could be
  105.          * anything, but we shouldn't ever find the root of the
  106.          * tree in the tree. Visit & scan are set to mark the next
  107.          * loop as either a visit, or a scan, or both.
  108.          */
  109.         if (flags & TREE_POST)
  110.             if (mylock != root) {
  111.                 scan = FALSE ;
  112.                 visit = TRUE ;
  113.                 }
  114.             else {
  115.                 scan = TRUE ;
  116.                 visit = flags & TREE_PRE ;
  117.                 mylock = DupLock(stacktop->sn_lock) ;
  118.                 }
  119.  
  120.         if (!(stat = Examine(mylock, fib))) break ;
  121.         if (visit && (stat = (*userfunc)(mylock, NULL))) break ;
  122.         while (ExNext(mylock, fib)
  123.         || IoErr() != ERROR_NO_MORE_ENTRIES) {
  124.             if (fib->fib_DirEntryType >= 0 && scan) {
  125.                 mylock = CurrentDir(mylock) ;
  126.                 stat = (childlock = Lock(fib->fib_FileName, ACCESS_READ))
  127.                     && push(childlock) ;
  128.                 mylock = CurrentDir(mylock) ;
  129.                 if (flags & TREE_POST)
  130.                     stat = stat && push(root) ;
  131.                 if (!stat) goto twobreak ;
  132.                 }
  133.             if (visit && (stat = (*userfunc)(mylock, fib)))
  134.                 goto twobreak ;
  135.             }
  136.         UnLock(mylock) ;
  137.         stat = TRUE ;        /* Make sure it's right for exit */
  138.         } while (mylock = pop()) ;
  139. /*
  140.  * C doesn't have a multi-level break, so we have to have a label for errors
  141.  * in the inner while to come to.
  142.  *
  143.  * Shit.
  144.  */
  145.  
  146. twobreak:
  147.     /* Free up the locks, the AllocMem'd memory, and then return */
  148.     while (mylock) {
  149.         if (mylock != root) UnLock(mylock) ;
  150.         mylock = pop() ;
  151.         } 
  152.  
  153.     FreeMem(fib, sizeof(*fib)) ;
  154.     return stat ;
  155.     }
  156.  
  157. #ifdef    TEST
  158. /*
  159.  * Test code - just print the tree starting at the current dir, or at
  160.  *    the first (only) argument.
  161.  */
  162.  
  163. #include <stdio.h>
  164. #include <stdlib.h>
  165. #include <string.h>
  166. #include <signal.h>
  167. #include <dos.h>
  168.  
  169. static int    haltflag = FALSE ;
  170.  
  171. static int
  172. testvisit(long lock, struct FileInfoBlock *fib) {
  173.     static char    pathname[FMSIZE] ;
  174.     char        *term ;
  175.  
  176.     if (haltflag) return TREE_STOP ;
  177.     if (!fib) {    /* A directory we're about to scan */
  178.         if (getpath(lock, pathname)) return TREE_STOP ;
  179.         term = strchr(pathname, ':') == NULL ? ":" : "/" ;
  180.         strcat(pathname, term) ;
  181.         puts(pathname) ;
  182.         return TREE_CONT ;
  183.         }
  184.  
  185.     /* A file from the directory we're now scanning */
  186.     if (fib->fib_DirEntryType < 0)
  187.         printf("%s%s\n", pathname, fib->fib_FileName) ;
  188.     return TREE_CONT ;
  189.     }
  190.  
  191. static int
  192. do_break(int sig) {
  193.  
  194.     haltflag = TRUE ;
  195.     signal(SIGINT, do_break) ;
  196.     return 0 ;
  197.     }
  198.  
  199. void
  200. main(int argc, char **argv) {
  201.     long    start ;
  202.     struct Process *proc ;
  203.  
  204.     switch (argc) {
  205.         case 0: case 1:
  206.             proc = (struct Process *) FindTask(NULL) ;
  207.             start = DupLock(proc->pr_CurrentDir) ;
  208.             break ;
  209.         case 2:
  210.             if ((start = Lock(argv[1], ACCESS_READ)) == NULL) {
  211.                 fprintf(stderr, "%s: Can't lock %s\n",
  212.                     argv[0], argv[1]) ;
  213.                 exit(RETURN_ERROR) ;
  214.                 }
  215.             break ;
  216.         default:
  217.             fprintf(stderr, "usage: %s [directory]\n", argv[0]) ;
  218.             exit(RETURN_ERROR) ;
  219.         }
  220.  
  221.     /* Arrange to catch signals if we're not ignoring them */
  222.     if (signal(SIGINT, do_break) == SIG_IGN)
  223.         signal(SIGINT, SIG_IGN) ;
  224.  
  225.     if (!treewalk(start, testvisit, TREE_BOTH)) {
  226.         fprintf(stderr, "%s: Something broke...\n", argv[0]) ;
  227.         UnLock(start) ;
  228.         exit(RETURN_WARN) ;
  229.         }
  230.     if (haltflag) printf("*** BREAK\n") ;
  231.     UnLock(start) ;
  232.     exit(RETURN_OK) ;
  233.     }
  234. #endif