From 85834517a224e8ec7971d710b330fe905852deb9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B8=CC=86=20=D0=9F?=
 =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=BE=D0=B2?= <alexey.pawlow@gmail.com>
Date: Sun, 14 Apr 2019 21:17:46 +0300
Subject: [PATCH 04/N] Add functionality for converting UNIX paths in
 arguments and environment variables to Windows form for native Win32
 applications.

---
 winsup/cygwin/Makefile.in          |   1 +
 winsup/cygwin/environ.cc           |  24 +-
 winsup/cygwin/environ.h            |   2 +-
 winsup/cygwin/external.cc          |   2 +-
 winsup/cygwin/include/sys/cygwin.h |   6 +
 winsup/cygwin/msys2_path_conv.cc   | 641 +++++++++++++++++++++++++++++
 winsup/cygwin/msys2_path_conv.h    | 147 +++++++
 winsup/cygwin/path.cc              |  69 ++++
 winsup/cygwin/spawn.cc             |  42 +-
 winsup/cygwin/winf.h               |   4 +
 10 files changed, 932 insertions(+), 6 deletions(-)
 create mode 100644 winsup/cygwin/msys2_path_conv.cc
 create mode 100644 winsup/cygwin/msys2_path_conv.h

diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
index 4c51cf3..1b67c9e 100644
--- a/winsup/cygwin/Makefile.in
+++ b/winsup/cygwin/Makefile.in
@@ -347,6 +347,7 @@ DLL_OFILES:= \
 	mmap.o \
 	mmap_alloc.o \
 	msg.o \
+	msys2_path_conv.o \
 	mount.o \
 	net.o \
 	netdb.o \
diff --git a/winsup/cygwin/environ.cc b/winsup/cygwin/environ.cc
index 3a03657..73df083 100644
--- a/winsup/cygwin/environ.cc
+++ b/winsup/cygwin/environ.cc
@@ -1056,7 +1056,7 @@ env_compare (const void *key, const void *memb)
    to the child. */
 char ** __reg3
 build_env (const char * const *envp, PWCHAR &envblock, int &envc,
-	   bool no_envblock, HANDLE new_token)
+	   bool no_envblock, HANDLE new_token, bool keep_posix)
 {
   PWCHAR cwinenv = NULL;
   size_t winnum = 0;
@@ -1149,6 +1149,19 @@ build_env (const char * const *envp, PWCHAR &envblock, int &envc,
   for (srcp = envp, dstp = newenv, pass_dstp = pass_env; *srcp; srcp++)
     {
       bool calc_tl = !no_envblock;
+#ifdef __MSYS__
+      /* Don't pass timezone environment to non-msys applications */
+      if (!keep_posix && ascii_strncasematch(*srcp, "TZ=", 3))
+        {
+          const char *v = *srcp + 3;
+          if (*v == ':')
+            goto next1;
+          for (; *v; v++)
+            if (!isalpha(*v) && !isdigit(*v) &&
+                *v != '-' && *v != '+' && *v != ':')
+              goto next1;
+        }
+#endif
       /* Look for entries that require special attention */
       for (unsigned i = 0; i < SPENVS_SIZE; i++)
 	if (!saw_spenv[i] && (*dstp = spenvs[i].retrieve (no_envblock, *srcp)))
@@ -1269,6 +1282,15 @@ build_env (const char * const *envp, PWCHAR &envblock, int &envc,
 		  saw_PATH = true;
 		}
 	    }
+#ifdef __MSYS__
+	  else if (!keep_posix) {
+	    char *win_arg = arg_heuristic(*srcp);
+	    debug_printf("WIN32_PATH is %s", win_arg);
+	    p = cstrdup1(win_arg);
+	    if (win_arg != *srcp)
+	      free (win_arg);
+	  }
+#endif
 	  else
 	    p = *srcp;		/* Don't worry about it */
 
diff --git a/winsup/cygwin/environ.h b/winsup/cygwin/environ.h
index 269591a..71c3f22 100644
--- a/winsup/cygwin/environ.h
+++ b/winsup/cygwin/environ.h
@@ -43,7 +43,7 @@ extern "C" char **__cygwin_environ, ***main_environ;
 extern "C" char __stdcall **cur_environ ();
 #endif
 char ** __reg3 build_env (const char * const *envp, PWCHAR &envblock,
-			  int &envc, bool need_envblock, HANDLE new_token);
+			  int &envc, bool need_envblock, HANDLE new_token, bool keep_posix);
 
 char ** __reg2 win32env_to_cygenv (PWCHAR rawenv, bool posify);
 
diff --git a/winsup/cygwin/external.cc b/winsup/cygwin/external.cc
index 74413a8..6adf620 100644
--- a/winsup/cygwin/external.cc
+++ b/winsup/cygwin/external.cc
@@ -141,7 +141,7 @@ create_winenv (const char * const *env)
   int unused_envc;
   PWCHAR envblock = NULL;
   char **envp = build_env (env ?: cur_environ (), envblock, unused_envc, false,
-			   NULL);
+			   NULL, true);
   PWCHAR p = envblock;
 
   if (envp)
