PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/share/perl5/vendor_perl/CPANPLUS/Shell
Viewing File: /usr/share/perl5/vendor_perl/CPANPLUS/Shell/Classic.pm
################################################## ### CPANPLUS/Shell/Classic.pm ### ### Backwards compatible shell for CPAN++ ### ### Written 08-04-2002 by Jos Boumans ### ################################################## package CPANPLUS::Shell::Classic; use strict; use CPANPLUS::Error; use CPANPLUS::Backend; use CPANPLUS::Configure::Setup; use CPANPLUS::Internals::Constants; use Cwd; use IPC::Cmd; use Term::UI; use Data::Dumper; use Term::ReadLine; use Module::Load qw[load]; use Params::Check qw[check]; use Module::Load::Conditional qw[can_load]; $Params::Check::VERBOSE = 1; $Params::Check::ALLOW_UNKNOWN = 1; BEGIN { use vars qw[ $VERSION @ISA ]; @ISA = qw[ CPANPLUS::Shell::_Base::ReadLine ]; $VERSION = "0.9138"; } load CPANPLUS::Shell; ### our command set ### my $map = { a => '_author', b => '_bundle', d => '_distribution', 'm' => '_module', i => '_find_all', r => '_uptodate', u => '_not_supported', ls => '_ls', get => '_fetch', make => '_install', test => '_install', install => '_install', clean => '_not_supported', look => '_shell', readme => '_readme', h => '_help', '?' => '_help', o => '_set_conf', reload => '_reload', autobundle => '_autobundle', '!' => '_bang', #'q' => '_quit', # done it the loop itself }; ### the shell object, scoped to the file ### my $Shell; my $Brand = 'cpan'; my $Prompt = $Brand . '> '; sub new { my $class = shift; my $cb = new CPANPLUS::Backend; my $self = $class->SUPER::_init( brand => $Brand, term => Term::ReadLine->new( $Brand ), prompt => $Prompt, backend => $cb, format => "%5s %-50s %8s %-10s\n", ); ### make it available package wide ### $Shell = $self; ### enable verbose, it's the cpan.pm way $cb->configure_object->set_conf( verbose => 1 ); ### register install callback ### $cb->_register_callback( name => 'install_prerequisite', code => \&__ask_about_install, ); ### register test report callback ### $cb->_register_callback( name => 'edit_test_report', code => \&__ask_about_test_report, ); if (my $histfile = $self->configure_object->get_conf( 'histfile' )) { my $term = $self->term; if ($term->can('AddHistory')) { if (open my $fh, '<', $histfile) { local $/ = "\n"; while (my $line = <$fh>) { chomp($line); $term->AddHistory($line); } close($fh); } } } return $self; } sub shell { my $self = shift; my $term = $self->term; $self->_show_banner; $self->_input_loop && print "\n"; $self->_quit; } sub _input_loop { my $self = shift; my $term = $self->term; my $cb = $self->backend; my $normal_quit = 0; while ( defined (my $input = eval { $term->readline($self->prompt) } ) or $self->_signals->{INT}{count} == 1 ) { ### re-initiate all signal handlers while (my ($sig, $entry) = each %{$self->_signals} ) { $SIG{$sig} = $entry->{handler} if exists($entry->{handler}); } last if $self->_dispatch_on_input( input => $input ); ### flush the lib cache ### $cb->_flush( list => [qw|lib load|] ); } continue { $self->_signals->{INT}{count}-- if $self->_signals->{INT}{count}; # clear the sigint count } return 1; } sub _dispatch_on_input { my $self = shift; my $conf = $self->backend->configure_object(); my $term = $self->term; my %hash = @_; my $string; my $tmpl = { input => { required => 1, store => \$string } }; check( $tmpl, \%hash ) or return; ### the original force setting; my $force_store = $conf->get_conf( 'force' ); ### parse the input: the first part before the space ### is the command, followed by arguments. ### see the usage below my $key; PARSE_INPUT: { $string =~ s|^\s*([\w\?\!]+)\s*||; chomp $string; $key = lc($1); } ### you prefixed the input with 'force' ### that means we set the force flag, and ### reparse the input... ### YAY goto block :) if( $key eq 'force' ) { $conf->set_conf( force => 1 ); goto PARSE_INPUT; } ### you want to quit return 1 if $key =~ /^q/; my $method = $map->{$key}; unless( $self->can( $method ) ) { print "Unknown command '$key'. Type ? for help.\n"; return; } ### dispatch the method call eval { $self->$method( command => $key, result => [ split /\s+/, $string ], input => $string ); }; warn $@ if $@; return; } ### displays quit message sub _quit { my $self = shift; my $term = $self->term; if ($term->can('GetHistory')) { my @history = $term->GetHistory; my $histfile = $self->configure_object->get_conf('histfile'); if (open my $fh, '>', $histfile) { foreach my $line (@history) { print {$fh} "$line\n"; } close($fh); } else { warn "Cannot open history file '$histfile' - $!"; } } ### well, that's what CPAN.pm says... print "Lockfile removed\n"; } sub _not_supported { my $self = shift; my %hash = @_; my $cmd; my $tmpl = { command => { required => 1, store => \$cmd } }; check( $tmpl, \%hash ) or return; print "Sorry, the command '$cmd' is not supported\n"; return; } sub _fetch { my $self = shift; my $cb = $self->backend; my %hash = @_; my($aref, $input); my $tmpl = { result => { store => \$aref, default => [] }, input => { default => 'all', store => \$input }, }; check( $tmpl, \%hash ) or return; for my $mod (@$aref) { my $obj; unless( $obj = $cb->module_tree($mod) ) { print "Warning: Cannot get $input, don't know what it is\n"; print "Try the command\n\n"; print "\ti /$mod/\n\n"; print "to find objects with matching identifiers.\n"; next; } $obj->fetch && $obj->extract; } return $aref; } sub _install { my $self = shift; my $cb = $self->backend; my %hash = @_; my $mapping = { make => { target => TARGET_CREATE, skiptest => 1 }, test => { target => TARGET_CREATE }, install => { target => TARGET_INSTALL }, }; my($aref,$cmd); my $tmpl = { result => { store => \$aref, default => [] }, command => { required => 1, store => \$cmd, allow => [keys %$mapping] }, }; check( $tmpl, \%hash ) or return; for my $mod (@$aref) { my $obj = $cb->module_tree( $mod ); unless( $obj ) { print "No such module '$mod'\n"; next; } my $opts = $mapping->{$cmd}; $obj->install( %$opts ); } return $aref; } sub _shell { my $self = shift; my $cb = $self->backend; my $conf = $cb->configure_object; my %hash = @_; my($aref, $cmd); my $tmpl = { result => { store => \$aref, default => [] }, command => { required => 1, store => \$cmd }, }; check( $tmpl, \%hash ) or return; my $shell = $conf->get_program('shell'); unless( $shell ) { print "Your configuration does not define a value for subshells.\n". qq[Please define it with "o conf shell <your shell>"\n]; return; } my $cwd = Cwd::cwd(); for my $mod (@$aref) { print "Running $cmd for $mod\n"; my $obj = $cb->module_tree( $mod ) or next; $obj->fetch or next; $obj->extract or next; $cb->_chdir( dir => $obj->status->extract ) or next; #local $ENV{PERL5OPT} = CPANPLUS::inc->original_perl5opt; if( system($shell) and $! ) { print "Error executing your subshell '$shell': $!\n"; next; } } $cb->_chdir( dir => $cwd ); return $aref; } sub _readme { my $self = shift; my $cb = $self->backend; my $conf = $cb->configure_object; my %hash = @_; my($aref, $cmd); my $tmpl = { result => { store => \$aref, default => [] }, command => { required => 1, store => \$cmd }, }; check( $tmpl, \%hash ) or return; for my $mod (@$aref) { my $obj = $cb->module_tree( $mod ) or next; if( my $readme = $obj->readme ) { $self->_pager_open; print $readme; $self->_pager_close; } } return 1; } sub _reload { my $self = shift; my $cb = $self->backend; my $conf = $cb->configure_object; my %hash = @_; my($input, $cmd); my $tmpl = { input => { default => 'all', store => \$input }, command => { required => 1, store => \$cmd }, }; check( $tmpl, \%hash ) or return; if ( $input =~ /cpan/i ) { print qq[You want to reload the CPAN code\n]; print qq[Just type 'q' and then restart... ] . qq[Trust me, it is MUCH safer\n]; } elsif ( $input =~ /index/i ) { $cb->reload_indices(update_source => 1); } else { print qq[cpan re-evals the CPANPLUS.pm file\n]; print qq[index re-reads the index files\n]; } return 1; } sub _autobundle { my $self = shift; my $cb = $self->backend; print qq[Writing bundle file... This may take a while\n]; my $where = $cb->autobundle(); print $where ? qq[\nWrote autobundle to $where\n] : qq[\nCould not create autobundle\n]; return 1; } sub _set_conf { my $self = shift; my $cb = $self->backend; my $conf = $cb->configure_object; my %hash = @_; my($aref, $input); my $tmpl = { result => { store => \$aref, default => [] }, input => { default => 'all', store => \$input }, }; check( $tmpl, \%hash ) or return; my $type = shift @$aref; if( $type eq 'debug' ) { print qq[Sorry you cannot set debug options through ] . qq[this shell in CPANPLUS\n]; return; } elsif ( $type eq 'conf' ) { ### from CPAN.pm :o) # CPAN::Shell::o and CPAN::Config::edit are closely related. 'o conf' # should have been called set and 'o debug' maybe 'set debug' # commit Commit changes to disk # defaults Reload defaults from disk # init Interactive setting of all options my $name = shift @$aref; my $value = "@$aref"; if( $name eq 'init' ) { my $setup = CPANPLUS::Configure::Setup->new( conf => $cb->configure_object, term => $self->term, backend => $cb, ); return $setup->init; } elsif ($name eq 'commit' ) {; $cb->configure_object->save; print "Your CPAN++ configuration info has been saved!\n\n"; return; } elsif ($name eq 'defaults' ) { print qq[Sorry, CPANPLUS cannot restore default for you.\n] . qq[Perhaps you should run the interactive setup again.\n] . qq[\ttry running 'o conf init'\n]; return; ### we're just supplying things in the 'conf' section now, ### not the program section.. it's a bit of a hassle to make that ### work cleanly with the original CPAN.pm interface, so we'll fix ### it when people start complaining, which is hopefully never. } else { unless( $name ) { my @list = grep { $_ ne 'hosts' } $conf->options( type => $type ); my $method = 'get_' . $type; local $Data::Dumper::Indent = 0; for my $name ( @list ) { my $val = $conf->$method($name); ($val) = ref($val) ? (Data::Dumper::Dumper($val) =~ /= (.*);$/) : "'$val'"; printf " %-25s %s\n", $name, $val; } } elsif ( $name eq 'hosts' ) { print "Setting hosts is not trivial.\n" . "It is suggested you edit the " . "configuration file manually"; } else { my $method = 'set_' . $type; if( $conf->$method($name => defined $value ? $value : '') ) { my $set_to = defined $value ? $value : 'EMPTY STRING'; print "Key '$name' was set to '$set_to'\n"; } } } } else { print qq[Known options:\n] . qq[ conf set or get configuration variables\n] . qq[ debug set or get debugging options\n]; } return; } ######################## ### search functions ### ######################## sub _author { my $self = shift; my $cb = $self->backend; my %hash = @_; my($aref, $short, $input, $class); my $tmpl = { result => { store => \$aref, default => ['/./'] }, short => { default => 0, store => \$short }, input => { default => 'all', store => \$input }, class => { default => 'Author', no_override => 1, store => \$class }, }; check( $tmpl, \%hash ) or return; my @regexes = map { m|/(.+)/| ? qr/$1/ : $_ } @$aref; my @rv; for my $type (qw[author cpanid]) { push @rv, $cb->search( type => $type, allow => \@regexes ); } unless( @rv ) { print "No object of type $class found for argument $input\n" unless $short; return; } return $self->_pp_author( result => \@rv, class => $class, short => $short, input => $input ); } ### find all modules matching a query ### sub _module { my $self = shift; my $cb = $self->backend; my %hash = @_; my($aref, $short, $input, $class); my $tmpl = { result => { store => \$aref, default => ['/./'] }, short => { default => 0, store => \$short }, input => { default => 'all', store => \$input }, class => { default => 'Module', no_override => 1, store => \$class }, }; check( $tmpl, \%hash ) or return; my @rv; for my $module (@$aref) { if( $module =~ m|/(.+)/| ) { push @rv, $cb->search( type => 'module', allow => [qr/$1/i] ); } else { my $obj = $cb->module_tree( $module ) or next; push @rv, $obj; } } return $self->_pp_module( result => \@rv, class => $class, short => $short, input => $input ); } ### find all bundles matching a query ### sub _bundle { my $self = shift; my $cb = $self->backend; my %hash = @_; my($aref, $short, $input, $class); my $tmpl = { result => { store => \$aref, default => ['/./'] }, short => { default => 0, store => \$short }, input => { default => 'all', store => \$input }, class => { default => 'Bundle', no_override => 1, store => \$class }, }; check( $tmpl, \%hash ) or return; my @rv; for my $bundle (@$aref) { if( $bundle =~ m|/(.+)/| ) { push @rv, $cb->search( type => 'module', allow => [qr/Bundle::.*?$1/i] ); } else { my $obj = $cb->module_tree( "Bundle::${bundle}" ) or next; push @rv, $obj; } } return $self->_pp_module( result => \@rv, class => $class, short => $short, input => $input ); } sub _distribution { my $self = shift; my $cb = $self->backend; my %hash = @_; my($aref, $short, $input, $class); my $tmpl = { result => { store => \$aref, default => ['/./'] }, short => { default => 0, store => \$short }, input => { default => 'all', store => \$input }, class => { default => 'Distribution', no_override => 1, store => \$class }, }; check( $tmpl, \%hash ) or return; my @rv; for my $module (@$aref) { ### if it's a regex... ### if ( my ($match) = $module =~ m|^/(.+)/$|) { ### something like /FOO/Bar.tar.gz/ was entered if (my ($path,$package) = $match =~ m|^/?(.+)/(.+)$|) { my $seen; my @data = $cb->search( type => 'package', allow => [qr/$package/i] ); my @list = $cb->search( type => 'path', allow => [qr/$path/i], data => \@data ); ### make sure we dont list the same dist twice for my $val ( @list ) { next if $seen->{$val->package}++; push @rv, $val; } ### something like /FOO/ or /Bar.tgz/ was entered ### so we look both in the path, as well as in the package name } else { my $seen; { my @list = $cb->search( type => 'package', allow => [qr/$match/i] ); ### make sure we dont list the same dist twice for my $val ( @list ) { next if $seen->{$val->package}++; push @rv, $val; } } { my @list = $cb->search( type => 'path', allow => [qr/$match/i] ); ### make sure we dont list the same dist twice for my $val ( @list ) { next if $seen->{$val->package}++; push @rv, $val; } } } } else { ### user entered a full dist, like: R/RC/RCAPUTO/POE-0.19.tar.gz if (my ($path,$package) = $module =~ m|^/?(.+)/(.+)$|) { my @data = $cb->search( type => 'package', allow => [qr/^$package$/] ); my @list = $cb->search( type => 'path', allow => [qr/$path$/i], data => \@data); ### make sure we dont list the same dist twice my $seen; for my $val ( @list ) { next if $seen->{$val->package}++; push @rv, $val; } } } } return $self->_pp_distribution( result => \@rv, class => $class, short => $short, input => $input ); } sub _find_all { my $self = shift; my @rv; for my $method (qw[_author _bundle _module _distribution]) { my $aref = $self->$method( @_, short => 1 ); push @rv, @$aref if $aref; } print scalar(@rv). " items found\n" } sub _uptodate { my $self = shift; my $cb = $self->backend; my %hash = @_; my($aref, $short, $input, $class); my $tmpl = { result => { store => \$aref, default => ['/./'] }, short => { default => 0, store => \$short }, input => { default => 'all', store => \$input }, class => { default => 'Uptodate', no_override => 1, store => \$class }, }; check( $tmpl, \%hash ) or return; my @rv; if( @$aref) { for my $module (@$aref) { if( $module =~ m|/(.+)/| ) { my @list = $cb->search( type => 'module', allow => [qr/$1/i] ); ### only add those that are installed and not core push @rv, grep { not $_->package_is_perl_core } grep { $_->installed_file } @list; } else { my $obj = $cb->module_tree( $module ) or next; push @rv, $obj; } } } else { @rv = @{$cb->_all_installed}; } return $self->_pp_uptodate( result => \@rv, class => $class, short => $short, input => $input ); } sub _ls { my $self = shift; my $cb = $self->backend; my %hash = @_; my($aref, $short, $input, $class); my $tmpl = { result => { store => \$aref, default => [] }, short => { default => 0, store => \$short }, input => { default => 'all', store => \$input }, class => { default => 'Uptodate', no_override => 1, store => \$class }, }; check( $tmpl, \%hash ) or return; my @rv; for my $name (@$aref) { my $auth = $cb->author_tree( uc $name ); unless( $auth ) { print qq[ls command rejects argument $name: not an author\n]; next; } push @rv, $auth->distributions; } return $self->_pp_ls( result => \@rv, class => $class, short => $short, input => $input ); } ############################ ### pretty printing subs ### ############################ sub _pp_author { my $self = shift; my %hash = @_; my( $aref, $short, $class, $input ); my $tmpl = { result => { required => 1, default => [], strict_type => 1, store => \$aref }, short => { default => 0, store => \$short }, class => { required => 1, store => \$class }, input => { required => 1, store => \$input }, }; check( $tmpl, \%hash ) or return; ### no results if( !@$aref ) { print "No objects of type $class found for argument $input\n" unless $short; ### one result, long output desired; } elsif( @$aref == 1 and !$short ) { ### should look like this: #cpan> a KANE #Author id = KANE # EMAIL boumans@frg.eur.nl # FULLNAME Jos Boumans my $obj = shift @$aref; print "$class id = ", $obj->cpanid(), "\n"; printf " %-12s %s\n", 'EMAIL', $obj->email(); printf " %-12s %s%s\n", 'FULLNAME', $obj->author(); } else { ### should look like this: #Author KANE (Jos Boumans) #Author LBROCARD (Leon Brocard) #2 items found for my $obj ( @$aref ) { printf qq[%-15s %s ("%s" (%s))\n], $class, $obj->cpanid, $obj->author, $obj->email; } print scalar(@$aref)." items found\n" unless $short; } return $aref; } sub _pp_module { my $self = shift; my %hash = @_; my( $aref, $short, $class, $input ); my $tmpl = { result => { required => 1, default => [], strict_type => 1, store => \$aref }, short => { default => 0, store => \$short }, class => { required => 1, store => \$class }, input => { required => 1, store => \$input }, }; check( $tmpl, \%hash ) or return; ### no results if( !@$aref ) { print "No objects of type $class found for argument $input\n" unless $short; ### one result, long output desired; } elsif( @$aref == 1 and !$short ) { ### should look like this: #Module id = LWP # DESCRIPTION Libwww-perl # CPAN_USERID GAAS (Gisle Aas <gisle@ActiveState.com>) # CPAN_VERSION 5.64 # CPAN_FILE G/GA/GAAS/libwww-perl-5.64.tar.gz # DSLI_STATUS RmpO (released,mailing-list,perl,object-oriented) # MANPAGE LWP - The World-Wide Web library for Perl # INST_FILE C:\Perl\site\lib\LWP.pm # INST_VERSION 5.62 my $obj = shift @$aref; my $aut_obj = $obj->author; my $format = " %-12s %s%s\n"; print "$class id = ", $obj->module(), "\n"; printf $format, 'DESCRIPTION', $obj->description() if $obj->description(); printf $format, 'CPAN_USERID', $aut_obj->cpanid() . " (" . $aut_obj->author() . " <" . $aut_obj->email() . ">)"; printf $format, 'CPAN_VERSION', $obj->version(); printf $format, 'CPAN_FILE', $obj->path() . '/' . $obj->package(); printf $format, 'DSLI_STATUS', $self->_pp_dslip($obj->dslip) if $obj->dslip() =~ /\w/; #printf $format, 'MANPAGE', $obj->foo(); ### this is for bundles... CPAN.pm downloads them, #printf $format, 'CONATAINS, # parses and goes from there... printf $format, 'INST_FILE', $obj->installed_file || '(not installed)'; printf $format, 'INST_VERSION', $obj->installed_version; } else { ### should look like this: #Module LWP (G/GA/GAAS/libwww-perl-5.64.tar.gz) #Module POE (R/RC/RCAPUTO/POE-0.19.tar.gz) #2 items found for my $obj ( @$aref ) { printf "%-15s %-15s (%s)\n", $class, $obj->module(), $obj->path() .'/'. $obj->package(); } print scalar(@$aref). " items found\n" unless $short; } return $aref; } sub _pp_dslip { my $self = shift; my $dslip = shift or return; my (%_statusD, %_statusS, %_statusL, %_statusI); @_statusD{qw(? i c a b R M S)} = qw(unknown idea pre-alpha alpha beta released mature standard); @_statusS{qw(? m d u n)} = qw(unknown mailing-list developer comp.lang.perl.* none); @_statusL{qw(? p c + o h)} = qw(unknown perl C C++ other hybrid); @_statusI{qw(? f r O h)} = qw(unknown functions references+ties object-oriented hybrid); my @status = split("", $dslip); my $results = sprintf( "%s (%s,%s,%s,%s)", $dslip, $_statusD{$status[0]}, $_statusS{$status[1]}, $_statusL{$status[2]}, $_statusI{$status[3]} ); return $results; } sub _pp_distribution { my $self = shift; my $cb = $self->backend; my %hash = @_; my( $aref, $short, $class, $input ); my $tmpl = { result => { required => 1, default => [], strict_type => 1, store => \$aref }, short => { default => 0, store => \$short }, class => { required => 1, store => \$class }, input => { required => 1, store => \$input }, }; check( $tmpl, \%hash ) or return; ### no results if( !@$aref ) { print "No objects of type $class found for argument $input\n" unless $short; ### one result, long output desired; } elsif( @$aref == 1 and !$short ) { ### should look like this: #Distribution id = S/SA/SABECK/POE-Component-Client-POP3-0.02.tar.gz # CPAN_USERID SABECK (Scott Beck <scott@gossamer-threads.com>) # CONTAINSMODS POE::Component::Client::POP3 my $obj = shift @$aref; my $aut_obj = $obj->author; my $pkg = $obj->package; my $format = " %-12s %s\n"; my @list = $cb->search( type => 'package', allow => [qr/^$pkg$/] ); print "$class id = ", $obj->path(), '/', $obj->package(), "\n"; printf $format, 'CPAN_USERID', $aut_obj->cpanid .' ('. $aut_obj->author . ' '. $aut_obj->email .')'; ### yes i know it's ugly, but it's what cpan.pm does printf $format, 'CONTAINSMODS', join (' ', map { $_->module } @list); } else { ### should look like this: #Distribution LWP (G/GA/GAAS/libwww-perl-5.64.tar.gz) #Distribution POE (R/RC/RCAPUTO/POE-0.19.tar.gz) #2 items found for my $obj ( @$aref ) { printf "%-15s %s\n", $class, $obj->path() .'/'. $obj->package(); } print scalar(@$aref). " items found\n" unless $short; } return $aref; } sub _pp_uptodate { my $self = shift; my $cb = $self->backend; my %hash = @_; my( $aref, $short, $class, $input ); my $tmpl = { result => { required => 1, default => [], strict_type => 1, store => \$aref }, short => { default => 0, store => \$short }, class => { required => 1, store => \$class }, input => { required => 1, store => \$input }, }; check( $tmpl, \%hash ) or return; my $format = "%-25s %9s %9s %s\n"; my @not_uptodate; my $no_version; my %seen; for my $mod (@$aref) { next if $mod->package_is_perl_core; next if $seen{ $mod->package }++; if( $mod->installed_file and not $mod->installed_version ) { $no_version++; next; } push @not_uptodate, $mod unless $mod->is_uptodate; } unless( @not_uptodate ) { my $string = $input ? "for $input" : ''; print "All modules are up to date $string\n"; return; } else { printf $format, ( 'Package namespace', 'installed', 'latest', 'in CPAN file' ); } for my $mod ( sort { $a->module cmp $b->module } @not_uptodate ) { printf $format, ( $mod->module, $mod->installed_version, $mod->version, $mod->path .'/'. $mod->package, ); } print "$no_version installed modules have no (parsable) version number\n" if $no_version; return \@not_uptodate; } sub _pp_ls { my $self = shift; my $cb = $self->backend; my %hash = @_; my( $aref, $short, $class, $input ); my $tmpl = { result => { required => 1, default => [], strict_type => 1, store => \$aref }, short => { default => 0, store => \$short }, class => { required => 1, store => \$class }, input => { required => 1, store => \$input }, }; check( $tmpl, \%hash ) or return; ### should look something like this: #6272 2002-05-12 KANE/Acme-Comment-1.00.tar.gz #8171 2002-08-13 KANE/Acme-Comment-1.01.zip #7110 2002-09-04 KANE/Acme-Comment-1.02.tar.gz #7571 2002-09-08 KANE/Acme-Intraweb-1.01.tar.gz #6625 2001-08-23 KANE/Acme-POE-Knee-1.10.zip #3058 2003-10-05 KANE/Acme-Test-0.02.tar.gz ### don't know size or mtime #my $format = "%8d %10s %s/%s\n"; for my $mod ( sort { $a->package cmp $b->package } @$aref ) { print "\t" . $mod->package . "\n"; } return $aref; } ############################# ### end pretty print subs ### ############################# sub _bang { my $self = shift; my %hash = @_; my( $input ); my $tmpl = { input => { required => 1, store => \$input }, }; check( $tmpl, \%hash ) or return; eval $input; warn $@ if $@; print "\n"; return; } sub _help { print qq[ Display Information a authors b string display bundles d or info distributions m /regex/ about modules i or anything of above r none reinstall recommendations u uninstalled distributions Download, Test, Make, Install... get download make make (implies get) test modules, make test (implies make) install dists, bundles make install (implies test) clean make clean look open subshell in these dists' directories readme display these dists' README files Other h,? display this menu ! perl-code eval a perl command o conf [opt] set and query options q quit the cpan shell reload cpan load CPAN.pm again reload index load newer indices autobundle Snapshot force cmd unconditionally do cmd ]; } 1; __END__ =pod =head1 NAME CPANPLUS::Shell::Classic - CPAN.pm emulation for CPANPLUS =head1 DESCRIPTION The Classic shell is designed to provide the feel of the CPAN.pm shell using CPANPLUS underneath. For detailed documentation, refer to L<CPAN>. =head1 BUG REPORTS Please report bugs or other issues to E<lt>bug-cpanplus@rt.cpan.org<gt>. =head1 AUTHOR This module by Jos Boumans E<lt>kane@cpan.orgE<gt>. =head1 COPYRIGHT The CPAN++ interface (of which this module is a part of) is copyright (c) 2001 - 2007, Jos Boumans E<lt>kane@cpan.orgE<gt>. All rights reserved. This library is free software; you may redistribute and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L<CPANPLUS::Configure>, L<CPANPLUS::Module>, L<CPANPLUS::Module::Author> =cut =head1 SEE ALSO L<CPAN> =cut # Local variables: # c-indentation-style: bsd # c-basic-offset: 4 # indent-tabs-mode: nil # End: # vim: expandtab shiftwidth=4: