1 /* vi: set sw=4 ts=4: */
3 * Mini find implementation for busybox
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 * Reworked by David Douthitt <n9ubh@callsign.net> and
8 * Matt Kraai <kraai@alumni.carnegiemellon.edu>.
10 * Licensed under the GPL version 2, see the file LICENSE in this tarball.
15 * # find file.txt -exec 'echo {}' '{} {}' ';'
16 * find: echo file.txt: No such file or directory
17 * # find file.txt -exec 'echo' '{} {}' '; '
18 * find: missing argument to `-exec'
19 * # find file.txt -exec 'echo {}' '{} {}' ';' junk
20 * find: paths must precede expression
21 * # find file.txt -exec 'echo {}' '{} {}' ';' junk ';'
22 * find: paths must precede expression
23 * # find file.txt -exec 'echo' '{} {}' ';'
25 * (strace: execve("/bin/echo", ["echo", "file.txt file.txt"], [ 30 vars ]))
27 * bboxed find rev 16467: above - works, below - doesn't
29 * # find file.txt -exec 'echo' '{} {}' ';' -print -exec pwd ';'
39 #if ENABLE_FEATURE_FIND_PRINT0
40 static char printsep = '\n';
43 #if ENABLE_FEATURE_FIND_TYPE
44 static int type_mask = 0;
47 #if ENABLE_FEATURE_FIND_PERM
48 static char perm_char = 0;
49 static int perm_mask = 0;
52 #if ENABLE_FEATURE_FIND_MTIME
53 static char mtime_char;
54 static int mtime_days;
57 #if ENABLE_FEATURE_FIND_MMIN
58 static char mmin_char;
62 #if ENABLE_FEATURE_FIND_XDEV
63 static dev_t *xdev_dev;
64 static int xdev_count = 0;
67 #if ENABLE_FEATURE_FIND_NEWER
68 static time_t newer_mtime;
71 #if ENABLE_FEATURE_FIND_INUM
72 static ino_t inode_num;
75 #if ENABLE_FEATURE_FIND_EXEC
76 static char **exec_argv;
77 static int *subst_count;
81 static int count_subst(const char *str)
84 while ((str = strstr(str, "{}"))) {
92 static char* subst(const char *src, int count, const char* filename)
94 char *buf, *dst, *end;
95 int flen = strlen(filename);
97 /* we replace each '{}' with filename: growth by strlen-2 */
98 buf = dst = xmalloc(strlen(src) + count*(flen-2) + 1);
99 while ((end = strstr(src, "{}"))) {
100 memcpy(dst, src, end - src);
103 memcpy(dst, filename, flen);
112 static int fileAction(const char *fileName, struct stat *statbuf, void* junk, int depth)
114 #if ENABLE_FEATURE_FIND_XDEV
115 if (S_ISDIR(statbuf->st_mode) && xdev_count) {
117 for (i=0; i<xdev_count; i++) {
118 if (xdev_dev[i] != statbuf->st_dev)
123 if (pattern != NULL) {
124 const char *tmp = strrchr(fileName, '/');
130 if (fnmatch(pattern, tmp, FNM_PERIOD) != 0)
133 #if ENABLE_FEATURE_FIND_TYPE
134 if (type_mask != 0) {
135 if (!((statbuf->st_mode & S_IFMT) == type_mask))
139 #if ENABLE_FEATURE_FIND_PERM
140 if (perm_mask != 0) {
141 if (!((isdigit(perm_char) && (statbuf->st_mode & 07777) == perm_mask) ||
142 (perm_char == '-' && (statbuf->st_mode & perm_mask) == perm_mask) ||
143 (perm_char == '+' && (statbuf->st_mode & perm_mask) != 0)))
147 #if ENABLE_FEATURE_FIND_MTIME
148 if (mtime_char != 0) {
149 time_t file_age = time(NULL) - statbuf->st_mtime;
150 time_t mtime_secs = mtime_days * 24 * 60 * 60;
151 if (!((isdigit(mtime_char) && file_age >= mtime_secs &&
152 file_age < mtime_secs + 24 * 60 * 60) ||
153 (mtime_char == '+' && file_age >= mtime_secs + 24 * 60 * 60) ||
154 (mtime_char == '-' && file_age < mtime_secs)))
158 #if ENABLE_FEATURE_FIND_MMIN
159 if (mmin_char != 0) {
160 time_t file_age = time(NULL) - statbuf->st_mtime;
161 time_t mmin_secs = mmin_mins * 60;
162 if (!((isdigit(mmin_char) && file_age >= mmin_secs &&
163 file_age < mmin_secs + 60) ||
164 (mmin_char == '+' && file_age >= mmin_secs + 60) ||
165 (mmin_char == '-' && file_age < mmin_secs)))
169 #if ENABLE_FEATURE_FIND_NEWER
170 if (newer_mtime != 0) {
171 time_t file_age = newer_mtime - statbuf->st_mtime;
176 #if ENABLE_FEATURE_FIND_INUM
177 if (inode_num != 0) {
178 if (!(statbuf->st_ino == inode_num))
182 #if ENABLE_FEATURE_FIND_EXEC
185 char *argv[exec_argc+1];
186 for (i = 0; i < exec_argc; i++)
187 argv[i] = subst(exec_argv[i], subst_count[i], fileName);
188 argv[i] = NULL; /* terminate the list */
190 wait4pid(spawn(argv));
192 bb_perror_msg("%s", argv[0]);
193 for (i = 0; i < exec_argc; i++)
199 #if ENABLE_FEATURE_FIND_PRINT0
200 printf("%s%c", fileName, printsep);
208 #if ENABLE_FEATURE_FIND_TYPE
209 static int find_type(char *type)
237 if (mask == 0 || type[1] != '\0')
238 bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type");
244 int find_main(int argc, char **argv)
246 int dereference = FALSE;
247 int i, j, firstopt, status = EXIT_SUCCESS;
249 for (firstopt = 1; firstopt < argc; firstopt++) {
250 if (argv[firstopt][0] == '-')
254 /* Parse any options */
255 for (i = firstopt; i < argc; i++) {
257 char *arg1 = argv[i+1];
258 if (strcmp(arg, "-follow") == 0)
260 else if (strcmp(arg, "-print") == 0) {
263 #if ENABLE_FEATURE_FIND_PRINT0
264 else if (strcmp(arg, "-print0") == 0)
267 else if (strcmp(arg, "-name") == 0) {
269 bb_error_msg_and_die(bb_msg_requires_arg, arg);
271 #if ENABLE_FEATURE_FIND_TYPE
272 } else if (strcmp(arg, "-type") == 0) {
274 bb_error_msg_and_die(bb_msg_requires_arg, arg);
275 type_mask = find_type(arg1);
277 #if ENABLE_FEATURE_FIND_PERM
279 * -perm mode File's permission bits are exactly mode (octal or symbolic).
280 * Symbolic modes use mode 0 as a point of departure.
281 * -perm -mode All of the permission bits mode are set for the file.
282 * -perm +mode Any of the permission bits mode are set for the file.
284 } else if (strcmp(arg, "-perm") == 0) {
286 bb_error_msg_and_die(bb_msg_requires_arg, arg);
287 perm_mask = xstrtol_range(arg1, 8, 0, 07777);
289 if (perm_char == '-')
290 perm_mask = -perm_mask;
292 #if ENABLE_FEATURE_FIND_MTIME
293 } else if (strcmp(arg, "-mtime") == 0) {
295 bb_error_msg_and_die(bb_msg_requires_arg, arg);
296 mtime_days = xatol(arg1);
297 mtime_char = arg1[0];
298 if (mtime_char == '-')
299 mtime_days = -mtime_days;
301 #if ENABLE_FEATURE_FIND_MMIN
302 } else if (strcmp(arg, "-mmin") == 0) {
304 bb_error_msg_and_die(bb_msg_requires_arg, arg);
305 mmin_mins = xatol(arg1);
307 if (mmin_char == '-')
308 mmin_mins = -mmin_mins;
310 #if ENABLE_FEATURE_FIND_XDEV
311 } else if (strcmp(arg, "-xdev") == 0) {
314 xdev_count = (firstopt - 1) ? (firstopt - 1) : 1;
315 xdev_dev = xmalloc(xdev_count * sizeof(dev_t));
319 xdev_dev[0] = stbuf.st_dev;
321 for (j = 1; j < firstopt; i++) {
322 xstat(argv[j], &stbuf);
323 xdev_dev[j-1] = stbuf.st_dev;
327 #if ENABLE_FEATURE_FIND_NEWER
328 } else if (strcmp(arg, "-newer") == 0) {
329 struct stat stat_newer;
331 bb_error_msg_and_die(bb_msg_requires_arg, arg);
332 xstat(arg1, &stat_newer);
333 newer_mtime = stat_newer.st_mtime;
335 #if ENABLE_FEATURE_FIND_INUM
336 } else if (strcmp(arg, "-inum") == 0) {
338 bb_error_msg_and_die(bb_msg_requires_arg, arg);
339 inode_num = xatoul(arg1);
341 #if ENABLE_FEATURE_FIND_EXEC
342 } else if (strcmp(arg, "-exec") == 0) {
343 i++; /* now: argv[i] is the first arg after -exec */
344 exec_argv = &argv[i];
347 if (i == argc) /* did not see ';' till end */
348 bb_error_msg_and_die(bb_msg_requires_arg, arg);
349 if (argv[i][0] == ';' && argv[i][1] == '\0')
353 exec_argc = i - exec_argc; /* number of --exec arguments */
355 bb_error_msg_and_die(bb_msg_requires_arg, arg);
356 subst_count = xmalloc(exec_argc * sizeof(int));
359 subst_count[j] = count_subst(exec_argv[j]);
366 if (!recursive_action(".", TRUE, dereference, FALSE, fileAction,
367 fileAction, NULL, 0))
368 status = EXIT_FAILURE;
370 for (i = 1; i < firstopt; i++) {
371 if (!recursive_action(argv[i], TRUE, dereference, FALSE,
372 fileAction, fileAction, NULL, 0))
373 status = EXIT_FAILURE;