From ae6fceff61edab2e97a3af6dcb2ded9a738c8bf0 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Mon, 23 Nov 2015 20:03:11 +0100
Subject: [PATCH 31/N] POSIX-ify the SHELL variable

When calling a non-MSys2 binary, all of the environment is converted from
POSIX to Win32, including the SHELL environment variable. In Git for
Windows, for example, `SHELL=/usr/bin/bash` is converted to
`SHELL=C:\Program Files\Git\usr\bin\bash.exe` when calling the `git.exe`
binary. This is appropriate because non-MSys2 binaries would not handle
POSIX paths correctly.

Under certain circumstances, however, `git.exe` calls an *MSys2* binary in
turn, such as `git config --edit` calling `vim.exe` unless Git is
configured to use another editor specifically.

Now, when this "improved vi" calls shell commands, it uses that $SHELL
variable *without quoting*, resulting in a nasty error:

	C:\Program: No such file or directory

Many other programs behave in the same manner, assuming that $SHELL does
not contain spaces and hence needs no quoting, unfortunately including
some of Git's own scripts.

Therefore let's make sure that $SHELL gets "posified" again when entering
MSys2 programs.

Earlier attempts by Git for Windows contributors claimed that adding
`SHELL` to the `conv_envvars` array does not have the intended effect.
These reports just missed that the `conv_start_chars` array (which makes
the code more performant) needs to be adjusted, too.

Note that we set the `immediate` flag to `true` so that the environment
variable is set immediately by the MSys2 runtime, i.e. not only spawned
processes will see the POSIX-ified `SHELL` variable, but the MSys2 runtime
*itself*, too.

This fixes https://github.com/git-for-windows/git/issues/542,
https://github.com/git-for-windows/git/issues/498, and
https://github.com/git-for-windows/git/issues/468.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 winsup/cygwin/environ.cc | 8 +++++++-
 winsup/cygwin/environ.h  | 2 +-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/winsup/cygwin/environ.cc b/winsup/cygwin/environ.cc
index 18c37ee..5c197a6 100644
--- a/winsup/cygwin/environ.cc
+++ b/winsup/cygwin/environ.cc
@@ -327,6 +327,7 @@ static win_env conv_envvars[] =
     {NL ("HOME="), NULL, NULL, env_path_to_posix, env_path_to_win32, false},
     {NL ("LD_LIBRARY_PATH="), NULL, NULL,
 			       env_plist_to_posix, env_plist_to_win32, true},
+    {NL ("SHELL="), NULL, NULL, env_path_to_posix, env_path_to_win32, true, true},
     {NL ("TMPDIR="), NULL, NULL, env_path_to_posix, env_path_to_win32, false},
     {NL ("TMP="), NULL, NULL, env_path_to_posix, env_path_to_win32, false},
     {NL ("TEMP="), NULL, NULL, env_path_to_posix, env_path_to_win32, false},
@@ -355,7 +356,7 @@ static const unsigned char conv_start_chars[256] =
     WC,       0,        0,        0,        WC,       0,        0,        0,
     /*  80 */
 /*  P         Q         R         S         T         U         V         W */
-    WC,       0,        0,        0,        WC,       0,        0,        0,
+    WC,       0,        0,        WC,       WC,       0,        0,        0,
     /*  88 */
 /*  x         Y         Z                                                   */
     0,        0,        0,        0,        0,        0,        0,        0,
@@ -384,6 +385,7 @@ win_env::operator = (struct win_env& x)
   toposix = x.toposix;
   towin32 = x.towin32;
   immediate = false;
+  skip_if_empty = x.skip_if_empty;
   return *this;
 }
 
@@ -405,6 +407,8 @@ win_env::add_cache (const char *in_posix, const char *in_native)
       native = (char *) realloc (native, namelen + 1 + strlen (in_native));
       stpcpy (stpcpy (native, name), in_native);
     }
+  else if (skip_if_empty && !*in_posix)
+    native = (char *) calloc(1, 1);
   else
     {
       tmp_pathbuf tp;
@@ -470,6 +474,8 @@ posify_maybe (char **here, const char *value, char *outenv)
     return;
 
   int len = strcspn (src, "=") + 1;
+  if (conv->skip_if_empty && !src[len])
+    return;
 
   /* Turn all the items from c:<foo>;<bar> into their
      mounted equivalents - if there is one.  */
diff --git a/winsup/cygwin/environ.h b/winsup/cygwin/environ.h
index 71c3f22..f33740d 100644
--- a/winsup/cygwin/environ.h
+++ b/winsup/cygwin/environ.h
@@ -21,7 +21,7 @@ struct win_env
     char *native;
     ssize_t (*toposix) (const void *, void *, size_t);
     ssize_t (*towin32) (const void *, void *, size_t);
-    bool immediate;
+    bool immediate, skip_if_empty;
     void __reg3 add_cache (const char *in_posix, const char *in_native = NULL);
     const char * get_native () const {return native ? native + namelen : NULL;}
     const char * get_posix () const {return posix ? posix : NULL;}
