1 # Copyright © 2008-2010 Raphaël Hertzog <hertzog@debian.org>
2 # Copyright © 2012-2014 Guillem Jover <guillem@debian.org>
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <https://www.gnu.org/licenses/>.
21 Dpkg::Compression::FileHandle - class dealing transparently with file compression
25 use Dpkg::Compression::FileHandle;
29 $fh = Dpkg::Compression::FileHandle->new(filename => 'sample.gz');
30 print $fh "Something\n";
33 $fh = Dpkg::Compression::FileHandle->new();
34 open($fh, '>', 'sample.bz2');
35 print $fh "Something\n";
38 $fh = Dpkg::Compression::FileHandle->new();
39 $fh->open('sample.xz', 'w');
40 $fh->print("Something\n");
43 $fh = Dpkg::Compression::FileHandle->new(filename => 'sample.gz');
47 $fh = Dpkg::Compression::FileHandle->new();
48 open($fh, '<', 'sample.bz2');
52 $fh = Dpkg::Compression::FileHandle->new();
53 $fh->open('sample.xz', 'r');
54 @lines = $fh->getlines();
59 Dpkg::Compression::FileHandle is a class that can be used
60 like any filehandle and that deals transparently with compressed
61 files. By default, the compression scheme is guessed from the filename
62 but you can override this behavior with the method set_compression().
64 If you don't open the file explicitly, it will be auto-opened on the
65 first read or write operation based on the filename set at creation time
66 (or later with the set_filename() method).
68 Once a file has been opened, the filehandle must be closed before being
69 able to open another file.
73 package Dpkg
::Compression
::FileHandle
1.01;
80 use Dpkg
::Compression
;
81 use Dpkg
::Compression
::Process
;
83 use Dpkg
::ErrorHandling
;
85 use parent
qw(IO::File Tie::Handle);
87 # Useful reference to understand some kludges required to
88 # have the class behave like a filehandle
89 # http://blog.woobling.org/2009/10/are-filehandles-objects.html
91 =head1 STANDARD FUNCTIONS
93 The standard functions acting on filehandles should accept a
94 Dpkg::Compression::FileHandle object transparently including
95 open() (only when using the variant with 3 parameters), close(),
96 binmode(), eof(), fileno(), getc(), print(), printf(), read(),
97 sysread(), say(), write(), syswrite(), seek(), sysseek(), tell().
99 Note however that seek() and sysseek() will only work on uncompressed
100 files as compressed files are really pipes to the compressor programs
101 and you can't seek on a pipe.
103 =head1 FileHandle METHODS
105 The class inherits from L<IO::File> so all methods that work on this
106 class should work for Dpkg::Compression::FileHandle too. There
107 may be exceptions though.
109 =head1 PUBLIC METHODS
113 =item $fh = Dpkg::Compression::FileHandle->new(%opts)
115 Creates a new filehandle supporting on-the-fly compression/decompression.
116 Supported options are "filename", "compression", "compression_level" (see
117 respective set_* functions) and "add_comp_ext". If "add_comp_ext"
118 evaluates to true, then the extension corresponding to the selected
119 compression scheme is automatically added to the recorded filename. It's
120 obviously incompatible with automatic detection of the compression method.
126 my ($this, %opts) = @_;
127 my $class = ref($this) || $this;
128 my $self = IO
::File
->new();
129 # Tying is required to overload the open functions and to auto-open
130 # the file on first read/write operation
131 tie
*$self, $class, $self; ## no critic (Miscellanea::ProhibitTies)
134 *$self->{compression
} = 'auto';
135 *$self->{compressor
} = Dpkg
::Compression
::Process
->new();
136 *$self->{add_comp_ext
} = $opts{add_compression_extension
} ||
137 $opts{add_comp_ext
} || 0;
138 *$self->{allow_sigpipe
} = 0;
139 if (exists $opts{filename
}) {
140 $self->set_filename($opts{filename
});
142 if (exists $opts{compression
}) {
143 $self->set_compression($opts{compression
});
145 if (exists $opts{compression_level
}) {
146 $self->set_compression_level($opts{compression_level
});
151 =item $fh->ensure_open($mode, %opts)
153 Ensure the file is opened in the requested mode ("r" for read and "w" for
154 write). The options are passed down to the compressor's spawn() call, if one
155 is used. Opens the file with the recorded filename if needed. If the file
156 is already open but not in the requested mode, then it errors out.
161 my ($self, $mode, %opts) = @_;
162 if (exists *$self->{mode
}) {
163 return if *$self->{mode
} eq $mode;
164 croak
"ensure_open requested incompatible mode: $mode";
167 delete $opts{from_pipe
};
168 delete $opts{from_file
};
169 delete $opts{to_pipe
};
170 delete $opts{to_file
};
173 $self->_open_for_write(%opts);
174 } elsif ($mode eq 'r') {
175 $self->_open_for_read(%opts);
177 croak
"invalid mode in ensure_open: $mode";
183 ## METHODS FOR TIED HANDLE
186 my ($class, $self) = @_;
191 my ($self, $scalar, $length, $offset) = @_;
192 $self->ensure_open('w');
193 return *$self->{file
}->write($scalar, $length, $offset);
197 my ($self, $scalar, $length, $offset) = @_;
198 $self->ensure_open('r');
199 return *$self->{file
}->read($scalar, $length, $offset);
204 $self->ensure_open('r');
205 return *$self->{file
}->getlines() if wantarray;
206 return *$self->{file
}->getline();
210 my ($self, @args) = @_;
212 if (scalar @args == 2) {
213 my ($mode, $filename) = @args;
214 $self->set_filename($filename);
216 $self->_open_for_write();
217 } elsif ($mode eq '<') {
218 $self->_open_for_read();
220 croak
'Dpkg::Compression::FileHandle does not support ' .
224 croak
'Dpkg::Compression::FileHandle only supports open() ' .
227 return 1; # Always works (otherwise errors out)
231 my ($self, @args) = @_;
233 if (defined *$self->{file
}) {
234 $ret = *$self->{file
}->close(@args) if *$self->{file
}->opened();
243 my ($self, @args) = @_;
245 return *$self->{file
}->fileno(@args) if defined *$self->{file
};
250 # Since perl 5.12, an integer parameter is passed describing how the
251 # function got called, just ignore it.
252 my ($self, $param, @args) = @_;
254 return *$self->{file
}->eof(@args) if defined *$self->{file
};
259 my ($self, @args) = @_;
261 return *$self->{file
}->seek(@args) if defined *$self->{file
};
266 my ($self, @args) = @_;
268 return *$self->{file
}->tell(@args) if defined *$self->{file
};
273 my ($self, @args) = @_;
275 return *$self->{file
}->binmode(@args) if defined *$self->{file
};
283 =item $fh->set_compression($comp)
285 Defines the compression method used. $comp should one of the methods supported by
286 L<Dpkg::Compression> or "none" or "auto". "none" indicates that the file is
287 uncompressed and "auto" indicates that the method must be guessed based
288 on the filename extension used.
292 sub set_compression
{
293 my ($self, $method) = @_;
294 if ($method ne 'none' and $method ne 'auto') {
295 *$self->{compressor
}->set_compression($method);
297 *$self->{compression
} = $method;
300 =item $fh->set_compression_level($level)
302 Indicate the desired compression level. It should be a value accepted
303 by the function compression_is_valid_level() of L<Dpkg::Compression>.
307 sub set_compression_level
{
308 my ($self, $level) = @_;
309 *$self->{compressor
}->set_compression_level($level);
312 =item $fh->set_filename($name, [$add_comp_ext])
314 Use $name as filename when the file must be opened/created. If
315 $add_comp_ext is passed, it indicates whether the default extension
316 of the compression method must be automatically added to the filename
322 my ($self, $filename, $add_comp_ext) = @_;
323 *$self->{filename
} = $filename;
324 # Automatically add compression extension to filename
325 if (defined($add_comp_ext)) {
326 *$self->{add_comp_ext
} = $add_comp_ext;
328 my $comp_ext_regex = compression_get_file_extension_regex
();
329 if (*$self->{add_comp_ext
} and $filename =~ /\.$comp_ext_regex$/) {
330 warning
('filename %s already has an extension of a compressed file ' .
331 'and add_comp_ext is active', $filename);
335 =item $file = $fh->get_filename()
337 Returns the filename that would be used when the filehandle must
338 be opened (both in read and write mode). This function errors out
339 if "add_comp_ext" is enabled while the compression method is set
340 to "auto". The returned filename includes the extension of the compression
341 method if "add_comp_ext" is enabled.
347 my $comp = *$self->{compression
};
348 if (*$self->{add_comp_ext
}) {
349 if ($comp eq 'auto') {
350 croak
'automatic detection of compression is ' .
351 'incompatible with add_comp_ext';
352 } elsif ($comp eq 'none') {
353 return *$self->{filename
};
355 return *$self->{filename
} . '.' .
356 compression_get_file_extension
($comp);
359 return *$self->{filename
};
363 =item $ret = $fh->use_compression()
365 Returns "0" if no compression is used and the compression method used
366 otherwise. If the compression is set to "auto", the value returned
367 depends on the extension of the filename obtained with the get_filename()
372 sub use_compression
{
374 my $comp = *$self->{compression
};
375 if ($comp eq 'none') {
377 } elsif ($comp eq 'auto') {
378 $comp = compression_guess_from_filename
($self->get_filename());
379 *$self->{compressor
}->set_compression($comp) if $comp;
384 =item $real_fh = $fh->get_filehandle()
386 Returns the real underlying filehandle. Useful if you want to pass it
387 along in a derived class.
393 return *$self->{file
} if exists *$self->{file
};
398 sub _open_for_write
{
399 my ($self, %opts) = @_;
402 croak
'cannot reopen an already opened compressed file'
403 if exists *$self->{mode
};
405 if ($self->use_compression()) {
406 *$self->{compressor
}->compress(from_pipe
=> \
$filehandle,
407 to_file
=> $self->get_filename(), %opts);
409 CORE
::open($filehandle, '>', $self->get_filename)
410 or syserr
(g_
('cannot write %s'), $self->get_filename());
412 *$self->{mode
} = 'w';
413 *$self->{file
} = $filehandle;
417 my ($self, %opts) = @_;
420 croak
'cannot reopen an already opened compressed file'
421 if exists *$self->{mode
};
423 if ($self->use_compression()) {
424 *$self->{compressor
}->uncompress(to_pipe
=> \
$filehandle,
425 from_file
=> $self->get_filename(), %opts);
426 *$self->{allow_sigpipe
} = 1;
428 CORE
::open($filehandle, '<', $self->get_filename)
429 or syserr
(g_
('cannot read %s'), $self->get_filename());
431 *$self->{mode
} = 'r';
432 *$self->{file
} = $filehandle;
437 my $cmdline = *$self->{compressor
}{cmdline
} // '';
438 *$self->{compressor
}->wait_end_process(nocheck
=> *$self->{allow_sigpipe
});
439 if (*$self->{allow_sigpipe
}) {
441 unless (($?
== 0) || (POSIX
::WIFSIGNALED
($?
) &&
442 (POSIX
::WTERMSIG
($?
) == POSIX
::SIGPIPE
()))) {
443 subprocerr
($cmdline);
445 *$self->{allow_sigpipe
} = 0;
447 delete *$self->{mode
};
448 delete *$self->{file
};
453 =head1 DERIVED CLASSES
455 If you want to create a class that inherits from
456 Dpkg::Compression::FileHandle you must be aware that
457 the object is a reference to a GLOB that is returned by Symbol::gensym()
458 and as such it's not a HASH.
460 You can store internal data in a hash but you have to use
461 C<*$self->{...}> to access the associated hash like in the example below:
464 my ($self, $value) = @_;
465 *$self->{option} = $value;
470 =head2 Version 1.01 (dpkg 1.17.11)
472 New argument: $fh->ensure_open() accepts an %opts argument.
474 =head2 Version 1.00 (dpkg 1.15.6)
476 Mark the module as public.