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