2 *******************************************************************************
4 * Copyright (C) 1999-2006, International Business Machines
5 * Corporation and others. All Rights Reserved.
7 *******************************************************************************
8 * file name: package.cpp
10 * tab size: 8 (not used)
13 * created on: 2005aug25
14 * created by: Markus W. Scherer
16 * Read, modify, and write ICU .dat data package files.
17 * This is an integral part of the icupkg tool, moved to the toolutil library
18 * because parts of tool implementations tend to be later shared by
20 * Subsumes functionality and implementation code from
21 * gencmn, decmn, and icuswap tools.
24 #include "unicode/utypes.h"
25 #include "unicode/putil.h"
26 #include "unicode/udata.h"
39 // general definitions ----------------------------------------------------- ***
41 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
43 /* UDataInfo cf. udata.h */
44 static const UDataInfo dataInfo
={
45 (uint16_t)sizeof(UDataInfo
),
50 (uint8_t)sizeof(UChar
),
53 {0x43, 0x6d, 0x6e, 0x44}, /* dataFormat="CmnD" */
54 {1, 0, 0, 0}, /* formatVersion */
55 {3, 0, 0, 0} /* dataVersion */
59 static void U_CALLCONV
60 printPackageError(void *context
, const char *fmt
, va_list args
) {
61 vfprintf((FILE *)context
, fmt
, args
);
66 readSwapUInt16(uint16_t x
) {
67 return (uint16_t)((x
<<8)|(x
>>8));
70 // platform types ---------------------------------------------------------- ***
72 static const char *types
="lb?e";
74 enum { TYPE_L
, TYPE_B
, TYPE_LE
, TYPE_E
, TYPE_COUNT
};
77 makeTypeEnum(uint8_t charset
, UBool isBigEndian
) {
78 return 2*(int32_t)charset
+isBigEndian
;
82 makeTypeEnum(char type
) {
84 type
== 'l' ? TYPE_L
:
85 type
== 'b' ? TYPE_B
:
86 type
== 'e' ? TYPE_E
:
91 makeTypeLetter(uint8_t charset
, UBool isBigEndian
) {
92 return types
[makeTypeEnum(charset
, isBigEndian
)];
96 makeTypeLetter(int32_t typeEnum
) {
97 return types
[typeEnum
];
101 makeTypeProps(char type
, uint8_t &charset
, UBool
&isBigEndian
) {
102 int32_t typeEnum
=makeTypeEnum(type
);
103 charset
=(uint8_t)(typeEnum
>>1);
104 isBigEndian
=(UBool
)(typeEnum
&1);
107 U_CFUNC
const UDataInfo
*
108 getDataInfo(const uint8_t *data
, int32_t length
,
109 int32_t &infoLength
, int32_t &headerLength
,
110 UErrorCode
*pErrorCode
) {
111 const DataHeader
*pHeader
;
112 const UDataInfo
*pInfo
;
114 if(pErrorCode
==NULL
|| U_FAILURE(*pErrorCode
)) {
118 (length
>=0 && length
<(int32_t)sizeof(DataHeader
))
120 *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
;
124 pHeader
=(const DataHeader
*)data
;
125 pInfo
=&pHeader
->info
;
126 if( (length
>=0 && length
<(int32_t)sizeof(DataHeader
)) ||
127 pHeader
->dataHeader
.magic1
!=0xda ||
128 pHeader
->dataHeader
.magic2
!=0x27 ||
129 pInfo
->sizeofUChar
!=2
131 *pErrorCode
=U_UNSUPPORTED_ERROR
;
135 if(pInfo
->isBigEndian
==U_IS_BIG_ENDIAN
) {
136 headerLength
=pHeader
->dataHeader
.headerSize
;
137 infoLength
=pInfo
->size
;
139 headerLength
=readSwapUInt16(pHeader
->dataHeader
.headerSize
);
140 infoLength
=readSwapUInt16(pInfo
->size
);
143 if( headerLength
<(int32_t)sizeof(DataHeader
) ||
144 infoLength
<(int32_t)sizeof(UDataInfo
) ||
145 headerLength
<(int32_t)(sizeof(pHeader
->dataHeader
)+infoLength
) ||
146 (length
>=0 && length
<headerLength
)
148 *pErrorCode
=U_UNSUPPORTED_ERROR
;
156 getTypeEnumForInputData(const uint8_t *data
, int32_t length
,
157 UErrorCode
*pErrorCode
) {
158 const UDataInfo
*pInfo
;
159 int32_t infoLength
, headerLength
;
161 /* getDataInfo() checks for illegal arguments */
162 pInfo
=getDataInfo(data
, length
, infoLength
, headerLength
, pErrorCode
);
167 return makeTypeEnum(pInfo
->charsetFamily
, (UBool
)pInfo
->isBigEndian
);
170 // file handling ----------------------------------------------------------- ***
173 extractPackageName(const char *filename
,
174 char pkg
[], int32_t capacity
) {
175 const char *basename
;
178 basename
=findBasename(filename
);
179 len
=(int32_t)strlen(basename
)-4; /* -4: subtract the length of ".dat" */
181 if(len
<=0 || 0!=strcmp(basename
+len
, ".dat")) {
182 fprintf(stderr
, "icupkg: \"%s\" is not recognized as a package filename (must end with .dat)\n",
184 exit(U_ILLEGAL_ARGUMENT_ERROR
);
188 fprintf(stderr
, "icupkg: the package name \"%s\" is too long (>=%ld)\n",
189 basename
, (long)capacity
);
190 exit(U_ILLEGAL_ARGUMENT_ERROR
);
193 memcpy(pkg
, basename
, len
);
198 getFileLength(FILE *f
) {
201 fseek(f
, 0, SEEK_END
);
202 length
=(int32_t)ftell(f
);
203 fseek(f
, 0, SEEK_SET
);
208 * Turn tree separators and alternate file separators into normal file separators.
210 #if U_TREE_ENTRY_SEP_CHAR==U_FILE_SEP_CHAR && U_FILE_ALT_SEP_CHAR==U_FILE_SEP_CHAR
211 #define treeToPath(s)
214 treeToPath(char *s
) {
217 for(t
=s
; *t
!=0; ++t
) {
218 if(*t
==U_TREE_ENTRY_SEP_CHAR
|| *t
==U_FILE_ALT_SEP_CHAR
) {
226 * Turn file separators into tree separators.
228 #if U_TREE_ENTRY_SEP_CHAR==U_FILE_SEP_CHAR && U_FILE_ALT_SEP_CHAR==U_FILE_SEP_CHAR
229 #define pathToTree(s)
232 pathToTree(char *s
) {
235 for(t
=s
; *t
!=0; ++t
) {
236 if(*t
==U_FILE_SEP_CHAR
|| *t
==U_FILE_ALT_SEP_CHAR
) {
237 *t
=U_TREE_ENTRY_SEP_CHAR
;
244 * Prepend the path (if any) to the name and run the name through treeToName().
247 makeFullFilename(const char *path
, const char *name
,
248 char *filename
, int32_t capacity
) {
251 // prepend the path unless NULL or empty
252 if(path
!=NULL
&& path
[0]!=0) {
253 if((int32_t)(strlen(path
)+1)>=capacity
) {
254 fprintf(stderr
, "pathname too long: \"%s\"\n", path
);
255 exit(U_BUFFER_OVERFLOW_ERROR
);
257 strcpy(filename
, path
);
259 // make sure the path ends with a file separator
260 s
=strchr(filename
, 0);
261 if(*(s
-1)!=U_FILE_SEP_CHAR
&& *(s
-1)!=U_FILE_ALT_SEP_CHAR
) {
262 *s
++=U_FILE_SEP_CHAR
;
268 // turn the name into a filename, turn tree separators into file separators
269 if((int32_t)((s
-filename
)+strlen(name
))>=capacity
) {
270 fprintf(stderr
, "path/filename too long: \"%s%s\"\n", filename
, name
);
271 exit(U_BUFFER_OVERFLOW_ERROR
);
278 makeFullFilenameAndDirs(const char *path
, const char *name
,
279 char *filename
, int32_t capacity
) {
281 UErrorCode errorCode
;
283 makeFullFilename(path
, name
, filename
, capacity
);
285 // make tree directories
286 errorCode
=U_ZERO_ERROR
;
287 sep
=strchr(filename
, 0)-strlen(name
);
288 while((sep
=strchr(sep
, U_FILE_SEP_CHAR
))!=NULL
) {
290 *sep
=0; // truncate temporarily
291 uprv_mkdir(filename
, &errorCode
);
292 if(U_FAILURE(errorCode
)) {
293 fprintf(stderr
, "icupkg: unable to create tree directory \"%s\"\n", filename
);
294 exit(U_FILE_ACCESS_ERROR
);
297 *sep
++=U_FILE_SEP_CHAR
; // restore file separator character
302 readFile(const char *path
, const char *name
, int32_t &length
, char &type
) {
306 UErrorCode errorCode
;
307 int32_t fileLength
, typeEnum
;
309 makeFullFilename(path
, name
, filename
, (int32_t)sizeof(filename
));
311 /* open the input file, get its length, allocate memory for it, read the file */
312 file
=fopen(filename
, "rb");
314 fprintf(stderr
, "icupkg: unable to open input file \"%s\"\n", filename
);
315 exit(U_FILE_ACCESS_ERROR
);
318 /* get the file length */
319 fileLength
=getFileLength(file
);
320 if(ferror(file
) || fileLength
<=0) {
321 fprintf(stderr
, "icupkg: empty input file \"%s\"\n", filename
);
323 exit(U_FILE_ACCESS_ERROR
);
326 /* allocate the buffer, pad to multiple of 16 */
327 length
=(fileLength
+0xf)&~0xf;
328 data
=(uint8_t *)malloc(length
);
331 exit(U_MEMORY_ALLOCATION_ERROR
);
335 if(fileLength
!=(int32_t)fread(data
, 1, fileLength
, file
)) {
336 fprintf(stderr
, "icupkg: error reading \"%s\"\n", filename
);
339 exit(U_FILE_ACCESS_ERROR
);
342 /* pad the file to a multiple of 16 using the usual padding byte */
343 if(fileLength
<length
) {
344 memset(data
+fileLength
, 0xaa, length
-fileLength
);
349 // minimum check for ICU-format data
350 errorCode
=U_ZERO_ERROR
;
351 typeEnum
=getTypeEnumForInputData(data
, length
, &errorCode
);
352 if(typeEnum
<0 || U_FAILURE(errorCode
)) {
353 fprintf(stderr
, "icupkg: not an ICU data file: \"%s\"\n", filename
);
355 exit(U_INVALID_FORMAT_ERROR
);
357 type
=makeTypeLetter(typeEnum
);
362 // .dat package file representation ---------------------------------------- ***
366 static int32_t U_CALLCONV
367 compareItems(const void * /*context*/, const void *left
, const void *right
) {
368 return (int32_t)strcmp(((Item
*)left
)->name
, ((Item
*)right
)->name
);
377 inCharset
=U_CHARSET_FAMILY
;
378 inIsBigEndian
=U_IS_BIG_ENDIAN
;
381 inStringTop
=outStringTop
=0;
384 findPrefix
=findSuffix
=NULL
;
385 findPrefixLength
=findSuffixLength
=0;
388 // create a header for an empty package
390 pHeader
=(DataHeader
*)header
;
391 pHeader
->dataHeader
.magic1
=0xda;
392 pHeader
->dataHeader
.magic2
=0x27;
393 memcpy(&pHeader
->info
, &dataInfo
, sizeof(dataInfo
));
394 headerLength
=(int32_t)(4+sizeof(dataInfo
));
395 if(headerLength
&0xf) {
396 /* NUL-pad the header to a multiple of 16 */
397 int32_t length
=(headerLength
+0xf)&~0xf;
398 memset(header
+headerLength
, 0, length
-headerLength
);
401 pHeader
->dataHeader
.headerSize
=(uint16_t)headerLength
;
404 Package::~Package() {
409 for(index
=0; index
<itemCount
; ++index
) {
410 if(items
[index
].isDataOwned
) {
411 free(items
[index
].data
);
417 Package::readPackage(const char *filename
) {
419 const UDataInfo
*pInfo
;
420 UErrorCode errorCode
;
422 const uint8_t *inBytes
;
424 int32_t length
, offset
, i
;
425 int32_t itemLength
, typeEnum
;
428 const UDataOffsetTOCEntry
*inEntries
;
430 extractPackageName(filename
, inPkgName
, (int32_t)sizeof(inPkgName
));
433 inData
=readFile(NULL
, filename
, inLength
, type
);
437 * swap the header - even if the swapping itself is a no-op
438 * because it tells us the header length
440 errorCode
=U_ZERO_ERROR
;
441 makeTypeProps(type
, inCharset
, inIsBigEndian
);
442 ds
=udata_openSwapper(inIsBigEndian
, inCharset
, U_IS_BIG_ENDIAN
, U_CHARSET_FAMILY
, &errorCode
);
443 if(U_FAILURE(errorCode
)) {
444 fprintf(stderr
, "icupkg: udata_openSwapper(\"%s\") failed - %s\n",
445 filename
, u_errorName(errorCode
));
449 ds
->printError
=printPackageError
;
450 ds
->printErrorContext
=stderr
;
452 headerLength
=sizeof(header
);
453 if(length
<headerLength
) {
456 headerLength
=udata_swapDataHeader(ds
, inData
, headerLength
, header
, &errorCode
);
457 if(U_FAILURE(errorCode
)) {
461 /* check data format and format version */
462 pInfo
=(const UDataInfo
*)((const char *)inData
+4);
464 pInfo
->dataFormat
[0]==0x43 && /* dataFormat="CmnD" */
465 pInfo
->dataFormat
[1]==0x6d &&
466 pInfo
->dataFormat
[2]==0x6e &&
467 pInfo
->dataFormat
[3]==0x44 &&
468 pInfo
->formatVersion
[0]==1
470 fprintf(stderr
, "icupkg: data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as an ICU .dat package\n",
471 pInfo
->dataFormat
[0], pInfo
->dataFormat
[1],
472 pInfo
->dataFormat
[2], pInfo
->dataFormat
[3],
473 pInfo
->formatVersion
[0]);
474 exit(U_UNSUPPORTED_ERROR
);
476 inIsBigEndian
=(UBool
)pInfo
->isBigEndian
;
477 inCharset
=pInfo
->charsetFamily
;
479 inBytes
=(const uint8_t *)inData
+headerLength
;
480 inEntries
=(const UDataOffsetTOCEntry
*)(inBytes
+4);
482 /* check that the itemCount fits, then the ToC table, then at least the header of the last item */
483 length
-=headerLength
;
485 /* itemCount does not fit */
488 itemCount
=udata_readInt32(ds
, *(const int32_t *)inBytes
);
491 } else if(length
<(4+8*itemCount
)) {
492 /* ToC table does not fit */
495 /* offset of the last item plus at least 20 bytes for its header */
496 offset
=20+(int32_t)ds
->readUInt32(inEntries
[itemCount
-1].dataOffset
);
500 fprintf(stderr
, "icupkg: too few bytes (%ld after header) for a .dat package\n",
502 exit(U_INDEX_OUTOFBOUNDS_ERROR
);
504 /* do not modify the package length variable until the last item's length is set */
507 char prefix
[MAX_PKG_NAME_LENGTH
+4];
508 char *s
, *inItemStrings
;
509 int32_t inPkgNameLength
, prefixLength
, stringsOffset
;
511 if(itemCount
>MAX_FILE_COUNT
) {
512 fprintf(stderr
, "icupkg: too many items, maximum is %d\n", MAX_FILE_COUNT
);
513 exit(U_BUFFER_OVERFLOW_ERROR
);
516 /* swap the item name strings */
517 stringsOffset
=4+8*itemCount
;
518 itemLength
=(int32_t)(ds
->readUInt32(inEntries
[0].dataOffset
))-stringsOffset
;
520 // don't include padding bytes at the end of the item names
521 while(itemLength
>0 && inBytes
[stringsOffset
+itemLength
-1]!=0) {
525 if((inStringTop
+itemLength
)>STRING_STORE_SIZE
) {
526 fprintf(stderr
, "icupkg: total length of item name strings too long\n");
527 exit(U_BUFFER_OVERFLOW_ERROR
);
530 inItemStrings
=inStrings
+inStringTop
;
531 ds
->swapInvChars(ds
, inBytes
+stringsOffset
, itemLength
, inItemStrings
, &errorCode
);
532 if(U_FAILURE(errorCode
)) {
533 fprintf(stderr
, "icupkg failed to swap the input .dat package item name strings\n");
534 exit(U_INVALID_FORMAT_ERROR
);
536 inStringTop
+=itemLength
;
538 // reset the Item entries
539 memset(items
, 0, itemCount
*sizeof(Item
));
541 inPkgNameLength
=strlen(inPkgName
);
542 memcpy(prefix
, inPkgName
, inPkgNameLength
);
543 prefixLength
=inPkgNameLength
;
546 * Get the common prefix of the items.
547 * New-style ICU .dat packages use tree separators ('/') between package names,
548 * tree names, and item names,
549 * while old-style ICU .dat packages (before multi-tree support)
550 * use an underscore ('_') between package and item names.
552 offset
=(int32_t)ds
->readUInt32(inEntries
[0].nameOffset
)-stringsOffset
;
553 s
=inItemStrings
+offset
;
554 if( (int32_t)strlen(s
)>=(inPkgNameLength
+2) &&
555 0==memcmp(s
, inPkgName
, inPkgNameLength
) &&
556 s
[inPkgNameLength
]=='_'
558 // old-style .dat package
559 prefix
[prefixLength
++]='_';
561 // new-style .dat package
562 prefix
[prefixLength
++]=U_TREE_ENTRY_SEP_CHAR
;
563 // if it turns out to not contain U_TREE_ENTRY_SEP_CHAR
564 // then the test in the loop below will fail
566 prefix
[prefixLength
]=0;
568 /* read the ToC table */
569 for(i
=0; i
<itemCount
; ++i
) {
570 // skip the package part of the item name, error if it does not match the actual package name
571 // or if nothing follows the package name
572 offset
=(int32_t)ds
->readUInt32(inEntries
[i
].nameOffset
)-stringsOffset
;
573 s
=inItemStrings
+offset
;
574 if(0!=strncmp(s
, prefix
, prefixLength
) || s
[prefixLength
]==0) {
575 fprintf(stderr
, "icupkg: input .dat item name \"%s\" does not start with \"%s\"\n",
577 exit(U_UNSUPPORTED_ERROR
);
579 items
[i
].name
=s
+prefixLength
;
581 // set the item's data
582 items
[i
].data
=(uint8_t *)inBytes
+ds
->readUInt32(inEntries
[i
].dataOffset
);
584 items
[i
-1].length
=(int32_t)(items
[i
].data
-items
[i
-1].data
);
586 // set the previous item's platform type
587 typeEnum
=getTypeEnumForInputData(items
[i
-1].data
, items
[i
-1].length
, &errorCode
);
588 if(typeEnum
<0 || U_FAILURE(errorCode
)) {
589 fprintf(stderr
, "icupkg: not an ICU data file: item \"%s\" in \"%s\"\n", items
[i
-1].name
, filename
);
590 exit(U_INVALID_FORMAT_ERROR
);
592 items
[i
-1].type
=makeTypeLetter(typeEnum
);
594 items
[i
].isDataOwned
=FALSE
;
596 // set the last item's length
597 items
[itemCount
-1].length
=length
-ds
->readUInt32(inEntries
[itemCount
-1].dataOffset
);
599 // set the last item's platform type
600 typeEnum
=getTypeEnumForInputData(items
[itemCount
-1].data
, items
[itemCount
-1].length
, &errorCode
);
601 if(typeEnum
<0 || U_FAILURE(errorCode
)) {
602 fprintf(stderr
, "icupkg: not an ICU data file: item \"%s\" in \"%s\"\n", items
[i
-1].name
, filename
);
603 exit(U_INVALID_FORMAT_ERROR
);
605 items
[itemCount
-1].type
=makeTypeLetter(typeEnum
);
607 if(type
!=U_ICUDATA_TYPE_LETTER
[0]) {
608 // sort the item names for the local charset
613 udata_closeSwapper(ds
);
617 Package::getInType() {
618 return makeTypeLetter(inCharset
, inIsBigEndian
);
622 Package::writePackage(const char *filename
, char outType
, const char *comment
) {
623 char prefix
[MAX_PKG_NAME_LENGTH
+4];
624 UDataOffsetTOCEntry entry
;
625 UDataSwapper
*dsLocalToOut
, *ds
[TYPE_COUNT
];
629 UErrorCode errorCode
;
630 int32_t i
, length
, prefixLength
, maxItemLength
, basenameOffset
, offset
, outInt32
;
632 UBool outIsBigEndian
;
634 extractPackageName(filename
, prefix
, MAX_PKG_NAME_LENGTH
);
636 // if there is an explicit comment, then use it, else use what's in the current header
638 /* get the header size minus the current comment */
642 pHeader
=(DataHeader
*)header
;
643 headerLength
=4+pHeader
->info
.size
;
644 length
=(int32_t)strlen(comment
);
645 if((int32_t)(headerLength
+length
)>=(int32_t)sizeof(header
)) {
646 fprintf(stderr
, "icupkg: comment too long\n");
647 exit(U_BUFFER_OVERFLOW_ERROR
);
649 memcpy(header
+headerLength
, comment
, length
+1);
650 headerLength
+=length
;
651 if(headerLength
&0xf) {
652 /* NUL-pad the header to a multiple of 16 */
653 length
=(headerLength
+0xf)&~0xf;
654 memset(header
+headerLength
, 0, length
-headerLength
);
657 pHeader
->dataHeader
.headerSize
=(uint16_t)headerLength
;
660 makeTypeProps(outType
, outCharset
, outIsBigEndian
);
662 // open (TYPE_COUNT-2) swappers
663 // one is a no-op for local type==outType
664 // one type (TYPE_LE) is bogus
665 errorCode
=U_ZERO_ERROR
;
666 i
=makeTypeEnum(outType
);
667 ds
[TYPE_B
]= i
==TYPE_B
? NULL
: udata_openSwapper(TRUE
, U_ASCII_FAMILY
, outIsBigEndian
, outCharset
, &errorCode
);
668 ds
[TYPE_L
]= i
==TYPE_L
? NULL
: udata_openSwapper(FALSE
, U_ASCII_FAMILY
, outIsBigEndian
, outCharset
, &errorCode
);
670 ds
[TYPE_E
]= i
==TYPE_E
? NULL
: udata_openSwapper(TRUE
, U_EBCDIC_FAMILY
, outIsBigEndian
, outCharset
, &errorCode
);
671 if(U_FAILURE(errorCode
)) {
672 fprintf(stderr
, "icupkg: udata_openSwapper() failed - %s\n", u_errorName(errorCode
));
675 for(i
=0; i
<TYPE_COUNT
; ++i
) {
677 ds
[i
]->printError
=printPackageError
;
678 ds
[i
]->printErrorContext
=stderr
;
682 dsLocalToOut
=ds
[makeTypeEnum(U_CHARSET_FAMILY
, U_IS_BIG_ENDIAN
)];
684 // create the file and write its contents
685 file
=fopen(filename
, "wb");
687 fprintf(stderr
, "icupkg: unable to create file \"%s\"\n", filename
);
688 exit(U_FILE_ACCESS_ERROR
);
691 // swap and write the header
692 if(dsLocalToOut
!=NULL
) {
693 udata_swapDataHeader(dsLocalToOut
, header
, headerLength
, header
, &errorCode
);
694 if(U_FAILURE(errorCode
)) {
695 fprintf(stderr
, "icupkg: udata_swapDataHeader(local to out) failed - %s\n", u_errorName(errorCode
));
699 length
=(int32_t)fwrite(header
, 1, headerLength
, file
);
700 if(length
!=headerLength
) {
701 fprintf(stderr
, "icupkg: unable to write complete header to file \"%s\"\n", filename
);
702 exit(U_FILE_ACCESS_ERROR
);
705 // prepare and swap the package name with a tree separator
706 // for prepending to item names
707 strcat(prefix
, U_TREE_ENTRY_SEP_STRING
);
708 prefixLength
=(int32_t)strlen(prefix
);
709 if(dsLocalToOut
!=NULL
) {
710 dsLocalToOut
->swapInvChars(dsLocalToOut
, prefix
, prefixLength
, prefix
, &errorCode
);
711 if(U_FAILURE(errorCode
)) {
712 fprintf(stderr
, "icupkg: swapInvChars(output package name) failed - %s\n", u_errorName(errorCode
));
716 // swap and sort the item names (sorting needs to be done in the output charset)
717 dsLocalToOut
->swapInvChars(dsLocalToOut
, inStrings
, inStringTop
, inStrings
, &errorCode
);
718 if(U_FAILURE(errorCode
)) {
719 fprintf(stderr
, "icupkg: swapInvChars(item names) failed - %s\n", u_errorName(errorCode
));
725 // create the output item names in sorted order, with the package name prepended to each
726 for(i
=0; i
<itemCount
; ++i
) {
727 length
=(int32_t)strlen(items
[i
].name
);
728 name
=allocString(FALSE
, length
+prefixLength
);
729 memcpy(name
, prefix
, prefixLength
);
730 memcpy(name
+prefixLength
, items
[i
].name
, length
+1);
734 // calculate offsets for item names and items, pad to 16-align items
735 // align only the first item; each item's length is a multiple of 16
736 basenameOffset
=4+8*itemCount
;
737 offset
=basenameOffset
+outStringTop
;
738 if((length
=(offset
&15))!=0) {
740 memset(allocString(FALSE
, length
-1), 0xaa, length
);
744 // write the table of contents
745 // first the itemCount
747 if(dsLocalToOut
!=NULL
) {
748 dsLocalToOut
->swapArray32(dsLocalToOut
, &outInt32
, 4, &outInt32
, &errorCode
);
749 if(U_FAILURE(errorCode
)) {
750 fprintf(stderr
, "icupkg: swapArray32(item count) failed - %s\n", u_errorName(errorCode
));
754 length
=(int32_t)fwrite(&outInt32
, 1, 4, file
);
756 fprintf(stderr
, "icupkg: unable to write complete item count to file \"%s\"\n", filename
);
757 exit(U_FILE_ACCESS_ERROR
);
760 // then write the item entries (and collect the maxItemLength)
762 for(i
=0; i
<itemCount
; ++i
) {
763 entry
.nameOffset
=(uint32_t)(basenameOffset
+(items
[i
].name
-outStrings
));
764 entry
.dataOffset
=(uint32_t)offset
;
765 if(dsLocalToOut
!=NULL
) {
766 dsLocalToOut
->swapArray32(dsLocalToOut
, &entry
, 8, &entry
, &errorCode
);
767 if(U_FAILURE(errorCode
)) {
768 fprintf(stderr
, "icupkg: swapArray32(item entry %ld) failed - %s\n", (long)i
, u_errorName(errorCode
));
772 length
=(int32_t)fwrite(&entry
, 1, 8, file
);
774 fprintf(stderr
, "icupkg: unable to write complete item entry %ld to file \"%s\"\n", (long)i
, filename
);
775 exit(U_FILE_ACCESS_ERROR
);
778 length
=items
[i
].length
;
779 if(length
>maxItemLength
) {
780 maxItemLength
=length
;
785 // write the item names
786 length
=(int32_t)fwrite(outStrings
, 1, outStringTop
, file
);
787 if(length
!=outStringTop
) {
788 fprintf(stderr
, "icupkg: unable to write complete item names to file \"%s\"\n", filename
);
789 exit(U_FILE_ACCESS_ERROR
);
793 for(pItem
=items
, i
=0; i
<itemCount
; ++pItem
, ++i
) {
794 int32_t type
=makeTypeEnum(pItem
->type
);
796 // swap each item from its platform properties to the desired ones
799 pItem
->data
, pItem
->length
, pItem
->data
,
801 if(U_FAILURE(errorCode
)) {
802 fprintf(stderr
, "icupkg: udata_swap(item %ld) failed - %s\n", (long)i
, u_errorName(errorCode
));
806 length
=(int32_t)fwrite(pItem
->data
, 1, pItem
->length
, file
);
807 if(length
!=pItem
->length
) {
808 fprintf(stderr
, "icupkg: unable to write complete item %ld to file \"%s\"\n", (long)i
, filename
);
809 exit(U_FILE_ACCESS_ERROR
);
814 fprintf(stderr
, "icupkg: unable to write complete file \"%s\"\n", filename
);
815 exit(U_FILE_ACCESS_ERROR
);
819 for(i
=0; i
<TYPE_COUNT
; ++i
) {
820 udata_closeSwapper(ds
[i
]);
825 Package::findItem(const char *name
, int32_t length
) {
826 int32_t i
, start
, limit
;
829 /* do a binary search for the string */
835 result
=strncmp(name
, items
[i
].name
, length
);
837 result
=strcmp(name
, items
[i
].name
);
844 * if we compared just prefixes, then we may need to back up
845 * to the first item with this prefix
847 while(i
>0 && 0==strncmp(name
, items
[i
-1].name
, length
)) {
852 } else if(result
<0) {
854 } else /* result>0 */ {
859 return ~start
; /* not found, return binary-not of the insertion point */
863 Package::findItems(const char *pattern
) {
866 if(pattern
==NULL
|| *pattern
==0) {
875 wild
=strchr(pattern
, '*');
878 findPrefixLength
=(int32_t)strlen(pattern
);
881 findPrefixLength
=(int32_t)(wild
-pattern
);
883 findSuffixLength
=(int32_t)strlen(findSuffix
);
884 if(NULL
!=strchr(findSuffix
, '*')) {
885 // two or more wildcards
886 fprintf(stderr
, "icupkg: syntax error (more than one '*') in item pattern \"%s\"\n", pattern
);
891 if(findPrefixLength
==0) {
894 findNextIndex
=findItem(findPrefix
, findPrefixLength
);
899 Package::findNextItem() {
900 const char *name
, *middle
, *treeSep
;
901 int32_t index
, nameLength
, middleLength
;
903 if(findNextIndex
<0) {
907 while(findNextIndex
<itemCount
) {
908 index
=findNextIndex
++;
909 name
=items
[index
].name
;
910 nameLength
=(int32_t)strlen(name
);
911 if(nameLength
<(findPrefixLength
+findSuffixLength
)) {
912 // item name too short for prefix & suffix
915 if(findPrefixLength
>0 && 0!=memcmp(findPrefix
, name
, findPrefixLength
)) {
916 // left the range of names with this prefix
919 middle
=name
+findPrefixLength
;
920 middleLength
=nameLength
-findPrefixLength
-findSuffixLength
;
921 if(findSuffixLength
>0 && 0!=memcmp(findSuffix
, name
+(nameLength
-findSuffixLength
), findSuffixLength
)) {
922 // suffix does not match
925 // prefix & suffix match
927 if(matchMode
&MATCH_NOSLASH
) {
928 treeSep
=strchr(middle
, U_TREE_ENTRY_SEP_CHAR
);
929 if(treeSep
!=NULL
&& (treeSep
-middle
)<middleLength
) {
930 // the middle (matching the * wildcard) contains a tree separator /
935 // found a matching item
945 Package::setMatchMode(uint32_t mode
) {
950 Package::addItem(const char *name
) {
951 addItem(name
, NULL
, 0, FALSE
, U_ICUDATA_TYPE_LETTER
[0]);
955 Package::addItem(const char *name
, uint8_t *data
, int32_t length
, UBool isDataOwned
, char type
) {
958 index
=findItem(name
);
960 // new item, make space at the insertion point
961 if(itemCount
>=MAX_FILE_COUNT
) {
962 fprintf(stderr
, "icupkg: too many items, maximum is %d\n", MAX_FILE_COUNT
);
963 exit(U_BUFFER_OVERFLOW_ERROR
);
965 // move the following items down
967 if(index
<itemCount
) {
968 memmove(items
+index
+1, items
+index
, (itemCount
-index
)*sizeof(Item
));
972 // reset this Item entry
973 memset(items
+index
, 0, sizeof(Item
));
975 // copy the item's name
976 items
[index
].name
=allocString(TRUE
, strlen(name
));
977 strcpy(items
[index
].name
, name
);
978 pathToTree(items
[index
].name
);
980 // same-name item found, replace it
981 if(items
[index
].isDataOwned
) {
982 free(items
[index
].data
);
985 // keep the item's name since it is the same
988 // set the item's data
989 items
[index
].data
=data
;
990 items
[index
].length
=length
;
991 items
[index
].isDataOwned
=isDataOwned
;
992 items
[index
].type
=type
;
996 Package::addFile(const char *filesPath
, const char *name
) {
1001 data
=readFile(filesPath
, name
, length
, type
);
1002 // readFile() exits the tool if it fails
1003 addItem(name
, data
, length
, TRUE
, type
);
1007 Package::addItems(const Package
&listPkg
) {
1011 for(pItem
=listPkg
.items
, i
=0; i
<listPkg
.itemCount
; ++pItem
, ++i
) {
1012 addItem(pItem
->name
, pItem
->data
, pItem
->length
, FALSE
, pItem
->type
);
1017 Package::removeItem(int32_t index
) {
1020 if(items
[index
].isDataOwned
) {
1021 free(items
[index
].data
);
1024 // move the following items up
1025 if((index
+1)<itemCount
) {
1026 memmove(items
+index
, items
+index
+1, (itemCount
-(index
+1))*sizeof(Item
));
1030 if(index
<=findNextIndex
) {
1037 Package::removeItems(const char *pattern
) {
1041 while((index
=findNextItem())>=0) {
1047 Package::removeItems(const Package
&listPkg
) {
1051 for(pItem
=listPkg
.items
, i
=0; i
<listPkg
.itemCount
; ++pItem
, ++i
) {
1052 removeItems(pItem
->name
);
1057 Package::extractItem(const char *filesPath
, const char *outName
, int32_t index
, char outType
) {
1058 char filename
[1024];
1063 uint8_t itemCharset
, outCharset
;
1064 UBool itemIsBigEndian
, outIsBigEndian
;
1066 if(index
<0 || itemCount
<=index
) {
1071 // swap the data to the outType
1072 // outType==0: don't swap
1073 if(outType
!=0 && pItem
->type
!=outType
) {
1075 UErrorCode errorCode
=U_ZERO_ERROR
;
1076 makeTypeProps(pItem
->type
, itemCharset
, itemIsBigEndian
);
1077 makeTypeProps(outType
, outCharset
, outIsBigEndian
);
1078 ds
=udata_openSwapper(itemIsBigEndian
, itemCharset
, outIsBigEndian
, outCharset
, &errorCode
);
1079 if(U_FAILURE(errorCode
)) {
1080 fprintf(stderr
, "icupkg: udata_openSwapper(item %ld) failed - %s\n",
1081 (long)index
, u_errorName(errorCode
));
1085 ds
->printError
=printPackageError
;
1086 ds
->printErrorContext
=stderr
;
1088 // swap the item from its platform properties to the desired ones
1089 udata_swap(ds
, pItem
->data
, pItem
->length
, pItem
->data
, &errorCode
);
1090 if(U_FAILURE(errorCode
)) {
1091 fprintf(stderr
, "icupkg: udata_swap(item %ld) failed - %s\n", (long)index
, u_errorName(errorCode
));
1094 udata_closeSwapper(ds
);
1097 // create the file and write its contents
1098 makeFullFilenameAndDirs(filesPath
, outName
, filename
, (int32_t)sizeof(filename
));
1099 file
=fopen(filename
, "wb");
1101 fprintf(stderr
, "icupkg: unable to create file \"%s\"\n", filename
);
1102 exit(U_FILE_ACCESS_ERROR
);
1104 fileLength
=(int32_t)fwrite(pItem
->data
, 1, pItem
->length
, file
);
1106 if(ferror(file
) || fileLength
!=pItem
->length
) {
1107 fprintf(stderr
, "icupkg: unable to write complete file \"%s\"\n", filename
);
1108 exit(U_FILE_ACCESS_ERROR
);
1114 Package::extractItem(const char *filesPath
, int32_t index
, char outType
) {
1115 extractItem(filesPath
, items
[index
].name
, index
, outType
);
1119 Package::extractItems(const char *filesPath
, const char *pattern
, char outType
) {
1123 while((index
=findNextItem())>=0) {
1124 extractItem(filesPath
, index
, outType
);
1129 Package::extractItems(const char *filesPath
, const Package
&listPkg
, char outType
) {
1133 for(pItem
=listPkg
.items
, i
=0; i
<listPkg
.itemCount
; ++pItem
, ++i
) {
1134 extractItems(filesPath
, pItem
->name
, outType
);
1139 Package::listItems(FILE *file
) {
1142 for(i
=0; i
<itemCount
; ++i
) {
1143 fprintf(file
, "%s\n", items
[i
].name
);
1148 Package::checkDependency(void *context
, const char *itemName
, const char *targetName
) {
1149 // check dependency: make sure the target item is in the package
1150 Package
*me
=(Package
*)context
;
1151 if(me
->findItem(targetName
)<0) {
1152 me
->isMissingItems
=TRUE
;
1153 fprintf(stderr
, "Item %s depends on missing item %s\n", itemName
, targetName
);
1158 Package::checkDependencies() {
1161 isMissingItems
=FALSE
;
1162 for(i
=0; i
<itemCount
; ++i
) {
1163 enumDependencies(items
+i
);
1165 return (UBool
)!isMissingItems
;
1169 Package::allocString(UBool in
, int32_t length
) {
1182 if(top
>STRING_STORE_SIZE
) {
1183 fprintf(stderr
, "icupkg: string storage overflow\n");
1184 exit(U_BUFFER_OVERFLOW_ERROR
);
1195 Package::sortItems() {
1196 UErrorCode errorCode
=U_ZERO_ERROR
;
1197 uprv_sortArray(items
, itemCount
, (int32_t)sizeof(Item
), compareItems
, NULL
, FALSE
, &errorCode
);
1198 if(U_FAILURE(errorCode
)) {
1199 fprintf(stderr
, "icupkg: sorting item names failed - %s\n", u_errorName(errorCode
));