Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* |
michael@0 | 2 | ******************************************************************************* |
michael@0 | 3 | * |
michael@0 | 4 | * Copyright (C) 2003-2011, International Business Machines |
michael@0 | 5 | * Corporation and others. All Rights Reserved. |
michael@0 | 6 | * |
michael@0 | 7 | ******************************************************************************* |
michael@0 | 8 | * file name: pkgitems.cpp |
michael@0 | 9 | * encoding: US-ASCII |
michael@0 | 10 | * tab size: 8 (not used) |
michael@0 | 11 | * indentation:4 |
michael@0 | 12 | * |
michael@0 | 13 | * created on: 2005sep18 |
michael@0 | 14 | * created by: Markus W. Scherer |
michael@0 | 15 | * |
michael@0 | 16 | * Companion file to package.cpp. Deals with details of ICU data item formats. |
michael@0 | 17 | * Used for item dependencies. |
michael@0 | 18 | * Contains adapted code from ucnv_bld.c (swapper code from 2003). |
michael@0 | 19 | */ |
michael@0 | 20 | |
michael@0 | 21 | #include "unicode/utypes.h" |
michael@0 | 22 | #include "unicode/ures.h" |
michael@0 | 23 | #include "unicode/putil.h" |
michael@0 | 24 | #include "unicode/udata.h" |
michael@0 | 25 | #include "cstring.h" |
michael@0 | 26 | #include "uinvchar.h" |
michael@0 | 27 | #include "ucmndata.h" |
michael@0 | 28 | #include "udataswp.h" |
michael@0 | 29 | #include "swapimpl.h" |
michael@0 | 30 | #include "toolutil.h" |
michael@0 | 31 | #include "package.h" |
michael@0 | 32 | #include "pkg_imp.h" |
michael@0 | 33 | |
michael@0 | 34 | #include <stdio.h> |
michael@0 | 35 | #include <stdlib.h> |
michael@0 | 36 | #include <string.h> |
michael@0 | 37 | |
michael@0 | 38 | /* item formats in common */ |
michael@0 | 39 | |
michael@0 | 40 | #include "uresdata.h" |
michael@0 | 41 | #include "ucnv_bld.h" |
michael@0 | 42 | #include "ucnv_io.h" |
michael@0 | 43 | |
michael@0 | 44 | // general definitions ----------------------------------------------------- *** |
michael@0 | 45 | |
michael@0 | 46 | #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) |
michael@0 | 47 | |
michael@0 | 48 | U_CDECL_BEGIN |
michael@0 | 49 | |
michael@0 | 50 | static void U_CALLCONV |
michael@0 | 51 | printError(void *context, const char *fmt, va_list args) { |
michael@0 | 52 | vfprintf((FILE *)context, fmt, args); |
michael@0 | 53 | } |
michael@0 | 54 | |
michael@0 | 55 | U_CDECL_END |
michael@0 | 56 | |
michael@0 | 57 | // a data item in native-platform form ------------------------------------- *** |
michael@0 | 58 | |
michael@0 | 59 | U_NAMESPACE_BEGIN |
michael@0 | 60 | |
michael@0 | 61 | class NativeItem { |
michael@0 | 62 | public: |
michael@0 | 63 | NativeItem() : pItem(NULL), pInfo(NULL), bytes(NULL), swapped(NULL), length(0) {} |
michael@0 | 64 | NativeItem(const Item *item, UDataSwapFn *swap) : swapped(NULL) { |
michael@0 | 65 | setItem(item, swap); |
michael@0 | 66 | } |
michael@0 | 67 | ~NativeItem() { |
michael@0 | 68 | delete [] swapped; |
michael@0 | 69 | } |
michael@0 | 70 | const UDataInfo *getDataInfo() const { |
michael@0 | 71 | return pInfo; |
michael@0 | 72 | } |
michael@0 | 73 | const uint8_t *getBytes() const { |
michael@0 | 74 | return bytes; |
michael@0 | 75 | } |
michael@0 | 76 | int32_t getLength() const { |
michael@0 | 77 | return length; |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | void setItem(const Item *item, UDataSwapFn *swap) { |
michael@0 | 81 | pItem=item; |
michael@0 | 82 | int32_t infoLength, itemHeaderLength; |
michael@0 | 83 | UErrorCode errorCode=U_ZERO_ERROR; |
michael@0 | 84 | pInfo=::getDataInfo(pItem->data, pItem->length, infoLength, itemHeaderLength, &errorCode); |
michael@0 | 85 | if(U_FAILURE(errorCode)) { |
michael@0 | 86 | exit(errorCode); // should succeed because readFile() checks headers |
michael@0 | 87 | } |
michael@0 | 88 | length=pItem->length-itemHeaderLength; |
michael@0 | 89 | |
michael@0 | 90 | if(pInfo->isBigEndian==U_IS_BIG_ENDIAN && pInfo->charsetFamily==U_CHARSET_FAMILY) { |
michael@0 | 91 | bytes=pItem->data+itemHeaderLength; |
michael@0 | 92 | } else { |
michael@0 | 93 | UDataSwapper *ds=udata_openSwapper((UBool)pInfo->isBigEndian, pInfo->charsetFamily, U_IS_BIG_ENDIAN, U_CHARSET_FAMILY, &errorCode); |
michael@0 | 94 | if(U_FAILURE(errorCode)) { |
michael@0 | 95 | fprintf(stderr, "icupkg: udata_openSwapper(\"%s\") failed - %s\n", |
michael@0 | 96 | pItem->name, u_errorName(errorCode)); |
michael@0 | 97 | exit(errorCode); |
michael@0 | 98 | } |
michael@0 | 99 | |
michael@0 | 100 | ds->printError=printError; |
michael@0 | 101 | ds->printErrorContext=stderr; |
michael@0 | 102 | |
michael@0 | 103 | swapped=new uint8_t[pItem->length]; |
michael@0 | 104 | if(swapped==NULL) { |
michael@0 | 105 | fprintf(stderr, "icupkg: unable to allocate memory for swapping \"%s\"\n", pItem->name); |
michael@0 | 106 | exit(U_MEMORY_ALLOCATION_ERROR); |
michael@0 | 107 | } |
michael@0 | 108 | swap(ds, pItem->data, pItem->length, swapped, &errorCode); |
michael@0 | 109 | pInfo=::getDataInfo(swapped, pItem->length, infoLength, itemHeaderLength, &errorCode); |
michael@0 | 110 | bytes=swapped+itemHeaderLength; |
michael@0 | 111 | udata_closeSwapper(ds); |
michael@0 | 112 | } |
michael@0 | 113 | } |
michael@0 | 114 | |
michael@0 | 115 | private: |
michael@0 | 116 | const Item *pItem; |
michael@0 | 117 | const UDataInfo *pInfo; |
michael@0 | 118 | const uint8_t *bytes; |
michael@0 | 119 | uint8_t *swapped; |
michael@0 | 120 | int32_t length; |
michael@0 | 121 | }; |
michael@0 | 122 | |
michael@0 | 123 | // check a dependency ------------------------------------------------------ *** |
michael@0 | 124 | |
michael@0 | 125 | /* |
michael@0 | 126 | * assemble the target item name from the source item name, an ID |
michael@0 | 127 | * and a suffix |
michael@0 | 128 | */ |
michael@0 | 129 | static void |
michael@0 | 130 | makeTargetName(const char *itemName, const char *id, int32_t idLength, const char *suffix, |
michael@0 | 131 | char *target, int32_t capacity, |
michael@0 | 132 | UErrorCode *pErrorCode) { |
michael@0 | 133 | const char *itemID; |
michael@0 | 134 | int32_t treeLength, suffixLength, targetLength; |
michael@0 | 135 | |
michael@0 | 136 | // get the item basename |
michael@0 | 137 | itemID=strrchr(itemName, '/'); |
michael@0 | 138 | if(itemID!=NULL) { |
michael@0 | 139 | ++itemID; |
michael@0 | 140 | } else { |
michael@0 | 141 | itemID=itemName; |
michael@0 | 142 | } |
michael@0 | 143 | |
michael@0 | 144 | // build the target string |
michael@0 | 145 | treeLength=(int32_t)(itemID-itemName); |
michael@0 | 146 | if(idLength<0) { |
michael@0 | 147 | idLength=(int32_t)strlen(id); |
michael@0 | 148 | } |
michael@0 | 149 | suffixLength=(int32_t)strlen(suffix); |
michael@0 | 150 | targetLength=treeLength+idLength+suffixLength; |
michael@0 | 151 | if(targetLength>=capacity) { |
michael@0 | 152 | fprintf(stderr, "icupkg/makeTargetName(%s) target item name length %ld too long\n", |
michael@0 | 153 | itemName, (long)targetLength); |
michael@0 | 154 | *pErrorCode=U_BUFFER_OVERFLOW_ERROR; |
michael@0 | 155 | return; |
michael@0 | 156 | } |
michael@0 | 157 | |
michael@0 | 158 | memcpy(target, itemName, treeLength); |
michael@0 | 159 | memcpy(target+treeLength, id, idLength); |
michael@0 | 160 | memcpy(target+treeLength+idLength, suffix, suffixLength+1); // +1 includes the terminating NUL |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | static void |
michael@0 | 164 | checkIDSuffix(const char *itemName, const char *id, int32_t idLength, const char *suffix, |
michael@0 | 165 | CheckDependency check, void *context, |
michael@0 | 166 | UErrorCode *pErrorCode) { |
michael@0 | 167 | char target[200]; |
michael@0 | 168 | makeTargetName(itemName, id, idLength, suffix, target, (int32_t)sizeof(target), pErrorCode); |
michael@0 | 169 | if(U_SUCCESS(*pErrorCode)) { |
michael@0 | 170 | check(context, itemName, target); |
michael@0 | 171 | } |
michael@0 | 172 | } |
michael@0 | 173 | |
michael@0 | 174 | /* assemble the target item name from the item's parent item name */ |
michael@0 | 175 | static void |
michael@0 | 176 | checkParent(const char *itemName, CheckDependency check, void *context, |
michael@0 | 177 | UErrorCode *pErrorCode) { |
michael@0 | 178 | const char *itemID, *parent, *parentLimit, *suffix; |
michael@0 | 179 | int32_t parentLength; |
michael@0 | 180 | |
michael@0 | 181 | // get the item basename |
michael@0 | 182 | itemID=strrchr(itemName, '/'); |
michael@0 | 183 | if(itemID!=NULL) { |
michael@0 | 184 | ++itemID; |
michael@0 | 185 | } else { |
michael@0 | 186 | itemID=itemName; |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | // get the item suffix |
michael@0 | 190 | suffix=strrchr(itemID, '.'); |
michael@0 | 191 | if(suffix==NULL) { |
michael@0 | 192 | // empty suffix, point to the end of the string |
michael@0 | 193 | suffix=strrchr(itemID, 0); |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | // get the position of the last '_' |
michael@0 | 197 | for(parentLimit=suffix; parentLimit>itemID && *--parentLimit!='_';) {} |
michael@0 | 198 | |
michael@0 | 199 | if(parentLimit!=itemID) { |
michael@0 | 200 | // get the parent item name by truncating the last part of this item's name */ |
michael@0 | 201 | parent=itemID; |
michael@0 | 202 | parentLength=(int32_t)(parentLimit-itemID); |
michael@0 | 203 | } else { |
michael@0 | 204 | // no '_' in the item name: the parent is the root bundle |
michael@0 | 205 | parent="root"; |
michael@0 | 206 | parentLength=4; |
michael@0 | 207 | if((suffix-itemID)==parentLength && 0==memcmp(itemID, parent, parentLength)) { |
michael@0 | 208 | // the item itself is "root", which does not depend on a parent |
michael@0 | 209 | return; |
michael@0 | 210 | } |
michael@0 | 211 | } |
michael@0 | 212 | checkIDSuffix(itemName, parent, parentLength, suffix, check, context, pErrorCode); |
michael@0 | 213 | } |
michael@0 | 214 | |
michael@0 | 215 | // get dependencies from resource bundles ---------------------------------- *** |
michael@0 | 216 | |
michael@0 | 217 | static const UChar SLASH=0x2f; |
michael@0 | 218 | |
michael@0 | 219 | /* |
michael@0 | 220 | * Check for the alias from the string or alias resource res. |
michael@0 | 221 | */ |
michael@0 | 222 | static void |
michael@0 | 223 | checkAlias(const char *itemName, |
michael@0 | 224 | Resource res, const UChar *alias, int32_t length, UBool useResSuffix, |
michael@0 | 225 | CheckDependency check, void *context, UErrorCode *pErrorCode) { |
michael@0 | 226 | int32_t i; |
michael@0 | 227 | |
michael@0 | 228 | if(!uprv_isInvariantUString(alias, length)) { |
michael@0 | 229 | fprintf(stderr, "icupkg/ures_enumDependencies(%s res=%08x) alias string contains non-invariant characters\n", |
michael@0 | 230 | itemName, res); |
michael@0 | 231 | *pErrorCode=U_INVALID_CHAR_FOUND; |
michael@0 | 232 | return; |
michael@0 | 233 | } |
michael@0 | 234 | |
michael@0 | 235 | // extract the locale ID from alias strings like |
michael@0 | 236 | // locale_ID/key1/key2/key3 |
michael@0 | 237 | // locale_ID |
michael@0 | 238 | |
michael@0 | 239 | // search for the first slash |
michael@0 | 240 | for(i=0; i<length && alias[i]!=SLASH; ++i) {} |
michael@0 | 241 | |
michael@0 | 242 | if(res_getPublicType(res)==URES_ALIAS) { |
michael@0 | 243 | // ignore aliases with an initial slash: |
michael@0 | 244 | // /ICUDATA/... and /pkgname/... go to a different package |
michael@0 | 245 | // /LOCALE/... are for dynamic sideways fallbacks and don't go to a fixed bundle |
michael@0 | 246 | if(i==0) { |
michael@0 | 247 | return; // initial slash ('/') |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | // ignore the intra-bundle path starting from the first slash ('/') |
michael@0 | 251 | length=i; |
michael@0 | 252 | } else /* URES_STRING */ { |
michael@0 | 253 | // the whole string should only consist of a locale ID |
michael@0 | 254 | if(i!=length) { |
michael@0 | 255 | fprintf(stderr, "icupkg/ures_enumDependencies(%s res=%08x) %%ALIAS contains a '/'\n", |
michael@0 | 256 | itemName, res); |
michael@0 | 257 | *pErrorCode=U_UNSUPPORTED_ERROR; |
michael@0 | 258 | return; |
michael@0 | 259 | } |
michael@0 | 260 | } |
michael@0 | 261 | |
michael@0 | 262 | // convert the Unicode string to char * |
michael@0 | 263 | char localeID[32]; |
michael@0 | 264 | if(length>=(int32_t)sizeof(localeID)) { |
michael@0 | 265 | fprintf(stderr, "icupkg/ures_enumDependencies(%s res=%08x) alias locale ID length %ld too long\n", |
michael@0 | 266 | itemName, res, (long)length); |
michael@0 | 267 | *pErrorCode=U_BUFFER_OVERFLOW_ERROR; |
michael@0 | 268 | return; |
michael@0 | 269 | } |
michael@0 | 270 | u_UCharsToChars(alias, localeID, length); |
michael@0 | 271 | localeID[length]=0; |
michael@0 | 272 | |
michael@0 | 273 | checkIDSuffix(itemName, localeID, -1, (useResSuffix ? ".res" : ""), check, context, pErrorCode); |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | /* |
michael@0 | 277 | * Enumerate one resource item and its children and extract dependencies from |
michael@0 | 278 | * aliases. |
michael@0 | 279 | */ |
michael@0 | 280 | static void |
michael@0 | 281 | ures_enumDependencies(const char *itemName, |
michael@0 | 282 | const ResourceData *pResData, |
michael@0 | 283 | Resource res, const char *inKey, const char *parentKey, int32_t depth, |
michael@0 | 284 | CheckDependency check, void *context, |
michael@0 | 285 | Package *pkg, |
michael@0 | 286 | UErrorCode *pErrorCode) { |
michael@0 | 287 | switch(res_getPublicType(res)) { |
michael@0 | 288 | case URES_STRING: |
michael@0 | 289 | { |
michael@0 | 290 | UBool useResSuffix = TRUE; |
michael@0 | 291 | // Check for %%ALIAS |
michael@0 | 292 | if(depth==1 && inKey!=NULL) { |
michael@0 | 293 | if(0!=strcmp(inKey, "%%ALIAS")) { |
michael@0 | 294 | break; |
michael@0 | 295 | } |
michael@0 | 296 | } |
michael@0 | 297 | // Check for %%DEPENDENCY |
michael@0 | 298 | else if(depth==2 && parentKey!=NULL) { |
michael@0 | 299 | if(0!=strcmp(parentKey, "%%DEPENDENCY")) { |
michael@0 | 300 | break; |
michael@0 | 301 | } |
michael@0 | 302 | useResSuffix = FALSE; |
michael@0 | 303 | } else { |
michael@0 | 304 | // we ignore all other strings |
michael@0 | 305 | break; |
michael@0 | 306 | } |
michael@0 | 307 | int32_t length; |
michael@0 | 308 | const UChar *alias=res_getString(pResData, res, &length); |
michael@0 | 309 | checkAlias(itemName, res, alias, length, useResSuffix, check, context, pErrorCode); |
michael@0 | 310 | } |
michael@0 | 311 | break; |
michael@0 | 312 | case URES_ALIAS: |
michael@0 | 313 | { |
michael@0 | 314 | int32_t length; |
michael@0 | 315 | const UChar *alias=res_getAlias(pResData, res, &length); |
michael@0 | 316 | checkAlias(itemName, res, alias, length, TRUE, check, context, pErrorCode); |
michael@0 | 317 | } |
michael@0 | 318 | break; |
michael@0 | 319 | case URES_TABLE: |
michael@0 | 320 | { |
michael@0 | 321 | /* recurse */ |
michael@0 | 322 | int32_t count=res_countArrayItems(pResData, res); |
michael@0 | 323 | for(int32_t i=0; i<count; ++i) { |
michael@0 | 324 | const char *itemKey; |
michael@0 | 325 | Resource item=res_getTableItemByIndex(pResData, res, i, &itemKey); |
michael@0 | 326 | ures_enumDependencies( |
michael@0 | 327 | itemName, pResData, |
michael@0 | 328 | item, itemKey, |
michael@0 | 329 | inKey, depth+1, |
michael@0 | 330 | check, context, |
michael@0 | 331 | pkg, |
michael@0 | 332 | pErrorCode); |
michael@0 | 333 | if(U_FAILURE(*pErrorCode)) { |
michael@0 | 334 | fprintf(stderr, "icupkg/ures_enumDependencies(%s table res=%08x)[%d].recurse(%s: %08x) failed\n", |
michael@0 | 335 | itemName, res, i, itemKey, item); |
michael@0 | 336 | break; |
michael@0 | 337 | } |
michael@0 | 338 | } |
michael@0 | 339 | } |
michael@0 | 340 | break; |
michael@0 | 341 | case URES_ARRAY: |
michael@0 | 342 | { |
michael@0 | 343 | /* recurse */ |
michael@0 | 344 | int32_t count=res_countArrayItems(pResData, res); |
michael@0 | 345 | for(int32_t i=0; i<count; ++i) { |
michael@0 | 346 | Resource item=res_getArrayItem(pResData, res, i); |
michael@0 | 347 | ures_enumDependencies( |
michael@0 | 348 | itemName, pResData, |
michael@0 | 349 | item, NULL, |
michael@0 | 350 | inKey, depth+1, |
michael@0 | 351 | check, context, |
michael@0 | 352 | pkg, |
michael@0 | 353 | pErrorCode); |
michael@0 | 354 | if(U_FAILURE(*pErrorCode)) { |
michael@0 | 355 | fprintf(stderr, "icupkg/ures_enumDependencies(%s array res=%08x)[%d].recurse(%08x) failed\n", |
michael@0 | 356 | itemName, res, i, item); |
michael@0 | 357 | break; |
michael@0 | 358 | } |
michael@0 | 359 | } |
michael@0 | 360 | } |
michael@0 | 361 | break; |
michael@0 | 362 | default: |
michael@0 | 363 | break; |
michael@0 | 364 | } |
michael@0 | 365 | } |
michael@0 | 366 | |
michael@0 | 367 | static void |
michael@0 | 368 | ures_enumDependencies(const char *itemName, const UDataInfo *pInfo, |
michael@0 | 369 | const uint8_t *inBytes, int32_t length, |
michael@0 | 370 | CheckDependency check, void *context, |
michael@0 | 371 | Package *pkg, |
michael@0 | 372 | UErrorCode *pErrorCode) { |
michael@0 | 373 | ResourceData resData; |
michael@0 | 374 | |
michael@0 | 375 | res_read(&resData, pInfo, inBytes, length, pErrorCode); |
michael@0 | 376 | if(U_FAILURE(*pErrorCode)) { |
michael@0 | 377 | fprintf(stderr, "icupkg: .res format version %02x.%02x not supported, or bundle malformed\n", |
michael@0 | 378 | pInfo->formatVersion[0], pInfo->formatVersion[1]); |
michael@0 | 379 | exit(U_UNSUPPORTED_ERROR); |
michael@0 | 380 | } |
michael@0 | 381 | |
michael@0 | 382 | /* |
michael@0 | 383 | * if the bundle attributes are present and the nofallback flag is not set, |
michael@0 | 384 | * then add the parent bundle as a dependency |
michael@0 | 385 | */ |
michael@0 | 386 | if(pInfo->formatVersion[0]>1 || (pInfo->formatVersion[0]==1 && pInfo->formatVersion[1]>=1)) { |
michael@0 | 387 | if(!resData.noFallback) { |
michael@0 | 388 | /* this bundle participates in locale fallback */ |
michael@0 | 389 | checkParent(itemName, check, context, pErrorCode); |
michael@0 | 390 | } |
michael@0 | 391 | } |
michael@0 | 392 | |
michael@0 | 393 | icu::NativeItem nativePool; |
michael@0 | 394 | |
michael@0 | 395 | if(resData.usesPoolBundle) { |
michael@0 | 396 | char poolName[200]; |
michael@0 | 397 | makeTargetName(itemName, "pool", 4, ".res", poolName, (int32_t)sizeof(poolName), pErrorCode); |
michael@0 | 398 | if(U_FAILURE(*pErrorCode)) { |
michael@0 | 399 | return; |
michael@0 | 400 | } |
michael@0 | 401 | check(context, itemName, poolName); |
michael@0 | 402 | int32_t index=pkg->findItem(poolName); |
michael@0 | 403 | if(index<0) { |
michael@0 | 404 | // We cannot work with a bundle if its pool resource is missing. |
michael@0 | 405 | // check() already printed a complaint. |
michael@0 | 406 | return; |
michael@0 | 407 | } |
michael@0 | 408 | // TODO: Cache the native version in the Item itself. |
michael@0 | 409 | nativePool.setItem(pkg->getItem(index), ures_swap); |
michael@0 | 410 | const UDataInfo *poolInfo=nativePool.getDataInfo(); |
michael@0 | 411 | if(poolInfo->formatVersion[0]<=1) { |
michael@0 | 412 | fprintf(stderr, "icupkg: %s is not a pool bundle\n", poolName); |
michael@0 | 413 | return; |
michael@0 | 414 | } |
michael@0 | 415 | const int32_t *poolIndexes=(const int32_t *)nativePool.getBytes()+1; |
michael@0 | 416 | int32_t poolIndexLength=poolIndexes[URES_INDEX_LENGTH]&0xff; |
michael@0 | 417 | if(!(poolIndexLength>URES_INDEX_POOL_CHECKSUM && |
michael@0 | 418 | (poolIndexes[URES_INDEX_ATTRIBUTES]&URES_ATT_IS_POOL_BUNDLE)) |
michael@0 | 419 | ) { |
michael@0 | 420 | fprintf(stderr, "icupkg: %s is not a pool bundle\n", poolName); |
michael@0 | 421 | return; |
michael@0 | 422 | } |
michael@0 | 423 | if(resData.pRoot[1+URES_INDEX_POOL_CHECKSUM]==poolIndexes[URES_INDEX_POOL_CHECKSUM]) { |
michael@0 | 424 | resData.poolBundleKeys=(const char *)(poolIndexes+poolIndexLength); |
michael@0 | 425 | } else { |
michael@0 | 426 | fprintf(stderr, "icupkg: %s has mismatched checksum for %s\n", poolName, itemName); |
michael@0 | 427 | return; |
michael@0 | 428 | } |
michael@0 | 429 | } |
michael@0 | 430 | |
michael@0 | 431 | ures_enumDependencies( |
michael@0 | 432 | itemName, &resData, |
michael@0 | 433 | resData.rootRes, NULL, NULL, 0, |
michael@0 | 434 | check, context, |
michael@0 | 435 | pkg, |
michael@0 | 436 | pErrorCode); |
michael@0 | 437 | } |
michael@0 | 438 | |
michael@0 | 439 | // get dependencies from conversion tables --------------------------------- *** |
michael@0 | 440 | |
michael@0 | 441 | /* code adapted from ucnv_swap() */ |
michael@0 | 442 | static void |
michael@0 | 443 | ucnv_enumDependencies(const UDataSwapper *ds, |
michael@0 | 444 | const char *itemName, const UDataInfo *pInfo, |
michael@0 | 445 | const uint8_t *inBytes, int32_t length, |
michael@0 | 446 | CheckDependency check, void *context, |
michael@0 | 447 | UErrorCode *pErrorCode) { |
michael@0 | 448 | uint32_t staticDataSize; |
michael@0 | 449 | |
michael@0 | 450 | const UConverterStaticData *inStaticData; |
michael@0 | 451 | |
michael@0 | 452 | const _MBCSHeader *inMBCSHeader; |
michael@0 | 453 | uint8_t outputType; |
michael@0 | 454 | |
michael@0 | 455 | /* check format version */ |
michael@0 | 456 | if(!( |
michael@0 | 457 | pInfo->formatVersion[0]==6 && |
michael@0 | 458 | pInfo->formatVersion[1]>=2 |
michael@0 | 459 | )) { |
michael@0 | 460 | fprintf(stderr, "icupkg/ucnv_enumDependencies(): .cnv format version %02x.%02x not supported\n", |
michael@0 | 461 | pInfo->formatVersion[0], pInfo->formatVersion[1]); |
michael@0 | 462 | exit(U_UNSUPPORTED_ERROR); |
michael@0 | 463 | } |
michael@0 | 464 | |
michael@0 | 465 | /* read the initial UConverterStaticData structure after the UDataInfo header */ |
michael@0 | 466 | inStaticData=(const UConverterStaticData *)inBytes; |
michael@0 | 467 | |
michael@0 | 468 | if( length<(int32_t)sizeof(UConverterStaticData) || |
michael@0 | 469 | (uint32_t)length<(staticDataSize=ds->readUInt32(inStaticData->structSize)) |
michael@0 | 470 | ) { |
michael@0 | 471 | udata_printError(ds, "icupkg/ucnv_enumDependencies(): too few bytes (%d after header) for an ICU .cnv conversion table\n", |
michael@0 | 472 | length); |
michael@0 | 473 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; |
michael@0 | 474 | return; |
michael@0 | 475 | } |
michael@0 | 476 | |
michael@0 | 477 | inBytes+=staticDataSize; |
michael@0 | 478 | length-=(int32_t)staticDataSize; |
michael@0 | 479 | |
michael@0 | 480 | /* check for supported conversionType values */ |
michael@0 | 481 | if(inStaticData->conversionType==UCNV_MBCS) { |
michael@0 | 482 | /* MBCS data */ |
michael@0 | 483 | uint32_t mbcsHeaderLength, mbcsHeaderFlags, mbcsHeaderOptions; |
michael@0 | 484 | int32_t extOffset; |
michael@0 | 485 | |
michael@0 | 486 | inMBCSHeader=(const _MBCSHeader *)inBytes; |
michael@0 | 487 | |
michael@0 | 488 | if(length<(int32_t)sizeof(_MBCSHeader)) { |
michael@0 | 489 | udata_printError(ds, "icupkg/ucnv_enumDependencies(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table\n", |
michael@0 | 490 | length); |
michael@0 | 491 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; |
michael@0 | 492 | return; |
michael@0 | 493 | } |
michael@0 | 494 | if(inMBCSHeader->version[0]==4 && inMBCSHeader->version[1]>=1) { |
michael@0 | 495 | mbcsHeaderLength=MBCS_HEADER_V4_LENGTH; |
michael@0 | 496 | } else if(inMBCSHeader->version[0]==5 && inMBCSHeader->version[1]>=3 && |
michael@0 | 497 | ((mbcsHeaderOptions=ds->readUInt32(inMBCSHeader->options))& |
michael@0 | 498 | MBCS_OPT_UNKNOWN_INCOMPATIBLE_MASK)==0 |
michael@0 | 499 | ) { |
michael@0 | 500 | mbcsHeaderLength=mbcsHeaderOptions&MBCS_OPT_LENGTH_MASK; |
michael@0 | 501 | } else { |
michael@0 | 502 | udata_printError(ds, "icupkg/ucnv_enumDependencies(): unsupported _MBCSHeader.version %d.%d\n", |
michael@0 | 503 | inMBCSHeader->version[0], inMBCSHeader->version[1]); |
michael@0 | 504 | *pErrorCode=U_UNSUPPORTED_ERROR; |
michael@0 | 505 | return; |
michael@0 | 506 | } |
michael@0 | 507 | |
michael@0 | 508 | mbcsHeaderFlags=ds->readUInt32(inMBCSHeader->flags); |
michael@0 | 509 | extOffset=(int32_t)(mbcsHeaderFlags>>8); |
michael@0 | 510 | outputType=(uint8_t)mbcsHeaderFlags; |
michael@0 | 511 | |
michael@0 | 512 | if(outputType==MBCS_OUTPUT_EXT_ONLY) { |
michael@0 | 513 | /* |
michael@0 | 514 | * extension-only file, |
michael@0 | 515 | * contains a base name instead of normal base table data |
michael@0 | 516 | */ |
michael@0 | 517 | char baseName[32]; |
michael@0 | 518 | int32_t baseNameLength; |
michael@0 | 519 | |
michael@0 | 520 | /* there is extension data after the base data, see ucnv_ext.h */ |
michael@0 | 521 | if(length<(extOffset+UCNV_EXT_INDEXES_MIN_LENGTH*4)) { |
michael@0 | 522 | 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 | 523 | length); |
michael@0 | 524 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; |
michael@0 | 525 | return; |
michael@0 | 526 | } |
michael@0 | 527 | |
michael@0 | 528 | /* swap the base name, between the header and the extension data */ |
michael@0 | 529 | const char *inBaseName=(const char *)inBytes+mbcsHeaderLength*4; |
michael@0 | 530 | baseNameLength=(int32_t)strlen(inBaseName); |
michael@0 | 531 | if(baseNameLength>=(int32_t)sizeof(baseName)) { |
michael@0 | 532 | udata_printError(ds, "icupkg/ucnv_enumDependencies(%s): base name length %ld too long\n", |
michael@0 | 533 | itemName, baseNameLength); |
michael@0 | 534 | *pErrorCode=U_UNSUPPORTED_ERROR; |
michael@0 | 535 | return; |
michael@0 | 536 | } |
michael@0 | 537 | ds->swapInvChars(ds, inBaseName, baseNameLength+1, baseName, pErrorCode); |
michael@0 | 538 | |
michael@0 | 539 | checkIDSuffix(itemName, baseName, -1, ".cnv", check, context, pErrorCode); |
michael@0 | 540 | } |
michael@0 | 541 | } |
michael@0 | 542 | } |
michael@0 | 543 | |
michael@0 | 544 | // ICU data formats -------------------------------------------------------- *** |
michael@0 | 545 | |
michael@0 | 546 | static const struct { |
michael@0 | 547 | uint8_t dataFormat[4]; |
michael@0 | 548 | } dataFormats[]={ |
michael@0 | 549 | { { 0x52, 0x65, 0x73, 0x42 } }, /* dataFormat="ResB" */ |
michael@0 | 550 | { { 0x63, 0x6e, 0x76, 0x74 } }, /* dataFormat="cnvt" */ |
michael@0 | 551 | { { 0x43, 0x76, 0x41, 0x6c } } /* dataFormat="CvAl" */ |
michael@0 | 552 | }; |
michael@0 | 553 | |
michael@0 | 554 | enum { |
michael@0 | 555 | FMT_RES, |
michael@0 | 556 | FMT_CNV, |
michael@0 | 557 | FMT_ALIAS, |
michael@0 | 558 | FMT_COUNT |
michael@0 | 559 | }; |
michael@0 | 560 | |
michael@0 | 561 | static int32_t |
michael@0 | 562 | getDataFormat(const uint8_t dataFormat[4]) { |
michael@0 | 563 | int32_t i; |
michael@0 | 564 | |
michael@0 | 565 | for(i=0; i<FMT_COUNT; ++i) { |
michael@0 | 566 | if(0==memcmp(dataFormats[i].dataFormat, dataFormat, 4)) { |
michael@0 | 567 | return i; |
michael@0 | 568 | } |
michael@0 | 569 | } |
michael@0 | 570 | return -1; |
michael@0 | 571 | } |
michael@0 | 572 | |
michael@0 | 573 | // enumerate dependencies of a package item -------------------------------- *** |
michael@0 | 574 | |
michael@0 | 575 | void |
michael@0 | 576 | Package::enumDependencies(Item *pItem, void *context, CheckDependency check) { |
michael@0 | 577 | int32_t infoLength, itemHeaderLength; |
michael@0 | 578 | UErrorCode errorCode=U_ZERO_ERROR; |
michael@0 | 579 | const UDataInfo *pInfo=getDataInfo(pItem->data, pItem->length, infoLength, itemHeaderLength, &errorCode); |
michael@0 | 580 | if(U_FAILURE(errorCode)) { |
michael@0 | 581 | return; // should not occur because readFile() checks headers |
michael@0 | 582 | } |
michael@0 | 583 | |
michael@0 | 584 | // find the data format and call the corresponding function, if any |
michael@0 | 585 | int32_t format=getDataFormat(pInfo->dataFormat); |
michael@0 | 586 | if(format>=0) { |
michael@0 | 587 | switch(format) { |
michael@0 | 588 | case FMT_RES: |
michael@0 | 589 | { |
michael@0 | 590 | /* |
michael@0 | 591 | * Swap the resource bundle (if necessary) so that we can use |
michael@0 | 592 | * the normal runtime uresdata.c code to read it. |
michael@0 | 593 | * We do not want to duplicate that code, especially not together with on-the-fly swapping. |
michael@0 | 594 | */ |
michael@0 | 595 | NativeItem nrb(pItem, ures_swap); |
michael@0 | 596 | ures_enumDependencies(pItem->name, nrb.getDataInfo(), nrb.getBytes(), nrb.getLength(), check, context, this, &errorCode); |
michael@0 | 597 | break; |
michael@0 | 598 | } |
michael@0 | 599 | case FMT_CNV: |
michael@0 | 600 | { |
michael@0 | 601 | // TODO: share/cache swappers |
michael@0 | 602 | UDataSwapper *ds=udata_openSwapper( |
michael@0 | 603 | (UBool)pInfo->isBigEndian, pInfo->charsetFamily, |
michael@0 | 604 | U_IS_BIG_ENDIAN, U_CHARSET_FAMILY, |
michael@0 | 605 | &errorCode); |
michael@0 | 606 | if(U_FAILURE(errorCode)) { |
michael@0 | 607 | fprintf(stderr, "icupkg: udata_openSwapper(\"%s\") failed - %s\n", |
michael@0 | 608 | pItem->name, u_errorName(errorCode)); |
michael@0 | 609 | exit(errorCode); |
michael@0 | 610 | } |
michael@0 | 611 | |
michael@0 | 612 | ds->printError=printError; |
michael@0 | 613 | ds->printErrorContext=stderr; |
michael@0 | 614 | |
michael@0 | 615 | const uint8_t *inBytes=pItem->data+itemHeaderLength; |
michael@0 | 616 | int32_t length=pItem->length-itemHeaderLength; |
michael@0 | 617 | |
michael@0 | 618 | ucnv_enumDependencies(ds, pItem->name, pInfo, inBytes, length, check, context, &errorCode); |
michael@0 | 619 | udata_closeSwapper(ds); |
michael@0 | 620 | break; |
michael@0 | 621 | } |
michael@0 | 622 | default: |
michael@0 | 623 | break; |
michael@0 | 624 | } |
michael@0 | 625 | |
michael@0 | 626 | if(U_FAILURE(errorCode)) { |
michael@0 | 627 | exit(errorCode); |
michael@0 | 628 | } |
michael@0 | 629 | } |
michael@0 | 630 | } |
michael@0 | 631 | |
michael@0 | 632 | U_NAMESPACE_END |