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