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