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 2012 DEY Storage Systems, Inc.  All rights reserved.
  23  *
  24  * Portions of this file developed by DEY Storage Systems, Inc. are licensed
  25  * under the terms of the Common Development and Distribution License (CDDL)
  26  * version 1.0 only.  The use of subsequent versions of the License are
  27  * is specifically prohibited unless those terms are not in conflict with
  28  * version 1.0 of the License.  You can find this license on-line at
  29  * http://www.illumos.org/license/CDDL
  30  */
  31 /*
  32  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  33  * Use is subject to license terms.
  34  */
  35 
  36 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  37 /*        All Rights Reserved   */
  38 
  39 
  40 #include <stdio.h>
  41 #include <sys/types.h>
  42 #include <sys/wait.h>
  43 #include <unistd.h>
  44 #include <fcntl.h>
  45 #include <string.h>
  46 #include <stdarg.h>
  47 #include <stdlib.h>
  48 #include <limits.h>
  49 #include <wchar.h>
  50 #include <locale.h>
  51 #include <langinfo.h>
  52 #include <stropts.h>
  53 #include <poll.h>
  54 #include <errno.h>
  55 #include <stdarg.h>
  56 #include "getresponse.h"
  57 
  58 #define HEAD    0
  59 #define TAIL    1
  60 #define FALSE 0
  61 #define TRUE 1
  62 #define MAXSBUF 255
  63 #define MAXIBUF 512
  64 #define MAXINSERTS 5
  65 #define BUFSIZE LINE_MAX
  66 #define MAXARGS 255
  67 #define INSPAT_STR      "{}"    /* default replstr string for -[Ii]     */
  68 #define FORK_RETRY      5
  69 
  70 #define QBUF_STARTLEN 255  /* start size of growable string buffer */
  71 #define QBUF_INC 100       /* how much to grow a growable string by */
  72 
  73 /* We use these macros to help make formatting look "consistent" */
  74 #define EMSG(s)         ermsg(gettext(s "\n"))
  75 #define EMSG2(s, a)     ermsg(gettext(s "\n"), a)
  76 #define PERR(s)         perror(gettext("xargs: " s))
  77 
  78 /* Some common error messages */
  79 
  80 #define LIST2LONG       "Argument list too long"
  81 #define ARG2LONG        "A single argument was greater than %d bytes"
  82 #define MALLOCFAIL      "Memory allocation failure"
  83 #define CORRUPTFILE     "Corrupt input file"
  84 #define WAITFAIL        "Wait failure"
  85 #define CHILDSIG        "Child killed with signal %d"
  86 #define CHILDFAIL       "Command could not continue processing data"
  87 #define FORKFAIL        "Could not fork child"
  88 #define EXECFAIL        "Could not exec command"
  89 #define MISSQUOTE       "Missing quote"
  90 #define BADESCAPE       "Incomplete escape"
  91 #define IBUFOVERFLOW    "Insert buffer overflow"
  92 #define NOCHILDSLOT     "No free child slot available"
  93 
  94 #define _(x)    gettext(x)
  95 
  96 static wctype_t blank;
  97 static char     *arglist[MAXARGS+1];
  98 static char     argbuf[BUFSIZE * 2 + 1];
  99 static char     lastarg[BUFSIZE + 1];
 100 static char     **ARGV = arglist;
 101 static char     *LEOF = "_";
 102 static char     *INSPAT = INSPAT_STR;
 103 static char     ins_buf[MAXIBUF];
 104 static char     *p_ibuf;
 105 
 106 static struct inserts {
 107         char    **p_ARGV;       /* where to put newarg ptr in arg list */
 108         char    *p_skel;        /* ptr to arg template */
 109 } saveargv[MAXINSERTS];
 110 
 111 static int      PROMPT = -1;
 112 static int      BUFLIM = BUFSIZE;
 113 static int      MAXPROCS = 1;
 114 static int      N_ARGS = 0;
 115 static int      N_args = 0;
 116 static int      N_lines = 0;
 117 static int      DASHX = FALSE;
 118 static int      MORE = TRUE;
 119 static int      PER_LINE = FALSE;
 120 static int      ERR = FALSE;
 121 static int      OK = TRUE;
 122 static int      LEGAL = FALSE;
 123 static int      TRACE = FALSE;
 124 static int      INSERT = FALSE;
 125 static int      ZERO = FALSE;
 126 static int      linesize = 0;
 127 static int      ibufsize = 0;
 128 static int      exitstat = 0;   /* our exit status                      */
 129 static int      mac;            /* modified argc, after parsing         */
 130 static char     **mav;          /* modified argv, after parsing         */
 131 static int      n_inserts;      /* # of insertions.                     */
 132 static pid_t    *procs;         /* pids of children                     */
 133 static int      n_procs;        /* # of child processes.                */
 134 
 135 /* our usage message:                                                   */
 136 #define USAGEMSG "Usage: xargs: [-t] [-p] [-0] [-e[eofstr]] [-E eofstr] "\
 137         "[-I replstr] [-i[replstr]] [-L #] [-l[#]] [-n # [-x]] [-P maxprocs] [-s size] "\
 138         "[cmd [args ...]]\n"
 139 
 140 static int      echoargs();
 141 static wint_t   getwchr(char *, size_t *);
 142 static void     lcall(char *sub, char **subargs);
 143 static void     addibuf(struct inserts *p);
 144 static void     ermsg(char *messages, ...);
 145 static char     *addarg(char *arg);
 146 static void     store_str(char **, char *, size_t);
 147 static char     *getarg(char *);
 148 static char     *insert(char *pattern, char *subst);
 149 static void     usage();
 150 static void     parseargs();
 151 static void     procs_malloc(void);
 152 static int      procs_find(pid_t child);
 153 static void     procs_store(pid_t child);
 154 static int      procs_delete(pid_t child);
 155 static pid_t    procs_waitpid(int blocking, int *stat_loc);
 156 static void     procs_wait(int blocking);
 157 
 158 int
 159 main(int argc, char **argv)
 160 {
 161         int     j;
 162         struct inserts *psave;
 163         int c;
 164         int     initsize;
 165         char    *cmdname, **initlist;
 166         char    *arg;
 167         char    *next;
 168 
 169         /* initialization */
 170         blank = wctype("blank");
 171         n_inserts = 0;
 172         psave = saveargv;
 173         (void) setlocale(LC_ALL, "");
 174 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D           */
 175 #define TEXT_DOMAIN "SYS_TEST"  /* Use this only if it weren't          */
 176 #endif
 177         (void) textdomain(TEXT_DOMAIN);
 178         if (init_yes() < 0) {
 179                 ermsg(_(ERR_MSG_INIT_YES), strerror(errno));
 180                 exit(1);
 181         }
 182 
 183         parseargs(argc, argv);
 184 
 185         /* handling all of xargs arguments:                             */
 186         while ((c = getopt(mac, mav, "0tpe:E:I:i:L:l:n:P:s:x")) != EOF) {
 187                 switch (c) {
 188                 case '0':
 189                         ZERO = TRUE;
 190                         break;
 191 
 192                 case 't':       /* -t: turn trace mode on               */
 193                         TRACE = TRUE;
 194                         break;
 195 
 196                 case 'p':       /* -p: turn on prompt mode.             */
 197                         if ((PROMPT = open("/dev/tty", O_RDONLY)) == -1) {
 198                                 PERR("can't read from tty for -p");
 199                         } else {
 200                                 TRACE = TRUE;
 201                         }
 202                         break;
 203 
 204                 case 'e':
 205                         /*
 206                          * -e[eofstr]: set/disable end-of-file.
 207                          * N.B. that an argument *isn't* required here; but
 208                          * parseargs forced an argument if not was given.  The
 209                          * forced argument is the default...
 210                          */
 211                         LEOF = optarg; /* can be empty */
 212                         break;
 213 
 214                 case 'E':
 215                         /*
 216                          * -E eofstr: change end-of-file string.
 217                          * eofstr *is* required here, but can be empty:
 218                          */
 219                         LEOF = optarg;
 220                         break;
 221 
 222                 case 'I':
 223                         /* -I replstr: Insert mode. replstr *is* required. */
 224                         INSERT = PER_LINE = LEGAL = TRUE;
 225                         N_ARGS = 0;
 226                         INSPAT = optarg;
 227                         if (*optarg == '\0') {
 228                                 ermsg(_("Option requires an argument: -%c\n"),
 229                                     c);
 230                         }
 231                         break;
 232 
 233                 case 'i':
 234                         /*
 235                          * -i [replstr]: insert mode, with *optional* replstr.
 236                          * N.B. that an argument *isn't* required here; if
 237                          * it's not given, then the string INSPAT_STR will
 238                          * be assumed.
 239                          *
 240                          * Since getopts(3C) doesn't handle the case of an
 241                          * optional variable argument at all, we have to
 242                          * parse this by hand:
 243                          */
 244 
 245                         INSERT = PER_LINE = LEGAL = TRUE;
 246                         N_ARGS = 0;
 247                         if ((optarg != NULL) && (*optarg != '\0')) {
 248                                 INSPAT = optarg;
 249                         } else {
 250                                 /*
 251                                  * here, there is no next argument. so
 252                                  * we reset INSPAT to the INSPAT_STR.
 253                                  * we *have* to do this, as -i/I may have
 254                                  * been given previously, and XCU4 requires
 255                                  * that only "the last one specified takes
 256                                  * effect".
 257                                  */
 258                                 INSPAT = INSPAT_STR;
 259                         }
 260                         break;
 261 
 262                 case 'L':
 263                         /*
 264                          * -L number: # of times cmd is executed
 265                          * number *is* required here:
 266                          */
 267                         PER_LINE = TRUE;
 268                         N_ARGS = 0;
 269                         INSERT = FALSE;
 270                         if ((PER_LINE = atoi(optarg)) <= 0) {
 271                                 ermsg(_("#lines must be positive int: %s\n"),
 272                                     optarg);
 273                         }
 274                         break;
 275 
 276                 case 'l':
 277                         /*
 278                          * -l [number]: # of times cmd is executed
 279                          * N.B. that an argument *isn't* required here; if
 280                          * it's not given, then 1 is assumed.
 281                          *
 282                          * parseargs handles the optional arg processing.
 283                          */
 284 
 285                         PER_LINE = LEGAL = TRUE;  /* initialization     */
 286                         N_ARGS = 0;
 287                         INSERT = FALSE;
 288 
 289                         if ((optarg != NULL) && (*optarg != '\0')) {
 290                                 if ((PER_LINE = atoi(optarg)) <= 0)
 291                                         PER_LINE = 1;
 292                         }
 293                         break;
 294 
 295                 case 'n':       /* -n number: # stdin args              */
 296                         /*
 297                          * -n number: # stdin args.
 298                          * number *is* required here:
 299                          */
 300                         if ((N_ARGS = atoi(optarg)) <= 0) {
 301                                 ermsg(_("#args must be positive int: %s\n"),
 302                                     optarg);
 303                         } else {
 304                                 LEGAL = DASHX || N_ARGS == 1;
 305                                 INSERT = PER_LINE = FALSE;
 306                         }
 307                         break;
 308 
 309                 case 'P':       /* -P maxprocs: # of child processses   */
 310                         MAXPROCS = atoi(optarg);
 311                         if (MAXPROCS <= 0) {
 312                                 ermsg(_("#maxprocs must be positive int: %s\n"),
 313                                     optarg);
 314                         }
 315                         break;
 316 
 317                 case 's':       /* -s size: set max size of each arg list */
 318                         BUFLIM = atoi(optarg);
 319                         if (BUFLIM > BUFSIZE || BUFLIM <= 0) {
 320                                 ermsg(_("0 < max-cmd-line-size <= %d: %s\n"),
 321                                     BUFSIZE, optarg);
 322                         }
 323                         break;
 324 
 325                 case 'x':       /* -x: terminate if args > size limit        */
 326                         DASHX = LEGAL = TRUE;
 327                         break;
 328 
 329                 default:
 330                         /*
 331                          * bad argument. complain and get ready to die.
 332                          */
 333                         usage();
 334                         exit(2);
 335                         break;
 336                 }
 337         }
 338 
 339         /*
 340          * if anything called ermsg(), something screwed up, so
 341          * we exit early.
 342          */
 343         if (OK == FALSE) {
 344                 usage();
 345                 exit(2);
 346         }
 347 
 348         /*
 349          * we're finished handling xargs's options, so now pick up
 350          * the command name (if any), and it's options.
 351          */
 352 
 353 
 354         mac -= optind;  /* dec arg count by what we've processed        */
 355         mav += optind;  /* inc to current mav                           */
 356 
 357         (void) procs_malloc();
 358 
 359         if (mac <= 0) {      /* if there're no more args to process, */
 360                 cmdname = "/usr/bin/echo";      /* our default command  */
 361                 *ARGV++ = addarg(cmdname);      /* use the default cmd. */
 362         } else {        /* otherwise keep parsing rest of the string.   */
 363                 /*
 364                  * note that we can't use getopts(3C), and *must* parse
 365                  * this by hand, as we don't know apriori what options the
 366                  * command will take.
 367                  */
 368                 cmdname = *mav; /* get the command name */
 369 
 370 
 371                 /* pick up the remaining args from the command line:    */
 372                 while ((OK == TRUE) && (mac-- > 0)) {
 373                         /*
 374                          * while we haven't crapped out, and there's
 375                          * work to do:
 376                          */
 377                         if (INSERT && ! ERR) {
 378                                 if (strstr(*mav, INSPAT) != NULL) {
 379                                         if (++n_inserts > MAXINSERTS) {
 380                                                 ermsg(_("too many args "
 381                                                     "with %s\n"), INSPAT);
 382                                                 ERR = TRUE;
 383                                         }
 384                                         psave->p_ARGV = ARGV;
 385                                         (psave++)->p_skel = *mav;
 386                                 }
 387                         }
 388                         *ARGV++ = addarg(*mav++);
 389                 }
 390         }
 391 
 392         /* pick up args from standard input */
 393 
 394         initlist = ARGV;
 395         initsize = linesize;
 396         lastarg[0] = '\0';
 397 
 398         while (OK) {
 399                 N_args = 0;
 400                 N_lines = 0;
 401                 ARGV = initlist;
 402                 linesize = initsize;
 403                 next = argbuf;
 404 
 405                 while (MORE || (lastarg[0] != '\0')) {
 406                         int l;
 407 
 408                         if (*lastarg != '\0') {
 409                                 arg = strcpy(next, lastarg);
 410                                 *lastarg = '\0';
 411                         } else if ((arg = getarg(next)) == NULL) {
 412                                 break;
 413                         }
 414 
 415                         l = strlen(arg) + 1;
 416                         linesize += l;
 417                         next += l;
 418 
 419                         /* Inserts are handled specially later. */
 420                         if ((n_inserts == 0) && (linesize >= BUFLIM)) {
 421                                 /*
 422                                  * Legal indicates hard fail if the list is
 423                                  * truncated due to size.  So fail, or if we
 424                                  * cannot create any list because it would be
 425                                  * too big.
 426                                  */
 427                                 if (LEGAL || N_args == 0) {
 428                                         EMSG(LIST2LONG);
 429                                         (void) procs_wait(1);
 430                                         exit(2);
 431                                         /* NOTREACHED */
 432                                 }
 433 
 434                                 /*
 435                                  * Otherwise just save argument for later.
 436                                  */
 437                                 (void) strcpy(lastarg, arg);
 438                                 break;
 439                         }
 440 
 441                         *ARGV++ = arg;
 442 
 443                         N_args++;
 444 
 445                         if ((PER_LINE && N_lines >= PER_LINE) ||
 446                             (N_ARGS && (N_args) >= N_ARGS)) {
 447                                 break;
 448                         }
 449 
 450 
 451                         if ((ARGV - arglist) == MAXARGS) {
 452                                 break;
 453                         }
 454                 }
 455 
 456                 *ARGV = NULL;
 457                 if (N_args == 0) {
 458                         /* Reached the end with no more work. */
 459                         break;
 460                 }
 461 
 462                 /* insert arg if requested */
 463 
 464                 if (!ERR && INSERT) {
 465 
 466                         p_ibuf = ins_buf;
 467                         ARGV--;
 468                         j = ibufsize = 0;
 469                         for (psave = saveargv; ++j <= n_inserts; ++psave) {
 470                                 addibuf(psave);
 471                                 if (ERR)
 472                                         break;
 473                         }
 474                 }
 475                 *ARGV = NULL;
 476 
 477                 if (n_inserts > 0) {
 478                         /*
 479                          * if we've done any insertions, re-calculate the
 480                          * linesize. bomb out if we've exceeded our length.
 481                          */
 482                         linesize = 0;
 483                         for (ARGV = arglist; *ARGV != NULL; ARGV++) {
 484                                 linesize += strlen(*ARGV) + 1;
 485                         }
 486                         if (linesize >= BUFLIM) {
 487                                 EMSG(LIST2LONG);
 488                                 (void) procs_wait(1);
 489                                 exit(2);
 490                                 /* NOTREACHED */
 491                         }
 492                 }
 493 
 494                 /* exec command */
 495 
 496                 if (!ERR) {
 497                         if (!MORE &&
 498                             (PER_LINE && N_lines == 0 || N_ARGS && N_args == 0))
 499                                 exit(exitstat);
 500                         OK = TRUE;
 501                         j = TRACE ? echoargs() : TRUE;
 502                         if (j) {
 503                                 /*
 504                                  * for xcu4, all invocations of cmdname must
 505                                  * return 0, in order for us to return 0.
 506                                  * so if we have a non-zero status here,
 507                                  * quit immediately.
 508                                  */
 509                                 (void) lcall(cmdname, arglist);
 510                         }
 511                 }
 512         }
 513 
 514         (void) procs_wait(1);
 515 
 516         if (OK)
 517                 return (exitstat);
 518 
 519         /*
 520          * if exitstat was set, to match XCU4 complience,
 521          * return that value, otherwise, return 1.
 522          */
 523         return (exitstat ? exitstat : 1);
 524 }
 525 
 526 static char *
 527 addarg(char *arg)
 528 {
 529         linesize += (strlen(arg) + 1);
 530         return (arg);
 531 }
 532 
 533 
 534 static void
 535 store_str(char **buffer, char *str, size_t len)
 536 {
 537         (void) memcpy(*buffer, str, len);
 538         (*buffer)[len] = '\0';
 539         *buffer += len;
 540 }
 541 
 542 
 543 static char *
 544 getarg(char *arg)
 545 {
 546         char    *xarg = arg;
 547         wchar_t c;
 548         char    mbc[MB_LEN_MAX];
 549         size_t  len;
 550         int     escape = 0;
 551         int     inquote = 0;
 552 
 553         arg[0] = '\0';
 554 
 555         while (MORE) {
 556 
 557                 len = 0;
 558                 c = getwchr(mbc, &len);
 559 
 560                 if (((arg - xarg) + len) > BUFLIM) {
 561                         EMSG2(ARG2LONG, BUFLIM);
 562                         exit(2);
 563                         ERR = TRUE;
 564                         return (NULL);
 565                 }
 566 
 567                 switch (c) {
 568                 case '\n':
 569                         if (ZERO) {
 570                                 store_str(&arg, mbc, len);
 571                                 continue;
 572                         }
 573                         /* FALLTHRU */
 574 
 575                 case '\0':
 576                 case WEOF:      /* Note WEOF == EOF */
 577 
 578                         if (escape) {
 579                                 EMSG(BADESCAPE);
 580                                 ERR = TRUE;
 581                                 return (NULL);
 582                         }
 583                         if (inquote) {
 584                                 EMSG(MISSQUOTE);
 585                                 ERR = TRUE;
 586                                 return (NULL);
 587                         }
 588 
 589                         N_lines++;
 590                         break;
 591 
 592                 case '"':
 593                         if (ZERO || escape || (inquote == 1)) {
 594                                 /* treat it literally */
 595                                 escape = 0;
 596                                 store_str(&arg, mbc, len);
 597 
 598                         } else if (inquote == 2) {
 599                                 /* terminating double quote */
 600                                 inquote = 0;
 601 
 602                         } else {
 603                                 /* starting quoted string */
 604                                 inquote = 2;
 605                         }
 606                         continue;
 607 
 608                 case '\'':
 609                         if (ZERO || escape || (inquote == 2)) {
 610                                 /* treat it literally */
 611                                 escape = 0;
 612                                 store_str(&arg, mbc, len);
 613 
 614                         } else if (inquote == 1) {
 615                                 /* terminating single quote */
 616                                 inquote = 0;
 617 
 618                         } else {
 619                                 /* starting quoted string */
 620                                 inquote = 1;
 621                         }
 622                         continue;
 623 
 624                 case '\\':
 625                         /*
 626                          * Any unquoted character can be escaped by
 627                          * preceding it with a backslash.
 628                          */
 629                         if (ZERO || inquote || escape) {
 630                                 escape = 0;
 631                                 store_str(&arg, mbc, len);
 632                         } else {
 633                                 escape = 1;
 634                         }
 635                         continue;
 636 
 637                 default:
 638                         /* most times we will just want to store it */
 639                         if (inquote || escape || ZERO || !iswctype(c, blank)) {
 640                                 escape = 0;
 641                                 store_str(&arg, mbc, len);
 642                                 continue;
 643                         }
 644                         /* unquoted blank */
 645                         break;
 646                 }
 647 
 648                 /*
 649                  * At this point we are processing a complete argument.
 650                  */
 651                 if (strcmp(xarg, LEOF) == 0 && *LEOF != '\0') {
 652                         MORE = FALSE;
 653                         return (NULL);
 654                 }
 655                 if (c == WEOF) {
 656                         MORE = FALSE;
 657                 }
 658                 if (xarg[0] == '\0')
 659                         continue;
 660                 break;
 661         }
 662 
 663         return (xarg[0] == '\0' ? NULL : xarg);
 664 }
 665 
 666 /*
 667  * ermsg():     print out an error message, and indicate failure globally.
 668  *
 669  *      Assumes that message has already been gettext()'d. It would be
 670  *      nice if we could just do the gettext() here, but we can't, since
 671  *      since xgettext(1M) wouldn't be able to pick up our error message.
 672  */
 673 /* PRINTFLIKE1 */
 674 static void
 675 ermsg(char *messages, ...)
 676 {
 677         va_list ap;
 678 
 679         va_start(ap, messages);
 680 
 681         (void) fprintf(stderr, "xargs: ");
 682         (void) vfprintf(stderr, messages, ap);
 683 
 684         va_end(ap);
 685         OK = FALSE;
 686 }
 687 
 688 static int
 689 echoargs()
 690 {
 691         char    **anarg;
 692         char    **tanarg;       /* tmp ptr                      */
 693         int             i;
 694         char            reply[LINE_MAX];
 695 
 696         tanarg = anarg = arglist-1;
 697 
 698         /*
 699          * write out each argument, separated by a space. the tanarg
 700          * nonsense is for xcu4 testsuite compliance - so that an
 701          * extra space isn't echoed after the last argument.
 702          */
 703         while (*++anarg) {              /* while there's an argument    */
 704                 ++tanarg;               /* follow anarg                 */
 705                 (void) write(2, *anarg, strlen(*anarg));
 706 
 707                 if (*++tanarg) {        /* if there's another argument: */
 708                         (void) write(2, " ", 1); /* add a space         */
 709                         --tanarg;       /* reset back to anarg          */
 710                 }
 711         }
 712         if (PROMPT == -1) {
 713                 (void) write(2, "\n", 1);
 714                 return (TRUE);
 715         }
 716 
 717         (void) write(2, "?...", 4);     /* ask the user for input       */
 718 
 719         for (i = 0; i < LINE_MAX && read(PROMPT, &reply[i], 1) > 0; i++) {
 720                 if (reply[i] == '\n') {
 721                         if (i == 0)
 722                                 return (FALSE);
 723                         break;
 724                 }
 725         }
 726         reply[i] = 0;
 727 
 728         /* flush remainder of line if necessary */
 729         if (i == LINE_MAX) {
 730                 char    bitbucket;
 731 
 732                 while ((read(PROMPT, &bitbucket, 1) > 0) && (bitbucket != '\n'))
 733                         ;
 734         }
 735 
 736         return (yes_check(reply));
 737 }
 738 
 739 
 740 static char *
 741 insert(char *pattern, char *subst)
 742 {
 743         static char     buffer[MAXSBUF+1];
 744         int             len, ipatlen;
 745         char    *pat;
 746         char    *bufend;
 747         char    *pbuf;
 748 
 749         len = strlen(subst);
 750         ipatlen = strlen(INSPAT) - 1;
 751         pat = pattern - 1;
 752         pbuf = buffer;
 753         bufend = &buffer[MAXSBUF];
 754 
 755         while (*++pat) {
 756                 if (strncmp(pat, INSPAT, ipatlen) == 0) {
 757                         if (pbuf + len >= bufend) {
 758                                 break;
 759                         } else {
 760                                 (void) strcpy(pbuf, subst);
 761                                 pat += ipatlen;
 762                                 pbuf += len;
 763                         }
 764                 } else {
 765                         *pbuf++ = *pat;
 766                         if (pbuf >= bufend)
 767                                 break;
 768                 }
 769         }
 770 
 771         if (!*pat) {
 772                 *pbuf = '\0';
 773                 return (buffer);
 774         } else {
 775                 ermsg(gettext("Maximum argument size with insertion via %s's "
 776                     "exceeded\n"), INSPAT);
 777                 ERR = TRUE;
 778                 return (NULL);
 779         }
 780 }
 781 
 782 
 783 static void
 784 addibuf(struct inserts  *p)
 785 {
 786         char    *newarg, *skel, *sub;
 787         int             l;
 788 
 789         skel = p->p_skel;
 790         sub = *ARGV;
 791         newarg = insert(skel, sub);
 792         if (ERR)
 793                 return;
 794 
 795         l = strlen(newarg) + 1;
 796         if ((ibufsize += l) > MAXIBUF) {
 797                 EMSG(IBUFOVERFLOW);
 798                 ERR = TRUE;
 799         }
 800         (void) strcpy(p_ibuf, newarg);
 801         *(p->p_ARGV) = p_ibuf;
 802         p_ibuf += l;
 803 }
 804 
 805 
 806 /*
 807  * getwchr():   get the next wide character.
 808  * description:
 809  *      we get the next character from stdin.  This returns WEOF if no
 810  *      character is present.  If ZERO is set, it gets a single byte instead
 811  *      a wide character.
 812  */
 813 static wint_t
 814 getwchr(char *mbc, size_t *sz)
 815 {
 816         size_t          i;
 817         int             c;
 818         wchar_t         wch;
 819 
 820         i = 0;
 821         while (i < MB_CUR_MAX) {
 822 
 823                 if ((c = fgetc(stdin)) == EOF) {
 824 
 825                         if (i == 0) {
 826                                 /* TRUE EOF has been reached */
 827                                 return (WEOF);
 828                         }
 829 
 830                         /*
 831                          * We have some characters in our buffer still so it
 832                          * must be an invalid character right before EOF.
 833                          */
 834                         break;
 835                 }
 836                 mbc[i++] = (char)c;
 837 
 838                 /* If this succeeds then we are done */
 839                 if (ZERO) {
 840                         *sz = i;
 841                         return ((char)c);
 842                 }
 843                 if (mbtowc(&wch, mbc, i) != -1) {
 844                         *sz = i;
 845                         return ((wint_t)wch);
 846                 }
 847         }
 848 
 849         /*
 850          * We have now encountered an illegal character sequence.
 851          * There is nothing much we can do at this point but
 852          * return an error.  If we attempt to recover we may in fact
 853          * return garbage as arguments, from the customer's point
 854          * of view.  After all what if they are feeding us a file
 855          * generated in another locale?
 856          */
 857         errno = EILSEQ;
 858         PERR(CORRUPTFILE);
 859         exit(1);
 860         /* NOTREACHED */
 861 }
 862 
 863 
 864 static void
 865 lcall(char *sub, char **subargs)
 866 {
 867         int     retry = 0;
 868         pid_t   child;
 869 
 870         for (;;) {
 871                 switch (child = fork()) {
 872                 default:
 873                         (void) procs_store(child);
 874                         (void) procs_wait(0);
 875                         return;
 876                 case 0:
 877                         (void) execvp(sub, subargs);
 878                         PERR(EXECFAIL);
 879                         if (errno == EACCES)
 880                                 exit(126);
 881                         exit(127);
 882                         /* NOTREACHED */
 883                 case -1:
 884                         if (errno != EAGAIN && retry++ < FORK_RETRY) {
 885                                 PERR(FORKFAIL);
 886                                 exit(123);
 887                         }
 888                         (void) sleep(1);
 889                 }
 890         }
 891 }
 892 
 893 static void
 894 procs_malloc(void)
 895 {
 896         int     i;
 897 
 898         procs = (pid_t *)(malloc(MAXPROCS * sizeof(pid_t)));
 899         if (procs == NULL) {
 900                 PERR(MALLOCFAIL);
 901                 exit(1);
 902         }
 903 
 904         for (i = 0; i < MAXPROCS; i++) {
 905                 procs[i] = (pid_t)(0);
 906         }
 907 }
 908 
 909 static int
 910 procs_find(pid_t child)
 911 {
 912         int     i;
 913 
 914         for (i = 0; i < MAXPROCS; i++) {
 915                 if (procs[i] == child) {
 916                         return (i);
 917                 }
 918         }
 919 
 920         return (-1);
 921 }
 922 
 923 static void
 924 procs_store(pid_t child)
 925 {
 926         int     i;
 927 
 928         i = procs_find((pid_t)(0));
 929         if (i < 0) {
 930                 PERR(NOCHILDSLOT);
 931                 exit(1);
 932         }
 933         procs[i] = child;
 934         n_procs++;
 935 }
 936 
 937 static int
 938 procs_delete(pid_t child)
 939 {
 940         int     i;
 941 
 942         i = procs_find(child);
 943         if (i < 0) {
 944                 return (0);
 945         }
 946         procs[i] = (pid_t)(0);
 947         n_procs--;
 948         return (1);
 949 }
 950 
 951 static pid_t
 952 procs_waitpid(int blocking, int *stat_loc)
 953 {
 954         pid_t   child;
 955         int     options;
 956 
 957         if (n_procs == 0) {
 958                 errno = ECHILD;
 959                 return (-1);
 960         }
 961 
 962         options = (blocking) ? 0 : WNOHANG;
 963 
 964         while ((child = waitpid(-1, stat_loc, options)) > 0) {
 965                 if (procs_delete(child)) {
 966                         break;
 967                 }
 968         }
 969 
 970         return (child);
 971 }
 972 
 973 static void
 974 procs_wait(int blocking)
 975 {
 976         pid_t   child;
 977         int     stat_loc;
 978 
 979         while ((child = procs_waitpid(blocking || (n_procs >= MAXPROCS) ? 1 : 0, &stat_loc)) > 0) {
 980                 if (WIFSIGNALED(stat_loc)) {
 981                         EMSG2(CHILDSIG, WTERMSIG(stat_loc));
 982                         exit(125);
 983                         /* NOTREACHED */
 984                 } else if ((WEXITSTATUS(stat_loc) & 0377) == 0377) {
 985                         EMSG(CHILDFAIL);
 986                         exit(124);
 987                         /* NOTREACHED */
 988                 } else {
 989                         exitstat |= WEXITSTATUS(stat_loc);
 990                 }
 991         }
 992 
 993         if (child == (pid_t)(-1) && errno != ECHILD) {
 994                 EMSG(WAITFAIL);
 995                 exit(122);
 996                 /* NOTREACHED */
 997         }
 998 }
 999 
