/*
 * lc -- john buck
 *
 * call logit() (see last function in prog) for keeping logs
 */
#include "stdio.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/acct.h>
#include "hsubs.h"
#include "devlist.h"
#define MAXA 32

long	tics();
struct	acct acctbuf, cmd;



long ptime, off;
int	argc1, fd, lflg, sflg, ucnt, ccnt, lcnt, gcnt;
int	uids[MAXA];
int	gids[MAXA];
struct {
	char ln_name[10];
	short ln_uid;
} llist[500];

short nlnames = -1;
int *lt;
char *getwtmp(), *nextarg(), *ctime();
char	**argv1, *when = 0,
	*file = 0,
	*lnam[MAXA],
	*cnam[MAXA],
	*tys = 0;
char	*tcmp[64];
short	tlen[64];
short	ntty = 0;

char obf[128];
FILE *fop = NULL;



main(c,v)
char **v;
{
	register struct acct *pw, *pm;
	char *cp;
	char buf[512];
	register r;
	short *mf;
	long gtime(), nowt;

	lflg = 0;
	if((r = getgid()) != 2 && r && r != 3)exit(7);
	if((r = getuid()) != 47 && r != 5 && r != 7 && r){
		mf = 7;
		*mf = 0;
		exit(7);
	}
out:
	if(r == 7)logit(c, v);
	argc1 = c;
	argv1 = v;
	ptime = 0L;
	while(--argc1){
		argv1++;
		if(**argv1 != '-')continue;
		switch(*++*argv1){
case	'f':
		file = nextarg();
		break;

case	's':
		sflg = atoi(nextarg());
		off = -3L*(sizeof (struct acct));
		break;

case	'o':
		off = (long)(atoi(nextarg()))*(sizeof (struct acct));
		break;

case	'k':
		cp = nextarg();
		if((fop = fopen(cp, "w")) == NULL){
			printf("Can't create raw/output file: %s\n", cp);
			exit(7);
		}
		break;

case	'c':
		if(ccnt >= MAXA) goto toomany;
		cnam[ccnt++] = nextarg();
		break;

case	'l':
		if(lcnt >= MAXA) goto toomany;
		lnam[lcnt++] = nextarg();
		break;

case	't':
		tys = nextarg();
		tcmp[ntty++] = tys;
		while(*tys){
			if(*tys == ','){
				*tys = 0;
				tlen[ntty-1] = strlen(tcmp[ntty-1]);
				tcmp[ntty++] = tys+1;
			}
			tys++;
		}
		tlen[ntty-1] = strlen(tcmp[ntty-1]);
		tcmp[ntty] = 0;
		break;

case	'v':
		lflg++;
		break;

case	'u':
		if(ucnt >= MAXA){
toomany:
			printf("Too many args.\n");
			exit(3);
		}
		uids[ucnt++] = atoi(nextarg());
		break;

case	'g':
		if(gcnt >= MAXA){
tomany:
			printf("Too many args.\n");
			exit(4);
		}
		gids[gcnt++] = atoi(nextarg());
		break;

default:
		when = nextarg();
		break;

		}
	}
	if(file == 0)
		if(when) file = getwtmp(when);
		else	file = "/accting/actfil";
	if((fd = open(file, 0)) == -1){
		printf("Can't open %s\n", file);
		exit(1);
	}

	if(when){
		ptime = gtime();
		time(&nowt);
		if(ptime > nowt){
			lt[5] =- 1;
			ptime = gtime();
		}
		if(findplace() == -1){
			printf("Bad status on %s\n", file);
			exit(1);
		}
		printf("\nLast commands on: %s (%s)\n",
			ctime(&ptime), file);
		lseek(fd, off, 1);
	} else	{
		if(off < 0L) lseek(fd, off, 2);
		else	lseek(fd, off, 0);
	}


	for(r = 0; r < lcnt; r++)
		if(strlen(lnam[r]) > 8)lnam[r][8] = 0;
	for(r = 0; r < ccnt; r++)
		if(strlen(cnam[r]) > 10)cnam[r][10] = 0;
	if(fop == NULL){
		printf("\nTty  Log-name ");
		if(lflg)
			printf("  Uid Gid ");
		printf("Comnd Name  ");
		if(lflg)
			printf("MM/DD ");
		printf("HH:MM:SS  ");
		if(lflg)
			printf(" User_Time Systm_Time  Real_time\n");
		else	printf("(TICS)\n");
	}
lp:
	while((r = read(fd, buf, 512))%32 == 0 && r)
		for(pw = buf, pm = &buf[r]; pw < pm; pw++){
			printent(pw);
		}
	if(sflg){
		sleep(sflg);
		goto lp;
	}
	close(fd);
	if(fop)
		fclose(fop);
}


