From ec5f6cebb54ef1cf871b9e9ac9a99d29c0cbd65b 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 230cdee83a5c59361f688b6aa29630fd24068be1 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 f0191036f5c1da720b2ae6f6f89840c276b9bb12 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 e2a0ad02741854d723d5eb0b1f987102ee0de868 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 adf3a8cec73c658742bc1c0ad60fde2a4be3cfe8 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]))