lib/charnames.pm
Jeffrey Friedl [Tue, 13 Nov 2001 00:36:21 +0000 (16:36 -0800)]
Message-Id: <200111130836.fAD8aLG76010@ventrue.corp.yahoo.com>

p4raw-id: //depot/perl@12968

lib/charnames.pm

index 70d6d17..b93f723 100644 (file)
@@ -1,23 +1,37 @@
 package charnames;
-
-our $VERSION = '1.00';
+use strict;
+use warnings;
+use Carp;
+our $VERSION = '1.01';
 
 use bytes ();          # for $bytes::hint_bits
-use warnings();
 $charnames::hint_bits = 0x20000;
 
 my $txt;
 
 # This is not optimized in any way yet
-sub charnames {
-  $name = shift;
+sub charnames
+{
+  my $name = shift;
+
+  ## Suck in the code/name list as a big string.
+  ## Lines look like:
+  ##     "0052\t\tLATIN CAPITAL LETTER R\n"
   $txt = do "unicore/Name.pl" unless $txt;
+
+  ## @off will hold the index into the code/name string of the start and
+  ## end of the name as we find it.
   my @off;
+
+  ## If :full, look for the the name exactly
   if ($^H{charnames_full} and $txt =~ /\t\t$name$/m) {
     @off = ($-[0], $+[0]);
   }
+
+  ## If we didn't get above, and :short allowed, look for the short name.
+  ## The short name is like "greek:Sigma"
   unless (@off) {
-    if ($^H{charnames_short} and $name =~ /^(.*?):(.*)/s) {
+    if ($^H{charnames_short} and $name =~ /^(.+?):(.+)/s) {
       my ($script, $cname) = ($1,$2);
       my $case = ( $cname =~ /[[:upper:]]/ ? "CAPITAL" : "SMALL");
       if ($txt =~ m/\t\t\U$script\E (?:$case )?LETTER \U$cname$/m) {
@@ -25,19 +39,42 @@ sub charnames {
       }
     }
   }
-  unless (@off) {
-    my $case = ( $name =~ /[[:upper:]]/ ? "CAPITAL" : "SMALL");
-    for ( @{$^H{charnames_scripts}} ) {
-      (@off = ($-[0], $+[0])), last 
-       if $txt =~ m/\t\t$_ (?:$case )?LETTER \U$name$/m;
-    }
+
+  ## If we still don't have it, check for the name among the loaded
+  ## scripts.
+  if (not @off)
+  {
+      my $case = ( $name =~ /[[:upper:]]/ ? "CAPITAL" : "SMALL");
+      for my $script ( @{$^H{charnames_scripts}} )
+      {
+          if ($txt =~ m/\t\t$script (?:$case )?LETTER \U$name$/m) {
+              @off = ($-[0], $+[0]);
+              last;
+          }
+      }
   }
+
+  ## If we don't have it by now, give up.
   die "Unknown charname '$name'" unless @off;
 
-  my $hexlen = 4; # Unicode guarantees 4-, 5-, or 6-digit format
-  $hexlen++ while
-      $hexlen < 6 && substr($txt, $off[0] - $hexlen - 1, 1) =~ /[0-9a-f]/;
-  my $ord = hex substr $txt, $off[0] - $hexlen, $hexlen;
+  ##
+  ## Now know where in the string the name starts.
+  ## The code, in hex, is befor that.
+  ##
+  ## The code can be 4-6 characters long, so we've got to sort of
+  ## go look for it, just after the newline that comes before $off[0].
+  ##
+  ## This would be much easier if unicore/Name.pl had info in
+  ## a name/code order, instead of code/name order.
+  ##
+  ## The +1 after the rindex() is to skip past the newline we're finding,
+  ## or, if the rindex() fails, to put us to an offset of zero.
+  ##
+  my $hexstart = rindex($txt, "\n", $off[0]) + 1;
+
+  ## we know where it starts, so turn into number - the ordinal for the char.
+  my $ord = hex substr($txt, $hexstart, $off[0] - $hexstart);
+
   if ($^H & $bytes::hint_bits) {       # "use bytes" in effect?
     use bytes;
     return chr $ord if $ord <= 255;
@@ -48,32 +85,76 @@ sub charnames {
   return pack "U", $ord;
 }
 
-sub import {
-  shift;
-  die "`use charnames' needs explicit imports list" unless @_;
+sub import
+{
+  shift; ## ignore class name
+
+  if (not @_)
+  {
+      carp("`use charnames' needs explicit imports list");
+  }
   $^H |= $charnames::hint_bits;
   $^H{charnames} = \&charnames ;
+
+  ##
+  ## fill %h keys with our @_ args.
+  ##
   my %h;
   @h{@_} = (1) x @_;
+
   $^H{charnames_full} = delete $h{':full'};
   $^H{charnames_short} = delete $h{':short'};
   $^H{charnames_scripts} = [map uc, keys %h];
-  if (warnings::enabled('utf8') && @{$^H{charnames_scripts}}) {
-       $txt = do "unicore/Name.pl" unless $txt;
-    for (@{$^H{charnames_scripts}}) {
-        warnings::warn('utf8',  "No such script: '$_'") unless
-           $txt =~ m/\t\t$_ (?:CAPITAL |SMALL )?LETTER /;
-       }
+
+  ##
+  ## If utf8? warnings are enabled, and some scripts were given,
+  ## see if at least we can find one letter of each script.
+  ##
+  if (warnings::enabled('utf8') && @{$^H{charnames_scripts}})
+  {
+      $txt = do "unicore/Name.pl" unless $txt;
+
+      for my $script (@{$^H{charnames_scripts}})
+      {
+          if (not $txt =~ m/\t\t$script (?:CAPITAL |SMALL )?LETTER /) {
+              warnings::warn('utf8',  "No such script: '$script'");
+          }
+      }
   }
 }
 
+sub viacode
+{
+    if (@_ != 1) {
+        carp "charnames::viacode() expects one numeric value";
+        return ()
+    }
+    my $arg = shift;
+
+    my $hex;
+    if ($arg =~ m/^[0-9]+$/) {
+        $hex = sprintf "%04X", $arg;
+    } else {
+        carp("unexpected arg \"$arg\" to charnames::viacode()");
+        return ();
+    }
+
+    $txt = do "unicore/Name.pl" unless $txt;
+
+    if ($txt =~ m/^$hex\t\t(.+)/m) {
+        return $1;
+    } else {
+        return ();
+    }
+}
+
 
 1;
 __END__
 
 =head1 NAME
 
-charnames - define character names for C<\N{named}> string literal escape.
+charnames - define character names for C<\N{named}> string literal escapes.
 
 =head1 SYNOPSIS
 
@@ -86,6 +167,8 @@ charnames - define character names for C<\N{named}> string literal escape.
   use charnames qw(cyrillic greek);
   print "\N{sigma} is Greek sigma, and \N{be} is Cyrillic b.\n";
 
+  print charname::viacode(0x1234); # prints "ETHIOPIC SYLLABLE SEE"
+
 =head1 DESCRIPTION
 
 Pragma C<use charnames> supports arguments C<:full>, C<:short> and
@@ -139,6 +222,20 @@ state of C<bytes>-flag as in:
        }
     }
 
+=head1 charnames::viacode(code)
+
+Returns the full name of the character indicated by the numeric code.
+The example
+
+    print charnames::viacode(0x2722);
+
+prints "FOUR TEARDROP-SPOKED ASTERISK".
+
+Returns nothing if no name is known for the code.
+
+This works only for the standard names, and does not yet aply 
+to custom translators.
+
 =head1 BUGS
 
 Since evaluation of the translation function happens in a middle of