summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/iterator.c2
-rw-r--r--src/path.c188
-rw-r--r--src/path.h26
-rw-r--r--src/win32/path_w32.c5
4 files changed, 213 insertions, 8 deletions
diff --git a/src/iterator.c b/src/iterator.c
index 52814bae7..c5c5fd7ce 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -1011,7 +1011,7 @@ static int dirload_with_stat(
const char *end_stat,
git_vector *contents)
{
- git_path_diriter diriter = {0};
+ git_path_diriter diriter = GIT_PATH_DIRITER_INIT;
const char *path;
int (*strncomp)(const char *a, const char *b, size_t sz);
size_t start_len = start_stat ? strlen(start_stat) : 0;
diff --git a/src/path.c b/src/path.c
index ee566985a..6c9852b79 100644
--- a/src/path.c
+++ b/src/path.c
@@ -1078,6 +1078,182 @@ int git_path_direach(
return error;
}
+#if defined(GIT_WIN32) && !defined(__MINGW32__)
+
+/* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7
+ * and better. Prior versions will ignore this.
+ */
+#ifndef FIND_FIRST_EX_LARGE_FETCH
+# define FIND_FIRST_EX_LARGE_FETCH 2
+#endif
+
+int git_path_diriter_init(
+ git_path_diriter *diriter,
+ const char *path,
+ unsigned int flags)
+{
+ git_win32_path path_filter;
+ git_buf hack = {0};
+
+ assert(diriter && path);
+
+ memset(diriter, 0, sizeof(git_path_diriter));
+ diriter->handle = INVALID_HANDLE_VALUE;
+
+ if (git_buf_puts(&diriter->path_utf8, path) < 0)
+ return -1;
+
+ git_path_trim_slashes(&diriter->path_utf8);
+
+ if (diriter->path_utf8.size == 0) {
+ giterr_set(GITERR_FILESYSTEM, "Could not open directory '%s'", path);
+ return -1;
+ }
+
+ if ((diriter->parent_len = git_win32_path_from_utf8(diriter->path, diriter->path_utf8.ptr)) < 0 ||
+ !git_win32__findfirstfile_filter(path_filter, diriter->path_utf8.ptr)) {
+ giterr_set(GITERR_OS, "Could not parse the directory path '%s'", path);
+ return -1;
+ }
+
+ diriter->handle = FindFirstFileExW(
+ path_filter,
+ FindExInfoBasic,
+ &diriter->current,
+ FindExSearchNameMatch,
+ NULL,
+ FIND_FIRST_EX_LARGE_FETCH);
+
+ if (diriter->handle == INVALID_HANDLE_VALUE) {
+ giterr_set(GITERR_OS, "Could not open directory '%s'", path);
+ return -1;
+ }
+
+ diriter->parent_utf8_len = diriter->path_utf8.size;
+ diriter->flags = flags;
+ return 0;
+}
+
+static int diriter_update_utf16(git_path_diriter *diriter)
+{
+ size_t filename_len, path_len;
+
+ filename_len = wcslen(diriter->current.cFileName);
+
+ if (GIT_ADD_SIZET_OVERFLOW(&path_len, diriter->parent_len, filename_len) ||
+ GIT_ADD_SIZET_OVERFLOW(&path_len, path_len, 2))
+ return -1;
+
+ if (path_len > GIT_WIN_PATH_UTF16) {
+ giterr_set(GITERR_FILESYSTEM,
+ "invalid path '%.*ls\\%ls' (path too long)",
+ diriter->parent_len, diriter->path, diriter->current.cFileName);
+ return -1;
+ }
+
+ diriter->path[diriter->parent_len] = L'\\';
+ memcpy(&diriter->path[diriter->parent_len+1],
+ diriter->current.cFileName, filename_len * sizeof(wchar_t));
+ diriter->path[path_len-1] = L'\0';
+
+ return 0;
+}
+
+static int diriter_update_utf8(git_path_diriter *diriter)
+{
+ git_win32_utf8_path filename_utf8;
+ wchar_t *filename_utf16;
+ int filename_utf8_len;
+
+ /* Don't copy the full UTF-16 path into the UTF-8 path, only do the
+ * UTF16 -> UTF8 conversion of the filename portion. This prevents us
+ * from trying to encode the parent path differently, which would be
+ * bad since we do arithmetic based on the already computed parent len.
+ */
+
+ filename_utf16 = &diriter->path[diriter->parent_len + 1];
+
+ if ((filename_utf8_len = git_win32_path_to_utf8(filename_utf8, filename_utf16)) < 0)
+ return filename_utf8_len;
+
+ git_buf_truncate(&diriter->path_utf8, diriter->parent_utf8_len);
+ git_buf_putc(&diriter->path_utf8, '/');
+ git_buf_put(&diriter->path_utf8, filename_utf8, (size_t)filename_utf8_len);
+
+ if (git_buf_oom(&diriter->path_utf8))
+ return -1;
+
+ return 0;
+}
+
+int git_path_diriter_next(git_path_diriter *diriter)
+{
+ bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT);
+
+ do {
+ /* Our first time through, we already have the data from
+ * FindFirstFileW. Use it, otherwise get the next file.
+ */
+ if (!diriter->needs_next)
+ diriter->needs_next = 1;
+ else if (!FindNextFileW(diriter->handle, &diriter->current))
+ return GIT_ITEROVER;
+ } while (skip_dot && git_path_is_dot_or_dotdotW(diriter->current.cFileName));
+
+ if (diriter_update_utf16(diriter) < 0 || diriter_update_utf8(diriter) < 0)
+ return -1;
+
+ return 0;
+}
+
+int git_path_diriter_filename(
+ const char **out,
+ size_t *out_len,
+ git_path_diriter *diriter)
+{
+ assert(out && out_len && diriter);
+
+ assert(diriter->path_utf8.size > diriter->parent_utf8_len);
+
+ *out = &diriter->path_utf8.ptr[diriter->parent_utf8_len+1];
+ *out_len = diriter->path_utf8.size - diriter->parent_utf8_len - 1;
+ return 0;
+}
+
+int git_path_diriter_fullpath(
+ const char **out,
+ size_t *out_len,
+ git_path_diriter *diriter)
+{
+ assert(out && out_len && diriter);
+
+ *out = diriter->path_utf8.ptr;
+ *out_len = diriter->path_utf8.size;
+ return 0;
+}
+
+int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter)
+{
+ assert(out && diriter);
+
+ return git_win32__file_attribute_to_stat(out,
+ (WIN32_FILE_ATTRIBUTE_DATA *)&diriter->current,
+ diriter->path);
+}
+
+void git_path_diriter_free(git_path_diriter *diriter)
+{
+ if (diriter == NULL)
+ return;
+
+ if (diriter->handle != INVALID_HANDLE_VALUE) {
+ FindClose(diriter->handle);
+ diriter->handle = INVALID_HANDLE_VALUE;
+ }
+}
+
+#else
+
int git_path_diriter_init(
git_path_diriter *diriter,
const char *path,
@@ -1090,9 +1266,13 @@ int git_path_diriter_init(
if (git_buf_puts(&diriter->path, path) < 0)
return -1;
- git_path_mkposix(diriter->path.ptr);
git_path_trim_slashes(&diriter->path);
+ if (diriter->path.size == 0) {
+ giterr_set(GITERR_FILESYSTEM, "Could not open directory '%s'", path);
+ return -1;
+ }
+
if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) {
git_buf_free(&diriter->path);
@@ -1159,6 +1339,8 @@ int git_path_diriter_filename(
{
assert(out && out_len && diriter);
+ assert(diriter->path.size > diriter->parent_len);
+
*out = &diriter->path.ptr[diriter->parent_len+1];
*out_len = diriter->path.size - diriter->parent_len - 1;
return 0;
@@ -1200,13 +1382,15 @@ void git_path_diriter_free(git_path_diriter *diriter)
git_buf_free(&diriter->path);
}
+#endif
+
int git_path_dirload(
git_vector *contents,
const char *path,
size_t prefix_len,
unsigned int flags)
{
- git_path_diriter iter = {0};
+ git_path_diriter iter = GIT_PATH_DIRITER_INIT;
const char *name;
size_t name_len;
char *dup;
diff --git a/src/path.h b/src/path.h
index 927d2fc6e..9c2b85a87 100644
--- a/src/path.h
+++ b/src/path.h
@@ -329,6 +329,28 @@ extern int git_path_walk_up(
typedef struct git_path_diriter git_path_diriter;
+#if defined(GIT_WIN32) && !defined(__MINGW32__)
+
+struct git_path_diriter
+{
+ git_win32_path path;
+ size_t parent_len;
+
+ git_buf path_utf8;
+ size_t parent_utf8_len;
+
+ HANDLE handle;
+
+ unsigned int flags;
+
+ WIN32_FIND_DATAW current;
+ unsigned int needs_next;
+};
+
+#define GIT_PATH_DIRITER_INIT { {0}, 0, GIT_BUF_INIT, 0, INVALID_HANDLE_VALUE }
+
+#else
+
struct git_path_diriter
{
git_buf path;
@@ -339,6 +361,10 @@ struct git_path_diriter
DIR *dir;
};
+#define GIT_PATH_DIRITER_INIT { GIT_BUF_INIT }
+
+#endif
+
/**
* Initialize a directory iterator.
*
diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c
index c145379f7..118e8bcc5 100644
--- a/src/win32/path_w32.c
+++ b/src/win32/path_w32.c
@@ -30,11 +30,6 @@
#define path__is_unc(p) \
(((p)[0] == '\\' && (p)[1] == '\\') || ((p)[0] == '/' && (p)[1] == '/'))
-/* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7
- * and better. Prior versions will ignore this.
- */
-#define _FIND_FIRST_EX_LARGE_FETCH 2
-
GIT_INLINE(int) path__cwd(wchar_t *path, int size)
{
int len;