/* $Header:df.c 12.0$ */
/* $ACIS:df.c 12.0$ */
/* $Source: /ibm/acis/usr/src/bin/RCS/df.c,v $ */

#ifndef lint
static char *rcsid = "$Header:df.c 12.0$";
#endif

#include <sys/nfs_defines.h>

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
 All rights reserved.\n";
#endif not lint

#ifndef lint
static char sccsid[] = "@(#)df.c	5.1 (Berkeley) 4/30/85";
#endif not lint

#ifdef NFS
/* @(#)df.c	1.2 87/07/16 3.2/4.3NFSSRC */
#endif NFS

#include <sys/param.h>
#ifndef NFS
#include <sys/fs.h>
#include <sys/stat.h>
#endif !NFS
#include <errno.h>
#ifdef NFS
#include <ufs/fs.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#endif NFS

#include <stdio.h>
#ifndef NFS
#include <fstab.h>
#include <mtab.h>
#else !NFS
#include <mntent.h>
#endif !NFS

#ifndef NFS
/*
 * df
 */
struct	mtab mtab[NMOUNT];
char	root[32];
#endif !NFS
char	*mpath();

int	iflag;

#ifdef NFS
struct mntent *getmntpt(), *mntdup();

#endif NFS
union {
	struct fs iu_fs;
	char dummy[SBSIZE];
} sb;
#define sblock sb.iu_fs

daddr_t	alloc();
char	*strcpy();
#ifndef NFS
int	fi;
#endif !NFS
char *progname;

