CD001UTF8 9CD 00&&"k 9VOLUMESET 9PUBLISHER DUMP9660 DUMP9660 2007091800262100200709180026210000000000000000000000000000000000CD001rio with coloured window background, border colours, font colours for regular and highlighted text; window -bg 0xff3333ff -text 0xffffffff -bord 0xffff88ff -htext 0xffff00ff -high 0xff00ffff # this crashes sometimes :(bind -bc /usr/matt/nutz /sys/src/cmd/rio compressed r8g8b8 0 0 48 48 48 4487  #)/5;AGMSY_ek$zMbt #)/5;AGMSY_ekq8Mbt{wusrq #)/5;AGMSY_ekqw, I ^Mytojgdba #)/5;AGMSY_ekqw, O  phq_ZWTRP #)/5;AGMSY_ekq84 뀡΀ i hqOJFCA@? #)/5;AGMSY_ekq K: k YKD}:620/. #)/5;AGMSY_ekqw} qZ@ [}  ]Y<5z*%"  #)/5;AGMSY_ qw}q`F | e=4,n  #)/5;AGMSY_ qw}fL2YM8& w #)/5;AGMSY_ekqw} H lR +!e )/5;AGM _ekqw}H rX )t #)5;AGM _ekqw} 7x^D  #)/;AGMSY_ekqw}" =q  ~dJ #)/5AGMSY_ekqw}k؁wi Ijz #)/5;GMSY_ekqw}" aR O3V  #)/5;AMSY_ekqw}Ewǀj<, [v\  #)/5;AGSY_e "w}td ӀD %# |b zvu #)/5;AGSY_ekqw}Qo_O? ߀)hxpjed #)/5;AGMY_ekqw} {J:*9ƀ /n kaZUS #)/5;AGMY_ekq egW 6 L 5t^JEC #)/5;AGMY_ekqw}Bu<2" ;!zG<52 #)/5;AGMY_ekqB sb }A0 < p A' /%! #)/5;AGMS_ekqw}ҁraP@ f  - #)/5;AGMS_ekqw}bq. G ) M3 #)/5;AGMSkq }}}}~?~?~?~??_____gWF6% xk^z 3 #)/5;AGMY_ekqw}{jZJ:* p t- #)/5;AGMY_ekqw}o_O fB )n'M #)/5;AGMY_ekqw}HtBD5 L  h!  #)/5;AGSY_ek<}\yK<, " bA #)/5;AGSY_ek } <(pa }4&E\; #)/5;AMSY_ekqw} e i"= ߆!؀V|5ȃ  #)/5;GMSY_ qw} Qqb 8+ [v /z #)/5AGMSY_ekqw}Ydžk]P O6)J%)  #)/;AGMSY_ekqw ug IMA DjKq  #)5;AGMSY_ekqw} =reYdt )/5;AG Y_ekqw}9 7w} d^e #)/5;AG Y_ekqw} Hk Ѐ|2X~,% w  #)/5;AGMSY_ekqw} ဠRx < *n"   #)/5;AGMSY kqw} LrڀRYD?: 20z. #)/5;AGMSY kqw}  FlqiaYOJ @} #)/5;AGMSY_ekqw}@f[Ԁw j _hWq  #)/5;AGMSY_ek } (:`Άtoh q  #)/5;AGMSY_ekD4ZM {s  #)/5;AGMSY_ekq8Tz O M݀ b t #)/5;AGMSY_ek3 GIF89a00{wusrqytojgdbap_ZWTRPiOJFCA@?kKD:620/.}]<5*%" |e=4, YM8& +!)zvuxpjedkaZUS^JECG<52/%!!Created with The GIMP,00@ @@ <A8h`(@`S@ 2$а<|B!@|!4$Ȁႅ &H(RX-Bx#4fȈㅋ-:@`a(N(Ab*l@#G:v B%K4q򤷓&L($ a"Q(A(@" *D4b ,Zhɂ劕*MB)hz `QpE-C,`lcQe1alN,11B]p6@J'`~p AXFkцoFlQi`Eh17PpRDAIȱWAGlqyǜzvQs\r$GR(  G 80A g$!D~`G BH!bH! "H`GB$qF 0C  91 3Aib$"z0҈#@$H $8#z,H"bd309A"~dF%oX"H#@I&lI'lI&@rI#XF%jxD1D+d`'@A^bTI(tBJ))(r(y|RX<=| P :@M\G"o1#dI*J+J+J*d#F"\4  ;@_Q"H$2 -b-- -K$b"eT `It . / 0/ + * t`IA B@FoH(r0R1 2 s1B0ȒI(Fi|D- `PK`1 f$x4f$X1X1 N&!( [(Q/!iPָ5AiH#.hQ@"v(`zxA5AD'T ^xF6 ntF6! fPE'0Aa a8@0 6a)VQ^#Ȇ7 p71dX)2A b<`18 І<(+lc48Arc6ьc(& APjT*ML$ME1Ã4|$ɉ $ ft$>l$DL$(;L$$=sZCΈD$8D$8;D$4rM)U]D$  ,$L$+L$LL$@fD$\$($L$L$ l$D\$(놉,$\$D$cft$>l$DÃt E D$$H@ø@ËE(D$LT$BB@D$B($L$L$L$L$ JÃT$BB@fD$$L$L$L$Ã,t$0\$4u'4$lL$L$L$ ,ËF,t_11ҋF(9‰T$$}<u4$\$T$l$ ,ÍL$" $ډl$( L$ t$0T$$\$4l$(B륉$, D$L$4 $ D$L$0 $L$4L$L$L$L$L$ 2,Ã\$CC@$D$Ã$|$(l$,u'<$rL$L$L$ $ËG,t+w(19} Zu<$l$\$$Cډft fu)ȃ\$CC@$D$UÃT$B fD$B($L$L$L$ ÁD$D$@,D$41D$LD$PD$H$@ p}$AA@D$H$@ xL$4xD$T$@ d Xbt Ĥÿ|$@D$4%uNT$,t5L$L $L$PL$D$D$ aT$,|$@1u |$H $ʼnD$X1t$8L$L $L$PL$|$@t$8l$XL$ $|$L$ $L$LL$L$PL$L$L$ L$L$L$ $L$$L$MD$T$8 $蒑\$@T$8YL$_ $:T$@BL$LJL$HJ L$DJ$|J,L$XJ4$t $PT$@B0$ܗL$$ $D$D$L$ L$@L$@ $|$t$$1L$L$L$GL$@A|L$$ $D$D$L$ L$@L$@ $|$t$$1L$L$L$=GT$@B|J|L$H $\$@ƒtit,tBt$אD$@pǃC(։T$<<$/\$@T$t0D$ 1  ЃÉЃ ø$D$T$T$랃 d-0`ÉD$;`tC19}4 9u*D$ $D$D$D$1 B뿋$й$踹1;0`} 9D$uB;0`|1 ø$щT$ D$D$T$ $D$ Ã4=t`}`$D$!t`  $D$ $#D$(L$  $D$,L$ ! $D$$L$ - $ `D$0t) $=D$D$$D$D$(D$ D$,D$|D$ $z|$8t$$4Ã=<`}`$KD$A<`  $@ JȉD$$lzD$Ã8L$ $kL$L$L$XL$P)șD$$|$$D$, X`IL$$D$dl$$L$TL$0`$|$t$4|$h`D$D$D$,D$D$0D$ <$`D$D$,D$D$0D$ |$ht5`D$D$,D$D$0D$X`D$D$DD$ >HË`ɋ`mL$dL$` $C4D$8$|$t$DD$TD$D$XD$D$D$\D$ D$$`$D$1D$@$L$iD$?tD$@iD$AoD$BnD$C D$D%D$EdD$F)D$GD$lD$X0 $vXu1dÉD$`$D$0D$UkD$`L$hHL$p $lTT$`BEBJ $XT$`B Jk $XT$`B$tB uI$XD$`@ $XD$`@$$XD$`@($XD$`$X1dËB$tL$X $L$XL$D$QL$`fAL$X $D$XL$X $L$XL$D$PL$`fA L$X $L$`D$XAAA 4fA fAA(A$$D$Ak D$iL$`A $D$AD$iD$`@D$X0D$X9L$X $L$XL$D$PD$PL$X $D$XD$X06D$X9&L$X $L$XL$D$OD$LL$X $gT$PL$LD$XD$Xtr"$[\$`$1dÁs9wՋD$XD$TL$X $L$TL$D$NOD$HD$T;D$XlD$T tD$T @L$T $D$XL$`I( $L$`IL$~V\$`C(C(u fCI $&U\$`ƒ-CK(ȉL$Pf L$LfJL$HJD$XD$TD$Xt D$X upD$XD$XT$\B L$T $PT$\BBu$U\$`L$X $L$`D$XfAD$XȃdËD$X tD$X tD$XSD$T D$HD$$D$X+D$lD$Y\$`D$$D$X+D$lD$zY\$`$dYT$`T$u1B9}KȉL$J(ȋL$A $TD$@$TD$$TL$T$AB9|1ۋB9}.؉\$k J$ȋHt`9u^CB9|ҋB,$)D$$TD$@ $TD$@$$TD$@($SD$$SÉ $.\$T$됃@T$X}jD$H$|$t$HT$WL$XD$8$L$L$hD$`$D$`D$D$dD$D$8D$ D$t\ʋL$H L$ JL$J(ЋL$\J,׃t$L׃t$$L$`J0B4B8Ѓ@ù< $2EƒD$4uL$H $L$v\$ ƒfXBBBD$H$D$褸g|$$t$L+DŽ$D$($ $ =D$ |Y$$t4$$D$I|$(t D$($JE1ø$IՋ$$D$$ $L$ L$[l$ $ƒlA$Y$nXBB؉\$$BBЃ$$D$l$ $ $D$T$I $L$/L$L$D$ D$ҁ=Ƅ$< $"CD$(ud$ $L$e\$$ƒ}fXBBB$$D$萶@$L$$HL$G $ƒu,$D$GD$H_D$(P,$5L$(A(L$S $-kL$(A0L$_ $kL$(AL$k $kL$(A L$w $jL$(A$ $jL$(A$ $jL$(A$ $jL$(A$ $jL$(A $ $jT$(B$B4B8ЁÃL$$ $L$ $D$L$˵l$\$ ƒu1NKHCBCBCBL$(JBЃ$D$$D$l$葝L$ $D$ƴ}1øÃL$ u1Ë L$ $D$芴L$ $L$l$T$ Ãu fJHBCBCBCB4t9u:J8,$J4t"L$} 11߃tʋA89u ȋJ8H8빋I8L$ $D$ D$$@D$ Ã$D$(D$,9L$tX$@E1$ù $?ƒD$u1$1ɃL$L$ $L$ʳt$(l$,ƒuD$$U@1$ ` `AZBB؉\$BNJFBFBFBMJ EB EB EB L$0J L$ $D$臲L$T$tEL$ L$JL$(JBt B@,u$kBT$L$,J Ѓ$A $I>D$u1ËL$ $ L$色t$ l$$\$ƒu$?1SވXމBBBBBBBL$ $D$聱\$|L$ L$ KCC ؃ÃL$u1ËD$$L$輱\$ƒu FKHCBCBCBL$ $L$ް} ËD$$=Ã$$L$(L$|$t$,L$$ùȍD$6$$D$"v$럃|$,J $L$,L$~$Bt9uD$($$$$$)G$É$D$0$D$(L$ $L$L$D$L$ L$ ÃL$ $L$L$D$L$ SÃL$ $L$$L$D$L$ 'D$D$$D$D$D$ @D$ÃL$ $D$L$L$L$ L$ ÃL$ $D$L$L$L$ ÃL$ $D$L$$L$L$ D$D$$D$D$D$ @D$$ÃPt$XD$T$t$D$\D$D$`D$ D$dD$D$hD$D$lD$D$pD$D$tD$ D$$D$(|$,D$<D$@D$D D$H$PÃPt$XD$T$t$D$\D$D$`D$ D$dD$D$hD$D$lD$D$pD$D$tD$ D$$D$(|$,D$<D$@D$DD$xD$HPÃPt$XD$T$t$D$\D$D$`D$ D$dD$D$hD$D$lD$D$pD$D$tD$ D$$D$xD$(|$,D$<D$@D$D D$HPÃPt$XD$T$t$D$\D$D$`D$ D$dD$D$hD$D$lD$D$pD$D$tD$ D$$D$xD$(|$,D$<D$@D$DD$|D$H`PÃPt$XD$T$t$D$\D$D$`D$ D$dD$D$hD$D$lD$D$pD$D$ D$tD$$D$(|$,D$<D$@D$D D$HPÃPt$XD$T$t$D$\D$D$`D$ D$dD$D$hD$D$lD$D$pD$D$ D$tD$$D$(|$,D$<D$@D$DD$xD$H1PÃPt$XD$T$t$D$\D$D$`D$ D$dD$D$hD$D$lD$D$pD$D$ D$tD$$D$xD$(|$,D$<D$@D$D D$HPÃPt$XD$T$t$D$\D$D$`D$ D$dD$D$hD$D$lD$D$pD$D$ D$tD$$D$xD$(|$,D$<D$@D$DD$|D$HPÁ$8$<*$$<D$,$@&$@D$($<u-$@fu$$$Ã$Dtٺd$D9}$D$8$L$,L$L$(L$L$@L$ T$$ L$L$0L$0 $8$$ $$dD$ $/ƒ$Xt $  $T$薢$ $,$X$8ƒu$(D$5xOJGBGBGBNJFBFBFBM,IJ E,@B E,@B E,@B $$J $$B$$B$$BM $(JE $(BE $(BE $(B$HJ$HB$HB$HB$LJ$LB$LB$LB$PJ$PB$PB$PB $TJ!$TB"$TB#$TB$$0J%$0B&$0B'$0B($4J)$4B*$4B+$4B,$J-$B./K CBCBCB$\J$\B$\B$\B$`J$`B $`B $`B $DL@ƍD$@9s B9r苄$ $$$ $\,$$8$)$D|$0lK $L$0L$$8ICT9J$sd$@D$( $<D$,L$ $L$v\$cKHCBCBCB|$ňBL$ JD$ BD$ BD$ B L$$J D$$B D$$B D$$B L$(JD$(BD$(BD$(BL$,JD$,BD$,BD$,B؉k0߃t$ 1:E26E$D$LMk 0 $l$L\$xT$PC$C$Mk$B$3T$Pt$Tl$xBBT$PB4$l$t$Tl$x\$PCCu!F l$N tÉsC@M f9vC@M )ȉC@ D$DD$($D$L$@L$D$0$t$D|$D$(D$ D$,D$yD$DƉ$ƒ|$T$D$D$0D$D$4D$ 载L$DD$($AD$A+D$@D$AD$ AD$T$DЉ$|$t$(D$x@D$D$փ|$Et$Tl$x\$P1ҋC@9}tKI@L$@)ȉǃ}1KIȇ߈X߉KI@L$@)ȉǃ}1KIȇ߈XBC@9|CM HC$$l$Ck 0D$vT$xBk J$ȉ$D$0D$q,T$xBk J$ȉD$PBD$L$QT$Pt$Tl$xiT$B(@u1ËZ$Jk  D$D$ ;\$s;t+\$C$.\$CCD$ ;\$rŋD$ ÃL\$PD$DCD$8|$\D$\L$$ $D$D$D$ D$Xl$TD$ L$ KL$jL$8 $|$t$$L$\0L$D$D$$D$@uD$D$VT$PB $D$BD$*D$DLËD$8$D$藋L$8 $ L$l$X\$@ƒu$T$PiKHCBCBCBBBBBL$PI J L$8 $D$}+$1D$D$@$ T$PD$P@,$\$XT$PL$@J,L$TfJ L$\JD$DB9 $T$PD$HD$DB $T$PL$XJL$HJ w l$ \$$~Y T|$|#D$HD$)ȃ 1+D$HƋD$HTl$ \$$띃<$t$ L$0L$ L$`Ã<$t$ L$0L$L$4Á$ $L$$$u$ĨÉ$$/D$ $@D$D$$D$ L$ $/L$'D$`$@D$ÒD$D$ D$ $D$q$D$`$~ |hD$ $@D$ɒD$$D$ L$L$L$ $L$(L$uL$ $~ĨI}$ $L$'u$ $EĨ1ĨÍD$ $D$& L$ $ L$Bp`uD$ ÃtL$ $T$ $`t1 ËA 9D$uAȃ Ãp`$tL$ $p`D$$`ËD$;$`up`$`ÃhD$xD$(u+L$l $L$tL$L$|L$D$(u1hËL$t $L$?L$$L$$t&|$xu D$($$>1hÍL$? $q;D$8k $u1hËL$t $D$4D$L$8kL$ L$8k9t$D$4$:1hÉ $tىD$0$D$4D$D$8D$|$|t D$l$L$K $:D$ L$W $:D$L$p $L$8L$L$ L$L$L$ L$0L$L$(L$D$,|$|t D$l$|$,uD$0$r'D$4$aD$,hÃL$ $L$ L$L$$L$D$ L$(L$Á$ $L${L$ L$ t 1ĸÍL${ $pL$ L$m$u%$ $T$$L$3ĸÉ$$L$1L$- 1t 1ĸË$D$\11҃ }D{ $ t|$1ĸÃ\$p[Ƅ$L${ $D$<u$D${D$o1ĸÍ$ $8D$D$ $8D$H$ $v8D$L$ $c8D$PD$D;D$LD$H;D$PD$HD$XD$PD$TL$< $D$,<$t$DL$,L$cD$l$t$$G$ $|$t$DL$D$@$pD$D$>L$ $'>(øJ$T$ l` $L$L$0uȃÃD$L$ $D$ D$$D$ D$D$$D$L$뵃 l` $L$L$S0u4|$ tuȃÉL$ $D$D$D$HL$؃D$L$ $WD$ D$$D$ D$yD$$D$L$뇃 L$tl`$ȃD$2 ÃT$$uD$ $1Ã|$ t D$ D$  l` $L$ L$T$11t`ȃD$L$ $D$D$$D$D$L$|$ u(L$ $fD$D$$D$D$L$ȃà l` $L$L$2 ÃD$ D$d$ D$ $L$:D$t$L$ $D$ D$$D$ D$D$ÃT$L$ tQÃÃT$L$ tQÃËL$AøËL$u1ÉȃÃ1҃!}5<t%L$ $L$L$T$ЋT$uB!|˸$o$_Ã=`u$:`D$ `$\#t$ l$|$ t81҃!}<u$,D$ `$$D$ B11҃!}!<tu 9t$CB!|߃u`$t$ ǃ P`t D$$Ѹ$D$D$D$t:Ã@T$HL$D9w1@D$L$L$HD$D$ D$$D$(D$PD$,D$$D$LD$%D$D$@à D$0L$$ $L$(L$L$,L$D$D$ X ÃD$ L$ $D$D$4ÃT$B,B$B(BD$L$$J$L$ L$$L$A,A$A(ȋL$H|1ÉЃø@ø@ø@ø@ø@ø@Á$D$$$D$$D$$L$ kD$$D$VĜÃD$D$ $@4} ËD$ (`1Ã(`D$ \$  $4\$ } ËT$(`؃ËD$h`ÃbD$ T$ ubT$;T$ u 88T$$8T$ubBЃà 뷃D$$L$AuA $ p\$‹C uASS T$B$l L$ $L$h`ЃtD$ÉP뽃D$$MT$ZtB\$KJBuB $ L$ $E#L$h`ЃtB$ÃL$ $L$ u1ËAuA $É $1ÃD$$L$AuA t`8\$‹CuHS SBT$B$-L$ $L$h`ЃtD$ÉPA $ÃD$ $L$ AuA t $1A $ÃD$$T$B 5T$Z JBct^\$Ct 5\$T$KJ B uBB$4L$ $D$h`ЃtÉ$ ÃD$$L$AuAthAD$L$\$ƒuHS SBT$B$L$ $L$h`ЃtD$ÉAA $nÃD$ ${L$ AuAt $?1A $*ÃD$$2T$Bu I4T$Z uB$CuA\$KJ B uB$L$ $D$h`ЃtCt 3T$B t B @t"B uBB$TËZ KJ B\$$D$h`Ћ\$T$t똃D$uS3D$$D$@u23T$@BZC\$ZXtq\$KH@u @ $L$ $E#L$h`ЃtL$ $L$h`ЃtD$Ë@$0BXWD$uX2D$$"T$@u 72T$Zu$1ËKJBuB IKX@ uX C$ÃD$ L$ $QtD$ D$ Á$$t ĜÍD$$D$L$NL$ $L$ 1D$} ĜË$ $1L$ $$L$D$D$0L$;D$t $0ĜÉ $01ĜÍD$$D$L$M[D$@$D$`D$$D$ XL$@ $D$70} 1É$$L$@L$L$#$$$/$ 1D @L$@ $L$L$ L$ t 1ËL$4 $Ã,D$q1ɃL$L$> $u1,ÉD$(L$u$L$ $T$ L$&L$T$u'tBu‰ЃÃuBB'tB1뙃딃\$D$D$;D$ }0t L$$ $\$ L$%\$u3uD$ËL$T$$L$$L$D$뛉C띃\$D$D$;D$ }1t! $\$ L$%\$u4uD$ËL$T$$L$D$C렃D$P;T$tjT$ BL$I9uoBtB@9tׯ$kT$ BtB@9t$HT$ =^u R;T$uø$!T$ į$T$ {L$=^A9D$s A;D$rC$L$AtA@9t^$L$AtA@9t$L$ $L$AtA$D$D$AD$UL$A tA $AD$D$D$/ø.$#L$)T$L$ tYuȃÉL$ =^u&@9uȃË@9s ŋ 뾸$T$L$ ø$T$L$ 됃 L$ $L$L$i Ã|$ tLL$ $L$ IL$B\$ ‹uCC D$Ë*MKM K Ű$륃|$$L$ $L$$IL$\$$ʼnD$9umCuK MC ʹʉKD$ ËC uKMىك @t @uȋJ KJK J U롸$il$\$$yӰ$O@T$L$ 1ۃu؃ËA9uȃËA9sˋIڋI Ջ\$T$u RRËKJZBPBPËl$\$C9uCʹʉK1ËCKHCKH9ukCʹʉKÃ$L$,D$(@4ut^L$( $T$JL$FD$D$$L$L$4‹D$9tL$ $T$L$D$(L$IH D$$Éȃ$D$AD$dL$,c L$$ $L$(IL$D$T$$L$(L$t$l$\$(ur $l$n\$(L$C ʹʉKD$$K)H D$$@4u  ʉ؃ É؃$D$CD$\$(9t $D$\$(L$눸$T$9L$T$B9sJBHJI!Ë\$T$ B9sZBȋJI!Ãl$ \$K 9=^uD$$\$l$ \$E=^uD$$l$hl$ \$K @@@@MKK KH@@EE ʉ؃ø$l$ \$M\$=^t#L$KK KH@؃ø $\$ʃ\$ K @J ȋK)ȃÃL$ $L$(L$`\$(t$$N9r~F)=s[NŋF)%EN)فMÃ9v݃9sڃ ` C9røC$\$(t$$뎸"$\$(t$$h L$$ $L$,L$\$(S)‰T$C+D$,=s?9r s&  D$$$\$D$,D$D$( ËD$$@9rω$L$S\$(K D$$@4u7 ʉL$ $D$D$D$$$D$D$G\$(qL$ $D$D$D$/L$릃\$ L$KK ^@Ã,\$4T$0B4B؋J9vV$ L$0uA4u$,É $lD$AD$D$4D$ AD$AHƉ$B(Љƒu,ËD$0L$4HT$($D$D$($D$4D$D$0$D$(D$\$(K  ʉ$C؉T$)ЃD$D$0$D$D$KD$0$D$D$D$0$D$D$#|$0l$(1_$9v ދ[ 9w] tE hutHEh Et<$l$ED$I|$0l$(E t<$E D$l$&,Éo$빉$]D$\$BHЋ\$4T$06T$$=^L$ $T$D$ $L$L$JD$$D$(D$D$ $D$D$"\$K ‹=^uL$ $\$T${ËD$ $\$\$D$ $D$D$ZÉT$$SD$D$$D$(D$D$ $D$D$D$D$뺃 D$$$D$(D$rD$$$D$,D$^\$(T$,C9u9w$a\$(T$,D$$@,$T$D$$@,Ћt$$l$,\$(ECCX K Ë@)ÃM D$4$\$\$D$$$D$D$\$(D$,$H)؉D$L$D$$$L$D$)ȉD$D$$$D$D$\D$( É^$`1 Ã@\$HT$D$D$D$\$ D$CD$CD$C D$CD$ CD$$CD$(BHЋl$D\$HC=@r@ËK ,$D$AD$AD$ AD$AD$AD$AD$D$ AD$$EHЋT$H=  $l$H\$D‹E9r@Éщ D$4$?D$CHЋT$DL$4$JD$AD$AD$ AD$AD$AD$AD$AD$ AD$$BHЋT$DL$4${D$D$AD$ AD$AD$AD$AD$AD$ AD$$BHЃ@ÃL$ $D$D$D$AHЋD$$D$D$ ÃL$ $D$D$D$AHЋD$$D$D$L$ $D$ALЃÃ4l$8\$<=ޭ=^t/== t,$\$òD$gl$8\$ $u1,ÉD$()F$_FnFF"F|FMraB3G yhGiWCG*GJH\)c=H7]rHaxHyֲHL}YI\CkFI3T|I\'Isȡ1I:~^Jd~QJvaJ0}GJ>nllJ$KAjZK=P1PKMZ>dKW`M}Kmn/LDcL^LpuLafirM?O8MrbnMG9Mz)M:Ft NdȋBN=ֺ.wN 9iNCNuOILLO֯OO[пOE!P/'%UP_QP6PbDP{U[*QmUx`Q*4VQz5߼QlX R.4R9mr"iRY) kRعeR$N(Sa򮌮>S }W-sSO\]ScbuSp] T%L9hGT.B}T}Ô%IT\nTsqUFQU`RUxӫU?+dpU5=%VN=@[Vҟ&VG0JV=:YVf$0W&sdWW)>W]3sMXk5!a9XBioX)8ӣX*4X5AHxY(-\CYr%4xYv/AYiY?ZOMZ20HwZ~$|7Z-[bZXC}"[;/V[ ;C-[SJ[= \[M"4+\0IΕ2a\|AH\[R\ysKp]WPM4]mH=j]Į]-f]u8W]am ^|M$D@^`-Ut^x^WUH^P.5_[ypH_r]~_':__ k_EW`RVR`'.N`(:W"`Yv5`unnamed fontstringwidth: bad character set for rune 0x%.4ux in %s 6Am0_?sbrkmem/env/MALLOCFDcanlock(&pv->lk)==0panic: panic: WRKiA 0 0 0 2304 15 0B@C``H А@`p8pc~>?  @ @HA`AA>@H$  @8 0<`T_o}o08` x0A00$v P@0(p$ pH$ p 8 cx>| ?o 0|x|B# <`>n>`ű8lc1G?|~?>?o C0'Ar" C , `0 `0c8l6 1f3|><7p1f3Ə0lH$ `8 @ A@ AѠ0 cb r1` 3c 9eca230x#1fw@lv'c1Tx~oϓ~?w{o a0x(O0J ل@, `0 `0c,l6 d1f31`0lb3``0;a61f3@@lp$ @Pp  `  0cc<103 3 yefa0H3`cٌc1L21p䪘}/?}o>ߟ0(Ygr? 2Lf3c1>`0,l6 81f1`0lc1``071f3g1f3@@l 8 Al00c90>0# 3c 5|a 0?3`cٌc1ٺuT}O|ov1([J t8 eRTF#b100 `0c&l6 1f0~??`0c1f3k1f1l0@$  @ p(00cg0g 003c 5`a& 0c3`cٌc1ٺ8}o9oy~ ``0f(M`8??0 `0c&l6 81f0c1f3``0 `0c1f3k1f1l 8 @$a$p0 1o0`c ```c`Mᙌ3c `aac `0c3`ncٌc1a& `T~o|_qy0a>c? g x``c 00;`cpٌ8| `c `T?w~?~? `0=al6< G P Y b k t }                   ( 1 : C L U ^ g p y               q->size == t->sizeq->next==nil || q->next->prev==qq->prev==nil || q->prev->next==qq->magic==FREE_MAGICt->magic==FREE_MAGICa < t->size && t->size < bt->next==nil || t->next->prev==tt->prev==nil || t->prev->next==tt != nil (*t)->magic == FREE_MAGICnode != nil node != nil *loc == nodeolst != nil B2NB(a) == bb->magic != FREE_MAGIC b->size >= dsize2bsize(p, dsize)b->size - dsize < 0x10000newarena %lud pool too big: %lud+%lud > %lud memory pool too largebot->aup == top && top > botpool %s block %p hdr %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux tail %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux | %.8lux %.8lux user data %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux | %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %s %s pool panicbad magiccorrupt tail magiccorrupt tail ptrcorrupt tail magiccorrupt tail ptrdangling pointer writebad arena sizebad arena tail sizeblock too bigmem user overflow (magic0)corrupt tail magic0corrupt tail magic1corrupt tail ptrtoo much block datamem user overflowmem user overflowdon't call me when pool->move is nil B2D called on unworthy blockD2B called on non-block %p (double-free?)invalid allocation sizea->size >= nbsizepoolalloc %p %lud = %p poolcompact %p poolrealloc %p %p %ld = %p poolfree %p %p dsize >= getdsize(b)poolmsize %p %p = %ld found wrong tailpool %p %s (%p %.8lux %lud) *default*getsubfont: can't open %s: %r getsubfont: can't read %s: %r GMTThu Jan 01 00:00:00 GMT 1970 SunMonTueWedThuFriSatJanFebMarAprMayJunJulAugSepOctNovDec/env/timezoneGMT events: eread stat errorevents: mouse not initializedevents: keyboard not initialzedevents: ecanread stat errorevents: bad file descriptorevents: timer started twiceevents: einit pipe%s/mouseeinit: can't open mouse %s/cursoreinit: can't open cursor %s/consevents: can't open console%s/consctl/dev/consctlevents: can't open consctlrawoneof on event pipeevents: protocol error: short readevents: protocol error: keyboardevents: protocol error: mouseevents: protocol error 4events: fork error forkevents: bad slave assignmentdieevents: mouse not initializedb: %d xy: %P events: keyboard not initialzedm%d %datomouse: bad count*default*R{PD?string: %r /dev/time@ͽ|`Aimage line: %r rgbkamx0123456789abcdef0123456789ABCDEF ((((( H ?-DT! @bad channel descriptorbad channel descriptorallocimage: %sallocimage: %rname too longnamedimage: %snamedimage: %rbad channel '%.12s' from devdrawNaN+Inf-Infe%de%de%d@?rdsubfonfile: header read error: %rrdsubfonfile: fontchar read error: %r$@assert failed: %s bad height or ascent in font fileillegal subfont rangemaincan't reattach to windowexitclockimageinit: can't open default subfont: %r /env/font%d %d 0 %d %s imageinit: can't open default font: %r imageinit: can't open font %s: %r %s/label%s/winname/dev/dev/draw/new#i/devimageinit: can't bind /dev/draw: %r%s/%sbuf is : %s gengetwindow: %r nobordernamedimage %s failed: %r image->chan != 0noborderbordersizebordersize was : %d for windir : %s (*winp)->chan != 0%s/winname/dev/devinitdisplay: directory name too long%s/draw/new#iinitdisplay: %s: %r%s/draw/%d/data%s/draw/%d/refreshiounit %d too small%s/labelproc %d waiting for display lock... draw: %s: %s flushimage fail: d=%p: n=%d nn=%d %r bad count in bufimage      *<P/proc/%d/note/proc/%d/notepgfont cache resize failed: %r resize: init failed: %r f@allocscreen: image and fill on different displayss->image && s->image->chan != 0top/bottom: ridiculous number of windows top/bottom: windows not on same screen image ellipse: %r 4 Fzcompressed readimage: bad formatreadimage: bad channel string %sreadimage: bad ldepth %dreadimage: bad rectanglereadimage: image too wide for bufferreadimage: read count %d not %d: %rAloadimage: bad rectangleloadimage: insufficient dataloadimage: image too wide for bufferbufimage failednaninfinityinf7635118191655355242878388607671088631342177271251562519531251220703125152587890625190734863281251192092895507812514901161193847656257450580596923828125/dev/bintime/dev/nsecq?$?b@ԣAB5F *default*APetextVchan.c`debuglockdisplay`.string`drawld2chanbind.s/`screenðZPøZRAstrtod.c`.stringpstrrchr.cdirfstat.ctreadn.costrtol.c`onexlockEfmtlock.cbrk_.sWbuildfont.c.rathole(atol.c[alloc.caknownfmtwatnotify.cntms$2a.string7utflen.ceslavec$2Tdraw.c draw.hr$2Bpow10.c` f$1͐endumalloc.cJmemccpy.s`f$2bxomode$1bqlfcall.h$6753134.c152db0fnoted.s.string$0.0argv0.sefont.c$4d0e21da.40c27b8c?utfecpy.c%strtoll.csbrkmempriv.string`displaykcreadimage.cðedata libc.a libc.h}rfork.sNinit.c`argv0Yarith.c386.string`logfidclock.c/pread.s` screenidqlock.cMline.c`$lastsubfont$65b3fc28.406094e9tab.stringgetcallerpc.cfmtalloc$$36d28a98.411adc9c1close.s>rerrstr.c,$6dc9c883.3fe45f30'lock.c`(bloc4.string u.hfmtt$1f521d57.4169e64b|defontdatasrcusr sys`,rad$2^stringwidth.c.string`0nslave getpid.c-sleep.sread.cZallocimagemix.c,dofmt.c`4ntm$2ireadsubfont.cUdefont.cRemenuhit.c`8doquote4deffontname@.stringnsec.c~pipe.s.string0open.s`atoi .frames ) AB   *Vu>&_fmtFdFlush.framenf>svfprintP.frame4nfmtargs0buffd0f + AB   *Vr?fmtfdflush .framef?,fmtfdinit.framefd sizefbuf , AB   *V[?adofmt8.framenstruner rsrt nfmtffmtA_fmtflush .framelenftB_fmtpad$.framen ifB_rfmtpad$.framen ifC_fmtcpyH.frame*_runest r$ncrsrtn fl(wfme szmvmF_fmtrcpyD.frame&_runerts men fl$wmvmfI_charfmt.framexfI_runefmt.framexfIfmtstrcpy0.frame.safei rjsfJ_strfmt.framefKfmtrunestrcpy(.framesfK_runesfmt.framefK_percentfmt.framexfK_ifmt.framehi.safelnLpCbufxisvdbasePconv\uXvupflf`negRN_countfmt.framefR_flagfmt.framefSg_badfmt .framexf -Ssleep.frame . Sseek.framea /Spread.frame 0Sopen.frame 1Sclose.frame 2 AB  Swrite.framenbuffd 3 AB  Tabort.frame 47T!_mulv.frame barTI_mul64by32.frame barTg_div64by32.frameba rTz_addv.frame barT_subv.frame bar 5T_tas.framel 6Tstrlen.framep 7 AB  Tutflen .frame runens 8 AB  U#chartorune.framerunestrUrunetochar .framerunestrVWrunelen .frame strrunecV{runenlen.framernruneVfullrune .framestrn 9 AB   *VW_fmtinstall.framecfWtfmtinstall.frameretfcWfmtfmt.framepcX4_fmtdispatch .frame.saferunefmtfisrunes : AB   ; *tZxadd.framevanZYxsub.framenvaZxdtoa|.frameDc2Lc4Hc3@c18is1,h4dh.safe$g0eXchrPucaseTsigns2f\precfmtb_floatfmtL.frame4.safef(sfmtcW_efgfmt .framedf < AB   *V_cerrfmt.framef.safebuf =cpwrite.frame > AB   crerrstr.framenbufbuftmp ? AB  dRutfecpy .framefromtoe @ ;1dtoupper.framecdtolower.framec A AB   ;$dstrtodH.frameLdallocimage4.framevalreplchanrd̑_allocimageD.frameai valcliprar(refresh id$screenidrepld$depth ierrchanЈnamedimage.framebufiddnnameierrӫnameimage .frameinniname`_freeimage1 .framediCfreeimage.framereti L\ AB     jallocscreen(.framepublic idtrysdfillimagepublicscreen.framechanidds׻freescreen.framedsWallocwindow(.framevalrefrsؒ_allocwindow<.framerefvalristopbottom$.frametop.safenw)bottomwindow.framew]topwindow.framewڒbottomnwindows.framenwڵtopnwindows.framenworiginwindow4.frame delta scrlogw L] AB     $ܚallocsubfont.framefiinfo ascentnnameheight L^ AB     h[_stringnwidth.framerunelsubfontnamewidcbuf nmaxtwidrptrrsptrsf lenߵstringnwidth.framelensfstringwidth.framesf stringsize.frame.ret.safesf\runestringnwidth.framelenrfrunestringwidth.framerfrunestringsize.frame.ret.saferf L_ AB     stringT.frame sfspsrcpt.retdststringopT.frame$op sfspsrcpt.retdst2stringnT.frame$len sfspsrcpt.retdststringnopT.frame(op$len sfspsrcpt.retdst_runestringT.frame rfspsrcpt.retdstrunestringopT.frame$op rfspsrcpt.retdstrunestringnT.frame$len rfspsrcpt.retdst%runestringnopT.frame(op$len rfspsrcpt.retdst_string.frame.ret@bgpsp,cliprptsrc-loadimage@.framedatan bpldyndata.saferchunki Lb AB     .openfont0.framefntd ibufndirfdname Lc AB     ,_getsubfont$.frameffddname Ld AB     freesubfont .framef Le AB     cachecharsh.framewp cphDecL.safe2vrmaxrwwisubfontname widrr0rp(spssfzagefont .frame sesfAcf2subfont$.framefcfloadcharx.frame hbottomtopcnoflushfi4dL.safe0bsubfontname(of$subf cfpicrffreeup.frames nfesffontresizeP.framei newwidncache,.safe depthdretf Lf AB     *unitsperline.framerbitsperunitdCwordsperline .framedrobytesperline .framedr Lg AB     4subfontname.frameiHtmp1fnametmp2tmaxdepthcfname Lh AB     /Vlookupsubfont.framednameinstallsubfont .framesubfontnameuninstallsubfont.framesubfont Li AB     Breadsubfontil.frame,UQENL BG @eEHH iVH "`YP{CI VDSKD^Mr  TE A`"uNFT[ K h*XP LSMETMAG؇KFLH  TR DG[J`ZWHQAIOHMBGUT8DAFBB ACVFJDdFPGTBBBBBIKH IKENILEIDGS  CS K~P^CMMFYBUH OT BOH OSDFA DFAFF HH JHEKN IQEFH KDJHVG ALC OHCGm5BOIN='zBF7dFCP [ NME%AFQ  Ki(COYS L5BBCEFEBCEFEBBCEFEABCEFEBBCEHCHECAAAAAAlBBPVFL#include #include #include #include Image *hrhand, *minhand; Image *dots, *back; Point circlept(Point c, int r, int degrees) { double rad; rad = (double) degrees * PI/180.0; c.x += cos(rad)*r; c.y -= sin(rad)*r; return c; } void redraw(Image *screen) { static int tm, ntm; static Rectangle r; static Point c; static int rad; static Image *im; int i; int anghr, angmin; static Tm tms; static Tm ntms; ntm = time(0); if(ntm == tm && eqrect(screen->r, r)) return; ntms = *localtime(ntm); anghr = 90-(ntms.hour*5 + ntms.min/10)*6; angmin = 90-ntms.min*6; tm = ntm; tms = ntms; r = screen->r; c = divpt(addpt(r.min, r.max), 2); rad = Dx(r) < Dy(r) ? Dx(r) : Dy(r); rad /= 2; rad -= 8; draw(screen, screen->r, back, nil, ZP); for(i=0; i<12; i++) fillellipse(screen, circlept(c, rad, i*(360/12)), 2, 2, dots, ZP); line(screen, c, circlept(c, (rad*3)/4, angmin), 0, 0, 1, minhand, ZP); line(screen, c, circlept(c, rad/2, anghr), 0, 0, 1, hrhand, ZP); flushimage(display, 1); } void eresized(int new) { if(new && getwindow(display, Refnone) < 0) fprint(2,"can't reattach to window"); redraw(screen); } void main(int, char**) { Event e; Mouse m; Menu menu; char *mstr[] = {"exit", 0}; int key, timer; int t; initdraw(0,0,"clock"); back = allocimagemix(display, DPalebluegreen, DWhite); hrhand = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DDarkblue); minhand = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPaleblue); dots = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DBlue); redraw(screen); einit(Emouse); t = (30*1000); timer = etimer(0, t); menu.item = mstr; menu.lasthit = 0; for(;;) { key = event(&e); if(key == Emouse) { m = e.mouse; if(m.buttons & 4) { if(emenuhit(3, &m, &menu) == 0) exits(0); } } else if(key == timer) { redraw(screen); } } } enum { Qdir, /* /dev for this window */ Qcons, Qconsctl, Qcursor, Qwdir, Qwinid, Qwinname, Qkbdin, Qlabel, Qbordersize, Qmouse, Qnew, Qscreen, Qsnarf, Qtext, Qwctl, Qwindow, Qwsys, /* directory of window directories */ Qwsysdir, /* window directory, child of wsys */ QMAX, }; #define STACK 8192 typedef struct Consreadmesg Consreadmesg; typedef struct Conswritemesg Conswritemesg; typedef struct Stringpair Stringpair; typedef struct Dirtab Dirtab; typedef struct Fid Fid; typedef struct Filsys Filsys; typedef struct Mouseinfo Mouseinfo; typedef struct Mousereadmesg Mousereadmesg; typedef struct Mousestate Mousestate; typedef struct Ref Ref; typedef struct Timer Timer; typedef struct Wctlmesg Wctlmesg; typedef struct Window Window; typedef struct Xfid Xfid; enum { Selborder = 2, /* border of selected window */ Unselborder = 8, /* border of unselected window */ Scrollwid = 12, /* width of scroll bar */ Scrollgap = 4, /* gap right of scroll bar */ BIG = 3, /* factor by which window dimension can exceed screen */ TRUE = 1, FALSE = 0, }; #define QID(w,q) ((w<<8)|(q)) #define WIN(q) ((((ulong)(q).path)>>8) & 0xFFFFFF) #define FILE(q) (((ulong)(q).path) & 0xFF) enum /* control messages */ { Wakeup, Reshaped, Moved, Refresh, Movemouse, Rawon, Rawoff, Holdon, Holdoff, Deleted, Exited, }; struct Wctlmesg { int type; Rectangle r; Image *image; }; struct Conswritemesg { Channel *cw; /* chan(Stringpair) */ }; struct Consreadmesg { Channel *c1; /* chan(tuple(char*, int) == Stringpair) */ Channel *c2; /* chan(tuple(char*, int) == Stringpair) */ }; struct Mousereadmesg { Channel *cm; /* chan(Mouse) */ }; struct Stringpair /* rune and nrune or byte and nbyte */ { void *s; int ns; }; struct Mousestate { Mouse; ulong counter; /* serial no. of mouse event */ }; struct Mouseinfo { Mousestate queue[16]; int ri; /* read index into queue */ int wi; /* write index */ ulong counter; /* serial no. of last mouse event we received */ ulong lastcounter; /* serial no. of last mouse event sent to client */ int lastb; /* last button state we received */ uchar qfull; /* filled the queue; no more recording until client comes back */ }; struct Window { Ref; QLock; Frame; Image *i; Mousectl mc; Mouseinfo mouse; Channel *ck; /* chan(Rune[10]) */ Channel *cctl; /* chan(Wctlmesg)[20] */ Channel *conswrite; /* chan(Conswritemesg) */ Channel *consread; /* chan(Consreadmesg) */ Channel *mouseread; /* chan(Mousereadmesg) */ Channel *wctlread; /* chan(Consreadmesg) */ uint nr; /* number of runes in window */ uint maxr; /* number of runes allocated in r */ Rune *r; uint nraw; Rune *raw; uint org; uint q0; uint q1; uint qh; int id; char name[32]; uint namecount; Rectangle scrollr; /* * Rio once used originwindow, so screenr could be different from i->r. * Now they're always the same but the code doesn't assume so. */ Rectangle screenr; /* screen coordinates of window */ int resized; int wctlready; Rectangle lastsr; int topped; int notefd; uchar scrolling; Cursor cursor; Cursor *cursorp; uchar holding; uchar rawing; uchar ctlopen; uchar wctlopen; uchar deleted; uchar mouseopen; char *label; int pid; char *dir; Image *colors[NCOL]; int bordersize; }; int winborder(Window*, Point); void winctl(void*); void winshell(void*); Window* wlookid(int); Window* wmk(Image*, Mousectl*, Channel*, Channel*, int, char *, int, int, int, int, int, int); // nutz more params Window* wpointto(Point); Window* wtop(Point); void wtopme(Window*); void wbottomme(Window*); char* wcontents(Window*, int*); int wbswidth(Window*, Rune); int wclickmatch(Window*, int, int, int, uint*); int wclose(Window*); int wctlmesg(Window*, int, Rectangle, Image*); int wctlmesg(Window*, int, Rectangle, Image*); uint wbacknl(Window*, uint, uint); uint winsert(Window*, Rune*, int, uint); void waddraw(Window*, Rune*, int); void wborder(Window*, int); void wclosewin(Window*); void wcurrent(Window*); void wcut(Window*); void wdelete(Window*, uint, uint); void wdoubleclick(Window*, uint*, uint*); void wfill(Window*); void wframescroll(Window*, int); void wkeyctl(Window*, Rune); void wmousectl(Window*); void wmovemouse(Window*, Point); void wpaste(Window*); void wplumb(Window*); void wrefresh(Window*, Rectangle); void wrepaint(Window*); void wresize(Window*, Image*, int); void wscrdraw(Window*); void wscroll(Window*, int); void wselect(Window*); void wsendctlmesg(Window*, int, Rectangle, Image*); void wsetcursor(Window*, int); void wsetname(Window*); void wsetorigin(Window*, uint, int); void wsetpid(Window*, int, int); void wsetselect(Window*, uint, uint); void wshow(Window*, uint); void wsnarf(Window*); void wscrsleep(Window*, uint); void wsetcols(Window*); struct Dirtab { char *name; uchar type; uint qid; uint perm; }; struct Fid { int fid; int busy; int open; int mode; Qid qid; Window *w; Dirtab *dir; Fid *next; int nrpart; uchar rpart[UTFmax]; }; struct Xfid { Ref; Xfid *next; Xfid *free; Fcall; Channel *c; /* chan(void(*)(Xfid*)) */ Fid *f; uchar *buf; Filsys *fs; QLock active; int flushing; /* another Xfid is trying to flush us */ int flushtag; /* our tag, so flush can find us */ Channel *flushc; /* channel(int) to notify us we're being flushed */ }; Channel* xfidinit(void); void xfidctl(void*); void xfidflush(Xfid*); void xfidattach(Xfid*); void xfidopen(Xfid*); void xfidclose(Xfid*); void xfidread(Xfid*); void xfidwrite(Xfid*); enum { Nhash = 16, }; struct Filsys { int cfd; int sfd; int pid; char *user; Channel *cxfidalloc; /* chan(Xfid*) */ Fid *fids[Nhash]; }; Filsys* filsysinit(Channel*); int filsysmount(Filsys*, int); Xfid* filsysrespond(Filsys*, Xfid*, Fcall*, char*); void filsyscancel(Xfid*); void wctlproc(void*); void wctlthread(void*); void deletetimeoutproc(void*); struct Timer { int dt; int cancel; Channel *c; /* chan(int) */ Timer *next; }; Font *font; Font *altfont; Mousectl *mousectl; Mouse *mouse; Keyboardctl *keyboardctl; Display *display; Image *view; Screen *wscreen; Cursor boxcursor; Cursor crosscursor; Cursor sightcursor; Cursor whitearrow; Cursor query; Cursor *corners[9]; Image *background; Image *lightgrey; Image *red; Window **window; Window *wkeyboard; /* window of simulated keyboard */ int nwindow; int snarffd; Window *input; QLock all; /* BUG */ Filsys *filsys; Window *hidden[100]; int nhidden; int nsnarf; Rune* snarf; int scrolling; int maxtab; Channel* winclosechan; Channel* deletechan; char *startdir; int sweeping; int wctlfd; char srvpipe[]; char srvwctl[]; int errorshouldabort; int menuing; /* menu action is pending; waiting for window to be indicated */ int snarfversion; /* updated each time it is written */ int messagesize; /* negotiated in 9P version setup */ #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" Cursor crosscursor = { {-7, -7}, {0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, }, {0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE, 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, } }; Cursor boxcursor = { {-7, -7}, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00, } }; Cursor sightcursor = { {-7, -7}, {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8, }, {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00, } }; Cursor whitearrow = { {0, 0}, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }, {0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C, 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C, 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, } }; Cursor query = { {-7,-7}, {0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8, 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, }, {0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c, 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, } }; Cursor tl = { {-4, -4}, {0xfe, 0x00, 0x82, 0x00, 0x8c, 0x00, 0x87, 0xff, 0xa0, 0x01, 0xb0, 0x01, 0xd0, 0x01, 0x11, 0xff, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x1f, 0x00, }, {0x00, 0x00, 0x7c, 0x00, 0x70, 0x00, 0x78, 0x00, 0x5f, 0xfe, 0x4f, 0xfe, 0x0f, 0xfe, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00, } }; Cursor t = { {-7, -8}, {0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x06, 0xc0, 0x1c, 0x70, 0x10, 0x10, 0x0c, 0x60, 0xfc, 0x7f, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, 0x03, 0x80, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } }; Cursor tr = { {-11, -4}, {0x00, 0x7f, 0x00, 0x41, 0x00, 0x31, 0xff, 0xe1, 0x80, 0x05, 0x80, 0x0d, 0x80, 0x0b, 0xff, 0x88, 0x00, 0x88, 0x0, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xf8, }, {0x00, 0x00, 0x00, 0x3e, 0x00, 0x0e, 0x00, 0x1e, 0x7f, 0xfa, 0x7f, 0xf2, 0x7f, 0xf0, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, } }; Cursor r = { {-8, -7}, {0x07, 0xc0, 0x04, 0x40, 0x04, 0x40, 0x04, 0x58, 0x04, 0x68, 0x04, 0x6c, 0x04, 0x06, 0x04, 0x02, 0x04, 0x06, 0x04, 0x6c, 0x04, 0x68, 0x04, 0x58, 0x04, 0x40, 0x04, 0x40, 0x04, 0x40, 0x07, 0xc0, }, {0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x90, 0x03, 0x90, 0x03, 0xf8, 0x03, 0xfc, 0x03, 0xf8, 0x03, 0x90, 0x03, 0x90, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, } }; Cursor br = { {-11, -11}, {0x00, 0xf8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0xff, 0x88, 0x80, 0x0b, 0x80, 0x0d, 0x80, 0x05, 0xff, 0xe1, 0x00, 0x31, 0x00, 0x41, 0x00, 0x7f, }, {0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x0, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x7f, 0xf0, 0x7f, 0xf2, 0x7f, 0xfa, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x3e, 0x00, 0x00, } }; Cursor b = { {-7, -7}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xfc, 0x7f, 0x0c, 0x60, 0x10, 0x10, 0x1c, 0x70, 0x06, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, }, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x03, 0x80, 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } }; Cursor bl = { {-4, -11}, {0x1f, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0xff, 0xd0, 0x01, 0xb0, 0x01, 0xa0, 0x01, 0x87, 0xff, 0x8c, 0x00, 0x82, 0x00, 0xfe, 0x00, }, {0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0f, 0xfe, 0x4f, 0xfe, 0x5f, 0xfe, 0x78, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x00, 0x0, } }; Cursor l = { {-7, -7}, {0x03, 0xe0, 0x02, 0x20, 0x02, 0x20, 0x1a, 0x20, 0x16, 0x20, 0x36, 0x20, 0x60, 0x20, 0x40, 0x20, 0x60, 0x20, 0x36, 0x20, 0x16, 0x20, 0x1a, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x03, 0xe0, }, {0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x09, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x3f, 0xc0, 0x1f, 0xc0, 0x09, 0xc0, 0x09, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x00, 0x00, } }; Cursor *corners[9] = { &tl, &t, &tr, &l, nil, &r, &bl, &b, &br, }; void iconinit(void) { background = allocimage(display, Rect(0,0,1,1), RGB24, 1, 0x777777FF); red = allocimage(display, Rect(0,0,1,1), RGB24, 1, 0xDD0000FF); } window -font /usr/matt/lib/font/ttf/times/times.16.font -bg 0xffcaf1ff window -font /usr/matt/lib/font/ttf/luximb/luximb.14.font -bg 0xffffaaff -text 0xff0000ff -high 0xff0000ff -htext 0xffffffff -bord 0x00ff00ff window -font /usr/matt/lib/font/ttf/verdana/verdana.12.font -bg 0xcaf1ffff window -font /usr/matt/ink/Adore64/Adore64.14.font -bg 0xff3333ff -text 0xffffffff -bord 0xffff88ff -htext 0xffff00ff -high 0xff00ffff #!/bin/rc srcbind bind -bc /usr/matt/plan9_local/386/lib /386/lib bind /usr/matt/plan9_local/sys/src/libdraw/init.c /sys/src/libdraw/init.c bind /usr/matt/plan9_local/sys/include/draw.h /sys/include/draw.h defn go(bp) { win(); bpset(bp); cont(); regs(); } void keyboardsend(char*, int); int whide(Window*); int wunhide(int); void freescrtemps(void); int parsewctl(char**, Rectangle, Rectangle*, int*, int*, int*, int*, char**, char**, int *, int *, int *, int *, int *, int *, char*, char*); int writewctl(Xfid*, char*); Window *new(Image*, int, int, int, char*, char*, int, int, int, int, int, int, char*, char**); void riosetcursor(Cursor*, int); int min(int, int); int max(int, int); Rune* strrune(Rune*, Rune); int isalnum(Rune); void timerstop(Timer*); void timercancel(Timer*); Timer* timerstart(int); void error(char*); void killprocs(void); int shutdown(void*, char*); void iconinit(void); void *erealloc(void*, uint); void *emalloc(uint); char *estrdup(char*); void button3menu(void); void button2menu(Window*); void cvttorunes(char*, int, Rune*, int*, int*, int*); /* was (byte*,int) runetobyte(Rune*, int); */ char* runetobyte(Rune*, int, int*); void putsnarf(void); void getsnarf(void); void timerinit(void); int goodrect(Rectangle); #define runemalloc(n) malloc((n)*sizeof(Rune)) #define runerealloc(a, n) realloc(a, (n)*sizeof(Rune)) #define runemove(a, b, n) memmove(a, b, (n)*sizeof(Rune)) #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" char Eperm[] = "permission denied"; char Eexist[] = "file does not exist"; char Enotdir[] = "not a directory"; char Ebadfcall[] = "bad fcall type"; char Eoffset[] = "illegal offset"; int messagesize = 8192+IOHDRSZ; /* good start */ enum{ DEBUG = 0 }; Dirtab dirtab[]= { { ".", QTDIR, Qdir, 0500|DMDIR }, { "cons", QTFILE, Qcons, 0600 }, { "cursor", QTFILE, Qcursor, 0600 }, { "consctl", QTFILE, Qconsctl, 0200 }, { "winid", QTFILE, Qwinid, 0400 }, { "winname", QTFILE, Qwinname, 0400 }, { "kbdin", QTFILE, Qkbdin, 0200 }, { "label", QTFILE, Qlabel, 0600 }, { "mouse", QTFILE, Qmouse, 0600 }, { "screen", QTFILE, Qscreen, 0400 }, { "snarf", QTFILE, Qsnarf, 0600 }, { "text", QTFILE, Qtext, 0400 }, { "wdir", QTFILE, Qwdir, 0600 }, { "wctl", QTFILE, Qwctl, 0600 }, { "bordersize", QTFILE, Qbordersize, 0400 }, { "window", QTFILE, Qwindow, 0400 }, { "wsys", QTDIR, Qwsys, 0500|DMDIR }, { nil, } }; static uint getclock(void); static void filsysproc(void*); static Fid* newfid(Filsys*, int); static int dostat(Filsys*, int, Dirtab*, uchar*, int, uint); int clockfd; int firstmessage = 1; char srvpipe[64]; char srvwctl[64]; static Xfid* filsysflush(Filsys*, Xfid*, Fid*); static Xfid* filsysversion(Filsys*, Xfid*, Fid*); static Xfid* filsysauth(Filsys*, Xfid*, Fid*); static Xfid* filsysnop(Filsys*, Xfid*, Fid*); static Xfid* filsysattach(Filsys*, Xfid*, Fid*); static Xfid* filsyswalk(Filsys*, Xfid*, Fid*); static Xfid* filsysopen(Filsys*, Xfid*, Fid*); static Xfid* filsyscreate(Filsys*, Xfid*, Fid*); static Xfid* filsysread(Filsys*, Xfid*, Fid*); static Xfid* filsyswrite(Filsys*, Xfid*, Fid*); static Xfid* filsysclunk(Filsys*, Xfid*, Fid*); static Xfid* filsysremove(Filsys*, Xfid*, Fid*); static Xfid* filsysstat(Filsys*, Xfid*, Fid*); static Xfid* filsyswstat(Filsys*, Xfid*, Fid*); Xfid* (*fcall[Tmax])(Filsys*, Xfid*, Fid*) = { [Tflush] = filsysflush, [Tversion] = filsysversion, [Tauth] = filsysauth, [Tattach] = filsysattach, [Twalk] = filsyswalk, [Topen] = filsysopen, [Tcreate] = filsyscreate, [Tread] = filsysread, [Twrite] = filsyswrite, [Tclunk] = filsysclunk, [Tremove]= filsysremove, [Tstat] = filsysstat, [Twstat] = filsyswstat, }; void post(char *name, char *envname, int srvfd) { int fd; char buf[32]; fd = create(name, OWRITE|ORCLOSE|OCEXEC, 0600); if(fd < 0) error(name); sprint(buf, "%d",srvfd); if(write(fd, buf, strlen(buf)) != strlen(buf)) error("srv write"); putenv(envname, name); } /* * Build pipe with OCEXEC set on second fd. * Can't put it on both because we want to post one in /srv. */ int cexecpipe(int *p0, int *p1) { /* pipe the hard way to get close on exec */ if(bind("#|", "/mnt/temp", MREPL) < 0) return -1; *p0 = open("/mnt/temp/data", ORDWR); *p1 = open("/mnt/temp/data1", ORDWR|OCEXEC); unmount(nil, "/mnt/temp"); if(*p0<0 || *p1<0) return -1; return 0; } Filsys* filsysinit(Channel *cxfidalloc) { int n, fd, pid, p0; Filsys *fs; Channel *c; char buf[128]; fs = emalloc(sizeof(Filsys)); if(cexecpipe(&fs->cfd, &fs->sfd) < 0) goto Rescue; fmtinstall('F', fcallfmt); clockfd = open("/dev/time", OREAD|OCEXEC); fd = open("/dev/user", OREAD); strcpy(buf, "Jean-Paul_Belmondo"); if(fd >= 0){ n = read(fd, buf, sizeof buf-1); if(n > 0) buf[n] = 0; close(fd); } fs->user = estrdup(buf); fs->cxfidalloc = cxfidalloc; pid = getpid(); /* * Create and post wctl pipe */ if(cexecpipe(&p0, &wctlfd) < 0) goto Rescue; sprint(srvwctl, "/srv/riowctl.%s.%d", fs->user, pid); post(srvwctl, "wctl", p0); close(p0); /* * Start server processes */ c = chancreate(sizeof(char*), 0); if(c == nil) error("wctl channel"); proccreate(wctlproc, c, 4096); threadcreate(wctlthread, c, 4096); proccreate(filsysproc, fs, 10000); /* * Post srv pipe */ sprint(srvpipe, "/srv/rio.%s.%d", fs->user, pid); post(srvpipe, "wsys", fs->cfd); return fs; Rescue: free(fs); return nil; } static void filsysproc(void *arg) { int n; Xfid *x; Fid *f; Fcall t; uchar *buf; Filsys *fs; threadsetname("FILSYSPROC"); fs = arg; fs->pid = getpid(); x = nil; for(;;){ buf = emalloc(messagesize+UTFmax); /* UTFmax for appending partial rune in xfidwrite */ n = read9pmsg(fs->sfd, buf, messagesize); if(n <= 0){ yield(); /* if threadexitsall'ing, will not return */ fprint(2, "rio: %d: read9pmsg: %d %r\n", getpid(), n); errorshouldabort = 0; error("eof or i/o error on server channel"); } if(x == nil){ send(fs->cxfidalloc, nil); recv(fs->cxfidalloc, &x); x->fs = fs; } x->buf = buf; if(convM2S(buf, n, x) != n) error("convert error in convM2S"); if(DEBUG) fprint(2, "rio:<-%F\n", &x->Fcall); if(fcall[x->type] == nil) x = filsysrespond(fs, x, &t, Ebadfcall); else{ if(x->type==Tversion || x->type==Tauth) f = nil; else f = newfid(fs, x->fid); x->f = f; x = (*fcall[x->type])(fs, x, f); } firstmessage = 0; } } /* * Called only from a different FD group */ int filsysmount(Filsys *fs, int id) { char buf[32]; close(fs->sfd); /* close server end so mount won't hang if exiting */ sprint(buf, "%d", id); if(mount(fs->cfd, -1, "/mnt/wsys", MREPL, buf) < 0){ fprint(2, "mount failed: %r\n"); return -1; } if(bind("/mnt/wsys", "/dev", MBEFORE) < 0){ fprint(2, "bind failed: %r\n"); return -1; } return 0; } Xfid* filsysrespond(Filsys *fs, Xfid *x, Fcall *t, char *err) { int n; if(err){ t->type = Rerror; t->ename = err; }else t->type = x->type+1; t->fid = x->fid; t->tag = x->tag; if(x->buf == nil) x->buf = malloc(messagesize); n = convS2M(t, x->buf, messagesize); if(n <= 0) error("convert error in convS2M"); if(write(fs->sfd, x->buf, n) != n) error("write error in respond"); if(DEBUG) fprint(2, "rio:->%F\n", t); free(x->buf); x->buf = nil; return x; } void filsyscancel(Xfid *x) { if(x->buf){ free(x->buf); x->buf = nil; } } static Xfid* filsysversion(Filsys *fs, Xfid *x, Fid*) { Fcall t; if(!firstmessage) return filsysrespond(x->fs, x, &t, "version request not first message"); if(x->msize < 256) return filsysrespond(x->fs, x, &t, "version: message size too small"); messagesize = x->msize; t.msize = messagesize; if(strncmp(x->version, "9P2000", 6) != 0) return filsysrespond(x->fs, x, &t, "unrecognized 9P version"); t.version = "9P2000"; return filsysrespond(fs, x, &t, nil); } static Xfid* filsysauth(Filsys *fs, Xfid *x, Fid*) { Fcall t; return filsysrespond(fs, x, &t, "rio: authentication not required"); } static Xfid* filsysflush(Filsys*, Xfid *x, Fid*) { sendp(x->c, xfidflush); return nil; } static Xfid* filsysattach(Filsys *, Xfid *x, Fid *f) { Fcall t; if(strcmp(x->uname, x->fs->user) != 0) return filsysrespond(x->fs, x, &t, Eperm); f->busy = TRUE; f->open = FALSE; f->qid.path = Qdir; f->qid.type = QTDIR; f->qid.vers = 0; f->dir = dirtab; f->nrpart = 0; sendp(x->c, xfidattach); return nil; } static int numeric(char *s) { for(; *s!='\0'; s++) if(*s<'0' || '9'<*s) return 0; return 1; } static Xfid* filsyswalk(Filsys *fs, Xfid *x, Fid *f) { Fcall t; Fid *nf; int i, id; uchar type; ulong path; Dirtab *d, *dir; Window *w; char *err; Qid qid; if(f->open) return filsysrespond(fs, x, &t, "walk of open file"); nf = nil; if(x->fid != x->newfid){ /* BUG: check exists */ nf = newfid(fs, x->newfid); if(nf->busy) return filsysrespond(fs, x, &t, "clone to busy fid"); nf->busy = TRUE; nf->open = FALSE; nf->dir = f->dir; nf->qid = f->qid; nf->w = f->w; incref(f->w); nf->nrpart = 0; /* not open, so must be zero */ f = nf; /* walk f */ } t.nwqid = 0; err = nil; /* update f->qid, f->dir only if walk completes */ qid = f->qid; dir = f->dir; if(x->nwname > 0){ for(i=0; inwname; i++){ if((qid.type & QTDIR) == 0){ err = Enotdir; break; } if(strcmp(x->wname[i], "..") == 0){ type = QTDIR; path = Qdir; dir = dirtab; if(FILE(qid) == Qwsysdir) path = Qwsys; id = 0; Accept: if(i == MAXWELEM){ err = "name too long"; break; } qid.type = type; qid.vers = 0; qid.path = QID(id, path); t.wqid[t.nwqid++] = qid; continue; } if(qid.path == Qwsys){ /* is it a numeric name? */ if(!numeric(x->wname[i])) break; /* yes: it's a directory */ id = atoi(x->wname[i]); qlock(&all); w = wlookid(id); if(w == nil){ qunlock(&all); break; } path = Qwsysdir; type = QTDIR; qunlock(&all); incref(w); sendp(winclosechan, f->w); f->w = w; dir = dirtab; goto Accept; } if(snarffd>=0 && strcmp(x->wname[i], "snarf")==0) break; /* don't serve /dev/snarf if it's provided in the environment */ id = WIN(f->qid); d = dirtab; d++; /* skip '.' */ for(; d->name; d++) if(strcmp(x->wname[i], d->name) == 0){ path = d->qid; type = d->type; dir = d; goto Accept; } break; /* file not found */ } if(i==0 && err==nil) err = Eexist; } if(err!=nil || t.nwqidnwname){ if(nf){ if(nf->w) sendp(winclosechan, nf->w); nf->open = FALSE; nf->busy = FALSE; } }else if(t.nwqid == x->nwname){ f->dir = dir; f->qid = qid; } return filsysrespond(fs, x, &t, err); } static Xfid* filsysopen(Filsys *fs, Xfid *x, Fid *f) { Fcall t; int m; /* can't truncate anything, so just disregard */ x->mode &= ~(OTRUNC|OCEXEC); /* can't execute or remove anything */ if(x->mode==OEXEC || (x->mode&ORCLOSE)) goto Deny; switch(x->mode){ default: goto Deny; case OREAD: m = 0400; break; case OWRITE: m = 0200; break; case ORDWR: m = 0600; break; } if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m) goto Deny; sendp(x->c, xfidopen); return nil; Deny: return filsysrespond(fs, x, &t, Eperm); } static Xfid* filsyscreate(Filsys *fs, Xfid *x, Fid*) { Fcall t; return filsysrespond(fs, x, &t, Eperm); } static int idcmp(void *a, void *b) { return *(int*)a - *(int*)b; } static Xfid* filsysread(Filsys *fs, Xfid *x, Fid *f) { Fcall t; uchar *b; int i, n, o, e, len, j, k, *ids; Dirtab *d, dt; uint clock; char buf[16]; if((f->qid.type & QTDIR) == 0){ sendp(x->c, xfidread); return nil; } o = x->offset; e = x->offset+x->count; clock = getclock(); b = malloc(messagesize-IOHDRSZ); /* avoid memset of emalloc */ if(b == nil) return filsysrespond(fs, x, &t, "out of memory"); n = 0; switch(FILE(f->qid)){ case Qdir: case Qwsysdir: d = dirtab; d++; /* first entry is '.' */ for(i=0; d->name!=nil && if->qid), d, b+n, x->count-n, clock); if(len <= BIT16SZ) break; if(i >= o) n += len; d++; } break; case Qwsys: qlock(&all); ids = emalloc(nwindow*sizeof(int)); for(j=0; jid; qunlock(&all); qsort(ids, nwindow, sizeof ids[0], idcmp); dt.name = buf; for(i=0, j=0; jcount-n, clock); if(len == 0) break; if(i >= o) n += len; j++; } free(ids); break; } t.data = (char*)b; t.count = n; filsysrespond(fs, x, &t, nil); free(b); return x; } static Xfid* filsyswrite(Filsys*, Xfid *x, Fid*) { sendp(x->c, xfidwrite); return nil; } static Xfid* filsysclunk(Filsys *fs, Xfid *x, Fid *f) { Fcall t; if(f->open){ f->busy = FALSE; f->open = FALSE; sendp(x->c, xfidclose); return nil; } if(f->w) sendp(winclosechan, f->w); f->busy = FALSE; f->open = FALSE; return filsysrespond(fs, x, &t, nil); } static Xfid* filsysremove(Filsys *fs, Xfid *x, Fid*) { Fcall t; return filsysrespond(fs, x, &t, Eperm); } static Xfid* filsysstat(Filsys *fs, Xfid *x, Fid *f) { Fcall t; t.stat = emalloc(messagesize-IOHDRSZ); t.nstat = dostat(fs, WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock()); x = filsysrespond(fs, x, &t, nil); free(t.stat); return x; } static Xfid* filsyswstat(Filsys *fs, Xfid *x, Fid*) { Fcall t; return filsysrespond(fs, x, &t, Eperm); } static Fid* newfid(Filsys *fs, int fid) { Fid *f, *ff, **fh; ff = nil; fh = &fs->fids[fid&(Nhash-1)]; for(f=*fh; f; f=f->next) if(f->fid == fid) return f; else if(ff==nil && f->busy==FALSE) ff = f; if(ff){ ff->fid = fid; return ff; } f = emalloc(sizeof *f); f->fid = fid; f->next = *fh; *fh = f; return f; } static uint getclock(void) { char buf[32]; seek(clockfd, 0, 0); read(clockfd, buf, sizeof buf); return atoi(buf); } static int dostat(Filsys *fs, int id, Dirtab *dir, uchar *buf, int nbuf, uint clock) { Dir d; d.qid.path = QID(id, dir->qid); if(dir->qid == Qsnarf) d.qid.vers = snarfversion; else d.qid.vers = 0; d.qid.type = dir->type; d.mode = dir->perm; d.length = 0; /* would be nice to do better */ d.name = dir->name; d.uid = fs->user; d.gid = fs->user; d.muid = fs->user; d.atime = clock; d.mtime = clock; return convD2M(&d, buf, nbuf); } #!/bin/rc acid -l f.acid 8.out ! __.SYMDEF 1083595320 0 0 644 2956 ` TZwritesubfontTwriteimageTԅwritecolmapTFetopwindowTFepublicscreenTFeoriginwindowTFeallocwindowTFebottomnwindowsTFefreescreenTFebottomwindowTFeallocscreenTFe_allocwindowTFetopnwindowsTXunloadimageT QsubfontnameT|LinstallsubfontT|LuninstallsubfontT|LlookupsubfontD|LlastsubfontTGallocsubfontTT9stringsizeTT9runestringnwidthTT9_stringnwidthTT9stringwidthTT9stringnwidthTT9runestringwidthTT9runestringsizeTf/stringsubfontTf/strsubfontwidthTrunestringbgopTstringnbgopTstringbgTrunestringnbgopTrunestringnbgTstringnbgTstringbgopTrunestringbgTXrunestringTXstringTXstringnTX_stringTXstringopTXrunestringnTXrunestringnopTXstringnopTXrunestringopT^rgb2cmapT^cmap2rgbaT^cmap2rgbT,replcliprTrectclipTpreadsubfontiTpreadsubfontTdreadimageTreadcolmapTloadimageT@polyopT@fillpolyT@fillpolyopT@polyTޙopenfontTnewwindowT}initmouseT}readmouseT}movetoT}closemouseT}setcursorTtmkfontDX0.ratholeTX0menuhitT6$lineT6$lineopTclosekeyboardTinitkeyboardTctlkeyboardTlockdisplayTgengetwindowTdrawerrorTgeninitdrawTinitdisplayDscreenTbufimageTflushimageDfontD_drawdebugD_screenTgetwindowTunlockdisplayDdisplayTclosedisplayDdebuglockdisplayTinitdrawTset_atoi_from_fileTڮicossin2TxicossinTܜ_getsubfontTJzgetrectTJzdrawgetrectTdpgetdefontTmfreesubfontT3agefontT3cachecharsT3loadcharT/PfmtT/RfmtTemovetoTecanreadTecankbdTeventTereadmouseTereadTetimerTestartTekbdTeinitTeatomouseTesetcursorTecanmouseTemouseTestartfnD".ratholeT"emenuhitT\fillarcT\ellipseopT\ellipseT\fillarcopT\arcopT\fillellipseT\fillellipseopT\arcToedrawgetrectToegetrectTkdrawreplxyTkdrawreplT\drawT\drawopT\gendrawT\_setdrawopT\gendrawopT_unpackinfoDsizeofdefontDdefontdataTdrawsetdebugTcreadimageT_twiddlecompressedT_compblocksizeTcloadimageTstrtochanTchantostrTchantodepthTwordsperlineTbytesperlineTbuildfontTfreefontTtborderTTbezsplineopTTbezsplineTTfillbezsplineopTTfillbezierTTfillbezsplineTTbezsplineptsTTbezieropTTfillbezieropTTbezierT=RectT=setalphaT=rectXrectT=canonrectD=drawld2chanT=rectaddptD=log2T=eqrectT=ptinrectT=RptT=divptT=combinerectT=rectinrectT=insetrectT=PtT=eqptT=subptT=rectsubptT=addptT=mulptD=ZRD=ZPT\1allocimagemixT namedimageT allocimageT nameimageT _allocimageT freeimageT _freeimage1alloc.8 1083591578 0 0 664 9552 ` ~E .string- ; > bad chan- ; > nel descp > Dp S~= werrstr  =p!A!p$ S~= chantodepth$ =&%A~? depthp% ?X%8<-&; > riptorb-&; > ad chann-&; > el descrp&> Dp&?W'(<&(?AO(0<-);( > iptoral-);0 > locimagep)>. Dp)Sp)?p)S) =W)3<-+;8 > : %sall-+;@ > ocimage:p+>= Dp+S+ =p, ?p,S~=free,=p-A-p1@p1Sp1AS~=flushimage1=p2@ p2 Sp23A p2 S~=bufimage2=p2@p2@~@screenidp2$@ p2 &3AX3H @ o> Qp> @>Ao>Qp> @>Ao>Qp> @>A>Ao>Qp?@ o? Qp?@?Ao?Qp?@?Ao?Qp?@?A?Ao?Qp@@ o@ Qp@@@Ao@Qp@@@Ao@Qp@@@A@A~?ap@ ?o@Q&AAOA<~?clipraC?pCSpCApCSpCApCSpC?ApC SpC?ApCS~=RectC=pC? WC %rname-v;P > too lonpv>L Dpv?WwK<&x?AOxS<-y;X > gnamedi-y;` > mage: %spy>Z DpySpy?pySy =WyV<-{;h > namedim-{;p > age: %rp{>i Dp{S{ =&|` ?AO|[ bad chan-; > nel '%.1-; > 2s' from-; > devdrawp>x DpSa?pS =W 5 >AIallocimagemix.8 1083591578 0 0 664 3058 ` ~Eqmask$1&>AX<~?.safea? p SpASpASpA p  SpA p S~=Rect=~@dp@ p SaSa?pA !yp8A p SpA p Sp???A p S~=allocimage=p>p@pOp(O&AQ~p.Sp. =p.Sp. =p. S. =p/?p/S/ =p0 ?0050>AI0arith.8 1083591578 0 0 664 5837 ` ~Eappendpt> A~@lp@ pR&AX<pR&AXnormsq&>Ap(@=(@p(?p(@=(@p( p(? ( ((~>psdist,>8Aa0@p0Sp0@p0Sp0@p0S~@ ap0 @p0 Sp0 @p0S~= subpt0 =~@ ba1 @p1Sp1 @p1Sp1 @p1Sp1 @p1 Sp1 @p1S1 =p2@=2 @p2?p2@=2 @p2 p2? 2 &3A~? nump3 ?Q3a4p5 @ p5 Sp5 @ p5 S5>p5 ? &6 ~? denp6 ?U6{7a8? p8 Sp8 @ p8 Sp8 @ p8 Sp8  S~=mulpt8=a8? p8 Sp8? p8 Sp8? p8 Sp8 ? p8  S~=divpt8=a8? p8 Sp8? p8 Sp8? p8 Sp8@ p8  Sp8@ p8 S8 =p8? p8 Sp8? p8 S8>88~>bpts1A>A~?tp0aE?pES~@p0pE@pESpE@pES~@scalepE$@pE SE=~?tp1aF?pFS~@p1pF @pFSpF@pFSpF$@pF SF=~?tp2aG?pGS~@p2pG@pGSpG@pGSpG$@pG SG=~?tp3aH?pHS~@p3pH@pHSpH @pHSpH$@pH SH=pI? pI SpI? pI SpI? pI SpI? pI  SpI? pI SpI? pI SI>&IAQI&IASIpK@pKSpK?pKSpK?pKSK>pL@pLSpL?pLSpL?pLSL>WL<&S$@ASS aa?paSpa?paSpa?paSpaApa Sa=aa?paSpa?paSpa?paSpaApa Sa=aa?paSpa@paSpa @paSpaApa Sa=pa@paSpa ?paSpa ?paSpa?pa Spa?paSpa?paSpa?paSpa?paSpa?pa Spa$@aApa$Sa>a~>!bptsf!>,Aph@phSph@phSph@phSph @ph Sph@phSph@phSph@phSph@phSph @ph SphAph$Sh>h~>"bezierptsl">(Apn@pnSpn@pnSpn@pnSpn @pn Spn@pnSpn@pnSpn@pnSpn@pnSpn @pn Sn!>po@poSpo@poSpo @poSo>o~>#_bezsplineptss#>hA~@$nptps$@ ~@%ptps%@ &y APy<za{ R {Ap{ p| a|Sp|A !||y|p| ~?&epp| &? |Aa|Sp|A !||y|~='eqpt|'=~?(periodicp|(?&}(?AO}p%@ Wp-? W~=.bezsplinepts.=A~?/lpA/?pA/?a/?pSp%@pSp$@pS#>~@0ppp0@p/? p Op/?~=1bezier1=DA~@dstp@ p Sp@ p Sp@ p Sp @ p  Sp@ p Sp@ p Sp@ p Sp@ p Sp @ p  S~@end0p$@ p $S~@end1p(@ p (S~@radiusp,@ p ,S~@srcp0@ p 0S~@spp4@ p 4Sp8@ p 8Sp A p <S~=bezierop==DApA/?a/?pSp@pSp@pSp @p Sp@pSp@pSp@pSp@pSp @p S">&/?AX&/?AX/&/?AX&/?AXskip>A~@sp@ Wp3?p4? p4 Sa4? p4 Sp4AS4=p4? q4 Pp5? p5 S5>p5? p5?v6P&6AS6.string-7;> bad heig-7;> ht or as-7;> cent in -7;> font filp7>Dp7S~=werrstr7=p7? W8z$Pp>Sp>ASp>P=> Ap>S> =p>? p? Pp?Sp?ASp?P?Ap?S? =p@?p@AOWupH?pJ?rJO&J0AUJpO? pO? pO?pP?rPO&PAOP<&P AMP eillega-Q;(> l subfon-Q;0> t rangepQ>"DpQSQ=pQ? WRpY?WY@ps? ps?DtPWt8AIbytesperline.8 1083591579 0 0 664 1250 ` ~Eunitsperline> A~@dp@ ~@bitsperunitp@ & AS<&  AQ""~=bytesperline&=Aa(Sa(@p(A !((y(p(@ p( Sp(A p( S(>((I(chan.8 1083591579 0 0 664 2934 ` ~Echannames- ;>rA- ;>gA- ;>bA- ;>kA- ;>aA- ;>mA- ;>xA~=chantostr=A~@ccp@ p S~=chantodepth=~@bufp@ &AX p  C oPp  C  p   A 0A o  OW isspace)>A~@co)@ r+  &+  AO+7p7 ? &7AX7V&:AO:eD p; Sp; ? r;P p; S~= strchr; =p; ? &;AX;pDp= r>R&>0AU>yR&>9AQ>y{AIScloadimage.8 1083591579 0 0 664 2987 ` ~E.string-;> cloadima-;> ge: bad -;> rectanglp>DpS~=werrstr=pAp@~?minyp?~?mpA?aSa@pA !yp@ p(P p S~= _compblocksize =p? ~? ncblockp ?W6( ecreadi-; > mage: ba-;(> d maxy %p>DpSp S=pA !A @~@ndata"A@ #A?&$ AS$I dcreadi-%;8> mage: ba-%;@> d count p%>2Dp%Sp% S%=p&A&p(@ p(P p( Sp(  ~?nbp( ? (A p( S~=bufimage(=p(@p(? p( ? p( &)AX)c %d57>PAI7computil.8 1083591579 0 0 664 945 ` ~E.string-&;> creadima-&;> ge: bad p&>Dp&S~=werrstr&=p'A'&) A~?newp) ?O)4 formatc-,;> readimag-,; > e: bad c-,;(> hannel s-,;0> tring %sp,>Dp,Sa,?p,S,=p-A-W-D creadim-2;@> age: bad-2;H> ldepth p2>9Dp2Sp2 S2=p3A3~= drawld2chanp5  =p5 ?a7? p7 S~= atoi7 =~? rp7 ?a8? p8 S8 =p8 ?a9? p9 S9 =p9 ?a:? p: S: =p: ?p; ?&; ?Q;[ %dcread-<;X> image: b-<;`> ad rectap<>SDp<S<=p=A=~@ dolock&@ @AO@e<~@dpA@pAS~=lockdisplayA=pB@ pB SaBSaB ?pBA !BByBpB ? pB SpBASpBAS~=allocimageB=pB ~?ipB?&C @AOCz nglecre-[;p> adimage:-[;x> bad maxp[>mDp[Sp[ S[=p[?W\<&^ AS^ y %dcre-_;> adimage:-_;> bad coup_>Dp_Sp_ S_=p_?W` nt %d5w>AIwdebug.8 1083591579 0 0 664 663 ` ~E.string-;> drawsetd-;> ebug: %rpApSp>DpS~=fprint=oDAO~@vp@ o O-;> 5>AIdefont.8 1083591579 0 0 664 90286 ` ~EA-2;=?A-2;=A-2;=A-2; =A-2; =A-2; =A-2; =A-2; =A-2;=A-2;=A-5;4= A-5;5=A-5;6= A-5;7=A-5;8=A-5;9=@A-5;:=A-5;==A-5;>= A-5;?=A-6;@=A-6;A=@A-6;B=A-6;C=A-6;D=A-6;E=HA-6;G=A-6;H=A-6;I=A-6;J=A-6;K=AA-6;L=`A-6;M=A-7;P=A-7;Q=A-7;R=A-7;S=AA-7;T=AA-7;U=A-9;|=>A-9;}=A-9;~=A-9;=A-:;=A-:;=A-:;=A-:;=@A-:;=A-:;=HA-;;=$A-;;=A-;;= A-;;=A-;;=A-;;=A-;;=A-;;= A-;;=A-<;=A-<;=A-<;=@A-<;=8A-<;=A-<;=A-=;=A-@;=A-@;=A-@;=A-@;=A-@;= A-@;=A-@;=0A-@;=<A-A;=`A-A;=A-A;=A-A;=A-A;=A-A;=A-B;=A-B;=A-B;=A-B; =A-B; =A-B; =TA-B; =A-B; =A-B;=_A-B;=A-C;=A-C;=A-C;=A-C;=A-C;=A-C;=A-C;=A-C;=A-C;=A-C;=A-C;=A-C;=A-C;=A-C;=A-C;=oA-C;=A-D; =A-D;!=A-D;"=A-D;#=A-D;$=}A-D;%=A-D;&=A-D;'=oA-D;(=A-D;)=A-D;*=A-D;+=A-D;,=A-D;-=A-D;.=A-D;/=A-E;0=A-E;2=A-E;7=0A-E;9=A-F;A=8A-F;G=`A-G;Y=A-G;Z= A-I;w=xA-I;x=0A-I;y=A-I;z=A-I;{=A-I;|=A-I;}=A-I;~=A-J;=A-J;=A-J;=A-J;=A-J;=AA-J;=A-J;=0A-J;=0A-J;=$A-J;=vA-J;= A-J;= A-J;=A-J;=A-K;=A-K;=PA-K;=A-K;=A-K;=A-K;=A-K;=@A-K;=0A-K;=(A-K;=A-K;=A-K;=A-K;=A-L;=A-L;=A-L;=A-L;=A-L;=A-L;=pA-L;=$A-L;=A-L;= A-L;=A-L;=A-L;=A-L;=A-L;=pA-L;=A-L;=HA-M;=$A-M;=A-M;= A-M;=A-M;=A-M;=A-M;=A-M;=A-M;=pA-M;=A-M;=A-M;=A-M;=A-M;=A-M;=A-M;=A-N;= A-N;=A-N;=A-N;=A-N;=A-N;=A-N;=A-N;=8A-N;= A-N;= A-N;=A-O;=A-O;=A-O;=A-O;=A-O;=A-O;=A-O;=cA-O;=A-O;=xA-O;=A-O;=>A-O;=A-P;=|A-P;=A-P;= A-P;=A-P;=A-P;=A-P;=A-P;=A-P;=A-P;=<A-P;=cA-P;=?A-P;=A-Q;=A-Q;=fA-Q;=A-Q;=A-Q;=A-Q;=xA-Q;=~A-Q;=A-Q;=A-Q;=A-Q;=A-Q;=A-Q;=A-Q;= A-Q;=A-Q;=cA-R;=aA-R;=A-R;=A-R;=A-R;=0A-R;=A-R; =A-R; =0A-R;=`A-S;=`A-S;=A-S;=A-S;=A-S;=A-S;=A-S;=A-T;&=A-T;(=A-T;)=`A-T;+=A-T;,=A-T;-=A-T;.=CA-T;/=A-U;0=A-U;1=A-U;2=A-U;3=?A-U;4=A-U;5=A-U;6=A-U;7=A-U;8=A-U;9=A-U;:=A-U;;=A-U;<=A-U;==A-U;>=oA-U;?=A-V;@=A-V;A=A-V;B=A-V;C=A-V;D=~A-V;E=~A-V;F=?A-V;G=A-V;H=A-V;I=A-V;J=A-V;K=A-V;L=A-V;M=>A-V;N=?A-V;O=oA-W;R=A-W;S=A-W;T=A-W;U= A-W;V= A-W;W=0A-W;X=|A-W;Y=A-W;Z=A-W;[=A-X;`=xA-X;b=A-X;d=A-X;e=A-X;f=A-X;g=A-X;i=|A-X;l=A-X;m=A-X;n=A-X;o=A-Y;p=BA-Y;q=#A-Y;r=A-Y;t=A-Y;u= A-Y;v=A-Y;w=A-Y;x=A-Y;y=A-Y;z=A-Y;{=<A-Y;|=<A-Y;}=?A-Y;~=A-Y;=A-Z;=A-Z;=A-Z;=A-Z;=A-Z;=A-Z;=A-Z;=|A-Z;=0A-Z;=A-Z;=A-Z;=A-Z;=A-Z;=A-Z;=A-Z;==A-[;=1A-[;=A-[;=A-[;=fA-[;=6A-[;=A-[;=A-[;=A-[;= A-[;=A-[;= A-[;= A-[;=A-[;=A-[;= A-\;=A-\;= A-\;=A-\;=A-\;=@A-\;=`A-\;=A-\;=HA-\;=$A-\;=A-\;=A-\;=A-\;=A-\;=A-\;=!A-];=`A-];=PA-];=A-];=A-];=A-];=AA-];=@A-];=A-];=A-];=(A-];=A-];=A-];=A-];=A-^;=A-^;=A-^;=A-^;=@A-^;=8A-^;=HA-^;=$A-^;=A-^;= A-^;=A-^;=A-^;=A-^;=A-^;=A-^;=HA-_;=$A-_;=A-_;= A-_;=A-_;=A-_;=AA-_;=A-_;=@A-_;=A-_;=A-_;=A-_;=A-_;=A-_;= A-_;=A-`;= A-`;=A-`;=A-`;=A-`;=SA-`;=eA-`;=A-`;=A-`;=A-`;=A-`;= A-`;=A-a;= A-a;=6A-a;=A-a;=A-a;=A-a;=A-a;=A-a;=A-a;=A-a;=A-a;=cA-a;=A-b;=A-b;=bA-b;= A-b;=A-b;=A-b;=fA-b; =cA-b; =A-b; =A-b; =fA-b; =cA-b;= A-b;=A-c;=A-c;=A-c;=A-c;=A-c;=A-c;=A-c;=cA-c;=3A-c;=A-c;=A-c;=aA-c;=A-c;=A-c;= A-c;=A-c;=cA-d; =aA-d;!=A-d;"=A-d;#=A-d;%=0A-d;&=0A-d;(=A-d;*=A-d;-=0A-d;/=`A-e;1=`A-e;4= A-e;6=A-e;>=A-e;?=A-f;F=A-f;H=A-f;I=`A-f;J=A-f;K=TA-f;L=A-f;M=A-f;N={A-f;O=A-g;P=A-g;Q=A-g;R=A-g;S=A-g;T=A-g;U=A-g;V=A-g;W=A-g;X=A-g;Y=A-g;Z=A-g;[=~A-g;\=A-g;]=A-g;^=oA-g;_=A-h;`=A-h;a=A-h;b=A-h;c=A-h;d=A-h;e=A-h;f=A-h;g=A-h;h=A-h;i=A-h;j=A-h;k=A-h;l=A-h;m=A-h;n=A-h;o=oA-i;p=A-i;r=A-i;s=A-i;t=$A-i;u=A-i;v= A-i;w=0A-i;x=A-i;z=A-i;{=A-i;|=A-j;=A-j;="A-j;=A-j;=A-j;=A-j;=A-j;=,A-j;=A-j;=A-j;= A-k;=FA-k;= A-k;=A-k;=A-k;= A-k;=A-k;=A-k;=A-k;=A-k;=A-k;=pA-k;=fA-k;=0A-k;=A-k;= A-l;=A-l;=A-l;=A-l;=A-l;=`A-l;=0A-l;=fA-l;=8A-l;=A-l;=A-l;=A-l;=cA-l;=1A-l;=A-l;=fA-m;=1A-m;=A-m;=A-m;=fA-m;=6A-m;=A-m;=A-m;=A-m;=A-n;=lA-o;=A-o;=A-o;=A-o;=A-o;=A-o;=A-p;=A-p;=A-p;=A-p;=@A-p;=|A-p;=pA-p;=<A-p;=A-p;= A-p;=A-p;=A-p;=A-p;=`A-p;=A-p;=pA-q;=8A-q;=A-q;=A-q;=A-q;=A-q;=A-q;=A-q;=pA-q;=pA-q;=8A-q;=A-q;=A-q;=A-q;=A-q;=A-q;=A-r;= A-r;=A-r;=A-r;=CA-r;=iA-r;=A-r;=0A-r; =A-r; =A-r; =A-r; =A-s;= A-s;=cA-s;=A-s;=A-s;=aA-s;=bA-s;=A-s;=A-s;=A-s;=cA-s;=1A-s;=A-t; =`A-t;"=A-t;#=A-t;$=CA-t;%=A-t;&=A-t;'=A-t;(=A-t;)=3A-t;*=A-t;+=A-t;,=A-t;-=cA-t;.= A-t;/=A-u;0=A-u;1=A-u;2=A-u;3=9A-u;4=A-u;5=A-u;6=cA-u;7=aA-u;8=A-u;9=A-u;:=A-u;;=A-u;<=A-u;== A-u;>=A-u;?=cA-v;@=1A-v;A=A-v;B=A-v;C=A-v;E=0A-v;F=0A-v;H=A-v;I=>A-v;J=A-v;K=A-v;L=A-v;M=A-v;N=A-v;O=`A-w;P=>A-w;Q=nA-w;R=>A-w;S=A-w;T=A-w;U=`A-w;V=A-w;W=A-w;X=A-w;Y=8A-w;Z=lA-w;[=A-w;\=A-w;]=A-w;^=A-w;_=A-x;`=A-x;a=A-x;b=A-x;c=cA-x;d=1A-x;e=A-x;f=A-x;g=A-x;h=A-x;i=A-x;k=A-x;l=A-x;m=A-x;n=GA-x;o=A-y;p=A-y;q=A-y;r=A-y;s=A-y;t=A-y;u=A-y;v=A-y;w=A-y;x=A-y;y=A-y;z=A-y;{=A-y;|=?A-y;}=A-y;~=A-y;=A-z;=A-z;=A-z;=A-z;=A-z;=|A-z;=~A-z;=?A-z;=A-z;=A-z;=A-z;=A-z;=A-z;=A-z;=>A-z;=?A-z;=oA-{;= A-{;= A-{;=CA-{;=A-{;=A-{;=A-{;=0A-{;=A-{;='A-{;=AA-{;=A-{;=A-|;=rA-|;="A-|;=A-|;=A-|;=A-|;=A-|;=A-|;=A-|;=A-|;= A-|;=A-|;=A-|;=A-};=A-};=CA-};= A-};=A-};=,A-};=A-};= A-};=A-};=A-};=A-};=`A-};=A-};=A-};=0A-};=A-};= A-~;=A-~;=A-~;=A-~;=A-~;=`A-~;=0A-~;=cA-~;=8A-~;=A-~;=A-~;=lA-~;=6A-~;=A-~;= A-~;=A-;=1A-;=A-;=A-;=fA-;=3A-;=A-;=A-;=A-;=|A-;=>A-;=A-;=A-;=A-;=A-;=A-;=A-;=<A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=7A-;=A-;=A-;=A-;=A-;=A-;=pA-;=A-;=A-;=1A-;=A-;=A-;=fA-;=3A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=0A-;=lA-;=HA-;=$A-;=A-;=A-; =A-; =A-; =A-; = A-; =A-;=A-;=A-;=A-;=A-;=A-;=A-;=`A-;=A-;=8A-;= A-;=@A-;= A-;=A-;=A-;=A-;=AA-;=@A-;=A-;!= A-;#=A-;$=A-;%=AA-;&=A-;'=A-;)=0A-;*=A-;+= A-;,=A-;1=A-;2=cA-;3=A-;5=A-;6=A-;7=bA-;8=A-;9=A-;:= A-;;=rA-;<=1A-;==A-;>=A-;@=A-;B=`A-;C=A-;D=A-;E=A-;F=A-;G= A-;H=A-;I=3A-;J=A-;K=A-;L=A-;M=cA-;N= A-;O=A-;P=A-;Q=A-;R=A-;S=9A-;T=eA-;U=A-;V=cA-;W=aA-;X=A-;Y=A-;Z=A-;[=A-;\=A-;]=A-;^=A-;_=2A-;`=3A-;a=A-;b=A-;c=A-;d=A-;e=0A-;f=xA-;i=A-;j=A-;k=A-;l=#A-;m=1A-;n=A-;o=A-;p=fA-;q=wA-;r=A-;s=A-;t=A-;u=@A-;v=A-;w=A-;x=A-;y=lA-;z=vA-;{=A-;|=A-;}=A-;~='A-;=A-;=A-;=A-;=A-;=cA-;=1A-;=A-;=A-;=A-;=A-;=A-;=A-;=TA-;=A-;=A-;=xA-;=A-;=~A-;=A-;=oA-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=~A-;=?A-;=A-;=wA-;=A-;=A-;=A-;=A-;=A-;={A-;=A-;=A-;=A-;=oA-;=A-;= A-;=A-;=A-;=A-;=aA-;=A-;=0A-;=xA-;=(A-;=OA-;=A-;=0A-;=A-;=JA-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;= A-;=A-;=A-;=A-;=A-;=@A-;=A-;=A-;=,A-;=A-;= A-;=A-;=A-;=A-;=`A-;=A-;=A-;=0A-;=A-;= A-;=A-;=A-;=A-;=A-;=`A-;=0A-;=cA-;=,A-;=A-;=A-;=lA-;=6A-;=A-;= A-;=dA-;=A-;=1A-;=A-;=A-;=fA-;=3A-;=1A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=`A-;=0A-;=lA-;=bA-;=3A-;=A-;=A-;=A-;=`A-;=A-;=`A-;=0A-; =A-; =A-; =;A-; =A-; =A-;=A-;=aA-;=A-;=A-;=A-;=6A-;=1A-;=A-;=A-;=fA-;=3A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-; =A-;!=@A-;"=A-;#=@A-;$=lA-;%=pA-;&=$A-;'=A-;(=A-;)=A-;*=A-;+=A-;,=A-;-=A-;.=A-;/=A-;0= A-;1=A-;2=A-;3=@A-;4=A-;5=PA-;6=A-;7=A-;8=pA-;9= A-;:=A-;;=A-;<=A-;==A-;>= A-;?=`A-;A= A-;C=A-;D=A-;E=A-;F= A-;G=A-;I=0A-;J=A-;K=A-;L=A-;Q=A-;R=cA-;S=A-;T=A-;U=A-;V=A-;W=cA-;X=A-;Y=A-;Z=A-;[=<A-;\=1A-;]=A-;^=A-;_=A-;`=A-;a=A-;b=0A-;c=A-;d=A-;e=3A-;f=A-;g= A-;h=A-;i=3A-;j=A-;k=A-;l=A-;m=A-;n= A-;o=A-;p=A-;q=A-;r=A-;s=yA-;t=eA-;u=A-;v=fA-;w=aA-;x=A-;y=A-;z=A-;{=A-;|=A-;}=A-;~=A-;=A-;=A-;=A-;=A-;=A-;=A-;=0A-;=HA-;=A-;=A-;=A-;=A-;=3A-;=A-;=`A-;=A-;=cA-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=cA-;=1A-;=A-;=LA-;=A-;=A-;=A-;=A-;=A-;=2A-;=1A-;=A-;=A-;=A-;=pA-;=A-;=A-;=A-;=A-;=}A-;=A-;=A-;=A-;=/A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=?A-;=A-;=A-;=A-;=A-;=A-;=}A-;=A-;=oA-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=>A-;=A-;=A-;=A-;=A-;=A-;=0A-;=A-;=A-;=(A-;=YA-;=A-;=gA-;=A-;=A-;=rA-;=?A-;=A-;=A-;=A-;=A-;=A-;= A-;=A-;=A-;=A-;=2A-;=A-;=LA-;=fA-;=3A-;=A-;=A-;=A-;=cA-;=1A-;=A-;=A-;=>A-;=A-;=A-; =A-; =A-; =A-; =A-; =`A-; =0A-; =A-; =,A-; =A-; =A-; =lA-; =6A-; =A-; = A-; =8A-; =A-; =1A-; =A-; =A-; =fA-; =1A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =`A-; =0A-; =lA-; =A-;! =cA-;" =1A-;# =A-;$ =A-;% =`A-;& =A-;' =`A-;( =0A-;) =A-;* =7A-;+ =1A-;, =A-;- =A-;. =fA-;/ =3A-;0 =A-;1 =A-;3 =gA-;4 =1A-;5 =A-;6 =A-;7 =fA-;8 =3A-;9 =A-;: =A-;; =A-;< =A-;= =A-;> =A-;? =A-;@ =A-;A =@A-;B =A-;C =@A-;D =lA-;E =A-;F =A-;G =A-;H =A-;I =A-;J =A-;L =A-;M =A-;N =A-;O =A-;P =A-;Q =A-;R =A-;T =A-;U = A-;V =A-;W =A-;X =A-;Y =A-;Z =A-;\ =A-;] =8A-;^ =A-;_ =A-;a = A-;c =A-;d =A-;e =A-;f =AA-;g =lA-;i =0A-;j =A-;l =A-;m =A-;n =A-;o =A-;q =0A-;r =cA-;s =A-;t =A-;v =A-;w =A-;x =9A-;y =A-;z =0A-;{ =>A-;| =A-;} =A-; =A-; =A-; =0A-; =A-; =#A-; =A-; = A-; =A-; =3A-; =A-; =A-; =A-; =cA-; = A-; =A-; =A-; =A-; =A-; =A-; =5A-; =A-; =|A-; =aA-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; = A-; =A-; =A-; =A-; =0A-; =A-; =?A-; =A-; =A-; =A-; =3A-; =A-; =`A-; =A-; =cA-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =cA-; =1A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =uA-; =TA-; =A-; =A-; =}A-; =A-; =A-; =A-; =OA-; =A-; =A-; =A-; =A-; =|A-; =A-; =A-; =A-; =oA-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =vA-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =1A-; =A-; =A-; =(A-; =[A-; =A-; =A-; =A-; =A-; =JA-; =A-; = A-; =A-; =A-; =A-; =tA-; =8A-; = A-; =A-; =A-; =eA-; =RA-; =A-; =TA-; =A-; =FA-; =#A-; =A-; =A-; =A-; =bA-; =1A-; =0A-; =A-; =0A-; =A-; = A-; =A-;! =A-;" =A-;# =A-;$ =`A-;% =0A-;& =cA-;' =&A-;( =A-;) =A-;* =lA-;+ =6A-;, =A-;- = A-;. =A-;/ =A-;0 =1A-;1 =A-;2 =A-;3 =fA-;4 =0A-;5 =A-;6 =A-;7 =A-;8 =~A-;9 =?A-;: =A-;; =A-;< =A-;= =A-;> =A-;? =A-;@ =A-;A =A-;B =?A-;C =A-;D =A-;E =A-;F =A-;G =`A-;H =0A-;I =A-;J =cA-;K =1A-;L =A-;M =A-;N =fA-;O =3A-;P =A-;Q =A-;R =A-;S =kA-;T =1A-;U =A-;V =A-;W =fA-;X =1A-;Y =A-;Z =A-;[ =lA-;\ =A-;] =A-;^ =A-;_ =A-;` =A-;a =A-;b =A-;c =0A-;d =A-;e =A-;f =A-;g =A-;i =@A-;j =A-;k =A-;l =$A-;m =A-;n =A-;o =A-;p =A-;q =A-;r =A-;t =A-;u = A-;v =A-;w =A-;x = A-;y =A-;{ =A-;} =@A-;~ = A-; =A-; =A-; =A-; =A-; =pA-; =A-; =(A-; =0A-; =A-; =A-; =0A-; =cA-; =A-; =A-; =gA-; =A-; =A-; =A-; =0A-; =gA-; = A-; =A-; =A-; =A-; =A-; =0A-; =0A-; =A-; =A-; =A-; =A-; =A-; =3A-; =A-; =A-; =A-; =cA-; = A-; =A-; =A-; =A-; =A-; =A-; =5A-; =A-; =`A-; =aA-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =&A-; = A-; = A-; =A-; =A-; =0A-; =A-; =cA-; =A-; =A-; =A-; =3A-; =`A-; =A-; =cA-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =cA-; =1A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =8A-; =A-; =A-; =A-; =}A-; =A-; =A-; =A-; =oA-; =A-; =9A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =oA-; =A-; =A-; =A-; =A-; =yA-; =A-; =~A-; =A-; =A-; =A-; =A-; = A-; =A-; =A-; =A-; =`A-; =`A-; =0A-; =fA-; =(A-; =MA-; =A-; =`A-; =A-; =A-;# =A-;$ =A-;% =A-;& =A-;' =A-;( =A-;) =A-;* =8A-;, =?A-;- =A-;. =A-;/ =A-;0 =A-;1 =A-;2 =A-;3 =A-;4 =A-;5 =A-;6 =?A-;7 =A-;8 =A-;9 =A-;: =A-;; =A-;< =A-;= =0A-;> =A-;? = A-;@ =A-;A =A-;B =A-;C =A-;D =`A-;E =0A-;F =cA-;G =&A-;H =A-;I =A-;J =lA-;K =6A-;L =A-;M = A-;N =8A-;O =A-;P =1A-;Q =A-;R =A-;S =fA-;T =0A-;U =A-;V =A-;W =A-;X =A-;Y =cA-;Z =1A-;[ =A-;\ =A-;] =fA-;^ =3A-;_ =`A-;` =A-;a =`A-;b =0A-;c =A-;d = A-;f =A-;g =`A-;h =0A-;i =A-;j =cA-;k =1A-;l =A-;m =A-;n =fA-;o =3A-;p =A-;q =A-;s =kA-;t =1A-;u =A-;v =A-;w =fA-;x =1A-;y =A-;z =A-;{ =lA-;| =A-;} =A-;~ =A-; = A-; =A-; =8A-; =A-; = A-; =A-; =A-; =A-; =A-; =A-; =@A-; =A-; =A-; =$A-; =A-; =A-; =A-; =A-; =A-; =aA-; =A-; =$A-; =A-; =A-; =A-; =A-; =A-; =A-; =pA-; =A-; =0A-; =A-; = A-; =A-; =A-; =1A-; =oA-; =0A-; =A-; =A-; =A-; =`A-; =cA-; =A-; = A-; =`A-; =`A-; =A-; =A-; =`A-; =cA-; =A-; =A-; =A-; =`A-; =MA-; =A-; =A-; =A-; =A-; =3A-; =A-; =A-; =A-; =cA-; = A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =`A-; =aA-; =A-; =A-; =aA-; =A-; =A-; =A-; =A-; =cA-; = A-; = A-; =A-; =`A-; =0A-; =A-; =cA-; =A-; =A-; =A-; =3A-; =`A-; =nA-; =cA-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =cA-; =1A-; =A-; =aA-; =A-; =A-; =A-; =A-; =&A-; =A-; = A-; =A-; =A-; =`A-; =A-; =TA-; =A-; =A-; =~A-; =A-; =oA-; =A-; =A-; =A-; =|A-; =A-; =A-; =A-; =A-; =A-; =A-; =_A-; =A-; =A-; =A-; =qA-; =yA-;! =<A-;" =A-;# =oA-;$ =A-;% =A-;& =A-;' =A-;( =uA-;) =~A-;* =8A-;+ =A-;, =?A-;- =A-;. =A-;/ =A-;0 =A-;1 = A-;2 = A-;3 =A-;4 =A-;5 =A-;6 =A-;7 =0A-;8 =<A-;: ='A-;; =@A-;< =A-;= =0A-;@ =xA-;G =A-;H =A-;I =A-;N =A-;O =A-;P =A-;Q =A-;R =A-;S =pA-;T =A-;U =aA-;V =A-;W =A-;X =lA-;Y =6A-;Z =A-;[ =0A-;\ =A-;] =0A-;^ =A-;_ = A-;` =A-;a =A-;b =A-;c =A-;d =`A-;e =0A-;f =cA-;g =#A-;h =A-;i =A-;j =lA-;k =6A-;l =A-;m = A-;n =LA-;o =A-;p =1A-;q =A-;r =A-;s =fA-;t =0A-;u =A-;v =A-;w =A-;x =A-;y =cA-;z =1A-;{ =A-;| =A-;} =fA-;~ =3A-; =`A-; =A-; =`A-; =0A-; =A-; = A-; =A-; =`A-; =0A-; =A-; =cA-; =1A-; =A-; =A-; =fA-; =3A-; =A-; =A-; =A-; =sA-; =1A-; =A-; =A-; =fA-; =0A-; =A-; =A-; =8A-; =A-; =A-; =A-; =A-; =PA-; =A-; =$A-; = A-; =A-; =A-; =A-; =A-; =@A-; =A-; =A-; =$A-; =A-; =A-; =A-; =A-; = A-; =A-; =0A-; =(A-; =A-; = A-; =A-; =A-; =A-; = A-; =A-; = A-; =A-; =A-; =A-; = A-; =A-; =A-; =2A-; =oA-; =A-; =A-; =A-; =A-; =A-; =A-; =pA-; =`A-; =6A-; =A-; =A-; =A-; =A-; =cA-; =0A-; =A-; =`A-; =cA-; =3A-; =A-; =A-; =`A-; =A-; =0A-; =`A-; =aA-; =A-; =A-; =fA-; =cA-; =A-; =A-; =fA-; =cA-; = A-; =A-; = A-; =fA-; =A-; =A-; =A-; =A-; =`A-; =3A-; =A-; =A-; =aA-; =A-; =A-; =`A-; =A-; =cA-; = A-; =A-; =A-; =`A-; =0A-; =gA-; =A-; =A-; =#A-; =qA-; =A-; =`A-; =6A-; =cA-; =A-; =A-; =A-; =`A-; =A-; =A-; =A-; =lA-; =fA-; =A-; =A-; =A-; =aA-; =A-; =A-;! =pA-;" =A-;# =cA-;$ = A-;% =A-;& =A-;( =A-;) =`A-;+ =A-;, =A-;- =A-;. =A-;/ =_A-;0 =A-;1 =A-;2 =A-;3 =A-;5 =A-;6 =A-;7 =A-;8 =A-;9 =A-;: =A-;; =oA-;< =A-;= =A-;> =A-;? =A-;@ =~A-;A =A-;B =A-;C =A-;D =A-;E =A-;F =A-;G =A-;H =uA-;I =~A-;J =A-;K =oA-;L =A-;M =A-;N =A-;O =A-;Q = A-;R =A-;S =A-;T =A-;U =A-;V =`A-;W =0A-;X =A-;Z =A-;[ =A-;c =?A-;d =A-;g =A-;h =A-;i =A-;o =A-;p =A-;q =!A-;r =A-;s =A-;t =A-;u =aA-;v =A-;w =A-;x =lA-;y =6A-;z =A-;{ =0A-;| =fA-;} =0A-;~ =A-; = A-; =A-; =A-; =A-; =A-; =`A-; =0A-; =fA-; =#A-; =A-; =A-; =A-; =cA-; =1A-; =A-; =fA-; =A-; = A-; =A-; =A-; =`A-; =A-; =A-; =A-; =A-; =gA-; =3A-; =A-; =A-; =A-; =sA-; =tA-; =bA-; =1A-; =A-; =A-; =FA-; = A-; =A-; =`A-; =0A-; =A-; =6A-; =1A-; =A-; =A-; =A-; =aA-; =A-; =A-; =A-; =6A-; =;A-; =A-; =A-; =A-; =pA-; =A-; =A-; =0A-; =A-; =A-; =A-; =`A-; =A-; =$A-; = A-; =8A-; =A-; =A-; =A-; =@A-; =A-; =A-; =A-; =A-; =A-; =A-; =@A-; =`A-; =PA-; =0A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =A-; =pA-; =8A-; =A-; = A-; =A-; =A-; =A-; =9A-; =A-; = A-; = A-; =A-; =A-; =pA-; =A-; =A-; =A-; =A-; =A-; =A-; =aA-; =A-; =pA-; =`A-; =>A-; =A-; =A-; =A-;=0A-;=A-;=aA-;=A-;=A-;=A-; =A-; =A-; =A-; =>A-; =cA-;=?A-;=A-;= A-;=gA-;=A-;=A-;= A-;=xA-;=`A-;=A-;=A-;=A-;=A-;=A-;=A-;=`A-;=A-;=cA-; = A-;!=A-;"=A-;$=0A-;%=0A-;)=;A-;*=A-;+=A-;,=A-;-=A-;.=A-;/=`A-;0=A-;1=cA-;2=A-;3=A-;4=A-;5=pA-;6=A-;7=A-;8=A-;9=8A-;:=|A-;;= A-;<=A-;==A-;>=A-;?=A-;@=A-;A=`A-;B=A-;C=cA-;D= A-;E=A-;F=A-;H=A-;I=`A-;J=A-;K=TA-;L=A-;M=A-;N=A-;O=?A-;P=A-;Q=A-;R=A-;S=A-;T=A-;U=A-;V=A-;W=A-;X=A-;Y=A-;Z=A-;[=A-;\=A-;]=A-;^=A-;_=A-;`=A-;a=A-;b=A-;c=A-;d=A-;e=A-;f=A-;g=A-;h=wA-;i=~A-;j=?A-;k=~A-;l=?A-;m=A-;n=A-;o=A-;p=A-;q= A-;r=A-;s=A-;t=A-;u=A-;v=`A-;w=0A-;x=A-;z=A-;=A-;=A-;=A-;=A-;=A-;==A-;=A-;=A-;=A-;=aA-;=A-;=A-;=lA-;=6A-;=A-;=<A-;=<A-;=?A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=|A-;=!A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=wA-;=;A-;=A-;=A-;=A-;=sA-;=A-;=A-;=<A-;=A-;=A-;=A-;=A-;=A-;=A-;=`A-;=0A-;=A-;=A-;=1A-;=A-;=A-;=A-;=A-;=A-;=pA-;=\A-;=A-;=A-;=A-;=cA-;=A-;=A-;=A-;=0A-;=A-;=A-;=@A-;=A-;=A-;=(A-;= A-;=A-;=A-;=A-;=A-;=A-;=A-;=(A-;=A-;= A-;=A-;=A-;=A-;= A-;=A-;=A-; =A-; =A-;=@A-;=A-;=A-;7=A-;B=A-;C=A-;D=1A-;E=A-;F=A-;G=A-;P=A-;S=A-;T=A-;Z=`A-;[=A-;\=A-;d=A-;f=A-;g=A-;h=A-;i=A-;l=A-;m=A-;n=~A-;o=A- ;p=_A- ;q=A- ;r=A- ;s=A- ;t=A- ;u=A- ;v=A- ;w=A- ;x=A- ;y=A- ;z=A- ;{=A- ;|=A- ;}=A- ;~=A- ;=A- ;=A- ;=A- ;=A- ;=A- ;=A- ;=A- ;=A- ;=A- ;=A- ;=A- ;=A- ;=A- ;=A- ;=A- ;=A- ;=A- ;= A- ;=A- ;=0A- ;=|A- ;=A- ;=A- ;=A- ;=A- ;=A-;=A-;=A-;=A-;=A-;=`A-;=A-;=A-;= A-;=A-;=A-;=A-;= A-;=A-;=A-;=A-;=A-;=`A-;=A-;=$A-;=A-;=A-;=A-;=A-;=A-;=A-;=pA-;-=A-;.=A-;>=A-;W=A-;X=A-;p=xA-;s=A-;z=`A-;{=A-;|=A-;=A-;=A-;=A-;=A-;=oA-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=~A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=A-;=?A-;=A-;=A-;=A-;=A-;=A-;= A-;=A-;=0A-;=|A-;=0A-";=0A-#;=A-#;=A-#;=A-#;=`A-#;= A-#;= A-#;= A-#;= A-$; = A-$;!= A-$;"= A-$;#= A-$;$=2A-$;%=5A-$;&=6A-$;'= A-$;(= A-$;)= A-$;*= A-$;+= A-$;,= A-$;-= A-$;.= A-$;/= A-%;0= A-%;1=1A-%;2=5A-%;3= A-%;4= A-%;5= A-%;6= A-%;7= A-%;8= A-%;9= A-%;:= A-%;;= A-%;<= A-%;==1A-%;>=3A-%;?= A-&;B=A-&;C= A-&;E= A-&;F= A-&;H=A-&;I=A-&;K= A-&;L=A-&;N=A-&;O=A-';Q= A-';R=A-';T=A-';U=A-';W= A-';X=$A-';Z=A-';[=A-';]= A-';^=-A-(;`=A-(;a=A-(;c= A-(;d=6A-(;f=A-(;g=A-(;i= A-(;j=?A-(;l=A-(;m= A-(;o= A-);p=HA-);r=A-);s= A-);u= A-);v=QA-);x=A-);y= A-);{= A-);|=ZA-);~=A-);= A-*;= A-*;=cA-*;=A-*;= A-*;= A-*;=lA-*;=A-*;= A-*;= A-*;=uA-+;=A-+;=A-+;= A-+;=~A-+;=A-+;= A-+;= A-+;=A-+;=A-+;= A-+;= A-,;=A-,;=A-,;=A-,;= A-,;=A-,;=A-,;=A-,;= A-,;=A-,;=A-,;=A--;= A--;=A--;=A--;=A--;= A--;=A--;=A--;=A--;= A--;=A-.;=A-.;=A-.;= A-.;=A-.;=A-.;=A-.;= A-.;=A-.;=A-.;=A-.;= A-/;=A-/;=A-/;=A-/;= A-/;=A-/;=A-/;= A-/;= A-/;=A-/;=A-/;=A-0;= A-0;=A-0;=A-0;=A-0;= A-0;=A-0;=A-0;= A-0;= A-0;=A-0;=A-1;=A-1;= A-1;= A-1;=A-1;=A-1;=A-1;= A-1;= A-1;=A-1;=A-1;=A-1;= A-1;= A-2;= A-2;=A-2;= A-2;=)A-2;=A-2;=A-2; = A-2; = A-2; =2A-2; =A-2;=A-2;=A-3;= A-3;=;A-3;=A-3;=A-3;= A-3;= A-3;=DA-3;=A-3;=A-3;=A-3;= A-3;=MA-3;=A-4; =A-4;!= A-4;#= A-4;$=VA-4;%=A-4;&=A-4;'= A-4;)= A-4;*=_A-4;+=A-4;,=A-4;-=A-4;/= A-5;0=hA-5;1=A-5;2=A-5;3=A-5;5= A-5;6=qA-5;7=A-5;8=A-5;9=A-5;;= A-5;<=zA-5;==A-5;>=A-5;?=A-6;A= A-6;B=A-6;C=A-6;D=A-6;E= A-6;G= A-6;H=A-6;I=A-6;J= A-6;K=A-6;M= A-6;N=A-6;O=A-7;P=A-7;Q= A-7;S= A-7;T=A-7;U=A-7;V= A-7;W= A-7;Y= A-7;Z=A-7;[=A-7;\=A-7;]=A-7;_= A-8;`=A-8;a=A-8;b=A-8;c= A-8;e= A-8;f=A-8;g=A-8;h=A-8;i= A-8;k= A-8;l=A-8;m=A-8;n=A-8;o= A-9;q= A-9;r=A-9;s=A-9;t=A-9;u= A-9;w= A-9;x=A-9;y=A-9;z=A-9;{= A-9;}= A-9;~=A-9;=A-:;=A-:;= A-:;= A-:;=A-:;=A-:;=A-:;= A-:;= A-:;=A-:;=A-:;=A-:;= A-:;= A-;;=A-;;=A-;;=A-;;= A-;;= A-;;=A-;;=A-;;=A-;;= A-;;= A-;;= A-;;=A-;;=A-;;= A-<;= A-<;=A-<;=A-<;=A-<;=A-<;= A-<;=A-<;=A-<;=A-<;= A-<;= A-<;=%A-<;=A-=;=A-=;= A-=;= A-=;=.A-=;=A-=;=A-=;= A-=;= A-=;=7A-=;=A-=;=A-=;= A-=;= A->;=@A->;=A->;=A->;= A->;= A->;=IA->;=A->;=A->;= A->;= A->;=RA->;=A->;=A->;= A-?;= A-?;=[A-?;=A-?;=A-?;= A-?;= A-?;=dA-?;=A-?;=A-?;= A-?;= A-?;=mA-?;=A-@;=A-@;= A-@;= A-@;=vA-@;=A-@;=A-@;= A-@;= A-@;=A-@;=A-@;=A-@;= A-@;= A-A;=A-A;=A-A;=A-A;= A-A;= A-A;=A-A;=A-A;=A-A;= A-A;= A-A;=A-A;=A-A;=A-A;= A-B;= A-B;=A-B;=A-B;=A-B;= A-B;= A-B;=A-B; =A-B; =A-B; = A-B; = A-B;=A-B;=A-C;=A-C;= A-C;= A-C;=A-C;=A-C;=A-C;= A-C;= A-C;=A-C;=A-C;=A-C;= A-C;= A-D; =A-D;!=A-D;"=A-D;#= A-D;%= A-D;&=A-D;'=A-D;(=A-D;)=A-D;+= A-D;,=A-D;-=A-D;.=A-D;/= A-E;1= A-E;2=A-E;3=A-E;4=A-E;5= A-E;7= A-E;8=A-E;9=A-E;:=A-E;;= A-E;== A-E;>=A-E;?=A-F;@=A-F;A= A-F;C= A-F;D=A-F;E=A-F;F=A-F;G= A-F;I= A-F;J=A-F;K=A-F;L=A-F;M= A-F;O= A-G;P=A-G;Q=A-G;R=A-G;S= A-G;U= A-G;V=!A-G;W=A-G;X=A-G;Y= A-G;[= A-G;\=*A-G;]=A-G;^=A-G;_= A-H;a= A-H;b=3A-H;c=A-H;d=A-H;e=A-H;g= A-H;h=<A-H;i=A-H;j=A-H;k=A-H;m= A-H;n=EA-H;o=A-I;p=A-I;q=A-I;s= A-I;t=NA-I;u=A-I;v=A-I;w= A-I;y= A-I;z=WA-I;{=A-I;|= A-I;}=A-I;= A-J;=`A-J;=A-J;=A-J;=A-J;= A-J;=iA-J;=A-J;=A-J;= A-J;= A-J;=rA-J;=A-J;=A-J;= A-K;= A-K;={A-K;=A-K;=A-K;= A-K;= A-K;=A-K;=A-K;=A-K;= A-K;= A-K;=A-K;=A-L;=A-L;= A-L;= A-L;=A-L;=A-L;=A-L;= A-L;= A-L;=A-L;=A-L;=A-L;=A-L;= A-M;=A-M;=A-M;=A-M;= A-M;= A-M;=A-M;=A-M;=A-M;= A-M;= A-M;=A-M;=A-M;=A-M;=A-N;= A-N;=A-N;=A-N;=A-N;= A-N;= A-N;=A-N;=A-N;=A-N;= A-N;= A-N;=A-N;=A-O;=A-O;= A-O;= A-O;=A-O;=A-O;=A-O;= A-O;= A-O;=A-O;=A-O;=A-O;= A-O;= A-P;=A-P;=A-P;=A-P;=A-P;= A-P;=A-P;=A-P;=A-P;=A-P;= A-P;=A-P;=A-P;=A-P;= A-Q;= A-Q;= A-Q;=A-Q;=A-Q;= A-Q;= A-Q;=A-Q;=A-Q;=A-Q;= A-Q;= A-Q;=A-Q;=A-R;=A-R;= A-R;= A-R;=&A-R;=A-R;=A-R;= A-R; = A-R; =/A-R; =A-R; =A-R; = A-R;= A-S;=8A-S;=A-S;=A-S;= A-S;= A-S;=AA-S;=A-S;=A-S;=A-S;= A-S;=JA-S;=A-S;=A-S;= A-T;!= A-T;"=SA-T;#=A-T;$=A-T;%=A-T;'= A-T;(=\A-T;)=A-T;*=A-T;+=A-T;-= A-T;.=eA-T;/=A-U;0=A-U;1=A-U;3= A-U;4=nA-U;5=A-U;6=A-U;7= A-U;9= A-U;:=wA-U;;=A-U;<=A-U;== A-U;?= A-V;@=A-V;A=A-V;C=A-V;E= A-V;F=A-V;G=A-V;I=A-V;K= A-V;L=A-V;M=A-V;O=A-W;Q= A-W;R=A-W;S=A-W;U=A-W;W= A-W;X=A-W;Y=A-W;[=A-W;]= A-W;^=A-W;_=A-X;a=A-X;c= A-X;d=A-X;e=A-X;g=A-X;i= A-X;j=A-X;k=A-X;m=A-X;o= A-Y;p=A-Y;q=A-Y;s=A-Y;u= A-Y;v=A-Y;w=A-Y;y=A-Y;{= A-Y;|=A-Y;}=A-Y;=A-Z;= A-Z;=A-Z;=A-Z;=A-Z;= A-Z;=A-Z;=A-Z;=A-Z;= A-Z;=A-Z;=A-[;=A-[;= A-[;=A-[;=A-[;=A-[;= A-[;=A-[;=A-[;=A-[;= A-\;=A-\;=A-\;=A-\;= A-\;=A-\;=A-\;=A-\;= A-\;="A-\;=A-\;=A-];= A-];=+A-];=A-];=A-];= A-];=4A-];=A-];=A-];= A-];==A-];=A-^;=A-^;= A-^;=FA-^;=A-^;=A-^;= A-^;=OA-^;=A-^;=A-^;= A-_;=XA-_;=A-_;=A-_;= A-_;=aA-_;=A-_;=A-_;= A-_;=jA-_;=A-_;=A-`;= A-`;=sA-`;=A-`;=A-`;= A-`;=|A-`;=A-`;=A-`;= A-`;=A-`;=A-a;=A-a;= A-a;=A-a;=A-a;=A-a;= A-a;=A-a;=A-a;=A-a;= A-b;=A-b;=A-b;= A-b;= A-b;=A-b;=A-b;=A-b; =A-b; = A-b; =A-b; =A-b;=A-b;=A-c;= A-c;=A-c;=A-c;=A-c;= A-c;= A-c;=A-c;=A-c;=A-c;= A-c;= A-c;=A-c;=A-d; =A-d;!= A-d;#= A-d;$=A-d;%=A-d;&=A-d;'=A-d;)= A-d;*=A-d;+=A-d;,=A-d;-=A-d;/= A-e;0=A-e;1=A-e;2=A-e;3=A-e;5= A-e;6=A-e;7=A-e;8=A-e;9= A-e;;= A-e;<=A-e;==A-e;>=A-e;?= A-f;A= A-f;B=A-f;C=A-f;D=A-f;E= A-f;G= A-f;H= A-f;I=A-f;J=A-f;K= A-f;M= A-f;N=A-f;O=A-g;P=A-g;Q= A-g;S= A-g;T=A-g;U=A-g;V=A-g;W= A-g;Y= A-g;Z='A-g;[=A-g;\=A-g;]=A-g;_= A-h;`=0A-h;a=A-h;b=A-h;c=A-h;e= A-h;f=9A-h;g=A-h;h=A-h;i= A-h;k= A-h;l=BA-h;m=A-h;n=A-h;o= A-i;q= A-i;r=KA-i;s=A-i;t=A-i;u= A-i;w= A-i;x=TA-i;y=A-i;z=A-i;{=A-i;}= A-i;~=]A-i;=A-j;=A-j;=A-j;= A-j;=fA-j;=A-j;=A-j;=A-j;= A-j;=oA-j;=A-j;=A-j;= A-j;= A-k;=xA-k;=A-k;= A-k;=A-k;= A-k;=A-k;=A-k;=A-k;= A-k;= A-k;=A-k;=A-k;=A-k;= A-l;= A-l;=A-l;=A-l;=A-l;= A-l;= A-l;=A-l;=A-l;=A-l;= A-l;= A-l;=A-l;=A-m;=A-m;= A-m;= A-m;=A-m;=A-m;=A-m;= A-m;= A-m;=A-m;=A-m;=A-m;=A-m;= A-n;=A-n;=A-n;= A-n;= A-n;=A-n;=A-n;= A-n;= A-n;=A-n;=A-n;= A-o;= A-o;=A-o;=A-o;= A-o;= A-o;=A-o;=A-o;= A-o;= A-o;=A-o;=A-p;=A-p;= A-p;= A-p;=A-p;=A-p;=A-p;= A-p;= A-p;=A-p;=A-p;=A-p;=A-p;= A-q;=A-q;=A-q;= A-q;= A-q;=A-q;=A-q;= A-q;= A-q;=A-q;=A-q;= A-r;= A-r;=#A-r;=A-r;= A-r;= A-r;=,A-r; =A-r; = A-r; = A-r;=5A-r;=A-s;= A-s;= A-s;=>A-s;=A-s;= A-s;= A-s;=GA-s;=A-s;= A-s;= A-t; =PA-t;!=A-t;"=A-t;#= A-t;%= A-t;&=YA-t;'=A-t;)= A-t;+= A-t;,=bA-t;-=A-t;/= A-u;1= A-u;2=kA-u;3=A-u;5= A-u;7= A-u;8=tA-u;9=A-u;;= A-u;== A-u;>=}A-u;?=A-v;A= A-v;C= A-v;D=A-v;E=A-v;G= A-v;I= A-v;J=A-v;K=A-v;L=A-v;M= A-v;O= A-w;P=A-w;Q=A-w;R=A-w;S= A-w;U= A-w;V=A-w;W=A-w;Y= A-w;[= A-w;\=A-w;]=A-w;_= A-x;a= A-x;b=A-x;c=A-x;e= A-x;g= A-x;h=A-x;i=A-x;k= A-x;m= A-x;n=A-x;o=A-y;q= A-y;s= A-y;t=A-y;u=A-y;v=A-y;w= A-y;y= A-y;z=A-y;{=A-y;|=A-y;}= A-y;= A-z;=A-z;=A-z;=A-z;= A-z;= A-z;=A-z;=A-z;=A-z;= A-z;= A-z;=A-z;=A-z;=A-z;= A-{;= A-{;=A-{;=A-{;=A-{;= A-{;= A-{;=A-{;=A-{;=A-{;= A-{;= A-{;= A-{;=A-|;=A-|;= A-|;= A-|;=A-|;=A-|;=A-|;= A-|;= A-|;=A-|;=A-|;=A-|;=A-|;= A-};=(A-};=A-};=A-};= A-};= A-};=1A-};=A-};=A-};= A-};= A-};=:A-};=A-};=A-};= A-~;= A-~;=CA-~;=A-~;=A-~;= A-~;= A-~;=LA-~;=A-~;=A-~;= A-~;= A-~;=UA-~;=A-;=A-;= A-;= A-;=^A-;=A-;=A-;= A-;= A-;=gA-;=A-;=A-;= A-;= A-;=pA-;=A-;=A-;= A-;= A-;=yA-;=A-;=A-;= A-;= A-;=A-;=A-;=A-;= A-;= A-;=A-;=A-;=A-;= A-;= A-;=A-;=A-;=A-;= A-;= A-;=A-;=A-;=A-;= A-;= A-;=A-;=A-;=A-;= A-; = A-; =A-; =A-; =A-; = A-;= A-;=A-;=A-;=A-;= A-;= A-;=A-;=A-;=A-;= A-;= A-;=A-;=A-;=A-;= A-;!= A-;"=A-;#=A-;$=A-;%= A-;'= A-;(=A-;)=A-;*=A-;+= A-;-= A-;.=A-;/=A-;0=A-;1=A-;3= A-;4=A-;5=A-;6=A-;7=A-;9= A-;:=A-;;=A-;<=A-;==A-;?= A-;A= A~=sizeofdefont-;=FA~=_unpackinfo=A~@np@~@fcp@ ~@pp@ pA W draw1>A~@dstp@pOpSp@pS=p!@ p!P p! Sp!-A p! S!=~@maskp!@~@p0p! @~@ p1p! @ ~@ rp! @ p! &"AX"$<#~@ src&$ @AX$*9~= drawop= = Ap?@p?Sa? @p?Sp? @p?Sa? @p? Sp?@p?Sa? @p?Sp?$@p?S?>?~=gendrawC= ApE@pESaE @pESpE @pESaE@pE SpE @pESaE$ @pESpE ApESE>E~=gendrawopI= ApK@pKSaK @pKSpK @pKSaK@pK SpK @pKSaK$ @pKSpK,@pKSK>KIKdrawrepl.8 1083591580 0 0 664 913 ` ~Esweep-^;>A-^;>A-_;>A-_; >A-_; >A-_; >A-_; >A-_; >A-_;>A-_;>A-`;>A-`;>A-`;>A-`;>A-`;>A-`;>A-`;>A-`;>A-a;>A-a;>A-a;>A-a;>A-a;>A-a;>A-a;>A-a;>A-b; >A-b;!>?A-b;">A-b;#>A-b;$>A-b;%>A-b;&>A-b;'>A-c;*>A-c;+>A-c;,>@A-c;->A-c;.>@A-c;/>A-d;0>@A-d;1>A-d;2>@A-d;3>A-d;4>@A-d;5>A-d;6>AA-d;7>A-e;8>AA-e;9>A-e;:>AA-e;;>A-e;<>AA-e;=>rA-e;>>@A-e;?>8A-f;@>@A-f;A>A-f;B>@A-f;C>A-f;D>A-f;E>A~>brectsk>A~@rpm@m@&mAPmDp}S~=esetcursor}=~@ mp} @ WTfreetmp>A~>tmpp>pS~=freeimage=p>pS=p>pS=p >pS=~>redp>pS=pAp>p >p>p>p>=A~@up&@AO<&>AXpOp> pP  ~@rcp@ @ & UpOp> p P  p @ @ & U&>AXp= p SaSa ?pA !yp= p,P p SpASpA p S=p= p>a ?pSpASpASpAp SpQp Q  pS=p= p SaSa ?pA !yp= p,P p SpASpA p S=p>p= p SaSa ?pA !yp= p,P p SpASpA p S=p >~?.safea? p SpASpASpA p  SpA p S=p= p SaSa?pA !yp= p,P p SpA p SpA p S=p>&>AOs<&>AXtAXwAXz.string-;> getrect:-;> allocim-;> age failp=pSp>DpS~=drawerror=aSa@pA !y~?rectsa?pS>&@AXpSpAS~=ZPp=pSp=p S~=draw=p? W<pA WpSaSa?pA !yp=pSpASp Aa?aSpA !y=p=pSp?Aa?aSpA !yp>pSpASp=pSp=p S=p? W<-;> ed5>A5>A5>HA5> AIellipse.8 1083591580 0 0 664 5513 ` ~Edoellipse>A~@dstp@pOpS~@opp(@pS~=_setdrawop=p@ pP p Sp-A p S~=bufimage=~@alphap @~@phip$@~@cp@ ~@ spp @ p &AX<~> .string-; > image el-; > lipse: %pApSp> DpS~= fprint =~@ cmdp @ o Op@ pP o Op@pOAoQp@pOAoQp@pOAAoQ~@ srcp @ pP o Qp @pOAoQp @pOAoQp @pOAAoQpT o  QpTAo QpTAo QpTAAo QpT o  QpTAoQpTAoQpTAAoQ~@xrp @ o Qp @AoQp @AoQp @AAoQ~@yrp@ o Qp@AoQp@AoQp@AAoQ~@thickp @ o  Qp @ Ao Qp @ Ao Qp @ A Ao Qp!R o! Qp!R!Ao!Qp!R!Ao!Qp!R!A!Ao! Qp"R o" !Qp"R"Ao""Qp"R"Ao"#Qp"R"A"Ao"$Qo#%Qp##Ao#&Qp##Ao#'Qp##A#Ao#(Qo$)Qp$$Ao$*Qp$$Ao$+Qp$$A$Ao$,Q$~=ellipse(=0Ap*eAp*Sp*@p*Sa*@p*S~@ap* @p* S~@bp*@p*Sp*@p*Sp* @p*Sa* @p*Sp*A Sp*A$Sp* Ap*(S*>*~=ellipseop.=0Ap0eAp0Sp0@p0Sa0@p0Sp0 @p0 Sp0@p0Sp0@p0Sp0 @p0Sa0 @p0Sp0A Sp0A$Sp0$@p0(S0>0~=fillellipse4=0Ap6EAp6Sp6@p6Sa6@p6Sp6 @p6 Sp6@p6Sp6ASp6 @p6Sa6 @p6Sp6A Sp6A$Sp6 Ap6(S6>6~=fillellipseop:=0Ap<EAp<Sp<@p<Sa<@p<Sp< @p< Sp<@p<Sp<ASp< @p<Sa< @p<Sp<A Sp<A$Sp< @p<(S<><~=arc@=0Ap@$@ BA pCeApCSpC@pCSaC@pCSpC @pC SpC@pCSpC@pCSpC @pCSaC @pCSpC  SpC(@pC$SpC ApC(SC>C~=arcopG=0ApG$@ IA pJeApJSpJ@pJSaJ@pJSpJ @pJ SpJ@pJSpJ@pJSpJ @pJSaJ @pJSpJ  SpJ(@pJ$SpJ,@pJ(SJ>J~=fillarcN=0ApN @ PA pQEApQSpQ@pQSaQ@pQSpQ @pQ SpQ@pQSpQASpQ @pQSaQ @pQSpQ  SpQ$@pQ$SpQ ApQ(SQ>Q~=fillarcopU=0ApU @ WA pXEApXSpX@pXSaX@pXSpX @pX SpX@pXSpXASpX @pXSaX @pXSpX  SpX$@pX$SpX(@pX(SX>X-X; > r 5X >AIXemenuhit.8 1083591583 0 0 664 16688 ` ~Emenucolorsd>4A~=displaypg= pg SpgA pg SpgA pg S~=allocimagemixg=~>backpg>~?.safeah? ph SphASphASphA ph  SphA ph S~=Recth=ph= ph SahSah?phA !hhyhphXA ph SphA ph SphDDA ph S~=allocimageh=~>highph>ai? pi SpiASpiASpiA pi  SpiA pi Si=pi= pi SaiSai?piA !iiyipiXA pi SpiA pi SpïA pi Si=pi> ~> bordpi >&j AOj@<&j>AXjA textpl >~> htextpm  >nWpKprSr =ps >psSs =ps= pt|Ppt>puPpu>pvPpv >pwPpw >px|Ppx >x~> menurect >A~@ip@ ~=fontp= & APl<~@.retp@pSpASpASpA SpAS=vP A= ~@r @vP @ Ap@p@pSaSa@pA !ypApS~=insetrect=~>menusel> Aa@pSaSa@pA !ypApS=~@pp@ p Sp@ p SaSa@pA !y~=ptinrect=&AXpaintitem>HAp@ & AP<~?ra?pSaS~@textra@pA !yp S >~=screenp= ~@menup@ ~@restorep$@ ~@savep @ & AOWpSpASp!?pSp!?p S=~=#.ratholea#=pSp=pSp!?pSp!?p S&"@AO(W)pSp!?pSp!?pSp=pSp?p S~=$string$=~>%menuscan%>4Ap@pSaSa @pA !yp@pS~@&lastip &@pSpApSp$@p SpA$S>p=pSpApS~='flushimage'=~@(mp(@pS~=)emouse)=WR&A~?+ip+?Ov<& &@Owp+? & AXWP,menupaint,>@Aa?pSaSa@pA !ypApS=p=pSaSa?pA !yp>pSpAS~=-ZPp-=pSp-=p S=pA Wp+? W<~>/menuscrollpaint/>LAp=pSaS~@0scrollra0@pA !yp>pSpASp-=pSp-=p S=p@ ~@1nitemp1@ p0@p?p0@p?p 0@0@= : p0@  p ?p 0@0@p  .@ p ?=?: p0@  p ?p? A&?P pSp-=pSp-=p S~=border=~>menutxtp> & AX9OWpSpASp-=pSp-=p S=~=emenuhit=Ap= ~>back&>AX^<~>menucolors>p= p  A~?sca?pA !yp p SpASp  AaSpA !y~=replclipr=p@ ~? maxwidpA ?pAWwp/Sp/ASp/-=p/Sp/-=p/ S/=p0=p0Sa0Sa0?p0A !00y0p0Ap0S~>bordp0>p0Sp0-=p0Sp0-=p0 S0=a1` ? p1 Sa1Sa1?p1A !11y1p1AS~> menurect1 >p1= p1 Sa1Sa1` ?p1A !11y1p1= p1,P p1 Sp1ASp1A p1 S1=~?!savep1!?a2?p2Sa2Sa2?p2A !22y2p2?p2S2 >a3` ?p3Sp3?p3Sp3?p3Sp3?p3 Sp3?p3S~="addpt3"=a3h ?p3Sp3` ?p3Sp3d ?p3Sp3Ap3 S~=#divpt3#=p3h ?p3Sp3l ?p3S~=$emoveto3$=~@%menup4%@p4Sa4Sa4?p4A !44y4p4 ?p4Sp4?p4S4,>p4*@ p4(@ &5 ?AO5p6*@ p6(@ WL&menuscan8&>&9Ap9?U94 >p>Al ?:>l ?> &? AP?zaFSaF?pFA !FFyFpF ?pFSpF ?pFSpF?pFSF/>pI=pISpIApIS~=(flushimageI(=~@)mpJ)@pJS~=*emouseJ*=WJ5,high5V,>A~>-text5V->A~>.htext5V.>A~=/.rathole5V/=A5V>A5V>A5V>AIVevent.8 1083591583 0 0 664 18421 ` ~ESkeyboard-k;>A~>Smouse-l;>A~>Stimer-m;>A~>ebread}>AWepipep> p S~=dirfstat=p &A~?dp?X<~>.string-;> events: -;> eread st-;> at error~= displayp =pSp>DpS~= drawerror =p? p  $AaSpA !y~= _v2ul =~? lp ?p?pS~= free =~@sp@ pQ&AO#<& ?AO$extract>Wnslavep> ~@keysp@ & AXAeslave&>AXWXbXh<~=ekbd=p@ pPW<& >XnW p S>p? p@ p pO p Rp A& >AOp?Wp> p@ WBAP<-;> events:-; > mouse n-;(> ot initip =pSp>DpS =pA p S~=ecanread=~=ecankbd= A&>AP<-;0> alizede-;8> vents: k-;@> eyboard -;H> not initp =pSp>7DpS =pA p S==Ap> p@ WAX p S=p &Ap?X<-;P> ialzede-;X> vents: e-;`> canread -;h> stat errp =pSp>WDpS =p? p  $AaSpA !y =p ?p?pS =& ?AXp> p@ W orevent-;x> s: bad f-;> ile descp =pSp>sDpS =~@!n&!@AS <&!@ AQ #eforkslave#>p & AP<~@$fnp $@p  A p  >p  pA  A~?%bufo%?W p Sa%? p Sp ~?'rpt'?C p S~=(write(=pt'? C & O5pSa%?pSpApS(=pAS~=)_exits)=pA~=*estart*=Ap"@ p Sp @ p Sp!@ p SpA S=~=+etimer+=A&>AOV<-;> riptore-;> vents: t-;> imer sta-;> rted twip =pSp>DpS =p"@ p S#>p &  AP ap A   & !@AQ d A~?,to ,?o ,?Wu p Sa,? p SpA p S(=&AXkpSa,?pSpApS(=pAS)=pA~>.ekeyslave.>0ApA p S#>& AP<~?/knp A/?p!>o!,?W0 p. Sa.,? p. Sp.A p. S.(=&.AO.p3Sa3,?p3Sp3Ap3S3(=p4AS4)=4~=einit8= A~=getpid==~>parentpidp=>p>>D p> S~=pipe>=&>AP><-?;> ceevent-?;> s: einitp? =p?Sp?>Dp?S? =~>ekillp@>Dp@S~=atexit@=~>enotepA>DpASpAApAS~= atnotifyA =~> .string-B; > pipe%saB%?pBSpBApBSpB> DpBS~= displaypB =pB0OpB S~= snprintB =aC%? pC SpC"A pC S~= openC =&DA~>mousefdpD>PD<-E; > /mousee-E; > init: ca-E; > n't open-E; > mouse pE =pESpE> DpES~=drawerrorE=-F; > %s/cursoaF%?pFSpFApFSpF> DpFSpF =pF0OpF SF =aG%? pG SpG"A pG SG =&HA~>cursorfdpH>PH<-I; > reinit:-I; > can't o-I; > pen curspI =pISpI> DpISI=pJ@ JA&JAOJb<-K; > or %s/caK%?pKSpKApKSpK> DpKSpK =pK0OpK SK =aL%? pL SpLASL =&MA~?fdpM?PM2<-N; > onseven-N; > ts: can'-N; > t open cpN =pNSpN>  DpNSN=-O;  > onsole%-O;( > s/consctaO%?pOSpOApOSpO>' DpOSpO =pO0OpO SO =-P;0 > l/dev/cpP>2 D pP SpP!A pP SP =pP &QA~?ctlpQ?PQK<-R;8 > onsctle-R;@ > vents: c-R;H > an't ope-R;P > n consctpR =pRSpR>? DpRSR=pR? -S;X > lrawonpS SpS>Z DpSSpSApSSS(=pTA WTVSkeyboardpT >pTAT TA TA&TAOTTpX@ XA&XAOX{pYSpY1ApYSY*=pZA WZrSmousepZ >pZAZ ZA ZA&ZAOZpextract`> Ap` = pjQ&jAOjepipepv> pv S~?ebufavo? pv Spv A pv Sv&=~?npv?&vAUv eof on e-w;h > vent pippw =pwSpw>` DpwSw=&x?AXxnslave&{>P{<&{?AS{ eevents-|;x > : protoc-|; > ol error-|; > : short p| =p|Sp|>r Dp|S|=p|? p}  }A ~>eslavea} >~?sp}?~> Stimer&~  >X~X<&?AX readeve-; > nts: pro-; > tocol er-; > ror: keyp =pSp> DpS=p? & >X<&?AP<-; > boardev-; > ents: pr-; > otocol e-; > rror: mop =pSp> DpS=sp?&rAX useeven-; > ts: prot-; > ocol errp =pSp> DpS=p"? p? / p Qp  ApSap?pSp?/pS$=p? p"? pATpP&AO &eforkslave&>A~@'keyp'@ pA W*AO9& p ?Q@WO or 4eve-; > nts: for-; > k error pApSp> DpS~=(fprint(=p> DpS~=)exits)=p? WApAp  A p >p  A p >p W'<-; > forkev-; > ents: ba-; > d slave -; > assignmep =pSp> DpS=pA> A=p> p p+?&>O& XpSa,?pSpApS~=,write,=WypS~=-close-=pA>p>pS-=pA>pA WP&+?XpSp># DpS~=.postnote.=p? W ApASpAS>~=/emouse/=4A&>AP<-;  > ntdiee-;( > vents: m-;0 > ouse not-;8 > initialp =pSp>' DpS=p> A a > p S~>0ebread0>p p"?  A p S~=1atoi1=~?mp?p"? A p S1=p?p"? !A p S1= Ap?p"? -A p S1=~>logfidp> p?& AO<-;@ > izedb: -;H > %d xy: %p Sp>E DpSp?pSp?p Sp?pS(=p"?pS~=free=~@.retp@a?pA !y~=ekbd=A&>AP<-;P > P event-;X > s: keybo-;` > ard not -;h > initialzp =pSp>S DpS=p> A a > p S0>p s OAsQ  ~?cp?p S=p?~=emoveto=8A-;p > edm%d %~?bufa? p Sp>s D p S~@ ptp @ p Sp @ p  S~= sprint =p p>pSa?pSp S,=~= esetcursor  =\A~@ cp  @ & AX'pS~? cursa ?pSpAS,=WNpSa ?pSpHApS,=~=ereadmouse=AW$Umousefdp > p  Sa |? p  Sp A p  S~=read =&!AP!`.string-,;x> datomou-,;> se: bad p,>zDp,S~=werrstr,=p-A-~@bufp0@r0O&0rAX0x count56 >A~>parentpid56>A~>but$3756>A~>cursorfd56>A56>A~>epipe56>A56>A56>A56>A56>A~>Smouse56>A~>Skeyboard56>AI6fmt.8 1083591583 0 0 664 988 ` ~E.stringp>D p Sp? p Sp? p  Sp? p Sp? p S~=fmtprint=~=Pfmt= Ap@ p  AQpQ A~?pa?pA !y-;> %P %P[%p Sp>D p Sp? p Sp? p  S=-;> d %d]5>AIfont.8 1083591583 0 0 664 14747 ` ~E.stringp>D~?rpp?WDp?~@rrp@pOp?~?widp!A?~@ subfontnamep" @p"AO~? ip#A ?W#fontresizeR>WTcf2subfont> A~@cfp@ p@ p T & AX)p"?p@ p#? pRpR&AXp'0?p'@ p'#? &(AQ(<)~@cp,@w, @ q, Ov- Ts-U - p-R r-P - ~?topp-?v. Ts.U . p. #?p.R r.P . ~?bottomp.?p/@s/U o/ Ov0 T p0 ?~@hp0 @=0?p0@ q0Pp1@r1U o1 Op2Tp2Sp2AS~=flushimage2=~@fp3@ p3P p3 Sp3%A p3 S~=bufimage3=p3?p3@p3@ p30? p3 &4AX4R o> Qp>R>Ao>Qp>R>Ao>Qp>R>A>Ao>Qs?R o? Qs?R?Ao? Qs?R?Ao?!Qs?R?A?Ao?"Qs@R o@ #QsAR oA $QpBABB~>freeupH>ApH@ pM(QpMOpMO&MAXMrLApa@ ~? retpiA ?pjRpj+?~@ depth&k @AQk.string-p;> font -p;> cache re-p;> size fai-p;> led: %r ppAppSpp>DppS~=fprintp=~=abortq=pq@ Wr resize:-;(> init fa-;0> iled: %rpApSp>!DpS=p?pS=p@ W 5>@AIfreesubfont.8 1083591583 0 0 664 632 ` ~E.string-<;> *defaultp<? < A p< S<=~?.safep<?p<? <A p< S<=p<?p<>D p< Sp<? p< Sp<? p< Sp<? p<  Sp<? p< Sp< ? p< S~=allocsubfont<=&=AX= ?p>S>=p??p?S~=free?=p@A@BB-B;> *5B>AIBgetrect.8 1083591584 0 0 664 8789 ` ~Esweep-;>A-;>A-;>A-; >A-; >A-; >A-; >A-; >A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-; >A-;!>?A-;">A-;#>A-;$>A-;%>A-;&>A-;'>A-;*>A-;+>A-;,>@A-;->A-;.>@A-;/>A-;0>@A-;1>A-;2>@A-;3>A-;4>@A-;5>A-;6>AA-;7>A-;8>AA-;9>A-;:>AA-;;>A-;<>AA-;=>rA-;>>@A-;?>8A-;@>@A-;A>A-;B>@A-;C>A-;D>A-;E>A~>brects>A~@rp@@&APDpS~= setcursor =p@ WVfreetmp>A~>tmpp>pS~=freeimage=p>pS=p >p S =p  >p S =~>redp >p S =p Ap >p  >p >p >p > ~>max>A~@ap@ ~@bp@ &  SAX$p%Op%> p%P % ~@rcp%@ %@ &% U%p%Op%> p% P % p% @ %@ &% U%&'>AX'<~=displayp(= p(P p(P p(= p(Q p(Q (  p( Sp(@ (@ p( S(>~?.safep(?a( ?p(Sp(ASp(ASp(?p( Sp(Ap(S(=p)= p) Sa)Sa) ?p)A !))y)~=screenp)= p),P p) Sp)ASp)A p) S~=allocimage)=p)>p*= p* Sa*Sa* ?p*A !**y*p*= p*,P p* Sp*ASp*A p* S*=p*= p*>p+R p+P p+R p+ Q +  p+ Sp+ @ +@ p+ S+>p+?a+ ?p+Sp+ASp+ASp+Ap+ Sp+?p+S+=p,= p, Sa,Sa, ?p,A !,,y,p,= p,,P p, Sp,ASp,A p, S,=p,>p-= p- Sa-Sa- ?p-A !--y-p-= p-,P p- Sp-ASp-A p- S-=p- >a.? p. Sp.ASp.ASp.A p.  Sp.A p. S.=p.= p. Sa.Sa.?p.A !..y.p.= p.,P p. Sp.A p. Sp.A p. S.=p.>&/>AO/<&/>AX/AX/AX/~>.string-1;> getrect:-1;> allocim-1;> age failp1=p1Sp1>Dp1S~=drawerror1=a4Sa4@p4A !44y4~?rectsa4?p4S4>&5@AX5p7Sp7AS~=!ZPp7!=p7Sp7!=p7 S~="draw7"=p7 ? W7<8p:A W:p;Sa;Sa;?p;A !;;y;p;=p;Sp;ASp; ;Aa;?a;Sp;A !;;y;;"=p<=p<Sp< ?<Aa<?a<Sp<A !<<y<p<>p<Sp<ASp<!=p<Sp<!=p< S<"=p< ? W<<<-<;> ed5<>A5<>A5<>HA5<> AI<getsubfont.8 1083591584 0 0 664 1376 ` ~E.string-;> getsubfo-;> nt: can'-;> t open %pApSp>DpSp@pS~=fprint=pAp"Q&"AX" s: %r g-(; > etsubfon-(;(> t: can't-(;0> read %sp(Ap(Sp(>Dp(Sp(@p(S(=p)?p)S~= close) =p* ?**-*;8> : %r 5*>@AI*icossin.8 1083591584 0 0 664 3110 ` ~Esinus-;>A-;>$A-;>6A-;>GA-; >YA-; >kA-;>}A-;>A-;>A-;>A-;>A-;>A-;>A-;>A- ;> A-!; >A-";">+A-#;$><A-$;&>MA-%;(>^A-&;*>oA-';,>A-(;.>A-);0>A-*;2>A-+;4>A-,;6>A--;8>A-.;:>A-/;<>A-0;>>A-1;@>A-2;B>.A-3;D>=A-4;F>KA-5;H>ZA-6;J>hA-7;L>vA-8;N>A-9;P>A-:;R>A-;;T>A-<;V>A-=;X>A->;Z>A-?;\>A-@;^>A-A;`>A-B;b>A-C;d>A-D;f>A-E;h>'A-F;j>2A-G;l><A-H;n>GA-I;p>QA-J;r>[A-K;t>dA-L;v>nA-M;x>wA-N;z>A-O;|>A-P;~>A-Q;>A-R;>A-S;>A-T;>A-U;>A-V;>A-W;>A-X;>A-Y;>A-Z;>A-[;>A-\;>A-];>A-^;>A-_;>A-`;>A-a;>A-b;>A-c;>A-d;>A-e;>A-f;>A-g;>A-h;>A-i;>A-j;>A-k;>A~=icossino=A~@degpt@tpthA :t pt  &u APu < vhA ~?sinsignpwA?pxA pyApzAW(pp Ap>DWp Ap>DWAIicossin2.8 1083591584 0 0 664 5852 ` ~Esinus-;> A-;>A-;>A-;>)A-; >3A-; >=A-;>HA-;>RA-;>\A- ;>fA-!;>pA-";>zA-#;>A-$;>A-%;>A-&; >A-';">A-(;$>A-);&>A-*;(>A-+;*>A-,;,>A--;.>A-.;0>A-/;2>A-0;4>A-1;6> A-2;8>A-3;:>A-4;<>&A-5;>>/A-6;@>8A-7;B>AA-8;D>JA-9;F>RA-:;H>[A-;;J>cA-<;L>lA-=;N>tA->;P>|A-?;R>A-@;T>A-A;V>A-B;X>A-C;Z>A-D;\>A-E;^>A-F;`>A-G;b>A-H;d>A-I;f>A-J;h>A-K;j>A-L;l>A-M;n>A-N;p>A-O;r>A-P;t>A-Q;v>A-R;x>A-S;z>A-T;|>A-U;~>"A-V;>(A-W;>.A-X;>4A-Y;>:A-Z;>@A-[;>FA-\;>KA-];>QA-^;>VA-_;>\A-`;>aA-a;>fA-b;>lA-c;>qA-d;>vA-e;>{A-f;>A-g;>A-h;>A-i;>A-j;>A-k;>A-l;>A-m;>A-n;>A-o;>A-p;>A-q;>A-r;>A-s;>A-t;>A-u;>A-v;>A-w;>A-x;>A-y;>A-z;>A-{;>A~>cosinus-;>A-;>A-;>A-;>A-;>A-; >A-; >A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-; >A-;">A-;$>A-;&>A-;(>A-;*>A-;,>A-;.>A-;0>A-;2>A-;4>A-;6>A-;8>A-;:>A-;<>A-;>>A-;@>A-;B>A-;D>A-;F>A-;H>A-;J>A-;L>A-;N>A-;P>A-;R>A-;T>A-;V>A-;X>A-;Z>A-;\>A-;^>A-;`>A-;b>A-;d>A-;f>A-;h>A-;j>A-;l>A-;n>A-;p>}A-;r>zA-;t>vA-;v>rA-;x>nA-;z>jA-;|>fA-;~>bA-;>^A-;>[A-;>WA-;>SA-;>OA-;>KA-;>GA-;>CA-;>?A-;>;A-;>7A-;>3A-;>/A-;>+A-;>'A-;>$A-;> A-;>A-;>A-;>A-;>A-;> A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A-;>A~=icossin2=0A~@xp@ ~@yp@ & AX<& AU <~@sinpp @pAO~@cospp@pAOWp a >p W<p a >p p  =  A p  ?v Tv T  =  p  A ?:  ?p  p  ?v T   p   ?p =  ?p  @ p Pp  ?v Rv R  =  p  A ?:  ?p  p  ?v R   p   ?p ?=  ?p @ p P 5 >A5 >AI init.8 1083595164 0 0 664 19684 ` ~Edeffontname-;>*A-;>dA-;>eA-;>fA-;>aA-;>uA-;>lA-;>tA-;>*A~>drawshutdown>A~=displayp=&AO .string-3; > imageini-3; > t: can't-3; > open de-3; > fault su-3; > bfont: %p3Ap3Sp3> Dp3S~= fprint3 =W4# r /env/p:>+ D p: Sp:AS~=open:=p: ? &;AU;E<~?fdp<?p<S~?bufa<t? p< Sp<A p< S~=read<=&=AS==<&=AU=>At?a?t?p?@pA?pAS~=closeA=pA ? &H@AXHi<-J;0 > font%d -J;8 > %d 0 %d aJt?pJSpJApJSpJ>5 DpJSsJQpJ SrJQpJSvJQ/JpJSpJ>DpJS~=snprintJ=pL= pL SaLt? pL SpL>D pL S~=buildfontL=pL &MA~=fontpM=XMh<-N;@ > %s imag-N;H > einit: c-N;P > an't ope-N;X > n defaul-N;` > t font: pNApNSpN>D DpNSN =WO" %r imag-T;p > einit: c-T;x > an't ope-T; > n font %pTApTSpT>l DpTSpT@pTST =WU" s: %r %-^; > s/labela^t?p^Sp^Ap^Sp^> Dp^Sp^=p^4Op^ S^=a_t? p_ Sp_AS_=&`AU` %s/winnaakt?pkSpkApkSpk> DpkSpk=pk4Opk Sk=pl= pl Salt? pl S~=screenpl=D pl S~=_screenpl=D pl  S~@refpl@ pl S~= gengetwindowl =&lAPlDpoS~=!atexito!=pqAqq~="initdrawu"=$A-w; > me/devpw> D~?#devpw#?-y; > /dev/dra-y; > w/new#ipy> D py SpyAS~=$accessy$=&yAPy D py Spy> D py SpyA py S~=%bindy%=&yAUy /devim-z; > ageinit:-z; > can't b-z; > ind /dev-z; > /draw: %pzApzSpz> DpzSz =p{A{p}#? p} Sp}@ p} Sp}@ p} Sp}@ p}  Sp}#? p} Sp}A p} S}=}}~=&set_atoi_from_file&=$ApAA p S~='malloc'=&Ap?X r%s/%sp> D p S~@(dirp(@ p S~@)fnamep)@ p S~=*smprint*=&AX buf is :pApSp> DpSp S =p? p S~=-atoi-=~@.resultp.@ pPp?pS=p?pS+=pAp?pS=p?pS+=pA =A~@/winnamep/@ p SpAS=&AU[ %s gen-; > getwindopApSp> DpS =~@winpp@pAOp1@pAOpA-; > w: %r n-; > obordera?pSp> DpS~=strcpy=p? W namedima-; > ge %s fa-;  > iled: %rpApSp> DpSa?pS =p@pAOp1@pAOpAp?p,O&AO image--;0 > >chan !=p>* DpS~= _assert =p? p1@p Op Sp1@ p|P p SpAS~= allocscreen =p @ pQpQ&AX 0nobora? p Sp>; D p SpA p S~= strncmp =&AO<~> .string-;@ > derbordp1@ p4P p Sp>D D p S~?bordersizea? p S&=vp?-;H > ersizeb-;P > ordersiz-;X > e was : -;` > %d for w-;h > indir : pApSp>O DpSp?pSp1@p4Op S~=fprint=a ?pSp? AaSpA !yp?pS~=insetrect=p@ pP p Sp @ pP p SaSa ?pA !yp@ p SpA p S~=_allocwindow=p@ pQpQ&AX %s (*wi-;x > np)->chap>t DpS =pA~=getwindow=A-; > n != 0%-; > s/winnam~?winnamea?pSpApSp> DpSp1@p4Op S=p1@ p Sa? p Sp=D p Sp=D p  Sp@ p S =~=initdisplay=pApPApS~=Pfmtp=DpS~=fmtinstall=pRApS~=Rfmtp=DpS=~@devp@ & AXF D ~@win&@AXJ<-; > e/dev/p> Dp@p @p S=&gAQU devinit-; > display:-; > directo-; > ry name -; > too longp> DpS~=werrstr=pAp@ p S~=strdup=& A~?tp ?X b %s/draw~?bufa ?p Sp > Dp Sp @p S~=sprint =a ? p  Sp "A p  S~=open =p  &AP<-; > /new#ip> D p Sp@ p SpA p S%=&AP initdisp-; > lay: %s:p> DpSa?pS=pAa? p Sp"A p S=p & AP %r%s/d-$; > raw/%d/da$!? p$ S$-=~?%.safep$%?a$?p$Sp$> Dp$Sp$@p$Sp$%?p$ S$=a%? p% Sp%"A p% S%=&&A~?&datafdp&&?P& ata%s/d-(; > raw/%d/ra(!? p( S(-=p(%?a(?p(Sp(> Dp(Sp(@p(Sp(%?p( S(=a)? p) Sp) A p) S)=&*A~?'reffdp*'?P*AX> efreshi-T; > ounit %d-T; > too smapT> DpTSpTQpTST=pT? WU ll%s/laa|?pSpApSp> DpSp4Qp S~= snprint =a|? p SpA p S=p@ &A~? fdp ?U .string-; > belproc-;(> %d wait-;0> ing for -;8> display -;@> lock... ~=getpid=p%?pApSp>$DpSp%?pS~=fprint=pApS~=sleep=W2 draw: %pApSp>IDpSp@pSa?p S=p@pS~=exits=~>doflush>$Ap@ pQpQ  &AQy s: %s f-;X> lushimag-;`> e fail: -;h> d=%p: n=-;p> %d nn=%dpApSp>WDpSp Sp  Sp S=p@ pQ p QpApQ p QpA~=!flushimage!=Ap@ ~@"visible&"@AO~=#bufimage#=A~@$np$@p@ &AU %r bad-;> count i-;> n bufimap>}DpS~=%werrstr%=pApTpT  pT a U & Tp$@p@ &AP ge~=&_screen5&=A~='font5'=A~=(screen5(=A5 =A5 =A5>A5=A~>)deffontname5)> AIkeyboard.8 1083591584 0 0 664 4327 ` ~E.stringp>DpS~=postnote=p@p OpS~=close=p@pOpS=p@pOpS~=free=p@pOpS=p@pS=~>_ioproc>8A~@argp@~? kcp ?-;> killkbdp>DpS~= threadsetname =~= getpid =p ? pP~? npA ?W- prockey-;> board re-;> ad errorpApSp> DpS~=fprint=-; > : %r erp>&DpS~=threadexits=p?   ?W+<~=initkeyboard=ApA p SpA p S~=mallocz=~@filep@ &Ap ?Xv ror/devp>,D p @p S~=strdup=p ? pPp@ p Sp"A p S~=open=p ? pPp@ p S~=strlen= A~?.safep?p? p S~=malloc=p p ?pO&AU<& AO /cons%s~? tp  ?p Sp>6DpSp@pS~=!sprint!=p ? p Sp!A p S=p ? p Qp Q&AP<-;8> ctlinit-;@> keyboard-;H> : can't -;P> open %s:pApSp><DpSp ?pS=W %r rawp Sp>]D p S~="ctlkeyboard"=p ? &AP<-;`> oninitk-;h> eyboard:-;p> can't t-;x> urn on r-;> aw mode -;> on %s: %pApSp>cDpSp S=p ?p OpS=WDp Sp  Sp Ap S~=$proccreate $=p  ?  "=A~@%mp%@ p S=p?~@&kcp&@ p P p Sp%@ p Sp? p S~='write'=-;> r 5>AIline.8 1083591585 0 0 664 3046 ` ~E.string-;> image li-;> ne: %r pApSp>DpS~=fprint=oLAOp@ pP o Op@pOAoQp@pOAoQp@pOAAoQp @ o  Qp @ Ao Qp @ Ao Qp @ A Ao Qp!@ o!  Qp!@!Ao! Qp!@!Ao! Qp!@!A!Ao! Qp" @ o"  Qp" @"Ao"Qp" @"Ao"Qp" @"A"Ao"Qp#@ o# Qp#@#Ao#Qp#@#Ao#Qp#@#A#Ao#Qo$Qp$$Ao$Qp$$Ao$Qp$$A$Ao$Qo%Qp%%Ao%Qp%%Ao%Qp%%A%Ao%Qo& Qp& &Ao&Qp& &Ao&Qp& &A&Ao& Qp'R o' !Qp'R'Ao'"Qp'R'Ao'#Qp'R'A'Ao'$Qp($ @ o( %Qp($ @(Ao(&Qp($ @(Ao('Qp($ @(A(Ao((Qp)( @ o) )Qp)( @)Ao)*Qp)( @)Ao)+Qp)( @)A)Ao),Q)5)>AI)menuhit.8 1083591585 0 0 664 17430 ` ~Emenucolors>4A~=displayp= p SpA p SpA p S~=allocimagemix=~>backp>~?.safea? p SpASpASpA p  SpA p S~=Rect=p= p SaSa?pA !y~=screenp= p,P p SpA p SpDDA p S~=allocimage=~> highp >a? p SpASpASpA p  SpA p S=p= p SaSa?pA !yp= p,P p SpA p Sp̈A p S=p> ~> bordp >& AOB<& >AXC textp >~> htextp  >WMpS =p >pS =p= p|Pp>pPp >pPp >pPp >p|Pp >~>menurect>A~@ip@ ~=fontp= & APn<~@.retp@pSpASpASpA SpAS=vP A= ~@r @vP @ Ap@p@pSaSa@pA !ypApS~=insetrect=~>menusel> Aa@pSaSa@pA !ypApS=~@pp@ p Sp@ p SaSa@pA !y~=ptinrect=&AXpaintitem>HAp@ & AP<~?ra ?p Sa S~@textra @p A !  y p  S >~@mp @ ~@menup @ ~@restorep (@ ~@savep $@ &  AO WpSpASp"?pSp"?p S=~=$.ratholea$=pSp@pSp"?pSp"?p S& #@AO*W+pSp"?pSp"?pSp=pSp ?p S~=%string%=~>&menuscan&>8Ap"@p"Sp"@p"Sa"Sa"@p"A !""y"p" @p"S~@'lastip"$'@p"Sp"Ap" Sp"(@p"$Sp"A(S">~@(mcp# (@p#S~=)readmouse#)=p# (@ W#Vp$$'@ &%A~?+ip%+?O%s<&% O%tp'+? &( AX(W+P,menupaint1,>@Aa5?p5Sa5Sa5@p5A !55y5p5Ap5S5=p5@p5Sa5Sa5?p5A !55y5p5>p5Sp5AS~=-ZPp5-=p5Sp5-=p5 S5=p6A W6p7+? W7<7~>/menuscrollpaint;/>LAp?@p?Sa?S~@0scrollra?0@p?A !??y?p?>p?Sp?ASp?-=p?Sp?-=p? S?=p?@ ~@1nitemp?1@ p@0@p@?pA 0@pA?pB0@B0@=B B:B pB0@ B pB ?pC0@C0@pC  C.@ pC ?=C?C:C pC0@ C pC ?pD? DA&D?PDpFSpF-=pFSpF-=pF S~=borderF=~>menutxtpF> &G AXG<OIZpJSpJASpJ-=pJSpJ-=pJ SJ=J~=menuhitN=ApN= ~>back&W>AXWa<~>menucolorsX>pX= pY  YA~?scaY?pYA !YYyYpZ pZ SpZASpZ  ZAaZSpZA !ZZyZ~=replcliprZ=pZ@ ~? maxwidp[A ?p\AW\zpSpASp-=pSp-=p S!=p?pSaSa?pA !ypApS~>"bordp">pSp-=pSp-=p S=a\ ? p SaSa?pA !ypAS~>#menurect#>p= p SaSa\ ?pA !yp = p,P p SpASpA p S =~?$savep$?a?pSaSa?pA !yp?pS#>a\ ?pSp?pSp?pSp?p Sp?pS~=%addpt%=ad ?pSp\ ?pSp` ?pSpAp S~=&divpt&=p(@pSpd ?pSph ?pS~='moveto'=p?pS~@(menup(@pSaSa?pA !yp?pSp?pS,>p*@ ~@)mcp)@& ?AO3p*@ p)@W6*menuscan*>&Ap?UV-menupaint->p?pSaSa?pA !yp?pSp ?pSp?pS/>p)@pS~=.readmouse.=WW1high51>A~>text5>A~>htext5>A~=.rathole5=A5>A~>menutxt5>A5">AImkfont.8 1083591585 0 0 664 2101 ` ~E.string-;> D p S~=strdup=p? pQpEAQpAQpQ A p S=p? p QpQ = A p S=p? p$QpQ&AO, ic>5=>AI=mouse.8 1083591585 0 0 664 6049 ` ~E.stringp>DpS~@ptp@pSp@p S~=fprint=p@ Aa@pA !y~=closemouse=A~@mcp@ & AX<-;> m%d %dkpApSp$PpSp>DpS~=postnote=p@ W* illread-;> mouse: %pApSp> DpS=pApA~>_ioproc>tA~@argp@~?mcp?-;> r mousep>DpS~=threadsetname=~?onepA?~?ma?pSpASpApS~=memset=~=getpid=p? p$P~?nerrpA?Wr procmou-;(> se: bad -;0> count %d-;8> not 49:pApSp>%DpSp?pS=&?AU %r rea-;H> d errorp>EDpS~=threadexits=Wp /dev/moup>PD p !@p S~="strdup"=p? pPp!@ p Sp"A p S~=#open#=p? pQ-;X> se/dev/pQ&AP[D p S~=$strcmp$=p? &AO mouse#mp >fDp Sp >iDp Sp Ap S~=%bind %=p!!@ p! Sp!"A p! S!#=p!? p!Qp#Q&#AP# /dev/cp+Sp+>nDp+S+*=W+<--;p> ursor/d--;x> ev/cursop-)?p-Sp->vDp-S-*=p.)? p. Sp."A p. S.#=p.? p. Pp/)?p/S/ =p0?~@,ip0,@ p0 (Op1A p1 Sp1AS~=-chancreate1-=p1? p1Pp2A p2 Sp2A p2 S2-=p2? p2Qp3>Dp3Sp3 Sp3Ap3S~=.proccreate3.=p4?44~=/setcursor8/=\A~@0cp80@ &< AX<D<~@1mcp=1@p= Op=S~?cursa=?p=Sp=AS~=write==W=l.string-B;> r5B>AIBnewwindow.8 1083591585 0 0 664 1191 ` ~E.stringp>D p S~=getenv=&AX wsysnew~? bufa ?pSpApSp>DpSp  S~= snprint =W)<-;> %snewa ?pSp> DpS~= strcpy =p ? p  Sp A p  Sp >D p  Sp A p   Sa  ? p  S~= mount  =  - ;> /dev5 >AI openfont.8 1083591585 0 0 664 1317 ` ~Eaddcoord>A~@newxp@ ~@pp@ p  ~@oldx@ p  @A&ARdopoly >,A~@npp  @ &% AX%'<&p'  ='A p' S~=malloc'=~@ppp'@&(AX(/<)~? tp* ?p* ~? up* ?p+A~? oyp+ ?p,A W,9p- ?p-@ p- p.Q~? oxp. ?p/ Sp/ ? p/ Sa/Q p/P p/ S/>p/ ?p/@p/ ? p/ p/ ?a0 Up0Op0 ?W06<~@dstp3@p3Op3S~@opp3$@p3S~=_setdrawop3=p5@ p5P p5 Sp5 ? p5 ? 5  5A p5 S~=bufimage5=~@end1p5@~@radiusp5@~@srcp5@ ~@spp5 @ p5 &6AX6u.string-8;> image po-8;> ly: %r p8Ap8Sp8>Dp8S~=fprint8=9~@cmdp;@ o; Op<@ p<P o< Op<@p<O<Ao<Qp<@p<O<Ao<Qp<@p<O<A<Ao<Qp= @ /= o= Qp= @/==Ao=Q~@end0p>@ o> Qp>@>Ao>Qp>@>Ao> Qp>@>A>Ao> Qo? Qp??Ao? Qp??Ao? Qp??A?Ao?Qo@Qp@@Ao@Qp@@Ao@Qp@@A@Ao@QpAT oA QpATAAoAQpATAAoAQpATAAAAoAQpBR oB QpBRBAoBQpBRBAoBQpBRBABAoBQpCR oC QpCRCAoCQpCRCAoCQpCRCACAoCQpD  DApDSpD ?pDSpD ?D ?pDS~=memmoveD=pE ?pESE=E~=polyI=,ApKpApKSpK@pKSpK@pKSpK@pK SpK @pKSpK@pKSpK@pKSpK@pKSaK@pK SpK ApK$SK>K~=polyopO=,ApQpApQSpQ@pQSpQ@pQSpQ@pQ SpQ @pQSpQ@pQSpQ@pQSpQ@pQSaQ@pQ SpQ$@pQ$SQ>Q~=fillpolyU=,ApWPApWSpW@pWSpW@pWSpW@pW S~@windpW @pWSpWASpWASpW@pWSaW@pW SpW ApW$SW>W~= fillpolyop[ =,Ap]PAp]Sp]@p]Sp]@p]Sp]@p] Sp] @p]Sp]ASp]ASp]@p]Sa]@p] Sp]@p]$S]>]5]>AI]loadimage.8 1083591585 0 0 664 3228 ` ~E~=loadimage =<A~@ip @ pQpO A~?chunkp?aS~@ra@pA !yp  AaSpA !y~=rectinrect=&AX<~>.string-;> loadimag-;> e: bad r-;> ectanglep>DpS~=werrstr=pAaSa@pA !yp@ p(P p S~=bytesperline=p? p p@ @ ~? .safep  ?= ?~@ ndata& @S2<-;> loadima-; > ge: insu-;(> fficientp>DpS=pAp A @W96 datalo-&;8> adimage:-&;@> image t-&;H> oo wide -&;P> for buffp&>6Dp&S&=p'A'p) ~? dyp)  ?=) ~? bplp)  ?p*@ p*P p* Sp* ~? np* ? *A p* S~=bufimage*=p*@ p* ? p* &+AX+`<-,;X> erbufim-,;`> age failp,>[Dp,S,=p-A-o/yAOp0T o0 Op0T0Ao0Qp0T0Ao0Qp0T0A0Ao0Qp1@ o1 Qp1@1Ao1Qp1@1Ao1Qp1@1A1Ao1Qp2@ o2  Qp2@2Ao2 Qp2@2Ao2 Qp2@2A2Ao2 Qp3 @ o3  Qp3 @3Ao3Qp3 @3Ao3Qp3 @3A3Ao3Qp4@ 4  o4 Qp4@ 4 4Ao4Qp4@ 4 4Ao4Qp4@ 4 4A4Ao4Qp5  5Ap5S~@datap5@p5Sp5 ?p5S~=memmove5=p5? p5 ? p5 ? 6  @ 7 @p8 ? 8@W84 ed5<>pAI<readcolmap.8 1083591586 0 0 664 2152 ` ~EgetvalX>A~@pp]@ p]P p] S~?qa]? p] Sp]AS~=strtoul]=p] ^A^ p_ _A_ p`@p`? p` Opa aa~=readcolmape=A~>.string-n;> /dev/dra-n;> w/%d/col~?bufanp?pnSpn>DpnS~@dpn@pnOpnS~= sprintn =aop? po SpoAS~= Bopeno =~? bpo ?&p ?AXp)<-q;> ormaprd-q;> colmap: -q; > can't op-q;(> en color-q;0> map devipq@pqSpq>DpqS~= drawerrorq =W, cerdcol-y;@> map: bad-y;H> index pyApySpy>;DpyS~=fprinty=pz>PDpzS~=exitsz=p|?p|?a}? p} S}>p}? =} A ~@colmapp}@ }  p} Qa~? p~ S~>p~? =~ A p~@ ~  p~ Qa? p S>p? = A p@   p QW* bad5>XAIreadimage.8 1083591586 0 0 664 5071 ` ~E.string-;> compressa? p Sp>D p Sp A p S~=memcmp=p@ &AX<~@dp@ p Sp S~@dolockp@ p S~= creadimage =p Sa? p Sp1A p S=&1AO' ed read-0;> image: b-0;> ad formap0> Dp0S~= werrstr0 =p1A1&3 A~? newp3  ?O3S treadim-6;(> age: bad-6;0> channel-6;8> string p6>"Dp6Sa6?p6S6 =p7A7W7c %sreadi-<;H> mage: ba-<;P> d ldepthp<>CDp<Sp< S< =p=A=~=drawld2chanp? =p??aB? pB S~=atoiB=~?rpB?aC? pC SC=pC?aD? pD SD=pD?aE? pE SE=pE?pF?&F?QFz %dread-G;`> image: b-G;h> ad rectapG>\DpGSG =pHAHpK?~?minypK?pL?~?maxypL?pN? pN S~=chantodepthN=~?.safepNt?aNSaN?pNA !NNyNpNt? pN S~=bytesperlineN=~?lpN?&O@AOO nglerea-^;x> dimage: -^;> image to-^;> o wide f-^;> or buffep^>uDp^S^ =W_ rreadim-d;> age: rea-d;> d count -d;> %d not %pd>DpdSpd Spd Sd =We<&f@AOf d: %r5|>AI|readsubfont.8 1083591586 0 0 664 2632 ` ~E .string-; > rdsubfon-; > file: he-; > ader rea-; > d error:p> DpS~= werrstr =p A a"? p" S~= atoi" =p# ~?np#?=#A #A p# S~=malloc#=&$AX$1 %rrdsu-';( > bfonfile-';0 > : fontch-';8 > ar read -';@ > error: %p'>$ Dp'S' =W(D r5@ >PAI@rectclip.8 1083591586 0 0 664 1019 ` ~EAW>7&>AP><~?ip??p?S~=cmap2rgb?=p??p? @A @Ap@ pA AA AApApB  BA pC ~@crC@pC  C@ ~?.safepC ?=C?pC?pC ~@cbC@pC  C@ pC ?=C?pC pC? C pC?pC~@ cgC @pC C @ pC ?=C?pC pC? C &D?PD6@p>Sp>Sp>@p>Sp> @p> Sp>@p>Sp>@p>Sp>@p>Sp>@p>Sp>A Sp> @p>$Sp>$@p>(S >Aa>,Sp>A !>>y>p>A<Sp> =p>@Sp> =p>DSp>( @p>HS> =>>B =ApB@ &L @AXL8<~>.stringpM>DpM @~?sptrpNA?WN:DpR$@~?rptrpSA?WSB stri-c;> ng: %r pcApcSpc>DpcS~= fprintc =WdD<&f AOfAIstringbg.8 1083591587 0 0 664 4674 ` ~Ep< @ wBT'B?MB.stringp>Dp@~?sptrpAX?W Dp@~?rptrpA`?W unna-0;> med fontp0>D -1;> stringw-1;> idth: ba-1; > d charac-1;(> ter set -1;0> for rune-1;8> 0x%.4ux-1;@> in %s p1Ap1Sp1>Dp1Sw1f?p1Sp1  S~=fprint1=p2 ?2&4\?AO4u S~=agefont>=p> @ p>@ p? ? ? ?p@?@ W@HAIfsubfont.8 1083591587 0 0 664 1163 ` ~E.string-;> height !p>DpS~=_assert=pA p S~=malloc=~@namep@ p &AX = 0 5">AI"subfontcache.8 1083591587 0 0 664 1128 ` ~E.string-;> *default~@namep@ p Sp>D p S~=strcmp=~>lastnamep> &AX <~@dp@pO& AOp"S~= free" =p#@ p# S~= strdup# =p#>~@ subfontp$ @p$=$~= uninstallsubfont( =Ap* @&*=X*2p,A=,-,;> *5,>A5,=A5,>AI,subfontname.8 1083591587 0 0 664 1880 ` ~E.string-;> *default~@cfnamep@ p Sp>D p S~=strcmp=p@ ~@maxdepthp@ &AX DpS~@ fnamep @p S~= snprint =ax? p Sp/A p S~= utfrrune =&AO$ DpS~= strcpy =- ;> *%s.%~? tmp1a  ?p Sp @Ap Sp >Dp Sa x?p  Sp ?p S  =p @ a! ?p!?&$ AS$: s/%s%s.a)x?p)Sp)@Ap)Sp)>Dp)Sp)?p) S~?ip) t?p) S) =a*x? p* Sp*A p* S~=access*=p*t? &*AX*X %d52> AI2unloadimage.8 1083591588 0 0 664 3158 ` ~E.string-;> unloadim-;> age: bad-;> rectangp>DpS~=werrstr=pAaSa@pA !yp@ p(P p S~=bytesperline=p@ @ ~?.safep ?~? bplp ?=?~@ ndata& @P,<-;> leunloa-; > dimage: -;(> buffer t-;0> oo smallp>DpS=pAp@pO ~? dp  ?p SpAS~= flushimage =~? ntotpA ?W:6 unloadi-#;@> mage: %rp#>9Dp#S#=p$A$p&@A&:& ?p& &'AQ'R<-(;H> unloadi-(;P> mage: im-(;X> age too p(>IDp(S(=p)A)p+@+@&+ S+X wide5;>hAI;window.8 1083591588 0 0 664 8273 ` ~E.string-;> allocscr-;> een: ima-;> ge and f-;> ill on d-; > ifferent-;(> displayp>DpS~=werrstr=pApA p S~=malloc=p &A~?sp?X screenidC( >p( > o)AAQo* Qp* *Ao*Qp* *Ao*Qp* ~? idp*  ?*A*Ao*Qp+U o+ Qp+U+Ao+Qp+U+Ao+Qp+U+A+Ao+Qp,T o,  Qp,T,Ao, Qp,T,Ao, Qp,T,A,Ao, Q~@publicp-@ o-  Qp.? p. Sp.AS~=flushimage.=p. ? p.? &.AO._ ss->ima-4;8> ge && s--4;@> >image->-4;H> chan != p4>2Dp4S~=_assert4=p4? p6@ p6  Qp7 77~=publicscreen<=Ap@A p@ S@=&AApA?XAtopbottom> A~@ wp @~@!np!@ & AP+ 0top/bo-;X> ttom: ri-;`> diculous-;h> number -;p> of windopApSp>RDpS~="fprint"=& AX.<pVpOpO A~?#.safepA#?:#?& S8 ws top/-;> bottom: -;> windows -;> not on s-;> ame screpApSp>|DpS"=W<<& AXO<pSp  A A p S =p @potAO~@$topp$@ o Op!@ o Op!@AoUpA Wd~=&topwindow&=Ap @p4O&AX<a @pSpApSpApS>~='bottomnwindows'=Ap @pSp!@pSpAS>~=(topnwindows(=Ap @pSp!@pSpApS>~=)originwindow)=0Ap @pOpSpAS=p @ pP p SpA p S =p @ p &AX en 5 >A5>AIwritecolmap.8 1083591588 0 0 664 1952 ` ~E.string-;> /dev/dra-;> w/%d/col~?bufa?pSp>DpS~@dp@pOpS~=sprint=a? p SpA p S~=open=&A~?fdp?P<-;> ormapwr-;> colmap: -; > open col-;(> ormap fap@pSp>DpS~=drawerror=p A p S~= malloc =~@ mp @ ~? tp ?~? npA ?pA W! iled%d -#;8> %lud %lu-#;@> d %lud p# ? p# ? a# R p# Sp#>5D p# Sp#A #  ~?ip# ?p# Sp# ? p#  Sp#Sp#S#=p# @ p#? # ?W# wrcolmap-);P> : bad wrp)@p)Sp)>HDp)S)=)-);X> ite5)>`AI)writeimage.8 1083591588 0 0 664 8301 ` ~E<&7 ?AX7?.string-M;> compress-M;> ed %11s -M;> %11d %11-M;> d %11d %~?cbufaM? pM SpM@ pM,P pM S~=chantostrM=pM?~?hdraM?pMSpM>DpMSpM?pMSpM?pM SpM?pMSpM?pMSpM ?pMS~=sprintM=~@fdpN@ pN SaN? pN SpNGA pN S~=writeN=&NGAON 11d %11-;(> d %11ld a?pSp>%DpSp ?pSp ?p  S=p@pSa?pSpApS=p@pSp ?pSp?pS=p ?p?W 5>8AIwritesubfont.8 1083591588 0 0 664 1772 ` ~Epackinfo>A~@np@~@pp@ ~@fcp@ pA W .string-%;> %11d %11-%;> d %11d ~?hdra%?p%Sp%>Dp%Sv%Pp%Ss%Pp% Sr%Pp%S~= sprint% =~@ fdp& @ p& Sa&? p& Sp&$A p& S~= write& =&&$AO&8 writesub-(;> font: ba-(; > d write:p(>Dp(S~= werrstr( =p)A)p+@v+O=+A +A~? nbp, ?p,S~=malloc,=p,@ p, &-AX-Ep0 @ p0 Sp0? p0 Sp0 ? p0 S0 =&0 ?O0V %r53>0AI3cd nutz rc drwbind cd /sys/src/libdraw mk syms vc -aa $CFLAGS *.c >>syms syms vc -aa $CFLAGS *.c >>syms # mk mkme # Put<p`;D$$IÁD$ES$LD$0DŽ$8=u u$Ht-$Lc $dk $ds $dDx $dt4$<$D$D$P$<$D"=Pu P$8t$8$i$hRD$] $ L$GR t7D$8$D$L$,D$8$D$D$OR$D$D$0Ro$D$b$ L$ hRL$L$ D$L$Sq}#$D$.Ԑ$(| @ @<<$[<$N $td<S `|9q $D$< $D$ $D$x $D$ $D$d $D$I$ $D$. $D$ $L$=t =<P$D$, $'$ L$Fi u)$D$,4$&  $m|u#$:D$T,_$|&`h $ L$|9 $D$$D$ D$m $!L$h3!_$80$L$D$Sf|$D$؀$D$ D$x["$D$ D$Z($D$ D$<-/$D$ D$K0$D$ D$#$D$$$Dø$5 |$L|$0}$HD$0|$0uD$01҉$@;T$0}A $D$L$gN$@u B$@;T$0| @D$4$Hu$oD$>1;$H/$@$L $/L$ $@ƒtA$L$$@u$D$4$$LD$L$@A낉ؙD$|$;}ʼnD$ؙD$|$D$$$LD$$@른r$落$@$HD$|$D$0|$0TD$0G==x==$|=oa@$LC-u CfD$"t*L$" $\$(\$\$($LÉ\$(u$HD$"cFt[atGbt3$D$']$!\$($LiZKݻ\$(MtlR=lRt!Jt$H$L1˹L$( t0؃H$o \$($LD$0(Jt$H$L1뱃ftUlݻ\$(Mt$8eJt$H$L1ʉݻ\$(MthR=hRJt$H$L1$H8$< $D$D$L$ 7$4$@ $D$$@$D$D$D$4D$D$0D$ D$,D$D$蒹D$$$D$D$D$L$4D$L$,HK$4$D$$D$D$(D$$4$D$$@D$D$ u~$4ǀƁ $$4$p$4ȃ$AD$AD$q8Ã=XuL$$ $L$ 1Ƀ<8t^L$ 8 ${D$T$ 8 $L$$L$L$L$ L$uD$$$L$A<8u$D$$D$#E1ËL$$ $L$ XX;GX$D$T# t,$L$D$D$SD$HuԸ $ Fà $ $D$  $L$L$ L$IJ\$|($xD$$$D$D$밃 ÃhL$\ $E} ϑ$cOD$ $D$D$D$D$ 9   $L$L$ED$d}B $ E  $L$L$YED$d} $ȶD$$ D$D$\D$L$ $D$D$d$D$D$D$D$"ED$d$DD$$D$D$`D$]L$ $ L$D} $)D$`$DD$\$D$D$ D$hÃ4$  $zD$u$1L$$L$Ã$|D$"ĪتD$T>$ $lu幔 $L$"L$5|$5P\$TƒtT$tt $sT$t tYD$ | $L$"L$?|$D$t $D$@띋=D$"=D$"=D$"=D$$\ Tt tCL$$KD$gL$$L$D$$|$D$s닃PM$ ADHLAX \`lD$4ptu t$E趵$ F|$D$rD $ tu럋L$4I $jL$tu]L$4I $oL$[tQD$ $vL$=t7L$ $L$#u D$4$D$4$D$4$ 5 |$$󥸔$D $L$(L$L$,L$ L ‰D$L9t u$DD$8t߃|$$tD$D|$$2D$Du5L$( $L$,L$ցƌ|$葕T$LL$( $L$,L$ցƌ|$\\$8T$DL$L1u?$D$T$DL$LuՋ$D$T$wFL$Lt뗃|$$t$MD$\$8L$Lǁf$L$T$LD$$$RL$L|$8tD$8$IL$L 4 \t t9t|$8D$8$O֋D$$tV$L$@L$L$ $AxD$A|D$D$ x$$D$D$v$@t)$@$ǒD$$@D$̭|$D$h$@C$$@$DŽ$$ $o$$$$I $$IIL$$L$$L$ nuS $$ L$L$B} $D$$D$辬DŽ$$@$$R$u $D$$D$j$$$9$D$$D$)$$[|$D$g$:$ϒ$xD$D$ $T$tuD$D$D$щT$|$t&D$D$x$D$D$령( $衮D$ $D$L$D$L$$D$D$ D$댃ߒ$$$D$$$D$D$Xƒ l$0\$$1҃u,hRl$0uH1;t ,$ډ\$d I L$l$0\$d|$(tZT$$T$$<t$8T$$D$hR$T$D$0L$$hR|$,tGB@T$$V$L$A@  $-VT$T$$D$ C;t | $l$GD$u#$D$D$0D$ 1 ù $xD$L$H d $ tL$8T$ttȉ dL$ $o|$5t-duA19};9D$0}D$0Nl$4|$H󥋃9D$0jK L$,L$T $|$t$HHD$D$D$D$ |W\$4|$8t$HL$h $L$,R)ыSR)уL$l$4$D$$L[\$ d$ D$$ʉT$讇D$DL$4 $|$t$8D$D$}l$d< $|$t$|HD$D$D$D$ A$$|$t$|󥋄$D$|& $|$t$|HD$D$D$D$ IAD$4$|$t$|󥋄$D$K|| $$0L$qD$(L$( $D$$D$@D$ |$ D$$L$qL$@L$,L$, $L$HL$Yql$P$09}$0șD$,|$,u+D$@D$<$9\$ht$`$9}<u\$h)|<uF벹 $\$(ډ\$dD$$|$$D$(L$pD$,L$< $L$,L$rpt$`T$PL$d)L$h)D$<딹 $\$(ډ\$dD$$|$$D$(L$3pD$,L$< $L$,L$ pt$`T$PL$d)L$h)D$<C9É\$hZm$~i$\$h$M\(r $pD$ 5D$D|$4;D$Du $3;D$Dt $D$l$d\$`t,t $u\1ҋ9‰T$H ȋ95|$<L$<+L$4 $F}L$@+L$8 $1pL$@+L$8 $\$` }D$49D$< $L$t$@l$DD$81ۉ\$0tw|$8|L$8 BE{t?|$8}O$ED$5t$@l$D4$l$L$8 HЃ<ÍD$ $\$L$DT$@t2D$ $D$|$t$ D$&L$DT$@Y\$DtM9D$$v${4\$DT$@L$ JxL$$J|$\$T$@D$DX \$Du<ËUu E  T$4t "EtWD$ $\$$D$4D$|$t$ D$%l$Dt$@s$ЉT$|$5D$%l$D뷋B XE K8t$@l$D\$0T$4B B .L$8 BB * 8t$@l$D\$0‰ED$4.L$8 B*\$0t$@hBDu B! $2T$D L$ \$,txt Ë  $ L$c H$D$(D$D$,D$訕D$, fA1$D$$D$D$(D$\$ F1ømÃ|$$u1ËL$ $L$$L$L$$L$a‰D$  $L$D$D$ D$T$D$8Ã|$(~T$0$L$(L$L$(L$<`D$,fL$0 $諪L$DŽ$$$$D$$D$D$ $D$V$$=$$$t,$ЗD$$D$UÃ$tꃼ$t$ǀ͸1h-(L$0AuOD$T$T$L$, $T$L$#$oD$,$](ËQD$;t$D$T1ËL$ II $u $&L$ $D$ L$ L$ DL$p\$ T$Kvu:1ȃt% Jx J|$KL$ø1ǃL$ $L$L$ L$ Ã$ $WD$;}o )с ~ D$($T$ T$D$D$L$L$ D$4$D$0D$D$D$D$D$ :T$ D$;|D$$迫$ÃL$;D$$ue;D$~Y;D$ |>ȉ $|$t$D$$D$D$:ø!$^M $D$D$:D$$|$t$볃8D$,$D$<D$D$ $D$@@D$|$t$,D$L$L$L$|$| D$9D$w|$ t$ø$r݃})L$D$D$뷃~L$D$D$뤃,L$Dl$4t$@t$ t$Mt]|$<tVD$ $#l$,$ExD$E|D$D$$D$$$J AptʼnL$ $l$\$tǃ1ҋ9}ȋƀB׃D$ L$ $D$D$ZD$+$D$$D$D$IY$=IÃL$$uÉ= v$3D$8-øD$D$ `t $ML$$ $z0`$D$ D$D$$D$bt$-`\$$T$ Z t Cf fYZ-,H=u- $D$R$ҫ$D$ D$N  $Xt1t!$DD$L$L$+L$ $$D$ËH(,  f9uÉЃ,Ë,  f9uÃ,,;`s N$%ÃD$ D$|$~ -tK0|994Rƒ0|9~D$l$ ËD$ k ЃЉD$ ̋D$D$릃 t tD$| 7L$ȃÃT$Bt\tUB 9uHJ $ dL$@.T$BJȉ$D$dD$_D$d $-T$BBك\$T$Jȉ$JȃD$B)D$_D$HÃD$$\$T$Kȃ$KȉD$C)D$B_T$D$JȋL$BÃD$ @$老D$ $tà $,L$ HD$ H $,T$ BD$JfЃÃD$ @$D$ $à $2, $$D$L$L$D$ÃL$ $LP$PD$L$L$D$à $+$D$L$L$mD$Ã=$~( $$($/=$؃=~( $=؃=P~& PPP$=Pڃà L$\ta|z~A|Z~ 0|9V$L$ à th$L$Và T$@J9|B J $JL$*T$BJȋL$fJf Ã~;D$u Nà t\u0D$$ L$ u\D$$L$D뙃nu ;L$t܃|$ st\tЋD$$\D$L$뵃$D$ tR tMQƒ~L$ $T$T$ElT$t tD$ËD$$T$D$ D$$D$ D$}녃($D$$ D$D$D$ T$$D$~ u^D$$ D$\$ T$$|$}Ѓ(ÉJ.uJ@ uJf@D$$D$v%D$D$ D$ $:D$$$D$D$aD$;D$uGT$$]\$ 1҉f<<t( <9uЃBf<<uظÃ@1D$D$ D$1fD$$fD$&D$Qu1@Mu1@fD$(D$(cuZdL$( $Bƒ_D$( u‰Ǎt$Ѓ@É <‰D$4@u |$BtJ $#T$4fD$$BxXkrD$8 `W ${L$8 $8T$4D$su`$JD$D$$D$8D$sD$0T$4;D$8u$T$4guT$4fD$&Bt T$4D$BtX u#D$D$L$4IfH~L$D $D$e$_SBt^D$t1`T$d\$hl$l9݉\$\B|Jx9v Bx99ʼnl$Xs8$l$jD$‹D$\L$DЃHËL$$ $L$(L$ T$D$$|$PtL$L $L$L$L$ L$‹D$XL$$D$\L$(ЃHËAxD$A|D$ (D$8L$, $L$0L$L$4L$L$$L$ L$ L$@D$|$$u1(ËL$$ $L$ L$ L$8D$$$2aD$(à L$$ $D$L$趒t1$D$D$L$ D$D$$$ Ã$D$(D$$ht$(l$1ۋ9}qȋȃv9$tD$D$D$ $C9|$D$D$l$Ã$ÃT$totd|$ u~%$D$D$蛃Ë $D$'T$t뺃ÃL$ t$蘉ÃT$ |$tË $7Ã$T$( $L$4L$D$u$ËD$($D$4D$D$ D$ ;}eL$ D$1ۋD$9}4؉\$L$ȋD$(9u#CD$9|D$ D$ ;|$ÉT$$L$4L$\$;D$uD$$D$4D$V\$륃$Ã8D$@$D$D$D$0D$ D$(D$|$Ht$<1|$0|$(|$(  $L$ 8D$D$ |$(~D$D/|$L$ T$( $tD$0D$0$D$D$D$ D$:D$$]L$ T$0J$D$DD$D$(D$v:D$ D$(L$0 $L$(L$ND$4D$0$\L$4 $89L$4u $\1ɉȃ8ËL$( $D$0D$0$D$DD$D$(D$9낍D$$t$D$D$ d|$ D$.D$$\D$D$ |$LC1;\$(}T$0Z/u 1t$@D$0D$D|$( Cʃ$\$(T$,D$tuڃtN$)uD$D$T$,D$ |$tD$,$$Ë $D$T$,L$L$ y$D$,,D$뒃$Ë9 D$Mt ,$L$\$(T$,DT$H|$Ttu7v$ $L$0"T$HuDËD$$D$L$L$PL$L$\L$L$`L$ D$ƒu$MuD$TDËD$L~_T$@$HƒtJu61D$($PY|$(t$[uD$D$@D$DøʼnT$@$L$4L$T$LD$<1ɋ9}HȉL$8 ȋL$ $D$ $]T$LL$8A9|L$< $L$4L$ L$ L$ D$L$D$D$@D$L$,L$ /\$L|$,ǀ11ҋ9}. ȋ݈B9|ҋD$@$WD$<$WD$$$T$Lǀ1ɋ9}iȉL$8 ȋ$@D$L$ @D$D$ $T$LL$8A9|DËǀDÃLD$PD$ L$\ $L$`L$D$8$اT$PD$(t9L$\ $L$`L$L$L$ \$PT$(L$8 $L$L$4D$0}E$uD$D$8D$9D$($nVD$\$bVD$8$VVLù $D$@ $rD$Tl$ T$PD$$ÉׁnjރK\$(K ǂƅǂ1ۋ9}Nȋ<ȋC9|D$<$SD$@$xSD$($lSD$\$`SD$8$TSD$0$D$ $]Lǂƅǂ.L$(I@/'J9u:ށƌ<$փ|$>\$PT$(؋J߁njփJ t"$uD$D$8D$!$uD$D$8D$J8 tuD$ B8D$u䋃@J 9IO0L$4t u0ËuꋉD$ $L$l$0\$,T$(L$$T$49` $fLT$DL$,|$Lts$BxD$L$rT$Dt$趥D$D$<T$DtD$@9u<Ë$e$L$L$T$D뎹Mt ,$L$T$D<ËL$D$ )$T$@L$(t u$ËD$,~$D$D$8`|$H$D$D$D$ Z>H $L$`I L$5uD$,1D$L$@D$PD$L0L$tT$D) $D$PD$P$D$pD$D$tL$D)D$!L$P $L$t+L$DL$xvL$L$ urL$P $L$t+L$DL$vL$L$ WuCD$H$$@D$PD$HD$t+D$DD$@L$T $L$DL$L$tL$xD$L$?D$PD$LǃXÃ4L$8D$(D$ D$@$D$D$D$,D$ D$D$LT$L|$,t$D$ D$($D$,D$D$D$mT$LT$L$L$PL$L$$L$pD$0;D$Lu{|$ uD$(@u4ËD$(@I|:$vD$ȉL$ L$(DȋD$L$ I}$vD$f뛋L$PT$$) $D$,D$,$D$LD$D$PL$$)D$D$ D$($D$,D$D$P+D$$D$eL$0 $L$$L$L$PL$34ÃT$} ËL$ $vL$T$2u($vD$ËL$ $vL$L$L$2u($vD$>ËL$ $vL$L$L$D2u 18L$<TED$,D$ D$D$D$D$D$0D$ D$$D$|$0t |$$L$P $L$TL$L$(L$l$P\$,T$ 9t,$L$T+L$(L$|\$,ƒ|t`vtvt=$vD$D$D$ L$8$vD$L$L$0 $L$$L$\$,R8ÃqD$$$=wD$L$@D$$$D$e$f$l$$$(ypw$D$e$D$e$t$$$|tw$$|D$}p$d $U==1҉$4D$$wD$D$$4  $@‹$4D $$D$$8T$$8$4$4D $$wD$D$$4w $L$L$d$0$D$$D$$4D$d$0$Yd$x$d $-D$$x $D$D$L$ $tU$P$wD$$dD$$xD$ c>$$43$P$d$tP $p$wD$xD$xD$ $dD$D$$p$D$$LĔDŽ$<$d$N $$hT$($h$@$N tR$N tE$N w $$NL$O $h^DŽ$<$@d$<1퉬$D1ۉ$TDŽ$<$d$N $$hT$'$D$T$h$@$N $N $<uGDŽ$<$L$c1$D$hÉE$D؉$T$@B$L$1$D‰CЋ$x$tP$p$D$$TT$$T /.u A/tHD$$$wD$T$D$ l;$p$D$$D$$TD$5$TD$$$wD$D$(;$p$D$$D$$TD$DŽ$<|t <tr~VD$$$ZwD$L$:D$$$D$_$`w$D$_ND$$$owD$k:묍D$$$wD$L$P:둸Pw$D$u_DŽ$\DŽ$(=N$(6 $^($W_w$D$_$5_ $D$^$D$^V>  Bx$L$ D$AD$ $.L$ $^T$tB$-L$t"<$L$kD$$ËAt$D$L$A$-D$@ $-D$$-릃 |$(u ù( $D$ $D$ $D$T$L$$ L$(JL$,JL$0J L$4JL$8JL$4ÃL$~ $ȃ$D$fL$$(L$ $T$$D$D$D$D$T$L$ǂÃ(T$0L$,fD$ffD$D$D$ D$$t&$D$D$D$ =T$0$D$D$D$D$ =(ÃD$~ Q$L$ $L$L$L$ L$L$$L$ BÃ,L$0Hu1,É $D$D$D$D$ BD$ ,ÃLL$P|$Tȃ$D$ ȃHD$D$$ $OT$ D$8 $L$(L$D$D$H)D$(1;T$HL$H)с v D$ $D$(ЉT$4D$D$8D$L$,L$ /AD$P$D$DD$4D$D$8D$D$,D$ >;\$PT$41ɋ9sTȉL$0ȋ$D$DЉD$D$8D$D$,D$ D$Ǔ\$PT$4L$0A9rD$,;T$H#D$XL$DD$\L$DL$HrL$@$\$ZT$PL$>D$H)D$($$T$P|$HuCǂD$ $D$(D$D$D$HD$ ?D$PL$HL$H $xT$P뭋L$@$\$D$DD$D$DD$HD$ .T$PL$>$D$DD$D$DD$HD$$XƒuAD$$D$8,D$$$D$D$,D$ $XT$8D$4T$8/t CT$8$D$D$8$D$D$D$PD$D$HD$ D$DD$D$pD$4$D$<$D$D$D$L$PD$L$DH$P$D$D$D$4D$D$2D$D$D$xD$D$TD$l$ \$DT$H@L$x 9H9tAL$x $ى\$pAL$HfD$(Ã=t $KL$ $|$u=t HIJ$T$ÃL$ u LLutL=t$L$ t $L$ tuÁtu $VL$ 뤸k$L$ ` rk$u L$ L$J Ã=r k$8L$ = |DDËDD TL$X =Dw*t6D$$kD$L$D$$  Tøk$=w l$sDÃ$|$(t@;D$(}$=o==tr=t$l$$D$$\$ƒ=t =wuB H$CD$Qщډڹ| $D$| $D$  $‹D$@PD$ @T$P $L$ HL$ H$D$D$( $'D$  $P‹ D HD$ @T$P $( D HL$ H$D$D$%$ù* $D$ $\$‹CP J$T$=td=X? $gD$ $D$ $l$\$‰CMKEP$T${+ $D$ $7\$‹CP J$T$9q \$=wt'S=u R=tS =wuك ËD$LÃу .btY)4$CD$D$B|$t$0l$,D$4X\$4===tr=t,=wn$|$t$0l$,K $|$L$;|$t$0l$,\$4NK $|$D$|$t$0l$,\$4R D[C| KΉh[C| KΉh[t|$@t+L$@ $IL$J|$t$0l$,\$4 t|$DT$Dj@ lD$H;D$LT$DjljD$=DL$D $l$,D$ [L$=D| ;D}΍=DË;DuA;H8D$4D$$D$t$@D$=s D$1((Dt$(D$D$t;t-t|$D5D=D| 811|$|$t |$$oL$4i, (t$4D$4i,(D$$D$ D$$=D}s|$t;t$@vfD$ D$ =|$$plD$x{D(1)\ $D$\D$|$ t$(͉l$,EÃ2\$0= ===tY=tK9u=D$$D$$=A$CD$D$|$t$(l$,Xh uD$ D$ =,$CD$D$|$t$(l$,D$0X\$0=5==t|=t6=w;M1)ʉUu$|$t$(l$,SJK $|$L$|$t$(l$,\$0 K $|$D$}|$t$(l$,\$0D$<9L$< $t$F|$t$(l$,\$0 [C| Kʉp[C| Kʉp[ tNNt$(|$tW;t$@wO|$$L$< $IL$Et$(ljD$|$|$$;|$딃=D~릃=D+1((D$<l$ =D| E;H~,1ۃ }!ڋ@DډHC |߃ËE;HuE;D|D $|$t$L|@|D$D$D$D$ GT$H׃t$Lǂǂ|$4t$L@@D$8D$@Ѓ $D$0D$D$($D$D$D$ D$$D$0$D$(D$|$t$4D$$D$D$.T$0ǂL$HǂǂD$@D$8D$@ $|$t$4|D$D$D$D$ SFD$0$D$(|D$D$ D$ =L$0 $D$D$\DÃTt$X1|$DCPD$HD$`;D$D}1ҋ9‰T$,}ȋ(E9D$`\?9}BT$,l$0|$DD$L+D$Dd}1TË $|$t$D|@|D$D$D$D$ E|$4t$DL$` $L$LΉL$uD$D$8D$TD$,$|$t$Lt$d|$LD$8D$LD$LD$TD$T $|$t$L|D$D$D$D$ =L$dD$TD$LAD$Tȉ $|$t$LbD$d$\Ã\B9‰T$Hd|$gT$H@l$H\$D1ҋ9‰T$,ȋ9|$0|$Lt,$\$DT$,ȉ$ȃD$)D$臢T$D $L$5 -|\$,T$DuA4$|$t$0E|D$D$D$D$ #<@Ë9u~ȋHL$HD$0BD$84$|$t$0E|D$D$D$D$ ;D$H$|$t$0@ÉȋL$HAD$8B9‰T$,Jv|$el$H\$DT$,C(T$,1ɋ9}[ȉL$$ȋ֋L$0 $L$4L$ЉT$ |$NJT$,L$$tD$ (A9|1(Ã$L$, $L$0L$t$(|$IT$(t Ѓ $É$L$,L$L$0L$5t$L$,L$L$0L$D$1$Ã(|mD$,$=T$ tXu1L$4 $L$8L$ցƌ|$GIT$ uu%$D$0D$=D$,$D$ (ÉL$$ $KD$xD$$$D$ D$D$0D$}D$$$Gx묋L$, $L$4L$L$8L$(\$D$1ҋ9}2щT$ ы $-\$T$ !D$B9|΋D$ÃpD$tupù $ilT$x DD$HuCu%$|D$hD$H$pÉ$|D$L$T$HT$x$L$L$KD$d}$|D$D$xD$Ph둹8 $kD$L$D$dD$D$聊 $kD$3Ѓ=$DŽ$у $Һ$$ $t{$$@D$tDŽ$t$$L$\D$t$A $h$$ $O$$ $6D$d$% $ D$`$1 $ $$$Ń' 9~ȋK S)щL$4l$4D$4d|$4S‰\$pC9C 9‰$|0$D$D$T$ ȉL$<$h$ $$L$~$M$ $y$@< $$L$[ʼnD$|DŽ$1;$}!Z/u$Z W$u&D$<$l$$\$kl$|$$;$}Z|D$<$mD$<$$AD$T$|ZD$$)HD$ D$D$|$t$|l$<$$D$4D$0$$}D$D$4D$D$0D$ ޻$ $AL$L$1$}$}D$;X8 $[$$$D$D$cz1ɉ$;$$ ${$f u$u+${$$$$t$D$w$A$;$w$$z$$芯D$<$D$$D$D$ /$$\$<ǀ1ҋ9}3ȋƀB9|͉$sl$<|$ttR$D$D$D$ D$D$tD$$D$iD$t$蔮l$<9D$dw9D$`v}1D$`D$d$D$dD$D$`D$D$ W9T$<0 $NL$VD$4L$<L $L$4L$iVL$<0\D$d;D$`vt$^@+^@-$D$D$D$ D$D$D$Sl$$=$DŽ$$nt>$ $$L$$ $$L$$$ $ tD$@j} $$L$8UD$|$ $$L$Q$ $ $s@D$4L$4 $"W$$ $esD$4$$$D$D$4D$;$$D$D$|D$$D$ D$D$D$D$}$݇D$4L$4 ${V$$}D$DD$;$ $$L$TD$|$$$$L$SD$|$\r$JЃI$у> ${$ȋxȋ؉|$l|$Pމ\$h|$@T$XЉ$D$@D$X+D$P2}D$H+D$@2lD$($D$PD$D$TD$D$HD$ D$LD$. $|$t$(|@|D$D$D$D$ X!D$l$|$t$P$D$h$|$t$@$D$@D$H $|$t$@|D$D$D$D$ $$$9B$D$T$$"$$5 |$v $ $S$D$4 $D$u $u1D$D$4D$ 蔥1;}a\$1ɋ9}?ȉL$T$ȋ$D$$D$D$ Ћ\$T$L$A9|C;|à \$@|$t$(l$+l$u|$$t$ Áv l$8 l$< |$8vd$81D$D$<9s+L$ D$$袜D$$D$oL$$L$SpL$Ap$vL$49u 4T9u T\9u \9u 9u  Ã$D$(D$,\$ K $D$HL$1DL$ $L$ L$L$yt$ËD$ @L$I)ȃ$Ã@\$D~@ËK $bL$D$, P $L$Cd$,L$DfAXl$DmXl$(11;t$L}MT$HXؙ);L$,ؙؙt ؙ)9~F;t$L||$0`D$LHD$ 11;L$ L$<;t$L}Qt$8T$H D$D$|$|$D$L$AD$ o|$\$(L$ANjD$8D$ ;D$L|@D$D$|$|$lD$D$ n|$L$v/,$IL$D$L$ $?|$D$$}Ã(L$ $,L$ $L$L$L$L$D$ @BD$gEL$ $L$$L$6M(ËT$ J ËD$@Ã8@R$"$"D$ D$,D$0SD$$֐=+D$D$(L$|$(|1;\$0\$4T$ D$D$L$()1ɉL$$D$@D$$4\$4t6T$ $T$ D$D$0)D$=\$4L$0KC;\$0v|$0un  $3D$D$0;D$,u4D$, L$ $L$,L$`D$ |$ u JR$D$0D$0D$ L$!D$  $L$L$1D$L$I $D$\3L$$\$4 $dL$|-L$D$ D$+Ãt;B B L$ B$T$T$P2D$ù $ D$ $D$,T$B럃 t$8\$$l$,1|$;|$(}2=sSC fMEft-׉|$;|$(|΋D$0+L$$+D$,L$4 Ãtɉl$,$\$\$T|$t$8l$\$뒃$qD$D$D$4k $pÃ(L$0 $T$0ÉT$t0\$ $D$,D$D$;\$ D$Bf/T$\$ S$qD$D$:D$L$ $L$L$nD$$=u0 $D$L$蝎u r$ I $D$D$L$ {ƀD$$$D$ D$D$D$D$ $w\L$8I|nL$\$4 $$QD$ $D$ $L$T$4D$D$D$9D$$$D$ D$D$D$,"L$I}D$$ D$$(ÃT$ u`$D$D$D$ L$ $D$$D$L$tȃÉ $T$ tJ $J L$JL$JL$ 뢃T$ ht69u(ȃ$AD$D$$D$D$(D$ pËI(uʹ, $@\$ ‹ hH(D$t $;T$hЃ$D$D$$D$D$(D$ p$D$ /ÃD=ut$5 |$8$D$D$$D$D$ߋ=u +r$0hT$@ $EL$ Ë,D$0u ǁ,EL$8 $\$4\$P $\$4T$@D$ D$(1ɋB9B)ȉD$,|$, ~D$, Ѓ$L$$L$D$ D$D$,D$ tL$4 $D$D$ D$D$,D$ D$D$,D$5\$4T$@L$$D$,B9n$D$(D$D$D$ D$ $XD$4$D$4$9T$8L$0,Ƃ$D$@$IuL$@A(D$<t$l9L$@ $\XT$<GhDà D$,L$( $D$D$dƒD$uSr$'T$$LD$D$$$D$D$D$D$ ÃT$;T$ t1ËL$ $L$L$L$5u 1T$L$9sÉËT$L$9vÉà |$$u1 ËL$(kA $D$L$$ $DD$D$$D$D$XL$(D$$k@D$grD$L$ D$$D$RbD$ Ã,L$0 $x3D$$L$$ $D$0$D$$D$L$(L$D$$D$ D$ D$D$T$(D$ fBD$4L$ Ѓ,à fT$ƒ w1 ƒr =v(lr $ʉL$jDt1 ø 1 ËL$T$JÃ\$T$9r1É$\$Ë\$T$~ t uKD$  t\$T$~ u D$  t tKԋ5=D$à t;L$tË $D$D$uÃL$ $SID$u r$L$ $zBD$ D$$D$ D$)VD$ÃL$ $SD$u r$L$ $)BD$ D$$D$ D$UD$$D$D$D$1D$ÃL$ $L$L$eTD$u r$AL$ $AD$ D$$D$ D$cUD$Ã< \Չ-|$@tD$@u%,$D$D$L$ r<ËtЋ8D$$9D$$}\D$$ȋNNf9rNL)NL)9rD$$9D$$|C^K\)ȉ~|NH9vnC\ @IL$d$S(‰,$D$D$T$ qNs#$L$4L$D$LL$4ȃ<ËD$@9uC)D$|$L$@T$@)9"șD$|$:D$@҃t uw|$@tD$@uR=u# $D$L$ht hr$ڋD$@\D$`x $L$`=0tȃ$L$`ǁ\ǁl|$Lt$h@@D$PD$X<$\$L$`L$($D$`$ȉL$|$t$L<D$D$1\$`T$dǃ؃$D$CD$D$ D$d@L$, $qD$d@$D$L$0L$D$,D$ kD$`$D$D$0D$D$,D$ D$茾D$0$OD$`@$,L$,D$`$L$L$\$`T$d|$Lt$h@@@D$PD$X;D$P}D$PD$XD$8D$8dd$D$D$`I L$ FLD$4L$8 $L$`L$O"T$`ǂp$ȉL$|$t$LD$4D$HD$KL$PD$P@D$X $|$t$L D$D$D$D$ D$`$ϧ\$`߁t$h؋ ށƠ|$<BD$$D$9D$$$&L$$D$($D$D$D$ 74 9v!$$D$<D$r $сL$L$ L$ L$$D$$L$$hCt1$ЃD$BD$D$ )D$$L$e$D$$D$D$D$D$D$ D$%X$@= $~ DŽ$  $L$$ $$D$$L$`\$}!$$D$D$ `$$$$$D$D$ $$K'$T$w L$0 $$ |$D$ D$ L$0 $L$4L$$ŋPT$XtyF$  $L$$T$X$u2$$$$D$jD$ ËCK艬$ 78tUD$`$*D$l$1$$$D$D$`D$b |$Xt D$X$@ËK $$L$T$X$L$D$TD$$L$ lD$\D$T$b%T$\$t $$D$T$bA$ $$D$D$8$D$D$TD$$L$ UlD$\nI( $XL$$D$XD$Hh,s~wKꉬ$ $CD$$t$$A$0D$$D$$$Ƌ$@,$ $Q$@$$D$L$TL$$D$ $D$D$B$R$  $$+$L$B$tL$$\$T S $V L$u$$L$TfT$H‹\$DC$$$D$D$ t D$H CKK $$L$T$XD$Tс$L$HL$"t$XDŽ$D$L$$@(D$D$HD$|$ ց|$D$TD$D$ $D$$3D$($D$,$D$0L+D$T$$$;$}!$$D$D$C$u!$$D$<D$|$Xt$LB$$$D$D$q$T$`t] t Xց|$Lс$L$HL${$L$H9D$L9D$PJ $$B$BD$L$TL$$D$ $D$D$\$HD$X uP$\$HT$LD$P9v3$T$DT$D$PD$D$ i\$HT$DD$XCxD$@C|D$<$T$DT$D$TD$$D$ D$肌\$DT$@L$<9r $9r $D$H$T$L$īT$HL$D u%$L$$D$D$ pT$H$vD$X$dD$T$7$T$X$C$$$D$D$|$$D$<D$[$T$P ЃD$HEt$xD$DCKȉD$4D$,D$(D$0C=   $wD$H$x@$xI$|$D$D$X$x@9D$X$xIT$X D$@$BL$L$$|L$P$|$x \L$@ uCL$@ t\$X$x@9D$X`|$0tzD$H$2|$DtD$XD$XD$x$x$D$dD$D$DD$|$(t$|$|$,t$|$TttËD$H$넋L$@ $GL$L$$|u#ǁ\L$>PL$@ $NL$L$TuU$|ǁL$$$D$$ǀ$|ƀD$(yL$@ $TL$L$ $|u,ǀƂD$(#L$@ $ZL$L$ u6$| $AxD$A|D$D$ aL$@ $_L$L$- T$@ЃD$\$ L$L$4)D$L$@ $L$L$$|u"hlL$@ $L$ L$UuU$|$D$$|$$| zL$@ $L$L$u$|ƀ ?L$@ $L$L$u-P$|$D$(L$@ $L$L$Ru$|ƀ L$@ $&L$L$u!$|$&D$(pL$@ $/L$L$u$|ƀ 5P~D$DZD$0D$CD$d$D$A $購D$H7X$\D$8D$,A= v $rD$<1ۋ$\@9É\$@sq$\I ‹$`B ,BD$$ u B tT$4$L$0L$ L$D$ D$0;D$4ugnD$8|$,tID$<$|$8tD$@D$@D$\$\$D$HD$D$8D$XËD$<$O뵋T$0 u B tT$4$L$0L$ L$s$`l$ T$$ËD$0;D$4HL$0 u A tA $ȋL$@)ȋ$\I)ȉD$Da| zAZ9L$(99݉\$$D\$ T$L$(D$$Lt^XtltTxt$Dy $\$T$D$ D$G$D\$@D$D $\$T$D$ ɉ3D$,D$AD$d$D$A $߹D$$L$t$$D$`$D$d$D$h$D$l@D$lC"C9t'$D$xD$L$$\$xk JDˉ$D$hB$|$t$`BD$D$$D$$D$ T$@D$p$$$D$$D$$ $$L$$L$$L$ $\$x$$|$|D$|k BDljCk BDƹ $$L$$L$\$ Y\$p$D$hA(9D$h~A(D$hQ AH9wAL9rFA$|$t$`T$D$$D$$D$ RD$pQ뵃4L$8D$,QDAP9D$, $D$[D$h@$|$t$(D$|D$D$D$lD$D$pD$ Mt$`l$@\$hT$XF|f$CD$D$lD$D$pD$ $D$D$D$D$D$$@$%?$$A$$ $L$CL$5:ƒuH$D$D$CD$w$$ǀČÉD$<@,$$$I|L$D$>$u?$$A>$$ǀČËt$<|$,L$C $.L$L$-tQ$I4 $7L$L$(L$GD$(D$,$t$<|$D$(D$G.$ $$ L$|$t$,󥋌$L$L$@$uG$$?$D$<$=$ǀČË$ @,t ČøB$$$T$<$D$<u@$D$-$$ǀČÍD$C$D$%T$<D$$D$UD$$@4D$ 蕲$ $L$L$ L$dL$ $L$ĜÁpP$}D$2R$)D$$tu`$xu e$x$t$/gB$x $g*$x $D$Xu 1pÍ$$D$$tD$]$ $"L$ƒ}m $$tL$L$}-D$X$$$D$1pÍ$ $"L$ƒ|T$H$L$_L$L$8 }D$H$=uƄ D$WL$P}D$WL$_ $#D$0$$D$$tD$D$0D$ 5$ $"L$YD$LhL$_ $ëD$0$$D$$tD$D$0D$ կ$ $ L$D$D}D$L$$ $L$ѣƒD$@uD$D$D$8|$P3< $L$茣ƒu$D$@$ 멋L$@D$8@L$w $T$8B,J, $cL$8A($ $襪L$8A0$ $菪L$8A$ $yL$8A $ $cL$8A$ $ML$8A$ $7L$8A$ $!L$8A$ $ L$8A $ $T$@L$8A$L$WL$L $bT$@ ǂ@=} $D$T$8 $荡\$@T$8YL$_ $GT$@BL$LJL$HJ L$DJ$|J,L$XJ4$t $oT$@B0$UL$$ $D$D$L$ L$&L$@ $|$t$$1L$L$L$i-L$@A|L$$ $D$D$L$ L$@&L$@ $|$t$$1L$L$L$,T$@B|J|L$H $_\$@ƒtit,tBt$ҠD$@pǃC(։T$<<$莌\$@T$l$ \$CC묃 l$$\$(U uk1ҋCt4Ct CP(Ct CP(M $ L$T$s1\$(ƒu1 ËD$$P K $T$3 Ãtl$xD$|fD$^1E9}.M(ȋ0f;D$^wt$TFf9D$^v"BE9|D$^t fD$^1t1]$1ҋE9C9nM FfD$^D$^)ȋKI9sD$^)KIȉ@tVt$X)E 9| E $t tE 9}fU E\$PKI I(9} KI I(M,$M L$ML$ML$ qt$Xl$x\$PtË$L$|fHE NȋKI)ȉD$dE Nȉ\$PKI)ȉD$`$NHM L$4$l$4$f$NHE$D$L$xI $%L$|$`t$x$\$Xƒu1tlN,IHF,@BF,@BF,@BL$PII IJD$P@@ @BD$P@@ @BD$P@@ @BF )B F )B MJ EB EB EBL$dJD$dBD$dBD$dBCMȋ )ȈBCMȋ )BCMȋ )BCMȋ )BBBBB JBBBKJCB CB!CB"KJ#KJ$tËEKI I(9kk M$ȋ 9s׃ BE9dk M$ʋBt{ET$P )='w>E26E$D$LMk 0 $яl$L\$xT$PC$C$Mk$B$=9T$Pt$Tl$xBBT$PB4$l$t$Tl$x\$PCCu!F l$N tÉsC@M f9vC@M )ȉC@ D$DD$($D$L$@L$D$0$t$D|$D$(D$ D$,D$D$DƉ$ƒ|$T$D$D$0D$D$4D$ =L$DD$($AD$A+D$@D$AD$ AD$ T$DЉ$|$t$(D$x@D$D$փ|$t$Tl$x\$P1ҋC@9}tKI@L$@)ȉǃ}1KIȇ߈X߉KI@L$@)ȉǃ}1KIȇ߈XBC@9|CM HC$$l$Ck 0D$kT$xBk J$ȉ$D$0D$kT$xBk J$ȉD$PBD$L$ T$Pt$Tl$xiT$B(@u1ËZ$Jk  D$D$ ;\$s;t+\$C$86\$CCD$ ;\$rŋD$ ÃL\$PD$DCD$8|$\D$\L$$ $D$D$D$ D$Xl$TD$ L$ KL$L$8 $|$t$$L$\0L$D$D$D$@uD$ǖD$6 T$PB $D$BD$jD$DLËD$8$D$WL$8 $ L$l$X\$@ƒu$!T$PiKHCBCBCBBBBBL$PI J L$8 $D$}+$D$AD$@$ T$PD$P@,$q \$XT$PL$@J,L$TfJ L$\JD$DB9 $gT$PD$HD$DB $MT$PL$XJL$HJ w0T$4ЃBr|$ $L$L$ L$L$$L$ L$(L$L$,L$0à T$$ЃBr|$$L$L$L$L$L$ 覘 Ã|$ t(L$ $L$uOL$HÃD$$D$0D$L$ $-L$|$(t$$l$,\$ƒuÃ|$ uD$D$ u D$dL$IJD$@BD$@BD$@BL$ IJD$ @BD$ @BD$ @BOJ GB GB GB J BBBKJCBCBCBKJCBCBCBK JC BC BC BJBBB NJ!FB"FB#FB$MJ%EB&EB'EB(MJ)EB*EB+EB,à D$$$D$(D$D$8D$D$@D$ D$iD$?tD$@iD$AoD$BnD$C D$D%D$EdD$F)D$GD$lD$X0 $豂u1dÉD$`$D$0D$`D$`L$hHL$p $wT$`BEBJ $PT$`B Jk $;T$`B$tB uI$%D$`@ $D$`@$$D$`@($D$`$1dËB$tL$X $L$XL$D$3tL$`fAL$X $D$XL$X $L$XL$D$sL$`fA L$X $L$`D$XAAA 4fA fAA(A$$D$Ak D$_L$`A $D$AD$k_D$`@D$X0D$X9L$X $L$XL$D$1sD$PL$X $D$XD$X06D$X9&L$X $L$XL$D$rD$LL$X $gT$PL$LD$XD$Xtrޡ$"\$`$1dÁs9wՋD$XD$TL$X $L$TL$D$brD$HD$T;D$XlD$T tD$T @L$T $D$XL$`I( $L$`IL$蹀\$`C(C(u fCI $a\$`ƒ-CK(ȉL$Pf L$LfJL$HJD$XD$TD$Xt D$X upD$XD$XT$\B L$T $EtT$\BBu$\$`L$X $L$`D$XfAD$XȃdËD$X tD$X tD$XSD$T D$HD$$D$X+D$lD$=\$`D$$D$X+D$lD$\$`$T$`T$u1B9}KȉL$J(ȋL$A $~D$@$~D$$~L$T$AB9|1ۋB9}.؉\$k J$ȋHt|9u^CB9|ҋB,$)D$$Y~D$@ $J~D$@$$;~D$@($,~D$$ ~É $&\$T$됃@T$X}jD$H$|$t$HT$WL$XD$8$L$L$hD$`$D$`D$D$dD$D$8D$ D$t\ʋL$H L$ JL$J(ЋL$\J,׃t$L׃t$$L$`J0B4B8Ѓ@ù< $moƒD$4uL$H $L$\$ ƒfXBBBD$H$D$g|$$t$L+DŽ$D$($ $^L=D$ |Yp$$t4~$$D$臠|$(t D$($o1ø$^Ջ$$D$'$ $L$ L$l$ $ƒlA$Y$nXBB؉\$$BBЃ$$D$l$mL$ $D$$I $L$/L$L$D$ D$Ӟ=Ƅ$< $]mD$(ud$ $L$\$$ƒ}fXBBB$$D$@$L$$HL$G $ƒu$D$GD$豞_D$(P,$5L$(A(L$S $ztL$(A0L$_ $gtL$(AL$k $TtL$(A L$w $AtL$(A$ $+tL$(A$ $tL$(A$ $sL$(A$ $sL$(A $ $sT$(B$B4B8ЁÃL$$ $TIL$ $D$L$ l$\$ ƒu1NKHCBCBCBL$(JBЃ$D$$D$l$IL$ $D$}1øÃL$ u1Ë L$ $D$L$ $L$Cl$T$ Ãu fJHBCBCBCB4t9u:J8,$J4t"L$B} 11߃tʋA89u ȋJ8H8빋I8L$ $D$ D$$kD$ Ã$D$(D$,9L$t$1$ù $iƒD$u1$1ɃL$L$ $L$ t$(l$,ƒuD$$j1$((AZBB؉\$BNJFBFBFBMJ EB EB EB L$0J L$ $D$L$T$tEL$ L$JL$(JBt B@,u$qT$L$,J Ѓ$A $hD$u1ËL$ $ L$t$ l$$\$ƒu$Oi1SވXމBBBBBBBL$ $D$\$|L$ L$ KCC ؃ÃL$u1ËD$$L$\$ƒu FKHCBCBCBL$ $L$} ËD$$3hÃ$$L$(L$|$t$,L$ L$ $qL$VuD$ ÃtL$ $T$tV t1 ËA 9D$uAȃ Ã$aL$ $UD$ËD$;uÃ|$  $J`\$ƒu1ËL$fHL$ HL$$HL$(HL$,H D$@tG$VUL$L$, $L$L$T$uD$$T$T$ЃĊ$hMhD$xD$(u+L$l $L$tL$L$|L$D$(u1hËL$t $L$?L$$L$$t&|$xu D$($$$ 1hÍL$? $fD$8k $_u1hËL$t $D$4D$L$8kL$臔L$8k9tH$谐D$4$_1hÉ $^tىD$0$D$4D$D$8D$B|$|t D$l$L$K $>fD$ L$W $.fD$L$p $L$8L$L$ L$L$L$ L$0L$L$(L$D$,|$|t D$l$|$,uD$0$^'D$4$^D$,hÃL$ $L$ L$L$$L$D$ L$(L$Á$ $L${L$ L$cV t 1ĸÍL${ $L$ L$=<$u%$ $T$$L$ĸÉ$$L$1L$U1t 1ĸË$D$\11҃ }D{ $ t$虎1ĸÃ\$p[Ƅ$L${ $7D$<u$D${D$Q1ĸÍ$ $)dD$D$ $dD$H$ $dD$L$ $cD$PD$D;D$LD$H;D$PD$HD$XD$PD$TL$< $\D$,<$t$DL$,L$ D$l$t$$ǽ$ $|$t$DL$]D$TL$ $.]D$X$ $]D$\$ $]D$`D$T;D$\D$X;D$`w$t$$$ $|$t$TL$(L$D$D$v‰D$,$t$$.T$,u 1ĤÍ<$t$TJ(L$qD$8$WTt$,D$4|$4uS$t$$ht$,4$$t$$訶D$4$U1ĤËL$XL$H;L$`$ $L$gL$L$Lt$,nL$g $[D$DL$s $[L$D;L$HD$`9D$89$ $L$4L$T$LT$ø$zD$D$D$L@@ ÃL$} Ãu o@ ø($ B ,toL$ $B\$T$t3A 9u$B D$$B($BD$ËI0u͉$BD$u($sBà @PL$HЃ @@ÁD$ HL$A8t A8$>$ $[2L$A8D$ D$D$($D$4D$D$D$ IL$( $L$ n$|?$ $ @$$$D$L$.n$$mİ@@8@Xl Ã$H@1҃}BѸ!u*щT$  É$@D$ B|$@ÃL$ |*}%$?L$ !E@L$ȃXÃT$ @@uTȃ0$D$T$%+Á$$(uU$($VD$ L$/}D$($>l$1ۉ\$,t@|[($?|$u$($;C$1;T$}$T$D D$vD$xT$Bp uB 9u뀉C\$L$ ȋJ Ƃp ڃ ,u $L$V ȉ , à $D$D$(D$)P9ЉT$L$@0 $L$D$0k} |$$tD$$$D$ ù $D$jL$A$}$D$/j먹 $!L$mjT$B(}%B$$j$D$ia$D$iT$BL$(JL$,J BxD$@($.jT$J$ $у0L$L$n\$~1@0؃0$D$iD$@$$iC$$iL$$t $D$@4D$. ø$AT$6$i`D$$D$D$D$D$Á#$uÍD$$D$D$PD$D$ GueD$$lD$ZF$D$$$D$L$LL$ED$$lD$FD$$FË@t(D$$lD$A D$A@ D$ E뀍D$$lD$A D$E_$ƒt@uf1ҍD$$)ЉD$lD$$(D$ BL$$ $D$$D$$D$D$D$g$ÍL$$ $lL$J L$JI L$ /Bo$:\\D$ $:D$ Ã$L$4 sx~$L$ix $L$T$4D$P$$D$T$B B $D$D$4D$D$$D$,D$D$0D$T$8D$L$ qL$0 $\$4\$L$8L$D$0$L$D$$D$D$8@Ћt$$l$0l$D$4D$ D$D$D$\$ ;\$s $l$D$8Ћt$$l$0\$ |)t$D$9v$L$ $l$D$8Ћt$$l$0\$ ЋD$9,$D$D$t$D$8@Ћ|$8t$$\$0D$)ؙl$4)M9l$4|2$D$(D$|$\$4D$(@l$$D$0,ÉD$D$(@D$ $l$|$D$(Ã~̓ D$؉\$4l$$D$T$0 $D$D$t$D$8@Ћt$$l$0\$ \$,L$$D$0D$\$+D$ ؙD$ |$ t +D$ $D$(D$D$D$ à L$}ȃ øT$;D$~|$Ѓ ÃD$L$ $67u1ËL$@ᆳރà \$\$C=ᆳt i\$K ;D$uT$JK 1 ÃD$@T$ÃD$@T$[Ã0 $D$3} 0ÉD$($L$L$L$7}D$($30Ã|D L$ $ uȃ0ÃD$ PTT$Bu YT$BBBD$(B$D$$D$L$L$ Ã(D$,@TD$$<T$3Bu T$BBBOD$4 $L$L$L$0L$D$$D$ +D$D$ $D$D$2$D$D$D$ D$y2$D$D$Z2L$AtaA$D$D$12D$@$D$D$D$ D$2D$@$D$D$1L$ $(}(ø$4 T$  $L$L$ͪuȃÃD$L$ $fD$ D$$D$ D$D$$D$L$뵃  $L$L$_u4|$ tuȃÉL$ $D$D$D$L$؃D$L$ $D$ D$$D$ D$yD$$D$L$뇃 L$t$ȃD$詬 ÃT$$uD$ $1Ã|$ t D$ D$   $L$ L$T$=t`ȃD$L$ $D$D$$D$D$L$|$ u(L$ $D$D$$D$D$L$ȃà  $L$L$Ƭ ÃD$ D$d$ D$ $L$:D$t$L$ $YD$ D$$D$ D$D$ÃT$L$ tQÃÃT$L$ tQÃËL$AøËL$u1ÉȃÃQ$V4D$ D$|$у}uGdU˽_}RؙD$ȭ|$ƉؙD$ȭ|$iۏiG )˃}|QE_|ÃH$D$ $>H${ÃH$$4$Qs'u$$4| Qs| $ %4D$ H$D$ ÃL$ $yu1ɁL$ }2L$ $uyu$,L$ AL$ |1ɁL$ }3L$ $5yuød$g,L$ AL$ |͋L$ $yt$8,ÃL$ $xt1øËD$Ã{ $D$:,} {ÉD$$L$?L$[0D$D$$>,L$${D$( |$/D$T$:ƉÉEtE/uEEu9uC.E.uE/t EuEE.u E.t/T$:9tC/E/tvECE/t Eu9vK9P/D|$79tC/C.C.1T$11  -+0u Bu50|9~t1)ȉȃÉk B ȃЉBxt-BXt$0|7B ȃЉ܃Ã0|9~5a|f~A|F{؃ɉBʉ؃؃ЉB-u t   BBL$ $Ã$1Ƀ!}EL$ <͸u1:T$ ռD$ո$A!|$1à H:l$1҃!}ո9u ռ9t B!| ո:D$ |ոt D$;ռtJ}D$$(ÉT$ոӋT$ӋL$}1)ÉËL$}1)Éà 8t D$$Ѹ$D$D$D$atÃL$Au1ËQAD$$T$T$l$T$\$CCu#CCC $<1ÉkCK)ЉCK HC ÃD$$D$0D$UD$ T$$l$\$CCu ËKKK HC ;KkC1ÃDL$ $s}1DËD$LD$0L$ $L$HL$|$u1DÃ}D$$?1DËD$D$DÃ@T$HL$D9w1@D$L$L$HD$D$ D$$D$(D$PD$,D$$D$LD$D$D$@ÃT$BJ)ȃt$J $JL$D$D$L&T$;D$uJJ1ÁL$$$PD$D$D$D$ $X$8$ $$TL$D$~$ $@L$t ȁLøLà D$,L$$ $L$L$(L$D$D$ 谩 à D$0L$$ $L$(L$L$,L$D$D$ 聩 ÃD$ L$ $D$D$ÃL$Au1ËQAD$$щT$L$Tl$\$T$BBu#BBB $1ÉjB)JȉBJȃB ÃD$$D$0D$ D$ L$ $\$T$BBu ËJJJȃB >JZB1ÃDL$ $n}1DËD$LD$0L$ $L$HL$|$u1DÃ}D$$1DËD$fD$DÃD$ L$ $D$D$zÃD$$ $L$ L$D$D$%ÃD$(L$ $L$$L$D$D$ÃT$B,B$B(BD$L$$J$L$ L$ L$A,A$A(ȋL$H|1ÉЃÃT$B,B$B(BD$L$$J$L$ L${ L$A,A$A(ȋL$H|1ÉЃÃL$ $ ËD$ @ËT$ l$EUU\$ M =ML$ME1à t$l$~[}Suc ͤú9s9u&u Ér9u*1 Ã˸ ÃTL$ $L$L$pD$KD$Ã\$ ͤ¹9s9uAtAÃ9rT$t59u%$L$AD$ӥD$@Ãu蹥cÃ|$(T$ \$$B,B(1ɉJ$fƒ\$$ʉN ƒ3W08*.tBʉ $D$L$ $D$Ћ|$(t$ \$$}1Ãp؃ÃN,fL$ $\$$\$,|$(t$ \$$É\$$D$f@FFh} 1)ŃN,F,t N,n(N,n$1ËF,u N,@1ƒ0rƒ9v tKk ̓Ѓt fƒfC뿃1t2뭃7t4t5t6둃8t9끃 t$l$\$|<}7.9r!9 1 0J9sE1 1 à l$\$T$*9r)؃0|1 9I9sE9 ÃxT$|B,tJ(L$~D$݄$$2t$$$D$x݄$$L$jt$$(D$x݄$$L$1t$$-D$oxD$$ܜ$vܤ$ݜ$D$$D$(D$|@ D$ L$  ({tD$(L$ $iD$ D$H݄$\$T\$T݄$$D$HD$Þ؋L$HL$D$ o|$fD$l$\$l$L$L$H|$Hj||$HrD$HD$|$1)ȉL$D$Sz܌$\$LD$D+D$H$8zL$L\$T\$TvL$HD$D+D$H$zL$L\$T4\$TwD$HD$D+D$H$yL$L\$T1҃}HD$T|$fD$l$\$l$\$؃0шD]؉\$D$l$T 4\$TB|D]L$A|$ fuD$H$$D$]D$UD$HD$DD$l0D$m0D$n$2D$D$HD$D$]$D$Sm$\$TD$Tܜ$1ɉL$DD$@|$$6AL$D-D$8T$BD$0D$D$,D$ fg%L$] $T$4T$L$T$8|$0t$4$\$DtD$H}1҃}1~ ~&;D$,u C.C0Jڃ~4;D$,u C.D$]L$@D$@CʈÑ~ ;|$,u C.C0OD$|@,|$,u C.|$ et|$ gt+xÃ|$(CEt$H}~C-1)ƃd|,D$d|$0CʈD$d|$։D$ |$0CʈD$ |$Ѓ0Cʈ\C+놉Ce^|$ gt |$ h!N| .0t)/.u9CONčL$] $T$4T$L$L$T$8|$0t$4$\$DtD$H|$HD$H9+T$H+D$HD$,D$ hL$] $щT$4L$HL$L$|$0t$4$\$DtD$H1+T$H;T$~D$HHD$|@, tAL$D+D$|@,AL$D L$] $L$L$ht$D$HD$n$6D$D$HD$D$]$D$Ai$\$TD$Tܜ$D$]$T$D$DD$HD$n$:D$D$HD$ D$]$D$h$\$T݄$\$Tv)L$] $L$L$tL$H݄$\$THL$] $L$L$KD$HBD$D݄$\$LHD$L$D$ D$D$P\$D$L`,L$ $D$L$ $D$D$L$D$ D$D$D$D$D$ G1HÃT$ BB@\$$D$\$jÁD$ $D$&L$ $+D$L$ $D$$ $L$ L$L$L$L$L$ ĠÃ4|$ɉ $Pft$>l$DL$(;L$$=sZCΈD$8D$8;D$4rM)U]D$  ,$L$+L$LL$@fD$\$($L$L$9l$D\$(놉,$\$D$cft$>l$DÃt E D$$H@ø@ËE(D$LT$BB@D$B($L$L$L$L$ JÃT$BB@fD$$L$L$L$Ã,t$0\$4u'4$lL$L$L$ ,ËF,t_11ҋF(9‰T$$}<u4$\$T$l$ ,ÍL$" $ډl$( L$*t$0T$$\$4l$(B륉$D$L$4 $D$L$0 $L$4L$L$L$L$L$ 2,Ã\$CC@$D$Ã$|$(l$,u'<$lL$L$L$ $ËG,t+w(19} Zu<$l$\$$Cډft fu)ȃ\$CC@$D$UÃT$B fD$B($L$L$L$ ÁD$D$@,D$41D$LD$PD$H$@ p}$AA@D$H$@ xL$4lD$T$@ d Xbt Ĥÿ|$@D$4%uNT$,t5L$L $L$PL$D$D$ 'T$,|$@1u |$H $ʼnD$X1t$8L$L $L$PL$|$@t$8l$XL$ $|$dL$ $L$LL$L$PL$L$L$ L$L$L$ $L$$L$D$ET$Z JBct^\$Ct E\$T$KJ B uBB$,L$ $D$(ЃtÉ$ÃD$$L$AuAthAD$L$\$ƒuHS SBT$B$L$ $L$(ЃtD$ÉAA $fÃD$ $sL$ AuAt $71A $"ÃD$$*T$Bu CT$Z uB$CuA\$KJ B uB$L$ $D$(ЃtCt ?CT$B t B @t"B uBB$LËZ KJ B\$$D$(Ћ\$T$t똃D$uBD$$D$@uBT$@BZC\$ZXtq\$KH@u @ $L$ $E#L$(ЃtL$ $L$(ЃtD$Ë@$(BXWD$uAD$$T$@u AT$Zu$1ËKJBuB IKX@ uX C$ÃD$ L$ $QtD$ D$ Á$ $/L$t ĄÍD$$dD$pD$$D$ RL$! $$L$yt ĄÍL$ $L$L$P$} ĄË$ $"$ $$L$D$D$/$;D$t $ĄÉ $1ĄÃ$P5-p=u?t0E9}C9| L$t$D$Ã$T$ t =u${Ë  ߁$$t ĜÍD$$>~D$L$AL$ $L$hD$} ĜË$ $uL$ $$L$D$D$L$;D$t $SĜÉ $?1ĜÍD$$0~D$L$[|$hk1}2T$ E|Ã@x}o\Ԕ $ L$yƒx}B\ $ L$Lƒx}D$D@@Ã=\ts$L$3L$L$Ux}'$5xD$D@@ÍD$$$D$3D$|$Dt$$@D$,|$,d}L$ $T$D$D$ D$1L$ $L$L$D$D$ Ǭxt$$L$3L$ L$|x} D$,uD$?D$D$D$3D$D$D$ >@ÁD$@$D$D$$D$ L$@ $D$} 1É$$L$@L$L$$$$$ 1D @L$@ $L$L$ L$= t 1ËL$4 $Ã`R $D$}1ÉD$$L$ L$L$$L$LtD$D$$|$}1ËD$ Ã,D$$D$D$蓛r $D$v|)D$$D$D$D$D$$L$ $,Á$ $/L$蒚t 1ĠÍD$$$dD$,D$$D$ L$) $$L$t 1ĠÍL$$ $D$} 1ĠÍL$ $$D$D$D$ L$&L$ $L$L$$A $$$$D$D$D$ D$$ $$L$$L$-$$|"+H‰9su A9r$$$ĠÁ$ÉD$8$ AIQ r$Bʼn$ ȃr\k`.gjde}ftA$$\$D$L$ p$  $$L$Í$$\$D$l$ B D$BD$BD$p먍$$\$D$l$ B D$BD$ypu$$\$lD$l$ B D$BD$FpBL$@ $JL$$D$($$D$8D$D$$ D$ փ |$BD$D$(D$ohtxiL$@ $JL$A$D$($$D$8D$:D$$ D$ փ |$BD$D$(D$ZoV$$\$ D$l$ t$B D$BD$BD$o$$\$aD$l$ B D$no lmn$ $\$L$l$ t$J L$JL$n$Ey1E9k$L$8L$̞L$\$ ډ$ IL$8n$$C믍$$\$D$l$ n$$\$yD$l$ B D$m$ $\$ӞL$l$ J L$m$D$<C 1C 9щ$ L$@ $D$0H L$T$0D$(L$< $L$8L$L$$L$ ֍|$JL$L$(L$m$$D$$$\$RD$l$ t$BD$B D$BD$ly$v!sttZu$ $\$L$l$ JL$k$$D$8D$BD$BD$ ny$$\$D$l$ t$փ |$BD$:k6L$@ $JL$$D$($$D$8D$D$$ D$ փ |$BD$D$(D$BD$ j$ $\$L$l$ t$փ |$JL$j$$D$8D$BD$BD$ OZwt2x,$$\$?D$l$ t$'j#$$\$&D$l$ BD$i$$\$VD$l$ i}Yzt\{t2|$$\$D$l$ t$i$$\$}D$l$ fib$$\$eD$l$ t$=i9$ $\$L$l$ i$‰D$<A =v&$D$8D$D$A D$ hD$@‹A$A D$T$ЉT$4 $u1,ÉD$( $hu1,ÉD$(L$(AL$ $t$0 |$8L$ L$ $L$L$\$0T$(BK CBCBCBT$($CD$CD$}yT$(D$0@awt1xM EBEBEB+M EBEBEB}t?zt{|M EBEBEBM E BT$($ED$E D$xT$(D$0@ x~teM EBEBEBM E BT$($ED$E D$xT$(D$0@ t$ D$Ã9v1C C @9v1É$؉\$@D$L$L$w\$T$D$$@‰ЃÃ,\$8T$0Ѓ ;D$4v1, K BB J J ȉCD$$$B J ȉT$0J ȉD$谄T$0D$$BJ J J ȉD${D$$D$D$D$D$ D$ D$8$D$$D$D$(D$D$D$ D$ D$D$0,Ã@l$L\$DT$HƉ؃9v1@C K K ȉD$0|$0s1@ MCC fMEr&kgxdeft1@É؃9v1@C K K ȉE $t$8t$L$xt$8Ãu,t!9wL$DT$0 9uD$0@1@1@É$t$L$LL$*t$8Ãt밉؃9v1@C K K ȉE $t$8t$L$t$8W؃9v1@C K K ȉE $t$8t$L$vt$8$t$8t$ L$t$8Ãht6id$t$8t$ L$t$8Ã؃9v1@C K K ȉE؃9v1@C K K ȉE $t$8t$L$t$8Ã$t$L$LL$Zt$8Ã$t$8t$ L$-t$8olmn,؃ 9v1@C K K ȉEC K K ȉE C fMEv1@1E9$t$8t$|$4 L$M|$4t$8l$LÃG뺉؃9v1@C fM ؃9v1@C fM E v1@1E 9`$t$8t$|$4 L$|$4t$8l$LÃ$G뵃ptmq$t$8t$ L$t$8Ã9v1@C K K ȋL$LA؃9v1@C K K ȉE MCn؃9v1@C K K ȉE$t$8t$L$T$Lt$8à 9v1@C K K ȉB  JCyxvstt[u:؃9v1@C K K ȉEM 9v1@É]EG؃9t$8v1@C K K ȉED$$$C K ȉ\$<K ȉD$|T$L$u$L$ $T$ L$pL$T$u'tBu‰ЃÃuBB'tB1뙃딃\$D$D$;D$ }0t L$$ $\$ L$o\$u3uD$ËL$T$$L$$L$D$뛉C띃\$D$D$;D$ }1t!D $\$ L$ o\$u4uD$ËL$T$$DL$D$C렃\L$l\$dD$0D$PD$TD$,D$8 @- $  -\$X+ 0uCxWCXJD$ $L$lL$jD$@$D$D$D$ D$ D$$D$cl$l\$X\$X0| 9a| zA| ZЉT$49|$,uD$dÃ|$htD$h|$8t1|$0tD$`@\ËD$`@\Ã|$0t0D$`$D$D$D$PD$ D$TD$X[\Ë|$`t$P\ËL$P $L$TL$L$@L$L$DL$ QktD$8D$$D$lD$iD$ $D$PD$D$TD$D$D$ D$D$\ZD$$D$4D$SiD$H$D$ D$D$$D$D$D$ D$D$uZL$H $L$LL$L$PL$L$TL$ Pjl$l\$XtD$8D$HD$PD$LD$TCD$,ʃGʃ©=ʃ3\$Xu0t$ Cxt CXk\$X_C\$X-D$0 C  t D$LDŽ$$DŽ$@DŽ$411ҋ$HEÃ0| 9 7   m{t/$$t$@~$@D 0$@u DË$@D$$u$@$4$$t1)$4$4|$46~7$$t$-Dø$-DDŽ$0$4~7D$$$@D$$4D$$0D$ "$4Ƀ$4lD$5^$@D$0$@$<D$$$@D$+$0D$F$<$@9}^L$ +$@$D$D$$@D$9XD$$0D$$<+$@D$W$<$@$@DDŽ$011ۃ }DǃA$4$L$$l$ 0$$A $L$$L$/$}DŽDŽA|11҃}<t ѬB|č$$#L$ $$L$F1҃}&;t1ۋB|ڃQ$ )$ |$ $$ݜ$1҃}6݄$ D$D$}ݜ$B|ʋ$$tܤ$ݜ$݄$$$0ȉD$Y+ݜ$݄$DÃ}1҃}B拌$ $ )$ D$ d$ƃЉ CD$$$@D$$4D$$0D$ _T$@4$,$lL$$$Luȉу$uL$$Lu]$L$H$,$hL$;$L$At ȉ(DÃt 7MM0E t .tA+t-u$$$u븃먃$$ڃ$$$@$4tt{kaEt eYt K;kt[t]tXtStG$@u0t,$@D$$@$@Ȉ$4벺뫃t tuk ރ l$1һ~ Љ‰$KU à l$(1ۃ}DC|1ۃ}DD$ d$DC|D$$|D$0d$L$$ u20~ Ã|$u} |uA|1 9~  9} D$$^t$\$1҉ىugF uLtk ىt3t&k ىƉ)‰E0D$uEÉk ʃЋD$뎉ىlj)‰E0F uD$k ʃ˃l$\$T$ l$~3$T$ D$l$\$T$ l$̓~$T$l$à D$, | щT$k  L‹D$L$$ $JL$JL$P\$T$~KD$,)D$0D$$$D$(D$T$% Ã|$ t$1O;|$s.t%ؙ݉D$ |$k )N0uۃЋL$$؉řD$ |$k )N0띃(D$41)ƒ | щT$ k  Ĥ‹D$L$, $JL$JL$O|$,t$0l$ }MËD$8L$)D$4(.<$D$\$D$D$ (ÃD$D$D$t9T$D$ ({t $‹D$9t1ÃL$}W1)ȉ=} 4͘ÙD$ |$ ‰)ЉT$$\$D$$L$= Á} ͘ÉșD$ |$ ‰)ЉT$$i\$D$$YL$ÃD$P;T$tjT$ BL$I9uoBtB@9t~$xT$ BtB@9t$lxT$ =^u R;T$uø=$ExT$ ~$2xT$ {L$=^A9D$s A;D$rg$wL$AtA@9t$wL$AtA@9t$wL$ $L$AtA$D$D$AD$UL$A tA $AD$D$D$/øR$GwL$)T$L$ tYuȃÉL$ =^u&@9uȃË@9s ŋ 뾸$vT$L$ ø$vT$L$ 됃 L$ $L$L$i Ã|$ tLL$ $L$ IL$B\$ ‹uCC D$Ë*MKM K $>v륃|$$L$ $L$$IL$\$$ʼnD$9umCuK MC ʹʉKD$ ËC uKMىك @t @uȋJ KJK J U롸$ul$\$$y$su@T$L$ 1ۃu؃ËA9uȃËA9sˋIڋI Ջ\$T$u RRËKJZBPBPËl$\$C9uCʹʉK1ËCKHCKH9ukCʹʉKÃ$L$,D$(@4ut^L$( $T$JL$FD$D$$L$L$4‹D$9tL$ $T$L$D$(L$IH D$$Éȃ$D$AD$dL$,c L$$ $L$(IL$D$T$$L$(L$t$l$\$(ur $l$n\$(L$C ʹʉKD$$K)H D$$@4u  ʉ؃ É؃$D$CD$\$(9t $D$\$(L$눸$sT$9L$T$B9sJBHJI!Ë\$T$ B9sZBȋJI!Ãl$ \$K 9=^uD$$\$l$ \$E=^uD$$l$hl$ \$K @@@@MKK KH@@EE ʉ؃ø $rl$ \$M\$=^t#L$KK KH@؃ø-$q\$ʃ\$ K @J ȋK)ȃÃL$ $L$(L$`\$(t$$N9r~F)=s[NŋF)%EN)فMÃ9v݃9sڃ  C9røg$p\$(t$$뎸F$p\$(t$$h L$$ $L$,L$\$(S)‰T$C+D$,=s?9r s&  D$$$\$D$,D$D$( ËD$$@9rω$L$S\$(K D$$@4u7 ʉL$ $D$D$D$$$D$D$G\$(qL$ $D$D$D$/L$릃\$ L$KK ^@Ã,\$4T$0B4B؋J9vV$ L$0uA4u$.,É $D$AD$D$4D$ AD$AHƉ$B(Љƒu,ËD$0L$4HT$($D$D$($D$4D$D$0$D$(D$\$(K  ʉ$C؉T$)ЃD$D$0$D$D$KD$0$D$D$D$0$D$D$#|$0l$(1_$9v ދ[ 9w] tE hutHEh Et<$l$ED$I|$0l$(E t<$E D$l$&,Éo$빉$D$\$BHЋ\$4T$06T$$=^L$ $T$D$ $L$L$JD$$D$(D$D$ $D$D$"\$K ‹=^uL$ $\$T${ËD$ $\$\$D$ $D$D$ZÉT$$SD$D$$D$(D$D$ $D$D$D$D$뺃 D$$$D$(D$rD$$$D$,D$^\$(T$,C9u9wƀ$l\$(T$,D$$@,$T$D$$@,Ћt$$l$,\$(ECCX K Ë@)ÃM D$4$\$\$D$$$D$D$\$(D$,$H)؉D$L$D$$$L$D$)ȉD$D$$$D$D$\D$( É^$`1 Ã@\$HT$D$D$D$\$ D$CD$CD$C D$CD$ CD$$CD$(BHЋl$D\$HC=@r@ËK ,$#D$AD$AD$ AD$AD$AD$AD$D$ AD$$EHЋT$H=  $l$H\$D‹E9r@Éщ D$4$cD$CHЋT$DL$4$nD$AD$AD$ AD$AD$AD$AD$AD$ AD$$BHЋT$DL$4$D$D$AD$ AD$AD$AD$AD$AD$ AD$$BHЃ@ÃL$ $ԁD$D$D$AHЋD$$D$D$ ÃL$ $؁D$D$D$AHЋD$$D$D$L$ $܁D$ALЃÃ4l$8\$<=ޭ=^t/== t,$\$D$gl$8\$u10ÉD$,$D$D$L$<tD$$L$, $L$4L$D$L$8L$ L$$L$HL$, $L$4L$D$L$8L$ L$$L$L$, $L$4L$D$L$8L$ L$$L$L$, $L$4L$D$L$8L$ L$$L$L$ $L$4L$D$L$8L$ L$$L$L$ $L$4L$D$L$8L$ L$$L$wD$(|$$tD$,$ 10ËL$ $L$,AD$$J>L$ $Et$eD$ $41à L$$ $=}D$$@ ȃ ÈD$D$L$$ $Z\$ƒ}Ѓ ÍD$CȈL$ $\$\$+tL$ $L$L$*T$$B B 9D$~$;T$$L$B 9D$D$ ËT$BuBBtËJ ) B Ã0T$4Btt't 0BJ,J010ËB$Jȃu10ËJ $J(L$D$,D$c\$4T$,9u`D$$T$($D$4D$D$D$ D$D$D$ D$D$4H$1)ʉP10CC2\$SCK )ȉBT$ $\$ T$}1ËK,J B KȉBJJÃD\$HD$C,D$KT$ D$D$D$D$ \$$D$(D$PD$,L$ $L$LL$M‹D$HL$+L$HЃDÃ,T$0tA J, ,ËBtBuB,ËB($B,D$D$)T$0J $J(L$J$L$"fT$0ËJ(J0B}B,ËB$9}7B,)؃$B(D$؉\$(D$\$(T$0J,)ىJ01)ى D$$\$$D$0D$D$D$ D$D$D$ D$wT$0L$AuAAt Ã1҉T$;T$(}@L$ $L$ L$r&D$D$$D$$D$Z&T$L$D$f;D$tЃL$ L$$뤃L$ tA$0D$ $0ÃD$D$ $D$u$ÃLL$T $/L$ t$`1LËL$P $D$_}1LÉD$0$L$(L$D$@|$@D$0$`1LD$81;\$@}3ډ\$Hk<\$( I, $ \$H;D$8~D$8C;\$@|͋L$@ $.D$$L$@ $-D$L$8 $-D$|$$t|$u8D$$$.D$$.D$($.D$0$E_D$LÃtÉ$D$D$ L$T $ D$8D$<@Bl$,1;\$@L$T $ډ\$Hk<\$( I,L$L$8L$4"l$,\$Hu|kimage && s->image->chan != 0top/bottom: ridiculous number of windows top/bottom: windows not on same screen no file name given00 text.loadempty directory name can't open %s: %r can't fstat %s: %r %s is a directory; can't read with multiple windows on it %s: NUL bytes elided text.insert%c%d %d 0 %d %.*S %c%d %d 0 0 text.delete%c%d %d 0 0 .%.*S%.*Serror attempting completion: %r %.*S%s%.*S*%s /: no matches in: %s %stext.typetext.type backspacetext.type cq1frameselect not right frame#d/%dctlnot a directory%P %P[%d %d]editthreadstring too long Edit: %s ungetchbad delimiter %c newline expected (saw %C)bad right hand sidecommand takes no addressno addressbad addressdefcmdright brace with no left braceunknown command %cregular expression too longno regular expression definedbad address syntaxsimpleaddrbad address syntaxpppppppp b#b%i[+b,b-bCYENGNSH[X[b[c?Yd[eNfNgNhblbnbo[p[rNsZubx[Unhandled note %s, proc %p sys:Got note %ssys:Threadexitsallstatus = '%s' threadintlibthread: too many delayed notesslash character in name argument to complete()unnamed fontstringwidth: bad character set for rune 0x%.4ux in %s compressed readimage: bad formatreadimage: bad channel string %sreadimage: bad ldepth %dreadimage: bad rectanglereadimage: image too wide for bufferreadimage: read count %d not %d: %rfrselectpaint b==0%c%d %d %d %d %.*S %c%d %d %d 0 %c%d %d %d %d %.*S %c%d %d %d 0 acmetextclick=%dinsanely long file name (%d bytes) in plumb message (%.32s...) addrfilenameUntitled-%d%s/%sstring too long .-+/:%S/%.*S//%s/include/sys/include/env/%sprocexec %snot only thread in proc#|/mnt/temp/mnt/temp/data/mnt/temp/mnt/temp/data1/mnt/temp/mnt/tempp->needexec==0imageinit: can't open default subfont: %r /env/font%d %d 0 %d %s imageinit: can't open default font: %r imageinit: can't open font %s: %r %s/label%s/winname/dev/dev/draw/new#i/devimageinit: can't bind /dev/draw: %r%s/%sgengetwindow: %r nobordernamedimage %s failed: %r image->chan != 0noborderbordersize(*winp)->chan != 0%s/winname/dev/devinitdisplay: directory name too long%s/draw/new#iinitdisplay: %s: %r%s/draw/%d/data%s/draw/%d/refreshiounit %d too small%s/labelproc %d waiting for display lock... draw: %s: %s flushimage fail: d=%p: n=%d nn=%d %r bad count in bufimagenaninfinityinf7635118191655355242878388607671088631342177271251562519531251220703125152587890625190734863281251192092895507812514901161193847656257450580596923828125internal error: addblockinternal error: delblockinternal error: setcacheblock not foundinternal error: bufinsertinternal error: bufinsert1 cnc!=0internal error: bufinsert2 cnc!=0internal error: bufdeleteread error in Buffer.loadinternal error: bufloadbufread: internal errorL}])>%@' ,@ @1 5 : BHNT@@draw: _frcanfit==0&[; $$%J%&*default*%s.%s/%s%s.%dtimerproctimer realloc failed.̏unknown elog type 0x%ux internal error: replacement string too large(%d)elogapply: 0x%ux elogapply: can't happen %d %d %d rgbkamxthreadmainthreadmain/dev/cputime_schedexec %s_schedexec failed: %rexec failed_schedexecwait %dprocexec 0 0 0 2304 15 0B@C``H А@`p8pc~>?  @ @HA`AA>@H$  @8 0<`T_o}o08` x0A00$v P@0(p$ pH$ p 8 cx>| ?o 0|x|B# <`>n>`ű8lc1G?|~?>?o C0'Ar" C , `0 `0c8l6 1f3|><7p1f3Ə0lH$ `8 @ A@ AѠ0 cb r1` 3c 9eca230x#1fw@lv'c1Tx~oϓ~?w{o a0x(O0J ل@, `0 `0c,l6 d1f31`0lb3``0;a61f3@@lp$ @Pp  `  0cc<103 3 yefa0H3`cٌc1L21p䪘}/?}o>ߟ0(Ygr? 2Lf3c1>`0,l6 81f1`0lc1``071f3g1f3@@l 8 Al00c90>0# 3c 5|a 0?3`cٌc1ٺuT}O|ov1([J t8 eRTF#b100 `0c&l6 1f0~??`0c1f3k1f1l0@$  @ p(00cg0g 003c 5`a& 0c3`cٌc1ٺ8}o9oy~ ``0f(M`8??0 `0c&l6 81f0c1f3``0 `0c1f3k1f1l 8 @$a$p0 1o0`c ```c`Mᙌ3c `aac `0c3`ncٌc1a& `T~o|_qy0a>c? g x``c 00;`cpٌ8| `c `T?w~?~? `0=al6< G P Y b k t }                   ( 1 : C L U ^ g p y               string: %r deleted windowloadimage: bad rectangleloadimage: insufficient dataloadimage: image too wide for bufferbufimage failedfrptofcharend of string in frcharofptregexp: %s expression too longregcompunmatched `('unmatched `)'operand stack overflowoperator stack overflowmissing operand for %cmalformed regexpoperator stack underflowunknown regexp operatormalformed `[]'malformed `[]'regexp list overflow regexp list overflow warning: changes out of sequence 0123456789abcdef0123456789ABCDEFnoproc %d.%d %d._ %d.%d %s: assertion failed getsubfont: can't open %s: %r getsubfont: can't read %s: %r New Cut Paste Snarf Sort Zerox Delcol can't find windowcan't find windowcan't find windowMoribundDeadExecForkRunningReadyRendezvousunknowntop of schedinit, _threadexitsallstatus=%ppausing, state=%sall threads gone; exitingrunning %d.%d%d.%d marked to diesleeping for more workt->state == Readyreadying %d.%dwaking process %dbad event syntax_fraddbox_frclosebox_frdelbox_frfreebox_frgrowboxdupboxtruncateboxchopboxR{PD?.*default*acme: %s: %r +Errorscan't create column to make error windowinitializing columns in flushwarnings()runevsmprint failed%.*S!"#$%&'()*+,-./:;<=>?@[\]^`{|}~strdup failedmalloc failedrealloc failedcan't make column#c/pid| _////004CutDelDelcolDeleteDumpEditExitFontGetIDInclIndentKillLoadLocalLookNewNewcolPastePutPutallRedoSendSnarfSortTabUndoZerox`argument string too long %c%d %d %d %d %.*S %c%d %d %d 0 %c%d %d 0 %d %.*S %c%d %d 0 0 %c0 0 0 %d %s %c0 0 0 %d %s %c0 0 0 0 %.*S:#%d%.*S:#%d,#%dcan't delete column; %.*S is running an external command %.*S is a directory; Zerox illegal no file name %s is a directory; can't read with multiple windows on it %s not written; file already exists %s modified%s%s since last read by can't create file %s: %r %s not written; file is append only %.*Scan't write file %s: %r no file name no auto-Put of %s: %r /mnt/acme/%d/ fixvarfixvarfix%S ONIndent ON OFFIndent OFF onoffon%.*S: Indent %s Indent %s %.*S: Tab %d %.*S%dwinid%child: can't mount /dev/cons: %r mount/mnt/acme/%d/rdsel/dev/null/mnt/acme/%d/editout/mnt/acme/editout/mnt/acme/%d/wrsel/dev/cons/dev/cons/dev/nullacmeaddr#;&|^$=`'{}()<>[]*?^~`%s/%s/bin/%s./bin/env/path%s '%s'/bin/rcrc-crunwaittaskbad length in 9P2000 message header,s4stnewproc == nilt->stk != nilt->stk != nil*default*q->size == t->sizeq->next==nil || q->next->prev==qq->prev==nil || q->prev->next==qq->magic==FREE_MAGICt->magic==FREE_MAGICa < t->size && t->size < bt->next==nil || t->next->prev==tt->prev==nil || t->prev->next==tt != nil (*t)->magic == FREE_MAGICnode != nil node != nil *loc == nodeolst != nil B2NB(a) == bb->magic != FREE_MAGIC b->size >= dsize2bsize(p, dsize)b->size - dsize < 0x10000newarena %lud pool too big: %lud+%lud > %lud memory pool too largebot->aup == top && top > botpool %s block %p hdr %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux tail %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux | %.8lux %.8lux user data %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux | %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %s %s pool panicbad magiccorrupt tail magiccorrupt tail ptrcorrupt tail magiccorrupt tail ptrdangling pointer writebad arena sizebad arena tail sizeblock too bigmem user overflow (magic0)corrupt tail magic0corrupt tail magic1corrupt tail ptrtoo much block datamem user overflowmem user overflowdon't call me when pool->move is nil B2D called on unworthy blockD2B called on non-block %p (double-free?)invalid allocation sizea->size >= nbsizepoolalloc %p %lud = %p poolcompact %p poolrealloc %p %p %ld = %p poolfree %p %p dsize >= getdsize(b)poolmsize %p %p = %ld found wrong tailpool %p %s (%p %.8lux %lud) /tmp/X%d.%.4sacmeacme: can't create temp file: %r diskinitinternal error: ntosizewrite error to temp fileinternal error: diskreadread error from temp filecouldn't parse wait message?mainRendezvous with thread %d.%dRendezvous for tag %ludWoke after rendezvous; val is %lud_frcanfit can't?$@Y@@@@j@.AcAחAeA _BvH7BmB@0BļB4&k C7yAC؅W4vCNgmC=`XC@xDPKDMDJ-DyCxD(,* E52TEqىE/'E!1Eꌠ9Y>)F$_FnFF"F|FMraB3G yhGiWCG*GJH\)c=H7]rHaxHyֲHL}YI\CkFI3T|I\'Isȡ1I:~^Jd~QJvaJ0}GJ>nllJ$KAjZK=P1PKMZ>dKW`M}Kmn/LDcL^LpuLafirM?O8MrbnMG9Mz)M:Ft NdȋBN=ֺ.wN 9iNCNuOILLO֯OO[пOE!P/'%UP_QP6PbDP{U[*QmUx`Q*4VQz5߼QlX R.4R9mr"iRY) kRعeR$N(Sa򮌮>S }W-sSO\]ScbuSp] T%L9hGT.B}T}Ô%IT\nTsqUFQU`RUxӫU?+dpU5=%VN=@[Vҟ&VG0JV=:YVf$0W&sdWW)>W]3sMXk5!a9XBioX)8ӣX*4X5AHxY(-\CYr%4xYv/AYiY?ZOMZ20HwZ~$|7Z-[bZXC}"[;/V[ ;C-[SJ[= \[M"4+\0IΕ2a\|AH\[R\ysKp]WPM4]mH=j]Į]-f]u8W]am ^|M$D@^`-Ut^x^WUH^P.5_[ypH_r]~_':__ k_EW`RVR`'.N`(:W"`Yv5`assert failed: %s {[(<'"`height != 0 /mnt/plumb/%s/mnt/term/mnt/plumb/%splumbsrv/mnt/plumb/mnt/plumb/%s '= =%d /guide+Errors Del Snarf Undo Redo Put Get | Look %s: not a directory %s: %r %.*S modified unnamed file modified %11d %11d %11d %11d %11d %11d %sno window ownervsmprint failed/env/%s$@ a$|bdK|c}dR~eBfgp i܆mVprBstVuuvp w$xp̎yp̎=dBd|DdXfYf<d|d>dcan't find text in filedeltextinternal error: fileinsertinternal error: filedeleteundo in file.load unimplementedundo: 0x%ux @@ !@LO#p/%d/argsm%d %dkillreadmouse: %r mouseprocmouse: bad count %d not 49: %r read error/dev/mouse/dev/mouse#m/dev/cursor/dev/cursor/lib/font/bit/lucidasans/euro.8.font/lib/font/bit/lucm/unicode.9.fontusage: acme -a -c ncol -f fontname -F fixedwidthfontname -l loadfile usagecputypeobjtypehometabstopfont/dev/snarf/acme/bin/%s/bin/acme/bin/binacmeacme: can't open display: %r geninitdrawacme: can't create initial channels: %r channelsacme: can't initialize mouse: %r mouseacme: can't initialize keyboard: %r keyboardeditsendinitializing columns/guidedeletehangupkillexitkillexitacme: %s hangupacmeerrorproccan't create pipe/srv/acme.%s.%dcan't create acmeerror file%d/fd/%dcan't re-open acmeerror fileplumbprockeyboardthreadmousethreadattach to windowtextactionshowfileshowdatawaitthread%skillkill %S: %r Kill: no process %S libthread%s %s xfidallocthreadnewwindowthreadcan't open font file %s: %r internal error: can't find font in cache /dev/snarf%.*Screadimage: bad formatcreadimage: bad channel string %screadimage: bad ldepth %dcreadimage: bad rectanglecreadimage: bad maxy %dcreadimage: bad count %dmmmmmmn0bad address syntaxbad channel descriptorbad channel descriptorallocimage: %sallocimage: %rname too longnamedimage: %snamedimage: %rbad channel '%.12s' from devdrawscroll alloc/dev/bintime/dev/nsecchaninit %pchancreate %pc==(Channel*)~0nbERROR: channel alt returned %d expected channel with elements of size %d, got size %d(c->nentry==0 && c->qentry==nil) || (c->nentry && c->qentry)realloc channel entries: %rQueuing alt %p on channel %pDequeuing alt %p from channel %pcan rendez alt %p chan %pcan buffer alt %p chan %pbuffer recv alt %p chan %pbuffer send alt %p chan %prendez %s alt %p chan %p alt %precvsendunlocking the chanlockchanlock is %ludfont cache resize failed: %r resize: init failed: %r bBnqUXY!no current windowdot extends past end of buffer during { commandunknown command %c in cmdexecpermission deniedunknown state in edittextno such file %.*Scan't open %s: %r%s is a directory%s: NUL bytes elided internal error: g_cmd f!=addr.f bad regexp in g commandmove overlaps itselfbad regexp in s commandreplacement string too longright hand side too long in substitutionno substitutioncan't write file with pending modificationsno name specified for 'w' commandno command specified for %c%.*S:%lud,%lud #%d,#%d newline expected%S%c%c%c %.*S ' .bad regexp in %c commandcan't nest %c commandYXbad regexp in command addressno match for regexpaddressno match for regexpaddresscan't handle 'addresses in different filesaddresses out of ordercmdaddressno such file"%S"too many files match "%S"no file matches "%S"bad regexp in file match%c%c%c %.*S ' .address out of rangeaddress out of rangeaddress out of rangewarning: duplicate file name "%.*S" %s: %s %s out of memory_frinsurerdsubfonfile: header read error: %rrdsubfonfile: fontchar read error: %rinterruptAxfidctlthreadcan't create temp file%.*Scan't write temp file for pipe command %r unknown qid %d %11d %11d I/O error in temp fileunknown qid %d in readunknown qid %d in writelockunlockcleandirtyshowname nulls in file namebad character in file namedump nulls in dump stringdumpdir nulls in dump directory stringdeletedelfile dirtygetputdot=addraddr=dotlimit=addrnomarkmarknoscrollcleartagscroll%.*Sbad count in utfrune%.*Swindow shut down%.*STversion tag %ud msize %ud version '%s'Rversion tag %ud msize %ud version '%s'Tauth tag %ud afid %d uname %s aname %sRauth tag %ud qid (%.16llux %lud %s)Tattach tag %ud fid %d afid %d uname %s aname %sRattach tag %ud qid (%.16llux %lud %s)Rerror tag %ud ename %sTflush tag %ud oldtag %udRflush tag %udTwalk tag %ud fid %d newfid %d nwname %d %d:%s Rwalk tag %ud nwqid %ud %d:(%.16llux %lud %s) Topen tag %ud fid %ud mode %dRopen tag %ud qid (%.16llux %lud %s) iounit %ud Tcreate tag %ud fid %ud name %s perm %M mode %dRcreate tag %ud qid (%.16llux %lud %s) iounit %ud Tread tag %ud fid %d offset %lld count %udRread tag %ud count %ud Twrite tag %ud fid %d offset %lld count %ud Rwrite tag %ud count %udTclunk tag %ud fid %udRclunk tag %udTremove tag %ud fid %udRremove tag %udTstat tag %ud fid %udRstat tag %ud stat(%d bytes) stat Twstat tag %ud fid %ud stat(%d bytes) stat Rwstat tag %udunknown type %d'%s' '%s' '%s' '%s' q (%.16llux %lud %s) m %#luo at %ld mt %ld l %lld t %d d %d%2.2uxsbrkmem/env/MALLOCFDcanlock(&pv->lk)==0panic: panic: bad height or ascent in font fileillegal subfont range.acmeconsconsctldraweditoutindexlabelnew.addrbodyctldataeditouteventrdselwrseltagWile E. Coyotecan't create pipe/dev/time/dev/useri/o error on server channelconvert error in convM2Sbad fcall typefsysdelid: can't find id %d %d/mnt/acme/mnt/acme/mnt/wsys/mnt/acme/devconvert error in convS2Mwrite error in respondversion: message size too small9P2000unrecognized 9P version9P2000acme: authentication not requiredunknown id in attachwalk of open filenewfid already in use..name too longneww set in walk to new%dNaN+Inf-Infe%de%de%d yz|      address out of range_threadexitsallstatus set to %pthreadintfile does not existkillkbdprockeyboard read error: %r error/dev/cons%sctlinitkeyboard: can't open %s: %r rawoninitkeyboard: can't turn on raw mode on %s: %r 0123456789+-/$.#,;^+-.*?#,;[]()$address out of range no previous regular expression no match for regexp already in useBinits: unknown mode %d Bopen: unknown mode %d Aoff end in frdelete_frcanfit==0frptofchar in frdeletepermission denied 8etextMchan.cfrutil.c4argtext8NclassDmouse.cmouse.hmount.s_sched.clibframe.a`omode$1pow10.cendmalloc.cmemccpy.saddresst.frame$pat npatnc,evalp limmd4nrt.ret0qp(getcaprevc$q1sizedir q q0,rar AB           GH   - . Esizecache.framebnF-addblock.framenibFdelblock.frameibGzflush.framebGsetcache,.framebl iblpqbq0I7bufinsert,.frameism toff nq0bLPbufdelete$.framenq1bq0M:bufloader.frame nrrq0vMiloadfileD.frame fargnullsnrnbnfd q1q0mrpNbufload.frame nullsfdq0bObufread.framems nq0bObufreset.frameibP$bufclose .frameb  AB           GH   - . PucolinitH.frame.safetr1crRwcoladd\.frameclonew r1,t$v(i@.saferc yWkcolcloseD.framedofreericwYcolcloseall.frameicZ{colmousebut,.frame.safecZcolresize\.frame$r20.safei(wrr1c]:colcmp.frameba]colsortd.frame(r1i4wr0wp,rpc`colgrow.frame8y2Xv4y1(j,kPdnll.safeDtot@nyrxinit.frameLsregerror.frameeLnewinst .frametM realcompile.frameargNrxcompile,.frame oprogpi.safenrrOoperand.frametP!operator .frametPpushand .framelfQ#pushator .frametQpopandX.frame@bufopQpopator .frameRevaluntil(.frameinst1op2inst2op1priToptimize.framestartTstartlex.framesUlex .framecUnextrec .frameVybldcclass(.framec1na nclasspXclassmatch .frameclassnocnegateXaddinst .framelinstsepXrxnull.frameXrxexecute@.framerpinst tlpntl c eofrnctnnl$wrapped(startcharpstartpflag^newmatch.framesp^Vrxbexecute<.framerpinst tlpntlctp$startcharstartp wrappednnlflagbbnewmatch .framesp, AB           GH    QR x y c.rowinitH.frame .safetr1rowrerowaddX.framec r1$d(i0.safexrrowhrowresize`.frame,r2i0cr1 odxdxrrowjrowdragcol`.framer0dx$pirowc,opbmrowcloseD.framedofreerirowcorowwhichcol,.framecpirowp?rowwhich(.framerowpprowtype,.framewrtprowqrowclean.frameicleanrowrrowdumpt.framenq0 q1mdumped,aDt0fontname ? textinit(.framecolsrfrfttextredrawD.frameodx.safemaxtrrbfrttextresize0.frameodx .saferttextclose.framethdircmp(.framedbdabatextcolumnateD.frame w$dl(q1ji nrow0.safecolwdlpndlmaxtmintt1textload\.frametextselect3$.frameq1q0t˂textdoubleclick8.frame .safec rilqtq1q0textclickmatch.frameclcr dirnestqtΣtextbacknl.framejitpnItextsetorigin@.frame .safern fixupiexacttorgPtextreset.framet1 AB           GH   - . msec,.frame.safe,timerstop.framet@timercancel.frametLtimerproc<.framedelxidt oldnt nattimerinit.frame^timerstart.framedtt2 AB           GH   - . +cvttorunes$.framenr nbqsn jrpnullsxerror.framesկerrorwin1,.frameincl i ninclwdirrnndirxerrorwin .frameownerwmdaddwarningtext .framewarnnrrmdflushwarningsH.frame nnrq0$rt wownerwarnnextwarning$.framemd.saferargsہruneeq.frames2s1 n2n1min.framebamax.framebarunetobyte$.frame .safesnrebytetorune0.frameip nrrnbsisalnum.framec3rgetc.framevn@tgetc.frameanlskipbl.framenprnݥfindbl.framenprnsavemouse.framewrestoremouse.framewAclearmouse.frameLestrdup.frame .safetsޝemalloc.frame .safepn erealloc.frame.safenpcmakenewwindow@.framew(.safeit3 AB           GH   - . wininit`.frame br(rf$f,rp0ncclonerr1wDwinresizeX.frame0.safe(brsafeyrr1wQwinlock1 .frameownerwwinlock.frameownerfiwwinunlock.framefiwvwinmousebut,.frame.safewwindirfree.framedliwwinclose.frameiwJwindelete.framexwwinundo,.framebodyisundowwinsetname(.framev innametwwintype.framewirtwincleartag(.framei rnwwinsettag1p.frame@brbarj0q1,q0 knewi oldwwinsettag.framefiwwincommit(.framerw fitwinaddincl,.framewrsdanrwinclean.frameconservativewwinctlprint,.framefontsbufwwinevent(.framenbargfmtw4 AB           GH   - . Aclampaddr.framewxfidctl.framexargxfidflush<.framefcxw cij4xfidopen\.frame$m(ns r4q10q0,qfc@.safewtxxfidcloseD.frametq(.safewfcxnxfidread.frame bnbufoffwfcqx.safe *xfidwrite.framelbufcnt qtq1tq0q0perr$nba,evalxr(nrtfcqidtwx.safexfidctlwritex.framefc$nr nbbfrinsert.frame0pt\rhq1dq0Dy8n`nptst.safe4b(ppt1pt1opt0 ppt0 AB       ygG_frcanfit,.frameprw nrleftptf bh=_frcklinewrap.framepfbht_frcklinewrap0.framebpfh_fradvance.framepbfh_frnewwid.frame bptfi _frnewwid0.frame.safept bfi_frclean$.framebptnbn1 n0 cf 56? AB       /j_frallocstr.framefpnk>_frinsure$.frame.safepnbbnf 5@A AB     hl_stringnwidth.framerunelsubfontnamewidcbuf nmaxtwidrptrrsptrsf lenn]stringnwidth.framelensfnstringwidth.framesfnstringsize.frame.ret.safesforunestringnwidth.framelenrfo/runestringwidth.framerfo[runestringsize.frame.ret.saferf 5@B AB     ostringT.frame sfspsrcpt.retdstpCstringopT.frame$op sfspsrcpt.retdstpstringnT.frame$len sfspsrcpt.retdstqqstringnopT.frame(op$len sfspsrcpt.retdstrrunestringT.frame rfspsrcpt.retdstrrunestringopT.frame$op rfspsrcpt.retdsts6runestringnT.frame$len rfspsrcpt.retdstsrunestringnopT.frame(op$len rfspsrcpt.retdsttc_string.frame.ret@bgpsp,cliprptsrc_freeimage1 .framedi!freeimage.framereti 5@S AB     Hallocscreen(.framepublic idtrysdfillimagepublicscreen.framechaniddsʙfreescreen.frameds5allocwindow(.framevalrefrsp_allocwindow<.framerefvalristopbottom$.frametop.safenwbottomwindow.framew;topwindow.framewpbottomnwindows.framenw͓topnwindows.framenwͷoriginwindow4.frame delta scrlogw 5@T AB     4xsubfontname.frameiHtmp1fnametmp2tmaxdepthcfname 5@U AB     /3lookupsubfont.framednameўinstallsubfont .framesubfontnameuninstallsubfont.framesubfont 5@V AB     $allocsubfont.framefiinfo ascentnnameheight 5@W AB     Bұreadsubfontil.framemloadimage@.framedatan bpldyndata.saferchunki 5@Z AB     freesubfont .framef 5@[ AB     y?creadimage.frametaXnb`maxy\minypbuflncblockxiddolockPr|chandnew=hdrfd 5@\ AB     .'_twiddlecompressed.framenbufc_compblocksize$.framedepthr 5@] AB     *unitsperline.framerbitsperunitdwordsperline .framedratoi .frames 5m| AB  7:Qatexit.framefi:atexitdont.framef;exits.framesipid 5m| AB  ;^abs.framea;olabs.framea 5m| AB   ;_assert.frames 5m AB   5mV;fmtStrFlush.framensfsprint$.frameargsbuffmt 5m AB   >snprint$.frameargslenbuffmt 5m AB   >smprint.frameargsfmt 5m AB   5mV>runeFmtStrFlush.framensf?runefmtstrinit.framenf@runevsmprintH.framefmtargs0f 5m AB   @runesmprint.frameargsfmt 5m AB   @print .frameargsfmt 5m AB   @fprint .frameargsfdfmt 5m AB   5mVr@fmtvprint.framefmtargsvaf 5m AB   5mVsAcfmtprint.framefmtvaf 5m AB   5mVrAfmtfdflush .framefAfmtfdinit.framefd sizefbuf 5mJ AB   5mVB/_fmtinstall.framecfBfmtinstall.frameretfcBfmtfmt.framepcCc_fmtdispatch .frame.saferunefmtfisrunes 5m AB    5mtE2xadd.framevanExsub.framenvaExdtoa|.frameDc2Lc4Hc3@c18is1,h4dh.safe$g0eXchrPucaseTsigns2f\precfmtN_floatfmtL.frame4.safef(sfmtN_efgfmt .framedf 5m AB   5mV_Nerrfmt.framef.safebuf 5m AB   5mV[Odofmt8.framenstruner rsrt nfmtffmtQh_fmtflush .framelenftQ_fmtpad$.framen ifRI_rfmtpad$.framen ifR_fmtcpyH.frame*_runest r$ncrsrtn fl(wfme szmvmV_fmtrcpyD.frame&_runerts men fl$wmvmfY?_charfmt.framexfY_runefmt.framexfYfmtstrcpy0.frame.safei rjsfZ_strfmt.framefZfmtrunestrcpy(.framesf[H_runesfmt.framef[i_percentfmt.framexf[_ifmt.framehi.safelnLpCbufxisvdbasePconv\uXvupflf`negb_countfmt.framefb_flagfmt.framefc_badfmt .framexf 5mceunmount.frame 5mcmsleep.frame 5m cuseek.framea 5mcrfork.frame 5mcrendezvous.frame 5mcremove.frame 5mcpwrite.frame 5mcpread.frame 5mcpipe.frame 5mcopen.frame 5mcnotify.frame 5mcnoted.frame 5mcmount.frame 5mcexec.frame 5mcerrstr.frame 5mcdup.frame 5mccreate.frame 5mcclose.frame 5mdchdir.frame 5md bind.frame 5md_exits.frame 5m AB  dwrite.framenbuffd 5m AB   dNwerrstr.frameargbuffmt 5m AB   dwait8.frame wlfldbuf 5m AB  e_sysfatalimpl.frameargfmtbuffNsysfatal.frameargfmt 5m AB  !frbrk.frameblpfsbrk.framenbl 5m AB   frerrstr.framenbufbuftmp 5m AB   gQread9pmsg$.framelenn buffdabuf 5m AB  hread.framenbuffd 5m AB  fh;_qlockinit.framerhFgetqlp.framepophqlock.framempqi*qunlock.framepqicanqlock .frameqirlock.framempqjxcanrlock .frameqjrunlock.framepqk^wlock.framemppqkcanwlock .frameqlEwunlock.framepqmHrsleep.frametmernCrwakeup.framernrwakeupall.frameri 5m AB  oputenv.framelsvalfhenamename 5m AB  +pprivalloc.frameppprivfree .framep 5m AB  ppostnote.framernotefgroupfilepid 5m AB  @qbe2vlong.frameftornsecD.frame(.saferetriest b.ret 5m AB  siounit.frameargsicfdfdbuf 5m AB  tgetwd .framennbufbuffd 5m AB  ugetpid0.framefb 5m AB  !ugetenv.frameans sf.safe|enamename 5m AB   tw-fcallfmt .framedqpi.safetmptagffmtebufaqidtype .frametsdirfmt.framefmtbuf fdirconvh.frameebuf.safedtmpdumpsome(.framei.safe peansbuf count 5m AB   3dirstat0.framebufnamedind 5m AB   dirpackage4.framennsm inssdbuftsdirread .framedtsbuffddirreadall(.framed nfdtsbuf 5m AB   dirfstat0.framebuffddind 5m AB    pstring.framenpspqid .frame.safeqpstringsz .frames3sizeS2M.frame.safeinfconvS2M0.frame.safepiapnap sizef 5m AB   gstring.framesnpepTgqid0.frame.safeeppqbconvM2SD.framep,.safe iepsizenapapf 5m AB   Hstatcheck.framebufnbufconvM2DT.frame nssvi strsp<.safeebufdbufnbuf 5m AB   ?sizeD2M(.frameinssvdconvD2MX.frame@.safe4ss0nsvi nssvebufpnbufdbuf 5m AB  access.framedbnamemode 5m AB  abort.frame 5m%_tas.framel 5m61memccpy.framep1cp2 n 5m| 1stoupper.framectolower.framec 5m| AB  iqtoken.frametsepquotingsOetoken.framesepquotingtgettokens.frameargs sepmaxargsnargss@tokenize.frameargsmaxargsnargss 5m| AB  cstrtoll`.frame.retendptrnn(vmD.safep$ovfl0ndig n,negnptr base 5m| AB   $HstrtodH.frameLdpoolalloc$.framevnpIpoolcompact.framervppoolrealloc(.framenvnvp+poolfree.framevp"poolmsize(.framebdsizevppoolcheckarena.framebpatailapoolcheckl.frameaplpoolcheck .frameppoolblockcheck.frame.safevppooldumpl .frameap?pooldump .framepjpooldumparena$.framebapmemmark.framesigvsize 5m| AB  4NaN.framea8isNaN .frameadInf.framesignaisInf.frameadsign 5m| AB  gfrexp.framexdepUldexp(.frame.safexddeltaemodf$.framexdip 5m AB  [vsnprintD.framefmt args0fbuflen 5m AB   seprint$.frameargsebuffmt 5m AB  _fmtlock .frame_fmtunlock .frame 5m'stat.frame 5m/fstat.frame 5m7fd2path.frame 5m?brk_.frame 5mGawait.frame 5m Oumuldiv.framecba\muldiv.framecba 5 AB   2iplumbopen.framebufomodenameStrlen .frames Strcpy.frame.safestFquote.framesplumbpackattr(.framet s.safeanattrplumblookup.framenameattr1plumbpack4.framenpp bufn(.safeattrndatamplumbsend .framebuffdnmPplumbline .framelineppstartibadi nbufplumbfree.frameanextmplumbunpackattr4.framev aqattrprevpplumbaddattr .framenewattrplumbdelattr.framelnameattrprevplumbunpackpartial4.frameintextattrnbuf badmorepmplumbunpack.framenbuf!plumbrecv(.framemore nmbuffd 5 AB   KsBwrite.framenoc.safeerrbufccountpapbp 5 AB   KBrdline<.frameepj.safe idelimipbpBlinelen.framebp 5 AB   K]Bputc.framecbp 5 AB   K_>Bputrune$.frame nstrbprunec 5 AB   KWBprint .frameargbpfmt 5 AB   Kbatexit.framei+deinstall .framebpYinstall.framebpBinits.framef pbpmodesize{Binit.framemodefbpBopen(.framebpfnamemodeBterm .framebp 5 AB   KwBgetrune$.frame runeistrbpBungetrune.framebp 5 AB   KjBflush4.frame.safenbp 5 AB   KmfmtBflush.framebpfBvprintH.framefmtarg4fbp 5 AB   K~jBgetc0.frame.safeibpBungetc.framebp 5 AB   /longestprefixlength .frameb rbwa rani6freecompletion .framec]strpcmp.framevbva|completeP.frame,pnbytes minlennfile4c0mode(nameilen n$dirpfddirs 5m| AB  @strstr(.framec0s2s1 5m| AB  strpbrk .framemapbcbscs ] "iB HCEFCGi!AaEBVMLAG[AKQP"BI ABC%TKLDQ^ g S B) "h MGDR^0zBS`I| `WTOK  KZBHQEIXBNJ C bDIHFʆA +vC"QAHEBAGAK]CC*BHCJFbGZgBh*j&BF9&o QPLo/`h DAMEDK1Emh`%M GI LNFH A΂PTHOD8E]BK G ED AK I DACNH APQWbgK O~AAEFcFGGJ>EABD KA (COEiVMJPIE8FHDHE WBQNA$DE EFA  OYAT M$aACiQCN?DBCACGEGO!J FQIK EG BCWTI BFU%NOACA L F KA|>F*z:BHG`FKn/pRBB:H J EB AFBoDJD CANC IGEVL3BE  JA PBAAD FAABNLCCAAA  J NABC _][XeQDY;HO L}LLBEGCU WYBBQ`L BNBNJ1CEK G ROH J OHFG  8uEMGEER%B .IGENBIUQABAD OAF R DH ABBBAENJR`K1EIJ DDACLb0  ?~0r%l B\cUGEGQAAEFCdTC/I FZGI* LBBMO CDCF DWKBMVMTEBAIGJOKJJ KHAC  6H J ${DqTAJX$MAFņM F E?Gm.AdAF MEfA#v2I<SQXAGNNI MQ R?|VAl0y5Bd*g]\ JJI  BB RKVFB-CIDr /,jGN_oABAEWHzMIPBNDwAR$a EnkBRCFADYWD C B *l Wb ^GPAMAM OBEgDAAAKIBHIDB CB N`VDB\0QNUzBKDAAEHA;nMcRXj;Gz-qBAD)PGc"M BW_SBB\h&BTJ QS[DE D C7sHm  ZIAV.Bv)c_Ek)f$]ZL VHTlA  ZE8tHV.Cw)c_El*g%^XKIlBCH\D'M G AAK `J(DWCFKA xpDABFBTBF M:A[O O\JDSFm IEF Ҏ0ʁ:ȪE>Ą@EFr3G+h\J AtGfHJEgr _A XGDFABBOADAYM])HKI H TMPEJ% VOMPH ZT|DgD NFHG!F I])RYG"F  PDd/C IN DF AA(iOABJbfF)KPPXDnVAEECnGCBGYZBVV  J HB Aa!WIHVTALF[GAKABEF P RFXFMP' UDWFBU6AAACCW ABAMLNO  HAH LK VCE Rg G I DK DBHC Ec%C BEEB A AFGCaCDHS T]GAFG CAEF CEC AAM AE^JTFGZ\BCAA_ ELBC KBK JMCG=BN pVJ)'cYQ$BFL   AG@Au*y9  ]BVT Jf& BwseeD XCLB6w{jBDb@Bz A]H\irzyiJ_WfMETAA~9w5s1o(f#aZVPL GCT A5KElAA AZ l&V^A ^ ]e) `YEJ  BBCBWL AOE AHTFBTU7 GFGGP [FJ Y^E+k hJLP &FG A d8AKEL^S Pt.q[_`Xa A  AGFEDGWa ^D}EAD'DjdKAAQOSUAA\AAECBDBCD CDCAAnH F BCAG H CNC BE^ JQGF* 9y,iAsUFENX GNCsLDI F Y#__ 4C A[ZJ QF FG\CELrszBDH;FVpD G BRz>,UQENL BG @eEHH iVH "`YPzNAGxBB IHGAACAG DL] LARQO\LODBGCNObB To$Ca%K CHCEVCA6DSKD^AQEMr  TE A`"uNFT\WoI K h*XP LSMETMAG^CI +A`$QBBKIaAF  CB CE GE QELJAAABBLBJE3A\EI DB EAHAC}QTYi,Sb6IBEgF  CDFEBLBBAE AAAOEYC*BOJX  BGAEBLFABLGLAIKW!DkC(sM`K_iOaX KgUBD^ROIC ACLaM*S IEDDLLBAv3}:o,BQh%L BXaDAFLJFLJGNBM   QPCETFQOWHN  TR DG[J`ZWHQAHADLOHMCOGF ^ C EFHBfBGAA[A^ GPT8DAFBB GCCAbAJ BXD AOOJICATAPKI TADA%gEABACClJOlJBAHGNlHHTQTMEDD-nL(Lh*k P BNDG^^^G KG BB C$ VDDD QHGQHC|Ln J rDIzD lGC XCABBF ACACH UGDKH EHDKX-AODD BEBENAAHFHEBHJCAHCKCAOHYGU RBMEpVUQPL K GEB_ IGEB\VSPYBK EJBBGBBBBBBBBBBBBBBBBBB\AM]GaKH IKENILEIDGS LACg CS MA NAoRMJBv wosvkBcg`BAEZ]CMDHABt5y9}=q0Bh*m-g'B\a!d$[WBCPL   KDOAB8P]H HUE#AFIP\ ru4|i)Bc%kp0BATxa BY^WBTQJBEQBAGB};\>]s3Bm/oz:BzA]kA%_xnBYeZBTTBOIBFԈC-сTHCB4PCHBo݄}B4k(W`Cap{zCrYCkNC^ЊCMMFBEH oIZCYFAA׆BUH OT BOH OSDFA DFA  TR DG[J`ZWHAE"niABECAZArBVAVAXw}|rCdFc#_n0q+ }9yGDZV NBBRCAAAVEYDLDFC ELH"ɂFF HH JHEKN IQEFH KDJHVG ALC OHCGm5BOIN='zBF7dFCP [ NME%AFQ  Ki(COYS L5BBCEFEBCEFEBBCEFEABCEFEBBCEHCHECAAAAAAg I VV$COFBBBBB*FFAFC JFKAD?~0m J};cU IQSACACADHCJN HPFVJ^$NPDDBG LT L MBhGS [^]"CF C>F A AFaDA BF HN#include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" /* for generating syms in mkfile only: */ #include #include "edit.h" void mousethread(void*); void keyboardthread(void*); void waitthread(void*); void xfidallocthread(void*); void newwindowthread(void*); void plumbproc(void*); Reffont **fontcache; int nfontcache; char wdir[512] = "."; Reffont *reffonts[2]; int snarffd = -1; int mainpid; int plumbsendfd; int plumbeditfd; enum{ NSnarf = 1000 /* less than 1024, I/O buffer size */ }; Rune snarfrune[NSnarf+1]; char *fontnames[2] = { "/lib/font/bit/lucidasans/euro.8.font", "/lib/font/bit/lucm/unicode.9.font", }; Command *command; void acmeerrorinit(void); void readfile(Column*, char*); int shutdown(void*, char*); void derror(Display*, char *errorstr) { error(errorstr); } void threadmain(int argc, char *argv[]) { int i; char *p, *loadfile; char buf[256]; Column *c; int ncol; Display *d; static void *arg[1]; rfork(RFENVG|RFNAMEG); ncol = -1; loadfile = nil; ARGBEGIN{ case 'a': globalautoindent = TRUE; break; case 'b': bartflag = TRUE; break; case 'c': p = ARGF(); if(p == nil) goto Usage; ncol = atoi(p); if(ncol <= 0) goto Usage; break; case 'f': fontnames[0] = ARGF(); if(fontnames[0] == nil) goto Usage; break; case 'F': fontnames[1] = ARGF(); if(fontnames[1] == nil) goto Usage; break; case 'l': loadfile = ARGF(); if(loadfile == nil) goto Usage; break; default: Usage: fprint(2, "usage: acme -a -c ncol -f fontname -F fixedwidthfontname -l loadfile\n"); exits("usage"); }ARGEND cputype = getenv("cputype"); objtype = getenv("objtype"); home = getenv("home"); p = getenv("tabstop"); if(p != nil){ maxtab = strtoul(p, nil, 0); free(p); } if(maxtab == 0) maxtab = 4; if(loadfile) rowloadfonts(loadfile); putenv("font", fontnames[0]); snarffd = open("/dev/snarf", OREAD|OCEXEC); if(cputype){ sprint(buf, "/acme/bin/%s", cputype); bind(buf, "/bin", MBEFORE); } bind("/acme/bin", "/bin", MBEFORE); getwd(wdir, sizeof wdir); if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){ fprint(2, "acme: can't open display: %r\n"); exits("geninitdraw"); } d = display; font = d->defaultfont; reffont.f = font; reffonts[0] = &reffont; incref(&reffont); /* one to hold up 'font' variable */ incref(&reffont); /* one to hold up reffonts[0] */ fontcache = emalloc(sizeof(Reffont*)); nfontcache = 1; fontcache[0] = &reffont; iconinit(); timerinit(); rxinit(); cwait = threadwaitchan(); ccommand = chancreate(sizeof(Command**), 0); ckill = chancreate(sizeof(Rune*), 0); cxfidalloc = chancreate(sizeof(Xfid*), 0); cxfidfree = chancreate(sizeof(Xfid*), 0); cnewwindow = chancreate(sizeof(Channel*), 0); cerr = chancreate(sizeof(char*), 0); cedit = chancreate(sizeof(int), 0); cexit = chancreate(sizeof(int), 0); cwarn = chancreate(sizeof(void*), 1); if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){ fprint(2, "acme: can't create initial channels: %r\n"); exits("channels"); } mousectl = initmouse(nil, screen); if(mousectl == nil){ fprint(2, "acme: can't initialize mouse: %r\n"); exits("mouse"); } mouse = mousectl; keyboardctl = initkeyboard(nil); if(keyboardctl == nil){ fprint(2, "acme: can't initialize keyboard: %r\n"); exits("keyboard"); } mainpid = getpid(); plumbeditfd = plumbopen("edit", OREAD|OCEXEC); if(plumbeditfd >= 0){ cplumb = chancreate(sizeof(Plumbmsg*), 0); proccreate(plumbproc, nil, STACK); } plumbsendfd = plumbopen("send", OWRITE|OCEXEC); fsysinit(); #define WPERCOL 8 disk = diskinit(); if(loadfile) rowload(&row, loadfile, TRUE); else{ rowinit(&row, screen->clipr); if(ncol < 0){ if(argc == 0) ncol = 2; else{ ncol = (argc+(WPERCOL-1))/WPERCOL; if(ncol < 2) ncol = 2; } } if(ncol == 0) ncol = 2; for(i=0; i=row.ncol) readfile(c, argv[i]); else readfile(row.col[i/WPERCOL], argv[i]); } } flushimage(display, 1); acmeerrorinit(); threadcreate(keyboardthread, nil, STACK); threadcreate(mousethread, nil, STACK); threadcreate(waitthread, nil, STACK); threadcreate(xfidallocthread, nil, STACK); threadcreate(newwindowthread, nil, STACK); threadnotify(shutdown, 1); recvul(cexit); killprocs(); threadexitsall(nil); } void readfile(Column *c, char *s) { Window *w; Rune rb[256]; int nb, nr; Runestr rs; w = coladd(c, nil, nil, -1); cvttorunes(s, strlen(s), rb, &nb, &nr, nil); rs = cleanrname((Runestr){rb, nr}); winsetname(w, rs.r, rs.nr); textload(&w->body, 0, s, 1); w->body.file->mod = FALSE; w->dirty = FALSE; winsettag(w); textscrdraw(&w->body); textsetselect(&w->tag, w->tag.file->nc, w->tag.file->nc); } char *oknotes[] ={ "delete", "hangup", "kill", "exit", nil }; int dumping; int shutdown(void*, char *msg) { int i; killprocs(); if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){ dumping = TRUE; rowdump(&row, nil); } for(i=0; oknotes[i]; i++) if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0) threadexitsall(msg); print("acme: %s\n", msg); abort(); return 0; } void killprocs(void) { Command *c; fsysclose(); // if(display) // flushimage(display, 1); for(c=command; c; c=c->next) postnote(PNGROUP, c->pid, "hangup"); remove(acmeerrorfile); } static int errorfd; void acmeerrorproc(void *) { char *buf; int n; threadsetname("acmeerrorproc"); buf = emalloc(8192+1); while((n=read(errorfd, buf, 8192)) >= 0){ buf[n] = '\0'; sendp(cerr, estrdup(buf)); } } void acmeerrorinit(void) { int fd, pfd[2]; char buf[64]; if(pipe(pfd) < 0) error("can't create pipe"); sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid); fd = create(acmeerrorfile, OWRITE, 0666); if(fd < 0){ remove(acmeerrorfile); fd = create(acmeerrorfile, OWRITE, 0666); if(fd < 0) error("can't create acmeerror file"); } sprint(buf, "%d", pfd[0]); write(fd, buf, strlen(buf)); close(fd); /* reopen pfd[1] close on exec */ sprint(buf, "/fd/%d", pfd[1]); errorfd = open(buf, OREAD|OCEXEC); if(errorfd < 0) error("can't re-open acmeerror file"); close(pfd[1]); close(pfd[0]); proccreate(acmeerrorproc, nil, STACK); } void plumbproc(void *) { Plumbmsg *m; threadsetname("plumbproc"); for(;;){ m = plumbrecv(plumbeditfd); if(m == nil) threadexits(nil); sendp(cplumb, m); } } void keyboardthread(void *) { Rune r; Timer *timer; Text *t; enum { KTimer, KKey, NKALT }; static Alt alts[NKALT+1]; alts[KTimer].c = nil; alts[KTimer].v = nil; alts[KTimer].op = CHANNOP; alts[KKey].c = keyboardctl->c; alts[KKey].v = &r; alts[KKey].op = CHANRCV; alts[NKALT].op = CHANEND; timer = nil; typetext = nil; threadsetname("keyboardthread"); for(;;){ switch(alt(alts)){ case KTimer: timerstop(timer); t = typetext; if(t!=nil && t->what==Tag){ winlock(t->w, 'K'); wincommit(t->w, t); winunlock(t->w); flushimage(display, 1); } alts[KTimer].c = nil; alts[KTimer].op = CHANNOP; break; case KKey: casekeyboard: typetext = rowtype(&row, r, mouse->xy); t = typetext; if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */ activecol = t->col; if(t!=nil && t->w!=nil) t->w->body.file->curtext = &t->w->body; if(timer != nil) timercancel(timer); if(t!=nil && t->what==Tag) { timer = timerstart(500); alts[KTimer].c = timer->c; alts[KTimer].op = CHANRCV; }else{ timer = nil; alts[KTimer].c = nil; alts[KTimer].op = CHANNOP; } if(nbrecv(keyboardctl->c, &r) > 0) goto casekeyboard; flushimage(display, 1); break; } } } void mousethread(void *) { Text *t, *argt; int but; uint q0, q1; Window *w; Plumbmsg *pm; Mouse m; char *act; enum { MResize, MMouse, MPlumb, MWarnings, NMALT }; static Alt alts[NMALT+1]; threadsetname("mousethread"); alts[MResize].c = mousectl->resizec; alts[MResize].v = nil; alts[MResize].op = CHANRCV; alts[MMouse].c = mousectl->c; alts[MMouse].v = &mousectl->Mouse; alts[MMouse].op = CHANRCV; alts[MPlumb].c = cplumb; alts[MPlumb].v = ± alts[MPlumb].op = CHANRCV; alts[MWarnings].c = cwarn; alts[MWarnings].v = nil; alts[MWarnings].op = CHANRCV; if(cplumb == nil) alts[MPlumb].op = CHANNOP; alts[NMALT].op = CHANEND; for(;;){ qlock(&row); flushwarnings(); qunlock(&row); flushimage(display, 1); switch(alt(alts)){ case MResize: if(getwindow(display, Refnone) < 0) error("attach to window"); scrlresize(); rowresize(&row, screen->clipr); break; case MPlumb: if(strcmp(pm->type, "text") == 0){ act = plumblookup(pm->attr, "action"); if(act==nil || strcmp(act, "showfile")==0) plumblook(pm); else if(strcmp(act, "showdata")==0) plumbshow(pm); } plumbfree(pm); break; case MWarnings: break; case MMouse: /* * Make a copy so decisions are consistent; mousectl changes * underfoot. Can't just receive into m because this introduces * another race; see /sys/src/libdraw/mouse.c. */ m = mousectl->Mouse; qlock(&row); t = rowwhich(&row, m.xy); if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){ winlock(mousetext->w, 'M'); mousetext->eq0 = ~0; wincommit(mousetext->w, mousetext); winunlock(mousetext->w); } mousetext = t; if(t == nil) goto Continue; w = t->w; if(t==nil || m.buttons==0) goto Continue; but = 0; if(m.buttons == 1) but = 1; else if(m.buttons == 2) but = 2; else if(m.buttons == 4) but = 3; barttext = t; if(t->what==Body && ptinrect(m.xy, t->scrollr)){ if(but){ winlock(w, 'M'); t->eq0 = ~0; textscroll(t, but); winunlock(w); } goto Continue; } if(ptinrect(m.xy, t->scrollr)){ if(but){ if(t->what == Columntag) rowdragcol(&row, t->col, but); else if(t->what == Tag){ coldragwin(t->col, t->w, but); if(t->w) barttext = &t->w->body; } if(t->col) activecol = t->col; } goto Continue; } if(m.buttons){ if(w) winlock(w, 'M'); t->eq0 = ~0; if(w) wincommit(w, t); else textcommit(t, TRUE); if(m.buttons & 1){ textselect(t); if(w) winsettag(w); argtext = t; seltext = t; if(t->col) activecol = t->col; /* button 1 only */ if(t->w!=nil && t==&t->w->body) activewin = t->w; }else if(m.buttons & 2){ if(textselect2(t, &q0, &q1, &argt)) execute(t, q0, q1, FALSE, argt); }else if(m.buttons & 4){ if(textselect3(t, &q0, &q1)) look3(t, q0, q1, FALSE); } if(w) winunlock(w); goto Continue; } Continue: qunlock(&row); break; } } } /* * There is a race between process exiting and our finding out it was ever created. * This structure keeps a list of processes that have exited we haven't heard of. */ typedef struct Pid Pid; struct Pid { int pid; char msg[ERRMAX]; Pid *next; }; void waitthread(void *) { Waitmsg *w; Command *c, *lc; uint pid; int found, ncmd; Rune *cmd; char *err; Text *t; Pid *pids, *p, *lastp; enum { WErr, WKill, WWait, WCmd, NWALT }; Alt alts[NWALT+1]; threadsetname("waitthread"); pids = nil; alts[WErr].c = cerr; alts[WErr].v = &err; alts[WErr].op = CHANRCV; alts[WKill].c = ckill; alts[WKill].v = &cmd; alts[WKill].op = CHANRCV; alts[WWait].c = cwait; alts[WWait].v = &w; alts[WWait].op = CHANRCV; alts[WCmd].c = ccommand; alts[WCmd].v = &c; alts[WCmd].op = CHANRCV; alts[NWALT].op = CHANEND; command = nil; for(;;){ switch(alt(alts)){ case WErr: qlock(&row); warning(nil, "%s", err); free(err); flushimage(display, 1); qunlock(&row); break; case WKill: found = FALSE; ncmd = runestrlen(cmd); for(c=command; c; c=c->next){ /* -1 for blank */ if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){ if(postnote(PNGROUP, c->pid, "kill") < 0) warning(nil, "kill %S: %r\n", cmd); found = TRUE; } } if(!found) warning(nil, "Kill: no process %S\n", cmd); free(cmd); break; case WWait: pid = w->pid; lc = nil; for(c=command; c; c=c->next){ if(c->pid == pid){ if(lc) lc->next = c->next; else command = c->next; break; } lc = c; } qlock(&row); t = &row.tag; textcommit(t, TRUE); if(c == nil){ /* helper processes use this exit status */ if(strncmp(w->msg, "libthread", 9) != 0){ p = emalloc(sizeof(Pid)); p->pid = pid; strncpy(p->msg, w->msg, sizeof(p->msg)); p->next = pids; pids = p; } }else{ if(search(t, c->name, c->nname)){ textdelete(t, t->q0, t->q1, TRUE); textsetselect(t, 0, 0); } if(w->msg[0]) warning(c->md, "%s\n", w->msg); flushimage(display, 1); } qunlock(&row); free(w); Freecmd: if(c){ if(c->iseditcmd) sendul(cedit, 0); free(c->text); free(c->name); fsysdelid(c->md); free(c); } break; case WCmd: /* has this command already exited? */ lastp = nil; for(p=pids; p!=nil; p=p->next){ if(p->pid == c->pid){ if(p->msg[0]) warning(c->md, "%s\n", p->msg); if(lastp == nil) pids = p->next; else lastp->next = p->next; free(p); goto Freecmd; } lastp = p; } c->next = command; command = c; qlock(&row); t = &row.tag; textcommit(t, TRUE); textinsert(t, 0, c->name, c->nname, TRUE); textsetselect(t, 0, 0); flushimage(display, 1); qunlock(&row); break; } } } void xfidallocthread(void*) { Xfid *xfree, *x; enum { Alloc, Free, N }; static Alt alts[N+1]; threadsetname("xfidallocthread"); alts[Alloc].c = cxfidalloc; alts[Alloc].v = nil; alts[Alloc].op = CHANRCV; alts[Free].c = cxfidfree; alts[Free].v = &x; alts[Free].op = CHANRCV; alts[N].op = CHANEND; xfree = nil; for(;;){ switch(alt(alts)){ case Alloc: x = xfree; if(x) xfree = x->next; else{ x = emalloc(sizeof(Xfid)); x->c = chancreate(sizeof(void(*)(Xfid*)), 0); x->arg = x; threadcreate(xfidctl, x->arg, STACK); } sendp(cxfidalloc, x); break; case Free: x->next = xfree; xfree = x; break; } } } /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */ void newwindowthread(void*) { Window *w; threadsetname("newwindowthread"); for(;;){ /* only fsysproc is talking to us, so synchronization is trivial */ recvp(cnewwindow); w = makenewwindow(nil); winsettag(w); sendp(cnewwindow, w); } } Reffont* rfget(int fix, int save, int setfont, char *name) { Reffont *r; Font *f; int i; r = nil; if(name == nil){ name = fontnames[fix]; r = reffonts[fix]; } if(r == nil){ for(i=0; if->name) == 0){ r = fontcache[i]; goto Found; } f = openfont(display, name); if(f == nil){ warning(nil, "can't open font file %s: %r\n", name); return nil; } r = emalloc(sizeof(Reffont)); r->f = f; fontcache = realloc(fontcache, (nfontcache+1)*sizeof(Reffont*)); fontcache[nfontcache++] = r; } Found: if(save){ incref(r); if(reffonts[fix]) rfclose(reffonts[fix]); reffonts[fix] = r; free(fontnames[fix]); fontnames[fix] = name; } if(setfont){ reffont.f = r->f; incref(r); rfclose(reffonts[0]); font = r->f; reffonts[0] = r; incref(r); iconinit(); } incref(r); return r; } void rfclose(Reffont *r) { int i; if(decref(r) == 0){ for(i=0; i= nfontcache) warning(nil, "internal error: can't find font in cache\n"); else{ nfontcache--; memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*)); } freefont(r->f); free(r); } } Cursor boxcursor = { {-7, -7}, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00} }; void iconinit(void) { Rectangle r; Image *tmp; /* Blue */ tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite); tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen); tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue); tagcols[TEXT] = display->black; tagcols[HTEXT] = display->black; /* Yellow */ textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite); textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow); textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen); textcols[TEXT] = display->black; textcols[HTEXT] = display->black; if(button){ freeimage(button); freeimage(modbutton); freeimage(colbutton); } r = Rect(0, 0, Scrollwid+2, font->height+1); button = allocimage(display, r, screen->chan, 0, DNofill); draw(button, r, tagcols[BACK], nil, r.min); r.max.x -= 2; border(button, r, 2, tagcols[BORD], ZP); r = button->r; modbutton = allocimage(display, r, screen->chan, 0, DNofill); draw(modbutton, r, tagcols[BACK], nil, r.min); r.max.x -= 2; border(modbutton, r, 2, tagcols[BORD], ZP); r = insetrect(r, 2); tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue); draw(modbutton, r, tmp, nil, ZP); freeimage(tmp); r = button->r; colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue); but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF); but3col = allocimage(display, r, screen->chan, 1, 0x006600FF); } /* * /dev/snarf updates when the file is closed, so we must open our own * fd here rather than use snarffd */ /* rio truncates larges snarf buffers, so this avoids using the * service if the string is huge */ #define MAXSNARF 100*1024 void putsnarf(void) { int fd, i, n; if(snarffd<0 || snarfbuf.nc==0) return; if(snarfbuf.nc > MAXSNARF) return; fd = open("/dev/snarf", OWRITE); if(fd < 0) return; for(i=0; i= NSnarf) n = NSnarf; bufread(&snarfbuf, i, snarfrune, n); if(fprint(fd, "%.*S", n, snarfrune) < 0) break; } close(fd); } void getsnarf() { int nulls; if(snarfbuf.nc > MAXSNARF) return; if(snarffd < 0) return; seek(snarffd, 0, 0); bufreset(&snarfbuf); bufload(&snarfbuf, 0, snarffd, &nulls); } #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" enum { None = 0, Fore = '+', Back = '-', }; enum { Char, Line, }; int isaddrc(int r) { if(r && utfrune("0123456789+-/$.#,;", r)!=nil) return TRUE; return FALSE; } /* * quite hard: could be almost anything but white space, but we are a little conservative, * aiming for regular expressions of alphanumerics and no white space */ int isregexc(int r) { if(r == 0) return FALSE; if(isalnum(r)) return TRUE; if(utfrune("^+-.*?#,;[]()$", r)!=nil) return TRUE; return FALSE; } Range number(Mntdir *md, Text *t, Range r, int line, int dir, int size, int *evalp) { uint q0, q1; if(size == Char){ if(dir == Fore) line = r.q1+line; else if(dir == Back){ if(r.q0==0 && line>0) r.q0 = t->file->nc; line = r.q0 - line; } if(line<0 || line>t->file->nc) goto Rescue; *evalp = TRUE; return (Range){line, line}; } q0 = r.q0; q1 = r.q1; switch(dir){ case None: q0 = 0; q1 = 0; Forward: while(line>0 && q1file->nc) if(textreadc(t, q1++) == '\n' || q1==t->file->nc) if(--line > 0) q0 = q1; if(line > 0) goto Rescue; break; case Fore: if(q1 > 0) while(textreadc(t, q1-1) != '\n') q1++; q0 = q1; goto Forward; case Back: if(q0 < t->file->nc) while(q0>0 && textreadc(t, q0-1)!='\n') q0--; q1 = q0; while(line>0 && q0>0){ if(textreadc(t, q0-1) == '\n'){ if(--line >= 0) q1 = q0; } --q0; } if(line > 0) goto Rescue; while(q0>0 && textreadc(t, q0-1)!='\n') --q0; } *evalp = TRUE; return (Range){q0, q1}; Rescue: if(md != nil) warning(nil, "address out of range\n"); *evalp = FALSE; return r; } Range regexp(Mntdir *md, Text *t, Range lim, Range r, Rune *pat, int dir, int *foundp) { int found; Rangeset sel; int q; if(pat[0] == '\0' && rxnull()){ warning(md, "no previous regular expression\n"); *foundp = FALSE; return r; } if(pat[0] && rxcompile(pat) == FALSE){ *foundp = FALSE; return r; } if(dir == Back) found = rxbexecute(t, r.q0, &sel); else{ if(lim.q0 < 0) q = Infinity; else q = lim.q1; found = rxexecute(t, nil, r.q1, q, &sel); } if(!found && md==nil) warning(nil, "no match for regexp\n"); *foundp = found; return sel.r[0]; } Range address(Mntdir *md, Text *t, Range lim, Range ar, void *a, uint q0, uint q1, int (*getc)(void*, uint), int *evalp, uint *qp) { int dir, size, npat; int prevc, c, nc, n; uint q; Rune *pat; Range r, nr; r = ar; q = q0; dir = None; size = Line; c = 0; while(q < q1){ prevc = c; c = (*getc)(a, q++); switch(c){ default: *qp = q-1; return r; case ';': ar = r; /* fall through */ case ',': if(prevc == 0) /* lhs defaults to 0 */ r.q0 = 0; if(q>=q1 && t!=nil && t->file!=nil) /* rhs defaults to $ */ r.q1 = t->file->nc; else{ nr = address(md, t, lim, ar, a, q, q1, getc, evalp, &q); r.q1 = nr.q1; } *qp = q; return r; case '+': case '-': if(*evalp && (prevc=='+' || prevc=='-')) if((nc=(*getc)(a, q))!='#' && nc!='/' && nc!='?') r = number(md, t, r, 1, prevc, Line, evalp); /* do previous one */ dir = c; break; case '.': case '$': if(q != q0+1){ *qp = q-1; return r; } if(*evalp) if(c == '.') r = ar; else r = (Range){t->file->nc, t->file->nc}; if(q < q1) dir = Fore; else dir = None; break; case '#': if(q==q1 || (c=(*getc)(a, q++))<'0' || '9' #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" enum { Slop = 100, /* room to grow with reallocation */ }; static void sizecache(Buffer *b, uint n) { if(n <= b->cmax) return; b->cmax = n+Slop; b->c = runerealloc(b->c, b->cmax); } static void addblock(Buffer *b, uint i, uint n) { if(i > b->nbl) error("internal error: addblock"); b->bl = realloc(b->bl, (b->nbl+1)*sizeof b->bl[0]); if(i < b->nbl) memmove(b->bl+i+1, b->bl+i, (b->nbl-i)*sizeof(Block*)); b->bl[i] = disknewblock(disk, n); b->nbl++; } static void delblock(Buffer *b, uint i) { if(i >= b->nbl) error("internal error: delblock"); diskrelease(disk, b->bl[i]); b->nbl--; if(i < b->nbl) memmove(b->bl+i, b->bl+i+1, (b->nbl-i)*sizeof(Block*)); b->bl = realloc(b->bl, b->nbl*sizeof b->bl[0]); } /* * Move cache so b->cq <= q0 < b->cq+b->cnc. * If at very end, q0 will fall on end of cache block. */ static void flush(Buffer *b) { if(b->cdirty || b->cnc==0){ if(b->cnc == 0) delblock(b, b->cbi); else diskwrite(disk, &b->bl[b->cbi], b->c, b->cnc); b->cdirty = FALSE; } } static void setcache(Buffer *b, uint q0) { Block **blp, *bl; uint i, q; if(q0 > b->nc) error("internal error: setcache"); /* * flush and reload if q0 is not in cache. */ if(b->nc == 0 || (b->cq<=q0 && q0cq+b->cnc)) return; /* * if q0 is at end of file and end of cache, continue to grow this block */ if(q0==b->nc && q0==b->cq+b->cnc && b->cnccq){ q = 0; i = 0; }else{ q = b->cq; i = b->cbi; } blp = &b->bl[i]; while(q+(*blp)->n <= q0 && q+(*blp)->n < b->nc){ q += (*blp)->n; i++; blp++; if(i >= b->nbl) error("block not found"); } bl = *blp; /* remember position */ b->cbi = i; b->cq = q; sizecache(b, bl->n); b->cnc = bl->n; /*read block*/ diskread(disk, bl, b->c, b->cnc); } void bufinsert(Buffer *b, uint q0, Rune *s, uint n) { uint i, m, t, off; if(q0 > b->nc) error("internal error: bufinsert"); while(n > 0){ setcache(b, q0); off = q0-b->cq; if(b->cnc+n <= Maxblock){ /* Everything fits in one block. */ t = b->cnc+n; m = n; if(b->bl == nil){ /* allocate */ if(b->cnc != 0) error("internal error: bufinsert1 cnc!=0"); addblock(b, 0, t); b->cbi = 0; } sizecache(b, t); runemove(b->c+off+m, b->c+off, b->cnc-off); runemove(b->c+off, s, m); b->cnc = t; goto Tail; } /* * We must make a new block. If q0 is at * the very beginning or end of this block, * just make a new block and fill it. */ if(q0==b->cq || q0==b->cq+b->cnc){ if(b->cdirty) flush(b); m = min(n, Maxblock); if(b->bl == nil){ /* allocate */ if(b->cnc != 0) error("internal error: bufinsert2 cnc!=0"); i = 0; }else{ i = b->cbi; if(q0 > b->cq) i++; } addblock(b, i, m); sizecache(b, m); runemove(b->c, s, m); b->cq = q0; b->cbi = i; b->cnc = m; goto Tail; } /* * Split the block; cut off the right side and * let go of it. */ m = b->cnc-off; if(m > 0){ i = b->cbi+1; addblock(b, i, m); diskwrite(disk, &b->bl[i], b->c+off, m); b->cnc -= m; } /* * Now at end of block. Take as much input * as possible and tack it on end of block. */ m = min(n, Maxblock-b->cnc); sizecache(b, b->cnc+m); runemove(b->c+b->cnc, s, m); b->cnc += m; Tail: b->nc += m; q0 += m; s += m; n -= m; b->cdirty = TRUE; } } void bufdelete(Buffer *b, uint q0, uint q1) { uint m, n, off; if(!(q0<=q1 && q0<=b->nc && q1<=b->nc)) error("internal error: bufdelete"); while(q1 > q0){ setcache(b, q0); off = q0-b->cq; if(q1 > b->cq+b->cnc) n = b->cnc - off; else n = q1-q0; m = b->cnc - (off+n); if(m > 0) runemove(b->c+off, b->c+off+n, m); b->cnc -= n; b->cdirty = TRUE; q1 -= n; b->nc -= n; } } static int bufloader(void *v, uint q0, Rune *r, int nr) { bufinsert(v, q0, r, nr); return nr; } uint loadfile(int fd, uint q0, int *nulls, int(*f)(void*, uint, Rune*, int), void *arg) { char *p; Rune *r; int l, m, n, nb, nr; uint q1; p = emalloc((Maxblock+UTFmax+1)*sizeof p[0]); r = runemalloc(Maxblock); m = 0; n = 1; q1 = q0; /* * At top of loop, may have m bytes left over from * last pass, possibly representing a partial rune. */ while(n > 0){ n = read(fd, p+m, Maxblock); if(n < 0){ warning(nil, "read error in Buffer.load"); break; } m += n; p[m] = 0; l = m; if(n > 0) l -= UTFmax; cvttorunes(p, l, r, &nb, &nr, nulls); memmove(p, p+nb, m-nb); m -= nb; q1 += (*f)(arg, q1, r, nr); } free(p); free(r); return q1-q0; } uint bufload(Buffer *b, uint q0, int fd, int *nulls) { if(q0 > b->nc) error("internal error: bufload"); return loadfile(fd, q0, nulls, bufloader, b); } void bufread(Buffer *b, uint q0, Rune *s, uint n) { uint m; if(!(q0<=b->nc && q0+n<=b->nc)) error("bufread: internal error"); while(n > 0){ setcache(b, q0); m = min(n, b->cnc-(q0-b->cq)); runemove(s, b->c+(q0-b->cq), m); q0 += m; s += m; n -= m; } } void bufreset(Buffer *b) { int i; b->nc = 0; b->cnc = 0; b->cq = 0; b->cdirty = 0; b->cbi = 0; /* delete backwards to avoid n² behavior */ for(i=b->nbl-1; --i>=0; ) delblock(b, i); } void bufclose(Buffer *b) { bufreset(b); free(b->c); b->c = nil; b->cnc = 0; free(b->bl); b->bl = nil; b->nbl = 0; } #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" void colinit(Column *c, Rectangle r) { Rectangle r1; Text *t; draw(screen, r, display->white, nil, ZP); c->r = r; c->w = nil; c->nw = 0; t = &c->tag; t->w = nil; t->col = c; r1 = r; r1.max.y = r1.min.y + font->height; textinit(t, fileaddtext(nil, t), r1, &reffont, tagcols); t->what = Columntag; r1.min.y = r1.max.y; r1.max.y += Border; draw(screen, r1, display->black, nil, ZP); textinsert(t, 0, L"New Cut Paste Snarf Sort Zerox Delcol ", 38, TRUE); textsetselect(t, t->file->nc, t->file->nc); draw(screen, t->scrollr, colbutton, nil, colbutton->r.min); c->safe = TRUE; } Window* coladd(Column *c, Window *w, Window *clone, int y) { Rectangle r, r1; Window *v; int i, t; v = nil; r = c->r; r.min.y = c->tag.r.max.y+Border; if(ynw>0){ /* steal half of last window by default */ v = c->w[c->nw-1]; y = v->body.r.min.y+Dy(v->body.r)/2; } /* look for window we'll land on */ for(i=0; inw; i++){ v = c->w[i]; if(y < v->r.max.y) break; } if(c->nw > 0){ if(i < c->nw) i++; /* new window will go after v */ /* * if v's too small, grow it first. */ if(!c->safe || v->body.maxlines<=3){ colgrow(c, v, 1); y = v->body.r.min.y+Dy(v->body.r)/2; } r = v->r; if(i == c->nw) t = c->r.max.y; else t = c->w[i]->r.min.y-Border; r.max.y = t; draw(screen, r, textcols[BACK], nil, ZP); r1 = r; y = min(y, t-(v->tag.font->height+v->body.font->height+Border+1)); r1.max.y = min(y, v->body.r.min.y+v->body.nlines*v->body.font->height); r1.min.y = winresize(v, r1, FALSE); r1.max.y = r1.min.y+Border; draw(screen, r1, display->black, nil, ZP); r.min.y = r1.max.y; } if(w == nil){ w = emalloc(sizeof(Window)); w->col = c; draw(screen, r, textcols[BACK], nil, ZP); wininit(w, clone, r); }else{ w->col = c; winresize(w, r, FALSE); } w->tag.col = c; w->tag.row = c->row; w->body.col = c; w->body.row = c->row; c->w = realloc(c->w, (c->nw+1)*sizeof(Window*)); memmove(c->w+i+1, c->w+i, (c->nw-i)*sizeof(Window*)); c->nw++; c->w[i] = w; savemouse(w); /* near but not on the button */ moveto(mousectl, addpt(w->tag.scrollr.max, Pt(3, 3))); barttext = &w->body; c->safe = TRUE; return w; } void colclose(Column *c, Window *w, int dofree) { Rectangle r; int i; /* w is locked */ if(!c->safe) colgrow(c, w, 1); for(i=0; inw; i++) if(c->w[i] == w) goto Found; error("can't find window"); Found: r = w->r; w->tag.col = nil; w->body.col = nil; w->col = nil; restoremouse(w); if(dofree){ windelete(w); winclose(w); } memmove(c->w+i, c->w+i+1, (c->nw-i)*sizeof(Window*)); c->nw--; c->w = realloc(c->w, c->nw*sizeof(Window*)); if(c->nw == 0){ draw(screen, r, display->white, nil, ZP); return; } if(i == c->nw){ /* extend last window down */ w = c->w[i-1]; r.min.y = w->r.min.y; r.max.y = c->r.max.y; }else{ /* extend next window up */ w = c->w[i]; r.max.y = w->r.max.y; } draw(screen, r, textcols[BACK], nil, ZP); if(c->safe) winresize(w, r, FALSE); } void colcloseall(Column *c) { int i; Window *w; if(c == activecol) activecol = nil; textclose(&c->tag); for(i=0; inw; i++){ w = c->w[i]; winclose(w); } c->nw = 0; free(c->w); free(c); clearmouse(); } void colmousebut(Column *c) { moveto(mousectl, divpt(addpt(c->tag.scrollr.min, c->tag.scrollr.max), 2)); } void colresize(Column *c, Rectangle r) { int i; Rectangle r1, r2; Window *w; clearmouse(); r1 = r; r1.max.y = r1.min.y + c->tag.font->height; textresize(&c->tag, r1); draw(screen, c->tag.scrollr, colbutton, nil, colbutton->r.min); r1.min.y = r1.max.y; r1.max.y += Border; draw(screen, r1, display->black, nil, ZP); r1.max.y = r.max.y; for(i=0; inw; i++){ w = c->w[i]; w->maxlines = 0; if(i == c->nw-1) r1.max.y = r.max.y; else r1.max.y = r1.min.y+(Dy(w->r)+Border)*Dy(r)/Dy(c->r); r2 = r1; r2.max.y = r2.min.y+Border; draw(screen, r2, display->black, nil, ZP); r1.min.y = r2.max.y; r1.min.y = winresize(w, r1, FALSE); } c->r = r; } static int colcmp(void *a, void *b) { Rune *r1, *r2; int i, nr1, nr2; r1 = (*(Window**)a)->body.file->name; nr1 = (*(Window**)a)->body.file->nname; r2 = (*(Window**)b)->body.file->name; nr2 = (*(Window**)b)->body.file->nname; for(i=0; inw == 0) return; clearmouse(); rp = emalloc(c->nw*sizeof(Rectangle)); wp = emalloc(c->nw*sizeof(Window*)); memmove(wp, c->w, c->nw*sizeof(Window*)); qsort(wp, c->nw, sizeof(Window*), colcmp); for(i=0; inw; i++) rp[i] = wp[i]->r; r = c->r; r.min.y = c->tag.r.max.y; draw(screen, r, textcols[BACK], nil, ZP); y = r.min.y; for(i=0; inw; i++){ w = wp[i]; r.min.y = y; if(i == c->nw-1) r.max.y = c->r.max.y; else r.max.y = r.min.y+Dy(w->r)+Border; r1 = r; r1.max.y = r1.min.y+Border; draw(screen, r1, display->black, nil, ZP); r.min.y = r1.max.y; y = winresize(w, r, FALSE); } free(rp); free(c->w); c->w = wp; } void colgrow(Column *c, Window *w, int but) { Rectangle r, cr; int i, j, k, l, y1, y2, *nl, *ny, tot, nnl, onl, dnl, h; Window *v; for(i=0; inw; i++) if(c->w[i] == w) goto Found; error("can't find window"); Found: cr = c->r; if(but < 0){ /* make sure window fills its own space properly */ r = w->r; if(i==c->nw-1 || c->safe==FALSE) r.max.y = cr.max.y; else r.max.y = c->w[i+1]->r.min.y; winresize(w, r, FALSE); return; } cr.min.y = c->w[0]->r.min.y; if(but == 3){ /* full size */ if(i != 0){ v = c->w[0]; c->w[0] = w; c->w[i] = v; } draw(screen, cr, textcols[BACK], nil, ZP); winresize(w, cr, FALSE); for(i=1; inw; i++) c->w[i]->body.maxlines = 0; c->safe = FALSE; return; } /* store old #lines for each window */ onl = w->body.maxlines; nl = emalloc(c->nw * sizeof(int)); ny = emalloc(c->nw * sizeof(int)); tot = 0; for(j=0; jnw; j++){ l = c->w[j]->body.maxlines; nl[j] = l; tot += l; } /* approximate new #lines for this window */ if(but == 2){ /* as big as can be */ memset(nl, 0, c->nw * sizeof(int)); goto Pack; } nnl = min(onl + max(min(5, w->maxlines), onl/2), tot); if(nnl < w->maxlines) nnl = (w->maxlines+nnl)/2; if(nnl == 0) nnl = 2; dnl = nnl - onl; /* compute new #lines for each window */ for(k=1; knw; k++){ /* prune from later window */ j = i+k; if(jnw && nl[j]){ l = min(dnl, max(1, nl[j]/2)); nl[j] -= l; nl[i] += l; dnl -= l; } /* prune from earlier window */ j = i-k; if(j>=0 && nl[j]){ l = min(dnl, max(1, nl[j]/2)); nl[j] -= l; nl[i] += l; dnl -= l; } } Pack: /* pack everyone above */ y1 = cr.min.y; for(j=0; jw[j]; r = v->r; r.min.y = y1; r.max.y = y1+Dy(v->tag.all); if(nl[j]) r.max.y += 1 + nl[j]*v->body.font->height; if(!c->safe || !eqrect(v->r, r)){ draw(screen, r, textcols[BACK], nil, ZP); winresize(v, r, c->safe); } r.min.y = v->r.max.y; r.max.y += Border; draw(screen, r, display->black, nil, ZP); y1 = r.max.y; } /* scan to see new size of everyone below */ y2 = c->r.max.y; for(j=c->nw-1; j>i; j--){ v = c->w[j]; r = v->r; r.min.y = y2-Dy(v->tag.all); if(nl[j]) r.min.y -= 1 + nl[j]*v->body.font->height; r.min.y -= Border; ny[j] = r.min.y; y2 = r.min.y; } /* compute new size of window */ r = w->r; r.min.y = y1; r.max.y = r.min.y+Dy(w->tag.all); h = w->body.font->height; if(y2-r.max.y >= 1+h+Border){ r.max.y += 1; r.max.y += h*((y2-r.max.y)/h); } /* draw window */ if(!c->safe || !eqrect(w->r, r)){ draw(screen, r, textcols[BACK], nil, ZP); winresize(w, r, c->safe); } if(i < c->nw-1){ r.min.y = r.max.y; r.max.y += Border; draw(screen, r, display->black, nil, ZP); for(j=i+1; jnw; j++) ny[j] -= (y2-r.max.y); } /* pack everyone below */ y1 = r.max.y; for(j=i+1; jnw; j++){ v = c->w[j]; r = v->r; r.min.y = y1; r.max.y = y1+Dy(v->tag.all); if(nl[j]) r.max.y += 1 + nl[j]*v->body.font->height; if(!c->safe || !eqrect(v->r, r)){ draw(screen, r, textcols[BACK], nil, ZP); winresize(v, r, c->safe); } if(j < c->nw-1){ /* no border on last window */ r.min.y = v->r.max.y; r.max.y += Border; draw(screen, r, display->black, nil, ZP); } y1 = r.max.y; } r = w->r; r.min.y = y1; r.max.y = c->r.max.y; draw(screen, r, textcols[BACK], nil, ZP); free(nl); free(ny); c->safe = TRUE; winmousebut(w); } void coldragwin(Column *c, Window *w, int but) { Rectangle r; int i, b; Point p, op; Window *v; Column *nc; clearmouse(); setcursor(mousectl, &boxcursor); b = mouse->buttons; op = mouse->xy; while(mouse->buttons == b) readmouse(mousectl); setcursor(mousectl, nil); if(mouse->buttons){ while(mouse->buttons) readmouse(mousectl); return; } for(i=0; inw; i++) if(c->w[i] == w) goto Found; error("can't find window"); Found: p = mouse->xy; if(abs(p.x-op.x)<5 && abs(p.y-op.y)<5){ colgrow(c, w, but); winmousebut(w); return; } /* is it a flick to the right? */ if(abs(p.y-op.y)<10 && p.x>op.x+30 && rowwhichcol(c->row, p)==c) p.x = op.x+Dx(w->r); /* yes: toss to next column */ nc = rowwhichcol(c->row, p); if(nc!=nil && nc!=c){ colclose(c, w, FALSE); coladd(nc, w, nil, p.y); winmousebut(w); return; } if(i==0 && c->nw==1) return; /* can't do it */ if((i>0 && p.yw[i-1]->r.min.y) || (inw-1 && p.y>w->r.max.y) || (i==0 && p.y>w->r.max.y)){ /* shuffle */ colclose(c, w, FALSE); coladd(c, w, nil, p.y); winmousebut(w); return; } if(i == 0) return; v = c->w[i-1]; if(p.y < v->tag.all.max.y) p.y = v->tag.all.max.y; if(p.y > w->r.max.y-Dy(w->tag.all)-Border) p.y = w->r.max.y-Dy(w->tag.all)-Border; r = v->r; r.max.y = p.y; if(r.max.y > v->body.r.min.y){ r.max.y -= (r.max.y-v->body.r.min.y)%v->body.font->height; if(v->body.r.min.y == v->body.r.max.y) r.max.y++; } if(!eqrect(v->r, r)){ draw(screen, r, textcols[BACK], nil, ZP); winresize(v, r, c->safe); } r.min.y = v->r.max.y; r.max.y = r.min.y+Border; draw(screen, r, display->black, nil, ZP); r.min.y = r.max.y; if(i == c->nw-1) r.max.y = c->r.max.y; else r.max.y = c->w[i+1]->r.min.y-Border; if(!eqrect(w->r, r)){ draw(screen, r, textcols[BACK], nil, ZP); winresize(w, r, c->safe); } c->safe = TRUE; winmousebut(w); } Text* colwhich(Column *c, Point p) { int i; Window *w; if(!ptinrect(p, c->r)) return nil; if(ptinrect(p, c->tag.all)) return &c->tag; for(i=0; inw; i++){ w = c->w[i]; if(ptinrect(p, w->r)){ if(ptinrect(p, w->tag.all)) return &w->tag; return &w->body; } } return nil; } int colclean(Column *c) { int i, clean; clean = TRUE; for(i=0; inw; i++) clean &= winclean(c->w[i], TRUE); return clean; } enum { Qdir, Qacme, Qcons, Qconsctl, Qdraw, Qeditout, Qindex, Qlabel, Qnew, QWaddr, QWbody, QWctl, QWdata, QWeditout, QWevent, QWrdsel, QWwrsel, QWtag, QMAX, }; enum { Blockincr = 256, Maxblock = 8*1024, NRange = 10, Infinity = 0x7FFFFFFF, /* huge value for regexp address */ }; typedef struct Block Block; typedef struct Buffer Buffer; typedef struct Command Command; typedef struct Column Column; typedef struct Dirlist Dirlist; typedef struct Dirtab Dirtab; typedef struct Disk Disk; typedef struct Expand Expand; typedef struct Fid Fid; typedef struct File File; typedef struct Elog Elog; typedef struct Mntdir Mntdir; typedef struct Range Range; typedef struct Rangeset Rangeset; typedef struct Reffont Reffont; typedef struct Row Row; typedef struct Runestr Runestr; typedef struct Text Text; typedef struct Timer Timer; typedef struct Window Window; typedef struct Xfid Xfid; struct Runestr { Rune *r; int nr; }; struct Range { int q0; int q1; }; struct Block { uint addr; /* disk address in bytes */ union { uint n; /* number of used runes in block */ Block *next; /* pointer to next in free list */ }; }; struct Disk { int fd; uint addr; /* length of temp file */ Block *free[Maxblock/Blockincr+1]; }; Disk* diskinit(void); Block* disknewblock(Disk*, uint); void diskrelease(Disk*, Block*); void diskread(Disk*, Block*, Rune*, uint); void diskwrite(Disk*, Block**, Rune*, uint); struct Buffer { uint nc; Rune *c; /* cache */ uint cnc; /* bytes in cache */ uint cmax; /* size of allocated cache */ uint cq; /* position of cache */ int cdirty; /* cache needs to be written */ uint cbi; /* index of cache Block */ Block **bl; /* array of blocks */ uint nbl; /* number of blocks */ }; void bufinsert(Buffer*, uint, Rune*, uint); void bufdelete(Buffer*, uint, uint); uint bufload(Buffer*, uint, int, int*); void bufread(Buffer*, uint, Rune*, uint); void bufclose(Buffer*); void bufreset(Buffer*); struct Elog { short type; /* Delete, Insert, Filename */ uint q0; /* location of change (unused in f) */ uint nd; /* number of deleted characters */ uint nr; /* # runes in string or file name */ Rune *r; }; void elogterm(File*); void elogclose(File*); void eloginsert(File*, int, Rune*, int); void elogdelete(File*, int, int); void elogreplace(File*, int, int, Rune*, int); void elogapply(File*); struct File { Buffer; /* the data */ Buffer delta; /* transcript of changes */ Buffer epsilon; /* inversion of delta for redo */ Buffer *elogbuf; /* log of pending editor changes */ Elog elog; /* current pending change */ Rune *name; /* name of associated file */ int nname; /* size of name */ uvlong qidpath; /* of file when read */ uint mtime; /* of file when read */ int dev; /* of file when read */ int unread; /* file has not been read from disk */ int editclean; /* mark clean after edit command */ int seq; /* if seq==0, File acts like Buffer */ int mod; Text *curtext; /* most recently used associated text */ Text **text; /* list of associated texts */ int ntext; int dumpid; /* used in dumping zeroxed windows */ }; File* fileaddtext(File*, Text*); void fileclose(File*); void filedelete(File*, uint, uint); void filedeltext(File*, Text*); void fileinsert(File*, uint, Rune*, uint); uint fileload(File*, uint, int, int*); void filemark(File*); void filereset(File*); void filesetname(File*, Rune*, int); void fileundelete(File*, Buffer*, uint, uint); void fileuninsert(File*, Buffer*, uint, uint); void fileunsetname(File*, Buffer*); void fileundo(File*, int, uint*, uint*); uint fileredoseq(File*); enum /* Text.what */ { Columntag, Rowtag, Tag, Body, }; struct Text { File *file; Frame; Reffont *reffont; uint org; uint q0; uint q1; int what; int tabstop; Window *w; Rectangle scrollr; Rectangle lastsr; Rectangle all; Row *row; Column *col; uint eq0; /* start of typing for ESC */ uint cq0; /* cache position */ int ncache; /* storage for insert */ int ncachealloc; Rune *cache; int nofill; }; uint textbacknl(Text*, uint, uint); uint textbsinsert(Text*, uint, Rune*, uint, int, int*); int textbswidth(Text*, Rune); int textclickmatch(Text*, int, int, int, uint*); void textclose(Text*); void textcolumnate(Text*, Dirlist**, int); void textcommit(Text*, int); void textconstrain(Text*, uint, uint, uint*, uint*); void textdelete(Text*, uint, uint, int); void textdoubleclick(Text*, uint*, uint*); void textfill(Text*); void textframescroll(Text*, int); void textinit(Text*, File*, Rectangle, Reffont*, Image**); void textinsert(Text*, uint, Rune*, uint, int); uint textload(Text*, uint, char*, int); Rune textreadc(Text*, uint); void textredraw(Text*, Rectangle, Font*, Image*, int); void textreset(Text*); int textresize(Text*, Rectangle); void textscrdraw(Text*); void textscroll(Text*, int); void textselect(Text*); int textselect2(Text*, uint*, uint*, Text**); int textselect23(Text*, uint*, uint*, Image*, int); int textselect3(Text*, uint*, uint*); void textsetorigin(Text*, uint, int); void textsetselect(Text*, uint, uint); void textshow(Text*, uint, uint, int); void texttype(Text*, Rune); struct Window { QLock; Ref; Text tag; Text body; Rectangle r; uchar isdir; uchar isscratch; uchar filemenu; uchar dirty; uchar autoindent; int id; Range addr; Range limit; uchar nopen[QMAX]; uchar nomark; uchar noscroll; Range wrselrange; int rdselfd; int neditwrsel; Column *col; Xfid *eventx; char *events; int nevents; int owner; int maxlines; Dirlist **dlp; int ndl; int putseq; int nincl; Rune **incl; Reffont *reffont; QLock ctllock; uint ctlfid; char *dumpstr; char *dumpdir; int dumpid; int utflastqid; int utflastboff; int utflastq; }; void wininit(Window*, Window*, Rectangle); void winlock(Window*, int); void winlock1(Window*, int); void winunlock(Window*); void wintype(Window*, Text*, Rune); void winundo(Window*, int); void winsetname(Window*, Rune*, int); void winsettag(Window*); void winsettag1(Window*); void wincommit(Window*, Text*); int winresize(Window*, Rectangle, int); void winclose(Window*); void windelete(Window*); int winclean(Window*, int); void windirfree(Window*); void winevent(Window*, char*, ...); void winmousebut(Window*); void winaddincl(Window*, Rune*, int); void wincleartag(Window*); void winctlprint(Window*, char*, int); struct Column { Rectangle r; Text tag; Row *row; Window **w; int nw; int safe; }; void colinit(Column*, Rectangle); Window* coladd(Column*, Window*, Window*, int); void colclose(Column*, Window*, int); void colcloseall(Column*); void colresize(Column*, Rectangle); Text* colwhich(Column*, Point); void coldragwin(Column*, Window*, int); void colgrow(Column*, Window*, int); int colclean(Column*); void colsort(Column*); void colmousebut(Column*); struct Row { QLock; Rectangle r; Text tag; Column **col; int ncol; }; void rowinit(Row*, Rectangle); Column* rowadd(Row*, Column *c, int); void rowclose(Row*, Column*, int); Text* rowwhich(Row*, Point); Column* rowwhichcol(Row*, Point); void rowresize(Row*, Rectangle); Text* rowtype(Row*, Rune, Point); void rowdragcol(Row*, Column*, int but); int rowclean(Row*); void rowdump(Row*, char*); void rowload(Row*, char*, int); void rowloadfonts(char*); struct Timer { int dt; int cancel; Channel *c; /* chan(int) */ Timer *next; }; struct Command { int pid; Rune *name; int nname; char *text; char **av; int iseditcmd; Mntdir *md; Command *next; }; struct Dirtab { char *name; uchar type; uint qid; uint perm; }; struct Mntdir { int id; int ref; Rune *dir; int ndir; Mntdir *next; int nincl; Rune **incl; }; struct Fid { int fid; int busy; int open; Qid qid; Window *w; Dirtab *dir; Fid *next; Mntdir *mntdir; int nrpart; uchar rpart[UTFmax]; }; struct Xfid { void *arg; /* args to xfidinit */ Fcall; Xfid *next; Channel *c; /* chan(void(*)(Xfid*)) */ Fid *f; uchar *buf; int flushed; }; void xfidctl(void *); void xfidflush(Xfid*); void xfidopen(Xfid*); void xfidclose(Xfid*); void xfidread(Xfid*); void xfidwrite(Xfid*); void xfidctlwrite(Xfid*, Window*); void xfideventread(Xfid*, Window*); void xfideventwrite(Xfid*, Window*); void xfidindexread(Xfid*); void xfidutfread(Xfid*, Text*, uint, int); int xfidruneread(Xfid*, Text*, uint, uint); struct Reffont { Ref; Font *f; }; Reffont *rfget(int, int, int, char*); void rfclose(Reffont*); struct Rangeset { Range r[NRange]; }; struct Dirlist { Rune *r; int nr; int wid; }; struct Expand { uint q0; uint q1; Rune *name; int nname; char *bname; int jump; union{ Text *at; Rune *ar; }; int (*agetc)(void*, uint); int a0; int a1; }; enum { /* fbufalloc() guarantees room off end of BUFSIZE */ BUFSIZE = Maxblock+IOHDRSZ, /* size from fbufalloc() */ RBUFSIZE = BUFSIZE/sizeof(Rune), EVENTSIZE = 256, Scrollwid = 12, /* width of scroll bar */ Scrollgap = 4, /* gap right of scroll bar */ Margin = 4, /* margin around text */ Border = 2, /* line between rows, cols, windows */ }; #define QID(w,q) ((w<<8)|(q)) #define WIN(q) ((((ulong)(q).path)>>8) & 0xFFFFFF) #define FILE(q) ((q).path & 0xFF) enum { FALSE, TRUE, XXX, }; enum { Empty = 0, Null = '-', Delete = 'd', Insert = 'i', Replace = 'r', Filename = 'f', }; enum /* editing */ { Inactive = 0, Inserting, Collecting, }; uint globalincref; uint seq; uint maxtab; /* size of a tab, in units of the '0' character */ Display *display; Image *screen; Font *font; Mouse *mouse; Mousectl *mousectl; Keyboardctl *keyboardctl; Reffont reffont; Image *modbutton; Image *colbutton; Image *button; Image *but2col; Image *but3col; Cursor boxcursor; Row row; int timerpid; Disk *disk; Text *seltext; Text *argtext; Text *mousetext; /* global because Text.close needs to clear it */ Text *typetext; /* global because Text.close needs to clear it */ Text *barttext; /* shared between mousetask and keyboardthread */ int bartflag; Window *activewin; Column *activecol; Buffer snarfbuf; Rectangle nullrect; int fsyspid; char *cputype; char *objtype; char *home; char *fontnames[2]; char acmeerrorfile[128]; Image *tagcols[NCOL]; Image *textcols[NCOL]; int plumbsendfd; int plumbeditfd; char wdir[]; int editing; int messagesize; /* negotiated in 9P version setup */ int globalautoindent; Channel *ckeyboard; /* chan(Rune)[10] */ Channel *cplumb; /* chan(Plumbmsg*) */ Channel *cwait; /* chan(Waitmsg) */ Channel *ccommand; /* chan(Command*) */ Channel *ckill; /* chan(Rune*) */ Channel *cxfidalloc; /* chan(Xfid*) */ Channel *cxfidfree; /* chan(Xfid*) */ Channel *cnewwindow; /* chan(Channel*) */ Channel *mouseexit0; /* chan(int) */ Channel *mouseexit1; /* chan(int) */ Channel *cexit; /* chan(int) */ Channel *cerr; /* chan(char*) */ Channel *cedit; /* chan(int) */ Channel *cwarn; /* chan(void*)[1] (really chan(unit)[1]) */ #define STACK 8192 #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" static Block *blist; int tempfile(void) { char buf[128]; int i, fd; snprint(buf, sizeof buf, "/tmp/X%d.%.4sacme", getpid(), getuser()); for(i='A'; i<='Z'; i++){ buf[5] = i; if(access(buf, AEXIST) == 0) continue; fd = create(buf, ORDWR|ORCLOSE|OCEXEC, 0600); if(fd >= 0) return fd; } return -1; } Disk* diskinit() { Disk *d; d = emalloc(sizeof(Disk)); d->fd = tempfile(); if(d->fd < 0){ fprint(2, "acme: can't create temp file: %r\n"); threadexitsall("diskinit"); } return d; } static uint ntosize(uint n, uint *ip) { uint size; if(n > Maxblock) error("internal error: ntosize"); size = n; if(size & (Blockincr-1)) size += Blockincr - (size & (Blockincr-1)); /* last bucket holds blocks of exactly Maxblock */ if(ip) *ip = size/Blockincr; return size * sizeof(Rune); } Block* disknewblock(Disk *d, uint n) { uint i, j, size; Block *b; size = ntosize(n, &i); b = d->free[i]; if(b) d->free[i] = b->next; else{ /* allocate in chunks to reduce malloc overhead */ if(blist == nil){ blist = emalloc(100*sizeof(Block)); for(j=0; j<100-1; j++) blist[j].next = &blist[j+1]; } b = blist; blist = b->next; b->addr = d->addr; d->addr += size; } b->n = n; return b; } void diskrelease(Disk *d, Block *b) { uint i; ntosize(b->n, &i); b->next = d->free[i]; d->free[i] = b; } void diskwrite(Disk *d, Block **bp, Rune *r, uint n) { int size, nsize; Block *b; b = *bp; size = ntosize(b->n, nil); nsize = ntosize(n, nil); if(size != nsize){ diskrelease(d, b); b = disknewblock(d, n); *bp = b; } if(pwrite(d->fd, r, n*sizeof(Rune), b->addr) != n*sizeof(Rune)) error("write error to temp file"); b->n = n; } void diskread(Disk *d, Block *b, Rune *r, uint n) { if(n > b->n) error("internal error: diskread"); ntosize(b->n, nil); if(pread(d->fd, r, n*sizeof(Rune), b->addr) != n*sizeof(Rune)) error("read error from temp file"); } #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "edit.h" #include "fns.h" int Glooping; int nest; char Enoname[] = "no file name given"; Address addr; File *menu; Rangeset sel; extern Text* curtext; Rune *collection; int ncollection; int append(File*, Cmd*, long); int pdisplay(File*); void pfilename(File*); void looper(File*, Cmd*, int); void filelooper(Cmd*, int); void linelooper(File*, Cmd*); Address lineaddr(long, Address, int); int filematch(File*, String*); File *tofile(String*); Rune* cmdname(File *f, String *s, int); void runpipe(Text*, int, Rune*, int, int); void clearcollection(void) { free(collection); collection = nil; ncollection = 0; } void resetxec(void) { Glooping = nest = 0; clearcollection(); } void mkaddr(Address *a, File *f) { a->r.q0 = f->curtext->q0; a->r.q1 = f->curtext->q1; a->f = f; } int cmdexec(Text *t, Cmd *cp) { int i; Addr *ap; File *f; Window *w; Address dot; if(t == nil) w = nil; else w = t->w; if(w==nil && (cp->addr==0 || cp->addr->type!='"') && !utfrune("bBnqUXY!", cp->cmdc) && !(cp->cmdc=='D' && cp->text)) editerror("no current window"); i = cmdlookup(cp->cmdc); /* will be -1 for '{' */ f = nil; if(t && t->w){ t = &t->w->body; f = t->file; f->curtext = t; } if(i>=0 && cmdtab[i].defaddr != aNo){ if((ap=cp->addr)==0 && cp->cmdc!='\n'){ cp->addr = ap = newaddr(); ap->type = '.'; if(cmdtab[i].defaddr == aAll) ap->type = '*'; }else if(ap && ap->type=='"' && ap->next==0 && cp->cmdc!='\n'){ ap->next = newaddr(); ap->next->type = '.'; if(cmdtab[i].defaddr == aAll) ap->next->type = '*'; } if(cp->addr){ /* may be false for '\n' (only) */ static Address none = {0,0,nil}; if(f){ mkaddr(&dot, f); addr = cmdaddress(ap, dot, 0); }else /* a " */ addr = cmdaddress(ap, none, 0); f = addr.f; t = f->curtext; } } switch(cp->cmdc){ case '{': mkaddr(&dot, f); if(cp->addr != nil) dot = cmdaddress(cp->addr, dot, 0); for(cp = cp->cmd; cp; cp = cp->next){ if(dot.r.q1 > t->file->nc) editerror("dot extends past end of buffer during { command"); t->q0 = dot.r.q0; t->q1 = dot.r.q1; cmdexec(t, cp); } break; default: if(i < 0) editerror("unknown command %c in cmdexec", cp->cmdc); i = (*cmdtab[i].fn)(t, cp); return i; } return 1; } char* edittext(Window *w, int q, Rune *r, int nr) { File *f; f = w->body.file; switch(editing){ case Inactive: return "permission denied"; case Inserting: w->neditwrsel += nr; eloginsert(f, q, r, nr); return nil; case Collecting: collection = runerealloc(collection, ncollection+nr+1); runemove(collection+ncollection, r, nr); ncollection += nr; collection[ncollection] = '\0'; return nil; default: return "unknown state in edittext"; } } /* string is known to be NUL-terminated */ Rune* filelist(Text *t, Rune *r, int nr) { if(nr == 0) return nil; r = skipbl(r, nr, &nr); if(r[0] != '<') return runestrdup(r); /* use < command to collect text */ clearcollection(); runpipe(t, '<', r+1, nr-1, Collecting); return collection; } int a_cmd(Text *t, Cmd *cp) { return append(t->file, cp, addr.r.q1); } int b_cmd(Text*, Cmd *cp) { File *f; f = tofile(cp->text); if(nest == 0) pfilename(f); curtext = f->curtext; return TRUE; } int B_cmd(Text *t, Cmd *cp) { Rune *list, *r, *s; int nr; list = filelist(t, cp->text->r, cp->text->n); if(list == nil) editerror(Enoname); r = list; nr = runestrlen(r); r = skipbl(r, nr, &nr); if(nr == 0) new(t, t, nil, 0, 0, r, 0); else while(nr > 0){ s = findbl(r, nr, &nr); *s = '\0'; new(t, t, nil, 0, 0, r, runestrlen(r)); if(nr > 0) r = skipbl(s+1, nr-1, &nr); } clearcollection(); return TRUE; } int c_cmd(Text *t, Cmd *cp) { elogreplace(t->file, addr.r.q0, addr.r.q1, cp->text->r, cp->text->n); t->q0 = addr.r.q0; t->q1 = addr.r.q0+cp->text->n; return TRUE; } int d_cmd(Text *t, Cmd*) { if(addr.r.q1 > addr.r.q0) elogdelete(t->file, addr.r.q0, addr.r.q1); t->q0 = addr.r.q0; t->q1 = addr.r.q0; return TRUE; } void D1(Text *t) { if(t->w->body.file->ntext>1 || winclean(t->w, FALSE)) colclose(t->col, t->w, TRUE); } int D_cmd(Text *t, Cmd *cp) { Rune *list, *r, *s, *n; int nr, nn; Window *w; Runestr dir, rs; char buf[128]; list = filelist(t, cp->text->r, cp->text->n); if(list == nil){ D1(t); return TRUE; } dir = dirname(t, nil, 0); r = list; nr = runestrlen(r); r = skipbl(r, nr, &nr); do{ s = findbl(r, nr, &nr); *s = '\0'; /* first time through, could be empty string, meaning delete file empty name */ nn = runestrlen(r); if(r[0]=='/' || nn==0 || dir.nr==0){ rs.r = runestrdup(r); rs.nr = nn; }else{ n = runemalloc(dir.nr+1+nn); runemove(n, dir.r, dir.nr); n[dir.nr] = '/'; runemove(n+dir.nr+1, r, nn); rs = cleanrname((Runestr){n, dir.nr+1+nn}); } w = lookfile(rs.r, rs.nr); if(w == nil){ snprint(buf, sizeof buf, "no such file %.*S", rs.nr, rs.r); free(rs.r); editerror(buf); } free(rs.r); D1(&w->body); if(nr > 0) r = skipbl(s+1, nr-1, &nr); }while(nr > 0); clearcollection(); free(dir.r); return TRUE; } static int readloader(void *v, uint q0, Rune *r, int nr) { if(nr > 0) eloginsert(v, q0, r, nr); return 0; } int e_cmd(Text *t, Cmd *cp) { Rune *name; File *f; int i, isdir, q0, q1, fd, nulls, samename, allreplaced; char *s, tmp[128]; Dir *d; f = t->file; q0 = addr.r.q0; q1 = addr.r.q1; if(cp->cmdc == 'e'){ if(winclean(t->w, TRUE)==FALSE) editerror(""); /* winclean generated message already */ q0 = 0; q1 = f->nc; } allreplaced = (q0==0 && q1==f->nc); name = cmdname(f, cp->text, cp->cmdc=='e'); if(name == nil) editerror(Enoname); i = runestrlen(name); samename = runeeq(name, i, t->file->name, t->file->nname); s = runetobyte(name, i); free(name); fd = open(s, OREAD); if(fd < 0){ snprint(tmp, sizeof tmp, "can't open %s: %r", s); free(s); editerror(tmp); } d = dirfstat(fd); isdir = (d!=nil && (d->qid.type&QTDIR)); free(d); if(isdir){ close(fd); snprint(tmp, sizeof tmp, "%s is a directory", s); free(s); editerror(tmp); } elogdelete(f, q0, q1); nulls = 0; loadfile(fd, q1, &nulls, readloader, f); free(s); close(fd); if(nulls) warning(nil, "%s: NUL bytes elided\n", s); else if(allreplaced && samename) f->editclean = TRUE; return TRUE; } int f_cmd(Text *t, Cmd *cp) { Rune *name; String *str; String empty; if(cp->text == nil){ empty.n = 0; empty.r = L""; str = ∅ }else str = cp->text; name = cmdname(t->file, str, TRUE); free(name); pfilename(t->file); return TRUE; } int g_cmd(Text *t, Cmd *cp) { if(t->file != addr.f){ warning(nil, "internal error: g_cmd f!=addr.f\n"); return FALSE; } if(rxcompile(cp->re->r) == FALSE) editerror("bad regexp in g command"); if(rxexecute(t, nil, addr.r.q0, addr.r.q1, &sel) ^ cp->cmdc=='v'){ t->q0 = addr.r.q0; t->q1 = addr.r.q1; return cmdexec(t, cp->cmd); } return TRUE; } int i_cmd(Text *t, Cmd *cp) { return append(t->file, cp, addr.r.q0); } void copy(File *f, Address addr2) { long p; int ni; Rune *buf; buf = fbufalloc(); for(p=addr.r.q0; p RBUFSIZE) ni = RBUFSIZE; bufread(f, p, buf, ni); eloginsert(addr2.f, addr2.r.q1, buf, ni); } fbuffree(buf); } void move(File *f, Address addr2) { if(addr.f!=addr2.f || addr.r.q1<=addr2.r.q0){ elogdelete(f, addr.r.q0, addr.r.q1); copy(f, addr2); }else if(addr.r.q0 >= addr2.r.q1){ copy(f, addr2); elogdelete(f, addr.r.q0, addr.r.q1); }else error("move overlaps itself"); } int m_cmd(Text *t, Cmd *cp) { Address dot, addr2; mkaddr(&dot, t->file); addr2 = cmdaddress(cp->mtaddr, dot, 0); if(cp->cmdc == 'm') move(t->file, addr2); else copy(t->file, addr2); return TRUE; } int p_cmd(Text *t, Cmd*) { return pdisplay(t->file); } int s_cmd(Text *t, Cmd *cp) { int i, j, k, c, m, n, nrp, didsub; long p1, op, delta; String *buf; Rangeset *rp; char *err; Rune *rbuf; n = cp->num; op= -1; if(rxcompile(cp->re->r) == FALSE) editerror("bad regexp in s command"); nrp = 0; rp = nil; delta = 0; didsub = FALSE; for(p1 = addr.r.q0; p1<=addr.r.q1 && rxexecute(t, nil, p1, addr.r.q1, &sel); ){ if(sel.r[0].q0 == sel.r[0].q1){ /* empty match? */ if(sel.r[0].q0 == op){ p1++; continue; } p1 = sel.r[0].q1+1; }else p1 = sel.r[0].q1; op = sel.r[0].q1; if(--n>0) continue; nrp++; rp = erealloc(rp, nrp*sizeof(Rangeset)); rp[nrp-1] = sel; } rbuf = fbufalloc(); buf = allocstring(0); for(m=0; mn = 0; buf->r[0] = L'\0'; sel = rp[m]; for(i = 0; itext->n; i++) if((c = cp->text->r[i])=='\\' && itext->n-1){ c = cp->text->r[++i]; if('1'<=c && c<='9') { j = c-'0'; if(sel.r[j].q1-sel.r[j].q0>RBUFSIZE){ err = "replacement string too long"; goto Err; } bufread(t->file, sel.r[j].q0, rbuf, sel.r[j].q1-sel.r[j].q0); for(k=0; kRBUFSIZE){ err = "right hand side too long in substitution"; goto Err; } bufread(t->file, sel.r[0].q0, rbuf, sel.r[0].q1-sel.r[0].q0); for(k=0; kfile, sel.r[0].q0, sel.r[0].q1, buf->r, buf->n); delta -= sel.r[0].q1-sel.r[0].q0; delta += buf->n; didsub = 1; if(!cp->flag) break; } free(rp); freestring(buf); fbuffree(rbuf); if(!didsub && nest==0) editerror("no substitution"); t->q0 = addr.r.q0; t->q1 = addr.r.q1+delta; return TRUE; Err: free(rp); freestring(buf); fbuffree(rbuf); editerror(err); return FALSE; } int u_cmd(Text *t, Cmd *cp) { int n, oseq, flag; n = cp->num; flag = TRUE; if(n < 0){ n = -n; flag = FALSE; } oseq = -1; while(n-->0 && t->file->seq!=0 && t->file->seq!=oseq){ oseq = t->file->seq; undo(t, nil, nil, flag, 0, nil, 0); } return TRUE; } int w_cmd(Text *t, Cmd *cp) { Rune *r; File *f; f = t->file; if(f->seq == seq) editerror("can't write file with pending modifications"); r = cmdname(f, cp->text, FALSE); if(r == nil) editerror("no name specified for 'w' command"); putfile(f, addr.r.q0, addr.r.q1, r, runestrlen(r)); /* r is freed by putfile */ return TRUE; } int x_cmd(Text *t, Cmd *cp) { if(cp->re) looper(t->file, cp, cp->cmdc=='x'); else linelooper(t->file, cp); return TRUE; } int X_cmd(Text*, Cmd *cp) { filelooper(cp, cp->cmdc=='X'); return TRUE; } void runpipe(Text *t, int cmd, Rune *cr, int ncr, int state) { Rune *r, *s; int n; Runestr dir; Window *w; r = skipbl(cr, ncr, &n); if(n == 0) editerror("no command specified for %c", cmd); w = nil; if(state == Inserting){ w = t->w; t->q0 = addr.r.q0; t->q1 = addr.r.q1; w->neditwrsel = 0; if(cmd == '<' || cmd=='|') elogdelete(t->file, t->q0, t->q1); } s = runemalloc(n+2); s[0] = cmd; runemove(s+1, r, n); n++; dir.r = nil; dir.nr = 0; if(t != nil) dir = dirname(t, nil, 0); if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ free(dir.r); dir.r = nil; dir.nr = 0; } editing = state; if(t!=nil && t->w!=nil) incref(t->w); /* run will decref */ run(w, runetobyte(s, n), dir.r, dir.nr, TRUE, nil, nil, TRUE); free(s); if(t!=nil && t->w!=nil) winunlock(t->w); qunlock(&row); recvul(cedit); qlock(&row); editing = Inactive; if(t!=nil && t->w!=nil) winlock(t->w, 'M'); if(state == Inserting){ t->q0 = addr.r.q0; t->q1 = addr.r.q0 + t->w->neditwrsel; } } int pipe_cmd(Text *t, Cmd *cp) { runpipe(t, cp->cmdc, cp->text->r, cp->text->n, Inserting); return TRUE; } long nlcount(Text *t, long q0, long q1) { long nl; Rune *buf; int i, nbuf; buf = fbufalloc(); nbuf = 0; i = nl = 0; while(q0 < q1){ if(i == nbuf){ nbuf = q1-q0; if(nbuf > RBUFSIZE) nbuf = RBUFSIZE; bufread(t->file, q0, buf, nbuf); i = 0; } if(buf[i++] == '\n') nl++; q0++; } fbuffree(buf); return nl; } void printposn(Text *t, int charsonly) { long l1, l2; if (t != nil && t->file != nil && t->file->name != nil) warning(nil, "%.*S:", t->file->nname, t->file->name); if(!charsonly){ l1 = 1+nlcount(t, 0, addr.r.q0); l2 = l1+nlcount(t, addr.r.q0, addr.r.q1); /* check if addr ends with '\n' */ if(addr.r.q1>0 && addr.r.q1>addr.r.q0 && textreadc(t, addr.r.q1-1)=='\n') --l2; warning(nil, "%lud", l1); if(l2 != l1) warning(nil, ",%lud", l2); warning(nil, "\n"); return; } warning(nil, "#%d", addr.r.q0); if(addr.r.q1 != addr.r.q0) warning(nil, ",#%d", addr.r.q1); warning(nil, "\n"); } int eq_cmd(Text *t, Cmd *cp) { int charsonly; switch(cp->text->n){ case 0: charsonly = FALSE; break; case 1: if(cp->text->r[0] == '#'){ charsonly = TRUE; break; } default: SET(charsonly); editerror("newline expected"); } printposn(t, charsonly); return TRUE; } int nl_cmd(Text *t, Cmd *cp) { Address a; File *f; f = t->file; if(cp->addr == 0){ /* First put it on newline boundaries */ mkaddr(&a, f); addr = lineaddr(0, a, -1); a = lineaddr(0, a, 1); addr.r.q1 = a.r.q1; if(addr.r.q0==t->q0 && addr.r.q1==t->q1){ mkaddr(&a, f); addr = lineaddr(1, a, 1); } } textshow(t, addr.r.q0, addr.r.q1, 1); return TRUE; } int append(File *f, Cmd *cp, long p) { if(cp->text->n > 0) eloginsert(f, p, cp->text->r, cp->text->n); f->curtext->q0 = p; f->curtext->q1 = p+cp->text->n; return TRUE; } int pdisplay(File *f) { long p1, p2; int np; Rune *buf; p1 = addr.r.q0; p2 = addr.r.q1; if(p2 > f->nc) p2 = f->nc; buf = fbufalloc(); while(p1 < p2){ np = p2-p1; if(np>RBUFSIZE-1) np = RBUFSIZE-1; bufread(f, p1, buf, np); buf[np] = L'\0'; warning(nil, "%S", buf); p1 += np; } fbuffree(buf); f->curtext->q0 = addr.r.q0; f->curtext->q1 = addr.r.q1; return TRUE; } void pfilename(File *f) { int dirty; Window *w; w = f->curtext->w; /* same check for dirty as in settag, but we know ncache==0 */ dirty = !w->isdir && !w->isscratch && f->mod; warning(nil, "%c%c%c %.*S\n", " '"[dirty], '+', " ."[curtext!=nil && curtext->file==f], f->nname, f->name); } void loopcmd(File *f, Cmd *cp, Range *rp, long nrp) { long i; for(i=0; icurtext->q0 = rp[i].q0; f->curtext->q1 = rp[i].q1; cmdexec(f->curtext, cp); } } void looper(File *f, Cmd *cp, int xy) { long p, op, nrp; Range r, tr; Range *rp; r = addr.r; op= xy? -1 : r.q0; nest++; if(rxcompile(cp->re->r) == FALSE) editerror("bad regexp in %c command", cp->cmdc); nrp = 0; rp = nil; for(p = r.q0; p<=r.q1; ){ if(!rxexecute(f->curtext, nil, p, r.q1, &sel)){ /* no match, but y should still run */ if(xy || op>r.q1) break; tr.q0 = op, tr.q1 = r.q1; p = r.q1+1; /* exit next loop */ }else{ if(sel.r[0].q0==sel.r[0].q1){ /* empty match? */ if(sel.r[0].q0==op){ p++; continue; } p = sel.r[0].q1+1; }else p = sel.r[0].q1; if(xy) tr = sel.r[0]; else tr.q0 = op, tr.q1 = sel.r[0].q0; } op = sel.r[0].q1; nrp++; rp = erealloc(rp, nrp*sizeof(Range)); rp[nrp-1] = tr; } loopcmd(f, cp->cmd, rp, nrp); free(rp); --nest; } void linelooper(File *f, Cmd *cp) { long nrp, p; Range r, linesel; Address a, a3; Range *rp; nest++; nrp = 0; rp = nil; r = addr.r; a3.f = f; a3.r.q0 = a3.r.q1 = r.q0; a = lineaddr(0, a3, 1); linesel = a.r; for(p = r.q0; p= r.q1) break; if(linesel.q1 >= r.q1) linesel.q1 = r.q1; if(linesel.q1 > linesel.q0) if(linesel.q0>=a3.r.q1 && linesel.q1>a3.r.q1){ a3.r = linesel; nrp++; rp = erealloc(rp, nrp*sizeof(Range)); rp[nrp-1] = linesel; continue; } break; } loopcmd(f, cp->cmd, rp, nrp); free(rp); --nest; } struct Looper { Cmd *cp; int XY; Window **w; int nw; } loopstruct; /* only one; X and Y can't nest */ void alllooper(Window *w, void *v) { Text *t; struct Looper *lp; Cmd *cp; lp = v; cp = lp->cp; // if(w->isscratch || w->isdir) // return; t = &w->body; /* only use this window if it's the current window for the file */ if(t->file->curtext != t) return; // if(w->nopen[QWevent] > 0) // return; /* no auto-execute on files without names */ if(cp->re==nil && t->file->nname==0) return; if(cp->re==nil || filematch(t->file, cp->re)==lp->XY){ lp->w = erealloc(lp->w, (lp->nw+1)*sizeof(Window*)); lp->w[lp->nw++] = w; } } void alllocker(Window *w, void *v) { if(v) incref(w); else winclose(w); } void filelooper(Cmd *cp, int XY) { int i; if(Glooping++) editerror("can't nest %c command", "YX"[XY]); nest++; loopstruct.cp = cp; loopstruct.XY = XY; if(loopstruct.w) /* error'ed out last time */ free(loopstruct.w); loopstruct.w = nil; loopstruct.nw = 0; allwindows(alllooper, &loopstruct); /* * add a ref to all windows to keep safe windows accessed by X * that would not otherwise have a ref to hold them up during * the shenanigans. note this with globalincref so that any * newly created windows start with an extra reference. */ allwindows(alllocker, (void*)1); globalincref = 1; for(i=0; ibody, cp->cmd); allwindows(alllocker, (void*)0); globalincref = 0; free(loopstruct.w); loopstruct.w = nil; --Glooping; --nest; } void nextmatch(File *f, String *r, long p, int sign) { if(rxcompile(r->r) == FALSE) editerror("bad regexp in command address"); if(sign >= 0){ if(!rxexecute(f->curtext, nil, p, 0x7FFFFFFFL, &sel)) editerror("no match for regexp"); if(sel.r[0].q0==sel.r[0].q1 && sel.r[0].q0==p){ if(++p>f->nc) p = 0; if(!rxexecute(f->curtext, nil, p, 0x7FFFFFFFL, &sel)) editerror("address"); } }else{ if(!rxbexecute(f->curtext, p, &sel)) editerror("no match for regexp"); if(sel.r[0].q0==sel.r[0].q1 && sel.r[0].q1==p){ if(--p<0) p = f->nc; if(!rxbexecute(f->curtext, p, &sel)) editerror("address"); } } } File *matchfile(String*); Address charaddr(long, Address, int); Address lineaddr(long, Address, int); Address cmdaddress(Addr *ap, Address a, int sign) { File *f = a.f; Address a1, a2; do{ switch(ap->type){ case 'l': case '#': a = (*(ap->type=='#'?charaddr:lineaddr))(ap->num, a, sign); break; case '.': mkaddr(&a, f); break; case '$': a.r.q0 = a.r.q1 = f->nc; break; case '\'': editerror("can't handle '"); // a.r = f->mark; break; case '?': sign = -sign; if(sign == 0) sign = -1; /* fall through */ case '/': nextmatch(f, ap->re, sign>=0? a.r.q1 : a.r.q0, sign); a.r = sel.r[0]; break; case '"': f = matchfile(ap->re); mkaddr(&a, f); break; case '*': a.r.q0 = 0, a.r.q1 = f->nc; return a; case ',': case ';': if(ap->left) a1 = cmdaddress(ap->left, a, 0); else a1.f = a.f, a1.r.q0 = a1.r.q1 = 0; if(ap->type == ';'){ f = a1.f; a = a1; f->curtext->q0 = a1.r.q0; f->curtext->q1 = a1.r.q1; } if(ap->next) a2 = cmdaddress(ap->next, a, 0); else a2.f = a.f, a2.r.q0 = a2.r.q1 = f->nc; if(a1.f != a2.f) editerror("addresses in different files"); a.f = a1.f, a.r.q0 = a1.r.q0, a.r.q1 = a2.r.q1; if(a.r.q1 < a.r.q0) editerror("addresses out of order"); return a; case '+': case '-': sign = 1; if(ap->type == '-') sign = -1; if(ap->next==0 || ap->next->type=='+' || ap->next->type=='-') a = lineaddr(1L, a, sign); break; default: error("cmdaddress"); return a; } }while(ap = ap->next); /* assign = */ return a; } struct Tofile{ File *f; String *r; }; void alltofile(Window *w, void *v) { Text *t; struct Tofile *tp; tp = v; if(tp->f != nil) return; if(w->isscratch || w->isdir) return; t = &w->body; /* only use this window if it's the current window for the file */ if(t->file->curtext != t) return; // if(w->nopen[QWevent] > 0) // return; if(runeeq(tp->r->r, tp->r->n, t->file->name, t->file->nname)) tp->f = t->file; } File* tofile(String *r) { struct Tofile t; String rr; rr.r = skipbl(r->r, r->n, &rr.n); t.f = nil; t.r = &rr; allwindows(alltofile, &t); if(t.f == nil) editerror("no such file\"%S\"", rr.r); return t.f; } void allmatchfile(Window *w, void *v) { struct Tofile *tp; Text *t; tp = v; if(w->isscratch || w->isdir) return; t = &w->body; /* only use this window if it's the current window for the file */ if(t->file->curtext != t) return; // if(w->nopen[QWevent] > 0) // return; if(filematch(w->body.file, tp->r)){ if(tp->f != nil) editerror("too many files match \"%S\"", tp->r->r); tp->f = w->body.file; } } File* matchfile(String *r) { struct Tofile tf; tf.f = nil; tf.r = r; allwindows(allmatchfile, &tf); if(tf.f == nil) editerror("no file matches \"%S\"", r->r); return tf.f; } int filematch(File *f, String *r) { char *buf; Rune *rbuf; Window *w; int match, i, dirty; Rangeset s; /* compile expr first so if we get an error, we haven't allocated anything */ if(rxcompile(r->r) == FALSE) editerror("bad regexp in file match"); buf = fbufalloc(); w = f->curtext->w; /* same check for dirty as in settag, but we know ncache==0 */ dirty = !w->isdir && !w->isscratch && f->mod; snprint(buf, BUFSIZE, "%c%c%c %.*S\n", " '"[dirty], '+', " ."[curtext!=nil && curtext->file==f], f->nname, f->name); rbuf = bytetorune(buf, &i); fbuffree(buf); match = rxexecute(nil, rbuf, 0, i, &s); free(rbuf); return match; } Address charaddr(long l, Address addr, int sign) { if(sign == 0) addr.r.q0 = addr.r.q1 = l; else if(sign < 0) addr.r.q1 = addr.r.q0 -= l; else if(sign > 0) addr.r.q0 = addr.r.q1 += l; if(addr.r.q0<0 || addr.r.q1>addr.f->nc) editerror("address out of range"); return addr; } Address lineaddr(long l, Address addr, int sign) { int n; int c; File *f = addr.f; Address a; long p; a.f = f; if(sign >= 0){ if(l == 0){ if(sign==0 || addr.r.q1==0){ a.r.q0 = a.r.q1 = 0; return a; } a.r.q0 = addr.r.q1; p = addr.r.q1-1; }else{ if(sign==0 || addr.r.q1==0){ p = 0; n = 1; }else{ p = addr.r.q1-1; n = textreadc(f->curtext, p++)=='\n'; } while(n < l){ if(p >= f->nc) editerror("address out of range"); if(textreadc(f->curtext, p++) == '\n') n++; } a.r.q0 = p; } while(p < f->nc && textreadc(f->curtext, p++)!='\n') ; a.r.q1 = p; }else{ p = addr.r.q0; if(l == 0) a.r.q1 = addr.r.q0; else{ for(n = 0; ncurtext, p-1); if(c != '\n' || ++n != l) p--; } } a.r.q1 = p; if(p > 0) p--; } while(p > 0 && textreadc(f->curtext, p-1)!='\n') /* lines start after a newline */ p--; a.r.q0 = p; } return a; } struct Filecheck { File *f; Rune *r; int nr; }; void allfilecheck(Window *w, void *v) { struct Filecheck *fp; File *f; fp = v; f = w->body.file; if(w->body.file == fp->f) return; if(runeeq(fp->r, fp->nr, f->name, f->nname)) warning(nil, "warning: duplicate file name \"%.*S\"\n", fp->nr, fp->r); } Rune* cmdname(File *f, String *str, int set) { Rune *r, *s; int n; struct Filecheck fc; Runestr newname; r = nil; n = str->n; s = str->r; if(n == 0){ /* no name; use existing */ if(f->nname == 0) return nil; r = runemalloc(f->nname+1); runemove(r, f->name, f->nname); return r; } s = skipbl(s, n, &n); if(n == 0) goto Return; if(s[0] == '/'){ r = runemalloc(n+1); runemove(r, s, n); }else{ newname = dirname(f->curtext, runestrdup(s), n); r = newname.r; n = newname.nr; } fc.f = f; fc.r = r; fc.nr = n; allwindows(allfilecheck, &fc); if(f->nname == 0) set = TRUE; Return: if(set && !runeeq(r, n, f->name, f->nname)){ filemark(f); f->mod = TRUE; f->curtext->w->dirty = TRUE; winsetname(f->curtext->w, r, n); } return r; } #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "edit.h" #include "fns.h" static char linex[]="\n"; static char wordx[]=" \t\n"; struct cmdtab cmdtab[]={ /* cmdc text regexp addr defcmd defaddr count token fn */ '\n', 0, 0, 0, 0, aDot, 0, 0, nl_cmd, 'a', 1, 0, 0, 0, aDot, 0, 0, a_cmd, 'b', 0, 0, 0, 0, aNo, 0, linex, b_cmd, 'c', 1, 0, 0, 0, aDot, 0, 0, c_cmd, 'd', 0, 0, 0, 0, aDot, 0, 0, d_cmd, 'e', 0, 0, 0, 0, aNo, 0, wordx, e_cmd, 'f', 0, 0, 0, 0, aNo, 0, wordx, f_cmd, 'g', 0, 1, 0, 'p', aDot, 0, 0, g_cmd, 'i', 1, 0, 0, 0, aDot, 0, 0, i_cmd, 'm', 0, 0, 1, 0, aDot, 0, 0, m_cmd, 'p', 0, 0, 0, 0, aDot, 0, 0, p_cmd, 'r', 0, 0, 0, 0, aDot, 0, wordx, e_cmd, 's', 0, 1, 0, 0, aDot, 1, 0, s_cmd, 't', 0, 0, 1, 0, aDot, 0, 0, m_cmd, 'u', 0, 0, 0, 0, aNo, 2, 0, u_cmd, 'v', 0, 1, 0, 'p', aDot, 0, 0, g_cmd, 'w', 0, 0, 0, 0, aAll, 0, wordx, w_cmd, 'x', 0, 1, 0, 'p', aDot, 0, 0, x_cmd, 'y', 0, 1, 0, 'p', aDot, 0, 0, x_cmd, '=', 0, 0, 0, 0, aDot, 0, linex, eq_cmd, 'B', 0, 0, 0, 0, aNo, 0, linex, B_cmd, 'D', 0, 0, 0, 0, aNo, 0, linex, D_cmd, 'X', 0, 1, 0, 'f', aNo, 0, 0, X_cmd, 'Y', 0, 1, 0, 'f', aNo, 0, 0, X_cmd, '<', 0, 0, 0, 0, aDot, 0, linex, pipe_cmd, '|', 0, 0, 0, 0, aDot, 0, linex, pipe_cmd, '>', 0, 0, 0, 0, aDot, 0, linex, pipe_cmd, /* deliberately unimplemented: 'k', 0, 0, 0, 0, aDot, 0, 0, k_cmd, 'n', 0, 0, 0, 0, aNo, 0, 0, n_cmd, 'q', 0, 0, 0, 0, aNo, 0, 0, q_cmd, '!', 0, 0, 0, 0, aNo, 0, linex, plan9_cmd, */ 0, 0, 0, 0, 0, 0, 0, 0, }; Cmd *parsecmd(int); Addr *compoundaddr(void); Addr *simpleaddr(void); void freecmd(void); void okdelim(int); Rune *cmdstartp; Rune *cmdendp; Rune *cmdp; Channel *editerrc; String *lastpat; int patset; List cmdlist; List addrlist; List stringlist; Text *curtext; int editing = Inactive; String* newstring(int); void editthread(void*) { Cmd *cmdp; threadsetname("editthread"); while((cmdp=parsecmd(0)) != 0){ // ocurfile = curfile; // loaded = curfile && !curfile->unread; if(cmdexec(curtext, cmdp) == 0) break; freecmd(); } sendp(editerrc, nil); } void allelogterm(Window *w, void*) { elogterm(w->body.file); } void alleditinit(Window *w, void*) { textcommit(&w->tag, TRUE); textcommit(&w->body, TRUE); w->body.file->editclean = FALSE; } void allupdate(Window *w, void*) { Text *t; int i; File *f; t = &w->body; f = t->file; if(f->curtext != t) /* do curtext only */ return; if(f->elog.type == Null) elogterm(f); else if(f->elog.type != Empty){ elogapply(f); if(f->editclean){ f->mod = FALSE; for(i=0; intext; i++) f->text[i]->w->dirty = FALSE; } } textsetselect(t, t->q0, t->q1); textscrdraw(t); winsettag(w); } void editerror(char *fmt, ...) { va_list arg; char *s; va_start(arg, fmt); s = vsmprint(fmt, arg); va_end(arg); freecmd(); allwindows(allelogterm, nil); /* truncate the edit logs */ sendp(editerrc, s); threadexits(nil); } void editcmd(Text *ct, Rune *r, uint n) { char *err; if(n == 0) return; if(2*n > RBUFSIZE){ warning(nil, "string too long\n"); return; } allwindows(alleditinit, nil); if(cmdstartp) free(cmdstartp); cmdstartp = runemalloc(n+2); runemove(cmdstartp, r, n); if(r[n] != '\n') cmdstartp[n++] = '\n'; cmdstartp[n] = '\0'; cmdendp = cmdstartp+n; cmdp = cmdstartp; if(ct->w == nil) curtext = nil; else curtext = &ct->w->body; resetxec(); if(editerrc == nil){ editerrc = chancreate(sizeof(char*), 0); lastpat = allocstring(0); } threadcreate(editthread, nil, STACK); err = recvp(editerrc); editing = Inactive; if(err != nil){ if(err[0] != '\0') warning(nil, "Edit: %s\n", err); free(err); } /* update everyone whose edit log has data */ allwindows(allupdate, nil); } int getch(void) { if(*cmdp == *cmdendp) return -1; return *cmdp++; } int nextc(void) { if(*cmdp == *cmdendp) return -1; return *cmdp; } void ungetch(void) { if(--cmdp < cmdstartp) error("ungetch"); } long getnum(int signok) { long n; int c, sign; n = 0; sign = 1; if(signok>1 && nextc()=='-'){ sign = -1; getch(); } if((c=nextc())<'0' || '9'= 0) ungetch(); return c; } /* * Check that list has room for one more element. */ void growlist(List *l) { if(l->listptr==0 || l->nalloc==0){ l->nalloc = INCR; l->listptr = emalloc(INCR*sizeof(long)); l->nused = 0; }else if(l->nused == l->nalloc){ l->listptr = erealloc(l->listptr, (l->nalloc+INCR)*sizeof(long)); memset((void*)(l->longptr+l->nalloc), 0, INCR*sizeof(long)); l->nalloc += INCR; } } /* * Remove the ith element from the list */ void dellist(List *l, int i) { memmove(&l->longptr[i], &l->longptr[i+1], (l->nused-(i+1))*sizeof(long)); l->nused--; } /* * Add a new element, whose position is i, to the list */ void inslist(List *l, int i, long val) { growlist(l); memmove(&l->longptr[i+1], &l->longptr[i], (l->nused-i)*sizeof(long)); l->longptr[i] = val; l->nused++; } void listfree(List *l) { free(l->listptr); free(l); } String* allocstring(int n) { String *s; s = emalloc(sizeof(String)); s->n = n; s->nalloc = n+10; s->r = emalloc(s->nalloc*sizeof(Rune)); s->r[n] = '\0'; return s; } void freestring(String *s) { free(s->r); free(s); } Cmd* newcmd(void){ Cmd *p; p = emalloc(sizeof(Cmd)); inslist(&cmdlist, cmdlist.nused, (long)p); return p; } String* newstring(int n) { String *p; p = allocstring(n); inslist(&stringlist, stringlist.nused, (long)p); return p; } Addr* newaddr(void) { Addr *p; p = emalloc(sizeof(Addr)); inslist(&addrlist, addrlist.nused, (long)p); return p; } void freecmd(void) { int i; while(cmdlist.nused > 0) free(cmdlist.ucharptr[--cmdlist.nused]); while(addrlist.nused > 0) free(addrlist.ucharptr[--addrlist.nused]); while(stringlist.nused>0){ i = --stringlist.nused; freestring(stringlist.stringptr[i]); } } void okdelim(int c) { if(c=='\\' || ('a'<=c && c<='z') || ('A'<=c && c<='Z') || ('0'<=c && c<='9')) editerror("bad delimiter %c\n", c); } void atnl(void) { int c; cmdskipbl(); c = getch(); if(c != '\n') editerror("newline expected (saw %C)", c); } void Straddc(String *s, int c) { if(s->n+1 >= s->nalloc){ s->nalloc += 10; s->r = erealloc(s->r, s->nalloc*sizeof(Rune)); } s->r[s->n++] = c; s->r[s->n] = '\0'; } void getrhs(String *s, int delim, int cmd) { int c; while((c = getch())>0 && c!=delim && c!='\n'){ if(c == '\\'){ if((c=getch()) <= 0) error("bad right hand side"); if(c == '\n'){ ungetch(); c='\\'; }else if(c == 'n') c='\n'; else if(c!=delim && (cmd=='s' || c!='\\')) /* s does its own */ Straddc(s, '\\'); } Straddc(s, c); } ungetch(); /* let client read whether delimiter, '\n' or whatever */ } String * collecttoken(char *end) { String *s = newstring(0); int c; while((c=nextc())==' ' || c=='\t') Straddc(s, getch()); /* blanks significant for getname() */ while((c=getch())>0 && utfrune(end, c)==0) Straddc(s, c); if(c != '\n') atnl(); return s; } String * collecttext(void) { String *s; int begline, i, c, delim; s = newstring(0); if(cmdskipbl()=='\n'){ getch(); i = 0; do{ begline = i; while((c = getch())>0 && c!='\n') i++, Straddc(s, c); i++, Straddc(s, '\n'); if(c < 0) goto Return; }while(s->r[begline]!='.' || s->r[begline+1]!='\n'); s->r[s->n-2] = '\0'; }else{ okdelim(delim = getch()); getrhs(s, delim, 'a'); if(nextc()==delim) getch(); atnl(); } Return: return s; } int cmdlookup(int c) { int i; for(i=0; cmdtab[i].cmdc; i++) if(cmdtab[i].cmdc == c) return i; return -1; } Cmd* parsecmd(int nest) { int i, c; struct cmdtab *ct; Cmd *cp, *ncp; Cmd cmd; cmd.next = cmd.cmd = 0; cmd.re = 0; cmd.flag = cmd.num = 0; cmd.addr = compoundaddr(); if(cmdskipbl() == -1) return 0; if((c=getch())==-1) return 0; cmd.cmdc = c; if(cmd.cmdc=='c' && nextc()=='d'){ /* sleazy two-character case */ getch(); /* the 'd' */ cmd.cmdc='c'|0x100; } i = cmdlookup(cmd.cmdc); if(i >= 0){ if(cmd.cmdc == '\n') goto Return; /* let nl_cmd work it all out */ ct = &cmdtab[i]; if(ct->defaddr==aNo && cmd.addr) editerror("command takes no address"); if(ct->count) cmd.num = getnum(ct->count); if(ct->regexp){ /* x without pattern -> .*\n, indicated by cmd.re==0 */ /* X without pattern is all files */ if((ct->cmdc!='x' && ct->cmdc!='X') || ((c = nextc())!=' ' && c!='\t' && c!='\n')){ cmdskipbl(); if((c = getch())=='\n' || c<0) editerror("no address"); okdelim(c); cmd.re = getregexp(c); if(ct->cmdc == 's'){ cmd.text = newstring(0); getrhs(cmd.text, c, 's'); if(nextc() == c){ getch(); if(nextc() == 'g') cmd.flag = getch(); } } } } if(ct->addr && (cmd.mtaddr=simpleaddr())==0) editerror("bad address"); if(ct->defcmd){ if(cmdskipbl() == '\n'){ getch(); cmd.cmd = newcmd(); cmd.cmd->cmdc = ct->defcmd; }else if((cmd.cmd = parsecmd(nest))==0) error("defcmd"); }else if(ct->text) cmd.text = collecttext(); else if(ct->token) cmd.text = collecttoken(ct->token); else atnl(); }else switch(cmd.cmdc){ case '{': cp = 0; do{ if(cmdskipbl()=='\n') getch(); ncp = parsecmd(nest+1); if(cp) cp->next = ncp; else cmd.cmd = ncp; }while(cp = ncp); break; case '}': atnl(); if(nest==0) editerror("right brace with no left brace"); return 0; default: editerror("unknown command %c", cmd.cmdc); } Return: cp = newcmd(); *cp = cmd; return cp; } String* getregexp(int delim) { String *buf, *r; int i, c; buf = allocstring(0); for(i=0; ; i++){ if((c = getch())=='\\'){ if(nextc()==delim) c = getch(); else if(nextc()=='\\'){ Straddc(buf, c); c = getch(); } }else if(c==delim || c=='\n') break; if(i >= RBUFSIZE) editerror("regular expression too long"); Straddc(buf, c); } if(c!=delim && c) ungetch(); if(buf->n > 0){ patset = TRUE; freestring(lastpat); lastpat = buf; }else freestring(buf); if(lastpat->n == 0) editerror("no regular expression defined"); r = newstring(lastpat->n); runemove(r->r, lastpat->r, lastpat->n); /* newstring put \0 at end */ return r; } Addr * simpleaddr(void) { Addr addr; Addr *ap, *nap; addr.next = 0; addr.left = 0; switch(cmdskipbl()){ case '#': addr.type = getch(); addr.num = getnum(1); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': addr.num = getnum(1); addr.type='l'; break; case '/': case '?': case '"': addr.re = getregexp(addr.type = getch()); break; case '.': case '$': case '+': case '-': case '\'': addr.type = getch(); break; default: return 0; } if(addr.next = simpleaddr()) switch(addr.next->type){ case '.': case '$': case '\'': if(addr.type!='"') case '"': editerror("bad address syntax"); break; case 'l': case '#': if(addr.type=='"') break; /* fall through */ case '/': case '?': if(addr.type!='+' && addr.type!='-'){ /* insert the missing '+' */ nap = newaddr(); nap->type='+'; nap->next = addr.next; addr.next = nap; } break; case '+': case '-': break; default: error("simpleaddr"); } ap = newaddr(); *ap = addr; return ap; } Addr * compoundaddr(void) { Addr addr; Addr *ap, *next; addr.left = simpleaddr(); if((addr.type = cmdskipbl())!=',' && addr.type!=';') return addr.left; getch(); next = addr.next = compoundaddr(); if(next && (next->type==',' || next->type==';') && next->left==0) editerror("bad address syntax"); ap = newaddr(); *ap = addr; return ap; } #pragma varargck argpos editerror 1 typedef struct Addr Addr; typedef struct Address Address; typedef struct Cmd Cmd; typedef struct List List; typedef struct String String; struct String { int n; /* excludes NUL */ Rune *r; /* includes NUL */ int nalloc; }; struct Addr { char type; /* # (char addr), l (line addr), / ? . $ + - , ; */ union{ String *re; Addr *left; /* left side of , and ; */ }; ulong num; Addr *next; /* or right side of , and ; */ }; struct Address { Range r; File *f; }; struct Cmd { Addr *addr; /* address (range of text) */ String *re; /* regular expression for e.g. 'x' */ union{ Cmd *cmd; /* target of x, g, {, etc. */ String *text; /* text of a, c, i; rhs of s */ Addr *mtaddr; /* address for m, t */ }; Cmd *next; /* pointer to next element in {} */ short num; ushort flag; /* whatever */ ushort cmdc; /* command character; 'x' etc. */ }; extern struct cmdtab{ ushort cmdc; /* command character */ uchar text; /* takes a textual argument? */ uchar regexp; /* takes a regular expression? */ uchar addr; /* takes an address (m or t)? */ uchar defcmd; /* default command; 0==>none */ uchar defaddr; /* default address */ uchar count; /* takes a count e.g. s2/// */ char *token; /* takes text terminated by one of these */ int (*fn)(Text*, Cmd*); /* function to call with parse tree */ }cmdtab[]; #define INCR 25 /* delta when growing list */ struct List /* code depends on a long being able to hold a pointer */ { int nalloc; int nused; union{ void *listptr; Block *blkptr; long *longptr; uchar* *ucharptr; String* *stringptr; File* *fileptr; }; }; enum Defaddr{ /* default addresses */ aNo, aDot, aAll, }; int nl_cmd(Text*, Cmd*), a_cmd(Text*, Cmd*), b_cmd(Text*, Cmd*); int c_cmd(Text*, Cmd*), d_cmd(Text*, Cmd*); int B_cmd(Text*, Cmd*), D_cmd(Text*, Cmd*), e_cmd(Text*, Cmd*); int f_cmd(Text*, Cmd*), g_cmd(Text*, Cmd*), i_cmd(Text*, Cmd*); int k_cmd(Text*, Cmd*), m_cmd(Text*, Cmd*), n_cmd(Text*, Cmd*); int p_cmd(Text*, Cmd*); int s_cmd(Text*, Cmd*), u_cmd(Text*, Cmd*), w_cmd(Text*, Cmd*); int x_cmd(Text*, Cmd*), X_cmd(Text*, Cmd*), pipe_cmd(Text*, Cmd*); int eq_cmd(Text*, Cmd*); String *allocstring(int); void freestring(String*); String *getregexp(int); Addr *newaddr(void); Address cmdaddress(Addr*, Address, int); int cmdexec(Text*, Cmd*); void editerror(char*, ...); int cmdlookup(int); void resetxec(void); void Straddc(String*, int); #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" #include "edit.h" static char Wsequence[] = "warning: changes out of sequence\n"; static int warned = FALSE; /* * Log of changes made by editing commands. Three reasons for this: * 1) We want addresses in commands to apply to old file, not file-in-change. * 2) It's difficult to track changes correctly as things move, e.g. ,x m$ * 3) This gives an opportunity to optimize by merging adjacent changes. * It's a little bit like the Undo/Redo log in Files, but Point 3) argues for a * separate implementation. To do this well, we use Replace as well as * Insert and Delete */ typedef struct Buflog Buflog; struct Buflog { short type; /* Replace, Filename */ uint q0; /* location of change (unused in f) */ uint nd; /* # runes to delete */ uint nr; /* # runes in string or file name */ }; enum { Buflogsize = sizeof(Buflog)/sizeof(Rune), }; /* * Minstring shouldn't be very big or we will do lots of I/O for small changes. * Maxstring is RBUFSIZE so we can fbufalloc() once and not realloc elog.r. */ enum { Minstring = 16, /* distance beneath which we merge changes */ Maxstring = RBUFSIZE, /* maximum length of change we will merge into one */ }; void eloginit(File *f) { if(f->elog.type != Empty) return; f->elog.type = Null; if(f->elogbuf == nil) f->elogbuf = emalloc(sizeof(Buffer)); if(f->elog.r == nil) f->elog.r = fbufalloc(); bufreset(f->elogbuf); } void elogclose(File *f) { if(f->elogbuf){ bufclose(f->elogbuf); free(f->elogbuf); f->elogbuf = nil; } } void elogreset(File *f) { f->elog.type = Null; f->elog.nd = 0; f->elog.nr = 0; } void elogterm(File *f) { elogreset(f); if(f->elogbuf) bufreset(f->elogbuf); f->elog.type = Empty; fbuffree(f->elog.r); f->elog.r = nil; warned = FALSE; } void elogflush(File *f) { Buflog b; b.type = f->elog.type; b.q0 = f->elog.q0; b.nd = f->elog.nd; b.nr = f->elog.nr; switch(f->elog.type){ default: warning(nil, "unknown elog type 0x%ux\n", f->elog.type); break; case Null: break; case Insert: case Replace: if(f->elog.nr > 0) bufinsert(f->elogbuf, f->elogbuf->nc, f->elog.r, f->elog.nr); /* fall through */ case Delete: bufinsert(f->elogbuf, f->elogbuf->nc, (Rune*)&b, Buflogsize); break; } elogreset(f); } void elogreplace(File *f, int q0, int q1, Rune *r, int nr) { uint gap; if(q0==q1 && nr==0) return; eloginit(f); if(f->elog.type!=Null && q0elog.q0){ if(warned++ == 0) warning(nil, Wsequence); elogflush(f); } /* try to merge with previous */ gap = q0 - (f->elog.q0+f->elog.nd); /* gap between previous and this */ if(f->elog.type==Replace && f->elog.nr+gap+nr 0){ bufread(f, f->elog.q0+f->elog.nd, f->elog.r+f->elog.nr, gap); f->elog.nr += gap; } f->elog.nd += gap + q1-q0; runemove(f->elog.r+f->elog.nr, r, nr); f->elog.nr += nr; return; } } elogflush(f); f->elog.type = Replace; f->elog.q0 = q0; f->elog.nd = q1-q0; f->elog.nr = nr; if(nr > RBUFSIZE) editerror("internal error: replacement string too large(%d)", nr); runemove(f->elog.r, r, nr); } void eloginsert(File *f, int q0, Rune *r, int nr) { int n; if(nr == 0) return; eloginit(f); if(f->elog.type!=Null && q0elog.q0){ if(warned++ == 0) warning(nil, Wsequence); elogflush(f); } /* try to merge with previous */ if(f->elog.type==Insert && q0==f->elog.q0 && f->elog.nr+nrelog.r+f->elog.nr, r, nr); f->elog.nr += nr; return; } while(nr > 0){ elogflush(f); f->elog.type = Insert; f->elog.q0 = q0; n = nr; if(n > RBUFSIZE) n = RBUFSIZE; f->elog.nr = n; runemove(f->elog.r, r, n); r += n; nr -= n; } } void elogdelete(File *f, int q0, int q1) { if(q0 == q1) return; eloginit(f); if(f->elog.type!=Null && q0elog.q0+f->elog.nd){ if(warned++ == 0) warning(nil, Wsequence); elogflush(f); } /* try to merge with previous */ if(f->elog.type==Delete && f->elog.q0+f->elog.nd==q0){ f->elog.nd += q1-q0; return; } elogflush(f); f->elog.type = Delete; f->elog.q0 = q0; f->elog.nd = q1-q0; } #define tracelog 0 void elogapply(File *f) { Buflog b; Rune *buf; uint i, n, up, mod; uint q0, q1, tq0, tq1; Buffer *log; Text *t; elogflush(f); log = f->elogbuf; t = f->curtext; buf = fbufalloc(); mod = FALSE; /* * The edit commands have already updated the selection in t->q0, t->q1. * (At least, they are supposed to have updated them. * We still keep finding commands that don't do it right.) * The textinsert and textdelete calls below will update it again, so save the * current setting and restore it at the end. */ q0 = t->q0; q1 = t->q1; /* * We constrain the addresses in here (with textconstrain()) because * overlapping changes will generate bogus addresses. We will warn * about changes out of sequence but proceed anyway; here we must * keep things in range. */ while(log->nc > 0){ up = log->nc-Buflogsize; bufread(log, up, (Rune*)&b, Buflogsize); switch(b.type){ default: fprint(2, "elogapply: 0x%ux\n", b.type); abort(); break; case Replace: if(tracelog) warning(nil, "elog replace %d %d\n", b.q0, b.q0+b.nd); if(!mod){ mod = TRUE; filemark(f); } textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1); textdelete(t, tq0, tq1, TRUE); up -= b.nr; for(i=0; i RBUFSIZE) n = RBUFSIZE; bufread(log, up+i, buf, n); textinsert(t, tq0+i, buf, n, TRUE); } break; case Delete: if(tracelog) warning(nil, "elog delete %d %d\n", b.q0, b.q0+b.nd); if(!mod){ mod = TRUE; filemark(f); } textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1); textdelete(t, tq0, tq1, TRUE); break; case Insert: if(tracelog) warning(nil, "elog insert %d %d\n", b.q0, b.q0+b.nr); if(!mod){ mod = TRUE; filemark(f); } textconstrain(t, b.q0, b.q0, &tq0, &tq1); up -= b.nr; for(i=0; i RBUFSIZE) n = RBUFSIZE; bufread(log, up+i, buf, n); textinsert(t, tq0+i, buf, n, TRUE); } break; /* case Filename: f->seq = u.seq; fileunsetname(f, epsilon); f->mod = u.mod; up -= u.n; free(f->name); if(u.n == 0) f->name = nil; else f->name = runemalloc(u.n); bufread(delta, up, f->name, u.n); f->nname = u.n; break; */ } bufdelete(log, up, log->nc); } fbuffree(buf); if(warned){ /* * Changes were out of order, so the q0 and q1 * computed while generating those changes are not * to be trusted. */ q1 = min(q1, f->nc); q0 = min(q0, q1); } elogterm(f); /* * The q0 and q1 are supposed to be fine (see comment * above, where we saved them), but bad addresses * will cause bufload to crash, so double check. */ if(q0 > f->nc || q1 > f->nc || q0 > q1){ warning(nil, "elogapply: can't happen %d %d %d\n", q0, q1, f->nc); q1 = min(q1, f->nc); q0 = min(q0, q1); } t->q0 = q0; t->q1 = q1; } #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" Buffer snarfbuf; void del(Text*, Text*, Text*, int, int, Rune*, int); void delcol(Text*, Text*, Text*, int, int, Rune*, int); void dump(Text*, Text*, Text*, int, int, Rune*, int); void edit(Text*, Text*, Text*, int, int, Rune*, int); void exit(Text*, Text*, Text*, int, int, Rune*, int); void fontx(Text*, Text*, Text*, int, int, Rune*, int); void get(Text*, Text*, Text*, int, int, Rune*, int); void id(Text*, Text*, Text*, int, int, Rune*, int); void incl(Text*, Text*, Text*, int, int, Rune*, int); void indent(Text*, Text*, Text*, int, int, Rune*, int); void kill(Text*, Text*, Text*, int, int, Rune*, int); void local(Text*, Text*, Text*, int, int, Rune*, int); void look(Text*, Text*, Text*, int, int, Rune*, int); void newcol(Text*, Text*, Text*, int, int, Rune*, int); void paste(Text*, Text*, Text*, int, int, Rune*, int); void put(Text*, Text*, Text*, int, int, Rune*, int); void putall(Text*, Text*, Text*, int, int, Rune*, int); void sendx(Text*, Text*, Text*, int, int, Rune*, int); void sort(Text*, Text*, Text*, int, int, Rune*, int); void tab(Text*, Text*, Text*, int, int, Rune*, int); void zeroxx(Text*, Text*, Text*, int, int, Rune*, int); typedef struct Exectab Exectab; struct Exectab { Rune *name; void (*fn)(Text*, Text*, Text*, int, int, Rune*, int); int mark; int flag1; int flag2; }; Exectab exectab[] = { { L"Cut", cut, TRUE, TRUE, TRUE }, { L"Del", del, FALSE, FALSE, XXX }, { L"Delcol", delcol, FALSE, XXX, XXX }, { L"Delete", del, FALSE, TRUE, XXX }, { L"Dump", dump, FALSE, TRUE, XXX }, { L"Edit", edit, FALSE, XXX, XXX }, { L"Exit", exit, FALSE, XXX, XXX }, { L"Font", fontx, FALSE, XXX, XXX }, { L"Get", get, FALSE, TRUE, XXX }, { L"ID", id, FALSE, XXX, XXX }, { L"Incl", incl, FALSE, XXX, XXX }, { L"Indent", indent, FALSE, XXX, XXX }, { L"Kill", kill, FALSE, XXX, XXX }, { L"Load", dump, FALSE, FALSE, XXX }, { L"Local", local, FALSE, XXX, XXX }, { L"Look", look, FALSE, XXX, XXX }, { L"New", new, FALSE, XXX, XXX }, { L"Newcol", newcol, FALSE, XXX, XXX }, { L"Paste", paste, TRUE, TRUE, XXX }, { L"Put", put, FALSE, XXX, XXX }, { L"Putall", putall, FALSE, XXX, XXX }, { L"Redo", undo, FALSE, FALSE, XXX }, { L"Send", sendx, TRUE, XXX, XXX }, { L"Snarf", cut, FALSE, TRUE, FALSE }, { L"Sort", sort, FALSE, XXX, XXX }, { L"Tab", tab, FALSE, XXX, XXX }, { L"Undo", undo, FALSE, TRUE, XXX }, { L"Zerox", zeroxx, FALSE, XXX, XXX }, { nil, nil, 0, 0, 0 }, }; Exectab* lookup(Rune *r, int n) { Exectab *e; int nr; r = skipbl(r, n, &n); if(n == 0) return nil; findbl(r, n, &nr); nr = n-nr; for(e=exectab; e->name; e++) if(runeeq(r, nr, e->name, runestrlen(e->name)) == TRUE) return e; return nil; } int isexecc(int c) { if(isfilec(c)) return 1; return c=='<' || c=='|' || c=='>'; } void execute(Text *t, uint aq0, uint aq1, int external, Text *argt) { uint q0, q1; Rune *r, *s; char *b, *a, *aa; Exectab *e; int c, n, f; Runestr dir; q0 = aq0; q1 = aq1; if(q1 == q0){ /* expand to find word (actually file name) */ /* if in selection, choose selection */ if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){ q0 = t->q0; q1 = t->q1; }else{ while(q1file->nc && isexecc(c=textreadc(t, q1)) && c!=':') q1++; while(q0>0 && isexecc(c=textreadc(t, q0-1)) && c!=':') q0--; if(q1 == q0) return; } } r = runemalloc(q1-q0); bufread(t->file, q0, r, q1-q0); e = lookup(r, q1-q0); if(!external && t->w!=nil && t->w->nopen[QWevent]>0){ f = 0; if(e) f |= 1; if(q0!=aq0 || q1!=aq1){ bufread(t->file, aq0, r, aq1-aq0); f |= 2; } aa = getbytearg(argt, TRUE, TRUE, &a); if(a){ if(strlen(a) > EVENTSIZE){ /* too big; too bad */ free(aa); free(a); warning(nil, "`argument string too long\n"); return; } f |= 8; } c = 'x'; if(t->what == Body) c = 'X'; n = aq1-aq0; if(n <= EVENTSIZE) winevent(t->w, "%c%d %d %d %d %.*S\n", c, aq0, aq1, f, n, n, r); else winevent(t->w, "%c%d %d %d 0 \n", c, aq0, aq1, f, n); if(q0!=aq0 || q1!=aq1){ n = q1-q0; bufread(t->file, q0, r, n); if(n <= EVENTSIZE) winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q1, n, n, r); else winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1, n); } if(a){ winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(a), a); if(aa) winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(aa), aa); else winevent(t->w, "%c0 0 0 0 \n", c); } free(r); free(aa); free(a); return; } if(e){ if(e->mark && seltext!=nil) if(seltext->what == Body){ seq++; filemark(seltext->w->body.file); } s = skipbl(r, q1-q0, &n); s = findbl(s, n, &n); s = skipbl(s, n, &n); (*e->fn)(t, seltext, argt, e->flag1, e->flag2, s, n); free(r); return; } b = runetobyte(r, q1-q0); free(r); dir = dirname(t, nil, 0); if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ free(dir.r); dir.r = nil; dir.nr = 0; } aa = getbytearg(argt, TRUE, TRUE, &a); if(t->w) incref(t->w); run(t->w, b, dir.r, dir.nr, TRUE, aa, a, FALSE); } char* printarg(Text *argt, uint q0, uint q1) { char *buf; if(argt->what!=Body || argt->file->name==nil) return nil; buf = emalloc(argt->file->nname+32); if(q0 == q1) sprint(buf, "%.*S:#%d", argt->file->nname, argt->file->name, q0); else sprint(buf, "%.*S:#%d,#%d", argt->file->nname, argt->file->name, q0, q1); return buf; } char* getarg(Text *argt, int doaddr, int dofile, Rune **rp, int *nrp) { int n; Expand e; char *a; *rp = nil; *nrp = 0; if(argt == nil) return nil; a = nil; textcommit(argt, TRUE); if(expand(argt, argt->q0, argt->q1, &e)){ free(e.bname); if(e.nname && dofile){ e.name = runerealloc(e.name, e.nname+1); if(doaddr) a = printarg(argt, e.q0, e.q1); *rp = e.name; *nrp = e.nname; return a; } free(e.name); }else{ e.q0 = argt->q0; e.q1 = argt->q1; } n = e.q1 - e.q0; *rp = runemalloc(n+1); bufread(argt->file, e.q0, *rp, n); if(doaddr) a = printarg(argt, e.q0, e.q1); *nrp = n; return a; } char* getbytearg(Text *argt, int doaddr, int dofile, char **bp) { Rune *r; int n; char *aa; *bp = nil; aa = getarg(argt, doaddr, dofile, &r, &n); if(r == nil) return nil; *bp = runetobyte(r, n); free(r); return aa; } void newcol(Text *et, Text*, Text*, int, int, Rune*, int) { Column *c; c = rowadd(et->row, nil, -1); if(c) winsettag(coladd(c, nil, nil, -1)); } void delcol(Text *et, Text*, Text*, int, int, Rune*, int) { int i; Column *c; Window *w; c = et->col; if(c==nil || colclean(c)==0) return; for(i=0; inw; i++){ w = c->w[i]; if(w->nopen[QWevent]+w->nopen[QWaddr]+w->nopen[QWdata] > 0){ warning(nil, "can't delete column; %.*S is running an external command\n", w->body.file->nname, w->body.file->name); return; } } rowclose(et->col->row, et->col, TRUE); } void del(Text *et, Text*, Text*, int flag1, int, Rune*, int) { if(et->col==nil || et->w == nil) return; if(flag1 || et->w->body.file->ntext>1 || winclean(et->w, FALSE)) colclose(et->col, et->w, TRUE); } void sort(Text *et, Text*, Text*, int, int, Rune*, int) { if(et->col) colsort(et->col); } uint seqof(Window *w, int isundo) { /* if it's undo, see who changed with us */ if(isundo) return w->body.file->seq; /* if it's redo, see who we'll be sync'ed up with */ return fileredoseq(w->body.file); } void undo(Text *et, Text*, Text*, int flag1, int, Rune*, int) { int i, j; Column *c; Window *w; uint seq; if(et==nil || et->w== nil) return; seq = seqof(et->w, flag1); if(seq == 0){ /* nothing to undo */ return; } /* * Undo the executing window first. Its display will update. other windows * in the same file will not call show() and jump to a different location in the file. * Simultaneous changes to other files will be chaotic, however. */ winundo(et->w, flag1); for(i=0; inw; j++){ w = c->w[j]; if(w == et->w) continue; if(seqof(w, flag1) == seq) winundo(w, flag1); } } } char* getname(Text *t, Text *argt, Rune *arg, int narg, int isput) { char *s; Rune *r; int i, n, promote; Runestr dir; getarg(argt, FALSE, TRUE, &r, &n); promote = FALSE; if(r == nil) promote = TRUE; else if(isput){ /* if are doing a Put, want to synthesize name even for non-existent file */ /* best guess is that file name doesn't contain a slash */ promote = TRUE; for(i=0; ifile->name, t->file->nname); return s; } /* prefix with directory name if necessary */ dir.r = nil; dir.nr = 0; if(n>0 && arg[0]!='/'){ dir = dirname(t, nil, 0); if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ free(dir.r); dir.r = nil; dir.nr = 0; } } if(dir.r){ r = runemalloc(dir.nr+n+1); runemove(r, dir.r, dir.nr); free(dir.r); runemove(r+dir.nr, arg, n); n += dir.nr; }else{ r = runemalloc(n+1); runemove(r, arg, n); } } s = runetobyte(r, n); free(r); if(strlen(s) == 0){ free(s); s = nil; } return s; } void zeroxx(Text *et, Text *t, Text*, int, int, Rune*, int) { Window *nw; int c, locked; locked = FALSE; if(t!=nil && t->w!=nil && t->w!=et->w){ locked = TRUE; c = 'M'; if(et->w) c = et->w->owner; winlock(t->w, c); } if(t == nil) t = et; if(t==nil || t->w==nil) return; t = &t->w->body; if(t->w->isdir) warning(nil, "%.*S is a directory; Zerox illegal\n", t->file->nname, t->file->name); else{ nw = coladd(t->w->col, nil, t->w, -1); /* ugly: fix locks so w->unlock works */ winlock1(nw, t->w->owner); } if(locked) winunlock(t->w); } void get(Text *et, Text *t, Text *argt, int flag1, int, Rune *arg, int narg) { char *name; Rune *r; int i, n, dirty, samename, isdir; Window *w; Text *u; Dir *d; if(flag1) if(et==nil || et->w==nil) return; if(!et->w->isdir && (et->w->body.file->nc>0 && !winclean(et->w, TRUE))) return; w = et->w; t = &w->body; name = getname(t, argt, arg, narg, FALSE); if(name == nil){ warning(nil, "no file name\n"); return; } if(t->file->ntext>1){ d = dirstat(name); isdir = (d!=nil && (d->qid.type & QTDIR)); free(d); if(isdir) warning(nil, "%s is a directory; can't read with multiple windows on it\n", name); return; } r = bytetorune(name, &n); for(i=0; ifile->ntext; i++){ u = t->file->text[i]; /* second and subsequent calls with zero an already empty buffer, but OK */ textreset(u); windirfree(u->w); } samename = runeeq(r, n, t->file->name, t->file->nname); textload(t, 0, name, samename); if(samename){ t->file->mod = FALSE; dirty = FALSE; }else{ t->file->mod = TRUE; dirty = TRUE; } for(i=0; ifile->ntext; i++) t->file->text[i]->w->dirty = dirty; free(name); free(r); winsettag(w); t->file->unread = FALSE; for(i=0; ifile->ntext; i++){ u = t->file->text[i]; textsetselect(&u->w->tag, u->w->tag.file->nc, u->w->tag.file->nc); textscrdraw(u); } } void putfile(File *f, int q0, int q1, Rune *namer, int nname) { uint n, m; Rune *r; char *s, *name; int i, fd, q; Dir *d, *d1; Window *w; int isapp; w = f->curtext->w; name = runetobyte(namer, nname); d = dirstat(name); if(d!=nil && runeeq(namer, nname, f->name, f->nname)){ /* f->mtime+1 because when talking over NFS it's often off by a second */ if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime+1mtime){ f->dev = d->dev; f->qidpath = d->qid.path; f->mtime = d->mtime; if(f->unread) warning(nil, "%s not written; file already exists\n", name); else warning(nil, "%s modified%s%s since last read\n", name, d->muid[0]?" by ":"", d->muid); goto Rescue1; } } fd = create(name, OWRITE, 0666); if(fd < 0){ warning(nil, "can't create file %s: %r\n", name); goto Rescue1; } r = fbufalloc(); s = fbufalloc(); free(d); d = dirfstat(fd); isapp = (d!=nil && d->length>0 && (d->qid.type&QTAPPEND)); if(isapp){ warning(nil, "%s not written; file is append only\n", name); goto Rescue2; } for(q=q0; q BUFSIZE/UTFmax) n = BUFSIZE/UTFmax; bufread(f, q, r, n); m = snprint(s, BUFSIZE+1, "%.*S", n, r); if(write(fd, s, m) != m){ warning(nil, "can't write file %s: %r\n", name); goto Rescue2; } } if(runeeq(namer, nname, f->name, f->nname)){ if(q0!=0 || q1!=f->nc){ f->mod = TRUE; w->dirty = TRUE; f->unread = TRUE; }else{ d1 = dirfstat(fd); if(d1 != nil){ free(d); d = d1; } f->qidpath = d->qid.path; f->dev = d->dev; f->mtime = d->mtime; f->mod = FALSE; w->dirty = FALSE; f->unread = FALSE; } for(i=0; intext; i++){ f->text[i]->w->putseq = f->seq; f->text[i]->w->dirty = w->dirty; } } fbuffree(s); fbuffree(r); free(d); free(namer); free(name); close(fd); winsettag(w); return; Rescue2: fbuffree(s); fbuffree(r); close(fd); /* fall through */ Rescue1: free(d); free(namer); free(name); } void put(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) { int nname; Rune *namer; Window *w; File *f; char *name; if(et==nil || et->w==nil || et->w->isdir) return; w = et->w; f = w->body.file; name = getname(&w->body, argt, arg, narg, TRUE); if(name == nil){ warning(nil, "no file name\n"); return; } namer = bytetorune(name, &nname); putfile(f, 0, f->nc, namer, nname); free(name); } void dump(Text *, Text *, Text *argt, int isdump, int, Rune *arg, int narg) { char *name; if(narg) name = runetobyte(arg, narg); else getbytearg(argt, FALSE, TRUE, &name); if(isdump) rowdump(&row, name); else rowload(&row, name, FALSE); free(name); } void cut(Text *et, Text *t, Text*, int dosnarf, int docut, Rune*, int) { uint q0, q1, n, locked, c; Rune *r; /* use current window if snarfing and its selection is non-null */ if(et!=t && dosnarf && et->w!=nil){ if(et->w->body.q1>et->w->body.q0){ t = &et->w->body; if(docut) filemark(t->file); /* seq has been incremented by execute */ }else if(et->w->tag.q1>et->w->tag.q0) t = &et->w->tag; } if(t == nil){ /* can only happen if seltext == nil */ return; } locked = FALSE; if(t->w!=nil && et->w!=t->w){ locked = TRUE; c = 'M'; if(et->w) c = et->w->owner; winlock(t->w, c); } if(t->q0 == t->q1){ if(locked) winunlock(t->w); return; } if(dosnarf){ q0 = t->q0; q1 = t->q1; bufdelete(&snarfbuf, 0, snarfbuf.nc); r = fbufalloc(); while(q0 < q1){ n = q1 - q0; if(n > RBUFSIZE) n = RBUFSIZE; bufread(t->file, q0, r, n); bufinsert(&snarfbuf, snarfbuf.nc, r, n); q0 += n; } fbuffree(r); putsnarf(); } if(docut){ textdelete(t, t->q0, t->q1, TRUE); textsetselect(t, t->q0, t->q0); if(t->w){ textscrdraw(t); winsettag(t->w); } }else if(dosnarf) /* Snarf command */ argtext = t; if(locked) winunlock(t->w); } void paste(Text *et, Text *t, Text*, int selectall, int tobody, Rune*, int) { int c; uint q, q0, q1, n; Rune *r; /* if(tobody), use body of executing window (Paste or Send command) */ if(tobody && et!=nil && et->w!=nil){ t = &et->w->body; filemark(t->file); /* seq has been incremented by execute */ } if(t == nil) return; getsnarf(); if(t==nil || snarfbuf.nc==0) return; if(t->w!=nil && et->w!=t->w){ c = 'M'; if(et->w) c = et->w->owner; winlock(t->w, c); } cut(t, t, nil, FALSE, TRUE, nil, 0); q = 0; q0 = t->q0; q1 = t->q0+snarfbuf.nc; r = fbufalloc(); while(q0 < q1){ n = q1 - q0; if(n > RBUFSIZE) n = RBUFSIZE; if(r == nil) r = runemalloc(n); bufread(&snarfbuf, q, r, n); textinsert(t, q0, r, n, TRUE); q += n; q0 += n; } fbuffree(r); if(selectall) textsetselect(t, t->q0, q1); else textsetselect(t, q1, q1); if(t->w){ textscrdraw(t); winsettag(t->w); } if(t->w!=nil && et->w!=t->w) winunlock(t->w); } void look(Text *et, Text *t, Text *argt, int, int, Rune *arg, int narg) { Rune *r; int n; if(et && et->w){ t = &et->w->body; if(narg > 0){ search(t, arg, narg); return; } getarg(argt, FALSE, FALSE, &r, &n); if(r == nil){ n = t->q1-t->q0; r = runemalloc(n); bufread(t->file, t->q0, r, n); } search(t, r, n); free(r); } } void sendx(Text *et, Text *t, Text*, int, int, Rune*, int) { if(et->w==nil) return; t = &et->w->body; if(t->q0 != t->q1) cut(t, t, nil, TRUE, FALSE, nil, 0); textsetselect(t, t->file->nc, t->file->nc); paste(t, t, nil, TRUE, TRUE, nil, 0); if(textreadc(t, t->file->nc-1) != '\n'){ textinsert(t, t->file->nc, L"\n", 1, TRUE); textsetselect(t, t->file->nc, t->file->nc); } } void edit(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) { Rune *r; int len; if(et == nil) return; getarg(argt, FALSE, TRUE, &r, &len); seq++; if(r != nil){ editcmd(et, r, len); free(r); }else editcmd(et, arg, narg); } void exit(Text*, Text*, Text*, int, int, Rune*, int) { if(rowclean(&row)){ sendul(cexit, 0); threadexits(nil); } } void putall(Text*, Text*, Text*, int, int, Rune*, int) { int i, j, e; Window *w; Column *c; char *a; for(i=0; inw; j++){ w = c->w[j]; if(w->isscratch || w->isdir || w->body.file->nname==0) continue; if(w->nopen[QWevent] > 0) continue; a = runetobyte(w->body.file->name, w->body.file->nname); e = access(a, 0); if(w->body.file->mod || w->body.ncache) if(e < 0) warning(nil, "no auto-Put of %s: %r\n", a); else{ wincommit(w, &w->body); put(&w->body, nil, nil, XXX, XXX, nil, 0); } free(a); } } } void id(Text *et, Text*, Text*, int, int, Rune*, int) { if(et && et->w) warning(nil, "/mnt/acme/%d/\n", et->w->id); } void local(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) { char *a, *aa; Runestr dir; aa = getbytearg(argt, TRUE, TRUE, &a); dir = dirname(et, nil, 0); if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ free(dir.r); dir.r = nil; dir.nr = 0; } run(nil, runetobyte(arg, narg), dir.r, dir.nr, FALSE, aa, a, FALSE); } void kill(Text*, Text*, Text *argt, int, int, Rune *arg, int narg) { Rune *a, *cmd, *r; int na; getarg(argt, FALSE, FALSE, &r, &na); if(r) kill(nil, nil, nil, 0, 0, r, na); /* loop condition: *arg is not a blank */ for(;;){ a = findbl(arg, narg, &na); if(a == arg) break; cmd = runemalloc(narg-na+1); runemove(cmd, arg, narg-na); sendp(ckill, cmd); arg = skipbl(a, na, &narg); } } void fontx(Text *et, Text *t, Text *argt, int, int, Rune *arg, int narg) { Rune *a, *r, *flag, *file; int na, nf; char *aa; Reffont *newfont; Dirlist *dp; int i, fix; if(et==nil || et->w==nil) return; t = &et->w->body; flag = nil; file = nil; /* loop condition: *arg is not a blank */ nf = 0; for(;;){ a = findbl(arg, narg, &na); if(a == arg) break; r = runemalloc(narg-na+1); runemove(r, arg, narg-na); if(runeeq(r, narg-na, L"fix", 3) || runeeq(r, narg-na, L"var", 3)){ free(flag); flag = r; }else{ free(file); file = r; nf = narg-na; } arg = skipbl(a, na, &narg); } getarg(argt, FALSE, TRUE, &r, &na); if(r) if(runeeq(r, na, L"fix", 3) || runeeq(r, na, L"var", 3)){ free(flag); flag = r; }else{ free(file); file = r; nf = na; } fix = 1; if(flag) fix = runeeq(flag, runestrlen(flag), L"fix", 3); else if(file == nil){ newfont = rfget(FALSE, FALSE, FALSE, nil); if(newfont) fix = strcmp(newfont->f->name, t->font->name)==0; } if(file){ aa = runetobyte(file, nf); newfont = rfget(fix, flag!=nil, FALSE, aa); free(aa); }else newfont = rfget(fix, FALSE, FALSE, nil); if(newfont){ draw(screen, t->w->r, textcols[BACK], nil, ZP); rfclose(t->reffont); t->reffont = newfont; t->font = newfont->f; frinittick(t); if(t->w->isdir){ t->all.min.x++; /* force recolumnation; disgusting! */ for(i=0; iw->ndl; i++){ dp = t->w->dlp[i]; aa = runetobyte(dp->r, dp->nr); dp->wid = stringwidth(newfont->f, aa); free(aa); } } /* avoid shrinking of window due to quantization */ colgrow(t->w->col, t->w, -1); } free(file); free(flag); } void incl(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) { Rune *a, *r; Window *w; int na, n, len; if(et==nil || et->w==nil) return; w = et->w; n = 0; getarg(argt, FALSE, TRUE, &r, &len); if(r){ n++; winaddincl(w, r, len); } /* loop condition: *arg is not a blank */ for(;;){ a = findbl(arg, narg, &na); if(a == arg) break; r = runemalloc(narg-na+1); runemove(r, arg, narg-na); n++; winaddincl(w, r, narg-na); arg = skipbl(a, na, &narg); } if(n==0 && w->nincl){ for(n=w->nincl; --n>=0; ) warning(nil, "%S ", w->incl[n]); warning(nil, "\n"); } } static int indentval(Rune *s, int n){ if(n < 2) return -1; if(runestrncmp(s, L"ON", n) == 0){ globalautoindent = TRUE; warning(nil, "Indent ON\n"); return -2; } if(runestrncmp(s, L"OFF", n) == 0){ globalautoindent = FALSE; warning(nil, "Indent OFF\n"); return -2; } return runestrncmp(s, L"on", n) == 0; } void indent(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) { Rune *a, *r; Window *w; int na, len, autoindent; char *result; if(et==nil || et->w==nil) return; w = et->w; autoindent = -1; getarg(argt, FALSE, TRUE, &r, &len); if(r!=nil && len>0) autoindent = indentval(r, len); else{ a = findbl(arg, narg, &na); if(a != arg) autoindent = indentval(arg, narg-na); } if(autoindent >= 0) w->autoindent = autoindent; if(autoindent != -2){ result = "off"; if(w->autoindent) result = "on"; if(w->body.file->nname) warning(nil, "%.*S: Indent %s\n", w->body.file->nname, w->body.file->name, result); else warning(nil, "Indent %s\n", result); } } void tab(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) { Rune *a, *r; Window *w; int na, len, tab; char *p; if(et==nil || et->w==nil) return; w = et->w; getarg(argt, FALSE, TRUE, &r, &len); tab = 0; if(r!=nil && len>0){ p = runetobyte(r, len); if('0'<=p[0] && p[0]<='9') tab = atoi(p); free(p); }else{ a = findbl(arg, narg, &na); if(a != arg){ p = runetobyte(arg, narg-na); if('0'<=p[0] && p[0]<='9') tab = atoi(p); free(p); } } if(tab > 0){ if(w->body.tabstop != tab){ w->body.tabstop = tab; winresize(w, w->r, 1); } }else warning(nil, "%.*S: Tab %d\n", w->body.file->nname, w->body.file->name, w->body.tabstop); } void runproc(void *argvp) { /* args: */ Window *win; char *s; Rune *rdir; int ndir; int newns; char *argaddr; char *arg; Command *c; Channel *cpid; int iseditcmd; /* end of args */ char *e, *t, *name, *filename, *dir, **av, *news; Rune r, **incl; int ac, w, inarg, i, n, fd, nincl, winid; int pipechar; char buf[512]; static void *parg[2]; void **argv; argv = argvp; win = argv[0]; s = argv[1]; rdir = argv[2]; ndir = (int)argv[3]; newns = (int)argv[4]; argaddr = argv[5]; arg = argv[6]; c = argv[7]; cpid = argv[8]; iseditcmd = (int)argv[9]; free(argv); t = s; while(*t==' ' || *t=='\n' || *t=='\t') t++; for(e=t; *e; e++) if(*e==' ' || *e=='\n' || *e=='\t' ) break; name = emalloc((e-t)+2); memmove(name, t, e-t); name[e-t] = 0; e = utfrrune(name, '/'); if(e) strcpy(name, e+1); strcat(name, " "); /* add blank here for ease in waittask */ c->name = bytetorune(name, &c->nname); free(name); pipechar = 0; if(*t=='<' || *t=='|' || *t=='>') pipechar = *t++; c->iseditcmd = iseditcmd; c->text = s; if(rdir != nil){ dir = runetobyte(rdir, ndir); chdir(dir); /* ignore error: probably app. window */ free(dir); } if(newns){ nincl = 0; incl = nil; if(win){ filename = smprint("%.*S", win->body.file->nname, win->body.file->name); nincl = win->nincl; if(nincl > 0){ incl = emalloc(nincl*sizeof(Rune*)); for(i=0; iincl[i]); incl[i] = runemalloc(n+1); runemove(incl[i], win->incl[i], n); } } winid = win->id; }else{ filename = nil; winid = 0; if(activewin) winid = activewin->id; } rfork(RFNAMEG|RFENVG|RFFDG|RFNOTEG); sprint(buf, "%d", winid); putenv("winid", buf); if(filename){ putenv("%", filename); free(filename); } c->md = fsysmount(rdir, ndir, incl, nincl); if(c->md == nil){ fprint(2, "child: can't mount /dev/cons: %r\n"); threadexits("mount"); } close(0); if(winid>0 && (pipechar=='|' || pipechar=='>')){ sprint(buf, "/mnt/acme/%d/rdsel", winid); open(buf, OREAD); }else open("/dev/null", OREAD); close(1); if((winid>0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){ if(iseditcmd){ if(winid > 0) sprint(buf, "/mnt/acme/%d/editout", winid); else sprint(buf, "/mnt/acme/editout"); }else sprint(buf, "/mnt/acme/%d/wrsel", winid); open(buf, OWRITE); close(2); open("/dev/cons", OWRITE); }else{ open("/dev/cons", OWRITE); dup(1, 2); } }else{ rfork(RFFDG|RFNOTEG); fsysclose(); close(0); open("/dev/null", OREAD); close(1); open(acmeerrorfile, OWRITE); dup(1, 2); } if(win) winclose(win); if(argaddr) putenv("acmeaddr", argaddr); if(strlen(t) > sizeof buf-10) /* may need to print into stack */ goto Hard; inarg = FALSE; for(e=t; *e; e+=w){ w = chartorune(&r, e); if(r==' ' || r=='\t') continue; if(r < ' ') goto Hard; if(utfrune("#;&|^$=`'{}()<>[]*?^~`", r)) goto Hard; inarg = TRUE; } if(!inarg) goto Fail; ac = 0; av = nil; inarg = FALSE; for(e=t; *e; e+=w){ w = chartorune(&r, e); if(r==' ' || r=='\t'){ inarg = FALSE; *e = 0; continue; } if(!inarg){ inarg = TRUE; av = realloc(av, (ac+1)*sizeof(char**)); av[ac++] = e; } } av = realloc(av, (ac+2)*sizeof(char**)); av[ac++] = arg; av[ac] = nil; c->av = av; procexec(cpid, av[0], av); e = av[0]; if(e[0]=='/' || (e[0]=='.' && e[1]=='/')) goto Fail; if(cputype){ sprint(buf, "%s/%s", cputype, av[0]); procexec(cpid, buf, av); } sprint(buf, "/bin/%s", av[0]); procexec(cpid, buf, av); goto Fail; Hard: /* * ugly: set path = (. $cputype /bin) * should honor $path if unusual. */ if(cputype){ n = 0; memmove(buf+n, ".", 2); n += 2; i = strlen(cputype)+1; memmove(buf+n, cputype, i); n += i; memmove(buf+n, "/bin", 5); n += 5; fd = create("/env/path", OWRITE, 0666); write(fd, buf, n); close(fd); } if(arg){ news = emalloc(strlen(t) + 1 + 1 + strlen(arg) + 1 + 1); if(news){ sprint(news, "%s '%s'", t, arg); /* BUG: what if quote in arg? */ free(s); t = news; c->text = news; } } procexecl(cpid, "/bin/rc", "rc", "-c", t, nil); Fail: /* procexec hasn't happened, so send a zero */ sendul(cpid, 0); threadexits(nil); } void runwaittask(void *v) { Command *c; Channel *cpid; void **a; threadsetname("runwaittask"); a = v; c = a[0]; cpid = a[1]; free(a); do c->pid = recvul(cpid); while(c->pid == ~0); free(c->av); if(c->pid != 0) /* successful exec */ sendp(ccommand, c); else{ if(c->iseditcmd) sendul(cedit, 0); free(c->name); free(c->text); free(c); } chanfree(cpid); } void run(Window *win, char *s, Rune *rdir, int ndir, int newns, char *argaddr, char *xarg, int iseditcmd) { void **arg; Command *c; Channel *cpid; if(s == nil) return; arg = emalloc(10*sizeof(void*)); c = emalloc(sizeof *c); cpid = chancreate(sizeof(ulong), 0); arg[0] = win; arg[1] = s; arg[2] = rdir; arg[3] = (void*)ndir; arg[4] = (void*)newns; arg[5] = argaddr; arg[6] = xarg; arg[7] = c; arg[8] = cpid; arg[9] = (void*)iseditcmd; proccreate(runproc, arg, STACK); /* mustn't block here because must be ready to answer mount() call in run() */ arg = emalloc(2*sizeof(void*)); arg[0] = c; arg[1] = cpid; threadcreate(runwaittask, arg, STACK); } #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" /* * Structure of Undo list: * The Undo structure follows any associated data, so the list * can be read backwards: read the structure, then read whatever * data is associated (insert string, file name) and precedes it. * The structure includes the previous value of the modify bit * and a sequence number; successive Undo structures with the * same sequence number represent simultaneous changes. */ typedef struct Undo Undo; struct Undo { short type; /* Delete, Insert, Filename */ short mod; /* modify bit */ uint seq; /* sequence number */ uint p0; /* location of change (unused in f) */ uint n; /* # runes in string or file name */ }; enum { Undosize = sizeof(Undo)/sizeof(Rune), }; File* fileaddtext(File *f, Text *t) { if(f == nil){ f = emalloc(sizeof(File)); f->unread = TRUE; } f->text = realloc(f->text, (f->ntext+1)*sizeof(Text*)); f->text[f->ntext++] = t; f->curtext = t; return f; } void filedeltext(File *f, Text *t) { int i; for(i=0; intext; i++) if(f->text[i] == t) goto Found; error("can't find text in filedeltext"); Found: f->ntext--; if(f->ntext == 0){ fileclose(f); return; } memmove(f->text+i, f->text+i+1, (f->ntext-i)*sizeof(Text*)); if(f->curtext == t) f->curtext = f->text[0]; } void fileinsert(File *f, uint p0, Rune *s, uint ns) { if(p0 > f->nc) error("internal error: fileinsert"); if(f->seq > 0) fileuninsert(f, &f->delta, p0, ns); bufinsert(f, p0, s, ns); if(ns) f->mod = TRUE; } void fileuninsert(File *f, Buffer *delta, uint p0, uint ns) { Undo u; /* undo an insertion by deleting */ u.type = Delete; u.mod = f->mod; u.seq = f->seq; u.p0 = p0; u.n = ns; bufinsert(delta, delta->nc, (Rune*)&u, Undosize); } void filedelete(File *f, uint p0, uint p1) { if(!(p0<=p1 && p0<=f->nc && p1<=f->nc)) error("internal error: filedelete"); if(f->seq > 0) fileundelete(f, &f->delta, p0, p1); bufdelete(f, p0, p1); if(p1 > p0) f->mod = TRUE; } void fileundelete(File *f, Buffer *delta, uint p0, uint p1) { Undo u; Rune *buf; uint i, n; /* undo a deletion by inserting */ u.type = Insert; u.mod = f->mod; u.seq = f->seq; u.p0 = p0; u.n = p1-p0; buf = fbufalloc(); for(i=p0; i RBUFSIZE) n = RBUFSIZE; bufread(f, i, buf, n); bufinsert(delta, delta->nc, buf, n); } fbuffree(buf); bufinsert(delta, delta->nc, (Rune*)&u, Undosize); } void filesetname(File *f, Rune *name, int n) { if(f->seq > 0) fileunsetname(f, &f->delta); free(f->name); f->name = runemalloc(n); runemove(f->name, name, n); f->nname = n; f->unread = TRUE; } void fileunsetname(File *f, Buffer *delta) { Undo u; /* undo a file name change by restoring old name */ u.type = Filename; u.mod = f->mod; u.seq = f->seq; u.p0 = 0; /* unused */ u.n = f->nname; if(f->nname) bufinsert(delta, delta->nc, f->name, f->nname); bufinsert(delta, delta->nc, (Rune*)&u, Undosize); } uint fileload(File *f, uint p0, int fd, int *nulls) { if(f->seq > 0) error("undo in file.load unimplemented"); return bufload(f, p0, fd, nulls); } /* return sequence number of pending redo */ uint fileredoseq(File *f) { Undo u; Buffer *delta; delta = &f->epsilon; if(delta->nc == 0) return 0; bufread(delta, delta->nc-Undosize, (Rune*)&u, Undosize); return u.seq; } void fileundo(File *f, int isundo, uint *q0p, uint *q1p) { Undo u; Rune *buf; uint i, j, n, up; uint stop; Buffer *delta, *epsilon; if(isundo){ /* undo; reverse delta onto epsilon, seq decreases */ delta = &f->delta; epsilon = &f->epsilon; stop = f->seq; }else{ /* redo; reverse epsilon onto delta, seq increases */ delta = &f->epsilon; epsilon = &f->delta; stop = 0; /* don't know yet */ } buf = fbufalloc(); while(delta->nc > 0){ up = delta->nc-Undosize; bufread(delta, up, (Rune*)&u, Undosize); if(isundo){ if(u.seq < stop){ f->seq = u.seq; goto Return; } }else{ if(stop == 0) stop = u.seq; if(u.seq > stop) goto Return; } switch(u.type){ default: fprint(2, "undo: 0x%ux\n", u.type); abort(); break; case Delete: f->seq = u.seq; fileundelete(f, epsilon, u.p0, u.p0+u.n); f->mod = u.mod; bufdelete(f, u.p0, u.p0+u.n); for(j=0; jntext; j++) textdelete(f->text[j], u.p0, u.p0+u.n, FALSE); *q0p = u.p0; *q1p = u.p0; break; case Insert: f->seq = u.seq; fileuninsert(f, epsilon, u.p0, u.n); f->mod = u.mod; up -= u.n; for(i=0; i RBUFSIZE) n = RBUFSIZE; bufread(delta, up+i, buf, n); bufinsert(f, u.p0+i, buf, n); for(j=0; jntext; j++) textinsert(f->text[j], u.p0+i, buf, n, FALSE); } *q0p = u.p0; *q1p = u.p0+u.n; break; case Filename: f->seq = u.seq; fileunsetname(f, epsilon); f->mod = u.mod; up -= u.n; free(f->name); if(u.n == 0) f->name = nil; else f->name = runemalloc(u.n); bufread(delta, up, f->name, u.n); f->nname = u.n; break; } bufdelete(delta, up, delta->nc); } if(isundo) f->seq = 0; Return: fbuffree(buf); } void filereset(File *f) { bufreset(&f->delta); bufreset(&f->epsilon); f->seq = 0; } void fileclose(File *f) { free(f->name); f->nname = 0; f->name = nil; free(f->text); f->ntext = 0; f->text = nil; bufclose(f); bufclose(&f->delta); bufclose(&f->epsilon); elogclose(f); free(f); } void filemark(File *f) { if(f->epsilon.nc) bufdelete(&f->epsilon, 0, f->epsilon.nc); f->seq = seq; } #pragma varargck argpos warning 2 void warning(Mntdir*, char*, ...); #define fbufalloc() emalloc(BUFSIZE) #define fbuffree(x) free(x) void plumblook(Plumbmsg*m); void plumbshow(Plumbmsg*m); void putsnarf(void); void getsnarf(void); int tempfile(void); void scrlresize(void); Font* getfont(int, int, char*); char* getarg(Text*, int, int, Rune**, int*); char* getbytearg(Text*, int, int, char**); void new(Text*, Text*, Text*, int, int, Rune*, int); void undo(Text*, Text*, Text*, int, int, Rune*, int); void scrsleep(uint); void savemouse(Window*); void restoremouse(Window*); void clearmouse(void); void allwindows(void(*)(Window*, void*), void*); uint loadfile(int, uint, int*, int(*)(void*, uint, Rune*, int), void*); Window* errorwin(Mntdir*, int); Runestr cleanrname(Runestr); void run(Window*, char*, Rune*, int, int, char*, char*, int); void fsysclose(void); void setcurtext(Text*, int); int isfilec(Rune); void rxinit(void); int rxnull(void); Runestr dirname(Text*, Rune*, int); void error(char*); void cvttorunes(char*, int, Rune*, int*, int*, int*); void* tmalloc(uint); void tfree(void); void killprocs(void); void killtasks(void); int runeeq(Rune*, uint, Rune*, uint); int ALEF_tid(void); void iconinit(void); Timer* timerstart(int); void timerstop(Timer*); void timercancel(Timer*); void timerinit(void); void cut(Text*, Text*, Text*, int, int, Rune*, int); void paste(Text*, Text*, Text*, int, int, Rune*, int); void get(Text*, Text*, Text*, int, int, Rune*, int); void put(Text*, Text*, Text*, int, int, Rune*, int); void putfile(File*, int, int, Rune*, int); void fontx(Text*, Text*, Text*, int, int, Rune*, int); int isalnum(Rune); void execute(Text*, uint, uint, int, Text*); int search(Text*, Rune*, uint); void look3(Text*, uint, uint, int); void editcmd(Text*, Rune*, uint); uint min(uint, uint); uint max(uint, uint); Window* lookfile(Rune*, int); Window* lookid(int, int); char* runetobyte(Rune*, int); Rune* bytetorune(char*, int*); void fsysinit(void); Mntdir* fsysmount(Rune*, int, Rune**, int); void fsysincid(Mntdir*); void fsysdelid(Mntdir*); Xfid* respond(Xfid*, Fcall*, char*); int rxcompile(Rune*); int rgetc(void*, uint); int tgetc(void*, uint); int isaddrc(int); int isregexc(int); void *emalloc(uint); void *erealloc(void*, uint); char *estrdup(char*); Range address(Mntdir*, Text*, Range, Range, void*, uint, uint, int (*)(void*, uint), int*, uint*); int rxexecute(Text*, Rune*, uint, uint, Rangeset*); int rxbexecute(Text*, uint, Rangeset*); Window* makenewwindow(Text *t); int expand(Text*, uint, uint, Expand*); Rune* skipbl(Rune*, int, int*); Rune* findbl(Rune*, int, int*); char* edittext(Window*, int, Rune*, int); void flushwarnings(void); #define runemalloc(a) (Rune*)emalloc((a)*sizeof(Rune)) #define runerealloc(a, b) (Rune*)erealloc((a), (b)*sizeof(Rune)) #define runemove(a, b, c) memmove((a), (b), (c)*sizeof(Rune)) #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" static int cfd; static int sfd; enum { Nhash = 16, DEBUG = 0 }; static Fid *fids[Nhash]; Fid *newfid(int); static Xfid* fsysflush(Xfid*, Fid*); static Xfid* fsysauth(Xfid*, Fid*); static Xfid* fsysversion(Xfid*, Fid*); static Xfid* fsysattach(Xfid*, Fid*); static Xfid* fsyswalk(Xfid*, Fid*); static Xfid* fsysopen(Xfid*, Fid*); static Xfid* fsyscreate(Xfid*, Fid*); static Xfid* fsysread(Xfid*, Fid*); static Xfid* fsyswrite(Xfid*, Fid*); static Xfid* fsysclunk(Xfid*, Fid*); static Xfid* fsysremove(Xfid*, Fid*); static Xfid* fsysstat(Xfid*, Fid*); static Xfid* fsyswstat(Xfid*, Fid*); Xfid* (*fcall[Tmax])(Xfid*, Fid*) = { [Tflush] = fsysflush, [Tversion] = fsysversion, [Tauth] = fsysauth, [Tattach] = fsysattach, [Twalk] = fsyswalk, [Topen] = fsysopen, [Tcreate] = fsyscreate, [Tread] = fsysread, [Twrite] = fsyswrite, [Tclunk] = fsysclunk, [Tremove]= fsysremove, [Tstat] = fsysstat, [Twstat] = fsyswstat, }; char Eperm[] = "permission denied"; char Eexist[] = "file does not exist"; char Enotdir[] = "not a directory"; Dirtab dirtab[]= { { ".", QTDIR, Qdir, 0500|DMDIR }, { "acme", QTDIR, Qacme, 0500|DMDIR }, { "cons", QTFILE, Qcons, 0600 }, { "consctl", QTFILE, Qconsctl, 0000 }, { "draw", QTDIR, Qdraw, 0000|DMDIR }, /* to suppress graphics progs started in acme */ { "editout", QTFILE, Qeditout, 0200 }, { "index", QTFILE, Qindex, 0400 }, { "label", QTFILE, Qlabel, 0600 }, { "new", QTDIR, Qnew, 0500|DMDIR }, { nil, } }; Dirtab dirtabw[]= { { ".", QTDIR, Qdir, 0500|DMDIR }, { "addr", QTFILE, QWaddr, 0600 }, { "body", QTAPPEND, QWbody, 0600|DMAPPEND }, { "ctl", QTFILE, QWctl, 0600 }, { "data", QTFILE, QWdata, 0600 }, { "editout", QTFILE, QWeditout, 0200 }, { "event", QTFILE, QWevent, 0600 }, { "rdsel", QTFILE, QWrdsel, 0400 }, { "wrsel", QTFILE, QWwrsel, 0200 }, { "tag", QTAPPEND, QWtag, 0600|DMAPPEND }, { nil, } }; typedef struct Mnt Mnt; struct Mnt { QLock; int id; Mntdir *md; }; Mnt mnt; Xfid* respond(Xfid*, Fcall*, char*); int dostat(int, Dirtab*, uchar*, int, uint); uint getclock(void); char *user = "Wile E. Coyote"; int clockfd; static int closing = 0; int messagesize = Maxblock+IOHDRSZ; /* good start */ void fsysproc(void *); void fsysinit(void) { int p[2]; int n, fd; char buf[256]; if(pipe(p) < 0) error("can't create pipe"); cfd = p[0]; sfd = p[1]; fmtinstall('F', fcallfmt); clockfd = open("/dev/time", OREAD|OCEXEC); fd = open("/dev/user", OREAD); if(fd >= 0){ n = read(fd, buf, sizeof buf-1); if(n > 0){ buf[n] = 0; user = estrdup(buf); } close(fd); } proccreate(fsysproc, nil, STACK); } void fsysproc(void *) { int n; Xfid *x; Fid *f; Fcall t; uchar *buf; x = nil; for(;;){ buf = emalloc(messagesize+UTFmax); /* overflow for appending partial rune in xfidwrite */ n = read9pmsg(sfd, buf, messagesize); if(n <= 0){ if(closing) break; error("i/o error on server channel"); } if(x == nil){ sendp(cxfidalloc, nil); x = recvp(cxfidalloc); } x->buf = buf; if(convM2S(buf, n, x) != n) error("convert error in convM2S"); if(DEBUG) fprint(2, "%F\n", &x->Fcall); if(fcall[x->type] == nil) x = respond(x, &t, "bad fcall type"); else{ if(x->type==Tversion || x->type==Tauth) f = nil; else f = newfid(x->fid); x->f = f; x = (*fcall[x->type])(x, f); } } } Mntdir* fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl) { Mntdir *m; int id; qlock(&mnt); id = ++mnt.id; m = emalloc(sizeof *m); m->id = id; m->dir = dir; m->ref = 1; /* one for Command, one will be incremented in attach */ m->ndir = ndir; m->next = mnt.md; m->incl = incl; m->nincl = nincl; mnt.md = m; qunlock(&mnt); return m; } void fsysincid(Mntdir *m) { qlock(&mnt); m->ref++; qunlock(&mnt); } void fsysdelid(Mntdir *idm) { Mntdir *m, *prev; int i; char buf[64]; if(idm == nil) return; qlock(&mnt); if(--idm->ref > 0){ qunlock(&mnt); return; } prev = nil; for(m=mnt.md; m; m=m->next){ if(m == idm){ if(prev) prev->next = m->next; else mnt.md = m->next; for(i=0; inincl; i++) free(m->incl[i]); free(m->incl); free(m->dir); free(m); qunlock(&mnt); return; } prev = m; } qunlock(&mnt); sprint(buf, "fsysdelid: can't find id %d\n", idm->id); sendp(cerr, estrdup(buf)); } /* * Called only in exec.c:/^run(), from a different FD group */ Mntdir* fsysmount(Rune *dir, int ndir, Rune **incl, int nincl) { char buf[256]; Mntdir *m; /* close server side so don't hang if acme is half-exited */ close(sfd); m = fsysaddid(dir, ndir, incl, nincl); sprint(buf, "%d", m->id); if(mount(cfd, -1, "/mnt/acme", MREPL, buf) < 0){ fsysdelid(m); return nil; } close(cfd); bind("/mnt/acme", "/mnt/wsys", MREPL); if(bind("/mnt/acme", "/dev", MBEFORE) < 0){ fsysdelid(m); return nil; } return m; } void fsysclose(void) { closing = 1; close(cfd); close(sfd); } Xfid* respond(Xfid *x, Fcall *t, char *err) { int n; if(err){ t->type = Rerror; t->ename = err; }else t->type = x->type+1; t->fid = x->fid; t->tag = x->tag; if(x->buf == nil) x->buf = emalloc(messagesize); n = convS2M(t, x->buf, messagesize); if(n <= 0) error("convert error in convS2M"); if(write(sfd, x->buf, n) != n) error("write error in respond"); free(x->buf); x->buf = nil; if(DEBUG) fprint(2, "r: %F\n", t); return x; } static Xfid* fsysversion(Xfid *x, Fid*) { Fcall t; if(x->msize < 256) return respond(x, &t, "version: message size too small"); messagesize = x->msize; t.msize = messagesize; if(strncmp(x->version, "9P2000", 6) != 0) return respond(x, &t, "unrecognized 9P version"); t.version = "9P2000"; return respond(x, &t, nil); } static Xfid* fsysauth(Xfid *x, Fid*) { return respond(x, nil, "acme: authentication not required"); } static Xfid* fsysflush(Xfid *x, Fid*) { sendp(x->c, xfidflush); return nil; } static Xfid* fsysattach(Xfid *x, Fid *f) { Fcall t; int id; Mntdir *m; if(strcmp(x->uname, user) != 0) return respond(x, &t, Eperm); f->busy = TRUE; f->open = FALSE; f->qid.path = Qdir; f->qid.type = QTDIR; f->qid.vers = 0; f->dir = dirtab; f->nrpart = 0; f->w = nil; t.qid = f->qid; f->mntdir = nil; id = atoi(x->aname); qlock(&mnt); for(m=mnt.md; m; m=m->next) if(m->id == id){ f->mntdir = m; m->ref++; break; } if(m == nil) sendp(cerr, estrdup("unknown id in attach")); qunlock(&mnt); return respond(x, &t, nil); } static Xfid* fsyswalk(Xfid *x, Fid *f) { Fcall t; int c, i, j, id; Qid q; uchar type; ulong path; Fid *nf; Dirtab *d, *dir; Window *w; char *err; nf = nil; w = nil; if(f->open) return respond(x, &t, "walk of open file"); if(x->fid != x->newfid){ nf = newfid(x->newfid); if(nf->busy) return respond(x, &t, "newfid already in use"); nf->busy = TRUE; nf->open = FALSE; nf->mntdir = f->mntdir; if(f->mntdir) f->mntdir->ref++; nf->dir = f->dir; nf->qid = f->qid; nf->w = f->w; nf->nrpart = 0; /* not open, so must be zero */ if(nf->w) incref(nf->w); f = nf; /* walk f */ } t.nwqid = 0; err = nil; dir = nil; id = WIN(f->qid); q = f->qid; if(x->nwname > 0){ for(i=0; inwname; i++){ if((q.type & QTDIR) == 0){ err = Enotdir; break; } if(strcmp(x->wname[i], "..") == 0){ type = QTDIR; path = Qdir; id = 0; if(w){ winclose(w); w = nil; } Accept: if(i == MAXWELEM){ err = "name too long"; break; } q.type = type; q.vers = 0; q.path = QID(id, path); t.wqid[t.nwqid++] = q; continue; } /* is it a numeric name? */ for(j=0; (c=x->wname[i][j]); j++) if(c<'0' || '9'wname[i]); qlock(&row); w = lookid(id, FALSE); if(w == nil){ qunlock(&row); break; } incref(w); /* we'll drop reference at end if there's an error */ path = Qdir; type = QTDIR; qunlock(&row); dir = dirtabw; goto Accept; Regular: // if(FILE(f->qid) == Qacme) /* empty directory */ // break; if(strcmp(x->wname[i], "new") == 0){ if(w) error("w set in walk to new"); sendp(cnewwindow, nil); /* signal newwindowthread */ w = recvp(cnewwindow); /* receive new window */ incref(w); type = QTDIR; path = QID(w->id, Qdir); id = w->id; dir = dirtabw; goto Accept; } if(id == 0) d = dirtab; else d = dirtabw; d++; /* skip '.' */ for(; d->name; d++) if(strcmp(x->wname[i], d->name) == 0){ path = d->qid; type = d->type; dir = d; goto Accept; } break; /* file not found */ } if(i==0 && err == nil) err = Eexist; } if(err!=nil || t.nwqidnwname){ if(nf){ nf->busy = FALSE; fsysdelid(nf->mntdir); } }else if(t.nwqid == x->nwname){ if(w){ f->w = w; w = nil; /* don't drop the reference */ } if(dir) f->dir = dir; f->qid = q; } if(w != nil) winclose(w); return respond(x, &t, err); } static Xfid* fsysopen(Xfid *x, Fid *f) { Fcall t; int m; /* can't truncate anything, so just disregard */ x->mode &= ~(OTRUNC|OCEXEC); /* can't execute or remove anything */ if(x->mode==OEXEC || (x->mode&ORCLOSE)) goto Deny; switch(x->mode){ default: goto Deny; case OREAD: m = 0400; break; case OWRITE: m = 0200; break; case ORDWR: m = 0600; break; } if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m) goto Deny; sendp(x->c, xfidopen); return nil; Deny: return respond(x, &t, Eperm); } static Xfid* fsyscreate(Xfid *x, Fid*) { Fcall t; return respond(x, &t, Eperm); } static int idcmp(void *a, void *b) { return *(int*)a - *(int*)b; } static Xfid* fsysread(Xfid *x, Fid *f) { Fcall t; uchar *b; int i, id, n, o, e, j, k, *ids, nids; Dirtab *d, dt; Column *c; uint clock, len; char buf[16]; if(f->qid.type & QTDIR){ if(FILE(f->qid) == Qacme){ /* empty dir */ t.data = nil; t.count = 0; respond(x, &t, nil); return x; } o = x->offset; e = x->offset+x->count; clock = getclock(); b = emalloc(messagesize); id = WIN(f->qid); n = 0; if(id > 0) d = dirtabw; else d = dirtab; d++; /* first entry is '.' */ for(i=0; d->name!=nil && if->qid), d, b+n, x->count-n, clock); if(len <= BIT16SZ) break; if(i >= o) n += len; d++; } if(id == 0){ qlock(&row); nids = 0; ids = nil; for(j=0; jnw; k++){ ids = realloc(ids, (nids+1)*sizeof(int)); ids[nids++] = c->w[k]->id; } } qunlock(&row); qsort(ids, nids, sizeof ids[0], idcmp); j = 0; dt.name = buf; for(; jcount-n, clock); if(len == 0) break; if(i >= o) n += len; j++; } free(ids); } t.data = (char*)b; t.count = n; respond(x, &t, nil); free(b); return x; } sendp(x->c, xfidread); return nil; } static Xfid* fsyswrite(Xfid *x, Fid*) { sendp(x->c, xfidwrite); return nil; } static Xfid* fsysclunk(Xfid *x, Fid *f) { fsysdelid(f->mntdir); sendp(x->c, xfidclose); return nil; } static Xfid* fsysremove(Xfid *x, Fid*) { Fcall t; return respond(x, &t, Eperm); } static Xfid* fsysstat(Xfid *x, Fid *f) { Fcall t; t.stat = emalloc(messagesize-IOHDRSZ); t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock()); x = respond(x, &t, nil); free(t.stat); return x; } static Xfid* fsyswstat(Xfid *x, Fid*) { Fcall t; return respond(x, &t, Eperm); } Fid* newfid(int fid) { Fid *f, *ff, **fh; ff = nil; fh = &fids[fid&(Nhash-1)]; for(f=*fh; f; f=f->next) if(f->fid == fid) return f; else if(ff==nil && f->busy==FALSE) ff = f; if(ff){ ff->fid = fid; return ff; } f = emalloc(sizeof *f); f->fid = fid; f->next = *fh; *fh = f; return f; } uint getclock() { char buf[32]; buf[0] = '\0'; pread(clockfd, buf, sizeof buf, 0); return atoi(buf); } int dostat(int id, Dirtab *dir, uchar *buf, int nbuf, uint clock) { Dir d; d.qid.path = QID(id, dir->qid); d.qid.vers = 0; d.qid.type = dir->type; d.mode = dir->perm; d.length = 0; /* would be nice to do better */ d.name = dir->name; d.uid = user; d.gid = user; d.muid = user; d.atime = clock; d.mtime = clock; return convD2M(&d, buf, nbuf); } #include #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" Window* openfile(Text*, Expand*); int nuntitled; void look3(Text *t, uint q0, uint q1, int external) { int n, c, f, expanded; Text *ct; Expand e; Rune *r; uint p; Plumbmsg *m; Runestr dir; char buf[32]; ct = seltext; if(ct == nil) seltext = t; expanded = expand(t, q0, q1, &e); if(!external && t->w!=nil && t->w->nopen[QWevent]>0){ /* send alphanumeric expansion to external client */ if(expanded == FALSE) return; f = 0; if((e.at!=nil && t->w!=nil) || (e.nname>0 && lookfile(e.name, e.nname)!=nil)) f = 1; /* acme can do it without loading a file */ if(q0!=e.q0 || q1!=e.q1) f |= 2; /* second (post-expand) message follows */ if(e.nname) f |= 4; /* it's a file name */ c = 'l'; if(t->what == Body) c = 'L'; n = q1-q0; if(n <= EVENTSIZE){ r = runemalloc(n); bufread(t->file, q0, r, n); winevent(t->w, "%c%d %d %d %d %.*S\n", c, q0, q1, f, n, n, r); free(r); }else winevent(t->w, "%c%d %d %d 0 \n", c, q0, q1, f, n); if(q0==e.q0 && q1==e.q1) return; if(e.nname){ n = e.nname; if(e.a1 > e.a0) n += 1+(e.a1-e.a0); r = runemalloc(n); runemove(r, e.name, e.nname); if(e.a1 > e.a0){ r[e.nname] = ':'; bufread(e.at->file, e.a0, r+e.nname+1, e.a1-e.a0); } }else{ n = e.q1 - e.q0; r = runemalloc(n); bufread(t->file, e.q0, r, n); } f &= ~2; if(n <= EVENTSIZE) winevent(t->w, "%c%d %d %d %d %.*S\n", c, e.q0, e.q1, f, n, n, r); else winevent(t->w, "%c%d %d %d 0 \n", c, e.q0, e.q1, f, n); free(r); goto Return; } if(plumbsendfd >= 0){ /* send whitespace-delimited word to plumber */ m = emalloc(sizeof(Plumbmsg)); m->src = estrdup("acme"); m->dst = nil; dir = dirname(t, nil, 0); if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ free(dir.r); dir.r = nil; dir.nr = 0; } if(dir.nr == 0) m->wdir = estrdup(wdir); else m->wdir = runetobyte(dir.r, dir.nr); free(dir.r); m->type = estrdup("text"); m->attr = nil; buf[0] = '\0'; if(q1 == q0){ if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){ q0 = t->q0; q1 = t->q1; }else{ p = q0; while(q0>0 && (c=tgetc(t, q0-1))!=' ' && c!='\t' && c!='\n') q0--; while(q1file->nc && (c=tgetc(t, q1))!=' ' && c!='\t' && c!='\n') q1++; if(q1 == q0){ plumbfree(m); goto Return; } sprint(buf, "click=%d", p-q0); m->attr = plumbunpackattr(buf); } } r = runemalloc(q1-q0); bufread(t->file, q0, r, q1-q0); m->data = runetobyte(r, q1-q0); m->ndata = strlen(m->data); free(r); if(m->ndata= 0){ plumbfree(m); goto Return; } plumbfree(m); /* plumber failed to match; fall through */ } /* interpret alphanumeric string ourselves */ if(expanded == FALSE) return; if(e.name || e.at) openfile(t, &e); else{ if(t->w == nil) return; ct = &t->w->body; if(t->w != ct->w) winlock(ct->w, 'M'); if(t == ct) textsetselect(ct, e.q1, e.q1); n = e.q1 - e.q0; r = runemalloc(n); bufread(t->file, e.q0, r, n); if(search(ct, r, n) && e.jump) moveto(mousectl, addpt(frptofchar(ct, ct->p0), Pt(4, ct->font->height-4))); if(t->w != ct->w) winunlock(ct->w); free(r); } Return: free(e.name); free(e.bname); } int plumbgetc(void *a, uint n) { Rune *r; r = a; if(n<0 || n>runestrlen(r)) return 0; return r[n]; } void plumblook(Plumbmsg *m) { Expand e; char *addr; if(m->ndata >= BUFSIZE){ warning(nil, "insanely long file name (%d bytes) in plumb message (%.32s...)\n", m->ndata, m->data); return; } e.q0 = 0; e.q1 = 0; if(m->data[0] == '\0') return; e.ar = nil; e.bname = m->data; e.name = bytetorune(e.bname, &e.nname); e.jump = TRUE; e.a0 = 0; e.a1 = 0; addr = plumblookup(m->attr, "addr"); if(addr != nil){ e.ar = bytetorune(addr, &e.a1); e.agetc = plumbgetc; } openfile(nil, &e); free(e.name); free(e.at); } void plumbshow(Plumbmsg *m) { Window *w; Rune rb[256], *r; int nb, nr; Runestr rs; char *name, *p, namebuf[16]; w = makenewwindow(nil); name = plumblookup(m->attr, "filename"); if(name == nil){ name = namebuf; nuntitled++; snprint(namebuf, sizeof namebuf, "Untitled-%d", nuntitled); } p = nil; if(name[0]!='/' && m->wdir!=nil && m->wdir[0]!='\0'){ nb = strlen(m->wdir) + 1 + strlen(name) + 1; p = emalloc(nb); snprint(p, nb, "%s/%s", m->wdir, name); name = p; } cvttorunes(name, strlen(name), rb, &nb, &nr, nil); free(p); rs = cleanrname((Runestr){rb, nr}); winsetname(w, rs.r, rs.nr); r = runemalloc(m->ndata); cvttorunes(m->data, m->ndata, r, &nb, &nr, nil); textinsert(&w->body, 0, r, nr, TRUE); free(r); w->body.file->mod = FALSE; w->dirty = FALSE; winsettag(w); textscrdraw(&w->body); textsetselect(&w->tag, w->tag.file->nc, w->tag.file->nc); } int search(Text *ct, Rune *r, uint n) { uint q, nb, maxn; int around; Rune *s, *b, *c; if(n==0 || n>ct->file->nc) return FALSE; if(2*n > RBUFSIZE){ warning(nil, "string too long\n"); return FALSE; } maxn = max(2*n, RBUFSIZE); s = fbufalloc(); b = s; nb = 0; b[nb] = 0; around = 0; q = ct->q1; for(;;){ if(q >= ct->file->nc){ q = 0; around = 1; nb = 0; b[nb] = 0; } if(nb > 0){ c = runestrchr(b, r[0]); if(c == nil){ q += nb; nb = 0; b[nb] = 0; if(around && q>=ct->q1) break; continue; } q += (c-b); nb -= (c-b); b = c; } /* reload if buffer covers neither string nor rest of file */ if(nbfile->nc-q){ nb = ct->file->nc-q; if(nb >= maxn) nb = maxn-1; bufread(ct->file, q, s, nb); b = s; b[nb] = '\0'; } /* this runeeq is fishy but the null at b[nb] makes it safe */ if(runeeq(b, n, r, n)==TRUE){ if(ct->w){ textshow(ct, q, q+n, 1); winsettag(ct->w); }else{ ct->q0 = q; ct->q1 = q+n; } seltext = ct; fbuffree(s); return TRUE; } if(around && q>=ct->q1) break; --nb; b++; q++; } fbuffree(s); return FALSE; } int isfilec(Rune r) { if(isalnum(r)) return TRUE; if(runestrchr(L".-+/:", r)) return TRUE; return FALSE; } /* Runestr wrapper for cleanname */ Runestr cleanrname(Runestr rs) { char *s; int nb, nulls; s = runetobyte(rs.r, rs.nr); cleanname(s); cvttorunes(s, strlen(s), rs.r, &nb, &rs.nr, &nulls); free(s); return rs; } Runestr includefile(Rune *dir, Rune *file, int nfile) { int m, n; char *a; Rune *r; m = runestrlen(dir); a = emalloc((m+1+nfile)*UTFmax+1); sprint(a, "%S/%.*S", dir, nfile, file); n = access(a, 0); free(a); if(n < 0) return (Runestr){nil, 0}; r = runemalloc(m+1+nfile); runemove(r, dir, m); runemove(r+m, L"/", 1); runemove(r+m+1, file, nfile); free(file); return cleanrname((Runestr){r, m+1+nfile}); } static Rune *objdir; Runestr includename(Text *t, Rune *r, int n) { Window *w; char buf[128]; Runestr file; int i; if(objdir==nil && objtype!=nil){ sprint(buf, "/%s/include", objtype); objdir = bytetorune(buf, &i); objdir = runerealloc(objdir, i+1); objdir[i] = '\0'; } w = t->w; if(n==0 || r[0]=='/' || w==nil) goto Rescue; if(n>2 && r[0]=='.' && r[1]=='/') goto Rescue; file.r = nil; file.nr = 0; for(i=0; inincl && file.r==nil; i++) file = includefile(w->incl[i], r, n); if(file.r == nil) file = includefile(L"/sys/include", r, n); if(file.r==nil && objdir!=nil) file = includefile(objdir, r, n); if(file.r == nil) goto Rescue; return file; Rescue: return (Runestr){r, n}; } Runestr dirname(Text *t, Rune *r, int n) { Rune *b, c; uint m, nt; int slash; Runestr tmp; b = nil; if(t==nil || t->w==nil) goto Rescue; nt = t->w->tag.file->nc; if(nt == 0) goto Rescue; if(n>=1 && r[0]=='/') goto Rescue; b = runemalloc(nt+n+1); bufread(t->w->tag.file, 0, b, nt); slash = -1; for(m=0; mfile->nc && isfilec(c=textreadc(t, q1))){ if(c == ':'){ colon = q1; break; } q1++; } while(q0>0 && (isfilec(c=textreadc(t, q0-1)) || isaddrc(c) || isregexc(c))){ q0--; if(colon<0 && c==':') colon = q0; } /* * if it looks like it might begin file: , consume address chars after : * otherwise terminate expansion at : */ if(colon >= 0){ q1 = colon; if(colonfile->nc-1 && isaddrc(textreadc(t, colon+1))){ q1 = colon+1; while(q1file->nc-1 && isaddrc(textreadc(t, q1))) q1++; } } if(q1 > q0) if(colon >= 0){ /* stop at white space */ for(amax=colon+1; amaxfile->nc; amax++) if((c=textreadc(t, amax))==' ' || c=='\t' || c=='\n') break; }else amax = t->file->nc; } amin = amax; e->q0 = q0; e->q1 = q1; n = q1-q0; if(n == 0) return FALSE; /* see if it's a file name */ r = runemalloc(n); bufread(t->file, q0, r, n); /* first, does it have bad chars? */ nname = -1; for(i=0; ifile->nc && (i==n-1 || isaddrc(textreadc(t, q0+i+1)))) amin = q0+i; else goto Isntfile; nname = i; } } if(nname == -1) nname = n; for(i=0; i, and turn that into an include * file name if so. Should probably do it for "" too, but that's not * restrictive enough syntax and checking for a #include earlier on the * line would be silly. */ if(q0>0 && textreadc(t, q0-1)=='<' && q1file->nc && textreadc(t, q1)=='>'){ rs = includename(t, r, nname); r = rs.r; nname = rs.nr; } else if(amin == q0) goto Isfile; else{ rs = dirname(t, r, nname); r = rs.r; nname = rs.nr; } e->bname = runetobyte(r, nname); /* if it's already a window name, it's a file */ w = lookfile(r, nname); if(w != nil) goto Isfile; /* if it's the name of a file, it's a file */ if(access(e->bname, 0) < 0){ free(e->bname); e->bname = nil; goto Isntfile; } Isfile: e->name = r; e->nname = nname; e->at = t; e->a0 = amin+1; eval = FALSE; address(nil, nil, (Range){-1,-1}, (Range){0, 0}, t, e->a0, amax, tgetc, &eval, (uint*)&e->a1); return TRUE; Isntfile: free(r); return FALSE; } int expand(Text *t, uint q0, uint q1, Expand *e) { memset(e, 0, sizeof *e); e->agetc = tgetc; /* if in selection, choose selection */ e->jump = TRUE; if(q1==q0 && t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){ q0 = t->q0; q1 = t->q1; if(t->what == Tag) e->jump = FALSE; } if(expandfile(t, q0, q1, e)) return TRUE; if(q0 == q1){ while(q1file->nc && isalnum(textreadc(t, q1))) q1++; while(q0>0 && isalnum(textreadc(t, q0-1))) q0--; } e->q0 = q0; e->q1 = q1; return q1 > q0; } Window* lookfile(Rune *s, int n) { int i, j, k; Window *w; Column *c; Text *t; /* avoid terminal slash on directories */ if(n>1 && s[n-1] == '/') --n; for(j=0; jnw; i++){ w = c->w[i]; t = &w->body; k = t->file->nname; if(k>1 && t->file->name[k-1] == '/') k--; if(runeeq(t->file->name, k, s, n)){ w = w->body.file->curtext->w; if(w->col != nil) /* protect against race deleting w */ return w; } } } return nil; } Window* lookid(int id, int dump) { int i, j; Window *w; Column *c; for(j=0; jnw; i++){ w = c->w[i]; if(dump && w->dumpid == id) return w; if(!dump && w->id == id) return w; } } return nil; } Window* openfile(Text *t, Expand *e) { Range r; Window *w, *ow; int eval, i, n; Rune *rp; uint dummy; if(e->nname == 0){ w = t->w; if(w == nil) return nil; }else w = lookfile(e->name, e->nname); if(w){ t = &w->body; if(!t->col->safe && t->maxlines==0) /* window is obscured by full-column window */ colgrow(t->col, t->col->w[0], 1); }else{ ow = nil; if(t) ow = t->w; w = makenewwindow(t); t = &w->body; winsetname(w, e->name, e->nname); textload(t, 0, e->bname, 1); t->file->mod = FALSE; t->w->dirty = FALSE; winsettag(t->w); textsetselect(&t->w->tag, t->w->tag.file->nc, t->w->tag.file->nc); if(ow != nil){ for(i=ow->nincl; --i>=0; ){ n = runestrlen(ow->incl[i]); rp = runemalloc(n); runemove(rp, ow->incl[i], n); winaddincl(w, rp, n); } w->autoindent = ow->autoindent; }else w->autoindent = globalautoindent; } if(e->a1 == e->a0) eval = FALSE; else{ eval = TRUE; r = address(nil, t, (Range){-1, -1}, (Range){t->q0, t->q1}, e->at, e->a0, e->a1, e->agetc, &eval, &dummy); if(eval == FALSE) e->jump = FALSE; /* don't jump if invalid address */ } if(eval == FALSE){ r.q0 = t->q0; r.q1 = t->q1; } textshow(t, r.q0, r.q1, 1); winsettag(t->w); seltext = t; if(e->jump) moveto(mousectl, addpt(frptofchar(t, t->p0), Pt(4, font->height-4))); return w; } void new(Text *et, Text *t, Text *argt, int flag1, int flag2, Rune *arg, int narg) { int ndone; Rune *a, *f; int na, nf; Expand e; Runestr rs; getarg(argt, FALSE, TRUE, &a, &na); if(a){ new(et, t, nil, flag1, flag2, a, na); if(narg == 0) return; } /* loop condition: *arg is not a blank */ for(ndone=0; ; ndone++){ a = findbl(arg, narg, &na); if(a == arg){ if(ndone==0 && et->col!=nil) winsettag(coladd(et->col, nil, nil, -1)); break; } nf = narg-na; f = runemalloc(nf); runemove(f, arg, nf); rs = dirname(et, f, nf); f = rs.r; nf = rs.nr; memset(&e, 0, sizeof e); e.name = f; e.nname = nf; e.bname = runetobyte(f, nf); e.jump = TRUE; openfile(et, &e); free(f); free(e.bname); arg = skipbl(a, na, &narg); } } syms for(i in ????.c) 8c -aa $i >> syms #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" Rangeset sel; Rune *lastregexp; /* * Machine Information */ typedef struct Inst Inst; struct Inst { uint type; /* < 0x10000 ==> literal, otherwise action */ union { int sid; int subid; int class; Inst *other; Inst *right; }; union{ Inst *left; Inst *next; }; }; #define NPROG 1024 Inst program[NPROG]; Inst *progp; Inst *startinst; /* First inst. of program; might not be program[0] */ Inst *bstartinst; /* same for backwards machine */ Channel *rechan; /* chan(Inst*) */ typedef struct Ilist Ilist; struct Ilist { Inst *inst; /* Instruction of the thread */ Rangeset se; uint startp; /* first char of match */ }; #define NLIST 128 Ilist *tl, *nl; /* This list, next list */ Ilist list[2][NLIST]; static Rangeset sempty; /* * Actions and Tokens * * 0x100xx are operators, value == precedence * 0x200xx are tokens, i.e. operands for operators */ #define OPERATOR 0x10000 /* Bitmask of all operators */ #define START 0x10000 /* Start, used for marker on stack */ #define RBRA 0x10001 /* Right bracket, ) */ #define LBRA 0x10002 /* Left bracket, ( */ #define OR 0x10003 /* Alternation, | */ #define CAT 0x10004 /* Concatentation, implicit operator */ #define STAR 0x10005 /* Closure, * */ #define PLUS 0x10006 /* a+ == aa* */ #define QUEST 0x10007 /* a? == a|nothing, i.e. 0 or 1 a's */ #define ANY 0x20000 /* Any character but newline, . */ #define NOP 0x20001 /* No operation, internal use only */ #define BOL 0x20002 /* Beginning of line, ^ */ #define EOL 0x20003 /* End of line, $ */ #define CCLASS 0x20004 /* Character class, [] */ #define NCCLASS 0x20005 /* Negated character class, [^] */ #define END 0x20077 /* Terminate: match found */ #define ISATOR 0x10000 #define ISAND 0x20000 /* * Parser Information */ typedef struct Node Node; struct Node { Inst *first; Inst *last; }; #define NSTACK 20 Node andstack[NSTACK]; Node *andp; int atorstack[NSTACK]; int *atorp; int lastwasand; /* Last token was operand */ int cursubid; int subidstack[NSTACK]; int *subidp; int backwards; int nbra; Rune *exprp; /* pointer to next character in source expression */ #define DCLASS 10 /* allocation increment */ int nclass; /* number active */ int Nclass; /* high water mark */ Rune **class; int negateclass; void addinst(Ilist *l, Inst *inst, Rangeset *sep); void newmatch(Rangeset*); void bnewmatch(Rangeset*); void pushand(Inst*, Inst*); void pushator(int); Node *popand(int); int popator(void); void startlex(Rune*); int lex(void); void operator(int); void operand(int); void evaluntil(int); void optimize(Inst*); void bldcclass(void); void rxinit(void) { rechan = chancreate(sizeof(Inst*), 0); lastregexp = runemalloc(1); } void regerror(char *e) { lastregexp[0] = 0; warning(nil, "regexp: %s\n", e); sendp(rechan, nil); threadexits(nil); } Inst * newinst(int t) { if(progp >= &program[NPROG]) regerror("expression too long"); progp->type = t; progp->left = nil; progp->right = nil; return progp++; } void realcompile(void *arg) { int token; Rune *s; threadsetname("regcomp"); s = arg; startlex(s); atorp = atorstack; andp = andstack; subidp = subidstack; cursubid = 0; lastwasand = FALSE; /* Start with a low priority operator to prime parser */ pushator(START-1); while((token=lex()) != END){ if((token&ISATOR) == OPERATOR) operator(token); else operand(token); } /* Close with a low priority operator */ evaluntil(START); /* Force END */ operand(END); evaluntil(START); if(nbra) regerror("unmatched `('"); --andp; /* points to first and only operand */ sendp(rechan, andp->first); threadexits(nil); } /* r is null terminated */ int rxcompile(Rune *r) { int i, nr; Inst *oprogp; nr = runestrlen(r)+1; if(runeeq(lastregexp, runestrlen(lastregexp)+1, r, nr)==TRUE) return TRUE; lastregexp[0] = 0; for(i=0; itype = NCCLASS; /* UGH */ i->class = nclass-1; /* UGH */ } pushand(i, i); lastwasand = TRUE; } void operator(int t) { if(t==RBRA && --nbra<0) regerror("unmatched `)'"); if(t==LBRA){ cursubid++; /* silently ignored */ nbra++; if(lastwasand) operator(CAT); }else evaluntil(t); if(t!=RBRA) pushator(t); lastwasand = FALSE; if(t==STAR || t==QUEST || t==PLUS || t==RBRA) lastwasand = TRUE; /* these look like operands */ } void pushand(Inst *f, Inst *l) { if(andp >= &andstack[NSTACK]) error("operand stack overflow"); andp->first = f; andp->last = l; andp++; } void pushator(int t) { if(atorp >= &atorstack[NSTACK]) error("operator stack overflow"); *atorp++=t; if(cursubid >= NRange) *subidp++= -1; else *subidp++=cursubid; } Node * popand(int op) { char buf[64]; if(andp <= &andstack[0]) if(op){ sprint(buf, "missing operand for %c", op); regerror(buf); }else regerror("malformed regexp"); return --andp; } int popator() { if(atorp <= &atorstack[0]) error("operator stack underflow"); --subidp; return *--atorp; } void evaluntil(int pri) { Node *op1, *op2, *t; Inst *inst1, *inst2; while(pri==RBRA || atorp[-1]>=pri){ switch(popator()){ case LBRA: op1 = popand('('); inst2 = newinst(RBRA); inst2->subid = *subidp; op1->last->next = inst2; inst1 = newinst(LBRA); inst1->subid = *subidp; inst1->next = op1->first; pushand(inst1, inst2); return; /* must have been RBRA */ default: error("unknown regexp operator"); break; case OR: op2 = popand('|'); op1 = popand('|'); inst2 = newinst(NOP); op2->last->next = inst2; op1->last->next = inst2; inst1 = newinst(OR); inst1->right = op1->first; inst1->left = op2->first; pushand(inst1, inst2); break; case CAT: op2 = popand(0); op1 = popand(0); if(backwards && op2->first->type!=END){ t = op1; op1 = op2; op2 = t; } op1->last->next = op2->first; pushand(op1->first, op2->last); break; case STAR: op2 = popand('*'); inst1 = newinst(OR); op2->last->next = inst1; inst1->right = op2->first; pushand(inst1, inst1); break; case PLUS: op2 = popand('+'); inst1 = newinst(OR); op2->last->next = inst1; inst1->right = op2->first; pushand(op2->first, inst1); break; case QUEST: op2 = popand('?'); inst1 = newinst(OR); inst2 = newinst(NOP); inst1->left = inst2; inst1->right = op2->first; op2->last->next = inst2; pushand(inst1, inst2); break; } } } void optimize(Inst *start) { Inst *inst, *target; for(inst=start; inst->type!=END; inst++){ target = inst->next; while(target->type == NOP) target = target->next; inst->next = target; } } void startlex(Rune *s) { exprp = s; nbra = 0; } int lex(void){ int c; c = *exprp++; switch(c){ case '\\': if(*exprp) if((c= *exprp++)=='n') c='\n'; break; case 0: c = END; --exprp; /* In case we come here again */ break; case '*': c = STAR; break; case '?': c = QUEST; break; case '+': c = PLUS; break; case '|': c = OR; break; case '.': c = ANY; break; case '(': c = LBRA; break; case ')': c = RBRA; break; case '^': c = BOL; break; case '$': c = EOL; break; case '[': c = CCLASS; bldcclass(); break; } return c; } int nextrec(void) { if(exprp[0]==0 || (exprp[0]=='\\' && exprp[1]==0)) regerror("malformed `[]'"); if(exprp[0] == '\\'){ exprp++; if(*exprp=='n'){ exprp++; return '\n'; } return *exprp++|0x10000; } return *exprp++; } void bldcclass(void) { int c1, c2, n, na; Rune *classp; classp = runemalloc(DCLASS); n = 0; na = DCLASS; /* we have already seen the '[' */ if(*exprp == '^'){ classp[n++] = '\n'; /* don't match newline in negate case */ negateclass = TRUE; exprp++; }else negateclass = FALSE; while((c1 = nextrec()) != ']'){ if(c1 == '-'){ Error: free(classp); regerror("malformed `[]'"); } if(n+4 >= na){ /* 3 runes plus NUL */ na += DCLASS; classp = runerealloc(classp, na); } if(*exprp == '-'){ exprp++; /* eat '-' */ if((c2 = nextrec()) == ']') goto Error; classp[n+0] = 0xFFFF; classp[n+1] = c1; classp[n+2] = c2; n += 3; }else classp[n++] = c1; } classp[n] = 0; if(nclass == Nclass){ Nclass += DCLASS; class = realloc(class, Nclass*sizeof(Rune*)); } class[nclass++] = classp; } int classmatch(int classno, int c, int negate) { Rune *p; p = class[classno]; while(*p){ if(*p == 0xFFFF){ if(p[1]<=c && c<=p[2]) return !negate; p += 3; }else if(*p++ == c) return !negate; } return negate; } /* * Note optimization in addinst: * *l must be pending when addinst called; if *l has been looked * at already, the optimization is a bug. */ void addinst(Ilist *l, Inst *inst, Rangeset *sep) { Ilist *p; for(p = l; p->inst; p++){ if(p->inst==inst){ if((sep)->r[0].q0 < p->se.r[0].q0) p->se= *sep; /* this would be bug */ return; /* It's already there */ } } p->inst = inst; p->se= *sep; (p+1)->inst = nil; } int rxnull(void) { return startinst==nil || bstartinst==nil; } /* either t!=nil or r!=nil, and we match the string in the appropriate place */ int rxexecute(Text *t, Rune *r, uint startp, uint eof, Rangeset *rp) { int flag; Inst *inst; Ilist *tlp; uint p; int nnl, ntl; int nc, c; int wrapped; int startchar; flag = 0; p = startp; startchar = 0; wrapped = 0; nnl = 0; if(startinst->typetype; list[0][0].inst = list[1][0].inst = nil; sel.r[0].q0 = -1; if(t != nil) nc = t->file->nc; else nc = runestrlen(r); /* Execute machine once for each character */ for(;;p++){ doloop: if(p>=eof || p>=nc){ switch(wrapped++){ case 0: /* let loop run one more click */ case 2: break; case 1: /* expired; wrap to beginning */ if(sel.r[0].q0>=0 || eof!=Infinity) goto Return; list[0][0].inst = list[1][0].inst = nil; p = 0; goto doloop; default: goto Return; } c = 0; }else{ if(((wrapped && p>=startp) || sel.r[0].q0>0) && nnl==0) break; if(t != nil) c = textreadc(t, p); else c = r[p]; } /* fast check for first char */ if(startchar && nnl==0 && c!=startchar) continue; tl = list[flag]; nl = list[flag^=1]; nl->inst = nil; ntl = nnl; nnl = 0; if(sel.r[0].q0<0 && (!wrapped || p= NLIST){ Overflow: warning(nil, "regexp list overflow\n"); sel.r[0].q0 = -1; goto Return; } sempty.r[0].q0 = p; addinst(tl, startinst, &sempty); } /* Execute machine until this list is empty */ for(tlp = tl; inst = tlp->inst; tlp++){ /* assignment = */ Switchstmt: switch(inst->type){ default: /* regular character */ if(inst->type==c){ Addinst: if(++nnl >= NLIST) goto Overflow; addinst(nl, inst->next, &tlp->se); } break; case LBRA: if(inst->subid>=0) tlp->se.r[inst->subid].q0 = p; inst = inst->next; goto Switchstmt; case RBRA: if(inst->subid>=0) tlp->se.r[inst->subid].q1 = p; inst = inst->next; goto Switchstmt; case ANY: if(c!='\n') goto Addinst; break; case BOL: if(p==0 || (t!=nil && textreadc(t, p-1)=='\n') || (r!=nil && r[p-1]=='\n')){ Step: inst = inst->next; goto Switchstmt; } break; case EOL: if(c == '\n') goto Step; break; case CCLASS: if(c>=0 && classmatch(inst->class, c, 0)) goto Addinst; break; case NCCLASS: if(c>=0 && classmatch(inst->class, c, 1)) goto Addinst; break; case OR: /* evaluate right choice later */ if(++ntl >= NLIST) goto Overflow; addinst(tlp, inst->right, &tlp->se); /* efficiency: advance and re-evaluate */ inst = inst->left; goto Switchstmt; case END: /* Match! */ tlp->se.r[0].q1 = p; newmatch(&tlp->se); break; } } } Return: *rp = sel; return sel.r[0].q0 >= 0; } void newmatch(Rangeset *sp) { if(sel.r[0].q0<0 || sp->r[0].q0r[0].q0==sel.r[0].q0 && sp->r[0].q1>sel.r[0].q1)) sel = *sp; } int rxbexecute(Text *t, uint startp, Rangeset *rp) { int flag; Inst *inst; Ilist *tlp; int p; int nnl, ntl; int c; int wrapped; int startchar; flag = 0; nnl = 0; wrapped = 0; p = startp; startchar = 0; if(bstartinst->typetype; list[0][0].inst = list[1][0].inst = nil; sel.r[0].q0= -1; /* Execute machine once for each character, including terminal NUL */ for(;;--p){ doloop: if(p <= 0){ switch(wrapped++){ case 0: /* let loop run one more click */ case 2: break; case 1: /* expired; wrap to end */ if(sel.r[0].q0>=0) goto Return; list[0][0].inst = list[1][0].inst = nil; p = t->file->nc; goto doloop; case 3: default: goto Return; } c = 0; }else{ if(((wrapped && p<=startp) || sel.r[0].q0>0) && nnl==0) break; c = textreadc(t, p-1); } /* fast check for first char */ if(startchar && nnl==0 && c!=startchar) continue; tl = list[flag]; nl = list[flag^=1]; nl->inst = nil; ntl = nnl; nnl = 0; if(sel.r[0].q0<0 && (!wrapped || p>startp)){ /* Add first instruction to this list */ if(++ntl >= NLIST){ Overflow: warning(nil, "regexp list overflow\n"); sel.r[0].q0 = -1; goto Return; } /* the minus is so the optimizations in addinst work */ sempty.r[0].q0 = -p; addinst(tl, bstartinst, &sempty); } /* Execute machine until this list is empty */ for(tlp = tl; inst = tlp->inst; tlp++){ /* assignment = */ Switchstmt: switch(inst->type){ default: /* regular character */ if(inst->type == c){ Addinst: if(++nnl >= NLIST) goto Overflow; addinst(nl, inst->next, &tlp->se); } break; case LBRA: if(inst->subid>=0) tlp->se.r[inst->subid].q0 = p; inst = inst->next; goto Switchstmt; case RBRA: if(inst->subid >= 0) tlp->se.r[inst->subid].q1 = p; inst = inst->next; goto Switchstmt; case ANY: if(c != '\n') goto Addinst; break; case BOL: if(c=='\n' || p==0){ Step: inst = inst->next; goto Switchstmt; } break; case EOL: if(pfile->nc && textreadc(t, p)=='\n') goto Step; break; case CCLASS: if(c>0 && classmatch(inst->class, c, 0)) goto Addinst; break; case NCCLASS: if(c>0 && classmatch(inst->class, c, 1)) goto Addinst; break; case OR: /* evaluate right choice later */ if(++ntl >= NLIST) goto Overflow; addinst(tlp, inst->right, &tlp->se); /* efficiency: advance and re-evaluate */ inst = inst->left; goto Switchstmt; case END: /* Match! */ tlp->se.r[0].q0 = -tlp->se.r[0].q0; /* minus sign */ tlp->se.r[0].q1 = p; bnewmatch(&tlp->se); break; } } } Return: *rp = sel; return sel.r[0].q0 >= 0; } void bnewmatch(Rangeset *sp) { int i; if(sel.r[0].q0<0 || sp->r[0].q0>sel.r[0].q1 || (sp->r[0].q0==sel.r[0].q1 && sp->r[0].q1r[i].q1; sel.r[i].q1 = sp->r[i].q0; } } #include #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" void rowinit(Row *row, Rectangle r) { Rectangle r1; Text *t; draw(screen, r, display->white, nil, ZP); row->r = r; row->col = nil; row->ncol = 0; r1 = r; r1.max.y = r1.min.y + font->height; t = &row->tag; textinit(t, fileaddtext(nil, t), r1, rfget(FALSE, FALSE, FALSE, nil), tagcols); t->what = Rowtag; t->row = row; t->w = nil; t->col = nil; r1.min.y = r1.max.y; r1.max.y += Border; draw(screen, r1, display->black, nil, ZP); textinsert(t, 0, L"Newcol Kill Putall Dump Exit ", 29, TRUE); textsetselect(t, t->file->nc, t->file->nc); } Column* rowadd(Row *row, Column *c, int x) { Rectangle r, r1; Column *d; int i; d = nil; r = row->r; r.min.y = row->tag.r.max.y+Border; if(xncol>0){ /*steal 40% of last column by default */ d = row->col[row->ncol-1]; x = d->r.min.x + 3*Dx(d->r)/5; } /* look for column we'll land on */ for(i=0; incol; i++){ d = row->col[i]; if(x < d->r.max.x) break; } if(row->ncol > 0){ if(i < row->ncol) i++; /* new column will go after d */ r = d->r; if(Dx(r) < 100) return nil; draw(screen, r, display->white, nil, ZP); r1 = r; r1.max.x = min(x, r.max.x-50); if(Dx(r1) < 50) r1.max.x = r1.min.x+50; colresize(d, r1); r1.min.x = r1.max.x; r1.max.x = r1.min.x+Border; draw(screen, r1, display->black, nil, ZP); r.min.x = r1.max.x; } if(c == nil){ c = emalloc(sizeof(Column)); colinit(c, r); incref(&reffont); }else colresize(c, r); c->row = row; c->tag.row = row; row->col = realloc(row->col, (row->ncol+1)*sizeof(Column*)); memmove(row->col+i+1, row->col+i, (row->ncol-i)*sizeof(Column*)); row->col[i] = c; row->ncol++; clearmouse(); return c; } void rowresize(Row *row, Rectangle r) { int i, dx, odx; Rectangle r1, r2; Column *c; dx = Dx(r); odx = Dx(row->r); row->r = r; r1 = r; r1.max.y = r1.min.y + font->height; textresize(&row->tag, r1); r1.min.y = r1.max.y; r1.max.y += Border; draw(screen, r1, display->black, nil, ZP); r.min.y = r1.max.y; r1 = r; r1.max.x = r1.min.x; for(i=0; incol; i++){ c = row->col[i]; r1.min.x = r1.max.x; if(i == row->ncol-1) r1.max.x = r.max.x; else r1.max.x = r1.min.x+Dx(c->r)*dx/odx; if(i > 0){ r2 = r1; r2.max.x = r2.min.x+Border; draw(screen, r2, display->black, nil, ZP); r1.min.x = r2.max.x; } colresize(c, r1); } } void rowdragcol(Row *row, Column *c, int) { Rectangle r; int i, b, x; Point p, op; Column *d; clearmouse(); setcursor(mousectl, &boxcursor); b = mouse->buttons; op = mouse->xy; while(mouse->buttons == b) readmouse(mousectl); setcursor(mousectl, nil); if(mouse->buttons){ while(mouse->buttons) readmouse(mousectl); return; } for(i=0; incol; i++) if(row->col[i] == c) goto Found; error("can't find column"); Found: if(i == 0) return; p = mouse->xy; if((abs(p.x-op.x)<5 && abs(p.y-op.y)<5)) return; if((i>0 && p.xcol[i-1]->r.min.x) || (incol-1 && p.x>c->r.max.x)){ /* shuffle */ x = c->r.min.x; rowclose(row, c, FALSE); if(rowadd(row, c, p.x) == nil) /* whoops! */ if(rowadd(row, c, x) == nil) /* WHOOPS! */ if(rowadd(row, c, -1)==nil){ /* shit! */ rowclose(row, c, TRUE); return; } colmousebut(c); return; } d = row->col[i-1]; if(p.x < d->r.min.x+80+Scrollwid) p.x = d->r.min.x+80+Scrollwid; if(p.x > c->r.max.x-80-Scrollwid) p.x = c->r.max.x-80-Scrollwid; r = d->r; r.max.x = c->r.max.x; draw(screen, r, display->white, nil, ZP); r.max.x = p.x; colresize(d, r); r = c->r; r.min.x = p.x; r.max.x = r.min.x; r.max.x += Border; draw(screen, r, display->black, nil, ZP); r.min.x = r.max.x; r.max.x = c->r.max.x; colresize(c, r); colmousebut(c); } void rowclose(Row *row, Column *c, int dofree) { Rectangle r; int i; for(i=0; incol; i++) if(row->col[i] == c) goto Found; error("can't find column"); Found: r = c->r; if(dofree) colcloseall(c); memmove(row->col+i, row->col+i+1, (row->ncol-i)*sizeof(Column*)); row->ncol--; row->col = realloc(row->col, row->ncol*sizeof(Column*)); if(row->ncol == 0){ draw(screen, r, display->white, nil, ZP); return; } if(i == row->ncol){ /* extend last column right */ c = row->col[i-1]; r.min.x = c->r.min.x; r.max.x = row->r.max.x; }else{ /* extend next window left */ c = row->col[i]; r.max.x = c->r.max.x; } draw(screen, r, display->white, nil, ZP); colresize(c, r); } Column* rowwhichcol(Row *row, Point p) { int i; Column *c; for(i=0; incol; i++){ c = row->col[i]; if(ptinrect(p, c->r)) return c; } return nil; } Text* rowwhich(Row *row, Point p) { Column *c; if(ptinrect(p, row->tag.all)) return &row->tag; c = rowwhichcol(row, p); if(c) return colwhich(c, p); return nil; } Text* rowtype(Row *row, Rune r, Point p) { Window *w; Text *t; clearmouse(); qlock(row); if(bartflag) t = barttext; else t = rowwhich(row, p); if(t!=nil && !(t->what==Tag && ptinrect(p, t->scrollr))){ w = t->w; if(w == nil) texttype(t, r); else{ winlock(w, 'K'); wintype(w, t, r); winunlock(w); } } qunlock(row); return t; } int rowclean(Row *row) { int clean; int i; clean = TRUE; for(i=0; incol; i++) clean &= colclean(row->col[i]); return clean; } void rowdump(Row *row, char *file) { int i, j, fd, m, n, dumped; uint q0, q1; Biobuf *b; char *buf, *a, *fontname; Rune *r; Column *c; Window *w, *w1; Text *t; if(row->ncol == 0) return; buf = fbufalloc(); if(file == nil){ if(home == nil){ warning(nil, "can't find file for dump: $home not defined\n"); goto Rescue; } sprint(buf, "%s/acme.dump", home); file = buf; } fd = create(file, OWRITE, 0600); if(fd < 0){ warning(nil, "can't open %s: %r\n", file); goto Rescue; } b = emalloc(sizeof(Biobuf)); Binit(b, fd, OWRITE); r = fbufalloc(); Bprint(b, "%s\n", wdir); Bprint(b, "%s\n", fontnames[0]); Bprint(b, "%s\n", fontnames[1]); for(i=0; incol; i++){ c = row->col[i]; Bprint(b, "%11d", 100*(c->r.min.x-row->r.min.x)/Dx(row->r)); if(i == row->ncol-1) Bputc(b, '\n'); else Bputc(b, ' '); } for(i=0; incol; i++){ c = row->col[i]; for(j=0; jnw; j++) c->w[j]->body.file->dumpid = 0; } for(i=0; incol; i++){ c = row->col[i]; for(j=0; jnw; j++){ w = c->w[j]; wincommit(w, &w->tag); t = &w->body; /* windows owned by others get special treatment */ if(w->nopen[QWevent] > 0) if(w->dumpstr == nil) continue; /* zeroxes of external windows are tossed */ if(t->file->ntext > 1) for(n=0; nfile->ntext; n++){ w1 = t->file->text[n]->w; if(w == w1) continue; if(w1->nopen[QWevent]) goto Continue2; } fontname = ""; if(t->reffont->f != font) fontname = t->reffont->f->name; if(t->file->nname) a = runetobyte(t->file->name, t->file->nname); else a = emalloc(1); if(t->file->dumpid){ dumped = FALSE; Bprint(b, "x%11d %11d %11d %11d %11d %s\n", i, t->file->dumpid, w->body.q0, w->body.q1, 100*(w->r.min.y-c->r.min.y)/Dy(c->r), fontname); }else if(w->dumpstr){ dumped = FALSE; Bprint(b, "e%11d %11d %11d %11d %11d %s\n", i, t->file->dumpid, 0, 0, 100*(w->r.min.y-c->r.min.y)/Dy(c->r), fontname); }else if((w->dirty==FALSE && access(a, 0)==0) || w->isdir){ dumped = FALSE; t->file->dumpid = w->id; Bprint(b, "f%11d %11d %11d %11d %11d %s\n", i, w->id, w->body.q0, w->body.q1, 100*(w->r.min.y-c->r.min.y)/Dy(c->r), fontname); }else{ dumped = TRUE; t->file->dumpid = w->id; Bprint(b, "F%11d %11d %11d %11d %11d %11d %s\n", i, j, w->body.q0, w->body.q1, 100*(w->r.min.y-c->r.min.y)/Dy(c->r), w->body.file->nc, fontname); } free(a); winctlprint(w, buf, 0); Bwrite(b, buf, strlen(buf)); m = min(RBUFSIZE, w->tag.file->nc); bufread(w->tag.file, 0, r, m); n = 0; while(nfile->nc; while(q0 < q1){ n = q1 - q0; if(n > BUFSIZE/UTFmax) n = BUFSIZE/UTFmax; bufread(t->file, q0, r, n); Bprint(b, "%.*S", n, r); q0 += n; } } if(w->dumpstr){ if(w->dumpdir) Bprint(b, "%s\n%s\n", w->dumpdir, w->dumpstr); else Bprint(b, "\n%s\n", w->dumpstr); } Continue2:; } } Bterm(b); close(fd); free(b); fbuffree(r); Rescue: fbuffree(buf); } static char* rdline(Biobuf *b, int *linep) { char *l; l = Brdline(b, '\n'); if(l) (*linep)++; return l; } /* * Get font names from load file so we don't load fonts we won't use */ void rowloadfonts(char *file) { int i; Biobuf *b; char *l; b = Bopen(file, OREAD); if(b == nil) return; /* current directory */ l = Brdline(b, '\n'); if(l == nil) goto Return; /* global fonts */ for(i=0; i<2; i++){ l = Brdline(b, '\n'); if(l == nil) goto Return; l[Blinelen(b)-1] = 0; if(*l && strcmp(l, fontnames[i])!=0) fontnames[i] = estrdup(l); } Return: Bterm(b); } void rowload(Row *row, char *file, int initing) { int i, j, line, percent, y, nr, nfontr, n, ns, ndumped, dumpid, x, fd; Biobuf *b, *bout; char *buf, *l, *t, *fontname; Rune *r, rune, *fontr; Column *c, *c1, *c2; uint q0, q1; Rectangle r1, r2; Window *w; buf = fbufalloc(); if(file == nil){ if(home == nil){ warning(nil, "can't find file for load: $home not defined\n"); goto Rescue1; } sprint(buf, "%s/acme.dump", home); file = buf; } b = Bopen(file, OREAD); if(b == nil){ warning(nil, "can't open load file %s: %r\n", file); goto Rescue1; } /* current directory */ line = 0; l = rdline(b, &line); if(l == nil) goto Rescue2; l[Blinelen(b)-1] = 0; if(chdir(l) < 0){ warning(nil, "can't chdir %s\n", l); goto Rescue2; } /* global fonts */ for(i=0; i<2; i++){ l = rdline(b, &line); if(l == nil) goto Rescue2; l[Blinelen(b)-1] = 0; if(*l && strcmp(l, fontnames[i])!=0) rfget(i, TRUE, i==0 && initing, estrdup(l)); } if(initing && row->ncol==0) rowinit(row, screen->clipr); l = rdline(b, &line); if(l == nil) goto Rescue2; j = Blinelen(b)/12; if(j<=0 || j>10) goto Rescue2; for(i=0; i=100) goto Rescue2; x = row->r.min.x+percent*Dx(row->r)/100; if(i < row->ncol){ if(i == 0) continue; c1 = row->col[i-1]; c2 = row->col[i]; r1 = c1->r; r2 = c2->r; r1.max.x = x; r2.min.x = x+Border; if(Dx(r1) < 50 || Dx(r2) < 50) continue; draw(screen, Rpt(r1.min, r2.max), display->white, nil, ZP); colresize(c1, r1); colresize(c2, r2); r2.min.x = x; r2.max.x = x+Border; draw(screen, r2, display->black, nil, ZP); } if(i >= row->ncol) rowadd(row, nil, x); } for(;;){ l = rdline(b, &line); if(l == nil) break; dumpid = 0; switch(l[0]){ case 'e': if(Blinelen(b) < 1+5*12+1) goto Rescue2; l = rdline(b, &line); /* ctl line; ignored */ if(l == nil) goto Rescue2; l = rdline(b, &line); /* directory */ if(l == nil) goto Rescue2; l[Blinelen(b)-1] = 0; if(*l == '\0'){ if(home == nil) r = bytetorune("./", &nr); else{ t = emalloc(strlen(home)+1+1); sprint(t, "%s/", home); r = bytetorune(t, &nr); free(t); } }else r = bytetorune(l, &nr); l = rdline(b, &line); /* command */ if(l == nil) goto Rescue2; t = emalloc(Blinelen(b)+1); memmove(t, l, Blinelen(b)); run(nil, t, r, nr, TRUE, nil, nil, FALSE); /* r is freed in run() */ continue; case 'f': if(Blinelen(b) < 1+5*12+1) goto Rescue2; fontname = l+1+5*12; ndumped = -1; break; case 'F': if(Blinelen(b) < 1+6*12+1) goto Rescue2; fontname = l+1+6*12; ndumped = atoi(l+1+5*12+1); break; case 'x': if(Blinelen(b) < 1+5*12+1) goto Rescue2; fontname = l+1+5*12; ndumped = -1; dumpid = atoi(l+1+1*12); break; default: goto Rescue2; } l[Blinelen(b)-1] = 0; fontr = nil; nfontr = 0; if(*fontname) fontr = bytetorune(fontname, &nfontr); i = atoi(l+1+0*12); j = atoi(l+1+1*12); q0 = atoi(l+1+2*12); q1 = atoi(l+1+3*12); percent = atoi(l+1+4*12); if(i<0 || i>10) goto Rescue2; if(i > row->ncol) i = row->ncol; c = row->col[i]; y = c->r.min.y+(percent*Dy(c->r))/100; if(yr.min.y || y>=c->r.max.y) y = -1; if(dumpid == 0) w = coladd(c, nil, nil, y); else w = coladd(c, nil, lookid(dumpid, TRUE), y); if(w == nil) continue; w->dumpid = j; l = rdline(b, &line); if(l == nil) goto Rescue2; l[Blinelen(b)-1] = 0; r = bytetorune(l+5*12, &nr); ns = -1; for(n=0; ntag, w->tag.file->nc, r+n+1, nr-(n+1), TRUE); free(r); if(ndumped >= 0){ /* simplest thing is to put it in a file and load that */ sprint(buf, "/tmp/d%d.%.4sacme", getpid(), getuser()); fd = create(buf, OWRITE|ORCLOSE, 0600); if(fd < 0){ warning(nil, "can't create temp file: %r\n"); goto Rescue2; } bout = emalloc(sizeof(Biobuf)); Binit(bout, fd, OWRITE); for(n=0; nbody, 0, buf, 1); close(fd); w->body.file->mod = TRUE; for(n=0; nbody.file->ntext; n++) w->body.file->text[n]->w->dirty = TRUE; winsettag(w); }else if(dumpid==0 && r[ns+1]!='+' && r[ns+1]!='-') get(&w->body, nil, nil, FALSE, XXX, nil, 0); if(fontr){ fontx(&w->body, nil, nil, 0, 0, fontr, nfontr); free(fontr); } if(q0>w->body.file->nc || q1>w->body.file->nc || q0>q1) q0 = q1 = 0; textshow(&w->body, q0, q1, 1); w->maxlines = min(w->body.nlines, max(w->maxlines, w->body.maxlines)); } Bterm(b); Rescue1: fbuffree(buf); return; Rescue2: warning(nil, "bad load file %s:%d\n", file, line); Bterm(b); goto Rescue1; } void allwindows(void (*f)(Window*, void*), void *arg) { int i, j; Column *c; for(i=0; inw; j++) (*f)(c->w[j], arg); } } #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" static Image *scrtmp; static Rectangle scrpos(Rectangle r, uint p0, uint p1, uint tot) { Rectangle q; int h; q = r; h = q.max.y-q.min.y; if(tot == 0) return q; if(tot > 1024*1024){ tot>>=10; p0>>=10; p1>>=10; } if(p0 > 0) q.min.y += h*p0/tot; if(p1 < tot) q.max.y -= h*(tot-p1)/tot; if(q.max.y < q.min.y+2){ if(q.min.y+2 <= r.max.y) q.max.y = q.min.y+2; else q.min.y = q.max.y-2; } return q; } void scrlresize(void) { freeimage(scrtmp); scrtmp = allocimage(display, Rect(0, 0, 32, screen->r.max.y), screen->chan, 0, DNofill); if(scrtmp == nil) error("scroll alloc"); } void textscrdraw(Text *t) { Rectangle r, r1, r2; Image *b; if(t->w==nil || t!=&t->w->body) return; if(scrtmp == nil) scrlresize(); r = t->scrollr; b = scrtmp; r1 = r; r1.min.x = 0; r1.max.x = Dx(r); r2 = scrpos(r1, t->org, t->org+t->nchars, t->file->nc); if(!eqrect(r2, t->lastsr)){ t->lastsr = r2; draw(b, r1, t->cols[BORD], nil, ZP); draw(b, r2, t->cols[BACK], nil, ZP); r2.min.x = r2.max.x-1; draw(b, r2, t->cols[BORD], nil, ZP); draw(t->b, r, b, nil, Pt(0, r1.min.y)); /*flushimage(display, 1);/*BUG?*/ } } void scrsleep(uint dt) { Timer *timer; static Alt alts[3]; timer = timerstart(dt); alts[0].c = timer->c; alts[0].v = nil; alts[0].op = CHANRCV; alts[1].c = mousectl->c; alts[1].v = &mousectl->Mouse; alts[1].op = CHANRCV; alts[2].op = CHANEND; for(;;) switch(alt(alts)){ case 0: timerstop(timer); return; case 1: timercancel(timer); return; } } void textscroll(Text *t, int but) { uint p0, oldp0; Rectangle s; int x, y, my, h, first; s = insetrect(t->scrollr, 1); h = s.max.y-s.min.y; x = (s.min.x+s.max.x)/2; oldp0 = ~0; first = TRUE; do{ flushimage(display, 1); if(mouse->xy.xxy.x){ readmouse(mousectl); }else{ my = mouse->xy.y; if(my < s.min.y) my = s.min.y; if(my >= s.max.y) my = s.max.y; if(!eqpt(mouse->xy, Pt(x, my))){ moveto(mousectl, Pt(x, my)); readmouse(mousectl); /* absorb event generated by moveto() */ } if(but == 2){ y = my; if(y > s.max.y-2) y = s.max.y-2; if(t->file->nc > 1024*1024) p0 = ((t->file->nc>>10)*(y-s.min.y)/h)<<10; else p0 = t->file->nc*(y-s.min.y)/h; if(oldp0 != p0) textsetorigin(t, p0, FALSE); oldp0 = p0; readmouse(mousectl); continue; } if(but == 1) p0 = textbacknl(t, t->org, (my-s.min.y)/t->font->height); else p0 = t->org+frcharofpt(t, Pt(s.max.x, my)); if(oldp0 != p0) textsetorigin(t, p0, TRUE); oldp0 = p0; /* debounce */ if(first){ flushimage(display, 1); sleep(200); nbrecv(mousectl->c, &mousectl->Mouse); first = FALSE; } scrsleep(80); } }while(mouse->buttons & (1<<(but-1))); while(mouse->buttons) readmouse(mousectl); } #include #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" Image *tagcols[NCOL]; Image *textcols[NCOL]; enum{ TABDIR = 3 /* width of tabs in directory windows */ }; void textinit(Text *t, File *f, Rectangle r, Reffont *rf, Image *cols[NCOL]) { t->file = f; t->all = r; t->scrollr = r; t->scrollr.max.x = r.min.x+Scrollwid; t->lastsr = nullrect; r.min.x += Scrollwid+Scrollgap; t->eq0 = ~0; t->ncache = 0; t->reffont = rf; t->tabstop = maxtab; memmove(t->Frame.cols, cols, sizeof t->Frame.cols); textredraw(t, r, rf->f, screen, -1); } void textredraw(Text *t, Rectangle r, Font *f, Image *b, int odx) { int maxt; Rectangle rr; frinit(t, r, f, b, t->Frame.cols); rr = t->r; rr.min.x -= Scrollwid; /* back fill to scroll bar */ draw(t->b, rr, t->cols[BACK], nil, ZP); /* use no wider than 3-space tabs in a directory */ maxt = maxtab; if(t->what == Body){ if(t->w->isdir) maxt = min(TABDIR, maxtab); else maxt = t->tabstop; } t->maxtab = maxt*stringwidth(f, "0"); if(t->what==Body && t->w->isdir && odx!=Dx(t->all)){ if(t->maxlines > 0){ textreset(t); textcolumnate(t, t->w->dlp, t->w->ndl); textshow(t, 0, 0, 1); } }else{ textfill(t); textsetselect(t, t->q0, t->q1); } } int textresize(Text *t, Rectangle r) { int odx; if(Dy(r) > 0) r.max.y -= Dy(r)%t->font->height; else r.max.y = r.min.y; odx = Dx(t->all); t->all = r; t->scrollr = r; t->scrollr.max.x = r.min.x+Scrollwid; t->lastsr = nullrect; r.min.x += Scrollwid+Scrollgap; frclear(t, 0); textredraw(t, r, t->font, t->b, odx); return r.max.y; } void textclose(Text *t) { free(t->cache); frclear(t, 1); filedeltext(t->file, t); t->file = nil; rfclose(t->reffont); if(argtext == t) argtext = nil; if(typetext == t) typetext = nil; if(seltext == t) seltext = nil; if(mousetext == t) mousetext = nil; if(barttext == t) barttext = nil; } int dircmp(void *a, void *b) { Dirlist *da, *db; int i, n; da = *(Dirlist**)a; db = *(Dirlist**)b; n = min(da->nr, db->nr); i = memcmp(da->r, db->r, n*sizeof(Rune)); if(i) return i; return da->nr - db->nr; } void textcolumnate(Text *t, Dirlist **dlp, int ndl) { int i, j, w, colw, mint, maxt, ncol, nrow; Dirlist *dl; uint q1; if(t->file->ntext > 1) return; mint = stringwidth(t->font, "0"); /* go for narrower tabs if set more than 3 wide */ t->maxtab = min(maxtab, TABDIR)*mint; maxt = t->maxtab; colw = 0; for(i=0; iwid; if(maxt-w%maxt < mint || w%maxt==0) w += mint; if(w % maxt) w += maxt-(w%maxt); if(w > colw) colw = w; } if(colw == 0) ncol = 1; else ncol = max(1, Dx(t->r)/colw); nrow = (ndl+ncol-1)/ncol; q1 = 0; for(i=0; ifile, q1, dl->r, dl->nr); q1 += dl->nr; if(j+nrow >= ndl) break; w = dl->wid; if(maxt-w%maxt < mint){ fileinsert(t->file, q1, L"\t", 1); q1++; w += mint; } do{ fileinsert(t->file, q1, L"\t", 1); q1++; w += maxt-(w%maxt); }while(w < colw); } fileinsert(t->file, q1, L"\n", 1); q1++; } } uint textload(Text *t, uint q0, char *file, int setqid) { Rune *rp; Dirlist *dl, **dlp; int fd, i, j, n, ndl, nulls; uint q, q1; Dir *d, *dbuf; char *tmp; Text *u; if(t->ncache!=0 || t->file->nc || t->w==nil || t!=&t->w->body) error("text.load"); if(t->w->isdir && t->file->nname==0){ warning(nil, "empty directory name\n"); return 0; } fd = open(file, OREAD); if(fd < 0){ warning(nil, "can't open %s: %r\n", file); return 0; } d = dirfstat(fd); if(d == nil){ warning(nil, "can't fstat %s: %r\n", file); goto Rescue; } nulls = FALSE; if(d->qid.type & QTDIR){ /* this is checked in get() but it's possible the file changed underfoot */ if(t->file->ntext > 1){ warning(nil, "%s is a directory; can't read with multiple windows on it\n", file); goto Rescue; } t->w->isdir = TRUE; t->w->filemenu = FALSE; if(t->file->name[t->file->nname-1] != '/'){ rp = runemalloc(t->file->nname+1); runemove(rp, t->file->name, t->file->nname); rp[t->file->nname] = '/'; winsetname(t->w, rp, t->file->nname+1); free(rp); } dlp = nil; ndl = 0; dbuf = nil; while((n=dirread(fd, &dbuf)) > 0){ for(i=0; ir = bytetorune(tmp, &dl->nr); dl->wid = stringwidth(t->font, tmp); free(tmp); ndl++; dlp = realloc(dlp, ndl*sizeof(Dirlist*)); dlp[ndl-1] = dl; } free(dbuf); } qsort(dlp, ndl, sizeof(Dirlist*), dircmp); t->w->dlp = dlp; t->w->ndl = ndl; textcolumnate(t, dlp, ndl); q1 = t->file->nc; }else{ t->w->isdir = FALSE; t->w->filemenu = TRUE; q1 = q0 + fileload(t->file, q0, fd, &nulls); } if(setqid){ t->file->dev = d->dev; t->file->mtime = d->mtime; t->file->qidpath = d->qid.path; } close(fd); rp = fbufalloc(); for(q=q0; q RBUFSIZE) n = RBUFSIZE; bufread(t->file, q, rp, n); if(q < t->org) t->org += n; else if(q <= t->org+t->nchars) frinsert(t, rp, rp+n, q-t->org); if(t->lastlinefull) break; } fbuffree(rp); for(i=0; ifile->ntext; i++){ u = t->file->text[i]; if(u != t){ if(u->org > u->file->nc) /* will be 0 because of reset(), but safety first */ u->org = 0; textresize(u, u->all); textbacknl(u, u->org, 0); /* go to beginning of line */ } textsetselect(u, q0, q0); } if(nulls) warning(nil, "%s: NUL bytes elided\n", file); free(d); return q1-q0; Rescue: close(fd); return 0; } uint textbsinsert(Text *t, uint q0, Rune *r, uint n, int tofile, int *nrp) { Rune *bp, *tp, *up; int i, initial; if(t->what == Tag){ /* can't happen but safety first: mustn't backspace over file name */ Err: textinsert(t, q0, r, n, tofile); *nrp = n; return q0; } bp = r; for(i=0; i q0) initial = q0; q0 -= initial; textdelete(t, q0, q0+initial, tofile); } n = up-tp; textinsert(t, q0, tp, n, tofile); free(tp); *nrp = n; return q0; } goto Err; } void textinsert(Text *t, uint q0, Rune *r, uint n, int tofile) { int c, i; Text *u; if(tofile && t->ncache != 0) error("text.insert"); if(n == 0) return; if(tofile){ fileinsert(t->file, q0, r, n); if(t->what == Body){ t->w->dirty = TRUE; t->w->utflastqid = -1; } if(t->file->ntext > 1) for(i=0; ifile->ntext; i++){ u = t->file->text[i]; if(u != t){ u->w->dirty = TRUE; /* always a body */ textinsert(u, q0, r, n, FALSE); textsetselect(u, u->q0, u->q1); textscrdraw(u); } } } if(q0 < t->q1) t->q1 += n; if(q0 < t->q0) t->q0 += n; if(q0 < t->org) t->org += n; else if(q0 <= t->org+t->nchars) frinsert(t, r, r+n, q0-t->org); if(t->w){ c = 'i'; if(t->what == Body) c = 'I'; if(n <= EVENTSIZE) winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q0+n, n, n, r); else winevent(t->w, "%c%d %d 0 0 \n", c, q0, q0+n, n); } } void textfill(Text *t) { Rune *rp; int i, n, m, nl; if(t->lastlinefull || t->nofill) return; if(t->ncache > 0){ if(t->w != nil) wincommit(t->w, t); else textcommit(t, TRUE); } rp = fbufalloc(); do{ n = t->file->nc-(t->org+t->nchars); if(n == 0) break; if(n > 2000) /* educated guess at reasonable amount */ n = 2000; bufread(t->file, t->org+t->nchars, rp, n); /* * it's expensive to frinsert more than we need, so * count newlines. */ nl = t->maxlines-t->nlines; m = 0; for(i=0; i= nl) break; } } frinsert(t, rp, rp+i, t->nchars); }while(t->lastlinefull == FALSE); fbuffree(rp); } void textdelete(Text *t, uint q0, uint q1, int tofile) { uint n, p0, p1; int i, c; Text *u; if(tofile && t->ncache != 0) error("text.delete"); n = q1-q0; if(n == 0) return; if(tofile){ filedelete(t->file, q0, q1); if(t->what == Body){ t->w->dirty = TRUE; t->w->utflastqid = -1; } if(t->file->ntext > 1) for(i=0; ifile->ntext; i++){ u = t->file->text[i]; if(u != t){ u->w->dirty = TRUE; /* always a body */ textdelete(u, q0, q1, FALSE); textsetselect(u, u->q0, u->q1); textscrdraw(u); } } } if(q0 < t->q0) t->q0 -= min(n, t->q0-q0); if(q0 < t->q1) t->q1 -= min(n, t->q1-q0); if(q1 <= t->org) t->org -= n; else if(q0 < t->org+t->nchars){ p1 = q1 - t->org; if(p1 > t->nchars) p1 = t->nchars; if(q0 < t->org){ t->org = q0; p0 = 0; }else p0 = q0 - t->org; frdelete(t, p0, p1); textfill(t); } if(t->w){ c = 'd'; if(t->what == Body) c = 'D'; winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1); } } void textconstrain(Text *t, uint q0, uint q1, uint *p0, uint *p1) { *p0 = min(q0, t->file->nc); *p1 = min(q1, t->file->nc); } Rune textreadc(Text *t, uint q) { Rune r; if(t->cq0<=q && qcq0+t->ncache) r = t->cache[q-t->cq0]; else bufread(t->file, q, &r, 1); return r; } int textbswidth(Text *t, Rune c) { uint q, eq; Rune r; int skipping; /* there is known to be at least one character to erase */ if(c == 0x08) /* ^H: erase character */ return 1; q = t->q0; skipping = TRUE; while(q > 0){ r = textreadc(t, q-1); if(r == '\n'){ /* eat at most one more character */ if(q == t->q0) /* eat the newline */ --q; break; } if(c == 0x17){ eq = isalnum(r); if(eq && skipping) /* found one; stop skipping */ skipping = FALSE; else if(!eq && !skipping) break; } --q; } return t->q0-q; } int textfilewidth(Text *t, uint q0, int oneelement) { uint q; Rune r; q = q0; while(q > 0){ r = textreadc(t, q-1); if(r <= ' ') break; if(oneelement && r=='/') break; --q; } return q0-q; } Rune* textcomplete(Text *t) { int i, nstr, npath; uint q; Rune tmp[200]; Rune *str, *path; Rune *rp; Completion *c; char *s, *dirs; Runestr dir; /* control-f: filename completion; works back to white space or / */ if(t->q0file->nc && textreadc(t, t->q0)>' ') /* must be at end of word */ return nil; nstr = textfilewidth(t, t->q0, TRUE); str = runemalloc(nstr); npath = textfilewidth(t, t->q0-nstr, FALSE); path = runemalloc(npath); c = nil; rp = nil; dirs = nil; q = t->q0-nstr; for(i=0; iq0-nstr-npath; for(i=0; i0 && path[0]=='/') dir = (Runestr){path, npath}; else{ dir = dirname(t, nil, 0); if(dir.nr + 1 + npath > nelem(tmp)){ free(dir.r); goto Return; } if(dir.nr == 0){ dir.nr = 1; dir.r = runestrdup(L"."); } runemove(tmp, dir.r, dir.nr); tmp[dir.nr] = '/'; runemove(tmp+dir.nr+1, path, npath); free(dir.r); dir.r = tmp; dir.nr += 1+npath; dir = cleanrname(dir); } s = smprint("%.*S", nstr, str); dirs = smprint("%.*S", dir.nr, dir.r); c = complete(dirs, s); free(s); if(c == nil){ warning(nil, "error attempting completion: %r\n"); goto Return; } if(!c->advance){ warning(nil, "%.*S%s%.*S*%s\n", dir.nr, dir.r, dir.nr>0 && dir.r[dir.nr-1]!='/' ? "/" : "", nstr, str, c->nmatch? "" : ": no matches in:"); for(i=0; infile; i++) warning(nil, " %s\n", c->filename[i]); } if(c->advance) rp = runesmprint("%s", c->string); else rp = nil; Return: freecompletion(c); free(dirs); free(str); free(path); return rp; } void texttype(Text *t, Rune r) { uint q0, q1; int nnb, nb, n, i; int nr; Rune *rp; Text *u; if(t->what!=Body && r=='\n') return; nr = 1; rp = &r; switch(r){ case Kleft: if(t->q0 > 0){ textcommit(t, TRUE); textshow(t, t->q0-1, t->q0-1, TRUE); } return; case Kright: if(t->q1 < t->file->nc){ textcommit(t, TRUE); textshow(t, t->q1+1, t->q1+1, TRUE); } return; case Kdown: n = t->maxlines/3; goto case_Down; case Kpgdown: n = 2*t->maxlines/3; case_Down: q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+n*t->font->height)); textsetorigin(t, q0, FALSE); return; case Kup: n = t->maxlines/3; goto case_Up; case Kpgup: n = 2*t->maxlines/3; case_Up: q0 = textbacknl(t, t->org, n); textsetorigin(t, q0, FALSE); return; case Khome: textshow(t, 0, 0, FALSE); return; case Kend: if(t->w) wincommit(t->w, t); else textcommit(t, TRUE); textshow(t, t->file->nc, t->file->nc, FALSE); return; } if(t->what == Body){ seq++; filemark(t->file); } if(t->q1 > t->q0){ if(t->ncache != 0) error("text.type"); cut(t, t, nil, TRUE, TRUE, nil, 0); t->eq0 = ~0; } textshow(t, t->q0, t->q0, 1); switch(r){ case 0x06: case Kins: rp = textcomplete(t); if(rp == nil) return; nr = runestrlen(rp); break; /* fall through to normal insertion case */ case 0x1B: if(t->eq0 != ~0) textsetselect(t, t->eq0, t->q0); if(t->ncache > 0){ if(t->w != nil) wincommit(t->w, t); else textcommit(t, TRUE); } return; case 0x08: /* ^H: erase character */ case 0x15: /* ^U: erase line */ case 0x17: /* ^W: erase word */ if(t->q0 == 0) /* nothing to erase */ return; nnb = textbswidth(t, r); q1 = t->q0; q0 = q1-nnb; /* if selection is at beginning of window, avoid deleting invisible text */ if(q0 < t->org){ q0 = t->org; nnb = q1-q0; } if(nnb <= 0) return; for(i=0; ifile->ntext; i++){ u = t->file->text[i]; u->nofill = TRUE; nb = nnb; n = u->ncache; if(n > 0){ if(q1 != u->cq0+n) error("text.type backspace"); if(n > nb) n = nb; u->ncache -= n; textdelete(u, q1-n, q1, FALSE); nb -= n; } if(u->eq0==q1 || u->eq0==~0) u->eq0 = q0; if(nb && u==t) textdelete(u, q0, q0+nb, TRUE); if(u != t) textsetselect(u, u->q0, u->q1); else textsetselect(t, q0, q0); u->nofill = FALSE; } for(i=0; ifile->ntext; i++) textfill(t->file->text[i]); return; case '\n': if(t->w->autoindent){ /* find beginning of previous line using backspace code */ nnb = textbswidth(t, 0x15); /* ^U case */ rp = runemalloc(nnb + 1); nr = 0; rp[nr++] = r; for(i=0; iq0-nnb+i); if(r != ' ' && r != '\t') break; rp[nr++] = r; } } break; /* fall through to normal code */ } /* otherwise ordinary character; just insert, typically in caches of all texts */ for(i=0; ifile->ntext; i++){ u = t->file->text[i]; if(u->eq0 == ~0) u->eq0 = t->q0; if(u->ncache == 0) u->cq0 = t->q0; else if(t->q0 != u->cq0+u->ncache) error("text.type cq1"); textinsert(u, t->q0, rp, nr, FALSE); if(u != t) textsetselect(u, u->q0, u->q1); if(u->ncache+nr > u->ncachealloc){ u->ncachealloc += 10 + nr; u->cache = runerealloc(u->cache, u->ncachealloc); } runemove(u->cache+u->ncache, rp, nr); u->ncache += nr; } if(rp != &r) free(rp); textsetselect(t, t->q0+nr, t->q0+nr); if(r=='\n' && t->w!=nil) wincommit(t->w, t); } void textcommit(Text *t, int tofile) { if(t->ncache == 0) return; if(tofile) fileinsert(t->file, t->cq0, t->cache, t->ncache); if(t->what == Body){ t->w->dirty = TRUE; t->w->utflastqid = -1; } t->ncache = 0; } static Text *clicktext; static uint clickmsec; static Text *selecttext; static uint selectq; /* * called from frame library */ void framescroll(Frame *f, int dl) { if(f != &selecttext->Frame) error("frameselect not right frame"); textframescroll(selecttext, dl); } void textframescroll(Text *t, int dl) { uint q0; if(dl == 0){ scrsleep(100); return; } if(dl < 0){ q0 = textbacknl(t, t->org, -dl); if(selectq > t->org+t->p0) textsetselect(t, t->org+t->p0, selectq); else textsetselect(t, selectq, t->org+t->p0); }else{ if(t->org+t->nchars == t->file->nc) return; q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+dl*t->font->height)); if(selectq > t->org+t->p1) textsetselect(t, t->org+t->p1, selectq); else textsetselect(t, selectq, t->org+t->p1); } textsetorigin(t, q0, TRUE); } void textselect(Text *t) { uint q0, q1; int b, x, y; int state; selecttext = t; /* * To have double-clicking and chording, we double-click * immediately if it might make sense. */ b = mouse->buttons; q0 = t->q0; q1 = t->q1; selectq = t->org+frcharofpt(t, mouse->xy); if(clicktext==t && mouse->msec-clickmsec<500) if(q0==q1 && selectq==q0){ textdoubleclick(t, &q0, &q1); textsetselect(t, q0, q1); flushimage(display, 1); x = mouse->xy.x; y = mouse->xy.y; /* stay here until something interesting happens */ do readmouse(mousectl); while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3); mouse->xy.x = x; /* in case we're calling frselect */ mouse->xy.y = y; q0 = t->q0; /* may have changed */ q1 = t->q1; selectq = q0; } if(mouse->buttons == b){ t->Frame.scroll = framescroll; frselect(t, mousectl); /* horrible botch: while asleep, may have lost selection altogether */ if(selectq > t->file->nc) selectq = t->org + t->p0; t->Frame.scroll = nil; if(selectq < t->org) q0 = selectq; else q0 = t->org + t->p0; if(selectq > t->org+t->nchars) q1 = selectq; else q1 = t->org+t->p1; } if(q0 == q1){ if(q0==t->q0 && clicktext==t && mouse->msec-clickmsec<500){ textdoubleclick(t, &q0, &q1); clicktext = nil; }else{ clicktext = t; clickmsec = mouse->msec; } }else clicktext = nil; textsetselect(t, q0, q1); flushimage(display, 1); state = 0; /* undo when possible; +1 for cut, -1 for paste */ while(mouse->buttons){ mouse->msec = 0; b = mouse->buttons; if(b & 6){ if(state==0 && t->what==Body){ seq++; filemark(t->w->body.file); } if(b & 2){ if(state==-1 && t->what==Body){ winundo(t->w, TRUE); textsetselect(t, q0, t->q0); state = 0; }else if(state != 1){ cut(t, t, nil, TRUE, TRUE, nil, 0); state = 1; } }else{ if(state==1 && t->what==Body){ winundo(t->w, TRUE); textsetselect(t, q0, t->q1); state = 0; }else if(state != -1){ paste(t, t, nil, TRUE, FALSE, nil, 0); state = -1; } } textscrdraw(t); clearmouse(); } flushimage(display, 1); while(mouse->buttons == b) readmouse(mousectl); clicktext = nil; } } void textshow(Text *t, uint q0, uint q1, int doselect) { int qe; int nl; uint q; if(t->what != Body) return; if(t->w!=nil && t->maxlines==0) colgrow(t->col, t->w, 1); if(doselect) textsetselect(t, q0, q1); qe = t->org+t->nchars; if(t->org<=q0 && (q0file->nc+t->ncache))) textscrdraw(t); else{ if(t->w->nopen[QWevent] > 0) nl = 3*t->maxlines/4; else nl = t->maxlines/4; q = textbacknl(t, q0, nl); /* avoid going backwards if trying to go forwards - long lines! */ if(!(q0>t->org && qorg)) textsetorigin(t, q, TRUE); while(q0 > t->org+t->nchars) textsetorigin(t, t->org+1, FALSE); } } static int region(int a, int b) { if(a < b) return -1; if(a == b) return 0; return 1; } void selrestore(Frame *f, Point pt0, uint p0, uint p1) { if(p1<=f->p0 || p0>=f->p1){ /* no overlap */ frdrawsel0(f, pt0, p0, p1, f->cols[BACK], f->cols[TEXT]); return; } if(p0>=f->p0 && p1<=f->p1){ /* entirely inside */ frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]); return; } /* they now are known to overlap */ /* before selection */ if(p0 < f->p0){ frdrawsel0(f, pt0, p0, f->p0, f->cols[BACK], f->cols[TEXT]); p0 = f->p0; pt0 = frptofchar(f, p0); } /* after selection */ if(p1 > f->p1){ frdrawsel0(f, frptofchar(f, f->p1), f->p1, p1, f->cols[BACK], f->cols[TEXT]); p1 = f->p1; } /* inside selection */ frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]); } void textsetselect(Text *t, uint q0, uint q1) { int p0, p1; /* t->p0 and t->p1 are always right; t->q0 and t->q1 may be off */ t->q0 = q0; t->q1 = q1; /* compute desired p0,p1 from q0,q1 */ p0 = q0-t->org; p1 = q1-t->org; if(p0 < 0) p0 = 0; if(p1 < 0) p1 = 0; if(p0 > t->nchars) p0 = t->nchars; if(p1 > t->nchars) p1 = t->nchars; if(p0==t->p0 && p1==t->p1) return; /* screen disagrees with desired selection */ if(t->p1<=p0 || p1<=t->p0 || p0==p1 || t->p1==t->p0){ /* no overlap or too easy to bother trying */ frdrawsel(t, frptofchar(t, t->p0), t->p0, t->p1, 0); frdrawsel(t, frptofchar(t, p0), p0, p1, 1); goto Return; } /* overlap; avoid unnecessary painting */ if(p0 < t->p0){ /* extend selection backwards */ frdrawsel(t, frptofchar(t, p0), p0, t->p0, 1); }else if(p0 > t->p0){ /* trim first part of selection */ frdrawsel(t, frptofchar(t, t->p0), t->p0, p0, 0); } if(p1 > t->p1){ /* extend selection forwards */ frdrawsel(t, frptofchar(t, t->p1), t->p1, p1, 1); }else if(p1 < t->p1){ /* trim last part of selection */ frdrawsel(t, frptofchar(t, p1), p1, t->p1, 0); } Return: t->p0 = p0; t->p1 = p1; } /* * Release the button in less than DELAY ms and it's considered a null selection * if the mouse hardly moved, regardless of whether it crossed a char boundary. */ enum { DELAY = 2, MINMOVE = 4, }; uint xselect(Frame *f, Mousectl *mc, Image *col, uint *p1p) /* when called, button is down */ { uint p0, p1, q, tmp; ulong msec; Point mp, pt0, pt1, qt; int reg, b; mp = mc->xy; b = mc->buttons; msec = mc->msec; /* remove tick */ if(f->p0 == f->p1) frtick(f, frptofchar(f, f->p0), 0); p0 = p1 = frcharofpt(f, mp); pt0 = frptofchar(f, p0); pt1 = frptofchar(f, p1); reg = 0; frtick(f, pt0, 1); do{ q = frcharofpt(f, mc->xy); if(p1 != q){ if(p0 == p1) frtick(f, pt0, 0); if(reg != region(q, p0)){ /* crossed starting point; reset */ if(reg > 0) selrestore(f, pt0, p0, p1); else if(reg < 0) selrestore(f, pt1, p1, p0); p1 = p0; pt1 = pt0; reg = region(q, p0); if(reg == 0) frdrawsel0(f, pt0, p0, p1, col, display->white); } qt = frptofchar(f, q); if(reg > 0){ if(q > p1) frdrawsel0(f, pt1, p1, q, col, display->white); else if(q < p1) selrestore(f, qt, q, p1); }else if(reg < 0){ if(q > p1) selrestore(f, pt1, p1, q); else frdrawsel0(f, qt, q, p1, col, display->white); } p1 = q; pt1 = qt; } if(p0 == p1) frtick(f, pt0, 1); flushimage(f->display, 1); readmouse(mc); }while(mc->buttons == b); if(mc->msec-msec < DELAY && p0!=p1 && abs(mp.x-mc->xy.x)xy.y) 0) selrestore(f, pt0, p0, p1); else if(reg < 0) selrestore(f, pt1, p1, p0); p1 = p0; } if(p1 < p0){ tmp = p0; p0 = p1; p1 = tmp; } pt0 = frptofchar(f, p0); if(p0 == p1) frtick(f, pt0, 0); selrestore(f, pt0, p0, p1); /* restore tick */ if(f->p0 == f->p1) frtick(f, frptofchar(f, f->p0), 1); flushimage(f->display, 1); *p1p = p1; return p0; } int textselect23(Text *t, uint *q0, uint *q1, Image *high, int mask) { uint p0, p1; int buts; p0 = xselect(t, mousectl, high, &p1); buts = mousectl->buttons; if((buts & mask) == 0){ *q0 = p0+t->org; *q1 = p1+t->org; } while(mousectl->buttons) readmouse(mousectl); return buts; } int textselect2(Text *t, uint *q0, uint *q1, Text **tp) { int buts; *tp = nil; buts = textselect23(t, q0, q1, but2col, 4); if(buts & 4) return 0; if(buts & 1){ /* pick up argument */ *tp = argtext; return 1; } return 1; } int textselect3(Text *t, uint *q0, uint *q1) { int h; h = (textselect23(t, q0, q1, but3col, 1|2) == 0); return h; } static Rune left1[] = { L'{', L'[', L'(', L'<', L'«', 0 }; static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 }; static Rune left2[] = { L'\n', 0 }; static Rune left3[] = { L'\'', L'"', L'`', 0 }; static Rune *left[] = { left1, left2, left3, nil }; static Rune *right[] = { right1, left2, left3, nil }; void textdoubleclick(Text *t, uint *q0, uint *q1) { int c, i; Rune *r, *l, *p; uint q; for(i=0; left[i]!=nil; i++){ q = *q0; l = left[i]; r = right[i]; /* try matching character to left, looking right */ if(q == 0) c = '\n'; else c = textreadc(t, q-1); p = runestrchr(l, c); if(p != nil){ if(textclickmatch(t, c, r[p-l], 1, &q)) *q1 = q-(c!='\n'); return; } /* try matching character to right, looking left */ if(q == t->file->nc) c = '\n'; else c = textreadc(t, q); p = runestrchr(r, c); if(p != nil){ if(textclickmatch(t, c, l[p-r], -1, &q)){ *q1 = *q0+(*q0file->nc && c=='\n'); *q0 = q; if(c!='\n' || q!=0 || textreadc(t, 0)=='\n') (*q0)++; } return; } } /* try filling out word to right */ while(*q1file->nc && isalnum(textreadc(t, *q1))) (*q1)++; /* try filling out word to left */ while(*q0>0 && isalnum(textreadc(t, *q0-1))) (*q0)--; } int textclickmatch(Text *t, int cl, int cr, int dir, uint *q) { Rune c; int nest; nest = 1; for(;;){ if(dir > 0){ if(*q == t->file->nc) break; c = textreadc(t, *q); (*q)++; }else{ if(*q == 0) break; (*q)--; c = textreadc(t, *q); } if(c == cr){ if(--nest==0) return 1; }else if(c == cl) nest++; } return cl=='\n' && nest==1; } uint textbacknl(Text *t, uint p, uint n) { int i, j; /* look for start of this line if n==0 */ if(n==0 && p>0 && textreadc(t, p-1)!='\n') n = 1; i = n; while(i-->0 && p>0){ --p; /* it's at a newline now; back over it */ if(p == 0) break; /* at 128 chars, call it a line anyway */ for(j=128; --j>0 && p>0; p--) if(textreadc(t, p-1)=='\n') break; } return p; } void textsetorigin(Text *t, uint org, int exact) { int i, a, fixup; Rune *r; uint n; if(org>0 && !exact){ /* org is an estimate of the char posn; find a newline */ /* don't try harder than 256 chars */ for(i=0; i<256 && orgfile->nc; i++){ if(textreadc(t, org) == '\n'){ org++; break; } org++; } } a = org-t->org; fixup = 0; if(a>=0 && anchars){ frdelete(t, 0, a); fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */ } else if(a<0 && -anchars){ n = t->org - org; r = runemalloc(n); bufread(t->file, org, r, n); frinsert(t, r, r+n, 0); free(r); }else frdelete(t, 0, t->nchars); t->org = org; textfill(t); textscrdraw(t); textsetselect(t, t->q0, t->q1); if(fixup && t->p1 > t->p0) frdrawsel(t, frptofchar(t, t->p1-1), t->p1-1, t->p1, 1); } void textreset(Text *t) { t->file->seq = 0; t->eq0 = ~0; /* do t->delete(0, t->nc, TRUE) without building backup stuff */ textsetselect(t, t->org, t->org); frdelete(t, 0, t->nchars); t->org = 0; t->q0 = 0; t->q1 = 0; filereset(t->file); bufreset(t->file); } #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" static Channel* ctimer; /* chan(Timer*)[100] */ static Timer *timer; static uint msec(void) { return nsec()/1000000; } void timerstop(Timer *t) { t->next = timer; timer = t; } void timercancel(Timer *t) { t->cancel = TRUE; } static void timerproc(void*) { int i, nt, na, dt, del; Timer **t, *x; uint old, new; threadsetname("timerproc"); rfork(RFFDG); t = nil; na = 0; nt = 0; old = msec(); for(;;){ sleep(1); /* will sleep minimum incr */ new = msec(); dt = new-old; old = new; if(dt < 0) /* timer wrapped; go around, losing a tick */ continue; for(i=0; idt -= dt; del = FALSE; if(x->cancel){ timerstop(x); del = TRUE; }else if(x->dt <= 0){ /* * avoid possible deadlock if client is * now sending on ctimer */ if(nbsendul(x->c, 0) > 0) del = TRUE; } if(del){ memmove(&t[i], &t[i+1], (nt-i-1)*sizeof t[0]); --nt; --i; } } if(nt == 0){ x = recvp(ctimer); gotit: if(nt == na){ na += 10; t = realloc(t, na*sizeof(Timer*)); if(t == nil) error("timer realloc failed"); } t[nt++] = x; old = msec(); } if(nbrecv(ctimer, &x) > 0) goto gotit; } } void timerinit(void) { ctimer = chancreate(sizeof(Timer*), 100); proccreate(timerproc, nil, STACK); } Timer* timerstart(int dt) { Timer *t; t = timer; if(t) timer = timer->next; else{ t = emalloc(sizeof(Timer)); t->c = chancreate(sizeof(int), 0); } t->next = nil; t->dt = dt; t->cancel = FALSE; sendp(ctimer, t); return t; } #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" static Point prevmouse; static Window *mousew; void cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls) { uchar *q; Rune *s; int j, w; /* * Always guaranteed that n bytes may be interpreted * without worrying about partial runes. This may mean * reading up to UTFmax-1 more bytes than n; the caller * knows this. If n is a firm limit, the caller should * set p[n] = 0. */ q = (uchar*)p; s = r; for(j=0; jfilemenu = FALSE; winsetname(w, r, n); } free(r); for(i=nincl; --i>=0; ){ n = runestrlen(incl[i]); r = runemalloc(n); runemove(r, incl[i], n); winaddincl(w, r, n); } w->autoindent = globalautoindent; return w; } /* make new window, if necessary; return with it locked */ Window* errorwin(Mntdir *md, int owner) { Window *w; for(;;){ if(md == nil) w = errorwin1(nil, 0, nil, 0); else w = errorwin1(md->dir, md->ndir, md->incl, md->nincl); winlock(w, owner); if(w->col != nil) break; /* window was deleted too fast */ winunlock(w); } return w; } typedef struct Warning Warning; struct Warning{ Mntdir *md; Buffer buf; Warning *next; }; static Warning *warnings; static void addwarningtext(Mntdir *md, Rune *r, int nr) { Warning *warn; for(warn = warnings; warn; warn=warn->next){ if(warn->md == md){ bufinsert(&warn->buf, warn->buf.nc, r, nr); return; } } warn = emalloc(sizeof(Warning)); warn->next = warnings; warn->md = md; if(md) fsysincid(md); warnings = warn; bufinsert(&warn->buf, 0, r, nr); nbsendp(cwarn, 0); } /* called while row is locked */ void flushwarnings(void) { Warning *warn, *next; Window *w; Text *t; int owner, nr, q0, n; Rune *r; if(row.ncol == 0){ /* really early error */ rowinit(&row, screen->clipr); rowadd(&row, nil, -1); rowadd(&row, nil, -1); if(row.ncol == 0) error("initializing columns in flushwarnings()"); } for(warn=warnings; warn; warn=next) { w = errorwin(warn->md, 'E'); t = &w->body; owner = w->owner; if(owner == 0) w->owner = 'E'; wincommit(w, t); /* * Most commands don't generate much output. For instance, * Edit ,>cat goes through /dev/cons and is already in blocks * because of the i/o system, but a few can. Edit ,p will * put the entire result into a single hunk. So it's worth doing * this in blocks (and putting the text in a buffer in the first * place), to avoid a big memory footprint. */ r = fbufalloc(); q0 = t->file->nc; for(n = 0; n < warn->buf.nc; n += nr){ nr = warn->buf.nc - n; if(nr > RBUFSIZE) nr = RBUFSIZE; bufread(&warn->buf, n, r, nr); textbsinsert(t, t->file->nc, r, nr, TRUE, &nr); } textshow(t, q0, t->file->nc, 1); free(r); winsettag(t->w); textscrdraw(t); w->owner = owner; w->dirty = FALSE; winunlock(w); bufclose(&warn->buf); next = warn->next; if(warn->md) fsysdelid(warn->md); free(warn); } warnings = nil; } void warning(Mntdir *md, char *s, ...) { Rune *r; va_list arg; va_start(arg, s); r = runevsmprint(s, arg); va_end(arg); if(r == nil) error("runevsmprint failed"); addwarningtext(md, r, runestrlen(r)); } int runeeq(Rune *s1, uint n1, Rune *s2, uint n2) { if(n1 != n2) return FALSE; return memcmp(s1, s2, n1*sizeof(Rune)) == 0; } uint min(uint a, uint b) { if(a < b) return a; return b; } uint max(uint a, uint b) { if(a > b) return a; return b; } char* runetobyte(Rune *r, int n) { char *s; if(r == nil) return nil; s = emalloc(n*UTFmax+1); setmalloctag(s, getcallerpc(&r)); snprint(s, n*UTFmax+1, "%.*S", n, r); return s; } Rune* bytetorune(char *s, int *ip) { Rune *r; int nb, nr; nb = strlen(s); r = runemalloc(nb+1); cvttorunes(s, nb, r, &nb, &nr, nil); r[nr] = '\0'; *ip = nr; return r; } int isalnum(Rune c) { /* * Hard to get absolutely right. Use what we know about ASCII * and assume anything above the Latin control characters is * potentially an alphanumeric. */ if(c <= ' ') return FALSE; if(0x7F<=c && c<=0xA0) return FALSE; if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c)) return FALSE; return TRUE; } int rgetc(void *v, uint n) { return ((Rune*)v)[n]; } int tgetc(void *a, uint n) { Text *t; t = a; if(n >= t->file->nc) return 0; return textreadc(t, n); } Rune* skipbl(Rune *r, int n, int *np) { while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){ --n; r++; } *np = n; return r; } Rune* findbl(Rune *r, int n, int *np) { while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){ --n; r++; } *np = n; return r; } void savemouse(Window *w) { prevmouse = mouse->xy; mousew = w; } void restoremouse(Window *w) { if(mousew!=nil && mousew==w) moveto(mousectl, prevmouse); mousew = nil; } void clearmouse() { mousew = nil; } char* estrdup(char *s) { char *t; t = strdup(s); if(t == nil) error("strdup failed"); setmalloctag(t, getcallerpc(&s)); return t; } void* emalloc(uint n) { void *p; p = malloc(n); if(p == nil) error("malloc failed"); setmalloctag(p, getcallerpc(&n)); memset(p, 0, n); return p; } void* erealloc(void *p, uint n) { p = realloc(p, n); if(p == nil) error("realloc failed"); setmalloctag(p, getcallerpc(&n)); return p; } /* * Heuristic city. */ Window* makenewwindow(Text *t) { Column *c; Window *w, *bigw, *emptyw; Text *emptyb; int i, y, el; if(activecol) c = activecol; else if(seltext && seltext->col) c = seltext->col; else if(t && t->col) c = t->col; else{ if(row.ncol==0 && rowadd(&row, nil, -1)==nil) error("can't make column"); c = row.col[row.ncol-1]; } activecol = c; if(t==nil || t->w==nil || c->nw==0) return coladd(c, nil, nil, -1); /* find biggest window and biggest blank spot */ emptyw = c->w[0]; bigw = emptyw; for(i=1; inw; i++){ w = c->w[i]; /* use >= to choose one near bottom of screen */ if(w->body.maxlines >= bigw->body.maxlines) bigw = w; if(w->body.maxlines-w->body.nlines >= emptyw->body.maxlines-emptyw->body.nlines) emptyw = w; } emptyb = &emptyw->body; el = emptyb->maxlines-emptyb->nlines; /* if empty space is big, use it */ if(el>15 || (el>3 && el>(bigw->body.maxlines-1)/2)) y = emptyb->r.min.y+emptyb->nlines*font->height; else{ /* if this window is in column and isn't much smaller, split it */ if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3) bigw = t->w; y = (bigw->r.min.y + bigw->r.max.y)/2; } w = coladd(c, nil, nil, y); if(w->body.maxlines < 2) colgrow(w->col, w, 1); return w; } #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" int winid; void wininit(Window *w, Window *clone, Rectangle r) { Rectangle r1, br; File *f; Reffont *rf; Rune *rp; int nc; w->tag.w = w; w->body.w = w; w->id = ++winid; incref(w); if(globalincref) incref(w); w->ctlfid = ~0; w->utflastqid = -1; r1 = r; r1.max.y = r1.min.y + font->height; incref(&reffont); f = fileaddtext(nil, &w->tag); textinit(&w->tag, f, r1, &reffont, tagcols); w->tag.what = Tag; /* tag is a copy of the contents, not a tracked image */ if(clone){ textdelete(&w->tag, 0, w->tag.file->nc, TRUE); nc = clone->tag.file->nc; rp = runemalloc(nc); bufread(clone->tag.file, 0, rp, nc); textinsert(&w->tag, 0, rp, nc, TRUE); free(rp); filereset(w->tag.file); textsetselect(&w->tag, nc, nc); } r1 = r; r1.min.y += font->height + 1; if(r1.max.y < r1.min.y) r1.max.y = r1.min.y; f = nil; if(clone){ f = clone->body.file; w->body.org = clone->body.org; w->isscratch = clone->isscratch; rf = rfget(FALSE, FALSE, FALSE, clone->body.reffont->f->name); }else rf = rfget(FALSE, FALSE, FALSE, nil); f = fileaddtext(f, &w->body); w->body.what = Body; textinit(&w->body, f, r1, rf, textcols); r1.min.y -= 1; r1.max.y = r1.min.y+1; draw(screen, r1, tagcols[BORD], nil, ZP); textscrdraw(&w->body); w->r = r; w->r.max.y = w->body.r.max.y; br.min = w->tag.scrollr.min; br.max.x = br.min.x + Dx(button->r); br.max.y = br.min.y + Dy(button->r); draw(screen, br, button, nil, button->r.min); w->filemenu = TRUE; w->maxlines = w->body.maxlines; if(clone){ w->dirty = clone->dirty; textsetselect(&w->body, clone->body.q0, clone->body.q1); winsettag(w); } } int winresize(Window *w, Rectangle r, int safe) { Rectangle r1; int y; Image *b; Rectangle br; r1 = r; r1.max.y = r1.min.y + font->height; y = r1.max.y; if(!safe || !eqrect(w->tag.r, r1)){ y = textresize(&w->tag, r1); b = button; if(w->body.file->mod && !w->isdir && !w->isscratch) b = modbutton; br.min = w->tag.scrollr.min; br.max.x = br.min.x + Dx(b->r); br.max.y = br.min.y + Dy(b->r); draw(screen, br, b, nil, b->r.min); } if(!safe || !eqrect(w->body.r, r1)){ if(y+1+font->height > r.max.y){ /* no body */ r1.min.y = y; r1.max.y = y; textresize(&w->body, r1); w->r = r; w->r.max.y = y; return y; } r1 = r; r1.min.y = y; r1.max.y = y + 1; draw(screen, r1, tagcols[BORD], nil, ZP); r1.min.y = y + 1; r1.max.y = r.max.y; y = textresize(&w->body, r1); w->r = r; w->r.max.y = y; textscrdraw(&w->body); } w->maxlines = min(w->body.nlines, max(w->maxlines, w->body.maxlines)); return w->r.max.y; } void winlock1(Window *w, int owner) { incref(w); qlock(w); w->owner = owner; } void winlock(Window *w, int owner) { int i; File *f; f = w->body.file; for(i=0; intext; i++) winlock1(f->text[i]->w, owner); } void winunlock(Window *w) { int i; File *f; f = w->body.file; for(i=0; intext; i++){ w = f->text[i]->w; w->owner = 0; qunlock(w); winclose(w); /* winclose() can change up f->text; beware */ if(f->ntext>0 && w != f->text[i]->w) --i; /* winclose() deleted window */ } } void winmousebut(Window *w) { moveto(mousectl, divpt(addpt(w->tag.scrollr.min, w->tag.scrollr.max), 2)); } void windirfree(Window *w) { int i; Dirlist *dl; if(w->isdir){ for(i=0; indl; i++){ dl = w->dlp[i]; free(dl->r); free(dl); } free(w->dlp); } w->dlp = nil; w->ndl = 0; } void winclose(Window *w) { int i; if(decref(w) == 0){ windirfree(w); textclose(&w->tag); textclose(&w->body); if(activewin == w) activewin = nil; for(i=0; inincl; i++) free(w->incl[i]); free(w->incl); free(w->events); free(w); } } void windelete(Window *w) { Xfid *x; x = w->eventx; if(x){ w->nevents = 0; free(w->events); w->events = nil; w->eventx = nil; sendp(x->c, nil); /* wake him up */ } } void winundo(Window *w, int isundo) { Text *body; int i; File *f; Window *v; w->utflastqid = -1; body = &w->body; fileundo(body->file, isundo, &body->q0, &body->q1); textshow(body, body->q0, body->q1, 1); f = body->file; for(i=0; intext; i++){ v = f->text[i]->w; v->dirty = (f->seq != v->putseq); if(v != w){ v->body.q0 = v->body.p0+v->body.org; v->body.q1 = v->body.p1+v->body.org; } } winsettag(w); } void winsetname(Window *w, Rune *name, int n) { Text *t; Window *v; int i; t = &w->body; if(runeeq(t->file->name, t->file->nname, name, n) == TRUE) return; w->isscratch = FALSE; if(n>=6 && runeeq(L"/guide", 6, name+(n-6), 6)) w->isscratch = TRUE; else if(n>=7 && runeeq(L"+Errors", 7, name+(n-7), 7)) w->isscratch = TRUE; filesetname(t->file, name, n); for(i=0; ifile->ntext; i++){ v = t->file->text[i]->w; winsettag(v); v->isscratch = w->isscratch; } } void wintype(Window *w, Text *t, Rune r) { int i; texttype(t, r); if(t->what == Body) for(i=0; ifile->ntext; i++) textscrdraw(t->file->text[i]); winsettag(w); } void wincleartag(Window *w) { int i, n; Rune *r; /* w must be committed */ n = w->tag.file->nc; r = runemalloc(n); bufread(w->tag.file, 0, r, n); for(i=0; itag, i, n, TRUE); free(r); w->tag.file->mod = FALSE; if(w->tag.q0 > i) w->tag.q0 = i; if(w->tag.q1 > i) w->tag.q1 = i; textsetselect(&w->tag, w->tag.q0, w->tag.q1); } void winsettag1(Window *w) { int i, j, k, n, bar, dirty; Rune *new, *old, *r; Image *b; uint q0, q1; Rectangle br; /* there are races that get us here with stuff in the tag cache, so we take extra care to sync it */ if(w->tag.ncache!=0 || w->tag.file->mod) wincommit(w, &w->tag); /* check file name; also guarantees we can modify tag contents */ old = runemalloc(w->tag.file->nc+1); bufread(w->tag.file, 0, old, w->tag.file->nc); old[w->tag.file->nc] = '\0'; for(i=0; itag.file->nc; i++) if(old[i]==' ' || old[i]=='\t') break; if(runeeq(old, i, w->body.file->name, w->body.file->nname) == FALSE){ textdelete(&w->tag, 0, i, TRUE); textinsert(&w->tag, 0, w->body.file->name, w->body.file->nname, TRUE); free(old); old = runemalloc(w->tag.file->nc+1); bufread(w->tag.file, 0, old, w->tag.file->nc); old[w->tag.file->nc] = '\0'; } new = runemalloc(w->body.file->nname+100); i = 0; runemove(new+i, w->body.file->name, w->body.file->nname); i += w->body.file->nname; runemove(new+i, L" Del Snarf", 10); i += 10; if(w->filemenu){ if(w->body.file->delta.nc>0 || w->body.ncache){ runemove(new+i, L" Undo", 5); i += 5; } if(w->body.file->epsilon.nc > 0){ runemove(new+i, L" Redo", 5); i += 5; } dirty = w->body.file->nname && (w->body.ncache || w->body.file->seq!=w->putseq); if(!w->isdir && dirty){ runemove(new+i, L" Put", 4); i += 4; } } if(w->isdir){ runemove(new+i, L" Get", 4); i += 4; } runemove(new+i, L" |", 2); i += 2; r = runestrchr(old, '|'); if(r) k = r-old+1; else{ k = w->tag.file->nc; if(w->body.file->seq == 0){ runemove(new+i, L" Look ", 6); i += 6; } } if(runeeq(new, i, old, k) == FALSE){ n = k; if(n > i) n = i; for(j=0; jtag.q0; q1 = w->tag.q1; textdelete(&w->tag, j, k, TRUE); textinsert(&w->tag, j, new+j, i-j, TRUE); /* try to preserve user selection */ r = runestrchr(old, '|'); if(r){ bar = r-old; if(q0 > bar){ bar = (runestrchr(new, '|')-new)-bar; w->tag.q0 = q0+bar; w->tag.q1 = q1+bar; } } } free(old); free(new); w->tag.file->mod = FALSE; n = w->tag.file->nc+w->tag.ncache; if(w->tag.q0 > n) w->tag.q0 = n; if(w->tag.q1 > n) w->tag.q1 = n; textsetselect(&w->tag, w->tag.q0, w->tag.q1); b = button; if(!w->isdir && !w->isscratch && (w->body.file->mod || w->body.ncache)) b = modbutton; br.min = w->tag.scrollr.min; br.max.x = br.min.x + Dx(b->r); br.max.y = br.min.y + Dy(b->r); draw(screen, br, b, nil, b->r.min); } void winsettag(Window *w) { int i; File *f; Window *v; f = w->body.file; for(i=0; intext; i++){ v = f->text[i]->w; if(v->col->safe || v->body.maxlines>0) winsettag1(v); } } void wincommit(Window *w, Text *t) { Rune *r; int i; File *f; textcommit(t, TRUE); f = t->file; if(f->ntext > 1) for(i=0; intext; i++) textcommit(f->text[i], FALSE); /* no-op for t */ if(t->what == Body) return; r = runemalloc(w->tag.file->nc); bufread(w->tag.file, 0, r, w->tag.file->nc); for(i=0; itag.file->nc; i++) if(r[i]==' ' || r[i]=='\t') break; if(runeeq(r, i, w->body.file->name, w->body.file->nname) == FALSE){ seq++; filemark(w->body.file); w->body.file->mod = TRUE; w->dirty = TRUE; winsetname(w, r, i); winsettag(w); } free(r); } void winaddincl(Window *w, Rune *r, int n) { char *a; Dir *d; Runestr rs; a = runetobyte(r, n); d = dirstat(a); if(d == nil){ if(a[0] == '/') goto Rescue; rs = dirname(&w->body, r, n); r = rs.r; n = rs.nr; free(a); a = runetobyte(r, n); d = dirstat(a); if(d == nil) goto Rescue; r = runerealloc(r, n+1); r[n] = 0; } free(a); if((d->qid.type&QTDIR) == 0){ free(d); warning(nil, "%s: not a directory\n", a); free(r); return; } free(d); w->nincl++; w->incl = realloc(w->incl, w->nincl*sizeof(Rune*)); memmove(w->incl+1, w->incl, (w->nincl-1)*sizeof(Rune*)); w->incl[0] = runemalloc(n+1); runemove(w->incl[0], r, n); free(r); return; Rescue: warning(nil, "%s: %r\n", a); free(r); free(a); return; } int winclean(Window *w, int conservative) /* as it stands, conservative is always TRUE */ { if(w->isscratch || w->isdir) /* don't whine if it's a guide file, error window, etc. */ return TRUE; if(!conservative && w->nopen[QWevent]>0) return TRUE; if(w->dirty){ if(w->body.file->nname) warning(nil, "%.*S modified\n", w->body.file->nname, w->body.file->name); else{ if(w->body.file->nc < 100) /* don't whine if it's too small */ return TRUE; warning(nil, "unnamed file modified\n"); } w->dirty = FALSE; return FALSE; } return TRUE; } void winctlprint(Window *w, char *buf, int fonts) { int n; n = sprint(buf, "%11d %11d %11d %11d %11d ", w->id, w->tag.file->nc, w->body.file->nc, w->isdir, w->dirty); if(fonts) sprint(buf+n, "%11d %s" , Dx(w->body.r), w->body.reffont->f->name); } void winevent(Window *w, char *fmt, ...) { int n; char *b; Xfid *x; va_list arg; if(w->nopen[QWevent] == 0) return; if(w->owner == 0) error("no window owner"); va_start(arg, fmt); b = vsmprint(fmt, arg); va_end(arg); if(b == nil) error("vsmprint failed"); n = strlen(b); w->events = realloc(w->events, w->nevents+1+n); w->events[w->nevents++] = w->owner; memmove(w->events+w->nevents, b, n); free(b); w->nevents += n; x = w->eventx; if(x){ w->eventx = nil; sendp(x->c, nil); } } #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" enum { Ctlsize = 5*12 }; char Edel[] = "deleted window"; char Ebadctl[] = "ill-formed control message"; char Ebadaddr[] = "bad address syntax"; char Eaddr[] = "address out of range"; char Einuse[] = "already in use"; char Ebadevent[] = "bad event syntax"; extern char Eperm[]; static void clampaddr(Window *w) { if(w->addr.q0 < 0) w->addr.q0 = 0; if(w->addr.q1 < 0) w->addr.q1 = 0; if(w->addr.q0 > w->body.file->nc) w->addr.q0 = w->body.file->nc; if(w->addr.q1 > w->body.file->nc) w->addr.q1 = w->body.file->nc; } void xfidctl(void *arg) { Xfid *x; void (*f)(Xfid*); threadsetname("xfidctlthread"); x = arg; for(;;){ f = recvp(x->c); (*f)(x); flushimage(display, 1); sendp(cxfidfree, x); } } void xfidflush(Xfid *x) { Fcall fc; int i, j; Window *w; Column *c; Xfid *wx; /* search windows for matching tag */ qlock(&row); for(j=0; jnw; i++){ w = c->w[i]; winlock(w, 'E'); wx = w->eventx; if(wx!=nil && wx->tag==x->oldtag){ w->eventx = nil; wx->flushed = TRUE; sendp(wx->c, nil); winunlock(w); goto out; } winunlock(w); } } out: qunlock(&row); respond(x, &fc, nil); } void xfidopen(Xfid *x) { Fcall fc; Window *w; Text *t; char *s; Rune *r; int m, n, q, q0, q1; w = x->f->w; t = &w->body; if(w){ winlock(w, 'E'); q = FILE(x->f->qid); switch(q){ case QWaddr: if(w->nopen[q]++ == 0){ w->addr = (Range){0,0}; w->limit = (Range){-1,-1}; } break; case QWdata: w->nopen[q]++; break; case QWevent: if(w->nopen[q]++ == 0){ if(!w->isdir && w->col!=nil){ w->filemenu = FALSE; winsettag(w); } } break; case QWrdsel: /* * Use a temporary file. * A pipe would be the obvious, but we can't afford the * broken pipe notification. Using the code to read QWbody * is n², which should probably also be fixed. Even then, * though, we'd need to squirrel away the data in case it's * modified during the operation, e.g. by |sort */ if(w->rdselfd > 0){ winunlock(w); respond(x, &fc, Einuse); return; } w->rdselfd = tempfile(); if(w->rdselfd < 0){ winunlock(w); respond(x, &fc, "can't create temp file"); return; } w->nopen[q]++; q0 = t->q0; q1 = t->q1; r = fbufalloc(); s = fbufalloc(); while(q0 < q1){ n = q1 - q0; if(n > BUFSIZE/UTFmax) n = BUFSIZE/UTFmax; bufread(t->file, q0, r, n); m = snprint(s, BUFSIZE+1, "%.*S", n, r); if(write(w->rdselfd, s, m) != m){ warning(nil, "can't write temp file for pipe command %r\n"); break; } q0 += n; } fbuffree(s); fbuffree(r); break; case QWwrsel: w->nopen[q]++; seq++; filemark(t->file); cut(t, t, nil, FALSE, TRUE, nil, 0); w->wrselrange = (Range){t->q1, t->q1}; w->nomark = TRUE; break; case QWeditout: if(editing == FALSE){ winunlock(w); respond(x, &fc, Eperm); return; } w->wrselrange = (Range){t->q1, t->q1}; break; } winunlock(w); } fc.qid = x->f->qid; fc.iounit = messagesize-IOHDRSZ; x->f->open = TRUE; respond(x, &fc, nil); } void xfidclose(Xfid *x) { Fcall fc; Window *w; int q; Text *t; w = x->f->w; x->f->busy = FALSE; if(x->f->open == FALSE){ if(w != nil) winclose(w); respond(x, &fc, nil); return; } x->f->open = FALSE; if(w){ winlock(w, 'E'); q = FILE(x->f->qid); switch(q){ case QWctl: if(w->ctlfid!=~0 && w->ctlfid==x->f->fid){ w->ctlfid = ~0; qunlock(&w->ctllock); } break; case QWdata: w->nomark = FALSE; /* fall through */ case QWaddr: case QWevent: /* BUG: do we need to shut down Xfid? */ if(--w->nopen[q] == 0){ if(q == QWdata) w->nomark = FALSE; if(q==QWevent && !w->isdir && w->col!=nil){ w->filemenu = TRUE; winsettag(w); } if(q == QWevent){ free(w->dumpstr); free(w->dumpdir); w->dumpstr = nil; w->dumpdir = nil; } } break; case QWrdsel: close(w->rdselfd); w->rdselfd = 0; break; case QWwrsel: w->nomark = FALSE; t = &w->body; /* before: only did this if !w->noscroll, but that didn't seem right in practice */ textshow(t, min(w->wrselrange.q0, t->file->nc), min(w->wrselrange.q1, t->file->nc), 1); textscrdraw(t); break; } winunlock(w); winclose(w); } respond(x, &fc, nil); } void xfidread(Xfid *x) { Fcall fc; int n, q; uint off; char *b; char buf[128]; Window *w; q = FILE(x->f->qid); w = x->f->w; if(w == nil){ fc.count = 0; switch(q){ case Qcons: case Qlabel: break; case Qindex: xfidindexread(x); return; default: warning(nil, "unknown qid %d\n", q); break; } respond(x, &fc, nil); return; } winlock(w, 'F'); if(w->col == nil){ winunlock(w); respond(x, &fc, Edel); return; } off = x->offset; switch(q){ case QWaddr: textcommit(&w->body, TRUE); clampaddr(w); sprint(buf, "%11d %11d ", w->addr.q0, w->addr.q1); goto Readbuf; case QWbody: xfidutfread(x, &w->body, w->body.file->nc, QWbody); break; case QWctl: winctlprint(w, buf, 1); goto Readbuf; Readbuf: n = strlen(buf); if(off > n) off = n; if(off+x->count > n) x->count = n-off; fc.count = x->count; fc.data = buf+off; respond(x, &fc, nil); break; case QWevent: xfideventread(x, w); break; case QWdata: /* BUG: what should happen if q1 > q0? */ if(w->addr.q0 > w->body.file->nc){ respond(x, &fc, Eaddr); break; } w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->body.file->nc); w->addr.q1 = w->addr.q0; break; case QWtag: xfidutfread(x, &w->tag, w->tag.file->nc, QWtag); break; case QWrdsel: seek(w->rdselfd, off, 0); n = x->count; if(n > BUFSIZE) n = BUFSIZE; b = fbufalloc(); n = read(w->rdselfd, b, n); if(n < 0){ respond(x, &fc, "I/O error in temp file"); break; } fc.count = n; fc.data = b; respond(x, &fc, nil); fbuffree(b); break; default: sprint(buf, "unknown qid %d in read", q); respond(x, &fc, nil); } winunlock(w); } void xfidwrite(Xfid *x) { Fcall fc; int c, cnt, qid, q, nb, nr, eval; char buf[64], *err; Window *w; Rune *r; Range a; Text *t; uint q0, tq0, tq1; qid = FILE(x->f->qid); w = x->f->w; if(w){ c = 'F'; if(qid==QWtag || qid==QWbody) c = 'E'; winlock(w, c); if(w->col == nil){ winunlock(w); respond(x, &fc, Edel); return; } } x->data[x->count] = 0; switch(qid){ case Qcons: w = errorwin(x->f->mntdir, 'X'); t=&w->body; goto BodyTag; case Qlabel: fc.count = x->count; respond(x, &fc, nil); break; case QWaddr: x->data[x->count] = 0; r = bytetorune(x->data, &nr); t = &w->body; wincommit(w, t); eval = TRUE; a = address(x->f->mntdir, t, w->limit, w->addr, r, 0, nr, rgetc, &eval, (uint*)&nb); free(r); if(nb < nr){ respond(x, &fc, Ebadaddr); break; } if(!eval){ respond(x, &fc, Eaddr); break; } w->addr = a; fc.count = x->count; respond(x, &fc, nil); break; case Qeditout: case QWeditout: r = bytetorune(x->data, &nr); if(w) err = edittext(w, w->wrselrange.q1, r, nr); else err = edittext(nil, 0, r, nr); free(r); if(err != nil){ respond(x, &fc, err); break; } fc.count = x->count; respond(x, &fc, nil); break; case QWbody: case QWwrsel: t = &w->body; goto BodyTag; case QWctl: xfidctlwrite(x, w); break; case QWdata: a = w->addr; t = &w->body; wincommit(w, t); if(a.q0>t->file->nc || a.q1>t->file->nc){ respond(x, &fc, Eaddr); break; } r = runemalloc(x->count); cvttorunes(x->data, x->count, r, &nb, &nr, nil); if(w->nomark == FALSE){ seq++; filemark(t->file); } q0 = a.q0; if(a.q1 > q0){ textdelete(t, q0, a.q1, TRUE); w->addr.q1 = q0; } tq0 = t->q0; tq1 = t->q1; textinsert(t, q0, r, nr, TRUE); if(tq0 >= q0) tq0 += nr; if(tq1 >= q0) tq1 += nr; textsetselect(t, tq0, tq1); if(!t->w->noscroll) textshow(t, q0, q0+nr, 0); textscrdraw(t); winsettag(w); free(r); w->addr.q0 += nr; w->addr.q1 = w->addr.q0; fc.count = x->count; respond(x, &fc, nil); break; case QWevent: xfideventwrite(x, w); break; case QWtag: t = &w->tag; goto BodyTag; BodyTag: q = x->f->nrpart; cnt = x->count; if(q > 0){ memmove(x->data+q, x->data, cnt); /* there's room; see fsysproc */ memmove(x->data, x->f->rpart, q); cnt += q; x->f->nrpart = 0; } r = runemalloc(cnt); cvttorunes(x->data, cnt-UTFmax, r, &nb, &nr, nil); /* approach end of buffer */ while(fullrune(x->data+nb, cnt-nb)){ c = nb; nb += chartorune(&r[nr], x->data+c); if(r[nr]) nr++; } if(nb < cnt){ memmove(x->f->rpart, x->data+nb, cnt-nb); x->f->nrpart = cnt-nb; } if(nr > 0){ wincommit(w, t); if(qid == QWwrsel){ q0 = w->wrselrange.q1; if(q0 > t->file->nc) q0 = t->file->nc; }else q0 = t->file->nc; if(qid == QWtag) textinsert(t, q0, r, nr, TRUE); else{ if(w->nomark == FALSE){ seq++; filemark(t->file); } q0 = textbsinsert(t, q0, r, nr, TRUE, &nr); textsetselect(t, t->q0, t->q1); /* insert could leave it somewhere else */ if(qid!=QWwrsel && !t->w->noscroll) textshow(t, q0+nr, q0+nr, 1); textscrdraw(t); } winsettag(w); if(qid == QWwrsel) w->wrselrange.q1 += nr; free(r); } fc.count = x->count; respond(x, &fc, nil); break; default: sprint(buf, "unknown qid %d in write", qid); respond(x, &fc, buf); break; } if(w) winunlock(w); } void xfidctlwrite(Xfid *x, Window *w) { Fcall fc; int i, m, n, nb, nr, nulls; Rune *r; char *err, *p, *pp, *q, *e; int isfbuf, scrdraw, settag; Text *t; err = nil; e = x->data+x->count; scrdraw = FALSE; settag = FALSE; isfbuf = TRUE; if(x->count < RBUFSIZE) r = fbufalloc(); else{ isfbuf = FALSE; r = emalloc(x->count*UTFmax+1); } x->data[x->count] = 0; textcommit(&w->tag, TRUE); for(n=0; ncount; n+=m){ p = x->data+n; if(strncmp(p, "lock", 4) == 0){ /* make window exclusive use */ qlock(&w->ctllock); w->ctlfid = x->f->fid; m = 4; }else if(strncmp(p, "unlock", 6) == 0){ /* release exclusive use */ w->ctlfid = ~0; qunlock(&w->ctllock); m = 6; }else if(strncmp(p, "clean", 5) == 0){ /* mark window 'clean', seq=0 */ t = &w->body; t->eq0 = ~0; filereset(t->file); t->file->mod = FALSE; w->dirty = FALSE; settag = TRUE; m = 5; }else if(strncmp(p, "dirty", 5) == 0){ /* mark window 'dirty' */ t = &w->body; /* doesn't change sequence number, so "Put" won't appear. it shouldn't. */ t->file->mod = TRUE; w->dirty = TRUE; settag = TRUE; m = 5; }else if(strncmp(p, "show", 4) == 0){ /* show dot */ t = &w->body; textshow(t, t->q0, t->q1, 1); m = 4; }else if(strncmp(p, "name ", 5) == 0){ /* set file name */ pp = p+5; m = 5; q = memchr(pp, '\n', e-pp); if(q==nil || q==pp){ err = Ebadctl; break; } *q = 0; nulls = FALSE; cvttorunes(pp, q-pp, r, &nb, &nr, &nulls); if(nulls){ err = "nulls in file name"; break; } for(i=0; ibody.file); winsetname(w, r, nr); m += (q+1) - pp; }else if(strncmp(p, "dump ", 5) == 0){ /* set dump string */ pp = p+5; m = 5; q = memchr(pp, '\n', e-pp); if(q==nil || q==pp){ err = Ebadctl; break; } *q = 0; nulls = FALSE; cvttorunes(pp, q-pp, r, &nb, &nr, &nulls); if(nulls){ err = "nulls in dump string"; break; } w->dumpstr = runetobyte(r, nr); m += (q+1) - pp; }else if(strncmp(p, "dumpdir ", 8) == 0){ /* set dump directory */ pp = p+8; m = 8; q = memchr(pp, '\n', e-pp); if(q==nil || q==pp){ err = Ebadctl; break; } *q = 0; nulls = FALSE; cvttorunes(pp, q-pp, r, &nb, &nr, &nulls); if(nulls){ err = "nulls in dump directory string"; break; } w->dumpdir = runetobyte(r, nr); m += (q+1) - pp; }else if(strncmp(p, "delete", 6) == 0){ /* delete for sure */ colclose(w->col, w, TRUE); m = 6; }else if(strncmp(p, "del", 3) == 0){ /* delete, but check dirty */ if(!winclean(w, TRUE)){ err = "file dirty"; break; } colclose(w->col, w, TRUE); m = 3; }else if(strncmp(p, "get", 3) == 0){ /* get file */ get(&w->body, nil, nil, FALSE, XXX, nil, 0); m = 3; }else if(strncmp(p, "put", 3) == 0){ /* put file */ put(&w->body, nil, nil, XXX, XXX, nil, 0); m = 3; }else if(strncmp(p, "dot=addr", 8) == 0){ /* set dot */ textcommit(&w->body, TRUE); clampaddr(w); w->body.q0 = w->addr.q0; w->body.q1 = w->addr.q1; textsetselect(&w->body, w->body.q0, w->body.q1); settag = TRUE; m = 8; }else if(strncmp(p, "addr=dot", 8) == 0){ /* set addr */ w->addr.q0 = w->body.q0; w->addr.q1 = w->body.q1; m = 8; }else if(strncmp(p, "limit=addr", 10) == 0){ /* set limit */ textcommit(&w->body, TRUE); clampaddr(w); w->limit.q0 = w->addr.q0; w->limit.q1 = w->addr.q1; m = 10; }else if(strncmp(p, "nomark", 6) == 0){ /* turn off automatic marking */ w->nomark = TRUE; m = 6; }else if(strncmp(p, "mark", 4) == 0){ /* mark file */ seq++; filemark(w->body.file); settag = TRUE; m = 4; }else if(strncmp(p, "noscroll", 8) == 0){ /* turn off automatic scrolling */ w->noscroll = TRUE; m = 8; }else if(strncmp(p, "cleartag", 8) == 0){ /* wipe tag right of bar */ wincleartag(w); settag = TRUE; m = 8; }else if(strncmp(p, "scroll", 6) == 0){ /* turn on automatic scrolling (writes to body only) */ w->noscroll = FALSE; m = 6; }else{ err = Ebadctl; break; } while(p[m] == '\n') m++; } if(isfbuf) fbuffree(r); else free(r); if(err) n = 0; fc.count = n; respond(x, &fc, err); if(settag) winsettag(w); if(scrdraw) textscrdraw(&w->body); } void xfideventwrite(Xfid *x, Window *w) { Fcall fc; int m, n; Rune *r; char *err, *p, *q; int isfbuf; Text *t; int c; uint q0, q1; err = nil; isfbuf = TRUE; if(x->count < RBUFSIZE) r = fbufalloc(); else{ isfbuf = FALSE; r = emalloc(x->count*UTFmax+1); } for(n=0; ncount; n+=m){ p = x->data+n; w->owner = *p++; /* disgusting */ c = *p++; while(*p == ' ') p++; q0 = strtoul(p, &q, 10); if(q == p) goto Rescue; p = q; while(*p == ' ') p++; q1 = strtoul(p, &q, 10); if(q == p) goto Rescue; p = q; while(*p == ' ') p++; if(*p++ != '\n') goto Rescue; m = p-(x->data+n); if('a'<=c && c<='z') t = &w->tag; else if('A'<=c && c<='Z') t = &w->body; else goto Rescue; if(q0>t->file->nc || q1>t->file->nc || q0>q1) goto Rescue; qlock(&row); /* just like mousethread */ switch(c){ case 'x': case 'X': execute(t, q0, q1, TRUE, nil); break; case 'l': case 'L': look3(t, q0, q1, TRUE); break; default: qunlock(&row); goto Rescue; } qunlock(&row); } Out: if(isfbuf) fbuffree(r); else free(r); if(err) n = 0; fc.count = n; respond(x, &fc, err); return; Rescue: err = Ebadevent; goto Out; } void xfidutfread(Xfid *x, Text *t, uint q1, int qid) { Fcall fc; Window *w; Rune *r; char *b, *b1; uint q, off, boff; int m, n, nr, nb; w = t->w; wincommit(w, t); off = x->offset; r = fbufalloc(); b = fbufalloc(); b1 = fbufalloc(); n = 0; if(qid==w->utflastqid && off>=w->utflastboff && w->utflastq<=q1){ boff = w->utflastboff; q = w->utflastq; }else{ /* BUG: stupid code: scan from beginning */ boff = 0; q = 0; } w->utflastqid = qid; while(qcount){ /* * Updating here avoids partial rune problem: we're always on a * char boundary. The cost is we will usually do one more read * than we really need, but that's better than being n^2. */ w->utflastboff = boff; w->utflastq = q; nr = q1-q; if(nr > BUFSIZE/UTFmax) nr = BUFSIZE/UTFmax; bufread(t->file, q, r, nr); nb = snprint(b, BUFSIZE+1, "%.*S", nr, r); if(boff >= off){ m = nb; if(boff+m > off+x->count) m = off+x->count - boff; memmove(b1+n, b, m); n += m; }else if(boff+nb > off){ if(n != 0) error("bad count in utfrune"); m = nb - (off-boff); if(m > x->count) m = x->count; memmove(b1, b+(off-boff), m); n += m; } boff += nb; q += nr; } fbuffree(r); fbuffree(b); fc.count = n; fc.data = b1; respond(x, &fc, nil); fbuffree(b1); } int xfidruneread(Xfid *x, Text *t, uint q0, uint q1) { Fcall fc; Window *w; Rune *r, junk; char *b, *b1; uint q, boff; int i, rw, m, n, nr, nb; w = t->w; wincommit(w, t); r = fbufalloc(); b = fbufalloc(); b1 = fbufalloc(); n = 0; q = q0; boff = 0; while(qcount){ nr = q1-q; if(nr > BUFSIZE/UTFmax) nr = BUFSIZE/UTFmax; bufread(t->file, q, r, nr); nb = snprint(b, BUFSIZE+1, "%.*S", nr, r); m = nb; if(boff+m > x->count){ i = x->count - boff; /* copy whole runes only */ m = 0; nr = 0; while(m < i){ rw = chartorune(&junk, b+m); if(m+rw > i) break; m += rw; nr++; } if(m == 0) break; } memmove(b1+n, b, m); n += m; boff += nb; q += nr; } fbuffree(r); fbuffree(b); fc.count = n; fc.data = b1; respond(x, &fc, nil); fbuffree(b1); return q-q0; } void xfideventread(Xfid *x, Window *w) { Fcall fc; char *b; int i, n; i = 0; x->flushed = FALSE; while(w->nevents == 0){ if(i){ if(!x->flushed) respond(x, &fc, "window shut down"); return; } w->eventx = x; winunlock(w); recvp(x->c); winlock(w, 'F'); i++; } n = w->nevents; if(n > x->count) n = x->count; fc.count = n; fc.data = w->events; respond(x, &fc, nil); b = w->events; w->events = estrdup(w->events+n); free(b); w->nevents -= n; } void xfidindexread(Xfid *x) { Fcall fc; int i, j, m, n, nmax, isbuf, cnt, off; Window *w; char *b; Rune *r; Column *c; qlock(&row); nmax = 0; for(j=0; jnw; i++){ w = c->w[i]; nmax += Ctlsize + w->tag.file->nc*UTFmax + 1; } } nmax++; isbuf = (nmax<=RBUFSIZE); if(isbuf) b = (char*)x->buf; else b = emalloc(nmax); r = fbufalloc(); n = 0; for(j=0; jnw; i++){ w = c->w[i]; /* only show the currently active window of a set */ if(w->body.file->curtext != &w->body) continue; winctlprint(w, b+n, 0); n += Ctlsize; m = min(RBUFSIZE, w->tag.file->nc); bufread(w->tag.file, 0, r, m); m = n + snprint(b+n, nmax-n-1, "%.*S", m, r); while(noffset; cnt = x->count; if(off > n) off = n; if(off+cnt > n) cnt = n-off; fc.count = cnt; memmove(r, b+off, cnt); fc.data = (char*)r; if(!isbuf) free(b); respond(x, &fc, nil); fbuffree(r); } #!/bin/rc echo new -font /usr/matt/ink/AdventureSubtitles/AdventureSubtitles.14.font -bg 0xffff00ff -bord 0x777777ff -high 0x0000ffff -htext 0xffffffff -text 0xFF0000ff > /dev/wctl #include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" /* * WASHINGTON (AP) - The Food and Drug Administration warned * consumers Wednesday not to use ``Rio'' hair relaxer products * because they may cause severe hair loss or turn hair green.... * The FDA urged consumers who have experienced problems with Rio * to notify their local FDA office, local health department or the * company at 1‑800‑543‑3002. */ void resize(void); void move(void); void delete(void); void hide(void); void unhide(int); void newtile(int); Image *sweep(void); Image *bandsize(Window*); Image* drag(Window*, Rectangle*); void refresh(Rectangle); void resized(void); Channel *exitchan; /* chan(int) */ Channel *winclosechan; /* chan(Window*); */ Rectangle viewr; int threadrforkflag = 0; /* should be RFENVG but that hides rio from plumber */ void mousethread(void*); void keyboardthread(void*); void winclosethread(void*); void deletethread(void*); void initcmd(void*); char *fontname; int mainpid; enum { New, Reshape, Move, Delete, Hide, Exit, }; enum { Cut, Paste, Snarf, Plumb, Send, Scroll, }; char *menu2str[] = { [Cut] "cut", [Paste] "paste", [Snarf] "snarf", [Plumb] "plumb", [Send] "send", [Scroll] "scroll", nil }; Menu menu2 = { menu2str }; int Hidden = Exit+1; char *menu3str[100] = { [New] "New", [Reshape] "Resize", [Move] "Move", [Delete] "Delete", [Hide] "Hide", [Exit] "Exit", nil }; Menu menu3 = { menu3str }; char *rcargv[] = { "rc", "-i", nil }; char *kbdargv[] = { "rc", "-c", nil, nil }; int errorshouldabort = 0; void derror(Display*, char *errorstr) { error(errorstr); } void usage(void) { fprint(2, "usage: rio [-f font] [-i initcmd] [-k kbdcmd] [-s]\n"); exits("usage"); } void threadmain(int argc, char *argv[]) { char *initstr, *kbdin, *s; static void *arg[1]; char buf[256]; Image *i; Rectangle r; if(strstr(argv[0], ".out") == nil){ menu3str[Exit] = nil; Hidden--; } initstr = nil; kbdin = nil; maxtab = 0; ARGBEGIN{ case 'f': fontname = ARGF(); if(fontname == nil) usage(); break; case 'i': initstr = ARGF(); if(initstr == nil) usage(); break; case 'k': if(kbdin != nil) usage(); kbdin = ARGF(); if(kbdin == nil) usage(); break; case 's': scrolling = TRUE; break; }ARGEND mainpid = getpid(); if(getwd(buf, sizeof buf) == nil) startdir = estrdup("."); else startdir = estrdup(buf); if(fontname == nil) fontname = getenv("font"); if(fontname == nil) fontname = "/lib/font/bit/lucm/unicode.9.font"; s = getenv("tabstop"); if(s != nil) maxtab = strtol(s, nil, 0); if(maxtab == 0) maxtab = 4; free(s); /* check font before barging ahead */ if(access(fontname, 0) < 0){ fprint(2, "rio: can't access %s: %r\n", fontname); exits("font open"); } putenv("font", fontname); snarffd = open("/dev/snarf", OREAD|OCEXEC); if(geninitdraw(nil, derror, nil, "rio", nil, Refnone) < 0){ fprint(2, "rio: can't open display: %r\n"); exits("display open"); } iconinit(); view = screen; viewr = view->r; mousectl = initmouse(nil, screen); if(mousectl == nil) error("can't find mouse"); mouse = mousectl; keyboardctl = initkeyboard(nil); if(keyboardctl == nil) error("can't find keyboard"); wscreen = allocscreen(screen, background, 0); if(wscreen == nil) error("can't allocate screen"); draw(view, viewr, background, nil, ZP); flushimage(display, 1); exitchan = chancreate(sizeof(int), 0); winclosechan = chancreate(sizeof(Window*), 0); deletechan = chancreate(sizeof(char*), 0); timerinit(); threadcreate(keyboardthread, nil, STACK); threadcreate(mousethread, nil, STACK); threadcreate(winclosethread, nil, STACK); threadcreate(deletethread, nil, STACK); filsys = filsysinit(xfidinit()); if(filsys == nil) fprint(2, "rio: can't create file system server: %r\n"); else{ errorshouldabort = 1; /* suicide if there's trouble after this */ if(initstr) proccreate(initcmd, initstr, STACK); if(kbdin){ kbdargv[2] = kbdin; r = screen->r; r.max.x = r.min.x+300; r.max.y = r.min.y+80; i = allocwindow(wscreen, r, Refbackup, DWhite); wkeyboard = new(i, FALSE, scrolling, 0, nil, nil, Borderwidth, 0, 0, 0, 0, 0, "/bin/rc", kbdargv); if(wkeyboard == nil) error("can't create keyboard window"); } threadnotify(shutdown, 1); recv(exitchan, nil); } killprocs(); threadexitsall(nil); } /* * /dev/snarf updates when the file is closed, so we must open our own * fd here rather than use snarffd */ void putsnarf(void) { int fd, i, n; if(snarffd<0 || nsnarf==0) return; fd = open("/dev/snarf", OWRITE); if(fd < 0) return; /* snarf buffer could be huge, so fprint will truncate; do it in blocks */ for(i=0; i= 256) n = 256; if(fprint(fd, "%.*S", n, snarf+i) < 0) break; } close(fd); } void getsnarf(void) { int i, n, nb, nulls; char *sn, buf[1024]; if(snarffd < 0) return; sn = nil; i = 0; seek(snarffd, 0, 0); while((n = read(snarffd, buf, sizeof buf)) > 0){ sn = erealloc(sn, i+n+1); memmove(sn+i, buf, n); i += n; sn[i] = 0; } if(i > 0){ snarf = runerealloc(snarf, i+1); cvttorunes(sn, i, snarf, &nb, &nsnarf, &nulls); free(sn); } } void initcmd(void *arg) { char *cmd; cmd = arg; rfork(RFENVG|RFFDG|RFNOTEG|RFNAMEG); procexecl(nil, "/bin/rc", "rc", "-c", cmd, 0); fprint(2, "rio: exec failed: %r\n"); exits("exec"); } char *oknotes[] = { "delete", "hangup", "kill", "exit", nil }; int shutdown(void *, char *msg) { int i; killprocs(); for(i=0; oknotes[i]; i++) if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0) threadexitsall(msg); fprint(2, "rio %d: abort: %s\n", getpid(), msg); abort(); exits(msg); return 0; } void killprocs(void) { int i; for(i=0; ipid, "hangup"); } void keyboardthread(void*) { Rune buf[2][20], *rp; int n, i; threadsetname("keyboardthread"); n = 0; for(;;){ rp = buf[n]; n = 1-n; recv(keyboardctl->c, rp); for(i=1; ic, rp+i) <= 0) break; rp[i] = L'\0'; if(input != nil) sendp(input->ck, rp); } } /* * Used by /dev/kbdin */ void keyboardsend(char *s, int cnt) { Rune *r; int i, nb, nr; r = runemalloc(cnt); /* BUGlet: partial runes will be converted to error runes */ cvttorunes(s, cnt, r, &nb, &nr, nil); for(i=0; ic, &r[i]); free(r); } int portion(int x, int lo, int hi) { x -= lo; hi -= lo; if(x < 20) return 0; if(x > hi-20) return 2; return 1; } int whichcorner(Window *w, Point p) { int i, j; i = portion(p.x, w->screenr.min.x, w->screenr.max.x); j = portion(p.y, w->screenr.min.y, w->screenr.max.y); return 3*j+i; } void cornercursor(Window *w, Point p, int force) { if(w!=nil && winborder(w, p)) riosetcursor(corners[whichcorner(w, p)], force); else wsetcursor(w, force); } /* thread to allow fsysproc to synchronize window closing with main proc */ void winclosethread(void*) { Window *w; threadsetname("winclosethread"); for(;;){ w = recvp(winclosechan); wclose(w); } } /* thread to make Deleted windows that the client still holds disappear offscreen after an interval */ void deletethread(void*) { char *s; Image *i; threadsetname("deletethread"); for(;;){ s = recvp(deletechan); i = namedimage(display, s); if(i != nil){ /* move it off-screen to hide it, since client is slow in letting it go */ originwindow(i, i->r.min, view->r.max); } freeimage(i); free(s); } } void deletetimeoutproc(void *v) { char *s; s = v; sleep(750); /* remove window from screen after 3/4 of a second */ sendp(deletechan, s); } /* * Button 6 - keyboard toggle - has been pressed. * Send event to keyboard, wait for button up, send that. * Note: there is no coordinate translation done here; this * is just about getting button 6 to the keyboard simulator. */ void keyboardhide(void) { send(wkeyboard->mc.c, mouse); do readmouse(mousectl); while(mouse->buttons & (1<<5)); send(wkeyboard->mc.c, mouse); } void mousethread(void*) { int sending, inside, scrolling, moving, band; Window *oin, *w, *winput; Image *i; Rectangle r; Point xy; Mouse tmp; enum { MReshape, MMouse, NALT }; static Alt alts[NALT+1]; threadsetname("mousethread"); sending = FALSE; scrolling = FALSE; moving = FALSE; alts[MReshape].c = mousectl->resizec; alts[MReshape].v = nil; alts[MReshape].op = CHANRCV; alts[MMouse].c = mousectl->c; alts[MMouse].v = &mousectl->Mouse; alts[MMouse].op = CHANRCV; alts[NALT].op = CHANEND; for(;;) switch(alt(alts)){ case MReshape: resized(); break; case MMouse: if(wkeyboard!=nil && (mouse->buttons & (1<<5))){ keyboardhide(); break; } Again: winput = input; /* override everything for the keyboard window */ if(wkeyboard!=nil && ptinrect(mouse->xy, wkeyboard->screenr)){ /* make sure it's on top; this call is free if it is */ wtopme(wkeyboard); winput = wkeyboard; } if(winput!=nil && winput->i!=nil){ /* convert to logical coordinates */ xy.x = mouse->xy.x + (winput->i->r.min.x-winput->screenr.min.x); xy.y = mouse->xy.y + (winput->i->r.min.y-winput->screenr.min.y); inside = ptinrect(mouse->xy, insetrect(winput->screenr, winput->bordersize)); if(winput->mouseopen) scrolling = FALSE; else if(scrolling) scrolling = mouse->buttons; else scrolling = mouse->buttons && ptinrect(xy, winput->scrollr); /* topped will be zero if window has been bottomed */ if(sending == FALSE && !scrolling && winborder(winput, mouse->xy) && winput->topped>0){ moving = TRUE; }else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1))) sending = TRUE; }else sending = FALSE; if(sending){ if(mouse->buttons == 0){ cornercursor(winput, mouse->xy, 0); sending = FALSE; }else wsetcursor(winput, 0); tmp = mousectl->Mouse; tmp.xy = xy; send(winput->mc.c, &tmp); continue; } w = wpointto(mouse->xy); /* change cursor if over anyone's border */ if(w != nil) cornercursor(w, mouse->xy, 0); else riosetcursor(nil, 0); if(moving && (mouse->buttons&7)){ oin = winput; band = mouse->buttons & 3; sweeping = 1; if(band) i = bandsize(winput); else i = drag(winput, &r); sweeping = 0; if(i != nil){ if(winput == oin){ if(band) wsendctlmesg(winput, Reshaped, i->r, i); else wsendctlmesg(winput, Moved, r, i); cornercursor(winput, mouse->xy, 1); }else freeimage(i); } } if(w != nil) cornercursor(w, mouse->xy, 0); /* we're not sending the event, but if button is down maybe we should */ if(mouse->buttons){ /* w->topped will be zero if window has been bottomed */ if(w==nil || (w==winput && w->topped>0)){ if(mouse->buttons & 1){ ; }else if(mouse->buttons & 2){ if(winput && !winput->mouseopen) button2menu(winput); }else if(mouse->buttons & 4) button3menu(); }else{ /* if button 1 event in the window, top the window and wait for button up. */ /* otherwise, top the window and pass the event on */ if(wtop(mouse->xy) && (mouse->buttons!=1 || winborder(w, mouse->xy))) goto Again; goto Drain; } } moving = FALSE; break; Drain: do readmouse(mousectl); while(mousectl->buttons); moving = FALSE; goto Again; /* recalculate mouse position, cursor */ } } void resized(void) { Image *im; int i, j, ishidden; Rectangle r; Point o, n; Window *w; if(getwindow(display, Refnone) < 0) error("failed to re-attach window"); freescrtemps(); view = screen; freescreen(wscreen); wscreen = allocscreen(screen, background, 0); if(wscreen == nil) error("can't re-allocate screen"); draw(view, view->r, background, nil, ZP); o = subpt(viewr.max, viewr.min); n = subpt(view->clipr.max, view->clipr.min); for(i=0; ideleted) continue; r = rectsubpt(w->i->r, viewr.min); r.min.x = (r.min.x*n.x)/o.x; r.min.y = (r.min.y*n.y)/o.y; r.max.x = (r.max.x*n.x)/o.x; r.max.y = (r.max.y*n.y)/o.y; r = rectaddpt(r, screen->clipr.min); ishidden = 0; for(j=0; jchan, 0, DWhite); else im = allocwindow(wscreen, r, Refbackup, DWhite); if(im) wsendctlmesg(w, Reshaped, r, im); } viewr = screen->r; flushimage(display, 1); } void set_from_env(int *target, char *key, int dfault) { char *env = nil; if(key) env = getenv(key); if(env) { *target = (atoi(env) << 8) | 0xff; free(env); } else { *target = dfault; } } void set_defaults(int *borderwidth, int *bg, int *text, int *htext, int *bord, int *high) { set_from_env(borderwidth, "BORDERWIDTH", Borderwidth); set_from_env(bg, "BACK", 0xFFFFFF); set_from_env(text, "TEXT", 0x000000); set_from_env(htext, "HTEXT", 0xFFFFF); set_from_env(bord, "BORD", 0x777777); set_from_env(high, "HIGH", 0x0000FF); } void button3menu(void) { int i; int borderwidth, bg, text, htext, bord, high; for(i=0; ilabel; menu3str[i+Hidden] = nil; sweeping = 1; switch(i = menuhit(3, mousectl, &menu3, wscreen)){ case -1: break; case New: set_defaults(&borderwidth, &bg, &text, &htext, &bord, &high); new(sweep(), FALSE, scrolling, 0, nil, nil, borderwidth, bg, text, htext, bord, high, "/bin/rc", nil); break; case Reshape: resize(); break; case Move: move(); break; case Delete: delete(); break; case Hide: hide(); break; case Exit: if(Hidden > Exit){ send(exitchan, nil); break; } /* else fall through */ default: unhide(i); break; } sweeping = 0; } void button2menu(Window *w) { if(w->deleted) return; incref(w); if(w->scrolling) menu2str[Scroll] = "noscroll"; else menu2str[Scroll] = "scroll"; switch(menuhit(2, mousectl, &menu2, wscreen)){ case Cut: wsnarf(w); wcut(w); wscrdraw(w); break; case Snarf: wsnarf(w); break; case Paste: getsnarf(); wpaste(w); wscrdraw(w); break; case Plumb: wplumb(w); break; case Send: getsnarf(); wsnarf(w); if(nsnarf == 0) break; if(w->rawing){ waddraw(w, snarf, nsnarf); if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004') waddraw(w, L"\n", 1); }else{ winsert(w, snarf, nsnarf, w->nr); if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004') winsert(w, L"\n", 1, w->nr); } wsetselect(w, w->nr, w->nr); wshow(w, w->nr); break; case Scroll: if(w->scrolling ^= 1) wshow(w, w->nr); break; } wclose(w); wsendctlmesg(w, Wakeup, ZR, nil); flushimage(display, 1); } Point onscreen(Point p) { p.x = max(screen->clipr.min.x, p.x); p.x = min(screen->clipr.max.x, p.x); p.y = max(screen->clipr.min.y, p.y); p.y = min(screen->clipr.max.y, p.y); return p; } Image* sweep(void) { Image *i, *oi; Rectangle r; Point p0, p; i = nil; menuing = TRUE; riosetcursor(&crosscursor, 1); while(mouse->buttons == 0) readmouse(mousectl); p0 = onscreen(mouse->xy); p = p0; r.min = p; r.max = p; oi = nil; while(mouse->buttons == 4){ readmouse(mousectl); if(mouse->buttons != 4 && mouse->buttons != 0) break; if(!eqpt(mouse->xy, p)){ p = onscreen(mouse->xy); r = canonrect(Rpt(p0, p)); if(Dx(r)>5 && Dy(r)>5){ i = allocwindow(wscreen, r, Refnone, 0xEEEEEEFF); /* grey */ freeimage(oi); if(i == nil) goto Rescue; oi = i; border(i, r, Borderwidth, red, ZP); flushimage(display, 1); } } } if(mouse->buttons != 0) goto Rescue; if(i==nil || Dx(i->r)<100 || Dy(i->r)<3*font->height) goto Rescue; oi = i; i = allocwindow(wscreen, oi->r, Refbackup, DWhite); freeimage(oi); if(i == nil) goto Rescue; border(i, r, Borderwidth, red, ZP); cornercursor(input, mouse->xy, 1); goto Return; Rescue: freeimage(i); i = nil; cornercursor(input, mouse->xy, 1); while(mouse->buttons) readmouse(mousectl); Return: moveto(mousectl, mouse->xy); /* force cursor update; ugly */ menuing = FALSE; return i; } /* * BUG: should interlock so applications don't change screen * while tmp[] holds backing store */ Image* drag(Window *w, Rectangle *rp) { Image *i, *ni; Point p, op, d, dm, om; Rectangle r; i = w->i; menuing = TRUE; om = mouse->xy; riosetcursor(&boxcursor, 1); dm = subpt(mouse->xy, w->screenr.min); d = subpt(i->r.max, i->r.min); op = subpt(mouse->xy, dm); drawgetrect(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 1); flushimage(display, 1); while(mouse->buttons == 4){ p = subpt(mouse->xy, dm); if(!eqpt(p, op)){ drawgetrect(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 0); drawgetrect(Rect(p.x, p.y, p.x+d.x, p.y+d.y), 1); flushimage(display, 1); op = p; } readmouse(mousectl); } r = Rect(op.x, op.y, op.x+d.x, op.y+d.y); drawgetrect(r, 0); cornercursor(w, mouse->xy, 1); moveto(mousectl, mouse->xy); /* force cursor update; ugly */ menuing = FALSE; flushimage(display, 1); if(mouse->buttons!=0 || (ni=allocwindow(wscreen, r, Refbackup, DWhite))==nil){ moveto(mousectl, om); while(mouse->buttons) readmouse(mousectl); *rp = Rect(0, 0, 0, 0); return nil; } draw(ni, ni->r, i, nil, i->r.min); *rp = r; return ni; } Rectangle whichrect(Rectangle r, Point p, int which) { switch(which){ case 0: /* top left */ r = Rect(p.x, p.y, r.max.x, r.max.y); break; case 2: /* top right */ r = Rect(r.min.x, p.y, p.x, r.max.y); break; case 6: /* bottom left */ r = Rect(p.x, r.min.y, r.max.x, p.y); break; case 8: /* bottom right */ r = Rect(r.min.x, r.min.y, p.x, p.y); break; case 1: /* top edge */ r = Rect(r.min.x, p.y, r.max.x, r.max.y); break; case 5: /* right edge */ r = Rect(r.min.x, r.min.y, p.x, r.max.y); break; case 7: /* bottom edge */ r = Rect(r.min.x, r.min.y, r.max.x, p.y); break; case 3: /* left edge */ r = Rect(p.x, r.min.y, r.max.x, r.max.y); break; } return canonrect(r); } Image* bandsize(Window *w) { Image *i; Rectangle r, or; Point p, startp; int which, but; p = mouse->xy; startp = p; which = whichcorner(w, p); r = whichrect(w->screenr, p, which); drawgetrect(r, 1); or = r; but = mouse->buttons; while(mouse->buttons == but){ p = onscreen(mouse->xy); r = whichrect(w->screenr, p, which); if(!eqrect(r, or) && goodrect(r)){ drawgetrect(or, 0); drawgetrect(r, 1); flushimage(display, 1); or = r; } readmouse(mousectl); } p = mouse->xy; drawgetrect(or, 0); flushimage(display, 1); wsetcursor(w, 1); if(mouse->buttons!=0 || Dx(or)<100 || Dy(or)<3*font->height){ while(mouse->buttons) readmouse(mousectl); return nil; } if(abs(p.x-startp.x)+abs(p.y-startp.y) <= 1) return nil; i = allocwindow(wscreen, or, Refbackup, DWhite); if(i == nil) return nil; border(i, r, w->bordersize, red, ZP); return i; } Window* pointto(int wait) { Window *w; menuing = TRUE; riosetcursor(&sightcursor, 1); while(mouse->buttons == 0) readmouse(mousectl); if(mouse->buttons == 4) w = wpointto(mouse->xy); else w = nil; if(wait) while(mouse->buttons){ if(mouse->buttons!=4 && w !=nil){ /* cancel */ cornercursor(input, mouse->xy, 0); w = nil; } readmouse(mousectl); } cornercursor(input, mouse->xy, 0); moveto(mousectl, mouse->xy); /* force cursor update; ugly */ menuing = FALSE; return w; } void delete(void) { Window *w; w = pointto(TRUE); if(w) wsendctlmesg(w, Deleted, ZR, nil); } void resize(void) { Window *w; Image *i; w = pointto(TRUE); if(w == nil) return; i = sweep(); if(i) wsendctlmesg(w, Reshaped, i->r, i); } void move(void) { Window *w; Image *i; Rectangle r; w = pointto(FALSE); if(w == nil) return; i = drag(w, &r); if(i) wsendctlmesg(w, Moved, r, i); cornercursor(input, mouse->xy, 1); } int whide(Window *w) { Image *i; int j; for(j=0; j