1 /*
2 *******************************************************************************
3 *
4 * Copyright (C) 1999-2014, International Business Machines
5 * Corporation and others. All Rights Reserved.
6 *
7 *******************************************************************************
8 * file name: package.cpp
9 * encoding: US-ASCII
10 * tab size: 8 (not used)
11 * indentation:4
12 *
13 * created on: 2005aug25
14 * created by: Markus W. Scherer
15 *
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
19 * other tools.
20 * Subsumes functionality and implementation code from
21 * gencmn, decmn, and icuswap tools.
22 */
23
24 #include "unicode/utypes.h"
25 #include "unicode/putil.h"
26 #include "unicode/udata.h"
27 #include "cstring.h"
28 #include "uarrsort.h"
29 #include "ucmndata.h"
30 #include "udataswp.h"
31 #include "swapimpl.h"
32 #include "toolutil.h"
33 #include "package.h"
34 #include "cmemory.h"
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40
41 static const int32_t kItemsChunk = 256; /* How much to increase the filesarray by each time */
42
43 // general definitions ----------------------------------------------------- ***
44
45 /* UDataInfo cf. udata.h */
46 static const UDataInfo dataInfo={
47 (uint16_t)sizeof(UDataInfo),
48 0,
49
50 U_IS_BIG_ENDIAN,
51 U_CHARSET_FAMILY,
52 (uint8_t)sizeof(UChar),
53 0,
54
55 {0x43, 0x6d, 0x6e, 0x44}, /* dataFormat="CmnD" */
56 {1, 0, 0, 0}, /* formatVersion */
57 {3, 0, 0, 0} /* dataVersion */
58 };
59
60 U_CDECL_BEGIN
61 static void U_CALLCONV
printPackageError(void * context,const char * fmt,va_list args)62 printPackageError(void *context, const char *fmt, va_list args) {
63 vfprintf((FILE *)context, fmt, args);
64 }
65 U_CDECL_END
66
67 static uint16_t
readSwapUInt16(uint16_t x)68 readSwapUInt16(uint16_t x) {
69 return (uint16_t)((x<<8)|(x>>8));
70 }
71
72 // platform types ---------------------------------------------------------- ***
73
74 static const char *types="lb?e";
75
76 enum { TYPE_L, TYPE_B, TYPE_LE, TYPE_E, TYPE_COUNT };
77
78 static inline int32_t
makeTypeEnum(uint8_t charset,UBool isBigEndian)79 makeTypeEnum(uint8_t charset, UBool isBigEndian) {
80 return 2*(int32_t)charset+isBigEndian;
81 }
82
83 static inline int32_t
makeTypeEnum(char type)84 makeTypeEnum(char type) {
85 return
86 type == 'l' ? TYPE_L :
87 type == 'b' ? TYPE_B :
88 type == 'e' ? TYPE_E :
89 -1;
90 }
91
92 static inline char
makeTypeLetter(uint8_t charset,UBool isBigEndian)93 makeTypeLetter(uint8_t charset, UBool isBigEndian) {
94 return types[makeTypeEnum(charset, isBigEndian)];
95 }
96
97 static inline char
makeTypeLetter(int32_t typeEnum)98 makeTypeLetter(int32_t typeEnum) {
99 return types[typeEnum];
100 }
101
102 static void
makeTypeProps(char type,uint8_t & charset,UBool & isBigEndian)103 makeTypeProps(char type, uint8_t &charset, UBool &isBigEndian) {
104 int32_t typeEnum=makeTypeEnum(type);
105 charset=(uint8_t)(typeEnum>>1);
106 isBigEndian=(UBool)(typeEnum&1);
107 }
108
109 U_CFUNC const UDataInfo *
getDataInfo(const uint8_t * data,int32_t length,int32_t & infoLength,int32_t & headerLength,UErrorCode * pErrorCode)110 getDataInfo(const uint8_t *data, int32_t length,
111 int32_t &infoLength, int32_t &headerLength,
112 UErrorCode *pErrorCode) {
113 const DataHeader *pHeader;
114 const UDataInfo *pInfo;
115
116 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
117 return NULL;
118 }
119 if( data==NULL ||
120 (length>=0 && length<(int32_t)sizeof(DataHeader))
121 ) {
122 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
123 return NULL;
124 }
125
126 pHeader=(const DataHeader *)data;
127 pInfo=&pHeader->info;
128 if( (length>=0 && length<(int32_t)sizeof(DataHeader)) ||
129 pHeader->dataHeader.magic1!=0xda ||
130 pHeader->dataHeader.magic2!=0x27 ||
131 pInfo->sizeofUChar!=2
132 ) {
133 *pErrorCode=U_UNSUPPORTED_ERROR;
134 return NULL;
135 }
136
137 if(pInfo->isBigEndian==U_IS_BIG_ENDIAN) {
138 headerLength=pHeader->dataHeader.headerSize;
139 infoLength=pInfo->size;
140 } else {
141 headerLength=readSwapUInt16(pHeader->dataHeader.headerSize);
142 infoLength=readSwapUInt16(pInfo->size);
143 }
144
145 if( headerLength<(int32_t)sizeof(DataHeader) ||
146 infoLength<(int32_t)sizeof(UDataInfo) ||
147 headerLength<(int32_t)(sizeof(pHeader->dataHeader)+infoLength) ||
148 (length>=0 && length<headerLength)
149 ) {
150 *pErrorCode=U_UNSUPPORTED_ERROR;
151 return NULL;
152 }
153
154 return pInfo;
155 }
156
157 static int32_t
getTypeEnumForInputData(const uint8_t * data,int32_t length,UErrorCode * pErrorCode)158 getTypeEnumForInputData(const uint8_t *data, int32_t length,
159 UErrorCode *pErrorCode) {
160 const UDataInfo *pInfo;
161 int32_t infoLength, headerLength;
162
163 /* getDataInfo() checks for illegal arguments */
164 pInfo=getDataInfo(data, length, infoLength, headerLength, pErrorCode);
165 if(pInfo==NULL) {
166 return -1;
167 }
168
169 return makeTypeEnum(pInfo->charsetFamily, (UBool)pInfo->isBigEndian);
170 }
171
172 // file handling ----------------------------------------------------------- ***
173
174 static void
extractPackageName(const char * filename,char pkg[],int32_t capacity)175 extractPackageName(const char *filename,
176 char pkg[], int32_t capacity) {
177 const char *basename;
178 int32_t len;
179
180 basename=findBasename(filename);
181 len=(int32_t)strlen(basename)-4; /* -4: subtract the length of ".dat" */
182
183 if(len<=0 || 0!=strcmp(basename+len, ".dat")) {
184 fprintf(stderr, "icupkg: \"%s\" is not recognized as a package filename (must end with .dat)\n",
185 basename);
186 exit(U_ILLEGAL_ARGUMENT_ERROR);
187 }
188
189 if(len>=capacity) {
190 fprintf(stderr, "icupkg: the package name \"%s\" is too long (>=%ld)\n",
191 basename, (long)capacity);
192 exit(U_ILLEGAL_ARGUMENT_ERROR);
193 }
194
195 memcpy(pkg, basename, len);
196 pkg[len]=0;
197 }
198
199 static int32_t
getFileLength(FILE * f)200 getFileLength(FILE *f) {
201 int32_t length;
202
203 fseek(f, 0, SEEK_END);
204 length=(int32_t)ftell(f);
205 fseek(f, 0, SEEK_SET);
206 return length;
207 }
208
209 /*
210 * Turn tree separators and alternate file separators into normal file separators.
211 */
212 #if U_TREE_ENTRY_SEP_CHAR==U_FILE_SEP_CHAR && U_FILE_ALT_SEP_CHAR==U_FILE_SEP_CHAR
213 #define treeToPath(s)
214 #else
215 static void
treeToPath(char * s)216 treeToPath(char *s) {
217 char *t;
218
219 for(t=s; *t!=0; ++t) {
220 if(*t==U_TREE_ENTRY_SEP_CHAR || *t==U_FILE_ALT_SEP_CHAR) {
221 *t=U_FILE_SEP_CHAR;
222 }
223 }
224 }
225 #endif
226
227 /*
228 * Turn file separators into tree separators.
229 */
230 #if U_TREE_ENTRY_SEP_CHAR==U_FILE_SEP_CHAR && U_FILE_ALT_SEP_CHAR==U_FILE_SEP_CHAR
231 #define pathToTree(s)
232 #else
233 static void
pathToTree(char * s)234 pathToTree(char *s) {
235 char *t;
236
237 for(t=s; *t!=0; ++t) {
238 if(*t==U_FILE_SEP_CHAR || *t==U_FILE_ALT_SEP_CHAR) {
239 *t=U_TREE_ENTRY_SEP_CHAR;
240 }
241 }
242 }
243 #endif
244
245 /*
246 * Prepend the path (if any) to the name and run the name through treeToName().
247 */
248 static void
makeFullFilename(const char * path,const char * name,char * filename,int32_t capacity)249 makeFullFilename(const char *path, const char *name,
250 char *filename, int32_t capacity) {
251 char *s;
252
253 // prepend the path unless NULL or empty
254 if(path!=NULL && path[0]!=0) {
255 if((int32_t)(strlen(path)+1)>=capacity) {
256 fprintf(stderr, "pathname too long: \"%s\"\n", path);
257 exit(U_BUFFER_OVERFLOW_ERROR);
258 }
259 strcpy(filename, path);
260
261 // make sure the path ends with a file separator
262 s=strchr(filename, 0);
263 if(*(s-1)!=U_FILE_SEP_CHAR && *(s-1)!=U_FILE_ALT_SEP_CHAR) {
264 *s++=U_FILE_SEP_CHAR;
265 }
266 } else {
267 s=filename;
268 }
269
270 // turn the name into a filename, turn tree separators into file separators
271 if((int32_t)((s-filename)+strlen(name))>=capacity) {
272 fprintf(stderr, "path/filename too long: \"%s%s\"\n", filename, name);
273 exit(U_BUFFER_OVERFLOW_ERROR);
274 }
275 strcpy(s, name);
276 treeToPath(s);
277 }
278
279 static void
makeFullFilenameAndDirs(const char * path,const char * name,char * filename,int32_t capacity)280 makeFullFilenameAndDirs(const char *path, const char *name,
281 char *filename, int32_t capacity) {
282 char *sep;
283 UErrorCode errorCode;
284
285 makeFullFilename(path, name, filename, capacity);
286
287 // make tree directories
288 errorCode=U_ZERO_ERROR;
289 sep=strchr(filename, 0)-strlen(name);
290 while((sep=strchr(sep, U_FILE_SEP_CHAR))!=NULL) {
291 if(sep!=filename) {
292 *sep=0; // truncate temporarily
293 uprv_mkdir(filename, &errorCode);
294 if(U_FAILURE(errorCode)) {
295 fprintf(stderr, "icupkg: unable to create tree directory \"%s\"\n", filename);
296 exit(U_FILE_ACCESS_ERROR);
297 }
298 }
299 *sep++=U_FILE_SEP_CHAR; // restore file separator character
300 }
301 }
302
303 static uint8_t *
readFile(const char * path,const char * name,int32_t & length,char & type)304 readFile(const char *path, const char *name, int32_t &length, char &type) {
305 char filename[1024];
306 FILE *file;
307 uint8_t *data;
308 UErrorCode errorCode;
309 int32_t fileLength, typeEnum;
310
311 makeFullFilename(path, name, filename, (int32_t)sizeof(filename));
312
313 /* open the input file, get its length, allocate memory for it, read the file */
314 file=fopen(filename, "rb");
315 if(file==NULL) {
316 fprintf(stderr, "icupkg: unable to open input file \"%s\"\n", filename);
317 exit(U_FILE_ACCESS_ERROR);
318 }
319
320 /* get the file length */
321 fileLength=getFileLength(file);
322 if(ferror(file) || fileLength<=0) {
323 fprintf(stderr, "icupkg: empty input file \"%s\"\n", filename);
324 fclose(file);
325 exit(U_FILE_ACCESS_ERROR);
326 }
327
328 /* allocate the buffer, pad to multiple of 16 */
329 length=(fileLength+0xf)&~0xf;
330 data=(uint8_t *)uprv_malloc(length);
331 if(data==NULL) {
332 fclose(file);
333 fprintf(stderr, "icupkg: malloc error allocating %d bytes.\n", (int)length);
334 exit(U_MEMORY_ALLOCATION_ERROR);
335 }
336
337 /* read the file */
338 if(fileLength!=(int32_t)fread(data, 1, fileLength, file)) {
339 fprintf(stderr, "icupkg: error reading \"%s\"\n", filename);
340 fclose(file);
341 free(data);
342 exit(U_FILE_ACCESS_ERROR);
343 }
344
345 /* pad the file to a multiple of 16 using the usual padding byte */
346 if(fileLength<length) {
347 memset(data+fileLength, 0xaa, length-fileLength);
348 }
349
350 fclose(file);
351
352 // minimum check for ICU-format data
353 errorCode=U_ZERO_ERROR;
354 typeEnum=getTypeEnumForInputData(data, length, &errorCode);
355 if(typeEnum<0 || U_FAILURE(errorCode)) {
356 fprintf(stderr, "icupkg: not an ICU data file: \"%s\"\n", filename);
357 free(data);
358 #if !UCONFIG_NO_LEGACY_CONVERSION
359 exit(U_INVALID_FORMAT_ERROR);
360 #else
361 fprintf(stderr, "U_INVALID_FORMAT_ERROR occurred but UCONFIG_NO_LEGACY_CONVERSION is on so this is expected.\n");
362 exit(0);
363 #endif
364 }
365 type=makeTypeLetter(typeEnum);
366
367 return data;
368 }
369
370 // .dat package file representation ---------------------------------------- ***
371
372 U_CDECL_BEGIN
373
374 static int32_t U_CALLCONV
compareItems(const void *,const void * left,const void * right)375 compareItems(const void * /*context*/, const void *left, const void *right) {
376 U_NAMESPACE_USE
377
378 return (int32_t)strcmp(((Item *)left)->name, ((Item *)right)->name);
379 }
380
381 U_CDECL_END
382
383 U_NAMESPACE_BEGIN
384
Package()385 Package::Package()
386 : doAutoPrefix(FALSE), prefixEndsWithType(FALSE) {
387 inPkgName[0]=0;
388 pkgPrefix[0]=0;
389 inData=NULL;
390 inLength=0;
391 inCharset=U_CHARSET_FAMILY;
392 inIsBigEndian=U_IS_BIG_ENDIAN;
393
394 itemCount=0;
395 itemMax=0;
396 items=NULL;
397
398 inStringTop=outStringTop=0;
399
400 matchMode=0;
401 findPrefix=findSuffix=NULL;
402 findPrefixLength=findSuffixLength=0;
403 findNextIndex=-1;
404
405 // create a header for an empty package
406 DataHeader *pHeader;
407 pHeader=(DataHeader *)header;
408 pHeader->dataHeader.magic1=0xda;
409 pHeader->dataHeader.magic2=0x27;
410 memcpy(&pHeader->info, &dataInfo, sizeof(dataInfo));
411 headerLength=(int32_t)(4+sizeof(dataInfo));
412 if(headerLength&0xf) {
413 /* NUL-pad the header to a multiple of 16 */
414 int32_t length=(headerLength+0xf)&~0xf;
415 memset(header+headerLength, 0, length-headerLength);
416 headerLength=length;
417 }
418 pHeader->dataHeader.headerSize=(uint16_t)headerLength;
419 }
420
~Package()421 Package::~Package() {
422 int32_t idx;
423
424 free(inData);
425
426 for(idx=0; idx<itemCount; ++idx) {
427 if(items[idx].isDataOwned) {
428 free(items[idx].data);
429 }
430 }
431
432 uprv_free((void*)items);
433 }
434
435 void
setPrefix(const char * p)436 Package::setPrefix(const char *p) {
437 if(strlen(p)>=sizeof(pkgPrefix)) {
438 fprintf(stderr, "icupkg: --toc_prefix %s too long\n", p);
439 exit(U_ILLEGAL_ARGUMENT_ERROR);
440 }
441 strcpy(pkgPrefix, p);
442 }
443
444 void
readPackage(const char * filename)445 Package::readPackage(const char *filename) {
446 UDataSwapper *ds;
447 const UDataInfo *pInfo;
448 UErrorCode errorCode;
449
450 const uint8_t *inBytes;
451
452 int32_t length, offset, i;
453 int32_t itemLength, typeEnum;
454 char type;
455
456 const UDataOffsetTOCEntry *inEntries;
457
458 extractPackageName(filename, inPkgName, (int32_t)sizeof(inPkgName));
459
460 /* read the file */
461 inData=readFile(NULL, filename, inLength, type);
462 length=inLength;
463
464 /*
465 * swap the header - even if the swapping itself is a no-op
466 * because it tells us the header length
467 */
468 errorCode=U_ZERO_ERROR;
469 makeTypeProps(type, inCharset, inIsBigEndian);
470 ds=udata_openSwapper(inIsBigEndian, inCharset, U_IS_BIG_ENDIAN, U_CHARSET_FAMILY, &errorCode);
471 if(U_FAILURE(errorCode)) {
472 fprintf(stderr, "icupkg: udata_openSwapper(\"%s\") failed - %s\n",
473 filename, u_errorName(errorCode));
474 exit(errorCode);
475 }
476
477 ds->printError=printPackageError;
478 ds->printErrorContext=stderr;
479
480 headerLength=sizeof(header);
481 if(length<headerLength) {
482 headerLength=length;
483 }
484 headerLength=udata_swapDataHeader(ds, inData, headerLength, header, &errorCode);
485 if(U_FAILURE(errorCode)) {
486 exit(errorCode);
487 }
488
489 /* check data format and format version */
490 pInfo=(const UDataInfo *)((const char *)inData+4);
491 if(!(
492 pInfo->dataFormat[0]==0x43 && /* dataFormat="CmnD" */
493 pInfo->dataFormat[1]==0x6d &&
494 pInfo->dataFormat[2]==0x6e &&
495 pInfo->dataFormat[3]==0x44 &&
496 pInfo->formatVersion[0]==1
497 )) {
498 fprintf(stderr, "icupkg: data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as an ICU .dat package\n",
499 pInfo->dataFormat[0], pInfo->dataFormat[1],
500 pInfo->dataFormat[2], pInfo->dataFormat[3],
501 pInfo->formatVersion[0]);
502 exit(U_UNSUPPORTED_ERROR);
503 }
504 inIsBigEndian=(UBool)pInfo->isBigEndian;
505 inCharset=pInfo->charsetFamily;
506
507 inBytes=(const uint8_t *)inData+headerLength;
508 inEntries=(const UDataOffsetTOCEntry *)(inBytes+4);
509
510 /* check that the itemCount fits, then the ToC table, then at least the header of the last item */
511 length-=headerLength;
512 if(length<4) {
513 /* itemCount does not fit */
514 offset=0x7fffffff;
515 } else {
516 itemCount=udata_readInt32(ds, *(const int32_t *)inBytes);
517 setItemCapacity(itemCount); /* resize so there's space */
518 if(itemCount==0) {
519 offset=4;
520 } else if(length<(4+8*itemCount)) {
521 /* ToC table does not fit */
522 offset=0x7fffffff;
523 } else {
524 /* offset of the last item plus at least 20 bytes for its header */
525 offset=20+(int32_t)ds->readUInt32(inEntries[itemCount-1].dataOffset);
526 }
527 }
528 if(length<offset) {
529 fprintf(stderr, "icupkg: too few bytes (%ld after header) for a .dat package\n",
530 (long)length);
531 exit(U_INDEX_OUTOFBOUNDS_ERROR);
532 }
533 /* do not modify the package length variable until the last item's length is set */
534
535 if(itemCount<=0) {
536 if(doAutoPrefix) {
537 fprintf(stderr, "icupkg: --auto_toc_prefix[_with_type] but the input package is empty\n");
538 exit(U_INVALID_FORMAT_ERROR);
539 }
540 } else {
541 char prefix[MAX_PKG_NAME_LENGTH+4];
542 char *s, *inItemStrings;
543
544 if(itemCount>itemMax) {
545 fprintf(stderr, "icupkg: too many items, maximum is %d\n", itemMax);
546 exit(U_BUFFER_OVERFLOW_ERROR);
547 }
548
549 /* swap the item name strings */
550 int32_t stringsOffset=4+8*itemCount;
551 itemLength=(int32_t)(ds->readUInt32(inEntries[0].dataOffset))-stringsOffset;
552
553 // don't include padding bytes at the end of the item names
554 while(itemLength>0 && inBytes[stringsOffset+itemLength-1]!=0) {
555 --itemLength;
556 }
557
558 if((inStringTop+itemLength)>STRING_STORE_SIZE) {
559 fprintf(stderr, "icupkg: total length of item name strings too long\n");
560 exit(U_BUFFER_OVERFLOW_ERROR);
561 }
562
563 inItemStrings=inStrings+inStringTop;
564 ds->swapInvChars(ds, inBytes+stringsOffset, itemLength, inItemStrings, &errorCode);
565 if(U_FAILURE(errorCode)) {
566 fprintf(stderr, "icupkg failed to swap the input .dat package item name strings\n");
567 exit(U_INVALID_FORMAT_ERROR);
568 }
569 inStringTop+=itemLength;
570
571 // reset the Item entries
572 memset(items, 0, itemCount*sizeof(Item));
573
574 /*
575 * Get the common prefix of the items.
576 * New-style ICU .dat packages use tree separators ('/') between package names,
577 * tree names, and item names,
578 * while old-style ICU .dat packages (before multi-tree support)
579 * use an underscore ('_') between package and item names.
580 */
581 offset=(int32_t)ds->readUInt32(inEntries[0].nameOffset)-stringsOffset;
582 s=inItemStrings+offset; // name of the first entry
583 int32_t prefixLength;
584 if(doAutoPrefix) {
585 // Use the first entry's prefix. Must be a new-style package.
586 const char *prefixLimit=strchr(s, U_TREE_ENTRY_SEP_CHAR);
587 if(prefixLimit==NULL) {
588 fprintf(stderr,
589 "icupkg: --auto_toc_prefix[_with_type] but "
590 "the first entry \"%s\" does not contain a '%c'\n",
591 s, U_TREE_ENTRY_SEP_CHAR);
592 exit(U_INVALID_FORMAT_ERROR);
593 }
594 prefixLength=(int32_t)(prefixLimit-s);
595 if(prefixLength==0 || prefixLength>=UPRV_LENGTHOF(pkgPrefix)) {
596 fprintf(stderr,
597 "icupkg: --auto_toc_prefix[_with_type] but "
598 "the prefix of the first entry \"%s\" is empty or too long\n",
599 s);
600 exit(U_INVALID_FORMAT_ERROR);
601 }
602 if(prefixEndsWithType && s[prefixLength-1]!=type) {
603 fprintf(stderr,
604 "icupkg: --auto_toc_prefix_with_type but "
605 "the prefix of the first entry \"%s\" does not end with '%c'\n",
606 s, type);
607 exit(U_INVALID_FORMAT_ERROR);
608 }
609 memcpy(pkgPrefix, s, prefixLength);
610 pkgPrefix[prefixLength]=0;
611 memcpy(prefix, s, ++prefixLength); // include the /
612 } else {
613 // Use the package basename as prefix.
614 int32_t inPkgNameLength=strlen(inPkgName);
615 memcpy(prefix, inPkgName, inPkgNameLength);
616 prefixLength=inPkgNameLength;
617
618 if( (int32_t)strlen(s)>=(inPkgNameLength+2) &&
619 0==memcmp(s, inPkgName, inPkgNameLength) &&
620 s[inPkgNameLength]=='_'
621 ) {
622 // old-style .dat package
623 prefix[prefixLength++]='_';
624 } else {
625 // new-style .dat package
626 prefix[prefixLength++]=U_TREE_ENTRY_SEP_CHAR;
627 // if it turns out to not contain U_TREE_ENTRY_SEP_CHAR
628 // then the test in the loop below will fail
629 }
630 }
631 prefix[prefixLength]=0;
632
633 /* read the ToC table */
634 for(i=0; i<itemCount; ++i) {
635 // skip the package part of the item name, error if it does not match the actual package name
636 // or if nothing follows the package name
637 offset=(int32_t)ds->readUInt32(inEntries[i].nameOffset)-stringsOffset;
638 s=inItemStrings+offset;
639 if(0!=strncmp(s, prefix, prefixLength) || s[prefixLength]==0) {
640 fprintf(stderr, "icupkg: input .dat item name \"%s\" does not start with \"%s\"\n",
641 s, prefix);
642 exit(U_INVALID_FORMAT_ERROR);
643 }
644 items[i].name=s+prefixLength;
645
646 // set the item's data
647 items[i].data=(uint8_t *)inBytes+ds->readUInt32(inEntries[i].dataOffset);
648 if(i>0) {
649 items[i-1].length=(int32_t)(items[i].data-items[i-1].data);
650
651 // set the previous item's platform type
652 typeEnum=getTypeEnumForInputData(items[i-1].data, items[i-1].length, &errorCode);
653 if(typeEnum<0 || U_FAILURE(errorCode)) {
654 fprintf(stderr, "icupkg: not an ICU data file: item \"%s\" in \"%s\"\n", items[i-1].name, filename);
655 exit(U_INVALID_FORMAT_ERROR);
656 }
657 items[i-1].type=makeTypeLetter(typeEnum);
658 }
659 items[i].isDataOwned=FALSE;
660 }
661 // set the last item's length
662 items[itemCount-1].length=length-ds->readUInt32(inEntries[itemCount-1].dataOffset);
663
664 // set the last item's platform type
665 typeEnum=getTypeEnumForInputData(items[itemCount-1].data, items[itemCount-1].length, &errorCode);
666 if(typeEnum<0 || U_FAILURE(errorCode)) {
667 fprintf(stderr, "icupkg: not an ICU data file: item \"%s\" in \"%s\"\n", items[i-1].name, filename);
668 exit(U_INVALID_FORMAT_ERROR);
669 }
670 items[itemCount-1].type=makeTypeLetter(typeEnum);
671
672 if(type!=U_ICUDATA_TYPE_LETTER[0]) {
673 // sort the item names for the local charset
674 sortItems();
675 }
676 }
677
678 udata_closeSwapper(ds);
679 }
680
681 char
getInType()682 Package::getInType() {
683 return makeTypeLetter(inCharset, inIsBigEndian);
684 }
685
686 void
writePackage(const char * filename,char outType,const char * comment)687 Package::writePackage(const char *filename, char outType, const char *comment) {
688 char prefix[MAX_PKG_NAME_LENGTH+4];
689 UDataOffsetTOCEntry entry;
690 UDataSwapper *dsLocalToOut, *ds[TYPE_COUNT];
691 FILE *file;
692 Item *pItem;
693 char *name;
694 UErrorCode errorCode;
695 int32_t i, length, prefixLength, maxItemLength, basenameOffset, offset, outInt32;
696 uint8_t outCharset;
697 UBool outIsBigEndian;
698
699 extractPackageName(filename, prefix, MAX_PKG_NAME_LENGTH);
700
701 // if there is an explicit comment, then use it, else use what's in the current header
702 if(comment!=NULL) {
703 /* get the header size minus the current comment */
704 DataHeader *pHeader;
705 int32_t length;
706
707 pHeader=(DataHeader *)header;
708 headerLength=4+pHeader->info.size;
709 length=(int32_t)strlen(comment);
710 if((int32_t)(headerLength+length)>=(int32_t)sizeof(header)) {
711 fprintf(stderr, "icupkg: comment too long\n");
712 exit(U_BUFFER_OVERFLOW_ERROR);
713 }
714 memcpy(header+headerLength, comment, length+1);
715 headerLength+=length;
716 if(headerLength&0xf) {
717 /* NUL-pad the header to a multiple of 16 */
718 length=(headerLength+0xf)&~0xf;
719 memset(header+headerLength, 0, length-headerLength);
720 headerLength=length;
721 }
722 pHeader->dataHeader.headerSize=(uint16_t)headerLength;
723 }
724
725 makeTypeProps(outType, outCharset, outIsBigEndian);
726
727 // open (TYPE_COUNT-2) swappers
728 // one is a no-op for local type==outType
729 // one type (TYPE_LE) is bogus
730 errorCode=U_ZERO_ERROR;
731 i=makeTypeEnum(outType);
732 ds[TYPE_B]= i==TYPE_B ? NULL : udata_openSwapper(TRUE, U_ASCII_FAMILY, outIsBigEndian, outCharset, &errorCode);
733 ds[TYPE_L]= i==TYPE_L ? NULL : udata_openSwapper(FALSE, U_ASCII_FAMILY, outIsBigEndian, outCharset, &errorCode);
734 ds[TYPE_LE]=NULL;
735 ds[TYPE_E]= i==TYPE_E ? NULL : udata_openSwapper(TRUE, U_EBCDIC_FAMILY, outIsBigEndian, outCharset, &errorCode);
736 if(U_FAILURE(errorCode)) {
737 fprintf(stderr, "icupkg: udata_openSwapper() failed - %s\n", u_errorName(errorCode));
738 exit(errorCode);
739 }
740 for(i=0; i<TYPE_COUNT; ++i) {
741 if(ds[i]!=NULL) {
742 ds[i]->printError=printPackageError;
743 ds[i]->printErrorContext=stderr;
744 }
745 }
746
747 dsLocalToOut=ds[makeTypeEnum(U_CHARSET_FAMILY, U_IS_BIG_ENDIAN)];
748
749 // create the file and write its contents
750 file=fopen(filename, "wb");
751 if(file==NULL) {
752 fprintf(stderr, "icupkg: unable to create file \"%s\"\n", filename);
753 exit(U_FILE_ACCESS_ERROR);
754 }
755
756 // swap and write the header
757 if(dsLocalToOut!=NULL) {
758 udata_swapDataHeader(dsLocalToOut, header, headerLength, header, &errorCode);
759 if(U_FAILURE(errorCode)) {
760 fprintf(stderr, "icupkg: udata_swapDataHeader(local to out) failed - %s\n", u_errorName(errorCode));
761 exit(errorCode);
762 }
763 }
764 length=(int32_t)fwrite(header, 1, headerLength, file);
765 if(length!=headerLength) {
766 fprintf(stderr, "icupkg: unable to write complete header to file \"%s\"\n", filename);
767 exit(U_FILE_ACCESS_ERROR);
768 }
769
770 // prepare and swap the package name with a tree separator
771 // for prepending to item names
772 if(pkgPrefix[0]==0) {
773 prefixLength=(int32_t)strlen(prefix);
774 } else {
775 prefixLength=(int32_t)strlen(pkgPrefix);
776 memcpy(prefix, pkgPrefix, prefixLength);
777 if(prefixEndsWithType) {
778 prefix[prefixLength-1]=outType;
779 }
780 }
781 prefix[prefixLength++]=U_TREE_ENTRY_SEP_CHAR;
782 prefix[prefixLength]=0;
783 if(dsLocalToOut!=NULL) {
784 dsLocalToOut->swapInvChars(dsLocalToOut, prefix, prefixLength, prefix, &errorCode);
785 if(U_FAILURE(errorCode)) {
786 fprintf(stderr, "icupkg: swapInvChars(output package name) failed - %s\n", u_errorName(errorCode));
787 exit(errorCode);
788 }
789
790 // swap and sort the item names (sorting needs to be done in the output charset)
791 dsLocalToOut->swapInvChars(dsLocalToOut, inStrings, inStringTop, inStrings, &errorCode);
792 if(U_FAILURE(errorCode)) {
793 fprintf(stderr, "icupkg: swapInvChars(item names) failed - %s\n", u_errorName(errorCode));
794 exit(errorCode);
795 }
796 sortItems();
797 }
798
799 // create the output item names in sorted order, with the package name prepended to each
800 for(i=0; i<itemCount; ++i) {
801 length=(int32_t)strlen(items[i].name);
802 name=allocString(FALSE, length+prefixLength);
803 memcpy(name, prefix, prefixLength);
804 memcpy(name+prefixLength, items[i].name, length+1);
805 items[i].name=name;
806 }
807
808 // calculate offsets for item names and items, pad to 16-align items
809 // align only the first item; each item's length is a multiple of 16
810 basenameOffset=4+8*itemCount;
811 offset=basenameOffset+outStringTop;
812 if((length=(offset&15))!=0) {
813 length=16-length;
814 memset(allocString(FALSE, length-1), 0xaa, length);
815 offset+=length;
816 }
817
818 // write the table of contents
819 // first the itemCount
820 outInt32=itemCount;
821 if(dsLocalToOut!=NULL) {
822 dsLocalToOut->swapArray32(dsLocalToOut, &outInt32, 4, &outInt32, &errorCode);
823 if(U_FAILURE(errorCode)) {
824 fprintf(stderr, "icupkg: swapArray32(item count) failed - %s\n", u_errorName(errorCode));
825 exit(errorCode);
826 }
827 }
828 length=(int32_t)fwrite(&outInt32, 1, 4, file);
829 if(length!=4) {
830 fprintf(stderr, "icupkg: unable to write complete item count to file \"%s\"\n", filename);
831 exit(U_FILE_ACCESS_ERROR);
832 }
833
834 // then write the item entries (and collect the maxItemLength)
835 maxItemLength=0;
836 for(i=0; i<itemCount; ++i) {
837 entry.nameOffset=(uint32_t)(basenameOffset+(items[i].name-outStrings));
838 entry.dataOffset=(uint32_t)offset;
839 if(dsLocalToOut!=NULL) {
840 dsLocalToOut->swapArray32(dsLocalToOut, &entry, 8, &entry, &errorCode);
841 if(U_FAILURE(errorCode)) {
842 fprintf(stderr, "icupkg: swapArray32(item entry %ld) failed - %s\n", (long)i, u_errorName(errorCode));
843 exit(errorCode);
844 }
845 }
846 length=(int32_t)fwrite(&entry, 1, 8, file);
847 if(length!=8) {
848 fprintf(stderr, "icupkg: unable to write complete item entry %ld to file \"%s\"\n", (long)i, filename);
849 exit(U_FILE_ACCESS_ERROR);
850 }
851
852 length=items[i].length;
853 if(length>maxItemLength) {
854 maxItemLength=length;
855 }
856 offset+=length;
857 }
858
859 // write the item names
860 length=(int32_t)fwrite(outStrings, 1, outStringTop, file);
861 if(length!=outStringTop) {
862 fprintf(stderr, "icupkg: unable to write complete item names to file \"%s\"\n", filename);
863 exit(U_FILE_ACCESS_ERROR);
864 }
865
866 // write the items
867 for(pItem=items, i=0; i<itemCount; ++pItem, ++i) {
868 int32_t type=makeTypeEnum(pItem->type);
869 if(ds[type]!=NULL) {
870 // swap each item from its platform properties to the desired ones
871 udata_swap(
872 ds[type],
873 pItem->data, pItem->length, pItem->data,
874 &errorCode);
875 if(U_FAILURE(errorCode)) {
876 fprintf(stderr, "icupkg: udata_swap(item %ld) failed - %s\n", (long)i, u_errorName(errorCode));
877 exit(errorCode);
878 }
879 }
880 length=(int32_t)fwrite(pItem->data, 1, pItem->length, file);
881 if(length!=pItem->length) {
882 fprintf(stderr, "icupkg: unable to write complete item %ld to file \"%s\"\n", (long)i, filename);
883 exit(U_FILE_ACCESS_ERROR);
884 }
885 }
886
887 if(ferror(file)) {
888 fprintf(stderr, "icupkg: unable to write complete file \"%s\"\n", filename);
889 exit(U_FILE_ACCESS_ERROR);
890 }
891
892 fclose(file);
893 for(i=0; i<TYPE_COUNT; ++i) {
894 udata_closeSwapper(ds[i]);
895 }
896 }
897
898 int32_t
findItem(const char * name,int32_t length) const899 Package::findItem(const char *name, int32_t length) const {
900 int32_t i, start, limit;
901 int result;
902
903 /* do a binary search for the string */
904 start=0;
905 limit=itemCount;
906 while(start<limit) {
907 i=(start+limit)/2;
908 if(length>=0) {
909 result=strncmp(name, items[i].name, length);
910 } else {
911 result=strcmp(name, items[i].name);
912 }
913
914 if(result==0) {
915 /* found */
916 if(length>=0) {
917 /*
918 * if we compared just prefixes, then we may need to back up
919 * to the first item with this prefix
920 */
921 while(i>0 && 0==strncmp(name, items[i-1].name, length)) {
922 --i;
923 }
924 }
925 return i;
926 } else if(result<0) {
927 limit=i;
928 } else /* result>0 */ {
929 start=i+1;
930 }
931 }
932
933 return ~start; /* not found, return binary-not of the insertion point */
934 }
935
936 void
findItems(const char * pattern)937 Package::findItems(const char *pattern) {
938 const char *wild;
939
940 if(pattern==NULL || *pattern==0) {
941 findNextIndex=-1;
942 return;
943 }
944
945 findPrefix=pattern;
946 findSuffix=NULL;
947 findSuffixLength=0;
948
949 wild=strchr(pattern, '*');
950 if(wild==NULL) {
951 // no wildcard
952 findPrefixLength=(int32_t)strlen(pattern);
953 } else {
954 // one wildcard
955 findPrefixLength=(int32_t)(wild-pattern);
956 findSuffix=wild+1;
957 findSuffixLength=(int32_t)strlen(findSuffix);
958 if(NULL!=strchr(findSuffix, '*')) {
959 // two or more wildcards
960 fprintf(stderr, "icupkg: syntax error (more than one '*') in item pattern \"%s\"\n", pattern);
961 exit(U_PARSE_ERROR);
962 }
963 }
964
965 if(findPrefixLength==0) {
966 findNextIndex=0;
967 } else {
968 findNextIndex=findItem(findPrefix, findPrefixLength);
969 }
970 }
971
972 int32_t
findNextItem()973 Package::findNextItem() {
974 const char *name, *middle, *treeSep;
975 int32_t idx, nameLength, middleLength;
976
977 if(findNextIndex<0) {
978 return -1;
979 }
980
981 while(findNextIndex<itemCount) {
982 idx=findNextIndex++;
983 name=items[idx].name;
984 nameLength=(int32_t)strlen(name);
985 if(nameLength<(findPrefixLength+findSuffixLength)) {
986 // item name too short for prefix & suffix
987 continue;
988 }
989 if(findPrefixLength>0 && 0!=memcmp(findPrefix, name, findPrefixLength)) {
990 // left the range of names with this prefix
991 break;
992 }
993 middle=name+findPrefixLength;
994 middleLength=nameLength-findPrefixLength-findSuffixLength;
995 if(findSuffixLength>0 && 0!=memcmp(findSuffix, name+(nameLength-findSuffixLength), findSuffixLength)) {
996 // suffix does not match
997 continue;
998 }
999 // prefix & suffix match
1000
1001 if(matchMode&MATCH_NOSLASH) {
1002 treeSep=strchr(middle, U_TREE_ENTRY_SEP_CHAR);
1003 if(treeSep!=NULL && (treeSep-middle)<middleLength) {
1004 // the middle (matching the * wildcard) contains a tree separator /
1005 continue;
1006 }
1007 }
1008
1009 // found a matching item
1010 return idx;
1011 }
1012
1013 // no more items
1014 findNextIndex=-1;
1015 return -1;
1016 }
1017
1018 void
setMatchMode(uint32_t mode)1019 Package::setMatchMode(uint32_t mode) {
1020 matchMode=mode;
1021 }
1022
1023 void
addItem(const char * name)1024 Package::addItem(const char *name) {
1025 addItem(name, NULL, 0, FALSE, U_ICUDATA_TYPE_LETTER[0]);
1026 }
1027
1028 void
addItem(const char * name,uint8_t * data,int32_t length,UBool isDataOwned,char type)1029 Package::addItem(const char *name, uint8_t *data, int32_t length, UBool isDataOwned, char type) {
1030 int32_t idx;
1031
1032 idx=findItem(name);
1033 if(idx<0) {
1034 // new item, make space at the insertion point
1035 ensureItemCapacity();
1036 // move the following items down
1037 idx=~idx;
1038 if(idx<itemCount) {
1039 memmove(items+idx+1, items+idx, (itemCount-idx)*sizeof(Item));
1040 }
1041 ++itemCount;
1042
1043 // reset this Item entry
1044 memset(items+idx, 0, sizeof(Item));
1045
1046 // copy the item's name
1047 items[idx].name=allocString(TRUE, strlen(name));
1048 strcpy(items[idx].name, name);
1049 pathToTree(items[idx].name);
1050 } else {
1051 // same-name item found, replace it
1052 if(items[idx].isDataOwned) {
1053 free(items[idx].data);
1054 }
1055
1056 // keep the item's name since it is the same
1057 }
1058
1059 // set the item's data
1060 items[idx].data=data;
1061 items[idx].length=length;
1062 items[idx].isDataOwned=isDataOwned;
1063 items[idx].type=type;
1064 }
1065
1066 void
addFile(const char * filesPath,const char * name)1067 Package::addFile(const char *filesPath, const char *name) {
1068 uint8_t *data;
1069 int32_t length;
1070 char type;
1071
1072 data=readFile(filesPath, name, length, type);
1073 // readFile() exits the tool if it fails
1074 addItem(name, data, length, TRUE, type);
1075 }
1076
1077 void
addItems(const Package & listPkg)1078 Package::addItems(const Package &listPkg) {
1079 const Item *pItem;
1080 int32_t i;
1081
1082 for(pItem=listPkg.items, i=0; i<listPkg.itemCount; ++pItem, ++i) {
1083 addItem(pItem->name, pItem->data, pItem->length, FALSE, pItem->type);
1084 }
1085 }
1086
1087 void
removeItem(int32_t idx)1088 Package::removeItem(int32_t idx) {
1089 if(idx>=0) {
1090 // remove the item
1091 if(items[idx].isDataOwned) {
1092 free(items[idx].data);
1093 }
1094
1095 // move the following items up
1096 if((idx+1)<itemCount) {
1097 memmove(items+idx, items+idx+1, (itemCount-(idx+1))*sizeof(Item));
1098 }
1099 --itemCount;
1100
1101 if(idx<=findNextIndex) {
1102 --findNextIndex;
1103 }
1104 }
1105 }
1106
1107 void
removeItems(const char * pattern)1108 Package::removeItems(const char *pattern) {
1109 int32_t idx;
1110
1111 findItems(pattern);
1112 while((idx=findNextItem())>=0) {
1113 removeItem(idx);
1114 }
1115 }
1116
1117 void
removeItems(const Package & listPkg)1118 Package::removeItems(const Package &listPkg) {
1119 const Item *pItem;
1120 int32_t i;
1121
1122 for(pItem=listPkg.items, i=0; i<listPkg.itemCount; ++pItem, ++i) {
1123 removeItems(pItem->name);
1124 }
1125 }
1126
1127 void
extractItem(const char * filesPath,const char * outName,int32_t idx,char outType)1128 Package::extractItem(const char *filesPath, const char *outName, int32_t idx, char outType) {
1129 char filename[1024];
1130 UDataSwapper *ds;
1131 FILE *file;
1132 Item *pItem;
1133 int32_t fileLength;
1134 uint8_t itemCharset, outCharset;
1135 UBool itemIsBigEndian, outIsBigEndian;
1136
1137 if(idx<0 || itemCount<=idx) {
1138 return;
1139 }
1140 pItem=items+idx;
1141
1142 // swap the data to the outType
1143 // outType==0: don't swap
1144 if(outType!=0 && pItem->type!=outType) {
1145 // open the swapper
1146 UErrorCode errorCode=U_ZERO_ERROR;
1147 makeTypeProps(pItem->type, itemCharset, itemIsBigEndian);
1148 makeTypeProps(outType, outCharset, outIsBigEndian);
1149 ds=udata_openSwapper(itemIsBigEndian, itemCharset, outIsBigEndian, outCharset, &errorCode);
1150 if(U_FAILURE(errorCode)) {
1151 fprintf(stderr, "icupkg: udata_openSwapper(item %ld) failed - %s\n",
1152 (long)idx, u_errorName(errorCode));
1153 exit(errorCode);
1154 }
1155
1156 ds->printError=printPackageError;
1157 ds->printErrorContext=stderr;
1158
1159 // swap the item from its platform properties to the desired ones
1160 udata_swap(ds, pItem->data, pItem->length, pItem->data, &errorCode);
1161 if(U_FAILURE(errorCode)) {
1162 fprintf(stderr, "icupkg: udata_swap(item %ld) failed - %s\n", (long)idx, u_errorName(errorCode));
1163 exit(errorCode);
1164 }
1165 udata_closeSwapper(ds);
1166 pItem->type=outType;
1167 }
1168
1169 // create the file and write its contents
1170 makeFullFilenameAndDirs(filesPath, outName, filename, (int32_t)sizeof(filename));
1171 file=fopen(filename, "wb");
1172 if(file==NULL) {
1173 fprintf(stderr, "icupkg: unable to create file \"%s\"\n", filename);
1174 exit(U_FILE_ACCESS_ERROR);
1175 }
1176 fileLength=(int32_t)fwrite(pItem->data, 1, pItem->length, file);
1177
1178 if(ferror(file) || fileLength!=pItem->length) {
1179 fprintf(stderr, "icupkg: unable to write complete file \"%s\"\n", filename);
1180 exit(U_FILE_ACCESS_ERROR);
1181 }
1182 fclose(file);
1183 }
1184
1185 void
extractItem(const char * filesPath,int32_t idx,char outType)1186 Package::extractItem(const char *filesPath, int32_t idx, char outType) {
1187 extractItem(filesPath, items[idx].name, idx, outType);
1188 }
1189
1190 void
extractItems(const char * filesPath,const char * pattern,char outType)1191 Package::extractItems(const char *filesPath, const char *pattern, char outType) {
1192 int32_t idx;
1193
1194 findItems(pattern);
1195 while((idx=findNextItem())>=0) {
1196 extractItem(filesPath, idx, outType);
1197 }
1198 }
1199
1200 void
extractItems(const char * filesPath,const Package & listPkg,char outType)1201 Package::extractItems(const char *filesPath, const Package &listPkg, char outType) {
1202 const Item *pItem;
1203 int32_t i;
1204
1205 for(pItem=listPkg.items, i=0; i<listPkg.itemCount; ++pItem, ++i) {
1206 extractItems(filesPath, pItem->name, outType);
1207 }
1208 }
1209
1210 int32_t
getItemCount() const1211 Package::getItemCount() const {
1212 return itemCount;
1213 }
1214
1215 const Item *
getItem(int32_t idx) const1216 Package::getItem(int32_t idx) const {
1217 if (0 <= idx && idx < itemCount) {
1218 return &items[idx];
1219 }
1220 return NULL;
1221 }
1222
1223 void
checkDependency(void * context,const char * itemName,const char * targetName)1224 Package::checkDependency(void *context, const char *itemName, const char *targetName) {
1225 // check dependency: make sure the target item is in the package
1226 Package *me=(Package *)context;
1227 if(me->findItem(targetName)<0) {
1228 me->isMissingItems=TRUE;
1229 fprintf(stderr, "Item %s depends on missing item %s\n", itemName, targetName);
1230 }
1231 }
1232
1233 UBool
checkDependencies()1234 Package::checkDependencies() {
1235 isMissingItems=FALSE;
1236 enumDependencies(this, checkDependency);
1237 return (UBool)!isMissingItems;
1238 }
1239
1240 void
enumDependencies(void * context,CheckDependency check)1241 Package::enumDependencies(void *context, CheckDependency check) {
1242 int32_t i;
1243
1244 for(i=0; i<itemCount; ++i) {
1245 enumDependencies(items+i, context, check);
1246 }
1247 }
1248
1249 char *
allocString(UBool in,int32_t length)1250 Package::allocString(UBool in, int32_t length) {
1251 char *p;
1252 int32_t top;
1253
1254 if(in) {
1255 top=inStringTop;
1256 p=inStrings+top;
1257 } else {
1258 top=outStringTop;
1259 p=outStrings+top;
1260 }
1261 top+=length+1;
1262
1263 if(top>STRING_STORE_SIZE) {
1264 fprintf(stderr, "icupkg: string storage overflow\n");
1265 exit(U_BUFFER_OVERFLOW_ERROR);
1266 }
1267 if(in) {
1268 inStringTop=top;
1269 } else {
1270 outStringTop=top;
1271 }
1272 return p;
1273 }
1274
1275 void
sortItems()1276 Package::sortItems() {
1277 UErrorCode errorCode=U_ZERO_ERROR;
1278 uprv_sortArray(items, itemCount, (int32_t)sizeof(Item), compareItems, NULL, FALSE, &errorCode);
1279 if(U_FAILURE(errorCode)) {
1280 fprintf(stderr, "icupkg: sorting item names failed - %s\n", u_errorName(errorCode));
1281 exit(errorCode);
1282 }
1283 }
1284
setItemCapacity(int32_t max)1285 void Package::setItemCapacity(int32_t max)
1286 {
1287 if(max<=itemMax) {
1288 return;
1289 }
1290 Item *newItems = (Item*)uprv_malloc(max * sizeof(items[0]));
1291 Item *oldItems = items;
1292 if(newItems == NULL) {
1293 fprintf(stderr, "icupkg: Out of memory trying to allocate %lu bytes for %d items\n",
1294 (unsigned long)max*sizeof(items[0]), max);
1295 exit(U_MEMORY_ALLOCATION_ERROR);
1296 }
1297 if(items && itemCount>0) {
1298 uprv_memcpy(newItems, items, itemCount*sizeof(items[0]));
1299 }
1300 itemMax = max;
1301 items = newItems;
1302 uprv_free(oldItems);
1303 }
1304
ensureItemCapacity()1305 void Package::ensureItemCapacity()
1306 {
1307 if((itemCount+1)>itemMax) {
1308 setItemCapacity(itemCount+kItemsChunk);
1309 }
1310 }
1311
1312 U_NAMESPACE_END
1313