diff options
Diffstat (limited to 'libbb/recursive_action.c')
-rw-r--r-- | libbb/recursive_action.c | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/libbb/recursive_action.c b/libbb/recursive_action.c new file mode 100644 index 000000000..121a3dffd --- /dev/null +++ b/libbb/recursive_action.c | |||
@@ -0,0 +1,126 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Utility routines. | ||
4 | * | ||
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | ||
6 | * | ||
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | |||
12 | #undef DEBUG_RECURS_ACTION | ||
13 | |||
14 | /* | ||
15 | * Walk down all the directories under the specified | ||
16 | * location, and do something (something specified | ||
17 | * by the fileAction and dirAction function pointers). | ||
18 | * | ||
19 | * Unfortunately, while nftw(3) could replace this and reduce | ||
20 | * code size a bit, nftw() wasn't supported before GNU libc 2.1, | ||
21 | * and so isn't sufficiently portable to take over since glibc2.1 | ||
22 | * is so stinking huge. | ||
23 | */ | ||
24 | |||
25 | static int true_action(const char *fileName, struct stat *statbuf, void* userData, int depth) | ||
26 | { | ||
27 | return TRUE; | ||
28 | } | ||
29 | |||
30 | /* fileAction return value of 0 on any file in directory will make | ||
31 | * recursive_action() return 0, but it doesn't stop directory traversal | ||
32 | * (fileAction/dirAction will be called on each file). | ||
33 | * | ||
34 | * if !depthFirst, dirAction return value of 0 (FALSE) or 2 (SKIP) | ||
35 | * prevents recursion into that directory, instead | ||
36 | * recursive_action() returns 0 (if FALSE) or 1 (if SKIP). | ||
37 | * | ||
38 | * followLinks=0/1 differs mainly in handling of links to dirs. | ||
39 | * 0: lstat(statbuf). Calls fileAction on link name even if points to dir. | ||
40 | * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir. | ||
41 | */ | ||
42 | |||
43 | int recursive_action(const char *fileName, | ||
44 | int recurse, int followLinks, int depthFirst, | ||
45 | int (*fileAction)(const char *fileName, struct stat *statbuf, void* userData, int depth), | ||
46 | int (*dirAction)(const char *fileName, struct stat *statbuf, void* userData, int depth), | ||
47 | void* userData, | ||
48 | int depth) | ||
49 | { | ||
50 | struct stat statbuf; | ||
51 | int status; | ||
52 | DIR *dir; | ||
53 | struct dirent *next; | ||
54 | |||
55 | if (!fileAction) fileAction = true_action; | ||
56 | if (!dirAction) dirAction = true_action; | ||
57 | |||
58 | status = (followLinks ? stat : lstat)(fileName, &statbuf); | ||
59 | |||
60 | if (status < 0) { | ||
61 | #ifdef DEBUG_RECURS_ACTION | ||
62 | bb_error_msg("status=%d followLinks=%d TRUE=%d", | ||
63 | status, followLinks, TRUE); | ||
64 | #endif | ||
65 | bb_perror_msg("%s", fileName); | ||
66 | return FALSE; | ||
67 | } | ||
68 | |||
69 | /* If S_ISLNK(m), then we know that !S_ISDIR(m). | ||
70 | * Then we can skip checking first part: if it is true, then | ||
71 | * (!dir) is also true! */ | ||
72 | if ( /* (!followLinks && S_ISLNK(statbuf.st_mode)) || */ | ||
73 | !S_ISDIR(statbuf.st_mode) | ||
74 | ) { | ||
75 | return fileAction(fileName, &statbuf, userData, depth); | ||
76 | } | ||
77 | |||
78 | /* It's a directory (or a link to one, and followLinks is set) */ | ||
79 | |||
80 | if (!recurse) { | ||
81 | return dirAction(fileName, &statbuf, userData, depth); | ||
82 | } | ||
83 | |||
84 | if (!depthFirst) { | ||
85 | status = dirAction(fileName, &statbuf, userData, depth); | ||
86 | if (!status) { | ||
87 | bb_perror_msg("%s", fileName); | ||
88 | return FALSE; | ||
89 | } | ||
90 | if (status == SKIP) | ||
91 | return TRUE; | ||
92 | } | ||
93 | |||
94 | dir = opendir(fileName); | ||
95 | if (!dir) { | ||
96 | /* findutils-4.1.20 reports this */ | ||
97 | /* (i.e. it doesn't silently return with exit code 1) */ | ||
98 | /* To trigger: "find -exec rm -rf {} \;" */ | ||
99 | bb_perror_msg("%s", fileName); | ||
100 | return FALSE; | ||
101 | } | ||
102 | status = TRUE; | ||
103 | while ((next = readdir(dir)) != NULL) { | ||
104 | char *nextFile; | ||
105 | |||
106 | nextFile = concat_subpath_file(fileName, next->d_name); | ||
107 | if (nextFile == NULL) | ||
108 | continue; | ||
109 | if (!recursive_action(nextFile, TRUE, followLinks, depthFirst, | ||
110 | fileAction, dirAction, userData, depth+1)) { | ||
111 | status = FALSE; | ||
112 | } | ||
113 | free(nextFile); | ||
114 | } | ||
115 | closedir(dir); | ||
116 | if (depthFirst) { | ||
117 | if (!dirAction(fileName, &statbuf, userData, depth)) { | ||
118 | bb_perror_msg("%s", fileName); | ||
119 | return FALSE; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | if (!status) | ||
124 | return FALSE; | ||
125 | return TRUE; | ||
126 | } | ||