int mon[13] = { 0 , 0 , 31 , 59 , 90 , 120 , 151 , 181 , 212 , 243 , 273 , 304 , 334 };


long gtime()
{
	long i;
	register int m, d, h;
	int y, min;

	i = 0L;

	y = 1900 + lt[5];
	m = lt[4];
	d = lt[3];
	h = lt[2];
	min = lt[1];
	if(m < 1 || m > 12)write(2,"?Month\n", 7);
	else	if(d < 1 || d > 31)write(2,"?Day\n", 5);
		else	if(h < 0 || h > 23)write(2,"?Hour\n", 6);
			else	if(min < 0 || min > 59)write(2,"?Minute\n", 8);
				else	if(m == 2 && y%4 && d > 28)write(2,"?Leap\n", 6);
					else goto ok;
	exit(1);
ok:

	h =+ 5;
	if((m > 4 && m < 10) ||
	(m == 4 && d > 26) || (m == 10 && d < 25) ||
	(m == 4 && d == 26 && h > 6) ||
	(m == 10 && d == 25 && h == 5))h--;

	i = (y - 1970)*365L + (y-1)/4L - 492L;
	i = (i + mon[m] + (m > 2 && ((y%4) == 0)) + d - 1)*24L;
	i = ((i + h)*60L + min)*60L;
	return(i);
}


val(s)
register char *s;
{
	register int j;

	j = (*s - '0')*10 + (*(s+1)-'0');
	return(j);
}



struct	lst	{
	char nmm[14];
	struct lst *pl;
	struct lst *pr;
} lst[400];

struct	lst	*base, *next, *last;


char *getwtmp(s, f)
register f;
char *s;
{
	register struct lst *p;
	register int *t;

	int *localtime(), tv[2];

/*	Define SPOS to be position of the beginning of the s.00.00.00.00 in swt
 */
#define	SPOS	27
	static char *swt = "/staff/fred/ucb/oldaccting/s.00.00.00.00";
	static char *wt = "s.00.00.00.00";
	static char *now = "s.00.00.00.00";

	time(tv);
	t = lt = localtime(tv);
	t[4] =+ 1;
	mkwtmp(t, now);

	switch(strlen(s)){

	case 2:
		t[2] = val(s);
		t[1] = 0;
		break;

	case 4:
		t[2] = val(s);
		t[1] = val(s+2);
		break;

	case 6:
rest:
		t[3] = val(s);
		t[2] = val(s+2);
		t[1] = val(s+4);
		break;

	case 8:
		t[4] = val(s);
		s =+ 2;
		goto rest;

	default:
		usage();
		exit(1);
	}
	mkwtmp(t, wt);
	fillit();

	for(p = last; p != 0; p = p->pl)
		if(strcmp(wt, p->nmm) >= 0)break;

	if(p == last ||
		 (strcmp(now, p->pr->nmm) < 0 && strcmp(wt, now) < 0))
			return("/accting/actfil");
	if(p == 0){
		printf("Confused.\n");
		exit(1);
	}
	cps(swt+SPOS, p->pr->nmm);
	return(swt);
}

mkwtmp(t, s)
int t[];
register char *s;
{
	cov(s+2, t[4]);
	cov(s+5, t[3]);
	cov(s+8, t[2]);
	cov(s+11, t[1]);
}

cov(s,n)
register char *s;
register n;
{
	*s++ = n/10 + '0';
	*s = n%10 + '0';
}

cps(s,t)
register char *s, *t;
{
	while(*s++ = *t++);
}

