Skip to content

Commit

Permalink
mingw: special-case arguments to sh
Browse files Browse the repository at this point in the history
The MSYS2 runtime does its best to emulate the command-line wildcard
expansion and de-quoting which would be performed by the calling Unix
shell on Unix systems.

Those Unix shell quoting rules differ from the quoting rules applying to
Windows' cmd and Powershell, making it a little awkward to quote
command-line parameters properly when spawning other processes.

In particular, git.exe passes arguments to subprocesses that are *not*
intended to be interpreted as wildcards, and if they contain
backslashes, those are not to be interpreted as escape characters, e.g.
when passing Windows paths.

Note: this is only a problem when calling MSYS2 executables, not when
calling MINGW executables such as git.exe. However, we do call MSYS2
executables frequently, most notably when setting the use_shell flag in
the child_process structure.

There is no elegant way to determine whether the .exe file to be
executed is an MSYS2 program or a MINGW one. But since the use case of
passing a command line through the shell is so prevalent, we need to
work around this issue at least when executing sh.exe.

Let's introduce an ugly, hard-coded test whether argv[0] is "sh", and
whether it refers to the MSYS2 Bash, to determine whether we need to
quote the arguments differently than usual.

That still does not fix the issue completely, but at least it is
something.

Incidentally, this also fixes the problem where `git clone \\server\repo`
failed due to incorrect handling of the backslashes when handing the path
to the git-upload-pack process.

We need to take care to quote not only whitespace, but also curly
brackets. As aliases frequently go through the MSYS2 Bash, and
as aliases frequently get parameters such as HEAD@{yesterday}, let's
make sure that this does not regress by adding a test case for that.

Helped-by: Kim Gybels <[email protected]>
Signed-off-by: Johannes Schindelin <[email protected]>
  • Loading branch information
dscho authored and jamill committed Sep 5, 2018
1 parent 04afc33 commit 6b51ce6
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 2 deletions.
63 changes: 62 additions & 1 deletion compat/mingw.c
Original file line number Diff line number Diff line change
Expand Up @@ -1149,7 +1149,7 @@ char *mingw_getcwd(char *pointer, int len)
* See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx
* (Parsing C++ Command-Line Arguments)
*/
static const char *quote_arg(const char *arg)
static const char *quote_arg_msvc(const char *arg)
{
/* count chars to quote */
int len = 0, n = 0;
Expand Down Expand Up @@ -1204,6 +1204,37 @@ static const char *quote_arg(const char *arg)
return q;
}

#include "quote.h"

static const char *quote_arg_msys2(const char *arg)
{
struct strbuf buf = STRBUF_INIT;
const char *p2 = arg, *p;

for (p = arg; *p; p++) {
int ws = isspace(*p);
if (!ws && *p != '\\' && *p != '"' && *p != '{')
continue;
if (!buf.len)
strbuf_addch(&buf, '"');
if (p != p2)
strbuf_add(&buf, p2, p - p2);
if (!ws && *p != '{')
strbuf_addch(&buf, '\\');
p2 = p;
}

if (p == arg)
strbuf_addch(&buf, '"');
else if (!buf.len)
return arg;
else
strbuf_add(&buf, p2, p - p2),

strbuf_addch(&buf, '"');
return strbuf_detach(&buf, 0);
}

static const char *parse_interpreter(const char *cmd)
{
static char buf[100];
Expand Down Expand Up @@ -1514,6 +1545,34 @@ struct pinfo_t {
static struct pinfo_t *pinfo = NULL;
CRITICAL_SECTION pinfo_cs;

static int is_msys2_sh(const char *cmd)
{
if (cmd && !strcmp(cmd, "sh")) {
static int ret = -1;
char *p;

if (ret >= 0)
return ret;

p = path_lookup(cmd, 0);
if (!p)
ret = 0;
else {
size_t len = strlen(p);
ret = len > 15 &&
is_dir_sep(p[len - 15]) &&
!strncasecmp(p + len - 14, "usr", 3) &&
is_dir_sep(p[len - 11]) &&
!strncasecmp(p + len - 10, "bin", 3) &&
is_dir_sep(p[len - 7]) &&
!strcasecmp(p + len - 6, "sh.exe");
free(p);
}
return ret;
}
return 0;
}

static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv,
const char *dir,
int prepend_cmd, int fhin, int fhout, int fherr)
Expand All @@ -1526,6 +1585,8 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
BOOL ret;
HANDLE cons;
const char *strace_env;
const char *(*quote_arg)(const char *arg) =
is_msys2_sh(*argv) ? quote_arg_msys2 : quote_arg_msvc;

do_unset_environment_variables();

Expand Down
10 changes: 10 additions & 0 deletions t/t0061-run-command.sh
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,14 @@ test_expect_success 'GIT_TRACE with environment variables' '
)
'

test_expect_success MINGW 'verify curlies are quoted properly' '
: force the rev-parse through the MSYS2 Bash &&
git -c alias.r="!git rev-parse" r -- a{b}c >actual &&
cat >expect <<-\EOF &&
--
a{b}c
EOF
test_cmp expect actual
'

test_done
2 changes: 1 addition & 1 deletion t/t5580-clone-push-unc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ test_expect_success clone '
git clone "file://$UNCPATH" clone
'

test_expect_failure 'clone with backslashed path' '
test_expect_success 'clone with backslashed path' '
BACKSLASHED="$(echo "$UNCPATH" | tr / \\\\)" &&
git clone "$BACKSLASHED" backslashed
'
Expand Down

0 comments on commit 6b51ce6

Please sign in to comment.