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