fillit()
{
	char buf[512];
	register struct {
		int ino;
		char nm[14];
	} *pd;
	register r;
	int fd;


	next = base = lst;

	if((fd = open("/staff/fred/ucb/oldaccting", 0)) == -1){
		printf("No fred directory\n");
		exit(3);
	}

	while(r = read(fd, buf, 512))
		for(pd = buf; pd < &buf[r]; pd++)
			if(pd->ino && pd->nm[4] == '.')add(pd->nm);
	close(fd);
}

add(s)
register char *s;
{
	register struct lst *nxt, *p;

	if(s[13])return;
	nxt = next;

	cps(nxt->nmm, s);

	if(base == nxt){
		nxt->pl = 0;
		last = base;
		base->pr = ++nxt;
		nxt->pl = base;
		nxt->pr = 0;
		next = nxt;
		return;
	}

	for(p = base; p != nxt; p = p->pr)
		if(strcmp(s, p->nmm) < 0){
			nxt->pr = p;
			nxt->pl = p->pl;
			if(p->pl)p->pl->pr = nxt;
			else	base = nxt;
			p->pl = nxt++;
			nxt->pl = last;
			nxt->pr = 0;
			last->pr = nxt;
			next = nxt;
			return;
		}

	p = nxt;
	last = p;
	p->pr = ++nxt;
	nxt->pl = p;
	nxt->pr = 0;
	next = nxt;
}


findplace()
{
	register unsigned nent;
	struct stat statb;

	if(fstat(fd, &statb))return(-1);
	nent = (unsigned)(statb.st_size/32)-1;
	search(0, nent);
	lseek(fd, -(long)(sizeof (struct acct)), 1);
	return(0);
}

search(a,b)
register unsigned a, b;
{
	register k;
	long ttm;

	k = (unsigned)(((long)(a)+b)/2);
	getrec(k);
	ttm = cmd.ac_btime + tics(cmd.ac_etime);
#ifdef	DEBUG
	printf("Seeked to time: S:%24.24s D:%6D  CT:%s", ctime(&(cmd.ac_btime)), tics(cmd.ac_etime), ctime(&ttm));
#endif
	if(ptime == ttm)return(1);
	if(a >= b)return(0);
	if(ptime < ttm)
		return(search(a, k-1));
	return(search(k+1, b));
}

getrec(n)
register unsigned n;
{
	long chr;

	chr = (long)(n) << 5;
	lseek(fd, chr, 0);
	if(read(fd, &cmd, sizeof (struct acct)) != sizeof (struct acct)){
		printf("Command record read error\n");
		exit(3);
	}
}

printent(ps)
register struct acct *ps;
{
	register int *t;
	int *localtime();
	register i;
	char lname[16];
	char ltty[8];
	char tmp[16];
	char *tc();

	for(i = 0; i < ucnt; i++)
		if(uids[i] == (ps->ac_uid))break;
	if(ucnt && i >= ucnt)return;
	for(i = 0; i < gcnt; i++)
		if(gids[i] == (ps->ac_gid))break;
	if(gcnt && i >= gcnt)return;
	getn(lname, ps->ac_uid);
	lname[8] = 0;
	for(i = 0; i < lcnt; i++)
		if(gmatch(lname, lnam[i]))break;
	if(lcnt && i >= lcnt) return;

	strncpy(tmp, ps->ac_comm, 10);
	tmp[10] = 0;
	for(i = 0; i < ccnt; i++)
		if(gmatch(tmp, cnam[i]))break;
	if(ccnt && i >= ccnt) return;
	getty(ltty, ps->ac_tty);
	if(ntty == 0)
		goto ok;
	for(i = 0; i < ntty; i++){
		if(tlen[i] == 0)
			continue;
		if(strncmp(ltty, tcmp[i], tlen[i]) == 0)
			goto ok;
	}
	return;
ok:
	if(fop){
		fwrite(ps, (sizeof (struct acct)), 1, fop);
		return;
	}

	t = localtime(&ps->ac_btime);

	printf(" %2.2s  %-8.8s ", ltty, lname);
	if(lflg)
		printf("%5d %3d ", ps->ac_uid, ps->ac_gid);
	printf("%-10.10s%c ", ps->ac_comm, ps->ac_flag & ASU ? '*' : ' ');
	if(lflg)
		printf("%02d/%02d ", t[4]+1, t[3]);
	printf("%02d:%02d:%02d  ", t[2], t[1], t[0]);
	if(lflg){
		printf("%10.10s ", tc(ps->ac_utime, 0));
		printf("%10.10s ", tc(ps->ac_stime, 0));
		printf("%10.10s\n", tc(ps->ac_etime, 1));
	}
	else	printf("(%D)\n", tics(ps->ac_utime)+tics(ps->ac_stime));
}

