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