]> git.proxmox.com Git - libgit2.git/blob - src/win32/findfile.c
New upstream version 1.3.0+dfsg.1
[libgit2.git] / src / win32 / findfile.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
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
8 #include "findfile.h"
9
10 #include "path_w32.h"
11 #include "utf-conv.h"
12 #include "path.h"
13
14 #define REG_MSYSGIT_INSTALL_LOCAL L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
15
16 #ifndef _WIN64
17 #define REG_MSYSGIT_INSTALL REG_MSYSGIT_INSTALL_LOCAL
18 #else
19 #define REG_MSYSGIT_INSTALL L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
20 #endif
21
22 typedef struct {
23 git_win32_path path;
24 DWORD len;
25 } _findfile_path;
26
27 static int git_win32__expand_path(_findfile_path *dest, const wchar_t *src)
28 {
29 dest->len = ExpandEnvironmentStringsW(src, dest->path, ARRAY_SIZE(dest->path));
30
31 if (!dest->len || dest->len > ARRAY_SIZE(dest->path))
32 return -1;
33
34 return 0;
35 }
36
37 static int win32_path_to_8(git_buf *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_path_mkposix(utf8_path);
48
49 return git_buf_sets(dest, utf8_path);
50 }
51
52 static wchar_t *win32_walkpath(wchar_t *path, wchar_t *buf, size_t buflen)
53 {
54 wchar_t term, *base = path;
55
56 GIT_ASSERT_ARG_WITH_RETVAL(path, NULL);
57 GIT_ASSERT_ARG_WITH_RETVAL(buf, NULL);
58 GIT_ASSERT_ARG_WITH_RETVAL(buflen, NULL);
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
73 static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe, const wchar_t *subdir)
74 {
75 wchar_t *env = _wgetenv(L"PATH"), lastch;
76 _findfile_path root;
77 size_t gitexe_len = wcslen(gitexe);
78
79 if (!env)
80 return -1;
81
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 }
91
92 if (root.len + gitexe_len >= MAX_PATH)
93 continue;
94 wcscpy(&root.path[root.len], gitexe);
95
96 if (_waccess(root.path, F_OK) == 0 && root.len > 5) {
97 /* replace "bin\\" or "cmd\\" with subdir */
98 wcscpy(&root.path[root.len - 4], subdir);
99
100 win32_path_to_8(buf, root.path);
101 return 0;
102 }
103 }
104
105 return GIT_ENOTFOUND;
106 }
107
108 static int win32_find_git_in_registry(
109 git_buf *buf, const HKEY hive, const wchar_t *key, const wchar_t *subdir)
110 {
111 HKEY hKey;
112 int error = GIT_ENOTFOUND;
113
114 GIT_ASSERT_ARG(buf);
115
116 if (!RegOpenKeyExW(hive, key, 0, KEY_READ, &hKey)) {
117 DWORD dwType, cbData;
118 git_win32_path path;
119
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));
123
124 /* InstallLocation points to the root of the git directory */
125 if (!RegQueryValueExW(hKey, L"InstallLocation", NULL, &dwType, (LPBYTE)path, &cbData) &&
126 dwType == REG_SZ) {
127
128 /* Append the suffix */
129 wcscat(path, subdir);
130
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;
135 }
136
137 RegCloseKey(hKey);
138 }
139
140 return error;
141 }
142
143 static int win32_find_existing_dirs(
144 git_buf *out, const wchar_t *tmpl[])
145 {
146 _findfile_path path16;
147 git_buf buf = GIT_BUF_INIT;
148
149 git_buf_clear(out);
150
151 for (; *tmpl != NULL; tmpl++) {
152 if (!git_win32__expand_path(&path16, *tmpl) &&
153 path16.path[0] != L'%' &&
154 !_waccess(path16.path, F_OK))
155 {
156 win32_path_to_8(&buf, path16.path);
157
158 if (buf.size)
159 git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
160 }
161 }
162
163 git_buf_dispose(&buf);
164
165 return (git_buf_oom(out) ? -1 : 0);
166 }
167
168 int git_win32__find_system_dirs(git_buf *out, const wchar_t *subdir)
169 {
170 git_buf buf = GIT_BUF_INIT;
171
172 /* directories where git.exe & git.cmd are found */
173 if (!win32_find_git_in_path(&buf, L"git.exe", subdir) && buf.size)
174 git_buf_set(out, buf.ptr, buf.size);
175 else
176 git_buf_clear(out);
177
178 if (!win32_find_git_in_path(&buf, L"git.cmd", subdir) && buf.size)
179 git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
180
181 /* directories where git is installed according to registry */
182 if (!win32_find_git_in_registry(
183 &buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL, subdir) && buf.size)
184 git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
185
186 if (!win32_find_git_in_registry(
187 &buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL, subdir) && buf.size)
188 git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
189
190 git_buf_dispose(&buf);
191
192 return (git_buf_oom(out) ? -1 : 0);
193 }
194
195 int git_win32__find_global_dirs(git_buf *out)
196 {
197 static const wchar_t *global_tmpls[4] = {
198 L"%HOME%\\",
199 L"%HOMEDRIVE%%HOMEPATH%\\",
200 L"%USERPROFILE%\\",
201 NULL,
202 };
203
204 return win32_find_existing_dirs(out, global_tmpls);
205 }
206
207 int git_win32__find_xdg_dirs(git_buf *out)
208 {
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
219 return win32_find_existing_dirs(out, global_tmpls);
220 }
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 }