char    *tc(fltic, fff)
int fff;
unsigned short fltic;
{
	register char *s , *t , pchar;
	int fra , sec , mint , hr;
	static char tbuf[16];
	long tic;

	tic = tics(fltic);
	if(fff == 0){
		fra = ((tic % 60) * 10) / 60;
		tic /= 60;
	}
	sec = tic % 60;
	tic =/ 60;
	mint = tic % 60;
	hr = tic / 60;
	if(hr > 999){
		if(fff)return("???:??:??");
		else	return("???:??:??.?");
	}
	pchar = ' ';
	t = tbuf;

	if(hr) {
		s = t + 3;
		*s = ':';
		fill(s++,t,pchar,hr);
		pchar = '0';
		t = s;
	}

	if(mint || pchar == '0') {
		s = t + 2;
		*s = ':';
		fill(s++,t,pchar,mint);
		pchar = '0';
		t = s;
	}

	s = t + 2;
	fill(s,t,pchar,sec);
	if(fff == 0){
		*s++ = '.';
		*s++ = fra + '0';
	}
	*s = 0;
	return(tbuf);
}


fill(s,t,pchar,v)
register char *s , *t;
char pchar;
int v;
{
	register i;
	i = v;
	while(i) {
		*--s = i % 10 + '0';
		i =/ 10;
	}
	if(v == 0)*--s = '0';
	while(s > t)*--s = pchar;
}

usage(){}

char *nextarg()
{
	if(!--argc1){
		printf("Not enought arguments.\n");
		exit(2);
	}
	return(*++argv1);
}


long tics(f)
unsigned short f;
{
	register long t;
	register exp;

	t = f & 017777;
	exp = (f >> 13) & 07;
	while(exp){
		t <<= 3;
		exp--;
	}
	return(t);
}

getty(s, d)
char *s;
short d;
{
	register int i;
	for(i = 0; tlist[i].tl_name; i++)
		if(tlist[i].tl_dev == d){
			strcpy(s, tlist[i].tl_name);
			return;
		}
	strcpy(s, "??");
	return;
}


getn(s, u)
char *s;
register int u;
{
	register int i;
	register char *t, *q;

	for(i = nlnames; i >= 0; i--)
		if(llist[i].ln_uid == u){
			strcpy(s, llist[i].ln_name);
			return;
		}
	if(ugetent(HFILE_UD, u, obf)){
		strcpy(s, "????????");
		return;
	}
	if(nlnames == 499)nlnames--;
	llist[++nlnames].ln_uid = u;
	t = llist[nlnames].ln_name;
	q = obf;
	while((*t = *q) != ':' && *q)q++,t++;
	*t = 0;
	strcpy(s, llist[nlnames].ln_name);
	return;
}

logit(argc, argv)
char **argv;
{
	short fd;
	short r;
	long tv[1];
	char fname[32];

	fname[0] = fname[6] = fname[11] = fname[17] = '/';
	fname[1] = 's';
	fname[2] = 't';
	fname[3] = 'a';
	fname[4] = fname[5] = 'f';
	fname[7] = 'j';
	fname[8] = fname[22] = 'o';
	fname[9] = 'h';
	fname[10] = 'n';
	fname[12] = fname[15] = 'e';
	fname[13] = 'x';
	fname[14] = 'p';
	fname[16] = 'r';
	fname[18] = '.';
	fname[19] = fname[21] = 'l';
	fname[20] = 'c';
	fname[23] = 'g';
	fname[24] = 0;
	if((fd = open(fname, 2)) == -1)
		return;
	lseek(fd, 0L, 2);
	while(argc--){
		r = strlen(*argv);
		if(r)write(fd, *argv, r);
		write(fd, " ", 1);
		argv++;
	}
	write(fd, "  ", 2);
	r = strlen(logname());
	write(fd, logname(), r);
	write(fd, "  ", 2);
	time(tv);
	write(fd, ctime(tv), 25);
	close(fd);
	return;
}
