IO::Compress* 2.000_12
Paul Marquess [Wed, 17 May 2006 13:45:16 +0000 (14:45 +0100)]
From: "Paul Marquess" <paul.marquess@ntlworld.com>
Message-ID: <00c101c679af$c0305af0$2405140a@myopwv.com>

p4raw-id: //depot/perl@28214

35 files changed:
MANIFEST
ext/Compress/IO/Base/README
ext/Compress/IO/Base/lib/IO/Compress/Base.pm
ext/Compress/IO/Base/lib/IO/Compress/Base/Common.pm
ext/Compress/IO/Base/lib/IO/Uncompress/AnyUncompress.pm
ext/Compress/IO/Base/lib/IO/Uncompress/Base.pm
ext/Compress/IO/Base/t/01misc.t
ext/Compress/IO/Zlib/Changes
ext/Compress/IO/Zlib/README
ext/Compress/IO/Zlib/lib/IO/Compress/Adapter/Deflate.pm
ext/Compress/IO/Zlib/lib/IO/Compress/Adapter/Identity.pm
ext/Compress/IO/Zlib/lib/IO/Compress/Deflate.pm
ext/Compress/IO/Zlib/lib/IO/Compress/Gzip.pm
ext/Compress/IO/Zlib/lib/IO/Compress/Gzip/Constants.pm
ext/Compress/IO/Zlib/lib/IO/Compress/RawDeflate.pm
ext/Compress/IO/Zlib/lib/IO/Compress/Zip.pm
ext/Compress/IO/Zlib/lib/IO/Compress/Zip/Constants.pm
ext/Compress/IO/Zlib/lib/IO/Compress/Zlib/Constants.pm
ext/Compress/IO/Zlib/lib/IO/Compress/Zlib/Extra.pm [new file with mode: 0644]
ext/Compress/IO/Zlib/lib/IO/Uncompress/Adapter/Identity.pm
ext/Compress/IO/Zlib/lib/IO/Uncompress/Adapter/Inflate.pm
ext/Compress/IO/Zlib/lib/IO/Uncompress/AnyInflate.pm
ext/Compress/IO/Zlib/lib/IO/Uncompress/Gunzip.pm
ext/Compress/IO/Zlib/lib/IO/Uncompress/Inflate.pm
ext/Compress/IO/Zlib/lib/IO/Uncompress/RawInflate.pm
ext/Compress/IO/Zlib/lib/IO/Uncompress/Unzip.pm
ext/Compress/IO/Zlib/t/004gziphdr.t
ext/Compress/IO/Zlib/t/101truncate-zip.t
ext/Compress/Raw/Zlib/Changes [new file with mode: 0644]
ext/Compress/Raw/Zlib/README
ext/Compress/Raw/Zlib/lib/Compress/Raw/Zlib.pm
ext/Compress/Zlib/Changes
ext/Compress/Zlib/README
ext/Compress/Zlib/lib/Compress/Zlib.pm
t/lib/compress/CompTestUtils.pm

index 5fc8a5d..2c3fc2e 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -154,6 +154,7 @@ ext/Compress/Raw/Zlib/typemap               Compress::Raw::Zlib
 ext/Compress/Raw/Zlib/Makefile.PL      Compress::Raw::Zlib
 ext/Compress/Raw/Zlib/ppport.h         Compress::Raw::Zlib
 ext/Compress/Raw/Zlib/config.in                Compress::Raw::Zlib
+ext/Compress/Raw/Zlib/Changes          Compress::Raw::Zlib
 ext/Compress/Raw/Zlib/zlib-src/adler32.c       Compress::Raw::Zlib
 ext/Compress/Raw/Zlib/zlib-src/compress.c      Compress::Raw::Zlib
 ext/Compress/Raw/Zlib/zlib-src/crc32.c Compress::Raw::Zlib
@@ -283,6 +284,7 @@ ext/Compress/IO/Zlib/lib/IO/Compress/Zip.pm IO::Compress::Zlib
 ext/Compress/IO/Zlib/lib/IO/Compress/Zip/Constants.pm  IO::Compress::Zlib
 ext/Compress/IO/Zlib/lib/IO/Compress/Gzip/Constants.pm IO::Compress::Zlib
 ext/Compress/IO/Zlib/lib/IO/Compress/Zlib/Constants.pm IO::Compress::Zlib
+ext/Compress/IO/Zlib/lib/IO/Compress/Zlib/Extra.pm     IO::Compress::Zlib
 ext/Compress/IO/Zlib/lib/IO/Compress/Adapter/Deflate.pm        IO::Compress::Zlib
 ext/Compress/IO/Zlib/lib/IO/Compress/Adapter/Identity.pm       IO::Compress::Zlib
 ext/Compress/IO/Zlib/Makefile.PL       IO::Compress::Zlib
index cef3598..f6fea3d 100644 (file)
@@ -1,9 +1,9 @@
 
                              IO::Compress::Base
 
-                             Version 2.000_10
+                             Version 2.000_12
 
-                                13 Mar 2006 
+                                17 May 2006
 
 
        Copyright (c) 2005-2006 Paul Marquess. All rights reserved.
index 14363bc..952fd6c 100644 (file)
@@ -20,7 +20,7 @@ use bytes;
 our (@ISA, $VERSION, $got_encode);
 #@ISA    = qw(Exporter IO::File);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 
 #Can't locate object method "SWASHNEW" via package "utf8" (perhaps you forgot to load "utf8"?) at .../ext/Compress-Zlib/Gzip/blib/lib/Compress/Zlib/Common.pm line 16.
 
index f17fe47..0e1ffa0 100644 (file)
@@ -11,7 +11,7 @@ use File::GlobMapper;
 require Exporter;
 our ($VERSION, @ISA, @EXPORT, %EXPORT_TAGS);
 @ISA = qw(Exporter);
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 
 @EXPORT = qw( isaFilehandle isaFilename whatIsInput whatIsOutput 
               isaFileGlobString cleanFileGlobString oneTarget
index b733965..54ec621 100644 (file)
@@ -26,7 +26,7 @@ require Exporter ;
 
 our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, $AnyUncompressError);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 $AnyUncompressError = '';
 
 @ISA = qw( Exporter IO::Uncompress::Base );
@@ -387,9 +387,13 @@ If the C<$output> parameter is any other type, C<undef> will be returned.
 
 =head2 Notes
 
-When C<$input> maps to multiple files/buffers and C<$output> is a single
-file/buffer the uncompressed input files/buffers will all be stored
-in C<$output> as a single uncompressed stream.
+
+When C<$input> maps to multiple compressed files/buffers and C<$output> is
+a single file/buffer, after uncompression C<$output> will contain a
+concatenation of all the uncompressed data from each of the input
+files/buffers.
+
+
 
 
 
index 2580191..157926d 100644 (file)
@@ -10,7 +10,7 @@ our (@ISA, $VERSION, @EXPORT_OK, %EXPORT_TAGS);
 @ISA    = qw(Exporter );
 
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 
 use constant G_EOF => 0 ;
 use constant G_ERR => -1 ;
index dd8c1fb..6613fa3 100644 (file)
@@ -19,7 +19,7 @@ BEGIN {
     $extra = 1
         if eval { require Test::NoWarnings ;  import Test::NoWarnings; 1 };
 
-    plan tests => 29 + $extra ;
+    plan tests => 33 + $extra ;
 
 
     use_ok('IO::Compress::Base::Common');
@@ -67,6 +67,16 @@ sub My::testParseParameters()
     $got = ParseParameters(1, {'Fred' => [1, 1, 0x1000000, 0]}, Fred => 'abc') ;
     is $got->value('Fred'), "abc", "other" ;
 
+    $got = ParseParameters(1, {'Fred' => [0, 1, Parse_any, undef]}, Fred =>
+undef) ;
+    ok $got->parsed('Fred'), "undef" ;
+    ok ! defined $got->value('Fred'), "undef" ;
+
+    $got = ParseParameters(1, {'Fred' => [0, 1, Parse_string, undef]}, Fred =>
+undef) ;
+    ok $got->parsed('Fred'), "undef" ;
+    is $got->value('Fred'), "", "empty string" ;
+
 }
 
 My::testParseParameters();
index cc27f44..fd928ca 100644 (file)
@@ -1,6 +1,29 @@
 CHANGES
 -------
 
+  2.000_12 3 May 2006
+
+      * Moved the code for creating and parsing the gzip extra field into
+        IO::Compress::Zlib::Extra.pm so that IO::Compress::Zip &
+        IO::Uncompress::Unzip can use it as well. 
+
+      * Added ExtraFieldLocal & ExtraFieldCentral options to IO::Compress::Zip.
+        These allow the creation of user-defined extra fields in the local
+        and central headers, just like the ExtraField option in
+        IO::Compress::Gzip.
+
+      * Moved the zip constants into IO::Compress::Zip::Constants
+
+      * Added exTime option to IO::Compress::Zip. 
+        This allows creation of the extended timestamp extra field.
+
+      * Added Minimal option to IO::Compress::Zip. 
+        This disables the creation of all extended fields.
+
+      * Added TextFlag option to IO::Compress::Zip. 
+
+      * Documented Comment and ZipComment options in IO::Compress::Zip. 
+
   2.000_11 10 April 2006
 
       * Updated Documentation for zip modules.
