/* code adapted from ures_swap() */ static void ures_enumDependencies(const UDataSwapper *ds, const char *itemName, const UDataInfo *pInfo, const uint8_t *inBytes, int32_t length, CheckDependency check, void *context, UErrorCode *pErrorCode) { const Resource *inBundle; Resource rootRes; /* the following integers count Resource item offsets (4 bytes each), not bytes */ int32_t bundleLength; /* check format version */ if(pInfo->formatVersion[0]!=1) { fprintf(stderr, "icupkg: .res format version %02x not supported\n", pInfo->formatVersion[0]); exit(U_UNSUPPORTED_ERROR); } /* a resource bundle must contain at least one resource item */ bundleLength=length/4; /* formatVersion 1.1 must have a root item and at least 5 indexes */ if( bundleLength< (pInfo->formatVersion[1]==0 ? 1 : 1+5) ) { fprintf(stderr, "icupkg: too few bytes (%d after header) for a resource bundle\n", length); exit(U_INDEX_OUTOFBOUNDS_ERROR); } inBundle=(const Resource *)inBytes; rootRes=ds->readUInt32(*inBundle); ures_enumDependencies( ds, itemName, inBundle, bundleLength, rootRes, NULL, 0, check, context, pErrorCode); /* * if the bundle attributes are present and the nofallback flag is not set, * then add the parent bundle as a dependency */ if(pInfo->formatVersion[1]>=1) { int32_t indexes[URES_INDEX_TOP]; const int32_t *inIndexes; inIndexes=(const int32_t *)inBundle+1; indexes[URES_INDEX_LENGTH]=udata_readInt32(ds, inIndexes[URES_INDEX_LENGTH]); if(indexes[URES_INDEX_LENGTH]>URES_INDEX_ATTRIBUTES) { indexes[URES_INDEX_ATTRIBUTES]=udata_readInt32(ds, inIndexes[URES_INDEX_ATTRIBUTES]); if(0==(indexes[URES_INDEX_ATTRIBUTES]&URES_ATT_NO_FALLBACK)) { /* this bundle participates in locale fallback */ checkParent(itemName, check, context, pErrorCode); } } } }
static void ures_enumDependencies(const char *itemName, const UDataInfo *pInfo, const uint8_t *inBytes, int32_t length, CheckDependency check, void *context, Package *pkg, UErrorCode *pErrorCode) { ResourceData resData; res_read(&resData, pInfo, inBytes, length, pErrorCode); if(U_FAILURE(*pErrorCode)) { fprintf(stderr, "icupkg: .res format version %02x.%02x not supported, or bundle malformed\n", pInfo->formatVersion[0], pInfo->formatVersion[1]); exit(U_UNSUPPORTED_ERROR); } /* * if the bundle attributes are present and the nofallback flag is not set, * then add the parent bundle as a dependency */ if(pInfo->formatVersion[0]>1 || (pInfo->formatVersion[0]==1 && pInfo->formatVersion[1]>=1)) { if(!resData.noFallback) { /* this bundle participates in locale fallback */ checkParent(itemName, check, context, pErrorCode); } } icu::NativeItem nativePool; if(resData.usesPoolBundle) { char poolName[200]; makeTargetName(itemName, "pool", 4, ".res", poolName, (int32_t)sizeof(poolName), pErrorCode); if(U_FAILURE(*pErrorCode)) { return; } check(context, itemName, poolName); int32_t index=pkg->findItem(poolName); if(index<0) { // We cannot work with a bundle if its pool resource is missing. // check() already printed a complaint. return; } // TODO: Cache the native version in the Item itself. nativePool.setItem(pkg->getItem(index), ures_swap); const UDataInfo *poolInfo=nativePool.getDataInfo(); if(poolInfo->formatVersion[0]<=1) { fprintf(stderr, "icupkg: %s is not a pool bundle\n", poolName); return; } const int32_t *poolRoot=(const int32_t *)nativePool.getBytes(); const int32_t *poolIndexes=poolRoot+1; int32_t poolIndexLength=poolIndexes[URES_INDEX_LENGTH]&0xff; if(!(poolIndexLength>URES_INDEX_POOL_CHECKSUM && (poolIndexes[URES_INDEX_ATTRIBUTES]&URES_ATT_IS_POOL_BUNDLE)) ) { fprintf(stderr, "icupkg: %s is not a pool bundle\n", poolName); return; } if(resData.pRoot[1+URES_INDEX_POOL_CHECKSUM]==poolIndexes[URES_INDEX_POOL_CHECKSUM]) { resData.poolBundleKeys=(const char *)(poolIndexes+poolIndexLength); resData.poolBundleStrings=(const uint16_t *)(poolRoot+poolIndexes[URES_INDEX_KEYS_TOP]); } else { fprintf(stderr, "icupkg: %s has mismatched checksum for %s\n", poolName, itemName); return; } } ures_enumDependencies( itemName, &resData, resData.rootRes, NULL, NULL, 0, check, context, pkg, pErrorCode); }
/* * Enumerate one resource item and its children and extract dependencies from * aliases. */ static void ures_enumDependencies(const char *itemName, const ResourceData *pResData, Resource res, const char *inKey, const char *parentKey, int32_t depth, CheckDependency check, void *context, Package *pkg, UErrorCode *pErrorCode) { switch(res_getPublicType(res)) { case URES_STRING: { UBool useResSuffix = TRUE; // Check for %%ALIAS if(depth==1 && inKey!=NULL) { if(0!=strcmp(inKey, "%%ALIAS")) { break; } } // Check for %%DEPENDENCY else if(depth==2 && parentKey!=NULL) { if(0!=strcmp(parentKey, "%%DEPENDENCY")) { break; } useResSuffix = FALSE; } else { // we ignore all other strings break; } int32_t length; const UChar *alias=res_getString(pResData, res, &length); checkAlias(itemName, res, alias, length, useResSuffix, check, context, pErrorCode); } break; case URES_ALIAS: { int32_t length; const UChar *alias=res_getAlias(pResData, res, &length); checkAlias(itemName, res, alias, length, TRUE, check, context, pErrorCode); } break; case URES_TABLE: { /* recurse */ int32_t count=res_countArrayItems(pResData, res); for(int32_t i=0; i<count; ++i) { const char *itemKey; Resource item=res_getTableItemByIndex(pResData, res, i, &itemKey); ures_enumDependencies( itemName, pResData, item, itemKey, inKey, depth+1, check, context, pkg, pErrorCode); if(U_FAILURE(*pErrorCode)) { fprintf(stderr, "icupkg/ures_enumDependencies(%s table res=%08x)[%d].recurse(%s: %08x) failed\n", itemName, res, i, itemKey, item); break; } } } break; case URES_ARRAY: { /* recurse */ int32_t count=res_countArrayItems(pResData, res); for(int32_t i=0; i<count; ++i) { Resource item=res_getArrayItem(pResData, res, i); ures_enumDependencies( itemName, pResData, item, NULL, inKey, depth+1, check, context, pkg, pErrorCode); if(U_FAILURE(*pErrorCode)) { fprintf(stderr, "icupkg/ures_enumDependencies(%s array res=%08x)[%d].recurse(%08x) failed\n", itemName, res, i, item); break; } } } break; default: break; } }
/* * Enumerate one resource item and its children and extract dependencies from * aliases. * Code adapted from ures_preflightResource() and ures_swapResource(). */ static void ures_enumDependencies(const UDataSwapper *ds, const char *itemName, const Resource *inBundle, int32_t length, Resource res, const char *inKey, int32_t depth, CheckDependency check, void *context, UErrorCode *pErrorCode) { const Resource *p; int32_t offset; if(res==0 || RES_GET_TYPE(res)==URES_INT) { /* empty string or integer, nothing to do */ return; } /* all other types use an offset to point to their data */ offset=(int32_t)RES_GET_OFFSET(res); if(0<=length && length<=offset) { udata_printError(ds, "icupkg/ures_enumDependencies(%s res=%08x) resource offset exceeds bundle length %d\n", itemName, res, length); *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; return; } p=inBundle+offset; switch(RES_GET_TYPE(res)) { /* strings and aliases have physically the same value layout */ case URES_STRING: // we ignore all strings except top-level strings with a %%ALIAS key if(depth!=1) { break; } else { char key[8]; int32_t keyLength; keyLength=(int32_t)strlen(inKey); if(keyLength!=gAliasKeyLength) { break; } ds->swapInvChars(ds, inKey, gAliasKeyLength+1, key, pErrorCode); if(U_FAILURE(*pErrorCode)) { udata_printError(ds, "icupkg/ures_enumDependencies(%s res=%08x) string key contains variant characters\n", itemName, res); return; } if(0!=strcmp(key, gAliasKey)) { break; } } // for the top-level %%ALIAS string fall through to URES_ALIAS case URES_ALIAS: { char localeID[32]; const uint16_t *p16; int32_t i, stringLength; uint16_t u16, ored16; stringLength=udata_readInt32(ds, (int32_t)*p); /* top=offset+1+(string length +1)/2 rounded up */ offset+=1+((stringLength+1)+1)/2; if(offset>length) { break; // the resource does not fit into the bundle, print error below } // extract the locale ID from alias strings like // locale_ID/key1/key2/key3 // locale_ID if(U_IS_BIG_ENDIAN==ds->inIsBigEndian) { u16=0x2f; // slash in local endianness } else { u16=0x2f00; // slash in opposite endianness } p16=(const uint16_t *)(p+1); // Unicode string contents // search for the first slash for(i=0; i<stringLength && p16[i]!=u16; ++i) {} if(RES_GET_TYPE(res)==URES_ALIAS) { // ignore aliases with an initial slash: // /ICUDATA/... and /pkgname/... go to a different package // /LOCALE/... are for dynamic sideways fallbacks and don't go to a fixed bundle if(i==0) { break; // initial slash ('/') } // ignore the intra-bundle path starting from the first slash ('/') stringLength=i; } else /* URES_STRING */ { // the whole string should only consist of a locale ID if(i!=stringLength) { udata_printError(ds, "icupkg/ures_enumDependencies(%s res=%08x) %%ALIAS contains a '/'\n", itemName, res); *pErrorCode=U_UNSUPPORTED_ERROR; return; } } // convert the Unicode string to char * and // check that it has a bundle path but no package if(stringLength>=(int32_t)sizeof(localeID)) { udata_printError(ds, "icupkg/ures_enumDependencies(%s res=%08x) alias locale ID length %ld too long\n", itemName, res, stringLength); *pErrorCode=U_BUFFER_OVERFLOW_ERROR; return; } // convert the alias Unicode string to US-ASCII ored16=0; if(U_IS_BIG_ENDIAN==ds->inIsBigEndian) { for(i=0; i<stringLength; ++i) { u16=p16[i]; ored16|=u16; localeID[i]=(char)u16; } } else { for(i=0; i<stringLength; ++i) { u16=p16[i]; ored16|=u16; localeID[i]=(char)(u16>>8); } ored16=(uint16_t)((ored16<<8)|(ored16>>8)); } localeID[stringLength]=0; if(ored16>0x7f) { udata_printError(ds, "icupkg/ures_enumDependencies(%s res=%08x) alias string contains non-ASCII characters\n", itemName, res); *pErrorCode=U_INVALID_CHAR_FOUND; return; } #if (U_CHARSET_FAMILY==U_EBCDIC_FAMILY) // swap to EBCDIC // our swapper is probably not the right one, but // the function uses it only for printing errors uprv_ebcdicFromAscii(ds, localeID, stringLength, localeID, pErrorCode); if(U_FAILURE(*pErrorCode)) { return; } #endif #if U_CHARSET_FAMILY!=U_ASCII_FAMILY && U_CHARSET_FAMILY!=U_EBCDIC_FAMILY # error Unknown U_CHARSET_FAMILY value! #endif checkIDSuffix(itemName, localeID, -1, ".res", check, context, pErrorCode); } break; case URES_TABLE: case URES_TABLE32: { const uint16_t *pKey16; const int32_t *pKey32; Resource item; int32_t i, count; if(RES_GET_TYPE(res)==URES_TABLE) { /* get table item count */ pKey16=(const uint16_t *)p; count=ds->readUInt16(*pKey16++); pKey32=NULL; /* top=((1+ table item count)/2 rounded up)+(table item count) */ offset+=((1+count)+1)/2; } else { /* get table item count */ pKey32=(const int32_t *)p; count=udata_readInt32(ds, *pKey32++); pKey16=NULL; /* top=(1+ table item count)+(table item count) */ offset+=1+count; } p=inBundle+offset; /* pointer to table resources */ offset+=count; if(offset>length) { break; // the resource does not fit into the bundle, print error below } /* recurse */ for(i=0; i<count; ++i) { item=ds->readUInt32(*p++); ures_enumDependencies( ds, itemName, inBundle, length, item, ((const char *)inBundle)+ (pKey16!=NULL ? ds->readUInt16(pKey16[i]) : udata_readInt32(ds, pKey32[i])), depth+1, check, context, pErrorCode); if(U_FAILURE(*pErrorCode)) { udata_printError(ds, "icupkg/ures_enumDependencies(%s table res=%08x)[%d].recurse(%08x) failed\n", itemName, res, i, item); break; } } } break; case URES_ARRAY: { Resource item; int32_t i, count; /* top=offset+1+(array length) */ count=udata_readInt32(ds, (int32_t)*p++); offset+=1+count; if(offset>length) { break; // the resource does not fit into the bundle, print error below } /* recurse */ for(i=0; i<count; ++i) { item=ds->readUInt32(*p++); ures_enumDependencies( ds, itemName, inBundle, length, item, NULL, depth+1, check, context, pErrorCode); if(U_FAILURE(*pErrorCode)) { udata_printError(ds, "icupkg/ures_enumDependencies(%s array res=%08x)[%d].recurse(%08x) failed\n", itemName, res, i, item); break; } } } break; default: break; } if(U_FAILURE(*pErrorCode)) { /* nothing to do */ } else if(0<=length && length<offset) { udata_printError(ds, "icupkg/ures_enumDependencies(%s res=%08x) resource limit exceeds bundle length %d\n", itemName, res, length); *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; } }