Skip to content

Commit 8458e5c

Browse files
committed
Git wrapper: allow overriding what executable is called
The Git wrapper does one thing, and does it well: setting up the environment required to run Git and its scripts, and then hand off to another program. We already do this for the Git executable itself; in Git for Windows' context, we have exactly the same need also when calling the Git Bash or Git CMD. However, both are tied to what particular shell environment you use, though: MSys or MSys2 (or whatever else cunning developers make work for them). This means that the Git Bash and Git CMD need to be compiled in the respective context (e.g. when compiling the mingw-w64-git package in the MSys2 context). Happily, Windows offers a way to configure compiled executables: resources. So let's just look whether the current executable has a string resource and use it as the command-line to execute after the environment is set up. To support MSys2's Git Bash better (where `mintty` should, but might not, be available), we verify whether the specified executable exists, and keep looking for string resources if it does not. For even more flexibility, we expand environment variables specified as `@@<VARIABLE-NAME>@@`, and for convenience `@@EXEPATH@@` expands into the directory in which the executable resides. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent ef43288 commit 8458e5c

File tree

1 file changed

+105
-1
lines changed

1 file changed

+105
-1
lines changed

compat/win32/git-wrapper.c

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,105 @@ static LPWSTR fixup_commandline(LPWSTR exepath, LPWSTR *exep, int *wait,
173173
return cmd;
174174
}
175175

176+
static int configure_via_resource(LPWSTR basename, LPWSTR exepath, LPWSTR exep,
177+
LPWSTR *prefix_args, int *prefix_args_len,
178+
int *is_git_command, int *start_in_home)
179+
{
180+
int id = 0, wargc;
181+
LPWSTR *wargv;
182+
183+
#define BUFSIZE 65536
184+
static WCHAR buf[BUFSIZE];
185+
int len;
186+
187+
for (id = 0; ; id++) {
188+
len = LoadString(NULL, id, buf, BUFSIZE);
189+
190+
if (!len) {
191+
if (!id)
192+
return 0; /* no resources found */
193+
194+
fwprintf(stderr, L"Need a valid command-line; "
195+
L"Copy %s to edit-res.exe and call\n"
196+
L"\n\tedit-res.exe command %s "
197+
L"\"<command-line>\"\n",
198+
basename, basename);
199+
exit(1);
200+
}
201+
202+
if (len >= BUFSIZE) {
203+
fwprintf(stderr,
204+
L"Could not read resource (too large)\n");
205+
exit(1);
206+
}
207+
208+
buf[len] = L'\0';
209+
210+
if (!id)
211+
SetEnvironmentVariable(L"EXEPATH", exepath);
212+
213+
for (;;) {
214+
LPWSTR atat = wcsstr(buf, L"@@"), atat2;
215+
WCHAR save;
216+
int env_len, delta;
217+
218+
if (!atat)
219+
break;
220+
221+
atat2 = wcsstr(atat + 2, L"@@");
222+
if (!atat2)
223+
break;
224+
225+
*atat2 = L'\0';
226+
env_len = GetEnvironmentVariable(atat + 2, NULL, 0);
227+
delta = env_len - 1 - (atat2 + 2 - atat);
228+
if (len + delta >= BUFSIZE) {
229+
fwprintf(stderr,
230+
L"Substituting '%s' results in too "
231+
L"large a command-line\n", atat + 2);
232+
exit(1);
233+
}
234+
if (delta)
235+
memmove(atat2 + 2 + delta, atat2 + 2,
236+
sizeof(WCHAR) * (len + 1
237+
- (atat2 + 2 - buf)));
238+
len += delta;
239+
save = atat[env_len - 1];
240+
GetEnvironmentVariable(atat + 2, atat, env_len);
241+
atat[env_len - 1] = save;
242+
}
243+
244+
/* parse first argument */
245+
wargv = CommandLineToArgvW(buf, &wargc);
246+
if (wargc < 1) {
247+
fwprintf(stderr, L"Invalid command-line: '%s'\n", buf);
248+
exit(1);
249+
}
250+
if (*wargv[0] == L'\\' ||
251+
(isalpha(*wargv[0]) && wargv[0][1] == L':'))
252+
wcscpy(exep, wargv[0]);
253+
else {
254+
wcscpy(exep, exepath);
255+
PathAppend(exep, wargv[0]);
256+
}
257+
LocalFree(wargv);
258+
259+
if (_waccess(exep, 0) != -1)
260+
break;
261+
fwprintf(stderr,
262+
L"Skipping command-line '%s'\n('%s' not found)\n",
263+
buf, exep);
264+
}
265+
266+
*prefix_args = buf;
267+
*prefix_args_len = wcslen(buf);
268+
269+
*is_git_command = 0;
270+
*start_in_home = 1;
271+
272+
return 1;
273+
}
274+
176275
int main(void)
177276
{
178277
int r = 1, wait = 1, prefix_args_len = -1, needs_env_setup = 1,
@@ -192,7 +291,12 @@ int main(void)
192291
ExitProcess(1);
193292
}
194293
basename = exepath + wcslen(exepath) + 1;
195-
if (!wcsncmp(basename, L"git-", 4)) {
294+
if (configure_via_resource(basename, exepath, exep,
295+
&prefix_args, &prefix_args_len,
296+
&is_git_command, &start_in_home)) {
297+
/* do nothing */
298+
}
299+
else if (!wcsncmp(basename, L"git-", 4)) {
196300
needs_env_setup = 0;
197301

198302
/* Call a builtin */

0 commit comments

Comments
 (0)