From: Daniel Ferreira <bnmvco@gmail.com>
To: git@vger.kernel.org
Cc: gitster@pobox.com, sbeller@google.com, pclouds@gmail.com,
mhagger@alum.mit.edu, Daniel Ferreira <bnmvco@gmail.com>
Subject: [PATCH v3 1/2] [GSoC] dir_iterator: iterate over dir after its contents
Date: Sat, 25 Mar 2017 15:12:30 -0300 [thread overview]
Message-ID: <1490465551-71056-2-git-send-email-bnmvco@gmail.com> (raw)
In-Reply-To: <1490465551-71056-1-git-send-email-bnmvco@gmail.com>
Create an option for the dir_iterator API to iterate over a directory
path only after having iterated through its contents. This feature was
predicted, although not implemented by 0fe5043 ("dir_iterator: new API
for iterating over a directory tree", 2016-06-18).
This is useful for recursively removing a directory and calling rmdir()
on a directory only after all of its contents have been wiped.
An "options" member has been added to the dir_iterator struct. It
contains the "iterate_dirs_after_files" flag, that enables the feature
when set to 1. Default behavior continues to be iterating over directory
paths before its contents.
Two inline functions have been added to dir_iterator's code to avoid
code repetition inside dir_iterator_advance() and make code more clear.
No particular functions or wrappers for setting the options struct's
fields have been added to avoid either breaking the current dir_iterator
API or over-engineering an extremely simple option architecture.
Signed-off-by: Daniel Ferreira <bnmvco@gmail.com>
---
dir-iterator.c | 100 ++++++++++++++++++++++++++++++++++++++++++++-------------
dir-iterator.h | 7 ++++
2 files changed, 84 insertions(+), 23 deletions(-)
diff --git a/dir-iterator.c b/dir-iterator.c
index 34182a9..833d56a 100644
--- a/dir-iterator.c
+++ b/dir-iterator.c
@@ -50,6 +50,43 @@ struct dir_iterator_int {
struct dir_iterator_level *levels;
};
+static inline void push_dir_level(struct dir_iterator_int *iter, struct dir_iterator_level *level)
+{
+ level->dir_state = DIR_STATE_RECURSE;
+ ALLOC_GROW(iter->levels, iter->levels_nr + 1,
+ iter->levels_alloc);
+ level = &iter->levels[iter->levels_nr++];
+ level->initialized = 0;
+}
+
+static inline int pop_dir_level(struct dir_iterator_int *iter, struct dir_iterator_level *level)
+{
+ return --iter->levels_nr;
+}
+
+static inline int set_iterator_data(struct dir_iterator_int *iter, struct dir_iterator_level *level)
+{
+ if (lstat(iter->base.path.buf, &iter->base.st) < 0) {
+ if (errno != ENOENT)
+ warning("error reading path '%s': %s",
+ iter->base.path.buf,
+ strerror(errno));
+ return -1;
+ }
+
+ /*
+ * We have to set these each time because
+ * the path strbuf might have been realloc()ed.
+ */
+ iter->base.relative_path =
+ iter->base.path.buf + iter->levels[0].prefix_len;
+ iter->base.basename =
+ iter->base.path.buf + level->prefix_len;
+ level->dir_state = DIR_STATE_ITER;
+
+ return 0;
+}
+
int dir_iterator_advance(struct dir_iterator *dir_iterator)
{
struct dir_iterator_int *iter =
@@ -77,18 +114,16 @@ 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->base.options.iterate_dirs_after_files) {
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).
*/
- level->dir_state = DIR_STATE_RECURSE;
- ALLOC_GROW(iter->levels, iter->levels_nr + 1,
- iter->levels_alloc);
- level = &iter->levels[iter->levels_nr++];
- level->initialized = 0;
+ push_dir_level(iter, level);
continue;
} else {
/*
@@ -104,7 +139,7 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
* This level is exhausted (or wasn't opened
* successfully); pop up a level.
*/
- if (--iter->levels_nr == 0)
+ if (pop_dir_level(iter, level) == 0)
return dir_iterator_abort(dir_iterator);
continue;
@@ -120,16 +155,33 @@ 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->base.options.iterate_dirs_after_files) {
+ /* 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));
level->dir = NULL;
- if (--iter->levels_nr == 0)
+ if (pop_dir_level(iter, level) == 0)
return dir_iterator_abort(dir_iterator);
break;
}
@@ -138,26 +190,26 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
continue;
strbuf_addstr(&iter->base.path, de->d_name);
- if (lstat(iter->base.path.buf, &iter->base.st) < 0) {
- if (errno != ENOENT)
- warning("error reading path '%s': %s",
- iter->base.path.buf,
- strerror(errno));
+
+ if (set_iterator_data(iter, level))
continue;
- }
/*
- * We have to set these each time because
- * the path strbuf might have been realloc()ed.
+ * If we want to iterate dirs after files, we shall
+ * begin looking into them *before* we return the dir
+ * itself.
*/
- iter->base.relative_path =
- iter->base.path.buf + iter->levels[0].prefix_len;
- iter->base.basename =
- iter->base.path.buf + level->prefix_len;
- level->dir_state = DIR_STATE_ITER;
+ if (S_ISDIR(iter->base.st.st_mode) &&
+ iter->base.options.iterate_dirs_after_files) {
+ push_dir_level(iter, level);
+ goto continue_outer_loop;
+ }
return ITER_OK;
}
+
+continue_outer_loop:
+ ;
}
}
@@ -190,6 +242,8 @@ struct dir_iterator *dir_iterator_begin(const char *path)
if (!path || !*path)
die("BUG: empty path passed to dir_iterator_begin()");
+ iter->base.options.iterate_dirs_after_files = 0;
+
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..4304913 100644
--- a/dir-iterator.h
+++ b/dir-iterator.h
@@ -38,7 +38,14 @@
* dir_iterator_advance() again.
*/
+struct dir_iterator_options {
+ unsigned iterate_dirs_after_files : 1;
+};
+
struct dir_iterator {
+ /* Options for dir_iterator */
+ struct dir_iterator_options options;
+
/* The current path: */
struct strbuf path;
--
2.7.4 (Apple Git-66)
next prev parent reply other threads:[~2017-03-25 18:20 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-03-25 18:12 [PATCH v3 0/2] [GSoC] remove_subtree(): reimplement using iterators Daniel Ferreira
2017-03-25 18:12 ` Daniel Ferreira [this message]
2017-03-26 22:40 ` [PATCH v3 1/2] [GSoC] dir_iterator: iterate over dir after its contents Michael Haggerty
2017-03-26 22:48 ` Michael Haggerty
2017-03-25 18:12 ` [PATCH v3 2/2] [GSoC] remove_subtree(): reimplement using iterators Daniel Ferreira
2017-03-28 17:13 ` [PATCH v3 0/2] " Stefan Beller
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1490465551-71056-2-git-send-email-bnmvco@gmail.com \
--to=bnmvco@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=mhagger@alum.mit.edu \
--cc=pclouds@gmail.com \
--cc=sbeller@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).