Commit 4d383403 by Vicent Martí

Merge pull request #856 from libgit2/utf8-win

Windows: Perform UTF-8 path conversion on the Stack
parents c9d223f0 0f4c6175
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_git_windows_h__
#define INCLUDE_git_windows_h__
#include "common.h"
/**
* @file git2/windows.h
* @brief Windows-specific functions
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Set the active codepage for Windows syscalls
*
* All syscalls performed by the library will assume
* this codepage when converting paths and strings
* to use by the Windows kernel.
*
* The default value of UTF-8 will work automatically
* with most Git repositories created on Unix systems.
*
* This settings needs only be changed when working
* with repositories that contain paths in specific,
* non-UTF codepages.
*
* A full list of all available codepage identifiers may
* be found at:
*
* http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756(v=vs.85).aspx
*
* @param codepage numeric codepage identifier
*/
GIT_EXTERN(void) gitwin_set_codepage(unsigned int codepage);
/**
* Return the active codepage for Windows syscalls
*
* @return numeric codepage identifier
*/
GIT_EXTERN(unsigned int) gitwin_get_codepage(void);
/**
* Set the active Windows codepage to UTF-8 (this is
* the default value)
*/
GIT_EXTERN(void) gitwin_set_utf8(void);
/** @} */
GIT_END_DECL
#endif
......@@ -54,11 +54,10 @@ int git_futils_creat_locked(const char *path, const mode_t mode)
int fd;
#ifdef GIT_WIN32
wchar_t* buf;
wchar_t buf[GIT_WIN_PATH];
buf = gitwin_to_utf16(path);
git__utf8_to_16(buf, GIT_WIN_PATH, path);
fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
git__free(buf);
#else
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
#endif
......@@ -382,16 +381,16 @@ static int win32_expand_path(struct win32_path *s_root, const wchar_t *templ)
static int win32_find_file(git_buf *path, const struct win32_path *root, const char *filename)
{
int error = 0;
size_t len;
size_t len, alloc_len;
wchar_t *file_utf16 = NULL;
char *file_utf8 = NULL;
char file_utf8[GIT_PATH_MAX];
if (!root || !filename || (len = strlen(filename)) == 0)
return GIT_ENOTFOUND;
/* allocate space for wchar_t path to file */
file_utf16 = git__calloc(root->len + len + 2, sizeof(wchar_t));
alloc_len = root->len + len + 2;
file_utf16 = git__calloc(alloc_len, sizeof(wchar_t));
GITERR_CHECK_ALLOC(file_utf16);
/* append root + '\\' + filename as wchar_t */
......@@ -400,29 +399,20 @@ static int win32_find_file(git_buf *path, const struct win32_path *root, const c
if (*filename == '/' || *filename == '\\')
filename++;
if (gitwin_append_utf16(file_utf16 + root->len - 1, filename, len + 1) !=
(int)len + 1) {
error = -1;
goto cleanup;
}
git__utf8_to_16(file_utf16 + root->len - 1, alloc_len, filename);
/* check access */
if (_waccess(file_utf16, F_OK) < 0) {
error = GIT_ENOTFOUND;
goto cleanup;
git__free(file_utf16);
return GIT_ENOTFOUND;
}
/* convert to utf8 */
if ((file_utf8 = gitwin_from_utf16(file_utf16)) == NULL)
error = -1;
else {
git_path_mkposix(file_utf8);
git_buf_attach(path, file_utf8, 0);
}
git__utf16_to_8(file_utf8, file_utf16);
git_path_mkposix(file_utf8);
git_buf_sets(path, file_utf8);
cleanup:
git__free(file_utf16);
return error;
return 0;
}
#endif
......
......@@ -432,14 +432,14 @@ bool git_path_is_empty_dir(const char *path)
{
git_buf pathbuf = GIT_BUF_INIT;
HANDLE hFind = INVALID_HANDLE_VALUE;
wchar_t *wbuf;
wchar_t wbuf[GIT_WIN_PATH];
WIN32_FIND_DATAW ffd;
bool retval = true;
if (!git_path_isdir(path)) return false;
git_buf_printf(&pathbuf, "%s\\*", path);
wbuf = gitwin_to_utf16(git_buf_cstr(&pathbuf));
git__utf8_to_16(wbuf, GIT_WIN_PATH, git_buf_cstr(&pathbuf));
hFind = FindFirstFileW(wbuf, &ffd);
if (INVALID_HANDLE_VALUE == hFind) {
......@@ -455,7 +455,6 @@ bool git_path_is_empty_dir(const char *path)
FindClose(hFind);
git_buf_free(&pathbuf);
git__free(wbuf);
return retval;
}
......
......@@ -7,7 +7,6 @@
#define GIT__WIN32_NO_WRAP_DIR
#include "dir.h"
#include "utf-conv.h"
#include "git2/windows.h"
static int init_filter(char *filter, size_t n, const char *dir)
{
......@@ -26,8 +25,8 @@ static int init_filter(char *filter, size_t n, const char *dir)
git__DIR *git__opendir(const char *dir)
{
char filter[4096];
wchar_t* filter_w = NULL;
char filter[GIT_WIN_PATH];
wchar_t filter_w[GIT_WIN_PATH];
git__DIR *new = NULL;
if (!dir || !init_filter(filter, sizeof(filter), dir))
......@@ -41,12 +40,8 @@ git__DIR *git__opendir(const char *dir)
if (!new->dir)
goto fail;
filter_w = gitwin_to_utf16(filter);
if (!filter_w)
goto fail;
git__utf8_to_16(filter_w, GIT_WIN_PATH, filter);
new->h = FindFirstFileW(filter_w, &new->f);
git__free(filter_w);
if (new->h == INVALID_HANDLE_VALUE) {
giterr_set(GITERR_OS, "Could not open directory '%s'", dir);
......@@ -85,16 +80,9 @@ int git__readdir_ext(
if (wcslen(d->f.cFileName) >= sizeof(entry->d_name))
return -1;
git__utf16_to_8(entry->d_name, d->f.cFileName);
entry->d_ino = 0;
if (WideCharToMultiByte(
gitwin_get_codepage(), 0, d->f.cFileName, -1,
entry->d_name, GIT_PATH_MAX, NULL, NULL) == 0)
{
giterr_set(GITERR_OS, "Could not convert filename to UTF-8");
return -1;
}
*result = entry;
if (is_dir != NULL)
......@@ -113,8 +101,8 @@ struct git__dirent *git__readdir(git__DIR *d)
void git__rewinddir(git__DIR *d)
{
char filter[4096];
wchar_t* filter_w;
char filter[GIT_WIN_PATH];
wchar_t filter_w[GIT_WIN_PATH];
if (!d)
return;
......@@ -125,12 +113,11 @@ void git__rewinddir(git__DIR *d)
d->first = 0;
}
if (!init_filter(filter, sizeof(filter), d->dir) ||
(filter_w = gitwin_to_utf16(filter)) == NULL)
if (!init_filter(filter, sizeof(filter), d->dir))
return;
git__utf8_to_16(filter_w, GIT_WIN_PATH, filter);
d->h = FindFirstFileW(filter_w, &d->f);
git__free(filter_w);
if (d->h == INVALID_HANDLE_VALUE)
giterr_set(GITERR_OS, "Could not open directory '%s'", d->dir);
......
......@@ -21,13 +21,10 @@ GIT_INLINE(int) p_link(const char *old, const char *new)
GIT_INLINE(int) p_mkdir(const char *path, mode_t mode)
{
wchar_t* buf = gitwin_to_utf16(path);
int ret = _wmkdir(buf);
wchar_t buf[GIT_WIN_PATH];
GIT_UNUSED(mode);
git__free(buf);
return ret;
git__utf8_to_16(buf, GIT_WIN_PATH, path);
return _wmkdir(buf);
}
extern int p_unlink(const char *path);
......
......@@ -15,16 +15,10 @@
int p_unlink(const char *path)
{
int ret = 0;
wchar_t* buf;
if ((buf = gitwin_to_utf16(path)) != NULL) {
_wchmod(buf, 0666);
ret = _wunlink(buf);
git__free(buf);
}
return ret;
wchar_t buf[GIT_WIN_PATH];
git__utf8_to_16(buf, GIT_WIN_PATH, path);
_wchmod(buf, 0666);
return _wunlink(buf);
}
int p_fsync(int fd)
......@@ -61,10 +55,10 @@ GIT_INLINE(time_t) filetime_to_time_t(const FILETIME *ft)
static int do_lstat(const char *file_name, struct stat *buf)
{
WIN32_FILE_ATTRIBUTE_DATA fdata;
wchar_t fbuf[GIT_WIN_PATH];
DWORD last_error;
wchar_t* fbuf = gitwin_to_utf16(file_name);
if (!fbuf)
return -1;
git__utf8_to_16(fbuf, GIT_WIN_PATH, file_name);
if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) {
int fMode = S_IREAD;
......@@ -90,8 +84,6 @@ static int do_lstat(const char *file_name, struct stat *buf)
buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
git__free(fbuf);
return 0;
}
......@@ -101,7 +93,6 @@ static int do_lstat(const char *file_name, struct stat *buf)
else if (last_error == ERROR_PATH_NOT_FOUND)
errno = ENOTDIR;
git__free(fbuf);
return -1;
}
......@@ -143,7 +134,7 @@ int p_readlink(const char *link, char *target, size_t target_len)
static fpath_func pGetFinalPath = NULL;
HANDLE hFile;
DWORD dwRet;
wchar_t* link_w;
wchar_t link_w[GIT_WIN_PATH];
wchar_t* target_w;
int error = 0;
......@@ -166,8 +157,7 @@ int p_readlink(const char *link, char *target, size_t target_len)
}
}
link_w = gitwin_to_utf16(link);
GITERR_CHECK_ALLOC(link_w);
git__utf8_to_16(link_w, GIT_WIN_PATH, link);
hFile = CreateFileW(link_w, // file to open
GENERIC_READ, // open for reading
......@@ -177,8 +167,6 @@ int p_readlink(const char *link, char *target, size_t target_len)
FILE_FLAG_BACKUP_SEMANTICS, // normal file
NULL); // no attr. template
git__free(link_w);
if (hFile == INVALID_HANDLE_VALUE) {
giterr_set(GITERR_OS, "Cannot open '%s' for reading", link);
return -1;
......@@ -235,16 +223,12 @@ int p_symlink(const char *old, const char *new)
int p_open(const char *path, int flags, ...)
{
int fd;
wchar_t* buf;
wchar_t buf[GIT_WIN_PATH];
mode_t mode = 0;
buf = gitwin_to_utf16(path);
if (!buf)
return -1;
git__utf8_to_16(buf, GIT_WIN_PATH, path);
if (flags & O_CREAT)
{
if (flags & O_CREAT) {
va_list arg_list;
va_start(arg_list, flags);
......@@ -252,27 +236,20 @@ int p_open(const char *path, int flags, ...)
va_end(arg_list);
}
fd = _wopen(buf, flags | _O_BINARY, mode);
git__free(buf);
return fd;
return _wopen(buf, flags | _O_BINARY, mode);
}
int p_creat(const char *path, mode_t mode)
{
int fd;
wchar_t* buf = gitwin_to_utf16(path);
if (!buf)
return -1;
fd = _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode);
git__free(buf);
return fd;
wchar_t buf[GIT_WIN_PATH];
git__utf8_to_16(buf, GIT_WIN_PATH, path);
return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode);
}
int p_getcwd(char *buffer_out, size_t size)
{
int ret;
wchar_t* buf;
wchar_t *buf;
if ((size_t)((int)size) != size)
return -1;
......@@ -296,64 +273,43 @@ int p_stat(const char* path, struct stat* buf)
int p_chdir(const char* path)
{
wchar_t* buf = gitwin_to_utf16(path);
int ret;
if (!buf)
return -1;
ret = _wchdir(buf);
git__free(buf);
return ret;
wchar_t buf[GIT_WIN_PATH];
git__utf8_to_16(buf, GIT_WIN_PATH, path);
return _wchdir(buf);
}
int p_chmod(const char* path, mode_t mode)
{
wchar_t* buf = gitwin_to_utf16(path);
int ret;
if (!buf)
return -1;
ret = _wchmod(buf, mode);
git__free(buf);
return ret;
wchar_t buf[GIT_WIN_PATH];
git__utf8_to_16(buf, GIT_WIN_PATH, path);
return _wchmod(buf, mode);
}
int p_rmdir(const char* path)
{
wchar_t* buf = gitwin_to_utf16(path);
int ret;
if (!buf)
return -1;
ret = _wrmdir(buf);
git__free(buf);
return ret;
wchar_t buf[GIT_WIN_PATH];
git__utf8_to_16(buf, GIT_WIN_PATH, path);
return _wrmdir(buf);
}
int p_hide_directory__w32(const char *path)
{
int res;
wchar_t* buf = gitwin_to_utf16(path);
if (!buf)
return -1;
res = SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN);
git__free(buf);
return (res != 0) ? 0 : -1; /* MSDN states a "non zero" value indicates a success */
wchar_t buf[GIT_WIN_PATH];
git__utf8_to_16(buf, GIT_WIN_PATH, path);
return (SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN) != 0) ? 0 : -1;
}
char *p_realpath(const char *orig_path, char *buffer)
{
int ret, buffer_sz = 0;
wchar_t* orig_path_w = gitwin_to_utf16(orig_path);
wchar_t* buffer_w = (wchar_t*)git__malloc(GIT_PATH_MAX * sizeof(wchar_t));
if (!orig_path_w || !buffer_w)
return NULL;
wchar_t orig_path_w[GIT_WIN_PATH];
wchar_t buffer_w[GIT_WIN_PATH];
ret = GetFullPathNameW(orig_path_w, GIT_PATH_MAX, buffer_w, NULL);
git__free(orig_path_w);
git__utf8_to_16(orig_path_w, GIT_WIN_PATH, orig_path);
ret = GetFullPathNameW(orig_path_w, GIT_WIN_PATH, buffer_w, NULL);
/* According to MSDN, a return value equals to zero means a failure. */
if (ret == 0 || ret > GIT_PATH_MAX) {
if (ret == 0 || ret > GIT_WIN_PATH) {
buffer = NULL;
goto done;
}
......@@ -376,8 +332,7 @@ char *p_realpath(const char *orig_path, char *buffer)
}
}
if (!git_path_exists(buffer))
{
if (!git_path_exists(buffer)) {
if (buffer_sz > 0)
git__free(buffer);
......@@ -386,9 +341,9 @@ char *p_realpath(const char *orig_path, char *buffer)
}
done:
git__free(buffer_w);
if (buffer)
git_path_mkposix(buffer);
return buffer;
}
......@@ -443,32 +398,19 @@ int p_setenv(const char* name, const char* value, int overwrite)
int p_access(const char* path, mode_t mode)
{
wchar_t *buf = gitwin_to_utf16(path);
int ret;
if (!buf)
return -1;
ret = _waccess(buf, mode);
git__free(buf);
return ret;
wchar_t buf[GIT_WIN_PATH];
git__utf8_to_16(buf, GIT_WIN_PATH, path);
return _waccess(buf, mode);
}
int p_rename(const char *from, const char *to)
{
wchar_t *wfrom = gitwin_to_utf16(from);
wchar_t *wto = gitwin_to_utf16(to);
int ret;
if (!wfrom || !wto)
return -1;
ret = MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1;
git__free(wfrom);
git__free(wto);
wchar_t wfrom[GIT_WIN_PATH];
wchar_t wto[GIT_WIN_PATH];
return ret;
git__utf8_to_16(wfrom, GIT_WIN_PATH, from);
git__utf8_to_16(wto, GIT_WIN_PATH, to);
return MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1;
}
int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
......
......@@ -7,86 +7,75 @@
#include "common.h"
#include "utf-conv.h"
#include "git2/windows.h"
/*
* Default codepage value
*/
static int _active_codepage = CP_UTF8;
void gitwin_set_codepage(unsigned int codepage)
{
_active_codepage = codepage;
}
unsigned int gitwin_get_codepage(void)
{
return _active_codepage;
}
void gitwin_set_utf8(void)
{
_active_codepage = CP_UTF8;
}
#define U16_LEAD(c) (wchar_t)(((c)>>10)+0xd7c0)
#define U16_TRAIL(c) (wchar_t)(((c)&0x3ff)|0xdc00)
wchar_t* gitwin_to_utf16(const char* str)
#if 0
void git__utf8_to_16(wchar_t *dest, size_t length, const char *src)
{
wchar_t* ret;
int cb;
if (!str)
return NULL;
cb = MultiByteToWideChar(_active_codepage, 0, str, -1, NULL, 0);
if (cb == 0)
return (wchar_t *)git__calloc(1, sizeof(wchar_t));
ret = (wchar_t *)git__malloc(cb * sizeof(wchar_t));
if (!ret)
return NULL;
if (MultiByteToWideChar(_active_codepage, 0, str, -1, ret, (int)cb) == 0) {
giterr_set(GITERR_OS, "Could not convert string to UTF-16");
git__free(ret);
ret = NULL;
wchar_t *pDest = dest;
uint32_t ch;
const uint8_t* pSrc = (uint8_t*) src;
assert(dest && src && length);
length--;
while(*pSrc && length > 0) {
ch = *pSrc++;
length--;
if(ch < 0xc0) {
/*
* ASCII, or a trail byte in lead position which is treated like
* a single-byte sequence for better character boundary
* resynchronization after illegal sequences.
*/
*pDest++ = (wchar_t)ch;
continue;
} else if(ch < 0xe0) { /* U+0080..U+07FF */
if (pSrc[0]) {
/* 0x3080 = (0xc0 << 6) + 0x80 */
*pDest++ = (wchar_t)((ch << 6) + *pSrc++ - 0x3080);
continue;
}
} else if(ch < 0xf0) { /* U+0800..U+FFFF */
if (pSrc[0] && pSrc[1]) {
/* no need for (ch & 0xf) because the upper bits are truncated after <<12 in the cast to (UChar) */
/* 0x2080 = (0x80 << 6) + 0x80 */
ch = (ch << 12) + (*pSrc++ << 6);
*pDest++ = (wchar_t)(ch + *pSrc++ - 0x2080);
continue;
}
} else /* f0..f4 */ { /* U+10000..U+10FFFF */
if (length >= 1 && pSrc[0] && pSrc[1] && pSrc[2]) {
/* 0x3c82080 = (0xf0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */
ch = (ch << 18) + (*pSrc++ << 12);
ch += *pSrc++ << 6;
ch += *pSrc++ - 0x3c82080;
*(pDest++) = U16_LEAD(ch);
*(pDest++) = U16_TRAIL(ch);
length--; /* two bytes for this character */
continue;
}
}
/* truncated character at the end */
*pDest++ = 0xfffd;
break;
}
return ret;
*pDest++ = 0x0;
}
#endif
int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len)
void git__utf8_to_16(wchar_t *dest, size_t length, const char *src)
{
int result = MultiByteToWideChar(
_active_codepage, 0, str, -1, buffer, (int)len);
if (result == 0)
giterr_set(GITERR_OS, "Could not convert string to UTF-16");
return result;
MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, length);
}
char* gitwin_from_utf16(const wchar_t* str)
void git__utf16_to_8(char *out, const wchar_t *input)
{
char* ret;
int cb;
if (!str)
return NULL;
cb = WideCharToMultiByte(_active_codepage, 0, str, -1, NULL, 0, NULL, NULL);
if (cb == 0)
return (char *)git__calloc(1, sizeof(char));
ret = (char*)git__malloc(cb);
if (!ret)
return NULL;
if (WideCharToMultiByte(
_active_codepage, 0, str, -1, ret, (int)cb, NULL, NULL) == 0)
{
giterr_set(GITERR_OS, "Could not convert string to UTF-8");
git__free(ret);
ret = NULL;
}
return ret;
WideCharToMultiByte(CP_UTF8, 0, input, -1, out, GIT_WIN_PATH, NULL, NULL);
}
......@@ -10,9 +10,10 @@
#ifndef INCLUDE_git_utfconv_h__
#define INCLUDE_git_utfconv_h__
wchar_t* gitwin_to_utf16(const char* str);
int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len);
char* gitwin_from_utf16(const wchar_t* str);
#define GIT_WIN_PATH (260 + 1)
void git__utf8_to_16(wchar_t *dest, size_t length, const char *src);
void git__utf16_to_8(char *dest, const wchar_t *src);
#endif
......@@ -56,22 +56,23 @@ void cl_git_rewritefile(const char *filename, const char *new_content)
char *cl_getenv(const char *name)
{
wchar_t *name_utf16 = gitwin_to_utf16(name);
DWORD value_len, alloc_len;
wchar_t name_utf16[GIT_WIN_PATH];
DWORD alloc_len;
wchar_t *value_utf16;
char *value_utf8;
cl_assert(name_utf16);
git__utf8_to_16(name_utf16, GIT_WIN_PATH, name);
alloc_len = GetEnvironmentVariableW(name_utf16, NULL, 0);
if (alloc_len <= 0)
return NULL;
alloc_len = GIT_WIN_PATH;
cl_assert(value_utf16 = git__calloc(alloc_len, sizeof(wchar_t)));
value_len = GetEnvironmentVariableW(name_utf16, value_utf16, alloc_len);
cl_assert_equal_i(value_len, alloc_len - 1);
GetEnvironmentVariableW(name_utf16, value_utf16, alloc_len);
cl_assert(value_utf8 = gitwin_from_utf16(value_utf16));
cl_assert(value_utf8 = git__malloc(alloc_len));
git__utf16_to_8(value_utf8, value_utf16);
git__free(value_utf16);
......@@ -80,17 +81,16 @@ char *cl_getenv(const char *name)
int cl_setenv(const char *name, const char *value)
{
wchar_t *name_utf16 = gitwin_to_utf16(name);
wchar_t *value_utf16 = value ? gitwin_to_utf16(value) : NULL;
wchar_t name_utf16[GIT_WIN_PATH];
wchar_t value_utf16[GIT_WIN_PATH];
cl_assert(name_utf16);
cl_assert(SetEnvironmentVariableW(name_utf16, value_utf16));
git__utf8_to_16(name_utf16, GIT_WIN_PATH, name);
git__free(name_utf16);
git__free(value_utf16);
if (value != NULL)
git__utf8_to_16(value_utf16, GIT_WIN_PATH, value);
cl_assert(SetEnvironmentVariableW(name_utf16, value ? value_utf16 : NULL));
return 0;
}
#else
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment