From 4214b4aa2a4096655802b17d87fde79c655f3dfa Mon Sep 17 00:00:00 2001 From: Jeff Genovy <29107334+jefgen@users.noreply.github.com> Date: Tue, 11 Sep 2018 09:42:49 -0700 Subject: [PATCH] ICU-13687 ICU4C Enable Windows UWP version to use TZ update/override files. (#31) Enable the Windows UWP version of ICU4C to use TZ update/override files (.res files) for out-of-band timezone data updates. Also use the *W version of the Windows APIs to avoid code-page conversion. --- icu4c/source/common/putil.cpp | 75 +++++++++++++++++++++++--------- icu4c/source/common/umapfile.cpp | 27 +++++------- 2 files changed, 66 insertions(+), 36 deletions(-) diff --git a/icu4c/source/common/putil.cpp b/icu4c/source/common/putil.cpp index b9bd396c8a8..bc3fbb9ba62 100644 --- a/icu4c/source/common/putil.cpp +++ b/icu4c/source/common/putil.cpp @@ -1325,6 +1325,43 @@ uprv_pathIsAbsolute(const char *path) # endif #endif +#if U_PLATFORM_HAS_WINUWP_API != 0 +// Helper function to get the ICU Data Directory under the Windows directory location. +static BOOL U_CALLCONV getIcuDataDirectoryUnderWindowsDirectory(char* directoryBuffer, UINT bufferLength) +{ +#if defined(ICU_DATA_DIR_WINDOWS) + wchar_t windowsPath[MAX_PATH]; + char windowsPathUtf8[MAX_PATH]; + + UINT length = GetSystemWindowsDirectoryW(windowsPath, UPRV_LENGTHOF(windowsPath)); + if ((length > 0) && (length < (UPRV_LENGTHOF(windowsPath) - 1))) { + // Convert UTF-16 to a UTF-8 string. + UErrorCode status = U_ZERO_ERROR; + int32_t windowsPathUtf8Len = 0; + u_strToUTF8(windowsPathUtf8, static_cast(UPRV_LENGTHOF(windowsPathUtf8)), + &windowsPathUtf8Len, reinterpret_cast(windowsPath), -1, &status); + + if (U_SUCCESS(status) && (status != U_STRING_NOT_TERMINATED_WARNING) && + (windowsPathUtf8Len < (UPRV_LENGTHOF(windowsPathUtf8) - 1))) { + // Ensure it always has a separator, so we can append the ICU data path. + if (windowsPathUtf8[windowsPathUtf8Len - 1] != U_FILE_SEP_CHAR) { + windowsPathUtf8[windowsPathUtf8Len++] = U_FILE_SEP_CHAR; + windowsPathUtf8[windowsPathUtf8Len] = '\0'; + } + // Check if the concatenated string will fit. + if ((windowsPathUtf8Len + UPRV_LENGTHOF(ICU_DATA_DIR_WINDOWS)) < bufferLength) { + uprv_strcpy(directoryBuffer, windowsPathUtf8); + uprv_strcat(directoryBuffer, ICU_DATA_DIR_WINDOWS); + return TRUE; + } + } + } +#endif + + return FALSE; +} +#endif + static void U_CALLCONV dataDirectoryInitFn() { /* If we already have the directory, then return immediately. Will happen if user called * u_setDataDirectory(). @@ -1384,24 +1421,10 @@ static void U_CALLCONV dataDirectoryInitFn() { } #endif -#if defined(ICU_DATA_DIR_WINDOWS) && U_PLATFORM_HAS_WINUWP_API != 0 - // Use data from the %windir%\globalization\icu directory - // This is only available if ICU is built as a system component +#if U_PLATFORM_HAS_WINUWP_API != 0 && defined(ICU_DATA_DIR_WINDOWS) char datadir_path_buffer[MAX_PATH]; - UINT length = GetWindowsDirectoryA(datadir_path_buffer, UPRV_LENGTHOF(datadir_path_buffer)); - if (length > 0 && length < (UPRV_LENGTHOF(datadir_path_buffer) - sizeof(ICU_DATA_DIR_WINDOWS) - 1)) - { - if (datadir_path_buffer[length - 1] != '\\') - { - datadir_path_buffer[length++] = '\\'; - datadir_path_buffer[length] = '\0'; - } - - if ((length + 1 + sizeof(ICU_DATA_DIR_WINDOWS)) < UPRV_LENGTHOF(datadir_path_buffer)) - { - uprv_strcat(datadir_path_buffer, ICU_DATA_DIR_WINDOWS); - path = datadir_path_buffer; - } + if (getIcuDataDirectoryUnderWindowsDirectory(datadir_path_buffer, UPRV_LENGTHOF(datadir_path_buffer))) { + path = datadir_path_buffer; } #endif @@ -1450,20 +1473,30 @@ static void U_CALLCONV TimeZoneDataDirInitFn(UErrorCode &status) { status = U_MEMORY_ALLOCATION_ERROR; return; } -#if U_PLATFORM_HAS_WINUWP_API == 0 - const char *dir = getenv("ICU_TIMEZONE_FILES_DIR"); -#else - // TODO: UWP does not support alternate timezone data directories at this time + const char *dir = ""; + +#if U_PLATFORM_HAS_WINUWP_API != 0 + // The UWP version does not support the environment variable setting, but can possibly pick them up from the Windows directory. + char datadir_path_buffer[MAX_PATH]; + if (getIcuDataDirectoryUnderWindowsDirectory(datadir_path_buffer, UPRV_LENGTHOF(datadir_path_buffer))) { + dir = datadir_path_buffer; + } +#else + dir = getenv("ICU_TIMEZONE_FILES_DIR"); #endif // U_PLATFORM_HAS_WINUWP_API + #if defined(U_TIMEZONE_FILES_DIR) if (dir == NULL) { + // Build time configuration setting. dir = TO_STRING(U_TIMEZONE_FILES_DIR); } #endif + if (dir == NULL) { dir = ""; } + setTimeZoneFilesDir(dir, status); } diff --git a/icu4c/source/common/umapfile.cpp b/icu4c/source/common/umapfile.cpp index ffb18ef152e..175e1a93ef3 100644 --- a/icu4c/source/common/umapfile.cpp +++ b/icu4c/source/common/umapfile.cpp @@ -22,6 +22,7 @@ #include "uposixdefs.h" #include "unicode/putil.h" +#include "unicode/ustring.h" #include "udatamem.h" #include "umapfile.h" @@ -119,22 +120,18 @@ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_RANDOM_ACCESS, NULL); #else - // First we need to go from char to UTF-16 - // u_UCharsToChars could work but it requires length. - WCHAR utf16Path[MAX_PATH]; - int32_t i; - for (i = 0; i < UPRV_LENGTHOF(utf16Path); i++) - { - utf16Path[i] = path[i]; - if (path[i] == '\0') - { - break; - } + // Convert from UTF-8 string to UTF-16 string. + wchar_t utf16Path[MAX_PATH]; + int32_t pathUtf16Len = 0; + u_strFromUTF8(reinterpret_cast(utf16Path), static_cast(UPRV_LENGTHOF(utf16Path)), &pathUtf16Len, path, -1, status); + + if (U_FAILURE(*status)) { + return FALSE; } - if (i >= UPRV_LENGTHOF(utf16Path)) - { - // Ran out of room, unlikely but be safe - utf16Path[UPRV_LENGTHOF(utf16Path) - 1] = '\0'; + if (*status == U_STRING_NOT_TERMINATED_WARNING) { + // Report back an error instead of a warning. + *status = U_BUFFER_OVERFLOW_ERROR; + return FALSE; } // TODO: Is it worth setting extended parameters to specify random access?