+/* find COMPILE: cc -o find -s -O -i find.c -lS */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <sys/stat.h>
+#define A_DAY 86400L /* a day full of seconds */
+#define EQ(x, y) (strcmp(x, y)==0)
+
+int Randlast;
+char Pathname[200];
+
+struct anode {
+ int (*F)();
+ struct anode *L, *R;
+} Node[100];
+int Nn; /* number of nodes */
+char *Fname;
+long Now;
+int Argc,
+ Ai,
+ Pi;
+char **Argv;
+/* cpio stuff */
+int Cpio;
+short *Buf, *Dbuf, *Wp;
+int Bufsize = 5120;
+int Wct = 2560;
+
+long Newer;
+
+struct stat Statb;
+
+struct anode *exp(),
+ *e1(),
+ *e2(),
+ *e3(),
+ *mk();
+char *nxtarg();
+char Home[128];
+long Blocks;
+char *rindex();
+char *sbrk();
+main(argc, argv) char *argv[];
+{
+ struct anode *exlist;
+ int paths;
+ register char *cp, *sp = 0;
+ FILE *pwd, *popen();
+
+ time(&Now);
+ pwd = popen("pwd", "r");
+ fgets(Home, 128, pwd);
+ pclose(pwd);
+ Home[strlen(Home) - 1] = '\0';
+ Argc = argc; Argv = argv;
+ if(argc<3) {
+usage: pr("Usage: find path-list predicate-list\n");
+ exit(1);
+ }
+ for(Ai = paths = 1; Ai < (argc-1); ++Ai, ++paths)
+ if(*Argv[Ai] == '-' || EQ(Argv[Ai], "(") || EQ(Argv[Ai], "!"))
+ break;
+ if(paths == 1) /* no path-list */
+ goto usage;
+ if(!(exlist = exp())) { /* parse and compile the arguments */
+ pr("find: parsing error\n");
+ exit(1);
+ }
+ if(Ai<argc) {
+ pr("find: missing conjunction\n");
+ exit(1);
+ }
+ for(Pi = 1; Pi < paths; ++Pi) {
+ sp = 0;
+ chdir(Home);
+ strcpy(Pathname, Argv[Pi]);
+ if(cp = rindex(Pathname, '/')) {
+ sp = cp + 1;
+ *cp = '\0';
+ if(chdir(*Pathname? Pathname: "/") == -1) {
+ pr("find: bad starting directory\n");
+ exit(2);
+ }
+ *cp = '/';
+ }
+ Fname = sp? sp: Pathname;
+ descend(Pathname, Fname, exlist); /* to find files that match */
+ }
+ if(Cpio) {
+ strcpy(Pathname, "TRAILER!!!");
+ Statb.st_size = 0;
+ cpio();
+ }
+ exit(0);
+}
+
+/* compile time functions: priority is exp()<e1()<e2()<e3() */
+
+struct anode *exp() { /* parse ALTERNATION (-o) */
+ int or();
+ register struct anode * p1;
+
+ p1 = e1() /* get left operand */ ;
+ if(EQ(nxtarg(), "-o")) {
+ Randlast--;
+ return(mk(or, p1, exp()));
+ }
+ else if(Ai <= Argc) --Ai;
+ return(p1);
+}
+struct anode *e1() { /* parse CONCATENATION (formerly -a) */
+ int and();
+ register struct anode * p1;
+ register char *a;
+
+ p1 = e2();
+ a = nxtarg();
+ if(EQ(a, "-a")) {
+And:
+ Randlast--;
+ return(mk(and, p1, e1()));
+ } else if(EQ(a, "(") || EQ(a, "!") || (*a=='-' && !EQ(a, "-o"))) {
+ --Ai;
+ goto And;
+ } else if(Ai <= Argc) --Ai;
+ return(p1);
+}
+struct anode *e2() { /* parse NOT (!) */
+ int not();
+
+ if(Randlast) {
+ pr("find: operand follows operand\n");
+ exit(1);
+ }
+ Randlast++;
+ if(EQ(nxtarg(), "!"))
+ return(mk(not, e3(), (struct anode *)0));
+ else if(Ai <= Argc) --Ai;
+ return(e3());
+}
+struct anode *e3() { /* parse parens and predicates */
+ int exeq(), ok(), glob(), mtime(), atime(), ctime(), user(),
+ group(), size(), perm(), links(), print(),
+ type(), ino(), cpio(), newer();
+ struct anode *p1;
+ int i;
+ register char *a, *b, s;
+
+ a = nxtarg();
+ if(EQ(a, "(")) {
+ Randlast--;
+ p1 = exp();
+ a = nxtarg();
+ if(!EQ(a, ")")) goto err;
+ return(p1);
+ }
+ else if(EQ(a, "-print")) {
+ return(mk(print, (struct anode *)0, (struct anode *)0));
+ }
+ b = nxtarg();
+ s = *b;
+ if(s=='+') b++;
+ if(EQ(a, "-name"))
+ return(mk(glob, (struct anode *)b, (struct anode *)0));
+ else if(EQ(a, "-mtime"))
+ return(mk(mtime, (struct anode *)atoi(b), (struct anode *)s));
+ else if(EQ(a, "-atime"))
+ return(mk(atime, (struct anode *)atoi(b), (struct anode *)s));
+ else if(EQ(a, "-ctime"))
+ return(mk(ctime, (struct anode *)atoi(b), (struct anode *)s));
+ else if(EQ(a, "-user")) {
+ if((i=getunum("/etc/passwd", b)) == -1) {
+ if(gmatch(b, "[0-9][0-9][0-9]*")
+ || gmatch(b, "[0-9][0-9]")
+ || gmatch(b, "[0-9]"))
+ return mk(user, (struct anode *)atoi(b), (struct anode *)s);
+ pr("find: cannot find -user name\n");
+ exit(1);
+ }
+ return(mk(user, (struct anode *)i, (struct anode *)s));
+ }
+ else if(EQ(a, "-inum"))
+ return(mk(ino, (struct anode *)atoi(b), (struct anode *)s));
+ else if(EQ(a, "-group")) {
+ if((i=getunum("/etc/group", b)) == -1) {
+ if(gmatch(b, "[0-9][0-9][0-9]*")
+ || gmatch(b, "[0-9][0-9]")
+ || gmatch(b, "[0-9]"))
+ return mk(group, (struct anode *)atoi(b), (struct anode *)s);
+ pr("find: cannot find -group name\n");
+ exit(1);
+ }
+ return(mk(group, (struct anode *)i, (struct anode *)s));
+ } else if(EQ(a, "-size"))
+ return(mk(size, (struct anode *)atoi(b), (struct anode *)s));
+ else if(EQ(a, "-links"))
+ return(mk(links, (struct anode *)atoi(b), (struct anode *)s));
+ else if(EQ(a, "-perm")) {
+ for(i=0; *b ; ++b) {
+ if(*b=='-') continue;
+ i <<= 3;
+ i = i + (*b - '0');
+ }
+ return(mk(perm, (struct anode *)i, (struct anode *)s));
+ }
+ else if(EQ(a, "-type")) {
+ i = s=='d' ? S_IFDIR :
+ s=='b' ? S_IFBLK :
+ s=='c' ? S_IFCHR :
+ s=='f' ? 0100000 :
+ 0;
+ return(mk(type, (struct anode *)i, (struct anode *)0));
+ }
+ else if (EQ(a, "-exec")) {
+ i = Ai - 1;
+ while(!EQ(nxtarg(), ";"));
+ return(mk(exeq, (struct anode *)i, (struct anode *)0));
+ }
+ else if (EQ(a, "-ok")) {
+ i = Ai - 1;
+ while(!EQ(nxtarg(), ";"));
+ return(mk(ok, (struct anode *)i, (struct anode *)0));
+ }
+ else if(EQ(a, "-cpio")) {
+ if((Cpio = creat(b, 0666)) < 0) {
+ pr("find: cannot create "), pr(s), pr("\n");
+ exit(1);
+ }
+ Buf = (short *)sbrk(512);
+ Wp = Dbuf = (short *)sbrk(5120);
+ return(mk(cpio, (struct anode *)0, (struct anode *)0));
+ }
+ else if(EQ(a, "-newer")) {
+ if(stat(b, &Statb) < 0) {
+ pr("find: cannot access "), pr(b), pr("\n");
+ exit(1);
+ }
+ Newer = Statb.st_mtime;
+ return mk(newer, (struct anode *)0, (struct anode *)0);
+ }
+err: pr("find: bad option "), pr(a), pr("\n");
+ exit(1);
+}
+struct anode *mk(f, l, r)
+int (*f)();
+struct anode *l, *r;
+{
+ Node[Nn].F = f;
+ Node[Nn].L = l;
+ Node[Nn].R = r;
+ return(&(Node[Nn++]));
+}
+
+char *nxtarg() { /* get next arg from command line */
+ static strikes = 0;
+
+ if(strikes==3) {
+ pr("find: incomplete statement\n");
+ exit(1);
+ }
+ if(Ai>=Argc) {
+ strikes++;
+ Ai = Argc + 1;
+ return("");
+ }
+ return(Argv[Ai++]);
+}
+
+/* execution time functions */
+and(p)
+register struct anode *p;
+{
+ return(((*p->L->F)(p->L)) && ((*p->R->F)(p->R))?1:0);
+}
+or(p)
+register struct anode *p;
+{
+ return(((*p->L->F)(p->L)) || ((*p->R->F)(p->R))?1:0);
+}
+not(p)
+register struct anode *p;
+{
+ return( !((*p->L->F)(p->L)));
+}
+glob(p)
+register struct { int f; char *pat; } *p;
+{
+ return(gmatch(Fname, p->pat));
+}
+print()
+{
+ puts(Pathname);
+ return(1);
+}
+mtime(p)
+register struct { int f, t, s; } *p;
+{
+ return(scomp((int)((Now - Statb.st_mtime) / A_DAY), p->t, p->s));
+}
+atime(p)
+register struct { int f, t, s; } *p;
+{
+ return(scomp((int)((Now - Statb.st_atime) / A_DAY), p->t, p->s));
+}
+ctime(p)
+register struct { int f, t, s; } *p;
+{
+ return(scomp((int)((Now - Statb.st_ctime) / A_DAY), p->t, p->s));
+}
+user(p)
+register struct { int f, u, s; } *p;
+{
+ return(scomp(Statb.st_uid, p->u, p->s));
+}
+ino(p)
+register struct { int f, u, s; } *p;
+{
+ return(scomp((int)Statb.st_ino, p->u, p->s));
+}
+group(p)
+register struct { int f, u; } *p;
+{
+ return(p->u == Statb.st_gid);
+}
+links(p)
+register struct { int f, link, s; } *p;
+{
+ return(scomp(Statb.st_nlink, p->link, p->s));
+}
+size(p)
+register struct { int f, sz, s; } *p;
+{
+ return(scomp((int)((Statb.st_size+511)>>9), p->sz, p->s));
+}
+perm(p)
+register struct { int f, per, s; } *p;
+{
+ register i;
+ i = (p->s=='-') ? p->per : 07777; /* '-' means only arg bits */
+ return((Statb.st_mode & i & 07777) == p->per);
+}
+type(p)
+register struct { int f, per, s; } *p;
+{
+ return((Statb.st_mode&S_IFMT)==p->per);
+}
+exeq(p)
+register struct { int f, com; } *p;
+{
+ fflush(stdout); /* to flush possible `-print' */
+ return(doex(p->com));
+}
+ok(p)
+struct { int f, com; } *p;
+{
+ int c; int yes;
+ yes = 0;
+ fflush(stdout); /* to flush possible `-print' */
+ pr("< "), pr(Argv[p->com]), pr(" ... "), pr(Pathname), pr(" >? ");
+ fflush(stderr);
+ if((c=getchar())=='y') yes = 1;
+ while(c!='\n')
+ if(c==EOF)
+ exit(2);
+ else
+ c = getchar();
+ if(yes) return(doex(p->com));
+ return(0);
+}
+
+#define MKSHORT(v, lv) {U.l=1L;if(U.c[0]) U.l=lv, v[0]=U.s[1], v[1]=U.s[0]; else U.l=lv, v[0]=U.s[0], v[1]=U.s[1];}
+union { long l; short s[2]; char c[4]; } U;
+long mklong(v)
+short v[];
+{
+ U.l = 1;
+ if(U.c[0] /* VAX */)
+ U.s[0] = v[1], U.s[1] = v[0];
+ else
+ U.s[0] = v[0], U.s[1] = v[1];
+ return U.l;
+}
+cpio()
+{
+#define MAGIC 070707
+ struct header {
+ short h_magic,
+ h_dev,
+ h_ino,
+ h_mode,
+ h_uid,
+ h_gid,
+ h_nlink,
+ h_rdev;
+ short h_mtime[2];
+ short h_namesize;
+ short h_filesize[2];
+ char h_name[256];
+ } hdr;
+ register ifile, ct;
+ static long fsz;
+ register i;
+
+ hdr.h_magic = MAGIC;
+ strcpy(hdr.h_name, !strncmp(Pathname, "./", 2)? Pathname+2: Pathname);
+ hdr.h_namesize = strlen(hdr.h_name) + 1;
+ hdr.h_uid = Statb.st_uid;
+ hdr.h_gid = Statb.st_gid;
+ hdr.h_dev = Statb.st_dev;
+ hdr.h_ino = Statb.st_ino;
+ hdr.h_mode = Statb.st_mode;
+ MKSHORT(hdr.h_mtime, Statb.st_mtime);
+ hdr.h_nlink = Statb.st_nlink;
+ fsz = hdr.h_mode & S_IFREG? Statb.st_size: 0L;
+ MKSHORT(hdr.h_filesize, fsz);
+ hdr.h_rdev = Statb.st_rdev;
+ if(EQ(hdr.h_name, "TRAILER!!!")) {
+ bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize);
+ for(i = 0; i < 10; ++i)
+ bwrite(Buf, 512);
+ return;
+ }
+ if(!mklong(hdr.h_filesize)) {
+ bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize);
+ return;
+ }
+ if((ifile = open(Fname, 0)) < 0) {
+cerror:
+ pr("find: cannot copy "), pr(hdr.h_name), pr("\n");
+ return;
+ }
+ bwrite((short *)&hdr, (sizeof hdr-256)+hdr.h_namesize);
+ for(fsz = mklong(hdr.h_filesize); fsz > 0; fsz -= 512) {
+ ct = fsz>512? 512: fsz;
+ if(read(ifile, (char *)Buf, ct) < 0)
+ goto cerror;
+ bwrite(Buf, ct);
+ }
+ close(ifile);
+ return;
+}
+newer()
+{
+ return Statb.st_mtime > Newer;
+}
+
+/* support functions */
+scomp(a, b, s) /* funny signed compare */
+register a, b;
+register char s;
+{
+ if(s == '+')
+ return(a > b);
+ if(s == '-')
+ return(a < (b * -1));
+ return(a == b);
+}
+
+doex(com)
+{
+ register np;
+ register char *na;
+ static char *nargv[50];
+ static ccode;
+
+ ccode = np = 0;
+ while (na=Argv[com++]) {
+ if(strcmp(na, ";")==0) break;
+ if(strcmp(na, "{}")==0) nargv[np++] = Pathname;
+ else nargv[np++] = na;
+ }
+ nargv[np] = 0;
+ if (np==0) return(9);
+ if(fork()) /*parent*/ wait(&ccode);
+ else { /*child*/
+ chdir(Home);
+ execvp(nargv[0], nargv, np);
+ exit(1);
+ }
+ return(ccode ? 0:1);
+}
+
+getunum(f, s) char *f, *s; { /* find user/group name and return number */
+ register i;
+ register char *sp;
+ register c;
+ char str[20];
+ FILE *pin;
+
+ i = -1;
+ pin = fopen(f, "r");
+ c = '\n'; /* prime with a CR */
+ do {
+ if(c=='\n') {
+ sp = str;
+ while((c = *sp++ = getc(pin)) != ':')
+ if(c == EOF) goto RET;
+ *--sp = '\0';
+ if(EQ(str, s)) {
+ while((c=getc(pin)) != ':')
+ if(c == EOF) goto RET;
+ sp = str;
+ while((*sp = getc(pin)) != ':') sp++;
+ *sp = '\0';
+ i = atoi(str);
+ goto RET;
+ }
+ }
+ } while((c = getc(pin)) != EOF);
+ RET:
+ fclose(pin);
+ return(i);
+}
+
+descend(name, fname, exlist)
+struct anode *exlist;
+char *name, *fname;
+{
+ int dir = 0, /* open directory */
+ offset,
+ dsize,
+ entries,
+ dirsize;
+ struct direct dentry[32];
+ register struct direct *dp;
+ register char *c1, *c2;
+ int i;
+ int rv = 0;
+ char *endofname;
+
+ if(stat(fname, &Statb)<0) {
+ pr("find: bad status-- "), pr(name), pr("\n");
+ return(0);
+ }
+ (*exlist->F)(exlist);
+ if((Statb.st_mode&S_IFMT)!=S_IFDIR)
+ return(1);
+
+ for(c1 = name; *c1; ++c1);
+ if(*(c1-1) == '/')
+ --c1;
+ endofname = c1;
+ dirsize = Statb.st_size;
+
+ if(chdir(fname) == -1)
+ return(0);
+ for(offset=0 ; offset < dirsize ; offset += 512) { /* each block */
+ dsize = 512<(dirsize-offset)? 512: (dirsize-offset);
+ if(!dir) {
+ if((dir=open(".", 0))<0) {
+ pr("find: cannot open "), pr(name), pr("\n");
+ rv = 0;
+ goto ret;
+ }
+ if(offset) lseek(dir, (long)offset, 0);
+ if(read(dir, (char *)dentry, dsize)<0) {
+ pr("find: cannot read "), pr(name), pr("\n");
+ rv = 0;
+ goto ret;
+ }
+ if(dir > 10) {
+ close(dir);
+ dir = 0;
+ }
+ } else
+ if(read(dir, (char *)dentry, dsize)<0) {
+ pr("find: cannot read "), pr(name), pr("\n");
+ rv = 0;
+ goto ret;
+ }
+ for(dp=dentry, entries=dsize>>4; entries; --entries, ++dp) { /* each directory entry */
+ if(dp->d_ino==0
+ || (dp->d_name[0]=='.' && dp->d_name[1]=='\0')
+ || (dp->d_name[0]=='.' && dp->d_name[1]=='.' && dp->d_name[2]=='\0'))
+ continue;
+ c1 = endofname;
+ *c1++ = '/';
+ c2 = dp->d_name;
+ for(i=0; i<14; ++i)
+ if(*c2)
+ *c1++ = *c2++;
+ else
+ break;
+ *c1 = '\0';
+ if(c1 == endofname) { /* ?? */
+ rv = 0;
+ goto ret;
+ }
+ Fname = endofname+1;
+ if(!descend(name, Fname, exlist)) {
+ *endofname = '\0';
+ chdir(Home);
+ if(chdir(Pathname) == -1) {
+ pr("find: bad directory tree\n");
+ exit(1);
+ }
+ }
+ }
+ }
+ rv = 1;
+ret:
+ if(dir)
+ close(dir);
+ if(chdir("..") == -1) {
+ *endofname = '\0';
+ pr("find: bad directory "), pr(name), pr("\n");
+ rv = 1;
+ }
+ return(rv);
+}
+
+gmatch(s, p) /* string match as in glob */
+register char *s, *p;
+{
+ if (*s=='.' && *p!='.') return(0);
+ return amatch(s, p);
+}
+
+amatch(s, p)
+register char *s, *p;
+{
+ register cc;
+ int 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)
+register char *s, *p;
+{
+ if(*p==0) return(1);
+ while(*s)
+ if (amatch(s++, p)) return(1);
+ return(0);
+}
+
+bwrite(rp, c)
+register short *rp;
+register c;
+{
+ register short *wp = Wp;
+
+ c = (c+1) >> 1;
+ while(c--) {
+ if(!Wct) {
+again:
+ if(write(Cpio, (char *)Dbuf, Bufsize)<0) {
+ Cpio = chgreel(1, Cpio);
+ goto again;
+ }
+ Wct = Bufsize >> 1;
+ wp = Dbuf;
+ ++Blocks;
+ }
+ *wp++ = *rp++;
+ --Wct;
+ }
+ Wp = wp;
+}
+chgreel(x, fl)
+{
+ register f;
+ char str[22];
+ FILE *devtty;
+ struct stat statb;
+
+ pr("find: can't "), pr(x? "write output": "read input"), pr("\n");
+ fstat(fl, &statb);
+ if((statb.st_mode&S_IFMT) != S_IFCHR)
+ exit(1);
+again:
+ pr("If you want to go on, type device/file name when ready\n");
+ devtty = fopen("/dev/tty", "r");
+ fgets(str, 20, devtty);
+ str[strlen(str) - 1] = '\0';
+ if(!*str)
+ exit(1);
+ close(fl);
+ if((f = open(str, x? 1: 0)) < 0) {
+ pr("That didn't work");
+ fclose(devtty);
+ goto again;
+ }
+ return f;
+}
+pr(s)
+char *s;
+{
+ fputs(s, stderr);
+}