Imported Upstream version 2.21.0
[platform/upstream/git.git] / compat / mingw.c
index a010e0b..8141f77 100644 (file)
@@ -7,6 +7,7 @@
 #include "../cache.h"
 #include "win32/lazyload.h"
 #include "../config.h"
+#include "dir.h"
 
 #define HCAST(type, handle) ((type)(intptr_t)handle)
 
@@ -350,7 +351,7 @@ static inline int needs_hiding(const char *path)
                return 0;
 
        /* We cannot use basename(), as it would remove trailing slashes */
-       mingw_skip_dos_drive_prefix((char **)&path);
+       win32_skip_dos_drive_prefix((char **)&path);
        if (!*path)
                return 0;
 
@@ -389,12 +390,6 @@ int mingw_mkdir(const char *path, int mode)
 {
        int ret;
        wchar_t wpath[MAX_PATH];
-
-       if (!is_valid_win32_path(path)) {
-               errno = EINVAL;
-               return -1;
-       }
-
        if (xutftowcs_path(wpath, path) < 0)
                return -1;
        ret = _wmkdir(wpath);
@@ -468,7 +463,7 @@ int mingw_open (const char *filename, int oflags, ...)
        typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...);
        va_list args;
        unsigned mode;
-       int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
+       int fd;
        wchar_t wfilename[MAX_PATH];
        open_fn_t open_fn;
 
@@ -476,11 +471,6 @@ int mingw_open (const char *filename, int oflags, ...)
        mode = va_arg(args, int);
        va_end(args);
 
-       if (!is_valid_win32_path(filename)) {
-               errno = create ? EINVAL : ENOENT;
-               return -1;
-       }
-
        if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
 
@@ -547,11 +537,6 @@ FILE *mingw_fopen (const char *filename, const char *otype)
        int hide = needs_hiding(filename);
        FILE *file;
        wchar_t wfilename[MAX_PATH], wotype[4];
-       if (!is_valid_win32_path(filename)) {
-               int create = otype && strchr(otype, 'w');
-               errno = create ? EINVAL : ENOENT;
-               return NULL;
-       }
        if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
        if (xutftowcs_path(wfilename, filename) < 0 ||
@@ -574,11 +559,6 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
        int hide = needs_hiding(filename);
        FILE *file;
        wchar_t wfilename[MAX_PATH], wotype[4];
-       if (!is_valid_win32_path(filename)) {
-               int create = otype && strchr(otype, 'w');
-               errno = create ? EINVAL : ENOENT;
-               return NULL;
-       }
        if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
        if (xutftowcs_path(wfilename, filename) < 0 ||
@@ -1052,7 +1032,7 @@ char *mingw_getcwd(char *pointer, int len)
  * See "Parsing C++ Command-Line Arguments" at Microsoft's Docs:
  * https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-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;
@@ -1072,7 +1052,7 @@ static const char *quote_arg(const char *arg)
                                p++;
                                len++;
                        }
-                       if (*p == '"' || !*p)
+                       if (*p == '"')
                                n += count*2 + 1;
                        continue;
                }
@@ -1094,22 +1074,50 @@ static const char *quote_arg(const char *arg)
                                count++;
                                *d++ = *arg++;
                        }
-                       if (*arg == '"' || !*arg) {
+                       if (*arg == '"') {
                                while (count-- > 0)
                                        *d++ = '\\';
-                               /* don't escape the surrounding end quote */
-                               if (!*arg)
-                                       break;
                                *d++ = '\\';
                        }
                }
                *d++ = *arg++;
        }
        *d++ = '"';
-       *d++ = '\0';
+       *d++ = 0;
        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];
@@ -1341,6 +1349,47 @@ struct pinfo_t {
 static struct pinfo_t *pinfo = NULL;
 CRITICAL_SECTION pinfo_cs;
 
+/* Used to match and chomp off path components */
+static inline int match_last_path_component(const char *path, size_t *len,
+                                           const char *component)
+{
+       size_t component_len = strlen(component);
+       if (*len < component_len + 1 ||
+           !is_dir_sep(path[*len - component_len - 1]) ||
+           fspathncmp(path + *len - component_len, component, component_len))
+               return 0;
+       *len -= component_len + 1;
+       /* chomp off repeated dir separators */
+       while (*len > 0 && is_dir_sep(path[*len - 1]))
+               (*len)--;
+       return 1;
+}
+
+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 = match_last_path_component(p, &len, "sh.exe") &&
+                               match_last_path_component(p, &len, "bin") &&
+                               match_last_path_component(p, &len, "usr");
+                       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)
@@ -1352,6 +1401,8 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
        unsigned flags = CREATE_UNICODE_ENVIRONMENT;
        BOOL ret;
        HANDLE cons;
