File Coverage

blib/lib/Sjis.pm
Criterion Covered Total %
statement 65 134 48.5
branch 26 78 33.3
condition 0 3 0.0
subroutine 9 12 75.0
pod 0 2 0.0
total 100 229 43.6


line stmt bran cond sub pod time code
1             package Sjis;
2 385     385   259472 use strict;
  385         2832  
  385         11496  
3             ######################################################################
4             #
5             # Sjis - Source code filter to escape ShiftJIS script
6             #
7             # http://search.cpan.org/dist/Char-Sjis/
8             #
9             # Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018, 2019 INABA Hitoshi
10             ######################################################################
11              
12 385     385   8148 use 5.00503; # Galapagos Consensus 1998 for primetools
  385         1179  
13             # use 5.008001; # Lancaster Consensus 2013 for toolchains
14              
15             # 12.3. Delaying use Until Runtime
16             # in Chapter 12. Packages, Libraries, and Modules
17             # of ISBN 0-596-00313-7 Perl Cookbook, 2nd Edition.
18             # (and so on)
19              
20             # Version numbers should be boring
21             # http://www.dagolden.com/index.php/369/version-numbers-should-be-boring/
22             # For the impatient, the disinterested or those who just want to follow
23             # a recipe, my advice for all modules is this:
24             # our $VERSION = "0.001"; # or "0.001_001" for a dev release
25             # $VERSION = CORE::eval $VERSION; # No!! because '1.10' makes '1.1'
26              
27 385     385   2205 use vars qw($VERSION);
  385         692  
  385         86170  
