ddaf9b896579fcaa5db9875f4789a4014eacae24
[oweals/busybox.git] / libbb / recursive_action.c
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 /*
31  * followLinks=0/1 differs mainly in handling of links to dirs.
32  * 0: lstat(statbuf). Calls fileAction on link name even if points to dir.
33  * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir.
34  */
35
36 int recursive_action(const char *fileName,
37                 int recurse, int followLinks, int depthFirst,
38                 int (*fileAction)(const char *fileName, struct stat *statbuf, void* userData, int depth),
39                 int (*dirAction)(const char *fileName, struct stat *statbuf, void* userData, int depth),
40                 void* userData,
41                 int depth)
42 {
43         struct stat statbuf;
44         int status;
45         DIR *dir;
46         struct dirent *next;
47
48         if (!fileAction) fileAction = true_action;
49         if (!dirAction) dirAction = true_action;
50
51         status = (followLinks ? stat : lstat)(fileName, &statbuf);
52
53         if (status < 0) {
54 #ifdef DEBUG_RECURS_ACTION
55                 bb_error_msg("status=%d followLinks=%d TRUE=%d",
56                                 status, followLinks, TRUE);
57 #endif
58                 bb_perror_msg("%s", fileName);
59                 return FALSE;
60         }
61
62         /* If S_ISLNK(m), then we know that !S_ISDIR(m).
63          * Then we can skip checking first part: if it is true, then
64          * (!dir) is also true! */
65         if ( /* (!followLinks && S_ISLNK(statbuf.st_mode)) || */
66          !S_ISDIR(statbuf.st_mode)
67         ) {
68                 return fileAction(fileName, &statbuf, userData, depth);
69         }
70
71         /* It's a directory (or a link to one, and followLinks is set) */
72
73         if (!recurse) {
74                 return dirAction(fileName, &statbuf, userData, depth);
75         }
76
77         if (!depthFirst) {
78                 status = dirAction(fileName, &statbuf, userData, depth);
79                 if (!status) {
80                         bb_perror_msg("%s", fileName);
81                         return FALSE;
82                 }
83                 if (status == SKIP)
84                         return TRUE;
85         }
86
87         dir = opendir(fileName);
88         if (!dir) {
89                 return FALSE;
90         }
91         status = TRUE;
92         while ((next = readdir(dir)) != NULL) {
93                 char *nextFile;
94
95                 nextFile = concat_subpath_file(fileName, next->d_name);
96                 if (nextFile == NULL)
97                         continue;
98                 if (!recursive_action(nextFile, TRUE, followLinks, depthFirst,
99                                 fileAction, dirAction, userData, depth+1)) {
100                         status = FALSE;
101                 }
102                 free(nextFile);
103         }
104         closedir(dir);
105         if (depthFirst) {
106                 if (!dirAction(fileName, &statbuf, userData, depth)) {
107                         bb_perror_msg("%s", fileName);
108                         return FALSE;
109                 }
110         }
111
112         if (!status)
113                 return FALSE;
114         return TRUE;
115 }