Wed, 31 Dec 2014 07:22:50 +0100
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 |