From fd6c0eeda315a0cf1dacea1c0dbf74ff971cfbca Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Thu, 14 Sep 2017 11:26:13 +0200 Subject: [PATCH] Imported Upstream version 0.06 --- .gitignore | 11 + Changes | 24 + MANIFEST | 47 ++ MANIFEST.SKIP | 19 + META.json | 31 ++ META.yml | 19 + Makefile.PL | 115 ++++ lib/Archive/SevenZip.pm | 726 +++++++++++++++++++++++++ lib/Archive/SevenZip/API/ArchiveZip.pm | 244 +++++++++ lib/Archive/SevenZip/Entry.pm | 138 +++++ t/01-identity.t | 57 ++ t/02-add-scalar.t | 38 ++ t/02_main.t | 381 +++++++++++++ t/05_tree.t | 59 ++ t/20_bug_github11.t | 59 ++ t/badjpeg/expected.jpg | Bin 0 -> 56897 bytes t/badjpeg/source.zip | Bin 0 -> 52476 bytes t/common.pm | 257 +++++++++ t/data/bad_github11.zip | Bin 0 -> 455 bytes t/data/chmod.zip | Bin 0 -> 305 bytes t/data/crypcomp.zip | Bin 0 -> 218 bytes t/data/crypt.zip | Bin 0 -> 216 bytes t/data/def.zip | Bin 0 -> 111 bytes t/data/defstr.zip | Bin 0 -> 127 bytes t/data/emptydef.zip | Bin 0 -> 108 bytes t/data/emptydefstr.zip | Bin 0 -> 124 bytes t/data/emptystore.zip | Bin 0 -> 106 bytes t/data/emptystorestr.zip | Bin 0 -> 122 bytes t/data/fred | 1 + t/data/good_github11.zip | Bin 0 -> 407 bytes t/data/jar.zip | Bin 0 -> 448 bytes t/data/linux.zip | Bin 0 -> 140 bytes t/data/mkzip.pl | 54 ++ t/data/perl.zip | Bin 0 -> 106 bytes t/data/store.zip | Bin 0 -> 109 bytes t/data/storestr.zip | Bin 0 -> 125 bytes t/data/streamed.zip | Bin 0 -> 127 bytes t/data/winzip.zip | Bin 0 -> 106 bytes t/data/zip64.zip | Bin 0 -> 242 bytes xt/99-changes.t | 28 + xt/99-compile.t | 43 ++ xt/99-manifest.t | 31 ++ xt/99-minimumversion.t | 17 + xt/99-pod.t | 36 ++ xt/99-todo.t | 47 ++ xt/99-unix-text.t | 37 ++ xt/99-versions.t | 51 ++ 47 files changed, 2570 insertions(+) create mode 100644 .gitignore create mode 100644 Changes create mode 100644 MANIFEST create mode 100644 MANIFEST.SKIP create mode 100644 META.json create mode 100644 META.yml create mode 100644 Makefile.PL create mode 100644 lib/Archive/SevenZip.pm create mode 100644 lib/Archive/SevenZip/API/ArchiveZip.pm create mode 100644 lib/Archive/SevenZip/Entry.pm create mode 100644 t/01-identity.t create mode 100644 t/02-add-scalar.t create mode 100644 t/02_main.t create mode 100644 t/05_tree.t create mode 100644 t/20_bug_github11.t create mode 100644 t/badjpeg/expected.jpg create mode 100644 t/badjpeg/source.zip create mode 100644 t/common.pm create mode 100644 t/data/bad_github11.zip create mode 100644 t/data/chmod.zip create mode 100644 t/data/crypcomp.zip create mode 100644 t/data/crypt.zip create mode 100644 t/data/def.zip create mode 100644 t/data/defstr.zip create mode 100644 t/data/emptydef.zip create mode 100644 t/data/emptydefstr.zip create mode 100644 t/data/emptystore.zip create mode 100644 t/data/emptystorestr.zip create mode 100644 t/data/fred create mode 100644 t/data/good_github11.zip create mode 100644 t/data/jar.zip create mode 100644 t/data/linux.zip create mode 100644 t/data/mkzip.pl create mode 100644 t/data/perl.zip create mode 100644 t/data/store.zip create mode 100644 t/data/storestr.zip create mode 100644 t/data/streamed.zip create mode 100644 t/data/winzip.zip create mode 100644 t/data/zip64.zip create mode 100644 xt/99-changes.t create mode 100644 xt/99-compile.t create mode 100644 xt/99-manifest.t create mode 100644 xt/99-minimumversion.t create mode 100644 xt/99-pod.t create mode 100644 xt/99-todo.t create mode 100644 xt/99-unix-text.t create mode 100644 xt/99-versions.t diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e3e1e31 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +Makefile +Makefile.old +*.tar.gz +*.bak +pm_to_blib +blib/ +Archive-SevenZip-*/ +Archive-SevenZip-* +.releaserc +cover_db +MYMETA.* diff --git a/Changes b/Changes new file mode 100644 index 0000000..ff3782d --- /dev/null +++ b/Changes @@ -0,0 +1,24 @@ +0.06 20160411 + . Restore compatibility with Perl 5.6.x + This means foregoing the defined-or operator, but as that one + only came in with Perl 5.10, I'm removing the use + +0.05 20160410 + . More test fixes by Alexandr Ciornii + . No "undefined" warnings on non-Windows sytems + +0.04 20160409 + . Switch tests to make indirect reliance on Archive::Zip optional + . This time, test those changes using Test::Without::Module + . Fix some documentation, add SYNOPSIS to Archive::SevenZip::API::ArchiveZip + No code changes + +0.03 20160407 + . Switch tests to make indirect reliance on Archive::Zip optional + No code changes + +0.02 20160404 + ! Switch all IPC to IPC::Open3 + +0.01 20160403 + . Released on an unsuspecting world diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..7b4a21d --- /dev/null +++ b/MANIFEST @@ -0,0 +1,47 @@ +.gitignore +Changes +lib/Archive/SevenZip.pm +lib/Archive/SevenZip/API/ArchiveZip.pm +lib/Archive/SevenZip/Entry.pm +Makefile.PL +MANIFEST This list of files +MANIFEST.SKIP +META.json +META.yml +t/01-identity.t +t/02-add-scalar.t +t/02_main.t +t/05_tree.t +t/20_bug_github11.t +t/badjpeg/expected.jpg +t/badjpeg/source.zip +t/common.pm +t/data/bad_github11.zip +t/data/chmod.zip +t/data/crypcomp.zip +t/data/crypt.zip +t/data/def.zip +t/data/defstr.zip +t/data/emptydef.zip +t/data/emptydefstr.zip +t/data/emptystore.zip +t/data/emptystorestr.zip +t/data/fred +t/data/good_github11.zip +t/data/jar.zip +t/data/linux.zip +t/data/mkzip.pl +t/data/perl.zip +t/data/store.zip +t/data/storestr.zip +t/data/streamed.zip +t/data/winzip.zip +t/data/zip64.zip +xt/99-changes.t +xt/99-compile.t +xt/99-manifest.t +xt/99-minimumversion.t +xt/99-pod.t +xt/99-todo.t +xt/99-unix-text.t +xt/99-versions.t diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP new file mode 100644 index 0000000..e1d3db1 --- /dev/null +++ b/MANIFEST.SKIP @@ -0,0 +1,19 @@ +^\.git\/ +maint +^tags$ +.last_cover_stats +Makefile$ +^blib +^pm_to_blib +^.*.bak +^.*.old +^t.*sessions +^cover_db +^.*\.log +^.*\.swp$ +^jar/ +^cpan/ +^MYMETA +^.releaserc +^Archive-SevenZip-.*/ +^Archive-SevenZip-.*.tar.gz$ diff --git a/META.json b/META.json new file mode 100644 index 0000000..f456c85 --- /dev/null +++ b/META.json @@ -0,0 +1,31 @@ +{ + "abstract" : "Read/write 7z , zip , ISO9960 and other archives", + "author" : [ + "Max Maischein " + ], + "dynamic_config" : 1, + "generated_by" : "ExtUtils::MakeMaker version 6.66, CPAN::Meta::Converter version 2.120921", + "license" : [ + "perl_5" + ], + "meta-spec" : { + "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", + "version" : "2" + }, + "name" : "Archive-SevenZip", + "no_index" : { + "directory" : [ + "t", + "inc" + ] + }, + "release_status" : "stable", + "resources" : { + "repository" : { + "type" : "git", + "url" : "git://github.com/Corion/archive-sevenzip.git", + "web" : "https://github.com/Corion/archive-sevenzip" + } + }, + "version" : "0.06" +} diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..ca6eef6 --- /dev/null +++ b/META.yml @@ -0,0 +1,19 @@ +--- +abstract: 'Read/write 7z , zip , ISO9960 and other archives' +author: + - 'Max Maischein ' +build_requires: {} +dynamic_config: 1 +generated_by: 'ExtUtils::MakeMaker version 6.66, CPAN::Meta::Converter version 2.120921' +license: perl +meta-spec: + url: http://module-build.sourceforge.net/META-spec-v1.4.html + version: 1.4 +name: Archive-SevenZip +no_index: + directory: + - t + - inc +resources: + repository: git://github.com/Corion/archive-sevenzip.git +version: 0.06 diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..4c3063d --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,115 @@ +# -*- mode: perl; c-basic-offset: 4; indent-tabs-mode: nil; -*- + +use strict; +use ExtUtils::MakeMaker qw(WriteMakefile); +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. + +# Normalize version strings like 6.30_02 to 6.3002, +# so that we can do numerical comparisons on it. +my $eumm_version = $ExtUtils::MakeMaker::VERSION; +$eumm_version =~ s/_//; + +my $module = 'Archive::SevenZip'; +(my $main_file = "lib/$module.pm" ) =~ s!::!/!g; + +# I should maybe use something like Shipwright... +regen_README(); +#regen_EXAMPLES(); + +my @tests = map { glob $_ } 't/*.t', 't/*/*.t'; + +WriteMakefile1( + NAME => $module, + AUTHOR => q{Max Maischein }, + VERSION_FROM => $main_file, + ABSTRACT_FROM => $main_file, + META_MERGE => { + "meta-spec" => { version => 2 }, + resources => { + repository => { + web => 'https://github.com/Corion/archive-sevenzip', + url => 'git://github.com/Corion/archive-sevenzip.git', + type => 'git', + } + }, + }, + + MIN_PERL_VERSION => '5.006', + + ($eumm_version >= 6.3001 + ? ('LICENSE'=> 'perl') + : ()), + + PL_FILES => {}, + BUILD_REQUIRES => { + 'ExtUtils::MakeMaker' => 0, + }, + + PREREQ_PM => { + 'Test::More' => 0, + 'File::Spec' => 0, # some tests do, at least + 'Exporter' => 5, # for 'import' + + 'File::Temp' => 0, + 'File::Copy' => 0, + 'IPC::Open3' => 0, # for talking to 7zip + 'Path::Class' => 0, + 'Encode' => 0, + 'File::Basename' => 0, + 'Time::Piece' => 0, + }, + dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', }, + clean => { FILES => 'Archive-SevenZip-*' }, + + test => { TESTS => join( ' ', @tests ) }, +); + +1; + +sub WriteMakefile1 { #Written by Alexandr Ciornii, version 0.21. Added by eumm-upgrade. + my %params=@_; + my $eumm_version=$ExtUtils::MakeMaker::VERSION; + $eumm_version=eval $eumm_version; + die "EXTRA_META is deprecated" if exists $params{EXTRA_META}; + die "License not specified" if not exists $params{LICENSE}; + if ($params{BUILD_REQUIRES} and $eumm_version < 6.5503) { + #EUMM 6.5502 has problems with BUILD_REQUIRES + $params{PREREQ_PM}={ %{$params{PREREQ_PM} || {}} , %{$params{BUILD_REQUIRES}} }; + delete $params{BUILD_REQUIRES}; + } + delete $params{CONFIGURE_REQUIRES} if $eumm_version < 6.52; + delete $params{MIN_PERL_VERSION} if $eumm_version < 6.48; + delete $params{META_MERGE} if $eumm_version < 6.46; + delete $params{META_ADD} if $eumm_version < 6.46; + delete $params{LICENSE} if $eumm_version < 6.31; + delete $params{AUTHOR} if $] < 5.005; + delete $params{ABSTRACT_FROM} if $] < 5.005; + delete $params{BINARY_LOCATION} if $] < 5.005; + + WriteMakefile(%params); +} + +sub regen_README { + eval { + require Pod::Readme; + Pod::Readme->VERSION('1.0.2'); #0.11 may hang + + my $parser = Pod::Readme->new(); + + # Read POD from Module.pm and write to README + $parser->parse_from_file($_[0], 'README'); + }; + eval { + require Pod::Markdown; + + my $parser = Pod::Markdown->new(); + + # Read POD from Module.pm and write to README + $parser->parse_from_file($_[0]); + open my $fh, '>', 'README.mkdn' + or die "Couldn't open 'README.mkdn': $!"; + print $fh $parser->as_markdown; + }; +} + diff --git a/lib/Archive/SevenZip.pm b/lib/Archive/SevenZip.pm new file mode 100644 index 0000000..4585698 --- /dev/null +++ b/lib/Archive/SevenZip.pm @@ -0,0 +1,726 @@ +package Archive::SevenZip; +use strict; +use Carp qw(croak); +use Encode qw( decode encode ); +use File::Basename qw(dirname basename); +use Archive::SevenZip::Entry; +use File::Temp qw(tempfile tempdir); +use File::Copy; +use IPC::Open3 'open3'; +use Path::Class; +use Exporter 'import'; # for the error codes, in Archive::Zip API compatibility + + +=head1 NAME + +Archive::SevenZip - Read/write 7z , zip , ISO9960 and other archives + +=head1 SYNOPSIS + + my $ar = Archive::SevenZip->new( + find => 1, + archivename => $archivename, + verbose => $verbose, + ); + + for my $entry ( $ar->list ) { + my $target = join "/", "$target_dir", $entry->basename; + $ar->extractMember( $entry->fileName, $target ); + }; + +=head1 METHODS + +=cut + +use vars qw(%sevenzip_charsetname %class_defaults $VERSION @EXPORT_OK %EXPORT_TAGS); +$VERSION= '0.06'; + +# Archive::Zip API +# Error codes +use constant AZ_OK => 0; + +use constant COMPRESSION_STORED => 'Store'; # file is stored (no compression) +use constant COMPRESSION_DEFLATED => 'Deflate'; # file is Deflated + +@EXPORT_OK = (qw(AZ_OK COMPRESSION_STORED COMPRESSION_DEFLATED)); +%EXPORT_TAGS = ( + ERROR_CODES => [ + qw( + AZ_OK + ) + #AZ_STREAM_END + #AZ_ERROR + #AZ_FORMAT_ERROR + #AZ_IO_ERROR + ], + CONSTANTS => [ + qw(COMPRESSION_STORED COMPRESSION_DEFLATED) + ], +); + +%sevenzip_charsetname = ( + 'UTF-8' => 'UTF-8', + 'Latin-1' => 'WIN', + 'ISO-8859-1' => 'WIN', + '' => 'DOS', # dunno what the appropriate name would be +); + +if( $^O !~ /MSWin/ ) { + # Wipe all filesystem encodings because my Debian 7z 9.20 doesn't understand them + $sevenzip_charsetname{ $_ } = '' + for keys %sevenzip_charsetname; +}; + +%class_defaults = ( + '7zip' => '7z', + fs_encoding => 'UTF-8', + default_options => [ "-y", "-bd" ], + type => 'zip', +); + +=head2 C<< Archive::SevenZip->find_7z_executable >> + +Finds the 7z executable in the path or in C<< $ENV{ProgramFiles} >> +or C<< $ENV{ProgramFiles(x86)} >>. This is called +when a C<< Archive::SevenZip >> instance is created with the C +parameter set to 1. + +=cut + +sub find_7z_executable { + my($class) = @_; + my $old_default = $class_defaults{ '7zip' }; + my $envsep = $^O =~ /MSWin/ ? ';' : ':'; + my @search = split /$envsep/, $ENV{PATH}; + if( $^O =~ /MSWin/i ) { + push @search, map { "$_\\7-Zip" } grep {defined} ($ENV{'ProgramFiles'}, $ENV{'ProgramFiles(x86)'}); + }; + my $found = $class->version; + + while( ! defined $found and @search) { + my $dir = shift @search; + if ($^O eq 'MSWin32') { + next unless -e file("$dir", "7z.exe" ); + } + $class_defaults{'7zip'} = "" . file("$dir", "7z" ); + $found = $class->version; + }; + + if( ! $found) { + $class_defaults{ '7zip' } = $old_default; + }; + return defined $found ? $found : () +} + +=head2 C<< Archive::SevenZip->new >> + + my $ar = Archive::SevenZip->new( $archivename ); + + my $ar = Archive::SevenZip->new( + archivename => $archivename, + find => 1, + ); + +Creates a new class instance. + +C - will try to find the executable using C<< ->find_7z_executable >> + +=cut + +sub new { + my( $class, %options); + if( @_ == 2 ) { + ($class, $options{ archivename }) = @_; + } else { + ($class, %options) = @_; + }; + + if( $options{ find }) { + $class->find_7z_executable(); + }; + + for( keys %class_defaults ) { + $options{ $_ } = $class_defaults{ $_ } + unless defined $options{ $_ }; + }; + + bless \%options => $class +} + +sub version { + my( $self_or_class, %options) = @_; + for( keys %class_defaults ) { + $options{ $_ } = $class_defaults{ $_ } + unless defined $options{ $_ }; + }; + my $self = ref $self_or_class ? $self_or_class : $self_or_class->new( %options ); + + my $cmd = $self->get_command( + command => '', + archivename => undef, + options => [], # on Debian, 7z doesn't like any options... + fs_encoding => undef, # on Debian, 7z doesn't like any options... + default_options => [], # on Debian, 7z doesn't like any options... + ); + my $fh = eval { $self->run($cmd, binmode => ':raw') }; + + if( ! $@ ) { + local $/ = "\n"; + my @output = <$fh>; + if( @output >= 3) { + $output[1] =~ /^7-Zip\s+.*?(\d+\.\d+)\s+(?:\s*:\s*)?Copyright/ + or return undef; + return $1; + } else { + return undef + } + } +} + +=head2 C<< $ar->open >> + + my @entries = $ar->open; + for my $entry (@entries) { + print $entry->name, "\n"; + }; + +Lists the entries in the archive. A fresh archive which does not +exist on disk yet has no entries. The returned entries +are L instances. + +This method will one day move to the Path::Class-compatibility +API. + +=cut +# Iterate over the entries in the archive +# Path::Class API +sub open { + my( $self )= @_; + my @contents = $self->list(); +} + +=head2 C<< $ar->memberNamed >> + + my $entry = $ar->memberNamed('hello_world.txt'); + print $entry->fileName, "\n"; + +The path separator must be a forward slash ("/") + +This method will one day move to the Archive::Zip-compatibility +API. + +=cut + +# Archive::Zip API +sub memberNamed { + my( $self, $name, %options )= @_; + + my( $entry ) = grep { $_->fileName eq $name } $self->members( %options ); + $entry +} + +# Archive::Zip API +sub list { + my( $self, %options )= @_; + + if( ! grep { defined $_ } $options{archivename}, $self->{archivename}) { + # We are an archive that does not exist on disk yet + return + }; + my $cmd = $self->get_command( command => "l", options => ["-slt"], %options ); + + my $fh = $self->run($cmd, encoding => $options{ fs_encoding } ); + my @output = <$fh>; + my %results = ( + header => [], + archive => [], + ); + + # Get/skip header + while( @output and $output[0] !~ /^--\s*$/ ) { + my $line = shift @output; + $line =~ s!\s+$!!; + push @{ $results{ header }}, $line; + }; + + # Get/skip archive information + while( @output and $output[0] !~ /^----------\s*$/ ) { + my $line = shift @output; + $line =~ s!\s+$!!; + push @{ $results{ archive }}, $line; + }; + + if( $output[0] =~ /^----------\s*$/ ) { + shift @output; + } else { + warn "Unexpected line in 7zip output, hope that's OK: [$output[0]]"; + }; + + my @members; + + # Split entries + my %entry_info; + while( @output ) { + my $line = shift @output; + if( $line =~ /^([\w ]+) =(?: (.*?)|)\s*$/ ) { + $entry_info{ $1 } = $2; + } elsif($line =~ /^\s*$/) { + push @members, Archive::SevenZip::Entry->new( + %entry_info, + _Container => $self, + ); + %entry_info = (); + } else { + croak "Unknown file entry [$line]"; + }; + }; + + return @members +} +{ no warnings 'once'; +*members = \&list; +} + +=head2 C<< $ar->openMemberFH >> + + my $fh = $ar->openMemberFH('test.txt'); + while( <$fh> ) { + print "test.txt: $_"; + }; + +Reads the uncompressed content of the member from the archive. + +This method will one day move to the Archive::Zip-compatibility +API. + +=cut + +sub openMemberFH { + my( $self, %options ); + if( @_ == 2 ) { + ($self,$options{ membername })= @_; + } else { + ($self,%options) = @_; + }; + defined $options{ membername } or croak "Need member name to extract"; + + my $cmd = $self->get_command( command => "e", options => ["-so"], members => [$options{membername}] ); + my $fh = $self->run($cmd, encoding => $options{ encoding }, binmode => $options{ binmode }); + return $fh +} + +sub content { + my( $self, %options ) = @_; + my $fh = $self->openMemberFH( %options ); + local $/; + <$fh> +} +=head2 C<< $ar->extractMember >> + + $ar->extractMember('test.txt' => 'extracted_test.txt'); + +Extracts the uncompressed content of the member from the archive. + +This method will one day move to the Archive::Zip-compatibility +API. + +=cut + +# Archive::Zip API +sub extractMember { + my( $self, $memberOrName, $extractedName, %_options ) = @_; + $extractedName = $memberOrName + unless defined $extractedName; + + my %options = (%$self, %_options); + + my $target_dir = dirname $extractedName; + my $target_name = basename $extractedName; + my $cmd = $self->get_command( + command => "e", + archivename => $options{ archivename }, + members => [ $memberOrName ], + options => [ "-o$target_dir" ], + ); + my $fh = $self->run($cmd, encoding => $options{ encoding }); + + while( <$fh>) { + warn $_ if $options{ verbose }; + }; + if( basename $memberOrName ne $target_name ) { + rename "$target_dir/" . basename($memberOrName) => $extractedName + or croak "Couldn't move '$memberOrName' to '$extractedName': $!"; + }; + + return AZ_OK; +}; + +=head2 C<< $ar->removeMember >> + + $ar->removeMember('test.txt'); + +Removes the member from the archive. + +=cut + +# strikingly similar to Archive::Zip API +sub removeMember { + my( $self, $name, %_options ) = @_; + + my %options = (%$self, %_options); + + if( $^O =~ /MSWin/ ) { + $name =~ s!/!\\!g; + } + + my $cmd = $self->get_command( + command => "d", + archivename => $options{ archivename }, + members => [ $name ], + ); + my $fh = $self->run($cmd, encoding => $options{ encoding } ); + $self->wait($fh, %options); + + return AZ_OK; +}; + +sub add_quotes { + map { + defined $_ && /\s/ ? qq{"$_"} : $_ + } @_ +}; + +sub get_command { + my( $self, %options )= @_; + $options{ members } ||= []; + $options{ archivename } = $self->{ archivename } + unless defined $options{ archivename }; + if( ! exists $options{ fs_encoding }) { + $options{ fs_encoding } = defined $self->{ fs_encoding } ? $self->{ fs_encoding } : $class_defaults{ fs_encoding }; + }; + if( ! defined $options{ default_options }) { + $options{ default_options } = defined $self->{ default_options } ? $self->{ default_options } : $class_defaults{ default_options }; + }; + + my @charset; + if( defined $options{ fs_encoding }) { + exists $sevenzip_charsetname{ $options{ fs_encoding }} + or croak "Unknown filesystem encoding '$options{ fs_encoding }'"; + if( my $charset = $sevenzip_charsetname{ $options{ fs_encoding }}) { + push @charset, "-scs" . $sevenzip_charsetname{ $options{ fs_encoding }}; + }; + }; + for(@{ $options{ members }}) { + $_ = encode $options{ fs_encoding }, $_; + }; + + # Now quote what needs to be quoted + for( @{ $options{ options }}, @{ $options{ members }}, $options{ archivename }, "$self->{ '7zip' }") { + }; + + return [grep {defined $_} + add_quotes($self->{ '7zip' }), + @{ $options{ default_options }}, + $options{ command }, + @charset, + add_quotes( @{ $options{ options }} ), + add_quotes( $options{ archivename } ), + add_quotes( @{ $options{ members }} ), + ]; +} + +sub run { + my( $self, $cmd, %options )= @_; + + my $mode = '-|'; + if( defined $options{ stdin }) { + $mode = '|-'; + }; + + my $fh; + warn "Opening [@$cmd]" + if $options{ verbose }; + + if( $self->{verbose} ) { + CORE::open( $fh, $mode, @$cmd) + or croak "Couldn't launch [$mode @$cmd]: $!/$?"; + } else { + CORE::open( my $fh_err, '>', File::Spec->devnull ) + or warn "Couldn't redirect child STDERR"; + my $errh = fileno $fh_err; + # We accumulate zombie PIDs here, ah well. + my $pid = open3( my $fh_in, my $fh_out, '>&' . $errh, @$cmd) + or croak "Couldn't launch [$mode @$cmd]: $!/$?"; + if( $mode eq '|-' ) { + $fh = $fh_in; + } else { + $fh = $fh_out + }; + } + if( $options{ encoding }) { + binmode $fh, ":encoding($options{ encoding })"; + } elsif( $options{ binmode } ) { + binmode $fh, $options{ binmode }; + }; + + if( $options{ stdin }) { + print {$fh} $options{ stdin }; + close $fh; + } elsif( $options{ skip }) { + for( 1..$options{ skip }) { + # Read that many lines + local $/ = "\n"; + scalar <$fh>; + }; + }; + + $fh; +} + +sub archive_or_temp { + my( $self ) = @_; + if( ! defined $self->{archivename} ) { + $self->{is_tempfile} = 1; + (my( $fh ),$self->{archivename}) = tempfile( SUFFIX => ".$self->{type}" ); + close $fh; + unlink $self->{archivename}; + }; + $self->{archivename} +}; + +sub wait { + my( $self, $fh, %options ) = @_; + while( <$fh>) { + warn $_ if ($options{ verbose } || $self->{verbose}) + }; +} + +=head2 C<< $ar->add_scalar >> + + $ar->add_scalar( "Some name.txt", "This is the content" ); + +Adds a scalar as an archive member. + +Unfortunately, 7zip doesn't reliably read archive members from STDIN, +so the scalar will be written to a tempfile, added to the archive and then +renamed in the archive. + +This requires 7zip version 9.30+ + +=cut + +sub add_scalar { + my( $self, $name, $scalar )= @_; + + # 7zip doesn't really support reading archive members from STDIN :-( + my($fh, $tempname) = tempfile; + binmode $fh, ':raw'; + print {$fh} $scalar; + close $fh; + + # Only supports 7z archive type?! + # 7zip will magically append .7z to the filename :-( + my $cmd = $self->get_command( + command => 'a', + archivename => $self->archive_or_temp, + members => [$tempname], + #options => ], + ); + $fh = $self->run( $cmd ); + $self->wait($fh); + + unlink $tempname + or warn "Couldn't unlink '$tempname': $!"; + + # Hopefully your version of 7zip can rename members (9.30+): + $cmd = $self->get_command( + command => 'rn', + archivename => $self->archive_or_temp, + members => [basename($tempname), $name], + #options => ], + ); + $fh = $self->run( $cmd ); + $self->wait($fh); + + # Once 7zip supports reading from stdin, this will work again: + #my $fh = $self->run( $cmd, + # binmode => ':raw', + # stdin => $scalar, + # verbose => 1, + #); +}; + +=head2 C<< $ar->add_directory >> + + $ar->add_directory( "real_etc", "etc" ); + +Adds an empty directory + +This currently ignores the directory date and time if the directory +exists + +=cut + +sub add_directory { + my( $self, $localname, $target )= @_; + + $target ||= $localname; + + # Create an empty directory, add it to the archive, + # then rename that temp name to the wanted name: + my $tempname = tempdir; + + my $cmd = $self->get_command( + command => 'a', + archivename => $self->archive_or_temp, + members => [$tempname], + options => ['-r0'], + ); + my $fh = $self->run( $cmd ); + $self->wait($fh); + + # Hopefully your version of 7zip can rename members (9.30+): + $cmd = $self->get_command( + command => 'rn', + archivename => $self->archive_or_temp, + members => [basename($tempname), $target], + ); + $fh = $self->run( $cmd ); + $self->wait($fh); + + # Once 7zip supports reading from stdin, this will work again: + #my $fh = $self->run( $cmd, + # binmode => ':raw', + # stdin => $scalar, + # verbose => 1, + #); +}; + +sub add { + my( $self, %options )= @_; + + my @items = @{ delete $options{ items } || [] }; + + # Split up the list into one batch for the listfiles + # and the list of files we need to rename + + my @filelist; + for my $item (@items) { + if( ! ref $item ) { + $item = [ $item, $item ]; + }; + my( $name, $storedName ) = @$item; + + if( $name ne $storedName ) { + # We need to pipe to 7zip from stdin (no, we don't, we can rename afterwards) + # This still means we might overwrite an already existing file in the archive... + # But 7-zip seems to not like duplicate filenames anyway in "@"-listfiles... + my $cmd = $self->get_command( + command => 'a', + archivename => $self->archive_or_temp, + members => [$name], + #options => ], + ); + my $fh = $self->run( $cmd ); + $self->wait($fh, %options ); + $cmd = $self->get_command( + command => 'rn', + archivename => $self->archive_or_temp, + members => [$name, $storedName], + #options => ], + ); + $fh = $self->run( $cmd ); + $self->wait($fh, %options ); + + } else { + # 7zip can read the file from disk + # Write the name to a tempfile to be read by 7zip for batching + push @filelist, $name; + }; + }; + + if( @filelist ) { + my( $fh, $name) = tempfile; + binmode $fh, ':raw'; + print {$fh} join "\r\n", @filelist; + close $fh; + + my @options; + if( $options{ recursive }) { + push @options, '-r'; + }; + + my $cmd = $self->get_command( + command => 'a', + archivename => $self->archive_or_temp, + members => ['@'.$name], + options => \@options + ); + $fh = $self->run( $cmd ); + $self->wait($fh, %options); + }; +}; + +sub archiveZipApi { + my( $class, %options ) = @_; + require Archive::SevenZip::API::ArchiveZip; + Archive::SevenZip::API::ArchiveZip->new( %options ) +} + +package Path::Class::Archive::Handle; +use strict; + +=head1 NAME + +Path::Class::Archive - treat archives as directories + +=cut + +package Path::Class::Archive; + +1; + +__END__ + +=head1 CAUTION + +This module tries to mimic the API of L in some cases +and in other cases, the API of L. It is also a very rough +draft that just happens to be doing what I need, mostly extracting +files. + +=head1 SEE ALSO + +L - also supports unpacking from 7z archives + +L - uncompressor for the LZMA compression method used by 7z + +=head1 REPOSITORY + +The public repository of this module is +L. + +=head1 SUPPORT + +The public support forum of this module is +L. + +=head1 BUG TRACKER + +Please report bugs in this module via the RT CPAN bug queue at +L +or via mail to L. + +=head1 AUTHOR + +Max Maischein C + +=head1 COPYRIGHT (c) + +Copyright 2015-2016 by Max Maischein C. + +=head1 LICENSE + +This module is released under the same terms as Perl itself. + +=cut diff --git a/lib/Archive/SevenZip/API/ArchiveZip.pm b/lib/Archive/SevenZip/API/ArchiveZip.pm new file mode 100644 index 0000000..710d4a8 --- /dev/null +++ b/lib/Archive/SevenZip/API/ArchiveZip.pm @@ -0,0 +1,244 @@ +package Archive::SevenZip::API::ArchiveZip; +use strict; +use Carp qw(croak); +use Encode qw( decode encode ); +use File::Basename qw(dirname basename); +use File::Copy; +use Archive::SevenZip 'AZ_OK'; + +use vars qw($VERSION); +$VERSION= '0.06'; + +sub new { + my( $class, %options )= @_; + $options{ sevenZip } = Archive::SevenZip->new(); + bless \%options => $class; +}; + +sub sevenZip { $_[0]->{sevenZip} } + +=head1 NAME + +Archive::SevenZip::API::ArchiveZip - Archive::Zip compatibility API + +=head1 SYNOPSIS + + my $ar = Archive::SevenZip->archiveZipApi( + find => 1, + archivename => $archivename, + verbose => $verbose, + ); + +This module implements just enough of the L +API to pass some of the Archive::Zip test files. Ideally you can +use this API to enable a script that uses Archive::Zip +to also read other archive files supported by 7z. + +=cut + +sub writeToFileNamed { + my( $self, $targetName )= @_; + copy( $self->sevenZip->{archivename}, $targetName ); + return AZ_OK; +} + +sub addFileOrDirectory { + my($self, $name, $newName, $compressionLevel) = @_; + $newName = $name + unless defined $newName; + $self->sevenZip->add( + items => [ [$name, $newName] ], + compression => $compressionLevel + ); +} + +sub addString { + my( $self, $content, $name, %options ) = @_; + $self->sevenZip->add_scalar($name => $content); + $self->memberNamed($name, %options); +} + +sub addDirectory { + # Create just a directory name + my( $self, $name, $target, %options ) = @_; + $target ||= $name; + + if( ref $name ) { + croak "Hashref API not supported, sorry"; + }; + + $self->sevenZip->add_directory($name, $target, %options); + $self->memberNamed($target, %options); +} + +sub members { + my( $self ) = @_; + $self->sevenZip->members; +} + +sub memberNames { + my( $self ) = @_; + map { $_->fileName } $self->sevenZip->members; +} + +sub membersMatching { + my( $self, $re, %options ) = @_; + grep { $_->fileName =~ /$re/ } $self->sevenZip->members; +} + +=head2 C<< $ar->numberOfMembers >> + + my $count = $az->numberOfMembers(); + +=cut + +sub numberOfMembers { + my( $self, %options ) = @_; + my @m = $self->members( %options ); + 0+@m +} + +=head2 C<< $az->memberNamed >> + + my $entry = $az->memberNamed('hello_world.txt'); + print $entry->fileName, "\n"; + +=cut + +# Archive::Zip API +sub memberNamed { + my( $self, $name, %options )= @_; + $self->sevenZip->memberNamed($name, %options ); +} + +sub extractMember { + my( $self, $name, $target, %options ) = @_; + if( ref $name and $name->can('fileName')) { + $name = $name->fileName; + }; + $self->sevenZip->extractMember( $name, $target, %options ); +} + +sub removeMember { + my( $self, $name, $target, %options ) = @_; + # Just for the result: + my $res = ref $name ? $name : $self->memberNamed( $name ); + + if( ref $name and $name->can('fileName')) { + $name = $name->fileName; + }; + $self->sevenZip->removeMember( $name, %options ); + + $res +} + +=head2 C<< $ar->replaceMember >> + + $ar->replaceMember('backup.txt', 'new-backup.txt'); + +Replaces the member in the archive. This is just delete then add. + +I clearly don't understand the utility of this method. It clearly +does not update the content of one file with the content of another +file, as the name of the new file can be different. + +=cut + +# strikingly similar to Archive::Zip API +sub replaceMember { + my( $self, $name, $replacement, %_options ) = @_; + + my %options = (%$self, %_options); + + if( $^O =~ /MSWin/ ) { + $name =~ s!/!\\!g; + } + + my $res = $self->removeMember( $name ); + $self->add( $replacement ); + + $res +}; + + +sub addFile { + my( $self, $name, $target, %options ) = @_; + if( ref $name and $name->can('fileName')) { + $name = $name->fileName; + }; + $target ||= $name; + $self->sevenZip->add( items => [[ $name, $target ]], %options ); + return $self->memberNamed($target, %options); +} + +sub addMember { + my( $self, $name, $target, %options ) = @_; + if( ref $name and $name->can('fileName')) { + $name = $name->fileName; + }; + $target ||= $name; + $self->sevenZip->add( items => [[ $name, $target ]], %options ); + return $self->memberNamed($target, %options); +} +{ no warnings 'once'; +*add = \&addMember; +} + +sub addTree { + my( $self, $sourceDir, $target, $predicate, %options ) = @_; + + croak "Predicates are not supported, sorry" + if $predicate; + + $target ||= $sourceDir; + croak "Different target for ->addTree not supported, sorry" + if $target ne $sourceDir; + + $self->sevenZip->add( items => [[ $sourceDir, $target ]], recursive => 1, %options ); + return $self->memberNamed($target, %options); +} +*add = \&addMember; + +__END__ + +=head1 CAUTION + +This module tries to mimic the API of L. + +=head2 Differences between Archive::Zip and Archive::SevenZip + +=head3 7-Zip does not guarantee the order of entries within an archive + +The Archive::Zip test suite assumes that items added later to an +archive will appear later in the directory listing. 7-zip makes no +such guarantee. + +=head1 REPOSITORY + +The public repository of this module is +L. + +=head1 SUPPORT + +The public support forum of this module is +L. + +=head1 BUG TRACKER + +Please report bugs in this module via the RT CPAN bug queue at +L +or via mail to L. + +=head1 AUTHOR + +Max Maischein C + +=head1 COPYRIGHT (c) + +Copyright 2015-2016 by Max Maischein C. + +=head1 LICENSE + +This module is released under the same terms as Perl itself. + +=cut \ No newline at end of file diff --git a/lib/Archive/SevenZip/Entry.pm b/lib/Archive/SevenZip/Entry.pm new file mode 100644 index 0000000..54cd713 --- /dev/null +++ b/lib/Archive/SevenZip/Entry.pm @@ -0,0 +1,138 @@ +package Archive::SevenZip::Entry; +use strict; + +use Time::Piece; # for strptime +use File::Basename (); +use Path::Class (); + +use vars qw($VERSION); +$VERSION= '0.06'; + +sub new { + my( $class, %options) = @_; + + bless \%options => $class +} + +sub archive { + $_[0]->{_Container} +} + +sub fileName { + my( $self ) = @_; + + my $res = $self->{Path}; + + # Normalize to unixy path names + $res =~ s!\\!/!g; + + # If we're a directory, append the slash: + if( $self->{Folder} eq '+') { + $res .= '/'; + }; + + $res +} + +# Class::Path API +sub basename { + Path::Class::file( $_[0]->{Path} )->basename +} + +sub components { + my $cp = file( $_[0]->{Path} ); + $cp->components() +} + +sub lastModFileDateTime { + 0 +} + +sub crc32 { + hex( $_[0]->{CRC} ); +} + +sub crc32String { + lc $_[0]->{CRC}; +} + +sub desiredCompressionMethod { + $_[0]->{Method} +} + +sub uncompressedSize { + $_[0]->{Size} +} + +sub dir { + # We need to return the appropriate class here + # so that further calls to (like) dir->list + # still work properly + die "->dir Not implemented"; +} + +sub open { + my( $self, $mode, $permissions )= @_; + $self->archive->openMemberFH( membername => $self->fileName, binmode => $mode ); +} +{ no warnings 'once'; +*fh = \&open; # Archive::Zip API +} + +# Path::Class API +sub slurp { + my( $self, %options )= @_; + my $fh = $self->archive->openMemberFH( membername => $self->fileName, binmode => $options{ iomode } ); + local $/; + <$fh> +} + +# Archive::Zip API +#externalFileName() + +# Archive::Zip API +#fileName() + +# Archive::Zip API +#lastModFileDateTime() + +# Archive::Zip API +#lastModTime() + +# Archive::Zip API +sub extractToFileNamed { + my($self, $target) = @_; + $self->archive->extractMember( $self->fileName, $target ); +}; + +1; + +=head1 REPOSITORY + +The public repository of this module is +L. + +=head1 SUPPORT + +The public support forum of this module is +L. + +=head1 BUG TRACKER + +Please report bugs in this module via the RT CPAN bug queue at +L +or via mail to L. + +=head1 AUTHOR + +Max Maischein C + +=head1 COPYRIGHT (c) + +Copyright 2015-2016 by Max Maischein C. + +=head1 LICENSE + +This module is released under the same terms as Perl itself. + +=cut \ No newline at end of file diff --git a/t/01-identity.t b/t/01-identity.t new file mode 100644 index 0000000..f8cad0d --- /dev/null +++ b/t/01-identity.t @@ -0,0 +1,57 @@ +#!perl -w +use strict; +use Archive::SevenZip; +use File::Basename; +use Test::More tests => 2; +use File::Temp 'tempfile'; + +my $version = Archive::SevenZip->find_7z_executable(); +if( ! $version ) { + SKIP: { skip "7z binary not found (not installed?)", 2; } + exit; +}; +diag "7-zip version $version"; + +my $base = dirname($0) . '/data'; +my $archivename = "$base/def.zip"; +my $ar = Archive::SevenZip->new( + archivename => $archivename, +); + +# Check that extraction to scalar and extraction to file +# result in the same output + +sub slurp { + my( $fh ) = @_; + binmode $fh; + local $/; + <$fh> +}; + +my $originalname = "$base/fred"; +open my $fh, '<', $originalname + or die "Couldn't read '$originalname': $!"; +my $original= slurp($fh); + +sub data_matches_ok { + my( $memory, $name) = @_; + if( length($memory) == -s $originalname) { + cmp_ok $memory, 'eq', $original, "extracted data matches ($name)"; + } else { + fail "extracted data matches ($name)"; + diag "Got [$memory]"; + diag "expected [$original]"; + }; +} + +my $memory = slurp( $ar->openMemberFH("fred")); +data_matches_ok( $memory, "Memory extraction" ); + +( $fh, my $tempname)= tempfile(); +close $fh; +$ar->extractMember("fred",$tempname); +open $fh, '<', $tempname + or die "Couldn't read '$tempname': $!"; +my $disk = slurp($fh); +data_matches_ok( $disk, "Direct disk extraction" ); + diff --git a/t/02-add-scalar.t b/t/02-add-scalar.t new file mode 100644 index 0000000..12386d1 --- /dev/null +++ b/t/02-add-scalar.t @@ -0,0 +1,38 @@ +#!perl -w +use strict; +use Archive::SevenZip; +use File::Basename; +use Test::More tests => 2; +use File::Temp 'tempfile'; + +my $version = Archive::SevenZip->find_7z_executable(); +if( ! $version ) { + SKIP: { skip "7z binary not found (not installed?)", 2; } + exit; +}; +diag "7-zip version $version"; +if( $version <= 9.20) { + SKIP: { + skip "7z version $version does not support renaming", 2; + } + exit +}; + +my $base = dirname($0) . '/data'; +my $ar = Archive::SevenZip->new( + #archivename => $archivename, + #type => '7z', +); + +#(my $tempname, undef) = tempfile; + +my $content = "This is\x{0d}\x{0a}the content"; +$ar->add_scalar('some-member.txt',$content); +#$ar->writeToFileNamed($tempname); + +my @contents = map { $_->fileName } $ar->list(); +is_deeply \@contents, ["some-member.txt"], "Contents of created archive are OK"; + +my $written = $ar->content( membername => 'some-member.txt', binmode => ':raw'); +is $written, $content, "Reading back the same data as we wrote"; + diff --git a/t/02_main.t b/t/02_main.t new file mode 100644 index 0000000..2c98572 --- /dev/null +++ b/t/02_main.t @@ -0,0 +1,381 @@ +#!/usr/bin/perl + +# Main testing for Archive::Zip + +use strict; + +BEGIN { + $| = 1; + $^W = 1; +} + +use Archive::SevenZip qw( :ERROR_CODES :CONSTANTS ); +use FileHandle; +use File::Path; +use File::Spec; + +use Test::More; + +use vars qw($testZipDoesntWork $status); + +BEGIN { +if( ! eval { + require t::common; + t::common->import; + 1 +}) { + plan skip_all => "Archive::Zip not installed, skipping compatibility tests", 83; + exit; + } else { + plan tests => 83; + } +} + +##################################################################### +# Testing Utility Functions + +#--------- check CRC +is(TESTSTRINGCRC(), 0xac373f32, 'Testing CRC matches expected'); + +# Bad times die +SCOPE: { + my @errors = (); + local $Archive::Zip::ErrorHandler = sub { push @errors, @_ }; + eval { Archive::Zip::Member::_unixToDosTime(0) }; + ok($errors[0] =~ /Tried to add member with zero or undef/, + 'Got expected _unixToDosTime error'); +} + +#--------- check time conversion + +foreach my $unix_time ( + 315576062, 315576064, 315580000, 315600000, + 316000000, 320000000, 400000000, 500000000, + 600000000, 700000000, 800000000, 900000000, + 1000000000, 1100000000, 1200000000, int(time() / 2) * 2, + ) { + my $dos_time = Archive::Zip::Member::_unixToDosTime($unix_time); + my $round_trip = Archive::Zip::Member::_dosToUnixTime($dos_time); + is($unix_time, $round_trip, 'Got expected DOS DateTime value'); +} + +##################################################################### +# Testing Archives + +my $version = Archive::SevenZip->find_7z_executable(); +if( ! $version ) { + SKIP: { skip "7z binary not found (not installed?)", 65; } + exit; +}; +diag "7-zip version $version"; +if( $version <= 9.20) { + SKIP: { + skip "7z version $version does not support renaming", 65; + } + exit +}; + +#--------- empty file +# new # Archive::Zip +# new # Archive::Zip::Archive +my $zip = Archive::SevenZip->archiveZipApi(); +isa_ok($zip, 'Archive::SevenZip::API::ArchiveZip'); + +# members # Archive::Zip::Archive +my @members = $zip->members; +is(scalar(@members), 0, '->members is 0'); + +# numberOfMembers # Archive::Zip::Archive +my $numberOfMembers = $zip->numberOfMembers(); +is($numberOfMembers, 0, '->numberofMembers is 0'); + +# writeToFileNamed # Archive::Zip::Archive +my $status = $zip->writeToFileNamed(OUTPUTZIP()); +is($status, AZ_OK, '->writeToFileNames ok'); + +my $zipout; +SKIP: { + skip("No 'unzip' program to test against", 1) unless HAVEUNZIP(); + if ($^O eq 'MSWin32') { + print STDERR + "\n# You might see an expected 'zipfile is empty' warning now.\n"; + } + ($status, $zipout) = testZip(); + + # STDERR->print("status= $status, out=$zipout\n"); + + skip("test zip doesn't work", 1) if $testZipDoesntWork; + + skip("freebsd's unzip doesn't care about empty zips", 1) + if $^O eq 'freebsd'; + + ok($status != 0); +} + +# unzip -t returns error code=1 for warning on empty + +#--------- add a directory +my $memberName = TESTDIR() . '/'; +my $dirName = TESTDIR(); + +# addDirectory # Archive::Zip::Archive +# new # Archive::Zip::Member +my $member = $zip->addDirectory($memberName); +ok(defined($member)); +is($member->fileName(), $memberName); + +# On some (Windows systems) the modification time is +# corrupted. Save this to check late. +my $dir_time = $member->lastModFileDateTime(); + +# members # Archive::Zip::Archive +@members = $zip->members(); +is(scalar(@members), 1); +is($members[0]->fileName, $member->fileName); + +# numberOfMembers # Archive::Zip::Archive +$numberOfMembers = $zip->numberOfMembers(); +is($numberOfMembers, 1); + +# writeToFileNamed # Archive::Zip::Archive +$status = $zip->writeToFileNamed(OUTPUTZIP()); +is($status, AZ_OK); + +# Does the modification time get corrupted? +is(($zip->members)[0]->lastModFileDateTime(), $dir_time); + +SKIP: { + skip("No 'unzip' program to test against", 1) unless HAVEUNZIP(); + ($status, $zipout) = testZip(); + + # STDERR->print("status= $status, out=$zipout\n"); + skip("test zip doesn't work", 1) if $testZipDoesntWork; + is($status, 0); +} + +#--------- extract the directory by name +rmtree([TESTDIR()], 0, 0); +$status = $zip->extractMember($memberName); +is($status, AZ_OK); +ok(-d $dirName); + +#--------- extract the directory by identity +ok(rmdir($dirName)); # it's still empty +$status = $zip->extractMember($member); +is($status, AZ_OK); +ok(-d $dirName); + +#--------- add a string member, uncompressed +$memberName = TESTDIR() . '/string.txt'; + +# addString # Archive::Zip::Archive +# newFromString # Archive::Zip::Member +$member = $zip->addString(TESTSTRING(), $memberName); +ok(defined($member)); + +is($member->fileName(), $memberName); + +# members # Archive::Zip::Archive +@members = $zip->members(); +is(scalar(@members), 2); +#is($members[1]->fileName, $member->fileName); + +# numberOfMembers # Archive::Zip::Archive +$numberOfMembers = $zip->numberOfMembers(); +is($numberOfMembers, 2); + +# writeToFileNamed # Archive::Zip::Archive +$status = $zip->writeToFileNamed(OUTPUTZIP()); +is($status, AZ_OK); + +SKIP: { + skip("No 'unzip' program to test against", 1) unless HAVEUNZIP(); + ($status, $zipout) = testZip(); + + # STDERR->print("status= $status, out=$zipout\n"); + skip("test zip doesn't work", 1) if $testZipDoesntWork; + is($status, 0); +} + +is($member->crc32(), TESTSTRINGCRC()); + +is($member->crc32String(), sprintf("%08x", TESTSTRINGCRC())); + +#--------- extract it by name +$status = $zip->extractMember($memberName); +is($status, AZ_OK); +ok(-f $memberName); +is(fileCRC($memberName), TESTSTRINGCRC()); + +#--------- now compress it and re-test +#my $oldCompressionMethod = +# $member->desiredCompressionMethod(COMPRESSION_DEFLATED); +#is($oldCompressionMethod, COMPRESSION_STORED, 'old compression method OK'); + +# writeToFileNamed # Archive::Zip::Archive +$status = $zip->writeToFileNamed(OUTPUTZIP()); +is($status, AZ_OK, 'writeToFileNamed returns AZ_OK'); +is($member->crc32(), TESTSTRINGCRC()); +is($member->uncompressedSize(), TESTSTRINGLENGTH()); + +SKIP: { + skip("No 'unzip' program to test against", 1) unless HAVEUNZIP(); + ($status, $zipout) = testZip(); + + # STDERR->print("status= $status, out=$zipout\n"); + skip("test zip doesn't work", 1) if $testZipDoesntWork; + is($status, 0); +} + +#--------- extract it by name +$status = $zip->extractMember($memberName); +is($status, AZ_OK); +ok(-f $memberName); +is(fileCRC($memberName), TESTSTRINGCRC()); + +#--------- add a file member, compressed +ok(rename($memberName, TESTDIR() . '/file.txt')); +$memberName = TESTDIR() . '/file.txt'; + +# addFile # Archive::Zip::Archive +# newFromFile # Archive::Zip::Member +$member = $zip->addFile($memberName); +ok(defined($member)); + +is($member->desiredCompressionMethod(), COMPRESSION_DEFLATED); + +# writeToFileNamed # Archive::Zip::Archive +$status = $zip->writeToFileNamed(OUTPUTZIP()); +is($status, AZ_OK); +is($member->crc32(), TESTSTRINGCRC()); +is($member->uncompressedSize(), TESTSTRINGLENGTH()); + +SKIP: { + skip("No 'unzip' program to test against", 1) unless HAVEUNZIP(); + ($status, $zipout) = testZip(); + + # STDERR->print("status= $status, out=$zipout\n"); + skip("test zip doesn't work", 1) if $testZipDoesntWork; + is($status, 0); +} + +#--------- extract it by name (note we have to rename it first +#--------- or we will clobber the original file +my $newName = $memberName; +$newName =~ s/\.txt/2.txt/; +$status = $zip->extractMember($memberName, $newName); +is($status, AZ_OK); +ok(-f $newName); +is(fileCRC($newName), TESTSTRINGCRC()); + +#--------- now make it uncompressed and re-test +#$oldCompressionMethod = $member->desiredCompressionMethod(COMPRESSION_STORED); + +#is($oldCompressionMethod, COMPRESSION_DEFLATED); + +# writeToFileNamed # Archive::Zip::Archive +$status = $zip->writeToFileNamed(OUTPUTZIP()); +is($status, AZ_OK); +is($member->crc32(), TESTSTRINGCRC()); +is($member->uncompressedSize(), TESTSTRINGLENGTH()); + +SKIP: { + skip("No 'unzip' program to test against", 1) unless HAVEUNZIP(); + ($status, $zipout) = testZip(); + + # STDERR->print("status= $status, out=$zipout\n"); + skip("test zip doesn't work", 1) if $testZipDoesntWork; + is($status, 0); +} + +#--------- extract it by name +$status = $zip->extractMember($memberName, $newName); +is($status, AZ_OK); +ok(-f $newName); +is(fileCRC($newName), TESTSTRINGCRC()); + +# Now, the contents of OUTPUTZIP() are: +# Length Method Size Ratio Date Time CRC-32 Name +#-------- ------ ------- ----- ---- ---- ------ ---- +# 0 Stored 0 0% 03-17-00 11:16 00000000 TESTDIR/ +# 300 Defl:N 146 51% 03-17-00 11:16 ac373f32 TESTDIR/string.txt +# 300 Stored 300 0% 03-17-00 11:16 ac373f32 TESTDIR/file.txt +#-------- ------- --- ------- +# 600 446 26% 3 files + +# members # Archive::Zip::Archive +@members = $zip->members(); +is(scalar(@members), 3); +is_deeply([map {$_->fileName} + grep { $_->fileName eq $member->fileName } @members ], + [$member->fileName]) + or do { diag "Have: " . $_->fileName for @members }; + +# memberNames # Archive::Zip::Archive +my @memberNames = $zip->memberNames(); +is(scalar(@memberNames), 3); +is_deeply([ grep { $_ eq $member->fileName } @memberNames ], + [ $member->fileName ]) +or do { diag sprintf "[%s]", $member->fileName ; diag sprintf "[%s]", $_->fileName for @members }; + +# memberNamed # Archive::Zip::Archive +is($zip->memberNamed($memberName)->fileName, $member->fileName); + +# membersMatching # Archive::Zip::Archive +@members = $zip->membersMatching('file'); +is(scalar(@members), 1); +is($members[0]->fileName, $member->fileName); + +@members = sort { $a->fileName cmp $b->fileName } $zip->membersMatching('.txt$'); +is(scalar(@members), 2); +is($members[0]->fileName, $member->fileName); + +#--------- remove the string member and test the file +# removeMember # Archive::Zip::Archive +diag "Removing " . $members[0]->fileName; +$member = $zip->removeMember($members[0]); +is($member, $members[0]); + +$status = $zip->writeToFileNamed(OUTPUTZIP()); +is($status, AZ_OK); + +SKIP: { + skip("No 'unzip' program to test against", 1) unless HAVEUNZIP(); + ($status, $zipout) = testZip(); + + # STDERR->print("status= $status, out=$zipout\n"); + skip("test zip doesn't work", 1) if $testZipDoesntWork; + is($status, 0); +} + +#--------- add the string member at the end and test the file +# addMember # Archive::Zip::Archive +# This will never work in Archive::SevenZip, transplanting +# zip entries in-memory +# This also ruins all of the subsequent tests due to the weirdo +# approach of not setting up a common baseline for each test +# and the insistence on that the implementation maintains the +# order on archive members +# +#$zip->addMember($member); +#@members = $zip->members(); + +#is(scalar(@members), 3); +#is($members[2], $member); + +# memberNames # Archive::Zip::Archive +#@memberNames = $zip->memberNames(); +#is(scalar(@memberNames), 3); +#is($memberNames[1], $memberName); + +#$status = $zip->writeToFileNamed(OUTPUTZIP()); +#is($status, AZ_OK); + +#SKIP: { +# skip("No 'unzip' program to test against", 1) unless HAVEUNZIP(); +# ($status, $zipout) = testZip(); + +# # STDERR->print("status= $status, out=$zipout\n"); +# skip("test zip doesn't work", 1) if $testZipDoesntWork; +# is($status, 0); +#} diff --git a/t/05_tree.t b/t/05_tree.t new file mode 100644 index 0000000..dbb6edd --- /dev/null +++ b/t/05_tree.t @@ -0,0 +1,59 @@ +#!/usr/bin/perl + +use strict; + +BEGIN { + $| = 1; + $^W = 1; +} +use Archive::SevenZip; +use FileHandle; +use File::Spec; + +use Test::More tests => 2; +BEGIN { +if( ! eval { + require t::common; + t::common->import; + 1 +}) { SKIP: { + skip "Archive::Zip not installed, skipping compatibility tests", 2; + } + exit; +}} + +my $version = Archive::SevenZip->find_7z_executable(); +if( ! $version ) { + SKIP: { skip "7z binary not found (not installed?)", 2; } + exit; +}; + + +use constant FILENAME => File::Spec->catfile(TESTDIR(), 'testing.txt'); + +my $zip; +my @memberNames; + +sub makeZip { + my ($src, $dest, $pred) = @_; + $zip = Archive::SevenZip->archiveZipApi(); + $zip->addTree($src, $dest,); + @memberNames = $zip->memberNames(); +} + +sub makeZipAndLookFor { + my ($src, $dest, $pred, $lookFor) = @_; + makeZip($src, $dest, $pred); + ok(@memberNames); + ok((grep { $_ eq $lookFor } @memberNames) == 1) + or print STDERR "Can't find $lookFor in (" + . join(",", @memberNames) . ")\n"; +} + +my ($testFileVolume, $testFileDirs, $testFileName) = File::Spec->splitpath($0); + +makeZipAndLookFor('.', '', sub { print "file $_\n"; -f && /\.t$/ }, + 't/02_main.t'); +# Not supported: +#makeZipAndLookFor('.', 'e/', sub { -f && /\.t$/ }, 'e/t/02_main.t'); +#makeZipAndLookFor('./t', '', sub { -f && /\.t$/ }, '02_main.t'); diff --git a/t/20_bug_github11.t b/t/20_bug_github11.t new file mode 100644 index 0000000..fb397ee --- /dev/null +++ b/t/20_bug_github11.t @@ -0,0 +1,59 @@ +#!/usr/bin/perl + +# Github 11: "CRC or size mismatch" when extracting member second time +# Test for correct functionality to prevent regression + +use strict; +use warnings; + +use Archive::SevenZip 'AZ_OK'; +use File::Spec; +use File::Path; + +use Test::More; + +BEGIN { +if( ! eval { + require t::common; + t::common->import; + 1 +}) { + plan skip_all => "Archive::Zip not installed, skipping compatibility tests"; + exit; + } + else { + plan tests => 2; + } +} + +my $version = Archive::SevenZip->find_7z_executable(); +if( ! $version ) { + SKIP: { skip "7z binary not found (not installed?)", 2; }; + exit +}; +diag "7-zip version $version"; + +# create test env +my $GH_ISSUE = 'github11'; +my $TEST_NAME = "20_bug_$GH_ISSUE"; +my $TEST_DIR = File::Spec->catdir(TESTDIR, $TEST_NAME); +mkpath($TEST_DIR); + +# test 1 +my $DATA_DIR = File::Spec->catfile('t', 'data'); +my $GOOD_ZIP_FILE = File::Spec->catfile($DATA_DIR, "good_${GH_ISSUE}.zip"); +my $GOOD_ZIP = Archive::SevenZip->new($GOOD_ZIP_FILE); +my $MEMBER_FILE = 'FILE'; +my $member = $GOOD_ZIP->memberNamed($MEMBER_FILE); +my $OUT_FILE = File::Spec->catfile($TEST_DIR, "out"); +# Extracting twice triggered the bug +$member->extractToFileNamed($OUT_FILE); +is($member->extractToFileNamed($OUT_FILE), AZ_OK, 'Testing known good zip'); + +# test 2 +my $BAD_ZIP_FILE = File::Spec->catfile($DATA_DIR, "bad_${GH_ISSUE}.zip"); +my $BAD_ZIP = Archive::SevenZip->new($BAD_ZIP_FILE); +$member = $BAD_ZIP->memberNamed($MEMBER_FILE); +# Extracting twice triggered the bug +$member->extractToFileNamed($OUT_FILE); +is($member->extractToFileNamed($OUT_FILE), AZ_OK, 'Testing known bad zip'); diff --git a/t/badjpeg/expected.jpg b/t/badjpeg/expected.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0b362db828948180df9bedf2e4d80e6113c4d3c4 GIT binary patch literal 56897 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 do { + my $tmpdir = File::Spec->abs2rel(tempdir(DIR => 'testdir', CLEANUP => 1)); + $tmpdir =~ s!\\!/!g if $^O eq 'MSWin32'; + $tmpdir +}; +use constant INPUTZIP => + (tempfile('testin-XXXXX', SUFFIX => '.zip', TMPDIR => 1, $^O eq 'MSWin32' ? () : (UNLINK => 1)))[1]; +use constant OUTPUTZIP => + (tempfile('testout-XXXXX', SUFFIX => '.zip', TMPDIR => 1, $^O eq 'MSWin32' ? () : (UNLINK => 1)))[1]; + +# Do we have the 'zip' and 'unzip' programs? +# Embed a copy of the module, rather than adding a dependency +BEGIN { + + package File::Which; + + use File::Spec; + + my $Is_VMS = ($^O eq 'VMS'); + my $Is_MacOS = ($^O eq 'MacOS'); + my $Is_DOSish = + (($^O eq 'MSWin32') or ($^O eq 'dos') or ($^O eq 'os2')); + + # For Win32 systems, stores the extensions used for + # executable files + # For others, the empty string is used + # because 'perl' . '' eq 'perl' => easier + my @path_ext = (''); + if ($Is_DOSish) { + if ($ENV{PATHEXT} and $Is_DOSish) + { # WinNT. PATHEXT might be set on Cygwin, but not used. + push @path_ext, split ';', $ENV{PATHEXT}; + } else { + push @path_ext, qw(.com .exe .bat) + ; # Win9X or other: doesn't have PATHEXT, so needs hardcoded. + } + } elsif ($Is_VMS) { + push @path_ext, qw(.exe .com); + } + + sub which { + my ($exec) = @_; + + return undef unless $exec; + + my $all = wantarray; + my @results = (); + + # check for aliases first + if ($Is_VMS) { + my $symbol = `SHOW SYMBOL $exec`; + chomp($symbol); + if (!$?) { + return $symbol unless $all; + push @results, $symbol; + } + } + if ($Is_MacOS) { + my @aliases = split /\,/, $ENV{Aliases}; + foreach my $alias (@aliases) { + + # This has not been tested!! + # PPT which says MPW-Perl cannot resolve `Alias $alias`, + # let's just hope it's fixed + if (lc($alias) eq lc($exec)) { + chomp(my $file = `Alias $alias`); + last unless $file; # if it failed, just go on the normal way + return $file unless $all; + push @results, $file; + + # we can stop this loop as if it finds more aliases matching, + # it'll just be the same result anyway + last; + } + } + } + + my @path = File::Spec->path(); + unshift @path, File::Spec->curdir if $Is_DOSish or $Is_VMS or $Is_MacOS; + + for my $base (map { File::Spec->catfile($_, $exec) } @path) { + for my $ext (@path_ext) { + my $file = $base . $ext; + + # print STDERR "$file\n"; + + if ( + ( + -x $file or # executable, normal case + ( + $Is_MacOS + || # MacOS doesn't mark as executable so we check -e + ( + $Is_DOSish + and grep { $file =~ /$_$/i } + @path_ext[1 .. $#path_ext]) + + # DOSish systems don't pass -x on non-exe/bat/com files. + # so we check -e. However, we don't want to pass -e on files + # that aren't in PATHEXT, like README. + and -e _)) + and !-d _) + { # and finally, we don't want dirs to pass (as they are -x) + + # print STDERR "-x: ", -x $file, " -e: ", -e _, " -d: ", -d _, "\n"; + + return $file unless $all; + push @results, $file; # Make list to return later + } + } + } + + if ($all) { + return @results; + } else { + return undef; + } + } +} +use constant HAVEZIP => !!File::Which::which('zip'); +use constant HAVEUNZIP => !!File::Which::which('unzip'); + +use constant ZIP => 'zip '; +use constant ZIPTEST => 'unzip -t '; + +# 300-character test string +use constant TESTSTRING => join("\n", 1 .. 102) . "\n"; +use constant TESTSTRINGLENGTH => length(TESTSTRING); + +use Archive::Zip (); + +# CRC-32 should be ac373f32 +use constant TESTSTRINGCRC => Archive::Zip::computeCRC32(TESTSTRING); + +# This is so that it will work on other systems. +use constant CAT => $^X . ' -pe "BEGIN{binmode(STDIN);binmode(STDOUT)}"'; +use constant CATPIPE => '| ' . CAT . ' >'; + +use vars qw($zipWorks $testZipDoesntWork $catWorks); + +# Run ZIPTEST to test a zip file. +sub testZip { + my $zipName = shift || OUTPUTZIP; + if ($testZipDoesntWork) { + return wantarray ? (0, '') : 0; + } + my $cmd = ZIPTEST . $zipName . ($^O eq 'MSWin32' ? '' : ' 2>&1'); + my $zipout = `$cmd`; + return wantarray ? ($?, $zipout) : $?; +} + +# Return the crc-32 of the given file (0 if empty or error) +sub fileCRC { + my $fileName = shift; + local $/ = undef; + my $fh = IO::File->new($fileName, "r"); + binmode($fh); + return 0 if not defined($fh); + my $contents = <$fh>; + return Archive::Zip::computeCRC32($contents); +} + +#--------- check to see if cat works + +sub testCat { + my $fh = IO::File->new(CATPIPE . OUTPUTZIP); + binmode($fh); + my $testString = pack('C256', 0 .. 255); + my $testCrc = Archive::Zip::computeCRC32($testString); + $fh->write($testString, length($testString)) or return 0; + $fh->close(); + (-f OUTPUTZIP) or return 0; + my @stat = stat(OUTPUTZIP); + $stat[7] == length($testString) or return 0; + fileCRC(OUTPUTZIP) == $testCrc or return 0; + unlink(OUTPUTZIP); + return 1; +} + +BEGIN { + $catWorks = testCat(); + unless ($catWorks) { + warn('warning: ', CAT, " doesn't seem to work, may skip some tests"); + } +} + +#--------- check to see if zip works (and make INPUTZIP) + +BEGIN { + unlink(INPUTZIP); + + # Do we have zip installed? + if (HAVEZIP) { + my $cmd = ZIP . INPUTZIP . ' *' . ($^O eq 'MSWin32' ? '' : ' 2>&1'); + my $zipout = `$cmd`; + $zipWorks = not $?; + unless ($zipWorks) { + warn('warning: ', ZIP, + " doesn't seem to work, may skip some tests"); + } + } +} + +#--------- check to see if unzip -t works + +BEGIN { + $testZipDoesntWork = 1; + if (HAVEUNZIP) { + my ($status, $zipout) = do { local $testZipDoesntWork = 0; testZip(INPUTZIP) }; + $testZipDoesntWork = $status; + + # Again, on Win32 no big surprise if this doesn't work + if ($testZipDoesntWork) { + warn('warning: ', ZIPTEST, + " doesn't seem to work, may skip some tests"); + } + } +} + +sub passthrough +{ + my $fromFile = shift ; + my $toFile = shift ; + my $action = shift ; + + my $z = Archive::Zip->new; + $z->read($fromFile); + if ($action) + { + for my $member($z->members()) + { + &$action($member) ; + } + } + $z->writeToFileNamed($toFile); +} + +sub readFile +{ + my $name = shift ; + local $/; + open F, "<$name" + or die "Cannot open $name: $!\n"; + my $data = ; + close F ; + return $data; +} + +1; diff --git a/t/data/bad_github11.zip b/t/data/bad_github11.zip new file mode 100644 index 0000000000000000000000000000000000000000..3fe4892be1ac4b586547de2099a9a1a417b68faf GIT binary patch literal 455 zcmWIWW@Zs#-~hr6DSfUCNPv@pg~8V~#8KDN&rSc|DFy~+h5&DN4v-2asImZ@nni#r z;F^6M{XE@VgG2Ou-9G!CIql=Et9OytTUYDcne&^246YbIcv__A<*VcAd$DvC3+IfN zl1HSawKG_zKhyk?rY8PWJX~!0l4oMt70*;(XMQUBSi}f+$gRs$PP+rG0bztgmZJHB z1t{z0>Ep_se!^p^P{0!=uwLx~c3&=#6Tx~JnM4>+!vN+%P#B;BxTioN65x%h71>Fk s5I_K1AQP??DLjx(0687mVW?Ig1DXKxNPssh8^~N{AY2cmnHWJl0EkECtR_hm z9g*t|@Mh;gHb20dkx7mjm)#OT(-{~Tfp}XZhy`&8E5s#et_bjE1-cicnvuZ^NLPV4 F3;=Qo#Dc3d67N+17htK1KGV^Zk-K8zA?5&Yb`E4?1H2iT r5%TC=OtSH~`J^0B=?{kWxk<3Bp literal 0 HcmV?d00001 diff --git a/t/data/def.zip b/t/data/def.zip new file mode 100644 index 0000000000000000000000000000000000000000..2c2890f53f0a08b0c4d40ba11981a76f2807f1f9 GIT binary patch literal 111 zcmWIWW@Zs#U|`^2xH5Gn!y!i%Q&u348HibcIISo(#oNb=jUm9Bkx7IZw?d#=BmmMD S;LXYg5@G~GBOt8~mInZy3=NI| literal 0 HcmV?d00001 diff --git a/t/data/defstr.zip b/t/data/defstr.zip new file mode 100644 index 0000000000000000000000000000000000000000..60591d15835e5392c64d405b35649a0d96c9a8bb GIT binary patch literal 127 zcmWIWW@Zs#-~hraQ)e+g=03iMjSO5S3 literal 0 HcmV?d00001 diff --git a/t/data/emptydef.zip b/t/data/emptydef.zip new file mode 100644 index 0000000000000000000000000000000000000000..87b26a3950546c6b4262ad059e52adc38c7e23b0 GIT binary patch literal 108 zcmWIWW@Zs#U|`^2xH5GnkY-?D0+S33EI>M~C^dzdA;6oFNrV}8f%)kHu@*4`2 literal 0 HcmV?d00001 diff --git a/t/data/fred b/t/data/fred new file mode 100644 index 0000000..f2ba8f8 --- /dev/null +++ b/t/data/fred @@ -0,0 +1 @@ +abc \ No newline at end of file diff --git a/t/data/good_github11.zip b/t/data/good_github11.zip new file mode 100644 index 0000000000000000000000000000000000000000..39ee39265dee26537818e30229f2be63289e42b7 GIT binary patch literal 407 zcmWIWW@Zs#U|`^2*pSla3S=-afe0YrWME*`(P_14uocjo-&AcHH$51tn3dHL#i`d%zu z#lkt`rQ{K5Y3&S_>CZHOq^XHN6%QAizT}yhcEvN**O{M+J{B<|S-ceJO6>x6UoMb^ zK+FQfZk|4_-03GgmI?(tVFIdUWD)^82)mzv8jt|o(I6k9Yen}VLZ2>>3D=6`Ut|-o cITc~TG9VM=@BnXCHjpSY5UvN(mp~i_0EahKcmMzZ literal 0 HcmV?d00001 diff --git a/t/data/jar.zip b/t/data/jar.zip new file mode 100644 index 0000000000000000000000000000000000000000..a8da50f880c2ab3982ef14ecbbe7c27f25ad1584 GIT binary patch literal 448 zcmWIWW@Zs#;Nak3xV+)K8v_#HWME3R9;c=}!}UB$vV ziU4m^t;p^Hc^v_4flRno iq<}y-0oh@wRv!YI0P;nEH!B;+TxK9#52SyCI1B)k<5^Mw literal 0 HcmV?d00001 diff --git a/t/data/linux.zip b/t/data/linux.zip new file mode 100644 index 0000000000000000000000000000000000000000..3f8e449f509bb329061d3c01333c13de1ab7efbe GIT binary patch literal 140 zcmWIWW@h1H0D<#GK9*nxlwe^HWk}1<*AEThWMCHi_0a=_Ln~MqUNXOA4)A7V5@*Jx mkr!%&0K;2H5EEhyE5w-43I>J%Z&o&t1S1gI18H**hXDYQ2^Jmz literal 0 HcmV?d00001 diff --git a/t/data/mkzip.pl b/t/data/mkzip.pl new file mode 100644 index 0000000..b445846 --- /dev/null +++ b/t/data/mkzip.pl @@ -0,0 +1,54 @@ +#!/usr/bin/perl + +#This script will create test zip files used by some of the tests. +# +# File Length Streamed Method +# =============================================== +# emptydef.zip Yes No Deflate +# emptydefstr.zip Yes Yes Deflate +# emptystore.zip Yes No Store +# emptystorestr.zip Yes Yes Store +# + + +use warnings; +use strict; + +use IO::Compress::Zip qw(:all); + +my $time = 325532800; + +zip \"" => "emptydef.zip", + Name => "fred", Stream => 0, Method => ZIP_CM_DEFLATE, Time => $time + or die "Cannot create zip: $ZipError"; + +zip \"" => "emptydefstr.zip", + Name => "fred", Stream => 1, Method => ZIP_CM_DEFLATE, Time => $time + or die "Cannot create zip: $ZipError"; + +zip \"" => "emptystore.zip", + Name => "fred", Stream => 0, Method => ZIP_CM_STORE, Time => $time + or die "Cannot create zip: $ZipError"; + +zip \"" => "emptystorestr.zip", + Name => "fred", Stream => 1, Method => ZIP_CM_STORE, Time => $time + or die "Cannot create zip: $ZipError"; + + + +zip \"abc" => "def.zip", + Name => "fred", Stream => 0, Method => ZIP_CM_DEFLATE, Time => $time + or die "Cannot create zip: $ZipError"; + +zip \"abc" => "defstr.zip", + Name => "fred", Stream => 1, Method => ZIP_CM_DEFLATE, Time => $time + or die "Cannot create zip: $ZipError"; + +zip \"abc" => "store.zip", + Name => "fred", Stream => 0, Method => ZIP_CM_STORE, Time => $time + or die "Cannot create zip: $ZipError"; + +zip \"abc" => "storestr.zip", + Name => "fred", Stream => 1, Method => ZIP_CM_STORE, Time => $time + or die "Cannot create zip: $ZipError"; + diff --git a/t/data/perl.zip b/t/data/perl.zip new file mode 100644 index 0000000000000000000000000000000000000000..0430db34e442c1ba9c58d082ef57defcd31bff8a GIT binary patch literal 106 zcmWIWW@Zs#00Du`K9*nxlwbkUY5Dp30p5&EBFwne!1TOz1hF7GSlK{qMj$i-(n=r> F0{{-q2`B&n literal 0 HcmV?d00001 diff --git a/t/data/store.zip b/t/data/store.zip new file mode 100644 index 0000000000000000000000000000000000000000..f8e449661eff7b139e938d5da884d5051d895a37 GIT binary patch literal 109 zcmWIWW@Zs#0D&u0XEGdeR54`+vO$;yh|`KvQxcPs1H2iTM3`|a1FA#MCD#st literal 0 HcmV?d00001 diff --git a/t/data/streamed.zip b/t/data/streamed.zip new file mode 100644 index 0000000000000000000000000000000000000000..90c0ed37932c522fb555258e9ccc8c5e69126902 GIT binary patch literal 127 zcmWIWW@Zs#-~hsHp>tdrkN^viomP~Z;_c(b#t`7m&T+_5#gr8&$PB~*-i%Bl%m|Ih dav+T`u%r>h0vQwF&B_L1GXkLzkTwT#7yvZA4^aRB literal 0 HcmV?d00001 diff --git a/t/data/winzip.zip b/t/data/winzip.zip new file mode 100644 index 0000000000000000000000000000000000000000..d8b1f0c02a8ee6b0092c0914b5b73d1c7d8c959c GIT binary patch literal 106 zcmWIWW@h1H0D+v%K9*nxlwbkUY5Dp30p5&EA`H0IK$HtWbwPBnvVpjaKxhP{mB4ZU D93=>= literal 0 HcmV?d00001 diff --git a/t/data/zip64.zip b/t/data/zip64.zip new file mode 100644 index 0000000000000000000000000000000000000000..a2ee1fa33dca48e1ec8dfc7507640bfa09bddeb6 GIT binary patch literal 242 zcmWIWW@Zs#U|`^2Feu@2tb6`HQw7KaVKyKRa&>g^b>%*JLMKe)obQ>FfgY#Nc!n~3 zGWsmC$cFihkI1D@-9^IQUv@AAcr!BTGV7w4^dAb?7(g~az>-D~4KbIIK>%zMNCadf u2n2YuvFSjV47xxF1B_4xjP`)?VKh)5J4k2(lDYtIR*)wcVD13X3=9B3ZZ^#T literal 0 HcmV?d00001 diff --git a/xt/99-changes.t b/xt/99-changes.t new file mode 100644 index 0000000..c383123 --- /dev/null +++ b/xt/99-changes.t @@ -0,0 +1,28 @@ +#!perl -w +use warnings; +use strict; +use File::Find; +use Test::More tests => 2; + +=head1 PURPOSE + +This test ensures that the Changes file +mentions the current version and that a +release date is mentioned as well + +=cut + +my $module = 'Archive::SevenZip'; + +(my $file = $module) =~ s!::!/!g; +require "$file.pm"; + +my $version = sprintf '%0.2f', $module->VERSION; +diag "Checking for version " . $version; + +my $changes = do { local $/; open my $fh, 'Changes' or die $!; <$fh> }; + +ok $changes =~ /^(.*$version.*)$/m, "We find version $version"; +my $changes_line = $1; +ok $changes_line =~ /$version\s+20\d{6}/, "We find a release date on the same line" + or diag $changes_line; diff --git a/xt/99-compile.t b/xt/99-compile.t new file mode 100644 index 0000000..0bd8be6 --- /dev/null +++ b/xt/99-compile.t @@ -0,0 +1,43 @@ +#!perl -w + +use warnings; +use strict; +use File::Find; +use Test::More; +BEGIN { + eval 'use Capture::Tiny ":all"; 1'; + if ($@) { + plan skip_all => "Capture::Tiny needed for testing"; + exit 0; + }; +}; + +plan 'no_plan'; + +my $last_version = undef; + +sub check { + return if (! m{(\.pm|\.pl) \z}xmsi); + + my ($stdout, $stderr, $exit) = capture(sub { + system( $^X, '-Mblib', '-wc', $_ ); + }); + + s!\s*\z!! + for ($stdout, $stderr); + + if( $exit ) { + diag $exit; + fail($_); + } elsif( $stderr ne "$_ syntax OK") { + diag $stderr; + fail($_); + } else { + pass($_); + }; +} + +find({wanted => \&check, no_chdir => 1}, + grep { -d $_ } + 'blib', 'scripts', 'examples', 'bin', 'lib' + ); diff --git a/xt/99-manifest.t b/xt/99-manifest.t new file mode 100644 index 0000000..4a54e92 --- /dev/null +++ b/xt/99-manifest.t @@ -0,0 +1,31 @@ +use strict; +use Test::More; + +# Check that MANIFEST and MANIFEST.skip are sane : + +use File::Find; +use File::Spec; + +my @files = qw( MANIFEST MANIFEST.SKIP ); +plan tests => scalar @files * 4 + +1 # MANIFEST existence check + ; + +for my $file (@files) { + ok(-f $file, "$file exists"); + open F, "<$file" + or die "Couldn't open $file : $!"; + my @lines = ; + is_deeply([grep(/^$/, @lines)],[], "No empty lines in $file"); + is_deeply([grep(/^\s+$/, @lines)],[], "No whitespace-only lines in $file"); + is_deeply([grep(/^\s*\S\s+$/, @lines)],[],"No trailing whitespace on lines in $file"); + + if ($file eq 'MANIFEST') { + chomp @lines; + is_deeply([grep { s/\s.*//; ! -f } @lines], [], "All files in $file exist") + or do { diag "$_ is mentioned in $file but doesn't exist on disk" for grep { ! -f } @lines }; + }; + + close F; +}; + diff --git a/xt/99-minimumversion.t b/xt/99-minimumversion.t new file mode 100644 index 0000000..d649b7a --- /dev/null +++ b/xt/99-minimumversion.t @@ -0,0 +1,17 @@ +#!perl -w +use strict; +use Test::More; + +eval { + require Test::MinimumVersion::Fast; + Test::MinimumVersion::Fast->import; +}; + +my @files; + +if ($@) { + plan skip_all => "Test::MinimumVersion::Fast required for testing minimum Perl version"; +} +else { + all_minimum_version_from_metayml_ok(); +} diff --git a/xt/99-pod.t b/xt/99-pod.t new file mode 100644 index 0000000..079bc40 --- /dev/null +++ b/xt/99-pod.t @@ -0,0 +1,36 @@ +use Test::More; + +# Check our Pod +# The test was provided by Andy Lester, +# who stole it from Brian D. Foy +# Thanks to both ! + +use File::Spec; +use File::Find; +use strict; + +eval { + require Test::Pod; + Test::Pod->import; +}; + +my @files; + +if ($@) { + plan skip_all => "Test::Pod required for testing POD"; +} +elsif ($Test::Pod::VERSION < 0.95) { + plan skip_all => "Test::Pod 0.95 required for testing POD"; +} +else { + my $blib = File::Spec->catfile(qw(blib lib)); + find(\&wanted, grep { -d } ($blib, 'bin')); + plan tests => scalar @files; + foreach my $file (@files) { + pod_file_ok($file); + } +} + +sub wanted { + push @files, $File::Find::name if /\.p(l|m|od)$/; +} diff --git a/xt/99-todo.t b/xt/99-todo.t new file mode 100644 index 0000000..17f8aef --- /dev/null +++ b/xt/99-todo.t @@ -0,0 +1,47 @@ +use Test::More; +use File::Spec; +use File::Find; +use strict; + +# Check that all files do not contain any +# lines with "XXX" - such markers should +# either have been converted into Todo-stuff +# or have been resolved. +# The test was provided by Andy Lester. + +my @files; +my $blib = File::Spec->catfile(qw(blib lib)); +find(\&wanted, grep { -d } ($blib, 'bin')); +plan tests => 2* @files; +foreach my $file (@files) { + source_file_ok($file); +} + +sub wanted { + push @files, $File::Find::name if /\.p(l|m|od)$/; +} + +sub source_file_ok { + my $file = shift; + + open( my $fh, "<$file" ) or die "Can't open $file: $!"; + my @lines = <$fh>; + close $fh; + + my $n = 0; + for ( @lines ) { + ++$n; + s/^/$file ($n): /; + } + + my @x = grep /XXX/, @lines; + + if ( !is( scalar @x, 0, "Looking for XXXes in $file" ) ) { + diag( $_ ) for @x; + } + @x = grep /<<<|>>>/, @lines; + + if ( !is( scalar @x, 0, "Looking for <<<<|>>>> in $file" ) ) { + diag( $_ ) for @x; + } +} diff --git a/xt/99-unix-text.t b/xt/99-unix-text.t new file mode 100644 index 0000000..f91c526 --- /dev/null +++ b/xt/99-unix-text.t @@ -0,0 +1,37 @@ +use Test::More; + +# Check that all released module files are in +# UNIX text format + +use File::Spec; +use File::Find; +use strict; + +my @files; + +my $blib = File::Spec->catfile(qw(blib lib)); +find(\&wanted, grep { -d } ($blib, 'bin')); +plan tests => scalar @files; +foreach my $file (@files) { + unix_file_ok($file); +} + +sub wanted { + push @files, $File::Find::name if /\.p(l|m|od)$/; +} + +sub unix_file_ok { + my ($filename) = @_; + local $/; + open F, "< $filename" + or die "Couldn't open '$filename' : $!\n"; + binmode F; + my $content = ; + + my $i; + my @lines = grep { /\x0D\x0A$/sm } map { sprintf "%s: %s\x0A", $i++, $_ } split /\x0A/, $content; + unless (is(scalar @lines, 0,"'$filename' contains no windows newlines")) { + diag $_ for @lines; + }; + close F; +}; diff --git a/xt/99-versions.t b/xt/99-versions.t new file mode 100644 index 0000000..49da187 --- /dev/null +++ b/xt/99-versions.t @@ -0,0 +1,51 @@ +#!perl -w + +# Stolen from ChrisDolan on use.perl.org +# http://use.perl.org/comments.pl?sid=29264&cid=44309 + +use warnings; +use strict; +use File::Find; +use Test::More; +BEGIN { + eval 'use File::Slurp; 1'; + if ($@) { + plan skip_all => "File::Slurp needed for testing"; + exit 0; + }; +}; + +plan 'no_plan'; + +my $last_version = undef; + +sub check { + return if (! m{blib/script/}xms && ! m{\.pm \z}xms); + + my $content = read_file($_); + + # only look at perl scripts, not sh scripts + return if (m{blib/script/}xms && $content !~ m/\A \#![^\r\n]+?perl/xms); + + my @version_lines = $content =~ m/ ( [^\n]* \$VERSION \s* = [^=] [^\n]* ) /gxms; + if (@version_lines == 0) { + fail($_); + } + for my $line (@version_lines) { + $line =~ s/^\s+//; + $line =~ s/\s+$//; + if (!defined $last_version) { + $last_version = shift @version_lines; + diag "Checking for $last_version"; + pass($_); + } else { + is($line, $last_version, $_); + } + } +} + +find({wanted => \&check, no_chdir => 1}, 'blib'); + +if (! defined $last_version) { + fail('Failed to find any files with $VERSION'); +}