[comp.binaries.ibm.pc] Less for DOS

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?_/2&#8X=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%.&#1P7Z`%(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; &regdummy = 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 = &regdummy;
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 == &regdummy) {
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 != &regdummy)
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 == &regdummy) {
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 == &regdummy)
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 == &regdummy || 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 == &regdummy)
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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; &regdummy = 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 = &regdummy;
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 == &regdummy) {
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 != &regdummy)
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 == &regdummy) {
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 == &regdummy)
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 == &regdummy || 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 == &regdummy)
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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, &REG, &REG);
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; &regdummy = 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 = &regdummy;
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 == &regdummy) {
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 != &regdummy)
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 == &regdummy) {
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 == &regdummy)
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 == &regdummy || 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 == &regdummy)
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
---