/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 *
 */

/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"$Header: find.c 1.18 90/04/02 $@(#)find:find.c	4.22"

/*	find	COMPILE:	cc -o find -s -O -i find.c -lS	*/

#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <sys/types.h>
#include <sys/param.h>
#include <dirent.h>
#include <sys/stat.h>
#include <ctype.h>
#include <mntent.h>

/* for new dev_t */
#define	devtoshort(a)	((short)(((a)>>8)&0xff00)|((a)&0xff))
#define	shorttodev(a)	((dev_t)(((a)<<8)&0xff0000)|((a)&0xff))

#define	UID	1
#define	GID	2
#define A_DAY	86400L /* a day full of seconds */
#define EQ(x, y)	(strcmp(x, y)==0)
#define MK_USHORT(a)	(a & 00000177777)	/* Make unsigned shorts for        */
						/* portable header.  Hardware may  */
						/* only know integer operations    */
						/* and sign extend the large       */
						/* unsigned short resulting in 8   */
						/* rather than 6 octal char in the */
						/* header.			   */
#define BUFSIZE	512	/* In u370 I can't use BUFSIZ nor BSIZE */
#define CPIOBSZ	4096
#define Bufsize	5120

int	Randlast;
char	Pathname[MAXPATHLEN];

struct anode {
	int (*F)();
	struct anode *L, *R;
} Node[100];
int Nn;  /* number of nodes */
char	*Fname;
char	Needfs;		/* don't compute Fsname unless this is true */
char	*Fsname;
dev_t	Fsnum;
long	Now;
int	Argc,
	Ai,
	Pi;
char	**Argv;
/* cpio stuff */
int	Cpio;
short	*SBuf, *Dbuf, *Wp;
char	*Buf, *Cbuf, *Cp;
char	Strhdr[500],
	*Chdr = Strhdr;
int	Wct = Bufsize / 2, Cct = Bufsize;
int	Cflag;
int	depthf = 0;

long	Newer;
int	giveup = 0;	/* abort search in this directory */

struct stat Statb;

struct	anode	*exp(),
		*e1(),
		*e2(),
		*e3(),
		*mk();
char	*nxtarg();
char	Home[128];

 	/* Distributed UNIX options:  mount and local				   */
 	/*									   */
 	/* mount--When mount is specified the search is restricted to the file	   */
 	/* system containing the directory specified or, in the case no 	   */
 	/* directory is specified, the current directory.  This is intended	   */
 	/* for use by the administrator when backing up file systems.		   */
 	/*								 	   */
 	/* local--returns true if the file physically resides on the local machine.*/
 	/*									   */
 	/* When find is not used with distributed UNIX 				   */
 	/*									   */
 	/* mount--works no different.						   */
 	/*									   */
 	/* local--always return true.						   */

char	local;
char	mount_flag;
int	cur_dev;
long	Blocks;
void exit();
char *strrchr();
char *strcpy();
char *sbrk();
char *getmntpt();

	/* Cpio header format */
	struct header {
		short	h_magic,
			h_dev;
		ushort	h_ino,
			h_mode,
			h_uid,
			h_gid;
		short	h_nlink,
			h_rdev,
			h_mtime[2],
			h_namesize,
			h_filesize[2];
		char	h_name[256];
	} hdr;

extern int errno;
extern char *sys_errlist[];

