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