]> git.proxmox.com Git - libgit2.git/blame - src/win32/findfile.c
New upstream version 1.3.0+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
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
22typedef struct {
23 git_win32_path path;
24 DWORD len;
25} _findfile_path;
997579be 26
c2c81615 27static 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 37static 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 52static 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 73static 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 108static 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 143static 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 168int 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 195int 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 207int 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
222int 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}