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