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