gdiplus: Make GdipMeasureCharacterRanges/GdipMeasureString/GdipDrawString add extra...
[wine/multimedia.git] / tools / make_unicode
blob9d2d8a757509293f4ae2090799894da3f068e3b0
1 #!/usr/bin/perl -w
3 # Generate code page .c files from ftp.unicode.org descriptions
5 # Copyright 2000 Alexandre Julliard
7 # This library is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Lesser General Public
9 # License as published by the Free Software Foundation; either
10 # version 2.1 of the License, or (at your option) any later version.
12 # This library is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # Lesser General Public License for more details.
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this library; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 use strict;
24 # base URLs for www.unicode.org files
25 my $MAPPINGS = "http://www.unicode.org/Public/MAPPINGS";
26 my $UNIDATA = "http://www.unicode.org/Public/6.0.0/ucd";
28 # Sort keys file
29 my $SORTKEYS = "http://www.unicode.org/reports/tr10/allkeys.txt";
31 # RFC3454 (stringprep data)
32 my $STRINGPREP = "http://www.rfc-editor.org/rfc/rfc3454.txt";
34 # Defaults mapping
35 my $DEFAULTS = "tools/unicode-defaults";
37 # Default char for undefined mappings
38 my $DEF_CHAR = ord '?';
40 my @allfiles =
42 [ 37, "VENDORS/MICSFT/EBCDIC/CP037.TXT", 0, "IBM EBCDIC US Canada" ],
43 [ 424, "VENDORS/MISC/CP424.TXT", 0, "IBM EBCDIC Hebrew" ],
44 [ 437, "VENDORS/MICSFT/PC/CP437.TXT", 1, "OEM United States" ],
45 [ 500, "VENDORS/MICSFT/EBCDIC/CP500.TXT", 0, "IBM EBCDIC International" ],
46 [ 737, "VENDORS/MICSFT/PC/CP737.TXT", 1, "OEM Greek 437G" ],
47 [ 775, "VENDORS/MICSFT/PC/CP775.TXT", 1, "OEM Baltic" ],
48 [ 850, "VENDORS/MICSFT/PC/CP850.TXT", 1, "OEM Multilingual Latin 1" ],
49 [ 852, "VENDORS/MICSFT/PC/CP852.TXT", 1, "OEM Slovak Latin 2" ],
50 [ 855, "VENDORS/MICSFT/PC/CP855.TXT", 1, "OEM Cyrillic" ],
51 [ 856, "VENDORS/MISC/CP856.TXT", 0, "Hebrew PC" ],
52 [ 857, "VENDORS/MICSFT/PC/CP857.TXT", 1, "OEM Turkish" ],
53 [ 860, "VENDORS/MICSFT/PC/CP860.TXT", 1, "OEM Portuguese" ],
54 [ 861, "VENDORS/MICSFT/PC/CP861.TXT", 1, "OEM Icelandic" ],
55 [ 862, "VENDORS/MICSFT/PC/CP862.TXT", 1, "OEM Hebrew" ],
56 [ 863, "VENDORS/MICSFT/PC/CP863.TXT", 1, "OEM Canadian French" ],
57 [ 864, "VENDORS/MICSFT/PC/CP864.TXT", 0, "OEM Arabic" ],
58 [ 865, "VENDORS/MICSFT/PC/CP865.TXT", 1, "OEM Nordic" ],
59 [ 866, "VENDORS/MICSFT/PC/CP866.TXT", 1, "OEM Russian" ],
60 [ 869, "VENDORS/MICSFT/PC/CP869.TXT", 1, "OEM Greek" ],
61 [ 874, "VENDORS/MICSFT/WindowsBestFit/bestfit874.txt", 1, "ANSI/OEM Thai" ],
62 [ 875, "VENDORS/MICSFT/EBCDIC/CP875.TXT", 0, "IBM EBCDIC Greek" ],
63 [ 878, "VENDORS/MISC/KOI8-R.TXT", 0, "Russian KOI8" ],
64 [ 932, "VENDORS/MICSFT/WindowsBestFit/bestfit932.txt", 0, "ANSI/OEM Japanese Shift-JIS" ],
65 [ 936, "VENDORS/MICSFT/WindowsBestFit/bestfit936.txt", 0, "ANSI/OEM Simplified Chinese GBK" ],
66 [ 949, "VENDORS/MICSFT/WindowsBestFit/bestfit949.txt", 0, "ANSI/OEM Korean Unified Hangul" ],
67 [ 950, "VENDORS/MICSFT/WindowsBestFit/bestfit950.txt", 0, "ANSI/OEM Traditional Chinese Big5" ],
68 [ 1006, "VENDORS/MISC/CP1006.TXT", 0, "IBM Arabic" ],
69 [ 1026, "VENDORS/MICSFT/EBCDIC/CP1026.TXT", 0, "IBM EBCDIC Latin 5 Turkish" ],
70 [ 1250, "VENDORS/MICSFT/WindowsBestFit/bestfit1250.txt", 0, "ANSI Eastern Europe" ],
71 [ 1251, "VENDORS/MICSFT/WindowsBestFit/bestfit1251.txt", 0, "ANSI Cyrillic" ],
72 [ 1252, "VENDORS/MICSFT/WindowsBestFit/bestfit1252.txt", 0, "ANSI Latin 1" ],
73 [ 1253, "VENDORS/MICSFT/WindowsBestFit/bestfit1253.txt", 0, "ANSI Greek" ],
74 [ 1254, "VENDORS/MICSFT/WindowsBestFit/bestfit1254.txt", 0, "ANSI Turkish" ],
75 [ 1255, "VENDORS/MICSFT/WindowsBestFit/bestfit1255.txt", 0, "ANSI Hebrew" ],
76 [ 1256, "VENDORS/MICSFT/WindowsBestFit/bestfit1256.txt", 0, "ANSI Arabic" ],
77 [ 1257, "VENDORS/MICSFT/WindowsBestFit/bestfit1257.txt", 0, "ANSI Baltic" ],
78 [ 1258, "VENDORS/MICSFT/WindowsBestFit/bestfit1258.txt", 0, "ANSI/OEM Viet Nam" ],
79 [ 1361, "OBSOLETE/EASTASIA/KSC/JOHAB.TXT", 0, "Korean Johab" ],
80 [ 10000, "VENDORS/MICSFT/MAC/ROMAN.TXT", 0, "Mac Roman" ],
81 [ 10006, "VENDORS/MICSFT/MAC/GREEK.TXT", 0, "Mac Greek" ],
82 [ 10007, "VENDORS/MICSFT/MAC/CYRILLIC.TXT", 0, "Mac Cyrillic" ],
83 [ 10029, "VENDORS/MICSFT/MAC/LATIN2.TXT", 0, "Mac Latin 2" ],
84 [ 10079, "VENDORS/MICSFT/MAC/ICELAND.TXT", 0, "Mac Icelandic" ],
85 [ 10081, "VENDORS/MICSFT/MAC/TURKISH.TXT", 0, "Mac Turkish" ],
86 [ 20127, undef, 0, "US-ASCII (7bit)" ],
87 [ 20866, "VENDORS/MISC/KOI8-R.TXT", 0, "Russian KOI8" ],
88 [ 20932, "OBSOLETE/EASTASIA/JIS/JIS0208.TXT", 0, "EUC-JP" ],
89 [ 21866, "VENDORS/MISC/KOI8-U.TXT", 0, "Ukrainian KOI8" ],
90 [ 28591, "ISO8859/8859-1.TXT", 0, "ISO 8859-1 Latin 1" ],
91 [ 28592, "ISO8859/8859-2.TXT", 0, "ISO 8859-2 Latin 2 (East European)" ],
92 [ 28593, "ISO8859/8859-3.TXT", 0, "ISO 8859-3 Latin 3 (South European)" ],
93 [ 28594, "ISO8859/8859-4.TXT", 0, "ISO 8859-4 Latin 4 (Baltic old)" ],
94 [ 28595, "ISO8859/8859-5.TXT", 0, "ISO 8859-5 Cyrillic" ],
95 [ 28596, "ISO8859/8859-6.TXT", 0, "ISO 8859-6 Arabic" ],
96 [ 28597, "ISO8859/8859-7.TXT", 0, "ISO 8859-7 Greek" ],
97 [ 28598, "ISO8859/8859-8.TXT", 0, "ISO 8859-8 Hebrew" ],
98 [ 28599, "ISO8859/8859-9.TXT", 0, "ISO 8859-9 Latin 5 (Turkish)" ],
99 [ 28600, "ISO8859/8859-10.TXT", 0, "ISO 8859-10 Latin 6 (Nordic)" ],
100 [ 28603, "ISO8859/8859-13.TXT", 0, "ISO 8859-13 Latin 7 (Baltic)" ],
101 [ 28604, "ISO8859/8859-14.TXT", 0, "ISO 8859-14 Latin 8 (Celtic)" ],
102 [ 28605, "ISO8859/8859-15.TXT", 0, "ISO 8859-15 Latin 9 (Euro)" ],
103 [ 28606, "ISO8859/8859-16.TXT", 0, "ISO 8859-16 Latin 10 (Balkan)" ]
107 my %ctype =
109 "upper" => 0x0001,
110 "lower" => 0x0002,
111 "digit" => 0x0004,
112 "space" => 0x0008,
113 "punct" => 0x0010,
114 "cntrl" => 0x0020,
115 "blank" => 0x0040,
116 "xdigit" => 0x0080,
117 "alpha" => 0x0100,
118 "defin" => 0x0200
121 my %indic_types =
123 "Other" => 0x0000,
124 "Bindu" => 0x0001,
125 "Visarga" => 0x0002,
126 "Avagraha" => 0x0003,
127 "Nukta" => 0x0004,
128 "Virama" => 0x0005,
129 "Vowel_Independent" => 0x0006,
130 "Vowel_Dependent" => 0x0007,
131 "Vowel" => 0x0008,
132 "Consonant_Placeholder" => 0x0009,
133 "Consonant" => 0x000a,
134 "Consonant_Dead" => 0x000b,
135 "Consonant_Repha" => 0x000c,
136 "Consonant_Subjoined" => 0x000d,
137 "Consonant_Medial" => 0x000e,
138 "Consonant_Final" => 0x000f,
139 "Consonant_Head_Letter" => 0x0010,
140 "Modifying_Letter" => 0x0011,
141 "Tone_Letter" => 0x0012,
142 "Tone_Mark" => 0x0013,
143 "Register_Shifter" => 0x0014
146 my %matra_types =
148 "Right" => 0x01,
149 "Left" => 0x02,
150 "Visual_Order_Left" => 0x03,
151 "Left_And_Right" => 0x04,
152 "Top" => 0x05,
153 "Bottom" => 0x06,
154 "Top_And_Bottom" => 0x07,
155 "Top_And_Right" => 0x08,
156 "Top_And_Left" => 0x09,
157 "Top_And_Left_And_Right" => 0x0a,
158 "Bottom_And_Right" => 0x0b,
159 "Top_And_Bottom_And_Right" => 0x0c,
160 "Overstruck" => 0x0d,
161 "Invisible" => 0x0e
164 my %nameprep_flags =
166 "unassigned" => 0x01,
167 "prohibited" => 0x02,
168 "bidi_ral" => 0x04,
169 "bidi_l" => 0x08
172 my %break_types =
174 "BK" => 0x0001,
175 "CR" => 0x0002,
176 "LF" => 0x0003,
177 "CM" => 0x0004,
178 "SG" => 0x0005,
179 "GL" => 0x0006,
180 "CB" => 0x0007,
181 "SP" => 0x0008,
182 "ZW" => 0x0009,
183 "NL" => 0x000a,
184 "WJ" => 0x000b,
185 "JL" => 0x000c,
186 "JV" => 0x000d,
187 "JT" => 0x000e,
188 "H2" => 0x000f,
189 "H3" => 0x0010,
190 "XX" => 0x0011,
191 "OP" => 0x0012,
192 "CL" => 0x0013,
193 "CP" => 0x0014,
194 "QU" => 0x0015,
195 "NS" => 0x0016,
196 "EX" => 0x0017,
197 "SY" => 0x0018,
198 "IS" => 0x0019,
199 "PR" => 0x001a,
200 "PO" => 0x001b,
201 "NU" => 0x001c,
202 "AL" => 0x001d,
203 "ID" => 0x001e,
204 "IN" => 0x001f,
205 "HY" => 0x0020,
206 "BB" => 0x0021,
207 "BA" => 0x0022,
208 "SA" => 0x0023,
209 "AI" => 0x0024,
210 "B2" => 0x0025
213 my %categories =
215 "Lu" => $ctype{"defin"}|$ctype{"alpha"}|$ctype{"upper"}, # Letter, Uppercase
216 "Ll" => $ctype{"defin"}|$ctype{"alpha"}|$ctype{"lower"}, # Letter, Lowercase
217 "Lt" => $ctype{"defin"}|$ctype{"alpha"}|$ctype{"upper"}|$ctype{"lower"}, # Letter, Titlecase
218 "Mn" => $ctype{"defin"}, # Mark, Non-Spacing
219 "Mc" => $ctype{"defin"}, # Mark, Spacing Combining
220 "Me" => $ctype{"defin"}, # Mark, Enclosing
221 "Nd" => $ctype{"defin"}|$ctype{"digit"}, # Number, Decimal Digit
222 "Nl" => $ctype{"defin"}|$ctype{"alpha"}, # Number, Letter
223 "No" => $ctype{"defin"}, # Number, Other
224 "Zs" => $ctype{"defin"}|$ctype{"space"}, # Separator, Space
225 "Zl" => $ctype{"defin"}|$ctype{"space"}, # Separator, Line
226 "Zp" => $ctype{"defin"}|$ctype{"space"}, # Separator, Paragraph
227 "Cc" => $ctype{"defin"}|$ctype{"cntrl"}, # Other, Control
228 "Cf" => $ctype{"defin"}|$ctype{"cntrl"}, # Other, Format
229 "Cs" => $ctype{"defin"}, # Other, Surrogate
230 "Co" => $ctype{"defin"}, # Other, Private Use
231 "Cn" => $ctype{"defin"}, # Other, Not Assigned
232 "Lm" => $ctype{"defin"}|$ctype{"alpha"}, # Letter, Modifier
233 "Lo" => $ctype{"defin"}|$ctype{"alpha"}, # Letter, Other
234 "Pc" => $ctype{"defin"}|$ctype{"punct"}, # Punctuation, Connector
235 "Pd" => $ctype{"defin"}|$ctype{"punct"}, # Punctuation, Dash
236 "Ps" => $ctype{"defin"}|$ctype{"punct"}, # Punctuation, Open
237 "Pe" => $ctype{"defin"}|$ctype{"punct"}, # Punctuation, Close
238 "Pi" => $ctype{"defin"}|$ctype{"punct"}, # Punctuation, Initial quote
239 "Pf" => $ctype{"defin"}|$ctype{"punct"}, # Punctuation, Final quote
240 "Po" => $ctype{"defin"}|$ctype{"punct"}, # Punctuation, Other
241 "Sm" => $ctype{"defin"}, # Symbol, Math
242 "Sc" => $ctype{"defin"}, # Symbol, Currency
243 "Sk" => $ctype{"defin"}, # Symbol, Modifier
244 "So" => $ctype{"defin"} # Symbol, Other
247 # a few characters need additional categories that cannot be determined automatically
248 my %special_categories =
250 "xdigit" => [ ord('0')..ord('9'),ord('A')..ord('F'),ord('a')..ord('f'),
251 0xff10..0xff19, 0xff21..0xff26, 0xff41..0xff46 ],
252 "space" => [ 0x09..0x0d, 0x85 ],
253 "blank" => [ 0x09, 0x20, 0xa0, 0x3000, 0xfeff ],
254 "cntrl" => [ 0x070f, 0x200c, 0x200d,
255 0x200e, 0x200f, 0x202a, 0x202b, 0x202c, 0x202d, 0x202e,
256 0x206a, 0x206b, 0x206c, 0x206d, 0x206e, 0x206f, 0xfeff,
257 0xfff9, 0xfffa, 0xfffb ],
258 "punct" => [ 0x24, 0x2b, 0x3c..0x3e, 0x5e, 0x60, 0x7c, 0x7e, 0xa2..0xbe,
259 0xd7, 0xf7 ],
260 "digit" => [ 0xb2, 0xb3, 0xb9 ],
261 "lower" => [ 0x2071, 0x207f ]
264 my %directions =
266 "L" => 1, # Left-to-Right
267 "LRE" => 15, # Left-to-Right Embedding
268 "LRO" => 15, # Left-to-Right Override
269 "R" => 2, # Right-to-Left
270 "AL" => 12, # Right-to-Left Arabic
271 "RLE" => 15, # Right-to-Left Embedding
272 "RLO" => 15, # Right-to-Left Override
273 "PDF" => 15, # Pop Directional Format
274 "EN" => 3, # European Number
275 "ES" => 4, # European Number Separator
276 "ET" => 5, # European Number Terminator
277 "AN" => 6, # Arabic Number
278 "CS" => 7, # Common Number Separator
279 "NSM" => 13, # Non-Spacing Mark
280 "BN" => 14, # Boundary Neutral
281 "B" => 8, # Paragraph Separator
282 "S" => 9, # Segment Separator
283 "WS" => 10, # Whitespace
284 "ON" => 11 # Other Neutrals
287 my %joining_types =
289 "U" => 0, # Non_Joining
290 "T" => 1, # Transparent
291 "R" => 2, # Right_Joining
292 "L" => 3, # Left_Joining
293 "D" => 4, # Dual_Joining
294 "C" => 5, # Join_Causing
297 my @cp2uni = ();
298 my @lead_bytes = ();
299 my @uni2cp = ();
300 my @unicode_defaults = ();
301 my @unicode_aliases = ();
302 my @tolower_table = ();
303 my @toupper_table = ();
304 my @digitmap_table = ();
305 my @compatmap_table = ();
306 my @category_table = (0) x 65536;
307 my @joining_table = (0) x 65536;
308 my @direction_table = ();
309 my @decomp_table = ();
310 my @compose_table = ();
312 my %joining_forms =
314 "isolated" => [],
315 "final" => [],
316 "initial" => [],
317 "medial" => []
320 ################################################################
321 # fetch a unicode.org file and open it
322 sub open_data_file($)
324 my $url = shift;
325 (my $name = $url) =~ s/^.*\///;
326 local *FILE;
327 unless (-f "data/$name")
329 print "Fetching $url...\n";
330 mkdir "data";
331 !system "wget", "-q", "-O", "data/$name", $url or die "cannot fetch $url";
333 open FILE, "<data/$name" or die "cannot open data/$name";
334 return *FILE;
337 ################################################################
338 # read in the defaults file
339 sub READ_DEFAULTS($)
341 my $filename = shift;
342 my $start;
344 # first setup a few default mappings
346 open DEFAULTS, "$filename" or die "Cannot open $filename";
347 print "Loading $filename\n";
348 while (<DEFAULTS>)
350 next if /^\#/; # skip comments
351 next if /^$/; # skip empty lines
352 if (/^(([0-9a-fA-F]+)(,[0-9a-fA-F]+)*)\s+([0-9a-fA-F]+|'.'|none)\s+(\#.*)?/)
354 my @src = map hex, split /,/,$1;
355 my $dst = $4;
356 my $comment = $5;
357 if ($#src > 0) { push @unicode_aliases, \@src; }
358 next if ($dst eq "none");
359 $dst = ($dst =~ /\'.\'/) ? ord substr($dst,1,1) : hex $dst;
360 foreach my $src (@src)
362 die "Duplicate value" if defined($unicode_defaults[$src]);
363 $unicode_defaults[$src] = $dst;
365 next;
367 die "Unrecognized line $_\n";
369 close DEFAULTS;
371 # now build mappings from the decomposition field of the Unicode database
373 my $UNICODE_DATA = open_data_file "$UNIDATA/UnicodeData.txt";
374 while (<$UNICODE_DATA>)
376 # Decode the fields ...
377 my ($code, $name, $cat, $comb, $bidi,
378 $decomp, $dec, $dig, $num, $mirror,
379 $oldname, $comment, $upper, $lower, $title) = split /;/;
380 my $dst;
381 my $src = hex $code;
383 die "unknown category $cat" unless defined $categories{$cat};
384 die "unknown directionality $bidi" unless defined $directions{$bidi};
386 $category_table[$src] = $categories{$cat};
387 $direction_table[$src] = $directions{$bidi};
388 $joining_table[$src] = $joining_types{"T"} if $cat eq "Mn" || $cat eq "Me" || $cat eq "Cf";
390 if ($lower ne "")
392 $tolower_table[$src] = hex $lower;
394 if ($upper ne "")
396 $toupper_table[$src] = hex $upper;
398 if ($dec ne "")
400 $category_table[$src] |= $ctype{"digit"};
402 if ($dig ne "")
404 $digitmap_table[$src] = ord $dig;
407 # copy the category and direction for everything between First/Last pairs
408 if ($name =~ /, First>/) { $start = $src; }
409 if ($name =~ /, Last>/)
411 while ($start < $src)
413 $category_table[$start] = $category_table[$src];
414 $direction_table[$start] = $direction_table[$src];
415 $start++;
419 next if $decomp eq ""; # no decomposition, skip it
421 if ($decomp =~ /^<([a-zA-Z]+)>\s+([0-9a-fA-F]+)$/)
423 # decomposition of the form "<foo> 1234" -> use char if type is known
424 if (($src >= 0xf900 && $src < 0xfb00) || ($src >= 0xfe30 && $src < 0xfffd))
426 # Single char decomposition in the compatibility range
427 $compatmap_table[$src] = hex $2;
429 if ($1 eq "isolated" || $1 eq "final" || $1 eq "initial" || $1 eq "medial")
431 ${joining_forms{$1}}[hex $2] = $src;
432 next;
434 next unless ($1 eq "font" ||
435 $1 eq "noBreak" ||
436 $1 eq "circle" ||
437 $1 eq "super" ||
438 $1 eq "sub" ||
439 $1 eq "wide" ||
440 $1 eq "narrow" ||
441 $1 eq "compat" ||
442 $1 eq "small");
443 $dst = hex $2;
445 elsif ($decomp =~ /^<compat>\s+0020\s+([0-9a-fA-F]+)/)
447 # decomposition "<compat> 0020 1234" -> combining accent
448 $dst = hex $1;
450 elsif ($decomp =~ /^([0-9a-fA-F]+)/)
452 # decomposition contains only char values without prefix -> use first char
453 $dst = hex $1;
454 $category_table[$src] |= $category_table[$dst] if defined $category_table[$dst];
455 # store decomposition if it contains two chars
456 if ($decomp =~ /^([0-9a-fA-F]+)\s+([0-9a-fA-F]+)$/)
458 $decomp_table[$src] = [ hex $1, hex $2 ];
459 push @compose_table, [ hex $1, hex $2, $src ];
461 elsif ($decomp =~ /^(<[a-z]+>\s)*([0-9a-fA-F]+)$/ &&
462 (($src >= 0xf900 && $src < 0xfb00) || ($src >= 0xfe30 && $src < 0xfffd)))
464 # Single char decomposition in the compatibility range
465 $compatmap_table[$src] = hex $2;
468 else
470 next;
473 next if defined($unicode_defaults[$src]); # may have been set in the defaults file
475 # check for loops
476 for (my $i = $dst; ; $i = $unicode_defaults[$i])
478 die sprintf("loop detected for %04x -> %04x",$src,$dst) if $i == $src;
479 last unless defined($unicode_defaults[$i]);
481 $unicode_defaults[$src] = $dst;
483 close $UNICODE_DATA;
485 # patch the category of some special characters
487 foreach my $cat (keys %special_categories)
489 my $flag = $ctype{$cat};
490 foreach my $i (@{$special_categories{$cat}}) { $category_table[$i] |= $flag; }
495 ################################################################
496 # parse the input file
497 sub READ_FILE($)
499 my $name = shift;
500 my $INPUT = open_data_file $name;
502 while (<$INPUT>)
504 next if /^\#/; # skip comments
505 next if /^$/; # skip empty lines
506 next if /\x1a/; # skip ^Z
507 next if (/^0x([0-9a-fA-F]+)\s+\#UNDEFINED/); # undefined char
509 if (/^0x([0-9a-fA-F]+)\s+\#DBCS LEAD BYTE/)
511 my $cp = hex $1;
512 push @lead_bytes,$cp;
513 $cp2uni[$cp] = 0;
514 next;
516 if (/^0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)\s+(\#.*)?/)
518 my $cp = hex $1;
519 my $uni = hex $2;
520 $cp2uni[$cp] = $uni unless defined($cp2uni[$cp]);
521 $uni2cp[$uni] = $cp unless defined($uni2cp[$uni]);
522 if ($cp > 0xff && !defined($cp2uni[$cp >> 8]))
524 push @lead_bytes,$cp >> 8;
525 $cp2uni[$cp >> 8] = 0;
527 next;
529 die "$name: Unrecognized line $_\n";
531 close $INPUT;
535 ################################################################
536 # fill input data for the 20127 (us-ascii) codepage
537 sub fill_20127_codepage()
539 for (my $i = 0; $i < 128; $i++) { $cp2uni[$i] = $uni2cp[$i] = $i; }
540 for (my $i = 128; $i < 256; $i++) { $cp2uni[$i] = $i & 0x7f; }
543 ################################################################
544 # get a mapping including glyph chars for MB_USEGLYPHCHARS
546 sub get_glyphs_mapping(@)
548 $_[0x01] = 0x263a; # (WHITE SMILING FACE)
549 $_[0x02] = 0x263b; # (BLACK SMILING FACE)
550 $_[0x03] = 0x2665; # (BLACK HEART SUIT)
551 $_[0x04] = 0x2666; # (BLACK DIAMOND SUIT)
552 $_[0x05] = 0x2663; # (BLACK CLUB SUIT)
553 $_[0x06] = 0x2660; # (BLACK SPADE SUIT)
554 $_[0x07] = 0x2022; # (BULLET)
555 $_[0x08] = 0x25d8; # (INVERSE BULLET)
556 $_[0x09] = 0x25cb; # (WHITE CIRCLE)
557 $_[0x0a] = 0x25d9; # (INVERSE WHITE CIRCLE)
558 $_[0x0b] = 0x2642; # (MALE SIGN)
559 $_[0x0c] = 0x2640; # (FEMALE SIGN)
560 $_[0x0d] = 0x266a; # (EIGHTH NOTE)
561 $_[0x0e] = 0x266b; # (BEAMED EIGHTH NOTES)
562 $_[0x0f] = 0x263c; # (WHITE SUN WITH RAYS)
563 $_[0x10] = 0x25ba; # (BLACK RIGHT-POINTING POINTER)
564 $_[0x11] = 0x25c4; # (BLACK LEFT-POINTING POINTER)
565 $_[0x12] = 0x2195; # (UP DOWN ARROW)
566 $_[0x13] = 0x203c; # (DOUBLE EXCLAMATION MARK)
567 $_[0x14] = 0x00b6; # (PILCROW SIGN)
568 $_[0x15] = 0x00a7; # (SECTION SIGN)
569 $_[0x16] = 0x25ac; # (BLACK RECTANGLE)
570 $_[0x17] = 0x21a8; # (UP DOWN ARROW WITH BASE)
571 $_[0x18] = 0x2191; # (UPWARDS ARROW)
572 $_[0x19] = 0x2193; # (DOWNWARDS ARROW)
573 $_[0x1a] = 0x2192; # (RIGHTWARDS ARROW)
574 $_[0x1b] = 0x2190; # (LEFTWARDS ARROW)
575 $_[0x1c] = 0x221f; # (RIGHT ANGLE)
576 $_[0x1d] = 0x2194; # (LEFT RIGHT ARROW)
577 $_[0x1e] = 0x25b2; # (BLACK UP-POINTING TRIANGLE)
578 $_[0x1f] = 0x25bc; # (BLACK DOWN-POINTING TRIANGLE)
579 $_[0x7f] = 0x2302; # (HOUSE)
580 return @_;
583 ################################################################
584 # build EUC-JP table from the JIS 0208 file
585 # FIXME: for proper EUC-JP we should probably read JIS 0212 too
586 # but this would require 3-byte DBCS characters
587 sub READ_JIS0208_FILE($)
589 my $name = shift;
591 # ASCII chars
592 for (my $i = 0x00; $i <= 0x7f; $i++)
594 $cp2uni[$i] = $i;
595 $uni2cp[$i] = $i;
598 # JIS X 0201 right plane
599 for (my $i = 0xa1; $i <= 0xdf; $i++)
601 $cp2uni[0x8e00 + $i] = 0xfec0 + $i;
602 $uni2cp[0xfec0 + $i] = 0x8e00 + $i;
605 # lead bytes
606 foreach my $i (0x8e, 0x8f, 0xa1 .. 0xfe)
608 push @lead_bytes,$i;
609 $cp2uni[$i] = 0;
612 # undefined chars
613 foreach my $i (0x80 .. 0x8d, 0x90 .. 0xa0, 0xff)
615 $cp2uni[$i] = $DEF_CHAR;
618 # Shift-JIS compatibility
619 $uni2cp[0x00a5] = 0x5c;
620 $uni2cp[0x203e] = 0x7e;
622 # Fix backslash conversion
623 $cp2uni[0xa1c0] = 0xff3c;
624 $uni2cp[0xff3c] = 0xa1c0;
626 my $INPUT = open_data_file $name;
627 while (<$INPUT>)
629 next if /^\#/; # skip comments
630 next if /^$/; # skip empty lines
631 next if /\x1a/; # skip ^Z
632 if (/^0x[0-9a-fA-F]+\s+0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)\s+(\#.*)?/)
634 my $cp = 0x8080 + hex $1;
635 my $uni = hex $2;
636 $cp2uni[$cp] = $uni unless defined($cp2uni[$cp]);
637 $uni2cp[$uni] = $cp unless defined($uni2cp[$uni]);
638 next;
640 die "$name: Unrecognized line $_\n";
642 close $INPUT;
646 ################################################################
647 # build the sort keys table
648 sub READ_SORTKEYS_FILE()
650 my @sortkeys = ();
651 for (my $i = 0; $i < 65536; $i++) { $sortkeys[$i] = [ -1, 0, 0, 0, 0 ] };
653 my $INPUT = open_data_file $SORTKEYS;
654 while (<$INPUT>)
656 next if /^\#/; # skip comments
657 next if /^$/; # skip empty lines
658 next if /\x1a/; # skip ^Z
659 next if /^\@version/; # skip @version header
660 if (/^([0-9a-fA-F]+)\s+;\s+\[([*.])([0-9a-fA-F]{4})\.([0-9a-fA-F]{4})\.([0-9a-fA-F]{4})\.([0-9a-fA-F]+)\]/)
662 my ($uni,$variable) = (hex $1, $2);
663 next if $uni > 65535;
664 $sortkeys[$uni] = [ $uni, hex $3, hex $4, hex $5, hex $6 ];
665 next;
667 if (/^([0-9a-fA-F]+\s+)+;\s+\[[*.]([0-9a-fA-F]{4})\.([0-9a-fA-F]{4})\.([0-9a-fA-F]{4})\.([0-9a-fA-F]+)\]/)
669 # multiple character sequence, ignored for now
670 next;
672 die "$SORTKEYS: Unrecognized line $_\n";
674 close $INPUT;
676 # compress the keys to 32 bit:
677 # key 1 to 16 bits, key 2 to 8 bits, key 3 to 4 bits, key 4 to 1 bit
679 @sortkeys = sort { ${$a}[1] <=> ${$b}[1] or
680 ${$a}[2] <=> ${$b}[2] or
681 ${$a}[3] <=> ${$b}[3] or
682 ${$a}[4] <=> ${$b}[4] or
683 $a cmp $b; } @sortkeys;
685 my ($n2, $n3) = (1, 1);
686 my @keys = (-1, -1, -1, -1, -1 );
687 my @flatkeys = ();
689 for (my $i = 0; $i < 65536; $i++)
691 my @current = @{$sortkeys[$i]};
692 next if $current[0] == -1;
693 if ($current[1] == $keys[1])
695 if ($current[2] == $keys[2])
697 if ($current[3] == $keys[3])
699 # nothing
701 else
703 $keys[3] = $current[3];
704 $n3++;
705 die if ($n3 >= 16);
708 else
710 $keys[2] = $current[2];
711 $keys[3] = $current[3];
712 $n2++;
713 $n3 = 1;
714 die if ($n2 >= 256);
717 else
719 $keys[1] = $current[1];
720 $keys[2] = $current[2];
721 $keys[3] = $current[3];
722 $n2 = 1;
723 $n3 = 1;
726 if ($current[2]) { $current[2] = $n2; }
727 if ($current[3]) { $current[3] = $n3; }
728 if ($current[4]) { $current[4] = 1; }
730 $flatkeys[$current[0]] = ($current[1] << 16) | ($current[2] << 8) | ($current[3] << 4) | $current[4];
732 return @flatkeys;
736 ################################################################
737 # build the sort keys table
738 sub DUMP_SORTKEYS($@)
740 my ($filename, @keys) = @_;
742 # count the number of 256-key ranges that contain something
744 my @offsets = ();
745 my $ranges = 2;
746 for (my $i = 0; $i < 256; $i++) { $offsets[$i] = 256; }
747 for (my $i = 0; $i < 65536; $i++)
749 next unless defined $keys[$i];
750 $offsets[$i >> 8] = $ranges * 256;
751 $ranges++;
752 $i |= 255;
755 # output the range offsets
757 open OUTPUT,">$filename.new" or die "Cannot create $filename";
758 printf "Building $filename\n";
759 printf OUTPUT "/* Unicode collation element table */\n";
760 printf OUTPUT "/* generated from %s */\n", $SORTKEYS;
761 printf OUTPUT "/* DO NOT EDIT!! */\n\n";
763 printf OUTPUT "const unsigned int collation_table[%d] =\n{\n", $ranges*256;
764 printf OUTPUT " /* index */\n";
765 printf OUTPUT "%s,\n", DUMP_ARRAY( "0x%08x", 0, @offsets );
767 # output the default values
769 printf OUTPUT " /* defaults */\n";
770 printf OUTPUT "%s", DUMP_ARRAY( "0x%08x", 0, (0xffffffff) x 256 );
772 # output all the key ranges
774 for (my $i = 0; $i < 256; $i++)
776 next if $offsets[$i] == 256;
777 printf OUTPUT ",\n /* 0x%02x00 .. 0x%02xff */\n", $i, $i;
778 printf OUTPUT "%s", DUMP_ARRAY( "0x%08x", 0xffffffff, @keys[($i<<8) .. ($i<<8)+255] );
780 printf OUTPUT "\n};\n";
781 close OUTPUT;
782 save_file($filename);
786 ################################################################
787 # add default mappings once the file had been read
788 sub ADD_DEFAULT_MAPPINGS()
790 # Apply aliases
792 foreach my $alias (@unicode_aliases)
794 my $target = undef;
795 foreach my $src (@$alias)
797 if (defined($uni2cp[$src]))
799 $target = $uni2cp[$src];
800 last;
803 next unless defined($target);
805 # At least one char of the alias set is defined, set the others to the same value
806 foreach my $src (@$alias)
808 $uni2cp[$src] = $target unless defined($uni2cp[$src]);
812 # For every src -> target mapping in the defaults table,
813 # make uni2cp[src] = uni2cp[target] if uni2cp[target] is defined
815 for (my $src = 0; $src < 65536; $src++)
817 next if defined($uni2cp[$src]); # source has a definition already
818 next unless defined($unicode_defaults[$src]); # no default for this char
819 my $target = $unicode_defaults[$src];
821 # do a recursive mapping until we find a target char that is defined
822 while (!defined($uni2cp[$target]) &&
823 defined($unicode_defaults[$target])) { $target = $unicode_defaults[$target]; }
825 if (defined($uni2cp[$target])) { $uni2cp[$src] = $uni2cp[$target]; }
828 # Add an identity mapping for all undefined chars
830 for (my $i = 0; $i < 256; $i++)
832 next if defined($cp2uni[$i]);
833 next if defined($uni2cp[$i]);
834 $cp2uni[$i] = $uni2cp[$i] = $i;
838 ################################################################
839 # dump an array of integers
840 sub DUMP_ARRAY($$@)
842 my ($format,$default,@array) = @_;
843 my $i;
844 my $ret = " ";
845 for ($i = 0; $i < $#array; $i++)
847 $ret .= sprintf($format, defined $array[$i] ? $array[$i] : $default);
848 $ret .= (($i % 8) != 7) ? ", " : ",\n ";
850 $ret .= sprintf($format, defined $array[$i] ? $array[$i] : $default);
851 return $ret;
854 ################################################################
855 # dump an SBCS mapping table
856 sub dump_sbcs_table($$$$$)
858 my ($codepage, $has_glyphs, $name, $def, $defw) = @_;
859 my $i;
861 # output the ascii->unicode table
863 if ($has_glyphs)
865 printf OUTPUT "static const WCHAR cp2uni[512] =\n";
866 printf OUTPUT "{\n%s", DUMP_ARRAY( "0x%04x", $defw, @cp2uni[0 .. 255] );
867 printf OUTPUT ",\n /* glyphs */\n%s\n};\n\n",
868 DUMP_ARRAY( "0x%04x", $defw, get_glyphs_mapping(@cp2uni[0 .. 255]) );
870 else
872 printf OUTPUT "static const WCHAR cp2uni[256] =\n";
873 printf OUTPUT "{\n%s\n};\n\n", DUMP_ARRAY( "0x%04x", $defw, @cp2uni[0 .. 255] );
876 # count the number of unicode->ascii subtables that contain something
878 my @filled = ();
879 my $subtables = 1;
880 for (my $i = 0; $i < 65536; $i++)
882 next unless defined $uni2cp[$i];
883 $filled[$i >> 8] = 1;
884 $subtables++;
885 $i |= 255;
888 # output all the subtables into a single array
890 printf OUTPUT "static const unsigned char uni2cp_low[%d] =\n{\n", $subtables*256;
891 for (my $i = 0; $i < 256; $i++)
893 next unless $filled[$i];
894 printf OUTPUT " /* 0x%02x00 .. 0x%02xff */\n", $i, $i;
895 printf OUTPUT "%s,\n", DUMP_ARRAY( "0x%02x", $def, @uni2cp[($i<<8) .. ($i<<8)+255] );
897 printf OUTPUT " /* defaults */\n";
898 printf OUTPUT "%s\n};\n\n", DUMP_ARRAY( "0x%02x", 0, ($def) x 256 );
900 # output a table of the offsets of the subtables in the previous array
902 my $pos = 0;
903 my @offsets = ();
904 for (my $i = 0; $i < 256; $i++)
906 if ($filled[$i]) { push @offsets, $pos; $pos += 256; }
907 else { push @offsets, ($subtables-1) * 256; }
909 printf OUTPUT "static const unsigned short uni2cp_high[256] =\n";
910 printf OUTPUT "{\n%s\n};\n\n", DUMP_ARRAY( "0x%04x", 0, @offsets );
912 # output the code page descriptor
914 printf OUTPUT "const struct sbcs_table cptable_%03d =\n{\n", $codepage;
915 printf OUTPUT " { %d, 1, 0x%04x, 0x%04x, \"%s\" },\n",
916 $codepage, $def, $defw, $name;
917 printf OUTPUT " cp2uni,\n";
918 if ($has_glyphs) { printf OUTPUT " cp2uni + 256,\n"; }
919 else { printf OUTPUT " cp2uni,\n"; }
920 printf OUTPUT " uni2cp_low,\n";
921 printf OUTPUT " uni2cp_high\n};\n";
925 ################################################################
926 # dump a DBCS mapping table
927 sub dump_dbcs_table($$$$@)
929 my ($codepage, $name, $def, $defw, @lb_ranges) = @_;
931 # build a list of lead bytes that are actually used
933 my @lblist = ();
934 LBLOOP: for (my $y = 0; $y <= $#lead_bytes; $y++)
936 my $base = $lead_bytes[$y] << 8;
937 for (my $x = 0; $x < 256; $x++)
939 if (defined $cp2uni[$base+$x])
941 push @lblist,$lead_bytes[$y];
942 next LBLOOP;
946 my $unused = ($#lead_bytes > $#lblist);
948 # output the ascii->unicode table for the single byte chars
950 printf OUTPUT "static const WCHAR cp2uni[%d] =\n", 256 * ($#lblist + 2 + $unused);
951 printf OUTPUT "{\n%s,\n", DUMP_ARRAY( "0x%04x", $defw, @cp2uni[0 .. 255] );
953 # output the default table for unused lead bytes
955 if ($unused)
957 printf OUTPUT " /* unused lead bytes */\n";
958 printf OUTPUT "%s,\n", DUMP_ARRAY( "0x%04x", 0, ($defw) x 256 );
961 # output the ascii->unicode table for each DBCS lead byte
963 for (my $y = 0; $y <= $#lblist; $y++)
965 my $base = $lblist[$y] << 8;
966 printf OUTPUT " /* lead byte %02x */\n", $lblist[$y];
967 printf OUTPUT "%s", DUMP_ARRAY( "0x%04x", $defw, @cp2uni[$base .. $base+255] );
968 printf OUTPUT ($y < $#lblist) ? ",\n" : "\n};\n\n";
971 # output the lead byte subtables offsets
973 my @offsets = ();
974 for (my $x = 0; $x < 256; $x++) { $offsets[$x] = 0; }
975 for (my $x = 0; $x <= $#lblist; $x++) { $offsets[$lblist[$x]] = $x + 1; }
976 if ($unused)
978 # increment all lead bytes offset to take into account the unused table
979 for (my $x = 0; $x <= $#lead_bytes; $x++) { $offsets[$lead_bytes[$x]]++; }
981 printf OUTPUT "static const unsigned char cp2uni_leadbytes[256] =\n";
982 printf OUTPUT "{\n%s\n};\n\n", DUMP_ARRAY( "0x%02x", 0, @offsets );
984 # count the number of unicode->ascii subtables that contain something
986 my @filled = ();
987 my $subtables = 1;
988 for (my $i = 0; $i < 65536; $i++)
990 next unless defined $uni2cp[$i];
991 $filled[$i >> 8] = 1;
992 $subtables++;
993 $i |= 255;
996 # output all the subtables into a single array
998 printf OUTPUT "static const unsigned short uni2cp_low[%d] =\n{\n", $subtables*256;
999 for (my $y = 0; $y < 256; $y++)
1001 next unless $filled[$y];
1002 printf OUTPUT " /* 0x%02x00 .. 0x%02xff */\n", $y, $y;
1003 printf OUTPUT "%s,\n", DUMP_ARRAY( "0x%04x", $def, @uni2cp[($y<<8) .. ($y<<8)+255] );
1005 printf OUTPUT " /* defaults */\n";
1006 printf OUTPUT "%s\n};\n\n", DUMP_ARRAY( "0x%04x", 0, ($def) x 256 );
1008 # output a table of the offsets of the subtables in the previous array
1010 my $pos = 0;
1011 @offsets = ();
1012 for (my $y = 0; $y < 256; $y++)
1014 if ($filled[$y]) { push @offsets, $pos; $pos += 256; }
1015 else { push @offsets, ($subtables-1) * 256; }
1017 printf OUTPUT "static const unsigned short uni2cp_high[256] =\n";
1018 printf OUTPUT "{\n%s\n};\n\n", DUMP_ARRAY( "0x%04x", 0, @offsets );
1020 # output the code page descriptor
1022 printf OUTPUT "const struct dbcs_table cptable_%03d =\n{\n", $codepage;
1023 printf OUTPUT " { %d, 2, 0x%04x, 0x%04x, \"%s\" },\n",
1024 $codepage, $def, $defw, $name;
1025 printf OUTPUT " cp2uni,\n";
1026 printf OUTPUT " cp2uni_leadbytes,\n";
1027 printf OUTPUT " uni2cp_low,\n";
1028 printf OUTPUT " uni2cp_high,\n";
1029 printf OUTPUT " {\n %s\n }\n", DUMP_ARRAY( "0x%02x", 0, @lb_ranges, 0, 0 );
1030 printf OUTPUT "};\n";
1034 ################################################################
1035 # get the list of defined lead byte ranges
1036 sub get_lb_ranges()
1038 my @list = ();
1039 my @ranges = ();
1040 my $i = 0;
1041 foreach $i (@lead_bytes) { $list[$i] = 1; }
1042 my $on = 0;
1043 for (my $i = 0; $i < 256; $i++)
1045 if ($on)
1047 if (!defined $list[$i]) { push @ranges, $i-1; $on = 0; }
1049 else
1051 if ($list[$i]) { push @ranges, $i; $on = 1; }
1054 if ($on) { push @ranges, 0xff; }
1055 return @ranges;
1058 ################################################################
1059 # dump the Indic Syllabic Category table
1060 sub dump_indic($)
1062 my $filename = shift;
1063 my @indic_table = ($indic_types{'Other'}) x 65536;;
1065 my $INPUT = open_data_file "$UNIDATA/IndicSyllabicCategory.txt";
1066 while (<$INPUT>)
1068 next if /^\#/; # skip comments
1069 next if /^\s*$/; # skip empty lines
1070 next if /\x1a/; # skip ^Z
1071 if (/^\s*([0-9a-fA-F]+)\s*;\s*([a-zA-Z_]+)\s*#/)
1073 my $type = $2;
1074 die "unknown indic $type" unless defined $indic_types{$type};
1075 if (hex $1 < 65536)
1077 $indic_table[hex $1] = $indic_types{$type};
1079 next;
1081 elsif (/^\s*([0-9a-fA-F]+)..\s*([0-9a-fA-F]+)\s*;\s*([A-Za-z_]+)\s*#/)
1083 my $type = $3;
1084 die "unknown indic $type" unless defined $indic_types{$type};
1085 if (hex $1 < 65536 and hex $2 < 6536)
1087 foreach my $i (hex $1 .. hex $2)
1089 $indic_table[$i] = $indic_types{$type};
1092 next;
1094 die "malformed line $_";
1096 close $INPUT;
1098 $INPUT = open_data_file "$UNIDATA/IndicMatraCategory.txt";
1099 while (<$INPUT>)
1101 next if /^\#/; # skip comments
1102 next if /^\s*$/; # skip empty lines
1103 next if /\x1a/; # skip ^Z
1104 if (/^\s*([0-9a-fA-F]+)\s*;\s*([a-zA-Z]+)\s*#/)
1106 my $type = $2;
1107 die "unknown matra $type" unless defined $matra_types{$type};
1108 $indic_table[hex $1] += $matra_types{$type} << 8;
1109 next;
1111 elsif (/^\s*([0-9a-fA-F]+)..\s*([0-9a-fA-F]+)\s*;\s*([A-Za-z_]+)\s*#/)
1113 my $type = $3;
1114 die "unknown matra $type" unless defined $matra_types{$type};
1115 foreach my $i (hex $1 .. hex $2)
1117 $indic_table[$i] += $matra_types{$type} << 8;
1119 next;
1121 die "malformed line $_";
1123 close $INPUT;
1125 open OUTPUT,">$filename.new" or die "Cannot create $filename";
1126 print "Building $filename\n";
1127 print OUTPUT "/* Unicode Indic Syllabic Category */\n";
1128 print OUTPUT "/* generated from $UNIDATA/IndicSyllabicCategory.txt */\n";
1129 print OUTPUT "/* and from $UNIDATA/IndicMatraCategory.txt */\n";
1130 print OUTPUT "/* DO NOT EDIT!! */\n\n";
1131 print OUTPUT "#include \"wine/unicode.h\"\n\n";
1133 dump_two_level_mapping( "indic_syllabic_table", @indic_table);
1135 close OUTPUT;
1136 save_file($filename);
1139 ################################################################
1140 # dump the Line Break Properties table
1141 sub dump_linebreak($)
1143 my $filename = shift;
1144 my @break_table = ($break_types{'XX'}) x 65536;;
1145 my $next_group = 0;
1147 my $INPUT = open_data_file "$UNIDATA/LineBreak.txt";
1148 while (<$INPUT>)
1150 next if /^\#/; # skip comments
1151 next if /^\s*$/; # skip empty lines
1152 next if /\x1a/; # skip ^Z
1153 if (/^\s*([0-9a-fA-F]+)\s*;\s*([0-9A-Z][0-9A-Z])+\s*/)
1155 my $type = $2;
1156 die "unknown breaktype $type" unless defined $break_types{$type};
1157 $break_table[hex $1] = $break_types{$type};
1158 next;
1160 elsif (/^\s*([0-9a-fA-F]+)..\s*([0-9a-fA-F]+)\s*;\s*([0-9A-Z][0-9A-Z])+\s*/)
1162 my $type = $3;
1163 die "unknown breaktype $type" unless defined $break_types{$type};
1164 foreach my $i (hex $1 .. hex $2)
1166 $break_table[$i] = $break_types{$type};
1168 next;
1170 die "malformed line $_";
1172 close $INPUT;
1174 open OUTPUT,">$filename.new" or die "Cannot create $filename";
1175 print "Building $filename\n";
1176 print OUTPUT "/* Unicode Line Break Properties */\n";
1177 print OUTPUT "/* generated from $UNIDATA/LineBreak.txt */\n";
1178 print OUTPUT "/* DO NOT EDIT!! */\n\n";
1179 print OUTPUT "#include \"wine/unicode.h\"\n\n";
1181 dump_two_level_mapping( "wine_linebreak_table", @break_table);
1183 close OUTPUT;
1184 save_file($filename);
1188 ################################################################
1189 # dump the BiDi mirroring table
1190 sub dump_mirroring($)
1192 my $filename = shift;
1193 my @mirror_table = ();
1195 my $INPUT = open_data_file "$UNIDATA/BidiMirroring.txt";
1196 while (<$INPUT>)
1198 next if /^\#/; # skip comments
1199 next if /^$/; # skip empty lines
1200 next if /\x1a/; # skip ^Z
1201 if (/^\s*([0-9a-fA-F]+)\s*;\s*([0-9a-fA-F]+)/)
1203 $mirror_table[hex $1] = hex $2;
1204 next;
1206 die "malformed line $_";
1208 close $INPUT;
1210 open OUTPUT,">$filename.new" or die "Cannot create $filename";
1211 print "Building $filename\n";
1212 print OUTPUT "/* Unicode BiDi mirroring */\n";
1213 print OUTPUT "/* generated from $UNIDATA/BidiMirroring.txt */\n";
1214 print OUTPUT "/* DO NOT EDIT!! */\n\n";
1215 print OUTPUT "#include \"wine/unicode.h\"\n\n";
1216 DUMP_CASE_TABLE( "wine_mirror_map", @mirror_table );
1217 close OUTPUT;
1218 save_file($filename);
1222 ################################################################
1223 # dump the Arabic shaping table
1224 sub dump_shaping($)
1226 my $filename = shift;
1227 my %groups;
1228 my $next_group = 0;
1230 $groups{"No_Joining_Group"} = $next_group++;
1232 my $INPUT = open_data_file "$UNIDATA/ArabicShaping.txt";
1233 while (<$INPUT>)
1235 next if /^\#/; # skip comments
1236 next if /^\s*$/; # skip empty lines
1237 next if /\x1a/; # skip ^Z
1238 if (/^\s*([0-9a-fA-F]+)\s*;.*;\s*([RLDCUT])\s*;\s*(\w+)/)
1240 my $type = $2;
1241 my $group = $3;
1242 $groups{$group} = $next_group++ unless defined $groups{$group};
1243 $joining_table[hex $1] = $joining_types{$type} | ($groups{$group} << 8);
1244 next;
1246 die "malformed line $_";
1248 close $INPUT;
1250 open OUTPUT,">$filename.new" or die "Cannot create $filename";
1251 print "Building $filename\n";
1252 print OUTPUT "/* Unicode Arabic shaping */\n";
1253 print OUTPUT "/* generated from $UNIDATA/ArabicShaping.txt */\n";
1254 print OUTPUT "/* DO NOT EDIT!! */\n\n";
1255 print OUTPUT "#include \"wine/unicode.h\"\n\n";
1257 dump_two_level_mapping( "wine_shaping_table", @joining_table );
1259 print OUTPUT "\nconst unsigned short wine_shaping_forms[256][4] =\n{\n";
1260 for (my $i = 0x600; $i <= 0x6ff; $i++)
1262 printf OUTPUT " { 0x%04x, 0x%04x, 0x%04x, 0x%04x },\n",
1263 ${joining_forms{"isolated"}}[$i] || $i,
1264 ${joining_forms{"final"}}[$i] || $i,
1265 ${joining_forms{"initial"}}[$i] || $i,
1266 ${joining_forms{"medial"}}[$i] || $i;
1268 print OUTPUT "};\n";
1270 close OUTPUT;
1271 save_file($filename);
1275 ################################################################
1276 # dump the case mapping tables
1277 sub DUMP_CASE_MAPPINGS($)
1279 my $filename = shift;
1280 open OUTPUT,">$filename.new" or die "Cannot create $filename";
1281 printf "Building $filename\n";
1282 printf OUTPUT "/* Unicode case mappings */\n";
1283 printf OUTPUT "/* Automatically generated; DO NOT EDIT!! */\n\n";
1284 printf OUTPUT "#include \"wine/unicode.h\"\n\n";
1286 DUMP_CASE_TABLE( "wine_casemap_lower", @tolower_table );
1287 DUMP_CASE_TABLE( "wine_casemap_upper", @toupper_table );
1288 DUMP_CASE_TABLE( "wine_digitmap", @digitmap_table );
1289 DUMP_CASE_TABLE( "wine_compatmap", @compatmap_table );
1290 close OUTPUT;
1291 save_file($filename);
1295 ################################################################
1296 # dump a case mapping table
1297 sub DUMP_CASE_TABLE($@)
1299 my ($name,@table) = @_;
1301 # count the number of sub tables that contain something
1302 # also compute the low and upper populated bounds
1304 my @lowerbounds = ( 0, 0 );
1305 my @upperbounds = ( 0, 255 );
1306 my $index = 0;
1307 my @filled = ();
1308 for (my $i = 0; $i < 65536; $i++)
1310 next unless defined $table[$i];
1311 if (!defined $filled[$i >> 8])
1313 $lowerbounds[$index] = $i & 0xff;
1314 $upperbounds[$index] = 0xff - $lowerbounds[$index];
1315 $filled[$i >> 8] = $index * 256 + 512;
1316 $index++;
1318 else
1320 $upperbounds[$index-1] = 0xff - ($i & 0xff);
1322 $table[$i] = ($table[$i] - $i) & 0xffff;
1325 # Collapse blocks upwards if possible
1326 my $removed = 0;
1327 $index = 0;
1328 for (my $i = 0; $i < 256; $i++)
1330 next unless defined $filled[$i];
1331 if ($upperbounds[$index - 1] > $lowerbounds[$index])
1333 $removed = $removed + $lowerbounds[$index];
1335 else
1337 $removed = $removed + $upperbounds[$index - 1];
1338 $lowerbounds[$index] = $upperbounds[$index - 1];
1340 $filled[$i] = $filled[$i] - $removed;
1341 $index++;
1344 # dump the table
1346 printf OUTPUT "const WCHAR %s[%d] =\n", $name, $index * 256 + 512 - $removed;
1347 printf OUTPUT "{\n /* index */\n";
1348 printf OUTPUT "%s,\n", DUMP_ARRAY( "0x%04x", 256, @filled );
1349 printf OUTPUT " /* defaults */\n";
1350 printf OUTPUT "%s", DUMP_ARRAY( "0x%04x", 0, (0) x 256 );
1351 $index = 0;
1352 for (my $i = 0; $i < 256; $i++)
1354 next unless $filled[$i];
1355 printf OUTPUT ",\n /* 0x%02x%02x .. 0x%02xff */\n", $i, $lowerbounds[$index], $i;
1356 printf OUTPUT "%s", DUMP_ARRAY( "0x%04x", 0,
1357 @table[($i<<8) + $lowerbounds[$index] .. ($i<<8)+255] );
1358 $index++;
1360 printf OUTPUT "\n};\n";
1363 ################################################################
1364 # compress a mapping table by removing identical rows
1365 sub compress_array($@)
1367 my $rows = shift;
1368 my @table = @_;
1369 my $len = @table / $rows;
1370 my @array = (0) x $rows;
1371 my %sequences;
1373 # try to merge table rows
1374 for (my $row = 0; $row < $rows; $row++)
1376 my $rowtxt = pack "S*", @table[($row * $len)..($row * $len + $len - 1)];
1377 if (defined($sequences{$rowtxt}))
1379 # reuse an existing row
1380 $array[$row] = $sequences{$rowtxt};
1382 else
1384 # create a new row
1385 $sequences{$rowtxt} = $array[$row] = $#array + 1;
1386 push @array, @table[$row * $len..$row * $len + $len - 1];
1389 return @array;
1392 ################################################################
1393 # dump a simple char -> 16-bit value mapping table
1394 sub dump_simple_mapping($@)
1396 my $name = shift;
1397 my @array = compress_array( 256, @_[0..65535] );
1399 printf OUTPUT "const unsigned short %s[%d] =\n{\n", $name, $#array+1;
1400 printf OUTPUT " /* offsets */\n%s,\n", DUMP_ARRAY( "0x%04x", 0, @array[0..255] );
1401 printf OUTPUT " /* values */\n%s\n};\n", DUMP_ARRAY( "0x%04x", 0, @array[256..$#array] );
1404 ################################################################
1405 # dump a char -> 16-bit value mapping table using two-level tables
1406 sub dump_two_level_mapping($@)
1408 my $name = shift;
1409 my @row_array = compress_array( 4096, @_[0..65535] );
1410 my @array = compress_array( 256, @row_array[0..4095] );
1412 for (my $i = 256; $i < @array; $i++) { $array[$i] += @array - 4096; }
1414 printf OUTPUT "const unsigned short %s[%d] =\n{\n", $name, @array + @row_array - 4096;
1415 printf OUTPUT " /* level 1 offsets */\n%s,\n", DUMP_ARRAY( "0x%04x", 0, @array[0..255] );
1416 printf OUTPUT " /* level 2 offsets */\n%s,\n", DUMP_ARRAY( "0x%04x", 0, @array[256..$#array] );
1417 printf OUTPUT " /* values */\n%s\n};\n", DUMP_ARRAY( "0x%04x", 0, @row_array[4096..$#row_array] );
1420 ################################################################
1421 # dump a binary case mapping table in l_intl.nls format
1422 sub dump_binary_case_table(@)
1424 my (@table) = @_;
1426 my %difftables_hash = ();
1427 my @difftables;
1428 my %offtables2_hash = ();
1429 my @offtables2 = ();
1431 my @offtable = ();
1432 for (my $i = 0; $i < 256; $i++)
1434 my @offtable2 = ();
1435 for(my $j = 0; $j < 16; $j++) # offset table for xx00-xxFF characters
1437 my @difftable;
1438 for (my $k = 0; $k < 16; $k++) # case map table for xxx0-xxxF characters
1440 my $char = ($i<<8) + ($j<<4) + $k;
1441 $difftable[$k] = (defined $table[$char]) ? (($table[$char]-$char) & 0xffff) : 0;
1444 my $diff_key = pack "S*", @difftable;
1445 my $offset3 = $difftables_hash{$diff_key};
1446 if (!defined $offset3)
1448 $offset3 = scalar @difftables;
1449 $difftables_hash{$diff_key} = $offset3;
1450 push @difftables, @difftable;
1452 $offtable2[$j] = $offset3;
1455 my $offtable2_key = pack "S*", @offtable2;
1456 my $offset2 = $offtables2_hash{$offtable2_key};
1457 if (!defined $offset2)
1459 $offset2 = scalar @offtables2;
1460 $offtables2_hash{$offtable2_key} = $offset2;
1461 push @offtables2, \@offtable2;
1463 $offtable[$i] = $offset2;
1466 my @output;
1467 my $offset = 0x100; # offset of first subtable in words
1468 foreach (@offtable)
1470 push @output, 0x10 * $_ + $offset; # offset of subtable in words
1473 $offset = 0x100 + 0x10 * scalar @offtables2; # offset of first difftable in words
1474 foreach(@offtables2)
1476 my $table = $_;
1477 foreach(@$table)
1479 push @output, $_ + $offset; # offset of difftable in words
1483 my $len = 1 + scalar @output + scalar @difftables;
1484 return pack "S<*", $len, @output, @difftables;
1488 ################################################################
1489 # dump case mappings for l_intl.nls
1490 sub dump_intl_nls($)
1492 my $filename = shift;
1493 open OUTPUT,">$filename.new" or die "Cannot create $filename";
1494 printf "Building $filename\n";
1496 binmode OUTPUT;
1497 print OUTPUT pack "S<", 1; # version
1498 print OUTPUT dump_binary_case_table( @toupper_table );
1499 print OUTPUT dump_binary_case_table( @tolower_table );
1500 close OUTPUT;
1501 save_file($filename);
1505 sub load_nameprep_range_table($$$)
1507 my ($INPUT, $val, $table_ref) = @_;
1509 while (<$INPUT>)
1511 if (/^\s*([0-9a-fA-F]+)-([0-9a-fA-F]+)/)
1513 my $last = hex $2;
1514 $last = 65535 if($last >= 65536);
1515 foreach my $i (hex $1 .. $last)
1517 $table_ref->[$i] |= $val;
1519 next;
1521 elsif (/^\s*([0-9a-fA-F]+)/)
1523 if (hex $1 < 65536)
1525 $table_ref->[hex $1] |= $val;
1527 next;
1530 return if (/End\sTable/);
1534 sub load_nameprep_map_table($$)
1536 my ($INPUT, $table_ref) = @_;
1538 while (<$INPUT>)
1540 if (/^\s*([0-9a-fA-F]+);\s;/)
1542 # special value for map to nothing
1543 $table_ref->[hex $1] = [0xffff, 0xffff, 0xffff];
1544 next;
1546 elsif (/^\s*([0-9a-fA-F]+);\s([0-9a-fA-F]+);/)
1548 $table_ref->[hex $1] = [hex $2, 0, 0];
1549 next;
1551 elsif (/^\s*([0-9a-fA-F]+);\s([0-9a-fA-F]+)\s([0-9a-fA-F]+);/)
1553 $table_ref->[hex $1] = [hex $2, hex $3, 0];
1554 next;
1556 elsif (/^\s*([0-9a-fA-F]+);\s([0-9a-fA-F]+)\s([0-9a-fA-F]+)\s([0-9a-fA-F]+);/)
1558 $table_ref->[hex $1] = [hex $2, hex $3, hex $4];
1559 next;
1562 return if (/End\sTable/);
1566 ################################################################
1567 # dump mapping table, prohibited characters set, unassigned
1568 # characters, bidirectional rules used by nameprep algorithm
1569 sub dump_nameprep($)
1571 my $filename = shift;
1572 my @mapping_table = ();
1573 my @flags_table = (0) x 65536;
1575 my $INPUT = open_data_file $STRINGPREP;
1576 while (<$INPUT>)
1578 next unless /Start\sTable/;
1580 load_nameprep_range_table($INPUT, $nameprep_flags{"unassigned"}, \@flags_table) if (/A.1/);
1581 load_nameprep_range_table($INPUT, $nameprep_flags{"prohibited"}, \@flags_table) if (/C.1.2/);
1582 load_nameprep_range_table($INPUT, $nameprep_flags{"prohibited"}, \@flags_table) if (/C.2.2/);
1583 load_nameprep_range_table($INPUT, $nameprep_flags{"prohibited"}, \@flags_table) if (/C.3/);
1584 load_nameprep_range_table($INPUT, $nameprep_flags{"prohibited"}, \@flags_table) if (/C.4/);
1585 load_nameprep_range_table($INPUT, $nameprep_flags{"prohibited"}, \@flags_table) if (/C.5/);
1586 load_nameprep_range_table($INPUT, $nameprep_flags{"prohibited"}, \@flags_table) if (/C.6/);
1587 load_nameprep_range_table($INPUT, $nameprep_flags{"prohibited"}, \@flags_table) if (/C.7/);
1588 load_nameprep_range_table($INPUT, $nameprep_flags{"prohibited"}, \@flags_table) if (/C.8/);
1589 load_nameprep_range_table($INPUT, $nameprep_flags{"prohibited"}, \@flags_table) if (/C.9/);
1590 load_nameprep_range_table($INPUT, $nameprep_flags{"bidi_ral"}, \@flags_table) if (/D.1/);
1591 load_nameprep_range_table($INPUT, $nameprep_flags{"bidi_l"}, \@flags_table) if (/D.2/);
1593 load_nameprep_map_table($INPUT, \@mapping_table) if (/B.1/);
1594 load_nameprep_map_table($INPUT, \@mapping_table) if (/B.2/);
1596 close $INPUT;
1598 open OUTPUT,">$filename.new" or die "Cannot create $filename";
1599 print "Building $filename\n";
1600 print OUTPUT "/* Nameprep algorithm related data */\n";
1601 print OUTPUT "/* generated from $STRINGPREP */\n";
1602 print OUTPUT "/* DO NOT EDIT!! */\n\n";
1603 print OUTPUT "#include \"wine/unicode.h\"\n\n";
1605 dump_two_level_mapping( "nameprep_char_type", @flags_table );
1607 ######### mapping table
1608 # first determine all the 16-char subsets that contain something
1609 my @filled = ();
1610 my $pos = 16*3; # for the null subset
1611 for (my $i = 0; $i < 65536; $i++)
1613 next unless defined $mapping_table[$i];
1614 $filled[$i >> 4] = $pos;
1615 $pos += 16*3;
1616 $i |= 15;
1618 my $total = $pos;
1620 # now count the 256-char subsets that contain something
1621 my @filled_idx = (256) x 256;
1622 $pos = 256 + 16;
1623 for (my $i = 0; $i < 4096; $i++)
1625 next unless $filled[$i];
1626 $filled_idx[$i >> 4] = $pos;
1627 $pos += 16;
1628 $i |= 15;
1630 my $null_offset = $pos;
1631 $total += $pos;
1633 # add the index offsets to the subsets positions
1634 for (my $i = 0; $i < 4096; $i++)
1636 next unless $filled[$i];
1637 $filled[$i] += $null_offset;
1640 # dump the main index
1641 printf OUTPUT "const WCHAR nameprep_mapping[%d] =\n", $total;
1642 printf OUTPUT "{\n /* index */\n";
1643 printf OUTPUT "%s", DUMP_ARRAY( "0x%04x", 0, @filled_idx );
1644 printf OUTPUT ",\n /* null sub-index */\n%s", DUMP_ARRAY( "0x%04x", 0, ($null_offset) x 16 );
1646 # dump the second-level indexes
1647 for (my $i = 0; $i < 256; $i++)
1649 next unless ($filled_idx[$i] > 256);
1650 my @table = @filled[($i<<4)..($i<<4)+15];
1651 for (my $j = 0; $j < 16; $j++) { $table[$j] ||= $null_offset; }
1652 printf OUTPUT ",\n /* sub-index %02x */\n", $i;
1653 printf OUTPUT "%s", DUMP_ARRAY( "0x%04x", 0, @table );
1656 # dump the 16-char subsets
1657 printf OUTPUT ",\n /* null mapping */\n";
1658 printf OUTPUT "%s", DUMP_ARRAY( "0x%04x", 0, (0) x 48 );
1660 for (my $i = 0; $i < 4096; $i++)
1662 next unless $filled[$i];
1663 my @table = (0) x 48;
1664 for (my $j = 0; $j < 16; $j++)
1666 if (defined $mapping_table[($i<<4) + $j])
1668 $table[3 * $j] = ${$mapping_table[($i << 4) + $j]}[0];
1669 $table[3 * $j + 1] = ${$mapping_table[($i << 4) + $j]}[1];
1670 $table[3 * $j + 2] = ${$mapping_table[($i << 4) + $j]}[2];
1673 printf OUTPUT ",\n /* 0x%03x0 .. 0x%03xf */\n", $i, $i;
1674 printf OUTPUT "%s", DUMP_ARRAY( "0x%04x", 0, @table );
1677 printf OUTPUT "\n};\n";
1679 close OUTPUT;
1680 save_file($filename);
1683 ################################################################
1684 # dump the ctype tables
1685 sub DUMP_CTYPE_TABLES($)
1687 my $filename = shift;
1688 open OUTPUT,">$filename.new" or die "Cannot create $filename";
1689 printf "Building $filename\n";
1690 printf OUTPUT "/* Unicode ctype tables */\n";
1691 printf OUTPUT "/* Automatically generated; DO NOT EDIT!! */\n\n";
1692 printf OUTPUT "#include \"wine/unicode.h\"\n\n";
1694 # add the direction in the high 4 bits of the category
1695 for (my $i = 0; $i < 65536; $i++)
1697 $category_table[$i] |= $direction_table[$i] << 12 if defined $direction_table[$i];
1700 dump_simple_mapping( "wine_wctype_table", @category_table );
1702 close OUTPUT;
1703 save_file($filename);
1707 ################################################################
1708 # dump the char composition tables
1709 sub DUMP_COMPOSE_TABLES($)
1711 my $filename = shift;
1713 open OUTPUT,">$filename.new" or die "Cannot create $filename";
1714 printf "Building $filename\n";
1715 printf OUTPUT "/* Unicode char composition */\n";
1716 printf OUTPUT "/* Automatically generated; DO NOT EDIT!! */\n\n";
1717 printf OUTPUT "#include \"wine/unicode.h\"\n\n";
1719 ######### composition table
1721 my @filled = ();
1722 foreach my $i (@compose_table)
1724 my @comp = @$i;
1725 push @{$filled[$comp[1]]}, [ $comp[0], $comp[2] ];
1728 # count how many different second chars we have
1730 my $count = 0;
1731 for (my $i = 0; $i < 65536; $i++)
1733 next unless defined $filled[$i];
1734 $count++;
1737 # build the table of second chars and offsets
1739 my $pos = $count + 1;
1740 my @table = ();
1741 for (my $i = 0; $i < 65536; $i++)
1743 next unless defined $filled[$i];
1744 push @table, $i, $pos;
1745 $pos += @{$filled[$i]};
1747 # terminator with last position
1748 push @table, 0, $pos;
1749 printf OUTPUT "const WCHAR unicode_compose_table[0x%x] =\n{\n", 2*$pos;
1750 printf OUTPUT " /* second chars + offsets */\n%s", DUMP_ARRAY( "0x%04x", 0, @table );
1752 # build the table of first chars and mappings
1754 for (my $i = 0; $i < 65536; $i++)
1756 next unless defined $filled[$i];
1757 my @table = ();
1758 my @list = sort { $a->[0] <=> $b->[0] } @{$filled[$i]};
1759 for (my $j = 0; $j <= $#list; $j++)
1761 push @table, $list[$j][0], $list[$j][1];
1763 printf OUTPUT ",\n /* 0x%04x */\n%s", $i, DUMP_ARRAY( "0x%04x", 0, @table );
1765 printf OUTPUT "\n};\n\nconst unsigned int unicode_compose_table_size = %d;\n\n", $count;
1767 ######### decomposition table
1769 # first determine all the 16-char subsets that contain something
1771 @filled = (0) x 4096;
1772 $pos = 16*2; # for the null subset
1773 for (my $i = 0; $i < 65536; $i++)
1775 next unless defined $decomp_table[$i];
1776 $filled[$i >> 4] = $pos;
1777 $pos += 16*2;
1778 $i |= 15;
1780 my $total = $pos;
1782 # now count the 256-char subsets that contain something
1784 my @filled_idx = (256) x 256;
1785 $pos = 256 + 16;
1786 for (my $i = 0; $i < 4096; $i++)
1788 next unless $filled[$i];
1789 $filled_idx[$i >> 4] = $pos;
1790 $pos += 16;
1791 $i |= 15;
1793 my $null_offset = $pos; # null mapping
1794 $total += $pos;
1796 # add the index offsets to the subsets positions
1798 for (my $i = 0; $i < 4096; $i++)
1800 next unless $filled[$i];
1801 $filled[$i] += $null_offset;
1804 # dump the main index
1806 printf OUTPUT "const WCHAR unicode_decompose_table[%d] =\n", $total;
1807 printf OUTPUT "{\n /* index */\n";
1808 printf OUTPUT "%s", DUMP_ARRAY( "0x%04x", 0, @filled_idx );
1809 printf OUTPUT ",\n /* null sub-index */\n%s", DUMP_ARRAY( "0x%04x", 0, ($null_offset) x 16 );
1811 # dump the second-level indexes
1813 for (my $i = 0; $i < 256; $i++)
1815 next unless ($filled_idx[$i] > 256);
1816 my @table = @filled[($i<<4)..($i<<4)+15];
1817 for (my $j = 0; $j < 16; $j++) { $table[$j] ||= $null_offset; }
1818 printf OUTPUT ",\n /* sub-index %02x */\n", $i;
1819 printf OUTPUT "%s", DUMP_ARRAY( "0x%04x", 0, @table );
1822 # dump the 16-char subsets
1824 printf OUTPUT ",\n /* null mapping */\n";
1825 printf OUTPUT "%s", DUMP_ARRAY( "0x%04x", 0, (0) x 32 );
1827 for (my $i = 0; $i < 4096; $i++)
1829 next unless $filled[$i];
1830 my @table = (0) x 32;
1831 for (my $j = 0; $j < 16; $j++)
1833 if (defined $decomp_table[($i<<4) + $j])
1835 $table[2 * $j] = ${$decomp_table[($i << 4) + $j]}[0];
1836 $table[2 * $j + 1] = ${$decomp_table[($i << 4) + $j]}[1];
1839 printf OUTPUT ",\n /* 0x%03x0 .. 0x%03xf */\n", $i, $i;
1840 printf OUTPUT "%s", DUMP_ARRAY( "0x%04x", 0, @table );
1843 printf OUTPUT "\n};\n";
1844 close OUTPUT;
1845 save_file($filename);
1849 ################################################################
1850 # handle a "bestfit" Windows mapping file
1852 sub handle_bestfit_file($$$)
1854 my ($filename, $has_glyphs, $comment) = @_;
1855 my $state = "";
1856 my ($codepage, $width, $def, $defw, $count);
1857 my ($lb_cur, $lb_end);
1858 my @lb_ranges = ();
1860 my $INPUT = open_data_file "$MAPPINGS/$filename" or die "Cannot open $filename";
1862 while (<$INPUT>)
1864 next if /^;/; # skip comments
1865 next if /^\s*$/; # skip empty lines
1866 next if /\x1a/; # skip ^Z
1867 last if /^ENDCODEPAGE/;
1869 if (/^CODEPAGE\s+(\d+)/)
1871 $codepage = $1;
1872 next;
1874 if (/^CPINFO\s+(\d+)\s+0x([0-9a-fA-f]+)\s+0x([0-9a-fA-F]+)/)
1876 $width = $1;
1877 $def = hex $2;
1878 $defw = hex $3;
1879 next;
1881 if (/^(MBTABLE|WCTABLE|DBCSRANGE|DBCSTABLE)\s+(\d+)/)
1883 $state = $1;
1884 $count = $2;
1885 next;
1887 if (/^0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)/)
1889 if ($state eq "MBTABLE")
1891 my $cp = hex $1;
1892 my $uni = hex $2;
1893 $cp2uni[$cp] = $uni unless defined($cp2uni[$cp]);
1894 next;
1896 if ($state eq "WCTABLE")
1898 my $uni = hex $1;
1899 my $cp = hex $2;
1900 $uni2cp[$uni] = $cp unless defined($uni2cp[$uni]);
1901 next;
1903 if ($state eq "DBCSRANGE")
1905 my $start = hex $1;
1906 my $end = hex $2;
1907 push @lb_ranges, $start, $end;
1908 for (my $i = $start; $i <= $end; $i++)
1910 push @lead_bytes, $i;
1911 $cp2uni[$i] = 0;
1913 $lb_cur = $start;
1914 $lb_end = $end;
1915 next;
1917 if ($state eq "DBCSTABLE")
1919 my $mb = hex $1;
1920 my $uni = hex $2;
1921 my $cp = ($lb_cur << 8) | $mb;
1922 $cp2uni[$cp] = $uni unless defined($cp2uni[$cp]);
1923 if (!--$count)
1925 if (++$lb_cur > $lb_end) { $state = "DBCSRANGE"; }
1927 next;
1930 die "$filename: Unrecognized line $_\n";
1932 close $INPUT;
1934 my $output = sprintf "libs/wine/c_%03d.c", $codepage;
1935 open OUTPUT,">$output.new" or die "Cannot create $output";
1937 printf "Building %s from %s (%s)\n", $output, $filename, $comment;
1939 # dump all tables
1941 printf OUTPUT "/* code page %03d (%s) */\n", $codepage, $comment;
1942 printf OUTPUT "/* generated from $MAPPINGS/$filename */\n";
1943 printf OUTPUT "/* DO NOT EDIT!! */\n\n";
1944 printf OUTPUT "#include \"wine/unicode.h\"\n\n";
1946 if ($width == 1) { dump_sbcs_table( $codepage, $has_glyphs, $comment, $def, $defw ); }
1947 else { dump_dbcs_table( $codepage, $comment, $def, $defw, @lb_ranges ); }
1948 close OUTPUT;
1949 save_file($output);
1953 ################################################################
1954 # read an input file and generate the corresponding .c file
1955 sub HANDLE_FILE(@)
1957 my ($codepage,$filename,$has_glyphs,$comment) = @_;
1959 @cp2uni = ();
1960 @lead_bytes = ();
1961 @uni2cp = ();
1963 # symbol codepage file is special
1964 if ($codepage == 20932) { READ_JIS0208_FILE "$MAPPINGS/$filename"; }
1965 elsif ($codepage == 20127) { fill_20127_codepage(); }
1966 elsif ($filename =~ /\/bestfit/)
1968 handle_bestfit_file( $filename, $has_glyphs, $comment );
1969 return;
1971 else { READ_FILE "$MAPPINGS/$filename"; }
1973 ADD_DEFAULT_MAPPINGS();
1975 my $output = sprintf "libs/wine/c_%03d.c", $codepage;
1976 open OUTPUT,">$output.new" or die "Cannot create $output";
1978 printf "Building %s from %s (%s)\n", $output, $filename || "hardcoded data", $comment;
1980 # dump all tables
1982 printf OUTPUT "/* code page %03d (%s) */\n", $codepage, $comment;
1983 if ($filename)
1985 print OUTPUT "/* generated from $MAPPINGS/$filename */\n";
1986 print OUTPUT "/* DO NOT EDIT!! */\n\n";
1988 else
1990 printf OUTPUT "/* Automatically generated; DO NOT EDIT!! */\n\n";
1992 printf OUTPUT "#include \"wine/unicode.h\"\n\n";
1994 if (!@lead_bytes) { dump_sbcs_table( $codepage, $has_glyphs, $comment, $DEF_CHAR, $DEF_CHAR ); }
1995 else { dump_dbcs_table( $codepage, $comment, $DEF_CHAR, $DEF_CHAR, get_lb_ranges() ); }
1996 close OUTPUT;
1997 save_file($output);
2001 ################################################################
2002 # save a file if modified
2003 sub save_file($)
2005 my $file = shift;
2006 if (-f $file && !system "cmp $file $file.new >/dev/null")
2008 unlink "$file.new";
2010 else
2012 rename "$file.new", "$file";
2017 ################################################################
2018 # output the list of codepage tables into the cptable.c file
2019 sub output_cptable($)
2021 my $output = shift;
2022 my @tables_decl = ();
2024 printf "Building %s\n", $output;
2026 foreach my $file (@allfiles)
2028 my ($codepage,$filename,$comment) = @$file;
2029 push @tables_decl, sprintf("extern union cptable cptable_%03d;\n",$codepage);
2032 push @tables_decl, sprintf("\nstatic const union cptable * const cptables[%d] =\n{\n",$#allfiles+1);
2033 foreach my $file (@allfiles)
2035 my ($codepage,$filename,$comment) = @$file;
2036 push @tables_decl, sprintf(" &cptable_%03d,\n", $codepage);
2038 push @tables_decl, "};";
2039 REPLACE_IN_FILE( $output, @tables_decl );
2042 ################################################################
2043 # replace the contents of a file between ### cpmap ### marks
2045 sub REPLACE_IN_FILE($@)
2047 my $name = shift;
2048 my @data = @_;
2049 my @lines = ();
2050 open(FILE,$name) or die "Can't open $name";
2051 while (<FILE>)
2053 push @lines, $_;
2054 last if /\#\#\# cpmap begin \#\#\#/;
2056 push @lines, @data;
2057 while (<FILE>)
2059 if (/\#\#\# cpmap end \#\#\#/) { push @lines, "\n", $_; last; }
2061 push @lines, <FILE>;
2062 open(FILE,">$name.new") or die "Can't modify $name";
2063 print FILE @lines;
2064 close(FILE);
2065 save_file($name);
2068 ################################################################
2069 # main routine
2071 chdir ".." if -f "./make_unicode";
2072 READ_DEFAULTS( $DEFAULTS );
2073 DUMP_CASE_MAPPINGS( "libs/wine/casemap.c" );
2074 DUMP_SORTKEYS( "libs/wine/collation.c", READ_SORTKEYS_FILE() );
2075 DUMP_COMPOSE_TABLES( "libs/wine/compose.c" );
2076 DUMP_CTYPE_TABLES( "libs/wine/wctype.c" );
2077 dump_mirroring( "dlls/usp10/mirror.c" );
2078 dump_shaping( "dlls/usp10/shaping.c" );
2079 dump_linebreak( "dlls/usp10/linebreak.c" );
2080 dump_indic( "dlls/usp10/indicsyllable.c" );
2081 dump_intl_nls("tools/l_intl.nls");
2082 dump_nameprep( "dlls/kernel32/nameprep.c" );
2084 foreach my $file (@allfiles) { HANDLE_FILE( @{$file} ); }
2086 output_cptable("libs/wine/cptable.c");
2088 exit 0;
2090 # Local Variables:
2091 # compile-command: "./make_unicode"
2092 # End: