File Coverage

blib/lib/App/ChangeShebang.pm
Criterion Covered Total %
statement 57 65 87.6
branch 13 24 54.1
condition 1 3 33.3
subroutine 12 15 80.0
pod 0 6 0.0
total 83 113 73.4


line stmt bran cond sub pod time code
1             package App::ChangeShebang;
2 2     2   75401 use strict;
  2         12  
  2         48  
3 2     2   10 use warnings;
  2         23  
  2         42  
4 2     2   8 use utf8;
  2         2  
  2         9  
5 2     2   1215 use Getopt::Long qw(:config no_auto_abbrev no_ignore_case bundling);
  2         16338  
  2         8  
6 2     2   1155 use Pod::Usage 'pod2usage';
  2         77201  
  2         174  
7             require ExtUtils::MakeMaker;
8 2     2   16 use File::Basename 'dirname';
  2         5  
  2         122  
9 2     2   732 use File::Temp 'tempfile';
  2         13332  
  2         1310  
10 0     0 0 0 sub prompt { ExtUtils::MakeMaker::prompt(@_) }
11              
12             our $VERSION = '0.07';
13              
14             sub new {
15 4     4 0 13687 my $class = shift;
16 4         45 bless {@_}, $class;
17             }
18             sub parse_options {
19 4     4 0 7 my $self = shift;
20 4         13 local @ARGV = @_;
21             GetOptions
22 0     0   0 "version|v" => sub { printf "%s %s\n", __PACKAGE__, $VERSION; exit },
  0         0  
23             "quiet|q" => \$self->{quiet},
24             "force|f" => \$self->{force},
25 0     0   0 "help|h" => sub { pod2usage(0) },
26 4 50       32 or pod2usage(1);
27              
28 4         1710 my @file = @ARGV;
29 4 50       10 unless (@file) {
30 0         0 warn "Missing file arguments.\n";
31 0         0 pod2usage(1);
32             }
33 4         21 $self->{file} = \@file;
34 4         20 $self;
35             }
36              
37             sub run {
38 4     4 0 5 my $self = shift;
39 4         5 for my $file (@{ $self->{file} }) {
  4         9  
40 12 50 33     291 next unless -f $file && !-l $file;
41 12 100       40 next unless $self->is_perl_shebang( $file );
42 11 50       32 unless ($self->{force}) {
43 0         0 my $anser = prompt "change shebang line of $file? (y/N)", "N";
44 0 0       0 next if $anser !~ /^y(es)?$/i;
45             }
46 11         26 $self->change_shebang($file);
47 11 50       58 warn "changed shebang line of $file\n" unless $self->{quiet};
48             }
49             }
50              
51             sub is_perl_shebang {
52 12     12 0 23 my ($self, $file) = @_;
53 12 50       335 open my $fh, "<:raw", $file or die "open $file: $!\n";
54 12 50       163 read $fh, my $first, 100 or die "read $file: $!\n";
55 12 100       214 return $first =~ /^#!([^\n]*)perl/ ? 1 : 0;
56             }
57              
58             my $remove = do {
59             my $s = qr/[ \t]*/;
60             my $w = qr/[^\n]*/;
61             my $running_under_some_shell = qr/\n*
62             $s eval $s ['"] exec $w \n
63             $s if $s (?:0|\$running_under_some_shell) $w \n
64             /xsm;
65             my $shebang = qr/\n*
66             \#! $w \n
67             /xsm;
68             qr/\A(?:$running_under_some_shell|$shebang)+/;
69             };
70              
71             sub change_shebang {
72 11     11 0 22 my ($self, $file) = @_;
73 11         12 my $content = do {
74 11 50       289 open my $fh, "<:raw", $file or die "open $file: $!\n";
75 11         46 local $/; <$fh>;
  11         270  
76             };
77              
78 11         118 $content =~ s/$remove//;
79              
80 11         114 my $mode = (stat $file)[2];
81              
82 11         343 my ($tmp_fh, $tmp_name) = tempfile UNLINK => 0, DIR => dirname($file);
83 11         2824 chmod $mode, $tmp_name;
84 11         22 print {$tmp_fh} <<'...';
  11         52  
85             #!/bin/sh
86             exec "$(dirname "$0")"/perl -x "$0" "$@"
87             #!perl
88             ...
89 11         15 print {$tmp_fh} $content;
  11         14  
90 11         230 close $tmp_fh;
91 11 50       729 rename $tmp_name, $file or die "rename $tmp_name, $file: $!\n";
92             }
93              
94              
95             1;
96             __END__