From e78357e3c647194f2bb653a80c900b7097048d9a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 26 May 2015 15:19:04 +0200 Subject: [PATCH 1/5] git wrapper: refactor extraction of 1st arg into its own function This will be reused by the upcoming `--command=` option. Signed-off-by: Johannes Schindelin --- compat/win32/git-wrapper.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/compat/win32/git-wrapper.c b/compat/win32/git-wrapper.c index 1e214af2a4..1ca8a8a95b 100644 --- a/compat/win32/git-wrapper.c +++ b/compat/win32/git-wrapper.c @@ -188,6 +188,26 @@ static int strip_prefix(LPWSTR str, int *len, LPCWSTR prefix) return 0; } +static void extract_first_arg(LPWSTR command_line, LPWSTR exepath, LPWSTR buf) +{ + LPWSTR *wargv; + int wargc; + + wargv = CommandLineToArgvW(command_line, &wargc); + if (wargc < 1) { + fwprintf(stderr, L"Invalid command-line: '%s'\n", command_line); + exit(1); + } + if (*wargv[0] == L'\\' || + (isalpha(*wargv[0]) && wargv[0][1] == L':')) + wcscpy(buf, wargv[0]); + else { + wcscpy(buf, exepath); + PathAppend(buf, wargv[0]); + } + LocalFree(wargv); +} + static int configure_via_resource(LPWSTR basename, LPWSTR exepath, LPWSTR exep, LPWSTR *prefix_args, int *prefix_args_len, int *is_git_command, LPWSTR *working_directory, int *full_path, @@ -268,20 +288,7 @@ static int configure_via_resource(LPWSTR basename, LPWSTR exepath, LPWSTR exep, atat[env_len - 1] = save; } - /* parse first argument */ - wargv = CommandLineToArgvW(buf, &wargc); - if (wargc < 1) { - fwprintf(stderr, L"Invalid command-line: '%s'\n", buf); - exit(1); - } - if (*wargv[0] == L'\\' || - (isalpha(*wargv[0]) && wargv[0][1] == L':')) - wcscpy(exep, wargv[0]); - else { - wcscpy(exep, exepath); - PathAppend(exep, wargv[0]); - } - LocalFree(wargv); + extract_first_arg(buf, exepath, exep); if (_waccess(exep, 0) != -1) break; From a1026735fe6bd2d43970337fa81c4316e57f85f9 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 26 May 2015 15:19:04 +0200 Subject: [PATCH 2/5] git wrapper: refactor @@VAR@@ expansion into its own function We will enhance the function in the next commit to support @@VAR@@ expansion in the upcoming `--command=` option. Signed-off-by: Johannes Schindelin --- compat/win32/git-wrapper.c | 78 +++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/compat/win32/git-wrapper.c b/compat/win32/git-wrapper.c index 1ca8a8a95b..08daea1d60 100644 --- a/compat/win32/git-wrapper.c +++ b/compat/win32/git-wrapper.c @@ -208,6 +208,44 @@ static void extract_first_arg(LPWSTR command_line, LPWSTR exepath, LPWSTR buf) LocalFree(wargv); } +static LPWSTR expand_variables(LPWSTR buf, size_t alloc) +{ + size_t len = wcslen(buf); + + for (;;) { + LPWSTR atat = wcsstr(buf, L"@@"), atat2; + WCHAR save; + int env_len, delta; + + if (!atat) + break; + + atat2 = wcsstr(atat + 2, L"@@"); + if (!atat2) + break; + + *atat2 = L'\0'; + env_len = GetEnvironmentVariable(atat + 2, NULL, 0); + delta = env_len - 1 - (atat2 + 2 - atat); + if (len + delta >= alloc) { + fwprintf(stderr, + L"Substituting '%s' results in too " + L"large a command-line\n", atat + 2); + exit(1); + } + if (delta) + memmove(atat2 + 2 + delta, atat2 + 2, + sizeof(WCHAR) * (len + 1 + - (atat2 + 2 - buf))); + len += delta; + save = atat[env_len - 1]; + GetEnvironmentVariable(atat + 2, atat, env_len); + atat[env_len - 1] = save; + } + + return buf; +} + static int configure_via_resource(LPWSTR basename, LPWSTR exepath, LPWSTR exep, LPWSTR *prefix_args, int *prefix_args_len, int *is_git_command, LPWSTR *working_directory, int *full_path, @@ -218,6 +256,7 @@ static int configure_via_resource(LPWSTR basename, LPWSTR exepath, LPWSTR exep, #define BUFSIZE 65536 static WCHAR buf[BUFSIZE]; + LPWSTR buf2 = buf; int len; for (id = 0; ; id++) { @@ -257,48 +296,19 @@ static int configure_via_resource(LPWSTR basename, LPWSTR exepath, LPWSTR exep, if (!id) SetEnvironmentVariable(L"EXEPATH", exepath); - for (;;) { - LPWSTR atat = wcsstr(buf, L"@@"), atat2; - WCHAR save; - int env_len, delta; + buf2 = expand_variables(buf, BUFSIZE); - if (!atat) - break; - - atat2 = wcsstr(atat + 2, L"@@"); - if (!atat2) - break; - - *atat2 = L'\0'; - env_len = GetEnvironmentVariable(atat + 2, NULL, 0); - delta = env_len - 1 - (atat2 + 2 - atat); - if (len + delta >= BUFSIZE) { - fwprintf(stderr, - L"Substituting '%s' results in too " - L"large a command-line\n", atat + 2); - exit(1); - } - if (delta) - memmove(atat2 + 2 + delta, atat2 + 2, - sizeof(WCHAR) * (len + 1 - - (atat2 + 2 - buf))); - len += delta; - save = atat[env_len - 1]; - GetEnvironmentVariable(atat + 2, atat, env_len); - atat[env_len - 1] = save; - } - - extract_first_arg(buf, exepath, exep); + extract_first_arg(buf2, exepath, exep); if (_waccess(exep, 0) != -1) break; fwprintf(stderr, L"Skipping command-line '%s'\n('%s' not found)\n", - buf, exep); + buf2, exep); } - *prefix_args = buf; - *prefix_args_len = wcslen(buf); + *prefix_args = buf2; + *prefix_args_len = wcslen(buf2); *is_git_command = 0; *working_directory = (LPWSTR) 1; From 3692c1d11a1e18dd6598316c659002384cf14687 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 26 May 2015 15:26:15 +0200 Subject: [PATCH 3/5] git wrapper: auto-grow buffer in expand_variables() Signed-off-by: Johannes Schindelin --- compat/win32/git-wrapper.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/compat/win32/git-wrapper.c b/compat/win32/git-wrapper.c index 08daea1d60..603525eb7a 100644 --- a/compat/win32/git-wrapper.c +++ b/compat/win32/git-wrapper.c @@ -208,8 +208,11 @@ static void extract_first_arg(LPWSTR command_line, LPWSTR exepath, LPWSTR buf) LocalFree(wargv); } -static LPWSTR expand_variables(LPWSTR buf, size_t alloc) +#define alloc_nr(x) (((x)+16)*3/2) + +static LPWSTR expand_variables(LPWSTR buffer, size_t alloc) { + LPWSTR buf = buffer; size_t len = wcslen(buf); for (;;) { @@ -228,10 +231,27 @@ static LPWSTR expand_variables(LPWSTR buf, size_t alloc) env_len = GetEnvironmentVariable(atat + 2, NULL, 0); delta = env_len - 1 - (atat2 + 2 - atat); if (len + delta >= alloc) { - fwprintf(stderr, - L"Substituting '%s' results in too " - L"large a command-line\n", atat + 2); - exit(1); + LPWSTR buf2; + alloc = alloc_nr(alloc); + if (alloc <= len + delta) + alloc = len + delta + 1; + if (buf != buffer) + buf2 = realloc(buf, sizeof(WCHAR) * alloc); + else { + buf2 = malloc(sizeof(WCHAR) * alloc); + if (buf2) + memcpy(buf2, buf, sizeof(WCHAR) + * (len + 1)); + } + if (!buf2) { + fwprintf(stderr, + L"Substituting '%s' results in too " + L"large a command-line\n", atat + 2); + exit(1); + } + atat += buf2 - buf; + atat2 += buf2 - buf; + buf = buf2; } if (delta) memmove(atat2 + 2 + delta, atat2 + 2, From 179108f476cbdee24c2fb151e090f3d008fcbdb0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 26 May 2015 15:04:48 +0200 Subject: [PATCH 4/5] git-wrapper: allow overriding the command to spawn via command-line args By embedding string resources into the Git wrapper executable, it can be configured to execute custom commands (after setting up the environment in the way required for Git for Windows to work properly). This feature is used e.g. for `git-bash.exe` which launches a Bash in the configured terminal window. Here, we introduce command-line options to override those string resources. That way, a user can call `git-bash.exe` (which is a copy of the Git wrapper with `usr\bin\bash.exe --login -i` embedded as string resource) with command-line options that will override what command is run. ConEmu, for example, might want to call ...\git-bash.exe --needs-console --no-hide --minimal-search-path ^ --command=usr\\bin\\bash.exe --login -i In particular, the following options are supported now: --command=:: Executes `` instead of the embedded string resource --[no-]minimal-search-path:: Ensures that only `/cmd/` is added to the `PATH` instead of `/mingw??/bin` and `/usr/bin/`, or not --[no-]needs-console:: Ensures that there is a Win32 console associated with the spawned process, or not --[no-]hide:: Hides the console window, or not Helped-by: Eli Young Signed-off-by: Johannes Schindelin --- compat/win32/git-wrapper.c | 42 ++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/compat/win32/git-wrapper.c b/compat/win32/git-wrapper.c index 603525eb7a..396a4eec24 100644 --- a/compat/win32/git-wrapper.c +++ b/compat/win32/git-wrapper.c @@ -271,7 +271,7 @@ static int configure_via_resource(LPWSTR basename, LPWSTR exepath, LPWSTR exep, int *is_git_command, LPWSTR *working_directory, int *full_path, int *skip_arguments, int *allocate_console, int *show_console) { - int id, minimal_search_path, needs_a_console, no_hide, wargc; + int i, id, minimal_search_path, needs_a_console, no_hide, wargc; LPWSTR *wargv; #define BUFSIZE 65536 @@ -333,15 +333,41 @@ static int configure_via_resource(LPWSTR basename, LPWSTR exepath, LPWSTR exep, *is_git_command = 0; *working_directory = (LPWSTR) 1; wargv = CommandLineToArgvW(GetCommandLine(), &wargc); - if (wargc > 1) { - if (!wcscmp(L"--no-cd", wargv[1])) { + for (i = 1; i < wargc; i++) { + if (!wcscmp(L"--no-cd", wargv[i])) *working_directory = NULL; - *skip_arguments = 1; - } - else if (!wcsncmp(L"--cd=", wargv[1], 5)) { - *working_directory = wcsdup(wargv[1] + 5); - *skip_arguments = 1; + else if (!wcsncmp(L"--cd=", wargv[i], 5)) + *working_directory = wcsdup(wargv[i] + 5); + else if (!wcscmp(L"--minimal-search-path", wargv[i])) + minimal_search_path = 1; + else if (!wcscmp(L"--no-minimal-search-path", wargv[i])) + minimal_search_path = 0; + else if (!wcscmp(L"--needs-console", wargv[i])) + needs_a_console = 1; + else if (!wcscmp(L"--no-needs-console", wargv[i])) + needs_a_console = 0; + else if (!wcscmp(L"--hide", wargv[i])) + no_hide = 0; + else if (!wcscmp(L"--no-hide", wargv[i])) + no_hide = 1; + else if (!wcsncmp(L"--command=", wargv[i], 10)) { + LPWSTR expanded; + + wargv[i] += 10; + expanded = expand_variables(wargv[i], wcslen(wargv[i])); + if (expanded == wargv[i]) + expanded = wcsdup(expanded); + + extract_first_arg(expanded, exepath, exep); + + *prefix_args = expanded; + *prefix_args_len = wcslen(*prefix_args); + *skip_arguments = i; + break; } + else + break; + *skip_arguments = i; } if (minimal_search_path) *full_path = 0; From f39ec4a0f11c32bf9e79ebec298334ab56c8c2c0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 19 Jun 2015 20:20:42 +0000 Subject: [PATCH 5/5] git-wrapper: leave the working directory alone by default The idea of `git-bash.exe` automatically running the Git Bash in the home directory was to support the start menu item `Git Bash` (which should not start in C:\Program Files\Git, but in $HOME), and to make that behavior consistent with double-clicking in `git-bash.exe` portable Git. However, it turns out that one of the main use cases of portable Git is to run the Git Bash in GitHub for Windows, and it should start in the top-level directory of a given project. Therefore, the concern to keep double-clicking `git-bash.exe` consistent with the start menu item was actually unfounded. As to the start menu item: it can easily be changed to launch `git-bash.exe` with a command-line option. So let's introduce the --cd-to-home option for that purpose. As a bonus, the Git wrapper can now also serve as a drop-in redirector /bin/bash.exe to provide backwards-compatibility of Git for Windows 2.x with 1.x: some 3rd-party software expects to find that executable there, and it also expects it to leave the working directory unchanged. Signed-off-by: Johannes Schindelin --- compat/win32/git-wrapper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compat/win32/git-wrapper.c b/compat/win32/git-wrapper.c index 396a4eec24..576d1af6b8 100644 --- a/compat/win32/git-wrapper.c +++ b/compat/win32/git-wrapper.c @@ -331,11 +331,12 @@ static int configure_via_resource(LPWSTR basename, LPWSTR exepath, LPWSTR exep, *prefix_args_len = wcslen(buf2); *is_git_command = 0; - *working_directory = (LPWSTR) 1; wargv = CommandLineToArgvW(GetCommandLine(), &wargc); for (i = 1; i < wargc; i++) { if (!wcscmp(L"--no-cd", wargv[i])) *working_directory = NULL; + else if (!wcscmp(L"--cd-to-home", wargv[i])) + *working_directory = (LPWSTR) 1; else if (!wcsncmp(L"--cd=", wargv[i], 5)) *working_directory = wcsdup(wargv[i] + 5); else if (!wcscmp(L"--minimal-search-path", wargv[i]))