/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 *
 */

/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"$Header: du.c 1.14 90/04/05 $@(#)du:du.c	1.12"
/*	du	COMPILE:	cc -O du.c -s -i -o du	*/

/*
**	du -- summarize disk usage
**		du [-ars] [name ...]
*/

#include	<stdio.h>
#include	<sys/types.h>
#include	<sys/ufsinode.h>
#include	<sys/statfs.h>
#include	<sys/stat.h>
#include	<sys/param.h>
#include	<sys/dir.h>
#include	<dirent.h>


#define EQ(x,y)	(strcmp(x,y)==0)
#define ML	500
#define DIRECT	10	/* Number of direct blocks */
#define MAXOPENFILES	20
#define BUFSIZE	512	/* logical block */

struct 	{
	dev_t	dev;
	ino_t	ino;
} ml[ML];
int	linkc = 0;

struct	stat	Statb;
struct	statfs	Statfsb;

char	path[256];

int	aflag = 0;
int	rflag = 0;
int	sflag = 0;
ulong	descend();

main(argc, argv)
char **argv;
{
	ulong blocks = 0;
	int c;
	int errflg = 0;
	extern char *optarg;
	extern int optind, opterr;

#ifdef STANDALONE
	if (argv[0][0] == '\0')
		argc = getargv("du", &argv, 0);
#endif
	while ((c=getopt(argc, argv, "ars")) != EOF)
		switch(c) {
		case 'a':
			aflag++;
			continue;

		case 'r':
			rflag++;
			continue;

		case 's':
			sflag++;
			continue;

		default:
			errflg++;
	}

	if (errflg) {
		fprintf(stderr,
		    gettxt("du:1", "usage: du [-ars] [name ...]\n"));
		exit(2);
	}

	if(argc == optind) {
		optind--;
		argv[optind] = ".";
	}
	for ( ; optind < argc; optind++) {
		strcpy(path, argv[optind]);
		blocks = descend(path);
		if(sflag)
			printf("%lu	%s\n", blocks, path);
	}

	exit(0);
}

ulong descend(name)
char *name;
{
	register struct dirent	*dp;
	register char	*c1, *c2;
	ulong blocks = 0;
	int 	i;
	char	*endofname;
	DIR	*dir = NULL;
	long loc = 0L;			/* record directory position */
	static DIR **lastdir = NULL;	/* record previous directory */
	static long *lastloc;		/* record previous location */
	int nds1,nds2;			/* number file descriptors */
	long nblocks();

	/* Defense against running out of file descriptors is twofold:
	 * Try and increase the number of file descriptors.  When that
	 * no longer works, begin closing the last file descriptor used.
	 * This is done by maintaining on the stack the present "DIR" file,
	 * and if this is NULL, then the last location in the directory.
	 * We know that the directory name is ".", so after a call to
	 * descend we check to see if this closed the directory we were
	 * using.  If it did, then we have to reopen "." and seek to
	 * the last location in the directory.  This is updated when ever
	 * the lower routine had to close a directory.
	 *
	 * Potential problem:  If you close a directory it may be modified
	 * and the reopen/seek is not guaranteed to provide a correct read
	 * of the directory.  Best effort is provided by this routine.
	 */

	if(lstat(name,&Statb)<0) {
		if(rflag)
			fprintf(stderr,
				gettxt("du:2", "du: bad status < %s >\n"),
				name);
		return(0);
	}
	if(statfs(name,&Statfsb, sizeof(struct statfs), 0)<0) {
		if(rflag)
			fprintf(stderr,
				gettxt("du:2", "du: bad status < %s >\n"),
				name);
		return(0);
	}

	if(Statb.st_nlink>1 && (Statb.st_mode&S_IFMT)!=S_IFDIR && linkc<ML) {
		for(i = 0; i <= linkc; ++i) {
			if(ml[i].ino==Statb.st_ino && ml[i].dev==Statb.st_dev)
				return 0;
		}
		ml[linkc].dev = Statb.st_dev;
		ml[linkc].ino = Statb.st_ino;
		++linkc;
		if(linkc > ML) {
			fprintf(stderr,
				gettxt("du:3", "du: link table overflow. \n"));
			exit(1);
		}
	}
	blocks = (ulong) nblocks(Statb.st_size, Statfsb.f_bsize, Statfsb.f_frsize);

	if((Statb.st_mode&S_IFMT)!=S_IFDIR) {
		if(aflag)
			printf("%lu	%s\n", blocks, name);
		return(blocks);
	}

	for(c1 = name; *c1; ++c1);
	endofname = c1;

	
	dir = opendir(name);
	if (dir == NULL) {		/* failed */
	    if (access(name, 04) != 0) {
		(void) fprintf(stderr,
			gettxt("du:8", "du: cannot read %s\n"), name);
		return(blocks);
	    }
	    nds1 = getdtablesize();	/* increase file table size */
	    nds1 += 20;
	    nds2 = setdtablesize(nds1);
	    if ( (nds2 != 0) && (nds1 != nds2) )
		dir = opendir(name);	/* try again */
	    if (dir == NULL) {		/* failed again */
	        if (*lastdir != NULL) {	/* close previous dir and retry */
		    *lastloc = telldir(*lastdir);
		    closedir(*lastdir);
		    *lastdir = NULL;
		    if ((dir = opendir(name)) == NULL) {
		        (void) fprintf(stderr,
				gettxt("du:8", "du: cannot read %s\n"), name);
		        return(blocks);
		    }
		} else {
		        (void) fprintf(stderr,
				gettxt("du:8", "du: cannot read %s\n"), name);
		        return(blocks);
		}
	    }
	}
	lastdir = &dir;		/* record addr on stack for next level */
	lastloc = &loc;

	for (dp = readdir(dir); dp != NULL; dp = readdir(dir)) {
		if(dp->d_ino==0
		|| EQ(dp->d_name, ".")
		|| EQ(dp->d_name, ".."))
			continue;
		if (dp->d_ino == -1)
			continue;
		c1 = endofname;
		*c1++ = '/';
		c2 = dp->d_name;
		while(*c2)
			*c1++ = *c2++;
		*c1 = '\0';
		if(c1 == (endofname + 1)) { /* ?? */
			fprintf(stderr,
				gettxt("du:6", "bad dir entry <%s>\n"),
				dp->d_name);
			return(blocks);
		}
		blocks += descend(name);
		if (dir == NULL) {	/* must have been closed by descend */
			c1 = endofname;
			*c1 = '\0';
			if ((dir = opendir(name)) == NULL) {
			    (void) fprintf(stderr,
				gettxt("du:9", "du: cannot reopen \"%s\"\n"),
				name);
			    return(blocks);
			}
			seekdir(dir, loc);
		}
	}
	if(dir)
		closedir(dir);
	*endofname = '\0';
	if(!sflag)
		printf("%lu	%s\n", blocks, name);
	return(blocks);
}

/*
long nblock(size)
long size;
{
	long blocks, tot;

	blocks = tot = (size + (BUFSIZE - 1)) / BUFSIZE;
	if(blocks > DIRECT)
		tot += ((blocks - DIRECT - 1) >> NSHIFT) + 1;
	if(blocks > DIRECT + NINDIR)
		tot += ((blocks - DIRECT - NINDIR - 1) >> (NSHIFT * 2)) + 1;
	if(blocks > DIRECT + NINDIR + NINDIR*NINDIR)
		tot++;
	return(tot);
}
*/
