PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/bin/
Upload File :
Current File : //bin/tasksel
#!/usr/bin/perl
# Debian task selector, mark II.
# Copyright 2004-2011 by Joey Hess <joeyh@debian.org>.
# Licensed under the GPL, version 2 or higher.
use 5.014;
use Locale::gettext;
use Getopt::Long;
use warnings;
use strict;
textdomain('tasksel');

my $debconf_helper="/usr/lib/tasksel/tasksel-debconf";
my $testdir="/usr/lib/tasksel/tests";
my $packagesdir="/usr/lib/tasksel/packages";
my $descdir="/usr/share/tasksel/descs";
my $localdescdir="/usr/local/share/tasksel/descs";
my $statusfile="/var/lib/dpkg/status";
my $infodir="/usr/lib/tasksel/info";

# This boolean indicates whether we are in dry-run (no-do) mode.  More
# specifically, it disables the actual running of commands by the
# &run() function.
my $testmode=0;

my $taskpackageprefix="task-";

sub warning {
	print STDERR "tasksel: @_\n";
}

sub error {
	print STDERR "tasksel: @_\n";
	exit 1;
}

# my $statuscode = &run("ls", "-l", "/tmp");
# => 0
# Run a shell command except in test mode, and returns its exit code.
# Prints the command in test mode. Parameters should be pre-split for
# system.
sub run {
	if ($testmode) {
		print join(" ", @_)."\n";
		return 0;
	}
	else {
		return system(@_) >> 8;
	}
}

# my @paths = &list_task_descs();
# => ("/path/to/debian-tasks.desc", "/some/other/taskfile.desc")
# Get the list of desc files.
sub list_task_descs {
	# Setting DEBIAN_TASKS_ONLY is a way for the Debian installer
	# to tell tasksel to only use the Debian tasks (from
	# tasksel-data).
	if ($ENV{DEBIAN_TASKS_ONLY}) {
		return glob("$descdir/debian-tasks.desc");
	}
	else {
		return glob("$descdir/*.desc"), glob("$localdescdir/*.desc");
	}
}

# &read_task_desc("/path/to/taskfile.desc");
# => (
#      {
#        task => "gnome-desktop",
#        parent => "desktop",
#        relevance => 1,
#        key => [task-gnome-desktop"],
#        section => "user",
#        test-default-desktop => "3 gnome",
#        sortkey => 1desktop-01
#      },
#      ...
#    )
# Returns a list of hashes; hash values are arrays for multi-line fields.
sub read_task_desc {
	my $desc=shift;

        # %tasks maps the name of each task (the Task: field) to its
        # %%data information (that maps each key to value(s), see the
        # %"while" loop below).
	my %tasks;

	open (DESC, "<$desc") || die "Could not open $desc for reading: $!";
	local $/="\n\n";
	while (defined($_ = <DESC>)) {
		# %data will contain the keys/values of the current
		# stanza.
                # 
                # The keys are stored lowercase.
                # 
                # A single-line value is stored as a scalar "line1"; a
                # multi-line value is stored as a ref to array
                # ["line1", "line2"].
                #
                # $data{relevance} is set to 5 if not otherwise
                # specified in the stanza.
		my %data;

		my @lines=split("\n");
		while (@lines) {
			my $line=shift(@lines);
			if ($line=~/^([^ ]+):(?: (.*))?/) {
				my ($key, $value)=($1, $2);
				$key=lc($key);
				if (@lines && $lines[0] =~ /^\s+/) {
					# multi-line field
					my @values;

                                        # Ignore the first line if it is empty.
					if (defined $value && length $value) {
						push @values, $value;
					}

					while (@lines && $lines[0] =~ /^\s+(.*)/) {
						push @values, $1;
						shift @lines;
					}
					$data{$key}=[@values];
				}
				else {
					$data{$key}=$value;
				}
			}
			else {
				warning "$desc: in stanza $.: warning: parse error, ignoring line: $line";
			}
		}
		$data{relevance}=5 unless exists $data{relevance};
		if (exists $data{task}) {
			$tasks{$data{task}} = \%data;
		}
	}
	close DESC;

	my @ret;
        # In this loop, we simultaneously:
        # 
        # - enrich the %data structures of all tasks with a
        #   ->{sortkey} field
        #
        # - and collect them into @ret.
	foreach my $task (keys %tasks) {
		my $t=$tasks{$task};
		if (exists $t->{parent} && exists $tasks{$t->{parent}}) {
                        # This task has a "Parent:" task.  For example:
                        #
                        #   Task: sometask
                        #   Relevance: 3
                        #   Parent: parenttask
                        #
                        #   Task: parenttask
                        #   Relevance: 6
                        #
                        # In this case, we set the sortkey to "6parenttask-03".
                        #
                        # XXX TODO: support correct sorting when
                        # Relevance is 10 or more (e.g. package
                        # education-tasks).
			$t->{sortkey}=$tasks{$t->{parent}}->{relevance}.$t->{parent}."-0".$t->{relevance};
		}
		else {
                        # This task has no "Parent:" task.  For example:
                        # 
                        #   Task: sometask
                        #   Relevance: 3
                        #
                        # In this case, we set the sortkey to "3sometask-00".
			$t->{sortkey}=$t->{relevance}.$t->{task}."-00";
		}
		push @ret, $t;
	}
	return @ret;
}

