Re: [perl #46011] [RESOLVED] overload "0+" doesn't handle integer results
[p5sagit/p5-mst-13.2.git] / lib / Archive / Extract.pm
1 package Archive::Extract;
2
3 use strict;
4
5 use Cwd                         qw[cwd];
6 use Carp                        qw[carp];
7 use IPC::Cmd                    qw[run can_run];
8 use FileHandle;
9 use File::Path                  qw[mkpath];
10 use File::Spec;
11 use File::Basename              qw[dirname basename];
12 use Params::Check               qw[check];
13 use Module::Load::Conditional   qw[can_load check_install];
14 use Locale::Maketext::Simple    Style => 'gettext';
15
16 ### solaris has silly /bin/tar output ###
17 use constant ON_SOLARIS     => $^O eq 'solaris' ? 1 : 0;
18 use constant FILE_EXISTS    => sub { -e $_[0] ? 1 : 0 };
19
20 ### VMS may require quoting upper case command options
21 use constant ON_VMS         => $^O eq 'VMS' ? 1 : 0;
22
23 ### If these are changed, update @TYPES and the new() POD
24 use constant TGZ            => 'tgz';
25 use constant TAR            => 'tar';
26 use constant GZ             => 'gz';
27 use constant ZIP            => 'zip';
28 use constant BZ2            => 'bz2';
29 use constant TBZ            => 'tbz';
30 use constant Z              => 'Z';
31
32 use vars qw[$VERSION $PREFER_BIN $PROGRAMS $WARN $DEBUG];
33
34 $VERSION        = '0.24';
35 $PREFER_BIN     = 0;
36 $WARN           = 1;
37 $DEBUG          = 0;
38 my @Types       = ( TGZ, TAR, GZ, ZIP, BZ2, TBZ, Z ); # same as all constants
39
40 local $Params::Check::VERBOSE = $Params::Check::VERBOSE = 1;
41
42 =pod
43
44 =head1 NAME
45
46 Archive::Extract - A generic archive extracting mechanism
47
48 =head1 SYNOPSIS
49
50     use Archive::Extract;
51
52     ### build an Archive::Extract object ###
53     my $ae = Archive::Extract->new( archive => 'foo.tgz' );
54
55     ### extract to cwd() ###
56     my $ok = $ae->extract;
57
58     ### extract to /tmp ###
59     my $ok = $ae->extract( to => '/tmp' );
60
61     ### what if something went wrong?
62     my $ok = $ae->extract or die $ae->error;
63
64     ### files from the archive ###
65     my $files   = $ae->files;
66
67     ### dir that was extracted to ###
68     my $outdir  = $ae->extract_path;
69
70
71     ### quick check methods ###
72     $ae->is_tar     # is it a .tar file?
73     $ae->is_tgz     # is it a .tar.gz or .tgz file?
74     $ae->is_gz;     # is it a .gz file?
75     $ae->is_zip;    # is it a .zip file?
76     $ae->is_bz2;    # is it a .bz2 file?
77     $ae->is_tbz;    # is it a .tar.bz2 or .tbz file?
78
79     ### absolute path to the archive you provided ###
80     $ae->archive;
81
82     ### commandline tools, if found ###
83     $ae->bin_tar     # path to /bin/tar, if found
84     $ae->bin_gzip    # path to /bin/gzip, if found
85     $ae->bin_unzip   # path to /bin/unzip, if found
86     $ae->bin_bunzip2 # path to /bin/bunzip2 if found
87
88 =head1 DESCRIPTION
89
90 Archive::Extract is a generic archive extraction mechanism.
91
92 It allows you to extract any archive file of the type .tar, .tar.gz,
93 .gz, .Z, tar.bz2, .tbz, .bz2 or .zip without having to worry how it 
94 does so, or use different interfaces for each type by using either 
95 perl modules, or commandline tools on your system.
96
97 See the C<HOW IT WORKS> section further down for details.
98
99 =cut
100
101
102 ### see what /bin/programs are available ###
103 $PROGRAMS = {};
104 for my $pgm (qw[tar unzip gzip bunzip2 uncompress]) {
105     $PROGRAMS->{$pgm} = can_run($pgm);
106 }
107
108 ### mapping from types to extractor methods ###
109 my $Mapping = {
110     is_tgz  => '_untar',
111     is_tar  => '_untar',
112     is_gz   => '_gunzip',
113     is_zip  => '_unzip',
114     is_tbz  => '_untar',
115     is_bz2  => '_bunzip2',
116     is_Z    => '_uncompress',
117 };
118
119 {
120     my $tmpl = {
121         archive => { required => 1, allow => FILE_EXISTS },
122         type    => { default => '', allow => [ @Types ] },
123     };
124
125     ### build accesssors ###
126     for my $method( keys %$tmpl, 
127                     qw[_extractor _gunzip_to files extract_path],
128                     qw[_error_msg _error_msg_long]
129     ) {
130         no strict 'refs';
131         *$method = sub {
132                         my $self = shift;
133                         $self->{$method} = $_[0] if @_;
134                         return $self->{$method};
135                     }
136     }
137
138 =head1 METHODS
139
140 =head2 $ae = Archive::Extract->new(archive => '/path/to/archive',[type => TYPE])
141
142 Creates a new C<Archive::Extract> object based on the archive file you
143 passed it. Automatically determines the type of archive based on the
144 extension, but you can override that by explicitly providing the
145 C<type> argument.
146
147 Valid values for C<type> are:
148
149 =over 4
150
151 =item tar
152
153 Standard tar files, as produced by, for example, C</bin/tar>.
154 Corresponds to a C<.tar> suffix.
155
156 =item tgz
157
158 Gzip compressed tar files, as produced by, for example C</bin/tar -z>.
159 Corresponds to a C<.tgz> or C<.tar.gz> suffix.
160
161 =item gz
162
163 Gzip compressed file, as produced by, for example C</bin/gzip>.
164 Corresponds to a C<.gz> suffix.
165
166 =item Z
167
168 Lempel-Ziv compressed file, as produced by, for example C</bin/compress>.
169 Corresponds to a C<.Z> suffix.
170
171 =item zip
172
173 Zip compressed file, as produced by, for example C</bin/zip>.
174 Corresponds to a C<.zip>, C<.jar> or C<.par> suffix.
175
176 =item bz2
177
178 Bzip2 compressed file, as produced by, for example, C</bin/bzip2>.
179 Corresponds to a C<.bz2> suffix.
180
181 =item tbz
182
183 Bzip2 compressed tar file, as produced by, for exmample C</bin/tar -j>.
184 Corresponds to a C<.tbz> or C<.tar.bz2> suffix.
185
186 =back
187
188 Returns a C<Archive::Extract> object on success, or false on failure.
189
190 =cut
191
192     ### constructor ###
193     sub new {
194         my $class   = shift;
195         my %hash    = @_;
196
197         my $parsed = check( $tmpl, \%hash ) or return;
198
199         ### make sure we have an absolute path ###
200         my $ar = $parsed->{archive} = File::Spec->rel2abs( $parsed->{archive} );
201
202         ### figure out the type, if it wasn't already specified ###
203         unless ( $parsed->{type} ) {
204             $parsed->{type} =
205                 $ar =~ /.+?\.(?:tar\.gz|tgz)$/i     ? TGZ   :
206                 $ar =~ /.+?\.gz$/i                  ? GZ    :
207                 $ar =~ /.+?\.tar$/i                 ? TAR   :
208                 $ar =~ /.+?\.(zip|jar|par)$/i       ? ZIP   :
209                 $ar =~ /.+?\.(?:tbz2?|tar\.bz2?)$/i ? TBZ   :
210                 $ar =~ /.+?\.bz2$/i                 ? BZ2   :
211                 $ar =~ /.+?\.Z$/                    ? Z     :
212                 '';
213
214         }
215
216         ### don't know what type of file it is ###
217         return __PACKAGE__->_error(loc("Cannot determine file type for '%1'",
218                                 $parsed->{archive} )) unless $parsed->{type};
219
220         return bless $parsed, $class;
221     }
222 }
223
224 =head2 $ae->extract( [to => '/output/path'] )
225
226 Extracts the archive represented by the C<Archive::Extract> object to
227 the path of your choice as specified by the C<to> argument. Defaults to
228 C<cwd()>.
229
230 Since C<.gz> files never hold a directory, but only a single file; if 
231 the C<to> argument is an existing directory, the file is extracted 
232 there, with it's C<.gz> suffix stripped. 
233 If the C<to> argument is not an existing directory, the C<to> argument 
234 is understood to be a filename, if the archive type is C<gz>. 
235 In the case that you did not specify a C<to> argument, the output
236 file will be the name of the archive file, stripped from it's C<.gz>
237 suffix, in the current working directory.
238
239 C<extract> will try a pure perl solution first, and then fall back to
240 commandline tools if they are available. See the C<GLOBAL VARIABLES>
241 section below on how to alter this behaviour.
242
243 It will return true on success, and false on failure.
244
245 On success, it will also set the follow attributes in the object:
246
247 =over 4
248
249 =item $ae->extract_path
250
251 This is the directory that the files where extracted to.
252
253 =item $ae->files
254
255 This is an array ref with the paths of all the files in the archive,
256 relative to the C<to> argument you specified.
257 To get the full path to an extracted file, you would use:
258
259     File::Spec->catfile( $to, $ae->files->[0] );
260
261 Note that all files from a tar archive will be in unix format, as per
262 the tar specification.
263
264 =back
265
266 =cut
267
268 sub extract {
269     my $self = shift;
270     my %hash = @_;
271
272     my $to;
273     my $tmpl = {
274         to  => { default => '.', store => \$to }
275     };
276
277     check( $tmpl, \%hash ) or return;
278
279     ### so 'to' could be a file or a dir, depending on whether it's a .gz 
280     ### file, or basically anything else.
281     ### so, check that, then act accordingly.
282     ### set an accessor specifically so _gunzip can know what file to extract
283     ### to.
284     my $dir;
285     {   ### a foo.gz file
286         if( $self->is_gz or $self->is_bz2 or $self->is_Z) {
287     
288             my $cp = $self->archive; $cp =~ s/\.(?:gz|bz2?|Z)$//i;
289         
290             ### to is a dir?
291             if ( -d $to ) {
292                 $dir = $to; 
293                 $self->_gunzip_to( basename($cp) );
294
295             ### then it's a filename
296             } else {
297                 $dir = dirname($to);
298                 $self->_gunzip_to( basename($to) );
299             }
300
301         ### not a foo.gz file
302         } else {
303             $dir = $to;
304         }
305     }
306
307     ### make the dir if it doesn't exist ###
308     unless( -d $dir ) {
309         eval { mkpath( $dir ) };
310
311         return $self->_error(loc("Could not create path '%1': %2", $dir, $@))
312             if $@;
313     }
314
315     ### get the current dir, to restore later ###
316     my $cwd = cwd();
317
318     my $ok = 1;
319     EXTRACT: {
320
321         ### chdir to the target dir ###
322         unless( chdir $dir ) {
323             $self->_error(loc("Could not chdir to '%1': %2", $dir, $!));
324             $ok = 0; last EXTRACT;
325         }
326
327         ### set files to an empty array ref, so there's always an array
328         ### ref IN the accessor, to avoid errors like:
329         ### Can't use an undefined value as an ARRAY reference at
330         ### ../lib/Archive/Extract.pm line 742. (rt #19815)
331         $self->files( [] );
332
333         ### find what extractor method to use ###
334         while( my($type,$method) = each %$Mapping ) {
335
336             ### call the corresponding method if the type is OK ###
337             if( $self->$type) {
338                 $ok = $self->$method();
339             }
340         }
341
342         ### warn something went wrong if we didn't get an OK ###
343         $self->_error(loc("Extract failed, no extractor found"))
344             unless $ok;
345
346     }
347
348     ### and chdir back ###
349     unless( chdir $cwd ) {
350         $self->_error(loc("Could not chdir back to start dir '%1': %2'",
351                             $cwd, $!));
352     }
353
354     return $ok;
355 }
356
357 =pod
358
359 =head1 ACCESSORS
360
361 =head2 $ae->error([BOOL])
362
363 Returns the last encountered error as string.
364 Pass it a true value to get the C<Carp::longmess()> output instead.
365
366 =head2 $ae->extract_path
367
368 This is the directory the archive got extracted to.
369 See C<extract()> for details.
370
371 =head2 $ae->files
372
373 This is an array ref holding all the paths from the archive.
374 See C<extract()> for details.
375
376 =head2 $ae->archive
377
378 This is the full path to the archive file represented by this
379 C<Archive::Extract> object.
380
381 =head2 $ae->type
382
383 This is the type of archive represented by this C<Archive::Extract>
384 object. See accessors below for an easier way to use this.
385 See the C<new()> method for details.
386
387 =head2 $ae->types
388
389 Returns a list of all known C<types> for C<Archive::Extract>'s
390 C<new> method.
391
392 =cut
393
394 sub types { return @Types }
395
396 =head2 $ae->is_tgz
397
398 Returns true if the file is of type C<.tar.gz>.
399 See the C<new()> method for details.
400
401 =head2 $ae->is_tar
402
403 Returns true if the file is of type C<.tar>.
404 See the C<new()> method for details.
405
406 =head2 $ae->is_gz
407
408 Returns true if the file is of type C<.gz>.
409 See the C<new()> method for details.
410
411 =head2 $ae->is_Z
412
413 Returns true if the file is of type C<.Z>.
414 See the C<new()> method for details.
415
416 =head2 $ae->is_zip
417
418 Returns true if the file is of type C<.zip>.
419 See the C<new()> method for details.
420
421 =cut
422
423 ### quick check methods ###
424 sub is_tgz  { return $_[0]->type eq TGZ }
425 sub is_tar  { return $_[0]->type eq TAR }
426 sub is_gz   { return $_[0]->type eq GZ  }
427 sub is_zip  { return $_[0]->type eq ZIP }
428 sub is_tbz  { return $_[0]->type eq TBZ }
429 sub is_bz2  { return $_[0]->type eq BZ2 }
430 sub is_Z    { return $_[0]->type eq Z   }
431
432 =pod
433
434 =head2 $ae->bin_tar
435
436 Returns the full path to your tar binary, if found.
437
438 =head2 $ae->bin_gzip
439
440 Returns the full path to your gzip binary, if found
441
442 =head2 $ae->bin_unzip
443
444 Returns the full path to your unzip binary, if found
445
446 =cut
447
448 ### paths to commandline tools ###
449 sub bin_gzip        { return $PROGRAMS->{'gzip'}    if $PROGRAMS->{'gzip'}  }
450 sub bin_unzip       { return $PROGRAMS->{'unzip'}   if $PROGRAMS->{'unzip'} }
451 sub bin_tar         { return $PROGRAMS->{'tar'}     if $PROGRAMS->{'tar'}   }
452 sub bin_bunzip2     { return $PROGRAMS->{'bunzip2'} if $PROGRAMS->{'bunzip2'} }
453 sub bin_uncompress  { return $PROGRAMS->{'uncompress'} 
454                                                  if $PROGRAMS->{'uncompress'} }
455 =head2 $bool = $ae->have_old_bunzip2
456
457 Older versions of C</bin/bunzip2>, from before the C<bunzip2 1.0> release,
458 require all archive names to end in C<.bz2> or it will not extract
459 them. This method checks if you have a recent version of C<bunzip2>
460 that allows any extension, or an older one that doesn't.
461
462 =cut
463
464 sub have_old_bunzip2 {
465     my $self = shift;
466
467     ### no bunzip2? no old bunzip2 either :)
468     return unless $self->bin_bunzip2;
469
470     ### if we can't run this, we can't be sure if it's too old or not    
471     ### XXX stupid stupid stupid bunzip2 doesn't understand --version
472     ### is not a request to extract data:
473     ### $ bunzip2 --version
474     ### bzip2, a block-sorting file compressor.  Version 1.0.2, 30-Dec-2001.
475     ### [...]
476     ### bunzip2: I won't read compressed data from a terminal.
477     ### bunzip2: For help, type: `bunzip2 --help'.
478     ### $ echo $?
479     ### 1
480     ### HATEFUL!
481     my $buffer;
482     scalar run( command => [$self->bin_bunzip2, '--version'],
483          verbose => 0,
484          buffer  => \$buffer
485     );
486
487     ### no output
488     return unless $buffer;
489     
490     my ($version) = $buffer =~ /version \s+ (\d+)/ix;
491
492     return 1 if $version < 1;
493     return;
494 }
495
496 #################################
497 #
498 # Untar code
499 #
500 #################################
501
502
503 ### untar wrapper... goes to either Archive::Tar or /bin/tar
504 ### depending on $PREFER_BIN
505 sub _untar {
506     my $self = shift;
507
508     ### bzip2 support in A::T via IO::Uncompress::Bzip2
509     my   @methods = qw[_untar_at _untar_bin];
510          @methods = reverse @methods if $PREFER_BIN;
511
512     for my $method (@methods) {
513         $self->_extractor($method) && return 1 if $self->$method();
514     }
515
516     return $self->_error(loc("Unable to untar file '%1'", $self->archive));
517 }
518
519 ### use /bin/tar to extract ###
520 sub _untar_bin {
521     my $self = shift;
522
523     ### check for /bin/tar ###
524     return $self->_error(loc("No '%1' program found", '/bin/tar'))
525         unless $self->bin_tar;
526
527     ### check for /bin/gzip if we need it ###
528     return $self->_error(loc("No '%1' program found", '/bin/gzip'))
529         if $self->is_tgz && !$self->bin_gzip;
530
531     return $self->_error(loc("No '%1' program found", '/bin/bunzip2'))
532         if $self->is_tbz && !$self->bin_bunzip2;
533
534     ### XXX figure out how to make IPC::Run do this in one call --
535     ### currently i don't know how to get output of a command after a pipe
536     ### trapped in a scalar. Mailed barries about this 5th of june 2004.
537
538
539
540     ### see what command we should run, based on whether
541     ### it's a .tgz or .tar
542
543     ### XXX solaris tar and bsdtar are having different outputs
544     ### depending whether you run with -x or -t
545     ### compensate for this insanity by running -t first, then -x
546     {    my $cmd = 
547             $self->is_tgz ? [$self->bin_gzip, '-cdf', $self->archive, '|',
548                              $self->bin_tar, '-tf', '-'] :
549             $self->is_tbz ? [$self->bin_bunzip2, '-cd', $self->archive, '|',                             
550                              $self->bin_tar, '-tf', '-'] :
551             [$self->bin_tar, '-tf', $self->archive];
552
553         ### run the command ###
554         my $buffer = '';
555         unless( scalar run( command => $cmd,
556                             buffer  => \$buffer,
557                             verbose => $DEBUG )
558         ) {
559             return $self->_error(loc(
560                             "Error listing contents of archive '%1': %2",
561                             $self->archive, $buffer ));
562         }
563
564         ### no buffers available?
565         if( !IPC::Cmd->can_capture_buffer and !$buffer ) {
566             $self->_error( $self->_no_buffer_files( $self->archive ) );
567         
568         } else {
569             ### if we're on solaris we /might/ be using /bin/tar, which has
570             ### a weird output format... we might also be using
571             ### /usr/local/bin/tar, which is gnu tar, which is perfectly
572             ### fine... so we have to do some guessing here =/
573             my @files = map { chomp;
574                           !ON_SOLARIS ? $_
575                                       : (m|^ x \s+  # 'xtract' -- sigh
576                                             (.+?),  # the actual file name
577                                             \s+ [\d,.]+ \s bytes,
578                                             \s+ [\d,.]+ \s tape \s blocks
579                                         |x ? $1 : $_);
580
581                     } split $/, $buffer;
582
583             ### store the files that are in the archive ###
584             $self->files(\@files);
585         }
586     }
587
588     ### now actually extract it ###
589     {   my $cmd = 
590             $self->is_tgz ? [$self->bin_gzip, '-cdf', $self->archive, '|',
591                              $self->bin_tar, '-xf', '-'] :
592             $self->is_tbz ? [$self->bin_bunzip2, '-cd', $self->archive, '|',                             
593                              $self->bin_tar, '-xf', '-'] :
594             [$self->bin_tar, '-xf', $self->archive];
595
596         my $buffer = '';
597         unless( scalar run( command => $cmd,
598                             buffer  => \$buffer,
599                             verbose => $DEBUG )
600         ) {
601             return $self->_error(loc("Error extracting archive '%1': %2",
602                             $self->archive, $buffer ));
603         }
604
605         ### we might not have them, due to lack of buffers
606         if( $self->files ) {
607             ### now that we've extracted, figure out where we extracted to
608             my $dir = $self->__get_extract_dir( $self->files );
609     
610             ### store the extraction dir ###
611             $self->extract_path( $dir );
612         }
613     }
614
615     ### we got here, no error happened
616     return 1;
617 }
618
619 ### use archive::tar to extract ###
620 sub _untar_at {
621     my $self = shift;
622
623     ### we definitely need A::T, so load that first
624     {   my $use_list = { 'Archive::Tar' => '0.0' };
625
626         unless( can_load( modules => $use_list ) ) {
627
628             return $self->_error(loc("You do not have '%1' installed - " .
629                                  "Please install it as soon as possible.",
630                                  'Archive::Tar'));
631         }
632     }
633
634     ### we might pass it a filehandle if it's a .tbz file..
635     my $fh_to_read = $self->archive;
636
637     ### we will need Compress::Zlib too, if it's a tgz... and IO::Zlib
638     ### if A::T's version is 0.99 or higher
639     if( $self->is_tgz ) {
640         my $use_list = { 'Compress::Zlib' => '0.0' };
641            $use_list->{ 'IO::Zlib' } = '0.0'
642                 if $Archive::Tar::VERSION >= '0.99';
643
644         unless( can_load( modules => $use_list ) ) {
645             my $which = join '/', sort keys %$use_list;
646
647             return $self->_error(loc(
648                                 "You do not have '%1' installed - Please ".
649                                 "install it as soon as possible.", $which));
650
651         }
652     } elsif ( $self->is_tbz ) {
653         my $use_list = { 'IO::Uncompress::Bunzip2' => '0.0' };
654         unless( can_load( modules => $use_list ) ) {
655             return $self->_error(loc(
656                     "You do not have '%1' installed - Please " .
657                     "install it as soon as possible.", 
658                      'IO::Uncompress::Bunzip2'));
659         }
660
661         my $bz = IO::Uncompress::Bunzip2->new( $self->archive ) or
662             return $self->_error(loc("Unable to open '%1': %2",
663                             $self->archive,
664                             $IO::Uncompress::Bunzip2::Bunzip2Error));
665
666         $fh_to_read = $bz;
667     }
668
669     my $tar = Archive::Tar->new();
670
671     ### only tell it it's compressed if it's a .tgz, as we give it a file
672     ### handle if it's a .tbz
673     unless( $tar->read( $fh_to_read, ( $self->is_tgz ? 1 : 0 ) ) ) {
674         return $self->_error(loc("Unable to read '%1': %2", $self->archive,
675                                     $Archive::Tar::error));
676     }
677
678     ### workaround to prevent Archive::Tar from setting uid, which
679     ### is a potential security hole. -autrijus
680     ### have to do it here, since A::T needs to be /loaded/ first ###
681     {   no strict 'refs'; local $^W;
682
683         ### older versions of archive::tar <= 0.23
684         *Archive::Tar::chown = sub {};
685     }
686
687     ### for version of archive::tar > 1.04
688     local $Archive::Tar::Constant::CHOWN = 0;
689
690     {   local $^W;  # quell 'splice() offset past end of array' warnings
691                     # on older versions of A::T
692
693         ### older archive::tar always returns $self, return value slightly
694         ### fux0r3d because of it.
695         $tar->extract()
696             or return $self->_error(loc("Unable to extract '%1': %2",
697                                     $self->archive, $Archive::Tar::error ));
698     }
699
700     my @files   = $tar->list_files;
701     my $dir     = $self->__get_extract_dir( \@files );
702
703     ### store the files that are in the archive ###
704     $self->files(\@files);
705
706     ### store the extraction dir ###
707     $self->extract_path( $dir );
708
709     ### check if the dir actually appeared ###
710     return 1 if -d $self->extract_path;
711
712     ### no dir, we failed ###
713     return $self->_error(loc("Unable to extract '%1': %2",
714                                 $self->archive, $Archive::Tar::error ));
715 }
716
717 #################################
718 #
719 # Gunzip code
720 #
721 #################################
722
723 ### gunzip wrapper... goes to either Compress::Zlib or /bin/gzip
724 ### depending on $PREFER_BIN
725 sub _gunzip {
726     my $self = shift;
727
728     my @methods = qw[_gunzip_cz _gunzip_bin];
729        @methods = reverse @methods if $PREFER_BIN;
730
731     for my $method (@methods) {
732         $self->_extractor($method) && return 1 if $self->$method();
733     }
734
735     return $self->_error(loc("Unable to gunzip file '%1'", $self->archive));
736 }
737
738 sub _gunzip_bin {
739     my $self = shift;
740
741     ### check for /bin/gzip -- we need it ###
742     return $self->_error(loc("No '%1' program found", '/bin/gzip'))
743         unless $self->bin_gzip;
744
745
746     my $fh = FileHandle->new('>'. $self->_gunzip_to) or
747         return $self->_error(loc("Could not open '%1' for writing: %2",
748                             $self->_gunzip_to, $! ));
749
750     my $cmd = [ $self->bin_gzip, '-cdf', $self->archive ];
751
752     my $buffer;
753     unless( scalar run( command => $cmd,
754                         verbose => $DEBUG,
755                         buffer  => \$buffer )
756     ) {
757         return $self->_error(loc("Unable to gunzip '%1': %2",
758                                     $self->archive, $buffer));
759     }
760
761     ### no buffers available?
762     if( !IPC::Cmd->can_capture_buffer and !$buffer ) {
763         $self->_error( $self->_no_buffer_content( $self->archive ) );
764     }
765
766     print $fh $buffer if defined $buffer;
767
768     close $fh;
769
770     ### set what files where extract, and where they went ###
771     $self->files( [$self->_gunzip_to] );
772     $self->extract_path( File::Spec->rel2abs(cwd()) );
773
774     return 1;
775 }
776
777 sub _gunzip_cz {
778     my $self = shift;
779
780     my $use_list = { 'Compress::Zlib' => '0.0' };
781     unless( can_load( modules => $use_list ) ) {
782         return $self->_error(loc("You do not have '%1' installed - Please " .
783                         "install it as soon as possible.", 'Compress::Zlib'));
784     }
785
786     my $gz = Compress::Zlib::gzopen( $self->archive, "rb" ) or
787                 return $self->_error(loc("Unable to open '%1': %2",
788                             $self->archive, $Compress::Zlib::gzerrno));
789
790     my $fh = FileHandle->new('>'. $self->_gunzip_to) or
791         return $self->_error(loc("Could not open '%1' for writing: %2",
792                             $self->_gunzip_to, $! ));
793
794     my $buffer;
795     $fh->print($buffer) while $gz->gzread($buffer) > 0;
796     $fh->close;
797
798     ### set what files where extract, and where they went ###
799     $self->files( [$self->_gunzip_to] );
800     $self->extract_path( File::Spec->rel2abs(cwd()) );
801
802     return 1;
803 }
804
805 #################################
806 #
807 # Uncompress code
808 #
809 #################################
810
811
812 ### untar wrapper... goes to either Archive::Tar or /bin/tar
813 ### depending on $PREFER_BIN
814 sub _uncompress {
815     my $self = shift;
816
817     my   @methods = qw[_gunzip_cz _uncompress_bin];
818          @methods = reverse @methods if $PREFER_BIN;
819
820     for my $method (@methods) {
821         $self->_extractor($method) && return 1 if $self->$method();
822     }
823
824     return $self->_error(loc("Unable to untar file '%1'", $self->archive));
825 }
826
827 sub _uncompress_bin {
828     my $self = shift;
829
830     ### check for /bin/gzip -- we need it ###
831     return $self->_error(loc("No '%1' program found", '/bin/uncompress'))
832         unless $self->bin_uncompress;
833
834
835     my $fh = FileHandle->new('>'. $self->_gunzip_to) or
836         return $self->_error(loc("Could not open '%1' for writing: %2",
837                             $self->_gunzip_to, $! ));
838
839     my $cmd = [ $self->bin_uncompress, '-c', $self->archive ];
840
841     my $buffer;
842     unless( scalar run( command => $cmd,
843                         verbose => $DEBUG,
844                         buffer  => \$buffer )
845     ) {
846         return $self->_error(loc("Unable to uncompress '%1': %2",
847                                     $self->archive, $buffer));
848     }
849
850     ### no buffers available?
851     if( !IPC::Cmd->can_capture_buffer and !$buffer ) {
852         $self->_error( $self->_no_buffer_content( $self->archive ) );
853     }
854
855     print $fh $buffer if defined $buffer;
856
857     close $fh;
858
859     ### set what files where extract, and where they went ###
860     $self->files( [$self->_gunzip_to] );
861     $self->extract_path( File::Spec->rel2abs(cwd()) );
862
863     return 1;
864 }
865
866
867 #################################
868 #
869 # Unzip code
870 #
871 #################################
872
873 ### unzip wrapper... goes to either Archive::Zip or /bin/unzip
874 ### depending on $PREFER_BIN
875 sub _unzip {
876     my $self = shift;
877
878     my @methods = qw[_unzip_az _unzip_bin];
879        @methods = reverse @methods if $PREFER_BIN;
880
881     for my $method (@methods) {
882         $self->_extractor($method) && return 1 if $self->$method();
883     }
884
885     return $self->_error(loc("Unable to gunzip file '%1'", $self->archive));
886 }
887
888 sub _unzip_bin {
889     my $self = shift;
890
891     ### check for /bin/gzip if we need it ###
892     return $self->_error(loc("No '%1' program found", '/bin/unzip'))
893         unless $self->bin_unzip;
894
895
896     ### first, get the files.. it must be 2 different commands with 'unzip' :(
897     {   ### on VMS, capital letter options have to be quoted. This is
898         ### peported by John Malmberg on P5P Tue 21 Aug 2007 05:05:11 
899         ### Subject: [patch@31735]Archive Extract fix on VMS.
900         my $opt = ON_VMS ? '"-Z"' : '-Z';
901         my $cmd = [ $self->bin_unzip, $opt, '-1', $self->archive ];
902         
903         my $buffer = '';
904         unless( scalar run( command => $cmd,
905                             verbose => $DEBUG,
906                             buffer  => \$buffer )
907         ) {
908             return $self->_error(loc("Unable to unzip '%1': %2",
909                                         $self->archive, $buffer));
910         }
911
912         ### no buffers available?
913         if( !IPC::Cmd->can_capture_buffer and !$buffer ) {
914             $self->_error( $self->_no_buffer_files( $self->archive ) );
915
916         } else {
917             $self->files( [split $/, $buffer] );
918         }
919     }
920
921     ### now, extract the archive ###
922     {   my $cmd = [ $self->bin_unzip, '-qq', '-o', $self->archive ];
923
924         my $buffer;
925         unless( scalar run( command => $cmd,
926                             verbose => $DEBUG,
927                             buffer  => \$buffer )
928         ) {
929             return $self->_error(loc("Unable to unzip '%1': %2",
930                                         $self->archive, $buffer));
931         }
932
933         if( scalar @{$self->files} ) {
934             my $files   = $self->files;
935             my $dir     = $self->__get_extract_dir( $files );
936
937             $self->extract_path( $dir );
938         }
939     }
940
941     return 1;
942 }
943
944 sub _unzip_az {
945     my $self = shift;
946
947     my $use_list = { 'Archive::Zip' => '0.0' };
948     unless( can_load( modules => $use_list ) ) {
949         return $self->_error(loc("You do not have '%1' installed - Please " .
950                         "install it as soon as possible.", 'Archive::Zip'));
951     }
952
953     my $zip = Archive::Zip->new();
954
955     unless( $zip->read( $self->archive ) == &Archive::Zip::AZ_OK ) {
956         return $self->_error(loc("Unable to read '%1'", $self->archive));
957     }
958
959     my @files;
960     ### have to extract every memeber individually ###
961     for my $member ($zip->members) {
962         push @files, $member->{fileName};
963
964         unless( $zip->extractMember($member) == &Archive::Zip::AZ_OK ) {
965             return $self->_error(loc("Extraction of '%1' from '%2' failed",
966                         $member->{fileName}, $self->archive ));
967         }
968     }
969
970     my $dir = $self->__get_extract_dir( \@files );
971
972     ### set what files where extract, and where they went ###
973     $self->files( \@files );
974     $self->extract_path( File::Spec->rel2abs($dir) );
975
976     return 1;
977 }
978
979 sub __get_extract_dir {
980     my $self    = shift;
981     my $files   = shift || [];
982
983     return unless scalar @$files;
984
985     my($dir1, $dir2);
986     for my $aref ( [ \$dir1, 0 ], [ \$dir2, -1 ] ) {
987         my($dir,$pos) = @$aref;
988
989         ### add a catdir(), so that any trailing slashes get
990         ### take care of (removed)
991         ### also, a catdir() normalises './dir/foo' to 'dir/foo';
992         ### which was the problem in bug #23999
993         my $res = -d $files->[$pos]
994                     ? File::Spec->catdir( $files->[$pos], '' )
995                     : File::Spec->catdir( dirname( $files->[$pos] ) ); 
996
997         $$dir = $res;
998     }
999
1000     ### if the first and last dir don't match, make sure the 
1001     ### dirname is not set wrongly
1002     my $dir;
1003  
1004     ### dirs are the same, so we know for sure what the extract dir is
1005     if( $dir1 eq $dir2 ) {
1006         $dir = $dir1;
1007     
1008     ### dirs are different.. do they share the base dir?
1009     ### if so, use that, if not, fall back to '.'
1010     } else {
1011         my $base1 = [ File::Spec->splitdir( $dir1 ) ]->[0];
1012         my $base2 = [ File::Spec->splitdir( $dir2 ) ]->[0];
1013         
1014         $dir = File::Spec->rel2abs( $base1 eq $base2 ? $base1 : '.' ); 
1015     }        
1016
1017     return File::Spec->rel2abs( $dir );
1018 }
1019
1020 #################################
1021 #
1022 # Bunzip2 code
1023 #
1024 #################################
1025
1026 ### bunzip2 wrapper... 
1027 sub _bunzip2 {
1028     my $self = shift;
1029
1030     my @methods = qw[_bunzip2_cz2 _bunzip2_bin];
1031        @methods = reverse @methods if $PREFER_BIN;
1032
1033     for my $method (@methods) {
1034         $self->_extractor($method) && return 1 if $self->$method();
1035     }
1036
1037     return $self->_error(loc("Unable to bunzip2 file '%1'", $self->archive));
1038 }
1039
1040 sub _bunzip2_bin {
1041     my $self = shift;
1042
1043     ### check for /bin/gzip -- we need it ###
1044     return $self->_error(loc("No '%1' program found", '/bin/bunzip2'))
1045         unless $self->bin_bunzip2;
1046
1047
1048     my $fh = FileHandle->new('>'. $self->_gunzip_to) or
1049         return $self->_error(loc("Could not open '%1' for writing: %2",
1050                             $self->_gunzip_to, $! ));
1051     
1052     ### guard against broken bunzip2. See ->have_old_bunzip2()
1053     ### for details
1054     if( $self->have_old_bunzip2 and $self->archive !~ /\.bz2$/i ) {
1055         return $self->_error(loc("Your bunzip2 version is too old and ".
1056                                  "can only extract files ending in '%1'",
1057                                  '.bz2'));
1058     }
1059
1060     my $cmd = [ $self->bin_bunzip2, '-cd', $self->archive ];
1061
1062     my $buffer;
1063     unless( scalar run( command => $cmd,
1064                         verbose => $DEBUG,
1065                         buffer  => \$buffer )
1066     ) {
1067         return $self->_error(loc("Unable to bunzip2 '%1': %2",
1068                                     $self->archive, $buffer));
1069     }
1070
1071     ### no buffers available?
1072     if( !IPC::Cmd->can_capture_buffer and !$buffer ) {
1073         $self->_error( $self->_no_buffer_content( $self->archive ) );
1074     }
1075     
1076     print $fh $buffer if defined $buffer;
1077
1078     close $fh;
1079
1080     ### set what files where extract, and where they went ###
1081     $self->files( [$self->_gunzip_to] );
1082     $self->extract_path( File::Spec->rel2abs(cwd()) );
1083
1084     return 1;
1085 }
1086
1087 ### using cz2, the compact versions... this we use mainly in archive::tar
1088 ### extractor..
1089 # sub _bunzip2_cz1 {
1090 #     my $self = shift;
1091
1092 #     my $use_list = { 'IO::Uncompress::Bunzip2' => '0.0' };
1093 #     unless( can_load( modules => $use_list ) ) {
1094 #         return $self->_error(loc("You do not have '%1' installed - Please " .
1095 #                         "install it as soon as possible.",
1096 #                         'IO::Uncompress::Bunzip2'));
1097 #     }
1098
1099 #     my $bz = IO::Uncompress::Bunzip2->new( $self->archive ) or
1100 #                 return $self->_error(loc("Unable to open '%1': %2",
1101 #                             $self->archive,
1102 #                             $IO::Uncompress::Bunzip2::Bunzip2Error));
1103
1104 #     my $fh = FileHandle->new('>'. $self->_gunzip_to) or
1105 #         return $self->_error(loc("Could not open '%1' for writing: %2",
1106 #                             $self->_gunzip_to, $! ));
1107
1108 #     my $buffer;
1109 #     $fh->print($buffer) while $bz->read($buffer) > 0;
1110 #     $fh->close;
1111
1112 #     ### set what files where extract, and where they went ###
1113 #     $self->files( [$self->_gunzip_to] );
1114 #     $self->extract_path( File::Spec->rel2abs(cwd()) );
1115
1116 #     return 1;
1117 # }
1118
1119 sub _bunzip2_cz2 {
1120     my $self = shift;
1121
1122     my $use_list = { 'IO::Uncompress::Bunzip2' => '0.0' };
1123     unless( can_load( modules => $use_list ) ) {
1124         return $self->_error(loc("You do not have '%1' installed - Please " .
1125                         "install it as soon as possible.",
1126                         'IO::Uncompress::Bunzip2'));
1127     }
1128
1129     IO::Uncompress::Bunzip2::bunzip2($self->archive => $self->_gunzip_to)
1130         or return $self->_error(loc("Unable to uncompress '%1': %2",
1131                             $self->archive,
1132                             $IO::Uncompress::Bunzip2::Bunzip2Error));
1133
1134     ### set what files where extract, and where they went ###
1135     $self->files( [$self->_gunzip_to] );
1136     $self->extract_path( File::Spec->rel2abs(cwd()) );
1137
1138     return 1;
1139 }
1140
1141
1142 #################################
1143 #
1144 # Error code
1145 #
1146 #################################
1147
1148 sub _error {
1149     my $self    = shift;
1150     my $error   = shift;
1151     
1152     $self->_error_msg( $error );
1153     $self->_error_msg_long( Carp::longmess($error) );
1154     
1155     ### set $Archive::Extract::WARN to 0 to disable printing
1156     ### of errors
1157     if( $WARN ) {
1158         carp $DEBUG ? $self->_error_msg_long : $self->_error_msg;
1159     }
1160
1161     return;
1162 }
1163
1164 sub error {
1165     my $self = shift;
1166     return shift() ? $self->_error_msg_long : $self->_error_msg;
1167 }
1168
1169 sub _no_buffer_files {
1170     my $self = shift;
1171     my $file = shift or return;
1172     return loc("No buffer captured, unable to tell ".
1173                "extracted files or extraction dir for '%1'", $file);
1174 }
1175
1176 sub _no_buffer_content {
1177     my $self = shift;
1178     my $file = shift or return;
1179     return loc("No buffer captured, unable to get content for '%1'", $file);
1180 }
1181 1;
1182
1183 =pod
1184
1185 =head1 HOW IT WORKS
1186
1187 C<Archive::Extract> tries first to determine what type of archive you
1188 are passing it, by inspecting its suffix. It does not do this by using
1189 Mime magic, or something related. See C<CAVEATS> below.
1190
1191 Once it has determined the file type, it knows which extraction methods
1192 it can use on the archive. It will try a perl solution first, then fall
1193 back to a commandline tool if that fails. If that also fails, it will
1194 return false, indicating it was unable to extract the archive.
1195 See the section on C<GLOBAL VARIABLES> to see how to alter this order.
1196
1197 =head1 CAVEATS
1198
1199 =head2 File Extensions
1200
1201 C<Archive::Extract> trusts on the extension of the archive to determine
1202 what type it is, and what extractor methods therefore can be used. If
1203 your archives do not have any of the extensions as described in the
1204 C<new()> method, you will have to specify the type explicitly, or
1205 C<Archive::Extract> will not be able to extract the archive for you.
1206
1207 =head2 Supporting Very Large Files
1208
1209 C<Archive::Extract> can use either pure perl modules or command line
1210 programs under the hood. Some of the pure perl modules (like 
1211 C<Archive::Tar> take the entire contents of the archive into memory,
1212 which may not be feasible on your system. Consider setting the global
1213 variable C<$Archive::Extract::PREFER_BIN> to C<1>, which will prefer
1214 the use of command line programs and won't consume so much memory.
1215
1216 See the C<GLOBAL VARIABLES> section below for details.
1217
1218 =head2 Bunzip2 support of arbitrary extensions.
1219
1220 Older versions of C</bin/bunzip2> do not support arbitrary file 
1221 extensions and insist on a C<.bz2> suffix. Although we do our best
1222 to guard against this, if you experience a bunzip2 error, it may
1223 be related to this. For details, please see the C<have_old_bunzip2>
1224 method.
1225
1226 =head1 GLOBAL VARIABLES
1227
1228 =head2 $Archive::Extract::DEBUG
1229
1230 Set this variable to C<true> to have all calls to command line tools
1231 be printed out, including all their output.
1232 This also enables C<Carp::longmess> errors, instead of the regular
1233 C<carp> errors.
1234
1235 Good for tracking down why things don't work with your particular
1236 setup.
1237
1238 Defaults to C<false>.
1239
1240 =head2 $Archive::Extract::WARN
1241
1242 This variable controls whether errors encountered internally by
1243 C<Archive::Extract> should be C<carp>'d or not.
1244
1245 Set to false to silence warnings. Inspect the output of the C<error()>
1246 method manually to see what went wrong.
1247
1248 Defaults to C<true>.
1249
1250 =head2 $Archive::Extract::PREFER_BIN
1251
1252 This variables controls whether C<Archive::Extract> should prefer the
1253 use of perl modules, or commandline tools to extract archives.
1254
1255 Set to C<true> to have C<Archive::Extract> prefer commandline tools.
1256
1257 Defaults to C<false>.
1258
1259 =head1 TODO
1260
1261 =over 4
1262
1263 =item Mime magic support
1264
1265 Maybe this module should use something like C<File::Type> to determine
1266 the type, rather than blindly trust the suffix.
1267
1268 =back
1269
1270 =head1 BUG REPORTS
1271
1272 Please report bugs or other issues to E<lt>bug-archive-extract@rt.cpan.org<gt>.
1273
1274 =head1 AUTHOR
1275
1276 This module by Jos Boumans E<lt>kane@cpan.orgE<gt>.
1277
1278 =head1 COPYRIGHT
1279
1280 This library is free software; you may redistribute and/or modify it 
1281 under the same terms as Perl itself.
1282
1283 =cut
1284
1285 # Local variables:
1286 # c-indentation-style: bsd
1287 # c-basic-offset: 4
1288 # indent-tabs-mode: nil
1289 # End:
1290 # vim: expandtab shiftwidth=4:
1291