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