[comp.sources.x] v08i072: dtree, Part01/02

kk@hpkronos.hpl.hp.com (Konstantinos Konstantinides) (08/16/90)

Submitted-by: Konstantinos Konstantinides <kk@hpkronos.hpl.hp.com>
Posting-number: Volume 8, Issue 72
Archive-name: dtree/part01

This version of dtree is an X extension of the program originally
written by Dave Borman. It displays on a terminal or under X11
(using Motif widgets) the tree structure of a Unix directory tree.
On a color terminal, different colors can be used for files, directories,
and symbolic links. One can select nodes of the tree, and edit them,
view them, or print them. One can also traverse nodes not fully
shown on the original tree.

K. Konstantinides
kk@hpkronos.hpl.hp.com
-------------------------Beginning of submission--Part 1 of 2----------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by Konstantinos Konstantinides <kk@hpkronos> on Tue Aug 14 15:06:50 1990
#
# This archive contains:
#	dtree.1		dtree.c		Makefile	Dtree		
#	dtree-i.h	Imakefile	README		
#

LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH

echo x - dtree.1
cat >dtree.1 <<'@EOF'
.TH DTREE 1L
.SH NAME
dtree \- display directory tree structures
.SH SYNOPSIS
.B dtree
[
.I \-aDfghHNpsStvx
] [
.I \-l level
] [
.I \-c linelength
] [
.BR directory ...
]
.SH DESCRIPTION
.I dtree
displays a graphic representation of the directory structure of each given
.B directory 
and its children either on the terminal stdout or on an X11 window, using
the Motif widgets (default). If no directories are specified, the current
directory is used.
By default, only directories, not regular files, are shown, and only their
filenames are given.  Various options add additional
information to the tree.
.SS OPTIONS
.TP
.I \-a
Include files in the listing (excluding entries beginning with '.').
.TP
.I \-c linelength
Make
.I linelength
the length of each
column of the printout.  By default, this is 14.
Any entries longer than
the column length are truncated accordingly, and the last character that
fits into the column is replaced by an asterisk.
This option only has an effect if the
.I -v 
option is specified.
.TP
.I \-l level
Search only up to the specified level. (Maximum is 10).
.TP
.I \-D
List directories first.  For each directory, its subdirectories
will be listed first, and then all of its other entries.
.TP
.I \-f
List files first.  The reverse of
.IR  \-D .
.TP
.I \-S
Long listing.  Display useful information to the right of
each entry: the name of the file's owner, its size in blocks, and its mode.
.TP
.I \-g
Same as the
.I \-S
option, except that the group name is used instead of
the owner name.  If both the
.I \-S
and
.I \-g
options are used, both the
owner and group will be displayed.
.TP
.I \-H
Display a header at the top of the printout that gives the time and
date that the printout was made and a summary of the type of
information contained in the tree.
.TP
.I \-N
No sort.  Entries are listed in the order they are read
from the directories.
.TP
.I \-p
Include entries beginning with '.' (except '.' and '..').
.TP
.I \-s
Simplify the long listing: display the user id, size in blocks, and
octal mode of the file.  This option implies the
.I \-S
option unless the
.I \-g
option is specified.
.TP
.I \-v
Do not let column lengths vary; use the same
width for each column of output.  The width defaults to 14
but can be set with the
.I \-c
option.
.TP
.I \-x
Do not cross file systems.  
.I dtree
will not cross over to a
subdirectory if it is on a different file system.
.TP
.I \-h
Will print a list of the options.
.TP
.I \-t
Terminal mode.
In default mode
.I
dtree
will display the directory tree structure in an X11 window.
The \-t option allows the tree to be printed on the terminal stdout.

Under the X11 window mode, a button click on a tree node,  makes it
the "active" node. The name of the "active" node always appears
at the top of the window, by the "quit" button.
With a  button click in the area where the "active" node is shown,
a menu appears with the mode of the node, the group and user IDs,
and \fIfile\fR or \fIdirectory\fR options .
There are
four \fIfile\fR options: \fIview, topview, edit\fR and \fIprint\fR.
There are three \fIdirectory\fR options: \fIshow subtree, list files\fR,
and \fIlist '../'\fR.
Thus, the \-c, \-S, \-g, \-s, and \-v options are meaningful only
when the \-t option is used.
.TP
.B view 
Display the whole file on a scrolled window.
Since the program reads a file all at once,
the wait may be significant for a very large file.  However, the option
.TP
.B topview 
shows only the top of the file (2000 bytes).
.TP
.B edit
Edit the file. \fIdtree\fR first checks and executes the command 
specified by the
.B dtree*editor:
resource in .Xdefaults. If that command is NULL, \fIdtree\fR
forks an hpterm window using the editor command specified
in the
.B EDITOR
environmental variable. If that variable is not specified, it uses \fIvi\fR
.TP
.B print
Print the file. It prints the file on the printer destination specified
by the
.B dtree*lpdest:
resource in .Xdefaults.
If that variable is not specified it uses
the default lp destination.
.TP
.B "show subtree"
Show the directory subtree starting from the selected "active"
directory node.
.TP
.B "list files"
List all files and directories for the "active" directory node.
This is equivalent to an "ls" command on that directory.
.TP
.B "list \.\.\/"
List the directories in the parent (top) directory.  Allows the user
to traverse upwards the directory tree.

Except for the \fIview\fR and \fItopview\fR options, in all other cases
a click on the "quit" button of the "parent" will
cause the "parent" \fIdtree\fR window to freeze until the selected command
has been executed or the "child" window has been killed.
Then the "parent" window will automatically die.
Thus it is recommended to kill "children" applications before
you try to kill the "parent".

On a color terminal, one can choose different colors to distinguish
between directories, files, and symbolic links.
When the XmGraph widget is used, the directory tree can be seen both
vertically and horizontally.
To set up a simple color scheme for \fIdtree\fR, put the following entries
in your ~/.Xdefaults file:

dtree*geometry:                     500x500
.br
dtree*quit.background:              DarkSlateBlue
.br
dtree*dir.background:               Red      
.br
dtree*sym_link.background:          Brown  
.br
dtree*options.background:           Red		

A set of simple resources is also specified in
/usr/lib/X11/app-defaults/Dtree
.SH AUTHOR
Dave Borman, Digital Unix Engineering Group
.br
decvax!borman
.br
Originally written at St. Olaf College, Northfield, MN.
.br
Additions for the X11 windows display by K. Konstantinides,
Hewlett-Packard Laboratories.
Copyright: Hewlett-Packard, 1990.
e-mail:kk@hpkronos.hpl.hp.com
@EOF

chmod 644 dtree.1

echo x - dtree.c
cat >dtree.c <<'@EOF'
/*
 *	DTREE - Print the tree structure of a directory
 *	4/7/83 name was changed from TREE to DTREE
 *	9/7/83 mods for 4.1c and 4.2 dirctory structure added
 *
 *	Dave Borman, Digital Unix Engineering Group
 *		decvax!borman
 *	Originally written at St. Olaf College, Northfield MN.
 *	Copyright (c) 1983 by Dave Borman
 *	All rights reserved
 *	This program may not be sold, but may be distributed
 *	provided this header is included.
 *
 *	Usage:	dtree [-aDfghHlNpstvx] [-c line-length] [directory...]
 *	Flags:	-a) include non-directory entries in listing
 *		-D) sort tree with directories at the top
 *		-f) sort tree with files at the top
 *		-g) same as l, but use group name instead of user name
 *              -h) help
 *		-H) display a header at top
 *		-S) print stats with each listing
 *		    if both g & l flags are given, both owner and
 *		    group will be printed
 *		-N) do not sort the tree
 *		-p) include files starting with a '.' (except "." & "..")
 *		-s) use shorter stats. Implies -S if -g isn't given.
 *              -t) Use terminal mode (no X windows)
 *		-v) variable length columns off
 *		-x) do not cross mounted file systems.
 *		-c length) set max column length to "length"
 *		-l level) search up to the specified "level".
 */

 /*     Modified by      Ed Arnold      CSU-CS   (csu-cs!arnold)     3-5-84
  *
  *     Allows symbolic links to both directories and individual files.
  *     With a '-S' or '-aS' option, links are denoted with a 'l' in front of 
  *     file or directory permissions. In all other instances both links to 
  *     directories and files are represented just as files are. Contents of
  *     linked directories are not printed due to the possibility of 
  *     recursively linked directories.
  *
  *     Big directory name option added by:
  *                      Mike Vevea      CSU-CS (csu-cs!vevea)      3-22-84
  *
  *	Toggle sense of -v (running 4.2), and eliminate some extraneous
  *		print info	Mike Meyer Energy Analysts (mwm@ea)	4/17/84
  *
  *	Fix the exit status to correctly indicate what happened.
  *				Mike Meyer Energy Analysts (mwm@ea)	4/23/84
  *
  *	Change MAX to INITIAL_ELEM to fix conflict on Suns,
  *	fix incorrect default value for Clength when -v not given,
  *	add -H option to print header with date and description of tree,
  *	remove -b option (useless) and simplify array access,
  *	use getopt to parse options, fix usage message,
  *	use getwd instead of opening a pipe to pwd,
  *	make error messages more informative,
  *	use strncpy instead of sprintf for speed,
  *	move function declarations to top of file,
  *	comment out junk after #else and #endif,
  *	make symbolic link configuring automatic,
  *	check all error returns from malloc and realloc,
  *	add System V/Xenix/Unos compatibility,
  *	remove definition of rindex.
  *		David MacKenzie <djm@eng.umd.edu> 12/20/89
  *
  *	Modified to display the tree on X11 windows using the Motif
  *	widgets and the Tree widget described by D.Young in his book,
  *     "The X Window System, Programming and applications with Xt,
  *     OSF/MOTIF EDITION", Prentice Hall, 1990.
  *     Clicking on the "active" node pops-up a menu with the Mode, and Group
  *     and User IDs. Clicking on "view" pops up a window with the whole
  *     contents of the file. Clicking on "topview" shows the top
  *	of the file only (2000 bytes).
  *     On the tree, files, directories, and symbolic links can be
  *     shown with different colors.
  *     Changed some options so there is no conflict with X-window
  *	options ( -d to -D, and -n to -N).
  *	Added the -t and -h options. Changed the -l option to -S
  *     Added the "-l level" option.
  *	Konstantinos Konstantinides, Hewlett-Packard Laboratories
  *     konstantinos_konstantinides@hplabs.hp.com  3/23/90
  *
  *     Copyright: HEWLETT-PACKARD, 1990
  *     Motif is a trademark of Open Software Foundation, Inc.
  *     X Window System is a trademark of the Massachusetts Institute of
  *     Technology
  *
  *	Added support for both the Tree and XmGraph widgets
  *	K. Konstantinides 4/16/90
  */

/* Compile-time options:
 *
 * STATS	leave undefined to remove stats, giving more core space
 * and thus the ability to tree larger tree structures on PDP 11/70s.
 *
 * NEWDIR	directory structure a la Berkeley 4.1c or 4.2
 *
 * NDIR		use <sys/ndir.h> instead of <sys/dir.h>
 * NEWDIR must be defined as well.
 *
 * DIRENT	use Posix directory library.
 * NEWDIR must be defined as well.
 *
 * SYSV		use getcwd instead of getwd, strrchr instead of rindex.
 */
void re_orient();
void disarm_callback();
void activate_callback();
void arm_callback();
void clickB();
void w_print();
void button_print();
void pop_data();
void view();
void quit_b();
void quit_b_pop();
void do_menu();

static	char Sccsid[]="@(#)dtree.c	2.3	2/14/84";

#ifdef S_IFLNK
static  char Rcsid[] ="$Header: dtree.c,v 1.7.1.2 90/04/18 09:57:30 kk Exp $";
#endif /* S_IFLNK */

#include	<stdio.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<time.h>
#include	<string.h>
#include	<stdlib.h>

/* Include files for X windows */
#include <X11/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/StringDefs.h>
#include <Xm/Xm.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>
#include <Xm/PushBG.h>
#include <Xm/CascadeB.h>
#include <Xm/RowColumn.h>
#include <Xm/ScrolledW.h>
#include <Xm/Separator.h>
#include <Xm/MainW.h>
#include <Xm/Frame.h>
#include <Xm/Text.h>
#ifdef TREE
#include "Tree.h"
#else
#include "Graph.h"
#include "Arc.h"
#endif

/* Original Defs */
#ifdef	STATS
# include	<pwd.h>
# include	<grp.h>
#endif	/* STATS */

#ifdef	NEWDIR
# if	DIRENT
#  include	<dirent.h>
#  define	direct	dirent
# else
#  if	NDIR
#   include	<sys/ndir.h>
#  else
#   include	<sys/dir.h>
#  endif
# endif /* DIRENT */
#else
# include	<sys/dir.h>
#endif	/* NEWDIR */

/* default column length when -v is given */
#ifdef unos
#define DEFCOLWID	30
#else
#define DEFCOLWID       14
#endif

#include	<sys/param.h>	/* for MAXPATHLEN */
#ifndef MAXPATHLEN
# ifdef LPNMAX		/* Xenix from stdio.h */
#  define MAXPATHLEN (LPNMAX - 1)
# else
#  include <limits.h>	/* try somewhere else */
#  define MAXPATHLEN PATH_MAX
# endif
#endif

#ifndef MAXNAMLEN	/* defined with NEWDIR routines */
# ifdef LFNMAX		/* Xenix again */
#  define MAXNAMLEN (LFNMAX - 1)
# else
#  define MAXNAMLEN      DEFCOLWID
# endif
#endif

#define	DEPTH 10	/* maximum depth that dtree will go */
#define	INITIAL_ELEM	100	/* initial # of elements for list of files */

#define	FFIRST	2	/* sort files first */
#define DFIRST	1	/* sort directories first */
#define	FAIL	-1	/* failure return status of sys calls */
#define	GREATER	1	/* return value of strcmp if arg1 > arg2 */
#define	LESSTHAN -1	/* return value of strcmp if arg1 < arg2 */
#define	SAME	0	/* return value of strcmp if arg1 == arg2 */

#ifdef	STATS
char *getmode();
char *guid();
char *ggid();
struct passwd *getpwuid();
struct group *getgrgid();
#endif /* STATS */

#ifdef	SYSV
#define	rindex	strrchr
#endif /* SYSV */

char *getwd();
char *rindex();
/* 
int qsort();
char *malloc();
char *realloc();
*/
long time();

int compar();			/* comparison routine for qsort */
char *xmalloc();
char *xrealloc();

#ifdef	SYSV
#define getwd(buf) getcwd((buf), MAXPATHLEN + 1)
#endif /* SYSV */

int	Index = 0;		/* current element of list[]	*/
int     CLength = 0;		/* max length of a column       */
int	All = 0;		/* all != 0; list non-directory entries */
int	File_dir = 0;		/* flag for how to sort */
int	Sort = 1;		/* flag to cause sorting of entries */
int	Point = 1;		/* skip point files if set	*/
int	Header = 0;		/* print verbose header */
int	Maxes[DEPTH];		/* array keeps track of max length in columns */
int	Level = 0;		/* counter for how deep we are	*/
int	Device;			/* device that we are starting tree on */
int	Xdev = 1;		/* set to allow crossing of devices */
int	Varspaces = 1;		/* set to allow compaction of column width */
#ifdef	STATS
int	Gflag = 0;		/* set for group stats instead of owner */
int	Longflg = 0;		/* set for long listing */
int	Compact = 0;		/* set for shortened long listing */
#endif	/* STATS */
#undef Status
struct	stat Status;
#ifdef  S_IFLNK
struct  stat Lstat;   /* stat of link, if there is one */
#endif  /* S_IFLNK */

struct entry {
	int next;			/* index to next element in list */
					/* could be a ptr, but realloc() */
					/* might screw us then */
#ifdef	STATS
	off_t	e_size;			/* size in blocks */
	unsigned short	e_mode;		/* file mode */
	short	e_uid;			/* uid of owner */
	short	e_gid;			/* gid of owner */
#endif	/* STATS */
	unsigned short dir : 1;		/* entry is a directory */
	unsigned short last : 1;	/* last entry in the dir. */
	unsigned short dev : 1;		/* set if same device as top */
	unsigned short end : 13;	/* index of last subdir entry*/
	char	e_name[MAXNAMLEN + 1];	/* name from directory entry */
	char    path_name[MAXPATHLEN + 1]; /* path name */
} *List, *SaveList;

unsigned Size;				/* how big of space we've malloced */

char	*Spaces;	/* used for output */
char	Buf1[BUFSIZ];	/* buffers for stdio stuff.  We don't want	*/
#ifndef NEWDIR
char	Buf2[BUFSIZ];	/* anyone calling malloc, because then		*/
			/* realloc() will have to move the whole list	*/
#endif
#define FS(str) (void) fprintf(stderr,str)

