xref: /illumos-gate/usr/src/cmd/find/find.c (revision ba5b88a2af43e87b261a91f0dcb051dcc20e519b)
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
5d35170d6Srm88369  * Common Development and Distribution License (the "License").
6d35170d6Srm88369  * 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.
25f3a525d9SJohn Levon  * Copyright 2020 Joyent, Inc.
26*ba5b88a2SBill Sommerfeld  * Copyright 2023 Bill Sommerfeld <sommerfeld@alum.mit.edu>
277c478bd9Sstevel@tonic-gate  */
287c478bd9Sstevel@tonic-gate 
29da1a9cbeSjonb 
307c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
317c478bd9Sstevel@tonic-gate /*	  All Rights Reserved	*/
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate /*	Parts of this product may be derived from		*/
357c478bd9Sstevel@tonic-gate /*	Mortice Kern Systems Inc. and Berkeley 4.3 BSD systems.	*/
367c478bd9Sstevel@tonic-gate /*	licensed from  Mortice Kern Systems Inc. and		*/
377c478bd9Sstevel@tonic-gate /*	the University of California.				*/
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate /*
407c478bd9Sstevel@tonic-gate  * Copyright 1985, 1990 by Mortice Kern Systems Inc.  All rights reserved.
417c478bd9Sstevel@tonic-gate  */
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate #include <stdio.h>
447c478bd9Sstevel@tonic-gate #include <errno.h>
457c478bd9Sstevel@tonic-gate #include <pwd.h>
467c478bd9Sstevel@tonic-gate #include <grp.h>
477c478bd9Sstevel@tonic-gate #include <sys/types.h>
487c478bd9Sstevel@tonic-gate #include <sys/stat.h>
497c478bd9Sstevel@tonic-gate #include <sys/param.h>
507c478bd9Sstevel@tonic-gate #include <sys/acl.h>
517c478bd9Sstevel@tonic-gate #include <limits.h>
527c478bd9Sstevel@tonic-gate #include <unistd.h>
537c478bd9Sstevel@tonic-gate #include <stdlib.h>
547c478bd9Sstevel@tonic-gate #include <locale.h>
557c478bd9Sstevel@tonic-gate #include <string.h>
567c478bd9Sstevel@tonic-gate #include <strings.h>
577c478bd9Sstevel@tonic-gate #include <ctype.h>
587c478bd9Sstevel@tonic-gate #include <wait.h>
597c478bd9Sstevel@tonic-gate #include <fnmatch.h>
607c478bd9Sstevel@tonic-gate #include <langinfo.h>
617c478bd9Sstevel@tonic-gate #include <ftw.h>
629ab6dc39Schin #include <libgen.h>
63b34cd89aSYuri Pankov #include <err.h>
64b34cd89aSYuri Pankov #include <regex.h>
653d63ea05Sas145665 #include "getresponse.h"
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate #define	A_DAY		(long)(60*60*24)	/* a day full of seconds */
68da1a9cbeSjonb #define	A_MIN		(long)(60)
697c478bd9Sstevel@tonic-gate #define	BLKSIZ		512
707c478bd9Sstevel@tonic-gate #define	round(x, s)	(((x)+(s)-1)&~((s)-1))
717c478bd9Sstevel@tonic-gate #ifndef FTW_SLN
727c478bd9Sstevel@tonic-gate #define	FTW_SLN		7
737c478bd9Sstevel@tonic-gate #endif
747c478bd9Sstevel@tonic-gate #define	LINEBUF_SIZE		LINE_MAX	/* input or output lines */
757c478bd9Sstevel@tonic-gate #define	REMOTE_FS		"/etc/dfs/fstypes"
767c478bd9Sstevel@tonic-gate #define	N_FSTYPES		20
77d35170d6Srm88369 #define	SHELL_MAXARGS		253	/* see doexec() for description */
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate /*
807c478bd9Sstevel@tonic-gate  * This is the list of operations
817c478bd9Sstevel@tonic-gate  * F_USER and F_GROUP are named to avoid conflict with USER and GROUP defined
827c478bd9Sstevel@tonic-gate  * in sys/acl.h
837c478bd9Sstevel@tonic-gate  */
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate enum Command
867c478bd9Sstevel@tonic-gate {
87b34cd89aSYuri Pankov 	PRINT,
88b34cd89aSYuri Pankov 	ACL, AMIN, AND, ATIME, CMIN, CPIO, CSIZE, CTIME, DEPTH, EXEC, F_GROUP,
8905f32410SAndy Stormont 	F_GROUPACL, F_USER, F_USERACL, FOLLOW, FSTYPE, INAME, INUM, IPATH,
9005f32410SAndy Stormont 	IREGEX,	LINKS, LOCAL, LPAREN, LS, MAXDEPTH, MINDEPTH, MMIN, MOUNT,
9105f32410SAndy Stormont 	MTIME, NAME, NCPIO, NEWER, NOGRP, NOT, NOUSER, OK, OR, PATH, PERM,
92ab823b7fSPrasad Joshi 	PRINT0, PRUNE, REGEX, RPAREN, SIZE, TYPE, VARARGS, XATTR, DELETE
937c478bd9Sstevel@tonic-gate };
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate enum Type
967c478bd9Sstevel@tonic-gate {
977c478bd9Sstevel@tonic-gate 	Unary, Id, Num, Str, Exec, Cpio, Op
987c478bd9Sstevel@tonic-gate };
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate struct Args
1017c478bd9Sstevel@tonic-gate {
1027c478bd9Sstevel@tonic-gate 	char		name[10];
1037c478bd9Sstevel@tonic-gate 	enum Command	action;
1047c478bd9Sstevel@tonic-gate 	enum Type	type;
1057c478bd9Sstevel@tonic-gate };
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate /*
1087c478bd9Sstevel@tonic-gate  * Except for pathnames, these are the only legal arguments
1097c478bd9Sstevel@tonic-gate  */
1107c478bd9Sstevel@tonic-gate static struct Args commands[] =
1117c478bd9Sstevel@tonic-gate {
1127c478bd9Sstevel@tonic-gate 	"!",		NOT,		Op,
1137c478bd9Sstevel@tonic-gate 	"(",		LPAREN,		Unary,
1147c478bd9Sstevel@tonic-gate 	")",		RPAREN,		Unary,
1157c478bd9Sstevel@tonic-gate 	"-a",		AND,		Op,
116b34cd89aSYuri Pankov 	"-acl",		ACL,		Unary,
117da1a9cbeSjonb 	"-amin",	AMIN,		Num,
118b34cd89aSYuri Pankov 	"-and",		AND,		Op,
1197c478bd9Sstevel@tonic-gate 	"-atime",	ATIME,		Num,
120da1a9cbeSjonb 	"-cmin",	CMIN,		Num,
121b34cd89aSYuri Pankov 	"-cpio",	CPIO,		Cpio,
1227c478bd9Sstevel@tonic-gate 	"-ctime",	CTIME,		Num,
1237c478bd9Sstevel@tonic-gate 	"-depth",	DEPTH,		Unary,
124ab823b7fSPrasad Joshi 	"-delete",	DELETE,		Unary,
1257c478bd9Sstevel@tonic-gate 	"-exec",	EXEC,		Exec,
1267c478bd9Sstevel@tonic-gate 	"-follow",	FOLLOW,		Unary,
127b34cd89aSYuri Pankov 	"-fstype",	FSTYPE,		Str,
1287c478bd9Sstevel@tonic-gate 	"-group",	F_GROUP,	Num,
129b34cd89aSYuri Pankov 	"-groupacl",	F_GROUPACL,	Num,
130b34cd89aSYuri Pankov 	"-iname",	INAME,		Str,
1317c478bd9Sstevel@tonic-gate 	"-inum",	INUM,		Num,
13205f32410SAndy Stormont 	"-ipath",	IPATH,		Str,
133b34cd89aSYuri Pankov 	"-iregex",	IREGEX,		Str,
13403f45afcSYuri Pankov 	"-links",	LINKS,		Num,
13503f45afcSYuri Pankov 	"-local",	LOCAL,		Unary,
136b34cd89aSYuri Pankov 	"-ls",		LS,		Unary,
137b34cd89aSYuri Pankov 	"-maxdepth",	MAXDEPTH,	Num,
138b34cd89aSYuri Pankov 	"-mindepth",	MINDEPTH,	Num,
139da1a9cbeSjonb 	"-mmin",	MMIN,		Num,
140b34cd89aSYuri Pankov 	"-mount",	MOUNT,		Unary,
1417c478bd9Sstevel@tonic-gate 	"-mtime",	MTIME,		Num,
1427c478bd9Sstevel@tonic-gate 	"-name",	NAME,		Str,
1437c478bd9Sstevel@tonic-gate 	"-ncpio",	NCPIO,		Cpio,
1447c478bd9Sstevel@tonic-gate 	"-newer",	NEWER,		Str,
145b34cd89aSYuri Pankov 	"-nogroup",	NOGRP,		Unary,
146b34cd89aSYuri Pankov 	"-not",		NOT,		Op,
147b34cd89aSYuri Pankov 	"-nouser",	NOUSER,		Unary,
1487c478bd9Sstevel@tonic-gate 	"-o",		OR,		Op,
1497c478bd9Sstevel@tonic-gate 	"-ok",		OK,		Exec,
150b34cd89aSYuri Pankov 	"-or",		OR,		Op,
15105f32410SAndy Stormont 	"-path",	PATH,		Str,
1527c478bd9Sstevel@tonic-gate 	"-perm",	PERM,		Num,
1537c478bd9Sstevel@tonic-gate 	"-print",	PRINT,		Unary,
154b34cd89aSYuri Pankov 	"-print0",	PRINT0,		Unary,
155b34cd89aSYuri Pankov 	"-prune",	PRUNE,		Unary,
156b34cd89aSYuri Pankov 	"-regex",	REGEX,		Str,
1577c478bd9Sstevel@tonic-gate 	"-size",	SIZE,		Num,
1587c478bd9Sstevel@tonic-gate 	"-type",	TYPE,		Num,
1597c478bd9Sstevel@tonic-gate 	"-user",	F_USER,		Num,
160b34cd89aSYuri Pankov 	"-useracl",	F_USERACL,	Num,
1617c478bd9Sstevel@tonic-gate 	"-xattr",	XATTR,		Unary,
162b34cd89aSYuri Pankov 	"-xdev",	MOUNT,		Unary,
16327d3a169SToomas Soome 	0,		0,		0
1647c478bd9Sstevel@tonic-gate };
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate union Item
1677c478bd9Sstevel@tonic-gate {
1687c478bd9Sstevel@tonic-gate 	struct Node	*np;
1697c478bd9Sstevel@tonic-gate 	struct Arglist	*vp;
1707c478bd9Sstevel@tonic-gate 	time_t		t;
1717c478bd9Sstevel@tonic-gate 	char		*cp;
1727c478bd9Sstevel@tonic-gate 	char		**ap;
1737c478bd9Sstevel@tonic-gate 	long		l;
1747c478bd9Sstevel@tonic-gate 	int		i;
1757c478bd9Sstevel@tonic-gate 	long long	ll;
1767c478bd9Sstevel@tonic-gate };
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate struct Node
1797c478bd9Sstevel@tonic-gate {
1807c478bd9Sstevel@tonic-gate 	struct Node	*next;
1817c478bd9Sstevel@tonic-gate 	enum Command	action;
1827c478bd9Sstevel@tonic-gate 	enum Type	type;
1837c478bd9Sstevel@tonic-gate 	union Item	first;
1847c478bd9Sstevel@tonic-gate 	union Item	second;
1857c478bd9Sstevel@tonic-gate };
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate /* if no -print, -exec or -ok replace "expression" with "(expression) -print" */
1887c478bd9Sstevel@tonic-gate static	struct	Node PRINT_NODE = { 0, PRINT, 0, 0};
1897c478bd9Sstevel@tonic-gate static	struct	Node LPAREN_NODE = { 0, LPAREN, 0, 0};
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate /*
1937c478bd9Sstevel@tonic-gate  * Prototype variable size arglist buffer
1947c478bd9Sstevel@tonic-gate  */
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate struct Arglist
1977c478bd9Sstevel@tonic-gate {
1987c478bd9Sstevel@tonic-gate 	struct Arglist	*next;
1997c478bd9Sstevel@tonic-gate 	char		*end;
2007c478bd9Sstevel@tonic-gate 	char		*nextstr;
2017c478bd9Sstevel@tonic-gate 	char		**firstvar;
2027c478bd9Sstevel@tonic-gate 	char		**nextvar;
2037c478bd9Sstevel@tonic-gate 	char		*arglist[1];
2047c478bd9Sstevel@tonic-gate };
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 
20727d3a169SToomas Soome static int		compile(char **, struct Node *, int *);
20827d3a169SToomas Soome static int		execute(const char *, const struct stat *, int,
20927d3a169SToomas Soome     struct FTW *);
21027d3a169SToomas Soome static int		doexec(const char *, char **, int *);
21127d3a169SToomas Soome static int		dodelete(const char *, const struct stat *,
21227d3a169SToomas Soome     struct FTW *);
21327d3a169SToomas Soome static struct Args	*lookup(char *);
21427d3a169SToomas Soome static int		ok(const char *, char *[]);
2156c83d09fSrobbin static void		usage(void)	__NORETURN;
21627d3a169SToomas Soome static struct Arglist	*varargs(char **);
21727d3a169SToomas Soome static int		list(const char *, const struct stat *);
21827d3a169SToomas Soome static char		*getgroup(gid_t);
21927d3a169SToomas Soome static FILE		*cmdopen(char *, char **, char *, FILE *);
22027d3a169SToomas Soome static int		cmdclose(FILE *);
22127d3a169SToomas Soome static char		*getshell(void);
22227d3a169SToomas Soome static void		init_remote_fs(void);
22327d3a169SToomas Soome static char		*getname(uid_t);
22427d3a169SToomas Soome static int		readmode(const char *);
22527d3a169SToomas Soome static mode_t		getmode(mode_t);
22627d3a169SToomas Soome static const char	*gettail(const char *);
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 
22968a94df1Scf46844 static int walkflags = FTW_CHDIR|FTW_PHYS|FTW_ANYERR|FTW_NOLOOP;
2307c478bd9Sstevel@tonic-gate static struct Node	*topnode;
2317c478bd9Sstevel@tonic-gate static struct Node	*freenode;	/* next free node we may use later */
2327c478bd9Sstevel@tonic-gate static char		*cpio[] = { "cpio", "-o", 0 };
2337c478bd9Sstevel@tonic-gate static char		*ncpio[] = { "cpio", "-oc", 0 };
2347c478bd9Sstevel@tonic-gate static char		*cpiol[] = { "cpio", "-oL", 0 };
2357c478bd9Sstevel@tonic-gate static char		*ncpiol[] = { "cpio", "-ocL", 0 };
2367c478bd9Sstevel@tonic-gate static time_t		now;
2377c478bd9Sstevel@tonic-gate static FILE		*output;
2387c478bd9Sstevel@tonic-gate static char		*dummyarg = (char *)-1;
2397c478bd9Sstevel@tonic-gate static int		lastval;
2407c478bd9Sstevel@tonic-gate static int		varsize;
2417c478bd9Sstevel@tonic-gate static struct Arglist	*lastlist;
2427c478bd9Sstevel@tonic-gate static char		*cmdname;
2437c478bd9Sstevel@tonic-gate static char		*remote_fstypes[N_FSTYPES+1];
2447c478bd9Sstevel@tonic-gate static int		fstype_index = 0;
2457c478bd9Sstevel@tonic-gate static int		action_expression = 0;	/* -print, -exec, or -ok */
2467c478bd9Sstevel@tonic-gate static int		error = 0;
2477c478bd9Sstevel@tonic-gate static int		paren_cnt = 0;	/* keeps track of parentheses */
248b34cd89aSYuri Pankov static int		Eflag = 0;
2497c478bd9Sstevel@tonic-gate static int		hflag = 0;
2507c478bd9Sstevel@tonic-gate static int		lflag = 0;
251d35170d6Srm88369 /* set when doexec()-invoked utility returns non-zero */
252d35170d6Srm88369 static int		exec_exitcode = 0;
253b34cd89aSYuri Pankov static regex_t		*preg = NULL;
254b34cd89aSYuri Pankov static int		npreg = 0;
255b34cd89aSYuri Pankov static int		mindepth = -1, maxdepth = -1;
2567c478bd9Sstevel@tonic-gate extern char		**environ;
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate int
main(int argc,char ** argv)2597c478bd9Sstevel@tonic-gate main(int argc, char **argv)
2607c478bd9Sstevel@tonic-gate {
2617c478bd9Sstevel@tonic-gate 	char *cp;
2627c478bd9Sstevel@tonic-gate 	int c;
2637c478bd9Sstevel@tonic-gate 	int paths;
2647c478bd9Sstevel@tonic-gate 	char *cwdpath;
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
2677c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
2687c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
2697c478bd9Sstevel@tonic-gate #endif
2707c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	cmdname = argv[0];
2737c478bd9Sstevel@tonic-gate 	if (time(&now) == (time_t)(-1)) {
2747c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: time() %s\n"),
2757c478bd9Sstevel@tonic-gate 		    cmdname, strerror(errno));
2767c478bd9Sstevel@tonic-gate 		exit(1);
2777c478bd9Sstevel@tonic-gate 	}
278b34cd89aSYuri Pankov 	while ((c = getopt(argc, argv, "EHL")) != -1) {
2797c478bd9Sstevel@tonic-gate 		switch (c) {
280b34cd89aSYuri Pankov 		case 'E':
281b34cd89aSYuri Pankov 			Eflag = 1;
282b34cd89aSYuri Pankov 			break;
2837c478bd9Sstevel@tonic-gate 		case 'H':
2847c478bd9Sstevel@tonic-gate 			hflag = 1;
2857c478bd9Sstevel@tonic-gate 			lflag = 0;
2867c478bd9Sstevel@tonic-gate 			break;
2877c478bd9Sstevel@tonic-gate 		case 'L':
2887c478bd9Sstevel@tonic-gate 			hflag = 0;
2897c478bd9Sstevel@tonic-gate 			lflag = 1;
2907c478bd9Sstevel@tonic-gate 			break;
2917c478bd9Sstevel@tonic-gate 		case '?':
2927c478bd9Sstevel@tonic-gate 			usage();
2937c478bd9Sstevel@tonic-gate 			break;
2947c478bd9Sstevel@tonic-gate 		}
2957c478bd9Sstevel@tonic-gate 	}
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	argc -= optind;
2987c478bd9Sstevel@tonic-gate 	argv += optind;
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate 	if (argc < 1) {
3017c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
3027c478bd9Sstevel@tonic-gate 		    gettext("%s: insufficient number of arguments\n"), cmdname);
3037c478bd9Sstevel@tonic-gate 		usage();
3047c478bd9Sstevel@tonic-gate 	}
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	for (paths = 0; (cp = argv[paths]) != 0; ++paths) {
3077c478bd9Sstevel@tonic-gate 		if (*cp == '-')
3087c478bd9Sstevel@tonic-gate 			break;
3097c478bd9Sstevel@tonic-gate 		else if ((*cp == '!' || *cp == '(') && *(cp+1) == 0)
3107c478bd9Sstevel@tonic-gate 			break;
3117c478bd9Sstevel@tonic-gate 	}
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 	if (paths == 0) /* no path-list */
3147c478bd9Sstevel@tonic-gate 		usage();
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	output = stdout;
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 	/* lflag is the same as -follow */
3197c478bd9Sstevel@tonic-gate 	if (lflag)
3207c478bd9Sstevel@tonic-gate 		walkflags &= ~FTW_PHYS;
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate 	/* allocate enough space for the compiler */
3237c478bd9Sstevel@tonic-gate 	topnode = malloc((argc + 1) * sizeof (struct Node));
3247c478bd9Sstevel@tonic-gate 	(void) memset(topnode, 0, (argc + 1) * sizeof (struct Node));
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	if (compile(argv + paths, topnode, &action_expression) == 0) {
3277c478bd9Sstevel@tonic-gate 		/* no expression, default to -print */
3287c478bd9Sstevel@tonic-gate 		(void) memcpy(topnode, &PRINT_NODE, sizeof (struct Node));
3297c478bd9Sstevel@tonic-gate 	} else if (!action_expression) {
3307c478bd9Sstevel@tonic-gate 		/*
3317c478bd9Sstevel@tonic-gate 		 * if no action expression, insert an LPAREN node above topnode,
3327c478bd9Sstevel@tonic-gate 		 * with a PRINT node as its next node
3337c478bd9Sstevel@tonic-gate 		 */
3347c478bd9Sstevel@tonic-gate 		struct Node *savenode;
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate 		if (freenode == NULL) {
3377c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("%s: can't append -print"
3387c478bd9Sstevel@tonic-gate 			    " implicitly; try explicit -print option\n"),
3397c478bd9Sstevel@tonic-gate 			    cmdname);
3407c478bd9Sstevel@tonic-gate 			exit(1);
3417c478bd9Sstevel@tonic-gate 		}
3427c478bd9Sstevel@tonic-gate 		savenode = topnode;
3437c478bd9Sstevel@tonic-gate 		topnode = freenode++;
3447c478bd9Sstevel@tonic-gate 		(void) memcpy(topnode, &LPAREN_NODE, sizeof (struct Node));
3457c478bd9Sstevel@tonic-gate 		topnode->next = freenode;
3467c478bd9Sstevel@tonic-gate 		topnode->first.np = savenode;
3477c478bd9Sstevel@tonic-gate 		(void) memcpy(topnode->next, &PRINT_NODE, sizeof (struct Node));
3487c478bd9Sstevel@tonic-gate 	}
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate 	while (paths--) {
3517c478bd9Sstevel@tonic-gate 		char *curpath;
3527c478bd9Sstevel@tonic-gate 		struct stat sb;
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate 		curpath = *(argv++);
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 		/*
3577c478bd9Sstevel@tonic-gate 		 * If -H is specified, it means we walk the first
3587c478bd9Sstevel@tonic-gate 		 * level (pathname on command line) logically, following
3597c478bd9Sstevel@tonic-gate 		 * symlinks, but lower levels are walked physically.
3607c478bd9Sstevel@tonic-gate 		 * We use our own secret interface to nftw() to change
3617c478bd9Sstevel@tonic-gate 		 * the from stat to lstat after the top level is walked.
3627c478bd9Sstevel@tonic-gate 		 */
3637c478bd9Sstevel@tonic-gate 		if (hflag) {
3647c478bd9Sstevel@tonic-gate 			if (stat(curpath, &sb) < 0 && errno == ENOENT)
3657c478bd9Sstevel@tonic-gate 				walkflags &= ~FTW_HOPTION;
3667c478bd9Sstevel@tonic-gate 			else
3677c478bd9Sstevel@tonic-gate 				walkflags |= FTW_HOPTION;
3687c478bd9Sstevel@tonic-gate 		}
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate 		/*
3717c478bd9Sstevel@tonic-gate 		 * We need this check as nftw needs a CWD and we have no
3727c478bd9Sstevel@tonic-gate 		 * way of returning back from that code with a meaningful
3737c478bd9Sstevel@tonic-gate 		 * error related to this
3747c478bd9Sstevel@tonic-gate 		 */
3757c478bd9Sstevel@tonic-gate 		if ((cwdpath = getcwd(NULL, PATH_MAX)) == NULL) {
3764b808d43SRich Burridge 			if ((errno == EACCES) && (walkflags & FTW_CHDIR)) {
3774b808d43SRich Burridge 				/*
3784b808d43SRich Burridge 				 * A directory above cwd is inaccessible,
3794b808d43SRich Burridge 				 * so don't do chdir(2)s. Slower, but at least
3804b808d43SRich Burridge 				 * it works.
3814b808d43SRich Burridge 				 */
3824b808d43SRich Burridge 				walkflags &= ~FTW_CHDIR;
3834b808d43SRich Burridge 				free(cwdpath);
3844b808d43SRich Burridge 			} else {
3857c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
3864b808d43SRich Burridge 				    gettext("%s : cannot get the current "
3874b808d43SRich Burridge 				    "working directory\n"), cmdname);
3887c478bd9Sstevel@tonic-gate 				exit(1);
3894b808d43SRich Burridge 			}
3907c478bd9Sstevel@tonic-gate 		} else
3917c478bd9Sstevel@tonic-gate 			free(cwdpath);
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 		if (nftw(curpath, execute, 1000, walkflags)) {
3957c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
3967c478bd9Sstevel@tonic-gate 			    gettext("%s: cannot open %s: %s\n"),
3977c478bd9Sstevel@tonic-gate 			    cmdname, curpath, strerror(errno));
3987c478bd9Sstevel@tonic-gate 			error = 1;
3997c478bd9Sstevel@tonic-gate 		}
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 	}
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 	/* execute any remaining variable length lists */
4047c478bd9Sstevel@tonic-gate 	while (lastlist) {
4057c478bd9Sstevel@tonic-gate 		if (lastlist->end != lastlist->nextstr) {
4067c478bd9Sstevel@tonic-gate 			*lastlist->nextvar = 0;
40727d3a169SToomas Soome 			(void) doexec(NULL, lastlist->arglist,
408d35170d6Srm88369 			    &exec_exitcode);
4097c478bd9Sstevel@tonic-gate 		}
4107c478bd9Sstevel@tonic-gate 		lastlist = lastlist->next;
4117c478bd9Sstevel@tonic-gate 	}
4127c478bd9Sstevel@tonic-gate 	if (output != stdout)
4137c478bd9Sstevel@tonic-gate 		return (cmdclose(output));
414d35170d6Srm88369 	return ((exec_exitcode != 0) ? exec_exitcode : error);
4157c478bd9Sstevel@tonic-gate }
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate /*
4187c478bd9Sstevel@tonic-gate  * compile the arguments
4197c478bd9Sstevel@tonic-gate  */
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate static int
compile(char ** argv,struct Node * np,int * actionp)42227d3a169SToomas Soome compile(char **argv, struct Node *np, int *actionp)
4237c478bd9Sstevel@tonic-gate {
4247c478bd9Sstevel@tonic-gate 	char *b;
4257c478bd9Sstevel@tonic-gate 	char **av;
4267c478bd9Sstevel@tonic-gate 	struct Node *oldnp = topnode;
4277c478bd9Sstevel@tonic-gate 	struct Args *argp;
4287c478bd9Sstevel@tonic-gate 	char **com;
4297c478bd9Sstevel@tonic-gate 	int i;
4307c478bd9Sstevel@tonic-gate 	enum Command wasop = PRINT;
4317c478bd9Sstevel@tonic-gate 
4323d63ea05Sas145665 	if (init_yes() < 0) {
4333d63ea05Sas145665 		(void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
4343d63ea05Sas145665 		    strerror(errno));
4353d63ea05Sas145665 		exit(1);
4363d63ea05Sas145665 	}
4373d63ea05Sas145665 
4387c478bd9Sstevel@tonic-gate 	for (av = argv; *av && (argp = lookup(*av)); av++) {
4397c478bd9Sstevel@tonic-gate 		np->next = 0;
4407c478bd9Sstevel@tonic-gate 		np->action = argp->action;
4417c478bd9Sstevel@tonic-gate 		np->type = argp->type;
4427c478bd9Sstevel@tonic-gate 		np->second.i = 0;
4437c478bd9Sstevel@tonic-gate 		if (argp->type == Op) {
4447c478bd9Sstevel@tonic-gate 			if (wasop == NOT || (wasop && np->action != NOT)) {
4457c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
4467c478bd9Sstevel@tonic-gate 				    gettext("%s: operand follows operand\n"),
4477c478bd9Sstevel@tonic-gate 				    cmdname);
4487c478bd9Sstevel@tonic-gate 				exit(1);
4497c478bd9Sstevel@tonic-gate 			}
4507c478bd9Sstevel@tonic-gate 			if (np->action != NOT && oldnp == 0)
4517c478bd9Sstevel@tonic-gate 				goto err;
4527c478bd9Sstevel@tonic-gate 			wasop = argp->action;
4537c478bd9Sstevel@tonic-gate 		} else {
4547c478bd9Sstevel@tonic-gate 			wasop = PRINT;
4557c478bd9Sstevel@tonic-gate 			if (argp->type != Unary) {
4567c478bd9Sstevel@tonic-gate 				if (!(b = *++av)) {
45727d3a169SToomas Soome 					(void) fprintf(stderr, gettext(
45827d3a169SToomas Soome 					    "%s: incomplete statement\n"),
4597c478bd9Sstevel@tonic-gate 					    cmdname);
4607c478bd9Sstevel@tonic-gate 					exit(1);
4617c478bd9Sstevel@tonic-gate 				}
4627c478bd9Sstevel@tonic-gate 				if (argp->type == Num) {
463b34cd89aSYuri Pankov 					if (((argp->action == MAXDEPTH) ||
464b34cd89aSYuri Pankov 					    (argp->action == MINDEPTH)) &&
465b34cd89aSYuri Pankov 					    ((int)strtol(b, (char **)NULL,
466b34cd89aSYuri Pankov 					    10) < 0))
46727d3a169SToomas Soome 						errx(1, gettext(
46827d3a169SToomas Soome 						    "%s: value must be "
46927d3a169SToomas Soome 						    "positive"),
470b34cd89aSYuri Pankov 						    (argp->action == MAXDEPTH) ?
471b34cd89aSYuri Pankov 						    "maxdepth" : "mindepth");
4727c478bd9Sstevel@tonic-gate 					if ((argp->action != PERM) ||
4737c478bd9Sstevel@tonic-gate 					    (*b != '+')) {
4747c478bd9Sstevel@tonic-gate 						if (*b == '+' || *b == '-') {
4757c478bd9Sstevel@tonic-gate 							np->second.i = *b;
4767c478bd9Sstevel@tonic-gate 							b++;
4777c478bd9Sstevel@tonic-gate 						}
4787c478bd9Sstevel@tonic-gate 					}
4797c478bd9Sstevel@tonic-gate 				}
4807c478bd9Sstevel@tonic-gate 			}
4817c478bd9Sstevel@tonic-gate 		}
4827c478bd9Sstevel@tonic-gate 		switch (argp->action) {
4837c478bd9Sstevel@tonic-gate 		case AND:
4847c478bd9Sstevel@tonic-gate 			break;
4857c478bd9Sstevel@tonic-gate 		case NOT:
4867c478bd9Sstevel@tonic-gate 			break;
4877c478bd9Sstevel@tonic-gate 		case OR:
4887c478bd9Sstevel@tonic-gate 			np->first.np = topnode;
4897c478bd9Sstevel@tonic-gate 			topnode = np;
4907c478bd9Sstevel@tonic-gate 			oldnp->next = 0;
4917c478bd9Sstevel@tonic-gate 			break;
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 		case LPAREN: {
4947c478bd9Sstevel@tonic-gate 			struct Node *save = topnode;
4957c478bd9Sstevel@tonic-gate 			topnode = np+1;
4967c478bd9Sstevel@tonic-gate 			paren_cnt++;
4977c478bd9Sstevel@tonic-gate 			i = compile(++av, topnode, actionp);
4987c478bd9Sstevel@tonic-gate 			np->first.np = topnode;
4997c478bd9Sstevel@tonic-gate 			topnode = save;
5007c478bd9Sstevel@tonic-gate 			av += i;
5017c478bd9Sstevel@tonic-gate 			oldnp = np;
5027c478bd9Sstevel@tonic-gate 			np += i + 1;
5037c478bd9Sstevel@tonic-gate 			oldnp->next = np;
5047c478bd9Sstevel@tonic-gate 			continue;
5057c478bd9Sstevel@tonic-gate 		}
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 		case RPAREN:
5087c478bd9Sstevel@tonic-gate 			if (paren_cnt <= 0) {
5097c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
5107c478bd9Sstevel@tonic-gate 				    gettext("%s: unmatched ')'\n"),
5117c478bd9Sstevel@tonic-gate 				    cmdname);
5127c478bd9Sstevel@tonic-gate 				exit(1);
5137c478bd9Sstevel@tonic-gate 			}
5147c478bd9Sstevel@tonic-gate 			paren_cnt--;
5157c478bd9Sstevel@tonic-gate 			if (oldnp == 0)
5167c478bd9Sstevel@tonic-gate 				goto err;
5177c478bd9Sstevel@tonic-gate 			if (oldnp->type == Op) {
5187c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
5197c478bd9Sstevel@tonic-gate 				    gettext("%s: cannot immediately"
5207c478bd9Sstevel@tonic-gate 				    " follow an operand with ')'\n"),
5217c478bd9Sstevel@tonic-gate 				    cmdname);
5227c478bd9Sstevel@tonic-gate 				exit(1);
5237c478bd9Sstevel@tonic-gate 			}
5247c478bd9Sstevel@tonic-gate 			oldnp->next = 0;
5257c478bd9Sstevel@tonic-gate 			return (av-argv);
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 		case FOLLOW:
5287c478bd9Sstevel@tonic-gate 			walkflags &= ~FTW_PHYS;
5297c478bd9Sstevel@tonic-gate 			break;
5307c478bd9Sstevel@tonic-gate 		case MOUNT:
5317c478bd9Sstevel@tonic-gate 			walkflags |= FTW_MOUNT;
5327c478bd9Sstevel@tonic-gate 			break;
5337c478bd9Sstevel@tonic-gate 		case DEPTH:
5347c478bd9Sstevel@tonic-gate 			walkflags |= FTW_DEPTH;
5357c478bd9Sstevel@tonic-gate 			break;
536ab823b7fSPrasad Joshi 		case DELETE:
537ab823b7fSPrasad Joshi 			walkflags |= (FTW_DEPTH | FTW_PHYS);
538ab823b7fSPrasad Joshi 			walkflags &= ~FTW_CHDIR;
539ab823b7fSPrasad Joshi 			(*actionp)++;
540ab823b7fSPrasad Joshi 			break;
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 		case LOCAL:
5437c478bd9Sstevel@tonic-gate 			np->first.l = 0L;
5447c478bd9Sstevel@tonic-gate 			np->first.ll = 0LL;
5457c478bd9Sstevel@tonic-gate 			np->second.i = '+';
5467c478bd9Sstevel@tonic-gate 			/*
5477c478bd9Sstevel@tonic-gate 			 * Make it compatible to df -l for
5487c478bd9Sstevel@tonic-gate 			 * future enhancement. So, anything
5497c478bd9Sstevel@tonic-gate 			 * that is not remote, then it is
5507c478bd9Sstevel@tonic-gate 			 * local.
5517c478bd9Sstevel@tonic-gate 			 */
5527c478bd9Sstevel@tonic-gate 			init_remote_fs();
5537c478bd9Sstevel@tonic-gate 			break;
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 		case SIZE:
5567c478bd9Sstevel@tonic-gate 			if (b[strlen(b)-1] == 'c')
5577c478bd9Sstevel@tonic-gate 				np->action = CSIZE;
5587c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
5597c478bd9Sstevel@tonic-gate 		case INUM:
5607c478bd9Sstevel@tonic-gate 			np->first.ll = atoll(b);
5617c478bd9Sstevel@tonic-gate 			break;
5627c478bd9Sstevel@tonic-gate 
563da1a9cbeSjonb 		case CMIN:
5647c478bd9Sstevel@tonic-gate 		case CTIME:
565da1a9cbeSjonb 		case MMIN:
5667c478bd9Sstevel@tonic-gate 		case MTIME:
567da1a9cbeSjonb 		case AMIN:
5687c478bd9Sstevel@tonic-gate 		case ATIME:
5697c478bd9Sstevel@tonic-gate 		case LINKS:
5707c478bd9Sstevel@tonic-gate 			np->first.l = atol(b);
5717c478bd9Sstevel@tonic-gate 			break;
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 		case F_USER:
574b34cd89aSYuri Pankov 		case F_GROUP:
575b34cd89aSYuri Pankov 		case F_USERACL:
576b34cd89aSYuri Pankov 		case F_GROUPACL: {
5777c478bd9Sstevel@tonic-gate 			struct	passwd	*pw;
5787c478bd9Sstevel@tonic-gate 			struct	group *gr;
579ef497ae3SRich Burridge 			long value;
580ef497ae3SRich Burridge 			char *q;
581ef497ae3SRich Burridge 
582ef497ae3SRich Burridge 			value = -1;
583b34cd89aSYuri Pankov 			if (argp->action == F_USER ||
584b34cd89aSYuri Pankov 			    argp->action == F_USERACL) {
5857c478bd9Sstevel@tonic-gate 				if ((pw = getpwnam(b)) != 0)
586ef497ae3SRich Burridge 					value = (long)pw->pw_uid;
5877c478bd9Sstevel@tonic-gate 			} else {
5887c478bd9Sstevel@tonic-gate 				if ((gr = getgrnam(b)) != 0)
589ef497ae3SRich Burridge 					value = (long)gr->gr_gid;
5907c478bd9Sstevel@tonic-gate 			}
591ef497ae3SRich Burridge 			if (value == -1) {
592ef497ae3SRich Burridge 				errno = 0;
593ef497ae3SRich Burridge 				value = strtol(b, &q, 10);
594ef497ae3SRich Burridge 				if (errno != 0 || q == b || *q != '\0') {
5957c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, gettext(
5967c478bd9Sstevel@tonic-gate 					    "%s: cannot find %s name\n"),
5977c478bd9Sstevel@tonic-gate 					    cmdname, *av);
5987c478bd9Sstevel@tonic-gate 					exit(1);
5997c478bd9Sstevel@tonic-gate 				}
6007c478bd9Sstevel@tonic-gate 			}
601ef497ae3SRich Burridge 			np->first.l = value;
6027c478bd9Sstevel@tonic-gate 			break;
6037c478bd9Sstevel@tonic-gate 		}
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 		case EXEC:
6067c478bd9Sstevel@tonic-gate 		case OK:
6077c478bd9Sstevel@tonic-gate 			walkflags &= ~FTW_CHDIR;
6087c478bd9Sstevel@tonic-gate 			np->first.ap = av;
6097c478bd9Sstevel@tonic-gate 			(*actionp)++;
6107c478bd9Sstevel@tonic-gate 			for (;;) {
6117c478bd9Sstevel@tonic-gate 				if ((b = *av) == 0) {
61227d3a169SToomas Soome 					(void) fprintf(stderr, gettext(
61327d3a169SToomas Soome 					    "%s: incomplete statement\n"),
6147c478bd9Sstevel@tonic-gate 					    cmdname);
6157c478bd9Sstevel@tonic-gate 					exit(1);
6167c478bd9Sstevel@tonic-gate 				}
6177c478bd9Sstevel@tonic-gate 				if (strcmp(b, ";") == 0) {
6187c478bd9Sstevel@tonic-gate 					*av = 0;
6197c478bd9Sstevel@tonic-gate 					break;
6207c478bd9Sstevel@tonic-gate 				} else if (strcmp(b, "{}") == 0)
6217c478bd9Sstevel@tonic-gate 					*av = dummyarg;
6227c478bd9Sstevel@tonic-gate 				else if (strcmp(b, "+") == 0 &&
62327d3a169SToomas Soome 				    av[-1] == dummyarg && np->action == EXEC) {
6247c478bd9Sstevel@tonic-gate 					av[-1] = 0;
6257c478bd9Sstevel@tonic-gate 					np->first.vp = varargs(np->first.ap);
6267c478bd9Sstevel@tonic-gate 					np->action = VARARGS;
6277c478bd9Sstevel@tonic-gate 					break;
6287c478bd9Sstevel@tonic-gate 				}
6297c478bd9Sstevel@tonic-gate 				av++;
6307c478bd9Sstevel@tonic-gate 			}
6317c478bd9Sstevel@tonic-gate 			break;
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 		case NAME:
634b34cd89aSYuri Pankov 		case INAME:
63505f32410SAndy Stormont 		case PATH:
63605f32410SAndy Stormont 		case IPATH:
6377c478bd9Sstevel@tonic-gate 			np->first.cp = b;
6387c478bd9Sstevel@tonic-gate 			break;
639b34cd89aSYuri Pankov 		case REGEX:
640b34cd89aSYuri Pankov 		case IREGEX: {
641b34cd89aSYuri Pankov 			int error;
642b34cd89aSYuri Pankov 			size_t errlen;
643b34cd89aSYuri Pankov 			char *errmsg;
644b34cd89aSYuri Pankov 
645b34cd89aSYuri Pankov 			if ((preg = realloc(preg, (npreg + 1) *
646b34cd89aSYuri Pankov 			    sizeof (regex_t))) == NULL)
647b34cd89aSYuri Pankov 				err(1, "realloc");
648b34cd89aSYuri Pankov 			if ((error = regcomp(&preg[npreg], b,
649b34cd89aSYuri Pankov 			    ((np->action == IREGEX) ? REG_ICASE : 0) |
650b34cd89aSYuri Pankov 			    ((Eflag) ? REG_EXTENDED : 0))) != 0) {
651b34cd89aSYuri Pankov 				errlen = regerror(error, &preg[npreg], NULL, 0);
652b34cd89aSYuri Pankov 				if ((errmsg = malloc(errlen)) == NULL)
653b34cd89aSYuri Pankov 					err(1, "malloc");
654b34cd89aSYuri Pankov 				(void) regerror(error, &preg[npreg], errmsg,
655b34cd89aSYuri Pankov 				    errlen);
656b34cd89aSYuri Pankov 				errx(1, gettext("RE error: %s"), errmsg);
657b34cd89aSYuri Pankov 			}
658b34cd89aSYuri Pankov 			npreg++;
659b34cd89aSYuri Pankov 			break;
660b34cd89aSYuri Pankov 		}
6617c478bd9Sstevel@tonic-gate 		case PERM:
6627c478bd9Sstevel@tonic-gate 			if (*b == '-')
6637c478bd9Sstevel@tonic-gate 				++b;
6647c478bd9Sstevel@tonic-gate 
66527d3a169SToomas Soome 			if (readmode(b) != 0) {
6667c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(
6677c478bd9Sstevel@tonic-gate 				    "find: -perm: Bad permission string\n"));
6687c478bd9Sstevel@tonic-gate 				usage();
6697c478bd9Sstevel@tonic-gate 			}
6707c478bd9Sstevel@tonic-gate 			np->first.l = (long)getmode((mode_t)0);
6717c478bd9Sstevel@tonic-gate 			break;
6727c478bd9Sstevel@tonic-gate 		case TYPE:
6737c478bd9Sstevel@tonic-gate 			i = *b;
6747c478bd9Sstevel@tonic-gate 			np->first.l =
6757c478bd9Sstevel@tonic-gate 			    i == 'd' ? S_IFDIR :
6767c478bd9Sstevel@tonic-gate 			    i == 'b' ? S_IFBLK :
6777c478bd9Sstevel@tonic-gate 			    i == 'c' ? S_IFCHR :
6787c478bd9Sstevel@tonic-gate #ifdef S_IFIFO
6797c478bd9Sstevel@tonic-gate 			    i == 'p' ? S_IFIFO :
6807c478bd9Sstevel@tonic-gate #endif
6817c478bd9Sstevel@tonic-gate 			    i == 'f' ? S_IFREG :
6827c478bd9Sstevel@tonic-gate #ifdef S_IFLNK
6837c478bd9Sstevel@tonic-gate 			    i == 'l' ? S_IFLNK :
6847c478bd9Sstevel@tonic-gate #endif
6857c478bd9Sstevel@tonic-gate #ifdef S_IFSOCK
6867c478bd9Sstevel@tonic-gate 			    i == 's' ? S_IFSOCK :
6877c478bd9Sstevel@tonic-gate #endif
6887c478bd9Sstevel@tonic-gate #ifdef S_IFDOOR
6897c478bd9Sstevel@tonic-gate 			    i == 'D' ? S_IFDOOR :
6907c478bd9Sstevel@tonic-gate #endif
6917c478bd9Sstevel@tonic-gate 			    0;
6927c478bd9Sstevel@tonic-gate 			break;
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 		case CPIO:
6957c478bd9Sstevel@tonic-gate 			if (walkflags & FTW_PHYS)
6967c478bd9Sstevel@tonic-gate 				com = cpio;
6977c478bd9Sstevel@tonic-gate 			else
6987c478bd9Sstevel@tonic-gate 				com = cpiol;
6997c478bd9Sstevel@tonic-gate 			goto common;
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate 		case NCPIO: {
7027c478bd9Sstevel@tonic-gate 			FILE *fd;
7037c478bd9Sstevel@tonic-gate 
7047c478bd9Sstevel@tonic-gate 			if (walkflags & FTW_PHYS)
7057c478bd9Sstevel@tonic-gate 				com = ncpio;
7067c478bd9Sstevel@tonic-gate 			else
7077c478bd9Sstevel@tonic-gate 				com = ncpiol;
7087c478bd9Sstevel@tonic-gate 		common:
7097c478bd9Sstevel@tonic-gate 			/* set up cpio */
7107c478bd9Sstevel@tonic-gate 			if ((fd = fopen(b, "w")) == NULL) {
7117c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
7127c478bd9Sstevel@tonic-gate 				    gettext("%s: cannot create %s\n"),
7137c478bd9Sstevel@tonic-gate 				    cmdname, b);
7147c478bd9Sstevel@tonic-gate 				exit(1);
7157c478bd9Sstevel@tonic-gate 			}
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate 			np->first.l = (long)cmdopen("cpio", com, "w", fd);
7187c478bd9Sstevel@tonic-gate 			(void) fclose(fd);
7197c478bd9Sstevel@tonic-gate 			walkflags |= FTW_DEPTH;
7207c478bd9Sstevel@tonic-gate 			np->action = CPIO;
7217c478bd9Sstevel@tonic-gate 		}
7227c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
7237c478bd9Sstevel@tonic-gate 		case PRINT:
724b34cd89aSYuri Pankov 		case PRINT0:
7257c478bd9Sstevel@tonic-gate 			(*actionp)++;
7267c478bd9Sstevel@tonic-gate 			break;
7277c478bd9Sstevel@tonic-gate 
7287c478bd9Sstevel@tonic-gate 		case NEWER: {
7297c478bd9Sstevel@tonic-gate 			struct stat statb;
7307c478bd9Sstevel@tonic-gate 			if (stat(b, &statb) < 0) {
7317c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
7327c478bd9Sstevel@tonic-gate 				    gettext("%s: cannot access %s\n"),
7337c478bd9Sstevel@tonic-gate 				    cmdname, b);
7347c478bd9Sstevel@tonic-gate 				exit(1);
7357c478bd9Sstevel@tonic-gate 			}
7367c478bd9Sstevel@tonic-gate 			np->first.l = statb.st_mtime;
7377c478bd9Sstevel@tonic-gate 			np->second.i = '+';
7387c478bd9Sstevel@tonic-gate 			break;
7397c478bd9Sstevel@tonic-gate 		}
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 		case PRUNE:
7427c478bd9Sstevel@tonic-gate 		case NOUSER:
7437c478bd9Sstevel@tonic-gate 		case NOGRP:
7447c478bd9Sstevel@tonic-gate 			break;
7457c478bd9Sstevel@tonic-gate 		case FSTYPE:
7467c478bd9Sstevel@tonic-gate 			np->first.cp = b;
7477c478bd9Sstevel@tonic-gate 			break;
7487c478bd9Sstevel@tonic-gate 		case LS:
7497c478bd9Sstevel@tonic-gate 			(*actionp)++;
7507c478bd9Sstevel@tonic-gate 			break;
7517c478bd9Sstevel@tonic-gate 		case XATTR:
7527c478bd9Sstevel@tonic-gate 			break;
7537c478bd9Sstevel@tonic-gate 		case ACL:
7547c478bd9Sstevel@tonic-gate 			break;
755b34cd89aSYuri Pankov 		case MAXDEPTH:
75627d3a169SToomas Soome 			maxdepth = (int)strtol(b, NULL, 10);
757b34cd89aSYuri Pankov 			break;
758b34cd89aSYuri Pankov 		case MINDEPTH:
75927d3a169SToomas Soome 			mindepth = (int)strtol(b, NULL, 10);
760b34cd89aSYuri Pankov 			break;
7617c478bd9Sstevel@tonic-gate 		}
7627c478bd9Sstevel@tonic-gate 
7637c478bd9Sstevel@tonic-gate 		oldnp = np++;
7647c478bd9Sstevel@tonic-gate 		oldnp->next = np;
7657c478bd9Sstevel@tonic-gate 	}
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 	if ((*av) || (wasop))
7687c478bd9Sstevel@tonic-gate 		goto err;
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 	if (paren_cnt != 0) {
77127d3a169SToomas Soome 		(void) fprintf(stderr, gettext("%s: unmatched '('\n"), cmdname);
7727c478bd9Sstevel@tonic-gate 		exit(1);
7737c478bd9Sstevel@tonic-gate 	}
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate 	/* just before returning, save next free node from the list */
7767c478bd9Sstevel@tonic-gate 	freenode = oldnp->next;
7777c478bd9Sstevel@tonic-gate 	oldnp->next = 0;
7787c478bd9Sstevel@tonic-gate 	return (av-argv);
7797c478bd9Sstevel@tonic-gate err:
7807c478bd9Sstevel@tonic-gate 	if (*av)
7817c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
7827c478bd9Sstevel@tonic-gate 		    gettext("%s: bad option %s\n"), cmdname, *av);
7837c478bd9Sstevel@tonic-gate 	else
7847c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: bad option\n"), cmdname);
7857c478bd9Sstevel@tonic-gate 	usage();
7867c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
7877c478bd9Sstevel@tonic-gate }
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate /*
7907c478bd9Sstevel@tonic-gate  * print out a usage message
7917c478bd9Sstevel@tonic-gate  */
7927c478bd9Sstevel@tonic-gate 
7937c478bd9Sstevel@tonic-gate static void
usage(void)7946c83d09fSrobbin usage(void)
7957c478bd9Sstevel@tonic-gate {
7967c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr,
797b34cd89aSYuri Pankov 	    gettext("%s: [-E] [-H | -L] path-list predicate-list\n"), cmdname);
7987c478bd9Sstevel@tonic-gate 	exit(1);
7997c478bd9Sstevel@tonic-gate }
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate /*
802*ba5b88a2SBill Sommerfeld  * ACL matching is complex enough to warrant its own function.
803*ba5b88a2SBill Sommerfeld  */
804*ba5b88a2SBill Sommerfeld static int
aclmatch(struct Node * np,const char * filename)805*ba5b88a2SBill Sommerfeld aclmatch(struct Node *np, const char *filename)
806*ba5b88a2SBill Sommerfeld {
807*ba5b88a2SBill Sommerfeld 	int i, t1, t2;
808*ba5b88a2SBill Sommerfeld 	acl_t *acl;
809*ba5b88a2SBill Sommerfeld 	void *acl_entry;
810*ba5b88a2SBill Sommerfeld 	aclent_t *p1;
811*ba5b88a2SBill Sommerfeld 	ace_t *p2;
812*ba5b88a2SBill Sommerfeld 
813*ba5b88a2SBill Sommerfeld 	if (np->action == F_USERACL) {
814*ba5b88a2SBill Sommerfeld 		t1 = USER;
815*ba5b88a2SBill Sommerfeld 		t2 = 0;
816*ba5b88a2SBill Sommerfeld 	} else {
817*ba5b88a2SBill Sommerfeld 		t1 = GROUP;
818*ba5b88a2SBill Sommerfeld 		t2 = ACE_IDENTIFIER_GROUP;
819*ba5b88a2SBill Sommerfeld 	}
820*ba5b88a2SBill Sommerfeld 
821*ba5b88a2SBill Sommerfeld 	if (acl_get(filename, 0, &acl) != 0)
822*ba5b88a2SBill Sommerfeld 		return (0);
823*ba5b88a2SBill Sommerfeld 
824*ba5b88a2SBill Sommerfeld 	for (i = 0, acl_entry = acl->acl_aclp;
825*ba5b88a2SBill Sommerfeld 	    i != acl->acl_cnt; i++) {
826*ba5b88a2SBill Sommerfeld 		if (acl->acl_type == ACLENT_T) {
827*ba5b88a2SBill Sommerfeld 			p1 = (aclent_t *)acl_entry;
828*ba5b88a2SBill Sommerfeld 			if (p1->a_id == np->first.l && p1->a_type == t1) {
829*ba5b88a2SBill Sommerfeld 				acl_free(acl);
830*ba5b88a2SBill Sommerfeld 				return (1);
831*ba5b88a2SBill Sommerfeld 			}
832*ba5b88a2SBill Sommerfeld 		} else {
833*ba5b88a2SBill Sommerfeld 			p2 = (ace_t *)acl_entry;
834*ba5b88a2SBill Sommerfeld 			if (p2->a_who == np->first.l &&
835*ba5b88a2SBill Sommerfeld 			    ((p2->a_flags & ACE_TYPE_FLAGS) == t2)) {
836*ba5b88a2SBill Sommerfeld 				acl_free(acl);
837*ba5b88a2SBill Sommerfeld 				return (1);
838*ba5b88a2SBill Sommerfeld 			}
839*ba5b88a2SBill Sommerfeld 		}
840*ba5b88a2SBill Sommerfeld 		acl_entry = ((char *)acl_entry + acl->acl_entry_size);
841*ba5b88a2SBill Sommerfeld 	}
842*ba5b88a2SBill Sommerfeld 	acl_free(acl);
843*ba5b88a2SBill Sommerfeld 	return (0);
844*ba5b88a2SBill Sommerfeld }
845*ba5b88a2SBill Sommerfeld 
846*ba5b88a2SBill Sommerfeld /*
8477c478bd9Sstevel@tonic-gate  * This is the function that gets executed at each node
8487c478bd9Sstevel@tonic-gate  */
8497c478bd9Sstevel@tonic-gate 
8507c478bd9Sstevel@tonic-gate static int
execute(const char * name,const struct stat * statb,int type,struct FTW * state)85127d3a169SToomas Soome execute(const char *name, const struct stat *statb, int type, struct FTW *state)
8527c478bd9Sstevel@tonic-gate {
8537c478bd9Sstevel@tonic-gate 	struct Node *np = topnode;
8547c478bd9Sstevel@tonic-gate 	int val;
8557c478bd9Sstevel@tonic-gate 	time_t t;
8567c478bd9Sstevel@tonic-gate 	long l;
8577c478bd9Sstevel@tonic-gate 	long long ll;
8587c478bd9Sstevel@tonic-gate 	int not = 1;
85927d3a169SToomas Soome 	const char *filename;
860b34cd89aSYuri Pankov 	int cnpreg = 0;
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 	if (type == FTW_NS) {
8637c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: stat() error %s: %s\n"),
8647c478bd9Sstevel@tonic-gate 		    cmdname, name, strerror(errno));
8657c478bd9Sstevel@tonic-gate 		error = 1;
8667c478bd9Sstevel@tonic-gate 		return (0);
8677c478bd9Sstevel@tonic-gate 	} else if (type == FTW_DNR) {
8687c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: cannot read dir %s: %s\n"),
8697c478bd9Sstevel@tonic-gate 		    cmdname, name, strerror(errno));
8707c478bd9Sstevel@tonic-gate 		error = 1;
8710729abfeSRich Burridge 	} else if (type == FTW_SLN && lflag == 1) {
8727c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
8737c478bd9Sstevel@tonic-gate 		    gettext("%s: cannot follow symbolic link %s: %s\n"),
8747c478bd9Sstevel@tonic-gate 		    cmdname, name, strerror(errno));
8757c478bd9Sstevel@tonic-gate 		error = 1;
87668a94df1Scf46844 	} else if (type == FTW_DL) {
87768a94df1Scf46844 		(void) fprintf(stderr, gettext("%s: cycle detected for %s\n"),
87868a94df1Scf46844 		    cmdname, name);
87968a94df1Scf46844 		error = 1;
88068a94df1Scf46844 		return (0);
8817c478bd9Sstevel@tonic-gate 	}
8827c478bd9Sstevel@tonic-gate 
883b34cd89aSYuri Pankov 	if ((maxdepth != -1 && state->level > maxdepth) ||
884b34cd89aSYuri Pankov 	    (mindepth != -1 && state->level < mindepth))
885b34cd89aSYuri Pankov 		return (0);
886b34cd89aSYuri Pankov 
8877c478bd9Sstevel@tonic-gate 	while (np) {
8887c478bd9Sstevel@tonic-gate 		switch (np->action) {
8897c478bd9Sstevel@tonic-gate 		case NOT:
8907c478bd9Sstevel@tonic-gate 			not = !not;
8917c478bd9Sstevel@tonic-gate 			np = np->next;
8927c478bd9Sstevel@tonic-gate 			continue;
8937c478bd9Sstevel@tonic-gate 
8947c478bd9Sstevel@tonic-gate 		case AND:
8957c478bd9Sstevel@tonic-gate 			np = np->next;
8967c478bd9Sstevel@tonic-gate 			continue;
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 		case OR:
8997c478bd9Sstevel@tonic-gate 			if (np->first.np == np) {
9007c478bd9Sstevel@tonic-gate 				/*
9017c478bd9Sstevel@tonic-gate 				 * handle naked OR (no term on left hand side)
9027c478bd9Sstevel@tonic-gate 				 */
9037c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
9047c478bd9Sstevel@tonic-gate 				    gettext("%s: invalid -o construction\n"),
9057c478bd9Sstevel@tonic-gate 				    cmdname);
9067c478bd9Sstevel@tonic-gate 				exit(2);
9077c478bd9Sstevel@tonic-gate 			}
9087c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
9097c478bd9Sstevel@tonic-gate 		case LPAREN: {
9107c478bd9Sstevel@tonic-gate 			struct Node *save = topnode;
9117c478bd9Sstevel@tonic-gate 			topnode = np->first.np;
9127c478bd9Sstevel@tonic-gate 			(void) execute(name, statb, type, state);
9137c478bd9Sstevel@tonic-gate 			val = lastval;
9147c478bd9Sstevel@tonic-gate 			topnode = save;
9157c478bd9Sstevel@tonic-gate 			if (np->action == OR) {
9167c478bd9Sstevel@tonic-gate 				if (val)
9177c478bd9Sstevel@tonic-gate 					return (0);
9187c478bd9Sstevel@tonic-gate 				val = 1;
9197c478bd9Sstevel@tonic-gate 			}
9207c478bd9Sstevel@tonic-gate 			break;
9217c478bd9Sstevel@tonic-gate 		}
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 		case LOCAL: {
9247c478bd9Sstevel@tonic-gate 			int	nremfs;
9257c478bd9Sstevel@tonic-gate 			val = 1;
9267c478bd9Sstevel@tonic-gate 			/*
9277c478bd9Sstevel@tonic-gate 			 * If file system type matches the remote
9287c478bd9Sstevel@tonic-gate 			 * file system type, then it is not local.
9297c478bd9Sstevel@tonic-gate 			 */
9307c478bd9Sstevel@tonic-gate 			for (nremfs = 0; nremfs < fstype_index; nremfs++) {
9317c478bd9Sstevel@tonic-gate 				if (strcmp(remote_fstypes[nremfs],
9327c478bd9Sstevel@tonic-gate 				    statb->st_fstype) == 0) {
9337c478bd9Sstevel@tonic-gate 					val = 0;
9347c478bd9Sstevel@tonic-gate 					break;
9357c478bd9Sstevel@tonic-gate 				}
9367c478bd9Sstevel@tonic-gate 			}
9377c478bd9Sstevel@tonic-gate 			break;
9387c478bd9Sstevel@tonic-gate 		}
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate 		case TYPE:
9417c478bd9Sstevel@tonic-gate 			l = (long)statb->st_mode&S_IFMT;
9427c478bd9Sstevel@tonic-gate 			goto num;
9437c478bd9Sstevel@tonic-gate 
9447c478bd9Sstevel@tonic-gate 		case PERM:
9457c478bd9Sstevel@tonic-gate 			l = (long)statb->st_mode&07777;
9467c478bd9Sstevel@tonic-gate 			if (np->second.i == '-')
9477c478bd9Sstevel@tonic-gate 				val = ((l&np->first.l) == np->first.l);
9487c478bd9Sstevel@tonic-gate 			else
9497c478bd9Sstevel@tonic-gate 				val = (l == np->first.l);
9507c478bd9Sstevel@tonic-gate 			break;
9517c478bd9Sstevel@tonic-gate 
9527c478bd9Sstevel@tonic-gate 		case INUM:
9537c478bd9Sstevel@tonic-gate 			ll = (long long)statb->st_ino;
9547c478bd9Sstevel@tonic-gate 			goto llnum;
9557c478bd9Sstevel@tonic-gate 		case NEWER:
9567c478bd9Sstevel@tonic-gate 			l = statb->st_mtime;
9577c478bd9Sstevel@tonic-gate 			goto num;
9587c478bd9Sstevel@tonic-gate 		case ATIME:
9597c478bd9Sstevel@tonic-gate 			t = statb->st_atime;
9607c478bd9Sstevel@tonic-gate 			goto days;
9617c478bd9Sstevel@tonic-gate 		case CTIME:
9627c478bd9Sstevel@tonic-gate 			t = statb->st_ctime;
9637c478bd9Sstevel@tonic-gate 			goto days;
9647c478bd9Sstevel@tonic-gate 		case MTIME:
9657c478bd9Sstevel@tonic-gate 			t = statb->st_mtime;
9667c478bd9Sstevel@tonic-gate 		days:
9677c478bd9Sstevel@tonic-gate 			l = (now-t)/A_DAY;
9687c478bd9Sstevel@tonic-gate 			goto num;
969da1a9cbeSjonb 		case MMIN:
970da1a9cbeSjonb 			t = statb->st_mtime;
971da1a9cbeSjonb 			goto mins;
972da1a9cbeSjonb 		case AMIN:
973da1a9cbeSjonb 			t = statb->st_atime;
974da1a9cbeSjonb 			goto mins;
975da1a9cbeSjonb 		case CMIN:
976da1a9cbeSjonb 			t = statb->st_ctime;
977da1a9cbeSjonb 			goto mins;
978da1a9cbeSjonb 		mins:
979da1a9cbeSjonb 			l = (now-t)/A_MIN;
980da1a9cbeSjonb 			goto num;
9817c478bd9Sstevel@tonic-gate 		case CSIZE:
9827c478bd9Sstevel@tonic-gate 			ll = (long long)statb->st_size;
9837c478bd9Sstevel@tonic-gate 			goto llnum;
9847c478bd9Sstevel@tonic-gate 		case SIZE:
9857c478bd9Sstevel@tonic-gate 			ll = (long long)round(statb->st_size, BLKSIZ)/BLKSIZ;
9867c478bd9Sstevel@tonic-gate 			goto llnum;
9877c478bd9Sstevel@tonic-gate 		case F_USER:
9887c478bd9Sstevel@tonic-gate 			l = (long)statb->st_uid;
9897c478bd9Sstevel@tonic-gate 			goto num;
9907c478bd9Sstevel@tonic-gate 		case F_GROUP:
9917c478bd9Sstevel@tonic-gate 			l = (long)statb->st_gid;
9927c478bd9Sstevel@tonic-gate 			goto num;
9937c478bd9Sstevel@tonic-gate 		case LINKS:
9947c478bd9Sstevel@tonic-gate 			l = (long)statb->st_nlink;
9957c478bd9Sstevel@tonic-gate 			goto num;
9967c478bd9Sstevel@tonic-gate 		llnum:
9977c478bd9Sstevel@tonic-gate 			if (np->second.i == '+')
9987c478bd9Sstevel@tonic-gate 				val = (ll > np->first.ll);
9997c478bd9Sstevel@tonic-gate 			else if (np->second.i == '-')
10007c478bd9Sstevel@tonic-gate 				val = (ll < np->first.ll);
10017c478bd9Sstevel@tonic-gate 			else
10027c478bd9Sstevel@tonic-gate 				val = (ll == np->first.ll);
10037c478bd9Sstevel@tonic-gate 			break;
10047c478bd9Sstevel@tonic-gate 		num:
10057c478bd9Sstevel@tonic-gate 			if (np->second.i == '+')
10067c478bd9Sstevel@tonic-gate 				val = (l > np->first.l);
10077c478bd9Sstevel@tonic-gate 			else if (np->second.i == '-')
10087c478bd9Sstevel@tonic-gate 				val = (l < np->first.l);
10097c478bd9Sstevel@tonic-gate 			else
10107c478bd9Sstevel@tonic-gate 				val = (l == np->first.l);
10117c478bd9Sstevel@tonic-gate 			break;
10127c478bd9Sstevel@tonic-gate 		case OK:
10137c478bd9Sstevel@tonic-gate 			val = ok(name, np->first.ap);
10147c478bd9Sstevel@tonic-gate 			break;
10157c478bd9Sstevel@tonic-gate 		case EXEC:
1016d35170d6Srm88369 			val = doexec(name, np->first.ap, NULL);
10177c478bd9Sstevel@tonic-gate 			break;
1018ab823b7fSPrasad Joshi 		case DELETE:
1019ab823b7fSPrasad Joshi 			val = dodelete(name, statb, state);
1020ab823b7fSPrasad Joshi 			break;
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate 		case VARARGS: {
10237c478bd9Sstevel@tonic-gate 			struct Arglist *ap = np->first.vp;
10247c478bd9Sstevel@tonic-gate 			char *cp;
10257c478bd9Sstevel@tonic-gate 			cp = ap->nextstr - (strlen(name)+1);
10267c478bd9Sstevel@tonic-gate 			if (cp >= (char *)(ap->nextvar+3)) {
10277c478bd9Sstevel@tonic-gate 				/* there is room just copy the name */
10287c478bd9Sstevel@tonic-gate 				val = 1;
10297c478bd9Sstevel@tonic-gate 				(void) strcpy(cp, name);
10307c478bd9Sstevel@tonic-gate 				*ap->nextvar++ = cp;
10317c478bd9Sstevel@tonic-gate 				ap->nextstr = cp;
10327c478bd9Sstevel@tonic-gate 			} else {
10337c478bd9Sstevel@tonic-gate 				/* no more room, exec command */
103427d3a169SToomas Soome 				*ap->nextvar++ = (char *)name;
10357c478bd9Sstevel@tonic-gate 				*ap->nextvar = 0;
10367c478bd9Sstevel@tonic-gate 				val = 1;
103727d3a169SToomas Soome 				(void) doexec(NULL, ap->arglist,
1038d35170d6Srm88369 				    &exec_exitcode);
10397c478bd9Sstevel@tonic-gate 				ap->nextstr = ap->end;
10407c478bd9Sstevel@tonic-gate 				ap->nextvar = ap->firstvar;
10417c478bd9Sstevel@tonic-gate 			}
10427c478bd9Sstevel@tonic-gate 			break;
10437c478bd9Sstevel@tonic-gate 		}
10447c478bd9Sstevel@tonic-gate 
10457c478bd9Sstevel@tonic-gate 		case DEPTH:
10467c478bd9Sstevel@tonic-gate 		case MOUNT:
10477c478bd9Sstevel@tonic-gate 		case FOLLOW:
10487c478bd9Sstevel@tonic-gate 			val = 1;
10497c478bd9Sstevel@tonic-gate 			break;
10507c478bd9Sstevel@tonic-gate 
1051b34cd89aSYuri Pankov 		case NAME:
105205f32410SAndy Stormont 		case INAME:
105305f32410SAndy Stormont 		case PATH:
105405f32410SAndy Stormont 		case IPATH: {
105505f32410SAndy Stormont 			char *path;
105605f32410SAndy Stormont 			int fnmflags = 0;
105705f32410SAndy Stormont 
105805f32410SAndy Stormont 			if (np->action == INAME || np->action == IPATH)
105905f32410SAndy Stormont 				fnmflags = FNM_IGNORECASE;
10609ab6dc39Schin 
10619ab6dc39Schin 			/*
10629ab6dc39Schin 			 * basename(3c) may modify name, so
10639ab6dc39Schin 			 * we need to pass another string
10649ab6dc39Schin 			 */
106505f32410SAndy Stormont 			if ((path = strdup(name)) == NULL) {
10669ab6dc39Schin 				(void) fprintf(stderr,
10679ab6dc39Schin 				    gettext("%s: cannot strdup() %s: %s\n"),
10686b238a5aSchin 				    cmdname, name, strerror(errno));
10699ab6dc39Schin 				exit(2);
10709ab6dc39Schin 			}
10717c478bd9Sstevel@tonic-gate 			/*
10727c478bd9Sstevel@tonic-gate 			 * XPG4 find should not treat a leading '.' in a
10737c478bd9Sstevel@tonic-gate 			 * filename specially for pattern matching.
10747c478bd9Sstevel@tonic-gate 			 * /usr/bin/find  will not pattern match a leading
10757c478bd9Sstevel@tonic-gate 			 * '.' in a filename, unless '.' is explicitly
10767c478bd9Sstevel@tonic-gate 			 * specified.
1077f3a525d9SJohn Levon 			 *
1078f3a525d9SJohn Levon 			 * The legacy behavior makes no sense for PATH.
10797c478bd9Sstevel@tonic-gate 			 */
1080b34cd89aSYuri Pankov #ifndef XPG4
1081f3a525d9SJohn Levon 			if (np->action == NAME || np->action == INAME)
1082b34cd89aSYuri Pankov 				fnmflags |= FNM_PERIOD;
10837c478bd9Sstevel@tonic-gate #endif
108405f32410SAndy Stormont 
108505f32410SAndy Stormont 			val = !fnmatch(np->first.cp,
108627d3a169SToomas Soome 			    (np->action == NAME || np->action == INAME) ?
108727d3a169SToomas Soome 			    basename(path) : path, fnmflags);
108805f32410SAndy Stormont 			free(path);
10897c478bd9Sstevel@tonic-gate 			break;
10907c478bd9Sstevel@tonic-gate 		}
10917c478bd9Sstevel@tonic-gate 
10927c478bd9Sstevel@tonic-gate 		case PRUNE:
10937c478bd9Sstevel@tonic-gate 			if (type == FTW_D)
10947c478bd9Sstevel@tonic-gate 				state->quit = FTW_PRUNE;
10957c478bd9Sstevel@tonic-gate 			val = 1;
10967c478bd9Sstevel@tonic-gate 			break;
10977c478bd9Sstevel@tonic-gate 		case NOUSER:
10987c478bd9Sstevel@tonic-gate 			val = ((getpwuid(statb->st_uid)) == 0);
10997c478bd9Sstevel@tonic-gate 			break;
11007c478bd9Sstevel@tonic-gate 		case NOGRP:
11017c478bd9Sstevel@tonic-gate 			val = ((getgrgid(statb->st_gid)) == 0);
11027c478bd9Sstevel@tonic-gate 			break;
11037c478bd9Sstevel@tonic-gate 		case FSTYPE:
11047c478bd9Sstevel@tonic-gate 			val = (strcmp(np->first.cp, statb->st_fstype) == 0);
11057c478bd9Sstevel@tonic-gate 			break;
11067c478bd9Sstevel@tonic-gate 		case CPIO:
11077c478bd9Sstevel@tonic-gate 			output = (FILE *)np->first.l;
11087c478bd9Sstevel@tonic-gate 			(void) fprintf(output, "%s\n", name);
11097c478bd9Sstevel@tonic-gate 			val = 1;
11107c478bd9Sstevel@tonic-gate 			break;
11117c478bd9Sstevel@tonic-gate 		case PRINT:
1112b34cd89aSYuri Pankov 		case PRINT0:
1113b34cd89aSYuri Pankov 			(void) fprintf(stdout, "%s%c", name,
1114b34cd89aSYuri Pankov 			    (np->action == PRINT) ? '\n' : '\0');
11157c478bd9Sstevel@tonic-gate 			val = 1;
11167c478bd9Sstevel@tonic-gate 			break;
11177c478bd9Sstevel@tonic-gate 		case LS:
11187c478bd9Sstevel@tonic-gate 			(void) list(name, statb);
11197c478bd9Sstevel@tonic-gate 			val = 1;
11207c478bd9Sstevel@tonic-gate 			break;
11217c478bd9Sstevel@tonic-gate 		case XATTR:
1122f467e6fbSJohn Sonnenschein 			filename = (walkflags & FTW_CHDIR) ?
1123f467e6fbSJohn Sonnenschein 			    gettail(name) : name;
11247c478bd9Sstevel@tonic-gate 			val = (pathconf(filename, _PC_XATTR_EXISTS) == 1);
11257c478bd9Sstevel@tonic-gate 			break;
11267c478bd9Sstevel@tonic-gate 		case ACL:
11277c478bd9Sstevel@tonic-gate 			/*
11287c478bd9Sstevel@tonic-gate 			 * Need to get the tail of the file name, since we have
11297c478bd9Sstevel@tonic-gate 			 * already chdir()ed into the directory (performed in
11307c478bd9Sstevel@tonic-gate 			 * nftw()) of the file
11317c478bd9Sstevel@tonic-gate 			 */
1132f467e6fbSJohn Sonnenschein 			filename = (walkflags & FTW_CHDIR) ?
1133f467e6fbSJohn Sonnenschein 			    gettail(name) : name;
1134f467e6fbSJohn Sonnenschein 			val = acl_trivial(filename);
11357c478bd9Sstevel@tonic-gate 			break;
1136b34cd89aSYuri Pankov 		case F_USERACL:
1137b34cd89aSYuri Pankov 		case F_GROUPACL: {
1138b34cd89aSYuri Pankov 			filename = (walkflags & FTW_CHDIR) ?
1139b34cd89aSYuri Pankov 			    gettail(name) : name;
1140*ba5b88a2SBill Sommerfeld 			val = aclmatch(np, filename);
1141b34cd89aSYuri Pankov 			break;
1142b34cd89aSYuri Pankov 		}
1143b34cd89aSYuri Pankov 		case IREGEX:
1144b34cd89aSYuri Pankov 		case REGEX: {
1145b34cd89aSYuri Pankov 			regmatch_t pmatch;
1146b34cd89aSYuri Pankov 
1147b34cd89aSYuri Pankov 			val = 0;
114827d3a169SToomas Soome 			if (regexec(&preg[cnpreg], name, 1, &pmatch, 0) == 0)
1149b34cd89aSYuri Pankov 				val = ((pmatch.rm_so == 0) &&
1150b34cd89aSYuri Pankov 				    (pmatch.rm_eo == strlen(name)));
1151b34cd89aSYuri Pankov 			cnpreg++;
1152b34cd89aSYuri Pankov 			break;
1153b34cd89aSYuri Pankov 		}
1154b34cd89aSYuri Pankov 		case MAXDEPTH:
1155b34cd89aSYuri Pankov 			if (state->level == maxdepth && type == FTW_D)
1156b34cd89aSYuri Pankov 				state->quit = FTW_PRUNE;
1157b34cd89aSYuri Pankov 			/* FALLTHROUGH */
1158b34cd89aSYuri Pankov 		case MINDEPTH:
1159b34cd89aSYuri Pankov 			val = 1;
1160b34cd89aSYuri Pankov 			break;
11617c478bd9Sstevel@tonic-gate 		}
11627c478bd9Sstevel@tonic-gate 		/*
11637c478bd9Sstevel@tonic-gate 		 * evaluate 'val' and 'not' (exclusive-or)
11647c478bd9Sstevel@tonic-gate 		 * if no inversion (not == 1), return only when val == 0
11657c478bd9Sstevel@tonic-gate 		 * (primary not true). Otherwise, invert the primary
11667c478bd9Sstevel@tonic-gate 		 * and return when the primary is true.
11677c478bd9Sstevel@tonic-gate 		 * 'Lastval' saves the last result (fail or pass) when
11687c478bd9Sstevel@tonic-gate 		 * returning back to the calling routine.
11697c478bd9Sstevel@tonic-gate 		 */
11707c478bd9Sstevel@tonic-gate 		if (val ^ not) {
11717c478bd9Sstevel@tonic-gate 			lastval = 0;
11727c478bd9Sstevel@tonic-gate 			return (0);
11737c478bd9Sstevel@tonic-gate 		}
11747c478bd9Sstevel@tonic-gate 		lastval = 1;
11757c478bd9Sstevel@tonic-gate 		not = 1;
11767c478bd9Sstevel@tonic-gate 		np = np->next;
11777c478bd9Sstevel@tonic-gate 	}
11787c478bd9Sstevel@tonic-gate 	return (0);
11797c478bd9Sstevel@tonic-gate }
11807c478bd9Sstevel@tonic-gate 
11817c478bd9Sstevel@tonic-gate /*
11827c478bd9Sstevel@tonic-gate  * code for the -ok option
11837c478bd9Sstevel@tonic-gate  */
11847c478bd9Sstevel@tonic-gate 
11857c478bd9Sstevel@tonic-gate static int
ok(const char * name,char * argv[])118627d3a169SToomas Soome ok(const char *name, char *argv[])
11877c478bd9Sstevel@tonic-gate {
11883d63ea05Sas145665 	int  c;
11893d63ea05Sas145665 	int i = 0;
11903d63ea05Sas145665 	char resp[LINE_MAX + 1];
11917c478bd9Sstevel@tonic-gate 
11927c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);	/* to flush possible `-print' */
11937c478bd9Sstevel@tonic-gate 
11947c478bd9Sstevel@tonic-gate 	if ((*argv != dummyarg) && (strcmp(*argv, name)))
11957c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "< %s ... %s >?   ", *argv, name);
11967c478bd9Sstevel@tonic-gate 	else
11977c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "< {} ... %s >?   ", name);
11987c478bd9Sstevel@tonic-gate 
11997c478bd9Sstevel@tonic-gate 	(void) fflush(stderr);
12003d63ea05Sas145665 
12013d63ea05Sas145665 	while ((c = getchar()) != '\n') {
12027c478bd9Sstevel@tonic-gate 		if (c == EOF)
12037c478bd9Sstevel@tonic-gate 			exit(2);
12043d63ea05Sas145665 		if (i < LINE_MAX)
12053d63ea05Sas145665 			resp[i++] = c;
12063d63ea05Sas145665 	}
12073d63ea05Sas145665 	resp[i] = '\0';
12083d63ea05Sas145665 
12093d63ea05Sas145665 	if (yes_check(resp))
12103d63ea05Sas145665 		return (doexec(name, argv, NULL));
12117c478bd9Sstevel@tonic-gate 	else
12123d63ea05Sas145665 		return (0);
12137c478bd9Sstevel@tonic-gate }
12147c478bd9Sstevel@tonic-gate 
12157c478bd9Sstevel@tonic-gate /*
12167c478bd9Sstevel@tonic-gate  * execute argv with {} replaced by name
1217d35170d6Srm88369  *
1218d35170d6Srm88369  * Per XPG6, find must exit non-zero if an invocation through
1219d35170d6Srm88369  * -exec, punctuated by a plus sign, exits non-zero, so set
1220d35170d6Srm88369  * exitcode if we see a non-zero exit.
1221d35170d6Srm88369  * exitcode should be NULL when -exec or -ok is not punctuated
1222d35170d6Srm88369  * by a plus sign.
12237c478bd9Sstevel@tonic-gate  */
12247c478bd9Sstevel@tonic-gate 
12257c478bd9Sstevel@tonic-gate static int
doexec(const char * name,char * argv[],int * exitcode)122627d3a169SToomas Soome doexec(const char *name, char *argv[], int *exitcode)
12277c478bd9Sstevel@tonic-gate {
12287c478bd9Sstevel@tonic-gate 	char *cp;
12297c478bd9Sstevel@tonic-gate 	char **av = argv;
1230d35170d6Srm88369 	char *newargs[1 + SHELL_MAXARGS + 1];
12317c478bd9Sstevel@tonic-gate 	int dummyseen = 0;
1232d35170d6Srm88369 	int i, j, status, rc, r = 0;
1233d35170d6Srm88369 	int exit_status = 0;
1234d35170d6Srm88369 	pid_t pid, pid1;
12357c478bd9Sstevel@tonic-gate 
12367c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);	  /* to flush possible `-print' */
12377c478bd9Sstevel@tonic-gate 	if (name) {
12387c478bd9Sstevel@tonic-gate 		while (cp = *av++) {
12397c478bd9Sstevel@tonic-gate 			if (cp == dummyarg) {
12407c478bd9Sstevel@tonic-gate 				dummyseen = 1;
124127d3a169SToomas Soome 				av[-1] = (char *)name;
12427c478bd9Sstevel@tonic-gate 			}
12437c478bd9Sstevel@tonic-gate 
12447c478bd9Sstevel@tonic-gate 		}
12457c478bd9Sstevel@tonic-gate 	}
12467c478bd9Sstevel@tonic-gate 	if (argv[0] == NULL)    /* null command line */
12477c478bd9Sstevel@tonic-gate 		return (r);
12487c478bd9Sstevel@tonic-gate 
1249d35170d6Srm88369 	if ((pid = fork()) == -1) {
1250d35170d6Srm88369 		/* fork failed */
1251d35170d6Srm88369 		if (exitcode != NULL)
1252d35170d6Srm88369 			*exitcode = 1;
1253d35170d6Srm88369 		return (0);
1254d35170d6Srm88369 	}
1255d35170d6Srm88369 	if (pid != 0) {
1256d35170d6Srm88369 		/* parent */
1257d35170d6Srm88369 		do {
1258d35170d6Srm88369 			/* wait for child to exit */
1259d35170d6Srm88369 			if ((rc = wait(&r)) == -1 && errno != EINTR) {
1260d35170d6Srm88369 				(void) fprintf(stderr,
1261d35170d6Srm88369 				    gettext("wait failed %s"), strerror(errno));
1262d35170d6Srm88369 
1263d35170d6Srm88369 				if (exitcode != NULL)
1264d35170d6Srm88369 					*exitcode = 1;
1265d35170d6Srm88369 				return (0);
1266d35170d6Srm88369 			}
1267d35170d6Srm88369 		} while (rc != pid);
1268d35170d6Srm88369 	} else {
1269d35170d6Srm88369 		/* child */
12707c478bd9Sstevel@tonic-gate 		(void) execvp(argv[0], argv);
1271d35170d6Srm88369 		if (errno != E2BIG)
1272d35170d6Srm88369 			exit(1);
1273d35170d6Srm88369 
1274d35170d6Srm88369 		/*
1275d35170d6Srm88369 		 * We are in a situation where argv[0] points to a
1276d35170d6Srm88369 		 * script without the interpreter line, e.g. #!/bin/sh.
1277d35170d6Srm88369 		 * execvp() will execute either /usr/bin/sh or
1278d35170d6Srm88369 		 * /usr/xpg4/bin/sh against the script, and you will be
1279d35170d6Srm88369 		 * limited to SHELL_MAXARGS arguments. If you try to
1280d35170d6Srm88369 		 * pass more than SHELL_MAXARGS arguments, execvp()
1281d35170d6Srm88369 		 * fails with E2BIG.
1282d35170d6Srm88369 		 * See usr/src/lib/libc/port/gen/execvp.c.
1283d35170d6Srm88369 		 *
1284d35170d6Srm88369 		 * In this situation, process the argument list by
1285d35170d6Srm88369 		 * packets of SHELL_MAXARGS arguments with respect of
1286d35170d6Srm88369 		 * the following rules:
1287d35170d6Srm88369 		 * 1. the invocations have to complete before find exits
1288d35170d6Srm88369 		 * 2. only one invocation can be running at a time
1289d35170d6Srm88369 		 */
1290d35170d6Srm88369 
1291d35170d6Srm88369 		i = 1;
1292d35170d6Srm88369 		newargs[0] = argv[0];
1293d35170d6Srm88369 
1294d35170d6Srm88369 		while (argv[i]) {
1295d35170d6Srm88369 			j = 1;
1296d35170d6Srm88369 			while (j <= SHELL_MAXARGS && argv[i]) {
1297d35170d6Srm88369 				newargs[j++] = argv[i++];
1298d35170d6Srm88369 			}
1299d35170d6Srm88369 			newargs[j] = NULL;
1300d35170d6Srm88369 
1301d35170d6Srm88369 			if ((pid1 = fork()) == -1) {
1302d35170d6Srm88369 				/* fork failed */
13037c478bd9Sstevel@tonic-gate 				exit(1);
13047c478bd9Sstevel@tonic-gate 			}
1305d35170d6Srm88369 			if (pid1 == 0) {
1306d35170d6Srm88369 				/* child */
1307d35170d6Srm88369 				(void) execvp(newargs[0], newargs);
1308d35170d6Srm88369 				exit(1);
1309d35170d6Srm88369 			}
1310d35170d6Srm88369 
1311d35170d6Srm88369 			status = 0;
1312d35170d6Srm88369 
1313d35170d6Srm88369 			do {
1314d35170d6Srm88369 				/* wait for the child to exit */
1315d35170d6Srm88369 				if ((rc = wait(&status)) == -1 &&
1316d35170d6Srm88369 				    errno != EINTR) {
1317d35170d6Srm88369 					(void) fprintf(stderr,
1318d35170d6Srm88369 					    gettext("wait failed %s"),
1319d35170d6Srm88369 					    strerror(errno));
1320d35170d6Srm88369 					exit(1);
1321d35170d6Srm88369 				}
1322d35170d6Srm88369 			} while (rc != pid1);
1323d35170d6Srm88369 
1324d35170d6Srm88369 			if (status)
1325d35170d6Srm88369 				exit_status = 1;
1326d35170d6Srm88369 		}
1327d35170d6Srm88369 		/* all the invocations have completed */
1328d35170d6Srm88369 		exit(exit_status);
1329d35170d6Srm88369 	}
1330d35170d6Srm88369 
13317c478bd9Sstevel@tonic-gate 	if (name && dummyseen) {
13327c478bd9Sstevel@tonic-gate 		for (av = argv; cp = *av++; ) {
13337c478bd9Sstevel@tonic-gate 			if (cp == name)
13347c478bd9Sstevel@tonic-gate 				av[-1] = dummyarg;
13357c478bd9Sstevel@tonic-gate 		}
13367c478bd9Sstevel@tonic-gate 	}
13377c478bd9Sstevel@tonic-gate 
1338d35170d6Srm88369 	if (r && exitcode != NULL)
1339d35170d6Srm88369 		*exitcode = 3; /* use to indicate error in cmd invocation */
1340d35170d6Srm88369 
13417c478bd9Sstevel@tonic-gate 	return (!r);
13427c478bd9Sstevel@tonic-gate }
13437c478bd9Sstevel@tonic-gate 
1344ab823b7fSPrasad Joshi static int
dodelete(const char * name,const struct stat * statb,struct FTW * state)134527d3a169SToomas Soome dodelete(const char *name, const struct stat *statb, struct FTW *state)
1346ab823b7fSPrasad Joshi {
134727d3a169SToomas Soome 	const char *fn;
1348ab823b7fSPrasad Joshi 	int rc = 0;
1349ab823b7fSPrasad Joshi 
1350ab823b7fSPrasad Joshi 	/* restrict symlinks */
1351ab823b7fSPrasad Joshi 	if ((walkflags & FTW_PHYS) == 0) {
1352ab823b7fSPrasad Joshi 		(void) fprintf(stderr,
1353ab823b7fSPrasad Joshi 		    gettext("-delete is not allowed when symlinks are "
1354ab823b7fSPrasad Joshi 		    "followed.\n"));
1355ab823b7fSPrasad Joshi 		return (1);
1356ab823b7fSPrasad Joshi 	}
1357ab823b7fSPrasad Joshi 
1358ab823b7fSPrasad Joshi 	fn = name + state->base;
1359ab823b7fSPrasad Joshi 	if (strcmp(fn, ".") == 0) {
1360ab823b7fSPrasad Joshi 		/* nothing to do */
1361ab823b7fSPrasad Joshi 		return (1);
1362ab823b7fSPrasad Joshi 	}
1363ab823b7fSPrasad Joshi 
1364ab823b7fSPrasad Joshi 	if (strchr(fn, '/') != NULL) {
1365ab823b7fSPrasad Joshi 		(void) fprintf(stderr,
1366ab823b7fSPrasad Joshi 		    gettext("-delete with relative path is unsafe."));
1367ab823b7fSPrasad Joshi 		return (1);
1368ab823b7fSPrasad Joshi 	}
1369ab823b7fSPrasad Joshi 
1370ab823b7fSPrasad Joshi 	if (S_ISDIR(statb->st_mode)) {
1371ab823b7fSPrasad Joshi 		/* delete directory */
1372ab823b7fSPrasad Joshi 		rc = rmdir(name);
1373ab823b7fSPrasad Joshi 	} else {
1374ab823b7fSPrasad Joshi 		/* delete file */
1375ab823b7fSPrasad Joshi 		rc = unlink(name);
1376ab823b7fSPrasad Joshi 	}
1377ab823b7fSPrasad Joshi 
1378ab823b7fSPrasad Joshi 	if (rc < 0) {
1379ab823b7fSPrasad Joshi 		/* operation failed */
1380ab823b7fSPrasad Joshi 		(void) fprintf(stderr, gettext("delete failed %s: %s\n"),
1381ab823b7fSPrasad Joshi 		    name, strerror(errno));
1382ab823b7fSPrasad Joshi 		return (1);
1383ab823b7fSPrasad Joshi 	}
1384ab823b7fSPrasad Joshi 
1385ab823b7fSPrasad Joshi 	return (1);
1386ab823b7fSPrasad Joshi }
13877c478bd9Sstevel@tonic-gate 
13887c478bd9Sstevel@tonic-gate /*
13897c478bd9Sstevel@tonic-gate  *  Table lookup routine
13907c478bd9Sstevel@tonic-gate  */
13917c478bd9Sstevel@tonic-gate static struct Args *
lookup(char * word)139227d3a169SToomas Soome lookup(char *word)
13937c478bd9Sstevel@tonic-gate {
13947c478bd9Sstevel@tonic-gate 	struct Args *argp = commands;
13957c478bd9Sstevel@tonic-gate 	int second;
13967c478bd9Sstevel@tonic-gate 	if (word == 0 || *word == 0)
13977c478bd9Sstevel@tonic-gate 		return (0);
13987c478bd9Sstevel@tonic-gate 	second = word[1];
13997c478bd9Sstevel@tonic-gate 	while (*argp->name) {
14007c478bd9Sstevel@tonic-gate 		if (second == argp->name[1] && strcmp(word, argp->name) == 0)
14017c478bd9Sstevel@tonic-gate 			return (argp);
14027c478bd9Sstevel@tonic-gate 		argp++;
14037c478bd9Sstevel@tonic-gate 	}
14047c478bd9Sstevel@tonic-gate 	return (0);
14057c478bd9Sstevel@tonic-gate }
14067c478bd9Sstevel@tonic-gate 
14077c478bd9Sstevel@tonic-gate 
14087c478bd9Sstevel@tonic-gate /*
14097c478bd9Sstevel@tonic-gate  * Get space for variable length argument list
14107c478bd9Sstevel@tonic-gate  */
14117c478bd9Sstevel@tonic-gate 
14127c478bd9Sstevel@tonic-gate static struct Arglist *
varargs(char ** com)141327d3a169SToomas Soome varargs(char **com)
14147c478bd9Sstevel@tonic-gate {
14157c478bd9Sstevel@tonic-gate 	struct Arglist *ap;
14167c478bd9Sstevel@tonic-gate 	int n;
14177c478bd9Sstevel@tonic-gate 	char **ep;
14187c478bd9Sstevel@tonic-gate 	if (varsize == 0) {
14197c478bd9Sstevel@tonic-gate 		n = 2*sizeof (char **);
14207c478bd9Sstevel@tonic-gate 		for (ep = environ; *ep; ep++)
14217c478bd9Sstevel@tonic-gate 			n += (strlen(*ep)+sizeof (ep) + 1);
14227c478bd9Sstevel@tonic-gate 		varsize = sizeof (struct Arglist)+ARG_MAX-PATH_MAX-n-1;
14237c478bd9Sstevel@tonic-gate 	}
14247c478bd9Sstevel@tonic-gate 	ap = (struct Arglist *)malloc(varsize+1);
14257c478bd9Sstevel@tonic-gate 	ap->end = (char *)ap + varsize;
14267c478bd9Sstevel@tonic-gate 	ap->nextstr = ap->end;
14277c478bd9Sstevel@tonic-gate 	ap->nextvar = ap->arglist;
142827d3a169SToomas Soome 	while (*ap->nextvar++ = *com++)
142927d3a169SToomas Soome 		;
14307c478bd9Sstevel@tonic-gate 	ap->nextvar--;
14317c478bd9Sstevel@tonic-gate 	ap->firstvar = ap->nextvar;
14327c478bd9Sstevel@tonic-gate 	ap->next = lastlist;
14337c478bd9Sstevel@tonic-gate 	lastlist = ap;
14347c478bd9Sstevel@tonic-gate 	return (ap);
14357c478bd9Sstevel@tonic-gate }
14367c478bd9Sstevel@tonic-gate 
14377c478bd9Sstevel@tonic-gate /*
14387c478bd9Sstevel@tonic-gate  * filter command support
14397c478bd9Sstevel@tonic-gate  * fork and exec cmd(argv) according to mode:
14407c478bd9Sstevel@tonic-gate  *
14417c478bd9Sstevel@tonic-gate  *	"r"	with fp as stdin of cmd (default stdin), cmd stdout returned
14427c478bd9Sstevel@tonic-gate  *	"w"	with fp as stdout of cmd (default stdout), cmd stdin returned
14437c478bd9Sstevel@tonic-gate  */
14447c478bd9Sstevel@tonic-gate 
14457c478bd9Sstevel@tonic-gate #define	CMDERR	((1<<8)-1)	/* command error exit code		*/
14467c478bd9Sstevel@tonic-gate #define	MAXCMDS	8		/* max # simultaneous cmdopen()'s	*/
14477c478bd9Sstevel@tonic-gate 
14487c478bd9Sstevel@tonic-gate static struct			/* info for each cmdopen()		*/
14497c478bd9Sstevel@tonic-gate {
14507c478bd9Sstevel@tonic-gate 	FILE	*fp;		/* returned by cmdopen()		*/
14517c478bd9Sstevel@tonic-gate 	pid_t	pid;		/* pid used by cmdopen()		*/
14527c478bd9Sstevel@tonic-gate } cmdproc[MAXCMDS];
14537c478bd9Sstevel@tonic-gate 
14547c478bd9Sstevel@tonic-gate static FILE *
cmdopen(char * cmd,char ** argv,char * mode,FILE * fp)145527d3a169SToomas Soome cmdopen(char *cmd, char **argv, char *mode, FILE *fp)
14567c478bd9Sstevel@tonic-gate {
14577c478bd9Sstevel@tonic-gate 	int	proc;
14587c478bd9Sstevel@tonic-gate 	int	cmdfd;
14597c478bd9Sstevel@tonic-gate 	int	usrfd;
14607c478bd9Sstevel@tonic-gate 	int		pio[2];
14617c478bd9Sstevel@tonic-gate 
14627c478bd9Sstevel@tonic-gate 	switch (*mode) {
14637c478bd9Sstevel@tonic-gate 	case 'r':
14647c478bd9Sstevel@tonic-gate 		cmdfd = 1;
14657c478bd9Sstevel@tonic-gate 		usrfd = 0;
14667c478bd9Sstevel@tonic-gate 		break;
14677c478bd9Sstevel@tonic-gate 	case 'w':
14687c478bd9Sstevel@tonic-gate 		cmdfd = 0;
14697c478bd9Sstevel@tonic-gate 		usrfd = 1;
14707c478bd9Sstevel@tonic-gate 		break;
14717c478bd9Sstevel@tonic-gate 	default:
14727c478bd9Sstevel@tonic-gate 		return (0);
14737c478bd9Sstevel@tonic-gate 	}
14747c478bd9Sstevel@tonic-gate 
14757c478bd9Sstevel@tonic-gate 	for (proc = 0; proc < MAXCMDS; proc++)
14767c478bd9Sstevel@tonic-gate 		if (!cmdproc[proc].fp)
14777c478bd9Sstevel@tonic-gate 			break;
14787c478bd9Sstevel@tonic-gate 	if (proc >= MAXCMDS)
14797c478bd9Sstevel@tonic-gate 		return (0);
14807c478bd9Sstevel@tonic-gate 
14817c478bd9Sstevel@tonic-gate 	if (pipe(pio))
14827c478bd9Sstevel@tonic-gate 		return (0);
14837c478bd9Sstevel@tonic-gate 
14847c478bd9Sstevel@tonic-gate 	switch (cmdproc[proc].pid = fork()) {
14857c478bd9Sstevel@tonic-gate 	case -1:
14867c478bd9Sstevel@tonic-gate 		return (0);
14877c478bd9Sstevel@tonic-gate 	case 0:
14887c478bd9Sstevel@tonic-gate 		if (fp && fileno(fp) != usrfd) {
14897c478bd9Sstevel@tonic-gate 			(void) close(usrfd);
14907c478bd9Sstevel@tonic-gate 			if (dup2(fileno(fp), usrfd) != usrfd)
14917c478bd9Sstevel@tonic-gate 				_exit(CMDERR);
14927c478bd9Sstevel@tonic-gate 			(void) close(fileno(fp));
14937c478bd9Sstevel@tonic-gate 		}
14947c478bd9Sstevel@tonic-gate 		(void) close(cmdfd);
14957c478bd9Sstevel@tonic-gate 		if (dup2(pio[cmdfd], cmdfd) != cmdfd)
14967c478bd9Sstevel@tonic-gate 			_exit(CMDERR);
14977c478bd9Sstevel@tonic-gate 		(void) close(pio[cmdfd]);
14987c478bd9Sstevel@tonic-gate 		(void) close(pio[usrfd]);
14997c478bd9Sstevel@tonic-gate 		(void) execvp(cmd, argv);
15007c478bd9Sstevel@tonic-gate 		if (errno == ENOEXEC) {
15017c478bd9Sstevel@tonic-gate 			char	**p;
15027c478bd9Sstevel@tonic-gate 			char		**v;
15037c478bd9Sstevel@tonic-gate 
15047c478bd9Sstevel@tonic-gate 			/*
15057c478bd9Sstevel@tonic-gate 			 * assume cmd is a shell script
15067c478bd9Sstevel@tonic-gate 			 */
15077c478bd9Sstevel@tonic-gate 
15087c478bd9Sstevel@tonic-gate 			p = argv;
150927d3a169SToomas Soome 			while (*p++)
151027d3a169SToomas Soome 				;
151127d3a169SToomas Soome 			if (v = malloc((p - argv + 1) * sizeof (char **))) {
15127c478bd9Sstevel@tonic-gate 				p = v;
15137c478bd9Sstevel@tonic-gate 				*p++ = cmd;
151427d3a169SToomas Soome 				if (*argv)
151527d3a169SToomas Soome 					argv++;
151627d3a169SToomas Soome 				while (*p++ = *argv++)
151727d3a169SToomas Soome 					;
15187c478bd9Sstevel@tonic-gate 				(void) execv(getshell(), v);
15197c478bd9Sstevel@tonic-gate 			}
15207c478bd9Sstevel@tonic-gate 		}
15217c478bd9Sstevel@tonic-gate 		_exit(CMDERR);
15227c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
15237c478bd9Sstevel@tonic-gate 	default:
15247c478bd9Sstevel@tonic-gate 		(void) close(pio[cmdfd]);
15257c478bd9Sstevel@tonic-gate 		return (cmdproc[proc].fp = fdopen(pio[usrfd], mode));
15267c478bd9Sstevel@tonic-gate 	}
15277c478bd9Sstevel@tonic-gate }
15287c478bd9Sstevel@tonic-gate 
15297c478bd9Sstevel@tonic-gate /*
15307c478bd9Sstevel@tonic-gate  * close a stream opened by cmdopen()
15317c478bd9Sstevel@tonic-gate  * -1 returned if cmdopen() had a problem
15327c478bd9Sstevel@tonic-gate  * otherwise exit() status of command is returned
15337c478bd9Sstevel@tonic-gate  */
15347c478bd9Sstevel@tonic-gate 
15357c478bd9Sstevel@tonic-gate static int
cmdclose(FILE * fp)153627d3a169SToomas Soome cmdclose(FILE *fp)
15377c478bd9Sstevel@tonic-gate {
15387c478bd9Sstevel@tonic-gate 	int	i;
15397c478bd9Sstevel@tonic-gate 	pid_t	p, pid;
15407c478bd9Sstevel@tonic-gate 	int		status;
15417c478bd9Sstevel@tonic-gate 
15427c478bd9Sstevel@tonic-gate 	for (i = 0; i < MAXCMDS; i++)
15437c478bd9Sstevel@tonic-gate 		if (fp == cmdproc[i].fp) break;
15447c478bd9Sstevel@tonic-gate 	if (i >= MAXCMDS)
15457c478bd9Sstevel@tonic-gate 		return (-1);
15467c478bd9Sstevel@tonic-gate 	(void) fclose(fp);
15477c478bd9Sstevel@tonic-gate 	cmdproc[i].fp = 0;
15487c478bd9Sstevel@tonic-gate 	pid = cmdproc[i].pid;
154927d3a169SToomas Soome 	while ((p = wait(&status)) != pid && p != (pid_t)-1)
155027d3a169SToomas Soome 		;
15517c478bd9Sstevel@tonic-gate 	if (p == pid) {
15527c478bd9Sstevel@tonic-gate 		status = (status >> 8) & CMDERR;
15537c478bd9Sstevel@tonic-gate 		if (status == CMDERR)
15547c478bd9Sstevel@tonic-gate 			status = -1;
15557c478bd9Sstevel@tonic-gate 	}
15567c478bd9Sstevel@tonic-gate 	else
15577c478bd9Sstevel@tonic-gate 		status = -1;
15587c478bd9Sstevel@tonic-gate 	return (status);
15597c478bd9Sstevel@tonic-gate }
15607c478bd9Sstevel@tonic-gate 
15617c478bd9Sstevel@tonic-gate /*
15627c478bd9Sstevel@tonic-gate  * return pointer to the full path name of the shell
15637c478bd9Sstevel@tonic-gate  *
15647c478bd9Sstevel@tonic-gate  * SHELL is read from the environment and must start with /
15657c478bd9Sstevel@tonic-gate  *
15667c478bd9Sstevel@tonic-gate  * if set-uid or set-gid then the executable and its containing
15677c478bd9Sstevel@tonic-gate  * directory must not be writable by the real user
15687c478bd9Sstevel@tonic-gate  *
15697c478bd9Sstevel@tonic-gate  * /usr/bin/sh is returned by default
15707c478bd9Sstevel@tonic-gate  */
15717c478bd9Sstevel@tonic-gate 
15727c478bd9Sstevel@tonic-gate char *
getshell(void)157327d3a169SToomas Soome getshell(void)
15747c478bd9Sstevel@tonic-gate {
15757c478bd9Sstevel@tonic-gate 	char	*s;
15767c478bd9Sstevel@tonic-gate 	char	*sh;
15777c478bd9Sstevel@tonic-gate 	uid_t	u;
15787c478bd9Sstevel@tonic-gate 	int	j;
15797c478bd9Sstevel@tonic-gate 
15807c478bd9Sstevel@tonic-gate 	if (((sh = getenv("SHELL")) != 0) && *sh == '/') {
15817c478bd9Sstevel@tonic-gate 		if (u = getuid()) {
15827c478bd9Sstevel@tonic-gate 			if ((u != geteuid() || getgid() != getegid()) &&
15833d63ea05Sas145665 			    access(sh, 2) == 0)
15847c478bd9Sstevel@tonic-gate 				goto defshell;
15857c478bd9Sstevel@tonic-gate 			s = strrchr(sh, '/');
15867c478bd9Sstevel@tonic-gate 			*s = 0;
15877c478bd9Sstevel@tonic-gate 			j = access(sh, 2);
15887c478bd9Sstevel@tonic-gate 			*s = '/';
15897c478bd9Sstevel@tonic-gate 			if (!j) goto defshell;
15907c478bd9Sstevel@tonic-gate 		}
15917c478bd9Sstevel@tonic-gate 		return (sh);
15927c478bd9Sstevel@tonic-gate 	}
15937c478bd9Sstevel@tonic-gate defshell:
15947c478bd9Sstevel@tonic-gate 	return ("/usr/bin/sh");
15957c478bd9Sstevel@tonic-gate }
15967c478bd9Sstevel@tonic-gate 
15977c478bd9Sstevel@tonic-gate /*
15987c478bd9Sstevel@tonic-gate  * the following functions implement the added "-ls" option
15997c478bd9Sstevel@tonic-gate  */
16007c478bd9Sstevel@tonic-gate 
16017c478bd9Sstevel@tonic-gate #include <utmpx.h>
16027c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
16037c478bd9Sstevel@tonic-gate 
16047c478bd9Sstevel@tonic-gate struct		utmpx utmpx;
16057c478bd9Sstevel@tonic-gate #define	NMAX	(sizeof (utmpx.ut_name))
16067c478bd9Sstevel@tonic-gate #define	SCPYN(a, b)	(void) strncpy(a, b, NMAX)
16077c478bd9Sstevel@tonic-gate 
16087c478bd9Sstevel@tonic-gate #define	NUID	64
16097c478bd9Sstevel@tonic-gate #define	NGID	64
16107c478bd9Sstevel@tonic-gate 
16117c478bd9Sstevel@tonic-gate static struct ncache {
16127c478bd9Sstevel@tonic-gate 	int	id;
16137c478bd9Sstevel@tonic-gate 	char	name[NMAX+1];
16147c478bd9Sstevel@tonic-gate } nc[NUID], gc[NGID];
16157c478bd9Sstevel@tonic-gate 
16167c478bd9Sstevel@tonic-gate /*
16177c478bd9Sstevel@tonic-gate  * This function assumes that the password file is hashed
16187c478bd9Sstevel@tonic-gate  * (or some such) to allow fast access based on a name key.
16197c478bd9Sstevel@tonic-gate  */
16207c478bd9Sstevel@tonic-gate static char *
getname(uid_t uid)16217c478bd9Sstevel@tonic-gate getname(uid_t uid)
16227c478bd9Sstevel@tonic-gate {
16237c478bd9Sstevel@tonic-gate 	struct passwd *pw;
16247c478bd9Sstevel@tonic-gate 	int cp;
16257c478bd9Sstevel@tonic-gate 
16267c478bd9Sstevel@tonic-gate #if	(((NUID) & ((NUID) - 1)) != 0)
16277c478bd9Sstevel@tonic-gate 	cp = uid % (NUID);
16287c478bd9Sstevel@tonic-gate #else
16297c478bd9Sstevel@tonic-gate 	cp = uid & ((NUID) - 1);
16307c478bd9Sstevel@tonic-gate #endif
1631f48205beScasper 	if (nc[cp].id == uid && nc[cp].name[0])
16327c478bd9Sstevel@tonic-gate 		return (nc[cp].name);
16337c478bd9Sstevel@tonic-gate 	pw = getpwuid(uid);
16347c478bd9Sstevel@tonic-gate 	if (!pw)
16357c478bd9Sstevel@tonic-gate 		return (0);
16367c478bd9Sstevel@tonic-gate 	nc[cp].id = uid;
16377c478bd9Sstevel@tonic-gate 	SCPYN(nc[cp].name, pw->pw_name);
16387c478bd9Sstevel@tonic-gate 	return (nc[cp].name);
16397c478bd9Sstevel@tonic-gate }
16407c478bd9Sstevel@tonic-gate 
16417c478bd9Sstevel@tonic-gate /*
16427c478bd9Sstevel@tonic-gate  * This function assumes that the group file is hashed
16437c478bd9Sstevel@tonic-gate  * (or some such) to allow fast access based on a name key.
16447c478bd9Sstevel@tonic-gate  */
16457c478bd9Sstevel@tonic-gate static char *
getgroup(gid_t gid)16467c478bd9Sstevel@tonic-gate getgroup(gid_t gid)
16477c478bd9Sstevel@tonic-gate {
16487c478bd9Sstevel@tonic-gate 	struct group *gr;
16497c478bd9Sstevel@tonic-gate 	int cp;
16507c478bd9Sstevel@tonic-gate 
16517c478bd9Sstevel@tonic-gate #if	(((NGID) & ((NGID) - 1)) != 0)
16527c478bd9Sstevel@tonic-gate 	cp = gid % (NGID);
16537c478bd9Sstevel@tonic-gate #else
16547c478bd9Sstevel@tonic-gate 	cp = gid & ((NGID) - 1);
16557c478bd9Sstevel@tonic-gate #endif
1656f48205beScasper 	if (gc[cp].id == gid && gc[cp].name[0])
16577c478bd9Sstevel@tonic-gate 		return (gc[cp].name);
16587c478bd9Sstevel@tonic-gate 	gr = getgrgid(gid);
16597c478bd9Sstevel@tonic-gate 	if (!gr)
16607c478bd9Sstevel@tonic-gate 		return (0);
16617c478bd9Sstevel@tonic-gate 	gc[cp].id = gid;
16627c478bd9Sstevel@tonic-gate 	SCPYN(gc[cp].name, gr->gr_name);
16637c478bd9Sstevel@tonic-gate 	return (gc[cp].name);
16647c478bd9Sstevel@tonic-gate }
16657c478bd9Sstevel@tonic-gate 
16667c478bd9Sstevel@tonic-gate #define	permoffset(who)		((who) * 3)
16677c478bd9Sstevel@tonic-gate #define	permission(who, type)	((type) >> permoffset(who))
16687c478bd9Sstevel@tonic-gate #define	kbytes(bytes)		(((bytes) + 1023) / 1024)
16697c478bd9Sstevel@tonic-gate 
16707c478bd9Sstevel@tonic-gate static int
list(const char * file,const struct stat * stp)167127d3a169SToomas Soome list(const char *file, const struct stat *stp)
16727c478bd9Sstevel@tonic-gate {
16737c478bd9Sstevel@tonic-gate 	char pmode[32], uname[32], gname[32], fsize[32], ftime[32];
1674fa9e4066Sahrens 	int trivial;
16757c478bd9Sstevel@tonic-gate 
16767c478bd9Sstevel@tonic-gate /*
16777c478bd9Sstevel@tonic-gate  * Each line below contains the relevant permission (column 1) and character
16787c478bd9Sstevel@tonic-gate  * shown when  the corresponding execute bit is either clear (column 2)
16797c478bd9Sstevel@tonic-gate  * or set (column 3)
16807c478bd9Sstevel@tonic-gate  * These permissions are as shown by ls(1b)
16817c478bd9Sstevel@tonic-gate  */
16827c478bd9Sstevel@tonic-gate 	static long special[] = {	S_ISUID, 'S', 's',
16837c478bd9Sstevel@tonic-gate 					S_ISGID, 'S', 's',
16847c478bd9Sstevel@tonic-gate 					S_ISVTX, 'T', 't' };
16857c478bd9Sstevel@tonic-gate 
16867c478bd9Sstevel@tonic-gate 	static time_t sixmonthsago = -1;
16877c478bd9Sstevel@tonic-gate #ifdef	S_IFLNK
16887c478bd9Sstevel@tonic-gate 	char flink[MAXPATHLEN + 1];
16897c478bd9Sstevel@tonic-gate #endif
16907c478bd9Sstevel@tonic-gate 	int who;
16917c478bd9Sstevel@tonic-gate 	char *cp;
169227d3a169SToomas Soome 	const char *tailname;
16937c478bd9Sstevel@tonic-gate 	time_t now;
16947c478bd9Sstevel@tonic-gate 	long long ksize;
16957c478bd9Sstevel@tonic-gate 
16967c478bd9Sstevel@tonic-gate 	if (file == NULL || stp == NULL)
16977c478bd9Sstevel@tonic-gate 		return (-1);
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate 	(void) time(&now);
17007c478bd9Sstevel@tonic-gate 	if (sixmonthsago == -1)
17017c478bd9Sstevel@tonic-gate 		sixmonthsago = now - 6L*30L*24L*60L*60L;
17027c478bd9Sstevel@tonic-gate 
17037c478bd9Sstevel@tonic-gate 	switch (stp->st_mode & S_IFMT) {
17047c478bd9Sstevel@tonic-gate #ifdef	S_IFDIR
17057c478bd9Sstevel@tonic-gate 	case S_IFDIR:	/* directory */
17067c478bd9Sstevel@tonic-gate 		pmode[0] = 'd';
17077c478bd9Sstevel@tonic-gate 		break;
17087c478bd9Sstevel@tonic-gate #endif
17097c478bd9Sstevel@tonic-gate #ifdef	S_IFCHR
17107c478bd9Sstevel@tonic-gate 	case S_IFCHR:	/* character special */
17117c478bd9Sstevel@tonic-gate 		pmode[0] = 'c';
17127c478bd9Sstevel@tonic-gate 		break;
17137c478bd9Sstevel@tonic-gate #endif
17147c478bd9Sstevel@tonic-gate #ifdef	S_IFBLK
17157c478bd9Sstevel@tonic-gate 	case S_IFBLK:	/* block special */
17167c478bd9Sstevel@tonic-gate 		pmode[0] = 'b';
17177c478bd9Sstevel@tonic-gate 		break;
17187c478bd9Sstevel@tonic-gate #endif
17197c478bd9Sstevel@tonic-gate #ifdef	S_IFIFO
17207c478bd9Sstevel@tonic-gate 	case S_IFIFO:	/* fifo special */
17217c478bd9Sstevel@tonic-gate 		pmode[0] = 'p';
17227c478bd9Sstevel@tonic-gate 		break;
17237c478bd9Sstevel@tonic-gate #endif
17247c478bd9Sstevel@tonic-gate #ifdef	S_IFLNK
17257c478bd9Sstevel@tonic-gate 	case S_IFLNK:	/* symbolic link */
17267c478bd9Sstevel@tonic-gate 		pmode[0] = 'l';
17277c478bd9Sstevel@tonic-gate 		break;
17287c478bd9Sstevel@tonic-gate #endif
17297c478bd9Sstevel@tonic-gate #ifdef	S_IFSOCK
17307c478bd9Sstevel@tonic-gate 	case S_IFSOCK:	/* socket */
17317c478bd9Sstevel@tonic-gate 		pmode[0] = 's';
17327c478bd9Sstevel@tonic-gate 		break;
17337c478bd9Sstevel@tonic-gate #endif
17347c478bd9Sstevel@tonic-gate #ifdef	S_IFDOOR
17357c478bd9Sstevel@tonic-gate 	case S_IFDOOR:	/* door */
17367c478bd9Sstevel@tonic-gate 		pmode[0] = 'D';
17377c478bd9Sstevel@tonic-gate 		break;
17387c478bd9Sstevel@tonic-gate #endif
17397c478bd9Sstevel@tonic-gate #ifdef	S_IFREG
17407c478bd9Sstevel@tonic-gate 	case S_IFREG:	/* regular */
17417c478bd9Sstevel@tonic-gate 		pmode[0] = '-';
17427c478bd9Sstevel@tonic-gate 		break;
17437c478bd9Sstevel@tonic-gate #endif
17447c478bd9Sstevel@tonic-gate 	default:
17457c478bd9Sstevel@tonic-gate 		pmode[0] = '?';
17467c478bd9Sstevel@tonic-gate 		break;
17477c478bd9Sstevel@tonic-gate 	}
17487c478bd9Sstevel@tonic-gate 
17497c478bd9Sstevel@tonic-gate 	for (who = 0; who < 3; who++) {
17507c478bd9Sstevel@tonic-gate 		int is_exec =  stp->st_mode & permission(who, S_IEXEC)? 1 : 0;
17517c478bd9Sstevel@tonic-gate 
17527c478bd9Sstevel@tonic-gate 		if (stp->st_mode & permission(who, S_IREAD))
17537c478bd9Sstevel@tonic-gate 			pmode[permoffset(who) + 1] = 'r';
17547c478bd9Sstevel@tonic-gate 		else
17557c478bd9Sstevel@tonic-gate 			pmode[permoffset(who) + 1] = '-';
17567c478bd9Sstevel@tonic-gate 
17577c478bd9Sstevel@tonic-gate 		if (stp->st_mode & permission(who, S_IWRITE))
17587c478bd9Sstevel@tonic-gate 			pmode[permoffset(who) + 2] = 'w';
17597c478bd9Sstevel@tonic-gate 		else
17607c478bd9Sstevel@tonic-gate 			pmode[permoffset(who) + 2] = '-';
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate 		if (stp->st_mode & special[who * 3])
17637c478bd9Sstevel@tonic-gate 			pmode[permoffset(who) + 3] =
17647c478bd9Sstevel@tonic-gate 			    special[who * 3 + 1 + is_exec];
17657c478bd9Sstevel@tonic-gate 		else if (is_exec)
17667c478bd9Sstevel@tonic-gate 			pmode[permoffset(who) + 3] = 'x';
17677c478bd9Sstevel@tonic-gate 		else
17687c478bd9Sstevel@tonic-gate 			pmode[permoffset(who) + 3] = '-';
17697c478bd9Sstevel@tonic-gate 	}
17707c478bd9Sstevel@tonic-gate 
17717c478bd9Sstevel@tonic-gate 	/*
17727c478bd9Sstevel@tonic-gate 	 * Need to get the tail of the file name, since we have
17737c478bd9Sstevel@tonic-gate 	 * already chdir()ed into the directory of the file
17747c478bd9Sstevel@tonic-gate 	 */
17757c478bd9Sstevel@tonic-gate 
17767c478bd9Sstevel@tonic-gate 	tailname = gettail(file);
17777c478bd9Sstevel@tonic-gate 
1778fa9e4066Sahrens 	trivial = acl_trivial(tailname);
1779fa9e4066Sahrens 	if (trivial == -1)
1780fa9e4066Sahrens 		trivial =  0;
1781fa9e4066Sahrens 
1782fa9e4066Sahrens 	if (trivial == 1)
17837c478bd9Sstevel@tonic-gate 		pmode[permoffset(who) + 1] = '+';
17847c478bd9Sstevel@tonic-gate 	else
17857c478bd9Sstevel@tonic-gate 		pmode[permoffset(who) + 1] = ' ';
17867c478bd9Sstevel@tonic-gate 
17877c478bd9Sstevel@tonic-gate 	pmode[permoffset(who) + 2] = '\0';
17887c478bd9Sstevel@tonic-gate 
17897c478bd9Sstevel@tonic-gate 	/*
17907c478bd9Sstevel@tonic-gate 	 * Prepare uname and gname.  Always add a space afterwards
17917c478bd9Sstevel@tonic-gate 	 * to keep columns from running together.
17927c478bd9Sstevel@tonic-gate 	 */
17937c478bd9Sstevel@tonic-gate 	cp = getname(stp->st_uid);
17947c478bd9Sstevel@tonic-gate 	if (cp != NULL)
17957c478bd9Sstevel@tonic-gate 		(void) sprintf(uname, "%-8s ", cp);
17967c478bd9Sstevel@tonic-gate 	else
1797f48205beScasper 		(void) sprintf(uname, "%-8u ", stp->st_uid);
17987c478bd9Sstevel@tonic-gate 
17997c478bd9Sstevel@tonic-gate 	cp = getgroup(stp->st_gid);
18007c478bd9Sstevel@tonic-gate 	if (cp != NULL)
18017c478bd9Sstevel@tonic-gate 		(void) sprintf(gname, "%-8s ", cp);
18027c478bd9Sstevel@tonic-gate 	else
1803f48205beScasper 		(void) sprintf(gname, "%-8u ", stp->st_gid);
18047c478bd9Sstevel@tonic-gate 
18057c478bd9Sstevel@tonic-gate 	if (pmode[0] == 'b' || pmode[0] == 'c')
18067c478bd9Sstevel@tonic-gate 		(void) sprintf(fsize, "%3ld,%4ld",
18077c478bd9Sstevel@tonic-gate 		    major(stp->st_rdev), minor(stp->st_rdev));
18087c478bd9Sstevel@tonic-gate 	else {
18097c478bd9Sstevel@tonic-gate 		(void) sprintf(fsize, (stp->st_size < 100000000) ?
18107c478bd9Sstevel@tonic-gate 		    "%8lld" : "%lld", stp->st_size);
18117c478bd9Sstevel@tonic-gate #ifdef	S_IFLNK
18127c478bd9Sstevel@tonic-gate 		if (pmode[0] == 'l') {
18137c478bd9Sstevel@tonic-gate 			who = readlink(tailname, flink, sizeof (flink) - 1);
18147c478bd9Sstevel@tonic-gate 
18157c478bd9Sstevel@tonic-gate 			if (who >= 0)
18167c478bd9Sstevel@tonic-gate 				flink[who] = '\0';
18177c478bd9Sstevel@tonic-gate 			else
18187c478bd9Sstevel@tonic-gate 				flink[0] = '\0';
18197c478bd9Sstevel@tonic-gate 		}
18207c478bd9Sstevel@tonic-gate #endif
18217c478bd9Sstevel@tonic-gate 	}
18227c478bd9Sstevel@tonic-gate 
18237c478bd9Sstevel@tonic-gate 	cp = ctime(&stp->st_mtime);
18247c478bd9Sstevel@tonic-gate 	if (stp->st_mtime < sixmonthsago || stp->st_mtime > now)
18257c478bd9Sstevel@tonic-gate 		(void) sprintf(ftime, "%-7.7s %-4.4s", cp + 4, cp + 20);
18267c478bd9Sstevel@tonic-gate 	else
18277c478bd9Sstevel@tonic-gate 		(void) sprintf(ftime, "%-12.12s", cp + 4);
18287c478bd9Sstevel@tonic-gate 
18297c478bd9Sstevel@tonic-gate 	(void) printf((stp->st_ino < 100000) ? "%5llu " :
18307c478bd9Sstevel@tonic-gate 	    "%llu ", stp->st_ino);  /* inode #	*/
18317c478bd9Sstevel@tonic-gate #ifdef	S_IFSOCK
18327c478bd9Sstevel@tonic-gate 	ksize = (long long) kbytes(ldbtob(stp->st_blocks)); /* kbytes */
18337c478bd9Sstevel@tonic-gate #else
18347c478bd9Sstevel@tonic-gate 	ksize = (long long) kbytes(stp->st_size); /* kbytes */
18357c478bd9Sstevel@tonic-gate #endif
18367c478bd9Sstevel@tonic-gate 	(void) printf((ksize < 10000) ? "%4lld " : "%lld ", ksize);
183727d3a169SToomas Soome #ifdef	S_IFLNK
18387c478bd9Sstevel@tonic-gate 	(void) printf("%s %2ld %s%s%s %s %s%s%s\n",
18397c478bd9Sstevel@tonic-gate 	    pmode,					/* protection	*/
18407c478bd9Sstevel@tonic-gate 	    stp->st_nlink,				/* # of links	*/
18417c478bd9Sstevel@tonic-gate 	    uname,					/* owner	*/
18427c478bd9Sstevel@tonic-gate 	    gname,					/* group	*/
18437c478bd9Sstevel@tonic-gate 	    fsize,					/* # of bytes	*/
18447c478bd9Sstevel@tonic-gate 	    ftime,					/* modify time	*/
18457c478bd9Sstevel@tonic-gate 	    file,					/* name		*/
18467c478bd9Sstevel@tonic-gate 	    (pmode[0] == 'l') ? " -> " : "",
184727d3a169SToomas Soome 	    (pmode[0] == 'l') ? flink  : "");		/* symlink	*/
18487c478bd9Sstevel@tonic-gate #else
184927d3a169SToomas Soome 	(void) printf("%s %2ld %s%s%s %s %s\n",
185027d3a169SToomas Soome 	    pmode,					/* protection	*/
185127d3a169SToomas Soome 	    stp->st_nlink,				/* # of links	*/
185227d3a169SToomas Soome 	    uname,					/* owner	*/
185327d3a169SToomas Soome 	    gname,					/* group	*/
185427d3a169SToomas Soome 	    fsize,					/* # of bytes	*/
185527d3a169SToomas Soome 	    ftime,					/* modify time	*/
185627d3a169SToomas Soome 	    file);					/* name		*/
18577c478bd9Sstevel@tonic-gate #endif
18587c478bd9Sstevel@tonic-gate 
18597c478bd9Sstevel@tonic-gate 	return (0);
18607c478bd9Sstevel@tonic-gate }
18617c478bd9Sstevel@tonic-gate 
18627c478bd9Sstevel@tonic-gate static char *
new_string(char * s)18637c478bd9Sstevel@tonic-gate new_string(char *s)
18647c478bd9Sstevel@tonic-gate {
18657c478bd9Sstevel@tonic-gate 	char *p = strdup(s);
18667c478bd9Sstevel@tonic-gate 
18677c478bd9Sstevel@tonic-gate 	if (p)
18687c478bd9Sstevel@tonic-gate 		return (p);
18697c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("%s: out of memory\n"), cmdname);
18707c478bd9Sstevel@tonic-gate 	exit(1);
18717c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
18727c478bd9Sstevel@tonic-gate }
18737c478bd9Sstevel@tonic-gate 
18747c478bd9Sstevel@tonic-gate /*
18757c478bd9Sstevel@tonic-gate  * Read remote file system types from REMOTE_FS into the
18767c478bd9Sstevel@tonic-gate  * remote_fstypes array.
18777c478bd9Sstevel@tonic-gate  */
18787c478bd9Sstevel@tonic-gate static void
init_remote_fs(void)187927d3a169SToomas Soome init_remote_fs(void)
18807c478bd9Sstevel@tonic-gate {
18817c478bd9Sstevel@tonic-gate 	FILE    *fp;
18827c478bd9Sstevel@tonic-gate 	char    line_buf[LINEBUF_SIZE];
18837c478bd9Sstevel@tonic-gate 
18847c478bd9Sstevel@tonic-gate 	if ((fp = fopen(REMOTE_FS, "r")) == NULL) {
18857c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
18867c478bd9Sstevel@tonic-gate 		    gettext("%s: Warning: can't open %s, ignored\n"),
18877c478bd9Sstevel@tonic-gate 		    REMOTE_FS, cmdname);
18887c478bd9Sstevel@tonic-gate 		/* Use default string name for NFS */
18897c478bd9Sstevel@tonic-gate 		remote_fstypes[fstype_index++] = "nfs";
18907c478bd9Sstevel@tonic-gate 		return;
18917c478bd9Sstevel@tonic-gate 	}
18927c478bd9Sstevel@tonic-gate 
18937c478bd9Sstevel@tonic-gate 	while (fgets(line_buf, sizeof (line_buf), fp) != NULL) {
18947c478bd9Sstevel@tonic-gate 		char buf[LINEBUF_SIZE];
18957c478bd9Sstevel@tonic-gate 
18967c478bd9Sstevel@tonic-gate 		/* LINTED - unbounded string specifier */
18977c478bd9Sstevel@tonic-gate 		(void) sscanf(line_buf, "%s", buf);
18987c478bd9Sstevel@tonic-gate 		remote_fstypes[fstype_index++] = new_string(buf);
18997c478bd9Sstevel@tonic-gate 
19007c478bd9Sstevel@tonic-gate 		if (fstype_index == N_FSTYPES)
19017c478bd9Sstevel@tonic-gate 			break;
19027c478bd9Sstevel@tonic-gate 	}
19037c478bd9Sstevel@tonic-gate 	(void) fclose(fp);
19047c478bd9Sstevel@tonic-gate }
19057c478bd9Sstevel@tonic-gate 
19067c478bd9Sstevel@tonic-gate #define	NPERM	30			/* Largest machine */
19077c478bd9Sstevel@tonic-gate 
19087c478bd9Sstevel@tonic-gate /*
19097c478bd9Sstevel@tonic-gate  * The PERM struct is the machine that builds permissions.  The p_special
19107c478bd9Sstevel@tonic-gate  * field contains what permissions need to be checked at run-time in
19117c478bd9Sstevel@tonic-gate  * getmode().  This is one of 'X', 'u', 'g', or 'o'.  It contains '\0' to
19127c478bd9Sstevel@tonic-gate  * indicate normal processing.
19137c478bd9Sstevel@tonic-gate  */
19147c478bd9Sstevel@tonic-gate typedef	struct	PERMST	{
19157c478bd9Sstevel@tonic-gate 	ushort_t	p_who;		/* Range of permission (e.g. ugo) */
19167c478bd9Sstevel@tonic-gate 	ushort_t	p_perm;		/* Bits to turn on, off, assign */
19177c478bd9Sstevel@tonic-gate 	uchar_t		p_op;		/* Operation: + - = */
19187c478bd9Sstevel@tonic-gate 	uchar_t		p_special;	/* Special handling? */
19197c478bd9Sstevel@tonic-gate }	PERMST;
19207c478bd9Sstevel@tonic-gate 
19217c478bd9Sstevel@tonic-gate #ifndef	S_ISVTX
19227c478bd9Sstevel@tonic-gate #define	S_ISVTX	0			/* Not .1 */
19237c478bd9Sstevel@tonic-gate #endif
19247c478bd9Sstevel@tonic-gate 
19257c478bd9Sstevel@tonic-gate /* Mask values */
19267c478bd9Sstevel@tonic-gate #define	P_A	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) /* allbits */
19277c478bd9Sstevel@tonic-gate #define	P_U	(S_ISUID|S_ISVTX|S_IRWXU)		/* user */
19287c478bd9Sstevel@tonic-gate #define	P_G	(S_ISGID|S_ISVTX|S_IRWXG)		/* group */
19297c478bd9Sstevel@tonic-gate #define	P_O	(S_ISVTX|S_IRWXO)			/* other */
19307c478bd9Sstevel@tonic-gate 
19317c478bd9Sstevel@tonic-gate static	int	iswho(int c);
19327c478bd9Sstevel@tonic-gate static	int	isop(int c);
19337c478bd9Sstevel@tonic-gate static	int	isperm(PERMST *pp, int c);
19347c478bd9Sstevel@tonic-gate 
19357c478bd9Sstevel@tonic-gate static	PERMST	machine[NPERM];		/* Permission construction machine */
19367c478bd9Sstevel@tonic-gate static	PERMST	*endp;			/* Last used PERM structure */
19377c478bd9Sstevel@tonic-gate 
19387c478bd9Sstevel@tonic-gate static	uint_t	nowho;			/* No who for this mode (DOS kludge) */
19397c478bd9Sstevel@tonic-gate 
19407c478bd9Sstevel@tonic-gate /*
19417c478bd9Sstevel@tonic-gate  * Read an ASCII string containing the symbolic/octal mode and
19427c478bd9Sstevel@tonic-gate  * compile an automaton that recognizes it.  The return value
19437c478bd9Sstevel@tonic-gate  * is NULL if everything is OK, otherwise it is -1.
19447c478bd9Sstevel@tonic-gate  */
19457c478bd9Sstevel@tonic-gate static int
readmode(const char * ascmode)194627d3a169SToomas Soome readmode(const char *ascmode)
19477c478bd9Sstevel@tonic-gate {
19487c478bd9Sstevel@tonic-gate 	const char *amode = ascmode;
19497c478bd9Sstevel@tonic-gate 	PERMST *pp;
19507c478bd9Sstevel@tonic-gate 	int seen_X;
19517c478bd9Sstevel@tonic-gate 
19527c478bd9Sstevel@tonic-gate 	nowho = 0;
19537c478bd9Sstevel@tonic-gate 	seen_X = 0;
19547c478bd9Sstevel@tonic-gate 	pp = &machine[0];
19557c478bd9Sstevel@tonic-gate 	if (*amode >= '0' && *amode <= '7') {
19567c478bd9Sstevel@tonic-gate 		int mode;
19577c478bd9Sstevel@tonic-gate 
19587c478bd9Sstevel@tonic-gate 		mode = 0;
19597c478bd9Sstevel@tonic-gate 		while (*amode >= '0' && *amode <= '7')
19607c478bd9Sstevel@tonic-gate 			mode = (mode<<3) + *amode++ - '0';
19617c478bd9Sstevel@tonic-gate 		if (*amode != '\0')
19627c478bd9Sstevel@tonic-gate 			return (-1);
19637c478bd9Sstevel@tonic-gate #if	S_ISUID != 04000 || S_ISGID != 02000 || \
19647c478bd9Sstevel@tonic-gate 	S_IRUSR != 0400 || S_IWUSR != 0200 || S_IXUSR != 0100 || \
19657c478bd9Sstevel@tonic-gate 	S_IRGRP != 0040 || S_IWGRP != 0020 || S_IXGRP != 0010 || \
19667c478bd9Sstevel@tonic-gate 	S_IROTH != 0004 || S_IWOTH != 0002 || S_IXOTH != 0001
19677c478bd9Sstevel@tonic-gate 		/*
19687c478bd9Sstevel@tonic-gate 		 * There is no requirement of the octal mode bits being
19697c478bd9Sstevel@tonic-gate 		 * the same as the S_ macros.
19707c478bd9Sstevel@tonic-gate 		 */
19717c478bd9Sstevel@tonic-gate 	{
19727c478bd9Sstevel@tonic-gate 		mode_t mapping[] = {
19737c478bd9Sstevel@tonic-gate 			S_IXOTH, S_IWOTH, S_IROTH,
19747c478bd9Sstevel@tonic-gate 			S_IXGRP, S_IWGRP, S_IRGRP,
19757c478bd9Sstevel@tonic-gate 			S_IXUSR, S_IWUSR, S_IRUSR,
19767c478bd9Sstevel@tonic-gate 			S_ISGID, S_ISUID,
19777c478bd9Sstevel@tonic-gate 			0
19787c478bd9Sstevel@tonic-gate 		};
19797c478bd9Sstevel@tonic-gate 		int i, newmode = 0;
19807c478bd9Sstevel@tonic-gate 
19817c478bd9Sstevel@tonic-gate 		for (i = 0; mapping[i] != 0; i++)
19827c478bd9Sstevel@tonic-gate 			if (mode & (1<<i))
19837c478bd9Sstevel@tonic-gate 				newmode |= mapping[i];
19847c478bd9Sstevel@tonic-gate 		mode = newmode;
19857c478bd9Sstevel@tonic-gate 	}
19867c478bd9Sstevel@tonic-gate #endif
19877c478bd9Sstevel@tonic-gate 		pp->p_who = P_A;
19887c478bd9Sstevel@tonic-gate 		pp->p_perm = mode;
19897c478bd9Sstevel@tonic-gate 		pp->p_op = '=';
19907c478bd9Sstevel@tonic-gate 	} else	for (;;) {
19917c478bd9Sstevel@tonic-gate 		int t;
19927c478bd9Sstevel@tonic-gate 		int who = 0;
19937c478bd9Sstevel@tonic-gate 
19947c478bd9Sstevel@tonic-gate 		while ((t = iswho(*amode)) != 0) {
19957c478bd9Sstevel@tonic-gate 			++amode;
19967c478bd9Sstevel@tonic-gate 			who |= t;
19977c478bd9Sstevel@tonic-gate 		}
19987c478bd9Sstevel@tonic-gate 		if (who == 0) {
19997c478bd9Sstevel@tonic-gate 			mode_t currmask;
20007c478bd9Sstevel@tonic-gate 			(void) umask(currmask = umask((mode_t)0));
20017c478bd9Sstevel@tonic-gate 
20027c478bd9Sstevel@tonic-gate 			/*
20037c478bd9Sstevel@tonic-gate 			 * If no who specified, must use contents of
20047c478bd9Sstevel@tonic-gate 			 * umask to determine which bits to flip.  This
20057c478bd9Sstevel@tonic-gate 			 * is POSIX/V7/BSD behaviour, but not SVID.
20067c478bd9Sstevel@tonic-gate 			 */
20077c478bd9Sstevel@tonic-gate 			who = (~currmask)&P_A;
20087c478bd9Sstevel@tonic-gate 			++nowho;
20097c478bd9Sstevel@tonic-gate 		} else
20107c478bd9Sstevel@tonic-gate 			nowho = 0;
20117c478bd9Sstevel@tonic-gate 	samewho:
20127c478bd9Sstevel@tonic-gate 		if (!isop(pp->p_op = *amode++))
20137c478bd9Sstevel@tonic-gate 			return (-1);
20147c478bd9Sstevel@tonic-gate 		pp->p_perm = 0;
20157c478bd9Sstevel@tonic-gate 		pp->p_special = 0;
20167c478bd9Sstevel@tonic-gate 		while ((t = isperm(pp, *amode)) != 0) {
20177c478bd9Sstevel@tonic-gate 			if (pp->p_special == 'X') {
20187c478bd9Sstevel@tonic-gate 				seen_X = 1;
20197c478bd9Sstevel@tonic-gate 
20207c478bd9Sstevel@tonic-gate 				if (pp->p_perm != 0) {
20217c478bd9Sstevel@tonic-gate 					ushort_t op;
20227c478bd9Sstevel@tonic-gate 
20237c478bd9Sstevel@tonic-gate 					/*
20247c478bd9Sstevel@tonic-gate 					 * Remember the 'who' for the previous
20257c478bd9Sstevel@tonic-gate 					 * transformation.
20267c478bd9Sstevel@tonic-gate 					 */
20277c478bd9Sstevel@tonic-gate 					pp->p_who = who;
20287c478bd9Sstevel@tonic-gate 					pp->p_special = 0;
20297c478bd9Sstevel@tonic-gate 
20307c478bd9Sstevel@tonic-gate 					op = pp->p_op;
20317c478bd9Sstevel@tonic-gate 
20327c478bd9Sstevel@tonic-gate 					/* Keep 'X' separate */
20337c478bd9Sstevel@tonic-gate 					++pp;
20347c478bd9Sstevel@tonic-gate 					pp->p_special = 'X';
20357c478bd9Sstevel@tonic-gate 					pp->p_op = op;
20367c478bd9Sstevel@tonic-gate 				}
20377c478bd9Sstevel@tonic-gate 			} else if (seen_X) {
20387c478bd9Sstevel@tonic-gate 				ushort_t op;
20397c478bd9Sstevel@tonic-gate 
20407c478bd9Sstevel@tonic-gate 				/* Remember the 'who' for the X */
20417c478bd9Sstevel@tonic-gate 				pp->p_who = who;
20427c478bd9Sstevel@tonic-gate 
20437c478bd9Sstevel@tonic-gate 				op = pp->p_op;
20447c478bd9Sstevel@tonic-gate 
20457c478bd9Sstevel@tonic-gate 				/* Keep 'X' separate */
20467c478bd9Sstevel@tonic-gate 				++pp;
20477c478bd9Sstevel@tonic-gate 				pp->p_perm = 0;
20487c478bd9Sstevel@tonic-gate 				pp->p_special = 0;
20497c478bd9Sstevel@tonic-gate 				pp->p_op = op;
20507c478bd9Sstevel@tonic-gate 			}
20517c478bd9Sstevel@tonic-gate 			++amode;
20527c478bd9Sstevel@tonic-gate 			pp->p_perm |= t;
20537c478bd9Sstevel@tonic-gate 		}
20547c478bd9Sstevel@tonic-gate 
20557c478bd9Sstevel@tonic-gate 		/*
20567c478bd9Sstevel@tonic-gate 		 * These returned 0, but were actually parsed, so
20577c478bd9Sstevel@tonic-gate 		 * don't look at them again.
20587c478bd9Sstevel@tonic-gate 		 */
20597c478bd9Sstevel@tonic-gate 		switch (pp->p_special) {
20607c478bd9Sstevel@tonic-gate 		case 'u':
20617c478bd9Sstevel@tonic-gate 		case 'g':
20627c478bd9Sstevel@tonic-gate 		case 'o':
20637c478bd9Sstevel@tonic-gate 			++amode;
20647c478bd9Sstevel@tonic-gate 			break;
20657c478bd9Sstevel@tonic-gate 		}
20667c478bd9Sstevel@tonic-gate 		pp->p_who = who;
20677c478bd9Sstevel@tonic-gate 		switch (*amode) {
20687c478bd9Sstevel@tonic-gate 		case '\0':
20697c478bd9Sstevel@tonic-gate 			break;
20707c478bd9Sstevel@tonic-gate 
20717c478bd9Sstevel@tonic-gate 		case ',':
20727c478bd9Sstevel@tonic-gate 			++amode;
20737c478bd9Sstevel@tonic-gate 			++pp;
20747c478bd9Sstevel@tonic-gate 			continue;
20757c478bd9Sstevel@tonic-gate 
20767c478bd9Sstevel@tonic-gate 		default:
20777c478bd9Sstevel@tonic-gate 			++pp;
20787c478bd9Sstevel@tonic-gate 			goto samewho;
20797c478bd9Sstevel@tonic-gate 		}
20807c478bd9Sstevel@tonic-gate 		break;
20817c478bd9Sstevel@tonic-gate 	}
20827c478bd9Sstevel@tonic-gate 	endp = pp;
208327d3a169SToomas Soome 	return (0);
20847c478bd9Sstevel@tonic-gate }
20857c478bd9Sstevel@tonic-gate 
20867c478bd9Sstevel@tonic-gate /*
20877c478bd9Sstevel@tonic-gate  * Given a character from the mode, return the associated
20887c478bd9Sstevel@tonic-gate  * value as who (user designation) mask or 0 if this isn't valid.
20897c478bd9Sstevel@tonic-gate  */
20907c478bd9Sstevel@tonic-gate static int
iswho(int c)209127d3a169SToomas Soome iswho(int c)
20927c478bd9Sstevel@tonic-gate {
20937c478bd9Sstevel@tonic-gate 	switch (c) {
20947c478bd9Sstevel@tonic-gate 	case 'a':
20957c478bd9Sstevel@tonic-gate 		return (P_A);
20967c478bd9Sstevel@tonic-gate 
20977c478bd9Sstevel@tonic-gate 	case 'u':
20987c478bd9Sstevel@tonic-gate 		return (P_U);
20997c478bd9Sstevel@tonic-gate 
21007c478bd9Sstevel@tonic-gate 	case 'g':
21017c478bd9Sstevel@tonic-gate 		return (P_G);
21027c478bd9Sstevel@tonic-gate 
21037c478bd9Sstevel@tonic-gate 	case 'o':
21047c478bd9Sstevel@tonic-gate 		return (P_O);
21057c478bd9Sstevel@tonic-gate 
21067c478bd9Sstevel@tonic-gate 	default:
21077c478bd9Sstevel@tonic-gate 		return (0);
21087c478bd9Sstevel@tonic-gate 	}
21097c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
21107c478bd9Sstevel@tonic-gate }
21117c478bd9Sstevel@tonic-gate 
21127c478bd9Sstevel@tonic-gate /*
21137c478bd9Sstevel@tonic-gate  * Return non-zero if this is a valid op code
21147c478bd9Sstevel@tonic-gate  * in a symbolic mode.
21157c478bd9Sstevel@tonic-gate  */
21167c478bd9Sstevel@tonic-gate static int
isop(int c)211727d3a169SToomas Soome isop(int c)
21187c478bd9Sstevel@tonic-gate {
21197c478bd9Sstevel@tonic-gate 	switch (c) {
21207c478bd9Sstevel@tonic-gate 	case '+':
21217c478bd9Sstevel@tonic-gate 	case '-':
21227c478bd9Sstevel@tonic-gate 	case '=':
21237c478bd9Sstevel@tonic-gate 		return (1);
21247c478bd9Sstevel@tonic-gate 
21257c478bd9Sstevel@tonic-gate 	default:
21267c478bd9Sstevel@tonic-gate 		return (0);
21277c478bd9Sstevel@tonic-gate 	}
21287c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
21297c478bd9Sstevel@tonic-gate }
21307c478bd9Sstevel@tonic-gate 
21317c478bd9Sstevel@tonic-gate /*
21327c478bd9Sstevel@tonic-gate  * Return the permission bits implied by this character or 0
21337c478bd9Sstevel@tonic-gate  * if it isn't valid.  Also returns 0 when the pseudo-permissions 'u', 'g', or
21347c478bd9Sstevel@tonic-gate  * 'o' are used, and sets pp->p_special to the one used.
21357c478bd9Sstevel@tonic-gate  */
21367c478bd9Sstevel@tonic-gate static int
isperm(PERMST * pp,int c)213727d3a169SToomas Soome isperm(PERMST *pp, int c)
21387c478bd9Sstevel@tonic-gate {
21397c478bd9Sstevel@tonic-gate 	switch (c) {
21407c478bd9Sstevel@tonic-gate 	case 'u':
21417c478bd9Sstevel@tonic-gate 	case 'g':
21427c478bd9Sstevel@tonic-gate 	case 'o':
21437c478bd9Sstevel@tonic-gate 		pp->p_special = c;
21447c478bd9Sstevel@tonic-gate 		return (0);
21457c478bd9Sstevel@tonic-gate 
21467c478bd9Sstevel@tonic-gate 	case 'r':
21477c478bd9Sstevel@tonic-gate 		return (S_IRUSR|S_IRGRP|S_IROTH);
21487c478bd9Sstevel@tonic-gate 
21497c478bd9Sstevel@tonic-gate 	case 'w':
21507c478bd9Sstevel@tonic-gate 		return (S_IWUSR|S_IWGRP|S_IWOTH);
21517c478bd9Sstevel@tonic-gate 
21527c478bd9Sstevel@tonic-gate 	case 'x':
21537c478bd9Sstevel@tonic-gate 		return (S_IXUSR|S_IXGRP|S_IXOTH);
21547c478bd9Sstevel@tonic-gate 
21557c478bd9Sstevel@tonic-gate #if S_ISVTX != 0
21567c478bd9Sstevel@tonic-gate 	case 't':
21577c478bd9Sstevel@tonic-gate 		return (S_ISVTX);
21587c478bd9Sstevel@tonic-gate #endif
21597c478bd9Sstevel@tonic-gate 
21607c478bd9Sstevel@tonic-gate 	case 'X':
21617c478bd9Sstevel@tonic-gate 		pp->p_special = 'X';
21627c478bd9Sstevel@tonic-gate 		return (S_IXUSR|S_IXGRP|S_IXOTH);
21637c478bd9Sstevel@tonic-gate 
21647c478bd9Sstevel@tonic-gate #if S_ISVTX != 0
21657c478bd9Sstevel@tonic-gate 	case 'a':
21667c478bd9Sstevel@tonic-gate 		return (S_ISVTX);
21677c478bd9Sstevel@tonic-gate #endif
21687c478bd9Sstevel@tonic-gate 
21697c478bd9Sstevel@tonic-gate 	case 'h':
21707c478bd9Sstevel@tonic-gate 		return (S_ISUID);
21717c478bd9Sstevel@tonic-gate 
21727c478bd9Sstevel@tonic-gate 	/*
21737c478bd9Sstevel@tonic-gate 	 * This change makes:
21747c478bd9Sstevel@tonic-gate 	 *	chmod +s file
21757c478bd9Sstevel@tonic-gate 	 * set the system bit on dos but means that
21767c478bd9Sstevel@tonic-gate 	 *	chmod u+s file
21777c478bd9Sstevel@tonic-gate 	 *	chmod g+s file
21787c478bd9Sstevel@tonic-gate 	 *	chmod a+s file
21797c478bd9Sstevel@tonic-gate 	 * are all like UNIX.
21807c478bd9Sstevel@tonic-gate 	 */
21817c478bd9Sstevel@tonic-gate 	case 's':
21827c478bd9Sstevel@tonic-gate 		return (nowho ? S_ISGID : S_ISGID|S_ISUID);
21837c478bd9Sstevel@tonic-gate 
21847c478bd9Sstevel@tonic-gate 	default:
21857c478bd9Sstevel@tonic-gate 		return (0);
21867c478bd9Sstevel@tonic-gate 	}
21877c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
21887c478bd9Sstevel@tonic-gate }
21897c478bd9Sstevel@tonic-gate 
21907c478bd9Sstevel@tonic-gate /*
21917c478bd9Sstevel@tonic-gate  * Execute the automaton that is created by readmode()
21927c478bd9Sstevel@tonic-gate  * to generate the final mode that will be used.  This
21937c478bd9Sstevel@tonic-gate  * code is passed a starting mode that is usually the original
21947c478bd9Sstevel@tonic-gate  * mode of the file being changed (or 0).  Note that this mode must contain
21957c478bd9Sstevel@tonic-gate  * the file-type bits as well, so that S_ISDIR will succeed on directories.
21967c478bd9Sstevel@tonic-gate  */
21977c478bd9Sstevel@tonic-gate static mode_t
getmode(mode_t startmode)21987c478bd9Sstevel@tonic-gate getmode(mode_t startmode)
21997c478bd9Sstevel@tonic-gate {
22007c478bd9Sstevel@tonic-gate 	PERMST *pp;
22017c478bd9Sstevel@tonic-gate 	mode_t temp;
22027c478bd9Sstevel@tonic-gate 	mode_t perm;
22037c478bd9Sstevel@tonic-gate 
22047c478bd9Sstevel@tonic-gate 	for (pp = &machine[0]; pp <= endp; ++pp) {
22057c478bd9Sstevel@tonic-gate 		perm = (mode_t)0;
22067c478bd9Sstevel@tonic-gate 		/*
22077c478bd9Sstevel@tonic-gate 		 * For the special modes 'u', 'g' and 'o', the named portion
22087c478bd9Sstevel@tonic-gate 		 * of the mode refers to after the previous clause has been
22097c478bd9Sstevel@tonic-gate 		 * processed, while the 'X' mode refers to the contents of the
22107c478bd9Sstevel@tonic-gate 		 * mode before any clauses have been processed.
22117c478bd9Sstevel@tonic-gate 		 *
22127c478bd9Sstevel@tonic-gate 		 * References: P1003.2/D11.2, Section 4.7.7,
22137c478bd9Sstevel@tonic-gate 		 *  lines 2568-2570, 2578-2583
22147c478bd9Sstevel@tonic-gate 		 */
22157c478bd9Sstevel@tonic-gate 		switch (pp->p_special) {
22167c478bd9Sstevel@tonic-gate 		case 'u':
22177c478bd9Sstevel@tonic-gate 			temp = startmode & S_IRWXU;
22187c478bd9Sstevel@tonic-gate 			if (temp & (S_IRUSR|S_IRGRP|S_IROTH))
22197c478bd9Sstevel@tonic-gate 				perm |= ((S_IRUSR|S_IRGRP|S_IROTH) &
22207c478bd9Sstevel@tonic-gate 				    pp->p_who);
22217c478bd9Sstevel@tonic-gate 			if (temp & (S_IWUSR|S_IWGRP|S_IWOTH))
22227c478bd9Sstevel@tonic-gate 				perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who);
22237c478bd9Sstevel@tonic-gate 			if (temp & (S_IXUSR|S_IXGRP|S_IXOTH))
22247c478bd9Sstevel@tonic-gate 				perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who);
22257c478bd9Sstevel@tonic-gate 			break;
22267c478bd9Sstevel@tonic-gate 
22277c478bd9Sstevel@tonic-gate 		case 'g':
22287c478bd9Sstevel@tonic-gate 			temp = startmode & S_IRWXG;
22297c478bd9Sstevel@tonic-gate 			if (temp & (S_IRUSR|S_IRGRP|S_IROTH))
22307c478bd9Sstevel@tonic-gate 				perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & pp->p_who);
22317c478bd9Sstevel@tonic-gate 			if (temp & (S_IWUSR|S_IWGRP|S_IWOTH))
22327c478bd9Sstevel@tonic-gate 				perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who);
22337c478bd9Sstevel@tonic-gate 			if (temp & (S_IXUSR|S_IXGRP|S_IXOTH))
22347c478bd9Sstevel@tonic-gate 				perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who);
22357c478bd9Sstevel@tonic-gate 			break;
22367c478bd9Sstevel@tonic-gate 
22377c478bd9Sstevel@tonic-gate 		case 'o':
22387c478bd9Sstevel@tonic-gate 			temp = startmode & S_IRWXO;
22397c478bd9Sstevel@tonic-gate 			if (temp & (S_IRUSR|S_IRGRP|S_IROTH))
22407c478bd9Sstevel@tonic-gate 				perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & pp->p_who);
22417c478bd9Sstevel@tonic-gate 			if (temp & (S_IWUSR|S_IWGRP|S_IWOTH))
22427c478bd9Sstevel@tonic-gate 				perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who);
22437c478bd9Sstevel@tonic-gate 			if (temp & (S_IXUSR|S_IXGRP|S_IXOTH))
22447c478bd9Sstevel@tonic-gate 				perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who);
22457c478bd9Sstevel@tonic-gate 			break;
22467c478bd9Sstevel@tonic-gate 
22477c478bd9Sstevel@tonic-gate 		case 'X':
22487c478bd9Sstevel@tonic-gate 			perm = pp->p_perm;
22497c478bd9Sstevel@tonic-gate 			break;
22507c478bd9Sstevel@tonic-gate 
22517c478bd9Sstevel@tonic-gate 		default:
22527c478bd9Sstevel@tonic-gate 			perm = pp->p_perm;
22537c478bd9Sstevel@tonic-gate 			break;
22547c478bd9Sstevel@tonic-gate 		}
22557c478bd9Sstevel@tonic-gate 		switch (pp->p_op) {
22567c478bd9Sstevel@tonic-gate 		case '-':
22577c478bd9Sstevel@tonic-gate 			startmode &= ~(perm & pp->p_who);
22587c478bd9Sstevel@tonic-gate 			break;
22597c478bd9Sstevel@tonic-gate 
22607c478bd9Sstevel@tonic-gate 		case '=':
22617c478bd9Sstevel@tonic-gate 			startmode &= ~pp->p_who;
22627c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
22637c478bd9Sstevel@tonic-gate 		case '+':
22647c478bd9Sstevel@tonic-gate 			startmode |= (perm & pp->p_who);
22657c478bd9Sstevel@tonic-gate 			break;
22667c478bd9Sstevel@tonic-gate 		}
22677c478bd9Sstevel@tonic-gate 	}
22687c478bd9Sstevel@tonic-gate 	return (startmode);
22697c478bd9Sstevel@tonic-gate }
22707c478bd9Sstevel@tonic-gate 
22717c478bd9Sstevel@tonic-gate /*
22727c478bd9Sstevel@tonic-gate  * Returns the last component of a path name, unless it is
22737c478bd9Sstevel@tonic-gate  * an absolute path, in which case it returns the whole path
22747c478bd9Sstevel@tonic-gate  */
227527d3a169SToomas Soome static const char *
gettail(const char * fname)227627d3a169SToomas Soome gettail(const char *fname)
22777c478bd9Sstevel@tonic-gate {
227827d3a169SToomas Soome 	const char *base = fname;
22797c478bd9Sstevel@tonic-gate 
22807c478bd9Sstevel@tonic-gate 	if (*fname != '/') {
22817c478bd9Sstevel@tonic-gate 		if ((base = strrchr(fname, '/')) != NULL)
22827c478bd9Sstevel@tonic-gate 			base++;
22837c478bd9Sstevel@tonic-gate 		else
22847c478bd9Sstevel@tonic-gate 			base = fname;
22857c478bd9Sstevel@tonic-gate 	}
22867c478bd9Sstevel@tonic-gate 	return (base);
22877c478bd9Sstevel@tonic-gate }
2288