// This file contains the tables for doing locale fallback, which are generated // by the CLDR-to-ICU process directly from the CLDR data. This file should only // ever be included from here. #define INCLUDED_FROM_URESBUND_CPP #include"localefallback_data.h"
// the default script will be "Latn" if we don't find the locale ID in the tables
CharString result("Latn", err);
// if we were passed both language and region, make them into a locale ID and look that up in the default // script table if (!region.isEmpty()) {
CharString localeID;
localeID.append(language, err).append("_", err).append(region, err); if (U_FAILURE(err)) { return result;
}
defaultScript = performFallbackLookup(localeID.data(), dsLocaleIDChars, scriptCodeChars, defaultScriptTable, UPRV_LENGTHOF(defaultScriptTable));
}
// if we didn't find anything, look up just the language in the default script table if (defaultScript == nullptr) {
defaultScript = performFallbackLookup(language.data(), dsLocaleIDChars, scriptCodeChars, defaultScriptTable, UPRV_LENGTHOF(defaultScriptTable));
}
// if either lookup above succeeded, copy the result from "defaultScript" into "result"; otherwise, return "Latn" if (defaultScript != nullptr) {
result.clear();
result.append(defaultScript, err);
} return result;
}
enum UResOpenType { /** *Openaresourcebundleforthelocale; *ifthereisnotevenabaselanguagebundle,thenfallbacktothedefaultlocale; *ifthereisnobundleforthateither,thenloadtherootbundle. * *Thisisthedefaultbundleloadingbehavior.
*/
URES_OPEN_LOCALE_DEFAULT_ROOT, // TODO: ICU ticket #11271 "consistent default locale across locale trees" // Add an option to look at the main locale tree for whether to // fall back to root directly (if the locale has main data) or // fall back to the default locale first (if the locale does not even have main data). /** *Openaresourcebundleforthelocale; *ifthereisnotevenabaselanguagebundle,thenloadtherootbundle; *neverfallbacktothedefaultlocale. * *Thisisusedforalgorithmsthathavegoodpan-Unicodedefaultbehavior, *suchascasemappings,collation,andsegmentation(BreakIterator).
*/
URES_OPEN_LOCALE_ROOT, /** *Openaresourcebundlefortheexactbundlenameasrequested; *nofallbacks,donotloadparentbundles. * *Thisisusedforsupplemental(non-locale)data.
*/
URES_OPEN_DIRECT
}; typedefenum UResOpenType UResOpenType;
/** *Internalfunction,determinesthesearchpathforresourcebundlefiles. *Currently,thisfunctionisusedonlybyfindFirstExisting()tohelpsearchforresourcebundlefileswhenabundleforthespecified *localedoesn'texist.Thecodethatsupportsinheritanceofresourcesbetweenexistingresourcebundlefilescontinuesto *usechopLocale()below. *@paramnameIn-outparameter:Oninput,thelocaleIDtogetaparentlocaleIDfor(thisisalocale'sbasename,withoutkeywords);onoutput,the *requestedparentlocaleID. *@paramorigNameTheoriginallocaleIDthecalleroffindFirstExisting()requested.Thisisthesameas`name`onthefirstcalltothisfunction, *butasfindFirstExisting()ascendstheresourcebundle'sparenttree,thisparameterwillcontinuetobetheoriginallocaleIDrequested.
*/ staticbool getParentLocaleID(char *name, constchar *origName, UResOpenType openType) { // early out if the locale ID has a variant code or ends with _
size_t nameLen = uprv_strlen(name); if (!nameLen || name[nameLen - 1] == '_' || hasVariant(name)) { return chopLocale(name);
}
if (U_FAILURE(err)) { // hopefully this never happens... return chopLocale(name);
}
// if the open type is URES_OPEN_LOCALE_DEFAULT_ROOT, first look the locale ID up in the parent locale table; // if that table specifies a parent for it, return that (we don't do this for the other open types-- if we're not // falling back through the system default locale, we also want to do straight truncation fallback instead // of looking things up in the parent locale table-- see https://www.unicode.org/reports/tr35/tr35.html#Parent_Locales: // "Collation data, however, is an exception...") if (openType == URES_OPEN_LOCALE_DEFAULT_ROOT) { constchar* parentID = performFallbackLookup(name, parentLocaleChars, parentLocaleChars, parentLocaleTable, UPRV_LENGTHOF(parentLocaleTable)); if (parentID != nullptr) {
uprv_strcpy(name, parentID); returntrue;
}
}
CharString workingLocale;
// if it's not in the parent locale table, figure out the fallback script algorithmically // (see CLDR-15265 for an explanation of the algorithm) if (!script.isEmpty() && !region.isEmpty()) { // if "name" has both script and region, is the script the default script? // - if so, remove it and keep the region // - if not, remove the region and keep the script if (getDefaultScript(language, region) == script) {
workingLocale.append(language, err).append("_", err).append(region, err);
} else {
workingLocale.append(language, err).append("_", err).append(script, err);
}
} elseif (!region.isEmpty()) { // if "name" has region but not script, did the original locale ID specify a script? // - if yes, replace the region with the script from the original locale ID // - if no, replace the region with the default script for that language and region
UErrorCode err = U_ZERO_ERROR;
CharString origNameLanguage;
CharString origNameScript;
ulocimp_getSubtags(origName, &origNameLanguage, &origNameScript, nullptr, nullptr, nullptr, err); if (!origNameScript.isEmpty()) {
workingLocale.append(language, err).append("_", err).append(origNameScript, err);
} else {
workingLocale.append(language, err).append("_", err).append(getDefaultScript(language, region), err);
}
} elseif (!script.isEmpty()) { // if "name" has script but not region (and our open type if URES_OPEN_LOCALE_DEFAULT_ROOT), is the script // the default script for the language? // - if so, remove it from the locale ID // - if not, return false to continue up the chain // (we don't do this for other open types for the same reason we don't look things up in the parent // locale table for other open types-- see the reference to UTS #35 above) if (openType != URES_OPEN_LOCALE_DEFAULT_ROOT || getDefaultScript(language, CharString()) == script) {
workingLocale.append(language, err);
} else { returnfalse;
}
} else { // if "name" just contains a language code, return false so the calling code falls back to "root" returnfalse;
} if (U_SUCCESS(err) && !workingLocale.isEmpty()) {
uprv_strcpy(name, workingLocale.data()); returntrue;
} else { returnfalse;
}
}
if(*res == RES_BOGUS) { // If the resource is not found, we need to give an error.
*status = U_MISSING_RESOURCE_ERROR; return nullptr;
} // If the resource is found in parents, we need to adjust the error. if(i>1) { if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) {
*status = U_USING_DEFAULT_WARNING;
} else {
*status = U_USING_FALLBACK_WARNING;
}
} return dataEntry;
}
/* Works just like ucnv_flushCache() */ static int32_t ures_flushCache()
{
UResourceDataEntry *resB;
int32_t pos;
int32_t rbDeletedNum = 0; const UHashElement *e;
UBool deletedMore;
/*if shared data hasn't even been lazy evaluated yet *return0
*/
Mutex lock(&resbMutex); if (cache == nullptr) { return0;
}
do {
deletedMore = false; /*creates an enumeration to iterate through every element in the table */
pos = UHASH_FIRST; while ((e = uhash_nextElement(cache, &pos)) != nullptr)
{
resB = static_cast<UResourceDataEntry*>(e->value.pointer); /* Deletes only if reference counter == 0 *Don'tworryaboutthechildrenofthisnode. *Thosewilleventuallygetdeletedtoo,ifnotalready. *Don'tworryabouttheparentsofthisnode. *Thosewilleventuallygetdeletedtoo,ifnotalready.
*/ /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */ /* some resource bundles are still open somewhere. */
/* here we try to deduce the right locale name */ if(localeID == nullptr) { /* if localeID is nullptr, we're trying to open default locale */
name = uloc_getDefault();
} elseif(*localeID == 0) { /* if localeID is "" then we try to open root locale */
name = kRootLocaleName;
} else { /* otherwise, we'll open what we're given */
name = localeID;
}
/* calculate the hash value of the entry */ /*hashkey.pointer = (void *)&find;*/ /*hashValue = hashEntry(hashkey);*/
/* check to see if we already have this entry */
r = static_cast<UResourceDataEntry*>(uhash_get(cache, &find)); if(r == nullptr) { /* if the entry is not yet in the hash table, we'll try to construct a new one */
r = static_cast<UResourceDataEntry*>(uprv_malloc(sizeof(UResourceDataEntry))); if(r == nullptr) {
*status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
}
/* this is the actual loading */
res_load(&(r->fData), r->fPath, r->fName, status);
if (U_FAILURE(*status)) { /* if we failed to load due to an out-of-memory error, exit early. */ if (*status == U_MEMORY_ALLOCATION_ERROR) {
uprv_free(r); return nullptr;
} /* we have no such entry in dll, so it will always use fallback */
*status = U_USING_FALLBACK_WARNING;
r->fBogus = U_USING_FALLBACK_WARNING;
} else { /* if we have a regular entry */
Resource aliasres; if (r->fData.usesPoolBundle) {
r->fPool = getPoolEntry(r->fPath, status); if (U_SUCCESS(*status)) { const int32_t *poolIndexes = r->fPool->fData.pRoot + 1; if(r->fData.pRoot[1 + URES_INDEX_POOL_CHECKSUM] == poolIndexes[URES_INDEX_POOL_CHECKSUM]) {
r->fData.poolBundleKeys = reinterpret_cast<constchar*>(poolIndexes + (poolIndexes[URES_INDEX_LENGTH] & 0xff));
r->fData.poolBundleStrings = r->fPool->fData.p16BitUnits;
} else {
r->fBogus = *status = U_INVALID_FORMAT_ERROR;
}
} else {
r->fBogus = *status;
}
} if (U_SUCCESS(*status)) { /* handle the alias by trying to get out the %%Alias tag.*/ /* We'll try to get alias string from the bundle */
aliasres = res_getResource(&(r->fData), "%%ALIAS"); if (aliasres != RES_BOGUS) { // No tracing: called during initial data loading const char16_t *alias = res_getStringNoTrace(&(r->fData), aliasres, &aliasLen); if(alias != nullptr && aliasLen > 0) { /* if there is actual alias - unload and load new data */
u_UCharsToChars(alias, aliasName, aliasLen+1);
r->fAlias = init_entry(aliasName, path, status);
}
}
}
}
{
UResourceDataEntry *oldR = nullptr; if ((oldR = static_cast<UResourceDataEntry*>(uhash_get(cache, r))) == nullptr) { /* if the data is not cached */ /* just insert it in the cache */
UErrorCode cacheStatus = U_ZERO_ERROR;
uhash_put(cache, (void *)r, r, &cacheStatus); if (U_FAILURE(cacheStatus)) {
*status = cacheStatus;
free_entry(r);
r = nullptr;
}
} else { /* somebody have already inserted it while we were working, discard newly opened data */ /* Also, we could get here IF we opened an alias */
free_entry(r);
r = oldR;
}
}
} if(r != nullptr) { /* return the real bundle */ while(r->fAlias != nullptr) {
r = r->fAlias;
}
r->fCountExisting++; /* we increase its reference count */ /* if the resource has a warning */ /* we don't want to overwrite a status with no error */ if(r->fBogus != U_ZERO_ERROR && U_SUCCESS(*status)) {
*status = r->fBogus; /* set the returning status */
}
} return r;
}
/* INTERNAL: */ /* CAUTION: resbMutex must be locked when calling this function! */ static UResourceDataEntry *
findFirstExisting(constchar* path, char* name, constchar* defaultLocale, UResOpenType openType,
UBool *isRoot, UBool *foundParent, UBool *isDefault, UErrorCode* status) {
UResourceDataEntry *r = nullptr;
UBool hasRealData = false;
*foundParent = true; /* we're starting with a fresh name */ char origName[ULOC_FULLNAME_CAPACITY];
uprv_strcpy(origName, name); while(*foundParent && !hasRealData) {
r = init_entry(name, path, status); /* Null pointer test */ if (U_FAILURE(*status)) { return nullptr;
}
*isDefault = static_cast<UBool>(uprv_strncmp(name, defaultLocale, uprv_strlen(name)) == 0);
hasRealData = static_cast<UBool>(r->fBogus == U_ZERO_ERROR); if(!hasRealData) { /* this entry is not real. We will discard it. */ /* However, the parent line for this entry is */ /* not to be used - as there might be parent */ /* lines in cache from previous openings that */ /* are not updated yet. */
r->fCountExisting--; /*entryCloseInt(r);*/
r = nullptr;
*status = U_USING_FALLBACK_WARNING;
} else {
uprv_strcpy(name, r->fName); /* this is needed for supporting aliases */
}
/*Fallback data stuff*/ if (!hasRealData) {
*foundParent = getParentLocaleID(name, origName, openType);
} else { // we've already found a real resource file; what we return to the caller is the parent // locale ID for inheritance, which should come from chopLocale(), not getParentLocaleID()
*foundParent = chopLocale(name);
} if (*foundParent && *name == '\0') {
uprv_strcpy(name, "und");
}
} return r;
}
// Note: We need to query the default locale *before* locking resbMutex. constchar *defaultLocale = uloc_getDefault();
Mutex lock(&resbMutex); // Lock resbMutex until the end of this function.
/* We're going to skip all the locales that do not have any data */
r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus);
// If we failed due to out-of-memory, report the failure and exit early. if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
*status = intStatus; goto finish;
}
if(r != nullptr) { /* if there is one real locale, we can look for parents. */
t1 = r;
hasRealData = true; if ( usingUSRData ) { /* This code inserts user override data into the inheritance chain */
UErrorCode usrStatus = U_ZERO_ERROR;
UResourceDataEntry *u1 = init_entry(t1->fName, usrDataPath, &usrStatus); // If we failed due to out-of-memory, report the failure and exit early. if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
*status = intStatus; goto finish;
} if ( u1 != nullptr ) { if(u1->fBogus == U_ZERO_ERROR) {
u1->fParent = t1;
r = u1;
} else { /* the USR override data wasn't found, set it to be deleted */
u1->fCountExisting = 0;
}
}
} if ((hasChopped || mayHaveParent(name)) && !isRoot) { if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) { goto finish;
}
}
}
/* we could have reached this point without having any real data */ /* if that is the case, we need to chain in the default locale */ if(r==nullptr && openType == URES_OPEN_LOCALE_DEFAULT_ROOT && !isDefault && !isRoot) { /* insert default locale */
uprv_strcpy(name, defaultLocale);
r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus); // If we failed due to out-of-memory, report the failure and exit early. if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
*status = intStatus; goto finish;
}
intStatus = U_USING_DEFAULT_WARNING; if(r != nullptr) { /* the default locale exists */
t1 = r;
hasRealData = true;
isDefault = true; // TODO: Why not if (usingUSRData) { ... } like in the non-default-locale code path? if ((hasChopped || mayHaveParent(name)) && !isRoot) { if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) { goto finish;
}
}
}
}
/* we could still have r == nullptr at this point - maybe even default locale is not */ /* present */ if(r == nullptr) {
uprv_strcpy(name, kRootLocaleName);
r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus); // If we failed due to out-of-memory, report the failure and exit early. if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
*status = intStatus; goto finish;
} if(r != nullptr) {
t1 = r;
intStatus = U_USING_DEFAULT_WARNING;
hasRealData = true;
} else { /* we don't even have the root locale */
*status = U_MISSING_RESOURCE_ERROR; goto finish;
}
} elseif(!isRoot && uprv_strcmp(t1->fName, kRootLocaleName) != 0 &&
t1->fParent == nullptr && !r->fData.noFallback) { if (!insertRootBundle(t1, status)) { goto finish;
} if(!hasRealData) {
r->fBogus = U_USING_DEFAULT_WARNING;
}
}
// Note: We need to query the default locale *before* locking resbMutex. // If the localeID is nullptr, then we want to use the default locale. if (localeID == nullptr) {
localeID = uloc_getDefault();
} elseif (*localeID == 0) { // If the localeID is "", then we want to use the root locale.
localeID = kRootLocaleName;
}
Mutex lock(&resbMutex);
// findFirstExisting() without fallbacks.
UResourceDataEntry *r = init_entry(localeID, path, status); if(U_SUCCESS(*status)) { if(r->fBogus != U_ZERO_ERROR) {
r->fCountExisting--;
r = nullptr;
}
} else {
r = nullptr;
}
// Some code depends on the ures_openDirect() bundle to have a parent bundle chain, // unless it is marked with "nofallback".
UResourceDataEntry *t1 = r; if(r != nullptr && uprv_strcmp(localeID, kRootLocaleName) != 0 && // not root
r->fParent == nullptr && !r->fData.noFallback &&
uprv_strlen(localeID) < ULOC_FULLNAME_CAPACITY) { char name[ULOC_FULLNAME_CAPACITY];
uprv_strcpy(name, localeID); if(!chopLocale(name) || uprv_strcmp(name, kRootLocaleName) == 0 ||
loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), false, nullptr, status)) { if(uprv_strcmp(t1->fName, kRootLocaleName) != 0 && t1->fParent == nullptr) {
insertRootBundle(t1, status);
}
} if(U_FAILURE(*status)) {
r = nullptr;
}
}
while(resB != nullptr) {
p = resB->fParent;
resB->fCountExisting--;
/* Entries are left in the cache. TODO: add ures_flushCache() to force a flush
of the cache. */ /* if(resB->fCountExisting<=0){ uhash_remove(cache,resB); if(resB->fBogus==U_ZERO_ERROR){ res_unload(&(resB->fData)); } if(resB->fName!=nullptr){ uprv_free(resB->fName); } if(resB->fPath!=nullptr){ uprv_free(resB->fPath); } uprv_free(resB); }
*/
// TODO: Try to refactor further, so that we output a dataEntry + Resource + (optionally) resPath, // rather than a UResourceBundle. // May need to entryIncrease() the resulting dataEntry.
UResourceBundle *getAliasTargetAsResourceBundle( const ResourceData &resData, Resource r, constchar *key, int32_t idx,
UResourceDataEntry *validLocaleDataEntry, constchar *containerResPath,
int32_t recursionDepth,
UResourceBundle *resB, UErrorCode *status) { // TODO: When an error occurs: Should we return nullptr vs. resB? if (U_FAILURE(*status)) { return resB; }
U_ASSERT(RES_GET_TYPE(r) == URES_ALIAS);
int32_t len = 0; const char16_t *alias = res_getAlias(&resData, r, &len); if(len <= 0) { // bad alias
*status = U_ILLEGAL_ARGUMENT_ERROR; return resB;
}
// Copy the UTF-16 alias string into an invariant-character string. // // 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
CharString chAlias;
chAlias.appendInvariantChars(alias, len, *status); if (U_FAILURE(*status)) { return nullptr;
}
// We have an alias, now let's cut it up. constchar *path = nullptr, *locale = nullptr, *keyPath = nullptr; if(chAlias[0] == RES_PATH_SEPARATOR) { // There is a path included. char *chAliasData = chAlias.data(); char *sep = chAliasData + 1;
path = sep;
sep = uprv_strchr(sep, RES_PATH_SEPARATOR); if(sep != nullptr) {
*sep++ = 0;
} if(uprv_strcmp(path, "LOCALE") == 0) { // This is an XPath alias, starting with "/LOCALE/". // It contains the path to a resource which should be looked up // starting in the valid locale. // TODO: Can/should we forbid a /LOCALE alias without key path? // It seems weird to alias to the same path, just starting from the valid locale. // That will often yield an infinite loop.
keyPath = sep; // Read from the valid locale which we already have.
path = locale = nullptr;
} else { if(uprv_strcmp(path, "ICUDATA") == 0) { /* want ICU data */
path = nullptr;
} if (sep == nullptr) { // TODO: This ends up using the root bundle. Can/should we forbid this?
locale = "";
} else {
locale = sep;
sep = uprv_strchr(sep, RES_PATH_SEPARATOR); if(sep != nullptr) {
*sep++ = 0;
}
keyPath = sep;
}
}
} else { // No path, start with a locale. char *sep = chAlias.data();
locale = sep;
sep = uprv_strchr(sep, RES_PATH_SEPARATOR); if(sep != nullptr) {
*sep++ = 0;
}
keyPath = sep;
path = validLocaleDataEntry->fPath;
}
// Got almost everything, let's try to open. // First, open the bundle with real data.
LocalUResourceBundlePointer mainRes;
UResourceDataEntry *dataEntry; if (locale == nullptr) { // alias = /LOCALE/keyPath // Read from the valid locale which we already have.
dataEntry = validLocaleDataEntry;
} else {
UErrorCode intStatus = U_ZERO_ERROR; // TODO: Shouldn't we use ures_open() for locale data bundles (!noFallback)?
mainRes.adoptInstead(ures_openDirect(path, locale, &intStatus)); if(U_FAILURE(intStatus)) { // We failed to open the resource bundle we're aliasing to.
*status = intStatus; return resB;
}
dataEntry = mainRes->fData;
}
constchar* temp = nullptr; if(keyPath == nullptr) { // No key path. This means that we are going to to use the corresponding resource from // another bundle. // TODO: Why the special code path? // Why not put together a key path from containerResPath + key or idx, // as a comment below suggests, and go into the regular code branch? // First, we are going to get a corresponding container // resource to the one we are searching.
r = dataEntry->fData.rootRes; if(containerResPath) {
chAlias.clear().append(containerResPath, *status); if (U_FAILURE(*status)) { return nullptr;
} char *aKey = chAlias.data(); // TODO: should res_findResource() return a new dataEntry, too?
r = res_findResource(&dataEntry->fData, r, &aKey, &temp);
} if(key) { // We need to make keyPath from the containerResPath and // current key, if there is a key associated.
chAlias.clear().append(key, *status); if (U_FAILURE(*status)) { return nullptr;
} char *aKey = chAlias.data();
r = res_findResource(&dataEntry->fData, r, &aKey, &temp);
} elseif(idx != -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.
int32_t type = RES_GET_TYPE(r); if(URES_IS_TABLE(type)) { constchar *aKey;
r = res_getTableItemByIndex(&dataEntry->fData, r, idx, &aKey);
} else { /* array */
r = res_getArrayItem(&dataEntry->fData, r, idx);
}
} if(r != RES_BOGUS) {
resB = init_resb_result(
dataEntry, r, temp, -1, validLocaleDataEntry, nullptr, recursionDepth+1,
resB, status);
} else {
*status = U_MISSING_RESOURCE_ERROR;
}
} else { // This one is a bit trickier. // We start finding keys, but after we resolve one alias, the path might continue. // Consider: // aliastest:alias { "testtypes/anotheralias/Sequence" } // anotheralias:alias { "/ICUDATA/sh/CollationElements" } // aliastest resource should finally have the sequence, not collation elements.
CharString pathBuf(keyPath, *status); if (U_FAILURE(*status)) { return nullptr;
} char *myPath = pathBuf.data();
containerResPath = nullptr; // Now we have fallback following here. for(;;) {
r = dataEntry->fData.rootRes; // TODO: Move containerResPath = nullptr to here, // consistent with restarting from the rootRes of another bundle?!
// This loop handles 'found' resources over several levels. while(*myPath && U_SUCCESS(*status)) {
r = res_findResource(&(dataEntry->fData), r, &myPath, &temp); if(r == RES_BOGUS) { // No resource found, we don't really want to look anymore on this level. break;
} // Found a resource, but it might be an indirection.
resB = init_resb_result(
dataEntry, r, temp, -1,
validLocaleDataEntry, containerResPath, recursionDepth+1,
resB, status); if (U_FAILURE(*status)) { break;
} if (temp == nullptr || uprv_strcmp(keyPath, temp) != 0) { // The call to init_resb_result() above will set resB->fKeyPath to be // the same as resB->fKey, // throwing away any additional path elements if we had them -- // if the key path wasn't just a single resource ID, clear out // the bundle's key path and re-set it to be equal to keyPath.
ures_freeResPath(resB);
ures_appendResPath(resB, keyPath, static_cast<int32_t>(uprv_strlen(keyPath)), status); if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
} if (U_FAILURE(*status)) { break;
}
}
r = resB->fRes; /* switch to a new resource, possibly a new tree */
dataEntry = resB->fData;
containerResPath = resB->fResPath;
} if (U_FAILURE(*status) || r != RES_BOGUS) { break;
} // Fall back to the parent bundle, if there is one.
dataEntry = dataEntry->fParent; if (dataEntry == nullptr) {
*status = U_MISSING_RESOURCE_ERROR; break;
} // Copy the same keyPath again.
myPath = pathBuf.data();
uprv_strcpy(myPath, keyPath);
}
} if(mainRes.getAlias() == resB) {
mainRes.orphan();
}
ResourceTracer(resB).maybeTrace("getalias"); return resB;
}
// Recursive function, should be called only by itself, by its simpler wrapper, // or by getAliasTargetAsResourceBundle().
UResourceBundle *init_resb_result(
UResourceDataEntry *dataEntry, Resource r, constchar *key, int32_t idx,
UResourceDataEntry *validLocaleDataEntry, constchar *containerResPath,
int32_t recursionDepth,
UResourceBundle *resB, UErrorCode *status) { // TODO: When an error occurs: Should we return nullptr vs. resB? if(status == nullptr || U_FAILURE(*status)) { return resB;
} if (validLocaleDataEntry == nullptr) {
*status = U_ILLEGAL_ARGUMENT_ERROR; return nullptr;
} if(RES_GET_TYPE(r) == URES_ALIAS) { // This is an alias, need to exchange with real data. if(recursionDepth >= URES_MAX_ALIAS_LEVEL) {
*status = U_TOO_MANY_ALIASES_ERROR; return resB;
} return getAliasTargetAsResourceBundle(
dataEntry->fData, r, key, idx,
validLocaleDataEntry, containerResPath, recursionDepth, resB, status);
} if(resB == nullptr) {
resB = static_cast<UResourceBundle*>(uprv_malloc(sizeof(UResourceBundle))); if (resB == nullptr) {
*status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
}
ures_setIsStackObject(resB, false);
resB->fResPath = nullptr;
resB->fResPathLen = 0;
} else { if(resB->fData != nullptr) {
entryClose(resB->fData);
} if(resB->fVersion != nullptr) {
uprv_free(resB->fVersion);
} /* weiv:ifstackobjectwaspassedin,itdoesn'treallyneedtobereinited, sincethepurposeofinitingistoremovestackjunk.However,atthispoint wewouldnotdoanythingtoanallocatedobject,sostackobjectshouldbe treatedthesame
*/ /* if(ures_isStackObject(resB)!=false){ ures_initStackObject(resB); }
*/ if(containerResPath != resB->fResPath) {
ures_freeResPath(resB);
}
}
resB->fData = dataEntry;
entryIncrease(resB->fData);
resB->fHasFallback = false;
resB->fIsTopLevel = false;
resB->fIndex = -1;
resB->fKey = key;
resB->fValidLocaleDataEntry = validLocaleDataEntry; if(containerResPath != resB->fResPath) {
ures_appendResPath(
resB, containerResPath, static_cast<int32_t>(uprv_strlen(containerResPath)), status);
} if(key != nullptr) {
ures_appendResPath(resB, key, static_cast<int32_t>(uprv_strlen(key)), status); if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
}
} elseif(idx >= 0) { char buf[256];
int32_t len = T_CString_integerToString(buf, idx, 10);
ures_appendResPath(resB, buf, len, status); if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
}
} /* Make sure that Purify doesn't complain about uninitialized memory copies. */
{
int32_t usedLen = ((resB->fResBuf == resB->fResPath) ? resB->fResPathLen : 0);
uprv_memset(resB->fResBuf + usedLen, 0, sizeof(resB->fResBuf) - usedLen);
}
U_CAPI constchar * U_EXPORT2 ures_getKey(const UResourceBundle *resB) { // // TODO: Trace ures_getKey? I guess not usually. // // We usually get the key string to decide whether we want the value, or to // make a key-value pair. Tracing the value should suffice. // // However, I believe we have some data (e.g., in res_index) where the key // strings are the data. Tracing the enclosing table should suffice. // if(resB == nullptr) { return nullptr;
} return(resB->fKey);
}
/* 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->getResData(), resB->fRes, &path, &key); if(res != RES_BOGUS) {
result = init_resb_result(resB->fData, res, key, -1, resB, fillIn, status);
resB = result;
} else {
*status = U_MISSING_RESOURCE_ERROR; break;
}
} while(*path); /* there is more stuff in the path */
/* Likeres_getTableItemByKeybutacceptsfullpathslike"NumberElements/latn/patternsShort".
*/ static Resource getTableItemByKeyPath(const ResourceData *pResData, Resource table, constchar *key) {
Resource resource = table; /* The current resource */
icu::CharString path;
UErrorCode errorCode = U_ZERO_ERROR;
path.append(key, errorCode); if (U_FAILURE(errorCode)) { return RES_BOGUS; } char *pathPart = path.data(); /* Path from current resource to desired resource */
UResType type = static_cast<UResType>(RES_GET_TYPE(resource)); /* the current resource type */ while (*pathPart && resource != RES_BOGUS && URES_IS_CONTAINER(type)) { char *nextPathPart = uprv_strchr(pathPart, RES_PATH_SEPARATOR); if (nextPathPart != nullptr) {
*nextPathPart = 0; /* Terminating null for this part of path. */
nextPathPart++;
} else {
nextPathPart = uprv_strchr(pathPart, 0);
}
int32_t t; constchar *pathP = pathPart;
resource = res_getTableItemByKey(pResData, resource, &t, &pathP);
type = static_cast<UResType>(RES_GET_TYPE(resource));
pathPart = nextPathPart;
} if (*pathPart) { return RES_BOGUS;
} return resource;
}
staticvoid createPath(constchar* origResPath,
int32_t origResPathLen, constchar* resPath,
int32_t resPathLen, constchar* inKey,
CharString& path,
UErrorCode* status) { // This is a utility function used by ures_getByKeyWithFallback() below. This function builds a path from // resPath and inKey, returning the result in `path`. Originally, this function just cleared `path` and // appended resPath and inKey to it, but that caused problems for horizontal inheritance. // // In normal cases, resPath is the same as origResPath, but if ures_getByKeyWithFallback() has followed an // alias, resPath may be different from origResPath. Not only may the existing path elements be different, // but resPath may also have MORE path elements than origResPath did. If it does, those additional path // elements SUPERSEDE the corresponding elements of inKey. So this code counts the number of elements in // resPath and origResPath and, for each path element in resPath that doesn't have a counterpart in origResPath, // deletes a path element from the beginning of inKey. The remainder of inKey is then appended to // resPath to form the result. (We're not using uprv_strchr() here because resPath and origResPath may // not be zero-terminated.)
path.clear(); constchar* key = inKey; if (resPathLen > 0) {
path.append(resPath, resPathLen, *status); if (U_SUCCESS(*status)) { constchar* resPathLimit = resPath + resPathLen; constchar* origResPathLimit = origResPath + origResPathLen; constchar* resPathPtr = resPath; constchar* origResPathPtr = origResPath;
// Remove from the beginning of resPath the number of segments that are contained in origResPath. // If origResPath has MORE segments than resPath, this will leave resPath as the empty string. while (origResPathPtr < origResPathLimit && resPathPtr < resPathLimit) { while (origResPathPtr < origResPathLimit && *origResPathPtr != RES_PATH_SEPARATOR) {
++origResPathPtr;
} if (origResPathPtr < origResPathLimit && *origResPathPtr == RES_PATH_SEPARATOR) {
++origResPathPtr;
} while (resPathPtr < resPathLimit && *resPathPtr != RES_PATH_SEPARATOR) {
++resPathPtr;
} if (resPathPtr < resPathLimit && *resPathPtr == RES_PATH_SEPARATOR) {
++resPathPtr;
}
}
// New remove from the beginning of `key` the number of segments remaining in resPath. // If resPath has more segments than `key` does, `key` will end up empty. while (resPathPtr < resPathLimit && *key != '\0') { while (resPathPtr < resPathLimit && *resPathPtr != RES_PATH_SEPARATOR) {
++resPathPtr;
} if (resPathPtr < resPathLimit && *resPathPtr == RES_PATH_SEPARATOR) {
++resPathPtr;
} while (*key != '\0' && *key != RES_PATH_SEPARATOR) {
++key;
} if (*key == RES_PATH_SEPARATOR) {
++key;
}
}
} // Finally, append what's left of `key` to `path`. What you end up with here is `resPath`, plus // any pieces of `key` that aren't superseded by `resPath`. // Or, to put it another way, calculate <#-segments-in-key> - (<#-segments-in-resPath> - <#-segments-in-origResPath>), // and append that many segments from the end of `key` to `resPath` to produce the result.
path.append(key, *status);
} else {
path.append(inKey, *status);
}
}
int32_t type = RES_GET_TYPE(resB->fRes); if(URES_IS_TABLE(type)) { constchar* origResPath = resB->fResPath;
int32_t origResPathLen = resB->fResPathLen;
res = getTableItemByKeyPath(&resB->getResData(), resB->fRes, inKey); constchar* key = inKey; bool didRootOnce = false; if(res == RES_BOGUS) {
UResourceDataEntry *dataEntry = resB->fData;
CharString path; char *myPath = nullptr; constchar* resPath = resB->fResPath;
int32_t len = resB->fResPathLen; while(res == RES_BOGUS && (dataEntry->fParent != nullptr || !didRootOnce)) { /* Otherwise, we'll look in parents */ if (dataEntry->fParent != nullptr) {
dataEntry = dataEntry->fParent;
} else { // We can't just stop when we get to a bundle whose fParent is nullptr. That'll work most of the time, // but if the bundle that the caller passed to us was "root" (which happens in getAllItemsWithFallback(), // this function will drop right out without doing anything if "root" doesn't contain the exact key path // specified. In that case, we need one extra time through this loop to make sure we follow any // applicable aliases at the root level.
didRootOnce = true;
}
rootRes = dataEntry->fData.rootRes;
if(dataEntry->fBogus == U_ZERO_ERROR) {
createPath(origResPath, origResPathLen, resPath, len, inKey, path, status); if (U_FAILURE(*status)) {
ures_close(helper); return fillIn;
}
myPath = path.data();
key = inKey; do {
res = res_findResource(&(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, res, nullptr, -1, resB, helper, status); /*helper = init_resb_result(dataEntry, res, inKey, -1, resB, helper, status);*/ if(helper) {
dataEntry = helper->fData;
rootRes = helper->fRes;
resPath = helper->fResPath;
len = helper->fResPathLen;
} else { break;
}
} elseif (res == RES_BOGUS) { break;
}
} while(*myPath); /* Continue until the whole path is consumed */
}
} /*dataEntry = getFallbackData(resB, &key, &res, status);*/ if(res != RES_BOGUS) { /* check if resB->fResPath gives the right name here */ if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) {
*status = U_USING_DEFAULT_WARNING;
} else {
*status = U_USING_FALLBACK_WARNING;
}
void getAllItemsWithFallback( const UResourceBundle *bundle, ResourceDataValue &value,
ResourceSink &sink, UErrorCode &errorCode) { if (U_FAILURE(errorCode)) { return; } // We recursively enumerate child-first, // only storing parent items in the absence of child items. // The sink needs to store a placeholder value for the no-fallback/no-inheritance marker // to prevent a parent item from being stored. // // It would be possible to recursively enumerate parent-first, // overriding parent items with child items. // When the sink sees the no-fallback/no-inheritance marker, // then it would remove the parent's item. // We would deserialize parent values even though they are overridden in a child bundle.
value.setData(bundle->getResData());
value.setValidLocaleDataEntry(bundle->fValidLocaleDataEntry);
UResourceDataEntry *parentEntry = bundle->fData->fParent;
UBool hasParent = parentEntry != nullptr && U_SUCCESS(parentEntry->fBogus);
value.setResource(bundle->fRes, ResourceTracer(bundle));
sink.put(bundle->fKey, value, !hasParent, errorCode); if (hasParent) { // We might try to query the sink whether // any fallback from the parent bundle is still possible.
// Turn the parent UResourceDataEntry into a UResourceBundle, // much like in ures_openWithType(). // TODO: See if we can refactor ures_getByKeyWithFallback() // and pull out an inner function that takes and returns a UResourceDataEntry // so that we need not create UResourceBundle objects.
StackUResourceBundle parentBundle;
UResourceBundle &parentRef = parentBundle.ref();
parentRef.fData = parentEntry;
parentRef.fValidLocaleDataEntry = bundle->fValidLocaleDataEntry;
parentRef.fHasFallback = !parentRef.getResData().noFallback;
parentRef.fIsTopLevel = true;
parentRef.fRes = parentRef.getResData().rootRes;
parentRef.fSize = res_countArrayItems(&parentRef.getResData(), parentRef.fRes);
parentRef.fIndex = -1;
entryIncrease(parentEntry);
// Look up the container item in the parent bundle.
StackUResourceBundle containerBundle; const UResourceBundle *rb;
UErrorCode pathErrorCode = U_ZERO_ERROR; // Ignore if parents up to root do not have this path. if (bundle->fResPath == nullptr || *bundle->fResPath == 0) {
rb = parentBundle.getAlias();
} else {
rb = ures_getByKeyWithFallback(parentBundle.getAlias(), bundle->fResPath,
containerBundle.getAlias(), &pathErrorCode);
} if (U_SUCCESS(pathErrorCode)) {
getAllItemsWithFallback(rb, value, sink, errorCode);
}
}
}
if (aliasedValue.getType() != URES_TABLE) {
dest.put(key, aliasedValue, isRoot, errorCode);
} else { // if the resource we're aliasing over to is a table, the sink might iterate over its contents. // If it does, it'll get only the things defined in the actual alias target, not the things // the target inherits from its parent resources. So we walk the parent chain for the *alias target*, // calling dest.put() for each of the parent tables we could be inheriting from. This means // that dest.put() has to iterate over the children of multiple tables to get all of the inherited // resource values, but it already has to do that to handle normal vertical inheritance.
UResType aliasedValueType = URES_TABLE;
CharString tablePath;
tablePath.append(aliasRB->fResPath, errorCode); constchar* parentKey = key; // dest.put() changes the key
dest.put(parentKey, aliasedValue, isRoot, errorCode);
UResourceDataEntry* entry = aliasRB->fData;
Resource res = aliasRB->fRes; while (aliasedValueType == URES_TABLE && entry->fParent != nullptr) {
CharString localPath;
localPath.copyFrom(tablePath, errorCode); char* localPathAsCharPtr = localPath.data(); constchar* childKey;
entry = entry->fParent;
res = entry->fData.rootRes;
Resource newRes = res_findResource(&entry->fData, res, &localPathAsCharPtr, &childKey); if (newRes != RES_BOGUS) {
aliasedValue.setData(entry->fData); // TODO: do I also need to call aliasedValue.setValueLocaleDataEntry() ?
aliasedValue.setResource(newRes, ResourceTracer(aliasRB)); // probably wrong to use aliasRB here
aliasedValueType = aliasedValue.getType(); if (aliasedValueType == URES_ALIAS) { // in a few rare cases, when we get to the root resource bundle, the resource in question // won't be an actual table, but will instead be an alias to a table. That is, we have // two aliases in the inheritance path. (For some locales, such as Zulu, we see this with // children of the "fields" resource: "day-narrow" aliases to "day-short", which aliases // to "day".) When this happens, we need to make sure we follow all the aliases.
ResourceDataValue& rdv2 = static_cast<ResourceDataValue&>(aliasedValue);
aliasRB = getAliasTargetAsResourceBundle(rdv2.getData(), rdv2.getResource(), nullptr, -1,
rdv2.getValidLocaleDataEntry(), nullptr, 0,
stackTempBundle.getAlias(), &errorCode);
tablePath.clear();
tablePath.append(aliasRB->fResPath, errorCode);
entry = aliasRB->fData;
res = aliasRB->fRes;
aliasedValue.setData(entry->fData); // TODO: do I also need to call aliasedValue.setValueLocaleDataEntry() ?
aliasedValue.setResource(res, ResourceTracer(aliasRB)); // probably wrong to use aliasRB here
aliasedValueType = aliasedValue.getType();
} if (aliasedValueType == URES_TABLE) {
dest.put(parentKey, aliasedValue, isRoot, errorCode);
} else { // once we've followed the alias, the resource we're looking at really should // be a table
errorCode = U_INTERNAL_PROGRAM_ERROR; return;
}
}
}
}
}
} else {
dest.put(key, value, isRoot, errorCode);
} if (U_FAILURE(errorCode)) { return; }
}
}
};
// Virtual destructors must be defined out of line.
GetAllChildrenSink::~GetAllChildrenSink() {}
// Requires a ResourceDataValue fill-in, so that we need not cast from a ResourceValue. // Unfortunately, the caller must know which subclass to make and pass in. // Alternatively, we could make it as polymorphic as in Java by // returning a ResourceValue pointer (possibly wrapped into a LocalPointer) // that the caller then owns. // // Also requires a UResourceBundle fill-in, so that the value's ResourceTracer // can point to a non-local bundle. // Without tracing, the child bundle could be a function-local object.
U_CAPI void U_EXPORT2
ures_getValueWithFallback(const UResourceBundle *bundle, constchar *path,
UResourceBundle *tempFillIn,
ResourceDataValue &value, UErrorCode &errorCode) { if (U_FAILURE(errorCode)) { return; } if (path == nullptr) {
errorCode = U_ILLEGAL_ARGUMENT_ERROR; return;
} const UResourceBundle *rb; if (*path == 0) { // empty path
rb = bundle;
} else {
rb = ures_getByKeyWithFallback(bundle, path, tempFillIn, &errorCode); if (U_FAILURE(errorCode)) { return;
}
}
value.setData(rb->getResData());
value.setValidLocaleDataEntry(rb->fValidLocaleDataEntry);
value.setResource(rb->fRes, ResourceTracer(rb));
}
int32_t type = RES_GET_TYPE(resB->fRes); if(URES_IS_TABLE(type)) {
int32_t t;
res = res_getTableItemByKey(&resB->getResData(), resB->fRes, &t, &key); if(res == RES_BOGUS) {
key = inKey; if(resB->fHasFallback) {
dataEntry = getFallbackData(resB, &key, &res, status); if(U_SUCCESS(*status)) { /* check if resB->fResPath gives the right name here */ return init_resb_result(dataEntry, res, key, -1, resB, fillIn, status);
} else {
*status = U_MISSING_RESOURCE_ERROR;
}
} else {
*status = U_MISSING_RESOURCE_ERROR;
}
} else { return init_resb_result(resB->fData, res, key, -1, resB, fillIn, status);
}
} #if0 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */ /* not currently */ elseif(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == true) { /* here should go a first attempt to locate the key using index table */
dataEntry = getFallbackData(resB, &key, &res, status); if(U_SUCCESS(*status)) { return init_resb_result(dataEntry, res, key, resB, fillIn, status);
} else {
*status = U_MISSING_RESOURCE_ERROR;
}
} #endif else {
*status = U_RESOURCE_TYPE_MISMATCH;
} return fillIn;
}
/* If the version ID has not been built yet, then do so. Retrieve */ /* the minor version from the file. */
UErrorCode status = U_ZERO_ERROR;
int32_t minor_len = 0;
int32_t len;
/* 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 */ /* the end). */
len = (minor_len > 0) ? minor_len : 1;
/* Allocate the string, and build it up. */ /* + 1 for zero byte */
staticvoid getParentForFunctionalEquivalent(constchar* localeID,
UResourceBundle* res,
UResourceBundle* bund1,
CharString& parent) { // Get parent. // First check for a parent from %%Parent resource (Note that in resource trees // such as collation, data may have different parents than in parentLocales).
UErrorCode subStatus = U_ZERO_ERROR;
parent.clear(); if (res != nullptr) {
ures_getByKey(res, "%%Parent", bund1, &subStatus); if (U_SUCCESS(subStatus)) {
int32_t length16; const char16_t* s16 = ures_getString(bund1, &length16, &subStatus);
parent.appendInvariantChars(s16, length16, subStatus);
}
}
// If none there, use normal truncation parent if (U_FAILURE(subStatus) || parent.isEmpty()) {
subStatus = U_ZERO_ERROR;
parent = ulocimp_getParent(localeID, subStatus);
}
}
do {
subStatus = U_ZERO_ERROR;
res = ures_open(path, parent.data(), &subStatus); if(((subStatus == U_USING_FALLBACK_WARNING) ||
(subStatus == U_USING_DEFAULT_WARNING)) && isAvailable)
{
*isAvailable = false;
}
isAvailable = nullptr; /* only want to set this the first time around */
/* Now, see if we can find the kwVal collator.. start the search over.. */
parent.copyFrom(base, subStatus);
found.copyFrom(base, subStatus);
do {
res = ures_open(path, parent.data(), &subStatus); if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
*isAvailable = false;
}
isAvailable = nullptr; /* only want to set this the first time around */
subStatus = U_ZERO_ERROR;
UBool haveFound = false; // At least for collations which may be aliased, we need to use the VALID locale // as the parent instead of just truncating, as long as the VALID locale is not // root and has a different language than the parent. Use of the VALID locale // here is similar to the procedure used at the end of the previous do-while loop // for all resource types. if (res != nullptr && uprv_strcmp(resName, "collations") == 0) { constchar *validLoc = ures_getLocaleByType(res, ULOC_VALID_LOCALE, &subStatus); if (U_SUCCESS(subStatus) && validLoc != nullptr && validLoc[0] != 0 && uprv_strcmp(validLoc, "root") != 0) {
CharString validLang = ulocimp_getLanguage(validLoc, subStatus);
CharString parentLang = ulocimp_getLanguage(parent.toStringPiece(), subStatus); if (U_SUCCESS(subStatus) && validLang != parentLang) { // validLoc is not root and has a different language than parent, use it instead
found.clear().append(validLoc, subStatus);
haveFound = true;
}
}
subStatus = U_ZERO_ERROR;
} if (!haveFound) {
found.copyFrom(parent, subStatus);
}
do { /* search for 'default' named item */
res = ures_open(path, parent.data(), &subStatus); if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
*isAvailable = false;
}
isAvailable = nullptr; /* only want to set this the first time around */
if(U_SUCCESS(*status)) { if(full.isEmpty()) { #ifdefined(URES_TREE_DEBUG)
fprintf(stderr, "Still could not load keyword %s=%s\n", keyword, kwVal.data()); #endif
*status = U_MISSING_RESOURCE_ERROR;
} elseif(omitDefault) { #ifdefined(URES_TREE_DEBUG)
fprintf(stderr,"Trim? full=%s, defLoc=%s, found=%s\n", full.data(), defLoc.data(), found.data()); #endif if(defLoc.length() <= full.length()) { /* found the keyword in a *child* of where the default tag was present. */ if(kwVal == defVal) { /* if the requested kw is default, */ /* and the default is in or in an ancestor of the current locale */ #ifdefined(URES_TREE_DEBUG)
fprintf(stderr, "Removing unneeded var %s=%s\n", keyword, kwVal.data()); #endif
kwVal.clear();
}
}
}
found.copyFrom(full, subStatus); if(!kwVal.isEmpty()) {
found
.append("@", subStatus)
.append(keyword, subStatus)
.append("=", subStatus)
.append(kwVal, subStatus);
} elseif(!omitDefault) {
found
.append("@", subStatus)
.append(keyword, subStatus)
.append("=", subStatus)
.append(defVal, subStatus);
}
} /* we found the default locale - no need to repeat it.*/
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.