# &all_tasks();
# => (
#      {
#        task => "gnome-desktop",
#        parent => "desktop",
#        relevance => 1,
#        key => [task-gnome-desktop"],
#        section => "user",
#        test-default-desktop => "3 gnome",
#        sortkey => 1desktop-01
#      },
#      ...
#    )
# Loads info for all tasks, and returns a set of task structures.
sub all_tasks {
	my %seen;
        # Filter out duplicates: only the first occurrence of each
        # task name is taken into account.
	grep { $seen{$_->{task}}++; $seen{$_->{task}} < 2 }
	map { read_task_desc($_) } list_task_descs();
}


# my %apt_available = %_info_avail()
# => (
#   "debian-policy" => { priority => "optional", section => "doc" },
#   ...
# )
# 
# Call "apt-cache dumpavail" and collect the output information about
# package name, priority and section.
sub _info_avail {
	my %ret = ();
	# Might be better to use the perl apt bindings, but they are not
	# currently in base.
	open (AVAIL, "apt-cache dumpavail|");
	local $_;
	my ($package, $section, $priority);
	while (<AVAIL>) {
		chomp;
		if (not $_) {
                        # End of stanza
			if (defined $package && defined $priority && defined $section) {
				$ret{$package} = {
				       	"priority" => $priority,
					"section" => $section,
				};
			}
		}
		elsif (/^Package: (.*)/) {
			$package = $1;
		}
		elsif (/^Priority: (.*)/) {
			$priority = $1;
		}
		elsif (/^Section: (.*)/) {
			$section = $1;
		}
	}
	close AVAIL;
	return %ret;
}

# my @installed = &list_installed();
# => ("emacs", "vim", ...)
# Returns a list of all installed packages.
# This is not memoised and will run dpkg-query at each invocation.
# See &package_installed() for memoisation.
sub list_installed {
	my @list;
	open (LIST, q{LANG=C dpkg-query -W -f='${Package} ${Status}\n' |});
	while (<LIST>) {
                # Each line looks like this:
                # "adduser install ok installed"
		if (/^([^ ]+) .* installed$/m) {
			push @list, $1;
		}
	}
	close LIST;
	return @list;
}

my %_info_avail_cache;

# my $apt_available = &info_avail();
# => {
#   "debian-policy" => { priority => "optional", section => "doc" },
#   ...
# }
# Returns a hash of all available packages.  Memoised.
sub info_avail {
	my $package = shift;
	if (!%_info_avail_cache) {
		%_info_avail_cache = _info_avail();
	}
	return \%_info_avail_cache;
}

# if (&package_avail("debian-policy")) { ... }
# Given a package name, checks to see if it's installed or available.
# Memoised.
sub package_avail {
	my $package = shift;
	return info_avail()->{$package} || package_installed($package);
}

# Memoisation for &package_installed().
my %installed_pkgs;

