summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@microsoft.com>2015-04-29 11:05:27 -0400
committerEdward Thomson <ethomson@microsoft.com>2015-05-01 12:31:05 -0400
commitedbfc52cdd8657371c53070c5e09b58e004bb67a (patch)
tree08e658ddc002071357397647aa5fa4453e56fe39
parent544139f50bd7471a62135b29b6a1a2f7c64a1a1c (diff)
downloadlibgit2-edbfc52cdd8657371c53070c5e09b58e004bb67a.tar.gz
git_path: introduce 'git_path_diriter'
Introduce a new `git_path_diriter` that can iterate directories efficiently for each platform.
-rw-r--r--src/path.c130
-rw-r--r--src/path.h32
-rw-r--r--src/posix.h1
-rw-r--r--src/unix/posix.h1
4 files changed, 163 insertions, 1 deletions
diff --git a/src/path.c b/src/path.c
index 6a636bbd2..2390b4fd0 100644
--- a/src/path.c
+++ b/src/path.c
@@ -260,6 +260,20 @@ int git_path_root(const char *path)
return -1; /* Not a real error - signals that path is not rooted */
}
+void git_path_trim_slashes(git_buf *path)
+{
+ int ceiling = git_path_root(path->ptr) + 1;
+ assert(ceiling >= 0);
+
+ while (path->size > (size_t)ceiling) {
+ if (path->ptr[path->size-1] != '/')
+ break;
+
+ path->ptr[path->size-1] = '\0';
+ path->size--;
+ }
+}
+
int git_path_join_unrooted(
git_buf *path_out, const char *path, const char *base, ssize_t *root_at)
{
@@ -1181,6 +1195,122 @@ int git_path_with_stat_cmp_icase(const void *a, const void *b)
return strcasecmp(psa->path, psb->path);
}
+int git_path_diriter_init(
+ git_path_diriter *diriter,
+ const char *path,
+ unsigned int flags)
+{
+ assert(diriter && path);
+
+ memset(diriter, 0, sizeof(git_path_diriter));
+
+ if (git_buf_puts(&diriter->path, path) < 0)
+ return -1;
+
+ git_path_mkposix(diriter->path.ptr);
+ git_path_trim_slashes(&diriter->path);
+
+ if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) {
+ git_buf_free(&diriter->path);
+
+ giterr_set(GITERR_OS, "Failed to open directory '%s'", path);
+ return -1;
+ }
+
+#ifdef GIT_USE_ICONV
+ if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
+ (void)git_path_iconv_init_precompose(&ic);
+#endif
+
+ diriter->parent_len = diriter->path.size;
+ diriter->flags = flags;
+
+ return 0;
+}
+
+int git_path_diriter_next(
+ const char **out,
+ size_t *out_len,
+ git_path_diriter *diriter)
+{
+ struct dirent *de;
+ const char *filename;
+ size_t filename_len;
+ bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT);
+ int error = 0;
+
+ assert(out && out_len && diriter);
+
+ *out = NULL;
+ *out_len = 0;
+
+ errno = 0;
+
+ do {
+ if ((de = readdir(diriter->dir)) == NULL) {
+ if (!errno)
+ return GIT_ITEROVER;
+
+ giterr_set(GITERR_OS,
+ "Could not read directory '%s'", diriter->path);
+ return -1;
+ }
+ } while (skip_dot && git_path_is_dot_or_dotdot(de->d_name));
+
+ filename = de->d_name;
+ filename_len = strlen(filename);
+
+#ifdef GIT_USE_ICONV
+ if ((error = git_path_iconv(&diriter->ic, &filename, &filename_len)) < 0)
+ return error;
+#endif
+
+ git_buf_truncate(&diriter->path, diriter->parent_len);
+ git_buf_putc(&diriter->path, '/');
+ git_buf_put(&diriter->path, filename, filename_len);
+
+ if (git_buf_oom(&diriter->path))
+ return -1;
+
+ *out = &diriter->path.ptr[diriter->parent_len+1];
+ *out_len = filename_len;
+
+ return error;
+}
+
+int git_path_diriter_fullpath(
+ const char **out,
+ size_t *out_len,
+ git_path_diriter *diriter)
+{
+ assert(out && out_len && diriter);
+
+ *out = diriter->path.ptr;
+ *out_len = diriter->path.size;
+ return 0;
+}
+
+int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter)
+{
+ assert(out && diriter);
+
+ return git_path_lstat(diriter->path.ptr, out);
+}
+
+void git_path_diriter_free(git_path_diriter *diriter)
+{
+ if (diriter == NULL)
+ return;
+
+ closedir(diriter->dir);
+
+#ifdef GIT_USE_ICONV
+ git_path_iconv_clear(&diriter->ic);
+#endif
+
+ git_buf_free(&diriter->path);
+}
+
int git_path_dirload_with_stat(
const char *path,
size_t prefix_len,
diff --git a/src/path.h b/src/path.h
index 440b5420c..3a25d4aed 100644
--- a/src/path.h
+++ b/src/path.h
@@ -273,6 +273,7 @@ extern int git_path_apply_relative(git_buf *target, const char *relpath);
enum {
GIT_PATH_DIR_IGNORE_CASE = (1u << 0),
GIT_PATH_DIR_PRECOMPOSE_UNICODE = (1u << 1),
+ GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT = (1u << 2),
};
/**
@@ -326,6 +327,37 @@ extern int git_path_walk_up(
int (*callback)(void *payload, const char *path),
void *payload);
+typedef struct git_path_diriter git_path_diriter;
+
+struct git_path_diriter
+{
+ git_buf path;
+ size_t parent_len;
+
+ unsigned int flags;
+
+ DIR *dir;
+};
+
+extern int git_path_diriter_init(
+ git_path_diriter *diriter,
+ const char *path,
+ unsigned int flags);
+
+extern int git_path_diriter_next(
+ const char **out,
+ size_t *out_len,
+ git_path_diriter *diriter);
+
+extern int git_path_diriter_fullpath(
+ const char **out,
+ size_t *out_len,
+ git_path_diriter *diriter);
+
+extern int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter);
+
+extern void git_path_diriter_free(git_path_diriter *diriter);
+
/**
* Load all directory entries (except '.' and '..') into a vector.
*
diff --git a/src/posix.h b/src/posix.h
index 22f472c90..8785a4c99 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -122,7 +122,6 @@ extern int git__page_size(size_t *page_size);
#include "strnlen.h"
#ifdef NO_READDIR_R
-# include <dirent.h>
GIT_INLINE(int) p_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
{
GIT_UNUSED(entry);
diff --git a/src/unix/posix.h b/src/unix/posix.h
index e4f3ac67a..8b4f427f7 100644
--- a/src/unix/posix.h
+++ b/src/unix/posix.h
@@ -8,6 +8,7 @@
#define INCLUDE_posix__unix_h__
#include <stdio.h>
+#include <dirent.h>
#include <sys/param.h>
typedef int GIT_SOCKET;