[BACK]Return to xargs.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / xargs

Annotation of src/usr.bin/xargs/xargs.c, Revision 1.6

1.6     ! deraadt     1: /*     $OpenBSD: xargs.c,v 1.5 1997/09/12 01:47:25 deraadt Exp $       */
1.1       deraadt     2: /*     $NetBSD: xargs.c,v 1.7 1994/11/14 06:51:41 jtc 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. All advertising materials mentioning features or use of this software
                     20:  *    must display the following acknowledgement:
                     21:  *     This product includes software developed by the University of
                     22:  *     California, Berkeley and its contributors.
                     23:  * 4. Neither the name of the University nor the names of its contributors
                     24:  *    may be used to endorse or promote products derived from this software
                     25:  *    without specific prior written permission.
                     26:  *
                     27:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     28:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     29:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     30:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     31:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     32:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     33:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     34:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     35:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     36:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     37:  * SUCH DAMAGE.
                     38:  */
                     39:
                     40: #ifndef lint
                     41: static char copyright[] =
                     42: "@(#) Copyright (c) 1990, 1993\n\
                     43:        The Regents of the University of California.  All rights reserved.\n";
                     44: #endif /* not lint */
                     45:
                     46: #ifndef lint
                     47: #if 0
                     48: static char sccsid[] = "@(#)xargs.c    8.1 (Berkeley) 6/6/93";
                     49: #endif
1.6     ! deraadt    50: static char rcsid[] = "$OpenBSD: xargs.c,v 1.5 1997/09/12 01:47:25 deraadt Exp $";
1.1       deraadt    51: #endif /* not lint */
                     52:
                     53: #include <sys/types.h>
                     54: #include <sys/wait.h>
                     55: #include <errno.h>
                     56: #include <stdio.h>
                     57: #include <stdlib.h>
                     58: #include <string.h>
                     59: #include <unistd.h>
                     60: #include <limits.h>
                     61: #include <locale.h>
1.6     ! deraadt    62: #include <signal.h>
1.1       deraadt    63: #include <err.h>
                     64: #include "pathnames.h"
                     65:
                     66: int tflag, rval;
