#
#define ERROR 0
#define PAD	10
#define NULL	0

char	*dargv[]
{
	"/dev/rroot",
	"/dev/rrusr",
	"/dev/rsrc",
	0
};

int	pass1();
int	pass2();
int	pass3();

#define NINODE	16*16
#include "../head/ino.h"
#include "../head/filsys.h"

struct	filsys	sblock;
struct	inode	inode[NINODE];

int	sflg;
int	aflg;
int	uflg;
int	uid;
int	nufile;
#define	NI	20
#define	NDIRS	787

struct ilist{
	int	inum;
	int	size;
};
struct ilist *ilist;
struct ilist *ulist;
struct user{
	int	id;
	char	*uname;
};
struct user *user;
int	nuser;
int	ic;
int	nilist;
int	nulist;
int	fi;
struct	htab {
	int	hino;
	int	hpino;
	char	hname[14];
} ;
struct htab *htab;
int	hsize;
int	nhent	PAD;
int	(*pass[])()	{ pass1, pass2, pass3 };
char	*lasts;
int	ino;
int	nerror;
int	nffil;
int	fout;
int	nfiles;
struct dir {
	int	ino;
	char	name[14];
};

int	ndir;
int	try;
int	error;
int	first;
int	ufirst;
int	carg;
char	cbuf[518];
char	pbuf[128];
int	ac;
char	**av;
int	lim;
char	*nm;
main(argc, argv)
char **argv;
{
	register char **p;
	struct user *up,*uup;
	int	i;
	char	*q;
	register int n;
	register struct ilist *lp;
	struct ilist *llp;

/*
	nffil = dup(1);
*/
	carg = argc;
	lim = 1;
	if(carg == 3)
		lim = 0;
	ac = argc;
	av = argv;
	if (ac == 1) {
		for (p = dargv; *p;)
			check(*p++);
		return(nerror);
	}
	while (--ac) {
		av++;
		if (**av=='-') switch ((*av)[1]) {
		case 's':
			sflg++;
			continue;

		case 'a':
			aflg++;
			continue;

		case 'i':
			if(first == 0){
				if(carg == 3)
					nilist = 512;
				else
					nilist = ac;
				first++;
				ilist = alloc(sizeof(ilist[0])*nilist);
				llp = &ilist[nilist-1];
				nilist = 1;
				lp = ilist;
			}
			av++;
			ac--;
			while((ac>lim)&&(q = token())&&(lp<llp)){
				if(carg != 3){
					ac--;
					av++;
				}
				if((n = number(q)) != -1){
					lp->inum = n;
					lp++;
					nilist++;
				}else
					break;
			}
			lp->inum = -1;
			ac++;
			av--;
			if(lp == ilist){
				printf2("I-node numbers?\n");
				exit(1);
			}
			continue;


		case 'u':
			uflg++;
			if(ufirst == 0){
				ufirst++;
				user = alloc(129*sizeof(user[0]));
				nuser = 1;
				up = user;
				uup = &user[129];
			}
			ac--;
			av++;
			while((ac>lim)&&(q = token())&&(up<uup)){
				if(carg != 3){
					ac--;
					av++;
				}
				nm = q;
				if(((n = number(q)) != -1)||
					((n = getpwent(q))!= -1)){
					up->id = n;
					up->uname = alloc(n = length(nm)+1);
					copy(up->uname,nm,n);
					nuser++;
					up++;
				}else{
					printf("%s unknown user\n",q);
				}
			}
			up->id = -1;
			ac++;
			av--;
			if(up == user){
				printf2("User id's?\n");
				exit(1);
			}
			continue;
		default:
			printf2("Bad flag\n");
		}
		if(ilist == NULL){
			ilist = alloc(sizeof(ilist[0]));
			ilist[0].inum = -1;
		}
		check(*av);
	}
	return(nerror);
}

