xargs.c revision 1.16 1 1.16 christos /* $NetBSD: xargs.c,v 1.16 2007/04/18 15:56:07 christos Exp $ */
2 1.7 jtc
3 1.1 cgd /*-
4 1.7 jtc * Copyright (c) 1990, 1993
5 1.7 jtc * The Regents of the University of California. All rights reserved.
6 1.1 cgd *
7 1.1 cgd * This code is derived from software contributed to Berkeley by
8 1.1 cgd * John B. Roll Jr.
9 1.1 cgd *
10 1.1 cgd * Redistribution and use in source and binary forms, with or without
11 1.1 cgd * modification, are permitted provided that the following conditions
12 1.1 cgd * are met:
13 1.1 cgd * 1. Redistributions of source code must retain the above copyright
14 1.1 cgd * notice, this list of conditions and the following disclaimer.
15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 cgd * notice, this list of conditions and the following disclaimer in the
17 1.1 cgd * documentation and/or other materials provided with the distribution.
18 1.14 agc * 3. Neither the name of the University nor the names of its contributors
19 1.1 cgd * may be used to endorse or promote products derived from this software
20 1.1 cgd * without specific prior written permission.
21 1.1 cgd *
22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 1.1 cgd * SUCH DAMAGE.
33 1.16 christos *
34 1.16 christos * $xMach: xargs.c,v 1.6 2002/02/23 05:27:47 tim Exp $
35 1.1 cgd */
36 1.1 cgd
37 1.9 lukem #include <sys/cdefs.h>
38 1.1 cgd #ifndef lint
39 1.9 lukem __COPYRIGHT("@(#) Copyright (c) 1990, 1993\n\
40 1.9 lukem The Regents of the University of California. All rights reserved.\n");
41 1.7 jtc #if 0
42 1.7 jtc static char sccsid[] = "@(#)xargs.c 8.1 (Berkeley) 6/6/93";
43 1.16 christos __FBSDID("$FreeBSD: src/usr.bin/xargs/xargs.c,v 1.62 2006/01/01 22:59:54 jmallett Exp $");
44 1.7 jtc #endif
45 1.16 christos __RCSID("$NetBSD: xargs.c,v 1.16 2007/04/18 15:56:07 christos Exp $");
46 1.1 cgd #endif /* not lint */
47 1.1 cgd
48 1.16 christos #include <sys/param.h>
49 1.1 cgd #include <sys/wait.h>
50 1.16 christos
51 1.12 kleink #include <err.h>
52 1.1 cgd #include <errno.h>
53 1.16 christos #include <fcntl.h>
54 1.12 kleink #include <langinfo.h>
55 1.12 kleink #include <locale.h>
56 1.12 kleink #include <paths.h>
57 1.12 kleink #include <regex.h>
58 1.1 cgd #include <stdio.h>
59 1.1 cgd #include <stdlib.h>
60 1.1 cgd #include <string.h>
61 1.16 christos #include <signal.h>
62 1.1 cgd #include <unistd.h>
63 1.16 christos
64 1.1 cgd #include "pathnames.h"
65 1.1 cgd
66 1.16 christos static void parse_input(int, char *[]);
67 1.16 christos static void prerun(int, char *[]);
68 1.16 christos static int prompt(void);
69 1.16 christos static void run(char **);
70 1.16 christos static void usage(void) __attribute__((__noreturn__));
71 1.16 christos void strnsubst(char **, const char *, const char *, size_t);
72 1.16 christos static void waitchildren(const char *, int);
73 1.16 christos
74 1.16 christos static char echo[] = _PATH_ECHO;
75 1.16 christos static char **av, **bxp, **ep, **endxp, **xp;
76 1.16 christos static char *argp, *bbp, *ebp, *inpline, *p, *replstr;
77 1.16 christos static const char *eofstr;
78 1.16 christos static int count, insingle, indouble, oflag, pflag, tflag, Rflag, rval, zflag;
79 1.16 christos static int cnt, Iflag, jfound, Lflag, Sflag, wasquoted, xflag;
80 1.16 christos static int curprocs, maxprocs;
81 1.16 christos
82 1.16 christos static volatile int childerr;
83 1.16 christos
84 1.16 christos extern char **environ;
85 1.1 cgd
86 1.5 jtc int
87 1.16 christos main(int argc, char *argv[])
88 1.1 cgd {
89 1.15 joerg long arg_max;
90 1.16 christos int ch, Jflag, nargs, nflag, nline;
91 1.16 christos size_t linelen;
92 1.16 christos char *endptr;
93 1.16 christos
94 1.16 christos setprogname(argv[0]);
95 1.16 christos
96 1.16 christos inpline = replstr = NULL;
97 1.16 christos ep = environ;
98 1.16 christos eofstr = "";
99 1.16 christos Jflag = nflag = 0;
100 1.6 jtc
101 1.16 christos (void)setlocale(LC_ALL, "");
102 1.1 cgd
103 1.1 cgd /*
104 1.1 cgd * POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that
105 1.1 cgd * caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given
106 1.1 cgd * that the smallest argument is 2 bytes in length, this means that
107 1.1 cgd * the number of arguments is limited to:
108 1.1 cgd *
109 1.1 cgd * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
110 1.1 cgd *
111 1.1 cgd * We arbitrarily limit the number of arguments to 5000. This is
112 1.1 cgd * allowed by POSIX.2 as long as the resulting minimum exec line is
113 1.1 cgd * at least LINE_MAX. Realloc'ing as necessary is possible, but
114 1.1 cgd * probably not worthwhile.
115 1.1 cgd */
116 1.1 cgd nargs = 5000;
117 1.15 joerg if ((arg_max = sysconf(_SC_ARG_MAX)) == -1)
118 1.15 joerg errx(1, "sysconf(_SC_ARG_MAX) failed");
119 1.15 joerg nline = arg_max - 4 * 1024;
120 1.16 christos while (*ep != NULL) {
121 1.16 christos /* 1 byte for each '\0' */
122 1.16 christos nline -= strlen(*ep++) + 1 + sizeof(*ep);
123 1.16 christos }
124 1.16 christos maxprocs = 1;
125 1.16 christos while ((ch = getopt(argc, argv, "0E:I:J:L:n:oP:pR:S:s:rtx")) != -1)
126 1.16 christos switch (ch) {
127 1.16 christos case 'E':
128 1.16 christos eofstr = optarg;
129 1.16 christos break;
130 1.16 christos case 'I':
131 1.16 christos Jflag = 0;
132 1.16 christos Iflag = 1;
133 1.16 christos Lflag = 1;
134 1.16 christos replstr = optarg;
135 1.16 christos break;
136 1.16 christos case 'J':
137 1.16 christos Iflag = 0;
138 1.16 christos Jflag = 1;
139 1.16 christos replstr = optarg;
140 1.16 christos break;
141 1.16 christos case 'L':
142 1.16 christos Lflag = atoi(optarg);
143 1.8 lukem break;
144 1.1 cgd case 'n':
145 1.1 cgd nflag = 1;
146 1.1 cgd if ((nargs = atoi(optarg)) <= 0)
147 1.7 jtc errx(1, "illegal argument count");
148 1.1 cgd break;
149 1.16 christos case 'o':
150 1.16 christos oflag = 1;
151 1.16 christos break;
152 1.16 christos case 'P':
153 1.16 christos if ((maxprocs = atoi(optarg)) <= 0)
154 1.16 christos errx(1, "max. processes must be >0");
155 1.16 christos break;
156 1.12 kleink case 'p':
157 1.16 christos pflag = 1;
158 1.16 christos break;
159 1.16 christos case 'R':
160 1.16 christos Rflag = strtol(optarg, &endptr, 10);
161 1.16 christos if (*endptr != '\0')
162 1.16 christos errx(1, "replacements must be a number");
163 1.16 christos break;
164 1.16 christos case 'r':
165 1.16 christos /* GNU compatibility */
166 1.16 christos break;
167 1.16 christos case 'S':
168 1.16 christos Sflag = strtoul(optarg, &endptr, 10);
169 1.16 christos if (*endptr != '\0')
170 1.16 christos errx(1, "replsize must be a number");
171 1.12 kleink break;
172 1.1 cgd case 's':
173 1.1 cgd nline = atoi(optarg);
174 1.1 cgd break;
175 1.1 cgd case 't':
176 1.1 cgd tflag = 1;
177 1.1 cgd break;
178 1.1 cgd case 'x':
179 1.1 cgd xflag = 1;
180 1.1 cgd break;
181 1.16 christos case '0':
182 1.16 christos zflag = 1;
183 1.16 christos break;
184 1.1 cgd case '?':
185 1.1 cgd default:
186 1.1 cgd usage();
187 1.1 cgd }
188 1.1 cgd argc -= optind;
189 1.1 cgd argv += optind;
190 1.1 cgd
191 1.16 christos if (!Iflag && Rflag)
192 1.16 christos usage();
193 1.16 christos if (!Iflag && Sflag)
194 1.16 christos usage();
195 1.16 christos if (Iflag && !Rflag)
196 1.16 christos Rflag = 5;
197 1.16 christos if (Iflag && !Sflag)
198 1.16 christos Sflag = 255;
199 1.1 cgd if (xflag && !nflag)
200 1.1 cgd usage();
201 1.16 christos if (Iflag || Lflag)
202 1.16 christos xflag = 1;
203 1.16 christos if (replstr != NULL && *replstr == '\0')
204 1.16 christos errx(1, "replstr may not be empty");
205 1.1 cgd
206 1.1 cgd /*
207 1.1 cgd * Allocate pointers for the utility name, the utility arguments,
208 1.1 cgd * the maximum arguments to be read from stdin and the trailing
209 1.1 cgd * NULL.
210 1.1 cgd */
211 1.16 christos linelen = 1 + argc + nargs + 1;
212 1.16 christos if ((av = bxp = malloc(linelen * sizeof(char **))) == NULL)
213 1.16 christos errx(1, "malloc failed");
214 1.1 cgd
215 1.1 cgd /*
216 1.1 cgd * Use the user's name for the utility as argv[0], just like the
217 1.1 cgd * shell. Echo is the default. Set up pointers for the user's
218 1.1 cgd * arguments.
219 1.1 cgd */
220 1.16 christos if (*argv == NULL)
221 1.16 christos cnt = strlen(*bxp++ = echo);
222 1.1 cgd else {
223 1.1 cgd do {
224 1.16 christos if (Jflag && strcmp(*argv, replstr) == 0) {
225 1.16 christos char **avj;
226 1.16 christos jfound = 1;
227 1.16 christos argv++;
228 1.16 christos for (avj = argv; *avj; avj++)
229 1.16 christos cnt += strlen(*avj) + 1;
230 1.16 christos break;
231 1.16 christos }
232 1.1 cgd cnt += strlen(*bxp++ = *argv) + 1;
233 1.16 christos } while (*++argv != NULL);
234 1.1 cgd }
235 1.1 cgd
236 1.1 cgd /*
237 1.1 cgd * Set up begin/end/traversing pointers into the array. The -n
238 1.1 cgd * count doesn't include the trailing NULL pointer, so the malloc
239 1.1 cgd * added in an extra slot.
240 1.1 cgd */
241 1.16 christos endxp = (xp = bxp) + nargs;
242 1.1 cgd
243 1.1 cgd /*
244 1.1 cgd * Allocate buffer space for the arguments read from stdin and the
245 1.1 cgd * trailing NULL. Buffer space is defined as the default or specified
246 1.1 cgd * space, minus the length of the utility name and arguments. Set up
247 1.1 cgd * begin/end/traversing pointers into the array. The -s count does
248 1.1 cgd * include the trailing NULL, so the malloc didn't add in an extra
249 1.1 cgd * slot.
250 1.1 cgd */
251 1.1 cgd nline -= cnt;
252 1.1 cgd if (nline <= 0)
253 1.7 jtc errx(1, "insufficient space for command");
254 1.1 cgd
255 1.16 christos if ((bbp = malloc((size_t)(nline + 1))) == NULL)
256 1.16 christos errx(1, "malloc failed");
257 1.1 cgd ebp = (argp = p = bbp) + nline - 1;
258 1.16 christos for (;;)
259 1.16 christos parse_input(argc, argv);
260 1.16 christos }
261 1.1 cgd
262 1.16 christos static void
263 1.16 christos parse_input(int argc, char *argv[])
264 1.16 christos {
265 1.16 christos int ch, foundeof;
266 1.16 christos char **avj;
267 1.12 kleink
268 1.16 christos foundeof = 0;
269 1.12 kleink
270 1.16 christos switch (ch = getchar()) {
271 1.16 christos case EOF:
272 1.16 christos /* No arguments since last exec. */
273 1.16 christos if (p == bbp) {
274 1.16 christos waitchildren(*argv, 1);
275 1.16 christos exit(rval);
276 1.12 kleink }
277 1.16 christos goto arg1;
278 1.16 christos case ' ':
279 1.16 christos case '\t':
280 1.16 christos /* Quotes escape tabs and spaces. */
281 1.16 christos if (insingle || indouble || zflag)
282 1.16 christos goto addch;
283 1.16 christos goto arg2;
284 1.16 christos case '\0':
285 1.16 christos if (zflag) {
286 1.16 christos /*
287 1.16 christos * Increment 'count', so that nulls will be treated
288 1.16 christos * as end-of-line, as well as end-of-argument. This
289 1.16 christos * is needed so -0 works properly with -I and -L.
290 1.16 christos */
291 1.16 christos count++;
292 1.1 cgd goto arg2;
293 1.16 christos }
294 1.16 christos goto addch;
295 1.16 christos case '\n':
296 1.16 christos if (zflag)
297 1.8 lukem goto addch;
298 1.16 christos count++; /* Indicate end-of-line (used by -L) */
299 1.1 cgd
300 1.16 christos /* Quotes do not escape newlines. */
301 1.16 christos arg1: if (insingle || indouble)
302 1.16 christos errx(1, "unterminated quote");
303 1.16 christos arg2:
304 1.16 christos foundeof = *eofstr != '\0' &&
305 1.16 christos strncmp(argp, eofstr, (size_t)(p - argp)) == 0;
306 1.16 christos
307 1.16 christos /* Do not make empty args unless they are quoted */
308 1.16 christos if ((argp != p || wasquoted) && !foundeof) {
309 1.16 christos *p++ = '\0';
310 1.1 cgd *xp++ = argp;
311 1.16 christos if (Iflag) {
312 1.16 christos size_t curlen;
313 1.1 cgd
314 1.16 christos if (inpline == NULL)
315 1.16 christos curlen = 0;
316 1.16 christos else {
317 1.16 christos /*
318 1.16 christos * If this string is not zero
319 1.16 christos * length, append a space for
320 1.16 christos * separation before the next
321 1.16 christos * argument.
322 1.16 christos */
323 1.16 christos if ((curlen = strlen(inpline)) != 0)
324 1.16 christos (void)strcat(inpline, " ");
325 1.16 christos }
326 1.16 christos curlen++;
327 1.16 christos /*
328 1.16 christos * Allocate enough to hold what we will
329 1.16 christos * be holding in a second, and to append
330 1.16 christos * a space next time through, if we have
331 1.16 christos * to.
332 1.16 christos */
333 1.16 christos inpline = realloc(inpline, curlen + 2 +
334 1.16 christos strlen(argp));
335 1.16 christos if (inpline == NULL)
336 1.16 christos errx(1, "realloc failed");
337 1.16 christos if (curlen == 1)
338 1.16 christos (void)strcpy(inpline, argp);
339 1.16 christos else
340 1.16 christos (void)strcat(inpline, argp);
341 1.1 cgd }
342 1.16 christos }
343 1.1 cgd
344 1.16 christos /*
345 1.16 christos * If max'd out on args or buffer, or reached EOF,
346 1.16 christos * run the command. If xflag and max'd out on buffer
347 1.16 christos * but not on args, object. Having reached the limit
348 1.16 christos * of input lines, as specified by -L is the same as
349 1.16 christos * maxing out on arguments.
350 1.16 christos */
351 1.16 christos if (xp == endxp || p > ebp || ch == EOF ||
352 1.16 christos (Lflag <= count && xflag) || foundeof) {
353 1.16 christos if (xflag && xp != endxp && p > ebp)
354 1.7 jtc errx(1, "insufficient space for arguments");
355 1.16 christos if (jfound) {
356 1.16 christos for (avj = argv; *avj; avj++)
357 1.16 christos *xp++ = *avj;
358 1.16 christos }
359 1.16 christos prerun(argc, av);
360 1.16 christos if (ch == EOF || foundeof) {
361 1.16 christos waitchildren(*argv, 1);
362 1.16 christos exit(rval);
363 1.16 christos }
364 1.16 christos p = bbp;
365 1.1 cgd xp = bxp;
366 1.16 christos count = 0;
367 1.16 christos }
368 1.16 christos argp = p;
369 1.16 christos wasquoted = 0;
370 1.16 christos break;
371 1.16 christos case '\'':
372 1.16 christos if (indouble || zflag)
373 1.16 christos goto addch;
374 1.16 christos insingle = !insingle;
375 1.16 christos wasquoted = 1;
376 1.16 christos break;
377 1.16 christos case '"':
378 1.16 christos if (insingle || zflag)
379 1.16 christos goto addch;
380 1.16 christos indouble = !indouble;
381 1.16 christos wasquoted = 1;
382 1.16 christos break;
383 1.16 christos case '\\':
384 1.16 christos if (zflag)
385 1.16 christos goto addch;
386 1.16 christos /* Backslash escapes anything, is escaped by quotes. */
387 1.16 christos if (!insingle && !indouble && (ch = getchar()) == EOF)
388 1.16 christos errx(1, "backslash at EOF");
389 1.16 christos /* FALLTHROUGH */
390 1.16 christos default:
391 1.16 christos addch: if (p < ebp) {
392 1.1 cgd *p++ = ch;
393 1.1 cgd break;
394 1.1 cgd }
395 1.16 christos
396 1.16 christos /* If only one argument, not enough buffer space. */
397 1.16 christos if (bxp == xp)
398 1.16 christos errx(1, "insufficient space for argument");
399 1.16 christos /* Didn't hit argument limit, so if xflag object. */
400 1.16 christos if (xflag)
401 1.16 christos errx(1, "insufficient space for arguments");
402 1.16 christos
403 1.16 christos if (jfound) {
404 1.16 christos for (avj = argv; *avj; avj++)
405 1.16 christos *xp++ = *avj;
406 1.16 christos }
407 1.16 christos prerun(argc, av);
408 1.16 christos xp = bxp;
409 1.16 christos cnt = ebp - argp;
410 1.16 christos (void)memcpy(bbp, argp, (size_t)cnt);
411 1.16 christos p = (argp = bbp) + cnt;
412 1.16 christos *p++ = ch;
413 1.16 christos break;
414 1.16 christos }
415 1.1 cgd }
416 1.1 cgd
417 1.16 christos /*
418 1.16 christos * Do things necessary before run()'ing, such as -I substitution,
419 1.16 christos * and then call run().
420 1.16 christos */
421 1.12 kleink static void
422 1.16 christos prerun(int argc, char *argv[])
423 1.1 cgd {
424 1.16 christos char **tmp, **tmp2, **avj;
425 1.16 christos int repls;
426 1.16 christos
427 1.16 christos repls = Rflag;
428 1.16 christos
429 1.16 christos if (argc == 0 || repls == 0) {
430 1.16 christos *xp = NULL;
431 1.16 christos run(argv);
432 1.16 christos return;
433 1.16 christos }
434 1.16 christos
435 1.16 christos avj = argv;
436 1.16 christos
437 1.16 christos /*
438 1.16 christos * Allocate memory to hold the argument list, and
439 1.16 christos * a NULL at the tail.
440 1.16 christos */
441 1.16 christos tmp = malloc((argc + 1) * sizeof(char**));
442 1.16 christos if (tmp == NULL)
443 1.16 christos errx(1, "malloc failed");
444 1.16 christos tmp2 = tmp;
445 1.16 christos
446 1.16 christos /*
447 1.16 christos * Save the first argument and iterate over it, we
448 1.16 christos * cannot do strnsubst() to it.
449 1.16 christos */
450 1.16 christos if ((*tmp++ = strdup(*avj++)) == NULL)
451 1.16 christos errx(1, "strdup failed");
452 1.16 christos
453 1.16 christos /*
454 1.16 christos * For each argument to utility, if we have not used up
455 1.16 christos * the number of replacements we are allowed to do, and
456 1.16 christos * if the argument contains at least one occurrence of
457 1.16 christos * replstr, call strnsubst(), else just save the string.
458 1.16 christos * Iterations over elements of avj and tmp are done
459 1.16 christos * where appropriate.
460 1.16 christos */
461 1.16 christos while (--argc) {
462 1.16 christos *tmp = *avj++;
463 1.16 christos if (repls && strstr(*tmp, replstr) != NULL) {
464 1.16 christos strnsubst(tmp++, replstr, inpline, (size_t)Sflag);
465 1.16 christos if (repls > 0)
466 1.16 christos repls--;
467 1.16 christos } else {
468 1.16 christos if ((*tmp = strdup(*tmp)) == NULL)
469 1.16 christos errx(1, "strdup failed");
470 1.16 christos tmp++;
471 1.16 christos }
472 1.16 christos }
473 1.16 christos
474 1.16 christos /*
475 1.16 christos * Run it.
476 1.16 christos */
477 1.16 christos *tmp = NULL;
478 1.16 christos run(tmp2);
479 1.16 christos
480 1.16 christos /*
481 1.16 christos * Walk from the tail to the head, free along the way.
482 1.16 christos */
483 1.16 christos for (; tmp2 != tmp; tmp--)
484 1.16 christos free(*tmp);
485 1.16 christos /*
486 1.16 christos * Now free the list itself.
487 1.16 christos */
488 1.16 christos free(tmp2);
489 1.16 christos
490 1.16 christos /*
491 1.16 christos * Free the input line buffer, if we have one.
492 1.16 christos */
493 1.16 christos if (inpline != NULL) {
494 1.16 christos free(inpline);
495 1.16 christos inpline = NULL;
496 1.16 christos }
497 1.16 christos }
498 1.16 christos
499 1.16 christos static void
500 1.16 christos run(char **argv)
501 1.16 christos {
502 1.16 christos int fd;
503 1.16 christos char **avec;
504 1.1 cgd
505 1.16 christos /*
506 1.16 christos * If the user wants to be notified of each command before it is
507 1.16 christos * executed, notify them. If they want the notification to be
508 1.16 christos * followed by a prompt, then prompt them.
509 1.16 christos */
510 1.16 christos if (tflag || pflag) {
511 1.1 cgd (void)fprintf(stderr, "%s", *argv);
512 1.16 christos for (avec = argv + 1; *avec != NULL; ++avec)
513 1.16 christos (void)fprintf(stderr, " %s", *avec);
514 1.16 christos /*
515 1.16 christos * If the user has asked to be prompted, do so.
516 1.16 christos */
517 1.16 christos if (pflag)
518 1.16 christos /*
519 1.16 christos * If they asked not to exec, return without execution
520 1.16 christos * but if they asked to, go to the execution. If we
521 1.16 christos * could not open their tty, break the switch and drop
522 1.16 christos * back to -t behaviour.
523 1.16 christos */
524 1.16 christos switch (prompt()) {
525 1.16 christos case 0:
526 1.12 kleink return;
527 1.16 christos case 1:
528 1.16 christos goto exec;
529 1.16 christos case 2:
530 1.16 christos break;
531 1.12 kleink }
532 1.16 christos (void)fprintf(stderr, "\n");
533 1.16 christos (void)fflush(stderr);
534 1.1 cgd }
535 1.16 christos exec:
536 1.16 christos childerr = 0;
537 1.16 christos switch (vfork()) {
538 1.1 cgd case -1:
539 1.7 jtc err(1, "vfork");
540 1.16 christos /*NOTREACHED*/
541 1.1 cgd case 0:
542 1.16 christos if (oflag) {
543 1.16 christos if ((fd = open(_PATH_TTY, O_RDONLY)) == -1)
544 1.16 christos err(1, "can't open /dev/tty");
545 1.16 christos } else {
546 1.16 christos fd = open(_PATH_DEVNULL, O_RDONLY);
547 1.16 christos }
548 1.16 christos if (fd > STDIN_FILENO) {
549 1.16 christos if (dup2(fd, STDIN_FILENO) != 0)
550 1.16 christos err(1, "can't dup2 to stdin");
551 1.16 christos (void)close(fd);
552 1.16 christos }
553 1.16 christos (void)execvp(argv[0], argv);
554 1.16 christos childerr = errno;
555 1.1 cgd _exit(1);
556 1.1 cgd }
557 1.16 christos curprocs++;
558 1.16 christos waitchildren(*argv, 0);
559 1.16 christos }
560 1.16 christos
561 1.16 christos static void
562 1.16 christos waitchildren(const char *name, int waitall)
563 1.16 christos {
564 1.16 christos pid_t pid;
565 1.16 christos int status;
566 1.16 christos
567 1.16 christos while ((pid = waitpid(-1, &status, !waitall && curprocs < maxprocs ?
568 1.16 christos WNOHANG : 0)) > 0) {
569 1.16 christos curprocs--;
570 1.16 christos /* If we couldn't invoke the utility, exit. */
571 1.16 christos if (childerr != 0) {
572 1.16 christos errno = childerr;
573 1.16 christos err(errno == ENOENT ? 127 : 126, "%s", name);
574 1.16 christos }
575 1.16 christos /*
576 1.16 christos * According to POSIX, we have to exit if the utility exits
577 1.16 christos * with a 255 status, or is interrupted by a signal. xargs
578 1.16 christos * is allowed to return any exit status between 1 and 125
579 1.16 christos * in these cases, but we'll use 124 and 125, the same
580 1.16 christos * values used by GNU xargs.
581 1.16 christos */
582 1.16 christos if (WIFEXITED(status)) {
583 1.16 christos if (WEXITSTATUS (status) == 255) {
584 1.16 christos warnx ("%s exited with status 255", name);
585 1.16 christos exit(124);
586 1.16 christos } else if (WEXITSTATUS (status) != 0) {
587 1.16 christos rval = 123;
588 1.16 christos }
589 1.16 christos } else if (WIFSIGNALED (status)) {
590 1.16 christos if (WTERMSIG(status) < NSIG) {
591 1.16 christos warnx("%s terminated by SIG%s", name,
592 1.16 christos sys_signame[WTERMSIG(status)]);
593 1.16 christos } else {
594 1.16 christos warnx("%s terminated by signal %d", name,
595 1.16 christos WTERMSIG(status));
596 1.16 christos }
597 1.16 christos exit(125);
598 1.10 fair }
599 1.3 jtc }
600 1.16 christos if (pid == -1 && errno != ECHILD)
601 1.16 christos err(1, "wait3");
602 1.16 christos }
603 1.16 christos
604 1.16 christos /*
605 1.16 christos * Prompt the user about running a command.
606 1.16 christos */
607 1.16 christos static int
608 1.16 christos prompt(void)
609 1.16 christos {
610 1.16 christos regex_t cre;
611 1.16 christos size_t rsize;
612 1.16 christos int match;
613 1.16 christos char *response;
614 1.16 christos FILE *ttyfp;
615 1.16 christos
616 1.16 christos if ((ttyfp = fopen(_PATH_TTY, "r")) == NULL)
617 1.16 christos return (2); /* Indicate that the TTY failed to open. */
618 1.16 christos (void)fprintf(stderr, "?...");
619 1.16 christos (void)fflush(stderr);
620 1.16 christos if ((response = fgetln(ttyfp, &rsize)) == NULL ||
621 1.16 christos regcomp(&cre, nl_langinfo(YESEXPR), REG_BASIC) != 0) {
622 1.16 christos (void)fclose(ttyfp);
623 1.16 christos return (0);
624 1.16 christos }
625 1.16 christos response[rsize - 1] = '\0';
626 1.16 christos match = regexec(&cre, response, 0, NULL, 0);
627 1.16 christos (void)fclose(ttyfp);
628 1.16 christos regfree(&cre);
629 1.16 christos return (match == 0);
630 1.1 cgd }
631 1.1 cgd
632 1.12 kleink static void
633 1.16 christos usage(void)
634 1.1 cgd {
635 1.1 cgd (void)fprintf(stderr,
636 1.16 christos "Usage: %s [-0opt] [-E eofstr] [-I replstr [-R replacements] [-S replsize]]\n"
637 1.16 christos " [-J replstr] [-L number] [-n number [-x]] [-P maxprocs]\n"
638 1.16 christos " [-s size] [utility [argument ...]]\n", getprogname());
639 1.1 cgd exit(1);
640 1.1 cgd }
641