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