#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ino.h>
#include <sys/inode.h>
#include <sys/dir.h>
#include <sys/filsys.h>
#include <mnttab.h>

#define	NINODE	(INOPB*16)

struct	filsys	sblock;
struct	dinode	dinode[NINODE];
struct	mnttab	mount[NMOUNT];

int	aflg;
#define	NI	1024
#define	NDIRS	2042

int	ilpp;
int	ilist[NI] {-1};
long	sz[128] { -1L};
long	nad[NADDR];
long	*psz;
int	fi;
struct	htab {
	int	hino;
	int	hpino;
	char	hname[14];
} htab[NDIRS];
int	nhent	= 10;
int pass1(), pass2(), pass3();
int	(*pass[])()	{ pass1, pass2, pass3 };
char	*msg[3] = {
"Read Inode table",
"Read contents of all directories",
"Print names for Inodes"
};

char	*lsflgs;
char	*lasts;
int	ino;
int	nerror;
int	nfiles;
char	fbuf[512] , *ff , *pf , *pfb , *fils;

int spflg , sflg , dflg , nflg , iilp , lflg , bflg , mflags;
int gflg = -1;
int uflg = -1;
int glflg = -1;
int ggflg = -1;
int ulflg = -1;
int ugflg = -1;
long clong(), lsize();
long szflg = -1L;
int vflg = 0;
int nop = 1;

int ino;
int nmnt = 0;
int ndirs = 0;
char *dev, *getname();


main(argc, argv)
char **argv;
{
	int fd;

	register int *ilp;
	register char c;
	register v;

	if((v = getgid()) && v != 2)
		exit(7);
	if (argc == 1) {
		fprintf(stderr, "Usage: sncheck  [-a] [-sz ######] [-s] [-sp] [-b] [-d] [-n] [-l[lsflgs]] [-v]\n");
		fprintf(stderr, " [-m mask#] [-g[lg] group id] [-u[lg] user id] [-i inodes...] special ... ...\n");
		exit(1);
	}
	ilp = ilist;
	psz = sz;
	fils = "/";
	for(v = 0; v < 128; v++)
		sz[v] = -1L;
	while(--argc){
		argv++;
		if(**argv != '-')break;
		switch(*(*argv+1)){
	case 'v':
		vflg++;
		break;

	case 'a':
		aflg++;
		break;

	case 'm':
		if(--argc == 0)goto argcnt;
		mflags =| atoo(*++argv);
		nop = 0;
		break;

	case 'g':
		c = *(*argv+2);
		if(--argc == 0)goto argcnt;
		v = val(*++argv);
		switch(c){
		case 'l':
		case '<':
			glflg = v;
			break;
		case 'g':
		case '>':
			ggflg = v;
			break;
		default:
			gflg = v;
			break;
		}
		nop = 0;
		break;

	case 'u':
		nop = 0;
		c = *(*argv+2);
		if(--argc == 0)goto argcnt;
		v = val(*++argv);
		switch(c){
		case 'l':
		case '<':
			ulflg = v;
			break;
		case 'g':
		case '>':
			ugflg = v;
			break;
		default:
			uflg = v;
			break;
		}
		break;

	case 'b':
		nop = 0;
		bflg = 010000;
		break;

	case 's':
		if(*(*argv+2) == 'p')spflg = 060000;
		else	if(*(*argv+2) == 'z'){
				while(--argc){
					argv++;
					if(**argv == '-' || **argv == '/'){
						argc++;
						argv--;
						break;
					}
					if(psz < &sz[128])*psz++ = clong(*argv);
					else	if(psz == &sz[128]){
						psz++;
						fprintf(stderr, "sncheck: Too many sizes; stopped at: %D\n\n", sz[127]);
					}
				}
				if(argc == 0)goto argcnt;
			}
		else	sflg = 07000;
		nop = 0;
		break;

	case 'd':
		nop = 0;
		dflg = 040000;
		break;
	case 'n':
		nflg++;
		break;

	case 'l':
		lsflgs = *argv + 1;
		*lsflgs = '-';
		if(*(lsflgs+1) == 0)lsflgs = "-lsbid";
		lflg++;
		break;

	case 'i':
		v = 0;
		while(--argc){
			argv++;
			if(**argv == '-' || **argv == '/'){
				argc++;
				argv--;
				break;
			}
			if(iilp >= NI){
				if(v++ == 0){
					fprintf(stderr, "sncheck: Too many inodes on command line\n");
					fprintf(stderr, "Last inode used = %d - Ignoring all following it\n",*(ilp-1));
				}
				continue;
			}
			*ilp++ = val(*argv);
			iilp++;
		}
		if(argc == 0)goto argcnt;
		break;

	default:
		fprintf(stderr, "Illegal option: -%c\n", *(*argv+1));
		exit(2);
		break;

		}
	}
	mflags =| (bflg|sflg);
	if(vflg)
		fprintf(stdout, "Mask = 0%o\n", mflags);
	psz = sz;
	if(vflg && *psz != -1L){
		fprintf(stdout, "Search for files of size: ");
		while(*psz != -1L)
			fprintf(stdout, "%D  ", *psz++);
		fprintf(stdout, "\n");
	}
	if((fd = open("/etc/mnttab",0)) == -1){
		fprintf(stderr, "sncheck: No mount table\n");
		exit(2);
	}
	nmnt = read(fd, &mount, sizeof mount)/(sizeof (struct mnttab));
	close(fd);

	while(argc--)
		check(dev = *argv++);
	exit(0);
argcnt:
	fprintf(stderr, "sncheck: No device specified\n");
	exit(2);

}

