xargs.c revision 1.20 1 1.20 plunky /* $NetBSD: xargs.c,v 1.20 2010/12/17 11:32:57 plunky 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.19 lukem __COPYRIGHT("@(#) Copyright (c) 1990, 1993\
40 1.19 lukem The Regents of the University of California. All rights reserved.");
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.20 plunky __RCSID("$NetBSD: xargs.c,v 1.20 2010/12/17 11:32:57 plunky 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.17 perry static void usage(void) __dead;
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.18 apb * SUSv3 says of the exec family of functions:
105 1.18 apb * The number of bytes available for the new process'
106 1.18 apb * combined argument and environment lists is {ARG_MAX}. It
107 1.18 apb * is implementation-defined whether null terminators,
108 1.18 apb * pointers, and/or any alignment bytes are included in this
109 1.18 apb * total.
110 1.1 cgd *
111 1.18 apb * SUSv3 says of xargs:
112 1.18 apb * ... the combined argument and environment lists ...
113 1.18 apb * shall not exceed {ARG_MAX}-2048.
114 1.18 apb *
115 1.18 apb * To be conservative, we use ARG_MAX - 4K, and we do include
116 1.18 apb * nul terminators and pointers in the calculation.
117 1.18 apb *
118 1.18 apb * Given that the smallest argument is 2 bytes in length, this
119 1.18 apb * means that the number of arguments is limited to:
120 1.18 apb *
121 1.18 apb * (ARG_MAX - 4K - LENGTH(env + utility + arguments)) / 2.
122 1.1 cgd *
123 1.1 cgd * We arbitrarily limit the number of arguments to 5000. This is
124 1.1 cgd * allowed by POSIX.2 as long as the resulting minimum exec line is
125 1.1 cgd * at least LINE_MAX. Realloc'ing as necessary is possible, but
126 1.1 cgd * probably not worthwhile.
127 1.1 cgd */
128 1.1 cgd nargs = 5000;
129 1.15 joerg if ((arg_max = sysconf(_SC_ARG_MAX)) == -1)
130 1.15 joerg errx(1, "sysconf(_SC_ARG_MAX) failed");
131 1.15 joerg nline = arg_max - 4 * 1024;
132 1.16 christos while (*ep != NULL) {
133 1.16 christos /* 1 byte for each '\0' */
134 1.16 christos nline -= strlen(*ep++) + 1 + sizeof(*ep);
135 1.16 christos }
136 1.16 christos maxprocs = 1;
137 1.16 christos while ((ch = getopt(argc, argv, "0E:I:J:L:n:oP:pR:S:s:rtx")) != -1)
138 1.16 christos switch (ch) {
139 1.16 christos case 'E':
140 1.16 christos eofstr = optarg;
141 1.16 christos break;
142 1.16 christos case 'I':
143 1.16 christos Jflag = 0;
144 1.16 christos Iflag = 1;
145 1.16 christos Lflag = 1;
146 1.16 christos replstr = optarg;
147 1.16 christos break;
148 1.16 christos case 'J':
149 1.16 christos Iflag = 0;
150 1.16 christos Jflag = 1;
151 1.16 christos replstr = optarg;
152 1.16 christos break;
153 1.16 christos case 'L':
154 1.16 christos Lflag = atoi(optarg);
155 1.8 lukem break;
156 1.1 cgd case 'n':
157 1.1 cgd nflag = 1;
158 1.1 cgd if ((nargs = atoi(optarg)) <= 0)
159 1.7 jtc errx(1, "illegal argument count");
160 1.1 cgd break;
161 1.16 christos case 'o':
162 1.16 christos oflag = 1;
163 1.16 christos break;
164 1.16 christos case 'P':
165 1.16 christos if ((maxprocs = atoi(optarg)) <= 0)
166 1.16 christos errx(1, "max. processes must be >0");
167 1.16 christos break;
168 1.12 kleink case 'p':
169 1.16 christos pflag = 1;
170 1.16 christos break;
171 1.16 christos case 'R':
172 1.16 christos Rflag = strtol(optarg, &endptr, 10);
173 1.16 christos if (*endptr != '\0')
174 1.16 christos errx(1, "replacements must be a number");
175 1.16 christos break;
176 1.16 christos case 'r':
177 1.16 christos /* GNU compatibility */
178 1.16 christos break;
179 1.16 christos case 'S':
180 1.16 christos Sflag = strtoul(optarg, &endptr, 10);
181 1.16 christos if (*endptr != '\0')
182 1.16 christos errx(1, "replsize must be a number");
183 1.12 kleink break;
184 1.1 cgd case 's':
185 1.1 cgd nline = atoi(optarg);
186 1.1 cgd break;
187 1.1 cgd case 't':
188 1.1 cgd tflag = 1;
189 1.1 cgd break;
190 1.1 cgd case 'x':
191 1.1 cgd xflag = 1;
192 1.1 cgd break;
193 1.16 christos case '0':
194 1.16 christos zflag = 1;
195 1.16 christos break;
196 1.1 cgd case '?':
197 1.1 cgd default:
198 1.1 cgd usage();
199 1.1 cgd }
200 1.1 cgd argc -= optind;
201 1.1 cgd argv += optind;
202 1.1 cgd
203 1.16 christos if (!Iflag && Rflag)
204 1.16 christos usage();
205 1.16 christos if (!Iflag && Sflag)
206 1.16 christos usage();
207 1.16 christos if (Iflag && !Rflag)
208 1.16 christos Rflag = 5;
209 1.16 christos if (Iflag && !Sflag)
210 1.16 christos Sflag = 255;
211 1.1 cgd if (xflag && !nflag)
212 1.1 cgd usage();
213 1.16 christos if (Iflag || Lflag)
214 1.16 christos xflag = 1;
215 1.16 christos if (replstr != NULL && *replstr == '\0')
216 1.16 christos errx(1, "replstr may not be empty");
217 1.1 cgd
218 1.1 cgd /*
219 1.1 cgd * Allocate pointers for the utility name, the utility arguments,
220 1.1 cgd * the maximum arguments to be read from stdin and the trailing
221 1.1 cgd * NULL.
222 1.1 cgd */
223 1.16 christos linelen = 1 + argc + nargs + 1;
224 1.16 christos if ((av = bxp = malloc(linelen * sizeof(char **))) == NULL)
225 1.16 christos errx(1, "malloc failed");
226 1.1 cgd
227 1.1 cgd /*
228 1.1 cgd * Use the user's name for the utility as argv[0], just like the
229 1.1 cgd * shell. Echo is the default. Set up pointers for the user's
230 1.1 cgd * arguments.
231 1.1 cgd */
232 1.16 christos if (*argv == NULL)
233 1.16 christos cnt = strlen(*bxp++ = echo);
234 1.1 cgd else {
235 1.1 cgd do {
236 1.16 christos if (Jflag && strcmp(*argv, replstr) == 0) {
237 1.16 christos char **avj;
238 1.16 christos jfound = 1;
239 1.16 christos argv++;
240 1.16 christos for (avj = argv; *avj; avj++)
241 1.16 christos cnt += strlen(*avj) + 1;
242 1.16 christos break;
243 1.16 christos }
244 1.1 cgd cnt += strlen(*bxp++ = *argv) + 1;
245 1.16 christos } while (*++argv != NULL);
246 1.1 cgd }
247 1.1 cgd
248 1.1 cgd /*
249 1.1 cgd * Set up begin/end/traversing pointers into the array. The -n
250 1.1 cgd * count doesn't include the trailing NULL pointer, so the malloc
251 1.1 cgd * added in an extra slot.
252 1.1 cgd */
253 1.16 christos endxp = (xp = bxp) + nargs;
254 1.1 cgd
255 1.1 cgd /*
256 1.1 cgd * Allocate buffer space for the arguments read from stdin and the
257 1.1 cgd * trailing NULL. Buffer space is defined as the default or specified
258 1.1 cgd * space, minus the length of the utility name and arguments. Set up
259 1.1 cgd * begin/end/traversing pointers into the array. The -s count does
260 1.1 cgd * include the trailing NULL, so the malloc didn't add in an extra
261 1.1 cgd * slot.
262 1.1 cgd */
263 1.1 cgd nline -= cnt;
264 1.1 cgd if (nline <= 0)
265 1.7 jtc errx(1, "insufficient space for command");
266 1.1 cgd
267 1.16 christos if ((bbp = malloc((size_t)(nline + 1))) == NULL)
268 1.16 christos errx(1, "malloc failed");
269 1.1 cgd ebp = (argp = p = bbp) + nline - 1;
270 1.16 christos for (;;)
271 1.16 christos parse_input(argc, argv);
272 1.16 christos }
273 1.1 cgd
274 1.16 christos static void
275 1.16 christos parse_input(int argc, char *argv[])
276 1.16 christos {
277 1.16 christos int ch, foundeof;
278 1.16 christos char **avj;
279 1.12 kleink
280 1.16 christos foundeof = 0;
281 1.12 kleink
282 1.16 christos switch (ch = getchar()) {
283 1.16 christos case EOF:
284 1.16 christos /* No arguments since last exec. */
285 1.16 christos if (p == bbp) {
286 1.16 christos waitchildren(*argv, 1);
287 1.16 christos exit(rval);
288 1.12 kleink }
289 1.16 christos goto arg1;
290 1.16 christos case ' ':
291 1.16 christos case '\t':
292 1.16 christos /* Quotes escape tabs and spaces. */
293 1.16 christos if (insingle || indouble || zflag)
294 1.16 christos goto addch;
295 1.16 christos goto arg2;
296 1.16 christos case '\0':
297 1.16 christos if (zflag) {
298 1.16 christos /*
299 1.16 christos * Increment 'count', so that nulls will be treated
300 1.16 christos * as end-of-line, as well as end-of-argument. This
301 1.16 christos * is needed so -0 works properly with -I and -L.
302 1.16 christos */
303 1.16 christos count++;
304 1.1 cgd goto arg2;
305 1.16 christos }
306 1.16 christos goto addch;
307 1.16 christos case '\n':
308 1.16 christos if (zflag)
309 1.8 lukem goto addch;
310 1.16 christos count++; /* Indicate end-of-line (used by -L) */
311 1.1 cgd
312 1.16 christos /* Quotes do not escape newlines. */
313 1.16 christos arg1: if (insingle || indouble)
314 1.16 christos errx(1, "unterminated quote");
315 1.16 christos arg2:
316 1.16 christos foundeof = *eofstr != '\0' &&
317 1.16 christos strncmp(argp, eofstr, (size_t)(p - argp)) == 0;
318 1.16 christos
319 1.16 christos /* Do not make empty args unless they are quoted */
320 1.16 christos if ((argp != p || wasquoted) && !foundeof) {
321 1.16 christos *p++ = '\0';
322 1.1 cgd *xp++ = argp;
323 1.16 christos if (Iflag) {
324 1.16 christos size_t curlen;
325 1.1 cgd
326 1.16 christos if (inpline == NULL)
327 1.16 christos curlen = 0;
328 1.16 christos else {
329 1.16 christos /*
330 1.16 christos * If this string is not zero
331 1.16 christos * length, append a space for
332 1.16 christos * separation before the next
333 1.16 christos * argument.
334 1.16 christos */
335 1.16 christos if ((curlen = strlen(inpline)) != 0)
336 1.16 christos (void)strcat(inpline, " ");
337 1.16 christos }
338 1.16 christos curlen++;
339 1.16 christos /*
340 1.16 christos * Allocate enough to hold what we will
341 1.16 christos * be holding in a second, and to append
342 1.16 christos * a space next time through, if we have
343 1.16 christos * to.
344 1.16 christos */
345 1.16 christos inpline = realloc(inpline, curlen + 2 +
346 1.16 christos strlen(argp));
347 1.16 christos if (inpline == NULL)
348 1.16 christos errx(1, "realloc failed");
349 1.16 christos if (curlen == 1)
350 1.16 christos (void)strcpy(inpline, argp);
351 1.16 christos else
352 1.16 christos (void)strcat(inpline, argp);
353 1.1 cgd }
354 1.16 christos }
355 1.1 cgd
356 1.16 christos /*
357 1.16 christos * If max'd out on args or buffer, or reached EOF,
358 1.16 christos * run the command. If xflag and max'd out on buffer
359 1.16 christos * but not on args, object. Having reached the limit
360 1.16 christos * of input lines, as specified by -L is the same as
361 1.16 christos * maxing out on arguments.
362 1.16 christos */
363 1.16 christos if (xp == endxp || p > ebp || ch == EOF ||
364 1.16 christos (Lflag <= count && xflag) || foundeof) {
365 1.16 christos if (xflag && xp != endxp && p > ebp)
366 1.7 jtc errx(1, "insufficient space for arguments");
367 1.16 christos if (jfound) {
368 1.16 christos for (avj = argv; *avj; avj++)
369 1.16 christos *xp++ = *avj;
370 1.16 christos }
371 1.16 christos prerun(argc, av);
372 1.16 christos if (ch == EOF || foundeof) {
373 1.16 christos waitchildren(*argv, 1);
374 1.16 christos exit(rval);
375 1.16 christos }
376 1.16 christos p = bbp;
377 1.1 cgd xp = bxp;
378 1.16 christos count = 0;
379 1.16 christos }
380 1.16 christos argp = p;
381 1.16 christos wasquoted = 0;
382 1.16 christos break;
383 1.16 christos case '\'':
384 1.16 christos if (indouble || zflag)
385 1.16 christos goto addch;
386 1.16 christos insingle = !insingle;
387 1.16 christos wasquoted = 1;
388 1.16 christos break;
389 1.16 christos case '"':
390 1.16 christos if (insingle || zflag)
391 1.16 christos goto addch;
392 1.16 christos indouble = !indouble;
393 1.16 christos wasquoted = 1;
394 1.16 christos break;
395 1.16 christos case '\\':
396 1.16 christos if (zflag)
397 1.16 christos goto addch;
398 1.16 christos /* Backslash escapes anything, is escaped by quotes. */
399 1.16 christos if (!insingle && !indouble && (ch = getchar()) == EOF)
400 1.16 christos errx(1, "backslash at EOF");
401 1.16 christos /* FALLTHROUGH */
402 1.16 christos default:
403 1.16 christos addch: if (p < ebp) {
404 1.1 cgd *p++ = ch;
405 1.1 cgd break;
406 1.1 cgd }
407 1.16 christos
408 1.16 christos /* If only one argument, not enough buffer space. */
409 1.16 christos if (bxp == xp)
410 1.16 christos errx(1, "insufficient space for argument");
411 1.16 christos /* Didn't hit argument limit, so if xflag object. */
412 1.16 christos if (xflag)
413 1.16 christos errx(1, "insufficient space for arguments");
414 1.16 christos
415 1.16 christos if (jfound) {
416 1.16 christos for (avj = argv; *avj; avj++)
417 1.16 christos *xp++ = *avj;
418 1.16 christos }
419 1.16 christos prerun(argc, av);
420 1.16 christos xp = bxp;
421 1.16 christos cnt = ebp - argp;
422 1.16 christos (void)memcpy(bbp, argp, (size_t)cnt);
423 1.16 christos p = (argp = bbp) + cnt;
424 1.16 christos *p++ = ch;
425 1.16 christos break;
426 1.16 christos }
427 1.1 cgd }
428 1.1 cgd
429 1.16 christos /*
430 1.16 christos * Do things necessary before run()'ing, such as -I substitution,
431 1.16 christos * and then call run().
432 1.16 christos */
433 1.12 kleink static void
434 1.16 christos prerun(int argc, char *argv[])
435 1.1 cgd {
436 1.16 christos char **tmp, **tmp2, **avj;
437 1.16 christos int repls;
438 1.16 christos
439 1.16 christos repls = Rflag;
440 1.16 christos
441 1.16 christos if (argc == 0 || repls == 0) {
442 1.16 christos *xp = NULL;
443 1.16 christos run(argv);
444 1.16 christos return;
445 1.16 christos }
446 1.16 christos
447 1.16 christos avj = argv;
448 1.16 christos
449 1.16 christos /*
450 1.16 christos * Allocate memory to hold the argument list, and
451 1.16 christos * a NULL at the tail.
452 1.16 christos */
453 1.16 christos tmp = malloc((argc + 1) * sizeof(char**));
454 1.16 christos if (tmp == NULL)
455 1.16 christos errx(1, "malloc failed");
456 1.16 christos tmp2 = tmp;
457 1.16 christos
458 1.16 christos /*
459 1.16 christos * Save the first argument and iterate over it, we
460 1.16 christos * cannot do strnsubst() to it.
461 1.16 christos */
462 1.16 christos if ((*tmp++ = strdup(*avj++)) == NULL)
463 1.16 christos errx(1, "strdup failed");
464 1.16 christos
465 1.16 christos /*
466 1.16 christos * For each argument to utility, if we have not used up
467 1.16 christos * the number of replacements we are allowed to do, and
468 1.16 christos * if the argument contains at least one occurrence of
469 1.16 christos * replstr, call strnsubst(), else just save the string.
470 1.16 christos * Iterations over elements of avj and tmp are done
471 1.16 christos * where appropriate.
472 1.16 christos */
473 1.16 christos while (--argc) {
474 1.16 christos *tmp = *avj++;
475 1.16 christos if (repls && strstr(*tmp, replstr) != NULL) {
476 1.16 christos strnsubst(tmp++, replstr, inpline, (size_t)Sflag);
477 1.16 christos if (repls > 0)
478 1.16 christos repls--;
479 1.16 christos } else {
480 1.16 christos if ((*tmp = strdup(*tmp)) == NULL)
481 1.16 christos errx(1, "strdup failed");
482 1.16 christos tmp++;
483 1.16 christos }
484 1.16 christos }
485 1.16 christos
486 1.16 christos /*
487 1.16 christos * Run it.
488 1.16 christos */
489 1.16 christos *tmp = NULL;
490 1.16 christos run(tmp2);
491 1.16 christos
492 1.16 christos /*
493 1.16 christos * Walk from the tail to the head, free along the way.
494 1.16 christos */
495 1.16 christos for (; tmp2 != tmp; tmp--)
496 1.16 christos free(*tmp);
497 1.16 christos /*
498 1.16 christos * Now free the list itself.
499 1.16 christos */
500 1.16 christos free(tmp2);
501 1.16 christos
502 1.16 christos /*
503 1.16 christos * Free the input line buffer, if we have one.
504 1.16 christos */
505 1.16 christos if (inpline != NULL) {
506 1.16 christos free(inpline);
507 1.16 christos inpline = NULL;
508 1.16 christos }
509 1.16 christos }
510 1.16 christos
511 1.16 christos static void
512 1.16 christos run(char **argv)
513 1.16 christos {
514 1.16 christos int fd;
515 1.16 christos char **avec;
516 1.1 cgd
517 1.16 christos /*
518 1.16 christos * If the user wants to be notified of each command before it is
519 1.16 christos * executed, notify them. If they want the notification to be
520 1.16 christos * followed by a prompt, then prompt them.
521 1.16 christos */
522 1.16 christos if (tflag || pflag) {
523 1.1 cgd (void)fprintf(stderr, "%s", *argv);
524 1.16 christos for (avec = argv + 1; *avec != NULL; ++avec)
525 1.16 christos (void)fprintf(stderr, " %s", *avec);
526 1.16 christos /*
527 1.16 christos * If the user has asked to be prompted, do so.
528 1.16 christos */
529 1.16 christos if (pflag)
530 1.16 christos /*
531 1.16 christos * If they asked not to exec, return without execution
532 1.16 christos * but if they asked to, go to the execution. If we
533 1.16 christos * could not open their tty, break the switch and drop
534 1.16 christos * back to -t behaviour.
535 1.16 christos */
536 1.16 christos switch (prompt()) {
537 1.16 christos case 0:
538 1.12 kleink return;
539 1.16 christos case 1:
540 1.16 christos goto exec;
541 1.16 christos case 2:
542 1.16 christos break;
543 1.12 kleink }
544 1.16 christos (void)fprintf(stderr, "\n");
545 1.16 christos (void)fflush(stderr);
546 1.1 cgd }
547 1.16 christos exec:
548 1.16 christos childerr = 0;
549 1.16 christos switch (vfork()) {
550 1.1 cgd case -1:
551 1.7 jtc err(1, "vfork");
552 1.16 christos /*NOTREACHED*/
553 1.1 cgd case 0:
554 1.16 christos if (oflag) {
555 1.16 christos if ((fd = open(_PATH_TTY, O_RDONLY)) == -1)
556 1.16 christos err(1, "can't open /dev/tty");
557 1.16 christos } else {
558 1.16 christos fd = open(_PATH_DEVNULL, O_RDONLY);
559 1.16 christos }
560 1.16 christos if (fd > STDIN_FILENO) {
561 1.16 christos if (dup2(fd, STDIN_FILENO) != 0)
562 1.16 christos err(1, "can't dup2 to stdin");
563 1.16 christos (void)close(fd);
564 1.16 christos }
565 1.16 christos (void)execvp(argv[0], argv);
566 1.16 christos childerr = errno;
567 1.1 cgd _exit(1);
568 1.1 cgd }
569 1.16 christos curprocs++;
570 1.16 christos waitchildren(*argv, 0);
571 1.16 christos }
572 1.16 christos
573 1.16 christos static void
574 1.16 christos waitchildren(const char *name, int waitall)
575 1.16 christos {
576 1.16 christos pid_t pid;
577 1.16 christos int status;
578 1.16 christos
579 1.16 christos while ((pid = waitpid(-1, &status, !waitall && curprocs < maxprocs ?
580 1.16 christos WNOHANG : 0)) > 0) {
581 1.16 christos curprocs--;
582 1.16 christos /* If we couldn't invoke the utility, exit. */
583 1.16 christos if (childerr != 0) {
584 1.16 christos errno = childerr;
585 1.16 christos err(errno == ENOENT ? 127 : 126, "%s", name);
586 1.16 christos }
587 1.16 christos /*
588 1.16 christos * According to POSIX, we have to exit if the utility exits
589 1.16 christos * with a 255 status, or is interrupted by a signal. xargs
590 1.16 christos * is allowed to return any exit status between 1 and 125
591 1.16 christos * in these cases, but we'll use 124 and 125, the same
592 1.16 christos * values used by GNU xargs.
593 1.16 christos */
594 1.16 christos if (WIFEXITED(status)) {
595 1.16 christos if (WEXITSTATUS (status) == 255) {
596 1.16 christos warnx ("%s exited with status 255", name);
597 1.16 christos exit(124);
598 1.16 christos } else if (WEXITSTATUS (status) != 0) {
599 1.16 christos rval = 123;
600 1.16 christos }
601 1.16 christos } else if (WIFSIGNALED (status)) {
602 1.16 christos if (WTERMSIG(status) < NSIG) {
603 1.16 christos warnx("%s terminated by SIG%s", name,
604 1.16 christos sys_signame[WTERMSIG(status)]);
605 1.16 christos } else {
606 1.16 christos warnx("%s terminated by signal %d", name,
607 1.16 christos WTERMSIG(status));
608 1.16 christos }
609 1.16 christos exit(125);
610 1.10 fair }
611 1.3 jtc }
612 1.16 christos if (pid == -1 && errno != ECHILD)
613 1.20 plunky err(1, "waitpid");
614 1.16 christos }
615 1.16 christos
616 1.16 christos /*
617 1.16 christos * Prompt the user about running a command.
618 1.16 christos */
619 1.16 christos static int
620 1.16 christos prompt(void)
621 1.16 christos {
622 1.16 christos regex_t cre;
623 1.16 christos size_t rsize;
624 1.16 christos int match;
625 1.16 christos char *response;
626 1.16 christos FILE *ttyfp;
627 1.16 christos
628 1.16 christos if ((ttyfp = fopen(_PATH_TTY, "r")) == NULL)
629 1.16 christos return (2); /* Indicate that the TTY failed to open. */
630 1.16 christos (void)fprintf(stderr, "?...");
631 1.16 christos (void)fflush(stderr);
632 1.16 christos if ((response = fgetln(ttyfp, &rsize)) == NULL ||
633 1.16 christos regcomp(&cre, nl_langinfo(YESEXPR), REG_BASIC) != 0) {
634 1.16 christos (void)fclose(ttyfp);
635 1.16 christos return (0);
636 1.16 christos }
637 1.16 christos response[rsize - 1] = '\0';
638 1.16 christos match = regexec(&cre, response, 0, NULL, 0);
639 1.16 christos (void)fclose(ttyfp);
640 1.16 christos regfree(&cre);
641 1.16 christos return (match == 0);
642 1.1 cgd }
643 1.1 cgd
644 1.12 kleink static void
645 1.16 christos usage(void)
646 1.1 cgd {
647 1.1 cgd (void)fprintf(stderr,
648 1.16 christos "Usage: %s [-0opt] [-E eofstr] [-I replstr [-R replacements] [-S replsize]]\n"
649 1.16 christos " [-J replstr] [-L number] [-n number [-x]] [-P maxprocs]\n"
650 1.16 christos " [-s size] [utility [argument ...]]\n", getprogname());
651 1.1 cgd exit(1);
652 1.1 cgd }
653