# if (&package_installed("debian-policy")) { ... }
# Given a package name, checks to see if it's installed.  Memoised.
sub package_installed {
	my $package=shift;
	
	if (! %installed_pkgs) {
		foreach my $pkg (list_installed()) {
			$installed_pkgs{$pkg} = 1;
		}
	}

	return $installed_pkgs{$package};
}

# if (&task_avail($task)) { ... }
# Given a task hash, checks that all of its key packages are installed or available.
# Returns true if all key packages are installed or available.
# Returns false if any of the key packages is not.
sub task_avail {
	local $_;
	my $task=shift;
	if (! ref $task->{key}) {
		return 1;
	}
	else {
		foreach my $pkg (@{$task->{key}}) {
			if (! package_avail($pkg)) {
				return 0;
			}
		}
		return 1;
	}
}

# if (&task_installed($task)) { ... }
# Given a task hash, checks to see if it is already installed.
# All of its key packages must be installed.  Other packages are not checked.
sub task_installed {
	local $_;
	my $task=shift;
	if (! ref $task->{key}) {
		return 0; # can't tell with no key packages
	}
	else {
		foreach my $pkg (@{$task->{key}}) {
			if (! package_installed($pkg)) {
				return 0;
			}
		}
		return 1;
	}
}

# my @packages = &task_packages($task);
# Given a task hash, returns a list of all available packages in the task.
# 
# It is the list of "Key:" packages, plus the packages indicated
# through the "Packages:" field.
sub task_packages {
	my $task=shift;
	
        # The %list hashtable is used as a set: only its keys matter,
        # the value is irrelevant.
	my %list;

	# "Key:" packages are always included.
	if (ref $task->{key}) {
                # $task->{key} is not a line but a reference (to an
                # array of lines).
		map { $list{$_}=1 } @{$task->{key}};
	}
	
	if (! defined $task->{packages}) {
                # No "Packages:" field.
		# only key
	}
	elsif ($task->{packages} eq 'standard') {
                # Special case of "Packages: standard"
                #
                # The standard packages are the non-library ones in
                # "main" which priority is required, important or
                # standard.
                #
                # We add all standard packages to %list, except the
                # ones that are already installed.
		my %info_avail=%{info_avail()};
		while (my ($package, $info) = each(%info_avail)) {
			my ($priority, $section) = ($info->{priority}, $info->{section});
			if (($priority eq 'required' ||
			     $priority eq 'important' ||
			     $priority eq 'standard') &&
		            # Exclude packages in non-main and library sections
		            $section !~ /^lib|\// &&
			    # Exclude already installed packages
		            !package_installed($package)) {
				$list{$package} = 1;
			}
		}
	}
	else {
		# external method
		my ($method, @params);

                # "Packages:" requests to run a program and use its
                # output as the names of packages.
                #
                # There are basically two forms:
                #
                #   Packages: myprogram
                #
                # Runs /usr/lib/tasksel/packages/myprogram TASKNAME
                #
                #   Packages: myprogram
                #     arg1
                #     arg2...
                #
                # Runs /usr/lib/tasksel/packages/myprogram TASKNAME arg1 arg2...
                #
                # The tasksel package provides the simple "list"
                # program which simply outputs its arguments.
		if (ref $task->{packages}) {
			@params=@{$task->{packages}};
			$method=shift @params;
		}
		else {
			$method=$task->{packages};
		}
		
		map { $list{$_}=1 }
			grep { package_avail($_) }
			split(' ', `$packagesdir/$method $task->{task} @params`);
	}

	return keys %list;
}