check(file)
char *file;
{
	register i, j, pno;
	int *ilp;

	fi = open(file, 0);
	if (fi < 0) {
		fprintf(stderr, "\nNo device: %s\n",file);
		return;
	}
	sync();
	ilpp = iilp;
	bread(1L, &sblock, 512);
	nfiles = (sblock.s_isize-2)*INOPB;
	nhent = 10;
	ndirs = 0;
	fprintf(stdout, "%s (/%.6s):\n",dev,sblock.s_fname);
	for (i=0; i<NDIRS; i++)
		htab[i].hino = 0;
	for (pno=0; pno<3; pno++) {
		if(pno == 1){
			if(vflg){
				fprintf(stdout, "%d directories\n", ndirs);
				if(ilpp)
					fprintf(stdout, "%d file%s specification\n", ilpp,
						ilpp == 1 ? " matches" : "s match");
			}
			ilist[ilpp] = -1;
			if(nop == 0 && (ilpp == 0 || nflg))
				goto done;

		}
		if(vflg)fprintf(stdout, "Pass %d: %s\n", pno+1, msg[pno]);
		ino = 0;
		for (i=0; ino<nfiles; i =+ NINODE/INOPB) {
			bread((long)(i+2), dinode, sizeof dinode);
			for (j=0; j<NINODE && ino<nfiles; j++) {
				ino++;
				(*pass[pno])(&dinode[j]);
			}
		}
	}
done:
	fprintf(stdout, "\n\n");
	close(fi);
}

pass1(ip)
register struct dinode *ip;
{
	register u, g;
	long lsz;
	if(ip->di_mode == 0)return;
	g = ip->di_gid;
	u = ip->di_uid;
	if(nop)goto skip;
	if((glflg != -1 && g >= glflg) ||
	   (ggflg != -1 && g <= ggflg) ||
	   ( gflg != -1 && g != gflg ) ||
	   (ulflg != -1 && u >= ulflg) ||
	   (ugflg != -1 && u <= ugflg) ||
	   ( uflg != -1 && u != uflg))goto skip;
	if(sz[0] != -1L){
		lsz = ip->di_size;
		psz = sz;
		while(*psz != -1L)
			if(*psz == lsz)break;
			else	psz++;
		if(*psz == -1L)goto skip;
	}
	g = ip->di_mode & IFMT;
	if((ip->di_mode & mflags) ||
	   ((g == IFBLK || g == IFCHR) && spflg) ||
	   (dflg && (g == IFDIR)));
	else	if(dflg || spflg || mflags)goto skip;

	ilist[ilpp] = ino;
	if(++ilpp == NI){
		fprintf(stderr, "sncheck: Too many inodes qualify\n");
		exit(2);
	}

	if(nflg)
		if(lflg)ls(ino,1);
		else	fprintf(stdout, "%5d\n", ino);
skip:
	if((ip->di_mode&IFMT)!=IFDIR)
		return;
	lookup(ino, 1);
	ndirs++;
}

pass2(ip)
struct dinode *ip;
{
	register doff;
	register struct htab *hp;
	register struct direct *dp;
	int i;

	if((ip->di_mode & IFMT) != IFDIR)
		return;
	doff = 0;
	l3tol(nad, ip->di_addr, NADDR);
	while (dp = dread(ip, doff)) {
		doff =+ 16;
		if (dp->d_ino==0)
			continue;
		if ((hp = lookup(dp->d_ino, 0)) == 0)
			continue;
		if (dotname(dp))
			continue;
		hp->hpino = ino;
		for (i=0; i<14; i++)
			hp->hname[i] = dp->d_name[i];
	}
}

pass3(ip)
struct dinode *ip;
{
	register doff;
	register struct direct *dp;
	register int *ilp;

	if ((ip->di_mode & IFMT) != IFDIR)
		return;
	doff = 0;
	pfb = getname(fbuf);
	if(*(fils+1)){
		ff = fils;
		while(*pfb++ = *ff++);
		pfb--;
	}