help()
{
					fprintf(stderr,
#ifdef	STATS
		"Usage: dtree [-aDfghHlNpstvx] [-c linelength] [directory ... ]\n"
#else	/* STATS */
		"Usage: dtree [-aDfhHNptvx] [-c linelength] [directory ... ]\n"
#endif	/* STATS */
						);
FS("   options  -a) include non-directory entries in listing\n");
FS("            -D) sort tree with directories at the top\n");
FS("            -f) sort tree with files at the top\n");
FS("            -h) help - prints this message\n");
FS("            -H) display a header at top\n");
FS("            -x) do not cross mounted file systems.\n");
FS("            -p) include files starting with a . (except . and ..)\n");
FS("            -N) do not sort the tree\n");
FS("            -t) Use terminal mode, don't display on an X window\n\n");
FS("            -l level) Search only up to the specified level \n\n");
FS("            Valid only if -t is used. Under default mode, a click on a \n");
FS("            node pops up a menu with mode, user and group ids.\n\n");
FS("            -S) print stats with each listing\n");
FS("                if both g & l flags are given, both owner and\n");
FS("                group will be printed\n");
FS("            -g) same as l, but use group name instead of user name \n");
FS("            -s) use shorter stats. Implies -S if -g isn't given.\n");
FS("            -v) variable length columns \n");
FS("            -c length) set max column length to -length-\n");
FS("  X-Window version 1.1 4/16/90 \n");
}
int newlevel = DEPTH;
Widget boxes[11], options;
Widget file_menu, dir_menu;
struct entry *file_pointer;
main(argc, argv)
char	**argv;
int	argc;
{
	extern int optind;
	extern char *optarg;
	register int i;
	char	top[MAXPATHLEN + 1];	/* array for treetop name */
	char	home[MAXPATHLEN + 1];	/* starting dir for multiple trees */
	char	*ptr;

	Widget toplevel;
	Widget sw, tree, q_button, w_button;
	Widget menu_bar, frame, main_w, title;
	Arg    wargs[10];
	int    flagX=1;			/* use X windows as default */
	Widget quit_menu, exit_b, reorient;
	int really_quit, n;

	setbuf(stdout, Buf1);

	while ((i = getopt (argc, argv,
#ifdef STATS
			    "haDfgHl:NpsSvxtc:"
#else
			    "haDfHl:Npvxtc:"
#endif /* STATS */
			    )) != EOF) {
		switch (i) {
				case 'a':
					All = 1;
					break;
				case 'c':
					CLength = atoi(optarg);
					if (CLength > MAXNAMLEN)
						CLength = MAXNAMLEN;
					else if (CLength < 1)
						CLength = DEFCOLWID;
					break;
				case 'l':
					newlevel = atoi(optarg);
					if(newlevel > DEPTH) {
					newlevel = DEPTH;
					}
					break;
				case 'D':
					File_dir = DFIRST;
					break;
				case 'f':
					File_dir = FFIRST;
					break;
				case 'H':
					Header = 1;
					break;
				case 'N':
					Sort = 0;
					break;
				case 'p':
					Point = 0;
					break;
				case 'v':
					Varspaces = 0;
					break;
				case 'x':
					Xdev = 0;
					break;
				case 't':
					flagX = 0;
					break;

				case 'h':
					help();
					exit(0);
#ifdef	STATS
				case 'g':
					Gflag = 1;
					break;
				case 'S':
					Longflg = 1;
					break;
				case 's':
					Compact = 1;
					break;
#endif	/* STATS */
				default:
					help();
					exit(FAIL);
				}
	}
#ifdef	STATS
	if (Compact && !Gflag)
		Longflg = 1;
#endif	/* STATS */
	if (CLength == 0)
		CLength = Varspaces ? MAXNAMLEN : DEFCOLWID;

	/* Establish where we are (our home base...) */
	if (getwd(home) == 0) {
		fprintf(stderr,
			"dtree: Cannot get initial directory: %s\n", home);
		exit(1);
	}

	Spaces = xmalloc(MAXNAMLEN+2);
	for(i = 0; i <= MAXNAMLEN; i++)
		Spaces[i] = ' ';
	Spaces[i] = '\0';

	/* Get initial Storage space */
	Size = sizeof(struct entry) * INITIAL_ELEM;
	SaveList = (struct entry *)xmalloc(Size);

	/* adjust for no specified directory */
	if (optind == argc )
		if(optind > 1) argv[--optind] = ".";
		else { argv[1] = ".", optind = 1; argc = 2;}

	if (Header)
		print_date();

	/* walk down the rest of the args, treeing them one at at time */
	for (; optind < argc; optind++) {
		if (chdir(home) == -1) {
			fprintf(stderr, "dtree: Cannot change to initial directory ");
			perror(home);
			exit(1);
		}
		strncpy (top, argv[optind], MAXPATHLEN);

		if (chdir(top) == FAIL) {
			fprintf(stderr, "dtree: Cannot change to top directory ");
			perror(top);
			continue;
		} else if (getwd(top) == 0) {
			fprintf(stderr,"dtree: Cannot get current directory: %s\n", top);
			continue;
		}

		List = SaveList; Index = 0;
		getwd(List[0].path_name);
		ptr = rindex(top, '/');

		if (!ptr || *++ptr == '\0')
			strncpy(List[Index].e_name, top, MAXNAMLEN);
		else
			strncpy(List[Index].e_name, ptr, MAXNAMLEN);

		if(stat(top, &Status) == FAIL) {
			fprintf(stderr, "dtree: Cannot stat directory ");
			perror(top);
			continue;
		}
		Device = Status.st_dev;
		List[0].dir = 1;
		List[0].last = 1;
		List[0].next = 1;
#ifdef	STATS
		List[0].e_mode = Status.st_mode;
		List[0].e_uid = Status.st_uid;
		List[0].e_gid = Status.st_gid;
		List[0].e_size = Status.st_size;
#endif	/* STATS */
		Index = 1;
		for (i = 1; i < DEPTH; i++)
			Maxes[i] = 0;
		Maxes[0] = stln(List[0].e_name);
		Level = 1;

		/* Don't waste time if there is no display */
		if(flagX == 1)
		toplevel = XtInitialize(argv[0], "Dtree", NULL, 0, &argc, argv);

		fprintf(stderr,"Started searching....Please wait \n");
		/* search the tree */
		List[0].end = t_search(top, &List[0]);

		if (Index == 1)				/* empty tree */
			List[0].next = 0;

		if (Header) {
		if (All)
		    printf("\nDirectory structure and contents of %s\n", top);
		else
		    printf("\nDirectory structure of %s\n", top);
		if (Point)
			printf("(excluding entries that begin with '.')\n");
		}

		if(flagX == 0) pt_tree();
		else {

	/* Create a MainWindow, with a MenuBar and a Frame window
	   Put on the Frame, a ScrolledWindow and the Tree window
	   The MenuBar has a quit button and the directory Name
	*/
			fprintf(stderr,"Opening display now...Please wait\n");

			/* main window */
			main_w = XmCreateMainWindow(toplevel,"mainw",NULL,0);
			XtManageChild(main_w);

			/* menu bar */
			menu_bar = XmCreateMenuBar(main_w,"menu",NULL,0);
			XtManageChild(menu_bar);

			/* frame */
			XtSetArg(wargs[0], XmNshadowType, XmSHADOW_OUT);
			frame = XmCreateFrame(main_w,"frame",wargs,1);
			XtManageChild(frame);

			/* quit button */

			quit_menu = XmCreatePulldownMenu(menu_bar,
				    "quit_menu",NULL,0);
			XtSetArg(wargs[0], XmNsubMenuId,quit_menu);
			q_button = XmCreateCascadeButton(menu_bar, 
						   "quit",wargs,1);
			XtManageChild(q_button);

		       w_button =XtCreateManagedWidget("warning",
				xmLabelWidgetClass,quit_menu, NULL,0);
		w_print(w_button,"Don't exit unless all children are dead\n");
   			exit_b = XtCreateManagedWidget("Exit", 
				  xmPushButtonWidgetClass, quit_menu, NULL, 0);
   			XtAddCallback(exit_b, XmNarmCallback, 
				arm_callback, &really_quit);
   			XtAddCallback(exit_b, XmNdisarmCallback, 
				disarm_callback, &really_quit);
   			XtAddCallback(exit_b, XmNactivateCallback, 
				activate_callback, &really_quit);     

#ifndef TREE
			/* Reorient button */
			reorient = XmCreateCascadeButton(menu_bar,"ReOrient",
					      NULL,0);
			XtManageChild(reorient);
#endif
			/* Create the options pull-down menu */
			file_pointer = &List[0];
			show_menu(menu_bar,file_pointer);

			/* title (path) widget */

			title = XtCreateManagedWidget("title", 
			       xmCascadeButtonWidgetClass, menu_bar, NULL,0);
			XtSetArg(wargs[0],XmNmenuHelpWidget, title);
			XtSetValues(menu_bar, wargs, 1);
			w_print(title,top);

			XtSetArg(wargs[0], XmNscrollingPolicy, XmAUTOMATIC);
			sw = XtCreateManagedWidget("swindow",
				xmScrolledWindowWidgetClass, frame, wargs, 1);
			/* Tree widget */
#ifdef TREE
			tree = XtCreateManagedWidget("tree",XstreeWidgetClass,
					sw, NULL,0);
#else
			n=0;
			XtSetArg(wargs[n],XmNautoLayoutMode, TRUE); n++;
			XtSetArg(wargs[n],XmNsiblingSpacing, 5); n++;
			XtSetArg(wargs[n],XmNarcDrawMode,XmPOSITION_FIXED);n++;
			tree = XmCreateGraph(sw,"tree", wargs,n);
			/* XmAddTabGroup(tree); */
			XtManageChild(tree);

			XtAddCallback(reorient, XmNactivateCallback,
							re_orient, tree);
#endif
			XmMainWindowSetAreas(main_w, menu_bar, NULL,
			   NULL, NULL, frame);
			show_dtree(tree);
			clickB(options,file_pointer,NULL);
			XtRealizeWidget(toplevel);
			XtMainLoop();
		}
		
	}
	exit(0) ;
}


t_search(dir, addrs)
char *dir;
struct	entry *addrs;
{
	int	bsort;			/* index to begin sort */
	int	stmp;			/* save temporary index value */
	struct entry *sstep;		/* saved step in list */
	int	nitems;			/* # of items in this directory */
#ifdef	NEWDIR
	DIR	*dirp;			/* pointer to directory */
#else
	FILE	*dirp;
#endif
	char	sub[MAXNAMLEN+1];	/* used for subdirectory names */
	int	i;
#ifdef	NEWDIR
	struct direct *dp;
#else
	struct direct dirent;
	struct direct *dp = &dirent;
#endif	/* NEWDIR */
	int	n_subs = 0;
	int	tmp = 0;

#ifdef	NEWDIR
	dirp = opendir(".");
#else
	dirp = fopen(".", "r");
#endif	/* NEWDIR */
	if (dirp == NULL) {
		fprintf(stderr, "dtree: Cannot open directory ");
		perror(dir);
		return(0);
	}
#ifndef	NEWDIR
	setbuf(dirp, Buf2);
#endif	/* NEWDIR */

	bsort = Index;
	sstep = &List[bsort]; /* initialize sstep for for loop later on */
	nitems = Index;
	/* get the entries of the directory that we are interested in */
#ifndef	NEWDIR
	while (fread((char *)(dp), sizeof(struct direct), 1, dirp) == 1) {
#else
	while ((dp = readdir(dirp)) != NULL) {
#endif	/* NEWDIR */

		if (dp->d_ino
#ifdef unos
		    == -1
#else
		    == 0
#endif /* unos */
		    || (strcmp(dp->d_name, ".") == SAME)
		    || (strcmp(dp->d_name, "..") == SAME)
		    || (Point && dp->d_name[0] == '.'))
				continue;

		strncpy(sub, dp->d_name, MAXNAMLEN);
#ifdef S_IFLNK
		if (lstat(sub,&Lstat) == FAIL) {
			fprintf(stderr, "dtree: In directory %s, cannot lstat entry ", dir);
			perror(sub);
			continue;
                }
#endif /* S_IFLNK */
		if (stat(sub, &Status) == FAIL) {
			fprintf(stderr, "dtree: In directory %s, cannot stat entry ", dir);
			perror(sub);
			continue;
		}
#ifdef S_IFLNK
		if (((Lstat.st_mode & S_IFMT) == S_IFLNK)  &&
		    ((Status.st_mode & S_IFMT) == S_IFDIR)) 
		        List[Index].dir = 0;	
		else if ((((Lstat.st_mode & S_IFMT) == S_IFLNK) &&
			((Status.st_mode & S_IFMT) != S_IFDIR)) && (All)) 
                        List[Index].dir = 0;
#endif /* S_IFLNK */
		else if ((Status.st_mode & S_IFMT) == S_IFDIR)
			List[Index].dir = 1;
		else if (All)
			List[Index].dir = 0;
		else
			continue;
		strncpy(List[Index].e_name, dp->d_name, MAXNAMLEN);
		getwd(List[Index].path_name);
		List[Index].last = 0;
		List[Index].end = 0;
#ifdef S_IFLNK
		if ((Lstat.st_mode & S_IFMT) == S_IFLNK) {
		     List[Index].dev = (Device == Lstat.st_dev);
		     List[Index].e_mode = Lstat.st_mode;
		     List[Index].e_uid = Lstat.st_uid;
		     List[Index].e_gid = Lstat.st_gid;
		     List[Index].e_size = Lstat.st_size;
                }
                else {
#endif /* S_IFLNK */
		     List[Index].dev = (Device == Status.st_dev);
#ifdef STATS
		     List[Index].e_mode = Status.st_mode;
		     List[Index].e_uid = Status.st_uid;
		     List[Index].e_gid = Status.st_gid;
		     List[Index].e_size = Status.st_size;
#endif /* STATS */
#ifdef S_IFLNK
                }
#endif /* S_IFLNK */
		if (stln(List[Index].e_name) > Maxes[Level])
			Maxes[Level] = stln(List[Index].e_name);
		++Index;
		if (Index*sizeof(struct entry) >= Size) {
			Size += 20*sizeof(struct entry);
			List = (struct entry *)xrealloc((char *)List, Size);
		}
	}
#ifdef	NEWDIR
	closedir(dirp);
#else
	fclose(dirp);
#endif	/* NEWDIR */

	nitems = Index - nitems;	/* nitems now contains the # of */
					/* items in this dir, rather than */
					/* # total items before this dir */

	if (Sort)
		qsort(&List[bsort], nitems, sizeof(struct entry), compar);

	List[Index-1].last = 1;	/* mark last item for this dir */
	n_subs = nitems;
	stmp = Index;

	/* now walk through, and recurse on directory entries */
	/* sstep was initialized above */

	for (i = 0; i < nitems; sstep = &List[stmp - nitems+(++i)]) {
		if (sstep->dir && (Level < newlevel) && (Xdev || sstep->dev)) {
			sstep->next = Index;
			strncpy(sub, sstep->e_name, MAXNAMLEN);
			tmp = n_subs;
			Level++;
			if (chdir(sub) == FAIL) {
				fprintf(stderr,
					"dtree: Cannot change to directory %s/", dir);
				perror(sub);
			} else {
				n_subs += t_search(sub, sstep);
				if (chdir("..") == FAIL) {
					fprintf(stderr,
						"dtree: %s/%s lacks '..' entry\n",dir, sub);
					exit(1);
				}
			}
			--Level;
			if (n_subs - tmp <= 0)
				sstep->next = 0;
			else
				--n_subs;
		}
		else
			sstep->next = 0;
	}
	addrs->end = (unsigned)n_subs;
	return(n_subs);
}

/*
 *	comparison routine for qsort
 */

compar(a, b)
struct entry *a, *b;
{
	if (!File_dir)		/* straight alphabetical */
		return(strncmp(a->e_name, b->e_name, MAXNAMLEN));

	/* sort alphabetically if both dirs or both not dirs */

	if ((a->dir && b->dir) || (!a->dir && !b->dir))
		return(strncmp(a->e_name, b->e_name, MAXNAMLEN));

	if (File_dir == FFIRST) {	/* sort by files first */
		if (a->dir)
			return(GREATER);
		else
			return(LESSTHAN);
	}

