v09i085: dtree
David J. MacKenzie
djm at abyss.eng.umd.edu
Thu Dec 21 12:12:21 AEST 1989
Posting-number: Volume 9, Issue 85
Submitted-by: djm at abyss.eng.umd.edu (David J. MacKenzie)
Archive-name: dtree
This is a new revision of the dtree Unix directory tree printing
program. Its output has a different, and in my opinion more
attractive, format from that of the vtree program that appeared in
comp.sources.unix volume 18.
This version of dtree should work on all Unixes from version 7 onward,
with some tweaking of the Makefile.
#! /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: README dtree.1 dtree.c getcwd.c Makefile
# Wrapped by djm at abyss on Wed Dec 20 15:14:15 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(1014 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
Xdtree pretty-prints directory structures like this:
X
X |-df---------
X |-ftw--------
X |
X | |-bin-src-
X | |-date----
X |-gnu--------|-install-
X | |-lib-----
X|-src-| |-libc----
X | |-tput----
X |-misc-------
X |-patches----
X |-printf-----
X |-qterm------
X
XIt was originally written for v7 and 4.1BSD; a version that had been
Xpartially adapted to the Berkeley Fast File System and portable
Xdirectory package appeared in mod.sources several years ago, but it
Xhad several bugs (among the more severe, it always dumps core on some
Xsystems). This version fixes those and also has improved performance
Xand more helpful error messages. It also works on System V; for the
Xbenefit of System V users, I have packaged with dtree Doug Gwyn's free
Xreimplementation of the getcwd function that doesn't open a pipe to
X/bin/pwd but determines the current directory directly; this should
Xhelp performance on those systems.
END_OF_FILE
if test 1014 -ne `wc -c <'README'`; then
echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'dtree.1' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'dtree.1'\"
else
echo shar: Extracting \"'dtree.1'\" \(2365 characters\)
sed "s/^X//" >'dtree.1' <<'END_OF_FILE'
X.TH DTREE 1L
X.SH NAME
Xdtree \- display directory tree structures
X.SH SYNOPSIS
X.B dtree
X[
X.I \-adfgHlnpsvx
X] [
X.I \-c linelength
X] [
X.BR directory ...
X]
X.SH DESCRIPTION
X.I dtree
Xdisplays a graphic representation of the directory structure of each given
X.B directory
Xand its children. If no directories are specified, the current
Xdirectory is used.
XBy default, only
Xdirectories, not regular files, are shown, and only their
Xfilenames are given. Various options add additional
Xinformation to the tree.
X.SS OPTIONS
X.TP
X.I \-a
XInclude files in the listing (excluding entries beginning with '.').
X.TP
X.I \-c linelength
XMake
X.I linelength
Xthe length of each
Xcolumn of the printout. By default, this is 14.
XAny entries longer than
Xthe column length are truncated accordingly, and the last character that
Xfits into the column is replaced by an asterisk.
XThis option only has an effect if the
X.I -v
Xoption is specified.
X.TP
X.I \-d
XList directories first. For each directory, its subdirectories
Xwill be listed first, and then all of its other entries.
X.TP
X.I \-f
XList files first. The reverse of
X.IR \-d .
X.TP
X.I \-l
XLong listing. Display useful information to the right of
Xeach entry: the name of the file's owner, its size in blocks, and its mode.
X.TP
X.I \-g
XSame as the
X.I \-l
Xoption, except that the group name is used instead of
Xthe owner name. If both the
X.I \-l
Xand
X.I \-g
Xoptions are used, both the
Xowner and group will be displayed.
X.TP
X.I \-H
XDisplay a header at the top of the printout that gives the time and
Xdate that the printout was made and a summary of the type of
Xinformation contained in the tree.
X.TP
X.I \-n
XNo sort. Entries are listed in the order they are read
Xfrom the directories.
X.TP
X.I \-p
XInclude entries beginning with '.' (except '.' and '..').
X.TP
X.I \-s
XSimplify the long listing: display the user id, size in blocks, and
Xoctal mode of the file. This option implies the
X.I \-l
Xoption unless the
X.I \-g
Xoption is specified.
X.TP
X.I \-v
XDo not let column lengths vary; use the same
Xwidth for each column of output. The width defaults to 14
Xbut can be set with the
X.I \-c
Xoption.
X.TP
X.I \-x
XDo not cross file systems.
X.I dtree
Xwill not cross over to a
Xsubdirectory if it is on a different file system.
X.SH AUTHOR
XDave Borman, Digital Unix Engineering Group
X.br
Xdecvax!borman
X.br
XOriginally written at St. Olaf College, Northfield, MN.
END_OF_FILE
if test 2365 -ne `wc -c <'dtree.1'`; then
echo shar: \"'dtree.1'\" unpacked with wrong size!
fi
# end of 'dtree.1'
fi
if test -f 'dtree.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'dtree.c'\"
else
echo shar: Extracting \"'dtree.c'\" \(21819 characters\)
sed "s/^X//" >'dtree.c' <<'END_OF_FILE'
X/*
X * DTREE - Print the tree structure of a directory
X * 4/7/83 name was changed from TREE to DTREE
X * 9/7/83 mods for 4.1c and 4.2 dirctory structure added
X *
X * Dave Borman, Digital Unix Engineering Group
X * decvax!borman
X * Originally written at St. Olaf College, Northfield MN.
X * Copyright (c) 1983 by Dave Borman
X * All rights reserved
X * This program may not be sold, but may be distributed
X * provided this header is included.
X *
X * Usage: dtree [-adfgHlnpsvx] [-c line-length] [directory...]
X * Flags: -a) include non-directory entries in listing
X * -d) sort tree with directories at the top
X * -f) sort tree with files at the top
X * -g) same as l, but use group name instead of user name
X * -H) display a header at top
X * -l) print stats with each listing
X * if both g & l flags are given, both owner and
X * group will be printed
X * -n) do not sort the tree
X * -p) include files starting with a '.' (except "." & "..")
X * -s) use shorter stats. Implies -l if -g isn't given.
X * -v) variable length columns off
X * -x) do not cross mounted file systems.
X * -c length) set max column length to "length"
X */
X
X /* Modified by Ed Arnold CSU-CS (csu-cs!arnold) 3-5-84
X *
X * Allows symbolic links to both directories and individual files.
X * With a '-l' or '-al' option, links are denoted with a 'l' in front of
X * file or directory permissions. In all other instances both links to
X * directories and files are represented just as files are. Contents of
X * linked directories are not printed due to the possibility of
X * recursively linked directories.
X *
X * Big directory name option added by:
X * Mike Vevea CSU-CS (csu-cs!vevea) 3-22-84
X *
X * Toggle sense of -v (running 4.2), and eliminate some extraneous
X * print info Mike Meyer Energy Analysts (mwm at ea) 4/17/84
X *
X * Fix the exit status to correctly indicate what happened.
X * Mike Meyer Energy Analysts (mwm at ea) 4/23/84
X *
X * Change MAX to INITIAL_ELEM to fix conflict on Suns,
X * fix incorrect default value for Clength when -v not given,
X * add -H option to print header with date and description of tree,
X * remove -b option (useless) and simplify array access,
X * use getopt to parse options, fix usage message,
X * use getwd instead of opening a pipe to pwd,
X * make error messages more informative,
X * use strncpy instead of sprintf for speed,
X * move function declarations to top of file,
X * comment out junk after #else and #endif,
X * make symbolic link configuring automatic,
X * check all error returns from malloc and realloc,
X * add System V/Xenix/Unos compatibility,
X * remove definition of rindex.
X * David MacKenzie <djm at eng.umd.edu> 12/20/89
X */
X
X/* Compile-time options:
X *
X * STATS leave undefined to remove stats, giving more core space
X * and thus the ability to tree larger tree structures on PDP 11/70s.
X *
X * NEWDIR directory structure a la Berkeley 4.1c or 4.2
X *
X * NDIR use <sys/ndir.h> instead of <sys/dir.h>
X * NEWDIR must be defined as well.
X *
X * DIRENT use Posix directory library.
X * NEWDIR must be defined as well.
X *
X * SYSV use getcwd instead of getwd, strrchr instead of rindex.
X */
X
Xstatic char Sccsid[]="@(#)dtree.c 2.3 2/14/84";
X
X#ifdef S_IFLNK
Xstatic char Rcsid[] ="$Header: /mnt/ntape/RCS/dtree.c,v 1.2 84/04/23 10:33:41 root Exp $";
X#endif /* S_IFLNK */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <time.h>
X
X#ifdef STATS
X# include <pwd.h>
X# include <grp.h>
X#endif /* STATS */
X
X#ifdef NEWDIR
X# if DIRENT
X# include <dirent.h>
X# define direct dirent
X# else
X# if NDIR
X# include <sys/ndir.h>
X# else
X# include <sys/dir.h>
X# endif
X# endif /* DIRENT */
X#else
X# include <sys/dir.h>
X#endif /* NEWDIR */
X
X/* default column length when -v is given */
X#ifdef unos
X#define DEFCOLWID 30
X#else
X#define DEFCOLWID 14
X#endif
X
X#include <sys/param.h> /* for MAXPATHLEN */
X#ifndef MAXPATHLEN
X# ifdef LPNMAX /* Xenix from stdio.h */
X# define MAXPATHLEN (LPNMAX - 1)
X# else
X# include <limits.h> /* try somewhere else */
X# define MAXPATHLEN PATH_MAX
X# endif
X#endif
X
X#ifndef MAXNAMLEN /* defined with NEWDIR routines */
X# ifdef LFNMAX /* Xenix again */
X# define MAXNAMLEN (LFNMAX - 1)
X# else
X# define MAXNAMLEN DEFCOLWID
X# endif
X#endif
X
X#define DEPTH 10 /* maximum depth that dtree will go */
X#define INITIAL_ELEM 100 /* initial # of elements for list of files */
X
X#define FFIRST 2 /* sort files first */
X#define DFIRST 1 /* sort directories first */
X#define FAIL -1 /* failure return status of sys calls */
X#define GREATER 1 /* return value of strcmp if arg1 > arg2 */
X#define LESSTHAN -1 /* return value of strcmp if arg1 < arg2 */
X#define SAME 0 /* return value of strcmp if arg1 == arg2 */
X
X#ifdef STATS
Xchar *getmode();
Xchar *guid();
Xchar *ggid();
Xstruct passwd *getpwuid();
Xstruct group *getgrgid();
X#endif /* STATS */
X
X#ifdef SYSV
X#define rindex strrchr
X#endif /* SYSV */
X
Xchar *getwd();
Xchar *malloc();
Xchar *realloc();
Xchar *rindex();
Xint qsort();
Xlong time();
X
Xint compar(); /* comparison routine for qsort */
Xchar *xmalloc();
Xchar *xrealloc();
X
X#ifdef SYSV
X#define getwd(buf) getcwd((buf), MAXPATHLEN + 1)
X#endif /* SYSV */
X
Xint Index = 0; /* current element of list[] */
Xint CLength = 0; /* max length of a column */
Xint All = 0; /* all != 0; list non-directory entries */
Xint File_dir = 0; /* flag for how to sort */
Xint Sort = 1; /* flag to cause sorting of entries */
Xint Point = 1; /* skip point files if set */
Xint Header = 0; /* print verbose header */
Xint Maxes[DEPTH]; /* array keeps track of max length in columns */
Xint Level = 0; /* counter for how deep we are */
Xint Device; /* device that we are starting tree on */
Xint Xdev = 1; /* set to allow crossing of devices */
Xint Varspaces = 1; /* set to allow compaction of column width */
X#ifdef STATS
Xint Gflag = 0; /* set for group stats instead of owner */
Xint Longflg = 0; /* set for long listing */
Xint Compact = 0; /* set for shortened long listing */
X#endif /* STATS */
Xstruct stat Status;
X#ifdef S_IFLNK
Xstruct stat Lstat; /* stat of link, if there is one */
X#endif /* S_IFLNK */
X
Xstruct entry {
X int next; /* index to next element in list */
X /* could be a ptr, but realloc() */
X /* might screw us then */
X#ifdef STATS
X off_t e_size; /* size in blocks */
X unsigned short e_mode; /* file mode */
X short e_uid; /* uid of owner */
X short e_gid; /* gid of owner */
X#endif /* STATS */
X unsigned short dir : 1; /* entry is a directory */
X unsigned short last : 1; /* last entry in the dir. */
X unsigned short dev : 1; /* set if same device as top */
X unsigned short end : 13; /* index of last subdir entry*/
X char e_name[MAXNAMLEN + 1]; /* name from directory entry */
X} *List, *SaveList;
X
Xunsigned Size; /* how big of space we've malloced */
X
Xchar *Spaces; /* used for output */
Xchar Buf1[BUFSIZ]; /* buffers for stdio stuff. We don't want */
X#ifndef NEWDIR
Xchar Buf2[BUFSIZ]; /* anyone calling malloc, because then */
X /* realloc() will have to move the whole list */
X#endif
X
Xmain(argc, argv)
Xchar **argv;
Xint argc;
X{
X extern int optind;
X extern char *optarg;
X register int i;
X char top[MAXPATHLEN + 1]; /* array for treetop name */
X char home[MAXPATHLEN + 1]; /* starting dir for multiple trees */
X char *ptr;
X
X setbuf(stdout, Buf1);
X
X while ((i = getopt (argc, argv,
X#ifdef STATS
X "adfgHlnpsvxc:"
X#else
X "adfHnpvxc:"
X#endif /* STATS */
X )) != EOF) {
X switch (i) {
X case 'a':
X All = 1;
X break;
X case 'c':
X CLength = atoi(optarg);
X if (CLength > MAXNAMLEN)
X CLength = MAXNAMLEN;
X else if (CLength < 1)
X CLength = DEFCOLWID;
X break;
X case 'd':
X File_dir = DFIRST;
X break;
X case 'f':
X File_dir = FFIRST;
X break;
X case 'H':
X Header = 1;
X break;
X case 'n':
X Sort = 0;
X break;
X case 'p':
X Point = 0;
X break;
X case 'v':
X Varspaces = 0;
X break;
X case 'x':
X Xdev = 0;
X break;
X#ifdef STATS
X case 'g':
X Gflag = 1;
X break;
X case 'l':
X Longflg = 1;
X break;
X case 's':
X Compact = 1;
X break;
X#endif /* STATS */
X default:
X fprintf(stderr,
X#ifdef STATS
X "Usage: dtree [-adfgHlnpsvx] [-c linelength] [directory ... ]\n"
X#else /* STATS */
X "Usage: dtree [-adfHnpvx] [-c linelength] [directory ... ]\n"
X#endif /* STATS */
X );
X exit(FAIL);
X }
X }
X#ifdef STATS
X if (Compact && !Gflag)
X Longflg = 1;
X#endif /* STATS */
X if (CLength == 0)
X CLength = Varspaces ? MAXNAMLEN : DEFCOLWID;
X
X /* Establish where we are (our home base...) */
X if (getwd(home) == 0) {
X fprintf(stderr,
X "dtree: Cannot get initial directory: %s\n", home);
X exit(1);
X }
X
X Spaces = xmalloc(MAXNAMLEN+2);
X for(i = 0; i <= MAXNAMLEN; i++)
X Spaces[i] = ' ';
X Spaces[i] = '\0';
X
X /* Get initial Storage space */
X Size = sizeof(struct entry) * INITIAL_ELEM;
X SaveList = (struct entry *)xmalloc(Size);
X
X /* adjust for no specified directory */
X if (optind == argc)
X argv[--optind] = ".";
X
X if (Header)
X print_date();
X
X /* walk down the rest of the args, treeing them one at at time */
X for (; optind < argc; optind++) {
X if (chdir(home) == -1) {
X fprintf(stderr, "dtree: Cannot change to initial directory ");
X perror(home);
X exit(1);
X }
X strncpy (top, argv[optind], MAXPATHLEN);
X
X if (chdir(top) == FAIL) {
X fprintf(stderr, "dtree: Cannot change to top directory ");
X perror(top);
X continue;
X } else if (getwd(top) == 0) {
X fprintf(stderr,"dtree: Cannot get current directory: %s\n", top);
X continue;
X }
X
X List = SaveList; Index = 0;
X ptr = rindex(top, '/');
X
X if (!ptr || *++ptr == '\0')
X strncpy(List[Index].e_name, top, MAXNAMLEN);
X else
X strncpy(List[Index].e_name, ptr, MAXNAMLEN);
X
X if(stat(top, &Status) == FAIL) {
X fprintf(stderr, "dtree: Cannot stat directory ");
X perror(top);
X continue;
X }
X Device = Status.st_dev;
X List[0].dir = 1;
X List[0].last = 1;
X List[0].next = 1;
X#ifdef STATS
X List[0].e_mode = Status.st_mode;
X List[0].e_uid = Status.st_uid;
X List[0].e_gid = Status.st_gid;
X List[0].e_size = Status.st_size;
X#endif /* STATS */
X Index = 1;
X for (i = 1; i < DEPTH; i++)
X Maxes[i] = 0;
X Maxes[0] = stln(List[0].e_name);
X Level = 1;
X
X /* search the tree */
X List[0].end = t_search(top, &List[0]);
X
X if (Index == 1) /* empty tree */
X List[0].next = 0;
X
X if (Header) {
X if (All)
X printf("\nDirectory structure and contents of %s\n", top);
X else
X printf("\nDirectory structure of %s\n", top);
X if (Point)
X printf("(excluding entries that begin with '.')\n");
X }
X
X pt_tree(); /* print the tree */
X }
X exit(0) ;
X}
X
X
Xt_search(dir, addrs)
Xchar *dir;
Xstruct entry *addrs;
X{
X int bsort; /* index to begin sort */
X int stmp; /* save temporary index value */
X struct entry *sstep; /* saved step in list */
X int nitems; /* # of items in this directory */
X#ifdef NEWDIR
X DIR *dirp; /* pointer to directory */
X#else
X FILE *dirp;
X#endif
X char sub[MAXNAMLEN+1]; /* used for subdirectory names */
X int i;
X#ifdef NEWDIR
X struct direct *dp;
X#else
X struct direct dirent;
X struct direct *dp = &dirent;
X#endif /* NEWDIR */
X int n_subs = 0;
X int tmp = 0;
X
X#ifdef NEWDIR
X dirp = opendir(".");
X#else
X dirp = fopen(".", "r");
X#endif /* NEWDIR */
X if (dirp == NULL) {
X fprintf(stderr, "dtree: Cannot open directory ");
X perror(dir);
X return(0);
X }
X#ifndef NEWDIR
X setbuf(dirp, Buf2);
X#endif /* NEWDIR */
X
X bsort = Index;
X sstep = &List[bsort]; /* initialize sstep for for loop later on */
X nitems = Index;
X /* get the entries of the directory that we are interested in */
X#ifndef NEWDIR
X while (fread((char *)(dp), sizeof(struct direct), 1, dirp) == 1) {
X#else
X while ((dp = readdir(dirp)) != NULL) {
X#endif /* NEWDIR */
X
X if (dp->d_ino
X#ifdef unos
X == -1
X#else
X == 0
X#endif /* unos */
X || (strcmp(dp->d_name, ".") == SAME)
X || (strcmp(dp->d_name, "..") == SAME)
X || (Point && dp->d_name[0] == '.'))
X continue;
X
X strncpy(sub, dp->d_name, MAXNAMLEN);
X#ifdef S_IFLNK
X if (lstat(sub,&Lstat) == FAIL) {
X fprintf(stderr, "dtree: In directory %s, cannot lstat entry ", dir);
X perror(sub);
X continue;
X }
X#endif /* S_IFLNK */
X if (stat(sub, &Status) == FAIL) {
X fprintf(stderr, "dtree: In directory %s, cannot stat entry ", dir);
X perror(sub);
X continue;
X }
X#ifdef S_IFLNK
X if (((Lstat.st_mode & S_IFMT) == S_IFLNK) &&
X ((Status.st_mode & S_IFMT) == S_IFDIR))
X List[Index].dir = 0;
X else if ((((Lstat.st_mode & S_IFMT) == S_IFLNK) &&
X ((Status.st_mode & S_IFMT) != S_IFDIR)) && (All))
X List[Index].dir = 0;
X#endif /* S_IFLNK */
X else if ((Status.st_mode & S_IFMT) == S_IFDIR)
X List[Index].dir = 1;
X else if (All)
X List[Index].dir = 0;
X else
X continue;
X strncpy(List[Index].e_name, dp->d_name, MAXNAMLEN);
X List[Index].last = 0;
X List[Index].end = 0;
X#ifdef S_IFLNK
X if ((Lstat.st_mode & S_IFMT) == S_IFLNK) {
X List[Index].dev = (Device == Lstat.st_dev);
X List[Index].e_mode = Lstat.st_mode;
X List[Index].e_uid = Lstat.st_uid;
X List[Index].e_gid = Lstat.st_gid;
X List[Index].e_size = Lstat.st_size;
X }
X else {
X#endif /* S_IFLNK */
X List[Index].dev = (Device == Status.st_dev);
X#ifdef STATS
X List[Index].e_mode = Status.st_mode;
X List[Index].e_uid = Status.st_uid;
X List[Index].e_gid = Status.st_gid;
X List[Index].e_size = Status.st_size;
X#endif /* STATS */
X#ifdef S_IFLNK
X }
X#endif /* S_IFLNK */
X if (stln(List[Index].e_name) > Maxes[Level])
X Maxes[Level] = stln(List[Index].e_name);
X ++Index;
X if (Index*sizeof(struct entry) >= Size) {
X Size += 20*sizeof(struct entry);
X List = (struct entry *)xrealloc((char *)List, Size);
X }
X }
X#ifdef NEWDIR
X closedir(dirp);
X#else
X fclose(dirp);
X#endif /* NEWDIR */
X
X nitems = Index - nitems; /* nitems now contains the # of */
X /* items in this dir, rather than */
X /* # total items before this dir */
X
X if (Sort)
X qsort(&List[bsort], nitems, sizeof(struct entry), compar);
X
X List[Index-1].last = 1; /* mark last item for this dir */
X n_subs = nitems;
X stmp = Index;
X
X /* now walk through, and recurse on directory entries */
X /* sstep was initialized above */
X
X for (i = 0; i < nitems; sstep = &List[stmp - nitems+(++i)]) {
X if (sstep->dir && (Xdev || sstep->dev)) {
X sstep->next = Index;
X strncpy(sub, sstep->e_name, MAXNAMLEN);
X tmp = n_subs;
X Level++;
X if (chdir(sub) == FAIL) {
X fprintf(stderr,
X "dtree: Cannot change to directory %s/", dir);
X perror(sub);
X } else {
X n_subs += t_search(sub, sstep);
X if (chdir("..") == FAIL) {
X fprintf(stderr,
X "dtree: %s/%s lacks '..' entry\n",dir, sub);
X exit(1);
X }
X }
X --Level;
X if (n_subs - tmp <= 0)
X sstep->next = 0;
X else
X --n_subs;
X }
X else
X sstep->next = 0;
X }
X addrs->end = (unsigned)n_subs;
X return(n_subs);
X}
X
X/*
X * comparison routine for qsort
X */
X
Xcompar(a, b)
Xstruct entry *a, *b;
X{
X if (!File_dir) /* straight alphabetical */
X return(strncmp(a->e_name, b->e_name, MAXNAMLEN));
X
X /* sort alphabetically if both dirs or both not dirs */
X
X if ((a->dir && b->dir) || (!a->dir && !b->dir))
X return(strncmp(a->e_name, b->e_name, MAXNAMLEN));
X
X if (File_dir == FFIRST) { /* sort by files first */
X if (a->dir)
X return(GREATER);
X else
X return(LESSTHAN);
X }
X
X if (a->dir) /* sort by dir first */
X return(LESSTHAN);
X else
X return(GREATER);
X}
X
X
Xpt_tree()
X{
X register int i,j;
X struct entry *l;
X struct entry *hdr[DEPTH];
X int posit[DEPTH]; /* array of positions to print dirs */
X int con[DEPTH]; /* flags for connecting up tree */
X char flag = 0; /* flag to leave blank line after dir */
X struct entry *stack[DEPTH]; /* save positions for changing levels */
X int top = 0; /* index to top of stack */
X int count = 1; /* count of line of output */
X
X Level = 0; /* initialize Level */
X
X /* this loop appends each entry with dashes or spaces, for */
X /* directories or files respectively */
X
X for (i = 0; i < Index; i++) {
X for (j = 0; j < MAXNAMLEN; j++) {
X if (!List[i].e_name[j])
X break;
X }
X if (List[i].dir) {
X for (; j < MAXNAMLEN; j++)
X List[i].e_name[j] = '-';
X } else {
X for (; j < MAXNAMLEN; j++)
X List[i].e_name[j] = ' ';
X }
X }
X
X /* adjust the Maxes array according to the flags */
X
X for (i = 0; i < DEPTH; i++) {
X if (Varspaces) {
X if (Maxes[i] > CLength )
X Maxes[i] = CLength;
X } else
X Maxes[i] = CLength;
X }
X
X /* clear the connective and position flags */
X
X for (i = 0; i < DEPTH; i++)
X con[i] = posit[i] = 0;
X
X /* this is the main loop to print the tree structure. */
X l = &List[0];
X j = 0;
X for (;;) {
X /* directory entry, save it for later printing */
X if (l->dir != 0 && l->next != 0) {
X hdr[Level] = l;
X posit[Level] = count + (l->end + 1)/2 - 1;
X flag = 1;
X stack[top++] = l;
X l = &List[l->next];
X ++Level;
X continue;
X }
X
X#ifdef STATS
X do_it_again:
X#endif /* STATS */
X /* print columns up to our entry */
X for (j = 0; j < (flag ? Level-1 : Level); j++) {
X if (!flag && posit[j] && posit[j] <= count) {
X /* time to print it */
X if (hdr[j]->e_name[CLength-1] != '-')
X hdr[j]->e_name[CLength-1] = '*';
X printf("|-%.*s",Maxes[j],hdr[j]->e_name);
X posit[j] = 0;
X if (hdr[j]->last != 0)
X con[j] = 0;
X else
X con[j] = 1;
X#ifdef STATS
X if (Gflag || Longflg) {
X if ((i = j+1) <= Level)
X printf("| %.*s", Maxes[i], Spaces);
X for (i++; i <= Level; i++) {
X printf("%c %.*s",
X (con[i] ? '|' : ' '),
X Maxes[i], Spaces);
X }
X if (!Compact) {
X printf("%s ", getmode(hdr[j]->e_mode));
X if (Longflg)
X printf("%8.8s ",guid(hdr[j]->e_uid));
X if (Gflag)
X printf("%8.8s ",ggid(hdr[j]->e_gid));
X printf("%7ld\n",
X (hdr[j]->e_size+511L)/512L);
X } else {
X printf(" %04o ",hdr[j]->e_mode & 07777);
X if (Longflg)
X printf("%5u ", hdr[j]->e_uid);
X if (Gflag)
X printf("%5u ", hdr[j]->e_gid);
X printf("%7ld\n",
X (hdr[j]->e_size+511L)/512L);
X }
X goto do_it_again;
X }
X#endif /* STATS */
X } else
X printf("%c %.*s", (con[j] ? '|' : ' '),
X Maxes[j], Spaces);
X }
X if (flag) { /* start of directory, so leave a blank line */
X printf(con[j] ? "|\n" : "\n");
X flag = 0;
X continue;
X } else {
X /* normal file name, print it out */
X if (l->e_name[CLength-1] != '-' &&
X l->e_name[CLength-1] != ' ')
X l->e_name[CLength-1] = '*';
X printf("|-%.*s",Maxes[Level],l->e_name);
X if (l->last) {
X con[j] = 0;
X } else {
X con[j] = 1;
X }
X#ifdef STATS
X if (Gflag || Longflg) {
X if (Compact) {
X printf(" %04o ", l->e_mode & 07777);
X if (Longflg)
X printf("%5u ", l->e_uid);
X if (Gflag)
X printf("%5u ", l->e_gid);
X printf("%7ld",
X (l->e_size+511L)/512L);
X } else {
X printf("%s ", getmode(l->e_mode));
X if (Longflg)
X printf("%8.8s ",guid(l->e_uid));
X if (Gflag)
X printf("%8.8s ",ggid(l->e_gid));
X printf("%7ld",
X (l->e_size+511L)/512L);
X }
X }
X#endif /* STATS */
X }
X printf("\n");
X
X if (l->last) {
X /* walk back up */
X while (l->last) {
X --Level;
X if (--top <= 0)
X return;
X l = stack[top];
X }
X }
X l = &l[1];
X ++count;
X }
X}
X
X#ifdef STATS
X
Xchar *
Xguid(uid)
Xshort uid;
X{
X static char tb[10];
X struct passwd *pswd;
X
X pswd = getpwuid(uid);
X if (pswd == NULL)
X sprintf(tb,"%u", uid);
X else
X sprintf(tb, "%8s", pswd->pw_name);
X return(tb);
X}
X
Xchar *
Xggid(gid)
Xshort gid;
X{
X static char tb[10];
X struct group *grp;
X
X grp = getgrgid(gid);
X if (grp == NULL)
X sprintf(tb,"%u", gid);
X else
X sprintf(tb, "%8s", grp->gr_name);
X return(tb);
X}
X
X/* take the mode and make it into a nice character string */
X
Xchar *
Xgetmode(p_mode)
Xunsigned short p_mode;
X{
X static char a_mode[16];
X register int i = 0, j = 0;
X
X a_mode[j++] = ' ';
X
X switch (p_mode & S_IFMT) {
X#ifdef S_IFLNK
X case S_IFLNK:
X a_mode[j++] = 'l';
X break;
X#endif /* S_IFLNK */
X case S_IFDIR:
X a_mode[j++] = 'd';
X break;
X#ifdef S_IFMPC /* defined in stat.h if you have MPX files */
X case S_IFMPC:
X a_mode[j-1] = 'm';
X /* FALL THROUGH */
X#endif /* S_IFMPC */
X case S_IFCHR:
X a_mode[j++] = 'c';
X break;
X#ifdef S_IFMPB /* defined in stat.h if you have MPX files */
X case S_IFMPB:
X a_mode[j-1] = 'm';
X /* FALL THROUGH */
X#endif /* S_IFMPB */
X case S_IFBLK:
X a_mode[j++] = 'b';
X break;
X case S_IFREG:
X default:
X a_mode[j++] = (p_mode & S_ISVTX) ? 't' : ' ';
X break;
X }
X a_mode[j++] = ' ';
X for( i = 0;i<3;i++ ) {
X a_mode[j++] = (p_mode<<(3*i) & S_IREAD) ? 'r' : '-';
X a_mode[j++] = (p_mode<<(3*i) & S_IWRITE) ? 'w' : '-';
X a_mode[j++] = (i<2 && (p_mode<<i & S_ISUID)) ? 's' :
X ((p_mode<<(3*i) & S_IEXEC ) ? 'x' : '-');
X a_mode[j++] = ' ';
X }
X a_mode[j] = '\0';
X return(a_mode);
X}
X#endif
X
X/* like strlen, but returns length up to MAXNAMLEN-1 */
Xstln(st)
Xregister char *st;
X{
X register int t;
X
X for (t=0; t<MAXNAMLEN-1; ++t)
X if (!st[t])
X return (++t);
X return (++t);
X}
X
Xprint_date()
X{
X long now;
X
X time(&now);
X printf ("%s", ctime(&now));
X}
X
Xvoid
Xmemory_out()
X{
X fprintf(stderr, "dtree: Virtual memory exhausted\n");
X exit(1);
X}
X
X/* Allocate `size' bytes of memory dynamically, with error checking. */
X
Xchar *
Xxmalloc (size)
Xunsigned size;
X{
X char *ptr;
X
X ptr = malloc (size);
X if (ptr == 0 && size != 0)
X memory_out ();
X return ptr;
X}
X
X/* Change the size of an allocated block of memory `ptr' to `size' bytes,
X with error checking.
X If `ptr' is NULL, run xmalloc.
X If `size' is 0, run free and return NULL. */
X
Xchar *
Xxrealloc (ptr, size)
Xchar *ptr;
Xunsigned size;
X{
X if (ptr == 0)
X return xmalloc (size);
X if (size == 0) {
X free (ptr);
X return 0;
X }
X ptr = realloc (ptr, size);
X if (ptr == 0 && size != 0)
X memory_out ();
X return ptr;
X}
END_OF_FILE
if test 21819 -ne `wc -c <'dtree.c'`; then
echo shar: \"'dtree.c'\" unpacked with wrong size!
fi
# end of 'dtree.c'
fi
if test -f 'getcwd.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'getcwd.c'\"
else
echo shar: Extracting \"'getcwd.c'\" \(5247 characters\)
sed "s/^X//" >'getcwd.c' <<'END_OF_FILE'
X/*
X getcwd -- get current working directory name (POSIX and SVID compatible)
X
X last edit: 21-Sep-1987 D A Gwyn
X
X This public-domain getcwd() routine can be used to replace the UNIX
X System V library routine (which uses popen() to capture the output of
X the "pwd" command). Once that is done, "pwd" can be reimplemented as
X just puts(getcwd()).
X
X This implementation depends on every directory having entries for
X "." and "..". It also depends on the internals of the <dirent.h>
X data structures to some degree.
X
X I considered using chdir() to ascend the hierarchy, followed by a
X final chdir() to the path being returned by getcwd() to restore the
X location, but decided that error recovery was too difficult that way.
X The algorithm I settled on was inspired by my rewrite of the "pwd"
X utility, combined with the dotdots[] array trick from the SVR2 shell.
X*/
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <string.h>
X#include <dirent.h>
X#include <errno.h>
X
Xtypedef char *pointer; /* (void *) if you have it */
X
Xextern void free();
Xextern pointer malloc();
Xextern int fstat(), stat();
X
Xextern int errno; /* normally done by <errno.h> */
X
X#ifndef NULL
X#define NULL 0 /* amorphous null pointer constant */
X#endif
X
X#ifndef NAME_MAX
X#define NAME_MAX 255 /* maximum directory entry size */
X#endif
X
Xchar *
Xgetcwd( buf, size ) /* returns pointer to CWD pathname */
X char *buf; /* where to put name (NULL to malloc) */
X int size; /* size of buf[] or malloc()ed memory */
X {
X static char dotdots[] =
X"../../../../../../../../../../../../../../../../../../../../../../../../../..";
X char *dotdot; /* -> dotdots[.], right to left */
X DIR *dirp; /* -> parent directory stream */
X struct dirent *dir; /* -> directory entry */
X struct stat stat1, stat2; /* info from stat() */
X struct stat *d = &stat1; /* -> info about "." */
X struct stat *dd = &stat2; /* -> info about ".." */
X register char *buffer; /* local copy of buf, or malloc()ed */
X char *bufend; /* -> buffer[size] */
X register char *endp; /* -> end of reversed string */
X register char *dname; /* entry name ("" for root) */
X int serrno = errno; /* save entry errno */
X
X if ( size == 0 )
X {
X errno = EINVAL; /* invalid argument */
X return NULL;
X }
X
X if ( (buffer = buf) == NULL /* wants us to malloc() the string */
X && (buffer = (char *)malloc( (unsigned)size )) == NULL
X ) {
X errno = ENOMEM; /* cannot malloc() specified size */
X return NULL;
X }
X
X if ( stat( ".", dd ) != 0 ) /* prime the pump */
X goto error; /* errno already set */
X
X endp = buffer; /* initially, empty string */
X bufend = &buffer[size];
X
X for ( dotdot = &dotdots[sizeof(dotdots)]; dotdot != dotdots; )
X {
X dotdot -= 3; /* include one more "/.." section */
X /* (first time is actually "..") */
X
X /* swap stat() info buffers */
X {
X register struct stat *temp = d;
X
X d = dd; /* new current dir is old parent dir */
X dd = temp;
X }
X
X if ( (dirp = opendir( dotdot )) == NULL ) /* new parent */
X goto error; /* errno already set */
X
X if ( fstat( dirp->dd_fd, dd ) != 0 )
X {
X serrno = errno; /* set by fstat() */
X (void)closedir( dirp );
X errno = serrno; /* in case closedir() clobbered it */
X goto error;
X }
X
X if ( d->st_dev == dd->st_dev )
X { /* not crossing a mount point */
X if ( d->st_ino == dd->st_ino )
X { /* root directory */
X dname = "";
X goto append;
X }
X
X do
X if ( (dir = readdir( dirp )) == NULL )
X {
X (void)closedir( dirp );
X errno = ENOENT; /* missing entry */
X goto error;
X }
X while ( dir->d_ino != d->st_ino );
X }
X else { /* crossing a mount point */
X struct stat t; /* info re. test entry */
X char name[sizeof(dotdots) + 1 + NAME_MAX];
X
X (void)strcpy( name, dotdot );
X dname = &name[strlen( name )];
X *dname++ = '/';
X
X do {
X if ( (dir = readdir( dirp )) == NULL )
X {
X (void)closedir( dirp );
X errno = ENOENT; /* missing entry */
X goto error;
X }
X
X (void)strcpy( dname, dir->d_name );
X /* must fit if NAME_MAX is not a lie */
X }
X while ( stat( name, &t ) != 0
X || t.st_ino != d->st_ino
X || t.st_dev != d->st_dev
X );
X }
X
X dname = dir->d_name;
X
X /* append "/" and reversed dname string onto buffer */
X append:
X if ( endp != buffer /* avoid trailing / in final name */
X || dname[0] == '\0' /* but allow "/" when CWD is root */
X )
X *endp++ = '/';
X
X {
X register char *app; /* traverses dname string */
X
X for ( app = dname; *app != '\0'; ++app )
X ;
X
X if ( app - dname >= bufend - endp )
X {
X (void)closedir( dirp );
X errno = ERANGE; /* won't fit allotted space */
X goto error;
X }
X
X while ( app != dname )
X *endp++ = *--app;
X }
X
X (void)closedir( dirp );
X
X if ( dname[0] == '\0' ) /* reached root; wrap it up */
X {
X register char *startp; /* -> buffer[.] */
X
X *endp = '\0'; /* plant null terminator */
X
X /* straighten out reversed pathname string */
X for ( startp = buffer; --endp > startp; ++startp )
X {
X char temp = *endp;
X
X *endp = *startp;
X *startp = temp;
X }
X
X errno = serrno; /* restore entry errno */
X return buffer;
X }
X }
X
X errno = ENOMEM; /* actually, algorithm failure */
X
X error:
X if ( buf == NULL )
X free( (pointer)buffer );
X
X return NULL;
X }
X
END_OF_FILE
if test 5247 -ne `wc -c <'getcwd.c'`; then
echo shar: \"'getcwd.c'\" unpacked with wrong size!
fi
# end of 'getcwd.c'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(728 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# Makefile for dtree
X
X# Things that might go in DEFS:
X# -DSTATS -DNEWDIR -DDIRENT -DNDIR -DSYSV
XDEFS = -DSTATS -DNEWDIR -DDIRENT
XCFLAGS = $(DEFS) -O
XLDFLAGS = -s
XLIBS = # For Xenix use -lx with -DNDIR
X
XBINDIR = /usr/local/bin
XMANDIR = /usr/local/man/man1
X
XOBJECTS = dtree.o #getcwd.o
X
XARCH_FILES = README dtree.1 dtree.c getcwd.c Makefile
X
Xall: dtree
X
Xdtree: $(OBJECTS)
X $(CC) -o dtree $(LDFLAGS) $(OBJECTS) $(LIBS)
X
Xinstall: dtree dtree.1
X cp dtree $(BINDIR)
X cp dtree.1 $(MANDIR)
X
Xlint: dtree.c
X lint $(DEFS) dtree.c
X
Xshar: $(ARCH_FILES)
X shar $(ARCH_FILES) > dtree.shar
X
Xdist: dtree.tar.Z
X
Xtar: dtree.tar.Z
X
Xdtree.tar.Z: $(ARCH_FILES)
X tar cf - $(ARCH_FILES) | compress > dtree.tar.Z
X
Xclean:
X rm -f dtree *.o core tags a.out
END_OF_FILE
if test 728 -ne `wc -c <'Makefile'`; then
echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
echo shar: End of shell archive.
exit 0
More information about the Comp.sources.misc
mailing list