X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/4388f060552cc537e71e957d32f35e9d75a61233..340931cb2e044a2141d11567dd0f782524e32994:/icuSources/common/udata.cpp diff --git a/icuSources/common/udata.cpp b/icuSources/common/udata.cpp index c4f8752a..4f816312 100644 --- a/icuSources/common/udata.cpp +++ b/icuSources/common/udata.cpp @@ -1,12 +1,14 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html /* ****************************************************************************** * -* Copyright (C) 1999-2012, International Business Machines +* Copyright (C) 1999-2016, International Business Machines * Corporation and others. All Rights Reserved. * ****************************************************************************** * file name: udata.cpp -* encoding: US-ASCII +* encoding: UTF-8 * tab size: 8 (not used) * indentation:4 * @@ -29,7 +31,10 @@ might have to #include some other header #include "charstr.h" #include "cmemory.h" #include "cstring.h" +#include "mutex.h" #include "putilimp.h" +#include "restrace.h" +#include "uassert.h" #include "ucln_cmn.h" #include "ucmndata.h" #include "udatamem.h" @@ -65,19 +70,19 @@ might have to #include some other header /* If you are excruciatingly bored turn this on .. */ /* #define UDATA_DEBUG 1 */ +/* For debugging use of timezone data in a separate file */ +/* #define UDATA_TZFILES_DEBUG 1 */ -#if defined(UDATA_DEBUG) +#if defined(UDATA_DEBUG) || defined(UDATA_TZFILES_DEBUG) # include #endif -#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) - U_NAMESPACE_USE /* * Forward declarations */ -static UDataMemory *udata_findCachedData(const char *path); +static UDataMemory *udata_findCachedData(const char *path, UErrorCode &err); /*********************************************************************** * @@ -101,13 +106,20 @@ static UDataMemory *udata_findCachedData(const char *path); * that they really need, reducing the size of binaries that take advantage * of this. */ -static UDataMemory *gCommonICUDataArray[10] = { NULL }; +static UDataMemory *gCommonICUDataArray[10] = { NULL }; // Access protected by icu global mutex. -static UBool gHaveTriedToLoadCommonData = FALSE; /* See extendICUData(). */ +static u_atomic_int32_t gHaveTriedToLoadCommonData = ATOMIC_INT32_T_INITIALIZER(0); // See extendICUData(). static UHashtable *gCommonDataCache = NULL; /* Global hash table of opened ICU data files. */ - -static UDataFileAccess gDataFileAccess = UDATA_DEFAULT_ACCESS; +static icu::UInitOnce gCommonDataCacheInitOnce = U_INITONCE_INITIALIZER; + +#if !defined(ICU_DATA_DIR_WINDOWS) +static UDataFileAccess gDataFileAccess = UDATA_DEFAULT_ACCESS; // Access not synchronized. + // Modifying is documented as thread-unsafe. +#else +// If we are using the Windows data directory, then look in one spot only. +static UDataFileAccess gDataFileAccess = UDATA_NO_FILES; +#endif static UBool U_CALLCONV udata_cleanup(void) @@ -118,34 +130,37 @@ udata_cleanup(void) uhash_close(gCommonDataCache); /* Table owns the contents, and will delete them. */ gCommonDataCache = NULL; /* Cleanup is not thread safe. */ } + gCommonDataCacheInitOnce.reset(); - for (i = 0; i < LENGTHOF(gCommonICUDataArray) && gCommonICUDataArray[i] != NULL; ++i) { + for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray) && gCommonICUDataArray[i] != NULL; ++i) { udata_close(gCommonICUDataArray[i]); gCommonICUDataArray[i] = NULL; } - gHaveTriedToLoadCommonData = FALSE; + gHaveTriedToLoadCommonData = 0; return TRUE; /* Everything was cleaned up */ } static UBool U_CALLCONV -findCommonICUDataByName(const char *inBasename) +findCommonICUDataByName(const char *inBasename, UErrorCode &err) { UBool found = FALSE; int32_t i; - UDataMemory *pData = udata_findCachedData(inBasename); - if (pData == NULL) + UDataMemory *pData = udata_findCachedData(inBasename, err); + if (U_FAILURE(err) || pData == NULL) return FALSE; - for (i = 0; i < LENGTHOF(gCommonICUDataArray); ++i) { - if ((gCommonICUDataArray[i] != NULL) && (gCommonICUDataArray[i]->pHeader == pData->pHeader)) { - /* The data pointer is already in the array. */ - found = TRUE; - break; + { + Mutex lock; + for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray); ++i) { + if ((gCommonICUDataArray[i] != NULL) && (gCommonICUDataArray[i]->pHeader == pData->pHeader)) { + /* The data pointer is already in the array. */ + found = TRUE; + break; + } } } - return found; } @@ -172,10 +187,9 @@ setCommonICUData(UDataMemory *pData, /* The new common data. Belongs to ca /* their locals. */ UDatamemory_assign(newCommonData, pData); umtx_lock(NULL); - for (i = 0; i < LENGTHOF(gCommonICUDataArray); ++i) { + for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray); ++i) { if (gCommonICUDataArray[i] == NULL) { gCommonICUDataArray[i] = newCommonData; - ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup); didUpdate = TRUE; break; } else if (gCommonICUDataArray[i]->pHeader == pData->pHeader) { @@ -185,24 +199,30 @@ setCommonICUData(UDataMemory *pData, /* The new common data. Belongs to ca } umtx_unlock(NULL); - if (i == LENGTHOF(gCommonICUDataArray) && warn) { + if (i == UPRV_LENGTHOF(gCommonICUDataArray) && warn) { *pErr = U_USING_DEFAULT_WARNING; } - if (!didUpdate) { + if (didUpdate) { + ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup); + } else { uprv_free(newCommonData); } return didUpdate; } +#if !defined(ICU_DATA_DIR_WINDOWS) + static UBool setCommonICUDataPointer(const void *pData, UBool /*warn*/, UErrorCode *pErrorCode) { UDataMemory tData; UDataMemory_init(&tData); - tData.pHeader = (const DataHeader *)pData; + UDataMemory_setData(&tData, pData); udata_checkCommonData(&tData, pErrorCode); return setCommonICUData(&tData, FALSE, pErrorCode); } +#endif + static const char * findBasename(const char *path) { const char *basename=uprv_strrchr(path, U_FILE_SEP_CHAR); @@ -262,56 +282,41 @@ static void U_CALLCONV DataCacheElement_deleter(void *pDCEl) { uprv_free(pDCEl); /* delete 'this' */ } - /* udata_getCacheHashTable() - * Get the hash table used to store the data cache entries. - * Lazy create it if it doesn't yet exist. - */ -static UHashtable *udata_getHashTable() { - UErrorCode err = U_ZERO_ERROR; - UBool cacheIsInitialized; - UHashtable *tHT = NULL; - - UMTX_CHECK(NULL, (gCommonDataCache != NULL), cacheIsInitialized); - - if (cacheIsInitialized) { - return gCommonDataCache; - } - - tHT = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &err); - /* Check for null pointer. */ - if (tHT == NULL) { - return NULL; /* TODO: Handle this error better. */ - } - uhash_setValueDeleter(tHT, DataCacheElement_deleter); - - umtx_lock(NULL); - if (gCommonDataCache == NULL) { - gCommonDataCache = tHT; - tHT = NULL; - ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup); - } - umtx_unlock(NULL); - if (tHT != NULL) { - uhash_close(tHT); - } - +static void U_CALLCONV udata_initHashTable(UErrorCode &err) { + U_ASSERT(gCommonDataCache == NULL); + gCommonDataCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &err); if (U_FAILURE(err)) { - return NULL; /* TODO: handle this error better. */ + return; } + U_ASSERT(gCommonDataCache != NULL); + uhash_setValueDeleter(gCommonDataCache, DataCacheElement_deleter); + ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup); +} + + /* udata_getCacheHashTable() + * Get the hash table used to store the data cache entries. + * Lazy create it if it doesn't yet exist. + */ +static UHashtable *udata_getHashTable(UErrorCode &err) { + umtx_initOnce(gCommonDataCacheInitOnce, &udata_initHashTable, err); return gCommonDataCache; } -static UDataMemory *udata_findCachedData(const char *path) +static UDataMemory *udata_findCachedData(const char *path, UErrorCode &err) { UHashtable *htable; UDataMemory *retVal = NULL; DataCacheElement *el; const char *baseName; + htable = udata_getHashTable(err); + if (U_FAILURE(err)) { + return NULL; + } + baseName = findBasename(path); /* Cache remembers only the base name, not the full path. */ - htable = udata_getHashTable(); umtx_lock(NULL); el = (DataCacheElement *)uhash_get(htable, baseName); umtx_unlock(NULL); @@ -319,7 +324,7 @@ static UDataMemory *udata_findCachedData(const char *path) retVal = el->item; } #ifdef UDATA_DEBUG - fprintf(stderr, "Cache: [%s] -> %p\n", baseName, retVal); + fprintf(stderr, "Cache: [%s] -> %p\n", baseName, (void*) retVal); #endif return retVal; } @@ -333,6 +338,7 @@ static UDataMemory *udata_cacheDataItem(const char *path, UDataMemory *item, UEr DataCacheElement *oldValue = NULL; UErrorCode subErr = U_ZERO_ERROR; + htable = udata_getHashTable(*pErr); if (U_FAILURE(*pErr)) { return NULL; } @@ -365,7 +371,6 @@ static UDataMemory *udata_cacheDataItem(const char *path, UDataMemory *item, UEr /* Stick the new DataCacheElement into the hash table. */ - htable = udata_getHashTable(); umtx_lock(NULL); oldValue = (DataCacheElement *)uhash_get(htable, path); if (oldValue != NULL) { @@ -382,7 +387,7 @@ static UDataMemory *udata_cacheDataItem(const char *path, UDataMemory *item, UEr #ifdef UDATA_DEBUG fprintf(stderr, "Cache: [%s] <<< %p : %s. vFunc=%p\n", newElement->name, - newElement->item, u_errorName(subErr), newElement->item->vFuncs); + (void*) newElement->item, u_errorName(subErr), (void*) newElement->item->vFuncs); #endif if (subErr == U_USING_DEFAULT_WARNING || U_FAILURE(subErr)) { @@ -403,9 +408,6 @@ static UDataMemory *udata_cacheDataItem(const char *path, UDataMemory *item, UEr * * *----------------------------------------------------------------------*/ -#define U_DATA_PATHITER_BUFSIZ 128 /* Size of local buffer for paths */ - /* Overflow causes malloc of larger buf */ - U_NAMESPACE_BEGIN class UDataPathIterator @@ -420,7 +422,8 @@ private: const char *path; /* working path (u_icudata_Dir) */ const char *nextPath; /* path following this one */ const char *basename; /* item's basename (icudt22e_mt.res)*/ - const char *suffix; /* item suffix (can be null) */ + + StringPiece suffix; /* item suffix (can be null) */ uint32_t basenameLen; /* length of basename */ @@ -434,13 +437,15 @@ private: }; /** - * @param iter The iterator to be initialized. Its current state does not matter. - * @param path The full pathname to be iterated over. If NULL, defaults to U_ICUDATA_NAME - * @param pkg Package which is being searched for, ex "icudt28l". Will ignore leave directories such as /icudt28l - * @param item Item to be searched for. Can include full path, such as /a/b/foo.dat - * @param suffix Optional item suffix, if not-null (ex. ".dat") then 'path' can contain 'item' explicitly. - * Ex: 'stuff.dat' would be found in '/a/foo:/tmp/stuff.dat:/bar/baz' as item #2. - * '/blarg/stuff.dat' would also be found. + * @param iter The iterator to be initialized. Its current state does not matter. + * @param inPath The full pathname to be iterated over. If NULL, defaults to U_ICUDATA_NAME + * @param pkg Package which is being searched for, ex "icudt28l". Will ignore leaf directories such as /icudt28l + * @param item Item to be searched for. Can include full path, such as /a/b/foo.dat + * @param inSuffix Optional item suffix, if not-null (ex. ".dat") then 'path' can contain 'item' explicitly. + * Ex: 'stuff.dat' would be found in '/a/foo:/tmp/stuff.dat:/bar/baz' as item #2. + * '/blarg/stuff.dat' would also be found. + * Note: inSuffix may also be the 'item' being searched for as well, (ex: "ibm-5348_P100-1997.cnv"), in which case + * the 'item' parameter is often the same as pkg. (Though sometimes might have a tree part as well, ex: "icudt62l-curr"). */ UDataPathIterator::UDataPathIterator(const char *inPath, const char *pkg, const char *item, const char *inSuffix, UBool doCheckLastFour, @@ -476,7 +481,7 @@ UDataPathIterator::UDataPathIterator(const char *inPath, const char *pkg, nextPath = itemPath.data(); } #ifdef UDATA_DEBUG - fprintf(stderr, "SUFFIX=%s [%p]\n", inSuffix, inSuffix); + fprintf(stderr, "SUFFIX=%s [%p]\n", inSuffix, (void*) inSuffix); #endif /** Suffix **/ @@ -491,12 +496,11 @@ UDataPathIterator::UDataPathIterator(const char *inPath, const char *pkg, /* pathBuffer will hold the output path strings returned by this iterator */ #ifdef UDATA_DEBUG - fprintf(stderr, "%p: init %s -> [path=%s], [base=%s], [suff=%s], [itempath=%s], [nextpath=%s], [checklast4=%s]\n", - iter, + fprintf(stderr, "0: init %s -> [path=%s], [base=%s], [suff=%s], [itempath=%s], [nextpath=%s], [checklast4=%s]\n", item, path, basename, - suffix, + suffix.data(), itemPath.data(), nextPath, checkLastFour?"TRUE":"false"); @@ -552,7 +556,7 @@ const char *UDataPathIterator::next(UErrorCode *pErrorCode) fprintf(stderr, "rest of path (IDD) = %s\n", currentPath); fprintf(stderr, " "); { - uint32_t qqq; + int32_t qqq; for(qqq=0;qqq=4) && - uprv_strncmp(pathBuffer.data() +(pathLen-4), suffix, 4)==0 && /* suffix matches */ + uprv_strncmp(pathBuffer.data() +(pathLen-4), suffix.data(), 4)==0 && /* suffix matches */ uprv_strncmp(findBasename(pathBuffer.data()), basename, basenameLen)==0 && /* base matches */ uprv_strlen(pathBasename)==(basenameLen+4)) { /* base+suffix = full len */ #ifdef UDATA_DEBUG - fprintf(stderr, "Have %s file on the path: %s\n", suffix, pathBuffer.data()); + fprintf(stderr, "Have %s file on the path: %s\n", suffix.data(), pathBuffer.data()); #endif /* do nothing */ } @@ -604,8 +608,13 @@ const char *UDataPathIterator::next(UErrorCode *pErrorCode) /* + basename */ pathBuffer.append(packageStub.data()+1, packageStub.length()-1, *pErrorCode); - if(*suffix) /* tack on suffix */ + if (!suffix.empty()) /* tack on suffix */ { + if (suffix.length() > 4) { + // If the suffix is actually an item ("ibm-5348_P100-1997.cnv") and not an extension (".res") + // then we need to ensure that the path ends with a separator. + pathBuffer.ensureEndsWithFileSeparator(*pErrorCode); + } pathBuffer.append(suffix, *pErrorCode); } } @@ -629,12 +638,15 @@ U_NAMESPACE_END /*----------------------------------------------------------------------* * * - * Add a static reference to the common data library * + * Add a static reference to the common data library * * Unless overridden by an explicit udata_setCommonData, this will be * * our common data. * * * *----------------------------------------------------------------------*/ +#if !defined(ICU_DATA_DIR_WINDOWS) +// When using the Windows system data, we expect only a single data file. extern "C" const DataHeader U_DATA_API U_ICUDATA_ENTRY_POINT; +#endif /* * This would be a good place for weak-linkage declarations of @@ -674,10 +686,16 @@ openCommonData(const char *path, /* Path from OpenChoice? */ /* ??????? TODO revisit this */ if (commonDataIndex >= 0) { /* "mini-cache" for common ICU data */ - if(commonDataIndex >= LENGTHOF(gCommonICUDataArray)) { + if(commonDataIndex >= UPRV_LENGTHOF(gCommonICUDataArray)) { return NULL; } - if(gCommonICUDataArray[commonDataIndex] == NULL) { + { + Mutex lock; + if(gCommonICUDataArray[commonDataIndex] != NULL) { + return gCommonICUDataArray[commonDataIndex]; + } +#if !defined(ICU_DATA_DIR_WINDOWS) +// When using the Windows system data, we expect only a single data file. int32_t i; for(i = 0; i < commonDataIndex; ++i) { if(gCommonICUDataArray[i]->pHeader == &U_ICUDATA_ENTRY_POINT) { @@ -685,23 +703,30 @@ openCommonData(const char *path, /* Path from OpenChoice? */ return NULL; } } +#endif + } - /* Add the linked-in data to the list. */ - /* - * This is where we would check and call weakly linked partial-data-library - * access functions. - */ - /* - if (uprv_getICUData_collation) { - setCommonICUDataPointer(uprv_getICUData_collation(), FALSE, pErrorCode); - } - if (uprv_getICUData_conversion) { - setCommonICUDataPointer(uprv_getICUData_conversion(), FALSE, pErrorCode); - } - */ - setCommonICUDataPointer(&U_ICUDATA_ENTRY_POINT, FALSE, pErrorCode); + /* Add the linked-in data to the list. */ + /* + * This is where we would check and call weakly linked partial-data-library + * access functions. + */ + /* + if (uprv_getICUData_collation) { + setCommonICUDataPointer(uprv_getICUData_collation(), FALSE, pErrorCode); + } + if (uprv_getICUData_conversion) { + setCommonICUDataPointer(uprv_getICUData_conversion(), FALSE, pErrorCode); + } + */ +#if !defined(ICU_DATA_DIR_WINDOWS) +// When using the Windows system data, we expect only a single data file. + setCommonICUDataPointer(&U_ICUDATA_ENTRY_POINT, FALSE, pErrorCode); + { + Mutex lock; + return gCommonICUDataArray[commonDataIndex]; } - return gCommonICUDataArray[commonDataIndex]; +#endif } @@ -720,18 +745,18 @@ openCommonData(const char *path, /* Path from OpenChoice? */ #ifdef UDATA_DEBUG fprintf(stderr, "ocd: no basename in %s, bailing.\n", path); #endif - *pErrorCode=U_FILE_ACCESS_ERROR; + if (U_SUCCESS(*pErrorCode)) { + *pErrorCode=U_FILE_ACCESS_ERROR; + } return NULL; } /* Is the requested common data file already open and cached? */ /* Note that the cache is keyed by the base name only. The rest of the path, */ /* if any, is not considered. */ - { - UDataMemory *dataToReturn = udata_findCachedData(inBasename); - if (dataToReturn != NULL) { - return dataToReturn; - } + UDataMemory *dataToReturn = udata_findCachedData(inBasename, *pErrorCode); + if (dataToReturn != NULL || U_FAILURE(*pErrorCode)) { + return dataToReturn; } /* Requested item is not in the cache. @@ -740,16 +765,19 @@ openCommonData(const char *path, /* Path from OpenChoice? */ UDataPathIterator iter(u_getDataDirectory(), inBasename, path, ".dat", TRUE, pErrorCode); - while((UDataMemory_isLoaded(&tData)==FALSE) && (pathBuffer = iter.next(pErrorCode)) != NULL) + while ((UDataMemory_isLoaded(&tData)==FALSE) && (pathBuffer = iter.next(pErrorCode)) != NULL) { #ifdef UDATA_DEBUG fprintf(stderr, "ocd: trying path %s - ", pathBuffer); #endif - uprv_mapFile(&tData, pathBuffer); + uprv_mapFile(&tData, pathBuffer, pErrorCode); #ifdef UDATA_DEBUG fprintf(stderr, "%s\n", UDataMemory_isLoaded(&tData)?"LOADED":"not loaded"); #endif } + if (U_FAILURE(*pErrorCode)) { + return NULL; + } #if defined(OS390_STUBDATA) && defined(OS390BATCH) if (!UDataMemory_isLoaded(&tData)) { @@ -758,10 +786,13 @@ openCommonData(const char *path, /* Path from OpenChoice? */ uprv_strncpy(ourPathBuffer, path, 1019); ourPathBuffer[1019]=0; uprv_strcat(ourPathBuffer, ".dat"); - uprv_mapFile(&tData, ourPathBuffer); + uprv_mapFile(&tData, ourPathBuffer, pErrorCode); } #endif + if (U_FAILURE(*pErrorCode)) { + return NULL; + } if (!UDataMemory_isLoaded(&tData)) { /* no common data */ *pErrorCode=U_FILE_ACCESS_ERROR; @@ -806,10 +837,10 @@ static UBool extendICUData(UErrorCode *pErr) * Use a specific mutex to avoid nested locks of the global mutex. */ #if MAP_IMPLEMENTATION==MAP_STDIO - static UMTX extendICUDataMutex = NULL; + static UMutex extendICUDataMutex; umtx_lock(&extendICUDataMutex); #endif - if(!gHaveTriedToLoadCommonData) { + if(!umtx_loadAcquire(gHaveTriedToLoadCommonData)) { /* See if we can explicitly open a .dat file for the ICUData. */ pData = openCommonData( U_ICUDATA_NAME, /* "icudt20l" , for example. */ @@ -834,10 +865,10 @@ static UBool extendICUData(UErrorCode *pErr) pErr); /* setCommonICUData honors errors; NOP if error set */ } - gHaveTriedToLoadCommonData = TRUE; + umtx_storeRelease(gHaveTriedToLoadCommonData, 1); } - didUpdate = findCommonICUDataByName(U_ICUDATA_NAME); /* Return 'true' when a racing writes out the extended */ + didUpdate = findCommonICUDataByName(U_ICUDATA_NAME, *pErr); /* Return 'true' when a racing writes out the extended */ /* data after another thread has failed to see it (in openCommonData), so */ /* extended data can be examined. */ /* Also handles a race through here before gHaveTriedToLoadCommonData is set. */ @@ -846,7 +877,7 @@ static UBool extendICUData(UErrorCode *pErr) umtx_unlock(&extendICUDataMutex); #endif return didUpdate; /* Return true if ICUData pointer was updated. */ - /* (Could potentialy have been done by another thread racing */ + /* (Could potentially have been done by another thread racing */ /* us through here, but that's fine, we still return true */ /* so that current thread will also examine extended data. */ } @@ -972,12 +1003,12 @@ static UDataMemory *doLoadFromIndividualFiles(const char *pkgName, /* init path iterator for individual files */ UDataPathIterator iter(dataPath, pkgName, path, tocEntryPathSuffix, FALSE, pErrorCode); - while((pathBuffer = iter.next(pErrorCode))) + while ((pathBuffer = iter.next(pErrorCode)) != NULL) { #ifdef UDATA_DEBUG fprintf(stderr, "UDATA: trying individual file %s\n", pathBuffer); #endif - if(uprv_mapFile(&dataMemory, pathBuffer)) + if (uprv_mapFile(&dataMemory, pathBuffer, pErrorCode)) { pEntryData = checkDataItem(dataMemory.pHeader, isAcceptable, context, type, name, subErrorCode, pErrorCode); if (pEntryData != NULL) { @@ -993,7 +1024,7 @@ static UDataMemory *doLoadFromIndividualFiles(const char *pkgName, return pEntryData; } - /* the data is not acceptable, or some error occured. Either way, unmap the memory */ + /* the data is not acceptable, or some error occurred. Either way, unmap the memory */ udata_close(&dataMemory); /* If we had a nasty error, bail out completely. */ @@ -1045,13 +1076,13 @@ static UDataMemory *doLoadFromCommonData(UBool isICUData, const char * /*pkgName /* look up the data piece in the common data */ pHeader=pCommonData->vFuncs->Lookup(pCommonData, tocEntryName, &length, subErrorCode); #ifdef UDATA_DEBUG - fprintf(stderr, "%s: pHeader=%p - %s\n", tocEntryName, pHeader, u_errorName(*subErrorCode)); + fprintf(stderr, "%s: pHeader=%p - %s\n", tocEntryName, (void*) pHeader, u_errorName(*subErrorCode)); #endif if(pHeader!=NULL) { pEntryData = checkDataItem(pHeader, isAcceptable, context, type, name, subErrorCode, pErrorCode); #ifdef UDATA_DEBUG - fprintf(stderr, "pEntryData=%p\n", pEntryData); + fprintf(stderr, "pEntryData=%p\n", (void*) pEntryData); #endif if (U_FAILURE(*pErrorCode)) { return NULL; @@ -1062,6 +1093,11 @@ static UDataMemory *doLoadFromCommonData(UBool isICUData, const char * /*pkgName } } } + // If we failed due to being out-of-memory, then stop early and report the error. + if (*subErrorCode == U_MEMORY_ALLOCATION_ERROR) { + *pErrorCode = *subErrorCode; + return NULL; + } /* Data wasn't found. If we were looking for an ICUData item and there is * more data available, load it and try again, * otherwise break out of this loop. */ @@ -1078,6 +1114,17 @@ static UDataMemory *doLoadFromCommonData(UBool isICUData, const char * /*pkgName } } +/* + * Identify the Time Zone resources that are subject to special override data loading. + */ +static UBool isTimeZoneFile(const char *name, const char *type) { + return ((uprv_strcmp(type, "res") == 0) && + (uprv_strcmp(name, "zoneinfo64") == 0 || + uprv_strcmp(name, "timezoneTypes") == 0 || + uprv_strcmp(name, "windowsZones") == 0 || + uprv_strcmp(name, "metaZones") == 0)); +} + /* * A note on the ownership of Mapped Memory * @@ -1127,6 +1174,9 @@ doOpenChoice(const char *path, const char *type, const char *name, UBool isICUData = FALSE; + FileTracer::traceOpen(path, type, name); + + /* Is this path ICU data? */ if(path == NULL || !strcmp(path, U_ICUDATA_ALIAS) || /* "ICUDATA" */ @@ -1144,7 +1194,7 @@ doOpenChoice(const char *path, const char *type, const char *name, if(uprv_strchr(path,U_FILE_ALT_SEP_CHAR) != NULL) { altSepPath.append(path, *pErrorCode); char *p; - while((p=uprv_strchr(altSepPath.data(), U_FILE_ALT_SEP_CHAR))) { + while ((p = uprv_strchr(altSepPath.data(), U_FILE_ALT_SEP_CHAR)) != NULL) { *p = U_FILE_SEP_CHAR; } #if defined (UDATA_DEBUG) @@ -1227,16 +1277,22 @@ doOpenChoice(const char *path, const char *type, const char *name, tocEntryName.append(".", *pErrorCode).append(type, *pErrorCode); tocEntryPath.append(".", *pErrorCode).append(type, *pErrorCode); } - tocEntryPathSuffix = tocEntryPath.data()+tocEntrySuffixIndex; /* suffix starts here */ + // The +1 is for the U_FILE_SEP_CHAR that is always appended above. + tocEntryPathSuffix = tocEntryPath.data() + tocEntrySuffixIndex + 1; /* suffix starts here */ #ifdef UDATA_DEBUG fprintf(stderr, " tocEntryName = %s\n", tocEntryName.data()); fprintf(stderr, " tocEntryPath = %s\n", tocEntryName.data()); #endif +#if !defined(ICU_DATA_DIR_WINDOWS) if(path == NULL) { path = COMMON_DATA_NAME; /* "icudt26e" */ } +#else + // When using the Windows system data, we expects only a single data file. + path = COMMON_DATA_NAME; /* "icudt26e" */ +#endif /************************ Begin loop looking for ind. files ***************/ #ifdef UDATA_DEBUG @@ -1246,15 +1302,83 @@ doOpenChoice(const char *path, const char *type, const char *name, /* End of dealing with a null basename */ dataPath = u_getDataDirectory(); + /**** Time zone individual files override */ + if (isICUData && isTimeZoneFile(name, type)) { + const char *tzFilesDir = u_getTimeZoneFilesDirectory(pErrorCode); + if (tzFilesDir[0] != 0) { +#ifdef UDATA_DEBUG + fprintf(stderr, "Trying Time Zone Files directory = %s\n", tzFilesDir); +#endif +#ifdef UDATA_TZFILES_DEBUG + fprintf(stderr, "# dOC U_TIMEZONE_FILES_DIR: %s\n", U_TIMEZONE_FILES_DIR); +#endif + +#if defined(U_TIMEZONE_PACKAGE) + // make tztocEntryName, like tocEntryName but with our package name + UErrorCode tzpkgErrorCode = U_ZERO_ERROR; + CharString tztocPkgPath; + tztocPkgPath.append(tzFilesDir, tzpkgErrorCode); + tztocPkgPath.append(U_FILE_SEP_CHAR, tzpkgErrorCode).append(U_TIMEZONE_PACKAGE, tzpkgErrorCode); + CharString tztocEntryName; + tztocEntryName.append(U_TIMEZONE_PACKAGE, tzpkgErrorCode); + if(!treeName.isEmpty()) { + tztocEntryName.append(U_TREE_ENTRY_SEP_CHAR, tzpkgErrorCode).append(treeName, tzpkgErrorCode); + } + tztocEntryName.append(U_TREE_ENTRY_SEP_CHAR, tzpkgErrorCode).append(name, tzpkgErrorCode); + if(type!=NULL && *type!=0) { + tztocEntryName.append(".", tzpkgErrorCode).append(type, tzpkgErrorCode); + } +#ifdef UDATA_TZFILES_DEBUG + fprintf(stderr, "# dOC tz pkg, doLoadFromCommonData start; U_TIMEZONE_PACKAGE: %s, tztocPkgPath.data(): %s, tztocEntryName.data(): %s, name: %s\n", + U_TIMEZONE_PACKAGE, tztocPkgPath.data(), tztocEntryName.data(), name); +#endif + retVal = doLoadFromCommonData(FALSE, "" /*ignored*/, "" /*ignored*/, "" /*ignored*/, + tztocEntryName.data(), // tocEntryName, like icutz44/zoneinfo64.res + tztocPkgPath.data(), // path = path to pkg, like /usr/share/icu/icutz44l + type, name, isAcceptable, context, &subErrorCode, &tzpkgErrorCode); +#ifdef UDATA_TZFILES_DEBUG + fprintf(stderr, "# dOC tz pkg, doLoadFromCommonData end; status %d, retVal %p\n", tzpkgErrorCode, retVal); +#endif + if(U_SUCCESS(tzpkgErrorCode) && retVal != NULL) { + return retVal; + } +#endif /* defined(U_TIMEZONE_PACKAGE) */ + // The following assumes any timezone resources in tzFilesDir are in individual .res files +#ifdef UDATA_TZFILES_DEBUG + fprintf(stderr, "# dOC tz files, doLoadFromIndividualFiles start; tzFilesDir: %s, tocEntryPathSuffix: %s, name: %s\n", + tzFilesDir, tocEntryPathSuffix, name); +#endif + retVal = doLoadFromIndividualFiles(/* pkgName.data() */ "", tzFilesDir, tocEntryPathSuffix, + /* path */ "", type, name, isAcceptable, context, &subErrorCode, pErrorCode); +#ifdef UDATA_TZFILES_DEBUG + fprintf(stderr, "# dOC tz files, doLoadFromIndividualFiles end; status %d, retVal %p\n", *pErrorCode, retVal); +#endif + if((retVal != NULL) || U_FAILURE(*pErrorCode)) { + return retVal; + } + } + } + /**** COMMON PACKAGE - only if packages are first. */ if(gDataFileAccess == UDATA_PACKAGES_FIRST) { #ifdef UDATA_DEBUG fprintf(stderr, "Trying packages (UDATA_PACKAGES_FIRST)\n"); #endif /* #2 */ +#ifdef UDATA_TZFILES_DEBUG + if (isTimeZoneFile(name, type)) { + fprintf(stderr, "# dOC std common 1, doLoadFromCommonData start; U_TIMEZONE_PACKAGE: path: %s, tocEntryName.data(): %s, name: %s\n", + path, tocEntryName.data(), name); + } +#endif retVal = doLoadFromCommonData(isICUData, pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(), path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); +#ifdef UDATA_TZFILES_DEBUG + if (isTimeZoneFile(name, type)) { + fprintf(stderr, "# dOC std common 1, doLoadFromCommonData end; status %d, retVal %p\n", *pErrorCode, retVal); + } +#endif if((retVal != NULL) || U_FAILURE(*pErrorCode)) { return retVal; } @@ -1268,8 +1392,19 @@ doOpenChoice(const char *path, const char *type, const char *name, #endif /* Check to make sure that there is a dataPath to iterate over */ if ((dataPath && *dataPath) || !isICUData) { +#ifdef UDATA_TZFILES_DEBUG + if (isTimeZoneFile(name, type)) { + fprintf(stderr, "# dOC std indiv files, doLoadFromIndividualFiles start; dataPath: %s, tocEntryPathSuffix: %s, name: %s\n", + dataPath, tocEntryPathSuffix, name); + } +#endif retVal = doLoadFromIndividualFiles(pkgName.data(), dataPath, tocEntryPathSuffix, path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); +#ifdef UDATA_TZFILES_DEBUG + if (isTimeZoneFile(name, type)) { + fprintf(stderr, "# dOC std indiv files, doLoadFromIndividualFiles end; status %d, retVal %p\n", *pErrorCode, retVal); + } +#endif if((retVal != NULL) || U_FAILURE(*pErrorCode)) { return retVal; } @@ -1281,10 +1416,21 @@ doOpenChoice(const char *path, const char *type, const char *name, (gDataFileAccess==UDATA_FILES_FIRST)) { #ifdef UDATA_DEBUG fprintf(stderr, "Trying packages (UDATA_ONLY_PACKAGES || UDATA_FILES_FIRST)\n"); +#endif +#ifdef UDATA_TZFILES_DEBUG + if (isTimeZoneFile(name, type)) { + fprintf(stderr, "# dOC std common 2, doLoadFromCommonData start; U_TIMEZONE_PACKAGE: path: %s, tocEntryName.data(): %s, name: %s\n", + path, tocEntryName.data(), name); + } #endif retVal = doLoadFromCommonData(isICUData, pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(), path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); +#ifdef UDATA_TZFILES_DEBUG + if (isTimeZoneFile(name, type)) { + fprintf(stderr, "# dOC std common 2, doLoadFromCommonData end; status %d, retVal %p\n", *pErrorCode, retVal); + } +#endif if((retVal != NULL) || U_FAILURE(*pErrorCode)) { return retVal; } @@ -1386,5 +1532,6 @@ udata_getInfo(UDataMemory *pData, UDataInfo *pInfo) { U_CAPI void U_EXPORT2 udata_setFileAccess(UDataFileAccess access, UErrorCode * /*status*/) { + // Note: this function is documented as not thread safe. gDataFileAccess = access; }