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:/usr/lib/python3/dist-packages/reportbug/ui/
Upload File :
Current File : //usr/lib/python3/dist-packages/reportbug/ui/text_ui.py
# Text user interface for reportbug
#   Written by Chris Lawrence <lawrencc@debian.org>
#   (C) 2001-08 Chris Lawrence
#   Copyright (C) 2008-2022 Sandro Tosi <morph@debian.org>
#
# This program is freely distributable per the following license:
#
#  Permission to use, copy, modify, and distribute this software and its
#  documentation for any purpose and without fee is hereby granted,
#  provided that the above copyright notice appears in all copies and that
#  both that copyright notice and this permission notice appear in
#  supporting documentation.
#
#  I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL I
#  BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
#  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
#  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
#  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
#  SOFTWARE.

import sys
import os
import subprocess
import re
import math
import string
import glob
import getpass
import textwrap
import locale
from contextlib import suppress
try:
    import readline
except ImportError:
    readline = None

from reportbug import debbugs, hiermatch
from reportbug.exceptions import (
    NoReport, NoPackage, NoBugs, QuertBTSError,
    InvalidRegex,
)
from reportbug.urlutils import launch_browser
import reportbug.utils

ISATTY = sys.stdin.isatty()
charset = 'us-ascii'

try:
    r, c = subprocess.getoutput('stty size').split()
    rows, columns = int(r) or 24, int(c) or 79
except Exception:
    rows, columns = 24, 79


def ewrite(message, *args):
    if not ISATTY:
        return

    if args:
        message = message % args

    sys.stderr.write(message)
    sys.stderr.flush()


log_message = ewrite
display_failure = ewrite


def system(cmdline):
    try:
        _ = os.getcwd()
    except OSError:
        os.chdir('/')
    return os.system(cmdline)


def indent_wrap_text(text, starttext='', indent=0, linelen=None):
    """Wrapper for textwrap.fill to the existing API."""
    if not linelen:
        linelen = columns - 1

    if indent:
        si = ' ' * indent
    else:
        si = ''

    text = ' '.join(text.split())
    if not text:
        return starttext + '\n'

    output = textwrap.fill(text, width=linelen, initial_indent=starttext,
                           subsequent_indent=si)
    if output.endswith('\n'):
        return output
    return output + '\n'


# Readline support, if available
if readline is not None:
    readline.parse_and_bind("tab: complete")
    try:
        # minimize the word delimiter list if possible
        readline.set_completer_delims(' ')
    except Exception:
        pass


def _launch_mbox_reader(mbox_reader_cmd, bts, bugs, number, mirrors, archived,
                        mbox, http_proxy, timeout):
    try:
        number = int(number)
        if number not in bugs and 1 <= number <= len(bugs):
            number = bugs[number - 1]
        reportbug.utils.launch_mbox_reader(mbox_reader_cmd,
                                           debbugs.get_report_url(bts, number, mirrors, archived, mbox),
                                           http_proxy, timeout)
    except ValueError:
        ewrite('Invalid report number: %s\n',
               number)


class our_completer(object):
    def __init__(self, completions=None):
        self.completions = None
        if completions:
            self.completions = tuple(map(str, completions))

    def complete(self, text, i):
        if not self.completions:
            return None

        matching = [x for x in self.completions if x.startswith(text)]
        if i < len(matching):
            return matching[i]
        else:
            return None


def our_raw_input(prompt=None, completions=None, completer=None):
    istty = sys.stdout.isatty()
    if not istty:
        sys.stderr.write(prompt)
    sys.stderr.flush()
    if readline:
        if completions and not completer:
            completer = our_completer(completions).complete
        if completer:
            readline.set_completer(completer)

    try:
        if istty:
            ret = input(prompt)
        else:
            ret = input()
    except EOFError:
        ewrite('\nUser interrupt (^D).\n')
        raise SystemExit

    if readline:
        readline.set_completer(None)
    return ret.strip()


