2 *******************************************************************************
4 * Copyright (C) 2003-2008, 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 uresdata.c and 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"
37 /* item formats in common */
43 // general definitions ----------------------------------------------------- ***
45 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
49 static void U_CALLCONV
50 printError(void *context
, const char *fmt
, va_list args
) {
51 vfprintf((FILE *)context
, fmt
, args
);
56 // check a dependency ------------------------------------------------------ ***
59 * assemble the target item name from the source item name, an ID
63 checkIDSuffix(const char *itemName
, const char *id
, int32_t idLength
, const char *suffix
,
64 CheckDependency check
, void *context
,
65 UErrorCode
*pErrorCode
) {
68 int32_t treeLength
, suffixLength
, targetLength
;
70 // get the item basename
71 itemID
=strrchr(itemName
, '/');
78 // build the target string
79 treeLength
=(int32_t)(itemID
-itemName
);
81 idLength
=(int32_t)strlen(id
);
83 suffixLength
=(int32_t)strlen(suffix
);
84 targetLength
=treeLength
+idLength
+suffixLength
;
85 if(targetLength
>=(int32_t)sizeof(target
)) {
86 fprintf(stderr
, "icupkg/checkIDSuffix(%s) alias target item name length %ld too long\n",
87 itemName
, (long)targetLength
);
88 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
92 memcpy(target
, itemName
, treeLength
);
93 memcpy(target
+treeLength
, id
, idLength
);
94 memcpy(target
+treeLength
+idLength
, suffix
, suffixLength
+1); // +1 includes the terminating NUL
96 check(context
, itemName
, target
);
99 /* assemble the target item name from the item's parent item name */
101 checkParent(const char *itemName
, CheckDependency check
, void *context
,
102 UErrorCode
*pErrorCode
) {
103 const char *itemID
, *parent
, *parentLimit
, *suffix
;
104 int32_t parentLength
;
106 // get the item basename
107 itemID
=strrchr(itemName
, '/');
114 // get the item suffix
115 suffix
=strrchr(itemID
, '.');
117 // empty suffix, point to the end of the string
118 suffix
=strrchr(itemID
, 0);
121 // get the position of the last '_'
122 for(parentLimit
=suffix
; parentLimit
>itemID
&& *--parentLimit
!='_';) {}
124 if(parentLimit
!=itemID
) {
125 // get the parent item name by truncating the last part of this item's name */
127 parentLength
=(int32_t)(parentLimit
-itemID
);
129 // no '_' in the item name: the parent is the root bundle
132 if((suffix
-itemID
)==parentLength
&& 0==memcmp(itemID
, parent
, parentLength
)) {
133 // the item itself is "root", which does not depend on a parent
137 checkIDSuffix(itemName
, parent
, parentLength
, suffix
, check
, context
, pErrorCode
);
140 // get dependencies from resource bundles ---------------------------------- ***
142 static const char gAliasKey
[]="%%ALIAS";
143 static const char gDependencyKey
[]="%%DEPENDENCY";
144 enum { gAliasKeyLength
=7, gDependencyKeyLength
=12 };
147 * Enumerate one resource item and its children and extract dependencies from
149 * Code adapted from ures_preflightResource() and ures_swapResource().
152 ures_enumDependencies(const UDataSwapper
*ds
,
153 const char *itemName
,
154 const Resource
*inBundle
, int32_t length
,
155 Resource res
, const char *inKey
, const char *parentKey
, int32_t depth
,
156 CheckDependency check
, void *context
,
157 UErrorCode
*pErrorCode
) {
160 UBool useResSuffix
= TRUE
;
162 if(res
==0 || RES_GET_TYPE(res
)==URES_INT
) {
163 /* empty string or integer, nothing to do */
167 /* all other types use an offset to point to their data */
168 offset
=(int32_t)RES_GET_OFFSET(res
);
169 if(0<=length
&& length
<=offset
) {
170 udata_printError(ds
, "icupkg/ures_enumDependencies(%s res=%08x) resource offset exceeds bundle length %d\n",
171 itemName
, res
, length
);
172 *pErrorCode
=U_INDEX_OUTOFBOUNDS_ERROR
;
177 switch(RES_GET_TYPE(res
)) {
178 /* strings and aliases have physically the same value layout */
181 if(depth
==1 && inKey
!=NULL
) {
182 char key
[gAliasKeyLength
+1];
185 keyLength
=(int32_t)strlen(inKey
);
186 if(keyLength
!=gAliasKeyLength
) {
189 ds
->swapInvChars(ds
, inKey
, gAliasKeyLength
+1, key
, pErrorCode
);
190 if(U_FAILURE(*pErrorCode
)) {
191 udata_printError(ds
, "icupkg/ures_enumDependencies(%s res=%08x) string key contains variant characters\n",
195 if(0!=strcmp(key
, gAliasKey
)) {
199 // Check for %%DEPENDENCY
200 else if(depth
==2 && parentKey
!=NULL
) {
201 char key
[gDependencyKeyLength
+1];
204 keyLength
=(int32_t)strlen(parentKey
);
205 if(keyLength
!=gDependencyKeyLength
) {
208 ds
->swapInvChars(ds
, parentKey
, gDependencyKeyLength
+1, key
, pErrorCode
);
209 if(U_FAILURE(*pErrorCode
)) {
210 udata_printError(ds
, "icupkg/ures_enumDependencies(%s res=%08x) string key contains variant characters\n",
214 if(0!=strcmp(key
, gDependencyKey
)) {
217 useResSuffix
= FALSE
;
220 // we ignore all other strings
223 // for the top-level %%ALIAS or %%DEPENDENCY string fall through to URES_ALIAS
228 int32_t i
, stringLength
;
229 uint16_t u16
, ored16
;
231 stringLength
=udata_readInt32(ds
, (int32_t)*p
);
233 /* top=offset+1+(string length +1)/2 rounded up */
234 offset
+=1+((stringLength
+1)+1)/2;
236 break; // the resource does not fit into the bundle, print error below
239 // extract the locale ID from alias strings like
240 // locale_ID/key1/key2/key3
242 if(U_IS_BIG_ENDIAN
==ds
->inIsBigEndian
) {
243 u16
=0x2f; // slash in local endianness
245 u16
=0x2f00; // slash in opposite endianness
247 p16
=(const uint16_t *)(p
+1); // Unicode string contents
249 // search for the first slash
250 for(i
=0; i
<stringLength
&& p16
[i
]!=u16
; ++i
) {}
252 if(RES_GET_TYPE(res
)==URES_ALIAS
) {
253 // ignore aliases with an initial slash:
254 // /ICUDATA/... and /pkgname/... go to a different package
255 // /LOCALE/... are for dynamic sideways fallbacks and don't go to a fixed bundle
257 break; // initial slash ('/')
260 // ignore the intra-bundle path starting from the first slash ('/')
262 } else /* URES_STRING */ {
263 // the whole string should only consist of a locale ID
264 if(i
!=stringLength
) {
265 udata_printError(ds
, "icupkg/ures_enumDependencies(%s res=%08x) %%ALIAS contains a '/'\n",
267 *pErrorCode
=U_UNSUPPORTED_ERROR
;
272 // convert the Unicode string to char * and
273 // check that it has a bundle path but no package
274 if(stringLength
>=(int32_t)sizeof(localeID
)) {
275 udata_printError(ds
, "icupkg/ures_enumDependencies(%s res=%08x) alias locale ID length %ld too long\n",
276 itemName
, res
, stringLength
);
277 *pErrorCode
=U_BUFFER_OVERFLOW_ERROR
;
281 // convert the alias Unicode string to US-ASCII
283 if(U_IS_BIG_ENDIAN
==ds
->inIsBigEndian
) {
284 for(i
=0; i
<stringLength
; ++i
) {
287 localeID
[i
]=(char)u16
;
290 for(i
=0; i
<stringLength
; ++i
) {
293 localeID
[i
]=(char)(u16
>>8);
295 ored16
=(uint16_t)((ored16
<<8)|(ored16
>>8));
297 localeID
[stringLength
]=0;
299 udata_printError(ds
, "icupkg/ures_enumDependencies(%s res=%08x) alias string contains non-ASCII characters\n",
301 *pErrorCode
=U_INVALID_CHAR_FOUND
;
305 #if (U_CHARSET_FAMILY==U_EBCDIC_FAMILY)
307 // our swapper is probably not the right one, but
308 // the function uses it only for printing errors
309 uprv_ebcdicFromAscii(ds
, localeID
, stringLength
, localeID
, pErrorCode
);
310 if(U_FAILURE(*pErrorCode
)) {
314 #if U_CHARSET_FAMILY!=U_ASCII_FAMILY && U_CHARSET_FAMILY!=U_EBCDIC_FAMILY
315 # error Unknown U_CHARSET_FAMILY value!
318 checkIDSuffix(itemName
, localeID
, -1, (useResSuffix
? ".res" : ""), check
, context
, pErrorCode
);
324 const uint16_t *pKey16
;
325 const int32_t *pKey32
;
330 if(RES_GET_TYPE(res
)==URES_TABLE
) {
331 /* get table item count */
332 pKey16
=(const uint16_t *)p
;
333 count
=ds
->readUInt16(*pKey16
++);
337 /* top=((1+ table item count)/2 rounded up)+(table item count) */
338 offset
+=((1+count
)+1)/2;
340 /* get table item count */
341 pKey32
=(const int32_t *)p
;
342 count
=udata_readInt32(ds
, *pKey32
++);
346 /* top=(1+ table item count)+(table item count) */
350 p
=inBundle
+offset
; /* pointer to table resources */
354 break; // the resource does not fit into the bundle, print error below
358 for(i
=0; i
<count
; ++i
) {
359 item
=ds
->readUInt32(*p
++);
360 ures_enumDependencies(
361 ds
, itemName
, inBundle
, length
, item
,
362 ((const char *)inBundle
)+
364 ds
->readUInt16(pKey16
[i
]) :
365 udata_readInt32(ds
, pKey32
[i
])),
369 if(U_FAILURE(*pErrorCode
)) {
370 udata_printError(ds
, "icupkg/ures_enumDependencies(%s table res=%08x)[%d].recurse(%08x) failed\n",
371 itemName
, res
, i
, item
);
382 /* top=offset+1+(array length) */
383 count
=udata_readInt32(ds
, (int32_t)*p
++);
387 break; // the resource does not fit into the bundle, print error below
391 for(i
=0; i
<count
; ++i
) {
392 item
=ds
->readUInt32(*p
++);
393 ures_enumDependencies(
394 ds
, itemName
, inBundle
, length
,
395 item
, NULL
, inKey
, depth
+1,
398 if(U_FAILURE(*pErrorCode
)) {
399 udata_printError(ds
, "icupkg/ures_enumDependencies(%s array res=%08x)[%d].recurse(%08x) failed\n",
400 itemName
, res
, i
, item
);
410 if(U_FAILURE(*pErrorCode
)) {
412 } else if(0<=length
&& length
<offset
) {
413 udata_printError(ds
, "icupkg/ures_enumDependencies(%s res=%08x) resource limit exceeds bundle length %d\n",
414 itemName
, res
, length
);
415 *pErrorCode
=U_INDEX_OUTOFBOUNDS_ERROR
;
419 /* code adapted from ures_swap() */
421 ures_enumDependencies(const UDataSwapper
*ds
,
422 const char *itemName
, const UDataInfo
*pInfo
,
423 const uint8_t *inBytes
, int32_t length
,
424 CheckDependency check
, void *context
,
425 UErrorCode
*pErrorCode
) {
426 const Resource
*inBundle
;
429 /* the following integers count Resource item offsets (4 bytes each), not bytes */
430 int32_t bundleLength
;
432 /* check format version */
433 if(pInfo
->formatVersion
[0]!=1) {
434 fprintf(stderr
, "icupkg: .res format version %02x not supported\n",
435 pInfo
->formatVersion
[0]);
436 exit(U_UNSUPPORTED_ERROR
);
439 /* a resource bundle must contain at least one resource item */
440 bundleLength
=length
/4;
442 /* formatVersion 1.1 must have a root item and at least 5 indexes */
444 (pInfo
->formatVersion
[1]==0 ? 1 : 1+5)
446 fprintf(stderr
, "icupkg: too few bytes (%d after header) for a resource bundle\n",
448 exit(U_INDEX_OUTOFBOUNDS_ERROR
);
451 inBundle
=(const Resource
*)inBytes
;
452 rootRes
=ds
->readUInt32(*inBundle
);
454 ures_enumDependencies(
455 ds
, itemName
, inBundle
, bundleLength
,
456 rootRes
, NULL
, NULL
, 0,
461 * if the bundle attributes are present and the nofallback flag is not set,
462 * then add the parent bundle as a dependency
464 if(pInfo
->formatVersion
[1]>=1) {
465 int32_t indexes
[URES_INDEX_TOP
];
466 const int32_t *inIndexes
;
468 inIndexes
=(const int32_t *)inBundle
+1;
469 indexes
[URES_INDEX_LENGTH
]=udata_readInt32(ds
, inIndexes
[URES_INDEX_LENGTH
]);
470 if(indexes
[URES_INDEX_LENGTH
]>URES_INDEX_ATTRIBUTES
) {
471 indexes
[URES_INDEX_ATTRIBUTES
]=udata_readInt32(ds
, inIndexes
[URES_INDEX_ATTRIBUTES
]);
472 if(0==(indexes
[URES_INDEX_ATTRIBUTES
]&URES_ATT_NO_FALLBACK
)) {
473 /* this bundle participates in locale fallback */
474 checkParent(itemName
, check
, context
, pErrorCode
);
480 // get dependencies from conversion tables --------------------------------- ***
482 /* code adapted from ucnv_swap() */
484 ucnv_enumDependencies(const UDataSwapper
*ds
,
485 const char *itemName
, const UDataInfo
*pInfo
,
486 const uint8_t *inBytes
, int32_t length
,
487 CheckDependency check
, void *context
,
488 UErrorCode
*pErrorCode
) {
489 uint32_t staticDataSize
;
491 const UConverterStaticData
*inStaticData
;
493 const _MBCSHeader
*inMBCSHeader
;
496 /* check format version */
498 pInfo
->formatVersion
[0]==6 &&
499 pInfo
->formatVersion
[1]>=2
501 fprintf(stderr
, "icupkg/ucnv_enumDependencies(): .cnv format version %02x.%02x not supported\n",
502 pInfo
->formatVersion
[0], pInfo
->formatVersion
[1]);
503 exit(U_UNSUPPORTED_ERROR
);
506 /* read the initial UConverterStaticData structure after the UDataInfo header */
507 inStaticData
=(const UConverterStaticData
*)inBytes
;
509 if( length
<(int32_t)sizeof(UConverterStaticData
) ||
510 (uint32_t)length
<(staticDataSize
=ds
->readUInt32(inStaticData
->structSize
))
512 udata_printError(ds
, "icupkg/ucnv_enumDependencies(): too few bytes (%d after header) for an ICU .cnv conversion table\n",
514 *pErrorCode
=U_INDEX_OUTOFBOUNDS_ERROR
;
518 inBytes
+=staticDataSize
;
519 length
-=(int32_t)staticDataSize
;
521 /* check for supported conversionType values */
522 if(inStaticData
->conversionType
==UCNV_MBCS
) {
524 uint32_t mbcsHeaderLength
, mbcsHeaderFlags
, mbcsHeaderOptions
;
527 inMBCSHeader
=(const _MBCSHeader
*)inBytes
;
529 if(length
<(int32_t)sizeof(_MBCSHeader
)) {
530 udata_printError(ds
, "icupkg/ucnv_enumDependencies(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table\n",
532 *pErrorCode
=U_INDEX_OUTOFBOUNDS_ERROR
;
535 if(inMBCSHeader
->version
[0]==4 && inMBCSHeader
->version
[1]>=1) {
536 mbcsHeaderLength
=MBCS_HEADER_V4_LENGTH
;
537 } else if(inMBCSHeader
->version
[0]==5 && inMBCSHeader
->version
[1]>=3 &&
538 ((mbcsHeaderOptions
=ds
->readUInt32(inMBCSHeader
->options
))&
539 MBCS_OPT_UNKNOWN_INCOMPATIBLE_MASK
)==0
541 mbcsHeaderLength
=mbcsHeaderOptions
&MBCS_OPT_LENGTH_MASK
;
543 udata_printError(ds
, "icupkg/ucnv_enumDependencies(): unsupported _MBCSHeader.version %d.%d\n",
544 inMBCSHeader
->version
[0], inMBCSHeader
->version
[1]);
545 *pErrorCode
=U_UNSUPPORTED_ERROR
;
549 mbcsHeaderFlags
=ds
->readUInt32(inMBCSHeader
->flags
);
550 extOffset
=(int32_t)(mbcsHeaderFlags
>>8);
551 outputType
=(uint8_t)mbcsHeaderFlags
;
553 if(outputType
==MBCS_OUTPUT_EXT_ONLY
) {
555 * extension-only file,
556 * contains a base name instead of normal base table data
559 int32_t baseNameLength
;
561 /* there is extension data after the base data, see ucnv_ext.h */
562 if(length
<(extOffset
+UCNV_EXT_INDEXES_MIN_LENGTH
*4)) {
563 udata_printError(ds
, "icupkg/ucnv_enumDependencies(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table with extension data\n",
565 *pErrorCode
=U_INDEX_OUTOFBOUNDS_ERROR
;
569 /* swap the base name, between the header and the extension data */
570 const char *inBaseName
=(const char *)inBytes
+mbcsHeaderLength
*4;
571 baseNameLength
=(int32_t)strlen(inBaseName
);
572 if(baseNameLength
>=(int32_t)sizeof(baseName
)) {
573 udata_printError(ds
, "icupkg/ucnv_enumDependencies(%s): base name length %ld too long\n",
574 itemName
, baseNameLength
);
575 *pErrorCode
=U_UNSUPPORTED_ERROR
;
578 ds
->swapInvChars(ds
, inBaseName
, baseNameLength
+1, baseName
, pErrorCode
);
580 checkIDSuffix(itemName
, baseName
, -1, ".cnv", check
, context
, pErrorCode
);
585 // ICU data formats -------------------------------------------------------- ***
587 static const struct {
588 uint8_t dataFormat
[4];
590 { { 0x52, 0x65, 0x73, 0x42 } }, /* dataFormat="ResB" */
591 { { 0x63, 0x6e, 0x76, 0x74 } }, /* dataFormat="cnvt" */
592 { { 0x43, 0x76, 0x41, 0x6c } } /* dataFormat="CvAl" */
603 getDataFormat(const uint8_t dataFormat
[4]) {
606 for(i
=0; i
<FMT_COUNT
; ++i
) {
607 if(0==memcmp(dataFormats
[i
].dataFormat
, dataFormat
, 4)) {
614 // enumerate dependencies of a package item -------------------------------- ***
619 Package::enumDependencies(Item
*pItem
, void *context
, CheckDependency check
) {
620 const UDataInfo
*pInfo
;
621 const uint8_t *inBytes
;
622 int32_t format
, length
, infoLength
, itemHeaderLength
;
623 UErrorCode errorCode
;
625 errorCode
=U_ZERO_ERROR
;
626 pInfo
=getDataInfo(pItem
->data
,pItem
->length
, infoLength
, itemHeaderLength
, &errorCode
);
627 if(U_FAILURE(errorCode
)) {
628 return; // should not occur because readFile() checks headers
631 // find the data format and call the corresponding function, if any
632 format
=getDataFormat(pInfo
->dataFormat
);
636 // TODO: share/cache swappers
637 ds
=udata_openSwapper((UBool
)pInfo
->isBigEndian
, pInfo
->charsetFamily
, U_IS_BIG_ENDIAN
, U_CHARSET_FAMILY
, &errorCode
);
638 if(U_FAILURE(errorCode
)) {
639 fprintf(stderr
, "icupkg: udata_openSwapper(\"%s\") failed - %s\n",
640 pItem
->name
, u_errorName(errorCode
));
644 ds
->printError
=printError
;
645 ds
->printErrorContext
=stderr
;
647 inBytes
=pItem
->data
+itemHeaderLength
;
648 length
=pItem
->length
-itemHeaderLength
;
652 ures_enumDependencies(ds
, pItem
->name
, pInfo
, inBytes
, length
, check
, context
, &errorCode
);
655 ucnv_enumDependencies(ds
, pItem
->name
, pInfo
, inBytes
, length
, check
, context
, &errorCode
);
661 udata_closeSwapper(ds
);
663 if(U_FAILURE(errorCode
)) {