	if (a->dir)			/* sort by dir first */
		return(LESSTHAN);
	else
		return(GREATER);
}


pt_tree()
{
	register int	i,j;
	struct entry *l;
	struct entry *hdr[DEPTH];
	int posit[DEPTH];		/* array of positions to print dirs */
	int con[DEPTH];			/* flags for connecting up tree */
	char	flag = 0;		/* flag to leave blank line after dir */
	struct	entry *stack[DEPTH];	/* save positions for changing levels */
	int	top = 0;		/* index to top of stack */
	int	count = 1;		/* count of line of output */

	Level = 0;			/* initialize Level */

	/* this loop appends each entry with dashes or spaces, for */
	/* directories or files respectively */

	for (i = 0; i < Index; i++) {
		for (j = 0; j < MAXNAMLEN; j++) {
			if (!List[i].e_name[j])
				break;
		}
		if (List[i].dir) {
			for (; j < MAXNAMLEN; j++)
				List[i].e_name[j] = '-';
		} else {
			for (; j < MAXNAMLEN; j++)
				List[i].e_name[j] = ' ';
		}
	}

	/* adjust the Maxes array according to the flags */

	for (i = 0; i < DEPTH; i++) {
		if (Varspaces) {
			if (Maxes[i] > CLength )
				Maxes[i] = CLength;
		} else
			Maxes[i] = CLength;
	}

	/* clear the connective and position flags */

	for (i = 0; i < DEPTH; i++)
		con[i] = posit[i] = 0;

	/* this is the main loop to print the tree structure. */
	l = &List[0];
	j = 0;
	for (;;) {
		/* directory entry, save it for later printing */
		if (l->dir != 0 && l->next != 0) {
			hdr[Level] = l;
			posit[Level] = count + (l->end + 1)/2 - 1;
			flag = 1;
			stack[top++] = l;
			l = &List[l->next];
			++Level;
			continue;
		}

#ifdef	STATS
	do_it_again:
#endif	/* STATS */
		/* print columns up to our entry */
		for (j = 0; j < (flag ? Level-1 : Level); j++) {
			if (!flag && posit[j] && posit[j] <= count) {
				/* time to print it */
				if (hdr[j]->e_name[CLength-1] != '-')
					hdr[j]->e_name[CLength-1] = '*';
				printf("|-%.*s",Maxes[j],hdr[j]->e_name);
				posit[j] = 0;
				if (hdr[j]->last != 0)
				    con[j] = 0;
				else
				    con[j] = 1;
#ifdef	STATS
				if (Gflag || Longflg) {
				    if ((i = j+1) <= Level)
					printf("| %.*s", Maxes[i], Spaces);
				    for (i++; i <= Level; i++) {
					printf("%c %.*s",
					    (con[i] ? '|' : ' '),
					    Maxes[i], Spaces);
				    }
				    if (!Compact) {
					printf("%s ", getmode(hdr[j]->e_mode));
					if (Longflg)
					   printf("%8.8s ",guid(hdr[j]->e_uid));
					if (Gflag)
					   printf("%8.8s ",ggid(hdr[j]->e_gid));
					printf("%7ld\n",
					    (hdr[j]->e_size+511L)/512L);
				    } else {
					printf(" %04o ",hdr[j]->e_mode & 07777);
					if (Longflg)
					    printf("%5u ", hdr[j]->e_uid);
					if (Gflag)
					    printf("%5u ", hdr[j]->e_gid);
					printf("%7ld\n",
					    (hdr[j]->e_size+511L)/512L);
				    }
				    goto do_it_again;
				}
#endif	/* STATS */
			} else
				printf("%c %.*s", (con[j] ? '|' : ' '),
					Maxes[j], Spaces);
		}
		if (flag) {	/* start of directory, so leave a blank line */
			printf(con[j] ? "|\n" : "\n");
			flag = 0;
			continue;
		} else {
				/* normal file name, print it out */
			if (l->e_name[CLength-1] != '-' &&
			    l->e_name[CLength-1] != ' ')
			    l->e_name[CLength-1] = '*';
			printf("|-%.*s",Maxes[Level],l->e_name);
			if (l->last) {
				con[j] = 0;
			} else {
				con[j] = 1;
			}
#ifdef	STATS
			if (Gflag || Longflg) {
				if (Compact) {
					printf(" %04o ", l->e_mode & 07777);
					if (Longflg)
					    printf("%5u ", l->e_uid);
					if (Gflag)
					    printf("%5u ", l->e_gid);
					printf("%7ld",
					    (l->e_size+511L)/512L);
				} else {
					printf("%s ", getmode(l->e_mode));
					if (Longflg)
					    printf("%8.8s ",guid(l->e_uid));
					if (Gflag)
					    printf("%8.8s ",ggid(l->e_gid));
					printf("%7ld",
					    (l->e_size+511L)/512L);
				}
			}
#endif	/* STATS */
		}
		printf("\n");

		if (l->last) {
			/* walk back up */
			while (l->last) {
				--Level;
				if (--top <= 0)
					return;
				l = stack[top];
			}
		}
		l = &l[1];
		++count;
	}
}

#ifdef	STATS

char *
guid(uid)
short uid;
{
	static char tb[10];
	struct passwd *pswd;

	pswd = getpwuid(uid);
	if (pswd == NULL)
		sprintf(tb,"%u", uid);
	else
		sprintf(tb, "%8s", pswd->pw_name);
	return(tb);
}

char *
ggid(gid)
short gid;
{
	static char tb[10];
	struct group *grp;

	grp = getgrgid(gid);
	if (grp == NULL)
		sprintf(tb,"%u", gid);
	else
		sprintf(tb, "%8s", grp->gr_name);
	return(tb);
}

/* take the mode and make it into a nice character string */

char	*
getmode(p_mode)
unsigned short p_mode;
{
	static char a_mode[16];
	register int	i = 0, j = 0;

	a_mode[j++] = ' ';

	switch (p_mode & S_IFMT) {
#ifdef S_IFLNK
	case S_IFLNK:
		a_mode[j++] = 'l';
		break;
#endif /* S_IFLNK */
	case S_IFDIR:
		a_mode[j++] = 'd';
		break;
#ifdef	S_IFMPC		/* defined in stat.h if you have MPX files */
	case S_IFMPC:
		a_mode[j-1] = 'm';
		/* FALL THROUGH */
#endif	/* S_IFMPC */
	case S_IFCHR:
		a_mode[j++] = 'c';
		break;
#ifdef	S_IFMPB		/* defined in stat.h if you have MPX files */
	case S_IFMPB:
		a_mode[j-1] = 'm';
		/* FALL THROUGH */
#endif	/* S_IFMPB */
	case S_IFBLK:
		a_mode[j++] = 'b';
		break;
	case S_IFREG:
	default:
		a_mode[j++] = (p_mode & S_ISVTX) ? 't' : ' ';
		break;
	}
	a_mode[j++] = ' ';
	for( i = 0;i<3;i++ ) {
		a_mode[j++] = (p_mode<<(3*i) & S_IREAD) ? 'r' : '-';
		a_mode[j++] = (p_mode<<(3*i) & S_IWRITE) ? 'w' : '-';
		a_mode[j++] = (i<2 && (p_mode<<i & S_ISUID)) ? 's' :
			      ((p_mode<<(3*i) & S_IEXEC ) ? 'x' : '-');
		a_mode[j++] = ' ';
	}
	a_mode[j] = '\0';
	return(a_mode);
}
#endif

/* like strlen, but returns length up to MAXNAMLEN-1 */
stln(st)
register char *st;
{
	register int t;

	for (t=0; t<MAXNAMLEN-1; ++t)
		if (!st[t])
			return (++t);
	return (++t);
}

print_date()
{
	long now;

	time(&now);
	printf ("%s", ctime(&now));
}

void
memory_out()
{
	fprintf(stderr, "dtree: Virtual memory exhausted\n");
	exit(1);
}

/* Allocate `size' bytes of memory dynamically, with error checking.  */

char *
xmalloc (size)
unsigned size;
{
	char *ptr;

	ptr = malloc (size);
	if (ptr == 0 && size != 0)
		memory_out ();
	return ptr;
}

/* Change the size of an allocated block of memory `ptr' to `size' bytes,
   with error checking.
   If `ptr' is NULL, run xmalloc.
   If `size' is 0, run free and return NULL.  */

char *
xrealloc (ptr, size)
char *ptr;
unsigned size;
{
	if (ptr == 0)
		return xmalloc (size);
	if (size == 0) {
		free (ptr);
		return 0;
	}
	ptr = realloc (ptr, size);
	if (ptr == 0 && size != 0)
		memory_out ();
	return ptr;
}
/*-------------------------New functions for X windows -----------------*/

/* Main program to display the tree in an X window */

show_dtree(parent)
	Widget parent;

{
	Arg wargs[4];
	struct entry *stack[DEPTH], *branch;
	int n, top=0, *dir_index;
	int dir_node=0, dir_count=0, i;
	Widget parent_node=NULL, *Dir, arc;
	XmString label_s = NULL;	

	/* Find the number of directories */
	for(i=0; i < Index; i++)
		if(List[i].dir == 1) dir_count++;

	if((Dir = (Widget *) malloc( dir_count * sizeof(Widget))) == NULL){
		fprintf(stderr,"malloc failure\n");
	        exit(1);
	}
	if((dir_index = (int *) malloc(dir_count * sizeof(int))) == NULL) {
		fprintf(stderr,"malloc failure\n");
		exit(1) ;
	}

	branch = &List[0];

	if(dir_count == 1 && branch->next == 0) {
		if(All == 0) {
		fprintf(stderr,"No subdirectories under directory %s\n",
		     branch->e_name);
		fprintf(stderr,"You may want to try the -ap option\n");
		}
		else fprintf(stderr,"No files under directory %s\n", 
		     branch->e_name);
		exit(0);
	}

	/* Distinguish between directories and files */

	for(;;)  {
		if(branch->dir != 0 && branch->next != 0) {
			dir_index[top] = dir_node;
			stack[top++] = branch;
			n=0;
#ifdef TREE
			XtSetArg(wargs[n], XtNsuperNode,parent_node); n++;
#endif
			label_s = XmStringCreateLtoR(branch->e_name,
			                      XmSTRING_DEFAULT_CHARSET);
			XtSetArg(wargs[n], XmNlabelString,label_s); n++;
			Dir[dir_node] = XmCreatePushButton(parent,"dir",
			                        wargs,n);
			XtManageChild(Dir[dir_node]);
			XtAddCallback(Dir[dir_node],XmNactivateCallback,
			       clickB, branch);

#ifndef TREE
			/* create the arc */
			if(parent_node) {
			n=0;
			XtSetArg(wargs[n], XmNarcDirection, XmDIRECTED); n++;
			XtSetArg(wargs[n], XmNto, Dir[dir_node]); n++;
			XtSetArg(wargs[n], XmNfrom, parent_node); n++;
			arc = XmCreateArc(parent, branch->e_name,wargs,n);
			XtManageChild(arc);
			}
#endif

			branch = &List[branch->next];
			parent_node = Dir[dir_node++];
			continue;
		}
		create_file_node(parent,branch,parent_node);
		if(branch->last) {
			while(branch->last) {
				if(--top <= 0 ) {
					free(Dir);
					free(dir_index);
					return;
					}
				branch = stack[top];
				parent_node = Dir[dir_index[top-1]];
				}
			}
		branch = &branch[1];
	}
}
/* Function that creates a widget for a node. Three types are available,
   dir, for directories , file, for files, and sym_link for
   symbolic links
*/
create_file_node(pparent,bbranch,nnode)
	Widget pparent, nnode;
	struct entry *bbranch;
{
	Widget w, arc;
	Arg wargs[4];
	int n;
	char *win_type;
	XmString label_s = NULL;	
	int Gflag=1;		/* this is a gadget */


	n=0;
	label_s = XmStringCreateLtoR(bbranch->e_name,
	                      XmSTRING_DEFAULT_CHARSET);
	XtSetArg(wargs[n], XmNlabelString,label_s); n++;

	/* Check the type of the node */
	if(bbranch->dir == 1) { 
		win_type = "dir";
		Gflag = 0;
	}
#ifdef STATS
	else if(strncmp(" l",getmode(bbranch->e_mode),2) == 0)
			{win_type = "sym_link"; Gflag = 0;}
#endif /* STATS */
	else win_type = "file";
#ifdef TREE
        XtSetArg(wargs[n], XtNsuperNode, nnode); n++; 
	w = XmCreatePushButton(pparent, win_type, wargs, n);
#else
	if(Gflag == 0)
	w = XmCreatePushButton(pparent, win_type, wargs, n);
	else
	w = XmCreatePushButtonGadget(pparent, win_type, wargs, n);
#endif
	XtManageChild(w);
	XtAddCallback(w,XmNactivateCallback,clickB,bbranch);

#ifndef TREE
	/* create the arcs */
	n=0;
	XtSetArg(wargs[n], XmNarcDirection, XmDIRECTED); n++;
	XtSetArg(wargs[n], XmNto, w); n++;
	XtSetArg(wargs[n], XmNfrom, nnode); n++;
	arc = XmCreateArc(pparent, bbranch->e_name,wargs,n);
	XtManageChild(arc);
#endif
}
/* Function that creates the pull-down menu when someone clicks on the 
  active node of the tree.  
*/

show_menu(pparent, bbranch)
	Widget pparent;
	struct entry *bbranch;
{
	Widget menu ;
	Widget submenu_dir, submenu_file;
	int i;
	char *mode_name;
	Arg  wargs[4];

	menu = XmCreatePulldownMenu(pparent,"menu",NULL, 0);

	XtSetArg(wargs[0], XmNsubMenuId,menu);
	options = XtCreateManagedWidget("options",xmCascadeButtonWidgetClass,
		   pparent, wargs, 1);
	w_print(options, bbranch->e_name);
	/*
	boxes[0] = XtCreateManagedWidget("title",xmLabelWidgetClass, 
		   menu, NULL,0);
	XtCreateManagedWidget("separator",xmSeparatorWidgetClass, 
		   menu, NULL,0);
	*/
#ifdef STATS
	for(i = 1; i < 4 ; i++)
	boxes[i] =  XtCreateManagedWidget("id",xmLabelWidgetClass,
		   menu, NULL,0);
	button_print(bbranch); /* print the labels on the menu */

	XtCreateManagedWidget("separator2",xmSeparatorWidgetClass, 
		   menu, NULL,0);
	/* Create sub-menus */

#endif /*STATS */

	file_menu = XtCreateManagedWidget("File Options",
	    xmCascadeButtonWidgetClass, menu, NULL,0);
	dir_menu  = XtCreateManagedWidget("Directory Options",
	    xmCascadeButtonWidgetClass, menu, NULL,0);

	/* Create cascading sub-menus */

	/* If it is a file */
	submenu_file = XmCreatePulldownMenu(menu, "filesubmenu", NULL, 0);
	XtSetArg(wargs[0], XmNsubMenuId, submenu_file);
	XtSetValues(file_menu, wargs, 1);

	boxes[4] = XtCreateManagedWidget("view",xmPushButtonWidgetClass,
	       		        submenu_file, NULL,0);
		XtAddCallback(boxes[4], XmNactivateCallback,view, NULL);
		boxes[5] = XtCreateManagedWidget("topview",
			xmPushButtonWidgetClass, submenu_file, NULL,0);
		XtAddCallback(boxes[5], XmNactivateCallback,view, NULL);
		boxes[6] = XtCreateManagedWidget("edit",
			xmPushButtonWidgetClass, submenu_file, NULL,0);
		XtAddCallback(boxes[6], XmNactivateCallback,do_menu, NULL);
		boxes[7] = XtCreateManagedWidget("print",
			xmPushButtonWidgetClass, submenu_file, NULL,0);
		XtAddCallback(boxes[7], XmNactivateCallback,do_menu, NULL);

