Rewrite git_foreach_dirent into gitfo_dirent Our fileops API is currently private. We aren't planning on supplying a cross-platform file API to applications that link to us. If we did, we'd probably whole-sale publish fileops, not just the dirent code. By moving it to be private we can also change the call signature to permit the buffer to be passed down through the call chain. This is very helpful when we are doing a recursive scan as we can reuse just one buffer in all stack frames, reducing the impact the recursion has on the stack frames in the data cache. Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
diff --git a/src/fileops.c b/src/fileops.c
index 1dd35dc..0867c7b 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -187,31 +187,25 @@ int gitfo_close_cached(gitfo_cache *ioc)
return gitfo_close(fd);
}
-/**
- * Walk a directory and run 'fn' for each encountered entry
- * (except '.' and '..').
- */
-int git_foreach_dirent(const char *wd, int (*fn)(void *, const char *), void *arg)
+int gitfo_dirent(
+ char *path,
+ size_t path_sz,
+ int (*fn)(void *, char *),
+ void *arg)
{
- char path[GIT_PATH_MAX];
- size_t wd_len;
+ size_t wd_len = strlen(path);
DIR *dir;
struct dirent *de;
- if (!wd)
+ if (!wd_len || path_sz < wd_len + 2)
return GIT_ERROR;
- wd_len = strlen(wd);
- if (!wd_len || sizeof(path) < wd_len + 2)
- return GIT_ERROR;
-
- strcpy(path, wd);
while (path[wd_len - 1] == '/')
wd_len--;
path[wd_len++] = '/';
path[wd_len] = '\0';
- dir = opendir(wd);
+ dir = opendir(path);
if (!dir)
return git_os_error();
@@ -228,7 +222,7 @@ int git_foreach_dirent(const char *wd, int (*fn)(void *, const char *), void *ar
}
de_len = strlen(de->d_name);
- if (sizeof(path) < wd_len + de_len + 1) {
+ if (path_sz < wd_len + de_len + 1) {
closedir(dir);
return GIT_ERROR;
}
diff --git a/src/fileops.h b/src/fileops.h
index 336ab27..86726c5 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -9,6 +9,7 @@
/** Force 64 bit off_t size on POSIX. */
#define _FILE_OFFSET_BITS 64
+#include "common.h"
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
@@ -18,8 +19,6 @@
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
-#include "errors.h"
-#include "git/fileops.h"
#define GITFO_BUF_INIT {NULL, 0}
@@ -43,6 +42,23 @@ extern off_t gitfo_size(git_file fd);
extern int gitfo_read_file(gitfo_buf *obj, const char *path);
extern void gitfo_free_buf(gitfo_buf *obj);
+/**
+ * Walk each directory entry, except '.' and '..', calling fn(state).
+ *
+ * @param pathbuf buffer the function reads the initial directory
+ * path from, and updates with each successive entry's name.
+ * @param pathmax maximum allocation of pathbuf.
+ * @param fn function to invoke with each entry. The first arg is
+ * the input state and the second arg is pathbuf. The function
+ * may modify the pathbuf, but only by appending new text.
+ * @param state to pass to fn as the first arg.
+ */
+extern int gitfo_dirent(
+ char *pathbuf,
+ size_t pathmax,
+ int (*fn)(void *, char *),
+ void *state);
+
extern gitfo_cache *gitfo_enable_caching(git_file fd, size_t cache_size);
extern int gitfo_write_cached(gitfo_cache *ioc, void *buf, size_t len);
extern int gitfo_flush_cached(gitfo_cache *ioc);
diff --git a/src/git/fileops.h b/src/git/fileops.h
deleted file mode 100644
index 657cec1..0000000
--- a/src/git/fileops.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef INCLUDE_git_fileops_h__
-#define INCLUDE_git_fileops_h__
-
-#include "common.h"
-
-/**
- * @file git/fileops.h
- * @brief Git platform agnostic filesystem operations
- * @defgroup git_fileops Git filesystem operations
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * For each directory entry (except "." and ".."), run the function
- * "fn", passing it "arg" as its first argument and the path to
- * the entry as the second argument.
- * @param dir The directory to walk
- * @param fn The callback function to run for each entry in *dir.
- * "fn" may return >0 to signal "I'm done. Stop parsing and
- * return successfully" or <0 to signal an error. All non-zero
- * return codes cause directory traversal to stop.
- * @param arg The first argument that will be passed to 'fn'
- * @return GIT_SUCCESS if all entries were successfully traversed,
- * otherwise the result of fn.
- */
-GIT_EXTERN(int) git_foreach_dirent(const char *dir,
- int (*fn)(void *, const char *), void *arg);
-
-/** @} */
-GIT_END_DECL
-#endif /* INCLUDE_git_fileops_h__ */
diff --git a/tests/t0020-dirent.c b/tests/t0020-dirent.c
new file mode 100644
index 0000000..4c65453
--- /dev/null
+++ b/tests/t0020-dirent.c
@@ -0,0 +1,48 @@
+#include "test_lib.h"
+#include "fileops.h"
+
+static char path_buffer[GIT_PATH_MAX];
+static int state_loc;
+static const char* names[] = {
+ "./a",
+ "./asdf",
+ "./pack-foo.pack",
+ NULL
+};
+
+static int one_entry(void *state, char *path)
+{
+ const char **c;
+
+ must_be_true(state == &state_loc);
+ must_be_true(path == path_buffer);
+ for (c = names; *c; c++) {
+ if (!strcmp(*c, path)) {
+ *c = "";
+ return 0;
+ }
+ }
+ test_die("unexpected path \"%s\"", path);
+}
+
+BEGIN_TEST(setup)
+ const char **c;
+ for (c = names; *c; c++) {
+ git_file fd = gitfo_creat(*c, 0600);
+ must_be_true(fd >= 0);
+ gitfo_close(fd);
+ }
+END_TEST
+
+BEGIN_TEST(direent_walk)
+ const char **c;
+
+ strcpy(path_buffer, ".");
+ must_pass(gitfo_dirent(path_buffer,
+ sizeof(path_buffer),
+ one_entry,
+ &state_loc));
+
+ for (c = names; *c; c++)
+ must_pass(strcmp("", *c));
+END_TEST