Index: subversion/libsvn_subr/utf.c =================================================================== --- subversion/libsvn_subr/utf.c (revision 25597) +++ subversion/libsvn_subr/utf.c (working copy) @@ -162,6 +162,133 @@ #endif } +#ifdef WIN32 +typedef struct win32_xlate_t +{ + UINT from_page_id; + UINT to_page_id; +} win32_xlate_t; + +static apr_status_t +discover_windows_codepage_id(UINT *page_id, const char *page, apr_pool_t *pool) +{ + DWORD data; + DWORD data_len; + apr_status_t apr_err = APR_SUCCESS; + HKEY hkey; + svn_string_t * sub_key; + + if (page == APR_DEFAULT_CHARSET) + { + *page_id = CP_ACP; + return APR_SUCCESS; + } + else if (page == APR_LOCALE_CHARSET) + { + /* ### TODO: Probably we have to use CP_THREAD_ACP, but it doesn't + supported on Windows NT.*/ + *page_id = CP_ACP; + return APR_SUCCESS; + } + + sub_key = svn_string_createf(pool, "MIME\\Database\\Charset\\%s", page); + + apr_err = APR_FROM_OS_ERROR(RegOpenKeyEx + (HKEY_CLASSES_ROOT, sub_key->data, 0, + KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE, &hkey)); + + if (APR_STATUS_IS_ENOENT(apr_err)) + return APR_EINVAL; + else if (apr_err != APR_SUCCESS) + return apr_err; + + data_len = sizeof(data); + apr_err = APR_FROM_OS_ERROR(RegQueryValueEx + (hkey, "InternetEncoding", NULL, NULL, &data, &data_len)); + + if (APR_STATUS_IS_ENOENT(apr_err)) + apr_err = APR_EINVAL; + else if (apr_err == APR_SUCCESS) + *page_id = data; + + /* ### TODO: Handle charset aliases. */ + + RegCloseKey(hkey); + return apr_err; +} + +static apr_status_t +win32_xlate_open(apr_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; + win32_xlate_t *xlate; + + apr_err = discover_windows_codepage_id(&to_page_id, topage, pool); + if (apr_err != APR_SUCCESS) + return apr_err; + + apr_err = discover_windows_codepage_id(&from_page_id, frompage, pool); + if (apr_err != APR_SUCCESS) + return apr_err; + + xlate = apr_palloc(pool, sizeof(*xlate)); + xlate->from_page_id = from_page_id; + xlate->to_page_id = to_page_id; + + *xlate_p = xlate; + + return APR_SUCCESS; +} + +static apr_status_t +win32_convert_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_FROM_OS_ERROR(GetLastError()); + + 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_FROM_OS_ERROR(GetLastError()); + + retval = WideCharToMultiByte(handle->to_page_id, 0, wide_str, wide_size, + NULL, 0, NULL, NULL); + + if (retval == 0) + return APR_FROM_OS_ERROR(GetLastError()); + + /* 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_FROM_OS_ERROR(GetLastError()); + + (*dest)->len = retval; + return APR_SUCCESS; +} +#endif + /* Set *RET to a handle node for converting from FROMPAGE to TOPAGE, creating the handle node if it doesn't exist in USERDATA_KEY. If a node is not cached and apr_xlate_open() returns APR_EINVAL or @@ -252,10 +379,12 @@ pool = apr_hash_pool_get(xlate_handle_hash); /* Try to create a handle. */ -#ifndef AS400 +#if defined( WIN32) + apr_err = 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 +583,12 @@ svn_stringbuf_t **dest, apr_pool_t *pool) { +#ifdef WIN32 + apr_status_t apr_err; + + apr_err = win32_convert_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 +642,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/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); + } + } + + 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); + } + } + + 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 };