1 /*
2 ******************************************************************************
3 * Copyright (C) 1997-2015, 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     default: /*fall through*/
1489       return NULL;
1490     }
1491   }
1492 
1493   return NULL;
1494 }
1495 
ures_getNextResource(UResourceBundle * resB,UResourceBundle * fillIn,UErrorCode * status)1496 U_CAPI UResourceBundle* U_EXPORT2 ures_getNextResource(UResourceBundle *resB, UResourceBundle *fillIn, UErrorCode *status) {
1497     const char *key = NULL;
1498     Resource r = RES_BOGUS;
1499 
1500     if (status==NULL || U_FAILURE(*status)) {
1501             /*return NULL;*/
1502             return fillIn;
1503     }
1504     if(resB == NULL) {
1505             *status = U_ILLEGAL_ARGUMENT_ERROR;
1506             /*return NULL;*/
1507             return fillIn;
1508     }
1509 
1510     if(resB->fIndex == resB->fSize-1) {
1511       *status = U_INDEX_OUTOFBOUNDS_ERROR;
1512       /*return NULL;*/
1513     } else {
1514         resB->fIndex++;
1515         switch(RES_GET_TYPE(resB->fRes)) {
1516         case URES_INT:
1517         case URES_BINARY:
1518         case URES_STRING:
1519         case URES_STRING_V2:
1520         case URES_INT_VECTOR:
1521             return ures_copyResb(fillIn, resB, status);
1522         case URES_TABLE:
1523         case URES_TABLE16:
1524         case URES_TABLE32:
1525             r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, resB->fIndex, &key);
1526             if(r == RES_BOGUS && resB->fHasFallback) {
1527                 /* TODO: do the fallback */
1528             }
1529             return init_resb_result(&(resB->fResData), r, key, resB->fIndex, resB->fData, resB, 0, fillIn, status);
1530         case URES_ARRAY:
1531         case URES_ARRAY16:
1532             r = res_getArrayItem(&(resB->fResData), resB->fRes, resB->fIndex);
1533             if(r == RES_BOGUS && resB->fHasFallback) {
1534                 /* TODO: do the fallback */
1535             }
1536             return init_resb_result(&(resB->fResData), r, key, resB->fIndex, resB->fData, resB, 0, fillIn, status);
1537         default:
1538             /*return NULL;*/
1539             return fillIn;
1540         }
1541     }
1542     /*return NULL;*/
1543     return fillIn;
1544 }
1545 
ures_getByIndex(const UResourceBundle * resB,int32_t indexR,UResourceBundle * fillIn,UErrorCode * status)1546 U_CAPI UResourceBundle* U_EXPORT2 ures_getByIndex(const UResourceBundle *resB, int32_t indexR, UResourceBundle *fillIn, UErrorCode *status) {
1547     const char* key = NULL;
1548     Resource r = RES_BOGUS;
1549 
1550     if (status==NULL || U_FAILURE(*status)) {
1551         /*return NULL;*/
1552         return fillIn;
1553     }
1554     if(resB == NULL) {
1555         *status = U_ILLEGAL_ARGUMENT_ERROR;
1556         /*return NULL;*/
1557         return fillIn;
1558     }
1559 
1560     if(indexR >= 0 && resB->fSize > indexR) {
1561         switch(RES_GET_TYPE(resB->fRes)) {
1562         case URES_INT:
1563         case URES_BINARY:
1564         case URES_STRING:
1565         case URES_STRING_V2:
1566         case URES_INT_VECTOR:
1567             return ures_copyResb(fillIn, resB, status);
1568         case URES_TABLE:
1569         case URES_TABLE16:
1570         case URES_TABLE32:
1571             r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, indexR, &key);
1572             if(r == RES_BOGUS && resB->fHasFallback) {
1573                 /* TODO: do the fallback */
1574             }
1575             return init_resb_result(&(resB->fResData), r, key, indexR, resB->fData, resB, 0, fillIn, status);
1576         case URES_ARRAY:
1577         case URES_ARRAY16:
1578             r = res_getArrayItem(&(resB->fResData), resB->fRes, indexR);
1579             if(r == RES_BOGUS && resB->fHasFallback) {
1580                 /* TODO: do the fallback */
1581             }
1582             return init_resb_result(&(resB->fResData), r, key, indexR, resB->fData, resB, 0, fillIn, status);
1583         default:
1584             /*return NULL;*/
1585             return fillIn;
1586         }
1587     } else {
1588         *status = U_MISSING_RESOURCE_ERROR;
1589     }
1590     /*return NULL;*/
1591     return fillIn;
1592 }
1593 
ures_getStringByIndex(const UResourceBundle * resB,int32_t indexS,int32_t * len,UErrorCode * status)1594 U_CAPI const UChar* U_EXPORT2 ures_getStringByIndex(const UResourceBundle *resB, int32_t indexS, int32_t* len, UErrorCode *status) {
1595     const char* key = NULL;
1596     Resource r = RES_BOGUS;
1597 
1598     if (status==NULL || U_FAILURE(*status)) {
1599         return NULL;
1600     }
1601     if(resB == NULL) {
1602         *status = U_ILLEGAL_ARGUMENT_ERROR;
1603         return NULL;
1604     }
1605 
1606     if(indexS >= 0 && resB->fSize > indexS) {
1607         switch(RES_GET_TYPE(resB->fRes)) {
1608         case URES_STRING:
1609         case URES_STRING_V2:
1610             return res_getString(&(resB->fResData), resB->fRes, len);
1611         case URES_TABLE:
1612         case URES_TABLE16:
1613         case URES_TABLE32:
1614             r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, indexS, &key);
1615             if(r == RES_BOGUS && resB->fHasFallback) {
1616                 /* TODO: do the fallback */
1617             }
1618             return ures_getStringWithAlias(resB, r, indexS, len, status);
1619         case URES_ARRAY:
1620         case URES_ARRAY16:
1621             r = res_getArrayItem(&(resB->fResData), resB->fRes, indexS);
1622             if(r == RES_BOGUS && resB->fHasFallback) {
1623                 /* TODO: do the fallback */
1624             }
1625             return ures_getStringWithAlias(resB, r, indexS, len, status);
1626         case URES_ALIAS:
1627             return ures_getStringWithAlias(resB, resB->fRes, indexS, len, status);
1628         case URES_INT:
1629         case URES_BINARY:
1630         case URES_INT_VECTOR:
1631             *status = U_RESOURCE_TYPE_MISMATCH;
1632             break;
1633         default:
1634           /* must not occur */
1635           *status = U_INTERNAL_PROGRAM_ERROR;
1636           break;
1637         }
1638     } else {
1639         *status = U_MISSING_RESOURCE_ERROR;
1640     }
1641     return NULL;
1642 }
1643 
1644 U_CAPI const char * U_EXPORT2
ures_getUTF8StringByIndex(const UResourceBundle * resB,int32_t idx,char * dest,int32_t * pLength,UBool forceCopy,UErrorCode * status)1645 ures_getUTF8StringByIndex(const UResourceBundle *resB,
1646                           int32_t idx,
1647                           char *dest, int32_t *pLength,
1648                           UBool forceCopy,
1649                           UErrorCode *status) {
1650     int32_t length16;
1651     const UChar *s16 = ures_getStringByIndex(resB, idx, &length16, status);
1652     return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
1653 }
1654 
1655 /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1656   return resB->fResPath;
1657 }*/
1658 
1659 U_CAPI UResourceBundle* U_EXPORT2
ures_findResource(const char * path,UResourceBundle * fillIn,UErrorCode * status)1660 ures_findResource(const char* path, UResourceBundle *fillIn, UErrorCode *status)
1661 {
1662   UResourceBundle *first = NULL;
1663   UResourceBundle *result = fillIn;
1664   char *packageName = NULL;
1665   char *pathToResource = NULL, *save = NULL;
1666   char *locale = NULL, *localeEnd = NULL;
1667   int32_t length;
1668 
1669   if(status == NULL || U_FAILURE(*status)) {
1670     return result;
1671   }
1672 
1673   length = (int32_t)(uprv_strlen(path)+1);
1674   save = pathToResource = (char *)uprv_malloc(length*sizeof(char));
1675   /* test for NULL */
1676   if(pathToResource == NULL) {
1677     *status = U_MEMORY_ALLOCATION_ERROR;
1678     return result;
1679   }
1680   uprv_memcpy(pathToResource, path, length);
1681 
1682   locale = pathToResource;
1683   if(*pathToResource == RES_PATH_SEPARATOR) { /* there is a path specification */
1684     pathToResource++;
1685     packageName = pathToResource;
1686     pathToResource = uprv_strchr(pathToResource, RES_PATH_SEPARATOR);
1687     if(pathToResource == NULL) {
1688       *status = U_ILLEGAL_ARGUMENT_ERROR;
1689     } else {
1690       *pathToResource = 0;
1691       locale = pathToResource+1;
1692     }
1693   }
1694 
1695   localeEnd = uprv_strchr(locale, RES_PATH_SEPARATOR);
1696   if(localeEnd != NULL) {
1697     *localeEnd = 0;
1698   }
1699 
1700   first = ures_open(packageName, locale, status);
1701 
1702   if(U_SUCCESS(*status)) {
1703     if(localeEnd) {
1704       result = ures_findSubResource(first, localeEnd+1, fillIn, status);
1705     } else {
1706       result = ures_copyResb(fillIn, first, status);
1707     }
1708     ures_close(first);
1709   }
1710   uprv_free(save);
1711   return result;
1712 }
1713 
1714 U_CAPI UResourceBundle* U_EXPORT2
ures_findSubResource(const UResourceBundle * resB,char * path,UResourceBundle * fillIn,UErrorCode * status)1715 ures_findSubResource(const UResourceBundle *resB, char* path, UResourceBundle *fillIn, UErrorCode *status)
1716 {
1717   Resource res = RES_BOGUS;
1718   UResourceBundle *result = fillIn;
1719   const char *key;
1720 
1721   if(status == NULL || U_FAILURE(*status)) {
1722     return result;
1723   }
1724 
1725   /* here we do looping and circular alias checking */
1726   /* this loop is here because aliasing is resolved on this level, not on res level */
1727   /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1728   do {
1729     res = res_findResource(&(resB->fResData), resB->fRes, &path, &key);
1730     if(res != RES_BOGUS) {
1731         result = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
1732         resB = result;
1733     } else {
1734         *status = U_MISSING_RESOURCE_ERROR;
1735         break;
1736     }
1737   } while(*path); /* there is more stuff in the path */
1738 
1739   return result;
1740 }
1741 U_INTERNAL const UChar* U_EXPORT2
ures_getStringByKeyWithFallback(const UResourceBundle * resB,const char * inKey,int32_t * len,UErrorCode * status)1742 ures_getStringByKeyWithFallback(const UResourceBundle *resB,
1743                                 const char* inKey,
1744                                 int32_t* len,
1745                                 UErrorCode *status) {
1746 
1747     UResourceBundle stack;
1748     const UChar* retVal = NULL;
1749     ures_initStackObject(&stack);
1750     ures_getByKeyWithFallback(resB, inKey, &stack, status);
1751     int32_t length;
1752     retVal = ures_getString(&stack, &length, status);
1753     ures_close(&stack);
1754     if (U_FAILURE(*status)) {
1755         return NULL;
1756     }
1757     if (length == 3 && retVal[0] == EMPTY_SET && retVal[1] == EMPTY_SET && retVal[2] == EMPTY_SET ) {
1758         retVal = NULL;
1759         length = 0;
1760         *status = U_MISSING_RESOURCE_ERROR;
1761     }
1762     if (len != NULL) {
1763         *len = length;
1764     }
1765     return retVal;
1766 }
1767 
1768 /*
1769   Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
1770 */
getTableItemByKeyPath(const ResourceData * pResData,Resource table,const char * key)1771 static Resource getTableItemByKeyPath(const ResourceData *pResData, Resource table, const char *key) {
1772   Resource resource = table;  /* The current resource */
1773   icu::CharString path;
1774   UErrorCode errorCode = U_ZERO_ERROR;
1775   path.append(key, errorCode);
1776   if (U_FAILURE(errorCode)) { return RES_BOGUS; }
1777   char *pathPart = path.data();  /* Path from current resource to desired resource */
1778   UResType type = (UResType)RES_GET_TYPE(resource);  /* the current resource type */
1779   while (*pathPart && resource != RES_BOGUS && URES_IS_CONTAINER(type)) {
1780     char *nextPathPart = uprv_strchr(pathPart, RES_PATH_SEPARATOR);
1781     if (nextPathPart != NULL) {
1782       *nextPathPart = 0;  /* Terminating null for this part of path. */
1783       nextPathPart++;
1784     } else {
1785       nextPathPart = uprv_strchr(pathPart, 0);
1786     }
1787     int32_t t;
1788     const char *pathP = pathPart;
1789     resource = res_getTableItemByKey(pResData, resource, &t, &pathP);
1790     type = (UResType)RES_GET_TYPE(resource);
1791     pathPart = nextPathPart;
1792   }
1793   if (*pathPart) {
1794     return RES_BOGUS;
1795   }
1796   return resource;
1797 }
1798 
1799 U_CAPI UResourceBundle* U_EXPORT2
ures_getByKeyWithFallback(const UResourceBundle * resB,const char * inKey,UResourceBundle * fillIn,UErrorCode * status)1800 ures_getByKeyWithFallback(const UResourceBundle *resB,
1801                           const char* inKey,
1802                           UResourceBundle *fillIn,
1803                           UErrorCode *status) {
1804     Resource res = RES_BOGUS, rootRes = RES_BOGUS;
1805     /*UResourceDataEntry *realData = NULL;*/
1806     UResourceBundle *helper = NULL;
1807 
1808     if (status==NULL || U_FAILURE(*status)) {
1809         return fillIn;
1810     }
1811     if(resB == NULL) {
1812         *status = U_ILLEGAL_ARGUMENT_ERROR;
1813         return fillIn;
1814     }
1815 
1816     int32_t type = RES_GET_TYPE(resB->fRes);
1817     if(URES_IS_TABLE(type)) {
1818         res = getTableItemByKeyPath(&(resB->fResData), resB->fRes, inKey);
1819         const char* key = inKey;
1820         if(res == RES_BOGUS) {
1821             UResourceDataEntry *dataEntry = resB->fData;
1822             CharString path;
1823             char *myPath = NULL;
1824             const char* resPath = resB->fResPath;
1825             int32_t len = resB->fResPathLen;
1826             while(res == RES_BOGUS && dataEntry->fParent != NULL) { /* Otherwise, we'll look in parents */
1827                 dataEntry = dataEntry->fParent;
1828                 rootRes = dataEntry->fData.rootRes;
1829 
1830                 if(dataEntry->fBogus == U_ZERO_ERROR) {
1831                     path.clear();
1832                     if (len > 0) {
1833                         path.append(resPath, len, *status);
1834                     }
1835                     path.append(inKey, *status);
1836                     if (U_FAILURE(*status)) {
1837                         ures_close(helper);
1838                         return fillIn;
1839                     }
1840                     myPath = path.data();
1841                     key = inKey;
1842                     do {
1843                         res = res_findResource(&(dataEntry->fData), rootRes, &myPath, &key);
1844                         if (RES_GET_TYPE(res) == URES_ALIAS && *myPath) {
1845                             /* We hit an alias, but we didn't finish following the path. */
1846                             helper = init_resb_result(&(dataEntry->fData), res, NULL, -1, dataEntry, resB, 0, helper, status);
1847                             /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1848                             if(helper) {
1849                               dataEntry = helper->fData;
1850                               rootRes = helper->fRes;
1851                               resPath = helper->fResPath;
1852                               len = helper->fResPathLen;
1853 
1854                             } else {
1855                               break;
1856                             }
1857                         }
1858                     } while(*myPath); /* Continue until the whole path is consumed */
1859                 }
1860             }
1861             /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1862             if(res != RES_BOGUS) {
1863               /* check if resB->fResPath gives the right name here */
1864                 if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) {
1865                     *status = U_USING_DEFAULT_WARNING;
1866                 } else {
1867                     *status = U_USING_FALLBACK_WARNING;
1868                 }
1869 
1870                 fillIn = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, fillIn, status);
1871             } else {
1872                 *status = U_MISSING_RESOURCE_ERROR;
1873             }
1874         } else {
1875             fillIn = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
1876         }
1877     }
1878     else {
1879         *status = U_RESOURCE_TYPE_MISMATCH;
1880     }
1881     ures_close(helper);
1882     return fillIn;
1883 }
1884 
1885 namespace {
1886 
getAllContainerItemsWithFallback(const UResourceBundle * bundle,ResourceDataValue & value,ResourceArraySink * arraySink,ResourceTableSink * tableSink,UErrorCode & errorCode)1887 void getAllContainerItemsWithFallback(
1888         const UResourceBundle *bundle, ResourceDataValue &value,
1889         ResourceArraySink *arraySink, ResourceTableSink *tableSink,
1890         UErrorCode &errorCode) {
1891     if (U_FAILURE(errorCode)) { return; }
1892     // We recursively enumerate child-first,
1893     // only storing parent items in the absence of child items.
1894     // We store a placeholder value for the no-fallback/no-inheritance marker
1895     // to prevent a parent item from being stored.
1896     //
1897     // It would be possible to recursively enumerate parent-first,
1898     // overriding parent items with child items.
1899     // When we see the no-fallback/no-inheritance marker,
1900     // then we would remove the parent's item.
1901     // We would deserialize parent values even though they are overridden in a child bundle.
1902     UResType expectedType = arraySink != NULL ? URES_ARRAY : URES_TABLE;
1903     if (ures_getType(bundle) == expectedType) {
1904         value.pResData = &bundle->fResData;
1905         if (arraySink != NULL) {
1906             ures_getAllArrayItems(&bundle->fResData, bundle->fRes, value, *arraySink, errorCode);
1907         } else /* tableSink != NULL */ {
1908             ures_getAllTableItems(&bundle->fResData, bundle->fRes, value, *tableSink, errorCode);
1909         }
1910     }
1911     UResourceDataEntry *entry = bundle->fData->fParent;
1912     if (entry != NULL && U_SUCCESS(entry->fBogus)) {
1913         // We might try to query the sink whether
1914         // any fallback from the parent bundle is still possible.
1915 
1916         // Turn the parent UResourceDataEntry into a UResourceBundle,
1917         // much like in ures_openWithType().
1918         // TODO: See if we can refactor ures_getByKeyWithFallback()
1919         // and pull out an inner function that takes and returns a UResourceDataEntry
1920         // so that we need not create UResourceBundle objects.
1921         UResourceBundle parentBundle;
1922         ures_initStackObject(&parentBundle);
1923         parentBundle.fTopLevelData = parentBundle.fData = entry;
1924         // TODO: What is the difference between bundle fData and fTopLevelData?
1925         uprv_memcpy(&parentBundle.fResData, &entry->fData, sizeof(ResourceData));
1926         // TODO: Try to replace bundle.fResData with just using bundle.fData->fData.
1927         parentBundle.fHasFallback = !parentBundle.fResData.noFallback;
1928         parentBundle.fIsTopLevel = TRUE;
1929         parentBundle.fRes = parentBundle.fResData.rootRes;
1930         parentBundle.fSize = res_countArrayItems(&(parentBundle.fResData), parentBundle.fRes);
1931         parentBundle.fIndex = -1;
1932         entryIncrease(entry);
1933 
1934         // Look up the container item in the parent bundle.
1935         UResourceBundle containerBundle;
1936         ures_initStackObject(&containerBundle);
1937         const UResourceBundle *rb;
1938         if (bundle->fResPath == NULL || *bundle->fResPath == 0) {
1939             rb = &parentBundle;
1940         } else {
1941             rb = ures_getByKeyWithFallback(&parentBundle, bundle->fResPath,
1942                                            &containerBundle, &errorCode);
1943         }
1944         if (U_SUCCESS(errorCode) && ures_getType(rb) == expectedType) {
1945             getAllContainerItemsWithFallback(rb, value,
1946                                              arraySink, tableSink, errorCode);
1947         }
1948         ures_close(&containerBundle);
1949         ures_close(&parentBundle);
1950     }
1951 }
1952 
getAllContainerItemsWithFallback(const UResourceBundle * bundle,const char * path,ResourceArraySink * arraySink,ResourceTableSink * tableSink,UErrorCode & errorCode)1953 void getAllContainerItemsWithFallback(
1954         const UResourceBundle *bundle, const char *path,
1955         ResourceArraySink *arraySink, ResourceTableSink *tableSink,
1956         UErrorCode &errorCode) {
1957     if (U_FAILURE(errorCode)) { return; }
1958     if (path == NULL) {
1959         errorCode = U_ILLEGAL_ARGUMENT_ERROR;
1960         return;
1961     }
1962     UResourceBundle stackBundle;
1963     ures_initStackObject(&stackBundle);
1964     const UResourceBundle *rb;
1965     if (*path == 0) {
1966         // empty path
1967         rb = bundle;
1968     } else {
1969         rb = ures_getByKeyWithFallback(bundle, path, &stackBundle, &errorCode);
1970         if (U_FAILURE(errorCode)) {
1971             ures_close(&stackBundle);
1972             return;
1973         }
1974     }
1975     UResType expectedType = arraySink != NULL ? URES_ARRAY : URES_TABLE;
1976     if (ures_getType(rb) != expectedType) {
1977         errorCode = U_RESOURCE_TYPE_MISMATCH;
1978         ures_close(&stackBundle);
1979         return;
1980     }
1981     // Get all table items with fallback.
1982     ResourceDataValue value;
1983     getAllContainerItemsWithFallback(rb, value, arraySink, tableSink, errorCode);
1984     ures_close(&stackBundle);
1985 }
1986 
1987 }  // namespace
1988 
1989 U_CAPI void U_EXPORT2
ures_getAllArrayItemsWithFallback(const UResourceBundle * bundle,const char * path,ResourceArraySink & sink,UErrorCode & errorCode)1990 ures_getAllArrayItemsWithFallback(const UResourceBundle *bundle, const char *path,
1991                                   ResourceArraySink &sink, UErrorCode &errorCode) {
1992     getAllContainerItemsWithFallback(bundle, path, &sink, NULL, errorCode);
1993 }
1994 
1995 U_CAPI void U_EXPORT2
ures_getAllTableItemsWithFallback(const UResourceBundle * bundle,const char * path,ResourceTableSink & sink,UErrorCode & errorCode)1996 ures_getAllTableItemsWithFallback(const UResourceBundle *bundle, const char *path,
1997                                   ResourceTableSink &sink, UErrorCode &errorCode) {
1998     getAllContainerItemsWithFallback(bundle, path, NULL, &sink, errorCode);
1999 }
2000 
ures_getByKey(const UResourceBundle * resB,const char * inKey,UResourceBundle * fillIn,UErrorCode * status)2001 U_CAPI UResourceBundle* U_EXPORT2 ures_getByKey(const UResourceBundle *resB, const char* inKey, UResourceBundle *fillIn, UErrorCode *status) {
2002     Resource res = RES_BOGUS;
2003     UResourceDataEntry *realData = NULL;
2004     const char *key = inKey;
2005 
2006     if (status==NULL || U_FAILURE(*status)) {
2007         return fillIn;
2008     }
2009     if(resB == NULL) {
2010         *status = U_ILLEGAL_ARGUMENT_ERROR;
2011         return fillIn;
2012     }
2013 
2014     int32_t type = RES_GET_TYPE(resB->fRes);
2015     if(URES_IS_TABLE(type)) {
2016         int32_t t;
2017         res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);
2018         if(res == RES_BOGUS) {
2019             key = inKey;
2020             if(resB->fHasFallback == TRUE) {
2021                 const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2022                 if(U_SUCCESS(*status)) {
2023                   /* check if resB->fResPath gives the right name here */
2024                     return init_resb_result(rd, res, key, -1, realData, resB, 0, fillIn, status);
2025                 } else {
2026                     *status = U_MISSING_RESOURCE_ERROR;
2027                 }
2028             } else {
2029                 *status = U_MISSING_RESOURCE_ERROR;
2030             }
2031         } else {
2032             return init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
2033         }
2034     }
2035 #if 0
2036     /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2037     /* not currently */
2038     else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == TRUE) {
2039         /* here should go a first attempt to locate the key using index table */
2040         const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2041         if(U_SUCCESS(*status)) {
2042             return init_resb_result(rd, res, key, realData, resB, fillIn, status);
2043         } else {
2044             *status = U_MISSING_RESOURCE_ERROR;
2045         }
2046     }
2047 #endif
2048     else {
2049         *status = U_RESOURCE_TYPE_MISMATCH;
2050     }
2051     return fillIn;
2052 }
2053 
ures_getStringByKey(const UResourceBundle * resB,const char * inKey,int32_t * len,UErrorCode * status)2054 U_CAPI const UChar* U_EXPORT2 ures_getStringByKey(const UResourceBundle *resB, const char* inKey, int32_t* len, UErrorCode *status) {
2055     Resource res = RES_BOGUS;
2056     UResourceDataEntry *realData = NULL;
2057     const char* key = inKey;
2058 
2059     if (status==NULL || U_FAILURE(*status)) {
2060         return NULL;
2061     }
2062     if(resB == NULL) {
2063         *status = U_ILLEGAL_ARGUMENT_ERROR;
2064         return NULL;
2065     }
2066 
2067     int32_t type = RES_GET_TYPE(resB->fRes);
2068     if(URES_IS_TABLE(type)) {
2069         int32_t t=0;
2070 
2071         res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);
2072 
2073         if(res == RES_BOGUS) {
2074             key = inKey;
2075             if(resB->fHasFallback == TRUE) {
2076                 const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2077                 if(U_SUCCESS(*status)) {
2078                     switch (RES_GET_TYPE(res)) {
2079                     case URES_STRING:
2080                     case URES_STRING_V2:
2081                         return res_getString(rd, res, len);
2082                     case URES_ALIAS:
2083                       {
2084                         const UChar* result = 0;
2085                         UResourceBundle *tempRes = ures_getByKey(resB, inKey, NULL, status);
2086                         result = ures_getString(tempRes, len, status);
2087                         ures_close(tempRes);
2088                         return result;
2089                       }
2090                     default:
2091                         *status = U_RESOURCE_TYPE_MISMATCH;
2092                     }
2093                 } else {
2094                     *status = U_MISSING_RESOURCE_ERROR;
2095                 }
2096             } else {
2097                 *status = U_MISSING_RESOURCE_ERROR;
2098             }
2099         } else {
2100             switch (RES_GET_TYPE(res)) {
2101             case URES_STRING:
2102             case URES_STRING_V2:
2103                 return res_getString(&(resB->fResData), res, len);
2104             case URES_ALIAS:
2105               {
2106                 const UChar* result = 0;
2107                 UResourceBundle *tempRes = ures_getByKey(resB, inKey, NULL, status);
2108                 result = ures_getString(tempRes, len, status);
2109                 ures_close(tempRes);
2110                 return result;
2111               }
2112             default:
2113                 *status = U_RESOURCE_TYPE_MISMATCH;
2114             }
2115         }
2116     }
2117 #if 0
2118     /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2119     /* not currently */
2120     else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == TRUE) {
2121         /* here should go a first attempt to locate the key using index table */
2122         const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2123         if(U_SUCCESS(*status)) {
2124             return res_getString(rd, res, len);
2125         } else {
2126             *status = U_MISSING_RESOURCE_ERROR;
2127         }
2128     }
2129 #endif
2130     else {
2131         *status = U_RESOURCE_TYPE_MISMATCH;
2132     }
2133     return NULL;
2134 }
2135 
2136 U_CAPI const char * U_EXPORT2
ures_getUTF8StringByKey(const UResourceBundle * resB,const char * key,char * dest,int32_t * pLength,UBool forceCopy,UErrorCode * status)2137 ures_getUTF8StringByKey(const UResourceBundle *resB,
2138                         const char *key,
2139                         char *dest, int32_t *pLength,
2140                         UBool forceCopy,
2141                         UErrorCode *status) {
2142     int32_t length16;
2143     const UChar *s16 = ures_getStringByKey(resB, key, &length16, status);
2144     return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
2145 }
2146 
2147 /* TODO: clean from here down */
2148 
2149 /**
2150  *  INTERNAL: Get the name of the first real locale (not placeholder)
2151  *  that has resource bundle data.
2152  */
2153 U_INTERNAL const char*  U_EXPORT2
ures_getLocaleInternal(const UResourceBundle * resourceBundle,UErrorCode * status)2154 ures_getLocaleInternal(const UResourceBundle* resourceBundle, UErrorCode* status)
2155 {
2156     if (status==NULL || U_FAILURE(*status)) {
2157         return NULL;
2158     }
2159     if (!resourceBundle) {
2160         *status = U_ILLEGAL_ARGUMENT_ERROR;
2161         return NULL;
2162     } else {
2163       return resourceBundle->fData->fName;
2164     }
2165 }
2166 
2167 U_CAPI const char* U_EXPORT2
ures_getLocale(const UResourceBundle * resourceBundle,UErrorCode * status)2168 ures_getLocale(const UResourceBundle* resourceBundle,
2169                UErrorCode* status)
2170 {
2171   return ures_getLocaleInternal(resourceBundle, status);
2172 }
2173 
2174 
2175 U_CAPI const char* U_EXPORT2
ures_getLocaleByType(const UResourceBundle * resourceBundle,ULocDataLocaleType type,UErrorCode * status)2176 ures_getLocaleByType(const UResourceBundle* resourceBundle,
2177                      ULocDataLocaleType type,
2178                      UErrorCode* status) {
2179     if (status==NULL || U_FAILURE(*status)) {
2180         return NULL;
2181     }
2182     if (!resourceBundle) {
2183         *status = U_ILLEGAL_ARGUMENT_ERROR;
2184         return NULL;
2185     } else {
2186         switch(type) {
2187         case ULOC_ACTUAL_LOCALE:
2188             return resourceBundle->fData->fName;
2189         case ULOC_VALID_LOCALE:
2190             return resourceBundle->fTopLevelData->fName;
2191         case ULOC_REQUESTED_LOCALE:
2192         default:
2193             *status = U_ILLEGAL_ARGUMENT_ERROR;
2194             return NULL;
2195         }
2196     }
2197 }
2198 
ures_getName(const UResourceBundle * resB)2199 U_CFUNC const char* ures_getName(const UResourceBundle* resB) {
2200   if(resB == NULL) {
2201     return NULL;
2202   }
2203 
2204   return resB->fData->fName;
2205 }
2206 
2207 #ifdef URES_DEBUG
ures_getPath(const UResourceBundle * resB)2208 U_CFUNC const char* ures_getPath(const UResourceBundle* resB) {
2209   if(resB == NULL) {
2210     return NULL;
2211   }
2212 
2213   return resB->fData->fPath;
2214 }
2215 #endif
2216 
2217 static UResourceBundle*
ures_openWithType(UResourceBundle * r,const char * path,const char * localeID,UResOpenType openType,UErrorCode * status)2218 ures_openWithType(UResourceBundle *r, const char* path, const char* localeID,
2219                   UResOpenType openType, UErrorCode* status) {
2220     if(U_FAILURE(*status)) {
2221         return NULL;
2222     }
2223 
2224     UResourceDataEntry *entry;
2225     if(openType != URES_OPEN_DIRECT) {
2226         /* first "canonicalize" the locale ID */
2227         char canonLocaleID[ULOC_FULLNAME_CAPACITY];
2228         uloc_getBaseName(localeID, canonLocaleID, UPRV_LENGTHOF(canonLocaleID), status);
2229         if(U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) {
2230             *status = U_ILLEGAL_ARGUMENT_ERROR;
2231             return NULL;
2232         }
2233         entry = entryOpen(path, canonLocaleID, openType, status);
2234     } else {
2235         entry = entryOpenDirect(path, localeID, status);
2236     }
2237     if(U_FAILURE(*status)) {
2238         return NULL;
2239     }
2240     if(entry == NULL) {
2241         *status = U_MISSING_RESOURCE_ERROR;
2242         return NULL;
2243     }
2244 
2245     UBool isStackObject;
2246     if(r == NULL) {
2247         r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
2248         if(r == NULL) {
2249             entryClose(entry);
2250             *status = U_MEMORY_ALLOCATION_ERROR;
2251             return NULL;
2252         }
2253         isStackObject = FALSE;
2254     } else {  // fill-in
2255         isStackObject = ures_isStackObject(r);
2256         ures_closeBundle(r, FALSE);
2257     }
2258     uprv_memset(r, 0, sizeof(UResourceBundle));
2259     ures_setIsStackObject(r, isStackObject);
2260 
2261     r->fTopLevelData = r->fData = entry;
2262     uprv_memcpy(&r->fResData, &entry->fData, sizeof(ResourceData));
2263     r->fHasFallback = openType != URES_OPEN_DIRECT && !r->fResData.noFallback;
2264     r->fIsTopLevel = TRUE;
2265     r->fRes = r->fResData.rootRes;
2266     r->fSize = res_countArrayItems(&(r->fResData), r->fRes);
2267     r->fIndex = -1;
2268 
2269     return r;
2270 }
2271 
2272 U_CAPI UResourceBundle* U_EXPORT2
ures_open(const char * path,const char * localeID,UErrorCode * status)2273 ures_open(const char* path, const char* localeID, UErrorCode* status) {
2274     return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
2275 }
2276 
2277 U_CAPI UResourceBundle* U_EXPORT2
ures_openNoDefault(const char * path,const char * localeID,UErrorCode * status)2278 ures_openNoDefault(const char* path, const char* localeID, UErrorCode* status) {
2279     return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_ROOT, status);
2280 }
2281 
2282 /**
2283  *  Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
2284  *  or sought. However, alias substitution will happen!
2285  */
2286 U_CAPI UResourceBundle*  U_EXPORT2
ures_openDirect(const char * path,const char * localeID,UErrorCode * status)2287 ures_openDirect(const char* path, const char* localeID, UErrorCode* status) {
2288     return ures_openWithType(NULL, path, localeID, URES_OPEN_DIRECT, status);
2289 }
2290 
2291 /**
2292  *  API: This function is used to open a resource bundle
2293  *  proper fallback chaining is executed while initialization.
2294  *  The result is stored in cache for later fallback search.
2295  */
2296 U_CAPI void U_EXPORT2
ures_openFillIn(UResourceBundle * r,const char * path,const char * localeID,UErrorCode * status)2297 ures_openFillIn(UResourceBundle *r, const char* path,
2298                 const char* localeID, UErrorCode* status) {
2299     if(U_SUCCESS(*status) && r == NULL) {
2300         *status = U_ILLEGAL_ARGUMENT_ERROR;
2301         return;
2302     }
2303     ures_openWithType(r, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
2304 }
2305 
2306 /**
2307  *  API: Counts members. For arrays and tables, returns number of resources.
2308  *  For strings, returns 1.
2309  */
2310 U_CAPI int32_t  U_EXPORT2
ures_countArrayItems(const UResourceBundle * resourceBundle,const char * resourceKey,UErrorCode * status)2311 ures_countArrayItems(const UResourceBundle* resourceBundle,
2312                   const char* resourceKey,
2313                   UErrorCode* status)
2314 {
2315     UResourceBundle resData;
2316     ures_initStackObject(&resData);
2317     if (status==NULL || U_FAILURE(*status)) {
2318         return 0;
2319     }
2320     if(resourceBundle == NULL) {
2321         *status = U_ILLEGAL_ARGUMENT_ERROR;
2322         return 0;
2323     }
2324     ures_getByKey(resourceBundle, resourceKey, &resData, status);
2325 
2326     if(resData.fResData.data != NULL) {
2327         int32_t result = res_countArrayItems(&resData.fResData, resData.fRes);
2328         ures_close(&resData);
2329         return result;
2330     } else {
2331         *status = U_MISSING_RESOURCE_ERROR;
2332         ures_close(&resData);
2333         return 0;
2334     }
2335 }
2336 
2337 /**
2338  * Internal function.
2339  * Return the version number associated with this ResourceBundle as a string.
2340  *
2341  * @param resourceBundle The resource bundle for which the version is checked.
2342  * @return  A version number string as specified in the resource bundle or its parent.
2343  *          The caller does not own this string.
2344  * @see ures_getVersion
2345  * @internal
2346  */
2347 U_INTERNAL const char* U_EXPORT2
ures_getVersionNumberInternal(const UResourceBundle * resourceBundle)2348 ures_getVersionNumberInternal(const UResourceBundle *resourceBundle)
2349 {
2350     if (!resourceBundle) return NULL;
2351 
2352     if(resourceBundle->fVersion == NULL) {
2353 
2354         /* If the version ID has not been built yet, then do so.  Retrieve */
2355         /* the minor version from the file. */
2356         UErrorCode status = U_ZERO_ERROR;
2357         int32_t minor_len = 0;
2358         int32_t len;
2359 
2360         const UChar* minor_version = ures_getStringByKey(resourceBundle, kVersionTag, &minor_len, &status);
2361 
2362         /* Determine the length of of the final version string.  This is */
2363         /* the length of the major part + the length of the separator */
2364         /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2365         /* the end). */
2366 
2367         len = (minor_len > 0) ? minor_len : 1;
2368 
2369         /* Allocate the string, and build it up. */
2370         /* + 1 for zero byte */
2371 
2372 
2373         ((UResourceBundle *)resourceBundle)->fVersion = (char *)uprv_malloc(1 + len);
2374         /* Check for null pointer. */
2375         if (((UResourceBundle *)resourceBundle)->fVersion == NULL) {
2376             return NULL;
2377         }
2378 
2379         if(minor_len > 0) {
2380             u_UCharsToChars(minor_version, resourceBundle->fVersion , minor_len);
2381             resourceBundle->fVersion[len] =  '\0';
2382         }
2383         else {
2384             uprv_strcpy(resourceBundle->fVersion, kDefaultMinorVersion);
2385         }
2386     }
2387 
2388     return resourceBundle->fVersion;
2389 }
2390 
2391 U_CAPI const char*  U_EXPORT2
ures_getVersionNumber(const UResourceBundle * resourceBundle)2392 ures_getVersionNumber(const UResourceBundle*   resourceBundle)
2393 {
2394     return ures_getVersionNumberInternal(resourceBundle);
2395 }
2396 
ures_getVersion(const UResourceBundle * resB,UVersionInfo versionInfo)2397 U_CAPI void U_EXPORT2 ures_getVersion(const UResourceBundle* resB, UVersionInfo versionInfo) {
2398     if (!resB) return;
2399 
2400     u_versionFromString(versionInfo, ures_getVersionNumberInternal(resB));
2401 }
2402 
2403 /** Tree support functions *******************************/
2404 #define INDEX_LOCALE_NAME "res_index"
2405 #define INDEX_TAG         "InstalledLocales"
2406 #define DEFAULT_TAG       "default"
2407 
2408 #if defined(URES_TREE_DEBUG)
2409 #include <stdio.h>
2410 #endif
2411 
2412 typedef struct ULocalesContext {
2413     UResourceBundle installed;
2414     UResourceBundle curr;
2415 } ULocalesContext;
2416 
2417 static void U_CALLCONV
ures_loc_closeLocales(UEnumeration * enumerator)2418 ures_loc_closeLocales(UEnumeration *enumerator) {
2419     ULocalesContext *ctx = (ULocalesContext *)enumerator->context;
2420     ures_close(&ctx->curr);
2421     ures_close(&ctx->installed);
2422     uprv_free(ctx);
2423     uprv_free(enumerator);
2424 }
2425 
2426 static int32_t U_CALLCONV
ures_loc_countLocales(UEnumeration * en,UErrorCode *)2427 ures_loc_countLocales(UEnumeration *en, UErrorCode * /*status*/) {
2428     ULocalesContext *ctx = (ULocalesContext *)en->context;
2429     return ures_getSize(&ctx->installed);
2430 }
2431 
2432 static const char* U_CALLCONV
ures_loc_nextLocale(UEnumeration * en,int32_t * resultLength,UErrorCode * status)2433 ures_loc_nextLocale(UEnumeration* en,
2434                     int32_t* resultLength,
2435                     UErrorCode* status) {
2436     ULocalesContext *ctx = (ULocalesContext *)en->context;
2437     UResourceBundle *res = &(ctx->installed);
2438     UResourceBundle *k = NULL;
2439     const char *result = NULL;
2440     int32_t len = 0;
2441     if(ures_hasNext(res) && (k = ures_getNextResource(res, &ctx->curr, status))) {
2442         result = ures_getKey(k);
2443         len = (int32_t)uprv_strlen(result);
2444     }
2445     if (resultLength) {
2446         *resultLength = len;
2447     }
2448     return result;
2449 }
2450 
2451 static void U_CALLCONV
ures_loc_resetLocales(UEnumeration * en,UErrorCode *)2452 ures_loc_resetLocales(UEnumeration* en,
2453                       UErrorCode* /*status*/) {
2454     UResourceBundle *res = &((ULocalesContext *)en->context)->installed;
2455     ures_resetIterator(res);
2456 }
2457 
2458 
2459 static const UEnumeration gLocalesEnum = {
2460     NULL,
2461         NULL,
2462         ures_loc_closeLocales,
2463         ures_loc_countLocales,
2464         uenum_unextDefault,
2465         ures_loc_nextLocale,
2466         ures_loc_resetLocales
2467 };
2468 
2469 
2470 U_CAPI UEnumeration* U_EXPORT2
ures_openAvailableLocales(const char * path,UErrorCode * status)2471 ures_openAvailableLocales(const char *path, UErrorCode *status)
2472 {
2473     UResourceBundle *idx = NULL;
2474     UEnumeration *en = NULL;
2475     ULocalesContext *myContext = NULL;
2476 
2477     if(U_FAILURE(*status)) {
2478         return NULL;
2479     }
2480     myContext = static_cast<ULocalesContext *>(uprv_malloc(sizeof(ULocalesContext)));
2481     en =  (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
2482     if(!en || !myContext) {
2483         *status = U_MEMORY_ALLOCATION_ERROR;
2484         uprv_free(en);
2485         uprv_free(myContext);
2486         return NULL;
2487     }
2488     uprv_memcpy(en, &gLocalesEnum, sizeof(UEnumeration));
2489 
2490     ures_initStackObject(&myContext->installed);
2491     ures_initStackObject(&myContext->curr);
2492     idx = ures_openDirect(path, INDEX_LOCALE_NAME, status);
2493     ures_getByKey(idx, INDEX_TAG, &myContext->installed, status);
2494     if(U_SUCCESS(*status)) {
2495 #if defined(URES_TREE_DEBUG)
2496         fprintf(stderr, "Got %s::%s::[%s] : %s\n",
2497             path, INDEX_LOCALE_NAME, INDEX_TAG, ures_getKey(&myContext->installed));
2498 #endif
2499         en->context = myContext;
2500     } else {
2501 #if defined(URES_TREE_DEBUG)
2502         fprintf(stderr, "%s open failed - %s\n", path, u_errorName(*status));
2503 #endif
2504         ures_close(&myContext->installed);
2505         uprv_free(myContext);
2506         uprv_free(en);
2507         en = NULL;
2508     }
2509 
2510     ures_close(idx);
2511 
2512     return en;
2513 }
2514 
isLocaleInList(UEnumeration * locEnum,const char * locToSearch,UErrorCode * status)2515 static UBool isLocaleInList(UEnumeration *locEnum, const char *locToSearch, UErrorCode *status) {
2516     const char *loc;
2517     while ((loc = uenum_next(locEnum, NULL, status)) != NULL) {
2518         if (uprv_strcmp(loc, locToSearch) == 0) {
2519             return TRUE;
2520         }
2521     }
2522     return FALSE;
2523 }
2524 
2525 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)2526 ures_getFunctionalEquivalent(char *result, int32_t resultCapacity,
2527                              const char *path, const char *resName, const char *keyword, const char *locid,
2528                              UBool *isAvailable, UBool omitDefault, UErrorCode *status)
2529 {
2530     char kwVal[1024] = ""; /* value of keyword 'keyword' */
2531     char defVal[1024] = ""; /* default value for given locale */
2532     char defLoc[1024] = ""; /* default value for given locale */
2533     char base[1024] = ""; /* base locale */
2534     char found[1024];
2535     char parent[1024];
2536     char full[1024] = "";
2537     UResourceBundle bund1, bund2;
2538     UResourceBundle *res = NULL;
2539     UErrorCode subStatus = U_ZERO_ERROR;
2540     int32_t length = 0;
2541     if(U_FAILURE(*status)) return 0;
2542     uloc_getKeywordValue(locid, keyword, kwVal, 1024-1,&subStatus);
2543     if(!uprv_strcmp(kwVal, DEFAULT_TAG)) {
2544         kwVal[0]=0;
2545     }
2546     uloc_getBaseName(locid, base, 1024-1,&subStatus);
2547 #if defined(URES_TREE_DEBUG)
2548     fprintf(stderr, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
2549             locid, keyword, kwVal, base, u_errorName(subStatus));
2550 #endif
2551     ures_initStackObject(&bund1);
2552     ures_initStackObject(&bund2);
2553 
2554 
2555     uprv_strcpy(parent, base);
2556     uprv_strcpy(found, base);
2557 
2558     if(isAvailable) {
2559         UEnumeration *locEnum = ures_openAvailableLocales(path, &subStatus);
2560         *isAvailable = TRUE;
2561         if (U_SUCCESS(subStatus)) {
2562             *isAvailable = isLocaleInList(locEnum, parent, &subStatus);
2563         }
2564         uenum_close(locEnum);
2565     }
2566 
2567     if(U_FAILURE(subStatus)) {
2568         *status = subStatus;
2569         return 0;
2570     }
2571 
2572     do {
2573         subStatus = U_ZERO_ERROR;
2574         res = ures_open(path, parent, &subStatus);
2575         if(((subStatus == U_USING_FALLBACK_WARNING) ||
2576             (subStatus == U_USING_DEFAULT_WARNING)) && isAvailable)
2577         {
2578             *isAvailable = FALSE;
2579         }
2580         isAvailable = NULL; /* only want to set this the first time around */
2581 
2582 #if defined(URES_TREE_DEBUG)
2583         fprintf(stderr, "%s;%s -> %s [%s]\n", path?path:"ICUDATA", parent, u_errorName(subStatus), ures_getLocale(res, &subStatus));
2584 #endif
2585         if(U_FAILURE(subStatus)) {
2586             *status = subStatus;
2587         } else if(subStatus == U_ZERO_ERROR) {
2588             ures_getByKey(res,resName,&bund1, &subStatus);
2589             if(subStatus == U_ZERO_ERROR) {
2590                 const UChar *defUstr;
2591                 int32_t defLen;
2592                 /* look for default item */
2593 #if defined(URES_TREE_DEBUG)
2594                 fprintf(stderr, "%s;%s : loaded default -> %s\n",
2595                     path?path:"ICUDATA", parent, u_errorName(subStatus));
2596 #endif
2597                 defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2598                 if(U_SUCCESS(subStatus) && defLen) {
2599                     u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2600 #if defined(URES_TREE_DEBUG)
2601                     fprintf(stderr, "%s;%s -> default %s=%s,  %s\n",
2602                         path?path:"ICUDATA", parent, keyword, defVal, u_errorName(subStatus));
2603 #endif
2604                     uprv_strcpy(defLoc, parent);
2605                     if(kwVal[0]==0) {
2606                         uprv_strcpy(kwVal, defVal);
2607 #if defined(URES_TREE_DEBUG)
2608                         fprintf(stderr, "%s;%s -> kwVal =  %s\n",
2609                             path?path:"ICUDATA", parent, keyword, kwVal);
2610 #endif
2611                     }
2612                 }
2613             }
2614         }
2615 
2616         subStatus = U_ZERO_ERROR;
2617 
2618         if (res != NULL) {
2619             uprv_strcpy(found, ures_getLocaleByType(res, ULOC_VALID_LOCALE, &subStatus));
2620         }
2621 
2622         uloc_getParent(found,parent,sizeof(parent),&subStatus);
2623         ures_close(res);
2624     } while(!defVal[0] && *found && uprv_strcmp(found, "root") != 0 && U_SUCCESS(*status));
2625 
2626     /* Now, see if we can find the kwVal collator.. start the search over.. */
2627     uprv_strcpy(parent, base);
2628     uprv_strcpy(found, base);
2629 
2630     do {
2631         subStatus = U_ZERO_ERROR;
2632         res = ures_open(path, parent, &subStatus);
2633         if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
2634             *isAvailable = FALSE;
2635         }
2636         isAvailable = NULL; /* only want to set this the first time around */
2637 
2638 #if defined(URES_TREE_DEBUG)
2639         fprintf(stderr, "%s;%s -> %s (looking for %s)\n",
2640             path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);
2641 #endif
2642         if(U_FAILURE(subStatus)) {
2643             *status = subStatus;
2644         } else if(subStatus == U_ZERO_ERROR) {
2645             ures_getByKey(res,resName,&bund1, &subStatus);
2646 #if defined(URES_TREE_DEBUG)
2647 /**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, resName, u_errorName(subStatus));
2648 #endif
2649             if(subStatus == U_ZERO_ERROR) {
2650                 ures_getByKey(&bund1, kwVal, &bund2, &subStatus);
2651 #if defined(URES_TREE_DEBUG)
2652 /**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, kwVal, u_errorName(subStatus));
2653 #endif
2654                 if(subStatus == U_ZERO_ERROR) {
2655 #if defined(URES_TREE_DEBUG)
2656                     fprintf(stderr, "%s;%s -> full0 %s=%s,  %s\n",
2657                         path?path:"ICUDATA", parent, keyword, kwVal, u_errorName(subStatus));
2658 #endif
2659                     uprv_strcpy(full, parent);
2660                     if(*full == 0) {
2661                         uprv_strcpy(full, "root");
2662                     }
2663                         /* now, recalculate default kw if need be */
2664                         if(uprv_strlen(defLoc) > uprv_strlen(full)) {
2665                           const UChar *defUstr;
2666                           int32_t defLen;
2667                           /* look for default item */
2668 #if defined(URES_TREE_DEBUG)
2669                             fprintf(stderr, "%s;%s -> recalculating Default0\n",
2670                                     path?path:"ICUDATA", full);
2671 #endif
2672                           defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2673                           if(U_SUCCESS(subStatus) && defLen) {
2674                             u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2675 #if defined(URES_TREE_DEBUG)
2676                             fprintf(stderr, "%s;%s -> default0 %s=%s,  %s\n",
2677                                     path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));
2678 #endif
2679                             uprv_strcpy(defLoc, full);
2680                           }
2681                         } /* end of recalculate default KW */
2682 #if defined(URES_TREE_DEBUG)
2683                         else {
2684                           fprintf(stderr, "No trim0,  %s <= %s\n", defLoc, full);
2685                         }
2686 #endif
2687                 } else {
2688 #if defined(URES_TREE_DEBUG)
2689                     fprintf(stderr, "err=%s in %s looking for %s\n",
2690                         u_errorName(subStatus), parent, kwVal);
2691 #endif
2692                 }
2693             }
2694         }
2695 
2696         subStatus = U_ZERO_ERROR;
2697 
2698         uprv_strcpy(found, parent);
2699         uloc_getParent(found,parent,1023,&subStatus);
2700         ures_close(res);
2701     } while(!full[0] && *found && U_SUCCESS(*status));
2702 
2703     if((full[0]==0) && uprv_strcmp(kwVal, defVal)) {
2704 #if defined(URES_TREE_DEBUG)
2705         fprintf(stderr, "Failed to locate kw %s - try default %s\n", kwVal, defVal);
2706 #endif
2707         uprv_strcpy(kwVal, defVal);
2708         uprv_strcpy(parent, base);
2709         uprv_strcpy(found, base);
2710 
2711         do { /* search for 'default' named item */
2712             subStatus = U_ZERO_ERROR;
2713             res = ures_open(path, parent, &subStatus);
2714             if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
2715                 *isAvailable = FALSE;
2716             }
2717             isAvailable = NULL; /* only want to set this the first time around */
2718 
2719 #if defined(URES_TREE_DEBUG)
2720             fprintf(stderr, "%s;%s -> %s (looking for default %s)\n",
2721                 path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);
2722 #endif
2723             if(U_FAILURE(subStatus)) {
2724                 *status = subStatus;
2725             } else if(subStatus == U_ZERO_ERROR) {
2726                 ures_getByKey(res,resName,&bund1, &subStatus);
2727                 if(subStatus == U_ZERO_ERROR) {
2728                     ures_getByKey(&bund1, kwVal, &bund2, &subStatus);
2729                     if(subStatus == U_ZERO_ERROR) {
2730 #if defined(URES_TREE_DEBUG)
2731                         fprintf(stderr, "%s;%s -> full1 %s=%s,  %s\n", path?path:"ICUDATA",
2732                             parent, keyword, kwVal, u_errorName(subStatus));
2733 #endif
2734                         uprv_strcpy(full, parent);
2735                         if(*full == 0) {
2736                             uprv_strcpy(full, "root");
2737                         }
2738 
2739                         /* now, recalculate default kw if need be */
2740                         if(uprv_strlen(defLoc) > uprv_strlen(full)) {
2741                           const UChar *defUstr;
2742                           int32_t defLen;
2743                           /* look for default item */
2744 #if defined(URES_TREE_DEBUG)
2745                             fprintf(stderr, "%s;%s -> recalculating Default1\n",
2746                                     path?path:"ICUDATA", full);
2747 #endif
2748                           defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2749                           if(U_SUCCESS(subStatus) && defLen) {
2750                             u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2751 #if defined(URES_TREE_DEBUG)
2752                             fprintf(stderr, "%s;%s -> default %s=%s,  %s\n",
2753                                     path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));
2754 #endif
2755                             uprv_strcpy(defLoc, full);
2756                           }
2757                         } /* end of recalculate default KW */
2758 #if defined(URES_TREE_DEBUG)
2759                         else {
2760                           fprintf(stderr, "No trim1,  %s <= %s\n", defLoc, full);
2761                         }
2762 #endif
2763                     }
2764                 }
2765             }
2766             subStatus = U_ZERO_ERROR;
2767 
2768             uprv_strcpy(found, parent);
2769             uloc_getParent(found,parent,1023,&subStatus);
2770             ures_close(res);
2771         } while(!full[0] && *found && U_SUCCESS(*status));
2772     }
2773 
2774     if(U_SUCCESS(*status)) {
2775         if(!full[0]) {
2776 #if defined(URES_TREE_DEBUG)
2777           fprintf(stderr, "Still could not load keyword %s=%s\n", keyword, kwVal);
2778 #endif
2779           *status = U_MISSING_RESOURCE_ERROR;
2780         } else if(omitDefault) {
2781 #if defined(URES_TREE_DEBUG)
2782           fprintf(stderr,"Trim? full=%s, defLoc=%s, found=%s\n", full, defLoc, found);
2783 #endif
2784           if(uprv_strlen(defLoc) <= uprv_strlen(full)) {
2785             /* found the keyword in a *child* of where the default tag was present. */
2786             if(!uprv_strcmp(kwVal, defVal)) { /* if the requested kw is default, */
2787               /* and the default is in or in an ancestor of the current locale */
2788 #if defined(URES_TREE_DEBUG)
2789               fprintf(stderr, "Removing unneeded var %s=%s\n", keyword, kwVal);
2790 #endif
2791               kwVal[0]=0;
2792             }
2793           }
2794         }
2795         uprv_strcpy(found, full);
2796         if(kwVal[0]) {
2797             uprv_strcat(found, "@");
2798             uprv_strcat(found, keyword);
2799             uprv_strcat(found, "=");
2800             uprv_strcat(found, kwVal);
2801         } else if(!omitDefault) {
2802             uprv_strcat(found, "@");
2803             uprv_strcat(found, keyword);
2804             uprv_strcat(found, "=");
2805             uprv_strcat(found, defVal);
2806         }
2807     }
2808     /* we found the default locale - no need to repeat it.*/
2809 
2810     ures_close(&bund1);
2811     ures_close(&bund2);
2812 
2813     length = (int32_t)uprv_strlen(found);
2814 
2815     if(U_SUCCESS(*status)) {
2816         int32_t copyLength = uprv_min(length, resultCapacity);
2817         if(copyLength>0) {
2818             uprv_strncpy(result, found, copyLength);
2819         }
2820         if(length == 0) {
2821           *status = U_MISSING_RESOURCE_ERROR;
2822         }
2823     } else {
2824         length = 0;
2825         result[0]=0;
2826     }
2827     return u_terminateChars(result, resultCapacity, length, status);
2828 }
2829 
2830 U_CAPI UEnumeration* U_EXPORT2
ures_getKeywordValues(const char * path,const char * keyword,UErrorCode * status)2831 ures_getKeywordValues(const char *path, const char *keyword, UErrorCode *status)
2832 {
2833 #define VALUES_BUF_SIZE 2048
2834 #define VALUES_LIST_SIZE 512
2835 
2836     char       valuesBuf[VALUES_BUF_SIZE];
2837     int32_t    valuesIndex = 0;
2838     const char *valuesList[VALUES_LIST_SIZE];
2839     int32_t    valuesCount = 0;
2840 
2841     const char *locale;
2842     int32_t     locLen;
2843 
2844     UEnumeration *locs = NULL;
2845 
2846     UResourceBundle    item;
2847     UResourceBundle    subItem;
2848 
2849     ures_initStackObject(&item);
2850     ures_initStackObject(&subItem);
2851     locs = ures_openAvailableLocales(path, status);
2852 
2853     if(U_FAILURE(*status)) {
2854         ures_close(&item);
2855         ures_close(&subItem);
2856         return NULL;
2857     }
2858 
2859     valuesBuf[0]=0;
2860     valuesBuf[1]=0;
2861 
2862     while((locale = uenum_next(locs, &locLen, status))) {
2863         UResourceBundle   *bund = NULL;
2864         UResourceBundle   *subPtr = NULL;
2865         UErrorCode subStatus = U_ZERO_ERROR; /* don't fail if a bundle is unopenable */
2866         bund = ures_openDirect(path, locale, &subStatus);
2867 
2868 #if defined(URES_TREE_DEBUG)
2869         if(!bund || U_FAILURE(subStatus)) {
2870             fprintf(stderr, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
2871                 path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));
2872         }
2873 #endif
2874 
2875         ures_getByKey(bund, keyword, &item, &subStatus);
2876 
2877         if(!bund || U_FAILURE(subStatus)) {
2878 #if defined(URES_TREE_DEBUG)
2879             fprintf(stderr, "%s-%s values: Can't find in %s - skipping. (%s)\n",
2880                 path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));
2881 #endif
2882             ures_close(bund);
2883             bund = NULL;
2884             continue;
2885         }
2886 
2887         while((subPtr = ures_getNextResource(&item,&subItem,&subStatus))
2888             && U_SUCCESS(subStatus)) {
2889             const char *k;
2890             int32_t i;
2891             k = ures_getKey(subPtr);
2892 
2893 #if defined(URES_TREE_DEBUG)
2894             /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2895 #endif
2896             if(k == NULL || *k == 0 ||
2897                     uprv_strcmp(k, DEFAULT_TAG) == 0 || uprv_strncmp(k, "private-", 8) == 0) {
2898                 // empty or "default" or unlisted type
2899                 continue;
2900             }
2901             for(i=0; i<valuesCount; i++) {
2902                 if(!uprv_strcmp(valuesList[i],k)) {
2903                     k = NULL; /* found duplicate */
2904                     break;
2905                 }
2906             }
2907             if(k != NULL) {
2908                 int32_t kLen = (int32_t)uprv_strlen(k);
2909                 if((valuesCount >= (VALUES_LIST_SIZE-1)) ||       /* no more space in list .. */
2910                     ((valuesIndex+kLen+1+1) >= VALUES_BUF_SIZE)) { /* no more space in buffer (string + 2 nulls) */
2911                     *status = U_ILLEGAL_ARGUMENT_ERROR; /* out of space.. */
2912                 } else {
2913                     uprv_strcpy(valuesBuf+valuesIndex, k);
2914                     valuesList[valuesCount++] = valuesBuf+valuesIndex;
2915                     valuesIndex += kLen;
2916 #if defined(URES_TREE_DEBUG)
2917                     fprintf(stderr, "%s | %s | %s | [%s]   (UNIQUE)\n",
2918                         path?path:"<ICUDATA>", keyword, locale, k);
2919 #endif
2920                     valuesBuf[valuesIndex++] = 0; /* terminate */
2921                 }
2922             }
2923         }
2924         ures_close(bund);
2925     }
2926     valuesBuf[valuesIndex++] = 0; /* terminate */
2927 
2928     ures_close(&item);
2929     ures_close(&subItem);
2930     uenum_close(locs);
2931 #if defined(URES_TREE_DEBUG)
2932     fprintf(stderr, "%s:  size %d, #%d\n", u_errorName(*status),
2933         valuesIndex, valuesCount);
2934 #endif
2935     return uloc_openKeywordList(valuesBuf, valuesIndex, status);
2936 }
2937 #if 0
2938 /* This code isn't needed, and given the documentation warnings the implementation is suspect */
2939 U_INTERNAL UBool U_EXPORT2
2940 ures_equal(const UResourceBundle* res1, const UResourceBundle* res2){
2941     if(res1==NULL || res2==NULL){
2942         return res1==res2; /* pointer comparision */
2943     }
2944     if(res1->fKey==NULL||  res2->fKey==NULL){
2945         return (res1->fKey==res2->fKey);
2946     }else{
2947         if(uprv_strcmp(res1->fKey, res2->fKey)!=0){
2948             return FALSE;
2949         }
2950     }
2951     if(uprv_strcmp(res1->fData->fName, res2->fData->fName)!=0){
2952         return FALSE;
2953     }
2954     if(res1->fData->fPath == NULL||  res2->fData->fPath==NULL){
2955         return (res1->fData->fPath == res2->fData->fPath);
2956     }else{
2957         if(uprv_strcmp(res1->fData->fPath, res2->fData->fPath)!=0){
2958             return FALSE;
2959         }
2960     }
2961     if(uprv_strcmp(res1->fData->fParent->fName, res2->fData->fParent->fName)!=0){
2962         return FALSE;
2963     }
2964     if(uprv_strcmp(res1->fData->fParent->fPath, res2->fData->fParent->fPath)!=0){
2965         return FALSE;
2966     }
2967     if(uprv_strncmp(res1->fResPath, res2->fResPath, res1->fResPathLen)!=0){
2968         return FALSE;
2969     }
2970     if(res1->fRes != res2->fRes){
2971         return FALSE;
2972     }
2973     return TRUE;
2974 }
2975 U_INTERNAL UResourceBundle* U_EXPORT2
2976 ures_clone(const UResourceBundle* res, UErrorCode* status){
2977     UResourceBundle* bundle = NULL;
2978     UResourceBundle* ret = NULL;
2979     if(U_FAILURE(*status) || res == NULL){
2980         return NULL;
2981     }
2982     bundle = ures_open(res->fData->fPath, res->fData->fName, status);
2983     if(res->fResPath!=NULL){
2984         ret = ures_findSubResource(bundle, res->fResPath, NULL, status);
2985         ures_close(bundle);
2986     }else{
2987         ret = bundle;
2988     }
2989     return ret;
2990 }
2991 U_INTERNAL const UResourceBundle* U_EXPORT2
2992 ures_getParentBundle(const UResourceBundle* res){
2993     if(res==NULL){
2994         return NULL;
2995     }
2996     return res->fParentRes;
2997 }
2998 #endif
2999 
3000 U_INTERNAL void U_EXPORT2
ures_getVersionByKey(const UResourceBundle * res,const char * key,UVersionInfo ver,UErrorCode * status)3001 ures_getVersionByKey(const UResourceBundle* res, const char *key, UVersionInfo ver, UErrorCode *status) {
3002   const UChar *str;
3003   int32_t len;
3004   str = ures_getStringByKey(res, key, &len, status);
3005   if(U_SUCCESS(*status)) {
3006     u_versionFromUString(ver, str);
3007   }
3008 }
3009 
3010 /* eof */
3011