#include	"hsubs.h"
#include	"actrecs.h"
#include	"quotas.h"
#include	"sys/param.h"
#include	"sys/filsys.h"
#include	"sys/ino.h"

#define	NREAD	2560		/* Read buffer size */

struct	qent	q;
struct	filsys	sblk;
struct	dinode	inode[64];
char	*dev = "/dev/rrmxx";
char	qbuf[NREAD], abuf[NREAD];
char	*tfile = QUOSTATS;
char	*qfile = QUOFILE;
char	norepflg = 0;

short	qfd, tfd;
char	replenish = 0;

struct	qtab	{
	short	t_uid;
	short	t_gid;
	uint	t_blk;
	uint	t_fil;
	short	t_fs;
} qtab[NHENT];

char	ftab[32][8];
short	nfs;
short	cfs;


main(c,v)
char **v;
{
	int	*tv, *localtime();
	long	tim;
	register struct qent *pq, *pl;
	short r;

	while(--c){
		v++;
		if(**v == '-')
			switch(*++*v){
	case	'q':
		if(c > 1){
			c--;
			qfile = *++v;
		}
		break;

	case	't':
		if(c > 1){
			c--;
			tfile = *++v;
		}
		break;

	case	'n':
		norepflg = 1;
		break;

	default:
		printf("Usage: quoupdt [-q quofile] [-t todayfile] [-noreplenish]\n");
		exit(1);
			}
	}

	time(&tim);
	tv = localtime(&tim);
	replenish = (tv[6] == 0);	/* Sunday all gets replenished */

	if((qfd = open(qfile, 2)) == -1){
		printf("quoupdt: Can't open %s\n", qfile);
		exit(2);
	}
	if((tfd = open(tfile, 0)) == -1){
		printf("quoupdt: Can't open %s\n", tfile);
		exit(3);
	}
	while((r = read(qfd, qbuf, NREAD)) > 0 && (r%QSZ) == 0){
		for(pq = qbuf, pl = &qbuf[r]; pq < pl; pq++)
			doq(pq);
		lseek(qfd, -(long)(r), 1);
		write(qfd, qbuf, r);
	}
	dodisk();
	close(qfd);
	close(tfd);
	exit(0);
}


doq(pq)
register struct qent *pq;
{
	char err;
	long c, s, u, d;
	short r;
	register struct acctrec *pa, *pl;

	if(pq->q_ln[0] == 0)return;
	addent(pq);

	c = s = u = d = 0;
	err = 0;
	lseek(tfd, (long)(akey(pq->q_uid))*AREAD, 0);

lp:
	while((r = read(tfd, abuf, NREAD)) > 0 && (r%AREAD) == 0){
		for(pa = abuf, pl = &abuf[r]; pa < pl; pa++){
			if(pa->a_uid == 0 && pa->a_gid == 0){
				update(c, s, u, d, pq);
				return;
			}
			if(pa->a_uid == pq->q_uid){
				c += pa->a_connect;
				s += pa->a_syscpu;
				u += pa->a_usrcpu;
				d += pa->a_diskblks;
			}
		}
	}
	if(err++){
		printf("quoupdt: Quofile read error\n");
		exit(4);
	}
	lseek(tfd, 0L, 0);
	goto lp;
}

update(cn, sp, up, ds, pq)
long cn, sp, up, ds;
register struct qent *pq;
{
	float tot, dol;
	long lt;

	lt = cn/36;
	if(cn%36 > 17)lt++;
	tot = ((float)(lt)/100.0) * CONRATE;
	lt = sp/2160;
	if(sp%2160 > 1079)lt++;
	dol = ((float)(lt)/100.0) * TICRATE;
	tot += dol;
	lt = up/2160;
	if(up%2160 > 1079)lt++;
	dol = ((float)(lt)/100.0) * TICRATE;
	tot += dol;
	lt = ds/10;
	if(ds%10 > 4)lt++;
	dol = ((float)(lt)/100.0) * DISKRATE;
	tot += dol;
	pq->q_mused += (unsigned)((tot + .05)*10.);
	if(pq->q_mquo && replenish && !norepflg)
		pq->q_mquo += pq->q_inc*10;
}

addent(pq)
struct qent	*pq;
{
	register short u, h;
	short g, fs;
	register struct qtab *pt;

	fs = fsys(pq->q_fs);
	u = pq->q_uid;
	g = pq->q_gid;
	h = akey(u);
	pt = &qtab[h];