1.2       deraadt    67: int zflag;
1.1       deraadt    68:
                     69: void run __P((char **));
                     70: void usage __P((void));
                     71:
                     72: int
                     73: main(argc, argv)
                     74:        int argc;
                     75:        char **argv;
                     76: {
                     77:        register int ch;
                     78:        register char *p, *bbp, *ebp, **bxp, **exp, **xp;
                     79:        int cnt, indouble, insingle, nargs, nflag, nline, xflag;
                     80:        char **av, *argp;
                     81:
                     82:        setlocale(LC_ALL, "");
                     83:
                     84:        /*
                     85:         * POSIX.2 limits the exec line length to ARG_MAX - 2K.  Running that
                     86:         * caused some E2BIG errors, so it was changed to ARG_MAX - 4K.  Given
                     87:         * that the smallest argument is 2 bytes in length, this means that
                     88:         * the number of arguments is limited to:
                     89:         *
                     90:         *       (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
                     91:         *
                     92:         * We arbitrarily limit the number of arguments to 5000.  This is
                     93:         * allowed by POSIX.2 as long as the resulting minimum exec line is
                     94:         * at least LINE_MAX.  Realloc'ing as necessary is possible, but
                     95:         * probably not worthwhile.
                     96:         */
                     97:        nargs = 5000;
                     98:        nline = ARG_MAX - 4 * 1024;
                     99:        nflag = xflag = 0;
1.4       millert   100:        while ((ch = getopt(argc, argv, "0n:s:tx")) != -1)
1.1       deraadt   101:                switch(ch) {
                    102:                case 'n':
                    103:                        nflag = 1;
                    104:                        if ((nargs = atoi(optarg)) <= 0)
                    105:                                errx(1, "illegal argument count");
                    106:                        break;
                    107:                case 's':
                    108:                        nline = atoi(optarg);
                    109:                        break;
                    110:                case 't':
                    111:                        tflag = 1;
                    112:                        break;
                    113:                case 'x':
                    114:                        xflag = 1;
                    115:                        break;
1.2       deraadt   116:                case '0':
                    117:                        zflag = 1;
                    118:                        break;
1.1       deraadt   119:                case '?':
                    120:                default:
                    121:                        usage();
                    122:        }
                    123:        argc -= optind;
                    124:        argv += optind;
                    125:
                    126:        if (xflag && !nflag)
                    127:                usage();
                    128:
                    129:        /*
                    130:         * Allocate pointers for the utility name, the utility arguments,
                    131:         * the maximum arguments to be read from stdin and the trailing
                    132:         * NULL.
                    133:         */
                    134:        if (!(av = bxp =
                    135:            malloc((u_int)(1 + argc + nargs + 1) * sizeof(char **))))
                    136:                err(1, NULL);
                    137:
                    138:        /*
                    139:         * Use the user's name for the utility as argv[0], just like the
                    140:         * shell.  Echo is the default.  Set up pointers for the user's
                    141:         * arguments.
                    142:         */
                    143:        if (!*argv)
                    144:                cnt = strlen(*bxp++ = _PATH_ECHO);
                    145:        else {
                    146:                cnt = 0;
                    147:                do {
                    148:                        cnt += strlen(*bxp++ = *argv) + 1;
                    149:                } while (*++argv);
                    150:        }
                    151:
                    152:        /*
                    153:         * Set up begin/end/traversing pointers into the array.  The -n
                    154:         * count doesn't include the trailing NULL pointer, so the malloc
                    155:         * added in an extra slot.
                    156:         */
                    157:        exp = (xp = bxp) + nargs;
                    158:
                    159:        /*
                    160:         * Allocate buffer space for the arguments read from stdin and the
                    161:         * trailing NULL.  Buffer space is defined as the default or specified
                    162:         * space, minus the length of the utility name and arguments.  Set up
                    163:         * begin/end/traversing pointers into the array.  The -s count does
                    164:         * include the trailing NULL, so the malloc didn't add in an extra
                    165:         * slot.
                    166:         */
                    167:        nline -= cnt;
                    168:        if (nline <= 0)
                    169:                errx(1, "insufficient space for command");
                    170:
                    171:        if (!(bbp = malloc((u_int)nline + 1)))
                    172:                err(1, NULL);
                    173:        ebp = (argp = p = bbp) + nline - 1;
                    174:
                    175:        for (insingle = indouble = 0;;)
                    176:                switch(ch = getchar()) {
                    177:                case EOF:
                    178:                        /* No arguments since last exec. */
                    179:                        if (p == bbp)
                    180:                                exit(rval);
                    181:
                    182:                        /* Nothing since end of last argument. */
                    183:                        if (argp == p) {
                    184:                                *xp = NULL;
                    185:                                run(av);
                    186:                                exit(rval);
                    187:                        }
                    188:                        goto arg1;
                    189:                case ' ':
                    190:                case '\t':
                    191:                        /* Quotes escape tabs and spaces. */
1.2       deraadt   192:                        if (insingle || indouble || zflag)
1.1       deraadt   193:                                goto addch;
                    194:                        goto arg2;
1.2       deraadt   195:                case '\0':
                    196:                        if (zflag)
                    197:                                goto arg2;
                    198:                        goto addch;
1.1       deraadt   199:                case '\n':
1.2       deraadt   200:                        if (zflag)
                    201:                                goto addch;
                    202:
1.1       deraadt   203:                        /* Empty lines are skipped. */
                    204:                        if (argp == p)
                    205:                                continue;
                    206:
                    207:                        /* Quotes do not escape newlines. */
                    208: arg1:                  if (insingle || indouble)
                    209:                                 errx(1, "unterminated quote");
                    210:
                    211: arg2:                  *p = '\0';
                    212:                        *xp++ = argp;
                    213:
                    214:                        /*
                    215:                         * If max'd out on args or buffer, or reached EOF,
                    216:                         * run the command.  If xflag and max'd out on buffer
                    217:                         * but not on args, object.
                    218:                         */
                    219:                        if (xp == exp || p == ebp || ch == EOF) {
                    220:                                if (xflag && xp != exp && p == ebp)
                    221:                                        errx(1, "insufficient space for arguments");
                    222:                                *xp = NULL;
                    223:                                run(av);
                    224:                                if (ch == EOF)
                    225:                                        exit(rval);
                    226:                                p = bbp;
                    227:                                xp = bxp;
                    228:                        } else
                    229:                                ++p;
                    230:                        argp = p;
                    231:                        break;
                    232:                case '\'':
1.2       deraadt   233:                        if (indouble || zflag)
1.1       deraadt   234:                                goto addch;
                    235:                        insingle = !insingle;
                    236:                        break;
                    237:                case '"':
1.2       deraadt   238:                        if (insingle || zflag)
1.1       deraadt   239:                                goto addch;
                    240:                        indouble = !indouble;
                    241:                        break;
                    242:                case '\\':
1.2       deraadt   243:                        if (zflag)
                    244:                                goto addch;
1.1       deraadt   245:                        /* Backslash escapes anything, is escaped by quotes. */
                    246:                        if (!insingle && !indouble && (ch = getchar()) == EOF)
                    247:                                errx(1, "backslash at EOF");
                    248:                        /* FALLTHROUGH */
                    249:                default:
                    250: addch:                 if (p < ebp) {
                    251:                                *p++ = ch;
                    252:                                break;
                    253:                        }
                    254:
                    255:                        /* If only one argument, not enough buffer space. */
                    256:                        if (bxp == xp)
                    257:                                errx(1, "insufficient space for argument");
                    258:                        /* Didn't hit argument limit, so if xflag object. */
                    259:                        if (xflag)
                    260:                                errx(1, "insufficient space for arguments");
                    261:
                    262:                        *xp = NULL;
                    263:                        run(av);
                    264:                        xp = bxp;
                    265:                        cnt = ebp - argp;
                    266:                        bcopy(argp, bbp, cnt);
                    267:                        p = (argp = bbp) + cnt;
                    268:                        *p++ = ch;
                    269:                        break;
                    270:                }
                    271:        /* NOTREACHED */
                    272: }
                    273:
                    274: void
                    275: run(argv)
                    276:        char **argv;
                    277: {
                    278:        volatile int noinvoke;
                    279:        register char **p;
                    280:        pid_t pid;
                    281:        int status;
                    282:
                    283:        if (tflag) {
                    284:                (void)fprintf(stderr, "%s", *argv);
                    285:                for (p = argv + 1; *p; ++p)
                    286:                        (void)fprintf(stderr, " %s", *p);
                    287:                (void)fprintf(stderr, "\n");
                    288:                (void)fflush(stderr);
                    289:        }
                    290:        noinvoke = 0;
                    291:        switch(pid = vfork()) {
                    292:        case -1:
                    293:                err(1, "vfork");
                    294:        case 0:
                    295:                execvp(argv[0], argv);
                    296:                noinvoke = (errno == ENOENT) ? 127 : 126;
1.5       deraadt   297:                warn("%s", argv[0]);
1.1       deraadt   298:                _exit(1);
                    299:        }
                    300:        pid = waitpid(pid, &status, 0);
                    301:        if (pid == -1)
                    302:                err(1, "waitpid");
                    303:
                    304:        /*
                    305:         * If we couldn't invoke the utility or the utility didn't exit
                    306:         * properly, quit with 127 or 126 respectively.
                    307:         */
                    308:        if (noinvoke)
                    309:                exit(noinvoke);
                    310:
                    311:        /*
                    312:         * According to POSIX, we have to exit if the utility exits with
                    313:         * a 255 status, or is interrupted by a signal.   xargs is allowed
                    314:         * to return any exit status between 1 and 125 in these cases, but
                    315:         * we'll use 124 and 125, the same values used by GNU xargs.
                    316:         */
                    317:        if (WIFEXITED(status)) {
                    318:                if (WEXITSTATUS (status) == 255) {
                    319:                        warnx ("%s exited with status 255", argv[0]);
                    320:                        exit(124);
                    321:                } else if (WEXITSTATUS (status) != 0) {
                    322:                        rval = 123;
                    323:                }
                    324:        } else if (WIFSIGNALED (status)) {
1.6     ! deraadt   325:                if (WTERMSIG(status) != SIGPIPE) {
        !           326:                        if (WTERMSIG(status) < NSIG)
        !           327:                                warnx ("%s terminated by SIG%s", argv[0],
        !           328:                                    sys_signame[WTERMSIG(status)]);
        !           329:                        else
        !           330:                                warnx ("%s terminated by signal %d", argv[0],
        !           331:                                    WTERMSIG(status));
        !           332:                }
1.1       deraadt   333:                exit(125);
                    334:        }
                    335: }
                    336:
                    337: void
                    338: usage()
                    339: {
                    340:        (void)fprintf(stderr,
1.2       deraadt   341: "usage: xargs [-0] [-t] [-n number [-x]] [-s size] [utility [argument ...]]\n");
1.1       deraadt   342:        exit(1);
                    343: }