index 6d323cb..fdeadaa 100644 (file)
@@ -1,9 +1,9 @@
 
                              IO::Compress::Zlib
 
-                             Version 2.000_11
+                             Version 2.000_12
 
-                               10 April 2006
+                                17 May 2006
 
 
        Copyright (c) 2005-2006 Paul Marquess. All rights reserved.
index 454689e..07a84fa 100644 (file)
@@ -9,7 +9,7 @@ use IO::Compress::Base::Common qw(:Status);
 use Compress::Raw::Zlib qw(Z_OK Z_FINISH MAX_WBITS) ;
 our ($VERSION);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 
 sub mkCompObject
 {
index 72f6efc..e253d43 100644 (file)
@@ -7,7 +7,7 @@ use bytes;
 use IO::Compress::Base::Common qw(:Status);
 our ($VERSION);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 
 sub mkCompObject
 {
index df4af0c..9ef3ebc 100644 (file)
@@ -15,7 +15,7 @@ use IO::Compress::Base::Common qw(createSelfTiedObject);
 
 our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, $DeflateError);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 $DeflateError = '';
 
 @ISA    = qw(Exporter IO::Compress::RawDeflate);
@@ -378,9 +378,14 @@ If the C<$output> parameter is any other type, C<undef> will be returned.
 
 =head2 Notes
 
+
+
 When C<$input> maps to multiple files/buffers and C<$output> is a single
-file/buffer the compressed input files/buffers will all be stored
-in C<$output> as a single compressed stream.
+file/buffer the input files/buffers will be stored
+in C<$output> as a concatenated series of compressed data streams.
+
+
+
 
 
 
index 4d4c2d2..ce3a903 100644 (file)
@@ -13,6 +13,7 @@ use IO::Compress::RawDeflate;
 use Compress::Raw::Zlib ;
 use IO::Compress::Base::Common qw(:Status :Parse createSelfTiedObject);
 use IO::Compress::Gzip::Constants;
+use IO::Compress::Zlib::Extra;
 
 BEGIN
 {
@@ -26,7 +27,7 @@ require Exporter ;
 
 our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, $GzipError);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 $GzipError = '' ;
 
 @ISA    = qw(Exporter IO::Compress::RawDeflate);
@@ -74,7 +75,7 @@ sub getExtraParams
             'TextFlag'  => [0, 1, Parse_boolean,   0],
             'HeaderCRC' => [0, 1, Parse_boolean,   0],
             'OS_Code'   => [0, 1, Parse_unsigned,  $Compress::Raw::Zlib::gzip_os_code],
-            'ExtraField'=> [0, 1, Parse_string,    undef],
+            'ExtraField'=> [0, 1, Parse_any,       undef],
             'ExtraFlags'=> [0, 1, Parse_any,       undef],
 
         );
@@ -92,7 +93,7 @@ sub ckParams
     return 1
         if $got->value('Merge') ;
 
-    my $lax = ! $got->value('Strict') ;
+    my $strict = $got->value('Strict') ;
 
 
     {
@@ -108,11 +109,11 @@ sub ckParams
                 
             return $self->saveErrorString(undef, "Null Character found in Name",
                                                 Z_DATA_ERROR)
-                if ! $lax && $name =~ /\x00/ ;
+                if $strict && $name =~ /\x00/ ;
 
             return $self->saveErrorString(undef, "Non ISO 8859-1 Character found in Name",
                                                 Z_DATA_ERROR)
-                if ! $lax && $name =~ /$GZIP_FNAME_INVALID_CHAR_RE/o ;
+                if $strict && $name =~ /$GZIP_FNAME_INVALID_CHAR_RE/o ;
         }
 
         if ($got->parsed('Comment') && defined $got->value('Comment')) {
@@ -120,11 +121,11 @@ sub ckParams
 
             return $self->saveErrorString(undef, "Null Character found in Comment",
                                                 Z_DATA_ERROR)
-                if ! $lax && $comment =~ /\x00/ ;
+                if $strict && $comment =~ /\x00/ ;
 
             return $self->saveErrorString(undef, "Non ISO 8859-1 Character found in Comment",
                                                 Z_DATA_ERROR)
-                if ! $lax && $comment =~ /$GZIP_FCOMMENT_INVALID_CHAR_RE/o;
+                if $strict && $comment =~ /$GZIP_FCOMMENT_INVALID_CHAR_RE/o;
         }
 
         if ($got->parsed('OS_Code') ) {
@@ -145,16 +146,13 @@ sub ckParams
                 if $got->value('Level') == Z_BEST_COMPRESSION ;
         }
 
-        if ($got->parsed('ExtraField')) {
-
-            my $bad = $self->parseExtraField($got, $lax) ;
-            return $self->saveErrorString(undef, $bad, Z_DATA_ERROR)
+        my $data = $got->value('ExtraField') ;
+        if (defined $data) {
+            my $bad = IO::Compress::Zlib::Extra::parseExtraField($data, $strict, 1) ;
+            return $self->saveErrorString(undef, "Error with ExtraField Parameter: $bad", Z_DATA_ERROR)
                 if $bad ;
 
-            my $len = length $got->value('ExtraField') ;
-            return $self->saveErrorString(undef, ExtraFieldError("Too Large"), 
-                                                        Z_DATA_ERROR)
-                if $len > GZIP_FEXTRA_MAX_SIZE;
+            $got->value('ExtraField', $data) ;
         }
     }
 
@@ -265,158 +263,6 @@ sub mkHeader
     return $out ;
 }
 
