michael@0: /* michael@0: ******************************************************************************* michael@0: * michael@0: * Copyright (C) 2003-2012, International Business Machines michael@0: * Corporation and others. All Rights Reserved. michael@0: * michael@0: ******************************************************************************* michael@0: * file name: gensprep.c michael@0: * encoding: US-ASCII michael@0: * tab size: 8 (not used) michael@0: * indentation:4 michael@0: * michael@0: * created on: 2003-02-06 michael@0: * created by: Ram Viswanadha michael@0: * michael@0: * This program reads the Profile.txt files, michael@0: * parses them, and extracts the data for StringPrep profile. michael@0: * It then preprocesses it and writes a binary file for efficient use michael@0: * in various StringPrep conversion processes. michael@0: */ michael@0: michael@0: #define USPREP_TYPE_NAMES_ARRAY 1 michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "cmemory.h" michael@0: #include "cstring.h" michael@0: #include "unewdata.h" michael@0: #include "uoptions.h" michael@0: #include "uparse.h" michael@0: #include "sprpimpl.h" michael@0: michael@0: #include "unicode/uclean.h" michael@0: #include "unicode/udata.h" michael@0: #include "unicode/utypes.h" michael@0: #include "unicode/putil.h" michael@0: michael@0: michael@0: U_CDECL_BEGIN michael@0: #include "gensprep.h" michael@0: U_CDECL_END michael@0: michael@0: UBool beVerbose=FALSE, haveCopyright=TRUE; michael@0: michael@0: #define NORM_CORRECTIONS_FILE_NAME "NormalizationCorrections.txt" michael@0: michael@0: #define NORMALIZE_DIRECTIVE "normalize" michael@0: #define NORMALIZE_DIRECTIVE_LEN 9 michael@0: #define CHECK_BIDI_DIRECTIVE "check-bidi" michael@0: #define CHECK_BIDI_DIRECTIVE_LEN 10 michael@0: michael@0: /* prototypes --------------------------------------------------------------- */ michael@0: michael@0: static void michael@0: parseMappings(const char *filename, UBool reportError, UErrorCode *pErrorCode); michael@0: michael@0: static void michael@0: parseNormalizationCorrections(const char *filename, UErrorCode *pErrorCode); michael@0: michael@0: michael@0: /* -------------------------------------------------------------------------- */ michael@0: michael@0: static UOption options[]={ michael@0: UOPTION_HELP_H, michael@0: UOPTION_HELP_QUESTION_MARK, michael@0: UOPTION_VERBOSE, michael@0: UOPTION_COPYRIGHT, michael@0: UOPTION_DESTDIR, michael@0: UOPTION_SOURCEDIR, michael@0: UOPTION_ICUDATADIR, michael@0: UOPTION_BUNDLE_NAME, michael@0: { "normalization", NULL, NULL, NULL, 'n', UOPT_REQUIRES_ARG, 0 }, michael@0: { "norm-correction", NULL, NULL, NULL, 'm', UOPT_REQUIRES_ARG, 0 }, michael@0: { "check-bidi", NULL, NULL, NULL, 'k', UOPT_NO_ARG, 0}, michael@0: { "unicode", NULL, NULL, NULL, 'u', UOPT_REQUIRES_ARG, 0 }, michael@0: }; michael@0: michael@0: enum{ michael@0: HELP, michael@0: HELP_QUESTION_MARK, michael@0: VERBOSE, michael@0: COPYRIGHT, michael@0: DESTDIR, michael@0: SOURCEDIR, michael@0: ICUDATADIR, michael@0: BUNDLE_NAME, michael@0: NORMALIZE, michael@0: NORM_CORRECTION_DIR, michael@0: CHECK_BIDI, michael@0: UNICODE_VERSION michael@0: }; michael@0: michael@0: static int printHelp(int argc, char* argv[]){ michael@0: /* michael@0: * Broken into chucks because the C89 standard says the minimum michael@0: * required supported string length is 509 bytes. michael@0: */ michael@0: fprintf(stderr, michael@0: "Usage: %s [-options] [file_name]\n" michael@0: "\n" michael@0: "Read the files specified and\n" michael@0: "create a binary file [package-name]_[bundle-name]." DATA_TYPE " with the StringPrep profile data\n" michael@0: "\n", michael@0: argv[0]); michael@0: fprintf(stderr, michael@0: "Options:\n" michael@0: "\t-h or -? or --help print this usage text\n" michael@0: "\t-v or --verbose verbose output\n" michael@0: "\t-c or --copyright include a copyright notice\n"); michael@0: fprintf(stderr, michael@0: "\t-d or --destdir destination directory, followed by the path\n" michael@0: "\t-s or --sourcedir source directory of ICU data, followed by the path\n" michael@0: "\t-b or --bundle-name generate the ouput data file with the name specified\n" michael@0: "\t-i or --icudatadir directory for locating any needed intermediate data files,\n" michael@0: "\t followed by path, defaults to %s\n", michael@0: u_getDataDirectory()); michael@0: fprintf(stderr, michael@0: "\t-n or --normalize turn on the option for normalization and include mappings\n" michael@0: "\t from NormalizationCorrections.txt from the given path,\n" michael@0: "\t e.g: /test/icu/source/data/unidata\n"); michael@0: fprintf(stderr, michael@0: "\t-m or --norm-correction use NormalizationCorrections.txt from the given path\n" michael@0: "\t when the input file contains a normalization directive.\n" michael@0: "\t unlike -n/--normalize, this option does not force the\n" michael@0: "\t normalization.\n"); michael@0: fprintf(stderr, michael@0: "\t-k or --check-bidi turn on the option for checking for BiDi in the profile\n" michael@0: "\t-u or --unicode version of Unicode to be used with this profile followed by the version\n" michael@0: ); michael@0: return argc<0 ? U_ILLEGAL_ARGUMENT_ERROR : U_ZERO_ERROR; michael@0: } michael@0: michael@0: michael@0: extern int michael@0: main(int argc, char* argv[]) { michael@0: #if !UCONFIG_NO_IDNA michael@0: char* filename = NULL; michael@0: #endif michael@0: const char *srcDir=NULL, *destDir=NULL, *icuUniDataDir=NULL; michael@0: const char *bundleName=NULL, *inputFileName = NULL; michael@0: char *basename=NULL; michael@0: int32_t sprepOptions = 0; michael@0: michael@0: UErrorCode errorCode=U_ZERO_ERROR; michael@0: michael@0: U_MAIN_INIT_ARGS(argc, argv); michael@0: michael@0: /* preset then read command line options */ michael@0: options[DESTDIR].value=u_getDataDirectory(); michael@0: options[SOURCEDIR].value=""; michael@0: options[UNICODE_VERSION].value="0"; /* don't assume the unicode version */ michael@0: options[BUNDLE_NAME].value = DATA_NAME; michael@0: options[NORMALIZE].value = ""; michael@0: michael@0: argc=u_parseArgs(argc, argv, sizeof(options)/sizeof(options[0]), options); michael@0: michael@0: /* error handling, printing usage message */ michael@0: if(argc<0) { michael@0: fprintf(stderr, michael@0: "error in command line argument \"%s\"\n", michael@0: argv[-argc]); michael@0: } michael@0: if(argc<0 || options[HELP].doesOccur || options[HELP_QUESTION_MARK].doesOccur) { michael@0: return printHelp(argc, argv); michael@0: michael@0: } michael@0: michael@0: /* get the options values */ michael@0: beVerbose=options[VERBOSE].doesOccur; michael@0: haveCopyright=options[COPYRIGHT].doesOccur; michael@0: srcDir=options[SOURCEDIR].value; michael@0: destDir=options[DESTDIR].value; michael@0: bundleName = options[BUNDLE_NAME].value; michael@0: if(options[NORMALIZE].doesOccur) { michael@0: icuUniDataDir = options[NORMALIZE].value; michael@0: } else { michael@0: icuUniDataDir = options[NORM_CORRECTION_DIR].value; michael@0: } michael@0: michael@0: if(argc<2) { michael@0: /* print the help message */ michael@0: return printHelp(argc, argv); michael@0: } else { michael@0: inputFileName = argv[1]; michael@0: } michael@0: if(!options[UNICODE_VERSION].doesOccur){ michael@0: return printHelp(argc, argv); michael@0: } michael@0: if(options[ICUDATADIR].doesOccur) { michael@0: u_setDataDirectory(options[ICUDATADIR].value); michael@0: } michael@0: #if UCONFIG_NO_IDNA michael@0: michael@0: fprintf(stderr, michael@0: "gensprep writes dummy " U_ICUDATA_NAME "_" DATA_NAME "." DATA_TYPE michael@0: " because UCONFIG_NO_IDNA is set, \n" michael@0: "see icu/source/common/unicode/uconfig.h\n"); michael@0: generateData(destDir, bundleName); michael@0: michael@0: #else michael@0: michael@0: setUnicodeVersion(options[UNICODE_VERSION].value); michael@0: filename = (char* ) uprv_malloc(uprv_strlen(srcDir) + 300); /* hopefully this should be enough */ michael@0: michael@0: /* prepare the filename beginning with the source dir */ michael@0: if(uprv_strchr(srcDir,U_FILE_SEP_CHAR) == NULL && uprv_strchr(srcDir,U_FILE_ALT_SEP_CHAR) == NULL){ michael@0: filename[0] = '.'; michael@0: filename[1] = U_FILE_SEP_CHAR; michael@0: uprv_strcpy(filename+2,srcDir); michael@0: }else{ michael@0: uprv_strcpy(filename, srcDir); michael@0: } michael@0: michael@0: basename=filename+uprv_strlen(filename); michael@0: if(basename>filename && *(basename-1)!=U_FILE_SEP_CHAR) { michael@0: *basename++=U_FILE_SEP_CHAR; michael@0: } michael@0: michael@0: /* initialize */ michael@0: init(); michael@0: michael@0: /* process the file */ michael@0: uprv_strcpy(basename,inputFileName); michael@0: parseMappings(filename,FALSE, &errorCode); michael@0: if(U_FAILURE(errorCode)) { michael@0: fprintf(stderr, "Could not open file %s for reading. Error: %s \n", filename, u_errorName(errorCode)); michael@0: return errorCode; michael@0: } michael@0: michael@0: if(options[NORMALIZE].doesOccur){ /* this option might be set by @normalize;; in the source file */ michael@0: /* set up directory for NormalizationCorrections.txt */ michael@0: uprv_strcpy(filename,icuUniDataDir); michael@0: basename=filename+uprv_strlen(filename); michael@0: if(basename>filename && *(basename-1)!=U_FILE_SEP_CHAR) { michael@0: *basename++=U_FILE_SEP_CHAR; michael@0: } michael@0: michael@0: *basename++=U_FILE_SEP_CHAR; michael@0: uprv_strcpy(basename,NORM_CORRECTIONS_FILE_NAME); michael@0: michael@0: parseNormalizationCorrections(filename,&errorCode); michael@0: if(U_FAILURE(errorCode)){ michael@0: fprintf(stderr,"Could not open file %s for reading \n", filename); michael@0: return errorCode; michael@0: } michael@0: sprepOptions |= _SPREP_NORMALIZATION_ON; michael@0: } michael@0: michael@0: if(options[CHECK_BIDI].doesOccur){ /* this option might be set by @check-bidi;; in the source file */ michael@0: sprepOptions |= _SPREP_CHECK_BIDI_ON; michael@0: } michael@0: michael@0: setOptions(sprepOptions); michael@0: michael@0: /* process parsed data */ michael@0: if(U_SUCCESS(errorCode)) { michael@0: /* write the data file */ michael@0: generateData(destDir, bundleName); michael@0: michael@0: cleanUpData(); michael@0: } michael@0: michael@0: uprv_free(filename); michael@0: michael@0: u_cleanup(); michael@0: michael@0: #endif michael@0: michael@0: return errorCode; michael@0: } michael@0: michael@0: #if !UCONFIG_NO_IDNA michael@0: michael@0: static void U_CALLCONV michael@0: normalizationCorrectionsLineFn(void *context, michael@0: char *fields[][2], int32_t fieldCount, michael@0: UErrorCode *pErrorCode) { michael@0: uint32_t mapping[40]; michael@0: char *end, *s; michael@0: uint32_t code; michael@0: int32_t length; michael@0: UVersionInfo version; michael@0: UVersionInfo thisVersion; michael@0: michael@0: /* get the character code, field 0 */ michael@0: code=(uint32_t)uprv_strtoul(fields[0][0], &end, 16); michael@0: if(U_FAILURE(*pErrorCode)) { michael@0: fprintf(stderr, "gensprep: error parsing NormalizationCorrections.txt mapping at %s\n", fields[0][0]); michael@0: exit(*pErrorCode); michael@0: } michael@0: /* Original (erroneous) decomposition */ michael@0: s = fields[1][0]; michael@0: michael@0: /* parse the mapping string */ michael@0: length=u_parseCodePoints(s, mapping, sizeof(mapping)/4, pErrorCode); michael@0: michael@0: /* ignore corrected decomposition */ michael@0: michael@0: u_versionFromString(version,fields[3][0] ); michael@0: u_versionFromString(thisVersion, "3.2.0"); michael@0: michael@0: michael@0: michael@0: if(U_FAILURE(*pErrorCode)) { michael@0: fprintf(stderr, "gensprep error parsing NormalizationCorrections.txt of U+%04lx - %s\n", michael@0: (long)code, u_errorName(*pErrorCode)); michael@0: exit(*pErrorCode); michael@0: } michael@0: michael@0: /* store the mapping */ michael@0: if( version[0] > thisVersion[0] || michael@0: ((version[0]==thisVersion[0]) && (version[1] > thisVersion[1])) michael@0: ){ michael@0: storeMapping(code,mapping, length, USPREP_MAP, pErrorCode); michael@0: } michael@0: setUnicodeVersionNC(version); michael@0: } michael@0: michael@0: static void michael@0: parseNormalizationCorrections(const char *filename, UErrorCode *pErrorCode) { michael@0: char *fields[4][2]; michael@0: michael@0: if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { michael@0: return; michael@0: } michael@0: michael@0: u_parseDelimitedFile(filename, ';', fields, 4, normalizationCorrectionsLineFn, NULL, pErrorCode); michael@0: michael@0: /* fprintf(stdout,"Number of code points that have NormalizationCorrections mapping with length >1 : %i\n",len); */ michael@0: michael@0: if(U_FAILURE(*pErrorCode) && ( *pErrorCode!=U_FILE_ACCESS_ERROR)) { michael@0: fprintf(stderr, "gensprep error: u_parseDelimitedFile(\"%s\") failed - %s\n", filename, u_errorName(*pErrorCode)); michael@0: exit(*pErrorCode); michael@0: } michael@0: } michael@0: michael@0: static void U_CALLCONV michael@0: strprepProfileLineFn(void *context, michael@0: char *fields[][2], int32_t fieldCount, michael@0: UErrorCode *pErrorCode) { michael@0: uint32_t mapping[40]; michael@0: char *end, *map; michael@0: uint32_t code; michael@0: int32_t length; michael@0: /*UBool* mapWithNorm = (UBool*) context;*/ michael@0: const char* typeName; michael@0: uint32_t rangeStart=0,rangeEnd =0; michael@0: const char* filename = (const char*) context; michael@0: const char *s; michael@0: michael@0: s = u_skipWhitespace(fields[0][0]); michael@0: if (*s == '@') { michael@0: /* special directive */ michael@0: s++; michael@0: length = fields[0][1] - s; michael@0: if (length >= NORMALIZE_DIRECTIVE_LEN michael@0: && uprv_strncmp(s, NORMALIZE_DIRECTIVE, NORMALIZE_DIRECTIVE_LEN) == 0) { michael@0: options[NORMALIZE].doesOccur = TRUE; michael@0: return; michael@0: } michael@0: else if (length >= CHECK_BIDI_DIRECTIVE_LEN michael@0: && uprv_strncmp(s, CHECK_BIDI_DIRECTIVE, CHECK_BIDI_DIRECTIVE_LEN) == 0) { michael@0: options[CHECK_BIDI].doesOccur = TRUE; michael@0: return; michael@0: } michael@0: else { michael@0: fprintf(stderr, "gensprep error parsing a directive %s.", fields[0][0]); michael@0: } michael@0: } michael@0: michael@0: typeName = fields[2][0]; michael@0: map = fields[1][0]; michael@0: michael@0: if(uprv_strstr(typeName, usprepTypeNames[USPREP_UNASSIGNED])!=NULL){ michael@0: michael@0: u_parseCodePointRange(s, &rangeStart,&rangeEnd, pErrorCode); michael@0: if(U_FAILURE(*pErrorCode)){ michael@0: fprintf(stderr, "Could not parse code point range. Error: %s\n",u_errorName(*pErrorCode)); michael@0: return; michael@0: } michael@0: michael@0: /* store the range */ michael@0: storeRange(rangeStart,rangeEnd,USPREP_UNASSIGNED, pErrorCode); michael@0: michael@0: }else if(uprv_strstr(typeName, usprepTypeNames[USPREP_PROHIBITED])!=NULL){ michael@0: michael@0: u_parseCodePointRange(s, &rangeStart,&rangeEnd, pErrorCode); michael@0: if(U_FAILURE(*pErrorCode)){ michael@0: fprintf(stderr, "Could not parse code point range. Error: %s\n",u_errorName(*pErrorCode)); michael@0: return; michael@0: } michael@0: michael@0: /* store the range */ michael@0: storeRange(rangeStart,rangeEnd,USPREP_PROHIBITED, pErrorCode); michael@0: michael@0: }else if(uprv_strstr(typeName, usprepTypeNames[USPREP_MAP])!=NULL){ michael@0: michael@0: /* get the character code, field 0 */ michael@0: code=(uint32_t)uprv_strtoul(s, &end, 16); michael@0: if(end<=s || end!=fields[0][1]) { michael@0: fprintf(stderr, "gensprep: syntax error in field 0 at %s\n", fields[0][0]); michael@0: *pErrorCode=U_PARSE_ERROR; michael@0: exit(U_PARSE_ERROR); michael@0: } michael@0: michael@0: /* parse the mapping string */ michael@0: length=u_parseCodePoints(map, mapping, sizeof(mapping)/4, pErrorCode); michael@0: michael@0: /* store the mapping */ michael@0: storeMapping(code,mapping, length,USPREP_MAP, pErrorCode); michael@0: michael@0: }else{ michael@0: *pErrorCode = U_INVALID_FORMAT_ERROR; michael@0: } michael@0: michael@0: if(U_FAILURE(*pErrorCode)) { michael@0: fprintf(stderr, "gensprep error parsing %s line %s at %s. Error: %s\n",filename, michael@0: fields[0][0],fields[2][0],u_errorName(*pErrorCode)); michael@0: exit(*pErrorCode); michael@0: } michael@0: michael@0: } michael@0: michael@0: static void michael@0: parseMappings(const char *filename, UBool reportError, UErrorCode *pErrorCode) { michael@0: char *fields[3][2]; michael@0: michael@0: if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { michael@0: return; michael@0: } michael@0: michael@0: u_parseDelimitedFile(filename, ';', fields, 3, strprepProfileLineFn, (void*)filename, pErrorCode); michael@0: michael@0: /*fprintf(stdout,"Number of code points that have mappings with length >1 : %i\n",len);*/ michael@0: michael@0: if(U_FAILURE(*pErrorCode) && (reportError || *pErrorCode!=U_FILE_ACCESS_ERROR)) { michael@0: fprintf(stderr, "gensprep error: u_parseDelimitedFile(\"%s\") failed - %s\n", filename, u_errorName(*pErrorCode)); michael@0: exit(*pErrorCode); michael@0: } michael@0: } michael@0: michael@0: michael@0: #endif /* #if !UCONFIG_NO_IDNA */ michael@0: michael@0: /* michael@0: * Hey, Emacs, please set the following: michael@0: * michael@0: * Local Variables: michael@0: * indent-tabs-mode: nil michael@0: * End: michael@0: * michael@0: */