Win32: Unicode environment (outgoing)
[gitweb.git] / compat / mingw.c
index 3baaa4dfaedd1e451f810a6bead28b2f7e9aa95d..c725a3e20615d00c519451166b32b83a782d17eb 100644 (file)
@@ -1,8 +1,10 @@
 #include "../git-compat-util.h"
 #include "win32.h"
 #include <conio.h>
+#include <wchar.h>
 #include "../strbuf.h"
 #include "../run-command.h"
+#include "../cache.h"
 
 static const int delay[] = { 0, 1, 10, 20, 40 };
 
@@ -198,14 +200,16 @@ static int ask_yes_no_if_possible(const char *format, ...)
        }
 }
 
-#undef unlink
 int mingw_unlink(const char *pathname)
 {
        int ret, tries = 0;
+       wchar_t wpathname[MAX_PATH];
+       if (xutftowcs_path(wpathname, pathname) < 0)
+               return -1;
 
        /* read-only files cannot be removed */
-       chmod(pathname, 0666);
-       while ((ret = unlink(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
+       _wchmod(wpathname, 0666);
+       while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
                if (!is_file_in_use_error(GetLastError()))
                        break;
                /*
@@ -221,45 +225,45 @@ int mingw_unlink(const char *pathname)
        while (ret == -1 && is_file_in_use_error(GetLastError()) &&
               ask_yes_no_if_possible("Unlink of file '%s' failed. "
                        "Should I try again?", pathname))
-              ret = unlink(pathname);
+              ret = _wunlink(wpathname);
        return ret;
 }
 
-static int is_dir_empty(const char *path)
+static int is_dir_empty(const wchar_t *wpath)
 {
-       struct strbuf buf = STRBUF_INIT;
-       WIN32_FIND_DATAA findbuf;
+       WIN32_FIND_DATAW findbuf;
        HANDLE handle;
-
-       strbuf_addf(&buf, "%s\\*", path);
-       handle = FindFirstFileA(buf.buf, &findbuf);
-       if (handle == INVALID_HANDLE_VALUE) {
-               strbuf_release(&buf);
+       wchar_t wbuf[MAX_PATH + 2];
+       wcscpy(wbuf, wpath);
+       wcscat(wbuf, L"\\*");
+       handle = FindFirstFileW(wbuf, &findbuf);
+       if (handle == INVALID_HANDLE_VALUE)
                return GetLastError() == ERROR_NO_MORE_FILES;
-       }
 
-       while (!strcmp(findbuf.cFileName, ".") ||
-                       !strcmp(findbuf.cFileName, ".."))
-               if (!FindNextFile(handle, &findbuf)) {
-                       strbuf_release(&buf);
-                       return GetLastError() == ERROR_NO_MORE_FILES;
+       while (!wcscmp(findbuf.cFileName, L".") ||
+                       !wcscmp(findbuf.cFileName, L".."))
+               if (!FindNextFileW(handle, &findbuf)) {
+                       DWORD err = GetLastError();
+                       FindClose(handle);
+                       return err == ERROR_NO_MORE_FILES;
                }
        FindClose(handle);
-       strbuf_release(&buf);
        return 0;
 }
 
-#undef rmdir
 int mingw_rmdir(const char *pathname)
 {
        int ret, tries = 0;
+       wchar_t wpathname[MAX_PATH];
+       if (xutftowcs_path(wpathname, pathname) < 0)
+               return -1;
 
-       while ((ret = rmdir(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
+       while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
                if (!is_file_in_use_error(GetLastError()))
                        errno = err_win_to_posix(GetLastError());
                if (errno != EACCES)
                        break;
-               if (!is_dir_empty(pathname)) {
+               if (!is_dir_empty(wpathname)) {
                        errno = ENOTEMPTY;
                        break;
                }
@@ -276,16 +280,26 @@ int mingw_rmdir(const char *pathname)
        while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
               ask_yes_no_if_possible("Deletion of directory '%s' failed. "
                        "Should I try again?", pathname))
-              ret = rmdir(pathname);
+              ret = _wrmdir(wpathname);
+       return ret;
+}
+
+int mingw_mkdir(const char *path, int mode)
+{
+       int ret;
+       wchar_t wpath[MAX_PATH];
+       if (xutftowcs_path(wpath, path) < 0)
+               return -1;
+       ret = _wmkdir(wpath);
        return ret;
 }
 
-#undef open
 int mingw_open (const char *filename, int oflags, ...)
 {
        va_list args;
        unsigned mode;
        int fd;
+       wchar_t wfilename[MAX_PATH];
 
        va_start(args, oflags);
        mode = va_arg(args, int);
@@ -294,10 +308,12 @@ int mingw_open (const char *filename, int oflags, ...)
        if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
 
-       fd = open(filename, oflags, mode);
+       if (xutftowcs_path(wfilename, filename) < 0)
+               return -1;
+       fd = _wopen(wfilename, oflags, mode);
 
        if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {
-               DWORD attrs = GetFileAttributes(filename);
+               DWORD attrs = GetFileAttributesW(wfilename);
                if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
                        errno = EISDIR;
        }
@@ -332,17 +348,28 @@ int mingw_fgetc(FILE *stream)
 #undef fopen
 FILE *mingw_fopen (const char *filename, const char *otype)
 {
+       FILE *file;
+       wchar_t wfilename[MAX_PATH], wotype[4];
        if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
-       return fopen(filename, otype);
+       if (xutftowcs_path(wfilename, filename) < 0 ||
+               xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
+               return NULL;
+       file = _wfopen(wfilename, wotype);
+       return file;
 }
 
-#undef freopen
 FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
 {
+       FILE *file;
+       wchar_t wfilename[MAX_PATH], wotype[4];
        if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
-       return freopen(filename, otype, stream);
+       if (xutftowcs_path(wfilename, filename) < 0 ||
+               xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
+               return NULL;
+       file = _wfreopen(wfilename, wotype, stream);
+       return file;
 }
 
 #undef fflush
@@ -367,6 +394,31 @@ int mingw_fflush(FILE *stream)
        return ret;
 }
 
+int mingw_access(const char *filename, int mode)
+{
+       wchar_t wfilename[MAX_PATH];
+       if (xutftowcs_path(wfilename, filename) < 0)
+               return -1;
+       /* X_OK is not supported by the MSVCRT version */
+       return _waccess(wfilename, mode & ~X_OK);
+}
+
+int mingw_chdir(const char *dirname)
+{
+       wchar_t wdirname[MAX_PATH];
+       if (xutftowcs_path(wdirname, dirname) < 0)
+               return -1;
+       return _wchdir(wdirname);
+}
+
+int mingw_chmod(const char *filename, int mode)
+{
+       wchar_t wfilename[MAX_PATH];
+       if (xutftowcs_path(wfilename, filename) < 0)
+               return -1;
+       return _wchmod(wfilename, mode);
+}
+
 /*
  * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
  * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
@@ -392,10 +444,12 @@ static inline time_t filetime_to_time_t(const FILETIME *ft)
  */
 static int do_lstat(int follow, const char *file_name, struct stat *buf)
 {
-       int err;
        WIN32_FILE_ATTRIBUTE_DATA fdata;
+       wchar_t wfilename[MAX_PATH];
+       if (xutftowcs_path(wfilename, file_name) < 0)
+               return -1;
 
-       if (!(err = get_file_attr(file_name, &fdata))) {
+       if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
                buf->st_ino = 0;
                buf->st_gid = 0;
                buf->st_uid = 0;
@@ -408,8 +462,8 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
                buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
                buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
                if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
-                       WIN32_FIND_DATAA findbuf;
-                       HANDLE handle = FindFirstFileA(file_name, &findbuf);
+                       WIN32_FIND_DATAW findbuf;
+                       HANDLE handle = FindFirstFileW(wfilename, &findbuf);
                        if (handle != INVALID_HANDLE_VALUE) {
                                if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
                                                (findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
@@ -428,7 +482,23 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
                }
                return 0;
        }
-       errno = err;
+       switch (GetLastError()) {
+       case ERROR_ACCESS_DENIED:
+       case ERROR_SHARING_VIOLATION:
+       case ERROR_LOCK_VIOLATION:
+       case ERROR_SHARING_BUFFER_EXCEEDED:
+               errno = EACCES;
+               break;
+       case ERROR_BUFFER_OVERFLOW:
+               errno = ENAMETOOLONG;
+               break;
+       case ERROR_NOT_ENOUGH_MEMORY:
+               errno = ENOMEM;
+               break;
+       default:
+               errno = ENOENT;
+               break;
+       }
        return -1;
 }
 
@@ -516,16 +586,20 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
 {
        FILETIME mft, aft;
        int fh, rc;
+       DWORD attrs;
+       wchar_t wfilename[MAX_PATH];
+       if (xutftowcs_path(wfilename, file_name) < 0)
+               return -1;
 
        /* must have write permission */
-       DWORD attrs = GetFileAttributes(file_name);
+       attrs = GetFileAttributesW(wfilename);
        if (attrs != INVALID_FILE_ATTRIBUTES &&
            (attrs & FILE_ATTRIBUTE_READONLY)) {
                /* ignore errors here; open() will report them */
-               SetFileAttributes(file_name, attrs & ~FILE_ATTRIBUTE_READONLY);
+               SetFileAttributesW(wfilename, attrs & ~FILE_ATTRIBUTE_READONLY);
        }
 
-       if ((fh = open(file_name, O_RDWR | O_BINARY)) < 0) {
+       if ((fh = _wopen(wfilename, O_RDWR | O_BINARY)) < 0) {
                rc = -1;
                goto revert_attrs;
        }
@@ -548,7 +622,7 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
        if (attrs != INVALID_FILE_ATTRIBUTES &&
            (attrs & FILE_ATTRIBUTE_READONLY)) {
                /* ignore errors again */
-               SetFileAttributes(file_name, attrs);
+               SetFileAttributesW(wfilename, attrs);
        }
        return rc;
 }
@@ -559,6 +633,18 @@ unsigned int sleep (unsigned int seconds)
        return 0;
 }
 
+char *mingw_mktemp(char *template)
+{
+       wchar_t wtemplate[MAX_PATH];
+       if (xutftowcs_path(wtemplate, template) < 0)
+               return NULL;
+       if (!_wmktemp(wtemplate))
+               return NULL;
+       if (xwcstoutf(template, wtemplate, strlen(template) + 1) < 0)
+               return NULL;
+       return template;
+}
+
 int mkstemp(char *template)
 {
        char *filename = mktemp(template);
@@ -617,17 +703,18 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
        return result;
 }
 
-#undef getcwd
 char *mingw_getcwd(char *pointer, int len)
 {
        int i;
-       char *ret = getcwd(pointer, len);
-       if (!ret)
-               return ret;
+       wchar_t wpointer[MAX_PATH];
+       if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer)))
+               return NULL;
+       if (xwcstoutf(pointer, wpointer, len) < 0)
+               return NULL;
        for (i = 0; pointer[i]; i++)
                if (pointer[i] == '\\')
                        pointer[i] = '/';
-       return ret;
+       return pointer;
 }
 
 /*
@@ -833,9 +920,9 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
 {
        STARTUPINFOW si;
        PROCESS_INFORMATION pi;
-       struct strbuf envblk, args;
-       wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs;
-       unsigned flags;
+       struct strbuf args;
+       wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL;
+       unsigned flags = CREATE_UNICODE_ENVIRONMENT;
        BOOL ret;
 
        /* Determine whether or not we are associated to a console */
@@ -852,7 +939,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
                 * instead of CREATE_NO_WINDOW to make ssh
                 * recognize that it has no console.
                 */
-               flags = DETACHED_PROCESS;
+               flags |= DETACHED_PROCESS;
        } else {
                /* There is already a console. If we specified
                 * DETACHED_PROCESS here, too, Windows would
@@ -860,7 +947,6 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
                 * The same is true for CREATE_NO_WINDOW.
                 * Go figure!
                 */
-               flags = 0;
                CloseHandle(cons);
        }
        memset(&si, 0, sizeof(si));
