/*	@(#)df.c	2.1		*/
#include <stdio.h>
#include <sys/param.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/ino.h>
#include <mnttab.h>
#include <ustat.h>
#include <sys/stat.h>
#include <sys/filsys.h>
#include <sys/fblk.h>

#define EQ(x,y,z) (strncmp(x,y,z)==0)

struct	mnttab	M[NMOUNT];
struct stat	S;
struct filsys	sblock;
struct ustat	Fs_info, *Fs;

int	physblks;
long	bsize;
int 	bshift;
int	Flg, tflag;
int	fd;
daddr_t	blkno	= 1;
daddr_t	alloc();
char nfsmes[] = "          (%-10.10s): is not a file system\n";

main(argc, argv)
char **argv;
{
	register fi, i;
	register char	c;
	char	 dev[32];
	int	 j, k;

	for(j=1;j < argc;j++) {
		if(argv[j][0] != '-') {
			break;
		}
		for(k=1;argv[j][k] != '\0';k++) {
			switch(c = argv[j][k]) {
				case 'f':
					Flg++;
					break;

				case 'q':
					break;

				case 't':
					tflag=1;
					break;

				default:
					fprintf(stderr,"df: illegal arg -%c\n",c);
					exit(1);
			}
		}
	}
	if((fi = open("/etc/mnttab", 0)) < 0) {
		fprintf(stderr,"df: cannot open /etc/mnttab\n");
		exit(1);
	}
	i = read(fi, M, sizeof M);
	close(fi);

	for(i /= sizeof M[0]; --i >= 0;) {
		if(!M[i].mt_dev[0]) /* it's been umount'ed */
			continue;
		sprintf(dev, "/dev/%.10s", M[i].mt_dev);
		if(argc > j) {
			for(k=j;k < argc; ++k) {
				if(EQ(argv[k], dev, 14)
				|| EQ(argv[k], M[i].mt_dev, 10)
				|| EQ(argv[k], M[i].mt_filsys, 10)) {
					printit(dev, M[i].mt_filsys);
					argv[k][0] = '\0';
				}
			}
		} else
			printit(dev, M[i].mt_filsys);
	}
	for(i = j; i < argc; ++i) {
		if(argv[i][0])
			printit(argv[i], "\0");
	}
	exit(0);
}

printit(dev, fs_name)
char *dev, *fs_name;
{
	if((fd = open(dev, 0)) < 0) {
		fprintf(stderr,"df: cannot open %s\n",dev);
		return;
	}
	if(!Flg) {
		Fs = &Fs_info;
		if(stat(dev, &S) < 0) {
			fprintf(stderr, "df: cannot stat %s\n", dev);
			return;
		}
		if((S.st_mode & S_IFMT) != S_IFBLK || ustat(S.st_rdev, Fs) < 0) {
			Fs = (struct ustat *)&sblock.s_tfree;
		}
	} else {
		Fs = (struct ustat *)&sblock.s_tfree;
		sync();
	}
	if(lseek(fd, (long)SUPERBOFF, 0) < 0
	   || read(fd, &sblock, (sizeof sblock)) != (sizeof sblock)) {
		return;
	}
	if(sblock.s_magic == FsMAGIC) {
		if(sblock.s_type == Fs1b) {
			physblks = 1;
			bsize = BSIZE;
			bshift = BSHIFT;
		} else if(sblock.s_type == Fs2b) {
			physblks = 2;
			bsize = BSIZE*2;
			bshift = BSHIFT + 1;
		} else {
			printf("          (%-10s): bad block type\n", dev);
			return;
		}
	} else {
		physblks = 1;
		bsize = BSIZE;
		bshift = BSHIFT;
	}
/* ustat returns physical blocks,
 * filsys contains logical blocks,
 * make correction
 */
	if(Fs == (&Fs_info))
		Fs->f_tfree /= physblks;
/* file system sanity test */
	if(sblock.s_fsize <= sblock.s_isize
	   || sblock.s_fsize < Fs->f_tfree
	   || sblock.s_isize < Fs->f_tinode*sizeof(struct dinode)/bsize
	   || (long)sblock.s_isize*bsize/sizeof(struct dinode) > 0xffffL) {
/* inodes are assumed to be valid up to the largest unsigned short */
		printf(nfsmes, dev);
		return;
	}
	

	if(!Flg) {
		if(*fs_name == '\0') {
			printf("  %-6.6s  ", sblock.s_fname);
		}else {
			printf("%-10s", fs_name);
		}
		printf("(%-10s): %8ld blocks %7u i-nodes\n",
			dev, Fs->f_tfree*physblks,
			Fs->f_tinode);
	}
	else {
		daddr_t	i;

		i = 0;
		while(alloc())
			i++;
		printf("%-10s(%-10s): %8ld blocks\n",fs_name, dev,
			i*physblks);
	}
	if(tflag) {
		printf("                 total: %8ld blocks %7u i-nodes\n",
			sblock.s_fsize*physblks, (unsigned)((sblock.s_isize - 2)*bsize/sizeof(struct dinode)));
	}
	close(fd);
}

daddr_t
alloc()
{
	int i;
	daddr_t	b;
	struct fblk buf;

	i = --sblock.s_nfree;
	if(i<0 || i>=NICFREE) {
		printf("bad free count, b=%ld\n", blkno);
		return(0);
	}
	b = sblock.s_free[i];
	if(b == 0)
		return(0);
	if(b<sblock.s_isize || b>=sblock.s_fsize) {
		printf("bad free block (%ld)\n", b);
		return(0);
	}
	if(sblock.s_nfree <= 0) {
		bread(b, &buf, sizeof(buf));
		blkno = b;
		sblock.s_nfree = buf.df_nfree;
		for(i=0; i<NICFREE; i++)
			sblock.s_free[i] = buf.df_free[i];
	}
	return(b);
}

bread(bno, buf, cnt)
daddr_t bno;
char *buf;
{
	int n;
	extern errno;

	lseek(fd, bno<<bshift, 0);
	if((n=read(fd, buf, cnt)) != cnt) {
		printf("read error %ld\n", bno);
		printf("count = %d; errno = %d\n", n, errno);
		exit(0);
	}
}
