]>
Commit | Line | Data |
---|---|---|
997579be | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
997579be SS |
3 | * |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with | |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
6 | */ | |
7 | ||
eae0bfdc PP |
8 | #include "findfile.h" |
9 | ||
cceae9a2 | 10 | #include "path_w32.h" |
997579be SS |
11 | #include "utf-conv.h" |
12 | #include "path.h" | |
997579be | 13 | |
c55378fc | 14 | #define REG_MSYSGIT_INSTALL_LOCAL L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1" |
5540d947 | 15 | |
997579be | 16 | #ifndef _WIN64 |
c55378fc | 17 | #define REG_MSYSGIT_INSTALL REG_MSYSGIT_INSTALL_LOCAL |
997579be SS |
18 | #else |
19 | #define REG_MSYSGIT_INSTALL L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1" | |
20 | #endif | |
21 | ||
c2c81615 PK |
22 | typedef struct { |
23 | git_win32_path path; | |
24 | DWORD len; | |
25 | } _findfile_path; | |
997579be | 26 | |
c2c81615 | 27 | static int git_win32__expand_path(_findfile_path *dest, const wchar_t *src) |
5540d947 | 28 | { |
c2c81615 | 29 | dest->len = ExpandEnvironmentStringsW(src, dest->path, ARRAY_SIZE(dest->path)); |
5540d947 | 30 | |
c2c81615 PK |
31 | if (!dest->len || dest->len > ARRAY_SIZE(dest->path)) |
32 | return -1; | |
5540d947 | 33 | |
c2c81615 | 34 | return 0; |
5540d947 RB |
35 | } |
36 | ||
c2c81615 | 37 | static int win32_path_to_8(git_buf *dest, const wchar_t *src) |
997579be | 38 | { |
c2c81615 | 39 | git_win32_utf8_path utf8_path; |
997579be | 40 | |
c2c81615 | 41 | if (git_win32_path_to_utf8(utf8_path, src) < 0) { |
ac3d33df | 42 | git_error_set(GIT_ERROR_OS, "unable to convert path to UTF-8"); |
c2c81615 | 43 | return -1; |
997579be SS |
44 | } |
45 | ||
c2c81615 PK |
46 | /* Convert backslashes to forward slashes */ |
47 | git_path_mkposix(utf8_path); | |
5540d947 | 48 | |
c2c81615 | 49 | return git_buf_sets(dest, utf8_path); |
997579be SS |
50 | } |
51 | ||
c25aa7cd | 52 | static wchar_t *win32_walkpath(wchar_t *path, wchar_t *buf, size_t buflen) |
997579be SS |
53 | { |
54 | wchar_t term, *base = path; | |
55 | ||
c25aa7cd PP |
56 | GIT_ASSERT_ARG_WITH_RETVAL(path, NULL); |
57 | GIT_ASSERT_ARG_WITH_RETVAL(buf, NULL); | |
58 | GIT_ASSERT_ARG_WITH_RETVAL(buflen, NULL); | |
997579be SS |
59 | |
60 | term = (*path == L'"') ? *path++ : L';'; | |
61 | ||
62 | for (buflen--; *path && *path != term && buflen; buflen--) | |
63 | *buf++ = *path++; | |
64 | ||
65 | *buf = L'\0'; /* reserved a byte via initial subtract */ | |
66 | ||
67 | while (*path == term || *path == L';') | |
68 | path++; | |
69 | ||
70 | return (path != base) ? path : NULL; | |
71 | } | |
72 | ||
f84bc388 | 73 | static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe, const wchar_t *subdir) |
997579be | 74 | { |
5540d947 | 75 | wchar_t *env = _wgetenv(L"PATH"), lastch; |
c2c81615 | 76 | _findfile_path root; |
5540d947 | 77 | size_t gitexe_len = wcslen(gitexe); |
997579be | 78 | |
997579be SS |
79 | if (!env) |
80 | return -1; | |
81 | ||
5540d947 RB |
82 | while ((env = win32_walkpath(env, root.path, MAX_PATH-1)) && *root.path) { |
83 | root.len = (DWORD)wcslen(root.path); | |
84 | lastch = root.path[root.len - 1]; | |
85 | ||
86 | /* ensure trailing slash (MAX_PATH-1 to walkpath guarantees space) */ | |
87 | if (lastch != L'/' && lastch != L'\\') { | |
88 | root.path[root.len++] = L'\\'; | |
89 | root.path[root.len] = L'\0'; | |
90 | } | |
997579be | 91 | |
5540d947 RB |
92 | if (root.len + gitexe_len >= MAX_PATH) |
93 | continue; | |
94 | wcscpy(&root.path[root.len], gitexe); | |
997579be | 95 | |
5540d947 | 96 | if (_waccess(root.path, F_OK) == 0 && root.len > 5) { |
f84bc388 L |
97 | /* replace "bin\\" or "cmd\\" with subdir */ |
98 | wcscpy(&root.path[root.len - 4], subdir); | |
997579be | 99 | |
abf37327 | 100 | win32_path_to_8(buf, root.path); |
5540d947 | 101 | return 0; |
997579be SS |
102 | } |
103 | } | |
5540d947 | 104 | |
997579be SS |
105 | return GIT_ENOTFOUND; |
106 | } | |
107 | ||
5540d947 | 108 | static int win32_find_git_in_registry( |
c2c81615 | 109 | git_buf *buf, const HKEY hive, const wchar_t *key, const wchar_t *subdir) |
997579be | 110 | { |
5540d947 | 111 | HKEY hKey; |
c2c81615 | 112 | int error = GIT_ENOTFOUND; |
5540d947 | 113 | |
c25aa7cd | 114 | GIT_ASSERT_ARG(buf); |
997579be | 115 | |
c2c81615 PK |
116 | if (!RegOpenKeyExW(hive, key, 0, KEY_READ, &hKey)) { |
117 | DWORD dwType, cbData; | |
118 | git_win32_path path; | |
5540d947 | 119 | |
c2c81615 PK |
120 | /* Ensure that the buffer is big enough to have the suffix attached |
121 | * after we receive the result. */ | |
122 | cbData = (DWORD)(sizeof(path) - wcslen(subdir) * sizeof(wchar_t)); | |
5540d947 | 123 | |
c2c81615 PK |
124 | /* InstallLocation points to the root of the git directory */ |
125 | if (!RegQueryValueExW(hKey, L"InstallLocation", NULL, &dwType, (LPBYTE)path, &cbData) && | |
7110000d | 126 | dwType == REG_SZ) { |
5540d947 | 127 | |
c2c81615 PK |
128 | /* Append the suffix */ |
129 | wcscat(path, subdir); | |
5540d947 | 130 | |
c2c81615 PK |
131 | /* Convert to UTF-8, with forward slashes, and output the path |
132 | * to the provided buffer */ | |
133 | if (!win32_path_to_8(buf, path)) | |
134 | error = 0; | |
c55378fc | 135 | } |
ec56af08 | 136 | |
5540d947 | 137 | RegCloseKey(hKey); |
ec56af08 SS |
138 | } |
139 | ||
c2c81615 | 140 | return error; |
ec56af08 SS |
141 | } |
142 | ||
5540d947 | 143 | static int win32_find_existing_dirs( |
c4ac556e | 144 | git_buf *out, const wchar_t *tmpl[]) |
5540d947 | 145 | { |
c2c81615 | 146 | _findfile_path path16; |
5540d947 | 147 | git_buf buf = GIT_BUF_INIT; |
5540d947 | 148 | |
41954a49 RB |
149 | git_buf_clear(out); |
150 | ||
151 | for (; *tmpl != NULL; tmpl++) { | |
32460251 | 152 | if (!git_win32__expand_path(&path16, *tmpl) && |
5540d947 RB |
153 | path16.path[0] != L'%' && |
154 | !_waccess(path16.path, F_OK)) | |
155 | { | |
abf37327 | 156 | win32_path_to_8(&buf, path16.path); |
41954a49 RB |
157 | |
158 | if (buf.size) | |
159 | git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); | |
997579be SS |
160 | } |
161 | } | |
997579be | 162 | |
ac3d33df | 163 | git_buf_dispose(&buf); |
41954a49 RB |
164 | |
165 | return (git_buf_oom(out) ? -1 : 0); | |
997579be | 166 | } |
5540d947 | 167 | |
f84bc388 | 168 | int git_win32__find_system_dirs(git_buf *out, const wchar_t *subdir) |
5540d947 | 169 | { |
5540d947 RB |
170 | git_buf buf = GIT_BUF_INIT; |
171 | ||
5540d947 | 172 | /* directories where git.exe & git.cmd are found */ |
f84bc388 | 173 | if (!win32_find_git_in_path(&buf, L"git.exe", subdir) && buf.size) |
41954a49 RB |
174 | git_buf_set(out, buf.ptr, buf.size); |
175 | else | |
176 | git_buf_clear(out); | |
5540d947 | 177 | |
f84bc388 | 178 | if (!win32_find_git_in_path(&buf, L"git.cmd", subdir) && buf.size) |
41954a49 | 179 | git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); |
5540d947 RB |
180 | |
181 | /* directories where git is installed according to registry */ | |
182 | if (!win32_find_git_in_registry( | |
f84bc388 | 183 | &buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL, subdir) && buf.size) |
41954a49 | 184 | git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); |
5540d947 RB |
185 | |
186 | if (!win32_find_git_in_registry( | |
f84bc388 | 187 | &buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL, subdir) && buf.size) |
41954a49 RB |
188 | git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); |
189 | ||
ac3d33df | 190 | git_buf_dispose(&buf); |
5540d947 | 191 | |
41954a49 | 192 | return (git_buf_oom(out) ? -1 : 0); |
5540d947 RB |
193 | } |
194 | ||
32460251 | 195 | int git_win32__find_global_dirs(git_buf *out) |
5540d947 | 196 | { |
5540d947 RB |
197 | static const wchar_t *global_tmpls[4] = { |
198 | L"%HOME%\\", | |
199 | L"%HOMEDRIVE%%HOMEPATH%\\", | |
200 | L"%USERPROFILE%\\", | |
201 | NULL, | |
202 | }; | |
203 | ||
c4ac556e | 204 | return win32_find_existing_dirs(out, global_tmpls); |
5540d947 RB |
205 | } |
206 | ||
32460251 | 207 | int git_win32__find_xdg_dirs(git_buf *out) |
5540d947 | 208 | { |
5540d947 RB |
209 | static const wchar_t *global_tmpls[7] = { |
210 | L"%XDG_CONFIG_HOME%\\git", | |
211 | L"%APPDATA%\\git", | |
212 | L"%LOCALAPPDATA%\\git", | |
213 | L"%HOME%\\.config\\git", | |
214 | L"%HOMEDRIVE%%HOMEPATH%\\.config\\git", | |
215 | L"%USERPROFILE%\\.config\\git", | |
216 | NULL, | |
217 | }; | |
218 | ||
c4ac556e | 219 | return win32_find_existing_dirs(out, global_tmpls); |
5540d947 | 220 | } |
8c7c5fa5 CMN |
221 | |
222 | int git_win32__find_programdata_dirs(git_buf *out) | |
223 | { | |
224 | static const wchar_t *programdata_tmpls[2] = { | |
225 | L"%PROGRAMDATA%\\Git", | |
226 | NULL, | |
227 | }; | |
228 | ||
229 | return win32_find_existing_dirs(out, programdata_tmpls); | |
230 | } |