icu_util.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/i18n/icu_util.h" 6 7#include "build/build_config.h" 8 9#if defined(OS_WIN) 10#include <windows.h> 11#endif 12 13#include <string> 14 15#include "base/files/file_path.h" 16#include "base/files/memory_mapped_file.h" 17#include "base/logging.h" 18#include "base/path_service.h" 19#include "base/strings/string_util.h" 20#include "base/strings/sys_string_conversions.h" 21#include "third_party/icu/source/common/unicode/putil.h" 22#include "third_party/icu/source/common/unicode/udata.h" 23 24#if defined(OS_MACOSX) 25#include "base/mac/foundation_util.h" 26#endif 27 28#define ICU_UTIL_DATA_FILE 0 29#define ICU_UTIL_DATA_SHARED 1 30#define ICU_UTIL_DATA_STATIC 2 31 32#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE 33// Use an unversioned file name to simplify a icu version update down the road. 34// No need to change the filename in multiple places (gyp files, windows 35// build pkg configurations, etc). 'l' stands for Little Endian. 36#define ICU_UTIL_DATA_FILE_NAME "icudtl.dat" 37#elif ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED 38#define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat" 39#if defined(OS_WIN) 40#define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt.dll" 41#endif 42#endif 43 44namespace base { 45namespace i18n { 46 47namespace { 48 49#if !defined(NDEBUG) 50// Assert that we are not called more than once. Even though calling this 51// function isn't harmful (ICU can handle it), being called twice probably 52// indicates a programming error. 53bool g_called_once = false; 54bool g_check_called_once = true; 55#endif 56} 57 58bool InitializeICU() { 59#if !defined(NDEBUG) 60 DCHECK(!g_check_called_once || !g_called_once); 61 g_called_once = true; 62#endif 63 64#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED) 65 // We expect to find the ICU data module alongside the current module. 66 FilePath data_path; 67 PathService::Get(base::DIR_MODULE, &data_path); 68 data_path = data_path.AppendASCII(ICU_UTIL_DATA_SHARED_MODULE_NAME); 69 70 HMODULE module = LoadLibrary(data_path.value().c_str()); 71 if (!module) { 72 LOG(ERROR) << "Failed to load " << ICU_UTIL_DATA_SHARED_MODULE_NAME; 73 return false; 74 } 75 76 FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL); 77 if (!addr) { 78 LOG(ERROR) << ICU_UTIL_DATA_SYMBOL << ": not found in " 79 << ICU_UTIL_DATA_SHARED_MODULE_NAME; 80 return false; 81 } 82 83 UErrorCode err = U_ZERO_ERROR; 84 udata_setCommonData(reinterpret_cast<void*>(addr), &err); 85 return err == U_ZERO_ERROR; 86#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC) 87 // The ICU data is statically linked. 88 return true; 89#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) 90 // If the ICU data directory is set, ICU won't actually load the data until 91 // it is needed. This can fail if the process is sandboxed at that time. 92 // Instead, we map the file in and hand off the data so the sandbox won't 93 // cause any problems. 94 95 // Chrome doesn't normally shut down ICU, so the mapped data shouldn't ever 96 // be released. 97 CR_DEFINE_STATIC_LOCAL(base::MemoryMappedFile, mapped_file, ()); 98 if (!mapped_file.IsValid()) { 99#if !defined(OS_MACOSX) 100 FilePath data_path; 101#if defined(OS_WIN) 102 // The data file will be in the same directory as the current module. 103 bool path_ok = PathService::Get(base::DIR_MODULE, &data_path); 104#elif defined(OS_ANDROID) 105 bool path_ok = PathService::Get(base::DIR_ANDROID_APP_DATA, &data_path); 106#else 107 // For now, expect the data file to be alongside the executable. 108 // This is sufficient while we work on unit tests, but will eventually 109 // likely live in a data directory. 110 bool path_ok = PathService::Get(base::DIR_EXE, &data_path); 111#endif 112 DCHECK(path_ok); 113 data_path = data_path.AppendASCII(ICU_UTIL_DATA_FILE_NAME); 114#else 115 // Assume it is in the framework bundle's Resources directory. 116 FilePath data_path = 117 base::mac::PathForFrameworkBundleResource(CFSTR(ICU_UTIL_DATA_FILE_NAME)); 118 if (data_path.empty()) { 119 LOG(ERROR) << ICU_UTIL_DATA_FILE_NAME << " not found in bundle"; 120 return false; 121 } 122#endif // OS check 123 if (!mapped_file.Initialize(data_path)) { 124 LOG(ERROR) << "Couldn't mmap " << data_path.AsUTF8Unsafe(); 125 return false; 126 } 127 } 128 UErrorCode err = U_ZERO_ERROR; 129 udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err); 130 return err == U_ZERO_ERROR; 131#endif 132} 133 134void AllowMultipleInitializeCallsForTesting() { 135#if !defined(NDEBUG) 136 g_check_called_once = false; 137#endif 138} 139 140} // namespace i18n 141} // namespace base 142