/*
******************************************************************************
-* Copyright (C) 1997-2003, International Business Machines Corporation and *
+* Copyright (C) 1997-2004, International Business Machines Corporation and *
* others. All Rights Reserved. *
******************************************************************************
*
*/
#include "unicode/ustring.h"
-
+#include "unicode/ucnv.h"
#include "uresimp.h"
+#include "ustr_imp.h"
#include "cwchar.h"
#include "ucln_cmn.h"
#include "cmemory.h"
#include "cstring.h"
#include "uhash.h"
+#include "unicode/uenum.h"
+#include "uenumimp.h"
+#include "ulocimp.h"
#include "umutex.h"
+#include "putilimp.h"
/*
}
}
-/** INTERNAL: Initializes the cache for resources */
-static void initCache(UErrorCode *status) {
- UBool makeCache = FALSE;
- umtx_lock(&resbMutex);
- makeCache = (cache == NULL);
- umtx_unlock(&resbMutex);
- if(makeCache) {
- UHashtable *newCache = uhash_open(hashEntry, compareEntries, status);
- if (U_FAILURE(*status)) {
- return;
- }
- umtx_lock(&resbMutex);
- if(cache == NULL) {
- cache = newCache;
- newCache = NULL;
- }
- umtx_unlock(&resbMutex);
- if(newCache != NULL) {
- uhash_close(newCache);
- }
- }
-}
-
/* Works just like ucnv_flushCache() */
/* TODO: figure out why fCountExisting may not go to zero. Do not make this function public yet. */
static int32_t ures_flushCache()
/* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */
/* some resource bundles are still open somewhere. */
+ /*U_ASSERT(resB->fCountExisting == 0);*/
if (resB->fCountExisting == 0) {
rbDeletedNum++;
uhash_removeElement(cache, e);
return rbDeletedNum;
}
-UBool ures_cleanup(void)
+static UBool U_CALLCONV ures_cleanup(void)
{
if (cache != NULL) {
ures_flushCache();
if (cache != NULL && uhash_count(cache) == 0) {
uhash_close(cache);
cache = NULL;
- umtx_destroy(&resbMutex);
}
}
+ if (cache == NULL && resbMutex != NULL) {
+ umtx_destroy(&resbMutex);
+ }
return (cache == NULL);
}
-U_CFUNC void ures_init(UErrorCode *status) {
- umtx_init(&resbMutex);
+/** INTERNAL: Initializes the cache for resources */
+static void initCache(UErrorCode *status) {
+ UBool makeCache = FALSE;
+ umtx_lock(&resbMutex);
+ makeCache = (cache == NULL);
+ umtx_unlock(&resbMutex);
+ if(makeCache) {
+ UHashtable *newCache = uhash_open(hashEntry, compareEntries, status);
+ if (U_FAILURE(*status)) {
+ return;
+ }
+ umtx_lock(&resbMutex);
+ if(cache == NULL) {
+ cache = newCache;
+ newCache = NULL;
+ ucln_common_registerCleanup(UCLN_COMMON_URES, ures_cleanup);
+ }
+ umtx_unlock(&resbMutex);
+ if(newCache != NULL) {
+ uhash_close(newCache);
+ }
+ }
}
-
/** INTERNAL: sets the name (locale) of the resource bundle to given name */
static void setEntryName(UResourceDataEntry *res, char *name, UErrorCode *status) {
/**
* INTERNAL: Inits and opens an entry from a data DLL.
+ * CAUTION: resbMutex must be locked when calling this function.
*/
static UResourceDataEntry *init_entry(const char *localeID, const char *path, UErrorCode *status) {
UResourceDataEntry *r = NULL;
}
if(path != NULL) { /* if we actually have path, we'll use it */
- if(uprv_strcmp(path, u_getDataDirectory()) != 0) { /* unless it is system default path */
- myPath = path;
- }
+ myPath = path;
}
find.fName = name;
}
/* INTERNAL: */
+/* CAUTION: resbMutex must be locked when calling this function! */
static UResourceDataEntry *findFirstExisting(const char* path, char* name, UBool *isRoot, UBool *hasChopped, UBool *isDefault, UErrorCode* status) {
UResourceDataEntry *r = NULL;
UBool hasRealData = FALSE;
return r;
}
+static void ures_setIsStackObject( UResourceBundle* resB, UBool state) {
+ if(state) {
+ resB->fMagic1 = 0;
+ resB->fMagic2 = 0;
+ } else {
+ resB->fMagic1 = MAGIC1;
+ resB->fMagic2 = MAGIC2;
+ }
+}
+
+static UBool ures_isStackObject(const UResourceBundle* resB) {
+ return((resB->fMagic1 == MAGIC1 && resB->fMagic2 == MAGIC2)?FALSE:TRUE);
+}
+
+
+U_CFUNC void ures_initStackObject(UResourceBundle* resB) {
+ uprv_memset(resB, 0, sizeof(UResourceBundle));
+ ures_setIsStackObject(resB, TRUE);
+}
+
static UResourceDataEntry *entryOpen(const char* path, const char* localeID, UErrorCode* status) {
UErrorCode intStatus = U_ZERO_ERROR;
UErrorCode parentStatus = U_ZERO_ERROR;
/**
* Functions to create and destroy resource bundles.
+ * CAUTION: resbMutex must be locked when calling this function.
*/
/* INTERNAL: */
static void entryCloseInt(UResourceDataEntry *resB) {
const UChar *alias = res_getAlias(rdata, r, &len);
if(len > 0) {
/* we have an alias, now let's cut it up */
+ char stackAlias[200];
char *chAlias = NULL, *path = NULL, *locale = NULL, *keyPath = NULL;
- chAlias = (char *)uprv_malloc((len+1)*sizeof(char));
- /* test for NULL */
- if(chAlias == NULL) {
- *status = U_MEMORY_ALLOCATION_ERROR;
- return NULL;
+ int32_t capacity;
+
+ /*
+ * Allocate enough space for both the char * version
+ * of the alias and parent->fResPath.
+ *
+ * We do this so that res_findResource() can modify the path,
+ * which allows us to remove redundant _res_findResource() variants
+ * in uresdata.c.
+ * res_findResource() now NUL-terminates each segment so that table keys
+ * can always be compared with strcmp() instead of strncmp().
+ * Saves code there and simplifies testing and code coverage.
+ *
+ * markus 2003oct17
+ */
+ ++len; /* count the terminating NUL */
+ if(parent != NULL && parent->fResPath != NULL) {
+ capacity = uprv_strlen(parent->fResPath) + 1;
+ } else {
+ capacity = 0;
+ }
+ if(capacity < len) {
+ capacity = len;
+ }
+ if(capacity <= sizeof(stackAlias)) {
+ capacity = sizeof(stackAlias);
+ chAlias = stackAlias;
+ } else {
+ chAlias = (char *)uprv_malloc(capacity);
+ /* test for NULL */
+ if(chAlias == NULL) {
+ *status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
}
+ }
u_UCharsToChars(alias, chAlias, len);
- chAlias[len] = 0;
if(*chAlias == RES_PATH_SEPARATOR) {
/* there is a path included */
locale = uprv_strchr(chAlias+1, RES_PATH_SEPARATOR);
- *locale = 0;
- locale++;
+ if(locale == NULL) {
+ locale = uprv_strchr(chAlias, 0); /* avoid locale == NULL to make code below work */
+ } else {
+ *locale = 0;
+ locale++;
+ }
path = chAlias+1;
if(uprv_strcmp(path, "ICUDATA") == 0) { /* want ICU data */
path = NULL;
/* first, we are going to get a corresponding parent
* resource to the one we are searching.
*/
- const char* aKey = parent->fResPath;
+ char *aKey = parent->fResPath;
if(aKey) {
+ uprv_strcpy(chAlias, aKey); /* allocated large enough above */
+ aKey = chAlias;
r = res_findResource(&(mainRes->fResData), mainRes->fRes, &aKey, &temp);
} else {
r = mainRes->fRes;
}
if(key) {
- /* we need to make keyPath from parents fResPath and
- * current key, if there is a key associated
- */
- aKey = key;
+ /* we need to make keyPath from parent's fResPath and
+ * current key, if there is a key associated
+ */
+ len = uprv_strlen(key) + 1;
+ if(len > capacity) {
+ capacity = len;
+ if(chAlias == stackAlias) {
+ chAlias = (char *)uprv_malloc(capacity);
+ } else {
+ chAlias = (char *)uprv_realloc(chAlias, capacity);
+ }
+ if(chAlias == NULL) {
+ ures_close(mainRes);
+ *status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+ }
+ uprv_memcpy(chAlias, key, len);
+ aKey = chAlias;
r = res_findResource(&(mainRes->fResData), r, &aKey, &temp);
} else if(index != -1) {
/* if there is no key, but there is an index, try to get by the index */
/* here we have either a table or an array, so get the element */
- if(RES_GET_TYPE(r) == URES_TABLE) {
- r = res_getTableItemByIndex(&(mainRes->fResData), r, index, &aKey);
+ if(RES_GET_TYPE(r) == URES_TABLE || RES_GET_TYPE(r) == URES_TABLE32) {
+ r = res_getTableItemByIndex(&(mainRes->fResData), r, index, (const char **)&aKey);
} else { /* array */
r = res_getArrayItem(&(mainRes->fResData), r, index);
}
*/
result = mainRes;
while(*keyPath && U_SUCCESS(*status)) {
- r = res_findResource(&(result->fResData), result->fRes, (const char**)&keyPath, &temp);
+ r = res_findResource(&(result->fResData), result->fRes, &keyPath, &temp);
if(r == RES_BOGUS) {
*status = U_MISSING_RESOURCE_ERROR;
result = resB;
} else { /* we failed to open the resource we're aliasing to */
*status = intStatus;
}
- uprv_free(chAlias);
- ures_close(mainRes);
+ if(chAlias != stackAlias) {
+ uprv_free(chAlias);
+ }
+ if(mainRes != result) {
+ ures_close(mainRes);
+ }
return result;
}
} else {
}
ures_setIsStackObject(resB, FALSE);
resB->fResPath = NULL;
+ resB->fResPathLen = 0;
} else {
if(resB->fData != NULL) {
entryClose(resB->fData);
if(ures_isStackObject(resB) != FALSE) {
ures_initStackObject(resB);
}
+ if(parent != resB) {
+ ures_freeResPath(resB);
+ }
}
resB->fData = realData;
entryIncrease(resB->fData);
resB->fIsTopLevel = FALSE;
resB->fIndex = -1;
resB->fKey = key;
- ures_freeResPath(resB);
- if(parent->fResPath) {
+ resB->fParentRes = parent;
+ resB->fTopLevelData = parent->fTopLevelData;
+ if(parent->fResPath && parent != resB) {
ures_appendResPath(resB, parent->fResPath, parent->fResPathLen);
}
if(key != NULL) {
}
uprv_memcpy(r, original, sizeof(UResourceBundle));
r->fResPath = NULL;
+ r->fResPathLen = 0;
if(original->fResPath) {
ures_appendResPath(r, original->fResPath, original->fResPathLen);
}
case URES_BINARY:
case URES_ARRAY:
case URES_TABLE:
+ case URES_TABLE32:
default:
*status = U_RESOURCE_TYPE_MISMATCH;
}
case URES_INT_VECTOR:
case URES_ARRAY:
case URES_TABLE:
+ case URES_TABLE32:
default:
*status = U_RESOURCE_TYPE_MISMATCH;
}
case URES_ARRAY:
case URES_BINARY:
case URES_TABLE:
+ case URES_TABLE32:
default:
*status = U_RESOURCE_TYPE_MISMATCH;
}
}
-U_CAPI UResType U_EXPORT2 ures_getType(UResourceBundle *resB) {
+U_CAPI UResType U_EXPORT2 ures_getType(const UResourceBundle *resB) {
+ UResType type;
+
if(resB == NULL) {
return URES_NONE;
}
- return (UResType) (RES_GET_TYPE(resB->fRes));
+ type = (UResType) RES_GET_TYPE(resB->fRes);
+ return type == URES_TABLE32 ? URES_TABLE : type;
}
-U_CAPI const char * U_EXPORT2 ures_getKey(UResourceBundle *resB) {
+U_CAPI const char * U_EXPORT2 ures_getKey(const UResourceBundle *resB) {
if(resB == NULL) {
return NULL;
}
return(resB->fKey);
}
-U_CAPI int32_t U_EXPORT2 ures_getSize(UResourceBundle *resB) {
+U_CAPI int32_t U_EXPORT2 ures_getSize(const UResourceBundle *resB) {
if(resB == NULL) {
return 0;
}
resB->fIndex = -1;
}
-U_CAPI UBool U_EXPORT2 ures_hasNext(UResourceBundle *resB) {
+U_CAPI UBool U_EXPORT2 ures_hasNext(const UResourceBundle *resB) {
if(resB == NULL) {
return FALSE;
}
case URES_STRING:
return res_getString(&(resB->fResData), resB->fRes, len);
case URES_TABLE:
+ case URES_TABLE32:
r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, resB->fIndex, key);
if(r == RES_BOGUS && resB->fHasFallback) {
/* TODO: do the fallback */
case URES_STRING:
return ures_copyResb(fillIn, resB, status);
case URES_TABLE:
+ case URES_TABLE32:
r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, resB->fIndex, &key);
if(r == RES_BOGUS && resB->fHasFallback) {
/* TODO: do the fallback */
case URES_STRING:
return ures_copyResb(fillIn, resB, status);
case URES_TABLE:
+ case URES_TABLE32:
r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, indexR, &key);
if(r == RES_BOGUS && resB->fHasFallback) {
/* TODO: do the fallback */
case URES_STRING:
return res_getString(&(resB->fResData), resB->fRes, len);
case URES_TABLE:
+ case URES_TABLE32:
r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, indexS, &key);
if(r == RES_BOGUS && resB->fHasFallback) {
/* TODO: do the fallback */
char *packageName = NULL;
char *pathToResource = NULL;
char *locale = NULL, *localeEnd = NULL;
+ int32_t length;
+
if(status == NULL || U_FAILURE(*status)) {
return result;
}
- pathToResource = (char *)uprv_malloc((uprv_strlen(path)+1)*sizeof(char));
+
+ length = uprv_strlen(path)+1;
+ pathToResource = (char *)uprv_malloc(length*sizeof(char));
/* test for NULL */
if(pathToResource == NULL) {
*status = U_MEMORY_ALLOCATION_ERROR;
return result;
- }
- uprv_strcpy(pathToResource, path);
+ }
+ uprv_memcpy(pathToResource, path, length);
+
locale = pathToResource;
if(*pathToResource == RES_PATH_SEPARATOR) { /* there is a path specification */
pathToResource++;
}
}
- localeEnd = strchr(locale, RES_PATH_SEPARATOR);
+ localeEnd = uprv_strchr(locale, RES_PATH_SEPARATOR);
if(localeEnd != NULL) {
*localeEnd = 0;
}
}
U_CAPI UResourceBundle* U_EXPORT2
-ures_findSubResource(const UResourceBundle *resB, const char* path, UResourceBundle *fillIn, UErrorCode *status)
+ures_findSubResource(const UResourceBundle *resB, char* path, UResourceBundle *fillIn, UErrorCode *status)
{
Resource res = RES_BOGUS;
UResourceBundle *result = fillIn;
- const char *pathToResource = path;
const char *key;
if(status == NULL || U_FAILURE(*status)) {
}
/* here we do looping and circular alias checking */
+ /* this loop is here because aliasing is resolved on this level, not on res level */
+ /* so, when we encounter an alias, it is not an aggregate resource, so we return */
+ do {
+ res = res_findResource(&(resB->fResData), resB->fRes, &path, &key);
+ if(res != RES_BOGUS) {
+ result = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
+ resB = result;
+ } else {
+ *status = U_MISSING_RESOURCE_ERROR;
+ break;
+ }
+ } while(uprv_strlen(path)); /* there is more stuff in the path */
- res = res_findResource(&(resB->fResData), resB->fRes, &pathToResource, &key);
+ return result;
+}
- if(res != RES_BOGUS) {
- result = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
- } else {
- *status = U_MISSING_RESOURCE_ERROR;
- }
+U_CAPI UResourceBundle* U_EXPORT2
+ures_getByKeyWithFallback(const UResourceBundle *resB,
+ const char* inKey,
+ UResourceBundle *fillIn,
+ UErrorCode *status) {
+ Resource res = RES_BOGUS;
+ /*UResourceDataEntry *realData = NULL;*/
+ const char *key = inKey;
+ UResourceBundle *helper = NULL;
- return result;
+ if (status==NULL || U_FAILURE(*status)) {
+ return fillIn;
+ }
+ if(resB == NULL) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
+ return fillIn;
+ }
+
+ if(RES_GET_TYPE(resB->fRes) == URES_TABLE || RES_GET_TYPE(resB->fRes) == URES_TABLE32) {
+ int32_t t;
+ res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);
+ if(res == RES_BOGUS) {
+ UResourceDataEntry *dataEntry = resB->fData;
+ char path[256];
+ char* myPath = path;
+
+ while(res == RES_BOGUS && dataEntry->fParent != NULL) { /* Otherwise, we'll look in parents */
+ dataEntry = dataEntry->fParent;
+ if(dataEntry->fBogus == U_ZERO_ERROR) {
+ uprv_strncpy(path, resB->fResPath, resB->fResPathLen);
+ uprv_strcpy(path+resB->fResPathLen, inKey);
+ myPath = path;
+ key = inKey;
+ do {
+ res = res_findResource(&(dataEntry->fData), dataEntry->fData.rootRes, &myPath, &key);
+ if (RES_GET_TYPE(res) == URES_ALIAS && *myPath) {
+ /* We hit an alias, but we didn't finish following the path. */
+ helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);
+ dataEntry = helper->fData;
+ }
+ } while(uprv_strlen(myPath));
+ }
+ }
+ /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
+ if(res != RES_BOGUS) {
+ /* check if resB->fResPath gives the right name here */
+ fillIn = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, fillIn, status);
+ } else {
+ *status = U_MISSING_RESOURCE_ERROR;
+ }
+ } else {
+ fillIn = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
+ }
+ }
+ else {
+ *status = U_RESOURCE_TYPE_MISMATCH;
+ }
+ ures_close(helper);
+ return fillIn;
}
return fillIn;
}
- if(RES_GET_TYPE(resB->fRes) == URES_TABLE) {
+ if(RES_GET_TYPE(resB->fRes) == URES_TABLE || RES_GET_TYPE(resB->fRes) == URES_TABLE32) {
int32_t t;
res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);
if(res == RES_BOGUS) {
return NULL;
}
- if(RES_GET_TYPE(resB->fRes) == URES_TABLE) {
+ if(RES_GET_TYPE(resB->fRes) == URES_TABLE || RES_GET_TYPE(resB->fRes) == URES_TABLE32) {
int32_t t=0;
res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);
switch (RES_GET_TYPE(res)) {
case URES_STRING:
case URES_TABLE:
+ case URES_TABLE32:
case URES_ARRAY:
return res_getString(rd, res, len);
case URES_ALIAS:
switch (RES_GET_TYPE(res)) {
case URES_STRING:
case URES_TABLE:
+ case URES_TABLE32:
case URES_ARRAY:
return res_getString(&(resB->fResData), res, len);
case URES_ALIAS:
}
}
+U_CAPI const char* U_EXPORT2
+ures_getLocaleByType(const UResourceBundle* resourceBundle,
+ ULocDataLocaleType type,
+ UErrorCode* status) {
+ if (status==NULL || U_FAILURE(*status)) {
+ return NULL;
+ }
+ if (!resourceBundle) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
+ return NULL;
+ } else {
+ switch(type) {
+ case ULOC_ACTUAL_LOCALE:
+ return resourceBundle->fData->fName;
+ break;
+ case ULOC_VALID_LOCALE:
+ return resourceBundle->fTopLevelData->fName;
+ break;
+ case ULOC_REQUESTED_LOCALE:
+ return NULL;
+ break;
+ default:
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
+ return NULL;
+ }
+ }
+}
+
+
/*
U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
if(resB->fResPath == NULL) {
resB->fResPathLen = 0;
}
+
U_CFUNC const char* ures_getName(const UResourceBundle* resB) {
if(resB == NULL) {
return NULL;
r->fSize = res_countArrayItems(&(r->fResData), r->fRes);
/*r->fParent = RES_BOGUS;*/
/*r->fResPath = NULL;*/
+ r->fParentRes = NULL;
+ r->fTopLevelData = r->fData;
+
ures_freeResPath(r);
- /*
- if(r->fData->fPath != NULL) {
- ures_setResPath(r, r->fData->fPath);
- ures_appendResPath(r, RES_PATH_PACKAGE_S);
- ures_appendResPath(r, r->fData->fName);
- } else {
- ures_setResPath(r, r->fData->fName);
- }
- */
}
}
+
U_CAPI UResourceBundle* U_EXPORT2
ures_open(const char* path,
const char* localeID,
}
/* first "canonicalize" the locale ID */
- length = uloc_getName(localeID, canonLocaleID, sizeof(canonLocaleID), status);
+ length = uloc_getBaseName(localeID, canonLocaleID, sizeof(canonLocaleID), status);
if(U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) {
*status = U_ILLEGAL_ARGUMENT_ERROR;
return NULL;
uprv_free(r);
return NULL;
}
+ r->fParentRes = NULL;
+ r->fTopLevelData = r->fData;
hasData = r->fData;
while(hasData->fBogus != U_ZERO_ERROR) {
/*r->fParent = RES_BOGUS;*/
r->fSize = res_countArrayItems(&(r->fResData), r->fRes);
r->fResPath = NULL;
+ r->fResPathLen = 0;
/*
if(r->fData->fPath != NULL) {
ures_setResPath(r, r->fData->fPath);
return r;
}
-U_CAPI UResourceBundle* U_EXPORT2 ures_openU(const UChar* myPath,
- const char* localeID,
- UErrorCode* status)
-{
- UResourceBundle *r;
- int32_t pathSize = u_strlen(myPath) + 1;
- char *path = (char *)uprv_malloc(pathSize);
- /* test for NULL */
- if(path == NULL) {
- *status = U_MEMORY_ALLOCATION_ERROR;
- return NULL;
- }
-
- u_UCharsToChars(myPath, path, pathSize);
-
- r = ures_open(path, localeID, status);
- uprv_free(path);
-
- if (U_FAILURE(*status)) {
- return NULL;
- }
-
- return r;
-}
-
/**
* Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
* or sought. However, alias substitution will happen!
U_CAPI UResourceBundle* U_EXPORT2
ures_openDirect(const char* path, const char* localeID, UErrorCode* status) {
UResourceBundle *r;
+ UErrorCode subStatus = U_ZERO_ERROR;
if(status == NULL || U_FAILURE(*status)) {
return NULL;
r->fIsTopLevel = TRUE;
ures_setIsStackObject(r, FALSE);
r->fIndex = -1;
- r->fData = entryOpen(path, localeID, status);
- if(U_FAILURE(*status)) {
+ r->fData = entryOpen(path, localeID, &subStatus);
+ if(U_FAILURE(subStatus)) {
+ *status = subStatus;
uprv_free(r);
return NULL;
}
- if(*status != U_ZERO_ERROR /*r->fData->fBogus != U_ZERO_ERROR*/) {
+ if(subStatus != U_ZERO_ERROR /*r->fData->fBogus != U_ZERO_ERROR*/) {
/* we didn't find one we were looking for - so openDirect */
/* should fail */
entryClose(r->fData);
/*r->fParent = RES_BOGUS;*/
r->fSize = res_countArrayItems(&(r->fResData), r->fRes);
r->fResPath = NULL;
- /*
- if(r->fData->fPath != NULL) {
- ures_setResPath(r, r->fData->fPath);
- ures_appendResPath(r, RES_PATH_PACKAGE_S);
- ures_appendResPath(r, r->fData->fName);
- } else {
- ures_setResPath(r, r->fData->fName);
- }
- */
- return r;
-}
-
-U_CFUNC void ures_setIsStackObject( UResourceBundle* resB, UBool state) {
- if(state) {
- resB->fMagic1 = 0;
- resB->fMagic2 = 0;
- } else {
- resB->fMagic1 = MAGIC1;
- resB->fMagic2 = MAGIC2;
- }
-}
-
-U_CFUNC UBool ures_isStackObject(UResourceBundle* resB) {
- return((resB->fMagic1 == MAGIC1 && resB->fMagic2 == MAGIC2)?FALSE:TRUE);
-}
-
+ r->fResPathLen = 0;
+ r->fParentRes = NULL;
+ r->fTopLevelData = r->fData;
-U_CFUNC void ures_initStackObject(UResourceBundle* resB) {
- memset(resB, 0, sizeof(UResourceBundle));
- ures_setIsStackObject(resB, TRUE);
+ return r;
}
/**
{
UResourceBundle resData;
ures_initStackObject(&resData);
- if (status==NULL || U_FAILURE(*status)) {
- return 0;
- }
- if(resourceBundle == NULL) {
- *status = U_ILLEGAL_ARGUMENT_ERROR;
- return 0;
- }
+ if (status==NULL || U_FAILURE(*status)) {
+ return 0;
+ }
+ if(resourceBundle == NULL) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
+ return 0;
+ }
ures_getByKey(resourceBundle, resourceKey, &resData, status);
-
+
if(resData.fResData.data != NULL) {
- int32_t result = res_countArrayItems(&resData.fResData, resData.fRes);
- ures_close(&resData);
- return result;
+ int32_t result = res_countArrayItems(&resData.fResData, resData.fRes);
+ ures_close(&resData);
+ return result;
} else {
- *status = U_MISSING_RESOURCE_ERROR;
- ures_close(&resData);
- return 0;
+ *status = U_MISSING_RESOURCE_ERROR;
+ ures_close(&resData);
+ return 0;
}
}
if(ures_isStackObject(resB) == FALSE) {
uprv_free(resB);
}
+ else {
+#if 0 /*U_DEBUG*/
+ /* poison the data */
+ uprv_memset(resB, -1, sizeof(UResourceBundle));
+#endif
+ }
}
}
int32_t len;
const UChar* minor_version = ures_getStringByKey(resourceBundle, kVersionTag, &minor_len, &status);
-
+
/* Determine the length of of the final version string. This is */
/* the length of the major part + the length of the separator */
/* (==1) + the length of the minor part (+ 1 for the zero byte at */
((UResourceBundle *)resourceBundle)->fVersion = (char *)uprv_malloc(1 + len);
-
+
if(minor_len > 0) {
u_UCharsToChars(minor_version, resourceBundle->fVersion , minor_len);
resourceBundle->fVersion[len] = '\0';
}
else {
- uprv_strcat(resourceBundle->fVersion, kDefaultMinorVersion);
+ uprv_strcpy(resourceBundle->fVersion, kDefaultMinorVersion);
}
}
u_versionFromString(versionInfo, ures_getVersionNumber(resB));
}
+/** 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 = 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 = 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(&subItem);
+
+#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 = 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);
+}
+
/* eof */