	/* if it a directory */
	submenu_dir = XmCreatePulldownMenu(menu, "dirsubmenu", NULL, 0);
	XtSetArg(wargs[0], XmNsubMenuId, submenu_dir);
	XtSetValues(dir_menu, wargs, 1);
		boxes[8] = XtCreateManagedWidget("show subtree",
			xmPushButtonWidgetClass, submenu_dir, NULL,0);
		XtAddCallback(boxes[8], XmNactivateCallback,do_menu, NULL);
		boxes[9] = XtCreateManagedWidget("list files",
			xmPushButtonWidgetClass, submenu_dir, NULL,0);
		XtAddCallback(boxes[9], XmNactivateCallback,do_menu, NULL);
		boxes[10] = XtCreateManagedWidget("list ../",
			xmPushButtonWidgetClass, submenu_dir, NULL,0);
		XtAddCallback(boxes[10], XmNactivateCallback,do_menu, NULL);
}
/* Not used here, but may be useful in the future !!! */
/* Copied from D. Young's book */
/*
void post_menu_handler(w, menu, event)
	Widget w, menu;
	XEvent *event;
{
	Arg wargs[10];
	int button;

 	XtSetArg(wargs[0], XmNwhichButton, &button); 
	XtGetValues(menu, wargs, 1);
		if(event->xbutton.button == button) {
		XmMenuPosition(menu, event);
		XtManageChild(menu);
		}
}
*/
/* Quick and dirty way to view a file in a pop-up window 
   Speed may improve, if one reads and displays page by page
   No check to see if the file is binary
*/
void view(w, call_mydata, call_data)
	Widget w;
	caddr_t call_data, call_mydata;

{
	struct entry *branch;
	char *filename[MAXPATHLEN+1];  
        struct stat statbuf;         /* Information on a file. */
        int file_length;             /* Length of file.        */
        unsigned char * file_string; /* Contents of file.      */
	FILE *fp = NULL;
	Widget text, toplevel2, sw2, button, frame, main_w, menu_bar;
	Widget title;
	XmString name_string = NULL;
	char *button_name = NULL;
	int topflag=0;
	Arg	wargs[10];
	int n=0;

	branch = file_pointer;
	/* find which button called us */
	XtSetArg(wargs[0], XmNlabelString, &name_string);
	XtGetValues(w,wargs,1);
	XmStringGetLtoR(name_string,XmSTRING_DEFAULT_CHARSET,&button_name);
	if(strcmp("topview",button_name) == 0) topflag = 1;

	/* find first the correct path */

	strncpy(filename,branch->path_name,MAXPATHLEN);
	strcat(filename,"/");
	strcat(filename,branch->e_name);
	

	if ((fp = fopen(filename, "r")) == NULL) {
		fprintf(stderr,"Can't open file name %s\n",filename);
		return;
	}
	 if (stat(filename, &statbuf) == 0)
	       file_length = statbuf.st_size;
         else
      		file_length = 1000000; /* arbitrary file length */

	 /* read the file string */

	 if(topflag == 1 && file_length >= 2000 ) file_length = 2000;

	 file_string = (unsigned char *) XtMalloc((unsigned)file_length);
         fread(file_string, sizeof(char), file_length, fp);

	 if (fclose(fp) != NULL) fprintf(stderr, "Warning: unable to close file.\n");

	/* Create another pop-pup top-shell with a MainWindow */

	toplevel2 = XtCreateApplicationShell("Dtreef",topLevelShellWidgetClass,
			 NULL,0);

	main_w = XmCreateMainWindow(toplevel2,"mainw",NULL,0);
	XtManageChild(main_w);
	menu_bar = XmCreateMenuBar(main_w,"menu",NULL,0);
	XtManageChild(menu_bar);
	XtSetArg(wargs[0], XmNshadowType, XmSHADOW_OUT);
	frame = XmCreateFrame(main_w,"frame",wargs,1);
	XtManageChild(frame);
	XtSetArg(wargs[0], XmNscrollingPolicy, XmAUTOMATIC);
	sw2 = XtCreateManagedWidget("swindowf",
		xmScrolledWindowWidgetClass,frame, wargs,1);
	XmMainWindowSetAreas(main_w, menu_bar, NULL, NULL, NULL, frame);
	button = XmCreateCascadeButton(menu_bar, "quit",NULL,0);
	XtManageChild(button);
	XtAddCallback(button,XmNactivateCallback,quit_b_pop,toplevel2);

	title = XtCreateManagedWidget("title", 
		       xmCascadeButtonWidgetClass, menu_bar, NULL,0);
	w_print(title, filename);


	/* Create Text widget */
	n=0;
        XtSetArg (wargs[n], XmNeditable, FALSE); n++; 

	text = XmCreateText(sw2, "text", wargs, n);

	/* added the file string to the text widget */

	   XmTextSetString(text, file_string);
        if(file_string != NULL) XtFree(file_string);
        if(name_string != NULL) XtFree(name_string);

	/* Poput the text file */

	XtPopup(toplevel2,XtGrabNone); 
	XtManageChild(text); 
}

/* CallBack function of the quit button on a pop-up window */

void quit_b_pop(w, topshell, call_data)
	Widget w;
	Widget topshell;
	caddr_t  call_data;
{
	 XtPopdown(topshell);
}

/* function that forks a command from the menu */
/* Needs work!!!!!
*/
void do_menu(w,call_mydata,call_data)
	Widget w;
	caddr_t call_data, call_mydata;

{
	struct entry *branch;
	char *filename[MAXPATHLEN+1];  
	char *topdir[MAXPATHLEN+1];
	XmString name_string = NULL;
	char *button_name = NULL;
	char *lpdest=NULL, *editor = NULL;
	char  *EDITOR="EDITOR\0";
	int pid, pathl,namel;
	Arg  wargs[3];

	
	
	/* find which button called us */

	branch = file_pointer;
	XtSetArg(wargs[0], XmNlabelString, &name_string);
	XtGetValues(w,wargs,1);
	XmStringGetLtoR(name_string,XmSTRING_DEFAULT_CHARSET,&button_name);


	/* find first the correct path - append the file name 
	    except for the "root" directory */

	strncpy(filename,branch->path_name,MAXPATHLEN);
	if(branch->next != 1) {
		strcat(filename,"/");
		strcat(filename,branch->e_name);
	}
	
	/* fork a new process */
	pid = fork();

	if(pid > 0) {
		/* wait((int *)0); */
		return;
	}

	if(pid == 0) {
	/* 	for(fd = 0; fd < _NFILE; fd++) close(fd); */
		if(strcmp(button_name,"print") == 0) {
			
			lpdest = XGetDefault(XtDisplay(w),"dtree","lpdest");
			if (lpdest != NULL){
		     execl("/usr/bin/lp","lp","-d",lpdest,filename,(char *)0);
			}
			else
			execl("/usr/bin/lp","lp",filename,(char *)0);
		}
		else if(strcmp(button_name,"edit") == 0) {

			editor = XGetDefault(XtDisplay(w),"dtree","editor");

			if (editor == NULL) { 
				 editor = getenv(EDITOR);
				 if(editor == NULL) 
				 	editor="/usr/bin/vi";
		     	 	 execl("/usr/bin/X11/hpterm",
							"hpterm","-e",editor,
				                  filename,(char *)0);
				 } else 
			execlp(editor,editor, filename,(char *)0);
		}
		else if(strcmp(button_name,"list files") == 0) {
		 	 execlp("dtree", "dtree", 
				   "-a", "-l","1",filename, (char *)0);
		}
		else if(strcmp(button_name,"list ../") == 0) {

			/* find the parent directory */
			pathl = strlen(filename);
			namel = strlen(branch->e_name);
			strncpy(topdir,branch->path_name,pathl-namel);

		 	execlp("dtree", "dtree", "-l","1",topdir, (char *)0);
		}
			
		else
		 execlp("dtree", "dtree", 
				  "-a",filename, (char *)0);

	fprintf(stderr,"%s command failed\n",button_name);
	}
	fprintf(stderr,"fork for %s command failed\n",button_name);
}

/* Function that updates the pop-up menu labels and permissions
   when someone clicks on a node.
*/
void clickB( w,branch,call_data)
	Widget w;
	struct entry *branch;
	caddr_t call_data;
{
	Arg wargs[3];
	XrmValue bgcolor;
	if(branch->dir == 1) {
		XtSetSensitive(file_menu ,FALSE);
		XtSetSensitive(dir_menu ,True);
	}
	else {
		XtSetSensitive(dir_menu ,False);
#ifdef STATS
		/* Don't do anything with symb. links */
		if(strncmp(" l",getmode(branch->e_mode),2) == 0)
			 XtSetSensitive(file_menu ,False);
		else
#endif /* STATS */
		XtSetSensitive(file_menu ,True);
	}
	w_print(options, branch->e_name);

	 /* Get the background color of the activated button 
	    Need to get the foreground color too
         */
	 _XmSelectColorDefault (w, NULL, &bgcolor);
	XtSetArg(wargs[0], XmNbackground, *((Pixel *) bgcolor.addr));
	XtSetValues(options, wargs, 1);
	file_pointer = branch;
	button_print(branch);
}

/* Print a string on a widget */

void w_print(w, string)
	Widget w;
	char *string;
{
	XmString xmstr;
	Arg wargs[2];

	xmstr = XmStringLtoRCreate(string, XmSTRING_DEFAULT_CHARSET);
	XtSetArg(wargs[0], XmNlabelString, xmstr);
	XtSetValues(w,wargs, 1);
}

/* print the labels on the popup menu */
void button_print(branch)
	struct entry *branch;
{
	char tmp[40];
	/* w_print(boxes[0], branch->e_name); */
#ifdef STATS
	strncpy(tmp,"Mode :",40);
	strcat(tmp,getmode(branch->e_mode));
	w_print(boxes[1],tmp);
	strncpy(tmp,"User :",40);
	strcat(tmp,guid(branch->e_uid));
	w_print(boxes[2], tmp);
	strncpy(tmp,"Group:",40);
	strcat(tmp, ggid(branch->e_gid));
	w_print(boxes[3], tmp);
#endif /* STATS */
}

/* 
 * Copied from D. Young's book  with some changes
 * Define three callbacks. Make them static - no need 
 * to make them known outside this file.
 */
static void arm_callback(w, flag, call_data)
     Widget     w;
     int       *flag;
     XmAnyCallbackStruct *call_data; 
{
 *flag = FALSE;
}
static void activate_callback(w, flag, call_data)
     Widget     w;
     int       *flag;
     XmAnyCallbackStruct *call_data; 
{
  *flag = TRUE;
}
static void disarm_callback(w, flag, call_data)
     Widget     w;
     int       *flag;
     XmAnyCallbackStruct *call_data; 
{
  if(*flag){
    XtCloseDisplay(XtDisplay(w));
    exit(0);
  }
}

/* function to re-orient the graph */
#ifndef TREE
void re_orient(w, graph, call_data)
	Widget w, graph;
	caddr_t  call_data;

{
	Arg wargs[2];
	XtSetArg(wargs[0], XmNreorient, True);
	XtSetValues(graph, wargs, 1);
}
#endif
@EOF

chmod 444 dtree.c

echo x - Makefile
cat >Makefile <<'@EOF'
# Makefile for dtree

# Things that might go in DEFS:
# -DSTATS -DNEWDIR -DDIRENT -DNDIR -DSYSV
# The -DTREE flag is used to create dtree with the Tree widget
# otherwise it uses the XmGraph widget.
DEFS = -D_HPUX_SOURCE -DSTATS -DNEWDIR -DSYSV #-DDIRENT -DS_IFLNK
CFLAGS = $(DEFS) -O
LDFLAGS = 
LIBS = # For Xenix use -lx with -DNDIR
LIBS = -lmalloc -lXm -lXt -lX11 -lm


BINDIR = /usr/local/bin/X11
MANDIR = /usr/local/man/man1

OBJECTS_A = dtree.o Arc.o Graph.o 	#using XmGraph
OBJECTS_B = dtree_tree.o Tree.o         #using D. Young's tree.

ARCH_FILES = dtree.1 dtree.c Makefile Dtree Tree.h TreeP.h \
             dtree-i.h Tree.c Arc.c Arc.h ArcP.h Graph.c Graph.h GraphP.h

ARCH_FILESB = dtree.1 dtree.c Makefile Dtree dtree-i.h Imakefile README


all: dtree_tree

dtree: $(OBJECTS_A)
	$(CC) -o dtree $(LDFLAGS) $(OBJECTS_A) $(LIBS)

dtree_tree: $(OBJECTS_B)
		$(CC) -o dtree $(LDFLAGS) $(OBJECTS_B) $(LIBS)

Arc.o:	Arc.c
	$(CC) -c $(CFLAGS) Arc.c +Ns4000

Graph.o:	Graph.c
		$(CC) -c $(CFLAGS) Graph.c +Ns4000

dtree_tree.o:	dtree.c
		$(CC) -o dtree_tree.o -c $(CFLAGS) -DTREE dtree.c

install: dtree dtree.1
	cp dtree $(BINDIR)
	cp dtree.1 $(MANDIR)

lint: dtree.c
	lint $(DEFS) dtree.c

shar: $(ARCH_FILESB)
	shar $(ARCH_FILESB) > dtree.shar_1
	shar Tree* > dtree.shar_2
dist: dtree.tar.Z

tar: dtree.tar.Z

dtree.tar.Z: $(ARCH_FILES)
	tar cf - $(ARCH_FILES) | compress > dtree.tar.Z

clean:
	rm -f dtree *.o core tags a.out
@EOF

chmod 644 Makefile

echo x - Dtree
cat >Dtree <<'@EOF'
#
# app-defaults file for dtree
#
dtree*geometry:                     500x500
dtree*dir.background:               Red
dtree*file.background:              Gray
dtree*sym_link.background:          Brown
dtree*quit.background:              DarkSlateBlue
dtree*options.background:	    Red	
dtree*XmGraph*childSpacing:         50              #distance between levels
Mwm*Dtree*iconImage:                /usr/local/include/X11/bitmaps/dtree-i.h
Hpwm*Dtree*iconImage:               /usr/local/include/X11/bitmaps/dtree-i.h
!dtree*lpdest:  		    rljet           #Printer destination
!dtree*editor:                      emacsclient     #window-clever editor
!dtree*sym_link.foreground:    
@EOF

chmod 644 Dtree

echo x - dtree-i.h
cat >dtree-i.h <<'@EOF'
#define load_i_width 50
#define load_i_height 50
static char load_i_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x20,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10,
   0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
   0x00, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
   0x00, 0xf8, 0x0f, 0x10, 0x00, 0x20, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00,
   0x20, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x20, 0x00, 0x00, 0x0f, 0x08,
   0x00, 0x00, 0x20, 0x00, 0x00, 0x09, 0x08, 0x10, 0x00, 0x20, 0x00, 0x80,
   0x08, 0x08, 0x00, 0x00, 0x20, 0x00, 0x80, 0xf8, 0x0f, 0x10, 0x00, 0x20,
   0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x40, 0x00, 0x00, 0x10,
   0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00,
   0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x20, 0xfe,
   0x13, 0xfe, 0xe3, 0x17, 0x00, 0x20, 0x02, 0x0a, 0x02, 0x22, 0x04, 0x00,
   0x20, 0xaa, 0x0a, 0x56, 0x23, 0x14, 0x00, 0x20, 0x02, 0xfe, 0x03, 0x3e,
   0x04, 0x00, 0x20, 0xaa, 0x0a, 0xaa, 0x22, 0x14, 0x00, 0x20, 0x02, 0x0a,
   0x02, 0x22, 0x04, 0x00, 0x20, 0xfe, 0x13, 0xfe, 0xe3, 0x17, 0x00, 0x20,
   0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10,
   0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x40, 0xf8,
   0x0f, 0x10, 0x00, 0x20, 0x00, 0x40, 0x08, 0x08, 0x00, 0x00, 0x20, 0x00,
   0x80, 0x08, 0x08, 0x10, 0x00, 0x20, 0x00, 0x80, 0x08, 0x08, 0x00, 0x00,
   0x20, 0x00, 0x00, 0x0f, 0x08, 0x10, 0x00, 0x20, 0x00, 0x00, 0x08, 0x08,
   0x00, 0x00, 0x20, 0x00, 0x00, 0xf8, 0x0f, 0x10, 0x00, 0x20, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x20,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10,
   0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x55, 0x55, 0x55,
   0x55, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00};
@EOF

chmod 644 dtree-i.h

echo x - Imakefile
cat >Imakefile <<'@EOF'
#Imakefile to create dtree using the "tree" widget.

        DEFINES = -DSTATS -DNEWDIR -DSYSV -DTREE
LOCAL_LIBRARIES = 
  SYS_LIBRARIES = -lmalloc -lXm -lXt -lX11 -lm
           SRCS = dtree.c Tree.c
           OBJS = dtree.o Tree.o

ComplexProgramTarget(dtree)


@EOF

chmod 644 Imakefile

echo x - README
cat >README <<'@EOF'
This version of dtree is an X extension of the program originally
written by Dave Borman. It displays on a terminal or under X11
(using Motif widgets) the tree structure of a Unix directory tree.
On a color terminal, different colors can be used for files, directories,
and symbolic links. One can select nodes of the tree, and edit them,
view them, or print them. One can also traverse nodes not fully
shown on the original tree.

The program uses the "tree" widget described in D. Young's book on
X window programming (code included here). The program also runs
using the XmGraph widget, but because that code is not yet stable,
the code for the XmGraph widget is not included here.
To make the program run: make dtree_tree

The program runs faster with the "tree" widget, but the XmGraph
widgets may provide the potential for additional capabilities, such
as: removing files from the tree, copying of files and directories, etc.

Potential problem: because dtree creates a PushButton widget for every file
or directory shown, you may run out of memory. It is recommended that you
start with a moderate size tree, and create new ones as needed.
Experience will show how much you can push it!!
If you have a monochrome monitor, you may want to substitute those
widgets with gadgets.

For example: dtree -l 2
will show the tree of your current directory up to depth 2.
	     dtree -al 2
will show both files and directories up to depth 2.

K. Konstantinides
HP Labs
kk@hpkronos.hpl.hp.com
@EOF

chmod 644 README

exit 0

dan
----------------------------------------------------
O'Reilly && Associates   argv@sun.com / argv@ora.com
Opinions expressed reflect those of the author only.

