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
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";
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";
35 my $DEFAULTS = "tools/unicode-defaults";
37 # Default char for undefined mappings
38 my $DEF_CHAR = ord '?';
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)" ]
126 "Avagraha" => 0x0003,
129 "Vowel_Independent" => 0x0006,
130 "Vowel_Dependent" => 0x0007,
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
150 "Visual_Order_Left" => 0x03,
151 "Left_And_Right" => 0x04,
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,
166 "unassigned" => 0x01,
167 "prohibited" => 0x02,
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,
260 "digit" => [ 0xb2, 0xb3, 0xb9 ],
261 "lower" => [ 0x2071, 0x207f ]
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
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
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 = ();
320 ################################################################
321 # fetch a unicode.org file and open it
322 sub open_data_file($)
325 (my $name = $url) =~ s/^.*\///;
327 unless (-f "data/$name")
329 print "Fetching $url...\n";
331 !system "wget", "-q", "-O", "data/$name", $url or die "cannot fetch $url";
333 open FILE, "<data/$name" or die "cannot open data/$name";
337 ################################################################
338 # read in the defaults file
341 my $filename = shift;
344 # first setup a few default mappings
346 open DEFAULTS, "$filename" or die "Cannot open $filename";
347 print "Loading $filename\n";
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;
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;
367 die "Unrecognized line $_\n";
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 /;/;
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";
392 $tolower_table[$src] = hex $lower;
396 $toupper_table[$src] = hex $upper;
400 $category_table[$src] |= $ctype{"digit"};
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];
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;
434 next unless ($1 eq "font" ||
445 elsif ($decomp =~ /^<compat>\s+0020\s+([0-9a-fA-F]+)/)
447 # decomposition "<compat> 0020 1234" -> combining accent
450 elsif ($decomp =~ /^([0-9a-fA-F]+)/)
452 # decomposition contains only char values without prefix -> use first char
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;
473 next if defined($unicode_defaults[$src]); # may have been set in the defaults file
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;
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
500 my $INPUT = open_data_file $name;
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/)
512 push @lead_bytes,$cp;
516 if (/^0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)\s+(\#.*)?/)
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;
529 die "$name: Unrecognized line $_\n";
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)
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($)
592 for (my $i = 0x00; $i <= 0x7f; $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;
606 foreach my $i (0x8e, 0x8f, 0xa1 .. 0xfe)
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;
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;
636 $cp2uni[$cp] = $uni unless defined($cp2uni[$cp]);
637 $uni2cp[$uni] = $cp unless defined($uni2cp[$uni]);
640 die "$name: Unrecognized line $_\n";
646 ################################################################
647 # build the sort keys table
648 sub READ_SORTKEYS_FILE()
651 for (my $i = 0; $i < 65536; $i++) { $sortkeys[$i] = [ -1, 0, 0, 0, 0 ] };
653 my $INPUT = open_data_file $SORTKEYS;
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 ];
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
672 die "$SORTKEYS: Unrecognized line $_\n";
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 );
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])
703 $keys[3] = $current[3];
710 $keys[2] = $current[2];
711 $keys[3] = $current[3];
719 $keys[1] = $current[1];
720 $keys[2] = $current[2];
721 $keys[3] = $current[3];
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];
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
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;
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";
782 save_file($filename);
786 ################################################################
787 # add default mappings once the file had been read
788 sub ADD_DEFAULT_MAPPINGS()
792 foreach my $alias (@unicode_aliases)
795 foreach my $src (@$alias)
797 if (defined($uni2cp[$src]))
799 $target = $uni2cp[$src];
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
842 my ($format,$default,@array) = @_;
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);
854 ################################################################
855 # dump an SBCS mapping table
856 sub dump_sbcs_table($$$$$)
858 my ($codepage, $has_glyphs, $name, $def, $defw) = @_;
861 # output the ascii->unicode table
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]) );
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
880 for (my $i = 0; $i < 65536; $i++)
882 next unless defined $uni2cp[$i];
883 $filled[$i >> 8] = 1;
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
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
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];
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
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
974 for (my $x = 0; $x < 256; $x++) { $offsets[$x] = 0; }
975 for (my $x = 0; $x <= $#lblist; $x++) { $offsets[$lblist[$x]] = $x + 1; }
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
988 for (my $i = 0; $i < 65536; $i++)
990 next unless defined $uni2cp[$i];
991 $filled[$i >> 8] = 1;
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
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
1041 foreach $i (@lead_bytes) { $list[$i] = 1; }
1043 for (my $i = 0; $i < 256; $i++)
1047 if (!defined $list[$i]) { push @ranges, $i-1; $on = 0; }
1051 if ($list[$i]) { push @ranges, $i; $on = 1; }
1054 if ($on) { push @ranges, 0xff; }
1058 ################################################################
1059 # dump the Indic Syllabic Category table
1062 my $filename = shift;
1063 my @indic_table = ($indic_types{'Other'}) x 65536;;
1065 my $INPUT = open_data_file "$UNIDATA/IndicSyllabicCategory.txt";
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*#/)
1074 die "unknown indic $type" unless defined $indic_types{$type};
1077 $indic_table[hex $1] = $indic_types{$type};
1081 elsif (/^\s*([0-9a-fA-F]+)..\s*([0-9a-fA-F]+)\s*;\s*([A-Za-z_]+)\s*#/)
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};
1094 die "malformed line $_";
1098 $INPUT = open_data_file "$UNIDATA/IndicMatraCategory.txt";
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*#/)
1107 die "unknown matra $type" unless defined $matra_types{$type};
1108 $indic_table[hex $1] += $matra_types{$type} << 8;
1111 elsif (/^\s*([0-9a-fA-F]+)..\s*([0-9a-fA-F]+)\s*;\s*([A-Za-z_]+)\s*#/)
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;
1121 die "malformed line $_";
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);
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;;
1147 my $INPUT = open_data_file "$UNIDATA/LineBreak.txt";
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*/)
1156 die "unknown breaktype $type" unless defined $break_types{$type};
1157 $break_table[hex $1] = $break_types{$type};
1160 elsif (/^\s*([0-9a-fA-F]+)..\s*([0-9a-fA-F]+)\s*;\s*([0-9A-Z][0-9A-Z])+\s*/)
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};
1170 die "malformed line $_";
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);
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";
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;
1206 die "malformed line $_";
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 );
1218 save_file($filename);
1222 ################################################################
1223 # dump the Arabic shaping table
1226 my $filename = shift;
1230 $groups{"No_Joining_Group"} = $next_group++;
1232 my $INPUT = open_data_file "$UNIDATA/ArabicShaping.txt";
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+)/)
1242 $groups{$group} = $next_group++ unless defined $groups{$group};
1243 $joining_table[hex $1] = $joining_types{$type} | ($groups{$group} << 8);
1246 die "malformed line $_";
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";
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 );
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 );
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;
1320 $upperbounds[$index-1] = 0xff - ($i & 0xff);
1322 $table[$i] = ($table[$i] - $i) & 0xffff;
1325 # Collapse blocks upwards if possible
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];
1337 $removed = $removed + $upperbounds[$index - 1];
1338 $lowerbounds[$index] = $upperbounds[$index - 1];
1340 $filled[$i] = $filled[$i] - $removed;
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 );
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] );
1360 printf OUTPUT "\n};\n";
1363 ################################################################
1364 # compress a mapping table by removing identical rows
1365 sub compress_array($@)
1369 my $len = @table / $rows;
1370 my @array = (0) x $rows;
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};
1385 $sequences{$rowtxt} = $array[$row] = $#array + 1;
1386 push @array, @table[$row * $len..$row * $len + $len - 1];
1392 ################################################################
1393 # dump a simple char -> 16-bit value mapping table
1394 sub dump_simple_mapping($@)
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($@)
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(@)
1426 my %difftables_hash = ();
1428 my %offtables2_hash = ();
1429 my @offtables2 = ();
1432 for (my $i = 0; $i < 256; $i++)
1435 for(my $j = 0; $j < 16; $j++) # offset table for xx00-xxFF characters
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;
1467 my $offset = 0x100; # offset of first subtable in words
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)
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";
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 );
1501 save_file($filename);
1505 sub load_nameprep_range_table($$$)
1507 my ($INPUT, $val, $table_ref) = @_;
1511 if (/^\s*([0-9a-fA-F]+)-([0-9a-fA-F]+)/)
1514 $last = 65535 if($last >= 65536);
1515 foreach my $i (hex $1 .. $last)
1517 $table_ref->[$i] |= $val;
1521 elsif (/^\s*([0-9a-fA-F]+)/)
1525 $table_ref->[hex $1] |= $val;
1530 return if (/End\sTable/);
1534 sub load_nameprep_map_table($$)
1536 my ($INPUT, $table_ref) = @_;
1540 if (/^\s*([0-9a-fA-F]+);\s;/)
1542 # special value for map to nothing
1543 $table_ref->[hex $1] = [0xffff, 0xffff, 0xffff];
1546 elsif (/^\s*([0-9a-fA-F]+);\s([0-9a-fA-F]+);/)
1548 $table_ref->[hex $1] = [hex $2, 0, 0];
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];
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];
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;
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/);
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
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;
1620 # now count the 256-char subsets that contain something
1621 my @filled_idx = (256) x 256;
1623 for (my $i = 0; $i < 4096; $i++)
1625 next unless $filled[$i];
1626 $filled_idx[$i >> 4] = $pos;
1630 my $null_offset = $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";
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 );
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
1722 foreach my $i (@compose_table)
1725 push @{$filled[$comp[1]]}, [ $comp[0], $comp[2] ];
1728 # count how many different second chars we have
1731 for (my $i = 0; $i < 65536; $i++)
1733 next unless defined $filled[$i];
1737 # build the table of second chars and offsets
1739 my $pos = $count + 1;
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];
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;
1782 # now count the 256-char subsets that contain something
1784 my @filled_idx = (256) x 256;
1786 for (my $i = 0; $i < 4096; $i++)
1788 next unless $filled[$i];
1789 $filled_idx[$i >> 4] = $pos;
1793 my $null_offset = $pos; # null mapping
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";
1845 save_file($filename);
1849 ################################################################
1850 # handle a "bestfit" Windows mapping file
1852 sub handle_bestfit_file($$$)
1854 my ($filename, $has_glyphs, $comment) = @_;
1856 my ($codepage, $width, $def, $defw, $count);
1857 my ($lb_cur, $lb_end);
1860 my $INPUT = open_data_file "$MAPPINGS/$filename" or die "Cannot open $filename";
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+)/)
1874 if (/^CPINFO\s+(\d+)\s+0x([0-9a-fA-f]+)\s+0x([0-9a-fA-F]+)/)
1881 if (/^(MBTABLE|WCTABLE|DBCSRANGE|DBCSTABLE)\s+(\d+)/)
1887 if (/^0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)/)
1889 if ($state eq "MBTABLE")
1893 $cp2uni[$cp] = $uni unless defined($cp2uni[$cp]);
1896 if ($state eq "WCTABLE")
1900 $uni2cp[$uni] = $cp unless defined($uni2cp[$uni]);
1903 if ($state eq "DBCSRANGE")
1907 push @lb_ranges, $start, $end;
1908 for (my $i = $start; $i <= $end; $i++)
1910 push @lead_bytes, $i;
1917 if ($state eq "DBCSTABLE")
1921 my $cp = ($lb_cur << 8) | $mb;
1922 $cp2uni[$cp] = $uni unless defined($cp2uni[$cp]);
1925 if (++$lb_cur > $lb_end) { $state = "DBCSRANGE"; }
1930 die "$filename: Unrecognized line $_\n";
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;
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 ); }
1953 ################################################################
1954 # read an input file and generate the corresponding .c file
1957 my ($codepage,$filename,$has_glyphs,$comment) = @_;
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 );
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;
1982 printf OUTPUT "/* code page %03d (%s) */\n", $codepage, $comment;
1985 print OUTPUT "/* generated from $MAPPINGS/$filename */\n";
1986 print OUTPUT "/* DO NOT EDIT!! */\n\n";
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() ); }
2001 ################################################################
2002 # save a file if modified
2006 if (-f $file && !system "cmp $file $file.new >/dev/null")
2012 rename "$file.new", "$file";
2017 ################################################################
2018 # output the list of codepage tables into the cptable.c file
2019 sub output_cptable($)
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($@)
2050 open(FILE,$name) or die "Can't open $name";
2054 last if /\#\#\# cpmap begin \#\#\#/;
2059 if (/\#\#\# cpmap end \#\#\#/) { push @lines, "\n", $_; last; }
2061 push @lines, <FILE>;
2062 open(FILE,">$name.new") or die "Can't modify $name";
2068 ################################################################
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");
2091 # compile-command: "./make_unicode"