On Wed, Jul 16, 2025 at 6:34 PM Derrick Stolee via GitGitGadget <gitgitgadget@xxxxxxxxx> wrote: > > From: Derrick Stolee <stolee@xxxxxxxxx> > > There is sometimes a need to visit every file within a directory, > recursively. The main example is remove_dir_recursively(), though it has > some extra flags that make it want to iterate over paths in a custom > way. There is also the fill_directory() approach but that involves an > index and a pathspec. > > This change adds a new for_each_file_in_dir() method that will be > helpful in the next change. > > Signed-off-by: Derrick Stolee <stolee@xxxxxxxxx> > --- > dir.c | 28 ++++++++++++++++++++++++++++ > dir.h | 14 ++++++++++++++ > 2 files changed, 42 insertions(+) > > diff --git a/dir.c b/dir.c > index d2b0a5aef670..2e567ff92746 100644 > --- a/dir.c > +++ b/dir.c > @@ -30,6 +30,7 @@ > #include "read-cache-ll.h" > #include "setup.h" > #include "sparse-index.h" > +#include "strbuf.h" > #include "submodule-config.h" > #include "symlinks.h" > #include "trace2.h" > @@ -87,6 +88,33 @@ struct dirent *readdir_skip_dot_and_dotdot(DIR *dirp) > return e; > } > > +int for_each_file_in_dir(struct strbuf *path, file_iterator fn, const void *data) > +{ > + struct dirent *e; > + int res = 0; > + size_t baselen = path->len; > + DIR *dir = opendir(path->buf); > + > + if (!dir) > + return 0; > + > + while (!res && (e = readdir_skip_dot_and_dotdot(dir)) != NULL) { > + unsigned char dtype = get_dtype(e, path, 0); > + strbuf_setlen(path, baselen); > + strbuf_addstr(path, e->d_name); > + > + if (dtype == DT_REG) { > + res = fn(path->buf, data); > + } else if (dtype == DT_DIR) { > + strbuf_addch(path, '/'); > + res = for_each_file_in_dir(path, fn, data); > + } > + } > + > + closedir(dir); > + return res; > +} > + > int count_slashes(const char *s) > { > int cnt = 0; > diff --git a/dir.h b/dir.h > index d7e71aa8daa7..f4235cc12a2f 100644 > --- a/dir.h > +++ b/dir.h > @@ -536,6 +536,20 @@ int get_sparse_checkout_patterns(struct pattern_list *pl); > */ > int remove_dir_recursively(struct strbuf *path, int flag); > > +/* > + * This function pointer type is called on each file discovered in > + * for_each_file_in_dir. The iteration stops if this method returns > + * non-zero. > + */ > +typedef int (*file_iterator)(const char *path, const void *data); > + > +struct strbuf; > +/* > + * Given a directory path, recursively visit each file within, including > + * within subdirectories. > + */ > +int for_each_file_in_dir(struct strbuf *path, file_iterator fn, const void *data); > + > /* > * Tries to remove the path, along with leading empty directories so long as > * those empty directories are not startup_info->original_cwd. Ignores > -- > gitgitgadget Looks reasonable.