This patch implements the missing feature in find. It's configurable
and doesn't add any bloat when configured out.

With new feature compiled:
function                                             old     new   delta
scheduled_exec                                         -     237    +237
parse_params                                        1561    1647     +86
.rodata                                           150765  150842     +77
func_exec                                            187     235     +48
packed_usage                                       29441   29484     +43
llist_size                                             -      16     +16
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 4/0 up/down: 507/0)             Total: 507 bytes
   text    data     bss     dec     hex filename
 815295    4123    9504  828922   ca5fa busybox_old
 815759    4123    9504  829386   ca7ca busybox_unstripped

without it:
function                                             old     new   delta
parse_params                                        1561    1556      -5
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-5)               Total: -5 bytes
   text    data     bss     dec     hex filename
 815295    4123    9504  828922   ca5fa busybox_old
 815290    4123    9504  828917   ca5f5 busybox_unstripped

Signed-off-by: Bartosz Golaszewski <[email protected]>
---
 findutils/find.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 112 insertions(+), 20 deletions(-)

diff --git a/findutils/find.c b/findutils/find.c
index 6d34f4d..fa24dea 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -137,6 +137,14 @@
 //config:        Support the 'find -exec' option for executing commands based 
upon
 //config:        the files matched.
 //config:
+//config:config FEATURE_FIND_EXEC_PLUS
+//config:      bool "Enable -exec ... {} +"
+//config:      default y
+//config:      depends on FEATURE_FIND_EXEC
+//config:      help
+//config:        Support the 'find -exec ... {} +' option for executing 
commands
+//config:        for all matched files at once.
+//config:
 //config:config FEATURE_FIND_USER
 //config:      bool "Enable -user: username/uid matching"
 //config:      default y
@@ -319,6 +327,10 @@
 //usage:     "\n       -exec CMD ARG ; Run CMD with all instances of {} 
replaced by"
 //usage:     "\n                       file name. Fails if CMD exits with 
nonzero"
 //usage:       )
+//usage:       IF_FEATURE_FIND_EXEC_PLUS(
+//usage:     "\n       -exec CMD ARG + Same as above except that all file 
names are appended"
+//usage:     "\n                       at the end of the exec action"
+//usage:       )
 //usage:       IF_FEATURE_FIND_DELETE(
 //usage:     "\n       -delete         Delete current file/directory. Turns on 
-depth option"
 //usage:       )
@@ -375,7 +387,12 @@ IF_FEATURE_FIND_CONTEXT(ACTS(context, security_context_t 
context;))
 IF_FEATURE_FIND_PAREN(  ACTS(paren, action ***subexpr;))
 IF_FEATURE_FIND_PRUNE(  ACTS(prune))
 IF_FEATURE_FIND_DELETE( ACTS(delete))
-IF_FEATURE_FIND_EXEC(   ACTS(exec,  char **exec_argv; unsigned *subst_count; 
int exec_argc;))
+IF_FEATURE_FIND_EXEC(   ACTS(exec,
+                               char **exec_argv;
+                               unsigned *subst_count;
+                               int exec_argc;
+                               IF_FEATURE_FIND_EXEC_PLUS(int all_at_once;)
+                               ))
 IF_FEATURE_FIND_GROUP(  ACTS(group, gid_t gid;))
 IF_FEATURE_FIND_LINKS(  ACTS(links, char links_char; int links_count;))
 
@@ -389,6 +406,10 @@ struct globals {
        smallint need_print;
        smallint xdev_on;
        recurse_flags_t recurse_flags;
+#if ENABLE_FEATURE_FIND_EXEC_PLUS
+       llist_t *exec_all_acts;
+       llist_t *found_files;
+#endif
 } FIX_ALIASING;
 #define G (*(struct globals*)&bb_common_bufsiz1)
 #define INIT_G() do { \
@@ -452,6 +473,48 @@ static int exec_actions(action ***appp, const char 
*fileName, const struct stat
        return rc ^ TRUE; /* restore TRUE bit */
 }
 
+#if ENABLE_FEATURE_FIND_EXEC_PLUS
+static int scheduled_exec(void)
+{
+       /* Execute all scheduled '-exec +' actions. Expect
+        * the {} to be at the end of the command.
+        */
+       llist_t *actlist = G.exec_all_acts;
+       int rc = 0;
+
+       while (actlist) {
+               action_exec *ap;
+               llist_t *filelist;
+               size_t num_files;
+               char **argv;
+               int i;
+
+               ap = (action_exec*)actlist->data;
+               filelist = G.found_files;
+               num_files = llist_size(filelist);
+               if (num_files == 0)
+                       return 0;
+
+               argv = alloca((ap->exec_argc + num_files) * sizeof(char*));
+               for (i = 0; i < (ap->exec_argc - 1); i++)
+                       argv[i] = ap->exec_argv[i];
+               while (filelist) {
+                       argv[i++] = filelist->data;
+                       filelist = filelist->link;
+               }
+               argv[i] = NULL;
+
+               rc = spawn_and_wait(argv);
+               if (rc < 0)
+                       bb_simple_perror_msg(argv[0]);
+
+               actlist = actlist->link;
+       }
+
+       /* Return 1 if spawn_and_wait() failed. */
+       return rc != 0;
+}
+#endif /* ENABLE_FEATURE_FIND_EXEC_PLUS */
 
 #if !FNM_CASEFOLD
 static char *strcpy_upcase(char *dst, const char *src)
