2 ******************************************************************************* 
   4 *   Copyright (C) 1999-2015, 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" 
  41 static const int32_t kItemsChunk 
= 256; /* How much to increase the filesarray by each time */ 
  43 // general definitions ----------------------------------------------------- *** 
  45 /* UDataInfo cf. udata.h */ 
  46 static const UDataInfo dataInfo
={ 
  47     (uint16_t)sizeof(UDataInfo
), 
  52     (uint8_t)sizeof(UChar
), 
  55     {0x43, 0x6d, 0x6e, 0x44},     /* dataFormat="CmnD" */ 
  56     {1, 0, 0, 0},                 /* formatVersion */ 
  57     {3, 0, 0, 0}                  /* dataVersion */ 
  61 static void U_CALLCONV
 
  62 printPackageError(void *context
, const char *fmt
, va_list args
) { 
  63     vfprintf((FILE *)context
, fmt
, args
); 
  68 readSwapUInt16(uint16_t x
) { 
  69     return (uint16_t)((x
<<8)|(x
>>8)); 
  72 // platform types ---------------------------------------------------------- *** 
  74 static const char *types
="lb?e"; 
  76 enum { TYPE_L
, TYPE_B
, TYPE_LE
, TYPE_E
, TYPE_COUNT 
}; 
  79 makeTypeEnum(uint8_t charset
, UBool isBigEndian
) { 
  80     return 2*(int32_t)charset
+isBigEndian
; 
  84 makeTypeEnum(char type
) { 
  86         type 
== 'l' ? TYPE_L 
: 
  87         type 
== 'b' ? TYPE_B 
: 
  88         type 
== 'e' ? TYPE_E 
: 
  93 makeTypeLetter(uint8_t charset
, UBool isBigEndian
) { 
  94     return types
[makeTypeEnum(charset
, isBigEndian
)]; 
  98 makeTypeLetter(int32_t typeEnum
) { 
  99     return types
[typeEnum
]; 
 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); 
 109 U_CFUNC 
const UDataInfo 
* 
 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
; 
 116     if(pErrorCode
==NULL 
|| U_FAILURE(*pErrorCode
)) { 
 120         (length
>=0 && length
<(int32_t)sizeof(DataHeader
)) 
 122         *pErrorCode
=U_ILLEGAL_ARGUMENT_ERROR
; 
 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 
 133         *pErrorCode
=U_UNSUPPORTED_ERROR
; 
 137     if(pInfo
->isBigEndian
==U_IS_BIG_ENDIAN
) { 
 138         headerLength
=pHeader
->dataHeader
.headerSize
; 
 139         infoLength
=pInfo
->size
; 
 141         headerLength
=readSwapUInt16(pHeader
->dataHeader
.headerSize
); 
 142         infoLength
=readSwapUInt16(pInfo
->size
); 
 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
) 
 150         *pErrorCode
=U_UNSUPPORTED_ERROR
; 
 158 getTypeEnumForInputData(const uint8_t *data
, int32_t length
, 
 159                         UErrorCode 
*pErrorCode
) { 
 160     const UDataInfo 
*pInfo
; 
 161     int32_t infoLength
, headerLength
; 
 163     /* getDataInfo() checks for illegal arguments */ 
 164     pInfo
=getDataInfo(data
, length
, infoLength
, headerLength
, pErrorCode
); 
 169     return makeTypeEnum(pInfo
->charsetFamily
, (UBool
)pInfo
->isBigEndian
); 
 172 // file handling ----------------------------------------------------------- *** 
 175 extractPackageName(const char *filename
, 
 176                    char pkg
[], int32_t capacity
) { 
 177     const char *basename
; 
 180     basename
=findBasename(filename
); 
 181     len
=(int32_t)strlen(basename
)-4; /* -4: subtract the length of ".dat" */ 
 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", 
 186         exit(U_ILLEGAL_ARGUMENT_ERROR
); 
 190         fprintf(stderr
, "icupkg: the package name \"%s\" is too long (>=%ld)\n", 
 191                          basename
, (long)capacity
); 
 192         exit(U_ILLEGAL_ARGUMENT_ERROR
); 
 195     memcpy(pkg
, basename
, len
); 
 200 getFileLength(FILE *f
) { 
 203     fseek(f
, 0, SEEK_END
); 
 204     length
=(int32_t)ftell(f
); 
 205     fseek(f
, 0, SEEK_SET
); 
 210  * Turn tree separators and alternate file separators into normal file separators. 
 212 #if U_TREE_ENTRY_SEP_CHAR==U_FILE_SEP_CHAR && U_FILE_ALT_SEP_CHAR==U_FILE_SEP_CHAR 
 213 #define treeToPath(s) 
 216 treeToPath(char *s
) { 
 219     for(t
=s
; *t
!=0; ++t
) { 
 220         if(*t
==U_TREE_ENTRY_SEP_CHAR 
|| *t
==U_FILE_ALT_SEP_CHAR
) { 
 228  * Turn file separators into tree separators. 
 230 #if U_TREE_ENTRY_SEP_CHAR==U_FILE_SEP_CHAR && U_FILE_ALT_SEP_CHAR==U_FILE_SEP_CHAR 
 231 #define pathToTree(s) 
 234 pathToTree(char *s
) { 
 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
; 
 246  * Prepend the path (if any) to the name and run the name through treeToName(). 
 249 makeFullFilename(const char *path
, const char *name
, 
 250                  char *filename
, int32_t capacity
) { 
 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
); 
 259         strcpy(filename
, path
); 
 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
; 
 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
); 
 280 makeFullFilenameAndDirs(const char *path
, const char *name
, 
 281                         char *filename
, int32_t capacity
) { 
 283     UErrorCode errorCode
; 
 285     makeFullFilename(path
, name
, filename
, capacity
); 
 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
) { 
 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
); 
 299         *sep
++=U_FILE_SEP_CHAR
; // restore file separator character 
 304 readFile(const char *path
, const char *name
, int32_t &length
, char &type
) { 
 307     UErrorCode errorCode
; 
 308     int32_t fileLength
, typeEnum
; 
 310     makeFullFilename(path
, name
, filename
, (int32_t)sizeof(filename
)); 
 312     /* open the input file, get its length, allocate memory for it, read the file */ 
 313     file
=fopen(filename
, "rb"); 
 315         fprintf(stderr
, "icupkg: unable to open input file \"%s\"\n", filename
); 
 316         exit(U_FILE_ACCESS_ERROR
); 
 319     /* get the file length */ 
 320     fileLength
=getFileLength(file
); 
 321     if(ferror(file
) || fileLength
<=0) { 
 322         fprintf(stderr
, "icupkg: empty input file \"%s\"\n", filename
); 
 324         exit(U_FILE_ACCESS_ERROR
); 
 327     /* allocate the buffer, pad to multiple of 16 */ 
 328     length
=(fileLength
+0xf)&~0xf; 
 329     icu::LocalMemory
<uint8_t> data((uint8_t *)uprv_malloc(length
)); 
 332         fprintf(stderr
, "icupkg: malloc error allocating %d bytes.\n", (int)length
); 
 333         exit(U_MEMORY_ALLOCATION_ERROR
); 
 337     if(fileLength
!=(int32_t)fread(data
.getAlias(), 1, fileLength
, file
)) { 
 338         fprintf(stderr
, "icupkg: error reading \"%s\"\n", filename
); 
 340         exit(U_FILE_ACCESS_ERROR
); 
 343     /* pad the file to a multiple of 16 using the usual padding byte */ 
 344     if(fileLength
<length
) { 
 345         memset(data
.getAlias()+fileLength
, 0xaa, length
-fileLength
); 
 350     // minimum check for ICU-format data 
 351     errorCode
=U_ZERO_ERROR
; 
 352     typeEnum
=getTypeEnumForInputData(data
.getAlias(), length
, &errorCode
); 
 353     if(typeEnum
<0 || U_FAILURE(errorCode
)) { 
 354         fprintf(stderr
, "icupkg: not an ICU data file: \"%s\"\n", filename
); 
 355 #if !UCONFIG_NO_LEGACY_CONVERSION 
 356         exit(U_INVALID_FORMAT_ERROR
); 
 358         fprintf(stderr
, "U_INVALID_FORMAT_ERROR occurred but UCONFIG_NO_LEGACY_CONVERSION is on so this is expected.\n"); 
 362     type
=makeTypeLetter(typeEnum
); 
 364     return data
.orphan(); 
 367 // .dat package file representation ---------------------------------------- *** 
 371 static int32_t U_CALLCONV
 
 372 compareItems(const void * /*context*/, const void *left
, const void *right
) { 
 375     return (int32_t)strcmp(((Item 
*)left
)->name
, ((Item 
*)right
)->name
); 
 383         : doAutoPrefix(FALSE
), prefixEndsWithType(FALSE
) { 
 388     inCharset
=U_CHARSET_FAMILY
; 
 389     inIsBigEndian
=U_IS_BIG_ENDIAN
; 
 395     inStringTop
=outStringTop
=0; 
 398     findPrefix
=findSuffix
=NULL
; 
 399     findPrefixLength
=findSuffixLength
=0; 
 402     // create a header for an empty package 
 404     pHeader
=(DataHeader 
*)header
; 
 405     pHeader
->dataHeader
.magic1
=0xda; 
 406     pHeader
->dataHeader
.magic2
=0x27; 
 407     memcpy(&pHeader
->info
, &dataInfo
, sizeof(dataInfo
)); 
 408     headerLength
=(int32_t)(4+sizeof(dataInfo
)); 
 409     if(headerLength
&0xf) { 
 410         /* NUL-pad the header to a multiple of 16 */ 
 411         int32_t length
=(headerLength
+0xf)&~0xf; 
 412         memset(header
+headerLength
, 0, length
-headerLength
); 
 415     pHeader
->dataHeader
.headerSize
=(uint16_t)headerLength
; 
 418 Package::~Package() { 
 423     for(idx
=0; idx
<itemCount
; ++idx
) { 
 424         if(items
[idx
].isDataOwned
) { 
 425             uprv_free(items
[idx
].data
); 
 429     uprv_free((void*)items
); 
 433 Package::setPrefix(const char *p
) { 
 434     if(strlen(p
)>=sizeof(pkgPrefix
)) { 
 435         fprintf(stderr
, "icupkg: --toc_prefix %s too long\n", p
); 
 436         exit(U_ILLEGAL_ARGUMENT_ERROR
); 
 438     strcpy(pkgPrefix
, p
); 
 442 Package::readPackage(const char *filename
) { 
 444     const UDataInfo 
*pInfo
; 
 445     UErrorCode errorCode
; 
 447     const uint8_t *inBytes
; 
 449     int32_t length
, offset
, i
; 
 450     int32_t itemLength
, typeEnum
; 
 453     const UDataOffsetTOCEntry 
*inEntries
; 
 455     extractPackageName(filename
, inPkgName
, (int32_t)sizeof(inPkgName
)); 
 458     inData
=readFile(NULL
, filename
, inLength
, type
); 
 462      * swap the header - even if the swapping itself is a no-op 
 463      * because it tells us the header length 
 465     errorCode
=U_ZERO_ERROR
; 
 466     makeTypeProps(type
, inCharset
, inIsBigEndian
); 
 467     ds
=udata_openSwapper(inIsBigEndian
, inCharset
, U_IS_BIG_ENDIAN
, U_CHARSET_FAMILY
, &errorCode
); 
 468     if(U_FAILURE(errorCode
)) { 
 469         fprintf(stderr
, "icupkg: udata_openSwapper(\"%s\") failed - %s\n", 
 470                 filename
, u_errorName(errorCode
)); 
 474     ds
->printError
=printPackageError
; 
 475     ds
->printErrorContext
=stderr
; 
 477     headerLength
=sizeof(header
); 
 478     if(length
<headerLength
) { 
 481     headerLength
=udata_swapDataHeader(ds
, inData
, headerLength
, header
, &errorCode
); 
 482     if(U_FAILURE(errorCode
)) { 
 486     /* check data format and format version */ 
 487     pInfo
=(const UDataInfo 
*)((const char *)inData
+4); 
 489         pInfo
->dataFormat
[0]==0x43 &&   /* dataFormat="CmnD" */ 
 490         pInfo
->dataFormat
[1]==0x6d && 
 491         pInfo
->dataFormat
[2]==0x6e && 
 492         pInfo
->dataFormat
[3]==0x44 && 
 493         pInfo
->formatVersion
[0]==1 
 495         fprintf(stderr
, "icupkg: data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as an ICU .dat package\n", 
 496                 pInfo
->dataFormat
[0], pInfo
->dataFormat
[1], 
 497                 pInfo
->dataFormat
[2], pInfo
->dataFormat
[3], 
 498                 pInfo
->formatVersion
[0]); 
 499         exit(U_UNSUPPORTED_ERROR
); 
 501     inIsBigEndian
=(UBool
)pInfo
->isBigEndian
; 
 502     inCharset
=pInfo
->charsetFamily
; 
 504     inBytes
=(const uint8_t *)inData
+headerLength
; 
 505     inEntries
=(const UDataOffsetTOCEntry 
*)(inBytes
+4); 
 507     /* check that the itemCount fits, then the ToC table, then at least the header of the last item */ 
 508     length
-=headerLength
; 
 510         /* itemCount does not fit */ 
 513         itemCount
=udata_readInt32(ds
, *(const int32_t *)inBytes
); 
 514         setItemCapacity(itemCount
); /* resize so there's space */ 
 517         } else if(length
<(4+8*itemCount
)) { 
 518             /* ToC table does not fit */ 
 521             /* offset of the last item plus at least 20 bytes for its header */ 
 522             offset
=20+(int32_t)ds
->readUInt32(inEntries
[itemCount
-1].dataOffset
); 
 526         fprintf(stderr
, "icupkg: too few bytes (%ld after header) for a .dat package\n", 
 528         exit(U_INDEX_OUTOFBOUNDS_ERROR
); 
 530     /* do not modify the package length variable until the last item's length is set */ 
 534             fprintf(stderr
, "icupkg: --auto_toc_prefix[_with_type] but the input package is empty\n"); 
 535             exit(U_INVALID_FORMAT_ERROR
); 
 538         char prefix
[MAX_PKG_NAME_LENGTH
+4]; 
 539         char *s
, *inItemStrings
; 
 541         if(itemCount
>itemMax
) { 
 542             fprintf(stderr
, "icupkg: too many items, maximum is %d\n", itemMax
); 
 543             exit(U_BUFFER_OVERFLOW_ERROR
); 
 546         /* swap the item name strings */ 
 547         int32_t stringsOffset
=4+8*itemCount
; 
 548         itemLength
=(int32_t)(ds
->readUInt32(inEntries
[0].dataOffset
))-stringsOffset
; 
 550         // don't include padding bytes at the end of the item names 
 551         while(itemLength
>0 && inBytes
[stringsOffset
+itemLength
-1]!=0) { 
 555         if((inStringTop
+itemLength
)>STRING_STORE_SIZE
) { 
 556             fprintf(stderr
, "icupkg: total length of item name strings too long\n"); 
 557             exit(U_BUFFER_OVERFLOW_ERROR
); 
 560         inItemStrings
=inStrings
+inStringTop
; 
 561         ds
->swapInvChars(ds
, inBytes
+stringsOffset
, itemLength
, inItemStrings
, &errorCode
); 
 562         if(U_FAILURE(errorCode
)) { 
 563             fprintf(stderr
, "icupkg failed to swap the input .dat package item name strings\n"); 
 564             exit(U_INVALID_FORMAT_ERROR
); 
 566         inStringTop
+=itemLength
; 
 568         // reset the Item entries 
 569         memset(items
, 0, itemCount
*sizeof(Item
)); 
 572          * Get the common prefix of the items. 
 573          * New-style ICU .dat packages use tree separators ('/') between package names, 
 574          * tree names, and item names, 
 575          * while old-style ICU .dat packages (before multi-tree support) 
 576          * use an underscore ('_') between package and item names. 
 578         offset
=(int32_t)ds
->readUInt32(inEntries
[0].nameOffset
)-stringsOffset
; 
 579         s
=inItemStrings
+offset
;  // name of the first entry 
 580         int32_t prefixLength
; 
 582             // Use the first entry's prefix. Must be a new-style package. 
 583             const char *prefixLimit
=strchr(s
, U_TREE_ENTRY_SEP_CHAR
); 
 584             if(prefixLimit
==NULL
) { 
 586                         "icupkg: --auto_toc_prefix[_with_type] but " 
 587                         "the first entry \"%s\" does not contain a '%c'\n", 
 588                         s
, U_TREE_ENTRY_SEP_CHAR
); 
 589                 exit(U_INVALID_FORMAT_ERROR
); 
 591             prefixLength
=(int32_t)(prefixLimit
-s
); 
 592             if(prefixLength
==0 || prefixLength
>=UPRV_LENGTHOF(pkgPrefix
)) { 
 594                         "icupkg: --auto_toc_prefix[_with_type] but " 
 595                         "the prefix of the first entry \"%s\" is empty or too long\n", 
 597                 exit(U_INVALID_FORMAT_ERROR
); 
 599             if(prefixEndsWithType 
&& s
[prefixLength
-1]!=type
) { 
 601                         "icupkg: --auto_toc_prefix_with_type but " 
 602                         "the prefix of the first entry \"%s\" does not end with '%c'\n", 
 604                 exit(U_INVALID_FORMAT_ERROR
); 
 606             memcpy(pkgPrefix
, s
, prefixLength
); 
 607             pkgPrefix
[prefixLength
]=0; 
 608             memcpy(prefix
, s
, ++prefixLength
);  // include the / 
 610             // Use the package basename as prefix. 
 611             int32_t inPkgNameLength
=strlen(inPkgName
); 
 612             memcpy(prefix
, inPkgName
, inPkgNameLength
); 
 613             prefixLength
=inPkgNameLength
; 
 615             if( (int32_t)strlen(s
)>=(inPkgNameLength
+2) && 
 616                 0==memcmp(s
, inPkgName
, inPkgNameLength
) && 
 617                 s
[inPkgNameLength
]=='_' 
 619                 // old-style .dat package 
 620                 prefix
[prefixLength
++]='_'; 
 622                 // new-style .dat package 
 623                 prefix
[prefixLength
++]=U_TREE_ENTRY_SEP_CHAR
; 
 624                 // if it turns out to not contain U_TREE_ENTRY_SEP_CHAR 
 625                 // then the test in the loop below will fail 
 628         prefix
[prefixLength
]=0; 
 630         /* read the ToC table */ 
 631         for(i
=0; i
<itemCount
; ++i
) { 
 632             // skip the package part of the item name, error if it does not match the actual package name 
 633             // or if nothing follows the package name 
 634             offset
=(int32_t)ds
->readUInt32(inEntries
[i
].nameOffset
)-stringsOffset
; 
 635             s
=inItemStrings
+offset
; 
 636             if(0!=strncmp(s
, prefix
, prefixLength
) || s
[prefixLength
]==0) { 
 637                 fprintf(stderr
, "icupkg: input .dat item name \"%s\" does not start with \"%s\"\n", 
 639                 exit(U_INVALID_FORMAT_ERROR
); 
 641             items
[i
].name
=s
+prefixLength
; 
 643             // set the item's data 
 644             items
[i
].data
=(uint8_t *)inBytes
+ds
->readUInt32(inEntries
[i
].dataOffset
); 
 646                 items
[i
-1].length
=(int32_t)(items
[i
].data
-items
[i
-1].data
); 
 648                 // set the previous item's platform type 
 649                 typeEnum
=getTypeEnumForInputData(items
[i
-1].data
, items
[i
-1].length
, &errorCode
); 
 650                 if(typeEnum
<0 || U_FAILURE(errorCode
)) { 
 651                     fprintf(stderr
, "icupkg: not an ICU data file: item \"%s\" in \"%s\"\n", items
[i
-1].name
, filename
); 
 652                     exit(U_INVALID_FORMAT_ERROR
); 
 654                 items
[i
-1].type
=makeTypeLetter(typeEnum
); 
 656             items
[i
].isDataOwned
=FALSE
; 
 658         // set the last item's length 
 659         items
[itemCount
-1].length
=length
-ds
->readUInt32(inEntries
[itemCount
-1].dataOffset
); 
 661         // set the last item's platform type 
 662         typeEnum
=getTypeEnumForInputData(items
[itemCount
-1].data
, items
[itemCount
-1].length
, &errorCode
); 
 663         if(typeEnum
<0 || U_FAILURE(errorCode
)) { 
 664             fprintf(stderr
, "icupkg: not an ICU data file: item \"%s\" in \"%s\"\n", items
[i
-1].name
, filename
); 
 665             exit(U_INVALID_FORMAT_ERROR
); 
 667         items
[itemCount
-1].type
=makeTypeLetter(typeEnum
); 
 669         if(type
!=U_ICUDATA_TYPE_LETTER
[0]) { 
 670             // sort the item names for the local charset 
 675     udata_closeSwapper(ds
); 
 679 Package::getInType() { 
 680     return makeTypeLetter(inCharset
, inIsBigEndian
); 
 684 Package::writePackage(const char *filename
, char outType
, const char *comment
) { 
 685     char prefix
[MAX_PKG_NAME_LENGTH
+4]; 
 686     UDataOffsetTOCEntry entry
; 
 687     UDataSwapper 
*dsLocalToOut
, *ds
[TYPE_COUNT
]; 
 691     UErrorCode errorCode
; 
 692     int32_t i
, length
, prefixLength
, maxItemLength
, basenameOffset
, offset
, outInt32
; 
 694     UBool outIsBigEndian
; 
 696     extractPackageName(filename
, prefix
, MAX_PKG_NAME_LENGTH
); 
 698     // if there is an explicit comment, then use it, else use what's in the current header 
 700         /* get the header size minus the current comment */ 
 704         pHeader
=(DataHeader 
*)header
; 
 705         headerLength
=4+pHeader
->info
.size
; 
 706         length
=(int32_t)strlen(comment
); 
 707         if((int32_t)(headerLength
+length
)>=(int32_t)sizeof(header
)) { 
 708             fprintf(stderr
, "icupkg: comment too long\n"); 
 709             exit(U_BUFFER_OVERFLOW_ERROR
); 
 711         memcpy(header
+headerLength
, comment
, length
+1); 
 712         headerLength
+=length
; 
 713         if(headerLength
&0xf) { 
 714             /* NUL-pad the header to a multiple of 16 */ 
 715             length
=(headerLength
+0xf)&~0xf; 
 716             memset(header
+headerLength
, 0, length
-headerLength
); 
 719         pHeader
->dataHeader
.headerSize
=(uint16_t)headerLength
; 
 722     makeTypeProps(outType
, outCharset
, outIsBigEndian
); 
 724     // open (TYPE_COUNT-2) swappers 
 725     // one is a no-op for local type==outType 
 726     // one type (TYPE_LE) is bogus 
 727     errorCode
=U_ZERO_ERROR
; 
 728     i
=makeTypeEnum(outType
); 
 729     ds
[TYPE_B
]= i
==TYPE_B 
? NULL 
: udata_openSwapper(TRUE
, U_ASCII_FAMILY
, outIsBigEndian
, outCharset
, &errorCode
); 
 730     ds
[TYPE_L
]= i
==TYPE_L 
? NULL 
: udata_openSwapper(FALSE
, U_ASCII_FAMILY
, outIsBigEndian
, outCharset
, &errorCode
); 
 732     ds
[TYPE_E
]= i
==TYPE_E 
? NULL 
: udata_openSwapper(TRUE
, U_EBCDIC_FAMILY
, outIsBigEndian
, outCharset
, &errorCode
); 
 733     if(U_FAILURE(errorCode
)) { 
 734         fprintf(stderr
, "icupkg: udata_openSwapper() failed - %s\n", u_errorName(errorCode
)); 
 737     for(i
=0; i
<TYPE_COUNT
; ++i
) { 
 739             ds
[i
]->printError
=printPackageError
; 
 740             ds
[i
]->printErrorContext
=stderr
; 
 744     dsLocalToOut
=ds
[makeTypeEnum(U_CHARSET_FAMILY
, U_IS_BIG_ENDIAN
)]; 
 746     // create the file and write its contents 
 747     file
=fopen(filename
, "wb"); 
 749         fprintf(stderr
, "icupkg: unable to create file \"%s\"\n", filename
); 
 750         exit(U_FILE_ACCESS_ERROR
); 
 753     // swap and write the header 
 754     if(dsLocalToOut
!=NULL
) { 
 755         udata_swapDataHeader(dsLocalToOut
, header
, headerLength
, header
, &errorCode
); 
 756         if(U_FAILURE(errorCode
)) { 
 757             fprintf(stderr
, "icupkg: udata_swapDataHeader(local to out) failed - %s\n", u_errorName(errorCode
)); 
 761     length
=(int32_t)fwrite(header
, 1, headerLength
, file
); 
 762     if(length
!=headerLength
) { 
 763         fprintf(stderr
, "icupkg: unable to write complete header to file \"%s\"\n", filename
); 
 764         exit(U_FILE_ACCESS_ERROR
); 
 767     // prepare and swap the package name with a tree separator 
 768     // for prepending to item names 
 769     if(pkgPrefix
[0]==0) { 
 770         prefixLength
=(int32_t)strlen(prefix
); 
 772         prefixLength
=(int32_t)strlen(pkgPrefix
); 
 773         memcpy(prefix
, pkgPrefix
, prefixLength
); 
 774         if(prefixEndsWithType
) { 
 775             prefix
[prefixLength
-1]=outType
; 
 778     prefix
[prefixLength
++]=U_TREE_ENTRY_SEP_CHAR
; 
 779     prefix
[prefixLength
]=0; 
 780     if(dsLocalToOut
!=NULL
) { 
 781         dsLocalToOut
->swapInvChars(dsLocalToOut
, prefix
, prefixLength
, prefix
, &errorCode
); 
 782         if(U_FAILURE(errorCode
)) { 
 783             fprintf(stderr
, "icupkg: swapInvChars(output package name) failed - %s\n", u_errorName(errorCode
)); 
 787         // swap and sort the item names (sorting needs to be done in the output charset) 
 788         dsLocalToOut
->swapInvChars(dsLocalToOut
, inStrings
, inStringTop
, inStrings
, &errorCode
); 
 789         if(U_FAILURE(errorCode
)) { 
 790             fprintf(stderr
, "icupkg: swapInvChars(item names) failed - %s\n", u_errorName(errorCode
)); 
 796     // create the output item names in sorted order, with the package name prepended to each 
 797     for(i
=0; i
<itemCount
; ++i
) { 
 798         length
=(int32_t)strlen(items
[i
].name
); 
 799         name
=allocString(FALSE
, length
+prefixLength
); 
 800         memcpy(name
, prefix
, prefixLength
); 
 801         memcpy(name
+prefixLength
, items
[i
].name
, length
+1); 
 805     // calculate offsets for item names and items, pad to 16-align items 
 806     // align only the first item; each item's length is a multiple of 16 
 807     basenameOffset
=4+8*itemCount
; 
 808     offset
=basenameOffset
+outStringTop
; 
 809     if((length
=(offset
&15))!=0) { 
 811         memset(allocString(FALSE
, length
-1), 0xaa, length
); 
 815     // write the table of contents 
 816     // first the itemCount 
 818     if(dsLocalToOut
!=NULL
) { 
 819         dsLocalToOut
->swapArray32(dsLocalToOut
, &outInt32
, 4, &outInt32
, &errorCode
); 
 820         if(U_FAILURE(errorCode
)) { 
 821             fprintf(stderr
, "icupkg: swapArray32(item count) failed - %s\n", u_errorName(errorCode
)); 
 825     length
=(int32_t)fwrite(&outInt32
, 1, 4, file
); 
 827         fprintf(stderr
, "icupkg: unable to write complete item count to file \"%s\"\n", filename
); 
 828         exit(U_FILE_ACCESS_ERROR
); 
 831     // then write the item entries (and collect the maxItemLength) 
 833     for(i
=0; i
<itemCount
; ++i
) { 
 834         entry
.nameOffset
=(uint32_t)(basenameOffset
+(items
[i
].name
-outStrings
)); 
 835         entry
.dataOffset
=(uint32_t)offset
; 
 836         if(dsLocalToOut
!=NULL
) { 
 837             dsLocalToOut
->swapArray32(dsLocalToOut
, &entry
, 8, &entry
, &errorCode
); 
 838             if(U_FAILURE(errorCode
)) { 
 839                 fprintf(stderr
, "icupkg: swapArray32(item entry %ld) failed - %s\n", (long)i
, u_errorName(errorCode
)); 
 843         length
=(int32_t)fwrite(&entry
, 1, 8, file
); 
 845             fprintf(stderr
, "icupkg: unable to write complete item entry %ld to file \"%s\"\n", (long)i
, filename
); 
 846             exit(U_FILE_ACCESS_ERROR
); 
 849         length
=items
[i
].length
; 
 850         if(length
>maxItemLength
) { 
 851             maxItemLength
=length
; 
 856     // write the item names 
 857     length
=(int32_t)fwrite(outStrings
, 1, outStringTop
, file
); 
 858     if(length
!=outStringTop
) { 
 859         fprintf(stderr
, "icupkg: unable to write complete item names to file \"%s\"\n", filename
); 
 860         exit(U_FILE_ACCESS_ERROR
); 
 864     for(pItem
=items
, i
=0; i
<itemCount
; ++pItem
, ++i
) { 
 865         int32_t type
=makeTypeEnum(pItem
->type
); 
 867             // swap each item from its platform properties to the desired ones 
 870                 pItem
->data
, pItem
->length
, pItem
->data
, 
 872             if(U_FAILURE(errorCode
)) { 
 873                 fprintf(stderr
, "icupkg: udata_swap(item %ld) failed - %s\n", (long)i
, u_errorName(errorCode
)); 
 877         length
=(int32_t)fwrite(pItem
->data
, 1, pItem
->length
, file
); 
 878         if(length
!=pItem
->length
) { 
 879             fprintf(stderr
, "icupkg: unable to write complete item %ld to file \"%s\"\n", (long)i
, filename
); 
 880             exit(U_FILE_ACCESS_ERROR
); 
 885         fprintf(stderr
, "icupkg: unable to write complete file \"%s\"\n", filename
); 
 886         exit(U_FILE_ACCESS_ERROR
); 
 890     for(i
=0; i
<TYPE_COUNT
; ++i
) { 
 891         udata_closeSwapper(ds
[i
]); 
 896 Package::findItem(const char *name
, int32_t length
) const { 
 897     int32_t i
, start
, limit
; 
 900     /* do a binary search for the string */ 
 906             result
=strncmp(name
, items
[i
].name
, length
); 
 908             result
=strcmp(name
, items
[i
].name
); 
 915                  * if we compared just prefixes, then we may need to back up 
 916                  * to the first item with this prefix 
 918                 while(i
>0 && 0==strncmp(name
, items
[i
-1].name
, length
)) { 
 923         } else if(result
<0) { 
 925         } else /* result>0 */ { 
 930     return ~start
; /* not found, return binary-not of the insertion point */ 
 934 Package::findItems(const char *pattern
) { 
 937     if(pattern
==NULL 
|| *pattern
==0) { 
 946     wild
=strchr(pattern
, '*'); 
 949         findPrefixLength
=(int32_t)strlen(pattern
); 
 952         findPrefixLength
=(int32_t)(wild
-pattern
); 
 954         findSuffixLength
=(int32_t)strlen(findSuffix
); 
 955         if(NULL
!=strchr(findSuffix
, '*')) { 
 956             // two or more wildcards 
 957             fprintf(stderr
, "icupkg: syntax error (more than one '*') in item pattern \"%s\"\n", pattern
); 
 962     if(findPrefixLength
==0) { 
 965         findNextIndex
=findItem(findPrefix
, findPrefixLength
); 
 970 Package::findNextItem() { 
 971     const char *name
, *middle
, *treeSep
; 
 972     int32_t idx
, nameLength
, middleLength
; 
 974     if(findNextIndex
<0) { 
 978     while(findNextIndex
<itemCount
) { 
 980         name
=items
[idx
].name
; 
 981         nameLength
=(int32_t)strlen(name
); 
 982         if(nameLength
<(findPrefixLength
+findSuffixLength
)) { 
 983             // item name too short for prefix & suffix 
 986         if(findPrefixLength
>0 && 0!=memcmp(findPrefix
, name
, findPrefixLength
)) { 
 987             // left the range of names with this prefix 
 990         middle
=name
+findPrefixLength
; 
 991         middleLength
=nameLength
-findPrefixLength
-findSuffixLength
; 
 992         if(findSuffixLength
>0 && 0!=memcmp(findSuffix
, name
+(nameLength
-findSuffixLength
), findSuffixLength
)) { 
 993             // suffix does not match 
 996         // prefix & suffix match 
 998         if(matchMode
&MATCH_NOSLASH
) { 
 999             treeSep
=strchr(middle
, U_TREE_ENTRY_SEP_CHAR
); 
1000             if(treeSep
!=NULL 
&& (treeSep
-middle
)<middleLength
) { 
1001                 // the middle (matching the * wildcard) contains a tree separator / 
1006         // found a matching item 
1016 Package::setMatchMode(uint32_t mode
) { 
1021 Package::addItem(const char *name
) { 
1022     addItem(name
, NULL
, 0, FALSE
, U_ICUDATA_TYPE_LETTER
[0]); 
1026 Package::addItem(const char *name
, uint8_t *data
, int32_t length
, UBool isDataOwned
, char type
) { 
1031         // new item, make space at the insertion point 
1032         ensureItemCapacity(); 
1033         // move the following items down 
1036             memmove(items
+idx
+1, items
+idx
, (itemCount
-idx
)*sizeof(Item
)); 
1040         // reset this Item entry 
1041         memset(items
+idx
, 0, sizeof(Item
)); 
1043         // copy the item's name 
1044         items
[idx
].name
=allocString(TRUE
, strlen(name
)); 
1045         strcpy(items
[idx
].name
, name
); 
1046         pathToTree(items
[idx
].name
); 
1048         // same-name item found, replace it 
1049         if(items
[idx
].isDataOwned
) { 
1050             uprv_free(items
[idx
].data
); 
1053         // keep the item's name since it is the same 
1056     // set the item's data 
1057     items
[idx
].data
=data
; 
1058     items
[idx
].length
=length
; 
1059     items
[idx
].isDataOwned
=isDataOwned
; 
1060     items
[idx
].type
=type
; 
1064 Package::addFile(const char *filesPath
, const char *name
) { 
1069     data
=readFile(filesPath
, name
, length
, type
); 
1070     // readFile() exits the tool if it fails 
1071     addItem(name
, data
, length
, TRUE
, type
); 
1075 Package::addItems(const Package 
&listPkg
) { 
1079     for(pItem
=listPkg
.items
, i
=0; i
<listPkg
.itemCount
; ++pItem
, ++i
) { 
1080         addItem(pItem
->name
, pItem
->data
, pItem
->length
, FALSE
, pItem
->type
); 
1085 Package::removeItem(int32_t idx
) { 
1088         if(items
[idx
].isDataOwned
) { 
1089             uprv_free(items
[idx
].data
); 
1092         // move the following items up 
1093         if((idx
+1)<itemCount
) { 
1094             memmove(items
+idx
, items
+idx
+1, (itemCount
-(idx
+1))*sizeof(Item
)); 
1098         if(idx
<=findNextIndex
) { 
1105 Package::removeItems(const char *pattern
) { 
1109     while((idx
=findNextItem())>=0) { 
1115 Package::removeItems(const Package 
&listPkg
) { 
1119     for(pItem
=listPkg
.items
, i
=0; i
<listPkg
.itemCount
; ++pItem
, ++i
) { 
1120         removeItems(pItem
->name
); 
1125 Package::extractItem(const char *filesPath
, const char *outName
, int32_t idx
, char outType
) { 
1126     char filename
[1024]; 
1131     uint8_t itemCharset
, outCharset
; 
1132     UBool itemIsBigEndian
, outIsBigEndian
; 
1134     if(idx
<0 || itemCount
<=idx
) { 
1139     // swap the data to the outType 
1140     // outType==0: don't swap 
1141     if(outType
!=0 && pItem
->type
!=outType
) { 
1143         UErrorCode errorCode
=U_ZERO_ERROR
; 
1144         makeTypeProps(pItem
->type
, itemCharset
, itemIsBigEndian
); 
1145         makeTypeProps(outType
, outCharset
, outIsBigEndian
); 
1146         ds
=udata_openSwapper(itemIsBigEndian
, itemCharset
, outIsBigEndian
, outCharset
, &errorCode
); 
1147         if(U_FAILURE(errorCode
)) { 
1148             fprintf(stderr
, "icupkg: udata_openSwapper(item %ld) failed - %s\n", 
1149                     (long)idx
, u_errorName(errorCode
)); 
1153         ds
->printError
=printPackageError
; 
1154         ds
->printErrorContext
=stderr
; 
1156         // swap the item from its platform properties to the desired ones 
1157         udata_swap(ds
, pItem
->data
, pItem
->length
, pItem
->data
, &errorCode
); 
1158         if(U_FAILURE(errorCode
)) { 
1159             fprintf(stderr
, "icupkg: udata_swap(item %ld) failed - %s\n", (long)idx
, u_errorName(errorCode
)); 
1162         udata_closeSwapper(ds
); 
1163         pItem
->type
=outType
; 
1166     // create the file and write its contents 
1167     makeFullFilenameAndDirs(filesPath
, outName
, filename
, (int32_t)sizeof(filename
)); 
1168     file
=fopen(filename
, "wb"); 
1170         fprintf(stderr
, "icupkg: unable to create file \"%s\"\n", filename
); 
1171         exit(U_FILE_ACCESS_ERROR
); 
1173     fileLength
=(int32_t)fwrite(pItem
->data
, 1, pItem
->length
, file
); 
1175     if(ferror(file
) || fileLength
!=pItem
->length
) { 
1176         fprintf(stderr
, "icupkg: unable to write complete file \"%s\"\n", filename
); 
1177         exit(U_FILE_ACCESS_ERROR
); 
1183 Package::extractItem(const char *filesPath
, int32_t idx
, char outType
) { 
1184     extractItem(filesPath
, items
[idx
].name
, idx
, outType
); 
1188 Package::extractItems(const char *filesPath
, const char *pattern
, char outType
) { 
1192     while((idx
=findNextItem())>=0) { 
1193         extractItem(filesPath
, idx
, outType
); 
1198 Package::extractItems(const char *filesPath
, const Package 
&listPkg
, char outType
) { 
1202     for(pItem
=listPkg
.items
, i
=0; i
<listPkg
.itemCount
; ++pItem
, ++i
) { 
1203         extractItems(filesPath
, pItem
->name
, outType
); 
1208 Package::getItemCount() const { 
1213 Package::getItem(int32_t idx
) const { 
1214     if (0 <= idx 
&& idx 
< itemCount
) { 
1221 Package::checkDependency(void *context
, const char *itemName
, const char *targetName
) { 
1222     // check dependency: make sure the target item is in the package 
1223     Package 
*me
=(Package 
*)context
; 
1224     if(me
->findItem(targetName
)<0) { 
1225         me
->isMissingItems
=TRUE
; 
1226         fprintf(stderr
, "Item %s depends on missing item %s\n", itemName
, targetName
); 
1231 Package::checkDependencies() { 
1232     isMissingItems
=FALSE
; 
1233     enumDependencies(this, checkDependency
); 
1234     return (UBool
)!isMissingItems
; 
1238 Package::enumDependencies(void *context
, CheckDependency check
) { 
1241     for(i
=0; i
<itemCount
; ++i
) { 
1242         enumDependencies(items
+i
, context
, check
); 
1247 Package::allocString(UBool in
, int32_t length
) { 
1260     if(top
>STRING_STORE_SIZE
) { 
1261         fprintf(stderr
, "icupkg: string storage overflow\n"); 
1262         exit(U_BUFFER_OVERFLOW_ERROR
); 
1273 Package::sortItems() { 
1274     UErrorCode errorCode
=U_ZERO_ERROR
; 
1275     uprv_sortArray(items
, itemCount
, (int32_t)sizeof(Item
), compareItems
, NULL
, FALSE
, &errorCode
); 
1276     if(U_FAILURE(errorCode
)) { 
1277         fprintf(stderr
, "icupkg: sorting item names failed - %s\n", u_errorName(errorCode
)); 
1282 void Package::setItemCapacity(int32_t max
)  
1287   Item 
*newItems 
= (Item
*)uprv_malloc(max 
* sizeof(items
[0])); 
1288   Item 
*oldItems 
= items
; 
1289   if(newItems 
== NULL
) { 
1290     fprintf(stderr
, "icupkg: Out of memory trying to allocate %lu bytes for %d items\n",  
1291         (unsigned long)max
*sizeof(items
[0]), max
); 
1292     exit(U_MEMORY_ALLOCATION_ERROR
); 
1294   if(items 
&& itemCount
>0) { 
1295     uprv_memcpy(newItems
, items
, itemCount
*sizeof(items
[0])); 
1299   uprv_free(oldItems
); 
1302 void Package::ensureItemCapacity() 
1304   if((itemCount
+1)>itemMax
) { 
1305     setItemCapacity(itemCount
+kItemsChunk
);