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