@@ -899,6 +985,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
        if (env) {
                int count = 0;
                char **e, **sorted_env;
+               int size = 0, wenvsz = 0, wenvpos = 0;
 
                for (e = env; *e; e++)
                        count++;
@@ -908,20 +995,22 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
                memcpy(sorted_env, env, sizeof(*sorted_env) * (count + 1));
                qsort(sorted_env, count, sizeof(*sorted_env), env_compare);
 
-               strbuf_init(&envblk, 0);
+               /* create environment block from temporary environment */
                for (e = sorted_env; *e; e++) {
-                       strbuf_addstr(&envblk, *e);
-                       strbuf_addch(&envblk, '\0');
+                       size = 2 * strlen(*e) + 2; /* +2 for final \0 */
+                       ALLOC_GROW(wenvblk, (wenvpos + size) * sizeof(wchar_t), wenvsz);
+                       wenvpos += xutftowcs(&wenvblk[wenvpos], *e, size) + 1;
                }
+               /* add final \0 terminator */
+               wenvblk[wenvpos] = 0;
                free(sorted_env);
        }
 
        memset(&pi, 0, sizeof(pi));
        ret = CreateProcessW(wcmd, wargs, NULL, NULL, TRUE, flags,
-               env ? envblk.buf : NULL, dir ? wdir : NULL, &si, &pi);
+               wenvblk, dir ? wdir : NULL, &si, &pi);
 
