[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