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