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