blob: 5b683145935f710558ef1be9b0f58453758ac9b6 [file] [log] [blame]
/*
*******************************************************************************
*
* Copyright (C) 1998-2003, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
*
* File genrb.c
*
* Modification History:
*
* Date Name Description
* 05/25/99 stephen Creation.
* 5/10/01 Ram removed ustdio dependency
*******************************************************************************
*/
#include "genrb.h"
#include "unicode/uclean.h"
/* Protos */
static void processFile(const char *filename, const char* cp, const char *inputDir, const char *outputDir, const char *packageName, UErrorCode *status);
static char *make_res_filename(const char *filename, const char *outputDir,
const char *packageName, UErrorCode *status);
/* File suffixes */
#define RES_SUFFIX ".res"
#define COL_SUFFIX ".col"
static char theCurrentFileName[2048];
const char *gCurrentFileName = theCurrentFileName;
#ifdef XP_MAC_CONSOLE
#include <console.h>
#endif
enum
{
HELP1,
HELP2,
VERBOSE,
QUIET,
VERSION,
SOURCEDIR,
DESTDIR,
ENCODING,
ICUDATADIR,
WRITE_JAVA,
COPYRIGHT,
PACKAGE_NAME,
BUNDLE_NAME,
WRITE_XLIFF,
TOUCHFILE,
STRICT,
NO_BINARY_COLLATION,
/*added by Jing*/
LANGUAGE
};
UOption options[]={
UOPTION_HELP_H,
UOPTION_HELP_QUESTION_MARK,
UOPTION_VERBOSE,
UOPTION_QUIET,
UOPTION_VERSION,
UOPTION_SOURCEDIR,
UOPTION_DESTDIR,
UOPTION_ENCODING,
UOPTION_ICUDATADIR,
UOPTION_WRITE_JAVA,
UOPTION_COPYRIGHT,
UOPTION_PACKAGE_NAME,
UOPTION_BUNDLE_NAME,
UOPTION_DEF( "write-xliff", 'x', UOPT_OPTIONAL_ARG),
UOPTION_DEF( "touchfile", 't', UOPT_NO_ARG),
UOPTION_DEF( "strict", 'k', UOPT_NO_ARG), /* 14 */
UOPTION_DEF( "noBinaryCollation", 'C', UOPT_NO_ARG),/* 15 */
/*added by Jing*/
UOPTION_DEF( "language", 'l', UOPT_REQUIRES_ARG)
};
static UBool write_java = FALSE;
static UBool write_xliff = FALSE;
static UBool touchfile = FALSE;
static const char* outputEnc ="";
static const char* gPackageName=NULL;
static const char* bundleName=NULL;
/*added by Jing*/
static const char* language = NULL;
static const char* xliffOutputFileName = NULL;
int
main(int argc,
char* argv[])
{
UErrorCode status = U_ZERO_ERROR;
const char *arg = NULL;
const char *outputDir = NULL; /* NULL = no output directory, use current */
const char *inputDir = NULL;
const char *encoding = "";
int i;
U_MAIN_INIT_ARGS(argc, argv);
argc = u_parseArgs(argc, argv, (int32_t)(sizeof(options)/sizeof(options[0])), options);
/* error handling, printing usage message */
if(argc<0) {
fprintf(stderr, "%s: error in command line argument \"%s\"\n", argv[0], argv[-argc]);
} else if(argc<2) {
argc = -1;
}
if(options[VERSION].doesOccur) {
fprintf(stderr,
"%s version %s (ICU version %s).\n"
"%s\n",
argv[0], GENRB_VERSION, U_ICU_VERSION, U_COPYRIGHT_STRING);
return U_ZERO_ERROR;
}
if(argc<0 || options[HELP1].doesOccur || options[HELP2].doesOccur) {
/*
* Broken into chucks because the C89 standard says the minimum
* required supported string length is 509 bytes.
*/
fprintf(stderr,
"Usage: %s [OPTIONS] [FILES]\n"
"\tReads the list of resource bundle source files and creates\n"
"\tbinary version of reosurce bundles (.res files)\n",
argv[0]);
fprintf(stderr,
"Options:\n"
"\t-h or -? or --help this usage text\n"
"\t-q or --quiet do not display warnings\n"
"\t-v or --verbose print extra information when processing files\n"
"\t-V or --version prints out version number and exits\n"
"\t-c or --copyright include copyright notice\n");
fprintf(stderr,
"\t-e or --encoding encoding of source files\n"
"\t-d of --destdir destination directory, followed by the path, defaults to %s\n"
"\t-s or --sourcedir source directory for files followed by path, defaults to %s\n"
"\t-i or --icudatadir directory for locating any needed intermediate data files,\n"
"\t followed by path, defaults to %s\n",
u_getDataDirectory(), u_getDataDirectory(), u_getDataDirectory());
fprintf(stderr,
"\t-j or --write-java write a Java ListResourceBundle for ICU4J, followed by optional encoding\n"
"\t defaults to ASCII and \\uXXXX format.\n"
"\t-p or --package-name For ICU4J: package name for writing the ListResourceBundle for ICU4J,\n"
"\t defaults to com.ibm.icu.impl.data\n"
"\t For ICU4C: Package name for the .res files on output. Specfiying\n"
"\t 'ICUDATA' defaults to the current ICU4C data name.\n");
fprintf(stderr,
"\t-b or --bundle-name bundle name for writing the ListResourceBundle for ICU4J,\n"
"\t defaults to LocaleElements\n"
"\t-x or --write-xliff write a XLIFF file for the resource bundle. Followed by an optional output file name.\n"
"\t-k or --strict use pedantic parsing of syntax\n"
/*added by Jing*/
"\t-l or --language For XLIFF: language code compliant with ISO 639.\n");
return argc < 0 ? U_ILLEGAL_ARGUMENT_ERROR : U_ZERO_ERROR;
}
if(options[VERBOSE].doesOccur) {
setVerbose(TRUE);
}
if(options[QUIET].doesOccur) {
setShowWarning(FALSE);
}
if(options[STRICT].doesOccur) {
setStrict(TRUE);
}
if(options[COPYRIGHT].doesOccur){
setIncludeCopyright(TRUE);
}
if(options[SOURCEDIR].doesOccur) {
inputDir = options[SOURCEDIR].value;
}
if(options[DESTDIR].doesOccur) {
outputDir = options[DESTDIR].value;
}
if(options[PACKAGE_NAME].doesOccur) {
gPackageName = options[PACKAGE_NAME].value;
if(!strcmp(gPackageName, "ICUDATA"))
{
gPackageName = U_ICUDATA_NAME;
}
if(gPackageName[0] == 0)
{
gPackageName = NULL;
}
}
if(options[TOUCHFILE].doesOccur) {
if(gPackageName == NULL) {
fprintf(stderr, "%s: Don't use touchfile (-t) option with no package.\n",
argv[0]);
return -1;
}
touchfile = TRUE;
}
if(options[ENCODING].doesOccur) {
encoding = options[ENCODING].value;
}
if(options[ICUDATADIR].doesOccur) {
u_setDataDirectory(options[ICUDATADIR].value);
}
/* Initialize ICU */
u_init(&status);
if (U_FAILURE(status) && status != U_FILE_ACCESS_ERROR) {
/* Note: u_init() will try to open ICU property data.
* failures here are expected when building ICU from scratch.
* ignore them.
*/
fprintf(stderr, "%s: can not initialize ICU. status = %s\n",
argv[0], u_errorName(status));
exit(1);
}
status = U_ZERO_ERROR;
if(options[WRITE_JAVA].doesOccur) {
write_java = TRUE;
outputEnc = options[WRITE_JAVA].value;
}
if(options[BUNDLE_NAME].doesOccur) {
bundleName = options[BUNDLE_NAME].value;
}
if(options[WRITE_XLIFF].doesOccur) {
write_xliff = TRUE;
if(options[WRITE_XLIFF].value != NULL){
xliffOutputFileName = options[WRITE_XLIFF].value;
}
}
if(options[NO_BINARY_COLLATION].doesOccur) {
initParser(FALSE);
} else {
initParser(TRUE);
}
/*added by Jing*/
if(options[LANGUAGE].doesOccur) {
language = options[LANGUAGE].value;
}
/* generate the binary files */
for(i = 1; i < argc; ++i) {
status = U_ZERO_ERROR;
arg = getLongPathname(argv[i]);
if (inputDir) {
uprv_strcpy(theCurrentFileName, inputDir);
uprv_strcat(theCurrentFileName, U_FILE_SEP_STRING);
} else {
*theCurrentFileName = 0;
}
uprv_strcat(theCurrentFileName, arg);
if (isVerbose()) {
printf("Processing file \"%s\"\n", theCurrentFileName);
}
processFile(arg, encoding, inputDir, outputDir, gPackageName, &status);
}
return status;
}
/* Process a file */
static void
processFile(const char *filename, const char *cp, const char *inputDir, const char *outputDir, const char *packageName, UErrorCode *status) {
/*FileStream *in = NULL;*/
struct SRBRoot *data = NULL;
UCHARBUF *ucbuf = NULL;
char *rbname = NULL;
char *openFileName = NULL;
char *inputDirBuf = NULL;
char outputFileName[256];
int32_t dirlen = 0;
int32_t filelen = 0;
if (status==NULL || U_FAILURE(*status)) {
return;
}
if(filename==NULL){
*status=U_ILLEGAL_ARGUMENT_ERROR;
return;
}else{
filelen = (int32_t)uprv_strlen(filename);
}
if(inputDir == NULL) {
const char *filenameBegin = uprv_strrchr(filename, U_FILE_SEP_CHAR);
openFileName = (char *) uprv_malloc(dirlen + filelen + 2);
openFileName[0] = '\0';
if (filenameBegin != NULL) {
/*
* When a filename ../../../data/root.txt is specified,
* we presume that the input directory is ../../../data
* This is very important when the resource file includes
* another file, like UCARules.txt or thaidict.brk.
*/
int32_t filenameSize = (int32_t)(filenameBegin - filename + 1);
inputDirBuf = uprv_strncpy((char *)uprv_malloc(filenameSize), filename, filenameSize);
/* test for NULL */
if(inputDirBuf == NULL) {
*status = U_MEMORY_ALLOCATION_ERROR;
goto finish;
}
inputDirBuf[filenameSize - 1] = 0;
inputDir = inputDirBuf;
dirlen = (int32_t)uprv_strlen(inputDir);
}
}else{
dirlen = (int32_t)uprv_strlen(inputDir);
if(inputDir[dirlen-1] != U_FILE_SEP_CHAR) {
openFileName = (char *) uprv_malloc(dirlen + filelen + 2);
/* test for NULL */
if(openFileName == NULL) {
*status = U_MEMORY_ALLOCATION_ERROR;
goto finish;
}
openFileName[0] = '\0';
/*
* append the input dir to openFileName if the first char in
* filename is not file seperation char and the last char input directory is not '.'.
* This is to support :
* genrb -s. /home/icu/data
* genrb -s. icu/data
* The user cannot mix notations like
* genrb -s. /icu/data --- the absolute path specified. -s redundant
* user should use
* genrb -s. icu/data --- start from CWD and look in icu/data dir
*/
if( (filename[0] != U_FILE_SEP_CHAR) && (inputDir[dirlen-1] !='.')){
uprv_strcpy(openFileName, inputDir);
openFileName[dirlen] = U_FILE_SEP_CHAR;
}
openFileName[dirlen + 1] = '\0';
} else {
openFileName = (char *) uprv_malloc(dirlen + filelen + 1);
/* test for NULL */
if(openFileName == NULL) {
*status = U_MEMORY_ALLOCATION_ERROR;
goto finish;
}
uprv_strcpy(openFileName, inputDir);
}
}
uprv_strcat(openFileName, filename);
ucbuf = ucbuf_open(openFileName, &cp,getShowWarning(),TRUE, status);
if(*status == U_FILE_ACCESS_ERROR) {
fprintf(stderr, "couldn't open file %s\n", openFileName == NULL ? filename : openFileName);
goto finish;
}
if (ucbuf == NULL || U_FAILURE(*status)) {
fprintf(stderr, "An error occured processing file %s. Error: %s\n", openFileName == NULL ? filename : openFileName,u_errorName(*status));
goto finish;
}
/* auto detected popular encodings? */
if (cp!=NULL) {
printf("autodetected encoding %s\n", cp);
}
/* Parse the data into an SRBRoot */
data = parse(ucbuf, inputDir, status);
if (data == NULL || U_FAILURE(*status)) {
fprintf(stderr, "couldn't parse the file %s. Error:%s\n", filename,u_errorName(*status));
goto finish;
}
/* Determine the target rb filename */
rbname = make_res_filename(filename, outputDir, packageName, status);
if(touchfile == TRUE) {
FileStream *q;
char msg[1024];
char *tfname = NULL;
tfname = make_res_filename(filename, outputDir, NULL, status);
if(U_FAILURE(*status))
{
fprintf(stderr, "Error writing touchfile for \"%s\"\n", filename);
*status = U_FILE_ACCESS_ERROR;
} else {
uprv_strcat(tfname, ".res");
sprintf(msg, "This empty file tells nmake that %s in package %s has been updated.\n",
filename, packageName);
q = T_FileStream_open(tfname, "w");
if(q == NULL)
{
fprintf(stderr, "Error writing touchfile \"%s\"\n", tfname);
*status = U_FILE_ACCESS_ERROR;
}
else
{
T_FileStream_write(q, msg, (int32_t)uprv_strlen(msg));
T_FileStream_close(q);
}
uprv_free(tfname);
}
}
if(U_FAILURE(*status)) {
fprintf(stderr, "couldn't make the res fileName for bundle %s. Error:%s\n", filename,u_errorName(*status));
goto finish;
}
if(write_java== TRUE){
bundle_write_java(data,outputDir,outputEnc, outputFileName, sizeof(outputFileName),packageName,bundleName,status);
}else if(write_xliff ==TRUE){
bundle_write_xml(data,outputDir,outputEnc, filename, outputFileName, sizeof(outputFileName),language, xliffOutputFileName,status);
}else{
/* Write the data to the file */
bundle_write(data, outputDir, packageName, outputFileName, sizeof(outputFileName), status);
}
if (U_FAILURE(*status)) {
fprintf(stderr, "couldn't write bundle %s. Error:%s\n", outputFileName,u_errorName(*status));
}
bundle_close(data, status);
finish:
if (inputDirBuf != NULL) {
uprv_free(inputDirBuf);
}
if (openFileName != NULL) {
uprv_free(openFileName);
}
if(ucbuf) {
ucbuf_close(ucbuf);
}
if (rbname) {
uprv_free(rbname);
}
}
/* Generate the target .res file name from the input file name */
static char*
make_res_filename(const char *filename,
const char *outputDir,
const char *packageName,
UErrorCode *status) {
char *basename;
char *dirname;
char *resName;
int32_t pkgLen = 0; /* length of package prefix */
if (U_FAILURE(*status)) {
return 0;
}
if(packageName != NULL)
{
pkgLen = (int32_t)(1 + uprv_strlen(packageName));
}
/* setup */
basename = dirname = resName = 0;
/* determine basename, and compiled file names */
basename = (char*) uprv_malloc(sizeof(char) * (uprv_strlen(filename) + 1));
if(basename == 0) {
*status = U_MEMORY_ALLOCATION_ERROR;
goto finish;
}
get_basename(basename, filename);
dirname = (char*) uprv_malloc(sizeof(char) * (uprv_strlen(filename) + 1));
if(dirname == 0) {
*status = U_MEMORY_ALLOCATION_ERROR;
goto finish;
}
get_dirname(dirname, filename);
if (outputDir == NULL) {
/* output in same dir as .txt */
resName = (char*) uprv_malloc(sizeof(char) * (uprv_strlen(dirname)
+ pkgLen
+ uprv_strlen(basename)
+ uprv_strlen(RES_SUFFIX) + 8));
if(resName == 0) {
*status = U_MEMORY_ALLOCATION_ERROR;
goto finish;
}
uprv_strcpy(resName, dirname);
if(packageName != NULL)
{
uprv_strcat(resName, packageName);
uprv_strcat(resName, "_");
}
uprv_strcat(resName, basename);
} else {
int32_t dirlen = (int32_t)uprv_strlen(outputDir);
int32_t basenamelen = (int32_t)uprv_strlen(basename);
resName = (char*) uprv_malloc(sizeof(char) * (dirlen + pkgLen + basenamelen + 8));
if (resName == NULL) {
*status = U_MEMORY_ALLOCATION_ERROR;
goto finish;
}
uprv_strcpy(resName, outputDir);
if(outputDir[dirlen] != U_FILE_SEP_CHAR) {
resName[dirlen] = U_FILE_SEP_CHAR;
resName[dirlen + 1] = '\0';
}
if(packageName != NULL)
{
uprv_strcat(resName, packageName);
uprv_strcat(resName, "_");
}
uprv_strcat(resName, basename);
}
finish:
uprv_free(basename);
uprv_free(dirname);
return resName;
}
/*
* Local Variables:
* indent-tabs-mode: nil
* End:
*/