Print this page
4703 would like xargs support for -P


  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.


 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) {


 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)


 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"


 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);




  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.


 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) {


 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)


 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"


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);