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