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