Create an option for the dir_iterator API to iterate over subdirectories
only after having iterated through their contents. This feature was
predicted, although not implemented by 0fe5043 ("dir_iterator: new API
for iterating over a directory tree", 2016-06-18).

Add the "flags" parameter to dir_iterator_create, allowing for the
aforementioned "depth-first" iteration mode to be enabled. Currently,
the only acceptable flag is DIR_ITERATOR_DEPTH_FIRST.

This is useful for recursively removing a directory and calling rmdir()
on a directory only after all of its contents have been wiped.

Signed-off-by: Daniel Ferreira <bnm...@gmail.com>
---
 dir-iterator.c | 46 ++++++++++++++++++++++++++++++++++++++++++----
 dir-iterator.h | 14 +++++++++++---
 2 files changed, 53 insertions(+), 7 deletions(-)

diff --git a/dir-iterator.c b/dir-iterator.c
index 853c040..545d333 100644
--- a/dir-iterator.c
+++ b/dir-iterator.c
@@ -48,6 +48,9 @@ struct dir_iterator_int {
         * that will be included in this iteration.
         */
        struct dir_iterator_level *levels;
+
+       /* Holds the flags passed to dir_iterator_begin(). */
+       unsigned flags;
 };

 static inline void push_dir_level(struct dir_iterator_int *iter, struct 
dir_iterator_level *level)
@@ -114,12 +117,14 @@ int dir_iterator_advance(struct dir_iterator 
*dir_iterator)
                        }

                        level->initialized = 1;
-               } else if (S_ISDIR(iter->base.st.st_mode)) {
+               } else if (S_ISDIR(iter->base.st.st_mode) &&
+               !iter->flags & DIR_ITERATOR_DEPTH_FIRST) {
                        if (level->dir_state == DIR_STATE_ITER) {
                                /*
                                 * The directory was just iterated
                                 * over; now prepare to iterate into
-                                * it.
+                                * it (unless an option is set for us
+                                * to do otherwise).
                                 */
                                push_dir_level(iter, level);
                                continue;
@@ -153,10 +158,27 @@ int dir_iterator_advance(struct dir_iterator 
*dir_iterator)
                        de = readdir(level->dir);

                        if (!de) {
-                               /* This level is exhausted; pop up a level. */
+                               /* This level is exhausted  */
                                if (errno) {
                                        warning("error reading directory %s: 
%s",
                                                iter->base.path.buf, 
strerror(errno));
+                               } else if (iter->flags & 
DIR_ITERATOR_DEPTH_FIRST) {
+                                       /* If we are handling dirpaths after 
their contents,
+                                        * we have to iterate over the 
directory now that we'll
+                                        * have finished iterating into it. */
+                                       level->dir = NULL;
+
+                                       if (pop_dir_level(iter, level) == 0)
+                                               return 
dir_iterator_abort(dir_iterator);
+
+                                       level = &iter->levels[iter->levels_nr - 
1];
+                                       /* Remove a trailing slash */
+                                       strbuf_strip_suffix(&iter->base.path, 
"/");
+
+                                       if (set_iterator_data(iter, level))
+                                               continue;
+
+                                       return ITER_OK;
                                } else if (closedir(level->dir))
                                        warning("error closing directory %s: 
%s",
                                                iter->base.path.buf, 
strerror(errno));
@@ -175,8 +197,22 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
                        if (set_iterator_data(iter, level))
                                continue;

+                       /*
+                        * If we want to iterate dirs after files, we shall
+                        * begin looking into them *before* we return the dir
+                        * itself.
+                        */
+                       if (S_ISDIR(iter->base.st.st_mode) &&
+                       iter->flags & DIR_ITERATOR_DEPTH_FIRST) {
+                               push_dir_level(iter, level);
+                               goto continue_outer_loop;
+                       }
+
                        return ITER_OK;
                }
+
+continue_outer_loop:
+               ;
        }
 }

@@ -201,7 +237,7 @@ int dir_iterator_abort(struct dir_iterator *dir_iterator)
        return ITER_DONE;
 }

-struct dir_iterator *dir_iterator_begin(const char *path)
+struct dir_iterator *dir_iterator_begin(const char *path, unsigned flags)
 {
        struct dir_iterator_int *iter = xcalloc(1, sizeof(*iter));
        struct dir_iterator *dir_iterator = &iter->base;
@@ -209,6 +245,8 @@ struct dir_iterator *dir_iterator_begin(const char *path)
        if (!path || !*path)
                die("BUG: empty path passed to dir_iterator_begin()");

+       iter->flags = flags;
+
        strbuf_init(&iter->base.path, PATH_MAX);
        strbuf_addstr(&iter->base.path, path);

diff --git a/dir-iterator.h b/dir-iterator.h
index 27739e6..28ff3df 100644
--- a/dir-iterator.h
+++ b/dir-iterator.h
@@ -38,6 +38,13 @@
  * dir_iterator_advance() again.
  */

+/* Possible flags for dir_iterator_begin().
+ *
+ * DIR_ITERATOR_DEPTH_FIRST: ensures subdirectories and their contents
+ * are iterated through before the containing directory.
+ */
+#define DIR_ITERATOR_DEPTH_FIRST (1 << 1)
+
 struct dir_iterator {
        /* The current path: */
        struct strbuf path;
@@ -57,15 +64,16 @@ struct dir_iterator {
 };

 /*
- * Start a directory iteration over path. Return a dir_iterator that
- * holds the internal state of the iteration.
+ * Start a directory iteration over path, with options specified in
+ * 'flags'. Return a dir_iterator that holds the internal state of
+ * the iteration.
  *
  * The iteration includes all paths under path, not including path
  * itself and not including "." or ".." entries.
  *
  * path is the starting directory. An internal copy will be made.
  */
-struct dir_iterator *dir_iterator_begin(const char *path);
+struct dir_iterator *dir_iterator_begin(const char *path, unsigned flags);

 /*
  * Advance the iterator to the first or next item and return ITER_OK.
--
2.7.4 (Apple Git-66)

Reply via email to