-sub ExtraFieldError
-{
-    return "Error with ExtraField Parameter: $_[0]" ;
-}
-
-sub validateExtraFieldPair
-{
-    my $pair = shift ;
-    my $lax  = shift ;
-
-    return ExtraFieldError("Not an array ref")
-        unless ref $pair &&  ref $pair eq 'ARRAY';
-
-    return ExtraFieldError("SubField must have two parts")
-        unless @$pair == 2 ;
-
-    return ExtraFieldError("SubField ID is a reference")
-        if ref $pair->[0] ;
-
-    return ExtraFieldError("SubField Data is a reference")
-        if ref $pair->[1] ;
-
-    # ID is exactly two chars   
-    return ExtraFieldError("SubField ID not two chars long")
-        unless length $pair->[0] == GZIP_FEXTRA_SUBFIELD_ID_SIZE ;
-
-    # Check that the 2nd byte of the ID isn't 0    
-    return ExtraFieldError("SubField ID 2nd byte is 0x00")
-        if ! $lax && substr($pair->[0], 1, 1) eq "\x00" ;
-
-    return ExtraFieldError("SubField Data too long")
-        if length $pair->[1] > GZIP_FEXTRA_SUBFIELD_MAX_SIZE ;
-
-
-    return undef ;
-}
-
-sub parseExtra
-{
-    my $data = shift ;
-    my $lax = shift ;
-
-    return undef
-        if $lax ;
-
-    my $XLEN = length $data ;
-
-    return ExtraFieldError("Too Large")
-        if $XLEN > GZIP_FEXTRA_MAX_SIZE;
-
-    my $offset = 0 ;
-    while ($offset < $XLEN) {
-
-        return ExtraFieldError("FEXTRA Body")
-            if $offset + GZIP_FEXTRA_SUBFIELD_HEADER_SIZE  > $XLEN ;
-
-        my $id = substr($data, $offset, GZIP_FEXTRA_SUBFIELD_ID_SIZE);    
-        $offset += GZIP_FEXTRA_SUBFIELD_ID_SIZE;
-
-        my $subLen =  unpack("v", substr($data, $offset,
-                                            GZIP_FEXTRA_SUBFIELD_LEN_SIZE));
-        $offset += GZIP_FEXTRA_SUBFIELD_LEN_SIZE ;
-
-        return ExtraFieldError("FEXTRA Body")
-            if $offset + $subLen > $XLEN ;
-
-        my $bad = validateExtraFieldPair( [$id, 
-                                            substr($data, $offset, $subLen)], $lax );
-        return $bad if $bad ;
-
-        $offset += $subLen ;
-    }
-        
-    return undef ;
-}
-
-sub parseExtraField
-{
-    my $self = shift ;
-    my $got  = shift ;
-    my $lax  = shift ;
-
-    # ExtraField can be any of
-    #
-    #    -ExtraField => $data
-    #    -ExtraField => [$id1, $data1,
-    #                    $id2, $data2]
-    #                     ...
-    #                   ]
-    #    -ExtraField => [ [$id1 => $data1],
-    #                     [$id2 => $data2],
-    #                     ...
-    #                   ]
-    #    -ExtraField => { $id1 => $data1,
-    #                     $id2 => $data2,
-    #                     ...
-    #                   }
-
-    
-    return undef
-        unless $got->parsed('ExtraField') ;
-
-    return parseExtra($got->value('ExtraField'), $lax)
-        unless ref $got->value('ExtraField') ;
-
-    my $data = $got->value('ExtraField');
-    my $out = '' ;
-
-    if (ref $data eq 'ARRAY') {    
-        if (ref $data->[0]) {
-
-            foreach my $pair (@$data) {
-                return ExtraFieldError("Not list of lists")
-                    unless ref $pair eq 'ARRAY' ;
-
-                my $bad = validateExtraFieldPair($pair, $lax) ;
-                return $bad if $bad ;
-
-                $out .= $pair->[0] . pack("v", length $pair->[1]) . 
-                        $pair->[1] ;
-            }   
-        }   
-        else {
-            return ExtraFieldError("Not even number of elements")
-                unless @$data % 2  == 0;
-
-            for (my $ix = 0; $ix <= length(@$data) -1 ; $ix += 2) {
-                my $bad = validateExtraFieldPair([$data->[$ix], $data->[$ix+1]], $lax) ;
-                return $bad if $bad ;
-
-                $out .= $data->[$ix] . pack("v", length $data->[$ix+1]) . 
-                        $data->[$ix+1] ;
-            }   
-        }
-    }   
-    elsif (ref $data eq 'HASH') {    
-        while (my ($id, $info) = each %$data) {
-            my $bad = validateExtraFieldPair([$id, $info], $lax);
-            return $bad if $bad ;
-
-            $out .= $id .  pack("v", length $info) . $info ;
-        }   
-    }   
-    else {
-        return ExtraFieldError("Not a scalar, array ref or hash ref") ;
-    }
-
-    $got->value('ExtraField' => $out);
-
-    return undef;
-}
-
 sub mkFinalTrailer
 {
     return '';
@@ -651,9 +497,14 @@ If the C<$output> parameter is any other type, C<undef> will be returned.
 
 =head2 Notes
 
+
+
 When C<$input> maps to multiple files/buffers and C<$output> is a single
-file/buffer the compressed input files/buffers will all be stored
-in C<$output> as a single compressed stream.
+file/buffer the input files/buffers will be stored
+in C<$output> as a concatenated series of compressed data streams.
+
+
+
 
 
 
index 024c443..9c671b5 100644 (file)
@@ -9,7 +9,7 @@ require Exporter;
 our ($VERSION, @ISA, @EXPORT, %GZIP_OS_Names);
 our ($GZIP_FNAME_INVALID_CHAR_RE, $GZIP_FCOMMENT_INVALID_CHAR_RE);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 
 @ISA = qw(Exporter);
 
index fc195e7..0f917e2 100644 (file)
@@ -16,7 +16,7 @@ require Exporter ;
 
 our ($VERSION, @ISA, @EXPORT_OK, %DEFLATE_CONSTANTS, %EXPORT_TAGS, $RawDeflateError);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 $RawDeflateError = '';
 
 @ISA = qw(Exporter IO::Compress::Base);
@@ -462,9 +462,14 @@ If the C<$output> parameter is any other type, C<undef> will be returned.
 
 =head2 Notes
 
+
+
 When C<$input> maps to multiple files/buffers and C<$output> is a single
-file/buffer the compressed input files/buffers will all be stored
-in C<$output> as a single compressed stream.
+file/buffer the input files/buffers will be stored
+in C<$output> as a concatenated series of compressed data streams.
+
+
+
 
 
 
index 4441809..ea189b0 100644 (file)
@@ -8,14 +8,16 @@ use IO::Compress::Base::Common qw(:Status createSelfTiedObject);
 use IO::Compress::RawDeflate;
 use IO::Compress::Adapter::Deflate;
 use IO::Compress::Adapter::Identity;
+use IO::Compress::Zip::Constants;
+
 
 use Compress::Raw::Zlib qw(crc32) ;
 BEGIN
 {
     eval { require IO::Compress::Adapter::Bzip2; 
-           import IO::Compress::Adapter::Bzip2; 
+           import  IO::Compress::Adapter::Bzip2; 
            require IO::Compress::Bzip2; 
-           import IO::Compress::Bzip2; 
+           import  IO::Compress::Bzip2; 
          } ;
 }
 
@@ -24,7 +26,7 @@ require Exporter ;
 
 our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, $ZipError);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 $ZipError = '';
 
 @ISA = qw(Exporter IO::Compress::RawDeflate);
@@ -37,23 +39,6 @@ push @{ $EXPORT_TAGS{all} }, @{ $EXPORT_TAGS{zip_method} };
 
 Exporter::export_ok_tags('all');
 
-use constant ZIP_CM_STORE                      => 0 ;
-use constant ZIP_CM_DEFLATE                    => 8 ;
-use constant ZIP_CM_BZIP2                      => 12 ;
-
-use constant ZIP_LOCAL_HDR_SIG                 => 0x04034b50;
-use constant ZIP_DATA_HDR_SIG                  => 0x08074b50;
-use constant ZIP_CENTRAL_HDR_SIG               => 0x02014b50;
-use constant ZIP_END_CENTRAL_HDR_SIG           => 0x06054b50;
-
-
-our (%ZIP_CM_MIN_VERSIONS);
-%ZIP_CM_MIN_VERSIONS = (
-            ZIP_CM_STORE()                      => 20,
-            ZIP_CM_DEFLATE()                    => 20,
-            ZIP_CM_BZIP2()                      => 46,
-            );
-
 sub new
 {
     my $class = shift ;
@@ -137,6 +122,8 @@ sub mkHeader
     my $self  = shift;
     my $param = shift ;
     
+    *$self->{ZipData}{StartOffset} = *$self->{ZipData}{Offset} ;
+
     my $filename = '';
     $filename = $param->value('Name') || '';
 
@@ -146,52 +133,89 @@ sub mkHeader
     my $hdr = '';
 
     my $time = _unixToDosTime($param->value('Time'));
-    *$self->{ZipData}{StartOffset} = *$self->{ZipData}{Offset} ;
 
-    my $strm = *$self->{ZipData}{Stream} ? 8 : 0 ;
-    # bzip2 is 12, deflate is 8
+    my $extra = '';
+    my $ctlExtra = '';
+
+    if (! $param->value('Minimal')) {
+        if (defined $param->value('exTime'))
+        {
+            $extra .= mkExtendedTime($param->value('MTime'), 
+                                    $param->value('ATime'), 
+                                    $param->value('CTime'));
+
+            $ctlExtra .= mkExtendedTime($param->value('MTime'));
+        }
+
+    #    if ( $param->value('UID'))
+    #    {
+    #        $extra    .= mkUnixExtra( $param->value('UID'), $param->value('GID'));
+    #        $ctlExtra .= mkUnixExtra();
+    #    }
+
+        $extra .= $param->value('ExtraFieldLocal') 
+            if defined $param->value('ExtraFieldLocal');
+
+        $ctlExtra .= $param->value('ExtraFieldCentral') 
+            if defined $param->value('ExtraFieldCentral');
+    }
+
+    my $extAttr = 0;
+    $extAttr = $param->value('Mode') << 16
+        if defined $param->value('Mode') ;
+
+    my $gpFlag = 0 ;    
+    $gpFlag |= ZIP_GP_FLAG_STREAMING_MASK
+        if *$self->{ZipData}{Stream} ;
+
     my $method = *$self->{ZipData}{Method} ;
 
     # deflate is 20
     # bzip2 is 46
-    my $extract = $param->value('OS_Code') << 8 +
+    my $madeBy = ($param->value('OS_Code') << 8) +
                     $ZIP_CM_MIN_VERSIONS{$method};
+    my $extract = $ZIP_CM_MIN_VERSIONS{$method};
+
+    my $ifa = 0;
+    $ifa |= ZIP_IFA_TEXT_MASK
+        if $param->value('TextFlag');
 
     $hdr .= pack "V", ZIP_LOCAL_HDR_SIG ; # signature
     $hdr .= pack 'v', $extract   ; # extract Version & OS
-    $hdr .= pack 'v', $strm      ; # general purpose flag (set streaming mode)
+    $hdr .= pack 'v', $gpFlag    ; # general purpose flag (set streaming mode)
     $hdr .= pack 'v', $method    ; # compression method (deflate)
     $hdr .= pack 'V', $time      ; # last mod date/time
     $hdr .= pack 'V', 0          ; # crc32               - 0 when streaming
     $hdr .= pack 'V', 0          ; # compressed length   - 0 when streaming
     $hdr .= pack 'V', 0          ; # uncompressed length - 0 when streaming
     $hdr .= pack 'v', length $filename ; # filename length
-    $hdr .= pack 'v', 0          ; # extra length
+    $hdr .= pack 'v', length $extra ; # extra length
     
     $hdr .= $filename ;
+    $hdr .= $extra ;
 
 
     my $ctl = '';
 
     $ctl .= pack "V", ZIP_CENTRAL_HDR_SIG ; # signature
-    $ctl .= pack 'v', $extract   ; # version made by
+    $ctl .= pack 'v', $madeBy    ; # version made by
     $ctl .= pack 'v', $extract   ; # extract Version
-    $ctl .= pack 'v', $strm      ; # general purpose flag (streaming mode)
+    $ctl .= pack 'v', $gpFlag    ; # general purpose flag (streaming mode)
     $ctl .= pack 'v', $method    ; # compression method (deflate)
     $ctl .= pack 'V', $time      ; # last mod date/time
     $ctl .= pack 'V', 0          ; # crc32
     $ctl .= pack 'V', 0          ; # compressed length
     $ctl .= pack 'V', 0          ; # uncompressed length
     $ctl .= pack 'v', length $filename ; # filename length
-    $ctl .= pack 'v', 0          ; # extra length
+    $ctl .= pack 'v', length $ctlExtra ; # extra length
     $ctl .= pack 'v', length $comment ;  # file comment length
     $ctl .= pack 'v', 0          ; # disk number start 
-    $ctl .= pack 'v', 0          ; # internal file attributes
-    $ctl .= pack 'V', 0          ; # external file attributes
+    $ctl .= pack 'v', $ifa       ; # internal file attributes
+    $ctl .= pack 'V', $extAttr   ; # external file attributes
     $ctl .= pack 'V', *$self->{ZipData}{Offset}  ; # offset to local header
     
     $ctl .= $filename ;
-    #$ctl .= $extra ;
+    $ctl .= $ctlExtra ;
     $ctl .= $comment ;
 
     *$self->{ZipData}{Offset} += length $hdr ;
@@ -234,9 +258,6 @@ sub mkTrailer
 
     my $ctl = *$self->{ZipData}{CentralHeader} ;
     substr($ctl, 16, 12) = $data ;
-    #substr($ctl, 16, 4) = pack "V", $crc32 ;             # CRC32
-    #substr($ctl, 20, 4) = pack "V", $compressedBytes   ; # Compressed Size
-    #substr($ctl, 24, 4) = pack "V", $uncompressedBytes ; # Uncompressed Size
 
     *$self->{ZipData}{Offset} += length($hdr) + $compressedBytes;
     push @{ *$self->{ZipData}{CentralDir} }, $ctl ;
@@ -280,11 +301,21 @@ sub ckParams
         $got->value('Time' => time) ;
     }
 
