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