1#!/usr/bin/perl -w
2#
3#  Copyright (c) 2003-2004, Artem B. Bityuckiy, SoftMine Corporation.
4#
5#  Redistribution and use in source and binary forms, with or without
6#  modification, are permitted provided that the following conditions
7#  are met:
8#  1. Redistributions of source code must retain the above copyright
9#     notice, this list of conditions and the following disclaimer.
10#  2. Redistributions in binary form must reproduce the above copyright
11#     notice, this list of conditions and the following disclaimer in the
12#     documentation and/or other materials provided with the distribution.
13#
14#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15#  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17#  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20#  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21#  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22#  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23#  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24#  SUCH DAMAGE.
25#
26use integer;
27use Getopt::Std;
28use IO::Seekable;
29use strict;
30
31
32# ##############################################################################
33#
34# FUNCTION PROTOTYPES AND GLOBAL DATA DECLARATION SECTION
35#
36# ##############################################################################
37
38
39# SUPPLEMENTARY FUNCTIONS FORWARD DECLARATIONS
40sub ProcessOptions();
41sub Err($;$);
42sub Generate8bitToUCS();
43sub GenerateSpeed($);
44sub Generate16bitSize($);
45sub Output8bitToUCS(;$);
46sub Output8bitFromUCS(;$);
47sub OutputSpeed($;$);
48sub OutputSize($;$);
49
50# VARIABLES RELATING TO COMMAND-LINE OPTIONS
51my $Verbose;  # Be verbose if true
52my $Source;   # Output C source code instead of binary .cct file if true
53my $Plane;    # Use this plane if defined
54my $InFile;   # Use this file for input
55my $OutFile;  # Use this file for output
56my $CCSName;  # Use this CCS name
57my $NoSpeed;  # Don't generate speed-optimized tables (binary files only)
58my $NoSize;   # Don't generate size-optimized tables (binary files only)
59my $NoBE;     # Don't generate big-endian tables (binary files only)
60my $NoLE;     # Don't generate big-endian tables (binary files only)
61my $NoTo;     # Don't generate "to_ucs" table (binary files only)
62my $NoFrom;   # Don't generate "from_ucs" table (binary files only)
63my $CCSCol;   # CCS column number in source file
64my $UCSCol;   # UCS column number in source file
65
66
67# DATA STRUCTURES WITH "TO_UCS" AND "FROM_UCS" SPEED/SIZE -OPTIMIZED TABLES
68my (@FromSpeedTbl, @ToSpeedTbl, @FromSizeTbl, @ToSizeTbl);
69# "TO_UCS" AND "FROM_UCS" SPEED/SIZE -OPTIMIZED TABLES SIZE IN BYTES
70my ($FromSpeedBytes, $ToSpeedBytes, $FromSizeBytes, $ToSizeBytes) =
71   (0, 0, 0, 0);
72
73my (%CCSUCS, %UCSCCS); # CCS->UCS and UCS->CCS mappings
74my $Bits = 8;          # Table bits (8 or 16);
75
76# SPECIAL MARKER CODES
77my $InvCode  = 0xFFFF; # FFFF indicates 18 bit invalid codes
78my $InvBlock = 0xFFFF; # FFFF also mark empty blocks in speed-optimized tables
79my $LostCode = 0x3F;   # ASCII '?' marks codes lost during CCS->UCS mapping
80# To mark invalid codes in 8bit encodings 0xFF is used CCS's 0xFF mapping is saved
81# separately. $FFMap variable stores real 0xFF mapping if defined.
82my $InvCode8bit = 0xFF;
83my $FFMap;
84
85# 8 Bit "From UCS" table header size (bytes)
86my $Hdr8bitFromUCS = 2;
87# Binary table header size (bytes)
88my $HdrBinary = 8;
89
90# At first all lost CCS codes are marked by $TmpLost to distinguish between
91# code which is equivalent to $LostCode and lost codes. This is done in order to
92# output $MacroLostCode instead of $LostCode in source file.
93my $TmpLost = 0x1FFFF;
94
95# VARIABLES RELATING TO C SOURCE CODE
96my $MacroInvCode      = 'INVALC';
97my $MacroInvBlock     = 'INVBLK';
98my $MacroLostCode     = 'LOST_C';
99my $MacroCCSName      = 'ICONV_CCS_%s';
100my $GuardSize       = 'defined (TABLE_USE_SIZE_OPTIMIZATION)';
101my $GuardToUCS      = "ICONV_TO_UCS_CCS_%s";
102my $GuardFromUCS    = "ICONV_FROM_UCS_CCS_%s";
103my $MacroSpeedTable = 'TABLE_SPEED_OPTIMIZED';
104my $MacroSizeTable  = 'TABLE_SIZE_OPTIMIZED';
105my $Macro8bitTable  = 'TABLE_8BIT';
106my $Macro16bitTable = 'TABLE_16BIT';
107my $MacroVer1Table  = 'TABLE_VERSION_1';
108my $TypeBICCS       = 'iconv_ccs_t';
109my $VarToUCSSize    = "to_ucs_size_%s";
110my $VarToUCSSpeed   = "to_ucs_speed_%s";
111my $VarFromUCSSize  = "from_ucs_size_%s";
112my $VarFromUCSSpeed = "from_ucs_speed_%s";
113my $VarBICCS             = "_iconv_ccs_%s";
114# Text block that visually separates tables.
115my $Separator = '=' x 70;
116
117# ##############################################################################
118#
119# SCRIPT ENTRY POINT
120#
121# ##############################################################################
122
123
124# Parse command-line options, check them and set correspondent global variables
125ProcessOptions();
126
127# Initialize global variables tat depend on CCS name.
128$_ = sprintf $_, $CCSName foreach +($VarToUCSSpeed,
129                                    $VarToUCSSize,
130                                    $VarToUCSSpeed,
131                                    $VarFromUCSSpeed,
132                                    $VarFromUCSSize,
133                                    $VarBICCS);
134$_ = sprintf $_, "\U$CCSName" foreach +($GuardToUCS,
135                                        $GuardFromUCS,
136                                        $MacroCCSName);
137
138# Open input and output files
139Err "Can't open \"$InFile\" file for reading: $!.\n", 1
140unless open(INFILE, '<', $InFile);
141Err "Can't open \"$OutFile\" file for writing: $!.\n", 1
142unless open(OUTFILE, '>', $OutFile);
143
144# ==============================================================================
145# EXTRACT CODES MAP FROM INPUT FILE
146# ==============================================================================
147
148for (my $ln = 1; my $l = <INFILE>; $ln += 1)
149{
150  # Skip comment and empty lines, remove ending CR symbol
151  next if $l =~ /^#.*$/ or $l =~ /^\s*$/;
152  $l =~ s/^(.*)\n$/$1/, $l =~ s/^(.*)\r$/$1/;
153
154  # Remove comment and extra spaces
155  $l =~ s/(.*)\s*#.*/$1/;
156  $l =~ s/\s+/ /g;
157  $l =~ s/(.*)\s*$/$1/;
158
159  # Split line into individual codes
160  my @codes = split / /, $l;
161
162  # Skip line if there is no needed columns
163  unless (defined $codes[$CCSCol])
164  {
165    print("Warning (line $ln): no CCS column, skip.\n") if $Verbose;
166    next;
167  }
168  unless (defined $codes[$UCSCol])
169  {
170    print("Warning (line $ln): no UCS column, skip.\n") if $Verbose;
171    next;
172  }
173
174  # Extract codes strings from needed columns
175  my ($ccs, $ucs) = ($codes[$CCSCol], $codes[$UCSCol]);
176  my $patt = qr/(0[xX])?[0-9a-fA-F]{1,8}/; # HEX digit regexp pattern.
177
178  # Check that CCS and UCS code strings has right format.
179  unless ($ccs =~ m/^$patt$/)
180  {
181    print("Warning (line $ln): $ccs CCS code isn't recognized, skip.\n")
182    if $Verbose;
183    next;
184  }
185  unless ($ucs =~ m/^($patt(,|\+))*$patt$/)
186  {
187    print("Warning (line $ln): $ucs UCS code isn't recognized, skip.\n")
188    if $Verbose;
189    next;
190  }
191
192  # Convert code to numeric format (assume hex).
193  $ccs = hex $ccs;
194
195  if ($ucs =~ m/,/ or $ucs =~ m/\+/)
196  {
197    # Mark CCS codes with "one to many" mappings as lost
198    printf "Warning (line $ln): only one to one mapping is supported, "
199         . "mark 0x%.4X CCS code as lost.\n", hex $ccs if $Verbose;
200    $ucs = $TmpLost;
201  }
202  else
203  {
204    # Convert code to numeric format
205    $ucs = hex $ucs;
206
207    # Check that UCS code isn't longer than 16 bits.
208    if ($ucs > 0xFFFF)
209    {
210      printf("Warning (line $ln): UCS code should fit 16 bits, "
211           . "mark 0x%.4X CCS code as lost.\n", hex $ccs) if $Verbose;
212      $ucs = $TmpLost;
213    }
214  }
215
216  # If CCS value > 0xFFFF user should specify plane number.
217  if ($ccs > 0xFFFF && !defined $Plane)
218  {
219    print("Warning (line $ln): $ccs is > 16 bit, plane number should be specified,"
220        . " skip this mapping.\n") if $Verbose;
221    next;
222  }
223
224  if (defined $Plane)
225  {
226    next if (($ccs & 0xFFFF0000) >> 16) != hex $Plane; # Skip alien plane.
227    $ccs &= 0xFFFF;
228  }
229
230  # Check that reserved codes aren't used.
231  if ($ccs == $InvCode or $ucs == $InvCode)
232  {
233    print("Warning (line $ln): $InvCode is reserved to mark invalid codes and "
234       . "shouldn't be used in mappings, skip.\n") if $Verbose;
235    next;
236  }
237
238  # Save mapping in UCSCCS and CCSUCS hash arrays.
239  $UCSCCS{$ucs} = $ccs if $ucs != $TmpLost && !defined $UCSCCS{$ucs};
240  $CCSUCS{$ccs} = $ucs if !defined $CCSUCS{$ccs};
241
242  $Bits = 16 if $ccs > 0xFF;
243}
244
245if (not %CCSUCS)
246{
247  Err "Error: there is no plane $Plane in \"$0\".\n" if defined $Plane;
248  Err "Error: mapping wasn't found.\n";
249}
250
251
252# ==============================================================================
253# GENERATE TABLE DATA
254# ==============================================================================
255
256if ($Bits == 8)
257{
258  $FFMap = $CCSUCS{0xFF};
259  $FFMap = $InvCode if !defined $FFMap;
260}
261
262if ($Bits == 8)
263{
264  Generate8bitToUCS() unless $NoTo;
265}
266else
267{
268  GenerateSpeed("to_ucs") unless $NoTo || $NoSpeed;
269  Generate16bitSize("to_ucs")  unless $NoTo || $NoSize;
270}
271
272GenerateSpeed("from_ucs") unless $NoFrom || $NoSpeed;
273Generate16bitSize("from_ucs")  unless $NoFrom || $NoSize;
274
275# ==============================================================================
276# OUTPUT ARRAYS
277# ==============================================================================
278
279if ($Source)
280{
281  # OUTPUT SOURCE
282  print OUTFILE
283"/*
284 * This file was generated automatically - don't edit it.
285 * File contains iconv CCS tables for $CCSName encoding.
286 */
287
288#include \"ccsbi.h\"
289
290#if defined ($GuardToUCS) \\
291 || defined ($GuardFromUCS)
292
293#include <_ansi.h>
294#include <sys/types.h>
295#include <sys/param.h>
296#include \"ccs.h\"
297#include \"ccsnames.h\"
298
299";
300
301  if ($Bits == 8)
302  {
303    print OUTFILE
304"#if (_BYTE_ORDER == _LITTLE_ENDIAN)
305#  define W(word) (word) & 0xFF, (word) >> 8
306#elif (_BYTE_ORDER == _BIG_ENDIAN)
307#  define W(word) (word) >> 8, (word) & 0xFF
308#else
309#  error \"Unknown byte order.\"
310#endif
311
312";
313  }
314
315  unless ($NoTo)
316  {
317    if ($Bits == 8)
318    {
319      Output8bitToUCS();
320    }
321    else
322    {
323      OutputSpeed("to_ucs") unless $NoSpeed;
324      OutputSize("to_ucs")  unless $NoSize;
325    }
326  }
327  unless ($NoFrom)
328  {
329    if ($Bits == 8)
330    {
331      Output8bitFromUCS();
332    }
333    else
334    {
335      OutputSpeed("from_ucs") unless $NoSpeed;
336      OutputSize("from_ucs")  unless $NoSize;
337    }
338  }
339
340  # OUTPUT TABLE DESCRIPTION STRUCTURE
341  print OUTFILE
342"/*
343 * $CCSName CCS description table.
344 * $Separator
345 */
346const $TypeBICCS
347$VarBICCS =
348{
349\t$MacroVer1Table, /* Table version */
350\t$MacroCCSName, /* CCS name */
351";
352  if ($Bits == 8)
353  {
354    print OUTFILE
355"\t$Macro8bitTable, /* Table bits */
356\t0, /* Not Used */
357#if defined ($GuardFromUCS)
358\t(__uint16_t *)&$VarFromUCSSpeed, /* UCS -> $CCSName table */
359#else
360\t(__uint16_t *)NULL,
361#endif
362\t0, /* Not Used */
363#if defined ($GuardToUCS)
364\t(__uint16_t *)&$VarToUCSSpeed /* $CCSName -> UCS table */
365#else
366\t(__uint16_t *)NULL,
367#endif
368};\n";
369  }
370  else
371  {
372    print OUTFILE
373"\t$Macro16bitTable, /* Table bits */
374#if defined ($GuardFromUCS) \\
375 && ($GuardSize)
376\t$MacroSizeTable,
377\t(__uint16_t *)&$VarFromUCSSize, /* UCS -> $CCSName table size-optimized table */
378#elif defined ($GuardFromUCS) \\
379 && !($GuardSize)
380\t$MacroSpeedTable,
381\t(__uint16_t *)&$VarFromUCSSpeed, /* UCS -> $CCSName table speed-optimized table */
382#else
383\t$MacroSpeedTable,
384\t(__uint16_t *)NULL,
385#endif
386#if defined ($GuardToUCS) \\
387 && ($GuardSize)
388\t$MacroSizeTable,
389\t(__uint16_t *)&$VarToUCSSize /* $CCSName -> UCS table speed-optimized table */
390#elif defined ($GuardToUCS) \\
391 && !($GuardSize)
392\t$MacroSpeedTable,
393\t(__uint16_t *)&$VarToUCSSpeed /* $CCSName -> UCS table speed-optimized table */
394#else
395\t$MacroSpeedTable,
396\t(__uint16_t *)NULL,
397#endif
398};\n";
399  }
400  print OUTFILE "\n#endif /* $GuardToUCS) || ... */\n\n";
401}
402else
403{
404  # OUTPUT BINARY TABLES DESCRIPTION STRUCTURE (ALWAYS BIG ENDIAN)
405  print OUTFILE pack "n", 1;
406  print OUTFILE pack "n", $Bits;
407  my $len = length $CCSName;
408  print OUTFILE pack "N", $len;
409  print OUTFILE pack "a$len", $CCSName;
410
411  my $pos = $HdrBinary + $len;
412  if ($pos & 3)
413  {
414    my $l = 4 - ($pos & 3);
415    print OUTFILE pack "a$l", 'XXX';
416    $pos += $l;
417  }
418
419  $pos += 16*4;
420
421  my @tables;
422  for (my $i = 0; $i < 16; $i++)
423  {
424    $tables[$i] = 0;
425  }
426
427  $tables[0] = $pos, $tables[1] = $FromSpeedBytes, $pos += $FromSpeedBytes
428  unless $NoFrom || $NoSpeed || $NoBE;
429  $tables[2] = $pos, $tables[3] = $FromSpeedBytes, $pos += $FromSpeedBytes
430  unless $NoFrom || $NoSpeed || $NoLE;
431  if ($Bits == 16)
432  {
433    $tables[4] = $pos, $tables[5] = $FromSizeBytes, $pos += $FromSizeBytes
434    unless $NoFrom || $NoSize || $NoBE;
435    $tables[6] = $pos, $tables[7] = $FromSizeBytes, $pos += $FromSizeBytes
436    unless $NoFrom || $NoSize || $NoLE;
437  }
438  $tables[8] = $pos, $tables[9] = $ToSpeedBytes, $pos += $ToSpeedBytes
439  unless $NoTo || $NoSpeed || $NoBE;
440  $tables[10] = $pos, $tables[11] = $ToSpeedBytes, $pos += $ToSpeedBytes
441  unless $NoTo || $NoSpeed || $NoLE;
442  if ($Bits == 16)
443  {
444    $tables[12] = $pos, $tables[13] = $ToSizeBytes, $pos += $ToSizeBytes
445    unless $NoTo || $NoSize || $NoBE;
446    $tables[14] = $pos, $tables[15] = $ToSizeBytes, $pos += $ToSizeBytes
447    unless $NoTo || $NoSize || $NoLE;
448  }
449
450  print OUTFILE pack("N", $_) foreach @tables;
451
452  print "Total bytes for output: $pos.\n" if $Verbose;
453
454  # OUTPUT BINARY TABLES
455  unless ($NoFrom)
456  {
457    if ($Bits == 8)
458    {
459      Output8bitFromUCS("n") unless $NoBE;
460      Output8bitFromUCS("v") unless $NoLE;
461    }
462    else
463    {
464      unless ($NoSpeed)
465      {
466        OutputSpeed("from_ucs", "n") unless $NoBE;
467        OutputSpeed("from_ucs", "v") unless $NoLE;
468      }
469      unless ($NoSize)
470      {
471        OutputSize("from_ucs", "n") unless $NoBE;
472        OutputSize("from_ucs", "v") unless $NoLE;
473      }
474    }
475  }
476  unless ($NoTo)
477  {
478    if ($Bits == 8)
479    {
480      Output8bitToUCS("n") unless $NoBE;
481      Output8bitToUCS("v") unless $NoLE;
482    }
483    else
484    {
485      unless ($NoSpeed)
486      {
487        OutputSpeed("to_ucs", "n") unless $NoBE;
488        OutputSpeed("to_ucs", "v") unless $NoLE;
489      }
490      unless ($NoSize)
491      {
492        OutputSize("to_ucs", "n") unless $NoBE;
493        OutputSize("to_ucs", "v") unless $NoLE;
494      }
495    }
496  }
497}
498
499close INFILE;
500close OUTFILE;
501exit 0;
502
503
504# ##############################################################################
505#
506# SUPPLEMENTARY FUNCTIONS
507#
508# ##############################################################################
509
510
511# =============================================================================
512#
513# Generate 8bit "to_ucs" table. Store table data in %ToSpeedTbl hash.
514# Store table size in $ToSpeedBytes scalar.
515#
516# =============================================================================
517sub Generate8bitToUCS()
518{
519  for (my $i = 0; $i <= 255; $i++)
520  {
521    $ToSpeedTbl[$i] = defined $CCSUCS{$i} ? $CCSUCS{$i} : $InvCode;
522  }
523  $ToSpeedBytes = 256*2;
524}
525
526
527# =============================================================================
528#
529# Generate speed-optimized table.
530#
531# Parameter 1:
532#    "to_ucs"   - generate "to_ucs" table, store table data in @ToSpeedTbl
533#                 array, store table size in $ToSpeedBytes scalar.
534#    "from_ucs" - generate "from_ucs" table, store table data in @FromSpeedTbl
535#                 array, store table size in $FromSpeedBytes scalar.
536#
537# Data is written to @ToSpeedTbl or @FromSpeedTbl (@map) table and has the
538# following format:
539# $table[0] - 256-element array (control block);
540# $table[1 .. $#table] - 256-element arrays (data blocks).
541#
542# =============================================================================
543sub GenerateSpeed($)
544{
545  my $map;
546  my $tbl;
547  my $bytes;
548
549  if ($_[0] eq "to_ucs")
550  {
551    $map = \%CCSUCS;
552    $tbl = \@ToSpeedTbl;
553    $bytes = \$ToSpeedBytes;
554  }
555  elsif ($_[0] eq "from_ucs")
556  {
557    $map = \%UCSCCS;
558    $tbl = \@FromSpeedTbl;
559    $bytes = \$FromSpeedBytes;
560  }
561  else
562  {
563    Err "Internal script error in GenerateSpeed()\n";
564  }
565
566  # Identify unused blocks
567  my @busy_blocks;
568  $busy_blocks[$_ >> 8] = 1 foreach (keys %$map);
569
570  # GENERATE FIRST 256-ELEMENT CONTROL BLOCK
571  for (my $i = 0,
572       my $idx = $Bits == 16 ? 0 : 256 + $Hdr8bitFromUCS;
573       $i <= 0xFF; $i++)
574  {
575    $tbl->[0]->[$i] = $busy_blocks[$i] ? $idx += 256 : undef;
576  }
577
578  # GENERATE DATA BLOCKS
579  $$bytes = 0;
580  for (my $i = 0; $i <= 0xFF; $i++)
581  {
582    next unless $busy_blocks[$i];
583    $$bytes += 256;
584    for (my $j = 0; $j <= 0xFF; $j++)
585    {
586      $tbl->[$i+1]->[$j] = $map->{($i << 8) | $j};
587    }
588  }
589  $$bytes *= 2 if $Bits == 16;
590  $$bytes += $Hdr8bitFromUCS if $Bits == 8;
591  $$bytes += 512;
592}
593
594
595# =============================================================================
596#
597# Generate 16bit size-optimized table.
598#
599# Parameter 1:
600#    "to_ucs"   - generate "to_ucs" table, store table data in @ToSizeTbl
601#                 array, store table size in $ToSizeBytes scalar.
602#    "from_ucs" - generate "from_ucs" table, store table data in @FromSizeTbl
603#                 array, store table size in $FromSizeBytes scalar.
604#
605# Data is written to @ToSizeTbl or @FromSizeTbl (@map) table and has the
606# following format:
607# $table[0] - number of ranges;
608# $table[1] - number of unranged codes;
609# $table[2] - unranged codes index in resulting array;
610# $table[3]->[0 .. $table[0]] - array of arrays of ranges:
611#     $table[3]->[x]->[0] - first code;
612#     $table[3]->[x]->[1] - last code;
613#     $table[3]->[x]->[2] - range index in resulting array;
614# $table[4]->[0 .. $table[0]] - array of arrays of ranges content;
615# $table[5]->[0 .. $table[1]] - array of arrays of unranged codes;
616#     $table[5]->[x]->[0] - CCS code;
617#     $table[5]->[x]->[0] - UCS code;
618#
619# =============================================================================
620sub Generate16bitSize($)
621{
622  my $map;
623  my $tbl;
624  my $bytes;
625
626  if ($_[0] eq "to_ucs")
627  {
628    $map = \%CCSUCS;
629    $tbl = \@ToSizeTbl;
630    $bytes = \$ToSizeBytes;
631  }
632  elsif ($_[0] eq "from_ucs")
633  {
634    $map = \%UCSCCS;
635    $tbl = \@FromSizeTbl;
636    $bytes = \$FromSizeBytes;
637  }
638  else
639  {
640    Err "Internal script error  Generate16bitSize()\n";
641  }
642
643  # CREATE LIST OF RANGES.
644  my @codes = sort {$a <=> $b} keys %$map;
645  my @ranges;  # Code ranges
646  my @range;   # Current working range
647  foreach (@codes)
648  {
649    if (not @range or $_ - 1 == $range[$#range])
650    {
651      push @range, $_;
652    }
653    else
654    {
655      my @tmp = @range;
656      push @ranges, \@tmp;
657      undef @range;
658      redo;
659    }
660  }
661  # Add Last range too
662  if (@range)
663  {
664    my @tmp = @range;
665    push @ranges, \@tmp;
666  }
667
668  # OPTIMIZE LIST OF RANGES.
669  my $r = 0; # Working range number
670  while (1)
671  {
672    last if ($r == $#ranges);
673
674    my @r1 = @{$ranges[$r]};
675    my @r2 = @{$ranges[$r + 1]};
676
677    # Calculate how many array entries two ranges need
678    my ($s1, $s2);
679
680    if ($#r1 == 0)
681    { $s1 = 2; }
682    elsif ($#r1 == 1)
683    { $s1 = 4; }
684    else
685    { $s1 = $#r1 + 1 + 3; }
686
687    if ($#r2 == 0)
688    { $s2 = 2; }
689    elsif ($#r2 == 1)
690    { $s2 = 4; }
691    else
692    { $s2 = $#r2 + 1 + 3; }
693
694    my $two = $s1 + $s2;
695
696    # Calculate how many array entries will be needed if we join them
697    my $one = $r2[$#r2] - $r1[0] + 1 + 3;
698
699    $r += 1, next if ($one > $two);
700
701    # Join ranges
702    my @r; # New range.
703    push @r, $_ foreach (@r1);
704    for (my $i = $r1[$#r1]+1; $i < $r2[0]; $i++)
705    {
706      push @r, undef;
707    }
708    push @r, $_ foreach (@r2);
709    $ranges[$r] = \@r;
710    splice @ranges, $r+1, 1;
711  }
712
713  # SEPARATE RANGED AND UNRANGED CODES. SPLIT 2-CODES RANGES ON 2 UNRANGED.
714  my @unranged;
715  foreach (@ranges)
716  {
717    if ($#$_ == 0)
718    {
719      push @unranged, $$_[0];
720      undef $_;
721    }
722    elsif ($#$_ == 1)
723    {
724      push @unranged, $$_[0];
725      push @unranged, $$_[1];
726      undef $_;
727    }
728  }
729
730  # DELETE UNUSED ELEMENTS
731  for (my $i = 0; $i <= $#ranges; $i++)
732  {
733    splice @ranges, $i--, 1 unless defined $ranges[$i];
734  }
735
736  # CALCULATE UNRANGED CODES ARRAY INDEX
737  my $idx = 3 + ($#ranges + 1)*3;
738  $idx += $#$_ + 1 foreach @ranges;
739
740  # COMPOSE TABLE
741  $tbl->[0] = $#ranges + 1;   # Number of ranges
742  $tbl->[1] = $#unranged + 1; # Number of unranged codes
743  $tbl->[2] = $idx;           # Array index of unranged codes
744
745  # Generate ranges list
746  $idx = 3 + ($#ranges + 1)*3; # First range data index
747  $$bytes = $idx*2;
748  my $num = 0;
749  foreach (@ranges)
750  {
751    $tbl->[3]->[$num]->[0] = $_->[0];
752    $tbl->[3]->[$num]->[1] = $_->[$#$_];
753    $tbl->[3]->[$num]->[2] = $idx;
754    $idx += $#$_ + 1;
755    $num += 1;
756  }
757
758  # Generate ranges content
759  $num = 0;
760  foreach (@ranges)
761  {
762    for (my $i = 0; $i <= $#$_; $i++)
763    {
764      $tbl->[4]->[$num]->[$i] = defined $_->[$i] ? $map->{$_->[$i]} : undef;
765    }
766    $num += 1;
767    $$bytes += ($#$_ + 1)*2;
768  }
769
770  # Generate unranged codes list
771  $num = 0;
772  foreach (@unranged)
773  {
774    $tbl->[5]->[$num]->[0] = $_;
775    $tbl->[5]->[$num]->[1] = $map->{$_};
776    $num += 1;
777  }
778
779  $$bytes += ($#unranged + 1)*4;
780}
781
782
783# =============================================================================
784#
785# Output 8bit "to UCS" table. Output table's source code if $Source
786# and table's binary data if !$Source.
787#
788# Parameter 1: Not used when sources are output. Output BE binary if 'n' and
789#              LE binary if 'v'.
790#
791# =============================================================================
792sub Output8bitToUCS(;$)
793{
794  my $endian = $_[0];
795  my $br = 0;
796
797  printf "Output%s 8-bit UCS -> $CCSName table ($ToSpeedBytes bytes).\n",
798         defined $endian ? ($endian eq 'n' ?
799                 " Big Endian" : " Little Endian") : "" if $Verbose;
800  if ($Source)
801  {
802    # Output heading information
803    printf OUTFILE
804"/*
805 * 8-bit $CCSName -> UCS table ($ToSpeedBytes bytes).
806 * $Separator
807 */
808#if defined ($GuardToUCS)
809
810static const __uint16_t
811${VarToUCSSpeed}\[] =
812{\n\t";
813  }
814
815  if ($Source)
816  {
817    foreach (@ToSpeedTbl)
818    {
819      $br += 1;
820      if ($_ != $InvCode)
821      {
822        if ($_ != $TmpLost)
823        {
824          printf OUTFILE "0x%.4X,", $_;
825        }
826        else
827        {
828          print OUTFILE "$MacroLostCode,";
829        }
830      }
831      else
832      {
833        print OUTFILE "$MacroInvCode,";
834      }
835      print(OUTFILE "\n\t"), $br = 0 unless $br % 8;
836    }
837    print OUTFILE "\n};\n\n#endif /* $GuardToUCS */\n\n";
838  }
839  else
840  {
841    foreach (@ToSpeedTbl)
842    {
843      print OUTFILE pack($endian, $_ == $TmpLost ? $LostCode : $_);
844    }
845  }
846}
847
848
849# =============================================================================
850#
851# Output 8bit "from UCS" table. Output table's source code if $Source
852# and table's binary data if !$Source.
853#
854# Parameter 1: Not used when sources are output. Output BE binary if 'n' and
855#              LE binary if 'v'.
856#
857# =============================================================================
858sub Output8bitFromUCS(;$)
859{
860  my $endian = $_[0];
861
862  printf "Output%s 8-bit $CCSName -> UCS table ($FromSpeedBytes bytes).\n",
863         defined $endian ? ($endian eq 'n' ?
864                 " Big Endian" : " Little Endian") : "" if $Verbose;
865  if ($Source)
866  {
867    print OUTFILE
868"/*
869 * 8-bit UCS -> $CCSName speed-optimized table ($FromSpeedBytes bytes).
870 * $Separator
871 */
872
873#if defined ($GuardFromUCS)
874
875static const unsigned char
876${VarFromUCSSpeed}\[] =
877{
878";
879  }
880
881  # SAVE 0xFF MAPPING.
882  if ($Source)
883  {
884    printf OUTFILE "\tW(0x%.4X), /* Real 0xFF mapping. 0xFF is used "
885                 . "to mark invalid codes */\n", $FFMap;
886  }
887  else
888  {
889    print OUTFILE pack($endian, $FFMap);
890  }
891
892  # OUTPUT HEADING BLOCK (ALWAYS 16 BIT)
893  if ($Source)
894  {
895    my $count = 0;
896    print OUTFILE "\t/* Heading Block */";
897    for (my $i = 0, my $br = 0; $i < 256; $br = ++$i % 4)
898    {
899      print OUTFILE "\n\t" unless $br;
900      if (defined $FromSpeedTbl[0]->[$i])
901      {
902        printf OUTFILE "W(0x%.4X),", $FromSpeedTbl[0]->[$i];
903      }
904      else
905      {
906        print OUTFILE "W($MacroInvBlock),";
907      }
908    }
909  }
910  else
911  {
912    print OUTFILE pack($endian, defined $_ ? $_ : $InvBlock)
913    foreach @{$FromSpeedTbl[0]};
914  }
915
916  if ($Source)
917  {
918    my $index = 512 + $Hdr8bitFromUCS;
919    for (my $blk = 1; $blk <= $#FromSpeedTbl; $blk++)
920    {
921      next unless defined $FromSpeedTbl[$blk];
922      printf OUTFILE "\n\t/* Block $blk, Array index 0x%.4X */", $index;
923      $index += 256;
924      for (my $i = 0, my $br = 0; $i < 256; $i++, $br = $i % 8)
925      {
926        print OUTFILE "\n\t" unless $br;
927        my $code = $FromSpeedTbl[$blk]->[$i];
928        if (!defined $code)
929        {
930          printf OUTFILE "0x%.2X,", $InvCode8bit;
931        }
932        else
933        {
934          printf OUTFILE "0x%.2X,", $code == $TmpLost ? $LostCode : $code;
935        }
936      }
937    }
938    print OUTFILE "\n};\n\n#endif /* $GuardFromUCS */\n\n";
939  }
940  else
941  {
942    for (my $blk = 1; $blk <= $#FromSpeedTbl; $blk++)
943    {
944      next unless defined $FromSpeedTbl[$blk];
945      for (my $i = 0, my $br = 0; $i < 256; $br = ++$i % 8)
946      {
947        my $code = $FromSpeedTbl[$blk]->[$i];
948        if (!defined $code)
949        {
950          printf OUTFILE pack 'C', $InvCode8bit;
951        }
952        else
953        {
954          print OUTFILE $code == $TmpLost ? pack('C', $LostCode)
955                                          : pack('C', $code);
956        }
957      }
958    }
959  }
960}
961
962
963# =============================================================================
964#
965# Output 16bit Speed-optimized table. Output table's source code if $Source
966# and table's binary data if !$Source.
967#
968# Parameter 1:
969#    "to_ucs"   - Output "to_ucs" table.
970#    "from_ucs" - Output "from_ucs" table.
971# Parameter 2:    Not used when sources are output. Output BE binary if 'n' and
972#                 LE binary if 'v'.
973#
974# =============================================================================
975sub OutputSpeed($;$)
976{
977  my $endian = $_[1];
978  my $tbl;
979  my ($direction, $optimiz, $e, $bytes);
980  $optimiz = $Bits == 16 ? " speed-optimized" : "";
981  $e = $endian ? ($endian eq 'n' ? " Big Endian" : " Little Endian") : "";
982  if ($_[0] eq "to_ucs")
983  {
984    $tbl = \@ToSpeedTbl;
985    $direction = " $CCSName -> UCS";
986    $bytes = $ToSpeedBytes;
987
988    if ($Source)
989    {
990      print OUTFILE
991"/*
992 * 16-bit $CCSName -> UCS speed-optimized table ($ToSpeedBytes bytes).
993 * $Separator
994 */
995#if defined ($GuardToUCS) \\
996 && !($GuardSize)
997
998static const __uint16_t
999${VarToUCSSpeed}\[] =
1000{
1001";
1002    }
1003  }
1004  elsif ($_[0] eq "from_ucs")
1005  {
1006    $tbl = \@FromSpeedTbl;
1007    $direction = " UCS -> $CCSName";
1008    $bytes = $FromSpeedBytes;
1009
1010    if ($Source)
1011    {
1012      print OUTFILE
1013"/*
1014 * 16-bit UCS -> $CCSName speed-optimized table ($FromSpeedBytes bytes).
1015 * $Separator
1016 */
1017
1018#if defined ($GuardFromUCS) \\
1019 && !($GuardSize)
1020
1021static const __uint16_t
1022${VarFromUCSSpeed}\[] =
1023{
1024";
1025    }
1026  }
1027  else
1028  {
1029    Err "Internal script error  Output16bitSpeed()\n";
1030  }
1031
1032  printf "Output%s 16-bit%s%s table (%d bytes).\n",
1033  $e, $direction, $optimiz, $bytes if $Verbose;
1034
1035  # OUTPUT HEADING BLOCK (ALWAYS 16 BIT)
1036  if ($Source)
1037  {
1038    my $count = 0;
1039    print OUTFILE "\t/* Heading Block */";
1040    for (my $i = 0, my $br = 0; $i < 256; $br = ++$i % 8)
1041    {
1042      print OUTFILE "\n\t" unless $br;
1043      if (defined $tbl->[0]->[$i])
1044      {
1045        printf OUTFILE "0x%.4X,", $tbl->[0]->[$i];
1046      }
1047      else
1048      {
1049        print OUTFILE "$MacroInvBlock,";
1050      }
1051    }
1052  }
1053  else
1054  {
1055    print OUTFILE pack($endian, defined $_ ? $_ : $InvBlock)
1056    foreach @{$tbl->[0]};
1057  }
1058
1059  # OUTPUT OTHER BLOCKS
1060  if ($Source)
1061  {
1062    my $index = 256;
1063    for (my $blk = 1; $blk <= $#$tbl; $blk++)
1064    {
1065      next unless defined $tbl->[$blk];
1066      printf OUTFILE "\n\t/* Block $blk, Array index 0x%.4X */", $index;
1067      $index += 256;
1068      for (my $i = 0, my $br = 0; $i < 256; $br = ++$i % 8)
1069      {
1070        print OUTFILE "\n\t" unless $br;
1071        my $code = $tbl->[$blk]->[$i];
1072        print OUTFILE defined $code ?
1073            ($code == $TmpLost ? $MacroLostCode : sprintf "0x%.4X", $code)
1074                                     : $MacroInvCode, ",";
1075      }
1076    }
1077  }
1078  else
1079  {
1080    for (my $blk = 1; $blk <= $#$tbl; $blk++)
1081    {
1082      next unless defined $tbl->[$blk];
1083      for (my $i = 0, my $br = 0; $i < 256; $br = ++$i % 8)
1084      {
1085        my $code = $tbl->[$blk]->[$i];
1086        print OUTFILE pack($endian,
1087          defined $code ? ($code == $TmpLost ? $LostCode : $code) : $InvCode);
1088      }
1089    }
1090  }
1091
1092  if ($Source)
1093  {
1094    if ($_[0] eq "to_ucs")
1095    {
1096      print OUTFILE
1097"
1098};
1099
1100#endif /* $GuardToUCS && !$GuardSize */
1101
1102";
1103    }
1104    else
1105    {
1106      print OUTFILE
1107"
1108};
1109
1110#endif /* $GuardFromUCS && !$GuardSize */
1111
1112";
1113    }
1114  }
1115}
1116
1117# =============================================================================
1118#
1119# Output 16bit Size-optimized table. Output table's source code if $Source
1120# and table's binary data if !$Source.
1121#
1122# Parameter 1:
1123#    "to_ucs"   - Output "to_ucs" table.
1124#    "from_ucs" - Output "from_ucs" table.
1125# Parameter 2:    Not used when sources are output. Output BE binary if 'n' and
1126#                 LE binary if 'v'.
1127#
1128# =============================================================================
1129sub OutputSize($;$)
1130{
1131  my $endian = $_[1];
1132  my $tbl;
1133  my ($direction, $optimiz, $e, $bytes);
1134  $optimiz = $Bits == 16 ? " size-optimized" : "";
1135  $e = $endian ? ($endian eq 'n' ? " Big Endian" : " Little Endian") : "";
1136  if ($_[0] eq "to_ucs")
1137  {
1138    $tbl = \@ToSizeTbl;
1139    $direction = " $CCSName -> UCS";
1140    $bytes = $ToSizeBytes;
1141
1142    if ($Source)
1143    {
1144      print OUTFILE
1145"/*
1146 * 16-bit $CCSName -> UCS size-optimized table ($ToSizeBytes bytes).
1147 * $Separator
1148 */
1149#if defined ($GuardToUCS) \\
1150 && ($GuardSize)
1151
1152static const __uint16_t
1153${VarToUCSSize}\[] =
1154{
1155";
1156    }
1157  }
1158  elsif ($_[0] eq "from_ucs")
1159  {
1160    $tbl = \@FromSizeTbl;
1161    $direction = " UCS -> $CCSName";
1162    $bytes = $FromSizeBytes;
1163    if ($Source)
1164    {
1165      print OUTFILE
1166"/*
1167 * 16-bit UCS -> $CCSName size-optimized table ($FromSizeBytes bytes).
1168 * $Separator
1169 */
1170
1171#if defined ($GuardFromUCS) \\
1172 && ($GuardSize)
1173
1174static const __uint16_t
1175${VarFromUCSSize}\[] =
1176{
1177";
1178    }
1179  }
1180  else
1181  {
1182    Err "Internal script error  Output16bitSize()\n";
1183  }
1184
1185  printf "Output%s 16-bit%s%s table (%d bytes).\n",
1186  $e, $direction, $optimiz, $bytes if $Verbose;
1187
1188  # OUTPUT FIRST 3 ELEMENTS
1189  if ($Source)
1190  {
1191    printf OUTFILE "\t0x%.4X, /* Ranges number */\n", $tbl->[0];
1192    printf OUTFILE "\t0x%.4X, /* Unranged codes number */\n", $tbl->[1];
1193    printf OUTFILE "\t0x%.4X, /* First unranged code index */\n", $tbl->[2];
1194  }
1195  else
1196  {
1197    printf OUTFILE pack $endian, $tbl->[0];
1198    printf OUTFILE pack $endian, $tbl->[1];
1199    printf OUTFILE pack $endian, $tbl->[2];
1200  }
1201
1202  my $idx = 0;
1203  # OUTPUT RANGES
1204  if ($Source)
1205  {
1206    print OUTFILE "\t/* Ranges list: first code, last Code, array index. */\n";
1207    for (my $range = 0; $range <= $#{$tbl->[3]}; $range++)
1208    {
1209      printf OUTFILE "\t/* Array index: 0x%.4X */ 0x%.4X, 0x%.4X, 0x%.4X,\n",
1210             $idx += 3,
1211             $tbl->[3]->[$range]->[0],
1212             $tbl->[3]->[$range]->[1],
1213             $tbl->[3]->[$range]->[2];
1214    }
1215  }
1216  else
1217  {
1218    for (my $range = 0; $range <= $#{$tbl->[3]}; $range++)
1219    {
1220      print OUTFILE pack($endian, $tbl->[3]->[$range]->[0]),
1221                    pack($endian, $tbl->[3]->[$range]->[1]),
1222                    pack($endian, $tbl->[3]->[$range]->[2]);
1223    }
1224  }
1225  $idx += 3;
1226
1227  # OUTPUT RANGES CONTENT
1228  if ($Source)
1229  {
1230    print OUTFILE "\t/* Ranges content */";
1231    for (my $range = 0; $range <= $#{$tbl->[3]}; $range++)
1232    {
1233      printf OUTFILE "\n\t/* Range 0x%.4X - 0x%.4X, array index: 0x%.4X */",
1234             $tbl->[3]->[$range]->[0], $tbl->[3]->[$range]->[1], $idx;
1235             $idx += $tbl->[3]->[$range]->[1] - $tbl->[3]->[$range]->[0] + 1;
1236      for (my $elt = 0, my $br = 0;
1237           $elt <= $#{$tbl->[4]->[$range]};
1238           $br = ++$elt % 8)
1239      {
1240        print OUTFILE "\n\t" unless $br;
1241        if (defined $tbl->[4]->[$range]->[$elt])
1242        {
1243          if ($tbl->[4]->[$range]->[$elt] != $TmpLost)
1244          {
1245            printf OUTFILE "0x%.4X,", $tbl->[4]->[$range]->[$elt];
1246          }
1247          else
1248          {
1249            print OUTFILE "$MacroLostCode,";
1250          }
1251        }
1252        else
1253        {
1254          print OUTFILE "$MacroInvCode,";
1255        }
1256      }
1257    }
1258  }
1259  else
1260  {
1261    for (my $range = 0; $range <= $#{$tbl->[3]}; $range++)
1262    {
1263      for (my $elt = 0; $elt <= $#{$tbl->[4]->[$range]}; $elt++)
1264      {
1265        if (defined $tbl->[4]->[$range]->[$elt])
1266        {
1267          if ($tbl->[4]->[$range]->[$elt] != $TmpLost)
1268          {
1269            print OUTFILE pack $endian, $tbl->[4]->[$range]->[$elt];
1270          }
1271          else
1272          {
1273            print OUTFILE pack $endian, $LostCode;
1274          }
1275        }
1276        else
1277        {
1278          print OUTFILE pack $endian, $InvCode;
1279        }
1280      }
1281    }
1282  }
1283
1284  # OUTPUT UNRANGED CODES
1285  if ($Source)
1286  {
1287    printf OUTFILE "\n\t/* Unranged codes (%d codes) */", $#{$tbl->[4]} + 1;
1288    for (my $i = 0; $i <= $#{$tbl->[5]}; $i++)
1289    {
1290      printf OUTFILE "\n\t/* Array index: 0x%.4X */ 0x%.4X,0x%.4X,",
1291             $idx, $tbl->[5]->[$i]->[0], $tbl->[5]->[$i]->[1];
1292    }
1293  }
1294  else
1295  {
1296    for (my $i = 0; $i <= $#{$tbl->[5]}; $i++)
1297    {
1298      print OUTFILE pack($endian, $tbl->[5]->[$i]->[0]),
1299                    pack($endian, $tbl->[5]->[$i]->[1]);
1300    }
1301  }
1302
1303  if ($Source)
1304  {
1305    if ($_[0] eq "to_ucs")
1306    {
1307      print OUTFILE
1308"
1309};
1310
1311#endif /* $GuardToUCS && $GuardSize */
1312
1313";
1314    }
1315    else
1316    {
1317      print OUTFILE
1318"
1319};
1320
1321#endif /* $GuardFromUCS && $GuardSize */
1322
1323";
1324    }
1325  }
1326}
1327
1328
1329# =============================================================================
1330#
1331# Parse command line options
1332#
1333# =============================================================================
1334sub ProcessOptions()
1335{
1336  my $help_opt    = 'h'; # Print help option
1337  my $input_opt   = 'i'; # Input file name option
1338  my $output_opt  = 'o'; # Output file name option
1339  my $source_opt  = 'S'; # Generate C source file option
1340  my $enc_opt     = 'N'; # Encoding name
1341  my $plane_opt   = 'p'; # Plane number
1342  my $verbose_opt = 'v'; # Verbose output
1343  my $ccscol_opt  = 'x'; # Encoding's column number
1344  my $ucscol_opt  = 'y'; # UCS column number
1345  my $nosize_opt  = 'l'; # Don't generate size-optimized tables
1346  my $nospeed_opt = 'b'; # Don't generate speed-optimized tables
1347  my $nobe_opt    = 'B'; # Don't generate big-endian tables
1348  my $nole_opt    = 'L'; # Don't generate big-endian tables
1349  my $noto_opt    = 't'; # Don't generate "to_ucs" table
1350  my $nofrom_opt  = 'f'; # Don't generate "from_ucs" table
1351
1352  my %args;              # Command line arguments found by getopts()
1353
1354  my $getopts_string =
1355     "$help_opt$source_opt$enc_opt:$verbose_opt$input_opt:$output_opt:$plane_opt:"
1356   . "$nosize_opt$nospeed_opt$nobe_opt$nole_opt$noto_opt$nofrom_opt$ccscol_opt:"
1357   . "$ucscol_opt:";
1358
1359  getopts($getopts_string, \%args) || Err "getopts() failed: $!.\n", 1;
1360
1361  # Print usage rules and exit.
1362  if ($args{$help_opt})
1363  {
1364    print<<END
1365Usage:
1366     -$help_opt - this help message;
1367     -$input_opt - input file name (required);
1368     -$output_opt - output file name;
1369     -$enc_opt - CCS or encoding name;
1370     -$plane_opt - plane number (high 16 bits) to use (in hex);
1371     -$source_opt - generate C source file;
1372     -$nospeed_opt - don't generate speed-optimized tables (binary files only);
1373     -$nosize_opt - don't generate size-optimized tables (binary files only);
1374     -$nobe_opt - don't generate Big Endian tables (binary files only);
1375     -$nole_opt - don't generate Little Endian tables (binary files only);
1376     -$noto_opt - don't generate "to_ucs" table;
1377     -$nofrom_opt - don't generate "from_ucs" table;
1378     -$ccscol_opt - encoding's column number;
1379     -$ucscol_opt - UCS column number;
1380     -$verbose_opt - verbose output.
1381
1382If output file name isn't specified, <infile>.c (for sources) or
1383<infile>.cct (for binaries) is assumed.
1384If encoding name isn't specified <infile> is assumed.
1385<infile> is normalized (small letters, "-" are substituted by "_") input file
1386name base (no extension). For example, for Koi8-r.txt input file, <infile>
1387is koi8_r.
1388END
1389;
1390    exit 0;
1391  }
1392
1393  $Verbose   = $args{$verbose_opt};
1394  $Source    = $args{$source_opt};
1395  $NoSpeed   = $args{$nospeed_opt};
1396  $NoSize    = $args{$nosize_opt};
1397  $NoBE      = $args{$nobe_opt};
1398  $NoLE      = $args{$nole_opt};
1399  $NoFrom    = $args{$nofrom_opt};
1400  $NoTo      = $args{$noto_opt};
1401  $CCSCol    = $args{$ccscol_opt};
1402  $UCSCol    = $args{$ucscol_opt};
1403  $Plane     = $args{$plane_opt};
1404  $InFile    = $args{$input_opt};
1405  $OutFile   = $args{$output_opt};
1406  $CCSName   = $args{$enc_opt};
1407
1408  Err "Error: input file isn't defined. Use -$help_opt for help.\n", 1
1409  unless $InFile;
1410
1411  unless ($OutFile)
1412  {
1413    # Construct output file name
1414    $OutFile = $InFile;
1415    $OutFile =~ s/(.*\/)*([0-9a-zA-Z-_]*)(\..*)$/\L$2/;
1416    $OutFile =~ tr/-/_/;
1417    if ($Source)
1418    {
1419      $OutFile = "$OutFile.c";
1420    }
1421    else
1422    {
1423      $OutFile = "$OutFile.cct"
1424    }
1425  }
1426
1427  unless ($CCSName)
1428  {
1429    # Construct CCS name
1430    $CCSName = $InFile;
1431    $CCSName =~ s/(.*\/)*([0-9a-zA-Z-_]*)(\..*)$/\L$2/;
1432    $CCSName =~ tr/-/_/;
1433  }
1434
1435  Err "-$nosize_opt option can't be used with -$nospeed_opt option "
1436    . "simultaniously.\n", 1 if $NoSpeed && $NoSize;
1437
1438  Err "-$nobe_opt option can't be used with -$nole_opt option "
1439    . "simultaniously.\n", 1 if $NoBE && $NoLE;
1440
1441  Err "-$noto_opt option can't be used with -$nofrom_opt option"
1442    . "simultaniously.\n", 1 if $NoTo && $NoFrom;
1443
1444  Err "-$nosize_opt, -$nospeed_opt, -$nobe_opt -$nole_opt "
1445    . "-$noto_opt and -$nofrom_opt "
1446    . "options can't be used with -$source_opt option.\n"
1447    . "Source code always contains both speed- and size-optimized "
1448    . "tables in System Endian. Use -$help_opt for help.\n", 1
1449  if $Source and $NoSpeed || $NoSize || $NoBE || $NoLE || $NoTo || $NoFrom;
1450
1451  if (!$CCSCol && !$UCSCol)
1452  {
1453    $CCSCol = 0;
1454    $UCSCol = 1;
1455  }
1456  elsif ($CCSCol && $UCSCol)
1457  {
1458    Err "Column number should be >= 0\n", 1 if ($CCSCol <= 0 or $UCSCol <= 0);
1459    $CCSCol -= 1;
1460    $UCSCol -= 1;
1461  }
1462  else
1463  {
1464    Err "Please, define both CCS and UCS column numbers\n", 1;
1465  }
1466
1467  if ($Verbose)
1468  {
1469    print  "Use $InFile file for input.\n",
1470           "Use $OutFile file for output.\n",
1471           "Use $CCSName as CCS name.\n";
1472    print  "Generate C source file.\n"                if $Source;
1473    print  "Generate binary file.\n"                  if !$Source;
1474    printf "Use plane N 0x%.4X.\n", hex $Plane if defined $Plane;
1475    printf "Use column N $CCSCol for $CCSName.\n";
1476    printf "Use column N $UCSCol for UCS.\n";
1477    print  "Don't generate size-optimized tables.\n"  if $NoSize;
1478    print  "Don't generate speed-optimized tables.\n" if $NoSpeed;
1479    print  "Don't generate big-endian tables.\n"      if $NoBE;
1480    print  "Don't generate little-endian tables.\n"   if $NoLE;
1481    print  "Don't generate \"to_ucs\" table.\n"       if $NoTo;
1482    print  "Don't generate \"from_ucs\" table.\n"     if $NoFrom;
1483  }
1484
1485  return;
1486}
1487
1488
1489# =============================================================================
1490#
1491# Print error message, close all and exit
1492#
1493# Parameter 1: error message
1494# Parameter 2: don't delete output file if > 1
1495#
1496# =============================================================================
1497sub Err($;$)
1498{
1499  print STDERR "$_[0]";
1500  close INFILE;
1501  close OUTFILE;
1502  unlink $OutFile unless $_[1];
1503
1504  exit 1;
1505}