Merge branch 'sk/mingw-uni-fix'
authorJunio C Hamano <gitster@pobox.com>
Mon, 21 Jul 2014 18:18:50 +0000 (11:18 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 21 Jul 2014 18:18:50 +0000 (11:18 -0700)
* sk/mingw-uni-fix:
Win32: Unicode file name support (dirent)
Win32: Unicode file name support (except dirent)

compat/mingw.c
compat/mingw.h
compat/win32/dirent.c
compat/win32/dirent.h
index 3baaa4dfaedd1e451f810a6bead28b2f7e9aa95d..c19e3d954bc58b61028873b2e75b0604201531e5 100644 (file)
@@ -1,6 +1,7 @@
 #include "../git-compat-util.h"
 #include "win32.h"
 #include <conio.h>
+#include <wchar.h>
 #include "../strbuf.h"
 #include "../run-command.h"
 
@@ -198,14 +199,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 +224,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 +279,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 +307,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 +347,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 +393,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 +443,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 +461,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 +481,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 +585,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 +621,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 +632,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 +702,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;
 }
 
 /*
@@ -1485,33 +1571,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 +1846,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 +1863,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;
        }
index 7ff2376335dea7a092c626f74f52413be1c81e74..510530c69054e12dcf0d705a61041457ba62843d 100644 (file)
@@ -121,10 +121,7 @@ static inline int fcntl(int fd, int cmd, ...)
  * simple adaptors
  */
 
-static inline int mingw_mkdir(const char *path, int mode)
-{
-       return mkdir(path);
-}
+int mingw_mkdir(const char *path, int mode);
 #define mkdir mingw_mkdir
 
 #define WNOHANG 1
@@ -195,6 +192,19 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream);
 int mingw_fflush(FILE *stream);
 #define fflush mingw_fflush
 
+int mingw_access(const char *filename, int mode);
+#undef access
+#define access mingw_access
+
+int mingw_chdir(const char *dirname);
+#define chdir mingw_chdir
+
+int mingw_chmod(const char *filename, int mode);
+#define chmod mingw_chmod
+
+char *mingw_mktemp(char *template);
+#define mktemp mingw_mktemp
+
 char *mingw_getcwd(char *pointer, int len);
 #define getcwd mingw_getcwd
 
index 82a515c21b322a0e50c7b5dbdd12e74341b20e98..52420ec7d4dad7b8e984bd7f5a36330f1fa88e24 100644 (file)
@@ -6,10 +6,10 @@ struct DIR {
        int dd_stat;          /* 0-based index */
 };
 
-static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata)
+static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
 {
-       /* copy file name from WIN32_FIND_DATA to dirent */
-       memcpy(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
+       /* convert UTF-16 name to UTF-8 */
+       xwcstoutf(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
 
        /* Set file type, based on WIN32_FIND_DATA */
        if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
@@ -20,25 +20,15 @@ static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata)
 
 DIR *opendir(const char *name)
 {
-       char pattern[MAX_PATH];
-       WIN32_FIND_DATAA fdata;
+       wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
+       WIN32_FIND_DATAW fdata;
        HANDLE h;
        int len;
        DIR *dir;
 
-       /* check that name is not NULL */
-       if (!name) {
-               errno = EINVAL;
+       /* convert name to UTF-16 and check length < MAX_PATH */
+       if ((len = xutftowcs_path(pattern, name)) < 0)
                return NULL;
-       }
-       /* check that the pattern won't be too long for FindFirstFileA */
-       len = strlen(name);
-       if (len + 2 >= MAX_PATH) {
-               errno = ENAMETOOLONG;
-               return NULL;
-       }
-       /* copy name to temp buffer */
-       memcpy(pattern, name, len + 1);
 
        /* append optional '/' and wildcard '*' */
        if (len && !is_dir_sep(pattern[len - 1]))
@@ -47,7 +37,7 @@ DIR *opendir(const char *name)
        pattern[len] = 0;
 
        /* open find handle */
-       h = FindFirstFileA(pattern, &fdata);
+       h = FindFirstFileW(pattern, &fdata);
        if (h == INVALID_HANDLE_VALUE) {
                DWORD err = GetLastError();
                errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
@@ -72,8 +62,8 @@ struct dirent *readdir(DIR *dir)
        /* if first entry, dirent has already been set up by opendir */
        if (dir->dd_stat) {
                /* get next entry and convert from WIN32_FIND_DATA to dirent */
-               WIN32_FIND_DATAA fdata;
-               if (FindNextFileA(dir->dd_handle, &fdata)) {
+               WIN32_FIND_DATAW fdata;
+               if (FindNextFileW(dir->dd_handle, &fdata)) {
                        finddata2dirent(&dir->dd_dir, &fdata);
                } else {
                        DWORD lasterr = GetLastError();
index 8838cd61fc282b3713db76e088130f82b6c6fe33..058207e4bfed628376d94f8002278317b5c65074 100644 (file)
@@ -10,7 +10,7 @@ typedef struct DIR DIR;
 
 struct dirent {
        unsigned char d_type;      /* file type to prevent lstat after readdir */
-       char d_name[MAX_PATH];     /* file name */
+       char d_name[MAX_PATH * 3]; /* file name (* 3 for UTF-8 conversion) */
 };
 
 DIR *opendir(const char *dirname);