ICU-7881 add icupkg options --auto_toc_prefix --auto_toc_prefix_with_type --toc_prefix

X-SVN-Rev: 33500
This commit is contained in:
Markus Scherer 2013-04-08 21:31:58 +00:00
parent 74c81065f6
commit bc9b269353
3 changed files with 160 additions and 33 deletions

View file

@ -1,7 +1,7 @@
/*
*******************************************************************************
*
* Copyright (C) 2005-2012, International Business Machines
* Copyright (C) 2005-2013, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
@ -118,6 +118,30 @@ printUsage(const char *pname, UBool isHelp) {
"\t-m mode or --matchmode mode set the matching mode for item names with\n"
"\t wildcards\n"
"\t noslash: the '*' wildcard does not match the '/' tree separator\n");
fprintf(where,
"\n"
"\tIn the .dat package, the Table of Contents (ToC) contains an entry\n"
"\tfor each item of the form prefix/tree/itemname .\n"
"\tThe prefix normally matches the package basename, and icupkg checks that,\n"
"\tbut this is not necessary when ICU need not find and load the package by filename.\n"
"\tICU package names end with the platform type letter, and thus differ\n"
"\tbetween platform types. This is not required for user data packages.\n");
fprintf(where,
"\n"
"\t--auto_toc_prefix automatic ToC entries prefix\n"
"\t Uses the prefix of the first entry of the\n"
"\t input package, rather than its basename.\n"
"\t Requires a non-empty input package.\n"
"\t--auto_toc_prefix_with_type auto_toc_prefix + adjust platform type\n"
"\t Same as auto_toc_prefix but also checks that\n"
"\t the prefix ends with the input platform\n"
"\t type letter, and modifies it to the output\n"
"\t platform type letter.\n"
"\t At most one of the auto_toc_prefix options\n"
"\t can be used at a time.\n"
"\t--toc_prefix prefix ToC prefix to be used in the output package\n"
"\t Overrides the package basename\n"
"\t and --auto_toc_prefix.\n");
/*
* Usage text columns, starting after the initial TAB.
* 1 2 3 4 5 6 7 8
@ -150,8 +174,10 @@ printUsage(const char *pname, UBool isHelp) {
"\t-s path or --sourcedir path directory for the --add items\n"
"\t-d path or --destdir path directory for the --extract items\n"
"\n"
"\t-l or --list list the package items to stdout or to output list file\n"
"\t (after modifying the package)\n");
"\t-l or --list list the package items\n"
"\t (after modifying the package)\n"
"\t to stdout or to output list file\n"
"\t-o path or --outlist path path/filename for the --list output\n");
}
}
@ -175,8 +201,11 @@ static UOption options[]={
UOPTION_DEF("extract", 'x', UOPT_REQUIRES_ARG),
UOPTION_DEF("list", 'l', UOPT_NO_ARG),
UOPTION_DEF("outlist", 'o', UOPT_REQUIRES_ARG)
UOPTION_DEF("outlist", 'o', UOPT_REQUIRES_ARG),
UOPTION_DEF("auto_toc_prefix", '\1', UOPT_NO_ARG),
UOPTION_DEF("auto_toc_prefix_with_type", '\1', UOPT_NO_ARG),
UOPTION_DEF("toc_prefix", '\1', UOPT_REQUIRES_ARG)
};
enum {
@ -199,9 +228,12 @@ enum {
OPT_EXTRACT_LIST,
OPT_LIST_ITEMS,
OPT_LIST_FILE,
OPT_AUTO_TOC_PREFIX,
OPT_AUTO_TOC_PREFIX_WITH_TYPE,
OPT_TOC_PREFIX,
OPT_COUNT
};
@ -238,10 +270,6 @@ main(int argc, char *argv[]) {
printUsage(pname, TRUE);
return U_ZERO_ERROR;
}
if(argc<2 || 3<argc) {
printUsage(pname, FALSE);
return U_ILLEGAL_ARGUMENT_ERROR;
}
pkg=new Package;
if(pkg==NULL) {
@ -250,6 +278,25 @@ main(int argc, char *argv[]) {
}
isModified=FALSE;
int autoPrefix=0;
if(options[OPT_AUTO_TOC_PREFIX].doesOccur) {
pkg->setAutoPrefix();
++autoPrefix;
}
if(options[OPT_AUTO_TOC_PREFIX_WITH_TYPE].doesOccur) {
if(options[OPT_TOC_PREFIX].doesOccur) {
fprintf(stderr, "icupkg: --auto_toc_prefix_with_type and also --toc_prefix\n");
printUsage(pname, FALSE);
return U_ILLEGAL_ARGUMENT_ERROR;
}
pkg->setAutoPrefixWithType();
++autoPrefix;
}
if(argc<2 || 3<argc || autoPrefix>1) {
printUsage(pname, FALSE);
return U_ILLEGAL_ARGUMENT_ERROR;
}
if(options[OPT_SOURCEDIR].doesOccur) {
sourcePath=options[OPT_SOURCEDIR].value;
} else {
@ -264,6 +311,11 @@ main(int argc, char *argv[]) {
}
if(0==strcmp(argv[1], "new")) {
if(autoPrefix) {
fprintf(stderr, "icupkg: --auto_toc_prefix[_with_type] but no input package\n");
printUsage(pname, FALSE);
return U_ILLEGAL_ARGUMENT_ERROR;
}
inFilename=NULL;
isPackage=TRUE;
} else {
@ -479,6 +531,9 @@ main(int argc, char *argv[]) {
}
outFilename=outFilenameBuffer;
}
if(options[OPT_TOC_PREFIX].doesOccur) {
pkg->setPrefix(options[OPT_TOC_PREFIX].value);
}
result = writePackageDatFile(outFilename, outComment, NULL, NULL, pkg, outType);
}

View file

@ -384,8 +384,10 @@ U_CDECL_END
U_NAMESPACE_BEGIN
Package::Package() {
Package::Package()
: doAutoPrefix(FALSE), prefixEndsWithType(FALSE) {
inPkgName[0]=0;
pkgPrefix[0]=0;
inData=NULL;
inLength=0;
inCharset=U_CHARSET_FAMILY;
@ -432,6 +434,15 @@ Package::~Package() {
uprv_free((void*)items);
}
void
Package::setPrefix(const char *p) {
if(strlen(p)>=sizeof(pkgPrefix)) {
fprintf(stderr, "icupkg: --toc_prefix %s too long\n", p);
exit(U_ILLEGAL_ARGUMENT_ERROR);
}
strcpy(pkgPrefix, p);
}
void
Package::readPackage(const char *filename) {
UDataSwapper *ds;
@ -523,10 +534,14 @@ Package::readPackage(const char *filename) {
}
/* do not modify the package length variable until the last item's length is set */
if(itemCount>0) {
if(itemCount<=0) {
if(doAutoPrefix) {
fprintf(stderr, "icupkg: --auto_toc_prefix[_with_type] but the input package is empty\n");
exit(U_INVALID_FORMAT_ERROR);
}
} else {
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);
@ -534,7 +549,7 @@ Package::readPackage(const char *filename) {
}
/* swap the item name strings */
stringsOffset=4+8*itemCount;
int32_t 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
@ -558,10 +573,6 @@ Package::readPackage(const char *filename) {
// 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,
@ -570,18 +581,53 @@ Package::readPackage(const char *filename) {
* 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++]='_';
s=inItemStrings+offset; // name of the first entry
int32_t prefixLength;
if(doAutoPrefix) {
// Use the first entry's prefix. Must be a new-style package.
const char *prefixLimit=strchr(s, U_TREE_ENTRY_SEP_CHAR);
if(prefixLimit==NULL) {
fprintf(stderr,
"icupkg: --auto_toc_prefix[_with_type] but "
"the first entry \"%s\" does not contain a '%c'\n",
s, U_TREE_ENTRY_SEP_CHAR);
exit(U_INVALID_FORMAT_ERROR);
}
prefixLength=(int32_t)(prefixLimit-s);
if(prefixLength==0 || prefixLength>=LENGTHOF(pkgPrefix)) {
fprintf(stderr,
"icupkg: --auto_toc_prefix[_with_type] but "
"the prefix of the first entry \"%s\" is empty or too long\n",
s);
exit(U_INVALID_FORMAT_ERROR);
}
if(prefixEndsWithType && s[prefixLength-1]!=type) {
fprintf(stderr,
"icupkg: --auto_toc_prefix_with_type but "
"the prefix of the first entry \"%s\" does not end with '%c'\n",
s, type);
exit(U_INVALID_FORMAT_ERROR);
}
memcpy(pkgPrefix, s, prefixLength);
memcpy(prefix, s, ++prefixLength); // include the /
} 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
// Use the package basename as prefix.
int32_t inPkgNameLength=strlen(inPkgName);
memcpy(prefix, inPkgName, inPkgNameLength);
prefixLength=inPkgNameLength;
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;
@ -594,7 +640,7 @@ Package::readPackage(const char *filename) {
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);
exit(U_INVALID_FORMAT_ERROR);
}
items[i].name=s+prefixLength;
@ -724,8 +770,17 @@ Package::writePackage(const char *filename, char outType, const char *comment) {
// 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(pkgPrefix[0]==0) {
prefixLength=(int32_t)strlen(prefix);
} else {
prefixLength=(int32_t)strlen(pkgPrefix);
memcpy(prefix, pkgPrefix, prefixLength);
if(prefixEndsWithType) {
prefix[prefixLength-1]=outType;
}
}
prefix[prefixLength++]=U_TREE_ENTRY_SEP_CHAR;
prefix[prefixLength]=0;
if(dsLocalToOut!=NULL) {
dsLocalToOut->swapInvChars(dsLocalToOut, prefix, prefixLength, prefix, &errorCode);
if(U_FAILURE(errorCode)) {

View file

@ -1,7 +1,7 @@
/*
*******************************************************************************
*
* Copyright (C) 2005-2010, International Business Machines
* Copyright (C) 2005-2013, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
@ -51,6 +51,20 @@ public:
/* Destructor. */
~Package();
/**
* Uses the prefix of the first entry of the package in readPackage(),
* rather than the package basename.
*/
void setAutoPrefix() { doAutoPrefix=TRUE; }
/**
* Same as setAutoPrefix(), plus the prefix must end with the platform type letter.
*/
void setAutoPrefixWithType() {
doAutoPrefix=TRUE;
prefixEndsWithType=TRUE;
}
void setPrefix(const char *p);
/*
* Read an existing .dat package file.
* The header and item name strings are swapped into this object,
@ -141,12 +155,15 @@ private:
// data fields
char inPkgName[MAX_PKG_NAME_LENGTH];
char pkgPrefix[MAX_PKG_NAME_LENGTH];
uint8_t *inData;
uint8_t header[1024];
int32_t inLength, headerLength;
uint8_t inCharset;
UBool inIsBigEndian;
UBool doAutoPrefix;
UBool prefixEndsWithType;
int32_t itemCount;
int32_t itemMax;