[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.3

1.3     ! deraadt     1: /*     $OpenBSD: xargs.c,v 1.7 1994/11/14 06:51:41 jtc 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.3     ! deraadt    50: static char rcsid[] = "$OpenBSD: xargs.c,v 1.7 1994/11/14 06:51:41 jtc 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>
                     62: #include <err.h>
                     63: #include "pathnames.h"
                     64:
                     65: int tflag, rval;
1.2       deraadt    66: int zflag;
1.1       deraadt    67:
                     68: void run __P((char **));
                     69: void usage __P((void));
                     70:
                     71: int
                     72: main(argc, argv)
                     73:        int argc;
                     74:        char **argv;
                     75: {
                     76:        register int ch;
                     77:        register char *p, *bbp, *ebp, **bxp, **exp, **xp;
                     78:        int cnt, indouble, insingle, nargs, nflag, nline, xflag;
                     79:        char **av, *argp;
                     80:
                     81:        setlocale(LC_ALL, "");
                     82:
                     83:        /*
                     84:         * POSIX.2 limits the exec line length to ARG_MAX - 2K.  Running that
                     85:         * caused some E2BIG errors, so it was changed to ARG_MAX - 4K.  Given
                     86:         * that the smallest argument is 2 bytes in length, this means that
                     87:         * the number of arguments is limited to:
                     88:         *
                     89:         *       (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
                     90:         *
                     91:         * We arbitrarily limit the number of arguments to 5000.  This is
                     92:         * allowed by POSIX.2 as long as the resulting minimum exec line is
                     93:         * at least LINE_MAX.  Realloc'ing as necessary is possible, but
                     94:         * probably not worthwhile.
                     95:         */
                     96:        nargs = 5000;
                     97:        nline = ARG_MAX - 4 * 1024;
                     98:        nflag = xflag = 0;
1.2       deraadt    99:        while ((ch = getopt(argc, argv, "0n:s:tx")) != EOF)
1.1       deraadt   100:                switch(ch) {
                    101:                case 'n':
                    102:                        nflag = 1;
                    103:                        if ((nargs = atoi(optarg)) <= 0)
                    104:                                errx(1, "illegal argument count");
                    105:                        break;
                    106:                case 's':
                    107:                        nline = atoi(optarg);
                    108:                        break;
                    109:                case 't':
                    110:                        tflag = 1;
                    111:                        break;
                    112:                case 'x':
                    113:                        xflag = 1;
                    114:                        break;
1.2       deraadt   115:                case '0':
                    116:                        zflag = 1;
                    117:                        break;
1.1       deraadt   118:                case '?':
                    119:                default:
                    120:                        usage();
                    121:        }
                    122:        argc -= optind;
                    123:        argv += optind;
                    124:
                    125:        if (xflag && !nflag)
                    126:                usage();
                    127:
                    128:        /*
                    129:         * Allocate pointers for the utility name, the utility arguments,
                    130:         * the maximum arguments to be read from stdin and the trailing
                    131:         * NULL.
                    132:         */
                    133:        if (!(av = bxp =
                    134:            malloc((u_int)(1 + argc + nargs + 1) * sizeof(char **))))
                    135:                err(1, NULL);
                    136:
                    137:        /*
                    138:         * Use the user's name for the utility as argv[0], just like the
                    139:         * shell.  Echo is the default.  Set up pointers for the user's
                    140:         * arguments.
                    141:         */
                    142:        if (!*argv)
                    143:                cnt = strlen(*bxp++ = _PATH_ECHO);
                    144:        else {
                    145:                cnt = 0;
                    146:                do {
                    147:                        cnt += strlen(*bxp++ = *argv) + 1;
                    148:                } while (*++argv);
                    149:        }
                    150:
                    151:        /*
                    152:         * Set up begin/end/traversing pointers into the array.  The -n
                    153:         * count doesn't include the trailing NULL pointer, so the malloc
                    154:         * added in an extra slot.
                    155:         */
                    156:        exp = (xp = bxp) + nargs;
                    157:
                    158:        /*
                    159:         * Allocate buffer space for the arguments read from stdin and the
                    160:         * trailing NULL.  Buffer space is defined as the default or specified
                    161:         * space, minus the length of the utility name and arguments.  Set up
                    162:         * begin/end/traversing pointers into the array.  The -s count does
                    163:         * include the trailing NULL, so the malloc didn't add in an extra
                    164:         * slot.
                    165:         */
                    166:        nline -= cnt;
                    167:        if (nline <= 0)
                    168:                errx(1, "insufficient space for command");
                    169:
                    170:        if (!(bbp = malloc((u_int)nline + 1)))
                    171:                err(1, NULL);
                    172:        ebp = (argp = p = bbp) + nline - 1;
                    173:
                    174:        for (insingle = indouble = 0;;)
                    175:                switch(ch = getchar()) {
                    176:                case EOF:
                    177:                        /* No arguments since last exec. */
                    178:                        if (p == bbp)
                    179:                                exit(rval);
                    180:
                    181:                        /* Nothing since end of last argument. */
                    182:                        if (argp == p) {
                    183:                                *xp = NULL;
                    184:                                run(av);
                    185:                                exit(rval);
                    186:                        }
                    187:                        goto arg1;
                    188:                case ' ':
                    189:                case '\t':
                    190:                        /* Quotes escape tabs and spaces. */
1.2       deraadt   191:                        if (insingle || indouble || zflag)
1.1       deraadt   192:                                goto addch;
                    193:                        goto arg2;
1.2       deraadt   194:                case '\0':
                    195:                        if (zflag)
                    196:                                goto arg2;
                    197:                        goto addch;
1.1       deraadt   198:                case '\n':
1.2       deraadt   199:                        if (zflag)
                    200:                                goto addch;
                    201:
1.1       deraadt   202:                        /* Empty lines are skipped. */
                    203:                        if (argp == p)
                    204:                                continue;
                    205:
                    206:                        /* Quotes do not escape newlines. */
                    207: arg1:                  if (insingle || indouble)
                    208:                                 errx(1, "unterminated quote");
                    209:
                    210: arg2:                  *p = '\0';
                    211:                        *xp++ = argp;
                    212:
                    213:                        /*
                    214:                         * If max'd out on args or buffer, or reached EOF,
                    215:                         * run the command.  If xflag and max'd out on buffer
                    216:                         * but not on args, object.
                    217:                         */
                    218:                        if (xp == exp || p == ebp || ch == EOF) {
                    219:                                if (xflag && xp != exp && p == ebp)
                    220:                                        errx(1, "insufficient space for arguments");
                    221:                                *xp = NULL;
                    222:                                run(av);
                    223:                                if (ch == EOF)
                    224:                                        exit(rval);
                    225:                                p = bbp;
                    226:                                xp = bxp;
                    227:                        } else
                    228:                                ++p;
                    229:                        argp = p;
                    230:                        break;
                    231:                case '\'':
1.2       deraadt   232:                        if (indouble || zflag)
1.1       deraadt   233:                                goto addch;
                    234:                        insingle = !insingle;
                    235:                        break;
                    236:                case '"':
1.2       deraadt   237:                        if (insingle || zflag)
1.1       deraadt   238:                                goto addch;
                    239:                        indouble = !indouble;
                    240:                        break;
                    241:                case '\\':
1.2       deraadt   242:                        if (zflag)
                    243:                                goto addch;
1.1       deraadt   244:                        /* Backslash escapes anything, is escaped by quotes. */
                    245:                        if (!insingle && !indouble && (ch = getchar()) == EOF)
                    246:                                errx(1, "backslash at EOF");
                    247:                        /* FALLTHROUGH */
                    248:                default:
                    249: addch:                 if (p < ebp) {
                    250:                                *p++ = ch;
                    251:                                break;
                    252:                        }
                    253:
                    254:                        /* If only one argument, not enough buffer space. */
                    255:                        if (bxp == xp)
                    256:                                errx(1, "insufficient space for argument");
                    257:                        /* Didn't hit argument limit, so if xflag object. */
                    258:                        if (xflag)
                    259:                                errx(1, "insufficient space for arguments");
                    260:
                    261:                        *xp = NULL;
                    262:                        run(av);
                    263:                        xp = bxp;
                    264:                        cnt = ebp - argp;
                    265:                        bcopy(argp, bbp, cnt);
                    266:                        p = (argp = bbp) + cnt;
                    267:                        *p++ = ch;
                    268:                        break;
                    269:                }
                    270:        /* NOTREACHED */
                    271: }
                    272:
                    273: void
                    274: run(argv)
                    275:        char **argv;
                    276: {
                    277:        volatile int noinvoke;
                    278:        register char **p;
                    279:        pid_t pid;
                    280:        int status;
                    281:
                    282:        if (tflag) {
                    283:                (void)fprintf(stderr, "%s", *argv);
                    284:                for (p = argv + 1; *p; ++p)
                    285:                        (void)fprintf(stderr, " %s", *p);
                    286:                (void)fprintf(stderr, "\n");
                    287:                (void)fflush(stderr);
                    288:        }
                    289:        noinvoke = 0;
                    290:        switch(pid = vfork()) {
                    291:        case -1:
                    292:                err(1, "vfork");
                    293:        case 0:
                    294:                execvp(argv[0], argv);
                    295:                noinvoke = (errno == ENOENT) ? 127 : 126;
                    296:                warn("%s", argv[0]);;
                    297:                _exit(1);
                    298:        }
                    299:        pid = waitpid(pid, &status, 0);
                    300:        if (pid == -1)
                    301:                err(1, "waitpid");
                    302:
                    303:        /*
                    304:         * If we couldn't invoke the utility or the utility didn't exit
                    305:         * properly, quit with 127 or 126 respectively.
                    306:         */
                    307:        if (noinvoke)
                    308:                exit(noinvoke);
                    309:
                    310:        /*
                    311:         * According to POSIX, we have to exit if the utility exits with
                    312:         * a 255 status, or is interrupted by a signal.   xargs is allowed
                    313:         * to return any exit status between 1 and 125 in these cases, but
                    314:         * we'll use 124 and 125, the same values used by GNU xargs.
                    315:         */
                    316:        if (WIFEXITED(status)) {
                    317:                if (WEXITSTATUS (status) == 255) {
                    318:                        warnx ("%s exited with status 255", argv[0]);
                    319:                        exit(124);
                    320:                } else if (WEXITSTATUS (status) != 0) {
                    321:                        rval = 123;
                    322:                }
                    323:        } else if (WIFSIGNALED (status)) {
                    324:                warnx ("%s terminated by signal %d", argv[0], WTERMSIG(status));
                    325:                exit(125);
                    326:        }
                    327: }
                    328:
                    329: void
                    330: usage()
                    331: {
                    332:        (void)fprintf(stderr,
1.2       deraadt   333: "usage: xargs [-0] [-t] [-n number [-x]] [-s size] [utility [argument ...]]\n");
1.1       deraadt   334:        exit(1);
                    335: }