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