File Coverage

Char/Ecyrillic.pm
Criterion Covered Total %
statement 51 954 5.3
branch 4 562 0.7
condition 1 180 0.5
subroutine 20 85 23.5
pod 7 50 14.0
total 83 1831 4.5


line stmt bran cond sub pod time code
1             #
2             # This file is *** FOR CPAN USE ONLY ***.
3             #
4             package Char::Ecyrillic;
5             ######################################################################
6             #
7             # Char::Ecyrillic - Run-time routines for Char/Cyrillic.pm
8             #
9             # http://search.cpan.org/dist/Char-Cyrillic/
10             #
11             # Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 INABA Hitoshi
12             ######################################################################
13              
14 177     177   4705 use 5.00503; # Galapagos Consensus 1998 for primetools
  177         641  
  177         9359  
15             # use 5.008001; # Lancaster Consensus 2013 for toolchains
16              
17             # 12.3. Delaying use Until Runtime
18             # in Chapter 12. Packages, Libraries, and Modules
19             # of ISBN 0-596-00313-7 Perl Cookbook, 2nd Edition.
20             # (and so on)
21              
22 177     177   12169 BEGIN { eval q{ use vars qw($VERSION) } }
  177     177   1014  
  177         343  
  177         29902  
23             $VERSION = sprintf '%d.%02d', q$Revision: 1.01 $ =~ /(\d+)/xmsg;
24              
25             BEGIN {
26 177 50   177   1200 if ($^X =~ / jperl /oxmsi) {
27 0         0 die __FILE__, ": needs perl(not jperl) 5.00503 or later. (\$^X==$^X)";
28             }
29 177         377 if (CORE::ord('A') == 193) {
30             die __FILE__, ": is not US-ASCII script (may be EBCDIC or EBCDIK script).";
31             }
32 177         35459 if (CORE::ord('A') != 0x41) {
33             die __FILE__, ": is not US-ASCII script (must be US-ASCII script).";
34             }
35             }
36              
37             BEGIN {
38              
39             # instead of utf8.pm
40 177     177   11627 eval q{
  177     177   1566  
  177     58   355  
  177         30207  
  58         16869  
  66         10562  
  53         9899  
  59         9958  
  64         11684  
  54         9595  
41             no warnings qw(redefine);
42             *utf8::upgrade = sub { CORE::length $_[0] };
43             *utf8::downgrade = sub { 1 };
44             *utf8::encode = sub { };
45             *utf8::decode = sub { 1 };
46             *utf8::is_utf8 = sub { };
47             *utf8::valid = sub { 1 };
48             };
49 177 50       148580 if ($@) {
50 0         0 *utf8::upgrade = sub { CORE::length $_[0] };
  0         0  
51 0         0 *utf8::downgrade = sub { 1 };
  0         0  
52 0         0 *utf8::encode = sub { };
  0         0  
53 0         0 *utf8::decode = sub { 1 };
  0         0  
54 0         0 *utf8::is_utf8 = sub { };
  0         0  
55 0         0 *utf8::valid = sub { 1 };
  0         0  
56             }
57             }
58              
59             # instead of Symbol.pm
60             BEGIN {
61 177     177   442 my $genpkg = "Symbol::";
62 177         9072 my $genseq = 0;
63              
64             sub gensym () {
65 0     0 0 0 my $name = "GEN" . $genseq++;
66              
67             # here, no strict qw(refs); if strict.pm exists
68              
69 0         0 my $ref = \*{$genpkg . $name};
  0         0  
70 0         0 delete $$genpkg{$name};
71 0         0 return $ref;
72             }
73              
74             sub qualify ($;$) {
75 0     0 0 0 my ($name) = @_;
76 0 0 0     0 if (!ref($name) && (Char::Ecyrillic::index($name, '::') == -1) && (Char::Ecyrillic::index($name, "'") == -1)) {
      0        
77 0         0 my $pkg;
78 0         0 my %global = map {$_ => 1} qw(ARGV ARGVOUT ENV INC SIG STDERR STDIN STDOUT DATA);
  0         0  
79              
80             # Global names: special character, "^xyz", or other.
81 0 0 0     0 if ($name =~ /^(([^a-z])|(\^[a-z_]+))\z/i || $global{$name}) {
82             # RGS 2001-11-05 : translate leading ^X to control-char
83 0         0 $name =~ s/^\^([a-z_])/'qq(\c'.$1.')'/eei;
  0         0  
84 0         0 $pkg = "main";
85             }
86             else {
87 0 0       0 $pkg = (@_ > 1) ? $_[1] : caller;
88             }
89 0         0 $name = $pkg . "::" . $name;
90             }
91 0         0 return $name;
92             }
93              
94             sub qualify_to_ref ($;$) {
95              
96             # here, no strict qw(refs); if strict.pm exists
97              
98 0 0   0 0 0 return \*{ qualify $_[0], @_ > 1 ? $_[1] : caller };
  0         0  
99             }
100             }
101              
102             # Column: local $@
103             # in Chapter 9. Osaete okitai Perl no kiso
104             # of ISBN 10: 4798119172 | ISBN 13: 978-4798119175 MODAN Perl NYUMON
105             # (and so on)
106              
107             # use strict; if strict.pm exists
108             BEGIN {
109 177 50   177   790 if (eval { local $@; CORE::require strict }) {
  177         446  
  177         2504  
110 177         35181 strict::->import;
111             }
112             }
113              
114             # P.714 29.2.39. flock
115             # in Chapter 29: Functions
116             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
117              
118             # P.863 flock
119             # in Chapter 27: Functions
120             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
121              
122             sub LOCK_SH() {1}
123             sub LOCK_EX() {2}
124             sub LOCK_UN() {8}
125             sub LOCK_NB() {4}
126              
127             # instead of Carp.pm
128             sub carp;
129             sub croak;
130             sub cluck;
131             sub confess;
132              
133             my $your_char = q{[\x00-\xFF]};
134              
135             # regexp of character
136 177     177   17635 BEGIN { eval q{ use vars qw($q_char) } }
  177     177   1015  
  177         312  
  177         14442  
137             $q_char = qr/$your_char/oxms;
138              
139             #
140             # Cyrillic character range per length
141             #
142             my %range_tr = ();
143              
144             #
145             # alias of encoding name
146             #
147 177     177   11307 BEGIN { eval q{ use vars qw($encoding_alias) } }
  177     177   924  
  177         340  
  177         473264  