kk@hpkronos.hpl.hp.com (Konstantinos Konstantinides) (08/16/90)

Submitted-by: Konstantinos Konstantinides <kk@hpkronos.hpl.hp.com>
Posting-number: Volume 8, Issue 72
Archive-name: dtree/part01

This version of dtree is an X extension of the program originally
written by Dave Borman. It displays on a terminal or under X11
(using Motif widgets) the tree structure of a Unix directory tree.
On a color terminal, different colors can be used for files, directories,
and symbolic links. One can select nodes of the tree, and edit them,
view them, or print them. One can also traverse nodes not fully
shown on the original tree.

K. Konstantinides
kk@hpkronos.hpl.hp.com
-------------------------Beginning of submission--Part 1 of 2----------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by Konstantinos Konstantinides <kk@hpkronos> on Tue Aug 14 15:06:50 1990
#
# This archive contains:
#	dtree.1		dtree.c		Makefile	Dtree		
#	dtree-i.h	Imakefile	README		
#

LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH

echo x - dtree.1
cat >dtree.1 <<'@EOF'
.TH DTREE 1L
.SH NAME
dtree \- display directory tree structures
.SH SYNOPSIS
.B dtree
[
.I \-aDfghHNpsStvx
] [
.I \-l level
] [
.I \-c linelength
] [
.BR directory ...
]
.SH DESCRIPTION
.I dtree
displays a graphic representation of the directory structure of each given
.B directory 
and its children either on the terminal stdout or on an X11 window, using
the Motif widgets (default). If no directories are specified, the current
directory is used.
By default, only directories, not regular files, are shown, and only their
filenames are given.  Various options add additional
information to the tree.
.SS OPTIONS
.TP
.I \-a
Include files in the listing (excluding entries beginning with '.').
.TP
.I \-c linelength
Make
.I linelength
the length of each
column of the printout.  By default, this is 14.
Any entries longer than
the column length are truncated accordingly, and the last character that
fits into the column is replaced by an asterisk.
This option only has an effect if the
.I -v 
option is specified.
.TP
.I \-l level
Search only up to the specified level. (Maximum is 10).
.TP
.I \-D
List directories first.  For each directory, its subdirectories
will be listed first, and then all of its other entries.
.TP
.I \-f
List files first.  The reverse of
.IR  \-D .
.TP
.I \-S
Long listing.  Display useful information to the right of
each entry: the name of the file's owner, its size in blocks, and its mode.
.TP
.I \-g
Same as the
.I \-S
option, except that the group name is used instead of
the owner name.  If both the
.I \-S
and
.I \-g
options are used, both the
owner and group will be displayed.
.TP
.I \-H
Display a header at the top of the printout that gives the time and
date that the printout was made and a summary of the type of
information contained in the tree.
.TP
.I \-N
No sort.  Entries are listed in the order they are read
from the directories.
.TP
.I \-p
Include entries beginning with '.' (except '.' and '..').
.TP
.I \-s
Simplify the long listing: display the user id, size in blocks, and
octal mode of the file.  This option implies the
.I \-S
option unless the
.I \-g
option is specified.
.TP
.I \-v
Do not let column lengths vary; use the same
width for each column of output.  The width defaults to 14
but can be set with the
.I \-c
option.
.TP
.I \-x
Do not cross file systems.  
.I dtree
will not cross over to a
subdirectory if it is on a different file system.
.TP
.I \-h
Will print a list of the options.
.TP
.I \-t
Terminal mode.
In default mode
.I
dtree
will display the directory tree structure in an X11 window.
The \-t option allows the tree to be printed on the terminal stdout.

Under the X11 window mode, a button click on a tree node,  makes it
the "active" node. The name of the "active" node always appears
at the top of the window, by the "quit" button.
With a  button click in the area where the "active" node is shown,
a menu appears with the mode of the node, the group and user IDs,
and \fIfile\fR or \fIdirectory\fR options .
There are
four \fIfile\fR options: \fIview, topview, edit\fR and \fIprint\fR.
There are three \fIdirectory\fR options: \fIshow subtree, list files\fR,
and \fIlist '../'\fR.
Thus, the \-c, \-S, \-g, \-s, and \-v options are meaningful only
when the \-t option is used.
.TP
.B view 
Display the whole file on a scrolled window.
Since the program reads a file all at once,
the wait may be significant for a very large file.  However, the option
.TP
.B topview 
shows only the top of the file (2000 bytes).
.TP
.B edit
Edit the file. \fIdtree\fR first checks and executes the command 
specified by the
.B dtree*editor:
resource in .Xdefaults. If that command is NULL, \fIdtree\fR
forks an hpterm window using the editor command specified
in the
.B EDITOR
environmental variable. If that variable is not specified, it uses \fIvi\fR
.TP
.B print
Print the file. It prints the file on the printer destination specified
by the
.B dtree*lpdest:
resource in .Xdefaults.
If that variable is not specified it uses
the default lp destination.
.TP
.B "show subtree"
Show the directory subtree starting from the selected "active"
directory node.
.TP
.B "list files"
List all files and directories for the "active" directory node.
This is equivalent to an "ls" command on that directory.
.TP
.B "list \.\.\/"
List the directories in the parent (top) directory.  Allows the user
to traverse upwards the directory tree.

Except for the \fIview\fR and \fItopview\fR options, in all other cases
a click on the "quit" button of the "parent" will
cause the "parent" \fIdtree\fR window to freeze until the selected command
has been executed or the "child" window has been killed.
Then the "parent" window will automatically die.
Thus it is recommended to kill "children" applications before
you try to kill the "parent".

On a color terminal, one can choose different colors to distinguish
between directories, files, and symbolic links.
When the XmGraph widget is used, the directory tree can be seen both
vertically and horizontally.
To set up a simple color scheme for \fIdtree\fR, put the following entries
in your ~/.Xdefaults file:

dtree*geometry:                     500x500
.br
dtree*quit.background:              DarkSlateBlue
.br
dtree*dir.background:               Red      
.br
dtree*sym_link.background:          Brown  
.br
dtree*options.background:           Red		

A set of simple resources is also specified in
/usr/lib/X11/app-defaults/Dtree
.SH AUTHOR
Dave Borman, Digital Unix Engineering Group
.br
decvax!borman
.br
Originally written at St. Olaf College, Northfield, MN.
.br
Additions for the X11 windows display by K. Konstantinides,
Hewlett-Packard Laboratories.
Copyright: Hewlett-Packard, 1990.
e-mail:kk@hpkronos.hpl.hp.com
@EOF

chmod 644 dtree.1

echo x - dtree.c
cat >dtree.c <<'@EOF'
/*
 *	DTREE - Print the tree structure of a directory
 *	4/7/83 name was changed from TREE to DTREE
 *	9/7/83 mods for 4.1c and 4.2 dirctory structure added
 *
 *	Dave Borman, Digital Unix Engineering Group
 *		decvax!borman
 *	Originally written at St. Olaf College, Northfield MN.
 *	Copyright (c) 1983 by Dave Borman
 *	All rights reserved
 *	This program may not be sold, but may be distributed
 *	provided this header is included.
 *
 *	Usage:	dtree [-aDfghHlNpstvx] [-c line-length] [directory...]
 *	Flags:	-a) include non-directory entries in listing
 *		-D) sort tree with directories at the top
 *		-f) sort tree with files at the top
 *		-g) same as l, but use group name instead of user name
 *              -h) help
 *		-H) display a header at top
 *		-S) print stats with each listing
 *		    if both g & l flags are given, both owner and
 *		    group will be printed
 *		-N) do not sort the tree
 *		-p) include files starting with a '.' (except "." & "..")
 *		-s) use shorter stats. Implies -S if -g isn't given.
 *              -t) Use terminal mode (no X windows)
 *		-v) variable length columns off
 *		-x) do not cross mounted file systems.
 *		-c length) set max column length to "length"
 *		-l level) search up to the specified "level".
 */

 /*     Modified by      Ed Arnold      CSU-CS   (csu-cs!arnold)     3-5-84
  *
  *     Allows symbolic links to both directories and individual files.
  *     With a '-S' or '-aS' option, links are denoted with a 'l' in front of 
  *     file or directory permissions. In all other instances both links to 
  *     directories and files are represented just as files are. Contents of
  *     linked directories are not printed due to the possibility of 
  *     recursively linked directories.
  *
  *     Big directory name option added by:
  *                      Mike Vevea      CSU-CS (csu-cs!vevea)      3-22-84
  *
  *	Toggle sense of -v (running 4.2), and eliminate some extraneous
  *		print info	Mike Meyer Energy Analysts (mwm@ea)	4/17/84
  *
  *	Fix the exit status to correctly indicate what happened.
  *				Mike Meyer Energy Analysts (mwm@ea)	4/23/84
  *
  *	Change MAX to INITIAL_ELEM to fix conflict on Suns,
  *	fix incorrect default value for Clength when -v not given,
  *	add -H option to print header with date and description of tree,
  *	remove -b option (useless) and simplify array access,
  *	use getopt to parse options, fix usage message,
  *	use getwd instead of opening a pipe to pwd,
  *	make error messages more informative,
  *	use strncpy instead of sprintf for speed,
  *	move function declarations to top of file,
  *	comment out junk after #else and #endif,
  *	make symbolic link configuring automatic,
  *	check all error returns from malloc and realloc,
  *	add System V/Xenix/Unos compatibility,
  *	remove definition of rindex.
  *		David MacKenzie <djm@enumd.edu> 12/20/89
  *
  *	Modified to display the tree on X11 windows using the Motif
  *	widgets and the Tree widget described by D.Young in his book,
  *     "The X Window System, Programming and applications with Xt,
  *     OSF/MOTIF EDITION", Prentice Hall, 1990.
  *     Clicking on the "active" node pops-up a menu with the Mode, and Group
  *     and User IDs. Clicking on "view" pops up a window with the whole
  *     contents of the file. Clicking on "topview" shows the top
  *	of the file only (2000 bytes).
  *     On the tree, files, directories, and symbolic links can be
  *     shown with different colors.
  *     Changed some options so there is no conflict with X-window
  *	options ( -d to -D, and -n to -N).
  *	Added the -t and -h options. Changed the -l option to -S
  *     Added the "-l level" option.
  *	Konstantinos Konstantinides, Hewlett-Packard Laboratories
  *     konstantinos_konstantinides@hplabs.hp.com  3/23/90
  *
  *     Copyright: HEWLETT-PACKARD, 1990
  *     Motif is a trademark of Open Software Foundation, Inc.
  *     X Window System is a trademark of the Massachusetts Institute of
  *     Technology
  *
  *	Added support for both the Tree and XmGraph widgets
  *	K. Konstantinides 4/16/90
  */

/* Compile-time options:
 *
 * STATS	leave undefined to remove stats, giving more core space
 * and thus the ability to tree larger tree structures on PDP 11/70s.
 *
 * NEWDIR	directory structure a la Berkeley 4.1c or 4.2
 *
 * NDIR		use <sys/ndir.h> instead of <sys/dir.h>
 * NEWDIR must be defined as well.
 *
 * DIRENT	use Posix directory library.
 * NEWDIR must be defined as well.
 *
 * SYSV		use getcwd instead of getwd, strrchr instead of rindex.
 */
void re_orient();
void disarm_callback();
void activate_callback();
void arm_callback();
void clickB();
void w_print();
void button_print();
void pop_data();
void view();
void quit_b();
void quit_b_pop();
void do_menu();

static	char Sccsid[]="@(#)dtree.c	2.3	2/14/84";

#ifdef S_IFLNK
static  char Rcsid[] ="$Header: dtree.c,v 1.7.1.2 90/04/18 09:57:30 kk Exp $";
#endif /* S_IFLNK */

#include	<stdio.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<time.h>
#include	<string.h>
#include	<stdlib.h>

/* Include files for X windows */
#include <X11/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/StringDefs.h>
#include <Xm/Xm.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>
#include <Xm/PushBG.h>
#include <Xm/CascadeB.h>
#include <Xm/RowColumn.h>
#include <Xm/ScrolledW.h>
#include <Xm/Separator.h>
#include <Xm/MainW.h>
#include <Xm/Frame.h>
#include <Xm/Text.h>
#ifdef TREE
#include "Tree.h"
#else
#include "Graph.h"
#include "Arc.h"
#endif

/* Original Defs */
#ifdef	STATS
# include	<pwd.h>
# include	<grp.h>
#endif	/* STATS */

#ifdef	NEWDIR
# if	DIRENT
#  include	<dirent.h>
#  define	direct	dirent
# else
#  if	NDIR
#   include	<sys/ndir.h>
#  else
#   include	<sys/dir.h>
#  endif
# endif /* DIRENT */
#else
# include	<sys/dir.h>
#endif	/* NEWDIR */

/* default column length when -v is given */
#ifdef unos
#define DEFCOLWID	30
#else
#define DEFCOLWID       14
#endif

#include	<sys/param.h>	/* for MAXPATHLEN */
#ifndef MAXPATHLEN
# ifdef LPNMAX		/* Xenix from stdio.h */
#  define MAXPATHLEN (LPNMAX - 1)
# else
#  include <limits.h>	/* try somewhere else */
#  define MAXPATHLEN PATH_MAX
# endif
#endif

#ifndef MAXNAMLEN	/* defined with NEWDIR routines */
# ifdef LFNMAX		/* Xenix again */
#  define MAXNAMLEN (LFNMAX - 1)
# else
#  define MAXNAMLEN      DEFCOLWID
# endif
#endif

#define	DEPTH 10	/* maximum depth that dtree will go */
#define	INITIAL_ELEM	100	/* initial # of elements for list of files */

#define	FFIRST	2	/* sort files first */
#define DFIRST	1	/* sort directories first */
#define	FAIL	-1	/* failure return status of sys calls */
#define	GREATER	1	/* return value of strcmp if arg1 > arg2 */
#define	LESSTHAN -1	/* return value of strcmp if arg1 < arg2 */
#define	SAME	0	/* return value of strcmp if arg1 == arg2 */

#ifdef	STATS
char *getmode();
char *guid();
char *ggid();
struct passwd *getpwuid();
struct group *getgrgid();
#endif /* STATS */

#ifdef	SYSV
#define	rindex	strrchr
#endif /* SYSV */

char *getwd();
char *rindex();
/* 
int qsort();
char *malloc();
char *realloc();
*/
long time();

int compar();			/* comparison routine for qsort */
char *xmalloc();
char *xrealloc();

#ifdef	SYSV
#define getwd(buf) getcwd((buf), MAXPATHLEN + 1)
#endif /* SYSV */

int	Index = 0;		/* current element of list[]	*/
int     CLength = 0;		/* max length of a column       */
int	All = 0;		/* all != 0; list non-directory entries */
int	File_dir = 0;		/* flag for how to sort */
int	Sort = 1;		/* flag to cause sorting of entries */
int	Point = 1;		/* skip point files if set	*/
int	Header = 0;		/* print verbose header */
int	Maxes[DEPTH];		/* array keeps track of max length in columns */
int	Level = 0;		/* counter for how deep we are	*/
int	Device;			/* device that we are starting tree on */
int	Xdev = 1;		/* set to allow crossing of devices */
int	Varspaces = 1;		/* set to allow compaction of column width */
#ifdef	STATS
int	Gflag = 0;		/* set for group stats instead of owner */
int	Longflg = 0;		/* set for long listing */
int	Compact = 0;		/* set for shortened long listing */
#endif	/* STATS */
#undef Status
struct	stat Status;
#ifdef  S_IFLNK
struct  stat Lstat;   /* stat of link, if there is one */
#endif  /* S_IFLNK */

struct entry {
	int next;			/* index to next element in list */
					/* could be a ptr, but realloc() */
					/* might screw us then */
#ifdef	STATS
	off_t	e_size;			/* size in blocks */
	unsigned short	e_mode;		/* file mode */
	short	e_uid;			/* uid of owner */
	short	e_gid;			/* gid of owner */
#endif	/* STATS */
	unsigned short dir : 1;		/* entry is a directory */
	unsigned short last : 1;	/* last entry in the dir. */
	unsigned short dev : 1;		/* set if same device as top */
	unsigned short end : 13;	/* index of last subdir entry*/
	char	e_name[MAXNAMLEN + 1];	/* name from directory entry */
	char    path_name[MAXPATHLEN + 1]; /* path name */
} *List, *SaveList;

unsigned Size;				/* how big of space we've malloced */

char	*Spaces;	/* used for output */
char	Buf1[BUFSIZ];	/* buffers for stdio stuff.  We don't want	*/
#ifndef NEWDIR
char	Buf2[BUFSIZ];	/* anyone calling malloc, because then		*/
			/* realloc() will have to move the whole list	*/
#endif
#define FS(str) (void) fprintf(stderr,str)