+    if (! $got->parsed('exTime') ) {
+        my $timeRef = $got->value('exTime');
+        if ( defined $timeRef) {
+            return $self->saveErrorString(undef, "exTime not a 3-element array ref")   
+                if ref $timeRef ne 'ARRAY' || @$timeRef != 3;
+        }
+
+        $got->value("MTime", $timeRef->[1]);
+        $got->value("ATime", $timeRef->[0]);
+        $got->value("CTime", $timeRef->[2]);
+    }
+
     *$self->{ZipData}{Stream} = $got->value('Stream');
-    #*$self->{ZipData}{Store} = $got->value('Store');
 
     my $method = $got->value('Method');
-    #if ($method != 0 && $method != 8 && $method != 12) {
     return $self->saveErrorString(undef, "Unknown Method '$method'")   
         if ! defined $ZIP_CM_MIN_VERSIONS{$method};
 
@@ -296,6 +327,18 @@ sub ckParams
 
     *$self->{ZipData}{ZipComment} = $got->value('ZipComment') ;
 
+    for my $name (qw( ExtraFieldLocal ExtraFieldCentral ))
+    {
+        my $data = $got->value($name) ;
+        if (defined $data) {
+            my $bad = IO::Compress::Zlib::Extra::parseExtraField($data, 1, 0) ;
+            return $self->saveErrorString(undef, "Error with $name Parameter: $bad")
+                if $bad ;
+
+            $got->value($name, $data) ;
+        }
+    }
+
     return undef
         if defined $IO::Compress::Bzip2::VERSION
             and ! IO::Compress::Bzip2::ckParams($self, $got);
@@ -331,15 +374,17 @@ sub getExtraParams
             'Method'    => [0, 1, Parse_unsigned,  ZIP_CM_DEFLATE],
             
 #            # Zip header fields
-#           'Minimal'   => [0, 1, Parse_boolean,   0],
+            'Minimal'   => [0, 1, Parse_boolean,   0],
             'Comment'   => [0, 1, Parse_any,       ''],
             'ZipComment'=> [0, 1, Parse_any,       ''],
             'Name'      => [0, 1, Parse_any,       ''],
             'Time'      => [0, 1, Parse_any,       undef],
+            'exTime'    => [0, 1, Parse_any,       undef],
             'OS_Code'   => [0, 1, Parse_unsigned,  $Compress::Raw::Zlib::gzip_os_code],
             
-#           'TextFlag'  => [0, 1, Parse_boolean,   0],
-#           'ExtraField'=> [0, 1, Parse_string,    ''],
+           'TextFlag'  => [0, 1, Parse_boolean,   0],
+           'ExtraFieldLocal'  => [0, 1, Parse_any,    undef],
+           'ExtraFieldCentral'=> [0, 1, Parse_any,    undef],
 
             @Bzip2,
         );
@@ -357,17 +402,66 @@ sub getFileInfo
     my $params = shift;
     my $filename = shift ;
 
-    my $defaultTime = (stat($filename))[9] ;
+    my ($mode, $uid, $gid, $atime, $mtime, $ctime) 
+                = (stat($filename))[2, 4,5, 8,9,10] ;
 
     $params->value('Name' => $filename)
         if ! $params->parsed('Name') ;
 
-    $params->value('Time' => $defaultTime) 
+    $params->value('Time' => $mtime) 
         if ! $params->parsed('Time') ;
     
+    if ( ! $params->parsed('exTime'))
+    {
+        $params->value('MTime' => $mtime) ;
+        $params->value('ATime' => $atime) ;
+        $params->value('CTime' => $ctime) ;
+    }
+
+    $params->value('Mode' => $mode) ;
+
+    $params->value('UID' => $uid) ;
+    $params->value('GID' => $gid) ;
     
 }
 
