Add a dirent walker to the fileops API Since at least MS have something like GetFirstDirEnt() and GetNextDirEnt() (presumably with superior performance), we can let MS hackers add support for a dirent walker using that API instead, while we stick with the posix-style readdir() calls. Signed-off-by: Andreas Ericsson <ae@op5.se> 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
diff --git a/src/fileops.c b/src/fileops.c
index 62da145..0e6e7fd 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -134,3 +134,65 @@ 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)
+{
+ char path[PATH_MAX];
+ size_t wd_len;
+ DIR *dir;
+ struct dirent *de;
+
+ if (!wd)
+ 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);
+ if (!dir)
+ return GIT_ERROR;
+
+ while ((de = readdir(dir))) {
+ size_t de_len;
+ int result;
+
+ /* always skip '.' and '..' */
+ if (de->d_name[0] == '.') {
+ if (de->d_name[1] == '\0')
+ continue;
+ if (de->d_name[1] == '.' && de->d_name[2] == '\0')
+ continue;
+ }
+
+ de_len = strlen(de->d_name);
+ if (sizeof(path) < wd_len + de_len + 1) {
+ closedir(dir);
+ return GIT_ERROR;
+ }
+
+ strcpy(path + wd_len, de->d_name);
+ result = fn(arg, path);
+ if (result < 0) {
+ closedir(dir);
+ return result;
+ }
+ if (result > 0) {
+ closedir(dir);
+ return result;
+ }
+ }
+
+ closedir(dir);
+ return GIT_SUCCESS;
+}
diff --git a/src/fileops.h b/src/fileops.h
index 56d0888..2683a6b 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -17,7 +17,9 @@
#include <time.h>
#include <stdlib.h>
#include <string.h>
+#include <dirent.h>
#include "errors.h"
+#include "git/fileops.h"
typedef int git_file;
typedef struct stat gitfo_statbuf;
diff --git a/src/git/fileops.h b/src/git/fileops.h
new file mode 100644
index 0000000..856017f
--- /dev/null
+++ b/src/git/fileops.h
@@ -0,0 +1,32 @@
+#ifndef INCLUDE_git_fileops_h__
+#define INCLUDE_git_fileops_h__
+
+/**
+ * @file git/fileops.h
+ * @brief Git platform agnostic filesystem operations
+ * @defgroup Git gitfiles
+ * @ingroup Git
+ * @{
+ */
+
+#include "common.h"
+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__ */