home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 January
/
usenetsourcesnewsgroupsinfomagicjanuary1994.iso
/
sources
/
misc
/
volume32
/
tbtree
/
part01
/
tbtree.c
< prev
Wrap
C/C++ Source or Header
|
1992-10-15
|
10KB
|
312 lines
/*- tbtree - Graphical directory display. */
/*
* Usage: tbtree [directory]
*
* That command will give a "tree" drawing of the Unix sub-directories
* below. If no directory is specified, the current directory, ".",
* is used.
*/
/*
* This software is released to the public domain. Please don't
* sell it for a million dollars profit without giving me some.
* Please leave my name and this credit in the code, or everyone
* on the planet will forget I exist, even before I die. Besides,
* I know where you live.
*
* If you improve this program, please send me a copy.
* tombaker@world.std.com || tabaker@aol.com || BIX as tombaker Oct 2, 1992
*/
/*
* Install: Cut where it says.
* Compile as
* % cc -o tbtree tbtree.c
*/
/*
* 14-JUL-89 Original.
* 10-FEB-90 Changed the 'bugs' comment to a 'features' comment.
* 26-JUN-90 Fixed bug in which link names are not terminated correctly.
* 08-NOV-90 Prune of XYZZY hardwired into alternate version.
* 02-OCT-92 Cleaned up and posted in response to call for help over
* the Net.
* 05-OCT-92 stat() and lstat() now properly report errors ("You have no
* permission", "file no longer exists") verbosely,
* AND stat() ignores symbolic links which point at nonexistent
* files.
* (Get it? I fix a bug, which I probably have known about since
* the 1980's, three days AFTER I broadcast the source code to
* the world.)
*
* Features:
* Follows links.
* It indicates links with a "->".
* It DOES distinguish between a link to a file and one
* to a subdirectory; only the latter get displayed.
* It seems to follow circular links. Eg., "xxx -> ./"
* System errors in stat() and lstat() result in calls to
* perror(), so a useful error message is provided.
*
* Bugs:
* The opendir() call still has trouble with files which
* are not directories; the "*UNREADABLE*" pruning results.
*
* Suggestions:
* Some pruning is indicated:
* Go only a specified number of levels down.
* It is easy to lose track of what the lines point back UP to.
* An interactive version would be appreciated.
*
* The quick-and-dirty use of big, big arrays, instead of using
* dynamic allocations or the system defines, was okay because
* the swapper/pager can handle it and this is not that big a
* deal of a program. Guess I was tired that day, but it works.
* (pleasenoflames: you don't like it, fix it)
*
* Notes:
* STAT - In the case of a symbolic link that points to a file that
* does not exist (e.g., you create a linkfile "foo" that points
* to a file named "foobar", but then delete "foobar", leaving
* "foo"), stat() returns an error flag, and sets the "errno" global
* to ENOENT. This is normal, so we ignore this as a special case.
*
* Other system errors will thwart tbtree, and are reported.
*
* This works on Sun workstations. Try it on your version of Unix.
* (If I could guarantee it would work, I'd charge you.)
*
* The call to readlink() returns -1, and error = EINVAL if the
* file being tested is not a link. It returns the contents of
* symlink in nonterminated string, and the return value is the
* number of characters of the link name.
*
* If a link is a hard link, that means there is no symbolic link.
* This program just skips down the hard links like they aren't
* there.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#define FALSE (0)
#define TRUE (1)
#define MAX_SUBS (1000)
#define MAX_COMPS (100)
#define MAX_COMP_LEN (100)
#define MAX_PATH (MAX_COMPS*(MAX_COMP_LEN+1))
/*- DATA. */
int debug = FALSE;
int last_line_had_no_file = FALSE;
/*- MAIN() - Process args and start top directory. */
main(argc, argv)
int argc;
char **argv;
{
switch( argc )
{
case 1:
show( ".", " " );
break;
case 2:
show( argv[1], " " );
break;
default:
fprintf(stderr,"Usage: %s [directory].\n", argv[0]);
break;
}
}
/*- SHOW() - Display a directory and its subdirectories. RECURSIVE. */
show( path, prefix )
char *path, *prefix;
{
char *lastp;
DIR *dirp;
struct dirent *dp;
struct stat buf;
char newpath[MAX_PATH+1];
char newprefix[MAX_COMPS*4+1];
int num_subs = 0;
char darray[MAX_SUBS][MAX_COMP_LEN+1];
int i;
int symsize;
char sympath[MAX_PATH+1];
/*- Get the last component of directory name. */
lastp=strrchr( path, '/' );
if( lastp == NULL )
lastp = path;
else
lastp++;
/*- Show last component of directory name. */
printf("%s", lastp);
last_line_had_no_file = FALSE;
/*- Prune dirs ONE TWO and THREE.
* if( (!strcmp(lastp,"ONE"))
* || (!strcmp(lastp,"TWO"))
* || (!strcmp(lastp,"THREE"))
* )
* {
* if( strcmp(lastp, "THREE"))
* printf(" ");
* printf("\t*PRUNED*\n");
* last_line_had_no_file = FALSE;
* return;
* }
*/
/*- Open directory. */
if ((dirp = opendir(path)) == NULL)
{
/*- If can't (even after the above testing), yell and abort. */
/*
* The block below puts the message below the name.
* printf("\n");
* printf("%s|\n",prefix);
* printf("%s*UNREADABLE*\n", prefix );
* printf("%s\n", prefix );
* last_line_had_no_file = TRUE;
* The block in use shoves it to the right, giving one line
* per unreadable directory, just like directories with no
* subdirectories.
*/
printf("\t*UNREADABLE*\n");
last_line_had_no_file = FALSE;
return;
}
printf("\n");
/*- Read all entries. */
for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
{
/*- Skip directory entries '.' and '..' . */
if (!strcmp(dp->d_name, "."))
continue;
if (!strcmp(dp->d_name, ".."))
continue;
/*- If entry is a directory, */
strcpy( newpath, path );
strcat( newpath, "/" );
strcat( newpath, dp->d_name );
/* See note on "STAT" in file header. */
if( stat(newpath,&buf) && errno!=ENOENT )
{
fprintf(stderr,"Error running stat() on \"%s\")",newpath);
perror("");
}
if( (buf.st_mode&S_IFDIR) == S_IFDIR )
{
/*- Add it to array. */
strcpy( darray[num_subs], dp->d_name );
num_subs++;
}
}
/*- Close directory. */
closedir(dirp);
/*- Call sub to sort the array, in place. */
sort( num_subs, darray );
/*- Line above sub-directory. */
if( num_subs )
if( !last_line_had_no_file )
{
printf("%s|\n",prefix);
last_line_had_no_file = TRUE;
}
/*- For each sub-directory ... */
for( i=0; i<num_subs; i++ )
{
/*- Prepare sub-directory's path. */
strcpy( newpath, path );
strcat( newpath, "/" );
strcat( newpath, darray[i] );
/*- Sub-direcory's prefix string depends on ... */
if( (i+1) == num_subs )
{
/*- ... if sub-directory is last one ... */
strcpy( newprefix, prefix );
strcat( newprefix, " " );
}
else
{
/*- ... or not. */
strcpy( newprefix, prefix );
strcat( newprefix, "| " );
}
/*- If sub-directory is a symbolic link, */
/* See note on "STAT" in file header. */
if( lstat(newpath,&buf) && errno!=ENOENT )
{
fprintf(stderr,"Error running lstat() on \"%s\")",newpath);
perror("");
}
if( (buf.st_mode&S_IFLNK) == S_IFLNK )
{
/*- Get symbolic name as well. */
if( (symsize=readlink(newpath,sympath,MAX_PATH)) < 0 )
{
fprintf(stderr,"Error on readlink.\n");
}
if( (lastp=strchr(sympath, (int) ' ')) != NULL )
*lastp = '\0';
sympath[symsize] = '\0';
printf("%s+- %s -> %s\n",prefix,darray[i],sympath);
}
/*- Else, */
else
{
/*- Recurse to show sub-directory. */
printf("%s+- ",prefix);
show( newpath, newprefix );
}
}
/*- Space below each directory. */
if( num_subs )
if( !last_line_had_no_file )
{
printf("%s\n",prefix);
last_line_had_no_file = TRUE;
}
/*- Done. */
}
/*- SORT() - Sort array in place. */
sort( n, darray )
int n;
char darray[MAX_SUBS][MAX_COMP_LEN+1];
{
register int i, j;
char scratch[MAX_COMP_LEN+1];
/*- For each of the n-1 passes. */
for( i=0; i<(n-1); i++ )
/*- For each of the lowest (n-1-pass) pairs. */
for( j=0; j<(n-1-i); j++ )
/*- If the two are out of order. */
if( strcmp( darray[j], darray[j+1] ) > 0 )
{
/*- Swap them. */
strcpy( scratch, darray[j] );
strcpy( darray[j], darray[j+1] );
strcpy( darray[j+1], scratch );
}
}