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