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