udata.cpp (55048B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ****************************************************************************** 5 * 6 * Copyright (C) 1999-2016, International Business Machines 7 * Corporation and others. All Rights Reserved. 8 * 9 ****************************************************************************** 10 * file name: udata.cpp 11 * encoding: UTF-8 12 * tab size: 8 (not used) 13 * indentation:4 14 * 15 * created on: 1999oct25 16 * created by: Markus W. Scherer 17 */ 18 19 #include "unicode/utypes.h" /* U_PLATFORM etc. */ 20 21 #ifdef __GNUC__ 22 /* if gcc 23 #define ATTRIBUTE_WEAK __attribute__ ((weak)) 24 might have to #include some other header 25 */ 26 #endif 27 28 #include "unicode/putil.h" 29 #include "unicode/udata.h" 30 #include "unicode/uversion.h" 31 #include "charstr.h" 32 #include "cmemory.h" 33 #include "cstring.h" 34 #include "mutex.h" 35 #include "putilimp.h" 36 #include "restrace.h" 37 #include "uassert.h" 38 #include "ucln_cmn.h" 39 #include "ucmndata.h" 40 #include "udatamem.h" 41 #include "uhash.h" 42 #include "umapfile.h" 43 #include "umutex.h" 44 45 /*********************************************************************** 46 * 47 * Notes on the organization of the ICU data implementation 48 * 49 * All of the public API is defined in udata.h 50 * 51 * The implementation is split into several files... 52 * 53 * - udata.c (this file) contains higher level code that knows about 54 * the search paths for locating data, caching opened data, etc. 55 * 56 * - umapfile.c contains the low level platform-specific code for actually loading 57 * (memory mapping, file reading, whatever) data into memory. 58 * 59 * - ucmndata.c deals with the tables of contents of ICU data items within 60 * an ICU common format data file. The implementation includes 61 * an abstract interface and support for multiple TOC formats. 62 * All knowledge of any specific TOC format is encapsulated here. 63 * 64 * - udatamem.c has code for managing UDataMemory structs. These are little 65 * descriptor objects for blocks of memory holding ICU data of 66 * various types. 67 */ 68 69 /* configuration ---------------------------------------------------------- */ 70 71 /* If you are excruciatingly bored turn this on .. */ 72 /* #define UDATA_DEBUG 1 */ 73 74 #if defined(UDATA_DEBUG) 75 # include <stdio.h> 76 #endif 77 78 U_NAMESPACE_USE 79 80 /* 81 * Forward declarations 82 */ 83 static UDataMemory *udata_findCachedData(const char *path, UErrorCode &err); 84 85 /*********************************************************************** 86 * 87 * static (Global) data 88 * 89 ************************************************************************/ 90 91 /* 92 * Pointers to the common ICU data. 93 * 94 * We store multiple pointers to ICU data packages and iterate through them 95 * when looking for a data item. 96 * 97 * It is possible to combine this with dependency inversion: 98 * One or more data package libraries may export 99 * functions that each return a pointer to their piece of the ICU data, 100 * and this file would import them as weak functions, without a 101 * strong linker dependency from the common library on the data library. 102 * 103 * Then we can have applications depend on only that part of ICU's data 104 * that they really need, reducing the size of binaries that take advantage 105 * of this. 106 */ 107 static UDataMemory *gCommonICUDataArray[10] = { nullptr }; // Access protected by icu global mutex. 108 109 static u_atomic_int32_t gHaveTriedToLoadCommonData {0}; // See extendICUData(). 110 111 static UHashtable *gCommonDataCache = nullptr; /* Global hash table of opened ICU data files. */ 112 static icu::UInitOnce gCommonDataCacheInitOnce {}; 113 114 #if !defined(ICU_DATA_DIR_WINDOWS) 115 static UDataFileAccess gDataFileAccess = UDATA_DEFAULT_ACCESS; // Access not synchronized. 116 // Modifying is documented as thread-unsafe. 117 #else 118 // If we are using the Windows data directory, then look in one spot only. 119 static UDataFileAccess gDataFileAccess = UDATA_NO_FILES; 120 #endif 121 122 static UBool U_CALLCONV 123 udata_cleanup() 124 { 125 int32_t i; 126 127 if (gCommonDataCache) { /* Delete the cache of user data mappings. */ 128 uhash_close(gCommonDataCache); /* Table owns the contents, and will delete them. */ 129 gCommonDataCache = nullptr; /* Cleanup is not thread safe. */ 130 } 131 gCommonDataCacheInitOnce.reset(); 132 133 for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray) && gCommonICUDataArray[i] != nullptr; ++i) { 134 udata_close(gCommonICUDataArray[i]); 135 gCommonICUDataArray[i] = nullptr; 136 } 137 gHaveTriedToLoadCommonData = 0; 138 139 return true; /* Everything was cleaned up */ 140 } 141 142 static UBool U_CALLCONV 143 findCommonICUDataByName(const char *inBasename, UErrorCode &err) 144 { 145 UBool found = false; 146 int32_t i; 147 148 UDataMemory *pData = udata_findCachedData(inBasename, err); 149 if (U_FAILURE(err) || pData == nullptr) 150 return false; 151 152 { 153 Mutex lock; 154 for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray); ++i) { 155 if ((gCommonICUDataArray[i] != nullptr) && (gCommonICUDataArray[i]->pHeader == pData->pHeader)) { 156 /* The data pointer is already in the array. */ 157 found = true; 158 break; 159 } 160 } 161 } 162 return found; 163 } 164 165 166 /* 167 * setCommonICUData. Set a UDataMemory to be the global ICU Data 168 */ 169 static UBool 170 setCommonICUData(UDataMemory *pData, /* The new common data. Belongs to caller, we copy it. */ 171 UBool warn, /* If true, set USING_DEFAULT warning if ICUData was */ 172 /* changed by another thread before we got to it. */ 173 UErrorCode *pErr) 174 { 175 UDataMemory *newCommonData = UDataMemory_createNewInstance(pErr); 176 int32_t i; 177 UBool didUpdate = false; 178 if (U_FAILURE(*pErr)) { 179 return false; 180 } 181 182 /* For the assignment, other threads must cleanly see either the old */ 183 /* or the new, not some partially initialized new. The old can not be */ 184 /* deleted - someone may still have a pointer to it lying around in */ 185 /* their locals. */ 186 UDatamemory_assign(newCommonData, pData); 187 umtx_lock(nullptr); 188 for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray); ++i) { 189 if (gCommonICUDataArray[i] == nullptr) { 190 gCommonICUDataArray[i] = newCommonData; 191 didUpdate = true; 192 break; 193 } else if (gCommonICUDataArray[i]->pHeader == pData->pHeader) { 194 /* The same data pointer is already in the array. */ 195 break; 196 } 197 } 198 umtx_unlock(nullptr); 199 200 if (i == UPRV_LENGTHOF(gCommonICUDataArray) && warn) { 201 *pErr = U_USING_DEFAULT_WARNING; 202 } 203 if (didUpdate) { 204 ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup); 205 } else { 206 uprv_free(newCommonData); 207 } 208 return didUpdate; 209 } 210 211 #if !defined(ICU_DATA_DIR_WINDOWS) 212 213 static UBool 214 setCommonICUDataPointer(const void *pData, UBool /*warn*/, UErrorCode *pErrorCode) { 215 UDataMemory tData; 216 UDataMemory_init(&tData); 217 UDataMemory_setData(&tData, pData); 218 udata_checkCommonData(&tData, pErrorCode); 219 return setCommonICUData(&tData, false, pErrorCode); 220 } 221 222 #endif 223 224 static const char * 225 findBasename(const char *path) { 226 const char *basename=uprv_strrchr(path, U_FILE_SEP_CHAR); 227 if(basename==nullptr) { 228 return path; 229 } else { 230 return basename+1; 231 } 232 } 233 234 #ifdef UDATA_DEBUG 235 static const char * 236 packageNameFromPath(const char *path) 237 { 238 if((path == nullptr) || (*path == 0)) { 239 return U_ICUDATA_NAME; 240 } 241 242 path = findBasename(path); 243 244 if((path == nullptr) || (*path == 0)) { 245 return U_ICUDATA_NAME; 246 } 247 248 return path; 249 } 250 #endif 251 252 /*----------------------------------------------------------------------* 253 * * 254 * Cache for common data * 255 * Functions for looking up or adding entries to a cache of * 256 * data that has been previously opened. Avoids a potentially * 257 * expensive operation of re-opening the data for subsequent * 258 * uses. * 259 * * 260 * Data remains cached for the duration of the process. * 261 * * 262 *----------------------------------------------------------------------*/ 263 264 typedef struct DataCacheElement { 265 char *name; 266 UDataMemory *item; 267 } DataCacheElement; 268 269 270 271 /* 272 * Deleter function for DataCacheElements. 273 * udata cleanup function closes the hash table; hash table in turn calls back to 274 * here for each entry. 275 */ 276 static void U_CALLCONV DataCacheElement_deleter(void *pDCEl) { 277 DataCacheElement* p = static_cast<DataCacheElement*>(pDCEl); 278 udata_close(p->item); /* unmaps storage */ 279 uprv_free(p->name); /* delete the hash key string. */ 280 uprv_free(pDCEl); /* delete 'this' */ 281 } 282 283 static void U_CALLCONV udata_initHashTable(UErrorCode &err) { 284 U_ASSERT(gCommonDataCache == nullptr); 285 gCommonDataCache = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &err); 286 if (U_FAILURE(err)) { 287 return; 288 } 289 U_ASSERT(gCommonDataCache != nullptr); 290 uhash_setValueDeleter(gCommonDataCache, DataCacheElement_deleter); 291 ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup); 292 } 293 294 /* udata_getCacheHashTable() 295 * Get the hash table used to store the data cache entries. 296 * Lazy create it if it doesn't yet exist. 297 */ 298 static UHashtable *udata_getHashTable(UErrorCode &err) { 299 umtx_initOnce(gCommonDataCacheInitOnce, &udata_initHashTable, err); 300 return gCommonDataCache; 301 } 302 303 304 305 static UDataMemory *udata_findCachedData(const char *path, UErrorCode &err) 306 { 307 UHashtable *htable; 308 UDataMemory *retVal = nullptr; 309 DataCacheElement *el; 310 const char *baseName; 311 312 htable = udata_getHashTable(err); 313 if (U_FAILURE(err)) { 314 return nullptr; 315 } 316 317 baseName = findBasename(path); /* Cache remembers only the base name, not the full path. */ 318 umtx_lock(nullptr); 319 el = static_cast<DataCacheElement*>(uhash_get(htable, baseName)); 320 umtx_unlock(nullptr); 321 if (el != nullptr) { 322 retVal = el->item; 323 } 324 #ifdef UDATA_DEBUG 325 fprintf(stderr, "Cache: [%s] -> %p\n", baseName, (void*) retVal); 326 #endif 327 return retVal; 328 } 329 330 331 static UDataMemory *udata_cacheDataItem(const char *path, UDataMemory *item, UErrorCode *pErr) { 332 DataCacheElement *newElement; 333 const char *baseName; 334 int32_t nameLen; 335 UHashtable *htable; 336 DataCacheElement *oldValue = nullptr; 337 UErrorCode subErr = U_ZERO_ERROR; 338 339 htable = udata_getHashTable(*pErr); 340 if (U_FAILURE(*pErr)) { 341 return nullptr; 342 } 343 344 /* Create a new DataCacheElement - the thingy we store in the hash table - 345 * and copy the supplied path and UDataMemoryItems into it. 346 */ 347 newElement = static_cast<DataCacheElement*>(uprv_malloc(sizeof(DataCacheElement))); 348 if (newElement == nullptr) { 349 *pErr = U_MEMORY_ALLOCATION_ERROR; 350 return nullptr; 351 } 352 newElement->item = UDataMemory_createNewInstance(pErr); 353 if (U_FAILURE(*pErr)) { 354 uprv_free(newElement); 355 return nullptr; 356 } 357 UDatamemory_assign(newElement->item, item); 358 359 baseName = findBasename(path); 360 nameLen = static_cast<int32_t>(uprv_strlen(baseName)); 361 newElement->name = static_cast<char*>(uprv_malloc(nameLen + 1)); 362 if (newElement->name == nullptr) { 363 *pErr = U_MEMORY_ALLOCATION_ERROR; 364 uprv_free(newElement->item); 365 uprv_free(newElement); 366 return nullptr; 367 } 368 uprv_strcpy(newElement->name, baseName); 369 370 /* Stick the new DataCacheElement into the hash table. 371 */ 372 umtx_lock(nullptr); 373 oldValue = static_cast<DataCacheElement*>(uhash_get(htable, path)); 374 if (oldValue != nullptr) { 375 subErr = U_USING_DEFAULT_WARNING; 376 } 377 else { 378 uhash_put( 379 htable, 380 newElement->name, /* Key */ 381 newElement, /* Value */ 382 &subErr); 383 } 384 umtx_unlock(nullptr); 385 386 #ifdef UDATA_DEBUG 387 fprintf(stderr, "Cache: [%s] <<< %p : %s. vFunc=%p\n", newElement->name, 388 (void*) newElement->item, u_errorName(subErr), (void*) newElement->item->vFuncs); 389 #endif 390 391 if (subErr == U_USING_DEFAULT_WARNING || U_FAILURE(subErr)) { 392 *pErr = subErr; /* copy sub err unto fillin ONLY if something happens. */ 393 uprv_free(newElement->name); 394 uprv_free(newElement->item); 395 uprv_free(newElement); 396 return oldValue ? oldValue->item : nullptr; 397 } 398 399 return newElement->item; 400 } 401 402 /*----------------------------------------------------------------------*============== 403 * * 404 * Path management. Could be shared with other tools/etc if need be * 405 * later on. * 406 * * 407 *----------------------------------------------------------------------*/ 408 409 U_NAMESPACE_BEGIN 410 411 class UDataPathIterator 412 { 413 public: 414 UDataPathIterator(const char *path, const char *pkg, 415 const char *item, const char *suffix, UBool doCheckLastFour, 416 UErrorCode *pErrorCode); 417 const char *next(UErrorCode *pErrorCode); 418 419 private: 420 const char *path; /* working path (u_icudata_Dir) */ 421 const char *nextPath; /* path following this one */ 422 const char *basename; /* item's basename (icudt22e_mt.res)*/ 423 424 StringPiece suffix; /* item suffix (can be null) */ 425 426 uint32_t basenameLen; /* length of basename */ 427 428 CharString itemPath; /* path passed in with item name */ 429 CharString pathBuffer; /* output path for this it'ion */ 430 CharString packageStub; /* example: "/icudt28b". Will ignore that leaf in set paths. */ 431 432 UBool checkLastFour; /* if true then allow paths such as '/foo/myapp.dat' 433 * to match, checks last 4 chars of suffix with 434 * last 4 of path, then previous chars. */ 435 }; 436 437 /** 438 * @param iter The iterator to be initialized. Its current state does not matter. 439 * @param inPath The full pathname to be iterated over. If nullptr, defaults to U_ICUDATA_NAME 440 * @param pkg Package which is being searched for, ex "icudt28l". Will ignore leaf directories such as /icudt28l 441 * @param item Item to be searched for. Can include full path, such as /a/b/foo.dat 442 * @param inSuffix Optional item suffix, if not-null (ex. ".dat") then 'path' can contain 'item' explicitly. 443 * Ex: 'stuff.dat' would be found in '/a/foo:/tmp/stuff.dat:/bar/baz' as item #2. 444 * '/blarg/stuff.dat' would also be found. 445 * Note: inSuffix may also be the 'item' being searched for as well, (ex: "ibm-5348_P100-1997.cnv"), in which case 446 * the 'item' parameter is often the same as pkg. (Though sometimes might have a tree part as well, ex: "icudt62l-curr"). 447 */ 448 UDataPathIterator::UDataPathIterator(const char *inPath, const char *pkg, 449 const char *item, const char *inSuffix, UBool doCheckLastFour, 450 UErrorCode *pErrorCode) 451 { 452 #ifdef UDATA_DEBUG 453 fprintf(stderr, "SUFFIX1=%s PATH=%s\n", inSuffix, inPath); 454 #endif 455 /** Path **/ 456 if(inPath == nullptr) { 457 path = u_getDataDirectory(); 458 } else { 459 path = inPath; 460 } 461 462 /** Package **/ 463 if(pkg != nullptr) { 464 packageStub.append(U_FILE_SEP_CHAR, *pErrorCode).append(pkg, *pErrorCode); 465 #ifdef UDATA_DEBUG 466 fprintf(stderr, "STUB=%s [%d]\n", packageStub.data(), packageStub.length()); 467 #endif 468 } 469 470 /** Item **/ 471 basename = findBasename(item); 472 basenameLen = static_cast<int32_t>(uprv_strlen(basename)); 473 474 /** Item path **/ 475 if(basename == item) { 476 nextPath = path; 477 } else { 478 itemPath.append(item, static_cast<int32_t>(basename - item), *pErrorCode); 479 nextPath = itemPath.data(); 480 } 481 #ifdef UDATA_DEBUG 482 fprintf(stderr, "SUFFIX=%s [%p]\n", inSuffix, (void*) inSuffix); 483 #endif 484 485 /** Suffix **/ 486 if(inSuffix != nullptr) { 487 suffix = inSuffix; 488 } else { 489 suffix = ""; 490 } 491 492 checkLastFour = doCheckLastFour; 493 494 /* pathBuffer will hold the output path strings returned by this iterator */ 495 496 #ifdef UDATA_DEBUG 497 fprintf(stderr, "0: init %s -> [path=%s], [base=%s], [suff=%s], [itempath=%s], [nextpath=%s], [checklast4=%s]\n", 498 item, 499 path, 500 basename, 501 suffix.data(), 502 itemPath.data(), 503 nextPath, 504 checkLastFour?"true":"false"); 505 #endif 506 } 507 508 /** 509 * Get the next path on the list. 510 * 511 * @param iter The Iter to be used 512 * @param len If set, pointer to the length of the returned path, for convenience. 513 * @return Pointer to the next path segment, or nullptr if there are no more. 514 */ 515 const char *UDataPathIterator::next(UErrorCode *pErrorCode) 516 { 517 if(U_FAILURE(*pErrorCode)) { 518 return nullptr; 519 } 520 521 const char *currentPath = nullptr; 522 int32_t pathLen = 0; 523 const char *pathBasename; 524 525 do 526 { 527 if( nextPath == nullptr ) { 528 break; 529 } 530 currentPath = nextPath; 531 532 if(nextPath == itemPath.data()) { /* we were processing item's path. */ 533 nextPath = path; /* start with regular path next tm. */ 534 pathLen = static_cast<int32_t>(uprv_strlen(currentPath)); 535 } else { 536 /* fix up next for next time */ 537 nextPath = uprv_strchr(currentPath, U_PATH_SEP_CHAR); 538 if(nextPath == nullptr) { 539 /* segment: entire path */ 540 pathLen = static_cast<int32_t>(uprv_strlen(currentPath)); 541 } else { 542 /* segment: until next segment */ 543 pathLen = static_cast<int32_t>(nextPath - currentPath); 544 /* skip divider */ 545 nextPath ++; 546 } 547 } 548 549 if(pathLen == 0) { 550 continue; 551 } 552 553 #ifdef UDATA_DEBUG 554 fprintf(stderr, "rest of path (IDD) = %s\n", currentPath); 555 fprintf(stderr, " "); 556 { 557 int32_t qqq; 558 for(qqq=0;qqq<pathLen;qqq++) 559 { 560 fprintf(stderr, " "); 561 } 562 563 fprintf(stderr, "^\n"); 564 } 565 #endif 566 pathBuffer.clear().append(currentPath, pathLen, *pErrorCode); 567 568 /* check for .dat files */ 569 pathBasename = findBasename(pathBuffer.data()); 570 571 if(checkLastFour && 572 (pathLen>=4) && 573 uprv_strncmp(pathBuffer.data() +(pathLen-4), suffix.data(), 4)==0 && /* suffix matches */ 574 uprv_strncmp(findBasename(pathBuffer.data()), basename, basenameLen)==0 && /* base matches */ 575 uprv_strlen(pathBasename)==(basenameLen+4)) { /* base+suffix = full len */ 576 577 #ifdef UDATA_DEBUG 578 fprintf(stderr, "Have %s file on the path: %s\n", suffix.data(), pathBuffer.data()); 579 #endif 580 /* do nothing */ 581 } 582 else 583 { /* regular dir path */ 584 if(pathBuffer[pathLen-1] != U_FILE_SEP_CHAR) { 585 if((pathLen>=4) && 586 uprv_strncmp(pathBuffer.data()+(pathLen-4), ".dat", 4) == 0) 587 { 588 #ifdef UDATA_DEBUG 589 fprintf(stderr, "skipping non-directory .dat file %s\n", pathBuffer.data()); 590 #endif 591 continue; 592 } 593 594 /* Check if it is a directory with the same name as our package */ 595 if(!packageStub.isEmpty() && 596 (pathLen > packageStub.length()) && 597 !uprv_strcmp(pathBuffer.data() + pathLen - packageStub.length(), packageStub.data())) { 598 #ifdef UDATA_DEBUG 599 fprintf(stderr, "Found stub %s (will add package %s of len %d)\n", packageStub.data(), basename, basenameLen); 600 #endif 601 pathBuffer.truncate(pathLen - packageStub.length()); 602 } 603 pathBuffer.append(U_FILE_SEP_CHAR, *pErrorCode); 604 } 605 606 /* + basename */ 607 pathBuffer.append(packageStub.data()+1, packageStub.length()-1, *pErrorCode); 608 609 if (!suffix.empty()) /* tack on suffix */ 610 { 611 if (suffix.length() > 4) { 612 // If the suffix is actually an item ("ibm-5348_P100-1997.cnv") and not an extension (".res") 613 // then we need to ensure that the path ends with a separator. 614 pathBuffer.ensureEndsWithFileSeparator(*pErrorCode); 615 } 616 pathBuffer.append(suffix, *pErrorCode); 617 } 618 } 619 620 #ifdef UDATA_DEBUG 621 fprintf(stderr, " --> %s\n", pathBuffer.data()); 622 #endif 623 624 return pathBuffer.data(); 625 626 } while(path); 627 628 /* fell way off the end */ 629 return nullptr; 630 } 631 632 U_NAMESPACE_END 633 634 /* ==================================================================================*/ 635 636 637 /*----------------------------------------------------------------------* 638 * * 639 * Add a static reference to the common data library * 640 * Unless overridden by an explicit udata_setCommonData, this will be * 641 * our common data. * 642 * * 643 *----------------------------------------------------------------------*/ 644 #if !defined(ICU_DATA_DIR_WINDOWS) 645 // When using the Windows system data, we expect only a single data file. 646 extern "C" const DataHeader U_DATA_API U_ICUDATA_ENTRY_POINT; 647 #endif 648 649 /* 650 * This would be a good place for weak-linkage declarations of 651 * partial-data-library access functions where each returns a pointer 652 * to its data package, if it is linked in. 653 */ 654 /* 655 extern const void *uprv_getICUData_collation() ATTRIBUTE_WEAK; 656 extern const void *uprv_getICUData_conversion() ATTRIBUTE_WEAK; 657 */ 658 659 /*----------------------------------------------------------------------* 660 * * 661 * openCommonData Attempt to open a common format (.dat) file * 662 * Map it into memory (if it's not there already) * 663 * and return a UDataMemory object for it. * 664 * * 665 * If the requested data is already open and cached * 666 * just return the cached UDataMem object. * 667 * * 668 *----------------------------------------------------------------------*/ 669 static UDataMemory * 670 openCommonData(const char *path, /* Path from OpenChoice? */ 671 int32_t commonDataIndex, /* ICU Data (index >= 0) if path == nullptr */ 672 UErrorCode *pErrorCode) 673 { 674 UDataMemory tData; 675 const char *pathBuffer; 676 const char *inBasename; 677 678 if (U_FAILURE(*pErrorCode)) { 679 return nullptr; 680 } 681 682 UDataMemory_init(&tData); 683 684 /* ??????? TODO revisit this */ 685 if (commonDataIndex >= 0) { 686 /* "mini-cache" for common ICU data */ 687 if(commonDataIndex >= UPRV_LENGTHOF(gCommonICUDataArray)) { 688 return nullptr; 689 } 690 { 691 Mutex lock; 692 if(gCommonICUDataArray[commonDataIndex] != nullptr) { 693 return gCommonICUDataArray[commonDataIndex]; 694 } 695 #if !defined(ICU_DATA_DIR_WINDOWS) 696 // When using the Windows system data, we expect only a single data file. 697 int32_t i; 698 for(i = 0; i < commonDataIndex; ++i) { 699 if(gCommonICUDataArray[i]->pHeader == &U_ICUDATA_ENTRY_POINT) { 700 /* The linked-in data is already in the list. */ 701 return nullptr; 702 } 703 } 704 #endif 705 } 706 707 /* Add the linked-in data to the list. */ 708 /* 709 * This is where we would check and call weakly linked partial-data-library 710 * access functions. 711 */ 712 /* 713 if (uprv_getICUData_collation) { 714 setCommonICUDataPointer(uprv_getICUData_collation(), false, pErrorCode); 715 } 716 if (uprv_getICUData_conversion) { 717 setCommonICUDataPointer(uprv_getICUData_conversion(), false, pErrorCode); 718 } 719 */ 720 #if !defined(ICU_DATA_DIR_WINDOWS) 721 // When using the Windows system data, we expect only a single data file. 722 setCommonICUDataPointer(&U_ICUDATA_ENTRY_POINT, false, pErrorCode); 723 { 724 Mutex lock; 725 return gCommonICUDataArray[commonDataIndex]; 726 } 727 #endif 728 } 729 730 731 /* request is NOT for ICU Data. */ 732 733 /* Find the base name portion of the supplied path. */ 734 /* inBasename will be left pointing somewhere within the original path string. */ 735 inBasename = findBasename(path); 736 #ifdef UDATA_DEBUG 737 fprintf(stderr, "inBasename = %s\n", inBasename); 738 #endif 739 740 if(*inBasename==0) { 741 /* no basename. This will happen if the original path was a directory name, */ 742 /* like "a/b/c/". (Fallback to separate files will still work.) */ 743 #ifdef UDATA_DEBUG 744 fprintf(stderr, "ocd: no basename in %s, bailing.\n", path); 745 #endif 746 if (U_SUCCESS(*pErrorCode)) { 747 *pErrorCode=U_FILE_ACCESS_ERROR; 748 } 749 return nullptr; 750 } 751 752 /* Is the requested common data file already open and cached? */ 753 /* Note that the cache is keyed by the base name only. The rest of the path, */ 754 /* if any, is not considered. */ 755 UDataMemory *dataToReturn = udata_findCachedData(inBasename, *pErrorCode); 756 if (dataToReturn != nullptr || U_FAILURE(*pErrorCode)) { 757 return dataToReturn; 758 } 759 760 /* Requested item is not in the cache. 761 * Hunt it down, trying all the path locations 762 */ 763 764 UDataPathIterator iter(u_getDataDirectory(), inBasename, path, ".dat", true, pErrorCode); 765 766 while ((UDataMemory_isLoaded(&tData)==false) && (pathBuffer = iter.next(pErrorCode)) != nullptr) 767 { 768 #ifdef UDATA_DEBUG 769 fprintf(stderr, "ocd: trying path %s - ", pathBuffer); 770 #endif 771 uprv_mapFile(&tData, pathBuffer, pErrorCode); 772 #ifdef UDATA_DEBUG 773 fprintf(stderr, "%s\n", UDataMemory_isLoaded(&tData)?"LOADED":"not loaded"); 774 #endif 775 } 776 if (U_FAILURE(*pErrorCode)) { 777 return nullptr; 778 } 779 780 if (U_FAILURE(*pErrorCode)) { 781 return nullptr; 782 } 783 if (!UDataMemory_isLoaded(&tData)) { 784 /* no common data */ 785 *pErrorCode=U_FILE_ACCESS_ERROR; 786 return nullptr; 787 } 788 789 /* we have mapped a file, check its header */ 790 udata_checkCommonData(&tData, pErrorCode); 791 792 793 /* Cache the UDataMemory struct for this .dat file, 794 * so we won't need to hunt it down and map it again next time 795 * something is needed from it. */ 796 return udata_cacheDataItem(inBasename, &tData, pErrorCode); 797 } 798 799 800 /*----------------------------------------------------------------------* 801 * * 802 * extendICUData If the full set of ICU data was not loaded at * 803 * program startup, load it now. This function will * 804 * be called when the lookup of an ICU data item in * 805 * the common ICU data fails. * 806 * * 807 * return true if new data is loaded, false otherwise.* 808 * * 809 *----------------------------------------------------------------------*/ 810 static UBool extendICUData(UErrorCode *pErr) 811 { 812 UDataMemory *pData; 813 UDataMemory copyPData; 814 UBool didUpdate = false; 815 816 /* 817 * There is a chance for a race condition here. 818 * Normally, ICU data is loaded from a DLL or via mmap() and 819 * setCommonICUData() will detect if the same address is set twice. 820 * If ICU is built with data loading via fread() then the address will 821 * be different each time the common data is loaded and we may add 822 * multiple copies of the data. 823 * In this case, use a mutex to prevent the race. 824 * Use a specific mutex to avoid nested locks of the global mutex. 825 */ 826 #if MAP_IMPLEMENTATION==MAP_STDIO 827 static UMutex extendICUDataMutex; 828 umtx_lock(&extendICUDataMutex); 829 #endif 830 if(!umtx_loadAcquire(gHaveTriedToLoadCommonData)) { 831 /* See if we can explicitly open a .dat file for the ICUData. */ 832 pData = openCommonData( 833 U_ICUDATA_NAME, /* "icudt20l" , for example. */ 834 -1, /* Pretend we're not opening ICUData */ 835 pErr); 836 837 /* How about if there is no pData, eh... */ 838 839 UDataMemory_init(©PData); 840 if(pData != nullptr) { 841 UDatamemory_assign(©PData, pData); 842 copyPData.map = nullptr; /* The mapping for this data is owned by the hash table */ 843 copyPData.mapAddr = nullptr; /* which will unmap it when ICU is shut down. */ 844 /* CommonICUData is also unmapped when ICU is shut down.*/ 845 /* To avoid unmapping the data twice, zero out the map */ 846 /* fields in the UDataMemory that we're assigning */ 847 /* to CommonICUData. */ 848 849 didUpdate = /* no longer using this result */ 850 setCommonICUData(©PData,/* The new common data. */ 851 false, /* No warnings if write didn't happen */ 852 pErr); /* setCommonICUData honors errors; NOP if error set */ 853 } 854 855 umtx_storeRelease(gHaveTriedToLoadCommonData, 1); 856 } 857 858 didUpdate = findCommonICUDataByName(U_ICUDATA_NAME, *pErr); /* Return 'true' when a racing writes out the extended */ 859 /* data after another thread has failed to see it (in openCommonData), so */ 860 /* extended data can be examined. */ 861 /* Also handles a race through here before gHaveTriedToLoadCommonData is set. */ 862 863 #if MAP_IMPLEMENTATION==MAP_STDIO 864 umtx_unlock(&extendICUDataMutex); 865 #endif 866 return didUpdate; /* Return true if ICUData pointer was updated. */ 867 /* (Could potentially have been done by another thread racing */ 868 /* us through here, but that's fine, we still return true */ 869 /* so that current thread will also examine extended data. */ 870 } 871 872 /*----------------------------------------------------------------------* 873 * * 874 * udata_setCommonData * 875 * * 876 *----------------------------------------------------------------------*/ 877 U_CAPI void U_EXPORT2 878 udata_setCommonData(const void *data, UErrorCode *pErrorCode) { 879 UDataMemory dataMemory; 880 881 if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { 882 return; 883 } 884 885 if(data==nullptr) { 886 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; 887 return; 888 } 889 890 /* set the data pointer and test for validity */ 891 UDataMemory_init(&dataMemory); 892 UDataMemory_setData(&dataMemory, data); 893 udata_checkCommonData(&dataMemory, pErrorCode); 894 if (U_FAILURE(*pErrorCode)) {return;} 895 896 /* we have good data */ 897 /* Set it up as the ICU Common Data. */ 898 setCommonICUData(&dataMemory, true, pErrorCode); 899 } 900 901 /*--------------------------------------------------------------------------- 902 * 903 * udata_setAppData 904 * 905 *---------------------------------------------------------------------------- */ 906 U_CAPI void U_EXPORT2 907 udata_setAppData(const char *path, const void *data, UErrorCode *err) 908 { 909 UDataMemory udm; 910 911 if(err==nullptr || U_FAILURE(*err)) { 912 return; 913 } 914 if(data==nullptr) { 915 *err=U_ILLEGAL_ARGUMENT_ERROR; 916 return; 917 } 918 919 UDataMemory_init(&udm); 920 UDataMemory_setData(&udm, data); 921 udata_checkCommonData(&udm, err); 922 udata_cacheDataItem(path, &udm, err); 923 } 924 925 /*----------------------------------------------------------------------------* 926 * * 927 * checkDataItem Given a freshly located/loaded data item, either * 928 * an entry in a common file or a separately loaded file, * 929 * sanity check its header, and see if the data is * 930 * acceptable to the app. * 931 * If the data is good, create and return a UDataMemory * 932 * object that can be returned to the application. * 933 * Return nullptr on any sort of failure. * 934 * * 935 *----------------------------------------------------------------------------*/ 936 static UDataMemory * 937 checkDataItem 938 ( 939 const DataHeader *pHeader, /* The data item to be checked. */ 940 UDataMemoryIsAcceptable *isAcceptable, /* App's call-back function */ 941 void *context, /* pass-thru param for above. */ 942 const char *type, /* pass-thru param for above. */ 943 const char *name, /* pass-thru param for above. */ 944 UErrorCode *nonFatalErr, /* Error code if this data was not acceptable */ 945 /* but openChoice should continue with */ 946 /* trying to get data from fallback path. */ 947 UErrorCode *fatalErr /* Bad error, caller should return immediately */ 948 ) 949 { 950 UDataMemory *rDataMem = nullptr; /* the new UDataMemory, to be returned. */ 951 952 if (U_FAILURE(*fatalErr)) { 953 return nullptr; 954 } 955 956 if(pHeader->dataHeader.magic1==0xda && 957 pHeader->dataHeader.magic2==0x27 && 958 (isAcceptable==nullptr || isAcceptable(context, type, name, &pHeader->info)) 959 ) { 960 rDataMem=UDataMemory_createNewInstance(fatalErr); 961 if (U_FAILURE(*fatalErr)) { 962 return nullptr; 963 } 964 rDataMem->pHeader = pHeader; 965 } else { 966 /* the data is not acceptable, look further */ 967 /* If we eventually find something good, this errorcode will be */ 968 /* cleared out. */ 969 *nonFatalErr=U_INVALID_FORMAT_ERROR; 970 } 971 return rDataMem; 972 } 973 974 /** 975 * @return 0 if not loaded, 1 if loaded or err 976 */ 977 static UDataMemory *doLoadFromIndividualFiles(const char *pkgName, 978 const char *dataPath, const char *tocEntryPathSuffix, 979 /* following arguments are the same as doOpenChoice itself */ 980 const char *path, const char *type, const char *name, 981 UDataMemoryIsAcceptable *isAcceptable, void *context, 982 UErrorCode *subErrorCode, 983 UErrorCode *pErrorCode) 984 { 985 const char *pathBuffer; 986 UDataMemory dataMemory; 987 UDataMemory *pEntryData; 988 989 /* look in ind. files: package\nam.typ ========================= */ 990 /* init path iterator for individual files */ 991 UDataPathIterator iter(dataPath, pkgName, path, tocEntryPathSuffix, false, pErrorCode); 992 993 while ((pathBuffer = iter.next(pErrorCode)) != nullptr) 994 { 995 #ifdef UDATA_DEBUG 996 fprintf(stderr, "UDATA: trying individual file %s\n", pathBuffer); 997 #endif 998 if (uprv_mapFile(&dataMemory, pathBuffer, pErrorCode)) 999 { 1000 pEntryData = checkDataItem(dataMemory.pHeader, isAcceptable, context, type, name, subErrorCode, pErrorCode); 1001 if (pEntryData != nullptr) { 1002 /* Data is good. 1003 * Hand off ownership of the backing memory to the user's UDataMemory. 1004 * and return it. */ 1005 pEntryData->mapAddr = dataMemory.mapAddr; 1006 pEntryData->map = dataMemory.map; 1007 pEntryData->length = dataMemory.length; 1008 1009 #ifdef UDATA_DEBUG 1010 fprintf(stderr, "** Mapped file: %s\n", pathBuffer); 1011 #endif 1012 return pEntryData; 1013 } 1014 1015 /* the data is not acceptable, or some error occurred. Either way, unmap the memory */ 1016 udata_close(&dataMemory); 1017 1018 /* If we had a nasty error, bail out completely. */ 1019 if (U_FAILURE(*pErrorCode)) { 1020 return nullptr; 1021 } 1022 1023 /* Otherwise remember that we found data but didn't like it for some reason */ 1024 *subErrorCode=U_INVALID_FORMAT_ERROR; 1025 } 1026 #ifdef UDATA_DEBUG 1027 fprintf(stderr, "%s\n", UDataMemory_isLoaded(&dataMemory)?"LOADED":"not loaded"); 1028 #endif 1029 } 1030 return nullptr; 1031 } 1032 1033 /** 1034 * @return 0 if not loaded, 1 if loaded or err 1035 */ 1036 static UDataMemory *doLoadFromCommonData(UBool isICUData, const char * /*pkgName*/, 1037 const char * /*dataPath*/, const char * /*tocEntryPathSuffix*/, const char *tocEntryName, 1038 /* following arguments are the same as doOpenChoice itself */ 1039 const char *path, const char *type, const char *name, 1040 UDataMemoryIsAcceptable *isAcceptable, void *context, 1041 UErrorCode *subErrorCode, 1042 UErrorCode *pErrorCode) 1043 { 1044 UDataMemory *pEntryData; 1045 const DataHeader *pHeader; 1046 UDataMemory *pCommonData; 1047 int32_t commonDataIndex; 1048 UBool checkedExtendedICUData = false; 1049 /* try to get common data. The loop is for platforms such as the 390 that do 1050 * not initially load the full set of ICU data. If the lookup of an ICU data item 1051 * fails, the full (but slower to load) set is loaded, the and the loop repeats, 1052 * trying the lookup again. Once the full set of ICU data is loaded, the loop wont 1053 * repeat because the full set will be checked the first time through. 1054 * 1055 * The loop also handles the fallback to a .dat file if the application linked 1056 * to the stub data library rather than a real library. 1057 */ 1058 for (commonDataIndex = isICUData ? 0 : -1;;) { 1059 pCommonData=openCommonData(path, commonDataIndex, subErrorCode); /** search for pkg **/ 1060 1061 if(U_SUCCESS(*subErrorCode) && pCommonData!=nullptr) { 1062 int32_t length; 1063 1064 /* look up the data piece in the common data */ 1065 pHeader=pCommonData->vFuncs->Lookup(pCommonData, tocEntryName, &length, subErrorCode); 1066 #ifdef UDATA_DEBUG 1067 fprintf(stderr, "%s: pHeader=%p - %s\n", tocEntryName, (void*) pHeader, u_errorName(*subErrorCode)); 1068 #endif 1069 1070 if(pHeader!=nullptr) { 1071 pEntryData = checkDataItem(pHeader, isAcceptable, context, type, name, subErrorCode, pErrorCode); 1072 #ifdef UDATA_DEBUG 1073 fprintf(stderr, "pEntryData=%p\n", (void*) pEntryData); 1074 #endif 1075 if (U_FAILURE(*pErrorCode)) { 1076 return nullptr; 1077 } 1078 if (pEntryData != nullptr) { 1079 pEntryData->length = length; 1080 return pEntryData; 1081 } 1082 } 1083 } 1084 // If we failed due to being out-of-memory, then stop early and report the error. 1085 if (*subErrorCode == U_MEMORY_ALLOCATION_ERROR) { 1086 *pErrorCode = *subErrorCode; 1087 return nullptr; 1088 } 1089 /* Data wasn't found. If we were looking for an ICUData item and there is 1090 * more data available, load it and try again, 1091 * otherwise break out of this loop. */ 1092 if (!isICUData) { 1093 return nullptr; 1094 } else if (pCommonData != nullptr) { 1095 ++commonDataIndex; /* try the next data package */ 1096 } else if ((!checkedExtendedICUData) && extendICUData(subErrorCode)) { 1097 checkedExtendedICUData = true; 1098 /* try this data package slot again: it changed from nullptr to non-nullptr */ 1099 } else { 1100 return nullptr; 1101 } 1102 } 1103 } 1104 1105 /* 1106 * Identify the Time Zone resources that are subject to special override data loading. 1107 */ 1108 static UBool isTimeZoneFile(const char *name, const char *type) { 1109 return ((uprv_strcmp(type, "res") == 0) && 1110 (uprv_strcmp(name, "zoneinfo64") == 0 || 1111 uprv_strcmp(name, "timezoneTypes") == 0 || 1112 uprv_strcmp(name, "windowsZones") == 0 || 1113 uprv_strcmp(name, "metaZones") == 0)); 1114 } 1115 1116 /* 1117 * A note on the ownership of Mapped Memory 1118 * 1119 * For common format files, ownership resides with the UDataMemory object 1120 * that lives in the cache of opened common data. These UDataMemorys are private 1121 * to the udata implementation, and are never seen directly by users. 1122 * 1123 * The UDataMemory objects returned to users will have the address of some desired 1124 * data within the mapped region, but they wont have the mapping info itself, and thus 1125 * won't cause anything to be removed from memory when they are closed. 1126 * 1127 * For individual data files, the UDataMemory returned to the user holds the 1128 * information necessary to unmap the data on close. If the user independently 1129 * opens the same data file twice, two completely independent mappings will be made. 1130 * (There is no cache of opened data items from individual files, only a cache of 1131 * opened Common Data files, that is, files containing a collection of data items.) 1132 * 1133 * For common data passed in from the user via udata_setAppData() or 1134 * udata_setCommonData(), ownership remains with the user. 1135 * 1136 * UDataMemory objects themselves, as opposed to the memory they describe, 1137 * can be anywhere - heap, stack/local or global. 1138 * They have a flag to indicate when they're heap allocated and thus 1139 * must be deleted when closed. 1140 */ 1141 1142 1143 /*----------------------------------------------------------------------------* 1144 * * 1145 * main data loading functions * 1146 * * 1147 *----------------------------------------------------------------------------*/ 1148 static UDataMemory * 1149 doOpenChoice(const char *path, const char *type, const char *name, 1150 UDataMemoryIsAcceptable *isAcceptable, void *context, 1151 UErrorCode *pErrorCode) 1152 { 1153 UDataMemory *retVal = nullptr; 1154 1155 const char *dataPath; 1156 1157 int32_t tocEntrySuffixIndex; 1158 const char *tocEntryPathSuffix; 1159 UErrorCode subErrorCode=U_ZERO_ERROR; 1160 const char *treeChar; 1161 1162 UBool isICUData = false; 1163 1164 1165 FileTracer::traceOpen(path, type, name); 1166 1167 1168 /* Is this path ICU data? */ 1169 if(path == nullptr || 1170 !strcmp(path, U_ICUDATA_ALIAS) || /* "ICUDATA" */ 1171 !uprv_strncmp(path, U_ICUDATA_NAME U_TREE_SEPARATOR_STRING, /* "icudt26e-" */ 1172 uprv_strlen(U_ICUDATA_NAME U_TREE_SEPARATOR_STRING)) || 1173 !uprv_strncmp(path, U_ICUDATA_ALIAS U_TREE_SEPARATOR_STRING, /* "ICUDATA-" */ 1174 uprv_strlen(U_ICUDATA_ALIAS U_TREE_SEPARATOR_STRING))) { 1175 isICUData = true; 1176 } 1177 1178 #if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) /* Windows: try "foo\bar" and "foo/bar" */ 1179 /* remap from alternate path char to the main one */ 1180 CharString altSepPath; 1181 if(path) { 1182 if(uprv_strchr(path,U_FILE_ALT_SEP_CHAR) != nullptr) { 1183 altSepPath.append(path, *pErrorCode); 1184 char *p; 1185 while ((p = uprv_strchr(altSepPath.data(), U_FILE_ALT_SEP_CHAR)) != nullptr) { 1186 *p = U_FILE_SEP_CHAR; 1187 } 1188 #if defined (UDATA_DEBUG) 1189 fprintf(stderr, "Changed path from [%s] to [%s]\n", path, altSepPath.data()); 1190 #endif 1191 path = altSepPath.data(); 1192 } 1193 } 1194 #endif 1195 1196 CharString tocEntryName; /* entry name in tree format. ex: 'icudt28b/coll/ar.res' */ 1197 CharString tocEntryPath; /* entry name in path format. ex: 'icudt28b\\coll\\ar.res' */ 1198 1199 CharString pkgName; 1200 CharString treeName; 1201 1202 /* ======= Set up strings */ 1203 if(path==nullptr) { 1204 pkgName.append(U_ICUDATA_NAME, *pErrorCode); 1205 } else { 1206 const char *pkg; 1207 const char *first; 1208 pkg = uprv_strrchr(path, U_FILE_SEP_CHAR); 1209 first = uprv_strchr(path, U_FILE_SEP_CHAR); 1210 if(uprv_pathIsAbsolute(path) || (pkg != first)) { /* more than one slash in the path- not a tree name */ 1211 /* see if this is an /absolute/path/to/package path */ 1212 if(pkg) { 1213 pkgName.append(pkg+1, *pErrorCode); 1214 } else { 1215 pkgName.append(path, *pErrorCode); 1216 } 1217 } else { 1218 treeChar = uprv_strchr(path, U_TREE_SEPARATOR); 1219 if(treeChar) { 1220 treeName.append(treeChar+1, *pErrorCode); /* following '-' */ 1221 if(isICUData) { 1222 pkgName.append(U_ICUDATA_NAME, *pErrorCode); 1223 } else { 1224 pkgName.append(path, static_cast<int32_t>(treeChar - path), *pErrorCode); 1225 if (first == nullptr) { 1226 /* 1227 This user data has no path, but there is a tree name. 1228 Look up the correct path from the data cache later. 1229 */ 1230 path = pkgName.data(); 1231 } 1232 } 1233 } else { 1234 if(isICUData) { 1235 pkgName.append(U_ICUDATA_NAME, *pErrorCode); 1236 } else { 1237 pkgName.append(path, *pErrorCode); 1238 } 1239 } 1240 } 1241 } 1242 1243 #ifdef UDATA_DEBUG 1244 fprintf(stderr, " P=%s T=%s\n", pkgName.data(), treeName.data()); 1245 #endif 1246 1247 /* setting up the entry name and file name 1248 * Make up a full name by appending the type to the supplied 1249 * name, assuming that a type was supplied. 1250 */ 1251 1252 /* prepend the package */ 1253 tocEntryName.append(pkgName, *pErrorCode); 1254 tocEntryPath.append(pkgName, *pErrorCode); 1255 tocEntrySuffixIndex = tocEntryName.length(); 1256 1257 if(!treeName.isEmpty()) { 1258 tocEntryName.append(U_TREE_ENTRY_SEP_CHAR, *pErrorCode).append(treeName, *pErrorCode); 1259 tocEntryPath.append(U_FILE_SEP_CHAR, *pErrorCode).append(treeName, *pErrorCode); 1260 } 1261 1262 tocEntryName.append(U_TREE_ENTRY_SEP_CHAR, *pErrorCode).append(name, *pErrorCode); 1263 tocEntryPath.append(U_FILE_SEP_CHAR, *pErrorCode).append(name, *pErrorCode); 1264 if(type!=nullptr && *type!=0) { 1265 tocEntryName.append(".", *pErrorCode).append(type, *pErrorCode); 1266 tocEntryPath.append(".", *pErrorCode).append(type, *pErrorCode); 1267 } 1268 // The +1 is for the U_FILE_SEP_CHAR that is always appended above. 1269 tocEntryPathSuffix = tocEntryPath.data() + tocEntrySuffixIndex + 1; /* suffix starts here */ 1270 1271 #ifdef UDATA_DEBUG 1272 fprintf(stderr, " tocEntryName = %s\n", tocEntryName.data()); 1273 fprintf(stderr, " tocEntryPath = %s\n", tocEntryName.data()); 1274 #endif 1275 1276 #if !defined(ICU_DATA_DIR_WINDOWS) 1277 if(path == nullptr) { 1278 path = COMMON_DATA_NAME; /* "icudt26e" */ 1279 } 1280 #else 1281 // When using the Windows system data, we expects only a single data file. 1282 path = COMMON_DATA_NAME; /* "icudt26e" */ 1283 #endif 1284 1285 /************************ Begin loop looking for ind. files ***************/ 1286 #ifdef UDATA_DEBUG 1287 fprintf(stderr, "IND: inBasename = %s, pkg=%s\n", "(n/a)", packageNameFromPath(path)); 1288 #endif 1289 1290 /* End of dealing with a null basename */ 1291 dataPath = u_getDataDirectory(); 1292 1293 /**** Time zone individual files override */ 1294 if (isICUData && isTimeZoneFile(name, type)) { 1295 const char *tzFilesDir = u_getTimeZoneFilesDirectory(pErrorCode); 1296 if (tzFilesDir[0] != 0) { 1297 #ifdef UDATA_DEBUG 1298 fprintf(stderr, "Trying Time Zone Files directory = %s\n", tzFilesDir); 1299 #endif 1300 retVal = doLoadFromIndividualFiles(/* pkgName.data() */ "", tzFilesDir, tocEntryPathSuffix, 1301 /* path */ "", type, name, isAcceptable, context, &subErrorCode, pErrorCode); 1302 if((retVal != nullptr) || U_FAILURE(*pErrorCode)) { 1303 return retVal; 1304 } 1305 } 1306 } 1307 1308 /**** COMMON PACKAGE - only if packages are first. */ 1309 if(gDataFileAccess == UDATA_PACKAGES_FIRST) { 1310 #ifdef UDATA_DEBUG 1311 fprintf(stderr, "Trying packages (UDATA_PACKAGES_FIRST)\n"); 1312 #endif 1313 /* #2 */ 1314 retVal = doLoadFromCommonData(isICUData, 1315 pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(), 1316 path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); 1317 if((retVal != nullptr) || U_FAILURE(*pErrorCode)) { 1318 return retVal; 1319 } 1320 } 1321 1322 /**** INDIVIDUAL FILES */ 1323 if((gDataFileAccess==UDATA_PACKAGES_FIRST) || 1324 (gDataFileAccess==UDATA_FILES_FIRST)) { 1325 #ifdef UDATA_DEBUG 1326 fprintf(stderr, "Trying individual files\n"); 1327 #endif 1328 /* Check to make sure that there is a dataPath to iterate over */ 1329 if ((dataPath && *dataPath) || !isICUData) { 1330 retVal = doLoadFromIndividualFiles(pkgName.data(), dataPath, tocEntryPathSuffix, 1331 path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); 1332 if((retVal != nullptr) || U_FAILURE(*pErrorCode)) { 1333 return retVal; 1334 } 1335 } 1336 } 1337 1338 /**** COMMON PACKAGE */ 1339 if((gDataFileAccess==UDATA_ONLY_PACKAGES) || 1340 (gDataFileAccess==UDATA_FILES_FIRST)) { 1341 #ifdef UDATA_DEBUG 1342 fprintf(stderr, "Trying packages (UDATA_ONLY_PACKAGES || UDATA_FILES_FIRST)\n"); 1343 #endif 1344 retVal = doLoadFromCommonData(isICUData, 1345 pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(), 1346 path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); 1347 if((retVal != nullptr) || U_FAILURE(*pErrorCode)) { 1348 return retVal; 1349 } 1350 } 1351 1352 /* Load from DLL. If we haven't attempted package load, we also haven't had any chance to 1353 try a DLL (static or setCommonData/etc) load. 1354 If we ever have a "UDATA_ONLY_FILES", add it to the or list here. */ 1355 if(gDataFileAccess==UDATA_NO_FILES) { 1356 #ifdef UDATA_DEBUG 1357 fprintf(stderr, "Trying common data (UDATA_NO_FILES)\n"); 1358 #endif 1359 retVal = doLoadFromCommonData(isICUData, 1360 pkgName.data(), "", tocEntryPathSuffix, tocEntryName.data(), 1361 path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); 1362 if((retVal != nullptr) || U_FAILURE(*pErrorCode)) { 1363 return retVal; 1364 } 1365 } 1366 1367 /* data not found */ 1368 if(U_SUCCESS(*pErrorCode)) { 1369 if(U_SUCCESS(subErrorCode)) { 1370 /* file not found */ 1371 *pErrorCode=U_FILE_ACCESS_ERROR; 1372 } else { 1373 /* entry point not found or rejected */ 1374 *pErrorCode=subErrorCode; 1375 } 1376 } 1377 return retVal; 1378 } 1379 1380 1381 1382 /* API ---------------------------------------------------------------------- */ 1383 1384 U_CAPI UDataMemory * U_EXPORT2 1385 udata_open(const char *path, const char *type, const char *name, 1386 UErrorCode *pErrorCode) { 1387 #ifdef UDATA_DEBUG 1388 fprintf(stderr, "udata_open(): Opening: %s : %s . %s\n", (path?path:"nullptr"), name, type); 1389 fflush(stderr); 1390 #endif 1391 1392 if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { 1393 return nullptr; 1394 } else if(name==nullptr || *name==0) { 1395 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; 1396 return nullptr; 1397 } else { 1398 return doOpenChoice(path, type, name, nullptr, nullptr, pErrorCode); 1399 } 1400 } 1401 1402 1403 1404 U_CAPI UDataMemory * U_EXPORT2 1405 udata_openChoice(const char *path, const char *type, const char *name, 1406 UDataMemoryIsAcceptable *isAcceptable, void *context, 1407 UErrorCode *pErrorCode) { 1408 #ifdef UDATA_DEBUG 1409 fprintf(stderr, "udata_openChoice(): Opening: %s : %s . %s\n", (path?path:"nullptr"), name, type); 1410 #endif 1411 1412 if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) { 1413 return nullptr; 1414 } else if(name==nullptr || *name==0 || isAcceptable==nullptr) { 1415 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; 1416 return nullptr; 1417 } else { 1418 return doOpenChoice(path, type, name, isAcceptable, context, pErrorCode); 1419 } 1420 } 1421 1422 1423 1424 U_CAPI void U_EXPORT2 1425 udata_getInfo(UDataMemory *pData, UDataInfo *pInfo) { 1426 if(pInfo!=nullptr) { 1427 if(pData!=nullptr && pData->pHeader!=nullptr) { 1428 const UDataInfo *info=&pData->pHeader->info; 1429 uint16_t dataInfoSize=udata_getInfoSize(info); 1430 if(pInfo->size>dataInfoSize) { 1431 pInfo->size=dataInfoSize; 1432 } 1433 uprv_memcpy((uint16_t *)pInfo+1, (const uint16_t *)info+1, pInfo->size-2); 1434 if(info->isBigEndian!=U_IS_BIG_ENDIAN) { 1435 /* opposite endianness */ 1436 uint16_t x=info->reservedWord; 1437 pInfo->reservedWord=(uint16_t)((x<<8)|(x>>8)); 1438 } 1439 } else { 1440 pInfo->size=0; 1441 } 1442 } 1443 } 1444 1445 1446 U_CAPI void U_EXPORT2 udata_setFileAccess(UDataFileAccess access, UErrorCode * /*status*/) 1447 { 1448 // Note: this function is documented as not thread safe. 1449 gDataFileAccess = access; 1450 }