[alt.sources] tree dumper

bogatko@lzga.ATT.COM (George Bogatko) (08/23/90)

HI:

	Yeah, I know, another directory tree dumper.  But THIS one is
	smaller, *FAR* less complicated then some that I've seen, and
	did precisely what I wanted, namely generate a directory tree
	that I could then comment into a "where everything is" document.

GB

****** CUT HERE ****** CUT HERE ****** CUT HERE ****** CUT HERE ****** CUT HERE

/******************************************************************************
*                                                                             *
*                                  tree.c                                     *
*                                                                             *
******************************************************************************/

/*--------------------------  INITIAL CODING DATE -----------------------------
Wed Aug 22 20:03:03 EDT 1990 by George M. Bogatko

--------------------------------  HEADER FILES  -----------------------------*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <malloc.h>
#include <errno.h>

/*------------------  TYPEDEF'S, DEFINES, STRUCTURE DEF'S  ------------------*/
#define INCR BUFSIZ
#define USAGE fprintf(stderr, usage, argv[0])

/*----------------  IMPORTED GLOBAL VARIABLE/FUNCTION DEF'S  ----------------*/
extern char *calloc();
extern char *optarg;
extern int getopt();
extern int optind;
extern char *sys_errlist[];

/*----------------  EXPORTED GLOBAL VARIABLE/FUNCTION DEF'S  ----------------*/

/*----------------  INTERNAL GLOBAL VARIABLE/FUNCTION DEF'S  ----------------*/
#ident "@(#)tree.c	1.1 8/22/90 - George M. Bogatko -"
char *arg1;
char *seperator="\t";
char *usage = "usage: %s [-t(abstr)] directory\n";
char dirname[BUFSIZ];
char tbuf[BUFSIZ];
extern char **grabmem();
extern int compar();
extern void addmore();
extern void addname();
extern void delname();
extern void tab();
int tlevel = -1;

/*-----------------------------------------------------------------------------

SYNOPSIS:
	tree [-t(abstr)] directory

DESCRIPTION:
	TREE prints a directory tree similar to the one that MS-DOS 
	supplies, with the following additions for UNIX.

	When the file name is printed, it is appended with:

	ALL SYSTEMS:
		/ - if the file is a directory
		* - if the file is a regular file, and is executable
		# - if the file is a FIFO
	BSD SYSTEMS:
		@ - if the file is a symbolic link
		= - if the file is a socket.

	If a subdirectory cannot be entered, the directory name will
	be followed with "(cannot open)".  If the directory can
	be entered, but cannot be read, the name will be followed with
	a "(cannot read)".  Following that is the UNIX error.

OPTIONS:
	The one option, '-t' allows you to supply a different indent
	string.  The default is a TAB character ("\t").  

	One suggestion is "|  ", which will show a visual link with
	the parent directory

CAVEATS:
	Compile with -DBSD for BSD systems to get the defines for
	symbolic links and sockets.  Otherwise just compile straight.

COMMENTS:
	Suggestions for improvements are welcome as long as they are not
	of the 'kitchen sink' or 'second system' variety.


=============================================================================*/

main(argc, argv)
int argc;
char *argv[];
{
int c;

	while( (c = getopt(argc, argv, "t:")) != EOF )
	{
		switch(c)
		{
		case 't':
			seperator = strdup(optarg);
			break;
		case '?':
			USAGE;
			exit(-1);
		}
	}

	if(optind == argc) 
	{
		USAGE;
		exit(-1);
	}

	arg1=argv[optind];
	if( access(arg1,05) == -1 )
	{
		fprintf(stderr, "%s: %s\n", arg1, sys_errlist[errno]);
		exit(-1);
	}
	chdir(arg1);
	dumpdir();
	return 0;
}