-       if (env)
-               strbuf_release(&envblk);
+       free(wenvblk);
        free(wargs);
 
        if (!ret) {
@@ -1159,31 +1248,14 @@ char **make_augmented_environ(const char *const *vars)
 }
 
 #undef getenv
-
-/*
- * The system's getenv looks up the name in a case-insensitive manner.
- * This version tries a case-sensitive lookup and falls back to
- * case-insensitive if nothing was found.  This is necessary because,
- * as a prominent example, CMD sets 'Path', but not 'PATH'.
- * Warning: not thread-safe.
- */
-static char *getenv_cs(const char *name)
-{
-       size_t len = strlen(name);
-       int i = lookup_env(environ, name, len);
-       if (i >= 0)
-               return environ[i] + len + 1;    /* skip past name and '=' */
-       return getenv(name);
-}
-
 char *mingw_getenv(const char *name)
 {
-       char *result = getenv_cs(name);
+       char *result = getenv(name);
        if (!result && !strcmp(name, "TMPDIR")) {
                /* on Windows it is TMP and TEMP */
-               result = getenv_cs("TMP");
+               result = getenv("TMP");
                if (!result)
-                       result = getenv_cs("TEMP");
+                       result = getenv("TEMP");
        }
        return result;
 }
@@ -1485,33 +1557,36 @@ int mingw_rename(const char *pold, const char *pnew)
 {
        DWORD attrs, gle;
        int tries = 0;
+       wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
+       if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
+               return -1;
 
        /*
         * Try native rename() first to get errno right.
         * It is based on MoveFile(), which cannot overwrite existing files.
         */
-       if (!rename(pold, pnew))
+       if (!_wrename(wpold, wpnew))
                return 0;
        if (errno != EEXIST)
                return -1;
 repeat:
-       if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
+       if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
                return 0;
        /* TODO: translate more errors */
        gle = GetLastError();
        if (gle == ERROR_ACCESS_DENIED &&
-           (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) {
+           (attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
                if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
                        errno = EISDIR;
                        return -1;
                }
                if ((attrs & FILE_ATTRIBUTE_READONLY) &&
-                   SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
-                       if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
+                   SetFileAttributesW(wpnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
+                       if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
                                return 0;
                        gle = GetLastError();
                        /* revert file attributes on failure */
-                       SetFileAttributes(pnew, attrs);
+                       SetFileAttributesW(wpnew, attrs);
                }
        }
        if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
@@ -1757,11 +1832,16 @@ void mingw_open_html(const char *unixpath)
 
 int link(const char *oldpath, const char *newpath)
 {
-       typedef BOOL (WINAPI *T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
+       typedef BOOL (WINAPI *T)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
        static T create_hard_link = NULL;
+       wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
+       if (xutftowcs_path(woldpath, oldpath) < 0 ||
+               xutftowcs_path(wnewpath, newpath) < 0)
+               return -1;
+
        if (!create_hard_link) {
                create_hard_link = (T) GetProcAddress(
-                       GetModuleHandle("kernel32.dll"), "CreateHardLinkA");
+                       GetModuleHandle("kernel32.dll"), "CreateHardLinkW");
                if (!create_hard_link)
                        create_hard_link = (T)-1;
        }
@@ -1769,7 +1849,7 @@ int link(const char *oldpath, const char *newpath)
                errno = ENOSYS;
                return -1;
        }
-       if (!create_hard_link(newpath, oldpath, NULL)) {
+       if (!create_hard_link(wnewpath, woldpath, NULL)) {
                errno = err_win_to_posix(GetLastError());
                return -1;
        }