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)) 22might 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 76U_NAMESPACE_USE 77 78/* 79 * Forward declarations 80 */ 81static 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 */ 105static UDataMemory *gCommonICUDataArray[10] = { NULL }; 106 107static UBool gHaveTriedToLoadCommonData = FALSE; /* See extendICUData(). */ 108 109static UHashtable *gCommonDataCache = NULL; /* Global hash table of opened ICU data files. */ 110static icu::UInitOnce gCommonDataCacheInitOnce = U_INITONCE_INITIALIZER; 111 112static UDataFileAccess gDataFileAccess = UDATA_DEFAULT_ACCESS; 113 114static UBool U_CALLCONV 115udata_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 134static UBool U_CALLCONV 135findCommonICUDataByName(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 */ 159static UBool 160setCommonICUData(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 200static UBool 201setCommonICUDataPointer(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 209static const char * 210findBasename(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 220static const char * 221packageNameFromPath(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 249typedef 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 */ 261static 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 268static 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 */ 286static UHashtable *udata_getHashTable() { 287 umtx_initOnce(gCommonDataCacheInitOnce, &udata_initHashTable); 288 return gCommonDataCache; 289} 290 291 292 293static 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 315static 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 396U_NAMESPACE_BEGIN 397 398class UDataPathIterator 399{ 400public: 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 406private: 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 */ 432UDataPathIterator::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 */ 500const 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 612U_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 *----------------------------------------------------------------------*/ 624extern "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/* 632extern const void *uprv_getICUData_collation(void) ATTRIBUTE_WEAK; 633extern 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 *----------------------------------------------------------------------*/ 646static UDataMemory * 647openCommonData(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 /* 682 if (uprv_getICUData_collation) { 683 setCommonICUDataPointer(uprv_getICUData_collation(), FALSE, pErrorCode); 684 } 685 if (uprv_getICUData_conversion) { 686 setCommonICUDataPointer(uprv_getICUData_conversion(), FALSE, pErrorCode); 687 } 688 */ 689 setCommonICUDataPointer(&U_ICUDATA_ENTRY_POINT, FALSE, pErrorCode); 690 } 691 return gCommonICUDataArray[commonDataIndex]; 692 } 693 694 695 /* request is NOT for ICU Data. */ 696 697 /* Find the base name portion of the supplied path. */ 698 /* inBasename will be left pointing somewhere within the original path string. */ 699 inBasename = findBasename(path); 700#ifdef UDATA_DEBUG 701 fprintf(stderr, "inBasename = %s\n", inBasename); 702#endif 703 704 if(*inBasename==0) { 705 /* no basename. This will happen if the original path was a directory name, */ 706 /* like "a/b/c/". (Fallback to separate files will still work.) */ 707#ifdef UDATA_DEBUG 708 fprintf(stderr, "ocd: no basename in %s, bailing.\n", path); 709#endif 710 *pErrorCode=U_FILE_ACCESS_ERROR; 711 return NULL; 712 } 713 714 /* Is the requested common data file already open and cached? */ 715 /* Note that the cache is keyed by the base name only. The rest of the path, */ 716 /* if any, is not considered. */ 717 { 718 UDataMemory *dataToReturn = udata_findCachedData(inBasename); 719 if (dataToReturn != NULL) { 720 return dataToReturn; 721 } 722 } 723 724 /* Requested item is not in the cache. 725 * Hunt it down, trying all the path locations 726 */ 727 728 UDataPathIterator iter(u_getDataDirectory(), inBasename, path, ".dat", TRUE, pErrorCode); 729 730 while((UDataMemory_isLoaded(&tData)==FALSE) && (pathBuffer = iter.next(pErrorCode)) != NULL) 731 { 732#ifdef UDATA_DEBUG 733 fprintf(stderr, "ocd: trying path %s - ", pathBuffer); 734#endif 735 uprv_mapFile(&tData, pathBuffer); 736#ifdef UDATA_DEBUG 737 fprintf(stderr, "%s\n", UDataMemory_isLoaded(&tData)?"LOADED":"not loaded"); 738#endif 739 } 740 741#if defined(OS390_STUBDATA) && defined(OS390BATCH) 742 if (!UDataMemory_isLoaded(&tData)) { 743 char ourPathBuffer[1024]; 744 /* One more chance, for extendCommonData() */ 745 uprv_strncpy(ourPathBuffer, path, 1019); 746 ourPathBuffer[1019]=0; 747 uprv_strcat(ourPathBuffer, ".dat"); 748 uprv_mapFile(&tData, ourPathBuffer); 749 } 750#endif 751 752 if (!UDataMemory_isLoaded(&tData)) { 753 /* no common data */ 754 *pErrorCode=U_FILE_ACCESS_ERROR; 755 return NULL; 756 } 757 758 /* we have mapped a file, check its header */ 759 udata_checkCommonData(&tData, pErrorCode); 760 761 762 /* Cache the UDataMemory struct for this .dat file, 763 * so we won't need to hunt it down and map it again next time 764 * something is needed from it. */ 765 return udata_cacheDataItem(inBasename, &tData, pErrorCode); 766} 767 768 769/*----------------------------------------------------------------------* 770 * * 771 * extendICUData If the full set of ICU data was not loaded at * 772 * program startup, load it now. This function will * 773 * be called when the lookup of an ICU data item in * 774 * the common ICU data fails. * 775 * * 776 * return true if new data is loaded, false otherwise.* 777 * * 778 *----------------------------------------------------------------------*/ 779static UBool extendICUData(UErrorCode *pErr) 780{ 781 UDataMemory *pData; 782 UDataMemory copyPData; 783 UBool didUpdate = FALSE; 784 785 /* 786 * There is a chance for a race condition here. 787 * Normally, ICU data is loaded from a DLL or via mmap() and 788 * setCommonICUData() will detect if the same address is set twice. 789 * If ICU is built with data loading via fread() then the address will 790 * be different each time the common data is loaded and we may add 791 * multiple copies of the data. 792 * In this case, use a mutex to prevent the race. 793 * Use a specific mutex to avoid nested locks of the global mutex. 794 */ 795#if MAP_IMPLEMENTATION==MAP_STDIO 796 static UMutex extendICUDataMutex = U_MUTEX_INITIALIZER; 797 umtx_lock(&extendICUDataMutex); 798#endif 799 if(!gHaveTriedToLoadCommonData) { 800 /* See if we can explicitly open a .dat file for the ICUData. */ 801 pData = openCommonData( 802 U_ICUDATA_NAME, /* "icudt20l" , for example. */ 803 -1, /* Pretend we're not opening ICUData */ 804 pErr); 805 806 /* How about if there is no pData, eh... */ 807 808 UDataMemory_init(©PData); 809 if(pData != NULL) { 810 UDatamemory_assign(©PData, pData); 811 copyPData.map = 0; /* The mapping for this data is owned by the hash table */ 812 copyPData.mapAddr = 0; /* which will unmap it when ICU is shut down. */ 813 /* CommonICUData is also unmapped when ICU is shut down.*/ 814 /* To avoid unmapping the data twice, zero out the map */ 815 /* fields in the UDataMemory that we're assigning */ 816 /* to CommonICUData. */ 817 818 didUpdate = /* no longer using this result */ 819 setCommonICUData(©PData,/* The new common data. */ 820 FALSE, /* No warnings if write didn't happen */ 821 pErr); /* setCommonICUData honors errors; NOP if error set */ 822 } 823 824 gHaveTriedToLoadCommonData = TRUE; 825 } 826 827 didUpdate = findCommonICUDataByName(U_ICUDATA_NAME); /* Return 'true' when a racing writes out the extended */ 828 /* data after another thread has failed to see it (in openCommonData), so */ 829 /* extended data can be examined. */ 830 /* Also handles a race through here before gHaveTriedToLoadCommonData is set. */ 831 832#if MAP_IMPLEMENTATION==MAP_STDIO 833 umtx_unlock(&extendICUDataMutex); 834#endif 835 return didUpdate; /* Return true if ICUData pointer was updated. */ 836 /* (Could potentialy have been done by another thread racing */ 837 /* us through here, but that's fine, we still return true */ 838 /* so that current thread will also examine extended data. */ 839} 840 841/*----------------------------------------------------------------------* 842 * * 843 * udata_setCommonData * 844 * * 845 *----------------------------------------------------------------------*/ 846U_CAPI void U_EXPORT2 847udata_setCommonData(const void *data, UErrorCode *pErrorCode) { 848 UDataMemory dataMemory; 849 850 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { 851 return; 852 } 853 854 if(data==NULL) { 855 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; 856 return; 857 } 858 859 /* set the data pointer and test for validity */ 860 UDataMemory_init(&dataMemory); 861 UDataMemory_setData(&dataMemory, data); 862 udata_checkCommonData(&dataMemory, pErrorCode); 863 if (U_FAILURE(*pErrorCode)) {return;} 864 865 /* we have good data */ 866 /* Set it up as the ICU Common Data. */ 867 setCommonICUData(&dataMemory, TRUE, pErrorCode); 868} 869 870/*--------------------------------------------------------------------------- 871 * 872 * udata_setAppData 873 * 874 *---------------------------------------------------------------------------- */ 875U_CAPI void U_EXPORT2 876udata_setAppData(const char *path, const void *data, UErrorCode *err) 877{ 878 UDataMemory udm; 879 880 if(err==NULL || U_FAILURE(*err)) { 881 return; 882 } 883 if(data==NULL) { 884 *err=U_ILLEGAL_ARGUMENT_ERROR; 885 return; 886 } 887 888 UDataMemory_init(&udm); 889 UDataMemory_setData(&udm, data); 890 udata_checkCommonData(&udm, err); 891 udata_cacheDataItem(path, &udm, err); 892} 893 894/*----------------------------------------------------------------------------* 895 * * 896 * checkDataItem Given a freshly located/loaded data item, either * 897 * an entry in a common file or a separately loaded file, * 898 * sanity check its header, and see if the data is * 899 * acceptable to the app. * 900 * If the data is good, create and return a UDataMemory * 901 * object that can be returned to the application. * 902 * Return NULL on any sort of failure. * 903 * * 904 *----------------------------------------------------------------------------*/ 905static UDataMemory * 906checkDataItem 907( 908 const DataHeader *pHeader, /* The data item to be checked. */ 909 UDataMemoryIsAcceptable *isAcceptable, /* App's call-back function */ 910 void *context, /* pass-thru param for above. */ 911 const char *type, /* pass-thru param for above. */ 912 const char *name, /* pass-thru param for above. */ 913 UErrorCode *nonFatalErr, /* Error code if this data was not acceptable */ 914 /* but openChoice should continue with */ 915 /* trying to get data from fallback path. */ 916 UErrorCode *fatalErr /* Bad error, caller should return immediately */ 917 ) 918{ 919 UDataMemory *rDataMem = NULL; /* the new UDataMemory, to be returned. */ 920 921 if (U_FAILURE(*fatalErr)) { 922 return NULL; 923 } 924 925 if(pHeader->dataHeader.magic1==0xda && 926 pHeader->dataHeader.magic2==0x27 && 927 (isAcceptable==NULL || isAcceptable(context, type, name, &pHeader->info)) 928 ) { 929 rDataMem=UDataMemory_createNewInstance(fatalErr); 930 if (U_FAILURE(*fatalErr)) { 931 return NULL; 932 } 933 rDataMem->pHeader = pHeader; 934 } else { 935 /* the data is not acceptable, look further */ 936 /* If we eventually find something good, this errorcode will be */ 937 /* cleared out. */ 938 *nonFatalErr=U_INVALID_FORMAT_ERROR; 939 } 940 return rDataMem; 941} 942 943/** 944 * @return 0 if not loaded, 1 if loaded or err 945 */ 946static UDataMemory *doLoadFromIndividualFiles(const char *pkgName, 947 const char *dataPath, const char *tocEntryPathSuffix, 948 /* following arguments are the same as doOpenChoice itself */ 949 const char *path, const char *type, const char *name, 950 UDataMemoryIsAcceptable *isAcceptable, void *context, 951 UErrorCode *subErrorCode, 952 UErrorCode *pErrorCode) 953{ 954 const char *pathBuffer; 955 UDataMemory dataMemory; 956 UDataMemory *pEntryData; 957 958 /* look in ind. files: package\nam.typ ========================= */ 959 /* init path iterator for individual files */ 960 UDataPathIterator iter(dataPath, pkgName, path, tocEntryPathSuffix, FALSE, pErrorCode); 961 962 while((pathBuffer = iter.next(pErrorCode))) 963 { 964#ifdef UDATA_DEBUG 965 fprintf(stderr, "UDATA: trying individual file %s\n", pathBuffer); 966#endif 967 if(uprv_mapFile(&dataMemory, pathBuffer)) 968 { 969 pEntryData = checkDataItem(dataMemory.pHeader, isAcceptable, context, type, name, subErrorCode, pErrorCode); 970 if (pEntryData != NULL) { 971 /* Data is good. 972 * Hand off ownership of the backing memory to the user's UDataMemory. 973 * and return it. */ 974 pEntryData->mapAddr = dataMemory.mapAddr; 975 pEntryData->map = dataMemory.map; 976 977#ifdef UDATA_DEBUG 978 fprintf(stderr, "** Mapped file: %s\n", pathBuffer); 979#endif 980 return pEntryData; 981 } 982 983 /* the data is not acceptable, or some error occured. Either way, unmap the memory */ 984 udata_close(&dataMemory); 985 986 /* If we had a nasty error, bail out completely. */ 987 if (U_FAILURE(*pErrorCode)) { 988 return NULL; 989 } 990 991 /* Otherwise remember that we found data but didn't like it for some reason */ 992 *subErrorCode=U_INVALID_FORMAT_ERROR; 993 } 994#ifdef UDATA_DEBUG 995 fprintf(stderr, "%s\n", UDataMemory_isLoaded(&dataMemory)?"LOADED":"not loaded"); 996#endif 997 } 998 return NULL; 999} 1000 1001/** 1002 * @return 0 if not loaded, 1 if loaded or err 1003 */ 1004static UDataMemory *doLoadFromCommonData(UBool isICUData, const char * /*pkgName*/, 1005 const char * /*dataPath*/, const char * /*tocEntryPathSuffix*/, const char *tocEntryName, 1006 /* following arguments are the same as doOpenChoice itself */ 1007 const char *path, const char *type, const char *name, 1008 UDataMemoryIsAcceptable *isAcceptable, void *context, 1009 UErrorCode *subErrorCode, 1010 UErrorCode *pErrorCode) 1011{ 1012 UDataMemory *pEntryData; 1013 const DataHeader *pHeader; 1014 UDataMemory *pCommonData; 1015 int32_t commonDataIndex; 1016 UBool checkedExtendedICUData = FALSE; 1017 /* try to get common data. The loop is for platforms such as the 390 that do 1018 * not initially load the full set of ICU data. If the lookup of an ICU data item 1019 * fails, the full (but slower to load) set is loaded, the and the loop repeats, 1020 * trying the lookup again. Once the full set of ICU data is loaded, the loop wont 1021 * repeat because the full set will be checked the first time through. 1022 * 1023 * The loop also handles the fallback to a .dat file if the application linked 1024 * to the stub data library rather than a real library. 1025 */ 1026 for (commonDataIndex = isICUData ? 0 : -1;;) { 1027 pCommonData=openCommonData(path, commonDataIndex, subErrorCode); /** search for pkg **/ 1028 1029 if(U_SUCCESS(*subErrorCode) && pCommonData!=NULL) { 1030 int32_t length; 1031 1032 /* look up the data piece in the common data */ 1033 pHeader=pCommonData->vFuncs->Lookup(pCommonData, tocEntryName, &length, subErrorCode); 1034#ifdef UDATA_DEBUG 1035 fprintf(stderr, "%s: pHeader=%p - %s\n", tocEntryName, pHeader, u_errorName(*subErrorCode)); 1036#endif 1037 1038 if(pHeader!=NULL) { 1039 pEntryData = checkDataItem(pHeader, isAcceptable, context, type, name, subErrorCode, pErrorCode); 1040#ifdef UDATA_DEBUG 1041 fprintf(stderr, "pEntryData=%p\n", pEntryData); 1042#endif 1043 if (U_FAILURE(*pErrorCode)) { 1044 return NULL; 1045 } 1046 if (pEntryData != NULL) { 1047 pEntryData->length = length; 1048 return pEntryData; 1049 } 1050 } 1051 } 1052 /* Data wasn't found. If we were looking for an ICUData item and there is 1053 * more data available, load it and try again, 1054 * otherwise break out of this loop. */ 1055 if (!isICUData) { 1056 return NULL; 1057 } else if (pCommonData != NULL) { 1058 ++commonDataIndex; /* try the next data package */ 1059 } else if ((!checkedExtendedICUData) && extendICUData(subErrorCode)) { 1060 checkedExtendedICUData = TRUE; 1061 /* try this data package slot again: it changed from NULL to non-NULL */ 1062 } else { 1063 return NULL; 1064 } 1065 } 1066} 1067 1068/* 1069 * A note on the ownership of Mapped Memory 1070 * 1071 * For common format files, ownership resides with the UDataMemory object 1072 * that lives in the cache of opened common data. These UDataMemorys are private 1073 * to the udata implementation, and are never seen directly by users. 1074 * 1075 * The UDataMemory objects returned to users will have the address of some desired 1076 * data within the mapped region, but they wont have the mapping info itself, and thus 1077 * won't cause anything to be removed from memory when they are closed. 1078 * 1079 * For individual data files, the UDataMemory returned to the user holds the 1080 * information necessary to unmap the data on close. If the user independently 1081 * opens the same data file twice, two completely independent mappings will be made. 1082 * (There is no cache of opened data items from individual files, only a cache of 1083 * opened Common Data files, that is, files containing a collection of data items.) 1084 * 1085 * For common data passed in from the user via udata_setAppData() or 1086 * udata_setCommonData(), ownership remains with the user. 1087 * 1088 * UDataMemory objects themselves, as opposed to the memory they describe, 1089 * can be anywhere - heap, stack/local or global. 1090 * They have a flag to indicate when they're heap allocated and thus 1091 * must be deleted when closed. 1092 */ 1093 1094 1095/*----------------------------------------------------------------------------* 1096 * * 1097 * main data loading functions * 1098 * * 1099 *----------------------------------------------------------------------------*/ 1100static UDataMemory * 1101doOpenChoice(const char *path, const char *type, const char *name, 1102 UDataMemoryIsAcceptable *isAcceptable, void *context, 1103 UErrorCode *pErrorCode) 1104{ 1105 UDataMemory *retVal = NULL; 1106 1107 const char *dataPath; 1108 1109 int32_t tocEntrySuffixIndex; 1110 const char *tocEntryPathSuffix; 1111 UErrorCode subErrorCode=U_ZERO_ERROR; 1112 const char *treeChar; 1113 1114 UBool isICUData = FALSE; 1115 1116 1117 /* Is this path ICU data? */ 1118 if(path == NULL || 1119 !strcmp(path, U_ICUDATA_ALIAS) || /* "ICUDATA" */ 1120 !uprv_strncmp(path, U_ICUDATA_NAME U_TREE_SEPARATOR_STRING, /* "icudt26e-" */ 1121 uprv_strlen(U_ICUDATA_NAME U_TREE_SEPARATOR_STRING)) || 1122 !uprv_strncmp(path, U_ICUDATA_ALIAS U_TREE_SEPARATOR_STRING, /* "ICUDATA-" */ 1123 uprv_strlen(U_ICUDATA_ALIAS U_TREE_SEPARATOR_STRING))) { 1124 isICUData = TRUE; 1125 } 1126 1127#if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR) /* Windows: try "foo\bar" and "foo/bar" */ 1128 /* remap from alternate path char to the main one */ 1129 CharString altSepPath; 1130 if(path) { 1131 if(uprv_strchr(path,U_FILE_ALT_SEP_CHAR) != NULL) { 1132 altSepPath.append(path, *pErrorCode); 1133 char *p; 1134 while((p=uprv_strchr(altSepPath.data(), U_FILE_ALT_SEP_CHAR))) { 1135 *p = U_FILE_SEP_CHAR; 1136 } 1137#if defined (UDATA_DEBUG) 1138 fprintf(stderr, "Changed path from [%s] to [%s]\n", path, altSepPath.s); 1139#endif 1140 path = altSepPath.data(); 1141 } 1142 } 1143#endif 1144 1145 CharString tocEntryName; /* entry name in tree format. ex: 'icudt28b/coll/ar.res' */ 1146 CharString tocEntryPath; /* entry name in path format. ex: 'icudt28b\\coll\\ar.res' */ 1147 1148 CharString pkgName; 1149 CharString treeName; 1150 1151 /* ======= Set up strings */ 1152 if(path==NULL) { 1153 pkgName.append(U_ICUDATA_NAME, *pErrorCode); 1154 } else { 1155 const char *pkg; 1156 const char *first; 1157 pkg = uprv_strrchr(path, U_FILE_SEP_CHAR); 1158 first = uprv_strchr(path, U_FILE_SEP_CHAR); 1159 if(uprv_pathIsAbsolute(path) || (pkg != first)) { /* more than one slash in the path- not a tree name */ 1160 /* see if this is an /absolute/path/to/package path */ 1161 if(pkg) { 1162 pkgName.append(pkg+1, *pErrorCode); 1163 } else { 1164 pkgName.append(path, *pErrorCode); 1165 } 1166 } else { 1167 treeChar = uprv_strchr(path, U_TREE_SEPARATOR); 1168 if(treeChar) { 1169 treeName.append(treeChar+1, *pErrorCode); /* following '-' */ 1170 if(isICUData) { 1171 pkgName.append(U_ICUDATA_NAME, *pErrorCode); 1172 } else { 1173 pkgName.append(path, (int32_t)(treeChar-path), *pErrorCode); 1174 if (first == NULL) { 1175 /* 1176 This user data has no path, but there is a tree name. 1177 Look up the correct path from the data cache later. 1178 */ 1179 path = pkgName.data(); 1180 } 1181 } 1182 } else { 1183 if(isICUData) { 1184 pkgName.append(U_ICUDATA_NAME, *pErrorCode); 1185 } else { 1186 pkgName.append(path, *pErrorCode); 1187 } 1188 } 1189 } 1190 } 1191 1192#ifdef UDATA_DEBUG 1193 fprintf(stderr, " P=%s T=%s\n", pkgName.data(), treeName.data()); 1194#endif 1195 1196 /* setting up the entry name and file name 1197 * Make up a full name by appending the type to the supplied 1198 * name, assuming that a type was supplied. 1199 */ 1200 1201 /* prepend the package */ 1202 tocEntryName.append(pkgName, *pErrorCode); 1203 tocEntryPath.append(pkgName, *pErrorCode); 1204 tocEntrySuffixIndex = tocEntryName.length(); 1205 1206 if(!treeName.isEmpty()) { 1207 tocEntryName.append(U_TREE_ENTRY_SEP_CHAR, *pErrorCode).append(treeName, *pErrorCode); 1208 tocEntryPath.append(U_FILE_SEP_CHAR, *pErrorCode).append(treeName, *pErrorCode); 1209 } 1210 1211 tocEntryName.append(U_TREE_ENTRY_SEP_CHAR, *pErrorCode).append(name, *pErrorCode); 1212 tocEntryPath.append(U_FILE_SEP_CHAR, *pErrorCode).append(name, *pErrorCode); 1213 if(type!=NULL && *type!=0) { 1214 tocEntryName.append(".", *pErrorCode).append(type, *pErrorCode); 1215 tocEntryPath.append(".", *pErrorCode).append(type, *pErrorCode); 1216 } 1217 tocEntryPathSuffix = tocEntryPath.data()+tocEntrySuffixIndex; /* suffix starts here */ 1218 1219#ifdef UDATA_DEBUG 1220 fprintf(stderr, " tocEntryName = %s\n", tocEntryName.data()); 1221 fprintf(stderr, " tocEntryPath = %s\n", tocEntryName.data()); 1222#endif 1223 1224 if(path == NULL) { 1225 path = COMMON_DATA_NAME; /* "icudt26e" */ 1226 } 1227 1228 /************************ Begin loop looking for ind. files ***************/ 1229#ifdef UDATA_DEBUG 1230 fprintf(stderr, "IND: inBasename = %s, pkg=%s\n", "(n/a)", packageNameFromPath(path)); 1231#endif 1232 1233 /* End of dealing with a null basename */ 1234 dataPath = u_getDataDirectory(); 1235 1236 /**** COMMON PACKAGE - only if packages are first. */ 1237 if(gDataFileAccess == UDATA_PACKAGES_FIRST) { 1238#ifdef UDATA_DEBUG 1239 fprintf(stderr, "Trying packages (UDATA_PACKAGES_FIRST)\n"); 1240#endif 1241 /* #2 */ 1242 retVal = doLoadFromCommonData(isICUData, 1243 pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(), 1244 path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); 1245 if((retVal != NULL) || U_FAILURE(*pErrorCode)) { 1246 return retVal; 1247 } 1248 } 1249 1250 /**** INDIVIDUAL FILES */ 1251 if((gDataFileAccess==UDATA_PACKAGES_FIRST) || 1252 (gDataFileAccess==UDATA_FILES_FIRST)) { 1253#ifdef UDATA_DEBUG 1254 fprintf(stderr, "Trying individual files\n"); 1255#endif 1256 /* Check to make sure that there is a dataPath to iterate over */ 1257 if ((dataPath && *dataPath) || !isICUData) { 1258 retVal = doLoadFromIndividualFiles(pkgName.data(), dataPath, tocEntryPathSuffix, 1259 path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); 1260 if((retVal != NULL) || U_FAILURE(*pErrorCode)) { 1261 return retVal; 1262 } 1263 } 1264 } 1265 1266 /**** COMMON PACKAGE */ 1267 if((gDataFileAccess==UDATA_ONLY_PACKAGES) || 1268 (gDataFileAccess==UDATA_FILES_FIRST)) { 1269#ifdef UDATA_DEBUG 1270 fprintf(stderr, "Trying packages (UDATA_ONLY_PACKAGES || UDATA_FILES_FIRST)\n"); 1271#endif 1272 retVal = doLoadFromCommonData(isICUData, 1273 pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(), 1274 path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); 1275 if((retVal != NULL) || U_FAILURE(*pErrorCode)) { 1276 return retVal; 1277 } 1278 } 1279 1280 /* Load from DLL. If we haven't attempted package load, we also haven't had any chance to 1281 try a DLL (static or setCommonData/etc) load. 1282 If we ever have a "UDATA_ONLY_FILES", add it to the or list here. */ 1283 if(gDataFileAccess==UDATA_NO_FILES) { 1284#ifdef UDATA_DEBUG 1285 fprintf(stderr, "Trying common data (UDATA_NO_FILES)\n"); 1286#endif 1287 retVal = doLoadFromCommonData(isICUData, 1288 pkgName.data(), "", tocEntryPathSuffix, tocEntryName.data(), 1289 path, type, name, isAcceptable, context, &subErrorCode, pErrorCode); 1290 if((retVal != NULL) || U_FAILURE(*pErrorCode)) { 1291 return retVal; 1292 } 1293 } 1294 1295 /* data not found */ 1296 if(U_SUCCESS(*pErrorCode)) { 1297 if(U_SUCCESS(subErrorCode)) { 1298 /* file not found */ 1299 *pErrorCode=U_FILE_ACCESS_ERROR; 1300 } else { 1301 /* entry point not found or rejected */ 1302 *pErrorCode=subErrorCode; 1303 } 1304 } 1305 return retVal; 1306} 1307 1308 1309 1310/* API ---------------------------------------------------------------------- */ 1311 1312U_CAPI UDataMemory * U_EXPORT2 1313udata_open(const char *path, const char *type, const char *name, 1314 UErrorCode *pErrorCode) { 1315#ifdef UDATA_DEBUG 1316 fprintf(stderr, "udata_open(): Opening: %s : %s . %s\n", (path?path:"NULL"), name, type); 1317 fflush(stderr); 1318#endif 1319 1320 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { 1321 return NULL; 1322 } else if(name==NULL || *name==0) { 1323 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; 1324 return NULL; 1325 } else { 1326 return doOpenChoice(path, type, name, NULL, NULL, pErrorCode); 1327 } 1328} 1329 1330 1331 1332U_CAPI UDataMemory * U_EXPORT2 1333udata_openChoice(const char *path, const char *type, const char *name, 1334 UDataMemoryIsAcceptable *isAcceptable, void *context, 1335 UErrorCode *pErrorCode) { 1336#ifdef UDATA_DEBUG 1337 fprintf(stderr, "udata_openChoice(): Opening: %s : %s . %s\n", (path?path:"NULL"), name, type); 1338#endif 1339 1340 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { 1341 return NULL; 1342 } else if(name==NULL || *name==0 || isAcceptable==NULL) { 1343 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; 1344 return NULL; 1345 } else { 1346 return doOpenChoice(path, type, name, isAcceptable, context, pErrorCode); 1347 } 1348} 1349 1350 1351 1352U_CAPI void U_EXPORT2 1353udata_getInfo(UDataMemory *pData, UDataInfo *pInfo) { 1354 if(pInfo!=NULL) { 1355 if(pData!=NULL && pData->pHeader!=NULL) { 1356 const UDataInfo *info=&pData->pHeader->info; 1357 uint16_t dataInfoSize=udata_getInfoSize(info); 1358 if(pInfo->size>dataInfoSize) { 1359 pInfo->size=dataInfoSize; 1360 } 1361 uprv_memcpy((uint16_t *)pInfo+1, (const uint16_t *)info+1, pInfo->size-2); 1362 if(info->isBigEndian!=U_IS_BIG_ENDIAN) { 1363 /* opposite endianness */ 1364 uint16_t x=info->reservedWord; 1365 pInfo->reservedWord=(uint16_t)((x<<8)|(x>>8)); 1366 } 1367 } else { 1368 pInfo->size=0; 1369 } 1370 } 1371} 1372 1373 1374U_CAPI void U_EXPORT2 udata_setFileAccess(UDataFileAccess access, UErrorCode * /*status*/) 1375{ 1376 gDataFileAccess = access; 1377} 1378