# Copyright 2010, 2011 Kevin Ryde
# This file is part of Filter-gunzip.
#
# Filter-gunzip is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 3, or (at your option) any later
# version.
#
# Filter-gunzip is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with Filter-gunzip. If not, see .
package Filter::gunzip;
use strict;
use Carp;
use DynaLoader;
use PerlIO;
use PerlIO::gzip;
use vars qw($VERSION @ISA);
$VERSION = 4;
@ISA = ('DynaLoader');
__PACKAGE__->bootstrap($VERSION);
# uncomment this to run the ### lines
#use Smart::Comments;
sub import {
my ($class) = @_;
my $filters = _rsfp_filters();
### _rsfp_filters(): scalar(@{$filters})
if ($filters && ! @{$filters}) {
my $fh;
### _rsfp(): _rsfp()
if (($fh = _rsfp())
&& eval { require PerlIO;
require PerlIO::gzip;
1 }) {
### fh: $fh
### tell: tell($fh)
my @layers = PerlIO::get_layers($fh);
### layers: \@layers
if ($layers[-1] eq 'crlf') {
binmode ($fh, ':pop')
or croak "Oops, cannot pop crlf layer: $!";
}
binmode ($fh, ':gzip')
or croak "Cannot push gzip layer: $!";
if ($layers[-1] eq 'crlf') {
binmode ($fh, ':crlf')
or croak "Oops, cannot re-push crlf layer: $!";
}
# @layers = PerlIO::get_layers($fh);
# ### pushed gzip: \@layers
return;
}
}
require Filter::gunzip::Filter;
Filter::gunzip::Filter->import;
}
1;
__END__
=for stopwords gunzip Filter-gunzip uncompresses gzipped self-uncompressing gunzipping CRLF gzip CRC checksum zlib unbuffered Ryde
=head1 NAME
Filter::gunzip - gunzip Perl source code for execution
=head1 SYNOPSIS
perl -MFilter::gunzip foo.pl.gz
use Filter::gunzip;
... # inline gzipped source code bytes
=head1 DESCRIPTION
This filter uncompresses gzipped Perl code at run-time. It's slightly a
proof of concept, but works well as far as it goes. It can be used from the
command line to run a F file,
perl -MFilter::gunzip foo.pl.gz
Or in a self-uncompressing executable beginning with a C and gzip bytes
immediately following that line,
#!/usr/bin/perl
use Filter::gunzip;
... raw gzip bytes
The filter is implemented one of two ways. For the usual case that
C is the first filter and PerlIO is available then push a
C layer. Otherwise add a block-oriented source filter per
L. In both cases the uncompressed code can apply further source
filters in the usual way.
=head2 DATA Handle
The C token (L) and C handle can
be used in the compressed source, but only some of the time.
For the PerlIO case the C handle is simply the input, including the
C uncompressing layer, positioned just after the C token.
This works well for data compressed along with the code, though
C as of version 0.18 cannot dup or seek which means for
instance C doesn't work. (Duping and seeking are probably both
feasible, though seeking backward might be slow.)
For the filter case C doesn't really work properly. Perl stops
reading from the source filters at the C token, because that's
where the source ends. But a block oriented filter like C
may read ahead in the input file, so the position the C handle is left
is unpredictable, especially if there's a couple of block-oriented filters
stacked up.
=head2 Further Details
Perl source is normally read without CRLF translation (in Perl 5.6.1 and up
at least). If C sees a C layer on the input it
pushes the C underneath that, since the CRLF is almost certainly
meant to apply to the text, not to the raw gzip bytes. This should let it
work with the forced C suggested by F (see
L).
The gzip format has a CRC checksum at the end of the data. This might catch
subtle corruption in the compressed bytes, except as of Perl 5.10 the parser
usually doesn't report a read error and in any case the code is compiled and
C blocks are executed as uncompressing proceeds, so corruption will
likely provoke a syntax error before the CRC is reached.
Only the gzip format (RFC 1952) is supported. Zlib format (RFC 1950)
differs only in the header, but C (version 0.18) doesn't allow
it. The actual C program can handle some other formats, like Unix
F C, but they're probably best left to other modules.
The bzip2 format could be handled by a very similar filter, if F
files were used. Its decompressor uses at least 2.5 Mbytes of memory
though, so if choosing that format there'd have to be a big disk saving
before it was worth that much memory at runtime.
=head1 OTHER WAYS TO DO IT
C and the C program can the same thing, either from the
command line or self-expanding,
perl -MFilter::exec=zcat foo.pl.gz
Because C is a block-oriented filter (as of version 1.37) a
compressed C section within the script doesn't work.
C can be applied to a script with the C pragma and a
C. For example something like the following from the command line.
Since the C pragma is lexical it doesn't affect other later loads or
opens.
perl -e '{use open IN=>":gzip";require shift}' \
foo.pl.gz arg1 arg2
It doesn't work to set a C environment variable for a global
C layer, eg. C, because the default layers
are restricted to Perl builtins (see L).
=head1 SEE ALSO
L, L, L, L,
L, L, L
L setting up
a C command (and other forms) to run a C from Emacs.
=head1 HOME PAGE
http://user42.tuxfamily.org/filter-gunzip/index.html
=head1 LICENSE
Filter-gunzip is Copyright 2010, 2011 Kevin Ryde
Filter-gunzip is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
Filter-gunzip is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
Filter-gunzip. If not, see .
=cut