Integrate mainline
[p5sagit/p5-mst-13.2.git] / ext / Encode / compile
1 #!../../perl -w
2 BEGIN {
3     unshift @INC, '../../lib';
4     $ENV{PATH} .= ';../..' if $^O eq 'MSWin32';
5 }
6 use strict;
7 use Getopt::Std;
8 my @orig_ARGV = @ARGV;
9 my $perforce  = '$Id$';
10
11 sub encode_U
12 {
13  # UTF-8 encode long hand - only covers part of perl's range
14  my $uv = shift;
15  # chr() works in native space so convert value from table
16  # into that space before using chr().
17  my $ch = chr(utf8::unicode_to_native($uv));
18  # Now get core perl to encode that the way it likes.
19  utf8::encode($ch);
20  return $ch;
21 }
22
23 sub encode_S
24 {
25  # encode single byte
26  my ($ch,$page) = @_;
27  return chr($ch);
28 }
29
30 sub encode_D
31 {
32  # encode double byte MS byte first
33  my ($ch,$page) = @_;
34  return chr($page).chr($ch);
35 }
36
37 sub encode_M
38 {
39  # encode Multi-byte - single for 0..255 otherwise double
40  my ($ch,$page) = @_;
41  return &encode_D if $page;
42  return &encode_S;
43 }
44
45 # Win32 does not expand globs on command line
46 eval "\@ARGV = map(glob(\$_),\@ARGV)" if ($^O eq 'MSWin32');
47
48 my %opt;
49 getopts('qo:f:n:',\%opt);
50 my $cname = (exists $opt{'o'}) ? $opt{'o'} : shift(@ARGV);
51 chmod(0666,$cname) if -f $cname && !-w $cname;
52 open(C,">$cname") || die "Cannot open $cname:$!";
53
54
55 my $dname = $cname;
56 $dname =~ s/(\.[^\.]*)?$/.def/;
57
58 my ($doC,$doEnc,$doUcm,$doPet);
59
60 if ($cname =~ /\.(c|xs)$/)
61  {
62   $doC = 1;
63   chmod(0666,$dname) if -f $cname && !-w $dname;
64   open(D,">$dname") || die "Cannot open $dname:$!";
65   my $hname = $cname;
66   $hname =~ s/(\.[^\.]*)?$/.h/;
67   chmod(0666,$hname) if -f $cname && !-w $hname;
68   open(H,">$hname") || die "Cannot open $hname:$!";
69
70   foreach my $fh (\*C,\*D,\*H)
71   {
72    print $fh <<"END" unless $opt{'q'};
73 /*
74  !!!!!!!   DO NOT EDIT THIS FILE   !!!!!!!
75  This file was autogenerated by:
76  $^X $0 $cname @orig_ARGV
77 */
78 END
79   }
80
81   if ($cname =~ /(\w+)\.xs$/)
82    {
83     print C "#include <EXTERN.h>\n";
84     print C "#include <perl.h>\n";
85     print C "#include <XSUB.h>\n";
86     print C "#define U8 U8\n";
87    }
88   print C "#include \"encode.h\"\n";
89  }
90 elsif ($cname =~ /\.enc$/)
91  {
92   $doEnc = 1;
93  }
94 elsif ($cname =~ /\.ucm$/)
95  {
96   $doUcm = 1;
97  }
98 elsif ($cname =~ /\.pet$/)
99  {
100   $doPet = 1;
101  }
102
103 my @encfiles;
104 if (exists $opt{'f'})
105  {
106   # -F is followed by name of file containing list of filenames
107   my $flist = $opt{'f'};
108   open(FLIST,$flist) || die "Cannot open $flist:$!";
109   chomp(@encfiles = <FLIST>);
110   close(FLIST);
111  }
112 else
113  {
114   @encfiles = @ARGV;
115  }
116
117 my %encoding;
118 my %strings;
119
120 sub cmp_name
121 {
122  if ($a =~ /^.*-(\d+)/)
123   {
124    my $an = $1;
125    if ($b =~ /^.*-(\d+)/)
126     {
127      my $r = $an <=> $1;
128      return $r if $r;
129     }
130   }
131  return $a cmp $b;
132 }
133
134
135 foreach my $enc (sort cmp_name @encfiles)
136  {
137   my ($name,$sfx) = $enc =~ /^.*?([\w-]+)\.(enc|ucm)$/;
138   $name = $opt{'n'} if exists $opt{'n'};
139   if (open(E,$enc))
140    {
141     if ($sfx eq 'enc')
142      {
143       compile_enc(\*E,lc($name));
144      }
145     else
146      {
147       compile_ucm(\*E,lc($name));
148      }
149    }
150   else
151    {
152     warn "Cannot open $enc for $name:$!";
153    }
154  }
155
156 if ($doC)
157  {
158   foreach my $name (sort cmp_name keys %encoding)
159    {
160     my ($e2u,$u2e,$erep,$min_el,$max_el) = @{$encoding{$name}};
161     output(\*C,$name.'_utf8',$e2u);
162     output(\*C,'utf8_'.$name,$u2e);
163     push(@{$encoding{$name}},outstring(\*C,$e2u->{Cname}.'_def',$erep));
164    }
165   foreach my $enc (sort cmp_name keys %encoding)
166    {
167     my ($e2u,$u2e,$rep,$min_el,$max_el,$rsym) = @{$encoding{$enc}};
168     my @info = ($e2u->{Cname},$u2e->{Cname},$rsym,length($rep),$min_el,$max_el);
169     my $sym = "${enc}_encoding";
170     $sym =~ s/\W+/_/g;
171     print C "encode_t $sym = \n";
172     print C " {",join(',',@info,"{\"$enc\",(const char *)0}"),"};\n\n";
173    }
174
175   foreach my $enc (sort cmp_name keys %encoding)
176    {
177     my $sym = "${enc}_encoding";
178     $sym =~ s/\W+/_/g;
179     print H "extern encode_t $sym;\n";
180     print D " Encode_Define(aTHX_ &$sym);\n";
181    }
182
183   if ($cname =~ /(\w+)\.xs$/)
184    {
185     my $mod = $1;
186     print C "\nMODULE = Encode::$mod\tPACKAGE = Encode::$mod\n\n";
187     print C "BOOT:\n{\n";
188     print C "#include \"$dname\"\n";
189     print C "}\n";
190    }
191   close(D);
192   close(H);
193  }
194 elsif ($doEnc)
195  {
196   foreach my $name (sort cmp_name keys %encoding)
197    {
198     my ($e2u,$u2e,$erep,$min_el,$max_el) = @{$encoding{$name}};
199     output_enc(\*C,$name,$e2u);
200    }
201  }
202 elsif ($doUcm)
203  {
204   foreach my $name (sort cmp_name keys %encoding)
205    {
206     my ($e2u,$u2e,$erep,$min_el,$max_el) = @{$encoding{$name}};
207     output_ucm(\*C,$name,$u2e,$erep,$min_el,$max_el);
208    }
209  }
210
211 close(C);
212
213
214 sub compile_ucm
215 {
216  my ($fh,$name) = @_;
217  my $e2u = {};
218  my $u2e = {};
219  my $cs;
220  my %attr;
221  while (<$fh>)
222   {
223    s/#.*$//;
224    last if /^\s*CHARMAP\s*$/i;
225    if (/^\s*<(\w+)>\s+"?([^"]*)"?\s*$/i)
226     {
227      $attr{$1} = $2;
228     }
229   }
230  if (!defined($cs =  $attr{'code_set_name'}))
231   {
232    warn "No <code_set_name> in $name\n";
233   }
234  else
235   {
236    $name = $cs unless exists $opt{'n'};
237   }
238  my $erep;
239  my $urep;
240  my $max_el;
241  my $min_el;
242  if (exists $attr{'subchar'})
243   {
244    my @byte;
245    $attr{'subchar'} =~ /^\s*/cg;
246    push(@byte,$1) while $attr{'subchar'} =~ /\G\\x([0-9a-f]+)/icg;
247    $erep = join('',map(chr(hex($_)),@byte));
248   }
249  print "Scanning $name ($cs)\n";
250  my $nfb = 0;
251  my $hfb = 0;
252  while (<$fh>)
253   {
254    s/#.*$//;
255    last if /^\s*END\s+CHARMAP\s*$/i;
256    next if /^\s*$/;
257    my ($u,@byte);
258    my $fb = '';
259    $u = $1 if (/^<U([0-9a-f]+)>\s+/igc);
260    push(@byte,$1) while /\G\\x([0-9a-f]+)/igc;
261    $fb = $1 if /\G\s*(\|[0-3])/gc;
262    # warn "$_: $u @byte | $fb\n";
263    die "Bad line:$_" unless /\G\s*(#.*)?$/gc;
264    if (defined($u))
265     {
266      my $uch = encode_U(hex($u));
267      my $ech = join('',map(chr(hex($_)),@byte));
268      my $el  = length($ech);
269      $max_el = $el if (!defined($max_el) || $el > $max_el);
270      $min_el = $el if (!defined($min_el) || $el < $min_el);
271      if (length($fb))
272       {
273        $fb = substr($fb,1);
274        $hfb++;
275       }
276      else
277       {
278        $nfb++;
279        $fb = '0';
280       }
281      # $fb is fallback flag
282      # 0 - round trip safe
283      # 1 - fallback for unicode -> enc
284      # 2 - skip sub-char mapping
285      # 3 - fallback enc -> unicode
286      enter($u2e,$uch,$ech,$u2e,$fb+0) if ($fb =~ /[01]/);
287      enter($e2u,$ech,$uch,$e2u,$fb+0) if ($fb =~ /[03]/);
288     }
289    else
290     {
291      warn $_;
292     }
293   }
294  if ($nfb && $hfb)
295   {
296    die "$nfb entries without fallback, $hfb entries with\n";
297   }
298  $encoding{$name} = [$e2u,$u2e,$erep,$min_el,$max_el];
299 }
300
301 sub compile_enc
302 {
303  my ($fh,$name) = @_;
304  my $e2u = {};
305  my $u2e = {};
306
307  my $type;
308  while ($type = <$fh>)
309   {
310    last if $type !~ /^\s*#/;
311   }
312  chomp($type);
313  return if $type eq 'E';
314  my ($def,$sym,$pages) = split(/\s+/,scalar(<$fh>));
315  warn "$type encoded $name\n";
316  my $rep = '';
317  my $min_el;
318  my $max_el;
319  {
320   my $v = hex($def);
321   no strict 'refs';
322   $rep = &{"encode_$type"}($v & 0xFF, ($v >> 8) & 0xffe);
323  }
324  my %seen;
325  while ($pages--)
326   {
327    my $line = <$fh>;
328    chomp($line);
329    my $page = hex($line);
330    my $ch = 0;
331    for (my $i = 0; $i < 16; $i++)
332     {
333      my $line = <$fh>;
334      for (my $j = 0; $j < 16; $j++)
335       {
336        no strict 'refs';
337        my $ech = &{"encode_$type"}($ch,$page);
338        my $val = hex(substr($line,0,4,''));
339        next if $val == 0xFFFD;
340        if ($val || (!$ch && !$page))
341         {
342          my $el  = length($ech);
343          $max_el = $el if (!defined($max_el) || $el > $max_el);
344          $min_el = $el if (!defined($min_el) || $el < $min_el);
345          my $uch = encode_U($val);
346          if (exists $seen{$uch})
347           {
348            warn sprintf("U%04X is %02X%02X and %02X%02X\n",
349                         $val,$page,$ch,@{$seen{$uch}});
350           }
351          else
352           {
353            $seen{$uch} = [$page,$ch];
354           }
355          enter($e2u,$ech,$uch,$e2u,0);
356          enter($u2e,$uch,$ech,$u2e,0);
357         }
358        else
359         {
360          # No character at this position
361          # enter($e2u,$ech,undef,$e2u);
362         }
363        $ch++;
364       }
365     }
366   }
367  $encoding{$name} = [$e2u,$u2e,$rep,$min_el,$max_el];
368 }
369
370 sub enter
371 {
372  my ($a,$s,$d,$t,$fb) = @_;
373  $t = $a if @_ < 4;
374  my $b = substr($s,0,1);
375  my $e = $a->{$b};
376  unless ($e)
377   {     # 0  1  2  3         4  5
378    $e = [$b,$b,'',{},length($s),0,$fb];
379    $a->{$b} = $e;
380   }
381  if (length($s) > 1)
382   {
383    enter($e->[3],substr($s,1),$d,$t,$fb);
384   }
385  else
386   {
387    $e->[2] = $d;
388    $e->[3] = $t;
389    $e->[5] = length($d);
390   }
391 }
392
393 sub outstring
394 {
395  my ($fh,$name,$s) = @_;
396  my $sym = $strings{$s};
397  unless ($sym)
398   {
399    foreach my $o (keys %strings)
400     {
401      my $i = index($o,$s);
402      if ($i >= 0)
403       {
404        $sym = $strings{$o};
405        $sym .= sprintf("+0x%02x",$i) if ($i);
406        return $sym;
407       }
408     }
409    $strings{$s} = $sym = $name;
410    printf $fh "\nstatic const U8 %s[%d] =\n",$name,length($s);
411    # Do in chunks of 16 chars to constrain line length
412    # Assumes ANSI C adjacent string litteral concatenation
413    while (length($s))
414     {
415      my $c = substr($s,0,16,'');
416      print  $fh '"',join('',map(sprintf('\x%02x',ord($_)),split(//,$c))),'"';
417      print  $fh "\n" if length($s);
418     }
419    printf $fh ";\n";
420   }
421  return $sym;
422 }
423
424 sub process
425 {
426  my ($name,$a) = @_;
427  $name =~ s/\W+/_/g;
428  $a->{Cname} = $name;
429  my @keys = grep(ref($a->{$_}),sort keys %$a);
430  my $l;
431  my @ent;
432  foreach my $b (@keys)
433   {
434    my ($s,$f,$out,$t,$end) = @{$a->{$b}};
435    if (defined($l) &&
436        ord($b) == ord($a->{$l}[1])+1 &&
437        $a->{$l}[3] == $a->{$b}[3] &&
438        $a->{$l}[4] == $a->{$b}[4] &&
439        $a->{$l}[5] == $a->{$b}[5] &&
440        $a->{$l}[6] == $a->{$b}[6]
441        # && length($a->{$l}[2]) < 16
442       )
443     {
444      my $i = ord($b)-ord($a->{$l}[0]);
445      $a->{$l}[1]  = $b;
446      $a->{$l}[2] .= $a->{$b}[2];
447     }
448    else
449     {
450      $l = $b;
451      push(@ent,$b);
452     }
453    if (exists $t->{Cname})
454     {
455      $t->{'Forward'} = 1 if $t != $a;
456     }
457    else
458     {
459      process(sprintf("%s_%02x",$name,ord($s)),$t);
460     }
461   }
462  if (ord($keys[-1]) < 255)
463   {
464    my $t = chr(ord($keys[-1])+1);
465    $a->{$t} = [$t,chr(255),undef,$a,0,0];
466    push(@ent,$t);
467   }
468  $a->{'Entries'} = \@ent;
469 }
470
471 sub outtable
472 {
473  my ($fh,$a) = @_;
474  my $name = $a->{'Cname'};
475  # String tables
476  foreach my $b (@{$a->{'Entries'}})
477   {
478    next unless $a->{$b}[5];
479    my $s = ord($a->{$b}[0]);
480    my $e = ord($a->{$b}[1]);
481    outstring($fh,sprintf("%s__%02x_%02x",$name,$s,$e),$a->{$b}[2]);
482   }
483  if ($a->{'Forward'})
484   {
485    print $fh "\nstatic encpage_t $name\[",scalar(@{$a->{'Entries'}}),"];\n";
486   }
487  $a->{'Done'} = 1;
488  foreach my $b (@{$a->{'Entries'}})
489   {
490    my ($s,$e,$out,$t,$end,$l) = @{$a->{$b}};
491    outtable($fh,$t) unless $t->{'Done'};
492   }
493  print $fh "\nstatic encpage_t $name\[",scalar(@{$a->{'Entries'}}),"] = {\n";
494  foreach my $b (@{$a->{'Entries'}})
495   {
496    my ($s,$e,$out,$t,$end,$l,$fb) = @{$a->{$b}};
497    my $sc = ord($s);
498    my $ec = ord($e);
499    $end |= 0x80 if $fb;
500    print  $fh "{";
501    if ($l)
502     {
503      printf $fh outstring($fh,'',$out);
504     }
505    else
506     {
507      print  $fh "0";
508     }
509    print  $fh ",",$t->{Cname};
510    printf $fh ",0x%02x,0x%02x,$l,$end},\n",$sc,$ec;
511   }
512  print $fh "};\n";
513 }
514
515 sub output
516 {
517  my ($fh,$name,$a) = @_;
518  process($name,$a);
519  # Sub-tables
520  outtable($fh,$a);
521 }
522
523 sub output_enc
524 {
525  my ($fh,$name,$a) = @_;
526  foreach my $b (sort keys %$a)
527   {
528    my ($s,$e,$out,$t,$end,$l,$fb) = @{$a->{$b}};
529   }
530 }
531
532 sub decode_U
533 {
534  my $s = shift;
535 }
536
537 my @uname;
538 sub char_names
539 {
540  my $s = do "unicore/Name.pl";
541  die "char_names: unicore/Name.pl: $!\n" unless defined $s;
542  pos($s) = 0;
543  while ($s =~ /\G([0-9a-f]+)\t([0-9a-f]*)\t(.*?)\s*\n/igc)
544   {
545    my $name = $3;
546    my $s = hex($1);
547    last if $s >= 0x10000;
548    my $e = length($2) ? hex($2) : $s;
549    for (my $i = $s; $i <= $e; $i++)
550     {
551      $uname[$i] = $name;
552 #    print sprintf("U%04X $name\n",$i);
553     }
554   }
555 }
556
557 sub output_ucm_page
558 {
559  my ($cmap,$a,$t,$pre) = @_;
560  # warn sprintf("Page %x\n",$pre);
561  foreach my $b (sort keys %$t)
562   {
563    my ($s,$e,$out,$n,$end,$l,$fb) = @{$t->{$b}};
564    die "oops $s $e" unless $s eq $e;
565    my $u = ord($s);
566    if ($n != $a && $n != $t)
567     {
568      output_ucm_page($cmap,$a,$n,(($pre|($u &0x3F)) << 6)&0xFFFF);
569     }
570    elsif (length($out))
571     {
572      if ($pre)
573       {
574        $u = $pre|($u &0x3f);
575       }
576      my $s = sprintf "<U%04X> ",$u;
577      foreach my $c (split(//,$out))
578       {
579        $s .= sprintf "\\x%02X",ord($c);
580       }
581      $s .= sprintf " |%d # %s\n",($fb ? 1 : 0),$uname[$u];
582      push(@$cmap,$s);
583     }
584    else
585     {
586      warn join(',',@{$t->{$b}},$a,$t);
587     }
588   }
589 }
590
591 sub output_ucm
592 {
593  my ($fh,$name,$h,$rep,$min_el,$max_el) = @_;
594  print $fh "# $0 @orig_ARGV\n" unless $opt{'q'};
595  print $fh "<code_set_name> \"$name\"\n";
596  char_names();
597  if (defined $min_el)
598   {
599    print $fh "<mb_cur_min> $min_el\n";
600   }
601  if (defined $max_el)
602   {
603    print $fh "<mb_cur_max> $max_el\n";
604   }
605  if (defined $rep)
606   {
607    print $fh "<subchar> ";
608    foreach my $c (split(//,$rep))
609     {
610      printf $fh "\\x%02X",ord($c);
611     }
612    print $fh "\n";
613   }
614  my @cmap;
615  output_ucm_page(\@cmap,$h,$h,0);
616  print $fh "#\nCHARMAP\n";
617  foreach my $line (sort { substr($a,8) cmp substr($b,8) } @cmap)
618   {
619    print $fh $line;
620   }
621  print $fh "END CHARMAP\n";
622 }
623