[comp.unix.wizards] ftw

ruiu@dragos.uucp (dragos) (11/21/89)

In article <159@norsat.UUCP> dbin@norsat.UUCP (Dave Binette) writes:
>The consensus for the question "Anything faster than stat(S)?"
>is "No"
>
>Now for plan "B" (ignore me if I am trying your patience)
>
>"Is there anything faster than opendir(S), and readdir(S)"
>I can't and don't want to use  ftw(S)

Speaking of which, does anyone have any knowledge of the status of FTW ?
I've been tempted to try reverse engineering the routines from the Usenix
paper for my "quaint" SysV.2 system.

I'm only interested in the routines, not the complex find language they
describe. I'd just like to re-implement du with it and hopefully turn
a hideous process into only painful. Is there any chance Bell Labs will
release the code for it, or am I dreaming ?

E-mail please and I'll post a summary if enough people are interested.

-- 
Dragos Ruiu (ruiu@dragos.uucp)     All system administrators should hand out   
alberta!dragos!ruiu                a bottle of valium with every news-reader
uunet!myrias!dr                    man page.

gwyn@smoke.BRL.MIL (Doug Gwyn) (11/24/89)

In article <1989Nov21.070322.6352@dragos.uucp> ruiu@dragos.UUCP (dragos) writes:
>Speaking of which, does anyone have any knowledge of the status of FTW ?
>I've been tempted to try reverse engineering the routines from the Usenix
>paper for my "quaint" SysV.2 system.

I was going to do that and place it in the public domain, only to find
out that IEEE 1003 has been working on specs for a similar but
different facility.  I decided to wait for the dust to settle.

djm@eng.umd.edu (David J. MacKenzie) (11/24/89)

In article <11676@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>In article <1989Nov21.070322.6352@dragos.uucp> ruiu@dragos.UUCP (dragos) writes:
>>Speaking of which, does anyone have any knowledge of the status of FTW ?
>>I've been tempted to try reverse engineering the routines from the Usenix
>>paper for my "quaint" SysV.2 system.
>
>I was going to do that and place it in the public domain, only to find
>out that IEEE 1003 has been working on specs for a similar but
>different facility.  I decided to wait for the dust to settle.

Rich Salz has already written a PD version of FTW, and posted it:

Path: uunet!husc6!hao!oddjob!gargoyle!ihnp4!cbosgd!mandrill!hal!ncoast!allbery
From: rsalz@pebbles.bbn.com
Newsgroups: comp.sources.misc
Subject: scandir, ftw REDUX
Message-ID: <6943@ncoast.UUCP>
Date: 1 Jan 88 00:47:01 GMT
Sender: allbery@ncoast.UUCP
Lines: 505
Approved: allbery@ncoast.UUCP
X-Archive: comp.sources.misc/8712/15

Forget my previous message -- I just decided for completeness's sake to
implement the SysV ftw(3) routine, too.  

To repeat, these are public-domain implementations of the SystemV ftw()
routine, the BSD scandir() and alphasort() routines, and documentation for
same.  The FTW manpage could be more readable, but so it goes.

Anyhow, feel free to post these, and incorporate them into your existing
packages.  I have readdir() routiens for MSDOS and the Amiga if anyone
wants them, and should have them for VMS by the end of January; let me
know if you want copies.

Yours in filesystems,
	/r$

Anyhow, feel free to post
--
David J. MacKenzie <djm@eng.umd.edu>

djm@eng.umd.edu (David J. MacKenzie) (11/26/89)

In article <1989Nov21.070322.6352@dragos.uucp> ruiu@dragos.UUCP (dragos) writes:
>Speaking of which, does anyone have any knowledge of the status of FTW ?
>I've been tempted to try reverse engineering the routines from the Usenix
>paper for my "quaint" SysV.2 system.

