Annotation of src/usr.bin/xargs/xargs.c, Revision 1.11
1.11 ! millert 1: /* $OpenBSD: xargs.c,v 1.10 2002/02/16 21:27:59 millert 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.11 ! millert 50: static char rcsid[] = "$OpenBSD: xargs.c,v 1.10 2002/02/16 21:27:59 millert 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:
1.10 millert 69: void run(char **);
70: void usage(void);
1.1 deraadt 71:
72: int
73: main(argc, argv)
74: int argc;
75: char **argv;
76: {
1.9 mpech 77: int ch;
78: char *p, *bbp, *ebp, **bxp, **exp, **xp;
1.1 deraadt 79: int cnt, indouble, insingle, nargs, nflag, nline, xflag;
80: char **av, *argp;
1.8 deraadt 81: int arg_max;
1.1 deraadt 82:
83: setlocale(LC_ALL, "");
84:
85: /*
86: * POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that
87: * caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given
88: * that the smallest argument is 2 bytes in length, this means that
89: * the number of arguments is limited to:
90: *
91: * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
92: *
93: * We arbitrarily limit the number of arguments to 5000. This is
94: * allowed by POSIX.2 as long as the resulting minimum exec line is
95: * at least LINE_MAX. Realloc'ing as necessary is possible, but
96: * probably not worthwhile.
97: */
98: nargs = 5000;
1.8 deraadt 99: if ((arg_max = sysconf(_SC_ARG_MAX)) == -1)
100: errx(1, "sysconf(_SC_ARG_MAX) failed");
101: nline = arg_max - 4 * 1024;
1.1 deraadt 102: nflag = xflag = 0;
1.4 millert 103: while ((ch = getopt(argc, argv, "0n:s:tx")) != -1)
1.1 deraadt 104: switch(ch) {
105: case 'n':
106: nflag = 1;
107: if ((nargs = atoi(optarg)) <= 0)
108: errx(1, "illegal argument count");
109: break;
110: case 's':
111: nline = atoi(optarg);
112: break;
113: case 't':
114: tflag = 1;
115: break;
116: case 'x':
117: xflag = 1;
118: break;
1.2 deraadt 119: case '0':
120: zflag = 1;
121: break;
1.1 deraadt 122: case '?':
123: default:
124: usage();
125: }
126: argc -= optind;
127: argv += optind;
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;
1.9 mpech 279: char **p;
1.1 deraadt 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)) {
1.7 deraadt 318: if (WEXITSTATUS(status) == 255) {
319: warnx("%s exited with status 255", argv[0]);
1.1 deraadt 320: exit(124);
1.7 deraadt 321: } else if (WEXITSTATUS(status) != 0) {
1.1 deraadt 322: rval = 123;
323: }
1.7 deraadt 324: } else if (WIFSIGNALED(status)) {
1.6 deraadt 325: if (WTERMSIG(status) != SIGPIPE) {
326: if (WTERMSIG(status) < NSIG)
1.7 deraadt 327: warnx("%s terminated by SIG%s", argv[0],
1.6 deraadt 328: sys_signame[WTERMSIG(status)]);
329: else
1.7 deraadt 330: warnx("%s terminated by signal %d", argv[0],
1.6 deraadt 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: }