|
1 /* |
|
2 ******************************************************************************* |
|
3 * |
|
4 * Copyright (C) 2009-2012, International Business Machines |
|
5 * Corporation and others. All Rights Reserved. |
|
6 * |
|
7 ******************************************************************************* |
|
8 * file name: gennorm2.cpp |
|
9 * encoding: US-ASCII |
|
10 * tab size: 8 (not used) |
|
11 * indentation:4 |
|
12 * |
|
13 * created on: 2009nov25 |
|
14 * created by: Markus W. Scherer |
|
15 * |
|
16 * This program reads text files that define Unicode normalization, |
|
17 * parses them, and builds a binary data file. |
|
18 */ |
|
19 |
|
20 #include "unicode/utypes.h" |
|
21 #include "n2builder.h" |
|
22 |
|
23 #include <stdio.h> |
|
24 #include <stdlib.h> |
|
25 #include <string.h> |
|
26 #include "unicode/errorcode.h" |
|
27 #include "unicode/localpointer.h" |
|
28 #include "unicode/putil.h" |
|
29 #include "unicode/uchar.h" |
|
30 #include "unicode/unistr.h" |
|
31 #include "charstr.h" |
|
32 #include "normalizer2impl.h" |
|
33 #include "toolutil.h" |
|
34 #include "uoptions.h" |
|
35 #include "uparse.h" |
|
36 |
|
37 #if UCONFIG_NO_NORMALIZATION |
|
38 #include "unewdata.h" |
|
39 #endif |
|
40 |
|
41 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) |
|
42 |
|
43 U_NAMESPACE_BEGIN |
|
44 |
|
45 UBool beVerbose=FALSE, haveCopyright=TRUE; |
|
46 |
|
47 U_DEFINE_LOCAL_OPEN_POINTER(LocalStdioFilePointer, FILE, fclose); |
|
48 |
|
49 #if !UCONFIG_NO_NORMALIZATION |
|
50 void parseFile(FILE *f, Normalizer2DataBuilder &builder); |
|
51 #endif |
|
52 |
|
53 /* -------------------------------------------------------------------------- */ |
|
54 |
|
55 enum { |
|
56 HELP_H, |
|
57 HELP_QUESTION_MARK, |
|
58 VERBOSE, |
|
59 COPYRIGHT, |
|
60 SOURCEDIR, |
|
61 OUTPUT_FILENAME, |
|
62 UNICODE_VERSION, |
|
63 OPT_FAST |
|
64 }; |
|
65 |
|
66 static UOption options[]={ |
|
67 UOPTION_HELP_H, |
|
68 UOPTION_HELP_QUESTION_MARK, |
|
69 UOPTION_VERBOSE, |
|
70 UOPTION_COPYRIGHT, |
|
71 UOPTION_SOURCEDIR, |
|
72 UOPTION_DEF("output", 'o', UOPT_REQUIRES_ARG), |
|
73 UOPTION_DEF("unicode", 'u', UOPT_REQUIRES_ARG), |
|
74 UOPTION_DEF("fast", '\1', UOPT_NO_ARG) |
|
75 }; |
|
76 |
|
77 extern "C" int |
|
78 main(int argc, char* argv[]) { |
|
79 U_MAIN_INIT_ARGS(argc, argv); |
|
80 |
|
81 /* preset then read command line options */ |
|
82 options[SOURCEDIR].value=""; |
|
83 argc=u_parseArgs(argc, argv, sizeof(options)/sizeof(options[HELP_H]), options); |
|
84 |
|
85 /* error handling, printing usage message */ |
|
86 if(argc<0) { |
|
87 fprintf(stderr, |
|
88 "error in command line argument \"%s\"\n", |
|
89 argv[-argc]); |
|
90 } |
|
91 if(!options[OUTPUT_FILENAME].doesOccur) { |
|
92 argc=-1; |
|
93 } |
|
94 if( argc<2 || |
|
95 options[HELP_H].doesOccur || options[HELP_QUESTION_MARK].doesOccur |
|
96 ) { |
|
97 /* |
|
98 * Broken into chunks because the C89 standard says the minimum |
|
99 * required supported string length is 509 bytes. |
|
100 */ |
|
101 fprintf(stderr, |
|
102 "Usage: %s [-options] infiles+ -o outputfilename\n" |
|
103 "\n" |
|
104 "Reads the infiles with normalization data and\n" |
|
105 "creates a binary file (outputfilename) with the data.\n" |
|
106 "\n", |
|
107 argv[0]); |
|
108 fprintf(stderr, |
|
109 "Options:\n" |
|
110 "\t-h or -? or --help this usage text\n" |
|
111 "\t-v or --verbose verbose output\n" |
|
112 "\t-c or --copyright include a copyright notice\n" |
|
113 "\t-u or --unicode Unicode version, followed by the version like 5.2.0\n"); |
|
114 fprintf(stderr, |
|
115 "\t-s or --sourcedir source directory, followed by the path\n" |
|
116 "\t-o or --output output filename\n"); |
|
117 fprintf(stderr, |
|
118 "\t --fast optimize the .nrm file for fast normalization,\n" |
|
119 "\t which might increase its size (Writes fully decomposed\n" |
|
120 "\t regular mappings instead of delta mappings.\n" |
|
121 "\t You should measure the runtime speed to make sure that\n" |
|
122 "\t this is a good trade-off.)\n"); |
|
123 return argc<0 ? U_ILLEGAL_ARGUMENT_ERROR : U_ZERO_ERROR; |
|
124 } |
|
125 |
|
126 beVerbose=options[VERBOSE].doesOccur; |
|
127 haveCopyright=options[COPYRIGHT].doesOccur; |
|
128 |
|
129 IcuToolErrorCode errorCode("gennorm2/main()"); |
|
130 |
|
131 #if UCONFIG_NO_NORMALIZATION |
|
132 |
|
133 fprintf(stderr, |
|
134 "gennorm2 writes a dummy binary data file " |
|
135 "because UCONFIG_NO_NORMALIZATION is set, \n" |
|
136 "see icu/source/common/unicode/uconfig.h\n"); |
|
137 udata_createDummy(NULL, NULL, options[OUTPUT_FILENAME].value, errorCode); |
|
138 // Should not return an error since this is the expected behaviour if UCONFIG_NO_NORMALIZATION is on. |
|
139 // return U_UNSUPPORTED_ERROR; |
|
140 return 0; |
|
141 |
|
142 #else |
|
143 |
|
144 LocalPointer<Normalizer2DataBuilder> builder(new Normalizer2DataBuilder(errorCode)); |
|
145 errorCode.assertSuccess(); |
|
146 |
|
147 if(options[UNICODE_VERSION].doesOccur) { |
|
148 builder->setUnicodeVersion(options[UNICODE_VERSION].value); |
|
149 } |
|
150 |
|
151 if(options[OPT_FAST].doesOccur) { |
|
152 builder->setOptimization(Normalizer2DataBuilder::OPTIMIZE_FAST); |
|
153 } |
|
154 |
|
155 // prepare the filename beginning with the source dir |
|
156 CharString filename(options[SOURCEDIR].value, errorCode); |
|
157 int32_t pathLength=filename.length(); |
|
158 if( pathLength>0 && |
|
159 filename[pathLength-1]!=U_FILE_SEP_CHAR && |
|
160 filename[pathLength-1]!=U_FILE_ALT_SEP_CHAR |
|
161 ) { |
|
162 filename.append(U_FILE_SEP_CHAR, errorCode); |
|
163 pathLength=filename.length(); |
|
164 } |
|
165 |
|
166 for(int i=1; i<argc; ++i) { |
|
167 printf("gennorm2: processing %s\n", argv[i]); |
|
168 filename.append(argv[i], errorCode); |
|
169 LocalStdioFilePointer f(fopen(filename.data(), "r")); |
|
170 if(f==NULL) { |
|
171 fprintf(stderr, "gennorm2 error: unable to open %s\n", filename.data()); |
|
172 exit(U_FILE_ACCESS_ERROR); |
|
173 } |
|
174 builder->setOverrideHandling(Normalizer2DataBuilder::OVERRIDE_PREVIOUS); |
|
175 parseFile(f.getAlias(), *builder); |
|
176 filename.truncate(pathLength); |
|
177 } |
|
178 |
|
179 builder->writeBinaryFile(options[OUTPUT_FILENAME].value); |
|
180 |
|
181 return errorCode.get(); |
|
182 |
|
183 #endif |
|
184 } |
|
185 |
|
186 #if !UCONFIG_NO_NORMALIZATION |
|
187 |
|
188 void parseFile(FILE *f, Normalizer2DataBuilder &builder) { |
|
189 IcuToolErrorCode errorCode("gennorm2/parseFile()"); |
|
190 char line[300]; |
|
191 uint32_t startCP, endCP; |
|
192 while(NULL!=fgets(line, (int)sizeof(line), f)) { |
|
193 char *comment=(char *)strchr(line, '#'); |
|
194 if(comment!=NULL) { |
|
195 *comment=0; |
|
196 } |
|
197 u_rtrim(line); |
|
198 if(line[0]==0) { |
|
199 continue; // skip empty and comment-only lines |
|
200 } |
|
201 if(line[0]=='*') { |
|
202 const char *s=u_skipWhitespace(line+1); |
|
203 if(0==strncmp(s, "Unicode", 7)) { |
|
204 s=u_skipWhitespace(s+7); |
|
205 builder.setUnicodeVersion(s); |
|
206 } |
|
207 continue; // reserved syntax |
|
208 } |
|
209 const char *delimiter; |
|
210 int32_t rangeLength= |
|
211 u_parseCodePointRangeAnyTerminator(line, &startCP, &endCP, &delimiter, errorCode); |
|
212 if(errorCode.isFailure()) { |
|
213 fprintf(stderr, "gennorm2 error: parsing code point range from %s\n", line); |
|
214 exit(errorCode.reset()); |
|
215 } |
|
216 delimiter=u_skipWhitespace(delimiter); |
|
217 if(*delimiter==':') { |
|
218 const char *s=u_skipWhitespace(delimiter+1); |
|
219 char *end; |
|
220 unsigned long value=strtoul(s, &end, 10); |
|
221 if(end<=s || *u_skipWhitespace(end)!=0 || value>=0xff) { |
|
222 fprintf(stderr, "gennorm2 error: parsing ccc from %s\n", line); |
|
223 exit(U_PARSE_ERROR); |
|
224 } |
|
225 for(UChar32 c=(UChar32)startCP; c<=(UChar32)endCP; ++c) { |
|
226 builder.setCC(c, (uint8_t)value); |
|
227 } |
|
228 continue; |
|
229 } |
|
230 if(*delimiter=='-') { |
|
231 if(*u_skipWhitespace(delimiter+1)!=0) { |
|
232 fprintf(stderr, "gennorm2 error: parsing remove-mapping %s\n", line); |
|
233 exit(U_PARSE_ERROR); |
|
234 } |
|
235 for(UChar32 c=(UChar32)startCP; c<=(UChar32)endCP; ++c) { |
|
236 builder.removeMapping(c); |
|
237 } |
|
238 continue; |
|
239 } |
|
240 if(*delimiter=='=' || *delimiter=='>') { |
|
241 UChar uchars[Normalizer2Impl::MAPPING_LENGTH_MASK]; |
|
242 int32_t length=u_parseString(delimiter+1, uchars, LENGTHOF(uchars), NULL, errorCode); |
|
243 if(errorCode.isFailure()) { |
|
244 fprintf(stderr, "gennorm2 error: parsing mapping string from %s\n", line); |
|
245 exit(errorCode.reset()); |
|
246 } |
|
247 UnicodeString mapping(FALSE, uchars, length); |
|
248 if(*delimiter=='=') { |
|
249 if(rangeLength!=1) { |
|
250 fprintf(stderr, |
|
251 "gennorm2 error: round-trip mapping for more than 1 code point on %s\n", |
|
252 line); |
|
253 exit(U_PARSE_ERROR); |
|
254 } |
|
255 builder.setRoundTripMapping((UChar32)startCP, mapping); |
|
256 } else { |
|
257 for(UChar32 c=(UChar32)startCP; c<=(UChar32)endCP; ++c) { |
|
258 builder.setOneWayMapping(c, mapping); |
|
259 } |
|
260 } |
|
261 continue; |
|
262 } |
|
263 fprintf(stderr, "gennorm2 error: unrecognized data line %s\n", line); |
|
264 exit(U_PARSE_ERROR); |
|
265 } |
|
266 } |
|
267 |
|
268 #endif // !UCONFIG_NO_NORMALIZATION |
|
269 |
|
270 U_NAMESPACE_END |
|
271 |
|
272 /* |
|
273 * Hey, Emacs, please set the following: |
|
274 * |
|
275 * Local Variables: |
|
276 * indent-tabs-mode: nil |
|
277 * End: |
|
278 * |
|
279 */ |