ÿØÿà JFIF    ÿÛ „ !.%+&8&+/1555$;@;4?.451 4,$,44444444444414444444444444444444444444444444444444ÿÀ  á á" ÿÄ     ÿÄ ?    !1AQaq"2‘¡±ÁðBRbrÑá#‚’¢²3S CñÿÄ   ÿÄ !    !1QAa‘2ÿÚ   ? 5˜Z¯V¦cø)›t/? z¨±>Õ5€¶‹Á¤·¼z¼Ü¬+ñ®v¤¨_ˆR­BFn©—˜ý®ç̝P8gýt·ÉSTŦˆìät?þé¼íìN/Þa)ì–í6ô… Ï¿øÃj´¿KÇü]ÿ ªô¹-eKànëÕHTx}ýSÜ›ÿ ”7Ø×&µ<¦  ¥ÑO¶[Ù¯ä¨ÞÃÿ PZ-¬;#õ|•oaÿ ©CìÞz3˜öː/¤­ñTûIØ}š^ mÓ%ªxˆ¥ÉŸu=Z+ISe¿45™¼u;ú&WØ÷€æßQ™®{|íx*TC“#ZŠìZ§²‹ 6pv…³¿¡äª*áZÐ%ÒOáˆo"x«OHk w±æ+¬V(kMúŸ5Vö«$ ÁrÏbàb57/luR ¸ÑÛj Òµì`Мq­û žICÀÊ•©4€Âcà¨Ï€O´<èÐ:›ù(Ë^L8þ‘ÍÌ#¸Ð_Ì©ÙK(Öz 4¬û+¸;ü’V’84‘¬ÃŽ:[â‡ÔÌáõp¢~§ªlæ£ö{®G>J¼"°‡7¯ÆÉèßû ‹É‹§ÁòÃýâßî ^ƾÙõ‹×óH#«LP½ïX=xÑÍ$|W?•~• îëÔ©ª‹ {ÝT…Kÿ ”hûâá)J*ö˜–ÔU;iÇ€/ ÆþjóZ\ýwØ=Ìm ºèËL9 ýèÆð/¨’¥öo=nË.%Îì ŽÕ¯È|{Oj²ƒE6e/ßdÄõ²Ìâ1O®ò×TsəԸhOMýíMˆ¿¼H˜l²,7Â¥#MF/Úf°Ö½± ¸–dr‹NýÊ íjqx{œÉ ä-È ¦ øÄër¨q°ð †nцýÑÄÆ’mä…n<0È™;ÁÝá¯ÁZƒ7FÀmì­ É&9ˆîéi¶ùN§Y• ÃZãAâ?•‡©‰ , ó¾IŸŠc1 4â&y­&pŠ­6;M À 0¹qç»p.á …ŸÅáK@%6·y6ƒ‰3?”úºŽ‰éX5ªPT §µ!=Mž«Ú½‹ÅgÂSâÉaþÓoö–¯ÁÔìR>5éÿ üs¶ÆUcÌ kÇR ]ÿ ù¬¼«VŽ;Â|‡~¢¦”ÏŰæ {L™Õ°Óv¹ò¸írޡעCÃ!íVÕ {¶»sŒNPg/ "uÕbkm²“$ďå¿é¹§°½æz¯6 †s¿!s–wÚÝ“™Œ °.ûj>·+™Òa…©Œ&rÝÎtÛë긪Ît’LAVp%c Úý[ÄzJ¾ÇàXXç@˜ó<êL]·T˜¾¥1Ó©V‡g´æ½¦Ý@¹óø!_@´ÞâSÁ —S3™•& ]@JHÚý©ZŽ €×æÔr»Áf!‡yÞ4Mv*èÓã_{‘åóUuљØ«Oïé*®EvÑ Œ÷‡U \"㪒ÍK+À 4“M¡ï:0¥5í!'<@î´”>Ç»&Z–ïCCV˜Ì5Šo&îhè.žû |ÓK©h$s6KìŒëã)¹hI¦GïOåóI;ììü#É$Š0…Ææ¥TØ.5­¾gn´ “ÂÖ\:hœ89G)J@„}œ:’Ò{/Š"¦_Æ×7Æ3VÇŠÊa]ÚŒÙ€Ä–=®uÁßâACZƒ§§£ Qnâ:«,×{tyø¬iÛcœÜÄ€H½ÄÍCk´÷šß .W'b¤Íåh]÷€=,Žv×cÚEÚHXJX¶îo¨FÒtèöŸ>ªª6[J®Fµ£sGÁeqõfe\íjÒÐïÄÐGˆe1Ø‹.Ø”‘Ëuø Y­ˆÜ ŽG|zùªüMpDnQWÄ”%JŠ™)â*p@Örš«ÕT2Ð%ˆG#ª„ ·¤!°ŸOTÂT¸aÚ%4&h™LµšØüÐ.F¿²ÐÞ_Ç‚¾ÅÃaÜ÷09Æ q€öy˜v‡85õN÷]¬äѼóS{°_MެúÔ#°Ç¸0åÞè2ëôPcvÆw9®ií1Ä8F™˜à‰´+‰Ik1òÝ7“Ñ×ÒsÝ\x‚h`ÞÑ`ó"|µEcý£n˜h`}GÞ !±ù²Ápü²ß6 0ïi󜵩SÈÇ7˜-ÕURO˜¦´f$ªž-Í6(œ}<„ éc øs]ŽŽ„*—¾ ìdŽ„)méª\¿êÎIg¾ØÞ~I#C/¼¼´EÁÈŽi8“©õådô·>euä ƒ'Ê×लR1ÉJE1ÐAát`t;ÇР%Ý<‡¥„ÍÆ`×Oyó)õiI€ñQaŸ4Ûù\áàaÃÔ¹HÃu¹*k€¦<„e S‡&õÏ B!ŽhüÞ`yj}mªf×\¿ Ç~æ­9‡û\՞Ǖg²1Žû5V7 !àöšm° c`ܬøÇìµÒ'P"?…´Ö,"§^•õލsÔ)6˜sæéÍR¼ ò|Sl”‹7 nPW Gòú÷½§O¯‡„l¡kSÞŒr½PÊ@æ¢pŽ-mÿ #Ÿ˜Àº¶Áä¦;ïÔæ$1££`“Õ>„—·ž)ßð³ñ#Ï Ô$¶œ‰ÊE‹À;÷º ¯«P:Ñ”8–IÊtpÞ3ª“>ê“þës4ò2OÏÕ­±zô†Õ§‰.÷ä¸;¿˜“'œ›žª}«Œ{ª±Ì 9ÔóÞÕ‡0 $íWV3Üì¬ —@kÝ4@¿r¼±½¬™›?øØæ´'Áé®CË3-g$˜ö‡×auÚi´Žp/êÛ æF›Ú2v‹ã¿¿,nB1̨ƃqÞa5͝@&Æû“él÷ \C²½UÍc ¯k×¢U ÖéQå™—-r wô ÞÏ<Ò=&=ÿ Ôê Òêˈt,i—;LîÜ á¸*ÚÃ1$êL•LÍ <É)ýÐà’ ;F™{ƒ™˜€&'}‚ãÄK`¡ÞT@I;®žZóè‚s’7®°›+§O­Åq©é»²9<Ô J ¼9O’HL»Ùïì¸rk¼Ž_ý‘TŸu[²ßÚŒ·ü÷B%¯E ŸÔX5êO´ Ç•€’I0 ÉJX` ñ¹õ%;µŸD‘«´€àwÒ™U ûئžÖö\×®×´8 ½‡ºÐÆÓ§?Àkmœ=;d5*@-ì0F Rªýš[Ü6âö̃ڸr*KA9· u*µæ£?U¸Âêí†8@¦X4 e-ò„0s{ HâUpU?¼mñRa°®a%Ð'tÉ×’\¾ÊÉ]t›h>·(Ë@R¼¡Ãt h}’O÷au<+nT…Ö…MӐ??Óe95 q>í/;&JSû °¯ÊéÞ øƒ*Ã2½Ài&:nôUl=¾¿5eˆ3”ñc|Ú2V”>„»&eE;«ÚäC p¢Û úy 9š[ŒÌx¼擼A&DåÒ¯ˆ¤ÀÌ;"˜ ÏQä¸åhÊ}Ûq«Û0WžÒ|»€ø®öCm5•\ÇÀ§Pe3£]0ÃàLDÉ‰1øªxjgwT‚÷¿LΨK‹›ùs—xˆÜ±µ kæ¸f‰‰ÜGk/LÛØ6d9ò¶ùA{ƒA3š/¬D¬khÓk‰`˜"㯒r¿±Óã jx‡°e}<Ñø\3y:'À•/h½Í€Ç4~g ?Û(¼]v‘ªlKÎâ~?O‚W%{Ì:“'©úNq¾›úo(X’¥¯ˆ nFê{Ç€ü?º'ë ø‹ì Þ09ŒÌç9Æ —ËC`j@ÓÄ(+a‹un¸#ÂꟋ{K`‘ÑÍÍ'à´»/Û,KW;Þ4²þð ï Nm|~fGÏ(…³Ã)«1ö­Õ ¥‡¨©ƒÃ™ü-s=à=U66Ï«Ýc蓦W¹íž®›nÔ%êÇìŒ<#Ü×84ån®Ð ÒåOC` ñânÑs‡¢ç 1õ%Îhì½Ã½® e:ݼUZo™`  ÅZŸŒÊ«ê1ÏÄo$q¹Þ€©ˆhÐÉä¯ñ[!…Ú˜àJ:x2$Íß&PåT£6ç— ‡Í*4Ýšçjÿ ‰É nófÐ ó(L5C•åÆ\rMÒ@ò }y-W}™üýVù—ú¢=Ù”c®‘< M ž ´Phr ¦©TD ‘ù.$´÷O‡‘V2Æò.=IUŒ=ž‡â¬i™aþÓåÙ?òUø'ØÖ•.~* šTŒ!•-×áºTâ®ä#õü'´ eýlYÅÓeÕKÂrT"CÚ@u!Óxƒ{š3€}1¿(r}%«nËamjÑ%ÑNEò v ˜à  σöK³,*º.àzù¨™Ó ÚçâU¦*¿ 9{%Ö¹ njûdaXöb) kÛÆ±ûÓ\°M7ˆÂ=û›ç¿Ã‚­V»Cg–8ÙêE- j)k$º`Ã-ùEýeBÆÇ]c¡°ñty&Òd0nõ'¡W+ƒ*|–øµFa\GQªEAÔp5\Ǽ·¼Ç8·õ -â§Ú[ ‡ uZeÖ 3}×d'+¹:ð+K†Û®s!Ï$úe€<Û”x)1»a­¡LC]¸µík…ÚàA»AYº{†ªS[¦5HÒ7ù --,ísòDØ€èk ÞÀîÜ ò@â( ËNˆë›4ô½•/¦o‡€Û7 ê•ÆêòðÜy'Án½µ á˜ݦ ndeo…[ì¶Ê,¥R³Ä=À±—–ß;£™´ñSâ*g§”ïaið‘Jå~™ÓÞ ß³Õ¢»8x埒²52>AÊb&-÷\7´éÄù€T˜,w;3{ï˜k…à¹ÄqÀ«œ{€\ ˆ¾[´¨јr &Úé„Ívˆ±8†¿]|¬ņ4I×pÞS1ÈÖz‰#Ìv‡G!YNògñ:màTz¢Ý1ô©^O=~ë|5Bã™ç•¼µõ•bÆ@úÕS¬ÈŒ#¬zünrŸ û” Z²•èðV"ÁHÚý©wÝ €7¼Ìu1hÑa3Éä û f$o¿É ™Ú›ÝçnpÒ3äÌ3†Í§,Äï]$‰/pê †«À¼¸e9­Æê_C]žƒ·ý·frÁN«, E=›Çq -‰öŒ:aÏ¿±í&£Í:-} 84‘ÿ eƒQÑeëSsuiA ³g㟥ú£?ÿ ʼn*”“÷aühe:ÊWa@ÒÞk±eØ] F Ô—r.åä˜ @ö¥ªZoÐýYL·¥S²G/‡ñ <~*ZÆ´è>JlòàÛÆ½ÿ 窘ìGN¢:I®KšJp/`íIÁÀõ#Ä-€ö­šµŒoF4|ÆQØÆ@Ì|£Ô…¢À{9˜è½Üó›€ôYÒÎYsið;ís¤€à²ˆ‚4qÉVŒI$ ‰"° æµ8cXGjœˏ¡Aâý•ËÜ¢ûï e·çLx']á"oÅÎê3¯Ç—¹”ó0nå‚âg{Œñ> S´˜îè°g238‚ãköÝfÚd´6Ò€;ò÷±¢™¼›º ¢Æ'¥Ðx'e¬ç ]bÈÆV¢ó‹kýBO ðÊâ$Ÿ!×T 3Mýמ žìٍàÌü‘8÷€àæØ8æ©6‰©L´«…oãpð„~Çk‰!ñ;‹”ÛžÍ àž±z Ÿôû øŸÝužÏ;ÿ #|u6™Þ¬ÚˆÐõA4¶â|ôl|Ê2ŽÇ¤ÝÅÇY.<#Aí.k§hóF‚”Y; M½Ö4hŸ4&›­¿tès´%FìL¥£Ãk‰ÇT¤haÁ¤ÚxfÉ`ÑìË›>i 3t‚:,–+^÷´–{Û–Nxi"x‘Ûg î¨>¥Õ܁ùZH,2Û“:8xÊ¢Çí9.É-Ìâã-=çjwµS˜dütžçwýGòú®®ûº_ˆýx$–¡ãøO EÚÛÏ÷R„×w+3£Á£öUMyR²¹âŒ°š›¸Ñãò9§Ó_Dl+Ùßc›úšGÅÌc†Ž!Ko=¶.‘Îÿ c²(2®V mª.ÿ ¹B›¹å ù„öŸSV>™ü¯$y:G¢Z×àøúdî¹û­·ýÇ´:•c LÍõi_‹ö+ÎæGÊè>OŠ•äž´§Þ{X}¨1ÚTc›»Qþ•êô°t¿OP?eæ~É{5]•ÙR£r5†nZ\ã@ &îJõ ¾àC°þV>fé¥/ü5ñÊIº_é5 ;e­h<@ Ä&æÃëE%;X,ÒãÆÞ`Oò¦kŸm#˜!ÀyÄ¢| óLšò¥Ä` ¶R=|ÈCâh5ò3DˆïF†ðÒ#ÅìÛœ?¸yhBãœí ZxßÎÄhºRK„`Þödvײ™ÀÈÑÒgŒuY w³%†ƒÓzõ ÖÏp‚dH®¦A´ù§»ÓÇMæ~)ˆð‡û:ù&Ä •vGD´À n ݇¼Ö8Fö óáà£~Ë¥x`oK|Ä?fxiØü%pìR>éò+Û±éÎ>núlFŤ'tq8LZÏvÃ?„¡ß±È⽆¯³íü@x|PöUäèØã¡ð‚ŒAìÏ"vÍwóŸÍ{ ý0.z È•Ö{,N¡£¡ŸKÕÙž>Ýœþ ÍÀ°<×EA!Å‚D™IúOÍ¡>ôG}Â` ÍßkÜL™Ž Þð™ {IøF²¹òQ3&!ÃÂÞz.d&Ï-sH¸,Ôõ˜ŽP€ 77ˆÝ¼ÊëÜw =cÕ Ú,ØÐ5ÎYÐ)ì´öœgŒ[¤ßv㙑8心>h]§µháYš£²ºÑ.{Ï7Sð•?´~×SÃKýJÛ˜ ™Íäiúu<µX¶1õ^kâçIÑ£sZ4h>j*ÔšD:4­¿_ ÷¸ Õxæÿ ¸?Mù _•­ÊÐ ä ÷ý ÑwL œ­ïnTkÛUÍN©ë:¦fV ¶ÜÔÜMªÅâA½–¿R×TXš-%iTÊT•‡Ù‚JôϐZxWÑè‰f‰òG º ×Õû2aZ7OU3[“×AT–ÞŒ…-‘¤”Ì ì&(ˆ¿­•ƒkï’:ðY¦W‘ Å)“†‘˜³Åtcø˜ñTÂwÚÇ4|üLÇªí–v- qˆèU qPE.†â‘˜µ Æ,ÐÅs]8¾„oúÑ i>ÜxxÈó)ƒ ´æÁâØ$À‰vžŸf$Ž |ãw;ÀÁIJ»b` {¦Ó¤Ú$©YÀ‘n@Óïž«9J¼êG m¤ ܯ¹ÌW4€ÐÒÅÛ‡#褕Ÿn-?í|с¥÷Ú¹¬'´ÞÜ9ÓK `hê£SÄSà?7—Wí_´…óB›»:=Ãïq`<8ñÓŒÑlú2d¬ê³£hÖ[l|$vÝro~'R®‰§°ñmY ͧäP |PUª¹·:3Œ[Û{Xÿ ºâ@‚W–Äé u‚ ¯´*=íή.pûÒdt @G‰¬ s¸ ëÉücr ÞæÑ¨Ê@>¤¢Ö±. Þ'¯°ÌME[YéïĵÂCå½ Ué©Áû'Ê9%eÔðNU”ë‘ÌsD3/®+UI˜9h.WC”빓$#:pz:YÓ ¿xž* ³$Í +$kñAŠ‹†¢ Uê>¸)_š¬÷©ßAÂÔb9ÇU ¯¾á•9¯ÏÏ÷O÷¼¼Fähal1‰3Ì[Ïr•´UCksNÐ] R‘¸¥H+§Šé†c©vÖÞ0iÓ76s†î!§=ß ¼~Ô'°Ãmäoäš³ªøi1úÉ)³yV8 CLÄØÁ‘WYïi€H6ÖÑiámø^ÈY´°Ñ7¥Û*—Ñ©L«Qƒï—Ùrÿ ›£Ð*š¸ˆL©ˆ$ˆ ÷¾D§9È®«qbqC)–ˆïv´çñsÑVT­Ø, <àïºÀO«Jý·õ àfPìð .wFšir´þ’2_Y *Æ€x\« ì€9š@ Ž|F⇥ˆkZ@hÖÄ0t¿-<“‹qµ¾*ZL¤Ú)&BJpÓF5=$„at*Zš$’ÑtdûÝRI1 2މ$€$I$#‰SÞ’Hë¬ï;Á$¡t$’`<(ñÇt)$‡Ð.Êf¢X’Kt=Éé$‚ˆªè¢oÝëòI%Rgcª÷ŠyI%¡‰ÿ !ñ)´õ $¤ Ô’IIGÿÙ package IO::Uncompress::Base ; use strict ; use warnings; use bytes; our (@ISA, $VERSION, @EXPORT_OK, %EXPORT_TAGS); @ISA = qw(IO::File Exporter); $VERSION = '2.106'; use constant G_EOF => 0 ; use constant G_ERR => -1 ; use IO::Compress::Base::Common 2.106 ; use IO::File ; use Symbol; use Scalar::Util (); use List::Util (); use Carp ; %EXPORT_TAGS = ( ); push @{ $EXPORT_TAGS{all} }, @EXPORT_OK ; sub smartRead { my $self = $_[0]; my $out = $_[1]; my $size = $_[2]; $$out = "" ; my $offset = 0 ; my $status = 1; if (defined *$self->{InputLength}) { return 0 if *$self->{InputLengthRemaining} <= 0 ; $size = List::Util::min($size, *$self->{InputLengthRemaining}); } if ( length *$self->{Prime} ) { $$out = substr(*$self->{Prime}, 0, $size) ; substr(*$self->{Prime}, 0, $size) = '' ; if (length $$out == $size) { *$self->{InputLengthRemaining} -= length $$out if defined *$self->{InputLength}; return length $$out ; } $offset = length $$out ; } my $get_size = $size - $offset ; if (defined *$self->{FH}) { if ($offset) { # Not using this # # *$self->{FH}->read($$out, $get_size, $offset); # # because the filehandle may not support the offset parameter # An example is Net::FTP my $tmp = ''; $status = *$self->{FH}->read($tmp, $get_size) ; substr($$out, $offset) = $tmp if defined $status && $status > 0 ; } else { $status = *$self->{FH}->read($$out, $get_size) } } elsif (defined *$self->{InputEvent}) { my $got = 1 ; while (length $$out < $size) { last if ($got = *$self->{InputEvent}->($$out, $get_size)) <= 0; } if (length $$out > $size ) { *$self->{Prime} = substr($$out, $size, length($$out)); substr($$out, $size, length($$out)) = ''; } *$self->{EventEof} = 1 if $got <= 0 ; } else { no warnings 'uninitialized'; my $buf = *$self->{Buffer} ; $$buf = '' unless defined $$buf ; substr($$out, $offset) = substr($$buf, *$self->{BufferOffset}, $get_size); if (*$self->{ConsumeInput}) { substr($$buf, 0, $get_size) = '' } else { *$self->{BufferOffset} += length($$out) - $offset } } *$self->{InputLengthRemaining} -= length($$out) #- $offset if defined *$self->{InputLength}; if (! defined $status) { $self->saveStatus($!) ; return STATUS_ERROR; } $self->saveStatus(length $$out < 0 ? STATUS_ERROR : STATUS_OK) ; return length $$out; } sub pushBack { my $self = shift ; return if ! defined $_[0] || length $_[0] == 0 ; if (defined *$self->{FH} || defined *$self->{InputEvent} ) { *$self->{Prime} = $_[0] . *$self->{Prime} ; *$self->{InputLengthRemaining} += length($_[0]); } else { my $len = length $_[0]; if($len > *$self->{BufferOffset}) { *$self->{Prime} = substr($_[0], 0, $len - *$self->{BufferOffset}) . *$self->{Prime} ; *$self->{InputLengthRemaining} = *$self->{InputLength}; *$self->{BufferOffset} = 0 } else { *$self->{InputLengthRemaining} += length($_[0]); *$self->{BufferOffset} -= length($_[0]) ; } } } sub smartSeek { my $self = shift ; my $offset = shift ; my $truncate = shift; my $position = shift || SEEK_SET; # TODO -- need to take prime into account *$self->{Prime} = ''; if (defined *$self->{FH}) { *$self->{FH}->seek($offset, $position) } else { if ($position == SEEK_END) { *$self->{BufferOffset} = length(${ *$self->{Buffer} }) + $offset ; } elsif ($position == SEEK_CUR) { *$self->{BufferOffset} += $offset ; } else { *$self->{BufferOffset} = $offset ; } substr(${ *$self->{Buffer} }, *$self->{BufferOffset}) = '' if $truncate; return 1; } } sub smartTell { my $self = shift ; if (defined *$self->{FH}) { return *$self->{FH}->tell() } else { return *$self->{BufferOffset} } } sub smartWrite { my $self = shift ; my $out_data = shift ; if (defined *$self->{FH}) { # flush needed for 5.8.0 defined *$self->{FH}->write($out_data, length $out_data) && defined *$self->{FH}->flush() ; } else { my $buf = *$self->{Buffer} ; substr($$buf, *$self->{BufferOffset}, length $out_data) = $out_data ; *$self->{BufferOffset} += length($out_data) ; return 1; } } sub smartReadExact { return $_[0]->smartRead($_[1], $_[2]) == $_[2]; } sub smartEof { my ($self) = $_[0]; local $.; return 0 if length *$self->{Prime} || *$self->{PushMode}; if (defined *$self->{FH}) { # Could use # # *$self->{FH}->eof() # # here, but this can cause trouble if # the filehandle is itself a tied handle, but it uses sysread. # Then we get into mixing buffered & non-buffered IO, # which will cause trouble my $info = $self->getErrInfo(); my $buffer = ''; my $status = $self->smartRead(\$buffer, 1); $self->pushBack($buffer) if length $buffer; $self->setErrInfo($info); return $status == 0 ; } elsif (defined *$self->{InputEvent}) { *$self->{EventEof} } else { *$self->{BufferOffset} >= length(${ *$self->{Buffer} }) } } sub clearError { my $self = shift ; *$self->{ErrorNo} = 0 ; ${ *$self->{Error} } = '' ; } sub getErrInfo { my $self = shift ; return [ *$self->{ErrorNo}, ${ *$self->{Error} } ] ; } sub setErrInfo { my $self = shift ; my $ref = shift; *$self->{ErrorNo} = $ref->[0] ; ${ *$self->{Error} } = $ref->[1] ; } sub saveStatus { my $self = shift ; my $errno = shift() + 0 ; *$self->{ErrorNo} = $errno; ${ *$self->{Error} } = '' ; return *$self->{ErrorNo} ; } sub saveErrorString { my $self = shift ; my $retval = shift ; ${ *$self->{Error} } = shift ; *$self->{ErrorNo} = @_ ? shift() + 0 : STATUS_ERROR ; return $retval; } sub croakError { my $self = shift ; $self->saveErrorString(0, $_[0]); croak $_[0]; } sub closeError { my $self = shift ; my $retval = shift ; my $errno = *$self->{ErrorNo}; my $error = ${ *$self->{Error} }; $self->close(); *$self->{ErrorNo} = $errno ; ${ *$self->{Error} } = $error ; return $retval; } sub error { my $self = shift ; return ${ *$self->{Error} } ; } sub errorNo { my $self = shift ; return *$self->{ErrorNo}; } sub HeaderError { my ($self) = shift; return $self->saveErrorString(undef, "Header Error: $_[0]", STATUS_ERROR); } sub TrailerError { my ($self) = shift; return $self->saveErrorString(G_ERR, "Trailer Error: $_[0]", STATUS_ERROR); } sub TruncatedHeader { my ($self) = shift; return $self->HeaderError("Truncated in $_[0] Section"); } sub TruncatedTrailer { my ($self) = shift; return $self->TrailerError("Truncated in $_[0] Section"); } sub postCheckParams { return 1; } sub checkParams { my $self = shift ; my $class = shift ; my $got = shift || IO::Compress::Base::Parameters::new(); my $Valid = { 'blocksize' => [IO::Compress::Base::Common::Parse_unsigned, 16 * 1024], 'autoclose' => [IO::Compress::Base::Common::Parse_boolean, 0], 'strict' => [IO::Compress::Base::Common::Parse_boolean, 0], 'append' => [IO::Compress::Base::Common::Parse_boolean, 0], 'prime' => [IO::Compress::Base::Common::Parse_any, undef], 'multistream' => [IO::Compress::Base::Common::Parse_boolean, 0], 'transparent' => [IO::Compress::Base::Common::Parse_any, 1], 'scan' => [IO::Compress::Base::Common::Parse_boolean, 0], 'inputlength' => [IO::Compress::Base::Common::Parse_unsigned, undef], 'binmodeout' => [IO::Compress::Base::Common::Parse_boolean, 0], #'decode' => [IO::Compress::Base::Common::Parse_any, undef], #'consumeinput' => [IO::Compress::Base::Common::Parse_boolean, 0], $self->getExtraParams(), #'Todo - Revert to ordinary file on end Z_STREAM_END'=> 0, # ContinueAfterEof } ; $Valid->{trailingdata} = [IO::Compress::Base::Common::Parse_writable_scalar, undef] if *$self->{OneShot} ; $got->parse($Valid, @_ ) or $self->croakError("${class}: " . $got->getError()) ; $self->postCheckParams($got) or $self->croakError("${class}: " . $self->error()) ; return $got; } sub _create { my $obj = shift; my $got = shift; my $append_mode = shift ; my $class = ref $obj; $obj->croakError("$class: Missing Input parameter") if ! @_ && ! $got ; my $inValue = shift ; *$obj->{OneShot} = 0 ; if (! $got) { $got = $obj->checkParams($class, undef, @_) or return undef ; } my $inType = whatIsInput($inValue, 1); $obj->ckInputParam($class, $inValue, 1) or return undef ; *$obj->{InNew} = 1; $obj->ckParams($got) or $obj->croakError("${class}: " . *$obj->{Error}); if ($inType eq 'buffer' || $inType eq 'code') { *$obj->{Buffer} = $inValue ; *$obj->{InputEvent} = $inValue if $inType eq 'code' ; } else { if ($inType eq 'handle') { *$obj->{FH} = $inValue ; *$obj->{Handle} = 1 ; # Need to rewind for Scan *$obj->{FH}->seek(0, SEEK_SET) if $got->getValue('scan'); } else { no warnings ; my $mode = '<'; $mode = '+<' if $got->getValue('scan'); *$obj->{StdIO} = ($inValue eq '-'); *$obj->{FH} = IO::File->new( "$mode $inValue" ) or return $obj->saveErrorString(undef, "cannot open file '$inValue': $!", $!) ; } *$obj->{LineNo} = $. = 0; setBinModeInput(*$obj->{FH}) ; my $buff = "" ; *$obj->{Buffer} = \$buff ; } # if ($got->getValue('decode')) { # my $want_encoding = $got->getValue('decode'); # *$obj->{Encoding} = IO::Compress::Base::Common::getEncoding($obj, $class, $want_encoding); # } # else { # *$obj->{Encoding} = undef; # } *$obj->{InputLength} = $got->parsed('inputlength') ? $got->getValue('inputlength') : undef ; *$obj->{InputLengthRemaining} = $got->getValue('inputlength'); *$obj->{BufferOffset} = 0 ; *$obj->{AutoClose} = $got->getValue('autoclose'); *$obj->{Strict} = $got->getValue('strict'); *$obj->{BlockSize} = $got->getValue('blocksize'); *$obj->{Append} = $got->getValue('append'); *$obj->{AppendOutput} = $append_mode || $got->getValue('append'); *$obj->{ConsumeInput} = $got->getValue('consumeinput'); *$obj->{Transparent} = $got->getValue('transparent'); *$obj->{MultiStream} = $got->getValue('multistream'); # TODO - move these two into RawDeflate *$obj->{Scan} = $got->getValue('scan'); *$obj->{ParseExtra} = $got->getValue('parseextra') || $got->getValue('strict') ; *$obj->{Type} = ''; *$obj->{Prime} = $got->getValue('prime') || '' ; *$obj->{Pending} = ''; *$obj->{Plain} = 0; *$obj->{PlainBytesRead} = 0; *$obj->{InflatedBytesRead} = 0; *$obj->{UnCompSize} = U64->new; *$obj->{CompSize} = U64->new; *$obj->{TotalInflatedBytesRead} = 0; *$obj->{NewStream} = 0 ; *$obj->{EventEof} = 0 ; *$obj->{ClassName} = $class ; *$obj->{Params} = $got ; if (*$obj->{ConsumeInput}) { *$obj->{InNew} = 0; *$obj->{Closed} = 0; return $obj } my $status = $obj->mkUncomp($got); return undef unless defined $status; *$obj->{InNew} = 0; *$obj->{Closed} = 0; return $obj if *$obj->{Pause} ; if ($status) { # Need to try uncompressing to catch the case # where the compressed file uncompresses to an # empty string - so eof is set immediately. my $out_buffer = ''; $status = $obj->read(\$out_buffer); if ($status < 0) { *$obj->{ReadStatus} = [ $status, $obj->error(), $obj->errorNo() ]; } $obj->ungetc($out_buffer) if length $out_buffer; } else { return undef unless *$obj->{Transparent}; $obj->clearError(); *$obj->{Type} = 'plain'; *$obj->{Plain} = 1; $obj->pushBack(*$obj->{HeaderPending}) ; } push @{ *$obj->{InfoList} }, *$obj->{Info} ; $obj->saveStatus(STATUS_OK) ; *$obj->{InNew} = 0; *$obj->{Closed} = 0; return $obj; } sub ckInputParam { my $self = shift ; my $from = shift ; my $inType = whatIsInput($_[0], $_[1]); $self->croakError("$from: input parameter not a filename, filehandle, array ref or scalar ref") if ! $inType ; # if ($inType eq 'filename' ) # { # return $self->saveErrorString(1, "$from: input filename is undef or null string", STATUS_ERROR) # if ! defined $_[0] || $_[0] eq '' ; # # if ($_[0] ne '-' && ! -e $_[0] ) # { # return $self->saveErrorString(1, # "input file '$_[0]' does not exist", STATUS_ERROR); # } # } return 1; } sub _inf { my $obj = shift ; my $class = (caller)[0] ; my $name = (caller(1))[3] ; $obj->croakError("$name: expected at least 1 parameters\n") unless @_ >= 1 ; my $input = shift ; my $haveOut = @_ ; my $output = shift ; my $x = IO::Compress::Base::Validator->new($class, *$obj->{Error}, $name, $input, $output) or return undef ; push @_, $output if $haveOut && $x->{Hash}; *$obj->{OneShot} = 1 ; my $got = $obj->checkParams($name, undef, @_) or return undef ; if ($got->parsed('trailingdata')) { # my $value = $got->valueRef('TrailingData'); # warn "TD $value "; # #$value = $$value; ## warn "TD $value $$value "; # # return retErr($obj, "Parameter 'TrailingData' not writable") # if readonly $$value ; # # if (ref $$value) # { # return retErr($obj,"Parameter 'TrailingData' not a scalar reference") # if ref $$value ne 'SCALAR' ; # # *$obj->{TrailingData} = $$value ; # } # else # { # return retErr($obj,"Parameter 'TrailingData' not a scalar") # if ref $value ne 'SCALAR' ; # # *$obj->{TrailingData} = $value ; # } *$obj->{TrailingData} = $got->getValue('trailingdata'); } *$obj->{MultiStream} = $got->getValue('multistream'); $got->setValue('multistream', 0); $x->{Got} = $got ; # if ($x->{Hash}) # { # while (my($k, $v) = each %$input) # { # $v = \$input->{$k} # unless defined $v ; # # $obj->_singleTarget($x, $k, $v, @_) # or return undef ; # } # # return keys %$input ; # } if ($x->{GlobMap}) { $x->{oneInput} = 1 ; foreach my $pair (@{ $x->{Pairs} }) { my ($from, $to) = @$pair ; $obj->_singleTarget($x, $from, $to, @_) or return undef ; } return scalar @{ $x->{Pairs} } ; } if (! $x->{oneOutput} ) { my $inFile = ($x->{inType} eq 'filenames' || $x->{inType} eq 'filename'); $x->{inType} = $inFile ? 'filename' : 'buffer'; foreach my $in ($x->{oneInput} ? $input : @$input) { my $out ; $x->{oneInput} = 1 ; $obj->_singleTarget($x, $in, $output, @_) or return undef ; } return 1 ; } # finally the 1 to 1 and n to 1 return $obj->_singleTarget($x, $input, $output, @_); croak "should not be here" ; } sub retErr { my $x = shift ; my $string = shift ; ${ $x->{Error} } = $string ; return undef ; } sub _singleTarget { my $self = shift ; my $x = shift ; my $input = shift; my $output = shift; my $buff = ''; $x->{buff} = \$buff ; my $fh ; if ($x->{outType} eq 'filename') { my $mode = '>' ; $mode = '>>' if $x->{Got}->getValue('append') ; $x->{fh} = IO::File->new( "$mode $output" ) or return retErr($x, "cannot open file '$output': $!") ; binmode $x->{fh} ; } elsif ($x->{outType} eq 'handle') { $x->{fh} = $output; binmode $x->{fh} ; if ($x->{Got}->getValue('append')) { seek($x->{fh}, 0, SEEK_END) or return retErr($x, "Cannot seek to end of output filehandle: $!") ; } } elsif ($x->{outType} eq 'buffer' ) { $$output = '' unless $x->{Got}->getValue('append'); $x->{buff} = $output ; } if ($x->{oneInput}) { defined $self->_rd2($x, $input, $output) or return undef; } else { for my $element ( ($x->{inType} eq 'hash') ? keys %$input : @$input) { defined $self->_rd2($x, $element, $output) or return undef ; } } if ( ($x->{outType} eq 'filename' && $output ne '-') || ($x->{outType} eq 'handle' && $x->{Got}->getValue('autoclose'))) { $x->{fh}->close() or return retErr($x, $!); delete $x->{fh}; } return 1 ; } sub _rd2 { my $self = shift ; my $x = shift ; my $input = shift; my $output = shift; my $z = IO::Compress::Base::Common::createSelfTiedObject($x->{Class}, *$self->{Error}); $z->_create($x->{Got}, 1, $input, @_) or return undef ; my $status ; my $fh = $x->{fh}; while (1) { while (($status = $z->read($x->{buff})) > 0) { if ($fh) { local $\; print $fh ${ $x->{buff} } or return $z->saveErrorString(undef, "Error writing to output file: $!", $!); ${ $x->{buff} } = '' ; } } if (! $x->{oneOutput} ) { my $ot = $x->{outType} ; if ($ot eq 'array') { push @$output, $x->{buff} } elsif ($ot eq 'hash') { $output->{$input} = $x->{buff} } my $buff = ''; $x->{buff} = \$buff; } last if $status < 0 || $z->smartEof(); last unless *$self->{MultiStream}; $status = $z->nextStream(); last unless $status == 1 ; } return $z->closeError(undef) if $status < 0 ; ${ *$self->{TrailingData} } = $z->trailingData() if defined *$self->{TrailingData} ; $z->close() or return undef ; return 1 ; } sub TIEHANDLE { return $_[0] if ref($_[0]); die "OOPS\n" ; } sub UNTIE { my $self = shift ; } sub getHeaderInfo { my $self = shift ; wantarray ? @{ *$self->{InfoList} } : *$self->{Info}; } sub readBlock { my $self = shift ; my $buff = shift ; my $size = shift ; if (defined *$self->{CompressedInputLength}) { if (*$self->{CompressedInputLengthRemaining} == 0) { delete *$self->{CompressedInputLength}; *$self->{CompressedInputLengthDone} = 1; return STATUS_OK ; } $size = List::Util::min($size, *$self->{CompressedInputLengthRemaining} ); *$self->{CompressedInputLengthRemaining} -= $size ; } my $status = $self->smartRead($buff, $size) ; return $self->saveErrorString(STATUS_ERROR, "Error Reading Data: $!", $!) if $status == STATUS_ERROR ; if ($status == 0 ) { *$self->{Closed} = 1 ; *$self->{EndStream} = 1 ; return $self->saveErrorString(STATUS_ERROR, "unexpected end of file", STATUS_ERROR); } return STATUS_OK; } sub postBlockChk { return STATUS_OK; } sub _raw_read { # return codes # >0 - ok, number of bytes read # =0 - ok, eof # <0 - not ok my $self = shift ; return G_EOF if *$self->{Closed} ; return G_EOF if *$self->{EndStream} ; my $buffer = shift ; my $scan_mode = shift ; if (*$self->{Plain}) { my $tmp_buff ; my $len = $self->smartRead(\$tmp_buff, *$self->{BlockSize}) ; return $self->saveErrorString(G_ERR, "Error reading data: $!", $!) if $len == STATUS_ERROR ; if ($len == 0 ) { *$self->{EndStream} = 1 ; } else { *$self->{PlainBytesRead} += $len ; $$buffer .= $tmp_buff; } return $len ; } if (*$self->{NewStream}) { $self->gotoNextStream() > 0 or return G_ERR; # For the headers that actually uncompressed data, put the # uncompressed data into the output buffer. $$buffer .= *$self->{Pending} ; my $len = length *$self->{Pending} ; *$self->{Pending} = ''; return $len; } my $temp_buf = ''; my $outSize = 0; my $status = $self->readBlock(\$temp_buf, *$self->{BlockSize}, $outSize) ; return G_ERR if $status == STATUS_ERROR ; my $buf_len = 0; if ($status == STATUS_OK) { my $beforeC_len = length $temp_buf; my $before_len = defined $$buffer ? length $$buffer : 0 ; $status = *$self->{Uncomp}->uncompr(\$temp_buf, $buffer, defined *$self->{CompressedInputLengthDone} || $self->smartEof(), $outSize); # Remember the input buffer if it wasn't consumed completely $self->pushBack($temp_buf) if *$self->{Uncomp}{ConsumesInput}; return $self->saveErrorString(G_ERR, *$self->{Uncomp}{Error}, *$self->{Uncomp}{ErrorNo}) if $self->saveStatus($status) == STATUS_ERROR; $self->postBlockChk($buffer, $before_len) == STATUS_OK or return G_ERR; $buf_len = defined $$buffer ? length($$buffer) - $before_len : 0; *$self->{CompSize}->add($beforeC_len - length $temp_buf) ; *$self->{InflatedBytesRead} += $buf_len ; *$self->{TotalInflatedBytesRead} += $buf_len ; *$self->{UnCompSize}->add($buf_len) ; $self->filterUncompressed($buffer, $before_len); # if (*$self->{Encoding}) { # use Encode ; # *$self->{PendingDecode} .= substr($$buffer, $before_len) ; # my $got = *$self->{Encoding}->decode(*$self->{PendingDecode}, Encode::FB_QUIET) ; # substr($$buffer, $before_len) = $got; # } } if ($status == STATUS_ENDSTREAM) { *$self->{EndStream} = 1 ; my $trailer; my $trailer_size = *$self->{Info}{TrailerLength} ; my $got = 0; if (*$self->{Info}{TrailerLength}) { $got = $self->smartRead(\$trailer, $trailer_size) ; } if ($got == $trailer_size) { $self->chkTrailer($trailer) == STATUS_OK or return G_ERR; } else { return $self->TrailerError("trailer truncated. Expected " . "$trailer_size bytes, got $got") if *$self->{Strict}; $self->pushBack($trailer) ; } # TODO - if want file pointer, do it here if (! $self->smartEof()) { *$self->{NewStream} = 1 ; if (*$self->{MultiStream}) { *$self->{EndStream} = 0 ; return $buf_len ; } } } # return the number of uncompressed bytes read return $buf_len ; } sub reset { my $self = shift ; return *$self->{Uncomp}->reset(); } sub filterUncompressed { } #sub isEndStream #{ # my $self = shift ; # return *$self->{NewStream} || # *$self->{EndStream} ; #} sub nextStream { my $self = shift ; # An uncompressed file cannot have a next stream, so # return immediately. return 0 if *$self->{Plain} ; my $status = $self->gotoNextStream(); $status == 1 or return $status ; *$self->{Pending} = '' if $self !~ /IO::Uncompress::RawInflate/ && ! *$self->{MultiStream}; *$self->{TotalInflatedBytesRead} = 0 ; *$self->{LineNo} = $. = 0; return 1; } sub gotoNextStream { my $self = shift ; if (! *$self->{NewStream}) { my $status = 1; my $buffer ; # TODO - make this more efficient if know the offset for the end of # the stream and seekable $status = $self->read($buffer) while $status > 0 ; return $status if $status < 0; } *$self->{NewStream} = 0 ; *$self->{EndStream} = 0 ; *$self->{CompressedInputLengthDone} = undef ; *$self->{CompressedInputLength} = undef ; $self->reset(); *$self->{UnCompSize}->reset(); *$self->{CompSize}->reset(); my $magic = $self->ckMagic(); if ( ! defined $magic) { if (! *$self->{Transparent} || $self->eof()) { *$self->{EndStream} = 1 ; return 0; } # Not EOF, so Transparent mode kicks in now for trailing data # Reset member name in case anyone calls getHeaderInfo()->{Name} *$self->{Info} = { Name => undef, Type => 'plain' }; $self->clearError(); *$self->{Type} = 'plain'; *$self->{Plain} = 1; $self->pushBack(*$self->{HeaderPending}) ; } else { *$self->{Info} = $self->readHeader($magic); if ( ! defined *$self->{Info} ) { *$self->{EndStream} = 1 ; return -1; } } push @{ *$self->{InfoList} }, *$self->{Info} ; return 1; } sub streamCount { my $self = shift ; return 1 if ! defined *$self->{InfoList}; return scalar @{ *$self->{InfoList} } ; } sub read { # return codes # >0 - ok, number of bytes read # =0 - ok, eof # <0 - not ok my $self = shift ; if (defined *$self->{ReadStatus} ) { my $status = *$self->{ReadStatus}[0]; $self->saveErrorString( @{ *$self->{ReadStatus} } ); delete *$self->{ReadStatus} ; return $status ; } return G_EOF if *$self->{Closed} ; my $buffer ; if (ref $_[0] ) { $self->croakError(*$self->{ClassName} . "::read: buffer parameter is read-only") if Scalar::Util::readonly(${ $_[0] }); $self->croakError(*$self->{ClassName} . "::read: not a scalar reference $_[0]" ) unless ref $_[0] eq 'SCALAR' ; $buffer = $_[0] ; } else { $self->croakError(*$self->{ClassName} . "::read: buffer parameter is read-only") if Scalar::Util::readonly($_[0]); $buffer = \$_[0] ; } my $length = $_[1] ; my $offset = $_[2] || 0; if (! *$self->{AppendOutput}) { if (! $offset) { $$buffer = '' ; } else { if ($offset > length($$buffer)) { $$buffer .= "\x00" x ($offset - length($$buffer)); } else { substr($$buffer, $offset) = ''; } } } elsif (! defined $$buffer) { $$buffer = '' ; } return G_EOF if !length *$self->{Pending} && *$self->{EndStream} ; # the core read will return 0 if asked for 0 bytes return 0 if defined $length && $length == 0 ; $length = $length || 0; $self->croakError(*$self->{ClassName} . "::read: length parameter is negative") if $length < 0 ; # Short-circuit if this is a simple read, with no length # or offset specified. unless ( $length || $offset) { if (length *$self->{Pending}) { $$buffer .= *$self->{Pending} ; my $len = length *$self->{Pending}; *$self->{Pending} = '' ; return $len ; } else { my $len = 0; $len = $self->_raw_read($buffer) while ! *$self->{EndStream} && $len == 0 ; return $len ; } } # Need to jump through more hoops - either length or offset # or both are specified. my $out_buffer = *$self->{Pending} ; *$self->{Pending} = ''; while (! *$self->{EndStream} && length($out_buffer) < $length) { my $buf_len = $self->_raw_read(\$out_buffer); return $buf_len if $buf_len < 0 ; } $length = length $out_buffer if length($out_buffer) < $length ; return 0 if $length == 0 ; $$buffer = '' if ! defined $$buffer; $offset = length $$buffer if *$self->{AppendOutput} ; *$self->{Pending} = $out_buffer; $out_buffer = \*$self->{Pending} ; substr($$buffer, $offset) = substr($$out_buffer, 0, $length) ; substr($$out_buffer, 0, $length) = '' ; return $length ; } sub _getline { my $self = shift ; my $status = 0 ; # Slurp Mode if ( ! defined $/ ) { my $data ; 1 while ($status = $self->read($data)) > 0 ; return ($status, \$data); } # Record Mode if ( ref $/ eq 'SCALAR' && ${$/} =~ /^\d+$/ && ${$/} > 0) { my $reclen = ${$/} ; my $data ; $status = $self->read($data, $reclen) ; return ($status, \$data); } # Paragraph Mode if ( ! length $/ ) { my $paragraph ; while (($status = $self->read($paragraph)) > 0 ) { if ($paragraph =~ s/^(.*?\n\n+)//s) { *$self->{Pending} = $paragraph ; my $par = $1 ; return (1, \$par); } } return ($status, \$paragraph); } # $/ isn't empty, or a reference, so it's Line Mode. { my $line ; my $p = \*$self->{Pending} ; while (($status = $self->read($line)) > 0 ) { my $offset = index($line, $/); if ($offset >= 0) { my $l = substr($line, 0, $offset + length $/ ); substr($line, 0, $offset + length $/) = ''; $$p = $line; return (1, \$l); } } return ($status, \$line); } } sub getline { my $self = shift; if (defined *$self->{ReadStatus} ) { $self->saveErrorString( @{ *$self->{ReadStatus} } ); delete *$self->{ReadStatus} ; return undef; } return undef if *$self->{Closed} || (!length *$self->{Pending} && *$self->{EndStream}) ; my $current_append = *$self->{AppendOutput} ; *$self->{AppendOutput} = 1; my ($status, $lineref) = $self->_getline(); *$self->{AppendOutput} = $current_append; return undef if $status < 0 || length $$lineref == 0 ; $. = ++ *$self->{LineNo} ; return $$lineref ; } sub getlines { my $self = shift; $self->croakError(*$self->{ClassName} . "::getlines: called in scalar context\n") unless wantarray; my($line, @lines); push(@lines, $line) while defined($line = $self->getline); return @lines; } sub READLINE { goto &getlines if wantarray; goto &getline; } sub getc { my $self = shift; my $buf; return $buf if $self->read($buf, 1); return undef; } sub ungetc { my $self = shift; *$self->{Pending} = "" unless defined *$self->{Pending} ; *$self->{Pending} = $_[0] . *$self->{Pending} ; } sub trailingData { my $self = shift ; if (defined *$self->{FH} || defined *$self->{InputEvent} ) { return *$self->{Prime} ; } else { my $buf = *$self->{Buffer} ; my $offset = *$self->{BufferOffset} ; return substr($$buf, $offset) ; } } sub eof { my $self = shift ; return (*$self->{Closed} || (!length *$self->{Pending} && ( $self->smartEof() || *$self->{EndStream}))) ; } sub tell { my $self = shift ; my $in ; if (*$self->{Plain}) { $in = *$self->{PlainBytesRead} ; } else { $in = *$self->{TotalInflatedBytesRead} ; } my $pending = length *$self->{Pending} ; return 0 if $pending > $in ; return $in - $pending ; } sub close { # todo - what to do if close is called before the end of the gzip file # do we remember any trailing data? my $self = shift ; return 1 if *$self->{Closed} ; untie *$self if $] >= 5.008 ; my $status = 1 ; if (defined *$self->{FH}) { if ((! *$self->{Handle} || *$self->{AutoClose}) && ! *$self->{StdIO}) { local $.; $! = 0 ; $status = *$self->{FH}->close(); return $self->saveErrorString(0, $!, $!) if !*$self->{InNew} && $self->saveStatus($!) != 0 ; } delete *$self->{FH} ; $! = 0 ; } *$self->{Closed} = 1 ; return 1; } sub DESTROY { my $self = shift ; local ($., $@, $!, $^E, $?); $self->close() ; } sub seek { my $self = shift ; my $position = shift; my $whence = shift ; my $here = $self->tell() ; my $target = 0 ; if ($whence == SEEK_SET) { $target = $position ; } elsif ($whence == SEEK_CUR) { $target = $here + $position ; } elsif ($whence == SEEK_END) { $target = $position ; $self->croakError(*$self->{ClassName} . "::seek: SEEK_END not allowed") ; } else { $self->croakError(*$self->{ClassName} ."::seek: unknown value, $whence, for whence parameter"); } # short circuit if seeking to current offset if ($target == $here) { # On ordinary filehandles, seeking to the current # position also clears the EOF condition, so we # emulate this behavior locally while simultaneously # cascading it to the underlying filehandle if (*$self->{Plain}) { *$self->{EndStream} = 0; seek(*$self->{FH},0,1) if *$self->{FH}; } return 1; } # Outlaw any attempt to seek backwards $self->croakError( *$self->{ClassName} ."::seek: cannot seek backwards") if $target < $here ; # Walk the file to the new offset my $offset = $target - $here ; my $got; while (($got = $self->read(my $buffer, List::Util::min($offset, *$self->{BlockSize})) ) > 0) { $offset -= $got; last if $offset == 0 ; } $here = $self->tell() ; return $offset == 0 ? 1 : 0 ; } sub fileno { my $self = shift ; return defined *$self->{FH} ? fileno *$self->{FH} : undef ; } sub binmode { 1; # my $self = shift ; # return defined *$self->{FH} # ? binmode *$self->{FH} # : 1 ; } sub opened { my $self = shift ; return ! *$self->{Closed} ; } sub autoflush { my $self = shift ; return defined *$self->{FH} ? *$self->{FH}->autoflush(@_) : undef ; } sub input_line_number { my $self = shift ; my $last = *$self->{LineNo}; $. = *$self->{LineNo} = $_[1] if @_ ; return $last; } sub _notAvailable { my $name = shift ; return sub { croak "$name Not Available: File opened only for intput" ; } ; } { no warnings 'once'; *BINMODE = \&binmode; *SEEK = \&seek; *READ = \&read; *sysread = \&read; *TELL = \&tell; *EOF = \&eof; *FILENO = \&fileno; *CLOSE = \&close; *print = _notAvailable('print'); *PRINT = _notAvailable('print'); *printf = _notAvailable('printf'); *PRINTF = _notAvailable('printf'); *write = _notAvailable('write'); *WRITE = _notAvailable('write'); #*sysread = \&read; #*syswrite = \&_notAvailable; } package IO::Uncompress::Base ; 1 ; __END__ =head1 NAME IO::Uncompress::Base - Base Class for IO::Uncompress modules =head1 SYNOPSIS use IO::Uncompress::Base ; =head1 DESCRIPTION This module is not intended for direct use in application code. Its sole purpose is to be sub-classed by IO::Uncompress modules. =head1 SUPPORT General feedback/questions/bug reports should be sent to L (preferred) or L. =head1 SEE ALSO L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L L L, L, L, L =head1 AUTHOR This module was written by Paul Marquess, C. =head1 MODIFICATION HISTORY See the Changes file. =head1 COPYRIGHT AND LICENSE Copyright (c) 2005-2022 Paul Marquess. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.