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   50454 use strict;
  2         4  
  2         79  
3 2     2   8 use warnings;
  2         3  
  2         61  
4 2     2   17 use utf8;
  2         3  
  2         11  
5 2     2   1848 use Getopt::Long qw(:config no_auto_abbrev no_ignore_case bundling);
  2         27944  
  2         14  
6 2     2   2634 use Pod::Usage 'pod2usage';
  2         113926  
  2         283  
7             require ExtUtils::MakeMaker;
8 2     2   24 use File::Basename 'dirname';
  2         4  
  2         157  
9 2     2   7415 use File::Temp 'tempfile';
  2         20351  
  2         1869  
10 0     0 0 0 sub prompt { ExtUtils::MakeMaker::prompt(@_) }
11              
12             our $VERSION = '0.06';
13              
14             sub new {
15 4     4 0 17200 my $class = shift;
16 4         64 bless {@_}, $class;
17             }
18             sub parse_options {
19 4     4 0 9 my $self = shift;
20 4         17 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       66 or pod2usage(1);
27              
28 4         2054 my @file = @ARGV;
29 4 50       15 unless (@file) {
30 0         0 warn "Missing file arguments.\n";
31 0         0 pod2usage(1);
32             }
33 4         14 $self->{file} = \@file;
34 4         24 $self;
35             }
36              
37             sub run {
38 4     4 0 5 my $self = shift;
39 4         8 for my $file (@{ $self->{file} }) {
  4         13  
40 12 50 33     347 next unless -f $file && !-l $file;
41 12 100       47 next unless $self->is_perl_shebang( $file );
42 11 50       35 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         33 $self->change_shebang($file);
47 11 50       51 warn "changed shebang line of $file\n" unless $self->{quiet};
48             }
49             }
50              
51             sub is_perl_shebang {
52 12     12 0 19 my ($self, $file) = @_;
53 12 50       376 open my $fh, "<:raw", $file or die "open $file: $!\n";
54 12 50       123 read $fh, my $first, 100 or die "read $file: $!\n";
55 12 100       190 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 16 my ($self, $file) = @_;
73 11         10 my $content = do {
74 11 50       289 open my $fh, "<:raw", $file or die "open $file: $!\n";
75 11         45 local $/; <$fh>;
  11         182  
76             };
77              
78 11         118 $content =~ s/$remove//;
79              
80 11         330 my $mode = (stat $file)[2];
81              
82 11         488 my ($tmp_fh, $tmp_name) = tempfile UNLINK => 0, DIR => dirname($file);
83 11         4192 chmod $mode, $tmp_name;
84 11         17 print {$tmp_fh} <<'...';
  11         60  
85             #!/bin/sh
86             exec "$(dirname "$0")"/perl -x "$0" "$@"
87             #!perl
88             ...
89 11         15 print {$tmp_fh} $content;
  11         17  
90 11         364 close $tmp_fh;
91 11 50       878 rename $tmp_name, $file or die "rename $tmp_name, $file: $!\n";
92             }
93              
94              
95             1;
96             __END__