1/* vi: set sw=4 ts=4: */ 2/* 3 * Mini xargs implementation for busybox 4 * Options are supported: "-prtx -n max_arg -s max_chars -e[ouf_str]" 5 * 6 * (C) 2002,2003 by Vladimir Oleynik <dzo@simtreas.ru> 7 * 8 * Special thanks 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>. 12 * 13 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. 14 * 15 * xargs is described in the Single Unix Specification v3 at 16 * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html 17 * 18 */ 19 20#include "libbb.h" 21 22/* This is a NOEXEC applet. Be very careful! */ 23 24 25/* COMPAT: SYSV version defaults size (and has a max value of) to 470. 26 We try to make it as large as possible. */ 27#if !defined(ARG_MAX) && defined(_SC_ARG_MAX) 28#define ARG_MAX sysconf (_SC_ARG_MAX) 29#endif 30#ifndef ARG_MAX 31#define ARG_MAX 470 32#endif 33 34 35#ifdef TEST 36# ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 37# define ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 1 38# endif 39# ifndef ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 40# define ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 1 41# endif 42# ifndef ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 43# define ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 1 44# endif 45# ifndef ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 46# define ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 1 47# endif 48#endif 49 50/* 51 This function has special algorithm. 52 Don't use fork and include to main! 53*/ 54static int xargs_exec(char **args) 55{ 56 int status; 57 58 status = spawn_and_wait(args); 59 if (status < 0) { 60 bb_perror_msg("%s", args[0]); 61 return errno == ENOENT ? 127 : 126; 62 } 63 if (status == 255) { 64 bb_error_msg("%s: exited with status 255; aborting", args[0]); 65 return 124; 66 } 67/* Huh? I think we won't see this, ever. We don't wait with WUNTRACED! 68 if (WIFSTOPPED(status)) { 69 bb_error_msg("%s: stopped by signal %d", 70 args[0], WSTOPSIG(status)); 71 return 125; 72 } 73*/ 74 if (status >= 1000) { 75 bb_error_msg("%s: terminated by signal %d", 76 args[0], status - 1000); 77 return 125; 78 } 79 if (status) 80 return 123; 81 return 0; 82} 83 84 85typedef struct xlist_t { 86 struct xlist_t *link; 87 size_t length; 88 char xstr[1]; 89} xlist_t; 90 91static smallint eof_stdin_detected; 92 93#define ISBLANK(c) ((c) == ' ' || (c) == '\t') 94#define ISSPACE(c) (ISBLANK(c) || (c) == '\n' || (c) == '\r' \ 95 || (c) == '\f' || (c) == '\v') 96 97#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 98static xlist_t *process_stdin(xlist_t *list_arg, 99 const char *eof_str, size_t mc, char *buf) 100{ 101#define NORM 0 102#define QUOTE 1 103#define BACKSLASH 2 104#define SPACE 4 105 106 char *s = NULL; /* start word */ 107 char *p = NULL; /* pointer to end word */ 108 char q = '\0'; /* quote char */ 109 char state = NORM; 110 char eof_str_detected = 0; 111 size_t line_l = 0; /* size loaded args line */ 112 int c; /* current char */ 113 xlist_t *cur; 114 xlist_t *prev; 115 116 prev = cur = list_arg; 117 while (1) { 118 if (!cur) break; 119 prev = cur; 120 line_l += cur->length; 121 cur = cur->link; 122 } 123 124 while (!eof_stdin_detected) { 125 c = getchar(); 126 if (c == EOF) { 127 eof_stdin_detected = 1; 128 if (s) 129 goto unexpected_eof; 130 break; 131 } 132 if (eof_str_detected) 133 continue; 134 if (state == BACKSLASH) { 135 state = NORM; 136 goto set; 137 } else if (state == QUOTE) { 138 if (c != q) 139 goto set; 140 q = '\0'; 141 state = NORM; 142 } else { /* if (state == NORM) */ 143 if (ISSPACE(c)) { 144 if (s) { 145 unexpected_eof: 146 state = SPACE; 147 c = '\0'; 148 goto set; 149 } 150 } else { 151 if (s == NULL) 152 s = p = buf; 153 if (c == '\\') { 154 state = BACKSLASH; 155 } else if (c == '\'' || c == '"') { 156 q = c; 157 state = QUOTE; 158 } else { 159 set: 160 if ((size_t)(p - buf) >= mc) 161 bb_error_msg_and_die("argument line too long"); 162 *p++ = c; 163 } 164 } 165 } 166 if (state == SPACE) { /* word's delimiter or EOF detected */ 167 if (q) { 168 bb_error_msg_and_die("unmatched %s quote", 169 q == '\'' ? "single" : "double"); 170 } 171 /* word loaded */ 172 if (eof_str) { 173 eof_str_detected = (strcmp(s, eof_str) == 0); 174 } 175 if (!eof_str_detected) { 176 size_t length = (p - buf); 177 /* Dont xzalloc - it can be quite big */ 178 cur = xmalloc(offsetof(xlist_t, xstr) + length); 179 cur->link = NULL; 180 cur->length = length; 181 memcpy(cur->xstr, s, length); 182 if (prev == NULL) { 183 list_arg = cur; 184 } else { 185 prev->link = cur; 186 } 187 prev = cur; 188 line_l += length; 189 if (line_l > mc) { 190 /* stop memory usage :-) */ 191 break; 192 } 193 } 194 s = NULL; 195 state = NORM; 196 } 197 } 198 return list_arg; 199} 200#else 201/* The variant does not support single quotes, double quotes or backslash */ 202static xlist_t *process_stdin(xlist_t *list_arg, 203 const char *eof_str, size_t mc, char *buf) 204{ 205 206 int c; /* current char */ 207 char eof_str_detected = 0; 208 char *s = NULL; /* start word */ 209 char *p = NULL; /* pointer to end word */ 210 size_t line_l = 0; /* size loaded args line */ 211 xlist_t *cur; 212 xlist_t *prev; 213 214 prev = cur = list_arg; 215 while (1) { 216 if (!cur) break; 217 prev = cur; 218 line_l += cur->length; 219 cur = cur->link; 220 } 221 222 while (!eof_stdin_detected) { 223 c = getchar(); 224 if (c == EOF) { 225 eof_stdin_detected = 1; 226 } 227 if (eof_str_detected) 228 continue; 229 if (c == EOF || ISSPACE(c)) { 230 if (s == NULL) 231 continue; 232 c = EOF; 233 } 234 if (s == NULL) 235 s = p = buf; 236 if ((p - buf) >= mc) 237 bb_error_msg_and_die("argument line too long"); 238 *p++ = (c == EOF ? '\0' : c); 239 if (c == EOF) { /* word's delimiter or EOF detected */ 240 /* word loaded */ 241 if (eof_str) { 242 eof_str_detected = (strcmp(s, eof_str) == 0); 243 } 244 if (!eof_str_detected) { 245 size_t length = (p - buf); 246 /* Dont xzalloc - it can be quite big */ 247 cur = xmalloc(offsetof(xlist_t, xstr) + length); 248 cur->link = NULL; 249 cur->length = length; 250 memcpy(cur->xstr, s, length); 251 if (prev == NULL) { 252 list_arg = cur; 253 } else { 254 prev->link = cur; 255 } 256 prev = cur; 257 line_l += length; 258 if (line_l > mc) { 259 /* stop memory usage :-) */ 260 break; 261 } 262 s = NULL; 263 } 264 } 265 } 266 return list_arg; 267} 268#endif /* FEATURE_XARGS_SUPPORT_QUOTES */ 269 270 271#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 272/* Prompt the user for a response, and 273 if the user responds affirmatively, return true; 274 otherwise, return false. Uses "/dev/tty", not stdin. */ 275static int xargs_ask_confirmation(void) 276{ 277 FILE *tty_stream; 278 int c, savec; 279 280 tty_stream = xfopen(CURRENT_TTY, "r"); 281 fputs(" ?...", stderr); 282 fflush(stderr); 283 c = savec = getc(tty_stream); 284 while (c != EOF && c != '\n') 285 c = getc(tty_stream); 286 fclose(tty_stream); 287 return (savec == 'y' || savec == 'Y'); 288} 289#else 290# define xargs_ask_confirmation() 1 291#endif /* FEATURE_XARGS_SUPPORT_CONFIRMATION */ 292 293#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 294static xlist_t *process0_stdin(xlist_t *list_arg, 295 const char *eof_str ATTRIBUTE_UNUSED, size_t mc, char *buf) 296{ 297 int c; /* current char */ 298 char *s = NULL; /* start word */ 299 char *p = NULL; /* pointer to end word */ 300 size_t line_l = 0; /* size loaded args line */ 301 xlist_t *cur; 302 xlist_t *prev; 303 304 prev = cur = list_arg; 305 while (1) { 306 if (!cur) break; 307 prev = cur; 308 line_l += cur->length; 309 cur = cur->link; 310 } 311 312 while (!eof_stdin_detected) { 313 c = getchar(); 314 if (c == EOF) { 315 eof_stdin_detected = 1; 316 if (s == NULL) 317 break; 318 c = '\0'; 319 } 320 if (s == NULL) 321 s = p = buf; 322 if ((size_t)(p - buf) >= mc) 323 bb_error_msg_and_die("argument line too long"); 324 *p++ = c; 325 if (c == '\0') { /* word's delimiter or EOF detected */ 326 /* word loaded */ 327 size_t length = (p - buf); 328 /* Dont xzalloc - it can be quite big */ 329 cur = xmalloc(offsetof(xlist_t, xstr) + length); 330 cur->link = NULL; 331 cur->length = length; 332 memcpy(cur->xstr, s, length); 333 if (prev == NULL) { 334 list_arg = cur; 335 } else { 336 prev->link = cur; 337 } 338 prev = cur; 339 line_l += length; 340 if (line_l > mc) { 341 /* stop memory usage :-) */ 342 break; 343 } 344 s = NULL; 345 } 346 } 347 return list_arg; 348} 349#endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */ 350 351/* Correct regardless of combination of CONFIG_xxx */ 352enum { 353 OPTBIT_VERBOSE = 0, 354 OPTBIT_NO_EMPTY, 355 OPTBIT_UPTO_NUMBER, 356 OPTBIT_UPTO_SIZE, 357 OPTBIT_EOF_STRING, 358 USE_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,) 359 USE_FEATURE_XARGS_SUPPORT_TERMOPT( OPTBIT_TERMINATE ,) 360 USE_FEATURE_XARGS_SUPPORT_ZERO_TERM( OPTBIT_ZEROTERM ,) 361 362 OPT_VERBOSE = 1<<OPTBIT_VERBOSE , 363 OPT_NO_EMPTY = 1<<OPTBIT_NO_EMPTY , 364 OPT_UPTO_NUMBER = 1<<OPTBIT_UPTO_NUMBER, 365 OPT_UPTO_SIZE = 1<<OPTBIT_UPTO_SIZE , 366 OPT_EOF_STRING = 1<<OPTBIT_EOF_STRING , 367 OPT_INTERACTIVE = USE_FEATURE_XARGS_SUPPORT_CONFIRMATION((1<<OPTBIT_INTERACTIVE)) + 0, 368 OPT_TERMINATE = USE_FEATURE_XARGS_SUPPORT_TERMOPT( (1<<OPTBIT_TERMINATE )) + 0, 369 OPT_ZEROTERM = USE_FEATURE_XARGS_SUPPORT_ZERO_TERM( (1<<OPTBIT_ZEROTERM )) + 0, 370}; 371#define OPTION_STR "+trn:s:e::" \ 372 USE_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \ 373 USE_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \ 374 USE_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") 375 376int xargs_main(int argc, char **argv); 377int xargs_main(int argc, char **argv) 378{ 379 char **args; 380 int i, n; 381 xlist_t *list = NULL; 382 xlist_t *cur; 383 int child_error = 0; 384 char *max_args, *max_chars; 385 int n_max_arg; 386 size_t n_chars = 0; 387 long orig_arg_max; 388 const char *eof_str = "_"; 389 unsigned opt; 390 size_t n_max_chars; 391#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 392 xlist_t* (*read_args)(xlist_t*, const char*, size_t, char*) = process_stdin; 393#else 394#define read_args process_stdin 395#endif 396 397 opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &eof_str); 398 399 if (opt & OPT_ZEROTERM) 400 USE_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin); 401 402 argv += optind; 403 argc -= optind; 404 if (!argc) { 405 /* default behavior is to echo all the filenames */ 406 *argv = (char*)"echo"; 407 argc++; 408 } 409 410 orig_arg_max = ARG_MAX; 411 if (orig_arg_max == -1) 412 orig_arg_max = LONG_MAX; 413 orig_arg_max -= 2048; /* POSIX.2 requires subtracting 2048 */ 414 415 if (opt & OPT_UPTO_SIZE) { 416 n_max_chars = xatoul_range(max_chars, 1, orig_arg_max); 417 for (i = 0; i < argc; i++) { 418 n_chars += strlen(*argv) + 1; 419 } 420 if (n_max_chars < n_chars) { 421 bb_error_msg_and_die("cannot fit single argument within argument list size limit"); 422 } 423 n_max_chars -= n_chars; 424 } else { 425 /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which 426 have it at 1 meg). Things will work fine with a large ARG_MAX but it 427 will probably hurt the system more than it needs to; an array of this 428 size is allocated. */ 429 if (orig_arg_max > 20 * 1024) 430 orig_arg_max = 20 * 1024; 431 n_max_chars = orig_arg_max; 432 } 433 max_chars = xmalloc(n_max_chars); 434 435 if (opt & OPT_UPTO_NUMBER) { 436 n_max_arg = xatoul_range(max_args, 1, INT_MAX); 437 } else { 438 n_max_arg = n_max_chars; 439 } 440 441 while ((list = read_args(list, eof_str, n_max_chars, max_chars)) != NULL || 442 !(opt & OPT_NO_EMPTY)) 443 { 444 opt |= OPT_NO_EMPTY; 445 n = 0; 446 n_chars = 0; 447#if ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 448 for (cur = list; cur;) { 449 n_chars += cur->length; 450 n++; 451 cur = cur->link; 452 if (n_chars > n_max_chars || (n == n_max_arg && cur)) { 453 if (opt & OPT_TERMINATE) 454 bb_error_msg_and_die("argument list too long"); 455 break; 456 } 457 } 458#else 459 for (cur = list; cur; cur = cur->link) { 460 n_chars += cur->length; 461 n++; 462 if (n_chars > n_max_chars || n == n_max_arg) { 463 break; 464 } 465 } 466#endif /* FEATURE_XARGS_SUPPORT_TERMOPT */ 467 468 /* allocate pointers for execvp: 469 argc*arg, n*arg from stdin, NULL */ 470 args = xzalloc((n + argc + 1) * sizeof(char *)); 471 472 /* store the command to be executed 473 (taken from the command line) */ 474 for (i = 0; i < argc; i++) 475 args[i] = argv[i]; 476 /* (taken from stdin) */ 477 for (cur = list; n; cur = cur->link) { 478 args[i++] = cur->xstr; 479 n--; 480 } 481 482 if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) { 483 for (i = 0; args[i]; i++) { 484 if (i) 485 fputc(' ', stderr); 486 fputs(args[i], stderr); 487 } 488 if (!(opt & OPT_INTERACTIVE)) 489 fputc('\n', stderr); 490 } 491 if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) { 492 child_error = xargs_exec(args); 493 } 494 495 /* clean up */ 496 for (i = argc; args[i]; i++) { 497 cur = list; 498 list = list->link; 499 free(cur); 500 } 501 free(args); 502 if (child_error > 0 && child_error != 123) { 503 break; 504 } 505 } 506 if (ENABLE_FEATURE_CLEAN_UP) 507 free(max_chars); 508 return child_error; 509} 510 511 512#ifdef TEST 513 514const char *applet_name = "debug stuff usage"; 515 516void bb_show_usage(void) 517{ 518 fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n", 519 applet_name); 520 exit(1); 521} 522 523int main(int argc, char **argv) 524{ 525 return xargs_main(argc, argv); 526} 527#endif /* TEST */ 528