home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / perl / Zip.pm < prev    next >
Encoding:
Perl POD Document  |  2003-11-26  |  80.1 KB  |  2,979 lines

  1. #! perl -w
  2. # $Revision: 1.88 $
  3.  
  4. # Copyright (c) 2000-2002 Ned Konz. All rights reserved.  This program is free
  5. # software; you can redistribute it and/or modify it under the same terms as
  6. # Perl itself.
  7.  
  8. # ----------------------------------------------------------------------
  9. # class Archive::Zip
  10. # Note that the package Archive::Zip exists only for exporting and
  11. # sharing constants. Everything else is in another package
  12. # in this file.
  13. # Creation of a new Archive::Zip object actually creates a new object
  14. # of class Archive::Zip::Archive.
  15. # ----------------------------------------------------------------------
  16.  
  17. package Archive::Zip;
  18. require 5.003_96;
  19. use strict;
  20.  
  21. use Carp();
  22. use IO::File();
  23. use IO::Seekable();
  24. use Compress::Zlib();
  25. use File::Spec 0.8 ();
  26.  
  27. use vars
  28.   qw( @ISA @EXPORT_OK %EXPORT_TAGS $VERSION $ChunkSize $ErrorHandler $TempSequence);
  29.  
  30. # This is the size we'll try to read, write, and (de)compress.
  31. # You could set it to something different if you had lots of memory
  32. # and needed more speed.
  33. $ChunkSize = 32768;
  34.  
  35. $ErrorHandler = \&Carp::carp;
  36.  
  37. # BEGIN block is necessary here so that other modules can use the constants.
  38. BEGIN
  39. {
  40.     require Exporter;
  41.  
  42.     $VERSION = "1.09";
  43.     @ISA = qw( Exporter );
  44.  
  45.     my @ConstantNames = qw( FA_MSDOS FA_UNIX GPBF_ENCRYPTED_MASK
  46.       GPBF_DEFLATING_COMPRESSION_MASK GPBF_HAS_DATA_DESCRIPTOR_MASK
  47.       COMPRESSION_STORED COMPRESSION_DEFLATED COMPRESSION_LEVEL_NONE
  48.       COMPRESSION_LEVEL_DEFAULT COMPRESSION_LEVEL_FASTEST
  49.       COMPRESSION_LEVEL_BEST_COMPRESSION IFA_TEXT_FILE_MASK IFA_TEXT_FILE
  50.       IFA_BINARY_FILE );
  51.  
  52.     my @MiscConstantNames = qw( FA_AMIGA FA_VAX_VMS FA_VM_CMS FA_ATARI_ST
  53.       FA_OS2_HPFS FA_MACINTOSH FA_Z_SYSTEM FA_CPM FA_TOPS20
  54.       FA_WINDOWS_NTFS FA_QDOS FA_ACORN FA_VFAT FA_MVS FA_BEOS FA_TANDEM
  55.       FA_THEOS GPBF_IMPLODING_8K_SLIDING_DICTIONARY_MASK
  56.       GPBF_IMPLODING_3_SHANNON_FANO_TREES_MASK
  57.       GPBF_IS_COMPRESSED_PATCHED_DATA_MASK COMPRESSION_SHRUNK
  58.       DEFLATING_COMPRESSION_NORMAL DEFLATING_COMPRESSION_MAXIMUM
  59.       DEFLATING_COMPRESSION_FAST DEFLATING_COMPRESSION_SUPER_FAST
  60.       COMPRESSION_REDUCED_1 COMPRESSION_REDUCED_2 COMPRESSION_REDUCED_3
  61.       COMPRESSION_REDUCED_4 COMPRESSION_IMPLODED COMPRESSION_TOKENIZED
  62.       COMPRESSION_DEFLATED_ENHANCED
  63.       COMPRESSION_PKWARE_DATA_COMPRESSION_LIBRARY_IMPLODED );
  64.  
  65.     my @ErrorCodeNames = qw( AZ_OK AZ_STREAM_END AZ_ERROR AZ_FORMAT_ERROR
  66.       AZ_IO_ERROR );
  67.  
  68.     my @PKZipConstantNames = qw( SIGNATURE_FORMAT SIGNATURE_LENGTH
  69.       LOCAL_FILE_HEADER_SIGNATURE LOCAL_FILE_HEADER_FORMAT
  70.       LOCAL_FILE_HEADER_LENGTH DATA_DESCRIPTOR_FORMAT DATA_DESCRIPTOR_LENGTH
  71.       CENTRAL_DIRECTORY_FILE_HEADER_SIGNATURE
  72.       CENTRAL_DIRECTORY_FILE_HEADER_FORMAT CENTRAL_DIRECTORY_FILE_HEADER_LENGTH
  73.       END_OF_CENTRAL_DIRECTORY_SIGNATURE
  74.       END_OF_CENTRAL_DIRECTORY_SIGNATURE_STRING END_OF_CENTRAL_DIRECTORY_FORMAT
  75.       END_OF_CENTRAL_DIRECTORY_LENGTH );
  76.  
  77.     my @UtilityMethodNames = qw( _error _printError _ioError _formatError
  78.       _subclassResponsibility _binmode _isSeekable _newFileHandle _readSignature
  79.       _asZipDirName);
  80.  
  81.     @EXPORT_OK   = ('computeCRC32');
  82.     %EXPORT_TAGS = (
  83.         'CONSTANTS'      => \@ConstantNames,
  84.         'MISC_CONSTANTS' => \@MiscConstantNames,
  85.         'ERROR_CODES'    => \@ErrorCodeNames,
  86.  
  87.         # The following two sets are for internal use only
  88.         'PKZIP_CONSTANTS' => \@PKZipConstantNames,
  89.         'UTILITY_METHODS' => \@UtilityMethodNames
  90.     );
  91.  
  92.     # Add all the constant names and error code names to @EXPORT_OK
  93.     Exporter::export_ok_tags(
  94.         'CONSTANTS',       'ERROR_CODES',
  95.         'PKZIP_CONSTANTS', 'UTILITY_METHODS',
  96.         'MISC_CONSTANTS'
  97.     );
  98. }
  99.  
  100. # ------------------------- begin exportable error codes -------------------
  101.  
  102. use constant AZ_OK           => 0;
  103. use constant AZ_STREAM_END   => 1;
  104. use constant AZ_ERROR        => 2;
  105. use constant AZ_FORMAT_ERROR => 3;
  106. use constant AZ_IO_ERROR     => 4;
  107.  
  108. # ------------------------- end exportable error codes ---------------------
  109. # ------------------------- begin exportable constants ---------------------
  110.  
  111. # File types
  112. # Values of Archive::Zip::Member->fileAttributeFormat()
  113.  
  114. use constant FA_MSDOS        => 0;
  115. use constant FA_AMIGA        => 1;
  116. use constant FA_VAX_VMS      => 2;
  117. use constant FA_UNIX         => 3;
  118. use constant FA_VM_CMS       => 4;
  119. use constant FA_ATARI_ST     => 5;
  120. use constant FA_OS2_HPFS     => 6;
  121. use constant FA_MACINTOSH    => 7;
  122. use constant FA_Z_SYSTEM     => 8;
  123. use constant FA_CPM          => 9;
  124. use constant FA_TOPS20       => 10;
  125. use constant FA_WINDOWS_NTFS => 11;
  126. use constant FA_QDOS         => 12;
  127. use constant FA_ACORN        => 13;
  128. use constant FA_VFAT         => 14;
  129. use constant FA_MVS          => 15;
  130. use constant FA_BEOS         => 16;
  131. use constant FA_TANDEM       => 17;
  132. use constant FA_THEOS        => 18;
  133.  
  134. # general-purpose bit flag masks
  135. # Found in Archive::Zip::Member->bitFlag()
  136.  
  137. use constant GPBF_ENCRYPTED_MASK             => 1 << 0;
  138. use constant GPBF_DEFLATING_COMPRESSION_MASK => 3 << 1;
  139. use constant GPBF_HAS_DATA_DESCRIPTOR_MASK   => 1 << 3;
  140.  
  141. # deflating compression types, if compressionMethod == COMPRESSION_DEFLATED
  142. # ( Archive::Zip::Member->bitFlag() & GPBF_DEFLATING_COMPRESSION_MASK )
  143.  
  144. use constant DEFLATING_COMPRESSION_NORMAL     => 0 << 1;
  145. use constant DEFLATING_COMPRESSION_MAXIMUM    => 1 << 1;
  146. use constant DEFLATING_COMPRESSION_FAST       => 2 << 1;
  147. use constant DEFLATING_COMPRESSION_SUPER_FAST => 3 << 1;
  148.  
  149. # compression method
  150.  
  151. # these two are the only ones supported in this module
  152. use constant COMPRESSION_STORED   => 0;    # file is stored (no compression)
  153. use constant COMPRESSION_DEFLATED => 8;    # file is Deflated
  154.  
  155. use constant COMPRESSION_LEVEL_NONE             => 0;
  156. use constant COMPRESSION_LEVEL_DEFAULT          => -1;
  157. use constant COMPRESSION_LEVEL_FASTEST          => 1;
  158. use constant COMPRESSION_LEVEL_BEST_COMPRESSION => 9;
  159.  
  160. # internal file attribute bits
  161. # Found in Archive::Zip::Member::internalFileAttributes()
  162.  
  163. use constant IFA_TEXT_FILE_MASK => 1;
  164. use constant IFA_TEXT_FILE      => 1;      # file is apparently text
  165. use constant IFA_BINARY_FILE    => 0;
  166.  
  167. # PKZIP file format miscellaneous constants (for internal use only)
  168. use constant SIGNATURE_FORMAT => "V";
  169. use constant SIGNATURE_LENGTH => 4;
  170.  
  171. use constant LOCAL_FILE_HEADER_SIGNATURE => 0x04034b50;
  172. use constant LOCAL_FILE_HEADER_FORMAT    => "v3 V4 v2";
  173. use constant LOCAL_FILE_HEADER_LENGTH    => 26;
  174.  
  175. use constant DATA_DESCRIPTOR_FORMAT => "V3";
  176. use constant DATA_DESCRIPTOR_LENGTH => 12;
  177.  
  178. use constant CENTRAL_DIRECTORY_FILE_HEADER_SIGNATURE => 0x02014b50;
  179. use constant CENTRAL_DIRECTORY_FILE_HEADER_FORMAT    => "C2 v3 V4 v5 V2";
  180. use constant CENTRAL_DIRECTORY_FILE_HEADER_LENGTH    => 42;
  181.  
  182. use constant END_OF_CENTRAL_DIRECTORY_SIGNATURE        => 0x06054b50;
  183. use constant END_OF_CENTRAL_DIRECTORY_SIGNATURE_STRING =>
  184.   pack( "V", END_OF_CENTRAL_DIRECTORY_SIGNATURE );
  185. use constant END_OF_CENTRAL_DIRECTORY_FORMAT => "v4 V2 v";
  186. use constant END_OF_CENTRAL_DIRECTORY_LENGTH => 18;
  187.  
  188. use constant GPBF_IMPLODING_8K_SLIDING_DICTIONARY_MASK => 1 << 1;
  189. use constant GPBF_IMPLODING_3_SHANNON_FANO_TREES_MASK  => 1 << 2;
  190. use constant GPBF_IS_COMPRESSED_PATCHED_DATA_MASK      => 1 << 5;
  191.  
  192. # the rest of these are not supported in this module
  193. use constant COMPRESSION_SHRUNK    => 1;    # file is Shrunk
  194. use constant COMPRESSION_REDUCED_1 => 2;    # file is Reduced CF=1
  195. use constant COMPRESSION_REDUCED_2 => 3;    # file is Reduced CF=2
  196. use constant COMPRESSION_REDUCED_3 => 4;    # file is Reduced CF=3
  197. use constant COMPRESSION_REDUCED_4 => 5;    # file is Reduced CF=4
  198. use constant COMPRESSION_IMPLODED  => 6;    # file is Imploded
  199. use constant COMPRESSION_TOKENIZED => 7;    # reserved for Tokenizing compr.
  200. use constant COMPRESSION_DEFLATED_ENHANCED => 9;   # reserved for enh. Deflating
  201. use constant COMPRESSION_PKWARE_DATA_COMPRESSION_LIBRARY_IMPLODED => 10;
  202.  
  203. # ------------------------- end of exportable constants ---------------------
  204.  
  205. use constant ZIPARCHIVECLASS => 'Archive::Zip::Archive';
  206. use constant ZIPMEMBERCLASS  => 'Archive::Zip::Member';
  207.  
  208. sub new    # Archive::Zip
  209. {
  210.     my $class = shift;
  211.     return $class->ZIPARCHIVECLASS->new(@_);
  212. }
  213.  
  214. sub computeCRC32    # Archive::Zip
  215. {
  216.     my $data = shift;
  217.     $data = shift if ref($data);    # allow calling as an obj method
  218.     my $crc = shift;
  219.     return Compress::Zlib::crc32( $data, $crc );
  220. }
  221.  
  222. # Report or change chunk size used for reading and writing.
  223. # Also sets Zlib's default buffer size (eventually).
  224. sub setChunkSize    # Archive::Zip
  225. {
  226.     my $chunkSize = shift;
  227.     $chunkSize = shift if ref($chunkSize);    # object method on zip?
  228.     my $oldChunkSize = $Archive::Zip::ChunkSize;
  229.     $Archive::Zip::ChunkSize = $chunkSize if ($chunkSize);
  230.     return $oldChunkSize;
  231. }
  232.  
  233. sub chunkSize    # Archive::Zip
  234. {
  235.     return $Archive::Zip::ChunkSize;
  236. }
  237.  
  238. sub setErrorHandler (&)    # Archive::Zip
  239. {
  240.     my $errorHandler = shift;
  241.     $errorHandler = \&Carp::carp unless defined($errorHandler);
  242.     my $oldErrorHandler = $Archive::Zip::ErrorHandler;
  243.     $Archive::Zip::ErrorHandler = $errorHandler;
  244.     return $oldErrorHandler;
  245. }
  246.  
  247. # ----------------------------------------------------------------------
  248. # Private utility functions (not methods).
  249. # ----------------------------------------------------------------------
  250.  
  251. sub _printError    # Archive::Zip
  252. {
  253.     my $string = join ( ' ', @_, "\n" );
  254.     my $oldCarpLevel = $Carp::CarpLevel;
  255.     $Carp::CarpLevel += 2;
  256.     &{$ErrorHandler} ($string);
  257.     $Carp::CarpLevel = $oldCarpLevel;
  258. }
  259.  
  260. # This is called on format errors.
  261. sub _formatError    # Archive::Zip
  262. {
  263.     shift if ref( $_[0] );
  264.     _printError( 'format error:', @_ );
  265.     return AZ_FORMAT_ERROR;
  266. }
  267.  
  268. # This is called on IO errors.
  269. sub _ioError    # Archive::Zip
  270. {
  271.     shift if ref( $_[0] );
  272.     _printError( 'IO error:', @_, ':', $! );
  273.     return AZ_IO_ERROR;
  274. }
  275.  
  276. # This is called on generic errors.
  277. sub _error    # Archive::Zip
  278. {
  279.     shift if ref( $_[0] );
  280.     _printError( 'error:', @_ );
  281.     return AZ_ERROR;
  282. }
  283.  
  284. # Called when a subclass should have implemented
  285. # something but didn't
  286. sub _subclassResponsibility    # Archive::Zip
  287. {
  288.     Carp::croak("subclass Responsibility\n");
  289. }
  290.  
  291. # Try to set the given file handle or object into binary mode.
  292. sub _binmode    # Archive::Zip
  293. {
  294.     my $fh = shift;
  295.     return UNIVERSAL::can( $fh, 'binmode' ) ? $fh->binmode() : binmode($fh);
  296. }
  297.  
  298. # Attempt to guess whether file handle is seekable.
  299. # Because of problems with Windoze, this only returns true when
  300. # the file handle is a real file.
  301. sub _isSeekable    # Archive::Zip
  302. {
  303.     my $fh = shift;
  304.  
  305.     if ( UNIVERSAL::isa( $fh, 'IO::Scalar' ) )
  306.     {
  307.         return 0;
  308.     }
  309.     elsif ( UNIVERSAL::isa( $fh, 'IO::String' ) )
  310.     {
  311.         return 1;
  312.     }
  313.     elsif ( UNIVERSAL::can( $fh, 'stat' ) )
  314.     {
  315.         return -f $fh;
  316.     }
  317.     return UNIVERSAL::can( $fh, 'seek' );
  318. }
  319.  
  320. # Return an opened IO::Handle
  321. # my ( $status, fh ) = _newFileHandle( 'fileName', 'w' );
  322. # Can take a filename, file handle, or ref to GLOB
  323. # Or, if given something that is a ref but not an IO::Handle,
  324. # passes back the same thing.
  325. sub _newFileHandle    # Archive::Zip
  326. {
  327.     my $fd     = shift;
  328.     my $status = 1;
  329.     my $handle;
  330.  
  331.     if ( ref($fd) )
  332.     {
  333.         if ( UNIVERSAL::isa( $fd, 'IO::Scalar' )
  334.             or UNIVERSAL::isa( $fd, 'IO::String' ) )
  335.         {
  336.             $handle = $fd;
  337.         }
  338.         elsif ( UNIVERSAL::isa( $fd, 'IO::Handle' )
  339.             or UNIVERSAL::isa( $fd, 'GLOB' ) )
  340.         {
  341.             $handle = IO::File->new();
  342.             $status = $handle->fdopen( $fd, @_ );
  343.         }
  344.         else
  345.         {
  346.             $handle = $fd;
  347.         }
  348.     }
  349.     else
  350.     {
  351.         $handle = IO::File->new();
  352.         $status = $handle->open( $fd, @_ );
  353.     }
  354.  
  355.     return ( $status, $handle );
  356. }
  357.  
  358. # Returns next signature from given file handle, leaves
  359. # file handle positioned afterwards.
  360. # In list context, returns ($status, $signature)
  361. # ( $status, $signature) = _readSignature( $fh, $fileName );
  362.  
  363. sub _readSignature    # Archive::Zip
  364. {
  365.     my $fh                = shift;
  366.     my $fileName          = shift;
  367.     my $expectedSignature = shift;    # optional
  368.  
  369.     my $signatureData;
  370.     my $bytesRead = $fh->read( $signatureData, SIGNATURE_LENGTH );
  371.     return _ioError("reading header signature")
  372.       if $bytesRead != SIGNATURE_LENGTH;
  373.     my $signature = unpack( SIGNATURE_FORMAT, $signatureData );
  374.     my $status    = AZ_OK;
  375.  
  376.     # compare with expected signature, if any, or any known signature.
  377.     if ( ( defined($expectedSignature) && $signature != $expectedSignature )
  378.         || ( !defined($expectedSignature)
  379.             && $signature != CENTRAL_DIRECTORY_FILE_HEADER_SIGNATURE
  380.             && $signature != LOCAL_FILE_HEADER_SIGNATURE
  381.             && $signature != END_OF_CENTRAL_DIRECTORY_SIGNATURE ) )
  382.     {
  383.         my $errmsg = sprintf( "bad signature: 0x%08x", $signature );
  384.         if ( _isSeekable($fh) )
  385.         {
  386.             $errmsg .=
  387.               sprintf( " at offset %d", $fh->tell() - SIGNATURE_LENGTH );
  388.         }
  389.  
  390.         $status = _formatError("$errmsg in file $fileName");
  391.     }
  392.  
  393.     return ( $status, $signature );
  394. }
  395.  
  396. # Utility method to make and open a temp file.
  397. # Will create $temp_dir if it doesn't exist.
  398. # Returns file handle and name:
  399. #
  400. # my ($fh, $name) = Archive::Zip::tempFile();
  401. # my ($fh, $name) = Archive::Zip::tempFile('mytempdir');
  402. #
  403. BEGIN { $Archive::Zip::TempSequence = 0 }
  404.  
  405. sub tempFileName    # Archive::Zip
  406. {
  407.     my $temp_dir = shift;
  408.     $temp_dir = ( -d '/tmp' ? '/tmp' : $ENV{TMPDIR} || $ENV{TEMP} || '.' )
  409.       unless defined($temp_dir);
  410.     unless ( -d $temp_dir )
  411.     {
  412.         mkdir( $temp_dir, 0777 )
  413.           or die "Can't create temp directory $temp_dir\: $!\n";
  414.     }
  415.     my $base_name =
  416.       sprintf( "%d-%d.%d", $$, time(), $Archive::Zip::TempSequence++ );
  417.     return File::Spec->canonpath(
  418.         File::Spec->catpath( '', $temp_dir, $base_name ) );
  419. }
  420.  
  421. sub tempFile    # Archive::Zip
  422. {
  423.     my $full_name = tempFileName(@_);
  424.     my $fh = IO::File->new( $full_name, '+>' );
  425.     return defined($fh) ? ( $fh, $full_name ) : ();
  426. }
  427.  
  428. # Return the normalized directory name as used in a zip file (path
  429. # separators become slashes, etc.). 
  430. # Will translate internal slashes in path components (i.e. on Macs) to
  431. # underscores.  Discards volume names.
  432. # When $forceDir is set, returns paths with trailing slashes (or arrays
  433. # with trailing blank members).
  434. #
  435. # If third argument is a reference, returns volume information there.
  436. #
  437. # input         output
  438. # .                ('.')    '.'
  439. # ./a            ('a')    a
  440. # ./a/b            ('a','b')    a/b
  441. # ./a/b/        ('a','b')    a/b
  442. # a/b/            ('a','b')    a/b
  443. # /a/b/            ('','a','b')    /a/b
  444. # c:\a\b\c.doc    ('','a','b','c.doc')    /a/b/c.doc        # on Windoze
  445. # "i/o maps:whatever"    ('i_o maps', 'whatever')  "i_o maps/whatever"    # on Macs
  446. sub _asZipDirName    # Archive::Zip
  447. {
  448.     my $name      = shift;
  449.     my $forceDir  = shift;
  450.     my $volReturn = shift;
  451.     my ( $volume, $directories, $file ) =
  452.       File::Spec->splitpath( File::Spec->canonpath($name), $forceDir );
  453.     $$volReturn = $volume if ( ref($volReturn) );
  454.     my @dirs = map { $_ =~ s{/}{_}g; $_ } File::Spec->splitdir($directories);
  455.     if ( @dirs > 0 ) { pop (@dirs) unless $dirs[-1] }   # remove empty component
  456.     push ( @dirs, $file || '' );
  457.     return wantarray ? @dirs : join ( '/', @dirs );
  458. }
  459.  
  460. # Return an absolute local name for a zip name.
  461. # Assume a directory if zip name has trailing slash.
  462. # Takes an optional volume name in FS format (like 'a:').
  463. #
  464. sub _asLocalName    # Archive::Zip
  465. {
  466.     my $name   = shift;    # zip format
  467.     my $volume = shift;
  468.     $volume = '' unless defined($volume);    # local FS format
  469.  
  470.     my @paths = split ( /\//, $name );
  471.     my $filename  = pop (@paths);
  472.     my $localDirs = File::Spec->catdir(@paths);
  473.     my $localName = File::Spec->catpath( $volume, $localDirs, $filename );
  474.     $localName = File::Spec->rel2abs($localName) unless $volume;
  475.     return $localName;
  476. }
  477.  
  478. # ----------------------------------------------------------------------
  479. # class Archive::Zip::Archive (concrete)
  480. # Generic ZIP archive.
  481. # ----------------------------------------------------------------------
  482. package Archive::Zip::Archive;
  483. use File::Path;
  484. use File::Find();
  485. use File::Spec();
  486. use File::Copy();
  487. use File::Basename;
  488. use Cwd;
  489.  
  490. use vars qw( @ISA );
  491. @ISA = qw( Archive::Zip );
  492.  
  493. BEGIN
  494. {
  495.     use Archive::Zip qw( :CONSTANTS :ERROR_CODES :PKZIP_CONSTANTS
  496.       :UTILITY_METHODS );
  497. }
  498.  
  499. # Note that this returns undef on read errors, else new zip object.
  500.  
  501. sub new    # Archive::Zip::Archive
  502. {
  503.     my $class = shift;
  504.     my $self = bless( {
  505.           'diskNumber'                            => 0,
  506.           'diskNumberWithStartOfCentralDirectory' => 0,
  507.           'numberOfCentralDirectoriesOnThisDisk'  => 0,   # shld be # of members
  508.           'numberOfCentralDirectories'            => 0,   # shld be # of members
  509.           'centralDirectorySize' => 0,    # must re-compute on write
  510.           'centralDirectoryOffsetWRTStartingDiskNumber' => 0,  # must re-compute
  511.           'writeEOCDOffset'             => 0,
  512.           'writeCentralDirectoryOffset' => 0,
  513.           'zipfileComment'              => '',
  514.           'eocdOffset'                  => 0,
  515.           'fileName'                    => ''
  516.       },
  517.       $class
  518.     );
  519.     $self->{'members'} = [];
  520.     if (@_)
  521.     {
  522.         my $status = $self->read(@_);
  523.         return $status == AZ_OK ? $self : undef;
  524.     }
  525.     return $self;
  526. }
  527.  
  528. sub members    # Archive::Zip::Archive
  529. {
  530.     @{ shift->{'members'} };
  531. }
  532.  
  533. sub numberOfMembers    # Archive::Zip::Archive
  534. {
  535.     scalar( shift->members() );
  536. }
  537.  
  538. sub memberNames    # Archive::Zip::Archive
  539. {
  540.     my $self = shift;
  541.     return map { $_->fileName() } $self->members();
  542. }
  543.  
  544. # return ref to member with given name or undef
  545. sub memberNamed    # Archive::Zip::Archive
  546. {
  547.     my ( $self, $fileName ) = @_;
  548.     foreach my $member ( $self->members() )
  549.     {
  550.         return $member if $member->fileName() eq $fileName;
  551.     }
  552.     return undef;
  553. }
  554.  
  555. sub membersMatching    # Archive::Zip::Archive
  556. {
  557.     my ( $self, $pattern ) = @_;
  558.     return grep { $_->fileName() =~ /$pattern/ } $self->members();
  559. }
  560.  
  561. sub diskNumber    # Archive::Zip::Archive
  562. {
  563.     shift->{'diskNumber'};
  564. }
  565.  
  566. sub diskNumberWithStartOfCentralDirectory    # Archive::Zip::Archive
  567. {
  568.     shift->{'diskNumberWithStartOfCentralDirectory'};
  569. }
  570.  
  571. sub numberOfCentralDirectoriesOnThisDisk    # Archive::Zip::Archive
  572. {
  573.     shift->{'numberOfCentralDirectoriesOnThisDisk'};
  574. }
  575.  
  576. sub numberOfCentralDirectories    # Archive::Zip::Archive
  577. {
  578.     shift->{'numberOfCentralDirectories'};
  579. }
  580.  
  581. sub centralDirectorySize    # Archive::Zip::Archive
  582. {
  583.     shift->{'centralDirectorySize'};
  584. }
  585.  
  586. sub centralDirectoryOffsetWRTStartingDiskNumber    # Archive::Zip::Archive
  587. {
  588.     shift->{'centralDirectoryOffsetWRTStartingDiskNumber'};
  589. }
  590.  
  591. sub zipfileComment    # Archive::Zip::Archive
  592. {
  593.     my $self    = shift;
  594.     my $comment = $self->{'zipfileComment'};
  595.     if (@_)
  596.     {
  597.         $self->{'zipfileComment'} = shift;
  598.     }
  599.     return $comment;
  600. }
  601.  
  602. sub eocdOffset    # Archive::Zip::Archive
  603. {
  604.     shift->{'eocdOffset'};
  605. }
  606.  
  607. # Return the name of the file last read.
  608. sub fileName    # Archive::Zip::Archive
  609. {
  610.     shift->{'fileName'};
  611. }
  612.  
  613. sub removeMember    # Archive::Zip::Archive
  614. {
  615.     my ( $self, $member ) = @_;
  616.     $member = $self->memberNamed($member) unless ref($member);
  617.     return undef unless $member;
  618.     my @newMembers = grep { $_ != $member } $self->members();
  619.     $self->{'members'} = \@newMembers;
  620.     return $member;
  621. }
  622.  
  623. sub replaceMember    # Archive::Zip::Archive
  624. {
  625.     my ( $self, $oldMember, $newMember ) = @_;
  626.     $oldMember = $self->memberNamed($oldMember) unless ref($oldMember);
  627.     return undef unless $oldMember;
  628.     return undef unless $newMember;
  629.     my @newMembers =
  630.       map { ( $_ == $oldMember ) ? $newMember : $_ } $self->members();
  631.     $self->{'members'} = \@newMembers;
  632.     return $oldMember;
  633. }
  634.  
  635. sub extractMember    # Archive::Zip::Archive
  636. {
  637.     my $self   = shift;
  638.     my $member = shift;
  639.     $member = $self->memberNamed($member) unless ref($member);
  640.     return _error('member not found') unless $member;
  641.     my $name = shift;    # local FS name if given
  642.     my ( $volumeName, $dirName, $fileName );
  643.     if ( defined($name) )
  644.     {
  645.         ( $volumeName, $dirName, $fileName ) = File::Spec->splitpath($name);
  646.         $dirName = File::Spec->catpath( $volumeName, $dirName, '' );
  647.     }
  648.     else
  649.     {
  650.         $name = $member->fileName();
  651.         ( $dirName = $name ) =~ s{[^/]*$}{};
  652.         $dirName = Archive::Zip::_asLocalName($dirName);
  653.         $name    = Archive::Zip::_asLocalName($name);
  654.     }
  655.     if ( $dirName && !-d $dirName )
  656.     {
  657.         mkpath($dirName);
  658.         return _ioError("can't create dir $dirName") if ( !-d $dirName );
  659.     }
  660.     return $member->extractToFileNamed( $name, @_ );
  661. }
  662.  
  663. sub extractMemberWithoutPaths    # Archive::Zip::Archive
  664. {
  665.     my $self   = shift;
  666.     my $member = shift;
  667.     $member = $self->memberNamed($member) unless ref($member);
  668.     return _error('member not found') unless $member;
  669.     return AZ_OK if $member->isDirectory();
  670.     my $name = shift;
  671.     unless ($name)
  672.     {
  673.         $name = $member->fileName();
  674.         $name =~ s{.*/}{};    # strip off directories, if any
  675.         $name = Archive::Zip::_asLocalName($name);
  676.     }
  677.     return $member->extractToFileNamed( $name, @_ );
  678. }
  679.  
  680. sub addMember    # Archive::Zip::Archive
  681. {
  682.     my ( $self, $newMember ) = @_;
  683.     push ( @{ $self->{'members'} }, $newMember ) if $newMember;
  684.     return $newMember;
  685. }
  686.  
  687. sub addFile    # Archive::Zip::Archive
  688. {
  689.     my $self      = shift;
  690.     my $fileName  = shift;
  691.     my $newName   = shift;
  692.     my $newMember = $self->ZIPMEMBERCLASS->newFromFile( $fileName, $newName );
  693.     $self->addMember($newMember) if defined($newMember);
  694.     return $newMember;
  695. }
  696.  
  697. sub addString    # Archive::Zip::Archive
  698. {
  699.     my $self      = shift;
  700.     my $newMember = $self->ZIPMEMBERCLASS->newFromString(@_);
  701.     return $self->addMember($newMember);
  702. }
  703.  
  704. sub addDirectory    # Archive::Zip::Archive
  705. {
  706.     my ( $self, $name, $newName ) = @_;
  707.     my $newMember = $self->ZIPMEMBERCLASS->newDirectoryNamed( $name, $newName );
  708.     $self->addMember($newMember);
  709.     return $newMember;
  710. }
  711.  
  712. # add either a file or a directory.
  713.  
  714. sub addFileOrDirectory
  715. {
  716.     my ( $self, $name, $newName ) = @_;
  717.     if ( -f $name )
  718.     {
  719.         ( $newName =~ s{/$}{} ) if $newName;
  720.         return $self->addFile( $name, $newName );
  721.     }
  722.     elsif ( -d $name )
  723.     {
  724.         ( $newName =~ s{[^/]$}{&/} ) if $newName;
  725.         return $self->addDirectory( $name, $newName );
  726.     }
  727.     else
  728.     {
  729.         return _error("$name is neither a file nor a directory");
  730.     }
  731. }
  732.  
  733. sub contents    # Archive::Zip::Archive
  734. {
  735.     my ( $self, $member, $newContents ) = @_;
  736.     $member = $self->memberNamed($member) unless ref($member);
  737.     return undef unless $member;
  738.     return $member->contents($newContents);
  739. }
  740.  
  741. sub writeToFileNamed    # Archive::Zip::Archive
  742. {
  743.     my $self     = shift;
  744.     my $fileName = shift;    # local FS format
  745.     foreach my $member ( $self->members() )
  746.     {
  747.         if ( $member->_usesFileNamed($fileName) )
  748.         {
  749.             return _error( "$fileName is needed by member "
  750.                 . $member->fileName()
  751.                 . "; consider using overwrite() or overwriteAs() instead." );
  752.         }
  753.     }
  754.     my ( $status, $fh ) = _newFileHandle( $fileName, 'w' );
  755.     return _ioError("Can't open $fileName for write") unless $status;
  756.     my $retval = $self->writeToFileHandle( $fh, 1 );
  757.     $fh->close();
  758.     return $retval;
  759. }
  760.  
  761. # It is possible to write data to the FH before calling this,
  762. # perhaps to make a self-extracting archive.
  763. sub writeToFileHandle    # Archive::Zip::Archive
  764. {
  765.     my $self         = shift;
  766.     my $fh           = shift;
  767.     my $fhIsSeekable = @_ ? shift: _isSeekable($fh);
  768.     _binmode($fh);
  769.  
  770.     # Find out where the current position is.
  771.     my $offset = $fhIsSeekable ? $fh->tell() : 0;
  772.     $offset = 0 if $offset < 0;
  773.  
  774.     foreach my $member ( $self->members() )
  775.     {
  776.         my $retval = $member->_writeToFileHandle( $fh, $fhIsSeekable, $offset );
  777.         $member->endRead();
  778.         return $retval if $retval != AZ_OK;
  779.         $offset += $member->_localHeaderSize() + $member->_writeOffset();
  780.         $offset += $member->hasDataDescriptor() ? DATA_DESCRIPTOR_LENGTH: 0;
  781.  
  782.         # changed this so it reflects the last successful position
  783.         $self->{'writeCentralDirectoryOffset'} = $offset;
  784.     }
  785.     return $self->writeCentralDirectory($fh);
  786. }
  787.  
  788. # Write zip back to the original file,
  789. # as safely as possible.
  790. # Returns AZ_OK if successful.
  791. sub overwrite    # Archive::Zip::Archive
  792. {
  793.     my $self = shift;
  794.     return $self->overwriteAs( $self->{'fileName'} );
  795. }
  796.  
  797. # Write zip to the specified file,
  798. # as safely as possible.
  799. # Returns AZ_OK if successful.
  800. sub overwriteAs    # Archive::Zip::Archive
  801. {
  802.     my $self    = shift;
  803.     my $zipName = shift;
  804.     return _error("no filename in overwriteAs()") unless defined($zipName);
  805.  
  806.     my ( $fh, $tempName ) = Archive::Zip::tempFile();
  807.     return _error( "Can't open temp file", $! ) unless $fh;
  808.     ( my $backupName = $zipName ) =~ s{\.[^.]*$}{.zbk};
  809.     my $status;
  810.  
  811.     if ( ( $status = $self->writeToFileHandle($fh) ) == AZ_OK )
  812.     {
  813.         my $err;
  814.         $fh->close();
  815.  
  816.         # rename the zip
  817.         if ( -f $zipName && !rename( $zipName, $backupName ) )
  818.         {
  819.             $err = $!;
  820.             unlink($tempName);
  821.             return _error( "Can't rename $zipName as $backupName", $err );
  822.         }
  823.  
  824.         # move the temp to the original name (possibly copying)
  825.         unless ( File::Copy::move( $tempName, $zipName ) )
  826.         {
  827.             $err = $!;
  828.             rename( $backupName, $zipName );
  829.             unlink($tempName);
  830.             return _error( "Can't move $tempName to $zipName", $err );
  831.         }
  832.  
  833.         # unlink the backup
  834.         if ( -f $backupName && !unlink($backupName) )
  835.         {
  836.             $err = $!;
  837.             return _error( "Can't unlink $backupName", $err );
  838.         }
  839.         return AZ_OK;
  840.     }
  841.     else
  842.     {
  843.         $fh->close();
  844.         unlink($tempName);
  845.         _printError("Can't write to $tempName");
  846.         return $status;
  847.     }
  848. }
  849.  
  850. # Used only during writing
  851. sub _writeCentralDirectoryOffset    # Archive::Zip::Archive
  852. {
  853.     shift->{'writeCentralDirectoryOffset'};
  854. }
  855.  
  856. sub _writeEOCDOffset    # Archive::Zip::Archive
  857. {
  858.     shift->{'writeEOCDOffset'};
  859. }
  860.  
  861. # Expects to have _writeEOCDOffset() set
  862. sub _writeEndOfCentralDirectory    # Archive::Zip::Archive
  863. {
  864.     my ( $self, $fh ) = @_;
  865.  
  866.     $fh->print(END_OF_CENTRAL_DIRECTORY_SIGNATURE_STRING)
  867.       or return _ioError('writing EOCD Signature');
  868.     my $zipfileCommentLength = length( $self->zipfileComment() );
  869.  
  870.     my $header = pack(
  871.         END_OF_CENTRAL_DIRECTORY_FORMAT,
  872.         0,                          # {'diskNumber'},
  873.         0,                          # {'diskNumberWithStartOfCentralDirectory'},
  874.         $self->numberOfMembers(),   # {'numberOfCentralDirectoriesOnThisDisk'},
  875.         $self->numberOfMembers(),   # {'numberOfCentralDirectories'},
  876.         $self->_writeEOCDOffset() - $self->_writeCentralDirectoryOffset(),
  877.         $self->_writeCentralDirectoryOffset(),
  878.         $zipfileCommentLength
  879.     );
  880.     $fh->print($header)
  881.       or return _ioError('writing EOCD header');
  882.     if ($zipfileCommentLength)
  883.     {
  884.         $fh->print( $self->zipfileComment() )
  885.           or return _ioError('writing zipfile comment');
  886.     }
  887.     return AZ_OK;
  888. }
  889.  
  890. # $offset can be specified to truncate a zip file.
  891. sub writeCentralDirectory    # Archive::Zip::Archive
  892. {
  893.     my ( $self, $fh, $offset ) = @_;
  894.  
  895.     if ( defined($offset) )
  896.     {
  897.         $self->{'writeCentralDirectoryOffset'} = $offset;
  898.         $fh->seek( $offset, IO::Seekable::SEEK_SET )
  899.           or return _ioError('seeking to write central directory');
  900.     }
  901.     else
  902.     {
  903.         $offset = $self->_writeCentralDirectoryOffset();
  904.     }
  905.  
  906.     foreach my $member ( $self->members() )
  907.     {
  908.         my $status = $member->_writeCentralDirectoryFileHeader($fh);
  909.         return $status if $status != AZ_OK;
  910.         $offset += $member->_centralDirectoryHeaderSize();
  911.         $self->{'writeEOCDOffset'} = $offset;
  912.     }
  913.     return $self->_writeEndOfCentralDirectory($fh);
  914. }
  915.  
  916. sub read    # Archive::Zip::Archive
  917. {
  918.     my $self     = shift;
  919.     my $fileName = shift;
  920.     return _error('No filename given') unless $fileName;
  921.     my ( $status, $fh ) = _newFileHandle( $fileName, 'r' );
  922.     return _ioError("opening $fileName for read") unless $status;
  923.  
  924.     $status = $self->readFromFileHandle( $fh, $fileName );
  925.     return $status if $status != AZ_OK;
  926.  
  927.     $fh->close();
  928.     $self->{'fileName'} = $fileName;
  929.     return AZ_OK;
  930. }
  931.  
  932. sub readFromFileHandle    # Archive::Zip::Archive
  933. {
  934.     my $self     = shift;
  935.     my $fh       = shift;
  936.     my $fileName = shift;
  937.     $fileName = $fh unless defined($fileName);
  938.     return _error('No filehandle given')   unless $fh;
  939.     return _ioError('filehandle not open') unless $fh->opened();
  940.  
  941.     $fh->seek( 0, 0 );    # rewind the file
  942.     _binmode($fh);
  943.  
  944.     my $status = $self->_findEndOfCentralDirectory($fh);
  945.     return $status if $status != AZ_OK;
  946.  
  947.     my $eocdPosition = $fh->tell();
  948.  
  949.     $status = $self->_readEndOfCentralDirectory($fh);
  950.     return $status if $status != AZ_OK;
  951.  
  952.     $fh->seek( $eocdPosition - $self->centralDirectorySize(),
  953.         IO::Seekable::SEEK_SET )
  954.       or return _ioError("Can't seek $fileName");
  955.  
  956.     # Try to detect garbage at beginning of archives
  957.     # This should be 0
  958.     $self->{'eocdOffset'} = $eocdPosition - $self->centralDirectorySize() # here
  959.       - $self->centralDirectoryOffsetWRTStartingDiskNumber();
  960.  
  961.     for ( ; ; )
  962.     {
  963.         my $newMember =
  964.           $self->ZIPMEMBERCLASS->_newFromZipFile( $fh, $fileName,
  965.             $self->eocdOffset() );
  966.         my $signature;
  967.         ( $status, $signature ) = _readSignature( $fh, $fileName );
  968.         return $status if $status != AZ_OK;
  969.         last if $signature == END_OF_CENTRAL_DIRECTORY_SIGNATURE;
  970.         $status = $newMember->_readCentralDirectoryFileHeader();
  971.         return $status if $status != AZ_OK;
  972.         $status = $newMember->endRead();
  973.         return $status if $status != AZ_OK;
  974.         $newMember->_becomeDirectoryIfNecessary();
  975.         push ( @{ $self->{'members'} }, $newMember );
  976.     }
  977.  
  978.     $self->{'fileName'} = "$fh";
  979.     return AZ_OK;
  980. }
  981.  
  982. # Read EOCD, starting from position before signature.
  983. # Return AZ_OK on success.
  984. sub _readEndOfCentralDirectory    # Archive::Zip::Archive
  985. {
  986.     my $self = shift;
  987.     my $fh   = shift;
  988.  
  989.     # Skip past signature
  990.     $fh->seek( SIGNATURE_LENGTH, IO::Seekable::SEEK_CUR )
  991.       or return _ioError("Can't seek past EOCD signature");
  992.  
  993.     my $header = '';
  994.     my $bytesRead = $fh->read( $header, END_OF_CENTRAL_DIRECTORY_LENGTH );
  995.     if ( $bytesRead != END_OF_CENTRAL_DIRECTORY_LENGTH )
  996.     {
  997.         return _ioError("reading end of central directory");
  998.     }
  999.  
  1000.     my $zipfileCommentLength;
  1001.     ( $self->{'diskNumber'},
  1002.       $self->{'diskNumberWithStartOfCentralDirectory'},
  1003.       $self->{'numberOfCentralDirectoriesOnThisDisk'},
  1004.       $self->{'numberOfCentralDirectories'},
  1005.       $self->{'centralDirectorySize'},
  1006.       $self->{'centralDirectoryOffsetWRTStartingDiskNumber'},
  1007.       $zipfileCommentLength )
  1008.       = unpack( END_OF_CENTRAL_DIRECTORY_FORMAT, $header );
  1009.  
  1010.     if ($zipfileCommentLength)
  1011.     {
  1012.         my $zipfileComment = '';
  1013.         $bytesRead = $fh->read( $zipfileComment, $zipfileCommentLength );
  1014.         if ( $bytesRead != $zipfileCommentLength )
  1015.         {
  1016.             return _ioError("reading zipfile comment");
  1017.         }
  1018.         $self->{'zipfileComment'} = $zipfileComment;
  1019.     }
  1020.  
  1021.     return AZ_OK;
  1022. }
  1023.  
  1024. # Seek in my file to the end, then read backwards until we find the
  1025. # signature of the central directory record. Leave the file positioned right
  1026. # before the signature. Returns AZ_OK if success.
  1027. sub _findEndOfCentralDirectory    # Archive::Zip::Archive
  1028. {
  1029.     my $self = shift;
  1030.     my $fh   = shift;
  1031.     my $data = '';
  1032.     $fh->seek( 0, IO::Seekable::SEEK_END )
  1033.       or return _ioError("seeking to end");
  1034.  
  1035.     my $fileLength = $fh->tell();
  1036.     if ( $fileLength < END_OF_CENTRAL_DIRECTORY_LENGTH + 4 )
  1037.     {
  1038.         return _formatError("file is too short");
  1039.     }
  1040.  
  1041.     my $seekOffset = 0;
  1042.     my $pos        = -1;
  1043.     for ( ; ; )
  1044.     {
  1045.         $seekOffset += 512;
  1046.         $seekOffset = $fileLength if ( $seekOffset > $fileLength );
  1047.         $fh->seek( -$seekOffset, IO::Seekable::SEEK_END )
  1048.           or return _ioError("seek failed");
  1049.         my $bytesRead = $fh->read( $data, $seekOffset );
  1050.         if ( $bytesRead != $seekOffset )
  1051.         {
  1052.             return _ioError("read failed");
  1053.         }
  1054.         $pos = rindex( $data, END_OF_CENTRAL_DIRECTORY_SIGNATURE_STRING );
  1055.         last
  1056.           if ( $pos >= 0
  1057.             or $seekOffset == $fileLength
  1058.             or $seekOffset >= $Archive::Zip::ChunkSize );
  1059.     }
  1060.  
  1061.     if ( $pos >= 0 )
  1062.     {
  1063.         $fh->seek( $pos - $seekOffset, IO::Seekable::SEEK_CUR )
  1064.           or return _ioError("seeking to EOCD");
  1065.         return AZ_OK;
  1066.     }
  1067.     else
  1068.     {
  1069.         return _formatError("can't find EOCD signature");
  1070.     }
  1071. }
  1072.  
  1073. sub addTree    # Archive::Zip::Archive
  1074. {
  1075.     my $self = shift;
  1076.     my $root = shift or return _error("root arg missing in call to addTree()");
  1077.     my $dest = shift;
  1078.     $dest = '' unless defined($dest);
  1079.     my $pred = shift || sub { -r };
  1080.     my @files;
  1081.     my $startDir = cwd();
  1082.  
  1083.     # This avoids chdir'ing in Find, in a way compatible with older
  1084.     # versions of File::Find.
  1085.     my $wanted = sub {
  1086.         local $main::_ = $File::Find::name;
  1087.         my $dir = $File::Find::dir;
  1088.         chdir($startDir);
  1089.         push ( @files, $File::Find::name ) if (&$pred);
  1090.         chdir($dir);
  1091.     };
  1092.  
  1093.     File::Find::find( $wanted, $root );
  1094.  
  1095.     my $rootZipName = _asZipDirName( $root, 1 );    # with trailing slash
  1096.     my $pattern = $rootZipName eq './' ? '^' : "^\Q$rootZipName\E";
  1097.  
  1098.     $dest = _asZipDirName( $dest, 1 );              # with trailing slash
  1099.  
  1100.     foreach my $fileName (@files)
  1101.     {
  1102.         my $isDir = -d $fileName;
  1103.  
  1104.         # normalize, remove leading ./
  1105.         my $archiveName = _asZipDirName( $fileName, $isDir );
  1106.         if ( $archiveName eq $rootZipName ) { $archiveName = $dest }
  1107.         else { $archiveName =~ s{$pattern}{$dest} }
  1108.         next if $archiveName =~ m{^\.?/?$};    # skip current dir
  1109.         my $member =
  1110.           $isDir 
  1111.           ? $self->addDirectory( $fileName, $archiveName )
  1112.           : $self->addFile( $fileName, $archiveName );
  1113.         return _error("add $fileName failed in addTree()") if !$member;
  1114.     }
  1115.     return AZ_OK;
  1116. }
  1117.  
  1118. sub addTreeMatching    # Archive::Zip::Archive
  1119. {
  1120.     my $self = shift;
  1121.     my $root = shift
  1122.       or return _error("root arg missing in call to addTreeMatching()");
  1123.     my $dest = shift;
  1124.     $dest = '' unless defined($dest);
  1125.     my $pattern = shift
  1126.       or return _error("pattern missing in call to addTreeMatching()");
  1127.     my $pred    = shift;
  1128.     my $matcher =
  1129.       $pred ? sub { m{$pattern} && &$pred } : sub { m{$pattern} && -r };
  1130.     return $self->addTree( $root, $dest, $matcher );
  1131. }
  1132.  
  1133. # $zip->extractTree( $root, $dest [, $volume] );
  1134. #
  1135. # $root and $dest are Unix-style.
  1136. # $volume is in local FS format.
  1137. #
  1138. sub extractTree    # Archive::Zip::Archive
  1139. {
  1140.     my $self = shift;
  1141.     my $root = shift;    # Zip format
  1142.     $root = '' unless defined($root);
  1143.     my $dest = shift;    # Zip format
  1144.     $dest = './' unless defined($dest);
  1145.     my $volume  = shift;                              # optional
  1146.     my $pattern = "^\Q$root";
  1147.     my @members = $self->membersMatching($pattern);
  1148.  
  1149.     foreach my $member (@members)
  1150.     {
  1151.         my $fileName = $member->fileName();    # in Unix format
  1152.         $fileName =~ s{$pattern}{$dest};       # in Unix format
  1153.                                                # convert to platform format:
  1154.         $fileName = Archive::Zip::_asLocalName( $fileName, $volume );
  1155.         my $status = $member->extractToFileNamed($fileName);
  1156.         return $status if $status != AZ_OK;
  1157.     }
  1158.     return AZ_OK;
  1159. }
  1160.  
  1161. # $zip->updateMember( $memberOrName, $fileName );
  1162. # Returns (possibly updated) member, if any; undef on errors.
  1163.  
  1164. sub updateMember    # Archive::Zip::Archive
  1165. {
  1166.     my $self      = shift;
  1167.     my $oldMember = shift;
  1168.     my $fileName  = shift;
  1169.  
  1170.     if ( !defined($fileName) )
  1171.     {
  1172.         _error("updateMember(): missing fileName argument");
  1173.         return undef;
  1174.     }
  1175.  
  1176.     my @newStat = stat($fileName);
  1177.     if ( !@newStat )
  1178.     {
  1179.         _ioError("Can't stat $fileName");
  1180.         return undef;
  1181.     }
  1182.  
  1183.     my $isDir = -d _;
  1184.  
  1185.     my $memberName;
  1186.  
  1187.     if ( ref($oldMember) )
  1188.     {
  1189.         $memberName = $oldMember->fileName();
  1190.     }
  1191.     else
  1192.     {
  1193.         $oldMember = $self->memberNamed( $memberName = $oldMember )
  1194.           || $self->memberNamed( $memberName =
  1195.             _asZipDirName( $oldMember, $isDir ) );
  1196.     }
  1197.  
  1198.     unless ( defined($oldMember)
  1199.         && $oldMember->lastModTime() == $newStat[9]
  1200.         && $oldMember->isDirectory() == $isDir
  1201.         && ( $isDir || ( $oldMember->uncompressedSize() == $newStat[7] ) ) )
  1202.     {
  1203.  
  1204.         # create the new member
  1205.         my $newMember = $isDir
  1206.           ? $self->ZIPMEMBERCLASS->newDirectoryNamed( $fileName, $memberName )
  1207.           : $self->ZIPMEMBERCLASS->newFromFile( $fileName, $memberName );
  1208.  
  1209.         unless ( defined($newMember) )
  1210.         {
  1211.             _error("creation of member $fileName failed in updateMember()");
  1212.             return undef;
  1213.         }
  1214.  
  1215.         # replace old member or append new one
  1216.         if ( defined($oldMember) )
  1217.         {
  1218.             $self->replaceMember( $oldMember, $newMember );
  1219.         }
  1220.         else { $self->addMember($newMember); }
  1221.  
  1222.         return $newMember;
  1223.     }
  1224.  
  1225.     return $oldMember;
  1226. }
  1227.  
  1228. # $zip->updateTree( $root, [ $dest, [ $pred [, $mirror]]] );
  1229. #
  1230. # This takes the same arguments as addTree, but first checks to see
  1231. # whether the file or directory already exists in the zip file.
  1232. #
  1233. # If the fourth argument $mirror is true, then delete all my members
  1234. # if corresponding files weren't found.
  1235.  
  1236. sub updateTree    # Archive::Zip::Archive
  1237. {
  1238.     my $self = shift;
  1239.     my $root = shift
  1240.       or return _error("root arg missing in call to updateTree()");
  1241.     my $dest = shift;
  1242.     $dest = '' unless defined($dest);
  1243.     $dest = _asZipDirName( $dest, 1 );
  1244.     my $pred = shift || sub { -r };
  1245.     my $mirror = shift;
  1246.  
  1247.     my $rootZipName = _asZipDirName( $root, 1 );    # with trailing slash
  1248.     my $pattern = $rootZipName eq './' ? '^' : "^\Q$rootZipName\E";
  1249.  
  1250.     my $startDir = cwd();
  1251.     my @files;
  1252.  
  1253.     # This avoids chdir'ing in Find, in a way compatible with older
  1254.     # versions of File::Find.
  1255.     my $wanted = sub {
  1256.         local $main::_ = $File::Find::name;
  1257.         my $dir = $File::Find::dir;
  1258.         chdir($startDir);
  1259.         push ( @files, $File::Find::name ) if (&$pred);
  1260.         chdir($dir);
  1261.     };
  1262.  
  1263.     File::Find::find( $wanted, $root );
  1264.  
  1265.     # Now @files has all the files that I could potentially be adding to
  1266.     # the zip. Only add the ones that are necessary.
  1267.     # For each file (updated or not), add its member name to @done.
  1268.     my %done;
  1269.     foreach my $fileName (@files)
  1270.     {
  1271.         my @newStat = stat($fileName);
  1272.         my $isDir   = -d _;
  1273.  
  1274.         # normalize, remove leading ./
  1275.         my $memberName = _asZipDirName( $fileName, $isDir );
  1276.         if ( $memberName eq $rootZipName ) { $memberName = $dest }
  1277.         else { $memberName =~ s{$pattern}{$dest} }
  1278.         next if $memberName =~ m{^\.?/?$};    # skip current dir
  1279.  
  1280.         $done{$memberName} = 1;
  1281.         my $changedMember = $self->updateMember( $memberName, $fileName );
  1282.         return _error("updateTree failed to update $fileName")
  1283.           unless ref($changedMember);
  1284.     }
  1285.  
  1286.     # @done now has the archive names corresponding to all the found files.
  1287.     # If we're mirroring, delete all those members that aren't in @done.
  1288.     if ($mirror)
  1289.     {
  1290.         foreach my $member ( $self->members() )
  1291.         {
  1292.             $self->removeMember($member)
  1293.               unless $done{ $member->fileName() };
  1294.         }
  1295.     }
  1296.  
  1297.     return AZ_OK;
  1298. }
  1299.  
  1300. # ----------------------------------------------------------------------
  1301. # class Archive::Zip::Member
  1302. # A generic member of an archive ( abstract )
  1303. # ----------------------------------------------------------------------
  1304. package Archive::Zip::Member;
  1305. use vars qw( @ISA );
  1306. @ISA = qw ( Archive::Zip );
  1307.  
  1308. BEGIN
  1309. {
  1310.     use Archive::Zip qw( :CONSTANTS :MISC_CONSTANTS :ERROR_CODES
  1311.       :PKZIP_CONSTANTS :UTILITY_METHODS );
  1312. }
  1313.  
  1314. use Time::Local();
  1315. use Compress::Zlib qw( Z_OK Z_STREAM_END MAX_WBITS );
  1316. use File::Path;
  1317. use File::Basename;
  1318.  
  1319. use constant ZIPFILEMEMBERCLASS   => 'Archive::Zip::ZipFileMember';
  1320. use constant NEWFILEMEMBERCLASS   => 'Archive::Zip::NewFileMember';
  1321. use constant STRINGMEMBERCLASS    => 'Archive::Zip::StringMember';
  1322. use constant DIRECTORYMEMBERCLASS => 'Archive::Zip::DirectoryMember';
  1323.  
  1324. # Unix perms for default creation of files/dirs.
  1325. use constant DEFAULT_DIRECTORY_PERMISSIONS => 040755;
  1326. use constant DEFAULT_FILE_PERMISSIONS      => 0100666;
  1327. use constant DIRECTORY_ATTRIB              => 040000;
  1328. use constant FILE_ATTRIB                   => 0100000;
  1329.  
  1330. # Returns self if successful, else undef
  1331. # Assumes that fh is positioned at beginning of central directory file header.
  1332. # Leaves fh positioned immediately after file header or EOCD signature.
  1333. sub _newFromZipFile    # Archive::Zip::Member
  1334. {
  1335.     my $class = shift;
  1336.     my $self  = $class->ZIPFILEMEMBERCLASS->_newFromZipFile(@_);
  1337.     return $self;
  1338. }
  1339.  
  1340. sub newFromString    # Archive::Zip::Member
  1341. {
  1342.     my $class = shift;
  1343.     my $self  = $class->STRINGMEMBERCLASS->_newFromString(@_);
  1344.     return $self;
  1345. }
  1346.  
  1347. sub newFromFile    # Archive::Zip::Member
  1348. {
  1349.     my $class = shift;
  1350.     my $self  = $class->NEWFILEMEMBERCLASS->_newFromFileNamed(@_);
  1351.     return $self;
  1352. }
  1353.  
  1354. sub newDirectoryNamed    # Archive::Zip::Member
  1355. {
  1356.     my $class = shift;
  1357.     my $self  = $class->DIRECTORYMEMBERCLASS->_newNamed(@_);
  1358.     return $self;
  1359. }
  1360.  
  1361. sub new    # Archive::Zip::Member
  1362. {
  1363.     my $class = shift;
  1364.     my $self  = {
  1365.         'lastModFileDateTime'      => 0,
  1366.         'fileAttributeFormat'      => FA_UNIX,
  1367.         'versionMadeBy'            => 20,
  1368.         'versionNeededToExtract'   => 20,
  1369.         'bitFlag'                  => 0,
  1370.         'compressionMethod'        => COMPRESSION_STORED,
  1371.         'desiredCompressionMethod' => COMPRESSION_STORED,
  1372.         'desiredCompressionLevel'  => COMPRESSION_LEVEL_NONE,
  1373.         'internalFileAttributes'   => 0,
  1374.         'externalFileAttributes'   => 0,                        # set later
  1375.         'fileName'                 => '',
  1376.         'cdExtraField'             => '',
  1377.         'localExtraField'          => '',
  1378.         'fileComment'              => '',
  1379.         'crc32'                    => 0,
  1380.         'compressedSize'           => 0,
  1381.         'uncompressedSize'         => 0,
  1382.         @_
  1383.     };
  1384.     bless( $self, $class );
  1385.     $self->unixFileAttributes( $self->DEFAULT_FILE_PERMISSIONS );
  1386.     return $self;
  1387. }
  1388.  
  1389. sub _becomeDirectoryIfNecessary    # Archive::Zip::Member
  1390. {
  1391.     my $self = shift;
  1392.     $self->_become(DIRECTORYMEMBERCLASS)
  1393.       if $self->isDirectory();
  1394.     return $self;
  1395. }
  1396.  
  1397. # Morph into given class (do whatever cleanup I need to do)
  1398. sub _become    # Archive::Zip::Member
  1399. {
  1400.     return bless( $_[0], $_[1] );
  1401. }
  1402.  
  1403. sub versionMadeBy    # Archive::Zip::Member
  1404. {
  1405.     shift->{'versionMadeBy'};
  1406. }
  1407.  
  1408. sub fileAttributeFormat    # Archive::Zip::Member
  1409. {
  1410.     ( $#_ > 0 ) 
  1411.       ? ( $_[0]->{'fileAttributeFormat'} = $_[1] )
  1412.       : $_[0]->{'fileAttributeFormat'};
  1413. }
  1414.  
  1415. sub versionNeededToExtract    # Archive::Zip::Member
  1416. {
  1417.     shift->{'versionNeededToExtract'};
  1418. }
  1419.  
  1420. sub bitFlag    # Archive::Zip::Member
  1421. {
  1422.     shift->{'bitFlag'};
  1423. }
  1424.  
  1425. sub compressionMethod    # Archive::Zip::Member
  1426. {
  1427.     shift->{'compressionMethod'};
  1428. }
  1429.  
  1430. sub desiredCompressionMethod    # Archive::Zip::Member
  1431. {
  1432.     my $self                        = shift;
  1433.     my $newDesiredCompressionMethod = shift;
  1434.     my $oldDesiredCompressionMethod = $self->{'desiredCompressionMethod'};
  1435.     if ( defined($newDesiredCompressionMethod) )
  1436.     {
  1437.         $self->{'desiredCompressionMethod'} = $newDesiredCompressionMethod;
  1438.         if ( $newDesiredCompressionMethod == COMPRESSION_STORED )
  1439.         {
  1440.             $self->{'desiredCompressionLevel'} = 0;
  1441.         }
  1442.         elsif ( $oldDesiredCompressionMethod == COMPRESSION_STORED )
  1443.         {
  1444.             $self->{'desiredCompressionLevel'} = COMPRESSION_LEVEL_DEFAULT;
  1445.         }
  1446.     }
  1447.     return $oldDesiredCompressionMethod;
  1448. }
  1449.  
  1450. sub desiredCompressionLevel    # Archive::Zip::Member
  1451. {
  1452.     my $self                       = shift;
  1453.     my $newDesiredCompressionLevel = shift;
  1454.     my $oldDesiredCompressionLevel = $self->{'desiredCompressionLevel'};
  1455.     if ( defined($newDesiredCompressionLevel) )
  1456.     {
  1457.         $self->{'desiredCompressionLevel'}  = $newDesiredCompressionLevel;
  1458.         $self->{'desiredCompressionMethod'} =
  1459.           ( $newDesiredCompressionLevel 
  1460.           ? COMPRESSION_DEFLATED
  1461.           : COMPRESSION_STORED );
  1462.     }
  1463.     return $oldDesiredCompressionLevel;
  1464. }
  1465.  
  1466. sub fileName    # Archive::Zip::Member
  1467. {
  1468.     my $self    = shift;
  1469.     my $newName = shift;
  1470.     if ($newName)
  1471.     {
  1472.         $newName =~ s{[\\/]+}{/}g;    # deal with dos/windoze problems
  1473.         $self->{'fileName'} = $newName;
  1474.     }
  1475.     return $self->{'fileName'};
  1476. }
  1477.  
  1478. sub lastModFileDateTime    # Archive::Zip::Member
  1479. {
  1480.     my $modTime = shift->{'lastModFileDateTime'};
  1481.     $modTime =~ m/^(\d+)$/;    # untaint
  1482.     return $1;
  1483. }
  1484.  
  1485. sub lastModTime    # Archive::Zip::Member
  1486. {
  1487.     my $self = shift;
  1488.     return _dosToUnixTime( $self->lastModFileDateTime() );
  1489. }
  1490.  
  1491. sub setLastModFileDateTimeFromUnix    # Archive::Zip::Member
  1492. {
  1493.     my $self   = shift;
  1494.     my $time_t = shift;
  1495.     $self->{'lastModFileDateTime'} = _unixToDosTime($time_t);
  1496. }
  1497.  
  1498. # DOS date/time format
  1499. # 0-4 (5) Second divided by 2
  1500. # 5-10 (6) Minute (0-59)
  1501. # 11-15 (5) Hour (0-23 on a 24-hour clock)
  1502. # 16-20 (5) Day of the month (1-31)
  1503. # 21-24 (4) Month (1 = January, 2 = February, etc.)
  1504. # 25-31 (7) Year offset from 1980 (add 1980 to get actual year)
  1505.  
  1506. # Convert DOS date/time format to unix time_t format
  1507. # NOT AN OBJECT METHOD!
  1508. sub _dosToUnixTime    # Archive::Zip::Member
  1509. {
  1510.     my $dt = shift;
  1511.     return time() unless defined($dt);
  1512.  
  1513.     my $year = ( ( $dt >> 25 ) & 0x7f ) + 80;
  1514.     my $mon  = ( ( $dt >> 21 ) & 0x0f ) - 1;
  1515.     my $mday = ( ( $dt >> 16 ) & 0x1f );
  1516.  
  1517.     my $hour = ( ( $dt >> 11 ) & 0x1f );
  1518.     my $min  = ( ( $dt >> 5 ) & 0x3f );
  1519.     my $sec  = ( ( $dt << 1 ) & 0x3e );
  1520.  
  1521.     # catch errors
  1522.     my $time_t =
  1523.       eval { Time::Local::timelocal( $sec, $min, $hour, $mday, $mon, $year ); };
  1524.     return time() if ($@);
  1525.     return $time_t;
  1526. }
  1527.  
  1528. sub internalFileAttributes    # Archive::Zip::Member
  1529. {
  1530.     shift->{'internalFileAttributes'};
  1531. }
  1532.  
  1533. sub externalFileAttributes    # Archive::Zip::Member
  1534. {
  1535.     shift->{'externalFileAttributes'};
  1536. }
  1537.  
  1538. # Convert UNIX permissions into proper value for zip file
  1539. # NOT A METHOD!
  1540. sub _mapPermissionsFromUnix    # Archive::Zip::Member
  1541. {
  1542.     my $perms = shift;
  1543.     return $perms << 16;
  1544.  
  1545.     # TODO: map MS-DOS perms too (RHSA?)
  1546. }
  1547.  
  1548. # Convert ZIP permissions into Unix ones
  1549. #
  1550. # This was taken from Info-ZIP group's portable UnZip
  1551. # zipfile-extraction program, version 5.50.
  1552. # http://www.info-zip.org/pub/infozip/ 
  1553. #
  1554. # See the mapattr() function in unix/unix.c
  1555. # See the attribute format constants in unzpriv.h
  1556. #
  1557. # XXX Note that there's one situation that isn't implemented
  1558. # yet that depends on the "extra field."
  1559. sub _mapPermissionsToUnix    # Archive::Zip::Member
  1560. {
  1561.     my $self = shift;
  1562.  
  1563.     my $format  = $self->{'fileAttributeFormat'};
  1564.     my $attribs = $self->{'externalFileAttributes'};
  1565.  
  1566.     my $mode = 0;
  1567.  
  1568.     if ( $format == FA_AMIGA )
  1569.     {
  1570.         $attribs = $attribs >> 17 & 7;                         # Amiga RWE bits
  1571.         $mode    = $attribs << 6 | $attribs << 3 | $attribs;
  1572.         return $mode;
  1573.     }
  1574.  
  1575.     if ( $format == FA_THEOS )
  1576.     {
  1577.         $attribs &= 0xF1FFFFFF;
  1578.         if ( ( $attribs & 0xF0000000 ) != 0x40000000 )
  1579.         {
  1580.             $attribs &= 0x01FFFFFF;    # not a dir, mask all ftype bits
  1581.         }
  1582.         else
  1583.         {
  1584.             $attribs &= 0x41FFFFFF;    # leave directory bit as set
  1585.         }
  1586.     }
  1587.  
  1588.     if ( $format == FA_UNIX
  1589.         || $format == FA_VAX_VMS
  1590.         || $format == FA_ACORN
  1591.         || $format == FA_ATARI_ST
  1592.         || $format == FA_BEOS
  1593.         || $format == FA_QDOS
  1594.         || $format == FA_TANDEM )
  1595.     {
  1596.         $mode = $attribs >> 16;
  1597.         return $mode if $mode != 0 or not $self->localExtraField;
  1598.  
  1599.         # warn("local extra field is: ", $self->localExtraField, "\n");
  1600.  
  1601.         # XXX This condition is not implemented
  1602.         # I'm just including the comments from the info-zip section for now.
  1603.  
  1604.         # Some (non-Info-ZIP) implementations of Zip for Unix and
  1605.         # VMS (and probably others ??) leave 0 in the upper 16-bit
  1606.         # part of the external_file_attributes field. Instead, they
  1607.         # store file permission attributes in some extra field.
  1608.         # As a work-around, we search for the presence of one of
  1609.         # these extra fields and fall back to the MSDOS compatible
  1610.         # part of external_file_attributes if one of the known
  1611.         # e.f. types has been detected.
  1612.         # Later, we might implement extraction of the permission
  1613.         # bits from the VMS extra field. But for now, the work-around
  1614.         # should be sufficient to provide "readable" extracted files.
  1615.         # (For ASI Unix e.f., an experimental remap from the e.f.
  1616.         # mode value IS already provided!)
  1617.     }
  1618.  
  1619.     # PKWARE's PKZip for Unix marks entries as FA_MSDOS, but stores the
  1620.     # Unix attributes in the upper 16 bits of the external attributes
  1621.     # field, just like Info-ZIP's Zip for Unix.  We try to use that
  1622.     # value, after a check for consistency with the MSDOS attribute
  1623.     # bits (see below).
  1624.     if ( $format == FA_MSDOS )
  1625.     {
  1626.         $mode = $attribs >> 16;
  1627.     }
  1628.  
  1629.     # FA_MSDOS, FA_OS2_HPFS, FA_WINDOWS_NTFS, FA_MACINTOSH, FA_TOPS20
  1630.     $attribs = !( $attribs & 1 ) << 1 | ( $attribs & 0x10 ) >> 4;
  1631.  
  1632.     # keep previous $mode setting when its "owner"
  1633.     # part appears to be consistent with DOS attribute flags!
  1634.     return $mode if ( $mode & 0700 ) == ( 0400 | $attribs << 6 );
  1635.     $mode = 0444 | $attribs << 6 | $attribs << 3 | $attribs;
  1636.     return $mode;
  1637. }
  1638.  
  1639. sub unixFileAttributes    # Archive::Zip::Member
  1640. {
  1641.     my $self     = shift;
  1642.     my $oldPerms = $self->_mapPermissionsToUnix();
  1643.     if (@_)
  1644.     {
  1645.         my $perms = shift;
  1646.         if ( $self->isDirectory() )
  1647.         {
  1648.             $perms &= ~FILE_ATTRIB;
  1649.             $perms |= DIRECTORY_ATTRIB;
  1650.         }
  1651.         else
  1652.         {
  1653.             $perms &= ~DIRECTORY_ATTRIB;
  1654.             $perms |= FILE_ATTRIB;
  1655.         }
  1656.         $self->{'externalFileAttributes'} = _mapPermissionsFromUnix($perms);
  1657.     }
  1658.     return $oldPerms;
  1659. }
  1660.  
  1661. sub localExtraField    # Archive::Zip::Member
  1662. {
  1663.     ( $#_ > 0 ) 
  1664.       ? ( $_[0]->{'localExtraField'} = $_[1] )
  1665.       : $_[0]->{'localExtraField'};
  1666. }
  1667.  
  1668. sub cdExtraField    # Archive::Zip::Member
  1669. {
  1670.     ( $#_ > 0 ) ? ( $_[0]->{'cdExtraField'} = $_[1] ) : $_[0]->{'cdExtraField'};
  1671. }
  1672.  
  1673. sub extraFields    # Archive::Zip::Member
  1674. {
  1675.     my $self = shift;
  1676.     return $self->localExtraField() . $self->cdExtraField();
  1677. }
  1678.  
  1679. sub fileComment    # Archive::Zip::Member
  1680. {
  1681.     ( $#_ > 0 ) ? ( $_[0]->{'fileComment'} = $_[1] ) : $_[0]->{'fileComment'};
  1682. }
  1683.  
  1684. sub hasDataDescriptor    # Archive::Zip::Member
  1685. {
  1686.     my $self = shift;
  1687.     if (@_)
  1688.     {
  1689.         my $shouldHave = shift;
  1690.         if ($shouldHave)
  1691.         {
  1692.             $self->{'bitFlag'} |= GPBF_HAS_DATA_DESCRIPTOR_MASK;
  1693.         }
  1694.         else
  1695.         {
  1696.             $self->{'bitFlag'} &= ~GPBF_HAS_DATA_DESCRIPTOR_MASK;
  1697.         }
  1698.     }
  1699.     return $self->{'bitFlag'} & GPBF_HAS_DATA_DESCRIPTOR_MASK;
  1700. }
  1701.  
  1702. sub crc32    # Archive::Zip::Member
  1703. {
  1704.     shift->{'crc32'};
  1705. }
  1706.  
  1707. sub crc32String    # Archive::Zip::Member
  1708. {
  1709.     sprintf( "%08x", shift->{'crc32'} );
  1710. }
  1711.  
  1712. sub compressedSize    # Archive::Zip::Member
  1713. {
  1714.     shift->{'compressedSize'};
  1715. }
  1716.  
  1717. sub uncompressedSize    # Archive::Zip::Member
  1718. {
  1719.     shift->{'uncompressedSize'};
  1720. }
  1721.  
  1722. sub isEncrypted    # Archive::Zip::Member
  1723. {
  1724.     shift->bitFlag() & GPBF_ENCRYPTED_MASK;
  1725. }
  1726.  
  1727. sub isTextFile    # Archive::Zip::Member
  1728. {
  1729.     my $self = shift;
  1730.     my $bit  = $self->internalFileAttributes() & IFA_TEXT_FILE_MASK;
  1731.     if (@_)
  1732.     {
  1733.         my $flag = shift;
  1734.         $self->{'internalFileAttributes'} &= ~IFA_TEXT_FILE_MASK;
  1735.         $self->{'internalFileAttributes'} |=
  1736.           ( $flag ? IFA_TEXT_FILE: IFA_BINARY_FILE );
  1737.     }
  1738.     return $bit == IFA_TEXT_FILE;
  1739. }
  1740.  
  1741. sub isBinaryFile    # Archive::Zip::Member
  1742. {
  1743.     my $self = shift;
  1744.     my $bit  = $self->internalFileAttributes() & IFA_TEXT_FILE_MASK;
  1745.     if (@_)
  1746.     {
  1747.         my $flag = shift;
  1748.         $self->{'internalFileAttributes'} &= ~IFA_TEXT_FILE_MASK;
  1749.         $self->{'internalFileAttributes'} |=
  1750.           ( $flag ? IFA_BINARY_FILE: IFA_TEXT_FILE );
  1751.     }
  1752.     return $bit == IFA_BINARY_FILE;
  1753. }
  1754.  
  1755. sub extractToFileNamed    # Archive::Zip::Member
  1756. {
  1757.     my $self = shift;
  1758.     my $name = shift;    # local FS name
  1759.     return _error("encryption unsupported") if $self->isEncrypted();
  1760.     mkpath( dirname($name) );    # croaks on error
  1761.     my ( $status, $fh ) = _newFileHandle( $name, 'w' );
  1762.     return _ioError("Can't open file $name for write") unless $status;
  1763.     my $retval = $self->extractToFileHandle($fh);
  1764.     $fh->close();
  1765.     utime( $self->lastModTime(), $self->lastModTime(), $name );
  1766.     return $retval;
  1767. }
  1768.  
  1769. sub isDirectory    # Archive::Zip::Member
  1770. {
  1771.     return 0;
  1772. }
  1773.  
  1774. sub externalFileName    # Archive::Zip::Member
  1775. {
  1776.     return undef;
  1777. }
  1778.  
  1779. # The following are used when copying data
  1780. sub _writeOffset    # Archive::Zip::Member
  1781. {
  1782.     shift->{'writeOffset'};
  1783. }
  1784.  
  1785. sub _readOffset    # Archive::Zip::Member
  1786. {
  1787.     shift->{'readOffset'};
  1788. }
  1789.  
  1790. sub writeLocalHeaderRelativeOffset    # Archive::Zip::Member
  1791. {
  1792.     shift->{'writeLocalHeaderRelativeOffset'};
  1793. }
  1794.  
  1795. sub wasWritten { shift->{'wasWritten'} }
  1796.  
  1797. sub _dataEnded    # Archive::Zip::Member
  1798. {
  1799.     shift->{'dataEnded'};
  1800. }
  1801.  
  1802. sub _readDataRemaining    # Archive::Zip::Member
  1803. {
  1804.     shift->{'readDataRemaining'};
  1805. }
  1806.  
  1807. sub _inflater    # Archive::Zip::Member
  1808. {
  1809.     shift->{'inflater'};
  1810. }
  1811.  
  1812. sub _deflater    # Archive::Zip::Member
  1813. {
  1814.     shift->{'deflater'};
  1815. }
  1816.  
  1817. # Return the total size of my local header
  1818. sub _localHeaderSize    # Archive::Zip::Member
  1819. {
  1820.     my $self = shift;
  1821.     return SIGNATURE_LENGTH + LOCAL_FILE_HEADER_LENGTH +
  1822.       length( $self->fileName() ) + length( $self->localExtraField() );
  1823. }
  1824.  
  1825. # Return the total size of my CD header
  1826. sub _centralDirectoryHeaderSize    # Archive::Zip::Member
  1827. {
  1828.     my $self = shift;
  1829.     return SIGNATURE_LENGTH + CENTRAL_DIRECTORY_FILE_HEADER_LENGTH +
  1830.       length( $self->fileName() ) + length( $self->cdExtraField() ) +
  1831.       length( $self->fileComment() );
  1832. }
  1833.  
  1834. # convert a unix time to DOS date/time
  1835. # NOT AN OBJECT METHOD!
  1836. sub _unixToDosTime    # Archive::Zip::Member
  1837. {
  1838.     my $time_t = shift;
  1839.     my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime($time_t);
  1840.     my $dt = 0;
  1841.     $dt += ( $sec >> 1 );
  1842.     $dt += ( $min << 5 );
  1843.     $dt += ( $hour << 11 );
  1844.     $dt += ( $mday << 16 );
  1845.     $dt += ( ( $mon + 1 ) << 21 );
  1846.     $dt += ( ( $year - 80 ) << 25 );
  1847.     return $dt;
  1848. }
  1849.  
  1850. # Write my local header to a file handle.
  1851. # Stores the offset to the start of the header in my
  1852. # writeLocalHeaderRelativeOffset member.
  1853. # Returns AZ_OK on success.
  1854. sub _writeLocalFileHeader    # Archive::Zip::Member
  1855. {
  1856.     my $self = shift;
  1857.     my $fh   = shift;
  1858.  
  1859.     my $signatureData = pack( SIGNATURE_FORMAT, LOCAL_FILE_HEADER_SIGNATURE );
  1860.     $fh->print($signatureData)
  1861.       or return _ioError("writing local header signature");
  1862.  
  1863.     my $header = pack(
  1864.         LOCAL_FILE_HEADER_FORMAT,
  1865.         $self->versionNeededToExtract(),
  1866.         $self->bitFlag(),
  1867.         $self->desiredCompressionMethod(),
  1868.         $self->lastModFileDateTime(),
  1869.         $self->crc32(),
  1870.         $self->compressedSize(),    # may need to be re-written later
  1871.         $self->uncompressedSize(),
  1872.         length( $self->fileName() ),
  1873.         length( $self->localExtraField() )
  1874.     );
  1875.  
  1876.     $fh->print($header) or return _ioError("writing local header");
  1877.     if ( $self->fileName() )
  1878.     {
  1879.         $fh->print( $self->fileName() )
  1880.           or return _ioError("writing local header filename");
  1881.     }
  1882.     if ( $self->localExtraField() )
  1883.     {
  1884.         $fh->print( $self->localExtraField() )
  1885.           or return _ioError("writing local extra field");
  1886.     }
  1887.  
  1888.     return AZ_OK;
  1889. }
  1890.  
  1891. sub _writeCentralDirectoryFileHeader    # Archive::Zip::Member
  1892. {
  1893.     my $self = shift;
  1894.     my $fh   = shift;
  1895.  
  1896.     my $sigData =
  1897.       pack( SIGNATURE_FORMAT, CENTRAL_DIRECTORY_FILE_HEADER_SIGNATURE );
  1898.     $fh->print($sigData)
  1899.       or return _ioError("writing central directory header signature");
  1900.  
  1901.     my $fileNameLength    = length( $self->fileName() );
  1902.     my $extraFieldLength  = length( $self->cdExtraField() );
  1903.     my $fileCommentLength = length( $self->fileComment() );
  1904.  
  1905.     my $header = pack(
  1906.         CENTRAL_DIRECTORY_FILE_HEADER_FORMAT,
  1907.         $self->versionMadeBy(),
  1908.         $self->fileAttributeFormat(),
  1909.         $self->versionNeededToExtract(),
  1910.         $self->bitFlag(),
  1911.         $self->desiredCompressionMethod(),
  1912.         $self->lastModFileDateTime(),
  1913.         $self->crc32(),            # these three fields should have been updated
  1914.         $self->_writeOffset(),     # by writing the data stream out
  1915.         $self->uncompressedSize(), #
  1916.         $fileNameLength,
  1917.         $extraFieldLength,
  1918.         $fileCommentLength,
  1919.         0,                         # {'diskNumberStart'},
  1920.         $self->internalFileAttributes(),
  1921.         $self->externalFileAttributes(),
  1922.         $self->writeLocalHeaderRelativeOffset()
  1923.     );
  1924.  
  1925.     $fh->print($header)
  1926.       or return _ioError("writing central directory header");
  1927.     if ($fileNameLength)
  1928.     {
  1929.         $fh->print( $self->fileName() )
  1930.           or return _ioError("writing central directory header signature");
  1931.     }
  1932.     if ($extraFieldLength)
  1933.     {
  1934.         $fh->print( $self->cdExtraField() )
  1935.           or return _ioError("writing central directory extra field");
  1936.     }
  1937.     if ($fileCommentLength)
  1938.     {
  1939.         $fh->print( $self->fileComment() )
  1940.           or return _ioError("writing central directory file comment");
  1941.     }
  1942.  
  1943.     return AZ_OK;
  1944. }
  1945.  
  1946. # This writes a data descriptor to the given file handle.
  1947. # Assumes that crc32, writeOffset, and uncompressedSize are
  1948. # set correctly (they should be after a write).
  1949. # Further, the local file header should have the
  1950. # GPBF_HAS_DATA_DESCRIPTOR_MASK bit set.
  1951. sub _writeDataDescriptor    # Archive::Zip::Member
  1952. {
  1953.     my $self   = shift;
  1954.     my $fh     = shift;
  1955.     my $header = pack(
  1956.         DATA_DESCRIPTOR_FORMAT,
  1957.         $self->crc32(),
  1958.         $self->_writeOffset(),    # compressed size
  1959.         $self->uncompressedSize()
  1960.     );
  1961.  
  1962.     $fh->print($header)
  1963.       or return _ioError("writing data descriptor");
  1964.     return AZ_OK;
  1965. }
  1966.  
  1967. # Re-writes the local file header with new crc32 and compressedSize fields.
  1968. # To be called after writing the data stream.
  1969. # Assumes that filename and extraField sizes didn't change since last written.
  1970. sub _refreshLocalFileHeader    # Archive::Zip::Member
  1971. {
  1972.     my $self = shift;
  1973.     my $fh   = shift;
  1974.  
  1975.     my $here = $fh->tell();
  1976.     $fh->seek( $self->writeLocalHeaderRelativeOffset() + SIGNATURE_LENGTH,
  1977.         IO::Seekable::SEEK_SET )
  1978.       or return _ioError("seeking to rewrite local header");
  1979.  
  1980.     my $header = pack(
  1981.         LOCAL_FILE_HEADER_FORMAT,
  1982.         $self->versionNeededToExtract(),
  1983.         $self->bitFlag(),
  1984.         $self->desiredCompressionMethod(),
  1985.         $self->lastModFileDateTime(),
  1986.         $self->crc32(),
  1987.         $self->_writeOffset(),    # compressed size
  1988.         $self->uncompressedSize(),
  1989.         length( $self->fileName() ),
  1990.         length( $self->localExtraField() )
  1991.     );
  1992.  
  1993.     $fh->print($header)
  1994.       or return _ioError("re-writing local header");
  1995.     $fh->seek( $here, IO::Seekable::SEEK_SET )
  1996.       or return _ioError("seeking after rewrite of local header");
  1997.  
  1998.     return AZ_OK;
  1999. }
  2000.  
  2001. sub readChunk    # Archive::Zip::Member
  2002. {
  2003.     my ( $self, $chunkSize ) = @_;
  2004.  
  2005.     if ( $self->readIsDone() )
  2006.     {
  2007.         $self->endRead();
  2008.         my $dummy = '';
  2009.         return ( \$dummy, AZ_STREAM_END );
  2010.     }
  2011.  
  2012.     $chunkSize = $Archive::Zip::ChunkSize if not defined($chunkSize);
  2013.     $chunkSize = $self->_readDataRemaining()
  2014.       if $chunkSize > $self->_readDataRemaining();
  2015.  
  2016.     my $buffer = '';
  2017.     my $outputRef;
  2018.     my ( $bytesRead, $status ) = $self->_readRawChunk( \$buffer, $chunkSize );
  2019.     return ( \$buffer, $status ) unless $status == AZ_OK;
  2020.  
  2021.     $self->{'readDataRemaining'} -= $bytesRead;
  2022.     $self->{'readOffset'} += $bytesRead;
  2023.  
  2024.     if ( $self->compressionMethod() == COMPRESSION_STORED )
  2025.     {
  2026.         $self->{'crc32'} = $self->computeCRC32( $buffer, $self->{'crc32'} );
  2027.     }
  2028.  
  2029.     ( $outputRef, $status ) = &{ $self->{'chunkHandler'} } ( $self, \$buffer );
  2030.     $self->{'writeOffset'} += length($$outputRef);
  2031.  
  2032.     $self->endRead()
  2033.       if $self->readIsDone();
  2034.  
  2035.     return ( $outputRef, $status );
  2036. }
  2037.  
  2038. # Read the next raw chunk of my data. Subclasses MUST implement.
  2039. #    my ( $bytesRead, $status) = $self->_readRawChunk( \$buffer, $chunkSize );
  2040. sub _readRawChunk    # Archive::Zip::Member
  2041. {
  2042.     my $self = shift;
  2043.     return $self->_subclassResponsibility();
  2044. }
  2045.  
  2046. # A place holder to catch rewindData errors if someone ignores
  2047. # the error code.
  2048. sub _noChunk    # Archive::Zip::Member
  2049. {
  2050.     my $self = shift;
  2051.     return ( \undef, _error("trying to copy chunk when init failed") );
  2052. }
  2053.  
  2054. # Basically a no-op so that I can have a consistent interface.
  2055. # ( $outputRef, $status) = $self->_copyChunk( \$buffer );
  2056. sub _copyChunk    # Archive::Zip::Member
  2057. {
  2058.     my ( $self, $dataRef ) = @_;
  2059.     return ( $dataRef, AZ_OK );
  2060. }
  2061.  
  2062. # ( $outputRef, $status) = $self->_deflateChunk( \$buffer );
  2063. sub _deflateChunk    # Archive::Zip::Member
  2064. {
  2065.     my ( $self, $buffer ) = @_;
  2066.     my ( $out,  $status ) = $self->_deflater()->deflate($buffer);
  2067.  
  2068.     if ( $self->_readDataRemaining() == 0 )
  2069.     {
  2070.         my $extraOutput;
  2071.         ( $extraOutput, $status ) = $self->_deflater()->flush();
  2072.         $out .= $extraOutput;
  2073.         $self->endRead();
  2074.         return ( \$out, AZ_STREAM_END );
  2075.     }
  2076.     elsif ( $status == Z_OK )
  2077.     {
  2078.         return ( \$out, AZ_OK );
  2079.     }
  2080.     else
  2081.     {
  2082.         $self->endRead();
  2083.         my $retval = _error( 'deflate error', $status );
  2084.         my $dummy = '';
  2085.         return ( \$dummy, $retval );
  2086.     }
  2087. }
  2088.  
  2089. # ( $outputRef, $status) = $self->_inflateChunk( \$buffer );
  2090. sub _inflateChunk    # Archive::Zip::Member
  2091. {
  2092.     my ( $self, $buffer ) = @_;
  2093.     my ( $out,  $status ) = $self->_inflater()->inflate($buffer);
  2094.     my $retval;
  2095.     $self->endRead() unless $status == Z_OK;
  2096.     if ( $status == Z_OK || $status == Z_STREAM_END )
  2097.     {
  2098.         $retval = ( $status == Z_STREAM_END ) ? AZ_STREAM_END: AZ_OK;
  2099.         return ( \$out, $retval );
  2100.     }
  2101.     else
  2102.     {
  2103.         $retval = _error( 'inflate error', $status );
  2104.         my $dummy = '';
  2105.         return ( \$dummy, $retval );
  2106.     }
  2107. }
  2108.  
  2109. sub rewindData    # Archive::Zip::Member
  2110. {
  2111.     my $self = shift;
  2112.     my $status;
  2113.  
  2114.     # set to trap init errors
  2115.     $self->{'chunkHandler'} = $self->can('_noChunk');
  2116.  
  2117.     # Work around WinZip bug with 0-length DEFLATED files
  2118.     $self->desiredCompressionMethod(COMPRESSION_STORED)
  2119.       if $self->uncompressedSize() == 0;
  2120.  
  2121.     # assume that we're going to read the whole file, and compute the CRC anew.
  2122.     $self->{'crc32'} = 0
  2123.       if ( $self->compressionMethod() == COMPRESSION_STORED );
  2124.  
  2125.     # These are the only combinations of methods we deal with right now.
  2126.     if ( $self->compressionMethod() == COMPRESSION_STORED
  2127.         and $self->desiredCompressionMethod() == COMPRESSION_DEFLATED )
  2128.     {
  2129.         ( $self->{'deflater'}, $status ) = Compress::Zlib::deflateInit(
  2130.             '-Level'      => $self->desiredCompressionLevel(),
  2131.             '-WindowBits' => -MAX_WBITS(),  # necessary magic
  2132.             #            '-Bufsize'    => $Archive::Zip::ChunkSize,
  2133.             @_
  2134.         );    # pass additional options
  2135.         return _error( 'deflateInit error:', $status )
  2136.           unless $status == Z_OK;
  2137.         $self->{'chunkHandler'} = $self->can('_deflateChunk');
  2138.     }
  2139.     elsif ( $self->compressionMethod() == COMPRESSION_DEFLATED
  2140.         and $self->desiredCompressionMethod() == COMPRESSION_STORED )
  2141.     {
  2142.         ( $self->{'inflater'}, $status ) = Compress::Zlib::inflateInit(
  2143.             '-WindowBits' => -MAX_WBITS(),    # necessary magic
  2144.             #            '-Bufsize'    => $Archive::Zip::ChunkSize,
  2145.             @_
  2146.         );    # pass additional options
  2147.         return _error( 'inflateInit error:', $status )
  2148.           unless $status == Z_OK;
  2149.         $self->{'chunkHandler'} = $self->can('_inflateChunk');
  2150.     }
  2151.     elsif ( $self->compressionMethod() == $self->desiredCompressionMethod() )
  2152.     {
  2153.         $self->{'chunkHandler'} = $self->can('_copyChunk');
  2154.     }
  2155.     else
  2156.     {
  2157.         return _error(
  2158.             sprintf(
  2159.                 "Unsupported compression combination: read %d, write %d",
  2160.                 $self->compressionMethod(),
  2161.                 $self->desiredCompressionMethod()
  2162.             )
  2163.         );
  2164.     }
  2165.  
  2166.     $self->{'readDataRemaining'} =
  2167.       ( $self->compressionMethod() == COMPRESSION_STORED )
  2168.       ? $self->uncompressedSize()
  2169.       : $self->compressedSize();
  2170.     $self->{'dataEnded'}  = 0;
  2171.     $self->{'readOffset'} = 0;
  2172.  
  2173.     return AZ_OK;
  2174. }
  2175.  
  2176. sub endRead    # Archive::Zip::Member
  2177. {
  2178.     my $self = shift;
  2179.     delete $self->{'inflater'};
  2180.     delete $self->{'deflater'};
  2181.     $self->{'dataEnded'}         = 1;
  2182.     $self->{'readDataRemaining'} = 0;
  2183.     return AZ_OK;
  2184. }
  2185.  
  2186. sub readIsDone    # Archive::Zip::Member
  2187. {
  2188.     my $self = shift;
  2189.     return ( $self->_dataEnded() or !$self->_readDataRemaining() );
  2190. }
  2191.  
  2192. sub contents    # Archive::Zip::Member
  2193. {
  2194.     my $self        = shift;
  2195.     my $newContents = shift;
  2196.     if ( defined($newContents) )
  2197.     {
  2198.         $self->_become(STRINGMEMBERCLASS);
  2199.         return $self->contents($newContents);
  2200.     }
  2201.     else
  2202.     {
  2203.         my $oldCompression =
  2204.           $self->desiredCompressionMethod(COMPRESSION_STORED);
  2205.         my $status = $self->rewindData(@_);
  2206.         if ( $status != AZ_OK )
  2207.         {
  2208.             $self->endRead();
  2209.             return $status;
  2210.         }
  2211.         my $retval = '';
  2212.         while ( $status == AZ_OK )
  2213.         {
  2214.             my $ref;
  2215.             ( $ref, $status ) = $self->readChunk( $self->_readDataRemaining() );
  2216.  
  2217.             # did we get it in one chunk?
  2218.             if ( length($$ref) == $self->uncompressedSize() )
  2219.             {
  2220.                 $retval = $$ref;
  2221.             }
  2222.             else { $retval .= $$ref }
  2223.         }
  2224.         $self->desiredCompressionMethod($oldCompression);
  2225.         $self->endRead();
  2226.         $status = AZ_OK if $status == AZ_STREAM_END;
  2227.         $retval = undef unless $status == AZ_OK;
  2228.         return wantarray ? ( $retval, $status ) : $retval;
  2229.     }
  2230. }
  2231.  
  2232. sub extractToFileHandle    # Archive::Zip::Member
  2233. {
  2234.     my $self = shift;
  2235.     return _error("encryption unsupported") if $self->isEncrypted();
  2236.     my $fh = shift;
  2237.     _binmode($fh);
  2238.     my $oldCompression = $self->desiredCompressionMethod(COMPRESSION_STORED);
  2239.     my $status         = $self->rewindData(@_);
  2240.     $status = $self->_writeData($fh) if $status == AZ_OK;
  2241.     $self->desiredCompressionMethod($oldCompression);
  2242.     $self->endRead();
  2243.     return $status;
  2244. }
  2245.  
  2246. # write local header and data stream to file handle
  2247. sub _writeToFileHandle    # Archive::Zip::Member
  2248. {
  2249.     my $self         = shift;
  2250.     my $fh           = shift;
  2251.     my $fhIsSeekable = shift;
  2252.     my $offset       = shift;
  2253.  
  2254.     return _error("no member name given for $self")
  2255.       unless $self->fileName();
  2256.  
  2257.     $self->{'writeLocalHeaderRelativeOffset'} = $offset;
  2258.     $self->{'wasWritten'}                     = 0;
  2259.  
  2260.     # Determine if I need to write a data descriptor
  2261.     # I need to do this if I can't refresh the header
  2262.     # and I don't know compressed size or crc32 fields.
  2263.     my $headerFieldsUnknown =
  2264.       ( ( $self->uncompressedSize() > 0 )
  2265.       and ( $self->compressionMethod() == COMPRESSION_STORED
  2266.           or $self->desiredCompressionMethod() == COMPRESSION_DEFLATED ) );
  2267.  
  2268.     my $shouldWriteDataDescriptor =
  2269.       ( $headerFieldsUnknown and not $fhIsSeekable );
  2270.  
  2271.     $self->hasDataDescriptor(1)
  2272.       if ($shouldWriteDataDescriptor);
  2273.  
  2274.     $self->{'writeOffset'} = 0;
  2275.  
  2276.     my $status = $self->rewindData();
  2277.     ( $status = $self->_writeLocalFileHeader($fh) )
  2278.       if $status == AZ_OK;
  2279.     ( $status = $self->_writeData($fh) )
  2280.       if $status == AZ_OK;
  2281.     if ( $status == AZ_OK )
  2282.     {
  2283.         $self->{'wasWritten'} = 1;
  2284.         if ( $self->hasDataDescriptor() )
  2285.         {
  2286.             $status = $self->_writeDataDescriptor($fh);
  2287.         }
  2288.         elsif ($headerFieldsUnknown)
  2289.         {
  2290.             $status = $self->_refreshLocalFileHeader($fh);
  2291.         }
  2292.     }
  2293.  
  2294.     return $status;
  2295. }
  2296.  
  2297. # Copy my (possibly compressed) data to given file handle.
  2298. # Returns C<AZ_OK> on success
  2299. sub _writeData    # Archive::Zip::Member
  2300. {
  2301.     my $self    = shift;
  2302.     my $writeFh = shift;
  2303.  
  2304.     return AZ_OK if ( $self->uncompressedSize() == 0 );
  2305.     my $status;
  2306.     my $chunkSize = $Archive::Zip::ChunkSize;
  2307.     while ( $self->_readDataRemaining() > 0 )
  2308.     {
  2309.         my $outRef;
  2310.         ( $outRef, $status ) = $self->readChunk($chunkSize);
  2311.         return $status if ( $status != AZ_OK and $status != AZ_STREAM_END );
  2312.  
  2313.         if ( length($$outRef) > 0 )
  2314.         {
  2315.             $writeFh->print($$outRef)
  2316.               or return _ioError("write error during copy");
  2317.         }
  2318.  
  2319.         last if $status == AZ_STREAM_END;
  2320.     }
  2321.     $self->{'compressedSize'} = $self->_writeOffset();
  2322.     return AZ_OK;
  2323. }
  2324.  
  2325. # Return true if I depend on the named file
  2326. sub _usesFileNamed
  2327. {
  2328.     return 0;
  2329. }
  2330.  
  2331. # ----------------------------------------------------------------------
  2332. # class Archive::Zip::DirectoryMember
  2333. # ----------------------------------------------------------------------
  2334.  
  2335. package Archive::Zip::DirectoryMember;
  2336. use File::Path;
  2337.  
  2338. use vars qw( @ISA );
  2339. @ISA = qw ( Archive::Zip::Member );
  2340. BEGIN { use Archive::Zip qw( :ERROR_CODES :UTILITY_METHODS ) }
  2341.  
  2342. sub _newNamed    # Archive::Zip::DirectoryMember
  2343. {
  2344.     my $class    = shift;
  2345.     my $fileName = shift;    # FS name
  2346.     my $newName  = shift;    # Zip name
  2347.     $newName = _asZipDirName($fileName) unless $newName;
  2348.     my $self = $class->new(@_);
  2349.     $self->{'externalFileName'} = $fileName;
  2350.     $self->fileName($newName);
  2351.     if ( -e $fileName )
  2352.     {
  2353.  
  2354.         if ( -d _ )
  2355.         {
  2356.             my @stat = stat(_);
  2357.             $self->unixFileAttributes( $stat[2] );
  2358.             $self->setLastModFileDateTimeFromUnix( $stat[9] );
  2359.         }
  2360.         else    # hmm.. trying to add a non-directory?
  2361.         {
  2362.             _error( $fileName, ' exists but is not a directory' );
  2363.             return undef;
  2364.         }
  2365.     }
  2366.     else
  2367.     {
  2368.         $self->unixFileAttributes( $self->DEFAULT_DIRECTORY_PERMISSIONS );
  2369.         $self->setLastModFileDateTimeFromUnix( time() );
  2370.     }
  2371.     return $self;
  2372. }
  2373.  
  2374. sub externalFileName    # Archive::Zip::DirectoryMember
  2375. {
  2376.     shift->{'externalFileName'};
  2377. }
  2378.  
  2379. sub isDirectory    # Archive::Zip::DirectoryMember
  2380. {
  2381.     return 1;
  2382. }
  2383.  
  2384. sub extractToFileNamed    # Archive::Zip::DirectoryMember
  2385. {
  2386.     my $self    = shift;
  2387.     my $name    = shift;                                 # local FS name
  2388.     my $attribs = $self->unixFileAttributes() & 07777;
  2389.     mkpath( $name, 0, $attribs );                        # croaks on error
  2390.     utime( $self->lastModTime(), $self->lastModTime(), $name );
  2391.     return AZ_OK;
  2392. }
  2393.  
  2394. sub fileName    # Archive::Zip::DirectoryMember
  2395. {
  2396.     my $self    = shift;
  2397.     my $newName = shift;
  2398.     $newName =~ s{/?$}{/} if defined($newName);
  2399.     return $self->SUPER::fileName($newName);
  2400. }
  2401.  
  2402. # So people don't get too confused. This way it looks like the problem
  2403. # is in their code...
  2404. sub contents
  2405. {
  2406.     undef;
  2407. }
  2408.  
  2409. # ----------------------------------------------------------------------
  2410. # class Archive::Zip::FileMember
  2411. # Base class for classes that have file handles
  2412. # to external files
  2413. # ----------------------------------------------------------------------
  2414.  
  2415. package Archive::Zip::FileMember;
  2416. use vars qw( @ISA );
  2417. @ISA = qw ( Archive::Zip::Member );
  2418. BEGIN { use Archive::Zip qw( :UTILITY_METHODS ) }
  2419.  
  2420. sub externalFileName    # Archive::Zip::FileMember
  2421. {
  2422.     shift->{'externalFileName'};
  2423. }
  2424.  
  2425. # Return true if I depend on the named file
  2426. sub _usesFileNamed    # Archive::Zip::FileMember
  2427. {
  2428.     my $self     = shift;
  2429.     my $fileName = shift;
  2430.     my $xfn      = $self->externalFileName();
  2431.     return undef if ref($xfn);
  2432.     return $xfn eq $fileName;
  2433. }
  2434.  
  2435. sub fh    # Archive::Zip::FileMember
  2436. {
  2437.     my $self = shift;
  2438.     $self->_openFile()
  2439.       if !defined( $self->{'fh'} ) || !$self->{'fh'}->opened();
  2440.     return $self->{'fh'};
  2441. }
  2442.  
  2443. # opens my file handle from my file name
  2444. sub _openFile    # Archive::Zip::FileMember
  2445. {
  2446.     my $self = shift;
  2447.     my ( $status, $fh ) = _newFileHandle( $self->externalFileName(), 'r' );
  2448.     if ( !$status )
  2449.     {
  2450.         _ioError( "Can't open", $self->externalFileName() );
  2451.         return undef;
  2452.     }
  2453.     $self->{'fh'} = $fh;
  2454.     _binmode($fh);
  2455.     return $fh;
  2456. }
  2457.  
  2458. # Closes my file handle
  2459. sub _closeFile    # Archive::Zip::FileMember
  2460. {
  2461.     my $self = shift;
  2462.     my $fh   = $self->{'fh'};
  2463.     $self->{'fh'} = undef;
  2464. }
  2465.  
  2466. # Make sure I close my file handle
  2467. sub endRead    # Archive::Zip::FileMember
  2468. {
  2469.     my $self = shift;
  2470.     $self->_closeFile();
  2471.     return $self->SUPER::endRead(@_);
  2472. }
  2473.  
  2474. sub _become    # Archive::Zip::FileMember
  2475. {
  2476.     my $self     = shift;
  2477.     my $newClass = shift;
  2478.     return $self if ref($self) eq $newClass;
  2479.     delete( $self->{'externalFileName'} );
  2480.     delete( $self->{'fh'} );
  2481.     return $self->SUPER::_become($newClass);
  2482. }
  2483.  
  2484. # ----------------------------------------------------------------------
  2485. # class Archive::Zip::NewFileMember
  2486. # Used when adding a pre-existing file to an archive
  2487. # ----------------------------------------------------------------------
  2488.  
  2489. package Archive::Zip::NewFileMember;
  2490. use vars qw( @ISA );
  2491. @ISA = qw ( Archive::Zip::FileMember );
  2492.  
  2493. BEGIN { use Archive::Zip qw( :CONSTANTS :ERROR_CODES :UTILITY_METHODS ) }
  2494.  
  2495. # Given a file name, set up for eventual writing.
  2496. sub _newFromFileNamed    # Archive::Zip::NewFileMember
  2497. {
  2498.     my $class    = shift;
  2499.     my $fileName = shift;    # local FS format
  2500.     my $newName  = shift;
  2501.     $newName = _asZipDirName($fileName) unless defined($newName);
  2502.     return undef unless ( stat($fileName) && -r _ && !-d _ );
  2503.     my $self = $class->new(@_);
  2504.     $self->fileName($newName);
  2505.     $self->{'externalFileName'}  = $fileName;
  2506.     $self->{'compressionMethod'} = COMPRESSION_STORED;
  2507.     my @stat = stat(_);
  2508.     $self->{'compressedSize'} = $self->{'uncompressedSize'} = $stat[7];
  2509.     $self->desiredCompressionMethod( ( $self->compressedSize() > 0 ) 
  2510.         ? COMPRESSION_DEFLATED
  2511.         : COMPRESSION_STORED );
  2512.     $self->unixFileAttributes( $stat[2] );
  2513.     $self->setLastModFileDateTimeFromUnix( $stat[9] );
  2514.     $self->isTextFile( -T _ );
  2515.     return $self;
  2516. }
  2517.  
  2518. sub rewindData    # Archive::Zip::NewFileMember
  2519. {
  2520.     my $self = shift;
  2521.  
  2522.     my $status = $self->SUPER::rewindData(@_);
  2523.     return $status unless $status == AZ_OK;
  2524.  
  2525.     return AZ_IO_ERROR unless $self->fh();
  2526.     $self->fh()->clearerr();
  2527.     $self->fh()->seek( 0, IO::Seekable::SEEK_SET )
  2528.       or return _ioError( "rewinding", $self->externalFileName() );
  2529.     return AZ_OK;
  2530. }
  2531.  
  2532. # Return bytes read. Note that first parameter is a ref to a buffer.
  2533. # my $data;
  2534. # my ( $bytesRead, $status) = $self->readRawChunk( \$data, $chunkSize );
  2535. sub _readRawChunk    # Archive::Zip::NewFileMember
  2536. {
  2537.     my ( $self, $dataRef, $chunkSize ) = @_;
  2538.     return ( 0, AZ_OK ) unless $chunkSize;
  2539.     my $bytesRead = $self->fh()->read( $$dataRef, $chunkSize )
  2540.       or return ( 0, _ioError("reading data") );
  2541.     return ( $bytesRead, AZ_OK );
  2542. }
  2543.  
  2544. # If I already exist, extraction is a no-op.
  2545. sub extractToFileNamed    # Archive::Zip::NewFileMember
  2546. {
  2547.     my $self = shift;
  2548.     my $name = shift;    # local FS name
  2549.     if ( File::Spec->rel2abs($name) eq
  2550.         File::Spec->rel2abs( $self->externalFileName() ) and -r $name )
  2551.     {
  2552.         return AZ_OK;
  2553.     }
  2554.     else
  2555.     {
  2556.         return $self->SUPER::extractToFileNamed( $name, @_ );
  2557.     }
  2558. }
  2559.  
  2560. # ----------------------------------------------------------------------
  2561. # class Archive::Zip::ZipFileMember
  2562. # This represents a member in an existing zip file on disk.
  2563. # ----------------------------------------------------------------------
  2564.  
  2565. package Archive::Zip::ZipFileMember;
  2566. use vars qw( @ISA );
  2567. @ISA = qw ( Archive::Zip::FileMember );
  2568.  
  2569. BEGIN
  2570. {
  2571.     use Archive::Zip qw( :CONSTANTS :ERROR_CODES :PKZIP_CONSTANTS
  2572.       :UTILITY_METHODS );
  2573. }
  2574.  
  2575. # Create a new Archive::Zip::ZipFileMember
  2576. # given a filename and optional open file handle
  2577. sub _newFromZipFile    # Archive::Zip::ZipFileMember
  2578. {
  2579.     my $class              = shift;
  2580.     my $fh                 = shift;
  2581.     my $externalFileName   = shift;
  2582.     my $possibleEocdOffset = shift;    # normally 0
  2583.  
  2584.     my $self = $class->new(
  2585.         'crc32'                     => 0,
  2586.         'diskNumberStart'           => 0,
  2587.         'localHeaderRelativeOffset' => 0,
  2588.         'dataOffset' => 0,    # localHeaderRelativeOffset + header length
  2589.         @_
  2590.     );
  2591.     $self->{'externalFileName'}   = $externalFileName;
  2592.     $self->{'fh'}                 = $fh;
  2593.     $self->{'possibleEocdOffset'} = $possibleEocdOffset;
  2594.     return $self;
  2595. }
  2596.  
  2597. sub isDirectory    # Archive::Zip::ZipFileMember
  2598. {
  2599.     my $self = shift;
  2600.     return ( substr( $self->fileName(), -1, 1 ) eq '/'
  2601.         and $self->uncompressedSize() == 0 );
  2602. }
  2603.  
  2604. # Seek to the beginning of the local header, just past the signature.
  2605. # Verify that the local header signature is in fact correct.
  2606. # Update the localHeaderRelativeOffset if necessary by adding the possibleEocdOffset.
  2607. # Returns status.
  2608.  
  2609. sub _seekToLocalHeader    # Archive::Zip::ZipFileMember
  2610. {
  2611.     my $self  = shift;
  2612.     my $where = shift;
  2613.     $where = $self->localHeaderRelativeOffset() unless defined($where);
  2614.  
  2615.     my $status;
  2616.     my $signature;
  2617.  
  2618.     $status = $self->fh()->seek( $where, IO::Seekable::SEEK_SET );
  2619.     return _ioError("seeking to local header") unless $status;
  2620.  
  2621.     ( $status, $signature ) =
  2622.       _readSignature( $self->fh(), $self->externalFileName(),
  2623.         LOCAL_FILE_HEADER_SIGNATURE );
  2624.     return $status if $status == AZ_IO_ERROR;
  2625.  
  2626.     # retry with EOCD offset if any was given.
  2627.     if ( $status == AZ_FORMAT_ERROR && $self->{'possibleEocdOffset'} )
  2628.     {
  2629.         $status =
  2630.           $self->_seekToLocalHeader( $self->localHeaderRelativeOffset() +
  2631.             $self->{'possibleEocdOffset'} );
  2632.         if ( $status == AZ_OK )
  2633.         {
  2634.             $self->{'localHeaderRelativeOffset'} +=
  2635.               $self->{'possibleEocdOffset'};
  2636.             $self->{'possibleEocdOffset'} = 0;
  2637.         }
  2638.     }
  2639.  
  2640.     return $status;
  2641. }
  2642.  
  2643. # Because I'm going to delete the file handle, read the local file
  2644. # header if the file handle is seekable. If it isn't, I assume that
  2645. # I've already read the local header.
  2646. # Return ( $status, $self )
  2647.  
  2648. sub _become    # Archive::Zip::ZipFileMember
  2649. {
  2650.     my $self     = shift;
  2651.     my $newClass = shift;
  2652.     return $self if ref($self) eq $newClass;
  2653.  
  2654.     my $status = AZ_OK;
  2655.  
  2656.     if ( _isSeekable( $self->fh() ) )
  2657.     {
  2658.         my $here = $self->fh()->tell();
  2659.         $status = $self->_seekToLocalHeader();
  2660.         $status = $self->_readLocalFileHeader() if $status == AZ_OK;
  2661.         $self->fh()->seek( $here, IO::Seekable::SEEK_SET );
  2662.         return $status unless $status == AZ_OK;
  2663.     }
  2664.  
  2665.     delete( $self->{'diskNumberStart'} );
  2666.     delete( $self->{'localHeaderRelativeOffset'} );
  2667.     delete( $self->{'dataOffset'} );
  2668.  
  2669.     return $self->SUPER::_become($newClass);
  2670. }
  2671.  
  2672. sub diskNumberStart    # Archive::Zip::ZipFileMember
  2673. {
  2674.     shift->{'diskNumberStart'};
  2675. }
  2676.  
  2677. sub localHeaderRelativeOffset    # Archive::Zip::ZipFileMember
  2678. {
  2679.     shift->{'localHeaderRelativeOffset'};
  2680. }
  2681.  
  2682. sub dataOffset    # Archive::Zip::ZipFileMember
  2683. {
  2684.     shift->{'dataOffset'};
  2685. }
  2686.  
  2687. # Skip local file header, updating only extra field stuff.
  2688. # Assumes that fh is positioned before signature.
  2689. sub _skipLocalFileHeader    # Archive::Zip::ZipFileMember
  2690. {
  2691.     my $self = shift;
  2692.     my $header;
  2693.     my $bytesRead = $self->fh()->read( $header, LOCAL_FILE_HEADER_LENGTH );
  2694.     if ( $bytesRead != LOCAL_FILE_HEADER_LENGTH )
  2695.     {
  2696.         return _ioError("reading local file header");
  2697.     }
  2698.     my $fileNameLength;
  2699.     my $extraFieldLength;
  2700.     ( undef,    # $self->{'versionNeededToExtract'},
  2701.       undef,    # $self->{'bitFlag'},
  2702.       undef,    # $self->{'compressionMethod'},
  2703.       undef,    # $self->{'lastModFileDateTime'},
  2704.       undef,    # $crc32,
  2705.       undef,    # $compressedSize,
  2706.       undef,    # $uncompressedSize,
  2707.       $fileNameLength,
  2708.       $extraFieldLength )
  2709.       = unpack( LOCAL_FILE_HEADER_FORMAT, $header );
  2710.  
  2711.     if ($fileNameLength)
  2712.     {
  2713.         $self->fh()->seek( $fileNameLength, IO::Seekable::SEEK_CUR )
  2714.           or return _ioError("skipping local file name");
  2715.     }
  2716.  
  2717.     if ($extraFieldLength)
  2718.     {
  2719.         $bytesRead =
  2720.           $self->fh()->read( $self->{'localExtraField'}, $extraFieldLength );
  2721.         if ( $bytesRead != $extraFieldLength )
  2722.         {
  2723.             return _ioError("reading local extra field");
  2724.         }
  2725.     }
  2726.  
  2727.     $self->{'dataOffset'} = $self->fh()->tell();
  2728.  
  2729.     return AZ_OK;
  2730. }
  2731.  
  2732. # Read from a local file header into myself. Returns AZ_OK if successful.
  2733. # Assumes that fh is positioned after signature.
  2734. # Note that crc32, compressedSize, and uncompressedSize will be 0 if
  2735. # GPBF_HAS_DATA_DESCRIPTOR_MASK is set in the bitFlag.
  2736.  
  2737. sub _readLocalFileHeader    # Archive::Zip::ZipFileMember
  2738. {
  2739.     my $self = shift;
  2740.     my $header;
  2741.     my $bytesRead = $self->fh()->read( $header, LOCAL_FILE_HEADER_LENGTH );
  2742.     if ( $bytesRead != LOCAL_FILE_HEADER_LENGTH )
  2743.     {
  2744.         return _ioError("reading local file header");
  2745.     }
  2746.     my $fileNameLength;
  2747.     my $crc32;
  2748.     my $compressedSize;
  2749.     my $uncompressedSize;
  2750.     my $extraFieldLength;
  2751.     ( $self->{'versionNeededToExtract'}, $self->{'bitFlag'},
  2752.            $self->{'compressionMethod'}, $self->{'lastModFileDateTime'},
  2753.            $crc32,                       $compressedSize,
  2754.            $uncompressedSize,            $fileNameLength,
  2755.       $extraFieldLength )
  2756.       = unpack( LOCAL_FILE_HEADER_FORMAT, $header );
  2757.  
  2758.     if ($fileNameLength)
  2759.     {
  2760.         my $fileName;
  2761.         $bytesRead = $self->fh()->read( $fileName, $fileNameLength );
  2762.         if ( $bytesRead != $fileNameLength )
  2763.         {
  2764.             return _ioError("reading local file name");
  2765.         }
  2766.         $self->fileName($fileName);
  2767.     }
  2768.  
  2769.     if ($extraFieldLength)
  2770.     {
  2771.         $bytesRead =
  2772.           $self->fh()->read( $self->{'localExtraField'}, $extraFieldLength );
  2773.         if ( $bytesRead != $extraFieldLength )
  2774.         {
  2775.             return _ioError("reading local extra field");
  2776.         }
  2777.     }
  2778.  
  2779.     $self->{'dataOffset'} = $self->fh()->tell();
  2780.  
  2781.     # Don't trash these fields from the CD if we already have them.
  2782.     if ( not $self->hasDataDescriptor() )
  2783.     {
  2784.         $self->{'crc32'}            = $crc32;
  2785.         $self->{'compressedSize'}   = $compressedSize;
  2786.         $self->{'uncompressedSize'} = $uncompressedSize;
  2787.     }
  2788.  
  2789.     # We ignore data descriptors (we don't read them,
  2790.     # and we compute elsewhere whether we need to write them ).
  2791.     # And, we have the necessary data from the CD header.
  2792.     # So mark this entry as not having a data descriptor.
  2793.     $self->hasDataDescriptor(0);
  2794.  
  2795.     return AZ_OK;
  2796. }
  2797.  
  2798. # This will read the data descriptor, which is at the end of files that have
  2799. # GPBF_HAS_DATA_DESCRIPTOR_MASK set in their bitFlag.
  2800. # Note that you have to seek to the end of the compressed file to find this to
  2801. # read.
  2802. # sub _readDataDescriptor
  2803. # {
  2804. #     my $self = shift;
  2805. #     my $header;
  2806. #     $self->fh()->read( $header, DATA_DESCRIPTOR_LENGTH )
  2807. #         or return _ioError( "reading data descriptor" );
  2808. #     (
  2809. #         $self->{'crc32'},
  2810. #         $self->{'compressedSize'},
  2811. #         $self->{'uncompressedSize'}
  2812. #      ) = unpack( DATA_DESCRIPTOR_FORMAT, $header );
  2813. #     return AZ_OK;
  2814. # }
  2815.  
  2816. # Read a Central Directory header. Return AZ_OK on success.
  2817. # Assumes that fh is positioned right after the signature.
  2818.  
  2819. sub _readCentralDirectoryFileHeader    # Archive::Zip::ZipFileMember
  2820. {
  2821.     my $self      = shift;
  2822.     my $fh        = $self->fh();
  2823.     my $header    = '';
  2824.     my $bytesRead = $fh->read( $header, CENTRAL_DIRECTORY_FILE_HEADER_LENGTH );
  2825.     if ( $bytesRead != CENTRAL_DIRECTORY_FILE_HEADER_LENGTH )
  2826.     {
  2827.         return _ioError("reading central dir header");
  2828.     }
  2829.     my ( $fileNameLength, $extraFieldLength, $fileCommentLength );
  2830.     ( $self->{'versionMadeBy'},          $self->{'fileAttributeFormat'},
  2831.       $self->{'versionNeededToExtract'}, $self->{'bitFlag'},
  2832.       $self->{'compressionMethod'},      $self->{'lastModFileDateTime'},
  2833.       $self->{'crc32'},                  $self->{'compressedSize'},
  2834.       $self->{'uncompressedSize'},       $fileNameLength,
  2835.       $extraFieldLength,                 $fileCommentLength,
  2836.       $self->{'diskNumberStart'},        $self->{'internalFileAttributes'},
  2837.       $self->{'externalFileAttributes'}, $self->{'localHeaderRelativeOffset'} )
  2838.       = unpack( CENTRAL_DIRECTORY_FILE_HEADER_FORMAT, $header );
  2839.  
  2840.     if ($fileNameLength)
  2841.     {
  2842.         $bytesRead = $fh->read( $self->{'fileName'}, $fileNameLength );
  2843.         if ( $bytesRead != $fileNameLength )
  2844.         {
  2845.             _ioError("reading central dir filename");
  2846.         }
  2847.     }
  2848.     if ($extraFieldLength)
  2849.     {
  2850.         $bytesRead = $fh->read( $self->{'cdExtraField'}, $extraFieldLength );
  2851.         if ( $bytesRead != $extraFieldLength )
  2852.         {
  2853.             return _ioError("reading central dir extra field");
  2854.         }
  2855.     }
  2856.     if ($fileCommentLength)
  2857.     {
  2858.         $bytesRead = $fh->read( $self->{'fileComment'}, $fileCommentLength );
  2859.         if ( $bytesRead != $fileCommentLength )
  2860.         {
  2861.             return _ioError("reading central dir file comment");
  2862.         }
  2863.     }
  2864.  
  2865.     $self->desiredCompressionMethod( $self->compressionMethod() );
  2866.  
  2867.     return AZ_OK;
  2868. }
  2869.  
  2870. sub rewindData    # Archive::Zip::ZipFileMember
  2871. {
  2872.     my $self = shift;
  2873.  
  2874.     my $status = $self->SUPER::rewindData(@_);
  2875.     return $status unless $status == AZ_OK;
  2876.  
  2877.     return AZ_IO_ERROR unless $self->fh();
  2878.  
  2879.     $self->fh()->clearerr();
  2880.  
  2881.     # Seek to local file header.
  2882.     # The only reason that I'm doing this this way is that the extraField
  2883.     # length seems to be different between the CD header and the LF header.
  2884.     $status = $self->_seekToLocalHeader();
  2885.     return $status unless $status == AZ_OK;
  2886.  
  2887.     # skip local file header
  2888.     $status = $self->_skipLocalFileHeader();
  2889.     return $status unless $status == AZ_OK;
  2890.  
  2891.     # Seek to beginning of file data
  2892.     $self->fh()->seek( $self->dataOffset(), IO::Seekable::SEEK_SET )
  2893.       or return _ioError("seeking to beginning of file data");
  2894.  
  2895.     return AZ_OK;
  2896. }
  2897.  
  2898. # Return bytes read. Note that first parameter is a ref to a buffer.
  2899. # my $data;
  2900. # my ( $bytesRead, $status) = $self->readRawChunk( \$data, $chunkSize );
  2901. sub _readRawChunk    # Archive::Zip::ZipFileMember
  2902. {
  2903.     my ( $self, $dataRef, $chunkSize ) = @_;
  2904.     return ( 0, AZ_OK ) unless $chunkSize;
  2905.     my $bytesRead = $self->fh()->read( $$dataRef, $chunkSize )
  2906.       or return ( 0, _ioError("reading data") );
  2907.     return ( $bytesRead, AZ_OK );
  2908. }
  2909.  
  2910. # ----------------------------------------------------------------------
  2911. # class Archive::Zip::StringMember ( concrete )
  2912. # A Zip member whose data lives in a string
  2913. # ----------------------------------------------------------------------
  2914.  
  2915. package Archive::Zip::StringMember;
  2916. use vars qw( @ISA );
  2917. @ISA = qw ( Archive::Zip::Member );
  2918.  
  2919. BEGIN { use Archive::Zip qw( :CONSTANTS :ERROR_CODES ) }
  2920.  
  2921. # Create a new string member. Default is COMPRESSION_STORED.
  2922. # Can take a ref to a string as well.
  2923. sub _newFromString    # Archive::Zip::StringMember
  2924. {
  2925.     my $class  = shift;
  2926.     my $string = shift;
  2927.     my $name   = shift;
  2928.     my $self   = $class->new(@_);
  2929.     $self->contents($string);
  2930.     $self->fileName($name) if defined($name);
  2931.  
  2932.     # Set the file date to now
  2933.     $self->setLastModFileDateTimeFromUnix( time() );
  2934.     $self->unixFileAttributes( $self->DEFAULT_FILE_PERMISSIONS );
  2935.     return $self;
  2936. }
  2937.  
  2938. sub _become    # Archive::Zip::StringMember
  2939. {
  2940.     my $self     = shift;
  2941.     my $newClass = shift;
  2942.     return $self if ref($self) eq $newClass;
  2943.     delete( $self->{'contents'} );
  2944.     return $self->SUPER::_become($newClass);
  2945. }
  2946.  
  2947. # Get or set my contents. Note that we do not call the superclass
  2948. # version of this, because it calls us.
  2949. sub contents    # Archive::Zip::StringMember
  2950. {
  2951.     my $self   = shift;
  2952.     my $string = shift;
  2953.     if ( defined($string) )
  2954.     {
  2955.         $self->{'contents'} = ( ref($string) eq 'SCALAR' ) ? $$string : $string;
  2956.         $self->{'uncompressedSize'} = $self->{'compressedSize'} =
  2957.           length( $self->{'contents'} );
  2958.         $self->{'compressionMethod'} = COMPRESSION_STORED;
  2959.     }
  2960.     return $self->{'contents'};
  2961. }
  2962.  
  2963. # Return bytes read. Note that first parameter is a ref to a buffer.
  2964. # my $data;
  2965. # my ( $bytesRead, $status) = $self->readRawChunk( \$data, $chunkSize );
  2966. sub _readRawChunk    # Archive::Zip::StringMember
  2967. {
  2968.     my ( $self, $dataRef, $chunkSize ) = @_;
  2969.     $$dataRef = substr( $self->contents(), $self->_readOffset(), $chunkSize );
  2970.     return ( length($$dataRef), AZ_OK );
  2971. }
  2972.  
  2973. 1;
  2974. __END__
  2975.  
  2976.  
  2977. # vim: ts=4 sw=4 tw=80 wrap
  2978.