# &task_test($task, $new_install, $display_by_default, $install_by_default);
# Given a task hash, runs any test program specified in its data, and sets
# the _display and _install fields to 1 or 0 depending on its result.
#
# If _display is true, _install means the default proposal shown to
# the user, who can modify it.  If _display is false, _install says
# what to do, without asking the user.
sub task_test {
	my $task=shift;
	my $new_install=shift;
	$task->{_display} = shift; # default
	$task->{_install} = shift; # default
	$ENV{NEW_INSTALL}=$new_install if defined $new_install;
        # Each task may define one or more tests in the form:
        #
        #   Test-PROGRAM: ARGUMENTS...
        #
        # Each of the programs will be run like this:
        #
        #   /usr/lib/tasksel/tests/PROGRAM TASKNAME ARGUMENTS...
        #
        # If $new_install is true, the NEW_INSTALL environment
        # variable is set for invoking the program.
        #
        # The return code of the invocation then indicates what to set:
        #
        #   0 - don't display, but install it
        #   1 - don't display, don't install
        #   2 - display, mark for installation
        #   3 - display, don't mark for installation
        #   anything else - don't change the values of _display or _install
	foreach my $test (grep /^test-.*/, keys %$task) {
		$test=~s/^test-//;
		if (-x "$testdir/$test") {
			my $ret=system("$testdir/$test", $task->{task}, split " ", $task->{"test-$test"}) >> 8;
			if ($ret == 0) {
				$task->{_display} = 0;
				$task->{_install} = 1;
			}
			elsif ($ret == 1) {
				$task->{_display} = 0;
				$task->{_install} = 0;
			}
			elsif ($ret == 2) {
				$task->{_display} = 1;
				$task->{_install} = 1;
			}
			elsif ($ret == 3) {
				$task->{_display} = 1;
				$task->{_install} = 0;
			}
		}
	}
	
	delete $ENV{NEW_INSTALL};
	return $task;
}

# &hide_enhancing_tasks($task);
# 
# Hides a task and marks it not to be installed if it enhances other
# tasks.
#
# Returns $task.
sub hide_enhancing_tasks {
	my $task=shift;
	if (exists $task->{enhances} && length $task->{enhances}) {
		$task->{_display} = 0;
		$task->{_install} = 0;
	}
	return $task;
}

# &getdescriptions(@tasks);
# 
# Looks up the descriptions of a set of tasks, returning a new list
# with the ->{shortdesc} fields filled in.
#
# Ideally, the .desc file would indicate a description of each task,
# which would be retrieved quickly.  For missing Description fields,
# we fetch the data with "apt-cache show task-TASKNAME...", which
# takes longer.
#
# @tasks: list of references, each referencing a task data structure.
# 
# Each data structured is enriched with a ->{shortdesc} field,
# containing the localized short description.
#
# Returns @tasks.
sub getdescriptions {
	my @tasks=@_;

	# If the task has a description field in the task desc file,
	# just use it, looking up a translation in gettext.
	@tasks = map {
		if (defined $_->{description}) {
			$_->{shortdesc}=dgettext("debian-tasks", $_->{description}->[0]);
		}
		$_;
	} @tasks;

	# Otherwise, a more expensive apt-cache query is done,
	# to use the descriptions of task packages.
	my @todo = grep { ! defined $_->{shortdesc} } @tasks;
	if (@todo) {
		open(APT_CACHE, "apt-cache show ".join(" ", map { $taskpackageprefix.$_->{task} } @todo)." |") || die "apt-cache show: $!";
		local $/="\n\n";
		while (defined($_ = <APT_CACHE>)) {
			my ($name)=/^Package: $taskpackageprefix(.*)$/m;
			my ($description)=/^Description-(?:[a-z][a-z](?:_[A-Z][A-Z])?): (.*)$/m;
			($description)=/^Description: (.*)$/m
				unless defined $description;
			if (defined $name && defined $description) {
				@tasks = map {
					if ($_->{task} eq $name) {
						$_->{shortdesc}=$description;
					}
					$_;
				} @tasks;
			}
		}
		close APT_CACHE;
	}

	return @tasks;
}

# &task_to_debconf(@tasks);
# => "task1, task2, task3"
# Converts a list of tasks into a debconf list of the task short
# descriptions.
sub task_to_debconf {
	join ", ", map { format_description_for_debconf($_) } getdescriptions(@_);
}

# my $debconf_string = &format_description_for_debconf($task);
# => "... GNOME"
# Build a string for making a debconf menu item.
# If the task has a parent task, "... " is prepended.
sub format_description_for_debconf {
	my $task=shift;
	my $d=$task->{shortdesc};
	$d=~s/,/\\,/g;
	$d="... ".$d if exists $task->{parent};
	return $d;
}

