michael@0: /* michael@0: ******************************************************************************* michael@0: * michael@0: * Copyright (C) 2003-2011, International Business Machines michael@0: * Corporation and others. All Rights Reserved. michael@0: * michael@0: ******************************************************************************* michael@0: * file name: pkgitems.cpp michael@0: * encoding: US-ASCII michael@0: * tab size: 8 (not used) michael@0: * indentation:4 michael@0: * michael@0: * created on: 2005sep18 michael@0: * created by: Markus W. Scherer michael@0: * michael@0: * Companion file to package.cpp. Deals with details of ICU data item formats. michael@0: * Used for item dependencies. michael@0: * Contains adapted code from ucnv_bld.c (swapper code from 2003). michael@0: */ michael@0: michael@0: #include "unicode/utypes.h" michael@0: #include "unicode/ures.h" michael@0: #include "unicode/putil.h" michael@0: #include "unicode/udata.h" michael@0: #include "cstring.h" michael@0: #include "uinvchar.h" michael@0: #include "ucmndata.h" michael@0: #include "udataswp.h" michael@0: #include "swapimpl.h" michael@0: #include "toolutil.h" michael@0: #include "package.h" michael@0: #include "pkg_imp.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: /* item formats in common */ michael@0: michael@0: #include "uresdata.h" michael@0: #include "ucnv_bld.h" michael@0: #include "ucnv_io.h" michael@0: michael@0: // general definitions ----------------------------------------------------- *** michael@0: michael@0: #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) michael@0: michael@0: U_CDECL_BEGIN michael@0: michael@0: static void U_CALLCONV michael@0: printError(void *context, const char *fmt, va_list args) { michael@0: vfprintf((FILE *)context, fmt, args); michael@0: } michael@0: michael@0: U_CDECL_END michael@0: michael@0: // a data item in native-platform form ------------------------------------- *** michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: class NativeItem { michael@0: public: michael@0: NativeItem() : pItem(NULL), pInfo(NULL), bytes(NULL), swapped(NULL), length(0) {} michael@0: NativeItem(const Item *item, UDataSwapFn *swap) : swapped(NULL) { michael@0: setItem(item, swap); michael@0: } michael@0: ~NativeItem() { michael@0: delete [] swapped; michael@0: } michael@0: const UDataInfo *getDataInfo() const { michael@0: return pInfo; michael@0: } michael@0: const uint8_t *getBytes() const { michael@0: return bytes; michael@0: } michael@0: int32_t getLength() const { michael@0: return length; michael@0: } michael@0: michael@0: void setItem(const Item *item, UDataSwapFn *swap) { michael@0: pItem=item; michael@0: int32_t infoLength, itemHeaderLength; michael@0: UErrorCode errorCode=U_ZERO_ERROR; michael@0: pInfo=::getDataInfo(pItem->data, pItem->length, infoLength, itemHeaderLength, &errorCode); michael@0: if(U_FAILURE(errorCode)) { michael@0: exit(errorCode); // should succeed because readFile() checks headers michael@0: } michael@0: length=pItem->length-itemHeaderLength; michael@0: michael@0: if(pInfo->isBigEndian==U_IS_BIG_ENDIAN && pInfo->charsetFamily==U_CHARSET_FAMILY) { michael@0: bytes=pItem->data+itemHeaderLength; michael@0: } else { michael@0: UDataSwapper *ds=udata_openSwapper((UBool)pInfo->isBigEndian, pInfo->charsetFamily, U_IS_BIG_ENDIAN, U_CHARSET_FAMILY, &errorCode); michael@0: if(U_FAILURE(errorCode)) { michael@0: fprintf(stderr, "icupkg: udata_openSwapper(\"%s\") failed - %s\n", michael@0: pItem->name, u_errorName(errorCode)); michael@0: exit(errorCode); michael@0: } michael@0: michael@0: ds->printError=printError; michael@0: ds->printErrorContext=stderr; michael@0: michael@0: swapped=new uint8_t[pItem->length]; michael@0: if(swapped==NULL) { michael@0: fprintf(stderr, "icupkg: unable to allocate memory for swapping \"%s\"\n", pItem->name); michael@0: exit(U_MEMORY_ALLOCATION_ERROR); michael@0: } michael@0: swap(ds, pItem->data, pItem->length, swapped, &errorCode); michael@0: pInfo=::getDataInfo(swapped, pItem->length, infoLength, itemHeaderLength, &errorCode); michael@0: bytes=swapped+itemHeaderLength; michael@0: udata_closeSwapper(ds); michael@0: } michael@0: } michael@0: michael@0: private: michael@0: const Item *pItem; michael@0: const UDataInfo *pInfo; michael@0: const uint8_t *bytes; michael@0: uint8_t *swapped; michael@0: int32_t length; michael@0: }; michael@0: michael@0: // check a dependency ------------------------------------------------------ *** michael@0: michael@0: /* michael@0: * assemble the target item name from the source item name, an ID michael@0: * and a suffix michael@0: */ michael@0: static void michael@0: makeTargetName(const char *itemName, const char *id, int32_t idLength, const char *suffix, michael@0: char *target, int32_t capacity, michael@0: UErrorCode *pErrorCode) { michael@0: const char *itemID; michael@0: int32_t treeLength, suffixLength, targetLength; michael@0: michael@0: // get the item basename michael@0: itemID=strrchr(itemName, '/'); michael@0: if(itemID!=NULL) { michael@0: ++itemID; michael@0: } else { michael@0: itemID=itemName; michael@0: } michael@0: michael@0: // build the target string michael@0: treeLength=(int32_t)(itemID-itemName); michael@0: if(idLength<0) { michael@0: idLength=(int32_t)strlen(id); michael@0: } michael@0: suffixLength=(int32_t)strlen(suffix); michael@0: targetLength=treeLength+idLength+suffixLength; michael@0: if(targetLength>=capacity) { michael@0: fprintf(stderr, "icupkg/makeTargetName(%s) target item name length %ld too long\n", michael@0: itemName, (long)targetLength); michael@0: *pErrorCode=U_BUFFER_OVERFLOW_ERROR; michael@0: return; michael@0: } michael@0: michael@0: memcpy(target, itemName, treeLength); michael@0: memcpy(target+treeLength, id, idLength); michael@0: memcpy(target+treeLength+idLength, suffix, suffixLength+1); // +1 includes the terminating NUL michael@0: } michael@0: michael@0: static void michael@0: checkIDSuffix(const char *itemName, const char *id, int32_t idLength, const char *suffix, michael@0: CheckDependency check, void *context, michael@0: UErrorCode *pErrorCode) { michael@0: char target[200]; michael@0: makeTargetName(itemName, id, idLength, suffix, target, (int32_t)sizeof(target), pErrorCode); michael@0: if(U_SUCCESS(*pErrorCode)) { michael@0: check(context, itemName, target); michael@0: } michael@0: } michael@0: michael@0: /* assemble the target item name from the item's parent item name */ michael@0: static void michael@0: checkParent(const char *itemName, CheckDependency check, void *context, michael@0: UErrorCode *pErrorCode) { michael@0: const char *itemID, *parent, *parentLimit, *suffix; michael@0: int32_t parentLength; michael@0: michael@0: // get the item basename michael@0: itemID=strrchr(itemName, '/'); michael@0: if(itemID!=NULL) { michael@0: ++itemID; michael@0: } else { michael@0: itemID=itemName; michael@0: } michael@0: michael@0: // get the item suffix michael@0: suffix=strrchr(itemID, '.'); michael@0: if(suffix==NULL) { michael@0: // empty suffix, point to the end of the string michael@0: suffix=strrchr(itemID, 0); michael@0: } michael@0: michael@0: // get the position of the last '_' michael@0: for(parentLimit=suffix; parentLimit>itemID && *--parentLimit!='_';) {} michael@0: michael@0: if(parentLimit!=itemID) { michael@0: // get the parent item name by truncating the last part of this item's name */ michael@0: parent=itemID; michael@0: parentLength=(int32_t)(parentLimit-itemID); michael@0: } else { michael@0: // no '_' in the item name: the parent is the root bundle michael@0: parent="root"; michael@0: parentLength=4; michael@0: if((suffix-itemID)==parentLength && 0==memcmp(itemID, parent, parentLength)) { michael@0: // the item itself is "root", which does not depend on a parent michael@0: return; michael@0: } michael@0: } michael@0: checkIDSuffix(itemName, parent, parentLength, suffix, check, context, pErrorCode); michael@0: } michael@0: michael@0: // get dependencies from resource bundles ---------------------------------- *** michael@0: michael@0: static const UChar SLASH=0x2f; michael@0: michael@0: /* michael@0: * Check for the alias from the string or alias resource res. michael@0: */ michael@0: static void michael@0: checkAlias(const char *itemName, michael@0: Resource res, const UChar *alias, int32_t length, UBool useResSuffix, michael@0: CheckDependency check, void *context, UErrorCode *pErrorCode) { michael@0: int32_t i; michael@0: michael@0: if(!uprv_isInvariantUString(alias, length)) { michael@0: fprintf(stderr, "icupkg/ures_enumDependencies(%s res=%08x) alias string contains non-invariant characters\n", michael@0: itemName, res); michael@0: *pErrorCode=U_INVALID_CHAR_FOUND; michael@0: return; michael@0: } michael@0: michael@0: // extract the locale ID from alias strings like michael@0: // locale_ID/key1/key2/key3 michael@0: // locale_ID michael@0: michael@0: // search for the first slash michael@0: for(i=0; i=(int32_t)sizeof(localeID)) { michael@0: fprintf(stderr, "icupkg/ures_enumDependencies(%s res=%08x) alias locale ID length %ld too long\n", michael@0: itemName, res, (long)length); michael@0: *pErrorCode=U_BUFFER_OVERFLOW_ERROR; michael@0: return; michael@0: } michael@0: u_UCharsToChars(alias, localeID, length); michael@0: localeID[length]=0; michael@0: michael@0: checkIDSuffix(itemName, localeID, -1, (useResSuffix ? ".res" : ""), check, context, pErrorCode); michael@0: } michael@0: michael@0: /* michael@0: * Enumerate one resource item and its children and extract dependencies from michael@0: * aliases. michael@0: */ michael@0: static void michael@0: ures_enumDependencies(const char *itemName, michael@0: const ResourceData *pResData, michael@0: Resource res, const char *inKey, const char *parentKey, int32_t depth, michael@0: CheckDependency check, void *context, michael@0: Package *pkg, michael@0: UErrorCode *pErrorCode) { michael@0: switch(res_getPublicType(res)) { michael@0: case URES_STRING: michael@0: { michael@0: UBool useResSuffix = TRUE; michael@0: // Check for %%ALIAS michael@0: if(depth==1 && inKey!=NULL) { michael@0: if(0!=strcmp(inKey, "%%ALIAS")) { michael@0: break; michael@0: } michael@0: } michael@0: // Check for %%DEPENDENCY michael@0: else if(depth==2 && parentKey!=NULL) { michael@0: if(0!=strcmp(parentKey, "%%DEPENDENCY")) { michael@0: break; michael@0: } michael@0: useResSuffix = FALSE; michael@0: } else { michael@0: // we ignore all other strings michael@0: break; michael@0: } michael@0: int32_t length; michael@0: const UChar *alias=res_getString(pResData, res, &length); michael@0: checkAlias(itemName, res, alias, length, useResSuffix, check, context, pErrorCode); michael@0: } michael@0: break; michael@0: case URES_ALIAS: michael@0: { michael@0: int32_t length; michael@0: const UChar *alias=res_getAlias(pResData, res, &length); michael@0: checkAlias(itemName, res, alias, length, TRUE, check, context, pErrorCode); michael@0: } michael@0: break; michael@0: case URES_TABLE: michael@0: { michael@0: /* recurse */ michael@0: int32_t count=res_countArrayItems(pResData, res); michael@0: for(int32_t i=0; iformatVersion[0], pInfo->formatVersion[1]); michael@0: exit(U_UNSUPPORTED_ERROR); michael@0: } michael@0: michael@0: /* michael@0: * if the bundle attributes are present and the nofallback flag is not set, michael@0: * then add the parent bundle as a dependency michael@0: */ michael@0: if(pInfo->formatVersion[0]>1 || (pInfo->formatVersion[0]==1 && pInfo->formatVersion[1]>=1)) { michael@0: if(!resData.noFallback) { michael@0: /* this bundle participates in locale fallback */ michael@0: checkParent(itemName, check, context, pErrorCode); michael@0: } michael@0: } michael@0: michael@0: icu::NativeItem nativePool; michael@0: michael@0: if(resData.usesPoolBundle) { michael@0: char poolName[200]; michael@0: makeTargetName(itemName, "pool", 4, ".res", poolName, (int32_t)sizeof(poolName), pErrorCode); michael@0: if(U_FAILURE(*pErrorCode)) { michael@0: return; michael@0: } michael@0: check(context, itemName, poolName); michael@0: int32_t index=pkg->findItem(poolName); michael@0: if(index<0) { michael@0: // We cannot work with a bundle if its pool resource is missing. michael@0: // check() already printed a complaint. michael@0: return; michael@0: } michael@0: // TODO: Cache the native version in the Item itself. michael@0: nativePool.setItem(pkg->getItem(index), ures_swap); michael@0: const UDataInfo *poolInfo=nativePool.getDataInfo(); michael@0: if(poolInfo->formatVersion[0]<=1) { michael@0: fprintf(stderr, "icupkg: %s is not a pool bundle\n", poolName); michael@0: return; michael@0: } michael@0: const int32_t *poolIndexes=(const int32_t *)nativePool.getBytes()+1; michael@0: int32_t poolIndexLength=poolIndexes[URES_INDEX_LENGTH]&0xff; michael@0: if(!(poolIndexLength>URES_INDEX_POOL_CHECKSUM && michael@0: (poolIndexes[URES_INDEX_ATTRIBUTES]&URES_ATT_IS_POOL_BUNDLE)) michael@0: ) { michael@0: fprintf(stderr, "icupkg: %s is not a pool bundle\n", poolName); michael@0: return; michael@0: } michael@0: if(resData.pRoot[1+URES_INDEX_POOL_CHECKSUM]==poolIndexes[URES_INDEX_POOL_CHECKSUM]) { michael@0: resData.poolBundleKeys=(const char *)(poolIndexes+poolIndexLength); michael@0: } else { michael@0: fprintf(stderr, "icupkg: %s has mismatched checksum for %s\n", poolName, itemName); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: ures_enumDependencies( michael@0: itemName, &resData, michael@0: resData.rootRes, NULL, NULL, 0, michael@0: check, context, michael@0: pkg, michael@0: pErrorCode); michael@0: } michael@0: michael@0: // get dependencies from conversion tables --------------------------------- *** michael@0: michael@0: /* code adapted from ucnv_swap() */ michael@0: static void michael@0: ucnv_enumDependencies(const UDataSwapper *ds, michael@0: const char *itemName, const UDataInfo *pInfo, michael@0: const uint8_t *inBytes, int32_t length, michael@0: CheckDependency check, void *context, michael@0: UErrorCode *pErrorCode) { michael@0: uint32_t staticDataSize; michael@0: michael@0: const UConverterStaticData *inStaticData; michael@0: michael@0: const _MBCSHeader *inMBCSHeader; michael@0: uint8_t outputType; michael@0: michael@0: /* check format version */ michael@0: if(!( michael@0: pInfo->formatVersion[0]==6 && michael@0: pInfo->formatVersion[1]>=2 michael@0: )) { michael@0: fprintf(stderr, "icupkg/ucnv_enumDependencies(): .cnv format version %02x.%02x not supported\n", michael@0: pInfo->formatVersion[0], pInfo->formatVersion[1]); michael@0: exit(U_UNSUPPORTED_ERROR); michael@0: } michael@0: michael@0: /* read the initial UConverterStaticData structure after the UDataInfo header */ michael@0: inStaticData=(const UConverterStaticData *)inBytes; michael@0: michael@0: if( length<(int32_t)sizeof(UConverterStaticData) || michael@0: (uint32_t)length<(staticDataSize=ds->readUInt32(inStaticData->structSize)) michael@0: ) { michael@0: udata_printError(ds, "icupkg/ucnv_enumDependencies(): too few bytes (%d after header) for an ICU .cnv conversion table\n", michael@0: length); michael@0: *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; michael@0: return; michael@0: } michael@0: michael@0: inBytes+=staticDataSize; michael@0: length-=(int32_t)staticDataSize; michael@0: michael@0: /* check for supported conversionType values */ michael@0: if(inStaticData->conversionType==UCNV_MBCS) { michael@0: /* MBCS data */ michael@0: uint32_t mbcsHeaderLength, mbcsHeaderFlags, mbcsHeaderOptions; michael@0: int32_t extOffset; michael@0: michael@0: inMBCSHeader=(const _MBCSHeader *)inBytes; michael@0: michael@0: if(length<(int32_t)sizeof(_MBCSHeader)) { michael@0: udata_printError(ds, "icupkg/ucnv_enumDependencies(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table\n", michael@0: length); michael@0: *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; michael@0: return; michael@0: } michael@0: if(inMBCSHeader->version[0]==4 && inMBCSHeader->version[1]>=1) { michael@0: mbcsHeaderLength=MBCS_HEADER_V4_LENGTH; michael@0: } else if(inMBCSHeader->version[0]==5 && inMBCSHeader->version[1]>=3 && michael@0: ((mbcsHeaderOptions=ds->readUInt32(inMBCSHeader->options))& michael@0: MBCS_OPT_UNKNOWN_INCOMPATIBLE_MASK)==0 michael@0: ) { michael@0: mbcsHeaderLength=mbcsHeaderOptions&MBCS_OPT_LENGTH_MASK; michael@0: } else { michael@0: udata_printError(ds, "icupkg/ucnv_enumDependencies(): unsupported _MBCSHeader.version %d.%d\n", michael@0: inMBCSHeader->version[0], inMBCSHeader->version[1]); michael@0: *pErrorCode=U_UNSUPPORTED_ERROR; michael@0: return; michael@0: } michael@0: michael@0: mbcsHeaderFlags=ds->readUInt32(inMBCSHeader->flags); michael@0: extOffset=(int32_t)(mbcsHeaderFlags>>8); michael@0: outputType=(uint8_t)mbcsHeaderFlags; michael@0: michael@0: if(outputType==MBCS_OUTPUT_EXT_ONLY) { michael@0: /* michael@0: * extension-only file, michael@0: * contains a base name instead of normal base table data michael@0: */ michael@0: char baseName[32]; michael@0: int32_t baseNameLength; michael@0: michael@0: /* there is extension data after the base data, see ucnv_ext.h */ michael@0: if(length<(extOffset+UCNV_EXT_INDEXES_MIN_LENGTH*4)) { michael@0: udata_printError(ds, "icupkg/ucnv_enumDependencies(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table with extension data\n", michael@0: length); michael@0: *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; michael@0: return; michael@0: } michael@0: michael@0: /* swap the base name, between the header and the extension data */ michael@0: const char *inBaseName=(const char *)inBytes+mbcsHeaderLength*4; michael@0: baseNameLength=(int32_t)strlen(inBaseName); michael@0: if(baseNameLength>=(int32_t)sizeof(baseName)) { michael@0: udata_printError(ds, "icupkg/ucnv_enumDependencies(%s): base name length %ld too long\n", michael@0: itemName, baseNameLength); michael@0: *pErrorCode=U_UNSUPPORTED_ERROR; michael@0: return; michael@0: } michael@0: ds->swapInvChars(ds, inBaseName, baseNameLength+1, baseName, pErrorCode); michael@0: michael@0: checkIDSuffix(itemName, baseName, -1, ".cnv", check, context, pErrorCode); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // ICU data formats -------------------------------------------------------- *** michael@0: michael@0: static const struct { michael@0: uint8_t dataFormat[4]; michael@0: } dataFormats[]={ michael@0: { { 0x52, 0x65, 0x73, 0x42 } }, /* dataFormat="ResB" */ michael@0: { { 0x63, 0x6e, 0x76, 0x74 } }, /* dataFormat="cnvt" */ michael@0: { { 0x43, 0x76, 0x41, 0x6c } } /* dataFormat="CvAl" */ michael@0: }; michael@0: michael@0: enum { michael@0: FMT_RES, michael@0: FMT_CNV, michael@0: FMT_ALIAS, michael@0: FMT_COUNT michael@0: }; michael@0: michael@0: static int32_t michael@0: getDataFormat(const uint8_t dataFormat[4]) { michael@0: int32_t i; michael@0: michael@0: for(i=0; idata, pItem->length, infoLength, itemHeaderLength, &errorCode); michael@0: if(U_FAILURE(errorCode)) { michael@0: return; // should not occur because readFile() checks headers michael@0: } michael@0: michael@0: // find the data format and call the corresponding function, if any michael@0: int32_t format=getDataFormat(pInfo->dataFormat); michael@0: if(format>=0) { michael@0: switch(format) { michael@0: case FMT_RES: michael@0: { michael@0: /* michael@0: * Swap the resource bundle (if necessary) so that we can use michael@0: * the normal runtime uresdata.c code to read it. michael@0: * We do not want to duplicate that code, especially not together with on-the-fly swapping. michael@0: */ michael@0: NativeItem nrb(pItem, ures_swap); michael@0: ures_enumDependencies(pItem->name, nrb.getDataInfo(), nrb.getBytes(), nrb.getLength(), check, context, this, &errorCode); michael@0: break; michael@0: } michael@0: case FMT_CNV: michael@0: { michael@0: // TODO: share/cache swappers michael@0: UDataSwapper *ds=udata_openSwapper( michael@0: (UBool)pInfo->isBigEndian, pInfo->charsetFamily, michael@0: U_IS_BIG_ENDIAN, U_CHARSET_FAMILY, michael@0: &errorCode); michael@0: if(U_FAILURE(errorCode)) { michael@0: fprintf(stderr, "icupkg: udata_openSwapper(\"%s\") failed - %s\n", michael@0: pItem->name, u_errorName(errorCode)); michael@0: exit(errorCode); michael@0: } michael@0: michael@0: ds->printError=printError; michael@0: ds->printErrorContext=stderr; michael@0: michael@0: const uint8_t *inBytes=pItem->data+itemHeaderLength; michael@0: int32_t length=pItem->length-itemHeaderLength; michael@0: michael@0: ucnv_enumDependencies(ds, pItem->name, pInfo, inBytes, length, check, context, &errorCode); michael@0: udata_closeSwapper(ds); michael@0: break; michael@0: } michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: if(U_FAILURE(errorCode)) { michael@0: exit(errorCode); michael@0: } michael@0: } michael@0: } michael@0: michael@0: U_NAMESPACE_END