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