This time, I'm including the (PD) source code (repacked) along with the
header, as it seems that uunet has taken it offline.
-----
Path: uunet!husc6!hao!oddjob!gargoyle!ihnp4!cbosgd!mandrill!hal!ncoast!allbery
From: rsalz@pebbles.bbn.com
Newsgroups: comp.sources.misc
Subject: scandir, ftw REDUX
Message-ID: <6943@ncoast.UUCP>
Date: 1 Jan 88 00:47:01 GMT
Sender: allbery@ncoast.UUCP
Lines: 505
Approved: allbery@ncoast.UUCP
X-Archive: comp.sources.misc/8712/15

Forget my previous message -- I just decided for completeness's sake to
implement the SysV ftw(3) routine, too.  

To repeat, these are public-domain implementations of the SystemV ftw()
routine, the BSD scandir() and alphasort() routines, and documentation for
same.  The FTW manpage could be more readable, but so it goes.

Anyhow, feel free to post these, and incorporate them into your existing
packages.  I have readdir() routiens for MSDOS and the Amiga if anyone
wants them, and should have them for VMS by the end of January; let me
know if you want copies.

Yours in filesystems,
	/r$

Anyhow, feel free to post

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  alphasort.c ftw.3 ftw.c ftw.h scandir.3 scandir.c
# Wrapped by djm@abyss on Sat Nov 25 18:23:54 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'alphasort.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'alphasort.c'\"
else
echo shar: Extracting \"'alphasort.c'\" \(381 characters\)
sed "s/^X//" >'alphasort.c' <<'END_OF_FILE'
X/*
X**  ALPHASORT
X**  Trivial sorting predicate for scandir; puts entries in alphabetical order.
X*/
X
X#include <sys/types.h>
X#include <sys/dir.h>
X
X#ifdef	RCSID
Xstatic char RCS[] = "$Header: alphasort.c,v 1.1 87/12/29 21:35:59 rsalz Exp $";
X#endif	/* RCSID */
X
Xint
Xalphasort(d1, d2)
X    struct direct	**d1;
X    struct direct	**d2;
X{
X    return strcmp(d1[0]->d_name, d2[0]->d_name);
X}
END_OF_FILE
if test 381 -ne `wc -c <'alphasort.c'`; then
    echo shar: \"'alphasort.c'\" unpacked with wrong size!
