/* Sunsoft defines __SVR4, gcc defines __svr4__ */
#ifdef __SVR4
#	define	__svr4__
#endif

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/file.h>
#include <memory.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#ifdef __svr4__
#	include <sys/fs/ufs_quota.h>
#	include <sys/mnttab.h>
#	define MNTFILE		MNTTAB
#	define STRUCT_MNTTAB	struct mnttab
#	define	MNTDIR		mnt_mountp
#	define MNTFS		mnt_special
#	define setmntent	fopen
#	define endmntent	fclose
#else
#	include <ufs/quota.h>
#	include <mntent.h>
#	define MNTFILE		MOUNTED
#	define STRUCT_MNTTAB	struct mntent
#	define	MNTDIR		mnt_dir
#	define MNTFS		mnt_fsname
	extern int	printf (const char *, ...);
	extern int	fprintf (FILE *, const char *, ...);
	extern void	perror (const char *);
	extern int 	getopt (int, char **, char *);
	extern int 	quotactl(int, char *, int, caddr_t);
#endif
#include <errno.h>
#define  dsize		sizeof(struct dqblk)

#define VERSION		2.00
#define DATE		950412L

int	f_hard_block, f_soft_block, f_cur_count, f_hard_flimit,
	f_soft_flimit, f_cur_file, f_time_disk, f_time_file, f_error,
	f_header,
	p_h_blk, p_s_blk, p_c_cnt, p_h_file, p_s_file,
	p_c_file, p_t_disk, p_t_file, action, c, uid, print_uid = 0,
	only_zero = 0,
	loop_from, loop_to, loop_flag = 0;
extern char *optarg;
extern int errno, optind, opterr;
char	filesys[100], *path, quotas[100];
int	debug = 0;

struct dqblk	d;


/*
 * Print usage and exit.
 */
void
usage(void)
{
	printf("usage: mquota [options] filesys\n"
	       "-B n	Hard size in kb\n"
	       "-b n	Soft size in kb\n"
	       "-c n	Current size in kb\n"
	       "-F n	Hard file limit\n"
	       "-f n	Soft file limit\n"
	       "-h	This help\n"
	       "-H	Print header\n"
	       "-l a,b	Use all uid's from a to b\n"
	       "-n n	Current # of allocated files\n"
	       "-q	Turn quota on\n"
	       "-o	Turn quota off\n"
	       "-g	Print quota\n"
	       "-T n	Set time limit for disk\n"
	       "-t n	Set time limit for files\n"
	       "-U	Print uid\n"
	       "-u n	Use n as uid\n"
	       "-V	Print version\n"
	       "-z	Only zero quotas (blocks)\n");
	exit (1);
} /* usage */


/*
 * Extract two numbers form a string: "start,end".
 */
void
set_loop(char *s)
{
	char	*p = strchr(s, ',');

	if (!p)	{
		usage();
	}
	loop_from = atoi(s);
	loop_to = atoi(p + 1);
	++loop_flag;
	if (debug) {
		printf(" from %d to %d\n", loop_from, loop_to);
	}
} /* set_loop */


/*
 * Get mounted device.
 */
