blob: ae01a49beaf8cb151ddece900e50df613d22bb84
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * Mini xargs implementation for busybox |
4 | * |
5 | * (C) 2002,2003 by Vladimir Oleynik <dzo@simtreas.ru> |
6 | * |
7 | * Special thanks |
8 | * - Mark Whitley and Glenn McGrath for stimulus to rewrite :) |
9 | * - Mike Rendell <michael@cs.mun.ca> |
10 | * and David MacKenzie <djm@gnu.ai.mit.edu>. |
11 | * |
12 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
13 | * |
14 | * xargs is described in the Single Unix Specification v3 at |
15 | * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html |
16 | */ |
17 | |
18 | //config:config XARGS |
19 | //config: bool "xargs" |
20 | //config: default y |
21 | //config: help |
22 | //config: xargs is used to execute a specified command for |
23 | //config: every item from standard input. |
24 | //config: |
25 | //config:config FEATURE_XARGS_SUPPORT_CONFIRMATION |
26 | //config: bool "Enable -p: prompt and confirmation" |
27 | //config: default y |
28 | //config: depends on XARGS |
29 | //config: help |
30 | //config: Support -p: prompt the user whether to run each command |
31 | //config: line and read a line from the terminal. |
32 | //config: |
33 | //config:config FEATURE_XARGS_SUPPORT_QUOTES |
34 | //config: bool "Enable single and double quotes and backslash" |
35 | //config: default y |
36 | //config: depends on XARGS |
37 | //config: help |
38 | //config: Support quoting in the input. |
39 | //config: |
40 | //config:config FEATURE_XARGS_SUPPORT_TERMOPT |
41 | //config: bool "Enable -x: exit if -s or -n is exceeded" |
42 | //config: default y |
43 | //config: depends on XARGS |
44 | //config: help |
45 | //config: Support -x: exit if the command size (see the -s or -n option) |
46 | //config: is exceeded. |
47 | //config: |
48 | //config:config FEATURE_XARGS_SUPPORT_ZERO_TERM |
49 | //config: bool "Enable -0: NUL-terminated input" |
50 | //config: default y |
51 | //config: depends on XARGS |
52 | //config: help |
53 | //config: Support -0: input items are terminated by a NUL character |
54 | //config: instead of whitespace, and the quotes and backslash |
55 | //config: are not special. |
56 | //config: |
57 | //config:config FEATURE_XARGS_SUPPORT_REPL_STR |
58 | //config: bool "Enable -I STR: string to replace" |
59 | //config: default y |
60 | //config: depends on XARGS |
61 | //config: help |
62 | //config: Support -I STR and -i[STR] options. |
63 | |
64 | //applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs)) |
65 | |
66 | //kbuild:lib-$(CONFIG_XARGS) += xargs.o |
67 | |
68 | #include "libbb.h" |
69 | #include "common_bufsiz.h" |
70 | |
71 | /* This is a NOEXEC applet. Be very careful! */ |
72 | |
73 | |
74 | //#define dbg_msg(...) bb_error_msg(__VA_ARGS__) |
75 | #define dbg_msg(...) ((void)0) |
76 | |
77 | |
78 | #ifdef TEST |
79 | # ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION |
80 | # define ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 1 |
81 | # endif |
82 | # ifndef ENABLE_FEATURE_XARGS_SUPPORT_QUOTES |
83 | # define ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 1 |
84 | # endif |
85 | # ifndef ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT |
86 | # define ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 1 |
87 | # endif |
88 | # ifndef ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM |
89 | # define ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 1 |
90 | # endif |
91 | #endif |
92 | |
93 | |
94 | struct globals { |
95 | char **args; |
96 | #if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR |
97 | char **argv; |
98 | const char *repl_str; |
99 | char eol_ch; |
100 | #endif |
101 | const char *eof_str; |
102 | int idx; |
103 | } FIX_ALIASING; |
104 | #define G (*(struct globals*)bb_common_bufsiz1) |
105 | #define INIT_G() do { \ |
106 | setup_common_bufsiz(); \ |
107 | G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \ |
108 | IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \ |
109 | IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \ |
110 | } while (0) |
111 | |
112 | |
113 | static int xargs_exec(void) |
114 | { |
115 | int status; |
116 | |
117 | status = spawn_and_wait(G.args); |
118 | if (status < 0) { |
119 | bb_simple_perror_msg(G.args[0]); |
120 | return errno == ENOENT ? 127 : 126; |
121 | } |
122 | if (status == 255) { |
123 | bb_error_msg("%s: exited with status 255; aborting", G.args[0]); |
124 | return 124; |
125 | } |
126 | if (status >= 0x180) { |
127 | bb_error_msg("'%s' terminated by signal %d", |
128 | G.args[0], status - 0x180); |
129 | return 125; |
130 | } |
131 | if (status) |
132 | return 123; |
133 | return 0; |
134 | } |
135 | |
136 | /* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space. |
137 | * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13. |
138 | */ |
139 | #define ISSPACE(a) ({ unsigned char xargs__isspace = (a) - 9; xargs__isspace == (' ' - 9) || xargs__isspace <= (13 - 9); }) |
140 | |
141 | static void store_param(char *s) |
142 | { |
143 | /* Grow by 256 elements at once */ |
144 | if (!(G.idx & 0xff)) { /* G.idx == N*256 */ |
145 | /* Enlarge, make G.args[(N+1)*256 - 1] last valid idx */ |
146 | G.args = xrealloc(G.args, sizeof(G.args[0]) * (G.idx + 0x100)); |
147 | } |
148 | G.args[G.idx++] = s; |
149 | } |
150 | |
151 | /* process[0]_stdin: |
152 | * Read characters into buf[n_max_chars+1], and when parameter delimiter |
153 | * is seen, store the address of a new parameter to args[]. |
154 | * If reading discovers that last chars do not form the complete |
155 | * parameter, the pointer to the first such "tail character" is returned. |
156 | * (buf has extra byte at the end to accommodate terminating NUL |
157 | * of "tail characters" string). |
158 | * Otherwise, the returned pointer points to NUL byte. |
159 | * On entry, buf[] may contain some "seed chars" which are to become |
160 | * the beginning of the first parameter. |
161 | */ |
162 | |
163 | #if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES |
164 | static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf) |
165 | { |
166 | #define NORM 0 |
167 | #define QUOTE 1 |
168 | #define BACKSLASH 2 |
169 | #define SPACE 4 |
170 | char q = '\0'; /* quote char */ |
171 | char state = NORM; |
172 | char *s = buf; /* start of the word */ |
173 | char *p = s + strlen(buf); /* end of the word */ |
174 | |
175 | buf += n_max_chars; /* past buffer's end */ |
176 | |
177 | /* "goto ret" is used instead of "break" to make control flow |
178 | * more obvious: */ |
179 | |
180 | while (1) { |
181 | int c = getchar(); |
182 | if (c == EOF) { |
183 | if (p != s) |
184 | goto close_word; |
185 | goto ret; |
186 | } |
187 | if (state == BACKSLASH) { |
188 | state = NORM; |
189 | goto set; |
190 | } |
191 | if (state == QUOTE) { |
192 | if (c != q) |
193 | goto set; |
194 | q = '\0'; |
195 | state = NORM; |
196 | } else { /* if (state == NORM) */ |
197 | if (ISSPACE(c)) { |
198 | if (p != s) { |
199 | close_word: |
200 | state = SPACE; |
201 | c = '\0'; |
202 | goto set; |
203 | } |
204 | } else { |
205 | if (c == '\\') { |
206 | state = BACKSLASH; |
207 | } else if (c == '\'' || c == '"') { |
208 | q = c; |
209 | state = QUOTE; |
210 | } else { |
211 | set: |
212 | *p++ = c; |
213 | } |
214 | } |
215 | } |
216 | if (state == SPACE) { /* word's delimiter or EOF detected */ |
217 | if (q) { |
218 | bb_error_msg_and_die("unmatched %s quote", |
219 | q == '\'' ? "single" : "double"); |
220 | } |
221 | /* A full word is loaded */ |
222 | if (G.eof_str) { |
223 | if (strcmp(s, G.eof_str) == 0) { |
224 | while (getchar() != EOF) |
225 | continue; |
226 | p = s; |
227 | goto ret; |
228 | } |
229 | } |
230 | store_param(s); |
231 | dbg_msg("args[]:'%s'", s); |
232 | s = p; |
233 | n_max_arg--; |
234 | if (n_max_arg == 0) { |
235 | goto ret; |
236 | } |
237 | state = NORM; |
238 | } |
239 | if (p == buf) { |
240 | goto ret; |
241 | } |
242 | } |
243 | ret: |
244 | *p = '\0'; |
245 | /* store_param(NULL) - caller will do it */ |
246 | dbg_msg("return:'%s'", s); |
247 | return s; |
248 | } |
249 | #else |
250 | /* The variant does not support single quotes, double quotes or backslash */ |
251 | static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf) |
252 | { |
253 | char *s = buf; /* start of the word */ |
254 | char *p = s + strlen(buf); /* end of the word */ |
255 | |
256 | buf += n_max_chars; /* past buffer's end */ |
257 | |
258 | while (1) { |
259 | int c = getchar(); |
260 | if (c == EOF) { |
261 | if (p == s) |
262 | goto ret; |
263 | } |
264 | if (c == EOF || ISSPACE(c)) { |
265 | if (p == s) |
266 | continue; |
267 | c = EOF; |
268 | } |
269 | *p++ = (c == EOF ? '\0' : c); |
270 | if (c == EOF) { /* word's delimiter or EOF detected */ |
271 | /* A full word is loaded */ |
272 | if (G.eof_str) { |
273 | if (strcmp(s, G.eof_str) == 0) { |
274 | while (getchar() != EOF) |
275 | continue; |
276 | p = s; |
277 | goto ret; |
278 | } |
279 | } |
280 | store_param(s); |
281 | dbg_msg("args[]:'%s'", s); |
282 | s = p; |
283 | n_max_arg--; |
284 | if (n_max_arg == 0) { |
285 | goto ret; |
286 | } |
287 | } |
288 | if (p == buf) { |
289 | goto ret; |
290 | } |
291 | } |
292 | ret: |
293 | *p = '\0'; |
294 | /* store_param(NULL) - caller will do it */ |
295 | dbg_msg("return:'%s'", s); |
296 | return s; |
297 | } |
298 | #endif /* FEATURE_XARGS_SUPPORT_QUOTES */ |
299 | |
300 | #if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM |
301 | static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf) |
302 | { |
303 | char *s = buf; /* start of the word */ |
304 | char *p = s + strlen(buf); /* end of the word */ |
305 | |
306 | buf += n_max_chars; /* past buffer's end */ |
307 | |
308 | while (1) { |
309 | int c = getchar(); |
310 | if (c == EOF) { |
311 | if (p == s) |
312 | goto ret; |
313 | c = '\0'; |
314 | } |
315 | *p++ = c; |
316 | if (c == '\0') { /* NUL or EOF detected */ |
317 | /* A full word is loaded */ |
318 | store_param(s); |
319 | dbg_msg("args[]:'%s'", s); |
320 | s = p; |
321 | n_max_arg--; |
322 | if (n_max_arg == 0) { |
323 | goto ret; |
324 | } |
325 | } |
326 | if (p == buf) { |
327 | goto ret; |
328 | } |
329 | } |
330 | ret: |
331 | *p = '\0'; |
332 | /* store_param(NULL) - caller will do it */ |
333 | dbg_msg("return:'%s'", s); |
334 | return s; |
335 | } |
336 | #endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */ |
337 | |
338 | #if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR |
339 | /* |
340 | * Used if -I<repl> was specified. |
341 | * In this mode, words aren't appended to PROG ARGS. |
342 | * Instead, entire input line is read, then <repl> string |
343 | * in every PROG and ARG is replaced with the line: |
344 | * echo -e "ho ho\nhi" | xargs -I_ cmd __ _ |
345 | * results in "cmd 'ho hoho ho' 'ho ho'"; "cmd 'hihi' 'hi'". |
346 | * -n MAX_ARGS seems to be ignored. |
347 | * Tested with GNU findutils 4.5.10. |
348 | */ |
349 | //FIXME: n_max_chars is not handled the same way as in GNU findutils. |
350 | //FIXME: quoting is not implemented. |
351 | static char* FAST_FUNC process_stdin_with_replace(int n_max_chars, int n_max_arg UNUSED_PARAM, char *buf) |
352 | { |
353 | int i; |
354 | char *end, *p; |
355 | |
356 | /* Free strings from last invocation, if any */ |
357 | for (i = 0; G.args && G.args[i]; i++) |
358 | if (G.args[i] != G.argv[i]) |
359 | free(G.args[i]); |
360 | |
361 | end = buf + n_max_chars; |
362 | p = buf; |
363 | |
364 | while (1) { |
365 | int c = getchar(); |
366 | if (c == EOF || c == G.eol_ch) { |
367 | if (p == buf) |
368 | goto ret; /* empty line */ |
369 | c = '\0'; |
370 | } |
371 | *p++ = c; |
372 | if (c == '\0') { /* EOL or EOF detected */ |
373 | i = 0; |
374 | while (G.argv[i]) { |
375 | char *arg = G.argv[i]; |
376 | int count = count_strstr(arg, G.repl_str); |
377 | if (count != 0) |
378 | arg = xmalloc_substitute_string(arg, count, G.repl_str, buf); |
379 | store_param(arg); |
380 | dbg_msg("args[]:'%s'", arg); |
381 | i++; |
382 | } |
383 | p = buf; |
384 | goto ret; |
385 | } |
386 | if (p == end) { |
387 | goto ret; |
388 | } |
389 | } |
390 | ret: |
391 | *p = '\0'; |
392 | /* store_param(NULL) - caller will do it */ |
393 | dbg_msg("return:'%s'", buf); |
394 | return buf; |
395 | } |
396 | #endif |
397 | |
398 | #if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION |
399 | /* Prompt the user for a response, and |
400 | * if user responds affirmatively, return true; |
401 | * otherwise, return false. Uses "/dev/tty", not stdin. |
402 | */ |
403 | static int xargs_ask_confirmation(void) |
404 | { |
405 | FILE *tty_stream; |
406 | int c, savec; |
407 | |
408 | tty_stream = xfopen_for_read(CURRENT_TTY); |
409 | fputs(" ?...", stderr); |
410 | fflush_all(); |
411 | c = savec = getc(tty_stream); |
412 | while (c != EOF && c != '\n') |
413 | c = getc(tty_stream); |
414 | fclose(tty_stream); |
415 | return (savec == 'y' || savec == 'Y'); |
416 | } |
417 | #else |
418 | # define xargs_ask_confirmation() 1 |
419 | #endif |
420 | |
421 | //usage:#define xargs_trivial_usage |
422 | //usage: "[OPTIONS] [PROG ARGS]" |
423 | //usage:#define xargs_full_usage "\n\n" |
424 | //usage: "Run PROG on every item given by stdin\n" |
425 | //usage: IF_FEATURE_XARGS_SUPPORT_CONFIRMATION( |
426 | //usage: "\n -p Ask user whether to run each command" |
427 | //usage: ) |
428 | //usage: "\n -r Don't run command if input is empty" |
429 | //usage: IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( |
430 | //usage: "\n -0 Input is separated by NUL characters" |
431 | //usage: ) |
432 | //usage: "\n -t Print the command on stderr before execution" |
433 | //usage: "\n -e[STR] STR stops input processing" |
434 | //usage: "\n -n N Pass no more than N args to PROG" |
435 | //usage: "\n -s N Pass command line of no more than N bytes" |
436 | //usage: IF_FEATURE_XARGS_SUPPORT_REPL_STR( |
437 | //usage: "\n -I STR Replace STR within PROG ARGS with input line" |
438 | //usage: ) |
439 | //usage: IF_FEATURE_XARGS_SUPPORT_TERMOPT( |
440 | //usage: "\n -x Exit if size is exceeded" |
441 | //usage: ) |
442 | //usage:#define xargs_example_usage |
443 | //usage: "$ ls | xargs gzip\n" |
444 | //usage: "$ find . -name '*.c' -print | xargs rm\n" |
445 | |
446 | /* Correct regardless of combination of CONFIG_xxx */ |
447 | enum { |
448 | OPTBIT_VERBOSE = 0, |
449 | OPTBIT_NO_EMPTY, |
450 | OPTBIT_UPTO_NUMBER, |
451 | OPTBIT_UPTO_SIZE, |
452 | OPTBIT_EOF_STRING, |
453 | OPTBIT_EOF_STRING1, |
454 | IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,) |
455 | IF_FEATURE_XARGS_SUPPORT_TERMOPT( OPTBIT_TERMINATE ,) |
456 | IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( OPTBIT_ZEROTERM ,) |
457 | IF_FEATURE_XARGS_SUPPORT_REPL_STR( OPTBIT_REPLSTR ,) |
458 | IF_FEATURE_XARGS_SUPPORT_REPL_STR( OPTBIT_REPLSTR1 ,) |
459 | |
460 | OPT_VERBOSE = 1 << OPTBIT_VERBOSE , |
461 | OPT_NO_EMPTY = 1 << OPTBIT_NO_EMPTY , |
462 | OPT_UPTO_NUMBER = 1 << OPTBIT_UPTO_NUMBER, |
463 | OPT_UPTO_SIZE = 1 << OPTBIT_UPTO_SIZE , |
464 | OPT_EOF_STRING = 1 << OPTBIT_EOF_STRING , /* GNU: -e[<param>] */ |
465 | OPT_EOF_STRING1 = 1 << OPTBIT_EOF_STRING1, /* SUS: -E<param> */ |
466 | OPT_INTERACTIVE = IF_FEATURE_XARGS_SUPPORT_CONFIRMATION((1 << OPTBIT_INTERACTIVE)) + 0, |
467 | OPT_TERMINATE = IF_FEATURE_XARGS_SUPPORT_TERMOPT( (1 << OPTBIT_TERMINATE )) + 0, |
468 | OPT_ZEROTERM = IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( (1 << OPTBIT_ZEROTERM )) + 0, |
469 | OPT_REPLSTR = IF_FEATURE_XARGS_SUPPORT_REPL_STR( (1 << OPTBIT_REPLSTR )) + 0, |
470 | OPT_REPLSTR1 = IF_FEATURE_XARGS_SUPPORT_REPL_STR( (1 << OPTBIT_REPLSTR1 )) + 0, |
471 | }; |
472 | #define OPTION_STR "+trn:s:e::E:" \ |
473 | IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \ |
474 | IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \ |
475 | IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") \ |
476 | IF_FEATURE_XARGS_SUPPORT_REPL_STR( "I:i::") |
477 | |
478 | int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
479 | int xargs_main(int argc, char **argv) |
480 | { |
481 | int i; |
482 | int child_error = 0; |
483 | char *max_args; |
484 | char *max_chars; |
485 | char *buf; |
486 | unsigned opt; |
487 | int n_max_chars; |
488 | int n_max_arg; |
489 | #if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM \ |
490 | || ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR |
491 | char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin; |
492 | #else |
493 | #define read_args process_stdin |
494 | #endif |
495 | |
496 | INIT_G(); |
497 | |
498 | #if ENABLE_DESKTOP && ENABLE_LONG_OPTS |
499 | /* For example, Fedora's build system uses --no-run-if-empty */ |
500 | applet_long_options = |
501 | "no-run-if-empty\0" No_argument "r" |
502 | ; |
503 | #endif |
504 | opt = getopt32(argv, OPTION_STR, |
505 | &max_args, &max_chars, &G.eof_str, &G.eof_str |
506 | IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str) |
507 | ); |
508 | |
509 | /* -E ""? You may wonder why not just omit -E? |
510 | * This is used for portability: |
511 | * old xargs was using "_" as default for -E / -e */ |
512 | if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0') |
513 | G.eof_str = NULL; |
514 | |
515 | if (opt & OPT_ZEROTERM) { |
516 | IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin;) |
517 | IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\0';) |
518 | } |
519 | |
520 | argv += optind; |
521 | argc -= optind; |
522 | if (!argv[0]) { |
523 | /* default behavior is to echo all the filenames */ |
524 | *--argv = (char*)"echo"; |
525 | argc++; |
526 | } |
527 | |
528 | /* |
529 | * The Open Group Base Specifications Issue 6: |
530 | * "The xargs utility shall limit the command line length such that |
531 | * when the command line is invoked, the combined argument |
532 | * and environment lists (see the exec family of functions |
533 | * in the System Interfaces volume of IEEE Std 1003.1-2001) |
534 | * shall not exceed {ARG_MAX}-2048 bytes". |
535 | */ |
536 | n_max_chars = bb_arg_max(); |
537 | if (n_max_chars > 32 * 1024) |
538 | n_max_chars = 32 * 1024; |
539 | /* |
540 | * POSIX suggests substracting 2048 bytes from sysconf(_SC_ARG_MAX) |
541 | * so that the process may safely modify its environment. |
542 | */ |
543 | n_max_chars -= 2048; |
544 | |
545 | if (opt & OPT_UPTO_SIZE) { |
546 | n_max_chars = xatou_range(max_chars, 1, INT_MAX); |
547 | } |
548 | /* Account for prepended fixed arguments */ |
549 | { |
550 | size_t n_chars = 0; |
551 | for (i = 0; argv[i]; i++) { |
552 | n_chars += strlen(argv[i]) + 1; |
553 | } |
554 | n_max_chars -= n_chars; |
555 | } |
556 | /* Sanity check */ |
557 | if (n_max_chars <= 0) { |
558 | bb_error_msg_and_die("can't fit single argument within argument list size limit"); |
559 | } |
560 | |
561 | buf = xzalloc(n_max_chars + 1); |
562 | |
563 | n_max_arg = n_max_chars; |
564 | if (opt & OPT_UPTO_NUMBER) { |
565 | n_max_arg = xatou_range(max_args, 1, INT_MAX); |
566 | /* Not necessary, we use growable args[]: */ |
567 | /* if (n_max_arg > n_max_chars) n_max_arg = n_max_chars */ |
568 | } |
569 | |
570 | #if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR |
571 | if (opt & (OPT_REPLSTR | OPT_REPLSTR1)) { |
572 | /* |
573 | * -I<str>: |
574 | * Unmodified args are kept in G.argv[i], |
575 | * G.args[i] receives malloced G.argv[i] with <str> replaced |
576 | * with input line. Setting this up: |
577 | */ |
578 | G.args = NULL; |
579 | G.argv = argv; |
580 | argc = 0; |
581 | read_args = process_stdin_with_replace; |
582 | /* Make -I imply -r. GNU findutils seems to do the same: */ |
583 | /* (otherwise "echo -n | xargs -I% echo %" would SEGV) */ |
584 | opt |= OPT_NO_EMPTY; |
585 | } else |
586 | #endif |
587 | { |
588 | /* Allocate pointers for execvp. |
589 | * We can statically allocate (argc + n_max_arg + 1) elements |
590 | * and do not bother with resizing args[], but on 64-bit machines |
591 | * this results in args[] vector which is ~8 times bigger |
592 | * than n_max_chars! That is, with n_max_chars == 20k, |
593 | * args[] will take 160k (!), which will most likely be |
594 | * almost entirely unused. |
595 | * |
596 | * See store_param() for matching 256-step growth logic |
597 | */ |
598 | G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff)); |
599 | /* Store the command to be executed, part 1 */ |
600 | for (i = 0; argv[i]; i++) |
601 | G.args[i] = argv[i]; |
602 | } |
603 | |
604 | while (1) { |
605 | char *rem; |
606 | |
607 | G.idx = argc; |
608 | rem = read_args(n_max_chars, n_max_arg, buf); |
609 | store_param(NULL); |
610 | |
611 | if (!G.args[argc]) { |
612 | if (*rem != '\0') |
613 | bb_error_msg_and_die("argument line too long"); |
614 | if (opt & OPT_NO_EMPTY) |
615 | break; |
616 | } |
617 | opt |= OPT_NO_EMPTY; |
618 | |
619 | if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) { |
620 | const char *fmt = " %s" + 1; |
621 | char **args = G.args; |
622 | for (i = 0; args[i]; i++) { |
623 | fprintf(stderr, fmt, args[i]); |
624 | fmt = " %s"; |
625 | } |
626 | if (!(opt & OPT_INTERACTIVE)) |
627 | bb_putchar_stderr('\n'); |
628 | } |
629 | |
630 | if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) { |
631 | child_error = xargs_exec(); |
632 | } |
633 | |
634 | if (child_error > 0 && child_error != 123) { |
635 | break; |
636 | } |
637 | |
638 | overlapping_strcpy(buf, rem); |
639 | } /* while */ |
640 | |
641 | if (ENABLE_FEATURE_CLEAN_UP) { |
642 | free(G.args); |
643 | free(buf); |
644 | } |
645 | |
646 | return child_error; |
647 | } |
648 | |
649 | |
650 | #ifdef TEST |
651 | |
652 | const char *applet_name = "debug stuff usage"; |
653 | |
654 | void bb_show_usage(void) |
655 | { |
656 | fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n", |
657 | applet_name); |
658 | exit(EXIT_FAILURE); |
659 | } |
660 | |
661 | int main(int argc, char **argv) |
662 | { |
663 | return xargs_main(argc, argv); |
664 | } |
665 | #endif /* TEST */ |
666 |