fi
# end of 'alphasort.c'
fi
if test -f 'ftw.3' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ftw.3'\"
else
echo shar: Extracting \"'ftw.3'\" \(2139 characters\)
sed "s/^X//" >'ftw.3' <<'END_OF_FILE'
X.TH FTW 3
X.\" $Header: ftw.3,v 1.1 87/12/29 21:34:29 rsalz Exp $
X.SH NAME
Xftw \- walk a file tree
X.SH SYNOPSIS
X.ft B
X.nf
X#include <ftw.h>
X
Xint
Xftw(directory, funcptr, depth)
X	char *directory;
X	int (*funcptr)();
X	int depth;
X
X#include <sys/stat.h>
X
Xint
Xfuncptr(item, sb, flag)
X	char *item;
X	struct stat *sb;
X	int flag;
X.fi
X.ft R
X.SH DESCRIPTION
X.I Ftw
Xwalks through the directory tree starting from the indicated
X.IR path .
XFor every entry it finds in the tree, it calls the user-supplied
X.I funcptr
Xwith the calling sequence given in the synopsis above.
XThe first argument is the full pathname of the entry (rooted from
Xthe
X.I directory
Xparameter given to
X.IR ftw );
Xthe second argument is a pointer to the
X.IR stat (2)
Xstructure for the entry;
Xand the third argument is one of the #define's in the header file.
XThis value will be one of the following:
X.RS
X.ta \w'FTW_DNR  'u
X.nf
XFTW_F	Item is a normal file
XFTW_D	Item is a directory
XFTW_NS	The stat failed on the item
XFTW_DNR	Item is a directory which can't be read
X.fi
X.RE
XNote, however, that FTW_F is a misnomer; anything other than directories
Xare (e.g., symbolic links) get the FTW_F tag.
X.PP
X.I Ftw
Xrecursively calls itself when it encounters a directory.
XTo avoid using up all a program's file descriptors, the
X.I depth
Xargument specifies the number of simultaneous open directories to maintain.
XWhen the depth is exceeded, the routine will become noticeably
Xslower because directories are closed in ``most-recently-used'' order.
X.PP
XTo stop the tree walk, the user-supplied function should return a
Xnon\-zero value; this value will become the return value of
X.IR ftw .
XOtherwise,
X.I ftw
Xwill continue until it has scanned the entire tree, in which case it will
Xreturn zero, or until it hits an error such as a 
X.IR malloc (3)
Xfailure, in which case it will return \-1.
X.PP
XBecause
X.I ftw
Xuses dynamic data structures, the only safe way to exit out of a tree
Xwalk is to return a non-zero value.
XTo handle interrupts, for example, mark that the interrupt occured
Xand return a non\-zero value\(em don't use
X.I longjmp (3)
Xunless the program is going to terminate.
X.SH SEE ALSO
Xstat(2)
END_OF_FILE
if test 2139 -ne `wc -c <'ftw.3'`; then
    echo shar: \"'ftw.3'\" unpacked with wrong size!
fi
# end of 'ftw.3'
fi
if test -f 'ftw.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ftw.c'\"
else
echo shar: Extracting \"'ftw.c'\" \(2487 characters\)
sed "s/^X//" >'ftw.c' <<'END_OF_FILE'
X/*
X**  FTW
X**  Walk a directory hierarchy from a given point, calling a user-supplied
X**  function at each thing we find.  If we go below a specified depth,
X**  recycle file descriptors.
X*/
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X#include <ftw.h>
X
X#ifdef	RCSID
Xstatic char RCS[] = "$Header: ftw.c,v 1.1 87/12/29 21:38:52 rsalz Exp $";
X#endif	/* RCSID */
X
X#define EQ(a, b)	(strcmp((a), (b)) == 0)
X
Xextern char		*malloc();
Xextern char		*strcpy();
X
Xint
Xftw(directory, funcptr, depth)
X    char		 *directory;
X    int			(*funcptr)();
X    int			  depth;
X{
X    register DIR	 *dirp;
X    struct direct	 *entp;
X    struct stat		  stats;
X    register char	 *p;
X    register int	  i;
X    long		  seekpoint;
X    char		 *fullpath;
X
X    /* If can't stat, tell the user so. */
X    if (stat(directory, &stats) < 0)
X	return (*funcptr)(directory, &stats, FTW_NS);
X
X    /* If it's not a directory, call the user's function. */
X    if ((stats.st_mode & S_IFMT) != S_IFDIR)
X	/* Saying "FTW_F" here is lying; what if this is a symlink? */
X	return (*funcptr)(directory, &stats, FTW_F);
X
X    /* Open directory; if we can't, tell the user so. */
X    dirp = opendir(directory);
X    if (dirp == NULL)
X	return (*funcptr)(directory, &stats, FTW_DNR);
X
X    /* See if user wants to go further. */
X    i = (*funcptr)(directory, &stats, FTW_D);
X    if (i) {
X	closedir(dirp);
X	return i;
X    }
X
X    /* Get ready to hold the full paths. */
X    i = strlen(directory);
X    fullpath = malloc(i + 1 + MAXNAMLEN + 1);
X    if (fullpath == NULL) {
X	closedir(dirp);
X	return -1;
X    }
X    (void)strcpy(fullpath, directory);
X    p = &fullpath[i];
X    if (i && p[-1] != '/')
X	*p++ = '/';
X
X    /* Read all entries in the directory.. */
X    while (entp = readdir(dirp))
X	if (!EQ(entp->d_name, ".") && !EQ(entp->d_name, "..")) {
X	    if (depth <= 1) {
X		/* Going too deep; checkpoint and close this directory. */
X		seekpoint = telldir(dirp);
X		closedir(dirp);
X		dirp = NULL;
X	    }
X
X	    /* Process the file. */
X	    (void)strcpy(p, entp->d_name);
X	    i = ftw(fullpath, funcptr, depth - 1);
X	    if (i) {
X		/* User's finished; clean up. */
X		free(fullpath);
X		if (dirp)
X		    closedir(dirp);
X		return i;
X	    }
X
X	    /* Reopen the directory if necessary. */
X	    if (dirp == NULL) {
X		dirp = opendir(directory);
X		if (dirp == NULL) {
X		    free(fullpath);
X		    return -1;
X		}
X		seekdir(dirp, seekpoint);
X	    }
X	}
X
X    /* Clean up. */
X    free(fullpath);
X    closedir(dirp);
X    return 0;
X}
END_OF_FILE
if test 2487 -ne `wc -c <'ftw.c'`; then
    echo shar: \"'ftw.c'\" unpacked with wrong size!
fi
# end of 'ftw.c'
fi
if test -f 'ftw.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ftw.h'\"
else
echo shar: Extracting \"'ftw.h'\" \(358 characters\)
sed "s/^X//" >'ftw.h' <<'END_OF_FILE'
X/*
X**  <FTW.H>
X**  Header values for the third parameter to the user-supplied function
X**  for ftw().
X**
X**  $Header: ftw.h,v 1.1 87/12/29 21:34:34 rsalz Exp $
X*/
X
X#define FTW_NS		100	/* Something stat(2) failed on		*/
X#define FTW_DNR		200	/* Something opendir(3) failed on	*/
X#define FTW_F		300	/* A normal file			*/
X#define FTW_D		400	/* A directory				*/
END_OF_FILE
if test 358 -ne `wc -c <'ftw.h'`; then
    echo shar: \"'ftw.h'\" unpacked with wrong size!
fi
# end of 'ftw.h'
fi
if test -f 'scandir.3' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'scandir.3'\"
else
echo shar: Extracting \"'scandir.3'\" \(2350 characters\)
sed "s/^X//" >'scandir.3' <<'END_OF_FILE'
X.TH SCANDIR 3
X.\" $Header: scandir.3,v 1.1 87/12/29 21:35:54 rsalz Exp $
X.SH NAME
Xscandir, alphasort \- scan a directory
X.SH SYNOPSIS
X.nf
X.ft B
X#include <sys/types.h>
X#include <sys/dir.h>
X
Xint
Xscandir(name, list, selector, sorter)
X.in +4n
Xchar *name;
Xstruct direct ***list;
Xint (*selector)();
Xint (*sorter)();
X.in -4n
X
Xint
Xalphasort(d1, d2)
X.in +4n
Xstruct direct **d1;
Xstruct direct **d2;
X.in -4n
X.ft R
X.fi
X.SH DESCRIPTION
X.I Scandir
Xreads the directory
X.I name
Xand builds a NULL\-terminated array of pointers to the entries found
Xin that directory.
XThis array is put into the location pointed to by the
X.I list
Xparameter.
X.PP
XIf the
X.I selector
Xparameter is non\-NULL, it is taken to be a pointer to a function called
Xwith each entry, to determine whether or not it should be included in
Xthe returned list.
XIf the parameter is NULL, all entries are included.
X.PP
XAs an added feature, the entries can be sorted (with
X.IR qsort (3))
Xbefore the list is returned.
XIf the
X.I sorter
Xparameter is non\-NULL, it is passed to qsort to use as the comparison
Xfunction.
XThe
X.I alphasort
Xroutine is provided to sort the array alphabetically.
X.PP
XThe array pointed to by
X.I list
Xand the items it points to are all space obtained through
X.IR malloc (3),
Xand their storage can be reclaimed as shown in the example below.
X.SH "EXAMPLE"
XHere is a small
X.IR ls (1)\-like
Xprogram:
X.ne 50
X.RS
X.nf
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X
Xextern int alphasort();
X
Xstatic int
Xfilesonly(e)
X	struct direct *e;
X{
X	struct stat sb;
X
X	return(stat(e->d_name, &sb) >= 0 && (sb.st_mode & S_IFMT) == S_IFREG);
X}
X
Xmain(ac, av)
X	int ac;
X	char *av[];
X{
X	register int i;
X	register int j;
X	struct direct **list;
X
X	if (ac != 2) {
X		fprintf(stderr, "usage: %s dirname\n", av[0]);
X		exit(1);
X	}
X	if (chdir(av[1]) < 0) {
X		perror(av[1]);
X		exit(1);
X	}
X	if ((i = scandir(".", &list, filesonly, alphasort)) < 0) {
X		perror("Error reading directory");
X		exit(1);
X	}
X	for (j = 0; j < i; j++)
X		printf("%s\n", list[j]->d_name);
X	for (j = 0; j < i; j++)
X		free((char *)list[j]);
X	free((char *)list);
X	exit(0);
X}
X.fi
X.RE
X.SH "SEE ALSO"
Xdirectory(3), qsort(3)
X.SH DIAGNOSTICS
XReturns the number of entries in the ``list,'' or \-1 if the directory
Xcould not be opened or a memory allocation failed.
X.SH BUGS
XThe routine can be slightly wasteful of space.
END_OF_FILE
if test 2350 -ne `wc -c <'scandir.3'`; then
    echo shar: \"'scandir.3'\" unpacked with wrong size!
fi
# end of 'scandir.3'
fi
if test -f 'scandir.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'scandir.c'\"
else
echo shar: Extracting \"'scandir.c'\" \(1827 characters\)
sed "s/^X//" >'scandir.c' <<'END_OF_FILE'
X/*
X**  SCANDIR
X**  Scan a directory, collecting all (selected) items into a an array.
X*/
X
X#include <sys/types.h>
X#include <sys/dir.h>
X
X#ifdef	RCSID
Xstatic char RCS[] = "$Header: scandir.c,v 1.1 87/12/29 21:35:56 rsalz Exp $";
X#endif	/* RCSID */
X
X/* Initial guess at directory size. */
X#define INITIAL_SIZE	20
X
Xextern char		*malloc();
Xextern char		*realloc();
Xextern char		*strcpy();
X
Xint
Xscandir(name, list, selector, sorter)
X    char		  *name;
X    struct direct	***list;
X    int			 (*selector)();
X    int			 (*sorter)();
X{
X    register struct direct	 **names;
X    register struct direct	  *entp;
X    register DIR	  *dirp;
X    register int	   i;
X    register int	   size;
X
X    /* Get initial list space and open directory. */
X    size = INITIAL_SIZE;
X    names = (struct direct **)malloc(size * sizeof names[0]);
X    if (names == NULL)
X	return -1;
X    dirp = opendir(name);
X    if (dirp == NULL)
X	return -1;
X
X    /* Read entries in the directory. */
X    for (i = 0; entp = readdir(dirp); )
X	if (selector == NULL || (*selector)(entp)) {
X	    /* User wants them all, or he wants this one. */
X	    if (++i >= size) {
X		size <<= 1;
X		names = (struct direct **)
X		    realloc((char *)names, size * sizeof names[0]);
X		if (names == NULL) {
X		    closedir(dirp);
X		    return -1;
X		}
X	    }
X
X	    /* Copy the entry. */
X	    names[i - 1] = (struct direct *)malloc(DIRSIZ(entp));
X	    if (names[i - 1] == NULL) {
X		closedir(dirp);
X		return -1;
X	    }
X	    names[i - 1]->d_ino = entp->d_ino;
X	    names[i - 1]->d_reclen = entp->d_reclen;
X	    names[i - 1]->d_namlen = entp->d_namlen;
X	    (void)strcpy(names[i - 1]->d_name, entp->d_name);
X	}
X
X    /* Close things off. */
X    names[i] = NULL;
X    *list = names;
X    closedir(dirp);
X
X    /* Sort? */
X    if (i && sorter)
X	qsort((char *)names, i, sizeof names[0], sorter);
X
X    return i;
X}
END_OF_FILE
if test 1827 -ne `wc -c <'scandir.c'`; then
    echo shar: \"'scandir.c'\" unpacked with wrong size!
fi
# end of 'scandir.c'
fi
echo shar: End of shell archive.
exit 0
--
David J. MacKenzie <djm@eng.umd.edu>

lee@sq.sq.com (Liam R. E. Quin) (12/01/89)

djm@eng.umd.edu (David J. MacKenzie) writes:
> gwyn@brl.arpa (Doug Gwyn) writes:
>> ruiu@dragos.UUCP (dragos) writes:
>>>Speaking of which, does anyone have any knowledge of the status of FTW ?
>>>I've been tempted to try reverse engineering the routines from the Usenix
>>>paper for my "quaint" SysV.2 system.

The original message has vanished, but to the person who wanted something
faster than readdir()/clsedir(), the vversions of ftw() I have seen do
themselves use the ndir readdir() and closedir() stuff, so they are
certainly no faster.

On a reasonably recent System V system, ftw can be very fast.
For example, on my 16MHz 386/ix machine at home I was able to do a
	find / -print > /dev/null
in well under 20 seconds, with a second run producing no disk accesses
at all, as everything was in the cache.
I had over 250 MBytes' worth of data in over 50,000 files, so that is not
too bad (the amount of data being less significant than the number of
files, of course!).


One thing to do is to have a directory daemon -- you give it a directory,
and it returns all of the sub directory and file names marked as such.
This isn't too hard with messages, for example, and has the advantage that
while one process is processing (e.g. printing the file names), the other
can be doing stat() on them.
This might be part of the motivation for the
	find /dir -print | cpio -lots -of -options > /dev/ice
paradigm -- I don't know.

Some database systems (e.g. Oracle) have a read-ahead daemon that fetches
the next block in (for example) a linked list.  In many cases (not sure about
Oracle here) all it needs to do is read it -- this puts it in the Unix
buffer cache for a few seconds, long enough for the database client to
use it without Unix having to re-read it from disk.


The trouble with doing this for find(1)-like program is that it can be hard
to tell how effective it is in "real-life" situations, but there are cases
where it can be a real win.

Finally, if you are really in need of speed, you could consider keeping a
btree of filenames and paths.  You only need to check that the directory
has not altered to determine that it has no new, lost or renamed children,
so you can simply keep a time-since-last-changed.
Now you can do better than one stat per file, because you only have to
check each file once when building the database and each directory (not
file) again later.
I don't know how to make find(1) or ftw(3) much faster than this, and
this at at a considerable cost in complexity.

Lee
-- 
Liam R. Quin, Unixsys (UK) Ltd [note: not an employee of "sq" - a visitor!]
lee@sq.com (Whilst visiting Canada from England, until Christmas)
utai!anduk.uucp!lee (after Christmas)
 ...striving to promote the interproduction of epimorphistic conformability

joey@tessi.UUCP (Joe Pruett) (12/09/89)

A quick hack I've used in a directory traversal program is to use the
link count of . to determine how many subdirectories there are.  If .
has no subdirectories, then you don't have to stat every file name.
Since most of the files tend to be at the end of the tree, you really
save time because none of them get stat'ed.  Here's the program I use
instead of find . -print.  It puts a / at the end of directory names
so you can use egrep '/$' or the like.  It also takes a switch of
-d for depth first printout, or -h for "heighth" first printout.
You can replace the echo function with something else if you
want a canned program, but this combined with an egrep and xargs
fixes most of the problems you want to solve for a directory
tree.  Some quick timings on a sun 3/260 with local disk show:

find . -print > /dev/null			0.4u 3.3s 0:16
descend . > /dev/null				0.4u 1.8s 0:02

With 1485 files (195 of which are directories).

--------

#include <stdio.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <strings.h>

int numErrors;
int depth = 1;

int
echo(s)
char *s;
{
	printf("%s\n", s);
}

void
descend(name, command)
char *name;
int (*command)();
{
	struct stat stb;
	DIR *dirp;
	struct direct *dp;
	int ndirs;
	char buf[1024];

	if (lstat(name, &stb) != 0) {
		perror(name);
		numErrors++;
		return;
	}
	if ((stb.st_mode & S_IFDIR) == 0) {
		(void)strcpy(buf, name);
		(*command)(buf);
		return;
	}
	ndirs = stb.st_nlink - 2;
	if ((dirp = opendir(name)) == NULL) {
		perror(name);
		numErrors++;
		return;
	}
	if (depth == 0) {
		(void)strcpy(buf, name);
		(void)strcat(buf, "/");
		(*command)(buf);
	}
	for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
		if (strcmp(dp->d_name, ".") == 0
		|| strcmp(dp->d_name, "..") == 0) {
			continue;
		}
		(void)strcpy(buf, name);
		(void)strcat(buf, "/");
		(void)strcat(buf, dp->d_name);
		if (ndirs) {
			if (lstat(buf, &stb) != 0) {
				perror(buf);
				numErrors++;
				continue;
			}
			if ((stb.st_mode & S_IFDIR) != 0) {
				ndirs--;
				descend(buf, command);
			} else {
				(*command)(buf);
			}
		} else {
			(*command)(buf);
		}
	}
	if (depth == 1) {
		(void)strcpy(buf, name);
		(void)strcat(buf, "/");
		(*command)(buf);
	}
	closedir(dirp);
}