1000 static void
1001 usage()
1002 {
1003         ermsg(_(USAGEMSG));
1004         OK = FALSE;
1005 }
1006 
1007 
1008 
1009 /*
1010  * parseargs():         modify the args
1011  *      since the -e, -i and -l flags all take optional subarguments,
1012  *      and getopts(3C) is clueless about this nonsense, we change the
1013  *      our local argument count and strings to separate this out,
1014  *      and make it easier to handle via getopts(3c).
1015  *
1016  *      -e      -> "-e ""
1017  *      -e3     -> "-e "3"
1018  *      -Estr   -> "-E "str"
1019  *      -i      -> "-i "{}"
1020  *      -irep   -> "-i "rep"
1021  *      -l      -> "-i "1"
1022  *      -l10    -> "-i "10"
1023  *
1024  *      since the -e, -i and -l flags all take optional subarguments,
1025  */
1026 static void
1027 parseargs(int ac, char **av)
1028 {
1029         int i;                  /* current argument                     */
1030         int cflag;              /* 0 = not processing cmd arg           */
1031 
1032         if ((mav = malloc((ac * 2 + 1) * sizeof (char *))) == NULL) {
1033                 PERR(MALLOCFAIL);
1034                 exit(1);
1035         }
1036 
1037         /* for each argument, see if we need to change things:          */
1038         for (i = mac = cflag = 0; (av[i] != NULL) && i < ac; i++, mac++) {
1039                 if ((mav[mac] = strdup(av[i])) == NULL) {
1040                         PERR(MALLOCFAIL);
1041                         exit(1);
1042                 }
1043 
1044                 /* -- has been found or argument list is fully processes */
1045                 if (cflag)
1046                         continue;
1047 
1048                 /*
1049                  * if we're doing special processing, and we've got a flag
1050                  */
1051                 else if ((av[i][0] == '-') && (av[i][1] != NULL)) {
1052                         char    *def;
1053 
1054                         switch (av[i][1]) {
1055                         case    'e':
1056                                 def = ""; /* -e with no arg turns off eof */
1057                                 goto process_special;
1058                         case    'i':
1059                                 def = INSPAT_STR;
1060                                 goto process_special;
1061                         case    'l':
1062                                 def = "1";
1063 process_special:
1064                                 /*
1065                                  * if there's no sub-option, we *must* add
1066                                  * a default one. this is because xargs must
1067                                  * be able to distinguish between a valid
1068                                  * suboption, and a command name.
1069                                  */
1070                                 if (av[i][2] == NULL) {
1071                                         mav[++mac] = strdup(def);
1072                                 } else {
1073                                         /* clear out our version: */
1074                                         mav[mac][2] = NULL;
1075                                         mav[++mac] = strdup(&av[i][2]);
1076                                 }
1077                                 if (mav[mac] == NULL) {
1078                                         PERR(MALLOCFAIL);
1079                                         exit(1);
1080                                 }
1081                                 break;
1082 
1083                         /* flags with required subarguments:            */
1084 
1085                         /*
1086                          * there are two separate cases here. either the
1087                          * flag can have the normal XCU4 handling
1088                          * (of the form: -X subargument); or it can have
1089                          * the old solaris 2.[0-4] handling (of the
1090                          * form: -Xsubargument). in order to maintain
1091                          * backwards compatibility, we must support the
1092                          * latter case. we handle the latter possibility
1093                          * first so both the old solaris way of handling
1094                          * and the new XCU4 way of handling things are allowed.
1095                          */
1096                         case    'n':    /* FALLTHROUGH                  */
1097                         case    'P':    /* FALLTHROUGH                  */
1098                         case    's':    /* FALLTHROUGH                  */
1099                         case    'E':    /* FALLTHROUGH                  */
1100                         case    'I':    /* FALLTHROUGH                  */
1101                         case    'L':
1102                                 /*
1103                                  * if the second character isn't null, then
1104                                  * the user has specified the old syntax.
1105                                  * we move the subargument into our
1106                                  * mod'd argument list.
1107                                  */
1108                                 if (av[i][2] != NULL) {
1109                                         /* first clean things up:       */
1110                                         mav[mac][2] = NULL;
1111 
1112                                         /* now add the separation:      */
1113                                         ++mac;  /* inc to next mod'd arg */
1114                                         if ((mav[mac] = strdup(&av[i][2])) ==
1115                                             NULL) {
1116                                                 PERR(MALLOCFAIL);
1117                                                 exit(1);
1118                                         }
1119                                         break;
1120                                 }
1121                                 i++;
1122                                 mac++;
1123 
1124                                 if (av[i] == NULL) {
1125                                         mav[mac] = NULL;
1126                                         return;
1127                                 }
1128                                 if ((mav[mac] = strdup(av[i])) == NULL) {
1129                                         PERR(MALLOCFAIL);
1130                                         exit(1);
1131                                 }
1132                                 break;
1133 
1134                         /* flags */
1135                         case 'p' :
1136                         case 't' :
1137                         case 'x' :
1138                         case '0' :
1139                                 break;
1140 
1141                         case '-' :
1142                         default:
1143                                 /*
1144                                  * here we've hit the cmd argument. so
1145                                  * we'll stop special processing, as the
1146                                  * cmd may have a "-i" etc., argument,
1147                                  * and we don't want to add a "" to it.
1148                                  */
1149                                 cflag = 1;
1150                                 break;
1151                         }
1152                 } else if (i > 0) {  /* if we're not the 1st arg     */
1153                         /*
1154                          * if it's not a flag, then it *must* be the cmd.
1155                          * set cflag, so we don't mishandle the -[eil] flags.
1156                          */
1157                         cflag = 1;
1158                 }
1159         }
1160 
1161         mav[mac] = NULL;
1162 }