main(argc, argv) char *argv[];
{
	struct anode *exlist;
	int paths;
	register char *cp, *sp = 0;
	FILE *pwd, *popen();
	time_t time();
	char	*msgid[16];

	if (time(&Now) == (time_t) -1) {
		sprintf(msgid,"syserr:%d",errno+1);
		(void) fprintf(stderr, gettxt("find:33", "find: time() %s\n"),
			gettxt(msgid,sys_errlist[errno]));
		exit(2);
	}
	pwd = popen("pwd", "r");
	if (pwd == NULL) {
		(void) fprintf(stderr,
	    gettxt("find:1", "find: popen() cannot open pipe to `pwd'\n"));
		exit(2);
	}
	fgets(Home, 128, pwd);
	if (Home == NULL) {
		(void) fprintf(stderr,
	    gettxt("find:2", "find: fgets() cannot read `pwd' output\n"));
		exit(2);
	}
	if(pclose(pwd) & 0377) {
		(void) fprintf(stderr,
			gettxt("find:3", "find: cannot execute `pwd'\n"));
		exit(2);
	}
	Home[strlen(Home) - 1] = '\0';
	Argc = argc; Argv = argv;
	if(argc<3) {
		(void) fprintf(stderr,
	    gettxt("find:4", "find: insufficient number of arguments\n"));
usage:		(void) fprintf(stderr,
	    gettxt("find:5", "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 */
		(void) fprintf(stderr,
			gettxt("find:6", "find: parsing error\n"));
		exit(1);
	}
	if(Ai<argc) {
		(void) fprintf(stderr,
			gettxt("find:7", "find: missing conjunction\n"));
		exit(1);
	}
	for(Pi = 1; Pi < paths; ++Pi) {
		sp = "\0";
		strcpy(Pathname, Argv[Pi]);
		if(*Pathname != '/')
			chdir(Home);
		if(cp = strrchr(Pathname, '/')) {
			sp = cp + 1;
			if (*sp != '\0') {
				*cp = '\0';
				if(chdir(*Pathname? Pathname: "/") == -1) {
					(void) fprintf(stderr,
			    gettxt("find:8", "find: bad starting directory\n"));
					exit(2);
				}
				*cp = '/';
			}
		}
		Fname = *sp? sp: Pathname;
		if (Needfs)
			Fsname = getmntpt(Fname);
		if(mount_flag)
			if (stat(Fname, &Statb) <0) {
				fprintf(stderr,
			gettxt("find:9", "find: cannot stat %s\n"), Pathname);
				exit(2);
			} else
				cur_dev = Statb.st_dev;
		/* to find files that match  */
		descend(Pathname, Fname, Fsname, 0, exlist); 
	}
	if(Cpio) {
		strcpy(Pathname, "TRAILER!!!");
		Statb.st_size = 0;
		cpio();
	}
	exit(0);	/*NOTREACHED*/
}

/* 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) {
		(void) fprintf(stderr,
			gettxt("find:10", "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(), csize(), perm(), links(), print(),
		type(), fstype(), ino(), depth(), cpio(), newer(), prune(),
		localf(), mountf();
	struct anode *p1;
	struct anode *mkret;
	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));
	}
	else if(EQ(a, "-prune")) {
		return(mk(prune, (struct anode *)0, (struct anode *)0));
	}
	else if(EQ(a, "-depth")) {
		depthf = 1;
		return(mk(depth, (struct anode *)0, (struct anode *)0));
	}
	else if(EQ(a, "-local")) {
		local = 1;
		return(mk(localf, (struct anode *)0, (struct anode *)0));
	}
	else if(EQ(a, "-mount")) {
		mount_flag = 1;
		return(mk(mountf, (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(UID, 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);
			(void) fprintf(stderr,
		    gettxt("find:11", "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(GID, 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);
			(void) fprintf(stderr,
		    gettxt("find:12", "find: cannot find -group name\n"));
			exit(1);
		}
		return(mk(group, (struct anode *)i, (struct anode *)s));
	} else if(EQ(a, "-size")) {
		mkret = mk(size, (struct anode *)atoi(b), (struct anode *)s);
		if(*b == '+' || *b == '-')b++;
		while(isdigit(*b))b++;
		if(*b == 'c') Node[Nn-1].F = csize;
		return(mkret);
	} 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=='p' ? S_IFIFO :
		    s=='f' ? S_IFREG :
		    s=='l' ? S_IFLNK :
		    0;
		return(mk(type, (struct anode *)i, (struct anode *)0));
	}
	else if (EQ(a, "-fstype")) {
		Needfs = 1;
		return(mk(fstype, (struct anode *)b, (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) {
			(void) fprintf(stderr,
			    gettxt("find:13", "find: cannot create %s\n"), b);
			exit(1);
		}
		SBuf = (short *)sbrk(CPIOBSZ);
		Wp = Dbuf = (short *)sbrk(Bufsize);
		depthf = 1;
		return(mk(cpio, (struct anode *)0, (struct anode *)0));
	}
	else if(EQ(a, "-ncpio")) {
		if((Cpio = creat(b, 0666)) < 0) {
			(void) fprintf(stderr,
			    gettxt("find:13", "find: cannot create %s\n"), b);
			exit(1);
		}
		Buf = (char*)sbrk(CPIOBSZ);
		Cp = Cbuf = (char *)sbrk(Bufsize);
		Cflag++;
		depthf = 1;
		return(mk(cpio, (struct anode *)0, (struct anode *)0));
	}
	else if(EQ(a, "-newer")) {
		if(stat(b, &Statb) < 0) {
			(void) fprintf(stderr,
			    gettxt("find:14", "find: cannot access %s\n"), b);
			exit(1);
		}
		Newer = Statb.st_mtime;
		return mk(newer, (struct anode *)0, (struct anode *)0);
	}
err:	(void) fprintf(stderr,gettxt("find:15", "find: bad option %s\n"), a);
	(void) fprintf(stderr,
		gettxt("find:5", "Usage: find path-list predicate-list\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) {
		(void) fprintf(stderr,
			gettxt("find:16", "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);
}
localf()
{
	return (~Statb.st_dev < 0);
}
mountf()
{
}
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));
}
static int
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+(BUFSIZE - 1))/BUFSIZE), p->sz, p->s));
}
csize(p)
register struct { int f, sz, s; } *p; 
{
	return(scomp((int)Statb.st_size, 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);
}
fstype(p)
register struct { int f; char *typename; } *p;
{
	return(!strcmp(Fsname, p->typename));
}
prune(p)
register struct { int f, per, s; } *p;
{
	giveup = 1;
	return(1);
}
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, yes=0;

	fflush(stdout); /* to flush possible `-print' */
	(void) fprintf(stderr,"< %s ... %s >?   ", Argv[p->com], Pathname);
	fflush(stderr);
	if((c=getchar())=='y') yes = 1;
	while(c!='\n')
		if(c==EOF)
			exit(2);
		else
			c = getchar();
	return(yes? doex(p->com): 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;
}

depth()
{
	return(1);
}

#define	LINKS	500

static
long
GetLinkId()
{
	register long i;
	static struct ml {
		dev_t	m_dev;
		ino_t	m_ino;
	} **ml = 0;
	register struct ml	*mlp;
	static long mlsize = 0;
	static long mlinks = 0;


	if( !ml ) {
		mlsize = LINKS;
		ml = (struct ml **) malloc(mlsize * sizeof(struct ml *));
	}
	else if( mlinks == mlsize ) {
		mlsize += LINKS;
		ml = (struct ml **) realloc((char *) ml,
		    mlsize * sizeof(struct ml *));
	}
	if (ml == NULL) {
		fprintf(stderr, "find: Out of memory for links");
		exit(2);
	}
	for(i = 0; i < mlinks; i++) {
		mlp = ml[i];
		if(mlp->m_ino == Statb.st_ino && mlp->m_dev == Statb.st_dev)
			return i;
	}
	if( !(ml[mlinks] = (struct ml *)malloc(sizeof(struct ml)))) {
		fprintf(stderr, "find: Out of memory for links");
		exit(2);
	}
	ml[mlinks]->m_dev = Statb.st_dev;
	ml[mlinks]->m_ino = Statb.st_ino;
	return (mlinks++);
}

cpio()
{
#define MAGIC 070707
#define HDRSIZE	(hdr.h_name - (char *)&hdr)	/* header size minus filename field */
#define CHARS	76		/* ASCII header size minus filename field */
	register ifile, ct;
	static long fsz;
	register i;
	long tlong;
	int fstype;

	strcpy(hdr.h_name, !strncmp(Pathname, "./", 2)? Pathname+2: Pathname);
	hdr.h_magic = MAGIC;
	hdr.h_namesize = strlen(hdr.h_name) + 1;
	hdr.h_uid = Statb.st_uid;
	hdr.h_gid = Statb.st_gid;
	fstype = Statb.st_mode & S_IFMT;
	if (fstype != S_IFDIR && Statb.st_nlink > 1) {
		tlong = GetLinkId();
		hdr.h_dev = (tlong & 0xffff);
		hdr.h_ino = ((tlong >> 16) & 0xffff);
	} else {
		hdr.h_dev = devtoshort(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;
	if (fstype == S_IFREG || fstype == S_IFLNK)
		fsz = Statb.st_size;
	else
		fsz = 0L;
	MKSHORT(hdr.h_filesize, fsz);
	if ((fstype == S_IFBLK || fstype == S_IFCHR || fstype == S_IFIFO) 
	 && (Statb.st_rdev & 0xff00ff00)) {
		fprintf(stderr, "find: < %s > : long dev_t. ignoring.\n",
			hdr.h_name);
		return;
	}
	hdr.h_rdev = devtoshort(Statb.st_rdev);
	if (Cflag)
		bintochar(fsz);

	if(EQ(hdr.h_name, "TRAILER!!!")) {
		Cflag? writehdr(Chdr, CHARS + hdr.h_namesize):
			bwrite((short *)&hdr, HDRSIZE + hdr.h_namesize);
		for (i = 0; i < 10; ++i)
			Cflag? writehdr(Buf, BUFSIZE): bwrite(SBuf, BUFSIZE);
		return;
	}
	if(!mklong(hdr.h_filesize)) {
		Cflag? writehdr(Chdr, CHARS + hdr.h_namesize):
			bwrite((short *)&hdr, HDRSIZE + hdr.h_namesize);
		return;
	}

	/* Process symbolic links */

	if ((hdr.h_mode & S_IFMT) == S_IFLNK) {
		if (mklong(hdr.h_filesize) > CPIOBSZ) {
			fprintf(stderr,
			    gettxt("find:36", "find: symlink path too long\n"));
			return;
		}
		if (Cflag) {
			ct = readlink(Fname, Buf, CPIOBSZ);
			if (ct == -1) {
				fprintf(stderr,
		gettxt("find:31", "find: error, readlink '%s' failed\n"), Fname);
				return;
			}
			writehdr(Chdr, CHARS + hdr.h_namesize);
			writehdr(Buf, ct);
		} else {
			ct = readlink(Fname, SBuf, CPIOBSZ);
			if (ct == -1) {
				fprintf(stderr,
		gettxt("find:31", "find: error, readlink '%s' failed\n"), Fname);
				return;
			}
			bwrite((short *)&hdr, HDRSIZE+hdr.h_namesize);
			bwrite(SBuf, ct);
		}
		return 1;
	}

	if((ifile = open(Fname, 0)) < 0) {
/* cerror: */
		(void) fprintf(stderr,
			gettxt("find:17", "find: cannot copy %s\n"),
			hdr.h_name);
		return;
	}
	Cflag? writehdr(Chdr, CHARS + hdr.h_namesize):
		bwrite((short *)&hdr, HDRSIZE+hdr.h_namesize);
	for(fsz = mklong(hdr.h_filesize); fsz > 0; fsz -= CPIOBSZ) {
		ct = fsz>CPIOBSZ? CPIOBSZ: fsz;
		if(read(ifile, Cflag? Buf: (char *)SBuf, ct) < 0)  {
			(void) fprintf(stderr,
				gettxt("find:34", "find: cannot read %s\n"),
				hdr.h_name);
			continue;
		}
		Cflag? writehdr(Buf, ct): bwrite(SBuf, ct);
	}
	close(ifile);
	return 1;
}

bintochar(t)		/* ASCII header write */
long t;
{
	sprintf(Chdr,"%.6ho%.6ho%.6ho%.6ho%.6ho%.6ho%.6ho%.6ho%.11lo%.6ho%.11lo%s",
		MAGIC, MK_USHORT(hdr.h_dev), MK_USHORT(hdr.h_ino),
		Statb.st_mode, Statb.st_uid, Statb.st_gid,
		Statb.st_nlink, MK_USHORT(hdr.h_rdev),
		Statb.st_mtime, (short)strlen(hdr.h_name)+1, t, hdr.h_name);
}

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));
	return(a == b);
}

doex(com)
{
	register np;
	register char *na;
	static char *nargv[50];
	static ccode;
	static pid;

	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(pid = vfork())
		while(wait(&ccode) != pid);
	else { /*child*/
		chdir(Home);
		execvp(nargv[0], nargv, np);
		exit(1);
	}
	return(ccode ? 0:1);
}

getunum(t, s)
int	t;
char	*s;
{
	register i;
	struct	passwd	*getpwnam(), *pw;
	struct	group	*getgrnam(), *gr;

	i = -1;
	if( t == UID ){
		if( ((pw = getpwnam( s )) != (struct passwd *)NULL) && pw != (struct passwd *)EOF )
			i = pw->pw_uid;
	} else {
		if( ((gr = getgrnam( s )) != (struct group *)NULL) && gr != (struct group *)EOF )
			i = gr->gr_gid;
	}
	return(i);
}

descend(name, fname, pfsname, pfsnum, exlist)
struct anode *exlist;
dev_t pfsnum;		/* parent's dev */
char *name, *fname;
char *pfsname;		/* parent's fsname */
{
	int cdval = 0;
	DIR *dir = NULL;
	register struct dirent	*dp;
	char *endofname;
	register char *c1, *c2;
	long loc = 0L;			/* record directory position */
	static DIR **lastdir = NULL;	/* record previous directory */
	static long *lastloc;		/* record previous location */
	int nds1,nds2;			/* number file descriptors */
	dev_t fsnum;			/* current dev */
	char *fsname;			/* current fsname */
	char	msgid[16];

	/* Defense against running out of file descriptors is twofold:
	 * Try and increase the number of file descriptors.  When that
	 * no longer works, begin closing the last file descriptor used.
	 * This is done by maintaining on the stack the present "DIR" file,
	 * and if this is NULL, then the last location in the directory.
	 * We know that the directory name is ".", so after a call to
	 * descend we check to see if this closed the directory we were
	 * using.  If it did, then we have to reopen "." and seek to
	 * the last location in the directory.  This is updated when ever
	 * the lower routine had to close a directory.
	 *
	 * Potential problem:  If you close a directory it may be modified
	 * and the reopen/seek is not guaranteed to provide a correct read
	 * of the directory.  Best effort is provided by this routine.
	 */

	if(lstat(fname, &Statb)<0) {
		sprintf(msgid,"syserr:%d",errno+1);
		(void) fprintf(stderr,
		    gettxt("find:19", "find: stat() failed: %s: %s\n"),
		    name, gettxt(msgid,sys_errlist[errno]));
		return(0);
	}
	fsnum = Statb.st_dev;
	if (Needfs) {
		if (fsnum != pfsnum)
			Fsname = getmntpt(Fname);
		else
			Fsname = pfsname;
		fsname = Fsname;	/* squirrel this away on stack */
	}
	if(mount_flag && (Statb.st_dev != cur_dev))
		return(0);
	if(!depthf)
		(*exlist->F)(exlist);
	if (giveup) {
		giveup = 0;
		return(1);
	}
	if((Statb.st_mode&S_IFMT)!=S_IFDIR) {
		if(depthf)
			(*exlist->F)(exlist);
		return(1);
	}

	for(c1 = name; *c1; ++c1);
	/* too conservative ?? */
	if((int)(c1-name) >= (MAXPATHLEN - MAXNAMLEN)) {
		(void) fprintf(stderr,
			gettxt("find:20", "find: pathname too long"));
		exit(2);
	}
	if(*(c1-1) == '/')
		--c1;
	endofname = c1;

	if((cdval=chdir(fname)) == -1) {
		(void) fprintf(stderr,
			gettxt("find:22", "find: cannot chdir to %s\n"), name);
	} else {
		dir = opendir(".");
		if (dir == NULL) {		/* failed */
		    if (access(".", 04) != 0) {
			(void) fprintf(stderr,
				gettxt("find:34", "find: cannot read %s\n"),
				name);
			goto ret;
		    }
		    nds1 = getdtablesize();	/* increase file table size */
		    nds1 += 20;
		    nds2 = setdtablesize(nds1);
		    if ( (nds2 != 0) && (nds1 != nds2) )
			dir = opendir(".");	/* try again */
		    if (dir == NULL) {		/* failed again */
		        if (*lastdir != NULL) {	/* close previous dir and retry */
			    *lastloc = telldir(*lastdir);
			    closedir(*lastdir);
			    *lastdir = NULL;
			    if ((dir = opendir(".")) == NULL) {
				(void) fprintf(stderr,
				    gettxt("find:34", "find: cannot read %s\n"),
				    name);
			        goto ret;
			    }
			} else {
			    (void) fprintf(stderr,
				    gettxt("find:34", "find: cannot read %s\n"),
				    name);
			    goto ret;
			}
		    }
		}
		lastdir = &dir;		/* record addr on stack for next level */
		lastloc = &loc;
		for (dp = readdir(dir); dp != NULL; dp = readdir(dir)) {
			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;
			while (*c2)
				*c1++ = *c2++;
			*c1 = '\0';
			if(c1 == (endofname + 1)) { /* ?? */
				break;
			}
			Fname = endofname+1;
			descend(name, Fname, fsname, fsnum, exlist);
			if (dir == NULL) {	/* must have been closed by descend */
				if ((dir = opendir(".")) == NULL) {
					(void) fprintf(stderr,
			    gettxt("find:32", "find: cannot reopen \".\"\n"));
					goto ret;
				}
				seekdir(dir, loc);
			}
		}
	}
ret:
	if(dir)
		closedir(dir);
	c1 = endofname;
	*c1 = '\0';
	if(cdval == -1 || chdir("..") == -1) {
		if((endofname=strrchr(Pathname,'/')) == Pathname)
			chdir("/");
		else {
			if(endofname != NULL)
				*endofname = '\0';
			chdir(Home);
			if(chdir(Pathname) == -1) {
				(void) fprintf(stderr,
			    gettxt("find:24", "find: bad directory tree\n"));
				exit(1);
			}
			if(endofname != NULL)
				*endofname = '/';
		}
	}
	if(depthf) {
		if(lstat(fname, &Statb) < 0) {	/* XXX */
			sprintf(msgid,"syserr:%d",errno+1);
			(void) fprintf(stderr,
			    gettxt("find:35", "find: lstat failed: %s: %s\n"),
			    fname, gettxt(msgid,sys_errlist[errno]));
		}
		(*exlist->F)(exlist);
	}
/*	*c1 = '/';	*/
	return;
}

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;
}

