Skip Menu |

This queue is for tickets about the Archive-Zip CPAN distribution.

Report information
The Basics
Id: 7855
Status: open
Priority: 0/
Queue: Archive-Zip

People
Owner: Nobody in particular
Requestors: julian [...] mehnle.net
Cc:
AdminCc:

Bug Information
Severity: Normal
Broken in: 1.13
Fixed in: (no value)



Subject: Reading from IO::Scalar, IO::ScalarArray, or in-memory IO::File not supported (although all those are seekable)
Reading from IO::Scalar, IO::ScalarArray, or in-memory IO::File through readFromFileHandle is not supported, although all of those are seekable. I'm trying to use Archive::Zip in combination with MIME::Parser, completely in-memory, without temp files. So I'd like to read the ZIP archive from IO::Scalar and IO::ScalarArray objects, because that is what MIME::Parser generates when not using temp files. But the source code says: # Attempt to guess whether file handle is seekable. # Because of problems with Windoze, this only returns true when # the file handle is a real file. sub _isSeekable # Archive::Zip { my $fh = shift; if ( UNIVERSAL::isa( $fh, 'IO::Scalar' ) ) { return 0; } ... } Why is that? IO::Scalar *is* seekable, as is IO::ScalarArray. Further down it says: ... elsif ( UNIVERSAL::can( $fh, 'stat' ) ) { return -f $fh; } ... That prevents a work-around of mine, using Perl 5.8 in-memory files: my $handle = IO::File->new(); open($handle, '+<', \$raw_archive); because such in in-memory file doesn't stand the -f test. Could you make IO::Scalar, IO::ScalarArray, or in-memory IO::File work as arguments to the readFromFileHandle() method?
From: COMAND
[JMEHNLE - Sat Oct 2 21:41:20 2004]: Show quoted text
> Reading from IO::Scalar, IO::ScalarArray, or in-memory IO::File > through readFromFileHandle is not supported, although all of those > are seekable.
I ran into the same problem, and also found another. _isSeekable will return true if the filehandle is a pipe or socket. As a result, when calling writeToFileHandle, the object lights on fire. I've attached a patch which attempts to detect unseekable filehandles without resorting to naming specific objects. I don't have a Win32 platform to test with at the moment, so I'm not sure whether the restriction on the file existing still holds true with these changes. I left the check in there anyway...
diff --speed-large-files --minimal -Nru Archive-Zip-1.14.orig/lib/Archive/Zip.pm Archive-Zip-1.14/lib/Archive/Zip.pm --- Archive-Zip-1.14.orig/lib/Archive/Zip.pm 2004-10-21 08:26:02.000000000 -0700 +++ Archive-Zip-1.14/lib/Archive/Zip.pm 2005-02-16 09:47:28.390039000 -0800 @@ -311,19 +311,16 @@ { my $fh = shift; - if ( UNIVERSAL::isa( $fh, 'IO::Scalar' ) ) - { - return 0; - } - elsif ( UNIVERSAL::isa( $fh, 'IO::String' ) ) - { - return 1; - } - elsif ( UNIVERSAL::can( $fh, 'stat' ) ) - { - return -f $fh; - } - return UNIVERSAL::can( $fh, 'seek' ); + # File must exist on win32 + return -f $fh if $^O eq 'MSWin32'; + + # Check whether the fh has the proper methods + return 0 unless UNIVERSAL::can( $fh, 'stat' ) + and UNIVERSAL::can( $fh, 'seek' ) + and UNIVERSAL::can( $fh, 'getpos' ); + + # See if the handle can really seek + return (defined $fh->getpos() ? 1 : 0); } # Return an opened IO::Handle
From: COMAND
[JMEHNLE - Sat Oct 2 21:41:20 2004]: Show quoted text
> Reading from IO::Scalar, IO::ScalarArray, or in-memory IO::File > through readFromFileHandle is not supported, although all of those > are seekable.
I ran into the same problem, and also found another. _isSeekable will return true if the filehandle is a pipe or socket. As a result, when calling writeToFileHandle, the object lights on fire. I've attached a patch which attempts to detect unseekable filehandles without resorting to naming specific objects. I don't have a Win32 platform to test with at the moment, so I'm not sure whether the restriction on the file existing still holds true with these changes. I left the check in there anyway...
diff --speed-large-files --minimal -Nru Archive-Zip-1.14.orig/lib/Archive/Zip.pm Archive-Zip-1.14/lib/Archive/Zip.pm --- Archive-Zip-1.14.orig/lib/Archive/Zip.pm 2004-10-21 08:26:02.000000000 -0700 +++ Archive-Zip-1.14/lib/Archive/Zip.pm 2005-02-16 09:47:28.390039000 -0800 @@ -311,19 +311,16 @@ { my $fh = shift; - if ( UNIVERSAL::isa( $fh, 'IO::Scalar' ) ) - { - return 0; - } - elsif ( UNIVERSAL::isa( $fh, 'IO::String' ) ) - { - return 1; - } - elsif ( UNIVERSAL::can( $fh, 'stat' ) ) - { - return -f $fh; - } - return UNIVERSAL::can( $fh, 'seek' ); + # File must exist on win32 + return -f $fh if $^O eq 'MSWin32'; + + # Check whether the fh has the proper methods + return 0 unless UNIVERSAL::can( $fh, 'stat' ) + and UNIVERSAL::can( $fh, 'seek' ) + and UNIVERSAL::can( $fh, 'getpos' ); + + # See if the handle can really seek + return (defined $fh->getpos() ? 1 : 0); } # Return an opened IO::Handle
Unfortunately, the patch doesn't handle Perl FileHandles correctly. The patch is in the right direction, though, and has given me a good idea on how to handle this. Be looking for a fix in the next full release of Archive::Zip.
[SMPETERS - Fri Mar 11 09:26:36 2005]: Show quoted text
> Unfortunately, the patch doesn't handle Perl FileHandles correctly.
I solved this problem some time ago. See http://www.archlug.org/kwiki/PerlAndJavaBugId-4635869 Two code changes are required: 1) Override to allow seekable IO::Scalars in _isSeekable package Archive::Zip::Archive; sub _isSeekable { my $fh = shift; return (-f $fh || UNIVERSAL::isa( $fh, 'IO::Scalar' )); } 2) Override to force IO::Scalar::print to use seekable IO::Scalars package IO::Scalar; sub print { my $self = shift; # *$self->{Pos} = length(${*$self->{SR}} .= join('', @_)); my $pos = *$self->{Pos}; my $buf = join('', @_); my $len = length($buf); substr(${*$self->{SR}}, $pos, $len) = $buf; *$self->{Pos} += $len; 1; } And that's the short version. However, only fixing Archive::Zip will not fix the problem in IO::Scalars. See the above link for example code to drop into your applicatin to fix both.
Inside _isSeekable you probably want 'no warnings;' else you get '-f on unopened filehandle' messages.
Trying to clean up some RT tickets here. Is this still an issue? Does the latest revision fix the problem?
On 2012-04-19 11:17:15, BBYRD wrote: Show quoted text
> Trying to clean up some RT tickets here. Is this still an issue? Does > the latest revision fix the problem?
I can't test my use case right now, but this is what Archive::Zip's _isSeekable function looks like in the 1.30 release: # Attempt to guess whether file handle is seekable. # Because of problems with Windows, this only returns true when # the file handle is a real file. sub _isSeekable { my $fh = shift; return 0 unless ref $fh; if ( _ISA($fh, 'IO::Scalar') ) { # IO::Scalar objects are brokenly-seekable return 0; } if ( _ISA($fh, 'IO::String') ) { return 1; } if ( _ISA($fh, 'IO::Seekable') ) { # Unfortunately, some things like FileHandle objects # return true for Seekable, but AREN'T!!!!! if ( _ISA($fh, 'FileHandle') ) { return 0; } else { return 1; } } if ( _CAN($fh, 'stat') ) { return -f $fh; } return ( _CAN($fh, 'seek') and _CAN($fh, 'tell') ) ? 1 : 0; } I'm not sure what the comment "IO::Scalar objects are brokenly-seekable" is supposed to mean, but it seems like someone at least made a conscious decision to declare IO::Scalar objects as non-seekable for Archive::Zip's purposes. It sure would be good to know the rationale for that, since fundamentally IO::Scalar *is* seekable. As for IO::File objects, I don't know how Archive::Zip's _ISA and _CAN functions work (don't have time to read a lot of source code today), so I'm not sure how _isSeekable treats IO::File.
On Thu Apr 19 13:21:16 2012, JMEHNLE wrote: Show quoted text
> I'm not sure what the comment "IO::Scalar objects are brokenly-seekable" > ... > It sure would be good to know the rationale > for that, since fundamentally IO::Scalar *is* seekable.
The perldoc (BUGS & CAVEATS) says IO::Scalar incorrectly implements seeking and suggests using IO::String instead. IO::String's perldocs suggest using in memory handles on versions > 5.8. And, as was pointed out above, in memory handles (well, any GLOB ref) fail the _isSeekable test.