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