Merge branch 'sk/mingw-unicode-spawn-args'
[gitweb.git] / compat / mingw.c
index 1140a131bfa4e1cc004cef162ad749df985ca0d9..3baaa4dfaedd1e451f810a6bead28b2f7e9aa95d 100644 (file)
@@ -831,9 +831,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
                              const char *dir,
                              int prepend_cmd, int fhin, int fhout, int fherr)
 {
-       STARTUPINFO si;
+       STARTUPINFOW si;
        PROCESS_INFORMATION pi;
        struct strbuf envblk, args;
+       wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs;
        unsigned flags;
        BOOL ret;
 
@@ -869,6 +870,11 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
        si.hStdOutput = winansi_get_osfhandle(fhout);
        si.hStdError = winansi_get_osfhandle(fherr);
 
+       if (xutftowcs_path(wcmd, cmd) < 0)
+               return -1;
+       if (dir && xutftowcs_path(wdir, dir) < 0)
+               return -1;
+
        /* concatenate argv, quoting args as we go */
        strbuf_init(&args, 0);
        if (prepend_cmd) {
@@ -886,6 +892,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
                        free(quoted);
        }
 
+       wargs = xmalloc((2 * args.len + 1) * sizeof(wchar_t));
+       xutftowcs(wargs, args.buf, 2 * args.len + 1);
+       strbuf_release(&args);
+
        if (env) {
                int count = 0;
                char **e, **sorted_env;
@@ -907,12 +917,12 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
        }
 
        memset(&pi, 0, sizeof(pi));
-       ret = CreateProcess(cmd, args.buf, NULL, NULL, TRUE, flags,
-               env ? envblk.buf : NULL, dir, &si, &pi);
+       ret = CreateProcessW(wcmd, wargs, NULL, NULL, TRUE, flags,
+               env ? envblk.buf : NULL, dir ? wdir : NULL, &si, &pi);
 
        if (env)
                strbuf_release(&envblk);
-       strbuf_release(&args);
+       free(wargs);
 
        if (!ret) {
                errno = ENOENT;
@@ -1933,10 +1943,48 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
  */
 int _CRT_glob = 0;
 
+typedef struct {
+       int newmode;
+} _startupinfo;
+
+extern int __wgetmainargs(int *argc, wchar_t ***argv, wchar_t ***env, int glob,
+               _startupinfo *si);
+
+static NORETURN void die_startup()
+{
+       fputs("fatal: not enough memory for initialization", stderr);
+       exit(128);
+}
+
 void mingw_startup()
 {
-       /* copy executable name to argv[0] */
-       __argv[0] = xstrdup(_pgmptr);
+       int i, len, maxlen, argc;
+       char *buffer;
+       wchar_t **wenv, **wargv;
+       _startupinfo si;
+
+       /* get wide char arguments and environment */
+       si.newmode = 0;
+       if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)
+               die_startup();
+
+       /* determine size of argv and environ conversion buffer */
+       maxlen = wcslen(_wpgmptr);
+       for (i = 1; i < argc; i++)
+               maxlen = max(maxlen, wcslen(wargv[i]));
+
+       /* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */
+       maxlen = 3 * maxlen + 1;
+       buffer = xmalloc(maxlen);
+
+       /* convert command line arguments and environment to UTF-8 */
+       len = xwcstoutf(buffer, _wpgmptr, maxlen);
+       __argv[0] = xmemdupz(buffer, len);
+       for (i = 1; i < argc; i++) {
+               len = xwcstoutf(buffer, wargv[i], maxlen);
+               __argv[i] = xmemdupz(buffer, len);
+       }
+       free(buffer);
 
        /* initialize critical section for waitpid pinfo_t list */
        InitializeCriticalSection(&pinfo_cs);