[net.micro.6809] OS-9 file system walker

jejones@ea.UUCP (10/31/84)

Attached is, in shar form, header files and code for a directory tree
walker. There are some perhaps dubious design decisions therein, notably
the decision about how to treat files one can't open (but then, one
shouldn't pass them by--what if you want to walk the file system to
find exactly those files you can't open?), but on the whole it seems
a reasonable tool. (Perhaps someone out there will write and post a
find before I do...) An rdump indicates that the compiled code is
a hair over 1K (if memory serves), which seems rather nice.

					Share and enjoy,
					James Jones

------------------------------TARE HEAR------------------------------
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# bool.h WalkFS.h WalkFS.c

echo x - bool.h
sed 's/^	//' > "bool.h" << '//E*O*F bool.h//'
	/*
	 * bool.h -- for those who, like me, would really like to have
	 * something approaching a Boolean type in C, when we are forced
	 * to use C.
	 */
	
	typedef int bool;
	
	#define TRUE	1
	#define FALSE	0
//E*O*F bool.h//

echo x - WalkFS.h
sed 's/^	//' > "WalkFS.h" << '//E*O*F WalkFS.h//'
	/*
	 * WalkFS.h -- definitions for values returned from and parameters for
	 * the WalkFS() function.
	 */
	
	#define OK		0	/* WalkFS() succeeded			*/
	#define TooDeep		1	/* tree too deep for WalkFS()		*/
	#define Stymie		2	/* WalkFS() couldn't even start walking	*/
	#define BadDir		3	/* directory opened, but looks wrong	*/
	#define MungDir		4	/* directory tweaked behind our backs	*/
	#define VisitSuccess	5	/* visit returned non-zero value for	*/
					/* pathname (see AsSoonAs)		*/
	
	#define VisitDir	-1	/* visit only directories		*/
	#define VisitNDir	1	/* visit only non-directories		*/
	#define VisitAll	0	/* visit files whether directory or not	*/
	
	#define PreOrder	0	/* do a pre-order traversal		*/
	#define PostOrder	1	/* do a post-order traversal		*/
	
	#define Ignore		0	/* ignore results of visit function	*/
	#define AsSoonAs	1	/* return as soon as visit returns	*/
					/* non-zero value			*/
//E*O*F WalkFS.h//

