Index: icu46/source/tools/toolutil/package.cpp |
=================================================================== |
--- icu46/source/tools/toolutil/package.cpp (revision 0) |
+++ icu46/source/tools/toolutil/package.cpp (revision 0) |
@@ -0,0 +1,1256 @@ |
+/* |
+******************************************************************************* |
+* |
+* Copyright (C) 1999-2010, International Business Machines |
+* Corporation and others. All Rights Reserved. |
+* |
+******************************************************************************* |
+* file name: package.cpp |
+* encoding: US-ASCII |
+* tab size: 8 (not used) |
+* indentation:4 |
+* |
+* created on: 2005aug25 |
+* created by: Markus W. Scherer |
+* |
+* Read, modify, and write ICU .dat data package files. |
+* This is an integral part of the icupkg tool, moved to the toolutil library |
+* because parts of tool implementations tend to be later shared by |
+* other tools. |
+* Subsumes functionality and implementation code from |
+* gencmn, decmn, and icuswap tools. |
+*/ |
+ |
+#include "unicode/utypes.h" |
+#include "unicode/putil.h" |
+#include "unicode/udata.h" |
+#include "cstring.h" |
+#include "uarrsort.h" |
+#include "ucmndata.h" |
+#include "udataswp.h" |
+#include "swapimpl.h" |
+#include "toolutil.h" |
+#include "package.h" |
+#include "cmemory.h" |
+ |
+#include <stdio.h> |
+#include <stdlib.h> |
+#include <string.h> |
+ |
+ |
+static const int32_t kItemsChunk = 256; /* How much to increase the filesarray by each time */ |
+ |
+// general definitions ----------------------------------------------------- *** |
+ |
+#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) |
+ |
+/* UDataInfo cf. udata.h */ |
+static const UDataInfo dataInfo={ |
+ (uint16_t)sizeof(UDataInfo), |
+ 0, |
+ |
+ U_IS_BIG_ENDIAN, |
+ U_CHARSET_FAMILY, |
+ (uint8_t)sizeof(UChar), |
+ 0, |
+ |
+ {0x43, 0x6d, 0x6e, 0x44}, /* dataFormat="CmnD" */ |
+ {1, 0, 0, 0}, /* formatVersion */ |
+ {3, 0, 0, 0} /* dataVersion */ |
+}; |
+ |
+U_CDECL_BEGIN |
+static void U_CALLCONV |
+printPackageError(void *context, const char *fmt, va_list args) { |
+ vfprintf((FILE *)context, fmt, args); |
+} |
+U_CDECL_END |
+ |
+static uint16_t |
+readSwapUInt16(uint16_t x) { |
+ return (uint16_t)((x<<8)|(x>>8)); |
+} |
+ |
+// platform types ---------------------------------------------------------- *** |
+ |
+static const char *types="lb?e"; |
+ |
+enum { TYPE_L, TYPE_B, TYPE_LE, TYPE_E, TYPE_COUNT }; |
+ |
+static inline int32_t |
+makeTypeEnum(uint8_t charset, UBool isBigEndian) { |
+ return 2*(int32_t)charset+isBigEndian; |
+} |
+ |
+static inline int32_t |
+makeTypeEnum(char type) { |
+ return |
+ type == 'l' ? TYPE_L : |
+ type == 'b' ? TYPE_B : |
+ type == 'e' ? TYPE_E : |
+ -1; |
+} |
+ |
+static inline char |
+makeTypeLetter(uint8_t charset, UBool isBigEndian) { |
+ return types[makeTypeEnum(charset, isBigEndian)]; |
+} |
+ |
+static inline char |
+makeTypeLetter(int32_t typeEnum) { |
+ return types[typeEnum]; |
+} |
+ |
+static void |
+makeTypeProps(char type, uint8_t &charset, UBool &isBigEndian) { |
+ int32_t typeEnum=makeTypeEnum(type); |
+ charset=(uint8_t)(typeEnum>>1); |
+ isBigEndian=(UBool)(typeEnum&1); |
+} |
+ |
+U_CFUNC const UDataInfo * |
+getDataInfo(const uint8_t *data, int32_t length, |
+ int32_t &infoLength, int32_t &headerLength, |
+ UErrorCode *pErrorCode) { |
+ const DataHeader *pHeader; |
+ const UDataInfo *pInfo; |
+ |
+ if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { |
+ return NULL; |
+ } |
+ if( data==NULL || |
+ (length>=0 && length<(int32_t)sizeof(DataHeader)) |
+ ) { |
+ *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; |
+ return NULL; |
+ } |
+ |
+ pHeader=(const DataHeader *)data; |
+ pInfo=&pHeader->info; |
+ if( (length>=0 && length<(int32_t)sizeof(DataHeader)) || |
+ pHeader->dataHeader.magic1!=0xda || |
+ pHeader->dataHeader.magic2!=0x27 || |
+ pInfo->sizeofUChar!=2 |
+ ) { |
+ *pErrorCode=U_UNSUPPORTED_ERROR; |
+ return NULL; |
+ } |
+ |
+ if(pInfo->isBigEndian==U_IS_BIG_ENDIAN) { |
+ headerLength=pHeader->dataHeader.headerSize; |
+ infoLength=pInfo->size; |
+ } else { |
+ headerLength=readSwapUInt16(pHeader->dataHeader.headerSize); |
+ infoLength=readSwapUInt16(pInfo->size); |
+ } |
+ |
+ if( headerLength<(int32_t)sizeof(DataHeader) || |
+ infoLength<(int32_t)sizeof(UDataInfo) || |
+ headerLength<(int32_t)(sizeof(pHeader->dataHeader)+infoLength) || |
+ (length>=0 && length<headerLength) |
+ ) { |
+ *pErrorCode=U_UNSUPPORTED_ERROR; |
+ return NULL; |
+ } |
+ |
+ return pInfo; |
+} |
+ |
+static int32_t |
+getTypeEnumForInputData(const uint8_t *data, int32_t length, |
+ UErrorCode *pErrorCode) { |
+ const UDataInfo *pInfo; |
+ int32_t infoLength, headerLength; |
+ |
+ /* getDataInfo() checks for illegal arguments */ |
+ pInfo=getDataInfo(data, length, infoLength, headerLength, pErrorCode); |
+ if(pInfo==NULL) { |
+ return -1; |
+ } |
+ |
+ return makeTypeEnum(pInfo->charsetFamily, (UBool)pInfo->isBigEndian); |
+} |
+ |
+// file handling ----------------------------------------------------------- *** |
+ |
+static void |
+extractPackageName(const char *filename, |
+ char pkg[], int32_t capacity) { |
+ const char *basename; |
+ int32_t len; |
+ |
+ basename=findBasename(filename); |
+ len=(int32_t)strlen(basename)-4; /* -4: subtract the length of ".dat" */ |
+ |
+ if(len<=0 || 0!=strcmp(basename+len, ".dat")) { |
+ fprintf(stderr, "icupkg: \"%s\" is not recognized as a package filename (must end with .dat)\n", |
+ basename); |
+ exit(U_ILLEGAL_ARGUMENT_ERROR); |
+ } |
+ |
+ if(len>=capacity) { |
+ fprintf(stderr, "icupkg: the package name \"%s\" is too long (>=%ld)\n", |
+ basename, (long)capacity); |
+ exit(U_ILLEGAL_ARGUMENT_ERROR); |
+ } |
+ |
+ memcpy(pkg, basename, len); |
+ pkg[len]=0; |
+} |
+ |
+static int32_t |
+getFileLength(FILE *f) { |
+ int32_t length; |
+ |
+ fseek(f, 0, SEEK_END); |
+ length=(int32_t)ftell(f); |
+ fseek(f, 0, SEEK_SET); |
+ return length; |
+} |
+ |
+/* |
+ * Turn tree separators and alternate file separators into normal file separators. |
+ */ |
+#if U_TREE_ENTRY_SEP_CHAR==U_FILE_SEP_CHAR && U_FILE_ALT_SEP_CHAR==U_FILE_SEP_CHAR |
+#define treeToPath(s) |
+#else |
+static void |
+treeToPath(char *s) { |
+ char *t; |
+ |
+ for(t=s; *t!=0; ++t) { |
+ if(*t==U_TREE_ENTRY_SEP_CHAR || *t==U_FILE_ALT_SEP_CHAR) { |
+ *t=U_FILE_SEP_CHAR; |
+ } |
+ } |
+} |
+#endif |
+ |
+/* |
+ * Turn file separators into tree separators. |
+ */ |
+#if U_TREE_ENTRY_SEP_CHAR==U_FILE_SEP_CHAR && U_FILE_ALT_SEP_CHAR==U_FILE_SEP_CHAR |
+#define pathToTree(s) |
+#else |
+static void |
+pathToTree(char *s) { |
+ char *t; |
+ |
+ for(t=s; *t!=0; ++t) { |
+ if(*t==U_FILE_SEP_CHAR || *t==U_FILE_ALT_SEP_CHAR) { |
+ *t=U_TREE_ENTRY_SEP_CHAR; |
+ } |
+ } |
+} |
+#endif |
+ |
+/* |
+ * Prepend the path (if any) to the name and run the name through treeToName(). |
+ */ |
+static void |
+makeFullFilename(const char *path, const char *name, |
+ char *filename, int32_t capacity) { |
+ char *s; |
+ |
+ // prepend the path unless NULL or empty |
+ if(path!=NULL && path[0]!=0) { |
+ if((int32_t)(strlen(path)+1)>=capacity) { |
+ fprintf(stderr, "pathname too long: \"%s\"\n", path); |
+ exit(U_BUFFER_OVERFLOW_ERROR); |
+ } |
+ strcpy(filename, path); |
+ |
+ // make sure the path ends with a file separator |
+ s=strchr(filename, 0); |
+ if(*(s-1)!=U_FILE_SEP_CHAR && *(s-1)!=U_FILE_ALT_SEP_CHAR) { |
+ *s++=U_FILE_SEP_CHAR; |
+ } |
+ } else { |
+ s=filename; |
+ } |
+ |
+ // turn the name into a filename, turn tree separators into file separators |
+ if((int32_t)((s-filename)+strlen(name))>=capacity) { |
+ fprintf(stderr, "path/filename too long: \"%s%s\"\n", filename, name); |
+ exit(U_BUFFER_OVERFLOW_ERROR); |
+ } |
+ strcpy(s, name); |
+ treeToPath(s); |
+} |
+ |
+static void |
+makeFullFilenameAndDirs(const char *path, const char *name, |
+ char *filename, int32_t capacity) { |
+ char *sep; |
+ UErrorCode errorCode; |
+ |
+ makeFullFilename(path, name, filename, capacity); |
+ |
+ // make tree directories |
+ errorCode=U_ZERO_ERROR; |
+ sep=strchr(filename, 0)-strlen(name); |
+ while((sep=strchr(sep, U_FILE_SEP_CHAR))!=NULL) { |
+ if(sep!=filename) { |
+ *sep=0; // truncate temporarily |
+ uprv_mkdir(filename, &errorCode); |
+ if(U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: unable to create tree directory \"%s\"\n", filename); |
+ exit(U_FILE_ACCESS_ERROR); |
+ } |
+ } |
+ *sep++=U_FILE_SEP_CHAR; // restore file separator character |
+ } |
+} |
+ |
+static uint8_t * |
+readFile(const char *path, const char *name, int32_t &length, char &type) { |
+ char filename[1024]; |
+ FILE *file; |
+ uint8_t *data; |
+ UErrorCode errorCode; |
+ int32_t fileLength, typeEnum; |
+ |
+ makeFullFilename(path, name, filename, (int32_t)sizeof(filename)); |
+ |
+ /* open the input file, get its length, allocate memory for it, read the file */ |
+ file=fopen(filename, "rb"); |
+ if(file==NULL) { |
+ fprintf(stderr, "icupkg: unable to open input file \"%s\"\n", filename); |
+ exit(U_FILE_ACCESS_ERROR); |
+ } |
+ |
+ /* get the file length */ |
+ fileLength=getFileLength(file); |
+ if(ferror(file) || fileLength<=0) { |
+ fprintf(stderr, "icupkg: empty input file \"%s\"\n", filename); |
+ fclose(file); |
+ exit(U_FILE_ACCESS_ERROR); |
+ } |
+ |
+ /* allocate the buffer, pad to multiple of 16 */ |
+ length=(fileLength+0xf)&~0xf; |
+ data=(uint8_t *)malloc(length); |
+ if(data==NULL) { |
+ fclose(file); |
+ exit(U_MEMORY_ALLOCATION_ERROR); |
+ } |
+ |
+ /* read the file */ |
+ if(fileLength!=(int32_t)fread(data, 1, fileLength, file)) { |
+ fprintf(stderr, "icupkg: error reading \"%s\"\n", filename); |
+ fclose(file); |
+ free(data); |
+ exit(U_FILE_ACCESS_ERROR); |
+ } |
+ |
+ /* pad the file to a multiple of 16 using the usual padding byte */ |
+ if(fileLength<length) { |
+ memset(data+fileLength, 0xaa, length-fileLength); |
+ } |
+ |
+ fclose(file); |
+ |
+ // minimum check for ICU-format data |
+ errorCode=U_ZERO_ERROR; |
+ typeEnum=getTypeEnumForInputData(data, length, &errorCode); |
+ if(typeEnum<0 || U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: not an ICU data file: \"%s\"\n", filename); |
+ free(data); |
+#if !UCONFIG_NO_LEGACY_CONVERSION |
+ exit(U_INVALID_FORMAT_ERROR); |
+#else |
+ fprintf(stderr, "U_INVALID_FORMAT_ERROR occurred but UCONFIG_NO_LEGACY_CONVERSION is on so this is expected.\n"); |
+ exit(0); |
+#endif |
+ } |
+ type=makeTypeLetter(typeEnum); |
+ |
+ return data; |
+} |
+ |
+// .dat package file representation ---------------------------------------- *** |
+ |
+U_CDECL_BEGIN |
+ |
+static int32_t U_CALLCONV |
+compareItems(const void * /*context*/, const void *left, const void *right) { |
+ U_NAMESPACE_USE |
+ |
+ return (int32_t)strcmp(((Item *)left)->name, ((Item *)right)->name); |
+} |
+ |
+U_CDECL_END |
+ |
+U_NAMESPACE_BEGIN |
+ |
+Package::Package() { |
+ inPkgName[0]=0; |
+ inData=NULL; |
+ inLength=0; |
+ inCharset=U_CHARSET_FAMILY; |
+ inIsBigEndian=U_IS_BIG_ENDIAN; |
+ |
+ itemCount=0; |
+ itemMax=0; |
+ items=NULL; |
+ |
+ inStringTop=outStringTop=0; |
+ |
+ matchMode=0; |
+ findPrefix=findSuffix=NULL; |
+ findPrefixLength=findSuffixLength=0; |
+ findNextIndex=-1; |
+ |
+ // create a header for an empty package |
+ DataHeader *pHeader; |
+ pHeader=(DataHeader *)header; |
+ pHeader->dataHeader.magic1=0xda; |
+ pHeader->dataHeader.magic2=0x27; |
+ memcpy(&pHeader->info, &dataInfo, sizeof(dataInfo)); |
+ headerLength=(int32_t)(4+sizeof(dataInfo)); |
+ if(headerLength&0xf) { |
+ /* NUL-pad the header to a multiple of 16 */ |
+ int32_t length=(headerLength+0xf)&~0xf; |
+ memset(header+headerLength, 0, length-headerLength); |
+ headerLength=length; |
+ } |
+ pHeader->dataHeader.headerSize=(uint16_t)headerLength; |
+} |
+ |
+Package::~Package() { |
+ int32_t idx; |
+ |
+ free(inData); |
+ |
+ for(idx=0; idx<itemCount; ++idx) { |
+ if(items[idx].isDataOwned) { |
+ free(items[idx].data); |
+ } |
+ } |
+ |
+ uprv_free((void*)items); |
+} |
+ |
+void |
+Package::readPackage(const char *filename) { |
+ UDataSwapper *ds; |
+ const UDataInfo *pInfo; |
+ UErrorCode errorCode; |
+ |
+ const uint8_t *inBytes; |
+ |
+ int32_t length, offset, i; |
+ int32_t itemLength, typeEnum; |
+ char type; |
+ |
+ const UDataOffsetTOCEntry *inEntries; |
+ |
+ extractPackageName(filename, inPkgName, (int32_t)sizeof(inPkgName)); |
+ |
+ /* read the file */ |
+ inData=readFile(NULL, filename, inLength, type); |
+ length=inLength; |
+ |
+ /* |
+ * swap the header - even if the swapping itself is a no-op |
+ * because it tells us the header length |
+ */ |
+ errorCode=U_ZERO_ERROR; |
+ makeTypeProps(type, inCharset, inIsBigEndian); |
+ ds=udata_openSwapper(inIsBigEndian, inCharset, U_IS_BIG_ENDIAN, U_CHARSET_FAMILY, &errorCode); |
+ if(U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: udata_openSwapper(\"%s\") failed - %s\n", |
+ filename, u_errorName(errorCode)); |
+ exit(errorCode); |
+ } |
+ |
+ ds->printError=printPackageError; |
+ ds->printErrorContext=stderr; |
+ |
+ headerLength=sizeof(header); |
+ if(length<headerLength) { |
+ headerLength=length; |
+ } |
+ headerLength=udata_swapDataHeader(ds, inData, headerLength, header, &errorCode); |
+ if(U_FAILURE(errorCode)) { |
+ exit(errorCode); |
+ } |
+ |
+ /* check data format and format version */ |
+ pInfo=(const UDataInfo *)((const char *)inData+4); |
+ if(!( |
+ pInfo->dataFormat[0]==0x43 && /* dataFormat="CmnD" */ |
+ pInfo->dataFormat[1]==0x6d && |
+ pInfo->dataFormat[2]==0x6e && |
+ pInfo->dataFormat[3]==0x44 && |
+ pInfo->formatVersion[0]==1 |
+ )) { |
+ fprintf(stderr, "icupkg: data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as an ICU .dat package\n", |
+ pInfo->dataFormat[0], pInfo->dataFormat[1], |
+ pInfo->dataFormat[2], pInfo->dataFormat[3], |
+ pInfo->formatVersion[0]); |
+ exit(U_UNSUPPORTED_ERROR); |
+ } |
+ inIsBigEndian=(UBool)pInfo->isBigEndian; |
+ inCharset=pInfo->charsetFamily; |
+ |
+ inBytes=(const uint8_t *)inData+headerLength; |
+ inEntries=(const UDataOffsetTOCEntry *)(inBytes+4); |
+ |
+ /* check that the itemCount fits, then the ToC table, then at least the header of the last item */ |
+ length-=headerLength; |
+ if(length<4) { |
+ /* itemCount does not fit */ |
+ offset=0x7fffffff; |
+ } else { |
+ itemCount=udata_readInt32(ds, *(const int32_t *)inBytes); |
+ setItemCapacity(itemCount); /* resize so there's space */ |
+ if(itemCount==0) { |
+ offset=4; |
+ } else if(length<(4+8*itemCount)) { |
+ /* ToC table does not fit */ |
+ offset=0x7fffffff; |
+ } else { |
+ /* offset of the last item plus at least 20 bytes for its header */ |
+ offset=20+(int32_t)ds->readUInt32(inEntries[itemCount-1].dataOffset); |
+ } |
+ } |
+ if(length<offset) { |
+ fprintf(stderr, "icupkg: too few bytes (%ld after header) for a .dat package\n", |
+ (long)length); |
+ exit(U_INDEX_OUTOFBOUNDS_ERROR); |
+ } |
+ /* do not modify the package length variable until the last item's length is set */ |
+ |
+ if(itemCount>0) { |
+ char prefix[MAX_PKG_NAME_LENGTH+4]; |
+ char *s, *inItemStrings; |
+ int32_t inPkgNameLength, prefixLength, stringsOffset; |
+ |
+ if(itemCount>itemMax) { |
+ fprintf(stderr, "icupkg: too many items, maximum is %d\n", itemMax); |
+ exit(U_BUFFER_OVERFLOW_ERROR); |
+ } |
+ |
+ /* swap the item name strings */ |
+ stringsOffset=4+8*itemCount; |
+ itemLength=(int32_t)(ds->readUInt32(inEntries[0].dataOffset))-stringsOffset; |
+ |
+ // don't include padding bytes at the end of the item names |
+ while(itemLength>0 && inBytes[stringsOffset+itemLength-1]!=0) { |
+ --itemLength; |
+ } |
+ |
+ if((inStringTop+itemLength)>STRING_STORE_SIZE) { |
+ fprintf(stderr, "icupkg: total length of item name strings too long\n"); |
+ exit(U_BUFFER_OVERFLOW_ERROR); |
+ } |
+ |
+ inItemStrings=inStrings+inStringTop; |
+ ds->swapInvChars(ds, inBytes+stringsOffset, itemLength, inItemStrings, &errorCode); |
+ if(U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg failed to swap the input .dat package item name strings\n"); |
+ exit(U_INVALID_FORMAT_ERROR); |
+ } |
+ inStringTop+=itemLength; |
+ |
+ // reset the Item entries |
+ memset(items, 0, itemCount*sizeof(Item)); |
+ |
+ inPkgNameLength=strlen(inPkgName); |
+ memcpy(prefix, inPkgName, inPkgNameLength); |
+ prefixLength=inPkgNameLength; |
+ |
+ /* |
+ * Get the common prefix of the items. |
+ * New-style ICU .dat packages use tree separators ('/') between package names, |
+ * tree names, and item names, |
+ * while old-style ICU .dat packages (before multi-tree support) |
+ * use an underscore ('_') between package and item names. |
+ */ |
+ offset=(int32_t)ds->readUInt32(inEntries[0].nameOffset)-stringsOffset; |
+ s=inItemStrings+offset; |
+ if( (int32_t)strlen(s)>=(inPkgNameLength+2) && |
+ 0==memcmp(s, inPkgName, inPkgNameLength) && |
+ s[inPkgNameLength]=='_' |
+ ) { |
+ // old-style .dat package |
+ prefix[prefixLength++]='_'; |
+ } else { |
+ // new-style .dat package |
+ prefix[prefixLength++]=U_TREE_ENTRY_SEP_CHAR; |
+ // if it turns out to not contain U_TREE_ENTRY_SEP_CHAR |
+ // then the test in the loop below will fail |
+ } |
+ prefix[prefixLength]=0; |
+ |
+ /* read the ToC table */ |
+ for(i=0; i<itemCount; ++i) { |
+ // skip the package part of the item name, error if it does not match the actual package name |
+ // or if nothing follows the package name |
+ offset=(int32_t)ds->readUInt32(inEntries[i].nameOffset)-stringsOffset; |
+ s=inItemStrings+offset; |
+ if(0!=strncmp(s, prefix, prefixLength) || s[prefixLength]==0) { |
+ fprintf(stderr, "icupkg: input .dat item name \"%s\" does not start with \"%s\"\n", |
+ s, prefix); |
+ exit(U_UNSUPPORTED_ERROR); |
+ } |
+ items[i].name=s+prefixLength; |
+ |
+ // set the item's data |
+ items[i].data=(uint8_t *)inBytes+ds->readUInt32(inEntries[i].dataOffset); |
+ if(i>0) { |
+ items[i-1].length=(int32_t)(items[i].data-items[i-1].data); |
+ |
+ // set the previous item's platform type |
+ typeEnum=getTypeEnumForInputData(items[i-1].data, items[i-1].length, &errorCode); |
+ if(typeEnum<0 || U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: not an ICU data file: item \"%s\" in \"%s\"\n", items[i-1].name, filename); |
+ exit(U_INVALID_FORMAT_ERROR); |
+ } |
+ items[i-1].type=makeTypeLetter(typeEnum); |
+ } |
+ items[i].isDataOwned=FALSE; |
+ } |
+ // set the last item's length |
+ items[itemCount-1].length=length-ds->readUInt32(inEntries[itemCount-1].dataOffset); |
+ |
+ // set the last item's platform type |
+ typeEnum=getTypeEnumForInputData(items[itemCount-1].data, items[itemCount-1].length, &errorCode); |
+ if(typeEnum<0 || U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: not an ICU data file: item \"%s\" in \"%s\"\n", items[i-1].name, filename); |
+ exit(U_INVALID_FORMAT_ERROR); |
+ } |
+ items[itemCount-1].type=makeTypeLetter(typeEnum); |
+ |
+ if(type!=U_ICUDATA_TYPE_LETTER[0]) { |
+ // sort the item names for the local charset |
+ sortItems(); |
+ } |
+ } |
+ |
+ udata_closeSwapper(ds); |
+} |
+ |
+char |
+Package::getInType() { |
+ return makeTypeLetter(inCharset, inIsBigEndian); |
+} |
+ |
+void |
+Package::writePackage(const char *filename, char outType, const char *comment) { |
+ char prefix[MAX_PKG_NAME_LENGTH+4]; |
+ UDataOffsetTOCEntry entry; |
+ UDataSwapper *dsLocalToOut, *ds[TYPE_COUNT]; |
+ FILE *file; |
+ Item *pItem; |
+ char *name; |
+ UErrorCode errorCode; |
+ int32_t i, length, prefixLength, maxItemLength, basenameOffset, offset, outInt32; |
+ uint8_t outCharset; |
+ UBool outIsBigEndian; |
+ |
+ extractPackageName(filename, prefix, MAX_PKG_NAME_LENGTH); |
+ |
+ // if there is an explicit comment, then use it, else use what's in the current header |
+ if(comment!=NULL) { |
+ /* get the header size minus the current comment */ |
+ DataHeader *pHeader; |
+ int32_t length; |
+ |
+ pHeader=(DataHeader *)header; |
+ headerLength=4+pHeader->info.size; |
+ length=(int32_t)strlen(comment); |
+ if((int32_t)(headerLength+length)>=(int32_t)sizeof(header)) { |
+ fprintf(stderr, "icupkg: comment too long\n"); |
+ exit(U_BUFFER_OVERFLOW_ERROR); |
+ } |
+ memcpy(header+headerLength, comment, length+1); |
+ headerLength+=length; |
+ if(headerLength&0xf) { |
+ /* NUL-pad the header to a multiple of 16 */ |
+ length=(headerLength+0xf)&~0xf; |
+ memset(header+headerLength, 0, length-headerLength); |
+ headerLength=length; |
+ } |
+ pHeader->dataHeader.headerSize=(uint16_t)headerLength; |
+ } |
+ |
+ makeTypeProps(outType, outCharset, outIsBigEndian); |
+ |
+ // open (TYPE_COUNT-2) swappers |
+ // one is a no-op for local type==outType |
+ // one type (TYPE_LE) is bogus |
+ errorCode=U_ZERO_ERROR; |
+ i=makeTypeEnum(outType); |
+ ds[TYPE_B]= i==TYPE_B ? NULL : udata_openSwapper(TRUE, U_ASCII_FAMILY, outIsBigEndian, outCharset, &errorCode); |
+ ds[TYPE_L]= i==TYPE_L ? NULL : udata_openSwapper(FALSE, U_ASCII_FAMILY, outIsBigEndian, outCharset, &errorCode); |
+ ds[TYPE_LE]=NULL; |
+ ds[TYPE_E]= i==TYPE_E ? NULL : udata_openSwapper(TRUE, U_EBCDIC_FAMILY, outIsBigEndian, outCharset, &errorCode); |
+ if(U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: udata_openSwapper() failed - %s\n", u_errorName(errorCode)); |
+ exit(errorCode); |
+ } |
+ for(i=0; i<TYPE_COUNT; ++i) { |
+ if(ds[i]!=NULL) { |
+ ds[i]->printError=printPackageError; |
+ ds[i]->printErrorContext=stderr; |
+ } |
+ } |
+ |
+ dsLocalToOut=ds[makeTypeEnum(U_CHARSET_FAMILY, U_IS_BIG_ENDIAN)]; |
+ |
+ // create the file and write its contents |
+ file=fopen(filename, "wb"); |
+ if(file==NULL) { |
+ fprintf(stderr, "icupkg: unable to create file \"%s\"\n", filename); |
+ exit(U_FILE_ACCESS_ERROR); |
+ } |
+ |
+ // swap and write the header |
+ if(dsLocalToOut!=NULL) { |
+ udata_swapDataHeader(dsLocalToOut, header, headerLength, header, &errorCode); |
+ if(U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: udata_swapDataHeader(local to out) failed - %s\n", u_errorName(errorCode)); |
+ exit(errorCode); |
+ } |
+ } |
+ length=(int32_t)fwrite(header, 1, headerLength, file); |
+ if(length!=headerLength) { |
+ fprintf(stderr, "icupkg: unable to write complete header to file \"%s\"\n", filename); |
+ exit(U_FILE_ACCESS_ERROR); |
+ } |
+ |
+ // prepare and swap the package name with a tree separator |
+ // for prepending to item names |
+ strcat(prefix, U_TREE_ENTRY_SEP_STRING); |
+ prefixLength=(int32_t)strlen(prefix); |
+ if(dsLocalToOut!=NULL) { |
+ dsLocalToOut->swapInvChars(dsLocalToOut, prefix, prefixLength, prefix, &errorCode); |
+ if(U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: swapInvChars(output package name) failed - %s\n", u_errorName(errorCode)); |
+ exit(errorCode); |
+ } |
+ |
+ // swap and sort the item names (sorting needs to be done in the output charset) |
+ dsLocalToOut->swapInvChars(dsLocalToOut, inStrings, inStringTop, inStrings, &errorCode); |
+ if(U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: swapInvChars(item names) failed - %s\n", u_errorName(errorCode)); |
+ exit(errorCode); |
+ } |
+ sortItems(); |
+ } |
+ |
+ // create the output item names in sorted order, with the package name prepended to each |
+ for(i=0; i<itemCount; ++i) { |
+ length=(int32_t)strlen(items[i].name); |
+ name=allocString(FALSE, length+prefixLength); |
+ memcpy(name, prefix, prefixLength); |
+ memcpy(name+prefixLength, items[i].name, length+1); |
+ items[i].name=name; |
+ } |
+ |
+ // calculate offsets for item names and items, pad to 16-align items |
+ // align only the first item; each item's length is a multiple of 16 |
+ basenameOffset=4+8*itemCount; |
+ offset=basenameOffset+outStringTop; |
+ if((length=(offset&15))!=0) { |
+ length=16-length; |
+ memset(allocString(FALSE, length-1), 0xaa, length); |
+ offset+=length; |
+ } |
+ |
+ // write the table of contents |
+ // first the itemCount |
+ outInt32=itemCount; |
+ if(dsLocalToOut!=NULL) { |
+ dsLocalToOut->swapArray32(dsLocalToOut, &outInt32, 4, &outInt32, &errorCode); |
+ if(U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: swapArray32(item count) failed - %s\n", u_errorName(errorCode)); |
+ exit(errorCode); |
+ } |
+ } |
+ length=(int32_t)fwrite(&outInt32, 1, 4, file); |
+ if(length!=4) { |
+ fprintf(stderr, "icupkg: unable to write complete item count to file \"%s\"\n", filename); |
+ exit(U_FILE_ACCESS_ERROR); |
+ } |
+ |
+ // then write the item entries (and collect the maxItemLength) |
+ maxItemLength=0; |
+ for(i=0; i<itemCount; ++i) { |
+ entry.nameOffset=(uint32_t)(basenameOffset+(items[i].name-outStrings)); |
+ entry.dataOffset=(uint32_t)offset; |
+ if(dsLocalToOut!=NULL) { |
+ dsLocalToOut->swapArray32(dsLocalToOut, &entry, 8, &entry, &errorCode); |
+ if(U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: swapArray32(item entry %ld) failed - %s\n", (long)i, u_errorName(errorCode)); |
+ exit(errorCode); |
+ } |
+ } |
+ length=(int32_t)fwrite(&entry, 1, 8, file); |
+ if(length!=8) { |
+ fprintf(stderr, "icupkg: unable to write complete item entry %ld to file \"%s\"\n", (long)i, filename); |
+ exit(U_FILE_ACCESS_ERROR); |
+ } |
+ |
+ length=items[i].length; |
+ if(length>maxItemLength) { |
+ maxItemLength=length; |
+ } |
+ offset+=length; |
+ } |
+ |
+ // write the item names |
+ length=(int32_t)fwrite(outStrings, 1, outStringTop, file); |
+ if(length!=outStringTop) { |
+ fprintf(stderr, "icupkg: unable to write complete item names to file \"%s\"\n", filename); |
+ exit(U_FILE_ACCESS_ERROR); |
+ } |
+ |
+ // write the items |
+ for(pItem=items, i=0; i<itemCount; ++pItem, ++i) { |
+ int32_t type=makeTypeEnum(pItem->type); |
+ if(ds[type]!=NULL) { |
+ // swap each item from its platform properties to the desired ones |
+ udata_swap( |
+ ds[type], |
+ pItem->data, pItem->length, pItem->data, |
+ &errorCode); |
+ if(U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: udata_swap(item %ld) failed - %s\n", (long)i, u_errorName(errorCode)); |
+ exit(errorCode); |
+ } |
+ } |
+ length=(int32_t)fwrite(pItem->data, 1, pItem->length, file); |
+ if(length!=pItem->length) { |
+ fprintf(stderr, "icupkg: unable to write complete item %ld to file \"%s\"\n", (long)i, filename); |
+ exit(U_FILE_ACCESS_ERROR); |
+ } |
+ } |
+ |
+ if(ferror(file)) { |
+ fprintf(stderr, "icupkg: unable to write complete file \"%s\"\n", filename); |
+ exit(U_FILE_ACCESS_ERROR); |
+ } |
+ |
+ fclose(file); |
+ for(i=0; i<TYPE_COUNT; ++i) { |
+ udata_closeSwapper(ds[i]); |
+ } |
+} |
+ |
+int32_t |
+Package::findItem(const char *name, int32_t length) const { |
+ int32_t i, start, limit; |
+ int result; |
+ |
+ /* do a binary search for the string */ |
+ start=0; |
+ limit=itemCount; |
+ while(start<limit) { |
+ i=(start+limit)/2; |
+ if(length>=0) { |
+ result=strncmp(name, items[i].name, length); |
+ } else { |
+ result=strcmp(name, items[i].name); |
+ } |
+ |
+ if(result==0) { |
+ /* found */ |
+ if(length>=0) { |
+ /* |
+ * if we compared just prefixes, then we may need to back up |
+ * to the first item with this prefix |
+ */ |
+ while(i>0 && 0==strncmp(name, items[i-1].name, length)) { |
+ --i; |
+ } |
+ } |
+ return i; |
+ } else if(result<0) { |
+ limit=i; |
+ } else /* result>0 */ { |
+ start=i+1; |
+ } |
+ } |
+ |
+ return ~start; /* not found, return binary-not of the insertion point */ |
+} |
+ |
+void |
+Package::findItems(const char *pattern) { |
+ const char *wild; |
+ |
+ if(pattern==NULL || *pattern==0) { |
+ findNextIndex=-1; |
+ return; |
+ } |
+ |
+ findPrefix=pattern; |
+ findSuffix=NULL; |
+ findSuffixLength=0; |
+ |
+ wild=strchr(pattern, '*'); |
+ if(wild==NULL) { |
+ // no wildcard |
+ findPrefixLength=(int32_t)strlen(pattern); |
+ } else { |
+ // one wildcard |
+ findPrefixLength=(int32_t)(wild-pattern); |
+ findSuffix=wild+1; |
+ findSuffixLength=(int32_t)strlen(findSuffix); |
+ if(NULL!=strchr(findSuffix, '*')) { |
+ // two or more wildcards |
+ fprintf(stderr, "icupkg: syntax error (more than one '*') in item pattern \"%s\"\n", pattern); |
+ exit(U_PARSE_ERROR); |
+ } |
+ } |
+ |
+ if(findPrefixLength==0) { |
+ findNextIndex=0; |
+ } else { |
+ findNextIndex=findItem(findPrefix, findPrefixLength); |
+ } |
+} |
+ |
+int32_t |
+Package::findNextItem() { |
+ const char *name, *middle, *treeSep; |
+ int32_t idx, nameLength, middleLength; |
+ |
+ if(findNextIndex<0) { |
+ return -1; |
+ } |
+ |
+ while(findNextIndex<itemCount) { |
+ idx=findNextIndex++; |
+ name=items[idx].name; |
+ nameLength=(int32_t)strlen(name); |
+ if(nameLength<(findPrefixLength+findSuffixLength)) { |
+ // item name too short for prefix & suffix |
+ continue; |
+ } |
+ if(findPrefixLength>0 && 0!=memcmp(findPrefix, name, findPrefixLength)) { |
+ // left the range of names with this prefix |
+ break; |
+ } |
+ middle=name+findPrefixLength; |
+ middleLength=nameLength-findPrefixLength-findSuffixLength; |
+ if(findSuffixLength>0 && 0!=memcmp(findSuffix, name+(nameLength-findSuffixLength), findSuffixLength)) { |
+ // suffix does not match |
+ continue; |
+ } |
+ // prefix & suffix match |
+ |
+ if(matchMode&MATCH_NOSLASH) { |
+ treeSep=strchr(middle, U_TREE_ENTRY_SEP_CHAR); |
+ if(treeSep!=NULL && (treeSep-middle)<middleLength) { |
+ // the middle (matching the * wildcard) contains a tree separator / |
+ continue; |
+ } |
+ } |
+ |
+ // found a matching item |
+ return idx; |
+ } |
+ |
+ // no more items |
+ findNextIndex=-1; |
+ return -1; |
+} |
+ |
+void |
+Package::setMatchMode(uint32_t mode) { |
+ matchMode=mode; |
+} |
+ |
+void |
+Package::addItem(const char *name) { |
+ addItem(name, NULL, 0, FALSE, U_ICUDATA_TYPE_LETTER[0]); |
+} |
+ |
+void |
+Package::addItem(const char *name, uint8_t *data, int32_t length, UBool isDataOwned, char type) { |
+ int32_t idx; |
+ |
+ idx=findItem(name); |
+ if(idx<0) { |
+ // new item, make space at the insertion point |
+ ensureItemCapacity(); |
+ // move the following items down |
+ idx=~idx; |
+ if(idx<itemCount) { |
+ memmove(items+idx+1, items+idx, (itemCount-idx)*sizeof(Item)); |
+ } |
+ ++itemCount; |
+ |
+ // reset this Item entry |
+ memset(items+idx, 0, sizeof(Item)); |
+ |
+ // copy the item's name |
+ items[idx].name=allocString(TRUE, strlen(name)); |
+ strcpy(items[idx].name, name); |
+ pathToTree(items[idx].name); |
+ } else { |
+ // same-name item found, replace it |
+ if(items[idx].isDataOwned) { |
+ free(items[idx].data); |
+ } |
+ |
+ // keep the item's name since it is the same |
+ } |
+ |
+ // set the item's data |
+ items[idx].data=data; |
+ items[idx].length=length; |
+ items[idx].isDataOwned=isDataOwned; |
+ items[idx].type=type; |
+} |
+ |
+void |
+Package::addFile(const char *filesPath, const char *name) { |
+ uint8_t *data; |
+ int32_t length; |
+ char type; |
+ |
+ data=readFile(filesPath, name, length, type); |
+ // readFile() exits the tool if it fails |
+ addItem(name, data, length, TRUE, type); |
+} |
+ |
+void |
+Package::addItems(const Package &listPkg) { |
+ const Item *pItem; |
+ int32_t i; |
+ |
+ for(pItem=listPkg.items, i=0; i<listPkg.itemCount; ++pItem, ++i) { |
+ addItem(pItem->name, pItem->data, pItem->length, FALSE, pItem->type); |
+ } |
+} |
+ |
+void |
+Package::removeItem(int32_t idx) { |
+ if(idx>=0) { |
+ // remove the item |
+ if(items[idx].isDataOwned) { |
+ free(items[idx].data); |
+ } |
+ |
+ // move the following items up |
+ if((idx+1)<itemCount) { |
+ memmove(items+idx, items+idx+1, (itemCount-(idx+1))*sizeof(Item)); |
+ } |
+ --itemCount; |
+ |
+ if(idx<=findNextIndex) { |
+ --findNextIndex; |
+ } |
+ } |
+} |
+ |
+void |
+Package::removeItems(const char *pattern) { |
+ int32_t idx; |
+ |
+ findItems(pattern); |
+ while((idx=findNextItem())>=0) { |
+ removeItem(idx); |
+ } |
+} |
+ |
+void |
+Package::removeItems(const Package &listPkg) { |
+ const Item *pItem; |
+ int32_t i; |
+ |
+ for(pItem=listPkg.items, i=0; i<listPkg.itemCount; ++pItem, ++i) { |
+ removeItems(pItem->name); |
+ } |
+} |
+ |
+void |
+Package::extractItem(const char *filesPath, const char *outName, int32_t idx, char outType) { |
+ char filename[1024]; |
+ UDataSwapper *ds; |
+ FILE *file; |
+ Item *pItem; |
+ int32_t fileLength; |
+ uint8_t itemCharset, outCharset; |
+ UBool itemIsBigEndian, outIsBigEndian; |
+ |
+ if(idx<0 || itemCount<=idx) { |
+ return; |
+ } |
+ pItem=items+idx; |
+ |
+ // swap the data to the outType |
+ // outType==0: don't swap |
+ if(outType!=0 && pItem->type!=outType) { |
+ // open the swapper |
+ UErrorCode errorCode=U_ZERO_ERROR; |
+ makeTypeProps(pItem->type, itemCharset, itemIsBigEndian); |
+ makeTypeProps(outType, outCharset, outIsBigEndian); |
+ ds=udata_openSwapper(itemIsBigEndian, itemCharset, outIsBigEndian, outCharset, &errorCode); |
+ if(U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: udata_openSwapper(item %ld) failed - %s\n", |
+ (long)idx, u_errorName(errorCode)); |
+ exit(errorCode); |
+ } |
+ |
+ ds->printError=printPackageError; |
+ ds->printErrorContext=stderr; |
+ |
+ // swap the item from its platform properties to the desired ones |
+ udata_swap(ds, pItem->data, pItem->length, pItem->data, &errorCode); |
+ if(U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: udata_swap(item %ld) failed - %s\n", (long)idx, u_errorName(errorCode)); |
+ exit(errorCode); |
+ } |
+ udata_closeSwapper(ds); |
+ pItem->type=outType; |
+ } |
+ |
+ // create the file and write its contents |
+ makeFullFilenameAndDirs(filesPath, outName, filename, (int32_t)sizeof(filename)); |
+ file=fopen(filename, "wb"); |
+ if(file==NULL) { |
+ fprintf(stderr, "icupkg: unable to create file \"%s\"\n", filename); |
+ exit(U_FILE_ACCESS_ERROR); |
+ } |
+ fileLength=(int32_t)fwrite(pItem->data, 1, pItem->length, file); |
+ |
+ if(ferror(file) || fileLength!=pItem->length) { |
+ fprintf(stderr, "icupkg: unable to write complete file \"%s\"\n", filename); |
+ exit(U_FILE_ACCESS_ERROR); |
+ } |
+ fclose(file); |
+} |
+ |
+void |
+Package::extractItem(const char *filesPath, int32_t idx, char outType) { |
+ extractItem(filesPath, items[idx].name, idx, outType); |
+} |
+ |
+void |
+Package::extractItems(const char *filesPath, const char *pattern, char outType) { |
+ int32_t idx; |
+ |
+ findItems(pattern); |
+ while((idx=findNextItem())>=0) { |
+ extractItem(filesPath, idx, outType); |
+ } |
+} |
+ |
+void |
+Package::extractItems(const char *filesPath, const Package &listPkg, char outType) { |
+ const Item *pItem; |
+ int32_t i; |
+ |
+ for(pItem=listPkg.items, i=0; i<listPkg.itemCount; ++pItem, ++i) { |
+ extractItems(filesPath, pItem->name, outType); |
+ } |
+} |
+ |
+int32_t |
+Package::getItemCount() const { |
+ return itemCount; |
+} |
+ |
+const Item * |
+Package::getItem(int32_t idx) const { |
+ if (0 <= idx && idx < itemCount) { |
+ return &items[idx]; |
+ } |
+ return NULL; |
+} |
+ |
+void |
+Package::checkDependency(void *context, const char *itemName, const char *targetName) { |
+ // check dependency: make sure the target item is in the package |
+ Package *me=(Package *)context; |
+ if(me->findItem(targetName)<0) { |
+ me->isMissingItems=TRUE; |
+ fprintf(stderr, "Item %s depends on missing item %s\n", itemName, targetName); |
+ } |
+} |
+ |
+UBool |
+Package::checkDependencies() { |
+ isMissingItems=FALSE; |
+ enumDependencies(this, checkDependency); |
+ return (UBool)!isMissingItems; |
+} |
+ |
+void |
+Package::enumDependencies(void *context, CheckDependency check) { |
+ int32_t i; |
+ |
+ for(i=0; i<itemCount; ++i) { |
+ enumDependencies(items+i, context, check); |
+ } |
+} |
+ |
+char * |
+Package::allocString(UBool in, int32_t length) { |
+ char *p; |
+ int32_t top; |
+ |
+ if(in) { |
+ top=inStringTop; |
+ p=inStrings+top; |
+ } else { |
+ top=outStringTop; |
+ p=outStrings+top; |
+ } |
+ top+=length+1; |
+ |
+ if(top>STRING_STORE_SIZE) { |
+ fprintf(stderr, "icupkg: string storage overflow\n"); |
+ exit(U_BUFFER_OVERFLOW_ERROR); |
+ } |
+ if(in) { |
+ inStringTop=top; |
+ } else { |
+ outStringTop=top; |
+ } |
+ return p; |
+} |
+ |
+void |
+Package::sortItems() { |
+ UErrorCode errorCode=U_ZERO_ERROR; |
+ uprv_sortArray(items, itemCount, (int32_t)sizeof(Item), compareItems, NULL, FALSE, &errorCode); |
+ if(U_FAILURE(errorCode)) { |
+ fprintf(stderr, "icupkg: sorting item names failed - %s\n", u_errorName(errorCode)); |
+ exit(errorCode); |
+ } |
+} |
+ |
+void Package::setItemCapacity(int32_t max) |
+{ |
+ if(max<=itemMax) { |
+ return; |
+ } |
+ Item *newItems = (Item*)uprv_malloc(max * sizeof(items[0])); |
+ Item *oldItems = items; |
+ if(newItems == NULL) { |
+ fprintf(stderr, "icupkg: Out of memory trying to allocate %ld bytes for %d items\n", max*sizeof(items[0]), max); |
+ exit(U_MEMORY_ALLOCATION_ERROR); |
+ } |
+ if(items && itemCount>0) { |
+ uprv_memcpy(newItems, items, itemCount*sizeof(items[0])); |
+ } |
+ itemMax = max; |
+ items = newItems; |
+ uprv_free(oldItems); |
+} |
+ |
+void Package::ensureItemCapacity() |
+{ |
+ if((itemCount+1)>itemMax) { |
+ setItemCapacity(itemCount+kItemsChunk); |
+ } |
+} |
+ |
+U_NAMESPACE_END |
Property changes on: icu46/source/tools/toolutil/package.cpp |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |