Index: subversion/libsvn_subr/utf.c =================================================================== --- subversion/libsvn_subr/utf.c (revision 25597) +++ subversion/libsvn_subr/utf.c (working copy) @@ -32,6 +32,7 @@ #include "svn_utf.h" #include "utf_impl.h" #include "svn_private_config.h" +#include "win32_xlate.h" @@ -252,10 +253,12 @@ pool = apr_hash_pool_get(xlate_handle_hash); /* Try to create a handle. */ -#ifndef AS400 +#if defined( WIN32) + apr_err = svn_subr__win32_xlate_open(&handle, topage, frompage, pool); +#elif defined(AS400) + apr_err = apr_xlate_open(&handle, (int)topage, (int)frompage, pool); +#else apr_err = apr_xlate_open(&handle, topage, frompage, pool); -#else - apr_err = apr_xlate_open(&handle, (int)topage, (int)frompage, pool); #endif if (APR_STATUS_IS_EINVAL(apr_err) || APR_STATUS_IS_ENOTIMPL(apr_err)) @@ -454,6 +457,12 @@ svn_stringbuf_t **dest, apr_pool_t *pool) { +#ifdef WIN32 + apr_status_t apr_err; + + apr_err = svn_subr__win32_xlate_to_stringbuf(node->handle, src_data, + src_length, dest, pool); +#else apr_size_t buflen = src_length * 2; apr_status_t apr_err; apr_size_t srclen = src_length; @@ -507,6 +516,7 @@ (*dest)->len += ((buflen - (*dest)->len) - destlen); } while (! apr_err && srclen); +#endif /* If we exited the loop with an error, return the error. */ if (apr_err) Index: subversion/libsvn_subr/win32_xlate.c =================================================================== --- subversion/libsvn_subr/win32_xlate.c (revision 0) +++ subversion/libsvn_subr/win32_xlate.c (revision 0) @@ -0,0 +1,180 @@ +/* + * win32_xlate.c : Windows xlate stuff. + * + * ==================================================================== + * Copyright (c) 2007 CollabNet. All rights reserved. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://subversion.tigris.org/license-1.html. + * If newer versions of this license are posted there, you may use a + * newer version instead, at your option. + * + * This software consists of voluntary contributions made by many + * individuals. For exact contribution history, see the revision + * history and logs, available at http://subversion.tigris.org/. + * ==================================================================== + */ + +#ifdef WIN32 + +#include +#include + +#include +#include +#include + +#include "svn_pools.h" +#include "svn_string.h" +#include "svn_utf.h" + +#include "win32_xlate.h" + +typedef struct win32_xlate_t +{ + UINT from_page_id; + UINT to_page_id; +} win32_xlate_t; + +static apr_status_t +get_page_id_from_name(UINT *page_id_p, const char *page_name, apr_pool_t *pool) +{ + IMultiLanguage * pMLang = 0; + HRESULT hr; + MIMECSETINFO page_info; + WCHAR ucs2_page_name[128]; + + if (page_name == SVN_APR_DEFAULT_CHARSET) + { + *page_id_p = CP_ACP; + return APR_SUCCESS; + } + else if (page_name == SVN_APR_LOCALE_CHARSET) + { + OSVERSIONINFO ver_info; + ver_info.dwOSVersionInfoSize = sizeof(ver_info); + + /* CP_THREAD_ACP supported only on Windows 2000 and later.*/ + if (GetVersionEx(&ver_info) && ver_info.dwMajorVersion >= 5 + && ver_info.dwPlatformId == VER_PLATFORM_WIN32_NT) + { + *page_id_p = CP_THREAD_ACP; + return APR_SUCCESS; + } + + /* CP_THREAD_ACP isn't supported on current system, so get locale + encoding name from APR. */ + page_name = apr_os_locale_encoding(pool); + } + else if (!strcmp(page_name, "UTF-8")) + { + *page_id_p = CP_UTF8; + return APR_SUCCESS; + } + + hr = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, + &IID_IMultiLanguage, (void **)&pMLang); + + if (FAILED(hr)) + return APR_EGENERAL; + + /* Convert page name to wide string. */ + MultiByteToWideChar(CP_UTF8, 0, page_name, -1, ucs2_page_name, + sizeof(ucs2_page_name)/sizeof(ucs2_page_name[0])); + memset(&page_info, 0, sizeof(page_info)); + hr = pMLang->lpVtbl->GetCharsetInfo(pMLang, ucs2_page_name, &page_info); + if (FAILED(hr)) + { + pMLang->lpVtbl->Release(pMLang); + return APR_EINVAL; + } + + if (page_info.uiInternetEncoding) + *page_id_p = page_info.uiInternetEncoding; + else + *page_id_p = page_info.uiCodePage; + + pMLang->lpVtbl->Release(pMLang); + + return APR_SUCCESS; +} + +apr_status_t +svn_subr__win32_xlate_open(win32_xlate_t **xlate_p, const char *topage, + const char *frompage, apr_pool_t *pool) +{ + UINT from_page_id, to_page_id; + apr_status_t apr_err = APR_SUCCESS; + win32_xlate_t *xlate; + + CoInitialize(NULL); + + apr_err = get_page_id_from_name(&to_page_id, topage, pool); + if (apr_err == APR_SUCCESS) + apr_err = get_page_id_from_name(&from_page_id, frompage, pool); + + if (apr_err == APR_SUCCESS) + { + xlate = apr_palloc(pool, sizeof(*xlate)); + xlate->from_page_id = from_page_id; + xlate->to_page_id = to_page_id; + + *xlate_p = xlate; + } + + CoUninitialize(); + return apr_err; +} + +apr_status_t +svn_subr__win32_xlate_to_stringbuf(win32_xlate_t *handle, + const char *src_data, + apr_size_t src_length, + svn_stringbuf_t **dest, + apr_pool_t *pool) +{ + WCHAR * wide_str; + int retval, wide_size; + + *dest = svn_stringbuf_create("", pool); + + if (src_length == 0) + return APR_SUCCESS; + + retval = MultiByteToWideChar(handle->from_page_id, 0, src_data, src_length, + NULL, 0); + if (retval == 0) + return apr_get_os_error(); + + /* ### FIXME: Place for optimization. We can try allocate small strings + temporary buffers on stack instead of heap.*/ + wide_size = retval; + wide_str = apr_palloc(pool, wide_size * sizeof(WCHAR)); + retval = MultiByteToWideChar(handle->from_page_id, 0, src_data, src_length, + wide_str, wide_size); + + if (retval == 0) + return apr_get_os_error(); + + retval = WideCharToMultiByte(handle->to_page_id, 0, wide_str, wide_size, + NULL, 0, NULL, NULL); + + if (retval == 0) + return apr_get_os_error(); + + /* Ensure that buffer is enough to hold result string and termination + character. */ + svn_stringbuf_ensure(*dest, retval + 1); + (*dest)->len = retval; + + retval = WideCharToMultiByte(handle->to_page_id, 0, wide_str, wide_size, + (*dest)->data, (*dest)->len, NULL, NULL); + if (retval == 0) + return apr_get_os_error(); + + (*dest)->len = retval; + return APR_SUCCESS; +} + +#endif /* WIN32 */ Index: subversion/libsvn_subr/win32_xlate.h =================================================================== --- subversion/libsvn_subr/win32_xlate.h (revision 0) +++ subversion/libsvn_subr/win32_xlate.h (revision 0) @@ -0,0 +1,47 @@ +/* + * win32_xlate.h : Windows xlate stuff. + * + * ==================================================================== + * Copyright (c) 2007 CollabNet. All rights reserved. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://subversion.tigris.org/license-1.html. + * If newer versions of this license are posted there, you may use a + * newer version instead, at your option. + * + * This software consists of voluntary contributions made by many + * individuals. For exact contribution history, see the revision + * history and logs, available at http://subversion.tigris.org/. + * ==================================================================== + */ + +#ifndef SVN_LIBSVN_SUBR_WIN32_XLATE_H +#define SVN_LIBSVN_SUBR_WIN32_XLATE_H + +#ifdef WIN32 + +/* Opaque translation buffer. */ +typedef struct win32_xlate_t win32_xlate_t; + +/* Set *XLATE_P to a handle node for converting from FROMPAGE to TOPAGE. + Returns APR_EINVAL or APR_ENOTIMPL, if a conversion isn't supported. + If fail for any other reason, return the error. + + Allocate *RET in POOL. */ +apr_status_t svn_subr__win32_xlate_open(win32_xlate_t **xlate_p, + const char *topage, + const char *frompage, + apr_pool_t *pool); + +/* Convert SRC_LENGTH bytes of SRC_DATA in NODE->handle, store the result + in *DEST, which is allocated in POOL. */ +apr_status_t svn_subr__win32_xlate_to_stringbuf(win32_xlate_t *handle, + const char *src_data, + apr_size_t src_length, + svn_stringbuf_t **dest, + apr_pool_t *pool); + +#endif /* WIN32 */ + +#endif /* SVN_LIBSVN_SUBR_WIN32_XLATE_H */ Index: subversion/tests/libsvn_subr/utf-test.c =================================================================== --- subversion/tests/libsvn_subr/utf-test.c (revision 25597) +++ subversion/tests/libsvn_subr/utf-test.c (working copy) @@ -18,6 +18,8 @@ #include "../svn_test.h" #include "../../libsvn_subr/utf_impl.h" +#include "svn_utf.h" +#include "svn_pools.h" /* Random number seed. Yes, it's global, just pretend you can't see it. */ static apr_uint32_t diff_diff3_seed; @@ -222,6 +224,102 @@ return SVN_NO_ERROR; } +/* Test conversion from different codepages to utf8. */ +static svn_error_t * +test_utf_cstring_to_utf8_ex2(const char **msg, + svn_boolean_t msg_only, + svn_test_opts_t *opts, + apr_pool_t *pool) +{ + apr_size_t i; + apr_pool_t *subpool = svn_pool_create(pool); + + struct data { + const char *string; + const char *expected_result; + const char *from_page; + } tests[] = { + {"ascii text\n", "ascii text\n", "unexistant-page"}, + {"Edelwei\xdf", "Edelwei\xc3\x9f", "ISO-8859-1"} + }; + + *msg = "test svn_utf_cstring_to_utf8_ex2"; + + if (msg_only) + return SVN_NO_ERROR; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) + { + const char *dest; + + svn_pool_clear(subpool); + + SVN_ERR(svn_utf_cstring_to_utf8_ex2(&dest, tests[i].string, + tests[i].from_page, pool)); + + if (strcmp(dest, tests[i].expected_result)) + { + return svn_error_createf + (SVN_ERR_TEST_FAILED, NULL, + "svn_utf_cstring_to_utf8_ex2 ('%s', '%s') returned ('%s') " + "instead of ('%s')", + tests[i].string, tests[i].from_page, + dest, + tests[i].expected_result); + } + } + svn_pool_destroy(subpool); + return SVN_NO_ERROR; +} + +/* Test conversion to different codepages from utf8. */ +static svn_error_t * +test_utf_cstring_from_utf8_ex2(const char **msg, + svn_boolean_t msg_only, + svn_test_opts_t *opts, + apr_pool_t *pool) +{ + apr_size_t i; + apr_pool_t *subpool = svn_pool_create(pool); + + struct data { + const char *string; + const char *expected_result; + const char *to_page; + } tests[] = { + {"ascii text\n", "ascii text\n", "unexistant-page"}, + {"Edelwei\xc3\x9f", "Edelwei\xdf", "ISO-8859-1"} + }; + + *msg = "test svn_utf_cstring_from_utf8_ex2"; + + if (msg_only) + return SVN_NO_ERROR; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) + { + const char *dest; + + svn_pool_clear(subpool); + + SVN_ERR(svn_utf_cstring_from_utf8_ex2(&dest, tests[i].string, + tests[i].to_page, pool)); + + if (strcmp(dest, tests[i].expected_result)) + { + return svn_error_createf + (SVN_ERR_TEST_FAILED, NULL, + "svn_utf_cstring_from_utf8_ex2 ('%s', '%s') returned ('%s') " + "instead of ('%s')", + tests[i].string, tests[i].to_page, + dest, + tests[i].expected_result); + } + } + svn_pool_destroy(subpool); + return SVN_NO_ERROR; +} + /* The test table. */ @@ -230,5 +328,7 @@ SVN_TEST_NULL, SVN_TEST_PASS(utf_validate), SVN_TEST_PASS(utf_validate2), + SVN_TEST_PASS(test_utf_cstring_to_utf8_ex2), + SVN_TEST_PASS(test_utf_cstring_from_utf8_ex2), SVN_TEST_NULL };