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 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 *----------------------------------------------------------------------*/ 771static 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 *----------------------------------------------------------------------*/ 838U_CAPI void U_EXPORT2 839udata_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 *---------------------------------------------------------------------------- */ 867U_CAPI void U_EXPORT2 868udata_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 *----------------------------------------------------------------------------*/ 897static UDataMemory * 898checkDataItem 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 */ 938static 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 */ 996static 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 *----------------------------------------------------------------------------*/ 1092static UDataMemory * 1093doOpenChoice(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 1304U_CAPI UDataMemory * U_EXPORT2 1305udata_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 1324U_CAPI UDataMemory * U_EXPORT2 1325udata_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 1344U_CAPI void U_EXPORT2 1345udata_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 1366U_CAPI void U_EXPORT2 udata_setFileAccess(UDataFileAccess access, UErrorCode * /*status*/) 1367{ 1368 gDataFileAccess = access; 1369} 1370