intl/icu/source/tools/toolutil/package.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

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

mercurial