mike2@lcuxa.UUCP (M S Slomin) (04/23/88)
Below is a replacement for Dick Keily's port of 'less' to MS-DOS. He distributed it late in 1986. This version is somewhere between version 61 and 73 of less in most of its capabilities, although several options are absent. If you have a color monitor, use the flag -C (capital C) on the command line, or use the environment variable LESS to default to the color mode, e.g.: SET LESS=C Improvements over last year's version: 1. Both underlining and boldfacing are supported. On a monochrome monitor, they are displayed properly; on a color monitor (tested on CGA), underlined material is displayed in yellow. 2. When returning from editing a file, the modified file is read in and displayed. Previously, the original unmodified file in memory continued to be displayed, notwithstanding changes when it was edited. 3. This is compiled in TurboC, version 1.5, in the tiny model, and the resulting file was converted to a .com file. Benefits: it loads and executes somewhat faster, and it consumes less memory (which is important if you wish to do shell commands). For a demonstration of the new capabilities, uudecode and unarc the attached, and 'less' the 'less.doc' file, which contains both underlines and boldfacing (as produced by nroff). begin 600 tcless.arc M&@A40TQ%4U,N,0`E)24E/10``)40Z6O.!I`H```,+J@@`<&DR)0I()I,:4'D MR90&"EQ,&>@D2),B$-F4F3,'!)<6(-[`@?-F3AHZ94*:`='FC9PR$"4.G)+% MR1,H4Y(\C"@$A`B-'$%L:3%F2!DX=-HTB1.E3I4N0EN@X6(FB1.J0J`.%;/% M#)PN5*UB[2(BIA@Y,7N*`,&6[9858<>T(3/6;5@S:32Z"=.FS%@7@,M&G`B" MB,$A4I)`H9+DB9.820ANG`,Q3<<P(.#(>7-&#E\0)MOD#2,'!)TW(""S=`E3 M`8H8*5B`$%.'#H@[:-*,00,B#!LV;^YT%!-FS)H[I,ET;&FG3%\W="J[,8TF M)5Z-O3O>*?,[.P@S+I'+(;.Z^7,Z+B`&83/G#0O50"DK(/-F(P@W;VRC"=/< M-.J789!'1W4@E`%=&B^!D(8;<-3V75XIB5$&>`G.00=I="QXQGL*M'?;2;RQ M0=H9*2W8H&W7V7<2:!?*04='=<#Q71@6EE':@&%,AQ(>MI5!QDDN=<1&&FNT MYD)D=J0!T6LII!<1%%!`-,0;;;21HW*])4C<'#Z&-)T8^:&A6DLO072E:DDZ M.6655RX71AZSI:19&6.4X6.78L")&1ETIF$E&_?5T8:$<L@&T1B^:42>$PKJ M2""?<XPA1QI(I?&&&\-Q%YR35!#HAJ"$*@@CE^3E"1J5*8U!I95N*"<;C8W^ MB"A*9#@9$V%#/-%$$T$X0<1.23AJW1N_!:<A")!*2FF&E\XAFQ=8L%1&CAVI M"MUF;+2`A9-'0@&"F`H@P1T<.B!K&1PBZ@F:H%;*`><;*PVX4:JKMNED$BOE M\48=W[E$HFV)4I=2F#:"H"J;K3H+PDM]#5KP@)9Y60:W4$+F[110!#$$1@I, M(2FQ@%(HWJ(@#.G&1K+Q:488=;!AVZ4IW;$@?7>`@`*7`U=ZJ4<MZ!$G<'<T M"1&^(#`:,9DIX2@L:)+:.9U)>I0AVZ5LP"GO@WL!&NE+!IK1LJCFSH'NFSY2 M'&5$BOFTDDL@>&&$8%/PE1*L&&M<A-D6^R1&2*5Y(03<'W='G'$C%UWR@B@C M.R'++DO\8:O!V8QS2#I/]Y'/$@(MM`)$&[T<:]3E*##37#^=1M13NU'UZ'AE M3;K37@,:\8]BIUMV3!6C[:T415!1A12/=1QXR.$E9[C)B:O,N&TQ<$J@@1DF MR"CRER5(^]AY^"A;&<U-E\9*GJ^6H-*C;^TT:*=/C/O9W?HT<-]%P"VW=[S[ M#CS>NOND!M]M*R'8>AZZVASF!ZOZ_<X)^&N?"-[5MRP`#EN`&MQQC#<]Q"E, M>2UCGO-2`CT$I:2")ZM>2JYG.S)LKWL*`A_8D!:ZI9G/0.B+&K?2)H(U\,\+ M2Y!?7[R3AP32D#QL\P(1'@BR?LFA<"!,WN(R"((8P.!>*HR8V/J$%^TIR#82 M.IA]KG:RFF&P<11*38?J(`8NQ:$.T$-6;UH%`GX=C%7*\:&W1,"O(%:!B((K MS@3'<SP+IFR)C7,B%`TG13A0,0U67%$64=41+I;!BX!$$=L@,@<RFA&-T%'C ME=IHL'HE3(X^*4T0I7!#)@A&"D<)PX)L(\"F&0B44H`(*N&@RDRVLG0ILPRB MQG.L',&)-F8P@XVZ9*+:.*D*7(I="ED'(;#M)D=G.!9NFKFBB$GH6$EZY.V> MQ+ZTG0$B1T#-:0YW,D(N+45_7!D38V`S"473#6XX%KP>I)'-H>`*I(FG&\Y0 M+HBYZ9<IF0/0EAD^$<F!1"Y(P?KR=@1PBA,UR#,G,S62SN6-SD!`C!>!4N2D M>^930_W,S3_C!)J!?H]%5S)>,>E@*`5X1EXWVL]T,(-.L%USGPN;5JT4RLV\ MP<&A_NE-9DIR$DM-AU&&E$.=,KE*<6X40DYBU!S0L"\VE"I"9:##=F`(@S62 MQXE/7%*G(A8QDG`D#6+`SDDME%(^KM2F93@6@$P($=J\3'5P^AY\)@.1_5R& M#7.%TUR#>C6,JF2BZN.<;2+F&^3DH2,KLY!L['J?_/2X=U81:EM"%YJ\$ M$(G;#F$%!U"V`2)&`%EP\*0G-\`):#9"%)=*EE64%$J,[5I#(PDTACK(X269 M-&M1=R:S`87.-AJA@VU!*8(3"`8%4]`0=LZ8GXGQ-+7%VDZI6OO:U2J51BE) MKFUE\Q(Z^!93A"60<)DUG6GN!B+(Z<B<DK0O@<(IMUTJ+F]PA-S:VLA)V(7M M=M=(L-*89)_3K4-UR9O5\S;2J7(J"7M[$QT%N'>_!!*1A7QB4!*MA3G.2>,; M-QG?`N&!3K79YGH`E5L1EDS"MZG.3._S2'J6J"-EP`-?$%<KS[;O!;14KHW< M$-II*94W(E/I.9L9QJLYH07&C:BU+K2@8UTMR+9U0U05ITZ7/;B)&\Q,&(0L M!^]=)J=G:!EI3#RGLU[J51UYB:K.$,^H;5<UVC-3JV2S(.J0M0WH"C%TQFQ4 MV5@IKVX8`QOJP*?1!>A'[/4-1/IRH6=Z9@RVA5$\SY@TU."Y,T<),Y=(LQN4 MNN@RK"00EZQ%GHB2D&QD6!)E)W>UC[RL<K*YL,%D2J+=6F9S+LA=^WZ`Y2$7 MF=2\D6#A^GRU%!EQ=$^.,N(Z.>@JX_3*8\ZRJ(U<:K:>FL*CBZB?^O*C,7,' MH!1*&H%.(R-7GZN$H"2R`F8Y+=O,UU)UZ,BHCRR;,$8[1-.><BVMG&$:V3O; M0P9E$822HKWTI0L0*8*.15-.S'31QH-L-H0<?F.66.;`&QJ="'K[6^BMQ=DW MLQ.TO0H";XTX87755-"^LYDVA-LR+UM)BCK";-YZLM73CMA+6I#C'9^LQSW- M7_`D;G1UI^1D/+*QS<Q0\YMO>)X[!T$T4=AS>B$,Z"?;'-$L#JJ"A6^*8_@> M(LF#`ORP$C42HK;72/4AXU[MY8N*#42<#&7>0!U%U,3QQ'D,RK,QG>).S\Q+ MZ)MOC`]M)61W6&G.;LBT5]&$T.Z[XKF';\@&WL1-1WJPNSG''MSP"((APKO? MI&]4-8I"5IIP&,#D((UCYZ9G`$$VM<NA!2F:T<<Z24<XKF<!$2A/*%&)&;B4 M\]&!2;E4(B=6C_7J["&=:&8U25JE=L7+>D@SJ]QM>`UT!N/.T_:MV>258YO& M\S\5.[-_0W^NIF';5+]+R`,E$R"B^MJQ?G2?(GF'17_3%@8C862]E&J=)B/N M%U"NY`9\)F^/-EPZ`F&OYQ*Q9U1A@SWY!2*C8WI!=`2=]'53!R$0P7&3E0?) M!R_+EU6RH1?EQQN;9(":\09XX"?FEAGL-V@DTB0>415.4%TZ0!6DY$^'\G,& M(SIQ)U#!(1NTY"*ZH69RL#H[`QPXM7.@]!&HI5K:-1OO4DX-.()P)'V4,V'B M92,=D7)8I3D<XD\?TAVG<09G@!T"5%ORI%%C5H9&57R*MTI"U1<<$08DHCA; M@U8$]W0UQGP9LD^@M`)R$6L*,`0LPR7B!QJ5IW9=\HA!%7<YAF*T4B#%L5]^ M,C<T5C/.%C%%AWA(EUJED8J!)ALK((*(DF_VL5<<L7>H4649DBAP4GV@6&K. MYFV+F'MY6%AL!!$O51TQ)3I7XTX+HD]G`$I6`!%0,"G048G-(0<FL3,!&"KS M9(L=$7-R50=:YF-I$P<1=X,O`H[XTRG#DEW'LG=WX&D*@'<N9@>^D0;DT76% MX1"ZAX8:""_L:([>8@=#XP9V\`9%<AG3X2-`<B.HX9`*:#"^!5R`=WMQA5.[ MMTWN6""0QC81<R%%,AU4%WW&F"2;X0;GH7NDD0:SAQU%0`1)0`5/(`4<PC9? M]")!)0+.41QS(`+,%0*@41V_010_)QC!DI`+*513Q1V`,DYR0([E0Y39@G=: MEP;=DT"$<1.,X1@[L29C&%$BP5XNEBR3(B%7!31.T@0P-I9&-5)Q]TS[E%^Y MH1'@*$9"1X[0*!M)@AE7(P(?L19XAS]/4#DNYAL!%`8C27,F^3P(B2"7LI+X M."DOF1(B4!`'`91;V(H3]XK+I"_\,B1%@ENLT0(6D@?848.`-HRR,4[\\0;Z M:!IY``?'\A.3P3,V!Q@NL!;34FH9LD-W61D\AY`*:46@Z7%G@`:LY)1BU!!3 MH`,Q(38Q42>4E%4$81!3T`.G%1'2J0`=:2`H&9EI-)DNN7U@\X1T)R'I-CI6 MB3P<XB'M.6UNV2PAD8V3TFA7@YD(`9Z0J9+CV9*5.4/>8FL0$81R\"=5TV_% MPT?[MAOV(8P@H`;Y!C!F8%M[MVXBL8$E1(8HP%^B\BH3N6J70A[W!W9EL#F= MTGA)QJ#<5AW#^7OXR5L5"3VK4Z*O\TH0T9&VIH<[,XN4:$2%TZ`N&E00"A$2 MNF%A4*$/<WSY<1HVYV[^9WUMB`:--P=K0"FT>6TR6G+04:.K9WTW6H[Y\Q'; M::`(F@<<<B2T=8L*H)I(H1T>B!FJ8H5A1J8\.AT^6HMHLZ;R,4YN:AO9""9< MLCHH()HI,2:LH7<6YH'KIU1IU%2():`\TP0Y2B`?T01WFH2T&(Y[&A^XJ'A4 M@A0%@D(L%*@E<6XM)$9JBC22^A'HJ`!FRHM\!GDS)62KN4(!TFFD4T2TM&'& MF%&(!1%LHYZ@TXQE\$[0.(#OQWU78UL4YQL_(SMQ1HZY=QK2(2LYZ(8K@VEU MV@)QD*DH\1ORH::>J@!N%U366*W'QYQY6$F^.8ISD''K9B//"BA^)50BD"25 M!*V9PP8BP&>+I5G[B"DH$2"MV@)1$'%<MQ*N"C9;9R`M1:ZW"5]Y`2CIZGS, MV:,V0F7346`%\EM!\IYU4&JP0@>SV4O>@Y#Y2!Z65AS+5:DI\1%1`*Y..:Z1 M4:[C%'=GA$@``X?K-HH[<%P?2F/9N#"':!KTNB#\ZI3RFC1)ZSKWBAGY:AEU ML+2_\:_=%S&T.+`U8K!Y\Q&M$:N_834$0C5PXEA!E6,K$A]@TY<B%P>"^7/= MFC,3%J[LP:=!Q3).2FB(,K8F=A(7&E!T,J*F,8K=!R`.BF.MT@+PT@(<];4M M4`>/-SH?44>5X[!8";%B)$%B4QSV01I)`R"?2%K6>"'FV;*8AH8[<*%Y:!FM M61UZ4B%I-$[-^K30BALP)"]Z<H!KQH\K=;!5,+FUU@)5D*D1\[`06%=ZU+EU MXF+G"G[0L4TNT)U$<S(@4C"56UFE\1'%>[R9F[P*P+FTU+PQIALRR+NE$2!J MX+FVA!JB0XY\HHVJDB"H:Z$*`+JF(;I=@G8N.;;0J0!7`[\V@CP"DF,!JZ&P M)D;Y=K3.JK1L<`(=L1_C(1XI(<!3:&VYARBT)`9Y<1(]I![LX1ZSL;SC:Q^Z M1H-&%B=:=3Y:E8M\<B!]NVNDX;)H:";CH[_DP;^\6"Z!6Y]H2`=1:1Q(&W5E M5;I=`A$+K*5.>Z`.#,'?DAP4/!O$0@;;FBH&.'L=;+).\@0P-<+&P;R?:SW< MD556Y!N7DGOZ-3IS4B<.B5/U^U^0^U.PBH&RJJHW.[&+VATO04M^>$OG8RI; M`S+'4I(VUXQ-&GUA^$)B2C3#RX"7*T4N&&,&$ISF:B=8,DYR.8C&&#TC]*4` M^X:`HF@I[,>3?+]LQ,>91,BCPVYD2#-BVCX?`2YBFZ#@2+'=$<AY1#C)H1TR MYL5[A"4@MI(1<S+-.T#NTJV\,9]/<XE5=&96<H-M("B!(H#S1#U$.CR^/#+Q M.KG5-'QT,AFD836H`6+9O,OBHV[,*%(LX4O2YRP]K,A@L\>U](E]1E,+2J(> MA&E&U5&,/*],[#KT81_GNA_S1R!8/"0F>\T0I+RZ/!X*$\M=]6?HLG:=E3\M M(`8P"P(7'19N0(0T*ZZU#,"H08M"A1]N8)H7TBK&`TS"9&#ILT$)@K\MC%(J MS4<HD),I,!MU$$P%`S4H(ZSEI+5TA[LS=HJ9(E<ZQ7)74V#PU<N809N&)#DI M309*=B(Y;4T9F7MS%68D9Y%<MCPN5@,ZS=/:^&Q9MTDQ(`//!M4;T5%"L-,M M[6).)`,TX(4H$:\\U9'=B+U5T=%F0$K\V\Q"]2-DG49[+4HKP=)H&%2T&&87 MO<X`U2_9Q5IJ@[5]IFNR-3!X)4;H]]5,%#&9;$7#"B<B``=K<=FY46J9[26K M<S40P=:>W3B@S6MEPQ9<K(PRPR63%299DBJT+7II0Q0%2L=CFVN]3-47$L]) MG2?@V'L''"DY<G1IBRF^A<Y8M"(W8#-7DQO)R3?Q.QLG<=6-=%Y=PH*-`A&] MF6PJF!)S]2IL-$X&4DGCLQ\*&-,)@A^!TATH$#6;D=/(IR+R=C7(C1EMUS(1 MM-XN9EYE1DS0@1HB``2"*<$TK(UBIU$C-&:8$3%K@!]W4($D-7(:D2/_VL-$ MD:DR`X?,&=+C9*64XF?54AW&H2&RX:,HJR"`QGZ58IY31$R`MAGF`3T=A02K ME8VS.CH##C;G"N+3X@982XYS(BNFBQTO44E>!MDA,08D5]'MLP*3*Z=(*):7 MZXSHE<98`1=_/:7LW9,S4S#NEX?*##8BF;MP-S<I6U30:I76VJF3`6!LXXH4 M!0*QB+0@K>=LNN(M`C`3:5AAZ&S)N*3IC%7(*N-\N`(O@`=YX#-VRW-O9VJ( MSDS:^#)87MV)-C`K(0*6K@>GO60:X21!<&8Z',K@!8L\<-@^T!N8)B1$DA(K M,.ME)P<^<`9!R[H!^\G"6(F^6(RJQLQK1X:'O214M1U$#AHJ=W?\46]MU"I+ M:IEG$+<D&'_-4>&C\^;%7G>\4>9F;I.CHXO]*X8S.!)#LD5O@-[9""?.AGNZ MATB\5UFV<:03B1>?+C%AM@+LSD9FB59=<F_U5363]B;>-^=M1'<K;ITHP#:A MG=.BH^YWCH1AQ#TV0N]0!1&W,A!"4`5'L!-7T,M;ZR4T3=5NQ2`.@@*-OHQ% M#2&Q`0+'B2C3`6(,_<L=8;9"U3HG,3<M08XLU4DE)S8C:F5.E8<D`867(JP6 M+G6Z)D49(CAP/4Q(UY&/_>:'1E(I#]]X0$OO?7Q7;V`E["0:"%1#3$534RY# M3TT`)2707```E1`/3#GPS'4```S[&"ESD<A"(`&T8#0+L<B%``"+/+``X$C; MJ&9B&!E8)B:1AV,=78P3<\Q`-#'__J$K$("8CV)B%AU;A"W7OS\F`O7`<:-. M!A.+J@@`I*]''0B`S'W[9U(,(']9ZA!@>E*>JW%GAIC`4:".-4#-`-W+EL@! MS%P!`$P;-V@8@D#C\/WSF$R,@W\6?-EAI&V%ND4^N*0)]`]`@#D'?AE.%'CP M,5&$Y*`8X(/6'#DB8A&8]N[(CG=R,@QJ#(#.@=&6`=1QH!C"YSL'%O5;-R`= M*8C?!FCSN*VCAV]B0G%<@<T1,%I*KBS\,NV<(VF+SJ'SL0.=/RHS@+EP9.#@ M+V9V<D%>\6R>*KS"[/RS,0REC6#N?XE!5ZT"%'1BAFSW<!#=#`!W60",'8NP M0PL3BA@AP$*Y.```)`#H\H@8Z_S@`2X`U+"01[MD9$`O8F!8@D`5Z:+"@B%\ M,,R%`)10C`4=+J1B22<!`,`RP]`"Q"X/+31,+AX\&&$G8NS77SK_X#(``/=- M4A@`5EQ118&#L&/%(C9`8\<Z%RQ"A0&+$$'`#A988T<=">Q@`#5GUB9"`(O0 M@(N6.QA3ASA82D-@F7:$PF8B7R8BYB)\^AF>`@"BDPL2B5CA3R)&\#,:$GN4 M)@6AW/2IC1TL`3"(-``4E.DHFY)I9AT&J,DF'3"L9^9Z;&*J*:<I??J/%%#@ MHHX84#1BA"KW^5+%(,0PX"NPZ#2Q01;HX%/!.A-D!\4_=OA#+3_KF3,?(4@0 MB\`,20)`P`K'0+&(,0,<4P`"3&8[WQA/$&L`I/HL`(P?#@S@ASXR]2`N'P*L M$_`T@^P+0!\+X*+`&/?AHP&SHU1`F+A]8"!3)@-$.H&C!3ECQRC@+>+-`-\8 M<P2[H[%3:1TC/"&3O7QP(#+)@)R!P!\S?P/('^S6@4#.)B,`Q#K=A"G`(APD M<L31!"S"A0")'!"*EHD0<`P1`F@9$=5W0)TE@2-[X(T=BIR,R2+E=/&%%\-8 M,24[6&K)I9=@BKF#%0C4H<`.1AA01P&XI+7.`G'.:4>==V;7!=MO5TG`#-)@ M2`"NU!I`+0$.H(.,$8T^&NFDE=(!E`69AK)IIY\"<+<_?_/-#QT/4&NM'?R@ M\\@_661A+QTE+.+H(I$69.8H;$(N.>5V6&X'YNB\8<0H8V='6^`0I3T,Z*4M M@(X9`.P>\#WK9(#+0U!(.ZT-VJ(3P!'>XHI.&+AGD=WU/E!:6@*$>AP*>.M( M,#Z3YG,7?8S@K6$0RDR&R@6B,@<I7(5B;)F@R2*4@04M#&`8$XA&V[9&H"N` MZ0ACXE,=+*"J,T&`@P,0&]G,M@AXK*,`Z%!"/UJX`'O0@0-,&=L#[>`O`O`! M`B49FXT&L28[!&`0?(*(,=AFA7\X0(=CLU<?DC`:,P&`#RCXPVE\P"8`V.$# MV*.#"`[8)S85H%;2N-5]T``5W0%#;T&T@XVDUX,X_F,`@W`!FX[H`2MR<!$> M7`0(374F$A;Q*"A48=D0<#87PM`4_&CAN9@H)2JQPUQ^0$`HL'&X8_"A-G_P ME`_P4QH$K`<_Z!@#$+*`"P00``I70(?J<C<*_#"&DZ,A91VP\+_S<1(7%6"8 MKV!QGU1(P5O#O`\.,,!*5^:*?.@XA@YHB9^2</(AN12#:CK02UP009A&("8Z MY'!,8A@@F>IC)CI8(0$LX6<='3C7N@A0-7GB8Q^"$L#5+/>/JTWE'X$PABLW M:0==N/(>XAB``?!3ISM,PW!0("@2#$H`A-(DA?C!0M0B8LNEA0(_H]"21"F* M4(6"5$\ER51*2K*IE1K`8RX%3TK,AXX6]"-W:%,;VTH"#CN4PP[#P,5/U=33 MOU%O'21P8D\CTE-%'``3][F$!=QH&G1@@P9.%(<=T!$-K#I`J_-K'@W0,0D: M@((<=H`J.N8QU9)H-2UM>]LBDK<#&S"!$*M!QRG^L3O`"0X,Z^!"7:&`5^TY MR5YU^-XZEH`+']R!J':PPP]"X0-"%*`?_U"35OTP@W,QU0Z(.`!3>FH%=&A@ MJKN;`"Z\P"1T.&*J3-%J:3LPU1:6U@^P-8!6:8,.><Q`<6S[K#$.``">^M0. M0K7#?6*!@RP,(YHS4)\%VC8:*82.<!ZP+B!^4)J`>0`=^P`!#>VA-USHH+4O MF.K@T%&,&936"%-%AR]FP+;JK@R&O:#!.I@0$>URMPXX*(EU;91<R,Z!!-)" MAU2S,`A]_*,.&.A!2NHP`4#X`*TZ,(W"`,"E%^+B#!R&`#KXH%_L6I<IUG4J M)MI6278X[AA&\`>B8`$`4?S@#J)`:RY#]X#1R$`0`<@D]>X##`<PJPO^0(<! MHJN"Z$H8'6WXQR+P,9HK$*(TM=D"`2R,5M74YA8`X+(==%"'VK#!4_]0`!T* M,(A_-*`.,?%`3X<+`'3\PQ]B+D(=-@!C?OR4-O^(U"*\P(_M@H`.];@6.B30 M@"RLXP5B#@&$DWN?:+%2#@Q#1Q4JD`4![P$/8U@'!*Q`Z;.F]3Y1X("\C&&` M+J_#!V(FLP?:#`,^L/D?.?`#!7;@@[O2P0&\)BP=$,#J+J<C"O^()3MNNCMB MMUJ.Z2C%`-"!"G],.1TX^$<Z-#$`F;3@(3VX@Q=KHXAN8R,:XW#!/TYAB02P M0@$7:,`]%'`,!41"`8980+SC7>]O*$`&"ZCWOALP<(`//`+Z)KC"_3WPABO\ MWBQ@@,.?D/!W2%SA#@?%Q56Q`$/@^^,>#SG(1^[P?,>;XPX?.`$NWH:$.SP2 M"<?#Q1G!@%,L8`4N5S@G<IYRC/L\WN^.][SK?6\<+&`?">\WO!MP#`8D(^$4 MM[G!%8[P'UP\WOXN]IC1`0M^I(,$2>;!#.%1PS\4`$M(((03AES:3%P@=^EP M@P#0$8*QE_WL-DC[VM,"A=("X^U92,<4YFX`N]OC#P/P15JD55HS`#X=.YB[ M.R))]L,G?O'`Z#LZ(/3X$<S=&I2OH1\&D(AJ24MVZ*#!XQ,P=UZ$WAZC+[T_ M3E\M='#A\>\(0()EH0]FK>("Z1!'`-!1B=<C7O%U_@0_2@N/MZ=C&</WP^O[ M((`9V&,0_B##Z'U!ACH'8OGH(`$ST]&*X8.!\NB(`S_NCHXY7."%I<V%\RDQ M?"ETX#[GD$#@$S'\$-P?'=R@?^G@!P&`"[(`!_<1#0(X!P&0#@R(#B-@?)<' M$8]Q8SFV57VP#[872?B0#NQ0.P3P>J.7#DN"?*!@@6B%#EV@@0*P?-)B:FIE M":)A3EH'`.GP`<-'`W/W#1:0#A8P?/V@#UI7!.CP`OLP9.BP#"S`2GJ0:4*@ M?T2D53"4#_MP;8E0.]4@A,\6`NAP`!IH#E78@;031@E#")EV`@*8#`"`"U2P M![ZT![CP"0Q#:>AP!TS@+73("R_0:09@7:4P!ND@"G56!558><>7%J6U#0H0 M>(Y09T%0B&4W@:4U``L0>'H``%K7`NA@"?J`#G40AC7(:[Z6*CY`6(FU#EH0 M2\G``(XV!>A@#BZ`#@KP`KB`"IG&"!'`+$@0AJ)("'1`BJ88,#(02R'`3.NP M`O(5B^+@`KCP"IE&!KF(#B#`B[WFB\"(5P&C`+$D?HXV;:/@`ND0!U68-FL3 M5+R0:3:0B[@`#IGF`NI8`&1P'R:@CC$0C^C@`>KH!?9H`>IX"O;H`.H8#_9H M`.H8`65P'_X``:RD`P>)#O:@D+A`!PWI#A#)"0UI#A"I#0WI#1")`69P']8` MD5?PD>C@#!!)"21I#!"I"B0I#0HY#(W##F2`"[=`DJ\`D?)`DJ<`D2=P!O?Q M"1`)!CZI8#CIAKB`"3[I*YQP'P*P!/*BE/?1!Q#I#$-Y%*PD#T/9!A`9`VAP M'V4`D730E2H(D9H@EE4`D1F0!KF"#5T)E>@`#$KPE$:PE.BP`Q`)`FJ)#D>@ MD#G5-M:W#BJ`#IG0`K@0`GGY`7QI##V0%G?0)>>6;NLF!0Y@!0ZP!9FC#/[P M0NA@!/^P#@!`0/X@`'P0#4P4DP10,*:$*@5C.;!#+::4/.A0"O60.[N3`+J0 M$KB0$NE0"'66!_8P7FARFTFBFW-09Y9P1#)&!P100W6@/3Q@#YT#*?RP#B%0 M6O1'51&`#BA@#V?T#[82G9&R#@>`#C;PFRZT#`D6!P'`8!<6.B<0$42P!]NE M&M^%`>9Y+CV`**<H#S5$!]-F!?:`#N10#^`92;\C:&E3FI:$`*CY8`:PFO_` M.ZAY11_P!P_J!P;@15V"-\!C`-UI*^ZC#?1`F\!`![:)F[JY#:)$#J$3`WI5 M#^A`"O700NCP#C#*#O6`?0I0!]\UH)*DF/H9,/Q)7B<ZG-IF"KT)H\8PH_"@ MHW60G4U0#Z@#*HVB#Y""#_W#G`P`8_I@(S"&#S8B,.LP#=3B8':`#RHXHE2% M`,*9FYWY!;[#+T:`#P6J:74V`DPZ"'-Z1%;0I4X:,"]06E,``%1%`0F&!(2* M#D^0HVZ`#T?D!5VZ#L`0I\"##W>#`'Q`#*W3-W+0"PDF`H3J.]:&H(L35$1P M!Z-@!'LP"O%9$D(@;L5E`$``JW%5('-E`/U9&X```*'PJDC0`P4``'=0!C2! M;NIV"G0``;:`%,H*`<,``9O@K"?D`:I:-E)&K7L06A&AJB;S#X+`%*JZ#C6P MK7M@K>2JK=C:K80`KGNP#AI`KL/UK080K@T`KP>PKO/:KK'ZJC9BKP"0'>FP M``2`"SBP!VJBJG-@`-23#OR`1S[@#J%3&]$@`+UZ!SV0H790&\9P-(]YK`\0 M`1T0`=X@`38P`2D@`0,P`8$P`8O@/T7P6/DZ!PI`KCOS#PC@G^F0"4>#K67C M#YB`.`$C&O[P!70PL]BZ,T-[BG"01ZHJ`.D@!Q0[JPKE!(2@4$)`"$"@)I7% M!PJ;%NFP!PY+M0#@!R6`"T8`LP@K,T?[!_T``G703NEZ!/TP"*%`M4@0`+)Z M!]1)KCU[KD=PK=QZ!/Y`MU2+M[/*KNG@``$0"E+;AU5K`#R`M5I+"%R[L!/@ ML%)P961KMFB[!W.@MJJZ,VW[MO`JMX$0"IE[MWF[MSPKN'X+N'M@,OYPNIEK MN'?`KNM0!-A'M$!`LW\PM'1@`WPKN#]K)RHPO-:&K>@:N/X@"*7KG434M*YZ M!QFZ#F*PNWJ#O(W$M'N@3P;PJEX;!'#2NCY;)W20`[M;M+Z;M,=+ON<:&W'K M#X3PO($0O=T[O4NR#I[`O=[[J@1@@^[`JXT;N5/[N%>;M0:PM5UK@_,0`-/[ M$.F@AMB7-PDPO1F:#KL@P'=@M8^;N0H5N0BLP`L+#`[\O7<0K.E@"9[B#WF# M`/C+83PPO+"[O+'[MX(0"I%KNZ,1N6/[`#3[`VYK.0XPJ^LP%?DZO6'JE"SL M,Q8,PS+\OL]+"+U*"#KL`U?;PS\<Q$Y$Q$:LJDC,82N<`'5`!*/!KVN&PX0@ M,-FA4+,:PH0@9%[;#QPF-6T\"K/*N'>0"??@`]JP!PL@#77`#/::`.P*L-BP MPGE#!:,!L0)0!^AKQ>)VQI&KQL#`QB?\$),+QS:X"AR6`'A<``]AQW=@K\B` MN[$SQ*.<K@=@2OD*L*%46DV0`%05!&4<R06`QI1LR:"L.@G\Q@=P5%:P#IX\ MJ[LLRJ45`[(,",@`".];R,`@KK5<&K<\R=57R7GKQG[PRX+S?G1\!T`@RIZE MO*+5RL!0JE%BF@6C``[:H'2@`=2BSG:``.C``>]`HLV)#OGP#E/V,F_$IBC: MF4K@"Z?:9FJVG`^F/;OP#@6*5+@P!`9K#'>0G:GP#A^:1@O-`#*!"`1@!.@@ M"_J,I<A@#/];$O%YJJ(*/)%$CI2$SGZ@SJJ921%:`NE,H18ZH7:PH3-K!`A0 MT6J$#H#@#B0Z;&V:$NNP!0*]!TX@TG7F"1]-T$^*#B5`T6F1.@O]`@OP8%L: M8UX:*6'Z`=[T6,8@!]G9#^XPI:'",>%Y`$Z0T02P#K:0"#80GR=-JN5XSI9$ M!HN025>]9H#@+W4``J&@!`:[)GO`!PDC!GF)`P@0>!TPOH*-;BD$!7NP"#^` M"W*@EK&D`D-@SX]C#^L0!1@""+"$#JG07%GPHU+T`[&T!3L]#_^0*W2@EFZ) M!4+@+:/A!6E0&@K@EN!P`(Y6`6XI#+Y=$F"0!IAW'\O@`H'G"E#B'S10J+A0 M"'E9"[XM>@506K"@`H$7":*D+6-K2N@S'YZ@W25A#7N0%E?`AFZ(#K\@!+1) M7G[%8=4\"I0"U]H21K=,`WQ`&Z%@#NL]#4"-#KK0`*/!!*%C`*%@X*-@7:/A M!*%3`R6!VU/M`\6=/;&T`\-M`!4N2@-FHOTEG_^E`$A(#I7(6R>34^7X-H$- MMD8P)H3-!Q2P8UBF5P5H"GD9#P;@:%W2XG@KV!'QV.,0V7OP#S<P8E#1!3!9 M("N^`BW>!R*N"WDY##D^-RV>`@;@XQX`Y$).Y.C@!$?>-M/1#JA=H@=06F(P MY>X@BP:@H'`3S^@P!04H#7F9`C"0!?>1`0/`8!O[#TX`)GUC+WXP`DBS72U@ M&H/^`RM0!Q4PZ'^0%G10K]XP"'M>Y`+C:`]U#7G)#GLX"MBP!Z/1Z?<3$9T^ MGZAB37O@#6G0H<2S!W`MV24AV`3&=^@@"#*0!:/@X`V^,H],#>?=!PT@5'FY M"3>%#DS0F1Z08(]P4Z/AW9^$#CF0$E>^!RNN*H4M#@GV#$N(A/`@`A`C`NB@ M`B2`#I@@`J/!#972!Q(0"FF'!*-@[J-A`WQ0&E/A`.[.1;TNXN>0V#>U#BB0 M8"^@[#[`[`&#`1MV'T50`,SR#D8LV-/^XMC06!EP'T"P`NS)[-/&!>^`#DM2 MJNB@"-[N"MZ>`M[N"X^3>>B@!MR>8#JPA$$%5U&2':.@X-95$OX=K"6AWPP` M`+\@#[%;`&)P!+Z@#FFP#AY@-$Y#3P>@"`2`"`5P!/9`!`$@`,.F](``#.#` M].QC#`P`"#R@&MU@#,%:URU6)6J0US]S#"V@3?9R!RA0K)!Y"JJ0`:R0`>OQ M2WG0!KUB!)9P'R#P`W*Y]^CP"@00>$40`$&?!NG@F4\?``%`!S>@]#][#'4` M`XL/!'5PO%QPFO\5WY1\]!N5^8OP`9#M#?]`O<TS^.G`$NG`R8LO]8]/`)$_ M^95_^4<_"#\0`%(A/0-`/9X?&Z`O^D%.^J8O`ZB/##;H!@#0^N<+^04`#IDE M^2\P^RE0^S_0R"4_/0_1^T<?^J-?^@LE#WF>#HC`8=$/]01`!RD`^3L@^2(P M^S*3^40>H>>D]_<1`S[P]T26Y^MP!9*^]5V/LPV0;/@L`%1E`;&$`'F>*X2` M][ZB#_?Q#ST@+^OP`.F-"'CO*_IP'^[0`_*""SP/$%`:&=$'!=>B-@&-6(*" M#EN/0<00"%R([LR`+(O*=?GB99B5*U46L1OD!P&`.@/2B0FPR`L"0#].IKS" MTN4_(P@4'<"T"-^B8[D*`-@%98.+'0?H'!@R1-R]=(8`N/AW1D$"`"``K`#0 M`H`8`&0VA#4C=@.@#>MXM7SY8T6=!#<1+,(YBLF>N0A&2=F3CD#-G#M[F`&@ MM`<<PA?6J8B+ZVM"!`S1`<@XRMR>=2@8.Q8(&=VTR>5&T>"S;D)C`(\9!@,M MFD]EOE#^^5*7)ITD`/:(!`A`YX,B`IAV'*N#81`?`2<+X`H`8)V`&<`6<2&0 MZ$"ZS.G4X-8M@(Z'W\&'7S`N($`=`M#7#5@^>7KU=`/^K3.2.T#W$.`+@/LG MO,[XXP*<E]YZR$E'G77<^+/.!_410$<&X/4GP16<,;151@:^MXL_@QC#`"`\ M$):2&?]<@4LL"*&C`PT9L<?0/"ADL5%''X7$SEJ+W``B#'P4`&(.?E#PCQT* MX&),B@ZP6(Z+Z!P3XPS_[,B'63SD\(<%/^FB``#WB`,%>%H,`,T@PD"SB#Y& MK`/.6HG<\-.,'H$D$A-,#.+#%82(F,XR`-A)Q!XG26"G.H#2H=P];:B'2SYM MC/+G#/8LXL.?Z10"@"(%8)*($?[D8L!0*W1P5%(&,.74.EQ,=08#@?PCR#^# M_$/(/R9=-40'3'1010=>=-!'!V=T@$XI'@Q@@Q2$K(,$.K(4:P,/R?Z`3B'. M.I&L#>@,XJP0R;:`2U8,10-`%D8L8DPF]_B@S1X+2%,'.^M<@$XH'CBQ3@3W M<`H(80*L@\"0_J#3RKA&'`%(#WK.P9\-/A#"AP-V`I&G4GY.C``N6S)TR+@9 MP6DC202`P(<!(/]1AW+,.0==%\-\[`<!)BL7TSH&S$5``T``P/)'B]A`3AN! MN*,#'U#:"<\>YEF`RP6H_73$(M@H<LC0F##4RP999+$.!;@<T/0Q3T<]-1]5 MHW,*UEDXL8`]?RASC`$_#\W1,&_'S4?+(N&B`R`[P-W&'0.@\\D_BAA!P"(> M_(S(`?_XC<XC_^QLXR)V&(#+$:A9@4X4''1,HXWHU"!L%L)JT<'EJ`UI0.B= M+XEY0>:DB$+KZ/P@[!$>+`+/(/XH0$<%O#=`!P2\@T#'`N:*@@,?;^^UO.KH M[+!#%GTW[$?@%!#014>A*$'(``8804@!$J`62L-:K"",1Y-7;FX+_OS3`W-W M=`!U-.-,=<H^'B#PP3^YLP$2"-$A!1V@9P.TUV2\@3_\+>(2/`#$(J8&B.VQ M3R2^2%GN&#@.!T)0@A1<A#>6T,!Q).*#B:"@$78@0(=Q8Q%6D(L1:N:!`9*P M@R9$(04M:(7)M=!>&[RA!R,XP;V)L(0GC&`*]Z8VMGD#AC(T0"(L$,%1[(V' MD"K-$7%XC$-$\!__Z.+>P+C"%O+A')\3"13G8H`=4!$0?VC`R/JVMSDH!XSK M<$$&FP."+0X1A'O;00SYP(`_%&`',[1#`<R%A'4HH(P#Y(,V<`%&"]H)'(`Z MV;S0$KA8;`!O(V$'`7`1/X;(PA]92(05_+$I?I`D?G4@F1_X\0\Z-``=B""' M(/WQ%D3RXSR-DX$@.A:GCXF2<@@XAA'X`0``*-,?S20)!`C3@%`,$`D[,$8? M"O"+E#WG'POX1QV\8"<;\($P%;`F(;!IC#BB@P<;0(<"-J!,!Z1L=0R()PPV M0,I_,&0&J!0D`TZ&2-^I`!WHB)\=&#`D!:!C"ZC,0CV94\YST@$!Z.B'!M"Q M#`WX"Q?\2)$?^I$V<RT`&'S`PI`82B1TU*$=65,E`S:E`)(PM`XJ((D"Q$D" M9?*#HGYPP$DF`!V&)(.D@]"'.`O0`S#2H5\=>-N?FKG2AJ*#'23-PDW\$4Z4 MH*,3^UA'*$C"3#H8()B"4`\ZHL$A/T!3DZ1<QP'"28?`]8$E&IE;C>;$!&1" MB87<V,,?(.:':=I2G>S<)GM4!HQ-^>-MPFRF$TR*4B14M:5T@&DJK3!3(]34 M#S>E@$[%25BAUJ%?(Y`JH`!P68=.(JM'>.5)$H`.8F0`'16X[3WTL8Y8H.,& M7%TJ.D"@H`3(MJ[H&`=HX-1#D0Q"E)`J*CJ>P8_-LG*9KQ2G+&E)!R'90:'\ M0,<[V!'35;8R36LRAN=`^=Q1EA(=E*BN*J_K2K>*\P%D%2=M7]$/M*ZC`JHK M*L"&1$L[$``=W.@'1!1`3&.B!Q@,F8)\S8M=^]9A`?G5I!OZ\=\`0WC`=BCP M@6/!(6(P6".@A*P@FEE4`5=N2`1`+$/VT`]B2G<?^\B"%!@RC7]DC;VB-%LX M3DJ'(C'`#0R)0X[7$0-TG",<\VTE.H*!@<<5&+SH2$)UL]#B#W^7P#(.78E/ MO#,YW:@D167(-]94#B(7Z01(1D<'EOR")_SDI'[P`#I6$0[=F:L'6QK>VNI0 MI"/$F1_ZT)H#UD&.=6P#'6X(QX[1@8-_=&QNQL3H#:!,85<JM0X/:*HXB]2% M.!\CT>M@P0RD@0LR`&#'-LO$(IS@CY;PPP'H0$`*=HSK'IQ`E?K8%#Z&I%0[ MX`,=G\H:,?<:R@>_*-%17B8,&8R37?[!"SWRY1RNL,=U*`'$X47`.LI+W^S& M,K]TD``,:[U,02K@#R$@*$[NH(&BNDBZF4CTCC6G`06;.!US`$`9`^L'6*RC M!0T=$D:[D&.,P(.R?*@WA%W43X:(0=]0T%PT]K'@=3252`I'1R6^D34WX^(. M<9Y&/K1&`G3@8DT^.0:@"6,`NO;+';%2@%>C``YTO``<D\Y'5B]-HRQ.P(_C MZ"(S6A7&0RB#Z4;@G0;X@(Z.N(P`81@9R/3@AP;@@A)QCL/*%_L<8+#,1C!& MQS5\3.1^94"ZOEAY2P@PB&%\`HF76'H*GWYVYXHR[:E@.S">N@XW>8'N=B_A M`Y<^P:='V]/W%36A<8&+.`-@Y>L(YY=#C`XP1'399GXN@T%+$@2\X&3=?HX] M=$H8N7A!`3`YB6A]H(\VG&0!N"A&G'6!CRQ<)P#KD(&+'8J-BZ22/B>],"[( M$>=-]#X="0B`G6K?^G_8H/;H\`,(,$*?4=1>NNGH?5+%&0'),T"9^FBF,O'1 MS'7@8-`/D"XR>@]L8:^C!':2@44AP*18T-\*P68$^+`.#E!Q\_)_`8@/)/%I M$4`2PZ9\]!!G&O!\B-`G=S(Q*>$'`+`VO0%#^C`7^/!XQ#8DQ_8&[E!>";@. M'3""QH8.XG""FY6`'@B"C[>`XD0\?O"`16(`;\`0N7`/6I,9-O`GUH=]DK`` M)3=X"6!GQW!2?]`O*1$KX(8._+!E1%=,(K$.*8!,B^`'"D"`($(8/H(PI^4O M!?,;.E``=,`.87@RB]4`-\%@KQ=[=?`,*S,W=H($F:0!N$`%>U`0&-"#N$`# M@F`U,I`%!4"(##$),0`1!C`,=D(-2.,'_V,#DM@]>P`$!;$!@JB(Z&`)AYB( MA8@.;-"($3$,Z-`(!($++-")H[@'H>B)46"*CYAI!F@$0%A_`FB#YY:#M?0$ M+(@/N&`#KL@0/A"+HT@!IHA1`-`-->A6`,`')/`'VP4`=L`!X(8+0%",Z+`` MR,@0X0`#$"$7*&:+[S4-]A"#PH8.M<`-SPA-?)`$U$A6UA@$O"A+PT8'-0!N M,RB`,Y`,N^!JN`8(),!K["@".X8+1<"-G1`#B.B)<R".$;$.QO4R(H(.&&!I M*(8+3L"-LP`#68.)0-`WDM@'(X`X2K`'0X`_`^`!4+`'_W`#N#`%W$@%#BF* M#*$"$OF(QF``A`@`Z-`&`3,+`9,(`8,,_B!=^Y@%Z/`/]0`UQG`(/SF(@C`, M/?F3Z'`$`6,&`5,*20EAZ"`!/H9@3XD-43F5A&B5/KEBH2`.>]`#R&$'*7$8 M]Y,__W`*%Y`"'9`"-I`"69`"=F(-A;(Z`Q`P(L`AQ[ABR&4$\H$$@3F8Z*`. M_8`."8"8D64HZ(!P'\4<#+$*J'1_Z.`,"N8#@GF1YC"9)6"9BED`Z,`X<B5= MAO"9SF0`I0D`@.`#/RD@P+`.#(`X#1.5D465H%1FQB1+)E$'7@<&/8@.YN!C MT)$./\`2='$#A/`V+T`(S/$V*I`GLXD"W/DV-4`(+$`(N,`)J($.=G``62`0 M_@`%TE4!`9"$)_.<?F$GVQF-%U`'"!`(/N"=__`'<N!U;K"<C>"<P)`.Y@`` MH>"=!?`"YWD,(H`1^-!57M<'R_D&!IH.R#`9TUF=!G"=S/$3!>`"`#`*X5F> MYYD`ZLF>[@F6J#`N69!\!?"<EP``QE`$*&"C19`"SE0$*J!^1<`"S;0(]]`A M+Z`YH(41V!![=94.;S`9QE```S`9\``B"*"?BD`$`X`(..HO(&(>A;FC^>(/ M!$`83(!^ZK=,[0<#5EH')&`N4GJ>-O`"U+-,?%`!<#JEF_*!43JE#/$!=-I* MFG,!#.!P@\8,<Q%L/CH7_)`(0?H35\ALSU4SKW<,!P!4)D$'+6`GUZD`?.!U MBK"<>(!*SVD"`1`*U]E*C7.=C%H`%'">UU``$JHR_\"B\A2?NC-H,WJ@[$=7 M"?`1Z+`'!I`USK$(_C"FS%$'!_1Z@7`"\3,7_E`"9=H25G%`'D"=,,$'=9`# MC4.=MBH,'`,/NOJ<L```P'H#PYH%R'H2RZH`S?JLG"*MD_%Z*KH.OE!ZK=>J M'M`<9<<0/R"K>TIL5X`.^R"K&'$/ZP`!Q*8Y0Y"NFI,`2)BK]O`N^$H'&X`X MU-FMA``3*4`'!6@)RWD())4>-A!\^%H'+("Q&QL3='"2U[JR*5`'!3@*RQD& M([N;$,";N``+RSD%-UNLQQ"IH7=,+F&IS80+G\(0J4``Q\</D**R3+H$4/L# M?$`'0C"U'9L#MHHY$MI5#[8.-@"M)1"B+J&B@]8`8CNM97L`<G4%FL,!!DND MZ\`*@W8Q5\$01,"TC"JT#F:KC("KXCJQNYH.>L`2+ZL3B&`$_0`((Z4"#\*X M_;`"=#`!D/L#M@2M;')`QI`.4A``ZUH'E#M2ENMUO+"<:E!=SXD#`0"YDJL< M9:HH9(L`U0&Y*E`'%#"FR&%+5H`QY^D!3.M[WF";C5L'0;"[2=N-O]LUYYD) M%Y%QZ/`*OVL%FH,*R7N\@]"\FI.W68,+=XL.=94%SEL#OYL.-<JZMHN[U+2[ M"W">O6!\Z=`'PANY=0`$2'N>0G`1/>&VZ!``T:N\#`$#S8L.\F!\]<L0(M"\ M`SL.!-R]$X"]Z$`-QE=XE5L',6"\YTD&!'R\\B``X*LYL[#`YSD.')R_FB,* MQJ>_\&!\&CNUCJMN+PL3DIL`4_L#=<``N&`-RSD#.98>`F`N?.MWAU.T7C,9 M+ZNQ.H$)N<``0V$*+C`J2F$J3\$.`:`J&-!,)```.<JC/[H5)!H36P``7``` M7A"-7`$#^7,`,-`",-`#:PP#\.`";$P`+]`$,&`-+J#&3((#(PP/Z:`"`H`+ MR&$A>YP.'?#'?HH.'K#':Q$(#S"ET-?#+_P#7E`'_"<4#&$?#J>QCH0+9<H0 M\0"X4]L%="##D=P"E*RR1KP3#/$`(QS),I$.&Q/**)$.@#*UIOP%*QS)HDS* MV!H3D]<5#`$-\>EQ*JL(6DD+DX$/3W.X!X#,K<2H(WD,?>!UX;"<[+!RSYD% MP&<``[L)\7D$.S!+?E`/F@P!J&P`U'G$#/$'\9D.;?`/TE4'N*K+MH0+\K"< MJ8#-!VH!`;#"+L'(CFP``<"B3/(.B6:H@ON<\,"AT2H4:Z&BZ0`.`$!)RXD& M^IP.U0``Z0`-$\T`<,`06'#1Q:#1P$#$O7P27@<"'UT[%\T*$VT2G1FNYYS. MJHP.VS`NTD4-'///C:S1?\!:#D"=N+`"'VU]U.ES?MFU_VG#-[#2\-![Z>$% MQ1P`B:M@_Q``?A"Z_>"X'S)2DFL`$QP`=L;3%'75RKK(K/S2Y\FCAOH/63W3 MA*#.Z%`%X_($ZY`.TK4$'*->>44C9B8I)SJ>_8$!BV`!V[F@XS.E@R`-`#!% MVSD*WOD3ZX`E]U`X"(`(!&`$QE"FFLW9B6`#X?DFF)8W@FT`X4D'#U`XA^,! MX;DXC1.>CC0(!N"=TF?8>4*<?,6*A-`WX5D'6&+;B!VGB]W8M@W9R2(#@$U` MIOT]H1">CA7."F`'<$AK3X`I+>$/B[,.[D`Y"F#9F-W9`K?9`O?#[$`GNHU( M5]HO/H!,#PNPG");9;A(W[4.ZP`B!W#6_K`"7K@.B^2%*U!K0&$2TX`/@+`? MB*`;/U'@_W#@/=S7%W0CE<.!-BS8QF`H(&(`A.<`"I>GYVD(&LE#?UT2="5+ M6X*<N)`$*XT"0$BNEU($+H#,/6">]8P%*XTO7':@F=`G??"C=#`#W+T(7:`" MZJPYN&`"&"*N@P=@*"M.*J`Y8W`"&9%\4)@FV#!H7PL&<Z$`HQ`#&]L'/'H\ M#36P40`:ZY`$W`T(?9"C=&`"BH"C5:,Y:F#DY9"K@S<!P$JB43YX*-,<'Y`F MW+`.&`"L&0`:1+;GCB1"1D"'Z'"'?FTC=)+EH]`"A(`7HY`#DXX3KEJB,S#I M)8$7F>Y8OK`EI7$,0M$GQ]##7O!80SP(G"(`3?0'Z>#I#LH0)S`N1$;8GI6Y MH2#I;'($7'-/Z_"URZ570RL7=H``@Y82F2``V7L1C@4>OF:-*?%4=:D_T3`# M-6`#(4`#,D`#0T`#GD`#RD"(-9`/-(`%-&`/-(`,-<#N//`L\/[N\A[O\,X/ M-&#O^'[O^I[O]O[N/U`#_Q[P`#_P`O_ON[[;!L#EAO*<*2``Z6`!D"SI3#JX M&.#P!1#Q*YL<SWD``M`XDIX.\,`2?=K8TN8%_*`3B"/I.I`4@VL._<QYA5`" M$AHK6'T!5V!]DDY@CX-_Q%!S>OZ<P;`;/O#QJF"X$L^R%Y#R<:W*',X0I4`" M\CFXE=#/!O#Q?F#T&4\'2>\!DJ[.38\.>0#U,?KS!YH&5/_QG9L.3Q``Z<`$ M`0`>/3$(Q\`.!Z^JWX4.DX!*1"8"B_`-+.D!ECX(OHRVTN;W"@SXA%`=[)$. M(P!\@\L!;!]]<*^`QR`.=;],`(,.4*#W@\?WAM^2FR[X*+VWG^\!FZ[XS!'1 MS3&XV:#1QC"O_@`3&,Y@(^]83"8IO#[R#"$`G&]6B^_2B3#TA*`Y20"X'.@C M/(#AU/"<DZ#1^V(,1I`/L`\35RK#J6[,6QK](*)S7[L>S-%*E\^G<<H08\`Q M^!#._,`'+,"XTG\\2J\3.A#]E)SYB4!2A;Y8-&!G`S$`QQ#IR6*'NUD"I;%8 M'J"S:K#263`/-YZPE.,/Z0`&_8`+?+#21S`/-T[>=`(E/</KF*ZGG`(0B[P@ M4'0`4XL!`'H4`'"GS")LT<:Y^'=JD8U+-F+98&;C7$<;HFQ802=F1)9%^`;< M6_<DH#^".@C4(7#$R+IYZX``X@&`#@5%!#!!^6?''[I.(K)D60",#DV;Y]:5 MV-GS9]"A1=$)2KH4V,R:ZZ(JP/4(#A1TKN1EF?$OD8T6A!8=6U2NRQ<OPZQ< MJ;*(W2(["'"Q(+3#V,P9P-;14$0D`":4@<[]`Y`+`8!IYQ@+P#0`7^3)"_X= M3CQAYX$Z"!896W%L'8)U!E0/:`W`+EZ^[`:Q@X`82F]TB0`H/68$'@`#@WS` MVQ.@C@%C1N9=6$<`^KP#Q.D!`$#<WA,,C8I#"0\/"BX(`,[:BC"(6.RZ>7'K M!F\$W]DYPO_J2[Z\.78CZEP`P#H%$*?.`=P9@0X`">`"!0`K&).($>R$A\YX M"IJ'WEENL.=>>/:AXT=^==W6EVX00!>/`-8!H(@1!"!BQ#TO&B"C/>1A6-YY MZ:%3@X?O=1'?B;O]]9P1\0S08HXY\GA6`T"^>`\M`"1"P(OV4)D($0*\6(^6 M1%QI!#U@!HF7%?(5:8<!_]`AP#\TH&/'/TJI&$"+C,5H1#V,V3@FD^(YB<XK M$+1G9EY[$:G`(GX@$)X]9YU!YR)&V#.A/W_ALX`]?2`06A\$(+8.!X/X\T2C MH?D1:F(/R#87/%809=0!=)Y4UUW#X$8I`?=@<Y8J=`J99HI((J<<<\Y9-UUU MT1WPHI[P9+===]\!NN.&Z%!0J'NX!```7<(226P\#UBK88_D&OHB/%2"FRN1 M(*`CS3\](%@'"="%<Z<1XP``73D,0$=.`X""<^Y9N#Q@:$[Y*LDO``4?C`XG M"KN7[[[]>BEC.2_2(R,Y$0MJ1\5!(IHF!XPB,,@_#=3Q07CTG*6"/R?900]Q M_FPGZTCJ]*-4MP"DPPL`*R=0APC$\;/=.@\0-18(/>Y2ZS]&\#.('_P<X,<Z M0*=3"='_G.8`+@B>90<&670M"-@*U.$"S&<QX_-?])2*`1]C>7L6)S2O$X`1 M..M<U$A\^)RVMRS]FX!<'R<'"AP`U'%`<J)`3@<#H#RN@`&5RV@.;"\JX#ET MYR2(CK?AD8.AZH)*XH"A0)\5SMQ&D`,NKGKIZH<"_"%;P"]'##C`+Y#/8,\Z M#EP1T$``"(5..IB68P11"*#CP`=9%';'.;8-F1L[&I`^^;'-'9G.LM"E<P!T MY@3PZ%GQ\%.S/93B<ZEJ,JX#G3K^&M$.!M!AQQ/"8PX,%5!0]6B`H<"%%W04 MP!_#&,8Q#(`$0F2`.P;P`2$>-$$C$&):!N#!!S$HA1%.4`@F-(`34JB$%`(A MA41((10(40$,,H$0EGD7.X34F]_,0'Y9&$9O<(&!'ID`B,-(#CCV((`Z%``= M"QA08,J&CFM8((C#0(<<]I'%6G`Q@A.LW`,`($'.08Z,O6L.`B;X.`,,R`!L MA(.WROBX[0PC=O*2'RZ@=A84B.-P/1*&_.ZH-W3D8I"X00<@_H$.4OP#%Z0P M"SI,\$>B$``='_@C+DPAR0O\$1TK`(`HJ$`(=`3/DNB81P>R,(HJ$"(YKNQ) M8%`A20E0(`N`\`$I6U"'`9BR-CK4#0CR=0/H@`-BX@F/P=#QA@8H!4ON2M,P MC1".8AH!'/LRQPLBAHXD.--6X?K>-*MIS&RV@)LF^*:[)L@%0@B`C&@BD@1P M9@$,:H$0WB*..[83'G=@R)^XJ.=98L"`!=K!'8$PQV0F>,_MJ*8$?_A6B4S6 MET"P0QC?LL`RQ("+0%3)$D!HBR%\\`]B>(*D2S`!0:AD4I(.`!LF,`:"?F.* M`60A$880PC\F^`LQ>*NE_S`!('X0N0&DXQ$!`*I*#T`E(/3`HW/`@%)7^BUL MV.,0Z9`C'=@TTG^L(QM*'6I1T[&%I)XTJ%1=!#Q4TP,1]*0`I3I!':CPCZX" ME1&&F%XB/$&$DIYUJ50J#!T@$%:B;K6N)%W'./YABY`REJ2,G1YC^XJ.8P2@ M/0@H;$\(@-A_I(.N=\UK6_BZ4T,@83(0\X42VN('9TR5J=]:JS&80@<>J,:I M4#V`/0B1U>:TH%0JH`-<_?$#)P)M'0)`3`),^P_\%>`(I>*"<$NE@UXFHK6= M7<<LC+&00?CBM#TY@TG[*M0?M(`.7>A->*@Q'D.L]BR`F)Q[U+J.(##*&>&1 MQX28L0X)!(095&,&5<_R!0YD`1'/_:]8ZV`.N3!"&U*`@@4:X=Y_G.4=1".& MHXS`7G1LX0`G@<>FZ+"+=5B`L8ZU!61M(5E;4#89&49`.LC@CU#T%`C16&8] M`G!3(T"C!_\0C2^K,`Q*06,4P1!#+ILAA@',H0$!@<8Q#L`,.`S"QP)8!Q46 M88%CB"$4Q1!#`=Z9"$OHU1!]+7-(<4K2W[R"QU%.Q.2PK-G#=G4=\$!QD%6\ MYQ93U@@Q_H<#>FIC,4Q(&NL`P2)RVEPO0$/.5X;&FQB]"$_H5*QN6D<\ZJK3 M?SA!&I22!E/J8(TH3]FAMSJ3KHP0&RLH8`70$((40`EGV0)9R.A0A#`H98`= M&$$!<W``,;Q@@*4B@K.LMHDZC$$``/S#`#WM@HERXP-GP`$$QD7<!':U"`]8 M.R*).(07"/%L:R-&2+GK"U\`PX\5`",3N5```&X!@%]D51$<J%0`FH./7FP' M$/M8P58!SDL"%`'?1@!X#N[@`D"L`P9R2,$]PC&`84C`&G181W\7H8]X`V#B MESA$-.XA#DE<HN(3B`;"%7Z'`C@<!G,(1Q/X<(![:*-7@]!&;;J'&U0>(E@Z MY#8U`L&_`.Q`&',HP"BJ(89U)&""SQ"#97`1Y'!RVPH&&(`!F#X(:7P+&8$( M!@`NZ_4%2*,."M@!,^9@@$-LO>E/-T#4IUYU':*R%$!?-0$R$6%TV**Y\(GG M]PC`ZQF`6B`S2(8#T)&*!23""OZ8D-7\D#,Y,.`.R,&:)8WG=*+PHY$+"/&( M&P"+]'A^)%`HZ'R-P4`=TF((-5J$$U)C!0(T(P1R&$`DJ'.6&R0`W;I:TZ;X M,%P*\*%!;CR+!Q+`M$5X(R+'.`0BYA#DD30["]+VGI%HX0/GW][RT)<^]?\A M*@*<Q3793_?@H=,/`*`C&AYH%SS0H0P/5.8RZ&@7/BBE`$\K8-0``1<^**4` M3RM@U!`#TN4#L,@+@W]&&"@Z@,A(/UH`>C0`<&?*(FS1QKGX=XJ!$!1"K(0< M*;*DD!9";HQD0E+($"$S@*6+!H"6`U@'T'T(D&X.`%#0X$#<8:R/&B?KS@`5 M2A#>.B^@I#%=!,_(.BJ`?&2#`Z`.BT$^OG&E4T*1"6IP%#E,9,3?(F-`H"PZ M%A==I`Y2H%AHU!8*K@``H*`SU6$=B+>*H,U5!(^6@%L`T.T"H,@#6K4/$T#. M!1B=*@!&HC(ERB?@QJ40J1Y9;&$:'%L`=@C[$_#"/U@&H/RS$71W-#B[I>YV M#2N`8&<0GJ33Y]98XL6-'T?60MEAZG)=OG@19@!`E47L<!4+`B6F;CL$^.9& M]P1#EV'?V14D`.@'`#H4$!)82,R+`80&F*##`72@$],Z`_P%0!?<`8`+#!%` M)LL_Z!Q`F3#^``/1,%9<D8@+GA#2#`2+@$B(=L/0\@`Z[/PC&`4`9$$+`I"A M`\X_@)#S!R(F5`,'CVC!!YY:!/`P@!T&\$"@`+`,((HU0JW(RS^*`*.#`5#2 M`0$H4$*$#BO_J(C.*?\\^:,)V,"1%91',K`F'`06H.`Z`L0DRE;&&'`-'!F\ M2>`'N@``#R[J_),7+N'`(9@-_RP`3!T,H%-%HX_*"1B==FJCI@]0'E"'G`#` M@B`N#H[2#1S'&+`-5Z$"(,IOH@0%2IH$E"E5GJZY-\PP4$P1A10&>-!A%2XX MXD$@`OQC`2B$.%K'`>:ATTPO663GQ0<':)'%%E@\$]\B1A`P"K.XK$`$(\H< M.D)@Z.Q`P"+8#9,+"P!``H`NZL2Q3@*YK%#OO1;(L4Y-0.PB``#-A)"..+T, M"QX33/P"V")V,/"6HW1X,,,_ZSBPB#>#&)/L#>A,44$60`R`SR`\W'?`(AS4 MUY4Y1PSBAP+W'62'`NC8<'(!`P"@<B#_``""'`7$E$XE!!U3P`/L%H!`%FSY MXVQ`?1#45D$()'+`(&T]$(@9_L#S#\46&X-Q"BS?1P+,,M/1P<<AC_R/'?Z@ M4\\$65"]M1?^&&,A0OZL<TU!@0_>ELTXTR$"XL<<`!C8_B2[\]UY"\.WWVX! M+C@`A(/KSPK'K&/`.@0<2!!VVC4H)#NA.`.<`[)C;+$'LF?TSR5>$+)..LH* M(8>R1`QO@1'#HX>.'+Q4R^$5W]J!P")$.$!4'5JTS<<*]Q`A``)=64R$`HM8 M,X@P!<A6QQ''$```0>.7?W[ZPM1Q`_5=C[_."XOP8<``/#A""GAP!?PE8GQ0 M4`01"(`)P5A#`_#:P3'H$('N'4P`=5!`()@@``@XZ!\<JQ,PK/4Z6D#0"@9( M&"V<L`@G3,\*!$B8'%('#-0)!CWOB8\)%P%#%3Y!AC2T(3JX0(`<@@<7+\C+ MW6J%)+X@0#`"J,![',8.FX&O#@&A!$'\@(!!',,M-K``'=91`>H)@"AW6,`. M_'&',GI/@G?P&!W.Z`\[G,.,$K0#.!8!M&/LP'MUD,`BN""`14BN`(,LY!$$ MD(@B",!T9FQD(0DP`&-(L`X>*T``WN7(`Q:R<`-(!!]$"`7!=",?\%I$!09P M#?5E8!'W0-`BYHC'8]3!'HOHI/>N@(YVY(-J8:0#":D(,:XMXG[&J!K7[K%( M(-SG7<98Q]R\,+T_D,\83L!8`Q;QC2/41P%8)-PZY+,UUFWG8%:(G@&L@(XC M^",+&),3"-=Q!9LQ(`!U\$#;_/!**Y#/"`C("SOM]8],;"$+*\#&!I21""<H M(!'4#`09!/`%?[3O?08<W]UTIH"[(4"!#!1,!=Y5CD'HXQ\9[`$(/X6+>8JP M"U[@4'P&P8YW(8F=U>@'/(%!!P3H`H0MY5@/%%0>8)02I`U$!S4&,`AB=,T* M_F`+/_;93[<8@1\"10<""GJ112@#"UI8P3`V$`U(0A5<_(`73)\7GV)RT0,' M6,`_Z*`!6%"F!+D`(3_DX8I[1",)=%C`(GPP##'(-1%^\$<=%G`@%5"ND(E= M1`_D2H=W**(`"N!I.G0`CCJD8Q'9`$0.>E`'<5`,`?,PQ2&648=LK.88:D41 MJ6Z0,$4(8T/Q^<L-%-'#$!B1';2H`9$2MHAA,$(8OZ5%6<+E@6*@,&$?F"+T MP./1NW6T8G=S`#J(T0!G!1$&B_`#`U;`Q=7L;!T<`!<$&*$-)4J/G1^X0%YX M&89M'B,:X`B`$2!@A!W8P0%^^(:"ACE=^8B7O`@PKP+0ZU%VML,">5$O>^>+ M#A38%[_ZY:]_`2Q@P)!PINQ``%_P(1A%-"`+(S[/5I&TQ`H!H*G3BQ<5:>H` M#S3""?9HJ#P8805Z&,,(]JC"C^_1#'`AH"'X.(81\M&%9?"@!'*H``],<(<( M_#@?-E"R/AYA@(;R0\G^:#+%'/`/&LS5`!0CW[L(N0@FO(L*:.8#^7J*K7]X M01Z<X(05/&``1W@!'F-^!`$>000T5XS0"""T`PC-`(G>$P"B?"B<$^'F1!`R M$03X`!UR(QB(8"&V,06QB(U`8G0@@P$H)O5Y_J,\"S45S3(NL`?05N8SGW81 M:RZDFQ<!Y_[-&0'8JD+"NI#G/?<9<&@3-*$-K0!$*YK1C@9,I!,QZ4I?.M.; M+B4ZZ(6%#X.'%D10"YJ]\"Z'\A`!,F2`S;I3AW<)PW1"/-@4U;D`>_"!`H/P M!Q;L,`!?7`'2-K#%'!1!"%S,`1.66$<#[@$R?XC@#NI(E<!!^)91/$,,086I M,`Z66P`083X)(Q4`9A@-<40#&N^)Q0#6H8!8/-(`L0@`II)1A0Z!!USDLX+% MO.`Q-T!@!O:P1S#P^8$%2",/";B'-NZA4&L`(AD$6$`[\N"`>W3C'MO8@#N$ MCL$!`"(>[^K'`O11!P/<8QZ'D(=AHI`+$!#$'_B-AC2B88UHM+(?<EC`'0BP M@WD@;07SV$`_@"".<V2!ZW08@"46<0VA$X`.!U`ZTP>A#0!HIPO*`-\@\@$" M.R0-&#.0QC*2X+^X2V,=]UC&YCL_@+8#@/0&B(8^HH$-U*N>\YZ/2>A'7WIU MT-[VWR)W1@)Q"D3,X1\/$8):R$?N%KXPA2&0PR,+(!AC^",3T@6Q"-#LAQJ_ MS""#V`<)[H`%0.Q#`'*PR'Y;*($%)*,/$0#$S>ZS`&,4H`5'N$<V)I\-`#3" M#@`!1S8FGPT`-,(.$-[&/8BT0MJ]>9$.(J)@9%R"=?$**F2X`%B=>HV<>+N7 M;8`S?DX4$6"A8`X!`CK6#1!@!($J<>]@`5!U8)$1!XO*=?GB11@#`%46L1OD M!X&".@9L$LBTKH#2&=*D0/EGQP!5!+`"0($5!@HZ+_^Z#`O*CJI5.P2N4E4@ M<VM7=$3"CA5JEFI:.PC6$E5PM`"N``#6"9@!S*W7#W*M6))4"!@=`??&16I, MA\`]<P.068H\()J795:ND!W$CL"BH@O^U2E`.)T@`(N.%2@`H(2_?XOPA;)` M)Y$1?HN\`+\C8!&!'<?DM#"^Z-H@8<>%W5FPB$AQX8F."%BG(`4/XP.,+8*7 MZ/OO4;P7&2L@('".'39XU]F.()$=X(_7]8H-M<2_?X&(`P``4GTU3Q99^.8/ M4;?500!AW"UBAS_EJ<<>;#KQ-$QH9#&"C2/`G(:`A`HLX@0#T:3#SSRES!$` M*38AL),7&XHFE(<@BFBB`HH8P8`BX/!SSS&`5>:0*DFD.,\J+JHRXUQE5:46 M6N@<@ULY2HG%H5"D)66'`E=9014#5#F`CB0(5$0'"%1!0)4$-C&P0DL#&`$! M%#8YL((Q=4K@E1QI6O0@,.MH89\^>?K&SS%&^`/8#G9(T`=.-_FFCWV+-OK? M:?JL<T*;L>W9IU>S1"#555>@HXJI>/HX)P)`>%7.`0/XX8]-_-3I#W)U//,7 MAD]NR0Z.(;:T@Q$E&=#/KRKQ0YB$(_I1XHEJ&C!`/$X,D,\30Q9)P#@1D)+$ M"O"L@(^2+18P`#P#X$,*/T^2->5=!J!CRI590FDB`;1\9X4!S80@QW8$>$7+ M/EU`!Y10P1&P0C(%T0)$P`FW)RP33)RFP)``6(1`*+#,44+'"^##<0!U#)`. M(P"$(LL<(?@89RJ`H'S`KUZAL0]A4^&%S@/R(*A@#__5,<$]/@("0&40#O"K M@NM0P#$!=#"`"Q0`>%7#/NG4$MAV/0P)S]*(X<4S.F*.IUY%?3AE3#J5`&`% M.@0$O<X'ZU@PI3_HW(`;/&L#T[9ZZ]P!26!1,.K/@%=<A8X;`""HMC$5\;$# MSW.WH@^"-"]M0"#/```"=QP+4-G<#?S#X-(7B+UT!#CC[/@4\0Q"C`$+V,-' M!QP/0(=?`$`@V+/7!/@//@L(LX@WT8R3R"&(S*%>3L&2E96)"-P33'MUL`59 M,`0`,)],B_P;"SPB.&%4,+3T$+`<%ZC=DG_X-```(,LW_WSTA!>,S@SZZ`*- MK$"64#Q###LP@!?HT(<)#,('!P0`'QBP"`\<L'F+N`4&Z*`27W3`#F*J"BY6 M8`<HX`(-<O`**>I!/1J1!1<OD`HNL)!"NS3""`;P"B@4H"6R0.L?#+B'=0XG MO@,(40#<6T`@F"``"```%_])AS,`L,3V!&`0?##`TF(P"!Z\;FZDH$<6J@7% M?Z3C%8%10!D7004#K$`8B2"`%7ID`$QXI1,8@AL`TB$)`#P0!W80'PYF*(<= M&*,.,5`$$0A@1W2<(A]CM$@!`D$&`?QC60`0`!2.F,G?X:(]PRL,YM`1#:>4 M(QV%^`<@_&"`HWC@B$`0WP5B1X,2*I*17F$#!'+2`\#489:`@<(-<WC+1E9A ME^7HY>LX*8#N5=&)95S'`>A(B\``0(`;(@O&H$5'1!CA/Z8A`-MLT*/_$*%$ M7%``(HZ@2C_\0P%T,,`J_]&`.E3@B`A8F@,R=R`U^>4_42MG-=?1@'4L((3_ M0$<N_(&@=0!K@#X4(0VA,#=*7`F;PM+1EXQPCR((()]T2%_@Z)"!*P2'3#Y2 MQ`$:>88K$>T?E5F'.-8!CG7LP`DCI<$_G%(`ME7@I/](Z0'^4802=4&=[!RH M`TSJ!922Z09]NU4R_Q/38ZRC&.J9D3"T2)8;%@QG9VO`.[1DHZ&4)DY`Z$%[ MY-"`X""@>?>`WAPZR1I@I",2`!C36ARW"#_>3E&X2NM:V^J%MXXCKM&[7SQQ MM0XR',Y$P/F2!VIR*V_P@Q4\T``=6L"#!OSN)?18!P?$`0\#3`%G-_Q'SQ"` M#B_:S@!;.``_RJF*'>QC/NNHQ#I`<-KV;#(;&RA,%!QG!GS8;D2%;5X@'I%8 M`6PA5,.@GH;(0HL?-`Q[Y0.8P`CFE1#<0RS_,`$Q0!C1$C$@%J.K#P,&80Q+ MMB1&QS*`'^`AP*TNC!V!8`<H[N>#;,!A:2K#P15]\(W_IBP=+@C`*@D0`#JH MK`0!\(>/)*P`"2-`PO+T@P,,X.!T(`(`/5)`-:'0(P:,F)I9ZQ$"3HRL:L:J MQ"-&AS/R83L&-,(08%`MBDGL(Q8;@,4FSAHZ^D`/VRG@QCGF<9!YO&(A#^$? M2-8Q#EG<9![_.,4]%K(EBDP,!:3#$"!N,0!>G&4K5Q,)3&:QB+&\9'04@<;$ ML#&.I;QF):L9R#&FQSR,'&4[8[G*Z-"%/_H<8BK?F<TQ;@"7%;".#"C"!]2` M@R+,X(`>$4`1;N#1B111%458@;4]<`?U?"(W;6:L*M$0M3$480%LP,$6L-$& M5!32J![YHYJL=C6L[R&.15A#!=!`A)@F[8]RME!A!&087ICC!0/LX`!T2,`B M*'`+`-!"`.@(1SN"8X!$7,`?PE"$,'1@`%?+@0#^,(8L`)`("M0W?,(BC0+0 MX0#JX",VEK()!!+8#3BLAEF#`8:BU$0F.[C)#F8"BV_L$2=^^]LO@`FE@@BN MUR^A8P?_\,V]6_(/]:EI"JNC@P5NB`]A&D$?7M'"/ZI-"]8V8=OPX`XQO*`` M$RSB!H-`E@"(PH^E;>"&]C`YRM&A4_4H@ANY``S+$X`.$+1#YEYP@`D2@?.; M;&<4S6$YMA.P[0SU1`'W!41_#;R!!Q9X:12H.)CP4A<JX>(A$E#[E,Y"`'0, M("S"`#M90.'J:BX"&:"X!AS\#@V;*&`'P;B#"FSB[,2/P/#Q_<,&X"L="BAE M!])IJ[&,0(`_0/QKA$E8/I/-#@D9@!$$8`03&4&%U'/!`-A<A`.*(88HH(,> M]<A";M1$`@],UA$>.(889F"/%9'B`[FQB@V2(09T6*,>MBL.-@XPBF&(H2(' M7L(J").+_R3B!X,8A@#DX0H3X*``=:!'(@Y0(Q]J&%K5<8`AZ[!>/S!``'_X MW'_^(,(HGL*//@!(XO,`A#1_"'`,!@!($4<!#V0#@50'#8`+2%!(AV0`".B` M@-%%^@16A4%1Z``,[Y`31Q0^=#!4=,!:Q^`.64!)`C`/_W`,1*!%L+$>M)$( M1%`?YT04]T<'/Y`:=H`<!B`$<E`#8K`7XH,!J4(!N><;"J`F!U!%X=,4T90! MAF>#"F"#]9%%.H@RD\1$^11Z&C(@`S(:[.`"5N`!5V``%E`3+<$(VE`ML4`` MTV!W<&0$\F!Z]D$/C.`%]O`7*=`(?B`-B>`'^,`()V<$`>.'@"@.@\@/AN@/ M`6,`CF``RR`&$D(`OZ`TN?`'`'!9A.`8!?`2^"`3-%$%'M`(7B`/P%>)NJ`T MN```2V`#_V`#+@``LF@#,```+L`(%E``6.`"B6`"`X`%`>,"CM"+O[@(PH@% M-O`(!I"+S6@`M?@!73`'.80.!B`/Z\`O31`P!_`%'^`%U`,E;8=<:0%5O$`/ M.<%X#1,`-?$!QG```!!Z]N5#7D``\?A$M8`%)E<P9G%#".`5<R``8F&/^"B/ MN+"/_=@S9\%:5T"0V407/O,-D3,YL3)WZ$`%ZH@E.!0>&!51#8D.KY@%L3(W ML1`YX;%:Z-`"+(0EQO"1RF8`(J$#+4$'..$-1@`(/R`^@[*-Z[`.635`944L MP"$MBW`/,@`,W?</Y'</T;`(MX(7\V`*BO0/*E`$_X`)P3*4'P(,15DB2*F4 M_]&43UD4\P`C+;&59#$#_[`.&D`BZT!!A:52.D``N"4!.>E%=?`.084`1^!6 M.BD^WA`;:GDC75F4")"42TE^BQ`,]P`-2%"8PW*8&G.4<?)WXX`!BX`-24E^ M*Y`-B[`,4`DM4UF55YF5DMDEN*`#)80.Q-`."=(HV)<`"'A`[9$.A)!7/K,( M_Y`%L\%N)]<#2O,'#>`59!`TDX-]!V@`!Y1/Z]`%1*$/Z)<`QA`^QD`$`5!0 M+=`C^@`(P.`/B$``1H`.S]`.X6D$N"`$)30W6\!0N@</88(.4.">XU&=#6`$ M)*0/LVA]5#%HO^$5K)`.N1$;V`<!M2D&HX<.>K`.61!-'?`/%B"$5Q!"?*,L MKT45_(`.7\"@<Z,%#/HL7A>1I5<5ZN%WV&`/AY`.<$`UU&$,U50`(!`8"+`. M2>&BUH1-/F0%!)`+!/``N]`$]Z$(QB`-Z'`-JB"D(E`,1BIN1+JDPI"D1EHQ M<E-6^=5'C,(*`V(,1O`*4&``_`"(L)`(ON`&N#&FN/$EK*``CD$!/%`"=#`! MB%``1^`/3O`*?_`.Z#`@Z8`#!+`.YY`.`$``B6`+>O`/F.6FY""F9/HPB>`( M>/`/B.`(=@"ICG`'+V@(<O`?ETH'FKH.`<`*F+`(T#`)@+!;@+`/8#`'1:`( MG]`%=K`(P]`#%@``=J`RIS``F]D\+O`/IT`/6]`#7%`"7+`'7!`#7/`)7(`* M7.`-7'`.7#`&7%`!70`/7$`/7(`/7&`&79`!7H`+78`-76`&7R`)XUJN3\`% M6<`%Z=`&`P`(_0``=X`/@""I_Q``Z_`)[@JO[#"OD[H=I/JN=P`.@.`+EKH" M=``]EF"IZQ`(@&`*D_H-M5``Z6"I_/H/(!"QZZI*``L$]F`(_5H'(%"Q"%`+ M]GH!Z7`!N%H4BU``.8<``N"N`C`'"9`(F/H/M:`R/J"Q=AD.-,NI_E`,ZP`/ M[DH`<T`-M="GYR`)+``#F`"S=U`!M2``AU"SE4,-N@!VO!8`-;L.R3"T==`* M4\NIE<,+5PL`66L(G+H.L5"Q$!"QZX`*#3NI[^"VGW`+,ZH`M\!H"W`+$#`+ MZ2``VF`,AF"I`""X<?`/`(`(E@`'N*&R!5`0ZQ`!>6NX]:JXC*LC!9`)1W`$ M]J$`'3NI$%`9BU`!FSN(""`5R?,[%4L`C>`+??`/5Z`(QV!'BF`(APL%4X`. ME.`.:[@(EB"VTO`'*I.GZ4`/B;NX&?<EK<L'N%%8_W`#0S`$B5!8GVNQ=/`` MSQN]TXL`:X@.LM`/L*`#JM"]JM`/ZU&XMHNX<?L/^]`(CL"\*Y`/A_`,OLNI M.Q`-?U`<*3()[/8EEMNX(\*R+2$`:]BZKUM-B5``2;='Z]:Y_RLBU0L"]+<( M/="R`C!9J2%S%=P2J0<,"W`,JS%9OV`$=I"G>^0*`;`#CL"I:+$(*]R6<G`X M#HR\.O+""Y`,?3``N:!%5Q`%C>`)KSL%4O`K(N"QJH4.30!]+<&]!V#`_U"] M""!ROINI"R`-&F;"Z:`%`;`"T>`'@FH)CUJ[EJJF=$`!)@`(/=`"-OD$@^`* MCWI%8(RX)A"G6`P#`6"99KH(GI"IL%``13RI/(!^-Z`(IL"X@,`/;U`'#S`( MOO"H`/`')W`,AO#&@?$!AXP'JLL/6&!/%0L$@R`8;?RH`M`'%CC)B`L``^"H M3VS$S?0`ZY``L``"6"P@2[`#V?`'\U"]0$`'$@`+,(#%4U2[C(O%Q``;<5S% M?M`"*Z`,*Z`-)B`2/*#&",`#(%`9/+`"W6,"K(#%H0``2;`$AZ`,X\`6OZRG M_"L.*,K,V3`.$J#-<3JG=9I_6)P(`"`.[K``VV!_H1G+)KP.;'`XZ)P.(L`/ MG0O!1BS!);+!+CM9-$H,"DT`5P,`*K"E=<(*4R>Q\K`/BT"H94JFL%`"_;P" MAY.F%E$/J\2EI-S/*'`X;,@*ZY`$AW,%2RD39)D$7X`%ZI)7X/#.=/H*K(33 M`Z#34Q`%4F``-P0+YG+44-`X=D`F5N``6LJE`>`'K`"F!Z`%6;`%0*W3TM43 M^>1#G(<$N?9J9BL.`V``VP`'V:4$`R`,T0`.BV`!_98P\%96&`,*VC!XP.D/ M'H`]XZ`%Q*!AQ?`E_+`#]S`')1(,2/#6Z](N_;`Z#L(B-J4+VC``C``,C+`- M.S`,=!`!^L`.T(`,<K`/H(T,<[`/I;`/XA`/=_/9H3W:I7W:K:#1V%#:L!W: MIPVKJZ#:X8!\7I=WUC8`U>9(O28,PX`.+HAK%@`-9/U&0'#<Y/`/N9!/<ZC< MT=#<PO#<PI)?B@`;/B)8`#`'=?6W=YQ<AR57BA5W3;U7/O,.?F4`Z3`.L&'> M@7`*T7-)56C*IV%CMN!.J7%@>0``3Z#'C_H/IJQ2B#"XJH0/1X&/!'"?8@RI MXGEC[N1K*P`-@="=$I0$%(X;UG#AB6`)A?H/'-WA*AD([TVSS+L#AE"H=-`! MC,R\X7TS`"4&Z_``<;("QS``A@"_+=Z6/MS?9@0(4&8(%?[AT!#BA5K%=N`W M)6[DJN4X,9#B/?X/+.[B9><+,CX'--Z6$D!0<<+C/EZH*G&9(6J0<(7>40P! MGY0U/-,S=2<+`EH.$G-=ZI-=[_,`>'*/RL5<<P4!6+"-7J$'Z$!6O]`>OC"! MZP`!1Q34O\.>XY`%;&P,$/#?[!"&D.``5%`'<B`&;]`"0P`"+0`"0_`&<)`' M<I`&9X`&:X("8Y`"(!`#.8`#-P`"0O`&<L`&8>`&9``"2>`&=,`&M4@$:6`' M:4`&90`"92`'<H#K#:``02`&;H#K;1`&;``"<-#L9R`'8=`&U;SL;9`&;A`& M=)`&;^`&SPX`D(`!HD`([=[NZIXZIPP%:0`'R;[LS2X'.@`"NCX':U(";$`& M+``"=[#K_Q[P8"<'91`&O8[ON`X`0[#KT[XFULX&;S`&Y)[L)=#K8E`'9F`& MRSX'+@`"(%`%<R#N9P`"&P\"'?_Q(5^+$>\&$Z_L=E`&;@`"%7_Q&:_R'._Q M("\',$/R45`':4`'Y>X&9^`"8`<`(4`&YUX&`"`'`%`$>-#MXEX&^P[Q$O\& M:U(&9%#T(.#ONTX&82`'O2[N_D8'M3$'*C]7104`U2X':Y#U9\#U;P`"<3_W M,0H`9C#P4P`%03`$10`"D'``1H#K!6_V(/#T83\&"F_S2@\`8D#X#B`$83`& M:Y#XO<[X<^#X90#Y8%<&`Z\&`S\$4D#R)*\")'_X<J#Y(.`$_'[U<S#PR&X& M85`';+`F,1#Y>3#P:T#X"*#ZMG[YF5_VO0[[;"#[M%\&MH_[NA_YO0X)"R#\ MK._ZR*_\(%#[MY_[L0X#BR\'_!X&_O[ZU7SWFP_^(#L&;]`&U<[KD0^RTB_\ MEH_YUA_[;E`&LY_]S+_]NN_]N![^XP_[=&#^WR_!(*#^[#_VD0_^F2X%90`' M82#N:]+YC^\&D9_R\4_R1W#W='#WR0\0;LJ`<,("!)DR9L+484,'1`P7"@`< M`0%I@0H0()BD62/P3$$Q=1H>3+BP89HY(-B$F=.039J`(%Z",).&31F(`'I0 M=`!%SLN&8^K(D5/&34.:-D&X"=.F3$0F.WO^3/E2H)LZ;<24D0/BC1F'+*G" M;-$"Q)`@3D`(*0)B"I,G5T)$?`$G#!TZ6]U@Q'BQ;9DP<L:@F?E&SAW`9`AS M==*"SN`W8X(2=3-&H%<0=>_FQ?DC,UXY>O?VG?(W\&`Q8<:L.2PGL9G"`QL_ MCBRT:.6N7SUOCJBW8E\I9>#\;0B'J)TT;^J@G%-:,`@4KQ?+[DI[<N44../L M+((G#1V<30H+1%.P7Z`%(V0F\[M0Y8X@6+4CFY!C$+\^``&G&S-8RB;T$ M1T@XP5%0"10AT!<4;\SA'7)ZT?'&0)AM59E1,1DUH6,"(7531&WP8)-F<OA` MD0%-`+8&"!R"(-EDQ#7XX!MZW>'=8"*60:(/.)V0XXXG`D>'4!%.&`9FQB&G M'!MY@-"&B@!B)B,=$.+4`A84-4#%&V><D=219JBD'PI8/MFD5EW!=)D()<PA M`G81L;6%AUU0=`!W3%4%PI$!W3%331\"D!8D#N#9AIXM!H3'48`^9X8<;[3! M(GDN1OJD&XFY%!"<`$"Q'1YYPM1B<64<EQQ*'CH*J:0MCF%I&)B*50:G5D3E M$X9V;.4@C4IAI94<.(7@:AN7)@;)`5"LQ!Q*K;X:JX1[@C`'>6RPP>*$:):! M1QE!X44&3G9L5U]#K0H%XY])V>@8""0404025#RQ'@HC*<00"&URVM238\P! M``!#.$L&2D_*P5%BZ@YVD9G["41J90!&*4:3?/JZ54%.0!31OQ#\^Z\3$[Y8 M%*,V`=`"P+"Z\49#;PBG5PDNJ.#O$+">0&X8'&4XX%&K1OO9H4NQ(>B$*##F M6`I*:4MR&4,_9S0:2)-JJG+H,LU$$5-,`8"[\,H+P+ZI^7ORR0",$<"_[.PA MB1JMJ/$O&4'\Z\T>`$!B0!EG`X#.'L*HP8W;`*!!P#__<+,'/(#_"T?>-O`Q MP1H^K/$O'@0@`(`V>Y0AN<=SY$W.'G^L(<KF=`1PM@Q\_++&.IO?83H`ZNQ! M`1M`"/VU`/^*LT>8>K`!"0!LQ($[`.#LX0H;UK`A0!MU#._.'A:T(416;9SU M+SQT0R(!$33:O.<<<V`E$!EV'7F2BS;!"D`0X(MO4/DQH31&^FX`P+T;WL=1 MAW=[-E04&2WP2@L\!(`H[*\A=@'!_P)HA@$""@!"2,UJ$+.<^+RA6E0YE$E0 M4H),5<5?[0G#5"8F+0M6*S\SZ9D85B8A25T&/O*I7PA'V"3W&`6%CXK4M>"` MFQ+&$`!4"(,8I"4A.*"D5%MI4@>E59?*^.M^WIN#_LI0!CT(A'D,20,<DB(& ME;AA19HJ@[^F,,4J7K$D6N2B%\'X00`8H3"L20QS`.,<E@"&#JA2(0MU^,+X M4+%^;S0,8J35G,'840YX3*$.B]A#&/[1?B?98AB:I#+'X!`V841)&,SP&05B MBH$.+!D1(JF2)OE!,;+2)">WXDD`"I"`/8D4'!J2L&BYB@TTZM2J9@F"6AZI M*>`+PQF8%LLV\-*7(,B5'%;('">)<0["9)H44-@BK6`P.@H<2F%0@A8B]$^! M7GG!"LV@'FI2RIK6PN96("4'E("D(97\9AG".4Y!(9$KM])/-<LCM"I@:BMA M3`Q>%F404DXR2C*IPS_E$$8GO>$@Z\,@:E0SAR:*T44*:>8;E,D2GW`$@A*L M:&HN6IRIK`0$7D`"`);('_^T$P!>*,$85CI3$;2@32+0@5(F%#[GA$F8+L!( M%32JIJ^PR4T`L*E,<YHA.X3!):X1$V\LQI7S$45_:2!*8C;9R9N.(2(ZX`-& M4$`J\(%`"D6@0A6DX(04`,`/'@,`>EPPAS5H$0[YF6M$:.:&>#)'<]=J90\) MR-=X#I-<V"K#&5[B!A1>AH!&:-3YXNF8!.+2#6>P7_<:L@:5^<DFF%W78Q]8 MV)41$K#0JFR,'$2E7`X!#6]HD$".-*).:H4.=_@C"$X0AA/L*58GT,,)OJ:B MG3:$.71HFM24A!*BG&$A@%$@'LJZJ_HEP0U.A2IF[/*9^H',DI@-[!Q-TRGN MYL6XA%$H&5;J+T<U:HF7Z:!;,Z*G#K(``/O)`U[PQ8;UOJ`$_<4O"@!,AA*4 M8+Y%<`(1\%L6)RA-IVWZ6!68`!7`/+<I1@&`<[7%0PEA*PV930[+OB+2R@#` MPTZ"59-0X%:%/HD.@HD2BP'@8KO$.#$S5H-"5\2K_P%`!2OHBG#D`*O$N&HA MB<F6,>F0!P`$A"514L$/5@"`EV0W#8G90IV(C%FFU1C&Y,ER%ZILE+P\%01" M@0,`IGR1UU3K#7=`2271D)\3$[DF*.0"3$G@@BVPF`]LUO-/S&RM^D`3RG(0 MU(2A4A<B-^4S92N,'.HPRR@5ATN._EH9VE"8)KEJ*)1N;?T^/>E*)P8.;QCT M2U4=M/2R@=)U,T`(`/"49_X+""@8`=)L8M9DZ@I"(*`!#E;P@B9,H05$>(+6 MAE`W"9QM"$]H`A2"$(5_420!(<AVMBGB;"!`X@$"@(0"O'T`"MB-`)"@@+<- M@`&[(2#=0(@W$$!0MT"4U"A?T6F8WF`7%*)Z*M%YL9Q-JZF#-4`!\(%5OF>" MRWZ']]\8"KA=!MZ2EQ@\(G4H0L8S_J\$X$[<2$@TK05P-D@D``LB%X#*ZY8` M-(A\"`(8`,OQ('(A"(``+$>$R/_U#Y9C8N<`Z+G)40%TH2<`%T5G.3*2;G)L M,#T!Z'@Z/IZ.`']YS.@8L#K/68X"K0>=Y3CPNM&1(':68Z'L)D<#VA.`A\Z! M0`!O!P$!0"$`4/P#$A[@><_C.@&5$Z``!D"`X"E0@0),X!\6*$`$!'#W!A2@ M`'>'P`,(Q_C)$^X?A_^'R@L`[LL?_MN']Y@$Z#!Z,#B&#H"@@R;HD`HZ]((. MSZ##-^A`#SH0H`XAJ$,-ZD"$.F"A#O\J0TCJH(DZJ*(.N*A#,NJ@C3JP`_CI M]D<=&&"'$6Q-FUR!`0"N>V77*'0,HNY55K;2M)X.)E6PJ0]1P-_I\JXKGJ]1 M+Q!CFV(W-*EE1:F:OZ"PE4.!#]@'T5@``E*NT2A7,7Z)U@2;UFE[,AE'0@=$ M-BT#^%V>E!QG,!B?QC3<]U18YDR<)@=G@DNJL2=D0`9$`3[;AUT<F!A%<1R0 MX@88EEP;J%T2)X,JJ%VIX43R\U`:>(,=2#YT$`;E5P?.<1#'86)!H!E+%B70 M0A2<EBLN8BXC4U#K)R$@.#3'Q13C4RII8&(@XU!$H7\IV'T->&$CLSYRH!\N M$18H)@8@MC7;,@:*\6+9Q$X``RG@TP)&V(4"47``D"+K8F'BDV'`$3[W@F(J MD89,XT^=!6=ZL4Z%P5X0-@<;TP!Q!3#1-@504`3,)H?_@@)742UN!0,Q(`,S M0`,U8`,W@`,Y$`1",`1$4`1&```3,`$!,`$44(NUB#L3,``$,`$(4``F-P$5 MX0`6X``'H(NU6`'$F``,0(P*\``)X`#4>``)4(L&,`$1H``-4(L0,`$+4(O/ M>"P`L`4"8(YB,`1M0`1VP([N"!H(=&-L97-S+F1O8P```$@7``"6$+5M$9&1 M.P``#`T4!`P(`E(!)@@0*BR"@*'#*0@@2I08,2(*!!<SID"PL6/!"TT0A!Q) M46(+!"=3$D&PLN43!"]CEHQ8$(/"A`D=-FPX<TK/C!@Q=N3(<:#`HP4+.$&P MM&D0!$^CCA0I4J=#@I`4L"DS9PZ(%B#>P('S9DX:.F7"F@'1YHV<,D:3]LR" M@*[=IDR9QH0)$PH"OX![)D$PN'!/K%H1L%&LN`P"QY#G()!,F?+DR2"V(-#, M.25*E&,0A!X]!$'ITY`?/X:#@+5K.@A@RVZ#@+;MJ2/C(-#-.PH"W\#K(!!. MO`H"X\B[(%#.///FYUL\IT2#@+KU+TR9+U_NG/-FZ2C%(!!/WCMG,PC0JW?= MNK5VYMB=O%^.]8'YS2L0Y-^/?0SV-MB1,5\7('0''79F8)<&=FQ@5P9V;F`7 MQG\.#N@"`A=FF"&&&&Z885P&M<022U;Q5)%$IYEFFA0(L.AB8801!MA??U&! M@(TXPEC87C'AU11BV#'A('9S$`E"&EZ%`0(<<KQQAAQAM`&"66VDP488<H!` MQQL@4(C`&]C)X2`(*,20`@L@B%$''2#<@48:8Z`!0AALL/'&'7,@)D888ZQQ M!Y9D>-66'66T488;;*;AAI9HI&6&E6F%X=4=9=0YIU=FN/6G'&2P]0:AAB+J M`F)!L#''&V@R..1D1I+Q!E<@N/$&FVB$0:B67+X51J=T-`K"H72D\=:1;L"Q M)@B/;J5G&9D..P<=6`;KQAEHGMKF67)>*<<9:2EJ+)O)PGK6E-#*08=7=<"! MF!F2HI5EKV$LBA8>;)9!QEEN><5&&FNDA9T="I)IYJA(937$&VVT$6^@<PZ[ MYQSVAK6H&+/*Z268"(CYV)QN=/JO@BZ`<'#""PL:1AYIPJ5`00HP6<889=@; ML1@H*TG&RVDHS$:L=;0A1AERH#D&G5MUZ@2QC*9U\QQCR)$&',&^X4:>*V?U MLYUWA`P"%;ZZT?//62()0AT0=TKSE`BG-0;""G<\!YJ2$GOOT&B103"(!:1X M&H\PX2:2WTU$!154/C(E8DN'5:U`$O+ZFFF==RIZ!@A+-_UTU%.CZ046;)41 MKU=K(]HD&RU@<;?B<D)"`!*5PJ$#Y4C"<67-4_:LL!PHO[%6KURIS7;)6B>Q M5AYOU%%?L]RR27328?&>Y=HDNXWF6X;Z##2C8DM=QNDL3P%%$$,44=\43;]A M:;.;&@W"OFYPA>;-[-;!!IO:7]OQG61"G)98F-?7@AXIPUH*@K>6HXFM+<." M5^/2PK2W'&I*:=!#&=`D-3:@C'?'4Q2=IM2TF+G!#/([DE?N-0?9G<Q>W,O* M6MP"`B\887Q1BI17O`>^(J10`6((2Y:\((3QE<]2>^I3^D!P-/:YCW+,"H/\ MZ-<^^[GJ#OF+65B@E@:I^0^`5[O3``LD/"**T%,)K-4".>C`19E%@A1T@P63 MED$W;+"!'@3ASL1&0A/F`84@4H`4BD"%*DC!"3X<W<[0!R@O&O%M2(S?_$`0 M`ZUQ+2W`$E9:BJ@HKM0'2TJ+W>SLA:8R$&I1:2C@%Q&8%@4R#XX//./V\KB_ M'8JO:@^80@PO!8(]]O&/-U2##ENHA/J4REJ\F](LXV9+/SKAAKG;818":;Z= M!=%/A:1D^Q`)/R4NLI$%>N2O$"5)0U:2:@5Y`"9A5\)-DJ&3GSR2*`_HEE** M\90=3&4$5WD4HV"E`%`(`[<861`5H(!)BJH;"&CP`AG(X`4XP$$*[%G/AB;E M)@O9R4-.%)&?!$4C1/$()$!"%9)0=`K@:<'A6,*W)_2D)A#-B41-5!*+`@4% M0^D(0Q&SAEUZ80DP-!0M\W##3K'0"T1@YODT%<WU??-]25PB(V%`0"^*K80X M>Q0GZS.NGT$/5L%L'Q2KJ=1FU4X,$(M#'8!%.8YURGBPA%[;`G7#.MBT"D)U M)I^@R2EO3A.IBF13#)C*Q76"$Q(/@.H80ID&3AZ)359-FU>R6H:M)G617IU# M'<!:!K&2E0R7[-C80*!6X.4Q2S^5@DV94!\IE`$.80@H/,MX0]&JSK2H56TP M4>F&]R%I:)R2',=0IB8SF`%H$?/6F@@6SBI`3([J9%ZXOABG>)U!MVZ"U&&_ M^#/)U<<.A:64W?(X.=4=@4M;,FH3#3C&<.'5FGHET\^>ZP8WZ%9W&=S*%LET M!2RY=UJOZ]4!3Y:R*6$MN0;4%Y:XY8*%%DP!1R@(`;Z+*_%.$FG!-&\BT<N\ M0_ET=XZ#%'$!BX+ZRN&^9\COFTS&6P;^-Y3D6E@AA4N'Z87!>?4QI9(D++;J M3@L$NK*;@1$#!P4S.+Q*(HM9,.=%.``-9H@B5G@CK.$"'6T.:"@>&\RFLG#^ MC`Z4>B`,S+I4OI*):V(3FY#-(H:M)/=9*JXKBZGJ%1M/+L=H4A,3UXAB53V& M2$:J59+8D&.4Y;C!&(2EA=6BW":#(`F)VO.?\H"I=L7Y6+)2'AL6_=<'D(U9 M\BMP'DN@8%GJ-&YPN*&45&>$9MYI9C5S`\JP=F1)I64K='`7FFZWAL7Z:@QU MD,-;$%4?(9^EBHNZ`[88]>+UE2'60+OA"12,@BE(SLQBG=7V4E"@4D-.NVE* M]:I/+8>A0<S8R`Y:?=Y"AUQ/#="^\C61HQNG-H'Z+=@MWAS62&M[U4?8O2(V MFV#M+JU9F]5F2W7%KF>6:4&[#M)FP;B/;>[%@C?=92%RL=DMIV!>Z5D@$(&V MN"6"^@RJ4&3MK&;_Y)4RX.%E:T)A@4JU,UHG:5AVPKB;#G7)6#466=(5F\FC M5,GM'O@%J`VW&\;GN6[+B9!J+J]TO1I,)[0@WT;DK-2@I2CKPC*807>7&[1V M-*[.S^&,=&2Z7ZSU+X9AX6>0'Y9^A0>7=078<//*6]9V!O=*,.!=NC,"SFE6 M-"D*>Y7.F>Q`CJ@7PYTM_%74&-A0AYLQ;U?WPAR=/'YL/M4*2F-P%[K<:]D& MK^H,85I5J+?F*XAAJ=UH-E>2Z!#CTK],:IV*>AW-J=YCZ8]Y3Y\BYM!$\9E6 M#9_Z3(L,^OE/IR$J8@0U*$(5ZGN&/A0G$;5*3UQZT9ARY".`ZTE(1TJ$DIX4 M$C:!ODJE_U'J8S2FO@]G<Z>%U1$/,(\_R#K0A@[+*12]W<\<XM^9;&:F^\KI M4%=)]1$Z5`=BS"-_'R9V#'1_<I)ZYS(G;&)Q`@A+.6,H]_)BE5)BS>).I206 M#D9.=H1'![8HKW5:GL,F+A-O9#,E#(@F7@6`V5))4E=X57=C@19.%X>"9#=_ M-U0$"E8$>,!S3:0D6H5S6]%4_'<HLW1`2%)PU,)&L"0"N*9KP"("1I@6*'![ M3<=E4"!UT<,P670'U&8&3=(&K?=J2$(_:Q$N7K%_M_8[FA5U8O,6+;!S5=(^ M/H<81Z,Z0"B$'&AS]'*%9$*&",,\^X)QNG.%7O%<Z:0H9^B%:^5@\]5%1/@U MUQ-@4V)D@R55G8("D88K]?$S,PA"97,M^19,(F<T9\(\,`B(X))SE6:'/7=# M7<B'07B'?YB"5;2"X=)4E6@]68*)@D584W5U__=T<J*+\B:(.G>+LYA'/6!3 M1U`?1*!))^,5IZ)3BM(L"B-Q%',L29@RNH5=C56,X:1XC'<O-W867N%&GZ99 MP40S:*$69@`Q:L@\%!-K"%,?4>=F(&A.335F:5!F$S1==&(M`(4HME9E@+45 MTY)OB1B."W.`1T96$9EA9A8&%$,HCYB#_WA"L5=)-\0$"E:-Y72-S.,UP$AH M$CB$8U%TNE5LP;0E/09?QLA`\51;2`-YOR8UZ-8M'^06W0AL'WE'F`5+^%9Q MOA*-/Y5@J3B(TN6.!2F/^^-;]JAPL.209Y!O$QD&8]$D>)`S&+@D%5EXW+)% M\8$=&$,'#B)B8I.*`Q@O_35O=X(FJ&4N<*)V<K!&/FDG-\:&-P06I&9JV'8V M]7.1OO.%'\@_1,EO0.,561@SH5@I6H0F^C4I5K(S6W(&9V!FLW5LTC(Y%UEL MC.F3"]-KQJ<\G=,5P8=$#320NL584&2/H7E#*S`&;=`I0Z!$$+.0F1A5A=4I MN,DK7"**)H=R`N4Y[18LGV9S=Q!?W5)RSHB'_L9"=CAX:+("3LF;L&)G#\(J MF+$E5.5>P4(T*#-[_*6<1R==#AB3$>@K@P8E,&:,<AF/9<!>('9#5J!@4)": MODDH<F`6/JF28$-HWHEGF.&/<E`'6Y='<?"#8?F`!PJ><W!#VO0X6!.;=\`E MJ?AR:6$'=)(&G>*&:=%]4P`"`"J@BV*3+#.A13(9-V0'"L8X=O`&_9(DBV(O M^/(N7**C[ZDVN;9KKVAF;I9^!I%/^S0#Q)>0`I5\!Y50.]90SF<0*<4$)5($ MT_=1+R44&75]&Y5]'[5](^(2?"$3'X52XF>E*X6EY:>E%\6EZ">EL$2.VJ6` MOQ)Y+"0VT-(OBT*(4A),AX)=3>(&H<)Z<XHE::"19E8$1)`$5/`$4D!!6>)U M#QA>(E`H?#('(G!#(3`EC5(G+0"7L$2C-@HK2@)EE:*9N<*@I_2II/.4C'@H M*50`)34C@($C-W(C.D(8)54X3I`X+#,RD1AUI7ENXU0Y3O,S5(8U6M,$$:=[ MP$9BR\)98L0MG1)=9N:B1C*'#`IB:()=2A),(M`"5IB*-_0$5"0U'CHGI@)> M8<"GR%*&%>8&@BHUA8JBB*JH:2$"5NH3FXH8I98EU[D5?C<\Q6-4_0)&9=`" MSY('9L8D"$-%TV*97&(K;R"BB$$'>0`'NJ5Q7.$5+2`E+@`)`V"%ZJDE.>,O MBK$J+]J&;4BOI<IWQ.-657(&:&"H+(,6EF*B.@`B(V"/(-"O4]`#9GA@VA2H MPF*O9`6B3J.O7W27I?@S&\@\3VE$6,DRUE*U,EBL7O$I0.,TCA=,0KM-]4JH MP((83)NH!!F8;$*R3B"4YNF"1%57IF=TL.*`(*`&9*,\9N`NS$.3]8&>1OF! M*``OB08W/\J"H=,I@ALQ1O1^L,0UO#BWG5*W<2(N;K!XC?>'4RBD:]2X1QE. MM&6GN5>LG,6=F$*Y+'AZC0)V#E@?>HMQ8="WUQ./L[(E4B)[UFB4EHD&*S@' M:_`T'&N#C3*`00HLG[N[$3.Z>12R!3$`;RL'.F-!J;*R>M>R2U*&4(.97"EU M?DFZ4E*L<7EI7J&MX<DE$-L&4(.B0$,Q$+-&*+`O"7LQH?<8JYB44!A.1M9M M9!50#^<H&M:\3?"\VM0"`VRZ0T.^>=<@UVLDX96^Z^M)#T1*["L'[IN!K2>7 M])LQ#A*8#^JV<%LG>5"P'`.!:*&^B89X8<LE#=1,2](N\QJZ@(68@LA"4MM. M^'B?BF*`-"QA-PA8[G*'&W0U<R1W##HYX:4H<S.6EXDL?$('I!L']6&Z.FLJ M"\RR1O*)X65\2.PK1`R!M;.<*5NA]=%%,PDT0KPS>C8G&8==DC7$J2H"?I?" MER;##Z`HS^(Y>:@X+1`%SUL$C;@6+?"@8A.K.FF^F&DI7)S#EN*3,&,NJ45_ MX31P`JMK^5(M==!N<:.QPSLY<JDH(+HOPGEY3YQLV52\L-3'T.J355R^UON= MV!M>HBA6A25IFNDKS%D&.Z!O(G2U@-4^`(ICL8G+:*Q!SI2J2$C,TFO,(+#& M9Q>%;EP'<%PG<CQ=8E/'Q)+'NQ*8:0'"TFN>S%-!*+-H#69RX[(57?%%X,H\ M(A`''9=6<&@WIYP6=;C*\I*J^O*Q#:9$N&MX0R/"]6'.B0LQBXNRVL@FNG*Y M)=<Q+:`[+="+S?=[2)H6-+"DJ8E\!?6DS">G4WH0:7JE65H26PI374IM7]I1 M(J%]GZ$28TI290H3WQ=^$`72;2K2;TK2<3JE"M`";D6R9NPK/&W/7V3(<397 M)<0GIII`NB)0DH*:`>6TS85YFK?+AMO+25,SSD)62X;*X13$S#QS"U0S+[EV M)$HLWQ*85?"\/TW/:6VZA9P&GU34?7+4,+.NG\BD(EC&:]$^V'(]02TK6=(" M:?W6<9TF1HU:==TF;Z+)8RT'ET0&:H#4209DB\*@-Q.@:S,L45W*63).=+#4 M]@9+PVB>K\,\E@TT1L0K)I?"H#LV3IB_0%S,;L0&)^`5ET<&FY(6I[V7-3@Y M0X-:8F`E9\%3*]>NOOP`ST37L$)Q<]+8*8-E'J0E&WHD-\--_SR`I)QYC]DP MI03:E:N)B2K"(K8_*?K9F5Q3\X*S@"5F%WU6KWW&RSS;M=W,@)+;:6(^9,`N M,!.7P"W<&JLU3^`\ACW7B)W42E,IQV98="(UDY.4C^@R,*.C-[;9VBT'@=EC MWCR](WS%#8P9PJ;(IQ7)`YV3V49&S61UX>2G^'B[A4C#H]M7N%>3Z0I*E7:5 MBOU`B&QS]@)VZ[=/@,I-PR*X<WQOF<E96[%VLT7B$QE;2:;B,^F!LG<G#7I@ M+9`ZT!O"U,OAL&PD'[XS+0Q$<Y4^D](H$Q/F@%)I'W>O8M,^=3T'6$+<I$?/ M<F*ZPRA52>)Q81"6;=`S/+.2B7A(#?;E<B5$9]Y4XX(D]<'F'_OF#?9Q`TY7 M#$/!IM3$;;-J1_6(M/5%;\'D`O5W,Z:Z]S)WF*,U*"`\C^C5LTTYK]*.LT+? MMQ),&NG?%\3"/\0&H6CFG()(5;YE8::^^X)"4<HR+9!#)%O`8@`A0MW*6HZ@ M8'=I;"PK;L"PT-(QA=1;O^78HCU/"C@LGCW=:$;M=84"E$IMUDYP\_0V4]Q$ MUUR*8,W&RU5C9:!;?S:1P43)]T;F;,RQ1I8_TTX&*U8L:T)M\"[O>JR`QDN% M24:IZUH#:5('OK7=7L6&7!8#P]<LO?8T7$'J0N#PU[ZN>R4#%4V5%4IM<=[G M!0HA]5'GA94DL//P0FKRUY.(Y1Z@#79II"L&>%YBR$*8J)9Q9E#-?\?<WK8_ M:G1!&,F0#T"IS%6MAF7#*","<&"%0;_8<C+T$K-&/_P`^G[@>;7TSJ5R(!#@ MC2('P@8Q<58QW$VM7[_'PCX&SQN]&LY[^.[OT*+I>ESBB#S'7\0T\8*'H"AH M4Y-K[E1LP<TF-T`FP?0F-JM#EYTF9R'PBV5N$:,[:^&(@F9YV3:/<,9EX74H MDA5&,CGVD3(LLL(S=5(?*"!!34+N>8`6+\L\=*\DGB@_SM3Z!JXEDC^BB/(& M]2$"0%"NV:UYDXAA2O-B2B(V:R`K=R`OQ;FOB^<Y;B#'CQBJ0MWE6I*JRTZA M@1Z\/=;$E]LGDG/<"?S:GZR^%4E%!/F;P:6^30(JP$+J2'!J`'K<*!9,L?]% MGRB%1Q[]:,*@+C,WT)+^;R%97X=X>3#%8S"%FG9@*S"C:Z$D6MM$IKM>>&R* M<K(?^J$?O9L6U!/)ESW%&$::,_Y%>_I`LAPIH$2>X0V)],[[<IH4$PT"#`\) M_H37G>*DRQ>E$?U\,[VF(7TB(VU])LU1N*'2TL%]WG>FX%>E--U2;OI2UA>E M(/(`B!PR`<MV48*=(*"=UU\GKLS`6WZ^*68N8/SC%_:(RR6?8T]L8[1>.QS^ M7+8"+X`'>0!`RGX6,<;"Y:(\B?LH`4H_8S"%AP(SA"8">)`'>B#U2G>$OL3R MH[TS0Y^=/$"@0.,#<Y)Y^L(O:;$"/$"@0.,#9T#5M:+>=XQ(X^*`OBFX8#Q; MX,V)'TB@0(/Z448I\\^"G&LK)S@V'5.[OB("9U"N\3PG&UD&D!M.:RW4#LB] M<K(?^J$?^Z$?^M&[0/EK&R2JX>25LK/RY0R@*+-<_DBGAO6)L9NXCQ*@ZOT` MVF.G*P")$XFL`QDQRD@V],9?!]G\]8'-X06T*,!"/5X&U/;)Y!G>D#B17B7! MN`.=IW-/0H``0H``0H``0H`<QW$<1X``1X``1X``1P"L67$%^([-/OGM_IYT MWT(F\CGVQ+8H,P8IJSBSIRO)6>'H^7?F5T\[CT*>D=(6#-IB4D>%)01[L?EP M+\9C;D%D/2Q=S/U4P0)$'`]<\ESRPV[/B*$P.H_-G=]V].[%'$]P!7XZ$1W1 9$1W1$1W1$7U/$PT"-F#1`871R@>EO@<:`*$P ` end --- No warranties whatsoever. I got it for free. You're getting it for free. We all get what we pay for. Mike Slomin !bellcore!lcuxa!mike2
mike2@lcuxa.UUCP (M S Slomin) (04/23/88)
For those who wish to rebuild and/or modify the posted executable 'less.com', I am posting the sources in three shar files. ---------------------Part 1 of 3-------------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file # 3. Execute the file with /bin/sh (not csh) to create the files: # # ch.c # command.c # dos_port.h # funcs.h # help.c # input.c # less.h # less.prj # line.c # setargv.c # # Created on April 22, 1988 # if test -f 'ch.c' then echo shar: will not over-write existing file "'ch.c'" else echo extracting "'ch.c'" sed 's/^X//' >ch.c <<'SHAR_EOF' X/* X * Low level character input from the input file. X * We use these special purpose routines which optimize moving X * both forward and backward from the current read pointer. X */ X X#include "less.h" X Xpublic int file = -1; /* File descriptor of the input file */ X X/* X * Pool of buffers holding the most recently used blocks of the input file. X */ X#define BUFSIZ 1024 Xstatic struct buf { X struct buf *next, *prev; X long block; X char data[BUFSIZ]; X}; Xstatic struct buf *bufs = NULL; Xpublic int nbufs; X X/* X * The buffer pool is kept as a doubly-linked circular list, X * in order from most- to least-recently used. X * The circular list is anchored by buf_anchor. X */ Xstatic struct { X struct buf *next, *prev; X} buf_anchor; X#define END_OF_CHAIN ((struct buf *)&buf_anchor) X#define buf_head buf_anchor.next X#define buf_tail buf_anchor.prev X X/* X * If we fail to allocate enough memory for buffers, we try to limp X * along with a minimum number of buffers. X */ X#define DEF_NBUFS 2 /* Minimum number of buffers */ X Xextern int clean_data; Xextern int ispipe; X X/* X * Current position in file. X * Stored as a block number and an offset into the block. X */ Xstatic long ch_block; Xstatic int ch_offset; X X/* X * Length of file, needed if input is a pipe. X */ Xstatic POSITION ch_fsize; X X/* X * Largest block number read if input is standard input (a pipe). X */ Xstatic long last_piped_block; X X/* X * Get the character pointed to by the read pointer. X * ch_get() is a macro which is more efficient to call X * than fch_get (the function), in the usual case X * that the block desired is at the head of the chain. X */ X#define ch_get() ((buf_head->block == ch_block) ? \ X buf_head->data[ch_offset] : fch_get()) X static int Xfch_get() X{ X register struct buf *bp; X register int n; X register int end; X POSITION pos; X X /* X * Look for a buffer holding the desired block. X */ X for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) X if (bp->block == ch_block) X goto found; X /* X * Block is not in a buffer. X * Take the least recently used buffer X * and read the desired block into it. X */ X bp = buf_tail; X bp->block = ch_block; X pos = ch_block * BUFSIZ; X if (ispipe) X { X /* X * The block requested should be one more than X * the last block read. X */ X if (ch_block != ++last_piped_block) X { X /* This "should not happen". */ X char message[80]; X sprintf(message, "Pipe error: last %ld, want %ld\n", X last_piped_block-1, ch_block); X error(message); X quit(); X } X } else X lseek(file, pos, 0); X X /* X * Read the block. This may take several reads if the input X * is coming from standard input, due to the nature of pipes. X */ X end = 0; X while ((n = read(file, &bp->data[end], BUFSIZ-end)) > 0) X if ((end += n) >= BUFSIZ) X break; X X if (n < 0) X { X error("read error"); X quit(); X } X X /* X * Set an EOF marker in the buffered data itself. X * Then ensure the data is "clean": there are no X * extra EOF chars in the data and that the "meta" X * bit (the 0200 bit) is reset in each char. X */ X if (end < BUFSIZ) X { X ch_fsize = pos + end; X bp->data[end] = EOF; X } X X if (!clean_data) X while (--end >= 0) X { X bp->data[end] &= 0177; X if (bp->data[end] == EOF) X bp->data[end] = '@'; X } X X found: X /* if (buf_head != bp) {this is guaranteed by the ch_get macro} */ X { X /* X * Move the buffer to the head of the buffer chain. X * This orders the buffer chain, most- to least-recently used. X */ X bp->next->prev = bp->prev; X bp->prev->next = bp->next; X X bp->next = buf_head; X bp->prev = END_OF_CHAIN; X buf_head->prev = bp; X buf_head = bp; X } X return (bp->data[ch_offset]); X} X X/* X * Determine if a specific block is currently in one of the buffers. X */ X static int Xbuffered(block) X long block; X{ X register struct buf *bp; X X for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) X if (bp->block == block) X return (1); X return (0); X} X X/* X * Seek to a specified position in the file. X * Return 0 if successful, non-zero if can't seek there. X */ X public int Xch_seek(pos) X register POSITION pos; X{ X long new_block; X X new_block = pos / BUFSIZ; X if (!ispipe || new_block == last_piped_block + 1 || buffered(new_block)) X { X /* X * Set read pointer. X */ X ch_block = new_block; X ch_offset = pos % BUFSIZ; X return (0); X } X return (1); X} X X/* X * Seek to the end of the file. X */ X public int Xch_end_seek() X{ X if (ispipe) X { X /* X * Do it the slow way: read till end of data. X */ X while (ch_forw_get() != EOF) X ; X } else X { X (void) ch_seek((POSITION)(lseek(file, (off_t)0, 2))); X } X return (0); X} X X/* X * Return the length of the file, if known. X */ X public POSITION Xch_length() X{ X if (ispipe) X return (ch_fsize); X return ((POSITION)(lseek(file, (off_t)0, 2))); X} X X/* X * Return the current position in the file. X */ X public POSITION Xch_tell() X{ X return (ch_block * BUFSIZ + ch_offset); X} X X/* X * Get the current char and post-increment the read pointer. X */ X public int Xch_forw_get() X{ X register int c; X X c = ch_get(); X if (c != EOF && ++ch_offset >= BUFSIZ) X { X ch_offset = 0; X ch_block ++; X } X return (c); X} X X/* X * Pre-decrement the read pointer and get the new current char. X */ X public int Xch_back_get() X{ X register int c; X X if (--ch_offset < 0) X { X if (ch_block <= 0 || (ispipe && !buffered(ch_block-1))) X { X ch_offset = 0; X return (EOF); X } X ch_offset = BUFSIZ - 1; X ch_block--; X } X c = ch_get(); X return (c); X} X X/* X * Initialize the buffer pool to all empty. X * Caller suggests that we use want_nbufs buffers. X */ X public void Xch_init(want_nbufs) X int want_nbufs; X{ X register struct buf *bp; X char *calloc(); X X if (nbufs < want_nbufs) X { X /* X * We don't have enough buffers. X * Free what we have (if any) and allocate some new ones. X */ X if (bufs != NULL) X free((char *)bufs); X bufs = (struct buf *) calloc(want_nbufs, sizeof(struct buf)); X nbufs = want_nbufs; X if (bufs == NULL) X { X /* X * Couldn't get that many. X * Try for a small default number of buffers. X */ X char message[80]; X sprintf(message, X "Cannot allocate %d buffers. Using %d buffers.", X nbufs, DEF_NBUFS); X error(message); X bufs = (struct buf *) calloc(DEF_NBUFS, sizeof(struct buf)); X nbufs = DEF_NBUFS; X if (bufs == NULL) X { X /* X * Couldn't even get the smaller number of bufs. X * Something is wrong here, don't continue. X */ X sprintf(message, X "Cannot even allocate %d buffers! Quitting.\n", X DEF_NBUFS); X error(message); X quit(); X /*NOTREACHED*/ X } X } X } X X /* X * Initialize the buffers to empty. X * Set up the circular list. X */ X for (bp = &bufs[0]; bp < &bufs[nbufs]; bp++) X { X bp->next = bp + 1; X bp->prev = bp - 1; X bp->block = (long)(-1); X } X bufs[0].prev = bufs[nbufs-1].next = END_OF_CHAIN; X buf_head = &bufs[0]; X buf_tail = &bufs[nbufs-1]; X last_piped_block = -1; X ch_fsize = NULL_POSITION; X (void) ch_seek((POSITION)0); X} SHAR_EOF if test 6841 -ne "`wc -c < 'ch.c'`" then echo shar: error transmitting "'ch.c'" '(should have been 6841 characters)' fi fi if test -f 'command.c' then echo shar: will not over-write existing file "'command.c'" else echo extracting "'command.c'" sed 's/^X//' >command.c <<'SHAR_EOF' X/* X * User-level command processor. X */ X X#include "less.h" X#include "position.h" X Xextern int erase_char, kill_char; Xextern int pr_type; Xextern int sigs; Xextern int ispipe; Xextern int quit_at_eof; Xextern int hit_eof; Xextern int sc_width, sc_height; Xextern char *first_cmd; Xextern char version[]; Xextern char current_file[]; Xextern char *editor; Xextern int onintr(); X Xstatic char cmdbuf[90]; /* Buffer for holding a multi-char command */ Xstatic char *cp; /* Pointer into cmdbuf */ Xstatic int cmd_col; /* Current column of the multi-char command */ Xstatic char mcc; /* The multi-char command letter (e.g. '/') */ Xstatic char last_mcc; /* The previous mcc */ X X/* X * Reset command buffer (to empty). X */ Xcmd_reset() X{ X cp = cmdbuf; X} X X/* X * Backspace in command buffer. X */ X static int Xcmd_erase() X{ X if (cp == cmdbuf) X /* X * Backspace past beginning of the string: X * this usually means abort the command. X */ X return (1); X X if (control_char(*--cp)) X { X /* X * Erase an extra character, for the carat. X */ X backspace(); X cmd_col--; X } X backspace(); X cmd_col--; X return (0); X} X X/* X * Set up the display to start a new multi-character command. X */ Xstart_mcc() X{ X lower_left(); X clear_eol(); X putc(mcc); X cmd_col = 1; X} X X/* X * Process a single character of a multi-character command, such as X * a number, or the pattern of a search command. X */ X static int Xcmd_char(c) X int c; X{ X if (c == erase_char) X { X if (cmd_erase()) X return (1); X } else if (c == kill_char) X { X /* {{ Could do this faster, but who cares? }} */ X while (cmd_erase() == 0) X ; X } else X { X /* X * Append the character to the string, X * if there is room in the buffer and on the screen. X */ X if (cp < &cmdbuf[sizeof(cmdbuf)-1] && cmd_col < sc_width-3) X { X *cp++ = c; X if (control_char(c)) X { X putc('^'); X cmd_col++; X c = carat_char(c); X } X putc(c); X cmd_col++; X } else X bell(); X } X return (0); X} X X/* X * Return the number currently in the command buffer. X */ X static int Xcmd_int() X{ X *cp = '\0'; X cp = cmdbuf; X return (atoi(cmdbuf)); X} X X/* X * Move the cursor to lower left before executing a command. X * This looks nicer if the command takes a long time before X * updating the screen. X */ X static void Xcmd_exec() X{ X lower_left(); X flush(); X} X X/* X * Display the appropriate prompt. X */ X static void Xprompt() X{ X register char *p; X X if (first_cmd != NULL && *first_cmd != '\0') X /* X * No prompt necessary if commands are from first_cmd X * rather than from the user. X */ X return; X X /* X * Select the proper prompt and display it. X */ X p = pr_string(); X if (p == NULL) X putc(':'); X else X { X so_enter(); X puts(p); X so_exit(); X } X} X X/* X * Get command character. X * The character normally comes from the keyboard, X * but may come from the "first_cmd" string. X */ X static int Xgetcc() X{ X if (first_cmd == NULL) X return (getc()); X X if (*first_cmd == '\0') X { X /* X * Reached end of first_cmd input. X */ X first_cmd = NULL; X if (cp > cmdbuf && position(TOP) == NULL_POSITION) X { X /* X * Command is incomplete, so try to complete it. X * There are only two cases: X * 1. We have "/string" but no newline. Add the \n. X * 2. We have a number but no command. Treat as #g. X * (This is all pretty hokey.) X */ X if (mcc != ':') X return ('\n'); X else X return ('g'); X } X return (getc()); X } X return (*first_cmd++); X} X X/* X * Main command processor. X * Accept and execute commands until a quit command, then return. X */ X public void Xcommands() X{ X register int c; X register int n; X register int scroll = 10; X X mcc = last_mcc = 0; X X for (;;) X { X /* X * Display prompt and accept a character. X */ X/* psignals(); */ /* See if any signals need processing */ X X if (quit_at_eof && hit_eof > 1) X /* X * After hitting end-of-file for the second time, X * automatically advance to the next file. X * If there are no more files, quit. X */ X next_file(1); X X cmd_reset(); X lower_left(); X clear_eol(); X prompt(); X c = getcc(); X X again: X if (sigs) X continue; X X if (mcc) X { X /* X * We are in a multi-character command. X * All chars until newline go into the command buffer. X * (Note that mcc == ':' is a special case that X * means a number is being entered.) X */ X if (mcc != ':' && (c == '\n' || c == '\r')) X { X /* X * Execute the command. X */ X *cp = '\0'; X cmd_exec(); X if (mcc == 'E') X { X char *p; X /* X * Ignore leading spaces X * in the filename. X */ X for (p = cmdbuf; *p == ' '; p++) ; X edit(p); X#if SHELL_ESCAPE X } else if (mcc == '!') X { X lsystem(cmdbuf); X error("!done"); X first_cmd = "r"; /* Repaint */ X#endif X } else X search(mcc, cmdbuf, n); X mcc = 0; X } else X { X if (mcc == ':' && (c < '0' || c > '9') && X c != erase_char && c != kill_char) X { X /* X * This is not part of the number X * we were entering. Process X * it as a regular character. X */ X mcc = 0; X goto again; X } X X /* X * Append the char to the command buffer. X */ X if (cmd_char(c)) X { X /* Abort the multi-char command. */ X mcc = 0; X continue; X } X c = getcc(); X goto again; X } X } else switch (c) X { X case '0': case '1': case '2': case '3': case '4': X case '5': case '6': case '7': case '8': case '9': X /* X * First digit of a number. X */ X mcc = ':'; X start_mcc(); X goto again; X X case 'f': X case ' ': X case CONTROL('F'): X /* X * Forward one screen. X */ X n = cmd_int(); X if (n <= 0) X n = sc_height - 1; X forward(n, 1); X break; X X case 'b': X case CONTROL('B'): X /* X * Backward one screen. X */ X n = cmd_int(); X if (n <= 0) X n = sc_height - 1; X backward(n, 1); X break; X X case 'e': X case 'j': X case '\r': X case '\n': X case CONTROL('E'): X /* X * Forward N (default 1) line. X */ X n = cmd_int(); X if (n <= 0) X n = 1; X forward(n, 0); X break; X X case 'y': X case 'k': X case CONTROL('K'): X case CONTROL('Y'): X /* X * Backward N (default 1) line. X */ X n = cmd_int(); X if (n <= 0) X n = 1; X backward(n, 0); X break; X X case 'd': X case CONTROL('D'): X /* X * Forward N lines X * (default same as last 'd' or 'u' command). X */ X n = cmd_int(); X if (n > 0) X scroll = n; X forward(scroll, 0); X break; X X case 'u': X case CONTROL('U'): X /* X * Forward N lines X * (default same as last 'd' or 'u' command). X */ X n = cmd_int(); X if (n > 0) X scroll = n; X backward(scroll, 0); X break; X X case 'R': X /* X * Flush buffers, then repaint screen. X */ X ch_init(0); X /* Fall thru */ X case 'r': X case CONTROL('R'): X case CONTROL('L'): X /* X * Repaint screen. X */ X repaint(); X break; X X case 'g': X /* X * Go to line N, default beginning of file. X */ X n = cmd_int(); X if (n <= 0) X n = 1; X cmd_exec(); X jump_back(n); X break; X X case 'p': X case '%': X /* X * Go to a specified percentage into the file. X */ X n = cmd_int(); X if (n < 0) X n = 0; X if (n > 100) X n = 100; X cmd_exec(); X jump_percent(n); X break; X X case 'G': X /* X * Go to line N, default end of file. X */ X n = cmd_int(); X cmd_exec(); X if (n <= 0) X jump_forw(); X else X jump_back(n); X break; X X case '=': X case CONTROL('G'): X /* X * Print file name, etc. X */ X error(eq_message()); X break; X X case 'L': X /* Print full prompt, including line number X * of the top line of the screen -- can be X * very slow! X */ X error(eq_mess2()); X break; X X case 'V': X /* X * Print version number, without the "@(#)". X */ X error(version+4); X break; X X case 'q': X case 'Q': X /* X * Exit. X */ X return; X X case '/': X case '?': X /* X * Search for a pattern. X * Accept chars of the pattern until \n. X */ X n = cmd_int(); X if (n <= 0) X n = 1; X mcc = last_mcc = c; X start_mcc(); X c = getcc(); X goto again; X X case 'n': X /* X * Repeat previous search. X */ X n = cmd_int(); X if (n <= 0) X n = 1; X mcc = last_mcc; X start_mcc(); X cmd_exec(); X search(mcc, (char *)NULL, n); X mcc = 0; X break; X X case 'h': X /* X * Help. X */ X help(); X repaint(); X break; X X case 'E': X /* X * Edit a new file. Get the filename. X */ X cmd_reset(); X mcc = 'E'; X start_mcc(); X#ifdef MSDOS X curs_l(1); X puts("Examine: "); X#else X puts("dit: "); /* This looks nicer */ X#endif X cmd_col += 5; X c = getcc(); X goto again; X X#if SHELL_ESCAPE X case '!': X /* X * Shell escape. X */ X cmd_reset(); X mcc = '!'; X start_mcc(); X c = getcc(); X goto again; X#endif X X#if EDITOR X case 'v': X if (ispipe) X { X error("Cannot edit standard input"); X break; X } X sprintf(cmdbuf, "%s %s", editor, current_file); X lsystem(cmdbuf); X first_cmd = "E\n"; X break; X#endif X X case 'N': X /* X * Examine next file. X */ X n = cmd_int(); X if (n <= 0) X n = 1; X next_file(n); X break; X X case 'P': X /* X * Examine previous file. X */ X n = cmd_int(); X if (n <= 0) X n = 1; X prev_file(n); X break; X X case '-': X /* X * Toggle a flag setting. X */ X mcc = '-'; X start_mcc(); X c = getcc(); X mcc = 0; X if (c == erase_char || c == kill_char) X break; X toggle_option(c); X break; X X case 'm': X /* X * Set a mark. X */ X lower_left(); X clear_eol(); X puts("mark: "); X c = getcc(); X if (c == erase_char || c == kill_char) X break; X setmark(c); X break; X X case '\'': X /* X * Go to a mark. X */ X lower_left(); X clear_eol(); X puts("goto mark: "); X c = getcc(); X if (c == erase_char || c == kill_char) X break; X gomark(c); X break; X X default: X bell(); X break; X } X } X} SHAR_EOF if test 9711 -ne "`wc -c < 'command.c'`" then echo shar: error transmitting "'command.c'" '(should have been 9711 characters)' fi fi if test -f 'dos_port.h' then echo shar: will not over-write existing file "'dos_port.h'" else echo extracting "'dos_port.h'" sed 's/^X//' >dos_port.h <<'SHAR_EOF' X/* this file is necessary to handle all the defines for building the program X using Microsoft C. The compiler will not accept them all on the command X line when they are included in the makefile. X */ X#ifdef DOSPORT X#define MSDOS 1 X#define MSC 1 /* only if using Microsoft C compiler */ X#define REGEXP 1 X#define EDITOR 1 X#define EDIT_PGM "memacs" X#define SHELL_ESCAPE 1 X#endif X SHAR_EOF if test 390 -ne "`wc -c < 'dos_port.h'`" then echo shar: error transmitting "'dos_port.h'" '(should have been 390 characters)' fi fi if test -f 'funcs.h' then echo shar: will not over-write existing file "'funcs.h'" else echo extracting "'funcs.h'" sed 's/^X//' >funcs.h <<'SHAR_EOF' X public void edit (); X public void next_file (); X public void prev_file (); X public void quit (); X public void init_option (); X public void toggle_option (); X public void scan_option (); X public void forward (); X public void backward (); X public void repaint (); X public void jump_forw (); X public void jump_back (); X public void jump_percent (); X public void jump_loc (); X public void init_mark (); X public void setmark (); X public void gomark (); X public void search (); X public int ch_seek (); X public int ch_end_seek (); X public POSITION ch_length (); X public POSITION ch_tell (); X public int ch_forw_get (); X public int ch_back_get (); X public void ch_init (); X public POSITION position (); X public void add_forw_pos (); X public void add_back_pos (); X public void pos_clear (); X public int onscreen (); X public POSITION forw_line (); X public POSITION back_line (); X public void put_line (); X public int control_char (); X public int carat_char (); X public void flush (); X public void dropout (); X public void putc (); X public void puts (); X public void error (); X public int error_width (); X public void raw_mode (); X public void get_term (); X public void init (); X public void deinit (); X public void home (); X public void add_line (); X public void lower_left (); X public void bell (); X public void vbell (); X public void clear (); X public void clear_eol (); X public void so_enter (); X public void so_exit (); X public void ul_enter (); X public void ul_exit (); X public void backspace (); X public void putbs (); X public char * eq_message (); X public char * eq_mess2 (); X public char * pr_string (); X public void prewind (); X public int pappend (); X public POSITION forw_raw_line (); X public POSITION back_raw_line (); X public void init_signals (); X public void psignals (); X public void lsystem (); X public void help (); X public void open_getc (); X public int getc (); X public void commands (); SHAR_EOF if test 1900 -ne "`wc -c < 'funcs.h'`" then echo shar: error transmitting "'funcs.h'" '(should have been 1900 characters)' fi fi if test -f 'help.c' then echo shar: will not over-write existing file "'help.c'" else echo extracting "'help.c'" sed 's/^X//' >help.c <<'SHAR_EOF' X#include "less.h" X X/* X * Display some help. X * Help is in two pages. X */ X static void Xhelp0() X{ X puts("f, SPACE Forward one screen.\n"); X puts("b Backward one screen.\n"); X puts("e, j, CR * Forward N lines, default 1.\n"); X puts("y, k * Backward N lines, default 1.\n"); X puts("d * Forward N lines, default 10 or last N to d or u command.\n"); X puts("u * Backward N lines, default 10 or last N to d or u command.\n"); X puts("r Repaint screen.\n"); X puts("g * Go to line N, default 1.\n"); X puts("G * Like g, but default is last line in file.\n"); X puts("= Print current file name\n"); X puts("L Print line number of 1st line -- CAN BE SLOW!\n"); X puts("/pattern * Search forward for N-th occurence of pattern.\n"); X puts("?pattern * Search backward for N-th occurence of pattern.\n"); X puts("n * Repeat previous search (for N-th occurence).\n"); X puts("q Exit.\n"); X error("More help..."); X} X X static void Xhelp1() X{ X char message[100]; X extern char all_options[]; X X puts("R Repaint screen, discarding buffered input.\n"); X puts("p, % * Position to N percent into the file.\n"); X puts("m<letter> Mark the current position with <letter>.\n"); X puts("'<letter> Return to a previously marked position.\n"); X sprintf(message, X "-X Toggle a flag (X may be one of \"%s\").\n", X all_options); X puts(message); X puts("E [file] Examine a new file.\n"); X puts("N Examine the next file (from the command line).\n"); X puts("P Examine the previous file (from the command line).\n"); X puts("V Print version number.\n"); X#if SHELL_ESCAPE X puts("!command Passes the command to a shell to be executed.\n"); X#endif X#if EDITOR X sprintf(message, X "v Edit the current file with $EDITOR (default %s).\n", X EDIT_PGM); X puts(message); X#endif X error(""); X} X X public void Xhelp() X{ X register int i; X X for (i = 0; i < 2; i++) X { X clear(); X puts("Commands marked with * may be preceeded by a number, N.\n\n"); X X switch (i) X { X case 0: help0(); break; X case 1: help1(); break; X } X } X} SHAR_EOF if test 2235 -ne "`wc -c < 'help.c'`" then echo shar: error transmitting "'help.c'" '(should have been 2235 characters)' fi fi if test -f 'input.c' then echo shar: will not over-write existing file "'input.c'" else echo extracting "'input.c'" sed 's/^X//' >input.c <<'SHAR_EOF' X/* X * High level routines dealing with getting lines of input X * from the file being viewed. X * X * When we speak of "lines" here, we mean PRINTABLE lines; X * lines processed with respect to the screen width. X * We use the term "raw line" to refer to lines simply X * delimited by newlines; not processed with respect to screen width. X */ X X#include "less.h" X Xextern int do_bs; Xextern int squeeze; Xextern char *line; X X/* X * Get the next line. X * A "current" position is passed and a "new" position is returned. X * The current position is the position of the first character of X * a line. The new position is the position of the first character X * of the NEXT line. The line obtained is the line starting at curr_pos. X */ X public POSITION Xforw_line(curr_pos) X POSITION curr_pos; X{ X POSITION new_pos; X register int c; X X if (curr_pos == NULL_POSITION || ch_seek(curr_pos)) X return (NULL_POSITION); X X c = ch_forw_get(); X if (c == EOF) X return (NULL_POSITION); X X prewind(); X for (;;) X { X if (c == '\n' || c == EOF) X { X /* X * End of the line. X */ X new_pos = ch_tell(); X break; X } X X /* X * Append the char to the line and get the next char. X */ X if (pappend(c)) X { X /* X * The char won't fit in the line; the line X * is too long to print in the screen width. X * End the line here. X */ X new_pos = ch_tell() - 1; X break; X } X c = ch_forw_get(); X } X (void) pappend('\0'); X X if (squeeze && *line == '\0') X { X /* X * This line is blank. X * Skip down to the last contiguous blank line X * and pretend it is the one which we are returning. X */ X while ((c = ch_forw_get()) == '\n') X ; X if (c != EOF) X (void) ch_back_get(); X new_pos = ch_tell(); X } X X return (new_pos); X} X X/* X * Get the previous line. X * A "current" position is passed and a "new" position is returned. X * The current position is the position of the first character of X * a line. The new position is the position of the first character X * of the PREVIOUS line. The line obtained is the one starting at new_pos. X */ X public POSITION Xback_line(curr_pos) X POSITION curr_pos; X{ X POSITION new_pos, begin_new_pos; X int c; X X if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 || X ch_seek(curr_pos-1)) X return (NULL_POSITION); X X if (squeeze) X { X /* X * Find out if the "current" line was blank. X */ X (void) ch_forw_get(); /* Skip the newline */ X c = ch_forw_get(); /* First char of "current" line */ X (void) ch_back_get(); /* Restore our position */ X (void) ch_back_get(); X X if (c == '\n') X { X /* X * The "current" line was blank. X * Skip over any preceeding blank lines, X * since we skipped them in forw_line(). X */ X while ((c = ch_back_get()) == '\n') X ; X if (c == EOF) X return (NULL_POSITION); X (void) ch_forw_get(); X } X } X X /* X * Scan backwards until we hit the beginning of the line. X */ X for (;;) X { X c = ch_back_get(); X if (c == '\n') X { X /* X * This is the newline ending the previous line. X * We have hit the beginning of the line. X */ X new_pos = ch_tell() + 1; X break; X } X if (c == EOF) X { X /* X * We have hit the beginning of the file. X * This must be the first line in the file. X * This must, of course, be the beginning of the line. X */ X new_pos = (POSITION)0; X break; X } X } X X /* X * Now scan forwards from the beginning of this line. X * We keep discarding "printable lines" (based on screen width) X * until we reach the curr_pos. X * X * {{ This algorithm is pretty inefficient if the lines X * are much longer than the screen width, X * but I don't know of any better way. }} X */ X if (ch_seek(new_pos)) X return (NULL_POSITION); X loop: X begin_new_pos = new_pos; X prewind(); X X do X { X c = ch_forw_get(); X new_pos++; X if (c == '\n') X break; X if (pappend(c)) X { X /* X * Got a full printable line, but we haven't X * reached our curr_pos yet. Discard the line X * and start a new one. X */ X (void) pappend('\0'); X (void) ch_back_get(); X new_pos--; X goto loop; X } X } while (new_pos < curr_pos); X X (void) pappend('\0'); X X return (begin_new_pos); X} SHAR_EOF if test 4079 -ne "`wc -c < 'input.c'`" then echo shar: error transmitting "'input.c'" '(should have been 4079 characters)' fi fi if test -f 'less.h' then echo shar: will not over-write existing file "'less.h'" else echo extracting "'less.h'" sed 's/^X//' >less.h <<'SHAR_EOF' X/* X * Standard include file for "less". X */ X X/* X * To get the required defs when porting less to msdos X */ X#ifdef DOSPORT X#include "dos_port.h" X#endif X X/* X * Language details. X */ X#if !VOID X#define void int X#endif X#define public /* PUBLIC FUNCTION */ X X/* X * Special types and constants. X */ Xtypedef long POSITION; X/* X * {{ Warning: if POSITION is changed to other than "long", X * you may have to change some of the printfs which use "%ld" X * to print a variable of type POSITION. }} X */ X X#define END_POSITION ((POSITION)(-2)) X#define NULL_POSITION ((POSITION)(-1)) X X#define EOF (0) X#define NULL (0) X X/* How quiet should we be? */ X#define NOT_QUIET 0 /* Ring bell at eof and for errors */ X#define LITTLE_QUIET 1 /* Ring bell only for errors */ X#define VERY_QUIET 2 /* Never ring bell */ X X/* How should we prompt? */ X#define PR_SHORT 0 /* Prompt with colon */ X#define PR_MEDIUM 1 /* Prompt with message */ X#define PR_LONG 2 /* Prompt with longer message */ X X/* How should we handle backspaces? */ X#define BS_UNDERLINE 0 /* Underlining converted to underline mode */ X#define BS_NORMAL 1 /* \b treated as normal char; actually output */ X#define BS_CONTROL 2 /* \b treated as control char; prints as ^H */ X X/* Flag to eq_message() telling what to put in the message */ X#define MNAME 001 /* File name */ X#define MOF 002 /* "file x of y" */ X#define MBYTE 004 /* "byte x/y" */ X#define MPCT 010 /* Percentage into the file */ X X/* Special chars used to tell put_line() to do something special */ X#define UL_CHAR '\201' /* Enter underline mode */ X#define UE_CHAR '\202' /* Exit underline mode */ X#define BO_CHAR '\203' /* Enter boldface mode */ X#define BE_CHAR '\204' /* Exit boldface mode */ X X#define CONTROL(c) ((c)&037) X#define SIGNAL(sig,func) signal(sig,func) X X/* Must locate the typedef of off_t in the Microsoft package. If using X * a different compiler --- typedef long off_t ---. X */ X X#ifdef MSDOS X#ifdef MSC /* Microsoft's types.h is needed */ X#endif X#endif X Xtypedef long off_t; Xextern off_t lseek(); X X#include "funcs.h" X SHAR_EOF if test 2051 -ne "`wc -c < 'less.h'`" then echo shar: error transmitting "'less.h'" '(should have been 2051 characters)' fi fi if test -f 'less.prj' then echo shar: will not over-write existing file "'less.prj'" else echo extracting "'less.prj'" sed 's/^X//' >less.prj <<'SHAR_EOF' XCH.C (dos_port.h funcs.h less.h) XCOMMAND.C (dos_port.h funcs.h less.h position.h) XHELP.C (dos_port.h funcs.h less.h) XINPUT.C (dos_port.h funcs.h less.h) XLINE.C (dos_port.h funcs.h less.h) XMAIN.C (dos_port.h funcs.h less.h position.h) XOPTION.C (dos_port.h funcs.h less.h) XOUTPUT.C (dos_port.h funcs.h less.h scrn.h) XPOSITION.C (dos_port.h funcs.h less.h position.h) XPRIM.C (dos_port.h funcs.h less.h position.h) XPROMPT.C (dos_port.h funcs.h less.h position.h) XREGERROR.C XREGEXP.C (regexp.h regmagic.h) XSCRN.C (scrn.h) XSETARGV.C XSIGNAL.C (dos_port.h funcs.h less.h) XTTYIN.C (dos_port.h funcs.h less.h) XVERSION.C SHAR_EOF if test 610 -ne "`wc -c < 'less.prj'`" then echo shar: error transmitting "'less.prj'" '(should have been 610 characters)' fi fi if test -f 'line.c' then echo shar: will not over-write existing file "'line.c'" else echo extracting "'line.c'" sed 's/^X//' >line.c <<'SHAR_EOF' X/* X * Routines to manipulate the "line buffer". X * The line buffer holds a line of output as it is being built X * in preparation for output to the screen. X * We keep track of the PRINTABLE length of the line as it is being built. X */ X X#include "less.h" X Xstatic char linebuf[1024]; /* Buffer which holds the current output line */ Xstatic char *curr; /* Pointer into linebuf */ Xstatic int column; /* Printable length, accounting for X backspaces, etc. */ X/* X * A ridiculously complex state machine takes care of backspaces X * when in BS_SPECIAL mode. The complexity arises from the attempt X * to deal with all cases, especially involving long lines with underlining, X * boldfacing or whatever. There are still some cases which will break it. X * X * There are four states: X * LN_NORMAL is the normal state (not in underline mode). X * LN_UNDERLINE means we are in underline mode. We expect to get X * either a sequence like "_\bX" or "X\b_" to continue X * underline mode, or anything else to end underline mode. X * LN_BOLDFACE means we are in boldface mode. We expect to get sequences X * like "X\bX\b...X\bX" to continue boldface mode, or anything X * else to end boldface mode. X * LN_UL_X means we are one character after LN_UNDERLINE X * (we have gotten the '_' in "_\bX" or the 'X' in "X\b_"). X * LN_UL_XB means we are one character after LN_UL_X X * (we have gotten the backspace in "_\bX" or "X\b_"; X * we expect one more ordinary character, X * which will put us back in state LN_UNDERLINE). X * LN_BO_X means we are one character after LN_BOLDFACE X * (we have gotten the 'X' in "X\bX"). X * LN_BO_XB means we are one character after LN_BO_X X * (we have gotten the backspace in "X\bX"; X * we expect one more 'X' which will put us back X * in LN_BOLDFACE). X */ Xstatic int ln_state; /* Currently in normal/underline/bold/etc mode? */ X#define LN_NORMAL 0 /* Not in underline, boldface or whatever mode */ X#define LN_UNDERLINE 1 /* In underline, need next char */ X#define LN_UL_X 2 /* In underline, got char, need \b */ X#define LN_UL_XB 3 /* In underline, got char & \b, need one more */ X#define LN_BOLDFACE 4 /* In boldface, need next char */ X#define LN_BO_X 5 /* In boldface, got char, need \b */ X#define LN_BO_XB 6 /* In boldface, got char & \b, need same char */ X Xpublic char *line; /* Pointer to the current line. X Usually points to linebuf. */ X Xextern int bs_mode; Xextern int tabstop; Xextern int bo_width, be_width; Xextern int ul_width, ue_width; Xextern int sc_width, sc_height; X X/* X * Rewind the line buffer. X */ X public void Xprewind() X{ X line = curr = linebuf; X ln_state = LN_NORMAL; X column = 0; X} X X/* X * Append a character to the line buffer. X * Expand tabs into spaces, handle underlining, boldfacing, etc. X * Returns 0 if ok, 1 if couldn't fit in buffer. X */ X X#define NEW_COLUMN(newcol) if ((newcol) + ((ln_state)?ue_width:0) > sc_width) \ X return (1); else column = (newcol) X X public int Xpappend(c) X int c; X{ X if (c == '\0') X { X /* X * Terminate any special modes, if necessary. X * Append a '\0' to the end of the line. X */ X switch (ln_state) X { X case LN_UL_X: X curr[0] = curr[-1]; X curr[-1] = UE_CHAR; X curr++; X break; X case LN_BO_X: X curr[0] = curr[-1]; X curr[-1] = BE_CHAR; X curr++; X break; X case LN_UL_XB: X case LN_UNDERLINE: X *curr++ = UE_CHAR; X break; X case LN_BO_XB: X case LN_BOLDFACE: X *curr++ = BE_CHAR; X break; X } X ln_state = LN_NORMAL; X *curr = '\0'; X return (0); X } X X if (curr > linebuf + sizeof(linebuf) - 12) X /* X * Almost out of room in the line buffer. X * Don't take any chances. X * {{ Linebuf is supposed to be big enough that this X * will never happen, but may need to be made X * bigger for wide screens or lots of backspaces. }} X */ X return (1); X X if (bs_mode == BS_UNDERLINE) X { X /* X * Advance the state machine. X */ X switch (ln_state) X { X case LN_NORMAL: X if (curr <= linebuf + 1 || curr[-1] != '\b') X break; X X if (c == curr[-2]) X goto enter_boldface; X if (c == '_' || curr[-2] == '_') X goto enter_underline; X curr -= 2; X break; X Xenter_boldface: X /* X * We have "X\bX" (including the current char). X * Switch into boldface mode. X */ X if (column + bo_width + be_width + 1 >= sc_width) X /* X * Not enough room left on the screen to X * enter and exit boldface mode. X */ X return (1); X X if (bo_width > 0 && X curr > linebuf + 2 && curr[-3] == ' ') X { X /* X * Special case for magic cookie terminals: X * if the previous char was a space, replace X * it with the "enter boldface" sequence. X */ X curr[-3] = BO_CHAR; X column += bo_width-1; X } else X { X curr[-1] = curr[-2]; X curr[-2] = BO_CHAR; X column += bo_width; X curr++; X } X goto ln_bo_xb_case; X Xenter_underline: X /* X * We have either "_\bX" or "X\b_" (including X * the current char). Switch into underline mode. X */ X if (column + ul_width + ue_width + 1 >= sc_width) X /* X * Not enough room left on the screen to X * enter and exit underline mode. X */ X return (1); X X if (ul_width > 0 && X curr > linebuf + 2 && curr[-3] == ' ') X { X /* X * Special case for magic cookie terminals: X * if the previous char was a space, replace X * it with the "enter underline" sequence. X */ X curr[-3] = UL_CHAR; X column += ul_width-1; X } else X { X curr[-1] = curr[-2]; X curr[-2] = UL_CHAR; X column += ul_width; X curr++; X } X goto ln_ul_xb_case; X /*NOTREACHED*/ X case LN_UL_XB: X /* X * Termination of a sequence "_\bX" or "X\b_". X */ X if (c != '_' && curr[-2] != '_' && c == curr[-2]) X { X /* X * We seem to have run on from underlining X * into boldfacing - this is a nasty fix, but X * until this whole routine is rewritten as a X * real DFA, ... well ... X */ X curr[0] = curr[-2]; X curr[-2] = UE_CHAR; X curr[-1] = BO_CHAR; X curr += 2; /* char & non-existent backspace */ X ln_state = LN_BO_XB; X goto ln_bo_xb_case; X } Xln_ul_xb_case: X if (c == '_') X c = curr[-2]; X curr -= 2; X ln_state = LN_UNDERLINE; X break; X case LN_BO_XB: X /* X * Termination of a sequnce "X\bX". X */ X if (c != curr[-2] && (c == '_' || curr[-2] == '_')) X { X /* X * We seem to have run on from X * boldfacing into underlining. X */ X curr[0] = curr[-2]; X curr[-2] = BE_CHAR; X curr[-1] = UL_CHAR; X curr += 2; /* char & non-existent backspace */ X ln_state = LN_UL_XB; X goto ln_ul_xb_case; X } Xln_bo_xb_case: X curr -= 2; X ln_state = LN_BOLDFACE; X break; X case LN_UNDERLINE: X if (column + ue_width + bo_width + 1 + be_width >= sc_width) X /* X * We have just barely enough room to X * exit underline mode and handle a possible X * underline/boldface run on mixup. X */ X return (1); X ln_state = LN_UL_X; X break; X case LN_BOLDFACE: X if (c == '\b') X { X ln_state = LN_BO_XB; X break; X } X if (column + be_width + ul_width + 1 + ue_width >= sc_width) X /* X * We have just barely enough room to X * exit underline mode and handle a possible X * underline/boldface run on mixup. X */ X return (1); X ln_state = LN_BO_X; X break; X case LN_UL_X: X if (c == '\b') X ln_state = LN_UL_XB; X else X { X /* X * Exit underline mode. X * We have to shuffle the chars a bit X * to make this work. X */ X curr[0] = curr[-1]; X curr[-1] = UE_CHAR; X column += ue_width; X if (ue_width > 0 && curr[0] == ' ') X /* X * Another special case for magic X * cookie terminals: if the next X * char is a space, replace it X * with the "exit underline" sequence. X */ X column--; X else X curr++; X ln_state = LN_NORMAL; X } X break; X case LN_BO_X: X if (c == '\b') X ln_state = LN_BO_XB; X else X { X /* X * Exit boldface mode. X * We have to shuffle the chars a bit X * to make this work. X */ X curr[0] = curr[-1]; X curr[-1] = BE_CHAR; X column += be_width; X if (be_width > 0 && curr[0] == ' ') X /* X * Another special case for magic X * cookie terminals: if the next X * char is a space, replace it X * with the "exit boldface" sequence. X */ X column--; X else X curr++; X ln_state = LN_NORMAL; X } X break; X } X } X X if (c == '\t') X { X /* X * Expand a tab into spaces. X */ X do X { X NEW_COLUMN(column+1); X } while ((column % tabstop) != 0); X *curr++ = '\t'; X return (0); X } X X if (c == '\b') X { X if (bs_mode == BS_CONTROL) X { X /* X * Treat backspace as a control char: output "^H". X */ X NEW_COLUMN(column+2); X *curr++ = ('H' | 0200); X } else X { X /* X * Output a real backspace. X */ X column--; X *curr++ = '\b'; X } X return (0); X } X X if (control_char(c)) X { X /* X * Put a "^X" into the buffer. X * The 0200 bit is used to tell put_line() to prefix X * the char with a ^. We don't actually put the ^ X * in the buffer because we sometimes need to move X * chars around, and such movement might separate X * the ^ from its following character. X * {{ This should be redone so that we can use an X * 8 bit (e.g. international) character set. }} X */ X NEW_COLUMN(column+2); X *curr++ = (carat_char(c) | 0200); X return (0); X } X X /* X * Ordinary character. Just put it in the buffer. X */ X NEW_COLUMN(column+1); X *curr++ = c; X return (0); X} X X/* X * Analogous to forw_line(), but deals with "raw lines": X * lines which are not split for screen width. X * {{ This is supposed to be more efficient than forw_line(). }} X */ X public POSITION Xforw_raw_line(curr_pos) X POSITION curr_pos; X{ X register char *p; X register int c; X POSITION new_pos; X X if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || X (c = ch_forw_get()) == EOF) X return (NULL_POSITION); X X p = linebuf; X X for (;;) X { X if (c == '\n' || c == EOF) X { X new_pos = ch_tell(); X break; X } X if (p >= &linebuf[sizeof(linebuf)-1]) X { X /* X * Overflowed the input buffer. X * Pretend the line ended here. X * {{ The line buffer is supposed to be big X * enough that this never happens. }} X */ X new_pos = ch_tell() - 1; X break; X } X *p++ = c; X c = ch_forw_get(); X } X *p = '\0'; X line = linebuf; X return (new_pos); X} X X/* X * Analogous to back_line(), but deals with "raw lines". X * {{ This is supposed to be more efficient than back_line(). }} X */ X public POSITION Xback_raw_line(curr_pos) X POSITION curr_pos; X{ X register char *p; X register int c; X POSITION new_pos; X X if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 || X ch_seek(curr_pos-1)) X return (NULL_POSITION); X X p = &linebuf[sizeof(linebuf)]; X *--p = '\0'; X X for (;;) X { X c = ch_back_get(); X if (c == '\n') X { X /* X * This is the newline ending the previous line. X * We have hit the beginning of the line. X */ X new_pos = ch_tell() + 1; X break; X } X if (c == EOF) X { X /* X * We have hit the beginning of the file. X * This must be the first line in the file. X * This must, of course, be the beginning of the line. X */ X new_pos = (POSITION)0; X break; X } X if (p <= linebuf) X { X /* X * Overflowed the input buffer. X * Pretend the line ended here. X */ X new_pos = ch_tell() + 1; X break; X } X *--p = c; X } X line = p; X return (new_pos); X} SHAR_EOF if test 11264 -ne "`wc -c < 'line.c'`" then echo shar: error transmitting "'line.c'" '(should have been 11264 characters)' fi fi if test -f 'setargv.c' then echo shar: will not over-write existing file "'setargv.c'" else echo extracting "'setargv.c'" sed 's/^X//' >setargv.c <<'SHAR_EOF' X/* setargv -- setup argv with wild card expansion */ X/* copyright 1987 Michael M Rubenstein */ X X/* This program may be freely distributed provided no fee is assessed. */ X X/* This file implements wild card expansion in argv for Turbo C 1.5. */ X/* Strings of characters in either quotes (") or appostrophes (') on the */ X/* command line are considered a single argument. However, backslash */ X/* escapes are not implemented. A quote may be included in an argument */ X/* which is enclosed in appostrophes and an appostrophe may be included */ X/* in an argument enclosed in quotes. Either may be included as an */ X/* in an argument starting with any other character. */ X X/* Any argument which is not enclosed in quotes or appostrophes, does not */ X/* begin with a hyphen (-), and which contains an asterisk (*) or question */ X/* mark (?) will be expanded. It is NOT an error for an argument to have a */ X/* null expansion (no matching files). Only ordinary files (not */ X/* directories or hidden or system files) will be included in the */ X/* expansion. */ X X/* To use this function, simply compile it with the appropriate memory */ X/* model and include in the link. This can be accomplished very simply */ X/* in the integrated environment by simply including this file in the */ X/* project file. In the command line version, simply include this file */ X/* (or a precompiled .OBJ version) on the command line. */ X X#include <ctype.h> X#include <dir.h> X#include <dos.h> X#include <process.h> X X#define FALSE 0 X#define TRUE 1 X Xvoid putarg(unsigned char far *, unsigned char far *); X Xextern int _argc; /* NB: for TC 1.0, change to __argc */ Xextern char **_argv; /* NB: for TC 1.0, change to __argv */ Xextern unsigned _psp; Xextern unsigned _envseg; Xextern unsigned _envLng; Xextern unsigned char _osmajor; Xextern void _abort(); Xextern char *sbrk(int); X Xvoid _setargv() X{ X unsigned char far *cmdtail; X unsigned char *firstarg; X unsigned char far *cmdarg; X int wild; X int c; X unsigned char buffer[129]; X unsigned char *p, *q; X unsigned char *lastdir; X char **wargv; X int i; X struct ffblk ffb; X X cmdtail = MK_FP(_psp, 0x81); X cmdtail[cmdtail[-1]] = '\0'; /* make sure null at end */ X firstarg = (unsigned char *) sbrk(0); X _argc = 1; X X while (*cmdtail != '\0') X { X /* skip white space */ X while (isascii(*cmdtail) && isspace(*cmdtail)) X ++cmdtail; X X /* done with command loop if end of command tail */ X if (*cmdtail == '\0') X break; X X /* if quoted string, just save the argument */ X if ((c = *cmdtail) == '"' || c == '\'') X { X cmdarg = ++cmdtail; X while (*cmdtail != c && *cmdtail != '\0') X ++cmdtail; X putarg(cmdarg, cmdtail); X if (*cmdtail != '\0') X ++cmdtail; X continue; X } X X /* find word */ X cmdarg = cmdtail; X wild = FALSE; X p = lastdir = buffer; X while ((c = *cmdtail) != '\0' X && (!isascii(c) || !isspace(c))) X { X /* wild is TRUE if word contains * or ? */ X wild |= (c == '*' || c == '?'); X/* Next line was added to make the treatment of / and \ alike */ X if (c == '/') c = '\\'; X *(p++) = c; X X /* lastdir points to the first character of the base file name */ X if (c == '\\' || c == ':') X lastdir = p; X ++cmdtail; X } X *p = '\0'; X X if (wild && *cmdarg != '-') X for (c = findfirst((char *) buffer, &ffb, 0); X c == 0; X c = findnext(&ffb)) X { X /* use lower case for wild card expanded names (my prejudice) */ X for (p = lastdir, q = (unsigned char *) ffb.ff_name; *q != '\0';) X *(p++) = tolower(*(q++)); X ; X putarg(buffer, p); X } X else X putarg(cmdarg, cmdtail); X } X X /* allocate argv */ X if ((wargv = (char **) sbrk(sizeof(char *) * (_argc + 1))) == (char **) -1) X abort(); X _argv = wargv; X X /* store program name */ X if (_osmajor < 3) X *(wargv++) = "C"; X else X { X cmdtail = cmdarg = MK_FP(_envseg, _envLng + 2); X# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) X *(wargv++) = sbrk(0); X while (*cmdtail != '\0') X ++cmdtail; X putarg(cmdarg, cmdtail); X --_argc; X# else X *(wargv++) = (char *) cmdarg; X# endif X } X X /* store arguments */ X for (i = _argc; --i;) X { X *(wargv++) = (char *) firstarg; X while(*++firstarg != '\0') X ; X ++firstarg; X } X *wargv = (char *) 0; X} X Xstatic void putarg(from, to) X unsigned char far *from, far *to; X{ X char *p; X X if ((p = sbrk(to - from + 1)) == (char *) -1) X abort(); X while (from < to) X *(p++) = *(from++); X *p = '\0'; X ++_argc; X} SHAR_EOF if test 5142 -ne "`wc -c < 'setargv.c'`" then echo shar: error transmitting "'setargv.c'" '(should have been 5142 characters)' fi fi # end of shell archive exit 0 --- No warranties whatsoever. Mike Slomin !bellcore!lcuxa!mike2
mike2@lcuxa.UUCP (M S Slomin) (04/23/88)
------------------Part 2 of 3--------------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file # 3. Execute the file with /bin/sh (not csh) to create the files: # # main.c # option.c # output.c # position.c # position.h # prim.c # prompt.c # ttyin.c # version.c # # Created on 'date' # if test -f 'main.c' then echo shar: will not over-write existing file "'main.c'" else echo extracting "'main.c'" sed 's/^X//' >main.c <<'SHAR_EOF' X/* X * Entry point, initialization, miscellaneous routines. X */ X X#include "less.h" X#include "position.h" X#ifdef MSDOS X#ifdef MSC X#include <fcntl.h> /* needed to open file in untranslated mode */ X#endif X#endif X#include <dos.h> X Xextern unsigned _stklen = 4096; /* TurboC ver. 1.5 stack adjuster */ X Xpublic int ispipe; Xpublic char * first_cmd; Xpublic char * every_first_cmd; Xpublic int new_file; Xpublic int is_tty; Xpublic char current_file[128]; Xpublic int ac; Xpublic char **av; Xpublic int curr_ac; X#if EDITOR Xpublic char * editor; X#endif X Xextern int onintr(); Xextern int file; Xextern int nbufs; Xextern int sigs; Xextern int quit_at_eof; Xextern int p_nbufs, f_nbufs; Xextern int back_scroll; Xextern int top_scroll; Xextern int sc_height; X X X/* X * Edit a new file. X * Filename "-" means standard input. X * No filename means the "current" file, from the command line. X */ X public void Xedit(filename) X char *filename; X{ X register int f; X char message[100]; X static int any_edited = 0; X static int hold_scroll = 0; X X if (filename == NULL || *filename == '\0') X { X if (curr_ac >= ac) X { X error("No current file"); X return; X } X filename = av[curr_ac]; X } X if (strcmp(filename, "-") == 0) X f = 0; /* Standard input */ X#ifdef MSDOS X#ifdef MSC X else if ((f = open(filename, O_BINARY)) < 0) X /* untranslated mode */ X /* needed for MSDOS so that cr-lf translations do not */ X /* cause problems in the position calculations */ X#else X else if ((f = open(filename, 0)) < 0) X#endif X#endif X { X sprintf(message, "Cannot open %.*s", X error_width()-13, filename); X if (any_edited) X error(message); X else X { X puts(message); X hold_scroll = 1; X#ifdef MSDOS X exit(0); X#endif X } X return; X } X if (isatty(f)) X { X /* X * Not really necessary to call this an error, X * but if the control terminal (for commands) X * and the input file (for data) are the same, X * we get weird results at best. X */ X error("Can't take input from a terminal"); X if (f > 0) X close(f); X return; X } X X /* X * Close the current input file and set up to use the new one. X */ X if (file > 0) X close(file); X new_file = 1; X strcpy(current_file, filename); X ispipe = (f == 0); X file = f; X ch_init( (ispipe) ? p_nbufs : f_nbufs ); X init_mark(); X if (every_first_cmd != NULL) X first_cmd = every_first_cmd; X if (is_tty) X { X any_edited = 1; X if (hold_scroll) X { X /* X * Before erasing the screen contents, X * display the file name and ask for a keystroke. X */ X error(filename); X hold_scroll = 0; X } X if (first_cmd == NULL || *first_cmd == '\0') X { X /* X * Display the first screen. X */ X jump_back(1); X } else X { X /* X * The first_cmd will hopefully redisplay the X * screen, so we need not display anything yet. X * Indicate there is nothing yet on the screen. X */ X pos_clear(); X } X } X} X X/* X * Edit the next file in the command line list. X */ X public void Xnext_file(n) X int n; X{ X if (curr_ac + n >= ac) X { X if (quit_at_eof) X quit(); X error("No (N-th) next file"); X } else X edit(av[curr_ac += n]); X} X X/* X * Edit the previous file in the command line list. X */ X public void Xprev_file(n) X int n; X{ X if (curr_ac - n < 0) X error("No (N-th) previous file"); X else X edit(av[curr_ac -= n]); X} X X/* X * Copy a file directly to standard output. X * Used if standard output is not a tty. X */ X static void Xcat_file() X{ X register int c; X X while ((c = ch_forw_get()) != EOF) X putc(c); X flush(); X} X X/* X * Entry point. X */ Xmain(argc, argv) X int argc; X char *argv[]; X{ X char *getenv(); X X X /* X * Process command line arguments and LESS environment arguments. X * Command line arguments override environment arguments. X */ X init_option(); X scan_option(getenv("LESS")); X argv++; X while ( (--argc > 0) && X (argv[0][0] == '-' || argv[0][0] == '+') && X argv[0][1] != '\0') X scan_option(*argv++); X X#if EDITOR X editor = getenv("EDITOR"); X if (editor == NULL || *editor == '\0') X editor = EDIT_PGM; X#endif X X /* X * Set up list of files to be examined. X */ X ac = argc; X av = argv; X curr_ac = 0; X X /* X * Set up terminal, etc. X */ X is_tty = isatty(1); X if (!is_tty) X { X /* X * Output is not a tty. X * Just copy the input file(s) to output. X */ X if (ac < 1) X { X edit("-"); X cat_file(); X } else X { X do X { X edit((char *)NULL); X if (file >= 0) X cat_file(); X } while (++curr_ac < ac); X } X exit(0); X } X X raw_mode(1); X get_term(); X open_getc(); X init(); X X if (back_scroll < 0) X { X /* {{ KLUDGE }} */ X back_scroll = sc_height-1; X if (top_scroll) X back_scroll--; X } X X X /* X * Select the first file to examine. X */ X if (ac < 1) X edit("-"); /* Standard input */ X else X { X /* X * Try all the files named as command arguments. X * We are simply looking for one which can be X * opened without error. X */ X do X { X edit((char *)NULL); X if (file >= 0) X /* We can open this file. */ X break; X putc('\n'); flush(); X } while (++curr_ac < ac); X } X ctrlbrk(onintr); X if (file >= 0) X commands(); X quit(); X} X X/* X * Exit the program. X */ X public void Xquit() X{ X /* X * Put cursor at bottom left corner, clear the line, X * reset the terminal modes, and exit. X */ X lower_left(); X clear_eol(); X deinit(); X flush(); X raw_mode(0); X exit(0); X} X Xonintr() X{ X return(1); X} X SHAR_EOF if test 5217 -ne "`wc -c < 'main.c'`" then echo shar: error transmitting "'main.c'" '(should have been 5217 characters)' fi fi if test -f 'option.c' then echo shar: will not over-write existing file "'option.c'" else echo extracting "'option.c'" sed 's/^X//' >option.c <<'SHAR_EOF' X/* X * Process command line options. X * Each option is a single letter which controls a program variable. X * The options have defaults which may be changed via X * the command line option, or toggled via the "-" command. X */ X X#include "less.h" X X#define toupper(c) ((c)-'a'+'A') X X/* X * Types of options. X */ X#define BOOL 01 /* Boolean option: 0 or 1 */ X#define TRIPLE 02 /* Triple-valued option: 0, 1 or 2 */ X#define NUMBER 04 /* Numeric option */ X#define NO_TOGGLE 0100 /* Option cannot be toggled with "-" cmd */ X X/* X * Variables controlled by command line options. X */ Xpublic int p_nbufs, f_nbufs; /* Number of buffers. There are two values, X one used for input from a pipe and X the other for input from a file. */ Xpublic int clean_data; /* Can we assume the data is "clean"? X (That is, free of nulls, etc) */ Xpublic int quiet; /* Should we suppress the audible bell? */ Xpublic int top_search; /* Should forward searches start at the top X of the screen? (alternative is bottom) */ Xpublic int top_scroll; /* Repaint screen from top? X (alternative is scroll from bottom) */ Xpublic int pr_type; /* Type of prompt (short, medium, long) */ Xpublic int bs_mode; /* How to process backspaces */ Xpublic int know_dumb; /* Don't complain about dumb terminals */ Xpublic int quit_at_eof; /* Quit after hitting end of file twice */ Xpublic int squeeze; /* Squeeze multiple blank lines into one */ Xpublic int tabstop; /* Tab settings */ Xpublic int back_scroll; /* Repaint screen on backwards movement */ Xpublic int twiddle; /* Display "~" for lines after EOF */ X#ifdef MSDOS Xpublic int scrn_in_color; /* When using color monitor */ X#endif X Xextern int nbufs; Xextern char *first_cmd; Xextern char *every_first_cmd; X X#define DEF_F_NBUFS 5 /* Default for f_nbufs */ X#define DEF_P_NBUFS 12 /* Default for p_nbufs */ X Xstatic struct option X{ X char oletter; /* The controlling letter (a-z) */ X char otype; /* Type of the option */ X int odefault; /* Default value */ X int *ovar; /* Pointer to the associated variable */ X char *odesc[3]; /* Description of each value */ X} option[] = X{ X { 'c', BOOL, 0, &clean_data, X { "Don't assume data is clean", X "Assume data is clean", X NULL X } X }, X { 'd', BOOL|NO_TOGGLE, 0, &know_dumb, X { NULL, NULL, NULL} X }, X { 'e', BOOL, 0, &quit_at_eof, X { "Don't quit at end-of-file", X "Quit at end-of-file", X NULL X } X }, X { 'h', NUMBER, -1, &back_scroll, X { "Backwards scroll limit is %d lines", X NULL, NULL X } X }, X { 'p', BOOL, 0, &top_scroll, X { "Repaint by scrolling from bottom of screen", X "Repaint by painting from top of screen", X NULL X } X }, X { 'x', NUMBER, 8, &tabstop, X { "Tab stops every %d spaces", X NULL, NULL X } X }, X { 's', BOOL, 0, &squeeze, X { "Don't squeeze multiple blank lines", X "Squeeze multiple blank lines", X NULL X } X }, X { 't', BOOL, 1, &top_search, X { "Forward search starts from bottom of screen", X "Forward search starts from top of screen", X NULL X } X }, X { 'w', BOOL, 1, &twiddle, X { "Display nothing for lines after end-of-file", X "Display ~ for lines after end-of-file", X NULL X } X }, X { 'm', TRIPLE, 0, &pr_type, X { "Prompt with a colon", X "Prompt with a message", X "Prompt with a verbose message" X } X }, X { 'q', TRIPLE, 0, &quiet, X { "Ring the bell for errors AND at eof/bof", X "Ring the bell for errors but not at eof/bof", X "Never ring the bell" X } X }, X { 'u', TRIPLE, 0, &bs_mode, X { "Underlined text displayed in underline mode", X "All backspaces cause overstrike", X "Backspaces print as ^H" X } X }, X#ifdef MSDOS X { 'C', BOOL|NO_TOGGLE, 0, &scrn_in_color, X { NULL, NULL, NULL} X }, X#endif X { '\0' } X}; X Xpublic char all_options[64]; /* List of all valid options */ X X/* X * Initialize each option to its default value. X */ X public void Xinit_option() X{ X register struct option *o; X register char *p; X X /* X * First do special cases, not in option table. X */ X first_cmd = every_first_cmd = NULL; X f_nbufs = DEF_F_NBUFS; /* -bf */ X p_nbufs = DEF_P_NBUFS; /* -bp */ X X p = all_options; X *p++ = 'b'; X X for (o = option; o->oletter != '\0'; o++) X { X /* X * Set each variable to its default. X * Also make a list of all options, in "all_options". X */ X *(o->ovar) = o->odefault; X *p++ = o->oletter; X if (o->otype & TRIPLE) X *p++ = toupper(o->oletter); X } X *p = '\0'; X} X X/* X * Toggle command line flags from within the program. X * Used by the "-" command. X */ X public void Xtoggle_option(c) X int c; X{ X register struct option *o; X char message[100]; X char buf[5]; X X /* X * First check for special cases not handled by the option table. X */ X switch (c) X { X case 'b': X sprintf(message, "%d buffers", nbufs); X error(message); X return; X } X X X for (o = option; o->oletter != '\0'; o++) X { X if ((o->otype & BOOL) && (o->oletter == c) && X (o->otype & NO_TOGGLE) == 0) X { X /* X * Boolean option: X * just toggle it. X */ X *(o->ovar) = ! *(o->ovar); X error(o->odesc[*(o->ovar)]); X return; X } else if ((o->otype & TRIPLE) && (o->oletter == c) && X (o->otype & NO_TOGGLE) == 0) X { X /* X * Triple-valued option with lower case letter: X * make it 1 unless already 1, then make it 0. X */ X *(o->ovar) = (*(o->ovar) == 1) ? 0 : 1; X error(o->odesc[*(o->ovar)]); X return; X } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c) && X (o->otype & NO_TOGGLE) == 0) X { X /* X * Triple-valued option with upper case letter: X * make it 2 unless already 2, then make it 0. X */ X *(o->ovar) = (*(o->ovar) == 2) ? 0 : 2; X error(o->odesc[*(o->ovar)]); X return; X } else if ((o->otype & NUMBER) && (o->oletter == c) && X (o->otype & NO_TOGGLE) == 0) X { X sprintf(message, o->odesc[0], *(o->ovar)); X error(message); X return; X } X } X X if (control_char(c)) X sprintf(buf, "^%c", carat_char(c)); X else X sprintf(buf, "%c", c); X sprintf(message, "\"-%s\": no such flag. Use one of \"%s\"", X buf, all_options); X error(message); X} X X/* X * Scan an argument (either from command line or from LESS environment X * variable) and process it. X */ X public void Xscan_option(s) X char *s; X{ X register struct option *o; X register int c; X X if (s == NULL) X return; X X next: X if (*s == '\0') X return; X switch (c = *s++) X { X case '-': X case ' ': X case '\t': X goto next; X case '+': X if (*s == '+') X every_first_cmd = ++s; X first_cmd = s; X return; X case 'b': X switch (*s) X { X case 'f': X s++; X f_nbufs = getnum(&s, 'b'); X break; X case 'p': X s++; X p_nbufs = getnum(&s, 'b'); X break; X default: X f_nbufs = p_nbufs = getnum(&s, 'b'); X break; X } X goto next; X } X X for (o = option; o->oletter != '\0'; o++) X { X if ((o->otype & BOOL) && (o->oletter == c)) X { X *(o->ovar) = ! o->odefault; X goto next; X } else if ((o->otype & TRIPLE) && (o->oletter == c)) X { X *(o->ovar) = (o->odefault == 1) ? 0 : 1; X goto next; X } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c)) X { X *(o->ovar) = (o->odefault == 2) ? 0 : 2; X goto next; X } else if ((o->otype & NUMBER) && (o->oletter == c)) X { X *(o->ovar) = getnum(&s, c); X goto next; X } X } X X printf("\"-%c\": invalid flag\n", c); X exit(1); X} X X/* X * Translate a string into a number. X * Like atoi(), but takes a pointer to a char *, and updates X * the char * to point after the translated number. X */ X static int Xgetnum(sp, c) X char **sp; X int c; X{ X register char *s; X register int n; X X s = *sp; X if (*s < '0' || *s > '9') X { X printf("number is required after -%c\n", c); X exit(1); X } X X n = 0; X while (*s >= '0' && *s <= '9') X n = 10 * n + *s++ - '0'; X *sp = s; X return (n); X} X SHAR_EOF if test 7572 -ne "`wc -c < 'option.c'`" then echo shar: error transmitting "'option.c'" '(should have been 7572 characters)' fi fi if test -f 'output.c' then echo shar: will not over-write existing file "'output.c'" else echo extracting "'output.c'" sed 's/^X//' >output.c <<'SHAR_EOF' X/* X * High level routines dealing with the output to the screen. X */ X X#include "less.h" X#ifdef MSDOS X#include "scrn.h" X#endif X Xextern int sigs; Xextern int sc_width, sc_height; Xextern int ul_width, ue_width; Xextern int so_width, se_width; Xextern int bo_width, be_width; Xextern int tabstop; Xextern int twiddle; Xextern char *line; Xextern char *first_cmd; X#ifdef MSDOS Xextern int scrn_in_color; X#endif X X/* X * Display the line which is in the line buffer. X */ X public void Xput_line() X{ X register char *p; X register int c; X register int column; X extern int auto_wrap, ignaw; X X if (sigs) X /* X * Don't output if a signal is pending. X */ X return; X X if (line == NULL) X line = (twiddle) ? "~" : ""; X X column = 0; X for (p = line; *p != '\0'; p++) X { X switch (c = *p) X { X case UL_CHAR: X ul_enter(); X column += ul_width; X break; X case UE_CHAR: X ul_exit(); X column += ue_width; X break; X case BO_CHAR: X bo_enter(); X column += bo_width; X break; X case BE_CHAR: X bo_exit(); X column += be_width; X break; X X case '\t': X do X { X putc(' '); X column++; X } while ((column % tabstop) != 0); X break; X case '\b': X putbs(); X column--; X break; X default: X if (c & 0200) X { X#ifndef MSDOS X putc('^'); X putc(c & 0177); X column += 2; X#endif X } else X { X putc(c); X column++; X } X } X } X if (column < sc_width || !auto_wrap || ignaw) X putc('\n'); X} X X/* X * Is a given character a "control" character? X * {{ ASCII DEPENDENT }} X */ X public int Xcontrol_char(c) X int c; X{ X return (c < ' ' || c == '\177'); X} X X/* X * Return the printable character used to identify a control character X * (printed after a carat; e.g. '\3' => "^C"). X * {{ ASCII DEPENDENT }} X */ X public int Xcarat_char(c) X int c; X{ X return ((c == '\177') ? '?' : (c | 0100)); X} X X Xstatic char obuf[1024]; Xstatic char *ob = obuf; X X/* X * Flush buffered output. X */ X public void Xflush() X{ X#ifndef MSDOS X write(1, obuf, ob-obuf); X ob = obuf; X#else X int chr_cnt; X int i; X X chr_cnt = ob-obuf; X i = 0; X do X { X if (scrn_in_color == 1) X chr_put(*(obuf + i++), WHITE_ON_BLUE); X else X chr_put(*(obuf + i++), BW); X --chr_cnt; X } X while (chr_cnt > 0); X ob = obuf; X#endif X} X X/* X * Discard buffered output. X */ X public void Xdropout() X{ X ob = obuf; X} X X/* X * Output a character. X */ X public void Xputc(c) X int c; X{ X if (ob >= &obuf[sizeof(obuf)]) X flush(); X *ob++ = c; X#ifdef MSDOS X flush(); X#endif X} X X/* X * Output a string. X */ X public void Xputs(s) X register char *s; X{ X#ifndef MSDOS X while (*s != '\0') X putc(*s++); X#else X str_put(s, WHITE_ON_RED); X#endif X} X X/* X * Output a message in the lower left corner of the screen X * and wait for carriage return. X */ X Xstatic char return_to_continue[] = " (press RETURN)"; X X public void Xerror(s) X char *s; X{ X register int c; X static char buf[2]; X X lower_left(); X clear_eol(); X so_enter(); X#ifndef MSDOS X puts(s); X puts(return_to_continue); X#else X str_put(s, WHITE_ON_RED); X str_put(return_to_continue, WHITE_ON_RED); X#endif X so_exit(); X X#if ONLY_RETURN X while ((c = getc()) != '\n' && c != '\r') X bell(); X#else X c = getc(); X if (c != '\n' && c != '\r' && c != ' ') X { X buf[0] = c; X first_cmd = buf; X } X#endif X X if (strlen(s) > sc_width) X repaint(); X} X X public int Xerror_width() X{ X /* X * Don't use the last position, because some terminals X * will scroll if you write in the last char of the last line. X */ X return (sc_width - X (sizeof(return_to_continue) + so_width + se_width + 1)); X} X SHAR_EOF if test 3436 -ne "`wc -c < 'output.c'`" then echo shar: error transmitting "'output.c'" '(should have been 3436 characters)' fi fi if test -f 'position.c' then echo shar: will not over-write existing file "'position.c'" else echo extracting "'position.c'" sed 's/^X//' >position.c <<'SHAR_EOF' X/* X * Routines dealing with the "position" table. X * This is a table which tells the position (in the input file) of the X * first char on each currently displayed line. X * X * {{ The position table is scrolled by moving all the entries. X * Would be better to have a circular table X * and just change a couple of pointers. }} X */ X X#include "less.h" X#include "position.h" X X#define NPOS 100 /* {{ sc_height must be less than NPOS }} */ Xstatic POSITION table[NPOS]; /* The position table */ X Xextern int sc_width, sc_height; X X/* X * Return the position of one of: X * the top (first) line on the screen X * the second line on the screen X * the bottom line on the screen X * the line after the bottom line on the screen X */ X public POSITION Xposition(where) X int where; X{ X switch (where) X { X case BOTTOM: X where = sc_height - 2; X break; X case BOTTOM_PLUS_ONE: X where = sc_height - 1; X break; X } X return (table[where]); X} X X/* X * Add a new file position to the bottom of the position table. X */ X public void Xadd_forw_pos(pos) X POSITION pos; X{ X register int i; X X /* X * Scroll the position table up. X */ X for (i = 1; i < sc_height; i++) X table[i-1] = table[i]; X table[sc_height - 1] = pos; X} X X/* X * Add a new file position to the top of the position table. X */ X public void Xadd_back_pos(pos) X POSITION pos; X{ X register int i; X X /* X * Scroll the position table down. X */ X for (i = sc_height - 1; i > 0; i--) X table[i] = table[i-1]; X table[0] = pos; X} X X/* X * Initialize the position table, done whenever we clear the screen. X */ X public void Xpos_clear() X{ X register int i; X X for (i = 0; i < sc_height; i++) X table[i] = NULL_POSITION; X} X X/* X * See if the byte at a specified position is currently on the screen. X * Check the position table to see if the position falls within its range. X * Return the position table entry if found, -1 if not. X */ X public int Xonscreen(pos) X POSITION pos; X{ X register int i; X X if (pos < table[0]) X return (-1); X for (i = 1; i < sc_height; i++) X if (pos < table[i]) X return (i-1); X return (-1); X} SHAR_EOF if test 2040 -ne "`wc -c < 'position.c'`" then echo shar: error transmitting "'position.c'" '(should have been 2040 characters)' fi fi if test -f 'position.h' then echo shar: will not over-write existing file "'position.h'" else echo extracting "'position.h'" sed 's/^X//' >position.h <<'SHAR_EOF' X/* X * Include file for interfacing to position.c modules. X */ X#define TOP 0 X#define TOP_PLUS_ONE 1 X#define BOTTOM -1 X#define BOTTOM_PLUS_ONE -2 SHAR_EOF if test 146 -ne "`wc -c < 'position.h'`" then echo shar: error transmitting "'position.h'" '(should have been 146 characters)' fi fi if test -f 'prim.c' then echo shar: will not over-write existing file "'prim.c'" else echo extracting "'prim.c'" sed 's/^X//' >prim.c <<'SHAR_EOF' X/* X * Primitives for displaying the file on the screen. X */ X X#include "less.h" X#include "position.h" X Xpublic int hit_eof; /* Keeps track of how many times we hit end of file */ X Xextern int quiet; Xextern int top_search; Xextern int top_scroll; Xextern int back_scroll; Xextern int sc_width, sc_height; Xextern int sigs; Xextern char *line; Xextern char *first_cmd; X X/* X * Sound the bell to indicate he is trying to move past end of file. X */ X static void Xeof_bell() X{ X if (quiet == NOT_QUIET) X bell(); X else X vbell(); X} X X/* X * Check to see if the end of file is currently "displayed". X */ X static void Xeof_check() X{ X POSITION pos; X X /* X * If the bottom line is empty, we are at EOF. X * If the bottom line ends at the file length, X * we must be just at EOF. X */ X pos = position(BOTTOM_PLUS_ONE); X if (pos == NULL_POSITION || pos == ch_length()) X hit_eof++; X} X X/* X * Display n lines, scrolling forward, X * starting at position pos in the input file. X * "force" means display the n lines even if we hit end of file. X * "only_last" means display only the last screenful if n > screen size. X */ X static void Xforw(n, pos, force, only_last) X register int n; X POSITION pos; X int force; X int only_last; X{ X int eof = 0; X int nlines = 0; X int repaint_flag; X X /* X * repaint_flag tells us not to display anything till the end, X * then just repaint the entire screen. X */ X repaint_flag = (only_last && n > sc_height-1); X X if (!repaint_flag) X { X if (top_scroll && n >= sc_height - 1) X { X /* X * Start a new screen. X * {{ This is not really desirable if we happen X * to hit eof in the middle of this screen, X * but we don't know if that will happen now. }} X */ X clear(); X home(); X force = 1; X } else X { X lower_left(); X clear_eol(); X } X X if (pos != position(BOTTOM_PLUS_ONE)) X { X /* X * This is not contiguous with what is X * currently displayed. Clear the screen image X * (position table) and start a new screen. X */ X pos_clear(); X add_forw_pos(pos); X force = 1; X if (top_scroll) X { X clear(); X home(); X } else X { X puts("...skipping...\n"); X } X } X } X X while (--n >= 0) X { X /* X * Read the next line of input. X */ X pos = forw_line(pos); X if (pos == NULL_POSITION) X { X /* X * End of file: stop here unless the top line X * is still empty, or "force" is true. X */ X eof = 1; X if (!force && position(TOP) != NULL_POSITION) X break; X line = NULL; X } X /* X * Add the position of the next line to the position table. X * Display the current line on the screen. X */ X add_forw_pos(pos); X nlines++; X if (!repaint_flag) X put_line(); X } X X if (eof) X hit_eof++; X else X eof_check(); X if (nlines == 0) X eof_bell(); X else if (repaint_flag) X repaint(); X} X X/* X * Display n lines, scrolling backward. X */ X static void Xback(n, pos, force, only_last) X register int n; X POSITION pos; X int force; X int only_last; X{ X int nlines = 0; X int repaint_flag; X X repaint_flag = (n > back_scroll || (only_last && n > sc_height-1)); X hit_eof = 0; X while (--n >= 0) X { X /* X * Get the previous line of input. X */ X pos = back_line(pos); X if (pos == NULL_POSITION) X { X /* X * Beginning of file: stop here unless "force" is true. X */ X if (!force) X break; X line = NULL; X } X /* X * Add the position of the previous line to the position table. X * Display the line on the screen. X */ X add_back_pos(pos); X nlines++; X if (!repaint_flag) X { X home(); X add_line(); X put_line(); X } X } X X eof_check(); X if (nlines == 0) X eof_bell(); X else if (repaint_flag) X repaint(); X} X X X /* X Find the current line number X */ X X public int curln() X { X int n; X POSITION pos; X n=0; X pos = position(TOP); X while (pos!=NULL_POSITION) X { X pos = back_line(pos); X n++; X } X return(n); X } X X X X/* X * Display n more lines, forward. X * Start just after the line currently displayed at the bottom of the screen. X */ X public void Xforward(n, only_last) X int n; X int only_last; X{ X POSITION pos; X X pos = position(BOTTOM_PLUS_ONE); X if (pos == NULL_POSITION) X { X eof_bell(); X hit_eof++; X return; X } X forw(n, pos, 0, only_last); X} X X/* X * Display n more lines, backward. X * Start just before the line currently displayed at the top of the screen. X */ X public void Xbackward(n, only_last) X int n; X int only_last; X{ X POSITION pos; X X pos = position(TOP); X if (pos == NULL_POSITION) X { X /* X * This will almost never happen, X * because the top line is almost never empty. X */ X eof_bell(); X return; X } X back(n, pos, 0, only_last); X} X X/* X * Repaint the screen, starting from a specified position. X */ X static void Xprepaint(pos) X POSITION pos; X{ X hit_eof = 0; X forw(sc_height-1, pos, 0, 0); X} X X/* X * Repaint the screen. X */ X public void Xrepaint() X{ X /* X * Start at the line currently at the top of the screen X * and redisplay the screen. X */ X prepaint(position(TOP)); X} X X/* X * Jump to the end of the file. X * It is more convenient to paint the screen backward, X * from the end of the file toward the beginning. X */ X public void Xjump_forw() X{ X POSITION pos; X X if (ch_end_seek()) X { X error("Cannot seek to end of file"); X return; X } X pos = ch_tell(); X clear(); X pos_clear(); X add_back_pos(pos); X back(sc_height - 1, pos, 0, 0); X} X X/* X * Jump to line n in the file. X */ X public void Xjump_back(n) X register int n; X{ X register int c; X X /* X * This is done the slow way, by starting at the beginning X * of the file and counting newlines. X */ X if (ch_seek((POSITION)0)) X { X /* X * Probably a pipe with beginning of file no longer buffered. X */ X error("Cannot get to beginning of file"); X return; X } X X /* X * Start counting lines. X */ X while (--n > 0) X { X while ((c = ch_forw_get()) != '\n') X if (c == EOF) X { X error("File is not that long"); X /* {{ Maybe tell him how long it is? }} */ X return; X } X } X X /* X * Finally found the place to start. X * Clear and redisplay the screen from there. X * X * {{ We *could* figure out if the new position is X * close enough to just scroll there without clearing X * the screen, but it's not worth it. }} X */ X prepaint(ch_tell()); X} X X/* X * Jump to a specified percentage into the file. X * This is a poor compensation for not being able to X * quickly jump to a specific line number. X */ X public void Xjump_percent(percent) X int percent; X{ X POSITION pos, len; X X /* X * Determine the position in the file X * (the specified percentage of the file's length). X */ X if ((len = ch_length()) == NULL_POSITION) X { X error("Don't know length of file"); X return; X } X pos = (percent * len) / 100; X jump_loc(pos); X} X X public void Xjump_loc(pos) X POSITION pos; X{ X register int c; X register int nline; X POSITION tpos; X X /* X * See if the desired line is BEFORE the currently X * displayed screen. If so, see if it is close enough X * to scroll backwards to it. X */ X tpos = position(TOP); X if (pos < tpos) X { X for (nline = 1; nline <= back_scroll; nline++) X { X tpos = back_line(tpos); X if (tpos == NULL_POSITION || tpos <= pos) X { X back(nline, position(TOP), 1, 0); X return; X } X } X } else if ((nline = onscreen(pos)) >= 0) X { X /* X * The line is currently displayed. X * Just scroll there. X */ X forw(nline, position(BOTTOM_PLUS_ONE), 1, 0); X return; X } X X /* X * Line is not on screen. X * Back up to the beginning of the current line. X */ X if (ch_seek(pos)) X { X error("Cannot seek to that position"); X return; X } X while ((c = ch_back_get()) != '\n' && c != EOF) X ; X if (c == '\n') X (void) ch_forw_get(); X X /* X * Clear and paint the screen. X */ X prepaint(ch_tell()); X} X X/* X * The table of marks. X * A mark is simply a position in the file. X */ Xstatic POSITION marks[26]; X X/* X * Initialize the mark table to show no marks are set. X */ X public void Xinit_mark() X{ X int i; X X for (i = 0; i < 26; i++) X marks[i] = NULL_POSITION; X} X X/* X * See if a mark letter is valid (between a and z). X */ X static int Xbadmark(c) X int c; X{ X if (c < 'a' || c > 'z') X { X error("Choose a letter between 'a' and 'z'"); X return (1); X } X return (0); X} X X/* X * Set a mark. X */ X public void Xsetmark(c) X int c; X{ X if (badmark(c)) X return; X marks[c-'a'] = position(TOP); X} X X/* X * Go to a previously set mark. X */ X public void Xgomark(c) X int c; X{ X POSITION pos; X X if (badmark(c)) X return; X if ((pos = marks[c-'a']) == NULL_POSITION) X error("mark not set"); X else X jump_loc(pos); X} X X/* X * Search for the n-th occurence of a specified pattern, X * either forward (direction == '/'), or backwards (direction == '?'). X */ X public void Xsearch(direction, pattern, n) X int direction; X char *pattern; X register int n; X{ X register int search_forward = (direction == '/'); X POSITION pos, linepos; X X#if RECOMP X char *re_comp(); X char *errmsg; X X /* X * (re_comp handles a null pattern internally, X * so there is no need to check for a null pattern here.) X */ X if ((errmsg = re_comp(pattern)) != NULL) X { X error(errmsg); X return; X } X#else X#if REGCMP | REGEXP X#if REGEXP /* Use Harry Spencer's regexpression source */ X char *regcomp(); X#else X char *regcmp(); X#endif X static char *cpattern = NULL; X X if (pattern == NULL || *pattern == '\0') X { X /* X * A null pattern means use the previous pattern. X * The compiled previous pattern is in cpattern, so just use it. X */ X if (cpattern == NULL) X { X error("No previous regular expression"); X return; X } X } else X { X /* X * Otherwise compile the given pattern. X */ X char *s; X#if REGEXP X if ((s = regcomp(pattern, 0)) == NULL) X#else X if ((s = regcmp(pattern, 0)) == NULL) X#endif X { X error("Invalid pattern"); X return; X } X if (cpattern != NULL) X free(cpattern); X cpattern = s; X } X#else X static char lpbuf[100]; X static char *last_pattern = NULL; X X if (pattern == NULL || *pattern == '\0') X { X /* X * Null pattern means use the previous pattern. X */ X if (last_pattern == NULL) X { X error("No previous regular expression"); X return; X } X pattern = last_pattern; X } else X { X strcpy(lpbuf, pattern); X last_pattern = lpbuf; X } X#endif X#endif X X /* X * Figure out where to start the search. X */ X X if (position(TOP) == NULL_POSITION) X { X /* X * Nothing is currently displayed. X * Start at the beginning of the file. X * (This case is mainly for first_cmd searches, X * for example, "+/xyz" on the command line.) X */ X pos = (POSITION)0; X } else if (!search_forward) X { X /* X * Backward search: start just before the top line X * displayed on the screen. X */ X pos = position(TOP); X } else if (top_search) X { X /* X * Forward search and "start from top". X * Start at the second line displayed on the screen. X */ X pos = position(TOP_PLUS_ONE); X } else X { X /* X * Forward search but don't "start from top". X * Start just after the bottom line displayed on the screen. X */ X pos = position(BOTTOM_PLUS_ONE); X } X X if (pos == NULL_POSITION) X { X /* X * Can't find anyplace to start searching from. X */ X error("Nothing to search"); X return; X } X X for (;;) X { X /* X * Get lines until we find a matching one or X * until we hit end-of-file (or beginning-of-file X * if we're going backwards). X */ X if (sigs) X /* X * A signal aborts the search. X */ X return; X X if (search_forward) X { X /* X * Read the next line, and save the X * starting position of that line in linepos. X */ X linepos = pos; X pos = forw_raw_line(pos); X } else X { X /* X * Read the previous line and save the X * starting position of that line in linepos. X */ X pos = back_raw_line(pos); X linepos = pos; X } X X if (pos == NULL_POSITION) X { X /* X * We hit EOF/BOF without a match. X */ X error("Pattern not found"); X return; X } X X /* X * Test the next line to see if we have a match. X * This is done in a variety of ways, depending X * on what pattern matching functions are available. X */ X#if REGCMP | REGEXP X#if REGEXP X if ( (regexec(cpattern, line) != NULL) X#else X if ( (regex(cpattern, line) != NULL) X#endif X#else X#if RECOMP X if ( (re_exec(line) == 1) X#else X if ( (match(pattern, line)) X#endif X#endif X && (--n <= 0) ) X /* X * Found the matching line. X */ X break; X } X jump_loc(linepos); X} X X#if (!REGCMP) && (!RECOMP) X/* X * We have neither regcmp() nor re_comp(). X * We use this function to do simple pattern matching. X * It supports no metacharacters like *, etc. X */ X static int Xmatch(pattern, buf) X char *pattern, *buf; X{ X register char *pp, *lp; X X for ( ; *buf != '\0'; buf++) X { X for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) X if (*pp == '\0' || *lp == '\0') X break; X if (*pp == '\0') X return (1); X } X return (0); X} X#endif SHAR_EOF if test 12563 -ne "`wc -c < 'prim.c'`" then echo shar: error transmitting "'prim.c'" '(should have been 12563 characters)' fi fi if test -f 'prompt.c' then echo shar: will not over-write existing file "'prompt.c'" else echo extracting "'prompt.c'" sed 's/^X//' >prompt.c <<'SHAR_EOF' X/* X * Prompting and other messages. X * There are three flavors of prompts, SHORT, MEDIUM and LONG, X * selected by the -m/-M options. X * A prompt is either a colon or a message composed of various X * pieces, such as the name of the file being viewed, the percentage X * into the file, etc. X */ X X#include "less.h" X#include "position.h" X Xextern int pr_type; Xextern int ispipe; Xextern int hit_eof; Xextern int new_file; Xextern int sc_width; Xextern char current_file[]; Xextern int ac; Xextern char **av; Xextern int curr_ac; X Xstatic char message[500]; X X/* X * Append the name of the current file (to the message buffer). X */ X static void Xap_filename() X{ X if (!ispipe) X sprintf(message + strlen(message), X "%s", current_file); X} X X/* X * Append the "file N of M" message. X */ X static void Xap_of() X{ X if (ac > 1) X sprintf(message + strlen(message), X " (file %d of %d)", curr_ac+1, ac); X} X X/* Append the line number */ X X static void Xap_ln() X{ X sprintf(message + strlen(message), X " Line %d,", curln()); X} X X/* X * Append the byte offset into the current file. X */ X static void Xap_byte() X{ X POSITION pos, len; X X pos = position(BOTTOM_PLUS_ONE); X if (pos != NULL_POSITION) X { X sprintf(message + strlen(message), X " byte %ld", pos); X len = ch_length(); X if (len > 0) X sprintf(message + strlen(message), X "/%ld", len); X } X} X X/* X * Append the percentage into the current file. X * If we cannot find the percentage and must_print is true, X * the use the byte offset. X */ X static void Xap_percent(must_print) X{ X POSITION pos,len; X X pos = position(BOTTOM_PLUS_ONE); X len = ch_length(); X if (len > 0 && pos != NULL_POSITION) X sprintf(message + strlen(message), X " (%ld%%)", (100 * pos) / len); X else if (must_print) X ap_byte(); X} X X/* X * Append the end-of-file message. X */ X static void Xap_eof() X{ X strcat(message, " END"); X if (curr_ac + 1 < ac) X sprintf(message + strlen(message), X " - Next: %s", av[curr_ac+1]); X} X X/* X * Return a message suitable for printing by the "l" command. X */ X public char * X Xeq_mess2() X{ X message[0] = '\0'; X ap_filename(); X ap_ln(); X ap_of(); X ap_byte(); X ap_percent(0); X /* X * Truncate to the screen width. X * {{ This isn't very nice. }} X */ X message[error_width()] = '\0'; X return (message); X} X/* X * Return a message suitable for printing by the "=" command. X */ X public char * X Xeq_message() X{ X message[0] = '\0'; X ap_filename(); X ap_of(); X ap_byte(); X ap_percent(0); X /* X * Truncate to the screen width. X * {{ This isn't very nice. }} X */ X message[error_width()] = '\0'; X return (message); X} X X/* X * Return a prompt. X * This depends on the prompt type (SHORT, MEDIUM, LONG), etc. X * If we can't come up with an appropriate prompt, return NULL X * and the caller will prompt with a colon. X */ X public char * Xpr_string() X{ X message[0] = '\0'; X switch (pr_type) X { X case PR_SHORT: X if (new_file) X { X ap_filename(); X ap_of(); X } X if (hit_eof) X ap_eof(); X break; X case PR_MEDIUM: X if (new_file) X { X ap_filename(); X ap_of(); X } X if (hit_eof) X ap_eof(); X else X ap_percent(1); X break; X case PR_LONG: X ap_filename(); X if (new_file) X ap_of(); X ap_byte(); X if (hit_eof) X ap_eof(); X else X ap_percent(0); X break; X } X new_file = 0; X if (message[0] == '\0') X return (NULL); X /* X * Truncate to the screen width. X * {{ This isn't very nice. }} X */ X message[sc_width-2] = '\0'; X return (message); X} SHAR_EOF if test 3358 -ne "`wc -c < 'prompt.c'`" then echo shar: error transmitting "'prompt.c'" '(should have been 3358 characters)' fi fi if test -f 'ttyin.c' then echo shar: will not over-write existing file "'ttyin.c'" else echo extracting "'ttyin.c'" sed 's/^X//' >ttyin.c <<'SHAR_EOF' X/* X * Routines dealing with getting input from the keyboard (i.e. from the user). X */ X X#include "less.h" X X/* X * The boolean "reading" is set true or false according to whether X * we are currently reading from the keyboard. X * This information is used by the signal handling stuff in signal.c. X * {{ There are probably some race conditions here X * involving the variable "reading". }} X */ Xpublic int reading; X Xstatic int tty; X X/* X * Open keyboard for input. X * (Just use file descriptor 2.) X */ X public void Xopen_getc() X{ X tty = 2; X} X X/* X * Get a character from the keyboard. X */ X public int Xgetc() X{ X#if MSDOS X#if MSC X int c; X struct regs { X int ax, bx, cx, dx, si, di, ds, es; X } cregs, rregs; X int intno = 0x016; X#else X char c; X#endif X#endif X int result; X X reading = 1; X#if MSDOS X#if MSC X cregs.ax = 0x0000; /* set registers */ X int86(0x16, &cregs, &cregs); /* call BIOS - INT 16h */ X c = (cregs.ax & 0x00ff); X reading = 0; X return(c & 0177); X#else X do X { X flush(); X result = read(tty, &c, 1); X } while (result != 1); X reading = 0; X return (c & 0177); X#endif X#endif X} X SHAR_EOF if test 1080 -ne "`wc -c < 'ttyin.c'`" then echo shar: error transmitting "'ttyin.c'" '(should have been 1080 characters)' fi fi if test -f 'version.c' then echo shar: will not over-write existing file "'version.c'" else echo extracting "'version.c'" sed 's/^X//' >version.c <<'SHAR_EOF' X/* X * less X * Copyright (c) 1984,1985 Mark Nudelman X * X * This program may be freely used and/or modified, X * with the following provisions: X * 1. This notice and the above copyright notice must remain intact. X * 2. Neither this program, nor any modification of it, X * may not be sold for profit without written consent of the author. X * X * ----------------------------------------------------------------- X * X * This program is a paginator similar to "more", X * but allows you to move both forward and backward in the file. X * Commands are based on "more" and "vi". X * X * ----------------------- CHANGES --------------------------------- X * X * Allowed use on standard input 1/29/84 markn X * Added E, N, P commands 2/1/84 markn X * Added '=' command, 'stop' signal handling 4/17/84 markn X * Added line folding 4/20/84 markn X * v2: Fixed '=' command to use BOTTOM_PLUS_ONE, X * instead of TOP, added 'p' & 'v' commands 4/27/84 markn X * v3: Added -m and -t options, '-' command 5/3/84 markn X * v4: Added LESS environment variable 5/3/84 markn X * v5: New comments, fixed '-' command slightly 5/3/84 markn X * v6: Added -Q, visual bell 5/15/84 markn X * v7: Fixed jump_back(n) bug: n should count real X * lines, not folded lines. Also allow number X * on G command. 5/24/84 markn X * v8: Re-do -q and -Q commands 5/30/84 markn X * v9: Added "+<cmd>" argument 9/25/84 markn X * v10: Fixed bug in -b<n> argument processing 10/10/84 markn X * v11: Made error() ring bell if \n not entered. 10/18/84 markn X * ----------------------------------------------------------------- X * v12: Reorganized signal handling and made X * portable to 4.2bsd. 2/13/85 mark X * v13: Reword error message for '-' command. 2/16/85 mark X * v14: Added -bf and -bp variants of -b. 2/22/85 mark X * v15: Miscellaneous changes. 2/25/85 mark X * v16: Added -u flag for backspace processing. 3/13/85 mark X * v17: Added j and k commands, X * changed -t default. 4/13/85 mark X * v18: Rewrote signal handling code. 4/20/85 mark X * v19: Got rid of "verbose" eq_message(). 5/2/85 mark X * Made search() scroll in some cases. X * v20: Fixed screen.c ioctls for System V. 5/21/85 mark X * v21: Fixed some first_cmd bugs. 5/23/85 mark X * v22: Added support for no RECOMP nor REGCMP. 5/24/85 mark X * v23: Miscellanous changes and prettying up. 5/25/85 mark X * v24: Added ti,te terminal init & de-init 6/3/85 Mike Kersenbrock X * v25: Added -U flag, standout mode underlining. 6/8/85 mark X * v26: Added -M flag. 6/9/85 mark X * Use underline termcap (us) if it exists. X * v27: Renamed some variables to make unique in 6/15/85 mark X * 6 chars. Minor fix to -m. X * v28: Fixed right margin bug. 6/28/85 mark X * v29: Incorporated M.Rose's changes to signal.c 6/28/85 mark X * v30: Fixed stupid bug in argument processing. 6/29/85 mark X * v31: Added -p flag, changed repaint algorithm. 7/15/85 mark X * Added kludge for magic cookie terminals. X * v32: Added cat_file if output not a tty. 7/16/85 mark X * v33: Added -e flag and EDITOR. 7/23/85 mark X * v34: Added -s flag. 7/26/85 mark X * v35: Rewrote option handling; added option.c. 7/27/85 mark X * v36: Fixed -e flag to work if not last file. 7/29/85 mark X * v37: Added -x flag. 8/10/85 mark X * v38: Changed prompting; created prompt.c. 8/19/85 mark X * v39: (Not -p) does not initially clear screen. 8/24/85 mark X * v40: Added "skipping" indicator in forw(). 8/26/85 mark X * v41: ONLY_RETURN, control char commands, 9/17/85 mark X * faster search, other minor fixes. X * v42: Added ++ command line syntax; 9/25/85 mark X * ch_fsize for pipes. X * v43: Added -h flag, changed prim.c algorithms. 10/15/85 mark X * v44: Made END print in all cases of eof; 10/16/85 mark X * ignore SIGTTOU after receiving SIGTSTP. X * v45: Never print backspaces unless -u. 10/16/85 mark X * v46: Backwards scroll in jump_loc. 10/24/85 mark X * v47: Fixed bug in edit(): *first_cmd==0 10/30/85 mark X * v48: Use TIOCSETN instead of TIOCSETP. 11/16/85 mark X * Added marks (m and ' commands). X * v48+: Added 'L' command to show number of line 7/23/87 m.slomin X * at top of the screen. X * ----------------------------------------------------------------- X */ X Xchar version[] = "@(#) less version 48+/MS-DOS"; SHAR_EOF if test 4414 -ne "`wc -c < 'version.c'`" then echo shar: error transmitting "'version.c'" '(should have been 4414 characters)' fi fi # end of shell archive exit 0 --- No warranties whatsoever. Mike Slomin !bellcore!lcuxa!mike2
mike2@lcuxa.UUCP (M S Slomin) (04/23/88)
---------------------------Part 3 of 3------------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file # 3. Execute the file with /bin/sh (not csh) to create the files: # # readme # regerror.c # regexp.c # regexp.h # regmagic.h # scrn.c # scrn.h # signal.c # # Created on 'date' # if test -f 'readme' then echo shar: will not over-write existing file "'readme'" else echo extracting "'readme'" sed 's/^X//' >readme <<'SHAR_EOF' XPort of less to msdos: X XLess is a program similar to the Unix more command but is much more Xuseful. Read less.man for a good description of less and what it can Xdo for you. X XA. Dick Keily's original porting information for MSC 4.0: X XThe files included here will result in a full version of less Xfor use on msdos. This version of less has been tested on all the pc Xsystems that I could get my hands on. This includes monochrome, graphics, X(that includes systems with graphics cards and mono monitors), and color Xgraphics. X XThe following must be done to have less.exe work in its full glory: X X 1. Nothing if you don't want color when using a color monitor. X To get the color use the set command such as: X X set less=C (Case sensitive; c is another option) X X Do this prior to calling less. A command line option X less -C <filename ...> will also get you color. X X NOTE: Only use the color option when using a color monitor. X All other configurations need no special action. X X There are other less settings that can be used using the X set command. Read less.man. The one I use is less=mC on X an AT with enhanced monitor and ega card. X X 2. Less also has a v command that lets you can an editor from X within less to edit the file that you are viewing at the X time. The default is memacs which was taken from the Unix X mod.sources. If you don't have it use the dos set command X as follows to let less know which it is: X X set editor=your_editor X X 4. It is best to include the set commands in your autoexec.bat X which will be executed when you boot up your system. X X 5. Less is a very useful utility. Read less.man and when in X less the h command will put a description of the commands X on the screen for you. X X 6. This version of less is named less13.exe for distribution. X It replaces and other version that you have obtained from X my posting of the executable. Accept no substitutes. When X I get the time I will incorporate the changes to get up X the distributed Unix version 61. If you post this to any X bulletin board name any files associated with it with the X version 13.(referring to the MSDOS control). X X [N.B. No longer correct. This version is X somewhere between version 61 and 73 in its X capabilities.] X X 7. Less version 1.3 uses the system function but looks to see X if you are using the swchar program that allows you to use X the / rather than the \ character. The swchar state is changed X before the system call if you are and then resets it so that X it all becomes transparent to the user. X X [N.B. This is still correct.] X X 8. The source code for less is being posted to Net.sources. X It is all ifdef'd so that it should compile on Unix systems X also as is. X X [N.B. Probably not anymore. The ctrlbrk() X function will fail on Unix.] X X 9. Version 1.3 corrects the following: X The latest known to me fixes have been incorporated X in the regexpression routines written by Henry X Spencer and posted on the Unix news net. X X Dick Keily X Eastman Kodak Company X work: (716)477-1001 X unix: ...!seismo!rochester!kodak!keily X--------------------------------------------------------------------------- X--------------------------------------------------------------------------- X XB. Revised port to TurboC: X X 1. The port now incorporates boldface and underline (useful X for viewing documents created with nroff -man). I've X chosen the monochrome attributes for these, 0x01 and 0xf0, X and for CGA color, 0x06 (yellow) and 0xf0 (bright white). X If you wish to use others, revise the SCRN.H and/or SCRN.C X file(s) appropriately. X X 2. This will compile in the "tiny" model, and the resulting X file can be converted to a '.com' file for loading and X execution speed. An appropriate 'LESS.PRJ' file is X included; be certain to define DOSPORT in 'Options/Compiler' X (interactive mode). X X 3. A bug was fixed in the 'v' command (escape X to the editor): on return to less, the original unmodified X file in memory was displayed, not the edited one. This X is worked around in command.c by causing an 'E\n' command X to be executed on return. 'E' (Examine) loads in a new X file, and if no filename is given it loads in the previous X file. (Indeed, this is useful if 'E' is used normally. X After examining a different file, you can return to the X previous one by invoking 'E' with no file name.) X X 4. A new command, 'L', is implemented. 'L' gives X all of the information that '^G' and '=' give (i.e., X file name, number of bytes, approximate percentage into X the file) and in addition gives the number of the first X line appearing on the screen. This information is useful X if you wish to edit the file subsequently with a line- X oriented editor (e.g., vi, EDLIN, etc.); you know what X line to jump to. Unfortunately, it is a bit slow because X long ints are used in the calculation -- but a file might X exceed 65,000 lines!. X X 5. Sorry, I've not bothered to maintain downward compatibility X with Mr. Keily's MSC 4.0 port. I tried it both ways, and the X TurboC one is smaller and faster. If you want to do it with X MSC 4.0: (1) replace the ctrlbrk() command with an appropriate X signal() and interrupt handler; (2) compile it in the small X model; and (3) link with a /stack:4096 flag, linking in the X ssetargv.obj (small model) object file supplied by Microsoft X instead of the included setargv.c file. X X 6. If you haven't updated to TurboC 1.5, read the comments in X SETARGV.C. The variables _argc and _argv must be changed X throughout that file to __argc and __argv to work properly X in version 1.0. In any event, the SETARGV function is X needed only for DOS wildcard expansion, which is useful if X you want to specify multiple files for viewing with wildcards X and flip back and forth among them with 'N' and 'P'. If X you don't need the wildcard capability, omit SETARGV. X X 7. The public-domain REGEXP.C file supplied by Dick Keily X implements full regular expression matching a la X 'egrep' (i.e., using the '+' for one or more matches and X the '|' for disjunctive matches) in addition to the more X limited 'ed'/'sed' pattern match metacharacters. This is X useful, and better than classical Unix REGEXP. X X Mike Slomin X !bellcore!lcuxa!mike2 X X X SHAR_EOF if test 6263 -ne "`wc -c < 'readme'`" then echo shar: error transmitting "'readme'" '(should have been 6263 characters)' fi fi if test -f 'regerror.c' then echo shar: will not over-write existing file "'regerror.c'" else echo extracting "'regerror.c'" sed 's/^X//' >regerror.c <<'SHAR_EOF' X#include <stdio.h> X Xvoid Xregerror(s) Xchar *s; X{ X#ifndef DOSPORT X#ifdef ERRAVAIL X error("regexp: %s", s); X#else X fprintf(stderr, "regexp(3): %s", s); X exit(1); X#endif X /* NOTREACHED */ X#endif /* ifdef'd out for less's sake when reporting error inside less */ X} SHAR_EOF if test 260 -ne "`wc -c < 'regerror.c'`" then echo shar: error transmitting "'regerror.c'" '(should have been 260 characters)' fi fi if test -f 'regexp.c' then echo shar: will not over-write existing file "'regexp.c'" else echo extracting "'regexp.c'" sed 's/^X//' >regexp.c <<'SHAR_EOF' X/* X * regcomp and regexec -- regsub and regerror are elsewhere X * X * Copyright (c) 1986 by University of Toronto. X * Written by Henry Spencer. Not derived from licensed software. X * X * Permission is granted to anyone to use this software for any X * purpose on any computer system, and to redistribute it freely, X * subject to the following restrictions: X * X * 1. The author is not responsible for the consequences of use of X * this software, no matter how awful, even if they arise X * from defects in it. X * X * 2. The origin of this software must not be misrepresented, either X * by explicit claim or by omission. X * X * 3. Altered versions must be plainly marked as such, and must not X * be misrepresented as being the original software. X * X * Beware that some of this code is subtly aware of the way operator X * precedence is structured in regular expressions. Serious changes in X * regular-expression syntax might require a total rethink. X */ X#include <stdio.h> X#include "regexp.h" X#include "regmagic.h" X X/* X * The "internal use only" fields in regexp.h are present to pass info from X * compile to execute that permits the execute phase to run lots faster on X * simple cases. They are: X * X * regstart char that must begin a match; '\0' if none obvious X * reganch is the match anchored (at beginning-of-line only)? X * regmust string (pointer into program) that match must include, or NULL X * regmlen length of regmust string X * X * Regstart and reganch permit very fast decisions on suitable starting points X * for a match, cutting down the work a lot. Regmust permits fast rejection X * of lines that cannot possibly match. The regmust tests are costly enough X * that regcomp() supplies a regmust only if the r.e. contains something X * potentially expensive (at present, the only such thing detected is * or + X * at the start of the r.e., which can involve a lot of backup). Regmlen is X * supplied because the test in regexec() needs it and regcomp() is computing X * it anyway. X */ X X/* X * Structure for regexp "program". This is essentially a linear encoding X * of a nondeterministic finite-state machine (aka syntax charts or X * "railroad normal form" in parsing technology). Each node is an opcode X * plus a "next" pointer, possibly plus an operand. "Next" pointers of X * all nodes except BRANCH implement concatenation; a "next" pointer with X * a BRANCH on both ends of it is connecting two alternatives. (Here we X * have one of the subtle syntax dependencies: an individual BRANCH (as X * opposed to a collection of them) is never concatenated with anything X * because of operator precedence.) The operand of some types of node is X * a literal string; for others, it is a node leading into a sub-FSM. In X * particular, the operand of a BRANCH node is the first node of the branch. X * (NB this is *not* a tree structure: the tail of the branch connects X * to the thing following the set of BRANCHes.) The opcodes are: X */ X X/* definition number opnd? meaning */ X#define END 0 /* no End of program. */ X#define BOL 1 /* no Match "" at beginning of line. */ X#define EOL 2 /* no Match "" at end of line. */ X#define ANY 3 /* no Match any one character. */ X#define ANYOF 4 /* str Match any character in this string. */ X#define ANYBUT 5 /* str Match any character not in this string. */ X#define BRANCH 6 /* node Match this alternative, or the next... */ X#define BACK 7 /* no Match "", "next" ptr points backward. */ X#define EXACTLY 8 /* str Match this string. */ X#define NOTHING 9 /* no Match empty string. */ X#define STAR 10 /* node Match this (simple) thing 0 or more times. */ X#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ X#define OPEN 20 /* no Mark this point in input as start of #n. */ X /* OPEN+1 is number 1, etc. */ X#define CLOSE 30 /* no Analogous to OPEN. */ X X/* X * Opcode notes: X * X * BRANCH The set of branches constituting a single choice are hooked X * together with their "next" pointers, since precedence prevents X * anything being concatenated to any individual branch. The X * "next" pointer of the last BRANCH in a choice points to the X * thing following the whole choice. This is also where the X * final "next" pointer of each individual branch points; each X * branch starts with the operand node of a BRANCH node. X * X * BACK Normal "next" pointers all implicitly point forward; BACK X * exists to make loop structures possible. X * X * STAR,PLUS '?', and complex '*' and '+', are implemented as circular X * BRANCH structures using BACK. Simple cases (one character X * per match) are implemented with STAR and PLUS for speed X * and to minimize recursive plunges. X * X * OPEN,CLOSE ...are numbered at compile time. X */ X X/* X * A node is one char of opcode followed by two chars of "next" pointer. X * "Next" pointers are stored as two 8-bit pieces, high order first. The X * value is a positive offset from the opcode of the node containing it. X * An operand, if any, simply follows the node. (Note that much of the X * code generation knows about this implicit relationship.) X * X * Using two bytes for the "next" pointer is vast overkill for most things, X * but allows patterns to get big without disasters. X */ X#define OP(p) (*(p)) X#define NEXT(p) (((*((p)+1)&0377)<<8) + *((p)+2)&0377) X#define OPERAND(p) ((p) + 3) X X/* X * See regmagic.h for one further detail of program structure. X */ X X X/* X * Utility definitions. X */ X#ifndef CHARBITS X#define UCHARAT(p) ((int)*(unsigned char *)(p)) X#else X#define UCHARAT(p) ((int)*(p)&CHARBITS) X#endif X X#define FAIL(m) { regerror(m); return(NULL); } X#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') X#define META "^$.[()|?+*\\" X X/* X * Flags to be passed up and down. X */ X#define HASWIDTH 01 /* Known never to match null string. */ X#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ X#define SPSTART 04 /* Starts with * or +. */ X#define WORST 0 /* Worst case. */ X X/* X * Global work variables for regcomp(). X */ Xstatic char *regparse; /* Input-scan pointer. */ Xstatic int regnpar; /* () count. */ Xstatic char regdummy; Xstatic char *regcode; /* Code-emit pointer; ®dummy = don't. */ Xstatic long regsize; /* Code size. */ X X/* X * Forward declarations for regcomp()'s friends. X */ X#ifndef STATIC X#define STATIC static X#endif XSTATIC char *reg(); XSTATIC char *regbranch(); XSTATIC char *regpiece(); XSTATIC char *regatom(); XSTATIC char *regnode(); XSTATIC char *regnext(); XSTATIC void regc(); XSTATIC void reginsert(); XSTATIC void regtail(); XSTATIC void regoptail(); X#ifdef STRCSPN XSTATIC int strcspn(); X#endif X X/* X - regcomp - compile a regular expression into internal code X * X * We can't allocate space until we know how big the compiled form will be, X * but we can't compile it (and thus know how big it is) until we've got a X * place to put the code. So we cheat: we compile it twice, once with code X * generation turned off and size counting turned on, and once "for real". X * This also means that we don't allocate space until we are sure that the X * thing really will compile successfully, and we never have to move the X * code and thus invalidate pointers into it. (Note that it has to be in X * one piece because free() must be able to free it all.) X * X * Beware that the optimization-preparation code in here knows about some X * of the structure of the compiled regexp. X */ Xregexp * Xregcomp(exp) Xchar *exp; X{ X register regexp *r; X register char *scan; X register char *longest; X register int len; X int flags; X extern char *malloc(); X X if (exp == NULL) X FAIL("NULL argument"); X X /* First pass: determine size, legality. */ X regparse = exp; X regnpar = 1; X regsize = 0L; X regcode = ®dummy; X regc(MAGIC); X if (reg(0, &flags) == NULL) X return(NULL); X X /* Small enough for pointer-storage convention? */ X if (regsize >= 32767L) /* Probably could be 65535L. */ X FAIL("regexp too big"); X X /* Allocate space. */ X r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize); X if (r == NULL) X FAIL("out of space"); X X /* Second pass: emit code. */ X regparse = exp; X regnpar = 1; X regcode = r->program; X regc(MAGIC); X if (reg(0, &flags) == NULL) X return(NULL); X X /* Dig out information for optimizations. */ X r->regstart = '\0'; /* Worst-case defaults. */ X r->reganch = 0; X r->regmust = NULL; X r->regmlen = 0; X scan = r->program+1; /* First BRANCH. */ X if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ X scan = OPERAND(scan); X X /* Starting-point info. */ X if (OP(scan) == EXACTLY) X r->regstart = *OPERAND(scan); X else if (OP(scan) == BOL) X r->reganch++; X X /* X * If there's something expensive in the r.e., find the X * longest literal string that must appear and make it the X * regmust. Resolve ties in favor of later strings, since X * the regstart check works with the beginning of the r.e. X * and avoiding duplication strengthens checking. Not a X * strong reason, but sufficient in the absence of others. X */ X if (flags&SPSTART) { X longest = NULL; X len = 0; X for (; scan != NULL; scan = regnext(scan)) X if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { X longest = OPERAND(scan); X len = strlen(OPERAND(scan)); X } X r->regmust = longest; X r->regmlen = len; X } X } X X return(r); X} X X/* X - reg - regular expression, i.e. main body or parenthesized thing X * X * Caller must absorb opening parenthesis. X * X * Combining parenthesis handling with the base level of regular expression X * is a trifle forced, but the need to tie the tails of the branches to what X * follows makes it hard to avoid. X */ Xstatic char * Xreg(paren, flagp) Xint paren; /* Parenthesized? */ Xint *flagp; X{ X register char *ret; X register char *br; X register char *ender; X register int parno; X int flags; X X *flagp = HASWIDTH; /* Tentatively. */ X X /* Make an OPEN node, if parenthesized. */ X if (paren) { X if (regnpar >= NSUBEXP) X FAIL("too many ()"); X parno = regnpar; X regnpar++; X ret = regnode(OPEN+parno); X } else X ret = NULL; X X /* Pick up the branches, linking them together. */ X br = regbranch(&flags); X if (br == NULL) X return(NULL); X if (ret != NULL) X regtail(ret, br); /* OPEN -> first. */ X else X ret = br; X if (!(flags&HASWIDTH)) X *flagp &= ~HASWIDTH; X *flagp |= flags&SPSTART; X while (*regparse == '|') { X regparse++; X br = regbranch(&flags); X if (br == NULL) X return(NULL); X regtail(ret, br); /* BRANCH -> BRANCH. */ X if (!(flags&HASWIDTH)) X *flagp &= ~HASWIDTH; X *flagp |= flags&SPSTART; X } X X /* Make a closing node, and hook it on the end. */ X ender = regnode((paren) ? CLOSE+parno : END); X regtail(ret, ender); X X /* Hook the tails of the branches to the closing node. */ X for (br = ret; br != NULL; br = regnext(br)) X regoptail(br, ender); X X /* Check for proper termination. */ X if (paren && *regparse++ != ')') { X FAIL("unmatched ()"); X } else if (!paren && *regparse != '\0') { X if (*regparse == ')') { X FAIL("unmatched ()"); X } else X FAIL("junk on end"); /* "Can't happen". */ X /* NOTREACHED */ X } X X return(ret); X} X X/* X - regbranch - one alternative of an | operator X * X * Implements the concatenation operator. X */ Xstatic char * Xregbranch(flagp) Xint *flagp; X{ X register char *ret; X register char *chain; X register char *latest; X int flags; X X *flagp = WORST; /* Tentatively. */ X X ret = regnode(BRANCH); X chain = NULL; X while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { X latest = regpiece(&flags); X if (latest == NULL) X return(NULL); X *flagp |= flags&HASWIDTH; X if (chain == NULL) /* First piece. */ X *flagp |= flags&SPSTART; X else X regtail(chain, latest); X chain = latest; X } X if (chain == NULL) /* Loop ran zero times. */ X (void) regnode(NOTHING); X X return(ret); X} X X/* X - regpiece - something followed by possible [*+?] X * X * Note that the branching code sequences used for ? and the general cases X * of * and + are somewhat optimized: they use the same NOTHING node as X * both the endmarker for their branch list and the body of the last branch. X * It might seem that this node could be dispensed with entirely, but the X * endmarker role is not redundant. X */ Xstatic char * Xregpiece(flagp) Xint *flagp; X{ X register char *ret; X register char op; X register char *next; X int flags; X X ret = regatom(&flags); X if (ret == NULL) X return(NULL); X X op = *regparse; X if (!ISMULT(op)) { X *flagp = flags; X return(ret); X } X X if (!(flags&HASWIDTH) && op != '?') X FAIL("*+ operand could be empty"); X *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); X X if (op == '*' && (flags&SIMPLE)) X reginsert(STAR, ret); X else if (op == '*') { X /* Emit x* as (x&|), where & means "self". */ X reginsert(BRANCH, ret); /* Either x */ X regoptail(ret, regnode(BACK)); /* and loop */ X regoptail(ret, ret); /* back */ X regtail(ret, regnode(BRANCH)); /* or */ X regtail(ret, regnode(NOTHING)); /* null. */ X } else if (op == '+' && (flags&SIMPLE)) X reginsert(PLUS, ret); X else if (op == '+') { X /* Emit x+ as x(&|), where & means "self". */ X next = regnode(BRANCH); /* Either */ X regtail(ret, next); X regtail(regnode(BACK), ret); /* loop back */ X regtail(next, regnode(BRANCH)); /* or */ X regtail(ret, regnode(NOTHING)); /* null. */ X } else if (op == '?') { X /* Emit x? as (x|) */ X reginsert(BRANCH, ret); /* Either x */ X regtail(ret, regnode(BRANCH)); /* or */ X next = regnode(NOTHING); /* null. */ X regtail(ret, next); X regoptail(ret, next); X } X regparse++; X if (ISMULT(*regparse)) X FAIL("nested *?+"); X X return(ret); X} X X/* X - regatom - the lowest level X * X * Optimization: gobbles an entire sequence of ordinary characters so that X * it can turn them into a single node, which is smaller to store and X * faster to run. Backslashed characters are exceptions, each becoming a X * separate node; the code is simpler that way and it's not worth fixing. X */ Xstatic char * Xregatom(flagp) Xint *flagp; X{ X register char *ret; X int flags; X X *flagp = WORST; /* Tentatively. */ X X switch (*regparse++) { X case '^': X ret = regnode(BOL); X break; X case '$': X ret = regnode(EOL); X break; X case '.': X ret = regnode(ANY); X *flagp |= HASWIDTH|SIMPLE; X break; X case '[': { X register int class; X register int classend; X X if (*regparse == '^') { /* Complement of range. */ X ret = regnode(ANYBUT); X regparse++; X } else X ret = regnode(ANYOF); X if (*regparse == ']' || *regparse == '-') X regc(*regparse++); X while (*regparse != '\0' && *regparse != ']') { X if (*regparse == '-') { X regparse++; X if (*regparse == ']' || *regparse == '\0') X regc('-'); X else { X class = UCHARAT(regparse-2)+1; X classend = UCHARAT(regparse); X if (class > classend+1) X FAIL("invalid [] range"); X for (; class <= classend; class++) X regc(class); X regparse++; X } X } else X regc(*regparse++); X } X regc('\0'); X if (*regparse != ']') X FAIL("unmatched []"); X regparse++; X *flagp |= HASWIDTH|SIMPLE; X } X break; X case '(': X ret = reg(1, &flags); X if (ret == NULL) X return(NULL); X *flagp |= flags&(HASWIDTH|SPSTART); X break; X case '\0': X case '|': X case ')': X FAIL("internal urp"); /* Supposed to be caught earlier. */ X break; X case '?': X case '+': X case '*': X FAIL("?+* follows nothing"); X break; X case '\\': X if (*regparse == '\0') X FAIL("trailing \\"); X ret = regnode(EXACTLY); X regc(*regparse++); X regc('\0'); X *flagp |= HASWIDTH|SIMPLE; X break; X default: { X register int len; X register char ender; X X regparse--; X len = strcspn(regparse, META); X if (len <= 0) X FAIL("internal disaster"); X ender = *(regparse+len); X if (len > 1 && ISMULT(ender)) X len--; /* Back off clear of ?+* operand. */ X *flagp |= HASWIDTH; X if (len == 1) X *flagp |= SIMPLE; X ret = regnode(EXACTLY); X while (len > 0) { X regc(*regparse++); X len--; X } X regc('\0'); X } X break; X } X X return(ret); X} X X/* X - regnode - emit a node X */ Xstatic char * /* Location. */ Xregnode(op) Xchar op; X{ X register char *ret; X register char *ptr; X X ret = regcode; X if (ret == ®dummy) { X regsize += 3; X return(ret); X } X X ptr = ret; X *ptr++ = op; X *ptr++ = '\0'; /* Null "next" pointer. */ X *ptr++ = '\0'; X regcode = ptr; X X return(ret); X} X X/* X - regc - emit (if appropriate) a byte of code X */ Xstatic void Xregc(b) Xchar b; X{ X if (regcode != ®dummy) X *regcode++ = b; X else X regsize++; X} X X/* X - reginsert - insert an operator in front of already-emitted operand X * X * Means relocating the operand. X */ Xstatic void Xreginsert(op, opnd) Xchar op; Xchar *opnd; X{ X register char *src; X register char *dst; X register char *place; X X if (regcode == ®dummy) { X regsize += 3; X return; X } X X src = regcode; X regcode += 3; X dst = regcode; X while (src > opnd) X *--dst = *--src; X X place = opnd; /* Op node, where operand used to be. */ X *place++ = op; X *place++ = '\0'; X *place++ = '\0'; X} X X/* X - regtail - set the next-pointer at the end of a node chain X */ Xstatic void Xregtail(p, val) Xchar *p; Xchar *val; X{ X register char *scan; X register char *temp; X register int offset; X X if (p == ®dummy) X return; X X /* Find last node. */ X scan = p; X for (;;) { X temp = regnext(scan); X if (temp == NULL) X break; X scan = temp; X } X X if (OP(scan) == BACK) X offset = scan - val; X else X offset = val - scan; X *(scan+1) = (offset>>8)&0377; X *(scan+2) = offset&0377; X} X X/* X - regoptail - regtail on operand of first argument; nop if operandless X */ Xstatic void Xregoptail(p, val) Xchar *p; Xchar *val; X{ X /* "Operandless" and "op != BRANCH" are synonymous in practice. */ X if (p == NULL || p == ®dummy || OP(p) != BRANCH) X return; X regtail(OPERAND(p), val); X} X X/* X * regexec and friends X */ X X/* X * Global work variables for regexec(). X */ Xstatic char *reginput; /* String-input pointer. */ Xstatic char *regbol; /* Beginning of input, for ^ check. */ Xstatic char **regstartp; /* Pointer to startp array. */ Xstatic char **regendp; /* Ditto for endp. */ X X/* X * Forwards. X */ XSTATIC int regtry(); XSTATIC int regmatch(); XSTATIC int regrepeat(); X X#ifdef DEBUG Xint regnarrate = 0; Xvoid regdump(); XSTATIC char *regprop(); X#endif X X/* X - regexec - match a regexp against a string X */ Xint Xregexec(prog, string) Xregister regexp *prog; Xregister char *string; X{ X register char *s; X extern char *strchr(); X X /* Be paranoid... */ X if (prog == NULL || string == NULL) { X regerror("NULL parameter"); X return(0); X } X X /* Check validity of program. */ X if (UCHARAT(prog->program) != MAGIC) { X regerror("corrupted program"); X return(0); X } X X /* If there is a "must appear" string, look for it. */ X if (prog->regmust != NULL) { X s = string; X while ((s = strchr(s, prog->regmust[0])) != NULL) { X if (strncmp(s, prog->regmust, prog->regmlen) == 0) X break; /* Found it. */ X s++; X } X if (s == NULL) /* Not present. */ X return(0); X } X X /* Mark beginning of line for ^ . */ X regbol = string; X X /* Simplest case: anchored match need be tried only once. */ X if (prog->reganch) X return(regtry(prog, string)); X X /* Messy cases: unanchored match. */ X s = string; X if (prog->regstart != '\0') X /* We know what char it must start with. */ X while ((s = strchr(s, prog->regstart)) != NULL) { X if (regtry(prog, s)) X return(1); X s++; X } X else X /* We don't -- general case. */ X do { X if (regtry(prog, s)) X return(1); X } while (*s++ != '\0'); X X /* Failure. */ X return(0); X} X X/* X - regtry - try match at specific point X */ Xstatic int /* 0 failure, 1 success */ Xregtry(prog, string) Xregexp *prog; Xchar *string; X{ X register int i; X register char **sp; X register char **ep; X X reginput = string; X regstartp = prog->startp; X regendp = prog->endp; X X sp = prog->startp; X ep = prog->endp; X for (i = NSUBEXP; i > 0; i--) { X *sp++ = NULL; X *ep++ = NULL; X } X if (regmatch(prog->program + 1)) { X prog->startp[0] = string; X prog->endp[0] = reginput; X return(1); X } else X return(0); X} X X/* X - regmatch - main matching routine X * X * Conceptually the strategy is simple: check to see whether the current X * node matches, call self recursively to see whether the rest matches, X * and then act accordingly. In practice we make some effort to avoid X * recursion, in particular by going through "ordinary" nodes (that don't X * need to know whether the rest of the match failed) by a loop instead of X * by recursion. X */ Xstatic int /* 0 failure, 1 success */ Xregmatch(prog) Xchar *prog; X{ X register char *scan; /* Current node. */ X char *next; /* Next node. */ X extern char *strchr(); X X scan = prog; X#ifdef DEBUG X if (scan != NULL && regnarrate) X fprintf(stderr, "%s(\n", regprop(scan)); X#endif X while (scan != NULL) { X#ifdef DEBUG X if (regnarrate) X fprintf(stderr, "%s...\n", regprop(scan)); X#endif X next = regnext(scan); X X switch (OP(scan)) { X case BOL: X if (reginput != regbol) X return(0); X break; X case EOL: X if (*reginput != '\0') X return(0); X break; X case ANY: X if (*reginput == '\0') X return(0); X reginput++; X break; X case EXACTLY: { X register int len; X register char *opnd; X X opnd = OPERAND(scan); X /* Inline the first character, for speed. */ X if (*opnd != *reginput) X return(0); X len = strlen(opnd); X if (len > 1 && strncmp(opnd, reginput, len) != 0) X return(0); X reginput += len; X } X break; X case ANYOF: X if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL) X return(0); X reginput++; X break; X case ANYBUT: X if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL) X return(0); X reginput++; X break; X case NOTHING: X break; X case BACK: X break; X case OPEN+1: X case OPEN+2: X case OPEN+3: X case OPEN+4: X case OPEN+5: X case OPEN+6: X case OPEN+7: X case OPEN+8: X case OPEN+9: { X register int no; X register char *save; X X no = OP(scan) - OPEN; X save = reginput; X X if (regmatch(next)) { X /* X * Don't set startp if some later X * invocation of the same parentheses X * already has. X */ X if (regstartp[no] == NULL) X regstartp[no] = save; X return(1); X } else X return(0); X } X break; X case CLOSE+1: X case CLOSE+2: X case CLOSE+3: X case CLOSE+4: X case CLOSE+5: X case CLOSE+6: X case CLOSE+7: X case CLOSE+8: X case CLOSE+9: { X register int no; X register char *save; X X no = OP(scan) - CLOSE; X save = reginput; X X if (regmatch(next)) { X /* X * Don't set endp if some later X * invocation of the same parentheses X * already has. X */ X if (regendp[no] == NULL) X regendp[no] = save; X return(1); X } else X return(0); X } X break; X case BRANCH: { X register char *save; X X if (OP(next) != BRANCH) /* No choice. */ X next = OPERAND(scan); /* Avoid recursion. */ X else { X do { X save = reginput; X if (regmatch(OPERAND(scan))) X return(1); X reginput = save; X scan = regnext(scan); X } while (scan != NULL && OP(scan) == BRANCH); X return(0); X /* NOTREACHED */ X } X } X break; X case STAR: X case PLUS: { X register char nextch; X register int no; X register char *save; X register int min; X X /* X * Lookahead to avoid useless match attempts X * when we know what character comes next. X */ X nextch = '\0'; X if (OP(next) == EXACTLY) X nextch = *OPERAND(next); X min = (OP(scan) == STAR) ? 0 : 1; X save = reginput; X no = regrepeat(OPERAND(scan)); X while (no >= min) { X /* If it could work, try it. */ X if (nextch == '\0' || *reginput == nextch) X if (regmatch(next)) X return(1); X /* Couldn't or didn't -- back up. */ X no--; X reginput = save + no; X } X return(0); X } X break; X case END: X return(1); /* Success! */ X break; X default: X regerror("memory corruption"); X return(0); X break; X } X X scan = next; X } X X /* X * We get here only if there's trouble -- normally "case END" is X * the terminating point. X */ X regerror("corrupted pointers"); X return(0); X} X X/* X - regrepeat - repeatedly match something simple, report how many X */ Xstatic int Xregrepeat(p) Xchar *p; X{ X register int count = 0; X register char *scan; X register char *opnd; X extern char * strchr(); X X scan = reginput; X opnd = OPERAND(p); X switch (OP(p)) { X case ANY: X count = strlen(scan); X scan += count; X break; X case EXACTLY: X while (*opnd == *scan) { X count++; X scan++; X } X break; X case ANYOF: X while (*scan != '\0' && strchr(opnd, *scan) != NULL) { X count++; X scan++; X } X break; X case ANYBUT: X while (*scan != '\0' && strchr(opnd, *scan) == NULL) { X count++; X scan++; X } X break; X default: /* Oh dear. Called inappropriately. */ X regerror("internal foulup"); X count = 0; /* Best compromise. */ X break; X } X reginput = scan; X X return(count); X} X X/* X - regnext - dig the "next" pointer out of a node X */ Xstatic char * Xregnext(p) Xregister char *p; X{ X register int offset; X X if (p == ®dummy) X return(NULL); X X offset = NEXT(p); X if (offset == 0) X return(NULL); X X if (OP(p) == BACK) X return(p-offset); X else X return(p+offset); X} X X#ifdef DEBUG X XSTATIC char *regprop(); X X/* X - regdump - dump a regexp onto stdout in vaguely comprehensible form X */ Xvoid Xregdump(r) Xregexp *r; X{ X register char *s; X register char op = EXACTLY; /* Arbitrary non-END op. */ X register char *next; X extern char *strchr(); X X X s = r->program + 1; X while (op != END) { /* While that wasn't END last time... */ X op = OP(s); X printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ X next = regnext(s); X if (next == NULL) /* Next ptr. */ X printf("(0)"); X else X printf("(%d)", (s-r->program)+(next-s)); X s += 3; X if (op == ANYOF || op == ANYBUT || op == EXACTLY) { X /* Literal string, where present. */ X while (*s != '\0') { X putchar(*s); X s++; X } X s++; X } X putchar('\n'); X } X X /* Header fields of interest. */ X if (r->regstart != '\0') X printf("start `%c' ", r->regstart); X if (r->reganch) X printf("anchored "); X if (r->regmust != NULL) X printf("must have \"%s\"", r->regmust); X printf("\n"); X} X X/* X - regprop - printable representation of opcode X */ Xstatic char * Xregprop(op) Xchar *op; X{ X register char *p; X static char buf[50]; X X (void) strcpy(buf, ":"); X X switch (OP(op)) { X case BOL: X p = "BOL"; X break; X case EOL: X p = "EOL"; X break; X case ANY: X p = "ANY"; X break; X case ANYOF: X p = "ANYOF"; X break; X case ANYBUT: X p = "ANYBUT"; X break; X case BRANCH: X p = "BRANCH"; X break; X case EXACTLY: X p = "EXACTLY"; X break; X case NOTHING: X p = "NOTHING"; X break; X case BACK: X p = "BACK"; X break; X case END: X p = "END"; X break; X case OPEN+1: X case OPEN+2: X case OPEN+3: X case OPEN+4: X case OPEN+5: X case OPEN+6: X case OPEN+7: X case OPEN+8: X case OPEN+9: X sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN); X p = NULL; X break; X case CLOSE+1: X case CLOSE+2: X case CLOSE+3: X case CLOSE+4: X case CLOSE+5: X case CLOSE+6: X case CLOSE+7: X case CLOSE+8: X case CLOSE+9: X sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE); X p = NULL; X break; X case STAR: X p = "STAR"; X break; X case PLUS: X p = "PLUS"; X break; X default: X regerror("corrupted opcode"); X break; X } X if (p != NULL) X (void) strcat(buf, p); X return(buf); X} X#endif X X/* X * The following is provided for those people who do not have strcspn() in X * their C libraries. They should get off their butts and do something X * about it; at least one public-domain implementation of those (highly X * useful) string routines has been published on Usenet. X */ X#ifdef STRCSPN X/* X * strcspn - find length of initial segment of s1 consisting entirely X * of characters not from s2 X */ X Xstatic int Xstrcspn(s1, s2) Xchar *s1; Xchar *s2; X{ X register char *scan1; X register char *scan2; X register int count; X X count = 0; X for (scan1 = s1; *scan1 != '\0'; scan1++) { X for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */ X if (*scan1 == *scan2++) X return(count); X count++; X } X return(count); X} X#endif SHAR_EOF if test 27639 -ne "`wc -c < 'regexp.c'`" then echo shar: error transmitting "'regexp.c'" '(should have been 27639 characters)' fi fi if test -f 'regexp.h' then echo shar: will not over-write existing file "'regexp.h'" else echo extracting "'regexp.h'" sed 's/^X//' >regexp.h <<'SHAR_EOF' X/* X * Definitions etc. for regexp(3) routines. X * X * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], X * not the System V one. X */ X#define NSUBEXP 10 Xtypedef struct regexp { X char *startp[NSUBEXP]; X char *endp[NSUBEXP]; X char regstart; /* Internal use only. */ X char reganch; /* Internal use only. */ X char *regmust; /* Internal use only. */ X int regmlen; /* Internal use only. */ X char program[1]; /* Unwarranted chumminess with compiler. */ X} regexp; X Xextern regexp *regcomp(); Xextern int regexec(); Xextern void regsub(); Xextern void regerror(); SHAR_EOF if test 574 -ne "`wc -c < 'regexp.h'`" then echo shar: error transmitting "'regexp.h'" '(should have been 574 characters)' fi fi if test -f 'regmagic.h' then echo shar: will not over-write existing file "'regmagic.h'" else echo extracting "'regmagic.h'" sed 's/^X//' >regmagic.h <<'SHAR_EOF' X/* X * The first byte of the regexp internal "program" is actually this magic X * number; the start node begins in the second byte. X */ X#define MAGIC 0234 SHAR_EOF if test 153 -ne "`wc -c < 'regmagic.h'`" then echo shar: error transmitting "'regmagic.h'" '(should have been 153 characters)' fi fi if test -f 'scrn.c' then echo shar: will not over-write existing file "'scrn.c'" else echo extracting "'scrn.c'" sed 's/^X//' >scrn.c <<'SHAR_EOF' X/* Scrn.c replaces screen.c that was in the net distribution. Module contains X * PC screen routines plus whatever was in screen.c that had to be kept for X * linking with the other modules at link time. X */ X X#include <dos.h> X#include <stdlib.h> X#include <string.h> X#include "scrn.h" X X#define CNTL_H 0x08 X#define CNTL_U 0x15 X#define NL 0x0a X#define CR 0x0d X#define TAB 0x09 X#define BELL 0x07 X#define ROWS 25 X#define COLUMS 80 X X#define NOT_QUIET 0 /* Ring bell at eof and for errors */ X#define LITTLE_QUIET 1 /* Ring bell only for errors */ X#define VERY_QUIET 2 /* Never ring bell */ X Xextern int quiet; /* If VERY_QUIET, use visual bell for bell */ Xextern int scrn_in_color; /* When using color monitor */ Xint erase_char, kill_char; /* The user's erase and line-kill chars */ Xint sc_height, sc_width, se_width, ue_width, ul_width, bo_width, be_width, so_width; Xint auto_wrap, ignaw; Xvoid init(), dinit(), get_term(), home(), bell(), vbell(), add_line(); Xvoid lower_left(), clear(), clear_eol(), so_enter(), so_exit(), ul_enter(); Xvoid ul_exit(), bo_enter(), bo_exit(), backspace(), putbs(), raw_mode(); Xint ulflag = 0, boflag = 0; X Xcls() /* move cursor home and clear the screen */ X{ X union REGS REG; X int mode; X X gotoxy(0, 0); X REG.x.ax = 0x0600; X if (scrn_in_color == 1) X REG.h.bh = WHITE_ON_BLUE; X else X REG.h.bh = BW; X REG.x.cx = 0x0000; X REG.x.dx = 0x184f; X int86(0x10, ®, ®); X} X Xera_eol() X{ X union REGS REG; X int hold[4]; X int column; X X getxy(hold); X column = hold[1]; X if (scrn_in_color == 1) X REG.x.bx = WHITE_ON_BLUE; X else X REG.x.bx = BW; X REG.x.ax = 0x0900; /* ah = 10; al = null char to write */ X REG.x.cx = 80 - column; /* cx = no. of nulls to write */ X int86(0x10, ®, ®); X restorxy(hold); /* retore cursor to original position */ X return; X} X Xgotoxy(row, col) /* Position cursor at x,y on screen */ Xint row, col; X{ X union REGS REG; X X REG.h.ah = 02; X REG.h.bh = 00; X REG.h.dh = row; X REG.h.dl = col; X int86(0x10, ®, ®); X} X Xgetxy(hold) /* Get cursor coordinates */ Xint *hold; X{ X union REGS REG; X X REG.h.ah = 03; X REG.h.bh = 00; X int86(0x10, ®, ®); X hold[0] = REG.h.dh; X hold[1] = REG.h.dl; X hold[2] = REG.h.ch; X hold[3] = REG.h.cl; X} X X Xrestorxy(hold) /* Restore cursor gotten above */ Xint *hold; X{ X union REGS REG; X X gotoxy(hold[0], hold[1]); X REG.h.ah = 01; X REG.h.bh = 00; X REG.h.ch = hold[2]; X REG.h.cl = hold[3]; X int86(0x10, ®, ®); X} X Xcurs_r(n) /* move cursor right n places */ Xint n; X{ X int hold[4]; X int row, column; X X getxy(hold); X row = hold[0]; X column = hold[1]; X if (column < 0) X if (n < 0) X return(0); X if (column > 79) X if (n > 0) X return(0); X column = column + n; X gotoxy(row, column); X} X Xcurs_l(n) /* move cursor left n places */ Xint n; X{ X curs_r(-n); X} X Xscroll_up(n) Xint n; X{ X union REGS REG; X X REG.h.ah = 0x06; X if (scrn_in_color == 1) X REG.h.bh = WHITE_ON_BLUE; X else X REG.h.bh = BW; X REG.h.al = n; X REG.x.cx = 0x0000; X REG.x.dx = 256 * 24 + 79; X int86(0x10, ®, ®); X return(1); X} X Xget_mode() /* Check for Monochrome mode 7 */ X{ X union REGS REG; X X REG.h.ah = 15; X int86(0x10, ®, ®); X return(REG.h.al); X} X X X/* X * Set cursor checking for current cursor size parameters. X */ X Xset_cur() X{ X union REGS INREG, OUTREG; X X if (get_mode() == 7) X { X INREG.h.ah = 1; X INREG.h.bh = 0x00; X INREG.h.ch = 12; X INREG.h.cl = 13; X int86(0x10, &INREG, &OUTREG); X } X else X { X INREG.h.ah = 0x03; X INREG.h.bh = 0x00; X int86(0x10, &INREG, &OUTREG); X INREG.h.ah = 0x01; X INREG.h.bh = 0x00; X INREG.h.ch = OUTREG.h.ch; X INREG.h.cl = OUTREG.h.cl; X int86(0x10, &INREG, &OUTREG); X } X} X Xchr_put(c, attribute) Xint c; Xint attribute; X{ X union REGS REG; X int hold[4]; X int i, row, column; X X if (c == CR) X { X getxy(hold); X row = hold[0]; X column = 0; X gotoxy(row, column); X return(1); X } X if (c == TAB) X { X for (i = 0;i <= 7;++i) X chr_put(' ', attribute); X return(1); X } X if (c == BELL) X { X putch(7); X return(1); X } X if (c == NL) X { X getxy(hold); X row = hold[0]; X if (row >= 24) X scroll_up(1); X else X ++row; X column = 0; X gotoxy(row, column); X return(1); X } X REG.h.ah = 0x9; X REG.h.al = c; X if (ulflag || boflag) X REG.h.bl = (ulflag | boflag); X else X REG.h.bl = attribute; X REG.h.bh = 00; X REG.x.cx = 1; X int86(0x10, ®, ®); X curs_r(1); X return(REG.x.ax); X} X Xstr_put(str, attribute) Xchar *str; Xint attribute; X{ X int i; X X if (scrn_in_color == 1) X attribute = WHITE_ON_RED; X else X attribute = REV_VID; X for (i = 0;i < strlen(str);++i) X chr_put(*(str + i), attribute); X} X X X/* X * Add a blank line (called with cursor at home). X * Should scroll the display down. X */ X Xvoid Xadd_line() X{ X union REGS REG; X int hold[4]; X int row, column; X X REG.h.ah = 0x07; X if (scrn_in_color == 1) X REG.h.bh = WHITE_ON_BLUE; X else X REG.h.bh = BW; X REG.h.al = 1; X getxy(hold); X row = hold[0]; X column = hold[1]; X REG.h.ch = row; X REG.h.cl = 0; X REG.h.dh = 24; X REG.h.dl = 79; X int86(0x10, ®, ®); X} X X/* X * Below are the functions which perform all the "less terminal-specific" X * screen manipulation functions. They are taken from screen.c that was X * in the distribution of less on the news. X */ X X/* X * Initialize terminal X */ Xvoid Xinit() X{ X set_cur(); X} X X/* X * Deinitialize terminal X */ Xvoid Xdeinit() X{ X} X Xvoid Xget_term() X{ X sc_height = ROWS; X sc_width = COLUMS; X se_width = 0; X ue_width = 0; X ul_width = 0; X be_width = 0; X bo_width = 0; X so_width = 0; X auto_wrap = 0; /* chr_put doesn't autowrap */ X ignaw = 0; X /* sneak in kill and erase characters for command line editing */ X kill_char = CNTL_U; /* use ctrl-u as kill chararcter */ X erase_char = CNTL_H; /* use ctrl-h as erase character */ X} X Xvoid Xraw_mode(on) Xint on; X{ X /* left here in case there is a desire */ X /* to put terminal in raw_mode vs cooked */ X} X X/* X * Home cursor (move to upper left corner of screen). X */ X Xvoid Xhome() X{ X gotoxy(0, 0); X} X X/* X * Move cursor to lower left corner of screen. X */ Xvoid Xlower_left() X{ X gotoxy(24, 0); X} X X/* X * Ring the terminal bell. X */ Xvoid Xbell() X{ X if (quiet == VERY_QUIET) X vbell(); X else X putch(BELL); X} X X/* X * Output the "visual bell", if there is one. X */ Xvoid Xvbell() X{ X /* there is no visual bell at this time */ X return; X} X X/* X * Clear the screen. X */ Xvoid Xclear() X{ X cls(); X} X X/* X * Clear from the cursor to the end of the cursor's line. X * {{ This must not move the cursor. }} X */ Xvoid Xclear_eol() X{ X era_eol(); X} X X/* X * Begin "standout" (bold, underline, or whatever). X */ Xvoid Xso_enter() X{ X} X X/* X * End "standout". X */ Xvoid Xso_exit() X{ X} X/* X * Begin bold X*/ Xvoid Xbo_enter() X{ X boflag = BOLDFACE; X} X/* X * End bold X */ Xvoid Xbo_exit() X{ X boflag = 0; X} X X/* X * Begin "underline" (hopefully real underlining, X * otherwise whatever the terminal provides). X */ Xvoid Xul_enter() X{ X if (scrn_in_color == 1) X ulflag = COLOR_UL; X else X ulflag = UNDERLINE; X} X X/* X * End "underline". X */ Xvoid Xul_exit() X{ X ulflag = 0; X} X X/* X * Erase the character to the left of the cursor X * and move the cursor left. X */ Xvoid Xbackspace() X{ X /* X * Try to erase the previous character by overstriking with a space. X */ X curs_l(1); X putc(' '); X curs_l(1); X} X X/* X * Output a plain backspace, without erasing the previous char. X */ Xvoid Xputbs() X{ X curs_l(1); X} X SHAR_EOF if test 7306 -ne "`wc -c < 'scrn.c'`" then echo shar: error transmitting "'scrn.c'" '(should have been 7306 characters)' fi fi if test -f 'scrn.h' then echo shar: will not over-write existing file "'scrn.h'" else echo extracting "'scrn.h'" sed 's/^X//' >scrn.h <<'SHAR_EOF' X#define WHITE_ON_RED 0x47 X#define WHITE_ON_BLUE 0x17 X#define BW 0x07 X#define REV_VID 0x70 X#define BOLDFACE 0x0f X#define UNDERLINE 0x01 X#define COLOR_UL 0x06 X SHAR_EOF if test 160 -ne "`wc -c < 'scrn.h'`" then echo shar: error transmitting "'scrn.h'" '(should have been 160 characters)' fi fi if test -f 'signal.c' then echo shar: will not over-write existing file "'signal.c'" else echo extracting "'signal.c'" sed 's/^X//' >signal.c <<'SHAR_EOF' X/* X * Routines dealing with signals. X * X * A signal usually merely causes a bit to be set in the "signals" word. X * At some convenient time, the mainline code checks to see if any X * signals need processing by calling psignal(). X * An exception is made if we are reading from the keyboard when the X * signal is received. Some operating systems will simply call the X * signal handler and NOT return from the read (with EINTR). X * To handle this case, we service the interrupt directly from X * the handler if we are reading from the keyboard. X */ X X#include "less.h" X#ifdef MSDOS Xchar get_swchar(); Xvoid swchar_to_dos(); Xvoid swchar_to_unix(); X#include <process.h> X#include <dos.h> X#endif X Xextern char *first_cmd; Xint result; Xchar sw_char; Xpublic int sigs; X X/* X * Pass the specified command to a shell to be executed. X * Like plain "system()", but handles resetting terminal modes, etc. X */ Xvoid lsystem(cmd) X char *cmd; X{ X int inp; X X /* X * Print the command which is to be executed. X */ X lower_left(); X clear_eol(); X puts("!"); X puts(cmd); X puts("\n"); X X /* X * De-initialize the terminal and take out of raw mode. X */ X#ifndef MSDOS X deinit(); X flush(); X raw_mode(0); X#endif X /* X * Pass the command to the system to be executed. X */ X#ifndef MSDOS X inp = dup(0); X close(0); X open("/dev/tty", 0); X#endif X#ifdef MSDOS X sw_char = get_swchar(); X swchar_to_dos(); X result = system(cmd); X if (result != 0) X perror("Less"); X if (sw_char == '-') X swchar_to_unix(); X#else X system(cmd); X#endif X#ifndef MSDOS X close(0); X dup(inp); X close(inp); X#endif X /* X * Reset signals, raw mode, etc. X */ X#ifndef MSDOS X init_signals(); X raw_mode(1); X init(); X#endif X} X X#ifdef MSDOS Xchar Xget_swchar() X{ X union REGS inregs; X union REGS outregs; X X inregs.h.ah = 0x37; X inregs.h.al = 0; X intdos(&inregs, &outregs); X return(outregs.h.dl); X} X X Xvoid Xswchar_to_dos() X{ X union REGS inregs; X union REGS outregs; X X inregs.h.ah = 0x37; X inregs.h.al = 0x01; X inregs.h.dl = '/'; X intdos(&inregs, &outregs); X return; X} X Xvoid Xswchar_to_unix() X{ X union REGS inregs; X union REGS outregs; X X inregs.h.ah = 0x37; X inregs.h.al = 0x01; X inregs.h.dl = '-'; X intdos(&inregs, &outregs); X return; X} X X#endif X X SHAR_EOF if test 2167 -ne "`wc -c < 'signal.c'`" then echo shar: error transmitting "'signal.c'" '(should have been 2167 characters)' fi fi # end of shell archive exit 0 --- No warranties whatsoever. Mike Slomin !bellcore!lcuxa!mike2
mike2@lcuxa.UUCP (M S Slomin) (05/01/88)
I don't know why, but my 49K posting of part 3 of the less sources was apparently truncated at the majority of sites. (I did limit it to < 50K, which I thought was the limit for most mailers, but who knows?) Rather than continuing to mail it to those who request it, I've split the original posting in two, and am reposting it as part3a and part3b. Hope this solves the problem. -----------------CUT HERE (less source, part 3a-------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file # 3. Execute the file with /bin/sh (not csh) to create the files: # # readme # scrn.c # scrn.h # signal.c # # Created on May 1, 1988 # if test -f 'readme' then echo shar: will not over-write existing file "'readme'" else echo extracting "'readme'" sed 's/^X//' >readme <<'SHAR_EOF' XPort of less to msdos: X XLess is a program similar to the Unix more command but is much more Xuseful. Read less.man for a good description of less and what it can Xdo for you. X XA. Dick Keily's original porting information for MSC 4.0: X XThe files included here will result in a full version of less Xfor use on msdos. This version of less has been tested on all the pc Xsystems that I could get my hands on. This includes monochrome, graphics, X(that includes systems with graphics cards and mono monitors), and color Xgraphics. X XThe following must be done to have less.exe work in its full glory: X X 1. Nothing if you don't want color when using a color monitor. X To get the color use the set command such as: X X set less=C (Case sensitive; c is another option) X X Do this prior to calling less. A command line option X less -C <filename ...> will also get you color. X X NOTE: Only use the color option when using a color monitor. X All other configurations need no special action. X X There are other less settings that can be used using the X set command. Read less.man. The one I use is less=mC on X an AT with enhanced monitor and ega card. X X 2. Less also has a v command that lets you can an editor from X within less to edit the file that you are viewing at the X time. The default is memacs which was taken from the Unix X mod.sources. If you don't have it use the dos set command X as follows to let less know which it is: X X set editor=your_editor X X 4. It is best to include the set commands in your autoexec.bat X which will be executed when you boot up your system. X X 5. Less is a very useful utility. Read less.man and when in X less the h command will put a description of the commands X on the screen for you. X X 6. This version of less is named less13.exe for distribution. X It replaces and other version that you have obtained from X my posting of the executable. Accept no substitutes. When X I get the time I will incorporate the changes to get up X the distributed Unix version 61. If you post this to any X bulletin board name any files associated with it with the X version 13.(referring to the MSDOS control). X X [N.B. No longer correct. This version is X somewhere between version 61 and 73 in its X capabilities.] X X 7. Less version 1.3 uses the system function but looks to see X if you are using the swchar program that allows you to use X the / rather than the \ character. The swchar state is changed X before the system call if you are and then resets it so that X it all becomes transparent to the user. X X [N.B. This is still correct.] X X 8. The source code for less is being posted to Net.sources. X It is all ifdef'd so that it should compile on Unix systems X also as is. X X [N.B. Probably not anymore. The ctrlbrk() X function will fail on Unix.] X X 9. Version 1.3 corrects the following: X The latest known to me fixes have been incorporated X in the regexpression routines written by Henry X Spencer and posted on the Unix news net. X X Dick Keily X Eastman Kodak Company X work: (716)477-1001 X unix: ...!seismo!rochester!kodak!keily X--------------------------------------------------------------------------- X--------------------------------------------------------------------------- X XB. Revised port to TurboC: X X 1. The port now incorporates boldface and underline (useful X for viewing documents created with nroff -man). I've X chosen the monochrome attributes for these, 0x01 and 0xf0, X and for CGA color, 0x06 (yellow) and 0xf0 (bright white). X If you wish to use others, revise the SCRN.H and/or SCRN.C X file(s) appropriately. X X 2. This will compile in the "tiny" model, and the resulting X file can be converted to a '.com' file for loading and X execution speed. An appropriate 'LESS.PRJ' file is X included; be certain to define DOSPORT in 'Options/Compiler' X (interactive mode). X X 3. A bug was fixed in the 'v' command (escape X to the editor): on return to less, the original unmodified X file in memory was displayed, not the edited one. This X is worked around in command.c by causing an 'E\n' command X to be executed on return. 'E' (Examine) loads in a new X file, and if no filename is given it loads in the previous X file. (Indeed, this is useful if 'E' is used normally. X After examining a different file, you can return to the X previous one by invoking 'E' with no file name.) X X 4. A new command, 'L', is implemented. 'L' gives X all of the information that '^G' and '=' give (i.e., X file name, number of bytes, approximate percentage into X the file) and in addition gives the number of the first X line appearing on the screen. This information is useful X if you wish to edit the file subsequently with a line- X oriented editor (e.g., vi, EDLIN, etc.); you know what X line to jump to. Unfortunately, it is a bit slow because X long ints are used in the calculation -- but a file might X exceed 65,000 lines!. X X 5. Sorry, I've not bothered to maintain downward compatibility X with Mr. Keily's MSC 4.0 port. I tried it both ways, and the X TurboC one is smaller and faster. If you want to do it with X MSC 4.0: (1) replace the ctrlbrk() command with an appropriate X signal() and interrupt handler; (2) compile it in the small X model; and (3) link with a /stack:4096 flag, linking in the X ssetargv.obj (small model) object file supplied by Microsoft X instead of the included setargv.c file. X X 6. If you haven't updated to TurboC 1.5, read the comments in X SETARGV.C. The variables _argc and _argv must be changed X throughout that file to __argc and __argv to work properly X in version 1.0. In any event, the SETARGV function is X needed only for DOS wildcard expansion, which is useful if X you want to specify multiple files for viewing with wildcards X and flip back and forth among them with 'N' and 'P'. If X you don't need the wildcard capability, omit SETARGV. X X 7. The public-domain REGEXP.C file supplied by Dick Keily X implements full regular expression matching a la X 'egrep' (i.e., using the '+' for one or more matches and X the '|' for disjunctive matches) in addition to the more X limited 'ed'/'sed' pattern match metacharacters. This is X useful, and better than classical Unix REGEXP. X X Mike Slomin X !bellcore!lcuxa!mike2 X X X SHAR_EOF if test 6263 -ne "`wc -c < 'readme'`" then echo shar: error transmitting "'readme'" '(should have been 6263 characters)' fi fi if test -f 'scrn.c' then echo shar: will not over-write existing file "'scrn.c'" else echo extracting "'scrn.c'" sed 's/^X//' >scrn.c <<'SHAR_EOF' X/* Scrn.c replaces screen.c that was in the net distribution. Module contains X * PC screen routines plus whatever was in screen.c that had to be kept for X * linking with the other modules at link time. X */ X X#include <dos.h> X#include <stdlib.h> X#include <string.h> X#include "scrn.h" X X#define CNTL_H 0x08 X#define CNTL_U 0x15 X#define NL 0x0a X#define CR 0x0d X#define TAB 0x09 X#define BELL 0x07 X#define ROWS 25 X#define COLUMS 80 X X#define NOT_QUIET 0 /* Ring bell at eof and for errors */ X#define LITTLE_QUIET 1 /* Ring bell only for errors */ X#define VERY_QUIET 2 /* Never ring bell */ X Xextern int quiet; /* If VERY_QUIET, use visual bell for bell */ Xextern int scrn_in_color; /* When using color monitor */ Xint erase_char, kill_char; /* The user's erase and line-kill chars */ Xint sc_height, sc_width, se_width, ue_width, ul_width, bo_width, be_width, so_width; Xint auto_wrap, ignaw; Xvoid init(), dinit(), get_term(), home(), bell(), vbell(), add_line(); Xvoid lower_left(), clear(), clear_eol(), so_enter(), so_exit(), ul_enter(); Xvoid ul_exit(), bo_enter(), bo_exit(), backspace(), putbs(), raw_mode(); Xint ulflag = 0, boflag = 0; X Xcls() /* move cursor home and clear the screen */ X{ X union REGS REG; X int mode; X X gotoxy(0, 0); X REG.x.ax = 0x0600; X if (scrn_in_color == 1) X REG.h.bh = WHITE_ON_BLUE; X else X REG.h.bh = BW; X REG.x.cx = 0x0000; X REG.x.dx = 0x184f; X int86(0x10, ®, ®); X} X Xera_eol() X{ X union REGS REG; X int hold[4]; X int column; X X getxy(hold); X column = hold[1]; X if (scrn_in_color == 1) X REG.x.bx = WHITE_ON_BLUE; X else X REG.x.bx = BW; X REG.x.ax = 0x0900; /* ah = 10; al = null char to write */ X REG.x.cx = 80 - column; /* cx = no. of nulls to write */ X int86(0x10, ®, ®); X restorxy(hold); /* retore cursor to original position */ X return; X} X Xgotoxy(row, col) /* Position cursor at x,y on screen */ Xint row, col; X{ X union REGS REG; X X REG.h.ah = 02; X REG.h.bh = 00; X REG.h.dh = row; X REG.h.dl = col; X int86(0x10, ®, ®); X} X Xgetxy(hold) /* Get cursor coordinates */ Xint *hold; X{ X union REGS REG; X X REG.h.ah = 03; X REG.h.bh = 00; X int86(0x10, ®, ®); X hold[0] = REG.h.dh; X hold[1] = REG.h.dl; X hold[2] = REG.h.ch; X hold[3] = REG.h.cl; X} X X Xrestorxy(hold) /* Restore cursor gotten above */ Xint *hold; X{ X union REGS REG; X X gotoxy(hold[0], hold[1]); X REG.h.ah = 01; X REG.h.bh = 00; X REG.h.ch = hold[2]; X REG.h.cl = hold[3]; X int86(0x10, ®, ®); X} X Xcurs_r(n) /* move cursor right n places */ Xint n; X{ X int hold[4]; X int row, column; X X getxy(hold); X row = hold[0]; X column = hold[1]; X if (column < 0) X if (n < 0) X return(0); X if (column > 79) X if (n > 0) X return(0); X column = column + n; X gotoxy(row, column); X} X Xcurs_l(n) /* move cursor left n places */ Xint n; X{ X curs_r(-n); X} X Xscroll_up(n) Xint n; X{ X union REGS REG; X X REG.h.ah = 0x06; X if (scrn_in_color == 1) X REG.h.bh = WHITE_ON_BLUE; X else X REG.h.bh = BW; X REG.h.al = n; X REG.x.cx = 0x0000; X REG.x.dx = 256 * 24 + 79; X int86(0x10, ®, ®); X return(1); X} X Xget_mode() /* Check for Monochrome mode 7 */ X{ X union REGS REG; X X REG.h.ah = 15; X int86(0x10, ®, ®); X return(REG.h.al); X} X X X/* X * Set cursor checking for current cursor size parameters. X */ X Xset_cur() X{ X union REGS INREG, OUTREG; X X if (get_mode() == 7) X { X INREG.h.ah = 1; X INREG.h.bh = 0x00; X INREG.h.ch = 12; X INREG.h.cl = 13; X int86(0x10, &INREG, &OUTREG); X } X else X { X INREG.h.ah = 0x03; X INREG.h.bh = 0x00; X int86(0x10, &INREG, &OUTREG); X INREG.h.ah = 0x01; X INREG.h.bh = 0x00; X INREG.h.ch = OUTREG.h.ch; X INREG.h.cl = OUTREG.h.cl; X int86(0x10, &INREG, &OUTREG); X } X} X Xchr_put(c, attribute) Xint c; Xint attribute; X{ X union REGS REG; X int hold[4]; X int i, row, column; X X if (c == CR) X { X getxy(hold); X row = hold[0]; X column = 0; X gotoxy(row, column); X return(1); X } X if (c == TAB) X { X for (i = 0;i <= 7;++i) X chr_put(' ', attribute); X return(1); X } X if (c == BELL) X { X putch(7); X return(1); X } X if (c == NL) X { X getxy(hold); X row = hold[0]; X if (row >= 24) X scroll_up(1); X else X ++row; X column = 0; X gotoxy(row, column); X return(1); X } X REG.h.ah = 0x9; X REG.h.al = c; X if (ulflag || boflag) X REG.h.bl = (ulflag | boflag); X else X REG.h.bl = attribute; X REG.h.bh = 00; X REG.x.cx = 1; X int86(0x10, ®, ®); X curs_r(1); X return(REG.x.ax); X} X Xstr_put(str, attribute) Xchar *str; Xint attribute; X{ X int i; X X if (scrn_in_color == 1) X attribute = WHITE_ON_RED; X else X attribute = REV_VID; X for (i = 0;i < strlen(str);++i) X chr_put(*(str + i), attribute); X} X X X/* X * Add a blank line (called with cursor at home). X * Should scroll the display down. X */ X Xvoid Xadd_line() X{ X union REGS REG; X int hold[4]; X int row, column; X X REG.h.ah = 0x07; X if (scrn_in_color == 1) X REG.h.bh = WHITE_ON_BLUE; X else X REG.h.bh = BW; X REG.h.al = 1; X getxy(hold); X row = hold[0]; X column = hold[1]; X REG.h.ch = row; X REG.h.cl = 0; X REG.h.dh = 24; X REG.h.dl = 79; X int86(0x10, ®, ®); X} X X/* X * Below are the functions which perform all the "less terminal-specific" X * screen manipulation functions. They are taken from screen.c that was X * in the distribution of less on the news. X */ X X/* X * Initialize terminal X */ Xvoid Xinit() X{ X set_cur(); X} X X/* X * Deinitialize terminal X */ Xvoid Xdeinit() X{ X} X Xvoid Xget_term() X{ X sc_height = ROWS; X sc_width = COLUMS; X se_width = 0; X ue_width = 0; X ul_width = 0; X be_width = 0; X bo_width = 0; X so_width = 0; X auto_wrap = 0; /* chr_put doesn't autowrap */ X ignaw = 0; X /* sneak in kill and erase characters for command line editing */ X kill_char = CNTL_U; /* use ctrl-u as kill chararcter */ X erase_char = CNTL_H; /* use ctrl-h as erase character */ X} X Xvoid Xraw_mode(on) Xint on; X{ X /* left here in case there is a desire */ X /* to put terminal in raw_mode vs cooked */ X} X X/* X * Home cursor (move to upper left corner of screen). X */ X Xvoid Xhome() X{ X gotoxy(0, 0); X} X X/* X * Move cursor to lower left corner of screen. X */ Xvoid Xlower_left() X{ X gotoxy(24, 0); X} X X/* X * Ring the terminal bell. X */ Xvoid Xbell() X{ X if (quiet == VERY_QUIET) X vbell(); X else X putch(BELL); X} X X/* X * Output the "visual bell", if there is one. X */ Xvoid Xvbell() X{ X /* there is no visual bell at this time */ X return; X} X X/* X * Clear the screen. X */ Xvoid Xclear() X{ X cls(); X} X X/* X * Clear from the cursor to the end of the cursor's line. X * {{ This must not move the cursor. }} X */ Xvoid Xclear_eol() X{ X era_eol(); X} X X/* X * Begin "standout" (bold, underline, or whatever). X */ Xvoid Xso_enter() X{ X} X X/* X * End "standout". X */ Xvoid Xso_exit() X{ X} X/* X * Begin bold X*/ Xvoid Xbo_enter() X{ X boflag = BOLDFACE; X} X/* X * End bold X */ Xvoid Xbo_exit() X{ X boflag = 0; X} X X/* X * Begin "underline" (hopefully real underlining, X * otherwise whatever the terminal provides). X */ Xvoid Xul_enter() X{ X if (scrn_in_color == 1) X ulflag = COLOR_UL; X else X ulflag = UNDERLINE; X} X X/* X * End "underline". X */ Xvoid Xul_exit() X{ X ulflag = 0; X} X X/* X * Erase the character to the left of the cursor X * and move the cursor left. X */ Xvoid Xbackspace() X{ X /* X * Try to erase the previous character by overstriking with a space. X */ X curs_l(1); X putc(' '); X curs_l(1); X} X X/* X * Output a plain backspace, without erasing the previous char. X */ Xvoid Xputbs() X{ X curs_l(1); X} X SHAR_EOF if test 7306 -ne "`wc -c < 'scrn.c'`" then echo shar: error transmitting "'scrn.c'" '(should have been 7306 characters)' fi fi if test -f 'scrn.h' then echo shar: will not over-write existing file "'scrn.h'" else echo extracting "'scrn.h'" sed 's/^X//' >scrn.h <<'SHAR_EOF' X#define WHITE_ON_RED 0x47 X#define WHITE_ON_BLUE 0x17 X#define BW 0x07 X#define REV_VID 0x70 X#define BOLDFACE 0x0f X#define UNDERLINE 0x01 X#define COLOR_UL 0x06 X SHAR_EOF if test 160 -ne "`wc -c < 'scrn.h'`" then echo shar: error transmitting "'scrn.h'" '(should have been 160 characters)' fi fi if test -f 'signal.c' then echo shar: will not over-write existing file "'signal.c'" else echo extracting "'signal.c'" sed 's/^X//' >signal.c <<'SHAR_EOF' X/* X * Routines dealing with signals. X * X * A signal usually merely causes a bit to be set in the "signals" word. X * At some convenient time, the mainline code checks to see if any X * signals need processing by calling psignal(). X * An exception is made if we are reading from the keyboard when the X * signal is received. Some operating systems will simply call the X * signal handler and NOT return from the read (with EINTR). X * To handle this case, we service the interrupt directly from X * the handler if we are reading from the keyboard. X */ X X#include "less.h" X#ifdef MSDOS Xchar get_swchar(); Xvoid swchar_to_dos(); Xvoid swchar_to_unix(); X#include <process.h> X#include <dos.h> X#endif X Xextern char *first_cmd; Xint result; Xchar sw_char; Xpublic int sigs; X X/* X * Pass the specified command to a shell to be executed. X * Like plain "system()", but handles resetting terminal modes, etc. X */ Xvoid lsystem(cmd) X char *cmd; X{ X int inp; X X /* X * Print the command which is to be executed. X */ X lower_left(); X clear_eol(); X puts("!"); X puts(cmd); X puts("\n"); X X /* X * De-initialize the terminal and take out of raw mode. X */ X#ifndef MSDOS X deinit(); X flush(); X raw_mode(0); X#endif X /* X * Pass the command to the system to be executed. X */ X#ifndef MSDOS X inp = dup(0); X close(0); X open("/dev/tty", 0); X#endif X#ifdef MSDOS X sw_char = get_swchar(); X swchar_to_dos(); X result = system(cmd); X if (result != 0) X perror("Less"); X if (sw_char == '-') X swchar_to_unix(); X#else X system(cmd); X#endif X#ifndef MSDOS X close(0); X dup(inp); X close(inp); X#endif X /* X * Reset signals, raw mode, etc. X */ X#ifndef MSDOS X init_signals(); X raw_mode(1); X init(); X#endif X} X X#ifdef MSDOS Xchar Xget_swchar() X{ X union REGS inregs; X union REGS outregs; X X inregs.h.ah = 0x37; X inregs.h.al = 0; X intdos(&inregs, &outregs); X return(outregs.h.dl); X} X X Xvoid Xswchar_to_dos() X{ X union REGS inregs; X union REGS outregs; X X inregs.h.ah = 0x37; X inregs.h.al = 0x01; X inregs.h.dl = '/'; X intdos(&inregs, &outregs); X return; X} X Xvoid Xswchar_to_unix() X{ X union REGS inregs; X union REGS outregs; X X inregs.h.ah = 0x37; X inregs.h.al = 0x01; X inregs.h.dl = '-'; X intdos(&inregs, &outregs); X return; X} X X#endif X X SHAR_EOF if test 2167 -ne "`wc -c < 'signal.c'`" then echo shar: error transmitting "'signal.c'" '(should have been 2167 characters)' fi fi # end of shell archive exit 0 ----- No warranties whatsoever. Mike Slomin bellcore!lcuxa!mike2
mike2@lcuxa.UUCP (M S Slomin) (05/01/88)
And, here is part 3b: ---------------------CUT HERE (less source, part 3b)------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file # 3. Execute the file with /bin/sh (not csh) to create the files: # # regerror.c # regexp.c # regexp.h # regmagic.h # # Created on May 1, 1988 # if test -f 'regerror.c' then echo shar: will not over-write existing file "'regerror.c'" else echo extracting "'regerror.c'" sed 's/^X//' >regerror.c <<'SHAR_EOF' X#include <stdio.h> X Xvoid Xregerror(s) Xchar *s; X{ X#ifndef DOSPORT X#ifdef ERRAVAIL X error("regexp: %s", s); X#else X fprintf(stderr, "regexp(3): %s", s); X exit(1); X#endif X /* NOTREACHED */ X#endif /* ifdef'd out for less's sake when reporting error inside less */ X} SHAR_EOF if test 260 -ne "`wc -c < 'regerror.c'`" then echo shar: error transmitting "'regerror.c'" '(should have been 260 characters)' fi fi if test -f 'regexp.c' then echo shar: will not over-write existing file "'regexp.c'" else echo extracting "'regexp.c'" sed 's/^X//' >regexp.c <<'SHAR_EOF' X/* X * regcomp and regexec -- regsub and regerror are elsewhere X * X * Copyright (c) 1986 by University of Toronto. X * Written by Henry Spencer. Not derived from licensed software. X * X * Permission is granted to anyone to use this software for any X * purpose on any computer system, and to redistribute it freely, X * subject to the following restrictions: X * X * 1. The author is not responsible for the consequences of use of X * this software, no matter how awful, even if they arise X * from defects in it. X * X * 2. The origin of this software must not be misrepresented, either X * by explicit claim or by omission. X * X * 3. Altered versions must be plainly marked as such, and must not X * be misrepresented as being the original software. X * X * Beware that some of this code is subtly aware of the way operator X * precedence is structured in regular expressions. Serious changes in X * regular-expression syntax might require a total rethink. X */ X#include <stdio.h> X#include "regexp.h" X#include "regmagic.h" X X/* X * The "internal use only" fields in regexp.h are present to pass info from X * compile to execute that permits the execute phase to run lots faster on X * simple cases. They are: X * X * regstart char that must begin a match; '\0' if none obvious X * reganch is the match anchored (at beginning-of-line only)? X * regmust string (pointer into program) that match must include, or NULL X * regmlen length of regmust string X * X * Regstart and reganch permit very fast decisions on suitable starting points X * for a match, cutting down the work a lot. Regmust permits fast rejection X * of lines that cannot possibly match. The regmust tests are costly enough X * that regcomp() supplies a regmust only if the r.e. contains something X * potentially expensive (at present, the only such thing detected is * or + X * at the start of the r.e., which can involve a lot of backup). Regmlen is X * supplied because the test in regexec() needs it and regcomp() is computing X * it anyway. X */ X X/* X * Structure for regexp "program". This is essentially a linear encoding X * of a nondeterministic finite-state machine (aka syntax charts or X * "railroad normal form" in parsing technology). Each node is an opcode X * plus a "next" pointer, possibly plus an operand. "Next" pointers of X * all nodes except BRANCH implement concatenation; a "next" pointer with X * a BRANCH on both ends of it is connecting two alternatives. (Here we X * have one of the subtle syntax dependencies: an individual BRANCH (as X * opposed to a collection of them) is never concatenated with anything X * because of operator precedence.) The operand of some types of node is X * a literal string; for others, it is a node leading into a sub-FSM. In X * particular, the operand of a BRANCH node is the first node of the branch. X * (NB this is *not* a tree structure: the tail of the branch connects X * to the thing following the set of BRANCHes.) The opcodes are: X */ X X/* definition number opnd? meaning */ X#define END 0 /* no End of program. */ X#define BOL 1 /* no Match "" at beginning of line. */ X#define EOL 2 /* no Match "" at end of line. */ X#define ANY 3 /* no Match any one character. */ X#define ANYOF 4 /* str Match any character in this string. */ X#define ANYBUT 5 /* str Match any character not in this string. */ X#define BRANCH 6 /* node Match this alternative, or the next... */ X#define BACK 7 /* no Match "", "next" ptr points backward. */ X#define EXACTLY 8 /* str Match this string. */ X#define NOTHING 9 /* no Match empty string. */ X#define STAR 10 /* node Match this (simple) thing 0 or more times. */ X#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ X#define OPEN 20 /* no Mark this point in input as start of #n. */ X /* OPEN+1 is number 1, etc. */ X#define CLOSE 30 /* no Analogous to OPEN. */ X X/* X * Opcode notes: X * X * BRANCH The set of branches constituting a single choice are hooked X * together with their "next" pointers, since precedence prevents X * anything being concatenated to any individual branch. The X * "next" pointer of the last BRANCH in a choice points to the X * thing following the whole choice. This is also where the X * final "next" pointer of each individual branch points; each X * branch starts with the operand node of a BRANCH node. X * X * BACK Normal "next" pointers all implicitly point forward; BACK X * exists to make loop structures possible. X * X * STAR,PLUS '?', and complex '*' and '+', are implemented as circular X * BRANCH structures using BACK. Simple cases (one character X * per match) are implemented with STAR and PLUS for speed X * and to minimize recursive plunges. X * X * OPEN,CLOSE ...are numbered at compile time. X */ X X/* X * A node is one char of opcode followed by two chars of "next" pointer. X * "Next" pointers are stored as two 8-bit pieces, high order first. The X * value is a positive offset from the opcode of the node containing it. X * An operand, if any, simply follows the node. (Note that much of the X * code generation knows about this implicit relationship.) X * X * Using two bytes for the "next" pointer is vast overkill for most things, X * but allows patterns to get big without disasters. X */ X#define OP(p) (*(p)) X#define NEXT(p) (((*((p)+1)&0377)<<8) + *((p)+2)&0377) X#define OPERAND(p) ((p) + 3) X X/* X * See regmagic.h for one further detail of program structure. X */ X X X/* X * Utility definitions. X */ X#ifndef CHARBITS X#define UCHARAT(p) ((int)*(unsigned char *)(p)) X#else X#define UCHARAT(p) ((int)*(p)&CHARBITS) X#endif X X#define FAIL(m) { regerror(m); return(NULL); } X#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') X#define META "^$.[()|?+*\\" X X/* X * Flags to be passed up and down. X */ X#define HASWIDTH 01 /* Known never to match null string. */ X#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ X#define SPSTART 04 /* Starts with * or +. */ X#define WORST 0 /* Worst case. */ X X/* X * Global work variables for regcomp(). X */ Xstatic char *regparse; /* Input-scan pointer. */ Xstatic int regnpar; /* () count. */ Xstatic char regdummy; Xstatic char *regcode; /* Code-emit pointer; ®dummy = don't. */ Xstatic long regsize; /* Code size. */ X X/* X * Forward declarations for regcomp()'s friends. X */ X#ifndef STATIC X#define STATIC static X#endif XSTATIC char *reg(); XSTATIC char *regbranch(); XSTATIC char *regpiece(); XSTATIC char *regatom(); XSTATIC char *regnode(); XSTATIC char *regnext(); XSTATIC void regc(); XSTATIC void reginsert(); XSTATIC void regtail(); XSTATIC void regoptail(); X#ifdef STRCSPN XSTATIC int strcspn(); X#endif X X/* X - regcomp - compile a regular expression into internal code X * X * We can't allocate space until we know how big the compiled form will be, X * but we can't compile it (and thus know how big it is) until we've got a X * place to put the code. So we cheat: we compile it twice, once with code X * generation turned off and size counting turned on, and once "for real". X * This also means that we don't allocate space until we are sure that the X * thing really will compile successfully, and we never have to move the X * code and thus invalidate pointers into it. (Note that it has to be in X * one piece because free() must be able to free it all.) X * X * Beware that the optimization-preparation code in here knows about some X * of the structure of the compiled regexp. X */ Xregexp * Xregcomp(exp) Xchar *exp; X{ X register regexp *r; X register char *scan; X register char *longest; X register int len; X int flags; X extern char *malloc(); X X if (exp == NULL) X FAIL("NULL argument"); X X /* First pass: determine size, legality. */ X regparse = exp; X regnpar = 1; X regsize = 0L; X regcode = ®dummy; X regc(MAGIC); X if (reg(0, &flags) == NULL) X return(NULL); X X /* Small enough for pointer-storage convention? */ X if (regsize >= 32767L) /* Probably could be 65535L. */ X FAIL("regexp too big"); X X /* Allocate space. */ X r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize); X if (r == NULL) X FAIL("out of space"); X X /* Second pass: emit code. */ X regparse = exp; X regnpar = 1; X regcode = r->program; X regc(MAGIC); X if (reg(0, &flags) == NULL) X return(NULL); X X /* Dig out information for optimizations. */ X r->regstart = '\0'; /* Worst-case defaults. */ X r->reganch = 0; X r->regmust = NULL; X r->regmlen = 0; X scan = r->program+1; /* First BRANCH. */ X if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ X scan = OPERAND(scan); X X /* Starting-point info. */ X if (OP(scan) == EXACTLY) X r->regstart = *OPERAND(scan); X else if (OP(scan) == BOL) X r->reganch++; X X /* X * If there's something expensive in the r.e., find the X * longest literal string that must appear and make it the X * regmust. Resolve ties in favor of later strings, since X * the regstart check works with the beginning of the r.e. X * and avoiding duplication strengthens checking. Not a X * strong reason, but sufficient in the absence of others. X */ X if (flags&SPSTART) { X longest = NULL; X len = 0; X for (; scan != NULL; scan = regnext(scan)) X if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { X longest = OPERAND(scan); X len = strlen(OPERAND(scan)); X } X r->regmust = longest; X r->regmlen = len; X } X } X X return(r); X} X X/* X - reg - regular expression, i.e. main body or parenthesized thing X * X * Caller must absorb opening parenthesis. X * X * Combining parenthesis handling with the base level of regular expression X * is a trifle forced, but the need to tie the tails of the branches to what X * follows makes it hard to avoid. X */ Xstatic char * Xreg(paren, flagp) Xint paren; /* Parenthesized? */ Xint *flagp; X{ X register char *ret; X register char *br; X register char *ender; X register int parno; X int flags; X X *flagp = HASWIDTH; /* Tentatively. */ X X /* Make an OPEN node, if parenthesized. */ X if (paren) { X if (regnpar >= NSUBEXP) X FAIL("too many ()"); X parno = regnpar; X regnpar++; X ret = regnode(OPEN+parno); X } else X ret = NULL; X X /* Pick up the branches, linking them together. */ X br = regbranch(&flags); X if (br == NULL) X return(NULL); X if (ret != NULL) X regtail(ret, br); /* OPEN -> first. */ X else X ret = br; X if (!(flags&HASWIDTH)) X *flagp &= ~HASWIDTH; X *flagp |= flags&SPSTART; X while (*regparse == '|') { X regparse++; X br = regbranch(&flags); X if (br == NULL) X return(NULL); X regtail(ret, br); /* BRANCH -> BRANCH. */ X if (!(flags&HASWIDTH)) X *flagp &= ~HASWIDTH; X *flagp |= flags&SPSTART; X } X X /* Make a closing node, and hook it on the end. */ X ender = regnode((paren) ? CLOSE+parno : END); X regtail(ret, ender); X X /* Hook the tails of the branches to the closing node. */ X for (br = ret; br != NULL; br = regnext(br)) X regoptail(br, ender); X X /* Check for proper termination. */ X if (paren && *regparse++ != ')') { X FAIL("unmatched ()"); X } else if (!paren && *regparse != '\0') { X if (*regparse == ')') { X FAIL("unmatched ()"); X } else X FAIL("junk on end"); /* "Can't happen". */ X /* NOTREACHED */ X } X X return(ret); X} X X/* X - regbranch - one alternative of an | operator X * X * Implements the concatenation operator. X */ Xstatic char * Xregbranch(flagp) Xint *flagp; X{ X register char *ret; X register char *chain; X register char *latest; X int flags; X X *flagp = WORST; /* Tentatively. */ X X ret = regnode(BRANCH); X chain = NULL; X while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { X latest = regpiece(&flags); X if (latest == NULL) X return(NULL); X *flagp |= flags&HASWIDTH; X if (chain == NULL) /* First piece. */ X *flagp |= flags&SPSTART; X else X regtail(chain, latest); X chain = latest; X } X if (chain == NULL) /* Loop ran zero times. */ X (void) regnode(NOTHING); X X return(ret); X} X X/* X - regpiece - something followed by possible [*+?] X * X * Note that the branching code sequences used for ? and the general cases X * of * and + are somewhat optimized: they use the same NOTHING node as X * both the endmarker for their branch list and the body of the last branch. X * It might seem that this node could be dispensed with entirely, but the X * endmarker role is not redundant. X */ Xstatic char * Xregpiece(flagp) Xint *flagp; X{ X register char *ret; X register char op; X register char *next; X int flags; X X ret = regatom(&flags); X if (ret == NULL) X return(NULL); X X op = *regparse; X if (!ISMULT(op)) { X *flagp = flags; X return(ret); X } X X if (!(flags&HASWIDTH) && op != '?') X FAIL("*+ operand could be empty"); X *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); X X if (op == '*' && (flags&SIMPLE)) X reginsert(STAR, ret); X else if (op == '*') { X /* Emit x* as (x&|), where & means "self". */ X reginsert(BRANCH, ret); /* Either x */ X regoptail(ret, regnode(BACK)); /* and loop */ X regoptail(ret, ret); /* back */ X regtail(ret, regnode(BRANCH)); /* or */ X regtail(ret, regnode(NOTHING)); /* null. */ X } else if (op == '+' && (flags&SIMPLE)) X reginsert(PLUS, ret); X else if (op == '+') { X /* Emit x+ as x(&|), where & means "self". */ X next = regnode(BRANCH); /* Either */ X regtail(ret, next); X regtail(regnode(BACK), ret); /* loop back */ X regtail(next, regnode(BRANCH)); /* or */ X regtail(ret, regnode(NOTHING)); /* null. */ X } else if (op == '?') { X /* Emit x? as (x|) */ X reginsert(BRANCH, ret); /* Either x */ X regtail(ret, regnode(BRANCH)); /* or */ X next = regnode(NOTHING); /* null. */ X regtail(ret, next); X regoptail(ret, next); X } X regparse++; X if (ISMULT(*regparse)) X FAIL("nested *?+"); X X return(ret); X} X X/* X - regatom - the lowest level X * X * Optimization: gobbles an entire sequence of ordinary characters so that X * it can turn them into a single node, which is smaller to store and X * faster to run. Backslashed characters are exceptions, each becoming a X * separate node; the code is simpler that way and it's not worth fixing. X */ Xstatic char * Xregatom(flagp) Xint *flagp; X{ X register char *ret; X int flags; X X *flagp = WORST; /* Tentatively. */ X X switch (*regparse++) { X case '^': X ret = regnode(BOL); X break; X case '$': X ret = regnode(EOL); X break; X case '.': X ret = regnode(ANY); X *flagp |= HASWIDTH|SIMPLE; X break; X case '[': { X register int class; X register int classend; X X if (*regparse == '^') { /* Complement of range. */ X ret = regnode(ANYBUT); X regparse++; X } else X ret = regnode(ANYOF); X if (*regparse == ']' || *regparse == '-') X regc(*regparse++); X while (*regparse != '\0' && *regparse != ']') { X if (*regparse == '-') { X regparse++; X if (*regparse == ']' || *regparse == '\0') X regc('-'); X else { X class = UCHARAT(regparse-2)+1; X classend = UCHARAT(regparse); X if (class > classend+1) X FAIL("invalid [] range"); X for (; class <= classend; class++) X regc(class); X regparse++; X } X } else X regc(*regparse++); X } X regc('\0'); X if (*regparse != ']') X FAIL("unmatched []"); X regparse++; X *flagp |= HASWIDTH|SIMPLE; X } X break; X case '(': X ret = reg(1, &flags); X if (ret == NULL) X return(NULL); X *flagp |= flags&(HASWIDTH|SPSTART); X break; X case '\0': X case '|': X case ')': X FAIL("internal urp"); /* Supposed to be caught earlier. */ X break; X case '?': X case '+': X case '*': X FAIL("?+* follows nothing"); X break; X case '\\': X if (*regparse == '\0') X FAIL("trailing \\"); X ret = regnode(EXACTLY); X regc(*regparse++); X regc('\0'); X *flagp |= HASWIDTH|SIMPLE; X break; X default: { X register int len; X register char ender; X X regparse--; X len = strcspn(regparse, META); X if (len <= 0) X FAIL("internal disaster"); X ender = *(regparse+len); X if (len > 1 && ISMULT(ender)) X len--; /* Back off clear of ?+* operand. */ X *flagp |= HASWIDTH; X if (len == 1) X *flagp |= SIMPLE; X ret = regnode(EXACTLY); X while (len > 0) { X regc(*regparse++); X len--; X } X regc('\0'); X } X break; X } X X return(ret); X} X X/* X - regnode - emit a node X */ Xstatic char * /* Location. */ Xregnode(op) Xchar op; X{ X register char *ret; X register char *ptr; X X ret = regcode; X if (ret == ®dummy) { X regsize += 3; X return(ret); X } X X ptr = ret; X *ptr++ = op; X *ptr++ = '\0'; /* Null "next" pointer. */ X *ptr++ = '\0'; X regcode = ptr; X X return(ret); X} X X/* X - regc - emit (if appropriate) a byte of code X */ Xstatic void Xregc(b) Xchar b; X{ X if (regcode != ®dummy) X *regcode++ = b; X else X regsize++; X} X X/* X - reginsert - insert an operator in front of already-emitted operand X * X * Means relocating the operand. X */ Xstatic void Xreginsert(op, opnd) Xchar op; Xchar *opnd; X{ X register char *src; X register char *dst; X register char *place; X X if (regcode == ®dummy) { X regsize += 3; X return; X } X X src = regcode; X regcode += 3; X dst = regcode; X while (src > opnd) X *--dst = *--src; X X place = opnd; /* Op node, where operand used to be. */ X *place++ = op; X *place++ = '\0'; X *place++ = '\0'; X} X X/* X - regtail - set the next-pointer at the end of a node chain X */ Xstatic void Xregtail(p, val) Xchar *p; Xchar *val; X{ X register char *scan; X register char *temp; X register int offset; X X if (p == ®dummy) X return; X X /* Find last node. */ X scan = p; X for (;;) { X temp = regnext(scan); X if (temp == NULL) X break; X scan = temp; X } X X if (OP(scan) == BACK) X offset = scan - val; X else X offset = val - scan; X *(scan+1) = (offset>>8)&0377; X *(scan+2) = offset&0377; X} X X/* X - regoptail - regtail on operand of first argument; nop if operandless X */ Xstatic void Xregoptail(p, val) Xchar *p; Xchar *val; X{ X /* "Operandless" and "op != BRANCH" are synonymous in practice. */ X if (p == NULL || p == ®dummy || OP(p) != BRANCH) X return; X regtail(OPERAND(p), val); X} X X/* X * regexec and friends X */ X X/* X * Global work variables for regexec(). X */ Xstatic char *reginput; /* String-input pointer. */ Xstatic char *regbol; /* Beginning of input, for ^ check. */ Xstatic char **regstartp; /* Pointer to startp array. */ Xstatic char **regendp; /* Ditto for endp. */ X X/* X * Forwards. X */ XSTATIC int regtry(); XSTATIC int regmatch(); XSTATIC int regrepeat(); X X#ifdef DEBUG Xint regnarrate = 0; Xvoid regdump(); XSTATIC char *regprop(); X#endif X X/* X - regexec - match a regexp against a string X */ Xint Xregexec(prog, string) Xregister regexp *prog; Xregister char *string; X{ X register char *s; X extern char *strchr(); X X /* Be paranoid... */ X if (prog == NULL || string == NULL) { X regerror("NULL parameter"); X return(0); X } X X /* Check validity of program. */ X if (UCHARAT(prog->program) != MAGIC) { X regerror("corrupted program"); X return(0); X } X X /* If there is a "must appear" string, look for it. */ X if (prog->regmust != NULL) { X s = string; X while ((s = strchr(s, prog->regmust[0])) != NULL) { X if (strncmp(s, prog->regmust, prog->regmlen) == 0) X break; /* Found it. */ X s++; X } X if (s == NULL) /* Not present. */ X return(0); X } X X /* Mark beginning of line for ^ . */ X regbol = string; X X /* Simplest case: anchored match need be tried only once. */ X if (prog->reganch) X return(regtry(prog, string)); X X /* Messy cases: unanchored match. */ X s = string; X if (prog->regstart != '\0') X /* We know what char it must start with. */ X while ((s = strchr(s, prog->regstart)) != NULL) { X if (regtry(prog, s)) X return(1); X s++; X } X else X /* We don't -- general case. */ X do { X if (regtry(prog, s)) X return(1); X } while (*s++ != '\0'); X X /* Failure. */ X return(0); X} X X/* X - regtry - try match at specific point X */ Xstatic int /* 0 failure, 1 success */ Xregtry(prog, string) Xregexp *prog; Xchar *string; X{ X register int i; X register char **sp; X register char **ep; X X reginput = string; X regstartp = prog->startp; X regendp = prog->endp; X X sp = prog->startp; X ep = prog->endp; X for (i = NSUBEXP; i > 0; i--) { X *sp++ = NULL; X *ep++ = NULL; X } X if (regmatch(prog->program + 1)) { X prog->startp[0] = string; X prog->endp[0] = reginput; X return(1); X } else X return(0); X} X X/* X - regmatch - main matching routine X * X * Conceptually the strategy is simple: check to see whether the current X * node matches, call self recursively to see whether the rest matches, X * and then act accordingly. In practice we make some effort to avoid X * recursion, in particular by going through "ordinary" nodes (that don't X * need to know whether the rest of the match failed) by a loop instead of X * by recursion. X */ Xstatic int /* 0 failure, 1 success */ Xregmatch(prog) Xchar *prog; X{ X register char *scan; /* Current node. */ X char *next; /* Next node. */ X extern char *strchr(); X X scan = prog; X#ifdef DEBUG X if (scan != NULL && regnarrate) X fprintf(stderr, "%s(\n", regprop(scan)); X#endif X while (scan != NULL) { X#ifdef DEBUG X if (regnarrate) X fprintf(stderr, "%s...\n", regprop(scan)); X#endif X next = regnext(scan); X X switch (OP(scan)) { X case BOL: X if (reginput != regbol) X return(0); X break; X case EOL: X if (*reginput != '\0') X return(0); X break; X case ANY: X if (*reginput == '\0') X return(0); X reginput++; X break; X case EXACTLY: { X register int len; X register char *opnd; X X opnd = OPERAND(scan); X /* Inline the first character, for speed. */ X if (*opnd != *reginput) X return(0); X len = strlen(opnd); X if (len > 1 && strncmp(opnd, reginput, len) != 0) X return(0); X reginput += len; X } X break; X case ANYOF: X if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL) X return(0); X reginput++; X break; X case ANYBUT: X if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL) X return(0); X reginput++; X break; X case NOTHING: X break; X case BACK: X break; X case OPEN+1: X case OPEN+2: X case OPEN+3: X case OPEN+4: X case OPEN+5: X case OPEN+6: X case OPEN+7: X case OPEN+8: X case OPEN+9: { X register int no; X register char *save; X X no = OP(scan) - OPEN; X save = reginput; X X if (regmatch(next)) { X /* X * Don't set startp if some later X * invocation of the same parentheses X * already has. X */ X if (regstartp[no] == NULL) X regstartp[no] = save; X return(1); X } else X return(0); X } X break; X case CLOSE+1: X case CLOSE+2: X case CLOSE+3: X case CLOSE+4: X case CLOSE+5: X case CLOSE+6: X case CLOSE+7: X case CLOSE+8: X case CLOSE+9: { X register int no; X register char *save; X X no = OP(scan) - CLOSE; X save = reginput; X X if (regmatch(next)) { X /* X * Don't set endp if some later X * invocation of the same parentheses X * already has. X */ X if (regendp[no] == NULL) X regendp[no] = save; X return(1); X } else X return(0); X } X break; X case BRANCH: { X register char *save; X X if (OP(next) != BRANCH) /* No choice. */ X next = OPERAND(scan); /* Avoid recursion. */ X else { X do { X save = reginput; X if (regmatch(OPERAND(scan))) X return(1); X reginput = save; X scan = regnext(scan); X } while (scan != NULL && OP(scan) == BRANCH); X return(0); X /* NOTREACHED */ X } X } X break; X case STAR: X case PLUS: { X register char nextch; X register int no; X register char *save; X register int min; X X /* X * Lookahead to avoid useless match attempts X * when we know what character comes next. X */ X nextch = '\0'; X if (OP(next) == EXACTLY) X nextch = *OPERAND(next); X min = (OP(scan) == STAR) ? 0 : 1; X save = reginput; X no = regrepeat(OPERAND(scan)); X while (no >= min) { X /* If it could work, try it. */ X if (nextch == '\0' || *reginput == nextch) X if (regmatch(next)) X return(1); X /* Couldn't or didn't -- back up. */ X no--; X reginput = save + no; X } X return(0); X } X break; X case END: X return(1); /* Success! */ X break; X default: X regerror("memory corruption"); X return(0); X break; X } X X scan = next; X } X X /* X * We get here only if there's trouble -- normally "case END" is X * the terminating point. X */ X regerror("corrupted pointers"); X return(0); X} X X/* X - regrepeat - repeatedly match something simple, report how many X */ Xstatic int Xregrepeat(p) Xchar *p; X{ X register int count = 0; X register char *scan; X register char *opnd; X extern char * strchr(); X X scan = reginput; X opnd = OPERAND(p); X switch (OP(p)) { X case ANY: X count = strlen(scan); X scan += count; X break; X case EXACTLY: X while (*opnd == *scan) { X count++; X scan++; X } X break; X case ANYOF: X while (*scan != '\0' && strchr(opnd, *scan) != NULL) { X count++; X scan++; X } X break; X case ANYBUT: X while (*scan != '\0' && strchr(opnd, *scan) == NULL) { X count++; X scan++; X } X break; X default: /* Oh dear. Called inappropriately. */ X regerror("internal foulup"); X count = 0; /* Best compromise. */ X break; X } X reginput = scan; X X return(count); X} X X/* X - regnext - dig the "next" pointer out of a node X */ Xstatic char * Xregnext(p) Xregister char *p; X{ X register int offset; X X if (p == ®dummy) X return(NULL); X X offset = NEXT(p); X if (offset == 0) X return(NULL); X X if (OP(p) == BACK) X return(p-offset); X else X return(p+offset); X} X X#ifdef DEBUG X XSTATIC char *regprop(); X X/* X - regdump - dump a regexp onto stdout in vaguely comprehensible form X */ Xvoid Xregdump(r) Xregexp *r; X{ X register char *s; X register char op = EXACTLY; /* Arbitrary non-END op. */ X register char *next; X extern char *strchr(); X X X s = r->program + 1; X while (op != END) { /* While that wasn't END last time... */ X op = OP(s); X printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ X next = regnext(s); X if (next == NULL) /* Next ptr. */ X printf("(0)"); X else X printf("(%d)", (s-r->program)+(next-s)); X s += 3; X if (op == ANYOF || op == ANYBUT || op == EXACTLY) { X /* Literal string, where present. */ X while (*s != '\0') { X putchar(*s); X s++; X } X s++; X } X putchar('\n'); X } X X /* Header fields of interest. */ X if (r->regstart != '\0') X printf("start `%c' ", r->regstart); X if (r->reganch) X printf("anchored "); X if (r->regmust != NULL) X printf("must have \"%s\"", r->regmust); X printf("\n"); X} X X/* X - regprop - printable representation of opcode X */ Xstatic char * Xregprop(op) Xchar *op; X{ X register char *p; X static char buf[50]; X X (void) strcpy(buf, ":"); X X switch (OP(op)) { X case BOL: X p = "BOL"; X break; X case EOL: X p = "EOL"; X break; X case ANY: X p = "ANY"; X break; X case ANYOF: X p = "ANYOF"; X break; X case ANYBUT: X p = "ANYBUT"; X break; X case BRANCH: X p = "BRANCH"; X break; X case EXACTLY: X p = "EXACTLY"; X break; X case NOTHING: X p = "NOTHING"; X break; X case BACK: X p = "BACK"; X break; X case END: X p = "END"; X break; X case OPEN+1: X case OPEN+2: X case OPEN+3: X case OPEN+4: X case OPEN+5: X case OPEN+6: X case OPEN+7: X case OPEN+8: X case OPEN+9: X sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN); X p = NULL; X break; X case CLOSE+1: X case CLOSE+2: X case CLOSE+3: X case CLOSE+4: X case CLOSE+5: X case CLOSE+6: X case CLOSE+7: X case CLOSE+8: X case CLOSE+9: X sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE); X p = NULL; X break; X case STAR: X p = "STAR"; X break; X case PLUS: X p = "PLUS"; X break; X default: X regerror("corrupted opcode"); X break; X } X if (p != NULL) X (void) strcat(buf, p); X return(buf); X} X#endif X X/* X * The following is provided for those people who do not have strcspn() in X * their C libraries. They should get off their butts and do something X * about it; at least one public-domain implementation of those (highly X * useful) string routines has been published on Usenet. X */ X#ifdef STRCSPN X/* X * strcspn - find length of initial segment of s1 consisting entirely X * of characters not from s2 X */ X Xstatic int Xstrcspn(s1, s2) Xchar *s1; Xchar *s2; X{ X register char *scan1; X register char *scan2; X register int count; X X count = 0; X for (scan1 = s1; *scan1 != '\0'; scan1++) { X for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */ X if (*scan1 == *scan2++) X return(count); X count++; X } X return(count); X} X#endif SHAR_EOF if test 27639 -ne "`wc -c < 'regexp.c'`" then echo shar: error transmitting "'regexp.c'" '(should have been 27639 characters)' fi fi if test -f 'regexp.h' then echo shar: will not over-write existing file "'regexp.h'" else echo extracting "'regexp.h'" sed 's/^X//' >regexp.h <<'SHAR_EOF' X/* X * Definitions etc. for regexp(3) routines. X * X * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], X * not the System V one. X */ X#define NSUBEXP 10 Xtypedef struct regexp { X char *startp[NSUBEXP]; X char *endp[NSUBEXP]; X char regstart; /* Internal use only. */ X char reganch; /* Internal use only. */ X char *regmust; /* Internal use only. */ X int regmlen; /* Internal use only. */ X char program[1]; /* Unwarranted chumminess with compiler. */ X} regexp; X Xextern regexp *regcomp(); Xextern int regexec(); Xextern void regsub(); Xextern void regerror(); SHAR_EOF if test 574 -ne "`wc -c < 'regexp.h'`" then echo shar: error transmitting "'regexp.h'" '(should have been 574 characters)' fi fi if test -f 'regmagic.h' then echo shar: will not over-write existing file "'regmagic.h'" else echo extracting "'regmagic.h'" sed 's/^X//' >regmagic.h <<'SHAR_EOF' X/* X * The first byte of the regexp internal "program" is actually this magic X * number; the start node begins in the second byte. X */ X#define MAGIC 0234 SHAR_EOF if test 153 -ne "`wc -c < 'regmagic.h'`" then echo shar: error transmitting "'regmagic.h'" '(should have been 153 characters)' fi fi # end of shell archive exit 0 --- No warranties whatsoever. Mike Slomin bellcore!lcuxa!mike2
mike2@mrnet.mr.net (M S Slomin) (05/14/88)
[I don't think all sites received this before moderation, so here it is. This is a repost as described below for less; the other parts were posted before moderation and won't be repeated here. -- R.D.] I don't know why, but my 49K posting of part 3 of the less sources was apparently truncated at the majority of sites. (I did limit it to < 50K, which I thought was the limit for most mailers, but who knows?) Rather than continuing to mail it to those who request it, I've split the original posting in two, and am reposting it as part3a and part3b. Hope this solves the problem. -- No warranties whatsoever. Mike Slomin bellcore!lcuxa!mike2 ---------------------CUT HERE (less source, part 3a)------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file # 3. Execute the file with /bin/sh (not csh) to create the files: # # readme # scrn.c # scrn.h # signal.c # # Created on May 1, 1988 # if test -f 'readme' then echo shar: will not over-write existing file "'readme'" else echo extracting "'readme'" sed 's/^X//' >readme <<'SHAR_EOF' XPort of less to msdos: X XLess is a program similar to the Unix more command but is much more Xuseful. Read less.man for a good description of less and what it can Xdo for you. X XA. Dick Keily's original porting information for MSC 4.0: X XThe files included here will result in a full version of less Xfor use on msdos. This version of less has been tested on all the pc Xsystems that I could get my hands on. This includes monochrome, graphics, X(that includes systems with graphics cards and mono monitors), and color Xgraphics. X XThe following must be done to have less.exe work in its full glory: X X 1. Nothing if you don't want color when using a color monitor. X To get the color use the set command such as: X X set less=C (Case sensitive; c is another option) X X Do this prior to calling less. A command line option X less -C <filename ...> will also get you color. X X NOTE: Only use the color option when using a color monitor. X All other configurations need no special action. X X There are other less settings that can be used using the X set command. Read less.man. The one I use is less=mC on X an AT with enhanced monitor and ega card. X X 2. Less also has a v command that lets you can an editor from X within less to edit the file that you are viewing at the X time. The default is memacs which was taken from the Unix X mod.sources. If you don't have it use the dos set command X as follows to let less know which it is: X X set editor=your_editor X X 4. It is best to include the set commands in your autoexec.bat X which will be executed when you boot up your system. X X 5. Less is a very useful utility. Read less.man and when in X less the h command will put a description of the commands X on the screen for you. X X 6. This version of less is named less13.exe for distribution. X It replaces and other version that you have obtained from X my posting of the executable. Accept no substitutes. When X I get the time I will incorporate the changes to get up X the distributed Unix version 61. If you post this to any X bulletin board name any files associated with it with the X version 13.(referring to the MSDOS control). X X [N.B. No longer correct. This version is X somewhere between version 61 and 73 in its X capabilities.] X X 7. Less version 1.3 uses the system function but looks to see X if you are using the swchar program that allows you to use X the / rather than the \ character. The swchar state is changed X before the system call if you are and then resets it so that X it all becomes transparent to the user. X X [N.B. This is still correct.] X X 8. The source code for less is being posted to Net.sources. X It is all ifdef'd so that it should compile on Unix systems X also as is. X X [N.B. Probably not anymore. The ctrlbrk() X function will fail on Unix.] X X 9. Version 1.3 corrects the following: X The latest known to me fixes have been incorporated X in the regexpression routines written by Henry X Spencer and posted on the Unix news net. X X Dick Keily X Eastman Kodak Company X work: (716)477-1001 X unix: ...!seismo!rochester!kodak!keily X--------------------------------------------------------------------------- X--------------------------------------------------------------------------- X XB. Revised port to TurboC: X X 1. The port now incorporates boldface and underline (useful X for viewing documents created with nroff -man). I've X chosen the monochrome attributes for these, 0x01 and 0xf0, X and for CGA color, 0x06 (yellow) and 0xf0 (bright white). X If you wish to use others, revise the SCRN.H and/or SCRN.C X file(s) appropriately. X X 2. This will compile in the "tiny" model, and the resulting X file can be converted to a '.com' file for loading and X execution speed. An appropriate 'LESS.PRJ' file is X included; be certain to define DOSPORT in 'Options/Compiler' X (interactive mode). X X 3. A bug was fixed in the 'v' command (escape X to the editor): on return to less, the original unmodified X file in memory was displayed, not the edited one. This X is worked around in command.c by causing an 'E\n' command X to be executed on return. 'E' (Examine) loads in a new X file, and if no filename is given it loads in the previous X file. (Indeed, this is useful if 'E' is used normally. X After examining a different file, you can return to the X previous one by invoking 'E' with no file name.) X X 4. A new command, 'L', is implemented. 'L' gives X all of the information that '^G' and '=' give (i.e., X file name, number of bytes, approximate percentage into X the file) and in addition gives the number of the first X line appearing on the screen. This information is useful X if you wish to edit the file subsequently with a line- X oriented editor (e.g., vi, EDLIN, etc.); you know what X line to jump to. Unfortunately, it is a bit slow because X long ints are used in the calculation -- but a file might X exceed 65,000 lines!. X X 5. Sorry, I've not bothered to maintain downward compatibility X with Mr. Keily's MSC 4.0 port. I tried it both ways, and the X TurboC one is smaller and faster. If you want to do it with X MSC 4.0: (1) replace the ctrlbrk() command with an appropriate X signal() and interrupt handler; (2) compile it in the small X model; and (3) link with a /stack:4096 flag, linking in the X ssetargv.obj (small model) object file supplied by Microsoft X instead of the included setargv.c file. X X 6. If you haven't updated to TurboC 1.5, read the comments in X SETARGV.C. The variables _argc and _argv must be changed X throughout that file to __argc and __argv to work properly X in version 1.0. In any event, the SETARGV function is X needed only for DOS wildcard expansion, which is useful if X you want to specify multiple files for viewing with wildcards X and flip back and forth among them with 'N' and 'P'. If X you don't need the wildcard capability, omit SETARGV. X X 7. The public-domain REGEXP.C file supplied by Dick Keily X implements full regular expression matching a la X 'egrep' (i.e., using the '+' for one or more matches and X the '|' for disjunctive matches) in addition to the more X limited 'ed'/'sed' pattern match metacharacters. This is X useful, and better than classical Unix REGEXP. X X Mike Slomin X !bellcore!lcuxa!mike2 X X X SHAR_EOF if test 6263 -ne "`wc -c < 'readme'`" then echo shar: error transmitting "'readme'" '(should have been 6263 characters)' fi fi if test -f 'scrn.c' then echo shar: will not over-write existing file "'scrn.c'" else echo extracting "'scrn.c'" sed 's/^X//' >scrn.c <<'SHAR_EOF' X/* Scrn.c replaces screen.c that was in the net distribution. Module contains X * PC screen routines plus whatever was in screen.c that had to be kept for X * linking with the other modules at link time. X */ X X#include <dos.h> X#include <stdlib.h> X#include <string.h> X#include "scrn.h" X X#define CNTL_H 0x08 X#define CNTL_U 0x15 X#define NL 0x0a X#define CR 0x0d X#define TAB 0x09 X#define BELL 0x07 X#define ROWS 25 X#define COLUMS 80 X X#define NOT_QUIET 0 /* Ring bell at eof and for errors */ X#define LITTLE_QUIET 1 /* Ring bell only for errors */ X#define VERY_QUIET 2 /* Never ring bell */ X Xextern int quiet; /* If VERY_QUIET, use visual bell for bell */ Xextern int scrn_in_color; /* When using color monitor */ Xint erase_char, kill_char; /* The user's erase and line-kill chars */ Xint sc_height, sc_width, se_width, ue_width, ul_width, bo_width, be_width, so_width; Xint auto_wrap, ignaw; Xvoid init(), dinit(), get_term(), home(), bell(), vbell(), add_line(); Xvoid lower_left(), clear(), clear_eol(), so_enter(), so_exit(), ul_enter(); Xvoid ul_exit(), bo_enter(), bo_exit(), backspace(), putbs(), raw_mode(); Xint ulflag = 0, boflag = 0; X Xcls() /* move cursor home and clear the screen */ X{ X union REGS REG; X int mode; X X gotoxy(0, 0); X REG.x.ax = 0x0600; X if (scrn_in_color == 1) X REG.h.bh = WHITE_ON_BLUE; X else X REG.h.bh = BW; X REG.x.cx = 0x0000; X REG.x.dx = 0x184f; X int86(0x10, ®, ®); X} X Xera_eol() X{ X union REGS REG; X int hold[4]; X int column; X X getxy(hold); X column = hold[1]; X if (scrn_in_color == 1) X REG.x.bx = WHITE_ON_BLUE; X else X REG.x.bx = BW; X REG.x.ax = 0x0900; /* ah = 10; al = null char to write */ X REG.x.cx = 80 - column; /* cx = no. of nulls to write */ X int86(0x10, ®, ®); X restorxy(hold); /* retore cursor to original position */ X return; X} X Xgotoxy(row, col) /* Position cursor at x,y on screen */ Xint row, col; X{ X union REGS REG; X X REG.h.ah = 02; X REG.h.bh = 00; X REG.h.dh = row; X REG.h.dl = col; X int86(0x10, ®, ®); X} X Xgetxy(hold) /* Get cursor coordinates */ Xint *hold; X{ X union REGS REG; X X REG.h.ah = 03; X REG.h.bh = 00; X int86(0x10, ®, ®); X hold[0] = REG.h.dh; X hold[1] = REG.h.dl; X hold[2] = REG.h.ch; X hold[3] = REG.h.cl; X} X X Xrestorxy(hold) /* Restore cursor gotten above */ Xint *hold; X{ X union REGS REG; X X gotoxy(hold[0], hold[1]); X REG.h.ah = 01; X REG.h.bh = 00; X REG.h.ch = hold[2]; X REG.h.cl = hold[3]; X int86(0x10, ®, ®); X} X Xcurs_r(n) /* move cursor right n places */ Xint n; X{ X int hold[4]; X int row, column; X X getxy(hold); X row = hold[0]; X column = hold[1]; X if (column < 0) X if (n < 0) X return(0); X if (column > 79) X if (n > 0) X return(0); X column = column + n; X gotoxy(row, column); X} X Xcurs_l(n) /* move cursor left n places */ Xint n; X{ X curs_r(-n); X} X Xscroll_up(n) Xint n; X{ X union REGS REG; X X REG.h.ah = 0x06; X if (scrn_in_color == 1) X REG.h.bh = WHITE_ON_BLUE; X else X REG.h.bh = BW; X REG.h.al = n; X REG.x.cx = 0x0000; X REG.x.dx = 256 * 24 + 79; X int86(0x10, ®, ®); X return(1); X} X Xget_mode() /* Check for Monochrome mode 7 */ X{ X union REGS REG; X X REG.h.ah = 15; X int86(0x10, ®, ®); X return(REG.h.al); X} X X X/* X * Set cursor checking for current cursor size parameters. X */ X Xset_cur() X{ X union REGS INREG, OUTREG; X X if (get_mode() == 7) X { X INREG.h.ah = 1; X INREG.h.bh = 0x00; X INREG.h.ch = 12; X INREG.h.cl = 13; X int86(0x10, &INREG, &OUTREG); X } X else X { X INREG.h.ah = 0x03; X INREG.h.bh = 0x00; X int86(0x10, &INREG, &OUTREG); X INREG.h.ah = 0x01; X INREG.h.bh = 0x00; X INREG.h.ch = OUTREG.h.ch; X INREG.h.cl = OUTREG.h.cl; X int86(0x10, &INREG, &OUTREG); X } X} X Xchr_put(c, attribute) Xint c; Xint attribute; X{ X union REGS REG; X int hold[4]; X int i, row, column; X X if (c == CR) X { X getxy(hold); X row = hold[0]; X column = 0; X gotoxy(row, column); X return(1); X } X if (c == TAB) X { X for (i = 0;i <= 7;++i) X chr_put(' ', attribute); X return(1); X } X if (c == BELL) X { X putch(7); X return(1); X } X if (c == NL) X { X getxy(hold); X row = hold[0]; X if (row >= 24) X scroll_up(1); X else X ++row; X column = 0; X gotoxy(row, column); X return(1); X } X REG.h.ah = 0x9; X REG.h.al = c; X if (ulflag || boflag) X REG.h.bl = (ulflag | boflag); X else X REG.h.bl = attribute; X REG.h.bh = 00; X REG.x.cx = 1; X int86(0x10, ®, ®); X curs_r(1); X return(REG.x.ax); X} X Xstr_put(str, attribute) Xchar *str; Xint attribute; X{ X int i; X X if (scrn_in_color == 1) X attribute = WHITE_ON_RED; X else X attribute = REV_VID; X for (i = 0;i < strlen(str);++i) X chr_put(*(str + i), attribute); X} X X X/* X * Add a blank line (called with cursor at home). X * Should scroll the display down. X */ X Xvoid Xadd_line() X{ X union REGS REG; X int hold[4]; X int row, column; X X REG.h.ah = 0x07; X if (scrn_in_color == 1) X REG.h.bh = WHITE_ON_BLUE; X else X REG.h.bh = BW; X REG.h.al = 1; X getxy(hold); X row = hold[0]; X column = hold[1]; X REG.h.ch = row; X REG.h.cl = 0; X REG.h.dh = 24; X REG.h.dl = 79; X int86(0x10, ®, ®); X} X X/* X * Below are the functions which perform all the "less terminal-specific" X * screen manipulation functions. They are taken from screen.c that was X * in the distribution of less on the news. X */ X X/* X * Initialize terminal X */ Xvoid Xinit() X{ X set_cur(); X} X X/* X * Deinitialize terminal X */ Xvoid Xdeinit() X{ X} X Xvoid Xget_term() X{ X sc_height = ROWS; X sc_width = COLUMS; X se_width = 0; X ue_width = 0; X ul_width = 0; X be_width = 0; X bo_width = 0; X so_width = 0; X auto_wrap = 0; /* chr_put doesn't autowrap */ X ignaw = 0; X /* sneak in kill and erase characters for command line editing */ X kill_char = CNTL_U; /* use ctrl-u as kill chararcter */ X erase_char = CNTL_H; /* use ctrl-h as erase character */ X} X Xvoid Xraw_mode(on) Xint on; X{ X /* left here in case there is a desire */ X /* to put terminal in raw_mode vs cooked */ X} X X/* X * Home cursor (move to upper left corner of screen). X */ X Xvoid Xhome() X{ X gotoxy(0, 0); X} X X/* X * Move cursor to lower left corner of screen. X */ Xvoid Xlower_left() X{ X gotoxy(24, 0); X} X X/* X * Ring the terminal bell. X */ Xvoid Xbell() X{ X if (quiet == VERY_QUIET) X vbell(); X else X putch(BELL); X} X X/* X * Output the "visual bell", if there is one. X */ Xvoid Xvbell() X{ X /* there is no visual bell at this time */ X return; X} X X/* X * Clear the screen. X */ Xvoid Xclear() X{ X cls(); X} X X/* X * Clear from the cursor to the end of the cursor's line. X * {{ This must not move the cursor. }} X */ Xvoid Xclear_eol() X{ X era_eol(); X} X X/* X * Begin "standout" (bold, underline, or whatever). X */ Xvoid Xso_enter() X{ X} X X/* X * End "standout". X */ Xvoid Xso_exit() X{ X} X/* X * Begin bold X*/ Xvoid Xbo_enter() X{ X boflag = BOLDFACE; X} X/* X * End bold X */ Xvoid Xbo_exit() X{ X boflag = 0; X} X X/* X * Begin "underline" (hopefully real underlining, X * otherwise whatever the terminal provides). X */ Xvoid Xul_enter() X{ X if (scrn_in_color == 1) X ulflag = COLOR_UL; X else X ulflag = UNDERLINE; X} X X/* X * End "underline". X */ Xvoid Xul_exit() X{ X ulflag = 0; X} X X/* X * Erase the character to the left of the cursor X * and move the cursor left. X */ Xvoid Xbackspace() X{ X /* X * Try to erase the previous character by overstriking with a space. X */ X curs_l(1); X putc(' '); X curs_l(1); X} X X/* X * Output a plain backspace, without erasing the previous char. X */ Xvoid Xputbs() X{ X curs_l(1); X} X SHAR_EOF if test 7306 -ne "`wc -c < 'scrn.c'`" then echo shar: error transmitting "'scrn.c'" '(should have been 7306 characters)' fi fi if test -f 'scrn.h' then echo shar: will not over-write existing file "'scrn.h'" else echo extracting "'scrn.h'" sed 's/^X//' >scrn.h <<'SHAR_EOF' X#define WHITE_ON_RED 0x47 X#define WHITE_ON_BLUE 0x17 X#define BW 0x07 X#define REV_VID 0x70 X#define BOLDFACE 0x0f X#define UNDERLINE 0x01 X#define COLOR_UL 0x06 X SHAR_EOF if test 160 -ne "`wc -c < 'scrn.h'`" then echo shar: error transmitting "'scrn.h'" '(should have been 160 characters)' fi fi if test -f 'signal.c' then echo shar: will not over-write existing file "'signal.c'" else echo extracting "'signal.c'" sed 's/^X//' >signal.c <<'SHAR_EOF' X/* X * Routines dealing with signals. X * X * A signal usually merely causes a bit to be set in the "signals" word. X * At some convenient time, the mainline code checks to see if any X * signals need processing by calling psignal(). X * An exception is made if we are reading from the keyboard when the X * signal is received. Some operating systems will simply call the X * signal handler and NOT return from the read (with EINTR). X * To handle this case, we service the interrupt directly from X * the handler if we are reading from the keyboard. X */ X X#include "less.h" X#ifdef MSDOS Xchar get_swchar(); Xvoid swchar_to_dos(); Xvoid swchar_to_unix(); X#include <process.h> X#include <dos.h> X#endif X Xextern char *first_cmd; Xint result; Xchar sw_char; Xpublic int sigs; X X/* X * Pass the specified command to a shell to be executed. X * Like plain "system()", but handles resetting terminal modes, etc. X */ Xvoid lsystem(cmd) X char *cmd; X{ X int inp; X X /* X * Print the command which is to be executed. X */ X lower_left(); X clear_eol(); X puts("!"); X puts(cmd); X puts("\n"); X X /* X * De-initialize the terminal and take out of raw mode. X */ X#ifndef MSDOS X deinit(); X flush(); X raw_mode(0); X#endif X /* X * Pass the command to the system to be executed. X */ X#ifndef MSDOS X inp = dup(0); X close(0); X open("/dev/tty", 0); X#endif X#ifdef MSDOS X sw_char = get_swchar(); X swchar_to_dos(); X result = system(cmd); X if (result != 0) X perror("Less"); X if (sw_char == '-') X swchar_to_unix(); X#else X system(cmd); X#endif X#ifndef MSDOS X close(0); X dup(inp); X close(inp); X#endif X /* X * Reset signals, raw mode, etc. X */ X#ifndef MSDOS X init_signals(); X raw_mode(1); X init(); X#endif X} X X#ifdef MSDOS Xchar Xget_swchar() X{ X union REGS inregs; X union REGS outregs; X X inregs.h.ah = 0x37; X inregs.h.al = 0; X intdos(&inregs, &outregs); X return(outregs.h.dl); X} X X Xvoid Xswchar_to_dos() X{ X union REGS inregs; X union REGS outregs; X X inregs.h.ah = 0x37; X inregs.h.al = 0x01; X inregs.h.dl = '/'; X intdos(&inregs, &outregs); X return; X} X Xvoid Xswchar_to_unix() X{ X union REGS inregs; X union REGS outregs; X X inregs.h.ah = 0x37; X inregs.h.al = 0x01; X inregs.h.dl = '-'; X intdos(&inregs, &outregs); X return; X} X X#endif X X SHAR_EOF if test 2167 -ne "`wc -c < 'signal.c'`" then echo shar: error transmitting "'signal.c'" '(should have been 2167 characters)' fi fi # end of shell archive exit 0 -----
mike2@mrnet.mr.net (M S Slomin) (05/14/88)
[See part 3a for information about this. -- R.D.] ---------------------CUT HERE (less source, part 3b)------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file # 3. Execute the file with /bin/sh (not csh) to create the files: # # regerror.c # regexp.c # regexp.h # regmagic.h # # Created on May 1, 1988 # if test -f 'regerror.c' then echo shar: will not over-write existing file "'regerror.c'" else echo extracting "'regerror.c'" sed 's/^X//' >regerror.c <<'SHAR_EOF' X#include <stdio.h> X Xvoid Xregerror(s) Xchar *s; X{ X#ifndef DOSPORT X#ifdef ERRAVAIL X error("regexp: %s", s); X#else X fprintf(stderr, "regexp(3): %s", s); X exit(1); X#endif X /* NOTREACHED */ X#endif /* ifdef'd out for less's sake when reporting error inside less */ X} SHAR_EOF if test 260 -ne "`wc -c < 'regerror.c'`" then echo shar: error transmitting "'regerror.c'" '(should have been 260 characters)' fi fi if test -f 'regexp.c' then echo shar: will not over-write existing file "'regexp.c'" else echo extracting "'regexp.c'" sed 's/^X//' >regexp.c <<'SHAR_EOF' X/* X * regcomp and regexec -- regsub and regerror are elsewhere X * X * Copyright (c) 1986 by University of Toronto. X * Written by Henry Spencer. Not derived from licensed software. X * X * Permission is granted to anyone to use this software for any X * purpose on any computer system, and to redistribute it freely, X * subject to the following restrictions: X * X * 1. The author is not responsible for the consequences of use of X * this software, no matter how awful, even if they arise X * from defects in it. X * X * 2. The origin of this software must not be misrepresented, either X * by explicit claim or by omission. X * X * 3. Altered versions must be plainly marked as such, and must not X * be misrepresented as being the original software. X * X * Beware that some of this code is subtly aware of the way operator X * precedence is structured in regular expressions. Serious changes in X * regular-expression syntax might require a total rethink. X */ X#include <stdio.h> X#include "regexp.h" X#include "regmagic.h" X X/* X * The "internal use only" fields in regexp.h are present to pass info from X * compile to execute that permits the execute phase to run lots faster on X * simple cases. They are: X * X * regstart char that must begin a match; '\0' if none obvious X * reganch is the match anchored (at beginning-of-line only)? X * regmust string (pointer into program) that match must include, or NULL X * regmlen length of regmust string X * X * Regstart and reganch permit very fast decisions on suitable starting points X * for a match, cutting down the work a lot. Regmust permits fast rejection X * of lines that cannot possibly match. The regmust tests are costly enough X * that regcomp() supplies a regmust only if the r.e. contains something X * potentially expensive (at present, the only such thing detected is * or + X * at the start of the r.e., which can involve a lot of backup). Regmlen is X * supplied because the test in regexec() needs it and regcomp() is computing X * it anyway. X */ X X/* X * Structure for regexp "program". This is essentially a linear encoding X * of a nondeterministic finite-state machine (aka syntax charts or X * "railroad normal form" in parsing technology). Each node is an opcode X * plus a "next" pointer, possibly plus an operand. "Next" pointers of X * all nodes except BRANCH implement concatenation; a "next" pointer with X * a BRANCH on both ends of it is connecting two alternatives. (Here we X * have one of the subtle syntax dependencies: an individual BRANCH (as X * opposed to a collection of them) is never concatenated with anything X * because of operator precedence.) The operand of some types of node is X * a literal string; for others, it is a node leading into a sub-FSM. In X * particular, the operand of a BRANCH node is the first node of the branch. X * (NB this is *not* a tree structure: the tail of the branch connects X * to the thing following the set of BRANCHes.) The opcodes are: X */ X X/* definition number opnd? meaning */ X#define END 0 /* no End of program. */ X#define BOL 1 /* no Match "" at beginning of line. */ X#define EOL 2 /* no Match "" at end of line. */ X#define ANY 3 /* no Match any one character. */ X#define ANYOF 4 /* str Match any character in this string. */ X#define ANYBUT 5 /* str Match any character not in this string. */ X#define BRANCH 6 /* node Match this alternative, or the next... */ X#define BACK 7 /* no Match "", "next" ptr points backward. */ X#define EXACTLY 8 /* str Match this string. */ X#define NOTHING 9 /* no Match empty string. */ X#define STAR 10 /* node Match this (simple) thing 0 or more times. */ X#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ X#define OPEN 20 /* no Mark this point in input as start of #n. */ X /* OPEN+1 is number 1, etc. */ X#define CLOSE 30 /* no Analogous to OPEN. */ X X/* X * Opcode notes: X * X * BRANCH The set of branches constituting a single choice are hooked X * together with their "next" pointers, since precedence prevents X * anything being concatenated to any individual branch. The X * "next" pointer of the last BRANCH in a choice points to the X * thing following the whole choice. This is also where the X * final "next" pointer of each individual branch points; each X * branch starts with the operand node of a BRANCH node. X * X * BACK Normal "next" pointers all implicitly point forward; BACK X * exists to make loop structures possible. X * X * STAR,PLUS '?', and complex '*' and '+', are implemented as circular X * BRANCH structures using BACK. Simple cases (one character X * per match) are implemented with STAR and PLUS for speed X * and to minimize recursive plunges. X * X * OPEN,CLOSE ...are numbered at compile time. X */ X X/* X * A node is one char of opcode followed by two chars of "next" pointer. X * "Next" pointers are stored as two 8-bit pieces, high order first. The X * value is a positive offset from the opcode of the node containing it. X * An operand, if any, simply follows the node. (Note that much of the X * code generation knows about this implicit relationship.) X * X * Using two bytes for the "next" pointer is vast overkill for most things, X * but allows patterns to get big without disasters. X */ X#define OP(p) (*(p)) X#define NEXT(p) (((*((p)+1)&0377)<<8) + *((p)+2)&0377) X#define OPERAND(p) ((p) + 3) X X/* X * See regmagic.h for one further detail of program structure. X */ X X X/* X * Utility definitions. X */ X#ifndef CHARBITS X#define UCHARAT(p) ((int)*(unsigned char *)(p)) X#else X#define UCHARAT(p) ((int)*(p)&CHARBITS) X#endif X X#define FAIL(m) { regerror(m); return(NULL); } X#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') X#define META "^$.[()|?+*\\" X X/* X * Flags to be passed up and down. X */ X#define HASWIDTH 01 /* Known never to match null string. */ X#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ X#define SPSTART 04 /* Starts with * or +. */ X#define WORST 0 /* Worst case. */ X X/* X * Global work variables for regcomp(). X */ Xstatic char *regparse; /* Input-scan pointer. */ Xstatic int regnpar; /* () count. */ Xstatic char regdummy; Xstatic char *regcode; /* Code-emit pointer; ®dummy = don't. */ Xstatic long regsize; /* Code size. */ X X/* X * Forward declarations for regcomp()'s friends. X */ X#ifndef STATIC X#define STATIC static X#endif XSTATIC char *reg(); XSTATIC char *regbranch(); XSTATIC char *regpiece(); XSTATIC char *regatom(); XSTATIC char *regnode(); XSTATIC char *regnext(); XSTATIC void regc(); XSTATIC void reginsert(); XSTATIC void regtail(); XSTATIC void regoptail(); X#ifdef STRCSPN XSTATIC int strcspn(); X#endif X X/* X - regcomp - compile a regular expression into internal code X * X * We can't allocate space until we know how big the compiled form will be, X * but we can't compile it (and thus know how big it is) until we've got a X * place to put the code. So we cheat: we compile it twice, once with code X * generation turned off and size counting turned on, and once "for real". X * This also means that we don't allocate space until we are sure that the X * thing really will compile successfully, and we never have to move the X * code and thus invalidate pointers into it. (Note that it has to be in X * one piece because free() must be able to free it all.) X * X * Beware that the optimization-preparation code in here knows about some X * of the structure of the compiled regexp. X */ Xregexp * Xregcomp(exp) Xchar *exp; X{ X register regexp *r; X register char *scan; X register char *longest; X register int len; X int flags; X extern char *malloc(); X X if (exp == NULL) X FAIL("NULL argument"); X X /* First pass: determine size, legality. */ X regparse = exp; X regnpar = 1; X regsize = 0L; X regcode = ®dummy; X regc(MAGIC); X if (reg(0, &flags) == NULL) X return(NULL); X X /* Small enough for pointer-storage convention? */ X if (regsize >= 32767L) /* Probably could be 65535L. */ X FAIL("regexp too big"); X X /* Allocate space. */ X r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize); X if (r == NULL) X FAIL("out of space"); X X /* Second pass: emit code. */ X regparse = exp; X regnpar = 1; X regcode = r->program; X regc(MAGIC); X if (reg(0, &flags) == NULL) X return(NULL); X X /* Dig out information for optimizations. */ X r->regstart = '\0'; /* Worst-case defaults. */ X r->reganch = 0; X r->regmust = NULL; X r->regmlen = 0; X scan = r->program+1; /* First BRANCH. */ X if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ X scan = OPERAND(scan); X X /* Starting-point info. */ X if (OP(scan) == EXACTLY) X r->regstart = *OPERAND(scan); X else if (OP(scan) == BOL) X r->reganch++; X X /* X * If there's something expensive in the r.e., find the X * longest literal string that must appear and make it the X * regmust. Resolve ties in favor of later strings, since X * the regstart check works with the beginning of the r.e. X * and avoiding duplication strengthens checking. Not a X * strong reason, but sufficient in the absence of others. X */ X if (flags&SPSTART) { X longest = NULL; X len = 0; X for (; scan != NULL; scan = regnext(scan)) X if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { X longest = OPERAND(scan); X len = strlen(OPERAND(scan)); X } X r->regmust = longest; X r->regmlen = len; X } X } X X return(r); X} X X/* X - reg - regular expression, i.e. main body or parenthesized thing X * X * Caller must absorb opening parenthesis. X * X * Combining parenthesis handling with the base level of regular expression X * is a trifle forced, but the need to tie the tails of the branches to what X * follows makes it hard to avoid. X */ Xstatic char * Xreg(paren, flagp) Xint paren; /* Parenthesized? */ Xint *flagp; X{ X register char *ret; X register char *br; X register char *ender; X register int parno; X int flags; X X *flagp = HASWIDTH; /* Tentatively. */ X X /* Make an OPEN node, if parenthesized. */ X if (paren) { X if (regnpar >= NSUBEXP) X FAIL("too many ()"); X parno = regnpar; X regnpar++; X ret = regnode(OPEN+parno); X } else X ret = NULL; X X /* Pick up the branches, linking them together. */ X br = regbranch(&flags); X if (br == NULL) X return(NULL); X if (ret != NULL) X regtail(ret, br); /* OPEN -> first. */ X else X ret = br; X if (!(flags&HASWIDTH)) X *flagp &= ~HASWIDTH; X *flagp |= flags&SPSTART; X while (*regparse == '|') { X regparse++; X br = regbranch(&flags); X if (br == NULL) X return(NULL); X regtail(ret, br); /* BRANCH -> BRANCH. */ X if (!(flags&HASWIDTH)) X *flagp &= ~HASWIDTH; X *flagp |= flags&SPSTART; X } X X /* Make a closing node, and hook it on the end. */ X ender = regnode((paren) ? CLOSE+parno : END); X regtail(ret, ender); X X /* Hook the tails of the branches to the closing node. */ X for (br = ret; br != NULL; br = regnext(br)) X regoptail(br, ender); X X /* Check for proper termination. */ X if (paren && *regparse++ != ')') { X FAIL("unmatched ()"); X } else if (!paren && *regparse != '\0') { X if (*regparse == ')') { X FAIL("unmatched ()"); X } else X FAIL("junk on end"); /* "Can't happen". */ X /* NOTREACHED */ X } X X return(ret); X} X X/* X - regbranch - one alternative of an | operator X * X * Implements the concatenation operator. X */ Xstatic char * Xregbranch(flagp) Xint *flagp; X{ X register char *ret; X register char *chain; X register char *latest; X int flags; X X *flagp = WORST; /* Tentatively. */ X X ret = regnode(BRANCH); X chain = NULL; X while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { X latest = regpiece(&flags); X if (latest == NULL) X return(NULL); X *flagp |= flags&HASWIDTH; X if (chain == NULL) /* First piece. */ X *flagp |= flags&SPSTART; X else X regtail(chain, latest); X chain = latest; X } X if (chain == NULL) /* Loop ran zero times. */ X (void) regnode(NOTHING); X X return(ret); X} X X/* X - regpiece - something followed by possible [*+?] X * X * Note that the branching code sequences used for ? and the general cases X * of * and + are somewhat optimized: they use the same NOTHING node as X * both the endmarker for their branch list and the body of the last branch. X * It might seem that this node could be dispensed with entirely, but the X * endmarker role is not redundant. X */ Xstatic char * Xregpiece(flagp) Xint *flagp; X{ X register char *ret; X register char op; X register char *next; X int flags; X X ret = regatom(&flags); X if (ret == NULL) X return(NULL); X X op = *regparse; X if (!ISMULT(op)) { X *flagp = flags; X return(ret); X } X X if (!(flags&HASWIDTH) && op != '?') X FAIL("*+ operand could be empty"); X *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); X X if (op == '*' && (flags&SIMPLE)) X reginsert(STAR, ret); X else if (op == '*') { X /* Emit x* as (x&|), where & means "self". */ X reginsert(BRANCH, ret); /* Either x */ X regoptail(ret, regnode(BACK)); /* and loop */ X regoptail(ret, ret); /* back */ X regtail(ret, regnode(BRANCH)); /* or */ X regtail(ret, regnode(NOTHING)); /* null. */ X } else if (op == '+' && (flags&SIMPLE)) X reginsert(PLUS, ret); X else if (op == '+') { X /* Emit x+ as x(&|), where & means "self". */ X next = regnode(BRANCH); /* Either */ X regtail(ret, next); X regtail(regnode(BACK), ret); /* loop back */ X regtail(next, regnode(BRANCH)); /* or */ X regtail(ret, regnode(NOTHING)); /* null. */ X } else if (op == '?') { X /* Emit x? as (x|) */ X reginsert(BRANCH, ret); /* Either x */ X regtail(ret, regnode(BRANCH)); /* or */ X next = regnode(NOTHING); /* null. */ X regtail(ret, next); X regoptail(ret, next); X } X regparse++; X if (ISMULT(*regparse)) X FAIL("nested *?+"); X X return(ret); X} X X/* X - regatom - the lowest level X * X * Optimization: gobbles an entire sequence of ordinary characters so that X * it can turn them into a single node, which is smaller to store and X * faster to run. Backslashed characters are exceptions, each becoming a X * separate node; the code is simpler that way and it's not worth fixing. X */ Xstatic char * Xregatom(flagp) Xint *flagp; X{ X register char *ret; X int flags; X X *flagp = WORST; /* Tentatively. */ X X switch (*regparse++) { X case '^': X ret = regnode(BOL); X break; X case '$': X ret = regnode(EOL); X break; X case '.': X ret = regnode(ANY); X *flagp |= HASWIDTH|SIMPLE; X break; X case '[': { X register int class; X register int classend; X X if (*regparse == '^') { /* Complement of range. */ X ret = regnode(ANYBUT); X regparse++; X } else X ret = regnode(ANYOF); X if (*regparse == ']' || *regparse == '-') X regc(*regparse++); X while (*regparse != '\0' && *regparse != ']') { X if (*regparse == '-') { X regparse++; X if (*regparse == ']' || *regparse == '\0') X regc('-'); X else { X class = UCHARAT(regparse-2)+1; X classend = UCHARAT(regparse); X if (class > classend+1) X FAIL("invalid [] range"); X for (; class <= classend; class++) X regc(class); X regparse++; X } X } else X regc(*regparse++); X } X regc('\0'); X if (*regparse != ']') X FAIL("unmatched []"); X regparse++; X *flagp |= HASWIDTH|SIMPLE; X } X break; X case '(': X ret = reg(1, &flags); X if (ret == NULL) X return(NULL); X *flagp |= flags&(HASWIDTH|SPSTART); X break; X case '\0': X case '|': X case ')': X FAIL("internal urp"); /* Supposed to be caught earlier. */ X break; X case '?': X case '+': X case '*': X FAIL("?+* follows nothing"); X break; X case '\\': X if (*regparse == '\0') X FAIL("trailing \\"); X ret = regnode(EXACTLY); X regc(*regparse++); X regc('\0'); X *flagp |= HASWIDTH|SIMPLE; X break; X default: { X register int len; X register char ender; X X regparse--; X len = strcspn(regparse, META); X if (len <= 0) X FAIL("internal disaster"); X ender = *(regparse+len); X if (len > 1 && ISMULT(ender)) X len--; /* Back off clear of ?+* operand. */ X *flagp |= HASWIDTH; X if (len == 1) X *flagp |= SIMPLE; X ret = regnode(EXACTLY); X while (len > 0) { X regc(*regparse++); X len--; X } X regc('\0'); X } X break; X } X X return(ret); X} X X/* X - regnode - emit a node X */ Xstatic char * /* Location. */ Xregnode(op) Xchar op; X{ X register char *ret; X register char *ptr; X X ret = regcode; X if (ret == ®dummy) { X regsize += 3; X return(ret); X } X X ptr = ret; X *ptr++ = op; X *ptr++ = '\0'; /* Null "next" pointer. */ X *ptr++ = '\0'; X regcode = ptr; X X return(ret); X} X X/* X - regc - emit (if appropriate) a byte of code X */ Xstatic void Xregc(b) Xchar b; X{ X if (regcode != ®dummy) X *regcode++ = b; X else X regsize++; X} X X/* X - reginsert - insert an operator in front of already-emitted operand X * X * Means relocating the operand. X */ Xstatic void Xreginsert(op, opnd) Xchar op; Xchar *opnd; X{ X register char *src; X register char *dst; X register char *place; X X if (regcode == ®dummy) { X regsize += 3; X return; X } X X src = regcode; X regcode += 3; X dst = regcode; X while (src > opnd) X *--dst = *--src; X X place = opnd; /* Op node, where operand used to be. */ X *place++ = op; X *place++ = '\0'; X *place++ = '\0'; X} X X/* X - regtail - set the next-pointer at the end of a node chain X */ Xstatic void Xregtail(p, val) Xchar *p; Xchar *val; X{ X register char *scan; X register char *temp; X register int offset; X X if (p == ®dummy) X return; X X /* Find last node. */ X scan = p; X for (;;) { X temp = regnext(scan); X if (temp == NULL) X break; X scan = temp; X } X X if (OP(scan) == BACK) X offset = scan - val; X else X offset = val - scan; X *(scan+1) = (offset>>8)&0377; X *(scan+2) = offset&0377; X} X X/* X - regoptail - regtail on operand of first argument; nop if operandless X */ Xstatic void Xregoptail(p, val) Xchar *p; Xchar *val; X{ X /* "Operandless" and "op != BRANCH" are synonymous in practice. */ X if (p == NULL || p == ®dummy || OP(p) != BRANCH) X return; X regtail(OPERAND(p), val); X} X X/* X * regexec and friends X */ X X/* X * Global work variables for regexec(). X */ Xstatic char *reginput; /* String-input pointer. */ Xstatic char *regbol; /* Beginning of input, for ^ check. */ Xstatic char **regstartp; /* Pointer to startp array. */ Xstatic char **regendp; /* Ditto for endp. */ X X/* X * Forwards. X */ XSTATIC int regtry(); XSTATIC int regmatch(); XSTATIC int regrepeat(); X X#ifdef DEBUG Xint regnarrate = 0; Xvoid regdump(); XSTATIC char *regprop(); X#endif X X/* X - regexec - match a regexp against a string X */ Xint Xregexec(prog, string) Xregister regexp *prog; Xregister char *string; X{ X register char *s; X extern char *strchr(); X X /* Be paranoid... */ X if (prog == NULL || string == NULL) { X regerror("NULL parameter"); X return(0); X } X X /* Check validity of program. */ X if (UCHARAT(prog->program) != MAGIC) { X regerror("corrupted program"); X return(0); X } X X /* If there is a "must appear" string, look for it. */ X if (prog->regmust != NULL) { X s = string; X while ((s = strchr(s, prog->regmust[0])) != NULL) { X if (strncmp(s, prog->regmust, prog->regmlen) == 0) X break; /* Found it. */ X s++; X } X if (s == NULL) /* Not present. */ X return(0); X } X X /* Mark beginning of line for ^ . */ X regbol = string; X X /* Simplest case: anchored match need be tried only once. */ X if (prog->reganch) X return(regtry(prog, string)); X X /* Messy cases: unanchored match. */ X s = string; X if (prog->regstart != '\0') X /* We know what char it must start with. */ X while ((s = strchr(s, prog->regstart)) != NULL) { X if (regtry(prog, s)) X return(1); X s++; X } X else X /* We don't -- general case. */ X do { X if (regtry(prog, s)) X return(1); X } while (*s++ != '\0'); X X /* Failure. */ X return(0); X} X X/* X - regtry - try match at specific point X */ Xstatic int /* 0 failure, 1 success */ Xregtry(prog, string) Xregexp *prog; Xchar *string; X{ X register int i; X register char **sp; X register char **ep; X X reginput = string; X regstartp = prog->startp; X regendp = prog->endp; X X sp = prog->startp; X ep = prog->endp; X for (i = NSUBEXP; i > 0; i--) { X *sp++ = NULL; X *ep++ = NULL; X } X if (regmatch(prog->program + 1)) { X prog->startp[0] = string; X prog->endp[0] = reginput; X return(1); X } else X return(0); X} X X/* X - regmatch - main matching routine X * X * Conceptually the strategy is simple: check to see whether the current X * node matches, call self recursively to see whether the rest matches, X * and then act accordingly. In practice we make some effort to avoid X * recursion, in particular by going through "ordinary" nodes (that don't X * need to know whether the rest of the match failed) by a loop instead of X * by recursion. X */ Xstatic int /* 0 failure, 1 success */ Xregmatch(prog) Xchar *prog; X{ X register char *scan; /* Current node. */ X char *next; /* Next node. */ X extern char *strchr(); X X scan = prog; X#ifdef DEBUG X if (scan != NULL && regnarrate) X fprintf(stderr, "%s(\n", regprop(scan)); X#endif X while (scan != NULL) { X#ifdef DEBUG X if (regnarrate) X fprintf(stderr, "%s...\n", regprop(scan)); X#endif X next = regnext(scan); X X switch (OP(scan)) { X case BOL: X if (reginput != regbol) X return(0); X break; X case EOL: X if (*reginput != '\0') X return(0); X break; X case ANY: X if (*reginput == '\0') X return(0); X reginput++; X break; X case EXACTLY: { X register int len; X register char *opnd; X X opnd = OPERAND(scan); X /* Inline the first character, for speed. */ X if (*opnd != *reginput) X return(0); X len = strlen(opnd); X if (len > 1 && strncmp(opnd, reginput, len) != 0) X return(0); X reginput += len; X } X break; X case ANYOF: X if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL) X return(0); X reginput++; X break; X case ANYBUT: X if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL) X return(0); X reginput++; X break; X case NOTHING: X break; X case BACK: X break; X case OPEN+1: X case OPEN+2: X case OPEN+3: X case OPEN+4: X case OPEN+5: X case OPEN+6: X case OPEN+7: X case OPEN+8: X case OPEN+9: { X register int no; X register char *save; X X no = OP(scan) - OPEN; X save = reginput; X X if (regmatch(next)) { X /* X * Don't set startp if some later X * invocation of the same parentheses X * already has. X */ X if (regstartp[no] == NULL) X regstartp[no] = save; X return(1); X } else X return(0); X } X break; X case CLOSE+1: X case CLOSE+2: X case CLOSE+3: X case CLOSE+4: X case CLOSE+5: X case CLOSE+6: X case CLOSE+7: X case CLOSE+8: X case CLOSE+9: { X register int no; X register char *save; X X no = OP(scan) - CLOSE; X save = reginput; X X if (regmatch(next)) { X /* X * Don't set endp if some later X * invocation of the same parentheses X * already has. X */ X if (regendp[no] == NULL) X regendp[no] = save; X return(1); X } else X return(0); X } X break; X case BRANCH: { X register char *save; X X if (OP(next) != BRANCH) /* No choice. */ X next = OPERAND(scan); /* Avoid recursion. */ X else { X do { X save = reginput; X if (regmatch(OPERAND(scan))) X return(1); X reginput = save; X scan = regnext(scan); X } while (scan != NULL && OP(scan) == BRANCH); X return(0); X /* NOTREACHED */ X } X } X break; X case STAR: X case PLUS: { X register char nextch; X register int no; X register char *save; X register int min; X X /* X * Lookahead to avoid useless match attempts X * when we know what character comes next. X */ X nextch = '\0'; X if (OP(next) == EXACTLY) X nextch = *OPERAND(next); X min = (OP(scan) == STAR) ? 0 : 1; X save = reginput; X no = regrepeat(OPERAND(scan)); X while (no >= min) { X /* If it could work, try it. */ X if (nextch == '\0' || *reginput == nextch) X if (regmatch(next)) X return(1); X /* Couldn't or didn't -- back up. */ X no--; X reginput = save + no; X } X return(0); X } X break; X case END: X return(1); /* Success! */ X break; X default: X regerror("memory corruption"); X return(0); X break; X } X X scan = next; X } X X /* X * We get here only if there's trouble -- normally "case END" is X * the terminating point. X */ X regerror("corrupted pointers"); X return(0); X} X X/* X - regrepeat - repeatedly match something simple, report how many X */ Xstatic int Xregrepeat(p) Xchar *p; X{ X register int count = 0; X register char *scan; X register char *opnd; X extern char * strchr(); X X scan = reginput; X opnd = OPERAND(p); X switch (OP(p)) { X case ANY: X count = strlen(scan); X scan += count; X break; X case EXACTLY: X while (*opnd == *scan) { X count++; X scan++; X } X break; X case ANYOF: X while (*scan != '\0' && strchr(opnd, *scan) != NULL) { X count++; X scan++; X } X break; X case ANYBUT: X while (*scan != '\0' && strchr(opnd, *scan) == NULL) { X count++; X scan++; X } X break; X default: /* Oh dear. Called inappropriately. */ X regerror("internal foulup"); X count = 0; /* Best compromise. */ X break; X } X reginput = scan; X X return(count); X} X X/* X - regnext - dig the "next" pointer out of a node X */ Xstatic char * Xregnext(p) Xregister char *p; X{ X register int offset; X X if (p == ®dummy) X return(NULL); X X offset = NEXT(p); X if (offset == 0) X return(NULL); X X if (OP(p) == BACK) X return(p-offset); X else X return(p+offset); X} X X#ifdef DEBUG X XSTATIC char *regprop(); X X/* X - regdump - dump a regexp onto stdout in vaguely comprehensible form X */ Xvoid Xregdump(r) Xregexp *r; X{ X register char *s; X register char op = EXACTLY; /* Arbitrary non-END op. */ X register char *next; X extern char *strchr(); X X X s = r->program + 1; X while (op != END) { /* While that wasn't END last time... */ X op = OP(s); X printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ X next = regnext(s); X if (next == NULL) /* Next ptr. */ X printf("(0)"); X else X printf("(%d)", (s-r->program)+(next-s)); X s += 3; X if (op == ANYOF || op == ANYBUT || op == EXACTLY) { X /* Literal string, where present. */ X while (*s != '\0') { X putchar(*s); X s++; X } X s++; X } X putchar('\n'); X } X X /* Header fields of interest. */ X if (r->regstart != '\0') X printf("start `%c' ", r->regstart); X if (r->reganch) X printf("anchored "); X if (r->regmust != NULL) X printf("must have \"%s\"", r->regmust); X printf("\n"); X} X X/* X - regprop - printable representation of opcode X */ Xstatic char * Xregprop(op) Xchar *op; X{ X register char *p; X static char buf[50]; X X (void) strcpy(buf, ":"); X X switch (OP(op)) { X case BOL: X p = "BOL"; X break; X case EOL: X p = "EOL"; X break; X case ANY: X p = "ANY"; X break; X case ANYOF: X p = "ANYOF"; X break; X case ANYBUT: X p = "ANYBUT"; X break; X case BRANCH: X p = "BRANCH"; X break; X case EXACTLY: X p = "EXACTLY"; X break; X case NOTHING: X p = "NOTHING"; X break; X case BACK: X p = "BACK"; X break; X case END: X p = "END"; X break; X case OPEN+1: X case OPEN+2: X case OPEN+3: X case OPEN+4: X case OPEN+5: X case OPEN+6: X case OPEN+7: X case OPEN+8: X case OPEN+9: X sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN); X p = NULL; X break; X case CLOSE+1: X case CLOSE+2: X case CLOSE+3: X case CLOSE+4: X case CLOSE+5: X case CLOSE+6: X case CLOSE+7: X case CLOSE+8: X case CLOSE+9: X sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE); X p = NULL; X break; X case STAR: X p = "STAR"; X break; X case PLUS: X p = "PLUS"; X break; X default: X regerror("corrupted opcode"); X break; X } X if (p != NULL) X (void) strcat(buf, p); X return(buf); X} X#endif X X/* X * The following is provided for those people who do not have strcspn() in X * their C libraries. They should get off their butts and do something X * about it; at least one public-domain implementation of those (highly X * useful) string routines has been published on Usenet. X */ X#ifdef STRCSPN X/* X * strcspn - find length of initial segment of s1 consisting entirely X * of characters not from s2 X */ X Xstatic int Xstrcspn(s1, s2) Xchar *s1; Xchar *s2; X{ X register char *scan1; X register char *scan2; X register int count; X X count = 0; X for (scan1 = s1; *scan1 != '\0'; scan1++) { X for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */ X if (*scan1 == *scan2++) X return(count); X count++; X } X return(count); X} X#endif SHAR_EOF if test 27639 -ne "`wc -c < 'regexp.c'`" then echo shar: error transmitting "'regexp.c'" '(should have been 27639 characters)' fi fi if test -f 'regexp.h' then echo shar: will not over-write existing file "'regexp.h'" else echo extracting "'regexp.h'" sed 's/^X//' >regexp.h <<'SHAR_EOF' X/* X * Definitions etc. for regexp(3) routines. X * X * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], X * not the System V one. X */ X#define NSUBEXP 10 Xtypedef struct regexp { X char *startp[NSUBEXP]; X char *endp[NSUBEXP]; X char regstart; /* Internal use only. */ X char reganch; /* Internal use only. */ X char *regmust; /* Internal use only. */ X int regmlen; /* Internal use only. */ X char program[1]; /* Unwarranted chumminess with compiler. */ X} regexp; X Xextern regexp *regcomp(); Xextern int regexec(); Xextern void regsub(); Xextern void regerror(); SHAR_EOF if test 574 -ne "`wc -c < 'regexp.h'`" then echo shar: error transmitting "'regexp.h'" '(should have been 574 characters)' fi fi if test -f 'regmagic.h' then echo shar: will not over-write existing file "'regmagic.h'" else echo extracting "'regmagic.h'" sed 's/^X//' >regmagic.h <<'SHAR_EOF' X/* X * The first byte of the regexp internal "program" is actually this magic X * number; the start node begins in the second byte. X */ X#define MAGIC 0234 SHAR_EOF if test 153 -ne "`wc -c < 'regmagic.h'`" then echo shar: error transmitting "'regmagic.h'" '(should have been 153 characters)' fi fi # end of shell archive exit 0 ---