From df0553bbd2c28336795aea6e723414029390a1c4 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Sun, 29 Oct 2006 00:21:47 +0000 Subject: [PATCH] find: fix -exec to work like stock find does. --- findutils/find.c | 253 +++++++++++++++++++++++++++++------------------ 1 file changed, 155 insertions(+), 98 deletions(-) diff --git a/findutils/find.c b/findutils/find.c index 8618f3a97..7869840e2 100644 --- a/findutils/find.c +++ b/findutils/find.c @@ -10,55 +10,108 @@ * Licensed under the GPL version 2, see the file LICENSE in this tarball. */ +/* findutils-4.1.20: + * + * # find file.txt -exec 'echo {}' '{} {}' ';' + * find: echo file.txt: No such file or directory + * # find file.txt -exec 'echo' '{} {}' '; ' + * find: missing argument to `-exec' + * # find file.txt -exec 'echo {}' '{} {}' ';' junk + * find: paths must precede expression + * # find file.txt -exec 'echo {}' '{} {}' ';' junk ';' + * find: paths must precede expression + * # find file.txt -exec 'echo' '{} {}' ';' + * file.txt file.txt + * (strace: execve("/bin/echo", ["echo", "file.txt file.txt"], [ 30 vars ])) + * + * bboxed find rev 16467: above - works, below - doesn't + * + * # find file.txt -exec 'echo' '{} {}' ';' -print -exec pwd ';' + * file.txt file.txt + * file.txt + * /tmp + */ + #include "busybox.h" #include static char *pattern; -#ifdef CONFIG_FEATURE_FIND_PRINT0 +#if ENABLE_FEATURE_FIND_PRINT0 static char printsep = '\n'; #endif -#ifdef CONFIG_FEATURE_FIND_TYPE +#if ENABLE_FEATURE_FIND_TYPE static int type_mask = 0; #endif -#ifdef CONFIG_FEATURE_FIND_PERM +#if ENABLE_FEATURE_FIND_PERM static char perm_char = 0; static int perm_mask = 0; #endif -#ifdef CONFIG_FEATURE_FIND_MTIME +#if ENABLE_FEATURE_FIND_MTIME static char mtime_char; static int mtime_days; #endif -#ifdef CONFIG_FEATURE_FIND_MMIN +#if ENABLE_FEATURE_FIND_MMIN static char mmin_char; static int mmin_mins; #endif -#ifdef CONFIG_FEATURE_FIND_XDEV +#if ENABLE_FEATURE_FIND_XDEV static dev_t *xdev_dev; static int xdev_count = 0; #endif -#ifdef CONFIG_FEATURE_FIND_NEWER +#if ENABLE_FEATURE_FIND_NEWER static time_t newer_mtime; #endif -#ifdef CONFIG_FEATURE_FIND_INUM +#if ENABLE_FEATURE_FIND_INUM static ino_t inode_num; #endif -#ifdef CONFIG_FEATURE_FIND_EXEC -static char **exec_str; -static int num_matches; -static int exec_opt; +#if ENABLE_FEATURE_FIND_EXEC +static char **exec_argv; +static int *subst_count; +static int exec_argc; #endif +static int count_subst(const char *str) +{ + int count = 0; + while ((str = strstr(str, "{}"))) { + count++; + str++; + } + return count; +} + + +static char* subst(const char *src, int count, const char* filename) +{ + char *buf, *dst, *end; + int flen = strlen(filename); +//puts(src); + /* we replace each '{}' with filename: growth by strlen-2 */ + buf = dst = xmalloc(strlen(src) + count*(flen-2) + 1); + while ((end = strstr(src, "{}"))) { + memcpy(dst, src, end - src); + dst += end - src; + src = end + 2; + memcpy(dst, filename, flen); + dst += flen; + } + strcpy(dst, src); +//puts(buf); + return buf; +} + + static int fileAction(const char *fileName, struct stat *statbuf, void* junk, int depth) { -#ifdef CONFIG_FEATURE_FIND_XDEV +#if ENABLE_FEATURE_FIND_XDEV if (S_ISDIR(statbuf->st_mode) && xdev_count) { int i; for (i=0; ist_mode & S_IFMT) == type_mask)) goto no_match; } #endif -#ifdef CONFIG_FEATURE_FIND_PERM +#if ENABLE_FEATURE_FIND_PERM if (perm_mask != 0) { if (!((isdigit(perm_char) && (statbuf->st_mode & 07777) == perm_mask) || (perm_char == '-' && (statbuf->st_mode & perm_mask) == perm_mask) || @@ -91,7 +144,7 @@ static int fileAction(const char *fileName, struct stat *statbuf, void* junk, in goto no_match; } #endif -#ifdef CONFIG_FEATURE_FIND_MTIME +#if ENABLE_FEATURE_FIND_MTIME if (mtime_char != 0) { time_t file_age = time(NULL) - statbuf->st_mtime; time_t mtime_secs = mtime_days * 24 * 60 * 60; @@ -102,7 +155,7 @@ static int fileAction(const char *fileName, struct stat *statbuf, void* junk, in goto no_match; } #endif -#ifdef CONFIG_FEATURE_FIND_MMIN +#if ENABLE_FEATURE_FIND_MMIN if (mmin_char != 0) { time_t file_age = time(NULL) - statbuf->st_mtime; time_t mmin_secs = mmin_mins * 60; @@ -113,32 +166,37 @@ static int fileAction(const char *fileName, struct stat *statbuf, void* junk, in goto no_match; } #endif -#ifdef CONFIG_FEATURE_FIND_NEWER +#if ENABLE_FEATURE_FIND_NEWER if (newer_mtime != 0) { time_t file_age = newer_mtime - statbuf->st_mtime; if (file_age >= 0) goto no_match; } #endif -#ifdef CONFIG_FEATURE_FIND_INUM +#if ENABLE_FEATURE_FIND_INUM if (inode_num != 0) { if (!(statbuf->st_ino == inode_num)) goto no_match; } #endif -#ifdef CONFIG_FEATURE_FIND_EXEC - if (exec_opt) { +#if ENABLE_FEATURE_FIND_EXEC + if (exec_argc) { int i; - char *cmd_string = ""; - for (i = 0; i < num_matches; i++) - cmd_string = xasprintf("%s%s%s", cmd_string, exec_str[i], fileName); - cmd_string = xasprintf("%s%s", cmd_string, exec_str[num_matches]); - system(cmd_string); + char *argv[exec_argc+1]; + for (i = 0; i < exec_argc; i++) + argv[i] = subst(exec_argv[i], subst_count[i], fileName); + argv[i] = NULL; /* terminate the list */ + errno = 0; + wait4pid(spawn(argv)); + if (errno) + bb_perror_msg("%s", argv[0]); + for (i = 0; i < exec_argc; i++) + free(argv[i]); goto no_match; } #endif -#ifdef CONFIG_FEATURE_FIND_PRINT0 +#if ENABLE_FEATURE_FIND_PRINT0 printf("%s%c", fileName, printsep); #else puts(fileName); @@ -147,7 +205,7 @@ static int fileAction(const char *fileName, struct stat *statbuf, void* junk, in return TRUE; } -#ifdef CONFIG_FEATURE_FIND_TYPE +#if ENABLE_FEATURE_FIND_TYPE static int find_type(char *type) { int mask = 0; @@ -186,7 +244,7 @@ static int find_type(char *type) int find_main(int argc, char **argv) { int dereference = FALSE; - int i, firstopt, status = EXIT_SUCCESS; + int i, j, firstopt, status = EXIT_SUCCESS; for (firstopt = 1; firstopt < argc; firstopt++) { if (argv[firstopt][0] == '-') @@ -195,54 +253,62 @@ int find_main(int argc, char **argv) /* Parse any options */ for (i = firstopt; i < argc; i++) { - if (strcmp(argv[i], "-follow") == 0) + char *arg = argv[i]; + char *arg1 = argv[i+1]; + if (strcmp(arg, "-follow") == 0) dereference = TRUE; - else if (strcmp(argv[i], "-print") == 0) { + else if (strcmp(arg, "-print") == 0) { ; - } -#ifdef CONFIG_FEATURE_FIND_PRINT0 - else if (strcmp(argv[i], "-print0") == 0) + } +#if ENABLE_FEATURE_FIND_PRINT0 + else if (strcmp(arg, "-print0") == 0) printsep = '\0'; #endif - else if (strcmp(argv[i], "-name") == 0) { + else if (strcmp(arg, "-name") == 0) { if (++i == argc) - bb_error_msg_and_die(bb_msg_requires_arg, "-name"); - pattern = argv[i]; -#ifdef CONFIG_FEATURE_FIND_TYPE - } else if (strcmp(argv[i], "-type") == 0) { + bb_error_msg_and_die(bb_msg_requires_arg, arg); + pattern = arg1; +#if ENABLE_FEATURE_FIND_TYPE + } else if (strcmp(arg, "-type") == 0) { if (++i == argc) - bb_error_msg_and_die(bb_msg_requires_arg, "-type"); - type_mask = find_type(argv[i]); + bb_error_msg_and_die(bb_msg_requires_arg, arg); + type_mask = find_type(arg1); #endif -#ifdef CONFIG_FEATURE_FIND_PERM - } else if (strcmp(argv[i], "-perm") == 0) { +#if ENABLE_FEATURE_FIND_PERM +/* TODO: + * -perm mode File's permission bits are exactly mode (octal or symbolic). + * Symbolic modes use mode 0 as a point of departure. + * -perm -mode All of the permission bits mode are set for the file. + * -perm +mode Any of the permission bits mode are set for the file. + */ + } else if (strcmp(arg, "-perm") == 0) { if (++i == argc) - bb_error_msg_and_die(bb_msg_requires_arg, "-perm"); - perm_mask = xstrtol_range(argv[i], 8, 0, 07777); - perm_char = argv[i][0]; + bb_error_msg_and_die(bb_msg_requires_arg, arg); + perm_mask = xstrtol_range(arg1, 8, 0, 07777); + perm_char = arg1[0]; if (perm_char == '-') perm_mask = -perm_mask; #endif -#ifdef CONFIG_FEATURE_FIND_MTIME - } else if (strcmp(argv[i], "-mtime") == 0) { +#if ENABLE_FEATURE_FIND_MTIME + } else if (strcmp(arg, "-mtime") == 0) { if (++i == argc) - bb_error_msg_and_die(bb_msg_requires_arg, "-mtime"); - mtime_days = xatol(argv[i]); - mtime_char = argv[i][0]; + bb_error_msg_and_die(bb_msg_requires_arg, arg); + mtime_days = xatol(arg1); + mtime_char = arg1[0]; if (mtime_char == '-') mtime_days = -mtime_days; #endif -#ifdef CONFIG_FEATURE_FIND_MMIN - } else if (strcmp(argv[i], "-mmin") == 0) { +#if ENABLE_FEATURE_FIND_MMIN + } else if (strcmp(arg, "-mmin") == 0) { if (++i == argc) - bb_error_msg_and_die(bb_msg_requires_arg, "-mmin"); - mmin_mins = xatol(argv[i]); - mmin_char = argv[i][0]; + bb_error_msg_and_die(bb_msg_requires_arg, arg); + mmin_mins = xatol(arg1); + mmin_char = arg1[0]; if (mmin_char == '-') mmin_mins = -mmin_mins; #endif -#ifdef CONFIG_FEATURE_FIND_XDEV - } else if (strcmp(argv[i], "-xdev") == 0) { +#if ENABLE_FEATURE_FIND_XDEV + } else if (strcmp(arg, "-xdev") == 0) { struct stat stbuf; xdev_count = (firstopt - 1) ? (firstopt - 1) : 1; @@ -251,55 +317,46 @@ int find_main(int argc, char **argv) if (firstopt == 1) { xstat(".", &stbuf); xdev_dev[0] = stbuf.st_dev; - } - else { - - for (i = 1; i < firstopt; i++) { - xstat(argv[i], &stbuf); - xdev_dev[i-1] = stbuf.st_dev; + } else { + for (j = 1; j < firstopt; i++) { + xstat(argv[j], &stbuf); + xdev_dev[j-1] = stbuf.st_dev; } } #endif -#ifdef CONFIG_FEATURE_FIND_NEWER - } else if (strcmp(argv[i], "-newer") == 0) { +#if ENABLE_FEATURE_FIND_NEWER + } else if (strcmp(arg, "-newer") == 0) { struct stat stat_newer; if (++i == argc) - bb_error_msg_and_die(bb_msg_requires_arg, "-newer"); - xstat(argv[i], &stat_newer); + bb_error_msg_and_die(bb_msg_requires_arg, arg); + xstat(arg1, &stat_newer); newer_mtime = stat_newer.st_mtime; #endif -#ifdef CONFIG_FEATURE_FIND_INUM - } else if (strcmp(argv[i], "-inum") == 0) { +#if ENABLE_FEATURE_FIND_INUM + } else if (strcmp(arg, "-inum") == 0) { if (++i == argc) - bb_error_msg_and_die(bb_msg_requires_arg, "-inum"); - inode_num = xatoul(argv[i]); + bb_error_msg_and_die(bb_msg_requires_arg, arg); + inode_num = xatoul(arg1); #endif -#ifdef CONFIG_FEATURE_FIND_EXEC - } else if (strcmp(argv[i], "-exec") == 0) { - int b_pos; - char *cmd_string = ""; - - while (i++) { - if (i == argc) - bb_error_msg_and_die(bb_msg_requires_arg, "-exec"); - if (*argv[i] == ';') +#if ENABLE_FEATURE_FIND_EXEC + } else if (strcmp(arg, "-exec") == 0) { + i++; /* now: argv[i] is the first arg after -exec */ + exec_argv = &argv[i]; + exec_argc = i; + while (1) { + if (i == argc) /* did not see ';' till end */ + bb_error_msg_and_die(bb_msg_requires_arg, arg); + if (argv[i][0] == ';' && argv[i][1] == '\0') break; - cmd_string = xasprintf("%s %s", cmd_string, argv[i]); + i++; } - - if (*cmd_string == 0) - bb_error_msg_and_die(bb_msg_requires_arg, "-exec"); - cmd_string++; - exec_str = xmalloc(sizeof(char *)); - - while ((b_pos = strstr(cmd_string, "{}") - cmd_string), (b_pos >= 0)) { - num_matches++; - exec_str = xrealloc(exec_str, (num_matches + 1) * sizeof(char *)); - exec_str[num_matches - 1] = xstrndup(cmd_string, b_pos); - cmd_string += b_pos + 2; - } - exec_str[num_matches] = xstrdup(cmd_string); - exec_opt = 1; + exec_argc = i - exec_argc; /* number of --exec arguments */ + if (exec_argc == 0) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + subst_count = xmalloc(exec_argc * sizeof(int)); + j = exec_argc; + while (j--) + subst_count[j] = count_subst(exec_argv[j]); #endif } else bb_show_usage(); @@ -311,8 +368,8 @@ int find_main(int argc, char **argv) status = EXIT_FAILURE; } else { for (i = 1; i < firstopt; i++) { - if (!recursive_action(argv[i], TRUE, dereference, FALSE, fileAction, - fileAction, NULL, 0)) + if (!recursive_action(argv[i], TRUE, dereference, FALSE, + fileAction, fileAction, NULL, 0)) status = EXIT_FAILURE; } }