writehdr(rp, c)
register char *rp;
register c;
{
	register char *cp = Cp;

	while (c--)  {
		if (!Cct)  {
again:
			if(write(Cpio, Cbuf, Bufsize) < 0)  {
				Cpio = chgreel(1, Cpio);
				goto again;
			}
			Cct = Bufsize;
			cp = Cbuf;
			++Blocks;
		}
		*cp++ = *rp++;
		--Cct;
	}
	Cp = cp;
}

chgreel(x, fl)
{
	register f;
	char str[22];
	FILE *devtty;
	FILE *fopen();
	struct stat statb;

	if(x)
		(void) fprintf(stderr,
			gettxt("find:27", "find: can't write output\n"));
	else
		(void) fprintf(stderr,
			gettxt("find:28", "find: can't read input\n"));
	fstat(fl, &statb);
	if((statb.st_mode&S_IFMT) != S_IFCHR)
		exit(1);
again:
	close(fl);
	(void) fprintf(stderr,
gettxt("find:29", "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);
	if((f = open(str, x? 1: 0)) < 0) {
		(void) fprintf(stderr,gettxt("find:30", "That didn't work"));
		fclose(devtty);
		goto again;
	}
	return f;
}

/* Given a name like /usr/src/etc/foo.c returns the mntent
 * structure for the file system it lives in.
 */

char *
getmntpt(file)
	char *file;
{
	FILE *mntp;
	struct mntent *mnt;
	struct stat filestat, dirstat;
	char *mnttype;

	if (stat(file, &filestat) < 0) {
		perror(file);
		return(NULL);
	}

	if ((mntp = setmntent(MNTTABNAME, "r")) == 0) {
		perror(MNTTABNAME);
		exit(1);
	}

	while ((mnt = getmntent(mntp)) != 0) {
		if (strcmp(mnt->mnt_type, MNTTYPE_IGNORE) == 0 ||
		    strcmp(mnt->mnt_type, MNTTYPE_SWAP) == 0)
			continue;
		if (strcmp(mnt->mnt_fsname, file) == 0) {
			endmntent(mntp);
			return(mnt->mnt_type);
		}
		if (stat(mnt->mnt_dir, &dirstat) < 0) {
			perror(mnt->mnt_dir);
			endmntent(mntp);
			return(NULL);
		}
		if (filestat.st_dev == dirstat.st_dev) {
			endmntent(mntp);
			mnttype = (char *)malloc(strlen(mnt->mnt_type+1));
			strcpy(mnttype, mnt->mnt_type);
			return(mnttype);
		}
	}
	fprintf(stderr, "Couldn't find mount point for %s\n", file);
	exit(1);
	/*NOTREACHED*/
}
