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: }