]> git.saurik.com Git - apple/icu.git/blame - icuSources/tools/genbidi/genbidi.c
ICU-8.11.tar.gz
[apple/icu.git] / icuSources / tools / genbidi / genbidi.c
CommitLineData
73c04bcf
A
1/*
2*******************************************************************************
3*
4* Copyright (C) 2004-2006, International Business Machines
5* Corporation and others. All Rights Reserved.
6*
7*******************************************************************************
8* file name: genbidi.c
9* encoding: US-ASCII
10* tab size: 8 (not used)
11* indentation:4
12*
13* created on: 2004dec30
14* created by: Markus W. Scherer
15*
16* This program reads several of the Unicode character database text files,
17* parses them, and extracts the bidi/shaping properties for each character.
18* It then writes a binary file containing the properties
19* that is designed to be used directly for random-access to
20* the properties of each Unicode character.
21*/
22
23#include <stdio.h>
24#include "unicode/utypes.h"
25#include "unicode/uchar.h"
26#include "unicode/putil.h"
27#include "unicode/uclean.h"
28#include "cmemory.h"
29#include "cstring.h"
30#include "uarrsort.h"
31#include "unewdata.h"
32#include "uoptions.h"
33#include "uparse.h"
34#include "propsvec.h"
35#include "ubidi_props.h"
36#include "genbidi.h"
37
38#define LENGTHOF(array) (sizeof(array)/sizeof((array)[0]))
39
40/* data --------------------------------------------------------------------- */
41
42uint32_t *pv;
43
44UBool beVerbose=FALSE, haveCopyright=TRUE;
45
46/* prototypes --------------------------------------------------------------- */
47
48static UBool
49isToken(const char *token, const char *s);
50
51static void
52parseBidiMirroring(const char *filename, UErrorCode *pErrorCode);
53
54static void
55parseDB(const char *filename, UErrorCode *pErrorCode);
56
57/* miscellaneous ------------------------------------------------------------ */
58
59/* TODO: more common code, move functions to uparse.h|c */
60
61static char *
62trimTerminateField(char *s, char *limit) {
63 /* trim leading whitespace */
64 s=(char *)u_skipWhitespace(s);
65
66 /* trim trailing whitespace */
67 while(s<limit && (*(limit-1)==' ' || *(limit-1)=='\t')) {
68 --limit;
69 }
70 *limit=0;
71
72 return s;
73}
74
75static void
76parseTwoFieldFile(char *filename, char *basename,
77 const char *ucdFile, const char *suffix,
78 UParseLineFn *lineFn,
79 UErrorCode *pErrorCode) {
80 char *fields[2][2];
81
82 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
83 return;
84 }
85
86 writeUCDFilename(basename, ucdFile, suffix);
87
88 u_parseDelimitedFile(filename, ';', fields, 2, lineFn, NULL, pErrorCode);
89 if(U_FAILURE(*pErrorCode)) {
90 fprintf(stderr, "error parsing %s.txt: %s\n", ucdFile, u_errorName(*pErrorCode));
91 }
92}
93
94static void U_CALLCONV
95bidiClassLineFn(void *context,
96 char *fields[][2], int32_t fieldCount,
97 UErrorCode *pErrorCode);
98
99/* parse files with single enumerated properties ---------------------------- */
100
101/* TODO: more common code, move functions to uparse.h|c */
102
103struct SingleEnum {
104 const char *ucdFile, *propName;
105 UProperty prop;
106 int32_t vecWord, vecShift;
107 uint32_t vecMask;
108};
109typedef struct SingleEnum SingleEnum;
110
111static void
112parseSingleEnumFile(char *filename, char *basename, const char *suffix,
113 const SingleEnum *sen,
114 UErrorCode *pErrorCode);
115
116static const SingleEnum jtSingleEnum={
117 "DerivedJoiningType", "joining type",
118 UCHAR_JOINING_TYPE,
119 0, UBIDI_JT_SHIFT, UBIDI_JT_MASK
120};
121
122static const SingleEnum jgSingleEnum={
123 "DerivedJoiningGroup", "joining group",
124 UCHAR_JOINING_GROUP,
125 1, 0, 0xff /* column 1 bits 7..0 */
126};
127
128static void U_CALLCONV
129singleEnumLineFn(void *context,
130 char *fields[][2], int32_t fieldCount,
131 UErrorCode *pErrorCode) {
132 const SingleEnum *sen;
133 char *s;
134 uint32_t start, limit, uv;
135 int32_t value;
136
137 sen=(const SingleEnum *)context;
138
139 u_parseCodePointRange(fields[0][0], &start, &limit, pErrorCode);
140 if(U_FAILURE(*pErrorCode)) {
141 fprintf(stderr, "genbidi: syntax error in %s.txt field 0 at %s\n", sen->ucdFile, fields[0][0]);
142 exit(*pErrorCode);
143 }
144 ++limit;
145
146 /* parse property alias */
147 s=trimTerminateField(fields[1][0], fields[1][1]);
148 value=u_getPropertyValueEnum(sen->prop, s);
149 if(value<0) {
150 if(sen->prop==UCHAR_BLOCK) {
151 if(isToken("Greek", s)) {
152 value=UBLOCK_GREEK; /* Unicode 3.2 renames this to "Greek and Coptic" */
153 } else if(isToken("Combining Marks for Symbols", s)) {
154 value=UBLOCK_COMBINING_MARKS_FOR_SYMBOLS; /* Unicode 3.2 renames this to "Combining Diacritical Marks for Symbols" */
155 } else if(isToken("Private Use", s)) {
156 value=UBLOCK_PRIVATE_USE; /* Unicode 3.2 renames this to "Private Use Area" */
157 }
158 }
159 }
160 if(value<0) {
161 fprintf(stderr, "genbidi error: unknown %s name in %s.txt field 1 at %s\n",
162 sen->propName, sen->ucdFile, s);
163 exit(U_PARSE_ERROR);
164 }
165
166 uv=(uint32_t)(value<<sen->vecShift);
167 if((uv&sen->vecMask)!=uv) {
168 fprintf(stderr, "genbidi error: %s value overflow (0x%x) at %s\n",
169 sen->propName, (int)uv, s);
170 exit(U_INTERNAL_PROGRAM_ERROR);
171 }
172
173 if(!upvec_setValue(pv, start, limit, sen->vecWord, uv, sen->vecMask, pErrorCode)) {
174 fprintf(stderr, "genbidi error: unable to set %s code: %s\n",
175 sen->propName, u_errorName(*pErrorCode));
176 exit(*pErrorCode);
177 }
178}
179
180static void
181parseSingleEnumFile(char *filename, char *basename, const char *suffix,
182 const SingleEnum *sen,
183 UErrorCode *pErrorCode) {
184 char *fields[2][2];
185
186 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
187 return;
188 }
189
190 writeUCDFilename(basename, sen->ucdFile, suffix);
191
192 u_parseDelimitedFile(filename, ';', fields, 2, singleEnumLineFn, (void *)sen, pErrorCode);
193 if(U_FAILURE(*pErrorCode)) {
194 fprintf(stderr, "error parsing %s.txt: %s\n", sen->ucdFile, u_errorName(*pErrorCode));
195 }
196}
197
198/* parse files with multiple binary properties ------------------------------ */
199
200/* TODO: more common code, move functions to uparse.h|c */
201
202/* TODO: similar to genbidi/props2.c but not the same; same as in gencase/gencase.c */
203
204struct Binary {
205 const char *propName;
206 int32_t vecWord;
207 uint32_t vecValue, vecMask;
208};
209typedef struct Binary Binary;
210
211struct Binaries {
212 const char *ucdFile;
213 const Binary *binaries;
214 int32_t binariesCount;
215};
216typedef struct Binaries Binaries;
217
218static const Binary
219propListNames[]={
220 { "Bidi_Control", 0, U_MASK(UBIDI_BIDI_CONTROL_SHIFT), U_MASK(UBIDI_BIDI_CONTROL_SHIFT) },
221 { "Join_Control", 0, U_MASK(UBIDI_JOIN_CONTROL_SHIFT), U_MASK(UBIDI_JOIN_CONTROL_SHIFT) }
222};
223
224static const Binaries
225propListBinaries={
226 "PropList", propListNames, LENGTHOF(propListNames)
227};
228
229static void U_CALLCONV
230binariesLineFn(void *context,
231 char *fields[][2], int32_t fieldCount,
232 UErrorCode *pErrorCode) {
233 const Binaries *bin;
234 char *s;
235 uint32_t start, limit;
236 int32_t i;
237
238 bin=(const Binaries *)context;
239
240 u_parseCodePointRange(fields[0][0], &start, &limit, pErrorCode);
241 if(U_FAILURE(*pErrorCode)) {
242 fprintf(stderr, "genbidi: syntax error in %s.txt field 0 at %s\n", bin->ucdFile, fields[0][0]);
243 exit(*pErrorCode);
244 }
245 ++limit;
246
247 /* parse binary property name */
248 s=(char *)u_skipWhitespace(fields[1][0]);
249 for(i=0;; ++i) {
250 if(i==bin->binariesCount) {
251 /* ignore unrecognized properties */
252 return;
253 }
254 if(isToken(bin->binaries[i].propName, s)) {
255 break;
256 }
257 }
258
259 if(bin->binaries[i].vecMask==0) {
260 fprintf(stderr, "genbidi error: mask value %d==0 for %s %s\n",
261 (int)bin->binaries[i].vecMask, bin->ucdFile, bin->binaries[i].propName);
262 exit(U_INTERNAL_PROGRAM_ERROR);
263 }
264
265 if(!upvec_setValue(pv, start, limit, bin->binaries[i].vecWord, bin->binaries[i].vecValue, bin->binaries[i].vecMask, pErrorCode)) {
266 fprintf(stderr, "genbidi error: unable to set %s, code: %s\n",
267 bin->binaries[i].propName, u_errorName(*pErrorCode));
268 exit(*pErrorCode);
269 }
270}
271
272static void
273parseBinariesFile(char *filename, char *basename, const char *suffix,
274 const Binaries *bin,
275 UErrorCode *pErrorCode) {
276 char *fields[2][2];
277
278 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
279 return;
280 }
281
282 writeUCDFilename(basename, bin->ucdFile, suffix);
283
284 u_parseDelimitedFile(filename, ';', fields, 2, binariesLineFn, (void *)bin, pErrorCode);
285 if(U_FAILURE(*pErrorCode)) {
286 fprintf(stderr, "error parsing %s.txt: %s\n", bin->ucdFile, u_errorName(*pErrorCode));
287 }
288}
289
290/* -------------------------------------------------------------------------- */
291
292enum {
293 HELP_H,
294 HELP_QUESTION_MARK,
295 VERBOSE,
296 COPYRIGHT,
297 DESTDIR,
298 SOURCEDIR,
299 UNICODE_VERSION,
300 ICUDATADIR,
301 CSOURCE
302};
303
304/* Keep these values in sync with the above enums */
305static UOption options[]={
306 UOPTION_HELP_H,
307 UOPTION_HELP_QUESTION_MARK,
308 UOPTION_VERBOSE,
309 UOPTION_COPYRIGHT,
310 UOPTION_DESTDIR,
311 UOPTION_SOURCEDIR,
312 UOPTION_DEF("unicode", 'u', UOPT_REQUIRES_ARG),
313 UOPTION_ICUDATADIR,
314 UOPTION_DEF("csource", 'C', UOPT_NO_ARG)
315};
316
317extern int
318main(int argc, char* argv[]) {
319 char filename[300];
320 const char *srcDir=NULL, *destDir=NULL, *suffix=NULL;
321 char *basename=NULL;
322 UErrorCode errorCode=U_ZERO_ERROR;
323
324 U_MAIN_INIT_ARGS(argc, argv);
325
326 /* preset then read command line options */
327 options[DESTDIR].value=u_getDataDirectory();
328 options[SOURCEDIR].value="";
329 options[UNICODE_VERSION].value="";
330 options[ICUDATADIR].value=u_getDataDirectory();
331 argc=u_parseArgs(argc, argv, sizeof(options)/sizeof(options[0]), options);
332
333 /* error handling, printing usage message */
334 if(argc<0) {
335 fprintf(stderr,
336 "error in command line argument \"%s\"\n",
337 argv[-argc]);
338 }
339 if(argc<0 || options[HELP_H].doesOccur || options[HELP_QUESTION_MARK].doesOccur) {
340 /*
341 * Broken into chucks because the C89 standard says the minimum
342 * required supported string length is 509 bytes.
343 */
344 fprintf(stderr,
345 "Usage: %s [-options] [suffix]\n"
346 "\n"
347 "read the UnicodeData.txt file and other Unicode properties files and\n"
348 "create a binary file " UBIDI_DATA_NAME "." UBIDI_DATA_TYPE " with the bidi/shaping properties\n"
349 "\n",
350 argv[0]);
351 fprintf(stderr,
352 "Options:\n"
353 "\t-h or -? or --help this usage text\n"
354 "\t-v or --verbose verbose output\n"
355 "\t-c or --copyright include a copyright notice\n"
356 "\t-u or --unicode Unicode version, followed by the version like 3.0.0\n"
357 "\t-C or --csource generate a .c source file rather than the .icu binary\n");
358 fprintf(stderr,
359 "\t-d or --destdir destination directory, followed by the path\n"
360 "\t-s or --sourcedir source directory, followed by the path\n"
361 "\t-i or --icudatadir directory for locating any needed intermediate data files,\n"
362 "\t followed by path, defaults to %s\n"
363 "\tsuffix suffix that is to be appended with a '-'\n"
364 "\t to the source file basenames before opening;\n"
365 "\t 'genbidi new' will read UnicodeData-new.txt etc.\n",
366 u_getDataDirectory());
367 return argc<0 ? U_ILLEGAL_ARGUMENT_ERROR : U_ZERO_ERROR;
368 }
369
370 /* get the options values */
371 beVerbose=options[VERBOSE].doesOccur;
372 haveCopyright=options[COPYRIGHT].doesOccur;
373 srcDir=options[SOURCEDIR].value;
374 destDir=options[DESTDIR].value;
375
376 if(argc>=2) {
377 suffix=argv[1];
378 } else {
379 suffix=NULL;
380 }
381
382 if(options[UNICODE_VERSION].doesOccur) {
383 setUnicodeVersion(options[UNICODE_VERSION].value);
384 }
385 /* else use the default dataVersion in store.c */
386
387 if (options[ICUDATADIR].doesOccur) {
388 u_setDataDirectory(options[ICUDATADIR].value);
389 }
390
391 /* prepare the filename beginning with the source dir */
392 uprv_strcpy(filename, srcDir);
393 basename=filename+uprv_strlen(filename);
394 if(basename>filename && *(basename-1)!=U_FILE_SEP_CHAR) {
395 *basename++=U_FILE_SEP_CHAR;
396 }
397
398 /* initialize */
399 pv=upvec_open(2, 10000);
400
401 /* process BidiMirroring.txt */
402 writeUCDFilename(basename, "BidiMirroring", suffix);
403 parseBidiMirroring(filename, &errorCode);
404
405 /* process additional properties files */
406 *basename=0;
407
408 parseBinariesFile(filename, basename, suffix, &propListBinaries, &errorCode);
409
410 parseSingleEnumFile(filename, basename, suffix, &jtSingleEnum, &errorCode);
411
412 parseSingleEnumFile(filename, basename, suffix, &jgSingleEnum, &errorCode);
413
414 /* process UnicodeData.txt */
415 writeUCDFilename(basename, "UnicodeData", suffix);
416 parseDB(filename, &errorCode);
417
418 /* set proper bidi class for unassigned code points (Cn) */
419 parseTwoFieldFile(filename, basename, "DerivedBidiClass", suffix, bidiClassLineFn, &errorCode);
420
421 /* process parsed data */
422 if(U_SUCCESS(errorCode)) {
423 /* write the properties data file */
424 generateData(destDir, options[CSOURCE].doesOccur);
425 }
426
427 u_cleanup();
428 return errorCode;
429}
430
431U_CFUNC void
432writeUCDFilename(char *basename, const char *filename, const char *suffix) {
433 int32_t length=(int32_t)uprv_strlen(filename);
434 uprv_strcpy(basename, filename);
435 if(suffix!=NULL) {
436 basename[length++]='-';
437 uprv_strcpy(basename+length, suffix);
438 length+=(int32_t)uprv_strlen(suffix);
439 }
440 uprv_strcpy(basename+length, ".txt");
441}
442
443/* TODO: move to toolutil */
444static UBool
445isToken(const char *token, const char *s) {
446 const char *z;
447 int32_t j;
448
449 s=u_skipWhitespace(s);
450 for(j=0;; ++j) {
451 if(token[j]!=0) {
452 if(s[j]!=token[j]) {
453 break;
454 }
455 } else {
456 z=u_skipWhitespace(s+j);
457 if(*z==';' || *z==0) {
458 return TRUE;
459 } else {
460 break;
461 }
462 }
463 }
464
465 return FALSE;
466}
467
468/* parser for BidiMirroring.txt --------------------------------------------- */
469
470static void U_CALLCONV
471mirrorLineFn(void *context,
472 char *fields[][2], int32_t fieldCount,
473 UErrorCode *pErrorCode) {
474 char *end;
475 UChar32 src, mirror;
476
477 src=(UChar32)uprv_strtoul(fields[0][0], &end, 16);
478 if(end<=fields[0][0] || end!=fields[0][1]) {
479 fprintf(stderr, "genbidi: syntax error in BidiMirroring.txt field 0 at %s\n", fields[0][0]);
480 *pErrorCode=U_PARSE_ERROR;
481 exit(U_PARSE_ERROR);
482 }
483
484 mirror=(UChar32)uprv_strtoul(fields[1][0], &end, 16);
485 if(end<=fields[1][0] || end!=fields[1][1]) {
486 fprintf(stderr, "genbidi: syntax error in BidiMirroring.txt field 1 at %s\n", fields[1][0]);
487 *pErrorCode=U_PARSE_ERROR;
488 exit(U_PARSE_ERROR);
489 }
490
491 addMirror(src, mirror);
492}
493
494static void
495parseBidiMirroring(const char *filename, UErrorCode *pErrorCode) {
496 char *fields[2][2];
497
498 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
499 return;
500 }
501
502 u_parseDelimitedFile(filename, ';', fields, 2, mirrorLineFn, NULL, pErrorCode);
503}
504
505/* parser for UnicodeData.txt ----------------------------------------------- */
506
507static void U_CALLCONV
508unicodeDataLineFn(void *context,
509 char *fields[][2], int32_t fieldCount,
510 UErrorCode *pErrorCode) {
511 char *end;
512 UErrorCode errorCode;
513 UChar32 c;
514
515 errorCode=U_ZERO_ERROR;
516
517 /* get the character code, field 0 */
518 c=(UChar32)uprv_strtoul(fields[0][0], &end, 16);
519 if(end<=fields[0][0] || end!=fields[0][1]) {
520 fprintf(stderr, "genbidi: syntax error in field 0 at %s\n", fields[0][0]);
521 *pErrorCode=U_PARSE_ERROR;
522 exit(U_PARSE_ERROR);
523 }
524
525 /* get Mirrored flag, field 9 */
526 if(*fields[9][0]=='Y') {
527 if(!upvec_setValue(pv, c, c+1, 0, U_MASK(UBIDI_IS_MIRRORED_SHIFT), U_MASK(UBIDI_IS_MIRRORED_SHIFT), &errorCode)) {
528 fprintf(stderr, "genbidi error: unable to set 'is mirrored' for U+%04lx, code: %s\n",
529 (long)c, u_errorName(errorCode));
530 exit(errorCode);
531 }
532 } else if(fields[9][1]-fields[9][0]!=1 || *fields[9][0]!='N') {
533 fprintf(stderr, "genbidi: syntax error in field 9 at U+%04lx\n",
534 (long)c);
535 *pErrorCode=U_PARSE_ERROR;
536 exit(U_PARSE_ERROR);
537 }
538}
539
540static void
541parseDB(const char *filename, UErrorCode *pErrorCode) {
542 /* default Bidi classes for unassigned code points */
543 static const UChar32 defaultBidi[][3]={ /* { start, end, class } */
544 /* R: U+0590..U+05FF, U+07C0..U+08FF, U+FB1D..U+FB4F, U+10800..U+10FFF */
545 { 0x0590, 0x05FF, U_RIGHT_TO_LEFT },
546 { 0x07C0, 0x08FF, U_RIGHT_TO_LEFT },
547 { 0xFB1D, 0xFB4F, U_RIGHT_TO_LEFT },
548 { 0x10800, 0x10FFF, U_RIGHT_TO_LEFT },
549
550 /* AL: U+0600..U+07BF, U+FB50..U+FDCF, U+FDF0..U+FDFF, U+FE70..U+FEFE */
551 { 0x0600, 0x07BF, U_RIGHT_TO_LEFT_ARABIC },
552 { 0xFB50, 0xFDCF, U_RIGHT_TO_LEFT_ARABIC },
553 { 0xFDF0, 0xFDFF, U_RIGHT_TO_LEFT_ARABIC },
554 { 0xFE70, 0xFEFE, U_RIGHT_TO_LEFT_ARABIC }
555
556 /* L otherwise */
557 };
558
559 char *fields[15][2];
560 UChar32 start, end;
561 int32_t i;
562
563 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
564 return;
565 }
566
567 /*
568 * Set default Bidi classes for unassigned code points.
569 * See the documentation for Bidi_Class in UCD.html in the Unicode data.
570 * http://www.unicode.org/Public/
571 *
572 * Starting with Unicode 5.0, DerivedBidiClass.txt should (re)set
573 * the Bidi_Class values for all code points including unassigned ones
574 * and including L values for these.
575 * This code becomes unnecesary but harmless. Leave it for now in case
576 * someone uses genbidi on pre-Unicode 5.0 data.
577 */
578 for(i=0; i<LENGTHOF(defaultBidi); ++i) {
579 start=defaultBidi[i][0];
580 end=defaultBidi[i][1];
581 if(!upvec_setValue(pv, start, end+1, 0, (uint32_t)defaultBidi[i][2], UBIDI_CLASS_MASK, pErrorCode)) {
582 fprintf(stderr, "genbidi error: unable to set default bidi class for U+%04lx..U+%04lx, code: %s\n",
583 (long)start, (long)end, u_errorName(*pErrorCode));
584 exit(*pErrorCode);
585 }
586 }
587
588 u_parseDelimitedFile(filename, ';', fields, 15, unicodeDataLineFn, NULL, pErrorCode);
589
590 if(U_FAILURE(*pErrorCode)) {
591 return;
592 }
593}
594
595/* DerivedBidiClass.txt ----------------------------------------------------- */
596
597static void U_CALLCONV
598bidiClassLineFn(void *context,
599 char *fields[][2], int32_t fieldCount,
600 UErrorCode *pErrorCode) {
601 char *s;
602 uint32_t start, limit, value;
603
604 /* get the code point range */
605 u_parseCodePointRange(fields[0][0], &start, &limit, pErrorCode);
606 if(U_FAILURE(*pErrorCode)) {
607 fprintf(stderr, "genbidi: syntax error in DerivedBidiClass.txt field 0 at %s\n", fields[0][0]);
608 exit(*pErrorCode);
609 }
610 ++limit;
611
612 /* parse bidi class */
613 s=trimTerminateField(fields[1][0], fields[1][1]);
614 value=u_getPropertyValueEnum(UCHAR_BIDI_CLASS, s);
615 if((int32_t)value<0) {
616 fprintf(stderr, "genbidi error: unknown bidi class in DerivedBidiClass.txt field 1 at %s\n", s);
617 exit(U_PARSE_ERROR);
618 }
619
620 if(!upvec_setValue(pv, start, limit, 0, value, UBIDI_CLASS_MASK, pErrorCode)) {
621 fprintf(stderr, "genbidi error: unable to set derived bidi class for U+%04x..U+%04x - %s\n",
622 (int)start, (int)limit-1, u_errorName(*pErrorCode));
623 exit(*pErrorCode);
624 }
625}
626
627/*
628 * Hey, Emacs, please set the following:
629 *
630 * Local Variables:
631 * indent-tabs-mode: nil
632 * End:
633 *
634 */