+sub mkExtendedTime
+{
+    # order expected is m, a, c
+
+    my $times = '';
+    my $bit = 1 ;
+    my $flags = 0;
+
+    for my $time (@_)
+    {
+        if (defined $time)
+        {
+            $flags |= $bit;
+            $times .= pack("V", $time);
+        }
+
+        $bit <<= 1 ;
+    }
+
+    #return "UT" . pack("v C", length($times) + 1, $flags) . $times;
+    return IO::Compress::Zlib::Extra::mkSubField(ZIP_EXTRA_ID_EXT_TIMESTAMP,
+                                                 pack("C", $flags) .  $times);
+}
+
+sub mkUnixExtra
+{
+    my $ids = '';
+    for my $id (@_)
+    {
+        $ids .= pack("v", $id);
+    }
+
+    #return "Ux" . pack("v", length $ids) . $ids;
+    return IO::Compress::Zlib::Extra::mkSubField(ZIP_EXTRA_ID_INFO_ZIP_UNIX, $ids);
+}
+
+
 # from Archive::Zip
 sub _unixToDosTime    # Archive::Zip::Member
 {
@@ -559,10 +653,10 @@ If the C<$input> parameter is any other type, C<undef> will be returned.
 
 
 In addition, if C<$input> is a simple filename, the default values for
-the C<Name> and C<Time> options will be sourced from that file.
+the C<Name>, C<Time> and C<exTime> options will be sourced from that file.
 
 If you do not want to use these defaults they can be overridden by
-explicitly setting the C<Name> and C<Time> options or by setting the
+explicitly setting the C<Name>, C<Time> and C<exTime> options or by setting the
 C<Minimal> parameter.
 
 
@@ -616,9 +710,14 @@ If the C<$output> parameter is any other type, C<undef> will be returned.
 
 =head2 Notes
 
+
+
 When C<$input> maps to multiple files/buffers and C<$output> is a single
-file/buffer the compressed input files/buffers will all be stored
-in C<$output> as a single compressed stream.
+file/buffer the input files/buffers will each be stored
+in C<$output> as a distinct entry.
+
+
+
 
 
 
@@ -823,6 +922,34 @@ Sets the last modified time field in the zip header to $number.
 This field defaults to the time the C<IO::Compress::Zip> object was created
 if this option is not specified.
 
+=item -exTime =E<gt> [$atime, $mtime, $ctime]
+
+This option expects an array reference with exactly three elements:
+C<$atime>, C<mtime> and C<$ctime>. These correspond to the last access
+time, last modification time and creation time respectively.
+
+It uses these values to set the extended timestamp field in the local zip
+header to the three values, $atime, $mtime, $ctime and sets the extended
+timestamp field in the central zip header to C<$mtime>.
+
+If the C<Minimal> option is set to true, this option will be ignored.
+
+By default no extended time field is created.
+
+=item -Comment =E<gt> $comment
+
+Stores the contents of C<$comment> in the Central File Header of
+the zip file.
+
+By default, no comment field is written to the zip file.
+
+=item -ZipComment =E<gt> $comment
+
+Stores the contents of C<$comment> in the End of Central Directory record
+of the zip file.
+
+By default, no comment field is written to the zip file.
+
 =item Method =E<gt> $method
 
 Controls which compression method is used. At present three compression
@@ -844,13 +971,73 @@ content when C<IO::Compress::Bzip2> is not available.
 
 The default method is ZIP_CM_DEFLATE.
 
-=item -Stream =E<gt> 0|1
+=item Stream =E<gt> 0|1
 
 This option controls whether the zip file/buffer output is created in
 streaming mode.
 
+Note that when outputting to a file with streaming mode disabled (C<Stream>
+is 0), the output file must be seekable.
+
 The default is 1.
 
+=item -TextFlag =E<gt> 0|1
+
+This parameter controls the setting of a bit in the zip central header. It
+is used to signal that the data stored in the zip file/buffer is probably
+text.
+
+The default is 0. 
+
+=item ExtraFieldLocal =E<gt> $data
+=item ExtraFieldCentral =E<gt> $data
+
+These options allows additional metadata to be stored in the local and
+central headers in the zip file/buffer.
+
+An extra field consists of zero or more subfields. Each subfield consists
+of a two byte header followed by the subfield data.
+
+The list of subfields can be supplied in any of the following formats
+
+    ExtraFieldLocal => [$id1, $data1,
+                        $id2, $data2,
+                         ...
+                       ]
+
+    ExtraFieldLocal => [ [$id1 => $data1],
+                         [$id2 => $data2],
+                         ...
+                       ]
+
+    ExtraFieldLocal => { $id1 => $data1,
+                         $id2 => $data2,
+                         ...
+                       }
+
+Where C<$id1>, C<$id2> are two byte subfield ID's. 
+
+If you use the hash syntax, you have no control over the order in which
+the ExtraSubFields are stored, plus you cannot have SubFields with
+duplicate ID.
+
+Alternatively the list of subfields can by supplied as a scalar, thus
+
+    ExtraField => $rawdata
+
+
+
+If the C<Minimal> option is set to true, this option will be ignored.
+
+The maximum size of an extra field 65535 bytes.
+
+=item Minimal =E<gt> 1|0
+
+If specified, this option will disable the creation of all extended fields
+in the zip local and central headers.
+
+This parameter defaults to 0.
+
 =item BlockSize100K =E<gt> number
 
 Specify the number of 100K blocks bzip2 uses during compression. 
index 9761b82..952a3b3 100644 (file)
@@ -5,130 +5,64 @@ use warnings;
 
 require Exporter;
 
-our ($VERSION, @ISA, @EXPORT, %GZIP_OS_Names);
+our ($VERSION, @ISA, @EXPORT, %ZIP_CM_MIN_VERSIONS);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 
 @ISA = qw(Exporter);
 
 @EXPORT= qw(
 
-    ZIP_ID_SIZE
-    GZIP_ID1
-    GZIP_ID2
-
-    GZIP_FLG_DEFAULT
-    GZIP_FLG_FTEXT
-    GZIP_FLG_FHCRC
-    GZIP_FLG_FEXTRA
-    GZIP_FLG_FNAME
-    GZIP_FLG_FCOMMENT
-    GZIP_FLG_RESERVED
-
     ZIP_CM_STORE
-    ZIP_CM_DEFLATED
+    ZIP_CM_DEFLATE
     ZIP_CM_BZIP2
+    
+    ZIP_LOCAL_HDR_SIG
+    ZIP_DATA_HDR_SIG
+    ZIP_CENTRAL_HDR_SIG
+    ZIP_END_CENTRAL_HDR_SIG
 
-    GZIP_MIN_HEADER_SIZE
-    GZIP_TRAILER_SIZE
-
-    GZIP_MTIME_DEFAULT
-    GZIP_FEXTRA_DEFAULT
-    GZIP_FEXTRA_HEADER_SIZE
-    GZIP_FEXTRA_MAX_SIZE
-    GZIP_FEXTRA_SUBFIELD_HEADER_SIZE
-    GZIP_FEXTRA_SUBFIELD_ID_SIZE
-    GZIP_FEXTRA_SUBFIELD_LEN_SIZE
-    GZIP_FEXTRA_SUBFIELD_MAX_SIZE
-
-    GZIP_FNAME_INVALID_CHAR_RE
-    GZIP_FCOMMENT_INVALID_CHAR_RE
-
-    GZIP_FHCRC_SIZE
+    ZIP_GP_FLAG_STREAMING_MASK
 
-    GZIP_ISIZE_MAX
-    GZIP_ISIZE_MOD_VALUE
+    ZIP_EXTRA_ID_EXT_TIMESTAMP
+    ZIP_EXTRA_ID_INFO_ZIP_UNIX
 
+    ZIP_IFA_TEXT_MASK
 
-    GZIP_NULL_BYTE
-
-    GZIP_OS_DEFAULT
-
-    %GZIP_OS_Names
-
-    GZIP_MINIMUM_HEADER
+    %ZIP_CM_MIN_VERSIONS
 
     );
 
+# Compression types supported
+use constant ZIP_CM_STORE                      => 0 ;
+use constant ZIP_CM_DEFLATE                    => 8 ;
+use constant ZIP_CM_BZIP2                      => 12 ;
 
-# Constants for the Zip Local Header
-
-use constant ZIP_ID_SIZE                        => 4 ;
-use constant ZIP_LOCAL_ID                       => 0x02014B50;
-use constant ZIP_LOCAL_ID1                      => 0x04;
-use constant ZIP_LOCAL_ID2                      => 0x03;
-use constant ZIP_LOCAL_ID3                      => 0x4B;
-use constant ZIP_LOCAL_ID4                      => 0x50;
-
-use constant ZIP_MIN_HEADER_SIZE                => 30 ;
-use constant ZIP_TRAILER_SIZE                   => 0 ;
+# General Purpose Flag
+use constant ZIP_GP_FLAG_ENCRYPTED_MASK        => 1 ;
+use constant ZIP_GP_FLAG_STREAMING_MASK        => 8 ;
 
+# Internal File Attributes
+use constant ZIP_IFA_TEXT_MASK                 => 1;
 
-use constant GZIP_FLG_DEFAULT                   => 0x00 ;
-use constant GZIP_FLG_FTEXT                     => 0x01 ;
-use constant GZIP_FLG_FHCRC                     => 0x02 ; # called CONTINUATION in gzip
-use constant GZIP_FLG_FEXTRA                    => 0x04 ;
-use constant GZIP_FLG_FNAME                     => 0x08 ;
-use constant GZIP_FLG_FCOMMENT                  => 0x10 ;
-#use constant GZIP_FLG_ENCRYPTED                => 0x20 ; # documented in gzip sources
-use constant GZIP_FLG_RESERVED                  => (0x20 | 0x40 | 0x80) ;
+# Signatures for each of the headers
+use constant ZIP_LOCAL_HDR_SIG                 => 0x04034b50;
+use constant ZIP_DATA_HDR_SIG                  => 0x08074b50;
+use constant ZIP_CENTRAL_HDR_SIG               => 0x02014b50;
+use constant ZIP_END_CENTRAL_HDR_SIG           => 0x06054b50;
 
-use constant GZIP_MTIME_DEFAULT                 => 0x00 ;
-use constant GZIP_FEXTRA_DEFAULT                => 0x00 ;
-use constant GZIP_FEXTRA_HEADER_SIZE            => 2 ;
-use constant GZIP_FEXTRA_MAX_SIZE               => 0xFFFF ;
-use constant GZIP_FEXTRA_SUBFIELD_HEADER_SIZE   => 4 ;
-use constant GZIP_FEXTRA_SUBFIELD_ID_SIZE       => 2 ;
-use constant GZIP_FEXTRA_SUBFIELD_LEN_SIZE      => 2 ;
-use constant GZIP_FEXTRA_SUBFIELD_MAX_SIZE      => 0xFFFF ;
+# Extra Field ID's
+use constant ZIP_EXTRA_ID_EXT_TIMESTAMP        => "UT";
+use constant ZIP_EXTRA_ID_INFO_ZIP_UNIX        => "Ux";
 
-use constant GZIP_FNAME_INVALID_CHAR_RE         => qr/[\x00-\x1F\x7F-\x9F]/;
-use constant GZIP_FCOMMENT_INVALID_CHAR_RE      => qr/[\x00-\x09\x11-\x1F\x7F-\x9F]/;
+%ZIP_CM_MIN_VERSIONS = (
+            ZIP_CM_STORE()                      => 20,
+            ZIP_CM_DEFLATE()                    => 20,
+            ZIP_CM_BZIP2()                      => 46,
+            );
 
-use constant GZIP_FHCRC_SIZE                    => 2 ; # aka CONTINUATION in gzip
 
-use constant ZIP_CM_STORE                      => 0 ;
-use constant ZIP_CM_DEFLATE                    => 8 ;
-use constant ZIP_CM_BZIP2                      => 12 ;
+1;
 
-use constant GZIP_NULL_BYTE                     => "\x00";
-use constant GZIP_ISIZE_MAX                     => 0xFFFFFFFF ;
-use constant GZIP_ISIZE_MOD_VALUE               => GZIP_ISIZE_MAX + 1 ;
-
-# OS Names sourced from http://www.gzip.org/format.txt
-
-use constant GZIP_OS_DEFAULT=> 0xFF ;
-%ZIP_OS_Names = (
-    0               => 'MS-DOS',
-    1               => 'Amiga',
-    2               => 'VMS',
-    3               => 'Unix',
-    4               => 'VM/CMS',
-    5               => 'Atari TOS',
-    6               => 'HPFS (OS/2, NT)',
-    7               => 'Macintosh',
-    8               => 'Z-System',
-    9               => 'CP/M',
-    10              => 'TOPS-20',
-    11              => 'NTFS (NT)',
-    12              => 'SMS QDOS',
-    13              => 'Acorn RISCOS',
-    14              => 'VFAT file system (Win95, NT)',
-    15              => 'MVS',
-    16              => 'BeOS',
-    17              => 'Tandem/NSK',
-    18              => 'THEOS',
-    GZIP_OS_DEFAULT => 'Unknown',
-    ) ;
+__END__
 
-1;
index a06b6fb..98c9955 100644 (file)
@@ -9,7 +9,7 @@ require Exporter;
 
 our ($VERSION, @ISA, @EXPORT);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 
 @ISA = qw(Exporter);
 
diff --git a/ext/Compress/IO/Zlib/lib/IO/Compress/Zlib/Extra.pm b/ext/Compress/IO/Zlib/lib/IO/Compress/Zlib/Extra.pm
new file mode 100644 (file)
index 0000000..68ef02e
--- /dev/null
@@ -0,0 +1,203 @@
+package IO::Compress::Zlib::Extra;
+
+require 5.004 ;
+
+use strict ;
+use warnings;
+use bytes;
+
+our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS);
+
+$VERSION = '2.000_12';
+
+use IO::Compress::Gzip::Constants;
+
+sub ExtraFieldError
+{
+    return $_[0];
+    return "Error with ExtraField Parameter: $_[0]" ;
+}
+
+sub validateExtraFieldPair
+{
+    my $pair = shift ;
+    my $strict = shift;
+    my $gzipMode = shift ;
+
+    return ExtraFieldError("Not an array ref")
+        unless ref $pair &&  ref $pair eq 'ARRAY';
+
+    return ExtraFieldError("SubField must have two parts")
+        unless @$pair == 2 ;
+
+    return ExtraFieldError("SubField ID is a reference")
+        if ref $pair->[0] ;
+
+    return ExtraFieldError("SubField Data is a reference")
+        if ref $pair->[1] ;
+
+    # ID is exactly two chars   
+    return ExtraFieldError("SubField ID not two chars long")
+        unless length $pair->[0] == GZIP_FEXTRA_SUBFIELD_ID_SIZE ;
+
+    # Check that the 2nd byte of the ID isn't 0    
+    return ExtraFieldError("SubField ID 2nd byte is 0x00")
+        if $strict && $gzipMode && substr($pair->[0], 1, 1) eq "\x00" ;
+
+    return ExtraFieldError("SubField Data too long")
+        if length $pair->[1] > GZIP_FEXTRA_SUBFIELD_MAX_SIZE ;
+
+
+    return undef ;
+}
+
+sub parseRawExtra
+{
+    my $data     = shift ;
+    my $extraRef = shift;
+    my $strict   = shift;
+    my $gzipMode = shift ;
+
+    #my $lax = shift ;
+
+    #return undef
+    #    if $lax ;
+
+    my $XLEN = length $data ;
+
+    return ExtraFieldError("Too Large")
+        if $XLEN > GZIP_FEXTRA_MAX_SIZE;
+
+    my $offset = 0 ;
+    while ($offset < $XLEN) {
+
+        return ExtraFieldError("Truncated in FEXTRA Body Section")
+            if $offset + GZIP_FEXTRA_SUBFIELD_HEADER_SIZE  > $XLEN ;
+
+        my $id = substr($data, $offset, GZIP_FEXTRA_SUBFIELD_ID_SIZE);    
+        $offset += GZIP_FEXTRA_SUBFIELD_ID_SIZE;
+
+        my $subLen =  unpack("v", substr($data, $offset,
+                                            GZIP_FEXTRA_SUBFIELD_LEN_SIZE));
+        $offset += GZIP_FEXTRA_SUBFIELD_LEN_SIZE ;
+
+        return ExtraFieldError("Truncated in FEXTRA Body Section")
+            if $offset + $subLen > $XLEN ;
+
+        my $bad = validateExtraFieldPair( [$id, 
+                                           substr($data, $offset, $subLen)], 
+                                           $strict, $gzipMode );
+        return $bad if $bad ;
+        push @$extraRef, [$id => substr($data, $offset, $subLen)]
+            if defined $extraRef;;
+
+        $offset += $subLen ;
+    }
+
+        
+    return undef ;
+}
+
+
+sub mkSubField
+{
+    my $id = shift ;
+    my $data = shift ;
+
+    return $id . pack("v", length $data) . $data ;
+}
+
+sub parseExtraField
+{
+    my $dataRef  = $_[0];
+    my $strict   = $_[1];
+    my $gzipMode = $_[2];
+    #my $lax     = @_ == 2 ? $_[1] : 1;
+
+
+    # ExtraField can be any of
+    #
+    #    -ExtraField => $data
+    #
+    #    -ExtraField => [$id1, $data1,
+    #                    $id2, $data2]
+    #                     ...
+    #                   ]
+    #
+    #    -ExtraField => [ [$id1 => $data1],
+    #                     [$id2 => $data2],
+    #                     ...
+    #                   ]
+    #
+    #    -ExtraField => { $id1 => $data1,
+    #                     $id2 => $data2,
+    #                     ...
+    #                   }
+    
+    if ( ! ref $dataRef ) {
+
+        return undef
+            if ! $strict;
+
+        return parseRawExtra($dataRef, undef, 1, $gzipMode);
+    }
+
+    #my $data = $$dataRef;
+    my $data = $dataRef;
+    my $out = '' ;
+
+    if (ref $data eq 'ARRAY') {    
+        if (ref $data->[0]) {
+
+            foreach my $pair (@$data) {
+                return ExtraFieldError("Not list of lists")
+                    unless ref $pair eq 'ARRAY' ;
+
+                my $bad = validateExtraFieldPair($pair, $strict, $gzipMode) ;
+                return $bad if $bad ;
+
+                $out .= mkSubField(@$pair);
+                #$out .= $pair->[0] . pack("v", length $pair->[1]) . 
+                #        $pair->[1] ;
+            }   
+        }   
+        else {
+            return ExtraFieldError("Not even number of elements")
+                unless @$data % 2  == 0;
+
+            for (my $ix = 0; $ix <= length(@$data) -1 ; $ix += 2) {
+                my $bad = validateExtraFieldPair([$data->[$ix],
+                                                  $data->[$ix+1]], 
+                                                 $strict, $gzipMode) ;
+                return $bad if $bad ;
+
+                $out .= mkSubField($data->[$ix], $data->[$ix+1]);
+                #$out .= $data->[$ix] . pack("v", length $data->[$ix+1]) . 
+                #        $data->[$ix+1] ;
+            }   
+        }
+    }   
+    elsif (ref $data eq 'HASH') {    
+        while (my ($id, $info) = each %$data) {
+            my $bad = validateExtraFieldPair([$id, $info], $strict, $gzipMode);
+            return $bad if $bad ;
+
+            $out .= mkSubField($id, $info);
+            #$out .= $id .  pack("v", length $info) . $info ;
+        }   
+    }   
+    else {
+        return ExtraFieldError("Not a scalar, array ref or hash ref") ;
+    }
+
+    return ExtraFieldError("Too Large")
+        if length $out > GZIP_FEXTRA_MAX_SIZE;
+
+    $_[0] = $out ;
+
+    return undef;
+}
+
+1;
+
+__END__
index 288bf58..b91f432 100644 (file)
@@ -8,7 +8,7 @@ use IO::Compress::Base::Common qw(:Status);
 
 our ($VERSION);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 
 use Compress::Raw::Zlib ();
 