@@ -578,24 +641,33 @@ ACTF(inum)
 #if ENABLE_FEATURE_FIND_EXEC
 ACTF(exec)
 {
-       int i, rc;
+#if ENABLE_FEATURE_FIND_EXEC_PLUS
+       if (ap->all_at_once) {
+               llist_add_to_end(&G.found_files, xstrdup(fileName));
+               return 0;
+       } else {
+#endif /* ENABLE_FEATURE_FIND_EXEC_PLUS */
+               int i, rc;
 #if ENABLE_USE_PORTABLE_CODE
-       char **argv = alloca(sizeof(char*) * (ap->exec_argc + 1));
+               char **argv = alloca(sizeof(char*) * (ap->exec_argc + 1));
 #else /* gcc 4.3.1 generates smaller code: */
-       char *argv[ap->exec_argc + 1];
+               char *argv[ap->exec_argc + 1];
 #endif
-       for (i = 0; i < ap->exec_argc; i++)
-               argv[i] = xmalloc_substitute_string(ap->exec_argv[i], 
ap->subst_count[i], "{}", fileName);
-       argv[i] = NULL; /* terminate the list */
+               for (i = 0; i < ap->exec_argc; i++)
+                       argv[i] = xmalloc_substitute_string(ap->exec_argv[i], 
ap->subst_count[i], "{}", fileName);
+               argv[i] = NULL; /* terminate the list */
 
-       rc = spawn_and_wait(argv);
-       if (rc < 0)
-               bb_simple_perror_msg(argv[0]);
+               rc = spawn_and_wait(argv);
+               if (rc < 0)
+                       bb_simple_perror_msg(argv[0]);
 
-       i = 0;
-       while (argv[i])
-               free(argv[i++]);
-       return rc == 0; /* return 1 if exitcode 0 */
+               i = 0;
+               while (argv[i])
+                       free(argv[i++]);
+               return rc == 0; /* return 1 if exitcode 0 */
+#if ENABLE_FEATURE_FIND_EXEC_PLUS
+       }
+#endif /* ENABLE_FEATURE_FIND_EXEC_PLUS */
 }
 #endif
 #if ENABLE_FEATURE_FIND_USER
@@ -1037,6 +1109,7 @@ static action*** parse_params(char **argv)
                else if (parm == PARM_exec) {
                        int i;
                        action_exec *ap;
+                       IF_FEATURE_FIND_EXEC_PLUS(int all_subst = 0;)
                        dbg("%d", __LINE__);
                        G.need_print = 0;
                        ap = ALLOC_ACTION(exec);
@@ -1049,10 +1122,11 @@ static action*** parse_params(char **argv)
                                // executes "echo Foo >FILENAME<",
                                // find -exec echo Foo ">{}<" "+"
                                // executes "echo Foo FILENAME1 FILENAME2 
FILENAME3...".
-                               // TODO (so far we treat "+" just like ";")
-                               if ((argv[0][0] == ';' || argv[0][0] == '+')
-                                && argv[0][1] == '\0'
-                               ) {
+                               if ((argv[0][0] == ';'
+                                       IF_FEATURE_FIND_EXEC_PLUS(|| argv[0][0] 
== '+')
+                                       ) && argv[0][1] == '\0' ) {
+                                       IF_FEATURE_FIND_EXEC_PLUS(
+                                               ap->all_at_once = (argv[0][0] 
== '+' ? 1 : 0);)
                                        break;
                                }
                                argv++;
@@ -1062,8 +1136,22 @@ static action*** parse_params(char **argv)
                                bb_error_msg_and_die(bb_msg_requires_arg, arg);
                        ap->subst_count = xmalloc(ap->exec_argc * sizeof(int));
                        i = ap->exec_argc;
-                       while (i--)
-                               ap->subst_count[i] = 
count_strstr(ap->exec_argv[i], "{}");
+                       while (i--) {
+                               IF_FEATURE_FIND_EXEC_PLUS(all_subst +=)
+                                       ap->subst_count[i] = 
count_strstr(ap->exec_argv[i], "{}");
+                       }
+#if ENABLE_FEATURE_FIND_EXEC_PLUS
+                       /* coreutils expects {} to apear only once at the end 
of the
+                        * '-exec +' command.
+                        */
+                       if (ap->all_at_once && all_subst > 1)
+                               bb_error_msg_and_die("only one '{}' allowed for 
-exec +");
+                       if (ap->subst_count[ap->exec_argc-1] != 1)
+                               bb_error_msg_and_die(bb_msg_requires_arg, 
"-exec");
+                       /* Schedule execution of '-exec +' for later. */
+                       if (ap->all_at_once)
+                               llist_add_to_end(&G.exec_all_acts, ap);
+#endif /* ENABLE_FEATURE_FIND_EXEC_PLUS */
                }
 #endif
 #if ENABLE_FEATURE_FIND_PAREN
@@ -1335,8 +1423,12 @@ int find_main(int argc UNUSED_PARAM, char **argv)
                                0)              /* depth */
                ) {
                        status = EXIT_FAILURE;
+                       goto out;
                }
        }
 
+       IF_FEATURE_FIND_EXEC_PLUS(status = scheduled_exec();)
+
+out:
        return status;
 }
-- 
1.9.1

_______________________________________________
busybox mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to