1 /*
2 ******************************************************************************
3 * Copyright (C) 1997-2015, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ******************************************************************************
6 *
7 * File URESBUND.C
8 *
9 * Modification History:
10 *
11 *   Date        Name        Description
12 *   04/01/97    aliu        Creation.
13 *   06/14/99    stephen     Removed functions taking a filename suffix.
14 *   07/20/99    stephen     Changed for UResourceBundle typedef'd to void*
15 *   11/09/99    weiv            Added ures_getLocale()
16 *   March 2000  weiv        Total overhaul - using data in DLLs
17 *   06/20/2000  helena      OS/400 port changes; mostly typecast.
18 *   06/24/02    weiv        Added support for resource sharing
19 ******************************************************************************
20 */
21 
22 #include "unicode/ustring.h"
23 #include "unicode/ucnv.h"
24 #include "charstr.h"
25 #include "uresimp.h"
26 #include "ustr_imp.h"
27 #include "cwchar.h"
28 #include "ucln_cmn.h"
29 #include "cmemory.h"
30 #include "cstring.h"
31 #include "uhash.h"
32 #include "unicode/uenum.h"
33 #include "uenumimp.h"
34 #include "ulocimp.h"
35 #include "umutex.h"
36 #include "putilimp.h"
37 #include "uassert.h"
38 
39 using namespace icu;
40 
41 /*
42 Static cache for already opened resource bundles - mostly for keeping fallback info
43 TODO: This cache should probably be removed when the deprecated code is
44       completely removed.
45 */
46 static UHashtable *cache = NULL;
47 static icu::UInitOnce gCacheInitOnce;
48 
49 static UMutex resbMutex = U_MUTEX_INITIALIZER;
50 
51 /* INTERNAL: hashes an entry  */
hashEntry(const UHashTok parm)52 static int32_t U_CALLCONV hashEntry(const UHashTok parm) {
53     UResourceDataEntry *b = (UResourceDataEntry *)parm.pointer;
54     UHashTok namekey, pathkey;
55     namekey.pointer = b->fName;
56     pathkey.pointer = b->fPath;
57     return uhash_hashChars(namekey)+37u*uhash_hashChars(pathkey);
58 }
59 
60 /* INTERNAL: compares two entries */
compareEntries(const UHashTok p1,const UHashTok p2)61 static UBool U_CALLCONV compareEntries(const UHashTok p1, const UHashTok p2) {
62     UResourceDataEntry *b1 = (UResourceDataEntry *)p1.pointer;
63     UResourceDataEntry *b2 = (UResourceDataEntry *)p2.pointer;
64     UHashTok name1, name2, path1, path2;
65     name1.pointer = b1->fName;
66     name2.pointer = b2->fName;
67     path1.pointer = b1->fPath;
68     path2.pointer = b2->fPath;
69     return (UBool)(uhash_compareChars(name1, name2) &&
70         uhash_compareChars(path1, path2));
71 }
72 
73 
74 /**
75  *  Internal function, gets parts of locale name according
76  *  to the position of '_' character
77  */
chopLocale(char * name)78 static UBool chopLocale(char *name) {
79     char *i = uprv_strrchr(name, '_');
80 
81     if(i != NULL) {
82         *i = '\0';
83         return TRUE;
84     }
85 
86     return FALSE;
87 }
88 
89 /**
90  *  Internal function
91  */
entryIncrease(UResourceDataEntry * entry)92 static void entryIncrease(UResourceDataEntry *entry) {
93     umtx_lock(&resbMutex);
94     entry->fCountExisting++;
95     while(entry->fParent != NULL) {
96       entry = entry->fParent;
97       entry->fCountExisting++;
98     }
99     umtx_unlock(&resbMutex);
100 }
101 
102 /**
103  *  Internal function. Tries to find a resource in given Resource
104  *  Bundle, as well as in its parents
105  */
getFallbackData(const UResourceBundle * resBundle,const char ** resTag,UResourceDataEntry ** realData,Resource * res,UErrorCode * status)106 static const ResourceData *getFallbackData(const UResourceBundle* resBundle, const char* * resTag, UResourceDataEntry* *realData, Resource *res, UErrorCode *status) {
107     UResourceDataEntry *resB = resBundle->fData;
108     int32_t indexR = -1;
109     int32_t i = 0;
110     *res = RES_BOGUS;
111     if(resB != NULL) {
112         if(resB->fBogus == U_ZERO_ERROR) { /* if this resource is real, */
113             *res = res_getTableItemByKey(&(resB->fData), resB->fData.rootRes, &indexR, resTag); /* try to get data from there */
114             i++;
115         }
116         if(resBundle->fHasFallback == TRUE) {
117             while(*res == RES_BOGUS && resB->fParent != NULL) { /* Otherwise, we'll look in parents */
118                 resB = resB->fParent;
119                 if(resB->fBogus == U_ZERO_ERROR) {
120                     i++;
121                     *res = res_getTableItemByKey(&(resB->fData), resB->fData.rootRes, &indexR, resTag);
122                 }
123             }
124         }
125 
126         if(*res != RES_BOGUS) { /* If the resource is found in parents, we need to adjust the error */
127             if(i>1) {
128                 if(uprv_strcmp(resB->fName, uloc_getDefault())==0 || uprv_strcmp(resB->fName, kRootLocaleName)==0) {
129                     *status = U_USING_DEFAULT_WARNING;
130                 } else {
131                     *status = U_USING_FALLBACK_WARNING;
132                 }
133             }
134             *realData = resB;
135             return (&(resB->fData));
136         } else { /* If resource is not found, we need to give an error */
137             *status = U_MISSING_RESOURCE_ERROR;
138             return NULL;
139         }
140     } else {
141             *status = U_MISSING_RESOURCE_ERROR;
142             return NULL;
143     }
144 }
145 
146 static void
free_entry(UResourceDataEntry * entry)147 free_entry(UResourceDataEntry *entry) {
148     UResourceDataEntry *alias;
149     res_unload(&(entry->fData));
150     if(entry->fName != NULL && entry->fName != entry->fNameBuffer) {
151         uprv_free(entry->fName);
152     }
153     if(entry->fPath != NULL) {
154         uprv_free(entry->fPath);
155     }
156     if(entry->fPool != NULL) {
157         --entry->fPool->fCountExisting;
158     }
159     alias = entry->fAlias;
160     if(alias != NULL) {
161         while(alias->fAlias != NULL) {
162             alias = alias->fAlias;
163         }
164         --alias->fCountExisting;
165     }
166     uprv_free(entry);
167 }
168 
169 /* Works just like ucnv_flushCache() */
ures_flushCache()170 static int32_t ures_flushCache()
171 {
172     UResourceDataEntry *resB;
173     int32_t pos;
174     int32_t rbDeletedNum = 0;
175     const UHashElement *e;
176     UBool deletedMore;
177 
178     /*if shared data hasn't even been lazy evaluated yet
179     * return 0
180     */
181     umtx_lock(&resbMutex);
182     if (cache == NULL) {
183         umtx_unlock(&resbMutex);
184         return 0;
185     }
186 
187     do {
188         deletedMore = FALSE;
189         /*creates an enumeration to iterate through every element in the table */
190         pos = UHASH_FIRST;
191         while ((e = uhash_nextElement(cache, &pos)) != NULL)
192         {
193             resB = (UResourceDataEntry *) e->value.pointer;
194             /* Deletes only if reference counter == 0
195              * Don't worry about the children of this node.
196              * Those will eventually get deleted too, if not already.
197              * Don't worry about the parents of this node.
198              * Those will eventually get deleted too, if not already.
199              */
200             /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that    */
201             /* some resource bundles are still open somewhere. */
202 
203             if (resB->fCountExisting == 0) {
204                 rbDeletedNum++;
205                 deletedMore = TRUE;
206                 uhash_removeElement(cache, e);
207                 free_entry(resB);
208             }
209         }
210         /*
211          * Do it again to catch bundles (aliases, pool bundle) whose fCountExisting
212          * got decremented by free_entry().
213          */
214     } while(deletedMore);
215     umtx_unlock(&resbMutex);
216 
217     return rbDeletedNum;
218 }
219 
220 #ifdef URES_DEBUG
221 #include <stdio.h>
222 
ures_dumpCacheContents(void)223 U_CAPI UBool U_EXPORT2 ures_dumpCacheContents(void) {
224   UBool cacheNotEmpty = FALSE;
225   int32_t pos = UHASH_FIRST;
226   const UHashElement *e;
227   UResourceDataEntry *resB;
228 
229     umtx_lock(&resbMutex);
230     if (cache == NULL) {
231       umtx_unlock(&resbMutex);
232       fprintf(stderr,"%s:%d: RB Cache is NULL.\n", __FILE__, __LINE__);
233       return FALSE;
234     }
235 
236     while ((e = uhash_nextElement(cache, &pos)) != NULL) {
237       cacheNotEmpty=TRUE;
238       resB = (UResourceDataEntry *) e->value.pointer;
239       fprintf(stderr,"%s:%d: RB Cache: Entry @0x%p, refcount %d, name %s:%s.  Pool 0x%p, alias 0x%p, parent 0x%p\n",
240               __FILE__, __LINE__,
241               (void*)resB, resB->fCountExisting,
242               resB->fName?resB->fName:"NULL",
243               resB->fPath?resB->fPath:"NULL",
244               (void*)resB->fPool,
245               (void*)resB->fAlias,
246               (void*)resB->fParent);
247     }
248 
249     fprintf(stderr,"%s:%d: RB Cache still contains %d items.\n", __FILE__, __LINE__, uhash_count(cache));
250 
251     umtx_unlock(&resbMutex);
252 
253     return cacheNotEmpty;
254 }
255 
256 #endif
257 
ures_cleanup(void)258 static UBool U_CALLCONV ures_cleanup(void)
259 {
260     if (cache != NULL) {
261         ures_flushCache();
262         uhash_close(cache);
263         cache = NULL;
264     }
265     gCacheInitOnce.reset();
266     return TRUE;
267 }
268 
269 /** INTERNAL: Initializes the cache for resources */
createCache(UErrorCode & status)270 static void createCache(UErrorCode &status) {
271     U_ASSERT(cache == NULL);
272     cache = uhash_open(hashEntry, compareEntries, NULL, &status);
273     ucln_common_registerCleanup(UCLN_COMMON_URES, ures_cleanup);
274 }
275 
initCache(UErrorCode * status)276 static void initCache(UErrorCode *status) {
277     umtx_initOnce(gCacheInitOnce, &createCache, *status);
278 }
279 
280 /** INTERNAL: sets the name (locale) of the resource bundle to given name */
281 
setEntryName(UResourceDataEntry * res,const char * name,UErrorCode * status)282 static void setEntryName(UResourceDataEntry *res, const char *name, UErrorCode *status) {
283     int32_t len = (int32_t)uprv_strlen(name);
284     if(res->fName != NULL && res->fName != res->fNameBuffer) {
285         uprv_free(res->fName);
286     }
287     if (len < (int32_t)sizeof(res->fNameBuffer)) {
288         res->fName = res->fNameBuffer;
289     }
290     else {
291         res->fName = (char *)uprv_malloc(len+1);
292     }
293     if(res->fName == NULL) {
294         *status = U_MEMORY_ALLOCATION_ERROR;
295     } else {
296         uprv_strcpy(res->fName, name);
297     }
298 }
299 
300 static UResourceDataEntry *
301 getPoolEntry(const char *path, UErrorCode *status);
302 
303 /**
304  *  INTERNAL: Inits and opens an entry from a data DLL.
305  *    CAUTION:  resbMutex must be locked when calling this function.
306  */
init_entry(const char * localeID,const char * path,UErrorCode * status)307 static UResourceDataEntry *init_entry(const char *localeID, const char *path, UErrorCode *status) {
308     UResourceDataEntry *r = NULL;
309     UResourceDataEntry find;
310     /*int32_t hashValue;*/
311     const char *name;
312     char aliasName[100] = { 0 };
313     int32_t aliasLen = 0;
314     /*UBool isAlias = FALSE;*/
315     /*UHashTok hashkey; */
316 
317     if(U_FAILURE(*status)) {
318         return NULL;
319     }
320 
321     /* here we try to deduce the right locale name */
322     if(localeID == NULL) { /* if localeID is NULL, we're trying to open default locale */
323         name = uloc_getDefault();
324     } else if(*localeID == 0) { /* if localeID is "" then we try to open root locale */
325         name = kRootLocaleName;
326     } else { /* otherwise, we'll open what we're given */
327         name = localeID;
328     }
329 
330     find.fName = (char *)name;
331     find.fPath = (char *)path;
332 
333     /* calculate the hash value of the entry */
334     /*hashkey.pointer = (void *)&find;*/
335     /*hashValue = hashEntry(hashkey);*/
336 
337     /* check to see if we already have this entry */
338     r = (UResourceDataEntry *)uhash_get(cache, &find);
339     if(r == NULL) {
340         /* if the entry is not yet in the hash table, we'll try to construct a new one */
341         r = (UResourceDataEntry *) uprv_malloc(sizeof(UResourceDataEntry));
342         if(r == NULL) {
343             *status = U_MEMORY_ALLOCATION_ERROR;
344             return NULL;
345         }
346 
347         uprv_memset(r, 0, sizeof(UResourceDataEntry));
348         /*r->fHashKey = hashValue;*/
349 
350         setEntryName(r, name, status);
351         if (U_FAILURE(*status)) {
352             uprv_free(r);
353             return NULL;
354         }
355 
356         if(path != NULL) {
357             r->fPath = (char *)uprv_strdup(path);
358             if(r->fPath == NULL) {
359                 *status = U_MEMORY_ALLOCATION_ERROR;
360                 uprv_free(r);
361                 return NULL;
362             }
363         }
364 
365         /* this is the actual loading */
366         res_load(&(r->fData), r->fPath, r->fName, status);
367 
368         if (U_FAILURE(*status)) {
369             /* we have no such entry in dll, so it will always use fallback */
370             *status = U_USING_FALLBACK_WARNING;
371             r->fBogus = U_USING_FALLBACK_WARNING;
372         } else { /* if we have a regular entry */
373             Resource aliasres;
374             if (r->fData.usesPoolBundle) {
375                 r->fPool = getPoolEntry(r->fPath, status);
376                 if (U_SUCCESS(*status)) {
377                     const int32_t *poolIndexes = r->fPool->fData.pRoot + 1;
378                     if(r->fData.pRoot[1 + URES_INDEX_POOL_CHECKSUM] == poolIndexes[URES_INDEX_POOL_CHECKSUM]) {
379                         r->fData.poolBundleKeys = (const char *)(poolIndexes + (poolIndexes[URES_INDEX_LENGTH] & 0xff));
380                     } else {
381                         r->fBogus = *status = U_INVALID_FORMAT_ERROR;
382                     }
383                 } else {
384                     r->fBogus = *status;
385                 }
386             }
387             if (U_SUCCESS(*status)) {
388                 /* handle the alias by trying to get out the %%Alias tag.*/
389                 /* We'll try to get alias string from the bundle */
390                 aliasres = res_getResource(&(r->fData), "%%ALIAS");
391                 if (aliasres != RES_BOGUS) {
392                     const UChar *alias = res_getString(&(r->fData), aliasres, &aliasLen);
393                     if(alias != NULL && aliasLen > 0) { /* if there is actual alias - unload and load new data */
394                         u_UCharsToChars(alias, aliasName, aliasLen+1);
395                         r->fAlias = init_entry(aliasName, path, status);
396                     }
397                 }
398             }
399         }
400 
401         {
402             UResourceDataEntry *oldR = NULL;
403             if((oldR = (UResourceDataEntry *)uhash_get(cache, r)) == NULL) { /* if the data is not cached */
404                 /* just insert it in the cache */
405                 UErrorCode cacheStatus = U_ZERO_ERROR;
406                 uhash_put(cache, (void *)r, r, &cacheStatus);
407                 if (U_FAILURE(cacheStatus)) {
408                     *status = cacheStatus;
409                     free_entry(r);
410                     r = NULL;
411                 }
412             } else {
413                 /* somebody have already inserted it while we were working, discard newly opened data */
414                 /* Also, we could get here IF we opened an alias */
415                 free_entry(r);
416                 r = oldR;
417             }
418         }
419 
420     }
421     if(r != NULL) {
422         /* return the real bundle */
423         while(r->fAlias != NULL) {
424             r = r->fAlias;
425         }
426         r->fCountExisting++; /* we increase its reference count */
427         /* if the resource has a warning */
428         /* we don't want to overwrite a status with no error */
429         if(r->fBogus != U_ZERO_ERROR && U_SUCCESS(*status)) {
430              *status = r->fBogus; /* set the returning status */
431         }
432     }
433     return r;
434 }
435 
436 static UResourceDataEntry *
getPoolEntry(const char * path,UErrorCode * status)437 getPoolEntry(const char *path, UErrorCode *status) {
438     UResourceDataEntry *poolBundle = init_entry(kPoolBundleName, path, status);
439     if( U_SUCCESS(*status) &&
440         (poolBundle == NULL || poolBundle->fBogus != U_ZERO_ERROR || !poolBundle->fData.isPoolBundle)
441     ) {
442         *status = U_INVALID_FORMAT_ERROR;
443     }
444     return poolBundle;
445 }
446 
447 /* INTERNAL: */
448 /*   CAUTION:  resbMutex must be locked when calling this function! */
449 static UResourceDataEntry *
findFirstExisting(const char * path,char * name,UBool * isRoot,UBool * hasChopped,UBool * isDefault,UErrorCode * status)450 findFirstExisting(const char* path, char* name,
451                   UBool *isRoot, UBool *hasChopped, UBool *isDefault, UErrorCode* status) {
452     UResourceDataEntry *r = NULL;
453     UBool hasRealData = FALSE;
454     const char *defaultLoc = uloc_getDefault();
455     *hasChopped = TRUE; /* we're starting with a fresh name */
456 
457     while(*hasChopped && !hasRealData) {
458         r = init_entry(name, path, status);
459         /* Null pointer test */
460         if (U_FAILURE(*status)) {
461             return NULL;
462         }
463         *isDefault = (UBool)(uprv_strncmp(name, defaultLoc, uprv_strlen(name)) == 0);
464         hasRealData = (UBool)(r->fBogus == U_ZERO_ERROR);
465         if(!hasRealData) {
466             /* this entry is not real. We will discard it. */
467             /* However, the parent line for this entry is  */
468             /* not to be used - as there might be parent   */
469             /* lines in cache from previous openings that  */
470             /* are not updated yet. */
471             r->fCountExisting--;
472             /*entryCloseInt(r);*/
473             r = NULL;
474             *status = U_USING_FALLBACK_WARNING;
475         } else {
476             uprv_strcpy(name, r->fName); /* this is needed for supporting aliases */
477         }
478 
479         *isRoot = (UBool)(uprv_strcmp(name, kRootLocaleName) == 0);
480 
481         /*Fallback data stuff*/
482         *hasChopped = chopLocale(name);
483     }
484     return r;
485 }
486 
ures_setIsStackObject(UResourceBundle * resB,UBool state)487 static void ures_setIsStackObject( UResourceBundle* resB, UBool state) {
488     if(state) {
489         resB->fMagic1 = 0;
490         resB->fMagic2 = 0;
491     } else {
492         resB->fMagic1 = MAGIC1;
493         resB->fMagic2 = MAGIC2;
494     }
495 }
496 
ures_isStackObject(const UResourceBundle * resB)497 static UBool ures_isStackObject(const UResourceBundle* resB) {
498   return((resB->fMagic1 == MAGIC1 && resB->fMagic2 == MAGIC2)?FALSE:TRUE);
499 }
500 
501 
ures_initStackObject(UResourceBundle * resB)502 U_CFUNC void ures_initStackObject(UResourceBundle* resB) {
503   uprv_memset(resB, 0, sizeof(UResourceBundle));
504   ures_setIsStackObject(resB, TRUE);
505 }
506 
507 static UBool  // returns U_SUCCESS(*status)
loadParentsExceptRoot(UResourceDataEntry * & t1,char name[],int32_t nameCapacity,UBool usingUSRData,char usrDataPath[],UErrorCode * status)508 loadParentsExceptRoot(UResourceDataEntry *&t1,
509                       char name[], int32_t nameCapacity,
510                       UBool usingUSRData, char usrDataPath[], UErrorCode *status) {
511     if (U_FAILURE(*status)) { return FALSE; }
512     UBool hasChopped = TRUE;
513     while (hasChopped && t1->fParent == NULL && !t1->fData.noFallback &&
514             res_getResource(&t1->fData,"%%ParentIsRoot") == RES_BOGUS) {
515         Resource parentRes = res_getResource(&t1->fData, "%%Parent");
516         if (parentRes != RES_BOGUS) {  // An explicit parent was found.
517             int32_t parentLocaleLen = 0;
518             const UChar *parentLocaleName = res_getString(&(t1->fData), parentRes, &parentLocaleLen);
519             if(parentLocaleName != NULL && 0 < parentLocaleLen && parentLocaleLen < nameCapacity) {
520                 u_UCharsToChars(parentLocaleName, name, parentLocaleLen + 1);
521                 if (uprv_strcmp(name, kRootLocaleName) == 0) {
522                     return TRUE;
523                 }
524             }
525         }
526         // Insert regular parents.
527         UErrorCode parentStatus = U_ZERO_ERROR;
528         UResourceDataEntry *t2 = init_entry(name, t1->fPath, &parentStatus);
529         if (U_FAILURE(parentStatus)) {
530             *status = parentStatus;
531             return FALSE;
532         }
533         UResourceDataEntry *u2 = NULL;
534         UErrorCode usrStatus = U_ZERO_ERROR;
535         if (usingUSRData) {  // This code inserts user override data into the inheritance chain.
536             u2 = init_entry(name, usrDataPath, &usrStatus);
537         }
538 
539         if (usingUSRData && U_SUCCESS(usrStatus) && u2->fBogus == U_ZERO_ERROR) {
540             t1->fParent = u2;
541             u2->fParent = t2;
542         } else {
543             t1->fParent = t2;
544             if (usingUSRData) {
545                 // The USR override data wasn't found, set it to be deleted.
546                 u2->fCountExisting = 0;
547             }
548         }
549         t1 = t2;
550         hasChopped = chopLocale(name);
551     }
552     return TRUE;
553 }
554 
555 static UBool  // returns U_SUCCESS(*status)
insertRootBundle(UResourceDataEntry * & t1,UErrorCode * status)556 insertRootBundle(UResourceDataEntry *&t1, UErrorCode *status) {
557     if (U_FAILURE(*status)) { return FALSE; }
558     UErrorCode parentStatus = U_ZERO_ERROR;
559     UResourceDataEntry *t2 = init_entry(kRootLocaleName, t1->fPath, &parentStatus);
560     if (U_FAILURE(parentStatus)) {
561         *status = parentStatus;
562         return FALSE;
563     }
564     t1->fParent = t2;
565     t1 = t2;
566     return TRUE;
567 }
568 
569 enum UResOpenType {
570     /**
571      * Open a resource bundle for the locale;
572      * if there is not even a base language bundle, then fall back to the default locale;
573      * if there is no bundle for that either, then load the root bundle.
574      *
575      * This is the default bundle loading behavior.
576      */
577     URES_OPEN_LOCALE_DEFAULT_ROOT,
578     // TODO: ICU ticket #11271 "consistent default locale across locale trees"
579     // Add an option to look at the main locale tree for whether to
580     // fall back to root directly (if the locale has main data) or
581     // fall back to the default locale first (if the locale does not even have main data).
582     /**
583      * Open a resource bundle for the locale;
584      * if there is not even a base language bundle, then load the root bundle;
585      * never fall back to the default locale.
586      *
587      * This is used for algorithms that have good pan-Unicode default behavior,
588      * such as case mappings, collation, and segmentation (BreakIterator).
589      */
590     URES_OPEN_LOCALE_ROOT,
591     /**
592      * Open a resource bundle for the exact bundle name as requested;
593      * no fallbacks, do not load parent bundles.
594      *
595      * This is used for supplemental (non-locale) data.
596      */
597     URES_OPEN_DIRECT
598 };
599 typedef enum UResOpenType UResOpenType;
600 
entryOpen(const char * path,const char * localeID,UResOpenType openType,UErrorCode * status)601 static UResourceDataEntry *entryOpen(const char* path, const char* localeID,
602                                      UResOpenType openType, UErrorCode* status) {
603     U_ASSERT(openType != URES_OPEN_DIRECT);
604     UErrorCode intStatus = U_ZERO_ERROR;
605     UResourceDataEntry *r = NULL;
606     UResourceDataEntry *t1 = NULL;
607     UBool isDefault = FALSE;
608     UBool isRoot = FALSE;
609     UBool hasRealData = FALSE;
610     UBool hasChopped = TRUE;
611     UBool usingUSRData = U_USE_USRDATA && ( path == NULL || uprv_strncmp(path,U_ICUDATA_NAME,8) == 0);
612 
613     char name[ULOC_FULLNAME_CAPACITY];
614     char usrDataPath[96];
615 
616     initCache(status);
617 
618     if(U_FAILURE(*status)) {
619         return NULL;
620     }
621 
622     uprv_strncpy(name, localeID, sizeof(name) - 1);
623     name[sizeof(name) - 1] = 0;
624 
625     if ( usingUSRData ) {
626         if ( path == NULL ) {
627             uprv_strcpy(usrDataPath, U_USRDATA_NAME);
628         } else {
629             uprv_strncpy(usrDataPath, path, sizeof(usrDataPath) - 1);
630             usrDataPath[0] = 'u';
631             usrDataPath[1] = 's';
632             usrDataPath[2] = 'r';
633             usrDataPath[sizeof(usrDataPath) - 1] = 0;
634         }
635     }
636 
637     umtx_lock(&resbMutex);
638     { /* umtx_lock */
639         /* We're going to skip all the locales that do not have any data */
640         r = findFirstExisting(path, name, &isRoot, &hasChopped, &isDefault, &intStatus);
641 
642         if(r != NULL) { /* if there is one real locale, we can look for parents. */
643             t1 = r;
644             hasRealData = TRUE;
645             if ( usingUSRData ) {  /* This code inserts user override data into the inheritance chain */
646                 UErrorCode usrStatus = U_ZERO_ERROR;
647                 UResourceDataEntry *u1 = init_entry(t1->fName, usrDataPath, &usrStatus);
648                if ( u1 != NULL ) {
649                  if(u1->fBogus == U_ZERO_ERROR) {
650                    u1->fParent = t1;
651                    r = u1;
652                  } else {
653                    /* the USR override data wasn't found, set it to be deleted */
654                    u1->fCountExisting = 0;
655                  }
656                }
657             }
658             if (hasChopped && !isRoot) {
659                 if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) {
660                     goto finishUnlock;
661                 }
662             }
663         }
664 
665         /* we could have reached this point without having any real data */
666         /* if that is the case, we need to chain in the default locale   */
667         if(r==NULL && openType == URES_OPEN_LOCALE_DEFAULT_ROOT && !isDefault && !isRoot) {
668             /* insert default locale */
669             uprv_strcpy(name, uloc_getDefault());
670             r = findFirstExisting(path, name, &isRoot, &hasChopped, &isDefault, &intStatus);
671             intStatus = U_USING_DEFAULT_WARNING;
672             if(r != NULL) { /* the default locale exists */
673                 t1 = r;
674                 hasRealData = TRUE;
675                 isDefault = TRUE;
676                 // TODO: Why not if (usingUSRData) { ... } like in the non-default-locale code path?
677                 if (hasChopped && !isRoot) {
678                     if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) {
679                         goto finishUnlock;
680                     }
681                 }
682             }
683         }
684 
685         /* we could still have r == NULL at this point - maybe even default locale is not */
686         /* present */
687         if(r == NULL) {
688             uprv_strcpy(name, kRootLocaleName);
689             r = findFirstExisting(path, name, &isRoot, &hasChopped, &isDefault, &intStatus);
690             if(r != NULL) {
691                 t1 = r;
692                 intStatus = U_USING_DEFAULT_WARNING;
693                 hasRealData = TRUE;
694             } else { /* we don't even have the root locale */
695                 *status = U_MISSING_RESOURCE_ERROR;
696                 goto finishUnlock;
697             }
698         } else if(!isRoot && uprv_strcmp(t1->fName, kRootLocaleName) != 0 &&
699                 t1->fParent == NULL && !r->fData.noFallback) {
700             if (!insertRootBundle(t1, status)) {
701                 goto finishUnlock;
702             }
703             if(!hasRealData) {
704                 r->fBogus = U_USING_DEFAULT_WARNING;
705             }
706         }
707 
708         // TODO: Does this ever loop?
709         while(r != NULL && !isRoot && t1->fParent != NULL) {
710             t1->fParent->fCountExisting++;
711             t1 = t1->fParent;
712         }
713     } /* umtx_lock */
714 finishUnlock:
715     umtx_unlock(&resbMutex);
716 
717     if(U_SUCCESS(*status)) {
718         if(intStatus != U_ZERO_ERROR) {
719             *status = intStatus;
720         }
721         return r;
722     } else {
723         return NULL;
724     }
725 }
726 
727 /**
728  * Version of entryOpen() and findFirstExisting() for ures_openDirect(),
729  * with no fallbacks.
730  * Parent and root locale bundles are loaded if
731  * the requested bundle does not have the "nofallback" flag.
732  */
733 static UResourceDataEntry *
entryOpenDirect(const char * path,const char * localeID,UErrorCode * status)734 entryOpenDirect(const char* path, const char* localeID, UErrorCode* status) {
735     initCache(status);
736     if(U_FAILURE(*status)) {
737         return NULL;
738     }
739 
740     umtx_lock(&resbMutex);
741     // findFirstExisting() without fallbacks.
742     UResourceDataEntry *r = init_entry(localeID, path, status);
743     if(U_SUCCESS(*status)) {
744         if(r->fBogus != U_ZERO_ERROR) {
745             r->fCountExisting--;
746             r = NULL;
747         }
748     } else {
749         r = NULL;
750     }
751 
752     // Some code depends on the ures_openDirect() bundle to have a parent bundle chain,
753     // unless it is marked with "nofallback".
754     UResourceDataEntry *t1 = r;
755     if(r != NULL && uprv_strcmp(localeID, kRootLocaleName) != 0 &&  // not root
756             r->fParent == NULL && !r->fData.noFallback &&
757             uprv_strlen(localeID) < ULOC_FULLNAME_CAPACITY) {
758         char name[ULOC_FULLNAME_CAPACITY];
759         uprv_strcpy(name, localeID);
760         if(!chopLocale(name) || uprv_strcmp(name, kRootLocaleName) == 0 ||
761                 loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), FALSE, NULL, status)) {
762             if(uprv_strcmp(t1->fName, kRootLocaleName) != 0 && t1->fParent == NULL) {
763                 insertRootBundle(t1, status);
764             }
765         }
766         if(U_FAILURE(*status)) {
767             r = NULL;
768         }
769     }
770 
771     if(r != NULL) {
772         // TODO: Does this ever loop?
773         while(t1->fParent != NULL) {
774             t1->fParent->fCountExisting++;
775             t1 = t1->fParent;
776         }
777     }
778     umtx_unlock(&resbMutex);
779     return r;
780 }
781 
782 /**
783  * Functions to create and destroy resource bundles.
784  *     CAUTION:  resbMutex must be locked when calling this function.
785  */
786 /* INTERNAL: */
entryCloseInt(UResourceDataEntry * resB)787 static void entryCloseInt(UResourceDataEntry *resB) {
788     UResourceDataEntry *p = resB;
789 
790     while(resB != NULL) {
791         p = resB->fParent;
792         resB->fCountExisting--;
793 
794         /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush
795          of the cache. */
796 /*
797         if(resB->fCountExisting <= 0) {
798             uhash_remove(cache, resB);
799             if(resB->fBogus == U_ZERO_ERROR) {
800                 res_unload(&(resB->fData));
801             }
802             if(resB->fName != NULL) {
803                 uprv_free(resB->fName);
804             }
805             if(resB->fPath != NULL) {
806                 uprv_free(resB->fPath);
807             }
808             uprv_free(resB);
809         }
810 */
811 
812         resB = p;
813     }
814 }
815 
816 /**
817  *  API: closes a resource bundle and cleans up.
818  */
819 
entryClose(UResourceDataEntry * resB)820 static void entryClose(UResourceDataEntry *resB) {
821   umtx_lock(&resbMutex);
822   entryCloseInt(resB);
823   umtx_unlock(&resbMutex);
824 }
825 
826 /*
827 U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
828   if(resB->fResPath == NULL) {
829     resB->fResPath = resB->fResBuf;
830     *(resB->fResPath) = 0;
831   }
832   resB->fResPathLen = uprv_strlen(toAdd);
833   if(RES_BUFSIZE <= resB->fResPathLen+1) {
834     if(resB->fResPath == resB->fResBuf) {
835       resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
836     } else {
837       resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
838     }
839   }
840   uprv_strcpy(resB->fResPath, toAdd);
841 }
842 */
ures_appendResPath(UResourceBundle * resB,const char * toAdd,int32_t lenToAdd,UErrorCode * status)843 static void ures_appendResPath(UResourceBundle *resB, const char* toAdd, int32_t lenToAdd, UErrorCode *status) {
844     int32_t resPathLenOrig = resB->fResPathLen;
845     if(resB->fResPath == NULL) {
846         resB->fResPath = resB->fResBuf;
847         *(resB->fResPath) = 0;
848         resB->fResPathLen = 0;
849     }
850     resB->fResPathLen += lenToAdd;
851     if(RES_BUFSIZE <= resB->fResPathLen+1) {
852         if(resB->fResPath == resB->fResBuf) {
853             resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
854             /* Check that memory was allocated correctly. */
855             if (resB->fResPath == NULL) {
856                 *status = U_MEMORY_ALLOCATION_ERROR;
857                 return;
858             }
859             uprv_strcpy(resB->fResPath, resB->fResBuf);
860         } else {
861             char *temp = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
862             /* Check that memory was reallocated correctly. */
863             if (temp == NULL) {
864                 *status = U_MEMORY_ALLOCATION_ERROR;
865                 return;
866             }
867             resB->fResPath = temp;
868         }
869     }
870     uprv_strcpy(resB->fResPath + resPathLenOrig, toAdd);
871 }
872 
ures_freeResPath(UResourceBundle * resB)873 static void ures_freeResPath(UResourceBundle *resB) {
874     if (resB->fResPath && resB->fResPath != resB->fResBuf) {
875         uprv_free(resB->fResPath);
876     }
877     resB->fResPath = NULL;
878     resB->fResPathLen = 0;
879 }
880 
881 static void
ures_closeBundle(UResourceBundle * resB,UBool freeBundleObj)882 ures_closeBundle(UResourceBundle* resB, UBool freeBundleObj)
883 {
884     if(resB != NULL) {
885         if(resB->fData != NULL) {
886             entryClose(resB->fData);
887         }
888         if(resB->fVersion != NULL) {
889             uprv_free(resB->fVersion);
890         }
891         ures_freeResPath(resB);
892 
893         if(ures_isStackObject(resB) == FALSE && freeBundleObj) {
894             uprv_free(resB);
895         }
896 #if 0 /*U_DEBUG*/
897         else {
898             /* poison the data */
899             uprv_memset(resB, -1, sizeof(UResourceBundle));
900         }
901 #endif
902     }
903 }
904 
905 U_CAPI void  U_EXPORT2
ures_close(UResourceBundle * resB)906 ures_close(UResourceBundle* resB)
907 {
908     ures_closeBundle(resB, TRUE);
909 }
910 
init_resb_result(const ResourceData * rdata,Resource r,const char * key,int32_t idx,UResourceDataEntry * realData,const UResourceBundle * parent,int32_t noAlias,UResourceBundle * resB,UErrorCode * status)911 static UResourceBundle *init_resb_result(const ResourceData *rdata, Resource r,
912                                          const char *key, int32_t idx, UResourceDataEntry *realData,
913                                          const UResourceBundle *parent, int32_t noAlias,
914                                          UResourceBundle *resB, UErrorCode *status)
915 {
916     if(status == NULL || U_FAILURE(*status)) {
917         return resB;
918     }
919     if (parent == NULL) {
920         *status = U_ILLEGAL_ARGUMENT_ERROR;
921         return NULL;
922     }
923     if(RES_GET_TYPE(r) == URES_ALIAS) { /* This is an alias, need to exchange with real data */
924         if(noAlias < URES_MAX_ALIAS_LEVEL) {
925             int32_t len = 0;
926             const UChar *alias = res_getAlias(rdata, r, &len);
927             if(len > 0) {
928                 /* we have an alias, now let's cut it up */
929                 char stackAlias[200];
930                 char *chAlias = NULL, *path = NULL, *locale = NULL, *keyPath = NULL;
931                 int32_t capacity;
932 
933                 /*
934                 * Allocate enough space for both the char * version
935                 * of the alias and parent->fResPath.
936                 *
937                 * We do this so that res_findResource() can modify the path,
938                 * which allows us to remove redundant _res_findResource() variants
939                 * in uresdata.c.
940                 * res_findResource() now NUL-terminates each segment so that table keys
941                 * can always be compared with strcmp() instead of strncmp().
942                 * Saves code there and simplifies testing and code coverage.
943                 *
944                 * markus 2003oct17
945                 */
946                 ++len; /* count the terminating NUL */
947                 if(parent->fResPath != NULL) {
948                     capacity = (int32_t)uprv_strlen(parent->fResPath) + 1;
949                 } else {
950                     capacity = 0;
951                 }
952                 if(capacity < len) {
953                     capacity = len;
954                 }
955                 if(capacity <= (int32_t)sizeof(stackAlias)) {
956                     capacity = (int32_t)sizeof(stackAlias);
957                     chAlias = stackAlias;
958                 } else {
959                     chAlias = (char *)uprv_malloc(capacity);
960                     /* test for NULL */
961                     if(chAlias == NULL) {
962                         *status = U_MEMORY_ALLOCATION_ERROR;
963                         return NULL;
964                     }
965                 }
966                 u_UCharsToChars(alias, chAlias, len);
967 
968                 if(*chAlias == RES_PATH_SEPARATOR) {
969                     /* there is a path included */
970                     locale = uprv_strchr(chAlias+1, RES_PATH_SEPARATOR);
971                     if(locale == NULL) {
972                         locale = uprv_strchr(chAlias, 0); /* avoid locale == NULL to make code below work */
973                     } else {
974                         *locale = 0;
975                         locale++;
976                     }
977                     path = chAlias+1;
978                     if(uprv_strcmp(path, "LOCALE") == 0) {
979                         /* this is an XPath alias, starting with "/LOCALE/" */
980                         /* it contains the path to a resource which should be looked up */
981                         /* starting in the requested locale */
982                         keyPath = locale;
983                         locale = parent->fTopLevelData->fName; /* this is the requested locale's name */
984                         path = realData->fPath; /* we will be looking in the same package */
985                     } else {
986                         if(uprv_strcmp(path, "ICUDATA") == 0) { /* want ICU data */
987                             path = NULL;
988                         }
989                         keyPath = uprv_strchr(locale, RES_PATH_SEPARATOR);
990                         if(keyPath) {
991                             *keyPath = 0;
992                             keyPath++;
993                         }
994                     }
995                 } else {
996                     /* no path, start with a locale */
997                     locale = chAlias;
998                     keyPath = uprv_strchr(locale, RES_PATH_SEPARATOR);
999                     if(keyPath) {
1000                         *keyPath = 0;
1001                         keyPath++;
1002                     }
1003                     path = realData->fPath;
1004                 }
1005 
1006 
1007                 {
1008                     /* got almost everything, let's try to open */
1009                     /* first, open the bundle with real data */
1010                     UResourceBundle *result = resB;
1011                     const char* temp = NULL;
1012                     UErrorCode intStatus = U_ZERO_ERROR;
1013                     UResourceBundle *mainRes = ures_openDirect(path, locale, &intStatus);
1014                     if(U_SUCCESS(intStatus)) {
1015                         if(keyPath == NULL) {
1016                             /* no key path. This means that we are going to
1017                             * to use the corresponding resource from
1018                             * another bundle
1019                             */
1020                             /* first, we are going to get a corresponding parent
1021                             * resource to the one we are searching.
1022                             */
1023                             char *aKey = parent->fResPath;
1024                             if(aKey) {
1025                                 uprv_strcpy(chAlias, aKey); /* allocated large enough above */
1026                                 aKey = chAlias;
1027                                 r = res_findResource(&(mainRes->fResData), mainRes->fRes, &aKey, &temp);
1028                             } else {
1029                                 r = mainRes->fRes;
1030                             }
1031                             if(key) {
1032                                 /* we need to make keyPath from parent's fResPath and
1033                                 * current key, if there is a key associated
1034                                 */
1035                                 len = (int32_t)(uprv_strlen(key) + 1);
1036                                 if(len > capacity) {
1037                                     capacity = len;
1038                                     if(chAlias == stackAlias) {
1039                                         chAlias = (char *)uprv_malloc(capacity);
1040                                     } else {
1041                                         chAlias = (char *)uprv_realloc(chAlias, capacity);
1042                                     }
1043                                     if(chAlias == NULL) {
1044                                         ures_close(mainRes);
1045                                         *status = U_MEMORY_ALLOCATION_ERROR;
1046                                         return NULL;
1047                                     }
1048                                 }
1049                                 uprv_memcpy(chAlias, key, len);
1050                                 aKey = chAlias;
1051                                 r = res_findResource(&(mainRes->fResData), r, &aKey, &temp);
1052                             } else if(idx != -1) {
1053                                 /* if there is no key, but there is an index, try to get by the index */
1054                                 /* here we have either a table or an array, so get the element */
1055                                 int32_t type = RES_GET_TYPE(r);
1056                                 if(URES_IS_TABLE(type)) {
1057                                     r = res_getTableItemByIndex(&(mainRes->fResData), r, idx, (const char **)&aKey);
1058                                 } else { /* array */
1059                                     r = res_getArrayItem(&(mainRes->fResData), r, idx);
1060                                 }
1061                             }
1062                             if(r != RES_BOGUS) {
1063                                 result = init_resb_result(&(mainRes->fResData), r, temp, -1, mainRes->fData, mainRes, noAlias+1, resB, status);
1064                             } else {
1065                                 *status = U_MISSING_RESOURCE_ERROR;
1066                                 result = resB;
1067                             }
1068                         } else {
1069                             /* this one is a bit trickier.
1070                             * we start finding keys, but after we resolve one alias, the path might continue.
1071                             * Consider:
1072                             *     aliastest:alias { "testtypes/anotheralias/Sequence" }
1073                             *     anotheralias:alias { "/ICUDATA/sh/CollationElements" }
1074                             * aliastest resource should finally have the sequence, not collation elements.
1075                             */
1076                             UResourceDataEntry *dataEntry = mainRes->fData;
1077                             char stackPath[URES_MAX_BUFFER_SIZE];
1078                             char *pathBuf = stackPath, *myPath = pathBuf;
1079                             if(uprv_strlen(keyPath) > URES_MAX_BUFFER_SIZE) {
1080                                 pathBuf = (char *)uprv_malloc((uprv_strlen(keyPath)+1)*sizeof(char));
1081                                 if(pathBuf == NULL) {
1082                                     *status = U_MEMORY_ALLOCATION_ERROR;
1083                                     return NULL;
1084                                 }
1085                             }
1086                             uprv_strcpy(pathBuf, keyPath);
1087                             result = mainRes;
1088                             /* now we have fallback following here */
1089                             do {
1090                                 r = dataEntry->fData.rootRes;
1091                                 /* this loop handles 'found' resources over several levels */
1092                                 while(*myPath && U_SUCCESS(*status)) {
1093                                     r = res_findResource(&(dataEntry->fData), r, &myPath, &temp);
1094                                     if(r != RES_BOGUS) { /* found a resource, but it might be an indirection */
1095                                         resB = init_resb_result(&(dataEntry->fData), r, temp, -1, dataEntry, result, noAlias+1, resB, status);
1096                                         result = resB;
1097                                         if(result) {
1098                                             r = result->fRes; /* switch to a new resource, possibly a new tree */
1099                                             dataEntry = result->fData;
1100                                         }
1101                                     } else { /* no resource found, we don't really want to look anymore on this level */
1102                                         break;
1103                                     }
1104                                 }
1105                                 dataEntry = dataEntry->fParent;
1106                                 uprv_strcpy(pathBuf, keyPath);
1107                                 myPath = pathBuf;
1108                             } while(r == RES_BOGUS && dataEntry != NULL);
1109                             if(r == RES_BOGUS) {
1110                                 *status = U_MISSING_RESOURCE_ERROR;
1111                                 result = resB;
1112                             }
1113                             if(pathBuf != stackPath) {
1114                                 uprv_free(pathBuf);
1115                             }
1116                         }
1117                     } else { /* we failed to open the resource we're aliasing to */
1118                         *status = intStatus;
1119                     }
1120                     if(chAlias != stackAlias) {
1121                         uprv_free(chAlias);
1122                     }
1123                     if(mainRes != result) {
1124                         ures_close(mainRes);
1125                     }
1126                     return result;
1127                 }
1128             } else {
1129                 /* bad alias, should be an error */
1130                 *status = U_ILLEGAL_ARGUMENT_ERROR;
1131                 return resB;
1132             }
1133         } else {
1134             *status = U_TOO_MANY_ALIASES_ERROR;
1135             return resB;
1136         }
1137     }
1138     if(resB == NULL) {
1139         resB = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
1140         /* test for NULL */
1141         if (resB == NULL) {
1142             *status = U_MEMORY_ALLOCATION_ERROR;
1143             return NULL;
1144         }
1145         ures_setIsStackObject(resB, FALSE);
1146         resB->fResPath = NULL;
1147         resB->fResPathLen = 0;
1148     } else {
1149         if(resB->fData != NULL) {
1150             entryClose(resB->fData);
1151         }
1152         if(resB->fVersion != NULL) {
1153             uprv_free(resB->fVersion);
1154         }
1155         /*
1156         weiv: if stack object was passed in, it doesn't really need to be reinited,
1157         since the purpose of initing is to remove stack junk. However, at this point
1158         we would not do anything to an allocated object, so stack object should be
1159         treated the same
1160         */
1161         /*
1162         if(ures_isStackObject(resB) != FALSE) {
1163         ures_initStackObject(resB);
1164         }
1165         */
1166         if(parent != resB) {
1167             ures_freeResPath(resB);
1168         }
1169     }
1170     resB->fData = realData;
1171     entryIncrease(resB->fData);
1172     resB->fHasFallback = FALSE;
1173     resB->fIsTopLevel = FALSE;
1174     resB->fIndex = -1;
1175     resB->fKey = key;
1176     /*resB->fParentRes = parent;*/
1177     resB->fTopLevelData = parent->fTopLevelData;
1178     if(parent->fResPath && parent != resB) {
1179         ures_appendResPath(resB, parent->fResPath, parent->fResPathLen, status);
1180     }
1181     if(key != NULL) {
1182         ures_appendResPath(resB, key, (int32_t)uprv_strlen(key), status);
1183         if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
1184             ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
1185         }
1186     } else if(idx >= 0) {
1187         char buf[256];
1188         int32_t len = T_CString_integerToString(buf, idx, 10);
1189         ures_appendResPath(resB, buf, len, status);
1190         if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
1191             ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
1192         }
1193     }
1194     /* Make sure that Purify doesn't complain about uninitialized memory copies. */
1195     {
1196         int32_t usedLen = ((resB->fResBuf == resB->fResPath) ? resB->fResPathLen : 0);
1197         uprv_memset(resB->fResBuf + usedLen, 0, sizeof(resB->fResBuf) - usedLen);
1198     }
1199 
1200     resB->fVersion = NULL;
1201     resB->fRes = r;
1202     /*resB->fParent = parent->fRes;*/
1203     uprv_memmove(&resB->fResData, rdata, sizeof(ResourceData));
1204     resB->fSize = res_countArrayItems(&(resB->fResData), resB->fRes);
1205     return resB;
1206 }
1207 
ures_copyResb(UResourceBundle * r,const UResourceBundle * original,UErrorCode * status)1208 UResourceBundle *ures_copyResb(UResourceBundle *r, const UResourceBundle *original, UErrorCode *status) {
1209     UBool isStackObject;
1210     if(U_FAILURE(*status) || r == original) {
1211         return r;
1212     }
1213     if(original != NULL) {
1214         if(r == NULL) {
1215             isStackObject = FALSE;
1216             r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
1217             /* test for NULL */
1218             if (r == NULL) {
1219                 *status = U_MEMORY_ALLOCATION_ERROR;
1220                 return NULL;
1221             }
1222         } else {
1223             isStackObject = ures_isStackObject(r);
1224             ures_closeBundle(r, FALSE);
1225         }
1226         uprv_memcpy(r, original, sizeof(UResourceBundle));
1227         r->fResPath = NULL;
1228         r->fResPathLen = 0;
1229         if(original->fResPath) {
1230             ures_appendResPath(r, original->fResPath, original->fResPathLen, status);
1231         }
1232         ures_setIsStackObject(r, isStackObject);
1233         if(r->fData != NULL) {
1234             entryIncrease(r->fData);
1235         }
1236     }
1237     return r;
1238 }
1239 
1240 /**
1241  * Functions to retrieve data from resource bundles.
1242  */
1243 
ures_getString(const UResourceBundle * resB,int32_t * len,UErrorCode * status)1244 U_CAPI const UChar* U_EXPORT2 ures_getString(const UResourceBundle* resB, int32_t* len, UErrorCode* status) {
1245     const UChar *s;
1246     if (status==NULL || U_FAILURE(*status)) {
1247         return NULL;
1248     }
1249     if(resB == NULL) {
1250         *status = U_ILLEGAL_ARGUMENT_ERROR;
1251         return NULL;
1252     }
1253     s = res_getString(&(resB->fResData), resB->fRes, len);
1254     if (s == NULL) {
1255         *status = U_RESOURCE_TYPE_MISMATCH;
1256     }
1257     return s;
1258 }
1259 
1260 static const char *
ures_toUTF8String(const UChar * s16,int32_t length16,char * dest,int32_t * pLength,UBool forceCopy,UErrorCode * status)1261 ures_toUTF8String(const UChar *s16, int32_t length16,
1262                   char *dest, int32_t *pLength,
1263                   UBool forceCopy,
1264                   UErrorCode *status) {
1265     int32_t capacity;
1266 
1267     if (U_FAILURE(*status)) {
1268         return NULL;
1269     }
1270     if (pLength != NULL) {
1271         capacity = *pLength;
1272     } else {
1273         capacity = 0;
1274     }
1275     if (capacity < 0 || (capacity > 0 && dest == NULL)) {
1276         *status = U_ILLEGAL_ARGUMENT_ERROR;
1277         return NULL;
1278     }
1279 
1280     if (length16 == 0) {
1281         /* empty string, return as read-only pointer */
1282         if (pLength != NULL) {
1283             *pLength = 0;
1284         }
1285         if (forceCopy) {
1286             u_terminateChars(dest, capacity, 0, status);
1287             return dest;
1288         } else {
1289             return "";
1290         }
1291     } else {
1292         /* We need to transform the string to the destination buffer. */
1293         if (capacity < length16) {
1294             /* No chance for the string to fit. Pure preflighting. */
1295             return u_strToUTF8(NULL, 0, pLength, s16, length16, status);
1296         }
1297         if (!forceCopy && (length16 <= 0x2aaaaaaa)) {
1298             /*
1299              * We know the string will fit into dest because each UChar turns
1300              * into at most three UTF-8 bytes. Fill the latter part of dest
1301              * so that callers do not expect to use dest as a string pointer,
1302              * hopefully leading to more robust code for when resource bundles
1303              * may store UTF-8 natively.
1304              * (In which case dest would not be used at all.)
1305              *
1306              * We do not do this if forceCopy=TRUE because then the caller
1307              * expects the string to start exactly at dest.
1308              *
1309              * The test above for <= 0x2aaaaaaa prevents overflows.
1310              * The +1 is for the NUL terminator.
1311              */
1312             int32_t maxLength = 3 * length16 + 1;
1313             if (capacity > maxLength) {
1314                 dest += capacity - maxLength;
1315                 capacity = maxLength;
1316             }
1317         }
1318         return u_strToUTF8(dest, capacity, pLength, s16, length16, status);
1319     }
1320 }
1321 
1322 U_CAPI const char * U_EXPORT2
ures_getUTF8String(const UResourceBundle * resB,char * dest,int32_t * pLength,UBool forceCopy,UErrorCode * status)1323 ures_getUTF8String(const UResourceBundle *resB,
1324                    char *dest, int32_t *pLength,
1325                    UBool forceCopy,
1326                    UErrorCode *status) {
1327     int32_t length16;
1328     const UChar *s16 = ures_getString(resB, &length16, status);
1329     return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
1330 }
1331 
ures_getBinary(const UResourceBundle * resB,int32_t * len,UErrorCode * status)1332 U_CAPI const uint8_t* U_EXPORT2 ures_getBinary(const UResourceBundle* resB, int32_t* len,
1333                                                UErrorCode*               status) {
1334   const uint8_t *p;
1335   if (status==NULL || U_FAILURE(*status)) {
1336     return NULL;
1337   }
1338   if(resB == NULL) {
1339     *status = U_ILLEGAL_ARGUMENT_ERROR;
1340     return NULL;
1341   }
1342   p = res_getBinary(&(resB->fResData), resB->fRes, len);
1343   if (p == NULL) {
1344     *status = U_RESOURCE_TYPE_MISMATCH;
1345   }
1346   return p;
1347 }
1348 
ures_getIntVector(const UResourceBundle * resB,int32_t * len,UErrorCode * status)1349 U_CAPI const int32_t* U_EXPORT2 ures_getIntVector(const UResourceBundle* resB, int32_t* len,
1350                                                    UErrorCode*               status) {
1351   const int32_t *p;
1352   if (status==NULL || U_FAILURE(*status)) {
1353     return NULL;
1354   }
1355   if(resB == NULL) {
1356     *status = U_ILLEGAL_ARGUMENT_ERROR;
1357     return NULL;
1358   }
1359   p = res_getIntVector(&(resB->fResData), resB->fRes, len);
1360   if (p == NULL) {
1361     *status = U_RESOURCE_TYPE_MISMATCH;
1362   }
1363   return p;
1364 }
1365 
1366 /* this function returns a signed integer */
1367 /* it performs sign extension */
ures_getInt(const UResourceBundle * resB,UErrorCode * status)1368 U_CAPI int32_t U_EXPORT2 ures_getInt(const UResourceBundle* resB, UErrorCode *status) {
1369   if (status==NULL || U_FAILURE(*status)) {
1370     return 0xffffffff;
1371   }
1372   if(resB == NULL) {
1373     *status = U_ILLEGAL_ARGUMENT_ERROR;
1374     return 0xffffffff;
1375   }
1376   if(RES_GET_TYPE(resB->fRes) != URES_INT) {
1377     *status = U_RESOURCE_TYPE_MISMATCH;
1378     return 0xffffffff;
1379   }
1380   return RES_GET_INT(resB->fRes);
1381 }
1382 
ures_getUInt(const UResourceBundle * resB,UErrorCode * status)1383 U_CAPI uint32_t U_EXPORT2 ures_getUInt(const UResourceBundle* resB, UErrorCode *status) {
1384   if (status==NULL || U_FAILURE(*status)) {
1385     return 0xffffffff;
1386   }
1387   if(resB == NULL) {
1388     *status = U_ILLEGAL_ARGUMENT_ERROR;
1389     return 0xffffffff;
1390   }
1391   if(RES_GET_TYPE(resB->fRes) != URES_INT) {
1392     *status = U_RESOURCE_TYPE_MISMATCH;
1393     return 0xffffffff;
1394   }
1395   return RES_GET_UINT(resB->fRes);
1396 }
1397 
ures_getType(const UResourceBundle * resB)1398 U_CAPI UResType U_EXPORT2 ures_getType(const UResourceBundle *resB) {
1399   if(resB == NULL) {
1400     return URES_NONE;
1401   }
1402   return res_getPublicType(resB->fRes);
1403 }
1404 
ures_getKey(const UResourceBundle * resB)1405 U_CAPI const char * U_EXPORT2 ures_getKey(const UResourceBundle *resB) {
1406   if(resB == NULL) {
1407     return NULL;
1408   }
1409 
1410   return(resB->fKey);
1411 }
1412 
ures_getSize(const UResourceBundle * resB)1413 U_CAPI int32_t U_EXPORT2 ures_getSize(const UResourceBundle *resB) {
1414   if(resB == NULL) {
1415     return 0;
1416   }
1417 
1418   return resB->fSize;
1419 }
1420 
ures_getStringWithAlias(const UResourceBundle * resB,Resource r,int32_t sIndex,int32_t * len,UErrorCode * status)1421 static const UChar* ures_getStringWithAlias(const UResourceBundle *resB, Resource r, int32_t sIndex, int32_t *len, UErrorCode *status) {
1422   if(RES_GET_TYPE(r) == URES_ALIAS) {
1423     const UChar* result = 0;
1424     UResourceBundle *tempRes = ures_getByIndex(resB, sIndex, NULL, status);
1425     result = ures_getString(tempRes, len, status);
1426     ures_close(tempRes);
1427     return result;
1428   } else {
1429     return res_getString(&(resB->fResData), r, len);
1430   }
1431 }
1432 
ures_resetIterator(UResourceBundle * resB)1433 U_CAPI void U_EXPORT2 ures_resetIterator(UResourceBundle *resB){
1434   if(resB == NULL) {
1435     return;
1436   }
1437   resB->fIndex = -1;
1438 }
1439 
ures_hasNext(const UResourceBundle * resB)1440 U_CAPI UBool U_EXPORT2 ures_hasNext(const UResourceBundle *resB) {
1441   if(resB == NULL) {
1442     return FALSE;
1443   }
1444   return (UBool)(resB->fIndex < resB->fSize-1);
1445 }
1446 
ures_getNextString(UResourceBundle * resB,int32_t * len,const char ** key,UErrorCode * status)1447 U_CAPI const UChar* U_EXPORT2 ures_getNextString(UResourceBundle *resB, int32_t* len, const char ** key, UErrorCode *status) {
1448   Resource r = RES_BOGUS;
1449 
1450   if (status==NULL || U_FAILURE(*status)) {
1451     return NULL;
1452   }
1453   if(resB == NULL) {
1454     *status = U_ILLEGAL_ARGUMENT_ERROR;
1455     return NULL;
1456   }
1457 
1458   if(resB->fIndex == resB->fSize-1) {
1459     *status = U_INDEX_OUTOFBOUNDS_ERROR;
1460   } else {
1461     resB->fIndex++;
1462     switch(RES_GET_TYPE(resB->fRes)) {
1463     case URES_STRING:
1464     case URES_STRING_V2:
1465       return res_getString(&(resB->fResData), resB->fRes, len);
1466     case URES_TABLE:
1467     case URES_TABLE16:
1468     case URES_TABLE32:
1469       r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, resB->fIndex, key);
1470       if(r == RES_BOGUS && resB->fHasFallback) {
1471         /* TODO: do the fallback */
1472       }
1473       return ures_getStringWithAlias(resB, r, resB->fIndex, len, status);
1474     case URES_ARRAY:
1475     case URES_ARRAY16:
1476       r = res_getArrayItem(&(resB->fResData), resB->fRes, resB->fIndex);
1477       if(r == RES_BOGUS && resB->fHasFallback) {
1478         /* TODO: do the fallback */
1479       }
1480       return ures_getStringWithAlias(resB, r, resB->fIndex, len, status);
1481     case URES_ALIAS:
1482       return ures_getStringWithAlias(resB, resB->fRes, resB->fIndex, len, status);
1483     case URES_INT:
1484     case URES_BINARY:
1485     case URES_INT_VECTOR:
1486         *status = U_RESOURCE_TYPE_MISMATCH;
1487     default: /*fall through*/
1488       return NULL;
1489     }
1490   }
1491 
1492   return NULL;
1493 }
1494 
ures_getNextResource(UResourceBundle * resB,UResourceBundle * fillIn,UErrorCode * status)1495 U_CAPI UResourceBundle* U_EXPORT2 ures_getNextResource(UResourceBundle *resB, UResourceBundle *fillIn, UErrorCode *status) {
1496     const char *key = NULL;
1497     Resource r = RES_BOGUS;
1498 
1499     if (status==NULL || U_FAILURE(*status)) {
1500             /*return NULL;*/
1501             return fillIn;
1502     }
1503     if(resB == NULL) {
1504             *status = U_ILLEGAL_ARGUMENT_ERROR;
1505             /*return NULL;*/
1506             return fillIn;
1507     }
1508 
1509     if(resB->fIndex == resB->fSize-1) {
1510       *status = U_INDEX_OUTOFBOUNDS_ERROR;
1511       /*return NULL;*/
1512     } else {
1513         resB->fIndex++;
1514         switch(RES_GET_TYPE(resB->fRes)) {
1515         case URES_INT:
1516         case URES_BINARY:
1517         case URES_STRING:
1518         case URES_STRING_V2:
1519         case URES_INT_VECTOR:
1520             return ures_copyResb(fillIn, resB, status);
1521         case URES_TABLE:
1522         case URES_TABLE16:
1523         case URES_TABLE32:
1524             r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, resB->fIndex, &key);
1525             if(r == RES_BOGUS && resB->fHasFallback) {
1526                 /* TODO: do the fallback */
1527             }
1528             return init_resb_result(&(resB->fResData), r, key, resB->fIndex, resB->fData, resB, 0, fillIn, status);
1529         case URES_ARRAY:
1530         case URES_ARRAY16:
1531             r = res_getArrayItem(&(resB->fResData), resB->fRes, resB->fIndex);
1532             if(r == RES_BOGUS && resB->fHasFallback) {
1533                 /* TODO: do the fallback */
1534             }
1535             return init_resb_result(&(resB->fResData), r, key, resB->fIndex, resB->fData, resB, 0, fillIn, status);
1536         default:
1537             /*return NULL;*/
1538             return fillIn;
1539         }
1540     }
1541     /*return NULL;*/
1542     return fillIn;
1543 }
1544 
ures_getByIndex(const UResourceBundle * resB,int32_t indexR,UResourceBundle * fillIn,UErrorCode * status)1545 U_CAPI UResourceBundle* U_EXPORT2 ures_getByIndex(const UResourceBundle *resB, int32_t indexR, UResourceBundle *fillIn, UErrorCode *status) {
1546     const char* key = NULL;
1547     Resource r = RES_BOGUS;
1548 
1549     if (status==NULL || U_FAILURE(*status)) {
1550         /*return NULL;*/
1551         return fillIn;
1552     }
1553     if(resB == NULL) {
1554         *status = U_ILLEGAL_ARGUMENT_ERROR;
1555         /*return NULL;*/
1556         return fillIn;
1557     }
1558 
1559     if(indexR >= 0 && resB->fSize > indexR) {
1560         switch(RES_GET_TYPE(resB->fRes)) {
1561         case URES_INT:
1562         case URES_BINARY:
1563         case URES_STRING:
1564         case URES_STRING_V2:
1565         case URES_INT_VECTOR:
1566             return ures_copyResb(fillIn, resB, status);
1567         case URES_TABLE:
1568         case URES_TABLE16:
1569         case URES_TABLE32:
1570             r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, indexR, &key);
1571             if(r == RES_BOGUS && resB->fHasFallback) {
1572                 /* TODO: do the fallback */
1573             }
1574             return init_resb_result(&(resB->fResData), r, key, indexR, resB->fData, resB, 0, fillIn, status);
1575         case URES_ARRAY:
1576         case URES_ARRAY16:
1577             r = res_getArrayItem(&(resB->fResData), resB->fRes, indexR);
1578             if(r == RES_BOGUS && resB->fHasFallback) {
1579                 /* TODO: do the fallback */
1580             }
1581             return init_resb_result(&(resB->fResData), r, key, indexR, resB->fData, resB, 0, fillIn, status);
1582         default:
1583             /*return NULL;*/
1584             return fillIn;
1585         }
1586     } else {
1587         *status = U_MISSING_RESOURCE_ERROR;
1588     }
1589     /*return NULL;*/
1590     return fillIn;
1591 }
1592 
ures_getStringByIndex(const UResourceBundle * resB,int32_t indexS,int32_t * len,UErrorCode * status)1593 U_CAPI const UChar* U_EXPORT2 ures_getStringByIndex(const UResourceBundle *resB, int32_t indexS, int32_t* len, UErrorCode *status) {
1594     const char* key = NULL;
1595     Resource r = RES_BOGUS;
1596 
1597     if (status==NULL || U_FAILURE(*status)) {
1598         return NULL;
1599     }
1600     if(resB == NULL) {
1601         *status = U_ILLEGAL_ARGUMENT_ERROR;
1602         return NULL;
1603     }
1604 
1605     if(indexS >= 0 && resB->fSize > indexS) {
1606         switch(RES_GET_TYPE(resB->fRes)) {
1607         case URES_STRING:
1608         case URES_STRING_V2:
1609             return res_getString(&(resB->fResData), resB->fRes, len);
1610         case URES_TABLE:
1611         case URES_TABLE16:
1612         case URES_TABLE32:
1613             r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, indexS, &key);
1614             if(r == RES_BOGUS && resB->fHasFallback) {
1615                 /* TODO: do the fallback */
1616             }
1617             return ures_getStringWithAlias(resB, r, indexS, len, status);
1618         case URES_ARRAY:
1619         case URES_ARRAY16:
1620             r = res_getArrayItem(&(resB->fResData), resB->fRes, indexS);
1621             if(r == RES_BOGUS && resB->fHasFallback) {
1622                 /* TODO: do the fallback */
1623             }
1624             return ures_getStringWithAlias(resB, r, indexS, len, status);
1625         case URES_ALIAS:
1626             return ures_getStringWithAlias(resB, resB->fRes, indexS, len, status);
1627         case URES_INT:
1628         case URES_BINARY:
1629         case URES_INT_VECTOR:
1630             *status = U_RESOURCE_TYPE_MISMATCH;
1631             break;
1632         default:
1633           /* must not occur */
1634           *status = U_INTERNAL_PROGRAM_ERROR;
1635           break;
1636         }
1637     } else {
1638         *status = U_MISSING_RESOURCE_ERROR;
1639     }
1640     return NULL;
1641 }
1642 
1643 U_CAPI const char * U_EXPORT2
ures_getUTF8StringByIndex(const UResourceBundle * resB,int32_t idx,char * dest,int32_t * pLength,UBool forceCopy,UErrorCode * status)1644 ures_getUTF8StringByIndex(const UResourceBundle *resB,
1645                           int32_t idx,
1646                           char *dest, int32_t *pLength,
1647                           UBool forceCopy,
1648                           UErrorCode *status) {
1649     int32_t length16;
1650     const UChar *s16 = ures_getStringByIndex(resB, idx, &length16, status);
1651     return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
1652 }
1653 
1654 /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1655   return resB->fResPath;
1656 }*/
1657 
1658 U_CAPI UResourceBundle* U_EXPORT2
ures_findResource(const char * path,UResourceBundle * fillIn,UErrorCode * status)1659 ures_findResource(const char* path, UResourceBundle *fillIn, UErrorCode *status)
1660 {
1661   UResourceBundle *first = NULL;
1662   UResourceBundle *result = fillIn;
1663   char *packageName = NULL;
1664   char *pathToResource = NULL, *save = NULL;
1665   char *locale = NULL, *localeEnd = NULL;
1666   int32_t length;
1667 
1668   if(status == NULL || U_FAILURE(*status)) {
1669     return result;
1670   }
1671 
1672   length = (int32_t)(uprv_strlen(path)+1);
1673   save = pathToResource = (char *)uprv_malloc(length*sizeof(char));
1674   /* test for NULL */
1675   if(pathToResource == NULL) {
1676     *status = U_MEMORY_ALLOCATION_ERROR;
1677     return result;
1678   }
1679   uprv_memcpy(pathToResource, path, length);
1680 
1681   locale = pathToResource;
1682   if(*pathToResource == RES_PATH_SEPARATOR) { /* there is a path specification */
1683     pathToResource++;
1684     packageName = pathToResource;
1685     pathToResource = uprv_strchr(pathToResource, RES_PATH_SEPARATOR);
1686     if(pathToResource == NULL) {
1687       *status = U_ILLEGAL_ARGUMENT_ERROR;
1688     } else {
1689       *pathToResource = 0;
1690       locale = pathToResource+1;
1691     }
1692   }
1693 
1694   localeEnd = uprv_strchr(locale, RES_PATH_SEPARATOR);
1695   if(localeEnd != NULL) {
1696     *localeEnd = 0;
1697   }
1698 
1699   first = ures_open(packageName, locale, status);
1700 
1701   if(U_SUCCESS(*status)) {
1702     if(localeEnd) {
1703       result = ures_findSubResource(first, localeEnd+1, fillIn, status);
1704     } else {
1705       result = ures_copyResb(fillIn, first, status);
1706     }
1707     ures_close(first);
1708   }
1709   uprv_free(save);
1710   return result;
1711 }
1712 
1713 U_CAPI UResourceBundle* U_EXPORT2
ures_findSubResource(const UResourceBundle * resB,char * path,UResourceBundle * fillIn,UErrorCode * status)1714 ures_findSubResource(const UResourceBundle *resB, char* path, UResourceBundle *fillIn, UErrorCode *status)
1715 {
1716   Resource res = RES_BOGUS;
1717   UResourceBundle *result = fillIn;
1718   const char *key;
1719 
1720   if(status == NULL || U_FAILURE(*status)) {
1721     return result;
1722   }
1723 
1724   /* here we do looping and circular alias checking */
1725   /* this loop is here because aliasing is resolved on this level, not on res level */
1726   /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1727   do {
1728     res = res_findResource(&(resB->fResData), resB->fRes, &path, &key);
1729     if(res != RES_BOGUS) {
1730         result = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
1731         resB = result;
1732     } else {
1733         *status = U_MISSING_RESOURCE_ERROR;
1734         break;
1735     }
1736   } while(*path); /* there is more stuff in the path */
1737 
1738   return result;
1739 }
1740 U_INTERNAL const UChar* U_EXPORT2
ures_getStringByKeyWithFallback(const UResourceBundle * resB,const char * inKey,int32_t * len,UErrorCode * status)1741 ures_getStringByKeyWithFallback(const UResourceBundle *resB,
1742                                 const char* inKey,
1743                                 int32_t* len,
1744                                 UErrorCode *status) {
1745 
1746     UResourceBundle stack;
1747     const UChar* retVal = NULL;
1748     ures_initStackObject(&stack);
1749     ures_getByKeyWithFallback(resB, inKey, &stack, status);
1750     int32_t length;
1751     retVal = ures_getString(&stack, &length, status);
1752     ures_close(&stack);
1753     if (U_FAILURE(*status)) {
1754         return NULL;
1755     }
1756     if (length == 3 && retVal[0] == EMPTY_SET && retVal[1] == EMPTY_SET && retVal[2] == EMPTY_SET ) {
1757         retVal = NULL;
1758         length = 0;
1759         *status = U_MISSING_RESOURCE_ERROR;
1760     }
1761     if (len != NULL) {
1762         *len = length;
1763     }
1764     return retVal;
1765 }
1766 
1767 /*
1768   Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
1769 */
getTableItemByKeyPath(const ResourceData * pResData,Resource table,const char * key)1770 static Resource getTableItemByKeyPath(const ResourceData *pResData, Resource table, const char *key) {
1771   Resource resource = table;  /* The current resource */
1772   icu::CharString path;
1773   UErrorCode errorCode = U_ZERO_ERROR;
1774   path.append(key, errorCode);
1775   if (U_FAILURE(errorCode)) { return RES_BOGUS; }
1776   char *pathPart = path.data();  /* Path from current resource to desired resource */
1777   UResType type = (UResType)RES_GET_TYPE(resource);  /* the current resource type */
1778   while (*pathPart && resource != RES_BOGUS && URES_IS_CONTAINER(type)) {
1779     char *nextPathPart = uprv_strchr(pathPart, RES_PATH_SEPARATOR);
1780     if (nextPathPart != NULL) {
1781       *nextPathPart = 0;  /* Terminating null for this part of path. */
1782       nextPathPart++;
1783     } else {
1784       nextPathPart = uprv_strchr(pathPart, 0);
1785     }
1786     int32_t t;
1787     const char *pathP = pathPart;
1788     resource = res_getTableItemByKey(pResData, resource, &t, &pathP);
1789     type = (UResType)RES_GET_TYPE(resource);
1790     pathPart = nextPathPart;
1791   }
1792   if (*pathPart) {
1793     return RES_BOGUS;
1794   }
1795   return resource;
1796 }
1797 
1798 U_CAPI UResourceBundle* U_EXPORT2
ures_getByKeyWithFallback(const UResourceBundle * resB,const char * inKey,UResourceBundle * fillIn,UErrorCode * status)1799 ures_getByKeyWithFallback(const UResourceBundle *resB,
1800                           const char* inKey,
1801                           UResourceBundle *fillIn,
1802                           UErrorCode *status) {
1803     Resource res = RES_BOGUS, rootRes = RES_BOGUS;
1804     /*UResourceDataEntry *realData = NULL;*/
1805     UResourceBundle *helper = NULL;
1806 
1807     if (status==NULL || U_FAILURE(*status)) {
1808         return fillIn;
1809     }
1810     if(resB == NULL) {
1811         *status = U_ILLEGAL_ARGUMENT_ERROR;
1812         return fillIn;
1813     }
1814 
1815     int32_t type = RES_GET_TYPE(resB->fRes);
1816     if(URES_IS_TABLE(type)) {
1817         res = getTableItemByKeyPath(&(resB->fResData), resB->fRes, inKey);
1818         const char* key = inKey;
1819         if(res == RES_BOGUS) {
1820             UResourceDataEntry *dataEntry = resB->fData;
1821             CharString path;
1822             char *myPath = NULL;
1823             const char* resPath = resB->fResPath;
1824             int32_t len = resB->fResPathLen;
1825             while(res == RES_BOGUS && dataEntry->fParent != NULL) { /* Otherwise, we'll look in parents */
1826                 dataEntry = dataEntry->fParent;
1827                 rootRes = dataEntry->fData.rootRes;
1828 
1829                 if(dataEntry->fBogus == U_ZERO_ERROR) {
1830                     path.clear();
1831                     if (len > 0) {
1832                         path.append(resPath, len, *status);
1833                     }
1834                     path.append(inKey, *status);
1835                     if (U_FAILURE(*status)) {
1836                         ures_close(helper);
1837                         return fillIn;
1838                     }
1839                     myPath = path.data();
1840                     key = inKey;
1841                     do {
1842                         res = res_findResource(&(dataEntry->fData), rootRes, &myPath, &key);
1843                         if (RES_GET_TYPE(res) == URES_ALIAS && *myPath) {
1844                             /* We hit an alias, but we didn't finish following the path. */
1845                             helper = init_resb_result(&(dataEntry->fData), res, NULL, -1, dataEntry, resB, 0, helper, status);
1846                             /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1847                             if(helper) {
1848                               dataEntry = helper->fData;
1849                               rootRes = helper->fRes;
1850                               resPath = helper->fResPath;
1851                               len = helper->fResPathLen;
1852 
1853                             } else {
1854                               break;
1855                             }
1856                         }
1857                     } while(*myPath); /* Continue until the whole path is consumed */
1858                 }
1859             }
1860             /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1861             if(res != RES_BOGUS) {
1862               /* check if resB->fResPath gives the right name here */
1863                 if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) {
1864                     *status = U_USING_DEFAULT_WARNING;
1865                 } else {
1866                     *status = U_USING_FALLBACK_WARNING;
1867                 }
1868 
1869                 fillIn = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, fillIn, status);
1870             } else {
1871                 *status = U_MISSING_RESOURCE_ERROR;
1872             }
1873         } else {
1874             fillIn = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
1875         }
1876     }
1877     else {
1878         *status = U_RESOURCE_TYPE_MISMATCH;
1879     }
1880     ures_close(helper);
1881     return fillIn;
1882 }
1883 
1884 
ures_getByKey(const UResourceBundle * resB,const char * inKey,UResourceBundle * fillIn,UErrorCode * status)1885 U_CAPI UResourceBundle* U_EXPORT2 ures_getByKey(const UResourceBundle *resB, const char* inKey, UResourceBundle *fillIn, UErrorCode *status) {
1886     Resource res = RES_BOGUS;
1887     UResourceDataEntry *realData = NULL;
1888     const char *key = inKey;
1889 
1890     if (status==NULL || U_FAILURE(*status)) {
1891         return fillIn;
1892     }
1893     if(resB == NULL) {
1894         *status = U_ILLEGAL_ARGUMENT_ERROR;
1895         return fillIn;
1896     }
1897 
1898     int32_t type = RES_GET_TYPE(resB->fRes);
1899     if(URES_IS_TABLE(type)) {
1900         int32_t t;
1901         res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);
1902         if(res == RES_BOGUS) {
1903             key = inKey;
1904             if(resB->fHasFallback == TRUE) {
1905                 const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
1906                 if(U_SUCCESS(*status)) {
1907                   /* check if resB->fResPath gives the right name here */
1908                     return init_resb_result(rd, res, key, -1, realData, resB, 0, fillIn, status);
1909                 } else {
1910                     *status = U_MISSING_RESOURCE_ERROR;
1911                 }
1912             } else {
1913                 *status = U_MISSING_RESOURCE_ERROR;
1914             }
1915         } else {
1916             return init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
1917         }
1918     }
1919 #if 0
1920     /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1921     /* not currently */
1922     else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == TRUE) {
1923         /* here should go a first attempt to locate the key using index table */
1924         const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
1925         if(U_SUCCESS(*status)) {
1926             return init_resb_result(rd, res, key, realData, resB, fillIn, status);
1927         } else {
1928             *status = U_MISSING_RESOURCE_ERROR;
1929         }
1930     }
1931 #endif
1932     else {
1933         *status = U_RESOURCE_TYPE_MISMATCH;
1934     }
1935     return fillIn;
1936 }
1937 
ures_getStringByKey(const UResourceBundle * resB,const char * inKey,int32_t * len,UErrorCode * status)1938 U_CAPI const UChar* U_EXPORT2 ures_getStringByKey(const UResourceBundle *resB, const char* inKey, int32_t* len, UErrorCode *status) {
1939     Resource res = RES_BOGUS;
1940     UResourceDataEntry *realData = NULL;
1941     const char* key = inKey;
1942 
1943     if (status==NULL || U_FAILURE(*status)) {
1944         return NULL;
1945     }
1946     if(resB == NULL) {
1947         *status = U_ILLEGAL_ARGUMENT_ERROR;
1948         return NULL;
1949     }
1950 
1951     int32_t type = RES_GET_TYPE(resB->fRes);
1952     if(URES_IS_TABLE(type)) {
1953         int32_t t=0;
1954 
1955         res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);
1956 
1957         if(res == RES_BOGUS) {
1958             key = inKey;
1959             if(resB->fHasFallback == TRUE) {
1960                 const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
1961                 if(U_SUCCESS(*status)) {
1962                     switch (RES_GET_TYPE(res)) {
1963                     case URES_STRING:
1964                     case URES_STRING_V2:
1965                         return res_getString(rd, res, len);
1966                     case URES_ALIAS:
1967                       {
1968                         const UChar* result = 0;
1969                         UResourceBundle *tempRes = ures_getByKey(resB, inKey, NULL, status);
1970                         result = ures_getString(tempRes, len, status);
1971                         ures_close(tempRes);
1972                         return result;
1973                       }
1974                     default:
1975                         *status = U_RESOURCE_TYPE_MISMATCH;
1976                     }
1977                 } else {
1978                     *status = U_MISSING_RESOURCE_ERROR;
1979                 }
1980             } else {
1981                 *status = U_MISSING_RESOURCE_ERROR;
1982             }
1983         } else {
1984             switch (RES_GET_TYPE(res)) {
1985             case URES_STRING:
1986             case URES_STRING_V2:
1987                 return res_getString(&(resB->fResData), res, len);
1988             case URES_ALIAS:
1989               {
1990                 const UChar* result = 0;
1991                 UResourceBundle *tempRes = ures_getByKey(resB, inKey, NULL, status);
1992                 result = ures_getString(tempRes, len, status);
1993                 ures_close(tempRes);
1994                 return result;
1995               }
1996             default:
1997                 *status = U_RESOURCE_TYPE_MISMATCH;
1998             }
1999         }
2000     }
2001 #if 0
2002     /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2003     /* not currently */
2004     else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == TRUE) {
2005         /* here should go a first attempt to locate the key using index table */
2006         const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2007         if(U_SUCCESS(*status)) {
2008             return res_getString(rd, res, len);
2009         } else {
2010             *status = U_MISSING_RESOURCE_ERROR;
2011         }
2012     }
2013 #endif
2014     else {
2015         *status = U_RESOURCE_TYPE_MISMATCH;
2016     }
2017     return NULL;
2018 }
2019 
2020 U_CAPI const char * U_EXPORT2
ures_getUTF8StringByKey(const UResourceBundle * resB,const char * key,char * dest,int32_t * pLength,UBool forceCopy,UErrorCode * status)2021 ures_getUTF8StringByKey(const UResourceBundle *resB,
2022                         const char *key,
2023                         char *dest, int32_t *pLength,
2024                         UBool forceCopy,
2025                         UErrorCode *status) {
2026     int32_t length16;
2027     const UChar *s16 = ures_getStringByKey(resB, key, &length16, status);
2028     return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
2029 }
2030 
2031 /* TODO: clean from here down */
2032 
2033 /**
2034  *  INTERNAL: Get the name of the first real locale (not placeholder)
2035  *  that has resource bundle data.
2036  */
2037 U_INTERNAL const char*  U_EXPORT2
ures_getLocaleInternal(const UResourceBundle * resourceBundle,UErrorCode * status)2038 ures_getLocaleInternal(const UResourceBundle* resourceBundle, UErrorCode* status)
2039 {
2040     if (status==NULL || U_FAILURE(*status)) {
2041         return NULL;
2042     }
2043     if (!resourceBundle) {
2044         *status = U_ILLEGAL_ARGUMENT_ERROR;
2045         return NULL;
2046     } else {
2047       return resourceBundle->fData->fName;
2048     }
2049 }
2050 
2051 U_CAPI const char* U_EXPORT2
ures_getLocale(const UResourceBundle * resourceBundle,UErrorCode * status)2052 ures_getLocale(const UResourceBundle* resourceBundle,
2053                UErrorCode* status)
2054 {
2055   return ures_getLocaleInternal(resourceBundle, status);
2056 }
2057 
2058 
2059 U_CAPI const char* U_EXPORT2
ures_getLocaleByType(const UResourceBundle * resourceBundle,ULocDataLocaleType type,UErrorCode * status)2060 ures_getLocaleByType(const UResourceBundle* resourceBundle,
2061                      ULocDataLocaleType type,
2062                      UErrorCode* status) {
2063     if (status==NULL || U_FAILURE(*status)) {
2064         return NULL;
2065     }
2066     if (!resourceBundle) {
2067         *status = U_ILLEGAL_ARGUMENT_ERROR;
2068         return NULL;
2069     } else {
2070         switch(type) {
2071         case ULOC_ACTUAL_LOCALE:
2072             return resourceBundle->fData->fName;
2073         case ULOC_VALID_LOCALE:
2074             return resourceBundle->fTopLevelData->fName;
2075         case ULOC_REQUESTED_LOCALE:
2076         default:
2077             *status = U_ILLEGAL_ARGUMENT_ERROR;
2078             return NULL;
2079         }
2080     }
2081 }
2082 
ures_getName(const UResourceBundle * resB)2083 U_CFUNC const char* ures_getName(const UResourceBundle* resB) {
2084   if(resB == NULL) {
2085     return NULL;
2086   }
2087 
2088   return resB->fData->fName;
2089 }
2090 
2091 #ifdef URES_DEBUG
ures_getPath(const UResourceBundle * resB)2092 U_CFUNC const char* ures_getPath(const UResourceBundle* resB) {
2093   if(resB == NULL) {
2094     return NULL;
2095   }
2096 
2097   return resB->fData->fPath;
2098 }
2099 #endif
2100 
2101 static UResourceBundle*
ures_openWithType(UResourceBundle * r,const char * path,const char * localeID,UResOpenType openType,UErrorCode * status)2102 ures_openWithType(UResourceBundle *r, const char* path, const char* localeID,
2103                   UResOpenType openType, UErrorCode* status) {
2104     if(U_FAILURE(*status)) {
2105         return NULL;
2106     }
2107 
2108     UResourceDataEntry *entry;
2109     if(openType != URES_OPEN_DIRECT) {
2110         /* first "canonicalize" the locale ID */
2111         char canonLocaleID[ULOC_FULLNAME_CAPACITY];
2112         uloc_getBaseName(localeID, canonLocaleID, UPRV_LENGTHOF(canonLocaleID), status);
2113         if(U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) {
2114             *status = U_ILLEGAL_ARGUMENT_ERROR;
2115             return NULL;
2116         }
2117         entry = entryOpen(path, canonLocaleID, openType, status);
2118     } else {
2119         entry = entryOpenDirect(path, localeID, status);
2120     }
2121     if(U_FAILURE(*status)) {
2122         return NULL;
2123     }
2124     if(entry == NULL) {
2125         *status = U_MISSING_RESOURCE_ERROR;
2126         return NULL;
2127     }
2128 
2129     UBool isStackObject;
2130     if(r == NULL) {
2131         r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
2132         if(r == NULL) {
2133             entryClose(entry);
2134             *status = U_MEMORY_ALLOCATION_ERROR;
2135             return NULL;
2136         }
2137         isStackObject = FALSE;
2138     } else {  // fill-in
2139         isStackObject = ures_isStackObject(r);
2140         ures_closeBundle(r, FALSE);
2141     }
2142     uprv_memset(r, 0, sizeof(UResourceBundle));
2143     ures_setIsStackObject(r, isStackObject);
2144 
2145     r->fTopLevelData = r->fData = entry;
2146     uprv_memcpy(&r->fResData, &entry->fData, sizeof(ResourceData));
2147     r->fHasFallback = openType != URES_OPEN_DIRECT && !r->fResData.noFallback;
2148     r->fIsTopLevel = TRUE;
2149     r->fRes = r->fResData.rootRes;
2150     r->fSize = res_countArrayItems(&(r->fResData), r->fRes);
2151     r->fIndex = -1;
2152 
2153     return r;
2154 }
2155 
2156 U_CAPI UResourceBundle* U_EXPORT2
ures_open(const char * path,const char * localeID,UErrorCode * status)2157 ures_open(const char* path, const char* localeID, UErrorCode* status) {
2158     return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
2159 }
2160 
2161 U_CAPI UResourceBundle* U_EXPORT2
ures_openNoDefault(const char * path,const char * localeID,UErrorCode * status)2162 ures_openNoDefault(const char* path, const char* localeID, UErrorCode* status) {
2163     return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_ROOT, status);
2164 }
2165 
2166 /**
2167  *  Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
2168  *  or sought. However, alias substitution will happen!
2169  */
2170 U_CAPI UResourceBundle*  U_EXPORT2
ures_openDirect(const char * path,const char * localeID,UErrorCode * status)2171 ures_openDirect(const char* path, const char* localeID, UErrorCode* status) {
2172     return ures_openWithType(NULL, path, localeID, URES_OPEN_DIRECT, status);
2173 }
2174 
2175 /**
2176  *  API: This function is used to open a resource bundle
2177  *  proper fallback chaining is executed while initialization.
2178  *  The result is stored in cache for later fallback search.
2179  */
2180 U_CAPI void U_EXPORT2
ures_openFillIn(UResourceBundle * r,const char * path,const char * localeID,UErrorCode * status)2181 ures_openFillIn(UResourceBundle *r, const char* path,
2182                 const char* localeID, UErrorCode* status) {
2183     if(U_SUCCESS(*status) && r == NULL) {
2184         *status = U_ILLEGAL_ARGUMENT_ERROR;
2185         return;
2186     }
2187     ures_openWithType(r, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
2188 }
2189 
2190 /**
2191  *  API: Counts members. For arrays and tables, returns number of resources.
2192  *  For strings, returns 1.
2193  */
2194 U_CAPI int32_t  U_EXPORT2
ures_countArrayItems(const UResourceBundle * resourceBundle,const char * resourceKey,UErrorCode * status)2195 ures_countArrayItems(const UResourceBundle* resourceBundle,
2196                   const char* resourceKey,
2197                   UErrorCode* status)
2198 {
2199     UResourceBundle resData;
2200     ures_initStackObject(&resData);
2201     if (status==NULL || U_FAILURE(*status)) {
2202         return 0;
2203     }
2204     if(resourceBundle == NULL) {
2205         *status = U_ILLEGAL_ARGUMENT_ERROR;
2206         return 0;
2207     }
2208     ures_getByKey(resourceBundle, resourceKey, &resData, status);
2209 
2210     if(resData.fResData.data != NULL) {
2211         int32_t result = res_countArrayItems(&resData.fResData, resData.fRes);
2212         ures_close(&resData);
2213         return result;
2214     } else {
2215         *status = U_MISSING_RESOURCE_ERROR;
2216         ures_close(&resData);
2217         return 0;
2218     }
2219 }
2220 
2221 /**
2222  * Internal function.
2223  * Return the version number associated with this ResourceBundle as a string.
2224  *
2225  * @param resourceBundle The resource bundle for which the version is checked.
2226  * @return  A version number string as specified in the resource bundle or its parent.
2227  *          The caller does not own this string.
2228  * @see ures_getVersion
2229  * @internal
2230  */
2231 U_INTERNAL const char* U_EXPORT2
ures_getVersionNumberInternal(const UResourceBundle * resourceBundle)2232 ures_getVersionNumberInternal(const UResourceBundle *resourceBundle)
2233 {
2234     if (!resourceBundle) return NULL;
2235 
2236     if(resourceBundle->fVersion == NULL) {
2237 
2238         /* If the version ID has not been built yet, then do so.  Retrieve */
2239         /* the minor version from the file. */
2240         UErrorCode status = U_ZERO_ERROR;
2241         int32_t minor_len = 0;
2242         int32_t len;
2243 
2244         const UChar* minor_version = ures_getStringByKey(resourceBundle, kVersionTag, &minor_len, &status);
2245 
2246         /* Determine the length of of the final version string.  This is */
2247         /* the length of the major part + the length of the separator */
2248         /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2249         /* the end). */
2250 
2251         len = (minor_len > 0) ? minor_len : 1;
2252 
2253         /* Allocate the string, and build it up. */
2254         /* + 1 for zero byte */
2255 
2256 
2257         ((UResourceBundle *)resourceBundle)->fVersion = (char *)uprv_malloc(1 + len);
2258         /* Check for null pointer. */
2259         if (((UResourceBundle *)resourceBundle)->fVersion == NULL) {
2260             return NULL;
2261         }
2262 
2263         if(minor_len > 0) {
2264             u_UCharsToChars(minor_version, resourceBundle->fVersion , minor_len);
2265             resourceBundle->fVersion[len] =  '\0';
2266         }
2267         else {
2268             uprv_strcpy(resourceBundle->fVersion, kDefaultMinorVersion);
2269         }
2270     }
2271 
2272     return resourceBundle->fVersion;
2273 }
2274 
2275 U_CAPI const char*  U_EXPORT2
ures_getVersionNumber(const UResourceBundle * resourceBundle)2276 ures_getVersionNumber(const UResourceBundle*   resourceBundle)
2277 {
2278     return ures_getVersionNumberInternal(resourceBundle);
2279 }
2280 
ures_getVersion(const UResourceBundle * resB,UVersionInfo versionInfo)2281 U_CAPI void U_EXPORT2 ures_getVersion(const UResourceBundle* resB, UVersionInfo versionInfo) {
2282     if (!resB) return;
2283 
2284     u_versionFromString(versionInfo, ures_getVersionNumberInternal(resB));
2285 }
2286 
2287 /** Tree support functions *******************************/
2288 #define INDEX_LOCALE_NAME "res_index"
2289 #define INDEX_TAG         "InstalledLocales"
2290 #define DEFAULT_TAG       "default"
2291 
2292 #if defined(URES_TREE_DEBUG)
2293 #include <stdio.h>
2294 #endif
2295 
2296 typedef struct ULocalesContext {
2297     UResourceBundle installed;
2298     UResourceBundle curr;
2299 } ULocalesContext;
2300 
2301 static void U_CALLCONV
ures_loc_closeLocales(UEnumeration * enumerator)2302 ures_loc_closeLocales(UEnumeration *enumerator) {
2303     ULocalesContext *ctx = (ULocalesContext *)enumerator->context;
2304     ures_close(&ctx->curr);
2305     ures_close(&ctx->installed);
2306     uprv_free(ctx);
2307     uprv_free(enumerator);
2308 }
2309 
2310 static int32_t U_CALLCONV
ures_loc_countLocales(UEnumeration * en,UErrorCode *)2311 ures_loc_countLocales(UEnumeration *en, UErrorCode * /*status*/) {
2312     ULocalesContext *ctx = (ULocalesContext *)en->context;
2313     return ures_getSize(&ctx->installed);
2314 }
2315 
2316 static const char* U_CALLCONV
ures_loc_nextLocale(UEnumeration * en,int32_t * resultLength,UErrorCode * status)2317 ures_loc_nextLocale(UEnumeration* en,
2318                     int32_t* resultLength,
2319                     UErrorCode* status) {
2320     ULocalesContext *ctx = (ULocalesContext *)en->context;
2321     UResourceBundle *res = &(ctx->installed);
2322     UResourceBundle *k = NULL;
2323     const char *result = NULL;
2324     int32_t len = 0;
2325     if(ures_hasNext(res) && (k = ures_getNextResource(res, &ctx->curr, status))) {
2326         result = ures_getKey(k);
2327         len = (int32_t)uprv_strlen(result);
2328     }
2329     if (resultLength) {
2330         *resultLength = len;
2331     }
2332     return result;
2333 }
2334 
2335 static void U_CALLCONV
ures_loc_resetLocales(UEnumeration * en,UErrorCode *)2336 ures_loc_resetLocales(UEnumeration* en,
2337                       UErrorCode* /*status*/) {
2338     UResourceBundle *res = &((ULocalesContext *)en->context)->installed;
2339     ures_resetIterator(res);
2340 }
2341 
2342 
2343 static const UEnumeration gLocalesEnum = {
2344     NULL,
2345         NULL,
2346         ures_loc_closeLocales,
2347         ures_loc_countLocales,
2348         uenum_unextDefault,
2349         ures_loc_nextLocale,
2350         ures_loc_resetLocales
2351 };
2352 
2353 
2354 U_CAPI UEnumeration* U_EXPORT2
ures_openAvailableLocales(const char * path,UErrorCode * status)2355 ures_openAvailableLocales(const char *path, UErrorCode *status)
2356 {
2357     UResourceBundle *idx = NULL;
2358     UEnumeration *en = NULL;
2359     ULocalesContext *myContext = NULL;
2360 
2361     if(U_FAILURE(*status)) {
2362         return NULL;
2363     }
2364     myContext = static_cast<ULocalesContext *>(uprv_malloc(sizeof(ULocalesContext)));
2365     en =  (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
2366     if(!en || !myContext) {
2367         *status = U_MEMORY_ALLOCATION_ERROR;
2368         uprv_free(en);
2369         uprv_free(myContext);
2370         return NULL;
2371     }
2372     uprv_memcpy(en, &gLocalesEnum, sizeof(UEnumeration));
2373 
2374     ures_initStackObject(&myContext->installed);
2375     ures_initStackObject(&myContext->curr);
2376     idx = ures_openDirect(path, INDEX_LOCALE_NAME, status);
2377     ures_getByKey(idx, INDEX_TAG, &myContext->installed, status);
2378     if(U_SUCCESS(*status)) {
2379 #if defined(URES_TREE_DEBUG)
2380         fprintf(stderr, "Got %s::%s::[%s] : %s\n",
2381             path, INDEX_LOCALE_NAME, INDEX_TAG, ures_getKey(&myContext->installed));
2382 #endif
2383         en->context = myContext;
2384     } else {
2385 #if defined(URES_TREE_DEBUG)
2386         fprintf(stderr, "%s open failed - %s\n", path, u_errorName(*status));
2387 #endif
2388         ures_close(&myContext->installed);
2389         uprv_free(myContext);
2390         uprv_free(en);
2391         en = NULL;
2392     }
2393 
2394     ures_close(idx);
2395 
2396     return en;
2397 }
2398 
isLocaleInList(UEnumeration * locEnum,const char * locToSearch,UErrorCode * status)2399 static UBool isLocaleInList(UEnumeration *locEnum, const char *locToSearch, UErrorCode *status) {
2400     const char *loc;
2401     while ((loc = uenum_next(locEnum, NULL, status)) != NULL) {
2402         if (uprv_strcmp(loc, locToSearch) == 0) {
2403             return TRUE;
2404         }
2405     }
2406     return FALSE;
2407 }
2408 
2409 U_CAPI int32_t U_EXPORT2
ures_getFunctionalEquivalent(char * result,int32_t resultCapacity,const char * path,const char * resName,const char * keyword,const char * locid,UBool * isAvailable,UBool omitDefault,UErrorCode * status)2410 ures_getFunctionalEquivalent(char *result, int32_t resultCapacity,
2411                              const char *path, const char *resName, const char *keyword, const char *locid,
2412                              UBool *isAvailable, UBool omitDefault, UErrorCode *status)
2413 {
2414     char kwVal[1024] = ""; /* value of keyword 'keyword' */
2415     char defVal[1024] = ""; /* default value for given locale */
2416     char defLoc[1024] = ""; /* default value for given locale */
2417     char base[1024] = ""; /* base locale */
2418     char found[1024];
2419     char parent[1024];
2420     char full[1024] = "";
2421     UResourceBundle bund1, bund2;
2422     UResourceBundle *res = NULL;
2423     UErrorCode subStatus = U_ZERO_ERROR;
2424     int32_t length = 0;
2425     if(U_FAILURE(*status)) return 0;
2426     uloc_getKeywordValue(locid, keyword, kwVal, 1024-1,&subStatus);
2427     if(!uprv_strcmp(kwVal, DEFAULT_TAG)) {
2428         kwVal[0]=0;
2429     }
2430     uloc_getBaseName(locid, base, 1024-1,&subStatus);
2431 #if defined(URES_TREE_DEBUG)
2432     fprintf(stderr, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
2433             locid, keyword, kwVal, base, u_errorName(subStatus));
2434 #endif
2435     ures_initStackObject(&bund1);
2436     ures_initStackObject(&bund2);
2437 
2438 
2439     uprv_strcpy(parent, base);
2440     uprv_strcpy(found, base);
2441 
2442     if(isAvailable) {
2443         UEnumeration *locEnum = ures_openAvailableLocales(path, &subStatus);
2444         *isAvailable = TRUE;
2445         if (U_SUCCESS(subStatus)) {
2446             *isAvailable = isLocaleInList(locEnum, parent, &subStatus);
2447         }
2448         uenum_close(locEnum);
2449     }
2450 
2451     if(U_FAILURE(subStatus)) {
2452         *status = subStatus;
2453         return 0;
2454     }
2455 
2456     do {
2457         subStatus = U_ZERO_ERROR;
2458         res = ures_open(path, parent, &subStatus);
2459         if(((subStatus == U_USING_FALLBACK_WARNING) ||
2460             (subStatus == U_USING_DEFAULT_WARNING)) && isAvailable)
2461         {
2462             *isAvailable = FALSE;
2463         }
2464         isAvailable = NULL; /* only want to set this the first time around */
2465 
2466 #if defined(URES_TREE_DEBUG)
2467         fprintf(stderr, "%s;%s -> %s [%s]\n", path?path:"ICUDATA", parent, u_errorName(subStatus), ures_getLocale(res, &subStatus));
2468 #endif
2469         if(U_FAILURE(subStatus)) {
2470             *status = subStatus;
2471         } else if(subStatus == U_ZERO_ERROR) {
2472             ures_getByKey(res,resName,&bund1, &subStatus);
2473             if(subStatus == U_ZERO_ERROR) {
2474                 const UChar *defUstr;
2475                 int32_t defLen;
2476                 /* look for default item */
2477 #if defined(URES_TREE_DEBUG)
2478                 fprintf(stderr, "%s;%s : loaded default -> %s\n",
2479                     path?path:"ICUDATA", parent, u_errorName(subStatus));
2480 #endif
2481                 defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2482                 if(U_SUCCESS(subStatus) && defLen) {
2483                     u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2484 #if defined(URES_TREE_DEBUG)
2485                     fprintf(stderr, "%s;%s -> default %s=%s,  %s\n",
2486                         path?path:"ICUDATA", parent, keyword, defVal, u_errorName(subStatus));
2487 #endif
2488                     uprv_strcpy(defLoc, parent);
2489                     if(kwVal[0]==0) {
2490                         uprv_strcpy(kwVal, defVal);
2491 #if defined(URES_TREE_DEBUG)
2492                         fprintf(stderr, "%s;%s -> kwVal =  %s\n",
2493                             path?path:"ICUDATA", parent, keyword, kwVal);
2494 #endif
2495                     }
2496                 }
2497             }
2498         }
2499 
2500         subStatus = U_ZERO_ERROR;
2501 
2502         if (res != NULL) {
2503             uprv_strcpy(found, ures_getLocaleByType(res, ULOC_VALID_LOCALE, &subStatus));
2504         }
2505 
2506         uloc_getParent(found,parent,sizeof(parent),&subStatus);
2507         ures_close(res);
2508     } while(!defVal[0] && *found && uprv_strcmp(found, "root") != 0 && U_SUCCESS(*status));
2509 
2510     /* Now, see if we can find the kwVal collator.. start the search over.. */
2511     uprv_strcpy(parent, base);
2512     uprv_strcpy(found, base);
2513 
2514     do {
2515         subStatus = U_ZERO_ERROR;
2516         res = ures_open(path, parent, &subStatus);
2517         if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
2518             *isAvailable = FALSE;
2519         }
2520         isAvailable = NULL; /* only want to set this the first time around */
2521 
2522 #if defined(URES_TREE_DEBUG)
2523         fprintf(stderr, "%s;%s -> %s (looking for %s)\n",
2524             path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);
2525 #endif
2526         if(U_FAILURE(subStatus)) {
2527             *status = subStatus;
2528         } else if(subStatus == U_ZERO_ERROR) {
2529             ures_getByKey(res,resName,&bund1, &subStatus);
2530 #if defined(URES_TREE_DEBUG)
2531 /**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, resName, u_errorName(subStatus));
2532 #endif
2533             if(subStatus == U_ZERO_ERROR) {
2534                 ures_getByKey(&bund1, kwVal, &bund2, &subStatus);
2535 #if defined(URES_TREE_DEBUG)
2536 /**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, kwVal, u_errorName(subStatus));
2537 #endif
2538                 if(subStatus == U_ZERO_ERROR) {
2539 #if defined(URES_TREE_DEBUG)
2540                     fprintf(stderr, "%s;%s -> full0 %s=%s,  %s\n",
2541                         path?path:"ICUDATA", parent, keyword, kwVal, u_errorName(subStatus));
2542 #endif
2543                     uprv_strcpy(full, parent);
2544                     if(*full == 0) {
2545                         uprv_strcpy(full, "root");
2546                     }
2547                         /* now, recalculate default kw if need be */
2548                         if(uprv_strlen(defLoc) > uprv_strlen(full)) {
2549                           const UChar *defUstr;
2550                           int32_t defLen;
2551                           /* look for default item */
2552 #if defined(URES_TREE_DEBUG)
2553                             fprintf(stderr, "%s;%s -> recalculating Default0\n",
2554                                     path?path:"ICUDATA", full);
2555 #endif
2556                           defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2557                           if(U_SUCCESS(subStatus) && defLen) {
2558                             u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2559 #if defined(URES_TREE_DEBUG)
2560                             fprintf(stderr, "%s;%s -> default0 %s=%s,  %s\n",
2561                                     path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));
2562 #endif
2563                             uprv_strcpy(defLoc, full);
2564                           }
2565                         } /* end of recalculate default KW */
2566 #if defined(URES_TREE_DEBUG)
2567                         else {
2568                           fprintf(stderr, "No trim0,  %s <= %s\n", defLoc, full);
2569                         }
2570 #endif
2571                 } else {
2572 #if defined(URES_TREE_DEBUG)
2573                     fprintf(stderr, "err=%s in %s looking for %s\n",
2574                         u_errorName(subStatus), parent, kwVal);
2575 #endif
2576                 }
2577             }
2578         }
2579 
2580         subStatus = U_ZERO_ERROR;
2581 
2582         uprv_strcpy(found, parent);
2583         uloc_getParent(found,parent,1023,&subStatus);
2584         ures_close(res);
2585     } while(!full[0] && *found && U_SUCCESS(*status));
2586 
2587     if((full[0]==0) && uprv_strcmp(kwVal, defVal)) {
2588 #if defined(URES_TREE_DEBUG)
2589         fprintf(stderr, "Failed to locate kw %s - try default %s\n", kwVal, defVal);
2590 #endif
2591         uprv_strcpy(kwVal, defVal);
2592         uprv_strcpy(parent, base);
2593         uprv_strcpy(found, base);
2594 
2595         do { /* search for 'default' named item */
2596             subStatus = U_ZERO_ERROR;
2597             res = ures_open(path, parent, &subStatus);
2598             if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
2599                 *isAvailable = FALSE;
2600             }
2601             isAvailable = NULL; /* only want to set this the first time around */
2602 
2603 #if defined(URES_TREE_DEBUG)
2604             fprintf(stderr, "%s;%s -> %s (looking for default %s)\n",
2605                 path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);
2606 #endif
2607             if(U_FAILURE(subStatus)) {
2608                 *status = subStatus;
2609             } else if(subStatus == U_ZERO_ERROR) {
2610                 ures_getByKey(res,resName,&bund1, &subStatus);
2611                 if(subStatus == U_ZERO_ERROR) {
2612                     ures_getByKey(&bund1, kwVal, &bund2, &subStatus);
2613                     if(subStatus == U_ZERO_ERROR) {
2614 #if defined(URES_TREE_DEBUG)
2615                         fprintf(stderr, "%s;%s -> full1 %s=%s,  %s\n", path?path:"ICUDATA",
2616                             parent, keyword, kwVal, u_errorName(subStatus));
2617 #endif
2618                         uprv_strcpy(full, parent);
2619                         if(*full == 0) {
2620                             uprv_strcpy(full, "root");
2621                         }
2622 
2623                         /* now, recalculate default kw if need be */
2624                         if(uprv_strlen(defLoc) > uprv_strlen(full)) {
2625                           const UChar *defUstr;
2626                           int32_t defLen;
2627                           /* look for default item */
2628 #if defined(URES_TREE_DEBUG)
2629                             fprintf(stderr, "%s;%s -> recalculating Default1\n",
2630                                     path?path:"ICUDATA", full);
2631 #endif
2632                           defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2633                           if(U_SUCCESS(subStatus) && defLen) {
2634                             u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2635 #if defined(URES_TREE_DEBUG)
2636                             fprintf(stderr, "%s;%s -> default %s=%s,  %s\n",
2637                                     path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));
2638 #endif
2639                             uprv_strcpy(defLoc, full);
2640                           }
2641                         } /* end of recalculate default KW */
2642 #if defined(URES_TREE_DEBUG)
2643                         else {
2644                           fprintf(stderr, "No trim1,  %s <= %s\n", defLoc, full);
2645                         }
2646 #endif
2647                     }
2648                 }
2649             }
2650             subStatus = U_ZERO_ERROR;
2651 
2652             uprv_strcpy(found, parent);
2653             uloc_getParent(found,parent,1023,&subStatus);
2654             ures_close(res);
2655         } while(!full[0] && *found && U_SUCCESS(*status));
2656     }
2657 
2658     if(U_SUCCESS(*status)) {
2659         if(!full[0]) {
2660 #if defined(URES_TREE_DEBUG)
2661           fprintf(stderr, "Still could not load keyword %s=%s\n", keyword, kwVal);
2662 #endif
2663           *status = U_MISSING_RESOURCE_ERROR;
2664         } else if(omitDefault) {
2665 #if defined(URES_TREE_DEBUG)
2666           fprintf(stderr,"Trim? full=%s, defLoc=%s, found=%s\n", full, defLoc, found);
2667 #endif
2668           if(uprv_strlen(defLoc) <= uprv_strlen(full)) {
2669             /* found the keyword in a *child* of where the default tag was present. */
2670             if(!uprv_strcmp(kwVal, defVal)) { /* if the requested kw is default, */
2671               /* and the default is in or in an ancestor of the current locale */
2672 #if defined(URES_TREE_DEBUG)
2673               fprintf(stderr, "Removing unneeded var %s=%s\n", keyword, kwVal);
2674 #endif
2675               kwVal[0]=0;
2676             }
2677           }
2678         }
2679         uprv_strcpy(found, full);
2680         if(kwVal[0]) {
2681             uprv_strcat(found, "@");
2682             uprv_strcat(found, keyword);
2683             uprv_strcat(found, "=");
2684             uprv_strcat(found, kwVal);
2685         } else if(!omitDefault) {
2686             uprv_strcat(found, "@");
2687             uprv_strcat(found, keyword);
2688             uprv_strcat(found, "=");
2689             uprv_strcat(found, defVal);
2690         }
2691     }
2692     /* we found the default locale - no need to repeat it.*/
2693 
2694     ures_close(&bund1);
2695     ures_close(&bund2);
2696 
2697     length = (int32_t)uprv_strlen(found);
2698 
2699     if(U_SUCCESS(*status)) {
2700         int32_t copyLength = uprv_min(length, resultCapacity);
2701         if(copyLength>0) {
2702             uprv_strncpy(result, found, copyLength);
2703         }
2704         if(length == 0) {
2705           *status = U_MISSING_RESOURCE_ERROR;
2706         }
2707     } else {
2708         length = 0;
2709         result[0]=0;
2710     }
2711     return u_terminateChars(result, resultCapacity, length, status);
2712 }
2713 
2714 U_CAPI UEnumeration* U_EXPORT2
ures_getKeywordValues(const char * path,const char * keyword,UErrorCode * status)2715 ures_getKeywordValues(const char *path, const char *keyword, UErrorCode *status)
2716 {
2717 #define VALUES_BUF_SIZE 2048
2718 #define VALUES_LIST_SIZE 512
2719 
2720     char       valuesBuf[VALUES_BUF_SIZE];
2721     int32_t    valuesIndex = 0;
2722     const char *valuesList[VALUES_LIST_SIZE];
2723     int32_t    valuesCount = 0;
2724 
2725     const char *locale;
2726     int32_t     locLen;
2727 
2728     UEnumeration *locs = NULL;
2729 
2730     UResourceBundle    item;
2731     UResourceBundle    subItem;
2732 
2733     ures_initStackObject(&item);
2734     ures_initStackObject(&subItem);
2735     locs = ures_openAvailableLocales(path, status);
2736 
2737     if(U_FAILURE(*status)) {
2738         ures_close(&item);
2739         ures_close(&subItem);
2740         return NULL;
2741     }
2742 
2743     valuesBuf[0]=0;
2744     valuesBuf[1]=0;
2745 
2746     while((locale = uenum_next(locs, &locLen, status))) {
2747         UResourceBundle   *bund = NULL;
2748         UResourceBundle   *subPtr = NULL;
2749         UErrorCode subStatus = U_ZERO_ERROR; /* don't fail if a bundle is unopenable */
2750         bund = ures_openDirect(path, locale, &subStatus);
2751 
2752 #if defined(URES_TREE_DEBUG)
2753         if(!bund || U_FAILURE(subStatus)) {
2754             fprintf(stderr, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
2755                 path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));
2756         }
2757 #endif
2758 
2759         ures_getByKey(bund, keyword, &item, &subStatus);
2760 
2761         if(!bund || U_FAILURE(subStatus)) {
2762 #if defined(URES_TREE_DEBUG)
2763             fprintf(stderr, "%s-%s values: Can't find in %s - skipping. (%s)\n",
2764                 path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));
2765 #endif
2766             ures_close(bund);
2767             bund = NULL;
2768             continue;
2769         }
2770 
2771         while((subPtr = ures_getNextResource(&item,&subItem,&subStatus))
2772             && U_SUCCESS(subStatus)) {
2773             const char *k;
2774             int32_t i;
2775             k = ures_getKey(subPtr);
2776 
2777 #if defined(URES_TREE_DEBUG)
2778             /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2779 #endif
2780             if(k == NULL || *k == 0 ||
2781                     uprv_strcmp(k, DEFAULT_TAG) == 0 || uprv_strncmp(k, "private-", 8) == 0) {
2782                 // empty or "default" or unlisted type
2783                 continue;
2784             }
2785             for(i=0; i<valuesCount; i++) {
2786                 if(!uprv_strcmp(valuesList[i],k)) {
2787                     k = NULL; /* found duplicate */
2788                     break;
2789                 }
2790             }
2791             if(k != NULL) {
2792                 int32_t kLen = (int32_t)uprv_strlen(k);
2793                 if((valuesCount >= (VALUES_LIST_SIZE-1)) ||       /* no more space in list .. */
2794                     ((valuesIndex+kLen+1+1) >= VALUES_BUF_SIZE)) { /* no more space in buffer (string + 2 nulls) */
2795                     *status = U_ILLEGAL_ARGUMENT_ERROR; /* out of space.. */
2796                 } else {
2797                     uprv_strcpy(valuesBuf+valuesIndex, k);
2798                     valuesList[valuesCount++] = valuesBuf+valuesIndex;
2799                     valuesIndex += kLen;
2800 #if defined(URES_TREE_DEBUG)
2801                     fprintf(stderr, "%s | %s | %s | [%s]   (UNIQUE)\n",
2802                         path?path:"<ICUDATA>", keyword, locale, k);
2803 #endif
2804                     valuesBuf[valuesIndex++] = 0; /* terminate */
2805                 }
2806             }
2807         }
2808         ures_close(bund);
2809     }
2810     valuesBuf[valuesIndex++] = 0; /* terminate */
2811 
2812     ures_close(&item);
2813     ures_close(&subItem);
2814     uenum_close(locs);
2815 #if defined(URES_TREE_DEBUG)
2816     fprintf(stderr, "%s:  size %d, #%d\n", u_errorName(*status),
2817         valuesIndex, valuesCount);
2818 #endif
2819     return uloc_openKeywordList(valuesBuf, valuesIndex, status);
2820 }
2821 #if 0
2822 /* This code isn't needed, and given the documentation warnings the implementation is suspect */
2823 U_INTERNAL UBool U_EXPORT2
2824 ures_equal(const UResourceBundle* res1, const UResourceBundle* res2){
2825     if(res1==NULL || res2==NULL){
2826         return res1==res2; /* pointer comparision */
2827     }
2828     if(res1->fKey==NULL||  res2->fKey==NULL){
2829         return (res1->fKey==res2->fKey);
2830     }else{
2831         if(uprv_strcmp(res1->fKey, res2->fKey)!=0){
2832             return FALSE;
2833         }
2834     }
2835     if(uprv_strcmp(res1->fData->fName, res2->fData->fName)!=0){
2836         return FALSE;
2837     }
2838     if(res1->fData->fPath == NULL||  res2->fData->fPath==NULL){
2839         return (res1->fData->fPath == res2->fData->fPath);
2840     }else{
2841         if(uprv_strcmp(res1->fData->fPath, res2->fData->fPath)!=0){
2842             return FALSE;
2843         }
2844     }
2845     if(uprv_strcmp(res1->fData->fParent->fName, res2->fData->fParent->fName)!=0){
2846         return FALSE;
2847     }
2848     if(uprv_strcmp(res1->fData->fParent->fPath, res2->fData->fParent->fPath)!=0){
2849         return FALSE;
2850     }
2851     if(uprv_strncmp(res1->fResPath, res2->fResPath, res1->fResPathLen)!=0){
2852         return FALSE;
2853     }
2854     if(res1->fRes != res2->fRes){
2855         return FALSE;
2856     }
2857     return TRUE;
2858 }
2859 U_INTERNAL UResourceBundle* U_EXPORT2
2860 ures_clone(const UResourceBundle* res, UErrorCode* status){
2861     UResourceBundle* bundle = NULL;
2862     UResourceBundle* ret = NULL;
2863     if(U_FAILURE(*status) || res == NULL){
2864         return NULL;
2865     }
2866     bundle = ures_open(res->fData->fPath, res->fData->fName, status);
2867     if(res->fResPath!=NULL){
2868         ret = ures_findSubResource(bundle, res->fResPath, NULL, status);
2869         ures_close(bundle);
2870     }else{
2871         ret = bundle;
2872     }
2873     return ret;
2874 }
2875 U_INTERNAL const UResourceBundle* U_EXPORT2
2876 ures_getParentBundle(const UResourceBundle* res){
2877     if(res==NULL){
2878         return NULL;
2879     }
2880     return res->fParentRes;
2881 }
2882 #endif
2883 
2884 U_INTERNAL void U_EXPORT2
ures_getVersionByKey(const UResourceBundle * res,const char * key,UVersionInfo ver,UErrorCode * status)2885 ures_getVersionByKey(const UResourceBundle* res, const char *key, UVersionInfo ver, UErrorCode *status) {
2886   const UChar *str;
2887   int32_t len;
2888   str = ures_getStringByKey(res, key, &len, status);
2889   if(U_SUCCESS(*status)) {
2890     u_versionFromUString(ver, str);
2891   }
2892 }
2893 
2894 /* eof */
2895