2 *******************************************************************************
4 * Copyright (C) 2003-2014, International Business Machines
5 * Corporation and others. All Rights Reserved.
7 *******************************************************************************
8 * file name: pkgitems.cpp
10 * tab size: 8 (not used)
13 * created on: 2005sep18
14 * created by: Markus W. Scherer
16 * Companion file to package.cpp. Deals with details of ICU data item formats.
17 * Used for item dependencies.
18 * Contains adapted code from ucnv_bld.c (swapper code from 2003).
21 #include "unicode/utypes.h"
22 #include "unicode/ures.h"
23 #include "unicode/putil.h"
24 #include "unicode/udata.h"
38 /* item formats in common */
44 // general definitions ----------------------------------------------------- ***
48 static void U_CALLCONV
49 printError(void *context
, const char *fmt
, va_list args
) {
50 vfprintf((FILE *)context
, fmt
, args
);
55 // a data item in native-platform form ------------------------------------- ***
61 NativeItem() : pItem(NULL
), pInfo(NULL
), bytes(NULL
), swapped(NULL
), length(0) {}
62 NativeItem(const Item
*item
, UDataSwapFn
*swap
) : swapped(NULL
) {
68 const UDataInfo
*getDataInfo() const {
71 const uint8_t *getBytes() const {
74 int32_t getLength() const {
78 void setItem(const Item
*item
, UDataSwapFn
*swap
) {
80 int32_t infoLength
, itemHeaderLength
;
81 UErrorCode errorCode
=U_ZERO_ERROR
;
82 pInfo
=::getDataInfo(pItem
->data
, pItem
->length
, infoLength
, itemHeaderLength
, &errorCode
);
83 if(U_FAILURE(errorCode
)) {
84 exit(errorCode
); // should succeed because readFile() checks headers
86 length
=pItem
->length
-itemHeaderLength
;
88 if(pInfo
->isBigEndian
==U_IS_BIG_ENDIAN
&& pInfo
->charsetFamily
==U_CHARSET_FAMILY
) {
89 bytes
=pItem
->data
+itemHeaderLength
;
91 UDataSwapper
*ds
=udata_openSwapper((UBool
)pInfo
->isBigEndian
, pInfo
->charsetFamily
, U_IS_BIG_ENDIAN
, U_CHARSET_FAMILY
, &errorCode
);
92 if(U_FAILURE(errorCode
)) {
93 fprintf(stderr
, "icupkg: udata_openSwapper(\"%s\") failed - %s\n",
94 pItem
->name
, u_errorName(errorCode
));
98 ds
->printError
=printError
;
99 ds
->printErrorContext
=stderr
;
101 swapped
=new uint8_t[pItem
->length
];
103 fprintf(stderr
, "icupkg: unable to allocate memory for swapping \"%s\"\n", pItem
->name
);
104 exit(U_MEMORY_ALLOCATION_ERROR
);
106 swap(ds
, pItem
->data
, pItem
->length
, swapped
, &errorCode
);
107 pInfo
=::getDataInfo(swapped
, pItem
->length
, infoLength
, itemHeaderLength
, &errorCode
);
108 bytes
=swapped
+itemHeaderLength
;
109 udata_closeSwapper(ds
);
115 const UDataInfo
*pInfo
;
116 const uint8_t *bytes
;
121 // check a dependency ------------------------------------------------------ ***
124 * assemble the target item name from the source item name, an ID
128 makeTargetName(const char *itemName
, const char *id
, int32_t idLength
, const char *suffix
,
129 char *target
, int32_t capacity
,
130 UErrorCode
*pErrorCode
) {
132 int32_t treeLength
, suffixLength
, targetLength
;
134 // get the item basename
135 itemID
=strrchr(itemName
, '/');
142 // build the target string
143 treeLength
=(int32_t)(itemID
-itemName
);
145 idLength
=(int32_t)strlen(id
);
147 suffixLength
=(int32_t)strlen(suffix
);
148 targetLength
=treeLength
+idLength
+suffixLength
;
149 if(targetLength
>=capacity
) {
150 fprintf(stderr
, "icupkg/makeTargetName(%s) target item name length %ld too long\n",
151 itemName
, (long)targetLength
);
152 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
156 memcpy(target
, itemName
, treeLength
);
157 memcpy(target
+treeLength
, id
, idLength
);
158 memcpy(target
+treeLength
+idLength
, suffix
, suffixLength
+1); // +1 includes the terminating NUL
162 checkIDSuffix(const char *itemName
, const char *id
, int32_t idLength
, const char *suffix
,
163 CheckDependency check
, void *context
,
164 UErrorCode
*pErrorCode
) {
166 makeTargetName(itemName
, id
, idLength
, suffix
, target
, (int32_t)sizeof(target
), pErrorCode
);
167 if(U_SUCCESS(*pErrorCode
)) {
168 check(context
, itemName
, target
);
172 /* assemble the target item name from the item's parent item name */
174 checkParent(const char *itemName
, CheckDependency check
, void *context
,
175 UErrorCode
*pErrorCode
) {
176 const char *itemID
, *parent
, *parentLimit
, *suffix
;
177 int32_t parentLength
;
179 // get the item basename
180 itemID
=strrchr(itemName
, '/');
187 // get the item suffix
188 suffix
=strrchr(itemID
, '.');
190 // empty suffix, point to the end of the string
191 suffix
=strrchr(itemID
, 0);
194 // get the position of the last '_'
195 for(parentLimit
=suffix
; parentLimit
>itemID
&& *--parentLimit
!='_';) {}
197 if(parentLimit
!=itemID
) {
198 // get the parent item name by truncating the last part of this item's name */
200 parentLength
=(int32_t)(parentLimit
-itemID
);
202 // no '_' in the item name: the parent is the root bundle
205 if((suffix
-itemID
)==parentLength
&& 0==memcmp(itemID
, parent
, parentLength
)) {
206 // the item itself is "root", which does not depend on a parent
210 checkIDSuffix(itemName
, parent
, parentLength
, suffix
, check
, context
, pErrorCode
);
213 // get dependencies from resource bundles ---------------------------------- ***
215 static const UChar SLASH
=0x2f;
218 * Check for the alias from the string or alias resource res.
221 checkAlias(const char *itemName
,
222 Resource res
, const UChar
*alias
, int32_t length
, UBool useResSuffix
,
223 CheckDependency check
, void *context
, UErrorCode
*pErrorCode
) {
226 if(!uprv_isInvariantUString(alias
, length
)) {
227 fprintf(stderr
, "icupkg/ures_enumDependencies(%s res=%08x) alias string contains non-invariant characters\n",
229 *pErrorCode
=U_INVALID_CHAR_FOUND
;
233 // extract the locale ID from alias strings like
234 // locale_ID/key1/key2/key3
237 // search for the first slash
238 for(i
=0; i
<length
&& alias
[i
]!=SLASH
; ++i
) {}
240 if(res_getPublicType(res
)==URES_ALIAS
) {
241 // ignore aliases with an initial slash:
242 // /ICUDATA/... and /pkgname/... go to a different package
243 // /LOCALE/... are for dynamic sideways fallbacks and don't go to a fixed bundle
245 return; // initial slash ('/')
248 // ignore the intra-bundle path starting from the first slash ('/')
250 } else /* URES_STRING */ {
251 // the whole string should only consist of a locale ID
253 fprintf(stderr
, "icupkg/ures_enumDependencies(%s res=%08x) %%ALIAS contains a '/'\n",
255 *pErrorCode
=U_UNSUPPORTED_ERROR
;
260 // convert the Unicode string to char *
262 if(length
>=(int32_t)sizeof(localeID
)) {
263 fprintf(stderr
, "icupkg/ures_enumDependencies(%s res=%08x) alias locale ID length %ld too long\n",
264 itemName
, res
, (long)length
);
265 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
268 u_UCharsToChars(alias
, localeID
, length
);
271 checkIDSuffix(itemName
, localeID
, -1, (useResSuffix
? ".res" : ""), check
, context
, pErrorCode
);
275 * Enumerate one resource item and its children and extract dependencies from
279 ures_enumDependencies(const char *itemName
,
280 const ResourceData
*pResData
,
281 Resource res
, const char *inKey
, const char *parentKey
, int32_t depth
,
282 CheckDependency check
, void *context
,
284 UErrorCode
*pErrorCode
) {
285 switch(res_getPublicType(res
)) {
288 UBool useResSuffix
= TRUE
;
290 if(depth
==1 && inKey
!=NULL
) {
291 if(0!=strcmp(inKey
, "%%ALIAS")) {
295 // Check for %%DEPENDENCY
296 else if(depth
==2 && parentKey
!=NULL
) {
297 if(0!=strcmp(parentKey
, "%%DEPENDENCY")) {
300 useResSuffix
= FALSE
;
302 // we ignore all other strings
306 const UChar
*alias
=res_getString(pResData
, res
, &length
);
307 checkAlias(itemName
, res
, alias
, length
, useResSuffix
, check
, context
, pErrorCode
);
313 const UChar
*alias
=res_getAlias(pResData
, res
, &length
);
314 checkAlias(itemName
, res
, alias
, length
, TRUE
, check
, context
, pErrorCode
);
320 int32_t count
=res_countArrayItems(pResData
, res
);
321 for(int32_t i
=0; i
<count
; ++i
) {
323 Resource item
=res_getTableItemByIndex(pResData
, res
, i
, &itemKey
);
324 ures_enumDependencies(
331 if(U_FAILURE(*pErrorCode
)) {
332 fprintf(stderr
, "icupkg/ures_enumDependencies(%s table res=%08x)[%d].recurse(%s: %08x) failed\n",
333 itemName
, res
, i
, itemKey
, item
);
342 int32_t count
=res_countArrayItems(pResData
, res
);
343 for(int32_t i
=0; i
<count
; ++i
) {
344 Resource item
=res_getArrayItem(pResData
, res
, i
);
345 ures_enumDependencies(
352 if(U_FAILURE(*pErrorCode
)) {
353 fprintf(stderr
, "icupkg/ures_enumDependencies(%s array res=%08x)[%d].recurse(%08x) failed\n",
354 itemName
, res
, i
, item
);
366 ures_enumDependencies(const char *itemName
, const UDataInfo
*pInfo
,
367 const uint8_t *inBytes
, int32_t length
,
368 CheckDependency check
, void *context
,
370 UErrorCode
*pErrorCode
) {
371 ResourceData resData
;
373 res_read(&resData
, pInfo
, inBytes
, length
, pErrorCode
);
374 if(U_FAILURE(*pErrorCode
)) {
375 fprintf(stderr
, "icupkg: .res format version %02x.%02x not supported, or bundle malformed\n",
376 pInfo
->formatVersion
[0], pInfo
->formatVersion
[1]);
377 exit(U_UNSUPPORTED_ERROR
);
381 * if the bundle attributes are present and the nofallback flag is not set,
382 * then add the parent bundle as a dependency
384 if(pInfo
->formatVersion
[0]>1 || (pInfo
->formatVersion
[0]==1 && pInfo
->formatVersion
[1]>=1)) {
385 if(!resData
.noFallback
) {
386 /* this bundle participates in locale fallback */
387 checkParent(itemName
, check
, context
, pErrorCode
);
391 icu::NativeItem nativePool
;
393 if(resData
.usesPoolBundle
) {
395 makeTargetName(itemName
, "pool", 4, ".res", poolName
, (int32_t)sizeof(poolName
), pErrorCode
);
396 if(U_FAILURE(*pErrorCode
)) {
399 check(context
, itemName
, poolName
);
400 int32_t index
=pkg
->findItem(poolName
);
402 // We cannot work with a bundle if its pool resource is missing.
403 // check() already printed a complaint.
406 // TODO: Cache the native version in the Item itself.
407 nativePool
.setItem(pkg
->getItem(index
), ures_swap
);
408 const UDataInfo
*poolInfo
=nativePool
.getDataInfo();
409 if(poolInfo
->formatVersion
[0]<=1) {
410 fprintf(stderr
, "icupkg: %s is not a pool bundle\n", poolName
);
413 const int32_t *poolIndexes
=(const int32_t *)nativePool
.getBytes()+1;
414 int32_t poolIndexLength
=poolIndexes
[URES_INDEX_LENGTH
]&0xff;
415 if(!(poolIndexLength
>URES_INDEX_POOL_CHECKSUM
&&
416 (poolIndexes
[URES_INDEX_ATTRIBUTES
]&URES_ATT_IS_POOL_BUNDLE
))
418 fprintf(stderr
, "icupkg: %s is not a pool bundle\n", poolName
);
421 if(resData
.pRoot
[1+URES_INDEX_POOL_CHECKSUM
]==poolIndexes
[URES_INDEX_POOL_CHECKSUM
]) {
422 resData
.poolBundleKeys
=(const char *)(poolIndexes
+poolIndexLength
);
424 fprintf(stderr
, "icupkg: %s has mismatched checksum for %s\n", poolName
, itemName
);
429 ures_enumDependencies(
431 resData
.rootRes
, NULL
, NULL
, 0,
437 // get dependencies from conversion tables --------------------------------- ***
439 /* code adapted from ucnv_swap() */
441 ucnv_enumDependencies(const UDataSwapper
*ds
,
442 const char *itemName
, const UDataInfo
*pInfo
,
443 const uint8_t *inBytes
, int32_t length
,
444 CheckDependency check
, void *context
,
445 UErrorCode
*pErrorCode
) {
446 uint32_t staticDataSize
;
448 const UConverterStaticData
*inStaticData
;
450 const _MBCSHeader
*inMBCSHeader
;
453 /* check format version */
455 pInfo
->formatVersion
[0]==6 &&
456 pInfo
->formatVersion
[1]>=2
458 fprintf(stderr
, "icupkg/ucnv_enumDependencies(): .cnv format version %02x.%02x not supported\n",
459 pInfo
->formatVersion
[0], pInfo
->formatVersion
[1]);
460 exit(U_UNSUPPORTED_ERROR
);
463 /* read the initial UConverterStaticData structure after the UDataInfo header */
464 inStaticData
=(const UConverterStaticData
*)inBytes
;
466 if( length
<(int32_t)sizeof(UConverterStaticData
) ||
467 (uint32_t)length
<(staticDataSize
=ds
->readUInt32(inStaticData
->structSize
))
469 udata_printError(ds
, "icupkg/ucnv_enumDependencies(): too few bytes (%d after header) for an ICU .cnv conversion table\n",
471 *pErrorCode
=U_INDEX_OUTOFBOUNDS_ERROR
;
475 inBytes
+=staticDataSize
;
476 length
-=(int32_t)staticDataSize
;
478 /* check for supported conversionType values */
479 if(inStaticData
->conversionType
==UCNV_MBCS
) {
481 uint32_t mbcsHeaderLength
, mbcsHeaderFlags
, mbcsHeaderOptions
;
484 inMBCSHeader
=(const _MBCSHeader
*)inBytes
;
486 if(length
<(int32_t)sizeof(_MBCSHeader
)) {
487 udata_printError(ds
, "icupkg/ucnv_enumDependencies(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table\n",
489 *pErrorCode
=U_INDEX_OUTOFBOUNDS_ERROR
;
492 if(inMBCSHeader
->version
[0]==4 && inMBCSHeader
->version
[1]>=1) {
493 mbcsHeaderLength
=MBCS_HEADER_V4_LENGTH
;
494 } else if(inMBCSHeader
->version
[0]==5 && inMBCSHeader
->version
[1]>=3 &&
495 ((mbcsHeaderOptions
=ds
->readUInt32(inMBCSHeader
->options
))&
496 MBCS_OPT_UNKNOWN_INCOMPATIBLE_MASK
)==0
498 mbcsHeaderLength
=mbcsHeaderOptions
&MBCS_OPT_LENGTH_MASK
;
500 udata_printError(ds
, "icupkg/ucnv_enumDependencies(): unsupported _MBCSHeader.version %d.%d\n",
501 inMBCSHeader
->version
[0], inMBCSHeader
->version
[1]);
502 *pErrorCode
=U_UNSUPPORTED_ERROR
;
506 mbcsHeaderFlags
=ds
->readUInt32(inMBCSHeader
->flags
);
507 extOffset
=(int32_t)(mbcsHeaderFlags
>>8);
508 outputType
=(uint8_t)mbcsHeaderFlags
;
510 if(outputType
==MBCS_OUTPUT_EXT_ONLY
) {
512 * extension-only file,
513 * contains a base name instead of normal base table data
516 int32_t baseNameLength
;
518 /* there is extension data after the base data, see ucnv_ext.h */
519 if(length
<(extOffset
+UCNV_EXT_INDEXES_MIN_LENGTH
*4)) {
520 udata_printError(ds
, "icupkg/ucnv_enumDependencies(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table with extension data\n",
522 *pErrorCode
=U_INDEX_OUTOFBOUNDS_ERROR
;
526 /* swap the base name, between the header and the extension data */
527 const char *inBaseName
=(const char *)inBytes
+mbcsHeaderLength
*4;
528 baseNameLength
=(int32_t)strlen(inBaseName
);
529 if(baseNameLength
>=(int32_t)sizeof(baseName
)) {
530 udata_printError(ds
, "icupkg/ucnv_enumDependencies(%s): base name length %ld too long\n",
531 itemName
, baseNameLength
);
532 *pErrorCode
=U_UNSUPPORTED_ERROR
;
535 ds
->swapInvChars(ds
, inBaseName
, baseNameLength
+1, baseName
, pErrorCode
);
537 checkIDSuffix(itemName
, baseName
, -1, ".cnv", check
, context
, pErrorCode
);
542 // ICU data formats -------------------------------------------------------- ***
544 static const struct {
545 uint8_t dataFormat
[4];
547 { { 0x52, 0x65, 0x73, 0x42 } }, /* dataFormat="ResB" */
548 { { 0x63, 0x6e, 0x76, 0x74 } }, /* dataFormat="cnvt" */
549 { { 0x43, 0x76, 0x41, 0x6c } } /* dataFormat="CvAl" */
560 getDataFormat(const uint8_t dataFormat
[4]) {
563 for(i
=0; i
<FMT_COUNT
; ++i
) {
564 if(0==memcmp(dataFormats
[i
].dataFormat
, dataFormat
, 4)) {
571 // enumerate dependencies of a package item -------------------------------- ***
574 Package::enumDependencies(Item
*pItem
, void *context
, CheckDependency check
) {
575 int32_t infoLength
, itemHeaderLength
;
576 UErrorCode errorCode
=U_ZERO_ERROR
;
577 const UDataInfo
*pInfo
=getDataInfo(pItem
->data
, pItem
->length
, infoLength
, itemHeaderLength
, &errorCode
);
578 if(U_FAILURE(errorCode
)) {
579 return; // should not occur because readFile() checks headers
582 // find the data format and call the corresponding function, if any
583 int32_t format
=getDataFormat(pInfo
->dataFormat
);
589 * Swap the resource bundle (if necessary) so that we can use
590 * the normal runtime uresdata.c code to read it.
591 * We do not want to duplicate that code, especially not together with on-the-fly swapping.
593 NativeItem
nrb(pItem
, ures_swap
);
594 ures_enumDependencies(pItem
->name
, nrb
.getDataInfo(), nrb
.getBytes(), nrb
.getLength(), check
, context
, this, &errorCode
);
599 // TODO: share/cache swappers
600 UDataSwapper
*ds
=udata_openSwapper(
601 (UBool
)pInfo
->isBigEndian
, pInfo
->charsetFamily
,
602 U_IS_BIG_ENDIAN
, U_CHARSET_FAMILY
,
604 if(U_FAILURE(errorCode
)) {
605 fprintf(stderr
, "icupkg: udata_openSwapper(\"%s\") failed - %s\n",
606 pItem
->name
, u_errorName(errorCode
));
610 ds
->printError
=printError
;
611 ds
->printErrorContext
=stderr
;
613 const uint8_t *inBytes
=pItem
->data
+itemHeaderLength
;
614 int32_t length
=pItem
->length
-itemHeaderLength
;
616 ucnv_enumDependencies(ds
, pItem
->name
, pInfo
, inBytes
, length
, check
, context
, &errorCode
);
617 udata_closeSwapper(ds
);
624 if(U_FAILURE(errorCode
)) {