/* find -- find files in a pathname.
	Use of find is documented in /usr/man/man1/find.1 .

	In addition, find has a secret first arg "+" which
	causes each file name to be printed along with a period
	if the predicates succeed.
 */
int randlast;
char *pathname;
int verbose;
struct anode {
	int (*F)();
	struct anode *L, *R;
} node[100];
int nn;  /* number of nodes */
char *fname, *path;
int now[2];
int ap, ac;
char **av;

struct ibuf {
	int	idev;
	int	inum;
	int	iflags;
	char	inl;
	char	iuid;
	char	igid;
	char	isize0;
	int	isize;
	int	iaddr[8];
	int	iatime[2];
	int	imtime[2];
} statb;

main(argc,argv) char *argv[]; {

struct anode *exlist;
struct anode *exp();
struct anode *e1();
struct anode *e2();
struct anode *e3();
struct anode *mk();
int find();

	time(&now);
	ac = argc; av = argv; ap = 2;
	pathname = argv[1];
	if(compstr(argv[1],"+")==0) {
		verbose++;
		ap++;
		pathname = argv[2];
	} else verbose = 0; 
	argv[argc] = 0;
	if(argc<3) {
		printf("Insufficient args\n");
		exit(-1);
	}
	if(!(exlist = exp())) { /* parse and compile the arguments */
		printf("Odd usage\n");
		exit(-1);
	}
	if(ap<argc) {
		printf("Missing conjunction\n");
		exit(-1);
	}
	descend(pathname,'f',find,exlist); /* to find files that match  */
}

/* compile time functions:  priority is  exp()<e1()<e2()<e3()  */

struct anode *exp() { /* parse -o ... */
	int or();
	int p1;
	char *na;
	p1 = e1() /* get left operand */ ;
	if(compstr(na=nxtarg(),"-o")==0) {
		randlast--;
		return(mk(&or,p1,exp()));
	}
	else if(*na!=0) --ap;
	return(p1);
}
struct anode *e1() { /* parse -a */
	int and();
	int p1;
	char *na;
	p1 = e2();
	if(compstr(na=nxtarg(),"-a")==0) {
		randlast--;
		return(mk(&and,p1,e1()));
	}
	else if(*na!=0) --ap;
	return(p1);
}
struct anode *e2() { /* parse not (!) */
	int not();
	char *na;
	if(randlast) {
		printf("operand follows operand.\n");
		exit(-1);
	}
	randlast++;
	if(compstr(na=nxtarg(),"!")==0)
		return(mk(&not,e3(),0));
	else if(*na!=0) --ap;
	return(e3());
}
struct anode *e3() { /* parse parens and predicates */
	int exeq(), ok(), glob(),  mtime(), atime(), user(),
		group(), size(), perm(), links(), print(),
		type();
	int p1, i;
	char *a, *b, s;
	double atof();
	a = nxtarg();
	if(compstr(a,"(")==0) {
		randlast--;
		p1 = exp();
		a = nxtarg();
		if(compstr(a,")")!=0) goto err;
		return(p1);
	}
	else if(compstr(a,"-print")==0) {
		return(mk(&print,0,0));
	}
	b = nxtarg();
	s = *b;
	if(s=='+') b++;
	if(compstr(a,"-name")==0)
		return(mk(&glob,b,0));
	else if(compstr(a,"-mtime")==0)
		return(mk(&mtime,(i=atof(b)),s));
	else if(compstr(a,"-atime")==0)
		return(mk(&atime,(i=atof(b)),s));
	else if(compstr(a,"-user")==0) {
		if((i=getunum(b)) == -1) {
			printf("Cannot find user \"%s\"\n",b);
			exit(-1);
		}
		return(mk(&user,i,s));
	}
	else if(compstr(a,"-group")==0)
		return(mk(&group,(i=atof(b)),s));
	else if(compstr(a,"-size")==0)
		return(mk(&size,(i = atof(b)),s));
	else if(compstr(a,"-links")==0)
		return(mk(&links,(i=atof(b)),s));
	else if(compstr(a,"-perm")==0) {
		for(i=0; *b ; ++b) {
			if(*b=='-') continue;
			i =<< 3;
			i = i + (*b - '0');
		}
		return(mk(&perm,i,s));
	}
	else if(compstr(a,"-type")==0) {
		i = s=='d' ? 040000 :
		    s=='b' ? 060000 :
		    s=='c' ? 020000 :
		    000000;
		return(mk(&type,i,0));
	}
	else if (compstr(a,"-exec")==0) {
		i = ap - 1;
		while(compstr(nxtarg(),";")!=0);
		return(mk(&exeq,i,0));
	}
	else if (compstr(a,"-ok")==0) {
		i = ap - 1;
		while(compstr(nxtarg(),";")!=0);
		return(mk(&ok,i,0));
	}
	err: printf("Bad option: \"%s\" \"%s\"\n",a,b);
	exit(-1);
}
struct anode *mk(f,l,r) struct anode *l,*r; { /*make an expression node*/
	node[nn].F = f;
	node[nn].L = l;
	node[nn].R = r;
	return(&(node[nn++]));
}

