From ea790f337b0e401f5e4acabf9af9c2bc756d5f3b Mon Sep 17 00:00:00 2001 From: Andreas Ericsson Date: Sat, 29 Nov 2008 15:34:20 +0100 Subject: [PATCH] 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 Signed-off-by: Shawn O. Pearce --- src/fileops.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++ src/fileops.h | 2 ++ src/git/fileops.h | 32 ++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 src/git/fileops.h diff --git a/src/fileops.c b/src/fileops.c index 62da145b9..0e6e7fdcb 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 56d0888fe..2683a6b43 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -17,7 +17,9 @@ #include #include #include +#include #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 000000000..856017fbf --- /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__ */