xref: /illumos-gate/usr/src/cmd/find/find.c (revision f3a525d9)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5d35170d6Srm  * Common Development and Distribution License (the "License").
6d35170d6Srm  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22ef497ae3SRich Burridge  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
2303f45afcSYuri Pankov  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
2405f32410SAndy Stormont  * Copyright (c) 2013 Andrew Stormont.  All rights reserved.
25*f3a525d9SJohn Levon  * Copyright 2020 Joyent, Inc.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
28da1a9cbeSjonb 
297c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
3027d3a169SToomas Soome /*	  All Rights Reserved	*/
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate /*	Parts of this product may be derived from		*/
347c478bd9Sstevel@tonic-gate /*	Mortice Kern Systems Inc. and Berkeley 4.3 BSD systems.	*/
3527d3a169SToomas Soome /*	licensed from  Mortice Kern Systems Inc. and		*/
367c478bd9Sstevel@tonic-gate /*	the University of California.				*/
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate /*
397c478bd9Sstevel@tonic-gate  * Copyright 1985, 1990 by Mortice Kern Systems Inc.  All rights reserved.
407c478bd9Sstevel@tonic-gate  */
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate #include <stdio.h>
437c478bd9Sstevel@tonic-gate #include <errno.h>
447c478bd9Sstevel@tonic-gate #include <pwd.h>
457c478bd9Sstevel@tonic-gate #include <grp.h>
467c478bd9Sstevel@tonic-gate #include <sys/types.h>
477c478bd9Sstevel@tonic-gate #include <sys/stat.h>
487c478bd9Sstevel@tonic-gate #include <sys/param.h>
497c478bd9Sstevel@tonic-gate #include <sys/acl.h>
507c478bd9Sstevel@tonic-gate #include <limits.h>
517c478bd9Sstevel@tonic-gate #include <unistd.h>
527c478bd9Sstevel@tonic-gate #include <stdlib.h>
537c478bd9Sstevel@tonic-gate #include <locale.h>
547c478bd9Sstevel@tonic-gate #include <string.h>
557c478bd9Sstevel@tonic-gate #include <strings.h>
567c478bd9Sstevel@tonic-gate #include <ctype.h>
577c478bd9Sstevel@tonic-gate #include <wait.h>
587c478bd9Sstevel@tonic-gate #include <fnmatch.h>
597c478bd9Sstevel@tonic-gate #include <langinfo.h>
607c478bd9Sstevel@tonic-gate #include <ftw.h>
619ab6dc39Schin #include <libgen.h>
62b34cd89aSYuri Pankov #include <err.h>
63b34cd89aSYuri Pankov #include <regex.h>
643d63ea05Sas #include "getresponse.h"
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate #define	A_DAY		(long)(60*60*24)	/* a day full of seconds */
67da1a9cbeSjonb #define	A_MIN		(long)(60)
687c478bd9Sstevel@tonic-gate #define	BLKSIZ		512
697c478bd9Sstevel@tonic-gate #define	round(x, s)	(((x)+(s)-1)&~((s)-1))
707c478bd9Sstevel@tonic-gate #ifndef FTW_SLN
717c478bd9Sstevel@tonic-gate #define	FTW_SLN		7
727c478bd9Sstevel@tonic-gate #endif
737c478bd9Sstevel@tonic-gate #define	LINEBUF_SIZE		LINE_MAX	/* input or output lines */
747c478bd9Sstevel@tonic-gate #define	REMOTE_FS		"/etc/dfs/fstypes"
757c478bd9Sstevel@tonic-gate #define	N_FSTYPES		20
76d35170d6Srm #define	SHELL_MAXARGS		253	/* see doexec() for description */
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate /*
797c478bd9Sstevel@tonic-gate  * This is the list of operations
807c478bd9Sstevel@tonic-gate  * F_USER and F_GROUP are named to avoid conflict with USER and GROUP defined
817c478bd9Sstevel@tonic-gate  * in sys/acl.h
827c478bd9Sstevel@tonic-gate  */
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate enum Command
857c478bd9Sstevel@tonic-gate {
86b34cd89aSYuri Pankov 	PRINT,
87b34cd89aSYuri Pankov 	ACL, AMIN, AND, ATIME, CMIN, CPIO, CSIZE, CTIME, DEPTH, EXEC, F_GROUP,
8805f32410SAndy Stormont 	F_GROUPACL, F_USER, F_USERACL, FOLLOW, FSTYPE, INAME, INUM, IPATH,
8905f32410SAndy Stormont 	IREGEX,	LINKS, LOCAL, LPAREN, LS, MAXDEPTH, MINDEPTH, MMIN, MOUNT,
9005f32410SAndy Stormont 	MTIME, NAME, NCPIO, NEWER, NOGRP, NOT, NOUSER, OK, OR, PATH, PERM,
91ab823b7fSPrasad Joshi 	PRINT0, PRUNE, REGEX, RPAREN, SIZE, TYPE, VARARGS, XATTR, DELETE
927c478bd9Sstevel@tonic-gate };
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate enum Type
957c478bd9Sstevel@tonic-gate {
967c478bd9Sstevel@tonic-gate 	Unary, Id, Num, Str, Exec, Cpio, Op
977c478bd9Sstevel@tonic-gate };
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate struct Args
1007c478bd9Sstevel@tonic-gate {
1017c478bd9Sstevel@tonic-gate 	char		name[10];
1027c478bd9Sstevel@tonic-gate 	enum Command	action;
1037c478bd9Sstevel@tonic-gate 	enum Type	type;
1047c478bd9Sstevel@tonic-gate };
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate /*
1077c478bd9Sstevel@tonic-gate  * Except for pathnames, these are the only legal arguments
1087c478bd9Sstevel@tonic-gate  */
1097c478bd9Sstevel@tonic-gate static struct Args commands[] =
1107c478bd9Sstevel@tonic-gate {
111b34cd89aSYuri Pankov 	"!",		NOT,		Op,
112b34cd89aSYuri Pankov 	"(",		LPAREN,		Unary,
113b34cd89aSYuri Pankov 	")",		RPAREN,		Unary,
114b34cd89aSYuri Pankov 	"-a",		AND,		Op,
115b34cd89aSYuri Pankov 	"-acl",		ACL,		Unary,
116b34cd89aSYuri Pankov 	"-amin",	AMIN,		Num,
117b34cd89aSYuri Pankov 	"-and",		AND,		Op,
118b34cd89aSYuri Pankov 	"-atime",	ATIME,		Num,
119b34cd89aSYuri Pankov 	"-cmin",	CMIN,		Num,
120b34cd89aSYuri Pankov 	"-cpio",	CPIO,		Cpio,
121b34cd89aSYuri Pankov 	"-ctime",	CTIME,		Num,
122b34cd89aSYuri Pankov 	"-depth",	DEPTH,		Unary,
123ab823b7fSPrasad Joshi 	"-delete",	DELETE,		Unary,
124b34cd89aSYuri Pankov 	"-exec",	EXEC,		Exec,
125b34cd89aSYuri Pankov 	"-follow",	FOLLOW,		Unary,
126b34cd89aSYuri Pankov 	"-fstype",	FSTYPE,		Str,
1277c478bd9Sstevel@tonic-gate 	"-group",	F_GROUP,	Num,
128b34cd89aSYuri Pankov 	"-groupacl",	F_GROUPACL,	Num,
129b34cd89aSYuri Pankov 	"-iname",	INAME,		Str,
130b34cd89aSYuri Pankov 	"-inum",	INUM,		Num,
13105f32410SAndy Stormont 	"-ipath",	IPATH,		Str,
132b34cd89aSYuri Pankov 	"-iregex",	IREGEX,		Str,
13303f45afcSYuri Pankov 	"-links",	LINKS,		Num,
13403f45afcSYuri Pankov 	"-local",	LOCAL,		Unary,
135b34cd89aSYuri Pankov 	"-ls",		LS,		Unary,
136b34cd89aSYuri Pankov 	"-maxdepth",	MAXDEPTH,	Num,
137b34cd89aSYuri Pankov 	"-mindepth",	MINDEPTH,	Num,
138b34cd89aSYuri Pankov 	"-mmin",	MMIN,		Num,
139b34cd89aSYuri Pankov 	"-mount",	MOUNT,		Unary,
140b34cd89aSYuri Pankov 	"-mtime",	MTIME,		Num,
141b34cd89aSYuri Pankov 	"-name",	NAME,		Str,
142b34cd89aSYuri Pankov 	"-ncpio",	NCPIO,		Cpio,
143b34cd89aSYuri Pankov 	"-newer",	NEWER,		Str,
144b34cd89aSYuri Pankov 	"-nogroup",	NOGRP,		Unary,
145b34cd89aSYuri Pankov 	"-not",		NOT,		Op,
146b34cd89aSYuri Pankov 	"-nouser",	NOUSER,		Unary,
147b34cd89aSYuri Pankov 	"-o",		OR,		Op,
148b34cd89aSYuri Pankov 	"-ok",		OK,		Exec,
149b34cd89aSYuri Pankov 	"-or",		OR,		Op,
15005f32410SAndy Stormont 	"-path",	PATH,		Str,
151b34cd89aSYuri Pankov 	"-perm",	PERM,		Num,
152b34cd89aSYuri Pankov 	"-print",	PRINT,		Unary,
153b34cd89aSYuri Pankov 	"-print0",	PRINT0,		Unary,
154b34cd89aSYuri Pankov 	"-prune",	PRUNE,		Unary,
155b34cd89aSYuri Pankov 	"-regex",	REGEX,		Str,
156b34cd89aSYuri Pankov 	"-size",	SIZE,		Num,
157b34cd89aSYuri Pankov 	"-type",	TYPE,		Num,
158b34cd89aSYuri Pankov 	"-user",	F_USER,		Num,
159b34cd89aSYuri Pankov 	"-useracl",	F_USERACL,	Num,
160b34cd89aSYuri Pankov 	"-xattr",	XATTR,		Unary,
161b34cd89aSYuri Pankov 	"-xdev",	MOUNT,		Unary,
16227d3a169SToomas Soome 	0,		0,		0
1637c478bd9Sstevel@tonic-gate };
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate union Item
1667c478bd9Sstevel@tonic-gate {
1677c478bd9Sstevel@tonic-gate 	struct Node	*np;
1687c478bd9Sstevel@tonic-gate 	struct Arglist	*vp;
1697c478bd9Sstevel@tonic-gate 	time_t		t;
1707c478bd9Sstevel@tonic-gate 	char		*cp;
1717c478bd9Sstevel@tonic-gate 	char		**ap;
1727c478bd9Sstevel@tonic-gate 	long		l;
1737c478bd9Sstevel@tonic-gate 	int		i;
1747c478bd9Sstevel@tonic-gate 	long long	ll;
1757c478bd9Sstevel@tonic-gate };
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate struct Node
1787c478bd9Sstevel@tonic-gate {
1797c478bd9Sstevel@tonic-gate 	struct Node	*next;
1807c478bd9Sstevel@tonic-gate 	enum Command	action;
1817c478bd9Sstevel@tonic-gate 	enum Type	type;
1827c478bd9Sstevel@tonic-gate 	union Item	first;
1837c478bd9Sstevel@tonic-gate 	union Item	second;
1847c478bd9Sstevel@tonic-gate };
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate /* if no -print, -exec or -ok replace "expression" with "(expression) -print" */
1877c478bd9Sstevel@tonic-gate static	struct	Node PRINT_NODE = { 0, PRINT, 0, 0};
1887c478bd9Sstevel@tonic-gate static	struct	Node LPAREN_NODE = { 0, LPAREN, 0, 0};
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate /*
1927c478bd9Sstevel@tonic-gate  * Prototype variable size arglist buffer
1937c478bd9Sstevel@tonic-gate  */
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate struct Arglist
1967c478bd9Sstevel@tonic-gate {
1977c478bd9Sstevel@tonic-gate 	struct Arglist	*next;
1987c478bd9Sstevel@tonic-gate 	char		*end;
1997c478bd9Sstevel@tonic-gate 	char		*nextstr;
2007c478bd9Sstevel@tonic-gate 	char		**firstvar;
2017c478bd9Sstevel@tonic-gate 	char		**nextvar;
2027c478bd9Sstevel@tonic-gate 	char		*arglist[1];
2037c478bd9Sstevel@tonic-gate };
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 
20627d3a169SToomas Soome static int		compile(char **, struct Node *, int *);
20727d3a169SToomas Soome static int		execute(const char *, const struct stat *, int,
20827d3a169SToomas Soome     struct FTW *);
20927d3a169SToomas Soome static int		doexec(const char *, char **, int *);
21027d3a169SToomas Soome static int		dodelete(const char *, const struct stat *,
21127d3a169SToomas Soome     struct FTW *);
21227d3a169SToomas Soome static struct Args	*lookup(char *);
21327d3a169SToomas Soome static int		ok(const char *, char *[]);
2146c83d09fSrobbin static void		usage(void)	__NORETURN;
21527d3a169SToomas Soome static struct Arglist	*varargs(char **);
21627d3a169SToomas Soome static int		list(const char *, const struct stat *);
21727d3a169SToomas Soome static char		*getgroup(gid_t);
21827d3a169SToomas Soome static FILE		*cmdopen(char *, char **, char *, FILE *);
21927d3a169SToomas Soome static int		cmdclose(FILE *);
22027d3a169SToomas Soome static char		*getshell(void);
22127d3a169SToomas Soome static void		init_remote_fs(void);
22227d3a169SToomas Soome static char		*getname(uid_t);
22327d3a169SToomas Soome static int		readmode(const char *);
22427d3a169SToomas Soome static mode_t		getmode(mode_t);
22527d3a169SToomas Soome static const char	*gettail(const char *);
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 
22868a94df1Scf static int walkflags = FTW_CHDIR|FTW_PHYS|FTW_ANYERR|FTW_NOLOOP;
2297c478bd9Sstevel@tonic-gate static struct Node	*topnode;
2307c478bd9Sstevel@tonic-gate static struct Node	*freenode;	/* next free node we may use later */
2317c478bd9Sstevel@tonic-gate static char		*cpio[] = { "cpio", "-o", 0 };
2327c478bd9Sstevel@tonic-gate static char		*ncpio[] = { "cpio", "-oc", 0 };
2337c478bd9Sstevel@tonic-gate static char		*cpiol[] = { "cpio", "-oL", 0 };
2347c478bd9Sstevel@tonic-gate static char		*ncpiol[] = { "cpio", "-ocL", 0 };
2357c478bd9Sstevel@tonic-gate static time_t		now;
2367c478bd9Sstevel@tonic-gate static FILE		*output;
2377c478bd9Sstevel@tonic-gate static char		*dummyarg = (char *)-1;
2387c478bd9Sstevel@tonic-gate static int		lastval;
2397c478bd9Sstevel@tonic-gate static int		varsize;
2407c478bd9Sstevel@tonic-gate static struct Arglist	*lastlist;
2417c478bd9Sstevel@tonic-gate static char		*cmdname;
2427c478bd9Sstevel@tonic-gate static char		*remote_fstypes[N_FSTYPES+1];
2437c478bd9Sstevel@tonic-gate static int		fstype_index = 0;
2447c478bd9Sstevel@tonic-gate static int		action_expression = 0;	/* -print, -exec, or -ok */
2457c478bd9Sstevel@tonic-gate static int		error = 0;
2467c478bd9Sstevel@tonic-gate static int		paren_cnt = 0;	/* keeps track of parentheses */
247b34cd89aSYuri Pankov static int		Eflag = 0;
2487c478bd9Sstevel@tonic-gate static int		hflag = 0;
2497c478bd9Sstevel@tonic-gate static int		lflag = 0;
250d35170d6Srm /* set when doexec()-invoked utility returns non-zero */
251d35170d6Srm static int		exec_exitcode = 0;
252b34cd89aSYuri Pankov static regex_t		*preg = NULL;
253b34cd89aSYuri Pankov static int		npreg = 0;
254b34cd89aSYuri Pankov static int		mindepth = -1, maxdepth = -1;
2557c478bd9Sstevel@tonic-gate extern char		**environ;
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate int
2587c478bd9Sstevel@tonic-gate main(int argc, char **argv)
2597c478bd9Sstevel@tonic-gate {
2607c478bd9Sstevel@tonic-gate 	char *cp;
2617c478bd9Sstevel@tonic-gate 	int c;
2627c478bd9Sstevel@tonic-gate 	int paths;
2637c478bd9Sstevel@tonic-gate 	char *cwdpath;
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
2667c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
2677c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
2687c478bd9Sstevel@tonic-gate #endif
2697c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	cmdname = argv[0];
2727c478bd9Sstevel@tonic-gate 	if (time(&now) == (time_t)(-1)) {
2737c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: time() %s\n"),
2743d63ea05Sas 		    cmdname, strerror(errno));
2757c478bd9Sstevel@tonic-gate 		exit(1);
2767c478bd9Sstevel@tonic-gate 	}
277b34cd89aSYuri Pankov 	while ((c = getopt(argc, argv, "EHL")) != -1) {
2787c478bd9Sstevel@tonic-gate 		switch (c) {
279b34cd89aSYuri Pankov 		case 'E':
280b34cd89aSYuri Pankov 			Eflag = 1;
281b34cd89aSYuri Pankov 			break;
2827c478bd9Sstevel@tonic-gate 		case 'H':
2837c478bd9Sstevel@tonic-gate 			hflag = 1;
2847c478bd9Sstevel@tonic-gate 			lflag = 0;
2857c478bd9Sstevel@tonic-gate 			break;
2867c478bd9Sstevel@tonic-gate 		case 'L':
2877c478bd9Sstevel@tonic-gate 			hflag = 0;
2887c478bd9Sstevel@tonic-gate 			lflag = 1;
2897c478bd9Sstevel@tonic-gate 			break;
2907c478bd9Sstevel@tonic-gate 		case '?':
2917c478bd9Sstevel@tonic-gate 			usage();
2927c478bd9Sstevel@tonic-gate 			break;
2937c478bd9Sstevel@tonic-gate 		}
2947c478bd9Sstevel@tonic-gate 	}
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 	argc -= optind;
2977c478bd9Sstevel@tonic-gate 	argv += optind;
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 	if (argc < 1) {
3007c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
3017c478bd9Sstevel@tonic-gate 		    gettext("%s: insufficient number of arguments\n"), cmdname);
3027c478bd9Sstevel@tonic-gate 		usage();
3037c478bd9Sstevel@tonic-gate 	}
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	for (paths = 0; (cp = argv[paths]) != 0; ++paths) {
3067c478bd9Sstevel@tonic-gate 		if (*cp == '-')
3077c478bd9Sstevel@tonic-gate 			break;
3087c478bd9Sstevel@tonic-gate 		else if ((*cp == '!' || *cp == '(') && *(cp+1) == 0)
3097c478bd9Sstevel@tonic-gate 			break;
3107c478bd9Sstevel@tonic-gate 	}
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 	if (paths == 0) /* no path-list */
3137c478bd9Sstevel@tonic-gate 		usage();
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 	output = stdout;
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	/* lflag is the same as -follow */
3187c478bd9Sstevel@tonic-gate 	if (lflag)
3197c478bd9Sstevel@tonic-gate 		walkflags &= ~FTW_PHYS;
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 	/* allocate enough space for the compiler */
3227c478bd9Sstevel@tonic-gate 	topnode = malloc((argc + 1) * sizeof (struct Node));
3237c478bd9Sstevel@tonic-gate 	(void) memset(topnode, 0, (argc + 1) * sizeof (struct Node));
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 	if (compile(argv + paths, topnode, &action_expression) == 0) {
3267c478bd9Sstevel@tonic-gate 		/* no expression, default to -print */
3277c478bd9Sstevel@tonic-gate 		(void) memcpy(topnode, &PRINT_NODE, sizeof (struct Node));
3287c478bd9Sstevel@tonic-gate 	} else if (!action_expression) {
3297c478bd9Sstevel@tonic-gate 		/*
3307c478bd9Sstevel@tonic-gate 		 * if no action expression, insert an LPAREN node above topnode,
3317c478bd9Sstevel@tonic-gate 		 * with a PRINT node as its next node
3327c478bd9Sstevel@tonic-gate 		 */
3337c478bd9Sstevel@tonic-gate 		struct Node *savenode;
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 		if (freenode == NULL) {
3367c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("%s: can't append -print"
3373d63ea05Sas 			    " implicitly; try explicit -print option\n"),
3383d63ea05Sas 			    cmdname);
3397c478bd9Sstevel@tonic-gate 			exit(1);
3407c478bd9Sstevel@tonic-gate 		}
3417c478bd9Sstevel@tonic-gate 		savenode = topnode;
3427c478bd9Sstevel@tonic-gate 		topnode = freenode++;
3437c478bd9Sstevel@tonic-gate 		(void) memcpy(topnode, &LPAREN_NODE, sizeof (struct Node));
3447c478bd9Sstevel@tonic-gate 		topnode->next = freenode;
3457c478bd9Sstevel@tonic-gate 		topnode->first.np = savenode;
3467c478bd9Sstevel@tonic-gate 		(void) memcpy(topnode->next, &PRINT_NODE, sizeof (struct Node));
3477c478bd9Sstevel@tonic-gate 	}
3487c478bd9Sstevel@tonic-gate 
3497c478bd9Sstevel@tonic-gate 	while (paths--) {
3507c478bd9Sstevel@tonic-gate 		char *curpath;
3517c478bd9Sstevel@tonic-gate 		struct stat sb;
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 		curpath = *(argv++);
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate 		/*
3567c478bd9Sstevel@tonic-gate 		 * If -H is specified, it means we walk the first
3577c478bd9Sstevel@tonic-gate 		 * level (pathname on command line) logically, following
3587c478bd9Sstevel@tonic-gate 		 * symlinks, but lower levels are walked physically.
3597c478bd9Sstevel@tonic-gate 		 * We use our own secret interface to nftw() to change
3607c478bd9Sstevel@tonic-gate 		 * the from stat to lstat after the top level is walked.
3617c478bd9Sstevel@tonic-gate 		 */
3627c478bd9Sstevel@tonic-gate 		if (hflag) {
3637c478bd9Sstevel@tonic-gate 			if (stat(curpath, &sb) < 0 && errno == ENOENT)
3647c478bd9Sstevel@tonic-gate 				walkflags &= ~FTW_HOPTION;
3657c478bd9Sstevel@tonic-gate 			else
3667c478bd9Sstevel@tonic-gate 				walkflags |= FTW_HOPTION;
3677c478bd9Sstevel@tonic-gate 		}
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 		/*
3707c478bd9Sstevel@tonic-gate 		 * We need this check as nftw needs a CWD and we have no
3717c478bd9Sstevel@tonic-gate 		 * way of returning back from that code with a meaningful
3727c478bd9Sstevel@tonic-gate 		 * error related to this
3737c478bd9Sstevel@tonic-gate 		 */
3747c478bd9Sstevel@tonic-gate 		if ((cwdpath = getcwd(NULL, PATH_MAX)) == NULL) {
3754b808d43SRich Burridge 			if ((errno == EACCES) && (walkflags & FTW_CHDIR)) {
3764b808d43SRich Burridge 				/*
3774b808d43SRich Burridge 				 * A directory above cwd is inaccessible,
3784b808d43SRich Burridge 				 * so don't do chdir(2)s. Slower, but at least
3794b808d43SRich Burridge 				 * it works.
3804b808d43SRich Burridge 				 */
3814b808d43SRich Burridge 				walkflags &= ~FTW_CHDIR;
3824b808d43SRich Burridge 				free(cwdpath);
3834b808d43SRich Burridge 			} else {
3844b808d43SRich Burridge 				(void) fprintf(stderr,
3854b808d43SRich Burridge 				    gettext("%s : cannot get the current "
3864b808d43SRich Burridge 				    "working directory\n"), cmdname);
3874b808d43SRich Burridge 				exit(1);
3884b808d43SRich Burridge 			}
3897c478bd9Sstevel@tonic-gate 		} else
3907c478bd9Sstevel@tonic-gate 			free(cwdpath);
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 		if (nftw(curpath, execute, 1000, walkflags)) {
3947c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
3957c478bd9Sstevel@tonic-gate 			    gettext("%s: cannot open %s: %s\n"),
3967c478bd9Sstevel@tonic-gate 			    cmdname, curpath, strerror(errno));
3977c478bd9Sstevel@tonic-gate 			error = 1;
3987c478bd9Sstevel@tonic-gate 		}
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate 	}
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 	/* execute any remaining variable length lists */
4037c478bd9Sstevel@tonic-gate 	while (lastlist) {
4047c478bd9Sstevel@tonic-gate 		if (lastlist->end != lastlist->nextstr) {
4057c478bd9Sstevel@tonic-gate 			*lastlist->nextvar = 0;
40627d3a169SToomas Soome 			(void) doexec(NULL, lastlist->arglist,
407d35170d6Srm 			    &exec_exitcode);
4087c478bd9Sstevel@tonic-gate 		}
4097c478bd9Sstevel@tonic-gate 		lastlist = lastlist->next;
4107c478bd9Sstevel@tonic-gate 	}
4117c478bd9Sstevel@tonic-gate 	if (output != stdout)
4127c478bd9Sstevel@tonic-gate 		return (cmdclose(output));
413d35170d6Srm 	return ((exec_exitcode != 0) ? exec_exitcode : error);
4147c478bd9Sstevel@tonic-gate }
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate /*
4177c478bd9Sstevel@tonic-gate  * compile the arguments
4187c478bd9Sstevel@tonic-gate  */
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate static int
42127d3a169SToomas Soome compile(char **argv, struct Node *np, int *actionp)
4227c478bd9Sstevel@tonic-gate {
4237c478bd9Sstevel@tonic-gate 	char *b;
4247c478bd9Sstevel@tonic-gate 	char **av;
4257c478bd9Sstevel@tonic-gate 	struct Node *oldnp = topnode;
4267c478bd9Sstevel@tonic-gate 	struct Args *argp;
4277c478bd9Sstevel@tonic-gate 	char **com;
4287c478bd9Sstevel@tonic-gate 	int i;
4297c478bd9Sstevel@tonic-gate 	enum Command wasop = PRINT;
4307c478bd9Sstevel@tonic-gate 
4313d63ea05Sas 	if (init_yes() < 0) {
4323d63ea05Sas 		(void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
4333d63ea05Sas 		    strerror(errno));
4343d63ea05Sas 		exit(1);
4353d63ea05Sas 	}
4363d63ea05Sas 
4377c478bd9Sstevel@tonic-gate 	for (av = argv; *av && (argp = lookup(*av)); av++) {
4387c478bd9Sstevel@tonic-gate 		np->next = 0;
4397c478bd9Sstevel@tonic-gate 		np->action = argp->action;
4407c478bd9Sstevel@tonic-gate 		np->type = argp->type;
4417c478bd9Sstevel@tonic-gate 		np->second.i = 0;
4427c478bd9Sstevel@tonic-gate 		if (argp->type == Op) {
4437c478bd9Sstevel@tonic-gate 			if (wasop == NOT || (wasop && np->action != NOT)) {
4447c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
44527d3a169SToomas Soome 				    gettext("%s: operand follows operand\n"),
44627d3a169SToomas Soome 				    cmdname);
4477c478bd9Sstevel@tonic-gate 				exit(1);
4487c478bd9Sstevel@tonic-gate 			}
4497c478bd9Sstevel@tonic-gate 			if (np->action != NOT && oldnp == 0)
4507c478bd9Sstevel@tonic-gate 				goto err;
4517c478bd9Sstevel@tonic-gate 			wasop = argp->action;
4527c478bd9Sstevel@tonic-gate 		} else {
4537c478bd9Sstevel@tonic-gate 			wasop = PRINT;
4547c478bd9Sstevel@tonic-gate 			if (argp->type != Unary) {
4557c478bd9Sstevel@tonic-gate 				if (!(b = *++av)) {
45627d3a169SToomas Soome 					(void) fprintf(stderr, gettext(
45727d3a169SToomas Soome 					    "%s: incomplete statement\n"),
45827d3a169SToomas Soome 					    cmdname);
4597c478bd9Sstevel@tonic-gate 					exit(1);
4607c478bd9Sstevel@tonic-gate 				}
4617c478bd9Sstevel@tonic-gate 				if (argp->type == Num) {
462b34cd89aSYuri Pankov 					if (((argp->action == MAXDEPTH) ||
463b34cd89aSYuri Pankov 					    (argp->action == MINDEPTH)) &&
464b34cd89aSYuri Pankov 					    ((int)strtol(b, (char **)NULL,
465b34cd89aSYuri Pankov 					    10) < 0))
46627d3a169SToomas Soome 						errx(1, gettext(
46727d3a169SToomas Soome 						    "%s: value must be "
46827d3a169SToomas Soome 						    "positive"),
469b34cd89aSYuri Pankov 						    (argp->action == MAXDEPTH) ?
470b34cd89aSYuri Pankov 						    "maxdepth" : "mindepth");
4717c478bd9Sstevel@tonic-gate 					if ((argp->action != PERM) ||
4727c478bd9Sstevel@tonic-gate 					    (*b != '+')) {
4737c478bd9Sstevel@tonic-gate 						if (*b == '+' || *b == '-') {
4747c478bd9Sstevel@tonic-gate 							np->second.i = *b;
4757c478bd9Sstevel@tonic-gate 							b++;
4767c478bd9Sstevel@tonic-gate 						}
4777c478bd9Sstevel@tonic-gate 					}
4787c478bd9Sstevel@tonic-gate 				}
4797c478bd9Sstevel@tonic-gate 			}
4807c478bd9Sstevel@tonic-gate 		}
4817c478bd9Sstevel@tonic-gate 		switch (argp->action) {
4827c478bd9Sstevel@tonic-gate 		case AND:
4837c478bd9Sstevel@tonic-gate 			break;
4847c478bd9Sstevel@tonic-gate 		case NOT:
4857c478bd9Sstevel@tonic-gate 			break;
4867c478bd9Sstevel@tonic-gate 		case OR:
4877c478bd9Sstevel@tonic-gate 			np->first.np = topnode;
4887c478bd9Sstevel@tonic-gate 			topnode = np;
4897c478bd9Sstevel@tonic-gate 			oldnp->next = 0;
4907c478bd9Sstevel@tonic-gate 			break;
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 		case LPAREN: {
4937c478bd9Sstevel@tonic-gate 			struct Node *save = topnode;
4947c478bd9Sstevel@tonic-gate 			topnode = np+1;
4957c478bd9Sstevel@tonic-gate 			paren_cnt++;
4967c478bd9Sstevel@tonic-gate 			i = compile(++av, topnode, actionp);
4977c478bd9Sstevel@tonic-gate 			np->first.np = topnode;
4987c478bd9Sstevel@tonic-gate 			topnode = save;
4997c478bd9Sstevel@tonic-gate 			av += i;
5007c478bd9Sstevel@tonic-gate 			oldnp = np;
5017c478bd9Sstevel@tonic-gate 			np += i + 1;
5027c478bd9Sstevel@tonic-gate 			oldnp->next = np;
5037c478bd9Sstevel@tonic-gate 			continue;
5047c478bd9Sstevel@tonic-gate 		}
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate 		case RPAREN:
5077c478bd9Sstevel@tonic-gate 			if (paren_cnt <= 0) {
5087c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
5097c478bd9Sstevel@tonic-gate 				    gettext("%s: unmatched ')'\n"),
5107c478bd9Sstevel@tonic-gate 				    cmdname);
5117c478bd9Sstevel@tonic-gate 				exit(1);
5127c478bd9Sstevel@tonic-gate 			}
5137c478bd9Sstevel@tonic-gate 			paren_cnt--;
5147c478bd9Sstevel@tonic-gate 			if (oldnp == 0)
5157c478bd9Sstevel@tonic-gate 				goto err;
5167c478bd9Sstevel@tonic-gate 			if (oldnp->type == Op) {
5177c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
5187c478bd9Sstevel@tonic-gate 				    gettext("%s: cannot immediately"
5197c478bd9Sstevel@tonic-gate 				    " follow an operand with ')'\n"),
5207c478bd9Sstevel@tonic-gate 				    cmdname);
5217c478bd9Sstevel@tonic-gate 				exit(1);
5227c478bd9Sstevel@tonic-gate 			}
5237c478bd9Sstevel@tonic-gate 			oldnp->next = 0;
5247c478bd9Sstevel@tonic-gate 			return (av-argv);
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate 		case FOLLOW:
5277c478bd9Sstevel@tonic-gate 			walkflags &= ~FTW_PHYS;
5287c478bd9Sstevel@tonic-gate 			break;
5297c478bd9Sstevel@tonic-gate 		case MOUNT:
5307c478bd9Sstevel@tonic-gate 			walkflags |= FTW_MOUNT;
5317c478bd9Sstevel@tonic-gate 			break;
5327c478bd9Sstevel@tonic-gate 		case DEPTH:
5337c478bd9Sstevel@tonic-gate 			walkflags |= FTW_DEPTH;
5347c478bd9Sstevel@tonic-gate 			break;
535ab823b7fSPrasad Joshi 		case DELETE:
536ab823b7fSPrasad Joshi 			walkflags |= (FTW_DEPTH | FTW_PHYS);
537ab823b7fSPrasad Joshi 			walkflags &= ~FTW_CHDIR;
538ab823b7fSPrasad Joshi 			(*actionp)++;
539ab823b7fSPrasad Joshi 			break;
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate 		case LOCAL:
5427c478bd9Sstevel@tonic-gate 			np->first.l = 0L;
5437c478bd9Sstevel@tonic-gate 			np->first.ll = 0LL;
5447c478bd9Sstevel@tonic-gate 			np->second.i = '+';
5457c478bd9Sstevel@tonic-gate 			/*
5467c478bd9Sstevel@tonic-gate 			 * Make it compatible to df -l for
5477c478bd9Sstevel@tonic-gate 			 * future enhancement. So, anything
5487c478bd9Sstevel@tonic-gate 			 * that is not remote, then it is
5497c478bd9Sstevel@tonic-gate 			 * local.
5507c478bd9Sstevel@tonic-gate 			 */
5517c478bd9Sstevel@tonic-gate 			init_remote_fs();
5527c478bd9Sstevel@tonic-gate 			break;
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 		case SIZE:
5557c478bd9Sstevel@tonic-gate 			if (b[strlen(b)-1] == 'c')
5567c478bd9Sstevel@tonic-gate 				np->action = CSIZE;
5577c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
5587c478bd9Sstevel@tonic-gate 		case INUM:
5597c478bd9Sstevel@tonic-gate 			np->first.ll = atoll(b);
5607c478bd9Sstevel@tonic-gate 			break;
5617c478bd9Sstevel@tonic-gate 
562da1a9cbeSjonb 		case CMIN:
5637c478bd9Sstevel@tonic-gate 		case CTIME:
564da1a9cbeSjonb 		case MMIN:
5657c478bd9Sstevel@tonic-gate 		case MTIME:
566da1a9cbeSjonb 		case AMIN:
5677c478bd9Sstevel@tonic-gate 		case ATIME:
5687c478bd9Sstevel@tonic-gate 		case LINKS:
5697c478bd9Sstevel@tonic-gate 			np->first.l = atol(b);
5707c478bd9Sstevel@tonic-gate 			break;
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 		case F_USER:
573b34cd89aSYuri Pankov 		case F_GROUP:
574b34cd89aSYuri Pankov 		case F_USERACL:
575b34cd89aSYuri Pankov 		case F_GROUPACL: {
5767c478bd9Sstevel@tonic-gate 			struct	passwd	*pw;
5777c478bd9Sstevel@tonic-gate 			struct	group *gr;
578ef497ae3SRich Burridge 			long value;
579ef497ae3SRich Burridge 			char *q;
580ef497ae3SRich Burridge 
581ef497ae3SRich Burridge 			value = -1;
582b34cd89aSYuri Pankov 			if (argp->action == F_USER ||
583b34cd89aSYuri Pankov 			    argp->action == F_USERACL) {
5847c478bd9Sstevel@tonic-gate 				if ((pw = getpwnam(b)) != 0)
585ef497ae3SRich Burridge 					value = (long)pw->pw_uid;
5867c478bd9Sstevel@tonic-gate 			} else {
5877c478bd9Sstevel@tonic-gate 				if ((gr = getgrnam(b)) != 0)
588ef497ae3SRich Burridge 					value = (long)gr->gr_gid;
5897c478bd9Sstevel@tonic-gate 			}
590ef497ae3SRich Burridge 			if (value == -1) {
591ef497ae3SRich Burridge 				errno = 0;
592ef497ae3SRich Burridge 				value = strtol(b, &q, 10);
593ef497ae3SRich Burridge 				if (errno != 0 || q == b || *q != '\0') {
5947c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
5957c478bd9Sstevel@tonic-gate 					    "%s: cannot find %s name\n"),
59627d3a169SToomas Soome 					    cmdname, *av);
5977c478bd9Sstevel@tonic-gate 					exit(1);
5987c478bd9Sstevel@tonic-gate 				}
5997c478bd9Sstevel@tonic-gate 			}
600ef497ae3SRich Burridge 			np->first.l = value;
6017c478bd9Sstevel@tonic-gate 			break;
6027c478bd9Sstevel@tonic-gate 		}
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 		case EXEC:
6057c478bd9Sstevel@tonic-gate 		case OK:
6067c478bd9Sstevel@tonic-gate 			walkflags &= ~FTW_CHDIR;
6077c478bd9Sstevel@tonic-gate 			np->first.ap = av;
6087c478bd9Sstevel@tonic-gate 			(*actionp)++;
6097c478bd9Sstevel@tonic-gate 			for (;;) {
6107c478bd9Sstevel@tonic-gate 				if ((b = *av) == 0) {
61127d3a169SToomas Soome 					(void) fprintf(stderr, gettext(
61227d3a169SToomas Soome 					    "%s: incomplete statement\n"),
61327d3a169SToomas Soome 					    cmdname);
6147c478bd9Sstevel@tonic-gate 					exit(1);
6157c478bd9Sstevel@tonic-gate 				}
6167c478bd9Sstevel@tonic-gate 				if (strcmp(b, ";") == 0) {
6177c478bd9Sstevel@tonic-gate 					*av = 0;
6187c478bd9Sstevel@tonic-gate 					break;
6197c478bd9Sstevel@tonic-gate 				} else if (strcmp(b, "{}") == 0)
6207c478bd9Sstevel@tonic-gate 					*av = dummyarg;
6217c478bd9Sstevel@tonic-gate 				else if (strcmp(b, "+") == 0 &&
62227d3a169SToomas Soome 				    av[-1] == dummyarg && np->action == EXEC) {
6237c478bd9Sstevel@tonic-gate 					av[-1] = 0;
6247c478bd9Sstevel@tonic-gate 					np->first.vp = varargs(np->first.ap);
6257c478bd9Sstevel@tonic-gate 					np->action = VARARGS;
6267c478bd9Sstevel@tonic-gate 					break;
6277c478bd9Sstevel@tonic-gate 				}
6287c478bd9Sstevel@tonic-gate 				av++;
6297c478bd9Sstevel@tonic-gate 			}
6307c478bd9Sstevel@tonic-gate 			break;
6317c478bd9Sstevel@tonic-gate 
6327c478bd9Sstevel@tonic-gate 		case NAME:
633b34cd89aSYuri Pankov 		case INAME:
63405f32410SAndy Stormont 		case PATH:
63505f32410SAndy Stormont 		case IPATH:
6367c478bd9Sstevel@tonic-gate 			np->first.cp = b;
6377c478bd9Sstevel@tonic-gate 			break;
638b34cd89aSYuri Pankov 		case REGEX:
639b34cd89aSYuri Pankov 		case IREGEX: {
640b34cd89aSYuri Pankov 			int error;
641b34cd89aSYuri Pankov 			size_t errlen;
642b34cd89aSYuri Pankov 			char *errmsg;
643b34cd89aSYuri Pankov 
644b34cd89aSYuri Pankov 			if ((preg = realloc(preg, (npreg + 1) *
645b34cd89aSYuri Pankov 			    sizeof (regex_t))) == NULL)
646b34cd89aSYuri Pankov 				err(1, "realloc");
647b34cd89aSYuri Pankov 			if ((error = regcomp(&preg[npreg], b,
648b34cd89aSYuri Pankov 			    ((np->action == IREGEX) ? REG_ICASE : 0) |
649b34cd89aSYuri Pankov 			    ((Eflag) ? REG_EXTENDED : 0))) != 0) {
650b34cd89aSYuri Pankov 				errlen = regerror(error, &preg[npreg], NULL, 0);
651b34cd89aSYuri Pankov 				if ((errmsg = malloc(errlen)) == NULL)
652b34cd89aSYuri Pankov 					err(1, "malloc");
653b34cd89aSYuri Pankov 				(void) regerror(error, &preg[npreg], errmsg,
654b34cd89aSYuri Pankov 				    errlen);
655b34cd89aSYuri Pankov 				errx(1, gettext("RE error: %s"), errmsg);
656b34cd89aSYuri Pankov 			}
657b34cd89aSYuri Pankov 			npreg++;
658b34cd89aSYuri Pankov 			break;
659b34cd89aSYuri Pankov 		}
6607c478bd9Sstevel@tonic-gate 		case PERM:
6617c478bd9Sstevel@tonic-gate 			if (*b == '-')
6627c478bd9Sstevel@tonic-gate 				++b;
6637c478bd9Sstevel@tonic-gate 
66427d3a169SToomas Soome 			if (readmode(b) != 0) {
6657c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(
6667c478bd9Sstevel@tonic-gate 				    "find: -perm: Bad permission string\n"));
6677c478bd9Sstevel@tonic-gate 				usage();
6687c478bd9Sstevel@tonic-gate 			}
6697c478bd9Sstevel@tonic-gate 			np->first.l = (long)getmode((mode_t)0);
6707c478bd9Sstevel@tonic-gate 			break;
6717c478bd9Sstevel@tonic-gate 		case TYPE:
6727c478bd9Sstevel@tonic-gate 			i = *b;
6737c478bd9Sstevel@tonic-gate 			np->first.l =
6747c478bd9Sstevel@tonic-gate 			    i == 'd' ? S_IFDIR :
6757c478bd9Sstevel@tonic-gate 			    i == 'b' ? S_IFBLK :
6767c478bd9Sstevel@tonic-gate 			    i == 'c' ? S_IFCHR :
6777c478bd9Sstevel@tonic-gate #ifdef S_IFIFO
6787c478bd9Sstevel@tonic-gate 			    i == 'p' ? S_IFIFO :
6797c478bd9Sstevel@tonic-gate #endif
6807c478bd9Sstevel@tonic-gate 			    i == 'f' ? S_IFREG :
6817c478bd9Sstevel@tonic-gate #ifdef S_IFLNK
6827c478bd9Sstevel@tonic-gate 			    i == 'l' ? S_IFLNK :
6837c478bd9Sstevel@tonic-gate #endif
6847c478bd9Sstevel@tonic-gate #ifdef S_IFSOCK
6857c478bd9Sstevel@tonic-gate 			    i == 's' ? S_IFSOCK :
6867c478bd9Sstevel@tonic-gate #endif
6877c478bd9Sstevel@tonic-gate #ifdef S_IFDOOR
6887c478bd9Sstevel@tonic-gate 			    i == 'D' ? S_IFDOOR :
6897c478bd9Sstevel@tonic-gate #endif
6907c478bd9Sstevel@tonic-gate 			    0;
6917c478bd9Sstevel@tonic-gate 			break;
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate 		case CPIO:
6947c478bd9Sstevel@tonic-gate 			if (walkflags & FTW_PHYS)
6957c478bd9Sstevel@tonic-gate 				com = cpio;
6967c478bd9Sstevel@tonic-gate 			else
6977c478bd9Sstevel@tonic-gate 				com = cpiol;
6987c478bd9Sstevel@tonic-gate 			goto common;
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 		case NCPIO: {
7017c478bd9Sstevel@tonic-gate 			FILE *fd;
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate 			if (walkflags & FTW_PHYS)
7047c478bd9Sstevel@tonic-gate 				com = ncpio;
7057c478bd9Sstevel@tonic-gate 			else
7067c478bd9Sstevel@tonic-gate 				com = ncpiol;
7077c478bd9Sstevel@tonic-gate 		common:
7087c478bd9Sstevel@tonic-gate 			/* set up cpio */
7097c478bd9Sstevel@tonic-gate 			if ((fd = fopen(b, "w")) == NULL) {
7107c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
71127d3a169SToomas Soome 				    gettext("%s: cannot create %s\n"),
71227d3a169SToomas Soome 				    cmdname, b);
7137c478bd9Sstevel@tonic-gate 				exit(1);
7147c478bd9Sstevel@tonic-gate 			}
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 			np->first.l = (long)cmdopen("cpio", com, "w", fd);
7177c478bd9Sstevel@tonic-gate 			(void) fclose(fd);
7187c478bd9Sstevel@tonic-gate 			walkflags |= FTW_DEPTH;
7197c478bd9Sstevel@tonic-gate 			np->action = CPIO;
7207c478bd9Sstevel@tonic-gate 		}
7217c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
7227c478bd9Sstevel@tonic-gate 		case PRINT:
723b34cd89aSYuri Pankov 		case PRINT0:
7247c478bd9Sstevel@tonic-gate 			(*actionp)++;
7257c478bd9Sstevel@tonic-gate 			break;
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 		case NEWER: {
7287c478bd9Sstevel@tonic-gate 			struct stat statb;
7297c478bd9Sstevel@tonic-gate 			if (stat(b, &statb) < 0) {
7307c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
73127d3a169SToomas Soome 				    gettext("%s: cannot access %s\n"),
73227d3a169SToomas Soome 				    cmdname, b);
7337c478bd9Sstevel@tonic-gate 				exit(1);
7347c478bd9Sstevel@tonic-gate 			}
7357c478bd9Sstevel@tonic-gate 			np->first.l = statb.st_mtime;
7367c478bd9Sstevel@tonic-gate 			np->second.i = '+';
7377c478bd9Sstevel@tonic-gate 			break;
7387c478bd9Sstevel@tonic-gate 		}
7397c478bd9Sstevel@tonic-gate 
7407c478bd9Sstevel@tonic-gate 		case PRUNE:
7417c478bd9Sstevel@tonic-gate 		case NOUSER:
7427c478bd9Sstevel@tonic-gate 		case NOGRP:
7437c478bd9Sstevel@tonic-gate 			break;
7447c478bd9Sstevel@tonic-gate 		case FSTYPE:
7457c478bd9Sstevel@tonic-gate 			np->first.cp = b;
7467c478bd9Sstevel@tonic-gate 			break;
7477c478bd9Sstevel@tonic-gate 		case LS:
7487c478bd9Sstevel@tonic-gate 			(*actionp)++;
7497c478bd9Sstevel@tonic-gate 			break;
7507c478bd9Sstevel@tonic-gate 		case XATTR:
7517c478bd9Sstevel@tonic-gate 			break;
7527c478bd9Sstevel@tonic-gate 		case ACL:
7537c478bd9Sstevel@tonic-gate 			break;
754b34cd89aSYuri Pankov 		case MAXDEPTH:
75527d3a169SToomas Soome 			maxdepth = (int)strtol(b, NULL, 10);
756b34cd89aSYuri Pankov 			break;
757b34cd89aSYuri Pankov 		case MINDEPTH:
75827d3a169SToomas Soome 			mindepth = (int)strtol(b, NULL, 10);
759b34cd89aSYuri Pankov 			break;
7607c478bd9Sstevel@tonic-gate 		}
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 		oldnp = np++;
7637c478bd9Sstevel@tonic-gate 		oldnp->next = np;
7647c478bd9Sstevel@tonic-gate 	}
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	if ((*av) || (wasop))
7677c478bd9Sstevel@tonic-gate 		goto err;
7687c478bd9Sstevel@tonic-gate 
7697c478bd9Sstevel@tonic-gate 	if (paren_cnt != 0) {
77027d3a169SToomas Soome 		(void) fprintf(stderr, gettext("%s: unmatched '('\n"), cmdname);
7717c478bd9Sstevel@tonic-gate 		exit(1);
7727c478bd9Sstevel@tonic-gate 	}
7737c478bd9Sstevel@tonic-gate 
7747c478bd9Sstevel@tonic-gate 	/* just before returning, save next free node from the list */
7757c478bd9Sstevel@tonic-gate 	freenode = oldnp->next;
7767c478bd9Sstevel@tonic-gate 	oldnp->next = 0;
7777c478bd9Sstevel@tonic-gate 	return (av-argv);
7787c478bd9Sstevel@tonic-gate err:
7797c478bd9Sstevel@tonic-gate 	if (*av)
7807c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
7817c478bd9Sstevel@tonic-gate 		    gettext("%s: bad option %s\n"), cmdname, *av);
7827c478bd9Sstevel@tonic-gate 	else
7837c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: bad option\n"), cmdname);
7847c478bd9Sstevel@tonic-gate 	usage();
7857c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
7867c478bd9Sstevel@tonic-gate }
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate /*
7897c478bd9Sstevel@tonic-gate  * print out a usage message
7907c478bd9Sstevel@tonic-gate  */
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate static void
7936c83d09fSrobbin usage(void)
7947c478bd9Sstevel@tonic-gate {
7957c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr,
796b34cd89aSYuri Pankov 	    gettext("%s: [-E] [-H | -L] path-list predicate-list\n"), cmdname);
7977c478bd9Sstevel@tonic-gate 	exit(1);
7987c478bd9Sstevel@tonic-gate }
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate /*
8017c478bd9Sstevel@tonic-gate  * This is the function that gets executed at each node
8027c478bd9Sstevel@tonic-gate  */
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate static int
80527d3a169SToomas Soome execute(const char *name, const struct stat *statb, int type, struct FTW *state)
8067c478bd9Sstevel@tonic-gate {
8077c478bd9Sstevel@tonic-gate 	struct Node *np = topnode;
8087c478bd9Sstevel@tonic-gate 	int val;
8097c478bd9Sstevel@tonic-gate 	time_t t;
8107c478bd9Sstevel@tonic-gate 	long l;
8117c478bd9Sstevel@tonic-gate 	long long ll;
8127c478bd9Sstevel@tonic-gate 	int not = 1;
81327d3a169SToomas Soome 	const char *filename;
814b34cd89aSYuri Pankov 	int cnpreg = 0;
8157c478bd9Sstevel@tonic-gate 
8167c478bd9Sstevel@tonic-gate 	if (type == FTW_NS) {
8177c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: stat() error %s: %s\n"),
81827d3a169SToomas Soome 		    cmdname, name, strerror(errno));
8197c478bd9Sstevel@tonic-gate 		error = 1;
8207c478bd9Sstevel@tonic-gate 		return (0);
8217c478bd9Sstevel@tonic-gate 	} else if (type == FTW_DNR) {
8227c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: cannot read dir %s: %s\n"),
82327d3a169SToomas Soome 		    cmdname, name, strerror(errno));
8247c478bd9Sstevel@tonic-gate 		error = 1;
8250729abfeSRich Burridge 	} else if (type == FTW_SLN && lflag == 1) {
8267c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
82727d3a169SToomas Soome 		    gettext("%s: cannot follow symbolic link %s: %s\n"),
82827d3a169SToomas Soome 		    cmdname, name, strerror(errno));
8297c478bd9Sstevel@tonic-gate 		error = 1;
83068a94df1Scf 	} else if (type == FTW_DL) {
83168a94df1Scf 		(void) fprintf(stderr, gettext("%s: cycle detected for %s\n"),
83227d3a169SToomas Soome 		    cmdname, name);
83368a94df1Scf 		error = 1;
83468a94df1Scf 		return (0);
8357c478bd9Sstevel@tonic-gate 	}
8367c478bd9Sstevel@tonic-gate 
837b34cd89aSYuri Pankov 	if ((maxdepth != -1 && state->level > maxdepth) ||
838b34cd89aSYuri Pankov 	    (mindepth != -1 && state->level < mindepth))
839b34cd89aSYuri Pankov 		return (0);
840b34cd89aSYuri Pankov 
8417c478bd9Sstevel@tonic-gate 	while (np) {
8427c478bd9Sstevel@tonic-gate 		switch (np->action) {
8437c478bd9Sstevel@tonic-gate 		case NOT:
8447c478bd9Sstevel@tonic-gate 			not = !not;
8457c478bd9Sstevel@tonic-gate 			np = np->next;
8467c478bd9Sstevel@tonic-gate 			continue;
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 		case AND:
8497c478bd9Sstevel@tonic-gate 			np = np->next;
8507c478bd9Sstevel@tonic-gate 			continue;
8517c478bd9Sstevel@tonic-gate 
8527c478bd9Sstevel@tonic-gate 		case OR:
8537c478bd9Sstevel@tonic-gate 			if (np->first.np == np) {
8547c478bd9Sstevel@tonic-gate 				/*
8557c478bd9Sstevel@tonic-gate 				 * handle naked OR (no term on left hand side)
8567c478bd9Sstevel@tonic-gate 				 */
8577c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
8587c478bd9Sstevel@tonic-gate 				    gettext("%s: invalid -o construction\n"),
8597c478bd9Sstevel@tonic-gate 				    cmdname);
8607c478bd9Sstevel@tonic-gate 				exit(2);
8617c478bd9Sstevel@tonic-gate 			}
8627c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
8637c478bd9Sstevel@tonic-gate 		case LPAREN: {
8647c478bd9Sstevel@tonic-gate 			struct Node *save = topnode;
8657c478bd9Sstevel@tonic-gate 			topnode = np->first.np;
8667c478bd9Sstevel@tonic-gate 			(void) execute(name, statb, type, state);
8677c478bd9Sstevel@tonic-gate 			val = lastval;
8687c478bd9Sstevel@tonic-gate 			topnode = save;
8697c478bd9Sstevel@tonic-gate 			if (np->action == OR) {
8707c478bd9Sstevel@tonic-gate 				if (val)
8717c478bd9Sstevel@tonic-gate 					return (0);
8727c478bd9Sstevel@tonic-gate 				val = 1;
8737c478bd9Sstevel@tonic-gate 			}
8747c478bd9Sstevel@tonic-gate 			break;
8757c478bd9Sstevel@tonic-gate 		}
8767c478bd9Sstevel@tonic-gate 
8777c478bd9Sstevel@tonic-gate 		case LOCAL: {
8787c478bd9Sstevel@tonic-gate 			int	nremfs;
8797c478bd9Sstevel@tonic-gate 			val = 1;
8807c478bd9Sstevel@tonic-gate 			/*
8817c478bd9Sstevel@tonic-gate 			 * If file system type matches the remote
8827c478bd9Sstevel@tonic-gate 			 * file system type, then it is not local.
8837c478bd9Sstevel@tonic-gate 			 */
8847c478bd9Sstevel@tonic-gate 			for (nremfs = 0; nremfs < fstype_index; nremfs++) {
8857c478bd9Sstevel@tonic-gate 				if (strcmp(remote_fstypes[nremfs],
88627d3a169SToomas Soome 				    statb->st_fstype) == 0) {
8877c478bd9Sstevel@tonic-gate 					val = 0;
8887c478bd9Sstevel@tonic-gate 					break;
8897c478bd9Sstevel@tonic-gate 				}
8907c478bd9Sstevel@tonic-gate 			}
8917c478bd9Sstevel@tonic-gate 			break;
8927c478bd9Sstevel@tonic-gate 		}
8937c478bd9Sstevel@tonic-gate 
8947c478bd9Sstevel@tonic-gate 		case TYPE:
8957c478bd9Sstevel@tonic-gate 			l = (long)statb->st_mode&S_IFMT;
8967c478bd9Sstevel@tonic-gate 			goto num;
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 		case PERM:
8997c478bd9Sstevel@tonic-gate 			l = (long)statb->st_mode&07777;
9007c478bd9Sstevel@tonic-gate 			if (np->second.i == '-')
9017c478bd9Sstevel@tonic-gate 				val = ((l&np->first.l) == np->first.l);
9027c478bd9Sstevel@tonic-gate 			else
9037c478bd9Sstevel@tonic-gate 				val = (l == np->first.l);
9047c478bd9Sstevel@tonic-gate 			break;
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 		case INUM:
9077c478bd9Sstevel@tonic-gate 			ll = (long long)statb->st_ino;
9087c478bd9Sstevel@tonic-gate 			goto llnum;
9097c478bd9Sstevel@tonic-gate 		case NEWER:
9107c478bd9Sstevel@tonic-gate 			l = statb->st_mtime;
9117c478bd9Sstevel@tonic-gate 			goto num;
9127c478bd9Sstevel@tonic-gate 		case ATIME:
9137c478bd9Sstevel@tonic-gate 			t = statb->st_atime;
9147c478bd9Sstevel@tonic-gate 			goto days;
9157c478bd9Sstevel@tonic-gate 		case CTIME:
9167c478bd9Sstevel@tonic-gate 			t = statb->st_ctime;
9177c478bd9Sstevel@tonic-gate 			goto days;
9187c478bd9Sstevel@tonic-gate 		case MTIME:
9197c478bd9Sstevel@tonic-gate 			t = statb->st_mtime;
9207c478bd9Sstevel@tonic-gate 		days:
9217c478bd9Sstevel@tonic-gate 			l = (now-t)/A_DAY;
9227c478bd9Sstevel@tonic-gate 			goto num;
923da1a9cbeSjonb 		case MMIN:
924da1a9cbeSjonb 			t = statb->st_mtime;
925da1a9cbeSjonb 			goto mins;
926da1a9cbeSjonb 		case AMIN:
927da1a9cbeSjonb 			t = statb->st_atime;
928da1a9cbeSjonb 			goto mins;
929da1a9cbeSjonb 		case CMIN:
930da1a9cbeSjonb 			t = statb->st_ctime;
931da1a9cbeSjonb 			goto mins;
932da1a9cbeSjonb 		mins:
933da1a9cbeSjonb 			l = (now-t)/A_MIN;
934da1a9cbeSjonb 			goto num;
9357c478bd9Sstevel@tonic-gate 		case CSIZE:
9367c478bd9Sstevel@tonic-gate 			ll = (long long)statb->st_size;
9377c478bd9Sstevel@tonic-gate 			goto llnum;
9387c478bd9Sstevel@tonic-gate 		case SIZE:
9397c478bd9Sstevel@tonic-gate 			ll = (long long)round(statb->st_size, BLKSIZ)/BLKSIZ;
9407c478bd9Sstevel@tonic-gate 			goto llnum;
9417c478bd9Sstevel@tonic-gate 		case F_USER:
9427c478bd9Sstevel@tonic-gate 			l = (long)statb->st_uid;
9437c478bd9Sstevel@tonic-gate 			goto num;
9447c478bd9Sstevel@tonic-gate 		case F_GROUP:
9457c478bd9Sstevel@tonic-gate 			l = (long)statb->st_gid;
9467c478bd9Sstevel@tonic-gate 			goto num;
9477c478bd9Sstevel@tonic-gate 		case LINKS:
9487c478bd9Sstevel@tonic-gate 			l = (long)statb->st_nlink;
9497c478bd9Sstevel@tonic-gate 			goto num;
9507c478bd9Sstevel@tonic-gate 		llnum:
9517c478bd9Sstevel@tonic-gate 			if (np->second.i == '+')
9527c478bd9Sstevel@tonic-gate 				val = (ll > np->first.ll);
9537c478bd9Sstevel@tonic-gate 			else if (np->second.i == '-')
9547c478bd9Sstevel@tonic-gate 				val = (ll < np->first.ll);
9557c478bd9Sstevel@tonic-gate 			else
9567c478bd9Sstevel@tonic-gate 				val = (ll == np->first.ll);
9577c478bd9Sstevel@tonic-gate 			break;
9587c478bd9Sstevel@tonic-gate 		num:
9597c478bd9Sstevel@tonic-gate 			if (np->second.i == '+')
9607c478bd9Sstevel@tonic-gate 				val = (l > np->first.l);
9617c478bd9Sstevel@tonic-gate 			else if (np->second.i == '-')
9627c478bd9Sstevel@tonic-gate 				val = (l < np->first.l);
9637c478bd9Sstevel@tonic-gate 			else
9647c478bd9Sstevel@tonic-gate 				val = (l == np->first.l);
9657c478bd9Sstevel@tonic-gate 			break;
9667c478bd9Sstevel@tonic-gate 		case OK:
9677c478bd9Sstevel@tonic-gate 			val = ok(name, np->first.ap);
9687c478bd9Sstevel@tonic-gate 			break;
9697c478bd9Sstevel@tonic-gate 		case EXEC:
970d35170d6Srm 			val = doexec(name, np->first.ap, NULL);
9717c478bd9Sstevel@tonic-gate 			break;
972ab823b7fSPrasad Joshi 		case DELETE:
973ab823b7fSPrasad Joshi 			val = dodelete(name, statb, state);
974ab823b7fSPrasad Joshi 			break;
9757c478bd9Sstevel@tonic-gate 
9767c478bd9Sstevel@tonic-gate 		case VARARGS: {
9777c478bd9Sstevel@tonic-gate 			struct Arglist *ap = np->first.vp;
9787c478bd9Sstevel@tonic-gate 			char *cp;
9797c478bd9Sstevel@tonic-gate 			cp = ap->nextstr - (strlen(name)+1);
9807c478bd9Sstevel@tonic-gate 			if (cp >= (char *)(ap->nextvar+3)) {
9817c478bd9Sstevel@tonic-gate 				/* there is room just copy the name */
9827c478bd9Sstevel@tonic-gate 				val = 1;
9837c478bd9Sstevel@tonic-gate 				(void) strcpy(cp, name);
9847c478bd9Sstevel@tonic-gate 				*ap->nextvar++ = cp;
9857c478bd9Sstevel@tonic-gate 				ap->nextstr = cp;
9867c478bd9Sstevel@tonic-gate 			} else {
9877c478bd9Sstevel@tonic-gate 				/* no more room, exec command */
98827d3a169SToomas Soome 				*ap->nextvar++ = (char *)name;
9897c478bd9Sstevel@tonic-gate 				*ap->nextvar = 0;
9907c478bd9Sstevel@tonic-gate 				val = 1;
99127d3a169SToomas Soome 				(void) doexec(NULL, ap->arglist,
992d35170d6Srm 				    &exec_exitcode);
9937c478bd9Sstevel@tonic-gate 				ap->nextstr = ap->end;
9947c478bd9Sstevel@tonic-gate 				ap->nextvar = ap->firstvar;
9957c478bd9Sstevel@tonic-gate 			}
9967c478bd9Sstevel@tonic-gate 			break;
9977c478bd9Sstevel@tonic-gate 		}
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 		case DEPTH:
10007c478bd9Sstevel@tonic-gate 		case MOUNT:
10017c478bd9Sstevel@tonic-gate 		case FOLLOW:
10027c478bd9Sstevel@tonic-gate 			val = 1;
10037c478bd9Sstevel@tonic-gate 			break;
10047c478bd9Sstevel@tonic-gate 
1005b34cd89aSYuri Pankov 		case NAME:
100605f32410SAndy Stormont 		case INAME:
100705f32410SAndy Stormont 		case PATH:
100805f32410SAndy Stormont 		case IPATH: {
100905f32410SAndy Stormont 			char *path;
101005f32410SAndy Stormont 			int fnmflags = 0;
101105f32410SAndy Stormont 
101205f32410SAndy Stormont 			if (np->action == INAME || np->action == IPATH)
101305f32410SAndy Stormont 				fnmflags = FNM_IGNORECASE;
10149ab6dc39Schin 
10159ab6dc39Schin 			/*
10169ab6dc39Schin 			 * basename(3c) may modify name, so
10179ab6dc39Schin 			 * we need to pass another string
10189ab6dc39Schin 			 */
101905f32410SAndy Stormont 			if ((path = strdup(name)) == NULL) {
10209ab6dc39Schin 				(void) fprintf(stderr,
10219ab6dc39Schin 				    gettext("%s: cannot strdup() %s: %s\n"),
10226b238a5aSchin 				    cmdname, name, strerror(errno));
10239ab6dc39Schin 				exit(2);
10249ab6dc39Schin 			}
10257c478bd9Sstevel@tonic-gate 			/*
10267c478bd9Sstevel@tonic-gate 			 * XPG4 find should not treat a leading '.' in a
10277c478bd9Sstevel@tonic-gate 			 * filename specially for pattern matching.
10287c478bd9Sstevel@tonic-gate 			 * /usr/bin/find  will not pattern match a leading
10297c478bd9Sstevel@tonic-gate 			 * '.' in a filename, unless '.' is explicitly
10307c478bd9Sstevel@tonic-gate 			 * specified.
1031*f3a525d9SJohn Levon 			 *
1032*f3a525d9SJohn Levon 			 * The legacy behavior makes no sense for PATH.
10337c478bd9Sstevel@tonic-gate 			 */
1034b34cd89aSYuri Pankov #ifndef XPG4
1035*f3a525d9SJohn Levon 			if (np->action == NAME || np->action == INAME)
1036*f3a525d9SJohn Levon 				fnmflags |= FNM_PERIOD;
10377c478bd9Sstevel@tonic-gate #endif
103805f32410SAndy Stormont 
103905f32410SAndy Stormont 			val = !fnmatch(np->first.cp,
104027d3a169SToomas Soome 			    (np->action == NAME || np->action == INAME) ?
104127d3a169SToomas Soome 			    basename(path) : path, fnmflags);
104205f32410SAndy Stormont 			free(path);
10437c478bd9Sstevel@tonic-gate 			break;
10447c478bd9Sstevel@tonic-gate 		}
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate 		case PRUNE:
10477c478bd9Sstevel@tonic-gate 			if (type == FTW_D)
10487c478bd9Sstevel@tonic-gate 				state->quit = FTW_PRUNE;
10497c478bd9Sstevel@tonic-gate 			val = 1;
10507c478bd9Sstevel@tonic-gate 			break;
10517c478bd9Sstevel@tonic-gate 		case NOUSER:
10527c478bd9Sstevel@tonic-gate 			val = ((getpwuid(statb->st_uid)) == 0);
10537c478bd9Sstevel@tonic-gate 			break;
10547c478bd9Sstevel@tonic-gate 		case NOGRP:
10557c478bd9Sstevel@tonic-gate 			val = ((getgrgid(statb->st_gid)) == 0);
10567c478bd9Sstevel@tonic-gate 			break;
10577c478bd9Sstevel@tonic-gate 		case FSTYPE:
10587c478bd9Sstevel@tonic-gate 			val = (strcmp(np->first.cp, statb->st_fstype) == 0);
10597c478bd9Sstevel@tonic-gate 			break;
10607c478bd9Sstevel@tonic-gate 		case CPIO:
10617c478bd9Sstevel@tonic-gate 			output = (FILE *)np->first.l;
10627c478bd9Sstevel@tonic-gate 			(void) fprintf(output, "%s\n", name);
10637c478bd9Sstevel@tonic-gate 			val = 1;
10647c478bd9Sstevel@tonic-gate 			break;
10657c478bd9Sstevel@tonic-gate 		case PRINT:
1066b34cd89aSYuri Pankov 		case PRINT0:
1067b34cd89aSYuri Pankov 			(void) fprintf(stdout, "%s%c", name,
1068b34cd89aSYuri Pankov 			    (np->action == PRINT) ? '\n' : '\0');
10697c478bd9Sstevel@tonic-gate 			val = 1;
10707c478bd9Sstevel@tonic-gate 			break;
10717c478bd9Sstevel@tonic-gate 		case LS:
10727c478bd9Sstevel@tonic-gate 			(void) list(name, statb);
10737c478bd9Sstevel@tonic-gate 			val = 1;
10747c478bd9Sstevel@tonic-gate 			break;
10757c478bd9Sstevel@tonic-gate 		case XATTR:
1076f467e6fbSJohn Sonnenschein 			filename = (walkflags & FTW_CHDIR) ?
107727d3a169SToomas Soome 			    gettail(name) : name;
10787c478bd9Sstevel@tonic-gate 			val = (pathconf(filename, _PC_XATTR_EXISTS) == 1);
10797c478bd9Sstevel@tonic-gate 			break;
10807c478bd9Sstevel@tonic-gate 		case ACL:
10817c478bd9Sstevel@tonic-gate 			/*
10827c478bd9Sstevel@tonic-gate 			 * Need to get the tail of the file name, since we have
10837c478bd9Sstevel@tonic-gate 			 * already chdir()ed into the directory (performed in
10847c478bd9Sstevel@tonic-gate 			 * nftw()) of the file
10857c478bd9Sstevel@tonic-gate 			 */
1086f467e6fbSJohn Sonnenschein 			filename = (walkflags & FTW_CHDIR) ?
108727d3a169SToomas Soome 			    gettail(name) : name;
1088f467e6fbSJohn Sonnenschein 			val = acl_trivial(filename);
10897c478bd9Sstevel@tonic-gate 			break;
1090b34cd89aSYuri Pankov 		case F_USERACL:
1091b34cd89aSYuri Pankov 		case F_GROUPACL: {
1092b34cd89aSYuri Pankov 			int i;
1093b34cd89aSYuri Pankov 			acl_t *acl;
1094b34cd89aSYuri Pankov 			void *acl_entry;
1095b34cd89aSYuri Pankov 			aclent_t *p1;
1096b34cd89aSYuri Pankov 			ace_t *p2;
1097b34cd89aSYuri Pankov 
1098b34cd89aSYuri Pankov 			filename = (walkflags & FTW_CHDIR) ?
1099b34cd89aSYuri Pankov 			    gettail(name) : name;
1100b34cd89aSYuri Pankov 			val = 0;
1101b34cd89aSYuri Pankov 			if (acl_get(filename, 0, &acl) != 0)
1102b34cd89aSYuri Pankov 				break;
1103b34cd89aSYuri Pankov 			for (i = 0, acl_entry = acl->acl_aclp;
1104b34cd89aSYuri Pankov 			    i != acl->acl_cnt; i++) {
1105b34cd89aSYuri Pankov 				if (acl->acl_type == ACLENT_T) {
1106b34cd89aSYuri Pankov 					p1 = (aclent_t *)acl_entry;
1107b34cd89aSYuri Pankov 					if (p1->a_id == np->first.l) {
1108b34cd89aSYuri Pankov 						val = 1;
1109b34cd89aSYuri Pankov 						acl_free(acl);
1110b34cd89aSYuri Pankov 						break;
1111b34cd89aSYuri Pankov 					}
1112b34cd89aSYuri Pankov 				} else {
1113b34cd89aSYuri Pankov 					p2 = (ace_t *)acl_entry;
1114b34cd89aSYuri Pankov 					if (p2->a_who == np->first.l) {
1115b34cd89aSYuri Pankov 						val = 1;
1116b34cd89aSYuri Pankov 						acl_free(acl);
1117b34cd89aSYuri Pankov 						break;
1118b34cd89aSYuri Pankov 					}
1119b34cd89aSYuri Pankov 				}
1120b34cd89aSYuri Pankov 				acl_entry = ((char *)acl_entry +
1121b34cd89aSYuri Pankov 				    acl->acl_entry_size);
1122b34cd89aSYuri Pankov 			}
1123b34cd89aSYuri Pankov 			acl_free(acl);
1124b34cd89aSYuri Pankov 			break;
1125b34cd89aSYuri Pankov 		}
1126b34cd89aSYuri Pankov 		case IREGEX:
1127b34cd89aSYuri Pankov 		case REGEX: {
1128b34cd89aSYuri Pankov 			regmatch_t pmatch;
1129b34cd89aSYuri Pankov 
1130b34cd89aSYuri Pankov 			val = 0;
113127d3a169SToomas Soome 			if (regexec(&preg[cnpreg], name, 1, &pmatch, 0) == 0)
1132b34cd89aSYuri Pankov 				val = ((pmatch.rm_so == 0) &&
1133b34cd89aSYuri Pankov 				    (pmatch.rm_eo == strlen(name)));
1134b34cd89aSYuri Pankov 			cnpreg++;
1135b34cd89aSYuri Pankov 			break;
1136b34cd89aSYuri Pankov 		}
1137b34cd89aSYuri Pankov 		case MAXDEPTH:
1138b34cd89aSYuri Pankov 			if (state->level == maxdepth && type == FTW_D)
1139b34cd89aSYuri Pankov 				state->quit = FTW_PRUNE;
1140b34cd89aSYuri Pankov 			/* FALLTHROUGH */
1141b34cd89aSYuri Pankov 		case MINDEPTH:
1142b34cd89aSYuri Pankov 			val = 1;
1143b34cd89aSYuri Pankov 			break;
11447c478bd9Sstevel@tonic-gate 		}
11457c478bd9Sstevel@tonic-gate 		/*
11467c478bd9Sstevel@tonic-gate 		 * evaluate 'val' and 'not' (exclusive-or)
11477c478bd9Sstevel@tonic-gate 		 * if no inversion (not == 1), return only when val == 0
11487c478bd9Sstevel@tonic-gate 		 * (primary not true). Otherwise, invert the primary
11497c478bd9Sstevel@tonic-gate 		 * and return when the primary is true.
11507c478bd9Sstevel@tonic-gate 		 * 'Lastval' saves the last result (fail or pass) when
11517c478bd9Sstevel@tonic-gate 		 * returning back to the calling routine.
11527c478bd9Sstevel@tonic-gate 		 */
115327d3a169SToomas Soome 		if (val ^ not) {
11547c478bd9Sstevel@tonic-gate 			lastval = 0;
11557c478bd9Sstevel@tonic-gate 			return (0);
11567c478bd9Sstevel@tonic-gate 		}
11577c478bd9Sstevel@tonic-gate 		lastval = 1;
11587c478bd9Sstevel@tonic-gate 		not = 1;
11597c478bd9Sstevel@tonic-gate 		np = np->next;
11607c478bd9Sstevel@tonic-gate 	}
11617c478bd9Sstevel@tonic-gate 	return (0);
11627c478bd9Sstevel@tonic-gate }
11637c478bd9Sstevel@tonic-gate 
11647c478bd9Sstevel@tonic-gate /*
11657c478bd9Sstevel@tonic-gate  * code for the -ok option
11667c478bd9Sstevel@tonic-gate  */
11677c478bd9Sstevel@tonic-gate 
11687c478bd9Sstevel@tonic-gate static int
116927d3a169SToomas Soome ok(const char *name, char *argv[])
11707c478bd9Sstevel@tonic-gate {
11713d63ea05Sas 	int  c;
11723d63ea05Sas 	int i = 0;
11733d63ea05Sas 	char resp[LINE_MAX + 1];
11747c478bd9Sstevel@tonic-gate 
117527d3a169SToomas Soome 	(void) fflush(stdout);	/* to flush possible `-print' */
11767c478bd9Sstevel@tonic-gate 
11777c478bd9Sstevel@tonic-gate 	if ((*argv != dummyarg) && (strcmp(*argv, name)))
11787c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "< %s ... %s >?   ", *argv, name);
11797c478bd9Sstevel@tonic-gate 	else
11807c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "< {} ... %s >?   ", name);
11817c478bd9Sstevel@tonic-gate 
11827c478bd9Sstevel@tonic-gate 	(void) fflush(stderr);
11833d63ea05Sas 
11843d63ea05Sas 	while ((c = getchar()) != '\n') {
11857c478bd9Sstevel@tonic-gate 		if (c == EOF)
11867c478bd9Sstevel@tonic-gate 			exit(2);
11873d63ea05Sas 		if (i < LINE_MAX)
11883d63ea05Sas 			resp[i++] = c;
11893d63ea05Sas 	}
11903d63ea05Sas 	resp[i] = '\0';
11913d63ea05Sas 
11923d63ea05Sas 	if (yes_check(resp))
11933d63ea05Sas 		return (doexec(name, argv, NULL));
11943d63ea05Sas 	else
11953d63ea05Sas 		return (0);
11967c478bd9Sstevel@tonic-gate }
11977c478bd9Sstevel@tonic-gate 
11987c478bd9Sstevel@tonic-gate /*
11997c478bd9Sstevel@tonic-gate  * execute argv with {} replaced by name
1200d35170d6Srm  *
1201d35170d6Srm  * Per XPG6, find must exit non-zero if an invocation through
1202d35170d6Srm  * -exec, punctuated by a plus sign, exits non-zero, so set
1203d35170d6Srm  * exitcode if we see a non-zero exit.
1204d35170d6Srm  * exitcode should be NULL when -exec or -ok is not punctuated
1205d35170d6Srm  * by a plus sign.
12067c478bd9Sstevel@tonic-gate  */
12077c478bd9Sstevel@tonic-gate 
12087c478bd9Sstevel@tonic-gate static int
120927d3a169SToomas Soome doexec(const char *name, char *argv[], int *exitcode)
12107c478bd9Sstevel@tonic-gate {
12117c478bd9Sstevel@tonic-gate 	char *cp;
12127c478bd9Sstevel@tonic-gate 	char **av = argv;
1213d35170d6Srm 	char *newargs[1 + SHELL_MAXARGS + 1];
12147c478bd9Sstevel@tonic-gate 	int dummyseen = 0;
1215d35170d6Srm 	int i, j, status, rc, r = 0;
1216d35170d6Srm 	int exit_status = 0;
1217d35170d6Srm 	pid_t pid, pid1;
12187c478bd9Sstevel@tonic-gate 
12197c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);	  /* to flush possible `-print' */
12207c478bd9Sstevel@tonic-gate 	if (name) {
12217c478bd9Sstevel@tonic-gate 		while (cp = *av++) {
12227c478bd9Sstevel@tonic-gate 			if (cp == dummyarg) {
12237c478bd9Sstevel@tonic-gate 				dummyseen = 1;
122427d3a169SToomas Soome 				av[-1] = (char *)name;
12257c478bd9Sstevel@tonic-gate 			}
12267c478bd9Sstevel@tonic-gate 
12277c478bd9Sstevel@tonic-gate 		}
12287c478bd9Sstevel@tonic-gate 	}
12297c478bd9Sstevel@tonic-gate 	if (argv[0] == NULL)    /* null command line */
12307c478bd9Sstevel@tonic-gate 		return (r);
12317c478bd9Sstevel@tonic-gate 
1232d35170d6Srm 	if ((pid = fork()) == -1) {
1233d35170d6Srm 		/* fork failed */
1234d35170d6Srm 		if (exitcode != NULL)
1235d35170d6Srm 			*exitcode = 1;
1236d35170d6Srm 		return (0);
1237d35170d6Srm 	}
1238d35170d6Srm 	if (pid != 0) {
1239d35170d6Srm 		/* parent */
1240d35170d6Srm 		do {
1241d35170d6Srm 			/* wait for child to exit */
1242d35170d6Srm 			if ((rc = wait(&r)) == -1 && errno != EINTR) {
1243d35170d6Srm 				(void) fprintf(stderr,
1244d35170d6Srm 				    gettext("wait failed %s"), strerror(errno));
1245d35170d6Srm 
1246d35170d6Srm 				if (exitcode != NULL)
1247d35170d6Srm 					*exitcode = 1;
1248d35170d6Srm 				return (0);
1249d35170d6Srm 			}
1250d35170d6Srm 		} while (rc != pid);
1251d35170d6Srm 	} else {
1252d35170d6Srm 		/* child */
12537c478bd9Sstevel@tonic-gate 		(void) execvp(argv[0], argv);
1254d35170d6Srm 		if (errno != E2BIG)
1255d35170d6Srm 			exit(1);
1256d35170d6Srm 
1257d35170d6Srm 		/*
1258d35170d6Srm 		 * We are in a situation where argv[0] points to a
1259d35170d6Srm 		 * script without the interpreter line, e.g. #!/bin/sh.
1260d35170d6Srm 		 * execvp() will execute either /usr/bin/sh or
1261d35170d6Srm 		 * /usr/xpg4/bin/sh against the script, and you will be
1262d35170d6Srm 		 * limited to SHELL_MAXARGS arguments. If you try to
1263d35170d6Srm 		 * pass more than SHELL_MAXARGS arguments, execvp()
1264d35170d6Srm 		 * fails with E2BIG.
1265d35170d6Srm 		 * See usr/src/lib/libc/port/gen/execvp.c.
1266d35170d6Srm 		 *
1267d35170d6Srm 		 * In this situation, process the argument list by
1268d35170d6Srm 		 * packets of SHELL_MAXARGS arguments with respect of
1269d35170d6Srm 		 * the following rules:
1270d35170d6Srm 		 * 1. the invocations have to complete before find exits
1271d35170d6Srm 		 * 2. only one invocation can be running at a time
1272d35170d6Srm 		 */
1273d35170d6Srm 
1274d35170d6Srm 		i = 1;
1275d35170d6Srm 		newargs[0] = argv[0];
1276d35170d6Srm 
1277d35170d6Srm 		while (argv[i]) {
1278d35170d6Srm 			j = 1;
1279d35170d6Srm 			while (j <= SHELL_MAXARGS && argv[i]) {
1280d35170d6Srm 				newargs[j++] = argv[i++];
1281d35170d6Srm 			}
1282d35170d6Srm 			newargs[j] = NULL;
1283d35170d6Srm 
1284d35170d6Srm 			if ((pid1 = fork()) == -1) {
1285d35170d6Srm 				/* fork failed */
1286d35170d6Srm 				exit(1);
1287d35170d6Srm 			}
1288d35170d6Srm 			if (pid1 == 0) {
1289d35170d6Srm 				/* child */
1290d35170d6Srm 				(void) execvp(newargs[0], newargs);
1291d35170d6Srm 				exit(1);
1292d35170d6Srm 			}
1293d35170d6Srm 
1294d35170d6Srm 			status = 0;
1295d35170d6Srm 
1296d35170d6Srm 			do {
1297d35170d6Srm 				/* wait for the child to exit */
1298d35170d6Srm 				if ((rc = wait(&status)) == -1 &&
1299d35170d6Srm 				    errno != EINTR) {
1300d35170d6Srm 					(void) fprintf(stderr,
1301d35170d6Srm 					    gettext("wait failed %s"),
1302d35170d6Srm 					    strerror(errno));
1303d35170d6Srm 					exit(1);
1304d35170d6Srm 				}
1305d35170d6Srm 			} while (rc != pid1);
1306d35170d6Srm 
1307d35170d6Srm 			if (status)
1308d35170d6Srm 				exit_status = 1;
1309d35170d6Srm 		}
1310d35170d6Srm 		/* all the invocations have completed */
1311d35170d6Srm 		exit(exit_status);
13127c478bd9Sstevel@tonic-gate 	}
1313d35170d6Srm 
13147c478bd9Sstevel@tonic-gate 	if (name && dummyseen) {
13157c478bd9Sstevel@tonic-gate 		for (av = argv; cp = *av++; ) {
13167c478bd9Sstevel@tonic-gate 			if (cp == name)
13177c478bd9Sstevel@tonic-gate 				av[-1] = dummyarg;
13187c478bd9Sstevel@tonic-gate 		}
13197c478bd9Sstevel@tonic-gate 	}
13207c478bd9Sstevel@tonic-gate 
1321d35170d6Srm 	if (r && exitcode != NULL)
1322d35170d6Srm 		*exitcode = 3; /* use to indicate error in cmd invocation */
1323d35170d6Srm 
13247c478bd9Sstevel@tonic-gate 	return (!r);
13257c478bd9Sstevel@tonic-gate }
13267c478bd9Sstevel@tonic-gate 
1327ab823b7fSPrasad Joshi static int
132827d3a169SToomas Soome dodelete(const char *name, const struct stat *statb, struct FTW *state)
1329ab823b7fSPrasad Joshi {
133027d3a169SToomas Soome 	const char *fn;
1331ab823b7fSPrasad Joshi 	int rc = 0;
1332ab823b7fSPrasad Joshi 
1333ab823b7fSPrasad Joshi 	/* restrict symlinks */
1334ab823b7fSPrasad Joshi 	if ((walkflags & FTW_PHYS) == 0) {
1335ab823b7fSPrasad Joshi 		(void) fprintf(stderr,
1336ab823b7fSPrasad Joshi 		    gettext("-delete is not allowed when symlinks are "
1337ab823b7fSPrasad Joshi 		    "followed.\n"));
1338ab823b7fSPrasad Joshi 		return (1);
1339ab823b7fSPrasad Joshi 	}
1340ab823b7fSPrasad Joshi 
1341ab823b7fSPrasad Joshi 	fn = name + state->base;
1342ab823b7fSPrasad Joshi 	if (strcmp(fn, ".") == 0) {
1343ab823b7fSPrasad Joshi 		/* nothing to do */
1344ab823b7fSPrasad Joshi 		return (1);
1345ab823b7fSPrasad Joshi 	}
1346ab823b7fSPrasad Joshi 
1347ab823b7fSPrasad Joshi 	if (strchr(fn, '/') != NULL) {
1348ab823b7fSPrasad Joshi 		(void) fprintf(stderr,
1349ab823b7fSPrasad Joshi 		    gettext("-delete with relative path is unsafe."));
1350ab823b7fSPrasad Joshi 		return (1);
1351ab823b7fSPrasad Joshi 	}
1352ab823b7fSPrasad Joshi 
1353ab823b7fSPrasad Joshi 	if (S_ISDIR(statb->st_mode)) {
1354ab823b7fSPrasad Joshi 		/* delete directory */
1355ab823b7fSPrasad Joshi 		rc = rmdir(name);
1356ab823b7fSPrasad Joshi 	} else {
1357ab823b7fSPrasad Joshi 		/* delete file */
1358ab823b7fSPrasad Joshi 		rc = unlink(name);
1359ab823b7fSPrasad Joshi 	}
1360ab823b7fSPrasad Joshi 
1361ab823b7fSPrasad Joshi 	if (rc < 0) {
1362ab823b7fSPrasad Joshi 		/* operation failed */
1363ab823b7fSPrasad Joshi 		(void) fprintf(stderr, gettext("delete failed %s: %s\n"),
1364ab823b7fSPrasad Joshi 		    name, strerror(errno));
1365ab823b7fSPrasad Joshi 		return (1);
1366ab823b7fSPrasad Joshi 	}
1367ab823b7fSPrasad Joshi 
1368ab823b7fSPrasad Joshi 	return (1);
1369ab823b7fSPrasad Joshi }
13707c478bd9Sstevel@tonic-gate 
13717c478bd9Sstevel@tonic-gate /*
13727c478bd9Sstevel@tonic-gate  *  Table lookup routine
13737c478bd9Sstevel@tonic-gate  */
13747c478bd9Sstevel@tonic-gate static struct Args *
137527d3a169SToomas Soome lookup(char *word)
13767c478bd9Sstevel@tonic-gate {
13777c478bd9Sstevel@tonic-gate 	struct Args *argp = commands;
13787c478bd9Sstevel@tonic-gate 	int second;
13797c478bd9Sstevel@tonic-gate 	if (word == 0 || *word == 0)
13807c478bd9Sstevel@tonic-gate 		return (0);
13817c478bd9Sstevel@tonic-gate 	second = word[1];
13827c478bd9Sstevel@tonic-gate 	while (*argp->name) {
13837c478bd9Sstevel@tonic-gate 		if (second == argp->name[1] && strcmp(word, argp->name) == 0)
13847c478bd9Sstevel@tonic-gate 			return (argp);
13857c478bd9Sstevel@tonic-gate 		argp++;
13867c478bd9Sstevel@tonic-gate 	}
13877c478bd9Sstevel@tonic-gate 	return (0);
13887c478bd9Sstevel@tonic-gate }
13897c478bd9Sstevel@tonic-gate 
13907c478bd9Sstevel@tonic-gate 
13917c478bd9Sstevel@tonic-gate /*
13927c478bd9Sstevel@tonic-gate  * Get space for variable length argument list
13937c478bd9Sstevel@tonic-gate  */
13947c478bd9Sstevel@tonic-gate 
13957c478bd9Sstevel@tonic-gate static struct Arglist *
139627d3a169SToomas Soome varargs(char **com)
13977c478bd9Sstevel@tonic-gate {
13987c478bd9Sstevel@tonic-gate 	struct Arglist *ap;
13997c478bd9Sstevel@tonic-gate 	int n;
14007c478bd9Sstevel@tonic-gate 	char **ep;
14017c478bd9Sstevel@tonic-gate 	if (varsize == 0) {
14027c478bd9Sstevel@tonic-gate 		n = 2*sizeof (char **);
14037c478bd9Sstevel@tonic-gate 		for (ep = environ; *ep; ep++)
14047c478bd9Sstevel@tonic-gate 			n += (strlen(*ep)+sizeof (ep) + 1);
14057c478bd9Sstevel@tonic-gate 		varsize = sizeof (struct Arglist)+ARG_MAX-PATH_MAX-n-1;
14067c478bd9Sstevel@tonic-gate 	}
14077c478bd9Sstevel@tonic-gate 	ap = (struct Arglist *)malloc(varsize+1);
14087c478bd9Sstevel@tonic-gate 	ap->end = (char *)ap + varsize;
14097c478bd9Sstevel@tonic-gate 	ap->nextstr = ap->end;
14107c478bd9Sstevel@tonic-gate 	ap->nextvar = ap->arglist;
141127d3a169SToomas Soome 	while (*ap->nextvar++ = *com++)
141227d3a169SToomas Soome 		;
14137c478bd9Sstevel@tonic-gate 	ap->nextvar--;
14147c478bd9Sstevel@tonic-gate 	ap->firstvar = ap->nextvar;
14157c478bd9Sstevel@tonic-gate 	ap->next = lastlist;
14167c478bd9Sstevel@tonic-gate 	lastlist = ap;
14177c478bd9Sstevel@tonic-gate 	return (ap);
14187c478bd9Sstevel@tonic-gate }
14197c478bd9Sstevel@tonic-gate 
14207c478bd9Sstevel@tonic-gate /*
14217c478bd9Sstevel@tonic-gate  * filter command support
14227c478bd9Sstevel@tonic-gate  * fork and exec cmd(argv) according to mode:
14237c478bd9Sstevel@tonic-gate  *
14247c478bd9Sstevel@tonic-gate  *	"r"	with fp as stdin of cmd (default stdin), cmd stdout returned
14257c478bd9Sstevel@tonic-gate  *	"w"	with fp as stdout of cmd (default stdout), cmd stdin returned
14267c478bd9Sstevel@tonic-gate  */
14277c478bd9Sstevel@tonic-gate 
14287c478bd9Sstevel@tonic-gate #define	CMDERR	((1<<8)-1)	/* command error exit code		*/
14297c478bd9Sstevel@tonic-gate #define	MAXCMDS	8		/* max # simultaneous cmdopen()'s	*/
14307c478bd9Sstevel@tonic-gate 
14317c478bd9Sstevel@tonic-gate static struct			/* info for each cmdopen()		*/
14327c478bd9Sstevel@tonic-gate {
14337c478bd9Sstevel@tonic-gate 	FILE	*fp;		/* returned by cmdopen()		*/
14347c478bd9Sstevel@tonic-gate 	pid_t	pid;		/* pid used by cmdopen()		*/
14357c478bd9Sstevel@tonic-gate } cmdproc[MAXCMDS];
14367c478bd9Sstevel@tonic-gate 
14377c478bd9Sstevel@tonic-gate static FILE *
143827d3a169SToomas Soome cmdopen(char *cmd, char **argv, char *mode, FILE *fp)
14397c478bd9Sstevel@tonic-gate {
14407c478bd9Sstevel@tonic-gate 	int	proc;
14417c478bd9Sstevel@tonic-gate 	int	cmdfd;
14427c478bd9Sstevel@tonic-gate 	int	usrfd;
14437c478bd9Sstevel@tonic-gate 	int		pio[2];
14447c478bd9Sstevel@tonic-gate 
14457c478bd9Sstevel@tonic-gate 	switch (*mode) {
14467c478bd9Sstevel@tonic-gate 	case 'r':
14477c478bd9Sstevel@tonic-gate 		cmdfd = 1;
14487c478bd9Sstevel@tonic-gate 		usrfd = 0;
14497c478bd9Sstevel@tonic-gate 		break;
14507c478bd9Sstevel@tonic-gate 	case 'w':
14517c478bd9Sstevel@tonic-gate 		cmdfd = 0;
14527c478bd9Sstevel@tonic-gate 		usrfd = 1;
14537c478bd9Sstevel@tonic-gate 		break;
14547c478bd9Sstevel@tonic-gate 	default:
14557c478bd9Sstevel@tonic-gate 		return (0);
14567c478bd9Sstevel@tonic-gate 	}
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 	for (proc = 0; proc < MAXCMDS; proc++)
14597c478bd9Sstevel@tonic-gate 		if (!cmdproc[proc].fp)
14607c478bd9Sstevel@tonic-gate 			break;
14617c478bd9Sstevel@tonic-gate 	if (proc >= MAXCMDS)
14627c478bd9Sstevel@tonic-gate 		return (0);
14637c478bd9Sstevel@tonic-gate 
14647c478bd9Sstevel@tonic-gate 	if (pipe(pio))
14657c478bd9Sstevel@tonic-gate 		return (0);
14667c478bd9Sstevel@tonic-gate 
14677c478bd9Sstevel@tonic-gate 	switch (cmdproc[proc].pid = fork()) {
14687c478bd9Sstevel@tonic-gate 	case -1:
14697c478bd9Sstevel@tonic-gate 		return (0);
14707c478bd9Sstevel@tonic-gate 	case 0:
14717c478bd9Sstevel@tonic-gate 		if (fp && fileno(fp) != usrfd) {
14727c478bd9Sstevel@tonic-gate 			(void) close(usrfd);
14737c478bd9Sstevel@tonic-gate 			if (dup2(fileno(fp), usrfd) != usrfd)
14747c478bd9Sstevel@tonic-gate 				_exit(CMDERR);
14757c478bd9Sstevel@tonic-gate 			(void) close(fileno(fp));
14767c478bd9Sstevel@tonic-gate 		}
14777c478bd9Sstevel@tonic-gate 		(void) close(cmdfd);
14787c478bd9Sstevel@tonic-gate 		if (dup2(pio[cmdfd], cmdfd) != cmdfd)
14797c478bd9Sstevel@tonic-gate 			_exit(CMDERR);
14807c478bd9Sstevel@tonic-gate 		(void) close(pio[cmdfd]);
14817c478bd9Sstevel@tonic-gate 		(void) close(pio[usrfd]);
14827c478bd9Sstevel@tonic-gate 		(void) execvp(cmd, argv);
14837c478bd9Sstevel@tonic-gate 		if (errno == ENOEXEC) {
14847c478bd9Sstevel@tonic-gate 			char	**p;
14857c478bd9Sstevel@tonic-gate 			char		**v;
14867c478bd9Sstevel@tonic-gate 
14877c478bd9Sstevel@tonic-gate 			/*
14887c478bd9Sstevel@tonic-gate 			 * assume cmd is a shell script
14897c478bd9Sstevel@tonic-gate 			 */
14907c478bd9Sstevel@tonic-gate 
14917c478bd9Sstevel@tonic-gate 			p = argv;
149227d3a169SToomas Soome 			while (*p++)
149327d3a169SToomas Soome 				;
149427d3a169SToomas Soome 			if (v = malloc((p - argv + 1) * sizeof (char **))) {
14957c478bd9Sstevel@tonic-gate 				p = v;
14967c478bd9Sstevel@tonic-gate 				*p++ = cmd;
149727d3a169SToomas Soome 				if (*argv)
149827d3a169SToomas Soome 					argv++;
149927d3a169SToomas Soome 				while (*p++ = *argv++)
150027d3a169SToomas Soome 					;
15017c478bd9Sstevel@tonic-gate 				(void) execv(getshell(), v);
15027c478bd9Sstevel@tonic-gate 			}
15037c478bd9Sstevel@tonic-gate 		}
15047c478bd9Sstevel@tonic-gate 		_exit(CMDERR);
15057c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
15067c478bd9Sstevel@tonic-gate 	default:
15077c478bd9Sstevel@tonic-gate 		(void) close(pio[cmdfd]);
15087c478bd9Sstevel@tonic-gate 		return (cmdproc[proc].fp = fdopen(pio[usrfd], mode));
15097c478bd9Sstevel@tonic-gate 	}
15107c478bd9Sstevel@tonic-gate }
15117c478bd9Sstevel@tonic-gate 
15127c478bd9Sstevel@tonic-gate /*
15137c478bd9Sstevel@tonic-gate  * close a stream opened by cmdopen()
15147c478bd9Sstevel@tonic-gate  * -1 returned if cmdopen() had a problem
15157c478bd9Sstevel@tonic-gate  * otherwise exit() status of command is returned
15167c478bd9Sstevel@tonic-gate  */
15177c478bd9Sstevel@tonic-gate 
15187c478bd9Sstevel@tonic-gate static int
151927d3a169SToomas Soome cmdclose(FILE *fp)
15207c478bd9Sstevel@tonic-gate {
15217c478bd9Sstevel@tonic-gate 	int	i;
15227c478bd9Sstevel@tonic-gate 	pid_t	p, pid;
15237c478bd9Sstevel@tonic-gate 	int		status;
15247c478bd9Sstevel@tonic-gate 
15257c478bd9Sstevel@tonic-gate 	for (i = 0; i < MAXCMDS; i++)
15267c478bd9Sstevel@tonic-gate 		if (fp == cmdproc[i].fp) break;
15277c478bd9Sstevel@tonic-gate 	if (i >= MAXCMDS)
15287c478bd9Sstevel@tonic-gate 		return (-1);
15297c478bd9Sstevel@tonic-gate 	(void) fclose(fp);
15307c478bd9Sstevel@tonic-gate 	cmdproc[i].fp = 0;
15317c478bd9Sstevel@tonic-gate 	pid = cmdproc[i].pid;
153227d3a169SToomas Soome 	while ((p = wait(&status)) != pid && p != (pid_t)-1)
153327d3a169SToomas Soome 		;
15347c478bd9Sstevel@tonic-gate 	if (p == pid) {
15357c478bd9Sstevel@tonic-gate 		status = (status >> 8) & CMDERR;
15367c478bd9Sstevel@tonic-gate 		if (status == CMDERR)
15377c478bd9Sstevel@tonic-gate 			status = -1;
15387c478bd9Sstevel@tonic-gate 	}
15397c478bd9Sstevel@tonic-gate 	else
15407c478bd9Sstevel@tonic-gate 		status = -1;
15417c478bd9Sstevel@tonic-gate 	return (status);
15427c478bd9Sstevel@tonic-gate }
15437c478bd9Sstevel@tonic-gate 
15447c478bd9Sstevel@tonic-gate /*
15457c478bd9Sstevel@tonic-gate  * return pointer to the full path name of the shell
15467c478bd9Sstevel@tonic-gate  *
15477c478bd9Sstevel@tonic-gate  * SHELL is read from the environment and must start with /
15487c478bd9Sstevel@tonic-gate  *
15497c478bd9Sstevel@tonic-gate  * if set-uid or set-gid then the executable and its containing
15507c478bd9Sstevel@tonic-gate  * directory must not be writable by the real user
15517c478bd9Sstevel@tonic-gate  *
15527c478bd9Sstevel@tonic-gate  * /usr/bin/sh is returned by default
15537c478bd9Sstevel@tonic-gate  */
15547c478bd9Sstevel@tonic-gate 
15557c478bd9Sstevel@tonic-gate char *
155627d3a169SToomas Soome getshell(void)
15577c478bd9Sstevel@tonic-gate {
15587c478bd9Sstevel@tonic-gate 	char	*s;
15597c478bd9Sstevel@tonic-gate 	char	*sh;
15607c478bd9Sstevel@tonic-gate 	uid_t	u;
15617c478bd9Sstevel@tonic-gate 	int	j;
15627c478bd9Sstevel@tonic-gate 
15637c478bd9Sstevel@tonic-gate 	if (((sh = getenv("SHELL")) != 0) && *sh == '/') {
15647c478bd9Sstevel@tonic-gate 		if (u = getuid()) {
15657c478bd9Sstevel@tonic-gate 			if ((u != geteuid() || getgid() != getegid()) &&
15663d63ea05Sas 			    access(sh, 2) == 0)
15677c478bd9Sstevel@tonic-gate 				goto defshell;
15687c478bd9Sstevel@tonic-gate 			s = strrchr(sh, '/');
15697c478bd9Sstevel@tonic-gate 			*s = 0;
15707c478bd9Sstevel@tonic-gate 			j = access(sh, 2);
15717c478bd9Sstevel@tonic-gate 			*s = '/';
15727c478bd9Sstevel@tonic-gate 			if (!j) goto defshell;
15737c478bd9Sstevel@tonic-gate 		}
15747c478bd9Sstevel@tonic-gate 		return (sh);
15757c478bd9Sstevel@tonic-gate 	}
15767c478bd9Sstevel@tonic-gate defshell:
15777c478bd9Sstevel@tonic-gate 	return ("/usr/bin/sh");
15787c478bd9Sstevel@tonic-gate }
15797c478bd9Sstevel@tonic-gate 
15807c478bd9Sstevel@tonic-gate /*
15817c478bd9Sstevel@tonic-gate  * the following functions implement the added "-ls" option
15827c478bd9Sstevel@tonic-gate  */
15837c478bd9Sstevel@tonic-gate 
15847c478bd9Sstevel@tonic-gate #include <utmpx.h>
15857c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
15867c478bd9Sstevel@tonic-gate 
15877c478bd9Sstevel@tonic-gate struct		utmpx utmpx;
15887c478bd9Sstevel@tonic-gate #define	NMAX	(sizeof (utmpx.ut_name))
15897c478bd9Sstevel@tonic-gate #define	SCPYN(a, b)	(void) strncpy(a, b, NMAX)
15907c478bd9Sstevel@tonic-gate 
15917c478bd9Sstevel@tonic-gate #define	NUID	64
15927c478bd9Sstevel@tonic-gate #define	NGID	64
15937c478bd9Sstevel@tonic-gate 
15947c478bd9Sstevel@tonic-gate static struct ncache {
15957c478bd9Sstevel@tonic-gate 	int	id;
15967c478bd9Sstevel@tonic-gate 	char	name[NMAX+1];
15977c478bd9Sstevel@tonic-gate } nc[NUID], gc[NGID];
15987c478bd9Sstevel@tonic-gate 
15997c478bd9Sstevel@tonic-gate /*
16007c478bd9Sstevel@tonic-gate  * This function assumes that the password file is hashed
16017c478bd9Sstevel@tonic-gate  * (or some such) to allow fast access based on a name key.
16027c478bd9Sstevel@tonic-gate  */
16037c478bd9Sstevel@tonic-gate static char *
16047c478bd9Sstevel@tonic-gate getname(uid_t uid)
16057c478bd9Sstevel@tonic-gate {
16067c478bd9Sstevel@tonic-gate 	struct passwd *pw;
16077c478bd9Sstevel@tonic-gate 	int cp;
16087c478bd9Sstevel@tonic-gate 
16097c478bd9Sstevel@tonic-gate #if	(((NUID) & ((NUID) - 1)) != 0)
16107c478bd9Sstevel@tonic-gate 	cp = uid % (NUID);
16117c478bd9Sstevel@tonic-gate #else
16127c478bd9Sstevel@tonic-gate 	cp = uid & ((NUID) - 1);
16137c478bd9Sstevel@tonic-gate #endif
1614f48205beScasper 	if (nc[cp].id == uid && nc[cp].name[0])
16157c478bd9Sstevel@tonic-gate 		return (nc[cp].name);
16167c478bd9Sstevel@tonic-gate 	pw = getpwuid(uid);
16177c478bd9Sstevel@tonic-gate 	if (!pw)
16187c478bd9Sstevel@tonic-gate 		return (0);
16197c478bd9Sstevel@tonic-gate 	nc[cp].id = uid;
16207c478bd9Sstevel@tonic-gate 	SCPYN(nc[cp].name, pw->pw_name);
16217c478bd9Sstevel@tonic-gate 	return (nc[cp].name);
16227c478bd9Sstevel@tonic-gate }
16237c478bd9Sstevel@tonic-gate 
16247c478bd9Sstevel@tonic-gate /*
16257c478bd9Sstevel@tonic-gate  * This function assumes that the group file is hashed
16267c478bd9Sstevel@tonic-gate  * (or some such) to allow fast access based on a name key.
16277c478bd9Sstevel@tonic-gate  */
16287c478bd9Sstevel@tonic-gate static char *
16297c478bd9Sstevel@tonic-gate getgroup(gid_t gid)
16307c478bd9Sstevel@tonic-gate {
16317c478bd9Sstevel@tonic-gate 	struct group *gr;
16327c478bd9Sstevel@tonic-gate 	int cp;
16337c478bd9Sstevel@tonic-gate 
16347c478bd9Sstevel@tonic-gate #if	(((NGID) & ((NGID) - 1)) != 0)
16357c478bd9Sstevel@tonic-gate 	cp = gid % (NGID);
16367c478bd9Sstevel@tonic-gate #else
16377c478bd9Sstevel@tonic-gate 	cp = gid & ((NGID) - 1);
16387c478bd9Sstevel@tonic-gate #endif
1639f48205beScasper 	if (gc[cp].id == gid && gc[cp].name[0])
16407c478bd9Sstevel@tonic-gate 		return (gc[cp].name);
16417c478bd9Sstevel@tonic-gate 	gr = getgrgid(gid);
16427c478bd9Sstevel@tonic-gate 	if (!gr)
16437c478bd9Sstevel@tonic-gate 		return (0);
16447c478bd9Sstevel@tonic-gate 	gc[cp].id = gid;
16457c478bd9Sstevel@tonic-gate 	SCPYN(gc[cp].name, gr->gr_name);
16467c478bd9Sstevel@tonic-gate 	return (gc[cp].name);
16477c478bd9Sstevel@tonic-gate }
16487c478bd9Sstevel@tonic-gate 
16497c478bd9Sstevel@tonic-gate #define	permoffset(who)		((who) * 3)
16507c478bd9Sstevel@tonic-gate #define	permission(who, type)	((type) >> permoffset(who))
16517c478bd9Sstevel@tonic-gate #define	kbytes(bytes)		(((bytes) + 1023) / 1024)
16527c478bd9Sstevel@tonic-gate 
16537c478bd9Sstevel@tonic-gate static int
165427d3a169SToomas Soome list(const char *file, const struct stat *stp)
16557c478bd9Sstevel@tonic-gate {
16567c478bd9Sstevel@tonic-gate 	char pmode[32], uname[32], gname[32], fsize[32], ftime[32];
1657fa9e4066Sahrens 	int trivial;
16587c478bd9Sstevel@tonic-gate 
16597c478bd9Sstevel@tonic-gate /*
16607c478bd9Sstevel@tonic-gate  * Each line below contains the relevant permission (column 1) and character
16617c478bd9Sstevel@tonic-gate  * shown when  the corresponding execute bit is either clear (column 2)
16627c478bd9Sstevel@tonic-gate  * or set (column 3)
16637c478bd9Sstevel@tonic-gate  * These permissions are as shown by ls(1b)
16647c478bd9Sstevel@tonic-gate  */
16657c478bd9Sstevel@tonic-gate 	static long special[] = {	S_ISUID, 'S', 's',
16667c478bd9Sstevel@tonic-gate 					S_ISGID, 'S', 's',
16677c478bd9Sstevel@tonic-gate 					S_ISVTX, 'T', 't' };
16687c478bd9Sstevel@tonic-gate 
16697c478bd9Sstevel@tonic-gate 	static time_t sixmonthsago = -1;
16707c478bd9Sstevel@tonic-gate #ifdef	S_IFLNK
16717c478bd9Sstevel@tonic-gate 	char flink[MAXPATHLEN + 1];
16727c478bd9Sstevel@tonic-gate #endif
16737c478bd9Sstevel@tonic-gate 	int who;
16747c478bd9Sstevel@tonic-gate 	char *cp;
167527d3a169SToomas Soome 	const char *tailname;
16767c478bd9Sstevel@tonic-gate 	time_t now;
16777c478bd9Sstevel@tonic-gate 	long long ksize;
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate 	if (file == NULL || stp == NULL)
16807c478bd9Sstevel@tonic-gate 		return (-1);
16817c478bd9Sstevel@tonic-gate 
16827c478bd9Sstevel@tonic-gate 	(void) time(&now);
16837c478bd9Sstevel@tonic-gate 	if (sixmonthsago == -1)
16847c478bd9Sstevel@tonic-gate 		sixmonthsago = now - 6L*30L*24L*60L*60L;
16857c478bd9Sstevel@tonic-gate 
16867c478bd9Sstevel@tonic-gate 	switch (stp->st_mode & S_IFMT) {
16877c478bd9Sstevel@tonic-gate #ifdef	S_IFDIR
16887c478bd9Sstevel@tonic-gate 	case S_IFDIR:	/* directory */
16897c478bd9Sstevel@tonic-gate 		pmode[0] = 'd';
16907c478bd9Sstevel@tonic-gate 		break;
16917c478bd9Sstevel@tonic-gate #endif
16927c478bd9Sstevel@tonic-gate #ifdef	S_IFCHR
16937c478bd9Sstevel@tonic-gate 	case S_IFCHR:	/* character special */
16947c478bd9Sstevel@tonic-gate 		pmode[0] = 'c';
16957c478bd9Sstevel@tonic-gate 		break;
16967c478bd9Sstevel@tonic-gate #endif
16977c478bd9Sstevel@tonic-gate #ifdef	S_IFBLK
16987c478bd9Sstevel@tonic-gate 	case S_IFBLK:	/* block special */
16997c478bd9Sstevel@tonic-gate 		pmode[0] = 'b';
17007c478bd9Sstevel@tonic-gate 		break;
17017c478bd9Sstevel@tonic-gate #endif
17027c478bd9Sstevel@tonic-gate #ifdef	S_IFIFO
17037c478bd9Sstevel@tonic-gate 	case S_IFIFO:	/* fifo special */
17047c478bd9Sstevel@tonic-gate 		pmode[0] = 'p';
17057c478bd9Sstevel@tonic-gate 		break;
17067c478bd9Sstevel@tonic-gate #endif
17077c478bd9Sstevel@tonic-gate #ifdef	S_IFLNK
17087c478bd9Sstevel@tonic-gate 	case S_IFLNK:	/* symbolic link */
17097c478bd9Sstevel@tonic-gate 		pmode[0] = 'l';
17107c478bd9Sstevel@tonic-gate 		break;
17117c478bd9Sstevel@tonic-gate #endif
17127c478bd9Sstevel@tonic-gate #ifdef	S_IFSOCK
17137c478bd9Sstevel@tonic-gate 	case S_IFSOCK:	/* socket */
17147c478bd9Sstevel@tonic-gate 		pmode[0] = 's';
17157c478bd9Sstevel@tonic-gate 		break;
17167c478bd9Sstevel@tonic-gate #endif
17177c478bd9Sstevel@tonic-gate #ifdef	S_IFDOOR
17187c478bd9Sstevel@tonic-gate 	case S_IFDOOR:	/* door */
17197c478bd9Sstevel@tonic-gate 		pmode[0] = 'D';
17207c478bd9Sstevel@tonic-gate 		break;
17217c478bd9Sstevel@tonic-gate #endif
17227c478bd9Sstevel@tonic-gate #ifdef	S_IFREG
17237c478bd9Sstevel@tonic-gate 	case S_IFREG:	/* regular */
17247c478bd9Sstevel@tonic-gate 		pmode[0] = '-';
17257c478bd9Sstevel@tonic-gate 		break;
17267c478bd9Sstevel@tonic-gate #endif
17277c478bd9Sstevel@tonic-gate 	default:
17287c478bd9Sstevel@tonic-gate 		pmode[0] = '?';
17297c478bd9Sstevel@tonic-gate 		break;
17307c478bd9Sstevel@tonic-gate 	}
17317c478bd9Sstevel@tonic-gate 
17327c478bd9Sstevel@tonic-gate 	for (who = 0; who < 3; who++) {
17337c478bd9Sstevel@tonic-gate 		int is_exec =  stp->st_mode & permission(who, S_IEXEC)? 1 : 0;
17347c478bd9Sstevel@tonic-gate 
17357c478bd9Sstevel@tonic-gate 		if (stp->st_mode & permission(who, S_IREAD))
17367c478bd9Sstevel@tonic-gate 			pmode[permoffset(who) + 1] = 'r';
17377c478bd9Sstevel@tonic-gate 		else
17387c478bd9Sstevel@tonic-gate 			pmode[permoffset(who) + 1] = '-';
17397c478bd9Sstevel@tonic-gate 
17407c478bd9Sstevel@tonic-gate 		if (stp->st_mode & permission(who, S_IWRITE))
17417c478bd9Sstevel@tonic-gate 			pmode[permoffset(who) + 2] = 'w';
17427c478bd9Sstevel@tonic-gate 		else
17437c478bd9Sstevel@tonic-gate 			pmode[permoffset(who) + 2] = '-';
17447c478bd9Sstevel@tonic-gate 
17457c478bd9Sstevel@tonic-gate 		if (stp->st_mode & special[who * 3])
17467c478bd9Sstevel@tonic-gate 			pmode[permoffset(who) + 3] =
174727d3a169SToomas Soome 			    special[who * 3 + 1 + is_exec];
17487c478bd9Sstevel@tonic-gate 		else if (is_exec)
17497c478bd9Sstevel@tonic-gate 			pmode[permoffset(who) + 3] = 'x';
17507c478bd9Sstevel@tonic-gate 		else
17517c478bd9Sstevel@tonic-gate 			pmode[permoffset(who) + 3] = '-';
17527c478bd9Sstevel@tonic-gate 	}
17537c478bd9Sstevel@tonic-gate 
17547c478bd9Sstevel@tonic-gate 	/*
17557c478bd9Sstevel@tonic-gate 	 * Need to get the tail of the file name, since we have
17567c478bd9Sstevel@tonic-gate 	 * already chdir()ed into the directory of the file
17577c478bd9Sstevel@tonic-gate 	 */
17587c478bd9Sstevel@tonic-gate 
17597c478bd9Sstevel@tonic-gate 	tailname = gettail(file);
17607c478bd9Sstevel@tonic-gate 
1761fa9e4066Sahrens 	trivial = acl_trivial(tailname);
1762fa9e4066Sahrens 	if (trivial == -1)
1763fa9e4066Sahrens 		trivial =  0;
1764fa9e4066Sahrens 
1765fa9e4066Sahrens 	if (trivial == 1)
17667c478bd9Sstevel@tonic-gate 		pmode[permoffset(who) + 1] = '+';
17677c478bd9Sstevel@tonic-gate 	else
17687c478bd9Sstevel@tonic-gate 		pmode[permoffset(who) + 1] = ' ';
17697c478bd9Sstevel@tonic-gate 
17707c478bd9Sstevel@tonic-gate 	pmode[permoffset(who) + 2] = '\0';
17717c478bd9Sstevel@tonic-gate 
17727c478bd9Sstevel@tonic-gate 	/*
17737c478bd9Sstevel@tonic-gate 	 * Prepare uname and gname.  Always add a space afterwards
17747c478bd9Sstevel@tonic-gate 	 * to keep columns from running together.
17757c478bd9Sstevel@tonic-gate 	 */
17767c478bd9Sstevel@tonic-gate 	cp = getname(stp->st_uid);
17777c478bd9Sstevel@tonic-gate 	if (cp != NULL)
17787c478bd9Sstevel@tonic-gate 		(void) sprintf(uname, "%-8s ", cp);
17797c478bd9Sstevel@tonic-gate 	else
1780f48205beScasper 		(void) sprintf(uname, "%-8u ", stp->st_uid);
17817c478bd9Sstevel@tonic-gate 
17827c478bd9Sstevel@tonic-gate 	cp = getgroup(stp->st_gid);
17837c478bd9Sstevel@tonic-gate 	if (cp != NULL)
17847c478bd9Sstevel@tonic-gate 		(void) sprintf(gname, "%-8s ", cp);
17857c478bd9Sstevel@tonic-gate 	else
1786f48205beScasper 		(void) sprintf(gname, "%-8u ", stp->st_gid);
17877c478bd9Sstevel@tonic-gate 
17887c478bd9Sstevel@tonic-gate 	if (pmode[0] == 'b' || pmode[0] == 'c')
17897c478bd9Sstevel@tonic-gate 		(void) sprintf(fsize, "%3ld,%4ld",
179027d3a169SToomas Soome 		    major(stp->st_rdev), minor(stp->st_rdev));
17917c478bd9Sstevel@tonic-gate 	else {
17927c478bd9Sstevel@tonic-gate 		(void) sprintf(fsize, (stp->st_size < 100000000) ?
179327d3a169SToomas Soome 		    "%8lld" : "%lld", stp->st_size);
17947c478bd9Sstevel@tonic-gate #ifdef	S_IFLNK
17957c478bd9Sstevel@tonic-gate 		if (pmode[0] == 'l') {
17967c478bd9Sstevel@tonic-gate 			who = readlink(tailname, flink, sizeof (flink) - 1);
17977c478bd9Sstevel@tonic-gate 
17987c478bd9Sstevel@tonic-gate 			if (who >= 0)
17997c478bd9Sstevel@tonic-gate 				flink[who] = '\0';
18007c478bd9Sstevel@tonic-gate 			else
18017c478bd9Sstevel@tonic-gate 				flink[0] = '\0';
18027c478bd9Sstevel@tonic-gate 		}
18037c478bd9Sstevel@tonic-gate #endif
18047c478bd9Sstevel@tonic-gate 	}
18057c478bd9Sstevel@tonic-gate 
18067c478bd9Sstevel@tonic-gate 	cp = ctime(&stp->st_mtime);
18077c478bd9Sstevel@tonic-gate 	if (stp->st_mtime < sixmonthsago || stp->st_mtime > now)
18087c478bd9Sstevel@tonic-gate 		(void) sprintf(ftime, "%-7.7s %-4.4s", cp + 4, cp + 20);
18097c478bd9Sstevel@tonic-gate 	else
18107c478bd9Sstevel@tonic-gate 		(void) sprintf(ftime, "%-12.12s", cp + 4);
18117c478bd9Sstevel@tonic-gate 
18127c478bd9Sstevel@tonic-gate 	(void) printf((stp->st_ino < 100000) ? "%5llu " :
181327d3a169SToomas Soome 	    "%llu ", stp->st_ino);  /* inode #	*/
18147c478bd9Sstevel@tonic-gate #ifdef	S_IFSOCK
18157c478bd9Sstevel@tonic-gate 	ksize = (long long) kbytes(ldbtob(stp->st_blocks)); /* kbytes */
18167c478bd9Sstevel@tonic-gate #else
18177c478bd9Sstevel@tonic-gate 	ksize = (long long) kbytes(stp->st_size); /* kbytes */
18187c478bd9Sstevel@tonic-gate #endif
18197c478bd9Sstevel@tonic-gate 	(void) printf((ksize < 10000) ? "%4lld " : "%lld ", ksize);
18207c478bd9Sstevel@tonic-gate #ifdef	S_IFLNK
182127d3a169SToomas Soome 	(void) printf("%s %2ld %s%s%s %s %s%s%s\n",
182227d3a169SToomas Soome 	    pmode,					/* protection	*/
182327d3a169SToomas Soome 	    stp->st_nlink,				/* # of links	*/
182427d3a169SToomas Soome 	    uname,					/* owner	*/
182527d3a169SToomas Soome 	    gname,					/* group	*/
182627d3a169SToomas Soome 	    fsize,					/* # of bytes	*/
182727d3a169SToomas Soome 	    ftime,					/* modify time	*/
182827d3a169SToomas Soome 	    file,					/* name		*/
182927d3a169SToomas Soome 	    (pmode[0] == 'l') ? " -> " : "",
183027d3a169SToomas Soome 	    (pmode[0] == 'l') ? flink  : "");		/* symlink	*/
18317c478bd9Sstevel@tonic-gate #else
183227d3a169SToomas Soome 	(void) printf("%s %2ld %s%s%s %s %s\n",
183327d3a169SToomas Soome 	    pmode,					/* protection	*/
183427d3a169SToomas Soome 	    stp->st_nlink,				/* # of links	*/
183527d3a169SToomas Soome 	    uname,					/* owner	*/
183627d3a169SToomas Soome 	    gname,					/* group	*/
183727d3a169SToomas Soome 	    fsize,					/* # of bytes	*/
183827d3a169SToomas Soome 	    ftime,					/* modify time	*/
183927d3a169SToomas Soome 	    file);					/* name		*/
18407c478bd9Sstevel@tonic-gate #endif
18417c478bd9Sstevel@tonic-gate 
18427c478bd9Sstevel@tonic-gate 	return (0);
18437c478bd9Sstevel@tonic-gate }
18447c478bd9Sstevel@tonic-gate 
18457c478bd9Sstevel@tonic-gate static char *
18467c478bd9Sstevel@tonic-gate new_string(char *s)
18477c478bd9Sstevel@tonic-gate {
18487c478bd9Sstevel@tonic-gate 	char *p = strdup(s);
18497c478bd9Sstevel@tonic-gate 
18507c478bd9Sstevel@tonic-gate 	if (p)
18517c478bd9Sstevel@tonic-gate 		return (p);
18527c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("%s: out of memory\n"), cmdname);
18537c478bd9Sstevel@tonic-gate 	exit(1);
18547c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
18557c478bd9Sstevel@tonic-gate }
18567c478bd9Sstevel@tonic-gate 
18577c478bd9Sstevel@tonic-gate /*
18587c478bd9Sstevel@tonic-gate  * Read remote file system types from REMOTE_FS into the
18597c478bd9Sstevel@tonic-gate  * remote_fstypes array.
18607c478bd9Sstevel@tonic-gate  */
18617c478bd9Sstevel@tonic-gate static void
186227d3a169SToomas Soome init_remote_fs(void)
18637c478bd9Sstevel@tonic-gate {
18647c478bd9Sstevel@tonic-gate 	FILE    *fp;
18657c478bd9Sstevel@tonic-gate 	char    line_buf[LINEBUF_SIZE];
18667c478bd9Sstevel@tonic-gate 
18677c478bd9Sstevel@tonic-gate 	if ((fp = fopen(REMOTE_FS, "r")) == NULL) {
18687c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
18693d63ea05Sas 		    gettext("%s: Warning: can't open %s, ignored\n"),
18703d63ea05Sas 		    REMOTE_FS, cmdname);
18717c478bd9Sstevel@tonic-gate 		/* Use default string name for NFS */
18727c478bd9Sstevel@tonic-gate 		remote_fstypes[fstype_index++] = "nfs";
18737c478bd9Sstevel@tonic-gate 		return;
18747c478bd9Sstevel@tonic-gate 	}
18757c478bd9Sstevel@tonic-gate 
18767c478bd9Sstevel@tonic-gate 	while (fgets(line_buf, sizeof (line_buf), fp) != NULL) {
18777c478bd9Sstevel@tonic-gate 		char buf[LINEBUF_SIZE];
18787c478bd9Sstevel@tonic-gate 
18797c478bd9Sstevel@tonic-gate 		/* LINTED - unbounded string specifier */
18807c478bd9Sstevel@tonic-gate 		(void) sscanf(line_buf, "%s", buf);
18817c478bd9Sstevel@tonic-gate 		remote_fstypes[fstype_index++] = new_string(buf);
18827c478bd9Sstevel@tonic-gate 
18837c478bd9Sstevel@tonic-gate 		if (fstype_index == N_FSTYPES)
18847c478bd9Sstevel@tonic-gate 			break;
18857c478bd9Sstevel@tonic-gate 	}
18867c478bd9Sstevel@tonic-gate 	(void) fclose(fp);
18877c478bd9Sstevel@tonic-gate }
18887c478bd9Sstevel@tonic-gate 
18897c478bd9Sstevel@tonic-gate #define	NPERM	30			/* Largest machine */
18907c478bd9Sstevel@tonic-gate 
18917c478bd9Sstevel@tonic-gate /*
18927c478bd9Sstevel@tonic-gate  * The PERM struct is the machine that builds permissions.  The p_special
18937c478bd9Sstevel@tonic-gate  * field contains what permissions need to be checked at run-time in
18947c478bd9Sstevel@tonic-gate  * getmode().  This is one of 'X', 'u', 'g', or 'o'.  It contains '\0' to
18957c478bd9Sstevel@tonic-gate  * indicate normal processing.
18967c478bd9Sstevel@tonic-gate  */
18977c478bd9Sstevel@tonic-gate typedef	struct	PERMST	{
18987c478bd9Sstevel@tonic-gate 	ushort_t	p_who;		/* Range of permission (e.g. ugo) */
18997c478bd9Sstevel@tonic-gate 	ushort_t	p_perm;		/* Bits to turn on, off, assign */
19007c478bd9Sstevel@tonic-gate 	uchar_t		p_op;		/* Operation: + - = */
19017c478bd9Sstevel@tonic-gate 	uchar_t		p_special;	/* Special handling? */
19027c478bd9Sstevel@tonic-gate }	PERMST;
19037c478bd9Sstevel@tonic-gate 
19047c478bd9Sstevel@tonic-gate #ifndef	S_ISVTX
19057c478bd9Sstevel@tonic-gate #define	S_ISVTX	0			/* Not .1 */
19067c478bd9Sstevel@tonic-gate #endif
19077c478bd9Sstevel@tonic-gate 
19087c478bd9Sstevel@tonic-gate /* Mask values */
19097c478bd9Sstevel@tonic-gate #define	P_A	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) /* allbits */
19107c478bd9Sstevel@tonic-gate #define	P_U	(S_ISUID|S_ISVTX|S_IRWXU)		/* user */
19117c478bd9Sstevel@tonic-gate #define	P_G	(S_ISGID|S_ISVTX|S_IRWXG)		/* group */
19127c478bd9Sstevel@tonic-gate #define	P_O	(S_ISVTX|S_IRWXO)			/* other */
19137c478bd9Sstevel@tonic-gate 
19147c478bd9Sstevel@tonic-gate static	int	iswho(int c);
19157c478bd9Sstevel@tonic-gate static	int	isop(int c);
19167c478bd9Sstevel@tonic-gate static	int	isperm(PERMST *pp, int c);
19177c478bd9Sstevel@tonic-gate 
19187c478bd9Sstevel@tonic-gate static	PERMST	machine[NPERM];		/* Permission construction machine */
19197c478bd9Sstevel@tonic-gate static	PERMST	*endp;			/* Last used PERM structure */
19207c478bd9Sstevel@tonic-gate 
19217c478bd9Sstevel@tonic-gate static	uint_t	nowho;			/* No who for this mode (DOS kludge) */
19227c478bd9Sstevel@tonic-gate 
19237c478bd9Sstevel@tonic-gate /*
19247c478bd9Sstevel@tonic-gate  * Read an ASCII string containing the symbolic/octal mode and
19257c478bd9Sstevel@tonic-gate  * compile an automaton that recognizes it.  The return value
19267c478bd9Sstevel@tonic-gate  * is NULL if everything is OK, otherwise it is -1.
19277c478bd9Sstevel@tonic-gate  */
19287c478bd9Sstevel@tonic-gate static int
192927d3a169SToomas Soome readmode(const char *ascmode)
19307c478bd9Sstevel@tonic-gate {
19317c478bd9Sstevel@tonic-gate 	const char *amode = ascmode;
19327c478bd9Sstevel@tonic-gate 	PERMST *pp;
19337c478bd9Sstevel@tonic-gate 	int seen_X;
19347c478bd9Sstevel@tonic-gate 
19357c478bd9Sstevel@tonic-gate 	nowho = 0;
19367c478bd9Sstevel@tonic-gate 	seen_X = 0;
19377c478bd9Sstevel@tonic-gate 	pp = &machine[0];
19387c478bd9Sstevel@tonic-gate 	if (*amode >= '0' && *amode <= '7') {
19397c478bd9Sstevel@tonic-gate 		int mode;
19407c478bd9Sstevel@tonic-gate 
19417c478bd9Sstevel@tonic-gate 		mode = 0;
19427c478bd9Sstevel@tonic-gate 		while (*amode >= '0' && *amode <= '7')
19437c478bd9Sstevel@tonic-gate 			mode = (mode<<3) + *amode++ - '0';
19447c478bd9Sstevel@tonic-gate 		if (*amode != '\0')
19457c478bd9Sstevel@tonic-gate 			return (-1);
19467c478bd9Sstevel@tonic-gate #if	S_ISUID != 04000 || S_ISGID != 02000 || \
19477c478bd9Sstevel@tonic-gate 	S_IRUSR != 0400 || S_IWUSR != 0200 || S_IXUSR != 0100 || \
19487c478bd9Sstevel@tonic-gate 	S_IRGRP != 0040 || S_IWGRP != 0020 || S_IXGRP != 0010 || \
19497c478bd9Sstevel@tonic-gate 	S_IROTH != 0004 || S_IWOTH != 0002 || S_IXOTH != 0001
19507c478bd9Sstevel@tonic-gate 		/*
19517c478bd9Sstevel@tonic-gate 		 * There is no requirement of the octal mode bits being
19527c478bd9Sstevel@tonic-gate 		 * the same as the S_ macros.
19537c478bd9Sstevel@tonic-gate 		 */
19547c478bd9Sstevel@tonic-gate 	{
19557c478bd9Sstevel@tonic-gate 		mode_t mapping[] = {
19567c478bd9Sstevel@tonic-gate 			S_IXOTH, S_IWOTH, S_IROTH,
19577c478bd9Sstevel@tonic-gate 			S_IXGRP, S_IWGRP, S_IRGRP,
19587c478bd9Sstevel@tonic-gate 			S_IXUSR, S_IWUSR, S_IRUSR,
19597c478bd9Sstevel@tonic-gate 			S_ISGID, S_ISUID,
19607c478bd9Sstevel@tonic-gate 			0
19617c478bd9Sstevel@tonic-gate 		};
19627c478bd9Sstevel@tonic-gate 		int i, newmode = 0;
19637c478bd9Sstevel@tonic-gate 
19647c478bd9Sstevel@tonic-gate 		for (i = 0; mapping[i] != 0; i++)
19657c478bd9Sstevel@tonic-gate 			if (mode & (1<<i))
19667c478bd9Sstevel@tonic-gate 				newmode |= mapping[i];
19677c478bd9Sstevel@tonic-gate 		mode = newmode;
19687c478bd9Sstevel@tonic-gate 	}
19697c478bd9Sstevel@tonic-gate #endif
19707c478bd9Sstevel@tonic-gate 		pp->p_who = P_A;
19717c478bd9Sstevel@tonic-gate 		pp->p_perm = mode;
19727c478bd9Sstevel@tonic-gate 		pp->p_op = '=';
19737c478bd9Sstevel@tonic-gate 	} else	for (;;) {
19747c478bd9Sstevel@tonic-gate 		int t;
19757c478bd9Sstevel@tonic-gate 		int who = 0;
19767c478bd9Sstevel@tonic-gate 
19777c478bd9Sstevel@tonic-gate 		while ((t = iswho(*amode)) != 0) {
19787c478bd9Sstevel@tonic-gate 			++amode;
19797c478bd9Sstevel@tonic-gate 			who |= t;
19807c478bd9Sstevel@tonic-gate 		}
19817c478bd9Sstevel@tonic-gate 		if (who == 0) {
19827c478bd9Sstevel@tonic-gate 			mode_t currmask;
19837c478bd9Sstevel@tonic-gate 			(void) umask(currmask = umask((mode_t)0));
19847c478bd9Sstevel@tonic-gate 
19857c478bd9Sstevel@tonic-gate 			/*
19867c478bd9Sstevel@tonic-gate 			 * If no who specified, must use contents of
19877c478bd9Sstevel@tonic-gate 			 * umask to determine which bits to flip.  This
19887c478bd9Sstevel@tonic-gate 			 * is POSIX/V7/BSD behaviour, but not SVID.
19897c478bd9Sstevel@tonic-gate 			 */
19907c478bd9Sstevel@tonic-gate 			who = (~currmask)&P_A;
19917c478bd9Sstevel@tonic-gate 			++nowho;
19927c478bd9Sstevel@tonic-gate 		} else
19937c478bd9Sstevel@tonic-gate 			nowho = 0;
19947c478bd9Sstevel@tonic-gate 	samewho:
19957c478bd9Sstevel@tonic-gate 		if (!isop(pp->p_op = *amode++))
19967c478bd9Sstevel@tonic-gate 			return (-1);
19977c478bd9Sstevel@tonic-gate 		pp->p_perm = 0;
19987c478bd9Sstevel@tonic-gate 		pp->p_special = 0;
19997c478bd9Sstevel@tonic-gate 		while ((t = isperm(pp, *amode)) != 0) {
20007c478bd9Sstevel@tonic-gate 			if (pp->p_special == 'X') {
20017c478bd9Sstevel@tonic-gate 				seen_X = 1;
20027c478bd9Sstevel@tonic-gate 
20037c478bd9Sstevel@tonic-gate 				if (pp->p_perm != 0) {
20047c478bd9Sstevel@tonic-gate 					ushort_t op;
20057c478bd9Sstevel@tonic-gate 
20067c478bd9Sstevel@tonic-gate 					/*
20077c478bd9Sstevel@tonic-gate 					 * Remember the 'who' for the previous
20087c478bd9Sstevel@tonic-gate 					 * transformation.
20097c478bd9Sstevel@tonic-gate 					 */
20107c478bd9Sstevel@tonic-gate 					pp->p_who = who;
20117c478bd9Sstevel@tonic-gate 					pp->p_special = 0;
20127c478bd9Sstevel@tonic-gate 
20137c478bd9Sstevel@tonic-gate 					op = pp->p_op;
20147c478bd9Sstevel@tonic-gate 
20157c478bd9Sstevel@tonic-gate 					/* Keep 'X' separate */
20167c478bd9Sstevel@tonic-gate 					++pp;
20177c478bd9Sstevel@tonic-gate 					pp->p_special = 'X';
20187c478bd9Sstevel@tonic-gate 					pp->p_op = op;
20197c478bd9Sstevel@tonic-gate 				}
20207c478bd9Sstevel@tonic-gate 			} else if (seen_X) {
20217c478bd9Sstevel@tonic-gate 				ushort_t op;
20227c478bd9Sstevel@tonic-gate 
20237c478bd9Sstevel@tonic-gate 				/* Remember the 'who' for the X */
20247c478bd9Sstevel@tonic-gate 				pp->p_who = who;
20257c478bd9Sstevel@tonic-gate 
20267c478bd9Sstevel@tonic-gate 				op = pp->p_op;
20277c478bd9Sstevel@tonic-gate 
20287c478bd9Sstevel@tonic-gate 				/* Keep 'X' separate */
20297c478bd9Sstevel@tonic-gate 				++pp;
20307c478bd9Sstevel@tonic-gate 				pp->p_perm = 0;
20317c478bd9Sstevel@tonic-gate 				pp->p_special = 0;
20327c478bd9Sstevel@tonic-gate 				pp->p_op = op;
20337c478bd9Sstevel@tonic-gate 			}
20347c478bd9Sstevel@tonic-gate 			++amode;
20357c478bd9Sstevel@tonic-gate 			pp->p_perm |= t;
20367c478bd9Sstevel@tonic-gate 		}
20377c478bd9Sstevel@tonic-gate 
20387c478bd9Sstevel@tonic-gate 		/*
20397c478bd9Sstevel@tonic-gate 		 * These returned 0, but were actually parsed, so
20407c478bd9Sstevel@tonic-gate 		 * don't look at them again.
20417c478bd9Sstevel@tonic-gate 		 */
20427c478bd9Sstevel@tonic-gate 		switch (pp->p_special) {
20437c478bd9Sstevel@tonic-gate 		case 'u':
20447c478bd9Sstevel@tonic-gate 		case 'g':
20457c478bd9Sstevel@tonic-gate 		case 'o':
20467c478bd9Sstevel@tonic-gate 			++amode;
20477c478bd9Sstevel@tonic-gate 			break;
20487c478bd9Sstevel@tonic-gate 		}
20497c478bd9Sstevel@tonic-gate 		pp->p_who = who;
20507c478bd9Sstevel@tonic-gate 		switch (*amode) {
20517c478bd9Sstevel@tonic-gate 		case '\0':
20527c478bd9Sstevel@tonic-gate 			break;
20537c478bd9Sstevel@tonic-gate 
20547c478bd9Sstevel@tonic-gate 		case ',':
20557c478bd9Sstevel@tonic-gate 			++amode;
20567c478bd9Sstevel@tonic-gate 			++pp;
20577c478bd9Sstevel@tonic-gate 			continue;
20587c478bd9Sstevel@tonic-gate 
20597c478bd9Sstevel@tonic-gate 		default:
20607c478bd9Sstevel@tonic-gate 			++pp;
20617c478bd9Sstevel@tonic-gate 			goto samewho;
20627c478bd9Sstevel@tonic-gate 		}
20637c478bd9Sstevel@tonic-gate 		break;
20647c478bd9Sstevel@tonic-gate 	}
20657c478bd9Sstevel@tonic-gate 	endp = pp;
206627d3a169SToomas Soome 	return (0);
20677c478bd9Sstevel@tonic-gate }
20687c478bd9Sstevel@tonic-gate 
20697c478bd9Sstevel@tonic-gate /*
20707c478bd9Sstevel@tonic-gate  * Given a character from the mode, return the associated
20717c478bd9Sstevel@tonic-gate  * value as who (user designation) mask or 0 if this isn't valid.
20727c478bd9Sstevel@tonic-gate  */
20737c478bd9Sstevel@tonic-gate static int
207427d3a169SToomas Soome iswho(int c)
20757c478bd9Sstevel@tonic-gate {
20767c478bd9Sstevel@tonic-gate 	switch (c) {
20777c478bd9Sstevel@tonic-gate 	case 'a':
20787c478bd9Sstevel@tonic-gate 		return (P_A);
20797c478bd9Sstevel@tonic-gate 
20807c478bd9Sstevel@tonic-gate 	case 'u':
20817c478bd9Sstevel@tonic-gate 		return (P_U);
20827c478bd9Sstevel@tonic-gate 
20837c478bd9Sstevel@tonic-gate 	case 'g':
20847c478bd9Sstevel@tonic-gate 		return (P_G);
20857c478bd9Sstevel@tonic-gate 
20867c478bd9Sstevel@tonic-gate 	case 'o':
20877c478bd9Sstevel@tonic-gate 		return (P_O);
20887c478bd9Sstevel@tonic-gate 
20897c478bd9Sstevel@tonic-gate 	default:
20907c478bd9Sstevel@tonic-gate 		return (0);
20917c478bd9Sstevel@tonic-gate 	}
20927c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
20937c478bd9Sstevel@tonic-gate }
20947c478bd9Sstevel@tonic-gate 
20957c478bd9Sstevel@tonic-gate /*
20967c478bd9Sstevel@tonic-gate  * Return non-zero if this is a valid op code
20977c478bd9Sstevel@tonic-gate  * in a symbolic mode.
20987c478bd9Sstevel@tonic-gate  */
20997c478bd9Sstevel@tonic-gate static int
210027d3a169SToomas Soome isop(int c)
21017c478bd9Sstevel@tonic-gate {
21027c478bd9Sstevel@tonic-gate 	switch (c) {
21037c478bd9Sstevel@tonic-gate 	case '+':
21047c478bd9Sstevel@tonic-gate 	case '-':
21057c478bd9Sstevel@tonic-gate 	case '=':
21067c478bd9Sstevel@tonic-gate 		return (1);
21077c478bd9Sstevel@tonic-gate 
21087c478bd9Sstevel@tonic-gate 	default:
21097c478bd9Sstevel@tonic-gate 		return (0);
21107c478bd9Sstevel@tonic-gate 	}
21117c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
21127c478bd9Sstevel@tonic-gate }
21137c478bd9Sstevel@tonic-gate 
21147c478bd9Sstevel@tonic-gate /*
21157c478bd9Sstevel@tonic-gate  * Return the permission bits implied by this character or 0
21167c478bd9Sstevel@tonic-gate  * if it isn't valid.  Also returns 0 when the pseudo-permissions 'u', 'g', or
21177c478bd9Sstevel@tonic-gate  * 'o' are used, and sets pp->p_special to the one used.
21187c478bd9Sstevel@tonic-gate  */
21197c478bd9Sstevel@tonic-gate static int
212027d3a169SToomas Soome isperm(PERMST *pp, int c)
21217c478bd9Sstevel@tonic-gate {
21227c478bd9Sstevel@tonic-gate 	switch (c) {
21237c478bd9Sstevel@tonic-gate 	case 'u':
21247c478bd9Sstevel@tonic-gate 	case 'g':
21257c478bd9Sstevel@tonic-gate 	case 'o':
21267c478bd9Sstevel@tonic-gate 		pp->p_special = c;
21277c478bd9Sstevel@tonic-gate 		return (0);
21287c478bd9Sstevel@tonic-gate 
21297c478bd9Sstevel@tonic-gate 	case 'r':
21307c478bd9Sstevel@tonic-gate 		return (S_IRUSR|S_IRGRP|S_IROTH);
21317c478bd9Sstevel@tonic-gate 
21327c478bd9Sstevel@tonic-gate 	case 'w':
21337c478bd9Sstevel@tonic-gate 		return (S_IWUSR|S_IWGRP|S_IWOTH);
21347c478bd9Sstevel@tonic-gate 
21357c478bd9Sstevel@tonic-gate 	case 'x':
21367c478bd9Sstevel@tonic-gate 		return (S_IXUSR|S_IXGRP|S_IXOTH);
21377c478bd9Sstevel@tonic-gate 
21387c478bd9Sstevel@tonic-gate #if S_ISVTX != 0
21397c478bd9Sstevel@tonic-gate 	case 't':
21407c478bd9Sstevel@tonic-gate 		return (S_ISVTX);
21417c478bd9Sstevel@tonic-gate #endif
21427c478bd9Sstevel@tonic-gate 
21437c478bd9Sstevel@tonic-gate 	case 'X':
21447c478bd9Sstevel@tonic-gate 		pp->p_special = 'X';
21457c478bd9Sstevel@tonic-gate 		return (S_IXUSR|S_IXGRP|S_IXOTH);
21467c478bd9Sstevel@tonic-gate 
21477c478bd9Sstevel@tonic-gate #if S_ISVTX != 0
21487c478bd9Sstevel@tonic-gate 	case 'a':
21497c478bd9Sstevel@tonic-gate 		return (S_ISVTX);
21507c478bd9Sstevel@tonic-gate #endif
21517c478bd9Sstevel@tonic-gate 
21527c478bd9Sstevel@tonic-gate 	case 'h':
21537c478bd9Sstevel@tonic-gate 		return (S_ISUID);
21547c478bd9Sstevel@tonic-gate 
21557c478bd9Sstevel@tonic-gate 	/*
21567c478bd9Sstevel@tonic-gate 	 * This change makes:
21577c478bd9Sstevel@tonic-gate 	 *	chmod +s file
21587c478bd9Sstevel@tonic-gate 	 * set the system bit on dos but means that
21597c478bd9Sstevel@tonic-gate 	 *	chmod u+s file
21607c478bd9Sstevel@tonic-gate 	 *	chmod g+s file
21617c478bd9Sstevel@tonic-gate 	 *	chmod a+s file
21627c478bd9Sstevel@tonic-gate 	 * are all like UNIX.
21637c478bd9Sstevel@tonic-gate 	 */
21647c478bd9Sstevel@tonic-gate 	case 's':
21657c478bd9Sstevel@tonic-gate 		return (nowho ? S_ISGID : S_ISGID|S_ISUID);
21667c478bd9Sstevel@tonic-gate 
21677c478bd9Sstevel@tonic-gate 	default:
21687c478bd9Sstevel@tonic-gate 		return (0);
21697c478bd9Sstevel@tonic-gate 	}
21707c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
21717c478bd9Sstevel@tonic-gate }
21727c478bd9Sstevel@tonic-gate 
21737c478bd9Sstevel@tonic-gate /*
21747c478bd9Sstevel@tonic-gate  * Execute the automaton that is created by readmode()
21757c478bd9Sstevel@tonic-gate  * to generate the final mode that will be used.  This
21767c478bd9Sstevel@tonic-gate  * code is passed a starting mode that is usually the original
21777c478bd9Sstevel@tonic-gate  * mode of the file being changed (or 0).  Note that this mode must contain
21787c478bd9Sstevel@tonic-gate  * the file-type bits as well, so that S_ISDIR will succeed on directories.
21797c478bd9Sstevel@tonic-gate  */
21807c478bd9Sstevel@tonic-gate static mode_t
21817c478bd9Sstevel@tonic-gate getmode(mode_t startmode)
21827c478bd9Sstevel@tonic-gate {
21837c478bd9Sstevel@tonic-gate 	PERMST *pp;
21847c478bd9Sstevel@tonic-gate 	mode_t temp;
21857c478bd9Sstevel@tonic-gate 	mode_t perm;
21867c478bd9Sstevel@tonic-gate 
21877c478bd9Sstevel@tonic-gate 	for (pp = &machine[0]; pp <= endp; ++pp) {
21887c478bd9Sstevel@tonic-gate 		perm = (mode_t)0;
21897c478bd9Sstevel@tonic-gate 		/*
21907c478bd9Sstevel@tonic-gate 		 * For the special modes 'u', 'g' and 'o', the named portion
21917c478bd9Sstevel@tonic-gate 		 * of the mode refers to after the previous clause has been
21927c478bd9Sstevel@tonic-gate 		 * processed, while the 'X' mode refers to the contents of the
21937c478bd9Sstevel@tonic-gate 		 * mode before any clauses have been processed.
21947c478bd9Sstevel@tonic-gate 		 *
21957c478bd9Sstevel@tonic-gate 		 * References: P1003.2/D11.2, Section 4.7.7,
21967c478bd9Sstevel@tonic-gate 		 *  lines 2568-2570, 2578-2583
21977c478bd9Sstevel@tonic-gate 		 */
21987c478bd9Sstevel@tonic-gate 		switch (pp->p_special) {
21997c478bd9Sstevel@tonic-gate 		case 'u':
22007c478bd9Sstevel@tonic-gate 			temp = startmode & S_IRWXU;
22017c478bd9Sstevel@tonic-gate 			if (temp & (S_IRUSR|S_IRGRP|S_IROTH))
22027c478bd9Sstevel@tonic-gate 				perm |= ((S_IRUSR|S_IRGRP|S_IROTH) &
22037c478bd9Sstevel@tonic-gate 				    pp->p_who);
22047c478bd9Sstevel@tonic-gate 			if (temp & (S_IWUSR|S_IWGRP|S_IWOTH))
22057c478bd9Sstevel@tonic-gate 				perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who);
22067c478bd9Sstevel@tonic-gate 			if (temp & (S_IXUSR|S_IXGRP|S_IXOTH))
22077c478bd9Sstevel@tonic-gate 				perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who);
22087c478bd9Sstevel@tonic-gate 			break;
22097c478bd9Sstevel@tonic-gate 
22107c478bd9Sstevel@tonic-gate 		case 'g':
22117c478bd9Sstevel@tonic-gate 			temp = startmode & S_IRWXG;
22127c478bd9Sstevel@tonic-gate 			if (temp & (S_IRUSR|S_IRGRP|S_IROTH))
22137c478bd9Sstevel@tonic-gate 				perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & pp->p_who);
22147c478bd9Sstevel@tonic-gate 			if (temp & (S_IWUSR|S_IWGRP|S_IWOTH))
22157c478bd9Sstevel@tonic-gate 				perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who);
22167c478bd9Sstevel@tonic-gate 			if (temp & (S_IXUSR|S_IXGRP|S_IXOTH))
22177c478bd9Sstevel@tonic-gate 				perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who);
22187c478bd9Sstevel@tonic-gate 			break;
22197c478bd9Sstevel@tonic-gate 
22207c478bd9Sstevel@tonic-gate 		case 'o':
22217c478bd9Sstevel@tonic-gate 			temp = startmode & S_IRWXO;
22227c478bd9Sstevel@tonic-gate 			if (temp & (S_IRUSR|S_IRGRP|S_IROTH))
22237c478bd9Sstevel@tonic-gate 				perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & pp->p_who);
22247c478bd9Sstevel@tonic-gate 			if (temp & (S_IWUSR|S_IWGRP|S_IWOTH))
22257c478bd9Sstevel@tonic-gate 				perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who);
22267c478bd9Sstevel@tonic-gate 			if (temp & (S_IXUSR|S_IXGRP|S_IXOTH))
22277c478bd9Sstevel@tonic-gate 				perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who);
22287c478bd9Sstevel@tonic-gate 			break;
22297c478bd9Sstevel@tonic-gate 
22307c478bd9Sstevel@tonic-gate 		case 'X':
22317c478bd9Sstevel@tonic-gate 			perm = pp->p_perm;
22327c478bd9Sstevel@tonic-gate 			break;
22337c478bd9Sstevel@tonic-gate 
22347c478bd9Sstevel@tonic-gate 		default:
22357c478bd9Sstevel@tonic-gate 			perm = pp->p_perm;
22367c478bd9Sstevel@tonic-gate 			break;
22377c478bd9Sstevel@tonic-gate 		}
22387c478bd9Sstevel@tonic-gate 		switch (pp->p_op) {
22397c478bd9Sstevel@tonic-gate 		case '-':
22407c478bd9Sstevel@tonic-gate 			startmode &= ~(perm & pp->p_who);
22417c478bd9Sstevel@tonic-gate 			break;
22427c478bd9Sstevel@tonic-gate 
22437c478bd9Sstevel@tonic-gate 		case '=':
22447c478bd9Sstevel@tonic-gate 			startmode &= ~pp->p_who;
22457c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
22467c478bd9Sstevel@tonic-gate 		case '+':
22477c478bd9Sstevel@tonic-gate 			startmode |= (perm & pp->p_who);
22487c478bd9Sstevel@tonic-gate 			break;
22497c478bd9Sstevel@tonic-gate 		}
22507c478bd9Sstevel@tonic-gate 	}
22517c478bd9Sstevel@tonic-gate 	return (startmode);
22527c478bd9Sstevel@tonic-gate }
22537c478bd9Sstevel@tonic-gate 
22547c478bd9Sstevel@tonic-gate /*
22557c478bd9Sstevel@tonic-gate  * Returns the last component of a path name, unless it is
22567c478bd9Sstevel@tonic-gate  * an absolute path, in which case it returns the whole path
22577c478bd9Sstevel@tonic-gate  */
225827d3a169SToomas Soome static const char *
225927d3a169SToomas Soome gettail(const char *fname)
22607c478bd9Sstevel@tonic-gate {
226127d3a169SToomas Soome 	const char *base = fname;
22627c478bd9Sstevel@tonic-gate 
22637c478bd9Sstevel@tonic-gate 	if (*fname != '/') {
22647c478bd9Sstevel@tonic-gate 		if ((base = strrchr(fname, '/')) != NULL)
22657c478bd9Sstevel@tonic-gate 			base++;
22667c478bd9Sstevel@tonic-gate 		else
22677c478bd9Sstevel@tonic-gate 			base = fname;
22687c478bd9Sstevel@tonic-gate 	}
22697c478bd9Sstevel@tonic-gate 	return (base);
22707c478bd9Sstevel@tonic-gate }
2271