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