index 20f4e70..fa8242d 100644 (file)
@@ -8,7 +8,7 @@ use IO::Compress::Base::Common qw(:Status);
 use Compress::Raw::Zlib qw(Z_OK Z_DATA_ERROR Z_STREAM_END Z_FINISH MAX_WBITS);
 
 our ($VERSION);
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 
 
 
index 99dcd33..b2e8a98 100644 (file)
@@ -21,7 +21,7 @@ require Exporter ;
 
 our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, $AnyInflateError);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 $AnyInflateError = '';
 
 @ISA = qw( Exporter IO::Uncompress::Base );
@@ -344,9 +344,13 @@ If the C<$output> parameter is any other type, C<undef> will be returned.
 
 =head2 Notes
 
-When C<$input> maps to multiple files/buffers and C<$output> is a single
-file/buffer the uncompressed input files/buffers will all be stored
-in C<$output> as a single uncompressed stream.
+
+When C<$input> maps to multiple compressed files/buffers and C<$output> is
+a single file/buffer, after uncompression C<$output> will contain a
+concatenation of all the uncompressed data from each of the input
+files/buffers.
+
+
 
 
 
index fbb3af8..d665103 100644 (file)
@@ -14,6 +14,7 @@ use IO::Uncompress::RawInflate ;
 use Compress::Raw::Zlib qw( crc32 ) ;
 use IO::Compress::Base::Common qw(:Status createSelfTiedObject);
 use IO::Compress::Gzip::Constants;
+use IO::Compress::Zlib::Extra;
 
 require Exporter ;
 