diff --git a/winsup/cygwin/include/sys/cygwin.h b/winsup/cygwin/include/sys/cygwin.h
index 805671e..b395e96 100644
--- a/winsup/cygwin/include/sys/cygwin.h
+++ b/winsup/cygwin/include/sys/cygwin.h
@@ -83,6 +83,12 @@ extern ssize_t cygwin_conv_path_list (cygwin_conv_path_t what, const void *from,
    to one of the above values, or to ENOMEM if malloc fails. */
 extern void *cygwin_create_path (cygwin_conv_path_t what, const void *from);
 
+extern char * arg_heuristic_with_exclusions (char const * const arg,
+					     char const * exclusions,
+					     size_t exclusions_count);
+
+extern char * arg_heuristic (char const * const);
+
 extern pid_t cygwin_winpid_to_pid (int);
 extern int cygwin_posix_path_list_p (const char *);
 extern void cygwin_split_path (const char *, char *, char *);
diff --git a/winsup/cygwin/msys2_path_conv.cc b/winsup/cygwin/msys2_path_conv.cc
new file mode 100644
index 0000000..f9dcda5
--- /dev/null
+++ b/winsup/cygwin/msys2_path_conv.cc
@@ -0,0 +1,641 @@
+/*
+  The MSYS2 Path conversion source code is licensed under:
+  
+  CC0 1.0 Universal
+  
+  Official translations of this legal tool are available
+  
+  CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+  LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+  ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+  INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+  REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+  PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+  THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+  HEREUNDER.
+  
+  Statement of Purpose
+  
+  The laws of most jurisdictions throughout the world automatically
+  confer exclusive Copyright and Related Rights (defined below) upon the
+  creator and subsequent owner(s) (each and all, an "owner") of an
+  original work of authorship and/or a database (each, a "Work").
+  
+  Certain owners wish to permanently relinquish those rights to a Work
+  for the purpose of contributing to a commons of creative, cultural and
+  scientific works ("Commons") that the public can reliably and without
+  fear of later claims of infringement build upon, modify, incorporate
+  in other works, reuse and redistribute as freely as possible in any
+  form whatsoever and for any purposes, including without limitation
+  commercial purposes. These owners may contribute to the Commons to
+  promote the ideal of a free culture and the further production of
+  creative, cultural and scientific works, or to gain reputation or
+  greater distribution for their Work in part through the use and
+  efforts of others.
+  
+  For these and/or other purposes and motivations, and without any
+  expectation of additional consideration or compensation, the person
+  associating CC0 with a Work (the "Affirmer"), to the extent that he or
+  she is an owner of Copyright and Related Rights in the Work,
+  voluntarily elects to apply CC0 to the Work and publicly distribute
+  the Work under its terms, with knowledge of his or her Copyright and
+  Related Rights in the Work and the meaning and intended legal effect
+  of CC0 on those rights.
+  
+  1. Copyright and Related Rights. A Work made available under CC0 may
+  be protected by copyright and related or neighboring rights
+  ("Copyright and Related Rights"). Copyright and Related Rights
+  include, but are not limited to, the following:
+  
+  the right to reproduce, adapt, distribute, perform, display,
+  communicate, and translate a Work;
+  moral rights retained by the original author(s) and/or performer(s);
+  publicity and privacy rights pertaining to a person's image or
+  likeness depicted in a Work;
+  rights protecting against unfair competition in regards to a Work,
+  subject to the limitations in paragraph 4(a), below;
+  rights protecting the extraction, dissemination, use and reuse of data
+  in a Work;
+  database rights (such as those arising under Directive 96/9/EC of the
+  European Parliament and of the Council of 11 March 1996 on the legal
+  protection of databases, and under any national implementation
+  thereof, including any amended or successor version of such
+  directive); and
+  other similar, equivalent or corresponding rights throughout the world
+  based on applicable law or treaty, and any national implementations
+  thereof.
+  
+  2. Waiver. To the greatest extent permitted by, but not in
+  contravention of, applicable law, Affirmer hereby overtly, fully,
+  permanently, irrevocably and unconditionally waives, abandons, and
+  surrenders all of Affirmer's Copyright and Related Rights and
+  associated claims and causes of action, whether now known or unknown
+  (including existing as well as future claims and causes of action), in
+  the Work (i) in all territories worldwide, (ii) for the maximum
+  duration provided by applicable law or treaty (including future time
+  extensions), (iii) in any current or future medium and for any number
+  of copies, and (iv) for any purpose whatsoever, including without
+  limitation commercial, advertising or promotional purposes (the
+  "Waiver"). Affirmer makes the Waiver for the benefit of each member of
+  the public at large and to the detriment of Affirmer's heirs and
+  successors, fully intending that such Waiver shall not be subject to
+  revocation, rescission, cancellation, termination, or any other legal
+  or equitable action to disrupt the quiet enjoyment of the Work by the
+  public as contemplated by Affirmer's express Statement of Purpose.
+  
+  3. Public License Fallback. Should any part of the Waiver for any
+  reason be judged legally invalid or ineffective under applicable law,
+  then the Waiver shall be preserved to the maximum extent permitted
+  taking into account Affirmer's express Statement of Purpose. In
+  addition, to the extent the Waiver is so judged Affirmer hereby grants
+  to each affected person a royalty-free, non transferable, non
+  sublicensable, non exclusive, irrevocable and unconditional license to
+  exercise Affirmer's Copyright and Related Rights in the Work (i) in
+  all territories worldwide, (ii) for the maximum duration provided by
+  applicable law or treaty (including future time extensions), (iii) in
+  any current or future medium and for any number of copies, and (iv)
+  for any purpose whatsoever, including without limitation commercial,
+  advertising or promotional purposes (the "License"). The License shall
+  be deemed effective as of the date CC0 was applied by Affirmer to the
+  Work. Should any part of the License for any reason be judged legally
+  invalid or ineffective under applicable law, such partial invalidity
+  or ineffectiveness shall not invalidate the remainder of the License,
+  and in such case Affirmer hereby affirms that he or she will not (i)
+  exercise any of his or her remaining Copyright and Related Rights in
+  the Work or (ii) assert any associated claims and causes of action
+  with respect to the Work, in either case contrary to Affirmer's
+  express Statement of Purpose.
+  
+  4. Limitations and Disclaimers.
+  
+  No trademark or patent rights held by Affirmer are waived, abandoned,
+  surrendered, licensed or otherwise affected by this document.
+  Affirmer offers the Work as-is and makes no representations or
+  warranties of any kind concerning the Work, express, implied,
+  statutory or otherwise, including without limitation warranties of
+  title, merchantability, fitness for a particular purpose, non
+  infringement, or the absence of latent or other defects, accuracy, or
+  the present or absence of errors, whether or not discoverable, all to
+  the greatest extent permissible under applicable law.
+  Affirmer disclaims responsibility for clearing rights of other persons
+  that may apply to the Work or any use thereof, including without
+  limitation any person's Copyright and Related Rights in the Work.
+  Further, Affirmer disclaims responsibility for obtaining any necessary
+  consents, permissions or other rights required for any use of the
+  Work.
+  Affirmer understands and acknowledges that Creative Commons is not a
+  party to this document and has no duty or obligation with respect to
+  this CC0 or use of the Work.
+
+  Contributions thanks to:
+    niXman <i.nixman@autistici.org>
+    Ely Arzhannikov <iarzhannikov@gmail.com>
+    Alexey Pavlov <alexpux@gmail.com>
+    Ray Donnelly <mingw.android@gmail.com>
+    Johannes Schindelin <johannes.schindelin@gmx.de>
+  
+*/
+
+#include "winsup.h"
+#include "miscfuncs.h"
+#include <ctype.h>
+#include <winioctl.h>
+#include <shlobj.h>
+#include <sys/param.h>
+#include <sys/cygwin.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "shared_info.h"
+#include "cygtls.h"
+#include "tls_pbuf.h"
+#include "environ.h"
+#include <assert.h>
+#include <ntdll.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "msys2_path_conv.h"
+
+typedef enum PATH_TYPE_E {
+    NONE = 0,
+    SIMPLE_WINDOWS_PATH,
+    ESCAPE_WINDOWS_PATH,
+    WINDOWS_PATH_LIST,
+    UNC,
+    ESCAPED_PATH,
+    ROOTED_PATH,
+    POSIX_PATH_LIST,
+    RELATIVE_PATH,
+    URL
+} path_type;
+
+int is_special_posix_path(const char* from, const char* to, char** dst, const char* dstend);
+void posix_to_win32_path(const char* from, const char* to, char** dst, const char* dstend);
+
+
+path_type find_path_start_and_type(const char** src, int recurse, const char* end);
+void copy_to_dst(const char* from, const char* to, char** dst, const char* dstend);
+void convert_path(const char** from, const char* to, path_type type, char** dst, const char* dstend);
+
+//Transformations
+//SIMPLE_WINDOWS_PATH converter. Copy as is. Hold C:\Something\like\this
+void swp_convert(const char** from, const char* to, char** dst, const char* dstend);
+//ESCAPE_WINDOWS_PATH converter. Turn backslashes to slashes and skip first /. Hold /C:\Somethind\like\this
+void ewp_convert(const char** from, const char* to, char** dst, const char* dstend);
+//WINDOWS_PATH_LIST converter. Copy as is. Hold /something/like/this;
+void wpl_convert(const char** from, const char* to, char** dst, const char* dstend);
+//UNC convert converter. Copy as is. Hold //somethig/like/this
+void unc_convert(const char** from, const char* to, char** dst, const char* dstend);
+//ESCAPED_PATH converter. Turn backslashes to slashes and skip first /. Hold //something\like\this
+void ep_convert(const char** from, const char* to, char** dst, const char* dstend);
+//ROOTED_PATH converter. Prepend root dir to front. Hold /something/like/this
+void rp_convert(const char** from, const char* to, char** dst, const char* dstend);
+//URL converter. Copy as is.
+void url_convert(const char** from, const char* to, char** dst, const char* dstend);
+//POSIX_PATH_LIST. Hold x::x/y:z
+void ppl_convert(const char** from, const char* to, char** dst, const char* dstend);
+
+
+void find_end_of_posix_list(const char** to, int* in_string) {
+    for (; **to != '\0' && (in_string ? (**to != *in_string) : **to != ' '); ++*to) {
+    }
+
+    if (**to == *in_string) {
+        *in_string = 0;
+    }
+}
+
+void find_end_of_rooted_path(const char** from, const char** to, int* in_string) {
+    for (const char* it = *from; *it != '\0' && it != *to; ++it)
+        if (*it == '.' && *(it + 1) == '.' && *(it - 1) == '/') {
+            *to = it - 1;
+            return;
+        }
+
+    for (; **to != '\0'; ++*to) {
+        if (*in_string == 0 && **to == ' ') {
+            return;
+        }
+
+        if (**to == *in_string) {
+            *in_string = 0;
+            return;
+        }
+
+        if (**to == '/') {
+            if (*(*to - 1) == ' ') {
+                *to -= 1;
+                return;
+            }
+        }
+    }
+}
+
+void sub_convert(const char** from, const char** to, char** dst, const char* dstend, int* in_string) {
+    const char* copy_from = *from;
+    path_type type = find_path_start_and_type(from, false, *to);
+
+    if (type == POSIX_PATH_LIST) {
+        find_end_of_posix_list(to, in_string);
+    }
+
+    if (type == ROOTED_PATH) {
+        find_end_of_rooted_path(from, to, in_string);
+    }
+
+    copy_to_dst(copy_from, *from, dst, dstend);
+
+    if (type != NONE) {
+        convert_path(from, *to, type, dst, dstend);
+    }
+
+    if (*dst != dstend) {
+        **dst = **to;
+        *dst += 1;
+    }
+}
+
+const char* convert(char *dst, size_t dstlen, const char *src) {
+    if (dst == NULL || dstlen == 0 || src == NULL) {
+        return dst;
+    }
+
+    int need_convert = false;
+    for (const char* it = src; *it != '\0'; ++it) {
+        if (*it == '\\' || *it == '/') {
+            need_convert = true;
+            break;
+        }
+        if (isspace(*it)) {
+            need_convert = false;
+            break;
+        }
+    }
+
+    char* dstit = dst;
+    char* dstend = dst + dstlen;
+    if (!need_convert) {
+        copy_to_dst(src, NULL, &dstit, dstend);
+        *dstit = '\0';
+        return dst;
+    }
+    *dstend = '\0';
+
+    const char* srcit = src;
+    const char* srcbeg = src;
+
+    int in_string = false;
+
+    for (; *srcit != '\0'; ++srcit) {
+        if (*srcit == '\'' || *srcit == '"') {
+            if (in_string == *srcit) {
+                if (*(srcit + 1) != in_string) {
+                    in_string = 0;
+                }
+            } else {
+                in_string = *srcit;
+            }
+            continue;
+        }
+
+        if (isspace(*srcit)) {
+            //sub_convert(&srcbeg, &srcit, &dstit, dstend, &in_string);
+            //srcbeg = srcit + 1;
+            break;
+        }
+    }
+
+    sub_convert(&srcbeg, &srcit, &dstit, dstend, &in_string);
+    if (!*srcit) {
+      *dstit = '\0';
+      return dst;
+    }
+    srcbeg = srcit + 1;
+    for (; *srcit != '\0'; ++srcit) {
+      continue;
+    }
+    copy_to_dst(srcbeg, srcit, &dstit, dstend);
+    *dstit = '\0';
+
+    /*if (dstit - dst < 2) {
+        dstit = dst;
+        copy_to_dst(src, NULL, &dstit, dstend);
+        *dstit = '\0';
+    }*/
+
+    return dst;
+}
+
+void copy_to_dst(const char* from, const char* to, char** dst, const char* dstend) {
+    for (; (*from != '\0') && (from != to) && (*dst != dstend); ++from, ++(*dst)) {
+        **dst = *from;
+    }
+}
+
+const char** move(const char** p, int count) {
+    *p += count;
+    return p;
+}
+
+path_type find_path_start_and_type(const char** src, int recurse, const char* end) {
+    const char* it = *src;
+
+    if (*it == '\0' || it == end) return NONE;
+
+    if (!isalnum(*it) && *it != '/' && *it != '\\' && *it != ':' && *it != '-' && *it != '.') {
+        return find_path_start_and_type(move(src, 1), true, end);
+    }
+
+    path_type result = NONE;
+
+    if (isalpha(*it) && *(it + 1) == ':') {
+        if (*(it + 2) == '\\') {
+            return SIMPLE_WINDOWS_PATH;
+        }
+
+        if (*(it + 2) == '/' && memchr(it + 2, ':', end - (it + 2)) == NULL) {
+            return SIMPLE_WINDOWS_PATH;
+        }
+
+        if (*(it + 2) == '/' && memchr(it + 2, ';', end - (it + 2))) {
+            return WINDOWS_PATH_LIST;
+        }
+    }
+
+    if (*it == '.' && (*(it + 1) == '.' || *(it + 1) == '/') && memchr(it + 2, ':', end - (it + 2)) == NULL) {
+        return RELATIVE_PATH;
+    }
+
+    if (*it == '/') {
+        it += 1;
+
+        if (isalpha(*it) && *(it + 1) == ':') {
+            return ESCAPE_WINDOWS_PATH;
+        }
+
+        if (*it == '.' && *(it + 1) == '.') {
+            return SIMPLE_WINDOWS_PATH;
+        }
+
+        if (*it == '/') {
+            it += 1;
+            switch(*it) {
+              case ':': return URL;
+              case '/': return ESCAPED_PATH;
+            }
+            if (memchr(it, '/', end - it))
+              return UNC;
+            else
+              return ESCAPED_PATH;
+        }
+
+        for (; *it != '\0' && it != end; ++it) {
+            switch(*it) {
+                case ':': {char ch = *(it + 1); if (ch == '/' || ch == ':' || ch == '.') return POSIX_PATH_LIST;} return WINDOWS_PATH_LIST;
+                case ';': return WINDOWS_PATH_LIST;
+            }
+        }
+
+        if (result != NONE) {
+            return result;
+        }
+
+        return ROOTED_PATH;
+    }
+
+    int starts_with_minus = 0;
+    int starts_with_minus_alpha = 0;
+    if (*it == '-') {
+      starts_with_minus = 1;
+      it += 1;
+      if (isalpha(*it)) {
+        it += 1;
+        starts_with_minus_alpha = 1;
+        if (memchr(it, ';', end - it)) {
+        	return WINDOWS_PATH_LIST;
+        }
+      }
+    }
+
+    for (const char* it2 = it; *it2 != '\0' && it2 != end; ++it2) {
+        char ch = *it2;
+        if (starts_with_minus_alpha) {
+            if (isalpha(ch) && (*(it2+1) == ':') && (*(it2+2) == '/')) {
+                return SIMPLE_WINDOWS_PATH;
+            }
+            if (ch == '/'&& memchr(it2, ',', end - it) == NULL) {
+                *src = it2;
+                return find_path_start_and_type(src, true, end);
+            }
+            starts_with_minus_alpha = 0;
+        }
+        if (ch == '\'' || ch == '"')
+            starts_with_minus = false;
+        if ((ch == '=') || (ch == ':' && starts_with_minus) || ((ch == '\'' || ch == '"') && result == NONE)) {
+            *src = it2 + 1;
+            return find_path_start_and_type(src, true, end);
+        }
+
+        if (ch == ',' && starts_with_minus) {
+            *src = it2 + 1;
+            return find_path_start_and_type(src, true, end);
+        }
+
+        if (ch == ':' && it2 + 1 != end) {
+            it2 += 1;
+            ch = *it2;
+            if (ch == '/' || ch == ':' || ch == '.') {
+                if (ch == '/' && *(it2 + 1) == '/') {
+                    return URL;
+                } else {
+                    return POSIX_PATH_LIST;
+                }
+            } else if (memchr(it2, '=', end - it) == NULL) {
+                return SIMPLE_WINDOWS_PATH;
+            }
+        }
+    }
+
+    if (result != NONE) {
+        *src = it;
+        return result;
+    }
+
+    return SIMPLE_WINDOWS_PATH;
+}
+
+void convert_path(const char** from, const char* to, path_type type, char** dst, const char* dstend) {
+    switch(type) {
+        case SIMPLE_WINDOWS_PATH: swp_convert(from, to, dst, dstend); break;
+        case ESCAPE_WINDOWS_PATH: ewp_convert(from, to, dst, dstend); break;
+        case WINDOWS_PATH_LIST: wpl_convert(from, to, dst, dstend); break;
+        case RELATIVE_PATH: swp_convert(from, to, dst, dstend); break;
+        case UNC: unc_convert(from, to, dst, dstend); break;
+        case ESCAPED_PATH: ep_convert(from, to, dst, dstend); break;
+        case ROOTED_PATH: rp_convert(from, to, dst, dstend); break;
+        case URL: url_convert(from, to, dst, dstend); break;
+        case POSIX_PATH_LIST: ppl_convert(from, to, dst, dstend); break;
+        case NONE: // prevent warnings;
+        default:
+                return;
+    }
+}
+
+void swp_convert(const char** from, const char* to, char** dst, const char* dstend) {
+    copy_to_dst(*from, to, dst, dstend);
+}
+
+void ewp_convert(const char** from, const char* to, char** dst, const char* dstend) {
+    *from += 1;
+    unc_convert(from, to, dst, dstend);
+}
+
+void wpl_convert(const char** from, const char* to, char** dst, const char* dstend) {
+    swp_convert(from, to, dst, dstend);
+}
+
+void unc_convert(const char** from, const char* to, char** dst, const char* dstend) {
+    const char* it = *from;
+    for (; (*it != '\0' && it != to) && (*dst != dstend); ++it, ++(*dst)) {
+        if (*it == '\\') {
+            **dst = '/';
+        } else {
+            **dst = *it;
+        }
+    }
+}
+
+void ep_convert(const char** from, const char* to, char** dst, const char* dstend) {
+    ewp_convert(from, to, dst, dstend);
+}
+
+void rp_convert(const char** from, const char* to, char** dst, const char* dstend) {
+    const char* it = *from;
+    const char* real_to = to;
+
+    if (*real_to == '\0')  {
+        real_to -= 1;
+        if (*real_to != '\'' && *real_to != '"') {
+            real_to += 1;
+        }
+    }
+
+    if (!is_special_posix_path(*from, real_to, dst, dstend)) {
+        posix_to_win32_path(it, real_to, dst, dstend);
+    }
+
+    if (*dst != dstend && real_to != to) {
+        **dst = *real_to;
+        *dst += 1;
+    }
+}
+
+void url_convert(const char** from, const char* to, char** dst, const char* dstend) {
+    unc_convert(from, to, dst, dstend);
+}
+
+void subp_convert(const char** from, const char* end, int is_url, char** dst, const char* dstend) {
+    const char* begin = *from;
+    path_type type = is_url ? URL : find_path_start_and_type(from, 0, end);
+    copy_to_dst(begin, *from, dst, dstend);
+
+    if (type == NONE) {
+        return;
+    }
+
+    char* start = *dst;
+    convert_path(from, end, type, dst, dstend);
+
+    if (!is_url) {
+        for (; start != *dst; ++start) {
+            if (*start == '/') {
+                *start = '\\';
+            }
+        }
+    }
+}
+
+void ppl_convert(const char** from, const char* to, char** dst, const char* dstend) {
+    const char *orig_dst = *dst;
+    const char* it = *from;
+    const char* beg = it;
+    int prev_was_simc = 0;
+    int is_url = 0;
+    for (; (*it != '\0' && it != to) && (*dst != dstend); ++it) {
+        if (*it == ':') {
+            if (prev_was_simc) {
+                continue;
+            }
+            if (*(it + 1) == '/' && *(it + 2) == '/' && isalpha(*beg)) {
+                is_url = 1;
+                /* double-check: protocol must be alnum (or +) */
+                for (const char *p = beg; p != it; ++p)
+                    if (!isalnum(*p) && *p != '+') {
+                        is_url = 0;
+                        break;
+                    }
+                if (is_url)
+                    continue;
+            }
+            prev_was_simc = 1;
+            subp_convert(&beg, it, is_url, dst, dstend);
+            is_url = 0;
+
+            if (*dst == dstend) {
+                system_printf("Path cut off during conversion: %s\n", orig_dst);
+                break;
+            }
+
+            **dst = ';';
+            *dst += 1;
+        }
+
+        if (*it != ':' && prev_was_simc) {
+            prev_was_simc = 0;
+            beg = it;
+        }
+    }
+
+    if (!prev_was_simc) {
+        subp_convert(&beg, it, is_url, dst, dstend);
+    } else {
+        *dst -= 1;
+    }
+}
+
+int is_special_posix_path(const char* from, const char* to, char** dst, const char* dstend) {
+    const char dev_null[] = "/dev/null";
+
+    if ((to - from) == (sizeof(dev_null) - 1) && strncmp(from, "/dev/null", to - from) == 0) {
+        copy_to_dst("nul", NULL, dst, dstend);
+        return true;
+    }
+    return false;
+}
+
+void posix_to_win32_path(const char* from, const char* to, char** dst, const char* dstend) {
+    if ( from != to ) {
+        tmp_pathbuf tp;
+        char *one_path = tp.c_get();
+        strncpy(one_path, from, to-from);
+        one_path[to-from] = '\0';
+
+        path_conv conv (one_path, 0);
+        if (conv.error)
+        {
+          set_errno(conv.error);
+          copy_to_dst(one_path, NULL, dst, dstend);
+        } else {
+          char* win32_path = tp.c_get();
+          stpcpy (win32_path, conv.get_win32 ());
+          for (; (*win32_path != '\0') && (*dst != dstend); ++win32_path, ++(*dst)) {
+             **dst = (*win32_path == '\\') ? '/' : *win32_path;
+          }
+        }
+    }
+}
+
diff --git a/winsup/cygwin/msys2_path_conv.h b/winsup/cygwin/msys2_path_conv.h
new file mode 100644
index 0000000..67d85ec
--- /dev/null
+++ b/winsup/cygwin/msys2_path_conv.h
@@ -0,0 +1,147 @@
+/*
+  The MSYS2 Path conversion source code is licensed under:
+  
+  CC0 1.0 Universal
+  
+  Official translations of this legal tool are available
+  
+  CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+  LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+  ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+  INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+  REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+  PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+  THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+  HEREUNDER.
+  
+  Statement of Purpose
+  
+  The laws of most jurisdictions throughout the world automatically
+  confer exclusive Copyright and Related Rights (defined below) upon the
+  creator and subsequent owner(s) (each and all, an "owner") of an
+  original work of authorship and/or a database (each, a "Work").
+  
+  Certain owners wish to permanently relinquish those rights to a Work
+  for the purpose of contributing to a commons of creative, cultural and
+  scientific works ("Commons") that the public can reliably and without
+  fear of later claims of infringement build upon, modify, incorporate
+  in other works, reuse and redistribute as freely as possible in any
+  form whatsoever and for any purposes, including without limitation
+  commercial purposes. These owners may contribute to the Commons to
+  promote the ideal of a free culture and the further production of
+  creative, cultural and scientific works, or to gain reputation or
+  greater distribution for their Work in part through the use and
+  efforts of others.
+  
+  For these and/or other purposes and motivations, and without any
+  expectation of additional consideration or compensation, the person
+  associating CC0 with a Work (the "Affirmer"), to the extent that he or
+  she is an owner of Copyright and Related Rights in the Work,
+  voluntarily elects to apply CC0 to the Work and publicly distribute
+  the Work under its terms, with knowledge of his or her Copyright and
+  Related Rights in the Work and the meaning and intended legal effect
+  of CC0 on those rights.
+  
+  1. Copyright and Related Rights. A Work made available under CC0 may
+  be protected by copyright and related or neighboring rights
+  ("Copyright and Related Rights"). Copyright and Related Rights
+  include, but are not limited to, the following:
+  
+  the right to reproduce, adapt, distribute, perform, display,
+  communicate, and translate a Work;
+  moral rights retained by the original author(s) and/or performer(s);
+  publicity and privacy rights pertaining to a person's image or
+  likeness depicted in a Work;
+  rights protecting against unfair competition in regards to a Work,
+  subject to the limitations in paragraph 4(a), below;
+  rights protecting the extraction, dissemination, use and reuse of data
+  in a Work;
+  database rights (such as those arising under Directive 96/9/EC of the
+  European Parliament and of the Council of 11 March 1996 on the legal
+  protection of databases, and under any national implementation
+  thereof, including any amended or successor version of such
+  directive); and
+  other similar, equivalent or corresponding rights throughout the world
+  based on applicable law or treaty, and any national implementations
+  thereof.
+  
+  2. Waiver. To the greatest extent permitted by, but not in
+  contravention of, applicable law, Affirmer hereby overtly, fully,
+  permanently, irrevocably and unconditionally waives, abandons, and
+  surrenders all of Affirmer's Copyright and Related Rights and
+  associated claims and causes of action, whether now known or unknown
+  (including existing as well as future claims and causes of action), in
+  the Work (i) in all territories worldwide, (ii) for the maximum
+  duration provided by applicable law or treaty (including future time
+  extensions), (iii) in any current or future medium and for any number
+  of copies, and (iv) for any purpose whatsoever, including without
+  limitation commercial, advertising or promotional purposes (the
+  "Waiver"). Affirmer makes the Waiver for the benefit of each member of
+  the public at large and to the detriment of Affirmer's heirs and
+  successors, fully intending that such Waiver shall not be subject to
+  revocation, rescission, cancellation, termination, or any other legal
+  or equitable action to disrupt the quiet enjoyment of the Work by the
+  public as contemplated by Affirmer's express Statement of Purpose.
+  
+  3. Public License Fallback. Should any part of the Waiver for any
+  reason be judged legally invalid or ineffective under applicable law,
+  then the Waiver shall be preserved to the maximum extent permitted
+  taking into account Affirmer's express Statement of Purpose. In
+  addition, to the extent the Waiver is so judged Affirmer hereby grants
+  to each affected person a royalty-free, non transferable, non
+  sublicensable, non exclusive, irrevocable and unconditional license to
+  exercise Affirmer's Copyright and Related Rights in the Work (i) in
+  all territories worldwide, (ii) for the maximum duration provided by
+  applicable law or treaty (including future time extensions), (iii) in
+  any current or future medium and for any number of copies, and (iv)
+  for any purpose whatsoever, including without limitation commercial,
+  advertising or promotional purposes (the "License"). The License shall
+  be deemed effective as of the date CC0 was applied by Affirmer to the
+  Work. Should any part of the License for any reason be judged legally
+  invalid or ineffective under applicable law, such partial invalidity
+  or ineffectiveness shall not invalidate the remainder of the License,
+  and in such case Affirmer hereby affirms that he or she will not (i)
+  exercise any of his or her remaining Copyright and Related Rights in
+  the Work or (ii) assert any associated claims and causes of action
+  with respect to the Work, in either case contrary to Affirmer's
+  express Statement of Purpose.
+  
+  4. Limitations and Disclaimers.
+  
+  No trademark or patent rights held by Affirmer are waived, abandoned,
+  surrendered, licensed or otherwise affected by this document.
+  Affirmer offers the Work as-is and makes no representations or
+  warranties of any kind concerning the Work, express, implied,
+  statutory or otherwise, including without limitation warranties of
+  title, merchantability, fitness for a particular purpose, non
+  infringement, or the absence of latent or other defects, accuracy, or
+  the present or absence of errors, whether or not discoverable, all to
+  the greatest extent permissible under applicable law.
+  Affirmer disclaims responsibility for clearing rights of other persons
+  that may apply to the Work or any use thereof, including without
+  limitation any person's Copyright and Related Rights in the Work.
+  Further, Affirmer disclaims responsibility for obtaining any necessary
+  consents, permissions or other rights required for any use of the
+  Work.
+  Affirmer understands and acknowledges that Creative Commons is not a
+  party to this document and has no duty or obligation with respect to
+  this CC0 or use of the Work.
+
+  Contributions thanks to:
+    niXman <i.nixman@autistici.org>
+    Ely Arzhannikov <iarzhannikov@gmail.com>
+    Alexey Pavlov <alexpux@gmail.com>
+    Ray Donnelly <mingw.android@gmail.com>
+    Johannes Schindelin <johannes.schindelin@gmx.de>
+  
+*/
+
+#ifndef PATH_CONV_H_DB4IQBH3
+#define PATH_CONV_H_DB4IQBH3
+
+#include <stdlib.h>
+
+const char* convert(char *dst, size_t dstlen, const char *src);
+
+#endif /* end of include guard: PATH_CONV_H_DB4IQBH3 */
+
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index 1d6bcbe..4db7b24 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -66,6 +66,7 @@
 #include "shared_info.h"
 #include "tls_pbuf.h"
 #include "environ.h"
+#include "msys2_path_conv.h"
 #undef basename
 
 suffix_info stat_suffixes[] =
@@ -3676,6 +3677,74 @@ fchdir (int fd)
   return res;
 }
 
+//
+// Important: If returned pointer == arg, then this function
+//            did not malloc that pointer; otherwise free it.
+//
+extern "C" char *
+arg_heuristic_with_exclusions (char const * const arg, char const * exclusions, size_t exclusions_count)
+{
+  char *arg_result;
+
+  // Must return something ..
+  size_t arglen = (arg ? strlen (arg): 0);
+  
+  if (arglen == 0 || !arg)
+  {
+    arg_result = (char *)malloc (sizeof (char));
+    arg_result[0] = '\0';
+    return arg_result;
+  }
+
+  debug_printf("Input value: (%s)", arg);
+  for (size_t excl = 0; excl < exclusions_count; ++excl)
+    {
+      /* Since we've got regex linked we should maybe switch to that, but
+         running regexes for every argument could be too slow. */
+      if ( strcmp (exclusions, "*") == 0 || (strlen (exclusions) && strstr (arg, exclusions) == arg) )
+        return (char*)arg;
+      exclusions += strlen (exclusions) + 1;
+    }
+
+  // Leave enough room for at least 16 path elements; we might be converting
+  // a path list.
+  size_t stack_len = arglen + 16 * MAX_PATH;
+  char * stack_path = (char *)malloc (stack_len);
+  if (!stack_path)
+    {
+      debug_printf ("out of stack space?");
+      return (char *)arg;
+    }
+  memset (stack_path, 0, MAX_PATH);
+  convert (stack_path, stack_len - 1, arg);
+  debug_printf ("convert()'ed: %s (length %d)\n.....->: %s", arg, arglen, stack_path);
+  // Don't allocate memory if no conversion happened.
+  if (!strcmp (arg, stack_path))
+    {
+      if (arg != stack_path)
+        {
+          free (stack_path);
+        }
+      return ((char *)arg);
+    }
+  arg_result = (char *)realloc (stack_path, strlen (stack_path)+1);
+  // Windows doesn't like empty entries in PATH env. variables (;;)
+  char* semisemi = strstr(arg_result, ";;");
+  while (semisemi)
+    {
+      memmove(semisemi, semisemi+1, strlen(semisemi));
+      semisemi = strstr(semisemi, ";;");
+    }
+  return arg_result;
+}
+
+extern "C" char *
+arg_heuristic (char const * const arg)
+{
+  return arg_heuristic_with_exclusions (arg, NULL, 0);
+}
+
+
 /******************** Exported Path Routines *********************/
 
 /* Cover functions to the path conversion routines.
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index ec0cdd4..53ddc60 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -295,6 +295,27 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 	  }
       }
 
+  /* Environment variable MSYS2_ARG_CONV_EXCL contains a list
+     of ';' separated argument prefixes to pass un-modified..
+     It isn't applied to env. variables; only spawn arguments.
+     A value of * means don't convert any arguments. */
+  char* msys2_arg_conv_excl_env = getenv("MSYS2_ARG_CONV_EXCL");
+  char* msys2_arg_conv_excl = NULL;
+  size_t msys2_arg_conv_excl_count = 0;
+  if (msys2_arg_conv_excl_env)
+    {
+      msys2_arg_conv_excl = (char*)alloca (strlen(msys2_arg_conv_excl_env)+1);
+      strcpy (msys2_arg_conv_excl, msys2_arg_conv_excl_env);
+      msys2_arg_conv_excl_count = 1;
+      msys2_arg_conv_excl_env = strchr ( msys2_arg_conv_excl, ';' );
+      while (msys2_arg_conv_excl_env)
+        {
+          *msys2_arg_conv_excl_env = '\0';
+          ++msys2_arg_conv_excl_count;
+          msys2_arg_conv_excl_env = strchr ( msys2_arg_conv_excl_env + 1, ';' );
+        }
+    }
+
   /* Check if we have been called from exec{lv}p or spawn{lv}p and mask
      mode to keep only the spawn mode. */
   bool p_type_exec = !!(mode & _P_PATH_TYPE_EXEC);