	l3tol(nad, ip->di_addr, NADDR);
	while (dp = dread(ip, doff)) {
		doff =+ 16;
		if (dp->d_ino==0)
			continue;
		if (aflg == 0 && dotname(dp))
			continue;
		ilp = ilist;
		while(*ilp != -1)
			if(*ilp == dp->d_ino)break;
			else	ilp++;
		if(*ilp == -1 && ilp != ilist)continue;
		ff = pfb;
		pname(ino, 0);
		pf = dp->d_name;
		*ff++ = '/';
		while(pf < dp->d_name+14 && (*ff++ = *pf++));
		*ff = 0;
		if(lflg)ls(fbuf, 0);
		else	fprintf(stdout, "%4d %s\n",dp->d_ino,fbuf);
	}
}

dotname(adp)
{
	register struct direct *dp;

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

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

	if (i==2)
		return;
	if ((hp = lookup(i, 0)) == 0) {
		fprintf(stderr, "Lookup error in directory I=%d, File: ", ino);
		*ff++ = '?';
		*ff++ = '?';
		*ff++ = '?';
		return;
	}
	if (lev > 10) {
		*ff++ = '.';
		*ff++ = '.';
		*ff++ = '.';
		return;
	}
	pname(hp->hpino, ++lev);
	*ff++ = '/';
	pf = hp->hname;
	while(pf < hp->hname+14 && (*ff++ = *pf++));
	if(*(ff-1) == 0)ff--;
}

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

	for (hp = &htab[i%NDIRS]; hp->hino;) {
		if (hp->hino==i)
			return(hp);
		if (++hp >= &htab[NDIRS])
			hp = htab;
	}
	if (ef==0)
		return(0);
	if (++nhent >= NDIRS) {
		fprintf(stderr, "Out of core-- increase NDIRS\n");
		exit(1);
	}
	hp->hino = i;
	return(hp);
}

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

	off = aoff;
	ip = aip;
	if ((off&0777)==0) {
		if (off==0177000) {
			fprintf(stderr, "Monstrous directory %l\n", ino);
			return(0);
		}
		b = (off >> 9) & 0177;
		if(b < 10){
			if(nad[b] == 0)return(0);
			bread(nad[b], buf, 512);
		} else {
			if(off == 5120){
				if(nad[10] == 0)return(0);
				bread(nad[10], ibuf, 512);
			}
			b -= 10;
			if(ibuf[b] == 0)
				return(0);
			bread(ibuf[b], buf, 512);
		}
	}
	return(&buf[off&0777]);
}

bread(bno, buf, cnt)
long	bno;
{

	lseek(fi, bno << 9, 0);
	if (read(fi, buf, cnt) != cnt) {
		fprintf(stderr, "read error %d\n", bno);
		exit();
	}
}




eql(s,t)
register char *s , *t;
{
	register char *m;

	m = t+10;

	while(t < m && *s == *t++)
		if(*s++ == 0)return(1);
	if(t >= m)return(1);
	return(0);
}


ls(s,f)
{
	char *str();
	register char *t;
	fflush(stdout);
	fflush(stderr);
	if(fork())wait(&f);
	else	{
		if(f == 0){
			execl("/bin/ls","ls",lsflgs,s,0);
			execl("/bin/ls","ls","-ld",s,0);
bad:
			write(2,"sncheck (child): No ls\n",23);
			exit(1);
		}
		if(dev[6] == 'r'){
			t = &dev[6];
			while(*t = *(t+1))t++;
		}
		execl("/bin/ls","ls","-I",dev,str(s),0);
		goto bad;
	}
}

val(s)
register char *s;
{
	register int i;
	i = 0;
	while(*s >= '0' && *s <= '9')i = i * 10 + *s++ - '0';
	return(i);
}

char *str(i)
register i;
{
	static char n[16];
	register char *t;
	t = &n[15];
	*t = 0;
	do {
		*--t = i%10 + '0';
		i =/ 10;
	} while(i);
	return(t);
}




atoo(s)
register char *s;
{
	register int i;
	i = 0;
	while(*s >= '0' && *s <= '9')
		i = (i << 3) | ((*s++) - '0');
	return(i);
}



char *getname(s)
register char *s;
{
	register char *t;
	register struct mnttab *pm;
	char *pd;

	pd = dev+5;
	if(dev[6] == 'r')pd++;
	for(pm = &mount; pm < &mount[nmnt]; pm++)
		if(eql(pd, pm->mt_dev)){
			t = pm->mt_filsys;
			while(*s++ = *t++);
			s--;
			if(*(s-1) == '/')s--;
			return(s);
		}
	if(lflg && nflg == 0){
		fprintf(stdout, "%s: Not mounted for ls\n", dev);
		exit(1);
	}
	return(s);
}




long clong(s)
register char *s;
{
	long ltmp;
	ltmp = 0L;
	while(*s)
		if(*s >= '0' && *s <= '9')
				ltmp = ltmp*10L + (long)((*s++) - '0');
		else s++;
	return(ltmp);
}

