File Coverage

blib/lib/Archive/SelfExtract.pm
Criterion Covered Total %
statement 67 94 71.2
branch 5 20 25.0
condition n/a
subroutine 14 17 82.3
pod 2 3 66.6
total 88 134 65.6


line stmt bran cond sub pod time code
1             =pod
2              
3             =head1 NAME
4              
5             Archive::SelfExtract - bundle compressed archives with Perl code
6              
7             =head1 SYNOPSIS
8              
9             use Archive::SelfExtract;
10            
11             # writes output script to STDOUT
12             Archive::SelfExtract::createExtractor( "perlcode.pl", "somefiles.zip" );
13            
14             # with various options:
15             Archive::SelfExtract::createExtractor( "perlcode.pl", "somefiles.zip",
16             perlbin => "/opt/perl58/bin/perl",
17             output_fh => $someFileHandle,
18             );
19              
20             See also the command line tool, L.
21              
22             =cut
23              
24             package Archive::SelfExtract;
25              
26 1     1   81256 use strict;
  1         2  
  1         35  
27             # implicit:
28 1     1   17187 use Compress::Zlib;
  1         265613  
  1         1057  
29 1     1   15 use File::Spec;
  1         8  
  1         43  
30             # explicit:
31 1     1   11568 use Archive::Zip qw( :ERROR_CODES :CONSTANTS );
  1         127360  
  1         589  
32 1     1   13 use File::Temp qw(tempdir);
  1         3  
  1         72  
33 1     1   9 use File::Path qw(mkpath rmtree);
  1         2  
  1         149  
34 1     1   2032 use IO::Scalar;
  1         16175  
  1         363  
35 1     1   12 use Carp;
  1         2  
  1         395  
36              
37             our $VERSION = '1.3';
38              
39             # $Tempdir may be set before calling extract() to control where the
40             # zipfile is extracted to.
41             our $Tempdir;
42              
43 1     1   8 use constant QUIET => 0;
  1         3  
  1         84  
44 1     1   5 use constant STD => 1;
  1         3  
  1         44  
45 1     1   5 use constant VERBOSE => 2;
  1         2  
  1         40  
46 1     1   5 use constant NOISY => 3;
  1         2  
  1         41  
47 1     1   5 use constant DEBUG => 4;
  1         3  
  1         2036  
48              
49             # $DebugLevel may be set before calling extract() to control how verbose
50             # the extraction process is.
51             our $DebugLevel = STD;
52              
53             sub out {
54 0     0 0 0 my($level, $msg) = @_;
55 0 0       0 if ( $DebugLevel >= $level ) {
56 0         0 print "$msg\n";
57             }
58             }
59              
60             sub createExtractor {
61 1     1 1 2337 my( $scriptfn, $zipfn, %options ) = @_;
62              
63 1         3 my $perlbin = "/usr/bin/perl";
64 1 50       8 if ( exists($options{perlbin}) ) {
65 0         0 $perlbin = delete $options{perlbin};
66             }
67 1         2 my $out;
68 1 50       6 if ( exists($options{output_fh}) ) {
69 1         5 $out = delete $options{output_fh};
70             } else {
71 0         0 $out = \*STDOUT;
72             }
73 1 50       5 if ( %options ) {
74 0         0 croak "Unknown options (", join(",",keys %options), ") passed to createExtractor";
75             }
76              
77 1 50       53 open(my $script, "$scriptfn") ||
78             croak "Can't read script file $scriptfn ($!)";
79 1 50       33 open(my $zipdata, "$zipfn") ||
80             croak "Can't read zip file $zipfn ($!)";
81              
82 1         6 local $/=undef;
83              
84 1         21 print $out "#!$ {perlbin}\n";
85 1         3 print $out "\n";
86 1         3 print $out q{use warnings;}, "\n";
87 1         3 print $out q{use strict;}, "\n";
88 1         3 print $out q{use Archive::SelfExtract;}, "\n";
89 1         4 print $out q{Archive::SelfExtract::_extract(\*DATA);}, "\n";
90 1         3 print $out q{#}, "\n";
91 1         3 print $out q{# Start user script}, "\n";
92 1         3 print $out q{#}, "\n";
93 1         3 print $out "\n";
94 1         50 print $out +<$script>;
95 1         3 print $out "\n";
96 1         4 print $out q{#}, "\n";
97 1         3 print $out q{# End user script}, "\n";
98 1         3 print $out q{#}, "\n";
99 1         2 print $out q{__DATA__}, "\n";
100             # turn binmode on now: print raw data instead of text
101 1         53 binmode($out);
102 1         77 print $out scalar(<$zipdata>);
103              
104             }
105              
106             sub _extract {
107 0     0     my($fh) = @_;
108 0 0         if (defined($Tempdir)) {
109 0           out(DEBUG, "Verifying existance of tempdir $Tempdir");
110 0 0         mkpath($Tempdir, ($DebugLevel>=DEBUG), 0755) ||
111             croak "Could not create temporary directory (\$Tempdir) '$Tempdir' ($!)";
112             } else {
113 0           out(DEBUG, "Creating tempdir");
114 0           $Tempdir = tempdir();
115             }
116 0           out(STD, "Extracting into $Tempdir...");
117 0           my $arc = Archive::Zip->new();
118 0           out(DEBUG, "Reading from DATA into memory");
119 0           my $data = do {
120 0           local $/ = undef;
121 0           binmode($fh);
122 0           <$fh>;
123             };
124             # The alternative to this is to write a fh wrapper which
125             # i can open on $0 and which will "ignore" the "header" (script).
126 0           my $rwhandle = IO::Scalar->new( \$data );
127 0 0         if ( AZ_OK==$arc->readFromFileHandle( $rwhandle ) ) {
128 0           out(VERBOSE, "Unpacking ".$arc->numberOfMembers()." files");
129             # first param must be undef (not ""!) to extract all files
130             # second param must end with a "/", since AZ just concats the names
131 0           $arc->extractTree( undef, "$ {Tempdir}/" );
132             } else {
133 0           croak "Could not read zipfile data ($!)";
134             }
135 0           out(STD, "OK");
136             }
137              
138             # Remove the tempdir (and all its contents)
139             sub cleanup {
140 0 0   0 1   if ( -d $Tempdir ) {
141 0           rmtree( $Tempdir, ($DebugLevel>=DEBUG), 1);
142             }
143             }
144              
145             1;
146             __END__