find: fix -exec to work like stock find does.
No binary (build failed)
—
(log yok)
| @@ -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 <fnmatch.h> | |
| 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; i<xdev_count; i++) { | |
| @@ -77,13 +130,13 @@ static int fileAction(const char *fileName, struct stat *statbuf, void* junk, in | |
| if (fnmatch(pattern, tmp, FNM_PERIOD) != 0) | |
| goto no_match; | |
| } | |
| - | #ifdef CONFIG_FEATURE_FIND_TYPE |
| + | #if ENABLE_FEATURE_FIND_TYPE |
| if (type_mask != 0) { | |
| if (!((statbuf->st_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]); |
| - | } |
| - | |
| - | 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; |
| + | i++; |
| } | |
| - | 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; | |
| } | |
| } | |