stty: fix bug reported by Paul Albrecht <albrecht@rdi1.com> -
[oweals/busybox.git] / libbb / recursive_action.c
index 0d6567ddab6a9e1865cbd0babf55d8778046669f..25a87b88e451a48b560d2d01d1c7c6bbf96188b8 100644 (file)
@@ -11,7 +11,6 @@
 
 #undef DEBUG_RECURS_ACTION
 
-
 /*
  * Walk down all the directories under the specified
  * location, and do something (something specified
  * is so stinking huge.
  */
 
-static int true_action(const char *fileName, struct stat *statbuf, void* userData)
+static int true_action(const char *fileName, struct stat *statbuf, void* userData, int depth)
 {
        return TRUE;
 }
 
+/* fileAction return value of 0 on any file in directory will make
+ * recursive_action() return 0, but it doesn't stop directory traversal
+ * (fileAction/dirAction will be called on each file).
+ *
+ * if !depthFirst, dirAction return value of 0 (FALSE) or 2 (SKIP)
+ * prevents recursion into that directory, instead
+ * recursive_action() returns 0 (if FALSE) or 1 (if SKIP).
+ *
+ * followLinks=0/1 differs mainly in handling of links to dirs.
+ * 0: lstat(statbuf). Calls fileAction on link name even if points to dir.
+ * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir.
+ */
+
 int recursive_action(const char *fileName,
                int recurse, int followLinks, int depthFirst,
-               int (*fileAction) (const char *fileName, struct stat * statbuf, void* userData),
-               int (*dirAction) (const char *fileName, struct stat * statbuf, void* userData),
-               void* userData)
+               int (*fileAction)(const char *fileName, struct stat *statbuf, void* userData, int depth),
+               int (*dirAction)(const char *fileName, struct stat *statbuf, void* userData, int depth),
+               void* userData,
+               int depth)
 {
        struct stat statbuf;
        int status;
@@ -53,21 +66,23 @@ int recursive_action(const char *fileName,
                return FALSE;
        }
 
-       if (!followLinks && (S_ISLNK(statbuf.st_mode))) {
-               return fileAction(fileName, &statbuf, userData);
+       /* If S_ISLNK(m), then we know that !S_ISDIR(m).
+        * Then we can skip checking first part: if it is true, then
+        * (!dir) is also true! */
+       if ( /* (!followLinks && S_ISLNK(statbuf.st_mode)) || */
+        !S_ISDIR(statbuf.st_mode)
+       ) {
+               return fileAction(fileName, &statbuf, userData, depth);
        }
 
+       /* It's a directory (or a link to one, and followLinks is set) */
+
        if (!recurse) {
-               if (S_ISDIR(statbuf.st_mode)) {
-                       return dirAction(fileName, &statbuf, userData);
-               }
+               return dirAction(fileName, &statbuf, userData, depth);
        }
 
-       if (!S_ISDIR(statbuf.st_mode))
-               return fileAction(fileName, &statbuf, userData);
-
        if (!depthFirst) {
-               status = dirAction(fileName, &statbuf, userData);
+               status = dirAction(fileName, &statbuf, userData, depth);
                if (!status) {
                        bb_perror_msg("%s", fileName);
                        return FALSE;
@@ -78,6 +93,10 @@ int recursive_action(const char *fileName,
 
        dir = opendir(fileName);
        if (!dir) {
+               /* findutils-4.1.20 reports this */
+               /* (i.e. it doesn't silently return with exit code 1) */
+               /* To trigger: "find -exec rm -rf {} \;" */
+               bb_perror_msg("%s", fileName);
                return FALSE;
        }
        status = TRUE;
@@ -88,14 +107,14 @@ int recursive_action(const char *fileName,
                if (nextFile == NULL)
                        continue;
                if (!recursive_action(nextFile, TRUE, followLinks, depthFirst,
-                                       fileAction, dirAction, userData)) {
+                               fileAction, dirAction, userData, depth+1)) {
                        status = FALSE;
                }
                free(nextFile);
        }
        closedir(dir);
        if (depthFirst) {
-               if (!dirAction(fileName, &statbuf, userData)) {
+               if (!dirAction(fileName, &statbuf, userData, depth)) {
                        bb_perror_msg("%s", fileName);
                        return FALSE;
                }