Skip to content

Commit 9a98636

Browse files
committed
sysdir: move windows sysdir functions out of shared util
The windows sysdir functions are libgit2-specific and for git compatibility only; remove them from the shared util directory and bring them into the libgit2 source tree.
1 parent f02bcf7 commit 9a98636

File tree

6 files changed

+275
-318
lines changed

6 files changed

+275
-318
lines changed

src/libgit2/sysdir.c

Lines changed: 269 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,262 @@
1212
#include "fs_path.h"
1313
#include <ctype.h>
1414
#if GIT_WIN32
15-
#include "win32/findfile.h"
15+
# include "fs_path.h"
16+
# include "win32/path_w32.h"
17+
# include "win32/utf-conv.h"
1618
#else
17-
#include <unistd.h>
18-
#include <pwd.h>
19+
# include <unistd.h>
20+
# include <pwd.h>
1921
#endif
2022

23+
#ifdef GIT_WIN32
24+
# define REG_GITFORWINDOWS_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
25+
# define REG_GITFORWINDOWS_KEY_WOW64 L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
26+
27+
static int expand_win32_path(git_win32_path dest, const wchar_t *src)
28+
{
29+
DWORD len = ExpandEnvironmentStringsW(src, dest, GIT_WIN_PATH_UTF16);
30+
31+
if (!len || len > GIT_WIN_PATH_UTF16)
32+
return -1;
33+
34+
return 0;
35+
}
36+
37+
static int win32_path_to_utf8(git_str *dest, const wchar_t *src)
38+
{
39+
git_win32_utf8_path utf8_path;
40+
41+
if (git_win32_path_to_utf8(utf8_path, src) < 0) {
42+
git_error_set(GIT_ERROR_OS, "unable to convert path to UTF-8");
43+
return -1;
44+
}
45+
46+
/* Convert backslashes to forward slashes */
47+
git_fs_path_mkposix(utf8_path);
48+
49+
return git_str_sets(dest, utf8_path);
50+
}
51+
52+
static git_win32_path mock_registry;
53+
static bool mock_registry_set;
54+
55+
extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir)
56+
{
57+
if (!mock_sysdir) {
58+
mock_registry[0] = L'\0';
59+
mock_registry_set = false;
60+
} else {
61+
size_t len = wcslen(mock_sysdir);
62+
63+
if (len > GIT_WIN_PATH_MAX) {
64+
git_error_set(GIT_ERROR_INVALID, "mock path too long");
65+
return -1;
66+
}
67+
68+
wcscpy(mock_registry, mock_sysdir);
69+
mock_registry_set = true;
70+
}
71+
72+
return 0;
73+
}
74+
75+
static int lookup_registry_key(
76+
git_win32_path out,
77+
const HKEY hive,
78+
const wchar_t* key,
79+
const wchar_t *value)
80+
{
81+
HKEY hkey;
82+
DWORD type, size;
83+
int error = GIT_ENOTFOUND;
84+
85+
/*
86+
* Registry data may not be NUL terminated, provide room to do
87+
* it ourselves.
88+
*/
89+
size = (DWORD)((sizeof(git_win32_path) - 1) * sizeof(wchar_t));
90+
91+
if (RegOpenKeyExW(hive, key, 0, KEY_READ, &hkey) != 0)
92+
return GIT_ENOTFOUND;
93+
94+
if (RegQueryValueExW(hkey, value, NULL, &type, (LPBYTE)out, &size) == 0 &&
95+
type == REG_SZ &&
96+
size > 0 &&
97+
size < sizeof(git_win32_path)) {
98+
size_t wsize = size / sizeof(wchar_t);
99+
size_t len = wsize - 1;
100+
101+
if (out[wsize - 1] != L'\0') {
102+
len = wsize;
103+
out[wsize] = L'\0';
104+
}
105+
106+
if (out[len - 1] == L'\\')
107+
out[len - 1] = L'\0';
108+
109+
if (_waccess(out, F_OK) == 0)
110+
error = 0;
111+
}
112+
113+
RegCloseKey(hkey);
114+
return error;
115+
}
116+
117+
static int find_sysdir_in_registry(git_win32_path out)
118+
{
119+
if (mock_registry_set) {
120+
if (mock_registry[0] == L'\0')
121+
return GIT_ENOTFOUND;
122+
123+
wcscpy(out, mock_registry);
124+
return 0;
125+
}
126+
127+
if (lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
128+
lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0 ||
129+
lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
130+
lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0)
131+
return 0;
132+
133+
return GIT_ENOTFOUND;
134+
}
135+
136+
static int find_sysdir_in_path(git_win32_path out)
137+
{
138+
size_t out_len;
139+
140+
if (git_win32_path_find_executable(out, L"git.exe") < 0 &&
141+
git_win32_path_find_executable(out, L"git.cmd") < 0)
142+
return GIT_ENOTFOUND;
143+
144+
out_len = wcslen(out);
145+
146+
/* Trim the file name */
147+
if (out_len <= CONST_STRLEN(L"git.exe"))
148+
return GIT_ENOTFOUND;
149+
150+
out_len -= CONST_STRLEN(L"git.exe");
151+
152+
if (out_len && out[out_len - 1] == L'\\')
153+
out_len--;
154+
155+
/*
156+
* Git for Windows usually places the command in a 'bin' or
157+
* 'cmd' directory, trim that.
158+
*/
159+
if (out_len >= CONST_STRLEN(L"\\bin") &&
160+
wcsncmp(&out[out_len - CONST_STRLEN(L"\\bin")], L"\\bin", CONST_STRLEN(L"\\bin")) == 0)
161+
out_len -= CONST_STRLEN(L"\\bin");
162+
else if (out_len >= CONST_STRLEN(L"\\cmd") &&
163+
wcsncmp(&out[out_len - CONST_STRLEN(L"\\cmd")], L"\\cmd", CONST_STRLEN(L"\\cmd")) == 0)
164+
out_len -= CONST_STRLEN(L"\\cmd");
165+
166+
if (!out_len)
167+
return GIT_ENOTFOUND;
168+
169+
out[out_len] = L'\0';
170+
return 0;
171+
}
172+
173+
static int find_win32_dirs(
174+
git_str *out,
175+
const wchar_t* tmpl[])
176+
{
177+
git_win32_path path16;
178+
git_str buf = GIT_STR_INIT;
179+
180+
git_str_clear(out);
181+
182+
for (; *tmpl != NULL; tmpl++) {
183+
if (!expand_win32_path(path16, *tmpl) &&
184+
path16[0] != L'%' &&
185+
!_waccess(path16, F_OK)) {
186+
win32_path_to_utf8(&buf, path16);
187+
188+
if (buf.size)
189+
git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
190+
}
191+
}
192+
193+
git_str_dispose(&buf);
194+
195+
return (git_str_oom(out) ? -1 : 0);
196+
}
197+
198+
static int append_subdir(git_str *out, git_str *path, const char *subdir)
199+
{
200+
static const char* architecture_roots[] = {
201+
"",
202+
"mingw64",
203+
"mingw32",
204+
NULL
205+
};
206+
const char **root;
207+
size_t orig_path_len = path->size;
208+
209+
for (root = architecture_roots; *root; root++) {
210+
if ((*root[0] && git_str_joinpath(path, path->ptr, *root) < 0) ||
211+
git_str_joinpath(path, path->ptr, subdir) < 0)
212+
return -1;
213+
214+
if (git_fs_path_exists(path->ptr) &&
215+
git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, path->ptr) < 0)
216+
return -1;
217+
218+
git_str_truncate(path, orig_path_len);
219+
}
220+
221+
return 0;
222+
}
223+
224+
int git_win32__find_system_dirs(git_str *out, const char *subdir)
225+
{
226+
git_win32_path pathdir, regdir;
227+
git_str path8 = GIT_STR_INIT;
228+
bool has_pathdir, has_regdir;
229+
int error;
230+
231+
has_pathdir = (find_sysdir_in_path(pathdir) == 0);
232+
has_regdir = (find_sysdir_in_registry(regdir) == 0);
233+
234+
if (!has_pathdir && !has_regdir)
235+
return 0;
236+
237+
/*
238+
* Usually the git in the path is the same git in the registry,
239+
* in this case there's no need to duplicate the paths.
240+
*/
241+
if (has_pathdir && has_regdir && wcscmp(pathdir, regdir) == 0)
242+
has_regdir = false;
243+
244+
if (has_pathdir) {
245+
if ((error = win32_path_to_utf8(&path8, pathdir)) < 0 ||
246+
(error = append_subdir(out, &path8, subdir)) < 0)
247+
goto done;
248+
}
249+
250+
if (has_regdir) {
251+
if ((error = win32_path_to_utf8(&path8, regdir)) < 0 ||
252+
(error = append_subdir(out, &path8, subdir)) < 0)
253+
goto done;
254+
}
255+
256+
done:
257+
git_str_dispose(&path8);
258+
return error;
259+
}
260+
#endif /* WIN32 */
261+
21262
static int git_sysdir_guess_programdata_dirs(git_str *out)
22263
{
23264
#ifdef GIT_WIN32
24-
return git_win32__find_programdata_dirs(out);
265+
static const wchar_t *programdata_tmpls[2] = {
266+
L"%PROGRAMDATA%\\Git",
267+
NULL,
268+
};
269+
270+
return find_win32_dirs(out, programdata_tmpls);
25271
#else
26272
git_str_clear(out);
27273
return 0;
@@ -78,7 +324,14 @@ static int get_passwd_home(git_str *out, uid_t uid)
78324
static int git_sysdir_guess_global_dirs(git_str *out)
79325
{
80326
#ifdef GIT_WIN32
81-
return git_win32__find_global_dirs(out);
327+
static const wchar_t *global_tmpls[4] = {
328+
L"%HOME%\\",
329+
L"%HOMEDRIVE%%HOMEPATH%\\",
330+
L"%USERPROFILE%\\",
331+
NULL,
332+
};
333+
334+
return find_win32_dirs(out, global_tmpls);
82335
#else
83336
int error;
84337
uid_t uid, euid;
@@ -117,7 +370,17 @@ static int git_sysdir_guess_global_dirs(git_str *out)
117370
static int git_sysdir_guess_xdg_dirs(git_str *out)
118371
{
119372
#ifdef GIT_WIN32
120-
return git_win32__find_xdg_dirs(out);
373+
static const wchar_t *global_tmpls[7] = {
374+
L"%XDG_CONFIG_HOME%\\git",
375+
L"%APPDATA%\\git",
376+
L"%LOCALAPPDATA%\\git",
377+
L"%HOME%\\.config\\git",
378+
L"%HOMEDRIVE%%HOMEPATH%\\.config\\git",
379+
L"%USERPROFILE%\\.config\\git",
380+
NULL,
381+
};
382+
383+
return find_win32_dirs(out, global_tmpls);
121384
#else
122385
git_str env = GIT_STR_INIT;
123386
int error;

src/libgit2/sysdir.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,10 @@ extern int git_sysdir_set(git_sysdir_t which, const char *paths);
110110
*/
111111
extern int git_sysdir_reset(void);
112112

113+
/** Sets the registry system dir to a mock; for testing. */
114+
extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir);
115+
116+
/** Find the given system dir; for testing. */
117+
extern int git_win32__find_system_dirs(git_str *out, const char *subdir);
118+
113119
#endif

src/util/futils.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@
1313
#include "rand.h"
1414

1515
#include <ctype.h>
16-
#if GIT_WIN32
17-
#include "win32/findfile.h"
18-
#endif
1916

2017
#define GIT_FILEMODE_DEFAULT 0100666
2118

0 commit comments

Comments
 (0)