void
get_dev(void)
{
	FILE		*fp;
	STRUCT_MNTTAB	mnt, *p = &mnt;

	fp = setmntent(MNTFILE, "r");
	if (fp == NULL) {
		perror(MNTFILE);
		exit(1);
	}
#ifdef __svr4__
	while (getmntent(fp, &mnt) == 0) {
#else
	while ((p = getmntent(fp)) != NULL) {
#endif
		if (debug) {
			printf("directory %-20s => mounted filesys %s\n",
			       p->MNTDIR, p->MNTFS);
		}
		if (strcmp(path, p->MNTDIR) == 0) {
			strcpy(filesys, p->MNTFS);
			strcpy(quotas, p->MNTDIR);
			strcat(quotas, "/quotas");
			if (debug) {
				printf("USING quota file \"%s\" and "
				       "filesys \"%s\" (only sunos4)\n",
				       quotas, filesys);
			}
			/* Check if qouta file exists */
			if (access(quotas, F_OK) == -1) {
				perror(quotas);
				exit(1);
			}
			endmntent(fp);
			return;
		}
	}
	fprintf(stderr, "%s is not a mounted filesys\n", path);
	endmntent(fp);
	exit(1);
} /* get_dev */


/************************************************************************
       int quotactl(cmd, special, uid, addr)
       int cmd;
       char *special;
       int uid;
       caddr_t addr;

       int ioctl(int fd, Q_QUOTACTL, struct quotactl *qp)
       struct quotctl {
            int op;
            uid_t uid;
            caddr_t addr;
    };
************************************************************************/

#ifdef __svr4__
/*
 * Sunos 5 has no quotactl.
 */
int
quotactl(int cmd, char *special, int uid, caddr_t addr)
{
	int	fd, stat;
	struct quotctl	qctl;

	fd = open(quotas, O_RDWR);
	if (fd == -1) {
		perror(quotas);
		exit(1);
	}
	qctl.op = cmd;
	qctl.uid = uid;
	qctl.addr = addr;
	stat = ioctl(fd, Q_QUOTACTL, &qctl);
	close(fd);
	return stat;
} /*quotactl */
#endif


/*
 * Get quota.
 */
int
get_quota(void)
{
	if (!f_time_disk && !f_time_file && !uid) {
		printf("uid or time required\n");
		usage();
	}
	if (quotactl(Q_GETQUOTA, filesys, uid, (caddr_t)&d) < 0) {
		if (errno == ESRCH) {
			memset(&d, 0, dsize);
			return 0;
		}
		perror("man_quota:getquota");
		exit(1);
	}
	return d.dqb_bhardlimit+d.dqb_bsoftlimit;
} /* get_quota */


/*
 * Do the actual job.
 */
void
cmds(int action, char *path)
{
	int	flag = 0;
	
	switch (action) {
	case 3:			/* get quota				*/
		get_quota();
		if (f_header) {
			if (print_uid) {
				printf("%6s ", "Uid");
			}
			printf("%6s %6s %6s %6s %6s %6s %6s %6s\n",
			       "Blimit", "Bquota", "Fcur", "Flimit",
			       "Fquota", "Fcur", "Dtime", "Ftime");
		}
		if (print_uid) {
			printf("%6d ", uid);
		}
		printf("%6ld %6ld %6ld %6ld %6ld %6ld %6ld %6ld\n",
		       d.dqb_bhardlimit/2,/* absolute limit on disk blks */
		       d.dqb_bsoftlimit/2,/* preferred limit on disk blks */
		       d.dqb_curblocks/2, /* current block count */
		       d.dqb_fhardlimit,/* maximum # allocated files + 1 */
		       d.dqb_fsoftlimit,/* preferred file limit */
		       d.dqb_curfiles,  /* current # allocated files */
		       d.dqb_btimelimit,/* time limit for excessive disk use */
		       d.dqb_ftimelimit); /* time limit for excessive files */
		return;
	case 1:
		if (quotactl(Q_QUOTAON, filesys, 0, quotas) < 0) {
			perror("man_quota:quotaon");
			exit(1);
		}
		break;
	case 2:
		if (quotactl(Q_QUOTAOFF, filesys, 0, NULL) < 0) {
			perror("man_quota:quotaoff");
			exit(1);
		}
		break;
	case 4: 		/* set quota				*/
		flag = get_quota();
		if (only_zero && flag) {
			return;
		}
		if (f_hard_block) {
			d.dqb_bhardlimit = p_h_blk;
		}
		if (f_soft_block) {
			d.dqb_bsoftlimit = p_s_blk;
		}
		if (f_cur_count) {
			d.dqb_curblocks = p_c_cnt;
		}
		if (f_hard_flimit) {
			d.dqb_fhardlimit = p_h_file;
		}
		if (f_soft_flimit) {
			d.dqb_fsoftlimit = p_s_file;
		}
		if (f_cur_file) {
			d.dqb_curfiles = p_c_file;
		}
		if (f_time_disk) {
			d.dqb_btimelimit = p_t_disk;
		}
		if (f_time_file) {
			d.dqb_ftimelimit = p_t_file;
		}
		if (debug) {
			printf("set %6d ", uid);
			printf("%6ld %6ld %6ld %6ld %6ld %6ld %6ld %6ld\n",
			       d.dqb_bhardlimit/2, d.dqb_bsoftlimit/2,
			       d.dqb_curblocks/2, d.dqb_fhardlimit,
			       d.dqb_fsoftlimit, d.dqb_curfiles,
			       d.dqb_btimelimit, d.dqb_ftimelimit);
			return;
		}
		if (quotactl(Q_SETQUOTA, filesys, uid, (caddr_t)&d) < 0) {
			perror("man_quota:setquota");
			exit(1);
		}
		break;
	}
} /* cmds */


/*
 * Main procedure
 */
void
main(int argc, char **argv)
{
	while ((c = getopt(argc, argv,
			   "B:b:c:dF:f:Hl:n:qogT:t:Uu:Vz")) != EOF)
		switch (c) {
		case 'B':		/* set hardlimit		*/
			action = 4;
			p_h_blk = atoi(optarg) * 2;
			++f_hard_block;
			break;
		case 'b':		/* set softlimit		*/
			action = 4;
			p_s_blk = atoi(optarg) * 2;
			++f_soft_block;
			break;
		case 'c':		/* set current block count	*/
			action = 4;
			p_c_cnt = atoi(optarg) * 2;
			++f_cur_count;
			break;
		case 'd':		/* debug 			*/
			++debug;
			break;
		case 'F':		/* set hard file limit		*/
			action = 4;
			p_h_file = atoi(optarg);
			++f_hard_flimit;
			break;
		case 'f':		/* set soft file limit		*/
			action = 4;
			p_s_file = atoi(optarg);
			++f_soft_flimit;
			break;
		case 'h':		/* help				*/
			usage();
		case 'H':		/* write header			*/
			++f_header;
			break;
		case 'l':		/* loop				*/
			set_loop(optarg);
			break;
		case 'n':		/* set current allocated files	*/
			action = 4;
			p_c_file = atoi(optarg);
			++f_cur_file;
			break;
		case 'q':		/* turn on quota		*/
			action = 1;
			break;
		case 'o':		/* turn quota off		*/
			action = 2;
			break;
		case 'g':		/* get quota			*/
			action = 3;
			break;
		case 'T':		/* set time limit disk		*/
			action = 4;
			p_t_disk = atoi(optarg);
			++f_time_disk;
			break;
		case 't':		/* set time limit files		*/
			action = 4;
			p_t_file = atoi(optarg);
			++f_time_file;
			break;
		case 'u':
			uid = atoi(optarg);
			break;
		case 'U':
			++print_uid;
			break;
		case 'V':
			printf("Version %2.2f %ld, written by "
			       "Joergen Haegg and Per Foreby\n", 
				VERSION, DATE);
			exit(0);
		case 'z':
			++only_zero;
			break;
		case '?':
			++f_error;
			break;
		}

	if (f_error) {
		usage();
	}

	if (optind == argc) {
		printf("filesys required\n");
		usage();
	}
	path = argv[optind];
	get_dev();

	if (loop_flag) {
		for (uid = loop_from; uid <= loop_to; uid++)
			cmds(action, path);
	} else {
		cmds(action, path);
	}
} /* main */
