home *** CD-ROM | disk | FTP | other *** search
- /*
- * treewalk.c - generic tree walking routine for AmigaDOS trees.
- *
- * Copyright (C) 1989 Mike Meyer
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 1, or any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- #include <exec/types.h>
- #include <libraries/dosextens.h>
- #include <proto/dos.h>
- #include <proto/exec.h>
- #include "treewalk.h"
-
- /*
- * We keep a stack of locks on directories yet to be visited. Stacknode is the
- * used for this, containing nothing but the pointer down the stack, and
- * the current lock.
- */
- struct stacknode {
- struct stacknode *sn_next ;
- long sn_lock ;
- } *stacktop = NULL ;
-
- /*
- * Stacktop is global so that these two functions can work cleanly (bleah).
- * Push returns the top value on the stack while removing it from the stack.
- * Pop puts a new value on the stack, and returns FALSE if it fails.
- */
-
- static long
- pop(void) {
- long out ;
- struct stacknode *old ;
-
- if (!stacktop) return NULL ;
- old = stacktop ;
- stacktop = old->sn_next ;
- out = old->sn_lock ;
- FreeMem(old, sizeof(struct stacknode)) ;
- return out ;
- }
-
- static int
- push(long new) {
- struct stacknode *top ;
-
- if ((top = (struct stacknode *) AllocMem(sizeof(struct stacknode), 0)) == NULL) {
- UnLock(new) ; /* As we're about to throw it out... */
- return FALSE ;
- }
- top->sn_lock = new ;
- top->sn_next = stacktop ;
- stacktop = top ;
- return TRUE ;
- }
-
- int
- treewalk(long root, int (*userfunc)(long, struct FileInfoBlock *), int flags) {
- register long mylock, childlock ;
- register struct FileInfoBlock *fib ;
- register int stat ;
- int visit, scan ;
-
- if ((fib = AllocMem(sizeof(struct FileInfoBlock), 0)) == NULL)
- return FALSE ;
-
- /*
- * Set up the lock & stack as apropos for this pass. If we
- * are doing only a preorder walk, then we always visit & scan
- * simultaneously, so we just set them and forget it. postorder
- * walks will reset them as needed.
- */
- mylock = DupLock(root) ;
- if (!Examine(mylock, fib) /* Can't examine */
- || fib->fib_DirEntryType < 0) { /* Not a directory */
- UnLock(mylock) ;
- FreeMem(fib, sizeof(*fib)) ;
- return FALSE ;
- }
- if (flags & TREE_POST)
- if (push(mylock)) mylock = root ;
- else {
- UnLock(mylock) ;
- FreeMem(fib, sizeof(*fib)) ;
- return FALSE ;
- }
- visit = scan = flags & TREE_PRE ;
-
- do {
- /*
- * We use root as a "mark" that the following node has been
- * read for directories, but not visited. The mark could be
- * anything, but we shouldn't ever find the root of the
- * tree in the tree. Visit & scan are set to mark the next
- * loop as either a visit, or a scan, or both.
- */
- if (flags & TREE_POST)
- if (mylock != root) {
- scan = FALSE ;
- visit = TRUE ;
- }
- else {
- scan = TRUE ;
- visit = flags & TREE_PRE ;
- mylock = DupLock(stacktop->sn_lock) ;
- }
-
- if (!(stat = Examine(mylock, fib))) break ;
- if (visit && (stat = (*userfunc)(mylock, NULL))) break ;
- while (ExNext(mylock, fib)
- || IoErr() != ERROR_NO_MORE_ENTRIES) {
- if (fib->fib_DirEntryType >= 0 && scan) {
- mylock = CurrentDir(mylock) ;
- stat = (childlock = Lock(fib->fib_FileName, ACCESS_READ))
- && push(childlock) ;
- mylock = CurrentDir(mylock) ;
- if (flags & TREE_POST)
- stat = stat && push(root) ;
- if (!stat) goto twobreak ;
- }
- if (visit && (stat = (*userfunc)(mylock, fib)))
- goto twobreak ;
- }
- UnLock(mylock) ;
- stat = TRUE ; /* Make sure it's right for exit */
- } while (mylock = pop()) ;
- /*
- * C doesn't have a multi-level break, so we have to have a label for errors
- * in the inner while to come to.
- *
- * Shit.
- */
-
- twobreak:
- /* Free up the locks, the AllocMem'd memory, and then return */
- while (mylock) {
- if (mylock != root) UnLock(mylock) ;
- mylock = pop() ;
- }
-
- FreeMem(fib, sizeof(*fib)) ;
- return stat ;
- }
-
- #ifdef TEST
- /*
- * Test code - just print the tree starting at the current dir, or at
- * the first (only) argument.
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <signal.h>
- #include <dos.h>
-
- static int haltflag = FALSE ;
-
- static int
- testvisit(long lock, struct FileInfoBlock *fib) {
- static char pathname[FMSIZE] ;
- char *term ;
-
- if (haltflag) return TREE_STOP ;
- if (!fib) { /* A directory we're about to scan */
- if (getpath(lock, pathname)) return TREE_STOP ;
- term = strchr(pathname, ':') == NULL ? ":" : "/" ;
- strcat(pathname, term) ;
- puts(pathname) ;
- return TREE_CONT ;
- }
-
- /* A file from the directory we're now scanning */
- if (fib->fib_DirEntryType < 0)
- printf("%s%s\n", pathname, fib->fib_FileName) ;
- return TREE_CONT ;
- }
-
- static int
- do_break(int sig) {
-
- haltflag = TRUE ;
- signal(SIGINT, do_break) ;
- return 0 ;
- }
-
- void
- main(int argc, char **argv) {
- long start ;
- struct Process *proc ;
-
- switch (argc) {
- case 0: case 1:
- proc = (struct Process *) FindTask(NULL) ;
- start = DupLock(proc->pr_CurrentDir) ;
- break ;
- case 2:
- if ((start = Lock(argv[1], ACCESS_READ)) == NULL) {
- fprintf(stderr, "%s: Can't lock %s\n",
- argv[0], argv[1]) ;
- exit(RETURN_ERROR) ;
- }
- break ;
- default:
- fprintf(stderr, "usage: %s [directory]\n", argv[0]) ;
- exit(RETURN_ERROR) ;
- }
-
- /* Arrange to catch signals if we're not ignoring them */
- if (signal(SIGINT, do_break) == SIG_IGN)
- signal(SIGINT, SIG_IGN) ;
-
- if (!treewalk(start, testvisit, TREE_BOTH)) {
- fprintf(stderr, "%s: Something broke...\n", argv[0]) ;
- UnLock(start) ;
- exit(RETURN_WARN) ;
- }
- if (haltflag) printf("*** BREAK\n") ;
- UnLock(start) ;
- exit(RETURN_OK) ;
- }
- #endif