From fa4297826af2cba03d6eca117f8a4d5d40681646 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 13 Mar 2015 14:16:43 +0100 Subject: [PATCH] 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 --- compat/win32/git-wrapper.c | 194 +++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 compat/win32/git-wrapper.c diff --git a/compat/win32/git-wrapper.c b/compat/win32/git-wrapper.c new file mode 100644 index 0000000000..a7495c095f --- /dev/null +++ b/compat/win32/git-wrapper.c @@ -0,0 +1,194 @@ +/* + * git-wrapper - replace cmd\git.cmd with an executable + * + * Copyright (C) 2012 Pat Thoyts + */ + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#define UNICODE +#define _UNICODE +#include +#include +#include +#include + +static void print_error(LPCWSTR prefix, DWORD error_number) +{ + LPWSTR buffer = NULL; + DWORD count = 0; + + count = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error_number, LANG_NEUTRAL, + (LPTSTR)&buffer, 0, NULL); + if (count < 1) + count = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_STRING + | FORMAT_MESSAGE_ARGUMENT_ARRAY, + L"Code 0x%1!08x!", + 0, LANG_NEUTRAL, (LPTSTR)&buffer, 0, + (va_list*)&error_number); + fwprintf(stderr, L"%s: %s", prefix, buffer); + LocalFree((HLOCAL)buffer); +} + +int main(void) +{ + int r = 1, wait = 1; + WCHAR exepath[MAX_PATH], exe[MAX_PATH]; + LPWSTR cmd = NULL, path2 = NULL, exep = exe; + UINT codepage = 0; + int len; + + /* get the installation location */ + GetModuleFileName(NULL, exepath, MAX_PATH); + PathRemoveFileSpec(exepath); + PathRemoveFileSpec(exepath); + + /* set the default exe module */ + wcscpy(exe, exepath); + PathAppend(exe, L"bin\\git.exe"); + + /* if not set, set TERM to msys */ + if (!GetEnvironmentVariable(L"TERM", NULL, 0)) + SetEnvironmentVariable(L"TERM", L"msys"); + + /* if not set, set PLINK_PROTOCOL to ssh */ + if (!GetEnvironmentVariable(L"PLINK_PROTOCOL", NULL, 0)) + SetEnvironmentVariable(L"PLINK_PROTOCOL", L"ssh"); + + /* set HOME to %HOMEDRIVE%%HOMEPATH% or %USERPROFILE% + * With roaming profiles: HOMEPATH is the roaming location and + * USERPROFILE is the local location + */ + if (!GetEnvironmentVariable(L"HOME", NULL, 0)) { + LPWSTR e = NULL; + len = GetEnvironmentVariable(L"HOMEPATH", NULL, 0); + if (len == 0) { + len = GetEnvironmentVariable(L"USERPROFILE", NULL, 0); + if (len != 0) { + e = (LPWSTR)malloc(len * sizeof(WCHAR)); + GetEnvironmentVariable(L"USERPROFILE", e, len); + SetEnvironmentVariable(L"HOME", e); + free(e); + } + } + else { + int n; + len += GetEnvironmentVariable(L"HOMEDRIVE", NULL, 0); + e = (LPWSTR)malloc(sizeof(WCHAR) * (len + 2)); + n = GetEnvironmentVariable(L"HOMEDRIVE", e, len); + GetEnvironmentVariable(L"HOMEPATH", &e[n], len-n); + SetEnvironmentVariable(L"HOME", e); + free(e); + } + } + + /* extend the PATH */ + len = GetEnvironmentVariable(L"PATH", NULL, 0); + len = sizeof(WCHAR) * (len + 2 * MAX_PATH); + path2 = (LPWSTR)malloc(len); + wcscpy(path2, exepath); + PathAppend(path2, L"bin;"); + /* should do this only if it exists */ + wcscat(path2, exepath); + PathAppend(path2, L"mingw\\bin;"); + GetEnvironmentVariable(L"PATH", &path2[wcslen(path2)], + (len/sizeof(WCHAR))-wcslen(path2)); + SetEnvironmentVariable(L"PATH", path2); + free(path2); + + + /* fix up the command line to call git.exe + * We have to be very careful about quoting here so we just + * trim off the first argument and replace it leaving the rest + * untouched. + */ + { + int wargc = 0, gui = 0; + LPWSTR cmdline = NULL; + LPWSTR *wargv = NULL, p = NULL; + cmdline = GetCommandLine(); + wargv = CommandLineToArgvW(cmdline, &wargc); + cmd = (LPWSTR)malloc(sizeof(WCHAR) * + (wcslen(cmdline) + MAX_PATH)); + if (wargc > 1 && wcsicmp(L"gui", wargv[1]) == 0) { + wait = 0; + if (wargc > 2 && wcsicmp(L"citool", wargv[2]) == 0) { + wait = 1; + wcscpy(cmd, L"git.exe"); + } + else { + WCHAR script[MAX_PATH]; + gui = 1; + wcscpy(script, exepath); + PathAppend(script, + L"libexec\\git-core\\git-gui"); + PathQuoteSpaces(script); + wcscpy(cmd, L"wish.exe "); + wcscat(cmd, script); + wcscat(cmd, L" --"); + /* find the module from the commandline */ + exep = NULL; + } + } + else + wcscpy(cmd, L"git.exe"); + + /* append all after first space after the initial parameter */ + p = wcschr(&cmdline[wcslen(wargv[0])], L' '); + if (p && *p) { + /* for git gui subcommands, remove the 'gui' word */ + if (gui) { + while (*p == L' ') ++p; + p = wcschr(p, L' '); + } + if (p && *p) + wcscat(cmd, p); + } + LocalFree(wargv); + } + + /* set the console to ANSI/GUI codepage */ + codepage = GetConsoleCP(); + SetConsoleCP(GetACP()); + + { + STARTUPINFO si; + PROCESS_INFORMATION pi; + BOOL br = FALSE; + ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + br = CreateProcess(/* module: null means use command line */ + exep, + cmd, /* modified command line */ + NULL, /* process handle inheritance */ + NULL, /* thread handle inheritance */ + TRUE, /* handles inheritable? */ + CREATE_UNICODE_ENVIRONMENT, + NULL, /* environment: use parent */ + NULL, /* starting directory: use parent */ + &si, &pi); + if (br) { + if (wait) + WaitForSingleObject(pi.hProcess, INFINITE); + if (!GetExitCodeProcess(pi.hProcess, (DWORD *)&r)) + print_error(L"error reading exit code", + GetLastError()); + CloseHandle(pi.hProcess); + } + else { + print_error(L"error launching git", GetLastError()); + r = 1; + } + } + + free(cmd); + + /* reset the console codepage */ + SetConsoleCP(codepage); + ExitProcess(r); +}