[PATCH 2/2] find: implement 'find -exec ... {} +' option
Bartosz Golaszewski
bartekgola at gmail.com
Wed Jun 11 21:07:01 UTC 2014
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 <bartekgola at gmail.com>
---
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
More information about the busybox
mailing list