jscript: Use jsstr_flush in String_concat.
[wine] / tools / make_unicode
1 #!/usr/bin/perl -w
2 #
3 # Generate code page .c files from ftp.unicode.org descriptions
4 #
5 # Copyright 2000 Alexandre Julliard
6 #
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.
11 #
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.
16 #
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
20 #
21
22 use strict;
23
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";
27
28 # Sort keys file
29 my $SORTKEYS = "http://www.unicode.org/reports/tr10/allkeys.txt";
30
31 # RFC3454 (stringprep data)
32 my $STRINGPREP = "http://www.rfc-editor.org/rfc/rfc3454.txt";
33
34 # Defaults mapping
35 my $DEFAULTS = "tools/unicode-defaults";
36
37 # Default char for undefined mappings
38 my $DEF_CHAR = ord '?';
39
40 my @allfiles =
41 (
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)" ]
104 );
105
106
107 my %ctype =
108 (
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
119 );
120
121 my %indic_types =
122 (
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
144 );
145
146 my %matra_types =
147 (
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
162 );
163
164 my %nameprep_flags =
165 (
166     "unassigned" => 0x01,
167     "prohibited" => 0x02,
168     "bidi_ral"   => 0x04,
169     "bidi_l"     => 0x08
170 );
171
172 my %break_types =
173 (
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
211 );
212
213 my %categories =
214 (
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
245 );
246
247 # a few characters need additional categories that cannot be determined automatically
248 my %special_categories =
249 (
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 ]
262 );
263
264 my %directions =
265 (
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
285 );
286
287 my %joining_types =
288 (
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
295 );
296
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 = ();
311
312 my %joining_forms =
313 (
314    "isolated" => [],
315    "final" => [],
316    "initial" => [],
317    "medial" => []
318 );
319
320 ################################################################
321 # fetch a unicode.org file and open it
322 sub open_data_file($)
323 {
324     my $url = shift;
325     (my $name = $url) =~ s/^.*\///;
326     local *FILE;
327     unless (-f "data/$name")
328     {
329         print "Fetching $url...\n";
330         mkdir "data";
331         !system "wget", "-q", "-O", "data/$name", $url or die "cannot fetch $url";
332     }
333     open FILE, "<data/$name" or die "cannot open data/$name";
334     return *FILE;
335 }
336
337 ################################################################
338 # read in the defaults file
339 sub READ_DEFAULTS($)
340 {
341     my $filename = shift;
342     my $start;
343
344     # first setup a few default mappings
345
346     open DEFAULTS, "$filename" or die "Cannot open $filename";
347     print "Loading $filename\n";
348     while (<DEFAULTS>)
349     {
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+(\#.*)?/)
353         {
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)
361             {
362                 die "Duplicate value" if defined($unicode_defaults[$src]);
363                 $unicode_defaults[$src] = $dst;
364             }
365             next;
366         }
367         die "Unrecognized line $_\n";
368     }
369     close DEFAULTS;
370
371     # now build mappings from the decomposition field of the Unicode database
372
373     my $UNICODE_DATA = open_data_file "$UNIDATA/UnicodeData.txt";
374     while (<$UNICODE_DATA>)
375     {
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;
382
383         die "unknown category $cat" unless defined $categories{$cat};
384         die "unknown directionality $bidi" unless defined $directions{$bidi};
385
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";
389
390         if ($lower ne "")
391         {
392             $tolower_table[$src] = hex $lower;
393         }
394         if ($upper ne "")
395         {
396             $toupper_table[$src] = hex $upper;
397         }
398         if ($dec ne "")
399         {
400             $category_table[$src] |= $ctype{"digit"};
401         }
402         if ($dig ne "")
403         {
404             $digitmap_table[$src] = ord $dig;
405         }
406
407         # copy the category and direction for everything between First/Last pairs
408         if ($name =~ /, First>/) { $start = $src; }
409         if ($name =~ /, Last>/)
410         {
411             while ($start < $src)
412             {
413                 $category_table[$start] = $category_table[$src];
414                 $direction_table[$start] = $direction_table[$src];
415                 $start++;
416             }
417         }
418
419         next if $decomp eq "";  # no decomposition, skip it
420
421         if ($decomp =~ /^<([a-zA-Z]+)>\s+([0-9a-fA-F]+)$/)
422         {
423             # decomposition of the form "<foo> 1234" -> use char if type is known
424             if (($src >= 0xf900 && $src < 0xfb00) || ($src >= 0xfe30 && $src < 0xfffd))
425             {
426                 # Single char decomposition in the compatibility range
427                 $compatmap_table[$src] = hex $2;
428             }
429             if ($1 eq "isolated" || $1 eq "final" || $1 eq "initial" || $1 eq "medial")
430             {
431                 ${joining_forms{$1}}[hex $2] = $src;
432                 next;
433             }
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;
444         }
445         elsif ($decomp =~ /^<compat>\s+0020\s+([0-9a-fA-F]+)/)
446         {
447             # decomposition "<compat> 0020 1234" -> combining accent
448             $dst = hex $1;
449         }
450         elsif ($decomp =~ /^([0-9a-fA-F]+)/)
451         {
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]+)$/)
457             {
458                 $decomp_table[$src] = [ hex $1, hex $2 ];
459                 push @compose_table, [ hex $1, hex $2, $src ];
460             }
461             elsif ($decomp =~ /^(<[a-z]+>\s)*([0-9a-fA-F]+)$/ &&
462                    (($src >= 0xf900 && $src < 0xfb00) || ($src >= 0xfe30 && $src < 0xfffd)))
463             {
464                 # Single char decomposition in the compatibility range
465                 $compatmap_table[$src] = hex $2;
466             }
467         }
468         else
469         {
470             next;
471         }
472
473         next if defined($unicode_defaults[$src]);  # may have been set in the defaults file
474
475         # check for loops
476         for (my $i = $dst; ; $i = $unicode_defaults[$i])
477         {
478             die sprintf("loop detected for %04x -> %04x",$src,$dst) if $i == $src;
479             last unless defined($unicode_defaults[$i]);
480         }
481         $unicode_defaults[$src] = $dst;
482     }
483     close $UNICODE_DATA;
484
485     # patch the category of some special characters
486
487     foreach my $cat (keys %special_categories)
488     {
489         my $flag = $ctype{$cat};
490         foreach my $i (@{$special_categories{$cat}}) { $category_table[$i] |= $flag; }
491     }
492 }
493
494
495 ################################################################
496 # parse the input file
497 sub READ_FILE($)
498 {
499     my $name = shift;
500     my $INPUT = open_data_file $name;
501
502     while (<$INPUT>)
503     {
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
508
509         if (/^0x([0-9a-fA-F]+)\s+\#DBCS LEAD BYTE/)
510         {
511             my $cp = hex $1;
512             push @lead_bytes,$cp;
513             $cp2uni[$cp] = 0;
514             next;
515         }
516         if (/^0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)\s+(\#.*)?/)
517         {
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]))
523             {
524                 push @lead_bytes,$cp >> 8;
525                 $cp2uni[$cp >> 8] = 0;
526             }
527             next;
528         }
529         die "$name: Unrecognized line $_\n";
530     }
531     close $INPUT;
532 }
533
534
535 ################################################################
536 # fill input data for the 20127 (us-ascii) codepage
537 sub fill_20127_codepage()
538 {
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; }
541 }
542
543 ################################################################
544 # get a mapping including glyph chars for MB_USEGLYPHCHARS
545
546 sub get_glyphs_mapping(@)
547 {
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 @_;
581 }
582
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($)
588 {
589     my $name = shift;
590
591     # ASCII chars
592     for (my $i = 0x00; $i <= 0x7f; $i++)
593     {
594         $cp2uni[$i] = $i;
595         $uni2cp[$i] = $i;
596     }
597
598     # JIS X 0201 right plane
599     for (my $i = 0xa1; $i <= 0xdf; $i++)
600     {
601         $cp2uni[0x8e00 + $i] = 0xfec0 + $i;
602         $uni2cp[0xfec0 + $i] = 0x8e00 + $i;
603     }
604
605     # lead bytes
606     foreach my $i (0x8e, 0x8f, 0xa1 .. 0xfe)
607     {
608         push @lead_bytes,$i;
609         $cp2uni[$i] = 0;
610     }
611
612     # undefined chars
613     foreach my $i (0x80 .. 0x8d, 0x90 .. 0xa0, 0xff)
614     {
615         $cp2uni[$i] = $DEF_CHAR;
616     }
617
618     # Shift-JIS compatibility
619     $uni2cp[0x00a5] = 0x5c;
620     $uni2cp[0x203e] = 0x7e;
621
622     # Fix backslash conversion
623     $cp2uni[0xa1c0] = 0xff3c;
624     $uni2cp[0xff3c] = 0xa1c0;
625
626     my $INPUT = open_data_file $name;
627     while (<$INPUT>)
628     {
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+(\#.*)?/)
633         {
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;
639         }
640         die "$name: Unrecognized line $_\n";
641     }
642     close $INPUT;
643 }
644
645
646 ################################################################
647 # build the sort keys table
648 sub READ_SORTKEYS_FILE()
649 {
650     my @sortkeys = ();
651     for (my $i = 0; $i < 65536; $i++) { $sortkeys[$i] = [ -1, 0, 0, 0, 0 ] };
652
653     my $INPUT = open_data_file $SORTKEYS;
654     while (<$INPUT>)
655     {
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]+)\]/)
661         {
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;
666         }
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]+)\]/)
668         {
669             # multiple character sequence, ignored for now
670             next;
671         }
672         die "$SORTKEYS: Unrecognized line $_\n";
673     }
674     close $INPUT;
675
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
678
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;
684
685     my ($n2, $n3) = (1, 1);
686     my @keys = (-1, -1, -1, -1, -1 );
687     my @flatkeys = ();
688
689     for (my $i = 0; $i < 65536; $i++)
690     {
691         my @current = @{$sortkeys[$i]};
692         next if $current[0] == -1;
693         if ($current[1] == $keys[1])
694         {
695             if ($current[2] == $keys[2])
696             {
697                 if ($current[3] == $keys[3])
698                 {
699                     # nothing
700                 }
701                 else
702                 {
703                     $keys[3] = $current[3];
704                     $n3++;
705                     die if ($n3 >= 16);
706                 }
707             }
708             else
709             {
710                 $keys[2] = $current[2];
711                 $keys[3] = $current[3];
712                 $n2++;
713                 $n3 = 1;
714                 die if ($n2 >= 256);
715             }
716         }
717         else
718         {
719             $keys[1] = $current[1];
720             $keys[2] = $current[2];
721             $keys[3] = $current[3];
722             $n2 = 1;
723             $n3 = 1;
724         }
725
726         if ($current[2]) { $current[2] = $n2; }
727         if ($current[3]) { $current[3] = $n3; }
728         if ($current[4]) { $current[4] = 1; }
729
730         $flatkeys[$current[0]] = ($current[1] << 16) | ($current[2] << 8) | ($current[3] << 4) | $current[4];
731     }
732     return @flatkeys;
733 }
734
735
736 ################################################################
737 # build the sort keys table
738 sub DUMP_SORTKEYS($@)
739 {
740     my ($filename, @keys) = @_;
741
742     # count the number of 256-key ranges that contain something
743
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++)
748     {
749         next unless defined $keys[$i];
750         $offsets[$i >> 8] = $ranges * 256;
751         $ranges++;
752         $i |= 255;
753     }
754
755     # output the range offsets
756
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";
762
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 );
766
767     # output the default values
768
769     printf OUTPUT "    /* defaults */\n";
770     printf OUTPUT "%s", DUMP_ARRAY( "0x%08x", 0, (0xffffffff) x 256 );
771
772     # output all the key ranges
773
774     for (my $i = 0; $i < 256; $i++)
775     {
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] );
779     }
780     printf OUTPUT "\n};\n";
781     close OUTPUT;
782     save_file($filename);
783 }
784
785
786 ################################################################
787 # add default mappings once the file had been read
788 sub ADD_DEFAULT_MAPPINGS()
789 {
790     # Apply aliases
791
792     foreach my $alias (@unicode_aliases)
793     {
794         my $target = undef;
795         foreach my $src (@$alias)
796         {
797             if (defined($uni2cp[$src]))
798             {
799                 $target = $uni2cp[$src];
800                 last;
801             }
802         }
803         next unless defined($target);
804
805         # At least one char of the alias set is defined, set the others to the same value
806         foreach my $src (@$alias)
807         {
808             $uni2cp[$src] = $target unless defined($uni2cp[$src]);
809         }
810     }
811
812     # For every src -> target mapping in the defaults table,
813     # make uni2cp[src] = uni2cp[target] if uni2cp[target] is defined
814
815     for (my $src = 0; $src < 65536; $src++)
816     {
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];
820
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]; }
824
825         if (defined($uni2cp[$target])) { $uni2cp[$src] = $uni2cp[$target]; }
826     }
827
828     # Add an identity mapping for all undefined chars
829
830     for (my $i = 0; $i < 256; $i++)
831     {
832         next if defined($cp2uni[$i]);
833         next if defined($uni2cp[$i]);
834         $cp2uni[$i] = $uni2cp[$i] = $i;
835     }
836 }
837
838 ################################################################
839 # dump an array of integers
840 sub DUMP_ARRAY($$@)
841 {
842     my ($format,$default,@array) = @_;
843     my $i;
844     my $ret = "    ";
845     for ($i = 0; $i < $#array; $i++)
846     {
847         $ret .= sprintf($format, defined $array[$i] ? $array[$i] : $default);
848         $ret .= (($i % 8) != 7) ? ", " : ",\n    ";
849     }
850     $ret .= sprintf($format, defined $array[$i] ? $array[$i] : $default);
851     return $ret;
852 }
853
854 ################################################################
855 # dump an SBCS mapping table
856 sub dump_sbcs_table($$$$$)
857 {
858     my ($codepage, $has_glyphs, $name, $def, $defw) = @_;
859     my $i;
860
861     # output the ascii->unicode table
862
863     if ($has_glyphs)
864     {
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]) );
869     }
870     else
871     {
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] );
874     }
875
876     # count the number of unicode->ascii subtables that contain something
877
878     my @filled = ();
879     my $subtables = 1;
880     for (my $i = 0; $i < 65536; $i++)
881     {
882         next unless defined $uni2cp[$i];
883         $filled[$i >> 8] = 1;
884         $subtables++;
885         $i |= 255;
886     }
887
888     # output all the subtables into a single array
889
890     printf OUTPUT "static const unsigned char uni2cp_low[%d] =\n{\n", $subtables*256;
891     for (my $i = 0; $i < 256; $i++)
892     {
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] );
896     }
897     printf OUTPUT "    /* defaults */\n";
898     printf OUTPUT "%s\n};\n\n", DUMP_ARRAY( "0x%02x", 0, ($def) x 256 );
899
900     # output a table of the offsets of the subtables in the previous array
901
902     my $pos = 0;
903     my @offsets = ();
904     for (my $i = 0; $i < 256; $i++)
905     {
906         if ($filled[$i]) { push @offsets, $pos; $pos += 256; }
907         else { push @offsets, ($subtables-1) * 256; }
908     }
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 );
911
912     # output the code page descriptor
913
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";
922 }
923
924
925 ################################################################
926 # dump a DBCS mapping table
927 sub dump_dbcs_table($$$$@)
928 {
929     my ($codepage, $name, $def, $defw, @lb_ranges) = @_;
930
931     # build a list of lead bytes that are actually used
932
933     my @lblist = ();
934     LBLOOP: for (my $y = 0; $y <= $#lead_bytes; $y++)
935     {
936         my $base = $lead_bytes[$y] << 8;
937         for (my $x = 0; $x < 256; $x++)
938         {
939             if (defined $cp2uni[$base+$x])
940             {
941                 push @lblist,$lead_bytes[$y];
942                 next LBLOOP;
943             }
944         }
945     }
946     my $unused = ($#lead_bytes > $#lblist);
947
948     # output the ascii->unicode table for the single byte chars
949
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] );
952
953     # output the default table for unused lead bytes
954
955     if ($unused)
956     {
957         printf OUTPUT "    /* unused lead bytes */\n";
958         printf OUTPUT "%s,\n", DUMP_ARRAY( "0x%04x", 0, ($defw) x 256 );
959     }
960
961     # output the ascii->unicode table for each DBCS lead byte
962
963     for (my $y = 0; $y <= $#lblist; $y++)
964     {
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";
969     }
970
971     # output the lead byte subtables offsets
972
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)
977     {
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]]++; }
980     }
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 );
983
984     # count the number of unicode->ascii subtables that contain something
985
986     my @filled = ();
987     my $subtables = 1;
988     for (my $i = 0; $i < 65536; $i++)
989     {
990         next unless defined $uni2cp[$i];
991         $filled[$i >> 8] = 1;
992         $subtables++;
993         $i |= 255;
994     }
995
996     # output all the subtables into a single array
997
998     printf OUTPUT "static const unsigned short uni2cp_low[%d] =\n{\n", $subtables*256;
999     for (my $y = 0; $y < 256; $y++)
1000     {
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] );
1004     }
1005     printf OUTPUT "    /* defaults */\n";
1006     printf OUTPUT "%s\n};\n\n", DUMP_ARRAY( "0x%04x", 0, ($def) x 256 );
1007
1008     # output a table of the offsets of the subtables in the previous array
1009
1010     my $pos = 0;
1011     @offsets = ();
1012     for (my $y = 0; $y < 256; $y++)
1013     {
1014         if ($filled[$y]) { push @offsets, $pos; $pos += 256; }
1015         else { push @offsets, ($subtables-1) * 256; }
1016     }
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 );
1019
1020     # output the code page descriptor
1021
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";
1031 }
1032
1033
1034 ################################################################
1035 # get the list of defined lead byte ranges
1036 sub get_lb_ranges()
1037 {
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++)
1044     {
1045         if ($on)
1046         {
1047             if (!defined $list[$i]) { push @ranges, $i-1; $on = 0; }
1048         }
1049         else
1050         {
1051             if ($list[$i]) { push @ranges, $i; $on = 1; }
1052         }
1053     }
1054     if ($on) { push @ranges, 0xff; }
1055     return @ranges;
1056 }
1057
1058 ################################################################
1059 # dump the Indic Syllabic Category table
1060 sub dump_indic($)
1061 {
1062     my $filename = shift;
1063     my @indic_table = ($indic_types{'Other'}) x 65536;;
1064
1065     my $INPUT = open_data_file "$UNIDATA/IndicSyllabicCategory.txt";
1066     while (<$INPUT>)
1067     {
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*#/)
1072         {
1073             my $type = $2;
1074             die "unknown indic $type" unless defined $indic_types{$type};
1075             if (hex $1 < 65536)
1076             {
1077                 $indic_table[hex $1] = $indic_types{$type};
1078             }
1079             next;
1080         }
1081         elsif (/^\s*([0-9a-fA-F]+)..\s*([0-9a-fA-F]+)\s*;\s*([A-Za-z_]+)\s*#/)
1082         {
1083             my $type = $3;
1084             die "unknown indic $type" unless defined $indic_types{$type};
1085             if (hex $1 < 65536 and hex $2 < 6536)
1086             {
1087                 foreach my $i (hex $1 .. hex $2)
1088                 {
1089                     $indic_table[$i] = $indic_types{$type};
1090                 }
1091             }
1092             next;
1093         }
1094         die "malformed line $_";
1095     }
1096     close $INPUT;
1097
1098     $INPUT = open_data_file "$UNIDATA/IndicMatraCategory.txt";
1099     while (<$INPUT>)
1100     {
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*#/)
1105         {
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;
1110         }
1111         elsif (/^\s*([0-9a-fA-F]+)..\s*([0-9a-fA-F]+)\s*;\s*([A-Za-z_]+)\s*#/)
1112         {
1113             my $type = $3;
1114             die "unknown matra $type" unless defined $matra_types{$type};
1115             foreach my $i (hex $1 .. hex $2)
1116             {
1117                 $indic_table[$i] += $matra_types{$type} << 8;
1118             }
1119             next;
1120         }
1121         die "malformed line $_";
1122     }
1123     close $INPUT;
1124
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";
1132
1133     dump_two_level_mapping( "indic_syllabic_table", @indic_table);
1134
1135     close OUTPUT;
1136     save_file($filename);
1137 }
1138
1139 ################################################################
1140 # dump the Line Break Properties table
1141 sub dump_linebreak($)
1142 {
1143     my $filename = shift;
1144     my @break_table = ($break_types{'XX'}) x 65536;;
1145     my $next_group = 0;
1146
1147     my $INPUT = open_data_file "$UNIDATA/LineBreak.txt";
1148     while (<$INPUT>)
1149     {
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*/)
1154         {
1155             my $type = $2;
1156             die "unknown breaktype $type" unless defined $break_types{$type};
1157             $break_table[hex $1] = $break_types{$type};
1158             next;
1159         }
1160         elsif (/^\s*([0-9a-fA-F]+)..\s*([0-9a-fA-F]+)\s*;\s*([0-9A-Z][0-9A-Z])+\s*/)
1161         {
1162             my $type = $3;
1163             die "unknown breaktype $type" unless defined $break_types{$type};
1164             foreach my $i (hex $1 .. hex $2)
1165             {
1166                 $break_table[$i] = $break_types{$type};
1167             }
1168             next;
1169         }
1170         die "malformed line $_";
1171     }
1172     close $INPUT;
1173
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";
1180
1181     dump_two_level_mapping( "wine_linebreak_table", @break_table);
1182
1183     close OUTPUT;
1184     save_file($filename);
1185 }
1186
1187
1188 ################################################################
1189 # dump the BiDi mirroring table
1190 sub dump_mirroring($)
1191 {
1192     my $filename = shift;
1193     my @mirror_table = ();
1194
1195     my $INPUT = open_data_file "$UNIDATA/BidiMirroring.txt";
1196     while (<$INPUT>)
1197     {
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]+)/)
1202         {
1203             $mirror_table[hex $1] = hex $2;
1204             next;
1205         }
1206         die "malformed line $_";
1207     }
1208     close $INPUT;
1209
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);
1219 }
1220
1221
1222 ################################################################
1223 # dump the Arabic shaping table
1224 sub dump_shaping($)
1225 {
1226     my $filename = shift;
1227     my %groups;
1228     my $next_group = 0;
1229
1230     $groups{"No_Joining_Group"} = $next_group++;
1231
1232     my $INPUT = open_data_file "$UNIDATA/ArabicShaping.txt";
1233     while (<$INPUT>)
1234     {
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+)/)
1239         {
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;
1245         }
1246         die "malformed line $_";
1247     }
1248     close $INPUT;
1249
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";
1256
1257     dump_two_level_mapping( "wine_shaping_table", @joining_table );
1258
1259     print OUTPUT "\nconst unsigned short wine_shaping_forms[256][4] =\n{\n";
1260     for (my $i = 0x600; $i <= 0x6ff; $i++)
1261     {
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;
1267     }
1268     print OUTPUT "};\n";
1269
1270     close OUTPUT;
1271     save_file($filename);
1272 }
1273
1274
1275 ################################################################
1276 # dump the case mapping tables
1277 sub DUMP_CASE_MAPPINGS($)
1278 {
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";
1285
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);
1292 }
1293
1294
1295 ################################################################
1296 # dump a case mapping table
1297 sub DUMP_CASE_TABLE($@)
1298 {
1299     my ($name,@table) = @_;
1300
1301     # count the number of sub tables that contain something
1302     # also compute the low and upper populated bounds
1303
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++)
1309     {
1310         next unless defined $table[$i];
1311         if (!defined $filled[$i >> 8])
1312         {
1313           $lowerbounds[$index] = $i & 0xff;
1314           $upperbounds[$index] = 0xff - $lowerbounds[$index];
1315           $filled[$i >> 8] = $index * 256 + 512;
1316           $index++;
1317         }
1318         else
1319         {
1320           $upperbounds[$index-1] = 0xff - ($i & 0xff);
1321         }
1322         $table[$i] = ($table[$i] - $i) & 0xffff;
1323     }
1324
1325     # Collapse blocks upwards if possible
1326     my $removed = 0;
1327     $index = 0;
1328     for (my $i = 0; $i < 256; $i++)
1329     {
1330         next unless defined $filled[$i];
1331         if ($upperbounds[$index - 1] > $lowerbounds[$index])
1332         {
1333            $removed = $removed + $lowerbounds[$index];
1334         }
1335         else
1336         {
1337            $removed = $removed + $upperbounds[$index - 1];
1338            $lowerbounds[$index] = $upperbounds[$index - 1];
1339         }
1340         $filled[$i] = $filled[$i] - $removed;
1341         $index++;
1342     }
1343
1344     # dump the table
1345
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++)
1353     {
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++;
1359     }
1360     printf OUTPUT "\n};\n";
1361 }
1362
1363 ################################################################
1364 # compress a mapping table by removing identical rows
1365 sub compress_array($@)
1366 {
1367     my $rows = shift;
1368     my @table = @_;
1369     my $len = @table / $rows;
1370     my @array = (0) x $rows;
1371     my %sequences;
1372
1373     # try to merge table rows
1374     for (my $row = 0; $row < $rows; $row++)
1375     {
1376         my $rowtxt = pack "S*", @table[($row * $len)..($row * $len + $len - 1)];
1377         if (defined($sequences{$rowtxt}))
1378         {
1379             # reuse an existing row
1380             $array[$row] = $sequences{$rowtxt};
1381         }
1382         else
1383         {
1384             # create a new row
1385             $sequences{$rowtxt} = $array[$row] = $#array + 1;
1386             push @array, @table[$row * $len..$row * $len + $len - 1];
1387         }
1388     }
1389     return @array;
1390 }
1391
1392 ################################################################
1393 # dump a simple char -> 16-bit value mapping table
1394 sub dump_simple_mapping($@)
1395 {
1396     my $name = shift;
1397     my @array = compress_array( 256, @_[0..65535] );
1398
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] );
1402 }
1403
1404 ################################################################
1405 # dump a char -> 16-bit value mapping table using two-level tables
1406 sub dump_two_level_mapping($@)
1407 {
1408     my $name = shift;
1409     my @row_array = compress_array( 4096, @_[0..65535] );
1410     my @array = compress_array( 256, @row_array[0..4095] );
1411
1412     for (my $i = 256; $i < @array; $i++) { $array[$i] += @array - 4096; }
1413
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] );
1418 }
1419
1420 ################################################################
1421 # dump a binary case mapping table in l_intl.nls format
1422 sub dump_binary_case_table(@)
1423 {
1424     my (@table) = @_;
1425
1426     my %difftables_hash = ();
1427     my @difftables;
1428     my %offtables2_hash = ();
1429     my @offtables2 = ();
1430     
1431     my @offtable = ();
1432     for (my $i = 0; $i < 256; $i++)
1433     {
1434         my @offtable2 = ();
1435         for(my $j = 0; $j < 16; $j++) # offset table for xx00-xxFF characters
1436         {
1437             my @difftable;
1438             for (my $k = 0; $k < 16; $k++) # case map table for xxx0-xxxF characters
1439             {
1440                 my $char = ($i<<8) + ($j<<4) + $k;
1441                 $difftable[$k] = (defined $table[$char]) ? (($table[$char]-$char) & 0xffff) : 0;
1442             }
1443
1444             my $diff_key = pack "S*", @difftable;
1445             my $offset3 = $difftables_hash{$diff_key};
1446             if (!defined $offset3)
1447             {
1448                 $offset3 = scalar @difftables;
1449                 $difftables_hash{$diff_key} = $offset3;
1450                 push @difftables, @difftable;
1451             }
1452             $offtable2[$j] = $offset3;
1453         }
1454
1455         my $offtable2_key = pack "S*", @offtable2;
1456         my $offset2 = $offtables2_hash{$offtable2_key};
1457         if (!defined $offset2)
1458         {
1459             $offset2 = scalar @offtables2;
1460             $offtables2_hash{$offtable2_key} = $offset2;
1461             push @offtables2, \@offtable2;
1462         }
1463         $offtable[$i] = $offset2;
1464     }
1465
1466     my @output;
1467     my $offset = 0x100; # offset of first subtable in words
1468     foreach (@offtable)
1469     {
1470         push @output, 0x10 * $_ + $offset; # offset of subtable in words
1471     }
1472
1473     $offset = 0x100 + 0x10 * scalar @offtables2; # offset of first difftable in words
1474     foreach(@offtables2)
1475     {
1476         my $table = $_;
1477         foreach(@$table)
1478         {
1479             push @output, $_ + $offset; # offset of difftable in words
1480         }
1481     }
1482
1483     my $len = 1 + scalar @output + scalar @difftables;
1484     return pack "S<*", $len, @output, @difftables;
1485 }
1486
1487
1488 ################################################################
1489 # dump case mappings for l_intl.nls
1490 sub dump_intl_nls($)
1491 {
1492     my $filename = shift;
1493     open OUTPUT,">$filename.new" or die "Cannot create $filename";
1494     printf "Building $filename\n";
1495
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);
1502 }
1503
1504
1505 sub load_nameprep_range_table($$$)
1506 {
1507     my ($INPUT, $val, $table_ref) = @_;
1508
1509     while (<$INPUT>)
1510     {
1511         if (/^\s*([0-9a-fA-F]+)-([0-9a-fA-F]+)/)
1512         {
1513             my $last = hex $2;
1514             $last = 65535 if($last >= 65536);
1515             foreach my $i (hex $1 .. $last)
1516             {
1517                 $table_ref->[$i] |= $val;
1518             }
1519             next;
1520         }
1521         elsif (/^\s*([0-9a-fA-F]+)/)
1522         {
1523             if (hex $1 < 65536)
1524             {
1525                 $table_ref->[hex $1] |= $val;
1526             }
1527             next;
1528         }
1529
1530         return if (/End\sTable/);
1531     }
1532 }
1533
1534 sub load_nameprep_map_table($$)
1535 {
1536     my ($INPUT, $table_ref) = @_;
1537
1538     while (<$INPUT>)
1539     {
1540         if (/^\s*([0-9a-fA-F]+);\s;/)
1541         {
1542             # special value for map to nothing
1543             $table_ref->[hex $1] = [0xffff, 0xffff, 0xffff];
1544             next;
1545         }
1546         elsif (/^\s*([0-9a-fA-F]+);\s([0-9a-fA-F]+);/)
1547         {
1548             $table_ref->[hex $1] = [hex $2, 0, 0];
1549             next;
1550         }
1551         elsif (/^\s*([0-9a-fA-F]+);\s([0-9a-fA-F]+)\s([0-9a-fA-F]+);/)
1552         {
1553             $table_ref->[hex $1] = [hex $2, hex $3, 0];
1554             next;
1555         }
1556         elsif (/^\s*([0-9a-fA-F]+);\s([0-9a-fA-F]+)\s([0-9a-fA-F]+)\s([0-9a-fA-F]+);/)
1557         {
1558             $table_ref->[hex $1] = [hex $2, hex $3, hex $4];
1559             next;
1560         }
1561
1562         return if (/End\sTable/);
1563     }
1564 }
1565
1566 ################################################################
1567 # dump mapping table, prohibited characters set, unassigned
1568 # characters, bidirectional rules used by nameprep algorithm
1569 sub dump_nameprep($)
1570 {
1571     my $filename = shift;
1572     my @mapping_table = ();
1573     my @flags_table = (0) x 65536;
1574
1575     my $INPUT = open_data_file $STRINGPREP;
1576     while (<$INPUT>)
1577     {
1578         next unless /Start\sTable/;
1579
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/);
1592
1593         load_nameprep_map_table($INPUT, \@mapping_table) if (/B.1/);
1594         load_nameprep_map_table($INPUT, \@mapping_table) if (/B.2/);
1595     }
1596     close $INPUT;
1597
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";
1604
1605     dump_two_level_mapping( "nameprep_char_type", @flags_table );
1606
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++)
1612     {
1613         next unless defined $mapping_table[$i];
1614         $filled[$i >> 4] = $pos;
1615         $pos += 16*3;
1616         $i |= 15;
1617     }
1618     my $total = $pos;
1619
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++)
1624     {
1625         next unless $filled[$i];
1626         $filled_idx[$i >> 4] = $pos;
1627         $pos += 16;
1628         $i |= 15;
1629     }
1630     my $null_offset = $pos;
1631     $total += $pos;
1632
1633     # add the index offsets to the subsets positions
1634     for (my $i = 0; $i < 4096; $i++)
1635     {
1636         next unless $filled[$i];
1637         $filled[$i] += $null_offset;
1638     }
1639
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 );
1645
1646     # dump the second-level indexes
1647     for (my $i = 0; $i < 256; $i++)
1648     {
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 );
1654     }
1655
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 );
1659
1660     for (my $i = 0; $i < 4096; $i++)
1661     {
1662         next unless $filled[$i];
1663         my @table = (0) x 48;
1664         for (my $j = 0; $j < 16; $j++)
1665         {
1666             if (defined $mapping_table[($i<<4) + $j])
1667             {
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];
1671             }
1672         }
1673         printf OUTPUT ",\n    /* 0x%03x0 .. 0x%03xf */\n", $i, $i;
1674         printf OUTPUT "%s", DUMP_ARRAY( "0x%04x", 0, @table );
1675     }
1676
1677     printf OUTPUT "\n};\n";
1678
1679     close OUTPUT;
1680     save_file($filename);
1681 }
1682
1683 ################################################################
1684 # dump the ctype tables
1685 sub DUMP_CTYPE_TABLES($)
1686 {
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";
1693
1694     # add the direction in the high 4 bits of the category
1695     for (my $i = 0; $i < 65536; $i++)
1696     {
1697         $category_table[$i] |= $direction_table[$i] << 12 if defined $direction_table[$i];
1698     }
1699
1700     dump_simple_mapping( "wine_wctype_table", @category_table );
1701
1702     close OUTPUT;
1703     save_file($filename);
1704 }
1705
1706
1707 ################################################################
1708 # dump the char composition tables
1709 sub DUMP_COMPOSE_TABLES($)
1710 {
1711     my $filename = shift;
1712
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";
1718
1719     ######### composition table
1720
1721     my @filled = ();
1722     foreach my $i (@compose_table)
1723     {
1724         my @comp = @$i;
1725         push @{$filled[$comp[1]]}, [ $comp[0], $comp[2] ];
1726     }
1727
1728     # count how many different second chars we have
1729
1730     my $count = 0;
1731     for (my $i = 0; $i < 65536; $i++)
1732     {
1733         next unless defined $filled[$i];
1734         $count++;
1735     }
1736
1737     # build the table of second chars and offsets
1738
1739     my $pos = $count + 1;
1740     my @table = ();
1741     for (my $i = 0; $i < 65536; $i++)
1742     {
1743         next unless defined $filled[$i];
1744         push @table, $i, $pos;
1745         $pos += @{$filled[$i]};
1746     }
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 );
1751
1752     # build the table of first chars and mappings
1753
1754     for (my $i = 0; $i < 65536; $i++)
1755     {
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++)
1760         {
1761             push @table, $list[$j][0], $list[$j][1];
1762         }
1763         printf OUTPUT ",\n    /* 0x%04x */\n%s", $i, DUMP_ARRAY( "0x%04x", 0, @table );
1764     }
1765     printf OUTPUT "\n};\n\nconst unsigned int unicode_compose_table_size = %d;\n\n", $count;
1766
1767     ######### decomposition table
1768
1769     # first determine all the 16-char subsets that contain something
1770
1771     @filled = (0) x 4096;
1772     $pos = 16*2;  # for the null subset
1773     for (my $i = 0; $i < 65536; $i++)
1774     {
1775         next unless defined $decomp_table[$i];
1776         $filled[$i >> 4] = $pos;
1777         $pos += 16*2;
1778         $i |= 15;
1779     }
1780     my $total = $pos;
1781
1782     # now count the 256-char subsets that contain something
1783
1784     my @filled_idx = (256) x 256;
1785     $pos = 256 + 16;
1786     for (my $i = 0; $i < 4096; $i++)
1787     {
1788         next unless $filled[$i];
1789         $filled_idx[$i >> 4] = $pos;
1790         $pos += 16;
1791         $i |= 15;
1792     }
1793     my $null_offset = $pos;  # null mapping
1794     $total += $pos;
1795
1796     # add the index offsets to the subsets positions
1797
1798     for (my $i = 0; $i < 4096; $i++)
1799     {
1800         next unless $filled[$i];
1801         $filled[$i] += $null_offset;
1802     }
1803
1804     # dump the main index
1805
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 );
1810
1811     # dump the second-level indexes
1812
1813     for (my $i = 0; $i < 256; $i++)
1814     {
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 );
1820     }
1821
1822     # dump the 16-char subsets
1823
1824     printf OUTPUT ",\n    /* null mapping */\n";
1825     printf OUTPUT "%s", DUMP_ARRAY( "0x%04x", 0, (0) x 32 );
1826
1827     for (my $i = 0; $i < 4096; $i++)
1828     {
1829         next unless $filled[$i];
1830         my @table = (0) x 32;
1831         for (my $j = 0; $j < 16; $j++)
1832         {
1833             if (defined $decomp_table[($i<<4) + $j])
1834             {
1835                 $table[2 * $j] = ${$decomp_table[($i << 4) + $j]}[0];
1836                 $table[2 * $j + 1] = ${$decomp_table[($i << 4) + $j]}[1];
1837             }
1838         }
1839         printf OUTPUT ",\n    /* 0x%03x0 .. 0x%03xf */\n", $i, $i;
1840         printf OUTPUT "%s", DUMP_ARRAY( "0x%04x", 0, @table );
1841     }
1842
1843     printf OUTPUT "\n};\n";
1844     close OUTPUT;
1845     save_file($filename);
1846 }
1847
1848
1849 ################################################################
1850 # handle a "bestfit" Windows mapping file
1851
1852 sub handle_bestfit_file($$$)
1853 {
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 = ();
1859
1860     my $INPUT = open_data_file "$MAPPINGS/$filename" or die "Cannot open $filename";
1861
1862     while (<$INPUT>)
1863     {
1864         next if /^;/;  # skip comments
1865         next if /^\s*$/;  # skip empty lines
1866         next if /\x1a/;  # skip ^Z
1867         last if /^ENDCODEPAGE/;
1868
1869         if (/^CODEPAGE\s+(\d+)/)
1870         {
1871             $codepage = $1;
1872             next;
1873         }
1874         if (/^CPINFO\s+(\d+)\s+0x([0-9a-fA-f]+)\s+0x([0-9a-fA-F]+)/)
1875         {
1876             $width = $1;
1877             $def = hex $2;
1878             $defw = hex $3;
1879             next;
1880         }
1881         if (/^(MBTABLE|WCTABLE|DBCSRANGE|DBCSTABLE)\s+(\d+)/)
1882         {
1883             $state = $1;
1884             $count = $2;
1885             next;
1886         }
1887         if (/^0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)/)
1888         {
1889             if ($state eq "MBTABLE")
1890             {
1891                 my $cp = hex $1;
1892                 my $uni = hex $2;
1893                 $cp2uni[$cp] = $uni unless defined($cp2uni[$cp]);
1894                 next;
1895             }
1896             if ($state eq "WCTABLE")
1897             {
1898                 my $uni = hex $1;
1899                 my $cp = hex $2;
1900                 $uni2cp[$uni] = $cp unless defined($uni2cp[$uni]);
1901                 next;
1902             }
1903             if ($state eq "DBCSRANGE")
1904             {
1905                 my $start = hex $1;
1906                 my $end = hex $2;
1907                 push @lb_ranges, $start, $end;
1908                 for (my $i = $start; $i <= $end; $i++)
1909                 {
1910                     push @lead_bytes, $i;
1911                     $cp2uni[$i] = 0;
1912                 }
1913                 $lb_cur = $start;
1914                 $lb_end = $end;
1915                 next;
1916             }
1917             if ($state eq "DBCSTABLE")
1918             {
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)
1924                 {
1925                     if (++$lb_cur > $lb_end) { $state = "DBCSRANGE"; }
1926                 }
1927                 next;
1928             }
1929         }
1930         die "$filename: Unrecognized line $_\n";
1931     }
1932     close $INPUT;
1933
1934     my $output = sprintf "libs/wine/c_%03d.c", $codepage;
1935     open OUTPUT,">$output.new" or die "Cannot create $output";
1936
1937     printf "Building %s from %s (%s)\n", $output, $filename, $comment;
1938
1939     # dump all tables
1940
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";
1945
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);
1950 }
1951
1952
1953 ################################################################
1954 # read an input file and generate the corresponding .c file
1955 sub HANDLE_FILE(@)
1956 {
1957     my ($codepage,$filename,$has_glyphs,$comment) = @_;
1958
1959     @cp2uni = ();
1960     @lead_bytes = ();
1961     @uni2cp = ();
1962
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/)
1967     {
1968         handle_bestfit_file( $filename, $has_glyphs, $comment );
1969         return;
1970     }
1971     else { READ_FILE "$MAPPINGS/$filename"; }
1972
1973     ADD_DEFAULT_MAPPINGS();
1974
1975     my $output = sprintf "libs/wine/c_%03d.c", $codepage;
1976     open OUTPUT,">$output.new" or die "Cannot create $output";
1977
1978     printf "Building %s from %s (%s)\n", $output, $filename || "hardcoded data", $comment;
1979
1980     # dump all tables
1981
1982     printf OUTPUT "/* code page %03d (%s) */\n", $codepage, $comment;
1983     if ($filename)
1984     {
1985         print OUTPUT "/* generated from $MAPPINGS/$filename */\n";
1986         print OUTPUT "/* DO NOT EDIT!! */\n\n";
1987     }
1988     else
1989     {
1990         printf OUTPUT "/* Automatically generated; DO NOT EDIT!! */\n\n";
1991     }
1992     printf OUTPUT "#include \"wine/unicode.h\"\n\n";
1993
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);
1998 }
1999
2000
2001 ################################################################
2002 # save a file if modified
2003 sub save_file($)
2004 {
2005     my $file = shift;
2006     if (-f $file && !system "cmp $file $file.new >/dev/null")
2007     {
2008         unlink "$file.new";
2009     }
2010     else
2011     {
2012         rename "$file.new", "$file";
2013     }
2014 }
2015
2016
2017 ################################################################
2018 # output the list of codepage tables into the cptable.c file
2019 sub output_cptable($)
2020 {
2021     my $output = shift;
2022     my @tables_decl = ();
2023
2024     printf "Building %s\n", $output;
2025
2026     foreach my $file (@allfiles)
2027     {
2028         my ($codepage,$filename,$comment) = @$file;
2029         push @tables_decl, sprintf("extern union cptable cptable_%03d;\n",$codepage);
2030     }
2031
2032     push @tables_decl, sprintf("\nstatic const union cptable * const cptables[%d] =\n{\n",$#allfiles+1);
2033     foreach my $file (@allfiles)
2034     {
2035         my ($codepage,$filename,$comment) = @$file;
2036         push @tables_decl, sprintf("    &cptable_%03d,\n", $codepage);
2037     }
2038     push @tables_decl, "};";
2039     REPLACE_IN_FILE( $output, @tables_decl );
2040 }
2041
2042 ################################################################
2043 # replace the contents of a file between ### cpmap ### marks
2044
2045 sub REPLACE_IN_FILE($@)
2046 {
2047     my $name = shift;
2048     my @data = @_;
2049     my @lines = ();
2050     open(FILE,$name) or die "Can't open $name";
2051     while (<FILE>)
2052     {
2053         push @lines, $_;
2054         last if /\#\#\# cpmap begin \#\#\#/;
2055     }
2056     push @lines, @data;
2057     while (<FILE>)
2058     {
2059         if (/\#\#\# cpmap end \#\#\#/) { push @lines, "\n", $_; last; }
2060     }
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);
2066 }
2067
2068 ################################################################
2069 # main routine
2070
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" );
2083
2084 foreach my $file (@allfiles) { HANDLE_FILE( @{$file} ); }
2085
2086 output_cptable("libs/wine/cptable.c");
2087
2088 exit 0;
2089
2090 # Local Variables:
2091 # compile-command: "./make_unicode"
2092 # End: