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
28 #include <sys/types.h>
31 /* COMPAT: SYSV version defaults size (and has a max value of) to 470.
32 We try to make it as large as possible. */
33 #if !defined(ARG_MAX) && defined(_SC_ARG_MAX)
34 #define ARG_MAX sysconf (_SC_ARG_MAX)
42 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
43 # define CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
45 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
46 # define CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
48 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
49 # define CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
51 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
52 # define CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
57 This function have special algorithm.
58 Don`t use fork and include to main!
60 static int xargs_exec(char *const *args)
63 volatile int exec_errno = 0; /* shared vfork stack */
65 if ((p = vfork()) >= 0) {
68 execvp(args[0], args);
69 exec_errno = errno; /* set error to shared stack */
75 while (wait(&status) == (pid_t) - 1)
80 bb_perror_msg("%s", args[0]);
81 return exec_errno == ENOENT ? 127 : 126;
83 if (WEXITSTATUS(status) == 255) {
84 bb_error_msg("%s: exited with status 255; aborting", args[0]);
87 if (WIFSTOPPED(status)) {
88 bb_error_msg("%s: stopped by signal %d",
89 args[0], WSTOPSIG(status));
92 if (WIFSIGNALED(status)) {
93 bb_error_msg("%s: terminated by signal %d",
94 args[0], WTERMSIG(status));
97 if (WEXITSTATUS(status) != 0)
103 bb_perror_msg_and_die("vfork");
108 typedef struct xlist_s {
111 struct xlist_s *link;
114 static int eof_stdin_detected;
116 #define ISBLANK(c) ((c) == ' ' || (c) == '\t')
117 #define ISSPACE(c) (ISBLANK (c) || (c) == '\n' || (c) == '\r' \
118 || (c) == '\f' || (c) == '\v')
120 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
121 static xlist_t *process_stdin(xlist_t * list_arg,
122 const char *eof_str, size_t mc, char *buf)
129 char *s = NULL; /* start word */
130 char *p = NULL; /* pointer to end word */
131 char q = 0; /* quote char */
133 char eof_str_detected = 0;
134 size_t line_l = 0; /* size loaded args line */
135 int c; /* current char */
139 for (prev = cur = list_arg; cur; cur = cur->link) {
140 line_l += cur->lenght; /* previous allocated */
145 while (!eof_stdin_detected) {
148 eof_stdin_detected++;
153 if (eof_str_detected)
155 if (state == BACKSLASH) {
158 } else if (state == QUOTE) {
165 } else { /* if(state == NORM) */
179 } else if (c == '\'' || c == '"') {
184 if ((size_t)(p - buf) >= mc)
185 bb_error_msg_and_die("argument line too long");
190 if (state == SPACE) { /* word's delimiter or EOF detected */
192 bb_error_msg_and_die("unmatched %s quote",
193 q == '\'' ? "single" : "double");
197 eof_str_detected = strcmp(s, eof_str) == 0;
199 if (!eof_str_detected) {
200 size_t lenght = (p - buf);
202 cur = xmalloc(sizeof(xlist_t) + lenght);
203 cur->data = memcpy(cur + 1, s, lenght);
204 cur->lenght = lenght;
214 /* stop memory usage :-) */
225 /* The variant does not support single quotes, double quotes or backslash */
226 static xlist_t *process_stdin(xlist_t * list_arg,
227 const char *eof_str, size_t mc, char *buf)
230 int c; /* current char */
231 int eof_str_detected = 0;
232 char *s = NULL; /* start word */
233 char *p = NULL; /* pointer to end word */
234 size_t line_l = 0; /* size loaded args line */
238 for (prev = cur = list_arg; cur; cur = cur->link) {
239 line_l += cur->lenght; /* previous allocated */
244 while (!eof_stdin_detected) {
247 eof_stdin_detected++;
249 if (eof_str_detected)
251 if (c == EOF || ISSPACE(c)) {
259 bb_error_msg_and_die("argument line too long");
260 *p++ = c == EOF ? 0 : c;
261 if (c == EOF) { /* word's delimiter or EOF detected */
264 eof_str_detected = strcmp(s, eof_str) == 0;
266 if (!eof_str_detected) {
267 size_t lenght = (p - buf);
269 cur = xmalloc(sizeof(xlist_t) + lenght);
270 cur->data = memcpy(cur + 1, s, lenght);
271 cur->lenght = lenght;
281 /* stop memory usage :-) */
290 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_QUOTES */
293 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
294 /* Prompt the user for a response, and
295 if the user responds affirmatively, return true;
296 otherwise, return false. Used "/dev/tty", not stdin. */
297 static int xargs_ask_confirmation(void)
299 static FILE *tty_stream;
303 tty_stream = bb_xfopen(CURRENT_TTY, "r");
304 /* pranoidal security by vodz */
305 fcntl(fileno(tty_stream), F_SETFD, FD_CLOEXEC);
307 fputs(" ?...", stderr);
309 c = savec = getc(tty_stream);
310 while (c != EOF && c != '\n')
311 c = getc(tty_stream);
312 if (savec == 'y' || savec == 'Y')
320 # define xargs_ask_confirmation() 1
321 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION */
323 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
329 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
330 static xlist_t *process0_stdin(xlist_t * list_arg, const char *eof_str ATTRIBUTE_UNUSED,
331 size_t mc, char *buf)
333 int c; /* current char */
334 char *s = NULL; /* start word */
335 char *p = NULL; /* pointer to end word */
336 size_t line_l = 0; /* size loaded args line */
340 for (prev = cur = list_arg; cur; cur = cur->link) {
341 line_l += cur->lenght; /* previous allocated */
346 while (!eof_stdin_detected) {
349 eof_stdin_detected++;
356 if ((size_t)(p - buf) >= mc)
357 bb_error_msg_and_die("argument line too long");
359 if (c == 0) { /* word's delimiter or EOF detected */
361 size_t lenght = (p - buf);
363 cur = xmalloc(sizeof(xlist_t) + lenght);
364 cur->data = memcpy(cur + 1, s, lenght);
365 cur->lenght = lenght;
375 /* stop memory usage :-) */
384 # define READ_ARGS(l, e, nmc, mc) (*read_args)(l, e, nmc, mc)
385 # define OPT_INC_0 1 /* future use */
387 # define OPT_INC_0 0 /* future use */
388 # define READ_ARGS(l, e, nmc, mc) process_stdin(l, e, nmc, mc)
389 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM */
392 #define OPT_VERBOSE (1<<0)
393 #define OPT_NO_EMPTY (1<<1)
394 #define OPT_UPTO_NUMBER (1<<2)
395 #define OPT_UPTO_SIZE (1<<3)
396 #define OPT_EOF_STRING (1<<4)
397 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
398 #define OPT_INTERACTIVE (1<<5)
400 #define OPT_INTERACTIVE (0) /* require for algorithm &| */
402 #define OPT_TERMINATE (1<<(5+OPT_INC_P))
403 #define OPT_ZEROTERM (1<<(5+OPT_INC_P+OPT_INC_X))
405 #define OPT_NEXT_OTHER (1<<(5+OPT_INC_P+OPT_INC_X+OPT_INC_0))
408 int xargs_main(int argc, char **argv)
412 xlist_t *list = NULL;
415 char *max_args, *max_chars;
419 const char *eof_str = "_";
423 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
424 xlist_t *(*read_args) (xlist_t *, const char *, size_t, char *) = process_stdin;
427 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
428 bb_opt_complementally = "pt";
431 opt = bb_getopt_ulflags(argc, argv, "+trn:s:e::"
432 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
435 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
438 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
441 ,&max_args, &max_chars, &eof_str);
446 /* default behavior is to echo all the filenames */
451 orig_arg_max = ARG_MAX;
452 if (orig_arg_max == -1)
453 orig_arg_max = LONG_MAX;
454 orig_arg_max -= 2048; /* POSIX.2 requires subtracting 2048. */
455 if ((opt & OPT_UPTO_SIZE)) {
456 n_max_chars = bb_xgetularg10_bnd(max_chars, 1, orig_arg_max);
457 for (i = 0; i < a; i++) {
458 n_chars += strlen(*argv) + 1;
460 if (n_max_chars < n_chars) {
461 bb_error_msg_and_die("can not fit single argument within argument list size limit");
463 n_max_chars -= n_chars;
465 /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which
466 have it at 1 meg). Things will work fine with a large ARG_MAX but it
467 will probably hurt the system more than it needs to; an array of this
468 size is allocated. */
469 if (orig_arg_max > 20 * 1024)
470 orig_arg_max = 20 * 1024;
471 n_max_chars = orig_arg_max;
473 max_chars = xmalloc(n_max_chars);
475 if ((opt & OPT_UPTO_NUMBER)) {
476 n_max_arg = bb_xgetularg10_bnd(max_args, 1, INT_MAX);
478 n_max_arg = n_max_chars;
481 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
482 if (opt & OPT_ZEROTERM)
483 read_args = process0_stdin;
486 while ((list = READ_ARGS(list, eof_str, n_max_chars, max_chars)) != NULL ||
487 (opt & OPT_NO_EMPTY) == 0)
492 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
493 for (cur = list; cur;) {
494 n_chars += cur->lenght;
497 if (n_chars > n_max_chars || (n == n_max_arg && cur)) {
498 if (opt & OPT_TERMINATE)
499 bb_error_msg_and_die("argument list too long");
504 for (cur = list; cur; cur = cur->link) {
505 n_chars += cur->lenght;
507 if (n_chars > n_max_chars || n == n_max_arg) {
511 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT */
513 /* allocating pointers for execvp:
514 a*arg, n*arg from stdin, NULL */
515 args = xcalloc(n + a + 1, sizeof(char *));
517 /* Store the command to be executed
518 (taken from the command line) */
519 for (i = 0; i < a; i++)
521 /* (taken from stdin) */
522 for (cur = list; n; cur = cur->link) {
523 args[i++] = cur->data;
527 if ((opt & (OPT_INTERACTIVE | OPT_VERBOSE))) {
528 for (i = 0; args[i]; i++) {
531 fputs(args[i], stderr);
533 if ((opt & OPT_INTERACTIVE) == 0)
536 if ((opt & OPT_INTERACTIVE) == 0 || xargs_ask_confirmation() != 0) {
537 child_error = xargs_exec(args);
541 for (i = a; args[i]; i++) {
547 if (child_error > 0 && child_error != 123) {
551 #ifdef CONFIG_FEATURE_CLEAN_UP
560 const char *bb_applet_name = "debug stuff usage";
562 void bb_show_usage(void)
564 fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
569 int main(int argc, char **argv)
571 return xargs_main(argc, argv);