148              
149             #
150             # Cyrillic case conversion
151             #
152             my %lc = ();
153             @lc{qw(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)} =
154             qw(a b c d e f g h i j k l m n o p q r s t u v w x y z);
155             my %uc = ();
156             @uc{qw(a b c d e f g h i j k l m n o p q r s t u v w x y z)} =
157             qw(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z);
158             my %fc = ();
159             @fc{qw(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)} =
160             qw(a b c d e f g h i j k l m n o p q r s t u v w x y z);
161              
162             if (0) {
163             }
164              
165             elsif (__PACKAGE__ =~ / \b Ecyrillic \z/oxms) {
166             %range_tr = (
167             1 => [ [0x00..0xFF],
168             ],
169             );
170             $encoding_alias = qr/ \b (?: iso[-_ ]?8859-5 | iec[- ]?8859-5 | cyrillic ) \b /oxmsi;
171              
172             %lc = (%lc,
173             "\xA1" => "\xF1", # CYRILLIC LETTER IO
174             "\xA2" => "\xF2", # CYRILLIC LETTER DJE
175             "\xA3" => "\xF3", # CYRILLIC LETTER GJE
176             "\xA4" => "\xF4", # CYRILLIC LETTER UKRAINIAN IE
177             "\xA5" => "\xF5", # CYRILLIC LETTER DZE
178             "\xA6" => "\xF6", # CYRILLIC LETTER BYELORUSSIAN-UKRAINIAN I
179             "\xA7" => "\xF7", # CYRILLIC LETTER YI
180             "\xA8" => "\xF8", # CYRILLIC LETTER JE
181             "\xA9" => "\xF9", # CYRILLIC LETTER LJE
182             "\xAA" => "\xFA", # CYRILLIC LETTER NJE
183             "\xAB" => "\xFB", # CYRILLIC LETTER TSHE
184             "\xAC" => "\xFC", # CYRILLIC LETTER KJE
185             "\xAE" => "\xFE", # CYRILLIC LETTER SHORT U
186             "\xAF" => "\xFF", # CYRILLIC LETTER DZHE
187             "\xB0" => "\xD0", # CYRILLIC LETTER A
188             "\xB1" => "\xD1", # CYRILLIC LETTER BE
189             "\xB2" => "\xD2", # CYRILLIC LETTER VE
190             "\xB3" => "\xD3", # CYRILLIC LETTER GHE
191             "\xB4" => "\xD4", # CYRILLIC LETTER DE
192             "\xB5" => "\xD5", # CYRILLIC LETTER IE
193             "\xB6" => "\xD6", # CYRILLIC LETTER ZHE
194             "\xB7" => "\xD7", # CYRILLIC LETTER ZE
195             "\xB8" => "\xD8", # CYRILLIC LETTER I
196             "\xB9" => "\xD9", # CYRILLIC LETTER SHORT I
197             "\xBA" => "\xDA", # CYRILLIC LETTER KA
198             "\xBB" => "\xDB", # CYRILLIC LETTER EL
199             "\xBC" => "\xDC", # CYRILLIC LETTER EM
200             "\xBD" => "\xDD", # CYRILLIC LETTER EN
201             "\xBE" => "\xDE", # CYRILLIC LETTER O
202             "\xBF" => "\xDF", # CYRILLIC LETTER PE
203             "\xC0" => "\xE0", # CYRILLIC LETTER ER
204             "\xC1" => "\xE1", # CYRILLIC LETTER ES
205             "\xC2" => "\xE2", # CYRILLIC LETTER TE
206             "\xC3" => "\xE3", # CYRILLIC LETTER U
207             "\xC4" => "\xE4", # CYRILLIC LETTER EF
208             "\xC5" => "\xE5", # CYRILLIC LETTER HA
209             "\xC6" => "\xE6", # CYRILLIC LETTER TSE
210             "\xC7" => "\xE7", # CYRILLIC LETTER CHE
211             "\xC8" => "\xE8", # CYRILLIC LETTER SHA
212             "\xC9" => "\xE9", # CYRILLIC LETTER SHCHA
213             "\xCA" => "\xEA", # CYRILLIC LETTER HARD SIGN
214             "\xCB" => "\xEB", # CYRILLIC LETTER YERU
215             "\xCC" => "\xEC", # CYRILLIC LETTER SOFT SIGN
216             "\xCD" => "\xED", # CYRILLIC LETTER E
217             "\xCE" => "\xEE", # CYRILLIC LETTER YU
218             "\xCF" => "\xEF", # CYRILLIC LETTER YA
219             );
220              
221             %uc = (%uc,
222             "\xD0" => "\xB0", # CYRILLIC LETTER A
223             "\xD1" => "\xB1", # CYRILLIC LETTER BE
224             "\xD2" => "\xB2", # CYRILLIC LETTER VE
225             "\xD3" => "\xB3", # CYRILLIC LETTER GHE
226             "\xD4" => "\xB4", # CYRILLIC LETTER DE
227             "\xD5" => "\xB5", # CYRILLIC LETTER IE
228             "\xD6" => "\xB6", # CYRILLIC LETTER ZHE
229             "\xD7" => "\xB7", # CYRILLIC LETTER ZE
230             "\xD8" => "\xB8", # CYRILLIC LETTER I
231             "\xD9" => "\xB9", # CYRILLIC LETTER SHORT I
232             "\xDA" => "\xBA", # CYRILLIC LETTER KA
233             "\xDB" => "\xBB", # CYRILLIC LETTER EL
234             "\xDC" => "\xBC", # CYRILLIC LETTER EM
235             "\xDD" => "\xBD", # CYRILLIC LETTER EN
236             "\xDE" => "\xBE", # CYRILLIC LETTER O
237             "\xDF" => "\xBF", # CYRILLIC LETTER PE
238             "\xE0" => "\xC0", # CYRILLIC LETTER ER
239             "\xE1" => "\xC1", # CYRILLIC LETTER ES
240             "\xE2" => "\xC2", # CYRILLIC LETTER TE
241             "\xE3" => "\xC3", # CYRILLIC LETTER U
242             "\xE4" => "\xC4", # CYRILLIC LETTER EF
243             "\xE5" => "\xC5", # CYRILLIC LETTER HA
244             "\xE6" => "\xC6", # CYRILLIC LETTER TSE
245             "\xE7" => "\xC7", # CYRILLIC LETTER CHE
246             "\xE8" => "\xC8", # CYRILLIC LETTER SHA
247             "\xE9" => "\xC9", # CYRILLIC LETTER SHCHA
248             "\xEA" => "\xCA", # CYRILLIC LETTER HARD SIGN
249             "\xEB" => "\xCB", # CYRILLIC LETTER YERU
250             "\xEC" => "\xCC", # CYRILLIC LETTER SOFT SIGN
251             "\xED" => "\xCD", # CYRILLIC LETTER E
252             "\xEE" => "\xCE", # CYRILLIC LETTER YU
253             "\xEF" => "\xCF", # CYRILLIC LETTER YA
254             "\xF1" => "\xA1", # CYRILLIC LETTER IO
255             "\xF2" => "\xA2", # CYRILLIC LETTER DJE
256             "\xF3" => "\xA3", # CYRILLIC LETTER GJE
257             "\xF4" => "\xA4", # CYRILLIC LETTER UKRAINIAN IE
258             "\xF5" => "\xA5", # CYRILLIC LETTER DZE
259             "\xF6" => "\xA6", # CYRILLIC LETTER BYELORUSSIAN-UKRAINIAN I
260             "\xF7" => "\xA7", # CYRILLIC LETTER YI
261             "\xF8" => "\xA8", # CYRILLIC LETTER JE
262             "\xF9" => "\xA9", # CYRILLIC LETTER LJE
263             "\xFA" => "\xAA", # CYRILLIC LETTER NJE
264             "\xFB" => "\xAB", # CYRILLIC LETTER TSHE
265             "\xFC" => "\xAC", # CYRILLIC LETTER KJE
266             "\xFE" => "\xAE", # CYRILLIC LETTER SHORT U
267             "\xFF" => "\xAF", # CYRILLIC LETTER DZHE
268             );
269              
270             %fc = (%fc,
271             "\xA1" => "\xF1", # CYRILLIC CAPITAL LETTER IO --> CYRILLIC SMALL LETTER IO
272             "\xA2" => "\xF2", # CYRILLIC CAPITAL LETTER DJE --> CYRILLIC SMALL LETTER DJE
273             "\xA3" => "\xF3", # CYRILLIC CAPITAL LETTER GJE --> CYRILLIC SMALL LETTER GJE
274             "\xA4" => "\xF4", # CYRILLIC CAPITAL LETTER UKRAINIAN IE --> CYRILLIC SMALL LETTER UKRAINIAN IE
275             "\xA5" => "\xF5", # CYRILLIC CAPITAL LETTER DZE --> CYRILLIC SMALL LETTER DZE
276             "\xA6" => "\xF6", # CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I --> CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
277             "\xA7" => "\xF7", # CYRILLIC CAPITAL LETTER YI --> CYRILLIC SMALL LETTER YI
278             "\xA8" => "\xF8", # CYRILLIC CAPITAL LETTER JE --> CYRILLIC SMALL LETTER JE
279             "\xA9" => "\xF9", # CYRILLIC CAPITAL LETTER LJE --> CYRILLIC SMALL LETTER LJE
280             "\xAA" => "\xFA", # CYRILLIC CAPITAL LETTER NJE --> CYRILLIC SMALL LETTER NJE
281             "\xAB" => "\xFB", # CYRILLIC CAPITAL LETTER TSHE --> CYRILLIC SMALL LETTER TSHE
282             "\xAC" => "\xFC", # CYRILLIC CAPITAL LETTER KJE --> CYRILLIC SMALL LETTER KJE
283             "\xAE" => "\xFE", # CYRILLIC CAPITAL LETTER SHORT U --> CYRILLIC SMALL LETTER SHORT U
284             "\xAF" => "\xFF", # CYRILLIC CAPITAL LETTER DZHE --> CYRILLIC SMALL LETTER DZHE
285             "\xB0" => "\xD0", # CYRILLIC CAPITAL LETTER A --> CYRILLIC SMALL LETTER A
286             "\xB1" => "\xD1", # CYRILLIC CAPITAL LETTER BE --> CYRILLIC SMALL LETTER BE
287             "\xB2" => "\xD2", # CYRILLIC CAPITAL LETTER VE --> CYRILLIC SMALL LETTER VE
288             "\xB3" => "\xD3", # CYRILLIC CAPITAL LETTER GHE --> CYRILLIC SMALL LETTER GHE
289             "\xB4" => "\xD4", # CYRILLIC CAPITAL LETTER DE --> CYRILLIC SMALL LETTER DE
290             "\xB5" => "\xD5", # CYRILLIC CAPITAL LETTER IE --> CYRILLIC SMALL LETTER IE
291             "\xB6" => "\xD6", # CYRILLIC CAPITAL LETTER ZHE --> CYRILLIC SMALL LETTER ZHE
292             "\xB7" => "\xD7", # CYRILLIC CAPITAL LETTER ZE --> CYRILLIC SMALL LETTER ZE
293             "\xB8" => "\xD8", # CYRILLIC CAPITAL LETTER I --> CYRILLIC SMALL LETTER I
294             "\xB9" => "\xD9", # CYRILLIC CAPITAL LETTER SHORT I --> CYRILLIC SMALL LETTER SHORT I
295             "\xBA" => "\xDA", # CYRILLIC CAPITAL LETTER KA --> CYRILLIC SMALL LETTER KA
296             "\xBB" => "\xDB", # CYRILLIC CAPITAL LETTER EL --> CYRILLIC SMALL LETTER EL
297             "\xBC" => "\xDC", # CYRILLIC CAPITAL LETTER EM --> CYRILLIC SMALL LETTER EM
298             "\xBD" => "\xDD", # CYRILLIC CAPITAL LETTER EN --> CYRILLIC SMALL LETTER EN
299             "\xBE" => "\xDE", # CYRILLIC CAPITAL LETTER O --> CYRILLIC SMALL LETTER O
300             "\xBF" => "\xDF", # CYRILLIC CAPITAL LETTER PE --> CYRILLIC SMALL LETTER PE
301             "\xC0" => "\xE0", # CYRILLIC CAPITAL LETTER ER --> CYRILLIC SMALL LETTER ER
302             "\xC1" => "\xE1", # CYRILLIC CAPITAL LETTER ES --> CYRILLIC SMALL LETTER ES
303             "\xC2" => "\xE2", # CYRILLIC CAPITAL LETTER TE --> CYRILLIC SMALL LETTER TE
304             "\xC3" => "\xE3", # CYRILLIC CAPITAL LETTER U --> CYRILLIC SMALL LETTER U
305             "\xC4" => "\xE4", # CYRILLIC CAPITAL LETTER EF --> CYRILLIC SMALL LETTER EF
306             "\xC5" => "\xE5", # CYRILLIC CAPITAL LETTER HA --> CYRILLIC SMALL LETTER HA
307             "\xC6" => "\xE6", # CYRILLIC CAPITAL LETTER TSE --> CYRILLIC SMALL LETTER TSE
308             "\xC7" => "\xE7", # CYRILLIC CAPITAL LETTER CHE --> CYRILLIC SMALL LETTER CHE
309             "\xC8" => "\xE8", # CYRILLIC CAPITAL LETTER SHA --> CYRILLIC SMALL LETTER SHA
310             "\xC9" => "\xE9", # CYRILLIC CAPITAL LETTER SHCHA --> CYRILLIC SMALL LETTER SHCHA
311             "\xCA" => "\xEA", # CYRILLIC CAPITAL LETTER HARD SIGN --> CYRILLIC SMALL LETTER HARD SIGN
312             "\xCB" => "\xEB", # CYRILLIC CAPITAL LETTER YERU --> CYRILLIC SMALL LETTER YERU
313             "\xCC" => "\xEC", # CYRILLIC CAPITAL LETTER SOFT SIGN --> CYRILLIC SMALL LETTER SOFT SIGN
314             "\xCD" => "\xED", # CYRILLIC CAPITAL LETTER E --> CYRILLIC SMALL LETTER E
315             "\xCE" => "\xEE", # CYRILLIC CAPITAL LETTER YU --> CYRILLIC SMALL LETTER YU
316             "\xCF" => "\xEF", # CYRILLIC CAPITAL LETTER YA --> CYRILLIC SMALL LETTER YA
317             );
318             }
319              
320             else {
321             croak "Don't know my package name '@{[__PACKAGE__]}'";
322             }
323              
324             #
325             # @ARGV wildcard globbing
326             #
327             sub import {
328              
329 0 0   0   0 if ($^O =~ /\A (?: MSWin32 | NetWare | symbian | dos ) \z/oxms) {
330 0         0 my @argv = ();
331 0         0 for (@ARGV) {
332              
333             # has space
334 0 0       0 if (/\A (?:$q_char)*? [ ] /oxms) {
    0          
335 0 0       0 if (my @glob = Char::Ecyrillic::glob(qq{"$_"})) {
336 0         0 push @argv, @glob;
337             }
338             else {
339 0         0 push @argv, $_;
340             }
341             }
342              
343             # has wildcard metachar
344             elsif (/\A (?:$q_char)*? [*?] /oxms) {
345 0 0       0 if (my @glob = Char::Ecyrillic::glob($_)) {
346 0         0 push @argv, @glob;
347             }
348             else {
349 0         0 push @argv, $_;
350             }
351             }
352              
353             # no wildcard globbing
354             else {
355 0         0 push @argv, $_;
356             }
357             }
358 0         0 @ARGV = @argv;
359             }
360             }
361              
362             # P.230 Care with Prototypes
363             # in Chapter 6: Subroutines
364             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
365             #
366             # If you aren't careful, you can get yourself into trouble with prototypes.
367             # But if you are careful, you can do a lot of neat things with them. This is
368             # all very powerful, of course, and should only be used in moderation to make
369             # the world a better place.
370              
371             # P.332 Care with Prototypes
372             # in Chapter 7: Subroutines
373             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
374             #
375             # If you aren't careful, you can get yourself into trouble with prototypes.
376             # But if you are careful, you can do a lot of neat things with them. This is
377             # all very powerful, of course, and should only be used in moderation to make
378             # the world a better place.
379              
380             #
381             # Prototypes of subroutines
382             #
383 0     0   0 sub unimport {}
384             sub Char::Ecyrillic::split(;$$$);
385             sub Char::Ecyrillic::tr($$$$;$);
386             sub Char::Ecyrillic::chop(@);
387             sub Char::Ecyrillic::index($$;$);
388             sub Char::Ecyrillic::rindex($$;$);
389             sub Char::Ecyrillic::lcfirst(@);
390             sub Char::Ecyrillic::lcfirst_();
391             sub Char::Ecyrillic::lc(@);
392             sub Char::Ecyrillic::lc_();
393             sub Char::Ecyrillic::ucfirst(@);
394             sub Char::Ecyrillic::ucfirst_();
395             sub Char::Ecyrillic::uc(@);
396             sub Char::Ecyrillic::uc_();
397             sub Char::Ecyrillic::fc(@);
398             sub Char::Ecyrillic::fc_();
399             sub Char::Ecyrillic::ignorecase;
400             sub Char::Ecyrillic::classic_character_class;
401             sub Char::Ecyrillic::capture;
402             sub Char::Ecyrillic::chr(;$);
403             sub Char::Ecyrillic::chr_();
404             sub Char::Ecyrillic::glob($);
405             sub Char::Ecyrillic::glob_();
406              
407             sub Char::Cyrillic::ord(;$);
408             sub Char::Cyrillic::ord_();
409             sub Char::Cyrillic::reverse(@);
410             sub Char::Cyrillic::getc(;*@);
411             sub Char::Cyrillic::length(;$);
412             sub Char::Cyrillic::substr($$;$$);
413             sub Char::Cyrillic::index($$;$);
414             sub Char::Cyrillic::rindex($$;$);
415              
416             #
417             # Regexp work
418             #
419 177     177   16388 BEGIN { eval q{ use vars qw(
  177     177   1280  
  177         344  
  177         110735  
420             $Char::Cyrillic::re_a
421             $Char::Cyrillic::re_t
422             $Char::Cyrillic::re_n
423             $Char::Cyrillic::re_r
424             ) } }
425              
426             #
427             # Character class
428             #
429 177     177   14673 BEGIN { eval q{ use vars qw(
  177     177   1074  
  177         342  
  177         3344822  
430             $dot
431             $dot_s
432             $eD
433             $eS
434             $eW
435             $eH
436             $eV
437             $eR
438             $eN
439             $not_alnum
440             $not_alpha
441             $not_ascii
442             $not_blank
443             $not_cntrl
444             $not_digit
445             $not_graph
446             $not_lower
447             $not_lower_i
448             $not_print
449             $not_punct
450             $not_space
451             $not_upper
452             $not_upper_i
453             $not_word
454             $not_xdigit
455             $eb
456             $eB
457             ) } }
458              
459             ${Char::Ecyrillic::dot} = qr{(?:[^\x0A])};
460             ${Char::Ecyrillic::dot_s} = qr{(?:[\x00-\xFF])};
461             ${Char::Ecyrillic::eD} = qr{(?:[^0-9])};
462              
463             # Vertical tabs are now whitespace
464             # \s in a regex now matches a vertical tab in all circumstances.
465             # http://search.cpan.org/dist/perl-5.18.0/pod/perldelta.pod#Vertical_tabs_are_now_whitespace
466             # ${Char::Ecyrillic::eS} = qr{(?:[^\x09\x0A \x0C\x0D\x20])};
467             # ${Char::Ecyrillic::eS} = qr{(?:[^\x09\x0A\x0B\x0C\x0D\x20])};
468             ${Char::Ecyrillic::eS} = qr{(?:[^\s])};
469              
470             ${Char::Ecyrillic::eW} = qr{(?:[^0-9A-Z_a-z])};
471             ${Char::Ecyrillic::eH} = qr{(?:[^\x09\x20])};
472             ${Char::Ecyrillic::eV} = qr{(?:[^\x0A\x0B\x0C\x0D])};
473             ${Char::Ecyrillic::eR} = qr{(?:\x0D\x0A|[\x0A\x0D])};
474             ${Char::Ecyrillic::eN} = qr{(?:[^\x0A])};
475             ${Char::Ecyrillic::not_alnum} = qr{(?:[^\x30-\x39\x41-\x5A\x61-\x7A])};
476             ${Char::Ecyrillic::not_alpha} = qr{(?:[^\x41-\x5A\x61-\x7A])};
477             ${Char::Ecyrillic::not_ascii} = qr{(?:[^\x00-\x7F])};
478             ${Char::Ecyrillic::not_blank} = qr{(?:[^\x09\x20])};
479             ${Char::Ecyrillic::not_cntrl} = qr{(?:[^\x00-\x1F\x7F])};
480             ${Char::Ecyrillic::not_digit} = qr{(?:[^\x30-\x39])};
481             ${Char::Ecyrillic::not_graph} = qr{(?:[^\x21-\x7F])};
482             ${Char::Ecyrillic::not_lower} = qr{(?:[^\x61-\x7A])};
483             ${Char::Ecyrillic::not_lower_i} = qr{(?:[\x00-\xFF])};
484             ${Char::Ecyrillic::not_print} = qr{(?:[^\x20-\x7F])};
485             ${Char::Ecyrillic::not_punct} = qr{(?:[^\x21-\x2F\x3A-\x3F\x40\x5B-\x5F\x60\x7B-\x7E])};
486             ${Char::Ecyrillic::not_space} = qr{(?:[^\s\x0B])};
487             ${Char::Ecyrillic::not_upper} = qr{(?:[^\x41-\x5A])};
488             ${Char::Ecyrillic::not_upper_i} = qr{(?:[\x00-\xFF])};
489             ${Char::Ecyrillic::not_word} = qr{(?:[^\x30-\x39\x41-\x5A\x5F\x61-\x7A])};
490             ${Char::Ecyrillic::not_xdigit} = qr{(?:[^\x30-\x39\x41-\x46\x61-\x66])};
491             ${Char::Ecyrillic::eb} = qr{(?:\A(?=[0-9A-Z_a-z])|(?<=[\x00-\x2F\x40\x5B-\x5E\x60\x7B-\xFF])(?=[0-9A-Z_a-z])|(?<=[0-9A-Z_a-z])(?=[\x00-\x2F\x40\x5B-\x5E\x60\x7B-\xFF]|\z))};
492             ${Char::Ecyrillic::eB} = qr{(?:(?<=[0-9A-Z_a-z])(?=[0-9A-Z_a-z])|(?<=[\x00-\x2F\x40\x5B-\x5E\x60\x7B-\xFF])(?=[\x00-\x2F\x40\x5B-\x5E\x60\x7B-\xFF]))};
493              
494             # avoid: Name "Char::Ecyrillic::foo" used only once: possible typo at here.
495             ${Char::Ecyrillic::dot} = ${Char::Ecyrillic::dot};
496             ${Char::Ecyrillic::dot_s} = ${Char::Ecyrillic::dot_s};
497             ${Char::Ecyrillic::eD} = ${Char::Ecyrillic::eD};
498             ${Char::Ecyrillic::eS} = ${Char::Ecyrillic::eS};
499             ${Char::Ecyrillic::eW} = ${Char::Ecyrillic::eW};
500             ${Char::Ecyrillic::eH} = ${Char::Ecyrillic::eH};
501             ${Char::Ecyrillic::eV} = ${Char::Ecyrillic::eV};
502             ${Char::Ecyrillic::eR} = ${Char::Ecyrillic::eR};
503             ${Char::Ecyrillic::eN} = ${Char::Ecyrillic::eN};
504             ${Char::Ecyrillic::not_alnum} = ${Char::Ecyrillic::not_alnum};
505             ${Char::Ecyrillic::not_alpha} = ${Char::Ecyrillic::not_alpha};
506             ${Char::Ecyrillic::not_ascii} = ${Char::Ecyrillic::not_ascii};
507             ${Char::Ecyrillic::not_blank} = ${Char::Ecyrillic::not_blank};
508             ${Char::Ecyrillic::not_cntrl} = ${Char::Ecyrillic::not_cntrl};
509             ${Char::Ecyrillic::not_digit} = ${Char::Ecyrillic::not_digit};
510             ${Char::Ecyrillic::not_graph} = ${Char::Ecyrillic::not_graph};
511             ${Char::Ecyrillic::not_lower} = ${Char::Ecyrillic::not_lower};
512             ${Char::Ecyrillic::not_lower_i} = ${Char::Ecyrillic::not_lower_i};
513             ${Char::Ecyrillic::not_print} = ${Char::Ecyrillic::not_print};
514             ${Char::Ecyrillic::not_punct} = ${Char::Ecyrillic::not_punct};
515             ${Char::Ecyrillic::not_space} = ${Char::Ecyrillic::not_space};
516             ${Char::Ecyrillic::not_upper} = ${Char::Ecyrillic::not_upper};
517             ${Char::Ecyrillic::not_upper_i} = ${Char::Ecyrillic::not_upper_i};
518             ${Char::Ecyrillic::not_word} = ${Char::Ecyrillic::not_word};
519             ${Char::Ecyrillic::not_xdigit} = ${Char::Ecyrillic::not_xdigit};
520             ${Char::Ecyrillic::eb} = ${Char::Ecyrillic::eb};
521             ${Char::Ecyrillic::eB} = ${Char::Ecyrillic::eB};
522              
523             #
524             # Cyrillic split
525             #
526             sub Char::Ecyrillic::split(;$$$) {
527              
528             # P.794 29.2.161. split
529             # in Chapter 29: Functions
530             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
531              
532             # P.951 split
533             # in Chapter 27: Functions
534             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
535              
536 0     0 0 0 my $pattern = $_[0];
537 0         0 my $string = $_[1];
538 0         0 my $limit = $_[2];
539              
540             # if $pattern is also omitted or is the literal space, " "
541 0 0       0 if (not defined $pattern) {
542 0         0 $pattern = ' ';
543             }
544              
545             # if $string is omitted, the function splits the $_ string
546 0 0       0 if (not defined $string) {
547 0 0       0 if (defined $_) {
548 0         0 $string = $_;
549             }
550             else {
551 0         0 $string = '';
552             }
553             }
554              
555 0         0 my @split = ();
556              
557             # when string is empty
558 0 0       0 if ($string eq '') {
    0          
559              
560             # resulting list value in list context
561 0 0       0 if (wantarray) {
562 0         0 return @split;
563             }
564              
565             # count of substrings in scalar context
566             else {
567 0 0       0 carp "Use of implicit split to \@_ is deprecated" if $^W;
568 0         0 @_ = @split;
569 0         0 return scalar @_;
570             }
571             }
572              
573             # split's first argument is more consistently interpreted
574             #
575             # After some changes earlier in v5.17, split's behavior has been simplified:
576             # if the PATTERN argument evaluates to a string containing one space, it is
577             # treated the way that a literal string containing one space once was.
578             # http://search.cpan.org/dist/perl-5.18.0/pod/perldelta.pod#split's_first_argument_is_more_consistently_interpreted
579              
580             # if $pattern is also omitted or is the literal space, " ", the function splits
581             # on whitespace, /\s+/, after skipping any leading whitespace
582             # (and so on)
583              
584             elsif ($pattern eq ' ') {
585 0 0       0 if (not defined $limit) {
586 0         0 return CORE::split(' ', $string);
587             }
588             else {
589 0         0 return CORE::split(' ', $string, $limit);
590             }
591             }
592              
593             # if $limit is negative, it is treated as if an arbitrarily large $limit has been specified
594 0 0 0     0 if ((not defined $limit) or ($limit <= 0)) {
    0          
595              
596             # a pattern capable of matching either the null string or something longer than the
597             # null string will split the value of $string into separate characters wherever it
598             # matches the null string between characters
599             # (and so on)
600              
601 0 0       0 if ('' =~ / \A $pattern \z /xms) {
602 0         0 my $last_subexpression_offsets = _last_subexpression_offsets($pattern);
603 0         0 my $limit = scalar(() = $string =~ /($pattern)/oxmsg);
604              
605             # P.1024 Appendix W.10 Multibyte Processing
606             # of ISBN 1-56592-224-7 CJKV Information Processing
607             # (and so on)
608              
609             # the //m modifier is assumed when you split on the pattern /^/
610             # (and so on)
611              
612             # V
613 0   0     0 while ((--$limit > 0) and ($string =~ s/\A((?:$q_char)+?)$pattern//m)) {
614              
615             # if the $pattern contains parentheses, then the substring matched by each pair of parentheses
616             # is included in the resulting list, interspersed with the fields that are ordinarily returned
617             # (and so on)
618              
619 0         0 local $@;
620 0         0 for (my $digit=1; $digit <= ($last_subexpression_offsets + 1); $digit++) {
621 0         0 push @split, eval('$' . $digit);
622             }
623             }
624             }
625              
626             else {
627 0         0 my $last_subexpression_offsets = _last_subexpression_offsets($pattern);
628              
629             # V
630 0         0 while ($string =~ s/\A((?:$q_char)*?)$pattern//m) {
631 0         0 local $@;
632 0         0 for (my $digit=1; $digit <= ($last_subexpression_offsets + 1); $digit++) {
633 0         0 push @split, eval('$' . $digit);
634             }
635             }
636             }
637             }
638              
639             elsif ($limit > 0) {
640 0 0       0 if ('' =~ / \A $pattern \z /xms) {
641 0         0 my $last_subexpression_offsets = _last_subexpression_offsets($pattern);
642 0   0     0 while ((--$limit > 0) and (CORE::length($string) > 0)) {
643              
644             # V
645 0 0       0 if ($string =~ s/\A((?:$q_char)+?)$pattern//m) {
646 0         0 local $@;
647 0         0 for (my $digit=1; $digit <= ($last_subexpression_offsets + 1); $digit++) {
648 0         0 push @split, eval('$' . $digit);
649             }
650             }
651             }
652             }
653             else {
654 0         0 my $last_subexpression_offsets = _last_subexpression_offsets($pattern);
655 0   0     0 while ((--$limit > 0) and (CORE::length($string) > 0)) {
656              
657             # V
658 0 0       0 if ($string =~ s/\A((?:$q_char)*?)$pattern//m) {
659 0         0 local $@;
660 0         0 for (my $digit=1; $digit <= ($last_subexpression_offsets + 1); $digit++) {
661 0         0 push @split, eval('$' . $digit);
662             }
663             }
664             }
665             }
666             }
667              
668 0 0       0 if (CORE::length($string) > 0) {
669 0         0 push @split, $string;
670             }
671              
672             # if $_[2] (NOT "$limit") is omitted or zero, trailing null fields are stripped from the result
673 0 0 0     0 if ((not defined $_[2]) or ($_[2] == 0)) {
674 0   0     0 while ((scalar(@split) >= 1) and ($split[-1] eq '')) {
675 0         0 pop @split;
676             }
677             }
678              
679             # resulting list value in list context
680 0 0       0 if (wantarray) {
681 0         0 return @split;
682             }
683              
684             # count of substrings in scalar context
685             else {
686 0 0       0 carp "Use of implicit split to \@_ is deprecated" if $^W;
687 0         0 @_ = @split;
688 0         0 return scalar @_;
689             }
690             }
691              
692             #
693             # get last subexpression offsets
694             #
695             sub _last_subexpression_offsets {
696 0     0   0 my $pattern = $_[0];
697              
698             # remove comment
699 0         0 $pattern =~ s/\(\?\# .*? \)//oxmsg;
700              
701 0         0 my $modifier = '';
702 0 0       0 if ($pattern =~ /\(\?\^? ([\-A-Za-z]+) :/oxms) {
703 0         0 $modifier = $1;
704 0         0 $modifier =~ s/-[A-Za-z]*//;
705             }
706              
707             # with /x modifier
708 0         0 my @char = ();
709 0 0       0 if ($modifier =~ /x/oxms) {
710 0         0 @char = $pattern =~ /\G(
711             \\ (?:$q_char) |
712             \# (?:$q_char)*? $ |
713             \[ (?: \\\] | (?:$q_char))+? \] |
714             \(\? |
715             (?:$q_char)
716             )/oxmsg;
717             }
718              
719             # without /x modifier
720             else {
721 0         0 @char = $pattern =~ /\G(
722             \\ (?:$q_char) |
723             \[ (?: \\\] | (?:$q_char))+? \] |
724             \(\? |
725             (?:$q_char)
726             )/oxmsg;
727             }
728              
729 0         0 return scalar grep { $_ eq '(' } @char;
  0         0  
730             }
731              
732             #
733             # Cyrillic transliteration (tr///)
734             #
735             sub Char::Ecyrillic::tr($$$$;$) {
736              
737 0     0 0 0 my $bind_operator = $_[1];
738 0         0 my $searchlist = $_[2];
739 0         0 my $replacementlist = $_[3];
740 0   0     0 my $modifier = $_[4] || '';
741              
742 0 0       0 if ($modifier =~ /r/oxms) {
743 0 0       0 if ($bind_operator =~ / !~ /oxms) {
744 0         0 croak "Using !~ with tr///r doesn't make sense";
745             }
746             }
747              
748 0         0 my @char = $_[0] =~ /\G ($q_char) /oxmsg;
749 0         0 my @searchlist = _charlist_tr($searchlist);
750 0         0 my @replacementlist = _charlist_tr($replacementlist);
751              
752 0         0 my %tr = ();
753 0         0 for (my $i=0; $i <= $#searchlist; $i++) {
754 0 0       0 if (not exists $tr{$searchlist[$i]}) {
755 0 0 0     0 if (defined $replacementlist[$i] and ($replacementlist[$i] ne '')) {
    0 0        
    0          
756 0         0 $tr{$searchlist[$i]} = $replacementlist[$i];
757             }
758             elsif ($modifier =~ /d/oxms) {
759 0         0 $tr{$searchlist[$i]} = '';
760             }
761             elsif (defined $replacementlist[-1] and ($replacementlist[-1] ne '')) {
762 0         0 $tr{$searchlist[$i]} = $replacementlist[-1];
763             }
764             else {
765 0         0 $tr{$searchlist[$i]} = $searchlist[$i];
766             }
767             }
768             }
769              
770 0         0 my $tr = 0;
771 0         0 my $replaced = '';
772 0 0       0 if ($modifier =~ /c/oxms) {
773 0         0 while (defined(my $char = shift @char)) {
774 0 0       0 if (not exists $tr{$char}) {
775 0 0       0 if (defined $replacementlist[0]) {
776 0         0 $replaced .= $replacementlist[0];
777             }
778 0         0 $tr++;
779 0 0       0 if ($modifier =~ /s/oxms) {
780 0   0     0 while (@char and (not exists $tr{$char[0]})) {
781 0         0 shift @char;
782 0         0 $tr++;
783             }
784             }
785             }
786             else {
787 0         0 $replaced .= $char;
788             }
789             }
790             }
791             else {
792 0         0 while (defined(my $char = shift @char)) {
793 0 0       0 if (exists $tr{$char}) {
794 0         0 $replaced .= $tr{$char};
795 0         0 $tr++;
796 0 0       0 if ($modifier =~ /s/oxms) {
797 0   0     0 while (@char and (exists $tr{$char[0]}) and ($tr{$char[0]} eq $tr{$char})) {
      0        
798 0         0 shift @char;
799 0         0 $tr++;
800             }
801             }
802             }
803             else {
804 0         0 $replaced .= $char;
805             }
806             }
807             }
808              
809 0 0       0 if ($modifier =~ /r/oxms) {
810 0         0 return $replaced;
811             }
812             else {
813 0         0 $_[0] = $replaced;
814 0 0       0 if ($bind_operator =~ / !~ /oxms) {
815 0         0 return not $tr;
816             }
817             else {
818 0         0 return $tr;
819             }
820             }
821             }
822              
823             #
824             # Cyrillic chop
825             #
826             sub Char::Ecyrillic::chop(@) {
827              
828 0     0 0 0 my $chop;
829 0 0       0 if (@_ == 0) {
830 0         0 my @char = /\G ($q_char) /oxmsg;
831 0         0 $chop = pop @char;
832 0         0 $_ = join '', @char;
833             }
834             else {
835 0         0 for (@_) {
836 0         0 my @char = /\G ($q_char) /oxmsg;
837 0         0 $chop = pop @char;
838 0         0 $_ = join '', @char;
839             }
840             }
841 0         0 return $chop;
842             }
843              
844             #
845             # Cyrillic index by octet
846             #
847             sub Char::Ecyrillic::index($$;$) {
848              
849 0     0 1 0 my($str,$substr,$position) = @_;
850 0   0     0 $position ||= 0;
851 0         0 my $pos = 0;
852              
853 0         0 while ($pos < CORE::length($str)) {
854 0 0       0 if (CORE::substr($str,$pos,CORE::length($substr)) eq $substr) {
855 0 0       0 if ($pos >= $position) {
856 0         0 return $pos;
857             }
858             }
859 0 0       0 if (CORE::substr($str,$pos) =~ /\A ($q_char) /oxms) {
860 0         0 $pos += CORE::length($1);
861             }
862             else {
863 0         0 $pos += 1;
864             }
865             }
866 0         0 return -1;
867             }
868              
869             #
870             # Cyrillic reverse index
871             #
872             sub Char::Ecyrillic::rindex($$;$) {
873              
874 0     0 0 0 my($str,$substr,$position) = @_;
875 0   0     0 $position ||= CORE::length($str) - 1;
876 0         0 my $pos = 0;
877 0         0 my $rindex = -1;
878              
879 0   0     0 while (($pos < CORE::length($str)) and ($pos <= $position)) {
880 0 0       0 if (CORE::substr($str,$pos,CORE::length($substr)) eq $substr) {
881 0         0 $rindex = $pos;
882             }
883 0 0       0 if (CORE::substr($str,$pos) =~ /\A ($q_char) /oxms) {
884 0         0 $pos += CORE::length($1);
885             }
886             else {
887 0         0 $pos += 1;
888             }
889             }
890 0         0 return $rindex;
891             }
892              
893             #
894             # Cyrillic lower case first with parameter
895             #
896             sub Char::Ecyrillic::lcfirst(@) {
897 0 0   0 0 0 if (@_) {
898 0         0 my $s = shift @_;
899 0 0 0     0 if (@_ and wantarray) {
900 0         0 return Char::Ecyrillic::lc(CORE::substr($s,0,1)) . CORE::substr($s,1), @_;
901             }
902             else {
903 0         0 return Char::Ecyrillic::lc(CORE::substr($s,0,1)) . CORE::substr($s,1);
904             }
905             }
906             else {
907 0         0 return Char::Ecyrillic::lc(CORE::substr($_,0,1)) . CORE::substr($_,1);
908             }
909             }
910              
911             #
912             # Cyrillic lower case first without parameter
913             #
914             sub Char::Ecyrillic::lcfirst_() {
915 0     0 0 0 return Char::Ecyrillic::lc(CORE::substr($_,0,1)) . CORE::substr($_,1);
916             }
917              
918             #
919             # Cyrillic lower case with parameter
920             #
921             sub Char::Ecyrillic::lc(@) {
922 0 0   0 0 0 if (@_) {
923 0         0 my $s = shift @_;
924 0 0 0     0 if (@_ and wantarray) {
925 0 0       0 return join('', map {defined($lc{$_}) ? $lc{$_} : $_} ($s =~ /\G ($q_char) /oxmsg)), @_;
  0         0  
926             }
927             else {
928 0 0       0 return join('', map {defined($lc{$_}) ? $lc{$_} : $_} ($s =~ /\G ($q_char) /oxmsg));
  0         0  
929             }
930             }
931             else {
932 0         0 return Char::Ecyrillic::lc_();
933             }
934             }
935              
936             #
937             # Cyrillic lower case without parameter
938             #
939             sub Char::Ecyrillic::lc_() {
940 0     0 0 0 my $s = $_;
941 0 0       0 return join '', map {defined($lc{$_}) ? $lc{$_} : $_} ($s =~ /\G ($q_char) /oxmsg);
  0         0  
942             }
943              
944             #
945             # Cyrillic upper case first with parameter
946             #
947             sub Char::Ecyrillic::ucfirst(@) {
948 0 0   0 0 0 if (@_) {
949 0         0 my $s = shift @_;
950 0 0 0     0 if (@_ and wantarray) {
951 0         0 return Char::Ecyrillic::uc(CORE::substr($s,0,1)) . CORE::substr($s,1), @_;
952             }
953             else {
954 0         0 return Char::Ecyrillic::uc(CORE::substr($s,0,1)) . CORE::substr($s,1);
955             }
956             }
957             else {
958 0         0 return Char::Ecyrillic::uc(CORE::substr($_,0,1)) . CORE::substr($_,1);
959             }
960             }
961              
962             #
963             # Cyrillic upper case first without parameter
964             #
965             sub Char::Ecyrillic::ucfirst_() {
966 0     0 0 0 return Char::Ecyrillic::uc(CORE::substr($_,0,1)) . CORE::substr($_,1);
967             }
968              
969             #
970             # Cyrillic upper case with parameter
971             #
972             sub Char::Ecyrillic::uc(@) {
973 0 0   0 0 0 if (@_) {
974 0         0 my $s = shift @_;
975 0 0 0     0 if (@_ and wantarray) {
976 0 0       0 return join('', map {defined($uc{$_}) ? $uc{$_} : $_} ($s =~ /\G ($q_char) /oxmsg)), @_;
  0         0  
977             }
978             else {
979 0 0       0 return join('', map {defined($uc{$_}) ? $uc{$_} : $_} ($s =~ /\G ($q_char) /oxmsg));
  0         0  
980             }
981             }
982             else {
983 0         0 return Char::Ecyrillic::uc_();
984             }
985             }
986              
987             #
988             # Cyrillic upper case without parameter
989             #
990             sub Char::Ecyrillic::uc_() {
991 0     0 0 0 my $s = $_;
992 0 0       0 return join '', map {defined($uc{$_}) ? $uc{$_} : $_} ($s =~ /\G ($q_char) /oxmsg);
  0         0  
993             }
994              
995             #
996             # Cyrillic fold case with parameter
997             #
998             sub Char::Ecyrillic::fc(@) {
999 0 0   0 0 0 if (@_) {
1000 0         0 my $s = shift @_;
1001 0 0 0     0 if (@_ and wantarray) {
1002 0 0       0 return join('', map {defined($fc{$_}) ? $fc{$_} : $_} ($s =~ /\G ($q_char) /oxmsg)), @_;
  0         0  
1003             }
1004             else {
1005 0 0       0 return join('', map {defined($fc{$_}) ? $fc{$_} : $_} ($s =~ /\G ($q_char) /oxmsg));
  0         0  
1006             }
1007             }
1008             else {
1009 0         0 return Char::Ecyrillic::fc_();
1010             }
1011             }
1012              
1013             #
1014             # Cyrillic fold case without parameter
1015             #
1016             sub Char::Ecyrillic::fc_() {
1017 0     0 0 0 my $s = $_;
1018 0 0       0 return join '', map {defined($fc{$_}) ? $fc{$_} : $_} ($s =~ /\G ($q_char) /oxmsg);
  0         0  
1019             }
1020              
1021             #
1022             # Cyrillic regexp capture
1023             #
1024             {
1025             sub Char::Ecyrillic::capture {
1026 0     0 1 0 return $_[0];
1027             }
1028             }
1029              
1030             #
1031             # Cyrillic regexp ignore case modifier
1032             #
1033             sub Char::Ecyrillic::ignorecase {
1034              
1035 0     0 0 0 my @string = @_;
1036 0         0 my $metachar = qr/[\@\\|[\]{]/oxms;
1037              
1038             # ignore case of $scalar or @array
1039 0         0 for my $string (@string) {
1040              
1041             # split regexp
1042 0         0 my @char = $string =~ /\G(
1043             \[\^ |
1044             \\? (?:$q_char)
1045             )/oxmsg;
1046              
1047             # unescape character
1048 0         0 for (my $i=0; $i <= $#char; $i++) {
1049 0 0       0 next if not defined $char[$i];
1050              
1051             # open character class [...]
1052 0 0       0 if ($char[$i] eq '[') {
    0          
    0          
    0          
1053 0         0 my $left = $i;
1054              
1055             # [] make die "unmatched [] in regexp ..."
1056              
1057 0 0       0 if ($char[$i+1] eq ']') {
1058 0         0 $i++;
1059             }
1060              
1061 0         0 while (1) {
1062 0 0       0 if (++$i > $#char) {
1063 0         0 croak "Unmatched [] in regexp";
1064             }
1065 0 0       0 if ($char[$i] eq ']') {
1066 0         0 my $right = $i;
1067 0         0 my @charlist = charlist_qr(@char[$left+1..$right-1], 'i');
1068              
1069             # escape character
1070 0         0 for my $char (@charlist) {
1071 0 0       0 if (0) {
1072             }
1073              
1074 0         0 elsif ($char =~ /\A [.|)] \z/oxms) {
1075 0         0 $char = $1 . '\\' . $char;
1076             }
1077             }
1078              
1079             # [...]
1080 0         0 splice @char, $left, $right-$left+1, '(?:' . join('|', @charlist) . ')';
1081              
1082 0         0 $i = $left;
1083 0         0 last;
1084             }
1085             }
1086             }
1087              
1088             # open character class [^...]
1089             elsif ($char[$i] eq '[^') {
1090 0         0 my $left = $i;
1091              
1092             # [^] make die "unmatched [] in regexp ..."
1093              
1094 0 0       0 if ($char[$i+1] eq ']') {
1095 0         0 $i++;
1096             }
1097              
1098 0         0 while (1) {
1099 0 0       0 if (++$i > $#char) {
1100 0         0 croak "Unmatched [] in regexp";
1101             }
1102 0 0       0 if ($char[$i] eq ']') {
1103 0         0 my $right = $i;
1104 0         0 my @charlist = charlist_not_qr(@char[$left+1..$right-1], 'i');
1105              
1106             # escape character
1107 0         0 for my $char (@charlist) {
1108 0 0       0 if (0) {
1109             }
1110              
1111 0         0 elsif ($char =~ /\A [.|)] \z/oxms) {
1112 0         0 $char = '\\' . $char;
1113             }
1114             }
1115              
1116             # [^...]
1117 0         0 splice @char, $left, $right-$left+1, '(?!' . join('|', @charlist) . ")(?:$your_char)";
1118              
1119 0         0 $i = $left;
1120 0         0 last;
1121             }
1122             }
1123             }
1124              
1125             # rewrite classic character class or escape character
1126             elsif (my $char = classic_character_class($char[$i])) {
1127 0         0 $char[$i] = $char;
1128             }
1129              
1130             # with /i modifier
1131             elsif ($char[$i] =~ /\A [\x00-\xFF] \z/oxms) {
1132 0         0 my $uc = Char::Ecyrillic::uc($char[$i]);
1133 0         0 my $fc = Char::Ecyrillic::fc($char[$i]);
1134 0 0       0 if ($uc ne $fc) {
1135 0 0       0 if (CORE::length($fc) == 1) {
1136 0         0 $char[$i] = '[' . $uc . $fc . ']';
1137             }
1138             else {
1139 0         0 $char[$i] = '(?:' . $uc . '|' . $fc . ')';
1140             }
1141             }
1142             }
1143             }
1144              
1145             # characterize
1146 0         0 for (my $i=0; $i <= $#char; $i++) {
1147 0 0       0 next if not defined $char[$i];
1148              
1149 0 0       0 if (0) {
1150             }
1151              
1152             # quote character before ? + * {
1153 0 0       0 elsif (($i >= 1) and ($char[$i] =~ /\A [\?\+\*\{] \z/oxms)) {
1154 0 0       0 if ($char[$i-1] !~ /\A [\x00-\xFF] \z/oxms) {
1155 0         0 $char[$i-1] = '(?:' . $char[$i-1] . ')';
1156             }
1157             }
1158             }
1159              
1160 0         0 $string = join '', @char;
1161             }
1162              
1163             # make regexp string
1164 0         0 return @string;
1165             }
1166              
1167             #
1168             # classic character class ( \D \S \W \d \s \w \C \X \H \V \h \v \R \N \b \B )
1169             #
1170             sub Char::Ecyrillic::classic_character_class {
1171 0     0 0 0 my($char) = @_;
1172              
1173             return {
1174 0   0     0 '\D' => '${Char::Ecyrillic::eD}',
1175             '\S' => '${Char::Ecyrillic::eS}',
1176             '\W' => '${Char::Ecyrillic::eW}',
1177             '\d' => '[0-9]',
1178              
1179             # Before Perl 5.6, \s only matched the five whitespace characters
1180             # tab, newline, form-feed, carriage return, and the space character
1181             # itself, which, taken together, is the character class [\t\n\f\r ].
1182              
1183             # Vertical tabs are now whitespace
1184             # \s in a regex now matches a vertical tab in all circumstances.
1185             # http://search.cpan.org/dist/perl-5.18.0/pod/perldelta.pod#Vertical_tabs_are_now_whitespace
1186             # \t \n \v \f \r space
1187             # '\s' => '[\x09\x0A \x0C\x0D\x20]',
1188             # '\s' => '[\x09\x0A\x0B\x0C\x0D\x20]',
1189             '\s' => '\s',
1190              
1191             '\w' => '[0-9A-Z_a-z]',
1192             '\C' => '[\x00-\xFF]',
1193             '\X' => 'X',
1194              
1195             # \h \v \H \V
1196              
1197             # P.114 Character Class Shortcuts
1198             # in Chapter 7: In the World of Regular Expressions
1199             # of ISBN 978-0-596-52010-6 Learning Perl, Fifth Edition
1200              
1201             # P.357 13.2.3 Whitespace
1202             # in Chapter 13: perlrecharclass: Perl Regular Expression Character Classes
1203             # of ISBN-13: 978-1-906966-02-7 The Perl Language Reference Manual (for Perl version 5.12.1)
1204             #
1205             # 0x00009 CHARACTER TABULATION h s
1206             # 0x0000a LINE FEED (LF) vs
1207             # 0x0000b LINE TABULATION v
1208             # 0x0000c FORM FEED (FF) vs
1209             # 0x0000d CARRIAGE RETURN (CR) vs
1210             # 0x00020 SPACE h s
1211              
1212             # P.196 Table 5-9. Alphanumeric regex metasymbols
1213             # in Chapter 5. Pattern Matching
1214             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
1215              
1216             # (and so on)
1217              
1218             '\H' => '${Char::Ecyrillic::eH}',
1219             '\V' => '${Char::Ecyrillic::eV}',
1220             '\h' => '[\x09\x20]',
1221             '\v' => '[\x0A\x0B\x0C\x0D]',
1222             '\R' => '${Char::Ecyrillic::eR}',
1223              
1224             # \N
1225             #
1226             # http://perldoc.perl.org/perlre.html
1227             # Character Classes and other Special Escapes
1228             # Any character but \n (experimental). Not affected by /s modifier
1229              
1230             '\N' => '${Char::Ecyrillic::eN}',
1231              
1232             # \b \B
1233              
1234             # P.180 Boundaries: The \b and \B Assertions
1235             # in Chapter 5: Pattern Matching
1236             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
1237              
1238             # P.219 Boundaries: The \b and \B Assertions
1239             # in Chapter 5: Pattern Matching
1240             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
1241              
1242             # '\b' => '(?:(?<=\A|\W)(?=\w)|(?<=\w)(?=\W|\z))',
1243             '\b' => '${Char::Ecyrillic::eb}',
1244              
1245             # '\B' => '(?:(?<=\w)(?=\w)|(?<=\W)(?=\W))',
1246             '\B' => '${Char::Ecyrillic::eB}',
1247              
1248             }->{$char} || '';
1249             }
1250              
1251             #
1252             # prepare Cyrillic characters per length
1253             #
1254              
1255             # 1 octet characters
1256             my @chars1 = ();
1257             sub chars1 {
1258 0 0   0 0 0 if (@chars1) {
1259 0         0 return @chars1;
1260             }
1261 0 0       0 if (exists $range_tr{1}) {
1262 0         0 my @ranges = @{ $range_tr{1} };
  0         0  
1263 0         0 while (my @range = splice(@ranges,0,1)) {
1264 0         0 for my $oct0 (@{$range[0]}) {
  0         0  
1265 0         0 push @chars1, pack 'C', $oct0;
1266             }
1267             }
1268             }
1269 0         0 return @chars1;
1270             }
1271              
1272             # 2 octets characters
1273             my @chars2 = ();
1274             sub chars2 {
1275 0 0   0 0 0 if (@chars2) {
1276 0         0 return @chars2;
1277             }
1278 0 0       0 if (exists $range_tr{2}) {
1279 0         0 my @ranges = @{ $range_tr{2} };
  0         0  
1280 0         0 while (my @range = splice(@ranges,0,2)) {
1281 0         0 for my $oct0 (@{$range[0]}) {
  0         0  
1282 0         0 for my $oct1 (@{$range[1]}) {
  0         0  
1283 0         0 push @chars2, pack 'CC', $oct0,$oct1;
1284             }
1285             }
1286             }
1287             }
1288 0         0 return @chars2;
1289             }
1290              
1291             # 3 octets characters
1292             my @chars3 = ();
1293             sub chars3 {
1294 0 0   0 0 0 if (@chars3) {
1295 0         0 return @chars3;
1296             }
1297 0 0       0 if (exists $range_tr{3}) {
1298 0         0 my @ranges = @{ $range_tr{3} };
  0         0  
1299 0         0 while (my @range = splice(@ranges,0,3)) {
1300 0         0 for my $oct0 (@{$range[0]}) {
  0         0  
1301 0         0 for my $oct1 (@{$range[1]}) {
  0         0  
1302 0         0 for my $oct2 (@{$range[2]}) {
  0         0  
1303 0         0 push @chars3, pack 'CCC', $oct0,$oct1,$oct2;
1304             }
1305             }
1306             }
1307             }
1308             }
1309 0         0 return @chars3;
1310             }
1311              
1312             # 4 octets characters
1313             my @chars4 = ();
1314             sub chars4 {
1315 0 0   0 0 0 if (@chars4) {
1316 0         0 return @chars4;
1317             }
1318 0 0       0 if (exists $range_tr{4}) {
1319 0         0 my @ranges = @{ $range_tr{4} };
  0         0  
1320 0         0 while (my @range = splice(@ranges,0,4)) {
1321 0         0 for my $oct0 (@{$range[0]}) {
  0         0  
1322 0         0 for my $oct1 (@{$range[1]}) {
  0         0  
1323 0         0 for my $oct2 (@{$range[2]}) {
  0         0  
1324 0         0 for my $oct3 (@{$range[3]}) {
  0         0  
1325 0         0 push @chars4, pack 'CCCC', $oct0,$oct1,$oct2,$oct3;
1326             }
1327             }
1328             }
1329             }
1330             }
1331             }
1332 0         0 return @chars4;
1333             }
1334              
1335             #
1336             # Cyrillic open character list for tr
1337             #
1338             sub _charlist_tr {
1339              
1340 0     0   0 local $_ = shift @_;
1341              
1342             # unescape character
1343 0         0 my @char = ();
1344 0         0 while (not /\G \z/oxmsgc) {
1345 0 0       0 if (/\G (\\0?55|\\x2[Dd]|\\-) /oxmsgc) {
    0          
    0          
    0          
    0          
    0          
    0          
1346 0         0 push @char, '\-';
1347             }
1348             elsif (/\G \\ ([0-7]{2,3}) /oxmsgc) {
1349 0         0 push @char, CORE::chr(oct $1);
1350             }
1351             elsif (/\G \\x ([0-9A-Fa-f]{1,2}) /oxmsgc) {
1352 0         0 push @char, CORE::chr(hex $1);
1353             }
1354             elsif (/\G \\c ([\x40-\x5F]) /oxmsgc) {
1355 0         0 push @char, CORE::chr(CORE::ord($1) & 0x1F);
1356             }
1357             elsif (/\G (\\ [0nrtfbae]) /oxmsgc) {
1358 0         0 push @char, {
1359             '\0' => "\0",
1360             '\n' => "\n",
1361             '\r' => "\r",
1362             '\t' => "\t",
1363             '\f' => "\f",
1364             '\b' => "\x08", # \b means backspace in character class
1365             '\a' => "\a",
1366             '\e' => "\e",
1367             }->{$1};
1368             }
1369             elsif (/\G \\ ($q_char) /oxmsgc) {
1370 0         0 push @char, $1;
1371             }
1372             elsif (/\G ($q_char) /oxmsgc) {
1373 0         0 push @char, $1;
1374             }
1375             }
1376              
1377             # join separated multiple-octet
1378 0         0 @char = join('',@char) =~ /\G (\\-|$q_char) /oxmsg;
1379              
1380             # unescape '-'
1381 0         0 my @i = ();
1382 0         0 for my $i (0 .. $#char) {
1383 0 0       0 if ($char[$i] eq '\-') {
    0          
1384 0         0 $char[$i] = '-';
1385             }
1386             elsif ($char[$i] eq '-') {
1387 0 0 0     0 if ((0 < $i) and ($i < $#char)) {
1388 0         0 push @i, $i;
1389             }
1390             }
1391             }
1392              
1393             # open character list (reverse for splice)
1394 0         0 for my $i (CORE::reverse @i) {
1395 0         0 my @range = ();
1396              
1397             # range error
1398 0 0 0     0 if ((CORE::length($char[$i-1]) > CORE::length($char[$i+1])) or ($char[$i-1] gt $char[$i+1])) {
1399 0         0 croak "Invalid tr/// range \"\\x" . unpack('H*',$char[$i-1]) . '-\x' . unpack('H*',$char[$i+1]) . '"';
1400             }
1401              
1402             # range of multiple-octet code
1403 0 0       0 if (CORE::length($char[$i-1]) == 1) {
    0          
    0          
    0          
1404 0 0       0 if (CORE::length($char[$i+1]) == 1) {
    0          
    0          
    0          
1405 0 0       0 push @range, grep {($char[$i-1] le $_) and ($_ le $char[$i+1])} chars1();
  0         0  
1406             }
1407             elsif (CORE::length($char[$i+1]) == 2) {
1408 0         0 push @range, grep {$char[$i-1] le $_} chars1();
  0         0  
1409 0         0 push @range, grep {$_ le $char[$i+1]} chars2();
  0         0  
1410             }
1411             elsif (CORE::length($char[$i+1]) == 3) {
1412 0         0 push @range, grep {$char[$i-1] le $_} chars1();
  0         0  
1413 0         0 push @range, chars2();
1414 0         0 push @range, grep {$_ le $char[$i+1]} chars3();
  0         0  
1415             }
1416             elsif (CORE::length($char[$i+1]) == 4) {
1417 0         0 push @range, grep {$char[$i-1] le $_} chars1();
  0         0  
1418 0         0 push @range, chars2();
1419 0         0 push @range, chars3();
1420 0         0 push @range, grep {$_ le $char[$i+1]} chars4();
  0         0  
1421             }
1422             else {
1423 0         0 croak "Invalid tr/// range (over 4octets) \"\\x" . unpack('H*',$char[$i-1]) . '-\x' . unpack('H*',$char[$i+1]) . '"';
1424             }
1425             }
1426             elsif (CORE::length($char[$i-1]) == 2) {
1427 0 0       0 if (CORE::length($char[$i+1]) == 2) {
    0          
    0          
1428 0 0       0 push @range, grep {($char[$i-1] le $_) and ($_ le $char[$i+1])} chars2();
  0         0  
1429             }
1430             elsif (CORE::length($char[$i+1]) == 3) {
1431 0         0 push @range, grep {$char[$i-1] le $_} chars2();
  0         0  
1432 0         0 push @range, grep {$_ le $char[$i+1]} chars3();
  0         0  
1433             }
1434             elsif (CORE::length($char[$i+1]) == 4) {
1435 0         0 push @range, grep {$char[$i-1] le $_} chars2();
  0         0  
1436 0         0 push @range, chars3();
1437 0         0 push @range, grep {$_ le $char[$i+1]} chars4();
  0         0  
1438             }
1439             else {
1440 0         0 croak "Invalid tr/// range (over 4octets) \"\\x" . unpack('H*',$char[$i-1]) . '-\x' . unpack('H*',$char[$i+1]) . '"';
1441             }
1442             }
1443             elsif (CORE::length($char[$i-1]) == 3) {
1444 0 0       0 if (CORE::length($char[$i+1]) == 3) {
    0          
1445 0 0       0 push @range, grep {($char[$i-1] le $_) and ($_ le $char[$i+1])} chars3();
  0         0  
1446             }
1447             elsif (CORE::length($char[$i+1]) == 4) {
1448 0         0 push @range, grep {$char[$i-1] le $_} chars3();
  0         0  
1449 0         0 push @range, grep {$_ le $char[$i+1]} chars4();
  0         0  
1450             }
1451             else {
1452 0         0 croak "Invalid tr/// range (over 4octets) \"\\x" . unpack('H*',$char[$i-1]) . '-\x' . unpack('H*',$char[$i+1]) . '"';
1453             }
1454             }
1455             elsif (CORE::length($char[$i-1]) == 4) {
1456 0 0       0 if (CORE::length($char[$i+1]) == 4) {
1457 0 0       0 push @range, grep {($char[$i-1] le $_) and ($_ le $char[$i+1])} chars4();
  0         0  
1458             }
1459             else {
1460 0         0 croak "Invalid tr/// range (over 4octets) \"\\x" . unpack('H*',$char[$i-1]) . '-\x' . unpack('H*',$char[$i+1]) . '"';
1461             }
1462             }
1463             else {
1464 0         0 croak "Invalid tr/// range (over 4octets) \"\\x" . unpack('H*',$char[$i-1]) . '-\x' . unpack('H*',$char[$i+1]) . '"';
1465             }
1466              
1467 0         0 splice @char, $i-1, 3, @range;
1468             }
1469              
1470 0         0 return @char;
1471             }
1472              
1473             #
1474             # Cyrillic open character class
1475             #
1476             sub _cc {
1477 0 0   0   0 if (scalar(@_) == 0) {
    0          
    0          
1478 0         0 die __FILE__, ": subroutine cc got no parameter.";
1479             }
1480             elsif (scalar(@_) == 1) {
1481 0         0 return sprintf('\x%02X',$_[0]);
1482             }
1483             elsif (scalar(@_) == 2) {
1484 0 0       0 if ($_[0] > $_[1]) {
    0          
    0          
1485 0         0 die __FILE__, ": subroutine cc got \$_[0] > \$_[1] parameters).";
1486             }
1487             elsif ($_[0] == $_[1]) {
1488 0         0 return sprintf('\x%02X',$_[0]);
1489             }
1490             elsif (($_[0]+1) == $_[1]) {
1491 0         0 return sprintf('[\\x%02X\\x%02X]',$_[0],$_[1]);
1492             }
1493             else {
1494 0         0 return sprintf('[\\x%02X-\\x%02X]',$_[0],$_[1]);
1495             }
1496             }
1497             else {
1498 0         0 die __FILE__, ": subroutine cc got 3 or more parameters (@{[scalar(@_)]} parameters).";
  0         0  
1499             }
1500             }
1501              
1502             #
1503             # Cyrillic octet range
1504             #
1505             sub _octets {
1506 0     0   0 my $length = shift @_;
1507              
1508 0 0       0 if ($length == 1) {
1509 0         0 my($a1) = unpack 'C', $_[0];
1510 0         0 my($z1) = unpack 'C', $_[1];
1511              
1512 0 0       0 if ($a1 > $z1) {
1513 0         0 croak 'Invalid [] range in regexp (CORE::ord(A) > CORE::ord(B)) ' . '\x' . unpack('H*',$a1) . '-\x' . unpack('H*',$z1);
1514             }
1515              
1516 0 0       0 if ($a1 == $z1) {
    0          
1517 0         0 return sprintf('\x%02X',$a1);
1518             }
1519             elsif (($a1+1) == $z1) {
1520 0         0 return sprintf('\x%02X\x%02X',$a1,$z1);
1521             }
1522             else {
1523 0         0 return sprintf('\x%02X-\x%02X',$a1,$z1);
1524             }
1525             }
1526             else {
1527 0         0 die __FILE__, ": subroutine _octets got invalid length ($length).";
1528             }
1529             }
1530              
1531             #
1532             # Cyrillic range regexp
1533             #
1534             sub _range_regexp {
1535 0     0   0 my($length,$first,$last) = @_;
1536              
1537 0         0 my @range_regexp = ();
1538 0 0       0 if (not exists $range_tr{$length}) {
1539 0         0 return @range_regexp;
1540             }
1541              
1542 0         0 my @ranges = @{ $range_tr{$length} };
  0         0  
1543 0         0 while (my @range = splice(@ranges,0,$length)) {
1544 0         0 my $min = '';
1545 0         0 my $max = '';
1546 0         0 for (my $i=0; $i < $length; $i++) {
1547 0         0 $min .= pack 'C', $range[$i][0];
1548 0         0 $max .= pack 'C', $range[$i][-1];
1549             }
1550              
1551             # min___max
1552             # FIRST_____________LAST
1553             # (nothing)
1554              
1555 0 0 0     0 if ($max lt $first) {
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
1556             }
1557              
1558             # **********
1559             # min_________max
1560             # FIRST_____________LAST
1561             # **********
1562              
1563             elsif (($min le $first) and ($first le $max) and ($max le $last)) {
1564 0         0 push @range_regexp, _octets($length,$first,$max,$min,$max);
1565             }
1566              
1567             # **********************
1568             # min________________max
1569             # FIRST_____________LAST
1570             # **********************
1571              
1572             elsif (($min eq $first) and ($max eq $last)) {
1573 0         0 push @range_regexp, _octets($length,$first,$last,$min,$max);
1574             }
1575              
1576             # *********
1577             # min___max
1578             # FIRST_____________LAST
1579             # *********
1580              
1581             elsif (($first le $min) and ($max le $last)) {
1582 0         0 push @range_regexp, _octets($length,$min,$max,$min,$max);
1583             }
1584              
1585             # **********************
1586             # min__________________________max
1587             # FIRST_____________LAST
1588             # **********************
1589              
1590             elsif (($min le $first) and ($last le $max)) {
1591 0         0 push @range_regexp, _octets($length,$first,$last,$min,$max);
1592             }
1593              
1594             # *********
1595             # min________max
1596             # FIRST_____________LAST
1597             # *********
1598              
1599             elsif (($first le $min) and ($min le $last) and ($last le $max)) {
1600 0         0 push @range_regexp, _octets($length,$min,$last,$min,$max);
1601             }
1602              
1603             # min___max
1604             # FIRST_____________LAST
1605             # (nothing)
1606              
1607             elsif ($last lt $min) {
1608             }
1609              
1610             else {
1611 0         0 die __FILE__, ": subroutine _range_regexp panic.";
1612             }
1613             }
1614              
1615 0         0 return @range_regexp;
1616             }
1617              
1618             #
1619             # Cyrillic open character list for qr and not qr
1620             #
1621             sub _charlist {
1622              
1623 0     0   0 my $modifier = pop @_;
1624 0         0 my @char = @_;
1625              
1626 0 0       0 my $ignorecase = ($modifier =~ /i/oxms) ? 1 : 0;
1627              
1628             # unescape character
1629 0         0 for (my $i=0; $i <= $#char; $i++) {
1630              
1631             # escape - to ...
1632 0 0 0     0 if ($char[$i] eq '-') {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1633 0 0 0     0 if ((0 < $i) and ($i < $#char)) {
1634 0         0 $char[$i] = '...';
1635             }
1636             }
1637              
1638             # octal escape sequence
1639             elsif ($char[$i] =~ /\A \\o \{ ([0-7]+) \} \z/oxms) {
1640 0         0 $char[$i] = octchr($1);
1641             }
1642              
1643             # hexadecimal escape sequence
1644             elsif ($char[$i] =~ /\A \\x \{ ([0-9A-Fa-f]+) \} \z/oxms) {
1645 0         0 $char[$i] = hexchr($1);
1646             }
1647              
1648             # \N{CHARNAME} --> N\{CHARNAME}
1649             elsif ($char[$i] =~ /\A \\ (N) ( \{ ([^0-9\}][^\}]*) \} ) \z/oxms) {
1650 0         0 $char[$i] = $1 . '\\' . $2;
1651             }
1652              
1653             # \p{PROPERTY} --> p\{PROPERTY}
1654             elsif ($char[$i] =~ /\A \\ (p) ( \{ ([^0-9\}][^\}]*) \} ) \z/oxms) {
1655 0         0 $char[$i] = $1 . '\\' . $2;
1656             }
1657              
1658             # \P{PROPERTY} --> P\{PROPERTY}
1659             elsif ($char[$i] =~ /\A \\ (P) ( \{ ([^0-9\}][^\}]*) \} ) \z/oxms) {
1660 0         0 $char[$i] = $1 . '\\' . $2;
1661             }
1662              
1663             # \p, \P, \X --> p, P, X
1664             elsif ($char[$i] =~ /\A \\ ( [pPX] ) \z/oxms) {
1665 0         0 $char[$i] = $1;
1666             }
1667              
1668             elsif ($char[$i] =~ /\A \\ ([0-7]{2,3}) \z/oxms) {
1669 0         0 $char[$i] = CORE::chr oct $1;
1670             }
1671             elsif ($char[$i] =~ /\A \\x ([0-9A-Fa-f]{1,2}) \z/oxms) {
1672 0         0 $char[$i] = CORE::chr hex $1;
1673             }
1674             elsif ($char[$i] =~ /\A \\c ([\x40-\x5F]) \z/oxms) {
1675 0         0 $char[$i] = CORE::chr(CORE::ord($1) & 0x1F);
1676             }
1677             elsif ($char[$i] =~ /\A (\\ [0nrtfbaedswDSWHVhvR]) \z/oxms) {
1678 0         0 $char[$i] = {
1679             '\0' => "\0",
1680             '\n' => "\n",
1681             '\r' => "\r",
1682             '\t' => "\t",
1683             '\f' => "\f",
1684             '\b' => "\x08", # \b means backspace in character class
1685             '\a' => "\a",
1686             '\e' => "\e",
1687             '\d' => '[0-9]',
1688              
1689             # Vertical tabs are now whitespace
1690             # \s in a regex now matches a vertical tab in all circumstances.
1691             # http://search.cpan.org/dist/perl-5.18.0/pod/perldelta.pod#Vertical_tabs_are_now_whitespace
1692             # \t \n \v \f \r space
1693             # '\s' => '[\x09\x0A \x0C\x0D\x20]',
1694             # '\s' => '[\x09\x0A\x0B\x0C\x0D\x20]',
1695             '\s' => '\s',
1696              
1697             '\w' => '[0-9A-Z_a-z]',
1698             '\D' => '${Char::Ecyrillic::eD}',
1699             '\S' => '${Char::Ecyrillic::eS}',
1700             '\W' => '${Char::Ecyrillic::eW}',
1701              
1702             '\H' => '${Char::Ecyrillic::eH}',
1703             '\V' => '${Char::Ecyrillic::eV}',
1704             '\h' => '[\x09\x20]',
1705             '\v' => '[\x0A\x0B\x0C\x0D]',
1706             '\R' => '${Char::Ecyrillic::eR}',
1707              
1708             }->{$1};
1709             }
1710              
1711             # POSIX-style character classes
1712             elsif ($ignorecase and ($char[$i] =~ /\A ( \[\: \^? (?:lower|upper) :\] ) \z/oxms)) {
1713 0         0 $char[$i] = {
1714              
1715             '[:lower:]' => '[\x41-\x5A\x61-\x7A]',
1716             '[:upper:]' => '[\x41-\x5A\x61-\x7A]',
1717             '[:^lower:]' => '${Char::Ecyrillic::not_lower_i}',
1718             '[:^upper:]' => '${Char::Ecyrillic::not_upper_i}',
1719              
1720             }->{$1};
1721             }
1722             elsif ($char[$i] =~ /\A ( \[\: \^? (?:alnum|alpha|ascii|blank|cntrl|digit|graph|lower|print|punct|space|upper|word|xdigit) :\] ) \z/oxms) {
1723 0         0 $char[$i] = {
1724              
1725             '[:alnum:]' => '[\x30-\x39\x41-\x5A\x61-\x7A]',
1726             '[:alpha:]' => '[\x41-\x5A\x61-\x7A]',
1727             '[:ascii:]' => '[\x00-\x7F]',
1728             '[:blank:]' => '[\x09\x20]',
1729             '[:cntrl:]' => '[\x00-\x1F\x7F]',
1730             '[:digit:]' => '[\x30-\x39]',
1731             '[:graph:]' => '[\x21-\x7F]',
1732             '[:lower:]' => '[\x61-\x7A]',
1733             '[:print:]' => '[\x20-\x7F]',
1734             '[:punct:]' => '[\x21-\x2F\x3A-\x3F\x40\x5B-\x5F\x60\x7B-\x7E]',
1735              
1736             # P.174 POSIX-Style Character Classes
1737             # in Chapter 5: Pattern Matching
1738             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
1739              
1740             # P.311 11.2.4 Character Classes and other Special Escapes
1741             # in Chapter 11: perlre: Perl regular expressions
1742             # of ISBN-13: 978-1-906966-02-7 The Perl Language Reference Manual (for Perl version 5.12.1)
1743              
1744             # P.210 POSIX-Style Character Classes
1745             # in Chapter 5: Pattern Matching
1746             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
1747              
1748             '[:space:]' => '[\s\x0B]', # "\s" plus vertical tab ("\cK")
1749              
1750             '[:upper:]' => '[\x41-\x5A]',
1751             '[:word:]' => '[\x30-\x39\x41-\x5A\x5F\x61-\x7A]',
1752             '[:xdigit:]' => '[\x30-\x39\x41-\x46\x61-\x66]',
1753             '[:^alnum:]' => '${Char::Ecyrillic::not_alnum}',
1754             '[:^alpha:]' => '${Char::Ecyrillic::not_alpha}',
1755             '[:^ascii:]' => '${Char::Ecyrillic::not_ascii}',
1756             '[:^blank:]' => '${Char::Ecyrillic::not_blank}',
1757             '[:^cntrl:]' => '${Char::Ecyrillic::not_cntrl}',
1758             '[:^digit:]' => '${Char::Ecyrillic::not_digit}',
1759             '[:^graph:]' => '${Char::Ecyrillic::not_graph}',
1760             '[:^lower:]' => '${Char::Ecyrillic::not_lower}',
1761             '[:^print:]' => '${Char::Ecyrillic::not_print}',
1762             '[:^punct:]' => '${Char::Ecyrillic::not_punct}',
1763             '[:^space:]' => '${Char::Ecyrillic::not_space}',
1764             '[:^upper:]' => '${Char::Ecyrillic::not_upper}',
1765             '[:^word:]' => '${Char::Ecyrillic::not_word}',
1766             '[:^xdigit:]' => '${Char::Ecyrillic::not_xdigit}',
1767              
1768             }->{$1};
1769             }
1770             elsif ($char[$i] =~ /\A \\ ($q_char) \z/oxms) {
1771 0         0 $char[$i] = $1;
1772             }
1773             }
1774              
1775             # open character list
1776 0         0 my @singleoctet = ();
1777 0         0 my @multipleoctet = ();
1778 0         0 for (my $i=0; $i <= $#char; ) {
1779              
1780             # escaped -
1781 0 0 0     0 if (defined($char[$i+1]) and ($char[$i+1] eq '...')) {
    0          
    0          
    0          
    0          
    0          
1782 0         0 $i += 1;
1783 0         0 next;
1784             }
1785              
1786             # make range regexp
1787             elsif ($char[$i] eq '...') {
1788              
1789             # range error
1790 0 0       0 if (CORE::length($char[$i-1]) > CORE::length($char[$i+1])) {
    0          
1791 0         0 croak 'Invalid [] range in regexp (length(A) > length(B)) ' . '\x' . unpack('H*',$char[$i-1]) . '-\x' . unpack('H*',$char[$i+1]);
1792             }
1793             elsif (CORE::length($char[$i-1]) == CORE::length($char[$i+1])) {
1794 0 0       0 if ($char[$i-1] gt $char[$i+1]) {
1795 0         0 croak 'Invalid [] range in regexp (CORE::ord(A) > CORE::ord(B)) ' . '\x' . unpack('H*',$char[$i-1]) . '-\x' . unpack('H*',$char[$i+1]);
1796             }
1797             }
1798              
1799             # make range regexp per length
1800 0         0 for my $length (CORE::length($char[$i-1]) .. CORE::length($char[$i+1])) {
1801 0         0 my @regexp = ();
1802              
1803             # is first and last
1804 0 0 0     0 if (($length == CORE::length($char[$i-1])) and ($length == CORE::length($char[$i+1]))) {
    0 0        
    0          
    0          
1805 0         0 push @regexp, _range_regexp($length, $char[$i-1], $char[$i+1]);
1806             }
1807              
1808             # is first
1809             elsif ($length == CORE::length($char[$i-1])) {
1810 0         0 push @regexp, _range_regexp($length, $char[$i-1], "\xFF" x $length);
1811             }
1812              
1813             # is inside in first and last
1814             elsif ((CORE::length($char[$i-1]) < $length) and ($length < CORE::length($char[$i+1]))) {
1815 0         0 push @regexp, _range_regexp($length, "\x00" x $length, "\xFF" x $length);
1816             }
1817              
1818             # is last
1819             elsif ($length == CORE::length($char[$i+1])) {
1820 0         0 push @regexp, _range_regexp($length, "\x00" x $length, $char[$i+1]);
1821             }
1822              
1823             else {
1824 0         0 die __FILE__, ": subroutine make_regexp panic.";
1825             }
1826              
1827 0 0       0 if ($length == 1) {
1828 0         0 push @singleoctet, @regexp;
1829             }
1830             else {
1831 0         0 push @multipleoctet, @regexp;
1832             }
1833             }
1834              
1835 0         0 $i += 2;
1836             }
1837              
1838             # with /i modifier
1839             elsif ($char[$i] =~ /\A [\x00-\xFF] \z/oxms) {
1840 0 0       0 if ($modifier =~ /i/oxms) {
1841 0         0 my $uc = Char::Ecyrillic::uc($char[$i]);
1842 0         0 my $fc = Char::Ecyrillic::fc($char[$i]);
1843 0 0       0 if ($uc ne $fc) {
1844 0 0       0 if (CORE::length($fc) == 1) {
1845 0         0 push @singleoctet, $uc, $fc;
1846             }
1847             else {
1848 0         0 push @singleoctet, $uc;
1849 0         0 push @multipleoctet, $fc;
1850             }
1851             }
1852             else {
1853 0         0 push @singleoctet, $char[$i];
1854             }
1855             }
1856             else {
1857 0         0 push @singleoctet, $char[$i];
1858             }
1859 0         0 $i += 1;
1860             }
1861              
1862             # single character of single octet code
1863             elsif ($char[$i] =~ /\A (?: \\h ) \z/oxms) {
1864 0         0 push @singleoctet, "\t", "\x20";
1865 0         0 $i += 1;
1866             }
1867             elsif ($char[$i] =~ /\A (?: \\v ) \z/oxms) {
1868 0         0 push @singleoctet, "\x0A", "\x0B", "\x0C", "\x0D";
1869 0         0 $i += 1;
1870             }
1871             elsif ($char[$i] =~ /\A (?: \\d | \\s | \\w ) \z/oxms) {
1872 0         0 push @singleoctet, $char[$i];
1873 0         0 $i += 1;
1874             }
1875              
1876             # single character of multiple-octet code
1877             else {
1878 0         0 push @multipleoctet, $char[$i];
1879 0         0 $i += 1;
1880             }
1881             }
1882              
1883             # quote metachar
1884 0         0 for (@singleoctet) {
1885 0 0       0 if ($_ eq '...') {
    0          
    0          
    0          
    0          
1886 0         0 $_ = '-';
1887             }
1888             elsif (/\A \n \z/oxms) {
1889 0         0 $_ = '\n';
1890             }
1891             elsif (/\A \r \z/oxms) {
1892 0         0 $_ = '\r';
1893             }
1894             elsif (/\A ([\x00-\x20\x7F-\xFF]) \z/oxms) {
1895 0         0 $_ = sprintf('\x%02X', CORE::ord $1);
1896             }
1897             elsif (/\A [\x00-\xFF] \z/oxms) {
1898 0         0 $_ = quotemeta $_;
1899             }
1900             }
1901              
1902             # return character list
1903 0         0 return \@singleoctet, \@multipleoctet;
1904             }
1905              
1906             #
1907             # Cyrillic octal escape sequence
1908             #
1909             sub octchr {
1910 0     0 0 0 my($octdigit) = @_;
1911              
1912 0         0 my @binary = ();
1913 0         0 for my $octal (split(//,$octdigit)) {
1914 0         0 push @binary, {
1915             '0' => '000',
1916             '1' => '001',
1917             '2' => '010',
1918             '3' => '011',
1919             '4' => '100',
1920             '5' => '101',
1921             '6' => '110',
1922             '7' => '111',
1923             }->{$octal};
1924             }
1925 0         0 my $binary = join '', @binary;
1926              
1927 0         0 my $octchr = {
1928             # 1234567
1929             1 => pack('B*', "0000000$binary"),
1930             2 => pack('B*', "000000$binary"),
1931             3 => pack('B*', "00000$binary"),
1932             4 => pack('B*', "0000$binary"),
1933             5 => pack('B*', "000$binary"),
1934             6 => pack('B*', "00$binary"),
1935             7 => pack('B*', "0$binary"),
1936             0 => pack('B*', "$binary"),
1937              
1938             }->{CORE::length($binary) % 8};
1939              
1940 0         0 return $octchr;
1941             }
1942              
1943             #
1944             # Cyrillic hexadecimal escape sequence
1945             #
1946             sub hexchr {
1947 0     0 0 0 my($hexdigit) = @_;
1948              
1949 0         0 my $hexchr = {
1950             1 => pack('H*', "0$hexdigit"),
1951             0 => pack('H*', "$hexdigit"),
1952              
1953             }->{CORE::length($_[0]) % 2};
1954              
1955 0         0 return $hexchr;
1956             }
1957              
1958             #
1959             # Cyrillic open character list for qr
1960             #
1961             sub charlist_qr {
1962              
1963 0     0 0 0 my $modifier = pop @_;
1964 0         0 my @char = @_;
1965              
1966 0         0 my($singleoctet, $multipleoctet) = _charlist(@char, $modifier);
1967 0         0 my @singleoctet = @$singleoctet;
1968 0         0 my @multipleoctet = @$multipleoctet;
1969              
1970             # return character list
1971 0 0       0 if (scalar(@singleoctet) >= 1) {
1972              
1973             # with /i modifier
1974 0 0       0 if ($modifier =~ m/i/oxms) {
1975 0         0 my %singleoctet_ignorecase = ();
1976 0         0 for (@singleoctet) {
1977 0   0     0 while (s/ \A \\x(..) - \\x(..) //oxms or s/ \A \\x((..)) //oxms) {
1978 0         0 for my $ord (hex($1) .. hex($2)) {
1979 0         0 my $char = CORE::chr($ord);
1980 0         0 my $uc = Char::Ecyrillic::uc($char);
1981 0         0 my $fc = Char::Ecyrillic::fc($char);
1982 0 0       0 if ($uc eq $fc) {
1983 0         0 $singleoctet_ignorecase{unpack 'C*', $char} = 1;
1984             }
1985             else {
1986 0 0       0 if (CORE::length($fc) == 1) {
1987 0         0 $singleoctet_ignorecase{unpack 'C*', $uc} = 1;
1988 0         0 $singleoctet_ignorecase{unpack 'C*', $fc} = 1;
1989             }
1990             else {
1991 0         0 $singleoctet_ignorecase{unpack 'C*', $uc} = 1;
1992 0         0 push @multipleoctet, join '', map {sprintf('\x%02X',$_)} unpack 'C*', $fc;
  0         0  
1993             }
1994             }
1995             }
1996             }
1997 0 0       0 if ($_ ne '') {
1998 0         0 $singleoctet_ignorecase{unpack 'C*', $_} = 1;
1999             }
2000             }
2001 0         0 my $i = 0;
2002 0         0 my @singleoctet_ignorecase = ();
2003 0         0 for my $ord (0 .. 255) {
2004 0 0       0 if (exists $singleoctet_ignorecase{$ord}) {
2005 0         0 push @{$singleoctet_ignorecase[$i]}, $ord;
  0         0  
2006             }
2007             else {
2008 0         0 $i++;
2009             }
2010             }
2011 0         0 @singleoctet = ();
2012 0         0 for my $range (@singleoctet_ignorecase) {
2013 0 0       0 if (ref $range) {
2014 0 0       0 if (scalar(@{$range}) == 1) {
  0 0       0  
  0         0  
2015 0         0 push @singleoctet, sprintf('\x%02X', @{$range}[0]);
  0         0  
2016             }
2017             elsif (scalar(@{$range}) == 2) {
2018 0         0 push @singleoctet, sprintf('\x%02X\x%02X', @{$range}[0], @{$range}[-1]);
  0         0  
  0         0  
2019             }
2020             else {
2021 0         0 push @singleoctet, sprintf('\x%02X-\x%02X', @{$range}[0], @{$range}[-1]);
  0         0  
  0         0  
2022             }
2023             }
2024             }
2025             }
2026              
2027 0         0 my $not_anchor = '';
2028              
2029 0         0 push @multipleoctet, join('', $not_anchor, '[', @singleoctet, ']' );
2030             }
2031 0 0       0 if (scalar(@multipleoctet) >= 2) {
2032 0         0 return '(?:' . join('|', @multipleoctet) . ')';
2033             }
2034             else {
2035 0         0 return $multipleoctet[0];
2036             }
2037             }
2038              
2039             #
2040             # Cyrillic open character list for not qr
2041             #
2042             sub charlist_not_qr {
2043              
2044 0     0 0 0 my $modifier = pop @_;
2045 0         0 my @char = @_;
2046              
2047 0         0 my($singleoctet, $multipleoctet) = _charlist(@char, $modifier);
2048 0         0 my @singleoctet = @$singleoctet;
2049 0         0 my @multipleoctet = @$multipleoctet;
2050              
2051             # with /i modifier
2052 0 0       0 if ($modifier =~ m/i/oxms) {
2053 0         0 my %singleoctet_ignorecase = ();
2054 0         0 for (@singleoctet) {
2055 0   0     0 while (s/ \A \\x(..) - \\x(..) //oxms or s/ \A \\x((..)) //oxms) {
2056 0         0 for my $ord (hex($1) .. hex($2)) {
2057 0         0 my $char = CORE::chr($ord);
2058 0         0 my $uc = Char::Ecyrillic::uc($char);
2059 0         0 my $fc = Char::Ecyrillic::fc($char);
2060 0 0       0 if ($uc eq $fc) {
2061 0         0 $singleoctet_ignorecase{unpack 'C*', $char} = 1;
2062             }
2063             else {
2064 0 0       0 if (CORE::length($fc) == 1) {
2065 0         0 $singleoctet_ignorecase{unpack 'C*', $uc} = 1;
2066 0         0 $singleoctet_ignorecase{unpack 'C*', $fc} = 1;
2067             }
2068             else {
2069 0         0 $singleoctet_ignorecase{unpack 'C*', $uc} = 1;
2070 0         0 push @multipleoctet, join '', map {sprintf('\x%02X',$_)} unpack 'C*', $fc;
  0         0  
2071             }
2072             }
2073             }
2074             }
2075 0 0       0 if ($_ ne '') {
2076 0         0 $singleoctet_ignorecase{unpack 'C*', $_} = 1;
2077             }
2078             }
2079 0         0 my $i = 0;
2080 0         0 my @singleoctet_ignorecase = ();
2081 0         0 for my $ord (0 .. 255) {
2082 0 0       0 if (exists $singleoctet_ignorecase{$ord}) {
2083 0         0 push @{$singleoctet_ignorecase[$i]}, $ord;
  0         0  
2084             }
2085             else {
2086 0         0 $i++;
2087             }
2088             }
2089 0         0 @singleoctet = ();
2090 0         0 for my $range (@singleoctet_ignorecase) {
2091 0 0       0 if (ref $range) {
2092 0 0       0 if (scalar(@{$range}) == 1) {
  0 0       0  
  0         0  
2093 0         0 push @singleoctet, sprintf('\x%02X', @{$range}[0]);
  0         0  
2094             }
2095             elsif (scalar(@{$range}) == 2) {
2096 0         0 push @singleoctet, sprintf('\x%02X\x%02X', @{$range}[0], @{$range}[-1]);
  0         0  
  0         0  
2097             }
2098             else {
2099 0         0 push @singleoctet, sprintf('\x%02X-\x%02X', @{$range}[0], @{$range}[-1]);
  0         0  
  0         0  
2100             }
2101             }
2102             }
2103             }
2104              
2105             # return character list
2106 0 0       0 if (scalar(@multipleoctet) >= 1) {
2107 0 0       0 if (scalar(@singleoctet) >= 1) {
2108              
2109             # any character other than multiple-octet and single octet character class
2110 0         0 return '(?!' . join('|', @multipleoctet) . ')(?:[^' . join('', @singleoctet) . '])';
2111             }
2112             else {
2113              
2114             # any character other than multiple-octet character class
2115 0         0 return '(?!' . join('|', @multipleoctet) . ")(?:$your_char)";
2116             }
2117             }
2118             else {
2119 0 0       0 if (scalar(@singleoctet) >= 1) {
2120              
2121             # any character other than single octet character class
2122 0         0 return '(?:[^' . join('', @singleoctet) . '])';
2123             }
2124             else {
2125              
2126             # any character
2127 0         0 return "(?:$your_char)";
2128             }
2129             }
2130             }
2131              
2132             #
2133             # open file in read mode
2134             #
2135             sub _open_r {
2136 177     177   608 my(undef,$file) = @_;
2137 177         893 $file =~ s#\A (\s) #./$1#oxms;
2138 177   33     18516 return eval(q{open($_[0],'<',$_[1])}) ||
2139             open($_[0],"< $file\0");
2140             }
2141              
2142             #
2143             # open file in write mode
2144             #
2145             sub _open_w {
2146 0     0   0 my(undef,$file) = @_;
2147 0         0 $file =~ s#\A (\s) #./$1#oxms;
2148 0   0     0 return eval(q{open($_[0],'>',$_[1])}) ||
2149             open($_[0],"> $file\0");
2150             }
2151              
2152             #
2153             # open file in append mode
2154             #
2155             sub _open_a {
2156 0     0   0 my(undef,$file) = @_;
2157 0         0 $file =~ s#\A (\s) #./$1#oxms;
2158 0   0     0 return eval(q{open($_[0],'>>',$_[1])}) ||
2159             open($_[0],">> $file\0");
2160             }
2161              
2162             #
2163             # safe system
2164             #
2165             sub _systemx {
2166              
2167             # P.707 29.2.33. exec
2168             # in Chapter 29: Functions
2169             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
2170             #
2171             # Be aware that in older releases of Perl, exec (and system) did not flush
2172             # your output buffer, so you needed to enable command buffering by setting $|
2173             # on one or more filehandles to avoid lost output in the case of exec, or
2174             # misordererd output in the case of system. This situation was largely remedied
2175             # in the 5.6 release of Perl. (So, 5.005 release not yet.)
2176              
2177             # P.855 exec
2178             # in Chapter 27: Functions
2179             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
2180             #
2181             # In very old release of Perl (before v5.6), exec (and system) did not flush
2182             # your output buffer, so you needed to enable command buffering by setting $|
2183             # on one or more filehandles to avoid lost output with exec or misordered
2184             # output with system.
2185              
2186 177     177   764 $| = 1;
2187              
2188             # P.565 23.1.2. Cleaning Up Your Environment
2189             # in Chapter 23: Security
2190             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
2191              
2192             # P.656 Cleaning Up Your Environment
2193             # in Chapter 20: Security
2194             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
2195              
2196             # local $ENV{'PATH'} = '.';
2197 177         2208 local @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # Make %ENV safer
2198              
2199             # P.707 29.2.33. exec
2200             # in Chapter 29: Functions
2201             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
2202             #
2203             # As we mentioned earlier, exec treats a discrete list of arguments as an
2204             # indication that it should bypass shell processing. However, there is one
2205             # place where you might still get tripped up. The exec call (and system, too)
2206             # will not distinguish between a single scalar argument and an array containing
2207             # only one element.
2208             #
2209             # @args = ("echo surprise"); # just one element in list
2210             # exec @args # still subject to shell escapes
2211             # or die "exec: $!"; # because @args == 1
2212             #
2213             # To avoid this, you can use the PATHNAME syntax, explicitly duplicating the
2214             # first argument as the pathname, which forces the rest of the arguments to be
2215             # interpreted as a list, even if there is only one of them:
2216             #
2217             # exec { $args[0] } @args # safe even with one-argument list
2218             # or die "can't exec @args: $!";
2219              
2220             # P.855 exec
2221             # in Chapter 27: Functions
2222             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
2223             #
2224             # As we mentioned earlier, exec treats a discrete list of arguments as a
2225             # directive to bypass shell processing. However, there is one place where
2226             # you might still get tripped up. The exec call (and system, too) cannot
2227             # distinguish between a single scalar argument and an array containing
2228             # only one element.
2229             #
2230             # @args = ("echo surprise"); # just one element in list
2231             # exec @args # still subject to shell escapes
2232             # || die "exec: $!"; # because @args == 1
2233             #
2234             # To avoid this, use the PATHNAME syntax, explicitly duplicating the first
2235             # argument as the pathname, which forces the rest of the arguments to be
2236             # interpreted as a list, even if there is only one of them:
2237             #
2238             # exec { $args[0] } @args # safe even with one-argument list
2239             # || die "can't exec @args: $!";
2240              
2241 177         407 return CORE::system { $_[0] } @_; # safe even with one-argument list
  177         12030530  
2242             }
2243              
2244             #
2245             # Cyrillic order to character (with parameter)
2246             #
2247             sub Char::Ecyrillic::chr(;$) {
2248              
2249 0 0   0 0   my $c = @_ ? $_[0] : $_;
2250              
2251 0 0         if ($c == 0x00) {
2252 0           return "\x00";
2253             }
2254             else {
2255 0           my @chr = ();
2256 0           while ($c > 0) {
2257 0           unshift @chr, ($c % 0x100);
2258 0           $c = int($c / 0x100);
2259             }
2260 0           return pack 'C*', @chr;
2261             }
2262             }
2263              
2264             #
2265             # Cyrillic order to character (without parameter)
2266             #
2267             sub Char::Ecyrillic::chr_() {
2268              
2269 0     0 0   my $c = $_;
2270              
2271 0 0         if ($c == 0x00) {
2272 0           return "\x00";
2273             }
2274             else {
2275 0           my @chr = ();
2276 0           while ($c > 0) {
2277 0           unshift @chr, ($c % 0x100);
2278 0           $c = int($c / 0x100);
2279             }
2280 0           return pack 'C*', @chr;
2281             }
2282             }
2283              
2284             #
2285             # Cyrillic path globbing (with parameter)
2286             #
2287             sub Char::Ecyrillic::glob($) {
2288              
2289 0 0   0 0   if (wantarray) {
2290 0           my @glob = _DOS_like_glob(@_);
2291 0           for my $glob (@glob) {
2292 0           $glob =~ s{ \A (?:\./)+ }{}oxms;
2293             }
2294 0           return @glob;
2295             }
2296             else {
2297 0           my $glob = _DOS_like_glob(@_);
2298 0           $glob =~ s{ \A (?:\./)+ }{}oxms;
2299 0           return $glob;
2300             }
2301             }
2302              
2303             #
2304             # Cyrillic path globbing (without parameter)
2305             #
2306             sub Char::Ecyrillic::glob_() {
2307              
2308 0 0   0 0   if (wantarray) {
2309 0           my @glob = _DOS_like_glob();
2310 0           for my $glob (@glob) {
2311 0           $glob =~ s{ \A (?:\./)+ }{}oxms;
2312             }
2313 0           return @glob;
2314             }
2315             else {
2316 0           my $glob = _DOS_like_glob();
2317 0           $glob =~ s{ \A (?:\./)+ }{}oxms;
2318 0           return $glob;
2319             }
2320             }
2321              
2322             #
2323             # Cyrillic path globbing via File::DosGlob 1.10
2324             #
2325             # Often I confuse "_dosglob" and "_doglob".
2326             # So, I renamed "_dosglob" to "_DOS_like_glob".
2327             #
2328             my %iter;
2329             my %entries;
2330             sub _DOS_like_glob {
2331              
2332             # context (keyed by second cxix argument provided by core)
2333 0     0     my($expr,$cxix) = @_;
2334              
2335             # glob without args defaults to $_
2336 0 0         $expr = $_ if not defined $expr;
2337              
2338             # represents the current user's home directory
2339             #
2340             # 7.3. Expanding Tildes in Filenames
2341             # in Chapter 7. File Access
2342             # of ISBN 0-596-00313-7 Perl Cookbook, 2nd Edition.
2343             #
2344             # and File::HomeDir, File::HomeDir::Windows module
2345              
2346             # DOS-like system
2347 0 0         if ($^O =~ /\A (?: MSWin32 | NetWare | symbian | dos ) \z/oxms) {
2348 0           $expr =~ s{ \A ~ (?= [^/\\] ) }
2349 0           { my_home_MSWin32() }oxmse;
2350             }
2351              
2352             # UNIX-like system
2353             else {
2354 0           $expr =~ s{ \A ~ ( (?:[^/])* ) }
2355 0 0 0       { $1 ? (eval(q{(getpwnam($1))[7]})||my_home()) : my_home() }oxmse;
2356             }
2357              
2358             # assume global context if not provided one
2359 0 0         $cxix = '_G_' if not defined $cxix;
2360 0 0         $iter{$cxix} = 0 if not exists $iter{$cxix};
2361              
2362             # if we're just beginning, do it all first
2363 0 0         if ($iter{$cxix} == 0) {
2364 0           $entries{$cxix} = [ _do_glob(1, _parse_line($expr)) ];
2365             }
2366              
2367             # chuck it all out, quick or slow
2368 0 0         if (wantarray) {
2369 0           delete $iter{$cxix};
2370 0           return @{delete $entries{$cxix}};
  0            
2371             }
2372             else {
2373 0 0         if ($iter{$cxix} = scalar @{$entries{$cxix}}) {
  0            
2374 0           return shift @{$entries{$cxix}};
  0            
2375             }
2376             else {
2377             # return undef for EOL
2378 0           delete $iter{$cxix};
2379 0           delete $entries{$cxix};
2380 0           return undef;
2381             }
2382             }
2383             }
2384              
2385             #
2386             # Cyrillic path globbing subroutine
2387             #
2388             sub _do_glob {
2389              
2390 0     0     my($cond,@expr) = @_;
2391 0           my @glob = ();
2392 0           my $fix_drive_relative_paths = 0;
2393              
2394             OUTER:
2395 0           for my $expr (@expr) {
2396 0 0         next OUTER if not defined $expr;
2397 0 0         next OUTER if $expr eq '';
2398              
2399 0           my @matched = ();
2400 0           my @globdir = ();
2401 0           my $head = '.';
2402 0           my $pathsep = '/';
2403 0           my $tail;
2404              
2405             # if argument is within quotes strip em and do no globbing
2406 0 0         if ($expr =~ /\A " ((?:$q_char)*) " \z/oxms) {
2407 0           $expr = $1;
2408 0 0         if ($cond eq 'd') {
2409 0 0         if (-d $expr) {
2410 0           push @glob, $expr;
2411             }
2412             }
2413             else {
2414 0 0         if (-e $expr) {
2415 0           push @glob, $expr;
2416             }
2417             }
2418 0           next OUTER;
2419             }
2420              
2421             # wildcards with a drive prefix such as h:*.pm must be changed
2422             # to h:./*.pm to expand correctly
2423 0 0         if ($^O =~ /\A (?: MSWin32 | NetWare | symbian | dos ) \z/oxms) {
2424 0 0         if ($expr =~ s# \A ((?:[A-Za-z]:)?) ([^/\\]) #$1./$2#oxms) {
2425 0           $fix_drive_relative_paths = 1;
2426             }
2427             }
2428              
2429 0 0         if (($head, $tail) = _parse_path($expr,$pathsep)) {
2430 0 0         if ($tail eq '') {
2431 0           push @glob, $expr;
2432 0           next OUTER;
2433             }
2434 0 0         if ($head =~ / \A (?:$q_char)*? [*?] /oxms) {
2435 0 0         if (@globdir = _do_glob('d', $head)) {
2436 0           push @glob, _do_glob($cond, map {"$_$pathsep$tail"} @globdir);
  0            
2437 0           next OUTER;
2438             }
2439             }
2440 0 0 0       if ($head eq '' or $head =~ /\A [A-Za-z]: \z/oxms) {
2441 0           $head .= $pathsep;
2442             }
2443 0           $expr = $tail;
2444             }
2445              
2446             # If file component has no wildcards, we can avoid opendir
2447 0 0         if ($expr !~ / \A (?:$q_char)*? [*?] /oxms) {
2448 0 0         if ($head eq '.') {
2449 0           $head = '';
2450             }
2451 0 0 0       if ($head ne '' and ($head =~ / \G ($q_char) /oxmsg)[-1] ne $pathsep) {
2452 0           $head .= $pathsep;
2453             }
2454 0           $head .= $expr;
2455 0 0         if ($cond eq 'd') {
2456 0 0         if (-d $head) {
2457 0           push @glob, $head;
2458             }
2459             }
2460             else {
2461 0 0         if (-e $head) {
2462 0           push @glob, $head;
2463             }
2464             }
2465 0           next OUTER;
2466             }
2467 0 0         opendir(*DIR, $head) or next OUTER;
2468 0           my @leaf = readdir DIR;
2469 0           closedir DIR;
2470              
2471 0 0         if ($head eq '.') {
2472 0           $head = '';
2473             }
2474 0 0 0       if ($head ne '' and ($head =~ / \G ($q_char) /oxmsg)[-1] ne $pathsep) {
2475 0           $head .= $pathsep;
2476             }
2477              
2478 0           my $pattern = '';
2479 0           while ($expr =~ / \G ($q_char) /oxgc) {
2480 0           my $char = $1;
2481              
2482             # 6.9. Matching Shell Globs as Regular Expressions
2483             # in Chapter 6. Pattern Matching
2484             # of ISBN 0-596-00313-7 Perl Cookbook, 2nd Edition.
2485             # (and so on)
2486              
2487 0 0         if ($char eq '*') {
    0          
    0          
2488 0           $pattern .= "(?:$your_char)*",
2489             }
2490             elsif ($char eq '?') {
2491 0           $pattern .= "(?:$your_char)?", # DOS style
2492             # $pattern .= "(?:$your_char)", # UNIX style
2493             }
2494             elsif ((my $fc = Char::Ecyrillic::fc($char)) ne $char) {
2495 0           $pattern .= $fc;
2496             }
2497             else {
2498 0           $pattern .= quotemeta $char;
2499             }
2500             }
2501 0     0     my $matchsub = sub { Char::Ecyrillic::fc($_[0]) =~ /\A $pattern \z/xms };
  0            
2502              
2503             # if ($@) {
2504             # print STDERR "$0: $@\n";
2505             # next OUTER;
2506             # }
2507              
2508             INNER:
2509 0           for my $leaf (@leaf) {
2510 0 0 0       if ($leaf eq '.' or $leaf eq '..') {
2511 0           next INNER;
2512             }
2513 0 0 0       if ($cond eq 'd' and not -d "$head$leaf") {
2514 0           next INNER;
2515             }
2516              
2517 0 0         if (&$matchsub($leaf)) {
2518 0           push @matched, "$head$leaf";
2519 0           next INNER;
2520             }
2521              
2522             # [DOS compatibility special case]
2523             # Failed, add a trailing dot and try again, but only...
2524              
2525 0 0 0       if (Char::Ecyrillic::index($leaf,'.') == -1 and # if name does not have a dot in it *and*
      0        
2526             CORE::length($leaf) <= 8 and # name is shorter than or equal to 8 chars *and*
2527             Char::Ecyrillic::index($pattern,'\\.') != -1 # pattern has a dot.
2528             ) {
2529 0 0         if (&$matchsub("$leaf.")) {
2530 0           push @matched, "$head$leaf";
2531 0           next INNER;
2532             }
2533             }
2534             }
2535 0 0         if (@matched) {
2536 0           push @glob, @matched;
2537             }
2538             }
2539 0 0         if ($fix_drive_relative_paths) {
2540 0           for my $glob (@glob) {
2541 0           $glob =~ s# \A ([A-Za-z]:) \./ #$1#oxms;
2542             }
2543             }
2544 0           return @glob;
2545             }
2546              
2547             #
2548             # Cyrillic parse line
2549             #
2550             sub _parse_line {
2551              
2552 0     0     my($line) = @_;
2553              
2554 0           $line .= ' ';
2555 0           my @piece = ();
2556 0           while ($line =~ /
2557             " ( (?: [^"] )* ) " \s+ |
2558             ( (?: [^"\s] )* ) \s+
2559             /oxmsg
2560             ) {
2561 0 0         push @piece, defined($1) ? $1 : $2;
2562             }
2563 0           return @piece;
2564             }
2565              
2566             #
2567             # Cyrillic parse path
2568             #
2569             sub _parse_path {
2570              
2571 0     0     my($path,$pathsep) = @_;
2572              
2573 0           $path .= '/';
2574 0           my @subpath = ();
2575 0           while ($path =~ /
2576             ((?: [^\/\\] )+?) [\/\\]
2577             /oxmsg
2578             ) {
2579 0           push @subpath, $1;
2580             }
2581              
2582 0           my $tail = pop @subpath;
2583 0           my $head = join $pathsep, @subpath;
2584 0           return $head, $tail;
2585             }
2586              
2587             #
2588             # via File::HomeDir::Windows 1.00
2589             #
2590             sub my_home_MSWin32 {
2591              
2592             # A lot of unix people and unix-derived tools rely on
2593             # the ability to overload HOME. We will support it too
2594             # so that they can replace raw HOME calls with File::HomeDir.
2595 0 0 0 0 0   if (exists $ENV{'HOME'} and $ENV{'HOME'}) {
    0 0        
    0 0        
      0        
      0        
2596 0           return $ENV{'HOME'};
2597             }
2598              
2599             # Do we have a user profile?
2600             elsif (exists $ENV{'USERPROFILE'} and $ENV{'USERPROFILE'}) {
2601 0           return $ENV{'USERPROFILE'};
2602             }
2603              
2604             # Some Windows use something like $ENV{'HOME'}
2605             elsif (exists $ENV{'HOMEDRIVE'} and exists $ENV{'HOMEPATH'} and $ENV{'HOMEDRIVE'} and $ENV{'HOMEPATH'}) {
2606 0           return join '', $ENV{'HOMEDRIVE'}, $ENV{'HOMEPATH'};
2607             }
2608              
2609 0           return undef;
2610             }
2611              
2612             #
2613             # via File::HomeDir::Unix 1.00
2614             #
2615             sub my_home {
2616 0     0 0   my $home;
2617              
2618 0 0 0       if (exists $ENV{'HOME'} and defined $ENV{'HOME'}) {
    0 0        
2619 0           $home = $ENV{'HOME'};
2620             }
2621              
2622             # This is from the original code, but I'm guessing
2623             # it means "login directory" and exists on some Unixes.
2624             elsif (exists $ENV{'LOGDIR'} and $ENV{'LOGDIR'}) {
2625 0           $home = $ENV{'LOGDIR'};
2626             }
2627              
2628             ### More-desperate methods
2629              
2630             # Light desperation on any (Unixish) platform
2631             else {
2632 0           $home = eval q{ (getpwuid($<))[7] };
2633             }
2634              
2635             # On Unix in general, a non-existant home means "no home"
2636             # For example, "nobody"-like users might use /nonexistant
2637 0 0 0       if (defined $home and ! -d($home)) {
2638 0           $home = undef;
2639             }
2640 0           return $home;
2641             }
2642              
2643             #
2644             # ${^PREMATCH}, $PREMATCH, $` the string preceding what was matched
2645             #
2646             sub Char::Ecyrillic::PREMATCH {
2647 0     0 0   return $`;
2648             }
2649              
2650             #
2651             # ${^MATCH}, $MATCH, $& the string that matched
2652             #
2653             sub Char::Ecyrillic::MATCH {
2654 0     0 0   return $&;
2655             }
2656              
2657             #
2658             # ${^POSTMATCH}, $POSTMATCH, $' the string following what was matched
2659             #
2660             sub Char::Ecyrillic::POSTMATCH {
2661 0     0 0   return $';
2662             }
2663              
2664             #
2665             # Cyrillic character to order (with parameter)
2666             #
2667             sub Char::Cyrillic::ord(;$) {
2668              
2669 0 0   0 1   local $_ = shift if @_;
2670              
2671 0 0         if (/\A ($q_char) /oxms) {
2672 0           my @ord = unpack 'C*', $1;
2673 0           my $ord = 0;
2674 0           while (my $o = shift @ord) {
2675 0           $ord = $ord * 0x100 + $o;
2676             }
2677 0           return $ord;
2678             }
2679             else {
2680 0           return CORE::ord $_;
2681             }
2682             }
2683              
2684             #
2685             # Cyrillic character to order (without parameter)
2686             #
2687             sub Char::Cyrillic::ord_() {
2688              
2689 0 0   0 0   if (/\A ($q_char) /oxms) {
2690 0           my @ord = unpack 'C*', $1;
2691 0           my $ord = 0;
2692 0           while (my $o = shift @ord) {
2693 0           $ord = $ord * 0x100 + $o;
2694             }
2695 0           return $ord;
2696             }
2697             else {
2698 0           return CORE::ord $_;
2699             }
2700             }
2701              
2702             #
2703             # Cyrillic reverse
2704             #
2705             sub Char::Cyrillic::reverse(@) {
2706              
2707 0 0   0 0   if (wantarray) {
2708 0           return CORE::reverse @_;
2709             }
2710             else {
2711              
2712             # One of us once cornered Larry in an elevator and asked him what
2713             # problem he was solving with this, but he looked as far off into
2714             # the distance as he could in an elevator and said, "It seemed like
2715             # a good idea at the time."
2716              
2717 0           return join '', CORE::reverse(join('',@_) =~ /\G ($q_char) /oxmsg);
2718             }
2719             }
2720              
2721             #
2722             # Cyrillic getc (with parameter, without parameter)
2723             #
2724             sub Char::Cyrillic::getc(;*@) {
2725              
2726 0     0 0   my($package) = caller;
2727 0 0         my $fh = @_ ? qualify_to_ref(shift,$package) : \*STDIN;
2728 0 0 0       croak 'Too many arguments for Char::Cyrillic::getc' if @_ and not wantarray;
2729              
2730 0           my @length = sort { $a <=> $b } keys %range_tr;
  0            
2731 0           my $getc = '';
2732 0           for my $length ($length[0] .. $length[-1]) {
2733 0           $getc .= CORE::getc($fh);
2734 0 0         if (exists $range_tr{CORE::length($getc)}) {
2735 0 0         if ($getc =~ /\A ${Char::Ecyrillic::dot_s} \z/oxms) {
2736 0 0         return wantarray ? ($getc,@_) : $getc;
2737             }
2738             }
2739             }
2740 0 0         return wantarray ? ($getc,@_) : $getc;
2741             }
2742              
2743             #
2744             # Cyrillic length by character
2745             #
2746             sub Char::Cyrillic::length(;$) {
2747              
2748 0 0   0 1   local $_ = shift if @_;
2749              
2750 0           local @_ = /\G ($q_char) /oxmsg;
2751 0           return scalar @_;
2752             }
2753              
2754             #
2755             # Cyrillic substr by character
2756             #
2757             BEGIN {
2758              
2759             # P.232 The lvalue Attribute
2760             # in Chapter 6: Subroutines
2761             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
2762              
2763             # P.336 The lvalue Attribute
2764             # in Chapter 7: Subroutines
2765             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
2766              
2767             # P.144 8.4 Lvalue subroutines
2768             # in Chapter 8: perlsub: Perl subroutines
2769             # of ISBN-13: 978-1-906966-02-7 The Perl Language Reference Manual (for Perl version 5.12.1)
2770              
2771 177 50 0 177 1 317583 eval sprintf(<<'END', ($] >= 5.014000) ? ':lvalue' : '');
  0 0   0      
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
2772             # vv----------------*******
2773             sub Char::Cyrillic::substr($$;$$) %s {
2774              
2775             my @char = $_[0] =~ /\G ($q_char) /oxmsg;
2776              
2777             # If the substring is beyond either end of the string, substr() returns the undefined
2778             # value and produces a warning. When used as an lvalue, specifying a substring that
2779             # is entirely outside the string raises an exception.
2780             # http://perldoc.perl.org/functions/substr.html
2781              
2782             # A return with no argument returns the scalar value undef in scalar context,
2783             # an empty list () in list context, and (naturally) nothing at all in void
2784             # context.
2785              
2786             my $offset = $_[1];
2787             if (($offset > scalar(@char)) or ($offset < (-1 * scalar(@char)))) {
2788             return;
2789             }
2790              
2791             # substr($string,$offset,$length,$replacement)
2792             if (@_ == 4) {
2793             my(undef,undef,$length,$replacement) = @_;
2794             my $substr = join '', splice(@char, $offset, $length, $replacement);
2795             $_[0] = join '', @char;
2796              
2797             # return $substr; this doesn't work, don't say "return"
2798             $substr;
2799             }
2800              
2801             # substr($string,$offset,$length)
2802             elsif (@_ == 3) {
2803             my(undef,undef,$length) = @_;
2804             my $octet_offset = 0;
2805             my $octet_length = 0;
2806             if ($offset == 0) {
2807             $octet_offset = 0;
2808             }
2809             elsif ($offset > 0) {
2810             $octet_offset = CORE::length(join '', @char[0..$offset-1]);
2811             }
2812             else {
2813             $octet_offset = -1 * CORE::length(join '', @char[$#char+$offset+1..$#char]);
2814             }
2815             if ($length == 0) {
2816             $octet_length = 0;
2817             }
2818             elsif ($length > 0) {
2819             $octet_length = CORE::length(join '', @char[$offset..$offset+$length-1]);
2820             }
2821             else {
2822             $octet_length = -1 * CORE::length(join '', @char[$#char+$length+1..$#char]);
2823             }
2824             CORE::substr($_[0], $octet_offset, $octet_length);
2825             }
2826              
2827             # substr($string,$offset)
2828             else {
2829             my $octet_offset = 0;
2830             if ($offset == 0) {
2831             $octet_offset = 0;
2832             }
2833             elsif ($offset > 0) {
2834             $octet_offset = CORE::length(join '', @char[0..$offset-1]);
2835             }
2836             else {
2837             $octet_offset = -1 * CORE::length(join '', @char[$#char+$offset+1..$#char]);
2838             }
2839             CORE::substr($_[0], $octet_offset);
2840             }
2841             }
2842             END
2843             }
2844              
2845             #
2846             # Cyrillic index by character
2847             #
2848             sub Char::Cyrillic::index($$;$) {
2849              
2850 0     0 1   my $index;
2851 0 0         if (@_ == 3) {
2852 0           $index = Char::Ecyrillic::index($_[0], $_[1], CORE::length(Char::Cyrillic::substr($_[0], 0, $_[2])));
2853             }
2854             else {
2855 0           $index = Char::Ecyrillic::index($_[0], $_[1]);
2856             }
2857              
2858 0 0         if ($index == -1) {
2859 0           return -1;
2860             }
2861             else {
2862 0           return Char::Cyrillic::length(CORE::substr $_[0], 0, $index);
2863             }
2864             }
2865              
2866             #
2867             # Cyrillic rindex by character
2868             #
2869             sub Char::Cyrillic::rindex($$;$) {
2870              
2871 0     0 1   my $rindex;
2872 0 0         if (@_ == 3) {
2873 0           $rindex = Char::Ecyrillic::rindex($_[0], $_[1], CORE::length(Char::Cyrillic::substr($_[0], 0, $_[2])));
2874             }
2875             else {
2876 0           $rindex = Char::Ecyrillic::rindex($_[0], $_[1]);
2877             }
2878              
2879 0 0         if ($rindex == -1) {
2880 0           return -1;
2881             }
2882             else {
2883 0           return Char::Cyrillic::length(CORE::substr $_[0], 0, $rindex);
2884             }
2885             }
2886              
2887             #
2888             # instead of Carp::carp
2889             #
2890             sub carp {
2891 0     0 0   my($package,$filename,$line) = caller(1);
2892 0           print STDERR "@_ at $filename line $line.\n";
2893             }
2894              
2895             #
2896             # instead of Carp::croak
2897             #
2898             sub croak {
2899 0     0 0   my($package,$filename,$line) = caller(1);
2900 0           print STDERR "@_ at $filename line $line.\n";
2901 0           die "\n";
2902             }
2903              
2904             #
2905             # instead of Carp::cluck
2906             #
2907             sub cluck {
2908 0     0 0   my $i = 0;
2909 0           my @cluck = ();
2910 0           while (my($package,$filename,$line,$subroutine) = caller($i)) {
2911 0           push @cluck, "[$i] $filename($line) $package::$subroutine\n";
2912 0           $i++;
2913             }
2914 0           print STDERR CORE::reverse @cluck;
2915 0           print STDERR "\n";
2916 0           carp @_;
2917             }
2918              
2919             #
2920             # instead of Carp::confess
2921             #
2922             sub confess {
2923 0     0 0   my $i = 0;
2924 0           my @confess = ();
2925 0           while (my($package,$filename,$line,$subroutine) = caller($i)) {
2926 0           push @confess, "[$i] $filename($line) $package::$subroutine\n";
2927 0           $i++;
2928             }
2929 0           print STDERR CORE::reverse @confess;
2930 0           print STDERR "\n";
2931 0           croak @_;
2932             }
2933              
2934             1;
2935              
2936             __END__