aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2014-02-27 11:17:06 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2014-02-27 11:17:06 +0100
commit6f068904dc142657bb596f91196f9113f1838cbe (patch)
treeac02741b3c8b74bf3a078aeb13ef7ff1c5e1e23f
parent6885e49ba596239a0b0d3631fd72fc2692fbb65c (diff)
downloadbusybox-w32-6f068904dc142657bb596f91196f9113f1838cbe.tar.gz
busybox-w32-6f068904dc142657bb596f91196f9113f1838cbe.tar.bz2
busybox-w32-6f068904dc142657bb596f91196f9113f1838cbe.zip
xargs: add support for -I and -i. Closes 493
function old new delta process_stdin_with_replace - 195 +195 xmalloc_substitute_string - 145 +145 xargs_main 808 884 +76 count_strstr - 45 +45 packed_usage 29580 29571 -9 parse_params 1445 1416 -29 func_exec 285 138 -147 ------------------------------------------------------------------------------ (add/remove: 4/0 grow/shrink: 1/3 up/down: 461/-185) Total: 276 bytes text data bss dec hex filename 922156 932 17692 940780 e5aec busybox_old 922440 932 17692 941064 e5c08 busybox_unstripped Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--findutils/find.c32
-rw-r--r--findutils/xargs.c153
-rw-r--r--include/libbb.h2
-rw-r--r--libbb/replace.c45
4 files changed, 176 insertions, 56 deletions
diff --git a/findutils/find.c b/findutils/find.c
index 044f010b0..6d34f4d68 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -402,34 +402,6 @@ struct globals {
402 G.recurse_flags = ACTION_RECURSE; \ 402 G.recurse_flags = ACTION_RECURSE; \
403} while (0) 403} while (0)
404 404
405#if ENABLE_FEATURE_FIND_EXEC
406static unsigned count_subst(const char *str)
407{
408 unsigned count = 0;
409 while ((str = strstr(str, "{}")) != NULL) {
410 count++;
411 str++;
412 }
413 return count;
414}
415
416
417static char* subst(const char *src, unsigned count, const char* filename)
418{
419 char *buf, *dst, *end;
420 size_t flen = strlen(filename);
421 /* we replace each '{}' with filename: growth by strlen-2 */
422 buf = dst = xmalloc(strlen(src) + count*(flen-2) + 1);
423 while ((end = strstr(src, "{}")) != NULL) {
424 dst = mempcpy(dst, src, end - src);
425 dst = mempcpy(dst, filename, flen);
426 src = end + 2;
427 }
428 strcpy(dst, src);
429 return buf;
430}
431#endif
432
433/* Return values of ACTFs ('action functions') are a bit mask: 405/* Return values of ACTFs ('action functions') are a bit mask:
434 * bit 1=1: prune (use SKIP constant for setting it) 406 * bit 1=1: prune (use SKIP constant for setting it)
435 * bit 0=1: matched successfully (TRUE) 407 * bit 0=1: matched successfully (TRUE)
@@ -613,7 +585,7 @@ ACTF(exec)
613 char *argv[ap->exec_argc + 1]; 585 char *argv[ap->exec_argc + 1];
614#endif 586#endif
615 for (i = 0; i < ap->exec_argc; i++) 587 for (i = 0; i < ap->exec_argc; i++)
616 argv[i] = subst(ap->exec_argv[i], ap->subst_count[i], fileName); 588 argv[i] = xmalloc_substitute_string(ap->exec_argv[i], ap->subst_count[i], "{}", fileName);
617 argv[i] = NULL; /* terminate the list */ 589 argv[i] = NULL; /* terminate the list */
618 590
619 rc = spawn_and_wait(argv); 591 rc = spawn_and_wait(argv);
@@ -1091,7 +1063,7 @@ static action*** parse_params(char **argv)
1091 ap->subst_count = xmalloc(ap->exec_argc * sizeof(int)); 1063 ap->subst_count = xmalloc(ap->exec_argc * sizeof(int));
1092 i = ap->exec_argc; 1064 i = ap->exec_argc;
1093 while (i--) 1065 while (i--)
1094 ap->subst_count[i] = count_subst(ap->exec_argv[i]); 1066 ap->subst_count[i] = count_strstr(ap->exec_argv[i], "{}");
1095 } 1067 }
1096#endif 1068#endif
1097#if ENABLE_FEATURE_FIND_PAREN 1069#if ENABLE_FEATURE_FIND_PAREN
diff --git a/findutils/xargs.c b/findutils/xargs.c
index 0d1bb43fc..ed6dbd33e 100644
--- a/findutils/xargs.c
+++ b/findutils/xargs.c
@@ -53,6 +53,13 @@
53//config: Support -0: input items are terminated by a NUL character 53//config: Support -0: input items are terminated by a NUL character
54//config: instead of whitespace, and the quotes and backslash 54//config: instead of whitespace, and the quotes and backslash
55//config: are not special. 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.
56 63
57//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs)) 64//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs))
58 65
@@ -85,19 +92,22 @@
85 92
86struct globals { 93struct globals {
87 char **args; 94 char **args;
95#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
96 char **argv;
97 const char *repl_str;
98 char eol_ch;
99#endif
88 const char *eof_str; 100 const char *eof_str;
89 int idx; 101 int idx;
90} FIX_ALIASING; 102} FIX_ALIASING;
91#define G (*(struct globals*)&bb_common_bufsiz1) 103#define G (*(struct globals*)&bb_common_bufsiz1)
92#define INIT_G() do { \ 104#define INIT_G() do { \
93 G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \ 105 G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \
106 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \
107 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \
94} while (0) 108} while (0)
95 109
96 110
97/*
98 * This function has special algorithm.
99 * Don't use fork and include to main!
100 */
101static int xargs_exec(void) 111static int xargs_exec(void)
102{ 112{
103 int status; 113 int status;
@@ -301,7 +311,7 @@ static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf)
301 c = '\0'; 311 c = '\0';
302 } 312 }
303 *p++ = c; 313 *p++ = c;
304 if (c == '\0') { /* word's delimiter or EOF detected */ 314 if (c == '\0') { /* NUL or EOF detected */
305 /* A full word is loaded */ 315 /* A full word is loaded */
306 store_param(s); 316 store_param(s);
307 dbg_msg("args[]:'%s'", s); 317 dbg_msg("args[]:'%s'", s);
@@ -323,10 +333,71 @@ static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf)
323} 333}
324#endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */ 334#endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */
325 335
336#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
337/*
338 * Used if -I<repl> was specified.
339 * In this mode, words aren't appended to PROG ARGS.
340 * Instead, entire input line is read, then <repl> string
341 * in every PROG and ARG is replaced with the line:
342 * echo -e "ho ho\nhi" | xargs -I_ cmd __ _
343 * results in "cmd 'ho hoho ho' 'ho ho'"; "cmd 'hihi' 'hi'".
344 * -n MAX_ARGS seems to be ignored.
345 * Tested with GNU findutils 4.5.10.
346 */
347//FIXME: n_max_chars is not handled the same way as in GNU findutils.
348//FIXME: quoting is not implemented.
349static char* FAST_FUNC process_stdin_with_replace(int n_max_chars, int n_max_arg UNUSED_PARAM, char *buf)
350{
351 int i;
352 char *end, *p;
353
354 /* Free strings from last invocation, if any */
355 for (i = 0; G.args && G.args[i]; i++)
356 if (G.args[i] != G.argv[i])
357 free(G.args[i]);
358
359 end = buf + n_max_chars;
360 p = buf;
361
362 while (1) {
363 int c = getchar();
364 if (c == EOF || c == G.eol_ch) {
365 if (p == buf)
366 goto ret; /* empty line */
367 c = '\0';
368 }
369 *p++ = c;
370 if (c == '\0') { /* EOL or EOF detected */
371 i = 0;
372 while (G.argv[i]) {
373 char *arg = G.argv[i];
374 int count = count_strstr(arg, G.repl_str);
375 if (count != 0)
376 arg = xmalloc_substitute_string(arg, count, G.repl_str, s);
377 store_param(arg);
378 dbg_msg("args[]:'%s'", arg);
379 i++;
380 }
381 p = buf;
382 goto ret;
383 }
384 if (p == end) {
385 goto ret;
386 }
387 }
388 ret:
389 *p = '\0';
390 /* store_param(NULL) - caller will do it */
391 dbg_msg("return:'%s'", buf);
392 return buf;
393}
394#endif
395
326#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 396#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
327/* Prompt the user for a response, and 397/* Prompt the user for a response, and
328 if the user responds affirmatively, return true; 398 * if user responds affirmatively, return true;
329 otherwise, return false. Uses "/dev/tty", not stdin. */ 399 * otherwise, return false. Uses "/dev/tty", not stdin.
400 */
330static int xargs_ask_confirmation(void) 401static int xargs_ask_confirmation(void)
331{ 402{
332 FILE *tty_stream; 403 FILE *tty_stream;
@@ -360,6 +431,9 @@ static int xargs_ask_confirmation(void)
360//usage: "\n -e[STR] STR stops input processing" 431//usage: "\n -e[STR] STR stops input processing"
361//usage: "\n -n N Pass no more than N args to PROG" 432//usage: "\n -n N Pass no more than N args to PROG"
362//usage: "\n -s N Pass command line of no more than N bytes" 433//usage: "\n -s N Pass command line of no more than N bytes"
434//usage: IF_FEATURE_XARGS_SUPPORT_REPL_STR(
435//usage: "\n -I STR Replace STR within PROG ARGS with input line"
436//usage: )
363//usage: IF_FEATURE_XARGS_SUPPORT_TERMOPT( 437//usage: IF_FEATURE_XARGS_SUPPORT_TERMOPT(
364//usage: "\n -x Exit if size is exceeded" 438//usage: "\n -x Exit if size is exceeded"
365//usage: ) 439//usage: )
@@ -378,6 +452,8 @@ enum {
378 IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,) 452 IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,)
379 IF_FEATURE_XARGS_SUPPORT_TERMOPT( OPTBIT_TERMINATE ,) 453 IF_FEATURE_XARGS_SUPPORT_TERMOPT( OPTBIT_TERMINATE ,)
380 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( OPTBIT_ZEROTERM ,) 454 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( OPTBIT_ZEROTERM ,)
455 IF_FEATURE_XARGS_SUPPORT_REPL_STR( OPTBIT_REPLSTR ,)
456 IF_FEATURE_XARGS_SUPPORT_REPL_STR( OPTBIT_REPLSTR1 ,)
381 457
382 OPT_VERBOSE = 1 << OPTBIT_VERBOSE , 458 OPT_VERBOSE = 1 << OPTBIT_VERBOSE ,
383 OPT_NO_EMPTY = 1 << OPTBIT_NO_EMPTY , 459 OPT_NO_EMPTY = 1 << OPTBIT_NO_EMPTY ,
@@ -388,11 +464,14 @@ enum {
388 OPT_INTERACTIVE = IF_FEATURE_XARGS_SUPPORT_CONFIRMATION((1 << OPTBIT_INTERACTIVE)) + 0, 464 OPT_INTERACTIVE = IF_FEATURE_XARGS_SUPPORT_CONFIRMATION((1 << OPTBIT_INTERACTIVE)) + 0,
389 OPT_TERMINATE = IF_FEATURE_XARGS_SUPPORT_TERMOPT( (1 << OPTBIT_TERMINATE )) + 0, 465 OPT_TERMINATE = IF_FEATURE_XARGS_SUPPORT_TERMOPT( (1 << OPTBIT_TERMINATE )) + 0,
390 OPT_ZEROTERM = IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( (1 << OPTBIT_ZEROTERM )) + 0, 466 OPT_ZEROTERM = IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( (1 << OPTBIT_ZEROTERM )) + 0,
467 OPT_REPLSTR = IF_FEATURE_XARGS_SUPPORT_REPL_STR( (1 << OPTBIT_REPLSTR )) + 0,
468 OPT_REPLSTR1 = IF_FEATURE_XARGS_SUPPORT_REPL_STR( (1 << OPTBIT_REPLSTR1 )) + 0,
391}; 469};
392#define OPTION_STR "+trn:s:e::E:" \ 470#define OPTION_STR "+trn:s:e::E:" \
393 IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \ 471 IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
394 IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \ 472 IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \
395 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") 473 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") \
474 IF_FEATURE_XARGS_SUPPORT_REPL_STR( "I:i::")
396 475
397int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 476int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
398int xargs_main(int argc, char **argv) 477int xargs_main(int argc, char **argv)
@@ -405,7 +484,8 @@ int xargs_main(int argc, char **argv)
405 unsigned opt; 484 unsigned opt;
406 int n_max_chars; 485 int n_max_chars;
407 int n_max_arg; 486 int n_max_arg;
408#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 487#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM \
488 || ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
409 char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin; 489 char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin;
410#else 490#else
411#define read_args process_stdin 491#define read_args process_stdin
@@ -419,7 +499,10 @@ int xargs_main(int argc, char **argv)
419 "no-run-if-empty\0" No_argument "r" 499 "no-run-if-empty\0" No_argument "r"
420 ; 500 ;
421#endif 501#endif
422 opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &G.eof_str, &G.eof_str); 502 opt = getopt32(argv, OPTION_STR,
503 &max_args, &max_chars, &G.eof_str, &G.eof_str
504 IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str)
505 );
423 506
424 /* -E ""? You may wonder why not just omit -E? 507 /* -E ""? You may wonder why not just omit -E?
425 * This is used for portability: 508 * This is used for portability:
@@ -427,8 +510,10 @@ int xargs_main(int argc, char **argv)
427 if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0') 510 if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0')
428 G.eof_str = NULL; 511 G.eof_str = NULL;
429 512
430 if (opt & OPT_ZEROTERM) 513 if (opt & OPT_ZEROTERM) {
431 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin); 514 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin;)
515 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\0';)
516 }
432 517
433 argv += optind; 518 argv += optind;
434 argc -= optind; 519 argc -= optind;
@@ -486,20 +571,36 @@ int xargs_main(int argc, char **argv)
486 /* if (n_max_arg > n_max_chars) n_max_arg = n_max_chars */ 571 /* if (n_max_arg > n_max_chars) n_max_arg = n_max_chars */
487 } 572 }
488 573
489 /* Allocate pointers for execvp */ 574#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
490 /* We can statically allocate (argc + n_max_arg + 1) elements 575 if (opt & (OPT_REPLSTR | OPT_REPLSTR1)) {
491 * and do not bother with resizing args[], but on 64-bit machines 576 /*
492 * this results in args[] vector which is ~8 times bigger 577 * -I<str>:
493 * than n_max_chars! That is, with n_max_chars == 20k, 578 * Unmodified args are kept in G.argv[i],
494 * args[] will take 160k (!), which will most likely be 579 * G.args[i] receives malloced G.argv[i] with <str> replaced
495 * almost entirely unused. 580 * with input line. Setting this up:
496 */ 581 */
497 /* See store_param() for matching 256-step growth logic */ 582 G.args = NULL;
498 G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff)); 583 G.argv = argv;
499 584 argc = 0;
500 /* Store the command to be executed, part 1 */ 585 read_args = process_stdin_with_replace;
501 for (i = 0; argv[i]; i++) 586 } else
502 G.args[i] = argv[i]; 587#endif
588 {
589 /* Allocate pointers for execvp.
590 * We can statically allocate (argc + n_max_arg + 1) elements
591 * and do not bother with resizing args[], but on 64-bit machines
592 * this results in args[] vector which is ~8 times bigger
593 * than n_max_chars! That is, with n_max_chars == 20k,
594 * args[] will take 160k (!), which will most likely be
595 * almost entirely unused.
596 *
597 * See store_param() for matching 256-step growth logic
598 */
599 G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff));
600 /* Store the command to be executed, part 1 */
601 for (i = 0; argv[i]; i++)
602 G.args[i] = argv[i];
603 }
503 604
504 while (1) { 605 while (1) {
505 char *rem; 606 char *rem;
diff --git a/include/libbb.h b/include/libbb.h
index 96f33340e..1cbe2c8b4 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -650,6 +650,8 @@ char *xstrndup(const char *s, int n) FAST_FUNC RETURNS_MALLOC;
650void overlapping_strcpy(char *dst, const char *src) FAST_FUNC; 650void overlapping_strcpy(char *dst, const char *src) FAST_FUNC;
651char *safe_strncpy(char *dst, const char *src, size_t size) FAST_FUNC; 651char *safe_strncpy(char *dst, const char *src, size_t size) FAST_FUNC;
652char *strncpy_IFNAMSIZ(char *dst, const char *src) FAST_FUNC; 652char *strncpy_IFNAMSIZ(char *dst, const char *src) FAST_FUNC;
653unsigned count_strstr(const char *str, const char *sub) FAST_FUNC;
654char *xmalloc_substitute_string(const char *src, int count, const char *sub, const char *repl) FAST_FUNC;
653/* Guaranteed to NOT be a macro (smallest code). Saves nearly 2k on uclibc. 655/* Guaranteed to NOT be a macro (smallest code). Saves nearly 2k on uclibc.
654 * But potentially slow, don't use in one-billion-times loops */ 656 * But potentially slow, don't use in one-billion-times loops */
655int bb_putchar(int ch) FAST_FUNC; 657int bb_putchar(int ch) FAST_FUNC;
diff --git a/libbb/replace.c b/libbb/replace.c
new file mode 100644
index 000000000..8711f957d
--- /dev/null
+++ b/libbb/replace.c
@@ -0,0 +1,45 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9
10//kbuild:lib-y += replace.o
11
12#include "libbb.h"
13
14unsigned FAST_FUNC count_strstr(const char *str, const char *sub)
15{
16 size_t sub_len = strlen(sub);
17 unsigned count = 0;
18
19 while ((str = strstr(str, sub)) != NULL) {
20 count++;
21 str += sub_len;
22 }
23 return count;
24}
25
26char* FAST_FUNC xmalloc_substitute_string(const char *src, int count, const char *sub, const char *repl)
27{
28 char *buf, *dst, *end;
29 size_t sub_len = strlen(sub);
30 size_t repl_len = strlen(repl);
31
32 //dbg_msg("subst(s:'%s',count:%d,sub:'%s',repl:'%s'", src, count, sub, repl);
33
34 buf = dst = xmalloc(strlen(src) + count * ((int)repl_len - (int)sub_len) + 1);
35 /* we replace each sub with repl */
36 while ((end = strstr(src, sub)) != NULL) {
37 dst = mempcpy(dst, src, end - src);
38 dst = mempcpy(dst, repl, repl_len);
39 /*src = end + 1; - GNU findutils 4.5.10 doesn't do this... */
40 src = end + sub_len; /* but this. Try "xargs -Iaa echo aaa" */
41 }
42 strcpy(dst, src);
43 //dbg_msg("subst9:'%s'", buf);
44 return buf;
45}