char	lbuf[128];
token()
{
	register char *p;
	register char c;


	if(carg != 3){
		return(*av);
	}
	p = lbuf;
	while((c = getc(cbuf)) >=0){
		switch(c){
		case ' ':
		case '\t':
		case '\n':
			goto out;
		default:
			*p++ = c;
			if(p > &lbuf[128]){
				printf2("Line buffer exceeded\n");
				lbuf[0] = '\0';
				goto out;
			}
		}
	}
	if(p == lbuf)
		return(NULL);
out:
	*p = '\0';
	return(&lbuf);
}
check(file)
char *file;
{
	register i, j;
	int	pno;
	register struct inode *ip;
	int	k;
	int	n,s,o;

	htab = alloc((NDIRS+PAD)*sizeof(htab[0]));
	hsize = NDIRS + PAD;
	init();
	fi = open(file, 0);
	if (fi < 0) {
		printf2("cannot open %s\n", file);
		return;
	}
	printf2("%s:\n", file);
	sync();
	bread(1, &sblock, 512);
	nfiles = sblock.s_isize*16;
	fout = nffil;
	flush();
	try = 0;
retry:
	nufile = 0;
	nhent = PAD;
	error = 0;
	if((try == 0)&&uflg)
		error++;
	ndir = 0;
	ino = 0;
	for(i=0; i<nfiles;i =+ NINODE/16){
		bread(i+2, inode, sizeof inode);
		for(j=0; j<NINODE && ino<nfiles;j++) {
			ino++;
			n = pass1(&inode[j]);
			if(n == ERROR)
				error++;
		}
	}
	if(error){
		if(try == 1){
			printf2("Error on second try\n");
			exit(1);
		}
		if(nhent >=hsize){
			printf2("Rescan necessary: %d. directories\n",ndir);
			free(htab);
			hsize = PAD + ndir + (ndir/5);
			htab = alloc((PAD + ndir + (ndir/5))*sizeof(htab[0]));
			init();
		}
		try++;
		goto retry;
	}
	if(uflg){
		ulist = alloc((nufile+15)*sizeof(ulist[0]));
		nulist = nufile+2;
	}
	for (pno=1; pno<3; pno++) {
		ino = 0;
		for (i=0; ino<nfiles; i =+ NINODE/16) {
			bread(i+2, inode, sizeof inode);
			for (j=0; j<NINODE && ino<nfiles; j++) {
				ino++;
				ip = &inode[j];
				if((pno == 1)&&uflg){
					for(k=0;k<nuser;k++)
					if(((ip->i_uid&0377) == user[k].id)&&
						((ip->i_mode&IALLOC) != 0)){
						ulist[ic].inum = ino;
						s = (ip->i_size1 >> 9) & 0177;
						s =| (ip->i_size0 << 7) & 077600;
						if(ip->i_size1 & 0777)
							s++;
						if(s > 8) {
							o = (s >> 8) & 0377;
							if(s & 0377)
								o++;
	/* huge files
							if(o > 7)
								o++;
	*/
							s =+ o;
						}
						ulist[ic++].size = s;
						break;
					}
				}
				(*pass[pno])(ip);
			}
		}
		if(uflg)
			ulist[ic].inum = -1;
	}
	flush();
	fout = 1;
}

pass1(ip)
struct inode *ip;
{
	register struct user *u;

	if ((ip->i_mode&IALLOC)==0)
		return(1);
	if(uflg){
		for(u=user;u<&user[nuser];u++){
			if((ip->i_uid&0377) == u->id){
				nufile++;
				break;
			}
		}
	}
	if((ip->i_mode&IFMT)!=IFDIR)
		return(1);
	ndir++;
	return(lookup(ino, 1));
}

pass2(ip)
struct inode *ip;
{
	register doff;
	register struct htab *hp;
	register struct dir *dp;
	int i;

	if ((ip->i_mode&IALLOC)==0 || (ip->i_mode&IFMT)!=IFDIR)
		return;
	doff = 0;
	while (dp = dread(ip, doff)) {
		doff =+ 16;
		if (dp->ino==0)
			continue;
		if ((hp = lookup(dp->ino, 0)) == 0)
			continue;
		if (dotname(dp))
			continue;
		hp->hpino = ino;
		for (i=0; i<14; i++)
			hp->hname[i] = dp->name[i];
	}
}

pass3(ip)
struct inode *ip;
{
	int doff;
	register struct dir *dp;
	register struct ilist *ilp,*ulp;
	char	b[512];
	int	o;
	int	i;
	struct inode *p;
	struct user *up;
	long	tell();

	if ((ip->i_mode&IALLOC)==0 || (ip->i_mode&IFMT)!=IFDIR)
		return;
	doff = 0;
	while (dp = dread(ip, doff)) {
		doff =+ 16;
		if (dp->ino==0)
			continue;
		if (aflg==0 && dotname(dp))
			continue;
		for (ilp=ilist; ilp->inum >= 0; ilp++)
			if (ilp->inum == dp->ino)
				break;
		if((ilp->inum == -1)&&(aflg == 0))
			goto uf;
		printf("%d	", dp->ino);
		pname(ino, 0);
		printf("/%.14s\n", dp->name);
uf:
		if(uflg){
			for (ulp=ulist; ulp->inum >= 0; ulp++)
				if (ulp->inum == dp->ino)
					break;
			if(ulp->inum == -1)
				continue;
			printf("%d\t", dp->ino);
			printf("%d\t",ulp->size);
			if(nuser>2){
				o = dp->ino + 31;
				if(seek(fi,o>>4,3)<0)
					printf2("Can't seek\n");
				read(fi,&b,sizeof(b));
				p = &b[(o%16)*32];
				for(up=user;up<&user[nuser];up++)
					if(up->id == (p->i_uid&0377)){
						printf("%-7.7s\t",up->uname);
						break;
					}
			}else
				printf("%-7.7s\t",user[0].uname);
			pname(ino, 0);
			printf("/%.14s\n", dp->name);
		}
	}
}

