1 package Archive::Zip::Member;
3 # A generic membet of an archive
6 use vars qw( $VERSION @ISA );
10 @ISA = qw( Archive::Zip );
22 use Compress::Zlib qw( Z_OK Z_STREAM_END MAX_WBITS );
26 use constant ZIPFILEMEMBERCLASS => 'Archive::Zip::ZipFileMember';
27 use constant NEWFILEMEMBERCLASS => 'Archive::Zip::NewFileMember';
28 use constant STRINGMEMBERCLASS => 'Archive::Zip::StringMember';
29 use constant DIRECTORYMEMBERCLASS => 'Archive::Zip::DirectoryMember';
31 # Unix perms for default creation of files/dirs.
32 use constant DEFAULT_DIRECTORY_PERMISSIONS => 040755;
33 use constant DEFAULT_FILE_PERMISSIONS => 0100666;
34 use constant DIRECTORY_ATTRIB => 040000;
35 use constant FILE_ATTRIB => 0100000;
37 # Returns self if successful, else undef
38 # Assumes that fh is positioned at beginning of central directory file header.
39 # Leaves fh positioned immediately after file header or EOCD signature.
42 my $self = $class->ZIPFILEMEMBERCLASS->_newFromZipFile(@_);
48 my $self = $class->STRINGMEMBERCLASS->_newFromString(@_);
54 my $self = $class->NEWFILEMEMBERCLASS->_newFromFileNamed(@_);
58 sub newDirectoryNamed {
60 my $self = $class->DIRECTORYMEMBERCLASS->_newNamed(@_);
67 'lastModFileDateTime' => 0,
68 'fileAttributeFormat' => FA_UNIX,
69 'versionMadeBy' => 20,
70 'versionNeededToExtract' => 20,
72 'compressionMethod' => COMPRESSION_STORED,
73 'desiredCompressionMethod' => COMPRESSION_STORED,
74 'desiredCompressionLevel' => COMPRESSION_LEVEL_NONE,
75 'internalFileAttributes' => 0,
76 'externalFileAttributes' => 0, # set later
79 'localExtraField' => '',
82 'compressedSize' => 0,
83 'uncompressedSize' => 0,
86 bless( $self, $class );
87 $self->unixFileAttributes( $self->DEFAULT_FILE_PERMISSIONS );
91 sub _becomeDirectoryIfNecessary {
93 $self->_become(DIRECTORYMEMBERCLASS)
94 if $self->isDirectory();
98 # Morph into given class (do whatever cleanup I need to do)
100 return bless( $_[0], $_[1] );
104 shift->{'versionMadeBy'};
107 sub fileAttributeFormat {
109 ? ( $_[0]->{'fileAttributeFormat'} = $_[1] )
110 : $_[0]->{'fileAttributeFormat'};
113 sub versionNeededToExtract {
114 shift->{'versionNeededToExtract'};
121 sub compressionMethod {
122 shift->{'compressionMethod'};
125 sub desiredCompressionMethod {
127 my $newDesiredCompressionMethod = shift;
128 my $oldDesiredCompressionMethod = $self->{'desiredCompressionMethod'};
129 if ( defined($newDesiredCompressionMethod) ) {
130 $self->{'desiredCompressionMethod'} = $newDesiredCompressionMethod;
131 if ( $newDesiredCompressionMethod == COMPRESSION_STORED ) {
132 $self->{'desiredCompressionLevel'} = 0;
134 elsif ( $oldDesiredCompressionMethod == COMPRESSION_STORED ) {
135 $self->{'desiredCompressionLevel'} = COMPRESSION_LEVEL_DEFAULT;
138 return $oldDesiredCompressionMethod;
141 sub desiredCompressionLevel {
143 my $newDesiredCompressionLevel = shift;
144 my $oldDesiredCompressionLevel = $self->{'desiredCompressionLevel'};
145 if ( defined($newDesiredCompressionLevel) ) {
146 $self->{'desiredCompressionLevel'} = $newDesiredCompressionLevel;
147 $self->{'desiredCompressionMethod'} = (
148 $newDesiredCompressionLevel
149 ? COMPRESSION_DEFLATED
153 return $oldDesiredCompressionLevel;
160 $newName =~ s{[\\/]+}{/}g; # deal with dos/windoze problems
161 $self->{'fileName'} = $newName;
163 return $self->{'fileName'};
166 sub lastModFileDateTime {
167 my $modTime = shift->{'lastModFileDateTime'};
168 $modTime =~ m/^(\d+)$/; # untaint
174 return _dosToUnixTime( $self->lastModFileDateTime() );
177 sub setLastModFileDateTimeFromUnix {
180 $self->{'lastModFileDateTime'} = _unixToDosTime($time_t);
183 sub internalFileAttributes {
184 shift->{'internalFileAttributes'};
187 sub externalFileAttributes {
188 shift->{'externalFileAttributes'};
191 # Convert UNIX permissions into proper value for zip file
193 sub _mapPermissionsFromUnix {
197 # TODO: map MS-DOS perms too (RHSA?)
200 # Convert ZIP permissions into Unix ones
202 # This was taken from Info-ZIP group's portable UnZip
203 # zipfile-extraction program, version 5.50.
204 # http://www.info-zip.org/pub/infozip/
206 # See the mapattr() function in unix/unix.c
207 # See the attribute format constants in unzpriv.h
209 # XXX Note that there's one situation that isn't implemented
210 # yet that depends on the "extra field."
211 sub _mapPermissionsToUnix {
214 my $format = $self->{'fileAttributeFormat'};
215 my $attribs = $self->{'externalFileAttributes'};
219 if ( $format == FA_AMIGA ) {
220 $attribs = $attribs >> 17 & 7; # Amiga RWE bits
221 $mode = $attribs << 6 | $attribs << 3 | $attribs;
225 if ( $format == FA_THEOS ) {
226 $attribs &= 0xF1FFFFFF;
227 if ( ( $attribs & 0xF0000000 ) != 0x40000000 ) {
228 $attribs &= 0x01FFFFFF; # not a dir, mask all ftype bits
231 $attribs &= 0x41FFFFFF; # leave directory bit as set
235 if ( $format == FA_UNIX
236 || $format == FA_VAX_VMS
237 || $format == FA_ACORN
238 || $format == FA_ATARI_ST
239 || $format == FA_BEOS
240 || $format == FA_QDOS
241 || $format == FA_TANDEM )
243 $mode = $attribs >> 16;
244 return $mode if $mode != 0 or not $self->localExtraField;
246 # warn("local extra field is: ", $self->localExtraField, "\n");
248 # XXX This condition is not implemented
249 # I'm just including the comments from the info-zip section for now.
251 # Some (non-Info-ZIP) implementations of Zip for Unix and
252 # VMS (and probably others ??) leave 0 in the upper 16-bit
253 # part of the external_file_attributes field. Instead, they
254 # store file permission attributes in some extra field.
255 # As a work-around, we search for the presence of one of
256 # these extra fields and fall back to the MSDOS compatible
257 # part of external_file_attributes if one of the known
258 # e.f. types has been detected.
259 # Later, we might implement extraction of the permission
260 # bits from the VMS extra field. But for now, the work-around
261 # should be sufficient to provide "readable" extracted files.
262 # (For ASI Unix e.f., an experimental remap from the e.f.
263 # mode value IS already provided!)
266 # PKWARE's PKZip for Unix marks entries as FA_MSDOS, but stores the
267 # Unix attributes in the upper 16 bits of the external attributes
268 # field, just like Info-ZIP's Zip for Unix. We try to use that
269 # value, after a check for consistency with the MSDOS attribute
271 if ( $format == FA_MSDOS ) {
272 $mode = $attribs >> 16;
275 # FA_MSDOS, FA_OS2_HPFS, FA_WINDOWS_NTFS, FA_MACINTOSH, FA_TOPS20
276 $attribs = !( $attribs & 1 ) << 1 | ( $attribs & 0x10 ) >> 4;
278 # keep previous $mode setting when its "owner"
279 # part appears to be consistent with DOS attribute flags!
280 return $mode if ( $mode & 0700 ) == ( 0400 | $attribs << 6 );
281 $mode = 0444 | $attribs << 6 | $attribs << 3 | $attribs;
285 sub unixFileAttributes {
287 my $oldPerms = $self->_mapPermissionsToUnix();
290 if ( $self->isDirectory() ) {
291 $perms &= ~FILE_ATTRIB;
292 $perms |= DIRECTORY_ATTRIB;
295 $perms &= ~DIRECTORY_ATTRIB;
296 $perms |= FILE_ATTRIB;
298 $self->{'externalFileAttributes'} = _mapPermissionsFromUnix($perms);
303 sub localExtraField {
305 ? ( $_[0]->{'localExtraField'} = $_[1] )
306 : $_[0]->{'localExtraField'};
310 ( $#_ > 0 ) ? ( $_[0]->{'cdExtraField'} = $_[1] ) : $_[0]->{'cdExtraField'};
315 return $self->localExtraField() . $self->cdExtraField();
320 ? ( $_[0]->{'fileComment'} = pack( 'C0a*', $_[1] ) )
321 : $_[0]->{'fileComment'};
324 sub hasDataDescriptor {
327 my $shouldHave = shift;
329 $self->{'bitFlag'} |= GPBF_HAS_DATA_DESCRIPTOR_MASK;
332 $self->{'bitFlag'} &= ~GPBF_HAS_DATA_DESCRIPTOR_MASK;
335 return $self->{'bitFlag'} & GPBF_HAS_DATA_DESCRIPTOR_MASK;
343 sprintf( "%08x", shift->{'crc32'} );
347 shift->{'compressedSize'};
350 sub uncompressedSize {
351 shift->{'uncompressedSize'};
355 shift->bitFlag() & GPBF_ENCRYPTED_MASK;
360 my $bit = $self->internalFileAttributes() & IFA_TEXT_FILE_MASK;
363 $self->{'internalFileAttributes'} &= ~IFA_TEXT_FILE_MASK;
364 $self->{'internalFileAttributes'} |=
365 ( $flag ? IFA_TEXT_FILE: IFA_BINARY_FILE );
367 return $bit == IFA_TEXT_FILE;
372 my $bit = $self->internalFileAttributes() & IFA_TEXT_FILE_MASK;
375 $self->{'internalFileAttributes'} &= ~IFA_TEXT_FILE_MASK;
376 $self->{'internalFileAttributes'} |=
377 ( $flag ? IFA_BINARY_FILE: IFA_TEXT_FILE );
379 return $bit == IFA_BINARY_FILE;
382 sub extractToFileNamed {
384 my $name = shift; # local FS name
385 return _error("encryption unsupported") if $self->isEncrypted();
386 mkpath( dirname($name) ); # croaks on error
387 my ( $status, $fh ) = _newFileHandle( $name, 'w' );
388 return _ioError("Can't open file $name for write") unless $status;
389 my $retval = $self->extractToFileHandle($fh);
391 utime( $self->lastModTime(), $self->lastModTime(), $name );
399 sub externalFileName {
403 # The following are used when copying data
405 shift->{'writeOffset'};
409 shift->{'readOffset'};
412 sub writeLocalHeaderRelativeOffset {
413 shift->{'writeLocalHeaderRelativeOffset'};
416 sub wasWritten { shift->{'wasWritten'} }
419 shift->{'dataEnded'};
422 sub _readDataRemaining {
423 shift->{'readDataRemaining'};
434 # Return the total size of my local header
435 sub _localHeaderSize {
437 return SIGNATURE_LENGTH + LOCAL_FILE_HEADER_LENGTH +
438 length( $self->fileName() ) + length( $self->localExtraField() );
441 # Return the total size of my CD header
442 sub _centralDirectoryHeaderSize {
444 return SIGNATURE_LENGTH + CENTRAL_DIRECTORY_FILE_HEADER_LENGTH +
445 length( $self->fileName() ) + length( $self->cdExtraField() ) +
446 length( $self->fileComment() );
449 # DOS date/time format
450 # 0-4 (5) Second divided by 2
451 # 5-10 (6) Minute (0-59)
452 # 11-15 (5) Hour (0-23 on a 24-hour clock)
453 # 16-20 (5) Day of the month (1-31)
454 # 21-24 (4) Month (1 = January, 2 = February, etc.)
455 # 25-31 (7) Year offset from 1980 (add 1980 to get actual year)
457 # Convert DOS date/time format to unix time_t format
458 # NOT AN OBJECT METHOD!
461 return time() unless defined($dt);
463 my $year = ( ( $dt >> 25 ) & 0x7f ) + 80;
464 my $mon = ( ( $dt >> 21 ) & 0x0f ) - 1;
465 my $mday = ( ( $dt >> 16 ) & 0x1f );
467 my $hour = ( ( $dt >> 11 ) & 0x1f );
468 my $min = ( ( $dt >> 5 ) & 0x3f );
469 my $sec = ( ( $dt << 1 ) & 0x3e );
473 eval { Time::Local::timelocal( $sec, $min, $hour, $mday, $mon, $year ); };
474 return time() if ($@);
478 # Note, this isn't exactly UTC 1980, it's 1980 + 12 hours and 1
479 # minute so that nothing timezoney can muck us up.
480 my $safe_epoch = 315576060;
482 # convert a unix time to DOS date/time
483 # NOT AN OBJECT METHOD!
487 _error("Tried to add member with zero or undef value for time");
488 $time_t = $safe_epoch;
490 if ( $time_t < $safe_epoch ) {
491 _ioError("Unsupported date before 1980 encountered, moving to 1980");
492 $time_t = $safe_epoch;
494 my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime($time_t);
496 $dt += ( $sec >> 1 );
497 $dt += ( $min << 5 );
498 $dt += ( $hour << 11 );
499 $dt += ( $mday << 16 );
500 $dt += ( ( $mon + 1 ) << 21 );
501 $dt += ( ( $year - 80 ) << 25 );
505 # Write my local header to a file handle.
506 # Stores the offset to the start of the header in my
507 # writeLocalHeaderRelativeOffset member.
508 # Returns AZ_OK on success.
509 sub _writeLocalFileHeader {
513 my $signatureData = pack( SIGNATURE_FORMAT, LOCAL_FILE_HEADER_SIGNATURE );
514 $fh->print($signatureData)
515 or return _ioError("writing local header signature");
518 LOCAL_FILE_HEADER_FORMAT,
519 $self->versionNeededToExtract(),
521 $self->desiredCompressionMethod(),
522 $self->lastModFileDateTime(),
524 $self->compressedSize(), # may need to be re-written later
525 $self->uncompressedSize(),
526 length( $self->fileName() ),
527 length( $self->localExtraField() )
530 $fh->print($header) or return _ioError("writing local header");
531 if ( $self->fileName() ) {
532 $fh->print( $self->fileName() )
533 or return _ioError("writing local header filename");
535 if ( $self->localExtraField() ) {
536 $fh->print( $self->localExtraField() )
537 or return _ioError("writing local extra field");
543 sub _writeCentralDirectoryFileHeader {
548 pack( SIGNATURE_FORMAT, CENTRAL_DIRECTORY_FILE_HEADER_SIGNATURE );
550 or return _ioError("writing central directory header signature");
552 my $fileNameLength = length( $self->fileName() );
553 my $extraFieldLength = length( $self->cdExtraField() );
554 my $fileCommentLength = length( $self->fileComment() );
557 CENTRAL_DIRECTORY_FILE_HEADER_FORMAT,
558 $self->versionMadeBy(),
559 $self->fileAttributeFormat(),
560 $self->versionNeededToExtract(),
562 $self->desiredCompressionMethod(),
563 $self->lastModFileDateTime(),
564 $self->crc32(), # these three fields should have been updated
565 $self->_writeOffset(), # by writing the data stream out
566 $self->uncompressedSize(), #
570 0, # {'diskNumberStart'},
571 $self->internalFileAttributes(),
572 $self->externalFileAttributes(),
573 $self->writeLocalHeaderRelativeOffset()
577 or return _ioError("writing central directory header");
578 if ($fileNameLength) {
579 $fh->print( $self->fileName() )
580 or return _ioError("writing central directory header signature");
582 if ($extraFieldLength) {
583 $fh->print( $self->cdExtraField() )
584 or return _ioError("writing central directory extra field");
586 if ($fileCommentLength) {
587 $fh->print( $self->fileComment() )
588 or return _ioError("writing central directory file comment");
594 # This writes a data descriptor to the given file handle.
595 # Assumes that crc32, writeOffset, and uncompressedSize are
596 # set correctly (they should be after a write).
597 # Further, the local file header should have the
598 # GPBF_HAS_DATA_DESCRIPTOR_MASK bit set.
599 sub _writeDataDescriptor {
603 SIGNATURE_FORMAT . DATA_DESCRIPTOR_FORMAT,
604 DATA_DESCRIPTOR_SIGNATURE,
606 $self->_writeOffset(), # compressed size
607 $self->uncompressedSize()
611 or return _ioError("writing data descriptor");
615 # Re-writes the local file header with new crc32 and compressedSize fields.
616 # To be called after writing the data stream.
617 # Assumes that filename and extraField sizes didn't change since last written.
618 sub _refreshLocalFileHeader {
622 my $here = $fh->tell();
623 $fh->seek( $self->writeLocalHeaderRelativeOffset() + SIGNATURE_LENGTH,
624 IO::Seekable::SEEK_SET )
625 or return _ioError("seeking to rewrite local header");
628 LOCAL_FILE_HEADER_FORMAT,
629 $self->versionNeededToExtract(),
631 $self->desiredCompressionMethod(),
632 $self->lastModFileDateTime(),
634 $self->_writeOffset(), # compressed size
635 $self->uncompressedSize(),
636 length( $self->fileName() ),
637 length( $self->localExtraField() )
641 or return _ioError("re-writing local header");
642 $fh->seek( $here, IO::Seekable::SEEK_SET )
643 or return _ioError("seeking after rewrite of local header");
649 my ( $self, $chunkSize ) = @_;
651 if ( $self->readIsDone() ) {
654 return ( \$dummy, AZ_STREAM_END );
657 $chunkSize = $Archive::Zip::ChunkSize if not defined($chunkSize);
658 $chunkSize = $self->_readDataRemaining()
659 if $chunkSize > $self->_readDataRemaining();
663 my ( $bytesRead, $status ) = $self->_readRawChunk( \$buffer, $chunkSize );
664 return ( \$buffer, $status ) unless $status == AZ_OK;
666 $self->{'readDataRemaining'} -= $bytesRead;
667 $self->{'readOffset'} += $bytesRead;
669 if ( $self->compressionMethod() == COMPRESSION_STORED ) {
670 $self->{'crc32'} = $self->computeCRC32( $buffer, $self->{'crc32'} );
673 ( $outputRef, $status ) = &{ $self->{'chunkHandler'} }( $self, \$buffer );
674 $self->{'writeOffset'} += length($$outputRef);
677 if $self->readIsDone();
679 return ( $outputRef, $status );
682 # Read the next raw chunk of my data. Subclasses MUST implement.
683 # my ( $bytesRead, $status) = $self->_readRawChunk( \$buffer, $chunkSize );
686 return $self->_subclassResponsibility();
689 # A place holder to catch rewindData errors if someone ignores
693 return ( \undef, _error("trying to copy chunk when init failed") );
696 # Basically a no-op so that I can have a consistent interface.
697 # ( $outputRef, $status) = $self->_copyChunk( \$buffer );
699 my ( $self, $dataRef ) = @_;
700 return ( $dataRef, AZ_OK );
703 # ( $outputRef, $status) = $self->_deflateChunk( \$buffer );
705 my ( $self, $buffer ) = @_;
706 my ( $out, $status ) = $self->_deflater()->deflate($buffer);
708 if ( $self->_readDataRemaining() == 0 ) {
710 ( $extraOutput, $status ) = $self->_deflater()->flush();
711 $out .= $extraOutput;
713 return ( \$out, AZ_STREAM_END );
715 elsif ( $status == Z_OK ) {
716 return ( \$out, AZ_OK );
720 my $retval = _error( 'deflate error', $status );
722 return ( \$dummy, $retval );
726 # ( $outputRef, $status) = $self->_inflateChunk( \$buffer );
728 my ( $self, $buffer ) = @_;
729 my ( $out, $status ) = $self->_inflater()->inflate($buffer);
731 $self->endRead() unless $status == Z_OK;
732 if ( $status == Z_OK || $status == Z_STREAM_END ) {
733 $retval = ( $status == Z_STREAM_END ) ? AZ_STREAM_END: AZ_OK;
734 return ( \$out, $retval );
737 $retval = _error( 'inflate error', $status );
739 return ( \$dummy, $retval );
747 # set to trap init errors
748 $self->{'chunkHandler'} = $self->can('_noChunk');
750 # Work around WinZip bug with 0-length DEFLATED files
751 $self->desiredCompressionMethod(COMPRESSION_STORED)
752 if $self->uncompressedSize() == 0;
754 # assume that we're going to read the whole file, and compute the CRC anew.
756 if ( $self->compressionMethod() == COMPRESSION_STORED );
758 # These are the only combinations of methods we deal with right now.
759 if ( $self->compressionMethod() == COMPRESSION_STORED
760 and $self->desiredCompressionMethod() == COMPRESSION_DEFLATED )
762 ( $self->{'deflater'}, $status ) = Compress::Zlib::deflateInit(
763 '-Level' => $self->desiredCompressionLevel(),
764 '-WindowBits' => -MAX_WBITS(), # necessary magic
765 '-Bufsize' => $Archive::Zip::ChunkSize,
767 ); # pass additional options
768 return _error( 'deflateInit error:', $status )
769 unless $status == Z_OK;
770 $self->{'chunkHandler'} = $self->can('_deflateChunk');
772 elsif ( $self->compressionMethod() == COMPRESSION_DEFLATED
773 and $self->desiredCompressionMethod() == COMPRESSION_STORED )
775 ( $self->{'inflater'}, $status ) = Compress::Zlib::inflateInit(
776 '-WindowBits' => -MAX_WBITS(), # necessary magic
777 '-Bufsize' => $Archive::Zip::ChunkSize,
779 ); # pass additional options
780 return _error( 'inflateInit error:', $status )
781 unless $status == Z_OK;
782 $self->{'chunkHandler'} = $self->can('_inflateChunk');
784 elsif ( $self->compressionMethod() == $self->desiredCompressionMethod() ) {
785 $self->{'chunkHandler'} = $self->can('_copyChunk');
790 "Unsupported compression combination: read %d, write %d",
791 $self->compressionMethod(),
792 $self->desiredCompressionMethod()
797 $self->{'readDataRemaining'} =
798 ( $self->compressionMethod() == COMPRESSION_STORED )
799 ? $self->uncompressedSize()
800 : $self->compressedSize();
801 $self->{'dataEnded'} = 0;
802 $self->{'readOffset'} = 0;
809 delete $self->{'inflater'};
810 delete $self->{'deflater'};
811 $self->{'dataEnded'} = 1;
812 $self->{'readDataRemaining'} = 0;
818 return ( $self->_dataEnded() or !$self->_readDataRemaining() );
823 my $newContents = shift;
825 if ( defined($newContents) ) {
827 # change our type and call the subclass contents method.
828 $self->_become(STRINGMEMBERCLASS);
829 return $self->contents( pack( 'C0a*', $newContents ) )
830 ; # in case of Unicode
834 $self->desiredCompressionMethod(COMPRESSION_STORED);
835 my $status = $self->rewindData(@_);
836 if ( $status != AZ_OK ) {
841 while ( $status == AZ_OK ) {
843 ( $ref, $status ) = $self->readChunk( $self->_readDataRemaining() );
845 # did we get it in one chunk?
846 if ( length($$ref) == $self->uncompressedSize() ) {
849 else { $retval .= $$ref }
851 $self->desiredCompressionMethod($oldCompression);
853 $status = AZ_OK if $status == AZ_STREAM_END;
854 $retval = undef unless $status == AZ_OK;
855 return wantarray ? ( $retval, $status ) : $retval;
859 sub extractToFileHandle {
861 return _error("encryption unsupported") if $self->isEncrypted();
864 my $oldCompression = $self->desiredCompressionMethod(COMPRESSION_STORED);
865 my $status = $self->rewindData(@_);
866 $status = $self->_writeData($fh) if $status == AZ_OK;
867 $self->desiredCompressionMethod($oldCompression);
872 # write local header and data stream to file handle
873 sub _writeToFileHandle {
876 my $fhIsSeekable = shift;
879 return _error("no member name given for $self")
880 unless $self->fileName();
882 $self->{'writeLocalHeaderRelativeOffset'} = $offset;
883 $self->{'wasWritten'} = 0;
885 # Determine if I need to write a data descriptor
886 # I need to do this if I can't refresh the header
887 # and I don't know compressed size or crc32 fields.
888 my $headerFieldsUnknown = (
889 ( $self->uncompressedSize() > 0 )
890 and ($self->compressionMethod() == COMPRESSION_STORED
891 or $self->desiredCompressionMethod() == COMPRESSION_DEFLATED )
894 my $shouldWriteDataDescriptor =
895 ( $headerFieldsUnknown and not $fhIsSeekable );
897 $self->hasDataDescriptor(1)
898 if ($shouldWriteDataDescriptor);
900 $self->{'writeOffset'} = 0;
902 my $status = $self->rewindData();
903 ( $status = $self->_writeLocalFileHeader($fh) )
905 ( $status = $self->_writeData($fh) )
907 if ( $status == AZ_OK ) {
908 $self->{'wasWritten'} = 1;
909 if ( $self->hasDataDescriptor() ) {
910 $status = $self->_writeDataDescriptor($fh);
912 elsif ($headerFieldsUnknown) {
913 $status = $self->_refreshLocalFileHeader($fh);
920 # Copy my (possibly compressed) data to given file handle.
921 # Returns C<AZ_OK> on success
926 return AZ_OK if ( $self->uncompressedSize() == 0 );
928 my $chunkSize = $Archive::Zip::ChunkSize;
929 while ( $self->_readDataRemaining() > 0 ) {
931 ( $outRef, $status ) = $self->readChunk($chunkSize);
932 return $status if ( $status != AZ_OK and $status != AZ_STREAM_END );
934 if ( length($$outRef) > 0 ) {
935 $writeFh->print($$outRef)
936 or return _ioError("write error during copy");
939 last if $status == AZ_STREAM_END;
941 $self->{'compressedSize'} = $self->_writeOffset();
945 # Return true if I depend on the named file