Contents of /trunk/mkinitrd-magellan/busybox/findutils/find.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 9 months ago) by niro
File MIME type: text/plain
File size: 14516 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 9 months ago) by niro
File MIME type: text/plain
File size: 14516 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * Mini find implementation for busybox |
4 | * |
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> |
6 | * |
7 | * Reworked by David Douthitt <n9ubh@callsign.net> and |
8 | * Matt Kraai <kraai@alumni.carnegiemellon.edu>. |
9 | * |
10 | * Licensed under the GPL version 2, see the file LICENSE in this tarball. |
11 | */ |
12 | |
13 | /* findutils-4.1.20: |
14 | * |
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' '{} {}' ';' |
24 | * file.txt file.txt |
25 | * (strace: execve("/bin/echo", ["echo", "file.txt file.txt"], [ 30 vars ])) |
26 | * # find file.txt -exec 'echo' '{} {}' ';' -print -exec pwd ';' |
27 | * file.txt file.txt |
28 | * file.txt |
29 | * /tmp |
30 | * # find -name '*.c' -o -name '*.h' |
31 | * [shows files, *.c and *.h intermixed] |
32 | * # find file.txt -name '*f*' -o -name '*t*' |
33 | * file.txt |
34 | * # find file.txt -name '*z*' -o -name '*t*' |
35 | * file.txt |
36 | * # find file.txt -name '*f*' -o -name '*z*' |
37 | * file.txt |
38 | * |
39 | * # find t z -name '*t*' -print -o -name '*z*' |
40 | * t |
41 | * # find t z t z -name '*t*' -o -name '*z*' -print |
42 | * z |
43 | * z |
44 | * # find t z t z '(' -name '*t*' -o -name '*z*' ')' -o -print |
45 | * (no output) |
46 | */ |
47 | |
48 | #include "busybox.h" |
49 | #include <fnmatch.h> |
50 | |
51 | USE_FEATURE_FIND_XDEV(static dev_t *xdev_dev;) |
52 | USE_FEATURE_FIND_XDEV(static int xdev_count;) |
53 | |
54 | typedef int (*action_fp)(const char *fileName, struct stat *statbuf, void *); |
55 | |
56 | typedef struct { |
57 | action_fp f; |
58 | } action; |
59 | #define ACTS(name, arg...) typedef struct { action a; arg; } action_##name; |
60 | #define ACTF(name) static int func_##name(const char *fileName, struct stat *statbuf, action_##name* ap) |
61 | ACTS(print) |
62 | ACTS(name, char *pattern;) |
63 | USE_FEATURE_FIND_PRINT0(ACTS(print0)) |
64 | USE_FEATURE_FIND_TYPE( ACTS(type, int type_mask;)) |
65 | USE_FEATURE_FIND_PERM( ACTS(perm, char perm_char; int perm_mask;)) |
66 | USE_FEATURE_FIND_MTIME( ACTS(mtime, char mtime_char; int mtime_days;)) |
67 | USE_FEATURE_FIND_MMIN( ACTS(mmin, char mmin_char; int mmin_mins;)) |
68 | USE_FEATURE_FIND_NEWER( ACTS(newer, time_t newer_mtime;)) |
69 | USE_FEATURE_FIND_INUM( ACTS(inum, ino_t inode_num;)) |
70 | USE_FEATURE_FIND_EXEC( ACTS(exec, char **exec_argv; int *subst_count; int exec_argc;)) |
71 | USE_DESKTOP( ACTS(paren, action ***subexpr;)) |
72 | USE_DESKTOP( ACTS(size, off_t size;)) |
73 | USE_DESKTOP( ACTS(prune)) |
74 | |
75 | static action ***actions; |
76 | static int need_print = 1; |
77 | |
78 | |
79 | #if ENABLE_FEATURE_FIND_EXEC |
80 | static int count_subst(const char *str) |
81 | { |
82 | int count = 0; |
83 | while ((str = strstr(str, "{}"))) { |
84 | count++; |
85 | str++; |
86 | } |
87 | return count; |
88 | } |
89 | |
90 | |
91 | static char* subst(const char *src, int count, const char* filename) |
92 | { |
93 | char *buf, *dst, *end; |
94 | int flen = strlen(filename); |
95 | /* we replace each '{}' with filename: growth by strlen-2 */ |
96 | buf = dst = xmalloc(strlen(src) + count*(flen-2) + 1); |
97 | while ((end = strstr(src, "{}"))) { |
98 | memcpy(dst, src, end - src); |
99 | dst += end - src; |
100 | src = end + 2; |
101 | memcpy(dst, filename, flen); |
102 | dst += flen; |
103 | } |
104 | strcpy(dst, src); |
105 | return buf; |
106 | } |
107 | #endif |
108 | |
109 | |
110 | static int exec_actions(action ***appp, const char *fileName, struct stat *statbuf) |
111 | { |
112 | int cur_group; |
113 | int cur_action; |
114 | int rc = TRUE; |
115 | action **app, *ap; |
116 | |
117 | cur_group = -1; |
118 | while ((app = appp[++cur_group])) { |
119 | cur_action = -1; |
120 | do { |
121 | ap = app[++cur_action]; |
122 | } while (ap && (rc = ap->f(fileName, statbuf, ap))); |
123 | if (!ap) { |
124 | /* all actions in group were successful */ |
125 | break; |
126 | } |
127 | } |
128 | return rc; |
129 | } |
130 | |
131 | |
132 | ACTF(name) |
133 | { |
134 | const char *tmp = strrchr(fileName, '/'); |
135 | if (tmp == NULL) |
136 | tmp = fileName; |
137 | else |
138 | tmp++; |
139 | return fnmatch(ap->pattern, tmp, FNM_PERIOD) == 0; |
140 | } |
141 | #if ENABLE_FEATURE_FIND_TYPE |
142 | ACTF(type) |
143 | { |
144 | return ((statbuf->st_mode & S_IFMT) == ap->type_mask); |
145 | } |
146 | #endif |
147 | #if ENABLE_FEATURE_FIND_PERM |
148 | ACTF(perm) |
149 | { |
150 | return !((isdigit(ap->perm_char) && (statbuf->st_mode & 07777) == ap->perm_mask) |
151 | || (ap->perm_char == '-' && (statbuf->st_mode & ap->perm_mask) == ap->perm_mask) |
152 | || (ap->perm_char == '+' && (statbuf->st_mode & ap->perm_mask) != 0)); |
153 | } |
154 | #endif |
155 | #if ENABLE_FEATURE_FIND_MTIME |
156 | ACTF(mtime) |
157 | { |
158 | time_t file_age = time(NULL) - statbuf->st_mtime; |
159 | time_t mtime_secs = ap->mtime_days * 24 * 60 * 60; |
160 | return !((isdigit(ap->mtime_char) && file_age >= mtime_secs |
161 | && file_age < mtime_secs + 24 * 60 * 60) |
162 | || (ap->mtime_char == '+' && file_age >= mtime_secs + 24 * 60 * 60) |
163 | || (ap->mtime_char == '-' && file_age < mtime_secs)); |
164 | } |
165 | #endif |
166 | #if ENABLE_FEATURE_FIND_MMIN |
167 | ACTF(mmin) |
168 | { |
169 | time_t file_age = time(NULL) - statbuf->st_mtime; |
170 | time_t mmin_secs = ap->mmin_mins * 60; |
171 | return !((isdigit(ap->mmin_char) && file_age >= mmin_secs |
172 | && file_age < mmin_secs + 60) |
173 | || (ap->mmin_char == '+' && file_age >= mmin_secs + 60) |
174 | || (ap->mmin_char == '-' && file_age < mmin_secs)); |
175 | } |
176 | #endif |
177 | #if ENABLE_FEATURE_FIND_NEWER |
178 | ACTF(newer) |
179 | { |
180 | return (ap->newer_mtime >= statbuf->st_mtime); |
181 | } |
182 | #endif |
183 | #if ENABLE_FEATURE_FIND_INUM |
184 | ACTF(inum) |
185 | { |
186 | return (statbuf->st_ino != ap->inode_num); |
187 | } |
188 | #endif |
189 | #if ENABLE_FEATURE_FIND_EXEC |
190 | ACTF(exec) |
191 | { |
192 | int i, rc; |
193 | char *argv[ap->exec_argc+1]; |
194 | for (i = 0; i < ap->exec_argc; i++) |
195 | argv[i] = subst(ap->exec_argv[i], ap->subst_count[i], fileName); |
196 | argv[i] = NULL; /* terminate the list */ |
197 | rc = wait4pid(spawn(argv)); |
198 | if (rc) |
199 | bb_perror_msg("%s", argv[0]); |
200 | for (i = 0; i < ap->exec_argc; i++) |
201 | free(argv[i]); |
202 | return rc == 0; /* return 1 if success */ |
203 | } |
204 | #endif |
205 | |
206 | #if ENABLE_FEATURE_FIND_PRINT0 |
207 | ACTF(print0) |
208 | { |
209 | printf("%s%c", fileName, '\0'); |
210 | return TRUE; |
211 | } |
212 | #endif |
213 | |
214 | ACTF(print) |
215 | { |
216 | puts(fileName); |
217 | return TRUE; |
218 | } |
219 | |
220 | #if ENABLE_DESKTOP |
221 | ACTF(paren) |
222 | { |
223 | return exec_actions(ap->subexpr, fileName, statbuf); |
224 | } |
225 | |
226 | /* |
227 | * -prune: if -depth is not given, return true and do not descend |
228 | * current dir; if -depth is given, return false with no effect. |
229 | * Example: |
230 | * find dir -name 'asm-*' -prune -o -name '*.[chS]' -print |
231 | */ |
232 | ACTF(prune) |
233 | { |
234 | return SKIP; |
235 | } |
236 | |
237 | ACTF(size) |
238 | { |
239 | return statbuf->st_size == ap->size; |
240 | } |
241 | #endif |
242 | |
243 | |
244 | static int fileAction(const char *fileName, struct stat *statbuf, void* junk, int depth) |
245 | { |
246 | int rc; |
247 | #ifdef CONFIG_FEATURE_FIND_XDEV |
248 | if (S_ISDIR(statbuf->st_mode) && xdev_count) { |
249 | int i; |
250 | for (i = 0; i < xdev_count; i++) { |
251 | if (xdev_dev[i] != statbuf->st_dev) |
252 | return SKIP; |
253 | } |
254 | } |
255 | #endif |
256 | rc = exec_actions(actions, fileName, statbuf); |
257 | /* Had no explicit -print[0] or -exec? then print */ |
258 | if (rc && need_print) |
259 | puts(fileName); |
260 | /* Cannot return 0: our caller, recursive_action(), |
261 | * will perror() and skip dirs (if called on dir) */ |
262 | return rc == 0 ? TRUE : rc; |
263 | } |
264 | |
265 | |
266 | #if ENABLE_FEATURE_FIND_TYPE |
267 | static int find_type(char *type) |
268 | { |
269 | int mask = 0; |
270 | |
271 | switch (type[0]) { |
272 | case 'b': |
273 | mask = S_IFBLK; |
274 | break; |
275 | case 'c': |
276 | mask = S_IFCHR; |
277 | break; |
278 | case 'd': |
279 | mask = S_IFDIR; |
280 | break; |
281 | case 'p': |
282 | mask = S_IFIFO; |
283 | break; |
284 | case 'f': |
285 | mask = S_IFREG; |
286 | break; |
287 | case 'l': |
288 | mask = S_IFLNK; |
289 | break; |
290 | case 's': |
291 | mask = S_IFSOCK; |
292 | break; |
293 | } |
294 | |
295 | if (mask == 0 || type[1] != '\0') |
296 | bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type"); |
297 | |
298 | return mask; |
299 | } |
300 | #endif |
301 | |
302 | action*** parse_params(char **argv) |
303 | { |
304 | action*** appp; |
305 | int cur_group = 0; |
306 | int cur_action = 0; |
307 | |
308 | action* alloc_action(int sizeof_struct, action_fp f) |
309 | { |
310 | action *ap; |
311 | appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof(*appp)); |
312 | appp[cur_group][cur_action++] = ap = xmalloc(sizeof_struct); |
313 | appp[cur_group][cur_action] = NULL; |
314 | ap->f = f; |
315 | return ap; |
316 | } |
317 | #define ALLOC_ACTION(name) (action_##name*)alloc_action(sizeof(action_##name), (action_fp) func_##name) |
318 | |
319 | appp = xzalloc(2 * sizeof(*appp)); /* appp[0],[1] == NULL */ |
320 | |
321 | // Actions have side effects and return a true or false value |
322 | // We implement: -print, -print0, -exec |
323 | |
324 | // The rest are tests. |
325 | |
326 | // Tests and actions are grouped by operators |
327 | // ( expr ) Force precedence |
328 | // ! expr True if expr is false |
329 | // -not expr Same as ! expr |
330 | // expr1 [-a[nd]] expr2 And; expr2 is not evaluated if expr1 is false |
331 | // expr1 -o[r] expr2 Or; expr2 is not evaluated if expr1 is true |
332 | // expr1 , expr2 List; both expr1 and expr2 are always evaluated |
333 | // We implement: (), -a, -o |
334 | |
335 | while (*argv) { |
336 | char *arg = argv[0]; |
337 | char *arg1 = argv[1]; |
338 | /* --- Operators --- */ |
339 | if (strcmp(arg, "-a") == 0 |
340 | USE_DESKTOP(|| strcmp(arg, "-and") == 0) |
341 | ) { |
342 | /* no special handling required */ |
343 | } |
344 | else if (strcmp(arg, "-o") == 0 |
345 | USE_DESKTOP(|| strcmp(arg, "-or") == 0) |
346 | ) { |
347 | /* start new OR group */ |
348 | cur_group++; |
349 | appp = xrealloc(appp, (cur_group+2) * sizeof(*appp)); |
350 | appp[cur_group] = NULL; |
351 | appp[cur_group+1] = NULL; |
352 | cur_action = 0; |
353 | } |
354 | |
355 | /* --- Tests and actions --- */ |
356 | else if (strcmp(arg, "-print") == 0) { |
357 | need_print = 0; |
358 | (void) ALLOC_ACTION(print); |
359 | } |
360 | #if ENABLE_FEATURE_FIND_PRINT0 |
361 | else if (strcmp(arg, "-print0") == 0) { |
362 | need_print = 0; |
363 | (void) ALLOC_ACTION(print0); |
364 | } |
365 | #endif |
366 | else if (strcmp(arg, "-name") == 0) { |
367 | action_name *ap; |
368 | if (!*++argv) |
369 | bb_error_msg_and_die(bb_msg_requires_arg, arg); |
370 | ap = ALLOC_ACTION(name); |
371 | ap->pattern = arg1; |
372 | } |
373 | #if ENABLE_FEATURE_FIND_TYPE |
374 | else if (strcmp(arg, "-type") == 0) { |
375 | action_type *ap; |
376 | if (!*++argv) |
377 | bb_error_msg_and_die(bb_msg_requires_arg, arg); |
378 | ap = ALLOC_ACTION(type); |
379 | ap->type_mask = find_type(arg1); |
380 | } |
381 | #endif |
382 | #if ENABLE_FEATURE_FIND_PERM |
383 | /* TODO: |
384 | * -perm mode File's permission bits are exactly mode (octal or symbolic). |
385 | * Symbolic modes use mode 0 as a point of departure. |
386 | * -perm -mode All of the permission bits mode are set for the file. |
387 | * -perm +mode Any of the permission bits mode are set for the file. |
388 | */ |
389 | else if (strcmp(arg, "-perm") == 0) { |
390 | action_perm *ap; |
391 | if (!*++argv) |
392 | bb_error_msg_and_die(bb_msg_requires_arg, arg); |
393 | ap = ALLOC_ACTION(perm); |
394 | ap->perm_mask = xstrtol_range(arg1, 8, 0, 07777); |
395 | ap->perm_char = arg1[0]; |
396 | if (ap->perm_char == '-') |
397 | ap->perm_mask = -ap->perm_mask; |
398 | } |
399 | #endif |
400 | #if ENABLE_FEATURE_FIND_MTIME |
401 | else if (strcmp(arg, "-mtime") == 0) { |
402 | action_mtime *ap; |
403 | if (!*++argv) |
404 | bb_error_msg_and_die(bb_msg_requires_arg, arg); |
405 | ap = ALLOC_ACTION(mtime); |
406 | ap->mtime_days = xatol(arg1); |
407 | ap->mtime_char = arg1[0]; |
408 | if (ap->mtime_char == '-') |
409 | ap->mtime_days = -ap->mtime_days; |
410 | } |
411 | #endif |
412 | #if ENABLE_FEATURE_FIND_MMIN |
413 | else if (strcmp(arg, "-mmin") == 0) { |
414 | action_mmin *ap; |
415 | if (!*++argv) |
416 | bb_error_msg_and_die(bb_msg_requires_arg, arg); |
417 | ap = ALLOC_ACTION(mmin); |
418 | ap->mmin_mins = xatol(arg1); |
419 | ap->mmin_char = arg1[0]; |
420 | if (ap->mmin_char == '-') |
421 | ap->mmin_mins = -ap->mmin_mins; |
422 | } |
423 | #endif |
424 | #if ENABLE_FEATURE_FIND_NEWER |
425 | else if (strcmp(arg, "-newer") == 0) { |
426 | action_newer *ap; |
427 | struct stat stat_newer; |
428 | if (!*++argv) |
429 | bb_error_msg_and_die(bb_msg_requires_arg, arg); |
430 | xstat(arg1, &stat_newer); |
431 | ap = ALLOC_ACTION(newer); |
432 | ap->newer_mtime = stat_newer.st_mtime; |
433 | } |
434 | #endif |
435 | #if ENABLE_FEATURE_FIND_INUM |
436 | else if (strcmp(arg, "-inum") == 0) { |
437 | action_inum *ap; |
438 | if (!*++argv) |
439 | bb_error_msg_and_die(bb_msg_requires_arg, arg); |
440 | ap = ALLOC_ACTION(inum); |
441 | ap->inode_num = xatoul(arg1); |
442 | } |
443 | #endif |
444 | #if ENABLE_FEATURE_FIND_EXEC |
445 | else if (strcmp(arg, "-exec") == 0) { |
446 | int i; |
447 | action_exec *ap; |
448 | need_print = 0; |
449 | ap = ALLOC_ACTION(exec); |
450 | ap->exec_argv = ++argv; /* first arg after -exec */ |
451 | ap->exec_argc = 0; |
452 | while (1) { |
453 | if (!*argv) /* did not see ';' till end */ |
454 | bb_error_msg_and_die(bb_msg_requires_arg, arg); |
455 | if (LONE_CHAR(argv[0], ';')) |
456 | break; |
457 | argv++; |
458 | ap->exec_argc++; |
459 | } |
460 | if (ap->exec_argc == 0) |
461 | bb_error_msg_and_die(bb_msg_requires_arg, arg); |
462 | ap->subst_count = xmalloc(ap->exec_argc * sizeof(int)); |
463 | i = ap->exec_argc; |
464 | while (i--) |
465 | ap->subst_count[i] = count_subst(ap->exec_argv[i]); |
466 | } |
467 | #endif |
468 | #if ENABLE_DESKTOP |
469 | else if (LONE_CHAR(arg, '(')) { |
470 | action_paren *ap; |
471 | char **endarg; |
472 | int nested = 1; |
473 | |
474 | endarg = argv; |
475 | while (1) { |
476 | if (!*++endarg) |
477 | bb_error_msg_and_die("unpaired '('"); |
478 | if (LONE_CHAR(*endarg, '(')) |
479 | nested++; |
480 | else if (LONE_CHAR(*endarg, ')') && !--nested) { |
481 | *endarg = NULL; |
482 | break; |
483 | } |
484 | } |
485 | ap = ALLOC_ACTION(paren); |
486 | ap->subexpr = parse_params(argv + 1); |
487 | *endarg = ")"; /* restore NULLed parameter */ |
488 | argv = endarg; |
489 | } |
490 | else if (strcmp(arg, "-prune") == 0) { |
491 | (void) ALLOC_ACTION(prune); |
492 | } |
493 | else if (strcmp(arg, "-size") == 0) { |
494 | action_size *ap; |
495 | if (!*++argv) |
496 | bb_error_msg_and_die(bb_msg_requires_arg, arg); |
497 | ap = ALLOC_ACTION(size); |
498 | ap->size = XATOOFF(arg1); |
499 | } |
500 | #endif |
501 | else |
502 | bb_show_usage(); |
503 | argv++; |
504 | } |
505 | |
506 | return appp; |
507 | #undef ALLOC_ACTION |
508 | } |
509 | |
510 | |
511 | int find_main(int argc, char **argv) |
512 | { |
513 | int dereference = FALSE; |
514 | char *arg; |
515 | char **argp; |
516 | int i, firstopt, status = EXIT_SUCCESS; |
517 | |
518 | for (firstopt = 1; firstopt < argc; firstopt++) { |
519 | if (argv[firstopt][0] == '-') |
520 | break; |
521 | #if ENABLE_DESKTOP |
522 | if (LONE_CHAR(argv[firstopt], '(')) |
523 | break; |
524 | #endif |
525 | } |
526 | if (firstopt == 1) { |
527 | argv[0] = "."; |
528 | argv--; |
529 | firstopt++; |
530 | } |
531 | |
532 | // All options always return true. They always take effect, |
533 | // rather than being processed only when their place in the |
534 | // expression is reached |
535 | // We implement: -follow, -xdev |
536 | |
537 | /* Process options, and replace then with -a */ |
538 | /* (-a will be ignored by recursive parser later) */ |
539 | argp = &argv[firstopt]; |
540 | while ((arg = argp[0])) { |
541 | if (strcmp(arg, "-follow") == 0) { |
542 | dereference = TRUE; |
543 | argp[0] = "-a"; |
544 | } |
545 | #if ENABLE_FEATURE_FIND_XDEV |
546 | else if (strcmp(arg, "-xdev") == 0) { |
547 | struct stat stbuf; |
548 | if (!xdev_count) { |
549 | xdev_count = firstopt - 1; |
550 | xdev_dev = xmalloc(xdev_count * sizeof(dev_t)); |
551 | for (i = 1; i < firstopt; i++) { |
552 | /* not xstat(): shouldn't bomb out on |
553 | * "find not_exist exist -xdev" */ |
554 | if (stat(argv[i], &stbuf)) |
555 | stbuf.st_dev = -1L; |
556 | xdev_dev[i-1] = stbuf.st_dev; |
557 | } |
558 | } |
559 | argp[0] = "-a"; |
560 | } |
561 | #endif |
562 | argp++; |
563 | } |
564 | |
565 | actions = parse_params(&argv[firstopt]); |
566 | |
567 | for (i = 1; i < firstopt; i++) { |
568 | if (!recursive_action(argv[i], |
569 | TRUE, // recurse |
570 | dereference, // follow links |
571 | FALSE, // depth first |
572 | fileAction, // file action |
573 | fileAction, // dir action |
574 | NULL, // user data |
575 | 0)) // depth |
576 | status = EXIT_FAILURE; |
577 | } |
578 | return status; |
579 | } |