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