dir: add generic "walk all files" helper
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@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Derrick Stolee committed
Sep 12, 2025 at 10:30 UTC
1588e836bb956d14e6cb38e35933ed2749c023b4
2 files changed
+42
dir.c
+28
@@ -30,6 +30,7 @@
30
#include "read-cache-ll.h"
31
#include "setup.h"
32
#include "sparse-index.h"
33
+#include "strbuf.h"
34
#include "submodule-config.h"
35
#include "symlinks.h"
36
#include "trace2.h"
@@ -87,6 +88,33 @@ struct dirent *readdir_skip_dot_and_dotdot(DIR *dirp)
88
return e;
89
}
90
91
+int for_each_file_in_dir(struct strbuf *path, file_iterator fn, const void *data)
92
+{
93
+ struct dirent *e;
94
+ int res = 0;
95
+ size_t baselen = path->len;
96
+ DIR *dir = opendir(path->buf);
97
+
98
+ if (!dir)
99
+ return 0;
100
+
101
+ while (!res && (e = readdir_skip_dot_and_dotdot(dir)) != NULL) {
102
+ unsigned char dtype = get_dtype(e, path, 0);
103
+ strbuf_setlen(path, baselen);
104
+ strbuf_addstr(path, e->d_name);
105
+
106
+ if (dtype == DT_REG) {
107
+ res = fn(path->buf, data);
108
+ } else if (dtype == DT_DIR) {
109
+ strbuf_addch(path, '/');
110
+ res = for_each_file_in_dir(path, fn, data);
111
+ }
112
+ }
113
+
114
+ closedir(dir);
115
+ return res;
116
+}
117
+
118
int count_slashes(const char *s)
119
{
120
int cnt = 0;
dir.h
+14
@@ -536,6 +536,20 @@ int get_sparse_checkout_patterns(struct pattern_list *pl);
536
*/
537
int remove_dir_recursively(struct strbuf *path, int flag);
538
539
+/*
540
+ * This function pointer type is called on each file discovered in
541
+ * for_each_file_in_dir. The iteration stops if this method returns
542
+ * non-zero.
543
+ */
544
+typedef int (*file_iterator)(const char *path, const void *data);
545
+
546
+struct strbuf;
547
+/*
548
+ * Given a directory path, recursively visit each file within, including
549
+ * within subdirectories.
550
+ */
551
+int for_each_file_in_dir(struct strbuf *path, file_iterator fn, const void *data);
552
+
553
/*
554
* Tries to remove the path, along with leading empty directories so long as
555
* those empty directories are not startup_info->original_cwd. Ignores