This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Update IO-Compress to CPAN version 2.036
[perl5.git] / cpan / IO-Compress / lib / IO / Uncompress / Base.pm
CommitLineData
25f0751f
PM
1
2package IO::Uncompress::Base ;
3
4use strict ;
5use warnings;
6use bytes;
7
8our (@ISA, $VERSION, @EXPORT_OK, %EXPORT_TAGS);
93d092e2 9@ISA = qw(Exporter IO::File);
25f0751f
PM
10
11
529174d6 12$VERSION = '2.036';
25f0751f
PM
13
14use constant G_EOF => 0 ;
15use constant G_ERR => -1 ;
16
529174d6 17use IO::Compress::Base::Common 2.036 ;
25f0751f
PM
18
19use IO::File ;
20use Symbol;
21use Scalar::Util qw(readonly);
22use List::Util qw(min);
23use Carp ;
24
25%EXPORT_TAGS = ( );
26push @{ $EXPORT_TAGS{all} }, @EXPORT_OK ;
d54256af 27
25f0751f
PM
28sub smartRead
29{
30 my $self = $_[0];
31 my $out = $_[1];
32 my $size = $_[2];
33 $$out = "" ;
34
35 my $offset = 0 ;
cd0c0e65 36 my $status = 1;
25f0751f
PM
37
38
6ecef415
PM
39 if (defined *$self->{InputLength}) {
40 return 0
41 if *$self->{InputLengthRemaining} <= 0 ;
42 $size = min($size, *$self->{InputLengthRemaining});
2b4e0969
PM
43 }
44
25f0751f 45 if ( length *$self->{Prime} ) {
25f0751f
PM
46 $$out = substr(*$self->{Prime}, 0, $size) ;
47 substr(*$self->{Prime}, 0, $size) = '' ;
6ecef415
PM
48 if (length $$out == $size) {
49 *$self->{InputLengthRemaining} -= length $$out
50 if defined *$self->{InputLength};
51
25f0751f
PM
52 return length $$out ;
53 }
54 $offset = length $$out ;
55 }
56
57 my $get_size = $size - $offset ;
58
d54256af
PM
59 if (defined *$self->{FH}) {
60 if ($offset) {
61 # Not using this
62 #
63 # *$self->{FH}->read($$out, $get_size, $offset);
64 #
65 # because the filehandle may not support the offset parameter
66 # An example is Net::FTP
67 my $tmp = '';
cd0c0e65
CBW
68 $status = *$self->{FH}->read($tmp, $get_size) ;
69 substr($$out, $offset) = $tmp
70 if defined $status && $status > 0 ;
d54256af
PM
71 }
72 else
cd0c0e65 73 { $status = *$self->{FH}->read($$out, $get_size) }
d54256af 74 }
25f0751f
PM
75 elsif (defined *$self->{InputEvent}) {
76 my $got = 1 ;
77 while (length $$out < $size) {
78 last
79 if ($got = *$self->{InputEvent}->($$out, $get_size)) <= 0;
80 }
81
82 if (length $$out > $size ) {
25f0751f
PM
83 *$self->{Prime} = substr($$out, $size, length($$out));
84 substr($$out, $size, length($$out)) = '';
85 }
86
87 *$self->{EventEof} = 1 if $got <= 0 ;
88 }
89 else {
90 no warnings 'uninitialized';
91 my $buf = *$self->{Buffer} ;
92 $$buf = '' unless defined $$buf ;
25f0751f 93 substr($$out, $offset) = substr($$buf, *$self->{BufferOffset}, $get_size);
e7d45986
PM
94 if (*$self->{ConsumeInput})
95 { substr($$buf, 0, $get_size) = '' }
96 else
97 { *$self->{BufferOffset} += length($$out) - $offset }
25f0751f
PM
98 }
99
6ecef415
PM
100 *$self->{InputLengthRemaining} -= length($$out) #- $offset
101 if defined *$self->{InputLength};
25f0751f 102
cd0c0e65
CBW
103 if (! defined $status) {
104 $self->saveStatus($!) ;
105 return STATUS_ERROR;
106 }
107
25f0751f
PM
108 $self->saveStatus(length $$out < 0 ? STATUS_ERROR : STATUS_OK) ;
109
110 return length $$out;
111}
112
113sub pushBack
114{
115 my $self = shift ;
116
117 return if ! defined $_[0] || length $_[0] == 0 ;
118
119 if (defined *$self->{FH} || defined *$self->{InputEvent} ) {
120 *$self->{Prime} = $_[0] . *$self->{Prime} ;
2b4e0969 121 *$self->{InputLengthRemaining} += length($_[0]);
25f0751f
PM
122 }
123 else {
124 my $len = length $_[0];
125
126 if($len > *$self->{BufferOffset}) {
127 *$self->{Prime} = substr($_[0], 0, $len - *$self->{BufferOffset}) . *$self->{Prime} ;
128 *$self->{InputLengthRemaining} = *$self->{InputLength};
129 *$self->{BufferOffset} = 0
130 }
131 else {
132 *$self->{InputLengthRemaining} += length($_[0]);
133 *$self->{BufferOffset} -= length($_[0]) ;
134 }
135 }
136}
137
138sub smartSeek
139{
140 my $self = shift ;
141 my $offset = shift ;
142 my $truncate = shift;
529174d6 143 my $position = shift || SEEK_SET;
25f0751f
PM
144
145 # TODO -- need to take prime into account
146 if (defined *$self->{FH})
529174d6 147 { *$self->{FH}->seek($offset, $position) }
25f0751f 148 else {
529174d6
CBW
149 if ($position == SEEK_END) {
150 *$self->{BufferOffset} = length ${ *$self->{Buffer} } + $offset ;
151 }
152 elsif ($position == SEEK_CUR) {
153 *$self->{BufferOffset} += $offset ;
154 }
155 else {
156 *$self->{BufferOffset} = $offset ;
157 }
158
25f0751f
PM
159 substr(${ *$self->{Buffer} }, *$self->{BufferOffset}) = ''
160 if $truncate;
161 return 1;
162 }
163}
164
529174d6
CBW
165sub smartTell
166{
167 my $self = shift ;
168
169 if (defined *$self->{FH})
170 { return *$self->{FH}->tell() }
171 else
172 { return *$self->{BufferOffset} }
173}
174
25f0751f
PM
175sub smartWrite
176{
177 my $self = shift ;
178 my $out_data = shift ;
179
180 if (defined *$self->{FH}) {
181 # flush needed for 5.8.0
182 defined *$self->{FH}->write($out_data, length $out_data) &&
183 defined *$self->{FH}->flush() ;
184 }
185 else {
186 my $buf = *$self->{Buffer} ;
187 substr($$buf, *$self->{BufferOffset}, length $out_data) = $out_data ;
188 *$self->{BufferOffset} += length($out_data) ;
189 return 1;
190 }
191}
192
193sub smartReadExact
194{
195 return $_[0]->smartRead($_[1], $_[2]) == $_[2];
196}
197
198sub smartEof
199{
200 my ($self) = $_[0];
201 local $.;
202
e7d45986 203 return 0 if length *$self->{Prime} || *$self->{PushMode};
25f0751f
PM
204
205 if (defined *$self->{FH})
d54256af
PM
206 {
207 # Could use
208 #
209 # *$self->{FH}->eof()
210 #
211 # here, but this can cause trouble if
212 # the filehandle is itself a tied handle, but it uses sysread.
213 # Then we get into mixing buffered & non-buffered IO, which will cause trouble
214
215 my $info = $self->getErrInfo();
216
217 my $buffer = '';
218 my $status = $self->smartRead(\$buffer, 1);
219 $self->pushBack($buffer) if length $buffer;
220 $self->setErrInfo($info);
c23ee15d 221
d54256af
PM
222 return $status == 0 ;
223 }
25f0751f
PM
224 elsif (defined *$self->{InputEvent})
225 { *$self->{EventEof} }
226 else
227 { *$self->{BufferOffset} >= length(${ *$self->{Buffer} }) }
228}
229
230sub clearError
231{
232 my $self = shift ;
233
234 *$self->{ErrorNo} = 0 ;
235 ${ *$self->{Error} } = '' ;
236}
237
d54256af
PM
238sub getErrInfo
239{
240 my $self = shift ;
241
242 return [ *$self->{ErrorNo}, ${ *$self->{Error} } ] ;
243}
244
245sub setErrInfo
246{
247 my $self = shift ;
248 my $ref = shift;
249
250 *$self->{ErrorNo} = $ref->[0] ;
251 ${ *$self->{Error} } = $ref->[1] ;
252}
253
25f0751f
PM
254sub saveStatus
255{
256 my $self = shift ;
257 my $errno = shift() + 0 ;
25f0751f
PM
258
259 *$self->{ErrorNo} = $errno;
260 ${ *$self->{Error} } = '' ;
261
262 return *$self->{ErrorNo} ;
263}
264
265
266sub saveErrorString
267{
268 my $self = shift ;
269 my $retval = shift ;
270
25f0751f 271 ${ *$self->{Error} } = shift ;
c23ee15d 272 *$self->{ErrorNo} = @_ ? shift() + 0 : STATUS_ERROR ;
25f0751f 273
25f0751f
PM
274 return $retval;
275}
276
277sub croakError
278{
279 my $self = shift ;
280 $self->saveErrorString(0, $_[0]);
281 croak $_[0];
282}
283
284
285sub closeError
286{
287 my $self = shift ;
288 my $retval = shift ;
289
290 my $errno = *$self->{ErrorNo};
291 my $error = ${ *$self->{Error} };
292
293 $self->close();
294
295 *$self->{ErrorNo} = $errno ;
296 ${ *$self->{Error} } = $error ;
297
298 return $retval;
299}
300
301sub error
302{
303 my $self = shift ;
304 return ${ *$self->{Error} } ;
305}
306
307sub errorNo
308{
309 my $self = shift ;
310 return *$self->{ErrorNo};
311}
312
313sub HeaderError
314{
315 my ($self) = shift;
316 return $self->saveErrorString(undef, "Header Error: $_[0]", STATUS_ERROR);
317}
318
319sub TrailerError
320{
321 my ($self) = shift;
322 return $self->saveErrorString(G_ERR, "Trailer Error: $_[0]", STATUS_ERROR);
323}
324
325sub TruncatedHeader
326{
327 my ($self) = shift;
328 return $self->HeaderError("Truncated in $_[0] Section");
329}
330
2b4e0969
PM
331sub TruncatedTrailer
332{
333 my ($self) = shift;
334 return $self->TrailerError("Truncated in $_[0] Section");
335}
336
258133d1
PM
337sub postCheckParams
338{
339 return 1;
340}
341
25f0751f
PM
342sub checkParams
343{
344 my $self = shift ;
345 my $class = shift ;
346
347 my $got = shift || IO::Compress::Base::Parameters::new();
348
349 my $Valid = {
350 'BlockSize' => [1, 1, Parse_unsigned, 16 * 1024],
351 'AutoClose' => [1, 1, Parse_boolean, 0],
352 'Strict' => [1, 1, Parse_boolean, 0],
25f0751f
PM
353 'Append' => [1, 1, Parse_boolean, 0],
354 'Prime' => [1, 1, Parse_any, undef],
355 'MultiStream' => [1, 1, Parse_boolean, 0],
356 'Transparent' => [1, 1, Parse_any, 1],
357 'Scan' => [1, 1, Parse_boolean, 0],
358 'InputLength' => [1, 1, Parse_unsigned, undef],
359 'BinModeOut' => [1, 1, Parse_boolean, 0],
4e7676c7
SP
360 #'Encode' => [1, 1, Parse_any, undef],
361
e7d45986 362 #'ConsumeInput' => [1, 1, Parse_boolean, 0],
25f0751f
PM
363
364 $self->getExtraParams(),
365
25f0751f
PM
366 #'Todo - Revert to ordinary file on end Z_STREAM_END'=> 0,
367 # ContinueAfterEof
368 } ;
369
258133d1
PM
370 $Valid->{TrailingData} = [1, 1, Parse_writable_scalar, undef]
371 if *$self->{OneShot} ;
25f0751f
PM
372
373 $got->parse($Valid, @_ )
374 or $self->croakError("${class}: $got->{Error}") ;
375
258133d1
PM
376 $self->postCheckParams($got)
377 or $self->croakError("${class}: " . $self->error()) ;
25f0751f
PM
378
379 return $got;
380}
381
382sub _create
383{
384 my $obj = shift;
385 my $got = shift;
386 my $append_mode = shift ;
387
388 my $class = ref $obj;
389 $obj->croakError("$class: Missing Input parameter")
390 if ! @_ && ! $got ;
391
392 my $inValue = shift ;
393
258133d1
PM
394 *$obj->{OneShot} = 0 ;
395
25f0751f
PM
396 if (! $got)
397 {
398 $got = $obj->checkParams($class, undef, @_)
399 or return undef ;
400 }
401
402 my $inType = whatIsInput($inValue, 1);
403
404 $obj->ckInputParam($class, $inValue, 1)
405 or return undef ;
406
407 *$obj->{InNew} = 1;
408
409 $obj->ckParams($got)
258133d1 410 or $obj->croakError("${class}: " . *$obj->{Error});
25f0751f
PM
411
412 if ($inType eq 'buffer' || $inType eq 'code') {
413 *$obj->{Buffer} = $inValue ;
414 *$obj->{InputEvent} = $inValue
415 if $inType eq 'code' ;
416 }
417 else {
418 if ($inType eq 'handle') {
419 *$obj->{FH} = $inValue ;
420 *$obj->{Handle} = 1 ;
258133d1 421
25f0751f 422 # Need to rewind for Scan
258133d1
PM
423 *$obj->{FH}->seek(0, SEEK_SET)
424 if $got->value('Scan');
25f0751f
PM
425 }
426 else {
319fab50 427 no warnings ;
25f0751f
PM
428 my $mode = '<';
429 $mode = '+<' if $got->value('Scan');
430 *$obj->{StdIO} = ($inValue eq '-');
431 *$obj->{FH} = new IO::File "$mode $inValue"
432 or return $obj->saveErrorString(undef, "cannot open file '$inValue': $!", $!) ;
433 }
434
435 *$obj->{LineNo} = $. = 0;
436 setBinModeInput(*$obj->{FH}) ;
437
438 my $buff = "" ;
439 *$obj->{Buffer} = \$buff ;
440 }
441
4e7676c7
SP
442 if ($got->parsed('Encode')) {
443 my $want_encoding = $got->value('Encode');
444 *$obj->{Encoding} = getEncoding($obj, $class, $want_encoding);
445 }
446
25f0751f
PM
447
448 *$obj->{InputLength} = $got->parsed('InputLength')
449 ? $got->value('InputLength')
450 : undef ;
451 *$obj->{InputLengthRemaining} = $got->value('InputLength');
452 *$obj->{BufferOffset} = 0 ;
453 *$obj->{AutoClose} = $got->value('AutoClose');
454 *$obj->{Strict} = $got->value('Strict');
25f0751f
PM
455 *$obj->{BlockSize} = $got->value('BlockSize');
456 *$obj->{Append} = $got->value('Append');
457 *$obj->{AppendOutput} = $append_mode || $got->value('Append');
e7d45986 458 *$obj->{ConsumeInput} = $got->value('ConsumeInput');
25f0751f
PM
459 *$obj->{Transparent} = $got->value('Transparent');
460 *$obj->{MultiStream} = $got->value('MultiStream');
461
462 # TODO - move these two into RawDeflate
463 *$obj->{Scan} = $got->value('Scan');
464 *$obj->{ParseExtra} = $got->value('ParseExtra')
465 || $got->value('Strict') ;
25f0751f
PM
466 *$obj->{Type} = '';
467 *$obj->{Prime} = $got->value('Prime') || '' ;
468 *$obj->{Pending} = '';
469 *$obj->{Plain} = 0;
470 *$obj->{PlainBytesRead} = 0;
471 *$obj->{InflatedBytesRead} = 0;
e7d45986
PM
472 *$obj->{UnCompSize} = new U64;
473 *$obj->{CompSize} = new U64;
25f0751f
PM
474 *$obj->{TotalInflatedBytesRead} = 0;
475 *$obj->{NewStream} = 0 ;
476 *$obj->{EventEof} = 0 ;
477 *$obj->{ClassName} = $class ;
478 *$obj->{Params} = $got ;
479
e7d45986
PM
480 if (*$obj->{ConsumeInput}) {
481 *$obj->{InNew} = 0;
482 *$obj->{Closed} = 0;
483 return $obj
484 }
485
a1787f24 486 my $status = $obj->mkUncomp($got);
25f0751f
PM
487
488 return undef
489 unless defined $status;
490
c23ee15d
CBW
491 *$obj->{InNew} = 0;
492 *$obj->{Closed} = 0;
493
494 if ($status) {
495 # Need to try uncompressing to catch the case
496 # where the compressed file uncompresses to an
497 # empty string - so eof is set immediately.
498
499 my $out_buffer = '';
500
501 $status = $obj->read(\$out_buffer);
502
503 if ($status < 0) {
504 *$obj->{ReadStatus} = [ $status, $obj->error(), $obj->errorNo() ];
505 }
506
507 $obj->ungetc($out_buffer)
508 if length $out_buffer;
509 }
510 else {
25f0751f
PM
511 return undef
512 unless *$obj->{Transparent};
513
514 $obj->clearError();
515 *$obj->{Type} = 'plain';
516 *$obj->{Plain} = 1;
25f0751f
PM
517 $obj->pushBack(*$obj->{HeaderPending}) ;
518 }
519
520 push @{ *$obj->{InfoList} }, *$obj->{Info} ;
521
522 $obj->saveStatus(STATUS_OK) ;
523 *$obj->{InNew} = 0;
524 *$obj->{Closed} = 0;
525
526 return $obj;
527}
528
529sub ckInputParam
530{
531 my $self = shift ;
532 my $from = shift ;
533 my $inType = whatIsInput($_[0], $_[1]);
534
535 $self->croakError("$from: input parameter not a filename, filehandle, array ref or scalar ref")
536 if ! $inType ;
537
319fab50
PM
538# if ($inType eq 'filename' )
539# {
540# return $self->saveErrorString(1, "$from: input filename is undef or null string", STATUS_ERROR)
541# if ! defined $_[0] || $_[0] eq '' ;
542#
543# if ($_[0] ne '-' && ! -e $_[0] )
544# {
545# return $self->saveErrorString(1,
546# "input file '$_[0]' does not exist", STATUS_ERROR);
547# }
548# }
25f0751f
PM
549
550 return 1;
551}
552
553
554sub _inf
555{
556 my $obj = shift ;
557
558 my $class = (caller)[0] ;
559 my $name = (caller(1))[3] ;
560
561 $obj->croakError("$name: expected at least 1 parameters\n")
562 unless @_ >= 1 ;
563
564 my $input = shift ;
565 my $haveOut = @_ ;
566 my $output = shift ;
567
568
a1787f24 569 my $x = new IO::Compress::Base::Validator($class, *$obj->{Error}, $name, $input, $output)
25f0751f
PM
570 or return undef ;
571
572 push @_, $output if $haveOut && $x->{Hash};
258133d1
PM
573
574 *$obj->{OneShot} = 1 ;
25f0751f
PM
575
576 my $got = $obj->checkParams($name, undef, @_)
577 or return undef ;
578
258133d1
PM
579 if ($got->parsed('TrailingData'))
580 {
581 *$obj->{TrailingData} = $got->value('TrailingData');
582 }
583
e7d45986
PM
584 *$obj->{MultiStream} = $got->value('MultiStream');
585 $got->value('MultiStream', 0);
25f0751f 586
e7d45986 587 $x->{Got} = $got ;
25f0751f 588
e7d45986
PM
589# if ($x->{Hash})
590# {
591# while (my($k, $v) = each %$input)
592# {
593# $v = \$input->{$k}
594# unless defined $v ;
595#
596# $obj->_singleTarget($x, $k, $v, @_)
597# or return undef ;
598# }
599#
600# return keys %$input ;
601# }
25f0751f
PM
602
603 if ($x->{GlobMap})
604 {
605 $x->{oneInput} = 1 ;
606 foreach my $pair (@{ $x->{Pairs} })
607 {
608 my ($from, $to) = @$pair ;
e7d45986 609 $obj->_singleTarget($x, $from, $to, @_)
25f0751f
PM
610 or return undef ;
611 }
612
613 return scalar @{ $x->{Pairs} } ;
614 }
615
25f0751f
PM
616 if (! $x->{oneOutput} )
617 {
618 my $inFile = ($x->{inType} eq 'filenames'
619 || $x->{inType} eq 'filename');
620
621 $x->{inType} = $inFile ? 'filename' : 'buffer';
25f0751f
PM
622
623 foreach my $in ($x->{oneInput} ? $input : @$input)
624 {
625 my $out ;
626 $x->{oneInput} = 1 ;
627
e7d45986 628 $obj->_singleTarget($x, $in, $output, @_)
25f0751f 629 or return undef ;
25f0751f
PM
630 }
631
632 return 1 ;
633 }
634
635 # finally the 1 to 1 and n to 1
e7d45986 636 return $obj->_singleTarget($x, $input, $output, @_);
25f0751f
PM
637
638 croak "should not be here" ;
639}
640
641sub retErr
642{
643 my $x = shift ;
644 my $string = shift ;
645
646 ${ $x->{Error} } = $string ;
647
648 return undef ;
649}
650
651sub _singleTarget
652{
653 my $self = shift ;
654 my $x = shift ;
25f0751f
PM
655 my $input = shift;
656 my $output = shift;
657
e7d45986
PM
658 my $buff = '';
659 $x->{buff} = \$buff ;
25f0751f
PM
660
661 my $fh ;
662 if ($x->{outType} eq 'filename') {
663 my $mode = '>' ;
664 $mode = '>>'
665 if $x->{Got}->value('Append') ;
666 $x->{fh} = new IO::File "$mode $output"
667 or return retErr($x, "cannot open file '$output': $!") ;
668 binmode $x->{fh} if $x->{Got}->valueOrDefault('BinModeOut');
669
670 }
671
672 elsif ($x->{outType} eq 'handle') {
673 $x->{fh} = $output;
674 binmode $x->{fh} if $x->{Got}->valueOrDefault('BinModeOut');
675 if ($x->{Got}->value('Append')) {
676 seek($x->{fh}, 0, SEEK_END)
677 or return retErr($x, "Cannot seek to end of output filehandle: $!") ;
678 }
679 }
680
681
682 elsif ($x->{outType} eq 'buffer' )
683 {
684 $$output = ''
685 unless $x->{Got}->value('Append');
686 $x->{buff} = $output ;
687 }
688
689 if ($x->{oneInput})
690 {
e7d45986 691 defined $self->_rd2($x, $input, $output)
25f0751f
PM
692 or return undef;
693 }
694 else
695 {
25f0751f
PM
696 for my $element ( ($x->{inType} eq 'hash') ? keys %$input : @$input)
697 {
e7d45986 698 defined $self->_rd2($x, $element, $output)
25f0751f
PM
699 or return undef ;
700 }
701 }
702
703
704 if ( ($x->{outType} eq 'filename' && $output ne '-') ||
705 ($x->{outType} eq 'handle' && $x->{Got}->value('AutoClose'))) {
706 $x->{fh}->close()
707 or return retErr($x, $!);
25f0751f
PM
708 delete $x->{fh};
709 }
710
711 return 1 ;
712}
713
714sub _rd2
715{
716 my $self = shift ;
717 my $x = shift ;
718 my $input = shift;
e7d45986 719 my $output = shift;
25f0751f
PM
720
721 my $z = createSelfTiedObject($x->{Class}, *$self->{Error});
722
723 $z->_create($x->{Got}, 1, $input, @_)
724 or return undef ;
725
726 my $status ;
727 my $fh = $x->{fh};
728
e7d45986
PM
729 while (1) {
730
731 while (($status = $z->read($x->{buff})) > 0) {
732 if ($fh) {
733 print $fh ${ $x->{buff} }
734 or return $z->saveErrorString(undef, "Error writing to output file: $!", $!);
735 ${ $x->{buff} } = '' ;
736 }
737 }
738
739 if (! $x->{oneOutput} ) {
740 my $ot = $x->{outType} ;
741
742 if ($ot eq 'array')
743 { push @$output, $x->{buff} }
744 elsif ($ot eq 'hash')
745 { $output->{$input} = $x->{buff} }
746
747 my $buff = '';
748 $x->{buff} = \$buff;
25f0751f 749 }
e7d45986 750
319fab50 751 last if $status < 0 || $z->smartEof();
319fab50 752
e7d45986
PM
753 last
754 unless *$self->{MultiStream};
755
756 $status = $z->nextStream();
757
758 last
759 unless $status == 1 ;
25f0751f
PM
760 }
761
762 return $z->closeError(undef)
763 if $status < 0 ;
764
258133d1
PM
765 ${ *$self->{TrailingData} } = $z->trailingData()
766 if defined *$self->{TrailingData} ;
767
25f0751f
PM
768 $z->close()
769 or return undef ;
770
771 return 1 ;
772}
773
774sub TIEHANDLE
775{
776 return $_[0] if ref($_[0]);
777 die "OOPS\n" ;
778
779}
780
781sub UNTIE
782{
783 my $self = shift ;
784}
785
786
787sub getHeaderInfo
788{
789 my $self = shift ;
790 wantarray ? @{ *$self->{InfoList} } : *$self->{Info};
791}
792
793sub readBlock
794{
795 my $self = shift ;
796 my $buff = shift ;
797 my $size = shift ;
798
799 if (defined *$self->{CompressedInputLength}) {
800 if (*$self->{CompressedInputLengthRemaining} == 0) {
801 delete *$self->{CompressedInputLength};
802 *$self->{CompressedInputLengthDone} = 1;
803 return STATUS_OK ;
804 }
805 $size = min($size, *$self->{CompressedInputLengthRemaining} );
806 *$self->{CompressedInputLengthRemaining} -= $size ;
807 }
808
809 my $status = $self->smartRead($buff, $size) ;
c23ee15d 810 return $self->saveErrorString(STATUS_ERROR, "Error Reading Data: $!", $!)
cd0c0e65 811 if $status == STATUS_ERROR ;
25f0751f
PM
812
813 if ($status == 0 ) {
814 *$self->{Closed} = 1 ;
815 *$self->{EndStream} = 1 ;
816 return $self->saveErrorString(STATUS_ERROR, "unexpected end of file", STATUS_ERROR);
817 }
818
819 return STATUS_OK;
25f0751f
PM
820}
821
822sub postBlockChk
823{
824 return STATUS_OK;
825}
826
827sub _raw_read
828{
829 # return codes
830 # >0 - ok, number of bytes read
831 # =0 - ok, eof
832 # <0 - not ok
833
834 my $self = shift ;
835
836 return G_EOF if *$self->{Closed} ;
25f0751f
PM
837 return G_EOF if *$self->{EndStream} ;
838
839 my $buffer = shift ;
840 my $scan_mode = shift ;
841
842 if (*$self->{Plain}) {
843 my $tmp_buff ;
844 my $len = $self->smartRead(\$tmp_buff, *$self->{BlockSize}) ;
845
846 return $self->saveErrorString(G_ERR, "Error reading data: $!", $!)
cd0c0e65 847 if $len == STATUS_ERROR ;
25f0751f
PM
848
849 if ($len == 0 ) {
850 *$self->{EndStream} = 1 ;
851 }
852 else {
853 *$self->{PlainBytesRead} += $len ;
854 $$buffer .= $tmp_buff;
855 }
856
857 return $len ;
858 }
859
860 if (*$self->{NewStream}) {
861
e7d45986
PM
862 $self->gotoNextStream() > 0
863 or return G_ERR;
25f0751f
PM
864
865 # For the headers that actually uncompressed data, put the
866 # uncompressed data into the output buffer.
867 $$buffer .= *$self->{Pending} ;
868 my $len = length *$self->{Pending} ;
869 *$self->{Pending} = '';
870 return $len;
871 }
872
e7d45986 873 my $temp_buf = '';
25f0751f
PM
874 my $outSize = 0;
875 my $status = $self->readBlock(\$temp_buf, *$self->{BlockSize}, $outSize) ;
c23ee15d 876
25f0751f
PM
877 return G_ERR
878 if $status == STATUS_ERROR ;
879
880 my $buf_len = 0;
881 if ($status == STATUS_OK) {
e7d45986 882 my $beforeC_len = length $temp_buf;
25f0751f
PM
883 my $before_len = defined $$buffer ? length $$buffer : 0 ;
884 $status = *$self->{Uncomp}->uncompr(\$temp_buf, $buffer,
885 defined *$self->{CompressedInputLengthDone} ||
886 $self->smartEof(), $outSize);
319fab50 887
dcfdccf9
PM
888 # Remember the input buffer if it wasn't consumed completely
889 $self->pushBack($temp_buf) if *$self->{Uncomp}{ConsumesInput};
25f0751f
PM
890
891 return $self->saveErrorString(G_ERR, *$self->{Uncomp}{Error}, *$self->{Uncomp}{ErrorNo})
319fab50 892 if $self->saveStatus($status) == STATUS_ERROR;
25f0751f 893
258133d1 894 $self->postBlockChk($buffer, $before_len) == STATUS_OK
25f0751f
PM
895 or return G_ERR;
896
e11a3f9e 897 $buf_len = defined $$buffer ? length($$buffer) - $before_len : 0;
25f0751f 898
e7d45986
PM
899 *$self->{CompSize}->add($beforeC_len - length $temp_buf) ;
900
25f0751f
PM
901 *$self->{InflatedBytesRead} += $buf_len ;
902 *$self->{TotalInflatedBytesRead} += $buf_len ;
e7d45986 903 *$self->{UnCompSize}->add($buf_len) ;
4e7676c7 904
529174d6 905 $self->filterUncompressed($buffer, $before_len);
4e7676c7
SP
906
907 if (*$self->{Encoding}) {
908 $$buffer = *$self->{Encoding}->decode($$buffer);
909 }
25f0751f
PM
910 }
911
912 if ($status == STATUS_ENDSTREAM) {
913
914 *$self->{EndStream} = 1 ;
25f0751f
PM
915
916 my $trailer;
917 my $trailer_size = *$self->{Info}{TrailerLength} ;
918 my $got = 0;
919 if (*$self->{Info}{TrailerLength})
920 {
921 $got = $self->smartRead(\$trailer, $trailer_size) ;
922 }
923
924 if ($got == $trailer_size) {
925 $self->chkTrailer($trailer) == STATUS_OK
926 or return G_ERR;
927 }
928 else {
929 return $self->TrailerError("trailer truncated. Expected " .
930 "$trailer_size bytes, got $got")
931 if *$self->{Strict};
932 $self->pushBack($trailer) ;
933 }
934
258133d1
PM
935 # TODO - if want to file file pointer, do it here
936
e7d45986 937 if (! $self->smartEof()) {
25f0751f 938 *$self->{NewStream} = 1 ;
e7d45986
PM
939
940 if (*$self->{MultiStream}) {
941 *$self->{EndStream} = 0 ;
942 return $buf_len ;
943 }
25f0751f
PM
944 }
945
946 }
947
948
949 # return the number of uncompressed bytes read
950 return $buf_len ;
951}
952
2b4e0969
PM
953sub reset
954{
955 my $self = shift ;
956
957 return *$self->{Uncomp}->reset();
958}
959
960sub filterUncompressed
961{
962}
963
25f0751f
PM
964#sub isEndStream
965#{
966# my $self = shift ;
967# return *$self->{NewStream} ||
968# *$self->{EndStream} ;
969#}
970
e7d45986
PM
971sub nextStream
972{
973 my $self = shift ;
974
975 my $status = $self->gotoNextStream();
976 $status == 1
977 or return $status ;
978
979 *$self->{TotalInflatedBytesRead} = 0 ;
980 *$self->{LineNo} = $. = 0;
981
982 return 1;
983}
984
985sub gotoNextStream
986{
987 my $self = shift ;
988
989 if (! *$self->{NewStream}) {
990 my $status = 1;
991 my $buffer ;
992
993 # TODO - make this more efficient if know the offset for the end of
994 # the stream and seekable
995 $status = $self->read($buffer)
996 while $status > 0 ;
997
998 return $status
999 if $status < 0;
1000 }
1001
1002 *$self->{NewStream} = 0 ;
1003 *$self->{EndStream} = 0 ;
1004 $self->reset();
1005 *$self->{UnCompSize}->reset();
1006 *$self->{CompSize}->reset();
1007
6ecef415 1008 my $magic = $self->ckMagic();
e7d45986 1009
d54256af 1010 if ( ! defined $magic) {
315f9eea 1011 if (! *$self->{Transparent} || $self->eof())
f6fd7794
PM
1012 {
1013 *$self->{EndStream} = 1 ;
1014 return 0;
1015 }
e7d45986 1016
f6fd7794
PM
1017 $self->clearError();
1018 *$self->{Type} = 'plain';
1019 *$self->{Plain} = 1;
1020 $self->pushBack(*$self->{HeaderPending}) ;
1021 }
1022 else
1023 {
1024 *$self->{Info} = $self->readHeader($magic);
6ecef415 1025
f6fd7794
PM
1026 if ( ! defined *$self->{Info} ) {
1027 *$self->{EndStream} = 1 ;
1028 return -1;
1029 }
6ecef415 1030 }
e7d45986
PM
1031
1032 push @{ *$self->{InfoList} }, *$self->{Info} ;
1033
1034 return 1;
1035}
1036
25f0751f
PM
1037sub streamCount
1038{
1039 my $self = shift ;
1040 return 1 if ! defined *$self->{InfoList};
1041 return scalar @{ *$self->{InfoList} } ;
1042}
1043
1044sub read
1045{
1046 # return codes
1047 # >0 - ok, number of bytes read
1048 # =0 - ok, eof
1049 # <0 - not ok
1050
1051 my $self = shift ;
1052
c23ee15d
CBW
1053 if (defined *$self->{ReadStatus} ) {
1054 my $status = *$self->{ReadStatus}[0];
1055 $self->saveErrorString( @{ *$self->{ReadStatus} } );
1056 delete *$self->{ReadStatus} ;
1057 return $status ;
1058 }
1059
25f0751f 1060 return G_EOF if *$self->{Closed} ;
25f0751f
PM
1061
1062 my $buffer ;
1063
25f0751f
PM
1064 if (ref $_[0] ) {
1065 $self->croakError(*$self->{ClassName} . "::read: buffer parameter is read-only")
1066 if readonly(${ $_[0] });
1067
1068 $self->croakError(*$self->{ClassName} . "::read: not a scalar reference $_[0]" )
1069 unless ref $_[0] eq 'SCALAR' ;
1070 $buffer = $_[0] ;
1071 }
1072 else {
1073 $self->croakError(*$self->{ClassName} . "::read: buffer parameter is read-only")
1074 if readonly($_[0]);
1075
1076 $buffer = \$_[0] ;
1077 }
1078
1079 my $length = $_[1] ;
1080 my $offset = $_[2] || 0;
1081
93d092e2
PM
1082 if (! *$self->{AppendOutput}) {
1083 if (! $offset) {
1084 $$buffer = '' ;
1085 }
1086 else {
1087 if ($offset > length($$buffer)) {
1088 $$buffer .= "\x00" x ($offset - length($$buffer));
1089 }
1090 else {
1091 substr($$buffer, $offset) = '';
1092 }
1093 }
1094 }
c23ee15d
CBW
1095 elsif (! defined $$buffer) {
1096 $$buffer = '' ;
1097 }
93d092e2
PM
1098
1099 return G_EOF if !length *$self->{Pending} && *$self->{EndStream} ;
1100
25f0751f
PM
1101 # the core read will return 0 if asked for 0 bytes
1102 return 0 if defined $length && $length == 0 ;
1103
1104 $length = $length || 0;
1105
1106 $self->croakError(*$self->{ClassName} . "::read: length parameter is negative")
1107 if $length < 0 ;
1108
25f0751f
PM
1109 # Short-circuit if this is a simple read, with no length
1110 # or offset specified.
1111 unless ( $length || $offset) {
1112 if (length *$self->{Pending}) {
1113 $$buffer .= *$self->{Pending} ;
1114 my $len = length *$self->{Pending};
1115 *$self->{Pending} = '' ;
1116 return $len ;
1117 }
1118 else {
1119 my $len = 0;
1120 $len = $self->_raw_read($buffer)
1121 while ! *$self->{EndStream} && $len == 0 ;
1122 return $len ;
1123 }
1124 }
1125
1126 # Need to jump through more hoops - either length or offset
1127 # or both are specified.
f6fd7794 1128 my $out_buffer = *$self->{Pending} ;
319fab50 1129 *$self->{Pending} = '';
25f0751f 1130
93d092e2 1131
f6fd7794 1132 while (! *$self->{EndStream} && length($out_buffer) < $length)
25f0751f 1133 {
f6fd7794 1134 my $buf_len = $self->_raw_read(\$out_buffer);
25f0751f
PM
1135 return $buf_len
1136 if $buf_len < 0 ;
1137 }
1138
f6fd7794
PM
1139 $length = length $out_buffer
1140 if length($out_buffer) < $length ;
25f0751f 1141
258133d1
PM
1142 return 0
1143 if $length == 0 ;
1144
93d092e2
PM
1145 $$buffer = ''
1146 if ! defined $$buffer;
1147
1148 $offset = length $$buffer
1149 if *$self->{AppendOutput} ;
1150
f6fd7794
PM
1151 *$self->{Pending} = $out_buffer;
1152 $out_buffer = \*$self->{Pending} ;
1153
93d092e2
PM
1154 substr($$buffer, $offset) = substr($$out_buffer, 0, $length) ;
1155 substr($$out_buffer, 0, $length) = '' ;
25f0751f
PM
1156
1157 return $length ;
1158}
1159
1160sub _getline
1161{
1162 my $self = shift ;
cd0c0e65 1163 my $status = 0 ;
25f0751f
PM
1164
1165 # Slurp Mode
1166 if ( ! defined $/ ) {
1167 my $data ;
cd0c0e65 1168 1 while ($status = $self->read($data)) > 0 ;
c23ee15d 1169 return ($status, \$data);
258133d1
PM
1170 }
1171
1172 # Record Mode
1173 if ( ref $/ eq 'SCALAR' && ${$/} =~ /^\d+$/ && ${$/} > 0) {
1174 my $reclen = ${$/} ;
1175 my $data ;
cd0c0e65 1176 $status = $self->read($data, $reclen) ;
c23ee15d 1177 return ($status, \$data);
25f0751f
PM
1178 }
1179
1180 # Paragraph Mode
1181 if ( ! length $/ ) {
1182 my $paragraph ;
cd0c0e65 1183 while (($status = $self->read($paragraph)) > 0 ) {
25f0751f
PM
1184 if ($paragraph =~ s/^(.*?\n\n+)//s) {
1185 *$self->{Pending} = $paragraph ;
1186 my $par = $1 ;
c23ee15d 1187 return (1, \$par);
25f0751f
PM
1188 }
1189 }
c23ee15d 1190 return ($status, \$paragraph);
25f0751f
PM
1191 }
1192
258133d1 1193 # $/ isn't empty, or a reference, so it's Line Mode.
25f0751f
PM
1194 {
1195 my $line ;
258133d1 1196 my $p = \*$self->{Pending} ;
cd0c0e65 1197 while (($status = $self->read($line)) > 0 ) {
258133d1
PM
1198 my $offset = index($line, $/);
1199 if ($offset >= 0) {
1200 my $l = substr($line, 0, $offset + length $/ );
1201 substr($line, 0, $offset + length $/) = '';
1202 $$p = $line;
c23ee15d 1203 return (1, \$l);
25f0751f
PM
1204 }
1205 }
258133d1 1206
c23ee15d 1207 return ($status, \$line);
25f0751f
PM
1208 }
1209}
1210
1211sub getline
1212{
1213 my $self = shift;
c23ee15d
CBW
1214
1215 if (defined *$self->{ReadStatus} ) {
1216 $self->saveErrorString( @{ *$self->{ReadStatus} } );
1217 delete *$self->{ReadStatus} ;
1218 return undef;
1219 }
1220
1221 return undef
1222 if *$self->{Closed} || (!length *$self->{Pending} && *$self->{EndStream}) ;
1223
25f0751f
PM
1224 my $current_append = *$self->{AppendOutput} ;
1225 *$self->{AppendOutput} = 1;
c23ee15d
CBW
1226
1227 my ($status, $lineref) = $self->_getline();
25f0751f 1228 *$self->{AppendOutput} = $current_append;
c23ee15d
CBW
1229
1230 return undef
1231 if $status < 0 || length $$lineref == 0 ;
1232
1233 $. = ++ *$self->{LineNo} ;
1234
25f0751f
PM
1235 return $$lineref ;
1236}
1237
1238sub getlines
1239{
1240 my $self = shift;
1241 $self->croakError(*$self->{ClassName} .
1242 "::getlines: called in scalar context\n") unless wantarray;
1243 my($line, @lines);
258133d1
PM
1244 push(@lines, $line)
1245 while defined($line = $self->getline);
25f0751f
PM
1246 return @lines;
1247}
1248
1249sub READLINE
1250{
1251 goto &getlines if wantarray;
1252 goto &getline;
1253}
1254
1255sub getc
1256{
1257 my $self = shift;
1258 my $buf;
1259 return $buf if $self->read($buf, 1);
1260 return undef;
1261}
1262
1263sub ungetc
1264{
1265 my $self = shift;
1266 *$self->{Pending} = "" unless defined *$self->{Pending} ;
1267 *$self->{Pending} = $_[0] . *$self->{Pending} ;
1268}
1269
1270
1271sub trailingData
1272{
1273 my $self = shift ;
25f0751f
PM
1274
1275 if (defined *$self->{FH} || defined *$self->{InputEvent} ) {
1276 return *$self->{Prime} ;
1277 }
1278 else {
1279 my $buf = *$self->{Buffer} ;
1280 my $offset = *$self->{BufferOffset} ;
258133d1 1281 return substr($$buf, $offset) ;
25f0751f
PM
1282 }
1283}
1284
1285
1286sub eof
1287{
1288 my $self = shift ;
1289
1290 return (*$self->{Closed} ||
1291 (!length *$self->{Pending}
1292 && ( $self->smartEof() || *$self->{EndStream}))) ;
1293}
1294
1295sub tell
1296{
1297 my $self = shift ;
1298
1299 my $in ;
1300 if (*$self->{Plain}) {
1301 $in = *$self->{PlainBytesRead} ;
1302 }
1303 else {
1304 $in = *$self->{TotalInflatedBytesRead} ;
1305 }
1306
1307 my $pending = length *$self->{Pending} ;
1308
1309 return 0 if $pending > $in ;
1310 return $in - $pending ;
1311}
1312
1313sub close
1314{
1315 # todo - what to do if close is called before the end of the gzip file
1316 # do we remember any trailing data?
1317 my $self = shift ;
1318
1319 return 1 if *$self->{Closed} ;
1320
1321 untie *$self
1322 if $] >= 5.008 ;
1323
1324 my $status = 1 ;
1325
1326 if (defined *$self->{FH}) {
1327 if ((! *$self->{Handle} || *$self->{AutoClose}) && ! *$self->{StdIO}) {
25f0751f
PM
1328 local $.;
1329 $! = 0 ;
1330 $status = *$self->{FH}->close();
1331 return $self->saveErrorString(0, $!, $!)
1332 if !*$self->{InNew} && $self->saveStatus($!) != 0 ;
1333 }
1334 delete *$self->{FH} ;
1335 $! = 0 ;
1336 }
1337 *$self->{Closed} = 1 ;
1338
1339 return 1;
1340}
1341
1342sub DESTROY
1343{
1344 my $self = shift ;
e11a3f9e
PM
1345 local ($., $@, $!, $^E, $?);
1346
25f0751f
PM
1347 $self->close() ;
1348}
1349
1350sub seek
1351{
1352 my $self = shift ;
1353 my $position = shift;
1354 my $whence = shift ;
1355
1356 my $here = $self->tell() ;
1357 my $target = 0 ;
1358
1359
1360 if ($whence == SEEK_SET) {
1361 $target = $position ;
1362 }
1363 elsif ($whence == SEEK_CUR) {
1364 $target = $here + $position ;
1365 }
1366 elsif ($whence == SEEK_END) {
1367 $target = $position ;
1368 $self->croakError(*$self->{ClassName} . "::seek: SEEK_END not allowed") ;
1369 }
1370 else {
1371 $self->croakError(*$self->{ClassName} ."::seek: unknown value, $whence, for whence parameter");
1372 }
1373
1374 # short circuit if seeking to current offset
10c2b2bb
PM
1375 if ($target == $here) {
1376 # On ordinary filehandles, seeking to the current
1377 # position also clears the EOF condition, so we
1378 # emulate this behavior locally while simultaneously
1379 # cascading it to the underlying filehandle
1380 if (*$self->{Plain}) {
1381 *$self->{EndStream} = 0;
1382 seek(*$self->{FH},0,1) if *$self->{FH};
1383 }
1384 return 1;
1385 }
25f0751f
PM
1386
1387 # Outlaw any attempt to seek backwards
1388 $self->croakError( *$self->{ClassName} ."::seek: cannot seek backwards")
1389 if $target < $here ;
1390
1391 # Walk the file to the new offset
1392 my $offset = $target - $here ;
1393
4e7676c7
SP
1394 my $got;
1395 while (($got = $self->read(my $buffer, min($offset, *$self->{BlockSize})) ) > 0)
1396 {
1397 $offset -= $got;
1398 last if $offset == 0 ;
1399 }
25f0751f 1400
319fab50 1401 $here = $self->tell() ;
4e7676c7 1402 return $offset == 0 ? 1 : 0 ;
25f0751f
PM
1403}
1404
1405sub fileno
1406{
1407 my $self = shift ;
1408 return defined *$self->{FH}
1409 ? fileno *$self->{FH}
1410 : undef ;
1411}
1412
1413sub binmode
1414{
1415 1;
1416# my $self = shift ;
1417# return defined *$self->{FH}
1418# ? binmode *$self->{FH}
1419# : 1 ;
1420}
1421
1422sub opened
1423{
1424 my $self = shift ;
1425 return ! *$self->{Closed} ;
1426}
1427
1428sub autoflush
1429{
1430 my $self = shift ;
1431 return defined *$self->{FH}
1432 ? *$self->{FH}->autoflush(@_)
1433 : undef ;
1434}
1435
1436sub input_line_number
1437{
1438 my $self = shift ;
1439 my $last = *$self->{LineNo};
1440 $. = *$self->{LineNo} = $_[1] if @_ ;
1441 return $last;
1442}
1443
1444
1445*BINMODE = \&binmode;
1446*SEEK = \&seek;
1447*READ = \&read;
1448*sysread = \&read;
1449*TELL = \&tell;
1450*EOF = \&eof;
1451
1452*FILENO = \&fileno;
1453*CLOSE = \&close;
1454
1455sub _notAvailable
1456{
1457 my $name = shift ;
25f0751f
PM
1458 return sub { croak "$name Not Available: File opened only for intput" ; } ;
1459}
1460
1461
1462*print = _notAvailable('print');
1463*PRINT = _notAvailable('print');
1464*printf = _notAvailable('printf');
1465*PRINTF = _notAvailable('printf');
1466*write = _notAvailable('write');
1467*WRITE = _notAvailable('write');
1468
1469#*sysread = \&read;
1470#*syswrite = \&_notAvailable;
1471
25f0751f
PM
1472
1473
1474package IO::Uncompress::Base ;
1475
1476
14771 ;
1478__END__
1479
1480=head1 NAME
1481
25f0751f
PM
1482IO::Uncompress::Base - Base Class for IO::Uncompress modules
1483
25f0751f
PM
1484=head1 SYNOPSIS
1485
1486 use IO::Uncompress::Base ;
1487
1488=head1 DESCRIPTION
1489
25f0751f 1490This module is not intended for direct use in application code. Its sole
cd0c0e65 1491purpose if to to be sub-classed by IO::Uncompress modules.
25f0751f 1492
25f0751f
PM
1493=head1 SEE ALSO
1494
9b5fd1d4 1495L<Compress::Zlib>, L<IO::Compress::Gzip>, L<IO::Uncompress::Gunzip>, L<IO::Compress::Deflate>, L<IO::Uncompress::Inflate>, L<IO::Compress::RawDeflate>, L<IO::Uncompress::RawInflate>, L<IO::Compress::Bzip2>, L<IO::Uncompress::Bunzip2>, L<IO::Compress::Lzma>, L<IO::Uncompress::UnLzma>, L<IO::Compress::Xz>, L<IO::Uncompress::UnXz>, L<IO::Compress::Lzop>, L<IO::Uncompress::UnLzop>, L<IO::Compress::Lzf>, L<IO::Uncompress::UnLzf>, L<IO::Uncompress::AnyInflate>, L<IO::Uncompress::AnyUncompress>
25f0751f
PM
1496
1497L<Compress::Zlib::FAQ|Compress::Zlib::FAQ>
1498
1499L<File::GlobMapper|File::GlobMapper>, L<Archive::Zip|Archive::Zip>,
1500L<Archive::Tar|Archive::Tar>,
1501L<IO::Zlib|IO::Zlib>
1502
25f0751f
PM
1503=head1 AUTHOR
1504
cb7abd7f 1505This module was written by Paul Marquess, F<pmqs@cpan.org>.
25f0751f 1506
25f0751f
PM
1507=head1 MODIFICATION HISTORY
1508
1509See the Changes file.
1510
1511=head1 COPYRIGHT AND LICENSE
25f0751f 1512
cd0c0e65 1513Copyright (c) 2005-2011 Paul Marquess. All rights reserved.
25f0751f
PM
1514
1515This program is free software; you can redistribute it and/or
1516modify it under the same terms as Perl itself.
1517