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 the GPL v2 or later, see the file LICENSE in this tarball. 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//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, _BB_DIR_USR_BIN, _BB_SUID_DROP, xargs)) 19 20//kbuild:lib-$(CONFIG_XARGS) += xargs.o 21 22//config:config XARGS 23//config: bool "xargs" 24//config: default y 25//config: help 26//config: xargs is used to execute a specified command for 27//config: every item from standard input. 28//config: 29//config:config FEATURE_XARGS_SUPPORT_CONFIRMATION 30//config: bool "Enable -p: prompt and confirmation" 31//config: default y 32//config: depends on XARGS 33//config: help 34//config: Support -p: prompt the user whether to run each command 35//config: line and read a line from the terminal. 36//config: 37//config:config FEATURE_XARGS_SUPPORT_QUOTES 38//config: bool "Enable single and double quotes and backslash" 39//config: default y 40//config: depends on XARGS 41//config: help 42//config: Support quoting in the input. 43//config: 44//config:config FEATURE_XARGS_SUPPORT_TERMOPT 45//config: bool "Enable -x: exit if -s or -n is exceeded" 46//config: default y 47//config: depends on XARGS 48//config: help 49//config: Support -x: exit if the command size (see the -s or -n option) 50//config: is exceeded. 51//config: 52//config:config FEATURE_XARGS_SUPPORT_ZERO_TERM 53//config: bool "Enable -0: NUL-terminated input" 54//config: default y 55//config: depends on XARGS 56//config: help 57//config: Support -0: input items are terminated by a NUL character 58//config: instead of whitespace, and the quotes and backslash 59//config: are not special. 60 61#include "libbb.h" 62 63/* This is a NOEXEC applet. Be very careful! */ 64 65 66//#define dbg_msg(...) bb_error_msg(__VA_ARGS__) 67#define dbg_msg(...) ((void)0) 68 69 70#ifdef TEST 71# ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 72# define ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 1 73# endif 74# ifndef ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 75# define ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 1 76# endif 77# ifndef ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 78# define ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 1 79# endif 80# ifndef ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 81# define ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 1 82# endif 83#endif 84 85 86struct globals { 87 char **args; 88 const char *eof_str; 89 int idx; 90} FIX_ALIASING; 91#define G (*(struct globals*)&bb_common_bufsiz1) 92#define INIT_G() do { } while (0) 93 94 95/* 96 * This function has special algorithm. 97 * Don't use fork and include to main! 98 */ 99static int xargs_exec(void) 100{ 101 int status; 102 103 status = spawn_and_wait(G.args); 104 if (status < 0) { 105 bb_simple_perror_msg(G.args[0]); 106 return errno == ENOENT ? 127 : 126; 107 } 108 if (status == 255) { 109 bb_error_msg("%s: exited with status 255; aborting", G.args[0]); 110 return 124; 111 } 112 if (status >= 0x180) { 113 bb_error_msg("%s: terminated by signal %d", 114 G.args[0], status - 0x180); 115 return 125; 116 } 117 if (status) 118 return 123; 119 return 0; 120} 121 122/* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space. 123 * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13. 124 */ 125#define ISSPACE(a) ({ unsigned char xargs__isspace = (a) - 9; xargs__isspace == (' ' - 9) || xargs__isspace <= (13 - 9); }) 126 127static void store_param(char *s) 128{ 129 /* Grow by 256 elements at once */ 130 if (!(G.idx & 0xff)) { /* G.idx == N*256 */ 131 /* Enlarge, make G.args[(N+1)*256 - 1] last valid idx */ 132 G.args = xrealloc(G.args, sizeof(G.args[0]) * (G.idx + 0x100)); 133 } 134 G.args[G.idx++] = s; 135} 136 137/* process[0]_stdin: 138 * Read characters into buf[n_max_chars+1], and when parameter delimiter 139 * is seen, store the address of a new parameter to args[]. 140 * If reading discovers that last chars do not form the complete 141 * parameter, the pointer to the first such "tail character" is returned. 142 * (buf has extra byte at the end to accomodate terminating NUL 143 * of "tail characters" string). 144 * Otherwise, the returned pointer points to NUL byte. 145 * On entry, buf[] may contain some "seed chars" which are to become 146 * the beginning of the first parameter. 147 */ 148 149#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 150static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf) 151{ 152#define NORM 0 153#define QUOTE 1 154#define BACKSLASH 2 155#define SPACE 4 156 char q = '\0'; /* quote char */ 157 char state = NORM; 158 char *s = buf; /* start of the word */ 159 char *p = s + strlen(buf); /* end of the word */ 160 161 buf += n_max_chars; /* past buffer's end */ 162 163 /* "goto ret" is used instead of "break" to make control flow 164 * more obvious: */ 165 166 while (1) { 167 int c = getchar(); 168 if (c == EOF) { 169 if (p != s) 170 goto close_word; 171 goto ret; 172 } 173 if (state == BACKSLASH) { 174 state = NORM; 175 goto set; 176 } 177 if (state == QUOTE) { 178 if (c != q) 179 goto set; 180 q = '\0'; 181 state = NORM; 182 } else { /* if (state == NORM) */ 183 if (ISSPACE(c)) { 184 if (p != s) { 185 close_word: 186 state = SPACE; 187 c = '\0'; 188 goto set; 189 } 190 } else { 191 if (c == '\\') { 192 state = BACKSLASH; 193 } else if (c == '\'' || c == '"') { 194 q = c; 195 state = QUOTE; 196 } else { 197 set: 198 *p++ = c; 199 } 200 } 201 } 202 if (state == SPACE) { /* word's delimiter or EOF detected */ 203 if (q) { 204 bb_error_msg_and_die("unmatched %s quote", 205 q == '\'' ? "single" : "double"); 206 } 207 /* A full word is loaded */ 208 if (G.eof_str) { 209 if (strcmp(s, G.eof_str) == 0) { 210 while (getchar() != EOF) 211 continue; 212 p = s; 213 goto ret; 214 } 215 } 216 store_param(s); 217 dbg_msg("args[]:'%s'", s); 218 s = p; 219 n_max_arg--; 220 if (n_max_arg == 0) { 221 goto ret; 222 } 223 state = NORM; 224 } 225 if (p == buf) { 226 goto ret; 227 } 228 } 229 ret: 230 *p = '\0'; 231 /* store_param(NULL) - caller will do it */ 232 dbg_msg("return:'%s'", s); 233 return s; 234} 235#else 236/* The variant does not support single quotes, double quotes or backslash */ 237static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf) 238{ 239 char *s = buf; /* start of the word */ 240 char *p = s + strlen(buf); /* end of the word */ 241 242 buf += n_max_chars; /* past buffer's end */ 243 244 while (1) { 245 int c = getchar(); 246 if (c == EOF) { 247 if (p == s) 248 goto ret; 249 } 250 if (c == EOF || ISSPACE(c)) { 251 if (p == s) 252 continue; 253 c = EOF; 254 } 255 *p++ = (c == EOF ? '\0' : c); 256 if (c == EOF) { /* word's delimiter or EOF detected */ 257 /* A full word is loaded */ 258 if (G.eof_str) { 259 if (strcmp(s, G.eof_str) == 0) { 260 while (getchar() != EOF) 261 continue; 262 p = s; 263 goto ret; 264 } 265 } 266 store_param(s); 267 dbg_msg("args[]:'%s'", s); 268 s = p; 269 n_max_arg--; 270 if (n_max_arg == 0) { 271 goto ret; 272 } 273 } 274 if (p == buf) { 275 goto ret; 276 } 277 } 278 ret: 279 *p = '\0'; 280 /* store_param(NULL) - caller will do it */ 281 dbg_msg("return:'%s'", s); 282 return s; 283} 284#endif /* FEATURE_XARGS_SUPPORT_QUOTES */ 285 286#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 287static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf) 288{ 289 char *s = buf; /* start of the word */ 290 char *p = s + strlen(buf); /* end of the word */ 291 292 buf += n_max_chars; /* past buffer's end */ 293 294 while (1) { 295 int c = getchar(); 296 if (c == EOF) { 297 if (p == s) 298 goto ret; 299 c = '\0'; 300 } 301 *p++ = c; 302 if (c == '\0') { /* word's delimiter or EOF detected */ 303 /* A full word is loaded */ 304 store_param(s); 305 dbg_msg("args[]:'%s'", s); 306 s = p; 307 n_max_arg--; 308 if (n_max_arg == 0) { 309 goto ret; 310 } 311 } 312 if (p == buf) { 313 goto ret; 314 } 315 } 316 ret: 317 *p = '\0'; 318 /* store_param(NULL) - caller will do it */ 319 dbg_msg("return:'%s'", s); 320 return s; 321} 322#endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */ 323 324#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 325/* Prompt the user for a response, and 326 if the user responds affirmatively, return true; 327 otherwise, return false. Uses "/dev/tty", not stdin. */ 328static int xargs_ask_confirmation(void) 329{ 330 FILE *tty_stream; 331 int c, savec; 332 333 tty_stream = xfopen_for_read(CURRENT_TTY); 334 fputs(" ?...", stderr); 335 fflush_all(); 336 c = savec = getc(tty_stream); 337 while (c != EOF && c != '\n') 338 c = getc(tty_stream); 339 fclose(tty_stream); 340 return (savec == 'y' || savec == 'Y'); 341} 342#else 343# define xargs_ask_confirmation() 1 344#endif 345 346//usage:#define xargs_trivial_usage 347//usage: "[OPTIONS] [PROG ARGS]" 348//usage:#define xargs_full_usage "\n\n" 349//usage: "Run PROG on every item given by stdin\n" 350//usage: "\nOptions:" 351//usage: IF_FEATURE_XARGS_SUPPORT_CONFIRMATION( 352//usage: "\n -p Ask user whether to run each command" 353//usage: ) 354//usage: "\n -r Don't run command if input is empty" 355//usage: IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( 356//usage: "\n -0 Input is separated by NUL characters" 357//usage: ) 358//usage: "\n -t Print the command on stderr before execution" 359//usage: "\n -e[STR] STR stops input processing" 360//usage: "\n -n N Pass no more than N args to PROG" 361//usage: "\n -s N Pass command line of no more than N bytes" 362//usage: IF_FEATURE_XARGS_SUPPORT_TERMOPT( 363//usage: "\n -x Exit if size is exceeded" 364//usage: ) 365//usage:#define xargs_example_usage 366//usage: "$ ls | xargs gzip\n" 367//usage: "$ find . -name '*.c' -print | xargs rm\n" 368 369/* Correct regardless of combination of CONFIG_xxx */ 370enum { 371 OPTBIT_VERBOSE = 0, 372 OPTBIT_NO_EMPTY, 373 OPTBIT_UPTO_NUMBER, 374 OPTBIT_UPTO_SIZE, 375 OPTBIT_EOF_STRING, 376 OPTBIT_EOF_STRING1, 377 IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,) 378 IF_FEATURE_XARGS_SUPPORT_TERMOPT( OPTBIT_TERMINATE ,) 379 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( OPTBIT_ZEROTERM ,) 380 381 OPT_VERBOSE = 1 << OPTBIT_VERBOSE , 382 OPT_NO_EMPTY = 1 << OPTBIT_NO_EMPTY , 383 OPT_UPTO_NUMBER = 1 << OPTBIT_UPTO_NUMBER, 384 OPT_UPTO_SIZE = 1 << OPTBIT_UPTO_SIZE , 385 OPT_EOF_STRING = 1 << OPTBIT_EOF_STRING , /* GNU: -e[<param>] */ 386 OPT_EOF_STRING1 = 1 << OPTBIT_EOF_STRING1, /* SUS: -E<param> */ 387 OPT_INTERACTIVE = IF_FEATURE_XARGS_SUPPORT_CONFIRMATION((1 << OPTBIT_INTERACTIVE)) + 0, 388 OPT_TERMINATE = IF_FEATURE_XARGS_SUPPORT_TERMOPT( (1 << OPTBIT_TERMINATE )) + 0, 389 OPT_ZEROTERM = IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( (1 << OPTBIT_ZEROTERM )) + 0, 390}; 391#define OPTION_STR "+trn:s:e::E:" \ 392 IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \ 393 IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \ 394 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") 395 396int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 397int xargs_main(int argc, char **argv) 398{ 399 int i; 400 int child_error = 0; 401 char *max_args; 402 char *max_chars; 403 char *buf; 404 unsigned opt; 405 int n_max_chars; 406 int n_max_arg; 407#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 408 char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin; 409#else 410#define read_args process_stdin 411#endif 412 413 INIT_G(); 414 415 G.eof_str = NULL; 416 opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &G.eof_str, &G.eof_str); 417 418 /* -E ""? You may wonder why not just omit -E? 419 * This is used for portability: 420 * old xargs was using "_" as default for -E / -e */ 421 if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0') 422 G.eof_str = NULL; 423 424 if (opt & OPT_ZEROTERM) 425 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin); 426 427 argv += optind; 428 argc -= optind; 429 if (!argv[0]) { 430 /* default behavior is to echo all the filenames */ 431 *--argv = (char*)"echo"; 432 argc++; 433 } 434 435 /* -s NUM default. fileutils-4.4.2 uses 128k, but I heasitate 436 * to use such a big value - first need to change code to use 437 * growable buffer instead of fixed one. 438 */ 439 n_max_chars = 32 * 1024; 440 /* Make smaller if system does not allow our default value. 441 * The Open Group Base Specifications Issue 6: 442 * "The xargs utility shall limit the command line length such that 443 * when the command line is invoked, the combined argument 444 * and environment lists (see the exec family of functions 445 * in the System Interfaces volume of IEEE Std 1003.1-2001) 446 * shall not exceed {ARG_MAX}-2048 bytes". 447 */ 448 { 449 long arg_max = 0; 450#if defined _SC_ARG_MAX 451 arg_max = sysconf(_SC_ARG_MAX) - 2048; 452#elif defined ARG_MAX 453 arg_max = ARG_MAX - 2048; 454#endif 455 if (arg_max > 0 && n_max_chars > arg_max) 456 n_max_chars = arg_max; 457 } 458 if (opt & OPT_UPTO_SIZE) { 459 n_max_chars = xatou_range(max_chars, 1, INT_MAX); 460 } 461 /* Account for prepended fixed arguments */ 462 { 463 size_t n_chars = 0; 464 for (i = 0; argv[i]; i++) { 465 n_chars += strlen(argv[i]) + 1; 466 } 467 n_max_chars -= n_chars; 468 } 469 /* Sanity check */ 470 if (n_max_chars <= 0) { 471 bb_error_msg_and_die("can't fit single argument within argument list size limit"); 472 } 473 474 buf = xzalloc(n_max_chars + 1); 475 476 n_max_arg = n_max_chars; 477 if (opt & OPT_UPTO_NUMBER) { 478 n_max_arg = xatou_range(max_args, 1, INT_MAX); 479 /* Not necessary, we use growable args[]: */ 480 /* if (n_max_arg > n_max_chars) n_max_arg = n_max_chars */ 481 } 482 483 /* Allocate pointers for execvp */ 484 /* We can statically allocate (argc + n_max_arg + 1) elements 485 * and do not bother with resizing args[], but on 64-bit machines 486 * this results in args[] vector which is ~8 times bigger 487 * than n_max_chars! That is, with n_max_chars == 20k, 488 * args[] will take 160k (!), which will most likely be 489 * almost entirely unused. 490 */ 491 /* See store_param() for matching 256-step growth logic */ 492 G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff)); 493 494 /* Store the command to be executed, part 1 */ 495 for (i = 0; argv[i]; i++) 496 G.args[i] = argv[i]; 497 498 while (1) { 499 char *rem; 500 501 G.idx = argc; 502 rem = read_args(n_max_chars, n_max_arg, buf); 503 store_param(NULL); 504 505 if (!G.args[argc]) { 506 if (*rem != '\0') 507 bb_error_msg_and_die("argument line too long"); 508 if (opt & OPT_NO_EMPTY) 509 break; 510 } 511 opt |= OPT_NO_EMPTY; 512 513 if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) { 514 const char *fmt = " %s" + 1; 515 char **args = G.args; 516 for (i = 0; args[i]; i++) { 517 fprintf(stderr, fmt, args[i]); 518 fmt = " %s"; 519 } 520 if (!(opt & OPT_INTERACTIVE)) 521 bb_putchar_stderr('\n'); 522 } 523 524 if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) { 525 child_error = xargs_exec(); 526 } 527 528 if (child_error > 0 && child_error != 123) { 529 break; 530 } 531 532 overlapping_strcpy(buf, rem); 533 } /* while */ 534 535 if (ENABLE_FEATURE_CLEAN_UP) { 536 free(G.args); 537 free(buf); 538 } 539 540 return child_error; 541} 542 543 544#ifdef TEST 545 546const char *applet_name = "debug stuff usage"; 547 548void bb_show_usage(void) 549{ 550 fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n", 551 applet_name); 552 exit(EXIT_FAILURE); 553} 554 555int main(int argc, char **argv) 556{ 557 return xargs_main(argc, argv); 558} 559#endif /* TEST */ 560