From c9ab751d3d4cbaab082738d0c5cf96696b4f1f9b 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 8a315bdfff7363e04856d975040642cb44040000 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 f17d696b8d579bcdc78540fca06177e8bdc77d00 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 a9734ac2cddc8d1088b7cbefb685cb8ac06dd56a 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 e2ea974f211f2eeceebb977be5abd7cd547d586f 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]))