]> git.proxmox.com Git - libgit2.git/blame - src/win32/findfile.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / src / win32 / findfile.c
CommitLineData
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 11#include "utf-conv.h"
e579e0f7 12#include "fs_path.h"
997579be 13
e579e0f7
MB
14#define REG_GITFORWINDOWS_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
15#define REG_GITFORWINDOWS_KEY_WOW64 L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
5540d947 16
e579e0f7 17static int git_win32__expand_path(git_win32_path dest, const wchar_t *src)
5540d947 18{
e579e0f7 19 DWORD len = ExpandEnvironmentStringsW(src, dest, GIT_WIN_PATH_UTF16);
5540d947 20
e579e0f7 21 if (!len || len > GIT_WIN_PATH_UTF16)
c2c81615 22 return -1;
5540d947 23
c2c81615 24 return 0;
5540d947
RB
25}
26
e579e0f7 27static int win32_path_to_8(git_str *dest, const wchar_t *src)
997579be 28{
c2c81615 29 git_win32_utf8_path utf8_path;
997579be 30
c2c81615 31 if (git_win32_path_to_utf8(utf8_path, src) < 0) {
ac3d33df 32 git_error_set(GIT_ERROR_OS, "unable to convert path to UTF-8");
c2c81615 33 return -1;
997579be
SS
34 }
35
c2c81615 36 /* Convert backslashes to forward slashes */
e579e0f7 37 git_fs_path_mkposix(utf8_path);
5540d947 38
e579e0f7 39 return git_str_sets(dest, utf8_path);
997579be
SS
40}
41
e579e0f7
MB
42static git_win32_path mock_registry;
43static bool mock_registry_set;
997579be 44
e579e0f7
MB
45extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir)
46{
47 if (!mock_sysdir) {
48 mock_registry[0] = L'\0';
49 mock_registry_set = false;
50 } else {
51 size_t len = wcslen(mock_sysdir);
52
53 if (len > GIT_WIN_PATH_MAX) {
54 git_error_set(GIT_ERROR_INVALID, "mock path too long");
55 return -1;
56 }
997579be 57
e579e0f7
MB
58 wcscpy(mock_registry, mock_sysdir);
59 mock_registry_set = true;
60 }
997579be 61
e579e0f7 62 return 0;
997579be
SS
63}
64
e579e0f7
MB
65static int lookup_registry_key(
66 git_win32_path out,
67 const HKEY hive,
68 const wchar_t* key,
69 const wchar_t *value)
997579be 70{
e579e0f7
MB
71 HKEY hkey;
72 DWORD type, size;
73 int error = GIT_ENOTFOUND;
997579be 74
e579e0f7
MB
75 /*
76 * Registry data may not be NUL terminated, provide room to do
77 * it ourselves.
78 */
79 size = (DWORD)((sizeof(git_win32_path) - 1) * sizeof(wchar_t));
80
81 if (RegOpenKeyExW(hive, key, 0, KEY_READ, &hkey) != 0)
82 return GIT_ENOTFOUND;
83
84 if (RegQueryValueExW(hkey, value, NULL, &type, (LPBYTE)out, &size) == 0 &&
85 type == REG_SZ &&
86 size > 0 &&
87 size < sizeof(git_win32_path)) {
88 size_t wsize = size / sizeof(wchar_t);
89 size_t len = wsize - 1;
90
91 if (out[wsize - 1] != L'\0') {
92 len = wsize;
93 out[wsize] = L'\0';
94 }
997579be 95
e579e0f7
MB
96 if (out[len - 1] == L'\\')
97 out[len - 1] = L'\0';
5540d947 98
e579e0f7
MB
99 if (_waccess(out, F_OK) == 0)
100 error = 0;
101 }
997579be 102
e579e0f7
MB
103 RegCloseKey(hkey);
104 return error;
105}
997579be 106
e579e0f7
MB
107static int find_sysdir_in_registry(git_win32_path out)
108{
109 if (mock_registry_set) {
110 if (mock_registry[0] == L'\0')
111 return GIT_ENOTFOUND;
997579be 112
e579e0f7
MB
113 wcscpy(out, mock_registry);
114 return 0;
997579be 115 }
5540d947 116
e579e0f7
MB
117 if (lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
118 lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0 ||
119 lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
120 lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0)
121 return 0;
122
123 return GIT_ENOTFOUND;
997579be
SS
124}
125
e579e0f7 126static int find_sysdir_in_path(git_win32_path out)
997579be 127{
e579e0f7 128 size_t out_len;
5540d947 129
e579e0f7
MB
130 if (git_win32_path_find_executable(out, L"git.exe") < 0 &&
131 git_win32_path_find_executable(out, L"git.cmd") < 0)
132 return GIT_ENOTFOUND;
997579be 133
e579e0f7 134 out_len = wcslen(out);
5540d947 135
e579e0f7
MB
136 /* Trim the file name */
137 if (out_len <= CONST_STRLEN(L"git.exe"))
138 return GIT_ENOTFOUND;
5540d947 139
e579e0f7 140 out_len -= CONST_STRLEN(L"git.exe");
5540d947 141
e579e0f7
MB
142 if (out_len && out[out_len - 1] == L'\\')
143 out_len--;
5540d947 144
e579e0f7
MB
145 /*
146 * Git for Windows usually places the command in a 'bin' or
147 * 'cmd' directory, trim that.
148 */
149 if (out_len >= CONST_STRLEN(L"\\bin") &&
150 wcsncmp(&out[out_len - CONST_STRLEN(L"\\bin")], L"\\bin", CONST_STRLEN(L"\\bin")) == 0)
151 out_len -= CONST_STRLEN(L"\\bin");
152 else if (out_len >= CONST_STRLEN(L"\\cmd") &&
153 wcsncmp(&out[out_len - CONST_STRLEN(L"\\cmd")], L"\\cmd", CONST_STRLEN(L"\\cmd")) == 0)
154 out_len -= CONST_STRLEN(L"\\cmd");
ec56af08 155
e579e0f7
MB
156 if (!out_len)
157 return GIT_ENOTFOUND;
ec56af08 158
e579e0f7
MB
159 out[out_len] = L'\0';
160 return 0;
ec56af08
SS
161}
162
5540d947 163static int win32_find_existing_dirs(
e579e0f7
MB
164 git_str* out,
165 const wchar_t* tmpl[])
5540d947 166{
e579e0f7
MB
167 git_win32_path path16;
168 git_str buf = GIT_STR_INIT;
5540d947 169
e579e0f7 170 git_str_clear(out);
41954a49
RB
171
172 for (; *tmpl != NULL; tmpl++) {
e579e0f7
MB
173 if (!git_win32__expand_path(path16, *tmpl) &&
174 path16[0] != L'%' &&
175 !_waccess(path16, F_OK)) {
176 win32_path_to_8(&buf, path16);
41954a49
RB
177
178 if (buf.size)
e579e0f7 179 git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
997579be
SS
180 }
181 }
997579be 182
e579e0f7 183 git_str_dispose(&buf);
41954a49 184
e579e0f7 185 return (git_str_oom(out) ? -1 : 0);
997579be 186}
5540d947 187
e579e0f7 188static int append_subdir(git_str *out, git_str *path, const char *subdir)
5540d947 189{
e579e0f7
MB
190 static const char* architecture_roots[] = {
191 "",
192 "mingw64",
193 "mingw32",
194 NULL
195 };
196 const char **root;
197 size_t orig_path_len = path->size;
198
199 for (root = architecture_roots; *root; root++) {
200 if ((*root[0] && git_str_joinpath(path, path->ptr, *root) < 0) ||
201 git_str_joinpath(path, path->ptr, subdir) < 0)
202 return -1;
5540d947 203
e579e0f7
MB
204 if (git_fs_path_exists(path->ptr) &&
205 git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, path->ptr) < 0)
206 return -1;
5540d947 207
e579e0f7
MB
208 git_str_truncate(path, orig_path_len);
209 }
5540d947 210
e579e0f7
MB
211 return 0;
212}
5540d947 213
e579e0f7
MB
214int git_win32__find_system_dirs(git_str *out, const char *subdir)
215{
216 git_win32_path pathdir, regdir;
217 git_str path8 = GIT_STR_INIT;
218 bool has_pathdir, has_regdir;
219 int error;
220
221 has_pathdir = (find_sysdir_in_path(pathdir) == 0);
222 has_regdir = (find_sysdir_in_registry(regdir) == 0);
223
224 if (!has_pathdir && !has_regdir)
225 return 0;
226
227 /*
228 * Usually the git in the path is the same git in the registry,
229 * in this case there's no need to duplicate the paths.
230 */
231 if (has_pathdir && has_regdir && wcscmp(pathdir, regdir) == 0)
232 has_regdir = false;
233
234 if (has_pathdir) {
235 if ((error = win32_path_to_8(&path8, pathdir)) < 0 ||
236 (error = append_subdir(out, &path8, subdir)) < 0)
237 goto done;
238 }
41954a49 239
e579e0f7
MB
240 if (has_regdir) {
241 if ((error = win32_path_to_8(&path8, regdir)) < 0 ||
242 (error = append_subdir(out, &path8, subdir)) < 0)
243 goto done;
244 }
5540d947 245
e579e0f7
MB
246done:
247 git_str_dispose(&path8);
248 return error;
5540d947
RB
249}
250
e579e0f7 251int git_win32__find_global_dirs(git_str *out)
5540d947 252{
5540d947
RB
253 static const wchar_t *global_tmpls[4] = {
254 L"%HOME%\\",
255 L"%HOMEDRIVE%%HOMEPATH%\\",
256 L"%USERPROFILE%\\",
257 NULL,
258 };
259
c4ac556e 260 return win32_find_existing_dirs(out, global_tmpls);
5540d947
RB
261}
262
e579e0f7 263int git_win32__find_xdg_dirs(git_str *out)
5540d947 264{
5540d947
RB
265 static const wchar_t *global_tmpls[7] = {
266 L"%XDG_CONFIG_HOME%\\git",
267 L"%APPDATA%\\git",
268 L"%LOCALAPPDATA%\\git",
269 L"%HOME%\\.config\\git",
270 L"%HOMEDRIVE%%HOMEPATH%\\.config\\git",
271 L"%USERPROFILE%\\.config\\git",
272 NULL,
273 };
274
c4ac556e 275 return win32_find_existing_dirs(out, global_tmpls);
5540d947 276}
8c7c5fa5 277
e579e0f7 278int git_win32__find_programdata_dirs(git_str *out)
8c7c5fa5
CMN
279{
280 static const wchar_t *programdata_tmpls[2] = {
281 L"%PROGRAMDATA%\\Git",
282 NULL,
283 };
284
285 return win32_find_existing_dirs(out, programdata_tmpls);
286}