nxtarg() { /* get next arg from command line */
	if(ap>=ac) return("");
	return(av[ap++]);
}

find(exlist,fullname) /* execute predicat list with current file */
struct anode *exlist;
char *fullname;
{
register int i;
	path = fullname;
	if(verbose) printf("%s",path);
	for(i=0;fullname[i];++i)
		if(fullname[i]=='/') fname = &fullname[i+1];
	i = (*exlist->F)(exlist);
	if(verbose)
		if(i) printf(".\n");
		else printf("\n");
}

/* execution time functions */
and(p) struct anode *p; {
	return(((*p->L->F)(p->L)) && ((*p->R->F)(p->R))?1:0);
}
or(p) struct anode *p; {
	 return(((*p->L->F)(p->L)) || ((*p->R->F)(p->R))?1:0);
}
not(p) struct anode *p; {
	return( !((*p->L->F)(p->L)));
}
glob(p) struct { int f; char *pat; } *p;  {
	return(gmatch(fname,p->pat));
}
print() {
	printf("%s\n",path);
	return(1);
}
mtime(p) struct { int f, t, s; } *p;  {
	return(scomp((now[0]-statb.imtime[0])*3/4,p->t,p->s));
}
atime(p) struct { int f, t, s; } *p;  {
	return(scomp((now[0]-statb.iatime[0])*3/4,p->t,p->s));
}
user(p) struct { int f, u, s; } *p;  {
	return(scomp(statb.iuid,p->u,p->s));
}
group(p) struct { int f, u; } *p;  {
	return(p->u == statb.igid);
}
links(p) struct { int f, link, s; } *p;  {
	return(scomp(statb.inl,p->link,p->s));
}
size(p) struct { int f, sz, s; } *p;  {
	register int i;
	i = statb.isize0 << 7;
	i=| (statb.isize >> 9) & 0777;
	return(scomp(i,p->sz,p->s));
}
perm(p) struct { int f, per, s; } *p;  {
int i;
	i = (p->s=='-') ? p->per : 03777; /* '-' means only arg bits */
	return((statb.iflags & i & 017777) == p->per);
}
type(p) struct { int f, per, s; } *p; {
	return((statb.iflags&060000)==p->per);
}
exeq(p) struct { int f, com; } *p; {
	return(doex(p->com));
}
ok(p) struct { int f, com; } *p; {
	char c;  int yes;
	yes = 0;
	printf("%s ... %s ...? ",av[p->com],path);
	if((c=getchar())=='y') yes = 1;
	while(c!='\n') c = getchar();
	if(yes) return(doex(p->com));
	return(0);
}

/* support functions */
scomp(a,b,s) char s; { /* funny signed compare */
	if(s == '+')
		return(a > b);
	if(s == '-')
		return(a < (b * -1));
	return(a == b);
}
doex(com) {
	int ccode;
	int np, i, c;
	char *nargv[50], *ncom, *na;

	ccode = np = 0;
	while (na=av[com++]) {
		if(compstr(na,";")==0) break;
		if(compstr(na,"{}")==0) nargv[np++] = path;
		else nargv[np++] = na;
	}
	nargv[np] = 0;
	if (np==0) return(-1);
	if(fork()) /*parent*/ wait(&ccode);
	else { /*child*/
		execv(nargv[0], nargv, np);
		i = 0;
		ncom = "/usr/bin/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
		while(c=nargv[0][i])  {
			ncom[9+i++] = c;
		}
		ncom[9+i] = '\0';
		execv(ncom+4, nargv, np);
		execv(ncom, nargv, np);
		exit(-1);
	}
	return(ccode ? 0:1);
}

