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