A swath of VERSION patches from Nicholas Clark.
[p5sagit/p5-mst-13.2.git] / lib / Unicode / UCD.pm
index ff9cc8f..baafd1a 100644 (file)
@@ -3,7 +3,9 @@ package Unicode::UCD;
 use strict;
 use warnings;
 
-our $VERSION = '0.2';
+our $VERSION = '0.21';
+
+use Storable qw(dclone);
 
 require Exporter;
 
@@ -31,7 +33,7 @@ Unicode::UCD - Unicode character database
     my $charblock  = charblock($codepoint);
 
     use Unicode::UCD 'charscript';
-    my $charscript = charblock($codepoint);
+    my $charscript = charscript($codepoint);
 
     use Unicode::UCD 'charblocks';
     my $charblocks = charblocks();
@@ -108,7 +110,7 @@ as defined by the Unicode standard:
     title            titlecase equivalent mapping
 
     block            block the character belongs to (used in \p{In...})
-    script           script the character belongs to 
+    script           script the character belongs to
 
 If no match is found, a reference to an empty hash is returned.
 
@@ -126,9 +128,9 @@ you will need also the compexcl(), casefold(), and casespec() functions.
 sub _getcode {
     my $arg = shift;
 
-    if ($arg =~ /^\d+$/) {
+    if ($arg =~ /^[1-9]\d*$/) {
        return $arg;
-    } elsif ($arg =~ /^(?:U\+|0x)?([[:xdigit:]]+)$/) {
+    } elsif ($arg =~ /^(?:[Uu]\+|0[xX])?([[:xdigit:]]+)$/) {
        return hex($1);
     }
 
@@ -204,11 +206,12 @@ sub charinfo {
         last;
       }
     }
-    openunicode(\$UNICODEFH, "Unicode.txt");
+    openunicode(\$UNICODEFH, "UnicodeData.txt");
     if (defined $UNICODEFH) {
        use Search::Dict 1.02;
        if (look($UNICODEFH, "$hexk;", { xfrm => sub { $_[0] =~ /^([^;]+);(.+)/; sprintf "%06X;$2", hex($1) } } ) >= 0) {
            my $line = <$UNICODEFH>;
+           return unless defined $line;
            chomp $line;
            my %prop;
            @prop{qw(
@@ -280,13 +283,12 @@ positions within all blocks are defined.
 
 See also L</Blocks versus Scripts>.
 
-If supplied with an argument that can't be a code point, charblock()
-tries to do the opposite and interpret the argument as a character
-block.  The return value is a I<range>: an anonymous list that
-contains anonymous lists, which in turn contain I<start-of-range>,
-I<end-of-range> code point pairs.  You can test whether a code point
-is in a range using the L</charinrange> function.  If the argument is
-not a known charater block, C<undef> is returned.
+If supplied with an argument that can't be a code point, charblock() tries
+to do the opposite and interpret the argument as a character block. The
+return value is a I<range>: an anonymous list of lists that contain
+I<start-of-range>, I<end-of-range> code point pairs. You can test whether a
+code point is in a range using the L</charinrange> function. If the
+argument is not a known charater block, C<undef> is returned.
 
 =cut
 
@@ -296,6 +298,7 @@ my %BLOCKS;
 sub _charblocks {
     unless (@BLOCKS) {
        if (openunicode(\$BLOCKSFH, "Blocks.txt")) {
+           local $_;
            while (<$BLOCKSFH>) {
                if (/^([0-9A-F]+)\.\.([0-9A-F]+);\s+(.+)/) {
                    my ($lo, $hi) = (hex($1), hex($2));
@@ -320,7 +323,7 @@ sub charblock {
        _search(\@BLOCKS, 0, $#BLOCKS, $code);
     } else {
        if (exists $BLOCKS{$arg}) {
-           return $BLOCKS{$arg};
+           return dclone $BLOCKS{$arg};
        } else {
            return;
        }
@@ -342,13 +345,12 @@ character belongs to, e.g.  C<Latin>, C<Greek>, C<Han>.
 
 See also L</Blocks versus Scripts>.
 
-If supplied with an argument that can't be a code point, charscript()
-tries to do the opposite and interpret the argument as a character
-script.  The return value is a I<range>: an anonymous list that
-contains anonymous lists, which in turn contain I<start-of-range>,
-I<end-of-range> code point pairs.  You can test whether a code point
-is in a range using the L</charinrange> function.  If the argument is
-not a known charater script, C<undef> is returned.
+If supplied with an argument that can't be a code point, charscript() tries
+to do the opposite and interpret the argument as a character script. The
+return value is a I<range>: an anonymous list of lists that contain
+I<start-of-range>, I<end-of-range> code point pairs. You can test whether a
+code point is in a range using the L</charinrange> function. If the
+argument is not a known charater script, C<undef> is returned.
 
 =cut
 
@@ -358,6 +360,7 @@ my %SCRIPTS;
 sub _charscripts {
     unless (@SCRIPTS) {
        if (openunicode(\$SCRIPTSFH, "Scripts.txt")) {
+           local $_;
            while (<$SCRIPTSFH>) {
                if (/^([0-9A-F]+)(?:\.\.([0-9A-F]+))?\s+;\s+(\w+)/) {
                    my ($lo, $hi) = (hex($1), $2 ? hex($2) : hex($1));
@@ -385,7 +388,7 @@ sub charscript {
        _search(\@SCRIPTS, 0, $#SCRIPTS, $code);
     } else {
        if (exists $SCRIPTS{$arg}) {
-           return $SCRIPTS{$arg};
+           return dclone $SCRIPTS{$arg};
        } else {
            return;
        }
@@ -407,7 +410,7 @@ See also L</Blocks versus Scripts>.
 
 sub charblocks {
     _charblocks() unless %BLOCKS;
-    return \%BLOCKS;
+    return dclone \%BLOCKS;
 }
 
 =head2 charscripts
@@ -425,7 +428,7 @@ See also L</Blocks versus Scripts>.
 
 sub charscripts {
     _charscripts() unless %SCRIPTS;
-    return \%SCRIPTS;
+    return dclone \%SCRIPTS;
 }
 
 =head2 Blocks versus Scripts
@@ -433,13 +436,13 @@ sub charscripts {
 The difference between a block and a script is that scripts are closer
 to the linguistic notion of a set of characters required to present
 languages, while block is more of an artifact of the Unicode character
-numbering and separation into blocks of 256 characters.
+numbering and separation into blocks of (mostly) 256 characters.
 
 For example the Latin B<script> is spread over several B<blocks>, such
 as C<Basic Latin>, C<Latin 1 Supplement>, C<Latin Extended-A>, and
 C<Latin Extended-B>.  On the other hand, the Latin script does not
 contain all the characters of the C<Basic Latin> block (also known as
-the ASCII): it includes only the letters, not for example the digits
+the ASCII): it includes only the letters, and not, for example, the digits
 or the punctuation.
 
 For blocks see http://www.unicode.org/Public/UNIDATA/Blocks.txt
@@ -448,26 +451,21 @@ For scripts see UTR #24: http://www.unicode.org/unicode/reports/tr24/
 
 =head2 Matching Scripts and Blocks
 
-Both scripts and blocks can be matched using the regular expression
-construct C<\p{In...}> and its negation C<\P{In...}>.
-
-The name of the script or the block comes after the C<In>, for example
-C<\p{InCyrillic}>, C<\P{InBasicLatin}>.  Spaces and dashes ('-') are
-removed from the names for the C<\p{In...}>, for example
-C<LatinExtendedA> instead of C<Latin Extended-A>.
-
-There are a few cases where there is both a script and a block by the
-same name, in these cases the block version has C<Block> appended to
-its name: C<\p{InKatakana}> is the script, C<\p{InKatakanaBlock}> is
-the block.
+Scripts are matched with the regular-expression construct
+C<\p{...}> (e.g. C<\p{Tibetan}> matches characters of the Tibetan script),
+while C<\p{In...}> is used for blocks (e.g. C<\p{InTibetan}> matches
+any of the 256 code points in the Tibetan block).
 
 =head2 Code Point Arguments
 
-A <code point argument> is either a decimal or a hexadecimal scalar
-designating a Unicode character, or "U+" followed by hexadecimals
-designating a Unicode character.  Note that Unicode is B<not> limited
-to 16 bits (the number of Unicode characters is open-ended, in theory
-unlimited): you may have more than 4 hexdigits.
+A I<code point argument> is either a decimal or a hexadecimal scalar
+designating a Unicode character, or C<U+> followed by hexadecimals
+designating a Unicode character.  In other words, if you want a code
+point to be interpreted as a hexadecimal number, you must prefix it
+with either C<0x> or C<U+>, because a string like e.g. C<123> will
+be interpreted as a decimal code point.  Also note that Unicode is
+B<not> limited to 16 bits (the number of Unicode characters is
+open-ended, in theory unlimited): you may have more than 4 hexdigits.
 
 =head2 charinrange
 
@@ -502,9 +500,10 @@ my %COMPEXCL;
 
 sub _compexcl {
     unless (%COMPEXCL) {
-       if (openunicode(\$COMPEXCLFH, "CompExcl.txt")) {
+       if (openunicode(\$COMPEXCLFH, "CompositionExclusions.txt")) {
+           local $_;
            while (<$COMPEXCLFH>) {
-               if (/^([0-9A-F]+) \# /) {
+               if (/^([0-9A-F]+)\s+\#\s+/) {
                    my $code = hex($1);
                    $COMPEXCL{$code} = undef;
                }
@@ -529,7 +528,7 @@ sub compexcl {
 
     use Unicode::UCD 'casefold';
 
-    my %casefold = casefold("09dc");
+    my $casefold = casefold("00DF");
 
 The casefold() returns the locale-independent case folding of the
 character specified by a B<code point argument>.
@@ -572,7 +571,8 @@ my %CASEFOLD;
 
 sub _casefold {
     unless (%CASEFOLD) {
-       if (openunicode(\$CASEFOLDFH, "CaseFold.txt")) {
+       if (openunicode(\$CASEFOLDFH, "CaseFolding.txt")) {
+           local $_;
            while (<$CASEFOLDFH>) {
                if (/^([0-9A-F]+); ([CFSI]); ([0-9A-F]+(?: [0-9A-F]+)*);/) {
                    my $code = hex($1);
@@ -601,7 +601,7 @@ sub casefold {
 
     use Unicode::UCD 'casespec';
 
-    my %casespec = casespec("09dc");
+    my $casespec = casespec("FB00");
 
 The casespec() returns the potentially locale-dependent case mapping
 of the character specified by a B<code point argument>.  The mapping
@@ -624,7 +624,7 @@ more I<locales> or I<contexts>, separated by spaces (other than as
 used to separate elements, spaces are to be ignored).  A condition
 list overrides the normal behavior if all of the listed conditions are
 true.  Case distinctions in the condition list are not significant.
-Conditions preceded by "NON_" represent the negation of the condition
+Conditions preceded by "NON_" represent the negation of the condition.
 
 Note that when there are multiple case folding definitions for a
 single code point because of different locales, the value returned by
@@ -652,7 +652,8 @@ my %CASESPEC;
 
 sub _casespec {
     unless (%CASESPEC) {
-       if (openunicode(\$CASESPECFH, "SpecCase.txt")) {
+       if (openunicode(\$CASESPECFH, "SpecialCasing.txt")) {
+           local $_;
            while (<$CASESPECFH>) {
                if (/^([0-9A-F]+); ([0-9A-F]+(?: [0-9A-F]+)*)?; ([0-9A-F]+(?: [0-9A-F]+)*)?; ([0-9A-F]+(?: [0-9A-F]+)*)?; (\w+(?: \w+)*)?/) {
                    my ($hexcode, $lower, $title, $upper, $condition) =
@@ -668,9 +669,9 @@ sub _casespec {
                                                           title
                                                           upper
                                                           condition)};
-                           my ($oldlocale) =
+                           if (defined $oldcondition) {
+                               my ($oldlocale) =
                                ($oldcondition =~ /^([a-z][a-z](?:_\S+)?)/);
-                           if (defined $oldlocale) {
                                delete $CASESPEC{$code};
                                $CASESPEC{$code}->{$oldlocale} =
                                { code      => $hexcode,
@@ -678,8 +679,6 @@ sub _casespec {
                                  title     => $oldtitle,
                                  upper     => $oldupper,
                                  condition => $oldcondition };
-                           } else {
-                               warn __PACKAGE__, ": SpecCase.txt:", $., ": No oldlocale for 0x$hexcode\n"
                            }
                        }
                        my ($locale) =
@@ -713,7 +712,7 @@ sub casespec {
 
     _casespec() unless %CASESPEC;
 
-    return $CASESPEC{$code};
+    return ref $CASESPEC{$code} ? dclone $CASESPEC{$code} : $CASESPEC{$code};
 }
 
 =head2 Unicode::UCD::UnicodeVersion