From 9ec8d7082272c6b36ef6884bb977a4386c745e8d Mon Sep 17 00:00:00 2001 From: Klaas Freitag Date: Wed, 9 May 2012 16:55:56 +0200 Subject: [PATCH] Do proper handling of Win32 special characters. Introduced a tchar based handling of wide character encoding that is happening on Windows platforms. --- src/std/c_dir.c | 69 +++++++++++++++------------ src/std/c_file.c | 11 +++-- src/std/c_macro.h | 2 +- src/std/c_private.h | 50 +++++++++++++++++--- src/std/c_string.c | 73 +++++++++++++++++++++++++++++ src/std/c_string.h | 35 ++++++++++++++ src/vio/csync_vio.c | 16 +++++-- src/vio/csync_vio_local.c | 99 +++++++++++++++++++++++++++++---------- 8 files changed, 285 insertions(+), 70 deletions(-) diff --git a/src/std/c_dir.c b/src/std/c_dir.c index 245c4e9bf..80bbd0858 100644 --- a/src/std/c_dir.c +++ b/src/std/c_dir.c @@ -10,24 +10,26 @@ #include "c_macro.h" #include "c_alloc.h" #include "c_dir.h" -#include "c_private.h" +#include "c_string.h" int c_mkdirs(const char *path, mode_t mode) { int tmp; csync_stat_t sb; - + const _TCHAR *wpath = c_multibyte(path); + const _TCHAR *swpath = NULL; + if (path == NULL) { errno = EINVAL; return -1; } - if (lstat(path, &sb) == 0) { + if (_tstat(wpath, &sb) == 0) { if (! S_ISDIR(sb.st_mode)) { errno = ENOTDIR; return -1; } } - + tmp = strlen(path); while(tmp > 0 && path[tmp - 1] == '/') --tmp; while(tmp > 0 && path[tmp - 1] != '/') --tmp; @@ -37,41 +39,46 @@ int c_mkdirs(const char *path, mode_t mode) { char subpath[tmp + 1]; memcpy(subpath, path, tmp); subpath[tmp] = '\0'; - - if (lstat(subpath, &sb) == 0) { + swpath = c_multibyte(subpath); + if (_tstat(swpath, &sb) == 0) { if (! S_ISDIR(sb.st_mode)) { errno = ENOTDIR; return -1; } } else if (errno != ENOENT) { + c_free_multibyte(swpath); return -1; } else if (c_mkdirs(subpath, mode) < 0) { + c_free_multibyte(swpath); return -1; } } - -#ifndef _WIN32 - tmp = mkdir(path, mode); +#ifdef _WIN32 + tmp = _tmkdir(wpath); #else - tmp = mkdir(path); + tmp = _tmkdir(wpath, mode); #endif + c_free_multibyte(swpath); + c_free_multibyte(wpath); + if ((tmp < 0) && (errno == EEXIST)) { return 0; } - return tmp; } int c_rmdirs(const char *path) { - DIR *d; - struct dirent *dp; + _TDIR *d; + struct _tdirent *dp; csync_stat_t sb; - char *fname; - - if ((d = opendir(path)) != NULL) { - while(stat(path, &sb) == 0) { + char *fname = NULL; + const _TCHAR *wfname = NULL; + const _TCHAR *wpath = c_multibyte(path); + + if ((d = _topendir(wpath)) != NULL) { + while( _tstat(wpath, &sb) == 0) { /* if we can remove the directory we're done */ - if (rmdir(path) == 0) { + if (_trmdir(path) == 0) { break; } switch (errno) { @@ -80,11 +87,11 @@ int c_rmdirs(const char *path) { case EBADF: break; /* continue */ default: - closedir(d); + _tclosedir(d); return 0; } - while ((dp = readdir(d)) != NULL) { + while ((dp = _treaddir(d)) != NULL) { size_t len; /* skip '.' and '..' */ if (dp->d_name[0] == '.' && @@ -93,49 +100,53 @@ int c_rmdirs(const char *path) { continue; } - len = strlen(path) + strlen(dp->d_name) + 2; + len = strlen(path) + _tcslen(dp->d_name) + 2; fname = c_malloc(len); if (fname == NULL) { return -1; } snprintf(fname, len, "%s/%s", path, dp->d_name); - + wfname = c_multibyte(fname); + /* stat the file */ - if (lstat(fname, &sb) != -1) { + if (_tstat(wfname, &sb) != -1) { #ifdef __unix__ if (S_ISDIR(sb.st_mode) && !S_ISLNK(sb.st_mode)) { #else if (S_ISDIR(sb.st_mode)) { #endif - if (rmdir(fname) < 0) { /* can't be deleted */ + if (_trmdir(wfname) < 0) { /* can't be deleted */ if (errno == EACCES) { - closedir(d); + _tclosedir(d); SAFE_FREE(fname); + c_free_multibyte(wfname); return -1; } c_rmdirs(fname); } } else { - unlink(fname); + _tunlink(wfname); } } /* lstat */ SAFE_FREE(fname); + c_free_multibyte(wfname); } /* readdir */ - rewinddir(d); + _trewinddir(d); } } else { return -1; } - closedir(d); + _tclosedir(d); return 0; } int c_isdir(const char *path) { csync_stat_t sb; + const _TCHAR *wpath = c_multibyte(path); - if (lstat (path, &sb) == 0 && S_ISDIR(sb.st_mode)) { + if (_tstat (wpath, &sb) == 0 && S_ISDIR(sb.st_mode)) { return 1; } diff --git a/src/std/c_file.c b/src/std/c_file.c index a07a882db..1c0469701 100644 --- a/src/std/c_file.c +++ b/src/std/c_file.c @@ -39,8 +39,11 @@ /* check if path is a file */ int c_isfile(const char *path) { csync_stat_t sb; - - if (lstat (path, &sb) < 0) { + const _TCHAR *wpath = c_multibyte(path); + int re = _tstat(wpath, &sb); + c_free_multibyte(wpath); + + if (re< 0) { return 0; } @@ -73,8 +76,9 @@ int c_copy(const char* src, const char *dst, mode_t mode) { return -1; } -#endif +#else + /* Win32 does not come here. */ if (c_streq(src, dst)) { return -1; } @@ -146,5 +150,6 @@ out: unlink(dst); } return rc; +#endif } diff --git a/src/std/c_macro.h b/src/std/c_macro.h index 56702e46e..5d4cca383 100644 --- a/src/std/c_macro.h +++ b/src/std/c_macro.h @@ -43,7 +43,7 @@ #define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0) /** Free memory and zero the pointer */ -#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0) +#define SAFE_FREE(x) do { if ((x) != NULL) {free((void*)x); x=NULL;} } while(0) /** Get the smaller value */ #define MIN(a,b) ((a) < (b) ? (a) : (b)) diff --git a/src/std/c_private.h b/src/std/c_private.h index 2c2989d56..155865475 100644 --- a/src/std/c_private.h +++ b/src/std/c_private.h @@ -64,18 +64,56 @@ typedef struct stat csync_stat_t; #define lstat _stat #endif #ifdef _WIN32 -#define stat _stat -#define fstat _fstat -#define read _read -#define open _open -#define close _close -#define write _write +#define fstat _fstat #endif #ifndef O_NOATIME #define O_NOATIME 0 #endif +/* tchar definitions for clean win32 filenames */ +#define _UNICODE + +#if defined _WIN32 && defined _UNICODE +typedef wchar_t _TCHAR; +#define _tcslen wcslen +#define _topen _wopen +#define _tdirent _wdirent +#define _TDIR _WDIR +#define _topendir _wopendir +#define _tclosedir _wclosedir +#define _treaddir _wreaddir +#define _trewinddir _wrewinddir +#define _ttelldir _wtelldir +#define _tseekdir _wseekdir +#define _tcreat _wcreat +#define _tstat _wstat +#define _tunlink _wunlink +#define _tmkdir _wmkdir +#define _trmdir _rmdir +#define _tchmod _wchmod +#define _trewinddir _wrewinddir +#else +typedef char _TCHAR; +#define _tdirent dirent +#define _tcslen strlen +#define _topen open +#define _TDIR DIR +#define _topendir opendir +#define _tclosedir closedir +#define _treaddir readdir +#define _trewinddir rewinddir +#define _ttelldir telldir +#define _tseekdir seekdir +#define _tcreat creat +#define _tstat stat +#define _tunlink unlink +#define _tmkdir mkdir +#define _trmdir rmdir +#define _tchmod chmod +#define _trewinddir rewinddir +#endif + #endif //_C_PRIVATE_H /* vim: set ft=c.doxygen ts=8 sw=2 et cindent: */ diff --git a/src/std/c_string.c b/src/std/c_string.c index c43df21e9..2c425364e 100644 --- a/src/std/c_string.c +++ b/src/std/c_string.c @@ -25,10 +25,20 @@ #include #include +#include +#include +#include + +#include "config.h" + #include "c_string.h" #include "c_alloc.h" #include "c_macro.h" +#ifdef _WIN32 +#include +#endif + int c_streq(const char *a, const char *b) { register const char *s1 = a; register const char *s2 = b; @@ -186,3 +196,66 @@ char *c_lowercase(const char* str) { return new; } +/* Convert a wide multibyte String to UTF8 */ +const char* c_utf8(const _TCHAR *wstr) +{ + const char *dst = NULL; + +#ifdef _WIN32 + char *mdst = NULL; + + if(!wstr) return NULL; + size_t len = _tcslen( wstr ); + /* Call once to get the required size. */ + int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, len, NULL, 0, NULL, NULL); + if( size_needed > 0 ) { + mdst = c_malloc(1+size_needed); + memset( mdst, 0, 1+size_needed); + WideCharToMultiByte(CP_UTF8, 0, wstr, len, mdst, size_needed, NULL, NULL); + dst = mdst; + } +#else + dst = wstr; +#endif + return dst; +} + +/* Convert a an UTF8 string to multibyte */ +const _TCHAR* c_multibyte(const char *str) +{ +#ifdef _WIN32 + _TCHAR *wstrTo = NULL; + if(!str) return NULL; + + size_t len = strlen( str ); + int size_needed = MultiByteToWideChar(CP_UTF8, 0, str, len, NULL, 0); + if(size_needed > 0) { + int size_char = (size_needed+1)*sizeof(_TCHAR); + wstrTo = c_malloc(size_char); + memset( (void*)wstrTo, 0, size_char); + MultiByteToWideChar(CP_UTF8, 0, str, -1, wstrTo, size_needed); + } + return wstrTo; +#else + return str; +#endif +} + +void c_free_utf8(char* buf) +{ +#ifdef _WIN32 + SAFE_FREE(buf); +#else + (void)buf; +#endif +} + +void c_free_multibyte(const _TCHAR* buf) +{ +#ifdef _WIN32 + SAFE_FREE(buf); +#else + (void)buf; +#endif +} + diff --git a/src/std/c_string.h b/src/std/c_string.h index c9244f77e..8a8ecdf67 100644 --- a/src/std/c_string.h +++ b/src/std/c_string.h @@ -33,6 +33,8 @@ #ifndef _C_STR_H #define _C_STR_H +#include "c_private.h" + struct c_strlist_s; typedef struct c_strlist_s c_strlist_t; /** @@ -137,6 +139,39 @@ char *c_uppercase(const char* str); */ char *c_lowercase(const char* str); +/** + * @brief Convert a multibyte string to utf8 (Win32). + * + * @param str The multibyte encoded string to convert + * + * @return The malloced converted string or NULL on error. + */ +const char* c_utf8(const _TCHAR *str); + +/** + * @brief Convert a utf8 encoded string to multibyte (Win32). + * + * @param str The utf8 string to convert. + * + * @return The malloced converted multibyte string or NULL on error. + */ +const _TCHAR* c_multibyte(const char *wstr); + +/** + * @brief Free buffer malloced by c_utf8. + * + * @param buf The buffer to free. + * + */ +void c_free_utf8(char* buf); + +/** + * @brief Free buffer malloced by c_multibyte. + * + * @param buf The buffer to free. + */ +void c_free_multibyte(const _TCHAR* buf); + /** * }@ */ diff --git a/src/vio/csync_vio.c b/src/vio/csync_vio.c index 7610b97dc..c6641a727 100644 --- a/src/vio/csync_vio.c +++ b/src/vio/csync_vio.c @@ -53,27 +53,33 @@ int csync_vio_init(CSYNC *ctx, const char *module, const char *args) { char *err = NULL; csync_vio_method_t *m = NULL; csync_vio_method_init_fn init_fn; - + const _TCHAR *mpath = NULL; + + if (asprintf(&path, "%s/csync_%s.%s", PLUGINDIR, module, MODULE_EXTENSION) < 0) { return -1; } - if (lstat(path, &sb) < 0) { + mpath = c_multibyte(path); + if (_tstat(mpath, &sb) < 0) { SAFE_FREE(path); if (asprintf(&path, "%s/modules/csync_%s.%s", BINARYDIR, module, MODULE_EXTENSION) < 0) { return -1; } } - + c_free_multibyte(mpath); + #ifdef _WIN32 - if (lstat(path, &sb) < 0) { + mpath = c_multibyte(path); + if (_tstat(mpath, &sb) < 0) { SAFE_FREE(path); if (asprintf(&path, "modules/csync_%s.%s", module, MODULE_EXTENSION) < 0) { return -1; } } + c_free_multibyte(mpath); #endif - + #ifdef __APPLE__ if (lstat(path, &sb) < 0) { SAFE_FREE(path); diff --git a/src/vio/csync_vio_local.c b/src/vio/csync_vio_local.c index e4dd4e995..62a01dd0e 100644 --- a/src/vio/csync_vio_local.c +++ b/src/vio/csync_vio_local.c @@ -27,37 +27,51 @@ #include #include +#ifdef _WIN32 +#include "windows.h" +#define _UNICODE +#endif + #include "c_private.h" #include "c_lib.h" +#include "c_string.h" + #include "vio/csync_vio_local.h" typedef struct fhandle_s { int fd; } fhandle_t; + +/* the url comes in as utf-8 and in windows, it needs to be multibyte. */ csync_vio_method_handle_t *csync_vio_local_open(const char *durl, int flags, mode_t mode) { fhandle_t *handle = NULL; int fd = -1; - - if ((fd = open(durl, flags, mode)) < 0) { + const _TCHAR *url = c_multibyte(durl); + + if ((fd = _topen(url, flags, mode)) < 0) { return NULL; } - + handle = c_malloc(sizeof(fhandle_t)); if (handle == NULL) { return NULL; } handle->fd = fd; + + c_free_multibyte(url); + return (csync_vio_method_handle_t *) handle; } csync_vio_method_handle_t *csync_vio_local_creat(const char *durl, mode_t mode) { fhandle_t *handle = NULL; int fd = -1; + const _TCHAR *url = c_multibyte(durl); - if ((fd = creat(durl, mode)) < 0) { - return NULL; + if(( fd = _tcreat( url, mode)) < 0) { + return NULL; } handle = c_malloc(sizeof(fhandle_t)); @@ -66,6 +80,7 @@ csync_vio_method_handle_t *csync_vio_local_creat(const char *durl, mode_t mode) } handle->fd = fd; + c_free_multibyte(url); return (csync_vio_method_handle_t *) handle; } @@ -136,24 +151,28 @@ off_t csync_vio_local_lseek(csync_vio_method_handle_t *fhandle, off_t offset, in */ typedef struct dhandle_s { - DIR *dh; + _TDIR *dh; char *path; } dhandle_t; csync_vio_method_handle_t *csync_vio_local_opendir(const char *name) { dhandle_t *handle = NULL; - + const _TCHAR *dirname = c_multibyte(name); handle = c_malloc(sizeof(dhandle_t)); if (handle == NULL) { return NULL; } - handle->dh = opendir(name); + handle->dh = _topendir( dirname ); if (handle->dh == NULL) { SAFE_FREE(handle); return NULL; } - handle->path = c_strdup(name); +#ifdef _WIN32 + handle->path = c_utf8(dirname); +#else + handle->path = c_strdup(dirname); +#endif return (csync_vio_method_handle_t *) handle; } @@ -168,8 +187,7 @@ int csync_vio_local_closedir(csync_vio_method_handle_t *dhandle) { } handle = (dhandle_t *) dhandle; - - rc = closedir(handle->dh); + rc = _tclosedir(handle->dh); SAFE_FREE(handle->path); SAFE_FREE(handle); @@ -178,14 +196,15 @@ int csync_vio_local_closedir(csync_vio_method_handle_t *dhandle) { } csync_vio_file_stat_t *csync_vio_local_readdir(csync_vio_method_handle_t *dhandle) { - struct dirent *dirent = NULL; + struct _tdirent *dirent = NULL; + dhandle_t *handle = NULL; csync_vio_file_stat_t *file_stat = NULL; - + handle = (dhandle_t *) dhandle; errno = 0; - dirent = readdir(handle->dh); + dirent = _treaddir(handle->dh); if (dirent == NULL) { if (errno) { goto err; @@ -199,7 +218,11 @@ csync_vio_file_stat_t *csync_vio_local_readdir(csync_vio_method_handle_t *dhandl goto err; } +#ifdef _WIN32 + file_stat->name = c_utf8(dirent->d_name); +#else file_stat->name = c_strdup(dirent->d_name); +#endif file_stat->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE; #ifndef _WIN32 @@ -239,19 +262,28 @@ int csync_vio_local_mkdir(const char *uri, mode_t mode) { } int csync_vio_local_rmdir(const char *uri) { - return rmdir(uri); + const _TCHAR *dirname = c_multibyte(uri); + int re = -1; + + re = _trmdir(dirname); + c_free_multibyte(dirname); + return re; } + int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) { csync_stat_t sb; - - if (lstat(uri, &sb) < 0) { + const _TCHAR *wuri = c_multibyte( uri ); + if( _tstat(wuri, &sb) < 0) { + c_free_multibyte(wuri); return -1; } - + buf->name = c_basename(uri); + if (buf->name == NULL) { csync_vio_file_stat_destroy(buf); + c_free_multibyte(wuri); return -1; } buf->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE; @@ -334,38 +366,53 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) { buf->ctime = sb.st_ctime; buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME; + c_free_multibyte(wuri); return 0; } int csync_vio_local_rename(const char *olduri, const char *newuri) { #ifdef _WIN32 - if(olduri && newuri) { - if (MoveFileEx(olduri, newuri, MOVEFILE_COPY_ALLOWED + MOVEFILE_REPLACE_EXISTING + MOVEFILE_WRITE_THROUGH )) { + const _TCHAR *nuri = c_multibyte(newuri); + const _TCHAR *ouri = c_multibyte(olduri); + + if(ouri && nuri) { + if (MoveFileExW(ouri, nuri, MOVEFILE_COPY_ALLOWED + MOVEFILE_REPLACE_EXISTING + MOVEFILE_WRITE_THROUGH )) { return 0; } errno = GetLastError(); } else { errno = ENOENT; } + c_free_multibyte(nuri); + c_free_multibyte(ouri); return -1; +#else + return rename(olduri, newuri); #endif - return rename(olduri, newuri); } int csync_vio_local_unlink(const char *uri) { - return unlink(uri); + const _TCHAR *nuri = c_multibyte(uri); + int re = _tunlink( nuri ); + c_free_multibyte(nuri); + return re; } int csync_vio_local_chmod(const char *uri, mode_t mode) { - return chmod(uri, mode); + const _TCHAR *nuri = c_multibyte(uri); + int re = -1; + + re = _tchmod(nuri, mode); + c_free_multibyte(nuri); + return re; } int csync_vio_local_chown(const char *uri, uid_t owner, gid_t group) { -#ifndef _WIN32 - return chown(uri, owner, group); +#ifdef _WIN32 + return 0; #else - return 0; + return chown(uri, owner, group); #endif }