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