summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2022-01-17 17:16:02 +0000
committerEdward Thomson <ethomson@edwardthomson.com>2022-01-17 22:02:12 -0500
commit475c6eba4f5ad806b68ebf399818a7e9e9ec4a18 (patch)
tree81f894596bdf8f71bced4fa060043653066b7bce
parent925abee95b55ab24b0cf92771c713569f1f128f4 (diff)
downloadlibgit2-ethomson/win32_findfile_fixes.tar.gz
win32: improve impl & tests for system path / g4w interopethomson/win32_findfile_fixes
We look for a Git for Windows installation to use its git config, so that clients built on libgit2 can interoperate with the Git for Windows CLI (and clients that are built on top of _it_). Look for `git` both in the `PATH` and in the registry. Use the _first_ git install in the path, and the first git install in the registry. Look in both the `etc` dir and the architecture-specific `etc` dirs (`mingw64/etc` and `mingw32/etc`) beneath the installation root. Prefer the git in the `PATH` to the git location in the registry so that users can override that. Include more tests for this behavior.
-rw-r--r--src/sysdir.c20
-rw-r--r--src/win32/findfile.c316
-rw-r--r--src/win32/findfile.h6
-rw-r--r--tests/win32/systemdir.c328
-rw-r--r--tests/win32/systempath.c91
5 files changed, 490 insertions, 271 deletions
diff --git a/src/sysdir.c b/src/sysdir.c
index c0e14f551..450cb509b 100644
--- a/src/sysdir.c
+++ b/src/sysdir.c
@@ -31,7 +31,7 @@ static int git_sysdir_guess_programdata_dirs(git_str *out)
static int git_sysdir_guess_system_dirs(git_str *out)
{
#ifdef GIT_WIN32
- return git_win32__find_system_dirs(out, L"etc\\");
+ return git_win32__find_system_dirs(out, "etc");
#else
return git_str_sets(out, "/etc");
#endif
@@ -154,7 +154,7 @@ static int git_sysdir_guess_xdg_dirs(git_str *out)
static int git_sysdir_guess_template_dirs(git_str *out)
{
#ifdef GIT_WIN32
- return git_win32__find_system_dirs(out, L"share\\git-core\\templates");
+ return git_win32__find_system_dirs(out, "share/git-core/templates");
#else
return git_str_sets(out, "/usr/share/git-core/templates");
#endif
@@ -190,22 +190,22 @@ int git_sysdir_global_init(void)
error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf);
if (error)
- return error;
+ return error;
return git_runtime_shutdown_register(git_sysdir_global_shutdown);
}
int git_sysdir_reset(void)
{
- size_t i;
- int error = 0;
+ size_t i;
+ int error = 0;
- for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); ++i) {
- git_str_dispose(&git_sysdir__dirs[i].buf);
- error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf);
- }
+ for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); ++i) {
+ git_str_dispose(&git_sysdir__dirs[i].buf);
+ error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf);
+ }
- return error;
+ return error;
}
static int git_sysdir_check_selector(git_sysdir_t which)
diff --git a/src/win32/findfile.c b/src/win32/findfile.c
index b82347fc6..516391d7f 100644
--- a/src/win32/findfile.c
+++ b/src/win32/findfile.c
@@ -11,13 +11,8 @@
#include "utf-conv.h"
#include "fs_path.h"
-#define REG_MSYSGIT_INSTALL_LOCAL L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
-
-#ifndef _WIN64
-#define REG_MSYSGIT_INSTALL REG_MSYSGIT_INSTALL_LOCAL
-#else
-#define REG_MSYSGIT_INSTALL L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
-#endif
+#define REG_GITFORWINDOWS_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
+#define REG_GITFORWINDOWS_KEY_WOW64 L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
static int git_win32__expand_path(git_win32_path dest, const wchar_t *src)
{
@@ -44,164 +39,130 @@ static int win32_path_to_8(git_str *dest, const wchar_t *src)
return git_str_sets(dest, utf8_path);
}
-static wchar_t *win32_walkpath(wchar_t *path, wchar_t *buf, size_t buflen)
-{
- wchar_t term, *base = path;
-
- GIT_ASSERT_ARG_WITH_RETVAL(path, NULL);
- GIT_ASSERT_ARG_WITH_RETVAL(buf, NULL);
- GIT_ASSERT_ARG_WITH_RETVAL(buflen, NULL);
-
- term = (*path == L'"') ? *path++ : L';';
-
- for (buflen--; *path && *path != term && buflen; buflen--)
- *buf++ = *path++;
-
- *buf = L'\0'; /* reserved a byte via initial subtract */
+static git_win32_path mock_registry;
+static bool mock_registry_set;
- while (*path == term || *path == L';')
- path++;
-
- return (path != base) ? path : NULL;
-}
-
-static int win32_find_git_for_windows_architecture_root(git_win32_path root_path, const wchar_t *subdir)
+extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir)
{
- /* Git for Windows >= 2 comes with a special architecture root (mingw64 and mingw32)
- * under which the "share" folder is located, check which we need (none is also ok) */
-
- static const wchar_t *architecture_roots[4] = {
- L"", // starting with Git 2.24 the etc folder is directly in the root folder
- L"mingw64\\",
- L"mingw32\\",
- NULL,
- };
-
- const wchar_t **roots = architecture_roots;
- size_t root_path_len = wcslen(root_path);
-
- for (; *roots != NULL; ++roots) {
- git_win32_path tmp_root;
- DWORD subdir_len;
- if (wcscpy(tmp_root, root_path) &&
- root_path_len + wcslen(*roots) <= MAX_PATH &&
- wcscat(tmp_root, *roots) &&
- !_waccess(tmp_root, F_OK)) {
- wcscpy(root_path, tmp_root);
- root_path_len += (DWORD)wcslen(*roots);
-
- subdir_len = (DWORD)wcslen(subdir);
- if (root_path_len + subdir_len >= MAX_PATH)
- break;
-
- // append subdir and check whether it exists for the Git installation
- wcscat(tmp_root, subdir);
- if (!_waccess(tmp_root, F_OK)) {
- wcscpy(root_path, tmp_root);
- root_path_len += subdir_len;
- break;
- }
+ if (!mock_sysdir) {
+ mock_registry[0] = L'\0';
+ mock_registry_set = false;
+ } else {
+ size_t len = wcslen(mock_sysdir);
+
+ if (len > GIT_WIN_PATH_MAX) {
+ git_error_set(GIT_ERROR_INVALID, "mock path too long");
+ return -1;
}
+
+ wcscpy(mock_registry, mock_sysdir);
+ mock_registry_set = true;
}
return 0;
}
-static int win32_find_git_in_path(git_str *buf, const wchar_t *gitexe, const wchar_t *subdir)
+static int lookup_registry_key(
+ git_win32_path out,
+ const HKEY hive,
+ const wchar_t* key,
+ const wchar_t *value)
{
- wchar_t *path, *env, lastch;
- git_win32_path root;
- size_t gitexe_len = wcslen(gitexe);
- DWORD len;
- bool found = false;
-
- len = GetEnvironmentVariableW(L"PATH", NULL, 0);
-
- if (len < 0)
- return -1;
-
- path = git__malloc(len * sizeof(wchar_t));
- GIT_ERROR_CHECK_ALLOC(path);
+ HKEY hkey;
+ DWORD type, size;
+ int error = GIT_ENOTFOUND;
- len = GetEnvironmentVariableW(L"PATH", path, len);
+ /*
+ * Registry data may not be NUL terminated, provide room to do
+ * it ourselves.
+ */
+ size = (DWORD)((sizeof(git_win32_path) - 1) * sizeof(wchar_t));
+
+ if (RegOpenKeyExW(hive, key, 0, KEY_READ, &hkey) != 0)
+ return GIT_ENOTFOUND;
+
+ if (RegQueryValueExW(hkey, value, NULL, &type, (LPBYTE)out, &size) == 0 &&
+ type == REG_SZ &&
+ size > 0 &&
+ size < sizeof(git_win32_path)) {
+ size_t wsize = size / sizeof(wchar_t);
+ size_t len = wsize - 1;
+
+ if (out[wsize - 1] != L'\0') {
+ len = wsize;
+ out[wsize] = L'\0';
+ }
- if (len < 0)
- return -1;
+ if (out[len - 1] == L'\\')
+ out[len - 1] = L'\0';
- env = path;
+ if (_waccess(out, F_OK) == 0)
+ error = 0;
+ }
- while ((env = win32_walkpath(env, root, MAX_PATH-1)) && *root) {
- size_t root_len = wcslen(root);
- lastch = root[root_len - 1];
+ RegCloseKey(hkey);
+ return error;
+}
- /* ensure trailing slash (MAX_PATH-1 to walkpath guarantees space) */
- if (lastch != L'/' && lastch != L'\\') {
- root[root_len++] = L'\\';
- root[root_len] = L'\0';
- }
+static int find_sysdir_in_registry(git_win32_path out)
+{
+ if (mock_registry_set) {
+ if (mock_registry[0] == L'\0')
+ return GIT_ENOTFOUND;
- if (root_len + gitexe_len >= MAX_PATH)
- continue;
-
- if (!_waccess(root, F_OK)) {
- /* check whether we found a Git for Windows installation and do some path adjustments OR just append subdir */
- if ((root_len > 5 && wcscmp(root - 4, L"cmd\\")) || wcscmp(root - 4, L"bin\\")) {
- /* strip "bin" or "cmd" and try to find architecture root for appending subdir */
- root_len -= 4;
- root[root_len] = L'\0';
- if (win32_find_git_for_windows_architecture_root(root, subdir))
- continue;
- } else {
- if (root_len + wcslen(subdir) >= MAX_PATH)
- continue;
- wcscat(root, subdir);
- }
-
- win32_path_to_8(buf, root);
- found = true;
- break;
- }
+ wcscpy(out, mock_registry);
+ return 0;
}
- git__free(path);
- return found ? 0 : GIT_ENOTFOUND;
+ if (lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
+ lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0 ||
+ lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
+ lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0)
+ return 0;
+
+ return GIT_ENOTFOUND;
}
-static int win32_find_git_in_registry(
- git_str *buf, const HKEY hive, const wchar_t *key, const wchar_t *subdir)
+static int find_sysdir_in_path(git_win32_path out)
{
- HKEY hKey;
- int error = GIT_ENOTFOUND;
+ size_t out_len;
- GIT_ASSERT_ARG(buf);
+ if (git_win32_path_find_executable(out, L"git.exe") < 0 &&
+ git_win32_path_find_executable(out, L"git.cmd") < 0)
+ return GIT_ENOTFOUND;
- if (!RegOpenKeyExW(hive, key, 0, KEY_READ, &hKey)) {
- DWORD dwType, cbData;
- git_win32_path path;
+ out_len = wcslen(out);
- /* Ensure that the buffer is big enough to have the suffix attached
- * after we receive the result. */
- cbData = (DWORD)(sizeof(path) - wcslen(subdir) * sizeof(wchar_t));
+ /* Trim the file name */
+ if (out_len <= CONST_STRLEN(L"git.exe"))
+ return GIT_ENOTFOUND;
- /* InstallLocation points to the root of the git directory */
- if (!RegQueryValueExW(hKey, L"InstallLocation", NULL, &dwType, (LPBYTE)path, &cbData) &&
- dwType == REG_SZ) {
+ out_len -= CONST_STRLEN(L"git.exe");
- /* Convert to UTF-8, with forward slashes, and output the path
- * to the provided buffer */
- if (!win32_find_git_for_windows_architecture_root(path, subdir) &&
- !win32_path_to_8(buf, path))
- error = 0;
- }
+ if (out_len && out[out_len - 1] == L'\\')
+ out_len--;
- RegCloseKey(hKey);
- }
+ /*
+ * Git for Windows usually places the command in a 'bin' or
+ * 'cmd' directory, trim that.
+ */
+ if (out_len >= CONST_STRLEN(L"\\bin") &&
+ wcsncmp(&out[out_len - CONST_STRLEN(L"\\bin")], L"\\bin", CONST_STRLEN(L"\\bin")) == 0)
+ out_len -= CONST_STRLEN(L"\\bin");
+ else if (out_len >= CONST_STRLEN(L"\\cmd") &&
+ wcsncmp(&out[out_len - CONST_STRLEN(L"\\cmd")], L"\\cmd", CONST_STRLEN(L"\\cmd")) == 0)
+ out_len -= CONST_STRLEN(L"\\cmd");
- return error;
+ if (!out_len)
+ return GIT_ENOTFOUND;
+
+ out[out_len] = L'\0';
+ return 0;
}
static int win32_find_existing_dirs(
- git_str *out, const wchar_t *tmpl[])
+ git_str* out,
+ const wchar_t* tmpl[])
{
git_win32_path path16;
git_str buf = GIT_STR_INIT;
@@ -210,9 +171,8 @@ static int win32_find_existing_dirs(
for (; *tmpl != NULL; tmpl++) {
if (!git_win32__expand_path(path16, *tmpl) &&
- path16[0] != L'%' &&
- !_waccess(path16, F_OK))
- {
+ path16[0] != L'%' &&
+ !_waccess(path16, F_OK)) {
win32_path_to_8(&buf, path16);
if (buf.size)
@@ -225,47 +185,67 @@ static int win32_find_existing_dirs(
return (git_str_oom(out) ? -1 : 0);
}
-int git_win32__find_system_dir_in_path(git_str *out, const wchar_t *subdir)
-{
- /* directories where git.exe & git.cmd are found */
- if (win32_find_git_in_path(out, L"git.exe", subdir) == 0)
- return 0;
-
- return win32_find_git_in_path(out, L"git.cmd", subdir);
-}
-
-static int git_win32__find_system_dir_in_registry(git_str *out, const wchar_t *subdir)
+static int append_subdir(git_str *out, git_str *path, const char *subdir)
{
- git_str buf = GIT_STR_INIT;
-
- /* directories where git is installed according to registry */
- if (!win32_find_git_in_registry(
- &buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL, subdir) && buf.size)
- git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
+ static const char* architecture_roots[] = {
+ "",
+ "mingw64",
+ "mingw32",
+ NULL
+ };
+ const char **root;
+ size_t orig_path_len = path->size;
-#ifdef GIT_ARCH_64
- if (!win32_find_git_in_registry(
- &buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL_LOCAL, subdir) && buf.size)
- git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
-#endif
+ for (root = architecture_roots; *root; root++) {
+ if ((*root[0] && git_str_joinpath(path, path->ptr, *root) < 0) ||
+ git_str_joinpath(path, path->ptr, subdir) < 0)
+ return -1;
- if (!win32_find_git_in_registry(
- &buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL, subdir) && buf.size)
- git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
+ if (git_fs_path_exists(path->ptr) &&
+ git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, path->ptr) < 0)
+ return -1;
- git_str_dispose(&buf);
+ git_str_truncate(path, orig_path_len);
+ }
- return (git_str_oom(out) ? -1 : 0);
+ return 0;
}
-int git_win32__find_system_dirs(git_str *out, const wchar_t *subdir)
+int git_win32__find_system_dirs(git_str *out, const char *subdir)
{
+ git_win32_path pathdir, regdir;
+ git_str path8 = GIT_STR_INIT;
+ bool has_pathdir, has_regdir;
int error;
- if ((error = git_win32__find_system_dir_in_path(out, subdir)) == 0)
- error = git_win32__find_system_dir_in_registry(out, subdir);
+ has_pathdir = (find_sysdir_in_path(pathdir) == 0);
+ has_regdir = (find_sysdir_in_registry(regdir) == 0);
- return error;
+ if (!has_pathdir && !has_regdir)
+ return GIT_ENOTFOUND;
+
+ /*
+ * Usually the git in the path is the same git in the registry,
+ * in this case there's no need to duplicate the paths.
+ */
+ if (has_pathdir && has_regdir && wcscmp(pathdir, regdir) == 0)
+ has_regdir = false;
+
+ if (has_pathdir) {
+ if ((error = win32_path_to_8(&path8, pathdir)) < 0 ||
+ (error = append_subdir(out, &path8, subdir)) < 0)
+ goto done;
+ }
+
+ if (has_regdir) {
+ if ((error = win32_path_to_8(&path8, regdir)) < 0 ||
+ (error = append_subdir(out, &path8, subdir)) < 0)
+ goto done;
+ }
+
+done:
+ git_str_dispose(&path8);
+ return error;
}
int git_win32__find_global_dirs(git_str *out)
diff --git a/src/win32/findfile.h b/src/win32/findfile.h
index 7f7691ce1..61fb7dbad 100644
--- a/src/win32/findfile.h
+++ b/src/win32/findfile.h
@@ -10,8 +10,10 @@
#include "common.h"
-extern int git_win32__find_system_dir_in_path(git_str* out, const wchar_t* subdir);
-extern int git_win32__find_system_dirs(git_str *out, const wchar_t *subpath);
+/** Sets the mock registry root for Git for Windows for testing. */
+extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir);
+
+extern int git_win32__find_system_dirs(git_str *out, const char *subpath);
extern int git_win32__find_global_dirs(git_str *out);
extern int git_win32__find_xdg_dirs(git_str *out);
extern int git_win32__find_programdata_dirs(git_str *out);
diff --git a/tests/win32/systemdir.c b/tests/win32/systemdir.c
new file mode 100644
index 000000000..46fa06a50
--- /dev/null
+++ b/tests/win32/systemdir.c
@@ -0,0 +1,328 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "sysdir.h"
+#include "win32/findfile.h"
+
+#ifdef GIT_WIN32
+static char *path_save;
+static git_str gfw_path_root = GIT_STR_INIT;
+static git_str gfw_registry_root = GIT_STR_INIT;
+#endif
+
+void test_win32_systemdir__initialize(void)
+{
+#ifdef GIT_WIN32
+ git_str path_env = GIT_STR_INIT;
+
+ path_save = cl_getenv("PATH");
+ git_win32__set_registry_system_dir(L"");
+
+ cl_git_pass(git_str_puts(&path_env, "C:\\GitTempTest\\Foo;\"c:\\program files\\doesnotexisttesttemp\";C:\\fakefakedoesnotexist"));
+ cl_setenv("PATH", path_env.ptr);
+
+ cl_git_pass(git_str_puts(&gfw_path_root, clar_sandbox_path()));
+ cl_git_pass(git_str_puts(&gfw_path_root, "/fake_gfw_path_install"));
+
+ cl_git_pass(git_str_puts(&gfw_registry_root, clar_sandbox_path()));
+ cl_git_pass(git_str_puts(&gfw_registry_root, "/fake_gfw_registry_install"));
+
+ git_str_dispose(&path_env);
+#endif
+}
+
+void test_win32_systemdir__cleanup(void)
+{
+#ifdef GIT_WIN32
+ cl_fixture_cleanup("fake_gfw_path_install");
+ cl_fixture_cleanup("fake_gfw_registry_install");
+ git_str_dispose(&gfw_path_root);
+ git_str_dispose(&gfw_registry_root);
+
+ cl_setenv("PATH", path_save);
+ git__free(path_save);
+ path_save = NULL;
+
+ git_win32__set_registry_system_dir(NULL);
+ cl_sandbox_set_search_path_defaults();
+#endif
+}
+
+#ifdef GIT_WIN32
+static void fix_path(git_str *s)
+{
+ char *c;
+
+ for (c = s->ptr; *c; c++) {
+ if (*c == '/')
+ *c = '\\';
+ }
+}
+
+static void populate_fake_gfw(
+ git_str *expected_etc_dir,
+ const char *root,
+ const char *token,
+ bool create_gitconfig,
+ bool create_mingw64_gitconfig,
+ bool add_to_path,
+ bool add_to_registry)
+{
+ git_str bin_path = GIT_STR_INIT, exe_path = GIT_STR_INIT,
+ etc_path = GIT_STR_INIT, mingw64_path = GIT_STR_INIT,
+ config_path = GIT_STR_INIT, path_env = GIT_STR_INIT,
+ config_data = GIT_STR_INIT;
+
+ cl_git_pass(git_str_puts(&bin_path, root));
+ cl_git_pass(git_str_puts(&bin_path, "/cmd"));
+ cl_git_pass(git_futils_mkdir_r(bin_path.ptr, 0755));
+
+ cl_git_pass(git_str_puts(&exe_path, bin_path.ptr));
+ cl_git_pass(git_str_puts(&exe_path, "/git.cmd"));
+ cl_git_mkfile(exe_path.ptr, "This is a fake executable.");
+
+ cl_git_pass(git_str_puts(&etc_path, root));
+ cl_git_pass(git_str_puts(&etc_path, "/etc"));
+ cl_git_pass(git_futils_mkdir_r(etc_path.ptr, 0755));
+
+ cl_git_pass(git_str_puts(&mingw64_path, root));
+ cl_git_pass(git_str_puts(&mingw64_path, "/mingw64/etc"));
+ cl_git_pass(git_futils_mkdir_r(mingw64_path.ptr, 0755));
+
+ if (create_gitconfig) {
+ git_str_clear(&config_data);
+ git_str_printf(&config_data, "[gfw]\n\ttest = etc %s\n", token);
+
+ cl_git_pass(git_str_puts(&config_path, etc_path.ptr));
+ cl_git_pass(git_str_puts(&config_path, "/gitconfig"));
+ cl_git_mkfile(config_path.ptr, config_data.ptr);
+ }
+
+ if (create_mingw64_gitconfig) {
+ git_str_clear(&config_data);
+ git_str_printf(&config_data, "[gfw]\n\ttest = mingw64 %s\n", token);
+
+ git_str_clear(&config_path);
+ cl_git_pass(git_str_puts(&config_path, mingw64_path.ptr));
+ cl_git_pass(git_str_puts(&config_path, "/gitconfig"));
+ cl_git_mkfile(config_path.ptr, config_data.ptr);
+ }
+
+ if (add_to_path) {
+ fix_path(&bin_path);
+ cl_git_pass(git_str_puts(&path_env, "C:\\GitTempTest\\Foo;\"c:\\program files\\doesnotexisttesttemp\";"));
+ cl_git_pass(git_str_puts(&path_env, bin_path.ptr));
+ cl_git_pass(git_str_puts(&path_env, ";C:\\fakefakedoesnotexist"));
+ cl_setenv("PATH", path_env.ptr);
+ }
+
+ if (add_to_registry) {
+ git_win32_path registry_path;
+ size_t offset = 0;
+
+ cl_assert(git_win32_path_from_utf8(registry_path, root) >= 0);
+ if (wcsncmp(registry_path, L"\\\\?\\", CONST_STRLEN("\\\\?\\")) == 0)
+ offset = CONST_STRLEN("\\\\?\\");
+ git_win32__set_registry_system_dir(registry_path + offset);
+ }
+
+ cl_git_pass(git_str_join(expected_etc_dir, GIT_PATH_LIST_SEPARATOR, expected_etc_dir->ptr, etc_path.ptr));
+ cl_git_pass(git_str_join(expected_etc_dir, GIT_PATH_LIST_SEPARATOR, expected_etc_dir->ptr, mingw64_path.ptr));
+
+ git_str_dispose(&bin_path);
+ git_str_dispose(&exe_path);
+ git_str_dispose(&etc_path);
+ git_str_dispose(&mingw64_path);
+ git_str_dispose(&config_path);
+ git_str_dispose(&path_env);
+ git_str_dispose(&config_data);
+}
+
+static void populate_fake_ecosystem(
+ git_str *expected_etc_dir,
+ bool create_gitconfig,
+ bool create_mingw64_gitconfig,
+ bool path,
+ bool registry)
+{
+ if (path)
+ populate_fake_gfw(expected_etc_dir, gfw_path_root.ptr, "path", create_gitconfig, create_mingw64_gitconfig, true, false);
+
+ if (registry)
+ populate_fake_gfw(expected_etc_dir, gfw_registry_root.ptr, "registry", create_gitconfig, create_mingw64_gitconfig, false, true);
+}
+#endif
+
+void test_win32_systemdir__finds_etc_in_path(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
+ git_config *cfg;
+ git_buf value = GIT_BUF_INIT;
+
+ populate_fake_ecosystem(&expected, true, false, true, false);
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, expected.ptr);
+
+ git_sysdir_reset();
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
+ cl_assert_equal_s("etc path", value.ptr);
+
+ git_buf_dispose(&value);
+ git_str_dispose(&expected);
+ git_str_dispose(&out);
+ git_config_free(cfg);
+#endif
+}
+
+void test_win32_systemdir__finds_mingw64_etc_in_path(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
+ git_config* cfg;
+ git_buf value = GIT_BUF_INIT;
+
+ populate_fake_ecosystem(&expected, false, true, true, false);
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, expected.ptr);
+
+ git_sysdir_reset();
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
+ cl_assert_equal_s("mingw64 path", value.ptr);
+
+ git_buf_dispose(&value);
+ git_str_dispose(&expected);
+ git_str_dispose(&out);
+ git_config_free(cfg);
+#endif
+}
+
+void test_win32_systemdir__prefers_etc_to_mingw64_in_path(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
+ git_config* cfg;
+ git_buf value = GIT_BUF_INIT;
+
+ populate_fake_ecosystem(&expected, true, true, true, false);
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, expected.ptr);
+
+ git_sysdir_reset();
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
+ cl_assert_equal_s("etc path", value.ptr);
+
+ git_buf_dispose(&value);
+ git_str_dispose(&expected);
+ git_str_dispose(&out);
+ git_config_free(cfg);
+#endif
+}
+
+void test_win32_systemdir__finds_etc_in_registry(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
+ git_config* cfg;
+ git_buf value = GIT_BUF_INIT;
+
+ populate_fake_ecosystem(&expected, true, false, false, true);
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, expected.ptr);
+
+ git_sysdir_reset();
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
+ cl_assert_equal_s("etc registry", value.ptr);
+
+ git_buf_dispose(&value);
+ git_str_dispose(&expected);
+ git_str_dispose(&out);
+ git_config_free(cfg);
+#endif
+}
+
+void test_win32_systemdir__finds_mingw64_etc_in_registry(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
+ git_config* cfg;
+ git_buf value = GIT_BUF_INIT;
+
+ populate_fake_ecosystem(&expected, false, true, false, true);
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, expected.ptr);
+
+ git_sysdir_reset();
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
+ cl_assert_equal_s("mingw64 registry", value.ptr);
+
+ git_buf_dispose(&value);
+ git_str_dispose(&expected);
+ git_str_dispose(&out);
+ git_config_free(cfg);
+#endif
+}
+
+void test_win32_systemdir__prefers_etc_to_mingw64_in_registry(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
+ git_config* cfg;
+ git_buf value = GIT_BUF_INIT;
+
+ populate_fake_ecosystem(&expected, true, true, false, true);
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, expected.ptr);
+
+ git_sysdir_reset();
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
+ cl_assert_equal_s("etc registry", value.ptr);
+
+ git_buf_dispose(&value);
+ git_str_dispose(&expected);
+ git_str_dispose(&out);
+ git_config_free(cfg);
+#endif
+}
+
+void test_win32_systemdir__prefers_path_to_registry(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
+ git_config* cfg;
+ git_buf value = GIT_BUF_INIT;
+
+ populate_fake_ecosystem(&expected, true, true, true, true);
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, expected.ptr);
+
+ git_sysdir_reset();
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
+ cl_assert_equal_s("etc path", value.ptr);
+
+ git_buf_dispose(&value);
+ git_str_dispose(&expected);
+ git_str_dispose(&out);
+ git_config_free(cfg);
+#endif
+}
diff --git a/tests/win32/systempath.c b/tests/win32/systempath.c
deleted file mode 100644
index 0b77f7703..000000000
--- a/tests/win32/systempath.c
+++ /dev/null
@@ -1,91 +0,0 @@
-#include "clar_libgit2.h"
-#include "futils.h"
-#include "sysdir.h"
-#include "win32/findfile.h"
-
-static char *path_save;
-static git_str gfw_root = GIT_STR_INIT;
-
-void test_win32_systempath__initialize(void)
-{
- path_save = cl_getenv("PATH");
-
- cl_git_pass(git_str_puts(&gfw_root, clar_sandbox_path()));
- cl_git_pass(git_str_puts(&gfw_root, "/fake_gfw_install"));
-}
-
-void test_win32_systempath__cleanup(void)
-{
- cl_fixture_cleanup("fake_gfw_install");
- git_str_dispose(&gfw_root);
-
- cl_setenv("PATH", path_save);
- git__free(path_save);
- path_save = NULL;
-
- git_sysdir_reset();
-}
-
-static void fix_path(git_str *s)
-{
- char *c;
-
- for (c = s->ptr; *c; c++) {
- if (*c == '/')
- *c = '\\';
- }
-}
-
-void test_win32_systempath__etc_gitconfig(void)
-{
- git_str bin_path = GIT_STR_INIT, exe_path = GIT_STR_INIT,
- etc_path = GIT_STR_INIT, config_path = GIT_STR_INIT,
- path_env = GIT_STR_INIT, out = GIT_STR_INIT;
- git_config *cfg;
- int value;
-
- cl_git_pass(git_str_puts(&bin_path, gfw_root.ptr));
- cl_git_pass(git_str_puts(&bin_path, "/cmd"));
- cl_git_pass(git_futils_mkdir_r(bin_path.ptr, 0755));
-
- cl_git_pass(git_str_puts(&exe_path, bin_path.ptr));
- cl_git_pass(git_str_puts(&exe_path, "/git.cmd"));
- cl_git_mkfile(exe_path.ptr, "This is a fake executable.");
-
- cl_git_pass(git_str_puts(&etc_path, gfw_root.ptr));
- cl_git_pass(git_str_puts(&etc_path, "/etc"));
- cl_git_pass(git_futils_mkdir_r(etc_path.ptr, 0755));
-
- git_str_clear(&etc_path);
-
- cl_git_pass(git_str_puts(&etc_path, gfw_root.ptr));
- cl_git_pass(git_str_puts(&etc_path, "/etc"));
- cl_git_pass(git_futils_mkdir_r(etc_path.ptr, 0755));
-
- cl_git_pass(git_str_puts(&config_path, etc_path.ptr));
- cl_git_pass(git_str_puts(&config_path, "/gitconfig"));
- cl_git_mkfile(config_path.ptr, "[gfw]\n\ttest = 1337\n");
-
- fix_path(&bin_path);
-
- cl_git_pass(git_str_puts(&path_env, "C:\\GitTempTest\\Foo;\"c:\\program files\\doesnotexisttesttemp\";"));
- cl_git_pass(git_str_puts(&path_env, bin_path.ptr));
- cl_git_pass(git_str_puts(&path_env, ";C:\\fakefakedoesnotexist"));
- cl_setenv("PATH", path_env.ptr);
-
- cl_git_pass(git_win32__find_system_dir_in_path(&out, L"etc"));
- cl_assert_equal_s(out.ptr, etc_path.ptr);
-
- git_sysdir_reset();
-
- cl_git_pass(git_config_open_default(&cfg));
- cl_git_pass(git_config_get_int32(&value, cfg, "gfw.test"));
- cl_assert_equal_i(1337, value);
-
- git_str_dispose(&exe_path);
- git_str_dispose(&etc_path);
- git_str_dispose(&config_path);
- git_str_dispose(&path_env);
- git_str_dispose(&out);
- git_config_free(cfg);
-}