File Coverage

blib/lib/Unicode/Casing.pm
Criterion Covered Total %
statement 53 63 84.1
branch 15 34 44.1
condition 15 18 83.3
subroutine 12 12 100.0
pod 0 2 0.0
total 95 129 73.6


line stmt bran cond sub pod time code
1             package Unicode::Casing; # pod is after __END__ in this file
2              
3             require 5.010; # Because of Perl bugs; can work on earlier Perls with care
4 2     2   3770754 use strict;
  2         4  
  2         55  
5 2     2   7 use warnings;
  2         3  
  2         50  
6 2     2   5 use Carp;
  2         4  
  2         140  
7 2     2   804 use B::Hooks::OP::Check;
  2         2250  
  2         53  
8 2     2   714 use B::Hooks::OP::PPAddr;
  2         741  
  2         191  
9              
10             require Exporter;
11              
12             our @ISA = qw(Exporter);
13              
14             our @EXPORT_OK = ();
15              
16             our @EXPORT = ();
17              
18             our $VERSION = '0.16';
19              
20             require XSLoader;
21             XSLoader::load('Unicode::Casing', $VERSION);
22              
23             # List of references to functions that are overridden by this module
24             # anywhere in the program. Each gets a unique id, which is its index
25             # into this list.
26             my @_function_list;
27              
28             our @recursed;
29             local @recursed;
30              
31             # The way it works is that each function that is overridden has a
32             # reference stored to it in the array. The index in the array to it is
33             # stored in %^H with the key being the name of the overridden function,
34             # like 'uc'. This keeps track of scoping. A .xs function is set up to
35             # intercept calls to the overridden-functions, and it calls _dispatch
36             # with the name of the function which was being called and the string to
37             # change the case of. _dispatch looks up the function name in %^H to
38             # find the index which in turn yields the function reference. If there
39             # is no overridden function, the core one is called instead. (This can
40             # happen when part of the core code processing a call to one of these
41             # functions itself calls a casing function, as happens with Unicode
42             # table look-ups.)
43              
44             my $fc_glob;
45              
46             # This is a work-around, suggested by Matt S. Trout, to the problem that
47             # CORE::fc() is a syntax error on Perls prior to v5.15.8. We have to avoid
48             # compiling that expression on those Perls, but we want the compile-time
49             # version of it on Perls that handle it. Another solution would be to put it
50             # in a sub module that is loaded with a 'use if,'. We want CORE:: to get the
51             # official version. We can't do a string eval or otherwise defer this to
52             # runtime, because by the time _dispatch is called, the op has been replaced,
53             # and we would get infinite recursion.
54             # Actually, I'm not sure the CORE:: is actually needed at all, but am leaving
55             # it in just to be safe.
56             BEGIN {
57 2     2   9 no strict;
  2         2  
  2         59  
58 2 50   2   25 $fc_glob = \*{"CORE::fc"} if $^V ge v5.15.8;
  2         923  
59             }
60              
61             sub _dispatch {
62 92     92   4320 my ($string, $function) = @_;
63              
64             # Called by the XS op-interceptor to look for the correct user-defined
65             # function, and call it.
66             # $string is the scalar whose case is being changed
67             # $function is the generic name, like 'uc', of the case-changing
68             # function.
69              
70 92 50       143 return if ! defined $string;
71              
72             # This is the key that should be stored in the hash hints for this
73             # function if overridden
74 92         103 my $key = id_key($function);
75              
76             # For reasons I don't understand, the intermediate $hints_hash_ref cannot
77             # be skipped; in 5.13.11 anyway.
78 92         401 my $hints_hash_ref = (caller(0))[10];
79              
80 92         125 my $index = $hints_hash_ref->{$key};
81              
82 92 100 100     317 if (! defined $index # Not overridden
83             || defined $recursed[$index])
84             {
85 23 50       93 return CORE::uc($string) if $function eq 'uc';
86 0 0       0 return CORE::lc($string) if $function eq 'lc';
87 0 0       0 return CORE::ucfirst($string) if $function eq 'ucfirst';
88 0 0       0 return CORE::lcfirst($string) if $function eq 'lcfirst';
89 0 0       0 return &$fc_glob($string) if $function eq 'fc';
90             }
91              
92 69         63 local $recursed[$index] = $string;
93              
94             # Force scalar context and returning exactly one value;
95 69         45 my $ret = &{$_function_list[$index]}($string);
  69         105  
96 69         18271 return $ret;
97             }
98              
99             sub setup_key { # key into %^H for value returned from setup();
100 22     22 0 26 return __PACKAGE__ . "_setup_" . shift;
101             }
102              
103             sub id_key { # key into %^H for index into @_function_list
104 104     104 0 170 return __PACKAGE__ . "_id_" . shift;
105             }
106              
107             sub import {
108 5     5   78 shift; # Ignore 'casing' parameter.
109              
110 5         4 my %args;
111              
112 5         23 while (my $function = shift) {
113 12 50       21 return if $function eq '-load';
114 12         9 my $user_sub;
115 12 50       20 if (! defined ($user_sub = shift)) {
116 0         0 croak("Missing CODE reference for $function");
117             }
118 12 50       24 if (ref $user_sub ne 'CODE') {
119 0         0 croak("$user_sub (for $function) is not a CODE reference");
120             }
121 12 50 100     99 if ($function ne 'uc' && $function ne 'lc'
    50 100        
      100        
      33        
      66        
122             && $function ne 'ucfirst' && $function ne 'lcfirst'
123             && ! ($function eq 'fc' && $^V ge v5.15.8))
124             {
125 0         0 my $msg = "$function must be one of: 'uc', 'lc', 'ucfirst', 'lcfirst'";
126 0 0       0 $msg .= ", 'fc'" if $^V ge v5.15.8;
127 0         0 croak($msg);
128             }
129             elsif (exists $args{$function}) {
130 0         0 croak("Only one override for \"$function\" is allowed");
131             }
132 12         17 $args{$function} = 1;
133            
134 12         11 push @_function_list, $user_sub;
135 12         20 $^H{id_key($function)} = scalar @_function_list - 1;
136              
137             # Remove any existing override in the current scope
138 12         16 my $setup_key = setup_key($function);
139 12 100       21 teardown($function, $^H{$setup_key}) if exists $^H{$setup_key};
140              
141             # Save code returned so can tear down upon unimport();
142 12         76 $^H{$setup_key} = setup($function);
143             }
144              
145 5 50       9 croak("Must specify at least one case override") unless %args;
146 5         136 return;
147             }
148              
149             sub unimport {
150 2     2   10 foreach my $function (qw(lc uc lcfirst ucfirst fc)) {
151 10         8 my $id = $^H{setup_key($function)};
152 10 100       23 teardown($function, $id) if defined $id;
153             }
154 2         1094 return;
155             }
156            
157             1;
158             __END__