@@ -27,7 +28,7 @@ Exporter::export_ok_tags('all');
 
 $GunzipError = '';
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 
 sub new
 {
@@ -179,28 +180,10 @@ sub _readGzipHeader($)
         $keep .= $buffer . $EXTRA ;
 
         if ($XLEN && *$self->{'ParseExtra'}) {
-            my $offset = 0 ;
-            while ($offset < $XLEN) {
-
-                return $self->TruncatedHeader("FEXTRA Body")
-                    if $offset + GZIP_FEXTRA_SUBFIELD_HEADER_SIZE > $XLEN ;
-
-                my $id = substr($EXTRA, $offset, GZIP_FEXTRA_SUBFIELD_ID_SIZE);
-                $offset += GZIP_FEXTRA_SUBFIELD_ID_SIZE ;
-
-                return $self->HeaderError("SubField ID 2nd byte is 0x00")
-                    if *$self->{Strict} && substr($id, 1, 1) eq "\x00" ;
-
-                my ($subLen) = unpack("v", substr($EXTRA, $offset, 
-                                        GZIP_FEXTRA_SUBFIELD_LEN_SIZE)) ;
-                $offset += GZIP_FEXTRA_SUBFIELD_LEN_SIZE ;
-
-                return $self->TruncatedHeader("FEXTRA Body")
-                    if $offset + $subLen > $XLEN ;
-
-                push @EXTRA, [$id => substr($EXTRA, $offset, $subLen)];
-                $offset += $subLen ;
-            }
+            my $bad = IO::Compress::Zlib::Extra::parseRawExtra($EXTRA,
+                                                \@EXTRA, 1, 1);
+            return $self->HeaderError($bad)
+                if defined $bad;
         }
     }
 
@@ -504,9 +487,13 @@ If the C<$output> parameter is any other type, C<undef> will be returned.
 
 =head2 Notes
 
-When C<$input> maps to multiple files/buffers and C<$output> is a single
-file/buffer the uncompressed input files/buffers will all be stored
-in C<$output> as a single uncompressed stream.
+
+When C<$input> maps to multiple compressed files/buffers and C<$output> is
+a single file/buffer, after uncompression C<$output> will contain a
+concatenation of all the uncompressed data from each of the input
+files/buffers.
+
+
 
 
 
index d3efef6..bf9eadb 100644 (file)
@@ -13,7 +13,7 @@ use IO::Uncompress::RawInflate ;
 require Exporter ;
 our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, $InflateError);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 $InflateError = '';
 
 @ISA    = qw( Exporter IO::Uncompress::RawInflate );
@@ -386,9 +386,13 @@ If the C<$output> parameter is any other type, C<undef> will be returned.
 
 =head2 Notes
 
-When C<$input> maps to multiple files/buffers and C<$output> is a single
-file/buffer the uncompressed input files/buffers will all be stored
-in C<$output> as a single uncompressed stream.
+
+When C<$input> maps to multiple compressed files/buffers and C<$output> is
+a single file/buffer, after uncompression C<$output> will contain a
+concatenation of all the uncompressed data from each of the input
+files/buffers.
+
+
 
 
 
index 5977c9b..1ed435b 100644 (file)
@@ -17,7 +17,7 @@ use IO::Uncompress::Adapter::Inflate ;
 require Exporter ;
 our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, %DEFLATE_CONSTANTS, $RawInflateError);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 $RawInflateError = '';
 
 @ISA    = qw( Exporter IO::Uncompress::Base );
@@ -539,9 +539,13 @@ If the C<$output> parameter is any other type, C<undef> will be returned.
 
 =head2 Notes
 
-When C<$input> maps to multiple files/buffers and C<$output> is a single
-file/buffer the uncompressed input files/buffers will all be stored
-in C<$output> as a single uncompressed stream.
+
+When C<$input> maps to multiple compressed files/buffers and C<$output> is
+a single file/buffer, after uncompression C<$output> will contain a
+concatenation of all the uncompressed data from each of the input
+files/buffers.
+
+
 
 
 
index 4c9d882..177906a 100644 (file)
@@ -11,12 +11,15 @@ use bytes;
 use IO::Uncompress::RawInflate ;
 use IO::Compress::Base::Common qw(:Status createSelfTiedObject);
 use IO::Uncompress::Adapter::Identity;
+use IO::Compress::Zlib::Extra;
+use IO::Compress::Zip::Constants;
 
 use Compress::Raw::Zlib qw(crc32) ;
+
 BEGIN
 {
-    eval { require IO::Uncompress::Adapter::Bunzip2  ;
-           import IO::Uncompress::Adapter::Bunzip2 } ;
+    eval { require IO::Uncompress::Adapter::Bunzip2 ;
+           import  IO::Uncompress::Adapter::Bunzip2 } ;
 }
 
 
@@ -24,7 +27,7 @@ require Exporter ;
 
 our ($VERSION, @ISA, @EXPORT_OK, %EXPORT_TAGS, $UnzipError);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 $UnzipError = '';
 
 @ISA    = qw(Exporter IO::Uncompress::RawInflate);
@@ -33,7 +36,6 @@ $UnzipError = '';
 push @{ $EXPORT_TAGS{all} }, @EXPORT_OK ;
 Exporter::export_ok_tags('all');
 