main(argc, argv)
int argc;
char **argv;
{
	argc--;
	argv++;
	while (argc) {
		if ((*argv)[0] == '-') {
			switch ((*argv)[1]) {
			case 'd':
				depth = 1;
				break;
			case 'h':
				depth = 0;
				break;
			}
		} else {
			descend(*argv, echo);
		}
		argc--;
		argv++;
	}
	exit (numErrors);
}

tony@oha.UUCP (Tony Olekshy) (12/16/89)

In message <564@balthmus.tessi.UUCP>, joey@tessi.UUCP (Joe Pruett) writes:
>
> A quick hack I've used in a directory traversal program is to use the
> link count of . to determine how many subdirectories there are.  If .
> has no subdirectories, then you don't have to stat every file name.

This is great.
It's three times faster than find -print on my news spool directory.
I suggest you all grab <564@balthmus.tessi.UUCP> and keep it handy.

--
Yours, etc., Tony Olekshy (...!alberta!oha!tony or tony@oha.UUCP).

m5@lynx.uucp (Mike McNally) (12/19/89)

tony@oha.UUCP (Tony Olekshy) writes:

>In message <564@balthmus.tessi.UUCP>, joey@tessi.UUCP (Joe Pruett) writes:
>>
>> A quick hack I've used in a directory traversal program is to use the
>> link count of . to determine how many subdirectories there are.  If .
>> has no subdirectories, then you don't have to stat every file name.

>This is great.
>It's three times faster . . .

I'm going to give this a try in a couple of our utilities.  Of course, if
the tree traversal is supposed to follow symbolic links, you're stuck with
the lstats.  Most of the time it's not a problem.






-- 
Mike McNally                                    Lynx Real-Time Systems
uucp: {voder,athsys}!lynx!m5                    phone: 408 370 2233

            Where equal mind and contest equal, go.