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