-
 sub new
 {
     my $class = shift ;
@@ -170,7 +172,7 @@ sub chkTrailer
     if (*$self->{ZipData}{Streaming}) {
         ($sig, $CRC32, $cSize, $uSize) = unpack("V V V V", $trailer) ;
         return $self->TrailerError("Data Descriptor signature, got $sig")
-            if $sig != 0x08074b50;
+            if $sig != ZIP_DATA_HDR_SIG;
     }
     else {
         ($CRC32, $cSize, $uSize) = 
@@ -217,7 +219,7 @@ sub chkTrailer
 
         my $sig = unpack("V", $magic) ;
 
-        if ($sig == 0x02014b50)
+        if ($sig == ZIP_CENTRAL_HDR_SIG)
         {
             if ($self->skipCentralDirectory($magic) != STATUS_OK ) {
                 if (*$self->{Strict}) {
@@ -229,7 +231,7 @@ sub chkTrailer
                 }
             }
         }
-        elsif ($sig == 0x06054b50)
+        elsif ($sig == ZIP_END_CENTRAL_HDR_SIG)
         {
             if ($self->skipEndCentralDirectory($magic) != STATUS_OK) {
                 if (*$self->{Strict}) {
@@ -244,7 +246,7 @@ sub chkTrailer
             return STATUS_OK ;
             last;
         }
-        elsif ($sig == 0x04034b50)
+        elsif ($sig == ZIP_LOCAL_HDR_SIG)
         {
             $self->pushBack($magic)  ;
             return STATUS_OK ;
@@ -358,7 +360,7 @@ sub _isZipMagic
     my $buffer = shift ;
     return 0 if length $buffer < 4 ;
     my $sig = unpack("V", $buffer) ;
-    return $sig == 0x04034b50 ;
+    return $sig == ZIP_LOCAL_HDR_SIG ;
 }
 
 
@@ -409,7 +411,8 @@ sub _readZipHeader($)
 
     my $filename;
     my $extraField;
-    my $streamingMode = ($gpFlag & 0x08) ? 1 : 0 ;
+    my @EXTRA = ();
+    my $streamingMode = ($gpFlag & ZIP_GP_FLAG_STREAMING_MASK) ? 1 : 0 ;
 
     return $self->HeaderError("Streamed Stored content not supported")
         if $streamingMode && $compressedMethod == 0 ;
@@ -429,23 +432,29 @@ sub _readZipHeader($)
     if ($filename_length)
     {
         $self->smartReadExact(\$filename, $filename_length)
-            or return $self->HeaderError("xxx");
+            or return $self->TruncatedHeader("Filename");
         $keep .= $filename ;
     }
 
     if ($extra_length)
     {
         $self->smartReadExact(\$extraField, $extra_length)
-            or return $self->HeaderError("xxx");
+            or return $self->TruncatedHeader("Extra Field");
+
+        my $bad = IO::Compress::Zlib::Extra::parseRawExtra($extraField,
+                                                \@EXTRA, 1, 0);
+        return $self->HeaderError($bad)
+            if defined $bad;
+
         $keep .= $extraField ;
     }
 
     *$self->{ZipData}{Method} = $compressedMethod;
-    if ($compressedMethod == 8)
+    if ($compressedMethod == ZIP_CM_DEFLATE)
     {
         *$self->{Type} = 'zip-deflate';
     }
-    elsif ($compressedMethod == 12)
+    elsif ($compressedMethod == ZIP_CM_BZIP2)
     {
     #if (! defined $IO::Uncompress::Adapter::Bunzip2::VERSION)
         
@@ -458,7 +467,7 @@ sub _readZipHeader($)
         *$self->{ZipData}{CRC32} = crc32(undef);
 
     }
-    elsif ($compressedMethod == 0)
+    elsif ($compressedMethod == ZIP_CM_STORE)
     {
         # TODO -- add support for reading uncompressed
 
@@ -491,11 +500,13 @@ sub _readZipHeader($)
         'Stream'             => $streamingMode,
 
         'MethodID'           => $compressedMethod,
-        'MethodName'         => $compressedMethod == 8 
+        'MethodName'         => $compressedMethod == ZIP_CM_DEFLATE 
                                  ? "Deflated" 
-                                 : $compressedMethod == 0
-                                     ? "Stored"
-                                     : "Unknown" ,
+                                 : $compressedMethod == ZIP_CM_BZIP2
+                                     ? "Bzip2"
+                                     : $compressedMethod == ZIP_CM_STORE
+                                         ? "Stored"
+                                         : "Unknown" ,
 
 #        'TextFlag'      => $flag & GZIP_FLG_FTEXT ? 1 : 0,
 #        'HeaderCRCFlag' => $flag & GZIP_FLG_FHCRC ? 1 : 0,
@@ -509,8 +520,8 @@ sub _readZipHeader($)
 #        'HeaderCRC'     => $HeaderCRC,
 #        'Flags'         => $flag,
 #        'ExtraFlags'    => $xfl,
-#        'ExtraFieldRaw' => $EXTRA,
-#        'ExtraField'    => [ @EXTRA ],
+        'ExtraFieldRaw' => $extraField,
+        'ExtraField'    => [ @EXTRA ],
 
 
       }
@@ -770,9 +781,13 @@ If the C<$output> parameter is any other type, C<undef> will be returned.
 
 =head2 Notes
 
-When C<$input> maps to multiple files/buffers and C<$output> is a single
-file/buffer the uncompressed input files/buffers will all be stored
-in C<$output> as a single uncompressed stream.
+
+When C<$input> maps to multiple compressed files/buffers and C<$output> is
+a single file/buffer, after uncompression C<$output> will contain a
+concatenation of all the uncompressed data from each of the input
+files/buffers.
+
+
 
 
 
index 5956ac5..c09fc32 100644 (file)
@@ -380,20 +380,20 @@ for my $value ( "0D", "0A", "0A0D", "0D0A", "0A0A", "0D0D")
 
     my @tests = (
         ["Sub-field truncated",           
-            "Error with ExtraField Parameter: FEXTRA Body",
+            "Error with ExtraField Parameter: Truncated in FEXTRA Body Section",
             "Header Error: Truncated in FEXTRA Body Section",
             ['a', undef, undef]              ],
         ["Length of field incorrect",     
-            "Error with ExtraField Parameter: FEXTRA Body",
+            "Error with ExtraField Parameter: Truncated in FEXTRA Body Section",
             "Header Error: Truncated in FEXTRA Body Section",
             ["ab", 255, "abc"]               ],
         ["Length of 2nd field incorrect", 
-            "Error with ExtraField Parameter: FEXTRA Body",
+            "Error with ExtraField Parameter: Truncated in FEXTRA Body Section",
             "Header Error: Truncated in FEXTRA Body Section",
             ["ab", 3, "abc"], ["de", 7, "x"] ],
         ["Length of 2nd field incorrect", 
             "Error with ExtraField Parameter: SubField ID 2nd byte is 0x00",
-            "Header Error: Truncated in FEXTRA Body Section",
+            "Header Error: SubField ID 2nd byte is 0x00",
             ["a\x00", 3, "abc"], ["de", 7, "x"] ],
         );
 
@@ -428,16 +428,19 @@ for my $value ( "0D", "0A", "0A0D", "0D0A", "0A0A", "0D0D")
 
         foreach my $check (0, 1)    
         {
-            ok $x = new IO::Compress::Gzip \$buffer, -ExtraField  => $input, Strict => 0
-                or diag "GzipError is $GzipError" ;                            ;
+            ok $x = new IO::Compress::Gzip \$buffer, 
+                                           ExtraField => $input, 
+                                           Strict     => 0
+                or diag "GzipError is $GzipError" ;
             my $string = "abcd" ;
             $x->write($string) ;
             $x->close ;
             is anyUncompress(\$buffer), $string ;
 
-            $x = new IO::Uncompress::Gunzip \$buffer, Strict => 0,
+            $x = new IO::Uncompress::Gunzip \$buffer, 
+                                       Strict      => 0,
                                        Transparent => 0,
-                                       ParseExtra => $check;
+                                       ParseExtra  => $check;
             if ($check) {
                 ok ! $x ;
                 like $GunzipError, "/^$gunzip_error/";  
index d2548c5..719da36 100644 (file)
@@ -18,7 +18,7 @@ BEGIN {
     $extra = 1
         if eval { require Test::NoWarnings ;  import Test::NoWarnings; 1 };
 
-    plan tests => 1986 + $extra;
+    plan tests => 2316 + $extra;
 
 };
 
diff --git a/ext/Compress/Raw/Zlib/Changes b/ext/Compress/Raw/Zlib/Changes
new file mode 100644 (file)
index 0000000..d48a762
--- /dev/null
@@ -0,0 +1,68 @@
+CHANGES
+-------
+
+  2.000_10 13 March 2006
+
+      * Fixed a potential NULL pointer dereference problem in
+        Compress::Raw::Zlib::resetLastBlockByte.
+        Issue highlighted by David Dyck and reproduced by Marcus Holland-Moritz.
+
+  2.000_09 3 March 2006
+
+      * Released onto CPAN
+
+      * Documentation updates.
+
+  2.000_08 2 March 2006
+
+      * Moved the IO::* modules out into their own distributions.
+
+      * Breakout zlib specific code into separate modules.
+
+      * Limited support for reading/writing zip files added.
+
+  2.000_06 5 October 2005
+
+      * Added eof parameter to Compress::Zlib::inflate method.
+
+      * Fixed issue with 64-bit
+
+  2.000_05 4 October 2005
+
+      * Renamed IO::* to IO::Compress::* & IO::Uncompress::*
+
+  2.000_04 23 September 2005
+
+      * Fixed some more non-portable test that were failing on VMS.
+
+      * fixed problem where error messages in the oneshot interface were
+        getting lost.
+
+  2.000_03 12 September 2005
+
+      * Fixed some non-portable test that were failing on VMS.
+
+      * Fixed export of zlib constants from the IO::* classes
+
+  2.000_02 6 September 2005
+
+      * Split Append mode into Append and Merge
+
+      * Fixed typos in the documentation.
+
+      * Added pod/FAQ.pod
+
+      * Added libscan to Makefile.PL
+
+      * Added InputLength for IO::Gunzip et al
+
+  2.000_01 22 August 2005
+
+      * Fixed VERSION in Compress::Gzip::Constants
+
+      * Removed Compress::Gzip::Info from the distribution.
+
+  2.000_00 21 August 2005
+
+      * First Beta relase of Compress::zlib rewrite.
+
index 59b0133..0fc887b 100644 (file)
@@ -1,9 +1,9 @@
 
                              Compress::Raw::Zlib
 
-                             Version 2.000_11
+                             Version 2.000_12
 
-                               10 April 2006
+                                17 May 2006
 
 
        Copyright (c) 2005-2006 Paul Marquess. All rights reserved.
index d216bd2..84b3a7c 100644 (file)
@@ -13,7 +13,7 @@ use warnings ;
 use bytes ;
 our ($VERSION, $XS_VERSION, @ISA, @EXPORT, $AUTOLOAD);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 $XS_VERSION = $VERSION; 
 $VERSION = eval $VERSION;
 
index e63ff55..557c8dd 100644 (file)
@@ -1,6 +1,15 @@
 CHANGES
 -------
 
+  2.000_12 16 April 2006
+
+      * Fixed gzread to zap the output buffer to an empty string when zero
+        bytes are requested. This matches the behaviour of C::Z 1.x
+
+  2.000_11 10 April 2006
+
+      * No changes.
+
   2.000_10 13 March 2006
 
       * Changed gzread so that its behaviour matches C::Z::gzread 1.x if it
index d94463f..a05884b 100644 (file)
@@ -1,9 +1,9 @@
 
                              Compress::Zlib
 
-                             Version 2.000_11
+                             Version 2.000_12
 
-                               10 April 2006
+                                17 May 2006
 
 
        Copyright (c) 1995-2006 Paul Marquess. All rights reserved.
index a82021a..8f97b16 100644 (file)
@@ -18,7 +18,7 @@ use warnings ;
 use bytes ;
 our ($VERSION, $XS_VERSION, @ISA, @EXPORT, $AUTOLOAD);
 
-$VERSION = '2.000_11';
+$VERSION = '2.000_12';
 $XS_VERSION = $VERSION; 
 $VERSION = eval $VERSION;
 
index 668d66d..d86aba5 100644 (file)
@@ -501,16 +501,19 @@ sub mkComplete
 
     if ($class eq 'IO::Compress::Gzip') {
         %params = (
-            -Name       => "My name",
-            -Comment    => "a comment",
-            -ExtraField => ['ab' => "extra"],
-            -HeaderCRC  => 1);
+            Name       => "My name",
+            Comment    => "a comment",
+            ExtraField => ['ab' => "extra"],
+            HeaderCRC  => 1);
     }
     elsif ($class eq 'IO::Compress::Zip'){
         %params = (
-            # TODO -- add more here
-            -Name       => "My name",
-            -Comment    => "a comment",
+            Name              => "My name",
+            Comment           => "a comment",
+            ZipComment        => "last comment",
+            exTime            => [100, 200, 300],
+            ExtraFieldLocal   => ["ab" => "extra1"],
+            ExtraFieldCentral => ["cd" => "extra2"],
         );
     }