vendor/libgit2/src/win32/findfile.c in rugged-1.3.2.3 vs vendor/libgit2/src/win32/findfile.c in rugged-1.4.2

- old
+ new

@@ -7,194 +7,250 @@ #include "findfile.h" #include "path_w32.h" #include "utf-conv.h" -#include "path.h" +#include "fs_path.h" -#define REG_MSYSGIT_INSTALL_LOCAL L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1" +#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" -#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 - -typedef struct { - git_win32_path path; - DWORD len; -} _findfile_path; - -static int git_win32__expand_path(_findfile_path *dest, const wchar_t *src) +static int git_win32__expand_path(git_win32_path dest, const wchar_t *src) { - dest->len = ExpandEnvironmentStringsW(src, dest->path, ARRAY_SIZE(dest->path)); + DWORD len = ExpandEnvironmentStringsW(src, dest, GIT_WIN_PATH_UTF16); - if (!dest->len || dest->len > ARRAY_SIZE(dest->path)) + if (!len || len > GIT_WIN_PATH_UTF16) return -1; return 0; } -static int win32_path_to_8(git_buf *dest, const wchar_t *src) +static int win32_path_to_8(git_str *dest, const wchar_t *src) { git_win32_utf8_path utf8_path; if (git_win32_path_to_utf8(utf8_path, src) < 0) { git_error_set(GIT_ERROR_OS, "unable to convert path to UTF-8"); return -1; } /* Convert backslashes to forward slashes */ - git_path_mkposix(utf8_path); + git_fs_path_mkposix(utf8_path); - return git_buf_sets(dest, utf8_path); + return git_str_sets(dest, utf8_path); } -static wchar_t *win32_walkpath(wchar_t *path, wchar_t *buf, size_t buflen) +static git_win32_path mock_registry; +static bool mock_registry_set; + +extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir) { - wchar_t term, *base = path; + if (!mock_sysdir) { + mock_registry[0] = L'\0'; + mock_registry_set = false; + } else { + size_t len = wcslen(mock_sysdir); - GIT_ASSERT_ARG_WITH_RETVAL(path, NULL); - GIT_ASSERT_ARG_WITH_RETVAL(buf, NULL); - GIT_ASSERT_ARG_WITH_RETVAL(buflen, NULL); + if (len > GIT_WIN_PATH_MAX) { + git_error_set(GIT_ERROR_INVALID, "mock path too long"); + return -1; + } - term = (*path == L'"') ? *path++ : L';'; + wcscpy(mock_registry, mock_sysdir); + mock_registry_set = true; + } - for (buflen--; *path && *path != term && buflen; buflen--) - *buf++ = *path++; - - *buf = L'\0'; /* reserved a byte via initial subtract */ - - while (*path == term || *path == L';') - path++; - - return (path != base) ? path : NULL; + return 0; } -static int win32_find_git_in_path(git_buf *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 *env = _wgetenv(L"PATH"), lastch; - _findfile_path root; - size_t gitexe_len = wcslen(gitexe); + HKEY hkey; + DWORD type, size; + int error = GIT_ENOTFOUND; - if (!env) - return -1; + /* + * Registry data may not be NUL terminated, provide room to do + * it ourselves. + */ + size = (DWORD)((sizeof(git_win32_path) - 1) * sizeof(wchar_t)); - while ((env = win32_walkpath(env, root.path, MAX_PATH-1)) && *root.path) { - root.len = (DWORD)wcslen(root.path); - lastch = root.path[root.len - 1]; + if (RegOpenKeyExW(hive, key, 0, KEY_READ, &hkey) != 0) + return GIT_ENOTFOUND; - /* ensure trailing slash (MAX_PATH-1 to walkpath guarantees space) */ - if (lastch != L'/' && lastch != L'\\') { - root.path[root.len++] = L'\\'; - root.path[root.len] = L'\0'; + 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 (root.len + gitexe_len >= MAX_PATH) - continue; - wcscpy(&root.path[root.len], gitexe); + if (out[len - 1] == L'\\') + out[len - 1] = L'\0'; - if (_waccess(root.path, F_OK) == 0 && root.len > 5) { - /* replace "bin\\" or "cmd\\" with subdir */ - wcscpy(&root.path[root.len - 4], subdir); + if (_waccess(out, F_OK) == 0) + error = 0; + } - win32_path_to_8(buf, root.path); - return 0; - } + RegCloseKey(hkey); + return error; +} + +static int find_sysdir_in_registry(git_win32_path out) +{ + if (mock_registry_set) { + if (mock_registry[0] == L'\0') + return GIT_ENOTFOUND; + + wcscpy(out, mock_registry); + return 0; } - return 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_buf *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"); - /* Append the suffix */ - wcscat(path, subdir); + if (out_len && out[out_len - 1] == L'\\') + out_len--; - /* Convert to UTF-8, with forward slashes, and output the path - * to the provided buffer */ - if (!win32_path_to_8(buf, path)) - error = 0; - } + /* + * 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"); - RegCloseKey(hKey); - } + if (!out_len) + return GIT_ENOTFOUND; - return error; + out[out_len] = L'\0'; + return 0; } static int win32_find_existing_dirs( - git_buf *out, const wchar_t *tmpl[]) + git_str* out, + const wchar_t* tmpl[]) { - _findfile_path path16; - git_buf buf = GIT_BUF_INIT; + git_win32_path path16; + git_str buf = GIT_STR_INIT; - git_buf_clear(out); + git_str_clear(out); for (; *tmpl != NULL; tmpl++) { - if (!git_win32__expand_path(&path16, *tmpl) && - path16.path[0] != L'%' && - !_waccess(path16.path, F_OK)) - { - win32_path_to_8(&buf, path16.path); + if (!git_win32__expand_path(path16, *tmpl) && + path16[0] != L'%' && + !_waccess(path16, F_OK)) { + win32_path_to_8(&buf, path16); if (buf.size) - git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); + git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); } } - git_buf_dispose(&buf); + git_str_dispose(&buf); - return (git_buf_oom(out) ? -1 : 0); + return (git_str_oom(out) ? -1 : 0); } -int git_win32__find_system_dirs(git_buf *out, const wchar_t *subdir) +static int append_subdir(git_str *out, git_str *path, const char *subdir) { - git_buf buf = GIT_BUF_INIT; + static const char* architecture_roots[] = { + "", + "mingw64", + "mingw32", + NULL + }; + const char **root; + size_t orig_path_len = path->size; - /* directories where git.exe & git.cmd are found */ - if (!win32_find_git_in_path(&buf, L"git.exe", subdir) && buf.size) - git_buf_set(out, buf.ptr, buf.size); - else - git_buf_clear(out); + 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_path(&buf, L"git.cmd", subdir) && buf.size) - git_buf_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; - /* 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_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); + git_str_truncate(path, orig_path_len); + } - if (!win32_find_git_in_registry( - &buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL, subdir) && buf.size) - git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); + return 0; +} - git_buf_dispose(&buf); +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; - return (git_buf_oom(out) ? -1 : 0); + has_pathdir = (find_sysdir_in_path(pathdir) == 0); + has_regdir = (find_sysdir_in_registry(regdir) == 0); + + if (!has_pathdir && !has_regdir) + return 0; + + /* + * 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_buf *out) +int git_win32__find_global_dirs(git_str *out) { static const wchar_t *global_tmpls[4] = { L"%HOME%\\", L"%HOMEDRIVE%%HOMEPATH%\\", L"%USERPROFILE%\\", @@ -202,11 +258,11 @@ }; return win32_find_existing_dirs(out, global_tmpls); } -int git_win32__find_xdg_dirs(git_buf *out) +int git_win32__find_xdg_dirs(git_str *out) { static const wchar_t *global_tmpls[7] = { L"%XDG_CONFIG_HOME%\\git", L"%APPDATA%\\git", L"%LOCALAPPDATA%\\git", @@ -217,10 +273,10 @@ }; return win32_find_existing_dirs(out, global_tmpls); } -int git_win32__find_programdata_dirs(git_buf *out) +int git_win32__find_programdata_dirs(git_str *out) { static const wchar_t *programdata_tmpls[2] = { L"%PROGRAMDATA%\\Git", NULL, };