[git commit] find: add optional sup[port for '-exec ... {} +'

Denys Vlasenko vda.linux at googlemail.com
Tue Jun 17 15:09:17 UTC 2014


commit: http://git.busybox.net/busybox/commit/?id=363193ec5817b45fa83026eea843f7e325958529
branch: http://git.busybox.net/busybox/commit/?id=refs/heads/master

function                                             old     new   delta
do_exec                                                -     309    +309
parse_params                                        1416    1487     +71
find_main                                            342     406     +64
packed_usage                                       29958   30014     +56
func_exec                                            138     127     -11
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/1 up/down: 500/-11)           Total: 489 bytes

Signed-off-by: Bartosz Golaszewski <bartekgola at gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 findutils/find.c |  156 ++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 141 insertions(+), 15 deletions(-)

diff --git a/findutils/find.c b/findutils/find.c
index 6d34f4d..8ac3da7 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -137,6 +137,16 @@
 //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:	  Without this option, -exec + is a synonym for -exec ;
+//config:	  (IOW: it works correctly, but without expected speedup)
+//config:
 //config:config FEATURE_FIND_USER
 //config:	bool "Enable -user: username/uid matching"
 //config:	default y
@@ -319,6 +329,9 @@
 //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 + Run CMD with {} replaced by list of file names"
+//usage:	)
 //usage:	IF_FEATURE_FIND_DELETE(
 //usage:     "\n	-delete		Delete current file/directory. Turns on -depth option"
 //usage:	)
@@ -337,8 +350,12 @@
 # define FNM_CASEFOLD 0
 #endif
 
-#define dbg(...) ((void)0)
-/* #define dbg(...) bb_error_msg(__VA_ARGS__) */
+#if 1
+# define dbg(...) ((void)0)
+#else
+# define dbg(...) bb_error_msg(__VA_ARGS__)
+#endif
+
 
 /* This is a NOEXEC applet. Be very careful! */
 
@@ -375,7 +392,20 @@ 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; /* -exec ARGS */
+				unsigned *subst_count;
+				int exec_argc; /* count of ARGS */
+				IF_FEATURE_FIND_EXEC_PLUS(
+					/*
+					 * filelist is NULL if "exec ;"
+					 * non-NULL if "exec +"
+					 */
+					char **filelist;
+					int filelist_idx;
+					int file_len;
+				)
+				))
 IF_FEATURE_FIND_GROUP(  ACTS(group, gid_t gid;))
 IF_FEATURE_FIND_LINKS(  ACTS(links, char links_char; int links_count;))
 
@@ -452,7 +482,6 @@ static int exec_actions(action ***appp, const char *fileName, const struct stat
 	return rc ^ TRUE; /* restore TRUE bit */
 }
 
-
 #if !FNM_CASEFOLD
 static char *strcpy_upcase(char *dst, const char *src)
 {
@@ -576,17 +605,56 @@ ACTF(inum)
 }
 #endif
 #if ENABLE_FEATURE_FIND_EXEC
-ACTF(exec)
+static int do_exec(action_exec *ap, const char *fileName)
 {
 	int i, rc;
-#if ENABLE_USE_PORTABLE_CODE
-	char **argv = alloca(sizeof(char*) * (ap->exec_argc + 1));
-#else /* gcc 4.3.1 generates smaller code: */
-	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 */
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+	int size = ap->exec_argc + ap->filelist_idx + 1;
+# else
+	int size = ap->exec_argc + 1;
+# endif
+# if ENABLE_USE_PORTABLE_CODE
+	char **argv = alloca(sizeof(char*) * size);
+# else /* gcc 4.3.1 generates smaller code: */
+	char *argv[size];
+# endif
+	char **pp = argv;
+
+	for (i = 0; i < ap->exec_argc; i++) {
+		const char *arg = ap->exec_argv[i];
+
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+		if (ap->filelist) {
+			/* Handling "-exec +"
+			 * Only one exec_argv[i] has substitution in it.
+			 * Expand that one exec_argv[i] into file list.
+			 */
+			if (ap->subst_count[i] == 0) {
+				*pp++ = xstrdup(arg);
+			} else {
+				int j = 0;
+				while (ap->filelist[j]) {
+					*pp++ = xmalloc_substitute_string(arg, 1, "{}", ap->filelist[j]);
+					free(ap->filelist[j]);
+					j++;
+				}
+			}
+		} else
+# endif
+		{
+			/* Handling "-exec ;" */
+			*pp++ = xmalloc_substitute_string(arg, ap->subst_count[i], "{}", fileName);
+		}
+	}
+	*pp = NULL; /* terminate the list */
+
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+	if (ap->filelist) {
+		ap->filelist[0] = NULL;
+		ap->filelist_idx = 0;
+		ap->file_len = 0;
+	}
+# endif
 
 	rc = spawn_and_wait(argv);
 	if (rc < 0)
@@ -597,6 +665,48 @@ ACTF(exec)
 		free(argv[i++]);
 	return rc == 0; /* return 1 if exitcode 0 */
 }
+ACTF(exec)
+{
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+	if (ap->filelist) {
+		int rc = 0;
+
+		/* If we have lots of files already, exec the command */
+		if (ap->file_len >= 32*1024)
+			rc = do_exec(ap, NULL);
+
+		ap->file_len += strlen(fileName) + sizeof(char*) + 1;
+		ap->filelist = xrealloc_vector(ap->filelist, 8, ap->filelist_idx);
+		ap->filelist[ap->filelist_idx++] = xstrdup(fileName);
+		return rc == 0; /* return 1 if exitcode 0 */
+	}
+# endif
+	return do_exec(ap, fileName);
+}
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+static int flush_exec_plus(void)
+{
+	action *ap;
+	action **app;
+	action ***appp = G.actions;
+	while ((app = *appp++) != NULL) {
+		while ((ap = *app++) != NULL) {
+			if (ap->f == (action_fp)func_exec) {
+				action_exec *ae = (void*)ap;
+				if (ae->filelist_idx != 0) {
+					int rc = do_exec(ae, NULL);
+#  if ENABLE_FEATURE_FIND_NOT
+					if (ap->invert) rc = !rc;
+#  endif
+					if (rc)
+						return rc;
+				}
+			}
+		}
+	}
+	return 0;
+}
+# endif
 #endif
 #if ENABLE_FEATURE_FIND_USER
 ACTF(user)
@@ -1037,6 +1147,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 +1160,13 @@ 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 ENABLE_FEATURE_FIND_EXEC_PLUS
+					if (argv[0][0] == '+')
+						ap->filelist = xzalloc(sizeof(ap->filelist[0]));
+# endif
 					break;
 				}
 				argv++;
@@ -1062,8 +1176,17 @@ 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--)
+			while (i--) {
 				ap->subst_count[i] = count_strstr(ap->exec_argv[i], "{}");
+				IF_FEATURE_FIND_EXEC_PLUS(all_subst += ap->subst_count[i];)
+			}
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+			/*
+			 * coreutils expects {} to appear only once in "-exec +"
+			 */
+			if (all_subst != 1 && ap->filelist)
+				bb_error_msg_and_die("only one '{}' allowed for -exec +");
+# endif
 		}
 #endif
 #if ENABLE_FEATURE_FIND_PAREN
@@ -1335,8 +1458,11 @@ int find_main(int argc UNUSED_PARAM, char **argv)
 				0)              /* depth */
 		) {
 			status = EXIT_FAILURE;
+			goto out;
 		}
 	}
 
+	IF_FEATURE_FIND_EXEC_PLUS(status = flush_exec_plus();)
+out:
 	return status;
 }


More information about the busybox-cvs mailing list