# my $debconf_string = &task_to_debconf_C(@tasks);
# => "gnome-desktop, kde-desktop"
# Converts a list of tasks into a debconf list of the task names.
sub task_to_debconf_C {
	join ", ", map { $_->{task} } @_;
}

# my @my_tasks = &list_to_tasks("task1, task2, task3", @tasks);
# => ($task1, $task2, $task3)
# Given a first parameter that is a string listing task names, and then a
# list of task hashes, returns a list of hashes for all the tasks
# in the list.
sub list_to_tasks {
	my $list=shift;
	my %lookup = map { $_->{task} => $_ } @_;
	return grep { defined } map { $lookup{$_} } split /[, ]+/, $list;
}

# my @sorted_tasks = &order_for_display(@tasks);
# Orders a list of tasks for display.
# The tasks are ordered according to the ->{sortkey}.
sub order_for_display {
	sort {
		$a->{sortkey} cmp $b->{sortkey}
		              || 0 ||
	        $a->{task} cmp $b->{task}
	} @_;
}

# &name_to_task($taskname, &all_tasks());
# &name_to_task("gnome-desktop", &all_tasks());
# => {
#      task => "gnome-desktop",
#      parent => "desktop",
#      relevance => 1,
#      key => [task-gnome-desktop"],
#      section => "user",
#      test-default-desktop => "3 gnome",
#      sortkey => 1desktop-01
#    }
# Given a set of tasks and a name, returns the one with that name.
sub name_to_task {
	my $name=shift;
	return (grep { $_->{task} eq $name } @_)[0];
}

# &task_script($task, "preinst") or die;
# Run the task's (pre|post)(inst|rm) script, if there is any.
# Such scripts are located under /usr/lib/tasksel/info/.
sub task_script {
	my $task=shift;
	my $script=shift;

	my $path="$infodir/$task.$script";
	if (-e $path && -x _) {
		my $ret=run($path);
		if ($ret != 0) {
			warning("$path exited with nonzero code $ret");
			return 0;
		}
	}
	return 1;
}

# &usage;
# Print the usage.
sub usage {
        print STDERR gettext(q{tasksel [OPTIONS...] [COMMAND...]
 Commands:
  install TASK...       install tasks
  remove TASK...        uninstall tasks
  --task-packages=TASK  list packages installed by TASK; can be repeated
  --task-desc=TASK      print the description of a task
  --list-tasks          list tasks that would be displayed and exit
 Options:
  -t, --test            dry-run: don't really change anything
      --new-install     automatically install some tasks
  --debconf-apt-progress="ARGUMENTS..."
                        provide additional arguments to debconf-apt-progress(1)
});
}

# Process command line options and return them in a hash.
sub getopts {
	my %ret;
	Getopt::Long::Configure ("bundling");
	if (! GetOptions(\%ret, "test|t", "new-install", "list-tasks",
		   "task-packages=s@", "task-desc=s",
		   "debconf-apt-progress=s")) {
		usage();
		exit(1);
	}
	# Special case apt-like syntax.
	if (@ARGV) {
		my $cmd = shift @ARGV;
		if ($cmd eq "install") {
			$ret{cmd_install} = \@ARGV;
		}
		elsif ($cmd eq "remove") {
			$ret{cmd_remove} = \@ARGV;
		}
		else {
			usage();
			exit 1;
		}
	}
	$testmode=1 if $ret{test}; # set global
	return %ret;
}

