Commit edbfc52c by Edward Thomson

git_path: introduce 'git_path_diriter'

Introduce a new `git_path_diriter` that can iterate directories
efficiently for each platform.
parent 544139f5
...@@ -260,6 +260,20 @@ int git_path_root(const char *path) ...@@ -260,6 +260,20 @@ int git_path_root(const char *path)
return -1; /* Not a real error - signals that path is not rooted */ 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( int git_path_join_unrooted(
git_buf *path_out, const char *path, const char *base, ssize_t *root_at) 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) ...@@ -1181,6 +1195,122 @@ int git_path_with_stat_cmp_icase(const void *a, const void *b)
return strcasecmp(psa->path, psb->path); 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( int git_path_dirload_with_stat(
const char *path, const char *path,
size_t prefix_len, size_t prefix_len,
......
...@@ -273,6 +273,7 @@ extern int git_path_apply_relative(git_buf *target, const char *relpath); ...@@ -273,6 +273,7 @@ extern int git_path_apply_relative(git_buf *target, const char *relpath);
enum { enum {
GIT_PATH_DIR_IGNORE_CASE = (1u << 0), GIT_PATH_DIR_IGNORE_CASE = (1u << 0),
GIT_PATH_DIR_PRECOMPOSE_UNICODE = (1u << 1), 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( ...@@ -326,6 +327,37 @@ extern int git_path_walk_up(
int (*callback)(void *payload, const char *path), int (*callback)(void *payload, const char *path),
void *payload); 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. * Load all directory entries (except '.' and '..') into a vector.
* *
......
...@@ -122,7 +122,6 @@ extern int git__page_size(size_t *page_size); ...@@ -122,7 +122,6 @@ extern int git__page_size(size_t *page_size);
#include "strnlen.h" #include "strnlen.h"
#ifdef NO_READDIR_R #ifdef NO_READDIR_R
# include <dirent.h>
GIT_INLINE(int) p_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) GIT_INLINE(int) p_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
{ {
GIT_UNUSED(entry); GIT_UNUSED(entry);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#define INCLUDE_posix__unix_h__ #define INCLUDE_posix__unix_h__
#include <stdio.h> #include <stdio.h>
#include <dirent.h>
#include <sys/param.h> #include <sys/param.h>
typedef int GIT_SOCKET; typedef int GIT_SOCKET;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment