MSYS2-packages/msys2-runtime-3.5/0004-Add-functionality-for-converting-UNIX-paths-in-argum.patch
2025-04-10 10:05:41 +02:00

1151 lines
42 KiB
Diff

From 43cfd0b4595e54e2f4ffbbae8df306c7180bb0bf 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.am | 1 +
winsup/cygwin/environ.cc | 24 +-
winsup/cygwin/external.cc | 2 +-
winsup/cygwin/include/sys/cygwin.h | 6 +
winsup/cygwin/local_includes/environ.h | 2 +-
winsup/cygwin/local_includes/winf.h | 4 +
winsup/cygwin/msys2_path_conv.cc | 699 +++++++++++++++++++++++++
winsup/cygwin/msys2_path_conv.h | 147 ++++++
winsup/cygwin/path.cc | 69 +++
winsup/cygwin/spawn.cc | 38 +-
10 files changed, 988 insertions(+), 4 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.am b/winsup/cygwin/Makefile.am
index e65e675..b1b0df7 100644
--- a/winsup/cygwin/Makefile.am
+++ b/winsup/cygwin/Makefile.am
@@ -312,6 +312,7 @@ DLL_FILES= \
miscfuncs.cc \
mktemp.cc \
msg.cc \
+ msys2_path_conv.cc \
mount.cc \
net.cc \
netdb.cc \
diff --git a/winsup/cygwin/environ.cc b/winsup/cygwin/environ.cc
index d4cedcb..639e693 100644
--- a/winsup/cygwin/environ.cc
+++ b/winsup/cygwin/environ.cc
@@ -1046,7 +1046,7 @@ env_compare (const void *key, const void *memb)
to the child. */
char **
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;
@@ -1139,6 +1139,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)))
@@ -1259,6 +1272,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/external.cc b/winsup/cygwin/external.cc
index 97e4528..33863d8 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 ?: 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 2c5997b..6383646 100644
--- a/winsup/cygwin/include/sys/cygwin.h
+++ b/winsup/cygwin/include/sys/cygwin.h
@@ -60,6 +60,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/local_includes/environ.h b/winsup/cygwin/local_includes/environ.h
index 86e64a7..0dd4535 100644
--- a/winsup/cygwin/local_includes/environ.h
+++ b/winsup/cygwin/local_includes/environ.h
@@ -34,7 +34,7 @@ win_env *getwinenv (const char *name, const char *posix = NULL, win_env * = NULL
char *getwinenveq (const char *name, size_t len, int);
char **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 **win32env_to_cygenv (PWCHAR rawenv, bool posify);
diff --git a/winsup/cygwin/local_includes/winf.h b/winsup/cygwin/local_includes/winf.h
index b586934..bc53cd1 100644
--- a/winsup/cygwin/local_includes/winf.h
+++ b/winsup/cygwin/local_includes/winf.h
@@ -56,6 +56,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++)
diff --git a/winsup/cygwin/msys2_path_conv.cc b/winsup/cygwin/msys2_path_conv.cc
new file mode 100644
index 0000000..c527287
--- /dev/null
+++ b/winsup/cygwin/msys2_path_conv.cc
@@ -0,0 +1,699 @@
+/*
+ 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) {
+ }
+
+ 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);
+ debug_printf("found type %d for path %s", type, copy_from);
+
+ 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;
+ }
+ }
+
+ 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;
+
+ /* Let's not convert ~/.file to ~C:\msys64\.file */
+ if (*it == '~') {
+skip_p2w:
+ *src = end;
+ return NONE;
+ }
+
+ /*
+ * Prevent Git's :file.txt and :/message syntax from beeing modified.
+ */
+ if (*it == ':')
+ goto skip_p2w;
+
+ while (it != end && *it) {
+ switch (*it) {
+ case '`':
+ case '\'':
+ case '*':
+ case '?':
+ case '[':
+ case ']':
+ goto skip_p2w;
+ case '/':
+ if (it + 1 < end && it[1] == '~')
+ goto skip_p2w;
+ break;
+ case ':':
+ // Avoid mangling IPv6 addresses
+ if (it + 1 < end && it[1] == ':')
+ goto skip_p2w;
+
+ // Leave Git's <rev>:./name syntax alone
+ if (it + 1 < end && it[1] == '.') {
+ if (it + 2 < end && it[2] == '/')
+ goto skip_p2w;
+ if (it + 3 < end && it[2] == '.' && it[3] == '/')
+ goto skip_p2w;
+ }
+ break;
+ case '@':
+ // Paths do not contain '@@'
+ if (it + 1 < end && it[1] == '@')
+ goto skip_p2w;
+ }
+ ++it;
+ }
+ it = *src;
+
+ while (!isalnum(*it) && *it != '/' && *it != '\\' && *it != ':' && *it != '-' && *it != '.') {
+ recurse = true;
+ it = ++*src;
+ if (it == end || *it == '\0') return NONE;
+ }
+
+ path_type result = NONE;
+
+ if (it + 1 == end) {
+ switch (*it) {
+ case '/': return ROOTED_PATH ;
+ default: return SIMPLE_WINDOWS_PATH;
+ }
+ }
+
+ 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;
+ int only_dots = *it == '.';
+ int has_slashes = 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 - it2) == 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 {
+ if (!only_dots && !has_slashes)
+ goto skip_p2w;
+ return POSIX_PATH_LIST;
+ }
+ } else if (memchr(it2, '=', end - it2) == NULL) {
+ return SIMPLE_WINDOWS_PATH;
+ }
+ } else if (ch != '.') {
+ only_dots = 0;
+ if (ch == '/' || ch == '\\')
+ has_slashes = 1;
+ }
+ }
+
+ 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);
+ }
+}
+
+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 599809f..1297f23 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[] =
@@ -3869,6 +3870,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 f5a4b91..902cef8 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -292,6 +292,27 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
bool rc;
int res = -1;
+ /* 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);
@@ -383,6 +404,20 @@ 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 ()))
@@ -513,7 +548,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);