def select_options(msg, ok, help, allow_numbers=None, nowrap=False):
    err_message = ''
    for option in ok:
        if option in string.ascii_uppercase:
            default = option
            break

    if not help:
        help = {}

    if '?' not in ok:
        ok = ok + '?'

    msg = msg.rstrip()
    if msg[-1] == '?':
        msg = msg[:-1]

    if nowrap:
        longmsg = msg + ' [' + '|'.join(ok) + ']?' + ' '
    else:
        longmsg = indent_wrap_text(msg + ' [' + '|'.join(ok) + ']?').strip() + ' '
    ch = our_raw_input(longmsg, allow_numbers)
    # Allow entry of a bug number here
    if allow_numbers:
        while ch and ch[0] == '#':
            ch = ch[1:]
        if isinstance(allow_numbers, int):
            try:
                return str(int(ch))
            except ValueError:
                pass
        else:
            try:
                number = int(ch)
                if number in allow_numbers:
                    return str(number)
                else:
                    nums = list(allow_numbers)
                    nums.sort()
                    err_message = 'Only the following entries are allowed: ' + \
                                  ', '.join(map(str, nums))
            except (ValueError, TypeError):
                pass

    if not ch:
        ch = default
    ch = ch[0]
    if ch == '?':
        help['?'] = 'Display this help.'
        for ch in ok:
            if ch in string.ascii_uppercase:
                desc = '(default) '
            else:
                desc = ''
            desc += help.get(ch, help.get(ch.lower(),
                                          'No help for this option.'))
            ewrite(indent_wrap_text(desc + '\n', '%s - ' % ch, 4))
        return select_options(msg, ok, help, allow_numbers, nowrap)
    elif (ch.lower() in ok) or (ch.upper() in ok):
        return ch.lower()
    elif err_message:
        ewrite(indent_wrap_text(err_message))
    else:
        ewrite('Invalid selection.\n')

    return select_options(msg, ok, help, allow_numbers, nowrap)


def yes_no(msg, yeshelp, nohelp, default=True, nowrap=False):
    "Return True for yes, False for no."
    if default:
        ok = 'Ynq'
    else:
        ok = 'yNq'

    res = select_options(msg, ok, {'y': yeshelp, 'n': nohelp, 'q': 'Quit.'},
                         nowrap=nowrap)
    if res == 'q':
        raise SystemExit
    return (res == 'y')


def long_message(text, *args):
    if args:
        mtext = text % args
    else:
        mtext = text

    linelen = columns - 1
    # we do not use indent_wrap_text here because it does not preserve newlines
    mtext = '\n'.join(textwrap.fill(par, width=linelen) for par in mtext.splitlines())
    mtext = mtext.rstrip() + '\n'
    ewrite(mtext)


final_message = long_message


def get_string(prompt, options=None, title=None, empty_ok=False, force_prompt=False,
               default='', completer=None):
    if prompt and (len(prompt) < 2 * columns / 3) and not force_prompt:
        if default:
            prompt = '%s [%s]: ' % (prompt, default)
            response = our_raw_input(prompt, options, completer) or default
        else:
            response = our_raw_input(prompt, options, completer)
    else:
        if prompt:
            ewrite(indent_wrap_text(prompt))
        if default:
            response = our_raw_input('[%s]> ' % default, options, completer) or default
        else:
            response = our_raw_input('> ', options, completer)

    return response


def get_multiline(prompt):
    ewrite('\n')
    ewrite(indent_wrap_text(prompt + "  Press ENTER on a blank line to continue.\n"))
    ret = []
    while 1:
        entry = get_string('', force_prompt=True).strip()
        if not entry:
            break
        ret.append(entry)
    ewrite('\n')
    return ret


def get_password(prompt=None):
    return getpass.getpass(prompt)