# &interactive($options, @tasks);
# Ask the user and mark tasks to install or remove accordingly.
# The tasks are enriched with ->{_install} or ->{_remove} set to true accordingly.
sub interactive {
	my $options = shift;
	my @tasks = @_;

	if (! $options->{"new-install"}) {
		# Don't install hidden tasks if this is not a new install.
		map { $_->{_install} = 0 } grep { $_->{_display} == 0 } @tasks;
	}

	my @list = order_for_display(grep { $_->{_display} == 1 } @tasks);
	if (@list) {
		if (! $options->{"new-install"}) {
			# Find tasks that are already installed.
			map { $_->{_installed} = task_installed($_) } @list;
			# Don't install new tasks unless manually selected.
			map { $_->{_install} = 0 } @list;
		}
		else {
			# Assume that no tasks are installed, to ensure
			# that complete tasks get installed on new
			# installs.
			map { $_->{_installed} = 0 } @list;
		}
		my $question="tasksel/tasks";
		if ($options->{"new-install"}) {
			$question="tasksel/first";
		}
		my @default = grep { $_->{_display} == 1 && ($_->{_install} == 1 || $_->{_installed} == 1) } @tasks;
		my $tmpfile=`mktemp`;
		chomp $tmpfile;
		my $ret=system($debconf_helper, $tmpfile,
			task_to_debconf_C(@list),
			task_to_debconf(@list),
			task_to_debconf_C(@default),
			$question) >> 8;
		if ($ret == 30) {
			exit 10; # back up
		}
		elsif ($ret != 0) {
			error "debconf failed to run";
		}
		open(IN, "<$tmpfile");
		$ret=<IN>;
		if (! defined $ret) {
			die "tasksel canceled\n";
		}
		chomp $ret;
		close IN;
		unlink $tmpfile;
		
		# Set _install flags based on user selection.
		map { $_->{_install} = 0 } @list;
		foreach my $task (list_to_tasks($ret, @tasks)) {
			if (! $task->{_installed}) {
				$task->{_install} = 1;
			}
			$task->{_selected} = 1;
		}
		foreach my $task (@list) {
			if (! $task->{_selected} && $task->{_installed}) {
				$task->{_remove} = 1;
			}
		}
	}

        # When a $task Enhances: a @group_of_tasks, it means that
        # $task can only be installed if @group_of_tasks are also
        # installed; and if @group_of_tasks is installed, it is an
        # incentive to also install $task.
        #
        # For example, consider this task:
        # 
        #   Task: amharic-desktop
        #   Enhances: desktop, amharic
        #
        # The task amharic-desktop installs packages that make
        # particular sense if the user wants both a desktop and the
        # amharic language environment.  Conversely, if
        # amharic-desktop is selected (e.g. by preseeding), then it
        # automatically also selects tasks "desktop" and "amharic".

	# If an enhancing task is already marked for
	# install, probably by preseeding, mark the tasks
	# it enhances for install.
	foreach my $task (grep { $_->{_install} && exists $_->{enhances} &&
	                         length $_->{enhances} } @tasks) {
		map { $_->{_install}=1 } list_to_tasks($task->{enhances}, @tasks);
	}

	# Select enhancing tasks for install.
	# XXX FIXME ugly hack -- loop until enhances settle to handle
	# chained enhances. This is ugly and could loop forever if
	# there's a cycle.
	my $enhances_needswork=1;

        # %tested is the memoization of the below calls to
        # %&task_test().
	my %tested;

        # Loop as long as there is work to do.
	while ($enhances_needswork) {
		$enhances_needswork=0;

                # Loop over all unselected tasks that enhance one or
                # more things.
		foreach my $task (grep { ! $_->{_install} && exists $_->{enhances} &&
		                         length $_->{enhances} } @tasks) {
                        # TODO: the computation of %tasknames could be
                        # done once and for all outside of this nested
                        # loop, saving some redundant work.
			my %tasknames = map { $_->{task} => $_ } @tasks;

                        # @deps is the list of tasks enhanced by $task.
                        # 
                        # Basically, if all the deps are installed,
                        # and tests say that $task can be installed,
                        # then mark it to install.  Otherwise, don't
                        # install it.
			my @deps=map { $tasknames{$_} } split ", ", $task->{enhances};

			if (grep { ! defined $_ } @deps) {
				# task enhances an unavailable or
				# uninstallable task
				next;
			}

			if (@deps) {
                                # FIXME: isn't $orig_state always
                                # false, given that the "for" loop
                                # above keeps only $tasks that do
                                # not have $_->{_install}?
				my $orig_state=$task->{_install};

				# Mark enhancing tasks for install if their
				# dependencies are met and their test fields
				# mark them for install.
				if (! exists $tested{$task->{task}}) {
					$ENV{TESTING_ENHANCER}=1;
					task_test($task, $options->{"new-install"}, 0, 1);
					delete $ENV{TESTING_ENHANCER};
					$tested{$task->{task}}=$task->{_install};
				}
				else {
					$task->{_install}=$tested{$task->{task}};
				}

				foreach my $dep (@deps) {
					if (! $dep->{_install}) {
						$task->{_install} = 0;
					}
				}

				if ($task->{_install} != $orig_state) {
					# We have made progress:
					# continue another round.
                                        $enhances_needswork=1;
				}
			}
		}
	}

}