help()
{
					fprintf(stderr,
#ifdef	STATS
		"Usage: dtree [-aDfghHlNpstvx] [-c linelength] [directory ... ]\n"
#else	/* STATS */
		"Usage: dtree [-aDfhHNptvx] [-c linelength] [directory ... ]\n"
#endif	/* STATS */
						);
FS("   options  -a) include non-directory entries in listing\n");
FS("            -D) sort tree with directories at the top\n");
FS("            -f) sort tree with files at the top\n");
FS("            -h) help - prints this message\n");
FS("            -H) display a header at top\n");
FS("            -x) do not cross mounted file systems.\n");
FS("            -p) include files starting with a . (except . and ..)\n");
FS("            -N) do not sort the tree\n");
FS("            -t) Use terminal mode, don't display on an X window\n\n");
FS("            -l level) Search only up to the specified level \n\n");
FS("            Valid only if -t is used. Under default mode, a click on a \n");
FS("            node pops up a menu with mode, user and group ids.\n\n");
FS("            -S) print stats with each listing\n");
FS("                if both g & l flags are given, both owner and\n");
FS("                group will be printed\n");
FS("            -g) same as l, but use group name instead of user name \n");
FS("            -s) use shorter stats. Implies -S if -g isn't given.\n");
FS("            -v) variable length columns \n");
FS("            -c length) set max column length to -length-\n");
FS("  X-Window version 1.1 4/16/90 \n");
}
int newlevel = DEPTH;
Widget boxes[11], options;
Widget file_menu, dir_menu;
struct entry *file_pointer;
main(argc, argv)
char	**argv;
int	argc;
{
	extern int optind;
	extern char *optarg;
	register int i;
	char	top[MAXPATHLEN + 1];	/* array for treetop name */
	char	home[MAXPATHLEN + 1];	/* starting dir for multiple trees */
	char	*ptr;

	Widget toplevel;
	Widget sw, tree, q_button, w_button;
	Widget menu_bar, frame, main_w, title;
	Arg    wargs[10];
	int    flagX=1;			/* use X windows as default */
	Widget quit_menu, exit_b, reorient;
	int really_quit, n;

	setbuf(stdout, Buf1);

	while ((i = getopt (argc, argv,
#ifdef STATS
			    "haDfgHl:NpsSvxtc:"
#else
			    "haDfHl:Npvxtc:"
#endif /* STATS */
			    )) != EOF) {
		switch (i) {
				case 'a':
					All = 1;
					break;
				case 'c':
					CLength = atoi(optarg);
					if (CLength > MAXNAMLEN)
						CLength = MAXNAMLEN;
					else if (CLength < 1)
						CLength = DEFCOLWID;
					break;
				case 'l':
					newlevel = atoi(optarg);
					if(newlevel > DEPTH) {
					newlevel = DEPTH;
					}
					break;
				case 'D':
					File_dir = DFIRST;
					break;
				case 'f':
					File_dir = FFIRST;
					break;
				case 'H':
					Header = 1;
					break;
				case 'N':
					Sort = 0;
					break;
				case 'p':
					Point = 0;
					break;
				case 'v':
					Varspaces = 0;
					break;
				case 'x':
					Xdev = 0;
					break;
				case 't':
					flagX = 0;
					break;

				case 'h':
					help();
					exit(0);
#ifdef	STATS
				case 'g':
					Gflag = 1;
					break;
				case 'S':
					Longflg = 1;
					break;
				case 's':
					Compact = 1;
					break;
#endif	/* STATS */
				default:
					help();
					exit(FAIL);
				}
	}
#ifdef	STATS
	if (Compact && !Gflag)
		Longflg = 1;
#endif	/* STATS */
	if (CLength == 0)
		CLength = Varspaces ? MAXNAMLEN : DEFCOLWID;

	/* Establish where we are (our home base...) */
	if (getwd(home) == 0) {
		fprintf(stderr,
			"dtree: Cannot get initial directory: %s\n", home);
		exit(1);
	}

	Spaces = xmalloc(MAXNAMLEN+2);
	for(i = 0; i <= MAXNAMLEN; i++)
		Spaces[i] = ' ';
	Spaces[i] = '\0';

	/* Get initial Storage space */
	Size = sizeof(struct entry) * INITIAL_ELEM;
	SaveList = (struct entry *)xmalloc(Size);

	/* adjust for no specified directory */
	if (optind == argc )
		if(optind > 1) argv[--optind] = ".";
		else { argv[1] = ".", optind = 1; argc = 2;}

	if (Header)
		print_date();

	/* walk down the rest of the args, treeing them one at at time */
	for (; optind < argc; optind++) {
		if (chdir(home) == -1) {
			fprintf(stderr, "dtree: Cannot change to initial directory ");
			perror(home);
			exit(1);
		}
		strncpy (top, argv[optind], MAXPATHLEN);

		if (chdir(top) == FAIL) {
			fprintf(stderr, "dtree: Cannot change to top directory ");
			perror(top);
			continue;
		} else if (getwd(top) == 0) {
			fprintf(stderr,"dtree: Cannot get current directory: %s\n", top);
			continue;
		}

		List = SaveList; Index = 0;
		getwd(List[0].path_name);
		ptr = rindex(top, '/');

		if (!ptr || *++ptr == '\0')
			strncpy(List[Index].e_name, top, MAXNAMLEN);
		else
			strncpy(List[Index].e_name, ptr, MAXNAMLEN);

		if(stat(top, &Status) == FAIL) {
			fprintf(stderr, "dtree: Cannot stat directory ");
			perror(top);
			continue;
		}
		Device = Status.st_dev;
		List[0].dir = 1;
		List[0].last = 1;
		List[0].next = 1;
#ifdef	STATS
		List[0].e_mode = Status.st_mode;
		List[0].e_uid = Status.st_uid;
		List[0].e_gid = Status.st_gid;
		List[0].e_size = Status.st_size;
#endif	/* STATS */
		Index = 1;
		for (i = 1; i < DEPTH; i++)
			Maxes[i] = 0;
		Maxes[0] = stln(List[0].e_name);
		Level = 1;

		/* Don't waste time if there is no display */
		if(flagX == 1)
		toplevel = XtInitialize(argv[0], "Dtree", NULL, 0, &argc, argv);

		fprintf(stderr,"Started searchin...Please wait \n");
		/* search the tree */
		List[0].end = t_search(top, &List[0]);

		if (Index == 1)				/* empty tree */
			List[0].next = 0;

		if (Header) {
		if (All)
		    printf("\nDirectory structure and contents of %s\n", top);
		else
		    printf("\nDirectory structure of %s\n", top);
		if (Point)
			printf("(excluding entries that begin with '.')\n");
		}

		if(flagX == 0) pt_tree();
		else {

	/* Create a MainWindow, with a MenuBar and a Frame window
	   Put on the Frame, a ScrolledWindow and the Tree window
	   The MenuBar has a quit button and the directory Name
	*/
			fprintf(stderr,"Opening display now...Please wait\n");

			/* main window */
			main_w = XmCreateMainWindow(toplevel,"mainw",NULL,0);
			XtManageChild(main_w);

			/* menu bar */
			menu_bar = XmCreateMenuBar(main_w,"menu",NULL,0);
			XtManageChild(menu_bar);

			/* frame */
			XtSetArg(wargs[0], XmNshadowType, XmSHADOW_OUT);
			frame = XmCreateFrame(main_w,"frame",wargs,1);
			XtManageChild(frame);

			/* quit button */

			quit_menu = XmCreatePulldownMenu(menu_bar,
				    "quit_menu",NULL,0);
			XtSetArg(wargs[0], XmNsubMenuId,quit_menu);
			q_button = XmCreateCascadeButton(menu_bar, 
						   "quit",wargs,1);
			XtManageChild(q_button);

		       w_button =XtCreateManagedWidget("warning",
				xmLabelWidgetClass,quit_menu, NULL,0);
		w_print(w_button,"Don't exit unless all children are dead\n");
   			exit_b = XtCreateManagedWidget("Exit", 
				  xmPushButtonWidgetClass, quit_menu, NULL, 0);
   			XtAddCallback(exit_b, XmNarmCallback, 
				arm_callback, &really_quit);
   			XtAddCallback(exit_b, XmNdisarmCallback, 
				disarm_callback, &really_quit);
   			XtAddCallback(exit_b, XmNactivateCallback, 
				activate_callback, &really_quit);     

#ifndef TREE
			/* Reorient button */
			reorient = XmCreateCascadeButton(menu_bar,"ReOrient",
					      NULL,0);
			XtManageChild(reorient);
#endif
			/* Create the options pull-down menu */
			file_pointer = &List[0];
			show_menu(menu_bar,file_pointer);

			/* title (path) widget */

			title = XtCreateManagedWidget("title", 
			       xmCascadeButtonWidgetClass, menu_bar, NULL,0);
			XtSetArg(wargs[0],XmNmenuHelpWidget, title);
			XtSetValues(menu_bar, wargs, 1);
			w_print(title,top);

			XtSetArg(wargs[0], XmNscrollingPolicy, XmAUTOMATIC);
			sw = XtCreateManagedWidget("swindow",
				xmScrolledWindowWidgetClass, frame, wargs, 1);
			/* Tree widget */
#ifdef TREE
			tree = XtCreateManagedWidget("tree",XstreeWidgetClass,
					sw, NULL,0);
#else
			n=0;
			XtSetArg(wargs[n],XmNautoLayoutMode, TRUE); n++;
			XtSetArg(wargs[n],XmNsiblingSpacing, 5); n++;
			XtSetArg(wargs[n],XmNarcDrawMode,XmPOSITION_FIXED);n++;
			tree = XmCreateGraph(sw,"tree", wargs,n);
			/* XmAddTabGroup(tree); */
			XtManageChild(tree);

			XtAddCallback(reorient, XmNactivateCallback,
							re_orient, tree);
#endif
			XmMainWindowSetAreas(main_w, menu_bar, NULL,
			   NULL, NULL, frame);
			show_dtree(tree);
			clickB(options,file_pointer,NULL);
			XtRealizeWidget(toplevel);
			XtMainLoop();
		}
		
	}
	exit(0) ;
}


t_search(dir, addrs)
char *dir;
struct	entry *addrs;
{
	int	bsort;			/* index to begin sort */
	int	stmp;			/* save temporary index value */
	struct entry *sstep;		/* saved step in list */
	int	nitems;			/* # of items in this directory */
#ifdef	NEWDIR
	DIR	*dirp;			/* pointer to directory */
#else
	FILE	*dirp;
#endif
	char	sub[MAXNAMLEN+1];	/* used for subdirectory names */
	int	i;
#ifdef	NEWDIR
	struct direct *dp;
#else
	struct direct dirent;
	struct direct *dp = &dirent;
#endif	/* NEWDIR */
	int	n_subs = 0;
	int	tmp = 0;

#ifdef	NEWDIR
	dirp = opendir(".");
#else
	dirp = fopen(".", "r");
#endif	/* NEWDIR */
	if (dirp == NULL) {
		fprintf(stderr, "dtree: Cannot open directory ");
		perror(dir);
		return(0);
	}
#ifndef	NEWDIR
	setbuf(dirp, Buf2);
#endif	/* NEWDIR */

	bsort = Index;
	sstep = &List[bsort]; /* initialize sstep for for loop later on */
	nitems = Index;
	/* get the entries of the directory that we are interested in */
#ifndef	NEWDIR
	while (fread((char *)(dp), sizeof(struct direct), 1, dirp) == 1) {
#else
	while ((dp = readdir(dirp)) != NULL) {
#endif	/* NEWDIR */

		if (dp->d_ino
#ifdef unos
		    == -1
#else
		    == 0
#endif /* unos */
		    || (strcmp(dp->d_name, ".") == SAME)
		    || (strcmp(dp->d_name, "..") == SAME)
		    || (Point && dp->d_name[0] == '.'))
				continue;

		strncpy(sub, dp->d_name, MAXNAMLEN);
#ifdef S_IFLNK
		if (lstat(sub,&Lstat) == FAIL) {
			fprintf(stderr, "dtree: In directory %s, cannot lstat entry ", dir);
			perror(sub);
			continue;
                }
#endif /* S_IFLNK */
		if (stat(sub, &Status) == FAIL) {
			fprintf(stderr, "dtree: In directory %s, cannot stat entry ", dir);
			perror(sub);
			continue;
		}
#ifdef S_IFLNK
		if (((Lstat.st_mode & S_IFMT) == S_IFLNK)  &&
		    ((Status.st_mode & S_IFMT) == S_IFDIR)) 
		        List[Index].dir = 0;	
		else if ((((Lstat.st_mode & S_IFMT) == S_IFLNK) &&
			((Status.st_mode & S_IFMT) != S_IFDIR)) && (All)) 
                        List[Index].dir = 0;
#endif /* S_IFLNK */
		else if ((Status.st_mode & S_IFMT) == S_IFDIR)
			List[Index].dir = 1;
		else if (All)
			List[Index].dir = 0;
		else
			continue;
		strncpy(List[Index].e_name, dp->d_name, MAXNAMLEN);
		getwd(List[Index].path_name);
		List[Index].last = 0;
		List[Index].end = 0;
#ifdef S_IFLNK
		if ((Lstat.st_mode & S_IFMT) == S_IFLNK) {
		     List[Index].dev = (Device == Lstat.st_dev);
		     List[Index].e_mode = Lstat.st_mode;
		     List[Index].e_uid = Lstat.st_uid;
		     List[Index].e_gid = Lstat.st_gid;
		     List[Index].e_size = Lstat.st_size;
                }
                else {
#endif /* S_IFLNK */
		     List[Index].dev = (Device == Status.st_dev);
#ifdef STATS
		     List[Index].e_mode = Status.st_mode;
		     List[Index].e_uid = Status.st_uid;
		     List[Index].e_gid = Status.st_gid;
		     List[Index].e_size = Status.st_size;
#endif /* STATS */
#ifdef S_IFLNK
                }
#endif /* S_IFLNK */
		if (stln(List[Index].e_name) > Maxes[Level])
			Maxes[Level] = stln(List[Index].e_name);
		++Index;
		if (Index*sizeof(struct entry) >= Size) {
			Size += 20*sizeof(struct entry);
			List = (struct entry *)xrealloc((char *)List, Size);
		}
	}
#ifdef	NEWDIR
	closedir(dirp);
#else
	fclose(dirp);
#endif	/* NEWDIR */

	nitems = Index - nitems;	/* nitems now contains the # of */
					/* items in this dir, rather than */
					/* # total items before this dir */

	if (Sort)
		qsort(&List[bsort], nitems, sizeof(struct entry), compar);

	List[Index-1].last = 1;	/* mark last item for this dir */
	n_subs = nitems;
	stmp = Index;

	/* now walk through, and recurse on directory entries */
	/* sstep was initialized above */

	for (i = 0; i < nitems; sstep = &List[stmp - nitems+(++i)]) {
		if (sstep->dir && (Level < newlevel) && (Xdev || sstep->dev)) {
			sstep->next = Index;
			strncpy(sub, sstep->e_name, MAXNAMLEN);
			tmp = n_subs;
			Level++;
			if (chdir(sub) == FAIL) {
				fprintf(stderr,
					"dtree: Cannot change to directory %s/", dir);
				perror(sub);
			} else {
				n_subs += t_search(sub, sstep);
				if (chdir("..") == FAIL) {
					fprintf(stderr,
						"dtree: %s/%s lacks '..' entry\n",dir, sub);
					exit(1);
				}
			}
			--Level;
			if (n_subs - tmp <= 0)
				sstep->next = 0;
			else
				--n_subs;
		}
		else
			sstep->next = 0;
	}
	addrs->end = (unsigned)n_subs;
	return(n_subs);
}

/*
 *	comparison routine for qsort
 */

compar(a, b)
struct entry *a, *b;
{
	if (!File_dir)		/* straight alphabetical */
		return(strncmp(a->e_name, b->e_name, MAXNAMLEN));

	/* sort alphabetically if both dirs or both not dirs */

	if ((a->dir && b->dir) || (!a->dir && !b->dir))
		return(strncmp(a->e_name, b->e_name, MAXNAMLEN));

	if (File_dir == FFIRST) {	/* sort by files first */
		if (a->dir)
			return(GREATER);
		else
			return(LESSTHAN);
	}

