/*
 *	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 [-adfgHlnpsvxR] [-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) display a header at top
 *		-l) 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 -l if -g isn't given.
 *		-v) variable length columns off
 *		-x) do not cross mounted file systems.
 *		-c length) set max column length to "length"
 *		-R) ignore RCS and SCCS directories
 */

 /*     Modified by      Ed Arnold      CSU-CS   (csu-cs!arnold)     3-5-84
  *
  *     Allows symbolic links to both directories and individual files.
  *     With a '-l' or '-al' 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
  */

/* 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.
 */

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

static  char Rcsid[] ="$Id: dtree.c,v 1.3 1994/06/15 22:21:02 tytso Exp $";

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

# include	<pwd.h>
# include	<grp.h>

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

/* 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 */

char *getmode();
char *guid();
char *ggid();
struct passwd *getpwuid();
struct group *getgrgid();

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

char *getwd();
char *malloc();
char *realloc();
char *rindex();
int qsort();
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	NoRCS = 0;		/* unset to allow inclusion of RCS/SCCS dirs */
int	Varspaces = 1;		/* set to allow compaction of column width */
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 */
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 */
	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 */
	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 */
} *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	*/

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;

	setbuf(stdout, Buf1);

	while ((i = getopt (argc, argv,
			    "adfgHRlnpsvxc:"
			    )) != 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 '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 'R':
					NoRCS = 1;
					break;
				case 'g':
					Gflag = 1;
					break;
				case 'l':
					Longflg = 1;
					break;
				case 's':
					Compact = 1;
					break;
				default:
					fprintf(stderr,
		"Usage: dtree [-adfgHRlnpsvx] [-c linelength] [directory ... ]\n"
						);
					exit(FAIL);
				}
	}
	if (Compact && !Gflag)
		Longflg = 1;
	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)
		argv[--optind] = ".";

	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;
		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;
		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;
		Index = 1;
		for (i = 1; i < DEPTH; i++)
			Maxes[i] = 0;
		Maxes[0] = stln(List[0].e_name);
		Level = 1;

		/* 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");
		}

		pt_tree();				/* print the tree */
	}
	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 */
	DIR	*dirp;			/* pointer to directory */
	char	sub[MAXNAMLEN+1];	/* used for subdirectory names */
	int	i;
	struct direct *dp;
	int	n_subs = 0;
	int	tmp = 0;

	dirp = opendir(".");
	if (dirp == NULL) {
		fprintf(stderr, "dtree: Cannot open directory ");
		perror(dir);
		return(0);
	}

	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 */
	while ((dp = readdir(dirp)) != NULL) {

		if (dp->d_ino
#ifdef unos
		    == -1
#else
		    == 0
#endif /* unos */
		    || (strcmp(dp->d_name, ".") == SAME)
		    || (strcmp(dp->d_name, "..") == SAME)
		    || (NoRCS && ((strcmp(dp->d_name, "RCS") == SAME) ||
				  (strcmp(dp->d_name, "CVS") == SAME) ||
				  (strcmp(dp->d_name, "SCCS") == 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);
		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);
		     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;
#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);
		}
	}
	closedir(dirp);

	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 && (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;
		}

	do_it_again:
		/* 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;
				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;
				}
			} 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;
			}
			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);
				}
			}
		}
		printf("\n");

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


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);
}

/* 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;
}
