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