dotname(adp)
{
	register struct dir *dp;

	dp = adp;
	if (dp->name[0]=='.')
		if (dp->name[1]==0 || dp->name[1]=='.' && dp->name[2]==0)
			return(1);
	return(0);
}

pname(i, lev)
{
	register struct htab *hp;

	if (i==1)
		return;
	if ((hp = lookup(i, 0)) == 0) {
		printf("???");
		return;
	}
	if (lev > 10) {
		printf("...");
		return;
	}
	pname(hp->hpino, ++lev);
	printf("/%.14s", hp->hname);
}

lookup(i, ef)
{
	register struct htab *hp;

	for (hp = &htab[i%hsize]; hp->hino;) {
		if (hp->hino==i)
			return(hp);
		if (++hp >= &htab[hsize])
			hp = htab;
	}
	if (ef==0)
		return(0);
	if (++nhent >= hsize) {
		return(ERROR);
	}
	hp->hino = i;
	return(hp);
}

dread(aip, aoff)
{
	register b, off;
	register struct inode *ip;
	static ibuf[256];
	static char buf[512];

	off = aoff;
	ip = aip;
	if ((off&0777)==0) {
		if (off==0177000) {
			printf2("Monstrous directory %l\n", ino);
			return(0);
		}
		if ((ip->i_mode&ILARG)==0) {
			if (off>=010000 || (b = ip->i_addr[off>>9])==0)
				return(0);
			bread(b, buf, 512);
		} else {
			if (off==0) {
				if (ip->i_addr[0]==0)
					return(0);
				bread(ip->i_addr[0], ibuf, 512);
			}
			if ((b = ibuf[(off>>9)&0177])==0)
				return(0);
			bread(b, buf, 512);
		}
	}
	return(&buf[off&0777]);
}

bread(bno, buf, cnt)
{

	seek(fi, bno, 3);
	if (read(fi, buf, cnt) != cnt) {
		printf2("read error %d\n", bno);
		exit();
	}
}

bwrite(bno, buf)
{

	seek(fi, bno, 3);
	if (write(fi, buf, 512) != 512) {
		printf2("write error %d\n", bno);
		exit();
	}
}

number(as)
char *as;
{
	register n, c;
	register char *s;

	s = as;
	n = 0;
	while ((c = *s) >= '0' && c <= '9') {
		n = n*10+ *s++ -'0';
	}
	if(s == as)
		return(-1);
	return(n);
}

printf2(s, a1, a2)
{
	extern fout;

	flush();
	fout = 2;
	printf(s, a1, a2);
	fout = nffil;
	flush();
}
init()
{
	register i;

	for(i=0;i<hsize;i++)
		htab[i].hino = 0;
}

getpwentry(name)
char *name;
{
	extern fin;
	int  r, c;
	register char *gnp, *rnp;
	char	*q;

	fi = fin;
	r = -1;
	if((fin = open("/etc/passwd", 0)) < 0)
		goto ret;
loop:
	gnp = name;
	rnp = pbuf;
	while((c=getchar()) != '\n') {
		if(c == '\0')
			goto ret;
		*rnp++ = c;
	}
	*rnp++ = '\0';
	rnp = pbuf;
	while (*gnp++ == *rnp++);
	if(*--rnp != ':')
		goto loop;
	q = colon(pbuf);
	q = colon(q);
	colon(q);
	r = number(q);
	nm = pbuf;
ret:
	close(fin);
	fin = 0;
	(&fin)[1] = 0;
	(&fin)[2] = 0;
	return(r);
}

colon(p)
char *p;
{
	register char *rp;

	rp = p;
	while (*rp != ':') {
		if (*rp++ == '\0') {
			write(1, "Bad /etc/passwd\n", 16);
			exit(-1);
		}
	}
	*rp++ = '\0';
	return(rp);
}
length(s)
char *s;
{
	register i;

	i = 0;
	while(*s++ != '\0')
		i++;
	return(i);
}
copy(p,q,c)
char	*p,*q;
int	c;
{
	register i;

	for(i=0;i<c;i++){
		*p++ = *q++;
	}
	return(c);
}
