smacme/ 775 0 0 0 10453465761 110155ustar00rminnichsyssmacme/8.out 775 0 0 1513022 10453466022 11773ustar00rminnichsyso<tf M:D$$ÁD$ f$LD$0DŽ$8= u  u$Ht-$L h_ $Kh_ l_ $7l_Cߦ $[w  $Hw" $5w8  $D$<" $D$  $D$" $D$  $D$" $D$s! $D$X|  $D$=  $L$! = t =<"G$\D$g5$j/$  L$#p( u)$D$!5$$/ (  $s|!u#$D$4ۧ$.s! $ L$y!|9 $D$"$D$ D$ $!L$ e_$8(! $T$L$ex $D$貆$D$ D$~"$D$ D$R)$D$ D$a/$D$ D$C0$D$ D$%G$D$ $$5Dø$5 |$K|$0}$HD$0|$0uD$01҉$@;T$0}A $D$L$M$@u B$@;T$0| @D$4$Hu$D$>1;$H/$@$L $/L$$@ƒtA$L$$@u$D$4$$LD$:$@A낉ؙ;}ɉى$$LD$$@본$$@$HD$0|$0fD$0Y= ="= =!= xj@$LC-u CfD$"t*L$" $\$(\$\$($LÉ\$(u$HpD$"cFt[atGbt3$D$80٦$;*\$($Li!Z!Kݻ\$(Mtl_=l_t!Jt$H$L1˹L$( t0؃H$(\$($LD$0(Jt$H$L1뱃ftUlݻ\$(Mt$8eJt$H$L1ʉݻ\$(Mth_=h_Jt$H$L1$H8$< $D$D$L$ 8$4$@ $]D$$@$D$D$D$4D$D$0D$ D$,D$D$D$$$D$D$D$L$4D$L$,H$4$D$$D$D$(D$j$4$D$$@D$D$ M}$4ǀƁ $$4$o$4ȃ$AD$AD$ 8Ã=T!uL$$ $"L$1Ƀ<"t^L$ " $D$T$ " $L$$L$L$L$#L$uD$$$L$A<"u,$D$$D$d,ڜ1ËL$$ $'L$OXj;!GT!$D$S# t,$L$D$6D$6fD$HuԸ$$Xà =$ $D$ ! $L$L$ L$\\$|($D$!$D$D$:밃 ÃhL$\ $QX} K$F!D$$$]D$D$D$!D$ ($ $L$L$/XD$d}B$$W$ $L$L$WD$d} m$PD$$D$D$\D$(L$ $D$D$d$D$D$D$D$WD$d$WD$$D$D$`D$'L$ $ L$%W!} $豺D$`$AWD$\$5W$D$ D$hÃ$ ! $D$u$jL$"$L$TÃ$|!D$"D$T $ $ku幔 $L$"L$ JL$JL$ N\$T ƒtT$tt $T$t tYD$ |! $L$"L$@x $D$z  $蠷D$@띋=D$"=D$"=D$"=!D$$ T t tDL$$KD$L$$L$D$$0x $D$y닃Pɨ$" ( AHLPA\ `dpD$4tx u x$8X輻$Xx $D$xH $ tu럋L$4I $L$ u]L$4I $L$tQD$ $L$ t7L$ $L$w u D$4$m D$4$PD$4$P 5( |$$󥸔$,W $L$(L$L$,L$J ‰D$L9t & u$eWL$8t߃|$$tD$D|$$D$D!u9L$( $L$,L$ցƌ|$ML$8T$Lu L$( $L$,L$ցƌ|$\$8T$DL$Lu?$D$T$CL$L!uՋ$D$T$?FL$Lt!뗃|$${t$MD$$\$8L$Lǁf$L$T$LD$$$街L$L|$8tD$8$wL$L 4  X t !t9t|$8D$8$!֋D$$tV$L$@L$L$ $AxD$A|D$D$ "w$$D$D$菘$@t)$@$CD$$@D$Ӳx $D$2n$@ C$$@$DŽ$$ $A$ $$$I $$IIL$$L$$L$ uuS $$ L$L$T} $D$$D$űDŽ$$@$$R$u $$D$$D$q$$ $K$D$$D$0$$ x $D$l$"L$K$}" D$D$ $1T$tuD$D$D$щT$|$t&D$D$"$D$D$령( $襳D$ $D$OL$D$L$!$D$D$ D$`댃[$_"$n$D$$"$D$D$ƒ l$0T$$1ۃu,h_l$0uH1;l!#,$ډ\$`  I L$l$0\$` |$(ts\$$<\$T$$<t$K\$T$$D$0;h_t*h_$j L$0 $۱\$L$$h_|$,tGC@\$$蹿$L$A8!  $萿T\$\$${D$ C;l! x  $l$,ND$u#$kD$D$0D$1 ù $cD$L$H `  $ l!L$觱\$‹l!l!` L$ $Ծ|$5l!-` uA19};B= (Ã(T$,9D$0v*$T$,|$8%$D$0D$zl$0\$8T$,B)Ɖt$B= B؉D$\$ But$@l$DD$81ۉ\$0tw|$8|L$8 ֢E{t?|$8}$ED$4t$@l$D4$l$L$8 ܢЃ<ÍD$ $\$L$DT$@t2D$ $D$|$t$ D$d&L$DT$@Y\$DtM9D$$v$Q4\$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 ֢B * Q8t$@l$D\$0‰ED$4.L$8 ֢*\$0t$@hBDu B!ի$r2T$D T$,D$ $ txt GË ! $ "ɃL$i "!H$D$(D$D$,D$LD$,"" !fA1É $D$$D$D$(D$T$ E1ø5Ã|$$u1ËL$ $L$$L$L$$L$Yg‰D$  $L$D$D$ D$T$D$8Ã|$(~T$0$L$(L$L$(L$eD$,fL$0 $.L$ȉ $|$t$D$$D$D$:ø$!R $D$D$o:D$$|$t$볃8D$,$D$<D$D$ $D$@@D$|$t$,D$L$L$L$|$| D$9D$w|$ t$ø\$݃})L$D$D$뷃~L$D$D$뤃,L$Dl$4t$@t$ t$Mt]|$<tVD$ $t$4$FxD$F|D$D$$(D$$$P AptʼnL$ $nt$l$tDž1ۋ9}ƀCۃD$ L$ $D$D$D$y$D$ $D$D$a$QÃL$$uÉ= v${&D$3ø$D$P \ t $薍L$$ɉ $S6\ $D$ D$D$$D$}jt$-\ \$$T$ Z t CBf fYZ -$!@!= u- $D$y[ $ !$D$ D$W  $`$ t1t!$&D$L$L$1L$ $bf$D$Ë@!($!  f9uÉЃ$!Ë$!  f9uÃ$!$!;\ s &$m*ÃD$ D$|$~ -tN0|<97Rƒ0|9~D$ D$ËD$ ЃЉD$ ɋD$D$ 룃 t tD$| 4L$ȃÃT$BtZtSB 9uFJ $ dL$4T$BJ$D$dD$gD$d $b3T$BBكl$\$E$ED$E)D$ggD$HÃD$$%l$\$E$ED$E)D$g\$CL$L$CÃD$ @$ىD$ $͉à $2L$ HD$ Hɉ $j2\$ CCL$Hf؃ÃD$ @$nD$ $bà $2 $$D$L$L$D$ÃL$ $Ma$aD$L$L$D$à $1$D$L$L$xD$Ã=$~( $$($艈=$؃=~( $X=؃=a~& aaa$=aڃà L$\ta|z~A|Z~ 0|9&$L$ à t&$L$gà \$@K9|C K $KɉL$0\$CKAL$fKAf Ã'~;D$u bà t\u0D$&$%L$ u)\D$$L$F뙃nu ;L$t܃|$ st\tЋD$$\D$L$뵃$D$ tR tMeƒ~L$ $T$T$tT$t tD$ËD$$T$D$ D$$D$ D$녃($D$$ D$D$D$ T$$D$~ uZD$$ D$l$ \$$|$}؃(ËCh.uCh@ uKAf@+D$$D$z=D$D$ D$ $@D$$$D$D$aD$3;D$uM\$$a\$ 1҉f<Тt( Т9uЃBf<ТuظÃ@1D$D$ D$1fD$$fD$&D$lu1@eu1@fD$(D$(curdL$( $Bƒ_D$( u‰Ǎt$Ѓ@É Т‰D$4@u |$BtJ $;T$4fD$$BxXD$8 `W $L$8 $8T$4D$su`$PD$D$$D$8D$sD$HT$4;D$8u$ 4T$4guT$4fD$&Bt T$4D$BtX u#D$D$L$4IfH~L$D $D$e'$R!SBtbD$t1`T$d\$hl$l9݉\$\B|Jx9v Bx99ʼnl$Xs8$l$D$‹D$\L$DЃHËL$$ $L$(ɃL$gT$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$$$biD$(à L$$ $D$L$趑t1$D$D$L$ 诀D$D$$o* Ã$D$(D$$襠t$(l$1ۋ9}v ȃv9$D$D$D$ $C9|$D$D$i$Ã$ÃT$totd|$ u~%$D$D$0Ë $D$-T$t뺃ÃL$ t$ÃT$ |$tË $7Ã$T$( $L$4L$D$u$ËD$($D$4D$D$ D$ ;}aL$ D$1ۋD$9}0D$\$D$(9u#CD$9|D$ D$ ;|$ÉT$$L$4L$\$;D$uD$$D$4D$\$륃$Ã8D$@$D$D$D$0D$ D$(D$|$Ht$<1|$0|$(|$(  $L$ 8D$D$ |$(~D$D/@|$L$ L$(Ƀ $5D$0D$0$D$D$D$ D$]BD$$9e\$D|$ ~L$ T$0J/urL$ T$0J$\$D$(D$BD$ D$(L$0 $L$(L$ D$4D$0$dL$4 $@L$4u $d1ɉȃ8Ã|$(~/{D$ D$ T$0Bf/bL$(Ƀ $< D$0D$0$D$DD$D$(D$dASD$$t$D$D$ ;c|$ D$.D$$dD$D$ d|$L1;\$(}T$0Z/u 1t$@D$0D$D|$(Cʃ$\$(T$,D$tuڃtN$D$D$T$,D$ `|$tD$,$$Ë $D$T$,L$L$ _z$D$,,D$6뒃$Ë9 D$Mt ,$L$\$(T$,DT$H|$Ttu7v$ $L$b'T$HuDËD$$D$L$L$PL$L$\L$L$`L$ D$oƒu$D$DËD$L~\T$@$üƒtJ1D$($6aT$@|$(t$#D$T$dDÉT$@$L$4L$\$LD$<1ҋ9}DT$8L$ $WD$ $\$LT$8B9|L$< $L$4L$ L$ L$ )D$L$D$D$@D$L$,L$ l$L|$,Eǀ11ۋE9},EވCE9|ԋD$@$_D$<$_D$$$B!L$Lǀ1ҋ9}eT$8$@D$L$ @D$BD$ $ѯL$LT$8B9|DËEǀDÃLD$PD$ L$\ $L$`L$D$8$ST$P|$8D$(t=L$\ $L$`L$L$L$ j|$8t$Pl$(<$L$L$ƙD$0}E$D$D$8D$D$($T^D$\$H^D$8$<^Lù $D$@ $D$\t$ \$Pl$$EE Ml$(M ǃƆǃ1틃9}F<E9|D$<$[D$@$[D$($y[D$\$m[D$8$a[D$0$蝖D$ $LǃƆǃ6M@H@M9u1IÌ[9u9u@M 9-MEE M t$^D$|$ $D$|$M8 tD$ E8D$0L$4t u0ËuꋉD$ $L$$ $$L$$HL$$,L$ |$tBBu#$D$Lܑ$F$y$$$(}|t >oD$$$D$L$ID$$$D$y$Oy$l$$$(wnC$D$x$D$x$t$$)$|tW$$|D$˃$d $|== 1҉$4D$$D$D$C$4  $'@‹$4D $$ D$$8T$$8$4$4D $$D$D$$4 $L$L$w$0$D$$D$$4D$w$0$w$x$d $TD$$x $AD$D$L$ $tU$P$D$$dD$$xD$ {G$$;$P$d$tP $p$D$D$D$ $dD$D$$p$D$$2ĔDŽ$<$d$N $$hT$0$h$@$N tR$N tE$N ` $$NL$a($h^DŽ$<$@d$<1$D1퉬$TDŽ$<$d$N $$h\$/$D$T$h$@$N $N $<uGDŽ$<,$L$:$D$h‰F$DՉ$T$@B,$L$9$DÉE$x$tX$p$D$$T\$$T  /.u A/tHD$$$wD$T$D$ D$p$D$$D$$TD$$TD$$$}D$D$BD$p$D$$D$$TD$DŽ$<|t <tr~VD$$$D$L$CD$$$D$4s$gs9$D$sPD$$$D$C묍D$$$&D$L$jC둸$D$rDŽ$\DŽ$(=!P!$(8 $Hr]$rM$D$Rr$r$$D$/r$D$IrX>* B$L$ D$AD$ $6L$ $F T$tB$6L$t"<"$L$S D$$sËAt| $D$ L$A$B6D$@ $36D$$'6릃 |$(u ù( $D$ $D$ $D$qT$L$$ L$(JL$,JL$0J L$4JL$8JL$‹L$L$H$T$ D$ à T$u $ǀ $T$L$#5l$\$(؃ Ãt$ \$1틃9ʼnl$9uou $Ë$D$)D$hT$;D$ u E9ʼnl$o$l$\$dL$9D$v$L$~% $ȃ$D$D$D$D$$D$ =L$ $D$D$D$ D$D$$D$ B|$$tD$ǀÃ(T$,L$0fD$dfD$D$D$4D$ D$8D$$ $D$D$D$D$ A(Ã\$T$ L$9w9vuʤ$\$T$ L$~% $ȃ$D$\$T$ @\$T$ L$ $\$T$4DD$ ;D$vD$ǀË9v냃4T$8L$@fD$$ifD$&D$(L$,D$D)ȉD$0 $L$DD$ T$@9sg)с v D$8$T$T$D$ D$L$L$ 3FL$< $D$D$ D$D$D$ Y@L$DT$D$9rD$ $1L$< $D$D$$D$D$ @4ÃL$~ $ȃ$D$fL$$g1L$ɉ $'T$$D$D$D$D$GT$L$ǂÃ(T$0L$,fD$ffD$D$D$ D$$t&$D$D$D$ %?T$0$D$D$D$D$ ?(ÃD$~ $L$ $L$L$L$ L$L$$L$ &DÃ,L$0Hu1,É $D$D$D$D$ 6DD$ ,ÃLL$P|$Tȃ$D$ ȃHD$D$$ $L$ D$8 $T$(T$D$D$H)D$(1;\$HL$H)ف v D$ $D$(؉\$4D$D$8D$L$,L$ pBD$P$D$DD$4D$D$8D$D$,D$ D$H)D$($L-T$P|$HuCǂD$ $D$(D$D$D$HD$ AAD$PL$HL$Hɉ $T$P뭋L$@$\$D$DD$D$DD$HD$ 2T$PL$>$D$DD$D$DD$HD$>L$P1ҋ9sFT$0$D$DD$D$DD$HD$D$ 蓕L$PT$0B9rD$XL$DD$\L$DuL$@D$@9ȉL$$ȃHD$ ȃ$D$D$$D$ $$@D$ H$@D$ ǀÃD$ $+L$ ǁǁ$u+L$ ǁǁ $@D$ $$@D$ H$@D$ $D$ $+ÃL$AHt ȃH$D$AHD$xD$$4 [ $$8I L$L$0L$ L$ L$L$0fD$,$4 $L$ L$D$$4D$0$y$40Á$$( $L$L$(L$d$ÃL$1҉˃DXD$ t!9uȃÃuAt7I$u߃tЃù4 $豾\$ L$ H$ÉŃD$TD$X!uĤDŽ$$iZDŽ$;t$h8;l$l.|$tt$l$u L$)ى$ɉ $9$$$D$L$dL$$D$ j"$$$(D$$D$$D$ $D$$D$L$L$D$dD$ D$d$ $;D$hu$;D$l|$tT$t$;$~$+$@‰щ$ɉ $:D$d$D$pD$D$tD$f$$;$~UD$tL$dfA:$$$D$L$tT$dJD$$+$D$ !$$e$$(D$$D$D$hD$ D$lD$$D$L$L$D$dD$ D$d$r j$$(D$$D$D$hD$ D$lD$$D$L$8륋L$l+L$h$ɉ $ش$$D$hD$L$dL$$D$ $ĤË$(D$L$t$ l$$D$\$$|$tL$p $L$tL$3$$$ioD$|$r"D$ $QT$9wыT$ J1ÃDL$HA= |'$(D$AD$AD$ +DD$D$ AuDD$4AD$,L$, $L$(L$*D$$D$0D$<D$@L$HI $1)L$<t$L$@L$D$450D$8$D$D$D$$$ D$4$ DÁT$菳$P$XI $6)L$$XƒuAD$$D$8("D$$$D$?)D$("D$ [$XT$8D$4T$8/t CT$8$pD$D$8$D$D$D$PD$D$HD$ D$DD$D$0D$4$ D$<$D$D$D$L$PD$L$DH$P$D$2D$D$D$xD$D$TD$l$ \$DT$H@L$x 9H9tAL$x $ى\$pAL$nfD$L$l X D$p@D$0$ȃD$ALD$xD$($D$8!@D$xD$8$D$0D$D$4D$D$(D$ D$,D$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 |$$mL$4i,  t$4D$4i,  D$$D$ D$$=D}s|$t;t$@vfD$ D$ =|$$lD$7D(؉` $!D$`D$|$  t$(͉l$,EÃ0\$0= ===tY=tK9u=D$$D$$=A $CD$D$|$t$(l$,Xh uD$ D$ =,$CD$D$|$t$(l$,D$0X\$0=5==tz=t4=w;MىMu$|$t$(l$,ULK $|$L$|$t$(l$,\$0 K $|$D$|$t$(l$,\$0D$<9L$< $t$F|$t$(l$,\$0 [C| Kʉp[C| Kʉp[ tPNt$(|$tW;t$@wO|$$L$< $IL$Ft$(ljD$|$|$$;|$딃=D~릃=D-1*D$<l$ =D| E;H~,1ۃ }!ڋ@DډHC |߃ËE;HuE;D|D $|$t$Lx @|D$D$ D$AD$ OT$H׃t$Lǂǂ|$4t$L8!@D$8D$@Ѓ $D$0D$mD$($D$D$D$ ND$$D$0$D$(D$|$t$4D$$D$D$.T$0ǂL$HǂǂD$@D$8D$@ $|$t$4x D$D$ D$AD$ CND$0$D$D$D$ D$?=L$0 $D$D$UbDÃTt$XD$0|$DEPD$HD$`;D$D}1ۋ9É\$,}"ڍD$0D$0@9D$`N;9}C\$,t$0|$DD$L+D$Dd}1TË $|$t$Dx @|D$D$ D$AD$ L|$4t$DL$` $L$LΉL$Q|D$$$gDŽ$$F| eaf&x$_$>Ѓ=$DŽ$у $ $$ $$$@D$tDŽ$t$$L$'cD$t$A $$$ $$$ $D$d$% $nD$`$1 $X$$$ƃ 9~(M ])θQ‹Mщʉl$pE9E 9$4,$D$D$T$ ȉL$<$h$ $$L$;$$ $_$@< $$L$aʼnD$|DŽ$1;$}!Z/u$Z [$u&D$<$l$$\$7rl$|$$;$}Z|D$<$sD$<$$AD$T$|ZD$$)HD$ D$$$"ED$4D$0$$xD$D$4D$D$0D$ `$ $AL$L$$}&D$|$葹$D$^i8 $8b$$$D$D$1ɉ$;$$ $7f u$u>D$|$$$貊$$۸$$$$D$>$A$;$d$$N$$wD$<$D$$D$D$ $$l$<ǀ1ۋ9}/ƀC9|щ,$Oy|$ttPD$<$D$D$D$ D$D$tD$$D$jD$t$臷D$|${\$T$<0 $NL$]D$4L$<L $L$4L$\L$<0D9v닃$T$|Z@+T$|Z@-D$<$D$D$D$ D$D$D$%TCCu<$L$0D$4L$p $D$L$4L$$L$ $o$B>$=$DŽ$$>$ $$L$$ $$L$$g$ $諃 $ʺ$$p$Љt$l|$P։T$h|$@\$X؉$D$@D$X+D$P2WD$H+D$@2FD$($D$PD$D$TD$D$HD$ D$LD$6 $|$t$(x @|D$D$ D$AD$ )D$l$|$t$PsD$h$|$t$@W$D$@D$H $|$t$@x D$D$ D$AD$ v($$9$ ,$D$\$M$$5 |$C$D$u $u"1D$$D$ 2$iك1;}]\$ 1ҋ9};L$T$$D$$D$D$ Ћ\$L$T$B9|C;|Ãl$4\$8|$t$ D$+D$ u|$t$Áv l$0 |$0vd$01D$ 9s)1)D$D$ 9D$}D$ ;D$,D$ D$|$t$ËD$D$ ߃4L $CL$$ $D$D$ L$  IL$s3 x  $|$t$$  I,L$D$L$?:L u \$BN4ÃhT$l9=L u .T$lցƌ|$XL D$4|$Ht$XD$HD$`+D$XD$PD$8$|$t$HBtD$BZJtȉD$D$<$t$8t$lƜ|$3T$l:ׁǜt$8D$4$|$t$HBD$D$ D$AD$ $D$4$|$t$8D$l@D$D$ D$AD$ $D$@HD$8D$4$|$t$8D$l@D$D$ D$AD$ =$D$($D$D$LD$ 1D$l@ $|$t$XD$4D$D$D$(D$D$,D$ #hÃhÃL$ $J ( D$ @aaaAa bbba ${tuD$ $\HËD$ $8HÃPD$8$t$Tƌ|$D$0D$D+D$ D$$迥D$$D$L$$L$pL$Ap$6L$4 9u 4 T 9u T X 9u X  9u  !9u ! Ã$D$(D$,\$ K $D$HL$JL$ $L$ L$ɉL$>t$ËD$ @L$I)ȃ$Ã@\$D~@ËK $"L$xD$, L" $L$]Jd$,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$|$|$"D$D$ Do|$L$DD$($D$M,Ã4t$<\$8|$DtD$@)D$0|$0u4Ã|$D$t$D$@D$Dct$<\$8uƀǀl19l$$9t_ƀL$ $t$D$@D$D$ L$ $AxD$A|D$ D$$t$v/,$IL$D$L$ $1|$D$ L$,ÃD$1ҋ9}L$hT$PJ$D$D$ \$pD$h~1`L$hT$PJ$D$ D$\$pD$h$ЃD$yT$pSD$1ۋ9}Al$\$uNwC9|É $l$\$߃$D$,$D$rD$,~B1ҋ9}6L$T$$D$2L$T$B9|ʋD$,u$ËL$(I ɉ $\T$(B$D$L$ L$BD$ Qt$(l$ 1ۋF9s Z tZ t CF9r,$\$\$L$L$ uWP D$($L$(ǀƁ $D$ D$D$D$SD$($D$ $<$Ã(L$0 $L$4L$D$$$חD$ D$$/u9$AD$D$$D$D$0$0<D$$$$<(ÍD$$D$,D$D$0D$D$4D$ ;D$D$0D$D$4D$$$;L$0 $L$4L$D$$$ D$ UL$0 $L$4ɃL$‹D$4T$0fBD$$$y;L$ A%u5 $_;$,D$D$$D$D$0$6;(É $*;T$,@D $@L$,;T$,DD$DD$@D$L$4Ƀ $T$,DD$D$0D$D$4D$D$0$:(ÃL$|$utF$ID$D$D$ ?D$ƀ1Ëds $XD$¸øøà L$$D$($oD$D$AD$ D$D$D$DT$(\$$|$,tA $T$)щL$`I L$ HL$D ÉЃ Ã$L$(u$Ë,u $jD$0L$, $D$D$BƒD$u$:T$$T$($ $(‰D$ ʉAL$8\$($$((ȋ,$(ȉ$D$D$D$ D$'D$$8T$(L$ ( t ǂ $D$ $ËT$} ǂ} ǂ 9v  9v Ãd$.D$D$L$ $1 D$$ыx $D$ӗ $D$D$ 봁8$v1;\$  1ҋ9}jL$T$$L$ $ED$U\$ tB $<If9t_$\$ L$T$$B9|C;p$v$<$D$(D$D$58ǃ ǂ$$D$ D$$땁X$\@D$@tsL$D $ED$`t$@l$D$\ R1҉\$,"   t],$b$\ |$T D$d@$D$HD$D$XÃ=$ u/,$$\$D$HD$(D$XËN|N|TCډ "DžDžDžDžډ ƅ,$dl$Dt-P $L$@ $L$D$D$ D$D$D$T$@l$DJ|J|ƅ ~/,$]$\$D$HD$8D$?X;kl$@\$D}/$$\$D$HD$rD$ XËL$, ExD$(E|D$$ $qD$8 $`L$$T$(D$<9)с ~ D$@$T$(T$D$8D$L$0L$ yFL$< $ L$L$L$0L$ L$8L$=L$D $L$tƅ,$ut$$l$(ƅ u\\ 9Dž\L$'pl$(tƅ  $D$  L$YD$L$( $L$ L$7D$D$ $D$D$D$D$D$ ŶD$ $zl$(#$=jl$(Dž$ R1҉ʉ$HukDŽ$t*tGt $D$T$$$$D$D$r É$K#ÉL$ $FD$$$\$u2$ $$$D$ ~D$É R$3    tND$ $D$t$9$$$D$D$}D$$oË 9v,$$D$ D$<뽉,$فL$L$ L$ L$D$L$p$L$ L$L$!‰$$$$9vӋE9v)ىME$$ $,$$D$D$e$D$ 9 $+,,$D$D$ D$ $D$LD$$L$D$ $ɰD$D$D$ >7D$ ,$\$G.ts 9v!,$$D$ D$f,$فL$L$L$ L$,$؃D$CD$D$ $D$L$D$ D$ye$@= $~ DŽ$  $UL$ $$D$$L$i$}!$$D$԰D$V$$$$$D$D$!$$)$ *R1ҋPT$XtyF$: 1 $L$S$T$X$u2$$$$D$ ~D$fËCK$ P  7htUD$`$D$l$v4$$$D$D$`D$|$Xt D$X$ËC$$$D$D$뿋K $$L$T$X$L$D$TD$$L$ gD$\D$T$%(T$\$t $$D$T$;A$ $$D$D$$D$D$TD$$L$ fD$\nI( $XL$4$D$XD$Hh,s~wKꉬ$ $CD$$t$O$A$0D$$D$%$$Ƌ$@,$ɉ $$@$$D$L$TL$$D$ $D$D$$R$  $$+$L$c$tL$$\$T S $V L$$$L$Tf|$LtD$DL$H $L$PL$%\$\$|dD$8L$<)@șD$D$L$L$' $|u%$L$D$5DL$@ $L$L$ uW$| $L$$|uD$D"$L$D$4L$@ $ıL$L$^ uO$|$D$D$D$ D$D$D$hXL$@ $ȱL$L$ uP$|$D$D$D$ D$D$D$L$@ $̱L$L$y $|$D$W$|$$|hl$hD$lD$nD$(>L$@ $ձL$L$ $|u"hlL$@ $ޱL$ L$ uU$|$D$h$|$($| zL$@ $L$L$ u$|ƀ ?L$@ $L$L$u-P $|$1D$(L$@ $L$L$u$|ƀL$@ $L$L$Hu$|ƀyL$@ $L$L$ u$|ƀ >L$@ $ L$L$u!$|$D$(L$@ $L$L$u$|ƀ șD$DD$0J IA $購D$HX$\D$8D$,B= v $r$\D$<1ۋB9É\$@sjJ ‹$`B ,BD$$ u B tT$4$L$0L$ L$D$ D$0;D$4ugD$8|$,tID$<$|$8tD$@D$@D$\$\$D$HD$D$8D$XËD$<$뵋T$0 u B tT$4$L$0L$ L$Z$`l$ T$$ËD$0;D$4HL$0 u A tA $ȋL$@)ȋ$\I)ȉD$Da| zAZ9L$(99݉\$$Q\$ T$L$(D$$LteXtlt[xt$ Ry $\$T$D$ D$莞$Q$\\$@D$D $\$T$D$ N‰,D$,J IA $$\D$<{X$`L$D $T$$\ RL$0 $蟹D$@ $莹D$< $}t$D$h$\D$8D$$l9up9D$011҉l;$ds G9D$$rZD$@$OD$<$CD$$D$\D$8D$`$\$D$HD$D$3D$8$XÉl$,pt$d)с ~ $`$T$4T$D$@D$L$ L$ #L$< $ L$L$L$ L$ L$@L$Jt$D$\T$4l$,\$0D$9rit$O9vw)L$8T$$ $D$D$|$Hс9GD$9|D$DD$D;|$1J$`t$<Rm9~9~)݉l$`D$ $L$$ D$l$D$ D$d|$4u D$$$V $`$D$LD$D$VD$ $+ \ÉT$($3D$D$uD$<<  $L$(I L$D$(@$D$D$ D$L$@L$ \$ \$ T$,B,9D$4uF$L$L$T$,f)BV$D$D$BPHD$kL$(D$0D$4A$ËC$L$0L$L$4L$\$ \$ T$,D$u B$p\D$d5L$\$ T$,C9t)$D$D$L$T$,D$@JDȉËD$0D$ C uB D$0@D$4؉$L$0L$L$4L$\$ \$ T$,D$0뮃l$ \$1EP9s# [UDыI}CEP9r݉Í[MDȋ@߃l$ \$CP9D$vC$D$Y4l$ \$CPKR9v$D$l$ \$CPJ;T$|(<*<CDǍ4RCDƹJ;T$}fkPÃl$ \$CP9D$sCP9rgC$D$3l$ \$ECP9s2)L$<<CDǍ4RCDƹBCP9rΉ+L$f)KP;l$}둃\$T$L$AP9sAP9rPA$ʉD$*3\$T$L$ $\$T$&D$$D$D$D$D$ 9}몃L$\$D$ 9}CP9sVCP9D$ sLD$ ;T$ }<RKDȋ@|"T$RKDȋ@$\$T$B;T$ |ăËC$ԉD$_2L$\$떃 T$L$fJRJD $JR IL$T$BDBDuB$߉D$ 2 ÃT$ D$$@JDȋ@}B$D$1T$ $D$$D$D$5T$$\$ RKDȋ@|o RSDыI $@D$L$ $L$L$3D$$D$$@L$ IDȋ@D$ӭD$$@L$ IDȋL$HÃT$ 1ۉ\$;\$$}=sCʉ\$;\$$|ЃÍL$ $T$ T$\$T$ ̓ T$B|HB;D$|?L$)JJ $JL$zL$ $L$IL$ L$ ËD$@$D$e0T$룃T$$B|vB;D$(|mJ $L$(L$ D$$蜬@D$D$$@$D$D$D$D$~T$$L$()JL$ $JL$< L$$ËD$ @$D$/T$$rD$$D$D$\$T$$[JDȉD$[JDȋ@+D$D$T$$D$@JDȃ D$D$D$ÃD$$@L$ IDȉD$H $莫D$ L$I ${D$ @D$D$ $D$$D$D$D$T$J $JL$D$D$$D$@D$=\$$T$J  JJD$ $@D$@D$'Ã|$t$(l$ \$$@ODȉGP9sJ}P9v)9t<$El$ D$)؉D$il$ ËJ} E 륋BB밃(L$8D$,P(+T$0A}A 9 (19‰T$$|A(D$QtdfD$D$=snD$ L$, $T$T$L$T$)D$$|$$}D$(ËD$ D$uD$,@$|D$*-1(ÍL$ $T$T$T$D$ yT$ l$\$J}B M()9~M MIKËL$ $T$ L$JL$L$ L$ \$T$uK IJËl$\$ T$C} C t  ËM MIJL$ $L$L$L$ L$L$$L$ T$$Ãt$l$$~(\$E}aE uXE 9~^ \$FTËF )1NTL$t$)Ӊ+D$M 9|9 +D$E D$Eà T$$B(D$L$0D$4H9}{L$IJDȉ$D$(D$L$L$Vl$$\$T$B| D$4H9,$D$(D$[MDȉD$L$T$$AD$4H9|BP9s]L$IJDȉ$D$(D$L$T$$$D$(D$D$@JDȉD$.L$T$$ABP9rfB\B,9D$,|fB\ ËBEB L$(ȋ ;D$,,$\$l$$\$L$4[MDȉL$ $D$uD$@$D$*L$ȃà T$$D$(@JDȉD$@}B$*D$)T$$L$A;D$,v É$L$,L$[D$D$(@L$$IDȉD$H $@D$D$$D$@D$D$D$ŦD$@$D$L$H Á$$$( ($D$($"($D$0DŽ$$u$fDŽ$@$;$}$DŽ$$$L$(L$L$0L$L$8L$ $L$$L$L$,L$.$$$$ $ftP$fD$6t3 $1(D$D$6D$L$ $ù$(ȍD$6$$D$R$럃|$,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$IÃL$ $D$L$L$L$ L$ ÃL$ $D$L$L$L$ ÃL$ $D$L$$L$L$ D$D$$D$D$D$ @D$LIÃPt$XD$T$t$D$\D$D$`D$ D$dD$D$hD$D$lD$D$pD$D$tD$ D$$D$(|$,D$< D$@AD$D D$HPÃPt$XD$T$t$D$\D$D$`D$ D$dD$D$hD$D$lD$D$pD$D$tD$ D$$D$(|$,D$< D$@AD$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$@AD$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$@AD$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$@AD$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$@AD$DD$xD$H/PÃPt$XD$T$t$D$\D$D$`D$ D$dD$D$hD$D$lD$D$pD$D$ D$tD$$D$xD$(|$,D$< D$@AD$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$@AD$DD$|D$HPÁ$<$@S}$@D$0$D!}$DD$,D$$$@u1$Dfu!$ $($,AÃ$Htպd$H9}$H$<$L$0L$L$,L$L$DL$ T$$L$L$4L$%$<$$$$$hD$.4$҃/$\t $$ $T$?!$$$0$\$<ƒu$}D$xOJGBGBGBNJFBFBFBM,IJ E,@B E,@B E,@B $(J $(B$(B$(BM $,JE $,BE $,BE $,B$LJ$LB$LB$LB$PJ$PB$PB$PB$TJ$TB$TB$TB $XJ!$XB"$XB#$XB$$4J%$4B&$4B'$4B($8J)$8B*$8B+$8B,$J-$B./K CBCBCB$`J$`B$`B$`B$dJ$dB $dB $dB $DLDƍD$D9s B9r苄$$($$`,$$$<$)$H|$4jD$$$m!$<I $L$4L$$<D$$0Kt!C59-1sE$DD$,$@D$0 ! utE !tȃËL$ˉL$ D$ 5T|$fD$ l$ \$ l$D$ ùx $ ! ƒt_T$$%L$T$tk$b|$fD$ l$ \$ l$L$ D$$ ! utd)d $!뮃,L$4 $D$}1,ÉD$$$xuD$$$1,Ƀ$ RL$$GL$A $觸\$tL$$ $D$D$\$jD$ D$$$MT$D$ ;D$t$1,ËL$0 $T$L$4L$3D$(D$$D$(,ÃD$@$HD$D$D$D$ D$ L$D$AD$ AÃL$uø$A$D$OD$T$J $T$؋T$B$lD$@ $]D$@$D$@$D$@$D$$ܸà T$B(tB($D${T$J $T$}$TD$ 1 ÃtD$xD$c$EzD$hD$$D$D$"zL$A$D$lL$I $L$+L$=L$Z1D$ptFk$mD$D$pD$|$p| D$lD$l ~${D$lD$+mt rvD$@$D$hD$QL$, $貾D$L$8 $袾D$ L$D $蒾D$L$P $肾D$$D$@$D$D$|$t$ , $L$ڵT$$D$uT$$$莪L$AL$$ $"L$T$BB}"L$$ $L$荪T$ B}$脶1 ËL$$ $脒D$ L$ $̴D$$D$$D$uL$ $/L$F$D$FL$ $"L$L$A D$$D$L$(H( $D$eL$A $L$HT$B$T$D$D$ ËD$$D$裑X$D$D$L$$ $"L$HT$B\L$du#D$`@ $D$D$D$q\ËD$D$D$D$AD$AD$AD$AD$D$$ȃD$@D$賑D$`@ $D$D$HD$mL$uø$AD$)D$D$@ $D$@$sD$@$D$$D$$Ã8D$$ǀĈË$ @,t ĈøQ$臲7$xT$8E$D$8u@$D$起$$ǀĈÍD$?$D$藅T$8D$$D$dD$$@4D$ $ $L$L$ L$`"L$ $L$:ĜÁpP$YD$TR$D$>$tuo$xu t$x$t$衄g&$x $艄g$x $ D$Xu 1pÍ$$D$$tD$$ $"L$ƒ}m $$tL$L$B}-D$X$٧$$D$\1pÍ$ $"L$ƒ|T$H$L$_L$L$ }D$H$=uƄ D$WL$P}D$WL$_ $kD$0$$D$$tD$D$0D$ 蘲$ $"L$D$LhL$_ $ D$0$$чD$$tD$D$0D$ 8$ $ L$D$D}D$L$ $L$5ƒD$@uD$D$D$8|$P3< $L$ƒu$D$@$멋L$@D$8@L$w $RT$8B,J, $L$8A($ $L$8A0$ $׬L$8A$ $L$8A $ $諬L$8A$ $蕬L$8A$ $L$8A$ $iL$8A$ $SL$8A $ $=T$@L$8A$L$WL$L $T$@ ǂ@=} $D$9T$8 $\$@T$8YL$_ $菫T$@BL$LJL$HJ L$DJ$|J,L$XJ4$t $躗T$@B0$L$$ $D$D$L$ L$Y'L$@ $|$t$$1L$L$L$*.L$@A|L$$ $D$D$L$ L$&L$@ $|$t$$1L$L$L$-T$@B|twJ|L$H $t$@Ńt Eit,tEt,$¢D$@pdžF([Vx뾋B0$芢D$@@|${D$@$iT$8Ry$1pÁ$uĜ;x u x B8D$$D$D$B4D$ 賭L$ $L$$$|Lу8 $}D$$$$8D$D$D$$$$B0$n$@4$\$@|$$5$$5$@$k$@ $Y$@$G$$g$$ĜÃ= tKL$ $u6D$$D$D$D$蟮$C빃ËD$$gD$ $Á$tA,uTD$$D$c$&D$$D$D$D$ $$ĘÉ $$D$A,$T$()ȃ $ËJ $L$D$ D$\$ T$(9tD=h!t&$4D$T$\$ L$oT$($Ë$ÃT$ u1Ã|$ttvtZI@A@A@A$Ãt$l$|P9Fȋ 9v,$t$l$}1ËøZ$1à L$( $D$ƒD$}$$D$D$(D$1 Ã|$$tD$$@L$$ $L$(L$T$|$$tT$$R1ɉL$ =D$|$$t D$$@t]|$u$;D$D$(D$gD$$L$$ $ND$D$$D$D$荞D$ ËD$$$@땹kD$$$T$*Tf1҉˃t6)ȃ$\$L\$D")ȉL$HD$y\$L+D$HÉ\$L$ݣD$iD$?tD$@iD$AoD$BnD$C D$D%D$EdD$F)D$GD$lD$X0 $ju1dÉD$`$D$0D$aD$`L$hHL$p $kxT$`BEBJ $ T$`B J I $T$`B$tB uI$dD$`@ $UD$`@$$FD$`@($7D$`$+1dËB$tL$X $L$XL$D$tL$`fAL$X $D$XL$X $L$XL$D$tL$`fA L$X $~L$`D$XABA 5fA fAA(A$$D$A@D$M`L$`A $D$AD$,`D$`@D$X0D$X9L$X $L$XL$D$sD$PL$X $D$XD$X04D$X9$L$X $L$XL$D$sD$LL$X $aT$PL$LD$XD$XtrV$ T$`$1dÁs9wՋD$XD$TL$X $L$TL$D$sD$HD$T;D$XjD$T tD$T >L$T $D$XL$`I( $L$`IL$T$`B(B(u fBI $T$`Ã-BJ(L$Pf L$LfKL$HKD$XD$TD$Xt D$X upD$XD$X\$\C L$T $tT$\BBu$T$`L$X $L$`D$XfAD$XȃdËD$X tD$X tD$XSD$T D$HD$$D$X+D$lD$'T$`D$$D$X+D$lD$T$`4$T$`T$u1B9}GB(\$L$A $D$@$D$$\$T$CB9|1ۋB9}/\$[J$ȋHtx 9u^CB9|ыB,$D$$D$@ $D$@$$|D$@($mD$$aÉ $\$T$됃@T$X}jD$H$|$t$HT$ZL$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Ѓ@ù< $oƒD$4uL$H $L$\$ ƒfXBBBD$H$D$g|$$t$L+DŽ$D$($ $L=D$ |Y^$$t4^$$D$*|$(t D$($|p1ø^$Ջ$$D$$ $L$ L$l$ $ƒlA$Y$nXBB؉\$$BBЃ$$D$l$L$ $D$$I $L$/L$L$D$ D$v=Ƅ$< $mD$(ud$ $L$\$$ƒ}fXBBB$$D$@$L$$HL$G $Lƒu_$D$GD$T_D$(P,$L$(A(L$S $uL$(A0L$_ $uL$(AL$k $uL$(A L$w $uL$(A$ $}uL$(A$ $guL$(A$ $QuL$(A$ $;uL$(A $ $%uT$(B$B4B8ЁÃL$$ $IL$ $D$L$l$\$ ƒu1NKHCBCBCBL$(JBЃ$D$$D$l$ZJL$ $D$}1øÃL$  L$ $D$L$ $L$3l$T$ Ãu fJHBCBCBCB4t9u:J8,$J4t"L$'} 11߃tʋA89u ȋJ8H8빋I81ÃL$ $D$ D$$lD$ Ã$D$(D$,9L$t$v1$ù $5jƒD$u1$1ɃL$L$ $L$t$(l$,ƒuD$$xk1$< < AZBB؉\$BNJFBFBFBMJ EB EB EB L$0J L$ $D$L$T$tEL$ L$JL$(JBt B@,uF$4sT$L$,J Ѓ$A $hD$u1ËL$ $ L$t$ l$$\$ƒu$7j1SވXމBBBBBBBL$ $D$\$|L$ L$ KCC ؃ÃL$u1ËD$$L$\$ƒu FKHCBCBCBL$ $L$} ËD$$iÃ$$L$(L$|$t$,L$pD$D$$9dD$$$-dD$D$$|$4~D$4|Ѹ;D$4~.J}L$$ $L$uD$$(1(ù $L$$L$T$T$oD$$L$T$uD$$$cD$(ËD$$D$? |$tL$ $LL$3WtD=X"t!L$ $ X"L$W t1 ËA 9D$uAȃ ËD$ ÃX"$bL$ $VX"D$ ËD$; uX" Ã|$  $`\$ƒu1ËL$fHL$ HL$$HL$(HL$,H D$@tG$UL$L$, $L$L$T$uD$$T$T$Ѓh($yjMhD$xD$(u+L$l $L$tL$L$|L$D$(u1hËL$t $L$?L$$L$ڠ$t&|$xu D$($G~$1hÍL$? $hD$8 @Ƀ $_D$4|$4u%|$xu D$($D$4$a1hËL$t $L$4L$L$8 IɃL$;\$8 [Ƀ9t@~$d똉 $$_tD$0$D$4D$D$8D$|$|t D$l$詿L$K $gD$ L$W $gD$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$V t 1ĸÍL${ $L$ L$<$u%$ $T$$L$ĸÉ$$L$1L$`V$1t 1ĸÃ;D$\11҃ }D{ $ t$T1ĸÃ\$pƄ$L${ $D$<u$D${D$ 1ĸÍ$ $eD$D$ $eD$H$ $meD$L$ $ZeD$PD$D;D$LD$H;D$PD$HD$XD$PD$TL$< $+D$,<$t$DL$,L$ $D$lf$t$ļ$$|$t$DL$D$uȥ$D$ D$}L$ $4-D$D$$D$D$sAD$ X!|$ @B~-$D$D$ D$X!D$ M/|$$tD$$D$D$ D$D$ÁD$$$D$$D$$ D$ I t5$D$L$D$D$ QMD$$Wø$D$D$D$ M@@ ÃL$} Ãu o@ ø($A ,toL$ $A\$T$t3A 9u$B D$$B($BD$ËI0u͉$lBD$u($JBà @PL$HЃ @@ÁD$ HL$A8t A8$=$$ $D$$D$GT$B8B8tD$ tĬD$D$($D$$D$D$D$ DIL$( $L$qx$|L$I8 $`@D$$$D$@8D$D$D$x$$`x_@@8@X{l Ã"$@x"1҃}BѸ!u*щT$  Éx""$@D$ B|"$@ÃL$ |)}$"$~?L$ !x"蝺@L$XÃT$ @@u)ȃ0$D$T$Q*Á$$(u)$($)D$ !L$-D$($>t$1l$,t@|[($k?|$u$($mC$1;T$}$T$D D$)D$gT$Bp uC 9u뀉El$L$ K ƃp ܃ , u $L$R ȉ ,  à $D$D$(D$P9ЉT$L$@0ą $DžL$D$u} |$$tD$$$D$ ùх $D$uL$A$}$D$t먹 $!L$tT$B(}%B$$u$D$Uta$D$;tT$BL$(JL$,J BGD$@($tT$J$ $у0L$L$x\$~1@0؃0$D$9tD$@$$BtC$$2tL$$t $D$@4D$0 ø$OAT$6$=t`D$$D$D$D$D$Á!#$uÍD$$D$D$PD$D$ RueD$$D$MQ$D$$$D$L$LL$FD$$D$QD$$sQË@t(D$$D$A D$A@ D$ P뀍D$$D$A D$P_$ƒt@uf1ҍD$$)ЉD$D$$(D$ BL$$ $2D$$D$$D$D$D$grV$ÍL$$ $L$J L$JI L$ cBo $9d"d"D$ $:D$ Ã$L$4 s$L$tx $L$T$4D$P$$D$T$B B $D$D$4D$ D$$D$,D$D$0D$T$8D$L$} 0ÉD$($L$L$L$B}D$($>0Ã|D L$ $ uȃ0ÃD$ PTT$Bu YT$BBBD$(B$D$$D$L$L$ Ã(D$,@TD$$T$'Bu T$BBB`D$4 $L$L$L$0L$D$$D$ +D$D$ $D$D$=$D$D$D$ D$=$D$D$=L$At]A$D$D$Z=D$@$D$D$D$ D$;=D$@$D$D$= (ør$3 T$ ! $L$L$uȃÃD$L$ $_D$ D$$D$ D$D$$D$L$뵃 ! $L$L$ru4|$ tuȃÉL$ $D$D$D$L$؃D$L$ $D$ D$$D$ D$D$$D$ L$뇃$ ! $L$(L$L$,L$L$0L$ L$4L$ưuȃ$ÃD$ L$( $:D$D$ $D$D$yD$ $D$L$ 뵃 L$t!$ȃD$q ÃT$$uD$ $1Ã|$ t D$ D$  ! $L$ L$T$t`ȃD$L$ $xD$D$$D$D$L$|$ u(L$ $ID$D$$D$D$L$ȃà ! $L$L$莳 ÃD$ D$d$ D$ $L$D$t$L$ $D$ D$$D$ D$D$ÃT$L$ tQÃÃT$L$ tQÃËL$AøËL$u1ÉȃÃ@c$"xh,!D$ D$|$у}uGdU˽_}TɉG^‰։ؙD$ȭ|$iۏiG )˃}|@cE_|ÃH $D$ $ɉ $Oft$>l$DL$(;L$$=sZCΈD$8D$8;D$4rM)U]D$  ,$L$+L$LL$)@fD$\$($L$L$8l$D\$(놉,$\$D$rft$>l$DÃt E D$$H@ø@ËE(D$LT$BB@D$B($L$L$L$L$ YÃ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$ A,Ã\$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$,1D$HD$L1$@ p$AAx$@ xL$,xD$P$@ dxXYbt ĠD$8D$,%uDD$Lu D$HD$HL$L؃D$HL$LD$<$ʼnD$T1t$0YD$HD$Lu M0$H(9} D$]9J|$<9D$,0)D$,t|$8D$,@tD$,D$,t$|$8uM$I |$8tl|$8te|$<t6M-$`,$ $AL$t$t$ #ĠËD$, tM+뼋D$,tM 먉M0듋$H$9}D$]9w$@$:M0F؃|$8 E0u d$,FFM0FL$ $L$HL$L$LL$T$8T$ T$苬L$T$L$4$D$HD$ D$D$ L$8L$L$it$0l$TD$,%tЃ)ЃtMl$TT$P\$4 Ft$0M,F؃1t$8щ1t$8NjD$,%tЃ)ЃtMT$P F볉M,F,#ؗD$<D$8D$PD$8 otx{D$8D$8uD$,%tUD$,%t!$BJAD$HAD$L$BJAD$HAD$LD$,%t:D$,%t$AAx$AAxD$,%tDD$,%t$AAxO$AAx4D$,%t@D$,%t$AAx$AAxD$,%t$AAx$AAx \$k,CCP%tCщ‰Q1 É%tK %tK ԉ%tKf K 빋L$A -=t5 t*#t+t,tÁI,I, I,I,߃I,كht0ltuuʁI,A,%tI,I,뤋A,%tI,I,뇃L$ D$%A D$D$%A( $D$D$D$D$ *1ø#@ø@ø'@u L$Aø@ø"@ø@ø3@ø2@ø@ø@ø@ø@ø.@ø@ø)@ø@ø@ø@ø@ø@ø@ÃL$ $L$ L$L$$L$D$ D$YÁ$D$$$D$$D$$L$ uD$$D$DĜÁ4L$, $L$襀} 14D,L$, $L$L$L$Dt'$K14ËL$( $譟@$, $D$u 14ËL$ $L$L$ $L$AL$ $}L$AL$$ $jT$B уJB$D$(D$$,D$D$4ÁD$$$D$$D$$ D$  t5$fD$L$D$D$ D$$ø$ fD$D$D$p̓D$D$$L$L$ ЃÃD$D$ $~} ËD$ !1Ã!D$ \$  $~\$ } ËT$!؃ÁD$D$$D$$ $$ D$D$D$D$$D$Ĕà D$(L$$ $D$D$L$脸\$t} 1 C K K ȉƒ~9;D$,w3L$$ $كL$T$T$;D$}1 à ø$O ÃL$ $L$ L$L$$L$D$ D$tËD$ Ã+D$ T$ [u+T$;T$ u >T$$$?T$u+BЃà 뷃D$$"L$AuA $p\$‹C uASS T$B$L$ $L$ ЃtD$ÉP뽃D$$T$ZtB\$KJBuB $NL$ $E#L$ ЃtB$ÃL$ $L$ u1ËAuA $É $1ÃD$$L$AuA t`8\$‹CuHS SBT$B${L$ $L$ ЃtD$ÉPA $@ÃD$ $ML$ AuA t $1A $ÃD$$T$B 2<T$Z JBct^\$Ct <\$T$KJ B uBB$L$ $D$ ЃtÉ$ZÃD$$gL$AuAthAD$L$\$ƒuHS SBT$B$L$ $L$ ЃtD$ÉAA $ÃD$ $ɿL$ AuAt $1A $xÃD$$耿T$Bu :T$Z uB$4CuA\$KJ B uB$L$ $D$ ЃtCt 3:T$B t B @t"B uBB$袿ËZ KJ B\$$D$ Ћ\$T$t똃D$u9D$$kD$@u9T$@BZC\$ZXtq\$KH@u @ $پL$ $E#L$ ЃtL$ $L$ ЃtD$Ë@$~BXWD$u8D$$pT$@u 8T$Zu$'1ËKJBuB IKX@ uX C$ÃD$ L$ $QtD$ D$ Á$ $/L$t ĄÍD$$dD$D$$D$ L$! $$L$+t ĄÍL$ $L$L$P$} ĄË$ $$ $$L$D$D$/$;D$t $ĄÉ $1ĄÃx!$覻5!-t"=t!u?t!t0E9}C9|! !L$t!x!$D$Ãx!$T$ t =t!ux!$ѻË ! !߁$$t ĜÍD$$v$D$L$ L$ $L$hD$} ĜË$ $NL$ $$L$D$D$L$;D$t $SĜÉ $?1ĜÍD$$h$D$L$k[|$1}2T$ E|Ã@!}o!P_ $ L$yƒ!}B!]_ $ L$Lƒ!}D$D@@Ã=!tt$L$3L$L$U!}'$5!D$D@@ÍD$$$D$3D$L$DD$$D$(A@D$,|$,d}D$$T$D$D$ D$0!D$u D$sD$,뱉$L$3L$ L$!|D$?D$D$D$3D$D$D$ 6@ÁD$@$D$|D$$D$ L$@ $D$} 1É$$L$@L$L$$$$$ 1D @L$@ $L$L$ L$M5 t 1ËL$4 $_à $D$}1ÉD$$L$ L$L$$L$pD$D$$|$}1ËD$ Ã,D$$D$D$胐T $D$|)D$$D$D$D$D$$L$ $X,Á$ $/L$肏t 1ĠÍD$$$dD$(D$$D$ 踾L$) $$L$Ǧt 1ĠÍL$$ $D$} 1ĠÍL$ $$D$D$D$ L$=L$T$$A $а$$ $LD$$$D$D$舳$$D$D$D$ D$$ $$L$$L$($$|"+H‰9su A9r$$$ĠÁ$‰D$8$ AIY s$Cʼn$ rR kZ(ggde}ftA$$T$D$L$ m$  $$L$yÍ$$T$tD$l$ C D$CD$CD$l먍$$T$LD$l$ C D$CD$lu$$T$$D$l$ C D$CD$blBL$@ $KL$$D$($$D$8D$D$$ D$ B D$BD$BD$D$(D$khtuiL$@ $KL$/$D$($$D$8D$D$$ D$ B D$BD$BD$D$(D$|k\$$T$D$l$ t$C D$CD$CD$>k$$T$D$l$ C D$ko lmn$ $T$ZL$l$ t$K L$KL$j$E1E9q$L$8L$L$\$ ډ$ IL$Zj$$C믍$$T$KD$l$ 'j$$T$1D$l$ C D$i$ $T$L$l$ K L$i$D$<C 1C 9щ$ L$@ $D$0H L$\$0D$(L$< $L$8L$L$$L$ L$KL$KL$L$(L$1i$$D$ $ߢu1,ÉD$( $@u1,ÉD$(L$u$L$ $T$ L$FmL$T$u'tBu‰ЃÃuBB'tB1뙃딃\$D$D$;D$ }0t L$$ $\$ L$l\$u3uD$ËL$T$$L$$L$D$뛉C띃\$D$D$;D$ }1t!е $\$ L$Ml\$u4uD$ËL$T$$еL$D$C렃PL$`|$XD$$D$DD$HD$ D$,    -`|$L+P 0uGxGXD$4$D$D$L$`L$ L$(a|$LT$`0| 9ta| z\A| ZD;T$`|$ u|$X|$\tD$\8|$,t1|$$tD$T@PËD$T@PÃ|$$tD$DL$H؃T$TJPËL$TD$DD$HAPËD$H;D$8uD$D;D$4ЉT$`Ջt$DT$HD$ փuLƋD$ʉt$$,$L$$$Luȉу$L$$Luh$W$H$,$ L$J$L$At ȉ-DÃt AMM:E t .tA+t-u$$$u븃먃$$ڃ$$$@$4ttkaEt ect U;kt[t]tXtStG$@u0t,$@D$$@$@Ȉ$4벺뫃t tu؃Љ l$1һ~ Љ‰$KU à l$(1ۃ}DC|1҃}DDB|D$$D$0d$L$$ u20~ Ã|$u} |uA|1 9~  9} D$$gt$\$1҉ىumF?uNtƉt5t(‰Ɖ)‰E0D$uEÉ벍Љ‹D$눉ىlj)‰E0F?uD$눍Љǃl$\$T$ l$~3$T$ D$l$\$T$ l$̓~$T$l$à D$, | T$ R ص‹D$L$$ $JL$JL$N\$T$~KD$,)D$0D$$$D$(D$T$$ à |$t$1O;|$s2t)ݸgfff‰Ӎ)N0u׃ ЋL$؉Ÿgfff‰Ӎ)N0듃(D$4ڃ | T$ R P‹D$L$, $JL$JL$M|$,t$0l$ }MËD$8L$)D$4(.<$D$\$D$D$ (ÃD$D$D$t9T$D$ t $#‹D$9t1ÃL$}Vىȁ} 4͌Éȁ‰)ЉT$$\$D$$L$=Á} ͌Éȁ‰)ЉT$$l\$D$$\L$ÃD$P;T$tjT$ BL$I9uoBtB@9t_$\wT$ BtB@9t_$9wT$ =^u R;T$uø`$wT$ ะ_$vT$ {L$=^A9D$s A;D$r/`$vL$AtA@9tJ`$vL$AtA@9tk`$}vL$ $L$AtA$D$D$AD$UL$A tA $AD$D$D$/ø`$vL$)T$L$ tYuȃÉL$ =^u&@9uȃË@9s ŋ 뾸`$uT$L$ ø`$uT$L$ 됃 L$ $L$L$i Ã|$ tLL$ $L$ IL$B\$ ‹uCC D$Ë*MKM K 渱`$ u륃|$$L$ $L$$IL$\$$ʼnD$9umCuK MC ʹʉKD$ ËC uKMىك @t @uȋJ KJK J U롸`$Ztl$\$$y`$@t@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$"L$,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$눸`$qT$9T$L$B9sJ s BHJI!Ë\$T$ B9sZBȋJI!Ãl$ \$K 9=^uD$$\$l$ \$E=^uD$$l$`l$ \$K @@@@MKK KH@@EE ʉ؃ø`$pl$ \$M\$=^t#L$KK KH@؃ø`$~p\$ʃ\$ K @J ȋK)ȃÃL$ $L$(L$X\$(t$$N9r~F)=s[NŋF)%EN)فMÃ9v݃9sڃ p! C9rø/a$o\$(t$$뎸a$o\$(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$?\$(qL$ $D$D$D$EL$릃T$l$BHЋ\$ !ÁsE9r sËB9r+D$$E)؉\$ D$D$ ʋD$$D$ D$iL$ ʋD$$L$D$Ã\$ L$KK ^@Ã,\$4T$0B4B؋J9vV$ L$0uA4uxa$蒠,É $XaD$AD$D$4D$ AD$AHƉ$B(Љƒu,ËD$0L$4HT$($D$jD$($D$4D$D$0$D$(D$\$(K  ʉ$C)ȉL$D$D$0$D$D$MD$0$D$D$#D$0$D$D$%|$0l$(1_$9v ދ[ 9w] tE hutHEh Et<$l$ED$I|$0l$(E t<$E D$l$&,Éo$빉$IaD$\$BHЋ\$4T$06T$$=^L$ $T$D$ $L$L$LD$$D$(D$D$ $D$D$$\$K ‹=^uL$ $\$T$ËD$ $\$\$D$ $D$D$ÉT$$D$D$$D$(D$^D$ $D$D$D$D$p뺃 D$$$D$(D$tD$$$D$,D$`\$(T$,C9u9wa$j\$(T$,D$$@,$T$D$$@,Ћt$$l$,\$(ECCX K Ë@)ÃM D$4$\$\$D$$$D$D$\$(L$,$A)؉D$L$D$$$L$D$)ȉD$D$$$D$D$\D$( É^$^1 Ã@\$HT$D$aD$D$\$ D$CD$CD$C D$CD$ CD$$CD$(BHЋl$D\$HC=@r@ËK ,$aD$AD$AD$ AD$AD$AD$AD$D$ AD$$EHЋT$H=  $il$H\$D‹E9r@Éщ D$4$+bD$CHЋT$DL$4$6bD$AD$AD$ AD$AD$AD$AD$AD$ AD$$BHЋT$DL$4$gbD$D$AD$ AD$AD$AD$AD$AD$ AD$$BHЃ@ÃL$ $bD$D$D$AHЋD$$D$D$ ÃL$ $bD$D$D$AHЋD$$D$D$L$ $bD$ALЃÃ4l$8\$<=ޭ=^t/== t,$\$bD$gl$8\$D$|$u1$Ë\$L$(t|;\$tC \$$L$D$D$$~D$L$I $D$ D$$D$ D$L$ $c\$ËD$HuL$T$  9v臽D$$à T$t.T$ $L$L$2L$uA ËQu1 Ã0T$4BD$(}J $T$4D$(J $aD$L$4I $wD$L$ $gD$L$4 $UD$ L$4I $BD$L$4I $/L$(L$L$ L$L$L$,A $<uD$$ >10ÉD$$$L$4 L$B $L$4IL$B $L$4IL$B $L$4I L$B $L$L$B T$ $~L$L$(L$ID$ D$ $D$4@D$D$(D$u\$$T$8D$ D$()؉L$,A9|菻D$$'=D$$0ÃL$$ $L$L$%u ËL$ $D$D$L$L$FxD$D$$t$1;\$@}; IT$( @,T$$ IT$( @T$C;\$@|ŋD$@D$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 %ludeditthreadstring 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 syntaxcouldn't parse wait messageppppppppunnamed fontstringwidth: bad character set for rune 0x%.4ux in %s height != 0 frselectpaint b==0/env/%s%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_threadexitsallstatus set to %pthreadintkillkbdprockeyboard read error: %r error/dev/cons%sctlinitkeyboard: can't open %s: %r rawoninitkeyboard: can't turn on raw mode on %s: %r internal 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 error+}])>Ͳ@ϲ Բ@ @ٲ ݲ  @@_frcanfit==0rgbkamx:g3 ###$P$ %bad channel descriptorbad channel descriptorallocimage: %sallocimage: %rname too longnamedimage: %snamedimage: %rbad channel '%.12s' from devdrawtimerproctimer realloc failed/dev/bintime/dev/nsecHmfont cache resize failed: %r resize: init failed: %r 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 >= nbsizecannot satisfy dsize %lud span %lud with align %lud+%ldD2B(p, c) == bpoolalloc %p %lud = %p poolalignspanalloc %p %lud %lud %lud %ld = %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) unknown elog type 0x%ux internal error: replacement string too large(%d)elogapply: 0x%ux elogapply: can't happen %d %d %d %s: %s %s Unhandled note %s, proc %p sys:Got note %ssys:Threadexitsallstatus = '%s' threadintlibthread: too many delayed notes 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 *default*deleted windowrdsubfonfile: header read error: %rrdsubfonfile: fontchar read error: %r/mnt/plumb/send/mnt/plumb/mnt/term/mnt/plumb/send/mnt/term/mnt/plumb/mnt/plumbplumbsrv/mnt/plumb/mnt/plumb/send%s/%s '= =%d frptofcharend of string in frcharofptTversion 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.2uxbad height or ascent in font fileillegal subfont rangeregexp: %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 NaN+Inf-Infe%de%de%d Hu#Hu%n+Hu,Hu-HuClE bG bSnXobocldoe bf bg bhHulHuntoopor6bsnuHuxoprocexec %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: %rgengetwindow: %r nobordernamedimage %s failed: %r image->chan != 0noborder(*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 bufimageNew Cut Paste Snarf Sort Zerox Delcol can't find windowcan't find windowcan't find windowbad event syntax"',4:_fraddbox_frclosebox_frdelbox_frfreebox_frgrowboxdupboxtruncateboxchopboxR{PD?.allocscreen: image and fill on different displayss->image && s->image->chan != 0top/bottom: ridiculous number of windows top/bottom: windows not on same screen acme: %s: %r +Errorscan't create column to make error windowrunevsmprint failed%.*S!"#$%&'()*+,-./:;<=>?@[\]^`{|}~strdup failedmalloc failedrealloc failedcan't make column#d/%dctl%P %P[%d %d]\ 66.7i7/884CutDelDelcolDeleteDumpEditExitFontGetIDInclIndentKillLoadLocalLookNewNewcolPastePutPutallRedoSendSnarfSortTabUndoZerox`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 on%.*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-crunwaittaskb~b *4>sH3P;VV`n_x}IT8ŽЎ;ڎ; threadmainthreadmain/dev/cputime_schedexec %s_schedexec failed: %rexec failed_schedexecwait %dprocexecmousescrollsizecompressed readimage: bad formatreadimage: bad channel string %sreadimage: bad ldepth %dreadimage: bad rectanglereadimage: image too wide for bufferreadimage: read count %d not %d: %rinterrupt! endofframe_frcanfit==0frinsert pt1 too far/dev/usernonenone ((((( H Newcol Kill Putall Dump Exit can't find columncan't find columncan't find file for dump: $home not defined %s/acme.dumpcan't open %s: %r %s %s %s %11dx%11d %11d %11d %11d %11d %s e%11d %11d %11d %11d %11d %s f%11d %11d %11d %11d %11d %s F%11d %11d %11d %11d %11d %11d %s %.*S%.*S%s %s %s can't find file for load: $home not defined %s/acme.dumpcan't open load file %s: %r can't chdir %s ./%s//tmp/d%d.%.4sacmecan't create temp file: %r bad load file %s:%d /env/%sill-formed control messagenoproc %d.%d %d._ %d.%d %s: assertion failed getsubfont: can't open %s: %r getsubfont: can't read %s: %r /tmp/X%d.%.4sacmeacme: can't create temp file: %r diskinitinternal error: ntosizewrite error to temp fileinternal error: diskreadread error from temp file?mainMoribundDeadExecForkRunningReadyRendezvousunknowntop of schedinit, _threadexitsallstatus=%p%s %d: &x=%p n=%d t->stk=%p %s %d: stack overflow pausing, state=%sall threads gone; exitingrunning %d.%d%d.%d marked to diesleeping for more workt->state == Readyreadying %d.%dwaking process %dBinits: unknown mode %d Bopen: unknown mode %#x _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`{[(<'"`*default*.%s/%s%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 %s%11d %q %11d no window ownervsmprint failed.$@ a|b`!?|c}d;~e!+f!gpiņm?p׈r!+st?ucvpw!xpyp=`!>B`!|D`!~XfYf<`!|`!>`!can't find text in filedeltextinternal error: fileinsertinternal error: filedeleteundo in file.load unimplementedundo: 0x%ux @@òɲ@[!Malloc of size %ld failed: %r Malloc of size %ld, total %ld %s: %s %s slash character in name argument to complete()/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%.*Sloadimage: bad rectangleloadimage: insufficient dataloadimage: image too wide for bufferbufimage failed0bad address syntaxY@scroll alloc0123456789abcdef0123456789ABCDEFbad stacksize %dcreate thread %d.%d name %sp->newproc == nilt->stk != nilt->stk != nil*default*naninfinityinf7635118191655355242878388607671088631342177271251562519531251220703125152587890625190734863281251192092895507812514901161193847656257450580596923828125bBnqUXY!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" Rendezvous with thread %d.%dRendezvous for tag %pWoke after rendezvous; val is %pout of memory_frinsureassert failed: %s *default*Axfidctlthreadcan'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=addrnomarkmarknomenumenunoscrollcleartagscroll%.*Sbad count in utfrune%.*Swindow shut down%.*S#c/pidsbrkmem/env/MALLOCFDcanlock(&pv->lk)==0panic: panic: .acmeconsconsctldraweditoutindexlabelnew.addrbodyctldataeditouterrorseventrdselwrseltagxdataWile E. Coyotecan't create pipe/dev/time/dev/useri/o error on server channelconvert error in convM2Sbad fcall typefid not in usefsysdelid: 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%dbad length in 9P2000 message headeraddress out of range#p/%d/argsfile does not existm%d %dkillreadmouse: %r mouseprocmouse: bad count %d not 49: %r read error/dev/mouse/dev/mouse#m/dev/cursor/dev/cursor   # ',29A JTUY _ g r0123456789+-/$.#,;^+-.*?#,;[]()$address out of range no previous regular expression no match for regexp already in usecreadimage: bad formatcreadimage: bad channel string %screadimage: bad ldepth %dcreadimage: bad rectanglecreadimage: bad maxy %dcreadimage: bad count %dAoff end in frdelete_frcanfit==0frptofchar in frdeletepermission deniedetextPchan.cfrbox.cctype.cctype.hnotify.s{vlrt.c.stringH.string!tprivinitrendezvous.swerrstr.cXlefth.string!xprivlockXsnarfrune3util.c!|keyboardctl!f$3fltfmt.comode$1vsmprint.c.stringrunevsmprint.csources_ctype!qmask$1!wordxfrexp.c!mainmem!selecttext!mainpid!closingfmtdef.h.string.stringremove.s!activecolEbadctl!bartflag!negateclass!_threaddebuglevel.string!lastpat!selectq!lastregexp!activewin.string`Xonnote`panicbuf\.stringcursor.h!atexitflag!backwardsymain9.s$0.3ff00000abs.cutfrune.c _exits.stringD.string!barttextJgetdefont.c|.stringlibdraw.a!fsyspidtabsnprint.cdat.hbio.h!usebintime$3_exits.sfstat.sleft1!left2left3 include!_nprivatessprint.c.stringlchannel.c!plumbeditfd\loadimage.c!collection=frdraw.c.string.string@frstr.c$0.40240000seprint.cfmtprint.ctokenize.c!lines$1Eopenfont.cmxinc386.s!pts$11postnote.ccmdtab!bloc!warnedMfmt.cfns.hdup.s!errorfd!bstartinst^computil.c.string!lastwasand!globalautoindentglib.cdirtabrightnan.cerrfmt.c.string.stringAlibdraw8frselect.cWsubfontname.cH.string"cplumbastringlistaalts$7"cnewwindowKfreesubfont.cDscroll.c"ncollection.stringdref.crunesmprint.cb$subidstack8.string" timerunmount.s@Ebadaddr"_threadpasserpid"objdirvsnprint.ctas.s;frinit.c"button"objtype9sysdirread.cbtonnotepid" modbuttonT$0.40590000Igetsubfont.cb_threadrgrp\.stringc0loopstructl.string"$rng_tapsysfatal.cc@rng_vecvseprint.cRborder.c"(nuntitledlframetos.hatexit.c",colbutton.stringwrite.cfprint.c.stringm(snarfbuf .string"0mainp.stringconvD2M.c"4mouseexit0"8mouseexit1"rfget$.framesetfontsaverf ifix name3.rfclose.framer3iconinitP.frametmpr(.safe9putsnarf(.framei nfd:getsnarf$.framenulls BC        ]^   I J ;;isaddrc.framer;lisregexc.framer;number.framemdq1q0.ret evalp rsizetlinedir>regexpx.framefound limTselt dirr.ret$foundpmdpat?addresst.frame$pat npatnc,evalp limmd4nrt.ret0qp(getcaprevc$q1sizedir q q0,rar  BC        ]^   I J Fsizecache.framebnGaddblock.framenibGdelblock.frameibH;flush.framebHsetcache,.framebl iblpqbq0Ibufinsert,.frameism toff nq0bMbufdelete$.framenq1bq0Mbufloader.frame nrrq0vNloadfileD.frame fargnullsnrnbnfd q1q0mrpO[bufload.frame nullsfdq0bObufread.framems nq0bPxbufreset.frameibPbufclose .frameb! BC        ]^   I J QcolinitH.frame.safetr1crScoladd\.frameclonew@.safe r1,t$v(i yrcWcolcloseD.framedofreeriwcZecolcloseall.frameicZcolmousebut,.frame.safec[tcolresize\.frame$r20.safei(wrr1c]colcmp.frameba^colsortd.frame(r1i4wr0wp,rpc`colgrow.frame8y2Xv4y1(j,kPdnld.safeDtot@nyeq_cmd.frametcharsonlycpnl_cmd4.framef acptappend.framecppfpdisplay,.frame npbufp2p1fpfilename0.framefloopcmd.framecpi nrpfrplooper@.frametrpf rp nrpcpopxyrlinelooperX.framecplinesel$a0a3fr4rpnrpalllooper .framewlpv(alllocker .framevwQfilelooper.frameicpXYnextmatch.framepf signrcmdaddress@.framea2a1.retfaapsignalltofile$.framettpvwmtofile,.frametrrrallmatchfile.frametpvwomatchfile.framertffilematch.framematchhsrbufibuffr;charaddr .frame.retaddrlsignlineaddr0.framenp.reta faddrlsignallfilecheck$.framefpvwtcmdname@.framesetfc newname(.safes nrfstr$ BC        ]^   I J  editthread.frameallelogterm .framewalleditinit.framewfallupdate$.frame ftw'editerror.framesargfmteditcmd.frameerrctrnEgetch.frameqnextc.frameungetch .framegetnum.framesignok signn BC      %f_fraddbox.framebnfng-_frclosebox.framen0fn1g_frdelbox.framefn1n0h._frfreebox.framein1fn0h_frgrowbox.framedeltafidupbox .framep .safebnfiruneindex .frame runenipjBtruncatebox.framefnbjchopbox .frame .safepfnbk]_frsplitbox.framenbnfk_frmergebox .frame.safebfbnl_frfindbox.framepbn qf 67? BC      %m_frcanfit,.frameprw nrleftptf bn_frcklinewrap.framepfbnJ_frcklinewrap0.framebpfn_fradvance.framepbfn_frnewwid.frame bptfn_frnewwid0.frame.safept bfoy_frclean$.framebptnbn1 n0 cf 67@ BC      %Ep_frallocstr.framefpnq_frinsure$.frame.safepnbbnf 6AB BC   uq_stringnwidth.framerunelsubfontnamewidcbuf nmaxtwidrptrrsptrsf lentCstringnwidth.framelensftnstringwidth.framesftstringsize.frame.ret.safesftrunestringnwidth.framelenrfurunestringwidth.framerfuArunestringsize.frame.ret.saferf 6AC BC   ustringT.frame sfspsrcpt.retdstv(stringopT.frame$op sfspsrcpt.retdstvstringnT.frame$len sfspsrcpt.retdstwTstringnopT.frame(op$len sfspsrcpt.retdstwrunestringT.frame rfspsrcpt.retdstxrunestringopT.frame$op rfspsrcpt.retdstyrunestringnT.frame$len rfspsrcpt.retdstyrunestringnopT.frame(op$len rfspsrcpt.retdstzA_string .frame.ret@bgpsp,cliprptsrccreadimage.frametaXnb`maxy\minypbuflncblock.safexidolockdPr|chandnew=hdrfd 6A^ BC   ;_twiddlecompressed.framenbuf_compblocksize$.framedepthr 6A_ BC   7unitsperline.framerbitsperunitdwordsperline .framedrbytesperline .framedr 6`a BC    6`bPpsstate.frames_schedinit$.frametpargneedstack8.frame.safe tnx`_sched$.frame.safetprunthread.frametqpi_threadready .frameqtsyield.frame 6`c BC    6`bPyfinish.framevalrett_threadrendezvous(.frameretvalt l.safetag;_threadflagrendez.frametQ_threadbreakrendez .frame li 6`d BC    6`bP\incref .framerdecref .framer 6`e BC    6`bP$threadnotify .frameitopidto frominfdelayednotes(.framenvip _threadnote .framevnpsz_procsplhi.frame_procsplx.frames 6`f BC    6`bP'main,.frameargvargcpmainlauncher.frameargskip.framep_times.framerptfbQefork.frame.safebufeE_schedexec.framepidet_schedfork.framepidp_schedexit.frameexp`_schedexecwait,.framepid_systhreadinit.frame_threadgetproc.frame_threadsetproc.framep 6`g BC    6`bPr_threadmalloc .framez .safemsize_threadsysfatal.frameargfmtbuf 6`h BC    6`bPcthreadid.frameothreadpid.framepidpid8threadsetgrp.framengSthreadgetgrp.frame_threadsetname.framefdbuf.safeargfmttpethreadgetname.frameqthreaddata.frame}procdata.frametprivalloc.frameitprivfree .framei9tprivaddr.framei 6`i BC    6`bPLthreadexits .frameexitstrthreadexitsall(.frameipid npidmypidexitstrthreadwaitchan.frame 6`& BC    6`bPprocexec$.frameargspidcp tprogprocexecl.framefpidc 6`j BC    6`bP_threaddebug.frameargfmtbuffflag_threadassert(.frame.safesbuf 6`k BC    6`bPnextID.frameinewthread(.frameidpgrpnameargft stacksizeNthreadcreate(.framestacksizeargf.safe_newproc(.framegrp namestacksizeargfprforkflagEprocrfork,.frameid rforkflagstacksizeargfpproccreate.framestacksizeargf_freeproc.frametnexttpv_freethread.framept 6`l BC    6`bP0 _chanfree.framec xchanfree .framec chaninit.framecelemsizeelemcnt chancreate.framecelemsizeelemcnt ralt0.framerxa canaltsst GrunopD.frame nbvc,aop recv.framevcnbrecv.framevcEsend.framevcqnbsend.framevcchannelsize.frameszcsendul.framevc recvul.framevcLsendp.framevc|recvp.framevcnbsendul.framevcnbrecvul.framevc*nbsendp.framevcZnbrecvp.framevcemptyentry .frameextraic|enqueue .framecadequeue$.frameicaHcanexec(.frameaaltexecbuffered,.framewillreplace.safecaaltcopy.framedstszsrc[altexecD.framespl bufwaiter nicmebotheropa 6`m_xinc.framel _xdec.framel 6`n BC    6`bPd2launcher386 .framefargR_threadinitstack .framefargt 6opstrlen.framep 6oq$strcpy.framep1p2 6or'strchr.framecs 6os,strcat.framep1p2 6ot7longjmp.framelrRsetjmp.framel 6ou$amemset.framencp 6ovHmemmove.framenp2p1 6ow0memcmp.framep2p1n 6ox-memchr.framecpn 6oyM_mainL.frameinargvinargc 6o{_d2v,.framey$.safexd_f2v.framefy_v2d .frame.safex?_v2f.framexZslowdodiv(.framerqquohi iquolodennumedodiv@.framern xqnumdenrpqp _divvu .frame dnq_modvu .frame dnrvneg.framev_divv,.framednegnneg dnq_modv,.framenneg dnr _rshav .framear b _rshlv .framear b!h_lshv .framear b!_andv.frame bar!_orv.frame bar" _xorv.frame bar"&_vpp.framerl"E_vmm.framerl"d_ppv.framerl"_mmv.framerl"_vasop0.frameret typervtufnlv%_p2v .framepret%_sl2v .frameslret%_ul2v .frameulret%_si2v .framesiret& _ui2v .frameuiret&"_sh2v .frameshret&?_uh2v .frameulret&]_sc2v .frameucret&z_uc2v .frameulret&_v2sc .framerv&_v2uc.framerv&_v2sh .framerv&_v2uh.framerv&_v2sl.framerv&_v2ul.framerv&_v2si.framerv&_v2ui.framerv&_testv.framerv'_eqv.framervlv'"_nev.framervlv'@_ltv.framervlv'h_lev.framervlv'_gtv.framervlv'_gev.framervlv'_lov.framervlv(_lsv.framervlv(0_hiv.framervlv(X_hsv.framervlv 6o|(getcallerpc.framev 6o}~ BC $(utfrrune .framer s1sc 6o} BC #) utfrune .framersc 6o} BC )utflen .frame runens 6o} BC )utfecpy .framefromtoe 6o} BC g*@strtoul,.frameendptrmovfl$ndignegnptrbase 6o} BC k,Ystrtol,.frameendptrm$ndig negnptrbase 6o} BC .Dstrrchr.framercs 6o} BC .strncpy.frames1s2n 6o} BC .strncmp.frames2s1n 6o} BC /%strdup.frame .safes 6o} BC /dstrcmp.frames2s1 6o} BC /runestrlen.frames 6o} BC /runestrncmp .frames2s1n 6o} BC 0runestrdup.frame .safes 6o} BC 0_runestrcpy .frames1s2 6o} BC 0runestrchr .framesc 6o} BC 0chartorune.framerunestr1runetochar .framerunestr2runelen .frame strrunec29runenlen.framernrune2{fullrune .framestrn 6o} BC 2readn$.frame tfaavn 6o} F3swapb.framejies3Lswapi.frameijiies3ypivot$.frame pjpipkapn4eqsorts,.framej pipjpnaespn6-qsort$.framen s cmpvaes 6o} BC 6~nrand.frameslopn 6o} BC   ?@ X6sbrkalloc.framen6sbrkmerge.frameylxx7.plock.framepvp7ipunlock.framepvp7checkenv4.framebuffd8/pprint .framevfmtpvp8ppanic,.framenv msgfmtpvp9malloc.frame .safevsize:Smallocz.frameclr .safevsize:mallocalign(.frame .safev spanoffsetalignsize;xfree.framev;realloc .frame .safenvvsizelrand.framex 6o} BC />lock.frameilk?Vcanlock .framelk?yunlock.framelk 6o} BC ?getuser .framenfd 6o} BC E@ cleanname0.frame.safeerasedprefixrootedname 6o} BC ;Aatol.framesBatoi .frames 6o} BC Batof.framecp 6o} BC ABatexit.framefiCOatexitdont.framefCexits.framesipid 6o} BC Cabs.frameaClabs.framea 6o} BC D_assert.frames 6o BC   6o^DMfmtStrFlush.framensfDfmtstrinit.framenfE[vsmprintH.framefmtargs0f 6o BC EvseprintD.framefmt args0fbufe 6o BC   6o^}F0_fmtFdFlush.framenfF}vfprintP.frame4nfmtargs0buffd0f 6o BC Gsprint$.frameargsbuffmt 6o BC G>snprint$.frameargslenbuffmt 6o BC Gmsmprint .frame.safepargsfmt 6o BC   6o^GruneFmtStrFlush.framensfHOrunefmtstrinit.framenfHrunevsmprintH.framefmtargs0f 6o BC I:runesmprint.frameargsfmt 6o BC IYprint .frameargsfmt 6o BC Ifprint .frameargsfdfmt 6o BC   6o^zIfmtvprint.framefmtargsvaf 6o BC   6o^TJ_quotesetup.framerunesoutwc noutrninsharpqsL"qstrfmtP.frame2_runer,nc$rs rtst(fl0w rmermrinmeqmsin fR#_quotestrfmtL.frame$q rsrunesinfSquotestrfmt.framefSquoterunestrfmt.framefSquotefmtinstall.frameT _needsquotes<.framequotelenpqsT^_runeneedsquotes<.framequotelenpqr 6o BC   6o^{Tfmtprint.framefmtvaf 6o BC   6o^zU fmtfdflush .framefUHfmtfdinit.framefd sizefbuf 6oM BC   6o^%U}_fmtinstall.framecfUfmtinstall.frameretfcVfmtfmt.framepcV_fmtdispatch .frame.saferunefmtfisrunes 6o BC   &' 6o|Xxadd.framevanXxsub.framenvaY:xdtoa|.frameDc2Lc4Hc3@c18is1,h4dh.safe$g0eXchrPucaseTsigns2f\precfmta_floatfmtx.frame`.safefUsfmtb _efgfmt .framedf 6o BC   6o^gb6errfmt.framef.safebuf 6o BC   6o^fbdofmt8.framenstruner rsrt nfmtffmtd_fmtflush .framelenfte\_fmtpad$.framen ife_rfmtpad$.framen iffN_fmtcpyH.frame*_runest r$ncrsrtn fl(wfme szmvmj_fmtrcpyD.frame&_runerts men fl$wmvmfl_charfmt.framexfl_runefmt.framexfm,fmtstrcpy0.frame.safei rjsfn_strfmt.framefn2fmtrunestrcpy(.framesfn_runesfmt.framefn_percentfmt.framexfo_ifmt.frameli.safepnLpCbufhbasePconvXvutflfdnegt_countfmt.framefuH_flagfmt.framefu_badfmt .framexf 6ov*unmount.frame 6ov2sleep.frame 6o v:seek.framea 6ovPrfork.frame 6ovXrendezvous.frame 6ov`remove.frame 6ovhpwrite.frame 6ovppread.frame 6ovxpipe.frame 6ovopen.frame 6ovnotify.frame 6ovnoted.frame 6ovmount.frame 6ovexec.frame 6overrstr.frame 6ovdup.frame 6ovcreate.frame 6ovclose.frame 6ovchdir.frame 6ovbind.frame 6ov_exits.frame 6o BC vwrite.framenbuffd 6o BC wwerrstr.frameargbuffmt 6o BC   wkwait8.frame wlfldbuf 6o BC "x_sysfatalimpl.frameargfmtbufysysfatal.frameargfmt 6o BC )y7brk.frameblpynsbrk.framenbl 6o BC yrerrstr.framenbufbuftmp 6o BC   zread9pmsg$.framelenn buffdabuf 6o BC zread.framenbuffd 6o BC n{_qlockinit.framer{ getqlp.framepop{hqlock.framempq{qunlock.framepq|_canqlock .frameq|rlock.framempq}=canrlock .frameq}runlock.framepq~#wlock.framemppq~canwlock .frameq wunlock.framepq rsleep.frametmerrwakeup.framerrwakeupall.frameri 6o BC  putenv.framelsvalfhenamename 6o BC 3privalloc.framepyprivfree .framep 6o BC &postnote.framernotefgroupfilepid 6o BC Hbe2vlong.frameftonsecD.frame(.saferetriest b.ret 6o BC !{iounit.frameargsicfdfdbuf 6o BC Zgetwd .framennbufbuffd 6o BC getpid0.framefb 6o BC *1getenv.frameans sf.safe|enamename 6o BC   |fcallfmt .framedqpi.safetmptagffmtebufqidtype .frametspdirfmt.framefmtbuffdirconvh.frameebuf.safedtmpndumpsome$.framei peansbuf count 6o BC   dirstat0.framebufnamedind 6o BC   dirpackage4.framennsm inssdbuftsAdirread .framedtsbuffddirreadall(.framed nfdtsbuf 6o BC   |dirfstat0.framebuffddind 6o BC   Hpstring.framenpspqid.framepqSstringsz .frameswsizeS2M.frameinfconvS2M$.framepiapnap sizef 6o BC   ygstring.framesnpepgqid.frameeppqconvM2S(.frame iepsizenapapf 6o BC   statcheck.framebufnbufconvM2D8.frame nspsviebufbuf strsdnbuf 6o BC   sizeD2M(.frameinssvd convD2ML.frame4ss0nsvi nssvebufpbufnbufd 6o BC 'eaccess.framedbnamemode 6o BC abort.frame 6o7_mulv.frame bar_mul64by32.frame bar$_div64by32.frameba r7_addv.frame barQ_subv.frame bar 6ok_tas.framel 6o6wmemccpy.framep1cp2 n 6o} 1toupper.framectolower.framec 6o} BC qqtoken.frametsepquotingsetoken.framesepquotingt gettokens.frameargs sepmaxargsnargsstokenize.frameargsmaxargsnargss 6o} BC kstrtollT.frame.retendptrnn8.safemp$ovfl0ndig n,negnptr base 6o} BC   &+strtodH.frame4.safeLdF*z:BHG`FKn/pRBB:H J FA AFBoDJD CANC IGEXN7BE  JA PBAAD FAABNLCCAAA  K OA BC _][XeQEY;HO L}KLBEGCU WYBBQ`L BNBNJ0CEK G ROH J OHFG #aQ#F`%fK AGNNI MQ R?|VAl0y5Bd*g][ JJI  BB RKVFB-CID` /,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 VHTlB  ZE8tHV.Cw)c_El*g%^XKIlBCH\D'M G AAK `J(DWCFKA xpDABFBTBF M:A[O O\JDSFlKEG َ)с3Ȫ7Ą@EFq3G+h[K ArHgHJEgr _A XGDF‚ABBOADAZ LHKI H TMPEJ% VOMPH ZT|DgD NFHG!F I](RZF  PDd/C IN DE DF AA(iOABN^fFlPPVAEEC6DnVAEEChCR+BYTs8BAIDyFAGT#NQLHqs{y Gs/  J HBAa!S QIHWTALF[GAKABEF P RFXFMP'  WDWFBU6AAACCW ABAMLNO  HAH LK VCE Rg G I DK DBHCBEQ#C Ec%C BEEB A AFGCaCDH]U T]GA DCAEF CEC AAM AE^JTFGZ\BCAA_ ELBC KBK KMCH=B3 pVJ)(cYEN%B^L   AGMAj)f%3F  /B^\XT J Ba}|xejD XCLrvBj6nDbB;H@Bz AUHTajrwqJg_nUM\IIA={9w0n+i$b ^XTPL GCL A5KElAA AZ l&V^A ^ ]e) `YEJ  BBCBWL AOE AHTFBTU7FFGGP [FJ Y^E+k hJLP &FG A d8AKEL^S Pt.q[_`Xa A  AGFEDGWa ^D}EAD'DjdKAAQOSUAAYAAECBDBCD CDCAAnH F BCAG H CNCד BE^ JQGF* >~1nAt2`VFPEELENXGNCxLDI F Y#__ 4C  AYZJ QF FG\CELrszBDHQECAWuED G BRz>,UQENLI BG @gEHH iZH g#`ZQNAGwBB IHGAACAG DL] LARQO\LODBGCMNc B To#Ca%K CHCEOVCA5A KQLDD `AQF FOu  TE Ab$mSNFTH^Wo  K j,ZP LLPMETMAG^CI )A`$PBBKIPAF  CB CE GE QELJAAAB CLBJE/A\EI CDB EAHAC}QTYi,Sb6IBEgF CDFEBLBBAE AAAOEYC*BOJX  BGAEBLFACLGLAIKW!DkC4M`K_iOaX KgUBD^ROIC ACLaM*S IEDDLLBAv3}:o,BQh%L BXaDFLKFLJGOBM   QPCETHQOWHN  TR DG[J`ZWHRAIADLPHMCPHG ^ C EFHBfBG?AA[A^ GQjT7DAFDBB yGCCAcAK BDbFAQ T ^ICATAPKI TADA%oEABACCnJOkJBAHHMkHHTJ.lv0MOI_NO; Q IEEPBUUBANDQAL6PBIDTMEDD6wL(w:Uh*k P BNDG&g'g'gG KG BB C$ VDDD QHGQHC|Ln J rDIz D lGC XCABBF ACACH UEDKH EHDKV+AODD BEBENAAHFEBHJCAHCKCAYHHZU RBMEpVUQPL K GEB_ IGEB\VSPYBK EJBBGBBBBBBBBBBBBBBBBBB\AM]GaKH IKENILEIDGS MACg CS MA NAoSMKBu wpsvlBdh`BAEZ]BMEHABt5z:~>r0Bh*m-g'B\a!d$[WBCPL  KDOAB7P]H HUE#AFIP] ru4|i)Bc%kp0BATxa BY^WBTQJBEQBAGB};\>]s3Bm/oz:BzA]kA%_xnBYeZBTTBOIBFԈC-сTHCB4PCHBo݄}B4k(W`Cap{zCrYCkNC^ЊCMMFBEH oIZsCYFAAކBUH OTBOH OSDFA DFA  TRAEDG[J`ZWHAE"niABECAZArBVAVAXw}|rCdFc#_n0q+ }9yGDZV NBBRCAAAVEYDLDFC ELH"΂FF H H JHEKN IQEFH K DJHVG ALCPH  I DGm5BOIN='zBF7dFCP [ NME%AFABT  CMi(CPYS L7  ABDYPK2BBCEFEBBCEFEBCEFEBBCEFEABCEFEBBCEHCHECAAAAAAg I VV$CPFBBBBB2_OFFAFC JFKAD?~0m J};cU IQSACABADHCJN HvPFVK^$NPDDBG LT L MBhGS [^]"CF  C>F A AFaDA BF HO^ C EFHBfBG?AA[A^ GQjT7DAFDsmacme/addr.8 664 0 0 33321 10453465556 12044ustar00rminnichsys~E.string- ;> 01234567- ;> 89+-/$.#&  AO  D p  Sp  S~=utfrune =& AX  ,;^+-.*- ;> ?#,;[]()p >D p  Sp @ p  S =& AO % $addres- ;(> s out of- ;0> range p ASp >"Dp S~=warning =p  @p AOp  @ p  @p Pp @p P ~=regexp =tA~@patp @ w Q& AX <~=rxnull =p @ & AX  no previ- ;@> ous regu- ;H> lar expr- ;P> ession p @p Sp >8Dp S =~@foundpp $@p AOp  @ p @p Pp @p P w Q' AO % no match-' ;`> for regp' ASp' >XDp' S' =p' ? p( $@p(  Op)  @ p) ?p) Pp) ?p) P) ) ~=address- =pA~@arp5 @~?rp5 ?p5 @p5 ?~@ q0p6  @~?!qp6 !?p7 A ~?"dirp7  "?~?#sizep8 A#?p9 A W: j exp ~=0disk5 0=A~=1mouse5 1=A~=button5 =A~=ccommand5 =A~=cxfidalloc5 =A~=fontnames5 =A~=plumbsendfd5 =A~=font5 =A~=maxtab5 =A~=acmeerrorfile5 =A~= cxfidfree5  =A~= home5  =A~= keyboardctl5  =A~= objtype5  =A~= messagesize5  =A~=cwait5 =A~=cwarn5 =A~=colbutton5 =A~=reffont5 =A~=textcols5 =A~=but2col5 =A~=editing5 =A~=but3col5 =A~=seq5 =A~=screen5 =A~=mousetext5 =A~=globalautoindent5 =A~=row5 =A~=display5 =A~=globalincref5 =A~=mouseexit05 =A~=mouseexit15 =A~>.string5 >pA~= boxcursor5  =HA~=!bartflag5 !=A~="cplumb5 "=A~=#mousectl5 #=A~=$argtext5 $=A~=%modbutton5 %=A~=&nullrect5 &=A~='plumbeditfd5 '=A~=(activecol5 (=A~=)timerpid5 )=A~=*tagcols5 *=A~=+seltext5 +=A~=,cedit5 ,=A~=-cputype5 -=A~=.typetext5 .=A~=/activewin5 /=A~=0fsyspid5 0=A~=1cnewwindow5 1=A~=cerr5 =A~=snarfbuf5 =$A~=cexit5 =A~=ckill5 =A~=barttext5 =AI ?/  W <& AS  #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(q1file->nc && 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'sizecache > A~@np @ ~@bp @ p  Q&  R < p   dA p   Qp Q p  Sp  Q   p  S~=erealloc =p @ p P ~>addblock >Ap @ p  Q~@i& @T <~>.string- ;> internal- ;> error: - ;> addblockp >Dp S~=error =p @ p Q p  Sp  Q C  A p  S~= realloc  =p @ p @ p Rp  R&  L 5 delblock  >Ap @p  O& @M I<- ;> interna- ; > l error:- ;(> delblocp >Dp S =p  =p Sp @p Op @ a  Op Op S~=diskrelease =p @ p @ /  Rp  R&  L eflush >Ap @ p R& AX wW setcache >(A~@q0p @ p @ p Q&  T <- ;0> kintern- ;8> al error- ;@> : setcacp >2Dp S =p @ p @ p Q& AO p @ p R& @L  heblock- ;P> not foup >KDp S =p ?p ? p @ p ? W p ? p @ p R p  Qp  =p Sp  Sp Qp Sp Qp  S~=diskread = ~=bufinsert =(Ap @ p Q& @T <- ;X> ndinter- ;`> nal erro- ;h> r: bufinp >[Dp S =p @ W p @ p  @ p @ p Qp   ~?offp ?p Q   &  AR P sertint-" ;x> ernal er-" ;> ror: buf-" ;> insert1 p" >uDp" S" =p" @ p#  Sp# ASp# ?p# S# >p# @ p$ AQp&  Sp& ?p& S& >p& @ p& ? p' ? a'  Rp' T a' Pp' Sp' Ta'  Op' Sp' T'   ' p' S'  =p( @p( Op( ? a(  Op( S~@sp( @p( Sp( ? ( p( S(  =p( ? p( @ p) ? p)  QW* p3  @ p4  Sp4  A p4  S~=min4 =p4 @ p4 ?p5 Q&5 AX5 q cnc!=0i-7 ;> nternal -7 ;> error: b-7 ;> ufinsert-7 ;> 2 cnc!=0p7 >Dp7 S7 =p7 @ p8 A W8 v  Sp>  ?p>  Sp> ?p> S> >p? @p? Sp? ?p? S? >p@ @p@ Op@ Sp@ @p@ Sp@ ? @ p@ S@  =p@ ? p@ @ pA @ pA  QpB ? pB  QpC  QWD pM @ pN  =pN SpN RpN ? aN  OpN SpN RpN ? aN  OpN SpN ?pN  SN =pN  @ pN @ pO ? O  QpU  SpU Q pU  A U   pU  SU =pU @ pU  pV  SpV Q V  pV  ?pV SV >pV @ pW QpW Q aW PpW SpW @pW SpW ? W pW SW  =pW ? pW @ X  QW* < Z  Q [  @p\   \  \ @]   @p^ AQW^ <^ ~=bufdeletec = Apc @ pc @ ~@q1&g  @Rg  interna-h ;> l error:-h ;> bufdeleph >Dph Sh =ph @ ph @ Wi pj @ pj @ pk Rpk  k pl Rpl R l  &l @Tl !bufloader{ !>A~@"vp} "@p} Sp} @p} S~@#rp} #@p} S~@$nrp}  $@p}  S} =p~  $@~ ~ ~=%loadfile %=@Ap  A p  S~=&emalloc &=~?'pp '?p @A p  S &=~?(rp (?p A?p A p @~?)q1p )?W 7 teread - ;> error in- ;> Buffer.p ASp >Dp S~=,warning ,=W 6<  o A Rp  ?p   & Ap  ?S S< A p  Sp  Sp (?p S~?-nba -?p  S~?.nra .?p S~@/nullsp /@p S~=0cvttorunes 0=p '? p  Sp -? a  Qp Sp ? -?p S  =p -? ?~@1argp 1@ p  Sp )? p  Sp (? p  Sp .? p   S~@fp  @ p  ? )?W 5 loadint- ;> ernal er- ;> ror: bufp >Dp S =p *@ p  Sp @ p  Sp  /@ p  Sp >!D p   Sp @ p  S %= ~=bufread =Ap @p O& @R  loadbuf- ;> read: in- ;> ternal ep >Dp S =W p @ p  @ p  Sp R p R   @ p  S =p @ p  p @p Sp Qp @  p  p Q a Pp Sp  p  ? p S  =p ?  @p    @   @W < ~=bufreset =Ap @ p AQp AQp AQp AQp AQp  Q /  W p @ p ? W < ~=bufclose =Ap @p S =p @p Op S =p @ p APp APp Pp S =p @ p APp A P ~> .string- ; > rror5  =A~= mouse5  =A~= button5  =A~= ccommand5  =A~= cxfidalloc5  =A~=fontnames5 =A~=plumbsendfd5 =A~=font5 =A~=maxtab5 =A~=acmeerrorfile5 =A~=cxfidfree5 =A~=home5 =A~=keyboardctl5 =A~=objtype5 =A~=messagesize5 =A~=cwait5 =A~=cwarn5 =A~=colbutton5 =A~=reffont5 =A~=textcols5 =A~=but2col5 =A~=editing5 =A~=but3col5 =A~= seq5  =A~=!screen5 !=A~="mousetext5 "=A~=#globalautoindent5 #=A~=$row5 $=A~=%display5 %=A~=&globalincref5 &=A~='mouseexit05 '=A~=(mouseexit15 (=A5  > A~=)boxcursor5 )=HA~=*bartflag5 *=A~=+cplumb5 +=A~=,mousectl5 ,=A~=-argtext5 -=A~=.modbutton5 .=A~=/nullrect5 /=A~=0plumbeditfd5 0=A~=1activecol5 1=A~=timerpid5 =A~=tagcols5 =A~=seltext5 =A~=cedit5 =A~=cputype5 =A~=typetext5 =A~=activewin5 =A~=fsyspid5 =A~= cnewwindow5  =A~= cerr5  =A~= snarfbuf5  =$A~= cexit5  =A~= ckill5  =A~=barttext5 =AI : setcacp >2Dp S =p @ p @ p Q& AO  #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; } -?p  S~?.nra .?p smacme/cols.8 664 0 0 77474 10453465567 12115ustar00rminnichsys~E.string- ;> New - ;> Cut - ;> Past- ;> e Sn- ; > arf - ;(> Sort- ;0> Zer- ;8> ox D- ;@> elcop  ?p Sp ASp >Dp Sp &Ap  Sp Ap S~=textinsert =p  ? p  Sp Pp Op Sp Pp Op S~=textsetselect =~=colbuttonp = p =p Sp  ? Aa Sp A ! y p  Sp ASp Qp Sp  Qp  S =p @p AO ~=coladd =XAp @p A p  ~?ra ?p A ! y p @R Ap ?~@yp  @& ?P  l ca- ;P> n't find- ;X> windowp >NDp S~=*error *=W  APp? Pp? S~=free? =p@ @p@ S@ =~=clearmouseA =A ~=colmousebutE =(ApE @ aG  ?pG SpG QpG SpG QpG SpG QpG  SpG QpG SG %=aG  ?pG SpG  ?pG SpG  ?pG SpG ApG  S~=divptG =pG &=pG SpG  ?pG SpG  ?pG SG '=G ~=colresizeK =XAQ =pQ @ aR ?~@raR @pR A !R R yR pS QvS O S ?pS ?pT   T ApT SaT SaT ?pT A !T T yT ~=textresizeT =pT = ~=screenpU =pU S~@ cpU  @ U AaU SpU A !U U yU pU  SpU ASpU QpU SpU  QpU  S~= drawU  =~? r1pV  ?pV  ? W A ?pX =pX SaX SaX  ?pX A !X X yX ~= displaypX  =pX OpX SpX AS~= ZPaX  = pX PpX SpX PpX  SX  =pX  @ pY @pY  ?pZ AWZ colcmpl >A~@apl @ ~@bpl @ pq Qpq Opq O pr Qpr Opr Ops Pps Ops O pt Ppt Opt Opu A Wu EDp  S~=qsort =p ? p  @ p A W .string- ;`> can't fi- ;h> nd windop >`Dp S *=p ? p  @ W  $?p> S> =p?  @p? AOp@ @p@ S~=0winmousebut@ 0=@ ~=1coldragwinD 1=\AL =~=mousectlpM =pM S~=boxcursorpM =DpM S~=setcursorM =~=mousepM = pN Q~?bpN ?pO Q~?oppO ?pO QpO ?WP  wcan't -\ ;x> find winp\ >rDp\ S~=error\ =W[ ..string- ;> dow~=disk5 =A5 =A~=button5 =A~= ccommand5  =A~=!cxfidalloc5 !=A~="fontnames5 "=A~=#plumbsendfd5 #=A~=$font5 $=A~=%maxtab5 %=A~=&acmeerrorfile5 &=A~='cxfidfree5 '=A~=(home5 (=A~=)keyboardctl5 )=A~=*objtype5 *=A~=+messagesize5 +=A~=,cwait5 ,=A~=-cwarn5 -=A~=.colbutton5 .=A~=/reffont5 /=A~=0textcols5 0=A~=1but2col5 1=A~=editing5 =A~=but3col5 =A~=seq5 =A5 =A~=mousetext5 =A~=globalautoindent5 =A~=row5 =A5 =A~=globalincref5 =A~=mouseexit05 =A~= mouseexit15  =A5 >A~= boxcursor5  =HA~= bartflag5  =A~= cplumb5  =A~= mousectl5  =A~=argtext5 =A~=modbutton5 =A~=nullrect5 =A~=plumbeditfd5 =A~=activecol5 =A~=timerpid5 =A~=tagcols5 =A~=seltext5 =A~=cedit5 =A~=cputype5 =A~=typetext5 =A~=activewin5 =A~=fsyspid5 =A~=cnewwindow5 =A~=cerr5 =A~=snarfbuf5 =$A~=cexit5 =A~=ckill5 =A~= barttext5  =AI '  @p' O&' +?P' O #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; } /* scrollr drops below w->r on low windows */ if(ptinrect(p, w->body.scrollr)) 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; } y && c->nw>0){ /* steal half of lassmacme/dat.h 664 0 0 25542 10453465216 11761ustar00rminnichsysenum { Qdir, Qacme, Qcons, Qconsctl, Qdraw, Qeditout, Qindex, Qlabel, Qnew, QWaddr, QWbody, QWctl, QWdata, QWeditout, QWerrors, QWevent, QWrdsel, QWwrsel, QWtag, QWxdata, 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; 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*); char *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*); int 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; enum { Kscrolloneup = KF|0x20, Kscrollonedown = KF|0x21, }; 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 imer Timer; typedef struct Window Window; typedef struct Xfid Xfid; struct Runestr { Rune *r; int nr; }; struct Range { int q0; int q1; }; struct Blocksmacme/disk.8 664 0 0 14720 10453465572 12064ustar00rminnichsys~E.string- ;> /tmp/X%d- ;> .%.4sacm~=getpid =~?.safep p?~=getuser =p l?~?bufa ?p Sp Ap Sp >Dp Sp p?p  Sp l?p S~=snprint =p AA W  eacme: - ;> can't cr- ; > eate tem- ;(> p file: p Ap Sp >Dp S~=fprint =- ;0> %r diskp >4Dp S~=threadexitsall =p  ? p   ~>ntosize >A~@n& @ AT M<- ;8> initint- ;@> ernal er- ;H> ror: ntop >=Dp S~=error =p @ p   A& AO W~@dp @~>blistp >~?sizep ?p ? a  Vp O &  AO tp V p  Up ?  Vp @ p  Rp   ~=diskrelease =A~@bp @p Op Sa ?p S >p @p @ p ? a  R p P p  Up ? a  Rp O ~=diskwrite =(A~@bpp @p O~?bp ?p O p  Sp AS >p ?p  @ p  Sp AS >p ? p  p ?&  O  sizewri- ;X> te error- ;`> to tempp >UDp S =p ?p  @ p  O ~=diskread =A~@ bp  @p O&  @T <- ;h> filein- ;p> ternal e- ;x> rror: dip >nDp S =p  @p Op Sp AS >p @ p P p  Sp @ p  Sp  @    p  Sp  @ p P p   Sp AS~=!pread !=p  @    &  O <- ;> skreadr- ;> ead erro- ;> r from t- ;> emp filep >Dp S = - ;> ~="disk5 "=A~=#mouse5 #=A~=$button5 $=A~=%ccommand5 %=A~=&cxfidalloc5 &=A~='fontnames5 '=A~=(plumbsendfd5 (=A~=)font5 )=A~=*maxtab5 *=A~=+acmeerrorfile5 +=A~=,cxfidfree5 ,=A~=-home5 -=A~=.keyboardctl5 .=A~=/objtype5 /=A~=0messagesize5 0=A~=1cwait5 1=A~=cwarn5 =A~=colbutton5 =A~=reffont5 =A~=textcols5 =A~=but2col5 =A~=editing5 =A~=but3col5 =A~=seq5 =A~= screen5  =A~= mousetext5  =A~= globalautoindent5  =A~= row5  =A~= display5  =A~=globalincref5 =A~=mouseexit05 =A~=mouseexit15 =A~>.string5 >A~=boxcursor5 =HA~=bartflag5 =A~=cplumb5 =A~=mousectl5 =A~=argtext5 =A~=modbutton5 =A~=nullrect5 =A~=plumbeditfd5 =A~=activecol5 =A~=timerpid5 =A~=tagcols5 =A~=seltext5 =A~=cedit5 =A~=cputype5 =A~= typetext5  =A~=!activewin5 !=A~="fsyspid5 "=A~=#cnewwindow5 #=A~=$cerr5 $=A~=%snarfbuf5 %=$A~=&cexit5 &=A~>'blist5 '>A~=(ckill5 (=A~=)barttext5 )=AI id xfidopen(Xfid*); void xfidclose(Xfid*); voismacme/disk.c 664 0 0 4147 10453465216 12114ustar00rminnichsys#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"); } nel *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 imer Timer; typedef struct Window Window; typedef struct Xfid Xfid; struct Runestr { Rune *r; int nr; }; struct Range { int q0; int q1; }; struct Blocksmacme/ecmd.8 664 0 0 152110 10453465600 12046ustar00rminnichsys~E.string-E ;> bBnqUXY!&E  AXE *D pE  SwE Q pE  S~=utfruneE =pE @ &E AOE 4 no curr-H ;> ent windpH > DpH S~=editerrorH =pH @ wI Q pI  S~=cmdlookupI =pI  @pI @ ~?ipI ?pJ A ~?fpJ  ?&K AOK Mnone$9ab >pb A !b b yb pb ASb =pb @ pc = pd Rpd  @Wy  owdot e-n ; > xtends p-n ;(> ast end -n ;0> of buffe-n ;8> r during-n ;@> { commapn >Dpn Sn =pn @ pn  @ po ? po  xQpp ? pp  |Qpq  Spq  Sq  =pq  @ Wq  ndunkno-v ;P> wn comma-v ;X> nd %c in-v ;`> cmdexecpv >KDpv Swv Tpv Sv =pv  @pv @ pw Spw  Spw ? w A pw   =w x wx T&x {AOx  permiss- ;p> ion denip >iD p  S~@qp @p S~@ rp  @p Sp   S~=!eloginsert !=p A p = p  Sp =     A p  S~="erealloc "=p = p =a  Op Sp  @p Sp  @ p S~=#memmove #=p  @ =p =p = q APp A - ;x> edunkno- ;> wn state- ;> in editp >{D ~=$editingp $=& AO  <& AO  <& AO .string- ;> textno - ;> such fil~?bufa T?p Sp Ap Sp >Dp Sp ?p  Sp ?p S~=snprint =p ?p S~=free =a T?p S~=editerror =p ?p S =p ?  Ap S  =p 1? & ?AS  readloader  >A~@!nrp  !@ &  AS <~@"vp "@p S~@#q0p #@p S~@$rp $@p Sp   S~=%eloginsert %=p A  ~=&e_cmd" &=Ap" @ p* T ~?'fp*  '?~=(addrp+ (= p, (= ~?)q1p,  )?p- @w- O&- eAX-  e %.*Sp/ >Dp/ S/ =p/ '? p0 A p1 R p1  )?&3  A~?*q0p3  *?X3  can't op-= ;> en %s: %~?tmpa= T?p= Sp= Ap= Sp= >Dp= Sp= ?p=  S= =p> ?p> S> =a? T?p? S? =p? ? pA  S~=dirfstatA =pA  &B AOB > r%s is -F ;> a directaF T?pF SpF ApF SpF >DpF S~?spF ?pF  SF =pG ?pG SG =aH T?pH SH =pJ '?pJ SpJ *?pJ SpJ )?pJ SJ  =~? nullspK A ?pL ?pL SpL )?pL SaL  ?pL SpL > DpL  SpL '?pL S~= loadfileL  =pM ?pM SM =pN ?pN SN =&O  ?AOO {<-P ;> ory%s: -P ;> NUL byte-P ;> s elidedpP ASpP >DpP SpP ?pP S~= warningP  =WP <&Q +?AOQ <&Q 1?AXQ D p_   ?a`  ? W`  inte-m ;> rnal err-m ;> or: g_cm-m ;> d f!=addpm ASpm >Dpm Sm  =pn An pp @ pp P pp P pp  S~=rxcompilep =&p AXp <-q ;> r.f bad-q ;> regexp -q ;> in g compq >Dpq Sq =pr @ pr  Spr ASpr (= pr  Spr (= pr   S~=selpr =D pr  S~=rxexecuter =~@cppr @ pr @ wr R &r  vAXr #.string- ;#> mandmov- ; #> e overla- ;(#> ps itselp >#Dp S~=$error $= ~=%m_cmd %=8A~?&dota &?p Sp @p Op S~='mkaddr '=~?(addr2a (?p Sp @p Op Sa Sa &?p A ! y p AS~=)cmdaddress )=p @ p @w O& mAX _ fbad re- ;8#> gexp in - ;@#> s commanp >2#Dp S~=/editerror /=~?0nrpp A0?~?1rpp A1?~?deltap A?~?didsubp A?~=addrp = W  dreplac- ;P#> ement st- ;X#> ring toop >J#D~? errp  ?W D longri- ;h#> ght hand- ;p#> side to- ;x#> o long i- ;#> n substip >f#Dp  ?W  tutionn- ;#> o substip >#Dp S /=~@tp @p = p  xOp @p = p  |Op A W  tutionc-' ;#> an't wri-' ;#> te file -' ;#> with pen-' ;#> ding mod-' ;#> ificatiop' >#Dp' S' /=p' ? p(  S~@cpp( @ p( P p(  Sp( AS~=cmdname( =p(  &) A~?rp) ?X) <-* ;#> nsno na-* ;#> me speci-* ;#> fied for-* ;#> 'w' comp* >#Dp* S* /=p* ? p+  S~=runestrlen+ =~?.safep+ ?p+ ?p+ Sp+ =p+ Sp+ =p+ Sp+ ?p+  Sp+ ?p+ S~=putfile+ =p- A- - ~=x_cmd1 =Ap1 @ p1 @ p3 P&3 AO3  A> > ~=$runpipeB $=DA~@%crpI %@ pI  S~@&ncrpI  &@ pI  SaI -? pI  S~='skipblI '=~@(cmdpI (@ pI @ pI ?&J -?AXJ ;<~>).string-K ;)> mandno -K ;)> command -K ;)> specifie-K ;)> d for %cpK >)DpK SpK  SK /=pK (@ pK @ ~?*wpL A*?~@+state&M +@AXM P )Dp Sp Qp Op Sp Qp Op  S~=warning =p @ ~@charsonly& @AX R %.*S:%p ASp >)Dp Sp ?p S =p ? &  ?O M<- ;)> lud,%lup ASp >)Dp Sp  S =p ASp >)Dp S = - ;)> d #%dp ASp >)Dp Sp =p S =p =& =O a )Dp Sp =p S =p ASp >%)Dp S = ~=eq_cmd =A~@cpp @ W x ,#%d n- ;()> ewline e- ;0)> xpectedp >')Dp S~= editerror  =~?!charsonlyp !? W i1.stringp >81Dp Sp  S =p .? p -? p 0?  W  %S%c%c%- ;@1> c %.*S p ASp >;1Dp Sr  H1>p Sp +Ap  S&  AO Ip Sp Rp Sp Rp S = ~=loopcmd =A~@rpp @p )@p A W ] ' .ba- ;P1> d regexp- ;X1> in %c cp >N1Dp Sp @w Op S  =p A~?nrpp ?p! A ~?rpp!  ?p"  ?W"  ommandc- ;h1> an't nes- ;p1> t %c com- ;x1> mandYXp >g1Dp S~@#XYp #@ r  }1>p S~=$editerror $=C  =~@%cpp %@~=&loopstructp &=p #@p &=& &=AO  bad rege- ;1> xp in co- ;1> mmand adp >1Dp S $=~@+sign&  +@AU  <~@,fp ,@ p P p  Sp AS~@-pp -@ p  Sp A p   Sp =D p  S =p -@ & AX  <- ;1> dressno- ;1> match f- ;1> or regexp >1Dp S $=p -@ p =& =X  paddresp >1Dp S $=W C sno mat- ;1> ch for rp >1Dp S $=p -@ p =& =X 1 egexpadp >1Dp S $= ~=/cmdaddress /=<Ap +@ ~@0app 0@ ~@1ap 1@~?fp ?W%P .string- ;> dressca- ;> n't handp >Dp S $=p +@ p 0@ W Q <  &  AX ~ le 'add-;> resses i-;> n differ-;> ent filep>DpS$=p?p1@p?p1@p ?p 1@p 1@&1@P <-;> saddres-;> ses out -;> of orderp>DpS$=p@a1@pA !ypA p +@rQ&-AX cmdaddrp">Dp"S~= error" =p#@a#1@p#A !##y##p# 0@r#Q&#,AQ#C essno s-N;(> uch filepN>$DpNSpN?pNSN$=pO ?OO~=allmatchfileS=ApS@ pX@ sYQ%YAXY "%S"too-c;8> many fi-c;@> les matcpc>5DpcSpcPpcOpcSc$=pc ? pd pd@ pdP pd Odi=A~?tfpmA?pn*@pn?po=DpoSao?poSo'=&q?AXq <-r;H> h "%S"n-r;P> o file m-r;X> atches "pr>ODprSpr*@prOprSr$=ps?ssw=Ap*@ pP p S~=rxcompile=&AX <-;`> %S"bad -;h> regexp i-;p> n file mp>dDpS$=p A p S~=emalloc=~=curtextp= ~@fp@ ppRpOsO % AX atch%c%-;> c%c %.*S-;> ' .~?bufp?pSp ApSp>}DpSr >p Sp+ApS& AO pSpRpSpRpS~=snprint=p? p S~?ia? p S~=bytetorune=~?rbufp?p?pS~=free=pASp? p SpASp? p  S~?sa? p S~=rxexecute=~? matchp ?p?pS=p ?=Ap+@ ~@!lp!@ & AX= <~@"addrp  "@p "@WH <& APC < "@p"@p "@WH <& ASH <   "@p "@p"@&"@AUO address -;> out of rp>DpS$=p@a"@pA !y=,Ap+@ p!@ p"@~?#fp#?~?$ap$?& AU <& AXx <& AOi <& "@AOi angeadd-;> ress out-;> of rangp>DpS~=(editerror(=p#?p%? pU p Sp  C p %?p S&=p#?p'? p%? p!@ w& AX eaddres-;> s out ofp>DpS(=p#?p!@ p'? p%? W rangew-;> arning: -;> duplicat-;> e file n-;> ame "%.*pASp>DpSpPpSpPp S~=-warning-=~=.cmdname.=<A~@/strp/@ ~@0fp0@ ~?1rpA1?pPp'?pP&'?AXX .string- ; > S" ~= disk5  =A~= mouse5  =A~=button5 =A~=ccommand5 =A~=cxfidalloc5 =A~=ncollection5 =A~=fontnames5 =A~=plumbsendfd5 =A~=font5 =A~=maxtab5 =A~=acmeerrorfile5 =A~=cxfidfree5 =A~=home5 =A~=keyboardctl5 =A~=Enoname5 =A~=menu5 =A~=objtype5 =A~=messagesize5 =A~=nest5 =A~=cwait5 =A~= cwarn5  =A~=!colbutton5 !=A~="reffont5 "=A~=#textcols5 #=A~=$but2col5 $=A~=%editing5 %=A~=&but3col5 &=A~='sel5 '=PA~=(seq5 (=A~=)screen5 )=A~=*mousetext5 *=A~=+globalautoindent5 +=A~=,row5 ,=A~=-display5 -=A~=.globalincref5 .=A~=/mouseexit05 /=A~=0mouseexit15 0=A5  >A~=1boxcursor5 1=HA~=bartflag5 =A~=cplumb5 =A~=mousectl5 =A~=argtext5 =A~=modbutton5 =A~=nullrect5 =A~=plumbeditfd5 =A~=activecol5 =A~= timerpid5  =A~= tagcols5  =A~= seltext5  =A~= cedit5  =A~> none$95  > A~=addr5 = A~=loopstruct5 =A~=cputype5 =A~=typetext5 =A~=activewin5 =A~=fsyspid5 =A~=collection5 =A~=cnewwindow5 =A~=cerr5 =A~=Glooping5 =A~=snarfbuf5 =$A~=cexit5 =A~=ckill5 =A~=barttext5 =AI AO? essno s-N;(> uch filepN>$DpNsmacme/ecmd.c 664 0 0 57215 10453465220 12111ustar00rminnichsys#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: 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; 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; 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; 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'); } 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; 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); n = newname.nr; r = runemalloc(n+1); /* NUL terminate */ runemove(r, newname.r, n); free(newname.r); } 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; } { 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; icurtexsmacme/edit.8 664 0 0 57374 10453465605 12070ustar00rminnichsys~Elinex- ;> A~>wordx- ;> A- ;> A- ;> A~=cmdtab- ;= A- ;=A~=nl_cmd- ; ==D- ;=aA- ;=A- ;=A~=a_cmd- ;==D- ; =bA- ;(=>D~=b_cmd- ;,==D- ;0=cA- ;2=A- ;6=A~=c_cmd- ;<==D- ;@=dA- ;F=A~=d_cmd- ;L==D- ;P=eA- ;X=>D~= e_cmd- ;\== D- ;`=fA- ;h=>D~= f_cmd- ;l== D- ;p=gA- ;s=A- ;u=pA- ;v=A~= g_cmd- ;|== D- ;=iA- ;=A- ;=A~= i_cmd- ;== D- ;=mA- ;=A- ;=A~= m_cmd- ;== D- ;=pA- ;=A~=p_cmd- ;==D- ;=rA- ;=A- ;=>D- ;== D- ;=sA- ;=A- ;=A- ;=A~=s_cmd- ;==D- ;=tA- ;=A- ;=A- ;== D- ;=uA- ;=A~=u_cmd- ;==D- ;=vA- ;=A- ;=pA- ;=A- ;== D- ;=wA- ;=A- ;=>D~=w_cmd- ; ==D- ;=xA- ;=A- ;=pA- ;=A~=x_cmd- ;==D- ; =yA- ;#=A- ;%=pA- ;&=A- ;,==D-! ;0==A-! ;6=A-! ;8=>D~=eq_cmd-! ;<==D-" ;@=BA-" ;H=>D~=B_cmd-" ;L==D-# ;P=DA-# ;X=>D~=D_cmd-# ;\==D-$ ;`=XA-$ ;c=A-$ ;e=fA~=X_cmd-$ ;l==D-% ;p=YA-% ;s=A-% ;u=fA-% ;|==D-& ;=<A-& ;=A-& ;=>D~=pipe_cmd-& ;==D-' ;=|A-' ;=A-' ;=>D-' ;==D-( ;=>A-( ;=A-( ;=>D-( ;==D~=editthreadI =A~>.string-M ;> editthrepM >DpM S~=threadsetnameM =WN  adstrin- ;> g too lop ASp > Dp S~=warning = p =$Dp Sp AS =~=cmdstartpp = &  AO  ng Editp ASp >Dp S~?errp  ?p  S =p ? p  S =p =&Dp Sp AS = ~=getch =Ap  = w Qp  = w P '  X  : %s unp >&Dp S~=error = ~=getnum =A~?np A?~?signp A?~@signok& @AS < =& -AO  /=ApB )@ pB  SB =pB  ~=0stringlistpC =0DpC SpC 0=pC SpC  .?pC  SC &=pD .?D D ~=1newaddrH 1=ApL A pL  SL =pL  ~=addrlistpM =DpM SpM =pM SpM  .?pM  SM &=pN .?N N ~=freecmdR =AWV .string-e ;(> getchba-e ;0> d delimi-e ;8> ter %c pe >.Dpe Spe  S~=editerrore =e ~=atnli =Am =n =pn  &o  AOo <<-p ;@> newline -p ;H> expected-p ;P> (saw %Cpp >@Dpp Spp  Sp =p ~=Straddct = Apt +@ pv RCv pv R &v  Uv M< w  ARpx R px  Spx R x   px  Sx "=px +@ px Rpz RCz Rpz R az Ppz @ qz  Op{ Rp{ R a{ Pq{ AO{ ~= getrhs  =AW \ )bad ri- ;`> ght handp >ZDp S =p  ? &   AX v< =p \A W <&  nAX z sideco- ;p> mmand ta- ;x> kes no ap >nDp S =p ? s Q% AO f ddressn- ;> o addresp >Dp S =p !? p  S =p !? p  S~="getregexp "=p ? p ?w Q& sAX  sbad adp >Dp S =p ? s Q% AO <  =&  AX < = ,=p ?p ?p ? s P q  OW <~@$nestp $@ p  S =p ?& AX <- ;> dressdep >Dp S~=%error %=W  fcmdrig-! ;> ht brace-! ;> with no-! ;> left brp! >Dp! S! =p" A" -$ ;> aceunkn-$ ;> own commp$ >Dp$ Sw$ ?p$ S$ =W$  ;> and %cr-> ;> egular e-> ;> xpressio-> ;> n too lop> >Dp> S> =p> !? p? (?p? Sp?  S? =W? <&A   OA O<&A  AXA P ngno re-J ;> gular ex-J ;> pression-J ;> definedpJ >DpJ SJ =pK ,= pK P pK  SK /=pK ,= ~?.rpL .?pL OpL SpL QpL SpL Q L pL S~=/memmoveL /=pM .?M M Q #=(A~?0addrpV A0?pW A0?Wn  bad add-v ;> ress synpv >Dpv Sv =Ww  taxsimpp >$Dp S %=W  leaddrb- ;0> ad addre- ;8> ss syntap >/Dp S = 1=p  p a 0?p A ! y p   - ;@> x~=1disk5 1=A~=mouse5 =A~=button5 =A~=ccommand5 =A~=cxfidalloc5 =A~=fontnames5 =A~=editerrc5 =A~=plumbsendfd5 =A~=font5 =A~= maxtab5  =A~= acmeerrorfile5  =A~= cxfidfree5  =A~= home5  =A~= keyboardctl5  =A5 =A~=objtype5 =A~=messagesize5 =A~=cwait5 =A~=cwarn5 =A~=cmdendp5 =A~=colbutton5 =A~=reffont5 =A~=textcols5 =A~=addrlist5 = A~=but2col5 =A~=editing5 =A~=but3col5 =A~=seq5 =A~=screen5 =A~=stringlist5 = A~=mousetext5 =A5 ,=A~=globalautoindent5 =A~=row5 =A~= cmdlist5  = A~=!display5 !=A~="globalincref5 "=A~=#mouseexit05 #=A~=$mouseexit15 $=A~>%.string5 %>HA~=&boxcursor5 &=HA~='bartflag5 '=A~=(cplumb5 (=A~=)mousectl5 )=A~=*argtext5 *=A~=+modbutton5 +=A~=,curtext5 ,=A~>-linex5 ->A~=.nullrect5 .=A~=/plumbeditfd5 /=A~=0patset5 0=A~=1activecol5 1=A~=timerpid5 =A~>wordx5 >A~=tagcols5 =A~=cmdstartp5 =A~=seltext5 =A~=cedit5 =A~=cputype5 =A~=typetext5 =A~= activewin5  =A~= fsyspid5  =A~= cnewwindow5  =A~= cerr5  =A~= snarfbuf5  =$A~=cexit5 =A~=cmdp5 =A~=ckill5 =A~=barttext5 =AI  =p ? p ? &  ?AP  #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(void*)); l->nused = 0; }else if(l->nused == l->nalloc){ l->listptr = erealloc(l->listptr, (l->nalloc+INCR)*sizeof(void*)); memset(l->ptr+l->nalloc, 0, INCR*sizeof(void*)); l->nalloc += INCR; } } /* * Remove the ith element from the list */ void dellist(List *l, int i) { memmove(&l->ptr[i], &l->ptr[i+1], (l->nused-(i+1))*sizeof(void*)); l->nused--; } /* * Add a new element, whose position is i, to the list */ void inslist(List *l, int i, void *v) { growlist(l); memmove(&l->ptr[i+1], &l->ptr[i], (l->nused-i)*sizeof(void*)); l->ptr[i] = v; 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, p); return p; } String* newstring(int n) { String *p; p = allocstring(n); inslist(&stringlist, stringlist.nused, p); return p; } Addr* newaddr(void) { Addr *p; p = emalloc(sizeof(Addr)); inslist(&addrlist, addrlist.nused, 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'; s->n -= 2; }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; } rtp; 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((smacme/edit.h 664 0 0 4501 10453465222 12103ustar00rminnichsys#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 { int nalloc; int nused; union{ void *listptr; void* *ptr; uchar* *ucharptr; String* *stringptr; }; }; 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); llist(List *l, int i) { memmove(&l->ptr[i], &l->ptr[i+1], (l->nused-(i+1))*sizeof(void*)); l->nused--; } /* * Add a new element, whose position is i, to the list */ void inslist(List *l,smacme/elog.8 664 0 0 31033 10453465611 12046ustar00rminnichsys~EWsequence- ;>wA- ;>aA- ;>rA- ;>nA- ;>iA- ;>nA- ;>gA- ;>:A- ;> A- ; >cA- ; >hA- ; >aA- ; >nA- ; >gA- ;>eA- ;>sA- ;> A- ;>oA- ;>uA- ;>tA- ;> A- ;>oA- ;>fA- ;> A- ;>sA- ;>eA- ;>qA- ;>uA- ;>eA- ;>nA- ;>cA- ;>eA- ; > A~=eloginit0 =A~@fp0 @ v2 pQ&2 AO2 <3 q4 -ApQp5 lQ&5 AX5  warnedpW A >W ~= elogflush[  =(Ap[ @ v_ pP~? bq_  ?p` tPp`  ?pa xPpa  ?pb |Ppb  ?Wq s.string-e ;> unknown -e ;> elog typ-e ;> e 0x%ux pe ASpe >Dpe Sve pPpe S~=warninge =pe @ Wf OC~  >&~ AX~ Dp S =p @ p  S  =p @ p xQp @  p tQ   v pQ& rAX  interna- ; > l error:- ;(> replace- ;0> ment str- ;8> ing too - ;@> large(%dp >Dp Sp  S~=editerror =p @ p @ p Qp Sp  @p Sp   p S = ~=eloginsert =A&  @AX < p @p S =p @ v pQ& -AO C  >& AX Dp S =p @ p  S  =p @ v pQ& iAX C  >& AX `Dp S =p @ p  S  =p @ p @ v pQ& dAX l )elogap- ;P> ply: 0x%p Ap Sp >JDp Sv  ?p S~=#fprint #=~=$abort $=W <&  AX AXL <-M ;X> ux elog-M ;`> apply: c-M ;h> an't hap-M ;p> pen %d %pM ASpM >\DpM SpM xQpM SpM |QpM  SpM RpM SM =pM @ pM ? pN |Q pN  SpN R pN  S~=-minN -=pN ? pN |QpO xQ pO  SpO |Q pO  SO -=pO ? pO xPO -O ;x> d %d ~=.disk5O .=A~=/mouse5O /=A~=0button5O 0=A~=1ccommand5O 1=A5O >"A~=cxfidalloc5O =A~=fontnames5O =A~=plumbsendfd5O =A~=font5O =A~=maxtab5O =A~=acmeerrorfile5O =A~=cxfidfree5O =A~=home5O =A~= keyboardctl5O  =A~= objtype5O  =A~= messagesize5O  =A~= cwait5O  =A~= cwarn5O  =A~>warned5O >A~=colbutton5O =A~=reffont5O =A~=textcols5O =A~=but2col5O =A~=editing5O =A~=but3col5O =A~=seq5O =A~=screen5O =A~=mousetext5O =A~=globalautoindent5O =A~=row5O =A~=display5O =A~=globalincref5O =A~=mouseexit05O =A~=mouseexit15O =A~>.string5O >A~=boxcursor5O =HA~= bartflag5O  =A~=!cplumb5O !=A~="mousectl5O "=A~=#argtext5O #=A~=$modbutton5O $=A~=%nullrect5O %=A~=&plumbeditfd5O &=A~='activecol5O '=A~=(timerpid5O (=A~=)tagcols5O )=A~=*seltext5O *=A~=+cedit5O +=A~=,cputype5O ,=A~=-typetext5O -=A~=.activewin5O .=A~=/fsyspid5O /=A~=0cnewwindow5O 0=A~=1cerr5O 1=A~=snarfbuf5O =$A~=cexit5O =A~=ckill5O =A~=barttext5O =AIO smacme/elog.c 664 0 0 16176 10453465223 12133ustar00rminnichsys#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 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, * but using coordinates relative to the unmodified buffer. As we apply the log, * we have to update the coordinates to be relative to the modified buffer. * Textinsert and textdelete will do this for us; our only work is to apply the * convention that an insertion at t->q0==t->q1 is intended to select the * inserted text. */ /* * 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 (%d %d)\n", b.q0, b.q0+b.nd, t->q0, t->q1); 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); } if(t->q0 == b.q0 && t->q1 == b.q0) t->q1 += b.nr; break; case Delete: if(tracelog) warning(nil, "elog delete %d %d (%d %d)\n", b.q0, b.q0+b.nd, t->q0, t->q1); 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 (%d %d)\n", b.q0, b.q0+b.nr, t->q0, t->q1); 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); } if(t->q0 == b.q0 && t->q1 == b.q0) t->q1 += b.nr; 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); elogterm(f); /* * Bad addresses will cause bufload to crash, so double check. * If changes were out of order, we expect problems so don't complain further. */ if(t->q0 > f->nc || t->q1 > f->nc || t->q0 > t->q1){ if(!warned) warning(nil, "elogapply: can't happen %d %d %d\n", t->q0, t->q1, f->nc); t->q1 = min(t->q1, f->nc); t->q0 = min(t->q0, t->q1); } } DpM SpM xQpM SpM |QpM  SpM RpM SM =pM @ pM ? pN |Q pN  SpN R pN  S~=-minN -=pN ? pN |QpO xQ pO  SpO |Q pO  SO -=pO ? pO xPO -O ;x> d %d ~=.disk5O .=A~=/mouse5smacme/exec.8 664 0 0 174245 10453465617 12107ustar00rminnichsys~E.string- ;> Cut~=exectab- ;=>D~=cut- ;==D- ;=A- ; =A- ;=A- ;> Del- ;=>D~=del- ;==D- ;$=A- ;> Delc- ;(=>D~=delcol- ;,==D- ;4=A- ;8=A- ;> olD- ; > elet- ;<=>D- ;@==D- ;H=A- ;L=A- ;(> eDu- ;P=>,D~=dump- ;T==D- ;\=A- ;`=A- ;0> mpE- ;8> dit- ;d=>6D~=edit- ;h==D- ;p=A- ;t=A- ;@> Exit- ;x=>@D~=exit- ;|==D- ;=A- ;=A- ;H> Fon- ;=>JD~= fontx- ;== D- ;=A- ;=A- ;P> tGe- ;=>TD~= get- ;== D- ;=A- ;=A- ;X> tID- ;=>\D~= id- ;== D- ;=A- ;=A- ;`> Inc- ;=>bD~= incl- ;== D- ;=A- ;=A- ;h> lIn- ;p> dent- ;=>lD~= indent- ;== D- ;=A- ;=A- ;x> Kil- ;=>zD~=kill- ;==D- ;=A- ;=A- ;> lLo- ;=>D- ;==D- ;=A- ;> adL- ;> ocal- ;=>D~=local- ;==D- ;$=A- ;(=A- ;> Loo- ;,=>D~=look- ;0==D- ;8=A- ;<=A- ;> kNe- ;@=>D~=new- ;D==D- ;L=A- ;P=A- ;> wNe- ;> wcol- ;T=>D~=newcol- ;X==D- ;`=A- ;d=A- ;> Pas- ;h=>D~=paste- ;l==D- ;p=A- ;t=A- ;x=A- ;> teP- ;|=>D~=put- ;==D- ;=A- ;=A- ;> utP- ;> utal- ;=>D~=putall- ;==D- ;=A- ;=A- ;> lRe- ;=>D~=undo- ;==D- ;=A- ;> doS- ;> end- ;=>D~=sendx- ;==D- ;=A- ;=A- ;=A- ;> Snar- ;=>D- ;==D- ;=A- ;> fSo- ;=>D~=sort- ;==D- ;=A- ;=A- ;> rtT- ;=>D~=tab- ;==D- ;=A- ;=A- ;> abU- ;> ndo- ;=>D- ; ==D- ;=A- ;=A- ;> Zero- ;=>D~=zeroxx- ; ==D- ;(=A- ;,=A~=lookup =$A~@rp @ p  S~@np @ p  Sa @ p  S~=skipbl =& @AX  AO G .string-+ ;  > x`arg-+ ;( > ument st-+ ;0 > ring toop+ ASp+ >$ Dp+ S~= warning+  =, . A p0 xA p1 R&1 AX1  long %-5 ;@ > c%d %d %-5 ;H > d %d %.*p5 Rp5 Sp5 >? Dp5 Sp5  /?p5  Sp5  Sp5  Sp5  Sp5  ?p5 Sp5  ?p5 Sp5 1?p5  S~=winevent5 =p5 ,? W5 (<-7 ;P > S %c%d -7 ;X > %d %d 0 p7 Rp7 Sp7 >S Dp7 Sp7  /?p7  Sp7  Sp7  Sp7  Sp7  ?p7 S7 =p7 ,? &8  *@X8 . %c%d %-< ;h > d 0 %d %p< Qp< Sp< >b Dp< Sp<  Sp<   Sp<  Sp<  ?p< Sp<  ?p< Sp< 1?p< S< =W< Y<-> ;p > .*S %c%-> ;x > d %d 0 0p> Qp> Sp> >u Dp> Sp>  Sp>   Sp>  Sp>  ?p> S> =&@ ?AO@ <-A ; > %c0 0-A ; > 0 %d %spA ? pA  S~=utflenA =pA #?pA )@pA OpA SpA > DpA SpA /?pA SpA #?pA  SpA ?pA SA =pA ? &B  AOB ~<-C ; > %c0 0 -C ; > 0 %d %s pC  SC =pC #?pC )@pC OpC SpC > DpC SpC /?pC SpC #?pC  SpC ?pC SC =WC <-E ; > %c0 0 0pE )@pE OpE SpE > DpE SpE /?pE SE =pG 1?pG SG  =pH ?pH SH  =pI ?pI SI  =J &L  AOL  0 %.*~?bufpq ?pq Spq > Dpq Spq Ppq Opq Spq Ppq Opq  Spq  S~=sprintq =Wq ;<-s ; > S:#%d%.-s ; > *S:#%d,#ps ?ps Sps > Dps Sps Pps Ops Sps Pps Ops  Sps  Sps  Ss =pt ?t t ~=getargx =HApx @ ~@rpp~  @p~ AO~@nrpp @p AO&  AX H %dcan't- ; > delete - ; > column; - ; > %.*S is - ; > running - ; > an exter- ; > nal commp ASp > Dp Sp Qp Op Sp Qp Op  S  = W  ? p>  Sp> ? p>  S> =~?sp> ?p? ?p? S? =p@ ? p@  S~= strlen@  =p@ ? &@ AX@ 9$.string-[ ;$> and %.*-[ ;$> S is a d-[ ;$> irectory-[ ;$> ; Zerox -[ ;$> illegal p[ ASp[ >$Dp[ Sp[ Qp[ Op[ Sp[  @p[ Qp[ Op[  S~=%warning[ %=W[  no filepx ASpx >!$Dpx Sx %=y p{ @p{ Op{ O&{ AS{ <~?*namep|  *?p|  S~=+dirstat| +=p|  &} AO}  name %- ;0$> s is a d- ;8$> irectory- ;@$> ; can't - ;H$> read wit- ;P$> h multip- ;X$> le windo- ;`$> ws on itp ASp >/$Dp Sp  S %= p  *?p  Sa ? p  S~=-bytetorune -=p @ p ?p A W  %s not- ;p$> written- ;x$> ; file a- ;$> lready ep ASp >j$Dp Sp S %=W <- ;$> xists %- ;$> s modifi- ;$> ed%s%s s- ;$> ince las- ;$> t read p ASp >$Dp Sp Sp 8T r P %  AO $DW $Dp  Sp 8Tp S %=W  by ca- ;$> n't crea- ;$> te file - ;$> %s: %r p ASp >$Dp Sp *?p S %=W  %s not w- ;$> ritten; - ;$> file is - ;$> append op ASp >$Dp Sp *?p S %=W )<~@q0p @ W  nly %.*p ? p  Sp  A p  Sp >$D p  Sp ? p   Sp ? p  S~=snprint =p  ? p  Sp ? p  S~?mp ?p S~=write =p @p @ p ? & ?O *<- ;$> Scan't - ;$> write fi- ;$> le %s: %p ASp >$Dp Sp *?p S %=W  r no fi- ;$> le name p ASp >$Dp S %= p *?p S~?nnamea ? p  S -=p ? p  p  Sp ASp Qp Sp   Sp ?p S =p *?p S~= free  = ~=!dump !=Ap @ &  AO  A.?p? MA p@ R&@ AO@ .<&E .?AOE =.stringp >"Dp Sp Ap  Sp Ap S =p )@ p  Sp Pp Op Sp Pp Op S  = ~=edit =$A& -@AX < p @p Sp ASp Ap S~?ra ?p  S~?lena ?p S =p -@ ~=seqC =& ?AO  no- ;(> auto-Pu- ;0> t of %s:p ASp >&Dp Sp -?p S~=/warning /=W N %r /mn- ;@> t/acme/%p ASp >=Dp Sp Pp Op S /= ~=local =<A~@argtp @ p  Sp A p  Sp A p  Sa -? p   S~=getbytearg =~?aap ?~?dira ?p Sp @p Sp ASp A S~= dirname  =& ?AX  ?p> Sp> @p> Sp>  @>  ? > p> S> =~>.string-? ;H> d/ fi-? ;P> xvap? ? p?  Sp?  @ ?  ? p?  Sp? >LD p?  Sp? A p?   S~=runeeq? =&? AX? % <~?rp? ? p?  Sp?  @ ?  ? p?  Sp? >TD p?  Sp? A p?   S? =&? AX? % rfi-K ;`> xvapK ? pK  SpK  ? pK  SpK >\D pK  SpK A pK   SK =&K AXK _ dD pK  SpK A pK   SK =&K AXK _ rfipU ? pU  S~=runestrlenU =pU  ?pU ? pU  SpU  ? pU  SpU >lD pU  SpU A pU   SU =pU ?WU  <&V ?AXV  x%S p ASp >tDp Sp .?p DOp  /?a  Op Op S~=warning =p /? W i xDp S = ~>indentval >A~@np @ &  AP  ON~@sp @ p  Sp >zD p  Sp  S~=runestrncmp =& AX  <~= globalautoindentp A =- ;> Indent Op ASp >Dp S =p A - ;> N OFp @ p  Sp >D p  Sp @ p  S =& AX  FInde- ;> nt OFF p ASp >Dp S =p A p @ p  Sp >D p  Sp @ p  S =& AX  p  W  p  & .?AO  on%.- ;> *S: Tab p ASp >Dp Sp Qp Op Sp Qp Op  Sp tQp S = ~=runproc =A~@argvpp @ pP~?winp?pP~?sp?pP~?rdirp?p P~?ndirp?pP~?newnsp?pP~?argaddrp?pP~?argp?pP~? cp ?p P~?!cpidp!?p$P~?"iseditcmdp"?p S=p? W +.stringp&>+Dp&S~=,strcat&,=p'&? p' Sp' ? 'A p' S~=-bytetorune'-=p' ? p'Pp(&?p(S(=p(? p(? p( ? p($? ~?.pipecharp)A.?r*P&*<AO* AO* %d %.p7>+D p7 Sp7T p7P p7 Sp7T p7P p7 S~=smprint7=~?filenamep7?p8?p8@O&9Ap91?S9E  Qp>Sp>?p>DOa> Op>Op>Sp>? >p>S>'=p>? W># +DpISpI?pIS~= sprintI =-J;+> *S%dwipJ>+DpJSaJ ?pJS~= putenvJ =pJ? &L AOLi +DpMSpM SM =pN?pNSN=pP? pP SpP? pP SpP? pP SpP1? pP  S~= fsysmountP =pP ? pPQpQQ&QAXQ <-R;+> nid%ch-R;+> ild: can-R;+> 't mount-R;+> /dev/co-R;+> ns: %r pRApRSpR>+DpRS~=fprintR=pS>+DpSS~=threadexitsS=pUAS~=closeU=pU.? pU? &V ASV <&V |AOV <&V >AXV mount/m-W;+> nt/acme/-W;+> %d/rdselaW ?pWSpW>+DpWSpW SW =aX ?pXSpXAS~=openX=WX <-Z;+> /dev/nupZ>+DpZSpZASZ=p[Ap[S[=p["? p[.? p[? &\ AQ\ <&\ AX\ ll/mnt/-_;+> acme/%d/-_;+> editouta_ ?p_Sp_> +Dp_Sp_ S_ =W_ <-a; +> /mnt/acm-a;(+> e/editouaa ?paSpa> +DpaSa =Wa <-c;0+> t/mnt/a-c;8+> cme/%d/wac ?pcSpc>2+DpcSpc Sc =ad ?pdSpdApdSd=peApeSe=-f;@+> rsel/depf>E+DpfSpfApfSf=Wf <-h;H+> v/cons/-h;P+> dev/consph>O+DphSphAphSh=piApiSpiApiS~=dupi=Wi /dev/nupo>Y+DpoSpoASo=ppAppSp=~=acmeerrorfilepq=DpqSpqApqSq=prAprSprAprSr=&u?AOu llacmeapy>c+DpySpy?pySy =pz$? pz Sz)=&zASz ddr#;&|-;p+> ^$=`'{}(-;x+> )<>[]*?^p>l+D p Sw? p S~=utfrune=p#? &AO* ~`%s/%sa ?pSp>+DpSp SpRp S =p!?pSa ?pSp?pS=p? -;+> /bin/%sa ?pSp>+DpSpRpS =p!?pSa ?pSp?pS=W+DpSpApS'= A?p= p S)=Cp p? a  ?pSp=pSp ?p S'=p? ?-;+> ./binp? a  ?pSp>+DpSpApS'= A?-;+> /env/patp>+D p SpA p SpA p S~= create =~?!fdp!?pSa ?pSp?pS~="write"=p!?pS=~?#arg&#?AO h%s '%s~?$newsp$?pSp>+DpS~?%tp%?pSp#?p S =~?&sp&?pS~='free'=p$? p %?~?(cp(?p  O-;+> '/bin/r-;+> crc-c~?)cpidp)?pSp>+DpSp>+DpSp>+Dp Sp%?pSpAS~=*procexecl*=W-.string-;-> runwaittp>-DpS~=.threadsetname.=~@/vp/@ pPp(?pPp)?p S'=W' ask~=disk5=A~=mouse5=A~=button5=A51=A~=cxfidalloc5=A~>parg$1495>A~=fontnames5=A~=plumbsendfd5=A~=font5=A~=maxtab5=A~=acmeerrorfile5=A~=cxfidfree5=A~=home5=A~=keyboardctl5=A~=objtype5=A~= messagesize5 =A~=!cwait5!=A~="cwarn5"=A~=#colbutton5#=A~=$reffont5$=A~=%textcols5%=A~=&but2col5&=A~='editing5'=A~=(but3col5(=A~=)exectab5)=DA~=*seq5*=A~=+screen5+=A~=,mousetext5,=A~=-globalautoindent5-=A~=.row5.=A~=/display5/=A~=0globalincref50=A~=1mouseexit051=A~=mouseexit15=A~>.string5>A~=boxcursor5=HA~=bartflag5=A~=cplumb5=A~=mousectl5=A~=argtext5=A~=modbutton5=A~= nullrect5 =A~= plumbeditfd5 =A~= activecol5 =A~= timerpid5 =A~= tagcols5 =A~=seltext5=A~=cedit5=A~=cputype5=A~=typetext5=A~=activewin5=A~=fsyspid5=A~=cnewwindow5=A~=cerr5=A~=snarfbuf5=$A~=cexit5=A~=ckill5=A~=barttext5=AISpI?pIS~= sprintI =-J;+> *S%dwipJ>+DpJSaJ ?pJS~= putenvJ =pJ? &L AOLi +DpMSpM SM =pN?pNSN=pP? pP SpP? pP SpPsmacme/exec.c 664 0 0 67137 10453465225 12136ustar00rminnichsys#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]+w->nopen[QWxdata] > 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); if(dir.nr>0 && r[dir.nr]!='/' && n>0 && arg[0]!='/') r[dir.nr++] = '/'; 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"); } } enum { IGlobal = -2, IError = -1, Ion = 0, Ioff = 1, }; static int indentval(Rune *s, int n) { if(n < 2) return IError; if(runestrncmp(s, L"ON", n) == 0){ globalautoindent = TRUE; warning(nil, "Indent ON\n"); return IGlobal; } if(runestrncmp(s, L"OFF", n) == 0){ globalautoindent = FALSE; warning(nil, "Indent OFF\n"); return IGlobal; } 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; w = nil; if(et!=nil && et->w!=nil) w = et->w; autoindent = IError; 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(w != nil){ switch(autoindent){ case Ion: case Ioff: w->autoindent = autoindent; break; case IGlobal: w->autoindent = globalautoindent; break; } } } 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 = (uintptr)argv[3]; newns = (uintptr)argv[4]; argaddr = argv[5]; arg = argv[6]; c = argv[7]; cpid = argv[8]; iseditcmd = (uintptr)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) memmove(name, e+1, strlen(e+1)+1); /* strcpy but overlaps */ 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); } 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; }smacme/file.8 664 0 0 27747 10453465622 12062ustar00rminnichsys~E.string- ;> can't fi- ;> nd text - ;> in filedp >Dp S~= error  =p ? p @ W 6 eltexti- ; > nternal - ;(> error: f- ;0> ileinserp >Dp S  =p @ p P& AS g tintern- ;@> al error- ;H> : filedep >:Dp S  =p  @ p @ p @ p P& AS   =Ap@ @p@ O&@ AS@ <<-A ;P> leteund-A ;X> o in fil-A ;`> e.load u-A ;h> nimplemepA >UDpA SA  =pB @ pB  SpB  @ pB  S~@!fdpB !@ pB  S~@"nullspB  "@ pB   S~=#bufloadB #=B B ~=$fileredoseqG $=,ApL @ L HA pM P&M AXM O ntedund-y ;x> o: 0x%uxpy Apy Spy >uDpy Svy ?py S~=+fprinty +=~=,abortz ,=W{  .string- ; > ~= disk5  =A~= mouse5  =A~= button5  =A~= ccommand5  =A~=cxfidalloc5 =A~=fontnames5 =A~=plumbsendfd5 =A~=font5 =A~=maxtab5 =A~=acmeerrorfile5 =A~=cxfidfree5 =A~=home5 =A~=keyboardctl5 =A~=objtype5 =A~=messagesize5 =A~=cwait5 =A~=cwarn5 =A~=colbutton5 =A~=reffont5 =A~=textcols5 =A~=but2col5 =A~=editing5 =A~= but3col5  =A5 =A~=!screen5 !=A~="mousetext5 "=A~=#globalautoindent5 #=A~=$row5 $=A~=%display5 %=A~=&globalincref5 &=A~='mouseexit05 '=A~=(mouseexit15 (=A5  >A~=)boxcursor5 )=HA~=*bartflag5 *=A~=+cplumb5 +=A~=,mousectl5 ,=A~=-argtext5 -=A~=.modbutton5 .=A~=/nullrect5 /=A~=0plumbeditfd5 0=A~=1activecol5 1=A~=timerpid5 =A~=tagcols5 =A~=seltext5 =A~=cedit5 =A~=cputype5 =A~=typetext5 =A~=activewin5 =A~=fsyspid5 =A~= cnewwindow5  =A~= cerr5  =A~= snarfbuf5  =$A~= cexit5  =A~= ckill5  =A~=barttext5 =AI  O Asmacme/file.c 664 0 0 13125 10453465226 12116ustar00rminnichsys#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; }   AT fsysflush- ;=>D~>fsysversion- ;=>D~>fsysauth- ;=>D~>fsysattach- ;=>D~>fsyswalk- ;=>D~>fsysopen- ;=>D~>fsyscreate- ;=>D~> fsysread- ;=> D~> fsyswrite- ;=> D~> fsysclunk- ;=> D~> fsysremove- ;=> D~> fsysstat- ;=> D~>fsyswstat- ;=>D~=Eperm- ;=pA- ;=eA- ;=rA- ;=mA- ;=iA- ;=sA- ;=sA- ;=iA- ;=oA- ; =nA- ; = A- ; =dA- ; =eA- ; =nA- ;=iA- ;=eA- ;=dA~=Eexist- ;=fA- ;=iA- ;=lA- ;=eA- ;= A- ;=dA- ;=oA- ;=eA- ;=sA- ; = A- ; =nA- ; =oA- ; =tA- ; = A- ;=eA- ;=xA- ;=iA- ;=sA- ;=tA~=Enotdir- ;=nA- ;=oA- ;=tA- ;= A- ;=aA- ;= A- ;=dA- ;=iA- ;=rA- ; =eA- ; =cA- ; =tA- ; =oA- ; =rA- ;=yA~=dirtab~>.string- ;=>D- ;=A- ; =@A- ;=>D- ;=A- ;=A- ;=@A- ;> .acmec- ; =>D- ;(=A- ;,=A- ;> onscons- ;0=> D- ;8=A- ;> ctldraw- ;@=>D- ;D=A- ;H=A- ;L=A- ;> editout- ;P=>D- ;X=A- ;\=A- ;`=>!D- ;h=A- ;l=A- ; > indexl- ;p=>'D- ;x=A- ;|=A- ;(> abelnew- ;=>-D- ;=A- ;=A- ;=@A~=dirtabw- ;=>1D- ;=A- ; =@A- ;0> .addr- ;=>3D- ;= A- ;=A- ; =>8D- ;$=@A- ;(= A- ;,=@A- ;8> bodyctl- ;0=>=D- ;8= A- ;<=A- ;@=>AD- ;H= A- ;L=A- ;@> dataed- ;P=>FD- ;X= A- ;\=A- ;H> itouter- ;`=>ND- ;h=A- ;l=A- ;P> rorseve- ;p=>UD- ;x=A- ;|=A- ;X> ntrdsel- ;=>[D- ;=A- ;=A- ;=>aD- ;=A- ;=A- ;`> wrselt- ;=>gD- ;=@A- ;=A- ;=@A- ;h> agxdata- ;=>kD- ;=A- ;=A- ;p> Wile E.- ;x> Coyote~=user- ;=>qD~=messagesize- ;= A~=fsysinit =$A~?pa ? p  S~=pipe =& AP  <- ;> can't cr- ;> eate pipp >Dp S~=error =p ?~>cfdp >p ?~>sfdp >p FAp S~=fcallfmtp =Dp S~=fmtinstall =- ;> e/dev/tp >D p  Sp  A p  S~=open =~= clockfdp  =- ;> ime/devp >D p  Sp AS =& AU /<~?!fdp !?p S~?"bufa "? p  Sp A p  S~=#read #=& AS , p/  Sp/ "?p/ Sp/ = p/  S~=*read9pmsg/ *=p/ (? ~?+np/ +?&0 +?AQ0 Q<~>,closing&1 ,>AO1 M /useri/-3 ;> o error -3 ;> on serve-3 ;> r channep3 >Dp3 S3 =p3 (? &5  AX5 [<~=-cxfidallocp6 -=p6 Sp6 AS~=.sendp6 .=p7 -= p7  S~=/recvp7 /=p7  p9 "? p9   Qp: "? p:  Sp: +? p:  Sp:   p:  (? : A p:  S~=0convM2S: 0=p: (? &: +?O: m<-; ;> lconver-; ;> t error -; ;> in convMp; >Dp; S; =p; (? s> R&> =AX> x<-? ;> 2Sbad f-? ;> call typp?  S~?1ta? 1? p?  Sp? >D p?  S~=respond? =p? (?W?  efid no-N ;> t in usepN  SaN 1? pN  SpN >D pN  SN =pN (?WO 9.string- ;> fsysdel- ;> id: can'- ;> t find ia "?p Sp >Dp Sp @p Op S~=sprint =a "? p  S $=~?.safep ?~=cerrp =p Sp ?p S .= ~=fsysmount = Ap >p S %=p @ p  Sp  @ p  Sp  @ p  Sp  @ p   S =p  - ;> d %d %da "?p Sp >Dp Sp  ?p Pp S =- ;> /mnt/acp > p  Sp A p  Sp >D p  Sp A Sa "? p  S~=mount =& AP Qp S %=- ; > me/mnt/- ;(> acme/mnp >#Dp Sp >-Dp Sp AS~=bind =- ;0> t/wsys/- ;8> mnt/acmep >7D p  Sp >AD p  Sp A p  S =p ? & AP h~>cfdp >p S %=~>sfdp >p S %= =A~@errp @ ~@ tp  @ ~@!xp !@ &  AO } /devco- ;H> nvert er- ;P> ror in cp >FDp S~=$error $=p +? p > p  Sp !@ p  P p  Sp  S~=%write %=& +?O <- ;X> onvS2Mw- ;`> rite err- ;h> or in rep >_Dp S $=p !@p  Op S =p !@p A O ~>&fsysversion &>$Ap !@ p Q& AL <- ;p> spondve- ;x> rsion: m- ;> essage s- ;> ize too p  S~?'ta '? p  Sp >vD p  S = p Q p  "=p  '?- ;> small9Pp Q p  Sp >D p  Sp A p  S~=(strncmp (=p !@ & AO <- ;> 2000unr- ;> ecognize- ;> d 9P verp  Sa '? p  Sp >D p  S = - ;> sion9P2p >Dp '?p  Sa '? p  Sp AS = ~>)fsysauth )>$A- ;> 000acme- ;> : authen- ;> tication- ;> not reqp !@ p  Sa '? p  Sp >D p  S = ~>*fsysflush *> Ap !@p Op S~=+xfidflushp =+Dp S .=p A ~>,fsysattach ,>,Ap !@ p P p  S~=-userp -= p  S~=.strcmp .=p !@ ~@/fp /@ & AO  uiredun- ;> known id- ;> in attap >D p  S~=estrdup =p ?p =p Sp ?p S~=sendp =p =Dp S  =p !@ p  Sa '? p  Sp AS~=respond =  ~>fsyswalk >`A~?nfp' A?~?wp( A?p) /@p) O&) AO) M<-* ;> chwalk -* ;> of open p* !@ p*  Sa* '? p*  Sp* >D p*  S* =* p+ !@p+ Op+ !@ p+ P &+  O+  filenew-. ;> fid alre-. ;> ady in up. !@ p.  Sa. '? p.  Sp. >D p.  S. =. p/ ?p/ AOp0 ?p0 AOp1 ?p1 (Q p1  (Op2 (Q&2 AO2 q A ?~? dirp? A ?p@ /@ @  A p@ Qp@ Q @ A @ A~? idp@  ?pA /@ A  A~? qaA  ?pA A !A A yA pC !@wC O&C ATC hD pJ  SJ .=&J AXJ  se..na-T ; > me too lpT >DpT  ?WU .string-v ;(> ongnew~?ipv ? pv !@ av  R pv P pv  Spv >,D pv  Sv .=&v AXv A<&w ?AOw +<-x ;0> w set in-x ;8> walk topx >0Dpx Sx $=~=cnewwindowpy =py Spy ASy =pz = pz  S~=recvpz =p{ ? { Ap{ S{  =o| A p} ?p} O} Ap}  p~ ?p~ Op~  ?p =Dp  ?W <&  ?AX Efsysopen >(Ap !@ AQs Q& AO  fsyscreate  >$Ap !@ p  Sa '? p  Sp =0D p  S = ~>!idcmp !>A~@"ap "@p O~@#bp #@ p P   ~>$fsysread $>Ap /@ ~@%xp %@ s Q A& AO !Dp  S~=qsort =p ? p A ~?bufa ?~?dtp ?W  new%dp ?p Sp >EDp Sp  ?p  S~=sprint =p -? p ? p   Ap ?o A?p A?p  Sa ? p  Sp ,? p   a  R p  Sp %@ p P    p   Sp )? p  S .=p ? p 1? p  & AX  fsyswrite/  > Ap1 %@p1 Op1 S~=xfidwritep1 =Dp1 S1  =p2 A2 2 ~>fsysclunk7 > A~@fp9 @p9 (Op9 S9 =p: %@p: Op: S~=xfidclosep: =Dp: S:  =p; A; ; ~>fsysremove@ >$ApD %@ pD  SaD  ? pD  S~=EpermpD =D pD  SD  =D D ~>fsysstatI >0ApM *= M A pM  SM +=pM  ?N (=~?.safepN ?pN %@ pN R N  A pN R pN R N A N A pN  SpN @ pN  P pN  SpN  ? pN  SpN *= N A pN   SpN ? pN  SN .=wN qN  ?pO %@ pO  SaO  ? pO  SpO ASO  =pO %@pP  ?pP SP =pQ %@Q Q ~>fsyswstatV >$ApZ %@ pZ  SaZ  ? pZ  SpZ =D pZ  SZ  =Z Z ~=newfid^ =A~@fidp^ @ pb A pc   c A ~>fidsac  >~?fhpd ?pd O Wd H@A~=(button5 (=A~=)ccommand5 )=A~=*cxfidalloc5 *=A~=+fontnames5 +=A~=,plumbsendfd5 ,=A~=-font5 -=A~=.maxtab5 .=A~=/acmeerrorfile5 /=A~=0cxfidfree5 0=A~=1home5 1=A~=keyboardctl5 =A~=objtype5 =A~=messagesize5 =A~=cwait5 =A~=cwarn5 =A~>closing5 >A~>cfd5 >A~=colbutton5 =A~= reffont5  =A~= textcols5  =A~= but2col5  =A~= editing5  =A~= but3col5  =A~=Enotdir5 =A~=mnt5 =A~>sfd5 >A~=seq5 =A~=screen5 =A~=mousetext5 =A~=dirtabw5 =A~=dirtab5 =A~=globalautoindent5 =A~=row5 =A5 =A~=display5 =A~=globalincref5 =A~=mouseexit05 =A~=mouseexit15 =A~>.string5 >HA~=boxcursor5 =HA~=bartflag5 =A~=cplumb5 =A~= mousectl5  =A~=!argtext5 !=A~="modbutton5 "=A~=#nullrect5 #=A~=$plumbeditfd5 $=A~=%activecol5 %=A~=&Eperm5 &=A~='timerpid5 '=A~=(tagcols5 (=A~=)seltext5 )=A~=*cedit5 *=A~=+clockfd5 +=A~=,cputype5 ,=A~=-typetext5 -=A~=.activewin5 .=A~=/fsyspid5 /=A~=0Eexist5 0=A~=1cnewwindow5 1=A~=cerr5 =A~=snarfbuf5 =$A~=fcall5 =A~=cexit5 =A~=ckill5 =A~=barttext5 =AI =p{ ? { Ap{ S{  =o| A p} ?p} O} Ap}  p~ ?p~ Op~  ?p =Dp  ?W <&  ?AX E #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 }, { "errors", QTFILE, QWerrors, 0200 }, { "event", QTFILE, QWevent, 0600 }, { "rdsel", QTFILE, QWrdsel, 0400 }, { "wrsel", QTFILE, QWwrsel, 0200 }, { "tag", QTAPPEND, QWtag, 0600|DMAPPEND }, { "xdata", QTFILE, QWxdata, 0600 }, { 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{ switch(x->type){ case Tversion: case Tauth: case Tflush: f = nil; break; case Tattach: f = newfid(x->fid); break; default: f = newfid(x->fid); if(!f->busy){ x->f = f; x = respond(x, &t, "fid not in use"); continue; } break; } 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*) { Fcall t; return respond(x, &t, "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); } 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(messagesmacme/look.8 664 0 0 107262 10453465633 12120ustar00rminnichsys~E.string- ;> %c%d %d - ;> %d %d %.p @p Op Sp >Dp Sp  ?p Sp @p  Sp @p Sp  ?p Sp  Sp  Sp ?p  S~=winevent =p ?p S~=free =W }<- ;> *S %c%d- ;> %d %d 0p Qp Sp >Dp Sp  Sp  Sp  Sp  ?p Sp  S =p @& ?X  %c%d -& ;(> %d %d %dp& @p& Op& Sp& >#Dp& Sp&  ?p& Sp& ?p&  Sp& ?p& Sp&  ?p& Sp&  Sp&  Sp& ?p&  S& =W& <-( ;0> %.*S %-( ;8> c%d %d %p( @p( Op( Sp( >7Dp( Sp(  ?p( Sp( ?p(  Sp( ?p( Sp(  ?p( Sp(  S( =p) ?p) S) =W* s<~=plumbsendfd&, =AU,  d 0 acp/ >FD p/  S~=estrdup/ =p/ ? p/ Qp0 AQ~?dira1 ?p1 Sp1 @p1 Sp1 ASp1 A S~=dirname1 =&2 ?AX2  metextp< >KD p<  S< =p< @ p< @ p< ? p<  Pp= ?p= AO~?bufo> A?p? @&?  X?  click=%daM ?pM SpM >PDpM SpM ?M  pM S~= sprintM  =aN ? pN  S~=!plumbunpackattrN !=pN @ pN ? pN PpQ @ Q   pQ  @ Q   pQ  SQ =pQ @ pQ  pR @pR OpR SpR  SpR  ?pR  SpR @R  pR  SR =pS ? pS  SpS @ S @ pS  SS =pS ? pS QpT Q pT  S~="strlenT "=pT ? pT PpU ?pU SU =pU ? pV Q~=#messagesizepV #= V A &V  PV  insanel- ;`> y long f- ;h> ile name- ;p> (%d byt- ;x> es) in p- ;> lumb mes- ;> sage (%.- ;> 32s...) p ASp >YDp Sp Pp Sp Pp  S~=warning = ~?ep A?p A?p Pr O& AX ^< p A?p Pp ?p ? p  Sa ? p  S~=bytetorune =p ?p A?p A?p A?p @ p P p  Sp >D p  S~= plumblookup  =& AO y addrfip @ p P p  Sp >D p  S  =p @ p  & AX <~? namebufa  ?~?namep ?~=nuntitledC =- ;> lenameU- ;> ntitled-a  ?p Sp Ap Sp >Dp Sp =p  S~=snprint =p @ p ? p A?p  ?r Q& /AO .string- ;> %d%s/%sp ?p Sp ?p Sp >Dp Sp @p Op  Sp ?p S =p ? p  ?p  S "=p *?p ?p Sp *?p S~?rba ?p Sa ?p  S~?nra ?p Sp AS~=cvttorunes =p ?p S~=free =~?rsa ?p Sa Sp *?p *?a ? p  Op *?p ? p  O~=cleanrname =p  ?p Sp ?p Sp ?p S~=winsetname =p @ p P   p  S =p @ p  p Qp Sp Qp S~?rp  ?p  Sa ?p  Sa ?p Sp AS =p  ? Ap Sp ASp ?p Sp ?p  Sp Ap S~=textinsert =p ?p S =p  ? p Pp AOo APp  S~=winsettag =p  ? Ap S~=textscrdraw =p  ?p  Ap Sp Pp Op Sp Pp Op S (= )=4Ap @ &  AO .<~@ctp @p Op O&  R . string - ;> too longp ASp >Dp S =p A p     p  Sp  A p  S~= max  =~?!maxnp !?p  A p  S =p @p @~?"sp "?p "? p A q A R~?#aroundp A#?p |U W O .-+p >D p  Sw &@ p  S '=& AO  /:%S~? ap3  ?p3 Sp3 >Dp3 Sp3 @p3 Sp3  @p3  S~@ filep3  @p3 S~= sprint3  =p4  ? p4  Sp4 AS~= access4  =~? np4  ?p5  ?p5 S5 =&6  ?AP6 1 /%.*S/p: ? p: ? a:  Qp: Sp: >Dp: Sp: Ap: S: =p; ? p; ? a;  Q ; Ap; Sp;  @p; Sp;  @ ; p; S; =p<  @p< S< =p= @p= Sa= Sp= ?p= ?p= ? p=  Op= ?p=  @ = ? C=  p=  O= == = ~=includenameC =A~=objtypepC = ~@npC  @ ~@rpC @ ~>objdir&J >AXJ m<&J  AXJ n.string-K ;> /%s/in~?bufaK |?pK SpK >DpK SpK  SK  =aL |? pL  S~?iaL p? pL  S~=bytetoruneL =pM >pM SpM p? M   M A pM  S~=ereallocM =pM  @ pM @ pM >pN p?pN > qN AP~@tpQ @pQ O &R  AOR  clude/-\ ;> sys/-\ ;> incl-\ ;> udea\ t?p\ Sp\ >Dp\ Sp\  Sp\   S\ =p\  @ p\ @ &] t?AX] <&] >AX] p^ Sp^  Sp^   S^ =p^  @ p^ @ &_ t?AX_ AO #objdir5 #>A~=$objtype5 $=A~=%messagesize5 %=A~=&nuntitled5 &=A~='cwait5 '=A~=(cwarn5 (=A~=)colbutton5 )=A~=*reffont5 *=A~=+textcols5 +=A~=,but2col5 ,=A~=-editing5 -=A~=.but3col5 .=A~=/seq5 /=A~=0screen5 0=A~=1mousetext5 1=A~=globalautoindent5 =A~=row5 =A~=display5 =A~=globalincref5 =A~=mouseexit05 =A~=mouseexit15 =A~>.string5 >A~=boxcursor5 =HA~= bartflag5  =A~= cplumb5  =A~= mousectl5  =A~= argtext5  =A~= modbutton5  =A~=nullrect5 =A~=plumbeditfd5 =A~=.rathole5 =A~=activecol5 =A~=timerpid5 =A~=tagcols5 =A~=seltext5 =A~=cedit5 =A~=cputype5 =A~=typetext5 =A~=activewin5 =A~=fsyspid5 =A~=cnewwindow5 =A~=cerr5 =A~=snarfbuf5 =$A~=cexit5 =A~=ckill5 =A~=barttext5 =AI W  #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 && 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); } } = 0; addr = plumblookup(m->attr, "addr"); if(addr != nil){ e.ar = bytetorune(addr, &e.a1); e.agetc = plumbgetc; } opesmacme/mkfile 664 0 0 1045 10453465232 12200ustar00rminnichsys syms for(i in ????.c) 8c -aa $i >> syms turn 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 = smacme/regx.8 664 0 0 61021 10453465640 12067ustar00rminnichsys~E.string-" ;> regexp: p" ASp" >Dp" S~@ep" @p" S~= warning"  =p# =p# Sp# AS~= sendp#  =p$ AS~= threadexits$  =$ ~= newinst(  =A~= progpp(  = ~=program&*  =0DM* #<-+ ;> %s expr-+ ;> ession t-+ ;> oo longp+ > Dp+ S+ =p+  = ~@tp, @ p,  Qp- AQp. AQp/   /  A p/   =/ / ~=realcompile3 =A-8 ; > regcompp8 > Dp8 S~=threadsetname8 =~@argp9 @p: S~=startlex: =~=atorstackp; =D~=atorpp; =~=andstackp< =D~=andpp< =~=subidstackp= =D~=subidpp= =~=cursubidp> A=~=lastwasandp? A=pA ApA S~=pushatorA =WB A unmatchepN >(DpN SN = O A=pP =pP SpP =pP OpP SP  =pQ ASQ  =Q ~="rxcompileV "=(A~@#rp[ #@ p[  S~=$runestrlen[ $=C[ ~?%nrp[ %?p\ = p\  S\ $=C\ ~?&.safep\ &?p\ = p\  Sp\ &? p\  Sp\ #@ p\  Sp\ %? p\   S~='runeeq\ '=&\ AX\ ~ d `('un- ;8> matched p >6Dp S =p @ &  AX  `)'oper- ;H> and stac- ;P> k overflp >DDp S~=error =p = ~@fp @ p  Q~@lp @ p  Q A p  = =Ap =& =PDM '<~> .string- ;X > owopera- ;` > tor stac- ;h > k overflp >[ Dp S =p = A=p @ p  O& = AU 1 owmissi- ;x > ng opera- ; > nd for %~? bufa  ?p Sp >s Dp Sp  S~= sprint  =a  ?p S~=regerror =W J<- ; > cmalfor- ; > med regep > Dp S = A=p = ~=popator =Ap =& =DR U<- ; > xpopera- ; > tor stac- ; > k underfp > Dp S = A= A=p =p O  =$AW _ lowunkn- ; > own rege- ; > xp operap > Dp S =W g A W? 1 tormalf-X ; > ormed `[pX > DpX SX =pX = wY P&Y \AXY < Z A w[ P&[ nAX[ < \ A p\  =p]  A] p_   _ A p_  =w_ O_ A_ pa   a A pa  =wa Oa a e =$Apj A pj  S~=emallocj =pj  ~?classppj ?pk A ~?npk  ?~? napl  A ?pn =wn O&n ^AXn  ]'malfo-x ; > rmed `[]px > Dpx Sx =px ? pz ? z A&z  ?Uz < {  A ?p|  Sp|  ? |   p|  S| =p|  p| ?p~ =w~ O&~ -AX~ <  A= =p ?p ? p  & ]AX .string- ;> 'regexp- ;> list ov- ;> erflow p ASp >Dp S~=warning =p A=W <~>semptyp  >p  Sp =p Sp >Dp S (=p  ?p  = p /? p  W    regexp l- ;> ist overp ASp >Dp S =p A=W p  Sp =p Sp >Dp S (=p  ?p  = p /?p   W Z flow ~=disk5 =A~=mouse5 =A~=button5 =A~=ccommand5 =A~=cxfidalloc5 =A5 =A~=fontnames5 =A~= nclass5  =A~=!plumbsendfd5 !=A~="font5 "=A~=#maxtab5 #=A~=$acmeerrorfile5 $=A~=%cxfidfree5 %=A~=&home5 &=A~='keyboardctl5 '=A~=(atorp5 (=A~=)negateclass5 )=A~=*nbra5 *=A~=+objtype5 +=A~=,messagesize5 ,=A5 =XA~=-cursubid5 -=A~=.cwait5 .=A~=/progp5 /=A~=0cwarn5 0=A~=1colbutton5 1=A~=reffont5 =A~=textcols5 =A~=backwards5 =A~=subidstack5 =PA~=but2col5 =A~=editing5 =A~=but3col5 =A~=sel5 =PA~= seq5  =A~= screen5  =A5  =A~= mousetext5  =A~= globalautoindent5  =A~= row5  =A~=tl5 =A~=subidp5 =A~=Nclass5 =A~=display5 =A~=globalincref5 =A~=mouseexit05 =A~=mouseexit15 =A~>.string5 >A~=exprp5 =A~>sempty5 >PA~=boxcursor5 =HA~=andstack5 =A~=bartflag5 =A~=cplumb5 =A~=mousectl5 =A~=argtext5 =A~=modbutton5 =A~=program5 =0A~= nullrect5  =A~=!plumbeditfd5 !=A~="atorstack5 "=PA~=#activecol5 #=A~=$lastregexp5 $=A~=%timerpid5 %=A~=&tagcols5 &=A~='bstartinst5 '=A~=(seltext5 (=A~=)cedit5 )=A~=*lastwasand5 *=A~=+cputype5 +=A~=,typetext5 ,=A~=-activewin5 -=A~=.fsyspid5 .=A~=/andp5 /=A~=0cnewwindow5 0=A~=1cerr5 1=A~=snarfbuf5 =$A~=rechan5 =A~=cexit5 =A~=class5 =A~=ckill5 =A~=barttext5 =AI A-?p -?= ,A =D~= nlp  =p  =p AOp ?~?ntlp ?p A?& =AP <& 1?AO <&  .@L .string- ;> 'regexp- ;> list ov- ;> erflow p ASp >Dsmacme/regx.c 664 0 0 37271 10453465233 12152ustar00rminnichsys#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; } } 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 havsmacme/rows.8 664 0 0 115223 10453465644 12144ustar00rminnichsys~E.string- ;> Newc- ;> ol K- ;> ill - ;> Puta- ; > ll D- ;(> ump - ;0> Exitp  ?p Sp ASp >Dp Sp Ap  Sp Ap S~=textinsert =p  ? p  Sp Pp Op Sp Pp Op S~=textsetselect = ~=rowadd =TAp @~?dp A?p   A~?ra ?p A !  y p PT  Ap ?~@xp @& ?P  can'-{ ;@> t find cp{ ><Dp{ S~=error{ =p{ ? Wz %<&~  AX~ (< p -= p P~?pp ?p Pp ?p ? /? p  S~=abs =p ?p @ p 1@ & AP ? olumnca- ;P> n't find- ;X> columnp >NDp S =p  @ p ? W *.string- ;`*> can't fi- ;h*> nd file - ;p*> for dump- ;x*> : $home - ;*> not defip ASp >`*Dp S~=+warning +=W 9<- ;*> ned %s/- ;*> acme.dump Sp >*Dp Sp  S~=,sprint ,=p )? p  '@p  Sp A p  Sp A p  S~=-create -=& A~?.fdp .?P :<- ;*> pcan't - ;*> open %s:p ASp >*Dp Sp '@p S +=W! ? %r %s p& /?p& Sp& >*Dp& S~=wdirp& =Dp& S~=Bprint& =p' /?p' Sp' >*Dp' S~=fontnamesp' =p' S' =-( ;*> %s %s p( /?p( Sp( >*Dp( Sp( =p( S( =p) A W) b*Dp+ Sp+ Pp+  @ p+ P +  a+ Oa+ O+ A+ p+  @ p+ P p+  @ p+ R +   ~?.safep+  ?:+ ?p+ S+ =p+ /? p,  @p, O/, &, #?X,  `U&> AX> *D~?fontnamepI ?pJ pRpJ O~= font&J  =OJ  %11dx-R ;*> %11d %11-R ;*> d %11d %-R ;*> 11d %11dpR /?pR SpR >*DpR SpR #?pR SpR PpR OpR  SpR hTpR SpR lTpR SpR TpR U R  aR OaR OR AR pR  U pR U R   pR  ?:R ?pR SpR SR =WR  %s e%1-X ;*> 1d %11d -X ;*> %11d %11-X ;*> d %11d %pX /?pX SpX >*DpX SpX #?pX SpX PpX OpX  SpX ASpX ASpX TpX U X  aX OaX OX AX pX  U pX U X   pX  ?:X ?pX SpX SX =WX  s f%11d-_ ;*> %11d %1-_ ;*> 1d %11d -_ ;*> %11d %s p_ /?p_ Sp_ >*Dp_ Sp_ #?p_ Sp_ Tp_  Sp_ hTp_ Sp_ lTp_ Sp_ Tp_ U _  a_ Oa_ O_ A_ p_  U p_ U _   p_  ?:_ ?p_ Sp_ S_ =W_  F%11d %-f ; *> 11d %11d-f ;(*> %11d %1-f ;0*> 1d %11d pf /?pf Spf >*Dpf Spf #?pf Spf ?pf  Spf hTpf Spf lTpf Spf Tpf U f  af Oaf Of Af pf  U pf U f   pf  ?:f ?pf Spf Tpf Opf Spf  Sf =pk  ?pk S~=freek =pl ?pl Spl )?pl Spl AS~=winctlprintl =pm )? pm  S~=strlenm =pm ?pm /?pm Spm )?pm Spm ?pm S~=Bwritem =pn  A pn  Spn ? pn P pn P pn  S~=minn =pn  po ?po Opo Spo ASpo 1?po S~?mpo  ?po   S~=bufreado =po ?po 1? pp A Wq  %s %.*Spt /?pt Spt ><*Dpt Spt  Spt   St =pt ? &u  ?AOu A*Dp} Sp} ?p} Sp} 1?p}  S} =p} ? p} ? p} ? p~ ? ~  W~  %.*S%sp /?p Sp >F*Dp Sp dPp Sp `Pp  S =W 0<- ;H*> %s %sp /?p Sp >M*Dp Sp `Pp S =WG 1rdline >A~@bp @ p  Sp  A p  S~=Brdline =p  & AO O<~@linepp @C Op   ~=rowloadfonts =Ap '@ p  Sp AS~=Bopen =& AX Z< ~? bp  ?p Sp  A p  S =& AX b+.string- ;P+> can't - ;X+> find fil- ;`+> e for lo- ;h+> ad: $hom- ;p+> e not dep ASp >R+Dp S~=,warning ,=W <- ;x+> fined %- ;+> s/acme.dp Sp >+Dp Sp  S~=-sprint -=p *? p  (@p  Sp AS =& AX <- ;+> umpcan'- ;+> t open l- ;+> oad file- ;+> %s: %r p ASp >+Dp Sp (@p S ,=W + <~?.linep A.?p  ?p Sa .? p  S >& Ap !?X  can't c- ;+> hdir %s p ASp >+Dp Sp !?p S ,=W & Ap !?X & Ap !?X p  ? & AX AP & AX & Ap !?X +D p  S~?nra ? p  S~=bytetorune =~?rp ?W  ./%s/~?tp# ?p# Sp# >+Dp# Sp# )=p# S# -=p$ ? p$  Sa$ ? p$  S$ =p$ ?p% ?p% S~=free% =W% !&* Ap* !?X* *AP2 QA p;  S; =p; ?W<   S> "=p> !? &> >AP> k'rdline` '>&a A~?(lpa (?Xa /.string-u ;/> /tmp/d%d-u ;/> .%.4sacm~=0getpidu 0=pu h ?~=1getuseru 1=pu d ?~?bufpu ?pu Spu >/Dpu Spu h ?pu Spu d ?pu  S~=sprintu =pv ? pv  Spv AA pv  Spv A pv  S~=createv =&w A~?fdpw ?Pw h ecan't -y ;/> create t-y ;/> emp filepy ASpy >/Dpy S~=warningy =Wz  : %r ba- ;/> d load f- ;/> ile %s:%p ASp >/Dp S~@filep @p Sp  ?p  S =p &?p S  =W , d ~=disk5 =A~=mouse5 =A~=button5 =A~=ccommand5 =A~= cxfidalloc5  =A~=!fontnames5 !=A~="plumbsendfd5 "=A~=#font5 #=A~=$maxtab5 $=A~=%acmeerrorfile5 %=A~=&cxfidfree5 &=A~='home5 '=A~=(keyboardctl5 (=A~=)objtype5 )=A~=*messagesize5 *=A~=+cwait5 +=A~=,cwarn5 ,=A~=-colbutton5 -=A~=.reffont5 .=A~=/textcols5 /=A~=0but2col5 0=A~=1editing5 1=A~=but3col5 =A~=seq5 =A~=screen5 =A~=mousetext5 =A~=globalautoindent5 =A5 =A~=display5 =A~=globalincref5 =A~=mouseexit05 =A~= mouseexit15  =A~> .string5  >A~= boxcursor5  =HA~= bartflag5  =A~= cplumb5  =A~=mousectl5 =A~=argtext5 =A~=modbutton5 =A~=nullrect5 =A~=plumbeditfd5 =A~=activecol5 =A~=timerpid5 =A~=tagcols5 =A~=seltext5 =A~=cedit5 =A~=cputype5 =A~=typetext5 =A~=activewin5 =A~=fsyspid5 =A~=cnewwindow5 =A~=cerr5 =A~=snarfbuf5 =$A~=cexit5 =A~= ckill5  =A~=!barttext5 !=AI  Sa .? p  S >& Ap !?X +D p  S~?nra ? p  S~=bytetorune =~?rp ?W  #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){ free(fontnames[i]); fontnames[i] = estrdup(l); } } Return: Bterm(b); } int 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, 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); 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){ free(r); 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); } free(r); 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); fbuffree(buf); return TRUE; Rescue2: warning(nil, "bad load file %s:%d\n", file, line); Bterm(b); Rescue1: fbuffree(buf); return FALSE; } void allwindows(void (*f)(Window*, void*), void *arg) { int i, j; Column *c; for(i=0; inw; j++) (*f)(c->w[j], arg); } } .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, smacme/scrl.8 664 0 0 23040 10453465650 12065ustar00rminnichsys~Escrpos >A~@p1p @ ~@totp @ ~?qa ?~@ra @p A ! y p ? ?p  &  AX <~@.retp @a ?p A ! y &  AT <  A ~@p0  A@  A & @AT ! scrtmpp  >p S~= freeimage  =~? .safea  ? p  Sp ASp ASp  A p   S~= screenp  = p P p  S~= Rect  =~=displayp = p  Sa Sa  ?p A ! y p  = p ,P p  Sp ASp A p  S~=allocimage =& Ap  >X c<~>.string- ;> scroll ap >Dp S~=error = ~=textscrdraw =hA~@tp @ p Q& AO nAX s< =p @ p   A~?ra ?p A ! y p  >~?bp ?~?r1a ?a ?p A ! y p A?p ? ?p ?~?r2a ?p Sa Sa ?p A ! y p tQp Sw ZQp tQ  p Sp Qp Op S >a Sa ?p A ! y p @ Aa Sp A ! y ~=eqrect =p @ & AX !alts$7p !>p A!>p A!>p Pp !>p  !>p A!>p A0!>W !D p  S~=$alt $=& AO <& AO  lloc~= disk55  =A55 -=A~= button55  =A~= ccommand55  =A~= cxfidalloc55  =A~= fontnames55  =A~=plumbsendfd55 =A~=font55 =A~=maxtab55 =A~=acmeerrorfile55 =A~=cxfidfree55 =A~=home55 =A~=keyboardctl55 =A~=objtype55 =A~=messagesize55 =A~=cwait55 =A~=cwarn55 =A~=colbutton55 =A~=reffont55 =A~=textcols55 =A~=but2col55 =A~=editing55 =A~=but3col55 =A~=seq55 =A~= screen55  =A~=!mousetext55 !=A~="globalautoindent55 "=A~=#row55 #=A~=$display55 $=A~=%globalincref55 %=A~=&mouseexit055 &=A~='mouseexit155 '=A~>(.string55 (>A~>)scrtmp55 )>A~=*boxcursor55 *=HA~=+bartflag55 +=A~=,cplumb55 ,=A~=-mousectl55 -=A~=.argtext55 .=A~=/modbutton55 /=A~=0nullrect55 0=A~=1plumbeditfd55 1=A~=activecol55 =A~=timerpid55 =A~=tagcols55 =A~>alts$755 ><A~=seltext55 =A~=cedit55 =A~=cputype55 =A~=typetext55 =A~= activewin55  =A~= fsyspid55  =A~= cnewwindow55  =A~= cerr55  =A~= snarfbuf55  =$A~=cexit55 =A~=ckill55 =A~=barttext55 =AI5 664 0 0 23040 10453465650 12065ustar00rminnichsyssmacme/scrl.c 664 0 0 5473 10453465235 12131ustar00rminnichsys#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); 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; p0 = (vlong)t->file->nc*(y-s.min.y)/h; if(p0 >= t->q1) p0 = textbacknl(t, p0, 2); 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); } ! y p ?p Sa Sa ?p A ! y p Qp Sp AS~=ZPa = p Pp smacme/smacme.8 664 0 0 126504 10453465553 12422ustar00rminnichsys~E.string-m ;> /lib/fon-m ;> t/bit/lu-m ;> cidasans-m ;> /euro.8.~=fontnames-m ;=>D-n ; > font/li-n ;(> b/font/b-n ;0> it/lucm/-n ;8> unicode.-n ;=>%D~=derrorx =A~@errorstrpz @pz S~=errorz =z ~=threadmain~ =DAp Ap S~= rfork  =~@ argvp  @ ~? ncolp A ?~? loadfilep A ?~= argv0&  =AX  9.fontp >GD p  ?r R %  AO THD p  ?r T %  AO qID p  ?r T %  AO JD p  ?r T %  AO  usage- ;P> : acme -- ;X> a -c nco- ;`> l -f fon- ;h> tname -F- ;p> fixedwi- ;x> dthfontn- ;> ame -l l- ;> oadfile p Ap Sp >KDp S~=fprint =p >Dp S~=exits =p ? p  @ W H usagecp >D p  S~=getenv =~=cputypep =- ;> putypeop >D p  S =~=objtypep =- ;> bjtypehp >D p  S =~=homep =- ;> ometabsp >D p  S =& AO <~?pp ?p Sp ASp AS~=strtoul =~=maxtabp =p ?p S~= free  =& =AX  topfontp >Dp Sp =p S~="putenv "=- ;> /dev/snp >D p  Sp  A p  S~=#open #=p = p =&  AO <- ;> arf/acm- ;> e/bin/%s~?$bufa $?p Sp >Dp Sp  S~=%sprint %=a $?p Sp >Dp Sp Ap S~=&bind &=- ;> /bin/a- ;> cme/binp >Dp Sp >Dp Sp Ap S &=p =Dp Sp Ap S~='getwd '=- ;> /binacmp ASp =D p  Sp = p  Sp >D p   Sp ASp A p  S~=(geninitdraw (=& AP 1<- ;> eacme: - ;> can't op- ;> en displ- ;> ay: %r p Ap Sp >Dp S =- ;> geninitdp >Dp S =~=)displayp )=p O ~=*fontp  *=~=+reffontp  +=p =+D~=,reffontsp ,=p =+Dp S~=-incref -=p =+Dp S -=p A p  S~=.emalloc .=~=/nfontcachep A/=~=0fontcachep 0=p =+D p  O~=1iconinit 1=~=timerinit =~=rxinit =~=threadwaitchan =~=cwaitp =p A p  Sp AS~=chancreate =~=ccommandp =p A p  Sp AS =~=ckillp =p A p  Sp AS =~=cxfidallocp =p A p  Sp AS =~= cxfidfreep  =p A p  Sp AS =~= cnewwindowp  =p A p  Sp AS =~= cerrp  =p A p  Sp AS =~= ceditp  =p A p  Sp AS =~= cexitp  =p A p  Sp A p  S =~=cwarnp =& =AO {<& =AX |.string- ;> rawacme- ;> : can't - ; > create i- ;(> nitial c- ;0> hannels:p Ap Sp >Dp S =- ;8> %r chap >=Dp S =p AS~=screenp = p  S~=initmouse =p  & A~=mousectlp =X <- ;@> nnelsac- ;H> me: can'- ;P> t initia- ;X> lize mou- ;`> se: %r p Ap Sp >FDp S =p >hDp S =p = ~=mousep  =p AS~=initkeyboard =& A~=keyboardctlp =X <- ;h> mouseac- ;p> me: can'- ;x> t initia- ;> lize key- ;> board: %p Ap Sp >nDp S~=fprint =- ;> r keybop >Dp S~=exits =~=getpid =~=mainpidp =- ;> ardeditp >D p  Sp  A p  S~=plumbopen =& A~=plumbeditfdp =U D p  Sp !A p  S =~=plumbsendfdp =~= fsysinit  =~=!diskinit !=~?"loadfilep "? ~=#diskp #=&  AO <~=$rowp =$D p  Sp  Sp A p  S~=%rowload %=& AO  sendin- ;> itializi- ;> ng colump >Dp S~=+error +=p )? W  ns/guid& AO ;D p  S~=1strcmp 1=p )? & AX ; APp?  S~=winsettag? =p@ ? @ Ap@ S~=textscrdraw@ =p@ ? pA   A ApA SpA PpA OpA SpA PpA OpA S~=textsetselectA =A ~> .string-E ; > edelete~=!oknotes-E ;!=> D-F ; > hangup-F ;!=> D-G ;!=> D-H ; > killexi-H ; !=> DO  =AS  =-T ; > tkille~="dumping&T "=AXT <~@#msgpT #@ pT  SpT > D pT  ST 1=&T AXT  D pT  ST 1=&T AXT  xitacmep[ > Dp[ Sp[ #@p[ S~=)print[ )=~=*abort\ *=p] A] ] a  =A~=+fsysclosee +=~=,commandpi ,= Wi  : %s hapj Apj Spj  -?pj Ppj Spj > Dpj S~=.postnotej .=Wj <~=/acmeerrorfilepk =/Dpk S~=0removek 0=k ~=1acmeerrorprocq 1= A-v ; > ngupacm-v ; > eerrorprpv > Dpv S~=threadsetnamev =pw  A pw  S~=emallocw =~?bufpw ?Wx 8errorfdpx > px  Spx ? px  Spx  A px  S~=readx =px ? &x AUx 7 occan't- ; > create p > Dp S~= error  =- ; > pipe/sr- ; > v/acme.%~= getuser  =p ?p =/Dp Sp > Dp Sp ?p Sp %=p  S~=sprint =p =/D p  Sp A p  Sp A p  S~=create =& A~?fdp ?P { s.%dcan- ;( > 't creat- ;0 > e acmeer- ;8 > ror filep >% Dp S  =a ?p Sp >A Dp Sp  ?p S =a ? p  S =p ?p ?p Sa ?p Sp ?p S~=write =p ?p S~=close =- ;@ > %d/fd/a ?p Sp >D Dp Sp  ?p S =a ? p  Sp  A p  S~=open =& Ap >P <- ;H > %dcan't- ;P > re-open- ;X > acmeerr- ;` > or filep >K Dp S  =p  ?p S =p  ?p S =p =1Dp Sp ASp  Ap S~=proccreate = ~=plumbproc =A- ;h > plumbprop >h Dp S =W alts$34p A>p A>p A>~=keyboardctlp =p Op >~?ra ?p >p A>p A0>~?timerp A?~= typetextp A =~>!.string- ;p!> ckeyboa- ;x!> rdthreadp >r!Dp S =W p A>W p A>W @p A>p = p P p  Sa ? p  S~=/nbrecv /=& AS ID p  S~=0alt 0=& AO <& AO  mousethp >!Dp S =p = ~=mousectlp = p P~>alts$41p >p A>p A>p Pp >p  >p A>p  (>~?pma ?p ,>p A0>~=cwarnp =p <>p A@>p AD>&  AX rp AX>W} v readatt- ;!> ach to wp >!Dp S  =~= scrlresize  =p =)Dp S~= screenp  =  Aa Sp A !  y ~= rowresize  =W <- ;!> indowtep ? p  P p  Sp >!D p  S~= strcmp  =& AX <- ;!> xtactiop ? p P p  Sp >!D p  S~= plumblookup  =- ;!> nshowfi& AO <~?actp ?p Sp >!D p  S  =& AO  leshowdp ? p  Sp >!D p  S  =& AX  ?p> S> &=W@ MD p{  S{ 0=&{ AO{ <&{ AO{ <&{ AO{ <&{ AO{ ..string- ;.> atawaitp >.Dp S~=/threadsetname /=~?0pidsp A0?~=1cerrp 1=~?altsp l?~?erra ?p p?p At?~=ckillp =p ?~?cmda ?p ?p A?~=cwaitp =p ?a ?p ?p A?~=ccommandp =p ?~?ca ?p ?p A?p A?~=commandp A=W  thread%p ASp >.Dp Sp ?p S~= warning  =p ?p S~= free  =~= displayp  =p Sp Ap S~= flushimage  =p =,Dp S~=qunlock =W <~?foundp A?p ? p  S~=runestrlen =~?ncmdp ?p =p ?W @.D p  S~=postnote =& AP `<- ;.> skillk- ;.> ill %S: p ASp >.Dp Sp ?p S  =p A?W ;<& ?AX j<- ;.> %r Kill- ;.> : no pro- ;.> cess %S p ASp >.Dp Sp ?p S  =p ?p S  =W  libthre~?wp ? p P p  Sp >.D p  Sp  A p  S~=strncmp =& AO .Dp Sp ?p Op S  =p  =p Sp Ap S  =p =,Dp S =p ?p S  =W <& ?AO  ad%s %p ?p Op Sp >.Dp Sp  p  ? Ap S  =p  ? p ? &  AX # s xfida- ;.> llocthrep >.Dp S /=~=%cxfidallocp %=~>&alts$75p &>p A&>p A&>~='cxfidfreep '=p &>~?(xa (?p &>p A&>p A0&>~?)xfreep A)?W3 s&D p1  S1 #=p1 )? &1 AO1 u<&1 AO1 /.string-< ;/> adnewwi-< ;/> ndowthrep< >/Dp< S~=0threadsetname< 0=WD  adcan't-[ ;(/> open fo-[ ;0/> nt file -[ ;8/> %s: %r p[ ASp[ >#/Dp[ Sp[  @p[ S~=warning[ =p\ A\ p^ A p^  S^ =~?rp_ ?p_ ? p_  Op`  = p`  Sp`  = ` A ` A p`  S~=erealloc` =p` ? p`  pa  =Ca  =pa   =aa Qpa  OWW <~@save&d @AOd  internal- ;H/> error: - ;P/> can't fi- ;X/> nd font - ;`/> in cachep ASp >@/Dp S =W a =pA- ;? =A- ;@ =A- ;A =A- ;B =A- ;C =A- ;D =A- ;E =A =LAp  = p  Sp A p  Sp A p  S~=!allocimagemix !=~="tagcolsp "=~?#.safea #? p  Sp ASp ASp A p   Sp A p  S~=$Rect $=p  = p  Sa Sa #?p A ! y ~=%screenp %= p ,P p  Sp A p  Sp A p  S~=&allocimage &=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  = p "=p Qp  "=p Qp "=p  Sp A p  Sp A p  S !=~='textcolsp '=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 '=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 LA p  S &=p  = ~=(buttonp (= p '=p Qp  '=p Qp '=&  AO .string- ;h> /dev/sp >jD p  Sp A p  S~=open =~?fdp ?& ?AP  < p A W  narf%.*p ? p  Sp >uD p  Sp  ? p  Sp = D p   S~= fprint  =~? ip  ? & AP  S~=disk5 =A~=mouse5 =A5 (=A~=ccommand5 =A~=cxfidalloc5 =A~=fontnames5 =A~=plumbsendfd5 =A~=font5 =A~=maxtab5 =A~=fontcache5 =A~=acmeerrorfile5 =A~=cxfidfree5 =A~= home5  =A~=!keyboardctl5 !=A~="objtype5 "=A~=#mainpid5 #=A~=$messagesize5 $=A~=%cwait5 %=A~=&cwarn5 &=A5 +=A~='reffont5 '=A~>(errorfd5 (>A~=)textcols5 )=A5 =A~=*editing5 *=A5 =A5  =A~>+alts$415 +>dA~>,alts$345 ,><A~=-seq5 -=A~=.screen5 .=A~>/alts$755 /><A~=0wdir5 0=A~=1mousetext5 1=A~=globalautoindent5 =A~=row5 =A~=oknotes5 =A~=nfontcache5 =A~=display5 =A~=globalincref5 =A~=mouseexit05 =A~=mouseexit15 =A~> .string5  >A~= command5  =A~= boxcursor5  =HA~= bartflag5  =A~= cplumb5  =A~=mousectl5 =A~=argtext5 =A~=modbutton5 =A~=nullrect5 =A~=plumbeditfd5 =A~=.rathole5 =A~=activecol5 =A~>arg$25 >A~=timerpid5 =A~=tagcols5 =A~=seltext5 =A~=cedit5 =A~=cputype5 =A~=typetext5 =A~=dumping5 =A~=activewin5 =A~=fsyspid5 =A~=cnewwindow5 =A~= cerr5  =A~=!snarffd5 !=A~="snarfbuf5 "=$A~=#cexit5 #=A~=$reffonts5 $=A~=%ckill5 %=A~=&barttext5 &=AI smacme/smacme.c 664 0 0 46235 10453465237 12456ustar00rminnichsys#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 fontnames[0] = estrdup(fontnames[0]); fontnames[1] = estrdup(fontnames[1]); quotefmtinstall(); 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)){ 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; } /* scroll buttons, wheels, etc. */ if(t->what==Body && w != nil && (m.buttons & (8|16))){ if(m.buttons & 8) but = Kscrolloneup; else but = Kscrollonedown; winlock(w, 'M'); t->eq0 = ~0; texttype(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 = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*)); fontcache[nfontcache++] = r; } Found: if(save){ incref(r); if(reffonts[fix]) rfclose(reffonts[fix]); reffonts[fix] = r; if(name != fontnames[fix]){ free(fontnames[fix]); fontnames[fix] = estrdup(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); } smacme/text.8 664 0 0 206577 10453465737 12156ustar00rminnichsys~E.stringp >D p  S~=stringwidth =p @ = ?q XQp Q& AX <! p" *?p" Op" +? p" P "  " " & =@Ap& @ p, Rp, O&, AS, L<- p. R p.  Sp. >D p.  S. =~?-mintp. -?p0 = p0  Sp0 A p0  S0 =|0 -?p0 @ q0 XPp1 @ w1 XT p1  ?p2 Ap3 AW3 c A W>  00 pM @pM OpM SpM ?pM SpM >DpM SpM ApM  SM =pM ?CN pO -? O ?WU DpR SpR ApR  SR =pR ?pR ?pR ? pR ? CS pT  T :T  pT   T   T   WT  pW @pW OpW SpW ?pW SpW > DpW SpW ApW  SW =pW ?pW ? CX WX <X ~= textload]  =XAp] @ pg P&g AXg  text.loaph >Dph S~= errorh  =ph @ pi Psi O%i AOi * dempty -j ; > director-j ;(> y name pj ASpj >Dpj S~= warningj  =pk Ak ~@ filepm  @ pm  Spm AS~= openm  =&n APn ?<-o ;0> can't op-o ;8> en %s: %po ASpo >0Dpo Spo  @po So  =pp Ap ~?fdpr ?pr S~=dirfstatr =pr  @ pr @ &s AXs L<-t ;@> r can't-t ;H> fstat %pt ASpt >CDpt Spt  St  =Wu [<~?nullspw A?~?dpx ?sx O x A&x AOx  s: %r %-{ ;X> s is a d-{ ;`> irectory-{ ;h> ; can't -{ ;p> read wit-{ ;x> h multip-{ ;> le windo-{ ;> ws on itp{ ASp{ >WDp{ Sp{  S{  =W| ..string- ;.> %s: NU- ;.> L bytes - ;.> elided p ASp >.Dp Sp  @p S  =p ?p S )=p ? "@ Wu  text.insp >.Dp S~= error  =p "@p  1@ p  @ &  AX D< & @AO  ert%c%d-$ ;.> %d 0 %dp$ Rp$ Sp$ >.Dp$ Sp$  Sp$  Sp$  $  p$ Sp$  Sp$  Sp$ 0@p$ S~=winevent$ =W$ <-& ;.> %.*S %-& ;.> c%d %d 0p& Rp& Sp& >.Dp& Sp&  Sp&  Sp&  &  p& Sp&  S& =& ~=typecommit+ = Ap+  @ p- P&- AO-  0 tex-_ ;.> t.deletep_ >.Dp_ S_  =p_ "@p_  @ ~@q1p` @` p` ?&a ?AXa H<b &c  @AOc  %c%d %dp Rp Sp >.Dp Sp  Sp  Sp @p S = ~=textconstrain = Ap "@ p  Sp  @ p P p P p  S =~@p0p  @ p Pp @ p  Sp  @ p P p P p  S =~@p1p @ p P ~=textreadc =A~@qp @ p  @ p Q&  R <&  AO ?.string- ;> 0 0 p >D p  S~=runestrdup =p @0?~?tmpa `?p Sp @0?p Sp D0?  p S~=memmove =p D0?q /A`?p D0? a  b?p Sp X-?p Sp ,?  p S =p @0?p S =a `?p @0?p ,?C   D0?a @0?p Sp @0?p Sp D0?p S~=cleanrname =- ;> .%.*Sp >D p  Sp *? p  Sp \+? p  S~=smprint =~?sp L?p! >D p!  Sp! D0? p!  Sp! @0? p!  S! =p" H/?p" Sp" L? p"  S~= complete"  =p" P.?p# L?p# S# =p# P.? &$  AX$ ^<-% ;> %.*Ser-% ;> ror atte-% ;> mpting c-% ;> ompletiop% ASp% >Dp% S~= warning%  =p% P.? W&  n: %r %-* ; > .*S%s%.*-* ;(> S*%s /-* ;0> : no m-* ;8> atches ip* ASp* >Dp* Sp* D0?p* Sp* @0?p*  S&* D0?AS* p.DW* t0Dp* Sp* *?p* Sp* \+?p* Sp* T &*  AO* ~1DW* 2Dp* S*  =p* P.? p/ A W/  n: %s p0 ASp0 >CDp0 Sp0 T~? ip0   ?a0  Op0 Op0 S0  =p0 P.? p0  ? W0 HD p4  Sp4 T p4  S~= runesmprint4  =p4 P.? p4 T?W4  %stext.p >KDp S~="error "=p @ p  Sp  Sp ASp Ap  Sp Ap Sp ASp AS~=#cut #=p @ p ARp  Sp xRp Sp xRp Sp Ap  S =p ?p @ W\  typetex- ;X> t.type b- ;`> ackspacep >UDp S "=p )? p +? p *? &   S  text.ty-9 ;p> pe backsp9 >iDp9 S9 "=p9 )? p9 +? p9 *? &:   S: y +?>  p@ Q&@ (?O@  pacetex-e ;> t.type cpe >}Dpe Se "=pe ?pe *? pe @ pf  *?pf  Spf xRpf Spf $?pf Spf  Spf AS~=0textinsertf 0=pf *? &g  @Og X selecttextp > A~@f& @O  <~>.string- ;> q1frame- ;> select n- ;> ot rightp >Dp S "=p >p S~@ dlp  @p S~= textframescroll  =  = Ap  @ p @ &  AX  selectqp  > p @ p ?p tQp LQ  &  T   p @ p tQ  p  ?p tQp PQ  &  T  p R~?bp ?p xPp ?p |Pp (? A p  Sp R p  Sp R p  S =p = ~@tp @ p tQ  p   >~>clicktextp >&  X A clickmsec >& AM B p T& ?X   p @ p Qp O&  T  T  &  O  & AM  W  p  Tp >W  p  Sp ?p Sp (?p S &=p =p Sp Ap S =p = p @ p A ~?opp  ?~?statep  ?W  W <~=#textshow#= A~@$q0p$@ p@ pQ&AOz <~@%doselect& %@AOy +region7+>A~@,ap7,@ ~@-bp7-@ &9  P9 p @ p? p? p? & O<& ASp @ &Ap?X%left1-;%>{A-;%>[A-;%>(A-;%><A-;%>A~>&right1-;&>}A-;&>]A-;&>)A-;&>>A-;&>A~>'left2-;'> A~>(left3-;(>'A-;(>"A-;(>`A~>)left-;)>>%D-;)>>'D- ;)>>(D~>*right-%;*>>&D-&;*>>'D-';*>>(D~=+textdoubleclick,+=4A~@,q0p,,@~@-q1p,-@p,@ p2A W2AO2~?.lp4.?~?/ip5 /?p5 *>~?0rp50?&7?AX7-@&>? AO>BA W>CA p>? >  p> O?pBQpBO&B?XBM".string-;"> frame~=#disk5#=A~=$mouse5$=A~=%button5%=A~=&ccommand5&=A~='cxfidalloc5'=A~=(fontnames5(=A~=)plumbsendfd5)=A~>*clicktext5*>A~=+font5+=A~=,maxtab5,=A~>-right5->A~=.acmeerrorfile5.=A~=/cxfidfree5/=A~=0home50=A~=1keyboardctl51=A~>left5>A~=objtype5=A~=messagesize5=A~=cwait5=A~=cwarn5=A~>selecttext5>A~=colbutton5=A~=reffont5=A~= textcols5 =A~= but2col5 =A~= editing5 =A~= but3col5 =A~> selectq5 >A~>right15> A~=seq5=A~=screen5=A~=mousetext5=A~=globalautoindent5=A~=row5=A~>left15> A~>left25>A~>left35>A~=display5=A~=globalincref5=A~=mouseexit05=A~=mouseexit15=A5">A~=boxcursor5=HA~=bartflag5=A~=cplumb5=A~=mousectl5=A~=argtext5=A~= modbutton5 =A~=!nullrect5!=A~="plumbeditfd5"=A~=#activecol5#=A~=$timerpid5$=A~=%tagcols5%=A~=&seltext5&=A~='cedit5'=A~=(cputype5(=A~=)typetext5)=A~=*activewin5*=A~=+fsyspid5+=A~=,cnewwindow5,=A~=-cerr5-=A~=.snarfbuf5.=$A~>/clickmsec5/>A~=0cexit50=A~=1ckill51=A~=barttext5=AIpS=~@p1pp @p? p Op?~=textselect23smacme/text.c 664 0 0 75320 10453465725 12174ustar00rminnichsys#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+Scrollgap; /* 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 typecommit(Text *t) { if(t->w != nil) wincommit(t->w, t); else textcommit(t, TRUE); } void textfill(Text *t) { Rune *rp; int i, n, m, nl; if(t->lastlinefull || t->nofill) return; if(t->ncache > 0) typecommit(t); 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 textfswidth(Text *t, Rune ) { uint q; Rune r; int skipping; q = t->q0; skipping = TRUE; if(t->q0 >= t->file->nc) return 0; r = textreadc(t, q-1); //print("r %d\n", r); while(r != '\n'){ //print("r loop %d\n", r); if(q >= t->file->nc) break; ++q; r = textreadc(t, q-1); } //print("fsw: t->q0 %d q %d tfnc %d\n", t->q0, q, t->file->nc); return q-t->q0; } 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, col, nlcount; int nr; Rune *rp; Text *u; if(t->what!=Body && r=='\n') return; nr = 1; rp = &r; switch(r){ case 0x02: /* ^B: left one */ case Kleft: if(t->q0 > 0){ typecommit(t); textshow(t, t->q0-1, t->q0-1, TRUE); } return; case 0x06: case Kright: if(t->q1 < t->file->nc){ typecommit(t); textshow(t, t->q1+1, t->q1+1, TRUE); } return; case Kdown: n = t->maxlines/3; goto case_Down; case Kscrollonedown: n = mousescrollsize(t->maxlines); if(n <= 0) n = 1; 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, TRUE); return; case Kup: n = t->maxlines/3; goto case_Up; case Kscrolloneup: n = mousescrollsize(t->maxlines); goto case_Up; case Kpgup: n = 2*t->maxlines/3; case_Up: q0 = textbacknl(t, t->org, n); textsetorigin(t, q0, TRUE); return; case Khome: typecommit(t); textshow(t, 0, 0, FALSE); return; case Kend: typecommit(t); textshow(t, t->file->nc, t->file->nc, FALSE); return; case 0x01: /* ^A: beginning of line */ typecommit(t); /* go to where ^U would erase, if not already at BOL */ nnb = 0; if(t->q0>0 && textreadc(t, t->q0-1)!='\n') nnb = textbswidth(t, 0x15); textshow(t, t->q0-nnb, t->q0-nnb, TRUE); return; case 0x04: /* ^D: delete next char */ /* only delete if there is something to delete */ if(t->q1 < t->file->nc){ /* I'm lazy. Move right, then backspace. */ typecommit(t); textshow(t, t->q1+1, t->q1+1, TRUE); /* well, this is shit code but ... */ r = 0x08; } else return; break; case 0x05: /* ^E: end of line */ typecommit(t); q0 = t->q0; while(q0file->nc && textreadc(t, q0)!='\n') q0++; textshow(t, q0, q0, TRUE); return; case 0x0b: /* ^K: delete to end of line*/ /* only delete if there is something to delete */ if(t->q1 < t->file->nc){ typecommit(t); textshow(t, t->q1+1, t->q1+1, TRUE); } else return; break; case 0x0c: /* ^L: redraw*/ textresize(t, t->all); return; break; case 0x0d: /* ^M: CR */ break; case 0x0e: /* ^N : next line */ /* fairly simple, not. start at your column, and go * right, until you get to that column again */ /* find our column by, in essence, doing a ^A */ typecommit(t); /* go to where ^U would erase, if not already at BOL */ nnb = 0; if(t->q0>0 && textreadc(t, t->q0-1)!='\n') nnb = textbswidth(t, 0x15); /* nnb is our column */ /* now move forward, one at a time, until we * (a) pass a newline and * (b) hit EOF OR get back to nnb */ q0 = t->q0; while(q0file->nc && textreadc(t, q0)!='\n') q0++; /* now q0 gets to be 1 of max file or q0 + nnb */ q0 += nnb + 1; if (q0 > t->file->nc) q0 = t->file->nc; textshow(t,q0, q0, TRUE); return; break; case 0x0f: /* ^O: ?? */ break; case 0x10: /* ^P : prev line */ /* fairly simple, not. start at your column, and go * left, until you get to that column again */ typecommit(t); //print("tq0 %d\n", t->q0); /* move back. At BOF or first nl, set col. * at BOF or 2nd nl, break */ for(q0 = t->q0, col = 0, nlcount = 0; ;q0--) { //print("q0 %d col %d nlcount %d\n", q0, col, nlcount); if (q0 <= t->org) { /* at start of file -- so no NL */ /* all these friggin' corner cases which I am too dumb to get */ if (col) col--; break; } if ((q0>=t->q1) || (textreadc(t, q0) == '\n')){ if (nlcount == 1) break; nlcount++; continue; } if (! nlcount) col++; } //print("q0 %d col %d\n", q0, col); q0 += col; //print("q0 %d\n", q0); textshow(t,q0, q0, TRUE); return; break; } 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) typecommit(t); return; case 0x0b: /* ^K: kill to EOL */ if(t->q0 == 0) /* nothing to erase */ return; //print("tq0 %d tq1 %d\n", t->q0, t->q1); nnb = textfswidth(t, r); q1 = t->q0 + nnb - 1; q0 = t->q0 - 1; //print("q0 %d q1 %d\n", q0, q1); 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 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, op; 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 = op = 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 && op==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 && op != -1){ cut(t, t, nil, TRUE, TRUE, nil, 0); op = state = 1; } }else{ if(state==1 && t->what==Body){ winundo(t->w, TRUE); textsetselect(t, q0, t->q1); state = 0; }else if(state != -1 && op != 1){ paste(t, t, nil, TRUE, FALSE, nil, 0); op = 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){ if(doselect) textsetselect(t, q0, q1); 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); } * 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; } ifsmacme/time.8 664 0 0 12727 10453465742 12074ustar00rminnichsys~Emsec >(A~?.safea ? p  S~=nsec =a ? p  Sp ? p  Sp ? p  Sp @BA Sp AS~=_divv =p ?p ? ~=timerstop =A~@tp @ ~>timerp > p   Qp  > ~=timercancel =Ap @p AO ~> timerproc  >8A~> .string- ; > timerprop > Dp S~= threadsetname  =p Ap S~= rfork  =~? tp A ?~?nap A?~?ntp A? >~?oldp ?W *p  ?~?dtp ?p  ?& ?AP 5ctimerp > p  S~=recvp =p ?W w ctimer - ; > realloc p > Dp S~=error =p ?C ?p  ? a Qp ? p  O >p ?p > p  Sa ? p  S~=nbrecv =& AS p > Dp Sp ASp  Ap S~=proccreate = ~= timerstart  =Ap > &  AO W p Sp   ?p  S~=#sendp #=p  ?  - ; > failed~=$disk5 $=A~=%mouse5 %=A~=&button5 &=A~='ccommand5 '=A~=(cxfidalloc5 (=A~=)fontnames5 )=A~=*plumbsendfd5 *=A~=+font5 +=A~=,maxtab5 ,=A~=-acmeerrorfile5 -=A~=.cxfidfree5 .=A~=/home5 /=A~=0keyboardctl5 0=A~=1objtype5 1=A~=messagesize5 =A~=cwait5 =A~=cwarn5 =A~=colbutton5 =A~=reffont5 =A5 >A~=textcols5 =A~=but2col5 =A~=editing5 =A~= but3col5  =A~= seq5  =A~= screen5  =A~= mousetext5  =A~= globalautoindent5  =A~=row5 =A~=display5 =A~=globalincref5 =A~=mouseexit05 =A~=mouseexit15 =A~>.string5 > A~=boxcursor5 =HA~=bartflag5 =A~=cplumb5 =A~=mousectl5 =A~=argtext5 =A~=modbutton5 =A~=nullrect5 =A~=plumbeditfd5 =A~=activecol5 =A~=timerpid5 =A~=tagcols5 =A~=seltext5 =A~= cedit5  =A~>!ctimer5 !>A~="cputype5 "=A~=#typetext5 #=A~=$activewin5 $=A~=%fsyspid5 %=A~=&cnewwindow5 &=A~='cerr5 '=A~=(snarfbuf5 (=$A~=)cexit5 )=A~=*ckill5 *=A~=+barttext5 +=AI n p0; } int textselect23(Text *t, uint *smacme/time.c 664 0 0 3367 10453465242 12122ustar00rminnichsys#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; } 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 ansmacme/util.8 664 0 0 41453 10453465746 12115ustar00rminnichsys~E .string- ; > acme: %sp Ap Sp > Dp S~@sp @p S~=fprint =~=acmeerrorfilep =Dp S~=remove =~=abort = ~=errorwin1 =(A~@ndirp @   A p  S~=emalloc =p @ p  ~?np  ?&  AO Z<~?rp  ?p  S~@dirp @p Sp   p S~=memmove =p ? p ?p  C  p   a Qq /AO- ; > : %r +- ; > Errop  ?p   p  ?a  Qp Sp > Dp Sp Ap S = A?p ? p  Sp ? p  S~=lookfile =& A~?wp ?X <~=row& =AX z rsca- ; > n't crea- ;( > te colum- ;0 > n to mak- ;8 > e error p > Dp S  =p = p = a  R p P p  Sp ASp ASp A p   S~=coladd =o AOp ?p Sp ?p Sp ?p S~=winsetname =p ?p S~= free  =~@!ninclp  !@ W addwarningtext? >Ap? (@ ~>warningspC > WC O pJ  (O~?warnpK ?pK  O&L  AOL npO   O ApO SpO ASpO @pO SpO  @pO  SO =~=cwarnpP =pP SpP AS~= nbsendpP  =P ~= flushwarningsU  =DAp] > W]  ~=warning = A~@sa @p @ p  S~?argp ?p S~=runevsmprint =p  & Ap ?X <~>.string- ;@> windowr- ;H> unevsmpr- ;P> int failp >GDp S~=error =p ? p  S $=~?.safep ?p (@p Sp ?p Sp ?p S > ~= runeeq  =A~@!n1p !@ ~@"n2&   "@O  ed%.*Sp .?p Sa  QC p Sp >[Dp Sp   Sp +@p S~=1snprint 1=p .? ~=bytetorune =,A~@sp @ p  S~=strlen =~?nbp ?p ?   A p  S -=p  p @p Sp ?p S~?rp  ?p  Sa ?p  Sa ?p Sp AS~=cvttorunes =p ? p ?q AQ~@ipp @p ? p  Op   ~=isalnum = A~@ cq  @ w  &  AR  !"#$%&'(- ;h> )*+,-./:- ;p> ;<=>?@[\- ;x> ]^`{|}~p >`D p  Sw   p  S~= utfrune  =& AO prevmousea > p Pp Qp Pp Q~@wp @~>mousewp > ~=restoremouse =Ap > &  AO <&  @O  p Pp Sp Pp S~=moveto =p A> ~=clearmouse =Ap A> ~=estrdup =Ap @ p  S~=strdup =& A~?tp ?X <~>.string- ;> strdup fp >Dp S~= error  =a @ p  S /=~?!.safep !?p ?p Sp !?p S 0=p ?  -=Ap$ ,@ p$  S~="malloc$ "=&% A~?#pp% #?X% %<-& ;> ailedma-& ;> lloc faip& >Dp& S&  =a' ,@ p'  S' /=p' !?p' #?p' Sp' !?p' S' 0=p( #?p( Sp( ASp( ,@p( S~=$memset( $=p) #?) ) ~=%erealloc- %=A~@&pp/ &@ p/  Sp/ ,@ p/  S~='realloc/ '=&0 Ap0 &@X0 C<-1 ;> ledreal-1 ;> loc failp1 >Dp1 S1  =a2 ,@ p2  S2 /=p2 !?p2 &@p2 Sp2 !?p2 S2 0=p3 &@3 3 ~=(makenewwindow: (=<A~=)activecolp: )= ~=*seltextp: *= &A  AOA V edcan't-I ;> make copI >DpI SI  =pJ ,= pJ ,= aJ  QpJ O pL  )=&M +@AOM  lumn~=disk5i =A5i >A5i =A~=button5i =A~=ccommand5i =A~=cxfidalloc5i =A~=fontnames5i =A~=plumbsendfd5i =A5i 0=A~=maxtab5i =A~> warnings5i  >A~= acmeerrorfile5i  =A~= cxfidfree5i  =A~= home5i  =A~= keyboardctl5i  =A5i >A~=objtype5i =A~=messagesize5i =A~=cwait5i =A~=cwarn5i =A~=colbutton5i =A~=reffont5i =A~=textcols5i =A~=but2col5i =A~=editing5i =A~=but3col5i =A~=seq5i =A~=screen5i =A~=mousetext5i =A~=globalautoindent5i =A5i ,=A~=display5i =A~=globalincref5i =A~=mouseexit05i =A~=mouseexit15i =A~> .string5i  >A~=!boxcursor5i !=HA~="bartflag5i "=A~=#cplumb5i #=A~=$mousectl5i $=A~=%argtext5i %=A~=&modbutton5i &=A~='nullrect5i '=A~=(plumbeditfd5i (=A5i )=A~=)timerpid5i )=A~=*tagcols5i *=A~=+seltext5i +=A~=,cedit5i ,=A~=-cputype5i -=A~=.typetext5i .=A~=/activewin5i /=A~=0fsyspid5i 0=A~=1cnewwindow5i 1=A~=cerr5i =A~=snarfbuf5i =$A~=cexit5i =A~=ckill5i =A~=barttext5i =AIi ommitc =pl  A pl  Sl =pl  ? pl ? ~?rpl ?pm Rpm O~?q0pm ?pn A Wn  #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; } /* * Incoming window should be locked. * It will be unlocked and returned window * will be locked in its place. */ Window* errorwinforwin(Window *w) { int i, n, nincl, owner; Rune **incl; Runestr dir; Text *t; t = &w->body; dir = dirname(t, nil, 0); if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ free(dir.r); dir.r = nil; dir.nr = 0; } incl = nil; nincl = w->nincl; if(nincl > 0){ incl = emalloc(nincl*sizeof(Rune*)); for(i=0; iincl[i]); incl[i] = runemalloc(n+1); runemove(incl[i], w->incl[i], n); } } owner = w->owner; winunlock(w); for(;;){ w = errorwin1(dir.r, dir.nr, incl, nincl); winlock(w, owner); if(w->col != nil) break; /* window 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; 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; } =A~=button5i =A~=ccommand5i =A~=cxfidalloc5i =A~=fontnames5i =A~=plumbsendfd5i =A5i 0=A~=maxtab5i =A~> warningssmacme/wind.8 664 0 0 66635 10453465752 12107ustar00rminnichsys~E @p> S~=winclose> =p> 0? W> <> ~=winmousebutC =(A~@wpC @ aE *?pE SpE QpE SpE QpE SpE QpE  SpE QpE S~=addptE =aE *?pE SpE *?pE SpE *?pE SpE ApE  S~=divptE =~=mousectlpE =pE SpE *?pE SpE *?pE S~=movetoE =E ~= windirfreeI  =ApI @ sN P%N AON i.string- ;> /gui&  AU D p  Sp A p  Sp   p @ a  R A p  Sp A p   S =p @ & AX  de+- ;> Erro&  AU +D p  Sp A p  Sp   p @ a  R A p  Sp A p   S =p @ & AX , rs - ; > Del - ;(> Snarp ? p 0? a  Qp Sp >Dp Sp Ap S 1=p @  A?s R% AO  f U- ;8> ndop ? p 0? a  Qp Sp >4Dp Sp  Ap S 1=p @ A?p Rp HO& AT <- ;@> Redp ? p 0? a  Qp Sp >@Dp Sp  Ap S 1=p @ A?p R p P &  AO  o Pp ? p 0? a  Qp Sp >LDp Sp Ap S 1=p @ A?s R% AO <- ;P> ut - ;X> Getp ? p 0? a  Qp Sp >VDp Sp Ap S 1=  A?p ? p 0? a  Qp Sp >`Dp Sp Ap S 1=  A?p .? p  Sp |A p  S~=runestrchr =p @ & AO < .? AC ~?kp ?W  | - ;h> Lookp ? p 0? a  Qp Sp >fDp Sp  Ap S 1=  A?p 0? p  Sp ? p  Sp .? p  Sp ? p   S =p ? p .?p 0?& AX ;.string- ;p> %s: - ;x> not a di- ;> rectory p ASp >tDp Sp ?p S~=warning =p @p S *= p  S *=p @ C @Qp DQ p  Sp @Q A p  S~=realloc =p @ p DQp DQ Ap Sp DQp Sp @Q A Ap S 1=p @   A p  S &=p @ p DQ p Pp DQp Op Sp @p Sp @ p S 1=p @p S *= Ws <- ;> %s: %r p ASp >Dp Sp ?p S =p @p S *=p ?p S *= ~=winclean =Ap @ s P% AX  %.*S mo- ;> dified p ASp >Dp Sp Pp Op Sp Pp Op  S =W  unnamed - ;> file modp ASp >Dp S =p @o AOp A p A ~=!winctlprint != Ap @ - ;> ified %- ;> 11d %11d- ;> %11d %1- ;> 1d %11d ~@"bufp "@p Sp >Dp Sp Pp Sp Pp Op  Sp Pp Op Ss Pp Ss Pp S~=#sprint #=p "@ p @ ~@$fonts& $@AO <- ;> %s%11d - ;> %q %11d p >D p  Sp  Sp R p R   p  Sp `R p P p P p   Sw HR p  S~=%smprint %= p   ~=&winevent &=$Ap @ s P& AX < p ,P& AX <- ;> no wind- ;> ow ownerp >Dp S~='error '=~@(fmta (@p (@ p  S~?)argp )?p S~=*vsmprint *=p  & A~?+bp +?X <- ;> vsmprin- ;> t failedp >Dp S '=p +? p  S~=,strlen ,=p @ p $Q p  Sp (Q p  ~?-np -?   p   C  p  S =p @ p $Rp $Rp (R C (R  p ,R o  Op $Rp (R  p Sp +?p Sp -?p S 1=p +?p S~=.free .=p @ p -?  (Qp  Q &  AO ? ~=0disk5 0=A~=1mouse5 1=A5  =A~=ccommand5 =A~=cxfidalloc5 =A~=fontnames5 =A~=plumbsendfd5 =A~=font5 =A~=maxtab5 =A~=acmeerrorfile5 =A~=cxfidfree5 =A~= home5  =A~= keyboardctl5  =A~= objtype5  =A~= messagesize5  =A~= cwait5  =A~=cwarn5 =A~=colbutton5 =A~=reffont5 =A~=textcols5 =A~=but2col5 =A~=editing5 =A~=but3col5 =A~=seq5 =A~=screen5 =A~=mousetext5 =A~=globalautoindent5 =A~=row5 =A~=display5 =A~=globalincref5 =A~=mouseexit05 =A~=mouseexit15 =A~=winid5 =A~>.string5 >A~= boxcursor5  =HA~=!bartflag5 !=A~="cplumb5 "=A~=#mousectl5 #=A~=$argtext5 $=A~=%modbutton5 %=A~=&nullrect5 &=A~='plumbeditfd5 '=A~=(activecol5 (=A~=)timerpid5 )=A~=*tagcols5 *=A~=+seltext5 +=A~=,cedit5 ,=A~=-cputype5 -=A~=.typetext5 .=A~=/activewin5 /=A~=0fsyspid5 0=A~=1cnewwindow5 1=A~=cerr5 =A~=snarfbuf5 =$A~=cexit5 =A~=ckill5 =A~=barttext5 =AI - ;h> Lookp ? p 0? a  Qp Sp >fDp smacme/wind.c 664 0 0 25741 10453465244 12147ustar00rminnichsys#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; w->autoindent = globalautoindent; if(clone){ w->dirty = clone->dirty; textsetselect(&w->body, clone->body.q0, clone->body.q1); winsettag(w); w->autoindent = clone->autoindent; } } 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; /* * subtle: loop runs backwards to avoid tripping over * winclose indirectly editing f->text and freeing f * on the last iteration of the loop. */ f = w->body.file; for(i=f->ntext-1; i>=0; i--){ w = f->text[i]->w; w->owner = 0; qunlock(w); winclose(w); } } 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; } char* winctlprint(Window *w, char *buf, int fonts) { sprint(buf, "%11d %11d %11d %11d %11d ", w->id, w->tag.file->nc, w->body.file->nc, w->isdir, w->dirty); if(fonts) return smprint("%s%11d %q %11d " , buf, Dx(w->body.r), w->body.reffont->f->name, w->body.maxtab); return buf; } 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); } } font->height + 1; if(r1.max.y smacme/xfid.8 664 0 0 132470 10453465760 12106ustar00rminnichsys~Eclampaddr >A~@wp @ p Q& AP  .string- ; > xfidctltp > Dp S~= threadsetname  =~@ argp  @~? xp  ?W $ hreadca-$ ; > n't crea-$ ; > te temp p$ @p$ Sa$ ?p$ Sp$ > Dp$ S$ =% ~?$qp' $? a'  R' AOp( xT~?%q0p( %?p) |T~?&q1p) &?p*  A p*  S~='emalloc* '=~?(rp* (?p+  A p+  S+ '=p+ &? p+ %? ~?)sp+ )?W,  file%.*p1 )? p1  Sp1  A p1  Sp1 >% D p1  Sp1 *? p1   Sp1 (? p1  S~=,snprint1 ,=p2 ? p2 P p2  Sp2 )? p2  S~?-mp2 -?p2 S~=.write2 .=p2 &? p2 %? &2 -?O2 <-3 ;( > Scan't -3 ;0 > write te-3 ;8 > mp file -3 ;@ > for pipe-3 ;H > commandp3 ASp3 >* Dp3 S~=/warning3 /=W4  Up> S~=filemark> =p> !? p?  Sp?  Sp? ASp? A Sp? Ap? Sp? ASp? AS~=cut? =p? !? p? ? p@ |Q p@  Tp@ |Q p@  ToA A TWB <~=editing&D =AXD J.string- ;P> %r unk- ;X> nown qidp ASp >UDp Sp  S /=p @ W -<&  AO .<&  AO /<&  AO .clampaddr >p \? - ;`> %d %11- ;h> d %11d ~?bufa `?p Sp >eDp Sp Pp Sp Pp  S~=sprint =W  I/O erro- ;x> r in temp  S~?"fca "?p Sp >pDp S~=#respond #=W a p fileu- ;> nknown q- ;> id %d ina `?p Sp >Dp Sp S =p  @p Sa "?p Sp AS #=W a<& AQ ;  Sa> "?p> Sp> =Dp> S> #=W? s.string- ;> readun- ;> known qi- ;> d %d in ~?bufa ?p Sp >Dp Sp  S~=sprint =p  @p Sa "?p Sa ?p S #=W s writelo~?(pp (?p Sp >D p  Sp A p  S~=)strncmp )=& AX  ckunlocp (? p  Sp >D p  Sp A p  S )=p %@ & AX  kcleanp (? p  Sp >D p  Sp A p  S )=& AX D p  Sp A p  S )=p %@ & AX / dirtyshp (? p  Sp >D p  Sp A p  S )=& AX D owname p (? p  Sp >D p  Sp A p  S )=p (? & AX  nulls i- ;> n file np >Dp ?W  amebad - ;> characte- ;> r in filp >Dp ?W  e namedp"  Sp" >D p"  Sp" A p"  S" )=p" (? &" AX"  ump nul-. ;> ls in du-. ;> mp strinp. > Dp. ?W/  gdumpdip4  Sp4 >"D p4  Sp4 A p4  S4 )=p4 (? &4 AX4   Sp> ?>  p> Sp> ?p> Sa> ?p>  Sa> ?p> Sa> ?p> S>  =&? ?AO?  <-@ ;(> r nulls-@ ;0> in dump-@ ;8> directo-@ ;@> ry strinp@ >+Dp@ ?WA  gdeletepF  SpF >JD pF  SpF A pF  SF )=pF %@ &F AXF (QD pJ  SpJ A pJ  SJ )=&J AXJ D delfil-L ;X> e dirtypL >UDpL ?WM `D pR  SpR A pR  SR )=&R AXR Z getputpV (? pV  SpV >dD pV  SpV A pV  SV )=&V AXV q dot=addrpZ (? pZ  SpZ >hD pZ  SpZ A pZ  SZ )=&Z AXZ  clampaddr\  >p\ %@ p] Q p]  hQp^ Q p^  lQp_   _ Ap_ Sp_ hQp_ Sp_ lQp_ S_ =p` A"?pa A Wa  <-c ;p> addr=dopc (? pc  Spc >qD pc  Spc A pc  Sc )=pc %@ &c AXc  tlimit=ph (? ph  Sph >zD ph  Sph  A ph  Sh )=&h AXh pj %@ pk Q pk  Qpl Q pl  Qpm  A Wm  <-o ;> addrnompo (? po  Spo >D po  Spo A po  So )=&o AXo  arkmarkps (? ps  Sps >D ps  Sps A ps  Ss )=&s AXs <~= seqCt  =pu %@pu Opu S~=filemarku =pv A"?pw A Ww  <-y ;> nomenupy (? py  Spy >D py  Spy A py  Sy )=&y AXy D p}  Sp} A p}  S} )=&} AX}  menunosp (? p  Sp >D p  Sp A p  S )=& AX  crollclp (? p  Sp >D p  Sp A p  S )=& AX  eartagsp (? p  Sp >D p  Sp A p  S )=& AX (.string- ;(> croll%.p "? p  Sp  A p  Sp >(D p  Sp ? p   Sp ? p  S~=)snprint )=p ?~@*xp *@p ? p &? p !? p ?&   M  *Sbad c-& ;(> ount in -& ;(> utfrunep& >(Dp& S~=-error& -=p& *@p& &? p& !? p' ? '  '  p( V&( T(  (D pO  SpO ? pO   SpO ? pO  SO )=pO &? pO *@ pP ?pP  pQ   Q  pQ Q &Q  TQ 2 %.*Swin-z ;(> dow shutpz  Saz ?pz Spz >(Dpz Sz ={ p}   Qp~  S~=winunlock~ =p *@p Op S~=recvp =p @p Sp FAp S~=winlock =p 0? p *@ p @ C  W a down%.p "? a  R p  Sp  ?   /  p  Sp >(D p  Sp ? p   Sp ? p  S )=p 0?p "?p +? p    W + *S~=disk5 =A~=Ebadctl5 =A~=mouse5 =A~=button5 =A~=ccommand5 =A~=cxfidalloc5 =A~=Einuse5 =A~=fontnames5 =A~=plumbsendfd5 =A~=Edel5 =A~= font5  =A~=!maxtab5 !=A~="acmeerrorfile5 "=A~=#cxfidfree5 #=A~=$home5 $=A~=%keyboardctl5 %=A~=&objtype5 &=A~='messagesize5 '=A~=(cwait5 (=A~=)cwarn5 )=A~=*colbutton5 *=A~=+reffont5 +=A~=,Eaddr5 ,=A~=-textcols5 -=A~=.but2col5 .=A~=/editing5 /=A~=0but3col5 0=A~=1seq5 1=A~=screen5 =A~=mousetext5 =A~=globalautoindent5 =A~=row5 =A~=display5 =A~=globalincref5 =A~=mouseexit05 =A~=mouseexit15 =A~> .string5  >A~= boxcursor5  =HA~= bartflag5  =A~= Ebadevent5  =A~= cplumb5  =A~=mousectl5 =A~=argtext5 =A~=modbutton5 =A~=nullrect5 =A~=plumbeditfd5 =A~=.rathole5 =A~=activecol5 =A~=timerpid5 =A~=tagcols5 =A~=seltext5 =A~=cedit5 =A~=cputype5 =A~=typetext5 =A~=activewin5 =A~=fsyspid5 =A~=Ebadaddr5 =A~=cnewwindow5 =A~=cerr5 =A~= snarfbuf5  =$A~=!cexit5 !=A~="ckill5 "=A~=#barttext5 #=AI S +=p $@ p '? W b #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: case QWxdata: 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: case QWxdata: 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 || q == QWxdata) 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: b = winctlprint(w, buf, 1); goto Readb; Readbuf: b = buf; Readb: n = strlen(b); if(off > n) off = n; if(off+x->count > n) x->count = n-off; fc.count = x->count; fc.data = b+off; respond(x, &fc, nil); if(b != buf) free(b); 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 QWxdata: /* 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->addr.q1); 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 QWerrors: w = errorwinforwin(w); t = &w->body; goto BodyTag; 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, "nomenu", 6) == 0){ /* turn off automatic menu */ w->filemenu = FALSE; m = 6; }else if(strncmp(p, "menu", 4) == 0){ /* enable automatic menu */ w->filemenu = 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); } }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, TRU