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