xref: /illumos-gate/usr/src/cmd/find/find.c (revision 05f32410ae979b9796bef0f9ca68747c59821a3f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
24  * Copyright (c) 2013 Andrew Stormont.  All rights reserved.
25  */
26 
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 
32 /*	Parts of this product may be derived from		*/
33 /*	Mortice Kern Systems Inc. and Berkeley 4.3 BSD systems.	*/
34 /*	licensed from  Mortice Kern Systems Inc. and 		*/
35 /*	the University of California.				*/
36 
37 /*
38  * Copyright 1985, 1990 by Mortice Kern Systems Inc.  All rights reserved.
39  */
40 
41 #include <stdio.h>
42 #include <errno.h>
43 #include <pwd.h>
44 #include <grp.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/param.h>
48 #include <sys/acl.h>
49 #include <limits.h>
50 #include <unistd.h>
51 #include <stdlib.h>
52 #include <locale.h>
53 #include <string.h>
54 #include <strings.h>
55 #include <ctype.h>
56 #include <wait.h>
57 #include <fnmatch.h>
58 #include <langinfo.h>
59 #include <ftw.h>
60 #include <libgen.h>
61 #include <err.h>
62 #include <regex.h>
63 #include "getresponse.h"
64 
65 #define	A_DAY		(long)(60*60*24)	/* a day full of seconds */
66 #define	A_MIN		(long)(60)
67 #define	BLKSIZ		512
68 #define	round(x, s)	(((x)+(s)-1)&~((s)-1))
69 #ifndef FTW_SLN
70 #define	FTW_SLN		7
71 #endif
72 #define	LINEBUF_SIZE		LINE_MAX	/* input or output lines */
73 #define	REMOTE_FS		"/etc/dfs/fstypes"
74 #define	N_FSTYPES		20
75 #define	SHELL_MAXARGS		253	/* see doexec() for description */
76 
77 /*
78  * This is the list of operations
79  * F_USER and F_GROUP are named to avoid conflict with USER and GROUP defined
80  * in sys/acl.h
81  */
82 
83 enum Command
84 {
85 	PRINT,
86 	ACL, AMIN, AND, ATIME, CMIN, CPIO, CSIZE, CTIME, DEPTH, EXEC, F_GROUP,
87 	F_GROUPACL, F_USER, F_USERACL, FOLLOW, FSTYPE, INAME, INUM, IPATH,
88 	IREGEX,	LINKS, LOCAL, LPAREN, LS, MAXDEPTH, MINDEPTH, MMIN, MOUNT,
89 	MTIME, NAME, NCPIO, NEWER, NOGRP, NOT, NOUSER, OK, OR, PATH, PERM,
90 	PRINT0, PRUNE, REGEX, RPAREN, SIZE, TYPE, VARARGS, XATTR
91 };
92 
93 enum Type
94 {
95 	Unary, Id, Num, Str, Exec, Cpio, Op
96 };
97 
98 struct Args
99 {
100 	char		name[10];
101 	enum Command	action;
102 	enum Type	type;
103 };
104 
105 /*
106  * Except for pathnames, these are the only legal arguments
107  */
108 static struct Args commands[] =
109 {
110 	"!",		NOT,		Op,
111 	"(",		LPAREN,		Unary,
112 	")",		RPAREN,		Unary,
113 	"-a",		AND,		Op,
114 	"-acl",		ACL,		Unary,
115 	"-amin",	AMIN,		Num,
116 	"-and",		AND,		Op,
117 	"-atime",	ATIME,		Num,
118 	"-cmin",	CMIN,		Num,
119 	"-cpio",	CPIO,		Cpio,
120 	"-ctime",	CTIME,		Num,
121 	"-depth",	DEPTH,		Unary,
122 	"-exec",	EXEC,		Exec,
123 	"-follow",	FOLLOW,		Unary,
124 	"-fstype",	FSTYPE,		Str,
125 	"-group",	F_GROUP,	Num,
126 	"-groupacl",	F_GROUPACL,	Num,
127 	"-iname",	INAME,		Str,
128 	"-inum",	INUM,		Num,
129 	"-ipath",	IPATH,		Str,
130 	"-iregex",	IREGEX,		Str,
131 	"-links",	LINKS,		Num,
132 	"-local",	LOCAL,		Unary,
133 	"-ls",		LS,		Unary,
134 	"-maxdepth",	MAXDEPTH,	Num,
135 	"-mindepth",	MINDEPTH,	Num,
136 	"-mmin",	MMIN,		Num,
137 	"-mount",	MOUNT,		Unary,
138 	"-mtime",	MTIME,		Num,
139 	"-name",	NAME,		Str,
140 	"-ncpio",	NCPIO,		Cpio,
141 	"-newer",	NEWER,		Str,
142 	"-nogroup",	NOGRP,		Unary,
143 	"-not",		NOT,		Op,
144 	"-nouser",	NOUSER,		Unary,
145 	"-o",		OR,		Op,
146 	"-ok",		OK,		Exec,
147 	"-or",		OR,		Op,
148 	"-path",	PATH,		Str,
149 	"-perm",	PERM,		Num,
150 	"-print",	PRINT,		Unary,
151 	"-print0",	PRINT0,		Unary,
152 	"-prune",	PRUNE,		Unary,
153 	"-regex",	REGEX,		Str,
154 	"-size",	SIZE,		Num,
155 	"-type",	TYPE,		Num,
156 	"-user",	F_USER,		Num,
157 	"-useracl",	F_USERACL,	Num,
158 	"-xattr",	XATTR,		Unary,
159 	"-xdev",	MOUNT,		Unary,
160 	NULL,		0,		0
161 };
162 
163 union Item
164 {
165 	struct Node	*np;
166 	struct Arglist	*vp;
167 	time_t		t;
168 	char		*cp;
169 	char		**ap;
170 	long		l;
171 	int		i;
172 	long long	ll;
173 };
174 
175 struct Node
176 {
177 	struct Node	*next;
178 	enum Command	action;
179 	enum Type	type;
180 	union Item	first;
181 	union Item	second;
182 };
183 
184 /* if no -print, -exec or -ok replace "expression" with "(expression) -print" */
185 static	struct	Node PRINT_NODE = { 0, PRINT, 0, 0};
186 static	struct	Node LPAREN_NODE = { 0, LPAREN, 0, 0};
187 
188 
189 /*
190  * Prototype variable size arglist buffer
191  */
192 
193 struct Arglist
194 {
195 	struct Arglist	*next;
196 	char		*end;
197 	char		*nextstr;
198 	char		**firstvar;
199 	char		**nextvar;
200 	char		*arglist[1];
201 };
202 
203 
204 static int		compile();
205 static int		execute();
206 static int		doexec(char *, char **, int *);
207 static struct Args	*lookup();
208 static int		ok();
209 static void		usage(void)	__NORETURN;
210 static struct Arglist	*varargs();
211 static int		list();
212 static char		*getgroup();
213 static FILE		*cmdopen();
214 static int		cmdclose();
215 static char		*getshell();
216 static void 		init_remote_fs();
217 static char		*getname();
218 static int		readmode();
219 static mode_t		getmode();
220 static char		*gettail();
221 
222 
223 static int walkflags = FTW_CHDIR|FTW_PHYS|FTW_ANYERR|FTW_NOLOOP;
224 static struct Node	*topnode;
225 static struct Node	*freenode;	/* next free node we may use later */
226 static char		*cpio[] = { "cpio", "-o", 0 };
227 static char		*ncpio[] = { "cpio", "-oc", 0 };
228 static char		*cpiol[] = { "cpio", "-oL", 0 };
229 static char		*ncpiol[] = { "cpio", "-ocL", 0 };
230 static time_t		now;
231 static FILE		*output;
232 static char		*dummyarg = (char *)-1;
233 static int		lastval;
234 static int		varsize;
235 static struct Arglist	*lastlist;
236 static char		*cmdname;
237 static char		*remote_fstypes[N_FSTYPES+1];
238 static int		fstype_index = 0;
239 static int		action_expression = 0;	/* -print, -exec, or -ok */
240 static int		error = 0;
241 static int		paren_cnt = 0;	/* keeps track of parentheses */
242 static int		Eflag = 0;
243 static int		hflag = 0;
244 static int		lflag = 0;
245 /* set when doexec()-invoked utility returns non-zero */
246 static int		exec_exitcode = 0;
247 static regex_t		*preg = NULL;
248 static int		npreg = 0;
249 static int		mindepth = -1, maxdepth = -1;
250 extern char		**environ;
251 
252 int
253 main(int argc, char **argv)
254 {
255 	char *cp;
256 	int c;
257 	int paths;
258 	char *cwdpath;
259 
260 	(void) setlocale(LC_ALL, "");
261 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
262 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
263 #endif
264 	(void) textdomain(TEXT_DOMAIN);
265 
266 	cmdname = argv[0];
267 	if (time(&now) == (time_t)(-1)) {
268 		(void) fprintf(stderr, gettext("%s: time() %s\n"),
269 		    cmdname, strerror(errno));
270 		exit(1);
271 	}
272 	while ((c = getopt(argc, argv, "EHL")) != -1) {
273 		switch (c) {
274 		case 'E':
275 			Eflag = 1;
276 			break;
277 		case 'H':
278 			hflag = 1;
279 			lflag = 0;
280 			break;
281 		case 'L':
282 			hflag = 0;
283 			lflag = 1;
284 			break;
285 		case '?':
286 			usage();
287 			break;
288 		}
289 	}
290 
291 	argc -= optind;
292 	argv += optind;
293 
294 	if (argc < 1) {
295 		(void) fprintf(stderr,
296 		    gettext("%s: insufficient number of arguments\n"), cmdname);
297 		usage();
298 	}
299 
300 	for (paths = 0; (cp = argv[paths]) != 0; ++paths) {
301 		if (*cp == '-')
302 			break;
303 		else if ((*cp == '!' || *cp == '(') && *(cp+1) == 0)
304 			break;
305 	}
306 
307 	if (paths == 0) /* no path-list */
308 		usage();
309 
310 	output = stdout;
311 
312 	/* lflag is the same as -follow */
313 	if (lflag)
314 		walkflags &= ~FTW_PHYS;
315 
316 	/* allocate enough space for the compiler */
317 	topnode = malloc((argc + 1) * sizeof (struct Node));
318 	(void) memset(topnode, 0, (argc + 1) * sizeof (struct Node));
319 
320 	if (compile(argv + paths, topnode, &action_expression) == 0) {
321 		/* no expression, default to -print */
322 		(void) memcpy(topnode, &PRINT_NODE, sizeof (struct Node));
323 	} else if (!action_expression) {
324 		/*
325 		 * if no action expression, insert an LPAREN node above topnode,
326 		 * with a PRINT node as its next node
327 		 */
328 		struct Node *savenode;
329 
330 		if (freenode == NULL) {
331 			(void) fprintf(stderr, gettext("%s: can't append -print"
332 			    " implicitly; try explicit -print option\n"),
333 			    cmdname);
334 			exit(1);
335 		}
336 		savenode = topnode;
337 		topnode = freenode++;
338 		(void) memcpy(topnode, &LPAREN_NODE, sizeof (struct Node));
339 		topnode->next = freenode;
340 		topnode->first.np = savenode;
341 		(void) memcpy(topnode->next, &PRINT_NODE, sizeof (struct Node));
342 	}
343 
344 	while (paths--) {
345 		char *curpath;
346 		struct stat sb;
347 
348 		curpath = *(argv++);
349 
350 		/*
351 		 * If -H is specified, it means we walk the first
352 		 * level (pathname on command line) logically, following
353 		 * symlinks, but lower levels are walked physically.
354 		 * We use our own secret interface to nftw() to change
355 		 * the from stat to lstat after the top level is walked.
356 		 */
357 		if (hflag) {
358 			if (stat(curpath, &sb) < 0 && errno == ENOENT)
359 				walkflags &= ~FTW_HOPTION;
360 			else
361 				walkflags |= FTW_HOPTION;
362 		}
363 
364 		/*
365 		 * We need this check as nftw needs a CWD and we have no
366 		 * way of returning back from that code with a meaningful
367 		 * error related to this
368 		 */
369 		if ((cwdpath = getcwd(NULL, PATH_MAX)) == NULL) {
370 			if ((errno == EACCES) && (walkflags & FTW_CHDIR)) {
371 				/*
372 				 * A directory above cwd is inaccessible,
373 				 * so don't do chdir(2)s. Slower, but at least
374 				 * it works.
375 				 */
376 				walkflags &= ~FTW_CHDIR;
377 				free(cwdpath);
378 			} else {
379 				(void) fprintf(stderr,
380 				    gettext("%s : cannot get the current "
381 				    "working directory\n"), cmdname);
382 				exit(1);
383 			}
384 		} else
385 			free(cwdpath);
386 
387 
388 		if (nftw(curpath, execute, 1000, walkflags)) {
389 			(void) fprintf(stderr,
390 			    gettext("%s: cannot open %s: %s\n"),
391 			    cmdname, curpath, strerror(errno));
392 			error = 1;
393 		}
394 
395 	}
396 
397 	/* execute any remaining variable length lists */
398 	while (lastlist) {
399 		if (lastlist->end != lastlist->nextstr) {
400 			*lastlist->nextvar = 0;
401 			(void) doexec((char *)0, lastlist->arglist,
402 			    &exec_exitcode);
403 		}
404 		lastlist = lastlist->next;
405 	}
406 	if (output != stdout)
407 		return (cmdclose(output));
408 	return ((exec_exitcode != 0) ? exec_exitcode : error);
409 }
410 
411 /*
412  * compile the arguments
413  */
414 
415 static int
416 compile(argv, np, actionp)
417 char **argv;
418 struct Node *np;
419 int *actionp;
420 {
421 	char *b;
422 	char **av;
423 	struct Node *oldnp = topnode;
424 	struct Args *argp;
425 	char **com;
426 	int i;
427 	enum Command wasop = PRINT;
428 
429 	if (init_yes() < 0) {
430 		(void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
431 		    strerror(errno));
432 		exit(1);
433 	}
434 
435 	for (av = argv; *av && (argp = lookup(*av)); av++) {
436 		np->next = 0;
437 		np->action = argp->action;
438 		np->type = argp->type;
439 		np->second.i = 0;
440 		if (argp->type == Op) {
441 			if (wasop == NOT || (wasop && np->action != NOT)) {
442 				(void) fprintf(stderr,
443 				gettext("%s: operand follows operand\n"),
444 						cmdname);
445 				exit(1);
446 			}
447 			if (np->action != NOT && oldnp == 0)
448 				goto err;
449 			wasop = argp->action;
450 		} else {
451 			wasop = PRINT;
452 			if (argp->type != Unary) {
453 				if (!(b = *++av)) {
454 					(void) fprintf(stderr,
455 					gettext("%s: incomplete statement\n"),
456 							cmdname);
457 					exit(1);
458 				}
459 				if (argp->type == Num) {
460 					if (((argp->action == MAXDEPTH) ||
461 					    (argp->action == MINDEPTH)) &&
462 					    ((int)strtol(b, (char **)NULL,
463 					    10) < 0))
464 						errx(1,
465 					gettext("%s: value must be positive"),
466 						    (argp->action == MAXDEPTH) ?
467 						    "maxdepth" : "mindepth");
468 					if ((argp->action != PERM) ||
469 					    (*b != '+')) {
470 						if (*b == '+' || *b == '-') {
471 							np->second.i = *b;
472 							b++;
473 						}
474 					}
475 				}
476 			}
477 		}
478 		switch (argp->action) {
479 		case AND:
480 			break;
481 		case NOT:
482 			break;
483 		case OR:
484 			np->first.np = topnode;
485 			topnode = np;
486 			oldnp->next = 0;
487 			break;
488 
489 		case LPAREN: {
490 			struct Node *save = topnode;
491 			topnode = np+1;
492 			paren_cnt++;
493 			i = compile(++av, topnode, actionp);
494 			np->first.np = topnode;
495 			topnode = save;
496 			av += i;
497 			oldnp = np;
498 			np += i + 1;
499 			oldnp->next = np;
500 			continue;
501 		}
502 
503 		case RPAREN:
504 			if (paren_cnt <= 0) {
505 				(void) fprintf(stderr,
506 				    gettext("%s: unmatched ')'\n"),
507 				    cmdname);
508 				exit(1);
509 			}
510 			paren_cnt--;
511 			if (oldnp == 0)
512 				goto err;
513 			if (oldnp->type == Op) {
514 				(void) fprintf(stderr,
515 				    gettext("%s: cannot immediately"
516 				    " follow an operand with ')'\n"),
517 				    cmdname);
518 				exit(1);
519 			}
520 			oldnp->next = 0;
521 			return (av-argv);
522 
523 		case FOLLOW:
524 			walkflags &= ~FTW_PHYS;
525 			break;
526 		case MOUNT:
527 			walkflags |= FTW_MOUNT;
528 			break;
529 		case DEPTH:
530 			walkflags |= FTW_DEPTH;
531 			break;
532 
533 		case LOCAL:
534 			np->first.l = 0L;
535 			np->first.ll = 0LL;
536 			np->second.i = '+';
537 			/*
538 			 * Make it compatible to df -l for
539 			 * future enhancement. So, anything
540 			 * that is not remote, then it is
541 			 * local.
542 			 */
543 			init_remote_fs();
544 			break;
545 
546 		case SIZE:
547 			if (b[strlen(b)-1] == 'c')
548 				np->action = CSIZE;
549 			/*FALLTHROUGH*/
550 		case INUM:
551 			np->first.ll = atoll(b);
552 			break;
553 
554 		case CMIN:
555 		case CTIME:
556 		case MMIN:
557 		case MTIME:
558 		case AMIN:
559 		case ATIME:
560 		case LINKS:
561 			np->first.l = atol(b);
562 			break;
563 
564 		case F_USER:
565 		case F_GROUP:
566 		case F_USERACL:
567 		case F_GROUPACL: {
568 			struct	passwd	*pw;
569 			struct	group *gr;
570 			long value;
571 			char *q;
572 
573 			value = -1;
574 			if (argp->action == F_USER ||
575 			    argp->action == F_USERACL) {
576 				if ((pw = getpwnam(b)) != 0)
577 					value = (long)pw->pw_uid;
578 			} else {
579 				if ((gr = getgrnam(b)) != 0)
580 					value = (long)gr->gr_gid;
581 			}
582 			if (value == -1) {
583 				errno = 0;
584 				value = strtol(b, &q, 10);
585 				if (errno != 0 || q == b || *q != '\0') {
586 					(void) fprintf(stderr, gettext(
587 					    "%s: cannot find %s name\n"),
588 						cmdname, *av);
589 					exit(1);
590 				}
591 			}
592 			np->first.l = value;
593 			break;
594 		}
595 
596 		case EXEC:
597 		case OK:
598 			walkflags &= ~FTW_CHDIR;
599 			np->first.ap = av;
600 			(*actionp)++;
601 			for (;;) {
602 				if ((b = *av) == 0) {
603 					(void) fprintf(stderr,
604 					gettext("%s: incomplete statement\n"),
605 						cmdname);
606 					exit(1);
607 				}
608 				if (strcmp(b, ";") == 0) {
609 					*av = 0;
610 					break;
611 				} else if (strcmp(b, "{}") == 0)
612 					*av = dummyarg;
613 				else if (strcmp(b, "+") == 0 &&
614 					av[-1] == dummyarg &&
615 					np->action == EXEC) {
616 					av[-1] = 0;
617 					np->first.vp = varargs(np->first.ap);
618 					np->action = VARARGS;
619 					break;
620 				}
621 				av++;
622 			}
623 			break;
624 
625 		case NAME:
626 		case INAME:
627 		case PATH:
628 		case IPATH:
629 			np->first.cp = b;
630 			break;
631 		case REGEX:
632 		case IREGEX: {
633 			int error;
634 			size_t errlen;
635 			char *errmsg;
636 
637 			if ((preg = realloc(preg, (npreg + 1) *
638 			    sizeof (regex_t))) == NULL)
639 				err(1, "realloc");
640 			if ((error = regcomp(&preg[npreg], b,
641 			    ((np->action == IREGEX) ? REG_ICASE : 0) |
642 			    ((Eflag) ? REG_EXTENDED : 0))) != 0) {
643 				errlen = regerror(error, &preg[npreg], NULL, 0);
644 				if ((errmsg = malloc(errlen)) == NULL)
645 					err(1, "malloc");
646 				(void) regerror(error, &preg[npreg], errmsg,
647 				    errlen);
648 				errx(1, gettext("RE error: %s"), errmsg);
649 			}
650 			npreg++;
651 			break;
652 		}
653 		case PERM:
654 			if (*b == '-')
655 				++b;
656 
657 			if (readmode(b) != NULL) {
658 				(void) fprintf(stderr, gettext(
659 				    "find: -perm: Bad permission string\n"));
660 				usage();
661 			}
662 			np->first.l = (long)getmode((mode_t)0);
663 			break;
664 		case TYPE:
665 			i = *b;
666 			np->first.l =
667 			    i == 'd' ? S_IFDIR :
668 			    i == 'b' ? S_IFBLK :
669 			    i == 'c' ? S_IFCHR :
670 #ifdef S_IFIFO
671 			    i == 'p' ? S_IFIFO :
672 #endif
673 			    i == 'f' ? S_IFREG :
674 #ifdef S_IFLNK
675 			    i == 'l' ? S_IFLNK :
676 #endif
677 #ifdef S_IFSOCK
678 			    i == 's' ? S_IFSOCK :
679 #endif
680 #ifdef S_IFDOOR
681 			    i == 'D' ? S_IFDOOR :
682 #endif
683 			    0;
684 			break;
685 
686 		case CPIO:
687 			if (walkflags & FTW_PHYS)
688 				com = cpio;
689 			else
690 				com = cpiol;
691 			goto common;
692 
693 		case NCPIO: {
694 			FILE *fd;
695 
696 			if (walkflags & FTW_PHYS)
697 				com = ncpio;
698 			else
699 				com = ncpiol;
700 		common:
701 			/* set up cpio */
702 			if ((fd = fopen(b, "w")) == NULL) {
703 				(void) fprintf(stderr,
704 					gettext("%s: cannot create %s\n"),
705 					cmdname, b);
706 				exit(1);
707 			}
708 
709 			np->first.l = (long)cmdopen("cpio", com, "w", fd);
710 			(void) fclose(fd);
711 			walkflags |= FTW_DEPTH;
712 			np->action = CPIO;
713 		}
714 			/*FALLTHROUGH*/
715 		case PRINT:
716 		case PRINT0:
717 			(*actionp)++;
718 			break;
719 
720 		case NEWER: {
721 			struct stat statb;
722 			if (stat(b, &statb) < 0) {
723 				(void) fprintf(stderr,
724 					gettext("%s: cannot access %s\n"),
725 					cmdname, b);
726 				exit(1);
727 			}
728 			np->first.l = statb.st_mtime;
729 			np->second.i = '+';
730 			break;
731 		}
732 
733 		case PRUNE:
734 		case NOUSER:
735 		case NOGRP:
736 			break;
737 		case FSTYPE:
738 			np->first.cp = b;
739 			break;
740 		case LS:
741 			(*actionp)++;
742 			break;
743 		case XATTR:
744 			break;
745 		case ACL:
746 			break;
747 		case MAXDEPTH:
748 			maxdepth = (int)strtol(b, (char **)NULL, 10);
749 			break;
750 		case MINDEPTH:
751 			mindepth = (int)strtol(b, (char **)NULL, 10);
752 			break;
753 		}
754 
755 		oldnp = np++;
756 		oldnp->next = np;
757 	}
758 
759 	if ((*av) || (wasop))
760 		goto err;
761 
762 	if (paren_cnt != 0) {
763 		(void) fprintf(stderr, gettext("%s: unmatched '('\n"),
764 		cmdname);
765 		exit(1);
766 	}
767 
768 	/* just before returning, save next free node from the list */
769 	freenode = oldnp->next;
770 	oldnp->next = 0;
771 	return (av-argv);
772 err:
773 	if (*av)
774 		(void) fprintf(stderr,
775 		    gettext("%s: bad option %s\n"), cmdname, *av);
776 	else
777 		(void) fprintf(stderr, gettext("%s: bad option\n"), cmdname);
778 	usage();
779 	/*NOTREACHED*/
780 }
781 
782 /*
783  * print out a usage message
784  */
785 
786 static void
787 usage(void)
788 {
789 	(void) fprintf(stderr,
790 	    gettext("%s: [-E] [-H | -L] path-list predicate-list\n"), cmdname);
791 	exit(1);
792 }
793 
794 /*
795  * This is the function that gets executed at each node
796  */
797 
798 static int
799 execute(name, statb, type, state)
800 char *name;
801 struct stat *statb;
802 int type;
803 struct FTW *state;
804 {
805 	struct Node *np = topnode;
806 	int val;
807 	time_t t;
808 	long l;
809 	long long ll;
810 	int not = 1;
811 	char *filename;
812 	int cnpreg = 0;
813 
814 	if (type == FTW_NS) {
815 		(void) fprintf(stderr, gettext("%s: stat() error %s: %s\n"),
816 			cmdname, name, strerror(errno));
817 		error = 1;
818 		return (0);
819 	} else if (type == FTW_DNR) {
820 		(void) fprintf(stderr, gettext("%s: cannot read dir %s: %s\n"),
821 			cmdname, name, strerror(errno));
822 		error = 1;
823 	} else if (type == FTW_SLN && lflag == 1) {
824 		(void) fprintf(stderr,
825 			gettext("%s: cannot follow symbolic link %s: %s\n"),
826 			cmdname, name, strerror(errno));
827 		error = 1;
828 	} else if (type == FTW_DL) {
829 		(void) fprintf(stderr, gettext("%s: cycle detected for %s\n"),
830 			cmdname, name);
831 		error = 1;
832 		return (0);
833 	}
834 
835 	if ((maxdepth != -1 && state->level > maxdepth) ||
836 	    (mindepth != -1 && state->level < mindepth))
837 		return (0);
838 
839 	while (np) {
840 		switch (np->action) {
841 		case NOT:
842 			not = !not;
843 			np = np->next;
844 			continue;
845 
846 		case AND:
847 			np = np->next;
848 			continue;
849 
850 		case OR:
851 			if (np->first.np == np) {
852 				/*
853 				 * handle naked OR (no term on left hand side)
854 				 */
855 				(void) fprintf(stderr,
856 				    gettext("%s: invalid -o construction\n"),
857 				    cmdname);
858 				exit(2);
859 			}
860 			/* FALLTHROUGH */
861 		case LPAREN: {
862 			struct Node *save = topnode;
863 			topnode = np->first.np;
864 			(void) execute(name, statb, type, state);
865 			val = lastval;
866 			topnode = save;
867 			if (np->action == OR) {
868 				if (val)
869 					return (0);
870 				val = 1;
871 			}
872 			break;
873 		}
874 
875 		case LOCAL: {
876 			int	nremfs;
877 			val = 1;
878 			/*
879 			 * If file system type matches the remote
880 			 * file system type, then it is not local.
881 			 */
882 			for (nremfs = 0; nremfs < fstype_index; nremfs++) {
883 				if (strcmp(remote_fstypes[nremfs],
884 						statb->st_fstype) == 0) {
885 					val = 0;
886 					break;
887 				}
888 			}
889 			break;
890 		}
891 
892 		case TYPE:
893 			l = (long)statb->st_mode&S_IFMT;
894 			goto num;
895 
896 		case PERM:
897 			l = (long)statb->st_mode&07777;
898 			if (np->second.i == '-')
899 				val = ((l&np->first.l) == np->first.l);
900 			else
901 				val = (l == np->first.l);
902 			break;
903 
904 		case INUM:
905 			ll = (long long)statb->st_ino;
906 			goto llnum;
907 		case NEWER:
908 			l = statb->st_mtime;
909 			goto num;
910 		case ATIME:
911 			t = statb->st_atime;
912 			goto days;
913 		case CTIME:
914 			t = statb->st_ctime;
915 			goto days;
916 		case MTIME:
917 			t = statb->st_mtime;
918 		days:
919 			l = (now-t)/A_DAY;
920 			goto num;
921 		case MMIN:
922 			t = statb->st_mtime;
923 			goto mins;
924 		case AMIN:
925 			t = statb->st_atime;
926 			goto mins;
927 		case CMIN:
928 			t = statb->st_ctime;
929 			goto mins;
930 		mins:
931 			l = (now-t)/A_MIN;
932 			goto num;
933 		case CSIZE:
934 			ll = (long long)statb->st_size;
935 			goto llnum;
936 		case SIZE:
937 			ll = (long long)round(statb->st_size, BLKSIZ)/BLKSIZ;
938 			goto llnum;
939 		case F_USER:
940 			l = (long)statb->st_uid;
941 			goto num;
942 		case F_GROUP:
943 			l = (long)statb->st_gid;
944 			goto num;
945 		case LINKS:
946 			l = (long)statb->st_nlink;
947 			goto num;
948 		llnum:
949 			if (np->second.i == '+')
950 				val = (ll > np->first.ll);
951 			else if (np->second.i == '-')
952 				val = (ll < np->first.ll);
953 			else
954 				val = (ll == np->first.ll);
955 			break;
956 		num:
957 			if (np->second.i == '+')
958 				val = (l > np->first.l);
959 			else if (np->second.i == '-')
960 				val = (l < np->first.l);
961 			else
962 				val = (l == np->first.l);
963 			break;
964 		case OK:
965 			val = ok(name, np->first.ap);
966 			break;
967 		case EXEC:
968 			val = doexec(name, np->first.ap, NULL);
969 			break;
970 
971 		case VARARGS: {
972 			struct Arglist *ap = np->first.vp;
973 			char *cp;
974 			cp = ap->nextstr - (strlen(name)+1);
975 			if (cp >= (char *)(ap->nextvar+3)) {
976 				/* there is room just copy the name */
977 				val = 1;
978 				(void) strcpy(cp, name);
979 				*ap->nextvar++ = cp;
980 				ap->nextstr = cp;
981 			} else {
982 				/* no more room, exec command */
983 				*ap->nextvar++ = name;
984 				*ap->nextvar = 0;
985 				val = 1;
986 				(void) doexec((char *)0, ap->arglist,
987 				    &exec_exitcode);
988 				ap->nextstr = ap->end;
989 				ap->nextvar = ap->firstvar;
990 			}
991 			break;
992 		}
993 
994 		case DEPTH:
995 		case MOUNT:
996 		case FOLLOW:
997 			val = 1;
998 			break;
999 
1000 		case NAME:
1001 		case INAME:
1002 		case PATH:
1003 		case IPATH: {
1004 			char *path;
1005 			int fnmflags = 0;
1006 
1007 			if (np->action == INAME || np->action == IPATH)
1008 				fnmflags = FNM_IGNORECASE;
1009 
1010 			/*
1011 			 * basename(3c) may modify name, so
1012 			 * we need to pass another string
1013 			 */
1014 			if ((path = strdup(name)) == NULL) {
1015 				(void) fprintf(stderr,
1016 				    gettext("%s: cannot strdup() %s: %s\n"),
1017 				    cmdname, name, strerror(errno));
1018 				exit(2);
1019 			}
1020 			/*
1021 			 * XPG4 find should not treat a leading '.' in a
1022 			 * filename specially for pattern matching.
1023 			 * /usr/bin/find  will not pattern match a leading
1024 			 * '.' in a filename, unless '.' is explicitly
1025 			 * specified.
1026 			 */
1027 #ifndef XPG4
1028 			fnmflags |= FNM_PERIOD;
1029 #endif
1030 
1031 			val = !fnmatch(np->first.cp,
1032 			    (np->action == NAME || np->action == INAME)
1033 				? basename(path) : path, fnmflags);
1034 			free(path);
1035 			break;
1036 		}
1037 
1038 		case PRUNE:
1039 			if (type == FTW_D)
1040 				state->quit = FTW_PRUNE;
1041 			val = 1;
1042 			break;
1043 		case NOUSER:
1044 			val = ((getpwuid(statb->st_uid)) == 0);
1045 			break;
1046 		case NOGRP:
1047 			val = ((getgrgid(statb->st_gid)) == 0);
1048 			break;
1049 		case FSTYPE:
1050 			val = (strcmp(np->first.cp, statb->st_fstype) == 0);
1051 			break;
1052 		case CPIO:
1053 			output = (FILE *)np->first.l;
1054 			(void) fprintf(output, "%s\n", name);
1055 			val = 1;
1056 			break;
1057 		case PRINT:
1058 		case PRINT0:
1059 			(void) fprintf(stdout, "%s%c", name,
1060 			    (np->action == PRINT) ? '\n' : '\0');
1061 			val = 1;
1062 			break;
1063 		case LS:
1064 			(void) list(name, statb);
1065 			val = 1;
1066 			break;
1067 		case XATTR:
1068 			filename = (walkflags & FTW_CHDIR) ?
1069 				gettail(name) : name;
1070 			val = (pathconf(filename, _PC_XATTR_EXISTS) == 1);
1071 			break;
1072 		case ACL:
1073 			/*
1074 			 * Need to get the tail of the file name, since we have
1075 			 * already chdir()ed into the directory (performed in
1076 			 * nftw()) of the file
1077 			 */
1078 			filename = (walkflags & FTW_CHDIR) ?
1079 				gettail(name) : name;
1080 			val = acl_trivial(filename);
1081 			break;
1082 		case F_USERACL:
1083 		case F_GROUPACL: {
1084 			int i;
1085 			acl_t *acl;
1086 			void *acl_entry;
1087 			aclent_t *p1;
1088 			ace_t *p2;
1089 
1090 			filename = (walkflags & FTW_CHDIR) ?
1091 			    gettail(name) : name;
1092 			val = 0;
1093 			if (acl_get(filename, 0, &acl) != 0)
1094 				break;
1095 			for (i = 0, acl_entry = acl->acl_aclp;
1096 			    i != acl->acl_cnt; i++) {
1097 				if (acl->acl_type == ACLENT_T) {
1098 					p1 = (aclent_t *)acl_entry;
1099 					if (p1->a_id == np->first.l) {
1100 						val = 1;
1101 						acl_free(acl);
1102 						break;
1103 					}
1104 				} else {
1105 					p2 = (ace_t *)acl_entry;
1106 					if (p2->a_who == np->first.l) {
1107 						val = 1;
1108 						acl_free(acl);
1109 						break;
1110 					}
1111 				}
1112 				acl_entry = ((char *)acl_entry +
1113 				    acl->acl_entry_size);
1114 			}
1115 			acl_free(acl);
1116 			break;
1117 		}
1118 		case IREGEX:
1119 		case REGEX: {
1120 			regmatch_t pmatch;
1121 
1122 			val = 0;
1123 			if (regexec(&preg[cnpreg], name, 1, &pmatch, NULL) == 0)
1124 				val = ((pmatch.rm_so == 0) &&
1125 				    (pmatch.rm_eo == strlen(name)));
1126 			cnpreg++;
1127 			break;
1128 		}
1129 		case MAXDEPTH:
1130 			if (state->level == maxdepth && type == FTW_D)
1131 				state->quit = FTW_PRUNE;
1132 			/* FALLTHROUGH */
1133 		case MINDEPTH:
1134 			val = 1;
1135 			break;
1136 		}
1137 		/*
1138 		 * evaluate 'val' and 'not' (exclusive-or)
1139 		 * if no inversion (not == 1), return only when val == 0
1140 		 * (primary not true). Otherwise, invert the primary
1141 		 * and return when the primary is true.
1142 		 * 'Lastval' saves the last result (fail or pass) when
1143 		 * returning back to the calling routine.
1144 		 */
1145 		if (val^not) {
1146 			lastval = 0;
1147 			return (0);
1148 		}
1149 		lastval = 1;
1150 		not = 1;
1151 		np = np->next;
1152 	}
1153 	return (0);
1154 }
1155 
1156 /*
1157  * code for the -ok option
1158  */
1159 
1160 static int
1161 ok(name, argv)
1162 char *name;
1163 char *argv[];
1164 {
1165 	int  c;
1166 	int i = 0;
1167 	char resp[LINE_MAX + 1];
1168 
1169 	(void) fflush(stdout); 	/* to flush possible `-print' */
1170 
1171 	if ((*argv != dummyarg) && (strcmp(*argv, name)))
1172 		(void) fprintf(stderr, "< %s ... %s >?   ", *argv, name);
1173 	else
1174 		(void) fprintf(stderr, "< {} ... %s >?   ", name);
1175 
1176 	(void) fflush(stderr);
1177 
1178 	while ((c = getchar()) != '\n') {
1179 		if (c == EOF)
1180 			exit(2);
1181 		if (i < LINE_MAX)
1182 			resp[i++] = c;
1183 	}
1184 	resp[i] = '\0';
1185 
1186 	if (yes_check(resp))
1187 		return (doexec(name, argv, NULL));
1188 	else
1189 		return (0);
1190 }
1191 
1192 /*
1193  * execute argv with {} replaced by name
1194  *
1195  * Per XPG6, find must exit non-zero if an invocation through
1196  * -exec, punctuated by a plus sign, exits non-zero, so set
1197  * exitcode if we see a non-zero exit.
1198  * exitcode should be NULL when -exec or -ok is not punctuated
1199  * by a plus sign.
1200  */
1201 
1202 static int
1203 doexec(char *name, char *argv[], int *exitcode)
1204 {
1205 	char *cp;
1206 	char **av = argv;
1207 	char *newargs[1 + SHELL_MAXARGS + 1];
1208 	int dummyseen = 0;
1209 	int i, j, status, rc, r = 0;
1210 	int exit_status = 0;
1211 	pid_t pid, pid1;
1212 
1213 	(void) fflush(stdout);	  /* to flush possible `-print' */
1214 	if (name) {
1215 		while (cp = *av++) {
1216 			if (cp == dummyarg) {
1217 				dummyseen = 1;
1218 				av[-1] = name;
1219 			}
1220 
1221 		}
1222 	}
1223 	if (argv[0] == NULL)    /* null command line */
1224 		return (r);
1225 
1226 	if ((pid = fork()) == -1) {
1227 		/* fork failed */
1228 		if (exitcode != NULL)
1229 			*exitcode = 1;
1230 		return (0);
1231 	}
1232 	if (pid != 0) {
1233 		/* parent */
1234 		do {
1235 			/* wait for child to exit */
1236 			if ((rc = wait(&r)) == -1 && errno != EINTR) {
1237 				(void) fprintf(stderr,
1238 				    gettext("wait failed %s"), strerror(errno));
1239 
1240 				if (exitcode != NULL)
1241 					*exitcode = 1;
1242 				return (0);
1243 			}
1244 		} while (rc != pid);
1245 	} else {
1246 		/* child */
1247 		(void) execvp(argv[0], argv);
1248 		if (errno != E2BIG)
1249 			exit(1);
1250 
1251 		/*
1252 		 * We are in a situation where argv[0] points to a
1253 		 * script without the interpreter line, e.g. #!/bin/sh.
1254 		 * execvp() will execute either /usr/bin/sh or
1255 		 * /usr/xpg4/bin/sh against the script, and you will be
1256 		 * limited to SHELL_MAXARGS arguments. If you try to
1257 		 * pass more than SHELL_MAXARGS arguments, execvp()
1258 		 * fails with E2BIG.
1259 		 * See usr/src/lib/libc/port/gen/execvp.c.
1260 		 *
1261 		 * In this situation, process the argument list by
1262 		 * packets of SHELL_MAXARGS arguments with respect of
1263 		 * the following rules:
1264 		 * 1. the invocations have to complete before find exits
1265 		 * 2. only one invocation can be running at a time
1266 		 */
1267 
1268 		i = 1;
1269 		newargs[0] = argv[0];
1270 
1271 		while (argv[i]) {
1272 			j = 1;
1273 			while (j <= SHELL_MAXARGS && argv[i]) {
1274 				newargs[j++] = argv[i++];
1275 			}
1276 			newargs[j] = NULL;
1277 
1278 			if ((pid1 = fork()) == -1) {
1279 				/* fork failed */
1280 				exit(1);
1281 			}
1282 			if (pid1 == 0) {
1283 				/* child */
1284 				(void) execvp(newargs[0], newargs);
1285 				exit(1);
1286 			}
1287 
1288 			status = 0;
1289 
1290 			do {
1291 				/* wait for the child to exit */
1292 				if ((rc = wait(&status)) == -1 &&
1293 				    errno != EINTR) {
1294 					(void) fprintf(stderr,
1295 					    gettext("wait failed %s"),
1296 					    strerror(errno));
1297 					exit(1);
1298 				}
1299 			} while (rc != pid1);
1300 
1301 			if (status)
1302 				exit_status = 1;
1303 		}
1304 		/* all the invocations have completed */
1305 		exit(exit_status);
1306 	}
1307 
1308 	if (name && dummyseen) {
1309 		for (av = argv; cp = *av++; ) {
1310 			if (cp == name)
1311 				av[-1] = dummyarg;
1312 		}
1313 	}
1314 
1315 	if (r && exitcode != NULL)
1316 		*exitcode = 3; /* use to indicate error in cmd invocation */
1317 
1318 	return (!r);
1319 }
1320 
1321 
1322 /*
1323  *  Table lookup routine
1324  */
1325 static struct Args *
1326 lookup(word)
1327 char *word;
1328 {
1329 	struct Args *argp = commands;
1330 	int second;
1331 	if (word == 0 || *word == 0)
1332 		return (0);
1333 	second = word[1];
1334 	while (*argp->name) {
1335 		if (second == argp->name[1] && strcmp(word, argp->name) == 0)
1336 			return (argp);
1337 		argp++;
1338 	}
1339 	return (0);
1340 }
1341 
1342 
1343 /*
1344  * Get space for variable length argument list
1345  */
1346 
1347 static struct Arglist *
1348 varargs(com)
1349 char **com;
1350 {
1351 	struct Arglist *ap;
1352 	int n;
1353 	char **ep;
1354 	if (varsize == 0) {
1355 		n = 2*sizeof (char **);
1356 		for (ep = environ; *ep; ep++)
1357 			n += (strlen(*ep)+sizeof (ep) + 1);
1358 		varsize = sizeof (struct Arglist)+ARG_MAX-PATH_MAX-n-1;
1359 	}
1360 	ap = (struct Arglist *)malloc(varsize+1);
1361 	ap->end = (char *)ap + varsize;
1362 	ap->nextstr = ap->end;
1363 	ap->nextvar = ap->arglist;
1364 	while (*ap->nextvar++ = *com++);
1365 	ap->nextvar--;
1366 	ap->firstvar = ap->nextvar;
1367 	ap->next = lastlist;
1368 	lastlist = ap;
1369 	return (ap);
1370 }
1371 
1372 /*
1373  * filter command support
1374  * fork and exec cmd(argv) according to mode:
1375  *
1376  *	"r"	with fp as stdin of cmd (default stdin), cmd stdout returned
1377  *	"w"	with fp as stdout of cmd (default stdout), cmd stdin returned
1378  */
1379 
1380 #define	CMDERR	((1<<8)-1)	/* command error exit code		*/
1381 #define	MAXCMDS	8		/* max # simultaneous cmdopen()'s	*/
1382 
1383 static struct			/* info for each cmdopen()		*/
1384 {
1385 	FILE	*fp;		/* returned by cmdopen()		*/
1386 	pid_t	pid;		/* pid used by cmdopen()		*/
1387 } cmdproc[MAXCMDS];
1388 
1389 static FILE *
1390 cmdopen(cmd, argv, mode, fp)
1391 char	*cmd;
1392 char	**argv;
1393 char	*mode;
1394 FILE	*fp;
1395 {
1396 	int	proc;
1397 	int	cmdfd;
1398 	int	usrfd;
1399 	int		pio[2];
1400 
1401 	switch (*mode) {
1402 	case 'r':
1403 		cmdfd = 1;
1404 		usrfd = 0;
1405 		break;
1406 	case 'w':
1407 		cmdfd = 0;
1408 		usrfd = 1;
1409 		break;
1410 	default:
1411 		return (0);
1412 	}
1413 
1414 	for (proc = 0; proc < MAXCMDS; proc++)
1415 		if (!cmdproc[proc].fp)
1416 			break;
1417 	if (proc >= MAXCMDS)
1418 		return (0);
1419 
1420 	if (pipe(pio))
1421 		return (0);
1422 
1423 	switch (cmdproc[proc].pid = fork()) {
1424 	case -1:
1425 		return (0);
1426 	case 0:
1427 		if (fp && fileno(fp) != usrfd) {
1428 			(void) close(usrfd);
1429 			if (dup2(fileno(fp), usrfd) != usrfd)
1430 				_exit(CMDERR);
1431 			(void) close(fileno(fp));
1432 		}
1433 		(void) close(cmdfd);
1434 		if (dup2(pio[cmdfd], cmdfd) != cmdfd)
1435 			_exit(CMDERR);
1436 		(void) close(pio[cmdfd]);
1437 		(void) close(pio[usrfd]);
1438 		(void) execvp(cmd, argv);
1439 		if (errno == ENOEXEC) {
1440 			char	**p;
1441 			char		**v;
1442 
1443 			/*
1444 			 * assume cmd is a shell script
1445 			 */
1446 
1447 			p = argv;
1448 			while (*p++);
1449 			if (v = (char **)malloc((p - argv + 1) *
1450 					sizeof (char **))) {
1451 				p = v;
1452 				*p++ = cmd;
1453 				if (*argv) argv++;
1454 				while (*p++ = *argv++);
1455 				(void) execv(getshell(), v);
1456 			}
1457 		}
1458 		_exit(CMDERR);
1459 		/*NOTREACHED*/
1460 	default:
1461 		(void) close(pio[cmdfd]);
1462 		return (cmdproc[proc].fp = fdopen(pio[usrfd], mode));
1463 	}
1464 }
1465 
1466 /*
1467  * close a stream opened by cmdopen()
1468  * -1 returned if cmdopen() had a problem
1469  * otherwise exit() status of command is returned
1470  */
1471 
1472 static int
1473 cmdclose(fp)
1474 FILE	*fp;
1475 {
1476 	int	i;
1477 	pid_t	p, pid;
1478 	int		status;
1479 
1480 	for (i = 0; i < MAXCMDS; i++)
1481 		if (fp == cmdproc[i].fp) break;
1482 	if (i >= MAXCMDS)
1483 		return (-1);
1484 	(void) fclose(fp);
1485 	cmdproc[i].fp = 0;
1486 	pid = cmdproc[i].pid;
1487 	while ((p = wait(&status)) != pid && p != (pid_t)-1);
1488 	if (p == pid) {
1489 		status = (status >> 8) & CMDERR;
1490 		if (status == CMDERR)
1491 			status = -1;
1492 	}
1493 	else
1494 		status = -1;
1495 	return (status);
1496 }
1497 
1498 /*
1499  * return pointer to the full path name of the shell
1500  *
1501  * SHELL is read from the environment and must start with /
1502  *
1503  * if set-uid or set-gid then the executable and its containing
1504  * directory must not be writable by the real user
1505  *
1506  * /usr/bin/sh is returned by default
1507  */
1508 
1509 char *
1510 getshell()
1511 {
1512 	char	*s;
1513 	char	*sh;
1514 	uid_t	u;
1515 	int	j;
1516 
1517 	if (((sh = getenv("SHELL")) != 0) && *sh == '/') {
1518 		if (u = getuid()) {
1519 			if ((u != geteuid() || getgid() != getegid()) &&
1520 			    access(sh, 2) == 0)
1521 				goto defshell;
1522 			s = strrchr(sh, '/');
1523 			*s = 0;
1524 			j = access(sh, 2);
1525 			*s = '/';
1526 			if (!j) goto defshell;
1527 		}
1528 		return (sh);
1529 	}
1530 defshell:
1531 	return ("/usr/bin/sh");
1532 }
1533 
1534 /*
1535  * the following functions implement the added "-ls" option
1536  */
1537 
1538 #include <utmpx.h>
1539 #include <sys/mkdev.h>
1540 
1541 struct		utmpx utmpx;
1542 #define	NMAX	(sizeof (utmpx.ut_name))
1543 #define	SCPYN(a, b)	(void) strncpy(a, b, NMAX)
1544 
1545 #define	NUID	64
1546 #define	NGID	64
1547 
1548 static struct ncache {
1549 	int	id;
1550 	char	name[NMAX+1];
1551 } nc[NUID], gc[NGID];
1552 
1553 /*
1554  * This function assumes that the password file is hashed
1555  * (or some such) to allow fast access based on a name key.
1556  */
1557 static char *
1558 getname(uid_t uid)
1559 {
1560 	struct passwd *pw;
1561 	int cp;
1562 
1563 #if	(((NUID) & ((NUID) - 1)) != 0)
1564 	cp = uid % (NUID);
1565 #else
1566 	cp = uid & ((NUID) - 1);
1567 #endif
1568 	if (nc[cp].id == uid && nc[cp].name[0])
1569 		return (nc[cp].name);
1570 	pw = getpwuid(uid);
1571 	if (!pw)
1572 		return (0);
1573 	nc[cp].id = uid;
1574 	SCPYN(nc[cp].name, pw->pw_name);
1575 	return (nc[cp].name);
1576 }
1577 
1578 /*
1579  * This function assumes that the group file is hashed
1580  * (or some such) to allow fast access based on a name key.
1581  */
1582 static char *
1583 getgroup(gid_t gid)
1584 {
1585 	struct group *gr;
1586 	int cp;
1587 
1588 #if	(((NGID) & ((NGID) - 1)) != 0)
1589 	cp = gid % (NGID);
1590 #else
1591 	cp = gid & ((NGID) - 1);
1592 #endif
1593 	if (gc[cp].id == gid && gc[cp].name[0])
1594 		return (gc[cp].name);
1595 	gr = getgrgid(gid);
1596 	if (!gr)
1597 		return (0);
1598 	gc[cp].id = gid;
1599 	SCPYN(gc[cp].name, gr->gr_name);
1600 	return (gc[cp].name);
1601 }
1602 
1603 #define	permoffset(who)		((who) * 3)
1604 #define	permission(who, type)	((type) >> permoffset(who))
1605 #define	kbytes(bytes)		(((bytes) + 1023) / 1024)
1606 
1607 static int
1608 list(file, stp)
1609 	char *file;
1610 	struct stat *stp;
1611 {
1612 	char pmode[32], uname[32], gname[32], fsize[32], ftime[32];
1613 	int trivial;
1614 
1615 /*
1616  * Each line below contains the relevant permission (column 1) and character
1617  * shown when  the corresponding execute bit is either clear (column 2)
1618  * or set (column 3)
1619  * These permissions are as shown by ls(1b)
1620  */
1621 	static long special[] = {	S_ISUID, 'S', 's',
1622 					S_ISGID, 'S', 's',
1623 					S_ISVTX, 'T', 't' };
1624 
1625 	static time_t sixmonthsago = -1;
1626 #ifdef	S_IFLNK
1627 	char flink[MAXPATHLEN + 1];
1628 #endif
1629 	int who;
1630 	char *cp;
1631 	char *tailname;
1632 	time_t now;
1633 	long long ksize;
1634 
1635 	if (file == NULL || stp == NULL)
1636 		return (-1);
1637 
1638 	(void) time(&now);
1639 	if (sixmonthsago == -1)
1640 		sixmonthsago = now - 6L*30L*24L*60L*60L;
1641 
1642 	switch (stp->st_mode & S_IFMT) {
1643 #ifdef	S_IFDIR
1644 	case S_IFDIR:	/* directory */
1645 		pmode[0] = 'd';
1646 		break;
1647 #endif
1648 #ifdef	S_IFCHR
1649 	case S_IFCHR:	/* character special */
1650 		pmode[0] = 'c';
1651 		break;
1652 #endif
1653 #ifdef	S_IFBLK
1654 	case S_IFBLK:	/* block special */
1655 		pmode[0] = 'b';
1656 		break;
1657 #endif
1658 #ifdef	S_IFIFO
1659 	case S_IFIFO:	/* fifo special */
1660 		pmode[0] = 'p';
1661 		break;
1662 #endif
1663 #ifdef	S_IFLNK
1664 	case S_IFLNK:	/* symbolic link */
1665 		pmode[0] = 'l';
1666 		break;
1667 #endif
1668 #ifdef	S_IFSOCK
1669 	case S_IFSOCK:	/* socket */
1670 		pmode[0] = 's';
1671 		break;
1672 #endif
1673 #ifdef	S_IFDOOR
1674 	case S_IFDOOR:	/* door */
1675 		pmode[0] = 'D';
1676 		break;
1677 #endif
1678 #ifdef	S_IFREG
1679 	case S_IFREG:	/* regular */
1680 		pmode[0] = '-';
1681 		break;
1682 #endif
1683 	default:
1684 		pmode[0] = '?';
1685 		break;
1686 	}
1687 
1688 	for (who = 0; who < 3; who++) {
1689 		int is_exec =  stp->st_mode & permission(who, S_IEXEC)? 1 : 0;
1690 
1691 		if (stp->st_mode & permission(who, S_IREAD))
1692 			pmode[permoffset(who) + 1] = 'r';
1693 		else
1694 			pmode[permoffset(who) + 1] = '-';
1695 
1696 		if (stp->st_mode & permission(who, S_IWRITE))
1697 			pmode[permoffset(who) + 2] = 'w';
1698 		else
1699 			pmode[permoffset(who) + 2] = '-';
1700 
1701 		if (stp->st_mode & special[who * 3])
1702 			pmode[permoffset(who) + 3] =
1703 				special[who * 3 + 1 + is_exec];
1704 		else if (is_exec)
1705 			pmode[permoffset(who) + 3] = 'x';
1706 		else
1707 			pmode[permoffset(who) + 3] = '-';
1708 	}
1709 
1710 	/*
1711 	 * Need to get the tail of the file name, since we have
1712 	 * already chdir()ed into the directory of the file
1713 	 */
1714 
1715 	tailname = gettail(file);
1716 
1717 	trivial = acl_trivial(tailname);
1718 	if (trivial == -1)
1719 		trivial =  0;
1720 
1721 	if (trivial == 1)
1722 		pmode[permoffset(who) + 1] = '+';
1723 	else
1724 		pmode[permoffset(who) + 1] = ' ';
1725 
1726 	pmode[permoffset(who) + 2] = '\0';
1727 
1728 	/*
1729 	 * Prepare uname and gname.  Always add a space afterwards
1730 	 * to keep columns from running together.
1731 	 */
1732 	cp = getname(stp->st_uid);
1733 	if (cp != NULL)
1734 		(void) sprintf(uname, "%-8s ", cp);
1735 	else
1736 		(void) sprintf(uname, "%-8u ", stp->st_uid);
1737 
1738 	cp = getgroup(stp->st_gid);
1739 	if (cp != NULL)
1740 		(void) sprintf(gname, "%-8s ", cp);
1741 	else
1742 		(void) sprintf(gname, "%-8u ", stp->st_gid);
1743 
1744 	if (pmode[0] == 'b' || pmode[0] == 'c')
1745 		(void) sprintf(fsize, "%3ld,%4ld",
1746 			major(stp->st_rdev), minor(stp->st_rdev));
1747 	else {
1748 		(void) sprintf(fsize, (stp->st_size < 100000000) ?
1749 			"%8lld" : "%lld", stp->st_size);
1750 #ifdef	S_IFLNK
1751 		if (pmode[0] == 'l') {
1752 
1753 
1754 			who = readlink(tailname, flink, sizeof (flink) - 1);
1755 
1756 			if (who >= 0)
1757 				flink[who] = '\0';
1758 			else
1759 				flink[0] = '\0';
1760 		}
1761 #endif
1762 	}
1763 
1764 	cp = ctime(&stp->st_mtime);
1765 	if (stp->st_mtime < sixmonthsago || stp->st_mtime > now)
1766 		(void) sprintf(ftime, "%-7.7s %-4.4s", cp + 4, cp + 20);
1767 	else
1768 		(void) sprintf(ftime, "%-12.12s", cp + 4);
1769 
1770 	(void) printf((stp->st_ino < 100000) ? "%5llu " :
1771 		"%llu ", stp->st_ino);  /* inode #	*/
1772 #ifdef	S_IFSOCK
1773 	ksize = (long long) kbytes(ldbtob(stp->st_blocks)); /* kbytes */
1774 #else
1775 	ksize = (long long) kbytes(stp->st_size); /* kbytes */
1776 #endif
1777 	(void) printf((ksize < 10000) ? "%4lld " : "%lld ", ksize);
1778 	(void) printf("%s %2ld %s%s%s %s %s%s%s\n",
1779 		pmode,					/* protection	*/
1780 		stp->st_nlink,				/* # of links	*/
1781 		uname,					/* owner	*/
1782 		gname,					/* group	*/
1783 		fsize,					/* # of bytes	*/
1784 		ftime,					/* modify time	*/
1785 		file,					/* name		*/
1786 #ifdef	S_IFLNK
1787 		(pmode[0] == 'l') ? " -> " : "",
1788 		(pmode[0] == 'l') ? flink  : ""		/* symlink	*/
1789 #else
1790 		"",
1791 		""
1792 #endif
1793 );
1794 
1795 	return (0);
1796 }
1797 
1798 static char *
1799 new_string(char *s)
1800 {
1801 	char *p = strdup(s);
1802 
1803 	if (p)
1804 		return (p);
1805 	(void) fprintf(stderr, gettext("%s: out of memory\n"), cmdname);
1806 	exit(1);
1807 	/*NOTREACHED*/
1808 }
1809 
1810 /*
1811  * Read remote file system types from REMOTE_FS into the
1812  * remote_fstypes array.
1813  */
1814 static void
1815 init_remote_fs()
1816 {
1817 	FILE    *fp;
1818 	char    line_buf[LINEBUF_SIZE];
1819 
1820 	if ((fp = fopen(REMOTE_FS, "r")) == NULL) {
1821 		(void) fprintf(stderr,
1822 		    gettext("%s: Warning: can't open %s, ignored\n"),
1823 		    REMOTE_FS, cmdname);
1824 		/* Use default string name for NFS */
1825 		remote_fstypes[fstype_index++] = "nfs";
1826 		return;
1827 	}
1828 
1829 	while (fgets(line_buf, sizeof (line_buf), fp) != NULL) {
1830 		char buf[LINEBUF_SIZE];
1831 
1832 		/* LINTED - unbounded string specifier */
1833 		(void) sscanf(line_buf, "%s", buf);
1834 		remote_fstypes[fstype_index++] = new_string(buf);
1835 
1836 		if (fstype_index == N_FSTYPES)
1837 			break;
1838 	}
1839 	(void) fclose(fp);
1840 }
1841 
1842 #define	NPERM	30			/* Largest machine */
1843 
1844 /*
1845  * The PERM struct is the machine that builds permissions.  The p_special
1846  * field contains what permissions need to be checked at run-time in
1847  * getmode().  This is one of 'X', 'u', 'g', or 'o'.  It contains '\0' to
1848  * indicate normal processing.
1849  */
1850 typedef	struct	PERMST	{
1851 	ushort_t	p_who;		/* Range of permission (e.g. ugo) */
1852 	ushort_t	p_perm;		/* Bits to turn on, off, assign */
1853 	uchar_t		p_op;		/* Operation: + - = */
1854 	uchar_t		p_special;	/* Special handling? */
1855 }	PERMST;
1856 
1857 #ifndef	S_ISVTX
1858 #define	S_ISVTX	0			/* Not .1 */
1859 #endif
1860 
1861 /* Mask values */
1862 #define	P_A	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) /* allbits */
1863 #define	P_U	(S_ISUID|S_ISVTX|S_IRWXU)		/* user */
1864 #define	P_G	(S_ISGID|S_ISVTX|S_IRWXG)		/* group */
1865 #define	P_O	(S_ISVTX|S_IRWXO)			/* other */
1866 
1867 static	int	iswho(int c);
1868 static	int	isop(int c);
1869 static	int	isperm(PERMST *pp, int c);
1870 
1871 static	PERMST	machine[NPERM];		/* Permission construction machine */
1872 static	PERMST	*endp;			/* Last used PERM structure */
1873 
1874 static	uint_t	nowho;			/* No who for this mode (DOS kludge) */
1875 
1876 /*
1877  * Read an ASCII string containing the symbolic/octal mode and
1878  * compile an automaton that recognizes it.  The return value
1879  * is NULL if everything is OK, otherwise it is -1.
1880  */
1881 static int
1882 readmode(ascmode)
1883 const char *ascmode;
1884 {
1885 	const char *amode = ascmode;
1886 	PERMST *pp;
1887 	int seen_X;
1888 
1889 	nowho = 0;
1890 	seen_X = 0;
1891 	pp = &machine[0];
1892 	if (*amode >= '0' && *amode <= '7') {
1893 		int mode;
1894 
1895 		mode = 0;
1896 		while (*amode >= '0' && *amode <= '7')
1897 			mode = (mode<<3) + *amode++ - '0';
1898 		if (*amode != '\0')
1899 			return (-1);
1900 #if	S_ISUID != 04000 || S_ISGID != 02000 || \
1901 	S_IRUSR != 0400 || S_IWUSR != 0200 || S_IXUSR != 0100 || \
1902 	S_IRGRP != 0040 || S_IWGRP != 0020 || S_IXGRP != 0010 || \
1903 	S_IROTH != 0004 || S_IWOTH != 0002 || S_IXOTH != 0001
1904 		/*
1905 		 * There is no requirement of the octal mode bits being
1906 		 * the same as the S_ macros.
1907 		 */
1908 	{
1909 		mode_t mapping[] = {
1910 			S_IXOTH, S_IWOTH, S_IROTH,
1911 			S_IXGRP, S_IWGRP, S_IRGRP,
1912 			S_IXUSR, S_IWUSR, S_IRUSR,
1913 			S_ISGID, S_ISUID,
1914 			0
1915 		};
1916 		int i, newmode = 0;
1917 
1918 		for (i = 0; mapping[i] != 0; i++)
1919 			if (mode & (1<<i))
1920 				newmode |= mapping[i];
1921 		mode = newmode;
1922 	}
1923 #endif
1924 		pp->p_who = P_A;
1925 		pp->p_perm = mode;
1926 		pp->p_op = '=';
1927 	} else	for (;;) {
1928 		int t;
1929 		int who = 0;
1930 
1931 		while ((t = iswho(*amode)) != 0) {
1932 			++amode;
1933 			who |= t;
1934 		}
1935 		if (who == 0) {
1936 			mode_t currmask;
1937 			(void) umask(currmask = umask((mode_t)0));
1938 
1939 			/*
1940 			 * If no who specified, must use contents of
1941 			 * umask to determine which bits to flip.  This
1942 			 * is POSIX/V7/BSD behaviour, but not SVID.
1943 			 */
1944 			who = (~currmask)&P_A;
1945 			++nowho;
1946 		} else
1947 			nowho = 0;
1948 	samewho:
1949 		if (!isop(pp->p_op = *amode++))
1950 			return (-1);
1951 		pp->p_perm = 0;
1952 		pp->p_special = 0;
1953 		while ((t = isperm(pp, *amode)) != 0) {
1954 			if (pp->p_special == 'X') {
1955 				seen_X = 1;
1956 
1957 				if (pp->p_perm != 0) {
1958 					ushort_t op;
1959 
1960 					/*
1961 					 * Remember the 'who' for the previous
1962 					 * transformation.
1963 					 */
1964 					pp->p_who = who;
1965 					pp->p_special = 0;
1966 
1967 					op = pp->p_op;
1968 
1969 					/* Keep 'X' separate */
1970 					++pp;
1971 					pp->p_special = 'X';
1972 					pp->p_op = op;
1973 				}
1974 			} else if (seen_X) {
1975 				ushort_t op;
1976 
1977 				/* Remember the 'who' for the X */
1978 				pp->p_who = who;
1979 
1980 				op = pp->p_op;
1981 
1982 				/* Keep 'X' separate */
1983 				++pp;
1984 				pp->p_perm = 0;
1985 				pp->p_special = 0;
1986 				pp->p_op = op;
1987 			}
1988 			++amode;
1989 			pp->p_perm |= t;
1990 		}
1991 
1992 		/*
1993 		 * These returned 0, but were actually parsed, so
1994 		 * don't look at them again.
1995 		 */
1996 		switch (pp->p_special) {
1997 		case 'u':
1998 		case 'g':
1999 		case 'o':
2000 			++amode;
2001 			break;
2002 		}
2003 		pp->p_who = who;
2004 		switch (*amode) {
2005 		case '\0':
2006 			break;
2007 
2008 		case ',':
2009 			++amode;
2010 			++pp;
2011 			continue;
2012 
2013 		default:
2014 			++pp;
2015 			goto samewho;
2016 		}
2017 		break;
2018 	}
2019 	endp = pp;
2020 	return (NULL);
2021 }
2022 
2023 /*
2024  * Given a character from the mode, return the associated
2025  * value as who (user designation) mask or 0 if this isn't valid.
2026  */
2027 static int
2028 iswho(c)
2029 int c;
2030 {
2031 	switch (c) {
2032 	case 'a':
2033 		return (P_A);
2034 
2035 	case 'u':
2036 		return (P_U);
2037 
2038 	case 'g':
2039 		return (P_G);
2040 
2041 	case 'o':
2042 		return (P_O);
2043 
2044 	default:
2045 		return (0);
2046 	}
2047 	/* NOTREACHED */
2048 }
2049 
2050 /*
2051  * Return non-zero if this is a valid op code
2052  * in a symbolic mode.
2053  */
2054 static int
2055 isop(c)
2056 int c;
2057 {
2058 	switch (c) {
2059 	case '+':
2060 	case '-':
2061 	case '=':
2062 		return (1);
2063 
2064 	default:
2065 		return (0);
2066 	}
2067 	/* NOTREACHED */
2068 }
2069 
2070 /*
2071  * Return the permission bits implied by this character or 0
2072  * if it isn't valid.  Also returns 0 when the pseudo-permissions 'u', 'g', or
2073  * 'o' are used, and sets pp->p_special to the one used.
2074  */
2075 static int
2076 isperm(pp, c)
2077 PERMST *pp;
2078 int c;
2079 {
2080 	switch (c) {
2081 	case 'u':
2082 	case 'g':
2083 	case 'o':
2084 		pp->p_special = c;
2085 		return (0);
2086 
2087 	case 'r':
2088 		return (S_IRUSR|S_IRGRP|S_IROTH);
2089 
2090 	case 'w':
2091 		return (S_IWUSR|S_IWGRP|S_IWOTH);
2092 
2093 	case 'x':
2094 		return (S_IXUSR|S_IXGRP|S_IXOTH);
2095 
2096 #if S_ISVTX != 0
2097 	case 't':
2098 		return (S_ISVTX);
2099 #endif
2100 
2101 	case 'X':
2102 		pp->p_special = 'X';
2103 		return (S_IXUSR|S_IXGRP|S_IXOTH);
2104 
2105 #if S_ISVTX != 0
2106 	case 'a':
2107 		return (S_ISVTX);
2108 #endif
2109 
2110 	case 'h':
2111 		return (S_ISUID);
2112 
2113 	/*
2114 	 * This change makes:
2115 	 *	chmod +s file
2116 	 * set the system bit on dos but means that
2117 	 *	chmod u+s file
2118 	 *	chmod g+s file
2119 	 *	chmod a+s file
2120 	 * are all like UNIX.
2121 	 */
2122 	case 's':
2123 		return (nowho ? S_ISGID : S_ISGID|S_ISUID);
2124 
2125 	default:
2126 		return (0);
2127 	}
2128 	/* NOTREACHED */
2129 }
2130 
2131 /*
2132  * Execute the automaton that is created by readmode()
2133  * to generate the final mode that will be used.  This
2134  * code is passed a starting mode that is usually the original
2135  * mode of the file being changed (or 0).  Note that this mode must contain
2136  * the file-type bits as well, so that S_ISDIR will succeed on directories.
2137  */
2138 static mode_t
2139 getmode(mode_t startmode)
2140 {
2141 	PERMST *pp;
2142 	mode_t temp;
2143 	mode_t perm;
2144 
2145 	for (pp = &machine[0]; pp <= endp; ++pp) {
2146 		perm = (mode_t)0;
2147 		/*
2148 		 * For the special modes 'u', 'g' and 'o', the named portion
2149 		 * of the mode refers to after the previous clause has been
2150 		 * processed, while the 'X' mode refers to the contents of the
2151 		 * mode before any clauses have been processed.
2152 		 *
2153 		 * References: P1003.2/D11.2, Section 4.7.7,
2154 		 *  lines 2568-2570, 2578-2583
2155 		 */
2156 		switch (pp->p_special) {
2157 		case 'u':
2158 			temp = startmode & S_IRWXU;
2159 			if (temp & (S_IRUSR|S_IRGRP|S_IROTH))
2160 				perm |= ((S_IRUSR|S_IRGRP|S_IROTH) &
2161 				    pp->p_who);
2162 			if (temp & (S_IWUSR|S_IWGRP|S_IWOTH))
2163 				perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who);
2164 			if (temp & (S_IXUSR|S_IXGRP|S_IXOTH))
2165 				perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who);
2166 			break;
2167 
2168 		case 'g':
2169 			temp = startmode & S_IRWXG;
2170 			if (temp & (S_IRUSR|S_IRGRP|S_IROTH))
2171 				perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & pp->p_who);
2172 			if (temp & (S_IWUSR|S_IWGRP|S_IWOTH))
2173 				perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who);
2174 			if (temp & (S_IXUSR|S_IXGRP|S_IXOTH))
2175 				perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who);
2176 			break;
2177 
2178 		case 'o':
2179 			temp = startmode & S_IRWXO;
2180 			if (temp & (S_IRUSR|S_IRGRP|S_IROTH))
2181 				perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & pp->p_who);
2182 			if (temp & (S_IWUSR|S_IWGRP|S_IWOTH))
2183 				perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who);
2184 			if (temp & (S_IXUSR|S_IXGRP|S_IXOTH))
2185 				perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who);
2186 			break;
2187 
2188 		case 'X':
2189 			perm = pp->p_perm;
2190 			break;
2191 
2192 		default:
2193 			perm = pp->p_perm;
2194 			break;
2195 		}
2196 		switch (pp->p_op) {
2197 		case '-':
2198 			startmode &= ~(perm & pp->p_who);
2199 			break;
2200 
2201 		case '=':
2202 			startmode &= ~pp->p_who;
2203 			/* FALLTHROUGH */
2204 		case '+':
2205 			startmode |= (perm & pp->p_who);
2206 			break;
2207 		}
2208 	}
2209 	return (startmode);
2210 }
2211 
2212 /*
2213  * Returns the last component of a path name, unless it is
2214  * an absolute path, in which case it returns the whole path
2215  */
2216 static char
2217 *gettail(char *fname)
2218 {
2219 	char	*base = fname;
2220 
2221 	if (*fname != '/') {
2222 		if ((base = strrchr(fname, '/')) != NULL)
2223 			base++;
2224 		else
2225 			base = fname;
2226 	}
2227 	return (base);
2228 }
2229