1 /* vi: set sw=4 ts=4: */
3 * Mini xargs implementation for busybox
4 * Options are supported: "-prtx -n max_arg -s max_chars -e[ouf_str]"
6 * (C) 2002,2003 by Vladimir Oleynik <dzo@simtreas.ru>
9 * - Mark Whitley and Glenn McGrath for stimulus to rewrite :)
10 * - Mike Rendell <michael@cs.mun.ca>
11 * and David MacKenzie <djm@gnu.ai.mit.edu>.
13 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
15 * xargs is described in the Single Unix Specification v3 at
16 * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html
22 /* COMPAT: SYSV version defaults size (and has a max value of) to 470.
23 We try to make it as large as possible. */
24 #if !defined(ARG_MAX) && defined(_SC_ARG_MAX)
25 #define ARG_MAX sysconf (_SC_ARG_MAX)
33 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
34 # define CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
36 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
37 # define CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
39 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
40 # define CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
42 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
43 # define CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
48 This function has special algorithm.
49 Don't use fork and include to main!
51 static int xargs_exec(char *const *args)
54 volatile int exec_errno = 0; /* shared vfork stack */
59 bb_perror_msg_and_die("vfork");
63 BB_EXECVP(args[0], args);
64 exec_errno = errno; /* set error to shared stack */
69 while (wait(&status) == (pid_t) -1)
74 bb_perror_msg("%s", args[0]);
75 return exec_errno == ENOENT ? 127 : 126;
77 if (WEXITSTATUS(status) == 255) {
78 bb_error_msg("%s: exited with status 255; aborting", args[0]);
81 if (WIFSTOPPED(status)) {
82 bb_error_msg("%s: stopped by signal %d",
83 args[0], WSTOPSIG(status));
86 if (WIFSIGNALED(status)) {
87 bb_error_msg("%s: terminated by signal %d",
88 args[0], WTERMSIG(status));
91 if (WEXITSTATUS(status))
97 typedef struct xlist_s {
100 struct xlist_s *link;
103 static int eof_stdin_detected;
105 #define ISBLANK(c) ((c) == ' ' || (c) == '\t')
106 #define ISSPACE(c) (ISBLANK(c) || (c) == '\n' || (c) == '\r' \
107 || (c) == '\f' || (c) == '\v')
109 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
110 static xlist_t *process_stdin(xlist_t * list_arg,
111 const char *eof_str, size_t mc, char *buf)
118 char *s = NULL; /* start word */
119 char *p = NULL; /* pointer to end word */
120 char q = 0; /* quote char */
122 char eof_str_detected = 0;
123 size_t line_l = 0; /* size loaded args line */
124 int c; /* current char */
128 for (prev = cur = list_arg; cur; cur = cur->link) {
129 line_l += cur->lenght; /* previous allocated */
134 while (!eof_stdin_detected) {
137 eof_stdin_detected++;
142 if (eof_str_detected)
144 if (state == BACKSLASH) {
147 } else if (state == QUOTE) {
154 } else { /* if(state == NORM) */
168 } else if (c == '\'' || c == '"') {
173 if ((size_t)(p - buf) >= mc)
174 bb_error_msg_and_die("argument line too long");
179 if (state == SPACE) { /* word's delimiter or EOF detected */
181 bb_error_msg_and_die("unmatched %s quote",
182 q == '\'' ? "single" : "double");
186 eof_str_detected = strcmp(s, eof_str) == 0;
188 if (!eof_str_detected) {
189 size_t lenght = (p - buf);
191 cur = xmalloc(sizeof(xlist_t) + lenght);
192 cur->data = memcpy(cur + 1, s, lenght);
193 cur->lenght = lenght;
203 /* stop memory usage :-) */
214 /* The variant does not support single quotes, double quotes or backslash */
215 static xlist_t *process_stdin(xlist_t * list_arg,
216 const char *eof_str, size_t mc, char *buf)
219 int c; /* current char */
220 int eof_str_detected = 0;
221 char *s = NULL; /* start word */
222 char *p = NULL; /* pointer to end word */
223 size_t line_l = 0; /* size loaded args line */
227 for (prev = cur = list_arg; cur; cur = cur->link) {
228 line_l += cur->lenght; /* previous allocated */
233 while (!eof_stdin_detected) {
236 eof_stdin_detected++;
238 if (eof_str_detected)
240 if (c == EOF || ISSPACE(c)) {
248 bb_error_msg_and_die("argument line too long");
249 *p++ = c == EOF ? 0 : c;
250 if (c == EOF) { /* word's delimiter or EOF detected */
253 eof_str_detected = strcmp(s, eof_str) == 0;
255 if (!eof_str_detected) {
256 size_t lenght = (p - buf);
258 cur = xmalloc(sizeof(xlist_t) + lenght);
259 cur->data = memcpy(cur + 1, s, lenght);
260 cur->lenght = lenght;
270 /* stop memory usage :-) */
279 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_QUOTES */
282 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
283 /* Prompt the user for a response, and
284 if the user responds affirmatively, return true;
285 otherwise, return false. Used "/dev/tty", not stdin. */
286 static int xargs_ask_confirmation(void)
288 static FILE *tty_stream;
292 tty_stream = xfopen(CURRENT_TTY, "r");
293 /* pranoidal security by vodz */
294 fcntl(fileno(tty_stream), F_SETFD, FD_CLOEXEC);
296 fputs(" ?...", stderr);
298 c = savec = getc(tty_stream);
299 while (c != EOF && c != '\n')
300 c = getc(tty_stream);
301 if (savec == 'y' || savec == 'Y')
306 # define xargs_ask_confirmation() 1
307 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION */
309 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
310 static xlist_t *process0_stdin(xlist_t * list_arg, const char *eof_str ATTRIBUTE_UNUSED,
311 size_t mc, char *buf)
313 int c; /* current char */
314 char *s = NULL; /* start word */
315 char *p = NULL; /* pointer to end word */
316 size_t line_l = 0; /* size loaded args line */
320 for (prev = cur = list_arg; cur; cur = cur->link) {
321 line_l += cur->lenght; /* previous allocated */
326 while (!eof_stdin_detected) {
329 eof_stdin_detected++;
336 if ((size_t)(p - buf) >= mc)
337 bb_error_msg_and_die("argument line too long");
339 if (c == 0) { /* word's delimiter or EOF detected */
341 size_t lenght = (p - buf);
343 cur = xmalloc(sizeof(xlist_t) + lenght);
344 cur->data = memcpy(cur + 1, s, lenght);
345 cur->lenght = lenght;
355 /* stop memory usage :-) */
363 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM */
365 /* Correct regardless of combination of CONFIG_xxx */
372 USE_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,)
373 USE_FEATURE_XARGS_SUPPORT_TERMOPT( OPTBIT_TERMINATE ,)
374 USE_FEATURE_XARGS_SUPPORT_ZERO_TERM( OPTBIT_ZEROTERM ,)
376 OPT_VERBOSE = 1<<OPTBIT_VERBOSE ,
377 OPT_NO_EMPTY = 1<<OPTBIT_NO_EMPTY ,
378 OPT_UPTO_NUMBER = 1<<OPTBIT_UPTO_NUMBER,
379 OPT_UPTO_SIZE = 1<<OPTBIT_UPTO_SIZE ,
380 OPT_EOF_STRING = 1<<OPTBIT_EOF_STRING ,
381 OPT_INTERACTIVE = USE_FEATURE_XARGS_SUPPORT_CONFIRMATION((1<<OPTBIT_INTERACTIVE)) + 0,
382 OPT_TERMINATE = USE_FEATURE_XARGS_SUPPORT_TERMOPT( (1<<OPTBIT_TERMINATE )) + 0,
383 OPT_ZEROTERM = USE_FEATURE_XARGS_SUPPORT_ZERO_TERM( (1<<OPTBIT_ZEROTERM )) + 0,
385 #define OPTION_STR "+trn:s:e::" \
386 USE_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
387 USE_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \
388 USE_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0")
390 int xargs_main(int argc, char **argv);
391 int xargs_main(int argc, char **argv)
395 xlist_t *list = NULL;
398 char *max_args, *max_chars;
402 const char *eof_str = "_";
405 #if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
406 xlist_t* (*read_args)(xlist_t*, const char*, size_t, char*) = process_stdin;
408 #define read_args process_stdin
411 opt = getopt32(argc, argv, OPTION_STR, &max_args, &max_chars, &eof_str);
413 if (opt & OPT_ZEROTERM)
414 USE_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin);
419 /* default behavior is to echo all the filenames */
420 *argv = (char*)"echo";
424 orig_arg_max = ARG_MAX;
425 if (orig_arg_max == -1)
426 orig_arg_max = LONG_MAX;
427 orig_arg_max -= 2048; /* POSIX.2 requires subtracting 2048 */
429 if (opt & OPT_UPTO_SIZE) {
430 n_max_chars = xatoul_range(max_chars, 1, orig_arg_max);
431 for (i = 0; i < argc; i++) {
432 n_chars += strlen(*argv) + 1;
434 if (n_max_chars < n_chars) {
435 bb_error_msg_and_die("cannot fit single argument within argument list size limit");
437 n_max_chars -= n_chars;
439 /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which
440 have it at 1 meg). Things will work fine with a large ARG_MAX but it
441 will probably hurt the system more than it needs to; an array of this
442 size is allocated. */
443 if (orig_arg_max > 20 * 1024)
444 orig_arg_max = 20 * 1024;
445 n_max_chars = orig_arg_max;
447 max_chars = xmalloc(n_max_chars);
449 if (opt & OPT_UPTO_NUMBER) {
450 n_max_arg = xatoul_range(max_args, 1, INT_MAX);
452 n_max_arg = n_max_chars;
455 while ((list = read_args(list, eof_str, n_max_chars, max_chars)) != NULL ||
456 !(opt & OPT_NO_EMPTY))
461 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
462 for (cur = list; cur;) {
463 n_chars += cur->lenght;
466 if (n_chars > n_max_chars || (n == n_max_arg && cur)) {
467 if (opt & OPT_TERMINATE)
468 bb_error_msg_and_die("argument list too long");
473 for (cur = list; cur; cur = cur->link) {
474 n_chars += cur->lenght;
476 if (n_chars > n_max_chars || n == n_max_arg) {
480 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT */
482 /* allocate pointers for execvp:
483 argc*arg, n*arg from stdin, NULL */
484 args = xzalloc((n + argc + 1) * sizeof(char *));
486 /* store the command to be executed
487 (taken from the command line) */
488 for (i = 0; i < argc; i++)
490 /* (taken from stdin) */
491 for (cur = list; n; cur = cur->link) {
492 args[i++] = cur->data;
496 if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) {
497 for (i = 0; args[i]; i++) {
500 fputs(args[i], stderr);
502 if (!(opt & OPT_INTERACTIVE))
505 if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
506 child_error = xargs_exec(args);
510 for (i = argc; args[i]; i++) {
516 if (child_error > 0 && child_error != 123) {
520 if (ENABLE_FEATURE_CLEAN_UP) free(max_chars);
527 const char *applet_name = "debug stuff usage";
529 void bb_show_usage(void)
531 fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
536 int main(int argc, char **argv)
538 return xargs_main(argc, argv);