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