• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 *
6 *   Copyright (C) 2003-2016, International Business Machines
7 *   Corporation and others.  All Rights Reserved.
8 *
9 *******************************************************************************
10 *   file name:  gensprep.c
11 *   encoding:   UTF-8
12 *   tab size:   8 (not used)
13 *   indentation:4
14 *
15 *   created on: 2003-02-06
16 *   created by: Ram Viswanadha
17 *
18 *   This program reads the Profile.txt files,
19 *   parses them, and extracts the data for StringPrep profile.
20 *   It then preprocesses it and writes a binary file for efficient use
21 *   in various StringPrep conversion processes.
22 */
23 
24 #define USPREP_TYPE_NAMES_ARRAY 1
25 
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 
30 #include "cmemory.h"
31 #include "cstring.h"
32 #include "toolutil.h"
33 #include "unewdata.h"
34 #include "uoptions.h"
35 #include "uparse.h"
36 #include "sprpimpl.h"
37 
38 #include "unicode/uclean.h"
39 #include "unicode/udata.h"
40 #include "unicode/utypes.h"
41 #include "unicode/putil.h"
42 
43 
44 U_CDECL_BEGIN
45 #include "gensprep.h"
46 U_CDECL_END
47 
48 UBool beVerbose=false, haveCopyright=true;
49 
50 #define NORM_CORRECTIONS_FILE_NAME "NormalizationCorrections.txt"
51 
52 #define NORMALIZE_DIRECTIVE "normalize"
53 #define NORMALIZE_DIRECTIVE_LEN 9
54 #define CHECK_BIDI_DIRECTIVE "check-bidi"
55 #define CHECK_BIDI_DIRECTIVE_LEN 10
56 
57 /* prototypes --------------------------------------------------------------- */
58 
59 static void
60 parseMappings(const char *filename, UBool reportError, UErrorCode *pErrorCode);
61 
62 static void
63 parseNormalizationCorrections(const char *filename, UErrorCode *pErrorCode);
64 
65 
66 /* -------------------------------------------------------------------------- */
67 
68 static UOption options[]={
69     UOPTION_HELP_H,
70     UOPTION_HELP_QUESTION_MARK,
71     UOPTION_VERBOSE,
72     UOPTION_COPYRIGHT,
73     UOPTION_DESTDIR,
74     UOPTION_SOURCEDIR,
75     UOPTION_ICUDATADIR,
76     UOPTION_BUNDLE_NAME,
77     { "normalization", NULL, NULL, NULL, 'n', UOPT_REQUIRES_ARG, 0 },
78     { "norm-correction", NULL, NULL, NULL, 'm', UOPT_REQUIRES_ARG, 0 },
79     { "check-bidi", NULL, NULL, NULL,  'k', UOPT_NO_ARG, 0},
80     { "unicode", NULL, NULL, NULL, 'u', UOPT_REQUIRES_ARG, 0 },
81 };
82 
83 enum{
84     HELP,
85     HELP_QUESTION_MARK,
86     VERBOSE,
87     COPYRIGHT,
88     DESTDIR,
89     SOURCEDIR,
90     ICUDATADIR,
91     BUNDLE_NAME,
92     NORMALIZE,
93     NORM_CORRECTION_DIR,
94     CHECK_BIDI,
95     UNICODE_VERSION
96 };
97 
printHelp(int argc,char * argv[])98 static int printHelp(int argc, char* argv[]){
99     /*
100      * Broken into chucks because the C89 standard says the minimum
101      * required supported string length is 509 bytes.
102      */
103     fprintf(stderr,
104         "Usage: %s [-options] [file_name]\n"
105         "\n"
106         "Read the files specified and\n"
107         "create a binary file [package-name]_[bundle-name]." DATA_TYPE " with the StringPrep profile data\n"
108         "\n",
109         argv[0]);
110     fprintf(stderr,
111         "Options:\n"
112         "\t-h or -? or --help       print this usage text\n"
113         "\t-v or --verbose          verbose output\n"
114         "\t-c or --copyright        include a copyright notice\n");
115     fprintf(stderr,
116         "\t-d or --destdir          destination directory, followed by the path\n"
117         "\t-s or --sourcedir        source directory of ICU data, followed by the path\n"
118         "\t-b or --bundle-name      generate the output data file with the name specified\n"
119         "\t-i or --icudatadir       directory for locating any needed intermediate data files,\n"
120         "\t                         followed by path, defaults to %s\n",
121         u_getDataDirectory());
122     fprintf(stderr,
123         "\t-n or --normalize        turn on the option for normalization and include mappings\n"
124         "\t                         from NormalizationCorrections.txt from the given path,\n"
125         "\t                         e.g: /test/icu/source/data/unidata\n");
126     fprintf(stderr,
127         "\t-m or --norm-correction  use NormalizationCorrections.txt from the given path\n"
128         "\t                         when the input file contains a normalization directive.\n"
129         "\t                         unlike -n/--normalize, this option does not force the\n"
130         "\t                         normalization.\n");
131     fprintf(stderr,
132         "\t-k or --check-bidi       turn on the option for checking for BiDi in the profile\n"
133         "\t-u or --unicode          version of Unicode to be used with this profile followed by the version\n"
134         );
135     return argc<0 ? U_ILLEGAL_ARGUMENT_ERROR : U_ZERO_ERROR;
136 }
137 
138 
139 extern int
main(int argc,char * argv[])140 main(int argc, char* argv[]) {
141 #if !UCONFIG_NO_IDNA
142     char* filename = NULL;
143 #endif
144     const char *srcDir=NULL, *destDir=NULL, *icuUniDataDir=NULL;
145     const char *bundleName=NULL, *inputFileName = NULL;
146     char *basename=NULL;
147     int32_t sprepOptions = 0;
148 
149     UErrorCode errorCode=U_ZERO_ERROR;
150 
151     U_MAIN_INIT_ARGS(argc, argv);
152 
153     /* preset then read command line options */
154     options[DESTDIR].value=u_getDataDirectory();
155     options[SOURCEDIR].value="";
156     options[UNICODE_VERSION].value="0"; /* don't assume the unicode version */
157     options[BUNDLE_NAME].value = DATA_NAME;
158     options[NORMALIZE].value = "";
159 
160     argc=u_parseArgs(argc, argv, UPRV_LENGTHOF(options), options);
161 
162     /* error handling, printing usage message */
163     if(argc<0) {
164         fprintf(stderr,
165             "error in command line argument \"%s\"\n",
166             argv[-argc]);
167     }
168     if(argc<0 || options[HELP].doesOccur || options[HELP_QUESTION_MARK].doesOccur) {
169         return printHelp(argc, argv);
170 
171     }
172 
173     /* get the options values */
174     beVerbose=options[VERBOSE].doesOccur;
175     haveCopyright=options[COPYRIGHT].doesOccur;
176     srcDir=options[SOURCEDIR].value;
177     destDir=options[DESTDIR].value;
178     bundleName = options[BUNDLE_NAME].value;
179     if(options[NORMALIZE].doesOccur) {
180         icuUniDataDir = options[NORMALIZE].value;
181     } else {
182         icuUniDataDir = options[NORM_CORRECTION_DIR].value;
183     }
184 
185     if(argc<2) {
186         /* print the help message */
187         return printHelp(argc, argv);
188     } else {
189         inputFileName = argv[1];
190     }
191     if(!options[UNICODE_VERSION].doesOccur){
192         return printHelp(argc, argv);
193     }
194     if(options[ICUDATADIR].doesOccur) {
195         u_setDataDirectory(options[ICUDATADIR].value);
196     }
197 #if UCONFIG_NO_IDNA
198 
199     fprintf(stderr,
200         "gensprep writes dummy " U_ICUDATA_NAME "_" DATA_NAME "." DATA_TYPE
201         " because UCONFIG_NO_IDNA is set, \n"
202         "see icu/source/common/unicode/uconfig.h\n");
203     generateData(destDir, bundleName);
204 
205 #else
206 
207     setUnicodeVersion(options[UNICODE_VERSION].value);
208     filename = (char* ) uprv_malloc(uprv_strlen(srcDir) + uprv_strlen(inputFileName) + (icuUniDataDir == NULL ? 0 : uprv_strlen(icuUniDataDir)) + 40); /* hopefully this should be enough */
209 
210     /* prepare the filename beginning with the source dir */
211     if(uprv_strchr(srcDir,U_FILE_SEP_CHAR) == NULL && uprv_strchr(srcDir,U_FILE_ALT_SEP_CHAR) == NULL){
212         filename[0] = '.';
213         filename[1] = U_FILE_SEP_CHAR;
214         uprv_strcpy(filename+2,srcDir);
215     }else{
216         uprv_strcpy(filename, srcDir);
217     }
218 
219     basename=filename+uprv_strlen(filename);
220     if(basename>filename && *(basename-1)!=U_FILE_SEP_CHAR) {
221         *basename++=U_FILE_SEP_CHAR;
222     }
223 
224     /* initialize */
225     init();
226 
227     /* process the file */
228     uprv_strcpy(basename,inputFileName);
229     parseMappings(filename,false, &errorCode);
230     if(U_FAILURE(errorCode)) {
231         fprintf(stderr, "Could not open file %s for reading. Error: %s \n", filename, u_errorName(errorCode));
232         return errorCode;
233     }
234 
235     if(options[NORMALIZE].doesOccur){ /* this option might be set by @normalize;; in the source file */
236         /* set up directory for NormalizationCorrections.txt */
237         uprv_strcpy(filename,icuUniDataDir);
238         basename=filename+uprv_strlen(filename);
239         if(basename>filename && *(basename-1)!=U_FILE_SEP_CHAR) {
240             *basename++=U_FILE_SEP_CHAR;
241         }
242 
243         *basename++=U_FILE_SEP_CHAR;
244         uprv_strcpy(basename,NORM_CORRECTIONS_FILE_NAME);
245 
246         parseNormalizationCorrections(filename,&errorCode);
247         if(U_FAILURE(errorCode)){
248             fprintf(stderr,"Could not open file %s for reading \n", filename);
249             return errorCode;
250         }
251         sprepOptions |= _SPREP_NORMALIZATION_ON;
252     }
253 
254     if(options[CHECK_BIDI].doesOccur){ /* this option might be set by @check-bidi;; in the source file */
255         sprepOptions |= _SPREP_CHECK_BIDI_ON;
256     }
257 
258     setOptions(sprepOptions);
259 
260     /* process parsed data */
261     if(U_SUCCESS(errorCode)) {
262         /* write the data file */
263         generateData(destDir, bundleName);
264 
265         cleanUpData();
266     }
267 
268     uprv_free(filename);
269 
270     u_cleanup();
271 
272 #endif
273 
274     return errorCode;
275 }
276 
277 #if !UCONFIG_NO_IDNA
278 
279 static void U_CALLCONV
normalizationCorrectionsLineFn(void * context,char * fields[][2],int32_t fieldCount,UErrorCode * pErrorCode)280 normalizationCorrectionsLineFn(void *context,
281                     char *fields[][2], int32_t fieldCount,
282                     UErrorCode *pErrorCode) {
283     (void)context; // suppress compiler warnings about unused variable
284     (void)fieldCount; // suppress compiler warnings about unused variable
285     uint32_t mapping[40];
286     char *end, *s;
287     uint32_t code;
288     int32_t length;
289     UVersionInfo version;
290     UVersionInfo thisVersion;
291 
292     /* get the character code, field 0 */
293     code=(uint32_t)uprv_strtoul(fields[0][0], &end, 16);
294     if(U_FAILURE(*pErrorCode)) {
295         fprintf(stderr, "gensprep: error parsing NormalizationCorrections.txt mapping at %s\n", fields[0][0]);
296         exit(*pErrorCode);
297     }
298     /* Original (erroneous) decomposition */
299     s = fields[1][0];
300 
301     /* parse the mapping string */
302     length=u_parseCodePoints(s, mapping, sizeof(mapping)/4, pErrorCode);
303 
304     /* ignore corrected decomposition */
305 
306     u_versionFromString(version,fields[3][0] );
307     u_versionFromString(thisVersion, "3.2.0");
308 
309 
310 
311     if(U_FAILURE(*pErrorCode)) {
312         fprintf(stderr, "gensprep error parsing NormalizationCorrections.txt of U+%04lx - %s\n",
313                 (long)code, u_errorName(*pErrorCode));
314         exit(*pErrorCode);
315     }
316 
317     /* store the mapping */
318     if( version[0] > thisVersion[0] ||
319         ((version[0]==thisVersion[0]) && (version[1] > thisVersion[1]))
320         ){
321         storeMapping(code,mapping, length, USPREP_MAP, pErrorCode);
322     }
323     setUnicodeVersionNC(version);
324 }
325 
326 static void
parseNormalizationCorrections(const char * filename,UErrorCode * pErrorCode)327 parseNormalizationCorrections(const char *filename, UErrorCode *pErrorCode) {
328     char *fields[4][2];
329 
330     if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
331         return;
332     }
333 
334     u_parseDelimitedFile(filename, ';', fields, 4, normalizationCorrectionsLineFn, NULL, pErrorCode);
335 
336     /* fprintf(stdout,"Number of code points that have NormalizationCorrections mapping with length >1 : %i\n",len); */
337 
338     if(U_FAILURE(*pErrorCode) && ( *pErrorCode!=U_FILE_ACCESS_ERROR)) {
339         fprintf(stderr, "gensprep error: u_parseDelimitedFile(\"%s\") failed - %s\n", filename, u_errorName(*pErrorCode));
340         exit(*pErrorCode);
341     }
342 }
343 
344 static void U_CALLCONV
strprepProfileLineFn(void * context,char * fields[][2],int32_t fieldCount,UErrorCode * pErrorCode)345 strprepProfileLineFn(void *context,
346               char *fields[][2], int32_t fieldCount,
347               UErrorCode *pErrorCode) {
348     (void)fieldCount; // suppress compiler warnings about unused variable
349     uint32_t mapping[40];
350     char *end, *map;
351     uint32_t code;
352     int32_t length;
353    /*UBool* mapWithNorm = (UBool*) context;*/
354     const char* typeName;
355     uint32_t rangeStart=0,rangeEnd =0;
356     const char* filename = (const char*) context;
357     const char *s;
358 
359     s = u_skipWhitespace(fields[0][0]);
360     if (*s == '@') {
361         /* special directive */
362         s++;
363         length = (int32_t)(fields[0][1] - s);
364         if (length >= NORMALIZE_DIRECTIVE_LEN
365             && uprv_strncmp(s, NORMALIZE_DIRECTIVE, NORMALIZE_DIRECTIVE_LEN) == 0) {
366             options[NORMALIZE].doesOccur = true;
367             return;
368         }
369         else if (length >= CHECK_BIDI_DIRECTIVE_LEN
370             && uprv_strncmp(s, CHECK_BIDI_DIRECTIVE, CHECK_BIDI_DIRECTIVE_LEN) == 0) {
371             options[CHECK_BIDI].doesOccur = true;
372             return;
373         }
374         else {
375             fprintf(stderr, "gensprep error parsing a directive %s.", fields[0][0]);
376         }
377     }
378 
379     typeName = fields[2][0];
380     map = fields[1][0];
381 
382     if(uprv_strstr(typeName, usprepTypeNames[USPREP_UNASSIGNED])!=NULL){
383 
384         u_parseCodePointRange(s, &rangeStart,&rangeEnd, pErrorCode);
385         if(U_FAILURE(*pErrorCode)){
386             fprintf(stderr, "Could not parse code point range. Error: %s\n",u_errorName(*pErrorCode));
387             return;
388         }
389 
390         /* store the range */
391         storeRange(rangeStart,rangeEnd,USPREP_UNASSIGNED, pErrorCode);
392 
393     }else if(uprv_strstr(typeName, usprepTypeNames[USPREP_PROHIBITED])!=NULL){
394 
395         u_parseCodePointRange(s, &rangeStart,&rangeEnd, pErrorCode);
396         if(U_FAILURE(*pErrorCode)){
397             fprintf(stderr, "Could not parse code point range. Error: %s\n",u_errorName(*pErrorCode));
398             return;
399         }
400 
401         /* store the range */
402         storeRange(rangeStart,rangeEnd,USPREP_PROHIBITED, pErrorCode);
403 
404     }else if(uprv_strstr(typeName, usprepTypeNames[USPREP_MAP])!=NULL){
405 
406         /* get the character code, field 0 */
407         code=(uint32_t)uprv_strtoul(s, &end, 16);
408         if(end<=s || end!=fields[0][1]) {
409             fprintf(stderr, "gensprep: syntax error in field 0 at %s\n", fields[0][0]);
410             *pErrorCode=U_PARSE_ERROR;
411             exit(U_PARSE_ERROR);
412         }
413 
414         /* parse the mapping string */
415         length=u_parseCodePoints(map, mapping, sizeof(mapping)/4, pErrorCode);
416 
417         /* store the mapping */
418         storeMapping(code,mapping, length,USPREP_MAP, pErrorCode);
419 
420     }else{
421         *pErrorCode = U_INVALID_FORMAT_ERROR;
422     }
423 
424     if(U_FAILURE(*pErrorCode)) {
425         fprintf(stderr, "gensprep error parsing  %s line %s at %s. Error: %s\n",filename,
426                fields[0][0],fields[2][0],u_errorName(*pErrorCode));
427         exit(*pErrorCode);
428     }
429 
430 }
431 
432 static void
parseMappings(const char * filename,UBool reportError,UErrorCode * pErrorCode)433 parseMappings(const char *filename, UBool reportError, UErrorCode *pErrorCode) {
434     char *fields[3][2];
435 
436     if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
437         return;
438     }
439 
440     u_parseDelimitedFile(filename, ';', fields, 3, strprepProfileLineFn, (void*)filename, pErrorCode);
441 
442     /*fprintf(stdout,"Number of code points that have mappings with length >1 : %i\n",len);*/
443 
444     if(U_FAILURE(*pErrorCode) && (reportError || *pErrorCode!=U_FILE_ACCESS_ERROR)) {
445         fprintf(stderr, "gensprep error: u_parseDelimitedFile(\"%s\") failed - %s\n", filename, u_errorName(*pErrorCode));
446         exit(*pErrorCode);
447     }
448 }
449 
450 
451 #endif /* #if !UCONFIG_NO_IDNA */
452 
453 /*
454  * Hey, Emacs, please set the following:
455  *
456  * Local Variables:
457  * indent-tabs-mode: nil
458  * End:
459  *
460  */
461