xargs.c - randomcrap - random crap programs of varying quality
 (HTM) git clone git://git.codemadness.org/randomcrap
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       xargs.c (18899B)
       ---
            1 /*        $OpenBSD: xargs.c,v 1.38 2023/12/23 15:58:58 millert Exp $        */
            2 /*        $FreeBSD: xargs.c,v 1.51 2003/05/03 19:09:11 obrien Exp $        */
            3 
            4 /*-
            5  * Copyright (c) 1990, 1993
            6  *        The Regents of the University of California.  All rights reserved.
            7  *
            8  * This code is derived from software contributed to Berkeley by
            9  * John B. Roll Jr.
           10  *
           11  * Redistribution and use in source and binary forms, with or without
           12  * modification, are permitted provided that the following conditions
           13  * are met:
           14  * 1. Redistributions of source code must retain the above copyright
           15  *    notice, this list of conditions and the following disclaimer.
           16  * 2. Redistributions in binary form must reproduce the above copyright
           17  *    notice, this list of conditions and the following disclaimer in the
           18  *    documentation and/or other materials provided with the distribution.
           19  * 3. Neither the name of the University nor the names of its contributors
           20  *    may be used to endorse or promote products derived from this software
           21  *    without specific prior written permission.
           22  *
           23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
           24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
           25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
           26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
           27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
           28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
           29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
           30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
           31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
           32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
           33  * SUCH DAMAGE.
           34  *
           35  * $xMach: xargs.c,v 1.6 2002/02/23 05:27:47 tim Exp $
           36  */
           37 
           38 #include <sys/wait.h>
           39 
           40 #include <ctype.h>
           41 #include <err.h>
           42 #include <errno.h>
           43 #include <fcntl.h>
           44 #include <limits.h>
           45 #include <paths.h>
           46 #include <signal.h>
           47 #include <stdio.h>
           48 #include <stdlib.h>
           49 #include <string.h>
           50 #include <unistd.h>
           51 #include <limits.h>
           52 
           53 #define        _PATH_ECHO        "/bin/echo"
           54 
           55 #ifndef __OpenBSD__
           56 #define pledge(a,b) 0
           57 #endif
           58 
           59 static void        parse_input(int, char *[]);
           60 static void        prerun(int, char *[]);
           61 static int        prompt(void);
           62 static void        run(char **);
           63 static void        usage(void);
           64 static void        waitchildren(const char *, int);
           65 
           66 static char **av, **bxp, **ep, **endxp, **xp;
           67 static char *argp, *bbp, *ebp, *inpline, *p, *replstr;
           68 static const char *eofstr;
           69 static int count, insingle, indouble, oflag, pflag, tflag, Rflag, rval, zflag;
           70 static int cnt, Iflag, jfound, Lflag, wasquoted, xflag, runeof = 1;
           71 static int curprocs, maxprocs;
           72 static size_t inpsize;
           73 
           74 extern char **environ;
           75 
           76 #undef strlcpy
           77 #undef strlcat
           78 
           79 /*
           80  * Appends src to string dst of size dsize (unlike strncat, dsize is the
           81  * full size of dst, not space left).  At most dsize-1 characters
           82  * will be copied.  Always NUL terminates (unless dsize <= strlen(dst)).
           83  * Returns strlen(src) + MIN(dsize, strlen(initial dst)).
           84  * If retval >= dsize, truncation occurred.
           85  */
           86 size_t
           87 strlcat(char *dst, const char *src, size_t dsize)
           88 {
           89         const char *odst = dst;
           90         const char *osrc = src;
           91         size_t n = dsize;
           92         size_t dlen;
           93 
           94         /* Find the end of dst and adjust bytes left but don't go past end. */
           95         while (n-- != 0 && *dst != '\0')
           96                 dst++;
           97         dlen = dst - odst;
           98         n = dsize - dlen;
           99 
          100         if (n-- == 0)
          101                 return(dlen + strlen(src));
          102         while (*src != '\0') {
          103                 if (n != 0) {
          104                         *dst++ = *src;
          105                         n--;
          106                 }
          107                 src++;
          108         }
          109         *dst = '\0';
          110 
          111         return(dlen + (src - osrc));        /* count does not include NUL */
          112 }
          113 
          114 /*
          115  * Copy string src to buffer dst of size dsize.  At most dsize-1
          116  * chars will be copied.  Always NUL terminates (unless dsize == 0).
          117  * Returns strlen(src); if retval >= dsize, truncation occurred.
          118  */
          119 size_t
          120 strlcpy(char *dst, const char *src, size_t dsize)
          121 {
          122         const char *osrc = src;
          123         size_t nleft = dsize;
          124 
          125         /* Copy as many bytes as will fit. */
          126         if (nleft != 0) {
          127                 while (--nleft != 0) {
          128                         if ((*dst++ = *src++) == '\0')
          129                                 break;
          130                 }
          131         }
          132 
          133         /* Not enough room in dst, add NUL and traverse rest of src. */
          134         if (nleft == 0) {
          135                 if (dsize != 0)
          136                         *dst = '\0';                /* NUL-terminate dst */
          137                 while (*src++)
          138                         ;
          139         }
          140 
          141         return(src - osrc - 1);        /* count does not include NUL */
          142 }
          143 
          144 /*
          145  * Replaces str with a string consisting of str with match replaced with
          146  * replstr as many times as can be done before the constructed string is
          147  * maxsize bytes large.  It does not free the string pointed to by str, it
          148  * is up to the calling program to be sure that the original contents of
          149  * str as well as the new contents are handled in an appropriate manner.
          150  * If replstr is NULL, then that internally is changed to a nil-string, so
          151  * that we can still pretend to do somewhat meaningful substitution.
          152  * No value is returned.
          153  */
          154 void
          155 strnsubst(char **str, const char *match, const char *replstr, size_t maxsize)
          156 {
          157         char *s1, *s2, *this;
          158         size_t matchlen, s2len;
          159         int n;
          160 
          161         if ((s1 = *str) == NULL)
          162                 return;
          163         if ((s2 = malloc(maxsize)) == NULL)
          164                 err(1, NULL);
          165 
          166         if (replstr == NULL)
          167                 replstr = "";
          168 
          169         if (match == NULL || *match == '\0' || strlen(s1) >= maxsize) {
          170                 strlcpy(s2, s1, maxsize);
          171                 goto done;
          172         }
          173 
          174         *s2 = '\0';
          175         s2len = 0;
          176         matchlen = strlen(match);
          177         for (;;) {
          178                 if ((this = strstr(s1, match)) == NULL)
          179                         break;
          180                 n = snprintf(s2 + s2len, maxsize - s2len, "%.*s%s",
          181                     (int)(this - s1), s1, replstr);
          182                 if (n < 0 || n + s2len + strlen(this + matchlen) >= maxsize)
          183                         break;                        /* out of room */
          184                 s2len += n;
          185                 s1 = this + matchlen;
          186         }
          187         strlcpy(s2 + s2len, s1, maxsize - s2len);
          188 done:
          189         *str = s2;
          190         return;
          191 }
          192 
          193 /* strtonum: import from OpenBSD, see: openbsd/src/lib/libc/stdlib/strtonum.c */
          194 
          195 #define        INVALID                1
          196 #define        TOOSMALL        2
          197 #define        TOOLARGE        3
          198 
          199 long long
          200 strtonum(const char *numstr, long long minval, long long maxval,
          201     const char **errstrp)
          202 {
          203         long long ll = 0;
          204         int error = 0;
          205         char *ep;
          206         struct errval {
          207                 const char *errstr;
          208                 int err;
          209         } ev[4] = {
          210                 { NULL,                0 },
          211                 { "invalid",        EINVAL },
          212                 { "too small",        ERANGE },
          213                 { "too large",        ERANGE },
          214         };
          215 
          216         ev[0].err = errno;
          217         errno = 0;
          218         if (minval > maxval) {
          219                 error = INVALID;
          220         } else {
          221                 ll = strtoll(numstr, &ep, 10);
          222                 if (numstr == ep || *ep != '\0')
          223                         error = INVALID;
          224                 else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
          225                         error = TOOSMALL;
          226                 else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
          227                         error = TOOLARGE;
          228         }
          229         if (errstrp != NULL)
          230                 *errstrp = ev[error].errstr;
          231         errno = ev[error].err;
          232         if (error)
          233                 ll = 0;
          234 
          235         return (ll);
          236 }
          237 
          238 int
          239 main(int argc, char *argv[])
          240 {
          241         long arg_max;
          242         int ch, Jflag, nargs, nflag, nline;
          243         size_t linelen;
          244         char *endptr;
          245         const char *errstr;
          246 
          247         inpline = replstr = NULL;
          248         ep = environ;
          249         eofstr = "";
          250         Jflag = nflag = 0;
          251 
          252         /*
          253          * POSIX.2 limits the exec line length to ARG_MAX - 2K.  Running that
          254          * caused some E2BIG errors, so it was changed to ARG_MAX - 4K.  Given
          255          * that the smallest argument is 2 bytes in length, this means that
          256          * the number of arguments is limited to:
          257          *
          258          *         (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
          259          *
          260          * We arbitrarily limit the number of arguments to 5000.  This is
          261          * allowed by POSIX.2 as long as the resulting minimum exec line is
          262          * at least LINE_MAX.  Realloc'ing as necessary is possible, but
          263          * probably not worthwhile.
          264          */
          265         nargs = 5000;
          266         if ((arg_max = sysconf(_SC_ARG_MAX)) == -1)
          267                 errx(1, "sysconf(_SC_ARG_MAX) failed");
          268 
          269         if (pledge("stdio rpath proc exec", NULL) == -1)
          270                 err(1, "pledge");
          271 
          272         nline = arg_max - 4 * 1024;
          273         while (*ep != NULL) {
          274                 /* 1 byte for each '\0' */
          275                 nline -= strlen(*ep++) + 1 + sizeof(*ep);
          276         }
          277         maxprocs = 1;
          278         while ((ch = getopt(argc, argv, "0E:I:J:L:n:oP:pR:rs:tx")) != -1)
          279                 switch (ch) {
          280                 case 'E':
          281                         eofstr = optarg;
          282                         break;
          283                 case 'I':
          284                         Jflag = 0;
          285                         Iflag = 1;
          286                         Lflag = 1;
          287                         replstr = optarg;
          288                         break;
          289                 case 'J':
          290                         Iflag = 0;
          291                         Jflag = 1;
          292                         replstr = optarg;
          293                         break;
          294                 case 'L':
          295                         Lflag = strtonum(optarg, 0, INT_MAX, &errstr);
          296                         if (errstr)
          297                                 errx(1, "-L %s: %s", optarg, errstr);
          298                         break;
          299                 case 'n':
          300                         nflag = 1;
          301                         nargs = strtonum(optarg, 1, INT_MAX, &errstr);
          302                         if (errstr)
          303                                 errx(1, "-n %s: %s", optarg, errstr);
          304                         break;
          305                 case 'o':
          306                         oflag = 1;
          307                         break;
          308                 case 'P':
          309                         maxprocs = strtonum(optarg, 1, INT_MAX, &errstr);
          310                         if (errstr)
          311                                 errx(1, "-P %s: %s", optarg, errstr);
          312                         break;
          313                 case 'p':
          314                         pflag = 1;
          315                         break;
          316                 case 'r':
          317                         runeof = 0;
          318                         break;
          319                 case 'R':
          320                         Rflag = strtol(optarg, &endptr, 10);
          321                         if (*endptr != '\0')
          322                                 errx(1, "replacements must be a number");
          323                         break;
          324                 case 's':
          325                         nline = strtonum(optarg, 0, INT_MAX, &errstr);
          326                         if (errstr)
          327                                 errx(1, "-s %s: %s", optarg, errstr);
          328                         break;
          329                 case 't':
          330                         tflag = 1;
          331                         break;
          332                 case 'x':
          333                         xflag = 1;
          334                         break;
          335                 case '0':
          336                         zflag = 1;
          337                         break;
          338                 default:
          339                         usage();
          340                 }
          341         argc -= optind;
          342         argv += optind;
          343 
          344         if (!Iflag && Rflag)
          345                 usage();
          346         if (Iflag && !Rflag)
          347                 Rflag = 5;
          348         if (xflag && !nflag)
          349                 usage();
          350         if (Iflag || Lflag)
          351                 xflag = 1;
          352         if (replstr != NULL && *replstr == '\0')
          353                 errx(1, "replstr may not be empty");
          354 
          355         /*
          356          * Allocate pointers for the utility name, the utility arguments,
          357          * the maximum arguments to be read from stdin and the trailing
          358          * NULL.
          359          */
          360         linelen = 1 + argc + nargs + 1;
          361         if ((av = bxp = calloc(linelen, sizeof(char *))) == NULL)
          362                 err(1, NULL);
          363 
          364         /*
          365          * Use the user's name for the utility as argv[0], just like the
          366          * shell.  Echo is the default.  Set up pointers for the user's
          367          * arguments.
          368          */
          369         if (*argv == NULL)
          370                 cnt = strlen(*bxp++ = _PATH_ECHO);
          371         else {
          372                 do {
          373                         if (Jflag && strcmp(*argv, replstr) == 0) {
          374                                 char **avj;
          375                                 jfound = 1;
          376                                 argv++;
          377                                 for (avj = argv; *avj; avj++)
          378                                         cnt += strlen(*avj) + 1;
          379                                 break;
          380                         }
          381                         cnt += strlen(*bxp++ = *argv) + 1;
          382                 } while (*++argv != NULL);
          383         }
          384 
          385         /*
          386          * Set up begin/end/traversing pointers into the array.  The -n
          387          * count doesn't include the trailing NULL pointer, so the malloc
          388          * added in an extra slot.
          389          */
          390         endxp = (xp = bxp) + nargs;
          391 
          392         /*
          393          * Allocate buffer space for the arguments read from stdin and the
          394          * trailing NULL.  Buffer space is defined as the default or specified
          395          * space, minus the length of the utility name and arguments.  Set up
          396          * begin/end/traversing pointers into the array.  The -s count does
          397          * include the trailing NULL, so the malloc didn't add in an extra
          398          * slot.
          399          */
          400         nline -= cnt;
          401         if (nline <= 0)
          402                 errx(1, "insufficient space for command");
          403 
          404         if ((bbp = malloc((size_t)(nline + 1))) == NULL)
          405                 err(1, NULL);
          406         ebp = (argp = p = bbp) + nline - 1;
          407         for (;;)
          408                 parse_input(argc, argv);
          409 }
          410 
          411 static void
          412 parse_input(int argc, char *argv[])
          413 {
          414         int hasblank = 0;
          415         static int hadblank = 0;
          416         int ch, foundeof = 0;
          417         char **avj;
          418 
          419         ch = getchar();
          420         if (isblank(ch)) {
          421                 /* Quotes escape tabs and spaces. */
          422                 if (insingle || indouble)
          423                         goto addch;
          424                 hasblank = 1;
          425                 if (zflag)
          426                         goto addch;
          427                 goto arg2;
          428         }
          429 
          430         switch (ch) {
          431         case EOF:
          432                 /* No arguments since last exec. */
          433                 if (p == bbp) {
          434                         if (runeof)
          435                                 prerun(0, av);
          436                         waitchildren(*argv, 1);
          437                         exit(rval);
          438                 }
          439                 goto arg1;
          440         case '\0':
          441                 if (zflag) {
          442                         /*
          443                          * Increment 'count', so that nulls will be treated
          444                          * as end-of-line, as well as end-of-argument.  This
          445                          * is needed so -0 works properly with -I and -L.
          446                          */
          447                         count++;
          448                         goto arg2;
          449                 }
          450                 goto addch;
          451         case '\n':
          452                 if (zflag)
          453                         goto addch;
          454                 hasblank = 1;
          455                 if (hadblank == 0)
          456                         count++;
          457 
          458                 /* Quotes do not escape newlines. */
          459 arg1:                if (insingle || indouble)
          460                         errx(1, "unterminated quote");
          461 arg2:
          462                 foundeof = *eofstr != '\0' &&
          463                     strcmp(argp, eofstr) == 0;
          464 
          465                 /*
          466                  * Do not make empty args unless they are quoted or
          467                  * we are run as "find -0" and not at EOF.
          468                  */
          469                 if (((zflag && ch != EOF) || argp != p || wasquoted) &&
          470                     !foundeof) {
          471                         *p++ = '\0';
          472                         *xp++ = argp;
          473                         if (Iflag) {
          474                                 size_t curlen;
          475 
          476                                 if (inpline == NULL)
          477                                         curlen = 0;
          478                                 else {
          479                                         /*
          480                                          * If this string is not zero
          481                                          * length, append a space for
          482                                          * separation before the next
          483                                          * argument.
          484                                          */
          485                                         if ((curlen = strlen(inpline)))
          486                                                 strlcat(inpline, " ", inpsize);
          487                                 }
          488                                 curlen++;
          489                                 /*
          490                                  * Allocate enough to hold what we will
          491                                  * be holding in a second, and to append
          492                                  * a space next time through, if we have
          493                                  * to.
          494                                  */
          495                                 inpsize = curlen + 2 + strlen(argp);
          496                                 inpline = realloc(inpline, inpsize);
          497                                 if (inpline == NULL)
          498                                         errx(1, "realloc failed");
          499                                 if (curlen == 1)
          500                                         strlcpy(inpline, argp, inpsize);
          501                                 else
          502                                         strlcat(inpline, argp, inpsize);
          503                         }
          504                 }
          505 
          506                 /*
          507                  * If max'd out on args or buffer, or reached EOF,
          508                  * run the command.  If xflag and max'd out on buffer
          509                  * but not on args, object.  Having reached the limit
          510                  * of input lines, as specified by -L is the same as
          511                  * maxing out on arguments.
          512                  */
          513                 if (xp == endxp || p > ebp || ch == EOF ||
          514                     (Lflag <= count && xflag) || foundeof) {
          515                         if (xflag && xp != endxp && p > ebp)
          516                                 errx(1, "insufficient space for arguments");
          517                         if (jfound) {
          518                                 for (avj = argv; *avj; avj++)
          519                                         *xp++ = *avj;
          520                         }
          521                         prerun(argc, av);
          522                         if (ch == EOF || foundeof) {
          523                                 waitchildren(*argv, 1);
          524                                 exit(rval);
          525                         }
          526                         p = bbp;
          527                         xp = bxp;
          528                         count = 0;
          529                 }
          530                 argp = p;
          531                 wasquoted = 0;
          532                 break;
          533         case '\'':
          534                 if (indouble || zflag)
          535                         goto addch;
          536                 insingle = !insingle;
          537                 wasquoted = 1;
          538                 break;
          539         case '"':
          540                 if (insingle || zflag)
          541                         goto addch;
          542                 indouble = !indouble;
          543                 wasquoted = 1;
          544                 break;
          545         case '\\':
          546                 if (zflag)
          547                         goto addch;
          548                 /* Backslash escapes anything, is escaped by quotes. */
          549                 if (!insingle && !indouble && (ch = getchar()) == EOF)
          550                         errx(1, "backslash at EOF");
          551                 /* FALLTHROUGH */
          552         default:
          553 addch:                if (p < ebp) {
          554                         *p++ = ch;
          555                         break;
          556                 }
          557 
          558                 /* If only one argument, not enough buffer space. */
          559                 if (bxp == xp)
          560                         errx(1, "insufficient space for argument");
          561                 /* Didn't hit argument limit, so if xflag object. */
          562                 if (xflag)
          563                         errx(1, "insufficient space for arguments");
          564 
          565                 if (jfound) {
          566                         for (avj = argv; *avj; avj++)
          567                                 *xp++ = *avj;
          568                 }
          569                 prerun(argc, av);
          570                 xp = bxp;
          571                 cnt = ebp - argp;
          572                 memmove(bbp, argp, (size_t)cnt);
          573                 p = (argp = bbp) + cnt;
          574                 *p++ = ch;
          575                 break;
          576         }
          577         hadblank = hasblank;
          578 }
          579 
          580 /*
          581  * Do things necessary before run()'ing, such as -I substitution,
          582  * and then call run().
          583  */
          584 static void
          585 prerun(int argc, char *argv[])
          586 {
          587         char **tmp, **tmp2, **avj;
          588         int repls;
          589 
          590         repls = Rflag;
          591         runeof = 0;
          592 
          593         if (argc == 0 || repls == 0) {
          594                 *xp = NULL;
          595                 run(argv);
          596                 return;
          597         }
          598 
          599         avj = argv;
          600 
          601         /*
          602          * Allocate memory to hold the argument list, and
          603          * a NULL at the tail.
          604          */
          605         tmp = calloc(argc + 1, sizeof(char *));
          606         if (tmp == NULL)
          607                 err(1, NULL);
          608         tmp2 = tmp;
          609 
          610         /*
          611          * Save the first argument and iterate over it, we
          612          * cannot do strnsubst() to it.
          613          */
          614         if ((*tmp++ = strdup(*avj++)) == NULL)
          615                 err(1, NULL);
          616 
          617         /*
          618          * For each argument to utility, if we have not used up
          619          * the number of replacements we are allowed to do, and
          620          * if the argument contains at least one occurrence of
          621          * replstr, call strnsubst(), else just save the string.
          622          * Iterations over elements of avj and tmp are done
          623          * where appropriate.
          624          */
          625         while (--argc) {
          626                 *tmp = *avj++;
          627                 if (repls && strstr(*tmp, replstr) != NULL) {
          628                         strnsubst(tmp++, replstr, inpline, (size_t)255);
          629                         if (repls > 0)
          630                                 repls--;
          631                 } else {
          632                         if ((*tmp = strdup(*tmp)) == NULL)
          633                                 err(1, NULL);
          634                         tmp++;
          635                 }
          636         }
          637 
          638         /*
          639          * Run it.
          640          */
          641         *tmp = NULL;
          642         run(tmp2);
          643 
          644         /*
          645          * Walk from the tail to the head, free along the way.
          646          */
          647         for (; tmp2 != tmp; tmp--)
          648                 free(*tmp);
          649         /*
          650          * Now free the list itself.
          651          */
          652         free(tmp2);
          653 
          654         /*
          655          * Free the input line buffer, if we have one.
          656          */
          657         free(inpline);
          658         inpline = NULL;
          659 }
          660 
          661 static void
          662 run(char **argv)
          663 {
          664         pid_t pid;
          665         int fd;
          666         char **avec;
          667 
          668         /*
          669          * If the user wants to be notified of each command before it is
          670          * executed, notify them.  If they want the notification to be
          671          * followed by a prompt, then prompt them.
          672          */
          673         if (tflag || pflag) {
          674                 fprintf(stderr, "%s", *argv);
          675                 for (avec = argv + 1; *avec != NULL; ++avec)
          676                         fprintf(stderr, " %s", *avec);
          677                 /*
          678                  * If the user has asked to be prompted, do so.
          679                  */
          680                 if (pflag)
          681                         /*
          682                          * If they asked not to exec, return without execution
          683                          * but if they asked to, go to the execution.  If we
          684                          * could not open their tty, break the switch and drop
          685                          * back to -t behaviour.
          686                          */
          687                         switch (prompt()) {
          688                         case 0:
          689                                 return;
          690                         case 1:
          691                                 goto exec;
          692                         case 2:
          693                                 break;
          694                         }
          695                 fprintf(stderr, "\n");
          696                 fflush(stderr);
          697         }
          698 exec:
          699         switch (pid = vfork()) {
          700         case -1:
          701                 err(1, "vfork");
          702         case 0:
          703                 if (oflag) {
          704                         if ((fd = open(_PATH_TTY, O_RDONLY)) == -1) {
          705                                 warn("can't open /dev/tty");
          706                                 _exit(1);
          707                         }
          708                 } else {
          709                         fd = open(_PATH_DEVNULL, O_RDONLY);
          710                 }
          711                 if (fd > STDIN_FILENO) {
          712                         if (dup2(fd, STDIN_FILENO) != 0) {
          713                                 warn("can't dup2 to stdin");
          714                                 _exit(1);
          715                         }
          716                         close(fd);
          717                 }
          718                 execvp(argv[0], argv);
          719                 warn("%s", argv[0]);
          720                 _exit(errno == ENOENT ? 127 : 126);
          721         }
          722         curprocs++;
          723         waitchildren(*argv, 0);
          724 }
          725 
          726 static void
          727 waitchildren(const char *name, int waitall)
          728 {
          729         pid_t pid;
          730         int status;
          731 
          732         while ((pid = waitpid(-1, &status, !waitall && curprocs < maxprocs ?
          733             WNOHANG : 0)) > 0) {
          734                 curprocs--;
          735                 /*
          736                  * According to POSIX, we have to exit if the utility exits
          737                  * with a 255 status, or is interrupted by a signal.
          738                  * We are allowed to return any exit status between 1 and
          739                  * 125 in these cases, but we'll use 124 and 125, the same
          740                  * values used by GNU xargs.
          741                  */
          742                 if (WIFEXITED(status)) {
          743                         if (WEXITSTATUS(status) == 255) {
          744                                 warnx("%s exited with status 255", name);
          745                                 exit(124);
          746                         } else if (WEXITSTATUS(status) == 127 ||
          747                             WEXITSTATUS(status) == 126) {
          748                                 exit(WEXITSTATUS(status));
          749                         } else if (WEXITSTATUS(status) != 0) {
          750                                 rval = 123;
          751                         }
          752                 } else if (WIFSIGNALED(status)) {
          753                         if (WTERMSIG(status) != SIGPIPE) {
          754 /*                                if (WTERMSIG(status) < NSIG)
          755                                         warnx("%s terminated by SIG%s", name,
          756                                             sys_signame[WTERMSIG(status)]);
          757                                 else */
          758                                         warnx("%s terminated by signal %d",
          759                                             name, WTERMSIG(status));
          760                         }
          761                         exit(125);
          762                 }
          763         }
          764         if (pid == -1 && errno != ECHILD)
          765                 err(1, "waitpid");
          766 }
          767 
          768 /*
          769  * Prompt the user about running a command.
          770  */
          771 static int
          772 prompt(void)
          773 {
          774         size_t rsize;
          775         char *response;
          776         FILE *ttyfp;
          777         int doit = 0;
          778 
          779         if ((ttyfp = fopen(_PATH_TTY, "r")) == NULL)
          780                 return (2);        /* Indicate that the TTY failed to open. */
          781         fprintf(stderr, "?...");
          782         fflush(stderr);
          783 /*        response = fgetln(ttyfp, &rsize);
          784         doit = response != NULL && (*response == 'y' || *response == 'Y');*/
          785         
          786         int ch = getc(ttyfp);
          787         doit = (ch == 'y' || ch == 'Y');
          788         
          789         fclose(ttyfp);
          790         return (doit);
          791 }
          792 
          793 static void
          794 usage(void)
          795 {
          796         fprintf(stderr,
          797 "usage: xargs [-0oprt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]\n"
          798 "             [-L number] [-n number [-x]] [-P maxprocs] [-s size]\n"
          799 "             [utility [argument ...]]\n");
          800         exit(1);
          801 }