+       const char *(*quote_arg)(const char *arg) =
+               is_msys2_sh(*argv) ? quote_arg_msys2 : quote_arg_msvc;
 
        do_unset_environment_variables();
 
@@ -1581,7 +1632,7 @@ int mingw_kill(pid_t pid, int sig)
  */
 char *mingw_getenv(const char *name)
 {
-#define GETENV_MAX_RETAIN 30
+#define GETENV_MAX_RETAIN 64
        static char *values[GETENV_MAX_RETAIN];
        static int value_counter;
        int len_key, len_value;
@@ -2124,7 +2175,7 @@ static void stop_timer_thread(void)
        if (timer_event)
                SetEvent(timer_event);  /* tell thread to terminate */
        if (timer_thread) {
-               int rc = WaitForSingleObject(timer_thread, 1000);
+               int rc = WaitForSingleObject(timer_thread, 10000);
                if (rc == WAIT_TIMEOUT)
                        error("timer thread did not terminate timely");
                else if (rc != WAIT_OBJECT_0)
@@ -2299,57 +2350,6 @@ pid_t waitpid(pid_t pid, int *status, int options)
        return -1;
 }
 
-int mingw_has_dos_drive_prefix(const char *path)
-{
-       int i;
-
-       /*
-        * Does it start with an ASCII letter (i.e. highest bit not set),
-        * followed by a colon?
-        */
-       if (!(0x80 & (unsigned char)*path))
-               return *path && path[1] == ':' ? 2 : 0;
-
-       /*
-        * While drive letters must be letters of the English alphabet, it is
-        * possible to assign virtually _any_ Unicode character via `subst` as
-        * a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff
-        * like this:
-        *
-        *      subst ֍: %USERPROFILE%\Desktop
-        */
-       for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++)
-               ; /* skip first UTF-8 character */
-       return path[i] == ':' ? i + 1 : 0;
-}
-
-int mingw_skip_dos_drive_prefix(char **path)
-{
-       int ret = has_dos_drive_prefix(*path);
-       *path += ret;
-       return ret;
-}
-
-int mingw_offset_1st_component(const char *path)
-{
-       char *pos = (char *)path;
-
-       /* unc paths */
-       if (!skip_dos_drive_prefix(&pos) &&
-                       is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
-               /* skip server name */
-               pos = strpbrk(pos + 2, "\\/");
-               if (!pos)
-                       return 0; /* Error: malformed unc path */
-
-               do {
-                       pos++;
-               } while (*pos && !is_dir_sep(*pos));
-       }
-
-       return pos + is_dir_sep(*pos) - path;
-}
-
 int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)
 {
        int upos = 0, wpos = 0;
@@ -2464,50 +2464,6 @@ static void setup_windows_environment(void)
                setenv("TERM", "cygwin", 1);
 }
 
-int is_valid_win32_path(const char *path)
-{
-       int preceding_space_or_period = 0, i = 0, periods = 0;
-
-       if (!protect_ntfs)
-               return 1;
-
-       skip_dos_drive_prefix((char **)&path);
-
-       for (;;) {
-               char c = *(path++);
-               switch (c) {
-               case '\0':
-               case '/': case '\\':
-                       /* cannot end in ` ` or `.`, except for `.` and `..` */
-                       if (preceding_space_or_period &&
-                           (i != periods || periods > 2))
-                               return 0;
-                       if (!c)
-                               return 1;
-
-                       i = periods = preceding_space_or_period = 0;
-                       continue;
-               case '.':
-                       periods++;
-                       /* fallthru */
-               case ' ':
-                       preceding_space_or_period = 1;
-                       i++;
-                       continue;
-               case ':': /* DOS drive prefix was already skipped */
-               case '<': case '>': case '"': case '|': case '?': case '*':
-                       /* illegal character */
-                       return 0;
-               default:
-                       if (c > '\0' && c < '\x20')
-                               /* illegal character */
-                               return 0;
-               }
-               preceding_space_or_period = 0;
-               i++;
-       }
-}
-
 /*
  * Disable MSVCRT command line wildcard expansion (__getmainargs called from
  * mingw startup code, see init.c in mingw runtime).