28             $VERSION = '1.13';
29             $VERSION = $VERSION;
30              
31             BEGIN {
32 385 50   385   2519 if ($^X =~ / jperl /oxmsi) {
33 0         0 die __FILE__, ": needs perl(not jperl) 5.00503 or later. (\$^X==$^X)\n";
34             }
35 385         727 if (CORE::ord('A') == 193) {
36             die __FILE__, ": is not US-ASCII script (may be EBCDIC or EBCDIK script).\n";
37             }
38 385         29680 if (CORE::ord('A') != 0x41) {
39             die __FILE__, ": is not US-ASCII script (must be US-ASCII script).\n";
40             }
41             }
42              
43             BEGIN {
44 385     385   4540 (my $dirname = __FILE__) =~ s{^(.+)/[^/]*$}{$1};
45 385         2778 unshift @INC, $dirname;
46 385         760723 CORE::require Esjis;
47             }
48              
49             # instead of Symbol.pm
50 1155         1975 BEGIN {
51             sub gensym () {
52 1155     1155 0 5406 return \do { local *_ };
  385         4307  
53             }
54             }
55              
56             # P.714 29.2.39. flock
57             # in Chapter 29: Functions
58             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
59              
60             # P.863 flock
61             # in Chapter 27: Functions
62             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
63              
64             # P.228 Inlining Constant Functions
65             # in Chapter 6: Subroutines
66             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
67              
68             # P.331 Inlining Constant Functions
69             # in Chapter 7: Subroutines
70             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
71              
72             sub LOCK_SH() {1}
73             sub LOCK_EX() {2}
74             sub LOCK_UN() {8}
75             sub LOCK_NB() {4}
76              
77       0     sub unimport {}
78             sub Sjis::escape_script;
79              
80             # 6.18. Matching Multiple-Byte Characters
81             # in Chapter 6. Pattern Matching
82             # of ISBN 978-1-56592-243-3 Perl Perl Cookbook.
83             # (and so on)
84              
85             # regexp of character
86             my $qq_char = qr/(?> \\c[\x40-\x5F] | \\? (?:[\x81-\x9F\xE0-\xFC][\x00-\xFF] | [\x00-\xFF]) )/oxms;
87             my $q_char = qr/(?> [\x81-\x9F\xE0-\xFC][\x00-\xFF] | [\x00-\xFF] )/oxms;
88              
89             # when this script is main program
90             if ($0 eq __FILE__) {
91              
92             # show usage
93             unless (@ARGV) {
94             die <
95             $0: usage
96              
97             perl $0 ShiftJIS_script.pl > Escaped_script.pl.e
98             END
99             }
100              
101             print Sjis::escape_script($ARGV[0]);
102             exit 0;
103             }
104              
105             my($package,$filename,$line,$subroutine,$hasargs,$wantarray,$evaltext,$is_require,$hints,$bitmask) = caller 0;
106              
107             # called any package not main
108             if ($package ne 'main') {
109             die <
110             @{[__FILE__]}: escape by manually command '$^X @{[__FILE__]} "$filename" > "@{[__PACKAGE__]}::$filename"'
111             and rewrite "use $package;" to "use @{[__PACKAGE__]}::$package;" of script "$0".
112             END
113             }
114              
115             # P.302 Module Privacy and the Exporter
116             # in Chapter 11: Modules
117             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
118             #
119             # A module can do anything it jolly well pleases when it's used, since use just
120             # calls the ordinary import method for the module, and you can define that
121             # method to do anything you like.
122              
123             # P.406 Module Privacy and the Exporter
124             # in Chapter 11: Modules
125             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
126             #
127             # A module can do anything it jolly well pleases when it's used, since use just
128             # calls the ordinary import method for the module, and you can define that
129             # method to do anything you like.
130              
131             sub import {
132              
133 0 50   385   0 if (Esjis::e("$filename.e")) {
134 0 0       0 if (exists $ENV{'CHAR_DEBUG'}) {
    0          
135 0         0 Esjis::unlink "$filename.e";
136             }
137             elsif (Esjis::z("$filename.e")) {
138 0         0 Esjis::unlink "$filename.e";
139             }
140             else {
141              
142             #----------------------------------------------------
143             # older >
144             # newer >>>>>
145             #----------------------------------------------------
146             # Filter >
147             # Source >>>>>
148             # Escape >>> needs re-escape (Source was changed)
149             #
150             # Filter >>>
151             # Source >>>>>
152             # Escape > needs re-escape (Source was changed)
153             #
154             # Filter >>>>>
155             # Source >>>
156             # Escape > needs re-escape (Source was changed)
157             #
158             # Filter >>>>>
159             # Source >
160             # Escape >>> needs re-escape (Filter was changed)
161             #
162             # Filter >
163             # Source >>>
164             # Escape >>>>> executable without re-escape
165             #
166             # Filter >>>
167             # Source >
168             # Escape >>>>> executable without re-escape
169             #----------------------------------------------------
170              
171 0         0 my $mtime_filter = (Esjis::stat(__FILE__ ))[9];
172 0         0 my $mtime_source = (Esjis::stat($filename ))[9];
173 0         0 my $mtime_escape = (Esjis::stat("$filename.e"))[9];
174 0 0 0     0 if (($mtime_escape < $mtime_source) or ($mtime_escape < $mtime_filter)) {
175 385         1865 Esjis::unlink "$filename.e";
176             }
177             }
178             }
179              
180 385 50       1213 if (not Esjis::e("$filename.e")) {
181 385         2001 my $fh = gensym();
182 385 50       3832 Esjis::_open_a($fh, "$filename.e") or die __FILE__, ": Can't write open file: $filename.e\n";
183              
184             # 7.19. Flushing Output
185             # in Chapter 7. File Access
186             # of ISBN 0-596-00313-7 Perl Cookbook, 2nd Edition.
187              
188 385         2928 select((select($fh), $|=1)[0]);
189              
190 0 50       0 if (0) {
    50          
191             }
192 0         0 elsif ($^O eq 'MacOS') {
193 0         0 CORE::eval q{
194             CORE::require Mac::Files;
195             Mac::Files::FSpSetFLock("$filename.e");
196             };
197             }
198             elsif (exists $ENV{'CHAR_NONBLOCK'}) {
199              
200             # P.419 File Locking
201             # in Chapter 16: Interprocess Communication
202             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
203              
204             # P.524 File Locking
205             # in Chapter 15: Interprocess Communication
206             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
207              
208             # P.571 Handling Race Conditions
209             # in Chapter 23: Security
210             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
211              
212             # P.663 Handling Race Conditions
213             # in Chapter 20: Security
214             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
215              
216             # (and so on)
217              
218 0         0 CORE::eval q{ flock($fh, LOCK_EX | LOCK_NB) };
219 0 0       0 if ($@) {
220 385         26633 die __FILE__, ": Can't immediately write-lock the file: $filename.e\n";
221             }
222             }
223             else {
224 385         17099 CORE::eval q{ flock($fh, LOCK_EX) };
225             }
226              
227 385         5005 CORE::eval q{ truncate($fh, 0) };
228 385 50       1441 seek($fh, 0, 0) or die __FILE__, ": Can't seek file: $filename.e\n";
229              
230 385         1020 my $e_script = Sjis::escape_script($filename);
231 385         34373 print {$fh} $e_script;
  385         5124  
232              
233 385         12912 my $mode = (Esjis::stat($filename))[2] & 0777;
234 385         3201 chmod $mode, "$filename.e";
235              
236 0 50       0 if ($^O eq 'MacOS') {
237 385         66421 CORE::eval q{
238             CORE::require Mac::Files;
239             Mac::Files::FSpRstFLock("$filename.e");
240             };
241             }
242              
243 385 50       3373 close($fh) or die __FILE__, ": Can't close file: $filename.e\n";
244             }
245              
246 385         3132 my $fh = gensym();
247 385 50       3592 Esjis::_open_r($fh, "$filename.e") or die __FILE__, ": Can't read open file: $filename.e\n";
248              
249 0 50       0 if (0) {
    50          
250             }
251 0         0 elsif ($^O eq 'MacOS') {
252 0         0 CORE::eval q{
253             CORE::require Mac::Files;
254             Mac::Files::FSpSetFLock("$filename.e");
255             };
256             }
257             elsif (exists $ENV{'CHAR_NONBLOCK'}) {
258 0         0 CORE::eval q{ flock($fh, LOCK_SH | LOCK_NB) };
259 0 0       0 if ($@) {
260 385         42911 die __FILE__, ": Can't immediately read-lock the file: $filename.e\n";
261             }
262             }
263             else {
264 385         2213 CORE::eval q{ flock($fh, LOCK_SH) };
265             }
266              
267 385         2325 my @switch = ();
268 0 50       0 if ($^W) {
269 385         1884 push @switch, '-w';
270             }
271 0 50       0 if (defined $^I) {
272 0         0 push @switch, '-i' . $^I;
273 385         862 undef $^I;
274             }
275              
276             # P.707 29.2.33. exec
277             # in Chapter 29: Functions
278             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
279             #
280             # If there is more than one argument in LIST, or if LIST is an array with more
281             # than one value, the system shell will never be used. This also bypasses any
282             # shell processing of the command. The presence or absence of metacharacters in
283             # the arguments doesn't affect this list-triggered behavior, which makes it the
284             # preferred from in security-conscious programs that do not with to expose
285             # themselves to potential shell escapes.
286             # Environment variable PERL5SHELL(Microsoft ports only) will never be used, too.
287              
288             # P.855 exec
289             # in Chapter 27: Functions
290             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
291             #
292             # If there is more than one argument in LIST, or if LIST is an array with more
293             # than one value, the system shell will never be used. This also bypasses any
294             # shell processing of the command. The presence or absence of metacharacters in
295             # the arguments doesn't affect this list-triggered behavior, which makes it the
296             # preferred from in security-conscious programs that do not wish to expose
297             # themselves to injection attacks via shell escapes.
298             # Environment variable PERL5SHELL(Microsoft ports only) will never be used, too.
299              
300             # P.489 #! and Quoting on Non-Unix Systems
301             # in Chapter 19: The Command-Line Interface
302             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
303              
304             # P.578 #! and Quoting on Non-Unix Systems
305             # in Chapter 17: The Command-Line Interface
306             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
307              
308 385         3387 my $system = 0;
309              
310             # DOS-like system
311 0 50       0 if ($^O =~ /\A (?: MSWin32 | NetWare | symbian | dos ) \z/oxms) {
    50          
312             $system = Esjis::_systemx(
313             _escapeshellcmd_MSWin32($^X),
314              
315             # -I switch can not treat space included path
316             # (map { '-I' . _escapeshellcmd_MSWin32($_) } @INC),
317 0         0 (map { '-I' . $_ } @INC),
318              
319             @switch,
320             '--',
321 0         0 map { _escapeshellcmd_MSWin32($_) } "$filename.e", @ARGV
  0         0  
322             );
323             }
324              
325             # MacOS system
326             elsif ($^O eq 'MacOS') {
327             $system = Esjis::_systemx(
328             _escapeshellcmd_MacOS($^X),
329 0         0 (map { '-I' . _escapeshellcmd_MacOS($_) } @INC),
330             @switch,
331             '--',
332 0         0 map { _escapeshellcmd_MacOS($_) } "$filename.e", @ARGV
  0         0  
333             );
334 385         2175 CORE::eval q{
335             CORE::require Mac::Files;
336             Mac::Files::FSpRstFLock("$filename.e");
337             };
338             }
339              
340             # UNIX-like system
341             else {
342             $system = Esjis::_systemx(
343             _escapeshellcmd($^X),
344 385         1272 (map { '-I' . _escapeshellcmd($_) } @INC),
345             @switch,
346             '--',
347 4620         6699 map { _escapeshellcmd($_) } "$filename.e", @ARGV
  385         734049  
348             );
349             }
350              
351             # exit with actual exit value
352 0         0 exit($system >> 8);
353             }
354              
355             # escape shell command line on DOS-like system
356             sub _escapeshellcmd_MSWin32 {
357 0     0   0 my($word) = @_;
358 0 0       0 if ($word =~ / [ ] /oxms) {
359 0         0 return qq{"$word"};
360             }
361             else {
362 0         0 return $word;
363             }
364             }
365              
366             # escape shell command line on Mac OS
367             sub _escapeshellcmd_MacOS {
368 0     0   0 my($word) = @_;
369 5390         9522 return $word;
370             }
371              
372             # escape shell command line on UNIX-like system
373             sub _escapeshellcmd {
374 5390     5390   14407 my($word) = @_;
375 385         1028 return $word;
376             }
377              
378             # P.619 Source Filters
379             # in Chapter 24: Common Practices
380             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
381              
382             # P.718 Source Filters
383             # in Chapter 21: Common Practices
384             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
385              
386             # escape ShiftJIS script
387             sub Sjis::escape_script {
388 385     385 0 717 my($script) = @_;
389 385         976 my $e_script = '';
390              
391             # read ShiftJIS script
392 385         1634 my $fh = gensym();
393 385 50       2283 Esjis::_open_r($fh, $script) or die __FILE__, ": Can't open file: $script\n";
394 385         8248 local $/ = undef; # slurp mode
395 385         5072 $_ = <$fh>;
396 385 50       2532 close($fh) or die __FILE__, ": Can't close file: $script\n";
397              
398 0 50       0 if (/^ use Esjis(?:(?>\s+)(?>[0-9\.]*))?(?>\s*); $/oxms) {
399 385         1356 return $_;
400             }
401             else {
402              
403             # #! shebang line
404 0 50       0 if (s/\A(#!.+?\n)//oms) {
405 0         0 my $head = $1;
406 0         0 $head =~ s/\bjperl\b/perl/gi;
407 385         1104 $e_script .= $head;
408             }
409              
410             # DOS-like system header
411 0 50       0 if (s/\A(\@rem(?>\s*)=(?>\s*)'.*?'(?>\s*);\s*\n)//oms) {
412 0         0 my $head = $1;
413 0         0 $head =~ s/\bjperl\b/perl/gi;
414 385         16486 $e_script .= $head;
415             }
416              
417             # P.618 Generating Perl in Other Languages
418             # in Chapter 24: Common Practices
419             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
420              
421             # P.717 Generating Perl in Other Languages
422             # in Chapter 21: Common Practices
423             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
424              
425 0 50       0 if (s/(.*^#(?>\s*)line(?>\s+)(?>[0-9]+)(?:(?>\s+)"(?:$q_char)+?")?\s*\n)//oms) {
426 0         0 my $head = $1;
427 0         0 $head =~ s/\bjperl\b/perl/gi;
428 385         2300 $e_script .= $head;
429             }
430              
431             # P.210 5.10.3.3. Match-time code evaluation
432             # in Chapter 5: Pattern Matching
433             # of ISBN 0-596-00027-8 Programming Perl Third Edition.
434              
435             # P.255 Match-time code evaluation
436             # in Chapter 5: Pattern Matching
437             # of ISBN 978-0-596-00492-7 Programming Perl 4th Edition.
438              
439             # '...' quote to avoid "Octal number in vector unsupported" on perl 5.6
440              
441 385         4428 $e_script .= sprintf("use Esjis '%s.0'; # 'quote' for perl5.6\n", $Sjis::VERSION); # require run-time routines version
442              
443             # use Sjis version qw(ord reverse getc);
444 385 50       1205 if (s/^ (?>\s*) use (?>\s+) (?: Char | Sjis ) (?>\s*) ([^\x81-\x9F\xE0-\xFC;]*) ; \s* \n? $//oxms) {
445              
446             # require version
447 385         1512 my $list = $1;
448 0 50       0 if ($list =~ s/\A ((?>[0-9]+)\.(?>[0-9]+)) \.0 (?>\s*) //oxms) {
    50          
449 0         0 my $version = $1;
450 0 0       0 if ($version ne $Sjis::VERSION) {
451 0         0 my @file = grep -e, map {qq{$_/Sjis.pm}} @INC;
  0         0  
452 0         0 my %file = map { $_ => 1 } @file;
  0         0  
453 0 0       0 if (scalar(keys %file) >= 2) {
454 0         0 my $file = join "\n", sort keys %file;
455 0         0 warn <
456             ****************************************************
457             C A U T I O N
458              
459             CONFLICT Sjis.pm FILE
460              
461             $file
462             ****************************************************
463              
464             END
465             }
466 0         0 die "Script $0 expects Sjis.pm $version, but @{[__FILE__]} is version $Sjis::VERSION\n";
  0         0  
467             }
468 0         0 $e_script .= qq{die "Script \$0 expects Esjis.pm $version, but \\\$Esjis::VERSION is \$Esjis::VERSION" if \$Esjis::VERSION ne '$version';\n};
469             }
470             elsif ($list =~ s/\A ((?>[0-9]+)(?>\.[0-9]*)) (?>\s*) //oxms) {
471 0         0 my $version = $1;
472 0 0       0 if ($version > $Sjis::VERSION) {
473 0         0 die "Script $0 required Sjis.pm $version, but @{[__FILE__]} is only version $Sjis::VERSION\n";
  385         1790  
474             }
475             }
476              
477             # demand ord, reverse, and getc
478 0 50       0 if ($list !~ /\A (?>\s*) \z/oxms) {
479 0         0 local $@;
480 0         0 my @list = CORE::eval $list;
481 0         0 for (@list) {
482 0 0       0 $Esjis::function_ord = 'Sjis::ord' if /\A ord \z/oxms;
483 0 0       0 $Esjis::function_ord_ = 'Sjis::ord_' if /\A ord \z/oxms;
484 0 0       0 $Esjis::function_reverse = 'Sjis::reverse' if /\A reverse \z/oxms;
485 385 0       1604 $Esjis::function_getc = 'Sjis::getc' if /\A getc \z/oxms;
486             }
487             }
488             }
489             }
490              
491 385         4123 $e_script .= Sjis::escape();
492              
493             return $e_script;
494             }
495              
496             1;
497              
498             __END__