sub main {
	my %options=getopts();
	my @tasks_remove;
	my @tasks_install;

	# Options that output stuff and don't need a full processed list of
	# tasks.
	if (exists $options{"task-packages"}) {
		my @tasks=all_tasks();
		foreach my $taskname (@{$options{"task-packages"}}) {
			my $task=name_to_task($taskname, @tasks);
			if ($task) {
				print "$_\n" foreach task_packages($task);
			}
		}
		exit(0);
	}
	elsif ($options{"task-desc"}) {
		my $task=name_to_task($options{"task-desc"}, all_tasks());
		if ($task) {
                        # The Description looks like this:
                        #
                        #   Description: one-line short description
                        #     Longer description,
                        #     possibly spanning
                        #     multiple lines.
                        #
                        # $extdesc will contain the long description,
                        # reformatted to one line.
			my $extdesc=join(" ", @{$task->{description}}[1..$#{$task->{description}}]);
			print dgettext("debian-tasks", $extdesc)."\n";
			exit(0);
		}
		else {
			fprintf STDERR ("Task %s has no description\n", $options{"task-desc"});
			exit(1);
		}
	}

	# This is relatively expensive, get the full list of available tasks and
	# mark them.
	my @tasks=map { hide_enhancing_tasks($_) } map { task_test($_, $options{"new-install"}, 1, 0) }
	          grep { task_avail($_) } all_tasks();
	
	if ($options{"list-tasks"}) {
		map { $_->{_installed} = task_installed($_) } @tasks;
		@tasks=getdescriptions(@tasks);
                # TODO: use printf() instead of print for correct column alignment
		print "".($_->{_installed} ? "i" : "u")." ".$_->{task}."\t".$_->{shortdesc}."\n"
			foreach order_for_display(grep { $_->{_display} } @tasks);
		exit(0);
	}
	
	if ($options{cmd_install}) {
		@tasks_install = map { name_to_task($_, @tasks) } @{$options{cmd_install}};
	}
	elsif ($options{cmd_remove}) {
		@tasks_remove = map { name_to_task($_, @tasks) } @{$options{cmd_remove}};
	}
	else {
		interactive(\%options, @tasks);

		# Add tasks to install
		@tasks_install = grep { $_->{_install} } @tasks;
		# Add tasks to remove
		@tasks_remove = grep { $_->{_remove} } @tasks;
	}

	my @cmd;
	if (-x "/usr/bin/debconf-apt-progress") {
		@cmd = "debconf-apt-progress";
		push @cmd, split(' ', $options{'debconf-apt-progress'})
			if exists $options{'debconf-apt-progress'};
		push @cmd, "--";
	}
	push @cmd, qw{apt-get -q -y -o APT::Install-Recommends=true -o APT::Get::AutomaticRemove=true -o Acquire::Retries=3 install};

	# And finally, act on selected tasks.
	if (@tasks_install || @tasks_remove) {
		foreach my $task (@tasks_remove) {
			push @cmd, map { "$_-" } task_packages($task);
			task_script($task->{task}, "prerm");
		}
		foreach my $task (@tasks_install) {
			push @cmd, task_packages($task);
			task_script($task->{task}, "preinst");
		}
		my $ret=run(@cmd);
		if ($ret != 0) {
			error gettext("apt-get failed")." ($ret)";
		}
		foreach my $task (@tasks_remove) {
			task_script($task->{task}, "postrm");
		}
		foreach my $task (@tasks_install) {
			task_script($task->{task}, "postinst");
		}
	}
}

main();