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