def FilenameCompleter(text, i):
    text = os.path.expanduser(text)
    text = os.path.expandvars(text)
    paths = glob.glob(text + '*')
    if not paths:
        return None

    if i < len(paths):
        entry = paths[i]
        if os.path.isdir(entry):
            return entry + '/'
        return entry
    else:
        return None


def get_filename(prompt, title=None, force_prompt=False, default=''):
    return get_string(prompt, title=title, force_prompt=force_prompt,
                      default=default, completer=FilenameCompleter)


def select_multiple(par, options, prompt, title=None, order=None, extras=None):
    return menu(par, options, prompt, title=title, order=order, extras=extras,
                multiple=True, empty_ok=False)


def menu(par, options, prompt, default=None, title=None, any_ok=False,
         order=None, extras=None, multiple=False, empty_ok=False):
    selected = {}

    if not extras:
        extras = []
    else:
        extras = list(extras)

    if title:
        ewrite(title + '\n\n')

    ewrite(indent_wrap_text(par, linelen=columns) + '\n')

    if isinstance(options, dict):
        options = options.copy()
        # Convert to a list
        if order:
            olist = []
            for key in order:
                if key in options:
                    olist.append((key, options[key]))
                    del options[key]

            # Append anything out of order
            options = list(options.items())
            options.sort()
            for option in options:
                olist.append(option)
            options = olist
        else:
            options = list(options.items())
            options.sort()

    if multiple:
        options.append(('none', ''))
        default = 'none'
        extras += ['done']

    allowed = [x[0] for x in options]
    allowed = allowed + extras

    maxlen_name = min(max(list(map(len, allowed))), columns // 3)
    digits = int(math.ceil(math.log10(len(options) + 1)))

    i = 1
    for name, desc in options:
        text = indent_wrap_text(desc, indent=(maxlen_name + digits + 3),
                                starttext=('%*d %-*.*s  ' % (digits, i, maxlen_name, maxlen_name, name)))
        ewrite(text)
        if len(options) < 5:
            ewrite('\n')
        i += 1
    if len(options) >= 5:
        ewrite('\n')

    if multiple:
        prompt += '(one at a time) '

    while 1:
        if default:
            aprompt = prompt + '[%s] ' % default
        else:
            aprompt = prompt

        response = our_raw_input(aprompt, allowed)
        if not response:
            response = default

        try:
            num = int(response)
            if 1 <= num <= len(options):
                response = options[num - 1][0]
        except (ValueError, TypeError):
            pass

        if response in allowed or (response == default and response):
            if multiple:
                if response == 'done':
                    return list(selected.keys())
                elif response == 'none':
                    if len(selected) == 0:
                        return []
                    else:
                        selected = {}
                elif selected.get(response):
                    del selected[response]
                else:
                    selected[response] = 1
                ewrite('- selected: %s\n' % ', '.join(list(selected.keys())))
                if len(selected):
                    default = 'done'
                else:
                    default = 'none'
                continue
            else:
                return response

        if any_ok and response:
            return response
        elif empty_ok and not response:
            return

        ewrite('Invalid entry.\n')
    return


# Things that are very UI dependent go here
def show_report(number, system, mirrors,
                http_proxy, timeout, screen=None, queryonly=False, title='',
                archived='no', mbox_reader_cmd=None):
    sysinfo = debbugs.SYSTEMS[system]
    ewrite('Retrieving report #%d from %s bug tracking system...\n',
           number, sysinfo['name'])

    try:
        info = debbugs.get_report(number, timeout, system, mirrors=mirrors,
                                  followups=1, http_proxy=http_proxy, archived=archived)
    except Exception:
        info = None

    if not info:
        ewrite('No report available: #%s\n', number)
        raise NoBugs

    buginfo, messages = info

    # XXX: could this be ever possible?
    if not (buginfo.package or not buginfo.source):
        ewrite('Cannot retrieve bug\'s package, exiting...\n')
        sys.exit(-1)

    current_message = 0
    skip_pager = False

    while 1:
        if current_message:
            text = 'Followup %d - %s\n\n%s' % (current_message, buginfo.subject,
                                               messages[current_message])
        else:
            text = 'Original report - %s\n\n%s' % (buginfo.subject, messages[0])

        if not skip_pager:
            with suppress(BrokenPipeError):
                with subprocess.Popen('sensible-pager', stdin=subprocess.PIPE, errors='ignore') as fd:
                    fd.communicate(text)
        skip_pager = False

        options = 'xOrbeq'

        if (current_message + 1) < len(messages):
            options = 'N' + options.lower()
        if (current_message):
            options = 'p' + options

        x = select_options("What do you want to do now?", options,
                           {'x': 'Provide extra information.',
                            'o': 'Show other bug reports (return to bug listing).',
                            'n': 'Show next message (followup).',
                            'p': 'Show previous message (followup).',
                            'r': 'Redisplay this message.',
                            'e': 'Launch e-mail client to read full log.',
                            'b': 'Launch web browser to read full log.',
                            'q': "I'm bored; quit please."},
                           allow_numbers=list(range(1, len(messages) + 1)))
        if x == 'x':
            return buginfo
        elif x == 'q':
            raise NoReport
        elif x == 'b':
            launch_browser(debbugs.get_report_url(
                system, number, mirrors, archived))
            skip_pager = True
        elif x == 'e':
            reportbug.utils.launch_mbox_reader(mbox_reader_cmd,
                                               debbugs.get_report_url(system, number, mirrors, archived, True),
                                               http_proxy, timeout)
            skip_pager = True
        elif x == 'o':
            break
        elif x == 'n':
            current_message += 1
        elif x == 'p':
            current_message -= 1
    return


def handle_bts_query(package, bts, timeout, mirrors=None, http_proxy="",
                     queryonly=False, title="", screen=None, archived='no',
                     source=False, version=None, mbox=False, buglist=None,
                     mbox_reader_cmd=None, latest_first=False):
    root = debbugs.SYSTEMS[bts].get('btsroot')
    if not root:
        ewrite('%s bug tracking system has no web URL; bypassing query\n',
               debbugs.SYSTEMS[bts]['name'])
        return

    srcstr = ""
    if source:
        srcstr = " (source)"

    if isinstance(package, str):
        long_message('Querying %s BTS for reports on %s%s...\n',
                     debbugs.SYSTEMS[bts]['name'], package, srcstr)
    else:
        long_message('Querying %s BTS for reports on %s%s...\n',
                     debbugs.SYSTEMS[bts]['name'],
                     ' '.join([str(x) for x in package]), srcstr)

    bugs = []
    try:
        (count, title, hierarchy) = debbugs.get_reports(
            package, timeout, bts, mirrors=mirrors, version=version,
            source=source, http_proxy=http_proxy, archived=archived)
    except Exception as e:
        errmsg = 'Unable to connect to %s BTS (error: "%s"); ' % (debbugs.SYSTEMS[bts]['name'], repr(e))
        raise QuertBTSError(errmsg)

    try:
        # If there's no report, then skip all the rest
        if not count:
            if hierarchy is None:
                raise NoPackage
            else:
                raise NoBugs

        hierarchy_new = []

        if mbox:
            mboxbuglist = []
            for entry in hierarchy:
                for bug in entry[1]:
                    mboxbuglist.append(bug.bug_num)
            return mboxbuglist

        if buglist:
            for entry in hierarchy:
                # second item is a list of bugs report
                for bug in entry[1]:
                    msg = "#%d  %s" % (bug.bug_num, bug.subject)
                    print(msg)
            sys.exit(0)

        for entry in hierarchy:
            # first item is the title of the section
            entry_new = entry[0]
            bugs_new = {}
            # second item is a list of bug report objects
            for bug in entry[1]:
                # show if the bug is already resolved
                done = ''
                if bug.pending == 'done':
                    done = '  [RESOLVED]'
                # we take the info we need (bug number and subject)
                # we use a dict so it's easy to sort the keys
                bugs_new[bug.bug_num] = "%s%s" % (bug.subject, done)
            # then we sort the list
            bugs_numbers = sorted(bugs_new.keys(), reverse=latest_first)
            hierarchy_new.append((entry_new, ["{:>8}  {}".format(f"#{k}", bugs_new[k])
                                              for k in bugs_numbers]))
            bugs.extend(bugs_numbers)

        # replace old hierarchy with hierarchy_new
        hierarchy = hierarchy_new

        if not count:
            if hierarchy is None:
                raise NoPackage
            else:
                raise NoBugs
        elif count == 1:
            ewrite('%d bug report found:\n\n', count)
        else:
            ewrite('%d bug reports found:\n\n', count)

        # only pass a single package name to browse_bugs
        if isinstance(package, list):
            package = [p[4:] for p in package if p.startswith("src:")][0]
            source = True

        return browse_bugs(hierarchy, count, bugs, bts, queryonly,
                           mirrors, http_proxy, timeout, screen, title,
                           package, source, mbox_reader_cmd)

    except NoPackage:
        long_message('No record of this package found.')
        raise NoPackage


def browse_bugs(hierarchy, count, bugs, bts, queryonly, mirrors,
                http_proxy, timeout, screen, title, package,
                source, mbox_reader_cmd):
    try:
        _ = locale.getpreferredencoding()
    except locale.Error as msg:
        print(msg)
        sys.exit(1)
    endcount = catcount = 0
    scount = startcount = 1
    category = hierarchy[0]
    lastpage = []
    digits = len(str(len(bugs)))
    linefmt = '  %' + str(digits) + 'd) %s\n'
    while category:
        scount += 1
        catname, reports = category[0:2]
        while catname.endswith(':'):
            catname = catname[:-1]

        while len(reports):
            these = reports[:rows - 2]
            reports = reports[rows - 2:]
            remain = len(reports)

            rplural = 's'
            if remain != 1:
                rplural = ''

            if remain:
                lastpage.append(' %s: %d remain%s\n' %
                                (catname, remain, rplural))
            else:
                lastpage.append(catname + '\n')

            for report in these:
                scount = scount + 1
                endcount = endcount + 1
                lastpage.append(linefmt % (endcount, report[:columns - digits - 5]))

            if category == hierarchy[-1] or \
               (scount >= (rows - len(hierarchy[catcount + 1][1]) - 1)):
                skipmsg = ' (s to skip rest)'
                if endcount == count:
                    skipmsg = ''

                options = 'yNbmrqsfe'
                if queryonly:
                    options = 'Nbmrqfe'

                rstr = "(%d-%d/%d) " % (startcount, endcount, count)
                pstr = rstr + "Is the bug you found listed above"
                if queryonly:
                    pstr = rstr + "What would you like to do next"
                allowed = bugs + list(range(1, count + 1))
                helptext = {
                    'y': 'Problem already reported; optionally add extra information.',
                    'n': 'Problem not listed above; possibly check more.',
                    'b': 'Open the complete bugs list in a web browser.',
                    'm': 'Get more information about a bug (you can also enter a number\n'
                         '     without selecting "m" first).',
                    'r': 'Redisplay the last bugs shown.',
                    'q': "I'm bored; quit please.",
                    's': 'Skip remaining problems; file a new report immediately.',
                    'e': 'Open the report using an e-mail client.',
                    'f': 'Filter bug list using a pattern.'}
                if skipmsg:
                    helptext['n'] = helptext['n'][:-1] + ' (skip to Next page).'

                while 1:
                    for line in lastpage:
                        sys.stderr.write(line)
                    x = select_options(pstr, options, helptext,
                                       allow_numbers=allowed)
                    if x == 'n':
                        lastpage = []
                        break
                    elif x == 'b':
                        if source:
                            launch_browser('https://bugs.debian.org/src:%s' % package)
                        else:
                            launch_browser('https://bugs.debian.org/%s' % package)
                        continue
                    elif x == 'r':
                        continue
                    elif x == 'q':
                        raise NoReport
                    elif x == 's':
                        return
                    elif x == 'y':
                        if queryonly:
                            return

                        if len(bugs) == 1:
                            number = '1'
                        else:
                            number = our_raw_input(
                                'Enter the number of the bug report '
                                'you want to give more info on,\n'
                                'or press ENTER to exit: #', allowed)
                        while number and number[0] == '#':
                            number = number[1:]
                        if number:
                            try:
                                number = int(number)
                                if number not in bugs and 1 <= number <= len(bugs):
                                    number = bugs[number - 1]
                                return debbugs.get_report(number, timeout)[0]
                            except ValueError:
                                ewrite('Invalid report number: %s\n',
                                       number)
                        else:
                            raise NoReport
                    elif x == 'f':
                        # Do filter. Recursive done.
                        retval = search_bugs(hierarchy, bts, queryonly, mirrors, http_proxy, timeout, screen, title,
                                             package, mbox_reader_cmd)
                        if isinstance(retval, str) and retval in ["FilterEnd", "Top"]:
                            continue
                        else:
                            return retval
                    elif x == 'e':
                        number = our_raw_input('Please enter the number of the bug you would like to view: #', allowed)
                        _launch_mbox_reader(mbox_reader_cmd, bts, bugs, number,
                                            mirrors, 'no', True, http_proxy,
                                            timeout)
                    else:
                        if x == 'm' or x == 'i':
                            if len(bugs) == 1:
                                number = '1'
                            else:
                                number = our_raw_input('Please enter the number of the bug '
                                                       'you would like more info on: #', allowed)
                        else:
                            number = x

                        while number and number[0] == '#':
                            number = number[1:]

                        if number:
                            try:
                                number = int(number)
                                if number not in bugs and 1 <= number <= len(bugs):
                                    number = bugs[number - 1]
                                res = show_report(number, bts, mirrors,
                                                  http_proxy, timeout,
                                                  queryonly=queryonly,
                                                  screen=screen,
                                                  title=title,
                                                  mbox_reader_cmd=mbox_reader_cmd)
                                if res:
                                    return res
                            except ValueError:
                                ewrite('Invalid report number: %s\n',
                                       number)

                startcount = endcount + 1
                scount = 0

            # these now empty

        if category == hierarchy[-1]:
            break

        catcount = catcount + 1
        category = hierarchy[catcount]
        if scount:
            lastpage.append('\n')
            scount = scount + 1


def proc_hierarchy(hierarchy):
    """Find out bug count and bug # in the hierarchy."""

    lenlist = [len(i[1]) for i in hierarchy]
    if lenlist:
        count = sum(lenlist)
    else:
        return 0, 0

    # Copy & paste from handle_bts_query()
    bugs = []
    exp = re.compile(r'\ *\#(\d+)[ :]')
    for entry in hierarchy or []:
        for bug in entry[1]:
            match = exp.match(bug)
            if match:
                bugs.append(int(match.group(1)))
    return count, bugs


def search_bugs(hierarchyfull, bts, queryonly, mirrors,
                http_proxy, timeout, screen, title, package, mbox_reader_cmd):
    """Search for the bug list using a pattern."""
    """Return string "FilterEnd" when we are done with search."""

    try:
        _ = locale.getpreferredencoding()
    except locale.Error as msg:
        print(msg)
        sys.exit(1)

    pattern = our_raw_input('Enter the search pattern (a Perl-compatible regular expression)\n'
                            'or press ENTER to exit: ')
    if not pattern:
        return "FilterEnd"

    # Create new hierarchy match the pattern.
    try:
        hierarchy = hiermatch.matched_hierarchy(hierarchyfull, pattern)
    except InvalidRegex:
        our_raw_input('Invalid regular expression, press ENTER to continue.')
        return "FilterEnd"

    count, bugs = proc_hierarchy(hierarchy)

    if not count:
        our_raw_input('No match found, press ENTER to continue.')
        return "FilterEnd"

    endcount = catcount = 0
    scount = startcount = 1
    category = hierarchy[0]
    lastpage = []
    digits = len(str(len(bugs)))
    linefmt = '  %' + str(digits) + 'd) %s\n'
    # XXX: it's kinda non-sense to replicate all this code here!!! it's the same
    # as of browse_report!
    while category:
        scount = scount + 1
        catname, reports = category[0:2]
        while catname.endswith(':'):
            catname = catname[:-1]
        total = len(reports)

        while len(reports):
            these = reports[:rows - 2]
            reports = reports[rows - 2:]
            remain = len(reports)

            tplural = rplural = 's'
            if total == 1:
                tplural = ''
            if remain != 1:
                rplural = ''

            if remain:
                lastpage.append(' %s: %d report%s (%d remain%s)\n' %
                                (catname, total, tplural, remain, rplural))
            else:
                lastpage.append(' %s: %d report%s\n' %
                                (catname, total, tplural))

            for report in these:
                scount = scount + 1
                endcount = endcount + 1
                lastpage.append(linefmt % (endcount, report[:columns - digits - 5]))

            if category == hierarchy[-1] or \
               (scount >= (rows - len(hierarchy[catcount + 1][1]) - 1)):
                skipmsg = ' (s to skip rest)'
                if endcount == count:
                    skipmsg = ''

                options = 'yNbmrqsfute'
                if queryonly:
                    options = 'Nmbrqfute'

                rstr = "(%d-%d/%d) " % (startcount, endcount, count)
                pstr = rstr + "Is the bug you found listed above"
                if queryonly:
                    pstr = rstr + "What would you like to do next"
                allowed = bugs + list(range(1, count + 1))
                helptext = {
                    'y': 'Problem already reported; optionally add extra information.',
                    'n': 'Problem not listed above; possibly check more.',
                    'b': 'Open the complete bugs list in a web browser.',
                    'm': 'Get more information about a bug (you can also enter a number\n'
                         '     without selecting "m" first).',
                    'r': 'Redisplay the last bugs shown.',
                    'q': "I'm bored; quit please.",
                    's': 'Skip remaining problems; file a new report immediately.',
                    'f': 'Filter (search) bug list using a pattern.',
                    'u': 'Up one level of filter.',
                    'e': 'Open the report using an e-mail client.',
                    't': 'Top of the bug list (remove all filters).'}
                if skipmsg:
                    helptext['n'] = helptext['n'][:-1] + ' (skip to Next page).'

                while 1:
                    for line in lastpage:
                        sys.stderr.write(line)
                    x = select_options(pstr, options, helptext,
                                       allow_numbers=allowed)
                    if x == 'n':
                        lastpage = []
                        break
                    elif x == 'b':
                        launch_browser('https://bugs.debian.org/%s' % package)
                    elif x == 'r':
                        continue
                    elif x == 'q':
                        raise NoReport
                    elif x == 's':
                        return
                    elif x == 'y':
                        if queryonly:
                            return

                        if len(bugs) == 1:
                            number = '1'
                        else:
                            number = our_raw_input(
                                'Enter the number of the bug report '
                                'you want to give more info on,\n'
                                'or press ENTER to exit: #', allowed)
                        while number and number[0] == '#':
                            number = number[1:]
                        if number:
                            try:
                                number = int(number)
                                if number not in bugs and 1 <= number <= len(bugs):
                                    number = bugs[number - 1]
                                return debbugs.get_report(number, timeout)[0]
                            except ValueError:
                                ewrite('Invalid report number: %s\n',
                                       number)
                        else:
                            raise NoReport
                    elif x == 'f':
                        # Do filter. Recursive done.
                        retval = search_bugs(hierarchy, bts, queryonly, mirrors, http_proxy, timeout, screen,
                                             title, package, mbox_reader_cmd)
                        if isinstance(retval, str) and retval in ["FilterEnd", "Top"]:
                            continue
                        else:
                            return retval
                    elif x == 'u':
                        # Up a level
                        return "FilterEnd"
                    elif x == 't':
                        # go back to the Top level.
                        return "Top"
                    elif x == 'e':
                        number = our_raw_input('Please enter the number of the '
                                               'bug you would like to view: #', allowed)
                        _launch_mbox_reader(mbox_reader_cmd, bts, bugs, number,
                                            mirrors, 'no', True, http_proxy, timeout)
                    else:
                        if x == 'm' or x == 'i':
                            number = our_raw_input(
                                'Please enter the number of the bug '
                                'you would like more info on: #',
                                allowed)
                        else:
                            number = x

                        while number and number[0] == '#':
                            number = number[1:]

                        if number:
                            try:
                                number = int(number)
                                if number not in bugs and 1 <= number <= len(bugs):
                                    number = bugs[number - 1]
                                res = show_report(number, bts, mirrors,
                                                  http_proxy, timeout,
                                                  queryonly=queryonly,
                                                  screen=screen,
                                                  title=title)
                                if res:
                                    return res
                            except ValueError:
                                ewrite('Invalid report number: %s\n', number)

                startcount = endcount + 1
                scount = 0

            # these now empty

        if category == hierarchy[-1]:
            break

        catcount += 1
        category = hierarchy[catcount]
        if scount:
            lastpage.append('\n')
            scount += 1
    return "FilterEnd"


def display_report(text, use_pager=True, presubj=False):
    if not use_pager:
        ewrite(text)
        return
    elif presubj:
        text += "\n(You may need to press 'q' to exit your pager and continue using\nreportbug at this point.)"

    pager = os.environ.get('PAGER', 'sensible-pager')
    with suppress(OSError):
        with subprocess.Popen(pager, stdin=subprocess.PIPE, errors='ignore') as p:
            p.communicate(text)


def spawn_editor(message, filename, editor, charset='utf-8'):
    if not editor:
        ewrite('No editor found!\n')
        return (message, 0)

    edname = os.path.basename(editor.split()[0])

    # Move the cursor for lazy buggers like me; add your editor here...
    ourline = 0
    with open(filename, errors='backslashreplace') as f:
        for (lineno, line) in enumerate(f):
            if line == '\n' and not ourline:
                ourline = lineno + 2

    opts = ''
    if 'vim' in edname:
        # Force *vim to edit the file in the foreground, instead of forking
        opts = '-f '

    if ourline:
        if edname in ('vi', 'nvi', 'vim', 'elvis', 'gvim', 'kvim'):
            opts = '-c :%d' % ourline
        elif (edname in ('elvis-tiny', 'gnuclient', 'ee', 'pico', 'nano', 'zile')
              or 'emacs' in edname):
            opts = '+%d' % ourline
        elif edname in ('jed', 'xjed'):
            opts = '-g %d' % ourline
        elif edname == 'kate':
            opts = '--line %d' % ourline

    if '&' in editor or edname == 'kate':
        ewrite("Spawning %s in background; please press Enter when done "
               "editing.\n", edname)
    else:
        ewrite("Spawning %s...\n", edname)

    result = os.system("%s %s '%s'" % (editor, opts, filename))

    if result:
        ewrite('Warning: possible error exit from %s: %d\n', edname, result)

    if not os.path.exists(filename):
        ewrite('Bug report file %s removed!', filename)
        sys.exit(1)

    if '&' in editor:
        return (None, 1)

    with open(filename, errors='backslashreplace') as f:
        newmessage = f.read()

    if newmessage == message:
        ewrite('No changes were made in the editor.\n')

    return (newmessage, newmessage != message)


def initialize():
    return True


def can_input():
    return sys.stdin.isatty()