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