main(argc, argv)
	int argc;
	char **argv;
{
	int i;
	progname = argv[0];
#ifdef NFS
	struct stat statb;
	char tmpname[1024];
#endif NFS

	while (argc > 1 && argv[1][0]=='-') {
		switch (argv[1][1]) {

		case 'i':
			iflag++;
			break;
		default:
			fprintf(stderr, "usage: %s [ -i ] [ filsys... ]\n",progname);
			exit(0);
		}
		argc--, argv++;
	}
#ifndef NFS
	i = open("/etc/mtab", 0);
	if (i >= 0) {
		(void) read(i, (char *)mtab, sizeof (mtab));
		(void) close(i);
	}
#endif !NFS
	sync();
	printf("Filesystem    kbytes    used   avail capacity");
	if (iflag)
		printf(" iused   ifree  %%iused");
	printf("  Mounted on\n");
	if (argc <= 1) {
#ifndef NFS
		struct fstab *fsp;
#else !NFS
		register FILE *mtabp;
		register struct mntent *mnt;
#endif !NFS

#ifndef NFS
		if (setfsent() == 0)
			perror(FSTAB), exit(1);
		while (fsp = getfsent()) {
			if (strcmp(fsp->fs_type, FSTAB_RW) &&
			    strcmp(fsp->fs_type, FSTAB_RO) &&
			    strcmp(fsp->fs_type, FSTAB_RQ))
#else !NFS
		if ((mtabp = setmntent(MOUNTED, "r")) == NULL) 
			exit(1);
		while ((mnt = getmntent(mtabp)) != NULL) {
			if (strcmp(mnt->mnt_type, MNTTYPE_IGNORE) == 0 ||
			    strcmp(mnt->mnt_type, MNTTYPE_SWAP) == 0)
#endif !NFS
				continue;
#ifndef NFS
			if (root[0] == 0)
				(void) strcpy(root, fsp->fs_spec);
			dfree(fsp->fs_spec, 1);
#else !NFS
			 if (((strcmp(mnt->mnt_type, MNTTYPE_42) == 0) ||
                            (strcmp(mnt->mnt_type, MNTTYPE_43) == 0) ||
                            (strcmp(mnt->mnt_type, MNTTYPE_UFS) ==0)) &&
			    (stat(mnt->mnt_fsname, &statb) >= 0) &&
			   (((statb.st_mode & S_IFMT) == S_IFBLK) ||
			    ((statb.st_mode & S_IFMT) == S_IFCHR))) {
				(void)strcpy(tmpname, mnt->mnt_fsname);
				dfreedev(tmpname);
			} else {
				dfreemnt(mnt->mnt_dir, mnt);
			}
#endif !NFS
		}
#ifndef NFS
		endfsent();
#else !NFS
		(void)endmntent(mtabp);
#endif !NFS
		exit(0);
	}
#ifndef NFS
	for (i=1; i<argc; i++)
		dfree(argv[i], 0);
#else !NFS
	for (i=1; i<argc; i++) {
		register struct mntent *mnt;

		if (stat(argv[i], &statb) < 0) {
			(void) fprintf(stderr, "df: ");
			perror(argv[i]);
		} else {
			if ((statb.st_mode & S_IFMT) == S_IFBLK ||
			    (statb.st_mode & S_IFMT) == S_IFCHR) {
				dfreedev(argv[i]);
			} else {
				if ((mnt = getmntpt(argv[i])) != NULL)
					dfreemnt(argv[i], mnt);
			}
		}
	}
#endif !NFS
}

#ifndef NFS
dfree(file, infsent)
#else !NFS
dfreedev(file)
#endif !NFS
	char *file;
#ifndef NFS
	int infsent;
#endif !NFS
{
	long totalblks, availblks, avail, free, used;
#ifndef NFS
	struct stat stbuf;
	struct fstab *fsp;
#else !NFS
	int fi;
#endif !NFS

#ifndef NFS
	if (stat(file, &stbuf) == 0 &&
	    (stbuf.st_mode&S_IFMT) != S_IFCHR &&
	    (stbuf.st_mode&S_IFMT) != S_IFBLK) {
		if (infsent) {
			fprintf(stderr, "%s: screwy /etc/fstab entry\n", file);
			return;
		}
		setfsent();
		while (fsp = getfsent()) {
			struct stat stb;

			if (stat(fsp->fs_spec, &stb) == 0 &&
			    stb.st_rdev == stbuf.st_dev) {
				file = fsp->fs_spec;
				endfsent();
				goto found;
			}
		}
		endfsent();
		fprintf(stderr, "%s: mounted on unknown device\n", file);
		return;
	}
found:
#endif !NFS
	fi = open(file, 0);
	if (fi < 0) {
		perror(file);
		return;
	}
#ifndef NFS
	if (bread(SBLOCK, (char *)&sblock, SBSIZE) == 0) {
#else !NFS
	if (bread(fi, SBLOCK, (char *)&sblock, SBSIZE) == 0) {
#endif !NFS
		(void) close(fi);
		return;
	}
	printf("%-12.12s", file);
	totalblks = sblock.fs_dsize;
	free = sblock.fs_cstotal.cs_nbfree * sblock.fs_frag +
	    sblock.fs_cstotal.cs_nffree;
	used = totalblks - free;
	availblks = totalblks * (100 - sblock.fs_minfree) / 100;
	avail = availblks > used ? availblks - used : 0;
	printf("%8d%8d%8d", totalblks * sblock.fs_fsize / 1024,
	    used * sblock.fs_fsize / 1024, avail * sblock.fs_fsize / 1024);
	printf("%6.0f%%",
	    availblks == 0 ? 0.0 : (double) used / (double) availblks * 100.0);
	if (iflag) {
		int inodes = sblock.fs_ncg * sblock.fs_ipg;
		used = inodes - sblock.fs_cstotal.cs_nifree;
		printf("%8ld%8ld%6.0f%% ", used, sblock.fs_cstotal.cs_nifree,
		    inodes == 0 ? 0.0 : (double)used / (double)inodes * 100.0);
	} else 
		printf("  ");
	(void) printf("  %s\n", mpath(file));
	(void) close(fi);
}

#ifdef NFS
dfreemnt(file, mnt)
	char *file;
	struct mntent *mnt;
{
	long totalblks, avail, free, used, reserved;
	struct statfs fs;

	if (statfs(file, &fs) < 0) {
		perror(file);
		return;
	}

	if (strlen(mnt->mnt_fsname) > 12) {
		printf("%s\n", mnt->mnt_fsname);
		printf("            ");
	} else {
		printf("%-12.12s", mnt->mnt_fsname);
	}
	totalblks = fs.f_blocks;
	free = fs.f_bfree;
	used = totalblks - free;
	avail = fs.f_bavail;
	reserved = free - avail;
	if (avail < 0)
		avail = 0;
	printf("%8d%8d%8d", totalblks * fs.f_bsize / 1024,
	   used * fs.f_bsize / 1024, avail * fs.f_bsize / 1024);
	totalblks -= reserved;
	printf("%6.0f%%",
	    totalblks==0? 0.0: (double)used/(double)totalblks * 100.0);
	if (iflag) {
		long files, used;
	
		files = fs.f_files;
		used = files - fs.f_ffree;
		printf("%8ld%8ld%6.0f%% ", used, fs.f_ffree,
		    files == 0? 0.0: (double)used / (double)files * 100.00);
	} else
		printf("  ");
	printf("  %s\n", mnt->mnt_dir);
}

/*
 * Given a name like /usr/src/etc/foo.c returns the mntent
 * structure for the file system it lives in.
 */
struct mntent *
getmntpt(file)
	char *file;
{
	FILE *mntp;
	struct mntent *mnt, *mntsave;
	struct stat filestat, dirstat;

	if (stat(file, &filestat) < 0) {
		perror(file);
		return(NULL);
	}

	if ((mntp = setmntent(MOUNTED, "r")) == 0) {
		perror(MOUNTED);
		exit(1);
	}

	mntsave = NULL;
	while ((mnt = getmntent(mntp)) != 0) {
		if (strcmp(mnt->mnt_type, MNTTYPE_IGNORE) == 0 ||
		    strcmp(mnt->mnt_type, MNTTYPE_SWAP) == 0)
			continue;
		if ((stat(mnt->mnt_dir, &dirstat) >= 0) &&
		   (filestat.st_dev == dirstat.st_dev)) {
			mntsave = mntdup(mnt);
		}
	}
	(void)endmntent(mntp);
	if (mntsave) {
		return(mntsave);
	} else {
		fprintf(stderr, "Couldn't find mount point for %s\n", file);
		exit(1);
	}
	/*NOTREACHED*/
}

#endif NFS
long lseek();

#ifndef NFS
bread(bno, buf, cnt)
#else !NFS
bread(fi, bno, buf, cnt)
	int fi;
#endif !NFS
	daddr_t bno;
	char *buf;
{
	int n;
	extern errno;

	(void) lseek(fi, (long)(bno * DEV_BSIZE), 0);
	if ((n=read(fi, buf, cnt)) != cnt) {
		/* probably a dismounted disk if errno == EIO */
		if (errno != EIO) {
			printf("\nread error bno = %ld\n", bno);
			printf("count = %d; errno = %d\n", n, errno);
		}
		return (0);
	}
	return (1);
}

/*
 * Given a name like /dev/rrp0h, returns the mounted path, like /usr.
 */
char *
mpath(file)
	char *file;
{
#ifdef NFS
	FILE *mntp;
	register struct mntent *mnt;
#else NFS
	register struct mtab *mp;
#endif NFS
#ifdef NFS
	if ((mntp = setmntent(MOUNTED, "r")) == 0) {
		(void) fprintf(stderr, "df: ");
		perror(MOUNTED);
		exit(1);
	}

	while ((mnt = getmntent(mntp)) != 0) {
		if (strcmp(file, mnt->mnt_fsname) == 0) {
			(void) endmntent(mntp);
			return (mnt->mnt_dir);
		}
	}
	(void) endmntent(mntp);
#else NFS
	if (eq(file, root))
		return ("/");
	for (mp = mtab; mp < mtab + NMOUNT; mp++)
		if (eq(file, mp->m_dname))
			return (mp->m_path);
#endif NFS
	return "";
}

#ifdef NFS
char *
xmalloc(size)
	unsigned int size;
{
	register char *ret;
	char *malloc();

	if ((ret = (char *)malloc(size)) == NULL) {
		(void) fprintf(stderr, "umount: ran out of memory!\n");
		exit(1);
	}
	return (ret);
}


struct mntent *
mntdup(mnt)
	register struct mntent *mnt;
{
	register struct mntent *new;

	new = (struct mntent *)xmalloc(sizeof(*new));

	new->mnt_fsname = (char *)xmalloc(strlen(mnt->mnt_fsname) + 1);
	(void) strcpy(new->mnt_fsname, mnt->mnt_fsname);

	new->mnt_dir = (char *)xmalloc(strlen(mnt->mnt_dir) + 1);
	(void) strcpy(new->mnt_dir, mnt->mnt_dir);

	new->mnt_type = (char *)xmalloc(strlen(mnt->mnt_type) + 1);
	(void) strcpy(new->mnt_type, mnt->mnt_type);

	new->mnt_opts = (char *)xmalloc(strlen(mnt->mnt_opts) + 1);
	(void) strcpy(new->mnt_opts, mnt->mnt_opts);

	new->mnt_freq = mnt->mnt_freq;
	new->mnt_passno = mnt->mnt_passno;

	return (new);
}
#endif NFS

#ifndef NFS
eq(f1, f2)
	char *f1, *f2;
{
	if (strncmp(f1, "/dev/", 5) == 0)
		f1 += 5;
	if (strncmp(f2, "/dev/", 5) == 0)
		f2 += 5;
	if (!strcmp(f1, f2))
		return (1);
	if (*f1 == 'r' && !strcmp(f1+1, f2))
		return (1);
	if (*f2 == 'r' && !strcmp(f1, f2+1))
		return (1);
	if (*f1 == 'r' && *f2 == 'r' && strcmp(f1+1, f2+1) == 0)
		return (1);
	return (0);
}
#endif !NFS
