Skip to content

Commit 27db4f8

Browse files
committed
Add Git for Windows' wrapper executable
On Windows, Git is faced by the challenge that it has to set up certain environment variables before running Git under special circumstances such as when Git is called directly from cmd.exe (i.e. outside any Bash environment). This source code was taken from msysGit's commit 74a198d: https://github.com/msysgit/msysgit/blob/74a198d/src/git-wrapper/git-wrapper.c Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 0818409 commit 27db4f8

File tree

1 file changed

+194
-0
lines changed

1 file changed

+194
-0
lines changed

compat/win32/git-wrapper.c

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*
2+
* git-wrapper - replace cmd\git.cmd with an executable
3+
*
4+
* Copyright (C) 2012 Pat Thoyts <[email protected]>
5+
*/
6+
7+
#define STRICT
8+
#define WIN32_LEAN_AND_MEAN
9+
#define UNICODE
10+
#define _UNICODE
11+
#include <windows.h>
12+
#include <shlwapi.h>
13+
#include <shellapi.h>
14+
#include <stdio.h>
15+
16+
static void print_error(LPCWSTR prefix, DWORD error_number)
17+
{
18+
LPWSTR buffer = NULL;
19+
DWORD count = 0;
20+
21+
count = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
22+
| FORMAT_MESSAGE_FROM_SYSTEM
23+
| FORMAT_MESSAGE_IGNORE_INSERTS,
24+
NULL, error_number, LANG_NEUTRAL,
25+
(LPTSTR)&buffer, 0, NULL);
26+
if (count < 1)
27+
count = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
28+
| FORMAT_MESSAGE_FROM_STRING
29+
| FORMAT_MESSAGE_ARGUMENT_ARRAY,
30+
L"Code 0x%1!08x!",
31+
0, LANG_NEUTRAL, (LPTSTR)&buffer, 0,
32+
(va_list*)&error_number);
33+
fwprintf(stderr, L"%s: %s", prefix, buffer);
34+
LocalFree((HLOCAL)buffer);
35+
}
36+
37+
int main(void)
38+
{
39+
int r = 1, wait = 1;
40+
WCHAR exepath[MAX_PATH], exe[MAX_PATH];
41+
LPWSTR cmd = NULL, path2 = NULL, exep = exe;
42+
UINT codepage = 0;
43+
int len;
44+
45+
/* get the installation location */
46+
GetModuleFileName(NULL, exepath, MAX_PATH);
47+
PathRemoveFileSpec(exepath);
48+
PathRemoveFileSpec(exepath);
49+
50+
/* set the default exe module */
51+
wcscpy(exe, exepath);
52+
PathAppend(exe, L"bin\\git.exe");
53+
54+
/* if not set, set TERM to msys */
55+
if (!GetEnvironmentVariable(L"TERM", NULL, 0))
56+
SetEnvironmentVariable(L"TERM", L"msys");
57+
58+
/* if not set, set PLINK_PROTOCOL to ssh */
59+
if (!GetEnvironmentVariable(L"PLINK_PROTOCOL", NULL, 0))
60+
SetEnvironmentVariable(L"PLINK_PROTOCOL", L"ssh");
61+
62+
/* set HOME to %HOMEDRIVE%%HOMEPATH% or %USERPROFILE%
63+
* With roaming profiles: HOMEPATH is the roaming location and
64+
* USERPROFILE is the local location
65+
*/
66+
if (!GetEnvironmentVariable(L"HOME", NULL, 0)) {
67+
LPWSTR e = NULL;
68+
len = GetEnvironmentVariable(L"HOMEPATH", NULL, 0);
69+
if (len == 0) {
70+
len = GetEnvironmentVariable(L"USERPROFILE", NULL, 0);
71+
if (len != 0) {
72+
e = (LPWSTR)malloc(len * sizeof(WCHAR));
73+
GetEnvironmentVariable(L"USERPROFILE", e, len);
74+
SetEnvironmentVariable(L"HOME", e);
75+
free(e);
76+
}
77+
}
78+
else {
79+
int n;
80+
len += GetEnvironmentVariable(L"HOMEDRIVE", NULL, 0);
81+
e = (LPWSTR)malloc(sizeof(WCHAR) * (len + 2));
82+
n = GetEnvironmentVariable(L"HOMEDRIVE", e, len);
83+
GetEnvironmentVariable(L"HOMEPATH", &e[n], len-n);
84+
SetEnvironmentVariable(L"HOME", e);
85+
free(e);
86+
}
87+
}
88+
89+
/* extend the PATH */
90+
len = GetEnvironmentVariable(L"PATH", NULL, 0);
91+
len = sizeof(WCHAR) * (len + 2 * MAX_PATH);
92+
path2 = (LPWSTR)malloc(len);
93+
wcscpy(path2, exepath);
94+
PathAppend(path2, L"bin;");
95+
/* should do this only if it exists */
96+
wcscat(path2, exepath);
97+
PathAppend(path2, L"mingw\\bin;");
98+
GetEnvironmentVariable(L"PATH", &path2[wcslen(path2)],
99+
(len/sizeof(WCHAR))-wcslen(path2));
100+
SetEnvironmentVariable(L"PATH", path2);
101+
free(path2);
102+
103+
104+
/* fix up the command line to call git.exe
105+
* We have to be very careful about quoting here so we just
106+
* trim off the first argument and replace it leaving the rest
107+
* untouched.
108+
*/
109+
{
110+
int wargc = 0, gui = 0;
111+
LPWSTR cmdline = NULL;
112+
LPWSTR *wargv = NULL, p = NULL;
113+
cmdline = GetCommandLine();
114+
wargv = CommandLineToArgvW(cmdline, &wargc);
115+
cmd = (LPWSTR)malloc(sizeof(WCHAR) *
116+
(wcslen(cmdline) + MAX_PATH));
117+
if (wargc > 1 && wcsicmp(L"gui", wargv[1]) == 0) {
118+
wait = 0;
119+
if (wargc > 2 && wcsicmp(L"citool", wargv[2]) == 0) {
120+
wait = 1;
121+
wcscpy(cmd, L"git.exe");
122+
}
123+
else {
124+
WCHAR script[MAX_PATH];
125+
gui = 1;
126+
wcscpy(script, exepath);
127+
PathAppend(script,
128+
L"libexec\\git-core\\git-gui");
129+
PathQuoteSpaces(script);
130+
wcscpy(cmd, L"wish.exe ");
131+
wcscat(cmd, script);
132+
wcscat(cmd, L" --");
133+
/* find the module from the commandline */
134+
exep = NULL;
135+
}
136+
}
137+
else
138+
wcscpy(cmd, L"git.exe");
139+
140+
/* append all after first space after the initial parameter */
141+
p = wcschr(&cmdline[wcslen(wargv[0])], L' ');
142+
if (p && *p) {
143+
/* for git gui subcommands, remove the 'gui' word */
144+
if (gui) {
145+
while (*p == L' ') ++p;
146+
p = wcschr(p, L' ');
147+
}
148+
if (p && *p)
149+
wcscat(cmd, p);
150+
}
151+
LocalFree(wargv);
152+
}
153+
154+
/* set the console to ANSI/GUI codepage */
155+
codepage = GetConsoleCP();
156+
SetConsoleCP(GetACP());
157+
158+
{
159+
STARTUPINFO si;
160+
PROCESS_INFORMATION pi;
161+
BOOL br = FALSE;
162+
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
163+
ZeroMemory(&si, sizeof(STARTUPINFO));
164+
si.cb = sizeof(STARTUPINFO);
165+
br = CreateProcess(/* module: null means use command line */
166+
exep,
167+
cmd, /* modified command line */
168+
NULL, /* process handle inheritance */
169+
NULL, /* thread handle inheritance */
170+
TRUE, /* handles inheritable? */
171+
CREATE_UNICODE_ENVIRONMENT,
172+
NULL, /* environment: use parent */
173+
NULL, /* starting directory: use parent */
174+
&si, &pi);
175+
if (br) {
176+
if (wait)
177+
WaitForSingleObject(pi.hProcess, INFINITE);
178+
if (!GetExitCodeProcess(pi.hProcess, (DWORD *)&r))
179+
print_error(L"error reading exit code",
180+
GetLastError());
181+
CloseHandle(pi.hProcess);
182+
}
183+
else {
184+
print_error(L"error launching git", GetLastError());
185+
r = 1;
186+
}
187+
}
188+
189+
free(cmd);
190+
191+
/* reset the console codepage */
192+
SetConsoleCP(codepage);
193+
ExitProcess(r);
194+
}

0 commit comments

Comments
 (0)