	if (a->dir)			/* sort by dir first */
		return(LESSTHAN);
	else
		return(GREATER);
}


pt_tree()
{
	register int	i,j;
	struct entry *l;
	struct entry *hdr[DEPTH];
	int posit[DEPTH];		/* array of positions to print dirs */
	int con[DEPTH];			/* flags for connecting up tree */
	char	flag = 0;		/* flag to leave blank line after dir */
	struct	entry *stack[DEPTH];	/* save positions for changing levels */
	int	top = 0;		/* index to top of stack */
	int	count = 1;		/* count of line of output */

	Level = 0;			/* initialize Level */

	/* this loop appends each entry with dashes or spaces, for */
	/* directories or files respectively */

	for (i = 0; i < Index; i++) {
		for (j = 0; j < MAXNAMLEN; j++) {
			if (!List[i].e_name[j])
				break;
		}
		if (List[i].dir) {
			for (; j < MAXNAMLEN; j++)
				List[i].e_name[j] = '-';
		} else {
			for (; j < MAXNAMLEN; j++)
				List[i].e_name[j] = ' ';
		}
	}

	/* adjust the Maxes array according to the flags */

	for (i = 0; i < DEPTH; i++) {
		if (Varspaces) {
			if (Maxes[i] > CLength )
				Maxes[i] = CLength;
		} else
			Maxes[i] = CLength;
	}

	/* clear the connective and position flags */

	for (i = 0; i < DEPTH; i++)
		con[i] = posit[i] = 0;

	/* this is the main loop to print the tree structure. */
	l = &List[0];
	j = 0;
	for (;;) {
		/* directory entry, save it for later printing */
		if (l->dir != 0 && l->next != 0) {
			hdr[Level] = l;
			posit[Level] = count + (l->end + 1)/2 - 1;
			flag = 1;
			stack[top++] = l;
			l = &List[l->next];
			++Level;
			continue;
		}

#ifdef	STATS
	do_it_again:
#endif	/* STATS */
		/* print columns up to our entry */
		for (j = 0; j < (flag ? Level-1 : Level); j++) {
			if (!flag && posit[j] && posit[j] <= count) {
				/* time to print it */
				if (hdr[j]->e_name[CLength-1] != '-')
					hdr[j]->e_name[CLength-1] = '*';
				printf("|-%.*s",Maxes[j],hdr[j]->e_name);
				posit[j] = 0;
				if (hdr[j]->last != 0)
				    con[j] = 0;
				else
				    con[j] = 1;
#ifdef	STATS
				if (Gflag || Longflg) {
				    if ((i = j+1) <= Level)
					printf("| %.*s", Maxes[i], Spaces);
				    for (i++; i <= Level; i++) {
					printf("%c %.*s",
					    (con[i] ? '|' : ' '),
					    Maxes[i], Spaces);
				    }
				    if (!Compact) {
					printf("%s ", getmode(hdr[j]->e_mode));
					if (Longflg)
					   printf("%8.8s ",guid(hdr[j]->e_uid));
					if (Gflag)
					   printf("%8.8s ",ggid(hdr[j]->e_gid));
					printf("%7ld\n",
					    (hdr[j]->e_size+511L)/512L);
				    } else {
					printf(" %04o ",hdr[j]->e_mode & 07777);
					if (Longflg)
					    printf("%5u ", hdr[j]->e_uid);
					if (Gflag)
					    printf("%5u ", hdr[j]->e_gid);
					printf("%7ld\n",
					    (hdr[j]->e_size+511L)/512L);
				    }
				    goto do_it_again;
				}
#endif	/* STATS */
			} else
				printf("%c %.*s", (con[j] ? '|' : ' '),
					Maxes[j], Spaces);
		}
		if (flag) {	/* start of directory, so leave a blank line */
			printf(con[j] ? "|\n" : "\n");
			flag = 0;
			continue;
		} else {
				/* normal file name, print it out */
			if (l->e_name[CLength-1] != '-' &&
			    l->e_name[CLength-1] != ' ')
			    l->e_name[CLength-1] = '*';
			printf("|-%.*s",Maxes[Level],l->e_name);
			if (l->last) {
				con[j] = 0;
			} else {
				con[j] = 1;
			}
#ifdef	STATS
			if (Gflag || Longflg) {
				if (Compact) {
					printf(" %04o ", l->e_mode & 07777);
					if (Longflg)
					    printf("%5u ", l->e_uid);
					if (Gflag)
					    printf("%5u ", l->e_gid);
					printf("%7ld",
					    (l->e_size+511L)/512L);
				} else {
					printf("%s ", getmode(l->e_mode));
					if (Longflg)
					    printf("%8.8s ",guid(l->e_uid));
					if (Gflag)
					    printf("%8.8s ",ggid(l->e_gid));
					printf("%7ld",
					    (l->e_size+511L)/512L);
				}
			}
#endif	/* STATS */
		}
		printf("\n");

		if (l->last) {
			/* walk back up */
			while (l->last) {
				--Level;
				if (--top <= 0)
					return;
				l = stack[top];
			}
		}
		l = &l[1];
		++count;
	}
}

#ifdef	STATS

char *
guid(uid)
short uid;
{
	static char tb[10];
	struct passwd *pswd;

	pswd = getpwuid(uid);
	if (pswd == NULL)
		sprintf(tb,"%u", uid);
	else
		sprintf(tb, "%8s", pswd->pw_name);
	return(tb);
}

char *
ggid(gid)
short gid;
{
	static char tb[10];
	struct group *grp;

	grp = getgrgid(gid);
	if (grp == NULL)
		sprintf(tb,"%u", gid);
	else
		sprintf(tb, "%8s", grp->gr_name);
	return(tb);
}

/* take the mode and make it into a nice character string */

char	*
getmode(p_mode)
unsigned short p_mode;
{
	static char a_mode[16];
	register int	i = 0, j = 0;

	a_mode[j++] = ' ';

	switch (p_mode & S_IFMT) {
#ifdef S_IFLNK
	case S_IFLNK:
		a_mode[j++] = 'l';
		break;
#endif /* S_IFLNK */
	case S_IFDIR:
		a_mode[j++] = 'd';
		break;
#ifdef	S_IFMPC		/* defined in stat.h if you have MPX files */
	case S_IFMPC:
		a_mode[j-1] = 'm';
		/* FALL THROUGH */
#endif	/* S_IFMPC */
	case S_IFCHR:
		a_mode[j++] = 'c';
		break;
#ifdef	S_IFMPB		/* defined in stat.h if you have MPX files */
	case S_IFMPB:
		a_mode[j-1] = 'm';
		/* FALL THROUGH */
#endif	/* S_IFMPB */
	case S_IFBLK:
		a_mode[j++] = 'b';
		break;
	case S_IFREG:
	default:
		a_mode[j++] = (p_mode & S_ISVTX) ? 't' : ' ';
		break;
	}
	a_mode[j++] = ' ';
	for( i = 0;i<3;i++ ) {
		a_mode[j++] = (p_mode<<(3*i) & S_IREAD) ? 'r' : '-';
		a_mode[j++] = (p_mode<<(3*i) & S_IWRITE) ? 'w' : '-';
		a_mode[j++] = (i<2 && (p_mode<<i & S_ISUID)) ? 's' :
			      ((p_mode<<(3*i) & S_IEXEC ) ? 'x' : '-');
		a_mode[j++] = ' ';
	}
	a_mode[j] = '\0';
	return(a_mode);
}
#endif

/* like strlen, but returns length up to MAXNAMLEN-1 */
stln(st)
register char *st;
{
	register int t;

	for (t=0; t<MAXNAMLEN-1; ++t)
		if (!st[t])
			return (++t);
	return (++t);
}

print_date()
{
	long now;

	time(&now);
	printf ("%s", ctime(&now));
}

void
memory_out()
{
	fprintf(stderr, "dtree: Virtual memory exhausted\n");
	exit(1);
}

/* Allocate `size' bytes of memory dynamically, with error checking.  */

char *
xmalloc (size)
unsigned size;
{
	char *ptr;

	ptr = malloc (size);
	if (ptr == 0 && size != 0)
		memory_out ();
	return ptr;
}

/* Change the size of an allocated block of memory `ptr' to `size' bytes,
   with error checking.
   If `ptr' is NULL, run xmalloc.
   If `size' is 0, run free and return NULL.  */

char *
xrealloc (ptr, size)
char *ptr;
unsigned size;
{
	if (ptr == 0)
		return xmalloc (size);
	if (size == 0) {
		free (ptr);
		return 0;
	}
	ptr = realloc (ptr, size);
	if (ptr == 0 && size != 0)
		memory_out ();
	return ptr;
}
/*-------------------------New functions for X windows -----------------*/

/* Main program to display the tree in an X window */

show_dtree(parent)
	Widget parent;

{
	Arg wargs[4];
	struct entry *stack[DEPTH], *branch;
	int n, top=0, *dir_index;
	int dir_node=0, dir_count=0, i;
	Widget parent_node=NULL, *Dir, arc;
	XmString label_s = NULL;	

	/* Find the number of directories */
	for(i=0; i < Index; i++)
		if(List[i].dir == 1) dir_count++;

	if((Dir = (Widget *) malloc( dir_count * sizeof(Widget))) == NULL){
		fprintf(stderr,"malloc failure\n");
	        exit(1);
	}
	if((dir_index = (int *) malloc(dir_count * sizeof(int))) == NULL) {
		fprintf(stderr,"malloc failure\n");
		exit(1) ;
	}

	branch = &List[0];

	if(dir_count == 1 && branch->next == 0) {
		if(All == 0) {
		fprintf(stderr,"No subdirectories under directory %s\n",
		     branch->e_name);
		fprintf(stderr,"You may want to try the -ap option\n");
		}
		else fprintf(stderr,"No files under directory %s\n", 
		     branch->e_name);
		exit(0);
	}

	/* Distinguish between directories and files */

	for(;;)  {
		if(branch->dir != 0 && branch->next != 0) {
			dir_index[top] = dir_node;
			stack[top++] = branch;
			n=0;
#ifdef TREE
			XtSetArg(wargs[n], XtNsuperNode,parent_node); n++;
#endif
			label_s = XmStringCreateLtoR(branch->e_name,
			                      XmSTRING_DEFAULT_CHARSET);
			XtSetArg(wargs[n], XmNlabelString,label_s); n++;
			Dir[dir_node] = XmCreatePushButton(parent,"dir",
			                        wargs,n);
			XtManageChild(Dir[dir_node]);
			XtAddCallback(Dir[dir_node],XmNactivateCallback,
			       clickB, branch);

#ifndef TREE
			/* create the arc */
			if(parent_node) {
			n=0;
			XtSetArg(wargs[n], XmNarcDirection, XmDIRECTED); n++;
			XtSetArg(wargs[n], XmNto, Dir[dir_node]); n++;
			XtSetArg(wargs[n], XmNfrom, parent_node); n++;
			arc = XmCreateArc(parent, branch->e_name,wargs,n);
			XtManageChild(arc);
			}
#endif

			branch = &List[branch->next];
			parent_node = Dir[dir_node++];
			continue;
		}
		create_file_node(parent,branch,parent_node);
		if(branch->last) {
			while(branch->last) {
				if(--top <= 0 ) {
					free(Dir);
					free(dir_index);
					return;
					}
				branch = stack[top];
				parent_node = Dir[dir_index[top-1]];
				}
			}
		branch = &branch[1];
	}
}
/* Function that creates a widget for a node. Three types are available,
   dir, for directories , file, for files, and sym_link for
   symbolic links
*/
create_file_node(pparent,bbranch,nnode)
	Widget pparent, nnode;
	struct entry *bbranch;
{
	Widget w, arc;
	Arg wargs[4];
	int n;
	char *win_type;
	XmString label_s = NULL;	
	int Gflag=1;		/* this is a gadget */


	n=0;
	label_s = XmStringCreateLtoR(bbranch->e_name,
	                      XmSTRING_DEFAULT_CHARSET);
	XtSetArg(wargs[n], XmNlabelString,label_s); n++;

	/* Check the type of the node */
	if(bbranch->dir == 1) { 
		win_type = "dir";
		Gflag = 0;
	}
#ifdef STATS
	else if(strncmp(" l",getmode(bbranch->e_mode),2) == 0)
			{win_type = "sym_link"; Gflag = 0;}
#endif /* STATS */
	else win_type = "file";
#ifdef TREE
        XtSetArg(wargs[n], XtNsuperNode, nnode); n++; 
	w = XmCreatePushButton(pparent, win_type, wargs, n);
#else
	if(Gflag == 0)
	w = XmCreatePushButton(pparent, win_type, wargs, n);
	else
	w = XmCreatePushButtonGadget(pparent, win_type, wargs, n);
#endif
	XtManageChild(w);
	XtAddCallback(w,XmNactivateCallback,clickB,bbranch);

#ifndef TREE
	/* create the arcs */
	n=0;
	XtSetArg(wargs[n], XmNarcDirection, XmDIRECTED); n++;
	XtSetArg(wargs[n], XmNto, w); n++;
	XtSetArg(wargs[n], XmNfrom, nnode); n++;
	arc = XmCreateArc(pparent, bbranch->e_name,wargs,n);
	XtManageChild(arc);
#endif
}
/* Function that creates the pull-down menu when someone clicks on the 
  active node of the tree.  
*/

show_menu(pparent, bbranch)
	Widget pparent;
	struct entry *bbranch;
{
	Widget menu ;
	Widget submenu_dir, submenu_file;
	int i;
	char *mode_name;
	Arg  wargs[4];

	menu = XmCreatePulldownMenu(pparent,"menu",NULL, 0);

	XtSetArg(wargs[0], XmNsubMenuId,menu);
	options = XtCreateManagedWidget("options",xmCascadeButtonWidgetClass,
		   pparent, wargs, 1);
	w_print(options, bbranch->e_name);
	/*
	boxes[0] = XtCreateManagedWidget("title",xmLabelWidgetClass, 
		   menu, NULL,0);
	XtCreateManagedWidget("separator",xmSeparatorWidgetClass, 
		   menu, NULL,0);
	*/
#ifdef STATS
	for(i = 1; i < 4 ; i++)
	boxes[i] =  XtCreateManagedWidget("id",xmLabelWidgetClass,
		   menu, NULL,0);
	button_print(bbranch); /* print the labels on the menu */

	XtCreateManagedWidget("separator2",xmSeparatorWidgetClass, 
		   menu, NULL,0);
	/* Create sub-menus */

#endif /*STATS */

	file_menu = XtCreateManagedWidget("File Options",
	    xmCascadeButtonWidgetClass, menu, NULL,0);
	dir_menu  = XtCreateManagedWidget("Directory Options",
	    xmCascadeButtonWidgetClass, menu, NULL,0);

	/* Create cascading sub-menus */

	/* If it is a file */
	submenu_file = XmCreatePulldownMenu(menu, "filesubmenu", NULL, 0);
	XtSetArg(wargs[0], XmNsubMenuId, submenu_file);
	XtSetValues(file_menu, wargs, 1);

	boxes[4] = XtCreateManagedWidget("view",xmPushButtonWidgetClass,
	       		        submenu_file, NULL,0);
		XtAddCallback(boxes[4], XmNactivateCallback,view, NULL);
		boxes[5] = XtCreateManagedWidget("topview",
			xmPushButtonWidgetClass, submenu_file, NULL,0);
		XtAddCallback(boxes[5], XmNactivateCallback,view, NULL);
		boxes[6] = XtCreateManagedWidget("edit",
			xmPushButtonWidgetClass, submenu_file, NULL,0);
		XtAddCallback(boxes[6], XmNactivateCallback,do_menu, NULL);
		boxes[7] = XtCreateManagedWidget("print",
			xmPushButtonWidgetClass, submenu_file, NULL,0);
		XtAddCallback(boxes[7], XmNactivateCallback,do_menu, NULL);

