+/** Tree support functions *******************************/
+#define INDEX_LOCALE_NAME "res_index"
+#define INDEX_TAG "InstalledLocales"
+#define DEFAULT_TAG "default"
+
+#if defined(URES_TREE_DEBUG)
+#include <stdio.h>
+#endif
+
+typedef struct ULocalesContext {
+ UResourceBundle installed;
+ UResourceBundle curr;
+} ULocalesContext;
+
+static void U_CALLCONV
+ures_loc_closeLocales(UEnumeration *enumerator) {
+ ULocalesContext *ctx = (ULocalesContext *)enumerator->context;
+ ures_close(&ctx->curr);
+ ures_close(&ctx->installed);
+ uprv_free(ctx);
+ uprv_free(enumerator);
+}
+
+static int32_t U_CALLCONV
+ures_loc_countLocales(UEnumeration *en, UErrorCode *status) {
+ ULocalesContext *ctx = (ULocalesContext *)en->context;
+ return ures_getSize(&ctx->installed);
+}
+
+static const char* U_CALLCONV
+ures_loc_nextLocale(UEnumeration* en,
+ int32_t* resultLength,
+ UErrorCode* status) {
+ ULocalesContext *ctx = (ULocalesContext *)en->context;
+ UResourceBundle *res = &(ctx->installed);
+ UResourceBundle *k = NULL;
+ const char *result = NULL;
+ int32_t len = 0;
+ if(ures_hasNext(res) && (k = ures_getNextResource(res, &ctx->curr, status))) {
+ result = ures_getKey(k);
+ len = (int32_t)uprv_strlen(result);
+ }
+ if (resultLength) {
+ *resultLength = len;
+ }
+ return result;
+}
+
+static void U_CALLCONV
+ures_loc_resetLocales(UEnumeration* en,
+ UErrorCode* status) {
+ UResourceBundle *res = &((ULocalesContext *)en->context)->installed;
+ ures_resetIterator(res);
+}
+
+
+static const UEnumeration gLocalesEnum = {
+ NULL,
+ NULL,
+ ures_loc_closeLocales,
+ ures_loc_countLocales,
+ uenum_unextDefault,
+ ures_loc_nextLocale,
+ ures_loc_resetLocales
+};
+
+
+U_CAPI UEnumeration* U_EXPORT2
+ures_openAvailableLocales(const char *path, UErrorCode *status)
+{
+ UResourceBundle *index = NULL;
+ UEnumeration *en = NULL;
+ ULocalesContext *myContext = NULL;
+
+ if(U_FAILURE(*status)) {
+ return NULL;
+ }
+ myContext = uprv_malloc(sizeof(ULocalesContext));
+ en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
+ if(!en || !myContext) {
+ *status = U_MEMORY_ALLOCATION_ERROR;
+ uprv_free(en);
+ uprv_free(myContext);
+ return NULL;
+ }
+ uprv_memcpy(en, &gLocalesEnum, sizeof(UEnumeration));
+
+ ures_initStackObject(&myContext->installed);
+ ures_initStackObject(&myContext->curr);
+ index = ures_openDirect(path, INDEX_LOCALE_NAME, status);
+ ures_getByKey(index, INDEX_TAG, &myContext->installed, status);
+ if(U_SUCCESS(*status)) {
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "Got %s::%s::[%s] : %s\n",
+ path, INDEX_LOCALE_NAME, INDEX_TAG, ures_getKey(&myContext->installed));
+#endif
+ en->context = myContext;
+ } else {
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s open failed - %s\n", path, u_errorName(*status));
+#endif
+ ures_close(&myContext->installed);
+ uprv_free(myContext);
+ uprv_free(en);
+ en = NULL;
+ }
+
+ ures_close(index);
+
+ return en;
+}
+
+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)
+{
+ char kwVal[1024] = ""; /* value of keyword 'keyword' */
+ char defVal[1024] = ""; /* default value for given locale */
+ char defLoc[1024] = ""; /* default value for given locale */
+ char base[1024] = ""; /* base locale */
+ char found[1024];
+ char parent[1024];
+ char full[1024] = "";
+ UResourceBundle bund1, bund2;
+ UResourceBundle *res = NULL;
+ UErrorCode subStatus = U_ZERO_ERROR;
+ int32_t length = 0;
+ if(U_FAILURE(*status)) return 0;
+ if(isAvailable) {
+ *isAvailable = TRUE;
+ }
+ uloc_getKeywordValue(locid, keyword, kwVal, 1024-1,&subStatus);
+ if(!uprv_strcmp(kwVal, DEFAULT_TAG)) {
+ kwVal[0]=0;
+ }
+ uloc_getBaseName(locid, base, 1024-1,&subStatus);
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
+ locid, keyword, kwVal, base, u_errorName(subStatus));
+#endif
+ ures_initStackObject(&bund1);
+ ures_initStackObject(&bund2);
+
+
+ uprv_strcpy(parent, base);
+ uprv_strcpy(found, base);
+
+ if(U_FAILURE(subStatus)) {
+ *status = subStatus;
+ return 0;
+ }
+
+ do {
+ subStatus = U_ZERO_ERROR;
+ res = ures_open(path, parent, &subStatus);
+ if(((subStatus == U_USING_FALLBACK_WARNING) ||
+ (subStatus == U_USING_DEFAULT_WARNING)) && isAvailable) {
+ *isAvailable = FALSE;
+ }
+ isAvailable = NULL; /* only want to set this the first time around */
+
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s;%s -> %s [%s]\n", path?path:"ICUDATA", parent, u_errorName(subStatus), ures_getLocale(res, &subStatus));
+#endif
+ if(U_FAILURE(subStatus)) {
+ *status = subStatus;
+ } else if(subStatus == U_ZERO_ERROR) {
+ ures_getByKey(res,resName,&bund1, &subStatus);
+ if(subStatus == U_ZERO_ERROR) {
+ const UChar *defUstr;
+ int32_t defLen;
+ /* look for default item */
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s;%s : loaded default -> %s\n",
+ path?path:"ICUDATA", parent, u_errorName(subStatus));
+#endif
+ defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
+ if(U_SUCCESS(subStatus) && defLen) {
+ u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s;%s -> default %s=%s, %s\n",
+ path?path:"ICUDATA", parent, keyword, defVal, u_errorName(subStatus));
+#endif
+ uprv_strcpy(defLoc, parent);
+ if(kwVal[0]==0) {
+ uprv_strcpy(kwVal, defVal);
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s;%s -> kwVal = %s\n",
+ path?path:"ICUDATA", parent, keyword, kwVal);
+#endif
+ }
+ }
+ }
+ }
+
+ subStatus = U_ZERO_ERROR;
+
+ uprv_strcpy(found, parent);
+ uloc_getParent(found,parent,1023,&subStatus);
+ ures_close(res);
+ } while(!defVal[0] && *found && U_SUCCESS(*status));
+
+ /* Now, see if we can find the kwVal collator.. start the search over.. */
+ uprv_strcpy(parent, base);
+ uprv_strcpy(found, base);
+
+ do {
+ subStatus = U_ZERO_ERROR;
+ res = ures_open(path, parent, &subStatus);
+ if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
+ *isAvailable = FALSE;
+ }
+ isAvailable = NULL; /* only want to set this the first time around */
+
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s;%s -> %s (looking for %s)\n",
+ path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);
+#endif
+ if(U_FAILURE(subStatus)) {
+ *status = subStatus;
+ } else if(subStatus == U_ZERO_ERROR) {
+ ures_getByKey(res,resName,&bund1, &subStatus);
+#if defined(URES_TREE_DEBUG)
+/**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, resName, u_errorName(subStatus));
+#endif
+ if(subStatus == U_ZERO_ERROR) {
+ ures_getByKey(&bund1, kwVal, &bund2, &subStatus);
+#if defined(URES_TREE_DEBUG)
+/**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, kwVal, u_errorName(subStatus));
+#endif
+ if(subStatus == U_ZERO_ERROR) {
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s;%s -> full0 %s=%s, %s\n",
+ path?path:"ICUDATA", parent, keyword, kwVal, u_errorName(subStatus));
+#endif
+ uprv_strcpy(full, parent);
+ if(*full == 0) {
+ uprv_strcpy(full, "root");
+ }
+ /* now, recalculate default kw if need be */
+ if(uprv_strlen(defLoc) > uprv_strlen(full)) {
+ const UChar *defUstr;
+ int32_t defLen;
+ /* look for default item */
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s;%s -> recalculating Default0\n",
+ path?path:"ICUDATA", full);
+#endif
+ defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
+ if(U_SUCCESS(subStatus) && defLen) {
+ u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s;%s -> default0 %s=%s, %s\n",
+ path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));
+#endif
+ uprv_strcpy(defLoc, full);
+ }
+ } /* end of recalculate default KW */
+#if defined(URES_TREE_DEBUG)
+ else {
+ fprintf(stderr, "No trim0, %s <= %s\n", defLoc, full);
+ }
+#endif
+ } else {
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "err=%s in %s looking for %s\n",
+ u_errorName(subStatus), parent, kwVal);
+#endif
+ }
+ }
+ }
+
+ subStatus = U_ZERO_ERROR;
+
+ uprv_strcpy(found, parent);
+ uloc_getParent(found,parent,1023,&subStatus);
+ ures_close(res);
+ } while(!full[0] && *found && U_SUCCESS(*status));
+
+ if((full[0]==0) && uprv_strcmp(kwVal, defVal)) {
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "Failed to locate kw %s - try default %s\n", kwVal, defVal);
+#endif
+ uprv_strcpy(kwVal, defVal);
+ uprv_strcpy(parent, base);
+ uprv_strcpy(found, base);
+
+ do { /* search for 'default' named item */
+ subStatus = U_ZERO_ERROR;
+ res = ures_open(path, parent, &subStatus);
+ if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
+ *isAvailable = FALSE;
+ }
+ isAvailable = NULL; /* only want to set this the first time around */
+
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s;%s -> %s (looking for default %s)\n",
+ path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);
+#endif
+ if(U_FAILURE(subStatus)) {
+ *status = subStatus;
+ } else if(subStatus == U_ZERO_ERROR) {
+ ures_getByKey(res,resName,&bund1, &subStatus);
+ if(subStatus == U_ZERO_ERROR) {
+ ures_getByKey(&bund1, kwVal, &bund2, &subStatus);
+ if(subStatus == U_ZERO_ERROR) {
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s;%s -> full1 %s=%s, %s\n", path?path:"ICUDATA",
+ parent, keyword, kwVal, u_errorName(subStatus));
+#endif
+ uprv_strcpy(full, parent);
+ if(*full == 0) {
+ uprv_strcpy(full, "root");
+ }
+
+ /* now, recalculate default kw if need be */
+ if(uprv_strlen(defLoc) > uprv_strlen(full)) {
+ const UChar *defUstr;
+ int32_t defLen;
+ /* look for default item */
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s;%s -> recalculating Default1\n",
+ path?path:"ICUDATA", full);
+#endif
+ defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
+ if(U_SUCCESS(subStatus) && defLen) {
+ u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s;%s -> default %s=%s, %s\n",
+ path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));
+#endif
+ uprv_strcpy(defLoc, full);
+ }
+ } /* end of recalculate default KW */
+#if defined(URES_TREE_DEBUG)
+ else {
+ fprintf(stderr, "No trim1, %s <= %s\n", defLoc, full);
+ }
+#endif
+ }
+ }
+ }
+ subStatus = U_ZERO_ERROR;
+
+ uprv_strcpy(found, parent);
+ uloc_getParent(found,parent,1023,&subStatus);
+ ures_close(res);
+ } while(!full[0] && *found && U_SUCCESS(*status));
+ }
+
+ if(U_SUCCESS(*status)) {
+ if(!full[0]) {
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "Still could not load keyword %s=%s\n", keyword, kwVal);
+#endif
+ *status = U_MISSING_RESOURCE_ERROR;
+ } else if(omitDefault) {
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr,"Trim? full=%s, defLoc=%s, found=%s\n", full, defLoc, found);
+#endif
+ if(uprv_strlen(defLoc) <= uprv_strlen(full)) {
+ /* found the keyword in a *child* of where the default tag was present. */
+ if(!uprv_strcmp(kwVal, defVal)) { /* if the requested kw is default, */
+ /* and the default is in or in an ancestor of the current locale */
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "Removing unneeded var %s=%s\n", keyword, kwVal);
+#endif
+ kwVal[0]=0;
+ }
+ }
+ }
+ uprv_strcpy(found, full);
+ if(kwVal[0]) {
+ uprv_strcat(found, "@");
+ uprv_strcat(found, keyword);
+ uprv_strcat(found, "=");
+ uprv_strcat(found, kwVal);
+ } else if(!omitDefault) {
+ uprv_strcat(found, "@");
+ uprv_strcat(found, keyword);
+ uprv_strcat(found, "=");
+ uprv_strcat(found, defVal);
+ }
+ }
+ /* we found the default locale - no need to repeat it.*/
+
+ ures_close(&bund1);
+ ures_close(&bund2);
+
+ length = (int32_t)uprv_strlen(found);
+
+ if(U_SUCCESS(*status)) {
+ int32_t copyLength = uprv_min(length, resultCapacity);
+ if(copyLength>0) {
+ uprv_strncpy(result, found, copyLength);
+ }
+ if(length == 0) {
+ *status = U_MISSING_RESOURCE_ERROR;
+ }
+ } else {
+ length = 0;
+ result[0]=0;
+ }
+ return u_terminateChars(result, resultCapacity, length, status);
+}
+
+U_CAPI UEnumeration* U_EXPORT2
+ures_getKeywordValues(const char *path, const char *keyword, UErrorCode *status)
+{
+#define VALUES_BUF_SIZE 2048
+#define VALUES_LIST_SIZE 512
+
+ char valuesBuf[VALUES_BUF_SIZE];
+ int32_t valuesIndex = 0;
+ const char *valuesList[VALUES_LIST_SIZE];
+ int32_t valuesCount = 0;
+
+ const char *locale;
+ int32_t locLen;
+
+ UEnumeration *locs = NULL;
+
+ UResourceBundle item;
+ UResourceBundle subItem;
+
+ ures_initStackObject(&item);
+ ures_initStackObject(&subItem);
+ locs = ures_openAvailableLocales(path, status);
+
+ if(U_FAILURE(*status)) {
+ ures_close(&item);
+ ures_close(&subItem);
+ return NULL;
+ }
+
+ valuesBuf[0]=0;
+ valuesBuf[1]=0;
+
+ while((locale = uenum_next(locs, &locLen, status))) {
+ UResourceBundle *bund = NULL;
+ UResourceBundle *subPtr = NULL;
+ UErrorCode subStatus = U_ZERO_ERROR; /* don't fail if a bundle is unopenable */
+ bund = ures_openDirect(path, locale, &subStatus);
+
+#if defined(URES_TREE_DEBUG)
+ if(!bund || U_FAILURE(subStatus)) {
+ fprintf(stderr, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
+ path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));
+ }
+#endif
+
+ ures_getByKey(bund, keyword, &item, &subStatus);
+
+ if(!bund || U_FAILURE(subStatus)) {
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s-%s values: Can't find in %s - skipping. (%s)\n",
+ path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));
+#endif
+ ures_close(bund);
+ bund = NULL;
+ continue;
+ }
+
+ while((subPtr = ures_getNextResource(&item,&subItem,&subStatus))
+ && U_SUCCESS(subStatus)) {
+ const char *k;
+ int32_t i;
+ k = ures_getKey(subPtr);
+
+#if defined(URES_TREE_DEBUG)
+ /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
+#endif
+ for(i=0;k&&i<valuesCount;i++) {
+ if(!uprv_strcmp(valuesList[i],k)) {
+ k = NULL; /* found duplicate */
+ }
+ }
+ if(k && *k) {
+ int32_t kLen = (int32_t)uprv_strlen(k);
+ if(!uprv_strcmp(k,DEFAULT_TAG)) {
+ continue; /* don't need 'default'. */
+ }
+ if((valuesCount >= (VALUES_LIST_SIZE-1)) || /* no more space in list .. */
+ ((valuesIndex+kLen+1+1) >= VALUES_BUF_SIZE)) { /* no more space in buffer (string + 2 nulls) */
+ *status = U_ILLEGAL_ARGUMENT_ERROR; /* out of space.. */
+ } else {
+ uprv_strcpy(valuesBuf+valuesIndex, k);
+ valuesList[valuesCount++] = valuesBuf+valuesIndex;
+ valuesIndex += kLen;
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s | %s | %s | [%s] (UNIQUE)\n",
+ path?path:"<ICUDATA>", keyword, locale, k);
+#endif
+ valuesBuf[valuesIndex++] = 0; /* terminate */
+ }
+ }
+ }
+ ures_close(bund);
+ }
+ valuesBuf[valuesIndex++] = 0; /* terminate */
+
+ ures_close(&item);
+ ures_close(&subItem);
+ uenum_close(locs);
+#if defined(URES_TREE_DEBUG)
+ fprintf(stderr, "%s: size %d, #%d\n", u_errorName(*status),
+ valuesIndex, valuesCount);
+#endif
+ return uloc_openKeywordList(valuesBuf, valuesIndex, status);
+}
+U_INTERNAL UBool U_EXPORT2
+ures_equal(const UResourceBundle* res1, const UResourceBundle* res2){
+ if(res1==NULL || res2==NULL){
+ return res1==res2; /* pointer comparision */
+ }
+ if(res1->fKey==NULL|| res2->fKey==NULL){
+ return (res1->fKey==res2->fKey);
+ }else{
+ if(uprv_strcmp(res1->fKey, res2->fKey)!=0){
+ return FALSE;
+ }
+ }
+ if(uprv_strcmp(res1->fData->fName, res2->fData->fName)!=0){
+ return FALSE;
+ }
+ if(res1->fData->fPath == NULL|| res2->fData->fPath==NULL){
+ return (res1->fData->fPath == res2->fData->fPath);
+ }else{
+ if(uprv_strcmp(res1->fData->fPath, res2->fData->fPath)!=0){
+ return FALSE;
+ }
+ }
+ if(uprv_strcmp(res1->fData->fParent->fName, res2->fData->fParent->fName)!=0){
+ return FALSE;
+ }
+ if(uprv_strcmp(res1->fData->fParent->fPath, res2->fData->fParent->fPath)!=0){
+ return FALSE;
+ }
+ if(uprv_strncmp(res1->fResPath, res2->fResPath, res1->fResPathLen)!=0){
+ return FALSE;
+ }
+ if(res1->fRes != res2->fRes){
+ return FALSE;
+ }
+ return TRUE;
+}
+U_INTERNAL UResourceBundle* U_EXPORT2
+ures_clone(const UResourceBundle* res, UErrorCode* status){
+ UResourceBundle* bundle = NULL;
+ UResourceBundle* ret = NULL;
+ if(U_FAILURE(*status) || res == NULL){
+ return NULL;
+ }
+ bundle = ures_open(res->fData->fPath, res->fData->fName, status);
+ if(res->fResPath!=NULL){
+ ret = ures_findSubResource(bundle, res->fResPath, NULL, status);
+ ures_close(bundle);
+ }else{
+ ret = bundle;
+ }
+ return ret;
+}
+U_INTERNAL const UResourceBundle* U_EXPORT2
+ures_getParentBundle(const UResourceBundle* res){
+ if(res==NULL){
+ return NULL;
+ }
+ return res->fParentRes;
+}