	while(1){
		if(pt->t_uid == 0 && pt->t_gid == 0){
			pt->t_uid = u;
			pt->t_gid = g;
			pt->t_fs = fs;
			return;
		}
		if(pt->t_uid == u && pt->t_gid == g && pt->t_fs == fs){
/*
			printf("quoupdt: Duplicate uid/gid pair: %d,%d\n", u, g);
*/
			return;
		}
		h++;
		pt++;
		if(h >= NHENT){
			h = 0;
			pt = &qtab[0];
		}
	}
}

fsys(s)
register char *s;
{
	register short i;

	for(i = 0; i < nfs; i++)
		if(strncmp(s, &ftab[i][1], 6) == 0)
			return(i);
	ftab[i][0] = '/';
	strncpy(&ftab[i][1], s, 6);
	nfs++;
	return(i);
}

dodisk()
{
	register short i;
	register char *f;
	int sb[20];

	for(cfs = 0; cfs < nfs; cfs++){
		f = ftab[cfs];
		dev[8] = 0;
		if(stat(f, sb))
			continue;
		sb[0] &= 0377;
		f = dev + 8;
		if(sb[0] > 9)
			*f++ = '1';
		*f++ = sb[0]%10 + '0';
		*f = 0;
		check();
	}
	writeout();
}

check()
{
	int fd;
	register n, i, j;

	if((fd = open(dev, 0)) == -1){
		printf("quoupdt: Can not open %s\n", dev);
		return;
	}
	sync();
	lseek(fd, 512L, 0);
	if(read(fd, &sblk, (sizeof (sblk))) != (sizeof (sblk))){
		printf("quoupdt: bad super block %s\n", dev);
		close(fd);
		return;
	}
	n = (sblk.s_isize - 2)*INOPB;
	for(i = 0; i < n; i++){
		if((j = (i & 077)) == 0)
			if(read(fd, inode, sizeof inode) != sizeof inode){
				printf("quoupdt: bad inode read %s\n", dev);
				close(fd);
				return;
			}
		lookup(&inode[j]);
	}
	close(fd);
}


lookup(ip)
register struct dinode *ip;
{
	register short u, h;
	register struct qtab *pt;
	uint siz;
	if(ip->di_mode == 0)return;
	siz = (unsigned)((long)(ip->di_size + 511) >> 9L);
	u = ip->di_uid;
	h = akey(u);
	pt = &qtab[h];

	while(1){
		if(pt->t_uid == u && cfs == pt->t_fs &&
			(pt->t_gid == -1 || pt->t_gid == ip->di_gid)){
			pt->t_fil++;
			pt->t_blk += siz;
			return;
		}
		if(pt->t_uid == 0 && pt->t_gid == 0)
			return;
		h++;
		pt++;
		if(h >= NHENT){
			h = 0;
			pt = &qtab[0];
		}
	}
}


writeout()
{
	register struct qent *pq, *pl;
	register short r;

	lseek(qfd, 0L, 0);
	while((r = read(qfd, qbuf, NREAD)) > 0 && (r%QSZ) == 0){
		for(pq = qbuf, pl = &qbuf[r]; pq < pl; pq++)
			revise(pq);
		lseek(qfd, -(long)(r), 1);
		write(qfd, qbuf, r);
	}
}

revise(pq)
struct qent *pq;
{
	register short u, h;
	register struct qtab *pt;
	short g, fs;

	if(pq->q_ln[0] == 0)
		return;
	g = pq->q_gid;
	u = pq->q_uid;
	fs = fsys(pq->q_fs);
 	h = akey(u);
	pt = &qtab[h];

	while(1){
		if(pt->t_uid == u && pt->t_gid == g && pt->t_fs == fs){
			pq->q_dused = pt->t_blk;
			pq->q_fused = pt->t_fil;
			chkquo(pq);
			return;
		}
		if(pt->t_uid == 0 && pt->t_gid == 0)
			return;
		h++;
		pt++;
		if(h >= NHENT){
			h = 0;
			pt = qtab;
		}
	}
}

chkquo(pq)
register struct qent *pq;
{
	if((pq->q_flg & 037) &&
		pq->q_dused <= pq->q_dquo &&
		pq->q_fused <= pq->q_fquo &&
		(pq->q_mquo == 0 || pq->q_mused < pq->q_mquo))
			pq->q_flg &= ~037;
}
