Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit 3e60e64

Browse files
kbleeskasal
authored andcommitted
Win32: Unicode file name support (except dirent)
Replaces Windows "ANSI" APIs dealing with file- or path names with their Unicode equivalent, adding UTF-8/UTF-16LE conversion as necessary. The dirent API (opendir/readdir/closedir) is updated in a separate commit. Adds trivial wrappers for access, chmod and chdir. Adds wrapper for mktemp (needed for both mkstemp and mkdtemp). The simplest way to convert a repository with legacy-encoded (e.g. Cp1252) file names to UTF-8 ist to checkout with an old msysgit version and "git add --all & git commit" with the new version. Signed-off-by: Karsten Blees <[email protected]>
1 parent b5dc523 commit 3e60e64

File tree

2 files changed

+157
-56
lines changed

2 files changed

+157
-56
lines changed

compat/mingw.c

Lines changed: 143 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "../git-compat-util.h"
22
#include "win32.h"
33
#include <conio.h>
4+
#include <wchar.h>
45
#include "../strbuf.h"
56
#include "../run-command.h"
67

@@ -198,14 +199,16 @@ static int ask_yes_no_if_possible(const char *format, ...)
198199
}
199200
}
200201

201-
#undef unlink
202202
int mingw_unlink(const char *pathname)
203203
{
204204
int ret, tries = 0;
205+
wchar_t wpathname[MAX_PATH];
206+
if (xutftowcs_path(wpathname, pathname) < 0)
207+
return -1;
205208

206209
/* read-only files cannot be removed */
207-
chmod(pathname, 0666);
208-
while ((ret = unlink(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
210+
_wchmod(wpathname, 0666);
211+
while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
209212
if (!is_file_in_use_error(GetLastError()))
210213
break;
211214
/*
@@ -221,45 +224,42 @@ int mingw_unlink(const char *pathname)
221224
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
222225
ask_yes_no_if_possible("Unlink of file '%s' failed. "
223226
"Should I try again?", pathname))
224-
ret = unlink(pathname);
227+
ret = _wunlink(wpathname);
225228
return ret;
226229
}
227230

228-
static int is_dir_empty(const char *path)
231+
static int is_dir_empty(const wchar_t *wpath)
229232
{
230-
struct strbuf buf = STRBUF_INIT;
231-
WIN32_FIND_DATAA findbuf;
233+
WIN32_FIND_DATAW findbuf;
232234
HANDLE handle;
233-
234-
strbuf_addf(&buf, "%s\\*", path);
235-
handle = FindFirstFileA(buf.buf, &findbuf);
236-
if (handle == INVALID_HANDLE_VALUE) {
237-
strbuf_release(&buf);
235+
wchar_t wbuf[MAX_PATH + 2];
236+
wcscpy(wbuf, wpath);
237+
wcscat(wbuf, L"\\*");
238+
handle = FindFirstFileW(wbuf, &findbuf);
239+
if (handle == INVALID_HANDLE_VALUE)
238240
return GetLastError() == ERROR_NO_MORE_FILES;
239-
}
240241

241-
while (!strcmp(findbuf.cFileName, ".") ||
242-
!strcmp(findbuf.cFileName, ".."))
243-
if (!FindNextFile(handle, &findbuf)) {
244-
strbuf_release(&buf);
242+
while (!wcscmp(findbuf.cFileName, L".") ||
243+
!wcscmp(findbuf.cFileName, L".."))
244+
if (!FindNextFileW(handle, &findbuf))
245245
return GetLastError() == ERROR_NO_MORE_FILES;
246-
}
247246
FindClose(handle);
248-
strbuf_release(&buf);
249247
return 0;
250248
}
251249

252-
#undef rmdir
253250
int mingw_rmdir(const char *pathname)
254251
{
255252
int ret, tries = 0;
253+
wchar_t wpathname[MAX_PATH];
254+
if (xutftowcs_path(wpathname, pathname) < 0)
255+
return -1;
256256

257-
while ((ret = rmdir(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
257+
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
258258
if (!is_file_in_use_error(GetLastError()))
259259
errno = err_win_to_posix(GetLastError());
260260
if (errno != EACCES)
261261
break;
262-
if (!is_dir_empty(pathname)) {
262+
if (!is_dir_empty(wpathname)) {
263263
errno = ENOTEMPTY;
264264
break;
265265
}
@@ -276,16 +276,26 @@ int mingw_rmdir(const char *pathname)
276276
while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
277277
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
278278
"Should I try again?", pathname))
279-
ret = rmdir(pathname);
279+
ret = _wrmdir(wpathname);
280+
return ret;
281+
}
282+
283+
int mingw_mkdir(const char *path, int mode)
284+
{
285+
int ret;
286+
wchar_t wpath[MAX_PATH];
287+
if (xutftowcs_path(wpath, path) < 0)
288+
return -1;
289+
ret = _wmkdir(wpath);
280290
return ret;
281291
}
282292

283-
#undef open
284293
int mingw_open (const char *filename, int oflags, ...)
285294
{
286295
va_list args;
287296
unsigned mode;
288297
int fd;
298+
wchar_t wfilename[MAX_PATH];
289299

290300
va_start(args, oflags);
291301
mode = va_arg(args, int);
@@ -294,10 +304,12 @@ int mingw_open (const char *filename, int oflags, ...)
294304
if (filename && !strcmp(filename, "/dev/null"))
295305
filename = "nul";
296306

297-
fd = open(filename, oflags, mode);
307+
if (xutftowcs_path(wfilename, filename) < 0)
308+
return -1;
309+
fd = _wopen(wfilename, oflags, mode);
298310

299311
if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {
300-
DWORD attrs = GetFileAttributes(filename);
312+
DWORD attrs = GetFileAttributesW(wfilename);
301313
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
302314
errno = EISDIR;
303315
}
@@ -332,17 +344,28 @@ int mingw_fgetc(FILE *stream)
332344
#undef fopen
333345
FILE *mingw_fopen (const char *filename, const char *otype)
334346
{
347+
FILE *file;
348+
wchar_t wfilename[MAX_PATH], wotype[4];
335349
if (filename && !strcmp(filename, "/dev/null"))
336350
filename = "nul";
337-
return fopen(filename, otype);
351+
if (xutftowcs_path(wfilename, filename) < 0 ||
352+
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
353+
return NULL;
354+
file = _wfopen(wfilename, wotype);
355+
return file;
338356
}
339357

340-
#undef freopen
341358
FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
342359
{
360+
FILE *file;
361+
wchar_t wfilename[MAX_PATH], wotype[4];
343362
if (filename && !strcmp(filename, "/dev/null"))
344363
filename = "nul";
345-
return freopen(filename, otype, stream);
364+
if (xutftowcs_path(wfilename, filename) < 0 ||
365+
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
366+
return NULL;
367+
file = _wfreopen(wfilename, wotype, stream);
368+
return file;
346369
}
347370

348371
#undef fflush
@@ -367,6 +390,31 @@ int mingw_fflush(FILE *stream)
367390
return ret;
368391
}
369392

393+
int mingw_access(const char *filename, int mode)
394+
{
395+
wchar_t wfilename[MAX_PATH];
396+
if (xutftowcs_path(wfilename, filename) < 0)
397+
return -1;
398+
/* X_OK is not supported by the MSVCRT version */
399+
return _waccess(wfilename, mode & ~X_OK);
400+
}
401+
402+
int mingw_chdir(const char *dirname)
403+
{
404+
wchar_t wdirname[MAX_PATH];
405+
if (xutftowcs_path(wdirname, dirname) < 0)
406+
return -1;
407+
return _wchdir(wdirname);
408+
}
409+
410+
int mingw_chmod(const char *filename, int mode)
411+
{
412+
wchar_t wfilename[MAX_PATH];
413+
if (xutftowcs_path(wfilename, filename) < 0)
414+
return -1;
415+
return _wchmod(wfilename, mode);
416+
}
417+
370418
/*
371419
* The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
372420
* Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
@@ -392,10 +440,12 @@ static inline time_t filetime_to_time_t(const FILETIME *ft)
392440
*/
393441
static int do_lstat(int follow, const char *file_name, struct stat *buf)
394442
{
395-
int err;
396443
WIN32_FILE_ATTRIBUTE_DATA fdata;
444+
wchar_t wfilename[MAX_PATH];
445+
if (xutftowcs_path(wfilename, file_name) < 0)
446+
return -1;
397447

398-
if (!(err = get_file_attr(file_name, &fdata))) {
448+
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
399449
buf->st_ino = 0;
400450
buf->st_gid = 0;
401451
buf->st_uid = 0;
@@ -408,8 +458,8 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
408458
buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
409459
buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
410460
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
411-
WIN32_FIND_DATAA findbuf;
412-
HANDLE handle = FindFirstFileA(file_name, &findbuf);
461+
WIN32_FIND_DATAW findbuf;
462+
HANDLE handle = FindFirstFileW(wfilename, &findbuf);
413463
if (handle != INVALID_HANDLE_VALUE) {
414464
if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
415465
(findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
@@ -428,7 +478,23 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
428478
}
429479
return 0;
430480
}
431-
errno = err;
481+
switch (GetLastError()) {
482+
case ERROR_ACCESS_DENIED:
483+
case ERROR_SHARING_VIOLATION:
484+
case ERROR_LOCK_VIOLATION:
485+
case ERROR_SHARING_BUFFER_EXCEEDED:
486+
errno = EACCES;
487+
break;
488+
case ERROR_BUFFER_OVERFLOW:
489+
errno = ENAMETOOLONG;
490+
break;
491+
case ERROR_NOT_ENOUGH_MEMORY:
492+
errno = ENOMEM;
493+
break;
494+
default:
495+
errno = ENOENT;
496+
break;
497+
}
432498
return -1;
433499
}
434500

@@ -516,16 +582,20 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
516582
{
517583
FILETIME mft, aft;
518584
int fh, rc;
585+
DWORD attrs;
586+
wchar_t wfilename[MAX_PATH];
587+
if (xutftowcs_path(wfilename, file_name) < 0)
588+
return -1;
519589

520590
/* must have write permission */
521-
DWORD attrs = GetFileAttributes(file_name);
591+
attrs = GetFileAttributesW(wfilename);
522592
if (attrs != INVALID_FILE_ATTRIBUTES &&
523593
(attrs & FILE_ATTRIBUTE_READONLY)) {
524594
/* ignore errors here; open() will report them */
525-
SetFileAttributes(file_name, attrs & ~FILE_ATTRIBUTE_READONLY);
595+
SetFileAttributesW(wfilename, attrs & ~FILE_ATTRIBUTE_READONLY);
526596
}
527597

528-
if ((fh = open(file_name, O_RDWR | O_BINARY)) < 0) {
598+
if ((fh = _wopen(wfilename, O_RDWR | O_BINARY)) < 0) {
529599
rc = -1;
530600
goto revert_attrs;
531601
}
@@ -548,7 +618,7 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
548618
if (attrs != INVALID_FILE_ATTRIBUTES &&
549619
(attrs & FILE_ATTRIBUTE_READONLY)) {
550620
/* ignore errors again */
551-
SetFileAttributes(file_name, attrs);
621+
SetFileAttributesW(wfilename, attrs);
552622
}
553623
return rc;
554624
}
@@ -559,6 +629,18 @@ unsigned int sleep (unsigned int seconds)
559629
return 0;
560630
}
561631

632+
char *mingw_mktemp(char *template)
633+
{
634+
wchar_t wtemplate[MAX_PATH];
635+
if (xutftowcs_path(wtemplate, template) < 0)
636+
return NULL;
637+
if (!_wmktemp(wtemplate))
638+
return NULL;
639+
if (xwcstoutf(template, wtemplate, strlen(template) + 1) < 0)
640+
return NULL;
641+
return template;
642+
}
643+
562644
int mkstemp(char *template)
563645
{
564646
char *filename = mktemp(template);
@@ -617,17 +699,18 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
617699
return result;
618700
}
619701

620-
#undef getcwd
621702
char *mingw_getcwd(char *pointer, int len)
622703
{
623704
int i;
624-
char *ret = getcwd(pointer, len);
625-
if (!ret)
626-
return ret;
705+
wchar_t wpointer[MAX_PATH];
706+
if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer)))
707+
return NULL;
708+
if (xwcstoutf(pointer, wpointer, len) < 0)
709+
return NULL;
627710
for (i = 0; pointer[i]; i++)
628711
if (pointer[i] == '\\')
629712
pointer[i] = '/';
630-
return ret;
713+
return pointer;
631714
}
632715

633716
#undef getenv
@@ -1468,33 +1551,36 @@ int mingw_rename(const char *pold, const char *pnew)
14681551
{
14691552
DWORD attrs, gle;
14701553
int tries = 0;
1554+
wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
1555+
if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
1556+
return -1;
14711557

14721558
/*
14731559
* Try native rename() first to get errno right.
14741560
* It is based on MoveFile(), which cannot overwrite existing files.
14751561
*/
1476-
if (!rename(pold, pnew))
1562+
if (!_wrename(wpold, wpnew))
14771563
return 0;
14781564
if (errno != EEXIST)
14791565
return -1;
14801566
repeat:
1481-
if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
1567+
if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
14821568
return 0;
14831569
/* TODO: translate more errors */
14841570
gle = GetLastError();
14851571
if (gle == ERROR_ACCESS_DENIED &&
1486-
(attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) {
1572+
(attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
14871573
if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
14881574
errno = EISDIR;
14891575
return -1;
14901576
}
14911577
if ((attrs & FILE_ATTRIBUTE_READONLY) &&
1492-
SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
1493-
if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
1578+
SetFileAttributesW(wpnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
1579+
if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
14941580
return 0;
14951581
gle = GetLastError();
14961582
/* revert file attributes on failure */
1497-
SetFileAttributes(pnew, attrs);
1583+
SetFileAttributesW(wpnew, attrs);
14981584
}
14991585
}
15001586
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
@@ -1740,19 +1826,24 @@ void mingw_open_html(const char *unixpath)
17401826

17411827
int link(const char *oldpath, const char *newpath)
17421828
{
1743-
typedef BOOL (WINAPI *T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
1829+
typedef BOOL (WINAPI *T)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
17441830
static T create_hard_link = NULL;
1831+
wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
1832+
if (xutftowcs_path(woldpath, oldpath) < 0 ||
1833+
xutftowcs_path(wnewpath, newpath) < 0)
1834+
return -1;
1835+
17451836
if (!create_hard_link) {
17461837
create_hard_link = (T) GetProcAddress(
1747-
GetModuleHandle("kernel32.dll"), "CreateHardLinkA");
1838+
GetModuleHandle("kernel32.dll"), "CreateHardLinkW");
17481839
if (!create_hard_link)
17491840
create_hard_link = (T)-1;
17501841
}
17511842
if (create_hard_link == (T)-1) {
17521843
errno = ENOSYS;
17531844
return -1;
17541845
}
1755-
if (!create_hard_link(newpath, oldpath, NULL)) {
1846+
if (!create_hard_link(wnewpath, woldpath, NULL)) {
17561847
errno = err_win_to_posix(GetLastError());
17571848
return -1;
17581849
}

0 commit comments

Comments
 (0)