rsalz@pebbles.bbn.com (01/01/88)
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 rsalz@fig.bbn.com on Tue Dec 29 21:39:01 1987 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'\" \(425 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#include <sys/types.h> X#include <sys/dir.h> X#ifdef RCSID Xstatic char RCS[] = "$Header: alphasort.c,v 1.1 87/12/29 21:35:59 rsalz Exp $"; X#endif /* RCSID */ X X/* A convenient shorthand. */ Xtypedef struct direct ENTRY; X Xint Xalphasort(d1, d2) X ENTRY **d1; X ENTRY **d2; X{ X return(strcmp(d1[0]->d_name, d2[0]->d_name)); X} END_OF_FILE if test 425 -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'\" \(2455 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#include <stdio.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/dir.h> X#include <ftw.h> 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/* Handy shorthand. */ X#define EQ(a, b) (strcmp((a), (b)) == 0) X X/* Linked in later. */ Xextern char *malloc(); Xextern char *strcpy(); X X Xint Xftw(directory, funcptr, depth) X char *directory; X int (*funcptr)(); X int depth; X{ X register DIR *Dp; X register char *p; X register int i; X struct direct *E; X struct stat Sb; X long seekpoint; X char *fullpath; X X /* If can't stat, tell the user so. */ X if (stat(directory, &Sb) < 0) X return((*funcptr)(directory, &Sb, FTW_NS)); X X /* If it's not a directory, call the user's function. */ X if ((Sb.st_mode & S_IFMT) != S_IFDIR) X /* Saying "FTW_F" here is lying, what if this is a symlink? */ X return((*funcptr)(directory, &Sb, FTW_F)); X X /* Open directory; and if we can't tell the user so. */ X if ((Dp = opendir(directory)) == NULL) X return((*funcptr)(directory, &Sb, FTW_DNR)); X X /* See if user wants to go further. */ X if (i = (*funcptr)(directory, &Sb, FTW_D)) { X closedir(Dp); X return(i); X } X X /* Get ready to hold the full paths. */ X i = strlen(directory); X if ((fullpath = malloc(i + 1 + MAXNAMLEN + 1)) == NULL) { X closedir(Dp); 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 (E = readdir(Dp)) X if (!EQ(E->d_name, ".") && !EQ(E->d_name, "..")) { X if (depth <= 1) { X /* Going too deep; checkpoint and close this directory. */ X seekpoint = telldir(Dp); X closedir(Dp); X Dp = NULL; X } X X /* Process the file. */ X (void)strcpy(p, E->d_name); X if (i = ftw(fullpath, funcptr, depth - 1)) { X /* User's finished; clean up. */ X free(fullpath); X if (Dp) X closedir(Dp); X return(i); X } X X /* Reopen the directory if necessary. */ X if (Dp == NULL) { X if ((Dp = opendir(directory)) == NULL) { X /* WTF? */ X free(fullpath); X return(-1); X } X seekdir(Dp, seekpoint); X } X } X X /* Clean up. */ X free(fullpath); X closedir(Dp); X return(0); X} END_OF_FILE if test 2455 -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'\" \(1777 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#include <sys/types.h> X#include <sys/dir.h> 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 X/* A convenient shorthand. */ Xtypedef struct direct ENTRY; X X/* Linked in later. */ Xextern char *malloc(); Xextern char *realloc(); Xextern char *strcpy(); X X Xint Xscandir(Name, List, Selector, Sorter) X char *Name; X ENTRY ***List; X int (*Selector)(); X int (*Sorter)(); X{ X register ENTRY **names; X register ENTRY *E; X register DIR *Dp; X register int i; X register int size; X X /* Get initial list space and open directory. */ X size = INITIAL_SIZE; X if ((names = (ENTRY **)malloc(size * sizeof names[0])) == NULL X || (Dp = opendir(Name)) == NULL) X return(-1); X X /* Read entries in the directory. */ X for (i = 0; E = readdir(Dp); ) X if (Selector == NULL || (*Selector)(E)) { X /* User wants them all, or he wants this one. */ X if (++i >= size) { X size <<= 1; X names = (ENTRY **)realloc((char *)names, size * sizeof names[0]); X if (names == NULL) { X closedir(Dp); X return(-1); X } X } X X /* Copy the entry. */ X if ((names[i - 1] = (ENTRY *)malloc(DIRSIZ(E))) == NULL) { X closedir(Dp); X return(-1); X } X names[i - 1]->d_ino = E->d_ino; X names[i - 1]->d_reclen = E->d_reclen; X names[i - 1]->d_namlen = E->d_namlen; X (void)strcpy(names[i - 1]->d_name, E->d_name); X } X X /* Close things off. */ X names[i] = NULL; X *List = names; X closedir(Dp); 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 1777 -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