	/* if it a directory */
	submenu_dir = XmCreatePulldownMenu(menu, "dirsubmenu", NULL, 0);
	XtSetArg(wargs[0], XmNsubMenuId, submenu_dir);
	XtSetValues(dir_menu, wargs, 1);
		boxes[8] = XtCreateManagedWidget("show subtree",
			xmPushButtonWidgetClass, submenu_dir, NULL,0);
		XtAddCallback(boxes[8], XmNactivateCallback,do_menu, NULL);
		boxes[9] = XtCreateManagedWidget("list files",
			xmPushButtonWidgetClass, submenu_dir, NULL,0);
		XtAddCallback(boxes[9], XmNactivateCallback,do_menu, NULL);
		boxes[10] = XtCreateManagedWidget("list ../",
			xmPushButtonWidgetClass, submenu_dir, NULL,0);
		XtAddCallback(boxes[10], XmNactivateCallback,do_menu, NULL);
}
/* Not used here, but may be useful in the future !!! */
/* Copied from D. Young's book */
/*
void post_menu_handler(w, menu, event)
	Widget w, menu;
	XEvent *event;
{
	Arg wargs[10];
	int button;

 	XtSetArg(wargs[0], XmNwhichButton, &button); 
	XtGetValues(menu, wargs, 1);
		if(event->xbutton.button == button) {
		XmMenuPosition(menu, event);
		XtManageChild(menu);
		}
}
*/
/* Quick and dirty way to view a file in a pop-up window 
   Speed may improve, if one reads and displays page by page
   No check to see if the file is binary
*/
void view(w, call_mydata, call_data)
	Widget w;
	caddr_t call_data, call_mydata;

{
	struct entry *branch;
	char *filename[MAXPATHLEN+1];  
        struct stat statbuf;         /* Information on a file. */
        int file_length;             /* Length of file.        */
        unsigned char * file_string; /* Contents of file.      */
	FILE *fp = NULL;
	Widget text, toplevel2, sw2, button, frame, main_w, menu_bar;
	Widget title;
	XmString name_string = NULL;
	char *button_name = NULL;
	int topflag=0;
	Arg	wargs[10];
	int n=0;

	branch = file_pointer;
	/* find which button called us */
	XtSetArg(wargs[0], XmNlabelString, &name_string);
	XtGetValues(w,wargs,1);
	XmStringGetLtoR(name_string,XmSTRING_DEFAULT_CHARSET,&button_name);
	if(strcmp("topview",button_name) == 0) topflag = 1;

	/* find first the correct path */

	strncpy(filename,branch->path_name,MAXPATHLEN);
	strcat(filename,"/");
	strcat(filename,branch->e_name);
	

	if ((fp = fopen(filename, "r")) == NULL) {
		fprintf(stderr,"Can't open file name %s\n",filename);
		return;
	}
	 if (stat(filename, &statbuf) == 0)
	       file_length = statbuf.st_size;
         else
      		file_length = 1000000; /* arbitrary file length */

	 /* read the file string */

	 if(topflag == 1 && file_length >= 2000 ) file_length = 2000;

	 file_string = (unsigned char *) XtMalloc((unsigned)file_length);
         fread(file_string, sizeof(char), file_length, fp);

	 if (fclose(fp) != NULL) fprintf(stderr, "Warning: unable to close file.\n");

	/* Create another pop-pup top-shell with a MainWindow */

	toplevel2 = XtCreateApplicationShell("Dtreef",topLevelShellWidgetClass,
			 NULL,0);

	main_w = XmCreateMainWindow(toplevel2,"mainw",NULL,0);
	XtManageChild(main_w);
	menu_bar = XmCreateMenuBar(main_w,"menu",NULL,0);
	XtManageChild(menu_bar);
	XtSetArg(wargs[0], XmNshadowType, XmSHADOW_OUT);
	frame = XmCreateFrame(main_w,"frame",wargs,1);
	XtManageChild(frame);
	XtSetArg(wargs[0], XmNscrollingPolicy, XmAUTOMATIC);
	sw2 = XtCreateManagedWidget("swindowf",
		xmScrolledWindowWidgetClass,frame, wargs,1);
	XmMainWindowSetAreas(main_w, menu_bar, NULL, NULL, NULL, frame);
	button = XmCreateCascadeButton(menu_bar, "quit",NULL,0);
	XtManageChild(button);
	XtAddCallback(button,XmNactivateCallback,quit_b_pop,toplevel2);

	title = XtCreateManagedWidget("title", 
		       xmCascadeButtonWidgetClass, menu_bar, NULL,0);
	w_print(title, filename);


	/* Create Text widget */
	n=0;
        XtSetArg (wargs[n], XmNeditable, FALSE); n++; 

	text = XmCreateText(sw2, "text", wargs, n);

	/* added the file string to the text widget */

	   XmTextSetString(text, file_string);
        if(file_string != NULL) XtFree(file_string);
        if(name_string != NULL) XtFree(name_string);

	/* Poput the text file */

	XtPopup(toplevel2,XtGrabNone); 
	XtManageChild(text); 
}

/* CallBack function of the quit button on a pop-up window */

void quit_b_pop(w, topshell, call_data)
	Widget w;
	Widget topshell;
	caddr_t  call_data;
{
	 XtPopdown(topshell);
}

/* function that forks a command from the menu */
/* Needs work!!!!!
*/
void do_menu(w,call_mydata,call_data)
	Widget w;
	caddr_t call_data, call_mydata;

{
	struct entry *branch;
	char *filename[MAXPATHLEN+1];  
	char *topdir[MAXPATHLEN+1];
	XmString name_string = NULL;
	char *button_name = NULL;
	char *lpdest=NULL, *editor = NULL;
	char  *EDITOR="EDITOR\0";
	int pid, pathl,namel;
	Arg  wargs[3];

	
	
	/* find which button called us */

	branch = file_pointer;
	XtSetArg(wargs[0], XmNlabelString, &name_string);
	XtGetValues(w,wargs,1);
	XmStringGetLtoR(name_string,XmSTRING_DEFAULT_CHARSET,&button_name);


	/* find first the correct path - append the file name 
	    except for the "root" directory */

	strncpy(filename,branch->path_name,MAXPATHLEN);
	if(branch->next != 1) {
		strcat(filename,"/");
		strcat(filename,branch->e_name);
	}
	
	/* fork a new process */
	pid = fork();

	if(pid > 0) {
		/* wait((int *)0); */
		return;
	}

	if(pid == 0) {
	/* 	for(fd = 0; fd < _NFILE; fd++) close(fd); */
		if(strcmp(button_name,"print") == 0) {
			
			lpdest = XGetDefault(XtDisplay(w),"dtree","lpdest");
			if (lpdest != NULL){
		     execl("/usr/bin/lp","lp","-d",lpdest,filename,(char *)0);
			}
			else
			execl("/usr/bin/lp","lp",filename,(char *)0);
		}
		else if(strcmp(button_name,"edit") == 0) {

			editor = XGetDefault(XtDisplay(w),"dtree","editor");

			if (editor == NULL) { 
				 editor = getenv(EDITOR);
				 if(editor == NULL) 
				 	editor="/usr/bin/vi";
		     	 	 execl("/usr/bin/X11/hpterm",
							"hpterm","-e",editor,
				                  filename,(char *)0);
				 } else 
			execlp(editor,editor, filename,(char *)0);
		}
		else if(strcmp(button_name,"list files") == 0) {
		 	 execlp("dtree", "dtree", 
				   "-a", "-l","1",filename, (char *)0);
		}
		else if(strcmp(button_name,"list ../") == 0) {

			/* find the parent directory */
			pathl = strlen(filename);
			namel = strlen(branch->e_name);
			strncpy(topdir,branch->path_name,pathl-namel);

		 	execlp("dtree", "dtree", "-l","1",topdir, (char *)0);
		}
			
		else
		 execlp("dtree", "dtree", 
				  "-a",filename, (char *)0);

	fprintf(stderr,"%s command failed\n",button_name);
	}
	fprintf(stderr,"fork for %s command failed\n",button_name);
}

/* Function that updates the pop-up menu labels and permissions
   when someone clicks on a node.
*/
void clickB( w,branch,call_data)
	Widget w;
	struct entry *branch;
	caddr_t call_data;
{
	Arg wargs[3];
	XrmValue bgcolor;
	if(branch->dir == 1) {
		XtSetSensitive(file_menu ,FALSE);
		XtSetSensitive(dir_menu ,True);
	}
	else {
		XtSetSensitive(dir_menu ,False);
#ifdef STATS
		/* Don't do anything with symb. links */
		if(strncmp(" l",getmode(branch->e_mode),2) == 0)
			 XtSetSensitive(file_menu ,False);
		else
#endif /* STATS */
		XtSetSensitive(file_menu ,True);
	}
	w_print(options, branch->e_name);

	 /* Get the background color of the activated button 
	    Need to get the foreground color too
         */
	 _XmSelectColorDefault (w, NULL, &bgcolor);
	XtSetArg(wargs[0], XmNbackground, *((Pixel *) bgcolor.addr));
	XtSetValues(options, wargs, 1);
	file_pointer = branch;
	button_print(branch);
}

/* Print a string on a widget */

void w_print(w, string)
	Widget w;
	char *string;
{
	XmString xmstr;
	Arg wargs[2];

	xmstr = XmStringLtoRCreate(string, XmSTRING_DEFAULT_CHARSET);
	XtSetArg(wargs[0], XmNlabelString, xmstr);
	XtSetValues(w,wargs, 1);
}

/* print the labels on the popup menu */
void button_print(branch)
	struct entry *branch;
{
	char tmp[40];
	/* w_print(boxes[0], branch->e_name); */
#ifdef STATS
	strncpy(tmp,"Mode :",40);
	strcat(tmp,getmode(branch->e_mode));
	w_print(boxes[1],tmp);
	strncpy(tmp,"User :",40);
	strcat(tmp,guid(branch->e_uid));
	w_print(boxes[2], tmp);
	strncpy(tmp,"Group:",40);
	strcat(tmp, ggid(branch->e_gid));
	w_print(boxes[3], tmp);
#endif /* STATS */
}

/* 
 * Copied from D. Young's book  with some changes
 * Define three callbacks. Make them static - no need 
 * to make them known outside this file.
 */
static void arm_callback(w, flag, call_data)
     Widget     w;
     int       *flag;
     XmAnyCallbackStruct *call_data; 
{
 *flag = FALSE;
}
static void activate_callback(w, flag, call_data)
     Widget     w;
     int       *flag;
     XmAnyCallbackStruct *call_data; 
{
  *flag = TRUE;
}
static void disarm_callback(w, flag, call_data)
     Widget     w;
     int       *flag;
     XmAnyCallbackStruct *call_data; 
{
  if(*flag){
    XtCloseDisplay(XtDisplay(w));
    exit(0);
  }
}

/* function to re-orient the graph */
#ifndef TREE
void re_orient(w, graph, call_data)
	Widget w, graph;
	caddr_t  call_data;

{
	Arg wargs[2];
	XtSetArg(wargs[0], XmNreorient, True);
	XtSetValues(graph, wargs, 1);
}
#endif
@EOF

chmod 444 dtree.c

echo x - Makefile
cat >Makefile <<'@EOF'
# Makefile for dtree

# Things that might go in DEFS:
# -DSTATS -DNEWDIR -DDIRENT -DNDIR -DSYSV
# The -DTREE flag is used to create dtree with the Tree widget
# otherwise it uses the XmGraph widget.
DEFS = -D_HPUX_SOURCE -DSTATS -DNEWDIR -DSYSV #-DDIRENT -DS_IFLNK
CFLAGS = $(DEFS) -O
LDFLAGS = 
LIBS = # For Xenix use -lx with -DNDIR
LIBS = -lmalloc -lXm -lXt -lX11 -lm


BINDIR = /usr/local/bin/X11
MANDIR = /usr/local/man/man1

OBJECTS_A = dtree.o Arc.o Graph.o 	#using XmGraph
OBJECTS_B = dtree_tree.o Tree.o         #using D. Young's tree.

ARCH_FILES = dtree.1 dtree.c Makefile Dtree Tree.h TreeP.h \
             dtree-i.h Tree.c Arc.c Arc.h ArcP.h Graph.c Graph.h GraphP.h

ARCH_FILESB = dtree.1 dtree.c Makefile Dtree dtree-i.h Imakefile README


all: dtree_tree

dtree: $(OBJECTS_A)
	$(CC) -o dtree $(LDFLAGS) $(OBJECTS_A) $(LIBS)

dtree_tree: $(OBJECTS_B)
		$(CC) -o dtree $(LDFLAGS) $(OBJECTS_B) $(LIBS)

Arc.o:	Arc.c
	$(CC) -c $(CFLAGS) Arc.c +Ns4000

Graph.o:	Graph.c
		$(CC) -c $(CFLAGS) Graph.c +Ns4000

dtree_tree.o:	dtree.c
		$(CC) -o dtree_tree.o -c $(CFLAGS) -DTREE dtree.c

install: dtree dtree.1
	cp dtree $(BINDIR)
	cp dtree.1 $(MANDIR)

lint: dtree.c
	lint $(DEFS) dtree.c

shar: $(ARCH_FILESB)
	shar $(ARCH_FILESB) > dtree.shar_1
	shar Tree* > dtree.shar_2
dist: dtree.tar.Z

tar: dtree.tar.Z

dtree.tar.Z: $(ARCH_FILES)
	tar cf - $(ARCH_FILES) | compress > dtree.tar.Z

clean:
	rm -f dtree *.o core tags a.out
@EOF

chmod 644 Makefile

echo x - Dtree
cat >Dtree <<'@EOF'
#
# app-defaults file for dtree
#
dtree*geometry:                     500x500
dtree*dir.background:               Red
dtree*file.background:              Gray
dtree*sym_link.background:          Brown
dtree*quit.background:              DarkSlateBlue
dtree*options.background:	    Red	
dtree*XmGraph*childSpacing:         50              #distance between levels
Mwm*Dtree*iconImage:                /usr/local/include/X11/bitmaps/dtree-i.h
Hpwm*Dtree*iconImage:               /usr/local/include/X11/bitmaps/dtree-i.h
!dtree*lpdest:  		    rljet           #Printer destination
!dtree*editor:                      emacsclient     #window-clever editor
!dtree*sym_link.foreground:    
@EOF

chmod 644 Dtree

echo x - dtree-i.h
cat >dtree-i.h <<'@EOF'
#define load_i_width 50
#define load_i_height 50
static char load_i_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x20,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10,
   0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
   0x00, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
   0x00, 0xf8, 0x0f, 0x10, 0x00, 0x20, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00,
   0x20, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x20, 0x00, 0x00, 0x0f, 0x08,
   0x00, 0x00, 0x20, 0x00, 0x00, 0x09, 0x08, 0x10, 0x00, 0x20, 0x00, 0x80,
   0x08, 0x08, 0x00, 0x00, 0x20, 0x00, 0x80, 0xf8, 0x0f, 0x10, 0x00, 0x20,
   0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x40, 0x00, 0x00, 0x10,
   0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00,
   0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x20, 0xfe,
   0x13, 0xfe, 0xe3, 0x17, 0x00, 0x20, 0x02, 0x0a, 0x02, 0x22, 0x04, 0x00,
   0x20, 0xaa, 0x0a, 0x56, 0x23, 0x14, 0x00, 0x20, 0x02, 0xfe, 0x03, 0x3e,
   0x04, 0x00, 0x20, 0xaa, 0x0a, 0xaa, 0x22, 0x14, 0x00, 0x20, 0x02, 0x0a,
   0x02, 0x22, 0x04, 0x00, 0x20, 0xfe, 0x13, 0xfe, 0xe3, 0x17, 0x00, 0x20,
   0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x10,
   0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x40, 0xf8,
   0x0f, 0x10, 0x00, 0x20, 0x00, 0x40, 0x08, 0x08, 0x00, 0x00, 0x20, 0x00,
   0x80, 0x08, 0x08, 0x10, 0x00, 0x20, 0x00, 0x80, 0x08, 0x08, 0x00, 0x00,
   0x20, 0x00, 0x00, 0x0f, 0x08, 0x10, 0x00, 0x20, 0x00, 0x00, 0x08, 0x08,
   0x00, 0x00, 0x20, 0x00, 0x00, 0xf8, 0x0f, 0x10, 0x00, 0x20, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x20,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10,
   0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x55, 0x55, 0x55,
   0x55, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00};
@EOF

chmod 644 dtree-i.h

echo x - Imakefile
cat >Imakefile <<'@EOF'
#Imakefile to create dtree using the "tree" widget.

        DEFINES = -DSTATS -DNEWDIR -DSYSV -DTREE
LOCAL_LIBRARIES = 
  SYS_LIBRARIES = -lmalloc -lXm -lXt -lX11 -lm
           SRCS = dtree.c Tree.c
           OBJS = dtree.o Tree.o

ComplexProgramTarget(dtree)


@EOF

chmod 644 Imakefile

echo x - README
cat >README <<'@EOF'
This version of dtree is an X extension of the program originally
written by Dave Borman. It displays on a terminal or under X11
(using Motif widgets) the tree structure of a Unix directory tree.
On a color terminal, different colors can be used for files, directories,
and symbolic links. One can select nodes of the tree, and edit them,
view them, or print them. One can also traverse nodes not fully
shown on the original tree.

The program uses the "tree" widget described in D. Young's book on
X window programming (code included here). The program also runs
using the XmGraph widget, but because that code is not yet stable,
the code for the XmGraph widget is not included here.
To make the program run: make dtree_tree

The program runs faster with the "tree" widget, but the XmGraph
widgets may provide the potential for additional capabilities, such
as: removing files from the tree, copying of files and directories, etc.

Potential problem: because dtree creates a PushButton widget for every file
or directory shown, you may run out of memory. It is recommended that you
start with a moderate size tree, and create new ones as needed.
Experience will show how much you can push it!!
If you have a monochrome monitor, you may want to substitute those
widgets with gadgets.

For example: dtree -l 2
will show the tree of your current directory up to depth 2.
	     dtree -al 2
will show both files and directories up to depth 2.

K. Konstantinides
HP Labs
kk@hpkronos.hpl.hp.com
@EOF

chmod 644 README

exit 0

dan
----------------------------------------------------
O'Reilly && Associates   argv@sun.com / argv@ora.com
Opinions expressed reflect those of the author only.