char fin[518];
getunum(s) char *s; { /* find username in /etc/passwd & return num. */
int i;
char str[20], *sp, c;
extern double atof();
	i = -1;
	fin[0] = open("/etc/passwd",0);
	while(c = getchar()) {
		if(c=='\n') {
			sp = str;
			while((*sp = getchar()) != ':')
				if(! *sp++) goto RET;
			*sp = '\0';
			if(compstr(str,s)==0) {
				while((c=getchar()) != ':')
					if(! c) goto RET;
				sp = str;
				while((*sp = getchar()) != ':') sp++;
				*sp = '\0';
				i = atof(str);
				break;
			}
		}
	}
	RET:
	close(fin);
	fin[0] = 0;
	return(i);
}

compstr(s1,s2) char s1[], s2[]; {   /* compare strings: */
register char *c1, *c2;
	c1 = s1;  c2 = s2;
	while(*c1 == *c2)
		if(*c1++ == '\0')
			return(0); /* s1 == s2 */
		else c2++;
	return(*c1 > *c2 ? 1 : -1);
}

int descend(name,goal,func,arg)
int (*func)();
char *name, goal;
{
	int dir /* open directory */, offset /* in directory */;
	int dsize, top;
	struct {
		int	dinode;
		char	dname[14];
	} dentry[32];
	register int i, j, k;
	char aname[128];

	if(stat(name,&statb)<0) {
		printf("--bad status %s\n",name);
		return(0);
	}
/*
	if((statb.iflags&060000)!=040000){ /*not a directory*/
/*
		if(goal=='f'||goal=='b') /* search goal for files */
/*
			(*func)(arg,name);
		return(1);
	} else  if(goal=='d' || goal=='b') /* search goal is directories */
/*
			(*func)(arg,name);
*/
	(*func)(arg,name);
	if((statb.iflags&060000)!=040000)
		return(1);

	top = statb.isize;
	for(offset=0 ; offset < top ; offset =+ 512) { /* each block */
		dsize = 512<(top-offset) ? 512 : (top-offset);
		if((dir=open(name,0))<0) {
			printf("--cannot open %s\n",name);
			return(0);
		}
		if(offset) seek(dir,offset,0);
		if(read(dir,&dentry,dsize)<0) {
			printf("--cannot read %s\n",name);
			return(0);
		}
		close(dir);
		for(i = 0; i < (dsize>>4); ++i) { /* each dir. entry */
			if(dentry[i].dinode==0||dentry[i].dname[0]=='.')
				continue;
			if (dentry[i].dinode == -1) break;
			for(j=0;aname[j]=name[j];++j);
			if(aname[j-1]!='/') aname[j++] = '/';
			for(k=0; (aname[j++]=dentry[i].dname[k]) &&
				k<13; ++k);
			aname[j] = '\0';
			if(descend(aname,goal,func,arg)==0)
				printf("--%s\n",name);
		}
	}
	return(1);
}

gmatch(s, p) /* string match as in glob */
char *s, *p; {
	if (*s=='.' && *p!='.') return(0);
	return(amatch(s, p));
}

amatch(s, p)
char *s, *p;
{
	register int cc, scc, k;
	int c, lc;

	scc = *s;
	lc = 077777;
	switch (c = *p) {

	case '[':
		k = 0;
		while (cc = *++p) {
			switch (cc) {

			case ']':
				if (k)
					return(amatch(++s, ++p));
				else
					return(0);

			case '-':
				k =| lc <= scc & scc <= (cc=p[1]);
			}
			if (scc==(lc=cc)) k++;
		}
		return(0);

	case '?':
	caseq:
		if(scc) return(amatch(++s, ++p));
		return(0);
	case '*':
		return(umatch(s, ++p));
	case 0:
		return(!scc);
	}
	if (c==scc) goto caseq;
	return(0);
}

umatch(s, p)
char *s, *p;
{
	if(*p==0) return(1);
	while(*s)
		if (amatch(s++,p)) return(1);
	return(0);
}