echo x - WalkFS.c
sed 's/^	//' > "WalkFS.c" << '//E*O*F WalkFS.c//'
	/*
	 * WalkFS -- file system walker, or an iterator if you will.
	 *
	 * Parameters:
	 * a string with the pathname of the file to consider the root, a pointer
	 * to the function to use to visit files, an indicator of which files
	 * (directories, non-directories, or all files) to visit, an indicator
	 * of whether to do a pre-order or post-order walk, and an indicator of
	 * whether to quit early.
	 *
	 * "Visiting" a file, to WalkFS, means invoking the referenced
	 * function, with a pointer to the pathname of the file as argument.
	 * The visiting function is presumed to return an int; depending on
	 * the parameters passed, WalkFS can quit the first time the function
	 * returns a non-zero value.
	 *
	 * Method:
	 * WalkFS does a (non-recursive) traversal. This implies that we must
	 * remember where we are in each of the directories in the path down
	 * to where we are looking at any given moment. We can't always keep
	 * all those directories open, since a process is limited in the number
	 * of files it can have open concurrently, so when we run out, we re-
	 * member our position in the highest-level (nearest the root) directory
	 * we have open and then close it, making room for a new low-level
	 * directory to open.
	 *
	 * Warnings:
	 * 1. We do some checking if we re-open directories to make sure
	 *    that nothing horrible happened behind our backs. This checking
	 *    isn't as thorough as it might be.
	 * 2. If you visit directories in pre-order, then WalkFS() will have
	 *    them open while you visit them. In all other cases, WalkFS()
	 *    won't have the file being visited open. This is important if
	 *    Visit() modifies (or deletes, for that matter) the file!
	 * 3. If we can't open a file, we can't tell whether it's a directory.
	 *    By fiat, we will treat such files as "non-directories."
	 */
	
	#include <stdio.h>
	#include <errno.h>
	#include <direct.h>
	#include <modes.h>
	#include <WalkFS.h>
	#include <bool.h>
	
	#define MAXPATH	512
	#define NOFL	20
	#define DESIZE	sizeof(struct dirent)
	
	int
	WalkFS(Root, Visit, Itinerary, Order, QuitEarly)
	char	*Root;
	int	(*Visit)();
	int	Itinerary, Order, QuitEarly;
	{
		bool		DirVisit, NDirVisit;
		int		PathNo, PathStack[_NFILE];
		long		OflStack[NOFL];
		int		PathTop, OflTop;
		char		PathName[MAXPATH];
		int		PNEnd;
		struct dirent	DEntry;
		int		i;
	
		PathTop = OflTop = -1;
		DirVisit  = Itinerary <= 0;
		NDirVisit = Itinerary >= 0;
	
		if ((PNEnd = strlen(Root)) >= MAXPATH)
			return(TooDeep);
		strcpy(PathName, Root);
	
		if ((PathNo = open(PathName, S_IREAD | S_IFDIR)) < 0 &&
			(PathNo = open(PathName, S_IREAD) < 0)
		)
			return(Stymie);
		close(PathNo);
	
		for (;;) {
			PathNo = open(PathName, S_IREAD | S_IFDIR);
			if (PathNo < 0 && errno == E_PTHFUL) {
				if (OflTop >= NOFL) {
					BigClose(PathStack, PathTop);
					return(TooDeep);
				}
				OflStack[++OflTop] = lseek(PathStack[0], 0L, 1);
				close(PathStack[0]);
				for (i = 0; i < PathTop; i++)
					PathStack[i] = PathStack[i + 1];
				PathNo = open(PathName, S_IREAD | S_IFDIR);
			}
			if (PathNo >= 0) {
				if (DirVisit && Order == PreOrder)
					if ((*Visit)(PathName) && QuitEarly) {
						BigClose(PathStack, PathTop);
						return(VisitSuccess);
					}
				if (lseek(PathNo, (long) (2 * DESIZE), 0) < 0) {
					BigClose(PathStack, PathTop);
					return(BadDir);
				}
				PathStack[++PathTop] = PathNo;
			} else {
				if (NDirVisit)
					if ((*Visit)(PathName) && QuitEarly) {
						BigClose(PathStack, PathTop);
						return(VisitSuccess);
					}
				PNEnd = DelEntry(PathName, PNEnd);
			}
			for (;;) {
				if (PathTop < 0 && OflTop < 0)
					return(OK);
				if (read(PathStack[PathTop], &DEntry, DESIZE) != DESIZE)
				{
					close(PathStack[PathTop--]);
					if (DirVisit && Order == PostOrder)
						if ((*Visit)(PathName) && QuitEarly)
						{
							BigClose(PathStack, PathTop);
							return(VisitSuccess);
						}
					PNEnd = DelEntry(PathName, PNEnd);
					if (PathTop < 0 && OflTop >= 0) {
						PathNo = open(PathName, S_IREAD);
						if (PathNo < 0 ||
						    lseek(PathNo, OflStack[OflTop--], 0) < 0)
						{
							BigClose(PathStack, PathTop);
							return(MungDir);
						}
						PathStack[++PathTop] = PathNo;
					}
				} else if (DEntry.dir_name[0] != '\0') {
					PNEnd = AddEntry(PathName, &DEntry, PNEnd);
					if (PNEnd < 0) {
						BigClose(PathStack, PathTop);
						return(TooDeep);
					}
					break;
				}
			}
		}
	
	}
	
	/*
	 * BigClose -- close all the files that WalkFS has sitting around open;
	 * called in case of problems that we want to get out of immediately.
	 * (In the normal course of events, the directories that WalkFS opens
	 * are closed after they've been read in their entirety.)
	 */
	
	static
	BigClose(PList, Top)
	int	PList[];
	int	Top;
	{
		int	i;
	
		for (i = 0; i <= Top; i++)
			close(PList[i]);
	
	}
	
	/*
	 * AddEntry, DelEntry--adding and deleting bottom-level branches
	 * from pathnames
	 *
	 * These functions expect someone to keep track of the index in the
	 * pathname of the terminating '\0', and return a new value for that
	 * index (or -1 in case of error). Perhaps this is too specialized.
	 * Note also that the incoming entry in AddEntry is a real live
	 * directory entry, with high-order bit terminated string and all.
	 */
	
	static int
	AddEntry(Path, Entry, Tail)
	char		Path[];
	struct dirent	*Entry;
	int		Tail;
	{
		int	i;
	
		if (Tail < MAXPATH) {
			Path[Tail++] = '/';
			for (i = 0; Tail < MAXPATH; i++) {
				if (Entry->dir_name[i] & 0x80) {
					Path[Tail++] = Entry->dir_name[i] & 0x7f;
					if (Tail >= MAXPATH)
						break;
					Path[Tail] = '\0';
					return(Tail);
				} else
					Path[Tail++] = Entry->dir_name[i];
			}
		}
	
		return(-1);
	
	}
	
	static int
	DelEntry(Path, Tail)
	char	Path[];
	int	Tail;
	{
		for (; Tail > 0; Tail--)
			if (Path[Tail] == '/')
				break;
		Path[Tail] = '\0';
		return(Tail);
	}
//E*O*F WalkFS.c//

exit 0