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