int dumpdir()
{
DIR *dirp;
char **dnames;
char **fnames;
char *str;
int dnameoff=0;
int dnamesz=INCR;
int fnameoff=0;
int fnamesz=INCR;
int i;
int tl;
int x = 0;
struct dirent *dp;
struct stat sbuf;

	dnames = grabmem(dnamesz);
	fnames = grabmem(fnamesz);
/*
 * increment the tab level (see tab() )
 */
	tlevel++;
/*
 * try to open the directory
 */
	if( (dirp = opendir(".")) == (DIR *)NULL )
		return -1;
/*
 * KLUDGE number 1.  See the line with the printf statement
 * "printf("%s%s/",arg1,dirname);".  This is how we print
 * the (cannot open) statements, and then get a carrage return
 */
	putchar('\n'); 
/*
 * read the directory to the end
 */
	while( (dp = readdir (dirp)) != NULL )
	{
/*
 * gather some file statistics
 */
		if( stat(dp->d_name, &sbuf) == -1 )
		{
			perror("stat");
			return -1;
		}
/*
 * If the file is a directory, remember the name, in the directory
 * names bucket.
 */
		if((sbuf.st_mode & S_IFMT) == S_IFDIR)
		{
			dnames[dnameoff++] = strdup(dp->d_name);
			if( dnameoff > dnamesz )
				addmore(&dnames, &dnamesz);
			dnames[dnameoff+1] = (char *)NULL;
		}
		else
		{
/*
 * else it is some other file.  Put the name in another bucket.  Add
 * the mneumonic character (#=@*).
 */
			str = "";
			if((sbuf.st_mode & S_IFMT) == S_IFIFO)
				str = "#";
			else if((sbuf.st_mode & S_IFMT) == S_IFREG)
			{
				if(sbuf.st_mode & S_IXUSR)
					str="*";
			}
#if pyr || BSD
			else if((sbuf.st_mode & S_IFMT) == S_IFLNK)
				str = "@";
			else if((sbuf.st_mode & S_IFMT) == S_IFSOCK)
				str = "=";
#endif
			sprintf(tbuf, "%s%s",dp->d_name,str);
			fnames[fnameoff++]=strdup(tbuf);
			if( fnameoff >= fnamesz )
				addmore(&fnames, &fnamesz);
			fnames[fnameoff+1] = (char *)NULL;
		}
	}
/*
 * sort the buckets
 */
	qsort(fnames, fnameoff, sizeof(char *), compar);
	qsort(dnames, dnameoff, sizeof(char *), compar);
/*
 * dump the sorted list of non directory files
 */
	for(i=0; fnames[i] != (char *)NULL; i++)
	{
		tab();
		printf("%s\n",fnames[i]);
		
	}
/*
 * go through the sorted list of directory files. 
 */
	for(i=0; dnames[i] != (char *)NULL; i++)
	{
		if( strcmp(dnames[i], ".") &&
		    strcmp(dnames[i], "..") )
		{
/*
 * build up the pathname
 */
			addname(dnames[i]);
			tab();
/*
 * print it out
 */
			printf("%s%s/",arg1,dirname);
/*
 * chdir to the directory, and recurse.
 */
			if( chdir(dnames[i]) == -1 )
			{
				printf(" (cannot open: %s)\n", sys_errlist[errno]);
				delname();
			}
			else if( dumpdir() == -1 )
			{
				printf(" (cannot read: %s)\n", sys_errlist[errno]);
				tlevel--;
				chdir("..");
				delname();
			}
		}
	}
/*
 * cleanup the mallocs, close the files, decrement the tab level, 
 * and go back up a level
 */
	for(i=0; dnames[i] != (char *)NULL; i++)
	{
		free(dnames[i]);
	}
	free(dnames); 

	for(i=0; fnames[i] != (char *)NULL; i++)
	{
		free(fnames[i]);
	}
	free(fnames); 
	tlevel--;
	closedir(dirp);
	delname();
	chdir("..");
	return 0;
}

/*
 * indent according to current tab level
 */
void tab()
{
int tl = tlevel;

	while(tl--)
		printf("%s",seperator);
	return;
}

/*
 * add a name to the current path name
 */
void addname(s)
char *s;
{
	strcat(dirname,"/");
	strcat(dirname,s);
	return;
}

/*
 * delete a name from the current path name
 */
void delname()
{
char *ps;

	if( ps = strrchr(dirname, '/') )
		*ps = '\0';
	return;
}

/*
 * allocate memory
 */
char **grabmem(size)
unsigned size;
{
char **s;

	if( (s = (char **)calloc((size+2), sizeof(char **))) == (char **)NULL )
	{
		perror("calloc");
		exit(-1);
	}
	return s;
}

/*
 * grow memory
 */
void addmore(s, size)
char ***s;
unsigned *size;
{
	*size += INCR;
	if( (*s = (char **)realloc(*s, ((*size+2) * sizeof(char **)) )) == (char **)NULL )
	{
		perror("realloc");
		exit(-1);
	}
	return;
}

/*
 * function for qsort
 */
int compar(s1,s2)
char **s1, **s2;
{
	return( strcmp(*s1, *s2) );
}