@@ -404,9 +425,23 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 	      moreinfo->argc = newargv.argc;
 	      moreinfo->argv = newargv;
 	    }
+	  else
+	    {
+	      for (int i = 0; i < newargv.argc; i++)
+	        {
+	          //convert argv to win32
+	          int newargvlen = strlen (newargv[i]);
+	          char *tmpbuf = (char *)malloc (newargvlen + 1);
+	          memcpy (tmpbuf, newargv[i], newargvlen + 1);
+	          tmpbuf = arg_heuristic_with_exclusions(tmpbuf, msys2_arg_conv_excl, msys2_arg_conv_excl_count);
+	          debug_printf("newargv[%d] = %s", i, newargv[i]);
+	          newargv.replace (i, tmpbuf);
+	          free (tmpbuf);
+	        }
+	    }
 	  if ((wincmdln || !real_path.iscygexec ())
-	       && !cmd.fromargv (newargv, real_path.get_win32 (),
-				 real_path.iscygexec ()))
+	        && !cmd.fromargv (newargv, real_path.get_win32 (),
+		    real_path.iscygexec ()))
 	    {
 	      res = -1;
 	      __leave;
@@ -540,7 +575,8 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
       moreinfo->envp = build_env (envp, envblock, moreinfo->envc,
 				  real_path.iscygexec (),
 				  switch_user ? ::cygheap->user.primary_token ()
-					      : NULL);
+					      : NULL,
+				  real_path.iscygexec ());
       if (!moreinfo->envp || !envblock)
 	{
 	  set_errno (E2BIG);
diff --git a/winsup/cygwin/winf.h b/winsup/cygwin/winf.h
index e3a65f8..1aeec8d 100644
--- a/winsup/cygwin/winf.h
+++ b/winsup/cygwin/winf.h
@@ -51,6 +51,10 @@ class av
 	calloced = 1;
       }
   }
+  void replace (int i, const char *arg)
+  {
+    argv[i] = cstrdup1 (arg);
+  }
   void dup_all ()
   {
     for (int i = calloced; i < argc; i++)
-- 
2.31.1

