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