echo x - Makefile
sed '/^X/s///' > Makefile << '/'
X# Makefile for elle
X
XO=o
XCFLAGS = -c -O -DIGN_JOB_CONTROL -D_POSIX_SOURCE -m -wa -I$(INC)
XCC=exec cc
XINC=/include
X
XOBJ = eemain.$O eecmds.$O eesite.$O eevini.$O eedisp.$O eeterm.$O eeerr.$O  \
X      eeques.$O eebuff.$O eefile.$O eefed.$O eeedit.$O eebit.$O eef1.$O \
X      eef2.$O eefd.$O eehelp.$O eekmac.$O eef3.$O eesrch.$O eequer.$O \
X      eefill.$O eediag.$O sbstr.$O sbm.$O sberr.$O sbbcpy.$O
X
X# It probably isn't necessary to make all this stuff all the time, but it
X# is fairly easy and makes the whole process simpler.  If this is not done,
X# the dependencies are very complicated because some of the .c and .h files
X# are made dynamically.
Xelle:	ellec $(OBJ) $(FUN_OFILES) elle.h eesite.h
X	@rm -rf elle
X	@echo Start linking elle
X#	$(CC) $(CFLAGS) defprf.c	# depends on the new *.h files
X	$(CC) -i -o elle $(OBJ)
X	@echo elle done.
X
Xdefprf.c:	deffun.e
X	cat deffun.e defprf.e | ellec -Pconf  > defprf.c
X
Xeefdef.h:	deffun.e
X	cat deffun.e defprf.e | ellec -Fconf  > eefdef.h
X
Xeefidx.h:	deffun.e
X	cat deffun.e defprf.e | ellec -FXconf > eefidx.h
X
X# Don't flush these files if interrupted, dammit!
X.PRECIOUS: ellec deffun.e defprf.e
X
X# The following files must be recompiled if eefidx.h is changed
Xeecmds.$O eebuff.$O eeerr.$O eehelp.$O eejust.$O eemain.$O eeques.$O eef1.$O: eefidx.h
X
X# ELLE profile compiler.  
X#	Although eefdef.h and defprf.c are included by ELLEC, they
X#	are not listed as dependencies in order to avoid loops (see
X#	their target entries).  That is OK because their information is not
X#	used when generating the makecf files; it only furnishes default
X#	values needed when an ELLE user compiles a user profile.
Xellec: ellec.c
X	$(CC) -wa -o ellec ellec.c
X
Xclean:	
X	@rm -f *.o *.s *.bak core elle ellec
/
echo x - deffun.e
sed '/^X/s///' > deffun.e << '/'
X;;;
X;;; ELLE Master Function Definition file - "deffun.e"
X;;;
X;;;	This file serves as input to the ellec program.  It defines
X;;; all ELLE functions which may serve as keyboard-bound user commands.
X;;;
X;;; Format: (efun <Index> <Name> <Routine> <Module>)
X;;;		Index - an unique index # (used only within ELLE)
X;;;		Name - an unique string identifying this function to the user.
X;;;		Routine - the C routine implementing the function within ELLE.
X;;;		Module - the name of the C source file that the routine is in.
X;;;
X;;; The following definitions are roughly organized by object.
X;;; All functions that emulate EMACS functions are given names identical
X;;; to the EMACS function names.  For historical reasons these names
X;;; are not as consistent as they could be (sigh).
X;;; Those which have no exact counterpart in EMACS are identified by comments.
X
X(undefall)	; Ensure all predefined stuff is cleared out.
X
X; Simple Insertion
X(efun   1 "Insert Self"		f_insself	eef1)
X(efun   2 "Quoted Insert"	f_quotins	eef1)
X(efun   3 "CRLF"		f_crlf		eef1)
X
X; Characters
X(efun   4 "Forward Character"	f_fchar		eef1)
X(efun   5 "Backward Character"	f_bchar		eef1)
X(efun   6 "Delete Character"	f_dchar		eef1)
X(efun   7 "Backward Delete Character" f_bdchar	eef1)
X(efun   8 "Delete Horizontal Space" f_delspc	eef1)
X(efun   9 "Transpose Characters" f_tchars	eef1)
X
X; Words
X(efun  10 "Forward Word"	f_fword		eef1)
X(efun  11 "Backward Word"	f_bword		eef1)
X(efun  12 "Kill Word"		f_kword		eef1)
X(efun  13 "Backward Kill Word"	f_bkword	eef1)
X(efun  14 "Transpose Words"	f_twords	eef1)
X(efun  15 "Uppercase Word"	f_ucword	eef1)
X(efun  16 "Lowercase Word"	f_lcword	eef1)
X(efun  17 "Uppercase Initial"	f_uciword	eef1)
X     ; 18-19 reserved
X
X; Lines
X(efun  20 "Beginning of Line"	f_begline	eef2)
X(efun  21 "End of Line"		f_endline	eef2)
X(efun  22 "Next Line"		f_nxtline	eef2)
X(efun  23 "Previous Line"	f_prvline	eef2)
X(efun  24 "Down Real Line"	f_dnrline	eef2)
X(efun  25 "Up Real Line"	f_uprline	eef2)
X(efun  26 "Open Line"		f_oline		eef2)
X(efun  27 "Delete Blank Lines"	f_delblines	eef2)
X(efun  28 "Kill Line"		f_kline		eef2)
X(efun  29 "Backward Kill Line"	f_bkline	eef2)	; not EMACS
X(efun  30 "Goto Line"		f_goline	eef2)	; not EMACS
X     ; 31-34 reserved
X
X; Regions
X(efun  35 "Set/Pop Mark"	f_setmark	eef2)
X(efun  36 "Exchange Point and Mark" f_exchmark	eef2)
X(efun  37 "Kill Region"		f_kregion	eef2)
X(efun  38 "Copy Region"		f_copreg	eef2)
X(efun  39 "Uppercase Region"	f_ucreg		eef2)
X(efun  40 "Lowercase Region"	f_lcreg		eef2)
X(efun  41 "Fill Region"		f_fillreg	eef2)
X     ; 42-44 reserved
X
X; Paragraphs
X(efun  45 "Forward Paragraph"	f_fpara		eef2)
X(efun  46 "Backward Paragraph"	f_bpara		eef2)
X(efun  47 "Mark Paragraph"	f_mrkpara	eef2)
X(efun  48 "Fill Paragraph"	f_fillpara	eef2)
X     ; 49 reserved
X
X; Buffers
X(efun  50 "Select Buffer"	f_selbuffer	eebuff)
X(efun  51 "Select Existing Buffer" f_selxbuffer	eebuff)	; not EMACS
X(efun  52 "Kill Buffer"		f_kbuffer	eebuff)
X(efun  53 "List Buffers"	f_listbufs	eebuff)
X(efun  54 "Buffer Not Modified"	f_bufnotmod	eebuff)
X(efun  55 "EOL CRLF Mode"	f_eolmode	eebuff)	; ELLE
X(efun  56 "Goto Beginning"	f_gobeg		eebuff)
X(efun  57 "Goto End"		f_goend		eebuff)
X(efun  58 "What Page"		f_whatpage	eebuff)
X     ; 59 reserved
X
X; Files
X(efun  60 "Find File"		f_ffile		eefile)
X(efun  61 "Read File"		f_rfile		eefile)
X(efun  62 "Visit File"		f_vfile		eefile)
X(efun  63 "Insert File"		f_ifile		eefile)
X(efun  64 "Save File"		f_sfile		eefile)
X(efun  65 "Save All Files"	f_savefiles	eebuff)
X(efun  66 "Write File"		f_wfile		eefile)
X(efun  67 "Write Region"	f_wreg		eefile)
X(efun  68 "Write Last Kill"	f_wlastkill	eefile)	; not EMACS
X     ; 69 reserved
X
X; Windows
X(efun  70 "Two Windows"		f_2winds	eebuff)
X(efun  71 "One Window"		f_1wind		eebuff)
X(efun  72 "Other Window"	f_othwind	eebuff)
X(efun  73 "Grow Window"		f_growind	eebuff)
X(efun  74 "Shrink Window"	f_shrinkwind	eebuff)	; not EMACS	
X(efun  75 "Delete Window"	f_delwind	eebuff)	; not EMACS
X(efun  76 "Standout Window"	f_sowind	eebuff)	; ELLE
X(efun  77 "Two Mode Windows"	f_2modewinds	eebuff)	; ELLE
X
X; Window Positioning
X(efun  78 "New Window"		f_newwin	eefd)
X(efun  79 "Next Screen"		f_nscreen	eefd)
X(efun  80 "Previous Screen"	f_pscreen	eefd)
X(efun  81 "Other New Screen"	f_othnscreen	eefd)	; not EMACS
X(efun  82 "Line to Window Border" f_lwindbord	eefd)	; not EMACS
X(efun  83 "Scroll Window Up"	f_scupwind	eefd)	; not EMACS
X(efun  84 "Scroll Window Down"	f_scdnwind	eefd)	; not EMACS
X(efun  85 "Move to Window Top"	f_mvwtop	eefd)	; not EMACS
X(efun  86 "Move to Window Bottom" f_mvwbot	eefd)	; not EMACS
X     ; 87-89 reserved
X
X; Command Input
X(efun  90 "Set Profile"		f_setprof	eecmds)	; ELLE
X(efun  91 "Prefix Meta"		f_pfxmeta	eecmds)
X(efun  92 "Prefix Extend"	f_pfxext	eecmds)
X(efun  93 "Universal Arg"	f_uarg		eecmds)
X(efun  94 "Negative Argument"	f_negarg	eecmds)
X(efun  95 "Argument Digit"	f_argdig	eecmds)
X(efun  96 "VT100 Button Hack"	f_vtbuttons	eecmds)	; not EMACS
X
X; Help
X(efun  97 "Describe"		f_describe	eehelp)
X     ; 98-99 reserved
X
X; Keyboard Macros
X(efun 100 "Start Kbd Macro"	f_skmac		eekmac)
X(efun 101 "End Kbd Macro"	f_ekmac		eekmac)
X(efun 102 "Execute Kbd Macro"	f_xkmac		eekmac)
X(efun 103 "View Kbd Macro"	f_vkmac		eekmac)
X    ; 104 reserved
X
X; Killing
X(efun 105 "Un-kill"		f_unkill	eef3)
X(efun 106 "Un-kill Pop"		f_unkpop	eef3)
X(efun 107 "Append Next Kill"	f_appnkill	eef3)
X    ; 108-109 reserved
X
X; Searching
X(efun 110 "String Search"	f_srch		eesrch)
X(efun 111 "Reverse String Search" f_rsrch	eesrch)
X(efun 112 "Incremental Search"	f_isrch		eesrch)
X(efun 113 "Reverse Search"	f_risrch	eesrch)
X
X; Query Replace & friends
X(efun 114 "Replace String"	f_repstr	eequer)
X(efun 115 "Query Replace"	f_querep	eequer)
X(efun 116 "Replace in Line"	f_repline	eequer)	; not EMACS
X
X; Fill Mode
X(efun 117 "Set Fill Column"	f_sfcol		eefill)
X(efun 118 "Set Fill Prefix"	f_sfpref	eefill)
X(efun 119 "Auto Fill Mode"	f_fillmode	eefill)
X(efun 120 "Text Mode"		f_textmode	eefill)	; IMAGEN
X
X; Indentation
X(efun 121 "Indent According to Mode" f_indatm	eef3)
X(efun 122 "Indent New Line"	f_indnl		eef3)
X(efun 123 "Back to Indentation"	f_backind	eef3)
X(efun 124 "Indent for Comment"	f_indcomm	eef3)
X(efun 125 "Indent Relative"	f_indrel	eef3)
X	; 126-128 reserved
X
X; Miscellaneous
X(efun 129 "Match Bracket"	f_matchbrack	eef3)	; not EMACS 
X
X; Process Control
X(efun 130 "Push to Inferior"	f_pshinf	eemain)
X(efun 131 "Return to Superior"	f_retsup	eemain)
X(efun 132 "Write File Exit"	f_wfexit	eemain)	; not EMACS
X    ; 133-139 reserved
X
X; ELLE Debugging
X(efun 140 "Hit Breakpoint"	f_bkpt		eeerr)	; ELLE
X(efun 141 "Debug Mode"		f_debug		eediag)	; ELLE
X    ; 142-149 reserved
X;---------------------------------------------------------------
X
X; IMAGEN configuration only
X(efun 150 "Execute Unix Command" f_xucmd	eemake)	; IMAGEN
X(efun 151 "Execute Make"	f_make		eemake)	; IMAGEN
X(efun 152 "Find Next Error"	f_nxterr	eemake)	; IMAGEN
X
X; ICONOGRAPHICS-specific
X(efun 153 "ICO Extend Command"	f_icoxcmd	eefico)	; ICONOGRAPHICS
X(efun 154 "ICO Typeset Funs"	f_icotypfns	eefico)	; ICONOGRAPHICS
X(efun 155 "ICO Spec Input Funs" f_icospifns	eefico) ; ICONOGRAPHICS
X
X; SUN Mouse functions
X(efun 156 "Stuff Selection"	f_stuffsel	eesun)	; SUN
X(efun 157 "Select Region"	f_selregion	eesun)	; SUN
X
/
echo x - defprf.c
sed '/^X/s///' > defprf.c << '/'
X/* This file defines the initial data for ELLE's default user profile.
X** It is automatically generated by ELLEC, and should not be edited.
X*/
Xchar charmap[] = {
X	35,	/* (  0)  ^@  Set/Pop Mark */
X	20,	/* (  1)  ^A  Beginning of Line */
X	 5,	/* (  2)  ^B  Backward Character */
X	 0,	/* (  3)  ^C  unknown function */
X	 6,	/* (  4)  ^D  Delete Character */
X	21,	/* (  5)  ^E  End of Line */
X	 4,	/* (  6)  ^F  Forward Character */
X	 0,	/* (  7)  ^G  unknown function */
X	 5,	/* ( 10)  ^H  Backward Character */
X	121,	/* ( 11)  ^I  Indent According to Mode */
X	122,	/* ( 12)  ^J  Indent New Line */
X	28,	/* ( 13)  ^K  Kill Line */
X	78,	/* ( 14)  ^L  New Window */
X	 3,	/* ( 15)  ^M  CRLF */
X	24,	/* ( 16)  ^N  Down Real Line */
X	26,	/* ( 17)  ^O  Open Line */
X	25,	/* ( 20)  ^P  Up Real Line */
X	 2,	/* ( 21)  ^Q  Quoted Insert */
X	113,	/* ( 22)  ^R  Reverse Search */
X	112,	/* ( 23)  ^S  Incremental Search */
X	 9,	/* ( 24)  ^T  Transpose Characters */
X	93,	/* ( 25)  ^U  Universal Arg */
X	79,	/* ( 26)  ^V  Next Screen */
X	37,	/* ( 27)  ^W  Kill Region */
X	92,	/* ( 30)  ^X  Prefix Extend */
X	105,	/* ( 31)  ^Y  Un-kill */
X	 0,	/* ( 32)  ^Z  unknown function */
X	91,	/* ( 33)  ^[  Prefix Meta */
X	141,	/* ( 34)  ^\  Debug Mode */
X	 0,	/* ( 35)  ^]  unknown function */
X	140,	/* ( 36)  ^^  Hit Breakpoint */
X	97,	/* ( 37)  ^_  Describe */
X	 1,	/* ( 40)      Insert Self */
X	 1,	/* ( 41)   !  Insert Self */
X	 1,	/* ( 42)   "  Insert Self */
X	 1,	/* ( 43)   #  Insert Self */
X	 1,	/* ( 44)   $  Insert Self */
X	 1,	/* ( 45)   %  Insert Self */
X	 1,	/* ( 46)   &  Insert Self */
X	 1,	/* ( 47)   '  Insert Self */
X	 1,	/* ( 50)   (  Insert Self */
X	 1,	/* ( 51)   )  Insert Self */
X	 1,	/* ( 52)   *  Insert Self */
X	 1,	/* ( 53)   +  Insert Self */
X	 1,	/* ( 54)   ,  Insert Self */
X	 1,	/* ( 55)   -  Insert Self */
X	 1,	/* ( 56)   .  Insert Self */
X	 1,	/* ( 57)   /  Insert Self */
X	 1,	/* ( 60)   0  Insert Self */
X	 1,	/* ( 61)   1  Insert Self */
X	 1,	/* ( 62)   2  Insert Self */
X	 1,	/* ( 63)   3  Insert Self */
X	 1,	/* ( 64)   4  Insert Self */
X	 1,	/* ( 65)   5  Insert Self */
X	 1,	/* ( 66)   6  Insert Self */
X	 1,	/* ( 67)   7  Insert Self */
X	 1,	/* ( 70)   8  Insert Self */
X	 1,	/* ( 71)   9  Insert Self */
X	 1,	/* ( 72)   :  Insert Self */
X	 1,	/* ( 73)   ;  Insert Self */
X	 1,	/* ( 74)   <  Insert Self */
X	 1,	/* ( 75)   =  Insert Self */
X	 1,	/* ( 76)   >  Insert Self */
X	 1,	/* ( 77)   ?  Insert Self */
X	 1,	/* (100)   @  Insert Self */
X	 1,	/* (101)   A  Insert Self */
X	 1,	/* (102)   B  Insert Self */
X	 1,	/* (103)   C  Insert Self */
X	 1,	/* (104)   D  Insert Self */
X	 1,	/* (105)   E  Insert Self */
X	 1,	/* (106)   F  Insert Self */
X	 1,	/* (107)   G  Insert Self */
X	 1,	/* (110)   H  Insert Self */
X	 1,	/* (111)   I  Insert Self */
X	 1,	/* (112)   J  Insert Self */
X	 1,	/* (113)   K  Insert Self */
X	 1,	/* (114)   L  Insert Self */
X	 1,	/* (115)   M  Insert Self */
X	 1,	/* (116)   N  Insert Self */
X	 1,	/* (117)   O  Insert Self */
X	 1,	/* (120)   P  Insert Self */
X	 1,	/* (121)   Q  Insert Self */
X	 1,	/* (122)   R  Insert Self */
X	 1,	/* (123)   S  Insert Self */
X	 1,	/* (124)   T  Insert Self */
X	 1,	/* (125)   U  Insert Self */
X	 1,	/* (126)   V  Insert Self */
X	 1,	/* (127)   W  Insert Self */
X	 1,	/* (130)   X  Insert Self */
X	 1,	/* (131)   Y  Insert Self */
X	 1,	/* (132)   Z  Insert Self */
X	 1,	/* (133)   [  Insert Self */
X	 1,	/* (134)   \  Insert Self */
X	 1,	/* (135)   ]  Insert Self */
X	 1,	/* (136)   ^  Insert Self */
X	 1,	/* (137)   _  Insert Self */
X	 1,	/* (140)   `  Insert Self */
X	 1,	/* (141)   a  Insert Self */
X	 1,	/* (142)   b  Insert Self */
X	 1,	/* (143)   c  Insert Self */
X	 1,	/* (144)   d  Insert Self */
X	 1,	/* (145)   e  Insert Self */
X	 1,	/* (146)   f  Insert Self */
X	 1,	/* (147)   g  Insert Self */
X	 1,	/* (150)   h  Insert Self */
X	 1,	/* (151)   i  Insert Self */
X	 1,	/* (152)   j  Insert Self */
X	 1,	/* (153)   k  Insert Self */
X	 1,	/* (154)   l  Insert Self */
X	 1,	/* (155)   m  Insert Self */
X	 1,	/* (156)   n  Insert Self */
X	 1,	/* (157)   o  Insert Self */
X	 1,	/* (160)   p  Insert Self */
X	 1,	/* (161)   q  Insert Self */
X	 1,	/* (162)   r  Insert Self */
X	 1,	/* (163)   s  Insert Self */
X	 1,	/* (164)   t  Insert Self */
X	 1,	/* (165)   u  Insert Self */
X	 1,	/* (166)   v  Insert Self */
X	 1,	/* (167)   w  Insert Self */
X	 1,	/* (170)   x  Insert Self */
X	 1,	/* (171)   y  Insert Self */
X	 1,	/* (172)   z  Insert Self */
X	 1,	/* (173)   {  Insert Self */
X	 1,	/* (174)   |  Insert Self */
X	 1,	/* (175)   }  Insert Self */
X	 1,	/* (176)   ~  Insert Self */
X	 7,	/* (177) DEL  Backward Delete Character */
X};
X char metamap[] = {
X	02  , 86,	/* M-^B  Move to Window Bottom */
X	014 , 30,	/* M-^L  Goto Line */
X	016 , 84,	/* M-^N  Scroll Window Down */
X	020 , 83,	/* M-^P  Scroll Window Up */
X	022 ,111,	/* M-^R  Reverse String Search */
X	023 ,110,	/* M-^S  String Search */
X	024 , 85,	/* M-^T  Move to Window Top */
X	027 ,107,	/* M-^W  Append Next Kill */
X	030 , 51,	/* M-^X  Select Existing Buffer */
X	036 , 74,	/* M-^^  Shrink Window */
X	045 ,115,	/*  M-%  Query Replace */
X	055 , 94,	/*  M--  Negative Argument */
X	060 , 95,	/*  M-0  Argument Digit */
X	061 , 95,	/*  M-1  Argument Digit */
X	062 , 95,	/*  M-2  Argument Digit */
X	063 , 95,	/*  M-3  Argument Digit */
X	064 , 95,	/*  M-4  Argument Digit */
X	065 , 95,	/*  M-5  Argument Digit */
X	066 , 95,	/*  M-6  Argument Digit */
X	067 , 95,	/*  M-7  Argument Digit */
X	070 , 95,	/*  M-8  Argument Digit */
X	071 , 95,	/*  M-9  Argument Digit */
X	073 ,124,	/*  M-;  Indent for Comment */
X	074 , 56,	/*  M-<  Goto Beginning */
X	076 , 57,	/*  M->  Goto End */
X	0133, 46,	/*  M-[  Backward Paragraph */
X	0134,  8,	/*  M-\  Delete Horizontal Space */
X	0135, 45,	/*  M-]  Forward Paragraph */
X	0102, 11,	/*  M-B  Backward Word */
X	0103, 17,	/*  M-C  Uppercase Initial */
X	0104, 12,	/*  M-D  Kill Word */
X	0106, 10,	/*  M-F  Forward Word */
X	0107, 41,	/*  M-G  Fill Region */
X	0110, 47,	/*  M-H  Mark Paragraph */
X	0111,125,	/*  M-I  Indent Relative */
X	0114, 16,	/*  M-L  Lowercase Word */
X	0115,123,	/*  M-M  Back to Indentation */
X	0116, 22,	/*  M-N  Next Line */
X	0117, 96,	/*  M-O  VT100 Button Hack */
X	0120, 23,	/*  M-P  Previous Line */
X	0121, 48,	/*  M-Q  Fill Paragraph */
X	0124, 14,	/*  M-T  Transpose Words */
X	0125, 15,	/*  M-U  Uppercase Word */
X	0126, 80,	/*  M-V  Previous Screen */
X	0127, 38,	/*  M-W  Copy Region */
X	0131,106,	/*  M-Y  Un-kill Pop */
X	0176, 54,	/*  M-~  Buffer Not Modified */
X	0177, 13,	/* M-DEL  Backward Kill Word */
X};
X char extmap[] = {
X	02  , 53,	/* X-^B  List Buffers */
X	03  ,132,	/* X-^C  Write File Exit */
X	05  , 67,	/* X-^E  Write Region */
X	06  , 60,	/* X-^F  Find File */
X	013 , 68,	/* X-^K  Write Last Kill */
X	014 , 40,	/* X-^L  Lowercase Region */
X	015 , 55,	/* X-^M  EOL CRLF Mode */
X	017 , 27,	/* X-^O  Delete Blank Lines */
X	020 , 90,	/* X-^P  Set Profile */
X	022 , 61,	/* X-^R  Read File */
X	023 , 64,	/* X-^S  Save File */
X	025 , 39,	/* X-^U  Uppercase Region */
X	026 , 62,	/* X-^V  Visit File */
X	027 , 66,	/* X-^W  Write File */
X	030 , 36,	/* X-^X  Exchange Point and Mark */
X	032 ,131,	/* X-^Z  Return to Superior */
X	041 ,130,	/*  X-!  Push to Inferior */
X	044 ,116,	/*  X-$  Replace in Line */
X	045 ,114,	/*  X-%  Replace String */
X	050 ,100,	/*  X-(  Start Kbd Macro */
X	051 ,101,	/*  X-)  End Kbd Macro */
X	052 ,103,	/*  X-*  View Kbd Macro */
X	056 ,118,	/*  X-.  Set Fill Prefix */
X	060 , 75,	/*  X-0  Delete Window */
X	061 , 71,	/*  X-1  One Window */
X	062 , 70,	/*  X-2  Two Windows */
X	070 , 76,	/*  X-8  Standout Window */
X	071 , 77,	/*  X-9  Two Mode Windows */
X	075 , 58,	/*  X-=  What Page */
X	0136, 73,	/*  X-^  Grow Window */
X	0102, 50,	/*  X-B  Select Buffer */
X	0105,102,	/*  X-E  Execute Kbd Macro */
X	0106,117,	/*  X-F  Set Fill Column */
X	0111, 63,	/*  X-I  Insert File */
X	0113, 52,	/*  X-K  Kill Buffer */
X	0117, 72,	/*  X-O  Other Window */
X	0123, 65,	/*  X-S  Save All Files */
X	0124,119,	/*  X-T  Auto Fill Mode */
X	0177, 29,	/* X-DEL  Backward Kill Line */
X};
Xstruct profile def_prof = {
X  1, /* Ver */
X  sizeof(charmap),   charmap,
X  sizeof(metamap)/2, metamap,
X  sizeof(extmap)/2,  extmap, 
X  0, 0
X};
/
echo x - defprf.e
sed '/^X/s///' > defprf.e << '/'
X;;;
X;;; ELLE Default Command Profile - "defprf.e"
X;;;
X;;;	This file is input to the ellec program.  It defines the default
X;;; command key bindings that ELLE uses, in the absence of an individual
X;;; user profile.
X;;;	These defaults attempt to emulate the default EMACS command key
X;;; bindings.  Differences, where known, are commented.
X;;;
X;;;	"ELLE" means the function is unique to ELLE.
X;;;	E/G: (cmd altnam) "thisname";
X;;;		"E:" refers to TOPS-20 EMACS, "G:" refers to Gnu Emacs.
X;;;		(cmd) This function exists but is bound to "cmd" instead.
X;;;		    (*) function exists but is not bound to any specific key.
X;;;		    ()  function does not exist.
X;;;		    (=) function exists, with same binding (normally omitted)
X;;;		altnam  Name by which this function is known.
X;;;		"thisname" - name of function bound to this command.
X;;;		    -    means the command is unbound (undefined).
X
X(keyallunbind)		; Flush any predefined bindings
X
X(keybind ^@ "Set/Pop Mark")
X(keybind ^A "Beginning of Line")
X(keybind ^B "Backward Character")
X; ^C not bound.  			; E: ()- G: mode-specific-command-prefix
X(keybind ^D "Delete Character")
X(keybind ^E "End of Line")
X(keybind ^F "Forward Character")
X(keybind ^H "Backward Character")	; G: (^B) help-command
X(keybind ^I "Indent According to Mode")
X(keybind ^J "Indent New Line")
X(keybind ^K "Kill Line")
X(keybind ^L "New Window")
X(keybind ^M "CRLF")
X(keybind ^N "Down Real Line")
X(keybind ^O "Open Line")
X(keybind ^P "Up Real Line")
X(keybind ^Q "Quoted Insert")
X(keybind ^R "Reverse Search")
X(keybind ^S "Incremental Search")
X(keybind ^T "Transpose Characters")
X(keybind ^U "Universal Arg")
X(keybind ^V "Next Screen")
X(keybind ^W "Kill Region")
X(keybind ^X "Prefix Extend")
X(keybind ^Y "Un-kill")
X; ^Z not bound			; E: Prefix Control-Meta;  G: suspend-emacs
X(keybind ^[ "Prefix Meta")
X(keybind "^\" "Debug Mode")	; ELLE. E: () Prefix Meta;  G: () -
X; ^] not bound.			; E+G: Abort Recursive Edit
X(keybind ^^ "Hit Breakpoint")	; ELLE. E: () Prefix Control;  G: () -
X(keybind ^_ "Describe")		; E: (M-?) Help;  G: (^H-k) undo
X(keybind " " "Insert Self")
X(keybind ! "Insert Self")
X(keybind """" "Insert Self")
X(keybind # "Insert Self")
X(keybind $ "Insert Self")
X(keybind % "Insert Self")
X(keybind & "Insert Self")
X(keybind ' "Insert Self")
X(keybind "(" "Insert Self")
X(keybind ")" "Insert Self")
X(keybind * "Insert Self")
X(keybind + "Insert Self")
X(keybind , "Insert Self")
X(keybind - "Insert Self")
X(keybind . "Insert Self")
X(keybind / "Insert Self")
X(keybind 0 "Insert Self")
X(keybind 1 "Insert Self")
X(keybind 2 "Insert Self")
X(keybind 3 "Insert Self")
X(keybind 4 "Insert Self")
X(keybind 5 "Insert Self")
X(keybind 6 "Insert Self")
X(keybind 7 "Insert Self")
X(keybind 8 "Insert Self")
X(keybind 9 "Insert Self")
X(keybind : "Insert Self")
X(keybind ";" "Insert Self")
X(keybind < "Insert Self")
X(keybind = "Insert Self")
X(keybind > "Insert Self")
X(keybind ? "Insert Self")
X(keybind @ "Insert Self")
X(keybind A "Insert Self")
X(keybind B "Insert Self")
X(keybind C "Insert Self")
X(keybind D "Insert Self")
X(keybind E "Insert Self")
X(keybind F "Insert Self")
X(keybind G "Insert Self")
X(keybind H "Insert Self")
X(keybind I "Insert Self")
X(keybind J "Insert Self")
X(keybind K "Insert Self")
X(keybind L "Insert Self")
X(keybind M "Insert Self")
X(keybind N "Insert Self")
X(keybind O "Insert Self")
X(keybind P "Insert Self")
X(keybind Q "Insert Self")
X(keybind R "Insert Self")
X(keybind S "Insert Self")
X(keybind T "Insert Self")
X(keybind U "Insert Self")
X(keybind V "Insert Self")
X(keybind W "Insert Self")
X(keybind X "Insert Self")
X(keybind Y "Insert Self")
X(keybind Z "Insert Self")
X(keybind [ "Insert Self")
X(keybind "\" "Insert Self")
X(keybind ] "Insert Self")
X(keybind ^ "Insert Self")
X(keybind _ "Insert Self")
X(keybind ` "Insert Self")
X(keybind a "Insert Self")
X(keybind b "Insert Self")
X(keybind c "Insert Self")
X(keybind d "Insert Self")
X(keybind e "Insert Self")
X(keybind f "Insert Self")
X(keybind g "Insert Self")
X(keybind h "Insert Self")
X(keybind i "Insert Self")
X(keybind j "Insert Self")
X(keybind k "Insert Self")
X(keybind l "Insert Self")
X(keybind m "Insert Self")
X(keybind n "Insert Self")
X(keybind o "Insert Self")
X(keybind p "Insert Self")
X(keybind q "Insert Self")
X(keybind r "Insert Self")
X(keybind s "Insert Self")
X(keybind t "Insert Self")
X(keybind u "Insert Self")
X(keybind v "Insert Self")
X(keybind w "Insert Self")
X(keybind x "Insert Self")
X(keybind y "Insert Self")
X(keybind z "Insert Self")
X(keybind { "Insert Self")
X(keybind | "Insert Self")
X(keybind } "Insert Self")
X(keybind ~ "Insert Self")
X(keybind DEL "Backward Delete Character")
X
X; Meta chars
X
X(keybind M-^B "Move to Window Bottom")	; ELLE (ima). E+G:()-
X(keybind M-^L "Goto Line")		; E:();  G:(* goto-line) -
X(keybind M-^N "Scroll Window Down")	; ELLE (ima). E+G:()- forward-list
X(keybind M-^P "Scroll Window Up")	; ELLE (ima). E+G:()- backward-list
X(keybind M-^R "Reverse String Search")	; E:(*); G:(* search-backward) -
X(keybind M-^S "String Search")		; E:(*); G:(* search-forward) isearch-forward-regexp
X(keybind M-^T "Move to Window Top")	; ELLE (ima). E+G:()-
X(keybind M-^W "Append Next Kill")
X(keybind M-^X "Select Existing Buffer")	; ELLE (ima). E+G:()-
X(keybind M-^^ "Shrink Window")		; ELLE (ima). E+G:()-
X(keybind M-% "Query Replace")
X(keybind M-- "Negative Argument")
X(keybind M-0 "Argument Digit")
X(keybind M-1 "Argument Digit")
X(keybind M-2 "Argument Digit")
X(keybind M-3 "Argument Digit")
X(keybind M-4 "Argument Digit")
X(keybind M-5 "Argument Digit")
X(keybind M-6 "Argument Digit")
X(keybind M-7 "Argument Digit")
X(keybind M-8 "Argument Digit")
X(keybind M-9 "Argument Digit")
X(keybind "M-;" "Indent for Comment")
X(keybind M-< "Goto Beginning")
X(keybind M-> "Goto End")
X(keybind M-[ "Backward Paragraph")
X(keybind "M-\" "Delete Horizontal Space")
X(keybind M-] "Forward Paragraph")
X(keybind M-B "Backward Word")
X(keybind M-C "Uppercase Initial")
X(keybind M-D "Kill Word")
X(keybind M-F "Forward Word")
X(keybind M-G "Fill Region")
X(keybind M-H "Mark Paragraph")
X(keybind M-I "Indent Relative")		; E+G: (*) Tab to Tab Stop
X(keybind M-L "Lowercase Word")
X(keybind M-M "Back to Indentation")
X(keybind M-N  "Next Line")		; E:(*); G:(* forward-line) -
X(keybind M-O "VT100 button hack")	; ELLE. E+G: () -
X(keybind M-P  "Previous Line")		; E:(*); G:() -
X(keybind M-Q "Fill Paragraph")
X(keybind M-T "Transpose Words")
X(keybind M-U "Uppercase Word")
X(keybind M-V "Previous Screen")
X(keybind M-W "Copy Region")
X(keybind M-Y "Un-kill Pop")
X(keybind M-~ "Buffer Not Modified")
X(keybind M-DEL "Backward Kill Word")
X
X; Extended commands
X
X(keybind X-^B "List Buffers")
X(keybind X-^C "Write File Exit")	; ELLE (ima). E:()-; G: (= save-buffers-kill-emacs)
X(keybind X-^E "Write Region")		; E:(*)-;    G:(*) eval-last-sexp
X(keybind X-^F "Find File")
X(keybind X-^K "Write Last Kill")	; ELLE (mnx). E+G:()-
X(keybind X-^L "Lowercase Region")
X(keybind X-^M "EOL CRLF Mode")		; ELLE.  E+G: ()-
X(keybind X-^O "Delete Blank Lines")
X(keybind X-^P "Set Profile")		; ELLE.  E+G: () Mark Page
X(keybind X-^R "Read File")
X(keybind X-^S "Save File")
X(keybind X-^U "Uppercase Region")
X(keybind X-^V "Visit File")
X(keybind X-^W "Write File")
X(keybind X-^X "Exchange Point and Mark")
X(keybind X-^Z "Return to Superior")	; G:() suspend-emacs
X(keybind X-! "Push to Inferior")	; ELLE.  E:(*)-; G:()-
X(keybind X-$  "Replace in Line")	; ELLE (mnx). E+G:()-
X(keybind X-% "Replace String")		; E+G: (*) -
X(keybind "X-(" "Start Kbd Macro")
X(keybind "X-)" "End Kbd Macro")
X(keybind X-* "View Kbd Macro")		; E: (*)-; G: ()-
X(keybind X-. "Set Fill Prefix")
X(keybind X-0 "Delete Window")		; E: ()-
X(keybind X-1 "One Window")
X(keybind X-2 "Two Windows")
X(keybind X-8 "Standout Window")		; ELLE.  E+G:()-
X(keybind X-9 "Two Mode Windows")	; ELLE.  E+G:()-
X(keybind X-= "What Page")		; E+G: (*) What Cursor Position
X(keybind X-^ "Grow Window")
X(keybind X-B "Select Buffer")
X(keybind X-E "Execute Kbd Macro")
X(keybind X-F "Set Fill Column")
X(keybind X-I "Insert File")		; E: (*) Info
X(keybind X-K "Kill Buffer")
X(keybind X-O "Other Window")
X(keybind X-S "Save All Files")		; E:(*)-; G:(= save-some-buffers)
X(keybind X-T "Auto Fill Mode")		; E:(*) Transpose Regions;  G:(*)-
X(keybind X-DEL "Backward Kill Line")	; ELLE(ico)  E+G:() Backward Kill Sentence
X
X; IMAGEN-specific functions, not bound.
X;(keybind ""  "Text Mode")		; IMAGEN E:(*);	G:(*)
X;(keybind ""  "Execute Unix Command")	; IMAGEN E:();	G:(M-! shell-command)
X;(keybind ""  "Execute Make")		; IMAGEN E:(* Compile); G:(* compile)
X;(keybind ""  "Find Next Error")	; IMAGEN E:();	G:(X-` next-error)
X
X; SUN Mouse functions, for menuitem selection.
X;(menuitem "Stuff Selection")	; SUN
X;(menuitem "Select Region")	; SUN
X
X; Forget completely about these.
X;(keybind ""  "ICO Extend Command")	; ICONOGRAPHICS
X;(keybind ""  "ICO Typeset Funs")	; ICONOGRAPHICS
X;(keybind ""  "ICO Spec Input Funs")	; ICONOGRAPHICS
X
/
echo x - eebit.c
sed '/^X/s///' > eebit.c << '/'
X/* ELLE - Copyright 1985, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*
X * EEBIT	Bit Array functions
X */
X#include "sb.h"
X
X/* Char-bit array functions.   All assume that there are at least 8 bits
X * in a byte, and that the number of bytes per word is a power of 2.
X */
X/* CHBBITS represents log 2 of the # of bits stored per chbit-array word.
X *	WDBITS already has log2 of the # of bytes per word, and we are
X *	assuming each byte has at least 8 bits, so log2(8) = 3.
X */
X#define CHBSIZE (WDSIZE*8)	/* # bits per word */
X#define CHBBITS (WDBITS+3)	/* log2(CHBSIZE) */
X#define CHBMASK (CHBSIZE-1)
X#define CHBARYSIZ (128/CHBSIZE)	/* # words per ASCII array */
X
X/* CHBALLOC(size) - Allocates a char-bit array */
Xint *
Xchballoc(size)
Xint size;
X{	return((int *)calloc((size + CHBSIZE-1)/CHBSIZE, (sizeof(int))));
X}
X
X/* CHBIT(array, char) - Tests bit in char-bit array
X */
Xchbit(array,c)
Xregister int *array, c;
X{	return(array[c >> CHBBITS] & (1 << (c & CHBMASK)));
X}
X/* CHBIS (array, char) - Sets bit in char-bit array
X */
Xchbis(array,c)
Xregister int *array, c;
X{	array[c >> CHBBITS] |= (1 << (c & CHBMASK));
X}
X/* CHBIC (array, char) - Clears bit in char-bit array
X */
Xchbic(array,c)
Xregister int *array, c;
X{	array[c >> CHBBITS] &= ~(1 << (c & CHBMASK));
X}
/
echo x - eebuff.c
sed '/^X/s///' > eebuff.c << '/'
X/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X
X/* EEBUFF	Buffer and Window functions.
X *	Each buffer is an independent SB-string described by a
X *	buffer structure.  All buffer structures are allocated dynamically
X *	and chained together starting from buf_head.
X */
X
X#include "elle.h"
X
X#if FX_FILLMODE
Xextern int fill_mode;
X#endif
X#if FX_SKMAC
Xextern int kdef_mode;
X#endif
X
Xstruct buffer *make_buf(), *find_buf(), *sel_mbuf(), *sel_nbuf();
Xstruct window *make_win();
X
X/* EFUN: "Select Buffer" */
X/*	Select old buffer or create a new one.  Defaults to previously
X *	used buffer.
X */
Xf_selbuffer()
X{	register char *ans;
X	register struct buffer *b;
X
X	if((b = last_buf) == cur_buf)	/* If default same as current, */
X		if(!(b = sel_mbuf(b)))	/* try to pick a more useful one. */
X			b = sel_nbuf(cur_buf);
X
X	ans = ask("Select buffer (%s): ",b->b_name);
X	if (ans == 0)		       /* he aborted */
X		return;
X	if (*ans != '\0')		/* Null string => use last buff */
X	  {	b = find_buf (ans);	/* Else find/create one */
X		if (b == 0)
X			b = make_buf (ans);
X	  }
X	sel_buf(b);
X	chkfree(ans);
X}
X
X#if FX_SELXBUFFER
X/* EFUN: "Select Existing Buffer" (not EMACS) - from IMAGEN config */
X
Xstatic int findstr();
X
Xf_selxbuffer()
X{	register char *ans;
X	register struct buffer *b;
X
X	b = last_buf;			/* This is default */
X	ans = ask("Select existing buffer (%s): ", b->b_name);
X	if (ans == 0)			/* Aborted */
X		return;
X	if (*ans != 0)
X	  {	for (b = buf_head; b != 0; b = b->b_next)
X			if (findstr(ans, b->b_name))
X				break;
X		if (b == 0)
X			ding("That isn't a substring of any buffer name!");
X	  }
X	chkfree(ans);
X	if (b != 0)
X	  {	saytoo(" => ");
X		sayntoo(b->b_name);
X		sel_buf(b);
X	  }
X}
X
Xstatic int
Xfindstr(str, instr)			/* Find "str" in string "instr" */
Xregister char *str, *instr;
X{	register char *sp, *isp;
X
X	while (*instr)
X	  {	sp = str;
X		isp = instr;
X		while (*sp)
X			if (*sp++ != *isp++)
X				goto next;
X		return(1);
Xnext:		++instr;
X	  }
X	return(0);
X}
X#endif /*FX_SELXBUFFER*/
X
X
X/* EFUN: "Kill Buffer"	*/
X/*	Kill specified buffer - defaults to current buffer.
X * This code assumes a killed buffer will never be on a window list unless it
X * is pointed to by cur_buf or oth_win->w_buf!!!!
X */
Xf_kbuffer()
X{	register struct buffer *b, *ob;
X	register char *ans;
X
X	if((ans = ask("Kill buffer: ")) == 0)
X		return;
X	if(*ans == 0) b = cur_buf;
X	else if(*ans == SP) b = 0;
X	else b = find_buf(ans);
X
X	chkfree(ans);
X	if(!b)
X	  {	ding("No such buffer");
X		return;
X	  }
X#if IMAGEN
X	if (b->b_flags & B_PERMANENT)
X	  {	ding("Permanent buffer--cannot kill!");
X		return;
X	  }
X	if (b->b_flags & B_MODIFIED)
X	  {	if ((ans == ask("Buffer is modified; are you sure? ")) == 0)
X			return;
X		if(upcase(*ans) != 'Y')
X		  {	chkfree(ans);
X			return;
X		  }
X		chkfree(ans);
X	  }
X#endif /*IMAGEN*/
X	if(b == cur_buf || (oth_win && (oth_win->w_buf == b)))
X	  {	ob = last_buf;
X		do
X		  {
X			/* If default same as doomed buffer, try to pick
X			 * a more useful alternative. */
X			if((b == ob) && !(ob = sel_mbuf(b)))
X				ob = sel_nbuf(b);
X
X			ans = ask("Killing in-use buffer; select which other buffer (%s): ",
X					ob->b_name);
X			if(ans == 0) return;
X			if(*ans)
X			  {	if(*ans == SP) ob = 0;
X				else ob = find_buf(ans);
X			  }
X			chkfree(ans);
X			if(!ob)
X			  {	ding("No such buffer");
X				return;
X			  }
X		  } while (b == ob);
X
X		/* B is buffer to kill, OB is buffer to replace it with */
X		if(oth_win && (oth_win->w_buf == b))
X		  {	f_othwind();	/* Select other one */
X			chg_buf(ob);	/* Change to new buffer */
X			f_othwind();
X		  }
X		if(cur_buf == b)
X			chg_buf(ob);
X	  }
X
X	kill_buf(b);			/* Die!!!! */
X	if(last_buf == b)
X		last_buf = cur_buf;
X}
X
X/* EFUN: "List Buffers" */
X/*	Display a list of all user buffers.  Internal buffers, whose names
X *	start with a space, are not shown.
X */
Xf_listbufs()
X{
X	register struct buffer *b;
X	register char *cp;
X	register int i;
X	struct buffer *tbuf, *savbuf;
X	char temp[20];
X
X	/* First must set up special buffer... */
X	savbuf = cur_buf;
X	chg_buf(tbuf = make_buf(" **SHOW**"));
X	e_sputz("Buffers in this ELLE:\n\n");
X	for(b = buf_head; b; b = b->b_next)
X	  {	cp = b->b_name;
X		if(*cp == SP) continue;	/* Ignore internal buffs */
X		e_sputz((b->b_flags&B_MODIFIED) ? "* " : "  ");
X		e_sputz(cp);			/* Insert buffer name */
X		dottoa(temp,ex_blen(b));	/* Get buff-length string */
X		if((i = ((FNAMELEN > 14) ? 30 : 20)
X			 - strlen(cp) - strlen(temp)) > 0)
X			e_insn(SP, i);
X		e_sputz(" (");
X		e_sputz(temp);
X		e_sputz(") ");
X		if(cp = b->b_fn)
X			e_sputz(cp);
X#if IMAGEN
X		if (b->b_flags & B_CMODE)
X			e_sputz(" (C)");
X		else if (b->b_flags & B_TEXTMODE)
X			e_sputz(" (Text)");
X		else
X			e_sputz(" (Fundamental)");
X#endif /*IMAGEN*/
X		e_putc(LF);
X	  }
X	mk_showin(tbuf);	/* Show this buffer in temp window */
X	chg_buf(savbuf);	/* Return to real current buffer */
X	kill_buf(tbuf);
X}
X
X/* EFUN: "Buffer Not Modified" */
X/*	Mark the current buffer as not modified.
X */
Xf_bufnotmod()
X{
X	cur_buf -> b_flags &= ~B_MODIFIED;
X	redp(RD_MODE);
X}
X
X#if FX_EOLMODE
X/* EFUN: "EOL CRLF Mode" (not EMACS) */
X/*	Toggle the EOL mode of the current buffer.
X**		LF EOL Mode means LF alone is an EOL.
X**		CRLF EOL Mode means CRLF together is an EOL.
X*/
Xf_eolmode()
X{
X	cur_buf->b_flags ^= B_EOLCRLF;		/* Flip this bit */
X	say((cur_buf->b_flags & B_EOLCRLF)
X		? "EOL Mode is CRLF"		/* If now on */
X		: "EOL Mode is LF");		/* If now off */
X
X	redp(RD_WINRES);			/* Redo window for this buf */
X}
X#endif /*FX_EOLMODE*/
X
X#if FX_GOBEG
X/* EFUN: "Goto Beginning" */
Xf_gobeg()
X{	e_gobob();
X	ed_setcur();
X}
X#endif /*FX_GOBEG*/
X
X#if FX_GOEND
X/* EFUN: "Goto End" */
Xf_goend()
X{	e_goeob();
X	ed_setcur();
X}
X#endif /*FX_GOEND*/
X
X#if FX_WHATPAGE
X/* EFUN: "What Page" */
X/*	Extra info added as per earlier ICONOGRAPHICS "What Buffer Position"
X** Reports on current position as follows:
X**	Dot=<n>, Page <n>  Line <n> (line <n> of <m>)
X*/
Xf_whatpage()
X{
X	register chroff cnt;
X	register int c;
X	register int page, line;
X	int lineatp;
X	char tempstr[12], *dottoa ();
X
X        saynow("Dot=");
X        dottoa(tempstr, cur_dot);
X        sayntoo(tempstr);
X
X	e_gobob();
X	page = line = lineatp = 1;
X	for (cnt = cur_dot; --cnt >= 0;)
X		if ((c = e_getc()) == LF)
X			++line;
X		else if (c == FF)
X		  {	++page;
X			lineatp = line;
X		  }
X
X        saytoo(", Page ");
X        dottoa(tempstr, (chroff)page);
X        saytoo(tempstr);
X	saytoo("  Line ");
X        dottoa(tempstr, (chroff)(1 + line - lineatp));
X        saytoo(tempstr);
X	saytoo("  Col ");
X        dottoa(tempstr, (chroff)indtion(cur_dot));
X        saytoo(tempstr);
X	saytoo("  [line ");
X	dottoa(tempstr, (chroff)line);
X	saytoo(tempstr);
X	sayntoo(" of ");		/* Force out while scan rest */
X
X        for(e_gocur(); e_gonl() ; ++line) ;	/* Count lines until EOF */
X	c = e_rgetc();			/* Remember what last char is */
X        dottoa(tempstr, (chroff)line);
X        saytoo(tempstr);
X        if (c != LF)		/* Check last char */
X            saytoo(" (no EOL at EOF!)");
X        sayntoo("]");
X	e_gocur();			/* Back to original position */
X}
X#endif /*FX_WHATPAGE*/
X
Xinit_buf ()			       /* init buffer stuff */
X{
X	buf_head = 0;
X	lines_buf = cur_buf = make_buf(" **LINES**");	/* For sep_win */
X	e_insn('-',scr_wid-2);			/* Fill with dashes */
X	last_buf = cur_buf = make_buf ("Main");	/* Make Main buffer */
X	init_win();				/* Now can init windows */
X}
X
Xstruct buffer *
Xmake_buf(bname)	       /* create buffer "bname" if it doesn't exist */
Xchar *bname;
X{	register struct buffer *b;
X	register char *name;
X
X	b = find_buf(name = bname);
X	if (b)				/* if it exists already */
X		return(b);
X	b = (struct buffer *) memalloc(sizeof (struct buffer));
X	b -> b_next = buf_head;	       /* link it in */
X	buf_head = b;
X	b->b_name = strdup(name);	/* Allocate copy of name string */
X	b->b_dot = 0;		/* Set dot to beg */
X	sb_open(b,(SBSTR *)0);		/* Open buffer with null initial sbstring */
X	b->b_fn = 0;
X	b->b_flags = 0;
X	b->b_mode = cur_mode;	/* Inherit current mode */
X	return(b);
X}
X
X
Xstruct buffer *
Xfind_buf(name)	       /* returns pointer to buffer of that name or 0 */
Xchar *name;
X{	register struct buffer *b = buf_head;
X
X	while (b && strcmp(b->b_name, name))
X		b = b -> b_next;
X	return(b);
X}
X
Xsel_buf(b)				/* select buffer, saving last */
Xstruct buffer *b;
X{
X	if(b != cur_buf) last_buf = cur_buf;
X	chg_buf(b);
X}
X
Xchg_buf (newbuf)		       /* change current buffer to newbuf */
Xstruct buffer *newbuf;
X{	register struct buffer *obuf, *nbuf;
X
X	if ((nbuf = newbuf) == (obuf = cur_buf))
X		return;			/* Do nothing if same buffers */
X	obuf->b_dot = cur_dot;
X	cur_buf = nbuf;
X	cur_mode = nbuf->b_mode;
X	e_gosetcur(nbuf->b_dot);	/* Set cur_dot and go there */
X	cur_win->w_buf = nbuf;
X	cur_win->w_dot = cur_dot;
X#if IMAGEN
X	cur_win->w_redp = RD_WINRES|RD_REDO;
X#else
X	cur_win->w_redp = RD_WINRES;	/* Reset flags - do complete update */
X#endif /*-IMAGEN*/
X	unlk_buf(obuf);			/* Unlock previous buffer if can */
X	mark_p = 0;			/* this is lazy */
X	redp(RD_MODE|RD_WINRES);
X}
X
X/* See if specified buffer belongs to any active window, and
X * if not then get it into an idle, unlocked state; this helps the
X * edit package compact and swap stuff out while it's not being used.
X * Assumes proper state of dot has been stored into b_dot.
X */
Xunlk_buf(bufp)
Xstruct buffer *bufp;
X{	register struct buffer *b;
X	register struct window *w;
X
X	b = bufp;
X	for(w = win_head; w; w = w->w_next)
X		if(b == w->w_buf)
X			return;		/* Buffer is actively being shown */
X	sb_rewind((SBBUF *)b);		/* Return to idle state */
X}
X
X/* SEL_NBUF(buf) - Select next user buffer.  Ignores internal buffers.
X *	Arg of 0 starts at beg of buffer list.  Always returns
X *	a buffer pointer - returns argument (which may be 0)
X *	if found no other user buffers.
X *
X * SEL_MBUF(buf) - Select next modified buffer.
X *	Returns buffer ptr to "next" modified buffer, if any.
X *	Arg of 0 starts at beg of buffer list and scans all of them.
X *	Returns 0 if no other modified buffers exist (unlike SEL_NBUF!)
X *	Ignores internal buffers, whose names start with a space.
X */
X/* struct buffer *buf_mptr; */
X#if 0
Xstruct buffer *
Xsel_mbuf(buf)
Xstruct buffer *buf;
X{	register struct buffer *b;
X	register int sweep;
X
X	sweep = 0;			/* Make 2 sweeps only */
X	if(b = buf) b = b->b_next;
X	do {
X		if(b == 0)		/* Initialize if needed */
X			b = buf_head;
X		for(; b; b = b->b_next)
X			if((b->b_flags & B_MODIFIED) && (*b->b_name != SP))
X				return((b == buf) ? 0 : b);
X	  } while(sweep++ != 0);
X	return(0);
X}
X#endif /*COMMENT*/
X
Xstruct buffer *
Xsel_mbuf(buf)
Xregister struct buffer *buf;
X{	register struct buffer *b, *b2;
X	b = b2 = sel_nbuf(buf);
X	do {	if(b == buf) break;
X		if(b->b_flags & B_MODIFIED)
X			return(b);
X	  } while((b = sel_nbuf(b)) != b2);
X
X	return(0);
X}
X
Xstruct buffer *
Xsel_nbuf(buf)
Xregister struct buffer *buf;
X{	register struct buffer *b;
X
X	b = buf;
X	do {
X		if(!b || !(b = b->b_next))
X			b = buf_head;
X		if(*b->b_name != SP)
X			break;
X	  } while (b != buf);
X	return(b);
X}
X
X
Xkill_buf(buf)
Xstruct buffer *buf;
X{	register struct buffer *b, *b1, *bt;
X
X	b = buf;
X	b1 = 0;
X	for(bt = buf_head; bt && bt != b; bt = bt -> b_next)
X		b1 = bt;
X	if(bt == 0)
X	  {	ring_bell();
X		errbarf("No such buffer");	/* Internal error */
X		return;
X	  }
X	if (b1 == 0)
X		buf_head = b->b_next;
X	else
X		b1->b_next = b->b_next;
X	sbs_del(sb_close((SBBUF *)b));	/* Close buffer & delete sbstring */
X	sb_fdcls(-1);			/* Make sweep for unused FD's */
X	if(b->b_fn)
X		chkfree(b->b_fn);	/* Flush filename if one */
X	chkfree(b->b_name);		/* Flush name */
X	chkfree((char *)b);		/* Flush buffer */
X}
X
X/* ZAP_BUFFER - Delete all of the buffer, but if it's been modified,
X *	ask first.  Returns 0 if user aborts.
X */
Xzap_buffer()
X{
X#if IMAGEN
X	extern struct buffer *exec_buf;	/* in e_make.c */
X
X	if(cur_buf != exec_buf && cur_buf -> b_flags & B_MODIFIED)
X#else
X	if(cur_buf -> b_flags & B_MODIFIED)
X#endif /*-IMAGEN*/
X		if(ask_kbuf(cur_buf) <= 0)
X			return(0);		/* Aborted */
X	ed_reset();		/* This takes care of redisplay too */
X	mark_p = 0;
X#if IMAGEN
X	cur_buf->b_flags &= ~B_BACKEDUP; /* Clear backed-up flag */
X#endif
X	return(1);
X}
X
X
X
X/* ASK_KBUF - Ask user "are you sure?" before killing a buffer.
X *	Returns +1 if user says "yes" - OK to kill.
X *		 0 if user aborts (^G)
X *		-1 if user says "no".
X */
Xask_kbuf(buf)
Xstruct buffer *buf;
X{	register struct buffer *b;
X	register char *s;
X	register int ans;
X
X	b = buf;
X	s = ask("Buffer %s contains changes - forget them? ", b->b_name);
X	if(s == 0) return(0);
X	ans = (upcase(*s) == 'Y') ? 1 : -1;
X	chkfree(s);
X	return(ans);
X}
X
X/* Window stuff */
X
X/* Like EMACS, ELLE only provides at most two user windows.
X * The current user window is pointed to by user_win;
X * the "other" one is oth_win.  If oth_win == 0, there is only one user
X * window.
X */
X
X#if FX_2MODEWINDS
Xint sepmode_p = 0;	/* Set true if separator window is a 2nd mode win */
X#endif
X
X/* EFUN: "Two Windows" */
X/*	Divide the current window in half, put the current buffer in the
X *	other window, and go to the new window.
X */
Xf_2winds()
X{	register int h, t;
X	register struct window *w;
X
X	if (oth_win)
X	  {
X#if !(IMAGEN)
X		ding("Already 2 windows");
X#endif /*-IMAGEN*/
X		return;
X	  }
X	w = cur_win;
X	d_fixcur();			/* Stabilize current window */
X	h = (w->w_ht) / 2;
X	t = w->w_pos + h;		/* Pos of dividing window */
X	sep_win = make_win(t, 1, lines_buf);
X					/* assume using dashes to separate */
X	oth_win = make_win(t + 1, w->w_ht - (h + 1), cur_buf);
X					/* Other window has balance */
X#if FX_SOWIND
X	oth_win->w_flags |= cur_win->w_flags&W_STANDOUT;
X	sep_win->w_flags |= mode_win->w_flags&W_STANDOUT;
X#endif
X#if FX_2MODEWINDS
X	chk2modws();			/* Update 2-mode-wind status */
X#endif
X	w->w_ht = h;			/* Decrease current window to half */
X
X	/* Minimize redisplay by moving each window's dot into
X	 * a currently displayed area */
X	if(cur_dot < (oth_win->w_topldot = scr[t+1]->sl_boff))
X		oth_win->w_dot = oth_win->w_topldot;	/* Adj bottom win */
X	else					/* Adjust top window */
X	  {	oth_win->w_dot = cur_dot;	/* Bottom keeps dot */
X		cur_dot = scr[t-1]->sl_boff;	/* but top needs new one. */
X	  }
X	f_othwind();			/* switch to other window */
X	redp(RD_WINDS);			/* Update all windows */
X}
X
X
X/* EFUN: "One Window" */
X/*	Revert to using only one window; use the current buffer (unlike
X *	EMACS which always selects the top window's buffer)
X *	Ensures that current window's vars are correctly set for
X *	new dimensions (w_pos, w_ht, plus w_topldot to minimize redisplay),
X *	then kills unneeded windows.
X */
Xf_1wind()
X{	register struct window *w;
X
X	if (oth_win == 0)
X	  {
X#if (!IMAGEN)
X		ding("Only 1 window");
X#endif /*-IMAGEN*/
X		return;
X	  }
X	w = cur_win;
X	if(w->w_pos)		/* If not top window */
X	  {	d_fixcur();		/* Ensure screen-line data correct */
X		e_go(w->w_topldot);	/* Beginning from top of window, */
X		d_fgoloff(-w->w_pos);	/* Move back enough lines */
X		w->w_topldot = e_dot();	/* To set new start of window */
X		e_gocur();		/* Then move back to orig place */
X		w->w_pos = 0;
X	  }
X	w->w_ht += oth_win -> w_ht + 1;
X	kill_win (oth_win);
X	kill_win (sep_win);
X	oth_win = sep_win = 0;
X#if FX_2MODEWINDS
X	chk2modws();	/* Update notion of whether have 2 mode winds */
X#endif
X	redp(RD_FIXWIN|RD_WINDS|RD_MODE); /* New topldot for this window,
X					 * and check all remaining windows */
X}
X
X/* EFUN: "Other Window" */
X/*	Move to the "other" user window.
X */
Xf_othwind ()
X{	if (oth_win == 0)
X	  {
X#if !(IMAGEN)
X		ding("Only 1 window");
X#endif /*-IMAGEN*/
X		return;
X	  }
X	chg_win(oth_win);
X	oth_win = user_win;
X	user_win = cur_win;
X	redp(RD_MODE);
X}
X
X/* EFUN: "Grow Window" */
X/*	Grow the current window - while in two window mode,
X *	increase the size of the current window by the arg
X *	and decrease the other accordingly
X */
Xf_growind()
X{	register struct window *cw, *ow;
X	register int e;
X
X	if ((ow = oth_win) == 0)
X	  {
X#if !(IMAGEN)
X		ding("Only 1 window");
X#endif /*-IMAGEN*/
X		return;
X	  }
X	e = exp;
X	if((cw = cur_win)->w_pos != 0)	/* If current window is on bottom */
X	  {	cw = ow;		/* Then fake code to think it's top */
X		ow = cur_win;
X		e = -e;
X	  }
X	if(  cw->w_ht + e < 1
X	  || ow->w_ht + e < 1)
X	  {	ding("Too much");
X		return;
X	  }
X	cw -> w_ht += e;
X	ow -> w_pos += e;
X	ow -> w_ht -= e;
X	sep_win -> w_pos += e;
X	redp(RD_WINDS | RD_MODE);		/* Update all windows */
X}
X
X#if FX_SHRINKWIND
X/* EFUN: "Shrink Window" (not EMACS) - from IMAGEN config */
Xf_shrinkwind()
X{
X	if (! oth_win)
X		return;
X	f_othwind();
X	f_growind();
X	f_othwind();
X}
X#endif /*FX_SHRINKWIND*/
X
X#if FX_DELWIND
X/* EFUN: "Delete Window" (not EMACS) - from IMAGEN config */
Xf_delwind()
X{
X	if(!oth_win)
X		return;
X	f_othwind();
X	f_1wind();
X}
X#endif /*FX_DELWIND*/
X
X#if FX_SOWIND
X/* EFUN: "Standout Window" (not EMACS) */
X/*	Toggles the display standout mode for the current window.
X**	With argument of 4, toggles the standout mode for the non-buffer
X**	parts of the screen, such as the ELLE mode line.
X** (This corresponds to FS INVMOD$ in EMACS)
X**	With argument of 0, turns standout mode off for all windows.
X*/
X/* It suffices to set the window flag bit and force a RD_WINRES for that
X * window; the redisplay code will do the rest.
X*/
Xstatic void tgso_wind();
X
Xf_sowind()
X{
X	register struct window *w;
X	switch(exp)
X	  {	default:		/* Toggle current window */
X			tgso_wind(cur_win);
X			break;
X		case 4:			/* Toggle mode & separator windows */
X			tgso_wind(mode_win);
X			tgso_wind(sep_win);	/* This may not exist */
X			break;
X		case 0:			/* Turn off standout for all winds */
X			for(w = win_head; w; w = w->w_next)
X				if(w->w_flags&W_STANDOUT)
X					tgso_wind(w);
X	  }
X#if FX_2MODEWINDS
X	chk2modws();	/* Update notion of whether have 2 mode winds */
X#endif
X}
X
Xstatic void
Xtgso_wind(w)		/* Toggle standout mode for given window */
Xregister struct window *w;
X{
X	if (w == 0) return;		/* For case of no sep_win */
X	if (w->w_flags & W_STANDOUT)
X		w->w_flags &= ~W_STANDOUT;
X	else w->w_flags |= W_STANDOUT;
X	w->w_redp |= RD_WINRES;		/* Re-do this particular window */
X	redp(RD_CHKALL);		/* Check all windows for changes */
X}
X#endif /*FX_SOWIND*/
X
X
X#if FX_2MODEWINDS
X/* EFUN: "Two Mode Windows" (not EMACS) */
X/*	With arg, sets ev_2modws to that value (0, 1, or 2).
X**	No arg, toggles current setting between 0 and 2.
X*/
X
Xf_2modewinds()
X{
X	ev_2modws = exp_p ? exp : (ev_2modws ? 0 : 2);
X	chk2modws();
X}
X
X/* CHK2MODWS - Called after anything changes which might affect
X**	whether 2 mode windows are in effect or not.  Fixes up
X**	sep_win to either be or not be a mode window.
X*/
Xchk2modws()
X{	register struct window *w;
X	static struct buffer *sep_buf = 0;
X
X	if(!(w = sep_win))
X	  {	sepmode_p = 0;		/* Don't have 2 windows at all */
X		return;
X	  }
X	sepmode_p = (ev_2modws == 1)
X			? (mode_win->w_flags&W_STANDOUT)
X			: ev_2modws;
X
X	if(sepmode_p)		/* Turn 2-mode-winds on? */
X	  {
X		if(!sep_buf)
X			sep_buf = make_buf(" **SEPMODE**");
X		w->w_buf = sep_buf;
X		w->w_flags |= W_MODE;
X	  }
X	else			/* Turn 2-mode-winds off */
X	  {	w->w_buf = lines_buf;
X		w->w_flags &= ~W_MODE;
X		redp(RD_CHKALL);	/* No longer a mode win, so must */
X					/* check all to ensure it's updated */
X	  }
X	w->w_redp |= RD_WINRES;
X	redp(RD_MODE);
X}
X#endif /*FX_2MODEWINDS*/
X
Xinit_win ()
X{
X	win_head = 0;
X	oth_win = 0;
X	user_win = make_win(0, scr_ht - (ECHOLINES+1), cur_buf); /* Main */
X	mode_win = make_win(scr_ht - (ECHOLINES+1), 1, make_buf(" **MODE**"));
X	ask_win  = make_win(scr_ht - ECHOLINES,     1, make_buf(" **ASK**"));
X#if FX_SOWIND
X	if(ev_modwso)
X		mode_win->w_flags |= W_STANDOUT;
X#endif
X
X	cur_win = user_win;
X}
X
Xchg_win(newwin)		       /* change current window to newwin */
Xstruct window *newwin;
X{
X	cur_win->w_dot = cur_dot;	/* Save window's current dot */
X	cur_win->w_redp |= rd_type&RDS_WINFLGS;	/* and its redisplay flags */
X	cur_win = newwin;		/* OK, switch to new current window */
X	cur_buf = newwin->w_buf;	/* Set new buffer from win */
X	e_gosetcur(newwin->w_dot);	/* Set new cur_dot from win too */
X			/* Note done this way to canonicalize the location
X			** (may be past new EOB) and ensure SB buffer
X			** internals agree with cur_dot.
X			*/
X	rd_type &= ~RDS_WINFLGS;	/* Remove old per-window flags */
X	redp(RD_WINRES|RD_MODE);	/* Maybe caller shd handle? */
X			/* Note WINRES must be set in case we are pointing
X			 * to a buffer that was modified while we were in
X			 * the other window!
X			 */
X}
X
X
Xstruct window *
Xmake_win (pos, ht, buf)
Xint pos, ht;
Xstruct buffer *buf;
X{	register struct window *w;
X	register struct buffer *b;
X
X	b = buf;
X	w = (struct window *) memalloc(sizeof (struct window));
X	w->w_flags = 0;
X	w->w_pos = pos;
X	w->w_ht = ht;
X	w->w_buf = b;
X	w->w_dot = b->b_dot;	/* Set dot from buffer value */
X	w->w_topldot = 0;	/* Set top of window to beg of buffer */
X	w->w_pct = 200;		/* Assume "ALL" */
X	w->w_bmod = 0;
X	w->w_emod = 0;
X	w->w_oldz = 0;
X	w->w_redp = RD_WINRES;	/* Window will need complete update */
X	w->w_next = win_head;	/* Done, now link it in */
X	win_head = w;
X	return (w);
X}
X
Xkill_win (win)
Xstruct window *win;
X{	register struct window *w, *w1, *kw;
X
X	kw = win;
X	w1 = 0;
X	for (w = win_head; w && w != kw; w = w -> w_next)
X		w1 = w;
X	if (w == 0)
X	  {	ring_bell();
X		errbarf("No such window");	/* Internal error */
X		return;
X	  }
X	if (w1 == 0)
X		win_head = w -> w_next;
X	else
X		w1 -> w_next = w -> w_next;
X	kw->w_buf->b_dot = (kw == cur_win) ? cur_dot : kw->w_dot;
X	chkfree (kw);
X#if IMAGEN		/* Not needed? */
X	redp (RD_WINRES|RD_WINDS|RD_REDO);
X#endif /*IMAGEN*/
X}
X
X/*
X * "Show-window" routines, used to set up, step through, and close a
X * temporary "show" window.
X * MK_SHOWIN(bufp)
X * UP_SHOWIN()
X * KL_SHOWIN()
X */
X
X/* MK_SHOWIN(bufp) - Temporarily display a buffer
X */
Xmk_showin(b)
Xstruct buffer *b;
X{	register struct window *w;
X	register int i;
X	int moreflg, intflg;		/* Interrupt flag */
X	struct window *savwin;
X
X	/* First must set up special window... */
X	savwin = cur_win;
X	chg_win(w = make_win(0, scr_ht-(ECHOLINES+3), b));
X redo:
X	d_fixcur();		/* Fix up screen image of current window */
X
X	/* Find how many lines actually used, and reduce size to that */
X	i = w->w_ht;
X	while(--i >= 0)
X	  {
X		if(scr[i]->sl_boff != w->w_oldz) break;
X	  }
X	if(++i <= 0)
X		goto skipit;	/* Punt the whole thing */
X	if(!(moreflg = (i >= w->w_ht)))
X		w->w_ht = i;	/* Reduce size of window */
X
X	intflg = upd_wind(w);	/* Update the window! */
X	if(!intflg)		/* Unless input waiting, add prompt. */
X	  {
X		yellat( moreflg ?
X	"--MORE-- (type Space for more, or type any command to flush)" :
X	"------------------------------------------------ (Hit space to continue)--",
X			w->w_ht);
X		
X	  }
X	tbufls();		/* Ensure all output forced out */
X	i = cmd_read();		/* then wait for user to input a char */
X	if(i == SP)
X	  {	if(moreflg)
X		  {	yellat("", w->w_ht);
X			d_screen(1);
X			w->w_redp |= RD_WINRES;
X			goto redo;
X		  }
X	  }
X#if !(IMAGEN)		/* IMAGEN - always ignore what was typed */
X	else unrchf = i;
X#endif /*-IMAGEN*/
Xskipit:	chg_win(savwin);
X	kill_win(w);
X	redp(RD_WINDS);		/* Update all remaining windows */
X}
X
X/* Mode Line generation */
X
Xstruct window *
Xmake_mode(bw)
Xregister struct window *bw;	/* Base window we are reporting status of */
X{
X	register struct buffer *b;	/* Buffer of this window */
X	struct window *mw, *savew;	/* Save current window */
X	struct buffer *saveb;	/* and current buffer (in case different) */
X	char temp[20];
X
X	saveb = cur_buf;	/* Save values prior to context switch */
X	savew = cur_win;
X	b = bw->w_buf;		/* Get buffer for that window */
X
X#if FX_2MODEWINDS
X	if((mw = sep_win) && (mw->w_flags&W_MODE) &&
X	    (bw->w_pos == 0))		/* Base window is top window? */
X	  {				/* Use sep_win as mode wind */
X	  }
X	else
X#endif
X		mw = mode_win;		/* Default is normal mode window */
X	chg_win(mw);			/* Go to mode line window */
X	e_gobob();			/* go to beginning */
X	e_reset();			/* Flush buffer */
X#if IMAGEN
X	e_sputz(" ");
X	e_sputz(b->b_name);
X	if (b -> b_flags & B_MODIFIED)
X		e_sputz("*");
X	e_sputz(" (");
X	if (b->b_flags & B_QUERYREP)
X		e_sputz("[Query Replace] ");
X	if (b->b_flags & B_CMODE)
X		e_sputz("C");
X	else if (b->b_flags & B_TEXTMODE)
X		e_sputz("Text");
X	else
X		e_sputz("Fundamental");
X	e_sputz(")  ");
X	if (b->b_fn)
X		e_sputz(b->b_fn);
X	e_sputz("      ");
X#else
X	e_sputz(ev_verstr);		/* Editor name/version */
X	e_sputz(" (");
X	e_sputz(cur_mode->mjm_name);	/* insert major mode name */
X#if FX_FILLMODE
X	if(fill_mode) e_sputz(" Fill");
X#endif /*FX_FILLMODE*/
X#if FX_SKMAC
X	if(kdef_mode) e_sputz(" MacroDef");
X#endif /*FX_SKMAC*/
X	e_sputz(") ");
X	e_sputz(b->b_name);		/* buffer name */
X	e_sputz(": ");
X	if (b->b_fn)
X		e_sputz(b->b_fn);       /* file name */
X	if (b->b_flags & B_MODIFIED)
X		e_sputz(" *");
X	else	e_sputz("  ");
X#endif /*-IMAGEN*/
X	if(bw->w_pct < 200)		/* Not ALL? */
X	  {	e_sputz(" --");
X		switch(bw->w_pct)
X		  {	case -1:
X				e_sputz("TOP");
X				break;
X			case 150:
X				e_sputz("BOT");
X				break;
X			default:
X				dottoa(&temp[0],(chroff)bw->w_pct);
X				e_sputz(&temp[0]);
X				e_putc('%');
X		  }
X		e_sputz("--");
X	  }
X#if FX_SOWIND
X	if(mw->w_flags&W_STANDOUT)
X		e_insn(SP, (int)(scr_wd0 - e_blen()));	/* Stuff out with spaces */
X#endif
X
X	redp(RD_WINRES);
X	chg_win(savew);		/* Restore context */
X	chg_buf(saveb);
X	return(mw);		/* Return mode window */
X}
X
X
Xbuf_mod()
X{	register struct buffer *b;
X
X	b = cur_buf;
X	if((b->b_flags & B_MODIFIED) == 0)
X	  {	b->b_flags |= B_MODIFIED;
X		redp(RD_MODE);
X	  }
X}
X
X/* BUF_TMOD - called when text modified in buffer, to set all
X *	the appropriate indicators so that redisplay works right.
X *	Changed text is everything from CUR_DOT to the given offset
X *	from same.  If stuff was deleted, offset should be 0.
X * BUF_TMAT - similar but argument is location of other end of range,
X *	when caller knows that and wants life easy.
X */
X
Xbuf_tmat(dot)
Xchroff dot;
X{	buf_tmod(dot - cur_dot);	/* Convert to offset */
X}
Xbuf_tmod(offset)
Xchroff offset;
X{	register struct window *w;
X	chroff a, b, tmp;
X
X	w = cur_win;
X	a = cur_dot;
X	b = a + offset;
X	if(a > b)	/* Get into right order */
X	  {	tmp = a;
X		a = b;
X		b = tmp;
X	  }
X	b = e_blen() - b;	/* Make upper bound relative to EOB */
X	if(w->w_bmod < 0)	/* Have range vars been set yet? */
X	  {	w->w_bmod = a;	/* Nope, so can just set 'em now. */
X		w->w_emod = b;
X	  }
X	else
X	  {	if(a < w->w_bmod)
X			w->w_bmod = a;
X		if(b < w->w_emod)
X			w->w_emod = b;
X	  }
X	buf_mod();		/* Maybe later just insert here? */
X	redp(RD_TMOD);
X}
/
echo x - eecmds.c
sed '/^X/s///' > eecmds.c << '/'
X/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X
X/* EECMDS	Command table lookup and profile code
X */
X
X#include "elle.h"
X
X/* Function table, see the included file for explanation. */
X
X	/* First must pre-declare function addrs */
X#define EFUN(rtn,rtnstr,name) int rtn();
X#define EFUNHOLE
X#include "eefdef.h"
X
X	/* Now re-insert to define function table */
Xint (*funtab[])() =
X{
X#undef EFUN		/* Avoid redefinition error message */
X#undef EFUNHOLE
X#define EFUN(rtn,rtnstr,name) rtn,
X#define EFUNHOLE 0,
X#include "eefdef.h"
X};
Xint funmax = sizeof(funtab)/sizeof(funtab[0]);	/* 1st illegal function # */
X
X/* Insert default command char map tables and profile structure */
X
X#include "defprf.c"
X
X/* EFUN: "Prefix Meta" */
X/*	Meta-prefix command.
X *	For now, very simple.  Perhaps later try to hair up with
X *	time-out "M-" prompt?
X */
Xf_pfxmeta()
X{	return(cmd_xct(cmd_read()|CB_META));
X}
X
X/* EFUN: "Prefix Extend" */
X/*	Extended-prefix command.
X *	Likewise trivial; perhaps later hair up with timeout "^X-" prompt?
X */
Xf_pfxext()
X{	return(cmd_xct(cmd_read()|CB_EXT));
X}
X
X/* EFUN: "Universal Arg" */
X/*	This routine is also called by "Argument Digit" with a special arg
X * of -1 in order to share code.  Since that invocation always sets unrchf,
X * it should always complete at least one digit read loop.
X * Note that exp and exp_p are set to 1 and 0 at the top-level command
X * loop.
X */
Xf_uarg(ch)
Xint ch;
X{	register int c, oc, i;
X
X	/* Set distinguishing exp_p value depending on whether invoked
X	 * by CTRL-U or another function (Argument Digit, Negative Argument)
X	 */
X	exp_p = (ch < 0) ? 1 : 4;
X	i = 0;			/* Read numerical arg if any follows */
X	for(;;)
X	  {	oc = cmd_read();	/* Get next input char */
X		c = oc & 0177;
X		if(c == '-' && !i)
X		  {	exp_p = -1;
X			exp = 1;	/* Set in case no digits follow */
X		  }
X		else if('0' <= c && c <= '9')	/* If it's a digit too, */
X		  {	i = (i * 10) + c - '0';	/* add digit in. */
X			if(exp_p >= 0) exp_p = 1;
X			exp = i;
X		  }
X		else break;
X	  }
X	exp *= exp_p;		/* Multiply arg appropriately */
X	unrchf = oc;		/* Not a digit, re-read it next. */
X
X	this_cmd = ARGCMD;
X}
X
X/* EFUN: "Negative Argument" */
Xf_negarg(ch)
Xint ch;
X{	f_uarg(-1);		/* Invoke code from Universal Arg */
X	exp = -exp;
X}
X
X/* EFUN: "Argument Digit" */
Xf_argdig(ch)
Xint ch;
X{	unrchf = ch;		/* Re-read the digit */
X	f_uarg(-1);		/* Invoke code from Universal Arg */
X}
X
X/* EFUN: "Set Profile" */
X/*	Asks for a profile file and sets profile from it.
X */
Xf_setprof()
X{	int set_profile();
X	hack_file("Set Profile: ", set_profile);
X}
X
X#if FX_VTBUTTONS
X/* EFUN: "VT100 Button Hack" */
X/*	This must be bound to Meta-O if anything, because the VT100 sends
X *	an ESC O prefix when the function buttons are used.
X */
Xf_vtbuttons ()			/* vt100 function buttons */
X{
X	switch(cmd_read())
X	  {	case ('A'): 
X			return (f_uprline ());
X		case ('B'): 
X			return (f_dnrline ());
X		case ('C'): 
X			return (f_fword ());
X		case ('D'): 
X			return (f_bword ());
X		case ('Q'): 		/* PF1 */
X			return (f_kregion());
X		default: 
X			ring_bell ();
X		break;
X	  }
X}
X#endif /*FX_VTBUTTONS*/
X
X/* CMD_WAIT() - Return TRUE if any command input waiting.
X*/
Xcmd_wait()
X{	return(unrchf >= 0
X#if FX_SKMAC
X		|| km_inwait()		/* Check for kbdmac input waiting */
X#endif /*FX_SKMAC*/
X		|| tinwait());
X}
X
X/* CMD_READ() - Read a command (single char) from user, and return it.
X*/
Xcmd_read()
X{	register int c;
X
X	if((c = unrchf) >= 0)	/* Re-reading last char? */
X	  {	unrchf = -1;
X		return(c);
X	  }
X#if FX_SKMAC			/* Hacking keyboard macros? */
X	return(km_getc());	/* Yes.  This calls tgetc if no kbd macro */
X#else
X	return(tgetc());
X#endif /*-FX_SKMAC*/
X}
X
X/* CMD_XCT(ch) - Command Execution dispatch routine.
X**	Takes char and executes the function (efun) bound to that command key.
X*/
Xcmd_xct(ch)
Xint ch;
X{	register int (*funct) ();
X	register int c;
X	int (*(cmd_fun())) ();
X
X	if(funct = cmd_fun(c = ch))		/* Get function to run */
X		return((*funct) (c&0177));	/* Invoke with char arg */
X	ring_bell();		/* Undefined command char, error. */
X}
X
X/* CMD_FUN(ch) - Return function for char, 0 if none
X*/
Xint (*cmd_fun(c))()
Xint c;
X{
X	return(funtab[cmd_idx(c)]);
X}
X
X/* CMD_IDX(ch) - Given command char, return function index for it
X*/
Xcmd_idx(c)
Xregister int c;
X{	register char *cp;
X	register int i;
X
X	if(c&CB_EXT)
X	  {	cp = def_prof.extvec;
X		i = def_prof.extvcnt;
X		goto inlup;
X	  }
X	if(c&CB_META)
X	  {	cp = def_prof.metavec;
X		i = def_prof.metavcnt;
X	inlup:	c = upcase(c);
X		do {	if(*cp++ != c) cp++;
X			else
X			  {	i = *cp&0377;
X				break;
X			  }
X		  } while(--i);		/* If counts out, will return 0! */
X	  }
X	else i = def_prof.chrvec[c&0177]&0377;
X	if(i >= funmax)
X		return(0);
X	return(i);
X}
X
X/* Profile hacking */
X
X#if TOPS20
X#include <sys/file.h>		/* for O_BINARY */
X#endif
X
Xset_profile(filename)
Xchar *filename;
X{	char pfile[200];
X	char psfile[200];
X	register int pfd, len;
X	chroff sbx_fdlen();
X	register char *profptr;
X
X	if(filename) strcpy(pfile,filename);
X	else		/* Check for user's profile */
X	  {
X		strcat(strcat(strcpy(pfile,homedir),"/"),ev_profile);
X	  }
X	if((pfd = open(pfile,
X#if TOPS20
X				O_BINARY
X#else
X				0
X#endif
X					)) < 0)
X	  {	if(filename)
X		  {	ding("Cannot open file");
X		  }
X		return;
X	  }
X	if((len = (int)sbx_fdlen(pfd)) < sizeof(struct profile))
X		goto badfil;
X	profptr = memalloc((SBMO)len);
X	if(read(pfd,profptr,len) != len)
X		goto badfmt;
X
X	/* Have read profile into memory, now set up ptrs etc */
X	if(((struct profile *)profptr)->version != 1)
X		goto badfmt;
X	bcopy((SBMA)profptr,(SBMA)&def_prof,sizeof(struct profile));
X	def_prof.chrvec  += (long) profptr;
X	def_prof.metavec += (long) profptr;
X	def_prof.extvec  += (long) profptr;
X#if SUN
X	def_prof.menuvec += (long) profptr;
X#endif /*SUN*/
X	goto done;
X
Xbadfmt:	chkfree(profptr);
Xbadfil:	ding("Bad profile format");
Xdone:	close(pfd);
X}
X
X#if SUN
X/* SUN Menu profile hacking.
X *	This is here, instead of e_sun.c, because
X * the profile format is still evolving and for the time being I want to
X * keep all profile-hacking code in one place. --KLH
X */
X#include "suntool/tool_hs.h"
X#include "suntool/menu.h"
X
X#define MENUMAX	16
X
X/* Defined in eesun.c */
Xextern struct menu *menuptr;
Xextern struct menu menu;
X
Xchar *funamtab[] = {
X#undef EFUN
X#undef EFUNHOLE
X#define EFUN(rtn,rtnstr,name) name,
X#define EFUNHOLE 0,
X#include "eefdef.h"
X};
X
Xinit_menu() /* initialize the menu for elle from user profile */
X{
X	register struct menuitem *mi;
X	register int n, i, fni;
X
X	if((n = def_prof.menuvcnt) <= 0)
X		return;
X	if(n > MENUMAX) n = MENUMAX;
X	mi = menu.m_items = (struct menuitem *) calloc(n, sizeof *mi);
X
X	menu.m_itemcount = 0;
X	for(i = 0; i < n; i++)
X	  {	fni = def_prof.menuvec[i]&0377;
X		if(fni >= funmax) continue;
X		if(funtab[fni] && funamtab[fni])
X		  {	mi->mi_data = (caddr_t) funtab[fni];
X			mi->mi_imagedata = (caddr_t) strdup(funamtab[fni]);
X			mi->mi_imagetype = MENU_IMAGESTRING;
X			mi++;
X			menu.m_itemcount++;
X		  }
X	  }
X	if(menu.m_itemcount)
X		menuptr = &menu;
X}
X#endif /*SUN*/
/
echo x - eediag.c
sed '/^X/s///' > eediag.c << '/'
X/* ELLE - Copyright 1982, 1985, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*	EEDIAG - Error diagnostics and testing routines
X */
X
X#include "elle.h"
X
X#if FX_DEBUG
X
X/* EFUN: "Debug Mode" */
X/*	With no arg, toggles self-checking on and off.
X *	With arg of 4 (^U), enters special debug/diagnostics mode.
X */
X
Xf_debug(ch)
Xint ch;
X{	extern int (*vfy_vec)();	/* In E_MAIN.C */
X	char *vfy_data();
X
X	if(ch < 0)		/* Internal call? */
X	  {	dbg_diag();
X		return;
X	  }
X	if(exp == 4)
X	  {	askerr();
X		return;
X	  }
X	if(vfy_vec) vfy_vec = 0;		/* Toggle current value */
X	else vfy_vec = (int (*)())vfy_data;
X	say(vfy_vec ? "Self-checking on" : "Self-checking off");
X}
X
Xchar *
Xvfy_data(flag)		/* Flag = 0 for quiet check */
Xint flag;
X{
X	register char *res, *mess;
X	char *sbe_mvfy(), *sbe_sbvfy(), *sbe_svfy();
X
X	if(res = sbe_mvfy(0)) mess = "Mem mgt";
X	else if(res = sbe_sbvfy(cur_buf,0)) mess = "SBBUF";
X	else if(res = sbe_svfy(0)) mess = "SD list";
X	else return(0);		/* Success */
X
X	if(flag)
X	  {	int ostate = clean_exit();
X		printf("\n%s error: %s !!!\n",mess,res);
X		askerr();
X		if(ostate > 0) set_tty();
X	  }
X	return(res);	/* Error seen */
X}
X
Xextern char *asklin();
Xextern int sbx_nfl,sbm_nfl;
X
Xchar diaghelp[] = "\
XQ - Quit diag mode\n\
X! - Goto subshell\n\
XV - Verify Mem & SD lists\n\
XMF - Mem Freelist\n\
XM  - Mem list\n\
XB  - Current buffer SB\n\
XDF - SD Freelist\n\
XD  - SDs in use\n\
XDL - SD Logical lists\n\
XDP - SD Physical lists\n\
XC n - Compact; 0-7=sbx_comp(n), 8=SM freelist, 9=SD freelist.\n\
XW  - Window printout\n\
XX n - Xercise randomly (GC every n)\n\
XZ n - like X but with notes\n";
X
Xdbg_diag()
X{	register char *cp;
X	register int c;
X	char linbuf[100];
X	char *sbe_mfl();
X	char *sbe_sfl();
X	char *sbe_sbs();
X	char *sbe_sdlist();
X
X    for(;;)
X    {	printf("D>");
X	asklin(cp = linbuf);			/* Read a line of input */
X	switch(upcase(*cp++))
X	  {
X	case '?':
X		writez(1,diaghelp);	/* Too long for printf */
X		continue;
X	case '!':
X		f_pshinf();		/* Invoke inferior subshell */
X		clean_exit();		/* Restore normal modes */
X		continue;
X	case 'Q':		/* Quit */
X		return;
X
X	case 'B':		/* Print current SBBUF */
X		sbe_sbs(cur_buf,1);
X		continue;
X
X	case 'C':		/* C n - Compact */
X		c = atoi(&linbuf[1]);
X		if(c == 8)
X			sbm_ngc();	/* GC the SM nodes */
X#if 0 /* This doesn't work, dangerous to invoke. */
X		else if(c == 9)
X			sbm_xngc(&sbx_nfl,sizeof(struct sdblk),
X				SM_DNODS);
X#endif
X		else
X			sbx_comp(512,c);
X		continue;
X
X	case 'D':		/* Print all SD blocks in mem order */
X		switch(upcase(*cp))
X		  {
X		case 0:		/* D - all SDs in mem order */
X			sbe_sds();
X			continue;
X		case 'F':	/* DF - SD freelist */
X			sbe_sfl(1);
X			continue;
X		case 'L':	/* DL - SD logical list */
X			sbe_sdlist(1,0);
X			continue;
X		case 'P':	/* DP - SD physical list */
X			sbe_sdlist(1,1);
X			continue;
X		  }
X		break;		/* failure */
X
X	case 'M':	
X		switch(upcase(*cp))
X		  {
X		case 0:		/* M - all mem alloc info */
X			sbe_mem();
X			continue;
X		case 'F':	/* MF - mem freelist */
X			sbe_mfl(1);
X			continue;
X		  }
X		break;		/* failure */
X
X	case 'V':		/* Verify */
X		if(cp = vfy_data(0))
X			printf("  Failed: %s\n",cp);
X		else printf("  OK\n");
X		continue;
X	case 'W':		/* Print out current window */
X		db_prwind(cur_win);
X		continue;
X	case 'X':		/* Xercise */
X		c = atoi(&linbuf[1]);
X		vfy_exer(0, c ? c : 100);
X		continue;
X	case 'Z':		/* Zercise */
X		c = atoi(&linbuf[1]);
X		vfy_exer(1, c ? c : 100);
X		continue;
X
X	  }	/* End of switch */
X
X	printf("?? Type ? for help\n");
X    }	/* Loop forever */
X}
X
X
X/* VFY_EXER - a "random" editor exerciser.  It creates a buffer,
X *	fills it with some patterned stuff, and then edits it
X *	pseudo-randomly in ways which retain the basic pattern.
X *	Frequent GC's and self-checks are done, and execution
X *	halted either when an error is seen or when typein is detected.
X */
Xchar *xer_strs [] = {
X	"throne", "too", "sky", "fore", "fingers", "sex", "stone",
X	"010", "nazgul", "base"
X};
X
X
Xvfy_exer(pf, gcfrq)
Xint pf;			/* Nonzero to print notes as we go */
Xint gcfrq;		/* Frequency of GC invocation (# passes per GC) */
X{	register int i, k, c;
X	long npass;
X	char *res, linbuf[100];
X	chroff lbeg, lend;
X	struct buffer *bfp, *make_buf();
X
X	/* Clean out kill buffer first */
X	for(i = 0; i < KILL_LEN; ++i)
X		kill_push((SBSTR *)0);
X
X	bfp = make_buf("**EXORCISE**");
X	chg_buf(bfp);
X	i = 2000;
X	e_gobol();
X	do {
X		ed_sins("Line ");
X		ed_sins(xer_strs[i%10]);
X		e_putc(LF);
X	  } while(--i);
X	if(pf) printf("Bufflen: %ld\n", e_blen());
X
X	/* Buffer now has stuff in it, start hacking. */
X	npass = 0;
X	srand(1);	/* Start random seed */
X	for(;;)
X	  {	if(tinwait() && (*asklin(linbuf)))
X		  {	printf("Typein stop.\n");
X			break;
X		  }
X		++npass;
X		printf(" Pass %ld",npass);
X		if(npass%gcfrq == 0)		/* Time to do a GC? */
X		  {
X			i = rand();		/* Level between 0-4 */
X			i = (i < 0 ? -i : i) % 5;
X			printf(" - GC lev %d\n", i);
X			sbx_comp(512,i);
X			goto xerchk;
X		  }
X
X		k = (i = rand())%1024;
X		if (i&020000) k = -k;
X		e_igoff(k);		/* Move randomly */
X		e_gobol();		/* Get stuff to flush */
X		lbeg = e_dot();
X		k = (i = rand())%64;
X		if(i&010000) k = -k;
X		e_igoff(k);
X		lend = e_nldot();
X		if(pf) printf(" Kill %ld/ %d;", lbeg, k);
X		ed_kill(lbeg, lend);
X		if(res = vfy_data(0))
X		  {	printf("XERR after kill: %s\n",res);
X			break;
X		  }
X		k = (i = rand())%2048;
X		if(i&04000) k = -k;
X		e_igoff(k);
X		e_gobol();
X		e_setcur();
X		if(pf) printf(" Yank %ld;", e_dot());
X		f_unkill();		/* Yank back */
X		if(res = vfy_data(0))
X		  {	printf("XERR after yank: %s\n",res);
X			break;
X		  }
X		last_cmd = YANKCMD;
X		for(i = rand()%4; i >= 0; --i)
X		  {	if(pf) printf(" Pop;");
X			f_unkpop();	/* Do meta-Y */
X			if(res = vfy_data(0))
X			  {	printf("XERR after pop: %s\n",res);
X				goto out;
X			  }
X		  }
X		if(rand()&07)	/* Slowly add stuff */
X		  {	if(pf) printf(" Add");
X			ed_sins("Line ");
X			ed_sins(xer_strs[rand()%10]);
X			e_putc(LF);
X			if(res = vfy_data(0))
X			  {	printf("XERR after ins: %s\n",res);
X				break;
X			  }
X		  }
X		printf("\n");
X
X		/* Okay, done with this pass edits, run through the
X		 * file to ensure pattern is still there
X		 */
X	xerchk:	e_gobob();
X		while((c = e_getc()) != EOF)
X			if(c == LF && (c = e_getc()) != EOF)
X			  {	if(         c != 'L'
X				  || e_getc() != 'i'
X				  || e_getc() != 'n'
X				  || e_getc() != 'e'
X				  || e_getc() != ' ')
X				  {	printf("XERR in pattern!\n");
X					goto out;
X				  }
X			  }
X	  }
X	/* User typein or error, stop. */
Xout:	e_setcur();
X	redp(RD_SCREEN);
X	printf("Loop count = %ld\n",npass);
X}
X
X/* DB_PRWIND(win) - Print out stuff about given window
X */
Xdb_prwind(w)
Xregister struct window *w;
X{	register struct scr_line *s;
X	register int i;
X	char tstr[MAXLINE+MAXCHAR];
X	char *db_scflgs();
X
X	printf("cur_dot/ %ld  cur_buf/ %o cur_win/ %o\n",
X		cur_dot, cur_buf, cur_win);
X
X	printf("Window %o:\n", w);
X	printf("  next/ %o\n", w->w_next);
X	printf("  buf / %o\n", w->w_buf);
X	printf("  redp/ %o\n", w->w_redp);
X
X	printf("  topldot/ %ld\n", w->w_topldot);
X	printf("  dot / %ld\n", w->w_dot);
X	printf("  bmod/ %ld\n", w->w_bmod);
X	printf("  emod/ %ld\n", w->w_emod);
X	printf("  oldz/ %ld\n", w->w_oldz);
X
X	printf("  pos / %d\n", w->w_pos);
X	printf("  ht  / %d\n", w->w_ht);
X	printf("\
X#  Flags   Boff Len ! Cols Line\n");
X	for(i = w->w_pos; i < w->w_pos + w->w_ht; ++i)
X	  {	s = scr[i];
X		printf("%2d %-5.5s %6ld %3d %1d %4d ",
X			i, db_scflgs(s->sl_flg), s->sl_boff, s->sl_len,
X			s->sl_cont, s->sl_col);
X		strncpy(tstr, s->sl_line, MAXLINE);
X		tstr[s->sl_col] = 0;
X		printf("%-40.40s\n", tstr);
X		if(s->sl_flg&SL_MOD)
X		  {	printf("%26d ", s->sl_ncol);
X			strncpy(tstr, s->sl_nlin, MAXLINE);
X			tstr[s->sl_ncol] = 0;
X			printf("%-40.40s\n", tstr);
X		  }
X	  }
X}
X
Xchar *
Xdb_scflgs(flags)
Xint flags;
X{	static char retstr[10];
X	register char *cp;
X	
X	cp = retstr;
X	if(flags&SL_MOD) *cp++ = 'M';
X	if(flags&SL_EOL) *cp++ = 'E';
X	*cp = 0;
X	return(retstr);
X}
X
X#endif /*FX_DEBUG*/
/
echo x - eedisp.c
sed '/^X/s///' > eedisp.c << '/'
X/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/* EEDISP	Redisplay and screen image routines
X */
X
X#if 0
X
XNote that there are several different types of "efficiency" criteria
Xinvolved with respect to display updating:
X	(1) Terminal speed: minimize # characters output.
X	(2) Program speed: minimize CPU time used.
X	(3) Program size: minimize code and memory usage.
X	(4) Program modularity: minimize "hooks" between edit/display rtns.
XThe current algorithms necessarily represent a compromise among all of
Xthese objectives.
X
X	The cursor is always located at CUR_DOT in the buffer CUR_BUF
Xof the current window CUR_WIN.  This may not be true during function
Xexecution, but is always true at the top-level loop of command
Xexecution and redisplay.  In order to minimize update overhead, there
Xare various flags or variables that the edit functions can use to
Xcommunicate with "redisplay" and tell it how extensive the updates
Xreally need to be.
X
X	The entire known screen is always represented by a linked list
Xof "windows"; updating the entire screen consists of separately
Xupdating every window on the list.  Windows can only be defined
Xhorizontally (as a range of lines), and must not overlap.  Each window
Xhas a buffer associated with it; the redisplay routines are responsible
Xfor displaying the contents of this buffer.
X
X	The lowest level data structure for the screen consists of an
Xarray of SCR_LINE structures, one for each possible physical screen
Xline.  Each line structure has some flags, and pointers to three different
Xrepresentations of what should be on the line:
X	(1) SL_BOFF, SL_LEN - Defines the range of the buffer data which
X		this screen line should represent.
X		If the flag SL_EOL is set, this range ends with (and includes)
X		an EOL character.
X	(2) SL_LINE, SL_COL - Always keeps a copy of the current physical
X		screen line image.  Each byte is a character which occupies
X		only one column position on the screen.
X		If the flag SL_CSO is set, the line is in standout mode.
X	(3) SL_NLIN, SL_NCOL - The desired "new" screen line image.
X		This is only valid if the SL_MOD flag is set for the line,
X		indicating that these variables are set and point to the
X		new image of what the screen line should be.
X		If the flag SL_NSO is set, the new line should be in standout
X		mode.
X
X	Lastly there is a variable SL_CONT, which is needed for
Xcontinuation of too-long logical lines over several physical lines.  If
XSL_CONT is:
X	0 = logical line fits entirely on the screen.
X		Either SL_EOL is set, or this line is ended by EOF
X		(end of the buffer).
X	1 = logical line is too long, but the last buffer char fits
X		entirely on this physical line.  SL_EOL is never set.
X	>1 = logical line is too long, and the last buffer char
X		"overruns" the end of the physical image; that is, part of
X		its representation is at the end of this line, but the
X		rest of it is at the start of the next line.  This can
X		only happen with "big" characters like TAB, ^A, ~^A, etc.
X		that need more than one column of representation.
X		There are SL_CONT-1 chars of overrun stored at the
X		end of SL_LINE (SL_NLIN if SL_MOD is set).
X		SL_EOL is never set.
X
XNote that if a line contains any overrun, and the next line is also
Xpart of the same window, the next line''s screen image will start with
Xthe SL_CONT-1 chars of overrun, rather than with the representation of
Xthat line''s first buffer char.
X
X	The "EOL" character on Unix systems is normally the new-line
Xcharacter '\n' (ASCII LF).  However, on other systems EOL may be
Xindicated by a two-character CR-LF sequence, with either CR or LF alone
Xconsidered to be "stray".  For this reason, the buffer flag B_EOLCRLF
Xexists to control handling and display of EOLs.  If the flag is off,
Xthe EOL mode is LF, and there are no problems of splitting up characters.
XIf the flag is on, however, the EOL mode is CRLF and the following rules
Xhold:
X	EOL is the sequence CR-LF only.
X	LF without preceding CR is a "stray" LF, displayed as ^J.
X	CR without following LF is a "stray" CR, displayed as ^M.
X	Stray LFs and CRs do not terminate a logical line.
X	"End of Line" as a position is the dot just before the CR of a CR-LF.
X	"Beg of Line" as a position is the dot just after the LF of a CR-LF.
X	If the current dot is between a CR and LF, it is positioned at
X		the beginning of the physical screen line.
X
X
XSL_LINE and SL_COL are always accurate at every stage of processing.
XThe other variables are accurate only after fix_wind has been called
Xto "fix up" the line structures within a window.  If either
XRD_WINRES or RD_TMOD is set, none of these "other variables" should
Xbe depended on.  Any functions which are screen-relative (d_ type)
Xmust be sure that fix_wind is called if necessary, and must give
Xpreference to the "new" representation in SL_NLINE and SL_NCOL if
XSL_MOD is set.
X
XThe flag RD_UPDWIN will be set by fix_wind if any lines have been
Xmodified.  Because fix_wind does not perform any actual display update,
Xit is possible for functions to continue operating on the buffer and
Xscreen image without requiring that changes be displayed until there is
Xnothing else left to do.  The routine upd_wind performs the actual
Xterminal I/O necessary to update all the screen lines which have SL_MOD
Xset.  Although the process of updating each line is currently
Xnon-interruptible, it is possible for upd_wind to interrupt itself
Xbetween line updates if it detects that user input has happened, and it will
Xreturn with the window only partially updated.  The screen image state
Xwill be completely consistent, however, and the RD_UPDWIN flag will
Xremain set.
X
XCommunication between the editing functions and the redisplay routines
Xis limited as much as possible to the flags in the global RD_TYPE.
XEach window has its own copy of these flags in W_REDP, so that if
Xwindows are changed, the update hints for that window will be
Xpreserved.  The flags that can be set are listed below.  Those marked
Xwith "*" are global in nature; all others apply only within a single
Xwindow (normally the current window).
X
X* RD_SCREEN - Total refresh.  Clears entire screen and redisplays all
X	windows.
X* RD_MODE - Mode line has changed, update it.
X* RD_CHKALL - Check ALL windows for any redisplay flags, and perform
X	any updates necessary.  Otherwise only the current (or specified)
X	window flags are checked.
X* RD_WINDS - Updates all windows.  Like RD_WINRES applied to all windows.
X  RD_WINRES - Update window (assume completely changed).
X  RD_TMOD - Text changed in this window.  The range of changes is
X	specified by W_BMOD and W_EMOD in combination with W_OLDZ.
X	Redisplay checking will limit itself to this range.
X	These vars are set by buf_tmod in the main command loop, and
X	reset by fix_wind when the window is fixed up.
X  RD_MOVE - Cursor has moved within current window; may have moved outside
X	the window.  W_DOT or CUR_DOT specifies where it should be.
X  RD_ILIN - Hint: Line insert done.  Currently no function sets this.
X  RD_DLIN - Hint: Line delete done.  Currently no function sets this.
X
XInternal flags:
X  RD_UPDWIN - Window needs updating. Used by fix_wind and upd_wind only.
X	Set when window has been "fixed up" and at least one screen
X	line was modified.
X  RD_FIXWIN - Supposed to mean window needs fixing (via call to fix_wind).
X	Not really used.
X
XNot implemented, may never be, but comments retained:
X  RD_WINCLR - Clear window (not entire screen)
X  RD_NEWWIN - Window has moved.  (not needed? Random stuff here)
X	a. to follow cursor; redisplay selects a new TOPLDOT.
X	b. randomly; new TOPLDOT furnished, use unless cursor out (then a).
X	c. find new TOPLDOT as directed (move up/down N screen lines)
X	For now, assume that (c) doesn''t apply (ie C-V uses (b) and sets
X	TOPLDOT itself).  So fix_wind selects new one only if cursor
X	won''t fit.  topldot takes precedence over sl_boff.
X
X#endif /*COMMENT*/
X
X/* Declarations and stuff */
X
X#include "elle.h"
X
Xstatic int sctr();
X
Xint trm_mode;	/* 0 = TTY in normal, non-edit mode.
X		 * 1 = TTY in edit mode.
X		 * -1 = TTY detached (hung up).
X		 * This flag is only used by the 3 routines below,
X		 * plus hup_exit.
X		 */
X
X/* REDP_INIT() - Called once-only at startup to initialize redisplay
X *	and terminal
X */
Xredp_init ()
X{
X	trm_mode = 0;		/* Ensure flag says not in edit mode */
X	ts_init();		/* Get sys term info, set up stuff */
X	if (trm_ospeed == 0)	/* Default speed to 9600 if unknown */
X		trm_ospeed = 13;
X	t_init();		/* Identify term type, set term-dep stuff */
X	set_scr();		/* Set up software screen image */
X	set_tty();		/* Enter editing mode! */
X	redp(RD_SCREEN|RD_MODE); /* Force full re-display, new mode line */
X}
X
X/* SET_TTY() - Set up terminal modes for editing */
X
Xset_tty()
X{	if(trm_mode) return;	/* Ignore if detached or in edit mode */
X	trm_mode++;
X	ts_enter();		/* Set up system's ideas about terminal */
X	t_enter();		/* Set terminal up for editing */
X}
X
X/* CLEAN_EXIT() - Restore original terminal modes.
X *	Returns previous state.
X */
Xclean_exit ()
X{	register int prevstate = trm_mode;
X
X	if(prevstate > 0)	/* Ignore unless in editing mode */
X	  {	trm_mode = 0;
X		t_curpos(scr_ht-1, 0);	/* Go to screen bottom */
X		t_exit();		/* Clean up the terminal */
X		tbufls();		/* Force out all buffered output */
X		ts_exit();		/* Restore system's old term state */
X#if ! IMAGEN
X		writez(1,"\n");		/* Get fresh line using OS output */
X#endif /*-IMAGEN*/
X	  }
X	return prevstate;
X}
X
X/* SET_SCR() - Allocate screen image, set up screenline pointer table */
X
Xset_scr()
X{	register struct scr_line **scrp, *stp;
X	register scrsiz;
X	char *sbuf;
X
X	scr_wd0 = scr_wid - 1;
X	scrsiz = scr_ht*(scr_wid+MAXCHAR);
X	if(  scr_ht  > MAXHT || scr_wid > MAXLINE)
X	  {	clean_exit();
X		printf("ELLE: %dx%d screen too big\n",scr_ht,scr_wid);
X		exit(1);
X	  }
X	if((stp = (struct scr_line *) calloc(scr_ht*sizeof(struct scr_line)
X							 + scrsiz*2,1)) == 0)
X	  {	clean_exit();
X		printf("ELLE: not enough memory\n");
X		exit(1);
X	  }
X	sbuf = (char *)stp + scr_ht*sizeof(struct scr_line);
X	for(scrp = &scr[0]; scrp < &scr[scr_ht]; sbuf += scr_wid+MAXCHAR)
X	  {	stp->sl_line = sbuf;
X		stp->sl_nlin = sbuf + scrsiz;
X		*scrp++ = stp++;
X	  }
X}
X
X/* REDISPLAY()
X *	Main function of redisplay routines.  Called every time ELLE
X * forces update of the terminal screen.  "rd_type" contains hints
X * as to what has changed or needs updating, to avoid wasting time
X * on things which don't need attention.
X */
Xredisplay ()
X{	register struct window *w;
X	register i;
X	struct window *make_mode();
X
X	w = cur_win;
X	w->w_redp |= rd_type&RDS_WINFLGS;	/* Set cur_win's flags */
X	rd_type &= ~RDS_WINFLGS;		/* Leave only globals */
X
X	if (rd_type & RD_SCREEN)		/* Clear and refresh? */
X	  {
X		t_clear ();			/* Clear the screen */
X		for(i = scr_ht; --i >= 0;)	/* Clear screen image */
X			scr[i]->sl_col = 0;
X		if(w != ask_win)		/* If not in ask-window */
X		  {	chg_win(ask_win);
X			e_reset();		/* Then flush its contents */
X			chg_win(w);
X		  }
X		redp(RD_WINDS);		/* Update all windows */
X		rd_type &= ~RD_SCREEN;	/* If redisplay is interrupted, */
X					/* don't do it all over again */
X	  }
X	if (rd_type & RD_WINDS)		/* Update all windows? */
X	  {	redp(RD_CHKALL);
X		for (w = win_head; w; w = w -> w_next)	/* For each win */
X			w->w_redp |= RD_WINRES;
X		rd_type &= ~RD_WINDS;
X	  }
X	if (rd_type & RD_CHKALL)	/* Check all windows for changes? */
X	  {	for (w = win_head; w; w = w->w_next)	/* For each win */
X		    if(!(w->w_flags&W_MODE))		/* skip mode wins */
X			if(w->w_redp && upd_wind(w))
X				return;		/* May be interrupted */
X
X	  }
X
X	/* See if ask-window needs updating (to avoid RD_CHKALL in SAY) */
X	if((w = ask_win)->w_redp && upd_wind(w))
X		return;				/* May be interrupted */
X
X	/* Check current window for changes */
X	if((w = cur_win)->w_redp && upd_wind(w))
X		return;				/* May be interrupted */
X
X	/* Now update mode line(s) if necessary */
X	if(rd_type&RD_MODE)
X	  {
X		fupd_wind(w = make_mode(user_win));
X#if FX_2MODEWINDS
X		if (sep_win			/* If 2 windows */
X		  && (sep_win->w_flags&W_MODE)	/* and 2 mode windows */
X		  && (sep_win->w_redp || mode_win->w_redp))	/* Check */
X			fupd_wind(make_mode(oth_win));	/* Must update both */
X#endif
X	  }
X
X	/* Finally, leave cursor in right place. */
X	if(upd_curs(cur_dot)==0)		/* If something screwed up, */
X		errbarf("Cursor out of window");	/* Complain, */
X						/* and leave cursor at bot */
X	rd_type = 0;
X	tbufls();		/* Force out all terminal output */
X}
X
Xfupd_wind(w)		/* Force window update */
Xregister struct window *w;
X{
X	w->w_redp |= RD_WINRES;
X	if(fix_wind(w))
X		upd_wind(w);
X}
X
X/*
X * UPD_CURS
X *	Move screen cursor to position of specified dot within current window.
X *	Returns 0 if dot was not within window (and cursor was not moved),
X *	otherwise returns 1 for success.
X */
Xupd_curs(adot)
Xchroff adot;
X{	register struct scr_line *s;
X	register int y, x;
X	chroff savdot;
X
X	if((y = d_line(adot)) < 0)
X		return(0);	/* Fail, not within window */
X	s = scr[y];		/* Now have line that dot is on */
X
X	/* Get proper offset for any continuation chars from prev line */
X	if(y > cur_win->w_pos)
X	  {	if((x = scr[y-1]->sl_cont) > 0)
X			x--;
X	  }
X	else x = 0;
X
X	savdot = e_dot();
X	e_go(s->sl_boff);
X	if((x = d_ncols((int)(adot - s->sl_boff),x)) < 0)
X	  {	/* If lost, assume it's because we are just after a char
X		** which has its representation continued onto next line.
X		** Move cursor to end of that continuation.
X		** d_line should have ensured that this is safe, but
X		** we double-check just to make sure.
X		*/
X		if((x = s->sl_cont) > 0)	/* Set X to end of cont */
X			--x;
X						/* and on next line down */
X		if(++y >= (cur_win->w_pos + cur_win->w_ht))
X		  {	e_go(savdot);		/* Failed, below window */
X			return(0);
X		  }
X	  }
X	e_go(savdot);
X	t_move(y, x);		/* Move cursor cleverly */
X	return(1);		/* Return success! */
X}
X
X/* Return line # for given dot, -1 if out of current window */
Xd_line(cdot)
Xchroff cdot;
X{	register struct scr_line *s;
X	register struct window *w;
X	register int i;
X	chroff savdot;
X	int bot;
X
X	w = cur_win;
X	i = w->w_pos;
X	bot = i + w->w_ht;
X	for(; i < bot; i++)
X	  {	s = scr[i];
X		if(cdot <= s->sl_boff)
X			goto gotl;
X	  }
X	/* End of window, repeat test specially for last line */
X	savdot = s->sl_boff + (chroff)s->sl_len;
X	if(cdot > savdot)	/* If past last char of last line */
X		return(-1);	/* then clearly outside */
X	--i;			/* Make i match s (bottom line) */
X	if(savdot != cdot)	/* If not exactly at end */
X		return(i);	/* Then we're inside for sure */
X	goto linbet;
X
Xgotl:	if(s->sl_boff != cdot)	/* Are we on line boundary? */
X	  {	if(i <= w->w_pos)	/* No, off top of window? */
X			return(-1);	/* Above top, out for sure */
X		return(--i);
X	  }
X
X	/* Here, dot is exactly on line boundary, have to decide which
X	 * line it really belongs to.
X	 * Get S = pointer to line which cursor is at the end of.
X	 */
X	if(i <= w->w_pos)	/* Quick chk of trivial case, empty buffer */
X		return(i);
X	s = scr[--i];
Xlinbet:
X	if((s->sl_flg&SL_EOL)	/* If line has LF */
X	  || (s->sl_cont > 1))	/* or a continued char */
X		if(++i >= bot)		/* Then cursor is on next line */
X			return(-1);
X	return(i);
X}
X
X/* D_NCOLS - auxiliary for UPD_CURS.  (also called by indtion() in EEFD)
X**	 We are positioned at a place in the current buffer corresponding to
X** the beginning of the screen line, and given:
X**	lcnt - # of chars in buffer to move forward over
X**	ccol - current column position
X** Returns the new column position.  There are some special cases:
X**	Hits EOF: returns normally (new column position)
X**	Hits EOL: returns -1
X**	Position is past end of screen: returns -1
X** The buffer position has changed, but this is irrelevant as upd_curs
X** restores it just after the call.
X*/
Xd_ncols(lcnt, ccol)
Xint lcnt;
Xint ccol;
X{	register int col, i;
X	register SBBUF *sb;
X	int c;
X	char tmp[MAXCHAR*2];	/* MAXCHAR is enough, but *2 just in case */
X
X	col = ccol;
X	sb = (SBBUF *) cur_buf;
X	if((i = lcnt) > 0)
X		do {	if((c = sb_getc(sb)) == EOF)
X				break;
X			/* Check to see if we've run into an EOL */
X#if FX_EOLMODE
X			if(c == CR)
X			  {	if(eolcrlf(sb))
X				  {	if((c = sb_getc(sb)) == LF) /* EOL? */
X					/* Real EOL.  Fail unless point
X					** is between CR and LF, in which case
X					** we return 0 (left margin).
X					*/
X					    return (i==1 ? 0 : -1);
X					/* Stray CR, back up & fall thru */
X					if(c != EOF)
X						sb_backc(sb);
X					c = CR;
X				  }
X			  } else if (c == LF)
X			  {	if(!eolcrlf(sb))	/* Real EOL? */
X					return -1;	/* Yes, fail */
X				/* If EOL mode is CRLF then hitting a LF
X				** can only happen for stray LFs (the
X				** previous check for CR takes care of
X				** CRLFs, and we never start scanning
X				** from the middle of a CRLF.
X				** Drop thru to show stray LF.
X				*/
X			  }
X#else
X			if(c == LF)
X				return(-1);
X#endif /*-FX_EOLMODE*/
X			col += sctr(c, tmp, col);
X		  } while(--i);
X	if(col > scr_wd0)
X		return(-1);
X	return(col);
X}
X
X/* D_LUPD - called from command level to completely redisplay a
X *	specific line on the screen.
X */
Xd_lupd(w, idx)
Xstruct window *w;		/* Window this line belongs to, if known */
Xint idx;
X{	t_curpos(idx, 0);
X	t_docleol();		/* Zap physical screen line */
X	scr[idx]->sl_col = 0;	/* Reflect it on phys screen image */
X	if(w)			/* Mark window for updating */
X		w->w_redp |= RD_WINRES;
X	else redp(RD_WINDS);	/* No window given, assume global */
X	redp(RD_MOVE);		/* Cursor has moved */
X}
X
X/* Clear a window completely the "quickest possible way" */
Xclear_wind(w)
Xregister struct window *w;
X{
X	register int i = w->w_pos;	/* Top line of window */
X	register int bot = i + w->w_ht;	/* Bottom line (plus 1) of window */
X
X	for ( ; i < bot; ++i)
X		d_lupd(w, i);		/* Zap that line */
X}
X
X/* FIX_WIND - Sets up window screen image.  Does not generate any
X *	terminal output, but completely specifies what the new screen
X *	image should look like.
X *	Only the following 4 flags (lumped together in RDS_DOFIX)
X *	provoke fix_wind to do something:
X *		RD_MOVE - cursor has moved, must make sure still within
X *			window, and select new one if not.
X *		RD_TMOD - Text has been changed somewhere.
X *		RD_FIXWIN - Something requested that fix_wind fix things.
X *			Normally this is set when a new w_topldot is set.
X *		RD_WINRES - Window needs to be completely regenerated.
X * Results:
X *	Verifies that the current dot for the window (w_dot) exists.
X * If it is past the end of buffer, it is reset to EOB, and if this is
X * the current window, also updates cur_dot.  Otherwise, w_dot is never
X * adjusted; it is fix_wind's responsibility to make sure that the window
X * displays w_dot.
X *	Verifies that current w_topldot setting will result in cursor
X * (specified by w_dot) appearing within window.  If not, resets w_topldot
X * to an appropriate value (1/3 of way down from top, unless
X * moving up in which case 1/3 of way up from bottom).
X *	Makes sure that sl_boff, sl_len, sl_flg, and sl_cont
X * are set properly for all lines in window.  SL_MOD is set
X * for any lines requiring screen updates; these lines
X * also have sl_nlin and sl_ncol properly set.
X *	Note that sl_line and sl_col are NOT updated or changed, because
X * the physical screen has not been altered!
X *
X *	Returns 0 if no physical screen updates are needed (other than
X *		cursor moving and mode line updating).
X *	Returns 1 if screen updates are needed; RD_UPDWIN is set in w_redp,
X *		indicating that UPD_WIND should be called.
X */
X
Xfix_wind (win)
Xstruct window *win;
X{
X	register struct window *w;
X	register int i;
X	register struct scr_line *s;
X	chroff cdot, bdelta, updot, sdot, newz;
X	chroff savdot;
X	struct buffer *savbuf;
X	int bot, nlmod, savi, contf, ocontf, randomflg;
X	int newpct;
X
X	if(!(w = win))
X		return(0);
X	if(!(w->w_redp&RDS_DOFIX))	/* Anything we need to do? */
X		return(0);		/* Nope, just ignore */
X
X	/* Find current dot for this window, and set up other stuff */
X	cdot = (w == cur_win) ? cur_dot : w->w_dot;
X	bot = w->w_pos + w->w_ht;
X	savbuf = cur_buf;
X	cur_buf = w->w_buf;
X	savdot = e_dot();
X	nlmod = 0;			/* No screen image changes so far */
X
X	/* Dot (ie cursor) is before current top?  If so, must move
X	 * backwards to find a new topldot.  Note also that buffer may have
X	 * changed so that either cdot or topldot points past EOF.
X	 */
X	if(w->w_topldot > cdot)
X	  {	/* Yes, must search backwards scrht/3 screen lines */
X		/* from cdot in order to find topldot. */
X		/* Don't bother updating scr stuff beforehand since we'll
X		 * have to revise everything anyway and can do it on the fly.
X		 */
X		i = (ev_mvpct * w->w_ht) / 100;
X		goto skipdn;
X
X	finddn:	i = ((100 - ev_mvpct) * w->w_ht) / 100;
X	skipdn:	if(i <= 0) i = 1;	/* Ensure # is reasonable */
X		else if(i >= w->w_ht) i = w->w_ht-1;
X		e_go(cdot);		/* Start here (may normalize to EOF)*/
X		d_backup(i ? i : 1);	/* Try to back up cleverly */
X		w->w_topldot = e_dot();
X		randomflg = 0;		/* We have some idea where we are */
X	fixall:		/* Entry point for later recheck, with randomflg==1 */
X		newz = e_blen();
X		if(newz < cdot)		/* Part of buf may have gone away */
X		  {			/* So normalize dot to EOF */
X			w->w_dot = cdot = newz;
X			if(w == cur_win)	/* Special check for fixing */
X				cur_dot = newz;	/* up cur_dot too! */
X			goto finddn;	/* and get a new top-of-window loc */
X		  }
X	retry:	i = w->w_pos;
X		contf = 0;
X		s = 0;
X		for(; i < bot; i++)
X		  {	nlmod++;
X			fix_line(scr[i], s);	/* s = 0 the first time */
X			s = scr[i];
X#if FX_SOWIND
X			if(w->w_flags & W_STANDOUT)
X				s->sl_flg |= SL_NSO;
X			else s->sl_flg &= ~SL_NSO;
X#endif
X		  }
X		if(inwinp(w,cdot))	/* Ensure in window */
X			goto mdone;
X		if(randomflg)		/* If jumped randomly, */
X		  {	i = (ev_nwpct * w->w_ht) / 100;
X			goto skipdn;	/* Try to select new window */
X		  }
X
X		/* We tried to back up and went too far. */
X		if(cdot < w->w_topldot)	/* Verify place is ahead */
X		  {	errbarf("fix_wind failed");	/* Didn't back up?? */
X			goto finddn;
X		  }
X		/* Move down one line and try again */
X		if(w->w_ht > 1)
X			w->w_topldot = scr[w->w_pos+1]->sl_boff;
X		else
X		  {	s = scr[w->w_pos];
X			w->w_topldot = s->sl_boff + s->sl_len;
X		  }
X		e_go(w->w_topldot);
X		goto retry;
X	  }
X
X	/* At some future point, could separate out processing for
X	 * RD_WINRES and RD_FIXWIN.  Latter flag implies only w_topldot
X	 * has changed (new window selected).  Former implies whole
X	 * buffer has been munged, and everything is completely redone.
X	 */
X	if(w->w_redp&(RD_WINRES|RD_FIXWIN))	/* If re-figuring whole window */
X	  {	e_go(w->w_topldot);	/* Start here, and */
X		randomflg = 1;		/* set up flag saying random jump */
X		goto fixall;		/* and go crunch all lines. */
X	  }
X	if((w->w_redp&RD_TMOD)==0)	/* If claims no text mods, */
X	  {	if(inwinp(w,cdot)==0)	/* Just verify cursor loc. */
X			goto finddn;	/* Sigh.... */
X		newz = w->w_oldz;	/* Win, set up for exit. */
X		goto done;
X	  }
X	/* Here only when RD_TMOD is set, indicating changes are
X	 * between range variables.
X	 */
X	/* Find upper bound of any mods.  This is a little gross in the
X	 * speed dept and some faster way should perhaps be devised.
X	 * In particular the main loop should incrementally keep track of
X	 * buffer size, and should set a flag RD_TEXT if anything has
X	 * actually been changed.  Edit routines should have lots of
X	 * flags available to tell main loop more precisely what they did,
X	 * so main loop can take care of updating b/emod and stuff.
X	 */
X	if((newz = e_blen()) == 0)
X		goto finddn;		/* Ensure blank window is cleared */
X	bdelta = newz - w->w_oldz;
X	if((updot = newz) > w->w_emod)
X		updot -= w->w_emod;
X	if(bdelta == 0 && (updot == w->w_bmod))
X		goto inwinq;
X
X	/* Could also check for updot < w_topldot (changes above win)
X	 * or sl_boff+sl_len < w_bmod  (changes below win) but those
X	 * cases are probably pretty rare.
X	 */
X	/* First find line where changes start */
X	for(i = w->w_pos; i < bot; i++)
X	  {	s = scr[i];
X		if(w->w_bmod <= s->sl_boff)	/* Changes prior to this? */
X			break;
X	  }
X	if(i >= bot)			/* Test last line specially */
X	  {	if(w->w_bmod > (s->sl_boff + (chroff)s->sl_len))
X			goto inwinq;	/* Outside window */
X					/* Last line changed, hack it */
X	  }
X	if(i > w->w_pos			/* If we have a prev line */
X	  && (s->sl_len == 0		/* and we're at EOF, */
X	    || w->w_bmod != s->sl_boff	/* or not at start of line */
X	    || scr[i-1]->sl_cont))	/* or prev line is continuation */
X		s = scr[--i];		/* then it's prev line we want */
X
X	/* I has index for screen line changes begin on; S has ptr.
X	 * This piece of code handles case where buffer has been modified
X	 * starting at BMOD, and BDELTA chars have been inserted/deleted;
X	 * range of changes ends at UPDOT.
X	 */
X	savi = i;
X	while(++i < bot)
X		scr[i]->sl_boff += bdelta;
X	i = savi;
X
X	/* Now start with 1st changed line and start figuring new line
X	 * lengths.  Stop when hit end, or past updot and boff is correct
X	 * for start of line.
X	 */
X	/* can improve this by jumping out when past emod, and testing for
X	 * an EOL - then know stuff has to match someplace, so look for that.
X	 * could then simply update lengths or something?
X	 */
X	if(i > w->w_pos)	/* Find # cols already there from prev line*/
X		contf = scr[i-1]->sl_cont;
X	else contf = 0;
X	ocontf = 1;			/* Fake it so always update 1st line*/
X	e_go(sdot = s->sl_boff);
X	for(; i < bot; i++)
X	  {	s = scr[i];
X		if(updot <= sdot	/* If past changed stuff */
X		  && sdot == s->sl_boff	/* and locs are lined up */
X		  && contf == 0		/* and previous line clean */
X		  && ocontf == 0)	/* (both old and new images) */
X			break;		/* Then done. */
X		nlmod++;
X		ocontf = s->sl_cont;	/* Save old-image contf value */
X		fix_line(s, (i > w->w_pos) ? scr[i-1] : 0);
X#if FX_SOWIND
X		if(w->w_flags & W_STANDOUT)
X			s->sl_flg |= SL_NSO;
X		else s->sl_flg &= ~SL_NSO;
X#endif
X		sdot = e_dot();
X		contf = s->sl_cont;	/* Get new-image contf value */
X	  }
X	if(inwinp(w,cdot))	/* OK, screen fixed, see if cursor inside */
X		goto mdone;
X	goto finddn;
X
X	/* Test if still in window and dispatch appropriately */
Xinwinq:	if(inwinp(w,cdot))
X		goto done;
X	else goto finddn;
X
X	/* Come here when done, after mods made to window.
X	 * Calculate new %-of-buffer position for window's view, and
X	 * see if it's changed from current %.
X	 */
Xmdone:	if(w != cur_win) goto done;	/* If not current window, ignore */
X	s = scr[bot-1];
X	if((s->sl_boff + (chroff)s->sl_len) >= newz)
X		if(w->w_topldot) newpct = 150;	/* BOT */
X		else newpct = 200;		/* ALL */
X	else if(w->w_topldot == 0)
X		newpct = -1;			/* TOP */
X	else		/* NOTE: This won't work if topldot is huge */
X		newpct = (w->w_topldot*100)/newz;	/* nn% */
X	if(newpct != w->w_pct)		/* OK, now compare with old % */
X	  {	w->w_pct = newpct;	/* Different, must set and */
X		redp(RD_MODE);		/* invoke redisplay of mode line! */
X	  }
X
Xdone:	w->w_bmod = -1;		/* To indicate vars not set */
X	w->w_oldz = newz;
X	w->w_redp &= ~RDS_DOFIX;	/* Clear flags that invoked us */
X	if(nlmod)
X		w->w_redp |= RD_UPDWIN;	/* Say stuff to be updated */
X	e_go(savdot);
X	cur_buf = savbuf;
X	return(nlmod);
X}
X
X/* INWINP - Returns true if given dot is inside given window.
X */
Xinwinp(win,cdot)
Xstruct window *win;
Xchroff cdot;
X{	register struct scr_line *s;
X	register struct window *w;
X	chroff sdot;
X
X	w = win;
X	if(cdot < w->w_topldot)
X		return(0);
X	s = scr[(w->w_pos + w->w_ht) - 1];
X	sdot = s->sl_boff + (chroff)s->sl_len;
X	if(cdot < sdot)
X		return(1);		/* Yup, inside window. */
X	if(cdot > sdot)
X		return(0);
X
X	/* Dot is exactly at end of window, must check further. */
X	if(s->sl_len		/* If line exists, */
X	 && ((s->sl_flg&SL_EOL)	/* and ends in LF, */
X	    || s->sl_cont > 1))	/* or sl_cont > 1, lose. */
X		return(0);
X	return(1);		/* Else inside, win. */
X}
X
X/*
X * UPD_WIND
X *	If argument 0, assumes cur_win and DOESN'T interrupt if input
X *	detected.
X */
X
Xupd_wind(win)
Xstruct window *win;
X{	register int i, n;
X	register struct scr_line *s;
X	struct window *w;
X	int top, bot, dspf, num, isave, noicost, nodcost, iline, dline;
X#if FX_SOWIND
X	int oldso;
X#endif
X#if IMAGEN
X	int origdspf;
X	char redpmsg[128];
X#endif /*IMAGEN*/
X
X	if((w=win)==0)
X		w = cur_win;
X	dspf = w->w_redp;		/* Get update flags for window */
X#if IMAGEN
X	origdspf = dspf;
X#endif /*IMAGEN*/
X	if(w == cur_win)		/* If updating current window, */
X		dspf |= rd_type;	/* merge in global flags */
X	if((dspf &= RDS_WINFLGS) == 0)	/* Well, it might happen sometimes */
X		goto zdone;
X	w->w_redp = dspf;
X	if(dspf&(RD_WINRES|RD_TMOD|RD_MOVE|RD_FIXWIN))
X	  {	fix_wind(w);		/* May set some flags, so */
X		dspf = w->w_redp;	/* get them back... */
X	  }
X	if((dspf&RD_UPDWIN)==0)		/* Must ask for update! */
X		goto zdone;
X#if IMAGEN
X	if (dbg_redp)
X	  {	sprintf(redpmsg,
X			"buffer: %14s, rd_type: %06o, w_redp: %06o, dspf: %06o",
X			w->w_buf->b_name, rd_type, origdspf, dspf);
X		barf2(redpmsg);
X	  }
X#endif /*IMAGEN*/
X
X	/* Assume screen structure set up by FIX_WIND, just go
X	 * effect change for every line modified.
X	 */
X#if FX_SOWIND
X	oldso = t_dostandout((w->w_flags&W_STANDOUT)? 1:0);
X#endif
X	top = w->w_pos;
X	bot = top + w->w_ht;
X	for(i = top; i < bot; ++i)
X	  if((s = scr[i])->sl_flg&SL_MOD)
X	  {	if(win && tinwait())	/* If OK, stop if any chars typed */
X		  {	tbufls();
X			w->w_redp = dspf;
X#if FX_SOWIND
X			t_dostandout(oldso);
X#endif
X			return(1);	/* Return immediately, say int'd */
X		  }
X		if(slineq(s,s))		/* Compare old with new */
X			goto ldone;	/* Lines equal, no update needed */
X	
X#if IMAGEN
X		/* If hint says redo entirely */
X		if (dspf & RD_REDO)
X		  {	s->sl_flg |= SL_REDO;	 /* Do "fast update" */
X			goto nodel;		/* Just go update line */
X		  }
X#endif /*IMAGEN*/
X		if((trm_flags&TF_IDLIN)==0)
X			goto nodel;		/* Just go update line */
X
X
X		/* Check for I/D line.  If no hints exist, check for both
X		 * insert and delete.
X		 */
X		if((dspf&(RD_ILIN|RD_DLIN))==0)
X			dspf |= RD_ILIN|RD_DLIN;
X		noicost = 0;
X		nodcost = 0;
X
X		/* Check for insert line.  See if the current old screen
X		 * line is duplicated among any of the new lines which
X		 * follow it.  If a match is found, keep looking and add
X		 * up the number of characters in the matching lines.
X		 */
X		if(dspf&RD_ILIN)
X		  {
X			/* See if this old screen line is needed elsewhere */
X			if(s->sl_col == 0)	/* Ignore if blank */
X				goto noins;
X			
X			for(n = i+1; n < bot; n++)
X			  {	if((scr[n]->sl_flg&SL_MOD)==0)
X					break;
X				if(slineq(s, scr[n]))	/* Old, new */
X				  {	if(!noicost) iline = n;	/* 1st time */
X					noicost += s->sl_col;
X					s++;
X				  }
X				else if(noicost) break;
X			  }
X			if(!noicost)		/* If no match, forget it */
X				goto noins;	/* S will not have changed. */
X			s = scr[i];		/* Restore S */
X			n = iline;		/* Have matches, get index
X						 * of first matching line */
X
X			/* Heuristic to decide whether to perform
X			 * insert-line operation.  Kind of stupid, but
X			 * good enough for now.
X			 */
X			num = (n-i)*(tvc_ldn+tvc_lin) + (tvc_li + tvc_ld);
X			if((n-i) >= (scr_ht-(ECHOLINES+3))
X						/* Don't move lines all the
X						 * way down full screen! */
X			  || num >= noicost)	/* Compare cost with estimated
X						 * cost of not doing insert.*/
X				goto noins;
X
X			/* Insert lines! */
X			dspf &= ~RD_ILIN;
X			inslin(i, n - i, w);
X			for(; i < n; i++)	/* Update intervening lines */
X				upd_line (i);
X			goto ldone;
X		  }
Xnoins:
X
X		/* Check for delete line.  See if the new screen line
X		 * is duplicated among any of the old lines already on
X		 * the screen.  If a match is found, keep looking and add
X		 * up the number of characters in the matching lines.
X		 */
X		if(dspf&RD_DLIN)
X		  {
X			/* See if the new line already exists elsewhere */
X			if(s->sl_ncol == 0)	/* Ignore blank lines */
X				goto nodel;
X			for (n = i + 1; n < bot; n++)
X			  {	if((scr[n]->sl_flg&SL_MOD)==0)
X					break;
X				if(slineq(scr[n],s))	/* Old, new */
X				  {	if(!nodcost) dline = n;	/* 1st time */
X 					nodcost += s->sl_ncol;
X					s++;
X				  }
X				else if(nodcost) break;
X			  }
X			if(!nodcost)		/* If no match, forget it */
X				goto nodel;	/* S will not have changed. */
X			s = scr[i];		/* Restore S */
X			n = dline;		/* Index of 1st match */
X
X			/* Heuristic to decide whether to perform
X			 * delete-line operation.  Same hack as for
X			 * insert-line.
X			 */
X			num = (n-i)*(tvc_ldn+tvc_lin) + (tvc_li + tvc_ld);
X			if((n-i) >= (scr_ht-(ECHOLINES+3))
X			  || num >= nodcost)
X				goto nodel;
X
X			/* Delete lines! */
X			dspf &= ~RD_DLIN;
X			dellin(i, n - i, w);
X			goto ldone;
X		  }
Xnodel:
X		/* All failed, so just update line */
X		upd_line(i);
Xldone:		s->sl_flg &= ~SL_MOD;	/* Clear mod flag */
X	  }
Xdone:
X#if FX_SOWIND
X	t_dostandout(oldso);	/* Back to previous mode */
X#endif
Xzdone:	w->w_redp = 0;
X	return(0);		/* Say completed */
X}
X
X
X/*
X * SLINEQ - Compare old, new screen image lines.  If new line doesn't
X *	have the modified flag set, use its old image.
X *	If the standout mode differs, always fails.
X */
X
Xslineq(olds, news)
Xstruct scr_line *olds;
Xstruct scr_line *news;
X{	register char *cpo, *cpn;
X	register int cnt;
X
X	cpo = (char *)news;
X	if(((struct scr_line *)cpo)->sl_flg&SL_MOD)
X	  {	cnt = ((struct scr_line *)cpo)->sl_ncol;
X		cpn = ((struct scr_line *)cpo)->sl_nlin;
X#if FX_SOWIND		/* Mode of old must match mode of new */
X		if(((olds->sl_flg & SL_CSO)==0) !=
X			((((struct scr_line *)cpo)->sl_flg & SL_NSO)==0))
X			return 0;
X#endif
X	  }
X	else
X	  {	cnt = ((struct scr_line *)cpo)->sl_col;
X		cpn = ((struct scr_line *)cpo)->sl_line;
X#if FX_SOWIND		/* Modes of current lines must match */
X		if((olds->sl_flg & SL_CSO) !=
X			(((struct scr_line *)cpo)->sl_flg & SL_CSO))
X			return 0;
X#endif
X	  }
X
X	/* Crufty match stuff */
X	if(cnt != olds->sl_col)
X		return(0);
X	if(cnt)
X	  {	cpo = olds->sl_line;
X		do { if(*cpo++ != *cpn++)
X			return(0);
X		  } while(--cnt);
X	  }
X	return(1);
X}
X
X/* UPD_LINE(lineno) - Effects the update of a physical screen line,
X *	assuming that the screen line structure for that line has been
X *	properly set up by fix_wind.  It cannot be interrupted by typein.
X *	Does a lot of work to check out optimization for char I/D.
X *	Someday it could also check out the possibility of doing a CLEOL at
X *	some point to reduce the number of spaces that need to be output.
X */
X
Xupd_line(y)
Xint y;
X{	register i;
X	register char *sci, *cp;
X	struct scr_line *s;
X
X	int xpos;			/* actual screen position */
X	int c, c2, p2, cmpcost, delcost;
X	int savc, ocol, ncol;
X	char *savcp, *savsci;
X#if FX_SOWIND
X	int oldso, newso;
X	int writall = 0;
X#endif
X
X	s = scr[y];
X	savsci = sci = s->sl_line;	/* What is currently on the screen */
X#if IMAGEN
X	if (s->sl_flg & SL_REDO)
X	  {	/* Check for line-redo flag */
X		s->sl_flg &= ~SL_REDO;	/* Clear it: we are handling it */
X		writall = 1;	/* Re-do this line completely */
X		t_move(y, 0);
X		t_docleol();
X		s->sl_col = 0;
X	  }
X#endif /*IMAGEN*/
X
X#if FX_SOWIND
X	/* See whether modes of the lines are the same or not. */
X	newso = (s->sl_flg & SL_NSO)!=0;	/* Get new mode (true if SO)*/
X	if(((s->sl_flg & SL_CSO)!=0) !=	newso)
X	  {	t_move(y, 0);		/* Not same, must zap existing line */
X		t_docleol();
X		s->sl_col = 0;
X		writall = newso;	/* Output all if SO is new mode */
X	  }
X	oldso = t_dostandout(newso);	/* Get in right mode */
X#endif
X
X	ocol = s->sl_col;
X	savcp = cp = s->sl_nlin;
X	ncol = s->sl_ncol;
X
X	/* Find leading equalness */
X	i = ocol;
X	if(i > ncol) i = ncol;		/* Use minimum count */
X	if(i)
X	  {	do { if(*cp++ != *sci++)
X			  {	--cp;
X				break;
X			  }
X		  } while(--i);
X		i = cp - savcp;
X		sci = savsci;		/* Restore ptr to beg of cur line */
X	  }
X
X	/* From here on, "i" is now the x-coordinate (column addr)
X	 * of the first position that doesn't match.  "cp" points to
X	 * the first nonmatching char in the new line image.
X	 */
X#if COHERENT		/* Has direct video interface capability */
X	if(trm_flags&TF_DIRVID)
X	  {	if(ncol < ocol)
X		  {	/* Flesh out new line to completely replace old */
X			fillsp(&s->sl_nlin[ncol], ocol-ncol);
X			ncol = ocol;
X		  }
X		/* Spit out changed stuff.  t_direct will handle the
X		 * case where i == ncol (ie no changes needed).
X		 */
X		t_direct(y,i,cp,ncol-i);
X		goto done;
X	  }
X#endif /*COHERENT*/
X
X	if(i == ncol)			/* Matched up to end of new line? */
X		goto idone;		/* Yes, can skip big loop! */
X
X#if FX_SOWIND
X	if(writall)			/* If simply writing everything...*/
X	  {	t_move(y, 0);
X		tputn(cp, ncol);	/* Output them all */
X		curs_col = ncol;	/* Update cursor position */
X		goto idone;		/* then wrap up! */
X	  }
X#endif
X
X	/* Now must fill out remainder of old line with blanks. */
X	if(ocol < scr_wid)
X	  {
X#if FX_SOWIND
X		if(newso) fillset(&sci[ocol], scr_wid-ocol, 0);
X		else
X#endif
X		fillsp(&sci[ocol],scr_wid-ocol);	/* Fill out */
X	  }
X
X	/******  Main update loop. ******/
X	for (; i < ncol; i++)
X	  {	c = *cp++;		/* Note *CP will point to next */
X		if(c == sci[i])
X			continue;
X		if(i >= ocol)		/* Past EOL of old line? */
X		  {
Xputin:			sci[i] = c;
X			if(y != curs_lin || i != curs_col)
X				t_move(y, i);
X			tput(c);
X			curs_col++;
X			continue;
X		  }
X
X		if((trm_flags&TF_IDCHR)==0)	/* Replace */
X			goto putin;
X
X		/* Do checking to see whether char I/D operations should
X		 * be invoked.  This code is quite CPU intensive and
X		 * can cause noticeable pauses if run on a slow CPU with
X		 * a fast (9600) terminal line.  The optimization tradeoff
X		 * seems worthwhile most of the time, however.
X		 */
X		cmpcost = 0;		/* Default is don't compare */
X		if(ncol == ocol)	/* If line lengths same, must chk */
X		  {
X/*			if(ncol >= scr_wid) */	/* If line overrun, compare */
X				cmpcost++;
X		  }
X#if 0
XIf ncol == ocol, have problem with tabs:
X	If don''t use I/D char, but tabs exist, lots of wasteful update.
X	If DO use I/D char, and no tabs exist, potential for mistakenly
X		using I/D when didn''t have to.  Not too bad, though?
X	If DO use I/D char, then mild screw when inserting/deleting
X		just before a tab, since could have just overwritten,
X		but I/D insists on jerking things around.
X	Insert test:
X		If old char was space, replace? Problem: will cause cursor
X		jump if really should have shifted a long run of spaces.
X		But that is probably okay.
X	Delete test:
X		If new char is space, replace? again, will cause cursor jump
X		with long run of spaces.
X#endif /*COMMENT*/
X
X		if(ncol < ocol || cmpcost)	/* Try delete-char */
X		  {
X			/* Search old for match of c and nextc */
Xdodel:			savc = c;
X			if(i >= ncol-1)
X				goto putin;
X			c2 = *cp;
X			if(c == SP && ncol == ocol)
X				goto tryins;
X			p2 = i;
X			for(;;)
X			  {	if(c == sci[i] && c2 == sci[i+1])
X					break;
X				if(++i < ocol)
X					continue;
X				i = p2;
X				if(cmpcost) {cmpcost = 0; goto tryins;}
X				goto putin;
X			  }
X			/* Find # chars that match (i.e. will be saved) */
X			for(c=1; (i+c < ncol) && (sci[i+c] == cp[c-1]); c++);
X			delcost = tvc_cd + tvc_cdn*(i - p2);
X			if(delcost >= c)
X			  {	c = savc;
X				i = p2;
X				if(cmpcost) { cmpcost = 0; goto tryins;}
X				goto putin;	/* Punt */
X			  }
X			if(cmpcost)
X			  {	c = savc; i = p2;
X				goto tryins;
X			  }
X			t_move(y, p2);
X			c = i - p2;	/* Find # chars to flush */
X			strncpy(&sci[p2],&sci[i], ocol-i);
X			ocol -= c;
X			fillsp(&sci[ocol], c);
X			i = p2;			/* Restore i */
X			t_delchr(c);		/* Flush this many cols */
X			continue;
X		  }
X
X		/* Try ins-char */
X		/* Search new for match of i and i+1 */
X		/* Note this cannot be used while in standout mode, since
X		** the new spaces created will probably be in the wrong mode.
X		*/
Xtryins:
X#if FX_SOWIND
X		if(newso) goto putin;
X#endif
X		if(i+1 >= ocol)
X			goto putin;
X
X		savc = c;
X		savcp = cp;
X		c2 = sci[i+1];
X		if(sci[i] == SP && ncol == ocol)
X			goto putin;
X		xpos = i;		/* save current col */
X		i++;
X		for(;;)
X		  {	if(i >= ncol) goto puntx;
X			c = *cp++;
Xinlp2:			if(c != sci[xpos])
X			  {	if(i > scr_wid) goto puntx;
X				i++;
X				continue;
X			  }
X			if(i >= ncol) goto puntx;
X			c = *cp++;
X			if(c != c2)
X			  {	i++;		/* Allow for previous c */
X				goto inlp2;	/* which is always 1 */
X			  }
X			break;
X		  }
X		if(i >= scr_wid) goto puntx;
X
X		/* Find how many chars match (i.e. will be saved) */
X		for(c = 2; xpos+c < ncol && sci[xpos+c] == *cp++; c++);
X		if((p2 = tvc_ci + tvc_cin*(i - xpos)) >= c)
X			goto puntx;	/* Not worth it... */
X		if(cmpcost && p2 >= delcost)
X			goto puntx;	/* Do delchr instead */
X
X		/* We've decided to insert some chars! */
X		i -= xpos;		/* Get # char positions to insert */
X		cp = savcp;		/* Get ptr to newline string */
X		--cp;			/* Point at 1st char to insert */
X					/* Make room in scr array */
X		inspc(&sci[xpos],
X			&sci[(ocol+i >= scr_wid) ? scr_wid-i : ocol], i);
X		ocol += i;		/* Update size of old line */
X		strncpy(&sci[xpos], cp, i);	/* Copy all inserted chars */
X
X		t_move(y, xpos);	/* Now ensure in right place */
X		t_inschr(i, cp);	/* and insert string onto screen! */
X
X		cp += i;		/* Update source ptr */
X		cp++;			/* Point to next char */
X		i += xpos;
X		continue;		/* Now continue loop! */
X
X	puntx:	i = xpos;
X		c = savc;
X		cp = savcp;
X		if(cmpcost) { cmpcost = 0; goto dodel;}
X		goto putin;
X	  }
X
X	/* All done putting up new stuff.  Now see if any remaining old
X	** stuff needs to be cleared from end of line.
X	*/
Xidone:	if(i < ocol)		/* if still have text to right, */
X	  {	t_move(y,i);	/* move there */
X		t_docleol();	/* and clear old stuff. */
X	  }
X
Xdone:	s->sl_line = s->sl_nlin;	/* Replace old image by new */
X	s->sl_col = s->sl_ncol;
X	s->sl_nlin = sci;
X	s->sl_flg &= ~SL_MOD;
X#if FX_SOWIND			/* Copy standout mode to current */
X	if(newso) s->sl_flg |= SL_CSO;
X	else s->sl_flg &= ~SL_CSO;
X#endif
X}
X
X#if FX_SOWIND
Xfillset(str,cnt,c)
Xchar *str;
Xint cnt;
Xint c;
X{	register int n;
X	register char *cp;
X	if((n = cnt) <= 0) return;
X	cp = str;
X	do{ *cp++ = c;
X	  } while(--n);
X}
X#endif
X
Xfillsp(str,cnt)
Xchar *str;
Xint cnt;
X{	register int n;
X	register char *cp;
X	if((n = cnt) <= 0) return;
X	cp = str;
X	do{ *cp++ = SP;
X	  } while(--n);
X}
Xinspc(cp0, cpl, cnt)
Xchar *cp0, *cpl;
Xint cnt;
X{	register char *cp, *cp2;
X	register n;
X	if((n = cnt) <= 0) return;
X	cp = cpl;		/* ptr to last+1 char in string */
X	cp2 = cp+n;		/* ptr to loc+1 to move to */
X	n = cp - cp0;		/* # chars to move */
X	do *--cp2 = *--cp;
X	while(--n);
X	n = cnt;		/* Now fill gap with spaces */
X	do *cp++ = SP;
X	while(--n);
X}
X
X/* FIX_LINE - Fixes up new screen image for a single line.  Does not
X *	do any actual terminal I/O, and does not change the old screen
X *	image.  Assumes that previous line (if any is furnished) has
X *	already been properly set up.
X */
X
Xint sctreol = 0;	/* Ugly crock for talking to sctrin() */
X			/* 0 = no EOL seen, 1 = EOL seen, -1 = EOF seen */
Xfix_line(slp, olds)
Xstruct scr_line *slp;
Xstruct scr_line *olds;
X{	register struct scr_line *s;
X	register int col, scrw;
X	char *cp;
X	int ch;
X
X	col = 0;
X	scrw = scr_wid;
X	cp = slp->sl_nlin;
X	if((s = olds) && (col = s->sl_cont))
X	  {	if(--col)
X			strncpy(cp, (s->sl_flg&SL_MOD) ?
X					&s->sl_nlin[scrw]
X					 : &s->sl_line[scrw], col);
X		cp += col;
X	  }
X	scrw--;			/* Note now using scr_wd0 !! */
X	s = slp;
X	s->sl_boff = e_dot();
X	col = sctrin(cp, scrw, col);
X	if (col < scrw || sctreol)	/* Does line need continuation mark? */
X		s->sl_cont = 0;		/* No, say no cont chars */
X	else {
X		/* Yes, find # cols of overflow.  If not 0, must be > 0 */
X		/* and char is a biggie.  Make room for continuation chars */
X		if(col -= scrw)
X			inspc(&s->sl_nlin[scrw],&s->sl_nlin[scrw+col], 1);
X		s->sl_cont = col+1;		/* # cont chars, plus 1 */
X		s->sl_nlin[scrw] = CI_CLINE;	/* Display "contin" mark */
X		col = scrw+1;
X	  }
X
X	s->sl_ncol = col;
X	s->sl_len = e_dot() - s->sl_boff;
X	s->sl_flg |= (SL_MOD|SL_EOL);	/* Say new, and assume line has EOL */
X	if(sctreol <= 0)		/* unless it doesn't really */
X		s->sl_flg &= ~SL_EOL;	/* in which case turn off flag */
X	return;
X}
X
X/* SCTRIN - auxiliary for FIX_LINE.
X *	lim - # cols chars are allowed to use
X *	ccol - current column (0 = bol)
X * Returns when see EOL or EOF, or
X *	when all columns have been filled up.  Retval-ccol = # overflow.
X *	Note that any overflow is indivisible (i.e. a char with a
X *	multi-col representation is responsible for the overflow).
X *	So, overflow = 0 means next char would be in 1st non-ex column
X *	and overflow > 0 means last char read has extra columns, but
X *	it did start within bounds.
X */
Xsctrin(to, lim, ccol)
Xchar *to;
Xint lim;
Xint ccol;
X{	register SBBUF *sb;
X	register col, cnt;
X
X	sb = (SBBUF *) cur_buf;
X	col = ccol;
X	sctreol = 0;		/* No EOL or EOF seen */
X	do
X	  {	cnt = sb_getc(sb);
X		if(cnt == EOF)
X		  {	--sctreol;	/* Say EOF seen! */
X			return(col);
X		  }
X#if FX_EOLMODE
X		if(cnt == CR)		/* Possible EOL? */
X		  {	if(eolcrlf(sb))
X			  {	if((cnt = sb_getc(sb)) == LF)	/* Real EOL? */
X				  {	sctreol++;
X					return col;	/* Yes, return */
X				  }
X				/* Stray CR, back up & fall thru */
X				if(cnt != EOF)
X					sb_backc(sb);
X				cnt = CR;	/* Show stray CR */
X			  }
X		  } else if (cnt == LF)
X		  {	if(!eolcrlf(sb))	/* Real EOL? */
X			  {	sctreol++;
X				return col;	/* Yes, return */
X			  }
X			/* If EOL mode is CRLF then hitting a LF
X			** can only happen for stray LFs (the
X			** previous check for CR takes care of
X			** CRLFs, and we never start scanning
X			** from the middle of a CRLF.
X			** Drop thru to show stray LF.
X			*/
X		  }
X#else
X		if(cnt == LF)
X		  {	sctreol++;	/* Say EOL seen */
X			return col;
X		  }
X#endif /*_FX_EOLMODE*/
X		cnt = sctr(cnt, to, col);
X		to += cnt;
X		col += cnt;
X	  } while(col < lim);
X
X	/* If we're stopping because last char put us precisely at the
X	** end of the line, make a further check to see whether an EOL
X	** is next.  If so, we can include that in the line since it
X	** doesn't need any more columns for representation!
X	*/
X	if (col == lim)		/* If stopping exactly at edge of screen */
X	    switch (sb_getc(sb))	/* Check out next char */
X	      {	case EOF:
X			--sctreol;		/* Yes, note EOF seen */
X			break;			/* and can return immed */
X#if FX_EOLMODE
X		case CR:		/* Possible EOL? */
X			if(eolcrlf(sb))
X			  {	if((cnt = sb_getc(sb)) == LF)	/* Real EOL? */
X				  {	sctreol++;	/* Yes, set flag */
X					break;		/* and return */
X				  }
X				/* Stray CR, back up & fall thru */
X				if(cnt != EOF)		/* Back up char that */
X					sb_backc(sb);	/* came after the CR */
X				sb_rgetc(sb);		/* Then back over CR */
X				break;
X			  }
X			sb_backc(sb);
X			break;
X		case LF:
X			if(!eolcrlf(sb))	/* Real EOL? */
X			  {	sctreol++;	/* Yes, set flag */
X				break;		/* and return */
X			  }
X			/* If EOL mode is CRLF then hitting a LF
X			** can only happen for stray LFs (the
X			** previous check for CR takes care of
X			** CRLFs, and we never start scanning
X			** from the middle of a CRLF.
X			** Drop thru into default to back up over LF.
X			*/
X#else
X		case LF:
X			sctreol++;	/* Say EOL seen */
X			break;		/* and return */
X#endif /*-FX_EOLMODE*/
X		default:
X			sb_backc(sb);		/* Back up over random char */
X			break;
X	    }
X	return(col);
X}
X
X/* SCTR - Screen Char TRanslation routine.
X**	This routine is completely responsible for the way a buffer char is
X** displayed on the screen.  Given a char and the current column position,
X** it stores the representation using the given pointer and returns
X** the number of chars (columns) used by the representation.
X**	Normal printing chars (plus space) are simply themselves.
X**	TAB is a variable number of spaces depending on the column pos.
X**		(we use standard tabstops of 8)
X**	All control chars are uparrow followed by a printing char.
X**		e.g. ctrl-A = ^A
X**		This includes ESC which is ^[.
X**		DEL is shown as ^?.
X**	Chars with the 8th bit set have the prefix CI_META (currently ~) and
X**		the rest of the representation is as above (except for TAB).
X**	Chars with the 9th bit set have the prefix CI_TOP (currently |) and
X**		the rest of the representation is as above (except for TAB).
X**		This only exists for systems with 9-bit chars such as TOPS-20.
X*/
X
Xstatic int
Xsctr(ch, to, ccol)
Xint ch;			/* Buffer char to translate */
Xchar *to;		/* Place to deposit translation in */
Xint ccol;		/* Current column position */
X{	register char *cp;
X	register c, n;
X
X	c = ch;
X	if(037 < c && c < 0177)	/* Most common case */
X	  {	*to = c;
X		return(1);
X	  }
X	cp = to;
X	if(c == TAB)			/* Next most common case */
X	  {	n = 010 - (ccol&07);	/* Tab stops are every 8 cols */
X		ccol = n;		/* Save value */
X		do *cp++ = SP;
X		while (--n);
X		return(ccol);
X	  }
X	ccol = 1;			/* Re-use var */
X#if TOPS20
X	if(c&0400)			/* 9th bit set? */
X	  {	*cp++ = CI_TOP;
X		ccol++;
X	  }
X#endif /*TOPS20*/
X	if(c&0200)
X	  {	*cp++ = CI_META;
X		ccol++;
X	  }
X	if((c &= 0177) <= 037 || c == 0177)
X	  {	*cp++ = CI_CNTRL;
X		c ^= 0100;		/* Transform cntrl char */
X		ccol++;
X	  }
X	*cp = c;
X	return(ccol);
X}
X
X/* INSLIN(line, N, wind) - Insert lines
X * DELLIN(line, N, wind) - Delete lines
X *	Both routines insert/delete N lines at "line" in window "wind"
X *	and update the screen image accordingly.
X */
X
Xinslin (line, n, win)
Xint   line;			       /* line number to insert BEFORE */
Xint   n;			       /* number of lines to insert */
Xstruct window *win;		       /* window we are in */
X{	register int  i;
X	register int bot;
X	register char **savp;
X	char *savscr[MAXHT];
X
X	bot = win -> w_ht + win -> w_pos;
X	t_curpos (line, 0);
X	t_inslin (n, bot);		/* do the insertion on the screen */
X	savp = &savscr[0];
X	for (i = 1; i <= n; i++)	/* free lines that fall off-screen */
X		*savp++ = scr[bot - i]->sl_line;
X
X	for (i = bot - 1; i >= line + n; i--)		/* move down lines */
X	  {	scr[i]->sl_line = scr[i - n]->sl_line;	/* below the insertion */
X		scr[i]->sl_col = scr[i - n]->sl_col;
X	  }
X	savp = &savscr[0];
X	for (i = line + n - 1; i >= line; i--)
X				       /* blank lines where inserted */
X	  {	scr[i]->sl_line = *savp++;
X		scr[i]->sl_col = 0;
X	  }
X	for(i = line; i < bot; ++i)
X		scr[i]->sl_flg |= SL_MOD;
X}
X
Xdellin (line, n, win)
Xint   line;			       /* first line to be deleted */
Xint   n;			       /* number of lines to be deleted */
Xstruct window *win;		       /* window we are in */
X{	register int  i;
X	register int bot;
X	register char **savp;
X	char *savscr[MAXHT];
X
X	bot = win -> w_ht + win -> w_pos;
X
X	t_curpos (line, 0);
X	t_dellin (n, bot);	       /* do the deletion on the screen */
X	savp = &savscr[0];
X	for (i = line; i < line + n; i++)    /* free the deleted lines */
X		*savp++ = scr[i]->sl_line;
X	for (i = line; i < bot - n; i++)	/* move lines up to fill */
X	  {	scr[i]->sl_line = scr[i + n]->sl_line;	/* deleted spaces */
X		scr[i]->sl_col = scr[i + n]->sl_col;
X	  }
X
X	savp = &savscr[0];
X	for (i = bot - n; i < bot; i++)      /* blank lines at bottom */
X	  {	scr[i]->sl_line = *savp++;
X		scr[i]->sl_col = 0;
X	  }
X	for(i = line; i < bot; ++i)
X		scr[i]->sl_flg |= SL_MOD;
X}
X
X/* T_ Terminal functions - these are similar to the terminal-dependent
X *	routines in EETERM (which they call) but rely on some knowledge of
X *	the screen image in order to do their job cleverly.
X */
X
X#if FX_SOWIND
X
X/* T_DOSTANDOUT(on) - Turn standout mode on or off, cleverly.
X**	Returns previous state.
X*/
Xstatic int curso = 0;		/* Current state (initially off) */
Xint
Xt_dostandout(on)
Xint on;
X{
X	int oldso;
X
X	if ((oldso = curso) != on)	/* If desired state doesn't match, */
X	  {	t_standout(on);		/* invoke new state. */
X		curso = on;
X	  }
X	return oldso;
X}
X#endif
X
X
Xt_move(y,x)
Xregister int y,x;
X{	register int d;
X
X	if(y != curs_lin)		/* No vertical smarts yet */
X	  {	t_curpos(y, x);
X		return;
X	  }
X	if((d = (x - curs_col)) >= 0)	/* Find diff in column position */
X	  {	if(d == 0) return;	/* If none, nothing to do! */
X
X		/* Moving right.  If distance is less than abs-move cost,
X		 * do clever right-move by copying screen image */
X		if(d < tvc_pos)
X#if FX_SOWIND	/* Ensure not in standout mode */
X			if((scr[y]->sl_flg&(SL_CSO|SL_NSO))==0)
X#endif
X		  {
X			tputn(&scr[y]->sl_line[curs_col], d);
X			curs_col = x;
X			return;
X		  }
X	  }
X	/* Moving to left, try to do clever left-move by backspacing
X	 * instead of using abs move.
X	 */
X	else if((d = -d)*tvc_bs < tvc_pos)
X	  {	do { t_backspace();
X		  } while(--d);
X		return;
X	  }
X	/* No luck with cleverness, just move. */
X	t_curpos(y, x);
X}
X
Xt_docleol()
X{	register struct scr_line *s;
X	register int cnt, ocol;
X
X	if(trm_flags&TF_CLEOL) t_cleol();	/* Winning */
X	else		/* Losing */
X	  {	s = scr[curs_lin];
X		if((cnt = s->sl_col - curs_col) > 0)
X		  {
X#if FX_SOWIND
X			int oldso = t_dostandout(0);
X#endif
X			ocol = curs_col;
X			do { tput(SP); curs_col++;
X			  } while(--cnt);
X#if FX_SOWIND
X			t_dostandout(oldso);
X#endif
X			t_move(curs_lin, ocol);
X		  }
X	  }
X}
X
/
echo x - eeedit.c
sed '/^X/s///' > eeedit.c << '/'
X/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*	EEEDIT - E-type routines */
X
X#include "elle.h"
X
X/* E_	- Operate on cur_buf.  Do not change value of cur_dot unless
X *		unavoidable side effect (also e_setcur).
X * EX_	- Like E_ but take SB ptr value.  Never touch cur_dot.
X * ED_	- Like E_, operate on cur_buf, update cur_dot and display stuff.
X * D_	- Perform necessary display update for given operations.
X *
X * Note that "dot" refers to the current read/write pointer for a sbbuffer.
X * The name comes from EMACS/TECO where "." represents this value.
X */
X
X#define CURSBB (SBBUF *)cur_buf		/* Shorthand for current SB buffer */
X
Xe_reset()	/* Reset current buffer */
X{	ex_reset(CURSBB);
X	cur_dot = 0;
X}
X
X/* Basic functions - apply SB routines to current buffer.
X * There is some optimization here which knows that certain SB functions
X * are macros.
X */
Xe_rgetc()	/* Read/move 1 char backward */
X{	return(sb_rgetc((CURSBB)));
X}
Xe_rdelc()	/* Delete 1 char backward */
X{	return(sb_rdelc((CURSBB)));
X}
Xe_delc()	/* Delete 1 char forward */
X{	return(sb_deln(CURSBB,(chroff)1));
X}
Xe_getc()	/* Read/move 1 char forward */
X{	register SBBUF *sb;
X	sb = CURSBB;		/* Macro: use reg */
X	return(sb_getc(sb));
X}
Xe_backc()	/* Move 1 char backward */
X{	register SBBUF *sb;
X	sb = CURSBB;		/* Macro: use reg */
X	sb_backc(sb);			/* No value returned */
X}
Xe_putc(c)	/* Insert/write 1 char forward */
Xchar c;
X{	register SBBUF *sb;
X	sb = CURSBB;		/* Macro: use reg */
X	return(sb_putc(sb, c));
X}
Xe_peekc()	/* Read 1 char forward (no move) */
X{	register SBBUF *sb;
X	sb = CURSBB;		/* Macro: use reg */
X	return(sb_peekc(sb));
X}
Xe_ovwc(ch)	/* Overwrite 1 char forward */
Xchar ch;
X{
X	sb_setovw(CURSBB);	/* Turn on overwrite mode */
X	e_putc(ch);
X	sb_clrovw(CURSBB);	/* Turn off overwrite mode */
X}
X
XSBSTR *
Xe_copyn(off)	/* Copy N chars forward/backward, return SD to sbstring */
Xchroff off;
X{	return(sb_cpyn(CURSBB,off));
X}
Xe_deln(off)	/* Delete N chars forward/backward */
Xchroff off;
X{	return(sb_deln(CURSBB, off));
X}
X
X/* E_SETCUR() - set cur_dot to current position (dot).  This is the only
X *	E_ routine that mungs cur_dot except for e_reset.
X */
Xe_setcur()
X{	cur_dot = e_dot();
X}
Xe_gosetcur(dot)		/* Go to specified dot and set cur_dot as well */
Xchroff dot;
X{	sb_seek(CURSBB,dot,0);
X	e_setcur();	/* Not cur_dot = dot since want canonicalization */
X}
X
X/* E_GO(dot) - Move to specified location. */
X/* These "GO" routines all move to the location specified, returning
X *	0 if successful and -1 on error.  "cur_dot" is never changed,
X *	with the exception of e_gosetcur.
X * Note that other "GO" routines (eg E_GONL) will return 1 if successful
X *	and 0 if stopped by EOF.
X */
X
Xe_gocur() { return(e_go(cur_dot)); }		/* Move to cur_dot */
Xe_gobob() { return(e_go((chroff) 0)); }		/* Move to Beg Of Buffer */
Xe_goeob() { return(sb_seek(CURSBB,(chroff)0,2)); } /* Move to End Of Buffer */
Xe_go(dot)	/* Move to specified location. */
Xchroff dot;
X{	return(sb_seek(CURSBB,dot,0));
X}
Xe_igoff(ioff)	/* Move (int) N chars forward/backward */
Xint ioff;
X{	return(sb_seek(CURSBB,(chroff)ioff,1));
X}
X
Xe_goff(off)	/* Move (full) N chars forward/backward */
Xchroff off;
X{	return(sb_seek(CURSBB,off,1));
X}
X
Xint ex_gonl(), ex_gopl(), ex_gobol(), ex_goeol();
X
Xe_gobol() { return(ex_gobol(CURSBB)); }	/* Move to beg of this line */
Xe_goeol() { return(ex_goeol(CURSBB)); }	/* Move to end of this line */
Xe_gonl()  { return(ex_gonl(CURSBB)); }	/* Move to beg of next line */
Xe_gopl()  { return(ex_gopl(CURSBB)); }	/* Move to beg of prev line */
X
X
X/* E_DOT() - Return current value of dot. */
Xchroff e_dot()   { return(sb_tell(CURSBB)); }		/* Current pos */
Xchroff e_nldot() { return(e_alldot(CURSBB,ex_gonl)); }	/* Beg of next line */
Xchroff e_pldot() { return(e_alldot(CURSBB,ex_gopl)); }	/* Beg of prev line */
Xchroff e_boldot(){ return(e_alldot(CURSBB,ex_gobol));}	/* Beg of this line */
Xchroff e_eoldot(){ return(e_alldot(CURSBB,ex_goeol));}	/* End of this line */
X
Xchroff
Xe_alldot(sbp,rtn)	/* Auxiliary for above stuff */
XSBBUF *sbp;
Xint (*rtn)();
X{	return(ex_alldot(sbp,rtn,e_dot()));
X}
X
X/* E_BLEN - Return length of current buffer */
Xchroff
Xe_blen() { return(ex_blen(CURSBB)); }
X
X/* EX_ routines - similar to E_ but take a buffer/sbbuf argument
X *	instead of assuming current buffer.
X */
X
X/* EX_RESET - Reset a given buffer */
Xex_reset(b)
Xstruct buffer *b;
X{	sbs_del(sb_close((SBBUF *)b));
X	sb_open((SBBUF *)b,(SBSTR *)0);
X}
X
Xex_go(sbp,loc)		/* Move to given dot in specified sbbuf */
Xchroff loc;
XSBBUF *sbp;
X{	return(sb_seek(sbp,loc,0));
X}
X
Xchroff
Xex_dot(sbp)	/* Return current position in specified sbbuf */
XSBBUF *sbp;
X{
X	return(sb_tell(sbp));
X}
X
X
Xchroff
Xex_boldot(sbp,dot)	/* Return dot for BOL of specified sbbuf */
XSBBUF *sbp;
Xchroff dot;
X{	return(ex_alldot(sbp,ex_gobol,dot));
X}
X
Xchroff
Xex_alldot(sbp,rtn,dot)	/* Auxiliary for some e_ stuff */
XSBBUF *sbp;
Xint (*rtn)();
Xchroff dot;
X{	register SBBUF *sb;
X	chroff savloc, retloc;
X
X	savloc = sb_tell(sb = sbp);
X	sb_seek(sb,dot,0);
X	(*rtn)(sb);
X	retloc = sb_tell(sb);
X	sb_seek(sb,savloc,0);
X	return(retloc);
X}
X
X/* GO (forward) to Next Line of specified sbbuf - returns 0 if stopped at EOF
X * before an EOL is seen. */
Xex_gonl(sbp)
XSBBUF *sbp;
X{	register SBBUF *sb;
X	register int c;
X	sb = sbp;
X#if FX_EOLMODE
X	if(eolcrlf(sb))
X		while((c = sb_getc(sb)) != EOF)
X		  {	if(c == LF)		/* Possible EOL? */
X			  {	sb_backc(sb);	/* See if prev char was CR */
X				if((c = sb_rgetc(sb)) != EOF)
X					sb_getc(sb);
X				sb_getc(sb);	/* Must restore position */
X				if(c == CR)	/* Now test for CR */
X					return(1);	/* Won, CR-LF! */
X			  }
X		  }
X	else
X#endif
X	while((c = sb_getc(sb)) != EOF)
X		if(c == LF)
X			return(1);
X	return(0);
X}
X
X/* GO (forward) to End Of Line of specified sbbuf - returns 0 if stopped at
X * EOF before an EOL is seen. */
Xex_goeol(sbp)
XSBBUF *sbp;
X{	register SBBUF *sb;
X	register int c;
X	sb = sbp;
X#if FX_EOLMODE
X	if(eolcrlf(sb))
X		while((c = sb_getc(sb)) != EOF)
X		  {	if(c == LF)		/* Possible EOL? */
X			  {	sb_backc(sb);	/* See if prev char was CR */
X				if((c = sb_rgetc(sb)) == CR)
X					return(1);	/* Won, CR-LF! */
X				if(c != EOF)	/* No, must restore position */
X					sb_getc(sb);	/* Skip over */
X				sb_getc(sb);		/* Skip over LF */
X			  }
X		  }
X	else
X#endif
X	while((c = sb_getc(sb)) != EOF)
X		if(c == LF)
X		  {	sb_backc(sb);
X			return(1);
X		  }
X	return(0);
X}
X
X/* GO (backward) to Beg Of Line of specified sbbuf - returns 0 if stopped
X * at EOF
X */
Xex_gobol(sbp)
XSBBUF *sbp;
X{	register SBBUF *sb;
X	register int c;
X	sb = sbp;
X#if FX_EOLMODE
X	if(eolcrlf(sb))
X		while((c = sb_rgetc(sb)) != EOF)
X		  {	if(c == LF)		/* Possible EOL? */
X			  {	if((c = sb_rgetc(sb)) == CR)
X				  {	sb_getc(sb);	/* Won, CR-LF! */
X					sb_getc(sb);	/* Move back */
X					return(1);
X				  }
X				if(c != EOF)	/* No, must restore position */
X					sb_getc(sb);	/* Undo the rgetc */
X			  }
X		  }
X	else
X#endif
X	while((c = sb_rgetc(sb)) != EOF)
X		if(c == LF)
X		  {	sb_getc(sb);
X			return(1);
X		  }
X	return(0);
X}
X
X/* GO (backward) to Previous Line of specified sbbuf - returns 0 if stopped
X * at EOF before an EOL is seen (i.e. if already on 1st line of buffer)
X */
Xex_gopl(sbp)
XSBBUF *sbp;
X{	register SBBUF *sb;
X	register int c;
X	sb = sbp;
X#if FX_EOLMODE
X	if(eolcrlf(sb))
X		while((c = sb_rgetc(sb)) != EOF)
X		  {	if(c == LF)		/* Possible EOL? */
X			  {	if((c = sb_rgetc(sb)) == CR)
X				  {	ex_gobol(sb);
X					return(1);		/* Won! */
X				  }
X				if(c != EOF)	/* No, must restore position */
X					sb_getc(sb);	/* Undo the rgetc */
X			  }
X		  }
X	else
X#endif
X	while((c = sb_rgetc(sb)) != EOF)
X		if(c == LF)
X		  {	ex_gobol(sb);
X			return(1);	/* Won! */
X		  }
X	return(0);
X}
X
X
Xchroff
Xex_blen(sbp)		/* Return length of specified sbbuf */
XSBBUF *sbp;
X{
X	return(sb_tell(sbp)+sb_ztell(sbp));
X}
X
X/* Miscellaneous stuff */
X
X/* E_GOFWSP() - Forward over whitespace */
Xe_gofwsp()
X{	register int c;
X	while((c = e_getc()) == SP || c == TAB);
X	if(c != EOF) e_backc();
X}
X
X/* E_GOBWSP() - Backward over whitespace */
Xe_gobwsp()
X{	register int c;
X	while((c = e_rgetc()) == SP || c == TAB);
X	if(c != EOF) e_getc();
X}
X
X
X/* E_GOLINE(n) - Goes N lines forward (or backward).
X**	If N == 0, goes to beginning of current line.
X**	Returns 0 if hit EOF.
X*/
Xe_goline(i)
Xregister int i;
X{
X	if(i > 0)
X	  {	do { if(!e_gonl()) return(0); }
X		while(--i);
X	  }
X	else if(i < 0)
X	  {	i = -i;
X		do { if(!e_gopl()) return(0); }
X		while(--i);
X	  }
X	else e_gobol();		/* arg of 0 */
X	return 1;
X}
X
X/* E_LBLANKP() - Returns true if all characters in rest of line are blank.
X *	Moves to beginning of next line as side effect, unless fails.
X */
Xe_lblankp()
X{	register int c;
X	for(;;) switch(e_getc())
X	  {
X		case SP:
X		case TAB:
X			continue;
X		case LF:	/* Normally drop thru to return 1 as for EOF */
X#if FX_EOLMODE
X			if(eolcrlf(cur_buf))
X			  {	e_rgetc();
X				if((c = e_rgetc()) != EOF) /* Get prev char */
X					e_getc();
X				e_getc();
X				if(c != CR)		/* Now examine */
X					continue;	/* Not CR-LF, go on */
X			  }	/* Else drop thru to return win */
X#endif
X		case EOF:
X			return(1);
X		default:
X			return(0);
X	  }
X	/* Never drops out */
X}
X
X
Xe_insn(ch, cnt)
Xint ch;
Xint cnt;
X{	register int i;
X	if((i = cnt) > 0)
X		do { e_putc(ch);
X		  } while(--i);
X}
X
Xe_sputz(acp)
Xchar *acp;
X{	register SBBUF *sb;
X	register char *cp;
X	register int c;
X	if(cp = acp)
X	  {	sb = CURSBB;
X		while(c = *cp++)
X			sb_putc(sb,c);
X	  }
X}
X
X/* BOLEQ - Returns TRUE if 2 dots are on the same line
X *	(i.e. have the same Beg-Of-Line)
X */
Xboleq(dot1,dot2)
Xchroff dot1,dot2;
X{	return( (ex_boldot(CURSBB,dot1) == ex_boldot(CURSBB,dot2)));
X}
X
X
Xchar *
Xdottoa(str,val)
Xchar *str;
Xchroff val;
X{	register char *s;
X
X	s = str;
X	if(val < 0)
X	  {	*s++ = '-';
X		val = -val;
X	  }
X	if(val >= 10)
X		s = dottoa(s, val/10);
X	*s++ = '0' + (int)(val%10);
X	*s = 0;
X	return(s);
X}
X
X
X/* Paragraph utilities */
X
X#if FX_FPARA || FX_BPARA || FX_MRKPARA || FX_FILLPARA
X
X#if FX_SFPREF
Xextern char *fill_prefix;	/* Defined in eefill.c for now */
Xextern int fill_plen;		/* Ditto */
X#endif /*FX_SFPREF*/
X
X#if ICONOGRAPHICS
Xint para_mode = PARABLOCK;	/* eexcmd.c only other file that refs this */
X#endif /*ICONOGRAPHICS*/
X
X
X/* Go to beginning of paragraph */
Xe_gobpa()
X{	register int c;
X	chroff savdot;
X
X	savdot = e_dot();
X	e_bwsp();
X	while((c = e_rgetc()) != EOF)
X		if(c == LF)		/* Went past line? */
X		  {	e_getc();		/* Back up and check */
X#if FX_SFPREF
X			if(fill_plen)
X				if(tstfillp(fill_plen))
X				  {	e_igoff(-(fill_plen+1));
X					continue;
X				  }
X				else break;
X#endif /*FX_SFPREF*/
X#if ICONOGRAPHICS
X                        c = e_peekc ();
X
X                        if (para_mode == PARABLOCK)
X                            if (c == LF)
X                                break;
X
X                        if (para_mode == PARALINE)
X                            if (c_wsp (c))
X                                break;
X#else
X			if(c_pwsp(e_peekc()))	/* Check 1st chr for wsp */
X				break;		/* If wsp, done */
X#endif /*ICONOGRAPHICS*/
X			e_rgetc();		/* Nope, continue */
X		  }
X	if((c = e_peekc()) == '.' || c == '-')
X	  {	e_gonl();
X		if(e_dot() >= savdot)
X			e_gopl();
X	  }
X}
X
X/* Go to end of paragraph */
Xe_goepa()
X{	register int c;
X
X	e_gobol();			/* First go to beg of cur line */
X	e_fwsp();
X	while((c = e_getc()) != EOF)
X        	if (c == LF)
X		  {
X#if FX_SFPREF
X		if(fill_plen)		/* If Fill Prefix is defined */
X			if(tstfillp(fill_plen))	/* then must start with it */
X				continue;
X			else break;		/* or else we're done */
X#endif /*FX_SFPREF*/
X#if ICONOGRAPHICS
X                if (para_mode == PARABLOCK)
X                    if (e_peekc () == LF)
X                        break;
X
X                if (para_mode == PARALINE)
X                    if (c_wsp (e_peekc ()))
X                        break;
X#else
X                if(c_pwsp(e_peekc()))
X                        break;
X#endif /*-ICONOGRAPHICS*/
X		  }
X}
X
Xexp_do(rpos, rneg)
Xint (*rpos)(), (*rneg)();
X{	register int e;
X	register int (*rtn)();
X
X	if((e = exp) == 0)
X		return;
X	rtn = rpos;
X	if(e < 0)
X	  {	rtn = rneg;
X		e = -e;
X	  }
X	do { (*rtn)();
X	  } while(--e);
X}
X
X
Xe_fwsp()
X{	register int c;
X	while(c_wsp(c = e_getc()));
X	if(c != EOF) e_backc();
X}
Xe_bwsp()
X{	register int c;
X	while(c_wsp(c = e_rgetc()));
X	if(c != EOF) e_getc();
X}
X
X
Xc_wsp(ch)
Xint ch;
X{	register int c;
X	c = ch;
X	if(c == SP || c == TAB || c == LF || c == FF)
X		return(1);
X	return(0);
X}
Xc_pwsp(ch)
Xint ch;
X{	register int c;
X	c = ch;
X	if(c == '.' || c == '-')
X		return(1);
X	return(c_wsp(c));
X}
X
X#endif /* FX_FPARA || FX_BPARA || FX_MRKPARA || FX_FILLPARA */
X
X/* Word function auxiliaries */
X
X/* Returns true if this character is a delimiter. */
Xdelimp(c)
Xint  c;
X{	static int  delim_tab[] =
X	  {
X	    0177777, 0177777,		/* All controls */
X	    0177777, 0176000,		/* All punct but 0-9 */
X	    0000001, 0074000,		/* All punct but A-Z and _ */
X	    0000001, 0174000		/* All punct but a-z */
X	  };
X	return (delim_tab[c >> 4] & (1 << (c & 017)));
X}
X
Xe_wding(adot,n)
Xregister chroff *adot;
Xint n;
X{	chroff savdot;
X	savdot = e_dot();
X	e_gowd(n);
X	*adot = e_dot();
X	e_go(savdot);
X	if(*adot == savdot)
X	  {	ring_bell();
X		return(0);
X	  }
X	return(1);
X}
Xchroff
Xe_wdot(dot,n)
Xchroff dot;
Xint n;
X{	chroff savdot, retdot;
X	savdot = e_dot();
X	e_go(dot);
X	e_gowd(n);
X	retdot = e_dot();
X	e_go(savdot);
X	return(retdot);
X}
Xe_gowd(n)
Xint n;
X{	register int (*gch)(), c, cnt;
X	int e_getc(), e_rgetc();
X	chroff ret_dot;
X
X	if((cnt = n) == 0)
X		return;
X	if(cnt > 0)
X		gch = e_getc;		/* Forward routine */
X	else
X	  {	gch = e_rgetc;		/* Backward routine */
X		cnt = -cnt;
X	  }
X	do
X	  {	ret_dot = e_dot();	/* Remember dot for last word found */
X		while((c = (*gch)()) != EOF && delimp(c));
X		if(c == EOF)
X		  {	e_go(ret_dot);		/* Use last word found */
X			break;
X		  }
X		while((c = (*gch)()) != EOF && !delimp(c));
X		if(c == EOF)
X			break;
X		if(n < 0) e_getc(); else e_backc();
X	  } while(--cnt);
X}
X
X/* Searching */
X
Xe_search(mstr,mlen,backwards)
Xchar *mstr;
Xint mlen;
Xint backwards;
X{	register SBBUF *sb;
X	register char *cp;
X	register int c;
X	char *savcp;
X	int cnt, scnt;
X#if IMAGEN
X	register int c1;
X	register int caseless = (cur_buf->b_flags & B_TEXTMODE);
X#endif /*IMAGEN*/
X
X	sb = (SBBUF *) cur_buf;
X	if (!backwards)
X	  {		/* Search forwards */
X	sfwd:	cp = mstr;
X		while((c = sb_getc(sb)) != EOF)
X		  {
X#if IMAGEN
X			if((!caseless && c != *cp) || 
X			    (caseless && upcase(c) != upcase(*cp))) continue;
X#else
X			if(c != *cp) continue;
X#endif /*-IMAGEN*/
X			cp++;
X			cnt = mlen;
X			while(--cnt > 0)
X			  {
X#if IMAGEN
X				c1 = *cp++;
X				c = e_getc();
X				if ((!caseless && c1 != c) ||
X				     (caseless && upcase(c1) != upcase(c)))
X#else
X				if(*cp++ != (c = e_getc()))
X#endif /*-IMAGEN*/
X				  {	if(c == EOF) return(0);
X					sb_seek(sb,(chroff)(cnt-mlen),1);
X					goto sfwd;
X				  }
X			  }
X			return(1);
X		  }
X	  }
X	else
X	  {		/* Search backwards */
X		scnt = mlen - 1;
X		savcp = mstr + scnt;		/* Point to end of string */
X
X	sbck:	cp = savcp;
X		while((c = sb_rgetc(sb)) != EOF)
X		  {
X#if IMAGEN
X			if((!caseless && c != *cp) || 
X			    (caseless && upcase(c) != upcase(*cp))) continue;
X#else
X			if(c != *cp) continue;
X#endif /*-IMAGEN*/
X			cp--;
X			if((cnt = scnt) == 0) return(1);
X			do
X			  {
X#if IMAGEN
X				c1 = *cp--;
X				c = e_rgetc();
X				if ((!caseless && c1 != c) ||
X				     (caseless && upcase(c1) != upcase(c)))
X#else
X				if(*cp-- != (c = e_rgetc()))
X#endif /*-IMAGEN*/
X				  {	if(c == EOF) return(0);
X					sb_seek(sb,(chroff)(mlen-cnt),1);
X					goto sbck;
X				  }
X			  }
X			while(--cnt);
X			return(1);
X		  }
X	  }
X	return(0);		/* Failed */
X}
/
echo x - eeerr.c
sed '/^X/s///' > eeerr.c << '/'
X/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*	EEERR - Error handling & testing routines
X */
X
X#include "elle.h"
X
X#if V6
X#include "eesigs.h"
X#else
X#include <signal.h>
X#endif
X
X/* EFUN: "Hit Breakpoint" */
Xf_bkpt()
X{	clean_exit();
X	bpt();
X        set_tty();
X}
Xbpt() {}		/* Put a DDT/ADB breakpoint here */
X
X#if !(STRERROR)		/* If strerror() not supported, we provide it. */
Xextern int sys_nerr;		/* Max index into sys_errlist */
Xextern char *sys_errlist[];
X
Xchar *
Xstrerror(num)
Xint num;
X{
X	static char badbuf[30];
X	if (num > 0 && num <= sys_nerr)
X		return (sys_errlist[num]);
X	sprintf(badbuf, "unknown error %d", num);
X	return badbuf;
X}
X#endif /* -STRERROR */
X
X
Xerrsbm(type,adr,str,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12)
Xregister int type;	/* Type, flags */
Xint (*adr)();		/* Addr called from */
Xchar *str;		/* Printf string */
X{	register struct buffer *b;
X	int oldttystate;
X
X	oldttystate = clean_exit();	/* Ensure not in editing mode */
X	if(type == SBFERR)	/* File overwrite error?  A0 is FD */
X	  {	printf("WARNING - FILE CORRUPTED!\nBuffers affected:\n");
X		for(b = buf_head; b; b = b->b_next)
X		  {	if(sb_fdinp((SBBUF *)b, a0))
X				printf((b->b_fn ? "  %s: %s\n" : "  %s\n"),
X					b->b_name, b->b_fn);
X		  }
X		if (oldttystate > 0) set_tty();
X		return(1);	/* Try to continue normally */
X	  }
X	printf("%sERR: %o ", (type ? "SBX" : "SBM"), adr);
X	printf(str,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12);
X	askerr();
X}
X
X/*
X *  Bite_bag -- Try to save our rear ends after a catastrophe.
X *	This routine is mainly called from "interrupt"
X *	level when a memory fault or bus error occurs.
X *	We try to save the buffer to the file "ELLE.crash"
X *	in the current working directory.  If it loses, well
X *	then you have really lost.  Note: this routine does
X *	not reset the appropriate signal handler, so it is
X *	never re-entered.  If a fault repeats once in this
X *	code, then the world dies.
X */
X
Xbite_bag(fault)				/* We come here on any memory error */
Xint fault;
X{
X	int ostate;
X	/* Some systems, such as BSD4.x and SUN, do not reset caught signals
X	 * to SIG_DFL.
X	 * This is a win, but isn't what vanilla UNIX code expects.
X	 * Since it doesn't hurt to do it explicitly, we always turn it off
X	 * explicitly...
X	 */
X	signal(fault, SIG_DFL);		/* Reinstate default handling */
X
X	ostate = clean_exit();		/* Fix up the terminal modes first! */
X	printf("ELLE stopped by fatal interrupt (%d)!\n\
XType S or W to try saving your work.\n",fault);
X	askerr();
X	if(ostate > 0) set_tty();
X	signal(fault, bite_bag);	/* If continued, re-enable signal */
X}
X
X/* HUP_EXIT - Called by a SIGHUP hangup signal.
X *	Tries to save all modified buffers before exiting.
X *	Note that the TTY is not touched at all, although the terminal mode
X *	flag is set just in case further error handling routines are invoked.
X */
Xhup_exit()
X{	extern int trm_mode;		/* See e_disp.c */
X
X	trm_mode = -1;			/* Say TTY is now detached */
X	saveworld((struct buffer *)0, 0);	/* Save world, w/o feedback */
X	exit(1);
X}
X
Xerrint()		/* Routine provided for ADB jumps */
X{	askerr();
X}
Xchar askh1[] = "\
XA - Abort process\n\
XB - Breakpoint (must have \"bpt:b\" set in ADB)\n\
XC - Continue\n\
XD - Diagnostic command mode\n";
Xchar askh2[] = "\
XS - Try to save current buffer\n\
XW - Try to save world (all modified buffers)\n";
X
Xint bsaving = 0;	/* Set while in middle of saving buffer(s) */
X
Xaskerr()
X{	register struct buffer *b;
X	char linbuf[100];
X	char *asklin();
X	extern int (*funtab[])();	/* In E_CMDS.C */
X	int ostate;
X
X	ostate = clean_exit();		/* Clean up TTY if not already done */
Xreask:
X	printf("(A,B,C,D,S,W,?)");
X	switch(upcase(*asklin(linbuf)))
X	  {
X		case '?':
X			writez(1,askh1);	/* Too long for &$@! printf */
X			writez(1,askh2);	/* Too long for &$@! V6 C */
X			break;			/*    optimizer (/lib/c2) */
X		case 'A':
X			abort();
X			break;
X		case 'B':
X			bpt();
X			break;
X		case 'Q':
X		case 'C':
X			goto done;
X		case 'D':
X			if(funtab[FN_DEBUG])
X				(*funtab[FN_DEBUG])(-1);
X			else printf("Sorry, no diagnostics\n");
X			break;
X		case 'S':	/* Try to save current buffer only */
X			b = cur_buf;
X			goto savb;
X		case 'W':	/* Try to save all modified buffers */
X			b = 0;
X		savb:	if(bsaving++)
X			  {	printf("Already saving -- continued");
X				goto done;
X			  }
X			saveworld(b, 1);	/* Save, with feedback */
X			bsaving = 0;
X			break;
X	  }
X	goto reask;
Xdone:
X	if(ostate > 0)
X		set_tty();
X}
X
Xchar *
Xasklin(acp)
Xchar *acp;
X{	register char *cp;
X	register int c;
X	cp = acp;
X	while((c = tgetc()) != LF)
X		*cp++ = c;
X	*cp++ = 0;
X	return(acp);
X}
/
echo x - eef1.c
sed '/^X/s///' > eef1.c << '/'
X/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*
X * EEF1	Various functions
X *		Char move/ins/del
X *		Case change
X *		Char/word transpose
X */
X
X#include "elle.h"
X
X/* EFUN: "Insert Self" */
Xf_insself (c)
Xint c;
X{
X#if IMAGEN
X	fim_insself(c);
X#else
X#if FX_FILLMODE
X	extern int fill_mode;
X
X	if(fill_mode) fx_insfill(c);
X	else
X#endif /*FX_FILLMODE*/
X	ed_insn(c, exp);	/* Normal stuff */
X#endif /*-IMAGEN*/
X}
X
X/* EFUN: "Quoted Insert"
X**	Inserts next char directly, <exp> number of times.
X** Does not check anything about the char and does not do anything
X** depending on the mode.  In particular, CR is not turned into EOL.
X*/
Xf_quotins()
X{
X	ed_insn(cmd_read(), exp);	/* Insert next char directly */
X}
X
X#if FX_CRLF
X/* EFUN: "CRLF" */
Xf_crlf()
X{
X#if IMAGEN
X	fim_crlf();
X#else
X	register int i;
X
X	if(e_goeol() == cur_dot		/* If at end of current line */
X	  && exp == 1			/* and inserting only 1 new line */
X	  && e_lblankp() && e_lblankp())	/* and next 2 lines blank */
X	  {	e_gocur();		/* Then just re-use next line. */
X		e_gonl();		/* Go to its start */
X		e_setcur();		/* and establish cur_dot there. */
X		ed_delete(e_dot(), e_eoldot());	/* Ensure any blanks flushed */
X	  }
X	else
X	  {	e_gocur();		/* Otherwise back to original place */
X		if((i = exp) > 0)	/* and simply insert newlines */
X			do ed_crins();
X			while(--i);
X	  }
X#endif /*-IMAGEN*/
X}
X#endif /*FX_CRLF*/
X
X/* EFUN: "Forward Character" */
Xf_fchar()
X{	ed_igoff(exp);
X}
X
X/* EFUN: "Backward Character" */
Xf_bchar()
X{	ed_igoff(-exp);
X}
X
X/* EFUN: "Delete Character" */
Xf_dchar ()
X{
X#if IMAGEN
X	fim_dchar();
X#else
X	ef_deln(exp);
X#endif /*-IMAGEN*/
X}
X
X/* EFUN: "Backward Delete Character" */
Xf_bdchar ()
X{
X#if IMAGEN
X	fim_bdchar();
X#else
X	ef_deln(-exp);
X#endif /*-IMAGEN*/
X}
X
X/* Delete forward or backward N characters.
X * If arg, kills instead of deleting.
X */
Xef_deln(x)
Xint x;
X{
X	e_igoff(x);
X	if(exp_p) ed_kill(cur_dot, e_dot());
X	else ed_delete(cur_dot, e_dot());
X}
X
X#if FX_DELSPC
X/* EFUN: "Delete Horizontal Space" */
X/*	Delete spaces/tabs around point.
X */
Xf_delspc()
X{	chroff dot1;
X
X	e_gobwsp();			/* Move backward over whitespace */
X	dot1 = e_dot();			/* Save point */
X	e_gofwsp();			/* Move forward over whitespace */
X	ed_delete(dot1,e_dot());	/* Delete twixt start and here */
X}
X#endif /*FX_DELSPC*/
X
X#if FX_TCHARS
X/* EFUN: "Transpose Characters"
X *	Transpose chars before and after cursor.  Doesn't hack args yet.
X * EMACS: With positive arg, exchs chars before & after cursor, moves right,
X *	and repeats the specified # of times, dragging the char to the
X *	left of the cursor right.
X *	With negative arg, transposes 2 chars to left of cursor, moves
X *	between them, and repeats the specified # of times, exactly undoing
X *	the positive arg form.  With zero arg, transposes chars at point
X *	and mark.
X *	HOWEVER: at the end of a line, with no arg, the preceding 2 chars
X *	are transposed.
X */
Xf_tchars()
X{	register int c, c2;
X#if IMAGEN
X	c = e_rgetc();			/* Gosmacs style: twiddle prev 2 */
X	if (c == EOF)
X		return(e_gocur());	/* Do nothing at beginning of bfr */
X#else
X
X	if((c = e_getc()) == EOF	/* If at EOF */
X	  || e_rgetc() == LF)		/* or at end of line, */
X		c = e_rgetc();		/* use preceding 2 chars */
X#endif /*-IMAGEN*/
X
X	if((c2 = e_rgetc()) == EOF)	/* At beginning of buffer? */
X		return(e_gocur());	/* Yes, do nothing */
X	e_ovwc(c);
X	e_ovwc(c2);
X	e_setcur();
X	buf_tmod((chroff)-2);		/* Munged these 2 chars */
X}
X#endif /*FX_TCHARS*/
X
X#if FX_FWORD
X/* EFUN: "Forward Word" */
Xf_fword()
X{	chroff retdot;
X	if(e_wding(&retdot, exp))
X		ed_go(retdot);
X}
X#endif
X
X#if FX_BWORD
X/* EFUN: "Backward Word" */
Xf_bword()
X{	exp = -exp;
X	f_fword();
X}
X#endif
X
X#if FX_KWORD
X/* EFUN: "Kill Word" */
Xf_kword()
X{	chroff retdot;
X
X	if(e_wding(&retdot,exp))
X	  {	ed_kill(cur_dot,retdot);
X		this_cmd = KILLCMD;
X	  }
X}
X#endif
X
X#if FX_BKWORD
X/* EFUN: "Backward Kill Word" */
Xf_bkword()
X{	exp = -exp;
X	f_kword();
X}
X#endif
X
X#if FX_TWORDS
X/* EFUN: "Transpose Words" */
X/*	Transpose word.  Urk!
X */
Xf_twords()
X{	register SBSTR *sd1, *sd2;
X	register SBBUF *sb;
X	chroff begwd1, endwd1, begwd2, endwd2;
X
X	endwd2 = e_wdot(cur_dot, 1);	/* Go to end of 2nd word */
X	begwd2 = e_wdot(endwd2, -1);	/* Go to beg of 2nd word */
X	if(begwd2 >= endwd2)		/* If no 2nd word, punt. */
X		return;
X	begwd1 = e_wdot(begwd2, -1);	/* Go to beg of 1st word */
X	endwd1 = e_wdot(begwd1, 1);	/* Go to end of 1st word */
X	if(begwd1 >= endwd1)		/* If no 1st word, punt. */
X		return;
X	if(endwd1 > begwd2)		/* Avoid possible overlap */
X		return;
X
X	e_go(begwd2);
X	sb = (SBBUF *)cur_buf;
X	sd2 = sb_killn(sb, endwd2 - begwd2);	/* Excise wd 2 first */
X	e_go(begwd1);
X	sd1 = sb_killn(sb, endwd1 - begwd1);	/* Excise wd 1 */
X	sb_sins(sb, sd2);			/* Replace with wd 2 */
X	e_goff(begwd2 - endwd1);		/* Move past between stuff */
X	sb_sins(sb, sd1);			/* Insert wd 1 */
X	e_setcur();
X	buf_tmat(begwd1);			/* Modified this range */
X}
X#endif /*FX_TWORDS*/
X
X/* Case hacking functions and support */
X
X#if FX_UCWORD
X/* EFUN: "Uppercase Word" */
Xf_ucword()
X{	case_word(0);
X}
X#endif /*FX_UCWORD*/
X
X#if FX_LCWORD
X/* EFUN: "Lowercase Word" */
Xf_lcword()
X{	case_word(1);
X}
X#endif /*FX_LCWORD*/
X
X#if FX_UCIWORD
X/* EFUN: "Uppercase Initial" */
Xf_uciword()
X{	case_word(2);
X}
X#endif /*FX_UCIWORD*/
X
X#if FX_UCWORD||FX_LCWORD||FX_UCIWORD
Xcase_word (downp)
X{	chroff retdot;
X#if IMAGEN
X	chroff startdot;
X
X	/* Normalize our position to beginning of "current" word,
X	 * where "current" is defined to be the current word we are in,
X	 * or else the previous word if we are not in any word.
X	 * All this silly nonsense just to perpetuate Gosmacs's
X	 * wrong behaviour!
X	 */
X	startdot = cur_dot;	/* Save current position */
X	e_getc();	/* If at beg of word, ensure we get inside it */
X	e_gowd(-1);	/* Go to start of this or prev word */
X	e_setcur();	/* Set cur_dot */
X#endif /*IMAGEN*/
X
X	if(e_wding(&retdot, exp))
X	  {	ed_case(cur_dot,retdot,downp);
X		e_gosetcur(retdot);
X	  }
X#if IMAGEN
X	e_gosetcur(startdot);
X#endif /*IMAGEN*/
X}
X#endif /* any case_word caller */
/
echo x - eef2.c
sed '/^X/s///' > eef2.c << '/'
X/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*
X * EEF2		Various functions
X */
X
X#include "elle.h"
X
X/* Line Handling functions */
X
X/* EFUN: "Beginning of Line" */
Xf_begline()
X{	e_gobol();
X	ed_setcur();
X}
X
X/* EFUN: "End of Line" */
Xf_endline()
X{	e_goeol();
X	ed_setcur();
X}
X
X/* EFUN: "Next Line" */
X/*	Goes to beginning of next line */
Xf_nxtline()
X{	return(down_bline(exp));
X}
X
X/* EFUN: "Previous Line" */
X/*	Goes to beginning of previous line */
Xf_prvline()
X{	return(down_bline(-exp));
X}
X
X/* EFUN: "Down Real Line" */
Xf_dnrline ()
X{	down_line(exp);
X}
X
X/* EFUN: "Up Real Line" */
Xf_uprline ()
X{	down_line(-exp);
X}
X
X#if FX_OLINE
X/* EFUN: "Open Line" */
Xf_oline()
X{	register int i;
X	chroff savdot;
X
X	savdot = cur_dot;
X	if((i = exp) > 0)
X		do { ed_crins(); }
X		while(--i);
X	e_gosetcur(savdot);
X}
X#endif /*FX_OLINE*/
X
X#if FX_DELBLINES
X/* EFUN: "Delete Blank Lines" */
X/*	Delete blank lines around point.
X */
Xf_delblines()
X{	register int c;
X	chroff dot1, dot2, oldcur;
X
X	oldcur = cur_dot;
X	do { e_gobwsp(); }
X	while ((c = e_rgetc()) == LF);
X	if (c != EOF)
X		e_gonl();
X	dot1 = e_dot();
X	if(dot1 > oldcur) return;
X	do { e_gofwsp(); }
X	while ((c = e_getc()) == LF);
X	if(c != EOF)
X		e_gobol();
X	dot2 = e_dot();
X	if(dot2 < oldcur) return;
X	ed_delete(dot1,dot2);
X}
X#endif /*FX_DELBLINES*/
X
X#if FX_KLINE
X/* EFUN: "Kill Line" */
Xf_kline()
X{
X	if(exp_p)
X		e_goline(exp);		/* Move that many lines */
X					/* (if 0, goes to BOL) */
X	else				/* No arg, handle specially */
X	  {	if(e_lblankp())		/* Is rest of line blank? */
X			;		/* Yes, now at next line! */
X		else e_goeol();		/* No, go to EOL rather than NL */
X	  }
X	ed_kill(cur_dot,e_dot());
X	e_setcur();
X	this_cmd = KILLCMD;
X}
X#endif /*FX_KLINE*/
X
X#if FX_BKLINE
X/* EFUN: "Backward Kill Line" (not EMACS) */
X/*	Originally an Iconographics function.
X*/
Xf_bkline()
X{
X	if(exp_p) exp = -exp;		/* If arg, invert it */
X	else
X	  {	exp = 0;		/* No arg, furnish 0 */
X		exp_p = 1;
X	  }
X	f_kline();			/* Invoke "Kill Line" */
X}
X#endif /*FX_BKLINE*/
X
X#if FX_GOLINE
X/* EFUN: "Goto Line" (not EMACS) (GNU goto-line) */
Xf_goline()
X{
X        e_gobob();
X        down_bline(exp-1);    /* already at line 1 */
X}
X#endif /*FX_GOLINE*/
X
Xdown_bline(arg)
Xint arg;
X{	
X	if(arg)
X		e_goline(arg);
X	ed_setcur();
X}
X
X#if FX_DNRLINE || FX_UPRLINE
Xdown_line (x)
Xint x;
X{	register int i, res;
X
X	res = x ? e_goline(x) : 1;	/* Move that many lines */
X	goal = 0;
X	if(res == 0)			/* Hit buffer limits (EOF)? */
X	  {	if(x > 0)		/* Moving downwards? */
X		  {
X#if !(IMAGEN)		/* If IMAGEN, do not extend!! */
X			if(x == 1) ed_crins();	/* Yeah, maybe extend */
X			else
X#endif
X				goal = indtion(cur_dot);
X			goto done;
X		  }
X	  }
X
X	if(last_cmd == LINECMD		/* If previous cmd also a line move */
X	  && pgoal != -1)		/* and we have a previous goal col */
X		goal = pgoal;		/* then make it the current goal */
X	else goal = indtion(cur_dot);	/* Else invent goal from current pos */
X
X	i = inindex(e_dot(), goal);	/* See # chars needed to reach goal */
X	if(i == -1)			/* If off edge of line, */
X		e_goeol();		/* just move to end of this line */
X	else e_igoff(i);		/* else move to goal. */
X
Xdone:	pgoal = goal;
X	this_cmd = LINECMD;
X	ed_setcur();
X}
X#endif /*FX_DNRLINE || FX_UPRLINE*/
X
X
X
X/* Region Handling functions */
X
X/* EFUN: "Set/Pop Mark" */
Xf_setmark()
X{
X	mark_dot = e_dot();
X	mark_p = 1;
X	if(ev_markshow)			/* If have one, show indicator */
X		saytoo(ev_markshow);	/* that mark was set. */
X}
X
X/* EFUN: "Exchange Point and Mark" */
Xf_exchmark()
X{	chroff tmpdot;
X
X	if(chkmark())
X	  {	tmpdot = mark_dot;
X		mark_dot = cur_dot;
X		ed_go(tmpdot);		/* Set cur_dot and go there */
X	  }
X}
X
X/* EFUN: "Kill Region" */
Xf_kregion()
X{
X	if(chkmark())
X	  {	ed_kill(cur_dot,mark_dot); /* Will adj cur_dot, mark_dot */
X		e_gocur();
X		this_cmd = KILLCMD;
X	  }
X}
X
X/* EFUN: "Copy Region" */
Xf_copreg()
X{
X	if(chkmark())
X	  {	e_gocur();
X		kill_push(e_copyn(mark_dot - cur_dot));
X		e_gocur();
X	  }
X}
X
X
X/* EFUN: "Uppercase Region" */
Xf_ucreg()
X{	ef_creg(0);
X}
X
X/* EFUN: "Lowercase Region" */
Xf_lcreg()
X{	ef_creg(1);
X}
X
Xef_creg(downp)
Xint downp;
X{
X	if(chkmark())
X		ed_case(cur_dot,mark_dot,downp);
X}
X
X#if FX_FILLREG
X/* EFUN: "Fill Region" */
Xf_fillreg()
X{	if(chkmark())
X		ed_fill(mark_dot,cur_dot,0);
X}
X#endif /*FX_FILLREG*/
X
X/* CHKMARK() - minor utility for region-hacking functions.
X *	Returns TRUE if mark exists.
X *	Otherwise complains to user and returns 0.
X */
Xchkmark()
X{	if(mark_p == 0)
X		ding("No mark!");
X	return(mark_p);
X}
X
X/* Paragraph functions */
X
X#if FX_FPARA
X/* EFUN: "Forward Paragraph" */
Xf_fpara()
X{	int e_gobpa(), e_goepa();
X
X	exp_do(e_goepa, e_gobpa);
X	ed_setcur();
X}
X#endif /*FX_FPARA*/
X
X#if FX_BPARA
X/* EFUN: "Backward Paragraph" */
X/*	Go to beginning of paragraph.
X *	Skip all whitespace until text seen, then stop at beginning of
X *	1st line starting with whitespace.
X */
Xf_bpara()
X{	int e_gobpa(), e_goepa();
X
X	exp_do(e_gobpa, e_goepa);
X	ed_setcur();
X}
X#endif /*FX_BPARA*/
X
X#if FX_MRKPARA
X/* EFUN: "Mark Paragraph" */
Xf_mrkpara()
X{
X	f_fpara();		/* Go to end of paragraph */
X	f_setmark();		/* Put mark there */
X	f_bpara();		/* Then back to start of paragraph */
X}
X#endif /*FX_MRKPARA*/
X
X#if FX_FILLPARA
X/* EFUN: "Fill Paragraph" */
Xf_fillpara()
X{
X	chroff savloc, endloc;
X
X	savloc = cur_dot;
X#if ICONOGRAPHICS
X        e_getc();			/* DON'T go to next para if at end */
X        e_gobpa();			/* of this one!! */
X#endif /*ICONOGRAPHICS*/
X	e_goepa();			/* Go to end of parag */
X	if(e_rgetc() != LF)		/* If last char EOL, back over it. */
X		e_getc();
X	endloc = e_dot();		/* Remember where end is */
X	e_gobpa();			/* Go to beg of parag */
X#if ICONOGRAPHICS
X        kill_push(e_copyn(endloc - e_dot ()));
X                                        /* put old version on kill ring */
X#endif /*ICONOGRAPHICS*/
X	e_fwsp();			/* Move over initial whitespace */
X	ed_fill(e_dot(), endloc, 0);	/* Fill this region, return to dot */
X}
X#endif /*FX_FILLPARA*/
/
echo x - eef3.c
sed '/^X/s///' > eef3.c << '/'
X/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*
X * EEF3		Various Functions (Yanking, Indentation, miscellaneous)
X */
X
X#include "elle.h"
X
X#if FX_APPNKILL
X/* EFUN: "Append Next Kill" */
Xf_appnkill()
X{	this_cmd = KILLCMD;	/* Fake out next call to ed_kill */
X}
X#endif /*FX_APPNKILL*/
X
X#if FX_UNKILL
X/* EFUN: "Un-kill" */
Xf_unkill()
X{	register SBSTR *sd;
X
X	if((sd = kill_ring[kill_ptr]) == 0)
X	  {	ring_bell();
X		return;
X	  }
X	mark_dot = cur_dot;		/* Set mark at old location */
X	mark_p = 1;			/* Mark's been set */
X	sb_sins((SBBUF *)cur_buf,sbs_cpy(sd));	/* Insert copy of stuff */
X	cur_dot = e_dot();		/* We're now after the new stuff */
X	buf_tmat(mark_dot);		/* Say modified from here to cur_dot*/
X	this_cmd = YANKCMD;
X}
X#endif /*FX_UNKILL*/
X
X#if FX_UNKPOP
X/* EFUN: "Un-kill Pop" */
Xf_unkpop()
X{	register SBSTR *sd;
X	register int i;
X
X	if (last_cmd != YANKCMD)
X	  {	ring_bell ();
X		return;
X	  }
X	ed_delete(cur_dot,mark_dot);
X	if(cur_dot > mark_dot)
X		cur_dot = mark_dot;
X	i = KILL_LEN;
X	do {
X		if(--kill_ptr < 0)
X			kill_ptr = KILL_LEN-1;
X		if(sd = kill_ring[kill_ptr])
X			break;
X	  } while(--i);
X
X	/* kill_ptr now pointing to right place; effect the yank. */
X	e_gocur();		/* Make sure point at right place too! */
X	return(f_unkill());
X}
X#endif /*FX_UNKPOP*/
X
X/* Indentation routines - still not polished */
X
X#if FX_INDATM
X/* EFUN: "Indent According to Mode" */
X/*	In Fundamental mode, just inserts a tab.
X*/
Xf_indatm()
X{	f_insself(TAB);		/* This takes care of mode checking */
X}
X#endif /*FX_INDATM*/
X
X#if FX_INDNL
X/* EFUN: "Indent New Line" */
Xf_indnl()			/* execute CR followed by tab */
X{
X#if IMAGEN
X	/* Not dispatch-based, but rather hard-wired to do Gosmacs thing */
X	ed_crins();
X	f_indund();
X#else
X	cmd_xct(CR);
X	cmd_xct(TAB);
X#endif /*-IMAGEN*/
X}
X#endif /*FX_INDNL*/
X
X
X#if FX_BACKIND
X/* EFUN: "Back to Indentation"
X**	Moves to end of current line's indentation.
X*/
Xf_backind()
X{	e_gobol();	/* First move to beg of line */
X	e_gofwsp();	/* Then forward over whitespace */
X	ed_setcur();
X}
X#endif /*FX_BACKIND*/
X
X
X#if FX_INDCOMM
X
Xstatic char *comm_beg = "/* ";
Xstatic char *comm_end = " */";
X
X/* EFUN: "Indent for Comment" */
Xf_indcomm()
X{
X	f_endline();
X	if(indtion(cur_dot) < ev_ccolumn)
X		ed_indto(ev_ccolumn);
X	else ed_sins("  ");
X	ed_sins (comm_beg);
X	ed_sins (comm_end);
X	e_igoff(-strlen (comm_end));       /* back over end string */
X	e_setcur();
X}
X#endif /*FX_INDCOMM*/
X
X#if FX_INDREL
X/* EFUN: "Indent Relative" */
X/* This used to mistakenly be called Indent Under.
X**	Still not fully implemented.
X**	If at beginning of line, looks back at previous indented line,
X** and indents this line that much.  If there is no preceding indented
X** line or not at beginning of line, insert a tab.
X*/
Xf_indrel()
X{	register int c;
X	register  n;
X#if IMAGEN
X	chroff savdot;
X#endif /*IMAGEN*/
X#if ICONOGRAPHICS
X        chroff savdot;
X        int curind, newind, morebuf;
X#endif /*ICONOGRAPHICS*/
X
X	if((c = e_rgetc()) == EOF)
X#if IMAGEN
X		return(f_insself(TAB));	/* Do mode-based tabbing */
X#else
X		return(ed_insert(TAB));
X#endif /*-IMAGEN*/
X
X	if(c == LF)
X	  {	e_gobol();
X		e_gofwsp();
X		n = d_curind();
X		e_gonl();		/* Return to orig pos */
X		if(n)
X		  {	ed_indto(n);
X#if IMAGEN
X			savdot = e_dot();
X			e_gofwsp();
X			ed_delete(savdot, e_dot());
X#endif /*IMAGEN*/
X			return;
X		  }
X	  }
X#if ICONOGRAPHICS
X        else
X          {     e_igoff (1);
X                curind = indtion (savdot = e_dot ());
X                                /* get current dot and indentation */
X                while (1)       /* find a prev line that extends rightward */
X                   {    morebuf = e_gopl ();
X                        e_goeol ();
X                        if ((newind = d_curind()) > curind) break;
X                        if (morebuf == 0)  /* hit beginning of buffer */
X                        {       e_go (savdot);
X                                f_delspc();
X                                return (1);
X                        }
X                   }
X
X                e_gobol ();
X                e_igoff (inindex (e_dot (), curind));
X                if (d_curind() > curind)
X                        e_rgetc ();              /* pushed ahead by tab */
X
X                while (c_wsp (e_getc ()) == 0) ;
X                e_backc ();
X                e_fwsp ();
X                newind = d_curind();
X                e_go (savdot);
X                f_delspc();
X                ed_indto (newind);
X           }
X#else
X        else e_getc();
X#if IMAGEN
X	f_insself(TAB);			/* Do mode-based tabbing */
X#else
X	ed_insert(TAB);
X#endif /*-IMAGEN*/
X#endif /*-ICONOGRAPHICS*/
X}
X#endif /*FX_INDREL*/
X
X
X/* Paren matching stuff.  Note that this stuff will be very painful unless
X** tinwait() works properly.
X*/
X#if 0
X/* EFUN: "Self-Insert and Match" (intended to be bound to brackets) */
X/* (KLH: Evidently this was never finished)
X*/
Xinsertmatch(c)
Xregister int c;
X{
X	
X}
X#endif
X
X/* Should change this to Matching Paren */
X#if FX_MATCHBRACK
X/* EFUN: "Match Bracket" (not EMACS) - from IMAGEN config
X * Show the matching bracket for the character right before dot
X */
Xf_matchbrack()
X{
X	chroff savdot;
X	register int i, mc, secs;
X
X	if (exp_p)
X		secs = exp;
X	else
X		secs = 1;
X	savdot = cur_dot;		/* Save our location */
X	mc = e_rgetc();			/* Pick up character before dot */
X	if (mc != ')' && mc != ']' && mc != '}')
X	  {	e_getc();		/* Nothing, try at dot instead */
X		e_getc();
X		mc = e_rgetc();
X		if (mc != ')' && mc != ']' && mc != '}')
X		  {	ding("What bracket?");
X			e_go(savdot);
X			return;
X		  }
X	  }
X	if (! matchonelevel(mc))
X		ring_bell();
X	else
X	  {	ed_setcur();
X	        if (d_line(cur_dot) < 0)
X			secs = 10;	/* Wait longer if off-screen */
X		redisplay();		/* Wish it were simple upd_wind() */
X	        for (i = 1; i <= secs; ++i)
X		  {	if (tinwait())
X				break;
X			sleep(1);
X	          }
X	  }
X	e_gosetcur(savdot);		/* Back to origin */
X	redp(RD_MOVE);			/* Cursor has moved */
X}
X
X
X/* Try to match 'mc', return true iff found it */
Xmatchonelevel(mc)
Xregister int mc;
X{
X	register int c;
X
X	while ((c = e_rgetc()) != EOF)
X	  {	if (c == /*[*/ ']' || c == /*(*/ ')' || c == /*{*/ '}')
X		  {	if (! matchonelevel(c))
X				break;
X		  }
X		else if (c == '(' /*)*/)
X			return(mc == /*(*/ ')');
X		else if (c == '[' /*]*/)
X			return(mc == /*[*/ ']');
X		else if (c == '{' /*}*/)
X			return(mc == /*{*/ '}');
X	  }
X	return(0);
X}
X#endif /*FX_MATCHBRACK*/
/
echo x - eefd.c
sed '/^X/s///' > eefd.c << '/'
X/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X
X/* EEFD		Display control functions
X */
X
X#include "elle.h"
X
X
X#if FX_NEWWIN
X/* EFUN: "New Window" */
X/* 	Clear current window and set as requested.
X *		^L - clear current window and redisplay it (default top)
X *		<arg>^L - select new window so that current line is
X *			the <arg>'th from top of window (0 = top line)
X *		^U^L - clear current line and redisplay.
X */
Xf_newwin()
X{	register int i, n;
X	register struct window *w;
X
X	d_fixcur();		/* Ensure screen vars correct */
X	w = cur_win;
X	if (exp_p)
X	  {	if((n = exp) == 4 && exp_p == 4		/* CTRL-U? */
X		  && (i = d_line(cur_dot)) >= 0)	/* On valid line? */
X		  {	d_lupd(w, i);			/* Update it */
X			return;
X		  }
X	  }
X	else			/* No argument given */
X	  {	redp(RD_SCREEN);	/* Clear whole screen (later just window? */
X#if IMAGEN
X		return;
X#else
X		n = (ev_nwpct*w->w_ht)/100;	/* Set new window using % */
X#endif /*-IMAGEN*/
X	  }
X	if (n < 0) n = 0;		/* Ensure # is reasonable */
X	else if (n >= w->w_ht)
X		n = w->w_ht - 1;
X	d_fgoloff(-n);			/* Go up given # of lines */
X	w->w_topldot = e_dot();		/* Set new top-line dot */
X	e_gocur();			/* Move back to cur_dot */
X	redp(RD_FIXWIN);		/* Say to re-hack window */
X}
X#endif /*FX_NEWWIN*/
X
X#if FX_NSCREEN
X/* EFUN: "Next Screen" */
Xf_nscreen()
X{	d_screen( exp);
X}
X#endif /*FX_NSCREEN*/
X
X#if FX_PSCREEN
X/* EFUN: "Previous Screen" */
Xf_pscreen()
X{	d_screen(-exp);
X}
X#endif /*FX_PSCREEN*/
X
X#if FX_OTHNSCREEN
X/* EFUN: "Other New Screen" (not EMACS) - from IMAGEN config */
Xf_othnscreen()
X{
X	if (! oth_win)
X		return;
X	f_othwind();
X	if (exp_p)			/* With arg, back up */
X		d_screen(-1);
X	else
X		d_screen(1);
X	f_othwind();
X	redp(RD_WINDS);			/* Look at all windows */
X}
X#endif /*FX_OTHNSCREEN*/
X
X
X#if FX_LWINDBORD
X/* EFUN: "Line to Window Border" (not EMACS) - from IMAGEN config */
Xf_lwindbord()
X{
X	if (exp_p)
X		/* With arg, means "to bottom" */
X		exp = cur_win->w_ht - 1;
X	else
X		/* Else, to top */
X		exp = 0;
X
X	/* Just a "front end" for ^L */
X	exp_p = 1;
X	f_newwin();
X}
X#endif /*FX_LWINDBORD*/
X
X#if FX_SCUPWIND
X/* EFUN: "Scroll Window Up" (not EMACS) - from IMAGEN config */
Xf_scupwind()
X{
X	scroll_win(exp);
X}
X#endif /*FX_SCUPWIND*/
X
X#if FX_SCDNWIND
X/* EFUN: "Scroll Window Down" (not EMACS) - from IMAGEN config */
Xf_scdnwind()
X{
X	scroll_win(-exp);
X}
X#endif /*FX_SCDNWIND*/
X
X
X#if FX_MVWTOP
X/* EFUN: "Move to Window Top" (not EMACS) - from IMAGEN config */
Xf_mvwtop()
X{
X	extern moveborder();
X	moveborder(1);
X}
X#endif /*FX_MVWTOP*/
X
X#if FX_MVWBOT
X/* EFUN: "Move to Window Bottom" (not EMACS) - from IMAGEN config */
Xf_mvwbot()
X{
X	extern moveborder();
X	moveborder(0);
X}
X#endif /*FX_MVWBOT*/
X
X
X
X#if FX_NSCREEN || FX_PSCREEN || FX_OTHNSCREEN
X/* Move to new loc by N screenfuls.
X * If moving downward, keep bottom 2 lines of current screen on top of next.
X * If moving up, keep top 2 lines of current screen on bottom of next.
X */
Xd_screen(rep)
Xint rep;
X{
X	register int i;
X	register struct window *w;
X	chroff newdot;
X
X	w = cur_win;
X	if((i = w->w_ht - 2) <= 0)	/* Just-in-case check */
X		i = 1;
X	if((i *= rep) == 0)
X		return;
X	d_fixcur();			/* Ensure window fixed up */
X	e_go(w->w_topldot);		/* Start at top of screen */
X	d_fgoloff(i);
X
X	/* Find where we are now, and make that the new top of window. */
X	if((newdot = e_dot()) != e_blen())	/* If not at EOF, */
X		w->w_topldot = newdot;	/* set new top of window! */
X	else w->w_topldot = 0;		/* Else let fix_wind select top. */
X
X	e_setcur();			/* Ensure cur_dot set to real loc */
X#if IMAGEN
X	redp(RD_WINRES|RD_REDO);	/* HINT: just repaint screen */
X#else
X	redp(RD_FIXWIN|RD_MOVE);
X#endif /*-IMAGEN*/
X}
X#endif /*FX_NSCREEN || FX_PSCREEN || FX_OTHNSCREEN*/
X
X#if FX_SCUPWIND || FX_SCDNWIND	/* If want scroll-window function */
Xscroll_win(n)
Xregister int n;
X{	register struct window *w = cur_win;
X	chroff savdot;
X
X	if (n == 0) return;
X	d_fixcur();		/* Ensure screen vars for win all set up */
X	e_go(w->w_topldot);	/* Go to top of current window */
X	d_fgoloff(n);		/* Move given # of display lines */
X	w->w_topldot = e_dot();	/* Set new top of window */
X	redp(RD_FIXWIN);	/* Say new window needs fixing up */
X
X	/* Now adjust position of current dot so it is still within window */
X	if (n > 0)
X	  {	/* Moving screen text "up" (win down) */
X		if (cur_dot < w->w_topldot)	/* See if scrolled off top */
X			e_setcur();		/* yes, make dot be win top */
X	  }
X	else {	/* Moving screen text "down" (win up) */
X		savdot = cur_dot;	/* Save since must temporarily */
X		e_setcur();		/* set current dot within window, */
X		d_fixcur();		/* so screen can be fixed up. */
X		if (inwinp(w, savdot))	/* Now see if old dot in new win */
X			cur_dot = savdot;	/* Yes, just restore it! */
X		else			/* No, make it beg of bottom line. */
X			cur_dot = scr[w->w_pos + w->w_ht - 1]->sl_boff;
X	  }
X	e_gocur();		/* Make current pos be cur_dot */
X}
X#endif /* FX_SC%%WIND */
X
X#if FX_MVWTOP || FX_MVWBOT	/* Guts for above two functions */
Xstatic
Xmoveborder(top)
Xint top;
X{
X	d_fixcur();		/* Ensure current win screen image fixed up */
X	e_gosetcur(top ? cur_win->w_topldot
X			: scr[cur_win->w_pos + cur_win->w_ht - 1]->sl_boff);
X
X	redp(RD_MOVE);		/* Should only be cursor adjustment */
X}
X#endif /*FX_MVW%%%*/
X
X/* Given a line and a position in that line, return the xpos.
X * NOTE CAREFULLY that when line extends over several screen lines,
X * the value returned is the screen X position even though it
X * may be some lines down from the start of the logical line!
X * Also note this won't work very well if tabs exist on the extra
X * lines.  This rtn should not be used for cursor positioning.
X * Also note: d_ncols() will never return -1 meaning EOL because the
X * setup guarantees there is no EOL within the range checked.
X */
Xd_curind()	/* Find current indentation */
X{	indtion(e_dot());
X}
Xindtion(lin)
Xchroff lin;
X{	register int i, col;
X	chroff savdot;
X	chroff nchars;
X
X	savdot = e_dot();		/* Save current position */
X	e_go(lin);			/* Go to line caller wants */
X	e_gobol();			/* Go to its beginning */
X	col = 0;			/* Start at left margin */
X	if((nchars = lin - e_dot()) > 0)
X	    do {
X		if(nchars < (i = scr_wd0))
X			i = nchars;
X		if((col = d_ncols(i, col)) < 0)	/* Hit edge of screen? */
X			col = 0;		/* Reset to left margin */
X	    } while((nchars -= i) > 0);
X	e_go(savdot);			/* Restore current position */
X	return(col);
X}
X
X/* ININDEX - How many positions in lin must we go to get to xpos?
X * Returns -1 if can't be done.  Assumes "lin" is at beginning of a line!
X */
X
Xinindex (lin, xpos)
Xchroff lin;
Xint   xpos;
X{	register int col, x;
X	chroff savdot;
X	char tmp[MAXLINE+MAXCHAR];
X	extern int sctreol;		/* From EEDISP */
X
X	if((x = xpos) <= 0) return(0);
X	if(x >= MAXLINE) return(-1);	/* ?!? */
X	col = 0;
X	savdot = e_dot();
X	e_go(lin);			/* Assumes this is start of line */
X	col = sctrin(tmp, x, 0);	/* Translate from sb_getc input */
X	if((col - x) >= 0)		/* Exact count-out or past it? */
X	  {	x = e_dot() - lin;	/* Yup, win. */
X		if (sctreol > 0)	/* Did we hit (and include) EOL? */
X#if FX_EOLMODE			/* If so, back up over the EOL. */
X			x -= eolcrlf(cur_buf) ? 2 : 1;
X#else
X			--x;
X#endif
X	  }
X	else x = -1;			/* Nope, EOL or EOF hit too soon. */
X	e_go(savdot);
X	return(x);
X}
X
X/*
X * D_ ROUTINES - display-relative functions.  Similar to E_, but
X *	a "line" is defined as one line of the screen rather than
X *	as a logical text line.  Also, for efficiency reasons
X *	arguments are given saying how many lines to hack.
X */
X
Xd_gopl() { return(d_goloff(-1)); }
Xd_gonl() { return(d_goloff( 1)); }
X
X/* D_GOLOFF(i) - Go to beginning of a display line
X * D_FGOLOFF(i) - ditto, but assumes screen image of window already fixed up.
X *	i - # of lines offset.  Negative moves up, positive down.
X *		Zero arg goes to beginning of current display line.
X *	Side effects: screen image of window is fixed up at
X *	start of routine, but is NOT updated by the move to new location.
X */
Xd_goloff(cnt)
Xint cnt;
X{	d_fixcur();
X	d_fgoloff(cnt);		/* Now can invoke fixed-up fast version */
X}
Xd_fgoloff(cnt)
Xregister int cnt;
X{	register int y;
X	struct scr_line l;
X	char line[MAXLINE+MAXCHAR];
X	int top, bot;
X
X	/* Find current position in window, since can save time
X	 * by using stuff already in fixed-up screen image.
X	 */
X	if((y = d_line(e_dot())) < 0)		/* Get current Y position */
X	  {
X		errbarf("Dot out of window");
X		y = 0;
X	  }
X	top = cur_win->w_pos;		/* 1st line of window */
X	bot = top + cur_win->w_ht;	/* 1st line not in window */
X	l.sl_boff = scr[y]->sl_boff;
X	l.sl_nlin = &line[0];
X	l.sl_cont = 0;
X
X	if(cnt > 0) goto down;
X	
X	/* Go upwards.  This is hairy because we want to be clever about
X	 * huge logical lines -- avoid going all the way back to BOL.
X	 */
X	if((y+cnt) >= top)	/* Fits? */
X		goto onscr;	/* Hurray, hack it! */
X	cnt += y - top;		/* Sigh, find # lines to skip */
X	y = top;
X	l.sl_boff = scr[y]->sl_boff;
X	e_go(l.sl_boff);
X
X	/* Okay, here's the hairy part.  Must go backwards from top
X	 * line; if no EOL within scr_wid*cnt chars, then simply assume one is
X	 * seen.
X	 */
X	cnt = -cnt;
X	d_backup(cnt);
X	return;		/* Really should re-adjust stuff, but... */
X
X	/* Go downwards.  Not too bad... */
Xdown:
X	if((y+cnt) <= bot)	/* Fits? */
X		goto onscr;	/* Hurray, hack it! */
X	cnt -= bot - y;		/* Sigh, find # lines can skip */
X	y = bot - 1;
X	l.sl_boff = scr[y]->sl_boff + scr[y]->sl_len;
X	if(y > top
X	  && (l.sl_cont = scr[y-1]->sl_cont))
X		l.sl_line = scr[y-1]->sl_line;
X	e_go(l.sl_boff);
X
X	do {
X		fix_line(&l,&l);
X	  } while(--cnt > 0 && l.sl_len);
X	return;
X
Xonscr:	if((y += cnt) >= bot)
X	  {	--y;
X		e_go(scr[y]->sl_boff + scr[y]->sl_len);
X	  }
X	else e_go(scr[y]->sl_boff);
X}
X
X/* D_FIXCUR() - Ensure current window is fixed up, with
X *	current location (not necessarily cur_dot)!
X * Ensure cur_dot reflects real loc so that fix_wind will work,
X * and always call fix_wind to ensure that screen image vars
X * are set properly.  Note any active redisplay flags must be carried
X * on into window redisplay flags, so fix_wind will notice them.
X */
Xd_fixcur()
X{	register struct window *w;
X	chroff savedot;
X
X	w = cur_win;
X	savedot = cur_dot;
X	e_setcur();
X	w->w_redp |= rd_type&RDS_WINFLGS;
X	fix_wind(w);		/* Always ensure window is set up! */
X	redp(w->w_redp);	/* Add back new flags */
X	rd_type &= ~RDS_DOFIX;	/* and flush fix-invoking ones */
X	cur_dot = savedot;	/* Restore cur_dot, no longer hacked. */
X}
X
Xd_backup(nlin)		/* Try to back up by nlin screen lines */
Xint nlin;
X{	register int cnt, n, c;
X	int eolstop;
X
X	if((cnt = nlin+1) <= 0) return;
X	c = 0;
X	do
X	  {	n = scr_wid;
X		eolstop = 0;		/* Not yet stopped at EOL */
X		do {	if((c = e_rgetc()) == EOF)
X				return;
X			if(c == LF)
X			  {
X#if FX_EOLMODE
X				if(eolcrlf(cur_buf))
X				  {	if((c = e_rgetc()) == CR)
X					  {	eolstop++;
X						break;
X					  }
X					if(c != EOF)
X						e_getc();
X				  }
X				else
X#endif
X				  {	eolstop++;
X					break;
X				  }
X			  }
X		  } while(--n);
X	  } while(--cnt);
X	if(eolstop)
X	  {
X#if FX_EOLMODE
X		if(eolcrlf(cur_buf)) e_getc();	/* Skip back over CR */
X#endif
X		e_getc();		/* Skip back over LF */
X	  }
X
X	/* At this point, dot is guaranteed to be less than goal,
X	 * which is the important thing for fix_wind, which can handle
X	 * things okay if dot is off bottom of window.
X	 */
X	return(1);		/* Say always test result */
X}
/
echo x - eefdef.h
sed '/^X/s///' > eefdef.h << '/'
X/* .H Function Definition file, generated by ELLEC */
X/*   0 */ EFUNHOLE /* Always undefined */
X/*   1 */ EFUN( f_insself   , "f_insself"   , "Insert Self")
X/*   2 */ EFUN( f_quotins   , "f_quotins"   , "Quoted Insert")
X/*   3 */ EFUN( f_crlf      , "f_crlf"      , "CRLF")
X/*   4 */ EFUN( f_fchar     , "f_fchar"     , "Forward Character")
X/*   5 */ EFUN( f_bchar     , "f_bchar"     , "Backward Character")
X/*   6 */ EFUN( f_dchar     , "f_dchar"     , "Delete Character")
X/*   7 */ EFUN( f_bdchar    , "f_bdchar"    , "Backward Delete Character")
X/*   8 */ EFUN( f_delspc    , "f_delspc"    , "Delete Horizontal Space")
X/*   9 */ EFUN( f_tchars    , "f_tchars"    , "Transpose Characters")
X/*  10 */ EFUN( f_fword     , "f_fword"     , "Forward Word")
X/*  11 */ EFUN( f_bword     , "f_bword"     , "Backward Word")
X/*  12 */ EFUN( f_kword     , "f_kword"     , "Kill Word")
X/*  13 */ EFUN( f_bkword    , "f_bkword"    , "Backward Kill Word")
X/*  14 */ EFUN( f_twords    , "f_twords"    , "Transpose Words")
X/*  15 */ EFUN( f_ucword    , "f_ucword"    , "Uppercase Word")
X/*  16 */ EFUN( f_lcword    , "f_lcword"    , "Lowercase Word")
X/*  17 */ EFUN( f_uciword   , "f_uciword"   , "Uppercase Initial")
X/*  18 */ EFUNHOLE
X/*  19 */ EFUNHOLE
X/*  20 */ EFUN( f_begline   , "f_begline"   , "Beginning of Line")
X/*  21 */ EFUN( f_endline   , "f_endline"   , "End of Line")
X/*  22 */ EFUN( f_nxtline   , "f_nxtline"   , "Next Line")
X/*  23 */ EFUN( f_prvline   , "f_prvline"   , "Previous Line")
X/*  24 */ EFUN( f_dnrline   , "f_dnrline"   , "Down Real Line")
X/*  25 */ EFUN( f_uprline   , "f_uprline"   , "Up Real Line")
X/*  26 */ EFUN( f_oline     , "f_oline"     , "Open Line")
X/*  27 */ EFUN( f_delblines , "f_delblines" , "Delete Blank Lines")
X/*  28 */ EFUN( f_kline     , "f_kline"     , "Kill Line")
X/*  29 */ EFUN( f_bkline    , "f_bkline"    , "Backward Kill Line")
X/*  30 */ EFUN( f_goline    , "f_goline"    , "Goto Line")
X/*  31 */ EFUNHOLE
X/*  32 */ EFUNHOLE
X/*  33 */ EFUNHOLE
X/*  34 */ EFUNHOLE
X/*  35 */ EFUN( f_setmark   , "f_setmark"   , "Set/Pop Mark")
X/*  36 */ EFUN( f_exchmark  , "f_exchmark"  , "Exchange Point and Mark")
X/*  37 */ EFUN( f_kregion   , "f_kregion"   , "Kill Region")
X/*  38 */ EFUN( f_copreg    , "f_copreg"    , "Copy Region")
X/*  39 */ EFUN( f_ucreg     , "f_ucreg"     , "Uppercase Region")
X/*  40 */ EFUN( f_lcreg     , "f_lcreg"     , "Lowercase Region")
X/*  41 */ EFUN( f_fillreg   , "f_fillreg"   , "Fill Region")
X/*  42 */ EFUNHOLE
X/*  43 */ EFUNHOLE
X/*  44 */ EFUNHOLE
X/*  45 */ EFUN( f_fpara     , "f_fpara"     , "Forward Paragraph")
X/*  46 */ EFUN( f_bpara     , "f_bpara"     , "Backward Paragraph")
X/*  47 */ EFUN( f_mrkpara   , "f_mrkpara"   , "Mark Paragraph")
X/*  48 */ EFUN( f_fillpara  , "f_fillpara"  , "Fill Paragraph")
X/*  49 */ EFUNHOLE
X/*  50 */ EFUN( f_selbuffer , "f_selbuffer" , "Select Buffer")
X/*  51 */ EFUN( f_selxbuffer, "f_selxbuffer", "Select Existing Buffer")
X/*  52 */ EFUN( f_kbuffer   , "f_kbuffer"   , "Kill Buffer")
X/*  53 */ EFUN( f_listbufs  , "f_listbufs"  , "List Buffers")
X/*  54 */ EFUN( f_bufnotmod , "f_bufnotmod" , "Buffer Not Modified")
X/*  55 */ EFUN( f_eolmode   , "f_eolmode"   , "EOL CRLF Mode")
X/*  56 */ EFUN( f_gobeg     , "f_gobeg"     , "Goto Beginning")
X/*  57 */ EFUN( f_goend     , "f_goend"     , "Goto End")
X/*  58 */ EFUN( f_whatpage  , "f_whatpage"  , "What Page")
X/*  59 */ EFUNHOLE
X/*  60 */ EFUN( f_ffile     , "f_ffile"     , "Find File")
X/*  61 */ EFUN( f_rfile     , "f_rfile"     , "Read File")
X/*  62 */ EFUN( f_vfile     , "f_vfile"     , "Visit File")
X/*  63 */ EFUN( f_ifile     , "f_ifile"     , "Insert File")
X/*  64 */ EFUN( f_sfile     , "f_sfile"     , "Save File")
X/*  65 */ EFUN( f_savefiles , "f_savefiles" , "Save All Files")
X/*  66 */ EFUN( f_wfile     , "f_wfile"     , "Write File")
X/*  67 */ EFUN( f_wreg      , "f_wreg"      , "Write Region")
X/*  68 */ EFUN( f_wlastkill , "f_wlastkill" , "Write Last Kill")
X/*  69 */ EFUNHOLE
X/*  70 */ EFUN( f_2winds    , "f_2winds"    , "Two Windows")
X/*  71 */ EFUN( f_1wind     , "f_1wind"     , "One Window")
X/*  72 */ EFUN( f_othwind   , "f_othwind"   , "Other Window")
X/*  73 */ EFUN( f_growind   , "f_growind"   , "Grow Window")
X/*  74 */ EFUN( f_shrinkwind, "f_shrinkwind", "Shrink Window")
X/*  75 */ EFUN( f_delwind   , "f_delwind"   , "Delete Window")
X/*  76 */ EFUN( f_sowind    , "f_sowind"    , "Standout Window")
X/*  77 */ EFUN( f_2modewinds, "f_2modewinds", "Two Mode Windows")
X/*  78 */ EFUN( f_newwin    , "f_newwin"    , "New Window")
X/*  79 */ EFUN( f_nscreen   , "f_nscreen"   , "Next Screen")
X/*  80 */ EFUN( f_pscreen   , "f_pscreen"   , "Previous Screen")
X/*  81 */ EFUNHOLE
X/*  82 */ EFUNHOLE
X/*  83 */ EFUN( f_scupwind  , "f_scupwind"  , "Scroll Window Up")
X/*  84 */ EFUN( f_scdnwind  , "f_scdnwind"  , "Scroll Window Down")
X/*  85 */ EFUN( f_mvwtop    , "f_mvwtop"    , "Move to Window Top")
X/*  86 */ EFUN( f_mvwbot    , "f_mvwbot"    , "Move to Window Bottom")
X/*  87 */ EFUNHOLE
X/*  88 */ EFUNHOLE
X/*  89 */ EFUNHOLE
X/*  90 */ EFUN( f_setprof   , "f_setprof"   , "Set Profile")
X/*  91 */ EFUN( f_pfxmeta   , "f_pfxmeta"   , "Prefix Meta")
X/*  92 */ EFUN( f_pfxext    , "f_pfxext"    , "Prefix Extend")
X/*  93 */ EFUN( f_uarg      , "f_uarg"      , "Universal Arg")
X/*  94 */ EFUN( f_negarg    , "f_negarg"    , "Negative Argument")
X/*  95 */ EFUN( f_argdig    , "f_argdig"    , "Argument Digit")
X/*  96 */ EFUN( f_vtbuttons , "f_vtbuttons" , "VT100 Button Hack")
X/*  97 */ EFUN( f_describe  , "f_describe"  , "Describe")
X/*  98 */ EFUNHOLE
X/*  99 */ EFUNHOLE
X/* 100 */ EFUN( f_skmac     , "f_skmac"     , "Start Kbd Macro")
X/* 101 */ EFUN( f_ekmac     , "f_ekmac"     , "End Kbd Macro")
X/* 102 */ EFUN( f_xkmac     , "f_xkmac"     , "Execute Kbd Macro")
X/* 103 */ EFUN( f_vkmac     , "f_vkmac"     , "View Kbd Macro")
X/* 104 */ EFUNHOLE
X/* 105 */ EFUN( f_unkill    , "f_unkill"    , "Un-kill")
X/* 106 */ EFUN( f_unkpop    , "f_unkpop"    , "Un-kill Pop")
X/* 107 */ EFUN( f_appnkill  , "f_appnkill"  , "Append Next Kill")
X/* 108 */ EFUNHOLE
X/* 109 */ EFUNHOLE
X/* 110 */ EFUN( f_srch      , "f_srch"      , "String Search")
X/* 111 */ EFUN( f_rsrch     , "f_rsrch"     , "Reverse String Search")
X/* 112 */ EFUN( f_isrch     , "f_isrch"     , "Incremental Search")
X/* 113 */ EFUN( f_risrch    , "f_risrch"    , "Reverse Search")
X/* 114 */ EFUN( f_repstr    , "f_repstr"    , "Replace String")
X/* 115 */ EFUN( f_querep    , "f_querep"    , "Query Replace")
X/* 116 */ EFUN( f_repline   , "f_repline"   , "Replace in Line")
X/* 117 */ EFUN( f_sfcol     , "f_sfcol"     , "Set Fill Column")
X/* 118 */ EFUN( f_sfpref    , "f_sfpref"    , "Set Fill Prefix")
X/* 119 */ EFUN( f_fillmode  , "f_fillmode"  , "Auto Fill Mode")
X/* 120 */ EFUNHOLE
X/* 121 */ EFUN( f_indatm    , "f_indatm"    , "Indent According to Mode")
X/* 122 */ EFUN( f_indnl     , "f_indnl"     , "Indent New Line")
X/* 123 */ EFUN( f_backind   , "f_backind"   , "Back to Indentation")
X/* 124 */ EFUN( f_indcomm   , "f_indcomm"   , "Indent for Comment")
X/* 125 */ EFUN( f_indrel    , "f_indrel"    , "Indent Relative")
X/* 126 */ EFUNHOLE
X/* 127 */ EFUNHOLE
X/* 128 */ EFUNHOLE
X/* 129 */ EFUNHOLE
X/* 130 */ EFUN( f_pshinf    , "f_pshinf"    , "Push to Inferior")
X/* 131 */ EFUN( f_retsup    , "f_retsup"    , "Return to Superior")
X/* 132 */ EFUN( f_wfexit    , "f_wfexit"    , "Write File Exit")
X/* 133 */ EFUNHOLE
X/* 134 */ EFUNHOLE
X/* 135 */ EFUNHOLE
X/* 136 */ EFUNHOLE
X/* 137 */ EFUNHOLE
X/* 138 */ EFUNHOLE
X/* 139 */ EFUNHOLE
X/* 140 */ EFUN( f_bkpt      , "f_bkpt"      , "Hit Breakpoint")
X/* 141 */ EFUN( f_debug     , "f_debug"     , "Debug Mode")
X/* 142 */ EFUNHOLE
X/* 143 */ EFUNHOLE
X/* 144 */ EFUNHOLE
X/* 145 */ EFUNHOLE
X/* 146 */ EFUNHOLE
X/* 147 */ EFUNHOLE
X/* 148 */ EFUNHOLE
X/* 149 */ EFUNHOLE
X/* 150 */ EFUNHOLE
X/* 151 */ EFUNHOLE
X/* 152 */ EFUNHOLE
X/* 153 */ EFUNHOLE
X/* 154 */ EFUNHOLE
X/* 155 */ EFUNHOLE
X/* 156 */ EFUNHOLE
X/* 157 */ EFUNHOLE
/
echo x - eefed.c
sed '/^X/s///' > eefed.c << '/'
X/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*	EEFED - ED-type functions
X */
X#include "elle.h"
X
X/*
X * ED_INSERT -- Insert character given as argument.
X */
X
Xed_insert(c)
Xint c;
X{	register SBBUF *sb;
X
X	sb = (SBBUF *) cur_buf;		/* For a little speed */
X	sb_putc(sb,c);		/* Insert the char */
X	cur_dot++;		/* Advance dot */
X	buf_tmod((chroff)-1);	/* Mark buffer modified, for redisplay etc. */
X				/* Perhaps later use specialized routine? */
X}
X
Xed_insn(ch, cnt)
Xint ch, cnt;
X{	register int i;
X	if((i = cnt) > 0)
X		do { ed_insert(ch);
X		  } while(--i);
X}
X
Xed_crins()
X{
X#if FX_EOLMODE
X	if (eolcrlf(cur_buf))	/* If EOL is made of CR-LF */
X		ed_insert(CR);	/* then first insert CR, then drop down to */
X#endif
X	ed_insert(LF);		/* Insert LF */
X}
X
X
Xed_sins (s)			       /* insert this string */
Xregister char *s;
X{	register c;
X	while (c = *s++)
X		ed_insert (c);
X}
X
Xed_nsins (s, i)		/* Insert string of N chars */
Xregister char *s;
Xregister int i;
X{	if(i > 0)
X		do { ed_insert(*s++); } while(--i);
X}
X
X/* ED_INDTO(col) - Indent to specified column.
X**	Finds current cursor position, and inserts tabs and spaces
X** so cursor ends up at column goal.  Does nothing if already at or past
X** specified column.
X*/
X
Xed_indto(goal)
Xregister int goal;
X{	register int ng;
X
X	ng = goal & ~07;		/* Get distance to tab stop */
X	ed_insn(TAB, ((ng - (d_curind() & ~07)) >> 3));
X	ed_insn(SP, goal-ng);
X}
X
X/* Oddball routine - Set cur_dot to actual I/O location and
X * tell display that cursor probably moved.  This is not really a
X * function of itself; it provides support for real functions.
X */
Xed_setcur()
X{	e_setcur();	/* Set cur_dot */
X	redp(RD_MOVE);	/* Alert redisplay to check cursor loc */
X}
X
X/* Go to given dot */
Xed_go (dot)
Xchroff dot;
X{	e_go(dot);
X	ed_setcur();
X}
X
X/* Go to given offset from current location */
Xed_goff(off)
Xchroff off;
X{	e_goff(off);
X	ed_setcur();
X}
X
X/* Go to given INTEGER offset from current location */
Xed_igoff(ioff)
Xint ioff;
X{	e_igoff(ioff);
X	ed_setcur();
X}
X
X/* Reset (delete all of) Buffer
X * Should buffer be marked modified or not? Currently isn't.
X */
Xed_reset()
X{	if(e_blen() == 0)
X		return;		/* Already empty */
X	e_reset();
X	cur_dot = 0;
X	cur_win->w_topldot = 0;	/* Is this necessary? */
X#if IMAGEN
X	redp(RD_WINRES|RD_REDO);
X#else
X	redp(RD_WINRES);	/* This window needs complete update */
X#endif /*-IMAGEN*/
X
X/*	buf_mod(); */		/* Mark modified ?? */
X/*	mark_p = 0; */		/* Say no mark set ?? */
X}
X
Xed_deln(off)
Xchroff off;
X{	chroff dot;
X	dot = e_dot();
X	e_goff(off);	
X	ed_delete(e_dot(), dot);
X}
X
X/* ED_DELETE(dot1,dot2) -  Delete all characters between the two
X *	positions indicated by dot1 and dot2.  Their order does not
X *	matter; cur_dot and mark_dot are updated as necessary.
X */
Xed_delete(dot1,dot2)
Xchroff dot1,dot2;
X{	chroff tmpdot, savdot;
X
X	if(dot1 > dot2)
X	  {	tmpdot = dot1;
X		dot1 = dot2;
X		dot2 = tmpdot;
X	  }
X	e_go(dot1);
X	tmpdot = dot2-dot1;
X	sb_deln((SBBUF *)cur_buf,tmpdot);
X
X	savdot = cur_dot;		/* Save cur_dot value */
X	cur_dot = dot1;			/* so can set up for */
X	buf_tmod((chroff)0);		/* call to update disp-change vars */
X	cur_dot = savdot;
X
X	if(cur_dot >= dot2)
X		cur_dot -= tmpdot;
X	else if(cur_dot > dot1)
X		cur_dot = dot1;
X	if(mark_dot >= dot2)
X		mark_dot -= tmpdot;
X	else if(mark_dot > dot1)
X		mark_dot = dot1;
X	e_gocur();
X}
X
X/* ED_KILL(dot1,dot2) - Kill (save and delete) text between two places in
X *	the buffer.
X * We assume we are deleting from dot1 to dot2, thus if dot1 > dot2
X * then backwards deletion is implied, and the saved text is prefixed
X * (instead of appended) to any previously killed text.
X */
Xed_kill(dot1,dot2)
Xchroff dot1,dot2;
X{	register SBSTR *sd, *sdo;
X	SBSTR *e_copyn();
X
X	e_go(dot1);
X	sd = e_copyn(dot2-dot1);
X	if(sd == 0) return;
X	if(last_cmd == KILLCMD && (sdo = kill_ring[kill_ptr]))
X	  {	if(dot1 > dot2)	/* Prefix new killed stuff onto old stuff */
X		  {	sbs_app(sd,sdo);
X			kill_ring[kill_ptr] = sd;
X		  }
X		else		/* Append new stuff to old stuff */
X			sbs_app(sdo,sd);
X	  }
X	else kill_push(sd);
X	ed_delete(dot1,dot2);
X}
X
Xkill_push(sdp)
XSBSTR *sdp;
X{	register SBSTR *sd;
X
X	if(++kill_ptr >= KILL_LEN) kill_ptr = 0;
X	if(sd = kill_ring[kill_ptr])
X		sbs_del(sd);
X	kill_ring[kill_ptr] = sdp;
X}
X
X
X#define isupper(c) (('A' <= c) && (c <= 'Z'))
X#define islower(c) (('a' <= c) && (c <= 'z'))
X#define toupper(c) (c + ('A' - 'a'))
X#define tolower(c) (c + ('a' - 'A'))
X
X#if FX_UCWORD||FX_LCWORD||FX_UCIWORD||FX_UCREG||FX_LCREG
X
X/* ED_CASE(dot1,dot2,downp) - Change the case within a region.
X *	downp = 0 for uppercase, 1 for lowercase, 2 for capitalize.
X */
Xed_case(dot1, dot2, downp)
Xchroff dot1, dot2;
Xint downp;
X{	chroff dcnt;
X	register int c, a, z;
X	int modflg;
X
X	modflg = 0;
X	if((dcnt = dot2 - dot1) < 0)
X	  {	dcnt = dot1;
X		dot1 = dot2;
X		dot2 = dcnt;
X		dcnt -= dot1;
X	  }
X	e_go(dot1);
X
X	if(downp==2)
X	  {	a = 0;	/* 0 looking for wd, 1 in word */
X		while(--dcnt >= 0)
X		  {	if(delimp(c = e_getc()))	/* Char in wd? */
X			  {	a = 0;			/* No */
X				continue;
X			  }
X			 if(a)		/* If already inside word */
X			  {	if(isupper(c))
X					c = tolower(c);
X				else continue;
X			  }
X			else	/* If encountered start of word */
X			  {	a = 1;
X				if(islower(c))
X					c = toupper(c);
X				else continue;
X			  }
X			e_backc();
X			e_ovwc(c);
X			modflg++;
X		  }
X		goto casdon;
X	  }
X	if(downp==0)
X	  {	a = 'a';		/* Convert to lower case */
X		z = 'z';
X		downp = -040;
X	  }
X	else
X	  {	a = 'A';		/* Convert to upper case */
X		z = 'Z';
X		downp = 040;
X	  }
X	while(--dcnt >= 0)
X	  {	if(a <= (c = e_getc()) && c <= z)
X		  {	e_backc();
X			e_ovwc(c+downp);
X			modflg++;
X		  }
X	  }
X
Xcasdon:	dot2 = cur_dot;			/* Save dot */
X	e_setcur();			/* Set up for modification range chk*/
X	if(modflg)
X		buf_tmat(dot1);		/* Stuff munged from there to here */
X	ed_go(dot2);
X}
X#endif /* any ed_case caller */
X
X
X/* UPCASE(c) - Return upper-case version of character */
Xupcase(ch)
Xint ch;
X{	register int c;
X	c = ch&0177;
X	return(islower(c) ? toupper(c) : c);
X}
X
/
echo x - eefidx.h
sed '/^X/s///' > eefidx.h << '/'
X/* .H Function Index Definition file, generated by ELLEC */
X/* FN_ defines Function Numbers (indices) for all known functions */
X/* FX_ defines Function eXistence in this ELLE configuration */
X#define FN_INSSELF          1 /* Insert Self */
X#define FX_INSSELF          1
X#define FN_QUOTINS          2 /* Quoted Insert */
X#define FX_QUOTINS          2
X#define FN_CRLF             3 /* CRLF */
X#define FX_CRLF             3
X#define FN_FCHAR            4 /* Forward Character */
X#define FX_FCHAR            4
X#define FN_BCHAR            5 /* Backward Character */
X#define FX_BCHAR            5
X#define FN_DCHAR            6 /* Delete Character */
X#define FX_DCHAR            6
X#define FN_BDCHAR           7 /* Backward Delete Character */
X#define FX_BDCHAR           7
X#define FN_DELSPC           8 /* Delete Horizontal Space */
X#define FX_DELSPC           8
X#define FN_TCHARS           9 /* Transpose Characters */
X#define FX_TCHARS           9
X#define FN_FWORD           10 /* Forward Word */
X#define FX_FWORD           10
X#define FN_BWORD           11 /* Backward Word */
X#define FX_BWORD           11
X#define FN_KWORD           12 /* Kill Word */
X#define FX_KWORD           12
X#define FN_BKWORD          13 /* Backward Kill Word */
X#define FX_BKWORD          13
X#define FN_TWORDS          14 /* Transpose Words */
X#define FX_TWORDS          14
X#define FN_UCWORD          15 /* Uppercase Word */
X#define FX_UCWORD          15
X#define FN_LCWORD          16 /* Lowercase Word */
X#define FX_LCWORD          16
X#define FN_UCIWORD         17 /* Uppercase Initial */
X#define FX_UCIWORD         17
X#define FN_BEGLINE         20 /* Beginning of Line */
X#define FX_BEGLINE         20
X#define FN_ENDLINE         21 /* End of Line */
X#define FX_ENDLINE         21
X#define FN_NXTLINE         22 /* Next Line */
X#define FX_NXTLINE         22
X#define FN_PRVLINE         23 /* Previous Line */
X#define FX_PRVLINE         23
X#define FN_DNRLINE         24 /* Down Real Line */
X#define FX_DNRLINE         24
X#define FN_UPRLINE         25 /* Up Real Line */
X#define FX_UPRLINE         25
X#define FN_OLINE           26 /* Open Line */
X#define FX_OLINE           26
X#define FN_DELBLINES       27 /* Delete Blank Lines */
X#define FX_DELBLINES       27
X#define FN_KLINE           28 /* Kill Line */
X#define FX_KLINE           28
X#define FN_BKLINE          29 /* Backward Kill Line */
X#define FX_BKLINE          29
X#define FN_GOLINE          30 /* Goto Line */
X#define FX_GOLINE          30
X#define FN_SETMARK         35 /* Set/Pop Mark */
X#define FX_SETMARK         35
X#define FN_EXCHMARK        36 /* Exchange Point and Mark */
X#define FX_EXCHMARK        36
X#define FN_KREGION         37 /* Kill Region */
X#define FX_KREGION         37
X#define FN_COPREG          38 /* Copy Region */
X#define FX_COPREG          38
X#define FN_UCREG           39 /* Uppercase Region */
X#define FX_UCREG           39
X#define FN_LCREG           40 /* Lowercase Region */
X#define FX_LCREG           40
X#define FN_FILLREG         41 /* Fill Region */
X#define FX_FILLREG         41
X#define FN_FPARA           45 /* Forward Paragraph */
X#define FX_FPARA           45
X#define FN_BPARA           46 /* Backward Paragraph */
X#define FX_BPARA           46
X#define FN_MRKPARA         47 /* Mark Paragraph */
X#define FX_MRKPARA         47
X#define FN_FILLPARA        48 /* Fill Paragraph */
X#define FX_FILLPARA        48
X#define FN_SELBUFFER       50 /* Select Buffer */
X#define FX_SELBUFFER       50
X#define FN_SELXBUFFER      51 /* Select Existing Buffer */
X#define FX_SELXBUFFER      51
X#define FN_KBUFFER         52 /* Kill Buffer */
X#define FX_KBUFFER         52
X#define FN_LISTBUFS        53 /* List Buffers */
X#define FX_LISTBUFS        53
X#define FN_BUFNOTMOD       54 /* Buffer Not Modified */
X#define FX_BUFNOTMOD       54
X#define FN_EOLMODE         55 /* EOL CRLF Mode */
X#define FX_EOLMODE         55
X#define FN_GOBEG           56 /* Goto Beginning */
X#define FX_GOBEG           56
X#define FN_GOEND           57 /* Goto End */
X#define FX_GOEND           57
X#define FN_WHATPAGE        58 /* What Page */
X#define FX_WHATPAGE        58
X#define FN_FFILE           60 /* Find File */
X#define FX_FFILE           60
X#define FN_RFILE           61 /* Read File */
X#define FX_RFILE           61
X#define FN_VFILE           62 /* Visit File */
X#define FX_VFILE           62
X#define FN_IFILE           63 /* Insert File */
X#define FX_IFILE           63
X#define FN_SFILE           64 /* Save File */
X#define FX_SFILE           64
X#define FN_SAVEFILES       65 /* Save All Files */
X#define FX_SAVEFILES       65
X#define FN_WFILE           66 /* Write File */
X#define FX_WFILE           66
X#define FN_WREG            67 /* Write Region */
X#define FX_WREG            67
X#define FN_WLASTKILL       68 /* Write Last Kill */
X#define FX_WLASTKILL       68
X#define FN_2WINDS          70 /* Two Windows */
X#define FX_2WINDS          70
X#define FN_1WIND           71 /* One Window */
X#define FX_1WIND           71
X#define FN_OTHWIND         72 /* Other Window */
X#define FX_OTHWIND         72
X#define FN_GROWIND         73 /* Grow Window */
X#define FX_GROWIND         73
X#define FN_SHRINKWIND      74 /* Shrink Window */
X#define FX_SHRINKWIND      74
X#define FN_DELWIND         75 /* Delete Window */
X#define FX_DELWIND         75
X#define FN_SOWIND          76 /* Standout Window */
X#define FX_SOWIND          76
X#define FN_2MODEWINDS      77 /* Two Mode Windows */
X#define FX_2MODEWINDS      77
X#define FN_NEWWIN          78 /* New Window */
X#define FX_NEWWIN          78
X#define FN_NSCREEN         79 /* Next Screen */
X#define FX_NSCREEN         79
X#define FN_PSCREEN         80 /* Previous Screen */
X#define FX_PSCREEN         80
X#define FN_OTHNSCREEN      81 /* Other New Screen */
X#define FX_OTHNSCREEN       0
X#define FN_LWINDBORD       82 /* Line to Window Border */
X#define FX_LWINDBORD        0
X#define FN_SCUPWIND        83 /* Scroll Window Up */
X#define FX_SCUPWIND        83
X#define FN_SCDNWIND        84 /* Scroll Window Down */
X#define FX_SCDNWIND        84
X#define FN_MVWTOP          85 /* Move to Window Top */
X#define FX_MVWTOP          85
X#define FN_MVWBOT          86 /* Move to Window Bottom */
X#define FX_MVWBOT          86
X#define FN_SETPROF         90 /* Set Profile */
X#define FX_SETPROF         90
X#define FN_PFXMETA         91 /* Prefix Meta */
X#define FX_PFXMETA         91
X#define FN_PFXEXT          92 /* Prefix Extend */
X#define FX_PFXEXT          92
X#define FN_UARG            93 /* Universal Arg */
X#define FX_UARG            93
X#define FN_NEGARG          94 /* Negative Argument */
X#define FX_NEGARG          94
X#define FN_ARGDIG          95 /* Argument Digit */
X#define FX_ARGDIG          95
X#define FN_VTBUTTONS       96 /* VT100 Button Hack */
X#define FX_VTBUTTONS       96
X#define FN_DESCRIBE        97 /* Describe */
X#define FX_DESCRIBE        97
X#define FN_SKMAC          100 /* Start Kbd Macro */
X#define FX_SKMAC          100
X#define FN_EKMAC          101 /* End Kbd Macro */
X#define FX_EKMAC          101
X#define FN_XKMAC          102 /* Execute Kbd Macro */
X#define FX_XKMAC          102
X#define FN_VKMAC          103 /* View Kbd Macro */
X#define FX_VKMAC          103
X#define FN_UNKILL         105 /* Un-kill */
X#define FX_UNKILL         105
X#define FN_UNKPOP         106 /* Un-kill Pop */
X#define FX_UNKPOP         106
X#define FN_APPNKILL       107 /* Append Next Kill */
X#define FX_APPNKILL       107
X#define FN_SRCH           110 /* String Search */
X#define FX_SRCH           110
X#define FN_RSRCH          111 /* Reverse String Search */
X#define FX_RSRCH          111
X#define FN_ISRCH          112 /* Incremental Search */
X#define FX_ISRCH          112
X#define FN_RISRCH         113 /* Reverse Search */
X#define FX_RISRCH         113
X#define FN_REPSTR         114 /* Replace String */
X#define FX_REPSTR         114
X#define FN_QUEREP         115 /* Query Replace */
X#define FX_QUEREP         115
X#define FN_REPLINE        116 /* Replace in Line */
X#define FX_REPLINE        116
X#define FN_SFCOL          117 /* Set Fill Column */
X#define FX_SFCOL          117
X#define FN_SFPREF         118 /* Set Fill Prefix */
X#define FX_SFPREF         118
X#define FN_FILLMODE       119 /* Auto Fill Mode */
X#define FX_FILLMODE       119
X#define FN_TEXTMODE       120 /* Text Mode */
X#define FX_TEXTMODE         0
X#define FN_INDATM         121 /* Indent According to Mode */
X#define FX_INDATM         121
X#define FN_INDNL          122 /* Indent New Line */
X#define FX_INDNL          122
X#define FN_BACKIND        123 /* Back to Indentation */
X#define FX_BACKIND        123
X#define FN_INDCOMM        124 /* Indent for Comment */
X#define FX_INDCOMM        124
X#define FN_INDREL         125 /* Indent Relative */
X#define FX_INDREL         125
X#define FN_MATCHBRACK     129 /* Match Bracket */
X#define FX_MATCHBRACK       0
X#define FN_PSHINF         130 /* Push to Inferior */
X#define FX_PSHINF         130
X#define FN_RETSUP         131 /* Return to Superior */
X#define FX_RETSUP         131
X#define FN_WFEXIT         132 /* Write File Exit */
X#define FX_WFEXIT         132
X#define FN_BKPT           140 /* Hit Breakpoint */
X#define FX_BKPT           140
X#define FN_DEBUG          141 /* Debug Mode */
X#define FX_DEBUG          141
X#define FN_XUCMD          150 /* Execute Unix Command */
X#define FX_XUCMD            0
X#define FN_MAKE           151 /* Execute Make */
X#define FX_MAKE             0
X#define FN_NXTERR         152 /* Find Next Error */
X#define FX_NXTERR           0
X#define FN_ICOXCMD        153 /* ICO Extend Command */
X#define FX_ICOXCMD          0
X#define FN_ICOTYPFNS      154 /* ICO Typeset Funs */
X#define FX_ICOTYPFNS        0
X#define FN_ICOSPIFNS      155 /* ICO Spec Input Funs */
X#define FX_ICOSPIFNS        0
X#define FN_STUFFSEL       156 /* Stuff Selection */
X#define FX_STUFFSEL         0
X#define FN_SELREGION      157 /* Select Region */
X#define FX_SELREGION        0
/
echo x - eefile.c
sed '/^X/s///' > eefile.c << '/'
X/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X	 *	products without permission of the author.
X */
X/*
X * EEFILE	File reading/writing functions
X */
X
X#include "elle.h"
X#include <stdio.h>	/* Use "standard" I/O package for writing */
X#ifndef BUFSIZ
X#define BUFSIZ BUFSIZE	/* Some places use wrong name in stdio.h */
X#endif /*-BUFSIZ*/
X#if V6
X	struct stat {
X		int st_dev;
X		int st_ino;
X		char *st_mode;
X		char st_nlink;
X		char st_uid;
X		char st_gid;
X		char st_size0;
X		char st_size;
X		int st_addr[8];
X		long st_atime;
X		long st_mtime;
X	};
X#define ENOENT (2)	/* Syscall error - no such file or dir */
X#else
X#include <errno.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#endif /*-V6*/
X
X#if TOPS20
X#include <sys/file.h>		/* Get open mode bits */
X#endif
X
Xextern char *strerror();		/* Return error string for errno */
Xextern struct buffer *make_buf(), *find_buf();
X
Xchar *fncons(), *last_fname();
X
Xint hoardfd = -1;	/* Retain a FD here to ensure we can always write */
X
X/* Flags for iwritfile() */
X#define WF_SMASK 07	/* Source Mask */
X#define WF_SBUFF  0	/*   source: Buffer */
X#define WF_SREG   1	/*   source: Region */
X#define WF_SKILL 2	/*   source: Last Kill */
X#define WF_ASK 010	/* Ask for filename to write to */
Xstatic int iwritfile();
X
X/* EFUN: "Find File" */
X/*	Ask user for a filename and do a find_file for it.
X *	If buffer exists for that filename, select that buffer.
X *	Else create a buffer for it, and read in the file if it exists.
X */
Xf_ffile()
X{	int find_file();
X#if IMAGEN
X	hack_file("Visit file: ", find_file);
X#else
X	hack_file("Find file: ", find_file);
X#endif /*-IMAGEN*/
X}
X
X/* EFUN: "Read File" */
X/*	User read_file function, asks user for a filename and reads it
X */
Xf_rfile() { u_r_file("Read file: "); }
X
X/* EFUN: "Visit File" */
X/*	Same as Read File, with different prompt.
X */
Xf_vfile() { u_r_file("Visit file: "); }
X
X
Xu_r_file(prompt)
Xchar *prompt;
X{	register char *f_name;
X	register struct buffer *b;
X
X	if((f_name = ask (prompt))==0)	/* prompt user for filename */
X		return;				/* user punted... */
X	b = cur_buf;
X	if(*f_name == '\0')
X	  {	if (b -> b_fn == 0)
X			ding("No default file name.");
X		else read_file(b -> b_fn);
X	  }
X	else read_file(f_name);
X	chkfree(f_name);
X}
X
X/* EFUN: "Insert File" */
X/*	Asks for a filename and inserts the file at current location.
X *	Point is left at beginning, and the mark at the end.
X */
Xf_ifile()
X{	int ins_file();
X	hack_file("Insert file: ", ins_file);
X}
X
X/* EFUN: "Save File" */
X/*	Save current buffer to its default file name
X */
Xf_sfile()
X{	if(cur_buf->b_flags&B_MODIFIED)
X		return(iwritfile(WF_SBUFF));	/* Write buffer, don't ask */
X	else
X	  {	saynow("(No changes need to be written)");
X		return(1);
X	  }
X}
X
X#if FX_SAVEFILES || FX_WFEXIT
X/* EFUN: "Save All Files" */
X/*  F_SAVEFILES - Offer to save all modified files.
X *	With argument, doesn't ask.
X *	Returns 0 if user aborts or if an error happened.
X */
Xf_savefiles()
X{	register struct buffer *b, *savb;
X	register int res = 1;
X	char *ans;
X
X	savb = cur_buf;
X	for (b = buf_head; res && b; b = b->b_next)
X		if ((b->b_flags & B_MODIFIED) && b->b_fn)
X		  {	if(exp_p)		/* If arg, */
X			  {	chg_buf(b);	/* just save, don't ask */
X				res = f_sfile();
X				continue;	/* Check next buffer */
X			  }
X			/* Ask user whether to save */
X			ans = ask("Buffer %s contains changes - write out? ",
X					b->b_name);
X			if(ans == 0)
X			  {	res = 0;	/* User aborted */
X				break;
X			  }
X			if (upcase(*ans) == 'Y')
X			  {	chg_buf(b);
X				res = f_sfile();	/* Save File */
X			  }
X			chkfree(ans);
X		  }
X	chg_buf(savb);
X	return(res);
X}
X#endif /*FX_SAVEFILES||FX_WFEXIT*/
X
X/* EFUN: "Write File" */
X/*	Write out the buffer to an output file.
X */
Xf_wfile()
X{	return iwritfile(WF_ASK|WF_SBUFF);
X}
X
X/* EFUN: "Write Region" */
X/*	Write region out to a file
X */
Xf_wreg()
X{	return iwritfile(WF_ASK|WF_SREG);	/* Ask, write region */
X}
X
X#if FX_WLASTKILL
X/* EFUN: "Write Last Kill" (not EMACS) */
X/*	Write current kill buffer out to a file.
X**	This is mainly for MINIX.
X*/
Xextern int kill_ptr;		/* From EEF3 */
Xextern SBSTR *kill_ring[];
X
Xf_wlastkill()
X{	return iwritfile(WF_ASK|WF_SKILL);
X}
X#endif
X
X
X/* HACK_FILE - intermediate subroutine
X */
Xhack_file(prompt, rtn)
Xchar *prompt;
Xint (*rtn)();
X{	register char *f_name;
X
X	if((f_name = ask(prompt)) == 0)
X		return;
X	if (*f_name != '\0')			/* Check for null answer */
X		(*rtn)(f_name);
X	chkfree(f_name);
X}
X
X/* FIND_FILE(f_name)
X *	If there is a buffer whose fn == f_name, select that buffer.
X *	Else create one with name of the last section of f_name and
X *	read the file into that buffer.
X */
Xfind_file(f_name)
Xregister char *f_name;
X{	register struct buffer *b;
X	register char *ans;
X	char *lastn;
X	int fd;
X
X#if IMAGEN
X	char real_name[128];		/* File name w/ expanded ~ and $ */
X	expand_file(real_name, f_name);
X	f_name = real_name;
X#endif /*IMAGEN*/
X
X	for (b = buf_head; b; b = b -> b_next)
X		if(b->b_fn && (strcmp (b -> b_fn, f_name) == 0))
X			break;
X	if (b)				/* if we found one */
X	  {	sel_buf(b);		/* go there */
X		return;			/* and we are done */
X	  }
X	if((fd = open(f_name,0)) < 0)	/* See if file exists */
X	  {	if(errno != ENOENT)	/* No, check reason */
X		  {	ferr_ropn();	/* Strange error, complain */
X			return;		/* and do nothing else. */
X		  }
X	  }
X	else close(fd);			/* Found!  Close FD, since the */
X					/* read_file rtn will re-open. */
X
X	lastn = last_fname(f_name);	/* Find buffer name */
X	b = find_buf(lastn);		/* Is there a buffer of that name? */
X	if (b && (ex_blen(b) || b->b_fn))
X	  {	ans = ask("Buffer %s contains %s, which buffer shall I use? ",
X    			b -> b_name, b->b_fn ? b->b_fn : "something");
X		if(ans == 0) return;		/* Aborted */
X		if (*ans != '\0')		/* if null answer, use b */
X			b = make_buf(ans);	/* else use ans */
X		chkfree(ans);
X	  }
X	else
X		b = make_buf(lastn);
X	sel_buf(b);
X	if(fd < 0)		/* If file doesn't exist, */
X	  {	set_fn(f_name);	/* just say "new" and set filename */
X		return;		/* and return right away. */
X	  }
X	if (read_file(f_name)==0)	/* File exists, read it in! */
X	  {	if(b->b_fn)		/* Failed... if filename, */
X		  {	chkfree(b->b_fn);	/* flush the filename. */
X			b->b_fn = 0;
X		  }
X	  }
X}
X
X/* READ_FILE(f_name)
X *	Reads file into current buffer, flushing any
X *	previous contents (if buffer modified, will ask about saving)
X *	Returns 0 if failed.
X */
Xread_file(f_name)
Xchar *f_name;
X{
X#if IMAGEN
X	struct stat s;
X	char real_name[128];		/* File name w/ expanded ~ and $ */
X#endif /*IMAGEN*/
X
X	if(!zap_buffer())	/* Flush the whole buffer */
X		return;		/* Unless user aborts */
X#if IMAGEN
X	expand_file(real_name, f_name);
X	f_name = real_name;		/* Hack, hack! */
X#endif /*IMAGEN*/
X	set_fn(f_name);
X	if (ins_file(f_name)==0)
X		return 0;
X	f_bufnotmod();		/* Say not modified now */
X#if IMAGEN
X	stat(f_name, &s);		/* Get file stat */
X	cur_buf->b_mtime = s.st_mtime;	/*  and pick out last-modified time */
X#endif /*IMAGEN*/
X	return 1;
X}
X
X/* INS_FILE(f_name)
X *	Inserts file named f_name into current buffer at current point
X *	Point is not moved; mark is set to end of inserted stuff.
X *	Returns 0 if failed, 1 if won.
X */
Xins_file (f_name)
Xchar *f_name;
X{	register int ifd;
X	register SBSTR *sd;
X	chroff insdot;			/* To check for range of mods */
X
X#if IMAGEN
X	char real_name[128];		/* File name w/ expanded ~ and $ */
X	expand_file(real_name, f_name);
X	f_name = real_name;
X#endif /*IMAGEN*/
X#if !(TOPS20)
X	if((ifd = open(f_name,0)) < 0)
X#else
X	if((ifd = open(f_name,O_RDONLY|O_UNCONVERTED)) < 0)
X#endif /*TOPS20*/
X	  {	ferr_ropn();		/* Can't open, complain */
X		return 0;		/* no redisplay */
X	  }
X	errno = 0;
X	if((sd = sb_fduse(ifd)) == 0)
X	  {	if (ifd >= SB_NFILES)
X			dingtoo(" Cannot read - too many internal files");
X		else if (errno)
X			ferr_ropn();
X		else errbarf("SB rtn cannot read file?");
X		close(ifd);
X		return 0;
X	  }
X	sb_sins(cur_buf,sd);
X	insdot = e_dot();
X	f_setmark();			/* Set mark at current ptr */
X	if(cur_dot != insdot)		/* If pointer was advanced, */
X		buf_tmat(insdot);	/* then stuff was inserted */
X	e_gocur();
X	return 1;
X}
X
Xferr_ropn() { ferr(" Cannot read"); }
Xferr_wopn() { ferr(" Cannot write"); }
Xferr(str)
Xchar *str;
X{	saytoo(str);
X	saytoo(" - ");
X	dingtoo(strerror(errno));
X}
X
X
X/* IWRITFILE - auxiliary for writing files.
X**	Returns 1 if write successful, 0 if not.
X*/
Xstatic int
Xiwritfile(flags)
Xint flags;
X{	register struct buffer *b;
X	register char *o_name;		/* output file name */
X	int styp = flags & WF_SMASK;	/* Source type, one of WF_Sxxx */
X	char *prompt;
X#ifdef STDWRITE
X	register FILE *o_file;		/* output file pointer */
X	char obuf[BUFSIZ];
X	chroff dotcnt;
X#endif /*STDWRITE*/
X	int ofd;			/* output file FD */
X	SBSTR *sd;
X	char fname[FNAMSIZ];		/* To avoid chkfree hassle */
X	char newname[FNAMSIZ];		/* for robustness */
X	char oldname[FNAMSIZ];		/* ditto */
X	int res;
X	struct stat statb;
X	int statres;
X#if IMAGEN
X	struct stat s;
X	char real_name[128];		/* File name w/ expanded ~ and $ */
X#endif /*IMAGEN*/
X	res = 1;			/* Let's keep track of success */
X
X	/* Check for existence of source, and set prompt string */
X	switch(styp)
X	  {
X		case WF_SBUFF:
X			prompt = "Write File: ";
X			break;
X		case WF_SREG:
X			if(!mark_p)
X			  {	dingtoo(" No Mark!");
X				return(0);
X			  }
X			prompt = "Write Region: ";
X			break;
X#if FX_WLASTKILL
X		case WF_SKILL:
X			if(!kill_ring[kill_ptr])
X			  {	dingtoo("No killed stuff");
X				return(0);
X			  }
X			prompt = "Write Last Kill: ";
X			break;
X#endif
X		default:			/* Internal error */
X			errbarf("bad iwritfile arg");
X			return 0;
X	  }
X
X	if (flags&WF_ASK)
X	  {	if((o_name = ask(prompt))==0)
X			return(0);		/* User punted. */
X		strcpy(&fname[0], o_name);	/* Copy filename onto stack */
X		chkfree(o_name);
X	  }
X	o_name = &fname[0];
X	b = cur_buf;
X	if (!(flags&WF_ASK) || (*o_name == '\0'))
X	  {	if (b->b_fn == 0)
X		  {	ding("No default file name.");
X			return(0);
X		  }
X		strcpy(o_name, b->b_fn);
X	  }
X
X#if IMAGEN
X	expand_file(real_name, o_name);
X	o_name = real_name;		/* Hack, hack */
X#endif /*IMAGEN*/
X
X	statres = stat(o_name,&statb);	/* Get old file's info (if any) */
X
X#if IMAGEN
X	/* Now, make sure someone hasn't written the file behind our backs */
X	if ((styp==WF_SBUFF) && !(flags&WF_ASK)
X	  && b->b_fn && stat(b->b_fn, &s) >= 0)
X		if (s.st_mtime != b->b_mtime)
X		  {	char *ans;
X			ans = ask("Since you last read \"%s\", someone has changed it.\nDo you want to write it anyway (NOT RECOMMENDED!)? ",
X				   b->b_fn);
X			if (ans == 0 || upcase(*ans) != 'Y')
X			  {
X				ding("I suggest you either read it again, or\nwrite it to a temporary file, and merge the two versions manually.");
X				if (ans) chkfree(ans);
X				return(0);
X			  }
X			if (ans) chkfree(ans);
X		  }
X#endif /*IMAGEN*/
X
X  /* Try to get around major UNIX screw of smashing files.
X   * This still isn't perfect (screws up with long filenames) but...
X   * 1. Write out to <newname>
X   * 2. Rename <name> to <oldname> (may have to delete existing <oldname>)
X   * 3. Rename <newname> to <name>.
X   */
X	fncons(oldname,ev_fno1,o_name,ev_fno2);	/* Set up "old" filename */
X	fncons(newname,ev_fnn1,o_name,ev_fnn2);	/* Set up "new" filename */
X	unlink(newname);			/* Ensure we don't clobber */
X	unhoard();				/* Now give up saved FD */
X#if !(V6)	/* Standard V6 doesn't have access call */
X	if(statres >= 0)			/* If file exists, */
X	  {	if(access(o_name, 2) != 0)	/* check for write access */
X		  {	ferr_wopn();
X			res = 0;	/* Failure */
X			goto wdone;
X		  }
X	  }
X#endif /*-V6*/
X#ifdef STDWRITE
X	if(flags&WF_ASK)
X	  {	if((o_file = fopen(newname, "w")) ==0)	/* Create new output file */
X		  {	ferr_wopn();
X			res = 0;		/* Failure */
X			goto wdone;
X		  }
X		setbuf(o_file,obuf);	/* Ensure always have buffer */
X	  }
X	else	/* New stuff */
X#endif /*STDWRITE*/
X	  {
X#if !(TOPS20)
X		if((ofd = creat(newname,ev_filmod)) < 0)
X#else
X		if((ofd = open(newname,O_WRONLY|O_UNCONVERTED)) < 0)
X#endif /*TOPS20*/
X		  {	ferr_wopn();
X			res = 0;		/* Failure */
X			goto wdone;
X		  }
X	  }
X	if (styp==WF_SBUFF)
X		set_fn(o_name);		/* Won, so set default fn for buff */
X#if IMAGEN
X	saynow("Writing ");
X	switch(styp)
X	  {	case WF_SBUFF:	saytoo(b->b_fn); break;
X		case WF_SREG:	saytoo("region"); break;
X#if FX_WLASTKILL
X		case WF_SKILL:	saytoo("last kill"); break;
X#endif
X	  }
X	sayntoo("...");
X#else
X	saynow("Writing...");
X#endif /*-IMAGEN*/
X
X#if !(TOPS20)			/* T20 does all this already */
X	if(statres >= 0)		/* Get old file's modes */
X	  {				/* Try to duplicate them */
X		/* Do chmod first since after changing owner we may not
X		** have permission to change mode, at least on V6.
X		*/
X		chmod(newname,statb.st_mode & 07777);
X#if V6
X		chown(newname, (statb.st_gid<<8)|(statb.st_uid&0377));
X#else
X		chown(newname,statb.st_uid,statb.st_gid);
X#endif /*-V6*/
X	  }
X#if V6
X	/* If no old file existed, and we are a V6 system, try to set
X	 * the modes explicitly.  On V7 we're OK because the user can
X	 * diddle "umask" to get whatever is desired.
X	 * On TOPS-20 of course everything is all peachy.
X	 */
X	else chmod(newname, ev_filmod);
X#endif /*V6*/
X#endif /*TOPS20*/
X
X
X#ifdef STDWRITE
X	if(flags&WF_ASK)
X	  {	switch(styp)
X		  {
X			case WF_SBUFF:
X				dotcnt = e_blen();
X				e_gobob();
X				break;
X			case WF_SREG:
X				if((dotcnt = mark_dot - cur_dot) < 0)
X				  {	e_goff(dotcnt);
X					dotcnt = -dotcnt;
X				  }
X				else e_gocur();
X				break;
X			/* WF_SKILL not implemented here */
X		  }
X		while(--dotcnt >= 0)
X			putc(sb_getc(((SBBUF *)b)), o_file);
X		e_gocur();
X		fflush(o_file);			/* Force everything out */
X		res = ferror(o_file);		/* Save result of stuff */
X		fclose(o_file);			/* Now flush FD */
X	  }
X	else	/* New stuff */
X#endif /*STDWRITE*/
X	  {
X		switch(styp)
X		  {
X			case WF_SBUFF:
X				res = sb_fsave((SBBUF *)b, ofd);
X				break;
X			case WF_SREG:
X				e_gocur();
X				sd = e_copyn((chroff)(mark_dot - cur_dot));
X				res = sbx_aout(sd, 2, ofd);
X				sbs_del(sd);
X				break;
X#if FX_WLASTKILL
X			case WF_SKILL:
X				res = sbx_aout(kill_ring[kill_ptr], 2, ofd);
X				break;
X#endif
X		  }
X		close(ofd);
X	  }
X	if(errno = res)
X	  {	ferr(" Output error");
X		res = 0;		/* Failure */
X		goto wdone;
X	  }
X	else
X		res = 1;		/* Success so far */
X	if(styp == WF_SBUFF)
X		f_bufnotmod();		/* Reset "buffer modified" flag */
X
X	/* Here we effect the screw-prevention steps explained earlier. */
X	/* TOPS-20, with generation numbers, need not worry about this. */
X#if TOPS20
X	saynow("Written");
X
X#else /*-TOPS20*/
X#if IMAGEN	/* KLH -- This conditional bracketting is prone to lossage */
X	/* Only create the .BAK file once per editing session!! */
X	if ((styp==WF_SBUFF) || !(b->b_flags & B_BACKEDUP))
X	  {	if (styp==WF_SBUFF)
X			b->b_flags |= B_BACKEDUP;
X#endif /*IMAGEN*/
X	unlink(oldname);	/* remove any existing "old" file */
X	if(link(o_name,oldname) == 0)	/* Rename current to "old" */
X	 	unlink(o_name);
X		/* Here is the critical point... if we stop here, there is no
X		 * longer any file with the appropriate filename!!!
X		 */
X#if IMAGEN
X	  }
X	else
X		unlink(o_name);
X#endif /*IMAGEN*/
X	if(link(newname,o_name) == 0)	/* Rename "new" to current */
X	  {	unlink(newname);
X#if IMAGEN
X		sayntoo("OK");
X#else
X		saynow("Written");
X#endif /*-IMAGEN*/
X	  }
X	else
X	  {	dingtoo("rename error!");
X		res = 0;
X	  }
X#endif /*-TOPS20*/
X
X#if IMAGEN
X	/* Update the last-modified time for the file in this buffer */
X	if ((styp == WF_SBUFF) && b->b_fn)
X	  {	stat(b->b_fn, &s);
X		b->b_mtime = s.st_mtime;
X	  }
X#endif /*IMAGEN*/
X
Xwdone:
X	hoard();			/* Get back a retained FD */
X	return(res);
X}
X
X/* FNCONS(dest,pre,f_name,post)
X *	Specialized routine to cons up a filename string into "dest",
X *	given prefix and postfix strings to be added onto last component of
X *	filename.
X */
Xchar *
Xfncons(dest, pre, f_name, post)
Xchar *dest,*pre,*f_name,*post;
X{	register char *cp, *cp2;
X	char *last_fname();
X
X	cp = dest;
X	*cp = 0;			/* Make dest string null initially */
X	cp2 = last_fname(f_name);	/* Get pointer to beg of last name */
X	strncat(cp,f_name,cp2-f_name);	/* Copy first part of filename */
X	if(pre)	strcat(cp, pre);	/* If prefix exists, add it on */
X	cp = last_fname(cp);		/* Recheck in case levels added */
X	strcat(cp, cp2);		/* Now add last name */
X	if(cp2 = post)			/* If there's a postfix, must check */
X	  {	cp[FNAMELEN-strlen(cp2)] = 0;	/* and cut dest so postfix */
X		strcat(cp, cp2);		/* will fit on end. */
X	  }
X	return(dest);
X}
X
X/* LAST_FNAME(string)
X *	Get the last component of a file name.  Returns pointer to
X *	start of component; does NOT copy string!
X */
Xchar *
Xlast_fname(f_name)
Xchar *f_name;
X{	register char *cp, *p;
X	register int c;
X
X	p = f_name;		/* pointer to last slash */
X	cp = p;
X	while(c = *cp++)
X		if(c == '/')
X			p = cp;		/* point to after the slash */
X	return(p);
X}
X
X/* SET_FN(string)
X *	Set the default filename for current buffer to "string".
X */
Xset_fn (string)
Xchar *string;
X{	register struct buffer *b;
X	register char *str;
X#if IMAGEN
X	register char *cp;
X	register int len;
X#endif /*IMAGEN*/
X	char *strdup();
X
X	b = cur_buf;
X	str = strdup(string);		/* Copy now in case copying self */
X	if(b->b_fn)
X		chkfree(b->b_fn);
X	b -> b_fn = str;
X#if IMAGEN
X	/* Do mode determination based on file name (HACK HACK) */
X	len = strlen(str);
X	b->b_flags &= ~(B_CMODE|B_TEXTMODE);
X	if (len > 4)
X	  {	if (strcmp(&str[len - 5], "draft") == 0)
X			b->b_flags |= B_TEXTMODE;
X		else
X		  {	cp = &str[len - 4];
X			if (strcmp(cp, ".txt") == 0 ||
X			    strcmp(cp, ".mss") == 0)
X				b->b_flags |= B_TEXTMODE;
X		  }
X	  }
X	if (len > 2)
X	  {	cp = &str[len - 2];
X		if (strcmp(cp, ".h") == 0 || strcmp(cp, ".c") == 0)
X			b->b_flags |= B_CMODE;
X	  }
X#endif /*IMAGEN*/
X	redp(RD_MODE);
X}
X
X/* SAVEWORLD - Attempt to save all changes user has made.
X *	Currently this amounts to writing out all modified buffers
X *	to the files $HOME/+buffername.  If a buffer is given as argument,
X *	only that buffer is saved.
X *	This is only called from the error handling routines with
X *	the TTY either gone or in normal (non-edit) mode.  The "grunt"
X *	flag says whether to output feedback during the saving process.
X */
Xsaveworld(bp, grunt)
Xstruct buffer *bp;
Xint grunt;
X{	register struct buffer *b;
X	register int wfd;
X	char sfname[FNAMSIZ];
X	struct buffer *sel_mbuf();
X
X	unhoard();		/* Ensure a FD is free for writing */
X	if(b = bp) goto once;
X	while(!bp && (b = sel_mbuf(b)))
X	  {
X	once:	strcat(strcat(strcpy(sfname,homedir),"/+"),b->b_name);
X		if(grunt) printf("Saving %s...",sfname);
X#if !(TOPS20)
X		if((wfd = creat(sfname, ev_filmod)) < 0)
X#else
X		if((wfd = open(sfname,O_WRONLY|O_UNCONVERTED)) < 0)
X#endif /*TOPS20*/
X		  {	if(grunt)
X				printf(" error - %s\n", strerror(errno));
X		  }
X		else
X		  {	sb_fsave((SBBUF *)b, wfd);
X			close(wfd);
X			if(grunt) printf("\n");
X		  }
X		b->b_flags &= ~B_MODIFIED;
X	  }
X	hoard();
X}
X
X/* HOARD, UNHOARD - Routines to save a FD for writing, to make sure
X *	that we can always write out a buffer no matter how many
X *	file descriptors we are currently using.
X */
Xhoard()			/* Stash away a FD */
X{	if(hoardfd <= 0)
X#if !(TOPS20)
X		hoardfd = open("nul:", 1);
X#else
X		hoardfd = open("/dev/null", 1);
X#endif
X}
Xunhoard()		/* Give up our stashed FD so it can be re-used */
X{	close(hoardfd);
X	hoardfd = -1;
X}
X
X#if IMAGEN
X#include <pwd.h>
X#include <ctype.h>
X
X/*
X * expand_file: expand any ~user-name/ or $env-var/ prefixes in sfn,
X * producing the full name in dfn
X */
Xexpand_file(dfn, sfn)
Xregister char *dfn, *sfn;
X{
X	register char *sp, *tp;
X	register int c;
X	register struct passwd *pw;
X	char ts[128];
X
X	/* HORRIBLE, GROSS, DISGUSTING HACK: if the destination and
X	 * source strings are identical (same pointer), then do not
X	 * do any expansion--this happens to work with the current
X	 * structure very well, since multiple expansions may happen.
X	 */
X	if (dfn == sfn)
X		return;
X
X	ts[0] = 0;
X
X	/* If have a leading $, then expand environment variable */
X	if (*sfn == '$')
X	  {	++sfn;
X		tp = ts;
X		while (*tp++ = *sfn)
X			if (!isalnum(*sfn))
X				break;
X			else
X				++sfn;
X		*--tp = 0;		/* Just in case */
X		strcpy(ts, getenv(ts));	/* MARGINAL!! */
X	  }
X	/* If have leading ~, then expand login name (null means $HOME) */
X	else if (*sfn == '~')
X	  {	++sfn;
X		if (*sfn == '/' || *sfn == 0)
X			strcpy(ts, getenv("HOME"));
X		else
X		  {	tp = ts;
X			while (*sfn && *sfn != '/')
X				*tp++ = *sfn++;
X			*tp = 0;
X			pw = (struct passwd *)getpwnam(ts);
X			if (! pw)
X				strcpy(ts, "???");
X			else
X				strcpy(ts, pw->pw_dir);
X		  }
X	  }
X
X	/* Now, ts is either empty or contains the expansion;
X	 * sfn has been updated correctly.
X	 */
X	strcpy(dfn, ts);
X	strcat(dfn, sfn);
X}
X#endif /*IMAGEN*/
/
echo x - eefill.c
sed '/^X/s///' > eefill.c << '/'
X/* ELLE - Copyright 1982, 1985, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*
X * EEFILL	Fill Mode functions
X */
X
X#include "elle.h"
X
Xextern int ev_fcolumn;	/* Fill Column variable (defined in EEVINI) */
X#if FX_SFPREF
Xchar *fill_prefix;	/* Fill Prefix variable */
Xint fill_plen;		/* Length of Fill Prefix (0 = no prefix) */
X#endif /*FX_SFPREF*/
X
X#if FX_FILLMODE
Xint fill_mode = 0;	/* TRUE when Auto Fill Mode is on */
Xint *fill_trig;		/* Pointer to fill-trigger chbit array */
Xstatic char *fill_initrig = " \t.,;:)!";
X#endif /*FX_FILLMODE*/
X
X/* Following stuff for testing routines on */
X/*
X
X          1         2         3         4         5	    6         7
X0123456789012345678901234567890123456789012345678901234567890123456789012345
X
XOkay...  more stuff to hack.  Okay.  a b c d e f g h i j k l m
Xn o p q r s t u v w x y z dfsd stuff to hack 01234 Okay testing
Xmore stuff to hack.  Okay...  more stuff to hack more stuff to
Xhack.  Okay...  more stuff to line long stuff to hack.  Okay...
Xeven more gap and.  period.  okay, end of stuff.
X	This is another fence.
X*/
X
X
X#if FX_SFCOL
X/* EFUN: "Set Fill Column" */
Xf_sfcol()
X{	register int linel;
X	char temp[20];
X
X	linel = exp_p ? exp : d_curind();
X	if(linel < 0) linel = 0;
X	say("Fill column = ");
X	dottoa(temp,(chroff)linel);
X	saytoo(temp);
X	ev_fcolumn = linel;
X}
X#endif /*FX_SFCOL*/
X
X
X#if FX_SFPREF
X/* EFUN: "Set Fill Prefix" */
Xf_sfpref()
X{	register int i;
X	register char *cp;
X
X	if((i = cur_dot - e_boldot()) > MAXLINE)
X	  {	ding("Absurd Fill Prefix");
X		return;
X	  }
X	if(fill_prefix)
X	  {	chkfree(fill_prefix);
X		fill_plen = 0;
X	  }
X	if(i <= 0)
X	  {	fill_prefix = 0;
X		cp = "";
X	  }
X	else
X	  {	fill_prefix = cp = memalloc((SBMO)(i+1));
X		fill_plen = i;
X		e_gobol();
X		do { *cp++ = e_getc(); }
X		while(--i);
X		*cp = 0;
X		cp = fill_prefix;
X	  }
X	say("Fill Prefix = \"");
X	saytoo(cp);
X	saytoo("\"");
X}
X
X
X/* TSTFILLP(lim) - Check for existence of Fill Prefix at current dot.  If
X *	not there, returns 0 without changing dot.  If there, returns
X *	1 and leaves dot immediately after the Fill Prefix.
X *	Lim = # of chars allowed to scan from buffer.
X */
Xtstfillp(lim)
Xint lim;
X{	register int i, c;
X	register char *cp;
X	chroff savdot;
X
X	if(!(i = fill_plen) || (i > lim))
X		return(0);
X	savdot = e_dot();
X	cp = fill_prefix;
X	do {	if(*cp++ != e_getc())
X		  {	e_go(savdot);
X			return(0);
X		  }
X	  } while(--i);
X	return(1);
X}
X#endif /*FX_SFPREF*/
X
X#if FX_FILLREG || FX_FILLPARA
X
X/* ED_FILL(start, end, flag) - Fill a region.
X *	Flag	0 for full filling; extra whitespace is flushed.  First
X *			word is always retained.
X *		1 for skimpy filling such as Auto-Fill likes.
X *			Extra whitespace is NOT flushed, except at
X *			beginning of a newly created line.
X *			This is not yet implemented however.
X * Note: updates cur_dot to compensate for changes in buffer, and returns
X *	there when done!
X * Note: Checks for Fill Prefix when it exists.
X */
Xed_fill(begloc, endloc, flag)
Xchroff begloc, endloc;
Xint flag;
X{	register int c;
X	register int len, lastc;
X	chroff savloc;
X	int lastbrk;
X	int parlen;
X
X	parlen = endloc - begloc;
X	if(parlen < 0)
X	  {	begloc = endloc;
X		parlen = -parlen;
X	  }
X	e_go(begloc);
X	len = d_curind();		/* Set up current col */
X
X#if FX_SFPREF
X	/* If at beg of line, check for fill prefix and skip over it */
X	if((len == 0) && tstfillp(parlen))
X	  {	parlen -= fill_plen;
X		len = d_curind();
X	  }
X#endif /*FX_SFPREF*/
X	lastbrk = 0;			/* Put next word on no matter what. */
X	c = 0;
X	for(;;)
X	  {
X#if ICONOGRAPHICS
X             if (c != ')' && c != '"')  /* allow for two sp after .) or ." */
X#endif /*ICONOGRAPHICS*/
X		lastc = c;
X		if(--parlen < 0) break;
X		c = e_getc();
X		if(c == EOF)
X			break;
X#if FX_SFPREF
X		/* If at beg of line, check for fill prefix and flush it */
X		if((c == LF) && tstfillp(parlen))
X		  {	e_igoff(-(fill_plen+1));
X			e_ovwc(c = SP);
X			e_deln((chroff)fill_plen);
X			parlen -= fill_plen;
X			if(cur_dot >= e_dot())
X				cur_dot -= fill_plen;
X		  }
X#endif /*FX_SFPREF*/
X		if(c == TAB || c == LF)		/* Replace tabs+eols by sps */
X		  {	e_backc();		/* Back up 1 */
X			e_ovwc(c = SP);
X		  }
X		if(c == SP)
X		  {	if(lastc == SP)
X			  {	e_rdelc();
X				if(cur_dot > e_dot()) --cur_dot;
X				continue;
X			  }
X			lastbrk = len;
X			if(lastc == '.' || lastc == '!' || lastc == '?'
X#if ICONOGRAPHICS
X                                                        || lastc == ':'
X#endif /*ICONOGRAPHICS*/
X									)
X			  {	if(--parlen < 0) goto done;
X				if((c = e_getc()) == EOF)
X					goto done;
X				len++;
X				if(c != SP)
X				  {	e_backc();
X					e_putc(c = SP);
X					if(cur_dot >= e_dot()) ++cur_dot;
X				  }
X			  }
X		  }
X#if ICONOGRAPHICS
X		if (c == BS)                    /* adjust for backspaces */
X			if ((len -= 2) < 0) len = 0;
X#endif /*ICONOGRAPHICS*/
X		/* Normal char */
X		if(++len > ev_fcolumn && lastbrk)	/* If went too far */
X		  {	c = lastbrk - len;	/* Must put EOL at last SP */
X			e_igoff(c);
X			parlen -= c;	/* C is negative, actually adding */
X			parlen--;
X			e_ovwc(LF);
X			lastbrk = 0;
X			len = 0;
X			c = SP;		/* Pretend this char was space */
X#if FX_SFPREF
X			if(fill_plen)
X			  {	if(cur_dot >= e_dot())
X					cur_dot += fill_plen;
X				/* Better hope no nulls in prefix! */
X				e_sputz(fill_prefix);
X				len = d_curind();
X			  }
X#endif /*FX_SFPREF*/
X		  }
X	  }
Xdone:	savloc = cur_dot;
X	e_setcur();	/* Reached paragraph end, set cur_dot temporarily */
X	buf_tmod(begloc-cur_dot);	/* So that proper range is marked */
X	e_gosetcur(savloc);		/* Then restore original cur_dot */
X}
X#endif /*FX_FILLREG || FX_FILLPARA*/
X
X#if FX_FILLMODE
X
X/* EFUN: "Auto Fill Mode" */
X/*	Toggles Auto Fill Mode (a minor mode). */
Xf_fillmode()
X{	register char *cp;
X	int *chballoc();
X
X	fill_mode = fill_mode ? 0 : 1;
X	if(!fill_trig)
X	  {	fill_trig = chballoc(128);
X		for(cp = fill_initrig; *cp; ++cp)
X			chbis(fill_trig, *cp);
X	  }
X	redp(RD_MODE);
X}
X
X/* Called by F_INSSELF to handle char insertion in Auto Fill mode */
Xfx_insfill(c)
Xint c;
X{
X	ed_insn(c,exp);
X	if(chbit(fill_trig, c))
X	  {	fill_cur_line();
X
X	  }
X}
X
X
Xfill_cur_line()
X{
X	register int foundit, i;
X	chroff lastbrkdot, boldot, eoldot;
X
X	boldot = e_boldot();
X
X	/* First back up to find place to make first break. */
X	e_bwsp();
X	lastbrkdot = e_dot();
X	foundit = 0;
X	for(foundit = 0; foundit >= 0;)
X	  {	if((i = d_curind()) <= ev_fcolumn)
X		  {	if(foundit)
X				foundit = -1;
X			else break;
X		  }
X		else ++foundit;
X		while (!c_wsp (e_rgetc ())) ;
X		e_bwsp();
X		lastbrkdot = e_dot();
X		if(lastbrkdot <= boldot)
X		  {	lastbrkdot = boldot;
X			break;
X		  }
X	  }
X
X	if(foundit)
X		ed_fill(lastbrkdot, e_eoldot(), 1);
X}
X#endif /*FX_FILLMODE*/
X
X#if IMAGEN
X
X#if FX_TEXTMODE
X/* EFUN: "Text Mode Toggle" (not EMACS) */
Xf_textmode()
X{
X	cur_buf->b_flags ^= B_TEXTMODE;
X	redp(RD_MODE);
X}
X#endif /*FX_TEXTMODE*/
X
Xint curr_indent = -1;		/* Current indent (for text mode autowrap) */
X				/*  (misnomered: actually current column) */
Xchroff best_break;		/* Best break point so far */
X
X
X/* Fill-mode version of "Insert Self" */
X
Xfim_insself(c)
Xint c;
X{
X	register int ind, flags = cur_buf->b_flags;
X
X	/* In Text mode, auto-wrap happens at spaces after fill column */
X	if (c == SP && flags & B_TEXTMODE && exp == 1 && magic_wrap(c))
X		return;
X
X	/* In C-mode, tab stops are every 4 columns */
X	else if (c == TAB && flags & B_CMODE &&
X			(ind = magic_backto_bol()) >= 0)
X		ed_indto((ind + 4) & ~3);
X	else
X	  {	ed_insn(c, exp);
X
X		/* Keep track of indent, once we have a grip on it */
X		if (last_cmd == INSCMD && curr_indent != -1)
X		  {	this_cmd = INSCMD;  /* Keep the ball rolling */
X			if (c == TAB)
X				curr_indent = ((curr_indent + 8) & ~7)
X						 + 8 * (exp - 1);
X			else if (c == '\n')
X				curr_indent = 0;
X			else if (c < SP || c > 0176)
X				curr_indent += (2 * exp);
X			else
X				curr_indent += exp;
X		  }
X	  }
X}
X
X/* Fill-mode version of "Delete Character" */
X
Xfim_dchar()
X{	/* In C mode, deleting at BOL should do fake TAB preservation */
X	if (cur_buf->b_flags & B_CMODE)
X	  {	chroff savdot;
X		register int c, indent;
X
X		if (e_rgetc() != LF)
X		  {	/* Only hack this at BOL */
X			e_getc();
X			goto normal;
X	    	  }
X		e_getc();
X		savdot = e_dot();
X		indent = 0;
X		while ((c = e_getc()) == SP || c == TAB)
X			if (c == SP)
X				++indent;
X		else
X			indent = (indent + 8) & ~7;
X		e_rgetc();
X		if (indent >= 4)
X		  {	ed_delete(savdot, e_dot());
X			ed_indto((indent - 4) & ~3);
X			f_begline();		/* HACK!!!! */
X		  }
X		else
X		  {	e_go(savdot);
X			ef_deln(exp);
X		  }
X	  }
X	else
X normal:	return (ef_deln(exp));
X}
X
X/* Fill-mode version of "Backward Delete Character" */
X
Xfim_bdchar()
X{	register int ind;
X
X	/* If in C mode, and deleting into white space at BOL, hack tabs */
X	if (exp == 1 && cur_buf->b_flags & B_CMODE &&
X			(ind = magic_backto_bol()) > 0)
X		ed_indto(ind < 4 ? ind - 1 : ((ind - 4) & ~3));
X	else
X		return (ef_deln (-exp));
X}
X
X/* Fill-mode version of "CRLF" */
Xfim_crlf()
X{	register int i;
X
X	if(e_getc() == LF
X	  && exp == 1
X	  && e_lblankp() && e_lblankp())
X	  {	e_gocur();
X		e_gonl();
X		e_setcur();
X		ed_delete(e_dot(), e_eoldot());
X	  }
X	else
X	  {	e_gocur();
X#if IMAGEN
X		if (cur_buf->b_flags & B_TEXTMODE && exp == 1 &&
X		    magic_wrap('\n'))
X			return;
X		else
X#endif /*IMAGEN*/
X		if((i = exp) > 0)
X			do ed_crins();
X			while(--i);
X	  }
X}
X
X/* Do all magic for auto-wrap in Text mode:
X * return as did wrap (i.e., everything is taken care of)
X */
Xmagic_wrap(tc)
Xint tc;				/* "trigger char" */
X{
X	register int c, indent, i, nc;
X	chroff savdot, modstart, breakdot;
X    
X	savdot = e_dot();
X	nc = 0;
X	if (last_cmd == INSCMD && curr_indent != -1)
X	  {	indent = curr_indent;		/* Already know our indent */
X		breakdot = best_break;
X	  }
X	else
X	  {
X#ifdef INDENTDEBUG
X		barf2("Full indent calculation");
X#endif
X		for (nc = 0; (c = e_rgetc()) != EOF && c != '\n'; ++nc)
X		    ;				/* nc: # chars to look at */
X		if (c == '\n')			/* Go back over NL */
X			e_getc();
X		indent = 0;
X    
X		/* Search for last line break point, leaving it in breakdot */
X		breakdot = (chroff)0;
X		while (--nc >= 0)
X		  {	c = e_getc();
X			if (c == TAB)
X				indent = (indent + 8) & ~7;
X			else if (c < SP || c > 0176)
X				indent += 2;
X			else
X				++indent;
X			if ((c == SP || c == TAB) &&
X			  (breakdot == (chroff)0 || (indent <= ev_fcolumn)))
X				breakdot = e_dot();
X		  }
X	  }
X
X    /* If there is nothing to do, get out */
X	if (indent <= ev_fcolumn)
X	  {	e_go(savdot);
X		if (tc == SP)
X		  {	curr_indent = indent;
X			best_break = (chroff)(savdot + 1); /* Remember here, also */
X			this_cmd = INSCMD;		/* We do know current indent */
X		  }
X		else if (tc == '\n')
X		  {	curr_indent = 0;
X			best_break = (chroff)0;
X			this_cmd = INSCMD;
X		  }
X		else
X			errbarf("bad trigger");
X		return(0);
X	  }
X
X	if (breakdot == (chroff)0)
X	  {
X	/* No breakpoint found or none needed, just break line at end
X	 */
X		e_go(savdot);
X		modstart = savdot;
X		e_putc('\n');
X	  }
X	else
X	  {
X	/* Get to breakpoint and replace with newline
X	 */
X		e_go(breakdot);
X		e_rdelc();
X		modstart = e_dot();		/* Remember where changes start */
X		e_putc('\n');			/* Insert line break */
X		e_go(savdot);			/* Get back to trigger point */
X	  }
X	if (e_rgetc() != '\n')
X	  {		/* If not at line start, */
X		e_getc();
X		e_putc(tc);			/*  insert trigger character */
X
X	/* Once again, compute new indent by backing up to BOL */
X		for (nc = 0; (c = e_rgetc()) != EOF && c != '\n'; ++nc)
X			;
X		if (c == '\n')			/* Go back over NL */
X			e_getc();
X		indent = 0;
X		breakdot = (chroff)0;
X		while (--nc >= 0)
X		  {		/* Get back to current dot */
X			c = e_getc();
X			if (c == TAB)
X				indent = (indent + 8) & ~7;
X			else if (c < SP || c > 0176)
X				indent += 2;
X			else
X				++indent;
X			if ((c == SP || c == TAB) &&
X			  (breakdot == (chroff)0 || (indent <= ev_fcolumn)))
X				breakdot = e_dot();
X		  }
X		if (breakdot == (chroff)0)	/* If no good break found, use dot */
X			breakdot = e_dot();
X		curr_indent = indent;		/* Now we know where we are */
X		if (tc == '\n')			/* If trigger was NL */
X			best_break = (chroff)0;	/*  indent is 0, and no best break */
X		else
X			best_break = breakdot;	/* This is best break so far */
X	  }
X	else
X	  {	e_getc();
X		curr_indent = 0;		/* At line start, no indent */
X		best_break = (chroff)0;		/* Do not have a best break so far */
X	  }
X	ed_setcur();
X	buf_tmat(modstart);			/* Alert to potential changes */
X	this_cmd = INSCMD;			/* Say we know where we are */
X	return(1);
X}
X
X/* Do lots of magic things for C-mode indent:
X * erase back to BOL iff we are looking back at white space only,
X * returning the indent level of the original dot
X * (< 0 means no erasure done)
X */
X/*#define MYDEBUG /* */
X#ifdef MYDEBUG
Xreveal(msg, v1, v2, v3)
Xchar *msg;
X{
X	char ahint[128];
X	sprintf(ahint, msg, v1, v2, v3);
X	barf2(ahint);
X}
X#endif
X
Xmagic_backto_bol()
X{
X	chroff savdot;
X	register int c, indent, nc, i;
X
X	savdot = e_dot();
X        nc = 0;
X	while ((c = e_rgetc()) != EOF && c != LF)
X	  {	++nc;			/* Count # chars */
X		if (c != SP && c != TAB)
X		  {	e_go(savdot);
X#ifdef MYDEBUG
X			reveal("fail: nc: %d", nc);
X#endif
X			return -1;
X        	  }
X    	  }
X	if (c == LF)			/* Go back over the LF */
X		e_getc();
X    	indent = 0;			/* (zero-based indent) */
X    	savdot = e_dot();		/* BOL is now origin for delete */
X    	for (i = 1; i <= nc; ++i)
X    		if ((c = e_getc()) == SP)
X			++indent;
X		else 			/* (tab) */
X			indent = (indent + 8) & ~7;
X    	if (nc > 0)			/* Don't bother deleting nothing */
X		ed_delete(savdot, e_dot());
X#ifdef MYDEBUG
X	reveal("indent: %d, nc: %d, foo: %d", indent, nc, 234);
X#endif
X	return(indent);
X}
X#endif /*IMAGEN*/
X
X#if ICONOGRAPHICS
X/* Iconographics hack for Auto-Fill mode.  Too big and clumsy, but
X * retained for posterity in case it has some obscure feature.
X */
X
Xfill_current_line ()
X{
X        chroff startpos, endpos, savepos, limitpos;
X        int i, foundit;
X        SBSTR *savep;
X
X        foundit = 0;
X        while (d_curind() > ev_fcolumn)
X           {
X            foundit = 1;
X            startpos = e_dot ();
X            e_bwsp ();
X            while (d_curind() > ev_fcolumn) /* back up to ends of wds*/
X               {                                /* until <= fill column */
X                while (!c_wsp (e_rgetc ())) ;
X                e_bwsp ();
X               }
X            if (e_dot () == e_boldot ())
X               { /*	ding ("Word does not fit in fill column"); */
X	                return(0);
X               }
X            savep = e_copyn (startpos - e_dot ());
X            e_setcur ();                /* ed_delete does gocur */
X            ed_delete (savepos = e_dot (), startpos);
X
X		f_crlf();		/* Now insert newline */
X		e_sputz(fill_prefix);	/* With fill prefix */
X            startpos += e_dot () - savepos;
X            if (d_curind() > ev_fcolumn)
X               {	ed_delete (savepos, e_dot ());
X	                sb_sins (cur_buf, savep);
X	                e_setcur ();
X        	        ding ("Fill prefix > fill column???");
X                	return(0);
X               }
X            savepos = e_dot ();         /* gun inherited initial whitespace */
X            sb_sins (cur_buf, savep);
X            e_go (savepos);
X            e_fwsp ();
X            if ((limitpos = e_dot ()) > startpos) limitpos = startpos;
X                                        /* in case rest of line was white */
X            ed_delete (savepos, limitpos);
X            e_gosetcur (startpos + savepos - limitpos);
X           }
X
X        return foundit;
X  }
X#endif /*ICONOGRAPHICS*/
/
echo x - eehelp.c
sed '/^X/s///' > eehelp.c << '/'
X/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*	EEHELP - Help function
X */
X
X#include "elle.h"		       /* include structure definitions */
X
X
X#if FX_DESCRIBE
X/* EFUN: "Describe" */
X/*	DESCRIBE - Help-command hack.
X**	Crude approximation of EMACS function.
X*/
Xstatic struct buffer *help_buf;
X
Xf_describe()
X{	register char *cp;
X	register int i, c;
X	char str[10];
X	struct buffer *savbuf, *b, *make_buf();
X	chroff bdot;
X
X	saynow("Help for command: ");
X	i = cmd_idx(c = cmd_read());		/* Get function idx for cmd */
X	if(c&CB_META) sayntoo("M-");
X	if(i == FN_PFXMETA)
X	  {	sayntoo("M-");
X		i = cmd_idx(c = (cmd_read() | CB_META));
X	  }
X	else if(i == FN_PFXEXT)
X	  {	sayntoo("^X-");
X		i = cmd_idx(c = (cmd_read() | CB_EXT));
X	  }
X	str[0] = c&0177;
X	str[1] = 0;
X	sayntoo(str);
X
X	/* Now read in the help file, if necessary */
X	savbuf = cur_buf;
X	if(help_buf)
X		chg_buf(help_buf);
X	else
X	  {
X		saynow("Loading ");
X		sayntoo(ev_helpfile);
X		sayntoo("...");
X		chg_buf(help_buf = make_buf(" **HELP**"));
X		if(read_file(ev_helpfile) == 0)
X		  {	chg_buf(savbuf);
X			kill_buf(help_buf);
X			help_buf = 0;
X			return;
X		  }
X	  }
X
X
X	/* Find function index in current buffer */
X	cp = str;
X	*cp++ = '<';
X	*cp++ = 'F';
X	cp = dottoa(cp, (chroff)i);
X	*cp++ = '>';
X	e_gobob();
X	if(e_search(str, cp-str, 0) == 0)
X		sayntoo(" No help found");
X	else
X	  {
X		bdot = e_dot();
X		while(!e_lblankp()) e_gonl();	/* Move past 1st blank line */
X		b = make_buf(" *SHOW*");
X		sb_sins((SBBUF *)b, e_copyn(bdot - e_dot()));
X		mk_showin(b);			/* Show the stuff */
X		kill_buf(b);
X		sayclr();
X	  }
X	chg_buf(savbuf);
X}
X#endif /*FX_DESCRIBE*/
/
echo x - eekmac.c
sed '/^X/s///' > eekmac.c << '/'
X/* ELLE - Copyright 1982, 1985, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*	EEKMAC - Keyboard Macro routines
X *		Modelled after the "e_macro.c" for ICONOGRAPHICS
X *		by C. D. Tavares, 9/11/82
X */
X
X#include "elle.h"
X
X#if FX_SKMAC		/* Entire file is under this conditional! */
X
Xint kdef_mode;		/* Set when collecting (a "minor mode") */
Xstatic int km_flag = 0;	/* 1 = executing, -1 collecting, 0 neither */
Xstatic int km_exp;	/* Arg to "Execute Kbd Macro" - # times more to xct */
Xstatic struct buffer *km_buf;
X
X/* EFUN: "Start Kbd Macro" */
X
Xf_skmac()
X{	register struct buffer *b;
X	struct buffer *make_buf();
X
X	if(km_flag)
X	  {	ding("Kbd macro active, ignoring \"Start Kbd Macro\"");
X		return;
X	  }
X	if((b = km_buf) == 0)
X		b = km_buf = make_buf(" *KBDMAC*");
X	ex_reset(b);
X	km_flag = -1;		/* Say starting macro collection */
X	kdef_mode = 1;
X	redp(RD_MODE);
X}
X
X/* EFUN: "End Kbd Macro" */
X
Xf_ekmac()
X{
X	if(km_flag > 0 && (--km_exp >= 0))
X	  {	ex_go((SBBUF *)km_buf, (chroff)0);
X	  }
X	else if(km_flag)
X	  {	km_flag = 0;
X		kdef_mode = 0;	/* Flush minor mode */
X		redp(RD_MODE);
X	  }
X}
X
X/* EFUN: "Execute Kbd Macro" */
X
Xf_xkmac()
X{
X	if(km_flag)
X		ding("Already in kbd macro!");
X	else if(km_buf == 0)
X		ding("No kbd macro defined");
X	else if((km_exp = exp-1) >= 0)
X	  {
X		ex_go((SBBUF *)km_buf, (chroff) 0);
X		km_flag = 1;		/* Start macro execution */
X	  }
X}
X
X/* EFUN: "View Kbd Macro" */
X
Xf_vkmac()
X{	register struct buffer *b, *savbuf;
X	chroff prmplen;
X
X	if(!(b = km_buf))
X	  {	ding("No kbd macro defined");
X		return;
X	  }
X	savbuf = cur_buf;
X	chg_buf(b);
X	e_gobob();
X	e_sputz("Current Kbd macro:\n\n");
X	prmplen = e_dot();
X	mk_showin(b);		/* Show the macro buffer temporarily */
X	e_gobob();
X        chg_buf(savbuf);
X	sb_deln((SBBUF *)b, prmplen);	/* Flush the prompt */
X}
X
X/* KM_GETC - return next command char from kbd macro being executed.
X**	This is < 0 if not executing kbd macro.  Also responsible for
X**	gathering input for kbd macro.
X*/
Xkm_getc()
X{	register int c;
X
X	while (km_flag > 0)		/* Executing macro? */
X	  {	c = sb_getc(((SBBUF *)km_buf));	/* Yes, get char */
X		if(c != EOF)
X			return(c);		/* and return as cmd */
X
X		if(--km_exp >= 0)		/* Macro done.  Repeat? */
X			ex_go((SBBUF *)km_buf, (chroff)0);	/* Yes */
X		else km_flag = 0;		/* No, stop execution */
X	  }
X	c = tgetc();			/* Get char from user (TTY) */
X	if(km_flag < 0)			/* Save it if collecting macro */
X	  {	sb_putc(((SBBUF *)km_buf), c);
X	  }
X	return(c);
X}
X
X/* KM_INWAIT() - Return TRUE if any keyboard-macro input waiting.
X */
Xkm_inwait()
X{	register int c;
X	if(km_flag > 0)
X		if((c = sb_getc(((SBBUF *)km_buf))) != EOF || (km_exp > 0))
X		  {	sb_backc(((SBBUF *)km_buf));
X			return(1);
X		  }
X	return(0);
X}
X
Xkm_abort ()
X{
X	if(km_flag > 0)		/* Executing? */
X		km_flag = 0;	/* Stop */
X	else if(km_flag < 0)	/* Collecting? */
X		f_ekmac();	/* Close it out */
X}
X
X#endif /*FX_SKMAC*/
X
X#if 0	/* Old unused stuff */
Xstatic char mode_buf [60];
X
Xadd_mode (mode)
X  char *mode;
X  {
X        register char *cur, *c, *m;
X
X        if (cur_mode != mode_buf)
X           {
X            strcpy (mode_buf, cur_mode);
X            cur_mode = mode_buf;
X           }
X
X        if (cur_mode [0]) strcat (cur_mode, ", ");
X        strcat (cur_mode, mode);
X        make_mode ();
X  }
X
Xremove_mode (mode)
X  char *mode;
X  {
X        register char *cur, *c, *m;
X
X        if (*cur_mode == 0) return;
X
X        if (cur_mode != mode_buf)
X           {
X            strcpy (mode_buf, cur_mode);
X            cur_mode = mode_buf;
X           }
X
X        for (cur = cur_mode ; *cur ; cur++)
X            if (*cur == *mode)          /* 1st char matches */
X               {
X                for (c = cur, m = mode ; *m && (*m == *c) ; m++, c++) ;
X                if (!(*m))              /* ok, mode matched */
X                   {                    /* kill leading ", " */
X                    if (*(cur - 1) == ' ') --cur;
X                    if (*(cur - 1) == ',') --cur;
X                    for ( ; *cur = *c ; cur++, c++) ;   /* recopy to end */
X                    make_mode ();
X                    return;
X                   }
X               }
X  }
X#endif /*COMMENT*/
/
echo x - eemain.c
sed '/^X/s///' > eemain.c << '/'
X/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*
X * EEMAIN	ELLE Main Command Loop
X */
X
X#include "elle.h"
X
X#include <stdio.h>
X#if !(V6)
X#include <signal.h>
X#else
X#include "eesigs.h"		/* Use this on V6 system */
X#endif /*V6*/
X
Xchar *argfile[MAXARGFILES];	/* Filename args at startup */
X
Xextern int (*sbm_debug)();
Xextern int (*sbv_debug)();
Xint (*vfy_vec)();	/* If non-zero, routine to verify data
X			 * after each main-loop command */
X
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X	register int c;		/* Current command character */
X	register int i;
X	static int waitct;
X	extern int errsbm();
X#if SUN
X	extern int sun_rdevf;	/* from EESUN */
X#endif
X#ifdef STKMEM
X	char stackm[STKMEM];		/* Allocate some unused stack space */
X#endif /*STKMEM*/
X
X	sbv_debug = errsbm;		/* Load with addrs of routine to */
X	sbm_debug = errsbm;		/* process SB and SBM errors. */
X
X#ifdef STKMEM
X	sbm_init(&stackm[0],(SBMO)STKMEM);	/* Initialize mem alloc rtns */
X#endif /*STKMEM*/
X#if SUN
X	sun_main(&argc, argv);		/* On SUN, invoke window startup */
X#endif /*SUN*/
X
X	setbuf(stdout, (char *)NULL);	/* Remove all stdio buffering */
X	setbuf(stderr, (char *)NULL);	/* in case of error reports. */
X
X	waitct = 0;			/* debugging */
X	doargs(argc,argv);		/* Set up args */
X	initialize ();			/* Initialize the editor */
X
X	if (argfile[0])			/* shell line arg */
X		find_file(argfile[0]);
X#if MAXARGFILES > 1
X	if(argfile[1])
X	  {	f_2winds();		/* Make 2 windows, go to 2nd */
X		i = 1;
X#if MAXARGFILES > 2
X		for (; i < MAXARGFILES; ++i)
X#endif /* > 2 files */
X			find_file(argfile[i]);	/* Get further file(s) */
X		f_othwind();		/* Move back to 1st window */
X	  }
X#endif /* > 1 file */
X
X	redp(RD_SCREEN|RD_MODE);	/* Clear and show mode line */
X	setexit(0);			/* catch for ints, ^G throws */
X
X/* -----------------------------------------------------------
X**			ELLE MAIN LOOP
X**
X*/
X	for (;;)
X	  {
X		/* First set up default arg unless last cmd specified it */
X		if(this_cmd != ARGCMD)
X		  {	exp = 1;		/* Default arg is 1 */
X			exp_p = 0;		/* Say no explicit arg */
X			last_cmd = this_cmd;
X		  }
X		this_cmd = 0;
X
X		askclr();		/* If stuff asked, say to clear it */
X		if(cmd_wait())
X			waitct++;
X		else if(rd_type != 0)
X			redisplay();	/* Redisplay if needed and no input */
X#if SUN
X		sun_rdevf = 1;		/* Allow mouse events on this input */
X#endif
X		c = cmd_read();		/* Read an editor command */
X		sayclr();		/* Ask to clear echo area cleverly */
X
X#if SUN
X		if(c != -1)		/* SUN may not have real input */
X#endif					/*    if mouse event happened. */
X			cmd_xct(c);	/* Execute the command char! */
X
X		if(vfy_vec)		/* If debugging, */
X			(*vfy_vec)(1);	/* verify data structs right away */
X	  }
X}
X
Xchar *prof_file;	/* Can specify user profile filename */
X
Xdoargs(argc,argv)
Xint argc;
Xchar **argv;
X{	register int cnt, c;
X	register char **av;
X	extern int tibfmsk;
X	int argfiles = 0;
X	int argsignored = 0;
X
X	av = argv;
X	cnt = argc;
X
X#if V6	/* V6 doesn't have environment thus no TERM var */
X	/* Hack to force terminal type; analyze pgm name to get
X	 * possible ".type" suffix.
X	 */
X	if(cnt && (c = strlen(*av)))
X	  while(--c >= 0)
X	  {	switch(av[0][c])
X		  {	case '.':
X				tv_stype = &av[0][c+1];
X			case '/':
X				break;
X			default: continue;
X		  }
X		break;
X	  }
X#endif /*V6*/
X
X	while(--cnt > 0)
X	  {	++av;
X		if(*av[0] != '-')	/* If not switch, */
X		  {			/* assume it's an input filename */
X			if (argfiles < MAXARGFILES)
X				argfile[argfiles++] = *av;
X			else
X				++argsignored;
X			continue;
X		  }
X		c = upcase(av[0][1]);
X		switch(c)		/* Switches without args */
X		  {	case 'I':	/* Allow debug ints */
X				dbg_isw = 1;
X				continue;
X			case '8':		/* Ask for 8-bit input */
X				tibfmsk = 0377;
X				continue;
X			case '7':		/* Ask for 7-bit input */
X				tibfmsk = 0177;
X				continue;
X#if IMAGEN
X			case 'R':	/* Debug redisplay stuff */
X				dbg_redp = 1;
X				continue;
X#endif /*IMAGEN*/
X		  }
X		if(--cnt <= 0)
X			goto stop;
X		++av;
X		switch(c)		/* Switches with args */
X		  {	case 'T':	/* Terminal type */
X				tv_stype = *av;
X				break;	
X			case 'P':
X				prof_file = *av;
X			default:
X				goto stop;
X		  }
X		continue;
X	stop:	printf("ELLE: bad switch: %s\n",*av);
X		exit(1);
X	  }
X	if (argsignored > 0)
X	  {	printf("ELLE: more than %d file args, %d ignored.\n",
X			MAXARGFILES, argsignored);
X		sleep(2);	/* Complain but continue after pause */
X	  }
X}
X
Xint f_throw();		/* throw function */
Xint bite_bag();		/* Error handling routine */
Xint hup_exit();		/* Hangup handling routine */
X
Xstruct majmode ifunmode = { "Fundamental" };
X
Xinitialize ()				/* Initialization */
X{
X#if SUN
X	extern int sun_winfd;
X#endif
X	cur_mode = fun_mode = &ifunmode;	/* Set current major mode */
X	unrchf = pgoal = -1;
X	if(!homedir)
X	  {
X#if V6
X		extern char *logdir();
X		homedir = logdir();
X#else /* V7 */
X		homedir = getenv("HOME");
X#endif /*-V6*/
X	  }
X
X	sbx_tset((chroff)0,0);		/* Create swapout file */
X					/* (Temporary hack, fix up later) */
X	hoard();			/* Hoard a FD for write purposes */
X
X	redp_init();			/* Set up the display routines */
X	init_buf();			/* Set up initial buffers */
X	set_profile(prof_file);		/* Set up user profile */
X
X#if SUN
X	if(sun_winfd) sun_init();
X#endif /*SUN*/
X
X	/* Set up signal handlers */
X#if 0					/* not really used */
X	signal (SIGQUIT, f_throw);	/* Quit - on ^G */
X#endif
X#if !(MINIX)
X	signal (SIGSYS, bite_bag);	/* Bad arg to Sys call */
X#endif
X	signal (SIGSEGV, bite_bag);	/* Segmentation Violation */
X#if !(COHERENT)
X	signal (SIGILL, bite_bag);	/* Illegal Instruction interrupt */
X	signal (SIGBUS, bite_bag);	/* Bus Error interrupt */
X#endif /*-COHERENT*/
X#if !(TOPS20)				/* T20 just detaches job */
X	signal (SIGHUP, hup_exit);	/* Terminal Hangup interrupt */
X#endif /*-TOPS20*/
X}
X
X
X/* NOTE: This routine is not actually used, because ELLE does not
X * allow interrupts to do anything.
X */
X/* EFUN: "Error Throw" */
Xf_throw ()			       /* abort whatever is going on */
X{
X	ring_bell ();
X	curs_lin = -1000;		/* make t_curpos do something */
X	redp(RD_MOVE);		/* crock: cursor seems to move, so fix it */
X	signal(SIGQUIT, f_throw);	/* rearm signal */
X/*	unwind_stack(main); */
X	reset(1);			/* throw to main loop */
X}
X
X/* RING_BELL - General-purpose feeper when something goes wrong with
X *	a function.
X */
Xring_bell()
X{	t_bell();		/* Tell user something's wrong */
X
X#if FX_SKMAC
X        f_ekmac();		/* Stop collecting keyboard macro if any */
X#endif /*FX_SKMAC*/
X}
X
X/* EFUN: "Return to Superior"
X *	Behavior here is somewhat system-dependent.  If it is possible to
X * suspend the process and continue later, we do not ask about modified
X * buffers.  Otherwise, we do.  Questioning can always be forced by using
X * the prefix ^U.
X *	Note that here we try to be very careful about not letting the user
X * exit while buffers are still modified, since UNIX flushes the process
X * if we exit.  Also, the code here conspires with sel_mbuf to rotate
X * through all modified buffers, complaining about a different one each time,
X * so that the user need not even know how to select a buffer!
X */
Xf_retsup()
X{	register char *reply;
X	register int c;
X	register struct buffer *b, *b2;
X	extern struct buffer *sel_mbuf();
X	extern int tsf_pause;
X
X	/* If we have capability of pausing and later continuing, do that,
X	 * except if CTRL-U forces us into question/save/quit behavior.
X	 */
X	if(tsf_pause && (exp_p != 4))
X	  {	clean_exit();		/* Return TTY to normal mode */
X		ts_pause();		/* Pause this inferior */
X		set_tty();		/* Continued, return to edit mode */
X		redp(RD_SCREEN);
X		return;
X	  }
X
X	/* Sigh, do more typical "Are you sure" questioning prior to
X	 * killing the editor permanently.
X	 */
X	b = cur_buf;
X	if((b = sel_mbuf(b)) || (b = sel_mbuf((struct buffer *)0)) )
X	  {	if(b2 = sel_mbuf(b))
X			reply = ask(
X		"Quit: buffers %s, %s,... still have changes - forget them? ",
X				b->b_name, b2->b_name);
X		else
X			reply = ask(
X		"Quit: buffer %s still has changes - forget them? ",
X				b->b_name);
X		
X	  }
X	else
X	  {
X#if IMAGEN	/* Do not ask further if nothing modified */
X		barf("Bye");
X		clean_exit();
X		exit(0);
X#else
X		reply = ask("Quit? ");
X#endif /*-IMAGEN*/
X	  }
X
X	if (reply == 0)
X		return;			/* Aborted, just return */
X
X	c = upcase(*reply);		/* Get 1st char of reply */
X	chkfree(reply);
X
X	switch(c)
X	  {	case 'Y':
X#if IMAGEN
X			barf("Bye");
X#endif /*IMAGEN*/
X			clean_exit();
X			exit(0);
X#if 0
X		case 'S':		/* Suspend command for debugging */
X			bkpt();
X			return;
X#endif /*COMMENT*/
X		default:		/* Complain */
X			ring_bell();
X		case 'N':
X			if(b)	/* B set if we have any modified buffers */
X			  {	sel_buf(b);
X				if(b->b_fn)
X					saynow("Use ^X ^S to save buffer");
X				else	saynow("Use ^X ^W to write out buffer");
X			  }
X	  }
X}
X
X
X#if FX_WFEXIT
X/* EFUN: "Write File Exit" (not EMACS) - from IMAGEN config */
Xf_wfexit()
X{
X	exp_p = 1;		/* Ensure f_savefiles asks no questions */
X	if (! f_savefiles())	/* Save all modified buffers, but */
X		return;		/*  stay here if any save fails */
X	saynow("Bye");
X	clean_exit();
X	exit(0);
X}
X#endif /*FX_WFEXIT*/
X
X/* Subprocess-handling stuff; put here for time being. */
X
X/* EFUN: "Push to Inferior" */
X#if TOPS20
X#include <frkxec.h>	/* Support for KCC forkexec() call */
X#endif
Xf_pshinf()
X{
X	register int res;
X	register int (*sav2)(), (*sav3)();
X	int pid, status;
X	char *shellname;
X#if IMAGEN
X	char fullshell[64];
X#endif /*IMAGEN*/
X
X	sav2 = signal(SIGINT, SIG_IGN);		/* Ignore TTY interrupts */
X	sav3 = signal(SIGQUIT, SIG_IGN);	/* Ditto TTY "quit"s */
X	clean_exit();				/* Restore normal TTY modes */
X
X#if TOPS20
X    {
X	struct frkxec fx;
X	fx.fx_flags = FX_WAIT | FX_T20_PGMNAME;
X	fx.fx_name = "SYS:EXEC.EXE";
X	fx.fx_argv = fx.fx_envp = NULL;
X	if (forkexec(&fx) < 0)
X		writerr("Cannot run EXEC");
X    }
X#else /*-TOPS20*/
X	switch(pid = fork())
X	  {	case -1:
X			writerr("Cannot fork");
X			break;
X		case 0:		/* We're the child */
X			for(res = 3; res < 20;)	/* Don't let inf hack fd's */
X				close(res++);
X#if V6
X			execl("/bin/sh","-sh",0);
X#else
X			signal(SIGINT, SIG_DFL);	/* V7 shell wants this?? */
X			signal(SIGQUIT, SIG_DFL);	/*	*/
X#if IMAGEN
X			if((shellname = getenv("SHELL")) == 0)
X				shellname = "sh";
X			strcpy(fullshell, "/bin/");
X			strcat(fullshell, shellname);
X			shellname = fullshell;
X#else
X			if((shellname = getenv("SHELL")) == 0)
X				shellname = "/bin/sh";
X#endif /*-IMAGEN*/
X
X			if((shellname = getenv("SHELL")) == 0)
X				shellname = "/bin/sh";
X			execl(shellname, "shell", "-i", 0);
X#endif /*-V6*/
X			writerr("No shell!");
X			exit(1);
X			break;
X		default:
X			while((res = wait(&status)) != pid && res != -1);
X			break;
X	  }
X#endif /*-TOPS20*/
X
X	signal(SIGINT, sav2);		/* Restore signal settings */
X	signal(SIGQUIT, sav3);
X	set_tty();			/* Restore editor TTY modes */
X	redp(RD_SCREEN|RD_MODE);	/* Done, redisplay */
X}
X
X/* Miscellaneous utility routines - memory alloc/free and string hacking.
X * If this page becomes overly large, it can be split off into a separate
X * file called E_MISC.
X */
Xchar *
Xstrdup(s)
Xchar *s;	/* Note that STRCPY's return val must be its 1st arg */
X{	char *strcpy();
X	return(strcpy(memalloc((SBMO)(strlen(s)+1)), s));
X}
X
Xchar *
Xmemalloc(size)
XSBMO size;
X{	register SBMA ptr;
X	extern SBMA sbx_malloc();
X
X	if ((ptr = (SBMA)sbx_malloc(size)) != 0)
X		return((char *)ptr);
X	barf("ELLE: No memory left");
X	askerr();
X	return(0);		/* If we dare to continue... */
X}
X
Xchkfree (ptr)
XSBMA ptr;
X{
X	if(!free(ptr))
X	  {	errbarf("Something overwrote an allocated block!");
X		askerr();
X	  }
X}
X
X
X/* USTRCMP - Uppercase String Compare.
X *	Returns 0 if mismatch,
X *		1 if full match,
X *		-1 if str1 runs out first (partial match)
X */
Xustrcmp(str1,str2)
Xchar *str1, *str2;
X{	register char *s1, *s2;
X	register int c;
X	s1 = str1; s2 = str2;
X	while(c = *s1++)
X	  {	if(c != *s2 && upcase(c) != upcase(*s2))
X			return(0);
X		s2++;
X	  }
X	return(c == *s2 ? 1 : -1);
X}
X
X
X/* WRITERR(str) - Output string to standard error output.
X**	This is a separate routine to save a little space on calls.
X*/
Xwriterr(str)
Xchar *str;
X{	return(writez(2, str));
X}
X
X/* WRITEZ(fd, str) - Miscellaneous general-purpose string output.
X */
Xwritez(fd,acp)
Xint fd;
Xchar *acp;
X{	register char *cp;
X	cp = acp;
X	while(*cp++);
X	write(fd,acp,cp-acp-1);
X}
/
echo x - eemake.c
sed '/^X/s///' > eemake.c << '/'
X/* ELLE - Copyright 1982, 1985, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/* EEMAKE - IMAGEN configuration functions for interfacing to "make".
X *	Written by (I think) Chris Ryland at IMAGEN, who also
X * wrote other functions scattered through ELLE.  These are either
X * conditionalized or are commented as being derived from the IMAGEN
X * configuration.
X *
X * KLH: An attempt has been made to keep these routines updated as ELLE
X * changed, but their workings cannot be guaranteed.
X */
X
X
X/*
X * eemake: "make" (and other program) support
X *
X * Next-error depends on programs writing error messages of the form:
X *  "file", line n: message
X * which is a de facto standard, at least in some worlds.
X */
X
X#include "elle.h"
X
X#if !(IMAGEN)		/* Only with IMAGEN config for now */
Xf_xucmd() {}
Xf_make() {}
Xf_nxterr() {}
X#else
X
X#include <stdio.h>
X
Xstruct buffer *exec_buf;	/* Ptr to "Execution" buffer */
X				/* Only external ref is from e_buff.c */
X
X#define MSGLENGTH (scr_wid - 11)	/* Max length of message */
Xint fresh_make = 1;			/* Fresh Execution buffer contents */
Xchroff last_error_BOL;			/* Dot for last-error's BOL */
X
X/* EFUN: "Execute Unix Command" */
Xf_xucmd()
X{
X	make_or_unix_cmd(0);
X}
X
X/* EFUN: "Execute Make" */
Xf_make()
X{
X	make_or_unix_cmd(1);
X}
X
X/* EFUN: "Find Next Error" */
Xf_nxterr()
X{
X	register struct sbstr *sb;
X	register char *cp;
X	register int c;
X	char file[64], line[32];
X#ifdef ONEWINDOW
X	char msg[512];
X#endif
X	chroff linedot;
X	int lineno;
X	register int len;
X
X	sel_execbuf();
X	if (! fresh_make)
X	  {	e_go(last_error_BOL);
X		e_gonl();
X	  }
X	else
X	  {	fresh_make = 0;
X		e_gobob();
X		last_error_BOL = e_dot();
X	  }
X
X	/* Looking for `"file", line n: msg' */
X	if (! e_search("\", line ", 8, 0))
X		goto failed;
X	linedot = e_dot();
X	e_gobol();			/* Found something, get to BOL */
X	if (e_getc() != '"')
X		goto failed;		/* Insist on beginning "file" */
X	cp = file;			/* Pick up filename */
X	while ((c = e_getc()) != '"')
X		*cp++ = c;
X	*cp = 0;
X	e_go(linedot);			/* Back to after "line " */
X	cp = line;
X	while ((c = e_getc()) >= '0' && c <= '9')
X		*cp++ = c;
X	*cp = 0;
X	lineno = atoi(line);		/* Pick up line number */
X#ifdef ONEWINDOW
X	cp = msg;			/* Now get rest of line to msg */
X	len = 0;			/* But keep length <= MSGLENGTH */
X	e_getc();			/* Go past purported space */
X	while ((c = e_getc()) != LF && c != EOF && len <= MSGLENGTH)
X	  {	if (c == '\t')
X			len = (len + 8) & ~07;
X		else if (c < ' ' || c > 0176)
X			len += 2;
X		else
X			++len;
X		*cp++ = c;
X	  }
X	*cp = 0;
X	if (len > MSGLENGTH)
X		strcat(msg, "...");
X#ifdef DEBUG
X	say("file ");
X	saytoo(file);
X	saytoo(", line ");
X	saytoo(line);
X	saytoo(", msg: ");
X	sayntoo(msg);
X#else
X	say(line);
X	saytoo(": ");
X	sayntoo(msg);
X#endif /*DEBUG*/
X#else /* -ONEWINDOW */
X	f_begline();			/* Get to start of line */
X	last_error_BOL = e_dot();	/* Remember this position */
X	exp_p = 1;			/* Make this the new top line */
X	exp = 0;
X	f_newwin();
X	upd_wind(0);
X#endif /*ONEWINDOW*/
X
X	/* Now, visit the given file and line */
X#ifdef ONEWINDOW	
X#else
X	f_othwind();			/* To other window */
X#endif
X	find_file(file);
X	f_gobeg();
X	down_line(lineno - 1);
X#ifdef READJUST				/* NAW */
X	exp_p = 1;
X	exp = cur_win->w_ht / 4;	/* Adjust how we look at "error" */
X	f_newwin();
X#endif /*READJUST*/
X	return;
X
Xfailed:	ding("No more errors!");
X	fresh_make = 1;			/* Fake-out: pretend starting over */
X	return;
X}
X
X
X/* Do the "cmd" and put its output in the Execution buffer */
Xdo_exec(cmd, nicely)
Xchar *cmd;
Xint nicely;
X{
X	register int n;
X	int status, res, pid, fd[2];
X	char nicecmd[256];
X	char pipebuff[512];
X	struct buffer *b;
X
X	b = cur_buf;
X	sel_execbuf();			/* Get our execution buffer up */
X	ed_reset();			/* Clear it out */
X	fresh_make = 1;
X	upd_wind(0);
X	if (nicely)
X		sayntoo(" ...starting up...");
X	else
X		sayntoo(" ...starting up (nasty person)...");
X	pipe(fd);
X	switch (pid = fork())
X	  {
X	case -1:
X		/* Fork failed, in parent */
X		ding("Cannot fork!?!");
X		break;
X
X	case 0: /* In child */
X		for (n = 0; n < 20; ++n)
X			if (n != fd[0] && n != fd[1])
X				close(n);
X		open("/dev/null", 0);	/* Give ourselves empty stdin */
X		dup(fd[1]);
X		dup(fd[1]);		/* stdout, stderr out to pipe */
X		close(fd[1]);		/* Close the pipe itself */
X		close(fd[0]);
X		if (nicely)
X		  {	strcpy(nicecmd, "nice -4 ");
X			strcat(nicecmd, cmd);
X			execl("/bin/sh", "sh", "-c", nicecmd, 0);
X		  }
X		else
X			execl("/bin/sh", "sh", "-c", cmd, 0);
X		write(1, "Cannot execute!", 15);
X		_exit(-1);
X		break;
X
X	default:
X		/* Parent */
X		close(fd[1]);		/* Close the output direction */
X		while ((n = read(fd[0], pipebuff, sizeof(pipebuff))) > 0)
X		  {	ed_nsins(pipebuff, n);
X			upd_wind(0);
X			saynow("Chugging along...");
X		  }
X		close(fd[0]);
X		while ((res = wait(&status)) != pid && res != -1)
X			;		/* Wait for this fork to die */
X		f_bufnotmod();		/* Buffer is fresh */
X		saynow("Done!");
X		break;
X	  }
X	f_othwind();			/* Back to other window */
X	chg_buf(b);			/* Back to original buffer */
X}
X
Xchar last_make_cmd[256];		/* Last Unix/make command */
Xint have_last_make_cmd = 0;
X
Xmake_or_unix_cmd(domake)
Xint domake;
X{
X#if APOLLO
X	register int nicely = exp == 16;	/* modification for apollo */
X#else
X	register int nicely = exp != 16;
X#endif /*-APOLLO*/
X	register char *reply, *cmd = 0;
X
X	if (domake)			/* If "make"-style, */
X	  {	int savp = exp_p;
X		exp_p = 1;
X		f_savefiles();		/*  write modified files quietly */
X		exp_p = savp;
X	  }
X	if (exp_p || ! domake)
X	  {		/* Arg given make, or Unix command */
X		reply = ask((! domake) ? "Unix command: " : "Command: ");
X		if (! reply)
X			return;
X		if (*reply == 0)
X		  {	if (have_last_make_cmd)
X				cmd = &last_make_cmd[0];
X			else
X			  {	chkfree(reply);
X				ding("No previous command!");
X				return;
X			  }
X		  }
X		else
X			cmd = reply;
X		if (cmd != &last_make_cmd[0])	/* Update last command */
X			strcpy(last_make_cmd, cmd);
X		have_last_make_cmd = 1;
X		say("Command: ");
X		sayntoo(cmd);
X		do_exec(cmd, nicely);
X		chkfree(reply);
X	  }
X	else if (have_last_make_cmd)
X	  {	say("Command: ");
X		sayntoo(last_make_cmd);
X		do_exec(last_make_cmd, nicely);
X	  }
X	else
X	  {	saynow("Command: make");
X		do_exec("make", nicely);
X	  }
X}
X
Xsel_execbuf()
X{	if(!exec_buf)
X	  {	/* Make execution buffer; don't let anyone kill it */
X		exec_buf = make_buf("Execution");
X		exec_buf->b_flags |= B_PERMANENT;
X	  }
X	popto_buf(exec_buf);
X}
X
X/* Utility: pop the given buffer to a window, getting into 2-window mode */
Xpopto_buf(b)
Xregister struct buffer *b;
X{
X	/* See if we already have this buffer in a visible window */
X	if (b == cur_win->w_buf)
X	  {	if (oth_win == 0)
X		  {	f_2winds();
X			f_othwind();		/* Get back to our window */
X		  }
X	  }
X	else if (oth_win != 0 && b == oth_win->w_buf)
X		f_othwind();
X	else if (oth_win == 0)
X	  {		/* One window mode */
X		f_2winds();			/* Get two, get into second */
X		sel_buf(b);			/* Select our new buffer */
X	  }
X	else
X	  {	f_othwind();			/* Get to other window */
X		sel_buf(b);			/*  and select our buffer */
X	  }
X}
X
X#endif /*IMAGEN*/
/
echo x - eeprof.h
sed '/^X/s///' > eeprof.h << '/'
X#define PROF_VER (1)
X
Xstruct profile {  int version;
Xint chrvcnt;  char *chrvec; 
Xint metavcnt; char *metavec;
Xint extvcnt;  char *extvec; 
Xint menuvcnt; char *menuvec;
X};
/
echo x - eeproto.h
sed '/^X/s///' > eeproto.h << '/'
X#ifndef _ANSI
X#include <ansi.h>
X#endif
X
X/* eebit.c */
X_PROTOTYPE( int *chballoc, (int size) );
X_PROTOTYPE( int chbit, (int *array, int c) );
X_PROTOTYPE( int chbis, (int *array, int c) );
X_PROTOTYPE( int chbic, (int *array, int c) );
X
X/* eebuff.c */
X_PROTOTYPE( int f_selbuffer, (void) );
X_PROTOTYPE( int f_selxbuffer, (void) );
X_PROTOTYPE( int f_kbuffer, (void) );
X_PROTOTYPE( int f_listbufs, (void) );
X_PROTOTYPE( int f_bufnotmod, (void) );
X_PROTOTYPE( int f_eolmode, (void) );
X_PROTOTYPE( int f_gobeg, (void) );
X_PROTOTYPE( int f_goend, (void) );
X_PROTOTYPE( int f_whatpage, (void) );
X_PROTOTYPE( int init_buf, (void) );
X_PROTOTYPE( struct buffer *make_buf, (char *bname) );
X_PROTOTYPE( struct buffer *find_buf, (char *name) );
X_PROTOTYPE( int sel_buf, (struct buffer *b) );
X_PROTOTYPE( int chg_buf, (struct buffer *newbuf) );
X_PROTOTYPE( int unlk_buf, (struct buffer *bufp) );
X_PROTOTYPE( struct buffer *sel_mbuf, (struct buffer *buf) );
X_PROTOTYPE( struct buffer *sel_mbuf, (struct buffer *buf) );
X_PROTOTYPE( struct buffer *sel_nbuf, (struct buffer *buf) );
X_PROTOTYPE( int kill_buf, (struct buffer *buf) );
X_PROTOTYPE( int zap_buffer, (void) );
X_PROTOTYPE( int ask_kbuf, (struct buffer *buf) );
X_PROTOTYPE( int f_2winds, (void) );
X_PROTOTYPE( int f_1wind, (void) );
X_PROTOTYPE( int f_othwind, (void) );
X_PROTOTYPE( int f_growind, (void) );
X_PROTOTYPE( int f_shrinkwind, (void) );
X_PROTOTYPE( int f_delwind, (void) );
X_PROTOTYPE( int f_sowind, (void) );
X_PROTOTYPE( int f_2modewinds, (void) );
X_PROTOTYPE( int chk2modws, (void) );
X_PROTOTYPE( int init_win, (void) );
X_PROTOTYPE( int chg_win, (struct window *newwin) );
X_PROTOTYPE( struct window *make_win, (int pos, int ht, struct buffer *buf) );
X_PROTOTYPE( int kill_win, (struct window *win) );
X_PROTOTYPE( int mk_showin, (struct buffer *b) );
X_PROTOTYPE( struct window *make_mode, (struct window *bw) );
X_PROTOTYPE( int buf_mod, (void) );
X_PROTOTYPE( int buf_tmat, (chroff dot) );
X_PROTOTYPE( int buf_tmod, (chroff offset) );
X
X/* eecmds.c */
X_PROTOTYPE( int f_pfxmeta, (void) );
X_PROTOTYPE( int f_pfxext, (void) );
X_PROTOTYPE( int f_uarg, (int ch) );
X_PROTOTYPE( int f_negarg, (int ch) );
X_PROTOTYPE( int f_argdig, (int ch) );
X_PROTOTYPE( int f_setprof, (void) );
X_PROTOTYPE( int f_vtbuttons, (void) );
X_PROTOTYPE( int cmd_wait, (void) );
X_PROTOTYPE( int cmd_read, (void) );
X_PROTOTYPE( int cmd_xct, (int ch) );
X_PROTOTYPE( int cmd_idx, (int c) );
X_PROTOTYPE( int set_profile, (char *filename) );
X_PROTOTYPE( int init_menu, (void) );
X
X/* eediag.c */
X_PROTOTYPE( int f_debug, (int ch) );
X_PROTOTYPE( char *vfy_data, (int flag) );
X_PROTOTYPE( int dbg_diag, (void) );
X_PROTOTYPE( int vfy_exer, (int pf, int gcfrq) );
X_PROTOTYPE( int db_prwind, (struct window *w) );
X_PROTOTYPE( char *db_scflgs, (int flags) );
X
X/* eedisp.c */
X_PROTOTYPE( int set_tty, (void) );
X_PROTOTYPE( int clean_exit, (void) );
X_PROTOTYPE( int set_scr, (void) );
X_PROTOTYPE( int redisplay, (void) );
X_PROTOTYPE( int fupd_wind, (struct window *w) );
X_PROTOTYPE( int upd_curs, (chroff adot) );
X_PROTOTYPE( int d_line, (chroff cdot) );
X_PROTOTYPE( int d_ncols, (int lcnt, int ccol) );
X_PROTOTYPE( int d_lupd, (struct window *w, int idx) );
X_PROTOTYPE( int clear_wind, (struct window *w) );
X_PROTOTYPE( int fix_wind, (struct window *win) );
X_PROTOTYPE( int inwinp, (struct window *win, chroff cdot) );
X_PROTOTYPE( int upd_wind, (struct window *win) );
X_PROTOTYPE( int slineq, (struct scr_line *olds, struct scr_line *news) );
X_PROTOTYPE( int upd_line, (int y) );
X_PROTOTYPE( int fillset, (char *str, int cnt, int c) );
X_PROTOTYPE( int fillsp, (char *str, int cnt) );
X_PROTOTYPE( int inspc, (char *cp0, char *cpl, int cnt) );
X_PROTOTYPE( int fix_line, (struct scr_line *slp, struct scr_line *olds) );
X_PROTOTYPE( int sctrin, (char *to, int lim, int ccol) );
X_PROTOTYPE( int inslin, (int line, int n, struct window *win) );
X_PROTOTYPE( int dellin, (int line, int n, struct window *win) );
X_PROTOTYPE( int t_dostandout, (int on) );
X_PROTOTYPE( int t_move, (int y, int x) );
X_PROTOTYPE( int t_docleol, (void) );
X
X/* eeedit.c */
X_PROTOTYPE( int e_reset, (void) );
X_PROTOTYPE( int e_rgetc, (void) );
X_PROTOTYPE( int e_rdelc, (void) );
X_PROTOTYPE( int e_delc, (void) );
X_PROTOTYPE( int e_getc, (void) );
X_PROTOTYPE( int e_backc, (void) );
X_PROTOTYPE( int e_putc, (int c) );
X_PROTOTYPE( int e_peekc, (void) );
X_PROTOTYPE( int e_ovwc, (int ch) );
X_PROTOTYPE( SBSTR *e_copyn, (chroff off) );
X_PROTOTYPE( int e_deln, (chroff off) );
X_PROTOTYPE( int e_setcur, (void) );
X_PROTOTYPE( int e_gosetcur, (chroff dot) );
X_PROTOTYPE( int e_gocur, (void) );
X_PROTOTYPE( int e_gobob, (void) );
X_PROTOTYPE( int e_goeob, (void) );
X_PROTOTYPE( int e_go, (chroff dot) );
X_PROTOTYPE( int e_igoff, (int ioff) );
X_PROTOTYPE( int e_goff, (chroff off) );
X_PROTOTYPE( int e_gobol, (void) );
X_PROTOTYPE( int e_goeol, (void) );
X_PROTOTYPE( int e_gonl, (void) );
X_PROTOTYPE( int e_gopl, (void) );
X_PROTOTYPE( chroff e_dot, (void) );
X_PROTOTYPE( chroff e_nldot, (void) );
X_PROTOTYPE( chroff e_pldot, (void) );
X_PROTOTYPE( chroff e_boldot, (void) );
X_PROTOTYPE( chroff e_eoldot, (void) );
X_PROTOTYPE( chroff e_alldot, (SBBUF *sbp, int (*rtn )()) );
X_PROTOTYPE( chroff e_blen, (void) );
X_PROTOTYPE( int ex_reset, (struct buffer *b) );
X_PROTOTYPE( int ex_go, (SBBUF *sbp, chroff loc) );
X_PROTOTYPE( chroff ex_dot, (SBBUF *sbp) );
X_PROTOTYPE( chroff ex_boldot, (SBBUF *sbp, chroff dot) );
X_PROTOTYPE( chroff ex_alldot, (SBBUF *sbp, int (*rtn )(), chroff dot) );
X_PROTOTYPE( int ex_gonl, (SBBUF *sbp) );
X_PROTOTYPE( int ex_goeol, (SBBUF *sbp) );
X_PROTOTYPE( int ex_gobol, (SBBUF *sbp) );
X_PROTOTYPE( int ex_gopl, (SBBUF *sbp) );
X_PROTOTYPE( chroff ex_blen, (SBBUF *sbp) );
X_PROTOTYPE( int e_gofwsp, (void) );
X_PROTOTYPE( int e_gobwsp, (void) );
X_PROTOTYPE( int e_goline, (int i) );
X_PROTOTYPE( int e_lblankp, (void) );
X_PROTOTYPE( int e_insn, (int ch, int cnt) );
X_PROTOTYPE( int e_sputz, (char *acp) );
X_PROTOTYPE( int boleq, (chroff dot1, chroff dot2) );
X_PROTOTYPE( char *dottoa, (char *str, chroff val) );
X_PROTOTYPE( int e_gobpa, (void) );
X_PROTOTYPE( int e_goepa, (void) );
X_PROTOTYPE( int exp_do, (int (*rpos )(), int (*rneg )()) );
X_PROTOTYPE( int e_fwsp, (void) );
X_PROTOTYPE( int e_bwsp, (void) );
X_PROTOTYPE( int c_wsp, (int ch) );
X_PROTOTYPE( int c_pwsp, (int ch) );
X_PROTOTYPE( int delimp, (int c) );
X_PROTOTYPE( int e_wding, (chroff *adot, int n) );
X_PROTOTYPE( chroff e_wdot, (chroff dot, int n) );
X_PROTOTYPE( int e_gowd, (int n) );
X_PROTOTYPE( int e_search, (char *mstr, int mlen, int backwards) );
X
X/* eeerr.c */
X_PROTOTYPE( int f_bkpt, (void) );
X_PROTOTYPE( int bpt, (void) );
X_PROTOTYPE( char *strerror, (int num) );
X_PROTOTYPE( int errsbm, (int type, int (*adr )(), char *str, int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12) );
X_PROTOTYPE( int bite_bag, (int fault) );
X_PROTOTYPE( int hup_exit, (void) );
X_PROTOTYPE( int errint, (void) );
X_PROTOTYPE( int askerr, (void) );
X_PROTOTYPE( char *asklin, (char *acp) );
X
X/* eef1.c */
X_PROTOTYPE( int f_insself, (int c) );
X_PROTOTYPE( int f_quotins, (void) );
X_PROTOTYPE( int f_crlf, (void) );
X_PROTOTYPE( int f_fchar, (void) );
X_PROTOTYPE( int f_bchar, (void) );
X_PROTOTYPE( int f_dchar, (void) );
X_PROTOTYPE( int f_bdchar, (void) );
X_PROTOTYPE( int ef_deln, (int x) );
X_PROTOTYPE( int f_delspc, (void) );
X_PROTOTYPE( int f_tchars, (void) );
X_PROTOTYPE( int f_fword, (void) );
X_PROTOTYPE( int f_bword, (void) );
X_PROTOTYPE( int f_kword, (void) );
X_PROTOTYPE( int f_bkword, (void) );
X_PROTOTYPE( int f_twords, (void) );
X_PROTOTYPE( int f_ucword, (void) );
X_PROTOTYPE( int f_lcword, (void) );
X_PROTOTYPE( int f_uciword, (void) );
X_PROTOTYPE( int case_word, (int downp) );
X
X/* eef2.c */
X_PROTOTYPE( int f_begline, (void) );
X_PROTOTYPE( int f_endline, (void) );
X_PROTOTYPE( int f_nxtline, (void) );
X_PROTOTYPE( int f_prvline, (void) );
X_PROTOTYPE( int f_dnrline, (void) );
X_PROTOTYPE( int f_uprline, (void) );
X_PROTOTYPE( int f_oline, (void) );
X_PROTOTYPE( int f_delblines, (void) );
X_PROTOTYPE( int f_kline, (void) );
X_PROTOTYPE( int f_bkline, (void) );
X_PROTOTYPE( int f_goline, (void) );
X_PROTOTYPE( int down_bline, (int arg) );
X_PROTOTYPE( int down_line, (int x) );
X_PROTOTYPE( int f_setmark, (void) );
X_PROTOTYPE( int f_exchmark, (void) );
X_PROTOTYPE( int f_kregion, (void) );
X_PROTOTYPE( int f_copreg, (void) );
X_PROTOTYPE( int f_ucreg, (void) );
X_PROTOTYPE( int f_lcreg, (void) );
X_PROTOTYPE( int ef_creg, (int downp) );
X_PROTOTYPE( int f_fillreg, (void) );
X_PROTOTYPE( int chkmark, (void) );
X_PROTOTYPE( int f_fpara, (void) );
X_PROTOTYPE( int f_bpara, (void) );
X_PROTOTYPE( int f_mrkpara, (void) );
X_PROTOTYPE( int f_fillpara, (void) );
X
X/* eef3.c */
X_PROTOTYPE( int f_appnkill, (void) );
X_PROTOTYPE( int f_unkill, (void) );
X_PROTOTYPE( int f_unkpop, (void) );
X_PROTOTYPE( int f_indatm, (void) );
X_PROTOTYPE( int f_indnl, (void) );
X_PROTOTYPE( int f_backind, (void) );
X_PROTOTYPE( int f_indcomm, (void) );
X_PROTOTYPE( int f_indrel, (void) );
X_PROTOTYPE( int insertmatch, (int c) );
X_PROTOTYPE( int f_matchbrack, (void) );
X_PROTOTYPE( int matchonelevel, (int mc) );
X
X/* eefd.c */
X_PROTOTYPE( int f_newwin, (void) );
X_PROTOTYPE( int f_nscreen, (void) );
X_PROTOTYPE( int f_pscreen, (void) );
X_PROTOTYPE( int f_othnscreen, (void) );
X_PROTOTYPE( int f_lwindbord, (void) );
X_PROTOTYPE( int f_scupwind, (void) );
X_PROTOTYPE( int f_scdnwind, (void) );
X_PROTOTYPE( int f_mvwtop, (void) );
X_PROTOTYPE( int f_mvwbot, (void) );
X_PROTOTYPE( int d_screen, (int rep) );
X_PROTOTYPE( int scroll_win, (int n) );
X_PROTOTYPE( int d_curind, (void) );
X_PROTOTYPE( int indtion, (chroff lin) );
X_PROTOTYPE( int inindex, (chroff lin, int xpos) );
X_PROTOTYPE( int d_gopl, (void) );
X_PROTOTYPE( int d_gonl, (void) );
X_PROTOTYPE( int d_goloff, (int cnt) );
X_PROTOTYPE( int d_fgoloff, (int cnt) );
X_PROTOTYPE( int d_fixcur, (void) );
X_PROTOTYPE( int d_backup, (int nlin) );
X
X/* eefed.c */
X_PROTOTYPE( int ed_insert, (int c) );
X_PROTOTYPE( int ed_insn, (int ch, int cnt) );
X_PROTOTYPE( int ed_crins, (void) );
X_PROTOTYPE( int ed_sins, (char *s) );
X_PROTOTYPE( int ed_nsins, (char *s, int i) );
X_PROTOTYPE( int ed_indto, (int goal) );
X_PROTOTYPE( int ed_setcur, (void) );
X_PROTOTYPE( int ed_go, (chroff dot) );
X_PROTOTYPE( int ed_goff, (chroff off) );
X_PROTOTYPE( int ed_igoff, (int ioff) );
X_PROTOTYPE( int ed_reset, (void) );
X_PROTOTYPE( int ed_deln, (chroff off) );
X_PROTOTYPE( int ed_delete, (chroff dot1, chroff dot2) );
X_PROTOTYPE( int ed_kill, (chroff dot1, chroff dot2) );
X_PROTOTYPE( int kill_push, (SBSTR *sdp) );
X_PROTOTYPE( int ed_case, (chroff dot1, chroff dot2, int downp) );
X_PROTOTYPE( int upcase, (int ch) );
X
X/* eefile.c */
X_PROTOTYPE( int f_ffile, (void) );
X_PROTOTYPE( int f_rfile, (void) );
X_PROTOTYPE( int f_vfile, (void) );
X_PROTOTYPE( int u_r_file, (char *prompt) );
X_PROTOTYPE( int f_ifile, (void) );
X_PROTOTYPE( int f_sfile, (void) );
X_PROTOTYPE( int f_savefiles, (void) );
X_PROTOTYPE( int f_wfile, (void) );
X_PROTOTYPE( int f_wreg, (void) );
X_PROTOTYPE( int f_wlastkill, (void) );
X_PROTOTYPE( int hack_file, (char *prompt, int (*rtn )()) );
X_PROTOTYPE( int find_file, (char *f_name) );
X_PROTOTYPE( int read_file, (char *f_name) );
X_PROTOTYPE( int ins_file, (char *f_name) );
X_PROTOTYPE( int ferr_ropn, (void) );
X_PROTOTYPE( int ferr_wopn, (void) );
X_PROTOTYPE( int ferr, (char *str) );
X_PROTOTYPE( char *fncons, (char *dest, char *pre, char *f_name, char *post) );
X_PROTOTYPE( char *last_fname, (char *f_name) );
X_PROTOTYPE( int set_fn, (char *string) );
X_PROTOTYPE( int saveworld, (struct buffer *bp, int grunt) );
X_PROTOTYPE( int hoard, (void) );
X_PROTOTYPE( int unhoard, (void) );
X_PROTOTYPE( int expand_file, (char *dfn, char *sfn) );
X
X/* eefill.c */
X_PROTOTYPE( int f_sfcol, (void) );
X_PROTOTYPE( int f_sfpref, (void) );
X_PROTOTYPE( int tstfillp, (int lim) );
X_PROTOTYPE( int ed_fill, (chroff begloc, chroff endloc, int flag) );
X_PROTOTYPE( int f_fillmode, (void) );
X_PROTOTYPE( int fx_insfill, (int c) );
X_PROTOTYPE( int fill_cur_line, (void) );
X_PROTOTYPE( int f_textmode, (void) );
X_PROTOTYPE( int fim_insself, (int c) );
X_PROTOTYPE( int fim_dchar, (void) );
X_PROTOTYPE( int fim_bdchar, (void) );
X_PROTOTYPE( int fim_crlf, (void) );
X_PROTOTYPE( int magic_wrap, (int tc) );
X_PROTOTYPE( int reveal, (char *msg, int v1, int v2, int v3) );
X_PROTOTYPE( int magic_backto_bol, (void) );
X_PROTOTYPE( int fill_current_line, (void) );
X
X/* eehelp.c */
X_PROTOTYPE( int f_describe, (void) );
X
X/* eekmac.c */
X_PROTOTYPE( int f_skmac, (void) );
X_PROTOTYPE( int f_ekmac, (void) );
X_PROTOTYPE( int f_xkmac, (void) );
X_PROTOTYPE( int f_vkmac, (void) );
X_PROTOTYPE( int km_getc, (void) );
X_PROTOTYPE( int km_inwait, (void) );
X_PROTOTYPE( int km_abort, (void) );
X_PROTOTYPE( int add_mode, (char *mode) );
X_PROTOTYPE( int remove_mode, (char *mode) );
X
X/* eemain.c */
X_PROTOTYPE( int doargs, (int argc, char **argv) );
X_PROTOTYPE( int initialize, (void) );
X_PROTOTYPE( int f_throw, (void) );
X_PROTOTYPE( int ring_bell, (void) );
X_PROTOTYPE( int f_retsup, (void) );
X_PROTOTYPE( int f_wfexit, (void) );
X_PROTOTYPE( int f_pshinf, (void) );
X_PROTOTYPE( char *strdup, (char *s) );
X_PROTOTYPE( char *memalloc, (SBMO size) );
X_PROTOTYPE( int chkfree, (SBMA ptr) );
X_PROTOTYPE( int ustrcmp, (char *str1, char *str2) );
X_PROTOTYPE( int writerr, (char *str) );
X_PROTOTYPE( int writez, (int fd, char *acp) );
X
X/* eemake.c */
X_PROTOTYPE( int f_xucmd, (void) );
X_PROTOTYPE( int f_make, (void) );
X_PROTOTYPE( int f_nxterr, (void) );
X_PROTOTYPE( int f_xucmd, (void) );
X_PROTOTYPE( int f_make, (void) );
X_PROTOTYPE( int f_nxterr, (void) );
X_PROTOTYPE( int do_exec, (char *cmd, int nicely) );
X_PROTOTYPE( int make_or_unix_cmd, (int domake) );
X_PROTOTYPE( int sel_execbuf, (void) );
X_PROTOTYPE( int popto_buf, (struct buffer *b) );
X
X/* eequer.c */
X_PROTOTYPE( int f_querep, (void) );
X_PROTOTYPE( int f_repstr, (void) );
X_PROTOTYPE( int f_repline, (void) );
X_PROTOTYPE( int ed_dorep, (int type, struct majmode *mode) );
X
X/* eeques.c */
X#if 0
X_PROTOTYPE( char *ask, (char *string, char *arg1, char *arg2, char *arg3) );
X#else
Xchar *ask();
X#endif
X_PROTOTYPE( int askclr, (void) );
X_PROTOTYPE( int say, (char *str) );
X_PROTOTYPE( int saynow, (char *str) );
X_PROTOTYPE( int saytoo, (char *str) );
X_PROTOTYPE( int sayntoo, (char *str) );
X_PROTOTYPE( int ding, (char *str) );
X_PROTOTYPE( int dingtoo, (char *str) );
X_PROTOTYPE( int saylntoo, (char *str, int n) );
X_PROTOTYPE( int sayclr, (void) );
X#if 0
X_PROTOTYPE( int sayall, (char *str, int flags, int len) );
X#else
Xint sayall();
X#endif
X_PROTOTYPE( int yellat, (char *str, int line) );
X_PROTOTYPE( int yelltoo, (char *str) );
X_PROTOTYPE( int errbarf, (char *str) );
X_PROTOTYPE( int barf, (char *str) );
X_PROTOTYPE( int barf2, (char *str) );
X
X/* eesite.c */
X_PROTOTYPE( int ts_inp, (void) );
X_PROTOTYPE( int ts_init, (void) );
X_PROTOTYPE( int ts_enter, (void) );
X_PROTOTYPE( int ts_exit, (void) );
X_PROTOTYPE( int tpoke, (int cmd, int bn, int val) );
X_PROTOTYPE( int ts_pause, (void) );
X
X/* eesrch.c */
X_PROTOTYPE( int f_srch, (void) );
X_PROTOTYPE( int f_rsrch, (void) );
X_PROTOTYPE( int lin_search, (int backwards) );
X_PROTOTYPE( int srchint, (void) );
X_PROTOTYPE( char *srch_ask, (char *prompt) );
X_PROTOTYPE( int f_risrch, (void) );
X_PROTOTYPE( int i_search, (int back) );
X
X/* eeterm.c */
X_PROTOTYPE( int t_init, (void) );
X_PROTOTYPE( int t_fatal, (char *str) );
X_PROTOTYPE( int t_enter, (void) );
X_PROTOTYPE( int t_exit, (void) );
X_PROTOTYPE( int t_clear, (void) );
X_PROTOTYPE( int t_curpos, (int lin, int col) );
X_PROTOTYPE( int t_backspace, (void) );
X_PROTOTYPE( int t_bell, (void) );
X_PROTOTYPE( int t_cleol, (void) );
X_PROTOTYPE( int t_inslin, (int n, int bot) );
X_PROTOTYPE( int t_dellin, (int n, int bot) );
X_PROTOTYPE( int t_inschr, (int n, char *str) );
X_PROTOTYPE( int t_delchr, (int n) );
X_PROTOTYPE( int t_standout, (int on) );
X_PROTOTYPE( int t_direct, (int lin, int col, char *str, int len) );
X_PROTOTYPE( int tput, (int ch) );
X_PROTOTYPE( int tputz, (char *str) );
X_PROTOTYPE( int tputn, (char *str, int cnt) );
X_PROTOTYPE( int tbufls, (void) );
X_PROTOTYPE( int tgetc, (void) );
X_PROTOTYPE( int tinwait, (void) );
X
X/* eevini.c */
/
echo x - eequer.c
sed '/^X/s///' > eequer.c << '/'
X/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*
X * EEQUER	Query-Replace and Replace-String functions
X */
X
X#include "elle.h"		       /* include structure definitions */
X
X/* EFUN: "Query Replace" */
X/*	Crude approximation of EMACS function.
X */
Xf_querep()
X{	static struct majmode iqrpmode = { "Query Replace" };
X	ed_dorep(0, &iqrpmode);
X}
X
X/* EFUN: "Replace String" */
X/*	Similar to Query Replace and uses same code.
X */
Xf_repstr()
X{	static struct majmode irepmode = { "Replace String" };
X	ed_dorep(1, &irepmode);
X}
X
X#if FX_REPLINE
X/* EFUN: "Replace in Line" (not EMACS) */
X/*	Acts like Replace String but only operates on current line.
X**	Currently a big crock.
X**	Feature of crockishness is that Unkill Pop (M-Y) will restore old
X**	line.
X*/
Xf_repline()
X{
X	extern struct buffer *make_buf();
X	struct buffer *b, *oldb = cur_buf;
X	static struct majmode rlmode = { "Replace in Line" };
X
X	if(!(b = make_buf(" **LINE**")))
X	  {	ring_bell();
X		return;
X	  }
X	f_kline();		/* Kill line(s) from original buffer */
X	chg_buf(b);		/* Switch to temp buffer */
X	f_unkill();		/* Get killed stuff into temp buffer */
X	e_gosetcur((chroff)0);	/* Starting at beginning, */
X	ed_dorep(1, &rlmode);		/* Execute Replace String on it. */
X	ed_kill((chroff)0, e_blen());	/* Now kill everything in it, */
X	chg_buf(oldb);		/* switch back to original buffer, */
X	f_unkill();		/* and restore new stuff! */
X	kill_buf(b);		/* Now flush temporary buffer. */
X}
X#endif
X
X
X/* Note that the major mode is set without changing the buffer's major
X * mode.  When the function is done, the current major mode is reset
X * from the buffer mode.
X */
Xed_dorep(type, mode)		/* 0 = Query Replace, 1 = Replace String */
Xint type;
Xstruct majmode *mode;
X{	register int c;
X	register int olen, allflg;
X	char *srch_ask();
X	char *opromp, *npromp;
X	char *nstr, *ostr;	/* Note ostr is == to srch_str */
X	int nlen;
X	chroff last_loc;
X#if IMAGEN
X	int nrepled = 0;
X	char replmsg[64];
X#endif /*IMAGEN*/
X
X	/* Set mode, then get search string and replace string */
X#if IMAGEN
X	cur_win->w_buf->b_flags |= B_QUERYREP;
X#else
X	cur_mode = mode;	/* Set major mode pointer */
X#endif /*-IMAGEN*/
X
X	redp(RD_MODE);
X	nstr = 0;
X#if IMAGEN
X	opromp = "Old string: ";
X	npromp = "New string: ";
X#else
X	opromp = "Replace string: ";
X	npromp = "with string: ";
X#endif /*-IMAGEN*/
X	if((ostr = srch_ask(opromp)) == 0)
X		goto done;
X	olen = srch_len;	/* srch_ask sets this! */
X	if((nstr = ask("%s%s %s", opromp, ostr, npromp)) == 0)
X		goto done;
X	nlen = ask_len;
X
X	/* Now enter search and subcommand loop */
X	allflg = type;		/* Unless 0 for Query Rep, replace all */ 
X	for(;;)
X	  {	last_loc = cur_dot;
X		if(e_search(ostr,olen,0) == 0)
X			break;
X		ed_setcur();			/* Cursor moved */
X	redisp:
X		if(!allflg) redisplay();	/* Update screen */
X	getcmd:
X		if(!allflg) c = cmd_read();
X		else c = SP;
X		switch(c)
X		  {
X#if IMAGEN
X			case 'n':
X#endif /*IMAGEN*/
X			case DEL:	/* Don't replace, go on */
X				continue;
X#if IMAGEN
X			case ',':
X#endif /*IMAGEN*/
X			case '.':	/* Replace and exit */
X			case SP:	/* Replace, go on */
X				ed_delete(cur_dot,(chroff)(cur_dot-olen));
X				ed_nsins(nstr,nlen);
X#if IMAGEN
X				++nrepled;
X#endif /*IMAGEN*/
X				if(c == '.') goto done;
X				continue;
X#if IMAGEN
X			default:
X#endif /*IMAGEN*/
X			case '?':	/* Show options */
X#if IMAGEN
X				saynow("\
X' '=>change, 'n'=>don't, '.'=>change, quit, '!'=>change rest, '^'=>back up");
X#else
X				saynow("\
XSP=Replace, DEL=Don't, ESC=Stop, !=Replace all, ^=Back up, .=Replace & Stop");
X#endif /*-IMAGEN*/
X				goto getcmd;
X			case '^':	/* Return to last place found */
X				ed_go(last_loc);
X				goto redisp;
X
X			case CTRL('G'):
X			case ESC:	/* Exit where we are */
X				goto done;
X
X			case CTRL('L'):	/* Redisplay */
X				redp(RD_SCREEN);
X				goto redisp;
X
X			case '!':	/* Replace all the rest */
X				allflg++;
X				goto getcmd;
X
X#if !(IMAGEN)
X			case ',':	/* Replace and show */
X			case CTRL('R'):	/* Enter edit mode recursively */
X			case CTRL('W'):	/* Delete once and ^R */
X				saynow("not implemented");
X				goto getcmd;
X			default:	/* Exit and re-read char */
X				unrchf = c;
X				goto done;
X#endif /*-IMAGEN*/
X		  }
X	  }
Xdone:
X#if IMAGEN
X	cur_win->w_buf->b_flags &= ~B_QUERYREP;
X#else
X	cur_mode = cur_buf->b_mode;
X#endif /*-IMAGEN*/
X
X	redp(RD_MODE);
X	if(nstr) 	/* Free nstr (but not ostr, it's == srch_str!) */
X		chkfree(nstr);
X#if IMAGEN
X	if (nrepled <= 0)
X		saynow("No replacements done");
X	else
X	  {	sprintf(replmsg, "Replaced %d occurences", nrepled);
X		saynow(replmsg);
X	  }
X#endif /*IMAGEN*/
X}
/
echo x - eeques.c
sed '/^X/s///' > eeques.c << '/'
X/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*	EEQUES - Handle queries and status displays
X */
X#include "elle.h"
X
X/*
X * Ask -- ask the user for some input on the lowest line of the screen
X * 
X * The arg is a string in printf form, followed by up to three args
X * for the printf string
X *
X *        The string is read into a sort of mini buffer, only the
X *        last line of which is visible on the screen. All editing
X *        features are available to the user to edit the input string.
X *        When the delim character is typed, input is terminated and
X *        The input string is passed back to the caller.
X *	  The delim is either an escape or a cr.
X *        IT IS UP TO THE CALLER TO FREE THIS MEMORY.
X *
X * Note that the actual length of the returned string can be found
X * in the global variable ask_len.  This is a crock but allows
X * callers to hack nulls in arg strings if they want to.
X */
X
Xint chg_win();
Xstruct window *make_mode();
X
Xstatic int ask_lin;	/* Saved cursor location when ask is done */
Xstatic int ask_blen;	/* Non-zero if buffer contains something */
Xstatic int ask_cnt;	/* Incremented by ask(), cleared by askclr() */
X
X/* Table of allowed functions during ASK */
Xstatic char askftab[] = {
X	FN_PFXMETA, FN_INSSELF, FN_BEGLINE, FN_ENDLINE, FN_BCHAR, FN_FCHAR,
X	FN_DCHAR, FN_BDCHAR, FN_TCHARS, FN_QUOTINS, FN_UARG, FN_BKPT,
X	FN_DEBUG,
X	FN_GOBEG, FN_GOEND, FN_FWORD, FN_BWORD, FN_KWORD, FN_BKWORD,
X	FN_UCWORD, FN_LCWORD, FN_UCIWORD, FN_ARGDIG, FN_NEWWIN, FN_KLINE,
X	FN_UNKILL, FN_BKLINE,
X	0
X};
X
Xchar *
Xask (string, arg1, arg2, arg3)
Xchar *string, *arg1, *arg2, *arg3;
X
X{	register int i, c;
X	register char *s;
X	struct window *oldwin;
X	char *newline;		/* where output line goes */
X	char cbuf[200];			/* For prompt string creation */
X	int p_length;			/* length of prompt */
X	chroff anslen;			/* Length of answer */
X	int funnum;			/* Crock stuff */
X#if FX_FILLMODE
X	extern int fill_mode;
X	int ofillmode = fill_mode;	/* Gotta turn this one off */
X	fill_mode = 0;
X#endif /*FX_FILLMODE*/
X
X	oldwin = cur_win;
X	chg_win (ask_win);
X	ed_reset();			/* Flush contents & request redisp */
X	ask_lin = cur_win->w_pos;	/* Set here in case never redisp */
X	ask_cnt++;			/* Bump # of times called */
X
X  	/* copy 'string' into line */
X	cbuf[0] = 0;
Xasklp:	sprintf (&cbuf[strlen(cbuf)], string, arg1, arg2, arg3);
X	p_length = strlen(cbuf);	/* Find how long it is */
X
X  	/* now let the user type in */
X	for(;;)
X	  {
X		if ((rd_type & (RDS_WINFLGS|RD_MODE)) && tinwait () == 0)
X		  {
X			e_gobob();	/* Gross crock: insert prompt */
X			e_sputz(cbuf);		/* Ugh, bletch */
X			cur_dot += p_length;	/* Temporarily update loc */
X			redp(RD_WINRES);	/* Do complete re-crunch */
X			upd_wind((struct window *)0);		/* Don't interrupt */
X			/* Ensure mode line is spiffy too.  This should
X			** only have to be invoked the first time ask_win
X			** redisplay is done, and never thereafter.
X			*/
X			if(rd_type&RD_MODE)	/* If mode also needs it, */
X				fupd_wind(make_mode(user_win));	/* do it */
X
X			upd_curs(cur_dot);
X			rd_type &= ~(RDS_WINFLGS|RD_MODE);
X			ask_lin = curs_lin;	/* Remember line cursor on */
X			tbufls();
X
X			e_gobob();	/* More crock: Remove prompt */
X			sb_deln((SBBUF *)cur_buf,(chroff)p_length);	/* Ugh etc. */
X			cur_dot -= p_length;		/* Restore loc */
X			e_gocur();
X		  }
X		exp = 1;
X		exp_p = 0;
X	cont:	this_cmd = 0;
X		c = cmd_read();
X
X		if (
X#if !(ICONOGRAPHICS)
X			c == ESC ||
X#endif /*-ICONOGRAPHICS*/
X				 c == LF || c == CR)
X			break;
X		if (c == BELL)       /* ^G means punt.. */
X		  {	chg_win(oldwin);
X			ask_blen = 1;	/* Assume buffer has something */
X			ding((char *)0);	/* Clear echo window */
X			ask_cnt = 0;	/* Nothing for askclr to do */
X#if FX_SKMAC
X			km_abort();
X#endif /*FX_SKMAC*/
X#if FX_FILLMODE
X			fill_mode = ofillmode;
X#endif /*FX_FILLMODE*/
X			return(0);	/* Return 0 to indicate we quit */
X		  }
X		/* This censoring section is a real crock! */
X		funnum = cmd_idx(c);		/* Map key to command */
X		while(funnum == FN_PFXMETA)	/* Allow META prefix */
X			funnum = cmd_idx(c = CB_META|cmd_read());
X		for(s = askftab; (i = *s&0377); ++s)
X			if(funnum == i) break;
X		switch(i)
X		  {	default:	/* Permissible function */
X				cmd_xct(c);
X				break;
X			case FN_NEWWIN:	/* Wants redisplay, do specially */
X				clear_wind(ask_win);
X				break;
X			case 0:		/* Illegal function */
X				ring_bell();
X#if FX_SKMAC
X				km_abort();
X#endif /*FX_SKMAC*/
X				continue;
X		  }
X		if(this_cmd == ARGCMD)
X			goto cont;
X	  }
X
X	if((anslen = e_blen()) > 255)		/* Ridiculously long? */
X	  {	strcpy(cbuf,"Huh? Try again - ");
X#if FX_SKMAC
X			km_abort();
X#endif /*FX_SKMAC*/
X		goto asklp;
X	  }
X	i = anslen;
X	e_gobob();		/* Go to start of buffer */
X	e_sputz(cbuf);		/* Re-insert prompt so buffer == screen */
X	ask_blen = i + 1;	/* Say buffer has something in it */
X
X	s = memalloc((SBMO)(i + 1));	/* Allocate fixed loc for answer */
X	newline = s;		/* Return ptr to allocated string */
X	ask_len = i;		/* And return (via global) length of string */
X	if(i) do { *s++ = e_getc(); }
X		while(--i);
X	*s = '\0';		       /* make sure string terminated */
X	chg_win(oldwin);
X#if FX_FILLMODE
X	fill_mode = ofillmode;
X#endif /*FX_FILLMODE*/
X	return (newline);	       /* return pointer to data */
X}
X
X/* ASKCLR - Clears the echo area (but not immediately) if the last thing
X**	done to it was an ask() call.  Note that invoking a SAY routine
X**	specifically causes this to be a no-op; SAYCLR must be done instead.
X*/
Xaskclr()
X{
X	if(ask_cnt) sayclr();	/* Zap if need be */
X}
X
X/* SAY - put up some text on bottom line.
X *	Does this intelligently; text stays up until next SAY or
X *	screen refresh.
X * SAYNOW - like SAY but forces display right away
X * SAYTOO - adds to existing stuff
X * SAYNTOO - ditto but forces output right away.
X * DING - Ring_bell then SAYNOW 
X * DINGTOO - is to DING as SAYNTOO is to SAYNOW.
X * SAYCLR - Clears echo area (but not immediately)
X */
X#define SAY_NOW 01	/* Force display immediately */
X#define SAY_TOO 02	/* Add to existing stuff */
X#define SAY_BEL 04	/* Ding bell prior to text */
X#define SAY_LEN 010	/* String length specified by 3rd arg */
X
Xsay(str)	char *str; {	sayall(str, 0); }
Xsaynow(str)	char *str; {	sayall(str, SAY_NOW); }
Xsaytoo(str)	char *str; {	sayall(str, SAY_TOO); }
Xsayntoo(str)	char *str; {	sayall(str, SAY_NOW|SAY_TOO); }
Xding(str)	char *str; {	sayall(str, SAY_NOW|SAY_BEL); }
Xdingtoo(str)	char *str; {	sayall(str, SAY_NOW|SAY_TOO|SAY_BEL); }
Xsaylntoo(str,n)	char *str; {	sayall(str, SAY_NOW|SAY_TOO|SAY_LEN, n); }
Xsayclr()		   {	sayall((char *)0, 0); }
X
Xsayall(str,flags,len)
Xchar *str;
Xint flags, len;
X{	register struct window *w;
X	register f;
X
X	f = flags;
X	w = cur_win;
X
X	ask_cnt = 0;			/* Always reset this */
X	if(str == 0 && ask_blen == 0)	/* If clearing, and buff empty */
X		return;			/* nothing to do. */
X	chg_win(ask_win);
X	if(f&SAY_TOO)
X		e_goeob();	/* Add to end of existing stuff */
X	else e_reset();		/* Flush previous stuff if any */
X	if(str)
X	  {	if(f&SAY_LEN)		/* Insert string to post up */
X			ed_nsins(str,len);
X		else e_sputz(str);
X	  }
X	ask_blen = e_dot();	/* Remember whether buffer has something */
X
X	e_setcur();		/* and remember to set dot */
X	if(f&SAY_NOW)
X	  {	if(f&SAY_BEL)
X			ring_bell();
X		redp(RD_WINRES);
X		upd_wind((struct window *)0);
X		tbufls();
X	  }
X	else redp(RD_WINRES);	/* Set for this window */
X	chg_win(w);		/* Back to previous window */
X
X	/* redisplay() does a special check for ask_win->w_redp, so we
X	** don't need to set a global flag like RD_CHKALL.
X	*/
X}
X
X/* YELLAT -- post string on specified line of screen, immediately.
X *	Differs from SAYNOW and SAYNTOO in that NO buffer
X *	manipulation is done; screen image is hacked directly.
X */
X
Xyellat(str, line)
Xchar *str;
Xregister int line;
X{	register struct scr_line *s;
X
X	s = scr[line];
X	strncpy(s->sl_nlin, str, scr_wd0);
X	s->sl_ncol = strlen(str);
X#if IMAGEN
X	s->sl_flg |= SL_REDO;
X#endif
X	upd_line(line);
X	tbufls();
X}
X
X/* YELLTOO -- Append string to previous echo line of screen, immediately.
X**	Uses the ask_lin variable which is set by ask().
X**	Currently this function is only needed for srchint() in EESRCH.
X*/
Xyelltoo(str)
Xchar *str;
X{	register int i;
X	register struct scr_line *s;
X	char nstr[MAXLINE];
X
X	s = scr[ask_lin];
X	i = s->sl_col;
X	nstr[0] = 0;
X	strncat(strncat(nstr, s->sl_line, i),	/* Make new string */
X			str, MAXLINE - i);
X	yellat(nstr, ask_lin);			/* Post it */
X}
X
X/* BARF - output a message on the bottom line of the screen,
X**	bypassing everything (window, buffer, screen image).
X**	Does NOT know about SAY's stuff and does not update it!
X**	Use only in dire straits...
X** ERRBARF - same but uses a standard error-message prefix.
X*/
X
Xerrbarf(str)
Xchar *str;
X{
X	barf("\007ELLE Internal Error: ");
X	tputz(str);
X	tbufls();
X}
X
Xbarf(str)
Xchar *str;
X{
X	ask_cnt = 0;			/* Ensure askclr() disabled */
X	t_curpos(scr_ht - ECHOLINES, 0);       /* goto echo area */
X	t_cleol();
X	tputz(str);
X	tbufls();
X	curs_col = -1000;	/* Say we dunno where cursor is now */
X}
X
X#if IMAGEN
X/* Same, but do it far from harm's way */
Xbarf2(str)
Xchar *str;
X{
X	t_curpos (scr_ht - 1, scr_wid - strlen(str) - 8);
X	t_cleol ();
X	tputz(str);
X	tputz(" --M--");
X	tbufls();
X	tgetc();			/* Read any char & discard */
X	curs_col = -1000;	/* Say we dunno where cursor is now */
X}
X#endif /*IMAGEN*/
/
echo x - eesigs.h
sed '/^X/s///' > eesigs.h << '/'
X/* ELLE - Copyright 1984, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/* EESIGS.H
X *	This file is only provided for inclusion only by V6 systems, where
X * the standard /usr/include/signal.h file may not exist and thus we
X * need to do our own definitions.
X */
X
X/* Signals marked with "*" cause a core image dump
X * if not caught or ignored. */
X
X#define	SIGHUP	1	/*   Hangup (eg dialup carrier lost) */
X#define	SIGINT	2	/*   Interrupt (user TTY interrupt) */
X#define	SIGQUIT	3	/* * Quit (user TTY interrupt) */
X#define	SIGILL	4	/* * Illegal Instruction (not reset when caught) */
X#define	SIGTRAP	5	/* * Trace Trap (not reset when caught) */
X#define	SIGIOT	6	/* * IOT instruction */
X#define	SIGEMT	7	/* * EMT instruction */
X#define	SIGFPE	8	/* * Floating Point Exception */
X#define	SIGKILL	9	/*   Kill (cannot be caught or ignored) */
X#define	SIGBUS	10	/* * Bus Error */
X#define	SIGSEGV	11	/* * Segmentation Violation */
X#define	SIGSYS	12	/* * Bad argument to system call */
X#define	SIGPIPE	13	/*   Write on a pipe with no one to read it */
X#define	SIGALRM	14	/*   Alarm Clock */
X#define	SIGTERM	15	/*   Software termination signal (from "kill" pgm) */
X
X#define	SIG_DFL	(int (*)())0	/* Arg to "signal" to resume default action */
X#define	SIG_IGN	(int (*)())1	/* Arg to "signal" to ignore this sig */
/
echo x - eesite.c
sed '/^X/s///' > eesite.c << '/'
X/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/* EESITE	Site dependent frobs
X *	Primarily TS_ routines for TTY control.  Most site-dependent
X *	routine is TS_INP for detection of TTY input.
X */
X
X#include "elle.h"
X
X#if !(V6)
X#include <signal.h>	/* For SIGTSTP in ts_pause */
X#else
X#include "eesigs.h"
X#endif
X
Xint tsf_pause = 0;	/* Set if ts_pause works.  Ref'd by equit in e_main */
X
X#if !(SYSV || BBN)	/* SYSV and BBN have weird tty calls */
X
X#if V6
X	/* Normal V6 declarations, must provide explicitly */
Xstruct sgttyb {
X	char sg_ispeed;
X	char sg_ospeed;
X	char sg_erase;
X	char sg_kill;
X	int sg_flags;
X};
X#define ECHO (010)
X#define CRMOD (020)
X#define RAW (040)
X#else
X	/* Normal V7 UNIX declarations, can use include file */
X#include <sgtty.h>
X#endif
X
Xstruct sgttyb nstate;	/* Both V6 and V7 */
Xstruct sgttyb ostate;	/* Both V6 and V7 */
X#endif /*!(SYSV || BBN)*/
X
X
X#if BBN		/* BBN system frobs */
X#include "/sys/sys/h/modtty.h"
Xstruct modes  nstate;
Xstruct modes  ostate;
X#endif /*BBN*/
X
X#if DNTTY		/* DN TTY frobs */
X#include <tty.h>
Xchar partab[2];		/* to satisfy obscene ref in tty.h */
X#endif /*DNTTY*/
X
X
X#if (UCB || TOPS20)		/* UCB, TOPS20 additional frobs */
X#include <sys/ioctl.h>		/* For ts_inp() and tldisc */
X#if IMAGEN
Xstruct tchars otchars, ntchars;	/* Original and new tchars */
X#endif /*IMAGEN*/
X#endif /*(UCB || TOPS20)*/
X
X#if SYSV		/* System V (and PC/IX) crocks */
X#include <termio.h>
X#include <sys/ioctl.h>
X
Xstruct termio	/* terminal i/o status flags */
X	origterm,	/* status of terminal at start of ELLE */
X	newterm;	/* status of terminal when using ELLE */
X#endif /*SYSV*/
X
X/* TS_INP
X *	Ask system if terminal input is available (on file descriptor 0).
X *	Returns non-zero if so, else returns zero.
X *	Very important that this call NOT hang or block in any way,
X *	because it is used to detect type-ahead by the user;
X *	return should be immediate whether or not input is waiting.
X */
Xts_inp()
X{
X#if BBN				/* Idiosyncratic */
X	int   cap_buf[2];
X	capac (0, &cap_buf[0], 4);
X	return (cap_buf[0]);
X#endif /*BBN*/
X
X#if (DNTTY || ONYX)		/* Have "empty()" syscall */
X	return(empty(0) ? 0 : 1);
X#endif /*DNTTY || ONYX*/
X#if (UCB || TOPS20)		/* Have FIONREAD ioctl */
X	long retval;
X	if(ioctl(0,FIONREAD,&retval))	/* If this call fails, */
X		return(0);		/* assume no input waiting */
X	return((retval ? 1 : 0));
X#endif /*UCB || TOPS20*/
X#if COHERENT
X	int retval;
X	ioctl(0, TIOCQUERY, &retval);
X	return((retval ? 1 : 0));
X#endif /*COHERENT*/
X#if VENIX86
X	struct sgttyb iocbuf;
X	ioctl(0, TIOCQCNT, &iocbuf);
X	return(iocbuf.sg_ispeed != 0 );
X#endif /*VENIX86*/
X
X#if !(BBN||COHERENT||DNTTY||ONYX||TOPS20||UCB||VENIX86)
X	return(0);		/* Default - never any type-ahead, sigh */
X#endif
X}
X
X
X/* TS_INIT()
X *	Get terminal information from system, initialize things for
X *	ts_enter and ts_exit.  This is called before t_init.
X *	Must set "trm_ospeed".
X */
Xts_init()
X{
X#if DNTTY
X	signal(16,1);		/* DN peculiar - turn off ctl-A */
X#endif /*DNTTY*/
X
X#if !(SYSV || BBN)			/* Normal UNIX stuff */
X	gtty(1,&ostate);		/* Remember old state */
X	nstate = ostate;		/* Set up edit-mode state vars */
X	nstate.sg_flags |= RAW;			/* We'll want raw mode */
X	nstate.sg_flags &= ~(ECHO|CRMOD);	/* with no echoing */
X	trm_ospeed = ostate.sg_ospeed;
X
X#if (IMAGEN && UCB)
X	/* Get around 4.1+ remote/local flow control bug (from Gosmacs) */
X	ioctl(0, TIOCGETC, &otchars);  /* Save original tchars */
X	ntchars = otchars;
X	ntchars.t_startc = -1;		/* Kill start/stop */
X	ntchars.t_stopc  = -1;
X	ioctl(0, TIOCSETC, &ntchars);
X#endif /*IMAGEN && UCB*/
X#endif /*!(SYSV || BBN)*/
X
X#if BBN
X	modtty(1, M_GET | M_MODES, &ostate, sizeof(ostate));	/* Save old */
X	modtty(1, M_GET | M_MODES, &nstate, sizeof(nstate));	/* Setup new */
X	nstate.t_erase = nstate.t_kill = nstate.t_intr = nstate.t_esc =
X		nstate.t_eof = nstate.t_replay = 0377;
X	nstate.t_quit = BELL;			/* ^G */
X	nstate.t_breaks = TB_ALL;		/* break on all */
X	nstate.t_iflags &= ~TI_ECHO & ~TI_NOSPCL & ~TI_CRMOD;
X				/* no echos, specials on, no CR -> LF*/
X	nstate.t_iflags |= TI_CLR_MSB;			/* ignore parity */
X	nstate.t_oflags &= ~TO_CRMOD & ~TO_AUTONL;	/* no CR -> NL */
X	if (trm_flags & NOXONOFF)
X		nstate.t_oflags &= ~TO_XONXOFF;
X	else
X		nstate.t_oflags |= TO_XONXOFF;   
X
X	nstate.t_oflags |= TO_CLR_MSB;		/* no special high bits */
X	nstate.t_pagelen = 0;			/* no paging of output */
X	trm_ospeed = ostate.t_ospeed;
X#endif /*BBN*/
X
X#if SYSV
X	ioctl(0, TCGETA, &origterm);	/* How things are now */
X	newterm = origterm;		/* Save them for restore on exit */
X
X	/* input flags */
X	newterm.c_iflag |= IGNBRK;	/* Ignore break conditions.*/
X	newterm.c_iflag &= ~INLCR;	/* Don't map NL to CR on input */
X	newterm.c_iflag &= ~ICRNL;      /* Don't map CR to NL on input */
X	newterm.c_iflag &= ~BRKINT;	/* Do not signal on break.*/
X	newterm.c_iflag &= ~IXON;	/* Disable start/stop output control.*/
X	newterm.c_iflag &= ~IXOFF;	/* Disable start/stop input control.*/
X
X	/* line discipline */
X	newterm.c_lflag &= ~ISIG;	/* Disable signals.*/
X	newterm.c_lflag &= ~ICANON;	/* Want to disable canonical I/O */
X	newterm.c_lflag &= ~ECHO;	/* Disable echo.*/
X
X	newterm.c_cc[4] = 1;		/* Min. chars. on input (immed) */
X	newterm.c_cc[5] = 1;	        /* Min. time delay on input (immed) */
X
X	/* Make it stick */
X	ioctl(0, TCSETA, &newterm);
X#endif /*SYSV*/
X
X#if (UCB || TOPS20)
X	{	int tldisc;
X		ioctl(0, TIOCGETD, &tldisc);	/* Find line discipline */
X
X/* The flag IGN_JOB_CONTROL has been introduced to allow job control haters
X * to simply ignore the whole thing.  When ELLE is compiled with
X * -DIGN_JOB_CONTROL, it will exit properly when the Return to Superior
X * command is executed.
X*/
X#if SIGTSTP
X#ifndef IGN_JOB_CONTROL
X		if(tldisc == NTTYDISC) tsf_pause = 1;
X#endif
X#endif /*SIGTSTP*/
X
X	}
X#endif /*UCB || TOPS20*/
X}
X
X/* TS_ENTER()
X *	Tell system to enter right terminal mode for editing.
X *	This is called before t_enter.
X */
Xts_enter()
X{
X#if !(SYSV || BBN)
X	stty(1,&nstate);
X#if IMAGEN && UCB
X	ioctl(0, TIOCSETC, &ntchars);	/* Restore new tchars */
X#endif /*IMAGEN && UCB*/
X#endif /*!(SYSV||BBN)*/
X
X#if BBN
X	modtty (1, M_SET | M_MODES, &nstate, sizeof (nstate));
X#endif /*BBN*/
X
X#if SYSV
X	/* Make it behave as previously defined in ts_init */
X	ioctl(0, TCSETA, &newterm);
X#endif /*SYSV*/
X
X#if DNTTY	/* DN hackery!  Enable 8-bit input so as to read meta bit. */
X	if(dbg_isw)
X	  {	tpoke(TH_CSET,T_2FLGS2,EEI);	/* Enable ints */
X		tpoke(TH_CSETB,T_QUIT, 0377);	/* Turn off QUIT intrpt */
X	  }
X	else if(trm_flags & TF_METAKEY)
X		tpoke(TH_CSET,T_2FLGS2,T2_LITIN); /* Turn on 8-bit input! */
X#endif /*DNTTY*/
X}
X
X/* TS_EXIT
X *	Tell system to restore old terminal mode (we are leaving edit mode).
X *	This is called after t_exit.
X */
Xts_exit()
X{
X#if DNTTY
X	if(dbg_isw)
X		tpoke(TH_CCLR,T_2FLGS2,EEI);	/* Turn off EEI bit */
X	else if(trm_flags & TF_METAKEY)
X		tpoke(TH_CCLR,T_2FLGS2,T2_LITIN); /* Turn off 8-bit input */
X#endif /*DNTTY*/
X
X#if !(SYSV || BBN)
X	stty(1,&ostate);		/* SYSV and BBN don't use stty */
X#if IMAGEN && UCB
X	ioctl(0, TIOCSETC, &otchars);	/* Restore original tchars */
X#endif /*IMAGEN && UCB*/
X#endif /*!(SYSV || BBN)*/
X
X#if BBN
X	modtty (1, M_SET | M_MODES, &ostate, sizeof (ostate));
X#endif /*BBN*/
X
X#if SYSV
X	ioctl(0, TCSETA, &origterm);
X#endif /*SYSV*/
X}
X
X#if DNTTY
Xint thkcmd[] { 0, 0, -1 };
Xtpoke(cmd,bn,val)
Xint cmd, bn, val;
X{
X	thkcmd[0] = cmd|bn;
X	thkcmd[1] = val;
X	if(ttyhak(0,&thkcmd) < 0)
X		return(-1);
X	else return(thkcmd[1]);
X}
X#endif /*DNTTY*/
X
X
X/* TS_PAUSE - Stop process and return control of TTY to superior.
X *	There is also a flag variable, TSF_PAUSE, which indicates
X *	whether or not this routine will actually do anything.
X */
X#if TOPS20
X#include <jsys.h>
X#endif
X
Xts_pause()
X{
X#if TOPS20
X	int acs[5];
X	jsys(HALTF, acs);
X#endif
X
X#if UCB
X#if SIGTSTP
X	signal(SIGTSTP, SIG_DFL);
X#if BSD4_2
X#define	mask(s)	(1 << ((s)-1))
X	sigsetmask(sigblock(0) &~ mask(SIGTSTP));
X#endif /*BSD4_2*/
X	kill(0, SIGTSTP);
X#if BSD4_2
X	sigblock(mask(SIGTSTP));
X#endif /*BSD4_2*/
X#endif /*SIGTSTP*/
X#endif /*UCB*/
X}
/
echo x - eesite.h
sed '/^X/s///' > eesite.h << '/'
X/* ELLE - Copyright 1984, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*
X * EESITE.H	Site-dependent switches & definitions
X */
X
X/* CONDITIONAL COMPILATION SWITCHES */
X
X#define V6	0	/* Running on V6 system (else V7 assumed) */
X
X#define APOLLO 0	/* Running on an Apollo system */
X#define BBN	0	/* Running on BBN system (tty stuff) */
X#define BSD4_2	0	/* Running on 4.2BSD system */
X#define COHERENT 0	/* Running on Coherent IBM-PC system */
X#define DNTTY	0	/* Running on SRI V6 Deafnet system (tty stuff) */
X#define HPUX	0	/* Running on Hewlett-Packard System V + */
X#define MINIX	1	/* Running on MINIX (IBM-PC) system */
X#define ONYX	0	/* Running on ONYX Z8000 system */
X#define PCIX	0	/* Running on PC/IX (IBM-PC) system */
X#define SUN	0	/* Running on SUN workstation system */
X#define SYSV	0	/* Running on Unix System V (or perhaps Sys III) */
X#define TOPS20	0	/* Running on TOPS-20 KCC C implementation */
X#define UCB	0	/* Running on 2.8, 2.9, or 4.x BSD sys (tty stuff) */
X#define VENIX86 0	/* Running on Venix86 (IBM-PC) system */
X
X#define ICONOGRAPHICS 0 /* Using Iconographics configuration version */
X#define IMAGEN 0	/* Using Imagen configuration version */
X
X/* Resolve system dependencies */
X#if SUN
X#undef BSD4_2
X#define BSD4_2 1	/* SUN uses 4.2BSD */
X#endif
X
X#if BSD4_2
X#undef UCB
X#define UCB	1	/* 4.2 is special case of general UCB stuff */
X#endif /*BSD4_2*/
X
X#if (PCIX || HPUX)
X#undef SYSV
X#define SYSV	1	/* PC/IX & HP-UX are based on System III & V (resp) */
X#endif
X
X/* Set system or site dependent stuff here */
X
X#if V6
X#define void int	/* May need this for other systems too */
X#endif
X
X/* Changes to parameters (elle.h) or variable defaults (e_vinit.c) */
X
X#if COHERENT
X#define EVFNO2  0	/* "Old" filename postfix - use no old file! */
X#define EVFNN2 "+"	/* "New" filename postfix */
X#define TX_COHIBM 1	/* Ensure Coherent IBM-PC console support included */
X#endif /*COHERENT*/
X
X#if DNTTY
X#define EVLLEN 60	/* Short line length for TDDs */
X#endif /*DNTTY*/
X
X#if HPUX
X#define EVFNO2 "~"	/* Same as CCA Emacs.  Sorts last in listing. */
X#endif /*HPUX*/
X
X#if MINIX
X#define EVFNO2 ".bak"	/* "Old" filename postfix */
X#define EVMARKSHOW "Mark set"
X#define EVCCOL (33)	/* Use this as Comment Column */
X#define EVMVPCT 1	/* 1% - Try to use minimal window repositioning */
X#define EVMODWSO 1	/* Use mode window standout if can */
X#define STRERROR 1	/* Say that implementation provides strerror() */
X
X#include <sys/types.h>
X#include <string.h>
X#include <unistd.h>
X#include <stdio.h>
X#endif /*MINIX*/
X
X#if ONYX
X#define STKMEM (4*512)		/* ONYX Z8000 seems to have less room avail */
X#endif /*ONYX*/
X
X#if BSD4_2
X#define FNAMELEN 255	/* Max size of last filename component */
X#define FNAMSIZ 400	/* Max size of complete filename */
X#endif /*BSD4_2*/
X
X#if TOPS20
X#define EVHELPFILE "elle:help.dat"	/* T20 ELLE help file */
X#define EVPROFBINFILE "ellep.b1"	/* T20 binary profile file */
X#define EVPROFTEXTFILE "ellep.e"	/* T20 ASCII profile file */
X#define EVFNO2 0	/* No old filename postfix (T20 has generations) */
X#define EVFNN2 0	/* No new filename postfix (T20 has generations) */
X#define FNAMELEN (40*3)	/* Max size of non-directory filename component */
X#define FNAMSIZ (40*5)	/* Max size of complete filename */
X#define STRERROR 1	/* Say that implementation provides strerror() */
X#endif /*TOPS20*/
X
X#if VENIX86
X#define TIBFSIZ 1	/* Venix86 block reads in raw mode */
X#endif /*VENIX86*/
X
X/* Configuration settings */
X
X#if ICONOGRAPHICS
X#define EVFNO2 "@"	/* "Old" filename postfix */
X#define EVMARKSHOW "Set."
X#define PARABLOCK 1	/* Values meaningful only for ICONOGRAPHICS */
X#define PARALINE  2
X#define TXC_VISBEL 1	/* Use visible bell if possible */
X#endif /*ICONOGRAPHICS*/
X
X#if IMAGEN
X#define EVFNO2 ".BAK"	/* "Old" filename postfix */
X#define EVMARKSHOW "Mark set"
X#define TOBFSIZ (10*80)	/* Size of TTY output buffer */
X#define ECHOLINES 2	/* Use 2 echo-area lines, not 1 */
X#define MAXARGFILES 10	/* Several startup filename args */
X#endif /*IMAGEN*/
X
X/* Now set any defaults for things not already defined */
X
X/* TERMINAL SUPPORT SWITCHES */
X/* 	Only those terminals which have a switch defined here	*/
X/*	will be included in ELLE's "hardwired" support.		*/
X/*	Switch name:	Compiles support for:			*/
X#ifndef TX_TERMCAP
X#define TX_TERMCAP 1	/*    *	- most TERMCAP-defined terminals */
X#endif
X#ifndef TX_H19
X#define TX_H19	1	/* "H19"	- Heath/Zenith-19 */
X#endif
X#ifndef TX_DM2500
X#define TX_DM2500 1	/* "DM2500","DM3025" - Datamedia 2500 */
X#endif
X#ifndef TX_COHIBM
X#define TX_COHIBM 0	/* "COHIBM"	- Coherent IBM-PC console */
X#endif
X#ifndef TX_TVI925
X#define TX_TVI925 0	/* "TVI925"	- TeleVideo 925 */
X#endif
X#ifndef TX_OM8025
X#define TX_OM8025 0	/* "OM8025"	- Omron 8025AG */
X#endif
X
X#ifndef TXC_VISBEL	/* Non-zero if want to use visible bell */
X#define TXC_VISBEL 0
X#endif
X
X/* Default terminal type string, used if ELLE cannot get type either
X** from $TERM or from startup args.
X*/
X#ifndef TXS_DEFAULT
X#define TXS_DEFAULT "H19"	/* Default terminal type string */
X#endif
X
X/* Combination parameter/switch definitions */
X
X/* STKMEM - System-dependent stack allocation crock, defines amount of
X *	stack memory to grab for general-purpose use.  This is mainly
X *	useful for PDP-11s or machines with similarly brain-damaged
X *	address space hardware.  A PDP-11 memory segment is 8K bytes,
X *	or 16 512-byte blocks, and the stack segment quarantines all of
X *	this space even though the actual stack may only use a miniscule
X *	portion of it.
X */
X
X/* Use this if compiling for a PDP11 system, otherwise leave undefined.. */
X#if (V6 || 0)
X#define STKMEM (8*512)		/* Use half a PDP11 segment */
X#endif
X
X/* These defaults are in eesite.h so ELLEC can get at them too. */
X#ifndef EVPROFBINFILE	/* Location of binary user profile, relative to HOME */
X#define EVPROFBINFILE ".ellepro.b1"
X#endif
X#ifndef EVPROFTEXTFILE	/* Location of ASCII user profile (used by ELLEC) */
X#define EVPROFTEXTFILE ".ellepro.e"
X#endif
/
echo x - eesrch.c
sed '/^X/s///' > eesrch.c << '/'
X/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*
X * EESRCH	Searching functions
X */
X
X#include "elle.h"
X#if !(V6)
X#include <signal.h>
X#else
X#include "eesigs.h"		/* Use this on V6 system */
X#endif /*V6*/
X
X/*
X *   Buffer String Search routines
X *
X *      If no search string is provided, a string that was previously
X *      used in the last search is once again used.
X */
X
X/* EFUN: "String Search" */
Xf_srch()
X{	return (lin_search (0));
X}
X
X/* EFUN: "Reverse String Search" */
Xf_rsrch()
X{	return (lin_search (1));
X}
X
X/* LIN_SEARCH - Main routine for non-incremental String Search.  Asks for
X *	a search string and looks for it.
X */ 
Xlin_search (backwards)
Xint  backwards;
X{	register char *mem;	/* item to be searched for */
X	register int res;
X	int srchint(), (*sav_srchalarm)();
X	char *srch_ask();
X	chroff savdot;
X
X	savdot = cur_dot;		/* Save original loc */
X
X#if ICONOGRAPHICS
X        if((mem = srch_ask(backwards ? "Reverse Search%s%s%s"
X                                     : "Search%s%s%s"))==0)
X                return;
X#else
X        if((mem = srch_ask(backwards ? "Reverse Search: " : "Search: "))==0)
X                return;
X#endif /*-ICONOGRAPHICS*/
X	sav_srchalarm = signal(SIGALRM,/*&*/srchint);	/* Handle timeout */
X	alarm(1);					/* One sec from now */
X
X	res = e_search(mem,srch_len,backwards);	/* Search for str! */
X
X	alarm(0);				/* Turn off alarm */
X	signal(SIGALRM,sav_srchalarm);		/* Restore old handler */
X
X	if(res)				/* Search won? */
X	  {	ed_setcur();
X		return;
X	  }
X
X	/* Search failed */
X	e_gosetcur(savdot);
X	ding("Search Failed");
X}
X
Xsrchint()
X{	yelltoo(" ...");
X}
X
Xchar *
Xsrch_ask(prompt)
Xchar *prompt;
X{	register char *ans, *old;
X
X#if ICONOGRAPHICS
X        if (srch_str)
X           ans = ask(prompt, " (", srch_str, "): ");
X        else ans = ask (prompt, ": ", "", "");
X        if (ans == 0) return (0);
X#else
X        if((ans = ask(prompt)) == 0)
X                return(0);              /* user punted ... */
X#endif /*-ICONOGRAPHICS*/
X	old = srch_str;
X	if (*ans == '\0')
X	  {	chkfree(ans);
X		if ((ans = old) == 0)		/* no string specified */
X		  {	dingtoo("Nothing to search for");
X			return(0);
X		  }
X#if !(ICONOGRAPHICS)
X		saylntoo(old, srch_len);	/* Show what old string is */
X#endif /*-ICONOGRAPHICS*/
X	  }
X	else
X	  {	if (old)
X			chkfree(old);	/* free up old srch string */
X		srch_str = ans;
X		srch_len = ask_len;
X	  }
X	return(ans);
X}
X
X#if 0
X	Incremental Search stuff.
XDescription of EMACS behavior:
X	^Q quotes next char.
X	DEL cancels last char.  If this cancelled a match, point is moved
X		to previous match.
X	If not all of input can be found, it is not discarded.  Can rub out,
X		discard unmatched stuff with ^G, exit, etc.
X	^S repeats search forward; ^R repeats backward.
X		If empty string, either
X			changes direction (if not same)
X			or brings back previous string
X	ESC exits.  If empty string, changes to non-incremental string search.
X	^G of a winning search aborts, exits, and moves point back to origin.
X	^G of a failing search discards the input that wasn''t found.
X	Other C- or M- chars exit and are executed.
XELLE also interprets ^H (BS) as DEL, because some keyboards make it hard to
X	type DEL and there is no way the user can
X	re-bind the incremental-search commands.
X#endif /*COMMENT*/
X
X#if FX_ISRCH
X/* EFUN: "Incremental Search" */
Xf_isrch() { i_search(0); }
X#endif /*FX_ISRCH*/
X
X#if FX_RISRCH
X/* EFUN: "Reverse Search" */
Xf_risrch() { i_search(1); }
X#endif /*FX_RISRCH*/
X
X#if FX_ISRCH || FX_RISRCH
X
Xi_search(back)
Xint back;		/* Current mode: 0 if forward, 1 if backward */
X{	register int c;
X	register int inpcnt;	/* # chars in current input srch str */
X	int inpgood;		/* Length of last winning string */
X	char inpstr[ISRCHLIM];	/* Holds current input search string */
X	chroff inpdot[ISRCHLIM];	/* Holds winning addrs for each */
X	struct window *savwin;
X	int winning;	/* 1 = currently winning, 0 = currently failing */
X	int pref, shown;
X	int f_insself(), (*(cmd_fun()))();
X
X	winning = 1;
X	inpcnt = 0;
X	inpgood = 0;
X	inpdot[0] = cur_dot;
X	savwin = cur_win;
X
X	/* Set up prompt and read all TTY input thus far */
X	shown = 0;
X sloop:	c = cmd_wait();		/* See if any command input waiting */
X	if(shown || !c)
X	  {	e_setcur();	/* Assume we moved around, so set cur_dot */
X		chg_win(ask_win);
X		ed_reset();	/* Flush contents & invoke redisplay */
X		ed_sins(back ? "R-search: " : "I-search: ");
X		ed_nsins(inpstr, inpcnt);
X		if(!winning) ed_sins("\t(FAILING)");
X		upd_wind((struct window *)0);	/* Force ask_win update */
X		if(c)
X		  {	upd_curs(cur_dot);
X			tbufls();
X		  }
X		chg_win(savwin);
X		shown = 1;	/* Say search prompt has been shown */
X	  }
X	if(!c)			/* If no user input waiting, show buffer */
X	  {	redp(RD_MOVE);		/* Cursor moved in window */
X		redisplay();
X	  }
X	c = cmd_read();		/* Get input char */
X	switch(c)
X	  {	case DEL:		/* Cancel last char */
X		case BS:		/* Hard to type DEL on some kbds */
X			if(inpcnt <= 0) goto sloop;
X			if(--inpcnt > inpgood) goto sloop;
X			winning = 1;
X			if(inpcnt == inpgood) goto sloop;
X			inpgood--;
X			ed_go(inpdot[inpcnt]);
X			goto sloop;
X
X		case CTRL('Q'):
X			c = cmd_read();	/* Quote next char */
X			break;
X		case CTRL('S'):
X			pref = 0;
X			goto ctlsr;
X		case CTRL('R'):
X			pref = 1;
X			goto ctlsr;
X
X		case CTRL('G'):
X			if(winning)
X			  {	ed_go(inpdot[0]);
X				goto sdone;
X			  }
X			inpcnt = inpgood;
X			winning = 1;
X			goto sloop;
X		case ESC:
X		case CR:
X			if(inpcnt)
X				goto sdone;
X			lin_search(back);
X			return;
X		default:
X			if(f_insself != cmd_fun(c))
X			  {	unrchf = c;
X				goto sdone;
X			  }
X		case TAB:	/* Strange self-inserting char */
X			break;
X	  }
X	if(inpcnt >= ISRCHLIM-1)
X	  {	ding("I-search str too long");
X		sleep(1);
X		goto sdone;
X	  }
X	inpstr[inpcnt++] = c;
X	if(!winning) goto sloop;
X
X	/* Now search for string.  (Arm alarm interrupt?) */
X	/* cur_dot has current location cursor is at; we want to back off
X	 * from this so a repeated search will find the same location if
X	 * appropriate. */
X	e_igoff(back ? inpcnt : -(inpcnt-1));
Xdosrch:
X	winning = e_search(inpstr,inpcnt,back);
X	if (winning)
X	  {	inpgood = inpcnt;	/* Remember last win length */
X		inpdot[inpcnt] = e_dot();	/* and location */
X	  }
X	else e_gocur();			/* Back to start position */
X	goto sloop;
X
X ctlsr:	if (pref != back)
X	  {	back = pref;
X		if(inpcnt <= 0) goto sloop;
X	  }
X	if(inpcnt <= 0)
X	  {	if(!srch_str || (inpcnt = srch_len) <= 0)
X			goto sloop;
X		bcopy((SBMA)srch_str, (SBMA)inpstr, srch_len);
X		inpcnt = srch_len;
X		unrchf = c;		/* Repeat cmd after display */
X		shown = 1;		/* Force search-string display */
X		goto sloop;
X	  }
X	goto dosrch;
X
X  sdone:
X	if(srch_str) chkfree(srch_str);
X	srch_str = memalloc((SBMO)(inpcnt+1));
X	bcopy((SBMA)inpstr,(SBMA)srch_str,inpcnt);	/* Copy into srch_str */
X	srch_len = inpcnt;
X	e_setcur();
X	chg_win(ask_win);
X	ed_reset();
X	chg_win(savwin);
X	redp(RD_CHKALL);
X}
X#endif /*FX_ISRCH || FX_RISRCH*/
/
echo x - eeterm.c
sed '/^X/s///' > eeterm.c << '/'
X/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*
X *  EETERM	ELLE Terminal Driver.
X *	Directly supports DM2500, H-19, Omron 8025AG, Coherent/IBM-PC, TVI925.
X *	Others also supported if using TX_TERMCAP.
X */
X
X#include "elle.h"
X
X/* Define terminal indices (there may be holes but C preprocessor is too
X * stupid to let us close them).  Should be one TN_ definition for every
X * hardwired terminal type, even though whether or not it is actually
X * compiled depends on which TX_ switches are defined.
X */
X#define TN_TERMCAP 0
X#define TN_DM2500 1
X#define TN_H19	  2
X#define TN_OM8025 3
X#define TN_COHIBM 4	/* Coherent IBM-PC console */
X#define TN_TVI925 5
X
X#if TX_COHIBM && !(TX_H19)	/* Ensure H19 defined if COHIBM is. */
X#define TX_H19 1
X#endif
X
X#ifndef TXS_DEFAULT		/* If no default is explicitly specified */
X#define TXS_DEFAULT "H19"	/* Then settle for H-19 */
X#endif /*TXS_DEFAULT*/
X
X
X
Xextern char *tv_stype;	/* If set, specifies terminal type */
Xextern int tibfmsk;	/* Crock to mask off parity (meta) bit */
Xstatic int tv_padc;	/* Pad character to use */
Xstatic int tv_cspeed;	/* # msec per char (set from trm_ospeed) */
Xstatic int tv_type;	/* Index of selected terminal type */
X
X/* Internal functions */
Xstatic void tpadn(), tpad();
X
X/* Character speed table, indexed by system output speed value (0-017).
X * Value in table is 100 * <# msec used per character>.
X */
Xstatic int cspdtab[] =
X{	/* Val    Idx Baud CPS  Time/char in msec */
X	0,	/*  0 Hangup -	----		*/
X	13333,	/*  1   50 7.5 133.33  (Baudot)	*/
X	10000,	/*  2   75  10 100.0   (Baudot)	*/
X	10000,	/*  3  110  10 100.0		*/
X	 8200,	/*  4 134.5 12.2 82.0 (IBM2741)	*/
X	 6666,	/*  5  150  15 	66.6666 	*/
X	 5000,	/*  6  200  20	50.0		*/
X	 3333,	/*  7  300  30	33.3333 	*/
X	 1666,	/*  8  600  60  16.6666 	*/
X	  833,	/*  9 1200 120   8.3333 	*/
X	  555,	/* 10 1800 180   5.5555 	*/
X	  416,	/* 11 2400 240   4.1666 	*/
X	  208,	/* 12 4800 480   2.0833		*/
X	  104,	/* 13 9600 960   1.04166	*/
X	0,	/* 14 Ext A  ?	 ?		*/
X	0	/* 15 Ext B  ?	 ?		*/
X};
X
X#if TX_TERMCAP
X/* Declarations for TERMCAP stuff.  Only EETERM knows about them. */
X
X/* Termcap routines */
Xextern int tgetent(), tgetnum(), tgetflag(), tputs();
Xextern char *tgetstr(), *tgoto();
Xstatic int getcap();		/* Internal routines */
Xstatic void putpad(), putnpad(), putpar();
X
X/* Gross disgusting externals that must be defined for TERMCAP rtns */
Xchar	PC;		/* Pad char */
Xchar	*BC;		/* Backspace to use, if not ^H */
Xchar	*UP;		/* Cursor up */
Xshort	ospeed;		/* Terminal output speed */
X
X/* Termcap numerical values/flags */
Xstatic int
X	tc_am,		/* TRUE if has auto-wrap */
X	tc_km;		/* TRUE if meta key exists */
X
X/* Termcap capability strings we want to know about */
X
Xstruct tcap { char tcicod1, tcicod2, *tccap; };
Xstatic struct tcap tcap[] = {
X#define TC_al	tcap[0].tccap	/* Add (insert) line */
X	{'a','l', 0},
X#define TC_AL	tcap[1].tccap	/* Add N lines */
X	{'A','L', 0},
X#define TC_bc	tcap[2].tccap	/* Backspace Char (for BC) */
X	{'b','c', 0},
X#define TC_ce	tcap[3].tccap	/* Erase to end of line (CLEOL) */
X	{'c','e', 0},
X#define TC_cl	tcap[4].tccap	/* Clear screen */
X	{'c','l', 0},
X#define TC_cm	tcap[5].tccap	/* Cursor motion */
X	{'c','m', 0},
X#define TC_dc	tcap[6].tccap	/* Delete char */
X	{'d','c', 0},
X#define TC_DC	tcap[7].tccap	/* Delete N chars */
X	{'D','C', 0},
X#define TC_dl	tcap[8].tccap	/* Delete line */
X	{'d','l', 0},
X#define TC_DL	tcap[9].tccap	/* Delete N lines */
X	{'D','L', 0},
X#define TC_dm	tcap[10].tccap	/* Delete mode on */
X	{'d','m', 0},
X#define TC_ed	tcap[11].tccap	/* Delete mode off */
X	{'e','d', 0},
X#define TC_ei	tcap[12].tccap	/* Insert mode off */
X	{'e','i', 0},
X#define TC_ia	tcap[13].tccap	/* Add line while in insert mode (see note) */
X	{'i','a', 0},
X#define TC_ic	tcap[14].tccap	/* Insert blank char */
X	{'i','c', 0},
X#define TC_IC	tcap[15].tccap	/* Insert N blank chars */
X	{'I','C', 0},
X#define TC_id	tcap[16].tccap	/* Delete line while in del mode (see note) */
X	{'i','d', 0},
X#define TC_im	tcap[17].tccap	/* Insert mode on */
X	{'i','m', 0},
X#define TC_ip	tcap[18].tccap	/* Padding to send after char insertion */
X	{'i','p', 0},
X#define TC_mm	tcap[19].tccap	/* String to set (turn on) meta-key mode */
X	{'m','m', 0},
X#define TC_mo	tcap[20].tccap	/* String to reset (turn off) meta-key mode */
X	{'m','o', 0},
X#define TC_pc	tcap[21].tccap	/* Pad Char (for PC) */
X	{'p','c', 0},
X#define TC_se	tcap[22].tccap	/* End standout mode */
X	{'s','e', 0},
X#define TC_so	tcap[23].tccap	/* Enter standout mode */
X	{'s','o', 0},
X#define TC_te	tcap[24].tccap	/* String to end programs that use termcap */
X	{'t','e', 0},
X#define TC_ti	tcap[25].tccap	/* String to beg programs that use termcap */
X	{'t','i', 0},
X#define TC_up	tcap[26].tccap	/* Move cursor up (for UP) */
X	{'u','p', 0},
X#define TC_vb	tcap[27].tccap	/* Visible bell */
X	{'v','b', 0},
X};
X#define NTCAPS ((sizeof(tcap))/(sizeof(struct tcap)))	/* # entries */
X
X/*
X * There are many other things that must be taken into account.
X * The termcap code here will probably not work for many termcap entries,
X * but the only sure way to find out which ones they are is to try them.
X */
X/* Note that the "ia" and "id" strings are not defined by the TERMCAP doc;
X * their usage here is derived from examining other TERMCAP-using programs.
X * Sigh!!!!
X */
X#endif /*TX_TERMCAP*/
X
X/* T_INIT is called once only at program startup, to identify the
X *	terminal type and set up any one-time things.
X * T_FATAL is only called if some routine detects an error related to the
X *	terminal specification, before any initialization is done.
X *	It prints a short error message and exits the program.
X * T_ENTER is called after TS_ENTER to set the terminal parameters for
X *	editing (as opposed to normal typeout).  It may be called
X *	several times.
X * T_EXIT is called before TS_EXIT to restore normal typeout modes.
X *	It is called on exit from the program, and perhaps other times.
X */
Xt_init()
X{
X	char *getenv();
X
X	/* Set some default parameters */
X	scr_ht = 24;
X	scr_wid = 79;
X	trm_flags = 0;
X	tvc_cin = 1;		/* Assume 1 char per char I/D pos */
X	tvc_cdn = 1;
X	tvc_pos = 4;		/* Default abs-move cost is 4 chars */
X	tvc_bs = 1;		/* Default backspace cost is 1 char */
X	tv_cspeed = cspdtab[trm_ospeed];	/* Find # msec per char */
X
X	/* First must determine terminal type, and check for terminals
X	 * that are hardwired into ELLE. */
X	if(!tv_stype		/* String set in command line args? */
X#if !(V6)
X	 && !(tv_stype = getenv("TERM"))	/* or given by TERM var? */
X#endif /*-V6*/
X		) tv_stype = TXS_DEFAULT;	/* No, try using default */
X	if(0) ;			/* Sigh, stupid construct */
X#if TX_H19
X	else if(ustrcmp(tv_stype,"H19")) tv_type = TN_H19;
X#endif /*TX_H19*/
X#if TX_OM8025
X	else if(ustrcmp(tv_stype,"OM8025")) tv_type = TN_OM8025;
X#endif /*TX_OM8025*/
X#if TX_DM2500
X	else if(ustrcmp(tv_stype,"DM2500")) tv_type = TN_DM2500;
X	else if(ustrcmp(tv_stype,"DM3025")) tv_type = TN_DM2500;
X#endif /*TX_DM2500*/
X#if TX_COHIBM
X	else if(ustrcmp(tv_stype,"COHIBM")) tv_type = TN_COHIBM;
X#endif /*TX_COHIBM*/
X#if TX_TVI925
X	else if(ustrcmp(tv_stype,"TVI925")) tv_type = TN_TVI925;
X#endif /*TX_TVI925*/
X#if TX_TERMCAP	/* This should be last thing */
X	else if(getcap(tv_stype)) tv_type = TN_TERMCAP;
X#endif /*TX_TERMCAP*/
X	else t_fatal("type unknown");	/* Ugh, barf and exit */
X
X	/* Terminal selected, now initialize parameters for it. */
X	switch(tv_type)
X	  {
X#if TX_DM2500
X		case TN_DM2500:
X			tv_padc = 0177;		/* Use rubout for pad */
X			tvc_pos = 3;		/* Only 3 chars for abs mov */
X			tvc_ci = 2;
X		/*	tvc_cin = 1; */		/* Default is OK */
X			tvc_cd = 2;
X		/*	tvc_cdn = 1; */		/* Default is OK */
X			tvc_ld = 2;
X			tvc_ldn = 1;
X			tvc_li = 2;
X			tvc_lin = 1;
X			if(trm_ospeed == 13)	/* If 9600, */
X			  {	tvc_cin = 5;		/* Sigh, high cost */
X				tvc_cdn = 2;
X				tvc_lin = 18;
X				tvc_ldn = 2;
X			  }
X			trm_flags |= TF_IDLIN|TF_IDCHR|TF_CLEOL|TF_METAKEY;
X			break;
X#endif /*TX_DM2500*/
X#if TX_H19
X		case TN_H19:			
X			trm_flags |= TF_IDLIN|TF_IDCHR|TF_CLEOL;
X			tvc_ci = 8;
X		/*	tvc_cin = 1; */	/* default is ok */
X			tvc_cd = 0;
X			tvc_cdn = 2;
X		/*	tvc_ld = 0; */	/* Default is OK */
X			tvc_ldn = 1 << (trm_ospeed - 7);
X		/*	tvc_li = 0; */	/* Default is OK */
X			tvc_lin = tvc_ldn;
X			break;
X#endif /*TX_H19*/
X#if TX_COHIBM
X		case TN_COHIBM:
X			trm_flags |= TF_IDLIN|TF_IDCHR|TF_CLEOL|TF_METAKEY|TF_DIRVID;
X			/* Always use lowest possible costs */
X		/*	tvc_ci = 0; */	/* Default */
X			tvc_cin = 2;
X		/*	tvc_cd = 0; */	/* Default */
X			tvc_cdn = 2;
X		/*	tvc_ld = 0; */	/* Default */
X			tvc_ldn = 2;
X		/*	tvc_li = 0; */	/* Default */
X			tvc_lin = 2;
X			break;
X#endif /*TX_COHIBM*/
X#if TX_OM8025
X		case TN_OM8025:
X			trm_flags |= TF_IDLIN|TF_IDCHR|TF_CLEOL;
X			tvc_pos = 6;
X		/*	tvc_ci = tvc_cd = 0; */	/* Default */
X			tvc_cin = 4;
X			tvc_cdn = 2;
X		/*	tvc_ld = tvc_li = 0; */	/* Default */
X			tvc_ldn = 10;		/* Crude approx */
X			tvc_lin = 10;
X			if(trm_ospeed > 7)	/* If faster than 300 baud */
X				trm_flags &= ~TF_IDLIN;	/* Turn off LID */
X			break;
X#endif /*TX_OM8025*/
X#if TX_TVI925
X		case TN_TVI925:
X			trm_flags |= TF_IDLIN|TF_IDCHR|TF_CLEOL;
X			tvc_ci = tvc_cd = tvc_cin = tvc_cdn
X				= tvc_ldn = tvc_lin = 2;
X			break;
X#endif /*TX_TVI925*/
X	  }
X	if(tibfmsk < 0)		/* If mask is still default -1, set it. */
X		tibfmsk = ((trm_flags&TF_METAKEY) ? 0377 : 0177);
X}
X
X/* T_FATAL(str) - prints error message and exits.
X*/
Xt_fatal(str)
Xchar *str;
X{	writerr("ELLE: \"");
X	writerr(tv_stype);
X	writerr("\" terminal ");
X	writerr(str);
X	writerr("\n");
X	exit(1);		/* Terminate with prejudice */
X}
X
X/* T_ENTER is called after TS_ENTER to set the terminal parameters for
X *	editing (as opposed to normal typeout).
X *	Standout mode must initially be off.
X */
X
Xt_enter()
X{	switch(tv_type)
X	  {
X#if TX_TERMCAP
X		case TN_TERMCAP:
X                        putpad(TC_ti);
X			if(tc_km) putpad(TC_mm);	/* Use meta if poss */
X#if FX_SOWIND
X			t_standout(0);		/* Ensure standout mode off */
X#endif
X			break;
X#endif /*TX_TERMCAP*/
X#if TX_DM2500
X		case TN_DM2500:
X			tput(030);	/* Just in case, flush stray modes */
X			break;
X#endif /*TX_DM2500*/
X#if TX_COHIBM
X		case TN_COHIBM:		/* Note TN_H19 will exist too */
X#endif /*TX_COHIBM*/
X#if TX_H19
X		case TN_H19:
X			/* Enter ZDS (Heath) mode, then
X			 * Exit graphics mode (G) Exit ins-char mode (O)
X			 * exit rev video mode (q) exit hold-screen mode (\)
X			 * set cursor on (y5)
X			 */
X			tputz("\033[?2h\033G\033O\033q\033\\\033y5");
X			/* Set Discard-at-EOL (w)
X			 * Set no auto-CR (y9)
X			 * Enable 25th line (x1)
X			 */
X			tputz("\033w\033y9\033x1");
X			break;
X#endif /*TX_H19*/
X	  }
X}
X
X/* T_EXIT - Leave editing modes.  This function should restore
X**	the terminal's modes to what they were before ELLE was started.
X**	Standout mode is turned off.
X*/
X
Xt_exit()
X{
X	switch(tv_type)
X	  {
X#if TX_TERMCAP
X		case TN_TERMCAP:
X			if(tc_km) putpad(TC_mo);	/* Turn off meta */
X			putpad(TC_te);
X			break;
X#endif /*TX_TERMCAP*/
X#if TX_DM2500
X		case TN_DM2500:
X			tput(035);	/* Turn on roll mode */
X			break;
X#endif /*TX_DM2500*/
X#if TX_COHIBM
X		case TN_COHIBM:		/* If this exists, TN_H19 will too */
X#endif /*TX_COHIBM*/
X#if TX_H19
X		case TN_H19:
X			tputz("\033v");		/* Turn EOL-wrap back on */
X#if DNTTY
X			tputz("\033<");		/* Return to ANSI mode */
X#endif /*DNTTY*/
X			break;
X#endif /*TX_H19*/
X	  }
X}
X
X/* T_CLEAR() - Clears the screen and homes the cursor.
X *	Always valid - ELLE refuses to support terminals without this.
X */
X
Xt_clear ()
X{	switch(tv_type)
X	  {
X#if TX_TERMCAP
X		case TN_TERMCAP:
X			putnpad(TC_cl,scr_ht);
X			break;
X#endif /*TX_TERMCAP*/
X#if TX_DM2500
X		case TN_DM2500:
X			tputz("\036\036");	/* Double Master Clear */
X			break;
X#endif /*TX_DM2500*/
X#if TX_COHIBM
X		case TN_COHIBM:		/* Note TN_H19 will exist too */
X#endif /*TX_COHIBM*/
X#if TX_H19
X		case TN_H19:
X			tputz("\033E");
X		/*	tputn(zpadstr,9);	*/
X			break;
X#endif /*TX_H19*/
X#if TX_OM8025
X		case TN_OM8025:
X			tputz("\033H\033J");	/* Home then CLEOS */
X			tpad(1000);		/* One second!!!! */
X			break;
X#endif /*TX_OM8025*/
X#if TX_TVI925
X		case TN_TVI925:
X			tput(032);	/* ^Z */
X			break;
X#endif /*TX_TVI925*/
X	  }
X	curs_lin = curs_col = 0;
X}
X
X/* T_CURPOS(y, x) - Absolute move.  Place cursor in given position
X *	regardless of where it currently is.
X *	Updates curs_lin, curs_col.
X *	Always valid -- ELLE refuses to support terminals without this.
X */
X
Xt_curpos (lin, col)
Xregister int lin, col;
X{
X	if(col > scr_wid)		/* Easiest to catch here */
X		col = scr_wid;
X
X	/* Do absolute positioning */
X	switch(tv_type)
X	  {
X#if TX_TERMCAP
X		case TN_TERMCAP:
X			putpad(tgoto(TC_cm, col, lin));
X			break;
X#endif /*TX_TERMCAP*/
X#if TX_DM2500
X		case TN_DM2500:
X			tput(014);
X			tput(col^0140);
X			tput(lin^0140);
X			break;
X#endif /*TX_DM2500*/
X#if TX_COHIBM
X		case TN_COHIBM:		/* If this exists, TN_H19 will too */
X#endif /*TX_COHIBM*/
X#if TX_H19
X		case TN_H19:
X			tputz("\033Y");
X			tput(lin+040);
X			tput(col+040);
X			break;
X#endif /*TX_H19*/
X#if TX_OM8025
X		case TN_OM8025:
X			tputz("\033\175");
X			tput(0100+((lin+1)>>4));
X			tput(0100+((lin+1)&017));
X			tput(0100+((col+1)>>4));
X			tput(0100+((col+1)&017));
X			break;
X#endif /*TX_OM8025*/
X#if TX_TVI925
X		case TN_TVI925:
X			tputz("\033=");
X			tput(lin+040);
X			tput(col+040);
X			break;
X#endif /*TX_TVI925*/
X	  }
X	curs_lin = lin;
X	curs_col = col;
X}
X
X/* T_BACKSPACE() - Back up 1 character position.
X *	Updates curs_col.
X *	Only valid if tvc_bs has a "reasonable" value ( < 1000)
X */
X
Xt_backspace()
X{
X#if TX_TERMCAP
X	if(BC) tputz(BC);	/* Use alternate BS */
X	else
X#endif
X	tput('\010');		/* Send BS */
X	--curs_col;
X}
X
X/* T_BELL() - Ring terminal's bell (or flash something, or whatever).
X *	Forces out all output thus far, to ensure immediate attention.
X *	This used to be an unbuffered feep, but was changed to use normal
X *	output path in order to avoid messing up terminal escape sequences.
X */
Xt_bell()
X{
X#if TXC_VISBEL && TX_TERMCAP
X	if(TC_vb)
X	        tputz(TC_vb);		/* Do visible bell if possible */
X	else
X#endif
X        tput(BELL);
X        tbufls();       /* Force it out */
X}
X
X/* T_CLEOL() - Clear to End Of Line.
X *	Only valid if trm_flags has TF_CLEOL set.
X */
X
Xt_cleol ()
X{
X	switch(tv_type)
X	  {
X#if TX_TERMCAP
X		case TN_TERMCAP:
X			putpad(TC_ce);
X			break;
X#endif /*TX_TERMCAP*/
X#if TX_DM2500
X		case TN_DM2500:
X			tput(027);
X			break;
X#endif /*TX_DM2500*/
X#if TX_COHIBM
X		case TN_COHIBM:		/* If this exists, TN_H19 will too */
X#endif /*TX_COHIBM*/
X#if TX_H19
X		case TN_H19:
X			tputz("\033K");
X			break;
X#endif /*TX_H19*/
X#if TX_OM8025
X		case TN_OM8025:
X			tputz("\033K");
X			tpad(41);	/* 1/25 sec padding */
X			break;
X#endif /*TX_OM8025*/
X#if TX_TVI925
X		case TN_TVI925:
X			tputz("\033T");
X			break;
X#endif /*TX_TVI925*/
X	  }
X}
X
X/* T_INSLIN(n, bot) - Insert lines in window.
X *	n   - # blank lines to insert.
X *	bot - # of last line of current window
X *
X *		The current line is moved down and N blank lines inserted.
X *	Lines which are moved past bot are lost.
X *	May leave cursor in random place.
X *	Only valid if trm_flags has TF_IDLIN set.
X */
X
Xt_inslin (n, bot)
Xint   n;			/* number of lines */
Xint   bot;			/* line number of last line in window */
X{	register  i, j;
X	int savc,savl;
X
X	if((i = n) <= 0) return;
X	if(bot < (scr_ht-1))
X	  {	savc = curs_col;
X		savl = curs_lin;
X		t_curpos(bot-i, 0);
X		t_dellin(i, scr_ht);
X		t_curpos(savl, savc);
X	  }
X	switch(tv_type)
X	  {
X#if TX_TERMCAP
X		case TN_TERMCAP:
X			if(TC_AL)
X				putpar(TC_AL, i, i);
X			else if(TC_ia)
X			  {	putpad(TC_im);
X				do { putpad(TC_ia);
X				  } while(--i);
X				putpad(TC_ei);
X			  }
X			else
X				do { putnpad(TC_al, scr_ht - curs_lin);
X				  } while(--i);
X			break;
X#endif /*TX_TERMCAP*/
X#if TX_DM2500
X		case TN_DM2500:
X			tput(020);		/* Enter I/D mode */
X			do {	tput(012);		/* Insert line */
X			  	switch(trm_ospeed)
X				  {	case 13: j = 17; break;	/* 9600 */
X					case 12: j = 8; break;	/* 4800 */
X					case 11: j = 4; break;	/* 2400 */
X					case 9:  j = 2; break;	/* 1200 */
X					default: j = 0; break;
X				  }
X				tpadn(j);
X			  } while(--i);
X			tput(030);			/* Exit I/D mode */
X			break;
X#endif /*TX_DM2500*/
X#if TX_H19
X	/* NOTE: H19 supposedly requires 19 ms for each line during line I/D
X	 * operations.
X	 * In actual practice, at 9600 baud 25 pads are necessary (24 wont work!)
X	 * for both I and D.  Plus esc-E needs 9 pads.
X	 */
X		case TN_H19:
X			do {	tputz("\033L");
X				switch(trm_ospeed)
X				  {	case 13: j = 25; break;
X					case 9:	j = 4; break;
X					case 7: j = 1; break;
X					default: j = 0; break;
X				  }
X				tpadn(j);
X			  } while(--i);
X			break;
X#endif /*TX_H19*/
X#if TX_COHIBM
X		case TN_COHIBM:
X			do {	tputz("\033L");  /* no padding required */
X		  	  } while(--i);
X			break;
X#endif /*TX_COHIBM*/
X#if TX_OM8025
X		case TN_OM8025:
X			do {	tputz("\033L");
X				tpad(100*(scr_ht - curs_lin));	/* .1 per moved line*/
X			  } while(--i);
X			break;
X#endif /*TX_OM8025*/
X#if TX_TVI925
X		case TN_TVI925:
X			do tputz("\033E");
X			while(--i);
X			break;
X#endif /*TX_TVI925*/
X	  }
X}
X
X/* T_DELLIN(n, bot) - Delete lines from window.
X *	n   - # lines to delete.
X *	bot - # of last line of current window.
X *		The current line, and N-1 following lines, are deleted.
X *	Blank lines are inserted past bot.
X *	Cursor should be left at original position.
X *	Only valid if trm_flags has TF_IDLIN set.
X */
Xt_dellin (n, bot)
Xint   n;			/* number of lines */
Xint   bot;			/* line number of last line in window */
X{	register  i, j;
X	int savl, savc;
X
X	if((i = n) <= 0) return;
X	switch(tv_type)
X	  {
X#if TX_TERMCAP
X		case TN_TERMCAP:
X			if(TC_DL)
X				putpar(TC_DL, i, i);
X			else if(TC_id)
X			  {	putpad(TC_dm);
X				do putpad(TC_id);
X				while(--i);
X				putpad(TC_ed);
X			  }
X			else
X				do { putnpad(TC_dl,scr_ht - curs_lin);
X				  } while(--i);
X
X			break;
X#endif /*TX_TERMCAP*/
X#if TX_DM2500
X		case TN_DM2500:
X			tput(020);
X			do {	tput(032);
X			  	if(trm_ospeed >= 13)	/* 9600 */
X					tput(0177);
X			  } while(--i);
X			tput(030);
X			break;
X#endif /*TX_DM2500*/
X#if TX_H19
X		case TN_H19:
X			do {	tputz("\033M");
X				switch(trm_ospeed){
X					case 13: j = 25; break;
X					case 9:	j = 4; break;
X					case 7: j = 1; break;
X					default: j = 0; break;
X					}
X				tpadn(j);
X			  } while(--i);
X			break;
X#endif /*TX_H19*/
X#if TX_COHIBM
X		case TN_COHIBM:
X			do {	tputz("\033M");	  /* no padding required */
X			  } while(--i);
X			break;
X#endif /*TX_COHIBM*/
X#if TX_OM8025
X		case TN_OM8025:
X			do {	tputz("\033M");
X				tpad(100*(scr_ht - curs_lin));
X			  } while(--i);
X			break;
X#endif /*TX_OM8025*/
X#if TX_TVI925
X		case TN_TVI925:
X			do {	tputz("\033R");
X			  } while(--i);
X			break;
X#endif /*TX_TVI925*/
X	  }
X	if(bot < (scr_ht-1))
X	  {	savl = curs_lin;
X		savc = curs_col;
X		t_curpos(bot-n,0);
X		t_inslin(n,scr_ht);
X		t_curpos(savl,savc);
X	  }
X}
X
X/* T_INSCHR(n, str) - Insert n chars in current line
X *	n   - # characters to insert
X *	str - Pointer to char string.  If 0, insert spaces.
X *
X *	Insert N characters from string str at current position.
X *	The cursor may move but curs_col must be updated.
X *	Only valid if trm_flags has TF_IDCHR set.
X */
Xt_inschr(n, str)
Xint n;
Xchar *str;
X{	register int i;
X	register char *cp;
X
X	if((i = n) <= 0) return;
X	cp = str;
X	switch(tv_type)
X	  {
X#if TX_TERMCAP
X		case TN_TERMCAP:
X			putpad(TC_im);		/* Go into insert mode */
X			if(TC_IC)
X			  {	putpar(TC_IC, i, 1);
X				if(cp) tputn(cp, i);
X				else do tput(SP); while(--i);
X			  }
X			else do {
X				if(TC_ic) putpad(TC_ic);
X				if(cp) tput(*cp++);
X				else tput(SP);
X				if(TC_ip) putpad(TC_ip);
X			  } while(--i);
X			putpad(TC_ei);		/* Exit insert mode */
X			curs_col += n;
X			break;
X#endif /*TX_TERMCAP*/
X#if TX_COHIBM
X		case TN_COHIBM:		/* If this exists, TN_H19 will too */
X#endif /*TX_COHIBM*/
X#if TX_H19
X		case TN_H19:
X			tputz("\033@");		/* Enter ins char mode */
X			do {	if(cp) tput(*cp++);
X				else tput(SP);
X			  } while(--i);
X			tputz("\033O");		/* Exit ins char mode */
X			curs_col += n;
X			break;
X#endif /*TX_H19*/
X#if TX_DM2500
X		case TN_DM2500:
X			tput(020);		/* Enter I/D mode */
X			if(trm_ospeed == 13)	/* 9600 baud lossage */
X			  {	do {
X					tputz(" \177");	/* SP and DEL */
X				  } while(--i);
X				tput(030);
X				i = n;
X				if(i < 3)	/* If close enough, */
X					tputn("\010\010", i);	/* use BSes */
X				else t_curpos(curs_lin, curs_col);
X			  }
X			else			/* Not 9600, can win */
X			  {	do { tput(034);
X				  } while(--i);
X				tput(030);
X				if(cp == 0) return;
X				i = n;
X			  }
X
X			do {	if(cp) tput(*cp++);
X				else tput(SP);
X			  } while(--i);
X			curs_col += n;
X			break;
X#endif /*TX_DM2500*/
X#if TX_OM8025
X		case TN_OM8025:
X			do {
X				tputz("\033@");
X				if(cp) tput(*cp++);
X				else tput(SP);
X			  } while(--i);
X			curs_col += n;
X			break;
X#endif /*TX_OM8025*/
X#if TX_TVI925
X		case TN_TVI925:
X			do {	tputz("\033Q");
X			  } while(--i);
X			if(cp)
X			  {	tputn(cp, n);
X				curs_col += n;
X			  }
X			break;
X#endif /*TX_TVI925*/
X	  }
X}
X
X/* T_DELCHR(n) - Delete N chars in current line.
X *	Deletes the N characters to the right of the cursor.  Remaining
X *	chars are shifted left.  The cursor should not move.
X *	Only valid if trm_flags has TF_IDCHR set.
X */
Xt_delchr(n)		/* Delete N chars at current loc */
Xint n;
X{	register int i;
X
X	if((i = n) <= 0) return;
X	switch(tv_type)
X	  {
X#if TX_TERMCAP
X		case TN_TERMCAP:
X			putpad(TC_dm);	/* Enter delete mode */
X			if(TC_DC)
X				putpar(TC_DC, i, 1);
X			else do {	/* Delete char while in del mode */
X				putpad(TC_dc);
X			} while(--i);
X			putpad(TC_ed);	/* Exit delete mode */
X			break;
X#endif /*TX_TERMCAP*/
X#if TX_COHIBM
X		case TN_COHIBM:		/* If this exists, TN_H19 will too */
X#endif /*TX_COHIBM*/
X#if TX_H19
X		case TN_H19:
X			do tputz("\033N");
X			while(--i);
X			break;
X#endif /*TX_H19*/
X#if TX_DM2500
X		case TN_DM2500:
X			tput(020);		/* Enter I/D mode */
X			do if(trm_ospeed == 13)	/* 9600? */
X				tputz("\010\177");	/* BS and DEL */
X			  else tput(010);
X			while(--i);
X			tput(030);		/* Exit I/D mode */
X			break;
X#endif /*TX_DM2500*/
X#if TX_OM8025
X		case TN_OM8025:
X			do tputz("\033P");
X			while (--i);
X			break;
X#endif /*TX_OM8025*/
X#if TX_TVI925
X		case TN_TVI925:
X			do {	tputz("\033W");
X			  } while(--i);
X#endif /*TX_TVI925*/
X	  }
X}
X
X#if FX_SOWIND
X
X/* T_STANDOUT(n) - Enter or leave standout mode.
X *	n   - 0 to return to normal display mode,
X *	      1 to enter standout display mode.
X *		This is usually reverse video but may be something else.
X *
X *	Only valid if trm_flags has TF_SO set.
X */
X
Xt_standout(on)
Xint on;
X{
X	switch(tv_type)
X	  {
X#if TX_TERMCAP
X		case TN_TERMCAP:
X			putpad(on ? TC_so : TC_se);
X			break;
X#endif /*TX_TERMCAP*/
X
X#if TX_COHIBM
X		case TN_COHIBM:		/* Note TN_H19 will exist too */
X#endif /*TX_COHIBM*/
X#if TX_H19
X		case TN_H19:
X			tputz(on ? "\033p" : "\033q");
X			break;
X#endif /*TX_H19*/
X	  }
X}
X#endif /*FX_SOWIND*/
X
X
X/* TPADN(n) - Output N pad chars.
X */
Xstatic void
Xtpadn(n)
Xint n;
X{	register int i, pad;
X	if((i = n) > 0)
X	  {	pad = tv_padc;
X		do { tput(pad);
X		  } while(--i);
X	  }
X}
X
X/* TPAD(msec) - Output padding for given # of milliseconds.
X */
Xstatic void
Xtpad(n)
Xint n;
X{	register int i, i2;
X
X	i = n;
X	while(i > 0)
X	  {	if((i2 = 320) < i)	/* So can use integers */
X			i2 = i;
X		i -= i2;
X		i2 *= 100;
X		while((i2 -= tv_cspeed) > 0)
X			tput(tv_padc);
X	  }
X}
X#if TX_TERMCAP
X/*
X * Print the string str, interpreting padding.
X */
Xint tput();	/* Our output function */
Xstatic void
Xputpad(str)
Xchar *str;
X{	if(str) tputs(str, 1, tput);	/* Invoke TERMCAP function */
X}
Xstatic void
Xputnpad(str,n)
Xchar *str;
Xint n;
X{	if(str) tputs(str, n, tput);
X}
Xstatic void
Xputpar(str, par, n)		/* Wish we had tparm() */
Xchar *str;
Xint par,n;
X{	putnpad(tgoto(str, 0, par), n);
X}
X#endif /*TX_TERMCAP*/
X
X/*
X * Read in the stuff from termcap upon startup.
X */
X
X#if TX_TERMCAP
Xstatic int tstrlen(), tstrlp();
X
X#ifndef TCAPSLEN
X#define TCAPSLEN 1024	/* Default size of buffer for TERMCAP strings */
X#endif /*-TCAPSLEN*/
X
Xstatic int
Xgetcap(stype)
Xchar *stype;
X{	register char *t;
X	register int i;
X	int buflen;
X	char *tcbuf, *tcbptr;		/* Pointers into termcap buffer */
X	char tmpstr[4];
X	char tmpbuf[TCAPSLEN];		/* Allocate from stack */
X	char *malloc();
X	char *realloc();
X
X	/* First see if can find the terminal type. */
X	if((tgetent(tmpbuf, stype)) != 1)
X		return(0);
X
X	/* Found it!  Set up a string buffer to save the caps. */
X	if(!(tcbuf = malloc(TCAPSLEN)))	/* Get permanent buffer */
X		t_fatal(" - cannot allocate termcap buffer");
X	tcbptr = tcbuf;
X
X	/* Now gobble all the string caps that ELLE wants to know about. */
X	tmpstr[3] = '\0';
X	i = NTCAPS;
X	do {
X		tmpstr[0] = tcap[i].tcicod1;	/* Make str of the code */
X		tmpstr[1] = tcap[i].tcicod2;
X		tcap[i].tccap = tgetstr(tmpstr, &tcbptr);	/* Get cap */
X	} while(--i);
X	buflen = tcbptr - tcbuf;	/* String buffer done, finalize */
X	if(buflen >= TCAPSLEN)
X		t_fatal("description too big!");
X	realloc(tcbuf, buflen);		/* Free up unused part of buffer */
X					/* (this better not move it!!!) */
X
X	/* Now get the number/flag stuff that ELLE needs. */
X	tc_am = tgetflag("am");		/* auto wrap */
X	tc_km = (tgetflag("km")		/* TTY has meta key */
X		|| tgetflag("MT"));	/* Alternate version of "km"?? */
X	scr_ht = tgetnum("li");		/* Set screen height (# lines) */
X	scr_wid = tgetnum("co");	/* Set screen width (# cols) */
X
X	/* Now initialize the stupid external vars that TERMCAP rtns want. */
X	if(TC_pc) PC = *TC_pc;	/* Pad char */
X	BC = TC_bc;		/* Backspace str (if no BS) */
X	UP = TC_up;		/* Cursor up */
X	ospeed = trm_ospeed;	/* Put output speed here */
X
X
X	/* Basic data extracted, now mull over it and set the remaining
X	 * ELLE variables
X	 */
X#if FX_SOWIND
X	if(tgetnum("sg") <= 0)		/* If no magic cookie problems */
X	  {	if (TC_so && TC_se)	/* And have standout caps, */
X			trm_flags |= TF_SO;	/* Say has standout cap */
X	  }
X#endif
X
X	if (!(TC_cm && TC_cl))
X		t_fatal("lacks cursor addressing or clear screen.");
X	tvc_pos = tstrlen(TC_cm);		/* Find cost of abs move */
X	if(BC)					/* Find cost of backspace */
X		tvc_bs = tstrlen(BC);
X
X	/* Find costs for doing I/D char operations */
X	if ((TC_im||TC_ic) && (TC_dm||TC_dc))
X	  {	trm_flags |= TF_IDCHR;
X		tvc_ci  = tstrlen(TC_im)+tstrlen(TC_ei);
X		tvc_cin = tstrlen(TC_ic)+1+tstrlen(TC_ip);
X		if(TC_IC)			/* If have multi-IC, use it */
X		  {	tvc_ci += tstrlp(TC_IC, 1);
X			tvc_cin = 1;
X		  }
X		tvc_cd  = tstrlen(TC_dm)+tstrlen(TC_ed);
X		tvc_cdn = tstrlen(TC_dc);
X		if(TC_DC)			/* If have multi-DC, use it */
X		  {	tvc_cd += tstrlp(TC_DC, 1);
X			tvc_cdn = 0;
X		  }
X	  }
X
X	/* Find costs for doing I/D line operations */
X	if ((TC_ia || TC_al) && (TC_id || TC_dl))
X	  {	trm_flags |= TF_IDLIN;
X		tvc_li = 0;			/* Usual case */
X		tvc_lin = tstrlen(TC_al);
X		if(TC_AL)			/* If have multi-IL, use it */
X		  {	tvc_li  = tstrlp(TC_AL, 1);
X			tvc_lin = tstrlp(TC_AL, 2) - tvc_lin;
X		  }
X		else if(TC_ia)
X		  {	tvc_li  = tstrlen(TC_im)+tstrlen(TC_ei);
X			tvc_lin = tstrlen(TC_ia);
X		  }
X
X		tvc_ld = 0;			/* Usual case */
X		tvc_ldn = tstrlen(TC_dl);
X		if(TC_DL)			/* If have multi-DL, use it */
X		  {	tvc_ld  = tstrlp(TC_DL, 1);
X			tvc_ldn = tstrlp(TC_DL, 2) - tvc_ld;
X		  }
X		else if(TC_id)
X		  {	tvc_ld = tstrlen(TC_dm)+tstrlen(TC_ed);
X			tvc_ldn = tstrlen(TC_id);
X		  }
X	  }
X
X	if (tc_am)
X	  {	scr_wid--;		/* For now, avoid invoking wrap. */
X#if 0
X		trm_flags |= AUTOWRAP;	/* */
X#endif
X	  }
X	if (TC_ce) trm_flags |= TF_CLEOL;		/* Term has CLEOL? */
X	if (tc_km) trm_flags |= TF_METAKEY;	/* Term has meta key? */
X
X	return(1);
X}
X
X/* Pair of routines which conspire in order to find # chars actually output
X * by a particular termcap string.
X */
Xstatic int _tslen;		/* Stored count */
Xstatic void _tslinc(ch) { _tslen++; }
Xstatic int
Xtstrlen(str)
Xchar *str;
X{	_tslen = 0;
X	if(str && str[0])
X		tputs(str, 1, _tslinc);	/* Mult padding by just 1 */
X	return(_tslen);
X}
X
Xstatic int
Xtstrlp(str, par)		/* Same but with parameter */
Xchar *str;
Xint par;
X{
X#if 0
X	if(str)
X	  {	char *cp = tgoto(str, 0, par);
X		int i = strlen(cp);
X		while(--i >= 0)
X			printf(" %o", *cp++);
X		printf("\n");
X	  }
X#endif
X	return !str ? 0 : tstrlen(tgoto(str, 0, par));
X}
X#endif /*TX_TERMCAP*/
X
X/* Direct-Video terminal output routine
X *	Currently only COHERENT has this capability.
X */
X
X#if COHERENT
X#include <sgtty.h>
X
Xstruct vidctl {
X	int	v_position;		/* Position in video memory */
X	int	v_count;		/* Number of characters to transfer */
X	char	*v_buffer;		/* Character buffer to read/write */
X};
X/*
X * Attribute masks for TIOVPUTB - attributes occupy odd addresses
X * in video memory.
X */
X#define	VNORM	0x07			/* Ordinary Video */
X#define	VINTE	0x08			/* Intense video */
X#define	VBLIN	0x80			/* Blinking video */
X#define	VREVE	0x70			/* Reverse video */
X#define	VUNDE	0x01			/* Underline video (mono board) */
X
X/* T_DIRECT(line, col, string, len) - Do direct-video output of string.
X *	Puts the string ("len" chars in length) on the screen starting at
X *	the X,Y character position given by col, line.
X *	This routine is only called if terminal has the "TF_DIRVID" flag set.
X */
Xt_direct(lin, col, str, len)
Xint lin, col;
Xregister char *str;
Xregister int len;
X{	register char *cp;
X	char vbuf[MAXLINE*2];
X	struct vidctl v;
X
X	if(len <= 0) return;
X	tbufls();		/* Ensure normal output is forced out */
X	v.v_position = (lin*80 + col)*2;
X	v.v_count = len*2;
X	v.v_buffer = cp = vbuf;
X	do {
X		*cp++ = *str++;
X		*cp++ = VNORM;
X	  } while(--len);
X	ioctl(1, TIOVPUTB, &v);
X}
X#endif /*COHERENT*/
X
X/*
X * Terminal Output buffering routines
X */
X
Xstatic char tbuf[TOBFSIZ];	/* Output buffer */
Xstatic int tbufcnt = 0;		/* # chars of room left in buffer */
Xstatic char *tbufp = 0;		/* Pointer to deposit in buffer */
X
Xtput(ch)
Xint ch;
X{	if(--tbufcnt < 0)
X		tbufls();
X	*tbufp++ = ch;
X}
X
Xtputz(str)
Xchar *str;
X{	register int c;
X	register char *cp, *tp;
X	cp = str;
X	tp = tbufp;
X	while(c = *cp++)
X	  {	if(--tbufcnt < 0)
X		  {	tbufp = tp;
X			tbufls();
X			tp = tbufp;
X		  }
X		*tp++ = c;
X	  }
X	tbufp = tp;
X}
X
Xtputn(str,cnt)
Xchar *str;
Xint cnt;
X{	register int c;
X	register char *cp, *tp;
X	cp = str;
X	tp = tbufp;
X	if((c = cnt) > 0)
X	do {
X		if(--tbufcnt < 0)
X		  {
X			tbufp = tp;
X			tbufls();
X			tp = tbufp;
X		  }
X		*tp++ = *cp++;
X	  } while(--c);
X	tbufp = tp;
X}
X
Xtbufls()
X{	register int cnt;
X
X	if(tbufp
X	  && (cnt = tbufp - tbuf) > 0)		/* # chars written */
X		write(1, tbuf, cnt);		/* Out they go */
X	tbufp = tbuf;
X	tbufcnt = TOBFSIZ-1;	/* Allow for usual expected decrement */
X}
X
X/*
X * Terminal Input buffering routines
X */
X
Xint tibfmsk = -1;		/* Mask AND'ed with input chars (external) */
Xstatic char tibuf[TIBFSIZ];	/* TTY input buffer */
Xstatic char *tibfp;		/* Pointer to read from buffer */
Xstatic int tibfcnt = 0;		/* # chars left to be read from buffer */
X
Xtgetc()
X{
X#if SUN
X	register int c;
X	extern int sun_winfd, sun_rdevf;
X
X	if(sun_winfd)
X	  {	if(!sun_rdevf)
X			return(sun_input(1)&tibfmsk);
X		sun_rdevf = 0;		/* Check mouse too, but only once! */
X		c = sun_input(0);
X		if(c != -1) c &= tibfmsk;
X		return(c);
X	  }
X#endif /*SUN*/
X	while(--tibfcnt < 0)
X		tibfcnt = read(0,(tibfp = tibuf),TIBFSIZ);
X	return((*tibfp++)&tibfmsk);
X}
X
Xtinwait()
X{	return(tibfcnt > 0 || ts_inp());
X}
/
echo x - eevini.c
sed '/^X/s///' > eevini.c << '/'
X/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/* EEVINI - ELLE initialized variables and array storage.
X *	Initial values are defined here, but the vars must be
X * declared in ELLE.H as well so that references from all modules will
X * compile correctly.
X *	Arrays are also allocated here, so size re-definitions only require
X * re-compiling this single small module.
X */
X
X#define EXT		/* Allocate storage for non-initialized vars */
X#include "elle.h"
X
X#ifndef EVFILMOD
X#if V6
X#define EVFILMOD (0600)	/* (int) Default file creation mode on V6 */
X#else
X#define EVFILMOD (0666)	/* (int) Default file creation mode on V7, note */
X#endif /*-V6*/		/*       paranoids can use V7 "umask" in shell. */
X#endif
X#ifndef EVFNO1
X#define EVFNO1 0	/* (char *) "Old" filename prefix */
X#endif
X#ifndef EVFNN1
X#define EVFNN1 0	/* (char *) "New" filename prefix */
X#endif
X#ifndef EVFNO2
X#define EVFNO2 "O"	/* (char *) "Old" filename postfix */
X#endif
X#ifndef EVFNN2
X#define EVFNN2 "N"	/* (char *) "New" filename postfix */
X#endif
X#ifndef EVFCOL
X#define EVFCOL (71)	/* (int) Initial value for Fill Column */
X#endif
X#ifndef EVCCOL
X#define EVCCOL (40)	/* (int) Initial value for Comment Column */
X#endif
X#ifndef EVNWPCT
X#define EVNWPCT 50	/* (int) 50% For random New Window, center cursor. */
X#endif
X#ifndef EVMVPCT
X#define EVMVPCT 67	/* (int) 67% When move off edge, show 67% new stuff */
X#endif
X#ifndef EVMODWSO
X#define EVMODWSO 0	/* (bool) Initial mode window standout mode */
X#endif
X#ifndef EV2MODEWS
X#define EV2MODEWS 1	/* 2-mode-window flag. 0=Never, 1=if SO, 2=always */
X#endif
X#ifndef EVMARKSHOW
X#define EVMARKSHOW 0	/* (char *) String shown for Set Mark */
X#endif
X#ifndef EVHELPFILE	/* (char *) Location of ELLE help file. */
X#define EVHELPFILE "/usr/src/elle/help.dat"
X#endif
X
Xchar *ev_verstr = "ELLE 4.1b";	/* String: Editor name and version # */
Xint ev_filmod = EVFILMOD;	/* Default file creation mode */
Xchar *ev_fno1 = EVFNO1;		/* "Old" filename prefix */
Xchar *ev_fnn1 = EVFNN1;		/* "New" filename prefix */
Xchar *ev_fno2 = EVFNO2;		/* "Old" filename postfix */
Xchar *ev_fnn2 = EVFNN2;		/* "New" filename postfix */
X
Xint ev_fcolumn = EVFCOL;	/* Initial value for Fill Column */
X#if FX_INDCOMM
Xint ev_ccolumn = EVCCOL;	/* Initial value for Comment Column */
X#endif
X
X/* New window selection parameters.
X**	Both are expressed as an integer % of window lines (where the whole
X**	window is 100), and apply when a new window is selected.
X** ev_nwpct - when "New Window" is called, % of lines between top and cursor.
X**	Also applies when screen has changed randomly.
X** ev_mvpct - when cursor moves out of window, this is the % of lines
X**	between top and cursor (if moved off top) or between bottom and
X**	cursor (if moved off bottom).
X*/
Xint ev_nwpct = EVNWPCT;		/* New Window cursor loc preference (%) */
Xint ev_mvpct = EVMVPCT;		/* Moved cursor loc preference (%) */
X
X#if FX_SOWIND
Xint ev_modwso = EVMODWSO;	/* Initial mode window standout flag */
X#endif
X#if FX_2MODEWINDS
Xint ev_2modws = EV2MODEWS;	/* Initial 2-mode-wind flag */
X#endif
Xchar *ev_markshow = EVMARKSHOW;	/* String to display when Set Mark done */
X
Xchar *ev_helpfile = EVHELPFILE;	/* Location of ELLE help file */
Xchar *ev_profile = EVPROFBINFILE; /* Location of ELLE binary user profile */
X				/* Note ELLE doesn't use EVPROFTEXTFILE. */
X
X/* Array allocations */
X
Xstruct scr_line *scr[MAXHT];		/* Array of screen line structures */
XSBSTR *kill_ring[KILL_LEN];		/* Kill ring table */
/
echo x - elle.h
sed '/^X/s///' > elle.h << '/'
X/* ELLE - Copyright 1982, 1984 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/*
X * ELLE.H	Global ELLE definitions
X */
X
X#ifndef EXT
X#define EXT extern	/* Default assumes these are referencing decls */
X#endif
X
X/* Make identifiers unique in 1st 6 chars as per ANSI rule for externals */
X#define tvc_cin tvccin
X#define tvc_cdn tvccdn
X#define tvc_lin tvclin
X#define tvc_ldn tvcldn
X#define ev_fno1 evfno1
X#define ev_fno2 evfno2
X#define ev_fnn1 evfnn1
X#define ev_fnn2 evfnn2
X
X#define ask_sall	asksal	/* eebuff.c */
X#define ask_save	asksav
X#define buf_tmod	buftmo
X#define buf_tmat	buftma
X#define e_gobob		egobob	/* eeedit.c */
X#define e_gobol		egobol
X#define e_goeob		egoeob
X#define e_goeol		egoeol
X#define fill_prefix	filpfx	/* eefill.c */
X#define fill_plen	filpln
X#define fill_cur_line	filcln
X#define kill_ptr	kilptr		/* eef3.c */
X#define kill_push	kilpsh
X#define ed_insert	edinst	/* eefed.c */
X#define ed_insn		edinsn
X#define ed_deln		eddeln
X#define ed_delete	eddele
X#define f_fillreg	ffilrg	/* eejust.c */
X#define f_fillpara	ffilpa
X
X#include "eesite.h"	/* Insert site-dependent flags and parameters */
X#include "sb.h"		/* Insert SB package definitions */
X#include "eeprof.h"	/* Insert user profile definition.  This is a
X			 *	separate file so ELLEC can use it too. */
X#include "eefidx.h"	/* Insert desired function defs */
X
X/* ELLE Compile-time parameter defaults */
X
X#ifndef KILL_LEN
X#define KILL_LEN 8	/* Size of kill ring */
X#endif
X#ifndef MAXHT
X#define MAXHT 72	/* Height (# lines) of largest screen we'll suport */
X#endif
X#ifndef MAXLINE
X#define MAXLINE 132	/* Width  (# chars) of largest screen we'll support */
X#endif
X#ifndef FNAMELEN
X#define FNAMELEN 14	/* Sys-dep: Max size of last filename component */
X#endif			/*	Check FNAMSIZ if you change this. */
X#ifndef FNAMSIZ
X#define FNAMSIZ 100	/* Sys-dep: Max size of complete filename */
X#endif			/*	This must be at least as large as FNAMELEN! */
X#ifndef ISRCHLIM
X#define ISRCHLIM 50	/* Max # of chars to allow I-search on */
X#endif
X#ifndef TOBFSIZ
X#define TOBFSIZ 80	/* Size of TTY output buffer */
X#endif
X#ifndef TIBFSIZ
X#define TIBFSIZ 50	/* Size of TTY input buffer */
X#endif
X#ifndef ECHOLINES
X#define ECHOLINES 1	/* # of lines for echo area (below mode line) */
X#endif
X#ifndef MAXARGFILES
X#define MAXARGFILES 2	/* # of filename args OK at startup */
X#endif
X
X/* ELLE initialized variables.
X *	Initial values are defined in EEVINI.C, but the vars must be
X * declared here as well so that references from all modules will
X * compile correctly.
X */
X
Xextern char *ev_verstr;		/* String: Editor name and version # */
Xextern int ev_filmod;		/* Default file creation mode */
Xextern char *ev_fno1,*ev_fno2;	/* Pre, postfix for "old" filenames */
Xextern char *ev_fnn1,*ev_fnn2;	/* Pre, postfix for "new" filenames */
Xextern int ev_fcolumn;		/* Fill Column variable */
X#if FX_INDCOMM
Xextern int ev_ccolumn;		/* Comment Column variable */
X#endif
Xextern int ev_nwpct, ev_mvpct;	/* New window selection percentages */
X#if FX_SOWIND
Xextern int ev_modwso;		/* Initial mode window standout flag */
X#endif
X#if FX_2MODEWINDS
Xextern int ev_2modws;		/* Initial setting of 2-mode-window flag */
X#endif
Xextern char *ev_markshow;	/* String to show when Set Mark done */
Xextern char *ev_helpfile;	/* Location of ELLE help file */
Xextern char *ev_profile;	/* Filename of ELLE binary user profile */
Xextern struct profile def_prof;	/* ELLE default user profile */
X
X/* Global variables */
X
XEXT chroff cur_dot;		/* Current dot */
XEXT chroff mark_dot;		/* Dot for mark */
XEXT int mark_p;			/* flag indicating whether mark exists */
XEXT int this_cmd, last_cmd;	/* Command type */
XEXT int unrchf;			/* Stuffed character back for readcommand */
XEXT int exp;			/* Numeric argument for commands */
XEXT int exp_p;			/* Flag meaning an arg was given */
XEXT int pgoal;			/* Permanent goal column */
XEXT int goal;
XEXT char *srch_str;		/* Last search string specified (0 = none) */
XEXT int srch_len;		/* Length of srch_str string */
XEXT int ask_len;		/* Length of last string returned by "ask" */
XEXT char *homedir;		/* User's home directory */
XEXT int kill_ptr;		/* Index into kill ring */
Xextern SBSTR *kill_ring[];	/* Kill ring table (allocated in eevini) */
X
X/* Editor Command types */
X
X#define KILLCMD 1		/* Kill command, for kill merging */
X#define ARGCMD  2		/* Argument-setter, for main loop */
X#define YANKCMD 3		/* Yank command, for yankpop */
X#define LINECMD 4		/* Next or previous line goal hacking */
X#if IMAGEN
X#define INSCMD  5		/* Simple char-insert command, for autowrap */
X#endif /*IMAGEN*/
X
X/* Misc char definitions */
X#define CTRL(ch) (037&ch)
X#define BELL	('\007')	/* Will become \a in ANSI */
X#define BS	('\b')
X#define TAB	('\t')
X#define LF	('\n')
X#define FF	('\f')
X#define CR	('\r')
X#define ESC	('\033')
X#define SP	(' ')
X#define DEL	('\177')
X
X#define CB_META (0200)		/* Meta bit in command char */
X#define CB_EXT  (0400)		/* Extend bit in command char */
X#define METIZER ESC
X#define EXTIZER CTRL('X')
X
X/* Terminal parameters - set at runtime startup */
X
XEXT char *tv_stype;	/* Terminal type string specified by user/system */
XEXT int scr_ht;		/* # lines of main screen area */
XEXT int scr_wid;	/* # columns of screen */
XEXT int scr_wd0;	/* scr_wid - 1 (for 0-origin stuff) */
XEXT int trm_ospeed;	/* Output speed index */
XEXT int tvc_pos;	/* Cost for absolute move (# of output chars) */
XEXT int tvc_bs;		/* Cost for backspace */
XEXT int tvc_ci, tvc_cin;	/* Char ins cost per call, cost per column */
XEXT int tvc_cd, tvc_cdn;	/* Char del   "   "   "     "    "   "     */
XEXT int tvc_li, tvc_lin;	/* Line ins cost per call, cost per line */
XEXT int tvc_ld, tvc_ldn;	/* Line del   "   "   "     "    "   "   */
X
XEXT int trm_flags;	/* Terminal capabilities - bit flags */
X			/* Maybe change to word vars someday (faster) */
X#define TF_IDLIN	01	/* Has I/D line */
X#define TF_IDCHR	02	/* Has I/D char */
X#define TF_SO		04	/* Has usable standout mode */
X#define TF_CLEOL	010	/* Has clear-to-eol */
X#define TF_METAKEY	020	/* Has meta key */
X#define TF_DIRVID	040	/* Has direct-video type interface */
X
X
X/* Redisplay definitions */
X
XEXT int curs_lin;	/* Line # of current cursor (0 origin) */
XEXT int curs_col;	/* Column # of current cursor (0 origin) */
X
XEXT int rd_type;	/* Global var: holds redisplay "hints" */
X#define redp(n) rd_type |= (n)
X
X#define RD_SCREEN 01	/* Clear everything and redisplay */
X#define RD_WINDS 02	/* Check all windows for changes (b/emod) */
X#define RD_MODE 04	/* Mode line has changed, update it. */
X#define RD_WINRES 0400	/* Assume all of window was changed (clear b/emod) */
X#define RD_MOVE	010	/* Cursor has moved */
X#define RD_UPDWIN 020	/* Window fixed, must update modified screen lines */
X/*#define RD_ICHR 0	*//* Hint: Char insert done */
X/*#define RD_DCHR 0	*//* Hint: Char del done */
X#define RD_ILIN 0100	/* Hint: Line insert done */
X#define RD_DLIN 0200	/* Hint: Line del done */
X
X/* #define RD_MOVWIN 02000	*//* Window should be re-positioned */
X#define RD_FIXWIN 02000		/* Window needs fixing (call fix_wind) */
X#define RD_TMOD   04000		/* Text changed in this window, check it. */
X#define RD_WINCLR 010000	/* Clear window with CLEOLs (not yet) */
X#define RD_CHKALL 020000	/* Check all windows for redisplay flags */
X#if IMAGEN
X#define RD_REDO   040000	/* Just re-do the entire window, don't think */
X#endif /*IMAGEN*/
X
X	/* Flags with global effects, only seen in rd_type */
X#define RDS_GLOBALS (RD_SCREEN|RD_MODE|RD_WINDS|RD_CHKALL)
X	/* Flags which are allowed per-window (in w_redp) */
X#define RDS_WINFLGS (~RDS_GLOBALS)
X	/* Flags which force FIX_WIND() to do something */
X#define RDS_DOFIX (RD_WINRES|RD_TMOD|RD_FIXWIN|RD_MOVE)
X
X#define CI_CLINE '!'		/* Char indicator for continued line */
X#define CI_CNTRL '^'		/* Char indicator for control chars */
X#define CI_META  '~'		/* Char indicator for meta-bit (8th) set */
X#define CI_TOP   '|'		/* Char indicator for top-bit (9th) set */
X#define MAXCHAR (8+3)		/* Longest char representation (TAB) + slop */
X
X/* Definitions for screen structures */
X
Xstruct scr_line {
X	chroff sl_boff;		/* Ptr to start of line's text in buffer */
X	int sl_len;		/* # buffer chars in line (incl NL) */
X	char *sl_line;		/* Ptr to screen image of line */
X	int sl_col;		/* # chars in image == # columns used */
X	char sl_flg;		/* Flags - set if this line modified */
X	char sl_cont;		/* If line being continued on next, this */
X				/* contains 1 plus # extra chars (if any) */
X				/* stored at end of this line which shd be */
X				/* put at beg of next line. */
X	char *sl_nlin;	/* New screen image line if modified flag set */
X	int sl_ncol;
X};
X	/* sl_flg definitions */
X#define SL_MOD 01		/* New line exists, must update to it */
X#define SL_EOL 02		/* Buffer line ends with EOL */
X#define SL_CSO 04		/* Current screen line is in standout mode */
X#define SL_NSO 010		/* New screen line is in standout mode */
X#if IMAGEN
X#define SL_REDO 0100		/* Line should be redone completely */
X#endif /*IMAGEN*/
X
Xextern struct scr_line *scr[];	/* Screen line ptrs (allocated in e_vinit) */
X
X
X/* Buffer stuff */
X
Xstruct buffer 
X{	SBBUF b_sb;			/* MUST be 1st thing! */
X	struct buffer *b_next;		/* ptr to next in chain */
X	char *b_name;			/* text name */
X	char *b_fn;			/* filename */
X	chroff b_dot;			/* point (dot) */
X	int b_flags;			/* misc. bits */
X	struct majmode *b_mode;		/* Mode of buffer */
X#if IMAGEN
X	long b_mtime;			/* Last file modification time */
X#endif /*IMAGEN*/
X};
X	/* b_flags definitions */
X#define B_MODIFIED	01		/* Buffer is modified */
X#define B_EOLCRLF	0200		/* On = CRLF mode, off = LF mode */
X#if IMAGEN
X#define B_PERMANENT 002			/* buffer cannot be killed */
X#define B_CMODE	    004			/* "C" mode (HACK HACK) */
X#define B_BACKEDUP  010			/* Buffer has been backed up once */
X#define B_TEXTMODE  020			/* Text mode (auto-wrap, basically) */
X#define B_QUERYREP  040			/* Query-replace mode (qualifier) */
X#endif /*IMAGEN*/
X
X/* Handy macro to check EOL mode */
X#define eolcrlf(buf) (((struct buffer *)buf)->b_flags&B_EOLCRLF)
X
X/* Buffer pointers */
X
XEXT struct buffer
X		 *buf_head,		/* head of list of all buffers */
X		 *cur_buf,		/* buffer we are editing now */
X		 *last_buf,		/* buffer we were editing before */
X		 *lines_buf;		/* buffer for sep_win */
X
X/* Window stuff */
X
Xstruct window
X{	struct window *w_next;		/* ptr to next in chain */
X	int w_flags;			/* Window flags */
X	int w_pos;			/* index of top line */
X	int w_ht;			/* number of lines */
X	struct buffer *w_buf;		/* buffer in this window */
X	int w_pct;			/* % of buffer window is at */
X	int w_redp;			/* Redisplay hints */
X	chroff w_topldot;		/* line currently at top of window */
X	chroff w_dot;			/* Saved dot while not cur_win */
X	chroff w_bmod;			/* Lower bound of modified text */
X	chroff w_emod;			/* Upper bound of modified text */
X					/* (offset from end of buffer!) */
X	chroff w_oldz;			/* Buffer len as of last update */
X};
X
X/* Window flags */
X#define W_STANDOUT	01	/* Use terminal's standout mode for window */
X#define W_MODE		02	/* This is a mode window */
X
X/* Window pointers */
X
XEXT struct window
X		*win_head,		/* head of list of all windows */
X		*cur_win,		/* window we are now in */
X		*user_win,		/* current user window */
X		*oth_win,		/* "other" user window */
X		*mode_win,		/* window for mode line */
X		*ask_win,		/* window for ask (echo) area */
X		*sep_win;		/* window for separation dashes */
X
X/* Major Mode stuff.  Each buffer has its own major mode.
X * Only one major mode may be in effect at any time.
X */
Xstruct majmode {
X	char *mjm_name;		/* Simple for now */
X};
XEXT struct majmode *fun_mode;	/* Fundamental mode - the default */
XEXT struct majmode *cur_mode;	/* Current major mode */
X
X/* Minor modes are currently implemented by means of flag variables
X * which have global effects (regardless of buffer or major mode).
X * Each variable has the name "x_mode" where x is the name of the minor
X * mode.  These are declared in the modules containing their support code.
X * In the future this may be generalized along the lines of major modes.
X */
X
X
X/* Miscellaneous debug stuff */
X
XEXT int dbgval;		/* Set nonzero to do verify stuff */
XEXT int dbg_isw;	/* Set to enable interrupts if possible */
X#if IMAGEN
XEXT int dbg_redp;	/* Set to debug redisplay algorithms */
X#endif /*IMAGEN*/
Xextern int errno;
X
X/* V7 routines for setexit/reset emulation */
X
X#if !(V6)
X#include <setjmp.h>
XEXT jmp_buf env_main;
X#define setexit(a)	setjmp(env_main)
X#define reset(a)	longjmp(env_main,a)
X#endif /*-V6*/
X
X/* Declare functions returning CHROFF values (offsets into a buffer) */
X
Xextern chroff e_dot(),e_nldot(),e_pldot(),e_boldot(),e_eoldot(),
X	e_alldot(),ex_boldot(),ex_alldot(),
X	ex_blen(),e_blen(),ex_dot(),e_wdot();
X
Xextern SBSTR *e_copyn();
X
X/* Some other commonly needed declarations */
X
Xextern char *memalloc(), *ask(), *dottoa(), *strdup();
X#if !(V6)
Xextern char *getenv();
X#endif /*-V6*/
X#include "eeproto.h"	/* function prototypes */
/
echo x - ellec.c
sed '/^X/s///' > ellec.c << '/'
X/* ELLEC - Copyright 1983 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.
X */
X/* ELLEC - ELLE Compiler
X *    Invoked with no arguments, acts as general ELLE compiler filter;
X *	reads ASCII profile input, and outputs a binary profile.
X *    Otherwise:
X *	-Profile	Compiles user's profile.
X *		HOME/.ellepro.e -> HOME/.ellepro.bN (N = fmt version #)
X *		NOTE: on V6, "HOME/" is left out.
X *	-Pconf		Outputs defprf.c for ELLE (accepts std in) (old -E)
X *	-Fconf		Outputs eefdef.h for ELLE (accepts std in)
X *	-FXconf		Outputs eefidx.h for ELLE (accepts std in)
X *	-CMconf		Outputs config makefile for ELLE ( " )
X *	-CSconf	arg	Outputs config file using "arg" - for V6 config.v6 file.
X *	-Pdump		Outputs defprf.e user profile (old -P)
X *	-Fdump		Outputs deffun.e
X */
X
X#if 0
XThe ASCII profile file associates command chars with functions.
XIt is simply a series of lisp-like expressions of the form
X	(keybind <char spec> <fun spec>)
X		
X	e.g. (keybind "C-Y" "Yank Pop")
X
XCommand char specification:
X	Allowed prefixes:
X		<ch>	The char itself
X		C-	Controlify (zap bits 140)
X		^<ch>	Ditto
X		M-	Meta (add bit 200) - case ignored
X		X-	Extended (add bit) - case ignored
X	To quote a char or char spec, use quoted-string syntax.
X
XFunction name specification:
X	Function names are specified by quoted strings containing the entire
X	long-form ASCII function name.  Matching is done case-independently.
X
X#endif /*COMMENT*/
X
X
X#include "eesite.h"		/* Site specific stuff if any */
X#include <stdio.h>
X#include <ctype.h>
X#include <unistd.h>
X#include "eeprof.h"		/* Profile structure definition */
X
X
X#define EFUNMAX 400	/* Maximum # (+1) of functions that can be defined */
X#define KBTSIZ (128*2)	/* Initial size of key binding tables */
X
X
X/* EFUN Function definition table.
X**	Functions that were copied from the pre-defined table will
X** have a value of NULL in ef_mod.
X*/
Xstruct fun {
X	int ef_idx;	/* Function index (same as index of entry) */
X	char *ef_name;	/* Function name */
X	char *ef_adr;	/* C routine name in ELLE */
X	char *ef_mod;	/* Source module that C routine lives in */
X};
Xstruct fun efuntab[EFUNMAX];
Xint efxmax = 0;		/* Largest function idx used */
X
Xint format_ver = PROF_VER;	/* Current version # of binary profile fmt */
X
X/* Keybind mapping tables.  There are four separate tables:
X**	Simple character.  Always 128 single-byte items, indexed by the simple
X**		command char.  Each item is the corresponding function index.
X**	Meta char.  Variable number of 2-byte items; first is a command char
X**		and second is its function index.
X**	Extended char.  Same format as Meta char.
X**	Menu item (SUN only).  Variable number of single-byte items, each
X**		a function index.
X**
X*/
Xchar *chrptr;		/* Pointer to simple-char table */
Xint chrsiz = 128;	/* Current size of table */
Xint chrcnt = 0;		/* # bytes actually used */
X
Xchar *mtaptr;		/* Pointer to meta-char table */
Xint mtasiz = KBTSIZ;	/* Current size (twice the # of items) */
Xint mtacnt = 0;		/* # bytes actually used */
X
Xchar *extptr;		/* Pointer to ext-char table */
Xint extsiz = KBTSIZ;	/* Current size (twice the # of items) */
Xint extcnt = 0;		/* # bytes actually used */
X
Xchar *mnuptr;		/* Pointer to menu-item table (SUN only) */
Xint mnusiz = KBTSIZ;	/* Current size */
Xint mnucnt = 0;		/* # bytes actually used */
X
X
X#define CB_EXT 0400		/* "X-" prefix on command char */
X#define CB_META 0200		/* "M-" prefix on command char */
X
X
X/* Set up the pre-defined data areas.  This includes the
X** predefined function table plus the default profile (keyboard mappings).
X** Note this only contains entries for ef_name and ef_adr.
X*/
X
Xstruct fun pdfuntab[] = {	/* Pre-Defined Function table */
X#define EFUN(rtn,rtnstr,name) 0, name, rtnstr, 0,
X#define EFUNHOLE 0, 0, 0, 0,
X#include "eefdef.h"
X};
Xint npdfuns = sizeof(pdfuntab)/sizeof(struct fun);	/* # of entries */
X
X#include "defprf.c"	/* Insert default profile mapping */
X			/* This defines charmap, metamap, and extmap. */
X
X/* Stuff for feeble-minded list processor */
X#define NIL ((struct lnode *)0)
X#define LTRUE (&ltruenode)
X
X#define LT_VAL 0
X#define LT_STR 1
X#define LT_LIST 2
X#define LT_ATOM 3	/* Should use this later instead of LT_STR */
X
Xstruct lnode {
X	struct lnode *lnxt;
X	int ltyp;
X	union {
X		int lvi;
X		char *lvs;
X		struct lnode *lvl;
X	} lval;
X};
X
Xstruct lnode ltruenode;		/* Constant TRUE */
X
X_PROTOTYPE(int main , (int argc , char **argv ));
X_PROTOTYPE(int doargs , (int argc , char **argv ));
X_PROTOTYPE(char **findkey , (char *cp , char ***aretp , char **tabp , int tabsiz , int elsize ));
X_PROTOTYPE(int nstrcmp , (char *s1 , char *s2 ));
X_PROTOTYPE(int ustrcmp , (char *s1 , char *s2 ));
X_PROTOTYPE(int strueq , (char *s1 , char *s2 ));
X_PROTOTYPE(int do_opcod , (void));
X_PROTOTYPE(int do_opasc , (void));
X_PROTOTYPE(int outkbind , (int c , int fx ));
X_PROTOTYPE(int do_obprof , (void));
X_PROTOTYPE(int mupcase , (int ch ));
X_PROTOTYPE(int upcase , (int ch ));
X_PROTOTYPE(char *qstr , (char *str ));
X_PROTOTYPE(char *charep , (int c ));
X_PROTOTYPE(int do_ocnf , (char *str ));
X_PROTOTYPE(int do_ofcod , (void));
X_PROTOTYPE(int do_ofasc , (void));
X_PROTOTYPE(int do_ofxcod , (void));
X_PROTOTYPE(int compile_stdin , (void));
X_PROTOTYPE(int lrch , (void));
X_PROTOTYPE(struct lnode *lread , (void));
X_PROTOTYPE(int wspfls , (void));
X_PROTOTYPE(struct lnode *lrstr , (int flg ));
X_PROTOTYPE(int islword , (int c ));
X_PROTOTYPE(struct lnode *eval , (struct lnode *lp ));
X_PROTOTYPE(struct lnode *undefall , (struct lnode *lp ));
X_PROTOTYPE(struct lnode *efun , (struct lnode *lp ));
X_PROTOTYPE(struct lnode *keybind , (struct lnode *lp ));
X_PROTOTYPE(struct lnode *keyallun , (void));
X_PROTOTYPE(struct lnode *menuitem , (struct lnode *lp ));
X_PROTOTYPE(int repchar , (char *str ));
X_PROTOTYPE(struct lnode *getln , (void));
X_PROTOTYPE(int numcvt , (char *str , int *anum ));
X_PROTOTYPE(int listcnt , (struct lnode *lp ));
X_PROTOTYPE(char *funname , (int i ));
X_PROTOTYPE(int findfun , (char *name ));
X_PROTOTYPE(int funcnt , (int *arr ));
X_PROTOTYPE(int scpy , (char *from , char *to , int cnt ));
X_PROTOTYPE(char *stripsp , (char *cp ));
X
Xint warn();
Xint lerr();
Xint fatal();
X
X
X/* ELLEC argument stuff */
Xchar *argfile;
Xchar *outfile;
Xint swfilter;	/* If no args */
Xint swprof;	/* -P */
Xint swelle;	/* -E */
X
Xint uproflg;	/* Do compilation of user's profile */
Xint swpcnf;	/* Output defprf.c (C initialization of profile) */
Xint swfcnf;	/* Output eefdef.h */
Xint swfxcnf;	/* Output eefidx.h */
Xint swcmcnf;	/* Output config makefile (makecf.fun) */
Xchar *swcscnf;	/* Output config specially (for V6) */
Xint swallc;	/* Do all of config stuff */
Xint swfdmp;	/* Output deffun.e */
Xint nfiles;	/* # file specs seen */
X
Xmain(argc,argv)
Xint argc;
Xchar **argv;
X{	register int i;
X	register char *cp;
X	char temp[300];
X
X	/* Initialize LTRUE
X	** (cannot portably initialize a union at compile time)
X	*/
X	ltruenode.ltyp = LT_VAL;	/* Set type (other fields zero) */
X
X	/* Process switches */
X	if(argc <= 1) swfilter++;	/* If no args, assume filter */
X	else doargs(argc,argv);
X
X	/* Initialize function definitions and key bindings from predefs */
X	chrptr = malloc(chrsiz);
X	mtaptr = malloc(mtasiz);
X	extptr = malloc(extsiz);
X	mnuptr = malloc(mnusiz);
X	if (!chrptr || !mtaptr || !extptr || !mnuptr)
X		fatal("cannot init, no memory");
X
X	scpy(charmap, chrptr, (chrcnt = sizeof(charmap)));
X	scpy(metamap, mtaptr, (mtacnt = sizeof(metamap)));
X	scpy( extmap, extptr, (extcnt = sizeof(extmap)));
X	if(def_prof.menuvec)
X		scpy(def_prof.menuvec, mnuptr, (mnucnt = def_prof.menuvcnt));
X
X	for(i = 0; i < npdfuns; ++i)		/* Initialize function defs */
X		if(pdfuntab[i].ef_name)
X		  {	efuntab[i].ef_idx = i;
X			efuntab[i].ef_name = pdfuntab[i].ef_name;
X			efuntab[i].ef_adr = stripsp(pdfuntab[i].ef_adr);
X			if(efxmax < i) efxmax = i;
X		  }
X
X
X	/* Routines expect input from stdin and output their results
X	 * to stdout.
X	 */
X	if(argfile)
X		if(freopen(argfile,"r",stdin) == NULL)
X			fatal("cannot open input file \"%s\"",argfile);
X	if(outfile)
X		if(freopen(outfile,"w",stdout) == NULL)
X			fatal("cannot open output file \"%s\"",outfile);
X
X
X	/* Check for general compilation */
X	if(swfilter)
X	  {	/* Not really implemented yet */
X		fatal("bad usage, see doc");
X	  }
X
X	/* Do profile hacking of some kind */
X	if(swprof || swelle)
X	  {	if (compile_stdin())	/* Compile input profile */
X		  {	if(swprof)
X				do_opasc();	/* Output ASCII profile (.e) */
X			else if(swelle)
X				do_opcod();	/* Output bin profile (.b1) */
X		  }
X		exit(0);
X	  }
X
X	/* Check for variousness */
X	if(swpcnf)
X	  {	if(compile_stdin())		/* Compile input */
X			do_opcod();	/* Output the C initialization code */
X		exit(0);
X	  }
X	if(swfxcnf)
X	  {	if(compile_stdin())		/* Compile input */
X			do_ofxcod();	/* Output the eefidx.h code */
X		exit(0);
X	  }
X	if(swfcnf)
X	  {	if(compile_stdin())		/* Compile input */
X			do_ofcod();	/* Output the eefdef.h code */
X		exit(0);
X	  }
X	if(swcmcnf || swcscnf)
X	  {	if(compile_stdin())		/* Compile input */
X			do_ocnf(swcscnf);	/* Output the makecf.fun code */
X		exit(0);
X	  }
X	if(swfdmp)
X	  {	if(compile_stdin())		/* Compile input */
X			do_ofasc();	/* Output the deffun.e code */
X		exit(0);
X	  }
X
X
X	/* Hack user's profile */
X	if(!uproflg) exit(0);
X	if(!argfile)
X	  {
X		temp[0] = 0;
X#if !V6
X		if (cp = getenv("HOME"))
X			strcat(temp, cp);
X#if !TOPS20
X		strcat(temp,"/");
X#endif /*-TOPS20*/
X#endif /*-V6*/
X		strcat(temp, EVPROFTEXTFILE);
X		if(freopen(temp,"r",stdin) == NULL)
X			fatal("cannot open profile \"%s\"",temp);
X	  }
X	if(!outfile)
X	  {
X		temp[0] = 0;
X#if !V6
X		if (cp = getenv("HOME"))
X			strcat(temp, cp);
X#if !TOPS20
X		strcat(temp,"/");
X#endif /*-TOPS20*/
X#endif /*-V6*/
X		strcat(temp, EVPROFBINFILE);
X		if(freopen(temp,"wb",stdout) == NULL	/* Try binary 1st */
X		  && freopen(temp,"w",stdout) == NULL)
X			fatal("cannot open output profile \"%s\"",temp);
X
X	  }
X	/* Hack user's profile */
X	if(compile_stdin())		/* Compile input profile */
X		do_obprof();	/* Output the binary */
X
X}
X
X#define SW_FLG 0
X#define SW_VAR 1
X#define SW_STR 2
Xstruct swarg {
X	char *sw_name;
X	long sw_type;
X	int *sw_avar;
X	char **sw_astr;
X} swtab[] = {
X	"P",		SW_FLG, &swprof,	0,	/* Old stuff */
X	"E",		SW_FLG,	&swelle,	0,
X	"Profile",	SW_FLG, &uproflg,	0,
X	"Pconf",	SW_FLG, &swpcnf,	0,
X	"Fconf",	SW_FLG, &swfcnf,	0,
X	"FXconf",	SW_FLG, &swfxcnf,	0,
X	"CMconf",	SW_FLG, &swcmcnf,	0,
X	"CSconf",	SW_STR, 0,		&swcscnf,
X	"Allconf",	SW_FLG, &swallc,	0,
X	"Pdump",	SW_FLG,	&swprof,	0,
X	"Fdump",	SW_FLG, &swfdmp,	0
X};
X
Xdoargs(argc,argv)
Xint argc;
Xchar **argv;
X{	register int cnt, c;
X	register char **av;
X	register int i;
X	register struct swarg *swp;
X	struct swarg *swp2;
X	int swerrs = 0;
X
X	av = argv;
X	cnt = argc;
X	nfiles = 0;
X
X	while(--cnt > 0)
X	  {	++av;
X		if(*av[0] != '-')	/* If not switch, */
X		  {			/* assume it's an input filename */
X			nfiles++;
X			continue;
X		  }
X		av[0]++;
X
X		/* Try to look up switch in table */
X		swp = (struct swarg *)findkey(av[0], &swp2, swtab,
X			(sizeof(swtab))/(sizeof(struct swarg)),
X			(sizeof(struct swarg))/(sizeof(char *)));
X		if(swp2)
X		  {	fprintf(stderr,"ellec: ambiguous switch: -%s = %s or %s\n",
X				av[0], swp->sw_name, swp2->sw_name);
X			goto swdone;
X		  }
X		if(swp)	switch(swp->sw_type)
X		  {	case SW_FLG:
X				*(swp->sw_avar) = 1;
X				goto swdone;
X			case SW_VAR:
X				*(swp->sw_avar) = 1;
X				if(cnt <= 1) goto swdone;
X				if(isdigit(*av[1]))
X				  {	*(swp->sw_avar) = atoi(av[1]);
X					--cnt;
X					goto swargdone;
X				  }
X				goto swdone;
X				
X			case SW_STR:
X				if(cnt <= 1) goto swdone;
X				*(swp->sw_astr) = av[1];
X				goto swargdone;
X
X			default:
X				fprintf(stderr,"ellec: bad switch type: %s\n",
X					av[0]);
X				swerrs++;
X		  }
X
X	stop:	fprintf(stderr,"ellec: bad switch: %s\n",*av);
X		swerrs++;
X		goto swdone;
X	swargdone:
X		av[0] = 0;
X		av++;
X	swdone:	av[0] = 0;
X	  }
X	if(swerrs) exit(1);		/* Stop if any problems */
X}
X
Xchar **
Xfindkey(cp, aretp, tabp, tabsiz, elsize)
Xregister char *cp;
Xchar ***aretp;
Xregister char **tabp;
Xint tabsiz, elsize;
X{	register char **mp1, **mp2;
X	register int i, res;
X	
X	*aretp = mp1 = mp2 = 0;
X	for(i = 0; i < tabsiz; ++i, tabp += elsize)
X	  { if(res = ustrcmp(cp,*tabp))
X		  {	if(res > 0) return(tabp);
X			if(!mp1) mp1 = tabp;
X			else mp2 = tabp;
X		  }
X	  }
X	if(mp2)
X		*aretp = mp2;		/* Ambiguous */
X	return(mp1);
X}
X
X/* NSTRCMP - New string compare.
X *	Returns:
X *		2 if str2 > str1 (mismatch)
X *		1 if str2 counted out (str1 > str2)
X *		0 if full match
X *		-1 if str1 counted out (str1 < str2)
X *		-2 if str1 < str2 (mismatch)
X */
X
Xnstrcmp(s1,s2)
Xregister char *s1, *s2;
X{	register int c, d;
X
X	while(c = *s1++)
X	  {	if(c != *s2)
X		  {	if((d = upcase(c) - upcase(*s2)) != 0)
X				return(*s2==0 ? 1 : (d > 0 ? 2 : -2));
X		  }
X		++s2;
X	  }
X	return(*s2 ? -1 : 0);
X}
X
X/* USTRCMP - uppercase string compare.
X *	Returns 0 if mismatch,
X *		1 if full match,
X *		-1 if str1 runs out first (partial match)
X */
Xustrcmp(s1,s2)
Xregister char *s1, *s2;
X{	register int c;
X
X	if ( ! s1 || ! s2 ) return ( 0 );	/* Check for null ptr */
X	while(c = *s1++)
X	  { if(c != *s2)
X		  {	if(((c ^ *s2) != 040)
X			 || (upcase(c) != upcase(*s2)))
X				return(0);
X		  }
X		s2++;
X	  }
X	return(c == *s2 ? 1 : -1);
X}
X
Xstrueq(s1,s2)
Xchar *s1;
Xchar *s2;
X{	return (ustrcmp(s1, s2) > 0 ? 1 : 0);
X}
X
X/* Output C initialization code for default profile (defprf.c) */
X
Xdo_opcod()
X{	register int i, c, f;
X
X	printf("\
X/* This file defines the initial data for ELLE's default user profile.\n\
X** It is automatically generated by ELLEC, and should not be edited.\n\
X*/\n\
Xchar charmap[] = {\n");
X	for(i=0; i < chrcnt; i++)
X	  {	printf("\t%2d,",(f = chrptr[i]&0377));
X		printf("\t/* (%3o) %3s",i,charep(i));
X		printf("  %s",funname(f));
X		printf(" */\n");
X	  }
X
X	printf("};\n char metamap[] = {\n");
X	for(i = 0; i < mtacnt; i += 2)
X	  {	printf("\t0%-3o,%3d,",(c = mtaptr[i]&0377),(f = mtaptr[i+1]&0377));
X		printf("\t/* %4s",charep(c|CB_META));
X		printf("  %s",funname(f));
X		printf(" */\n");
X	  }
X
X	printf("};\n char extmap[] = {\n");
X	for(i = 0; i < extcnt; i += 2)
X	  {	printf("\t0%-3o,%3d,",(c = extptr[i]&0377),(f = extptr[i+1]&0377));
X		printf("\t/* %4s",charep(c|CB_EXT));
X		printf("  %s",funname(f));
X		printf(" */\n");
X	  }
X	printf("};\n");
X	printf("struct profile def_prof = {\n");
X	printf("  %d, /* Ver */\n", format_ver);
X	printf("  sizeof(charmap),   charmap,\n");
X	printf("  sizeof(metamap)/2, metamap,\n");
X	printf("  sizeof(extmap)/2,  extmap, \n");
X	printf("  0, 0\n");
X	printf("};\n");
X
X}
X
X/* Output ASCII version of default profile */
X
Xint oerrs;
X
Xdo_opasc()
X{	register int i, c, f;
X
X	oerrs = 0;
X	printf("; ELLE default ASCII profile\n\n");
X	printf("(keyallunbind)  ; To flush all existing bindings\n\n");
X	for(i=0; i < chrcnt; i++)
X		outkbind(i, chrptr[i]&0377);
X
X	printf("\n; Meta chars\n\n");
X	for(i = 0; i < mtacnt; i += 2)
X		outkbind(CB_META | (mtaptr[i]&0377), mtaptr[i+1]&0377);
X
X	printf("\n ; Extended commands\n\n");
X	for(i = 0; i < extcnt; i += 2)
X		outkbind(CB_EXT | (extptr[i]&0377), extptr[i+1]&0377);
X
X	printf("\n");
X	if(oerrs)
X		fatal("%d errors encountered, check output file.", oerrs);
X}
X
Xoutkbind(c, fx)
X{
X	if(fx == 0)		/* Allow key to be mapped to nothing. */
X		return;
X	if(fx <= 0 || fx > efxmax)
X		printf(";INTERNAL ERROR: Bad function index %d for key %s\n",
X			fx, charep(c));
X	else if(efuntab[fx].ef_name == NULL)
X		printf(";INTERNAL ERROR: No name for function %d while mapping key %s\n",
X			fx, charep(c));
X	else {
X	  	printf("(keybind %s \"%s\")\n",
X			qstr(charep(c)),efuntab[fx].ef_name);
X		return;
X	  }
X	oerrs++;
X}
X
X/* Output binary user profile */
X
Xdo_obprof()
X{	register unsigned int rp;	/* Relative "pointer" */
X
X	rp = sizeof(def_prof);		/* Initialize */
X
X	def_prof.chrvec = (char *)rp;
X	def_prof.chrvcnt = chrcnt;
X	rp += chrcnt;
X
X	def_prof.metavec = (char *)rp;
X	def_prof.metavcnt = mtacnt/2;
X	rp += mtacnt;
X
X	def_prof.extvec = (char *)rp;
X	def_prof.extvcnt = extcnt/2;
X	rp += extcnt;
X
X	def_prof.menuvec = (char *)rp;
X	def_prof.menuvcnt = mnucnt;
X	rp += mnucnt;
X
X	fwrite((char *)&def_prof,sizeof(def_prof),1,stdout);
X	fwrite(chrptr,sizeof(char),chrcnt,stdout);
X	if(mtacnt)  fwrite(mtaptr, sizeof(*mtaptr), mtacnt, stdout);
X	if(extcnt)  fwrite(extptr, sizeof(*extptr), extcnt, stdout);
X	if(mnucnt) fwrite(mnuptr,sizeof(*mnuptr),mnucnt,stdout);
X}
X
X/* Return upper-case version of character */
Xmupcase(ch)
Xregister int ch;
X{	return((ch&(~0177)) | upcase(ch));
X}
Xupcase (ch)
X{	register int c;
X	c = ch&0177;
X	return((('a' <= c) && (c <= 'z')) ? (c - ('a'-'A')) : c);
X}
X
Xchar *
Xqstr(str)
Xregister char *str;
X{	register int c;
X	static char qstrbuf[100];
X	register char *cp;
X	cp = str;
X	while((c = *cp++) && islword(c));
X	if(c == 0) return(str);	/* No quoting needed */
X
X	cp = qstrbuf;
X	*cp++ = '"';
X	while(*cp++ = c = *str++)
X		if(c == '"') *cp++ = c;	/* Double the quotes */
X	cp[-1] = '"';
X	*cp = 0;
X	return(qstrbuf);
X}
X
Xchar *
Xcharep(c)
Xregister int c;
X{	static char chrbuf[10];
X	register char *cp;
X	cp = chrbuf;
X	if(c&CB_EXT)
X	  {	*cp++ = 'X';
X		*cp++ = '-';
X		c &= ~CB_EXT;
X	  }
X	if(c&CB_META)
X	  {	*cp++ = 'M';
X		*cp++ = '-';
X		c &= ~CB_META;
X	  }
X	if(c <= 037)
X	  {	*cp++ = '^';
X		c += 0100;
X	  }
X	if(c == 0177)
X	  {	*cp++ = 'D';
X		*cp++ = 'E';
X		*cp++ = 'L';
X	  }
X	else *cp++ = c;
X	*cp = 0;
X	return(chrbuf);
X}
X
X/* Output config Makefile (makecf.fun)
X *	If argument is 0 (NULL), does Makefile type output.
X *	Otherwise uses string for special-purpose output.
X */
Xdo_ocnf(str)
Xchar *str;
X{	register struct fun *fnp;
X	register int i, mi;
X	register char *cp;
X	int fmtab[EFUNMAX];
X	int nfmods;
X	char *modtab[EFUNMAX];
X	char *unknown = "unknown-module";
X
X	if(str == NULL)			/* If not V6 version */
X	  {	printf("# Function module definition file, generated by ELLEC\n");
X		printf("FUN_OFILES = ");
X	  }
X
X	nfmods = 0;
X
X	funcnt(fmtab);		/* Count function occs */
X
X	for(i = 1; i <= efxmax; ++i)
X	  {	if(fmtab[i] == 0) continue;
X		fnp = &efuntab[i];
X
X		if(fnp->ef_name == 0)
X			fatal("internal error - no name for function %d", i);
X		
X		/* Got a function, store its module name if not a dup */
X		if ((cp = fnp->ef_mod) == NULL)	/* Substitute if undef */
X			cp = unknown;	
X		for(mi=0; mi < nfmods; ++mi)
X			if(ustrcmp(cp, modtab[mi]) > 0)
X				break;
X		if(mi < nfmods) continue;
X		modtab[nfmods++] = cp;
X	  }
X
X	/* Now have table of all modules used.  Crunch them into output. */
X	for(mi=0; mi < nfmods; ++mi)
X	    if (modtab[mi] != unknown)
X	      {	if(str != NULL)		/* V6 version? */
X			printf("%s %s\n", str, modtab[mi]);
X		else printf("\\\n\t%s.o", modtab[mi]);
X	      }
X	printf("\n");
X}
X
X/* Output eefdef.h */
X
Xdo_ofcod()
X{	register struct fun *fnp;
X	register int i;
X	char temp[40];
X	int fmtab[EFUNMAX];
X
X	printf("/* .H Function Definition file, generated by ELLEC */\n");
X	printf("/*   0 */ EFUNHOLE /* Always undefined */\n");
X
X	funcnt(fmtab);		/* Count function occs */
X
X	for(i = 1; i <= efxmax ; ++i)
X	  {
X		fnp = &efuntab[i];
X		printf("/* %3d */ ", i);
X		if(fmtab[i] == 0 || fnp->ef_name == 0)
X			printf("EFUNHOLE\n");
X		else
X		  {	sprintf(temp, "\"%s\"", fnp->ef_adr);
X			printf("EFUN( %-12s, %-14s, \"%s\")\n", fnp->ef_adr,
X				temp, fnp->ef_name);
X		  }
X	  }
X
X}
X
X/* Output ascii version of function def file
X*/
Xdo_ofasc()
X{	register struct fun *fnp;
X	register int i;
X	register char *fa, *fm;
X
X	printf("; Master Function Definition file\n");
X
X	for(i = 1; i <= efxmax ; ++i)
X	  {
X		fnp = &efuntab[i];
X		if(fnp->ef_idx == 0)	/* No definition for this index? */
X			continue;
X		if(fnp->ef_name == 0)
X		  {	warn("internal error - no name for function %d", i);
X			continue;
X		  }
X
X		if ((fa = fnp->ef_adr) == NULL)
X			fa = "unknown-addr";
X		if ((fm = fnp->ef_mod) == NULL)
X			fm = "unknown-module";
X		printf("(efun %d \"%s\" %s %s)\n",
X			fnp->ef_idx, fnp->ef_name, fa, fm);
X	  }
X}
X
X/* Output eefidx.h */
X
Xdo_ofxcod()
X{	register struct fun *fnp;
X	register int i;
X	register char *cp, *cp2;
X	int fmtab[EFUNMAX];
X	char tmpstr[100];
X
X	printf("\
X/* .H Function Index Definition file, generated by ELLEC */\n");
X	printf("\
X/* FN_ defines Function Numbers (indices) for all known functions */\n");
X	printf("\
X/* FX_ defines Function eXistence in this ELLE configuration */\n");
X
X	funcnt(fmtab);		/* Count function occs */
X
X	for(i = 1; i <= efxmax ; ++i)
X	  {
X		fnp = &efuntab[i];
X		if(fnp->ef_idx == 0)	/* No definition for this index? */
X			continue;
X		if(fnp->ef_adr == 0 || fnp->ef_name == 0)
X		  {	warn("internal error - no addr/name for function %d", i);
X			continue;
X		  }
X
X		cp2 = fnp->ef_adr;
X		cp = tmpstr;
X		while(*cp++ = upcase(*cp2++));
X		cp = tmpstr;
X		if((*cp++ != 'F') || (*cp++ != '_'))
X			cp = tmpstr;
X
X		/* Always define FN_ as index */
X		printf("#define FN_%-14s %3d /* %s */\n",
X				cp, i, fnp->ef_name);
X		/* Define FX_ as 0 if unused, else same as FN_ */
X		printf("#define FX_%-14s %3d\n", cp,
X			(fmtab[i] == 0) ? 0 : i);	/* 0 if unused */
X	  }
X
X}
X
X/* Compile input! */
X
Xcompile_stdin()
X{	register struct lnode *lp;
X
X	while((lp = lread()) != NIL)
X		eval(lp);
X
X	return(1);
X}
X
X#define MAXLINE 300
Xint llstch = -1;
Xint leofflg;
X#define unlrch(c) llstch = c
X
Xint lineno = 0;
Xchar linebuf[MAXLINE];
Xchar *linecp = linebuf;
X
Xlrch()
X{	register int c;
X	if((c = llstch) >= 0)
X	  {	if(c == 0 && leofflg)
X			return(EOF);
X		llstch = -1;
X		return(c);
X	  }
X	if((c = getc(stdin)) == EOF)
X	  {	leofflg = 1;
X		llstch = 0;
X		*linecp = 0;
X		linecp = linebuf;
X		return(c);
X	  }
X	if(c == '\n')
X	  {	lineno++;
X		linecp = linebuf;
X	  }
X	else *linecp++ = c;
X	return(c);
X}
X
Xstruct lnode *
Xlread()
X{	register int c;
X	register struct lnode *lp, *lp2;
X	struct lnode *head;
X
X	wspfls();
X	if((c = lrch())== EOF)
X		return(NIL);
X	if(c == ')')	/* End of a list? */
X		return(NIL);
X	if(c == '(')	/* Start of a list? */
X	  {	head = lp = getln();
X		lp->ltyp = LT_LIST;
X		if((head->lval.lvl = lp = lread()) == NIL)
X			return(head);	/* Return empty list */
X		while(lp2 = lread())
X		  {	lp->lnxt = lp2;
X			lp = lp2;
X		  }
X		return(head);
X	  }
X
X	/* Atom of some kind */
X	if(c=='"')
X	  {	return(lrstr(1));
X	  }
X	unlrch(c);
X	return(lrstr(0));
X}
X
Xwspfls()
X{	register int c;
X	for(;;)
X	  {	c = lrch();
X		if(isspace(c)) continue;
X		if(c == ';')
X			while((c = lrch()) != '\n')
X				if(c == EOF) return;		
X		break;
X	  }		
X	if(c != EOF) unlrch(c);
X}
X
X#define LSMAX 300	/* Max # chars in atom string */
Xstruct lnode *
Xlrstr(flg)
X{	char cbuf[LSMAX];
X	register char *cp;
X	register int c, i;
X	struct lnode *lp;
X
X	cp = cbuf;
X	i = 0;
X
X	while((c = lrch()) != EOF)
X	  if (flg) switch(c)
X	  {	case '"':
X			if((c = lrch()) == EOF)
X				return(NIL);
X			if(c != '"')
X			  {	unlrch(c);
X				goto out;
X			  }
X		default:
X		ok:
X			if(++i > LSMAX)
X				break;
X			*cp++ = c;
X			continue;
X	  }
X	else
X	  {	if(islword(c)) goto ok;
X		unlrch(c);
X		break;
X	  }
X  out:
X	lp = getln();
X	lp->ltyp = LT_STR;
X	lp->lval.lvs = malloc(i+1);
X	*cp = 0;
X	strcpy(lp->lval.lvs, cbuf);
X	return(lp);
X}
Xislword(c)
X{	return((040 < c && c < 0177
X		&& c != '(' && c !=')' && c != ';'
X		&& c != '"' && c != '\\') ? 1 : 0);
X}
X
X
Xstruct lnode *keybind(), *keyallun(), *menuitem(), *efun(),
X	*undefall();
X
Xstruct lfun {
X	char *lfname;			/* Name of list function */
X	struct lnode * (*lfrtn)();	/* Function address */
X} lfntab[] = {
X	"keybind",	keybind,
X	"efun",		efun,
X	"menuitem",	menuitem,
X	"keyallunbind",	keyallun,
X/*	"keyunbind",	keyunbind,	*/	/* Not yet */
X	"undefall",	undefall,
X/*	"undef",	undef,		*/	/* Not yet */
X	0, 0
X};
X
Xstruct lnode *
Xeval(lp)
Xregister struct lnode *lp;
X{	register struct lnode *flp;
X	register struct lfun *lfent;
X
X	if(lp->ltyp != LT_LIST)
X		return(lp);
X	if((flp = lp->lval.lvl) == NIL)
X		return(NIL);
X	if(flp->ltyp != LT_STR)
X		return(NIL);
X
X	/* Look up list function and invoke it */
X	for(lfent = lfntab; lfent->lfname; lfent++)
X		if(strueq(flp->lval.lvs, lfent->lfname))
X			return((*(lfent->lfrtn))(flp->lnxt));
X
X	lerr("unknown op: (%s)", flp->lval.lvs);
X	return(NIL);
X}
X
X
X/* UNDEFALL - (undefall)
X**	Undefines all functions.  Typically used to clear out
X** predefined functions prior to compiling a set of new efuns.
X*/
Xstruct lnode *
Xundefall(lp)
Xregister struct lnode *lp;
X{
X	register int i;
X	efxmax = 0;		/* Say nothing in function def table! */
X	for(i = 0; i < EFUNMAX; ++i)
X	  {	efuntab[i].ef_idx = 0;
X		efuntab[i].ef_name = 0;
X		efuntab[i].ef_adr = 0;
X		efuntab[i].ef_mod = 0;
X	  }
X	return(LTRUE);
X}
X
X/* EFUN - (efun <index> <functionname> <address> <module>)
X**	Checks out the args and if no problems, stores the function
X** definition in efuntab.
X*/
Xstruct lnode *
Xefun(lp)
Xregister struct lnode *lp;
X{	struct lnode *nlp;
X	register int c, i;
X	register struct fun *fnp;
X	char *fname, *faddr, *fmod;
X	int fni, num;
X
X	if(listcnt(lp) < 4)
X	  {	lerr("efun - not enough args");
X		return(NIL);
X	  }
X
X	/* First thing should be function index */
X	switch(lp->ltyp)
X	  {	case LT_VAL:
X			fni = lp->lval.lvi;
X			break;
X		case LT_STR:
X			if(numcvt(lp->lval.lvs, &num))
X			  {	fni = num;
X				break;
X			  }
X		default:
X			lerr("efun - non-value function index");
X			return(NIL);
X	  }
X
X	/* Next thing should be function name */
X	lp = lp->lnxt;
X	if(lp->ltyp != LT_STR)	/* Function name not a string */
X	  {	lerr("efun - non-string function name");
X		return(NIL);
X	  }
X	fname = lp->lval.lvs;
X
X	/* Next thing should be function addr */
X	lp = lp->lnxt;
X	if(lp->ltyp != LT_STR)	/* Function addr not a string */
X	  {	lerr("efun - non-string function addr");
X		return(NIL);
X	  }
X	faddr = lp->lval.lvs;
X
X	/* Next thing should be function module */
X	lp = lp->lnxt;
X	if(lp->ltyp != LT_STR)	/* Function module not a string */
X	  {	lerr("efun - non-string function module");
X		return(NIL);
X	  }
X	fmod = lp->lval.lvs;
X
X	/* Now see if already exists or anything */
X	if(fni <= 0 || fni > EFUNMAX)
X	  {	lerr("efun - bad function index %d", fni);
X		return(NIL);
X	  }
X	fnp = &efuntab[fni];
X	if(fnp->ef_idx != 0)
X	  {
X		if (fnp->ef_idx == fni
X		 && strueq(fnp->ef_name, fname)
X		 && strueq(fnp->ef_adr, faddr)
X		 && (fnp->ef_mod == NULL || strueq(fnp->ef_mod, fmod)))
X			goto win;		/* Benign redefinition */
X
Xlerr("efun - redefining function (%d \"%s\" %s %s)",
X		fnp->ef_idx, fnp->ef_name, fnp->ef_adr,
X		(fnp->ef_mod ? fnp->ef_mod : "unknown-module"));
X	  }
X	for(i = 0; i < EFUNMAX; ++i)
X	  {	if(efuntab[i].ef_idx == 0) continue;
X		if(ustrcmp(efuntab[i].ef_adr,faddr) > 0
X		  || ustrcmp(efuntab[i].ef_name, fname) > 0)
X		  {	if(i == fni) continue;
X			lerr("efun - name or address dup!  \"%s\"", fname);
X			return(NIL);
X		  }
X	  }
X
X	/* No problems, store the function def in efuntab! */
Xwin:	fnp->ef_idx = fni;
X	fnp->ef_mod = fmod;
X	fnp->ef_adr = faddr;
X	fnp->ef_name = fname;
X
X	if(efxmax < fni) efxmax = fni;
X	return(LTRUE);
X}
X
X
X/* KEYBIND - (keybind <charspec> <functionname>) */
X
Xstruct lnode *
Xkeybind(lp)
Xregister struct lnode *lp;
X{	struct lnode *nlp;
X	register int c, i;
X	int fni;
X
X	if(lp == NIL || (nlp = lp->lnxt)== NIL)
X		return(NIL);
X	switch(lp->ltyp)
X	  {	case LT_VAL:
X			c = lp->lval.lvi;
X			break;
X		case LT_LIST:
X			return(NIL);
X		case LT_STR:
X			c = repchar(lp->lval.lvs);
X			break;
X	  }
X	if(c == -1)
X		return(NIL);	/* No such command char name */
X
X	lp = nlp;
X	if(lp->ltyp != LT_STR)	/* Function name not a string */
X	  {	lerr("(keybind) non-string function name");
X		return(NIL);
X	  }
X	fni = findfun(lp->lval.lvs);
X	if(fni == 0)		/* No such function name */
X	  {	lerr("(keybind) no such function - \"%s\"", lp->lval.lvs);
X		return(NIL);
X	  }
X	if(c & CB_EXT)
X	  {	c &= ~CB_EXT;
X
X		/* Check for redefinition */
X		for(i = 0; i < extcnt; i += 2)
X			if(c == (extptr[i]&0377))	/* Already there? */
X			  {	if((extptr[i+1]&0377) != fni)	/* Yes, check fn */
X				    lerr("(keybind) redefining X-%s as %d=\"%s\"",
X					charep(c), fni, lp->lval.lvs);
X				break;
X			  }
X		if(i >= extcnt)		/* Didn't find? */
X		  {	if(extcnt >= extsiz)
X			  {	lerr("(keybind) too many X- commands");
X				return(NIL);	/* Too many EXT cmds */
X			  }
X			i = extcnt;	/* Increase size of table */
X			extcnt += 2;
X		  }
X		/* Now store new binding */
X		extptr[i] = c;
X		extptr[i+1] = fni;
X	  }
X	else if(c&CB_META)
X	  {	c &= ~CB_META;
X
X		/* Check for redefinition */
X		for(i = 0; i < mtacnt; i += 2)
X			if(c == (mtaptr[i]&0377))	/* Already there? */
X			  {	if((mtaptr[i+1]&0377) != fni)	/* Yes, check fn */
X				    lerr("(keybind) redefining M-%s as %d=\"%s\"",
X					charep(c), fni, lp->lval.lvs);
X				break;
X			  }
X		if(i >= mtacnt)		/* Didn't find? */
X		  {	if(mtacnt >= mtasiz)
X			  {	lerr("(keybind) too many M- commands");
X				return(NIL);	/* Too many META cmds */
X			  }
X			i = mtacnt;	/* Increase size of table */
X			mtacnt += 2;
X		  }
X		/* Now store new binding */
X		mtaptr[i] = c;
X		mtaptr[i+1] = fni;
X	  }
X	else {
X		i = c & 0177;
X		if (chrptr[i] && (chrptr[i]&0377) != fni)
X		    lerr("(keybind) redefining %s as %d=\"%s\"",
X			charep(c), fni, lp->lval.lvs);
X		chrptr[i] = fni;
X	  }
X	return(LTRUE);
X}
X
X/* KEYALLUNBIND - (keyallunbind) */
Xstruct lnode *
Xkeyallun()
X{	register int i;
X	register char *cp;
X
X/*	fprintf(stderr, "ellec: clearing all key definitions\n"); */
X	for(i = 0, cp = chrptr; i < chrcnt; i++)
X		*cp++ = 0;
X	mtacnt = extcnt = mnucnt = 0;
X	return(LTRUE);
X}
X
X/* MENUITEM - (menuitem <functionname>) */
X
Xstruct lnode *
Xmenuitem(lp)
Xregister struct lnode *lp;
X{	register int i, fni;
X
X	if(lp == NIL)
X		return(NIL);
X	switch(lp->ltyp)
X	  {	case LT_VAL:
X			fni = lp->lval.lvi;
X			break;
X		case LT_LIST:
X			return(NIL);
X		case LT_STR:
X			fni = findfun(lp->lval.lvs);
X			break;
X	  }
X	if(fni == 0) return(NIL);	/* Bad val or no such function name */
X	for(i = 0; i < mnusiz; i++)
X		if(fni == (mnuptr[i]&0377) || mnuptr[i] == 0)
X		  {	mnuptr[i] = fni;
X			mnucnt++;
X			return(LTRUE);
X		  }
X	return(NIL);		/* Too many menu items */
X}
X
Xrepchar(str)
Xregister char *str;
X{	register int c;
X	register int i, l;
X
X	if (str == 0) return (-1);
X	i = 0;
X	l = strlen(str);
X	c = (*str++)&0377;
X	if(l == 0) return(-1);
X	if(l == 1) return(c);	/* One-char representation */
X	if(c == '^')
X		if(l == 2) return((~0140) & mupcase(*str));
X		else return(-1);
X	c = mupcase(c);
X	if (*str == '-')
X	  {	if(*++str == 0) return(-1);
X		switch(c)
X		  {	case 'X': return(CB_EXT | mupcase(repchar(str)));
X			case 'M': return(CB_META | mupcase(repchar(str)));
X			case 'C': return((~0140) & repchar(str));
X		  }
X	  }
X	if(c == 'S' && upcase(*str) == 'P' && l == 2)
X		return(' ');
X	if(c == 'D' && upcase(*str++) == 'E' && upcase(*str++) == 'L'
X		&& *str == 0)
X		return(0177);
X	return(-1);		
X}
X
Xstruct lnode *
Xgetln()
X{	return((struct lnode *)calloc(1,sizeof(struct lnode)));
X}
X
Xnumcvt(str, anum)
Xchar *str;
Xint *anum;
X{	register char *cp;
X	register int i, c, sign;
X	if((cp = str) == 0)
X		return 0;
X	i = sign = 0;
X	if(*cp == '-')
X		cp++, sign++;
X	while(c = *cp++)
X		if(!isdigit(c)) return(0);
X		else i = 10*i + (c - '0');
X	*anum = sign ? -i : i;
X	return(1);
X}
X
X
X
Xlistcnt(lp)
Xregister struct lnode *lp;
X{	register int i;
X	i = 0;
X	while(lp)
X		++i, lp = lp->lnxt;
X	return(i);
X}
X
X/* FUNNAME - Given function index, return function name.
X**	Always wins; returns "unknown" for bad indices.
X*/
Xchar *
Xfunname(i)
Xregister int i;
X{
X	register char *cp = NULL;
X	if(0 < i && i <= efxmax && (cp = efuntab[i].ef_name))
X		return cp;
X	return("unknown function");
X}
X
Xfindfun(name)
Xregister char *name;
X{	register int i;
X	if((i = efxmax) > 0)
X	  {	do { if(strueq(name, efuntab[i].ef_name))
X			return(i);
X		  } while(--i);
X		return(0);
X	  }
X	return(0);
X}
X
X
X/* FUNCNT - Scan all key bindings, counting each occurrence of every
X**	function index.
X**	This is used to determine which functions are actually used.
X*/
Xfuncnt(arr)
Xregister int *arr;		/* Pointer to array of EFUNMAX ints */
X{
X	register int i;
X
X	for(i = 0; i < EFUNMAX; ++i)	/* Clear the array */
X		arr[i] = 0;
X	
X	for(i = 0; i < chrcnt; ++i)	/* Scan bindings */
X		arr[chrptr[i]&0377]++;
X	for(i = 0; i < mtacnt; i += 2)
X		arr[mtaptr[i+1]&0377]++;
X	for(i = 0; i < extcnt; i += 2)
X		arr[extptr[i+1]&0377]++;
X}
X
Xscpy(from,to,cnt)
Xregister char *from,*to;
Xregister int cnt;
X{	if(cnt > 0)
X		do { *to++ = *from++; }
X		while(--cnt);
X}
X
X/* STRIPSP - strip spaces from string.  Returns ptr to start. */
Xchar *
Xstripsp(cp)
Xregister char *cp;
X{
X	register char *ep, *lastp;
X	while(*cp == ' ') ++cp;
X	if (*cp)
X	  {	ep = cp + strlen(cp);	/* Point to null ending the str */
X		while (*--ep == ' ');
X		*++ep = 0;		/* Tie it off */
X	  }
X	return cp;
X}
X
Xwarn(str,a,b,c,d,e,f,g,h,i)
Xchar *str;
X{
X	fprintf(stderr, "ellec: ");
X	fprintf(stderr, str, a,b,c,d,e,f,g,h,i);
X	fprintf(stderr, "\n");
X}
X
Xlerr(str,a,b,c,d,e,f,g,h,i)
Xchar *str;
X{
X	warn(str, a,b,c,d,e,f,g,h,i);
X	*linecp = 0;			/* Tie off current line buffer */
X	fprintf(stderr, "    Line %d: %s\n", lineno, linebuf);
X}
X
Xfatal(str,a,b,c,d,e,f,g,h,i)
Xchar *str;
X{
X	warn(str, a,b,c,d,e,f,g,h,i);
X	exit(1);
X}
X
/
echo x - sb.h
sed '/^X/s///' > sb.h << '/'
X/* SB - Copyright 1982 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.  In all cases
X *	the source code and any modifications thereto must remain
X *	available to any user.
X *
X *	This is part of the SB library package.
X *	Any software using the SB library must likewise be made
X *	quasi-public, with freely available sources.
X */
X
X#ifdef COMMENT
X
XThe initials "SB" stand for "String Block" or "String Buffer".
X
XSBBUFFER - A SB buffer containing a sbstring opened for editing.
XSBFILE   - A structure holding file-specific information for all
X		SDBLKs pointing to that file.
XSBSTRING - A SB string; conceptually a single string, but actually
X		a linked list of SDBLKs.  Unless opened by a SBBUFFER,
X		only a few operations are allowed on SBSTRINGs (creating,
X		copying, deleting).
XSDBLK    - One of the linked nodes constituting a sbstring.  Each SDBLK
X		node points to a continuous string either in memory or
X		on disk, or both.
XSBLK	 - Another name for SDBLK.
XSMBLK	 - An allocated chunk of memory.  Also refers to the node structure
X		maintained by the SBM memory management routines, which
X		points to the actual chunk of memory.
XSBM	 - Name of the memory management package.  SBM routines are used
X		to allocate memory in general, and are not just for
X		use by SB routines.
X
X************ MACHINE DEPENDENT DEFINITIONS **********
X
X	The following compile time definitions represent machine
Xdependent parameters which are intended mainly for use only by SBM and
XSBSTR routines.  Other programs should use them with caution.  Note
Xthat a great deal of code assumes that type "int" corresponds to a basic
Xmachine word (as per C Reference Manual).
X
X	The current definitions will only work for machines which have
X1, 2, 4, or 8 "char" bytes in a machine word.  Any other size will
Xrequire some changes to the definitions and possibly to some places
Xusing them.
X
XWORD   - integer-type definition corresponding to machine word.
XWDSIZE - # addressable char bytes in a machine word.		(1, 2, 4, 8)
XWDBITS - # low order bits in an address, ie log2(WDSIZE).	(0, 1, 2, 3)
XWDMASK - Mask for low order bits of address			(0, 1, 3, 7)
XCHAR_MASK - If defined, machine does sign-extension on chars, and
X	they must be masked with this value.
X
X	Note that the macro for WDBITS has no mathematical significance
Xother than being an expression which happens to evaluate into the right
Xconstant for the 4 allowed values of WDSIZE, and in fact it is this
Xcrock which restricts WDSIZE!  If C had a base 2 logarithm expression
Xthen any power of 2 could be used.
X
XValues for machines
X				WORD	WDSIZE	WDBITS	WDMASK
X	PDP11, Z8000, I8086	int	2	1	01
X	VAX11, M68000, PDP10	int	4	2	03
X
X#endif /* COMMENT */
X
X/* First try to define a few things in a semi-portable way
X*/
X#include "eesite.h"
X#ifdef __STDC__		/* Implementation supports ANSI stuff? */
X#include <limits.h>		/* Get sizes for char stuff */
X#define _SBMUCHAR 1		/* Can use "unsigned char" */
X#define _SBMCHARSIGN (CHAR_MIN < 0)	/* True if "char" is sign-extended */
X#define CHAR_MASK (UCHAR_MAX)
X
X#else	/* not ANSI */
X#ifndef _SBMUCHAR		/* Default assumes no "unsigned char" */
X#define _SBMUCHAR 0
X#endif
X#ifndef _SBMCHARSIGN		/* Default assumes "char" is sign-extended */
X#define _SBMCHARSIGN 1
X#endif
X#ifndef CHAR_MASK		/* Default assumes "char" is 8 bits */
X#define CHAR_MASK 0377
X#endif
X#endif	/* not ANSI */
X
X/* Define "sb_uchartoint" as a macro which ensures that an unsigned
X** character value is converted properly to an int value.
X*/
X#if (_SBMUCHAR || (_SBMCHARSIGN==0))
X#define sb_uchartoint(a) (a)		/* No fear of sign extension */
X#else
X#define sb_uchartoint(a) ((a)&CHAR_MASK)	/* Bah, sign extension */
X#endif
X
X
X/* Defs for machines with a base-2 WDSIZE.  Yes, the (int) is indeed necessary
X * (to allow implicit conversion to long where needed - the PDP11 compiler
X * is known to lose without it, because sizeof is cast as "unsigned int"
X * which loses big in long masks!)
X */
X#define WORD int
X#define WDSIZE ((int)(sizeof(WORD)))
X#define WDMASK (WDSIZE-1)
X#define WDBITS ((WDSIZE>>2)+(1&WDMASK))
X
X#define rnddiv(a) ((a)>>WDBITS)		/* # words, rounded down */
X#define rndrem(a) ((a)&WDMASK)		/* # bytes remaining past wd bndary */
X#define rnddwn(a) ((a)&~WDMASK)		/* Round down to word boundary */
X#define rndup(a)  rnddwn((a)+WDSIZE-1)	/* Round up to word boundary */
X
X#ifdef COMMENT	/* The following are for machines without a base-2 WDSIZE */
X#define rnddiv(a) ((a)/WDSIZE)
X#define rndrem(a) ((a)%WDSIZE)
X#define rnddwn(a) ((a)-rndrem(a))
X#define rndup(a)  rnddwn((a)+WDSIZE-1)
X#undef WDMASK			/* These become meaningless and anything */
X#undef WDBITS			/* which uses them should be changed! */
X#endif /* COMMENT */
X
X/* The following 3 definitions are somewhat machine-dependent,
X * but are specifically intended for general use and work for all
X * currently known C implementations.
X *	SBMO must be an integer-type object large enough to hold
X *	the largest difference in SBMA pointers, and must not be
X *	used in signed comparisons.
X */
X
Xtypedef long chroff;		/* CHROFF - Char offset in disk/sbstr */
Xtypedef unsigned int SBMO;	/* SBMO - Char offset in memory */
Xtypedef
X#if _SBMUCHAR
X	unsigned
X#endif
X		char *SBMA;	/* SBMA - Pointer to char loc in memory */
X
X
X
X/* The following definitions tend to be system-dependent.  Only the
X * SBM and SBSTR routines use them.
X */
X#define SB_NFILES 32		/* # of open files we can hack.  Actually
X				 * this is max FD value plus 1. */
X#define SB_BUFSIZ 512		/* Optimal buffer size (system block size) */
X#define SB_SLOP (16*WDSIZE)	/* # slop chars to tolerate for allocations */
X
X#define SMNODES (20)		/* # SM or SD nodes to create when needed */
X#define SMCHUNKSIZ (16*512)	/* # bytes of mem to create (via sbrk) " " */
X#define MAXSBMO ((SBMO)-1)	/* Used in SBM only */
X		/* MAXSBMO should be the largest possible SBMO value. */
X
X#define EOF (-1)
X#define SBFILE struct sbfile
X#define SBBUF struct sbbuffer
X#define SBSTR struct sdblk	/* Start of a sbstring */
X
Xstruct sbfile {
X	int sfflags;		/* Various flags */
X	int sffd;		/* FD for file (-1 if none) */
X	struct sdblk *sfptr1;	/* Ptr to 1st node in phys list */
X	chroff sflen;		/* Original length of file FD is for */
X};
X
X	/* Definition of SBBUF string/buffer */
Xstruct sbbuffer {
X	SBMA sbiop;		/* I/O pointer into in-core text */
X	int sbrleft;		/* # chars left for reading */
X	int sbwleft;		/* # chars left for writing */
X	int sbflags;		/* Various flags */
X	chroff sbdot;		/* Logical pos for start of current sdblk */
X	chroff sboff;		/* Offset into current sdblk (if no smblk)*/
X	struct sdblk *sbcur;	/* Pointer to current SD block of string */
X};
X	/* Flags for "sbflags" */
X#define SB_OVW	01	/* Over-write mode */
X#define SB_WRIT 02	/* Written; smuse needs to be updated from sbiop */
X
X	/* NOTE: An unused sbbuf structure should be completely zeroed.
X	 *	This will cause routines to handle it properly
X	 *	if they are accidentally pointed at it.
X	 */
X
X	/* Definition of SDBLK */
Xstruct sdblk {
X	struct sdblk *slforw;	/* Logical sequence forward link */
X	struct sdblk *slback;	/* Logical sequence backward link */
X	int sdflags;
X	struct sdblk *sdforw;	/* Physical sequence (disk) */
X	struct sdblk *sdback;	/* ditto - backptr for easy flushing */
X	struct smblk *sdmem;	/* Mem pointer, 0 if no in-core version */
X	SBFILE *sdfile;		/* File pointer, 0 if no disk version */
X	chroff sdlen;		/* # chars in disk text */
X	chroff sdaddr;		/* Disk address of text */
X};
X	/* Flags for "sdflags" */
X#define SD_LOCK 0100000		/* Locked because opened by a SBBUF */
X#define SD_LCK2	0040000		/* Locked for other reasons */
X#define SD_MOD	0020000		/* Modified, mem blk is real stuff */
X#define SD_NID	   0323		/* Node ID marks active (not on freelist) */
X#define SD_LOCKS (SD_LOCK|SD_LCK2)
X
X/* Note sdback is ONLY needed for fixing up phys list when a sdblk is
X * deleted (so as to find previous blk in phys list).  Perhaps it shd
X * be flushed (ie only use SDFORW)?  How to do deletions - use circular
X * list?  Sigh.
X */
X
X	/* Definition of SMBLK (used by SBM routines) */
Xstruct smblk {
X	struct smblk *smforw;	/* Links to other mem blks, in phys order */
X	struct smblk *smback;
X	int smflags;		/* Type, in-use flags */
X	SBMA smaddr;		/* Mem address of text */
X	SBMO smlen;		/* # bytes in mem block */
X	SBMO smuse;		/* # bytes "used" in block */
X};
X	/* Flags for "smflags" */
X#define SM_USE	0100000		/* Block is in use (mem free if off) */
X#define SM_NXM	 040000		/* Block mem is non-existent */
X#define SM_EXT	 020000		/* Block mem owned by external (non-SBM) rtn*/
X#define SM_MNODS 010000		/* Block holds SMBLK nodes */
X#define SM_DNODS  04000		/* Block holds SDBLK nodes */
X#define SM_NID	   0315		/* Node in-use identifier (low byte) */
X
X/* Error handler type values */
X#define SBMERR 0	/* Error in SBM package */
X#define SBXERR 1	/* Error in SBSTR package */
X#define SBFERR 2	/* "Error" - SBSTR package found a file overwritten.
X			 *	Non-zero return will continue normally. */
X
X
X/* Redefine certain external symbols to be unique in the first 6 chars
X** to conform with ANSI requirements.
X*/
X#define sbm_nfre sbmnfre	/* SBM stuff */
X#define sbm_nfor sbmnfor
X#define sbm_nmov sbmnmov
X#define sbm_ngc  sbmngc
X#define sbx_ndget sbxndg	/* SBSTR stuff */
X#define sbx_ndel  sbxnde
X#define sbx_ndfre sbxndf
X#define sbx_sdcpy sbxsdc
X#define sbx_sdgc  sbxsdg
X#define sbe_sdlist sbesls	/* SBERR stuff */
X#define sbe_sdtab  sbestb
X#define sbe_sds    sbesds
X#define sbe_sbvfy  sbesbv
X#define sbe_sbs    sbesbs
X
X/* Forward declarations */
Xextern SBMA sbm_lowaddr;	/* For roundoff purposes */
X
Xextern SBFILE sbv_tf;		/* SBFILE for temp swapout file */
Xextern int (*sbv_debug)();	/* Error handler address */
Xextern off_t lseek();		/* For sbstr code mostly */
Xextern char *mktemp();
Xextern char *malloc();
Xextern char *calloc();
Xextern SBBUF *sb_open();
Xextern SBSTR *sb_close(), *sb_fduse(), *sbs_cpy(), *sbs_app(), *sb_cpyn(),
X	*sb_killn();
Xextern struct sdblk *sbx_ready();
Xextern chroff sb_tell(), sb_ztell(), sbs_len();
X
X/* Definition of SB_GETC, SB_PUTC, SB_BACKC macros */
X
X#define sb_putc(s,c) (--((s)->sbwleft) >= 0 ? \
X				(*(s)->sbiop++ = c) : sb_sputc(s,c))
X#define sb_getc(s)   (--((s)->sbrleft) >= 0 ? \
X				sb_uchartoint(*(s)->sbiop++) : sb_sgetc(s))
X#define sb_peekc(s)  ((s)->sbrleft > 0 ? \
X				sb_uchartoint(*(s)->sbiop)   : sb_speekc(s))
X
X/* WARNING - sb_backc must ONLY be used if last operation was a
X * successful sb_getc!!  For slow but sure invocation use sb_rgetc.
X */
X#define sb_backc(s) (++(s->sbrleft), --(s->sbiop))
X
X#include "sbproto.h"	/* function prototypes */
/
echo x - sbbcpy.c
sed '/^X/s///' > sbbcpy.c << '/'
X
X#include "sb.h"
X
X/* BCOPY(from,to,cnt) - Copy string of bytes.
X *	Normally this routine is an assembly-language library routine,
X *	but not all systems have it.  Hence this C-language version
X *	which tries to be fairly machine-independent.
X *	Attempts to be clever about using word moves instead of byte moves.
X *	Does not hack overlapping backward moves.
X */
Xbcopy(from, to, cnt)	/* Copy count bytes from -> to */
Xregister SBMA from;
Xregister SBMA to;
Xregister unsigned cnt;
X{
X	if(!cnt)
X		return;
X	while(rndrem((int)from))	/* Get source aligned */
X	  {     *to++ = *from++;
X		if(--cnt == 0) return;
X	  }
X	if(rndrem((int)to) == 0)	/* Do word move if dest now aligned */
X	  {	register unsigned tmp;
X		tmp = cnt;
X		if((cnt = rnddiv(cnt)) > 4)
X		  {	sbm_wcpy((int *)from, (int *)to, cnt);
X			if((cnt = rndrem(tmp)) == 0)
X				return;	/* No leftover bytes, all done */
X			tmp -= cnt;	/* Ugh, must update pointers */
X			from += tmp;
X			to += tmp;
X		  }
X		else cnt = tmp;		/* Not worth call overhead */
X	  }                             
X	do { *to++ = *from++; }		/* Finish up with byte loop */
X	while(--cnt);
X}
X
X/* SBM_WCPY - word-move auxiliary routine.
X *	This is a separate routine so that machines with only a few
X *	registers have a chance to use them for the word copy loop.
X *	This cannot be made part of BCOPY without doing some
X *	unnecessary pointer conversions and using extra variables
X *	(since most compilers will not accept type casts on lvalues,
X *	which are needed to treat (char *) as (int *)).
X */
Xsbm_wcpy(from, to, cnt)
Xregister int *from, *to;
Xregister unsigned cnt;
X{
X	if(cnt)
X		do { *to++ = *from++; }
X		while(--cnt);
X}
/
echo x - sberr.c
sed '/^X/s///' > sberr.c << '/'
X/* SB - Copyright 1982 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.  In all cases
X *	the source code and any modifications thereto must remain
X *	available to any user.
X *
X *	This is part of the SB library package.
X *	Any software using the SB library must likewise be made
X *	quasi-public, with freely available sources.
X */
X
X#define PRINT		/* Include printout stuff */
X
X#include "sb.h"
X#include <stdio.h>
X
Xextern struct smblk *sbm_nfl;
Xextern struct smblk *sbm_list;
Xextern struct sdblk *sbx_nfl;
X
X#ifdef PRINT
X#define PRF(stmt) {if(p) stmt;}
X#define PRFBUG(str,stmt) {if(p) stmt;else return(str);}
X#define PRFBAD(str,stmt) {if(p) stmt; return(str);}
X#else
X#define PRF(stmt) ;
X#define PRFBUG(str,stmt) return(str);
X#define PRFBAD(str,stmt) return(str);
X#endif
X
X#ifndef NPTRS
X#define NPTRS (1000)		/* Catch loops of period less than this. */
X#endif
X
Xint sbe_dec = 0;		/* Set nonzero to use decimal printout */
X
Xstruct ptab {
X	int pt_pflag;		/* Printflag value */
X	char *pt_err;		/* Error string return */
X	int pt_xerr;		/* Error index return */
X	int pt_hidx;		/* Highest freelist entry */
X	int pt_nsto;		/* # entries stored in table */
X	int pt_cnt;		/* # of entry store attempts */
X	struct smblk *pt_tab[NPTRS];
X};
X
X_PROTOTYPE( char *sbe_sdtab, (struct ptab *pt, int p, int phys) );
X_PROTOTYPE( char *sbe_schk, (struct sdblk *sd, struct ptab *pt) );
X_PROTOTYPE( int sbe_tbent, (struct ptab *pt, struct smblk *sm) );
X
X#define PTF_PRF	01	/* Do printout stuff */
X#define PTF_OVFERR 02	/* Complain if table overflows */
X#define PTF_SDPHYS 04	/* Follow SD phys links (else logical links) */
X
Xstruct flgt {
X	int flg_bit;
X	int flg_chr;
X};
X
X_PROTOTYPE( char *sbe_fstr, (int flags, struct flgt *fp) );
X
Xchar *sbe_mvfy(), *sbe_mfl(), *sbe_mlst();		/* SBM */
Xchar *sbe_sbvfy(), *sbe_sbs();				/* SBBUF */
Xchar *sbe_svfy(), *sbe_sdlist(), *sbe_sdtab(), *sbe_schk();	/* SD */
Xchar *sbe_fstr();				/* Misc utility */
X
X
X/* SBE_MEM() - Print out memory usage list
X*/
Xsbe_mem()
X{
X	printf("\nMemory Usage:\n");
X	printf("\tsbm_nfl : %6o\n",sbm_nfl);
X	printf("\tsbm_list: %6o\n",sbm_list);
X	printf("\tsmblk nodes are %o bytes long.\n",sizeof (struct smblk));
X
X	sbe_mlst(1);		/* Scan mem list, printing stuff. */
X}
X
X/* SBE_MVFY() - Verify memory allocation structures
X *	Returns error message (0 if no errors found).
X */
Xchar *
Xsbe_mvfy()
X{	register char *res;
X
X	if((res = sbe_mfl(0))
X	  || (res = sbe_mlst(0)))
X		return(res);
X	return(0);
X}
X
X/* SBM Debugging Routines */
X
Xstruct flgt smflgtab[] = {
X	SM_USE,	'U',
X	SM_NXM, 'N',
X	SM_EXT, 'E',
X	SM_MNODS,'M',
X	SM_DNODS,'D',
X	0,0
X};
X
Xstatic char smfhelp[] = "U-Used, N-NXM, E-External, M-SMnodes, D-SDnodes";
Xstatic char smhdline[] = "\
X      SM: back   smaddr   smlen  smuse  smflags";
X
X/* SBE_MFL(printflag) - Verify/Print memory freelist
X *	Returns error message (0 if no errors found).
X */
Xchar *
Xsbe_mfl(p)
Xint p;
X{	register struct smblk *sm;
X	register int i;
X	struct ptab smtab;		/* For loop detection */
X
X	PRF(printf("Tracing SM node freelist --\n"))
X	PRF(printf("    Maximum loop detection size is %d.", NPTRS))
X	if((sm = sbm_nfl) == 0)
X	  {	PRF(printf("\n\tNo list.\n"))
X		return(0);			/* Null freelist is ok */
X	  }
X	smtab.pt_pflag = p ? PTF_PRF : 0;
X	smtab.pt_nsto = smtab.pt_cnt = 0;
X	i = 0;				/* Print 8 addrs/line */
X	for(; sm; sm = sm->smforw)
X	  {
X		PRF(printf("%s%7o->", (i==0 ? "\n    " : ""), sm))
X		if(++i >= 8) i = 0;
X		if(sbe_tbent(&smtab, sm) < 0)	/* If hit loop, stop */
X			PRFBAD("SM freelist loop",
X			  printf("\nLOOP - %o seen as node %d!!\n",
X				sm, smtab.pt_xerr))
X		if(sm->smflags)
X		  {	PRF((i = 0, printf("\nFreelist node has flags:\n")))
X			PRFBUG("Free SM flagged", sbe_smp(sm, 0))
X		  }
X	  }
X	PRF(printf("\nEnd - %d nodes on SM freelist.\n", smtab.pt_cnt))
X	return(0);
X}
X
X/* SBE_MLST(printflag) - Verify/Print allocated memory list.
X *	Returns error message (0 if no errors found).
X */
Xchar *
Xsbe_mlst(p)
Xint p;
X{	register struct smblk *sm, *smf, *smb;
X	char *nextaddr;
X	int i;
X	struct ptab smtab;		/* For loop detection */
X
X	PRF(printf("Tracing mem list -- \n"))
X	if((sm = sbm_list) == 0)
X	  {	PRF(printf("\tNo list?!\n"))
X		if(sbm_nfl)		/* Ensure rest are 0 too */
X			return("No mem list?!!");
X		return(0);
X	  }
X
X	smtab.pt_pflag = p;
X	smtab.pt_cnt = smtab.pt_nsto = 0;
X	smb = 0;
X	PRF(printf("   Flags: %s\n%s\n", smfhelp, smhdline))
X	for(; sm; sm = smf)
X	  {	PRF(printf("  %6o: ",sm))
X		if(sbe_tbent(&smtab, sm) < 0)
X			PRFBAD("Loop in mem list!!",
X			  printf("LOOP - seen as node %d!!\n", smtab.pt_xerr))
X
X		if(sm->smback == smb)
X			PRF(printf("^ "))	/* Back ptr OK */
X
X		else PRFBUG("Bad back ptr!",
X			printf("%6o BAD Backptr!!\n\t    ",sm->smback))
X
X		if((sm->smflags&0377)!= SM_NID)
X			PRFBUG("SM: bad node ID",
X				printf("BAD - no node ID!\n\t    "))
X		PRF(printf((sm->smflags&SM_USE) ? "     " : "FREE "))
X		if(sm->smlen == 0)
X			PRFBUG("SM: len 0",
X				printf("Zero-length area!"))
X		if((sm->smflags&SM_USE)==0
X		  && rndrem(sm->smaddr - sbm_lowaddr))
X			PRFBUG("Bad free-mem block",
X				printf("Bad free-mem block"))
X		PRF(sbe_smp(sm, 1))		/* Print out rest of info */
X
X		if(nextaddr != sm->smaddr
X		  && smtab.pt_cnt != 1)		/* 1st time needs init */
X		  {	PRFBUG("Alignment error!",
X				printf("\t  BAD!! %6o expected; ",nextaddr))
X#if !(MINIX)
X			PRF((i = sm->smaddr - nextaddr) > 0
X				? printf("%d skipped.\n",i)
X				: printf("%d overlapped.\n",-i))
X#endif
X		  }
X		nextaddr = sm->smaddr + sm->smlen;
X		smf = sm->smforw;
X		smb = sm;			/* Save ptr to back */
X	  }
X	PRF(printf("End = %6o\n",nextaddr))
X	return(0);
X}
X
X#ifdef PRINT
Xsbe_smp(sm,type)
Xregister struct smblk *sm;
Xint type;
X{
X	if(type==0)
X		printf("  %6o:  %s  ", sm,
X			((sm->smflags&SM_USE) ? "    " : "FREE"));
X	printf("%6o: ", sm->smaddr);
X	printf((sbe_dec ? "%5d. %5d." : "%6o %6o"), sm->smlen, sm->smuse);
X	printf("  %7o = %s\n", sm->smflags, sbe_fstr(sm->smflags, smflgtab));
X}
X#endif /*PRINT*/
X
X/* SD (SBSTR) debugging routines */
X
Xstruct flgt sdflgtab[] = {
X	SD_LOCK, 'L',
X	SD_LCK2, 'T',
X	SD_MOD,	 '*',
X	0,0
X};
X
Xstatic char sdfhelp[] = "\
X<f> flags: *-MOD (disk outofdate), L-LOCK, T-LCK2 (temp)";
Xstatic char sdhdline[] = "\
X<f>      SD: slforw slback sdflgs sdforw sdback  sdmem sdfile  sdaddr sdlen";
X
X
X/* SBE_SFL(printflag) - Verify/Print SD freelist
X *	Returns error message (0 if no errors found).
X */
Xchar *
Xsbe_sfl(p)
Xint p;
X{	register struct sdblk *sd;
X	register int i;
X	struct ptab sdtab;		/* For loop detection */
X
X	PRF(printf("Tracing SDBLK node freelist --\n"))
X	PRF(printf("    Maximum loop detection size is %d.", NPTRS))
X	if((sd = sbx_nfl) == 0)
X	  {	PRF(printf("\n\tNo list.\n"))
X		return(0);			/* Null freelist is ok */
X	  }
X	sdtab.pt_pflag = p ? PTF_PRF : 0;
X	sdtab.pt_nsto = sdtab.pt_cnt = 0;
X	i = 0;				/* Print 8 addrs/line */
X	for(; sd; sd = sd->slforw)
X	  {
X		PRF(printf("%s%7o->", (i==0 ? "\n    " : ""), sd))
X		if(++i >= 8) i = 0;
X		if(sbe_tbent(&sdtab, sd) < 0)	/* If hit loop, stop */
X			PRFBAD("SD freelist loop",
X			  printf("\nLOOP - %o seen as node %d!!",
X				sd, sdtab.pt_xerr))
X		if(sd->sdflags)
X		  {	PRF((i = 0, printf("\nFreelist node has flags:\n")))
X			PRFBUG("Free SD flagged", sbe_psd(sd))
X		  }
X	  }
X	PRF(printf("\nEnd - %d nodes on SD freelist.\n", sdtab.pt_cnt))
X	return(0);
X}
X
X
X
X/* SBE_SDS() - Print out all sdblk data stuff
X */
Xsbe_sds()
X{	int sbe_psd();
X
X	printf("Printout of all in-use SDBLKs:\n");
X	printf("  %s\n", sdfhelp);
X	printf("%s\n", sdhdline);
X	sbm_nfor(SM_DNODS,sizeof(struct sdblk),sbe_psd,0);
X	printf("\n");
X}
X
X/* SBE_PSD - Auxiliary for invocation by SBE_SDS above. */
Xsbe_psd(sd)
Xregister struct sdblk *sd;
X{	register int flags;
X
X	flags = sd->sdflags;
X	printf("%c%c%c",
X		((flags&SD_MOD)  ? '*' : ' '),
X		((flags&SD_LOCK) ? 'L' : ' '),
X		((flags&SD_LCK2) ? 'T' : ' '));
X
X	printf(" %7o: %6o %6o %6o %6o %6o %6o %6o %7lo %5ld.\n", sd,
X		sd->slforw, sd->slback, sd->sdflags,
X		sd->sdforw, sd->sdback, sd->sdmem,
X		sd->sdfile, sd->sdaddr, sd->sdlen);
X	return(0);
X}
X
X/* SBE_SVFY() - Verify all SD blocks
X *	Returns error message (0 if no errors found).
X */
Xchar *
Xsbe_svfy()
X{	register char *res;
X	return((res = sbe_sdlist(0,0)) ? res : sbe_sdlist(0,1));
X}
X
X/* SBE_SDLIST(printflag, physflag) - Verify/Print all SD blocks.
X *	Show logical lists if physflag 0
X *	Show physical lists otherwise
X *	Returns error message (0 if no errors found).
X */
Xchar *
Xsbe_sdlist(p,phys)
Xint p, phys;
X{	register char *res;
X	struct ptab sdtab;	/* The SDLIST table to use */
X
X	/* First put freelist in table, then scan for all
X	 * SD nodes.  Each active node (not in table) gets
X	 * its entire list traced forward/backward and added to table.
X	 */
X	if(res = sbe_sdtab(&sdtab, p, phys))	/* Set up freelist table */
X		return(res);
X
X	/* Freelist entered in table, now scan all SD's */
X	res = (char *)sbm_nfor(SM_DNODS,sizeof(struct sdblk),
X			sbe_schk, &sdtab);
X
X	PRF(printf("\n"))
X	return(res);
X}
X
X/* SBE_SDTAB(tableptr, printflag, physflag) - Auxiliary for SBE_SDLIST.
X *	Stuffs all freelist SDBLK addresses in table for dup detection.
X *	Returns error message (0 if no errors found).
X */
Xchar *
Xsbe_sdtab(pt, p, phys)
Xregister struct ptab *pt;
Xint p, phys;
X{	register struct sdblk *sd;
X	register int res;
X
X	pt->pt_pflag = (p ? PTF_PRF : 0) | (phys ? PTF_SDPHYS : 0)
X			| PTF_OVFERR;
X	pt->pt_cnt = pt->pt_nsto = 0;	/* Initialize */
X
X	/* Stick freelist in table */
X	for(sd = sbx_nfl; sd; sd = sd->slforw)
X	  {	if(sbe_tbent(pt, sd) < 0)
X		  {	if(pt->pt_xerr < 0)
X				PRFBAD("SD freelist too long",
X					printf("SD freelist too long (%d)\n",
X						NPTRS))
X			PRFBAD("SD freelist loop",
X			  printf("SD freelist loop at %o\n", pt->pt_xerr))
X		  }
X
X		if(sd->sdflags)
X		  {
X			PRF(printf("Bad free SD, non-zero flag:\n"))
X			PRFBUG("Free SD flagged", sbe_psd(sd))
X		  }
X	  }
X	pt->pt_hidx = pt->pt_nsto;	/* Set idx of 1st non-FL entry */
X	return(0);
X}
X
X/* SBE_SCHK(SDptr, tableptr) - Auxiliary for SBE_SDLIST.
X *	If SD not already in table, verifies or prints
X *	the complete physical or logical list it's on, and enters all
X *	of its SDs into table (to prevent doing it again).
X *	Returns 0 if no errors, else error string.
X** There is a problem when the table overflows.  The tbent routine
X** wants to add it (wrapping around at bottom) in that case, because
X** that still helps detect loops.  But this routine wants to reset
X** the table back (after scanning to head of list) and once it starts
X** scanning forward again it will fail, because some of the SDs are
X** still in the table due to the wraparound!  Thus PTF_OVFERR is always
X** set, in order to at least give the right error message.
X*/
Xchar *
Xsbe_schk(sd, pt)
Xregister struct sdblk *sd;
Xstruct ptab *pt;
X{	register struct sdblk *sdx;
X	register struct smblk *sm;
X	struct sbfile *savfile;
X	chroff lastaddr;
X	int p, res, savidx, phys;
X
X	phys = pt->pt_pflag&PTF_SDPHYS;	/* Set up physflag */
X	if(phys && (sd->sdfile == 0))	/* Ignore non-phys stuff if phys */
X		return(0);
X	p = pt->pt_pflag&PTF_PRF;	/* Set up printflag */
X	savidx = pt->pt_nsto;		/* Remember initial extent of table */
X
X	if(sbe_tbent(pt, sd) < 0)
X	  {	if(pt->pt_xerr >= 0)	/* OK if already in table */
X			return(0);
X		PRFBAD("Too many SDs",
X			printf("Too many SDs for table (%d)\n",	NPTRS))
X	  }
X
X	/* Now search backward for start of list */
X	while(sdx = (phys ? sd->sdback : sd->slback))
X		if(sbe_tbent(pt,sdx) >= 0)
X			sd = sdx;
X		else break;
X	if(sdx)
X	  {	if(pt->pt_xerr < 0)	/* Table error? */
X			PRFBAD("Too many SDs",
X				printf("Too many SDs for table (%d)\n",NPTRS))
X		PRF(printf("Backlist loop!! Dup'd node:%s\n",
X				(pt->pt_xerr < pt->pt_hidx) ?
X					"(on freelist!)" : "" ))
X		PRFBUG((phys ? "Phys SD loop" : "SD loop"), sbe_psd(sdx))
X	  }
X	/* Reset table to flush nodes backed over */
X	pt->pt_cnt = pt->pt_nsto = savidx;
X
X	/* SD now points to start of list.  Begin stepping thru list... */
X	PRF(printf("---- %sList started: ", (phys ? "Phys " : "")))
X	if(phys)
X	  {	savfile = sd->sdfile;
X		PRF(printf(" SF: %o, fd= %d, ln= %ld\n",
X			savfile,savfile->sffd,savfile->sflen))
X		if(savfile->sfptr1 != sd)
X			PRFBUG("SFPTR1 bad",
X			  printf("  BAD!! Sfptr1 %o doesn't match SD %o!!\n",
X				savfile->sfptr1, sd))
X		lastaddr = 0;
X	  }
X	else PRF(printf("\n"))
X
X	PRF(printf("%s\n", sdhdline))
X	for(sdx = 0; sd; (sdx = sd, sd = (phys ? sd->sdforw : sd->slforw)))
X	  {
X		PRF(sbe_psd(sd))	/* Print it out */
X		if(sdx != (phys ? sd->sdback : sd->slback))
X		  {	if(phys)
X			  PRFBUG("PSD bad sdback",printf("\tBad phys backptr\n"))
X			else
X			  PRFBUG("SD bad slback",printf("\tBad backptr\n"))
X		  }
X
X		if((sd->sdflags&0377) != SD_NID)
X			PRFBUG("Bad SD node ID", printf("\tBad node ID!\n"))
X
X
X		if(sd->sdfile && (sd->sdlen < 0 || sd->sdaddr < 0))
X			PRFBUG("SD: neg len/addr",
X				printf("\tNeg disk len/addr\n"))
X		if(phys) goto dophys;
X
X		/* Do special stuff for logical list */
X		if(sm = sd->sdmem)
X		  {	if((sm->smflags&0377) != SM_NID)
X				PRFBUG("SD: bad SM",
X					printf("\nBad SMBLK ptr\n"))
X			if((sd->sdflags&SD_MOD)==0
X			  && sd->sdlen != sm->smuse)
X				PRFBUG("SD != SM",
X					printf("\tBad SMBLK? Len conflict\n"))
X			if(sm->smlen < sm->smuse)
X				PRFBUG("SD: SM len < use",
X					printf("\tBad SMBLK, len < use\n"))
X		  }
X		goto doboth;	/* Skip phys stuff */
X
X		/* Do special stuff for phys list */
X	dophys:	if(sd->sdfile != savfile)
X			PRFBUG("SD: bad sdfile",
X				printf("\tBad sdfile ptr! Shd be %o\n",
X					savfile))
X		if(sd->sdaddr < lastaddr)
X			PRFBUG("SD addr out of order",
X				printf("\tBad disk addr, not in order!\n"))
X		lastaddr = sd->sdaddr;
X		/* Done with special phys stuff */
X
X	doboth:	if(sbe_tbent(pt, sd) < 0)
X		  {	if(pt->pt_xerr < 0)
X				PRFBAD("Too many SDs",
X					printf("Too many SDs for table (%d)\n",NPTRS))
X
X			PRFBUG("SD loop",
X				printf("\tLOOP!! This SD already seen%s.\n",
X					(pt->pt_xerr < pt->pt_hidx) ?
X					" (on freelist!)" : "" ))
X			break;
X		  }
X	  }
X	PRF(printf("-----------\n"))
X	return(0);
X}
X
X/* SBE_DSK(SFptr) - Print out disk usage list for specific file
X */
X
Xsbe_dsk(sfp)
XSBFILE *sfp;
X{
X	printf("SBFILE printout not coded: %o\n",sfp);
X}
X
X/* SBBUF structure debugging routines */
X
Xstruct flgt sbflgtab[] = {
X	SB_OVW, 'O',
X	SB_WRIT,'W',
X	0,0
X};
Xstatic char sbfhelp[] = "O-Overwrite, W-Write";
X
X/* SBE_SBVFY(SBptr) - Verify a SB-string.
X *	Returns error message (0 if no errors found).
X */
Xchar *
Xsbe_sbvfy(sbp)
XSBBUF *sbp;
X{	return(sbe_sbs(sbp,0));
X}
X
X/* SBE_SBS(SBptr, printflag) - Verify/Print SBSTR data stuff
X *	Returns error message (0 if no errors found).
X */
Xchar *
Xsbe_sbs(sbp,p)
XSBBUF *sbp;
Xint p;
X{	register SBBUF *sb;
X	register struct smblk *sm;
X	register struct sdblk *sd;
X
X	sb = sbp;
X	PRF(printf("SBSTR %o: ",sb))
X	if(sb == 0)
X		PRFBUG(0,printf("Zero pointer???\n"))
X
X	/* First print out cryptic summary in case pointers bomb
X	 * out farther on. */
X	PRF(printf(" (io,cur,r,w,f,.,+ = %o,%o,%d,%d,%o,%lo,%lo)\n",
X		sb->sbiop, sb->sbcur, sb->sbrleft, sb->sbwleft,
X		sb->sbflags, sb->sbdot, sb->sboff))
X
X	PRF(printf("  sbflags %5o = %s (%s)\n",
X			sb->sbflags, sbe_fstr(sb->sbflags,sbflgtab),
X			sbfhelp))
X
X	if(sd = sb->sbcur)	/* Okay, now try getting pointers */
X		sm = sd->sdmem;
X	else sm = 0;
X
X	PRF(printf("  sbcur %6o",sd))
X	if(sd)
X	  {
X		PRF(printf("\n   %s\n   ", sdhdline))
X		PRF(sbe_psd(sd))
X
X		if((sd->sdflags&0377) != SD_NID)
X			PRFBUG("SBCUR not SD?",printf("   BAD SDBLK ID!! \n"))
X		if(sm)
X		  {
X			PRF(printf("   %s\n   ", smhdline))
X			PRF(sbe_smp(sm,0))
X			if((sm->smflags&0377) != SM_NID)
X				PRFBUG("SBCUR has bad SM",
X					printf("   BAD SMBLK ID!!\n"))
X		  }
X	  }
X
X
X	PRF(printf("  sbiop  %6o",sb->sbiop))
X	if(sb->sbiop)
X	  {	if(!sm || sb->sbiop < sm->smaddr
X		  || sb->sbiop > (sm->smaddr + sm->smlen))
X			PRFBUG("Bad SBIOP", printf("  BAD"))
X	  }
X	else if(sb->sbrleft > 0 || sb->sbwleft > 0)
X		PRFBUG("Bad SBIOP/cnts", printf("  BAD"))
X	PRF(printf("\n"))
X
X	PRF(printf("  sbrleft %5o = %5d.",sb->sbrleft, sb->sbrleft))
X	if(sb->sbrleft
X	  && (	!sm
X	    ||	sb->sbwleft
X	    ||	(sb->sbflags&SB_WRIT)
X	    ||	(sb->sbrleft != (sm->smuse - (sb->sbiop - sm->smaddr)))
X	    ))
X		PRFBUG("Bad sbrleft", printf("  BAD"))
X	PRF(printf("\n"))
X
X	PRF(printf("  sbwleft %5o = %5d.", sb->sbwleft, sb->sbwleft))
X	if(sb->sbwleft
X	  && (	!sm
X	    ||	(sb->sbflags&SB_WRIT) == 0
X	    ||	(sb->sbwleft > (sm->smlen - (sb->sbiop - sm->smaddr)))
X	    ))
X		PRFBUG("Bad sbwleft", printf("  BAD"))
X	PRF(printf("\n"))
X
X	PRF(printf("  sbdot %7lo = %7ld.", sb->sbdot, sb->sbdot))
X	if(sb->sbdot < 0)
X		PRFBUG("Bad sbdot", printf("  BAD"))
X
X	PRF(printf("\n  sboff %7lo = %7ld.\n", sb->sboff, sb->sboff))
X	PRF(printf("  I/O ptr loc: %ld.\n\n", sb_tell(sb)))
X
X	return(0);
X}
X
X/* SBE_TBENT() - Auxiliary to add and check entries to a pointer table.
X *	Note we assume here that smblk ptrs are used, although sdblks
X *	can also be hacked.  This wins as long as the two kinds of ptrs
X *	are basically identical (saves horrible casting problems).
X *	Returns index # if successful (between 0 and NPTRS-1 inclusive).
X *	Otherwise an error (-1), with relevant info in pt_xerr:
X *		-1 if out of room and flag set making it an error
X *		0-n if entry already existed.
X */
Xsbe_tbent(pt, sm)
Xregister struct ptab *pt;
Xstruct smblk *sm;
X{	register struct smblk **smt;
X	register int i;
X	int p;
X
X	p = pt->pt_pflag&PTF_PRF;	/* Set up print flag */
X	smt = &(pt->pt_tab[0]);
X	if(i = pt->pt_nsto)
X	  {	do {
X			if(sm == *smt++)
X			  {	pt->pt_xerr = pt->pt_nsto - i;
X				return(-1);
X			  }
X		  } while(--i);
X		--smt;
X	  }
X
X	i = pt->pt_cnt++;
X	if(++(pt->pt_nsto) > NPTRS)
X	  {	if(pt->pt_pflag&PTF_OVFERR)
X		  {	pt->pt_err = "Ptrtab overflow";
X			pt->pt_xerr = -1;
X			return(-1);
X		  }
X		pt->pt_nsto = NPTRS;
X		i %= NPTRS;
X	  }
X	pt->pt_tab[i] = sm;
X	return(i);
X}
X
X/* SBE_FSTR(flags, flagtab) - Auxiliary to convert flag word to a string
X *	and return pointer to it.  Handy for printfs.
X */
Xchar *
Xsbe_fstr(flags, fp)
Xregister int flags;
Xregister struct flgt *fp;
X{	static char retstr[17];	/* Max of 16 flags */
X	register char *cp;
X	cp = retstr;
X	for(; fp->flg_bit; ++fp)
X		*cp++ = (fp->flg_bit&flags) ? fp->flg_chr : ' ';
X	*cp = 0;
X	return(retstr);
X}
/
echo x - sbm.c
sed '/^X/s///' > sbm.c << '/'
X/* SB - Copyright 1982 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.  In all cases
X *	the source code and any modifications thereto must remain
X *	available to any user.
X *
X *	This is part of the SB library package.
X *	Any software using the SB library must likewise be made
X *	quasi-public, with freely available sources.
X */
X
X#if 0
X	This file contains the low-level memory allocation
Xsubroutines which are used by the SBLK routines.  The code here
Xis quite machine-dependent, and the definitions in "sb.h" should be
Xcarefully checked to verify that they are correct for the target
Xmachine.
X
X	The ultimate low-level routine is "sbrk()" which must be
Xprovided by the system''s C library.  SBM expects that successive calls
Xto sbrk() will return contiguous areas of memory with progressively
Xhigher addresses.  Also, the very first call to sbrk() is assumed to
Xreturn a word-aligned address.
X#endif /*COMMENT*/
X
X#include "sb.h"
X
X#define FUDGE (sizeof(struct smblk))	/* Allow this much fudge in
X				allocation, to prevent undue fragmentation */
X
Xchar *(*sbm_debug)();           /* Debug switch - user-furnished routine */
X
Xstruct smblk *sbm_nfl;          /* Pointer to node freelist */
Xstruct smblk *sbm_nxtra;	/* Reserved extra free node */
Xstruct smblk *sbm_list;         /* Pointer to smblk memory alloc list.
X				 * ALL smblks are strung onto this list
X				 * except for the freelist!
X				 */
XSBMA sbm_lowaddr;		/* Lowest word-aligned address we know about.*/
X
X/* If compiling with debug switch set, use special routine in place of
X * sbrk so we can pretend we have a very limited area of free memory.
X */
X#ifdef DBG_SIZE
X#define SBM_SBRK sbm_brk
Xchar *sbm_brk();
X#else
X#define SBM_SBRK sbrk
X#endif /*DBG_SIZE*/
X
X/* Forward routine declarations */
Xchar *sbrk();
Xstruct smblk *sbm_nmak(), *sbm_nget(), *sbm_mget(), *sbm_split();
Xstruct smblk *sbm_lmak(), *sbm_err();
X
X/* SBM_INIT - Initialize storage management.
X *	If args are zero, normal initialization is done.  Otherwise,
X *	args are understood to be pointers to an area of memory allocated
X *	on the stack (eg by an "int mem[2000]" declaration in MAIN) and
X *	initialization will include this area in addition to the
X *	unused space between "_end" and the start of the stack segment.
X *	This is mostly of use for PDP11s which would otherwise waste a lot
X *	of address space.
X *	Maybe should have a SBM_RESET() function?
X */
X
Xstruct smblk *
Xsbm_init(xaddr,xlen)
XSBMA xaddr;		/* Address of allocated stack area if any */
XSBMO xlen;		/* Size of this area */
X{       register struct smblk *sm, *sml;
X	register char *cp;
X
X	/* Get initial chunk of memory from standard system rtn */
X	if((cp = SBM_SBRK(SMNODES*sizeof(struct smblk))) == 0
X	  || (int) cp == -1)
X		return(sbm_err(0,"Can't sbrk"));
X	sm = (struct smblk *)cp;		/* Better be word-aligned! */
X	sbm_lmak(sm,(SBMO)sizeof(struct smblk),SMNODES);      /* Make list */
X	sbm_nfl = sm;				/* Point freelist at it */
X	sbm_lowaddr = (SBMA)sm;			/* Remember lowest addr seen */
X
X	/* Set up 1st node pointing to all memory from here on up.
X	 * We don't know exactly how much will be available at this point,
X	 * so we just pretend we have the maximum possible.
X	 */
X	sbm_list = sml = sbm_nget();
X	sml->smforw = sml->smback = 0;
X	sml->smflags = SM_USE|SM_NID;		/* Initial flags */
X	sml->smaddr = (SBMA) sml;
X	sml->smlen = MAXSBMO;			/* Pretend we have lots */
X	sml->smuse = (SMNODES * sizeof(struct smblk));
X
X	/* Now split off everything above initial allocation as NXM. */
X	sm = sbm_split(sml, sm->smuse);
X	sml->smflags |= SM_MNODS;	/* Mark 1st node as having SM nodes */
X	sm->smflags  |= SM_NXM;		/* Mark 2nd node as NXM */
X
X	/* Now possibly set up extra nodes, if stack mem is being allocated
X	 * (From our viewpoint it looks as if a chunk in the middle of
X	 * the initial NXM section has been declared usable)
X	 */
X	if(xlen)
X	  {     /* Allow for "extra" static stack memory */
X		/* Will lose if xaddr <= 1st NXM! */
X		sml = sbm_split(sm, (SBMO)(xaddr - sm->smaddr));
X		sbm_split(sml, xlen);		/* Split off following NXM */
X		sml->smflags &= ~(SM_USE|SM_NXM); /* This node is free mem! */
X	  }
X
X	/* Now set up a small additional node which points to the NXM
X	 * that we cannot get from SBRK.  At this stage, this is just
X	 * a place-holder, to reserve the node so we don't have to
X	 * worry about running out of nodes at the same time sbrk stops
X	 * returning memory.
X	 * SM points to the NXM that we expect SBRK to dig into.
X	 */
X	sbm_split(sm, sm->smlen - WDSIZE); /* Chop off teensy bit */
X	sm->smflags &= ~SM_USE;		/* Now mark NXM "free"!! */
X
X	/* Finally, reserve an "extra" SM node for use by sbm_nget
X	 * when it is allocating more freelist node chunks.
X	 */
X	sbm_nxtra = sbm_nget();
X
X	return(sbm_list);
X}
X
X/* SBM_NGET() - Get a free SM node.
X *	Note the hair to provide a spare SM node when
X *	we are allocating memory for more SM nodes.  This is necessary
X *	because sbm_mget and sbm_nget call each other recursively and
X *	sbm_mget cannot create any new memory without a SM node to point
X *	at the allocated chunk.
X */
Xstruct smblk *
Xsbm_nget()
X{       register struct smblk *sm, *sml;
X
X	if(!(sm = sbm_nfl))		/* Get a node from freelist */
X	  {	/* Freelist is empty, try to allocate more nodes. */
X
X		/* Put our "spare" smblk on freelist temporarily so that
X		 * sbm_mget has a chance of winning.
X		 * Infinite recursion is avoided by a test
X		 * in sbm_mget which checks sbm_nfl and sbm_nxtra.
X		 */
X		if(!(sm = sbm_nxtra))
X			return(sbm_err(0,"Zero sbm_nxtra!"));
X		sm->smforw = 0;
X		sbm_nfl = sm;
X		sbm_nxtra = 0;
X
X		/* Try to allocate another chunk of SM nodes. */
X		sml = sbm_nmak(sizeof(struct smblk),SM_MNODS);
X
X		/* Put the new free nodes (if any) on freelist.
X		 * Done this way because freelist may have had one or two
X		 * nodes added to it by sbm_mget, so can't just stick
X		 * a new pointer in sbm_nfl.
X		 */
X		while(sm = sml)
X		  {	sml = sm->smforw;
X			sbm_nfre(sm);
X		  }
X
X		/* Now reserve an extra node again.
X		 * It is an error if there is nothing on freelist here,
X		 * because even if sbm_mget failed the "extra node" should
X		 * still be on freelist.  The check for a zero sbm_nxtra
X		 * above will catch such an error.
X		 */
X		sbm_nxtra = sbm_nget();
X
X		/* Now see if anything to return */
X		if(!(sm = sbm_nfl))		/* If freelist empty again, */
X			return(0);		/* give up. */
X	  }
X	sbm_nfl = sm->smforw;   /* If win, take it off freelist */
X	return(sm);		/* Return ptr or 0 if none */
X}
X
X/* SBM_NFRE(sm) - Return a SM node to the SM freelist.
X */
Xsbm_nfre(smp)
Xstruct smblk *smp;
X{       register struct smblk *sm;
X	(sm = smp)->smflags = 0;
X	sm->smforw = sbm_nfl;
X	sbm_nfl = sm;
X}
X
X/* SBM_NMAK(elsize, flag) - Make (allocate & build) a typeless node freelist.
X */
Xstruct smblk *
Xsbm_nmak(elsize, flag)
XSBMO elsize;
Xunsigned flag;
X{       register struct smblk *sm, *smp;
X	register int cnt;
X
X	if((sm = sbm_mget(SMNODES*elsize,SMNODES*elsize)) == 0)
X		return(0);
X
X	sm->smflags |= flag;            /* Indicate type of nodes */
X	cnt = sm->smlen/elsize;		/* Find # nodes that will fit */
X	sm->smuse = cnt * elsize;	/* Actual size used */
X	smp = (struct smblk *)(sm->smaddr);	/* Ptr to 1st loc of mem */
X	sbm_lmak(smp, (SBMO)elsize, cnt);	/* Build freelist */
X	return(smp);            /* Return 1st free node. Caller is */
X				/* responsible for setting freelist ptr. */
X}
X
X/* SBM_LMAK - Build freelist of typeless nodes.
X *	Note this does not allocate memory, it just converts an already
X *	allocated memory area.
X */
Xstruct smblk *
Xsbm_lmak(addr, elsize, num)
XSBMA addr;
XSBMO elsize;
Xint num;
X{	register struct smblk *sm, *smp;
X	register int cnt;
X
X	smp = (struct smblk *) addr;
X	if((cnt = num) <= 0)
X		return(0);
X	do {	sm = smp;       /* Save ptr */
X		sm->smforw = (smp = (struct smblk *) ((SBMA)smp + elsize));
X		sm->smflags = 0;
X	  } while(--cnt);
X	sm->smforw = 0;         /* Last node points to nothing */
X	return(sm);		/* Return ptr to last node */
X}
X
X/* SBM_NMOV(sm1, sm2, begp, elsize) - Move a typeless node.
X *	Copy sm1 to sm2, adjust ptrs, leave sm1 free.
X */
Xsbm_nmov(smp1,smp2,begp,elsize)
Xstruct smblk *smp1, *smp2, **begp;
Xint elsize;
X{       register struct smblk *sm;
X
X	bcopy((SBMA)smp1,(SBMA)(sm = smp2), elsize);     /* Copy the stuff */
X	if(sm->smforw) sm->smforw->smback = sm; /* Fix up links */
X	if(sm->smback) sm->smback->smforw = sm;
X	else *begp = sm;
X}
X
X/* SBM_MGET(min,max) - Get a SMBLK with specified amount of memory.
X *      Returns 0 if none available.
X *      Memory is guaranteed to start on word boundary, but may not
X *              end on one.  Note that sbm_mfree is responsible for
X *              ensuring that free mem starts word-aligned.
X *	A subtle but major concern of this code is the number of freelist
X * nodes gobbled by a single call.  If the freelist happens to not have
X * enough nodes, then a recursive call to sbm_mget is made (via sbm_nget)
X * in order to allocate a new batch of freelist nodes!  sbm_nget will
X * always provide a single "spare" node during such an allocation, but
X * there is only one and it is essential that sbm_mget gobble only ONE
X * (if any) during such a call, which is indicated by sbm_nxtra==0.
X *	The maximum # of freelist nodes that sbm_mget can gobble is
X * 2, when (1) NXM memory is obtained, and a SM is needed to point at
X * the new free mem, plus (2) the resulting SM is too big, and has to
X * be split up, which requires another SM for the remainder.
X *	The "used-NXM" smblk is set up at init time precisely in order to
X * avoid the necessity of creating it here when sbrk stops winning, since
X * that would require yet another freelist node and make it possible for
X * sbm_mget to gobble 3 during one call -- too many.
X *	Further note: the sbm_nfl checks are necessary in order
X * to ensure that a SM node is available for use by sbm_split.  Otherwise
X * the calls to sbm_split might create a new SM freelist by gobbling the
X * very memory which we are hoping to return!
X */
XSBMO sbm_chksiz = SMCHUNKSIZ;	/* Current chunk size to feed sbrk */
X
Xstruct smblk *
Xsbm_mget(cmin,cmax)
XSBMO cmin,cmax;
X{       register struct smblk *sm, *sml;
X	register SBMO csiz;
X	register SBMA addr, xaddr;
X
X	if((sm = sbm_list) == 0         /* If never done, */
X	  && (sm = sbm_init((SBMA)0,(SBMO)0)) == 0)	/* initialize mem alloc stuff. */
X		return(0);		/* Can't init??? */
X
X	/* Round up sizes to word boundary */
X	if(rndrem(cmin)) cmin = rndup(cmin);
X	if(rndrem(cmax)) cmax = rndup(cmax);
X
X	/* Search for a free block having enough memory.
X	 * If run into a free-NXM block, always "win", since there may be
X	 * a combination of preceding free-mem and new mem which will satisfy
X	 * the request.  If it turns out this didn't work, we'll just fail
X	 * a little farther on.
X	 */
Xretry:	csiz = cmin;			/* Set size that will satisfy us */
X	do {
X		if(  ((sm->smflags&SM_USE) == 0)
X		  && ((sm->smlen >= csiz) || (sm->smflags&SM_NXM)) )
X			break;
X	  } while(sm = sm->smforw);
X	if(sm == 0)
X		return(0);	/* Found none that minimum would fit */
X
X	if(sm->smflags&SM_NXM)
X	  {	/* Found free area, but it's marked NXM and the system
X		 * must be persuaded (via sbrk) to let us use that portion
X		 * of our address space.  Grab a good-sized chunk.
X		 */
X		if(sbm_nfl == 0)	/* Verify a spare SM node is avail */
X			goto getnod;	/* Nope, must get one. */
X
X		/* Decide amount of mem to ask system for, via sbrk.
X		 * The fine point here is the check of sbm_nxtra to make sure
X		 * that, when building more freelist nodes, we don't have
X		 * to use more than one SM node in the process.  If we
X		 * asked for too much mem, we'd have to use a SM node
X		 * to hold the excess after splitting.
X		 */
X		csiz = cmax;
X		if(sbm_nxtra		/* If normal then try for big chunk */
X		  && csiz < sbm_chksiz) csiz = sbm_chksiz;	/* Max */
X		if (csiz > sm->smlen)  csiz = sm->smlen;	/* Min */
X
X		/* Get the NXM mem */
X		if((addr = (SBMA)SBM_SBRK(csiz)) != sm->smaddr)
X		  {     /* Unexpected value returned from SBRK! */
X
X			if((int)addr != 0 && (int)addr != -1)
X			  {	return(sbm_err(0,"SBRK %o != %o", addr,
X						sm->smaddr));
X#if 0
X			/* If value indicates couldn't get the stuff, then
X			 * we have probably hit our limit and the rest of
X			 * NXM should be declared "used" to prevent further
X			 * hopeless sbrk calls.  We split off the portion
X			 * of NXM that is known for sure to be unavailable,
X			 * and mark it "used".  If a "used NXM" area already
X			 * exists following this one, the two are merged.
X			 * The chunk size is then reduced by half, so
X			 * only log2(SMCHUNKSIZ) attempts will be made, and
X			 * we try again.
X			 */
X				/* If returned some mem which starts outside
X				 * the NXM then something is screwed up. */
X				if(addr < sm->smaddr
X				  || (addr >= sm->smaddr+sm->smlen))
X					return(sbm_err(0,"SBRK %o != %o",
X						addr, sm->smaddr));
X				/* Got some mem, falls within NXM.
X				 * Presumably someone else has called sbrk
X				 * since last time, so we need to fence off
X				 * the intervening area. */
X				sm = sbm_split((sml=sm),(addr - sm->smaddr));
X				sml->smflags |= SM_USE|SM_EXT;
X				return(sbm_mget(cmin,cmax));
X#endif /*COMMENT*/
X			  }
X
X			/* Handle case of SBRK claiming no more memory.
X			 * Gobble as much as we can, and then turn this NXM
X			 * block into a free-mem block, and leave the
X			 * remainder in the used-NXM block (which should
X			 * immediately follow this free-NXM block!)
X			 */
X			if(!(sml = sm->smforw)	/* Ensure have used-NXM blk */
X			  || (sml->smflags&(SM_USE|SM_NXM))
X					!= (SM_USE|SM_NXM))
X				return(sbm_err(0,"No uNXM node!"));
X			xaddr = sm->smaddr;	/* Use this for checking */
X			sm->smuse = 0;		/* Use this for sum */
X			for(csiz = sm->smlen; csiz > 0;)
X			  {	addr = SBM_SBRK(csiz);
X				if((int)addr == 0 || (int)addr == -1)
X				  {	csiz >>= 1;
X					continue;
X				  }
X				if(addr != xaddr)
X					return(sbm_err(0,"SBRK %o != %o", addr,
X						xaddr));
X				sm->smuse += csiz;
X				xaddr += csiz;
X			  }
X
X			/* Have gobbled as much from SBRK as we could.
X			 * Turn the free-NXM block into a free-mem block,
X			 * unless we got nothing, in which case just merge
X			 * it into the used-NXM block and continue
X			 * searching from this point.
X			 */
X			if(!(csiz = sm->smuse))	/* Get total added */
X			  {	sm->smflags = sml->smflags;	/* Ugh. */
X				sbm_mmrg(sm);
X				goto retry;		/* Keep looking */
X			  }
X			else
X			  {	sml->smaddr = sm->smaddr + csiz;
X				sml->smlen += sm->smlen - csiz;
X				sm->smlen = csiz;
X				sm->smflags &= ~SM_NXM;	/* No longer NXM */
X			  }
X		  }
X
X		/* Here when we've acquired CSIZ more memory from sbrk.
X		 * If preceding mem area is not in use, merge new mem
X		 * into it.
X		 */
X		if((sml = sm->smback) && 
X		  (sml->smflags&(SM_USE|SM_NXM))==0)    /* Previous free? */
X		  {     sml->smlen += csiz;		/* Yes, simple! */
X			sm->smaddr += csiz;		/* Fix up */
X			if((sm->smlen -= csiz) == 0)	/* If no NXM left,*/
X				sbm_mmrg(sml);	/* Merge NXM node w/prev */
X			sm = sml;		/* Prev is now winning node */
X		  }
X		else
X		  {	/* Prev node isn't a free area.  Split up the NXM
X			 * node to account for acquired mem, unless we
X			 * gobbled all the mem available.
X			 */
X			if(sm->smlen > csiz	/* Split unless all used */
X			  && !sbm_split(sm,csiz)) /* Call shd always win */
X				return(sbm_err(0,"getsplit err: %o",sm));
X			sm->smflags &= ~SM_NXM;	/* Node is now real mem */
X		  }
X
X		/* Now make a final check that we have enough memory.
X		 * This can fail because SBRK may not have been able
X		 * to gobble enough memory, either because (1) not
X		 * as much NXM was available as we thought,
X		 * or (2) we noticed the free-NXM area and immediately
X		 * gambled on trying it without checking any lengths.
X		 * In any case, we try again starting from the current SM
X		 * because there may be more free mem higher up (eg on
X		 * stack).
X		 */
X		if(sm->smlen < cmin)
X			goto retry;
X	  }
X
X	/* Check to see if node has too much mem.  This is especially true
X	 * for memory just acquired via sbrk, which gobbles a huge chunk each
X	 * time.  If there's too much, we split up the area.
X	 */
X	if(sm->smlen > cmax+FUDGE)	/* Got too much?  (Allow some fudge)*/
X		/* Yes, split up so don't gobble too much. */
X		if(sbm_nfl)                     /* If success guaranteed, */
X			sbm_split(sm,cmax);     /* split it, all's well. */
X		else goto getnod;
X
X	sm->smuse = 0;
X	sm->smflags |= SM_USE;  /* Finally seize it by marking "in-use". */
X	return(sm);
X
X	/* Come here when we will need to get another SM node but the
X	 * SM freelist is empty.  We have to forget about using the area
X	 * we just found, because sbm_nget may gobble it for the
X	 * freelist.  So, we first force a refill of the freelist, and then
X	 * invoke ourselves again on what's left.
X	 */
Xgetnod:
X	if(sml = sbm_nget())		/* Try to build freelist */
X	  {	sbm_nfre(sml);		/* Won, give node back, */
X		sm = sbm_list;		/* and retry, starting over! */
X		goto retry;	
X	  }
X	/* Failed.  Not enough memory for both this request
X	 * and one more block of SM nodes.  Since such a SM_MNODS
X	 * block isn't very big, we are so close to the limits that it
X	 * isn't worth trying to do something fancy here to satisfy the
X	 * original request.  So we just fail.
X	 */
X	return(0);
X}
X
X#ifdef DBG_SIZE
X/* Code for debugging stuff by imposing an artificial limitation on size
X * of available memory.
X */
XSBMO sbm_dlim = MAXSBMO;	/* Amount of mem to allow (default is max) */
X
Xchar *
Xsbm_brk(size)
Xunsigned size;
X{	register char *addr;
X
X	if(size > sbm_dlim) return(0);
X	addr = sbrk(size);
X	if((int)addr == 0 || (int)addr == -1)
X		return(0);
X	sbm_dlim -= size;
X	return(addr);
X}
X#endif /*DBG_SIZE*/
X
X/* SBM_MFREE(sm) - Free up an allocated memory area.
X */
Xsbm_mfree(sm)
Xregister struct smblk *sm;
X{       register struct smblk *smx;
X	register SBMO crem;
X
X	sm->smflags &= ~SM_USE;			/* Say mem is free */
X	if((smx = sm->smback)                   /* Check preceding mem */
X	  && (smx->smflags&(SM_USE|SM_NXM))==0) /*   If it's free, */
X		sbm_mmrg(sm = smx);		/*   then merge 'em. */
X	if((smx = sm->smforw)			/* Check following mem */
X	  && (smx->smflags&(SM_USE|SM_NXM))==0) /*   Again, if free,  */
X		sbm_mmrg(sm);                   /*   merge them.   */
X
X	if(sm->smlen == 0)              /* Just in case, chk for null blk */
X	  {     if(smx = sm->smback)            /* If pred exists, */
X			sbm_mmrg(smx);          /* merge quietly. */
X		else {
X			sbm_list = sm->smforw;  /* 1st node on list, so */
X			sbm_nfre(sm);           /* simply flush it. */
X		  }
X		return;
X	  }
X
X	/* This code is slightly over-general for some machines.
X	 * The pointer subtraction is done in order to get a valid integer
X	 * offset value regardless of the internal representation of a pointer.
X	 * We cannot reliably force alignment via casts; some C implementations
X	 * treat that as a no-op.
X	 */
X	if(crem = rndrem(sm->smaddr - sbm_lowaddr))	/* On word bndry? */
X	  {     /* No -- must adjust.  All free mem blks MUST, by fiat,
X		 * start on word boundary.  Here we fix things by
X		 * making the leftover bytes belong to the previous blk,
X		 * no matter what it is used for.  Prev blk is guaranteed to
X		 * (1) Exist (this cannot be 1st blk since 1st is known to
X		 * start on wd boundary) and to be (2) Non-free (else it would
X		 * have been merged).
X		 */
X		if((smx = sm->smback) == 0)     /* Get ptr to prev blk */
X		  {	sbm_err(0,"Align err");	/* Catch screws */
X			return;
X		  }
X		crem = WDSIZE - crem;	/* Find # bytes to flush */
X		if(crem >= sm->smlen)	/* Make sure node has that many */
X		  {	sbm_mmrg(smx);  /* Flush node to avoid zero length */
X			return;
X		  }
X		smx->smlen += crem;	/* Make stray bytes part of prev */
X		sm->smaddr += crem;	/* And flush from current. */
X		sm->smlen -= crem;
X	  }
X}
X
X/* SBM_EXP - Expand (or shrink) size of an allocated memory chunk.
X *	"nsize" is desired new size; may be larger or smaller than current
X *	size.
X */
Xstruct smblk *
Xsbm_exp(sm,size)
Xregister struct smblk *sm;
Xregister SBMO size;
X{       register struct smblk *smf;
X	register SBMO mexp, pred, succ;
X
X	if(sm->smlen >= size)		/* Do we want truncation? */
X		goto realo2;		/* Yup, go split block */
X
X	/* Block is expanding. */
X	mexp = size - sm->smlen;		/* Get # bytes to expand by */
X	pred = succ = 0;
X	if((smf = sm->smforw)           	/* See if free mem follows */
X	 && (smf->smflags&(SM_USE|SM_NXM)) == 0)
X		if((succ = smf->smlen) >= mexp)
X			goto realo1;		/* Quick stuff if succ OK */
X
X	if((smf = sm->smback)			/* See if free mem precedes */
X	 && (smf->smflags&(SM_USE|SM_NXM)) == 0)
X		pred = smf->smlen;
X
X	/* If not enough free space combined on both sides of this chunk,
X	 * we have to look for a completely new block.
X	 */
X	if(pred+succ < mexp)
X	  {	if((smf = sbm_mget(size,size)) == 0)
X			return(0);              /* Couldn't find one */
X		else pred = 0;			/* Won, indicate new block */
X	  }
X
X	/* OK, must copy either into new block or down into predecessor
X	 * (overlap is OK as long as bcopy moves 1st byte first)
X	 */
X	bcopy(sm->smaddr, smf->smaddr, sm->smlen);
X	smf->smflags = sm->smflags;     /* Copy extra attribs */
X	smf->smuse = sm->smuse;
X	if(!pred)			/* If invoked sbm_mget */
X	  {	sbm_mfree(sm);		/* then must free up old area */
X		return(smf);		/* and can return immediately. */
X	  }
X	sbm_mmrg(smf);			/* Merge current into pred blk */
X	sm = smf;			/* Now pred is current blk. */
X
X	if(succ)
Xrealo1:		sbm_mmrg(sm);		/* Merge succ into current blk */
Xrealo2: if(sm->smlen > size		/* If now have too much, */
X	  && sbm_split(sm, size))       /* split up and possibly */
X		sbm_mfree(sm->smforw);  /* free up unused space. */
X	return(sm);
X
X	/* Note that sbm_split can fail if it can't get a free node,
X	 * which is only possible if we are reducing the size of an area.
X	 * If it fails, we just return anyway without truncating the area.
X	 */
X}
X
X/* SBM_MMRG(sm) - Merge a memory area with the area following it.
X *	The node (and memory area) following the SM pointed to are
X *	merged in and the successor node freed up.  The flags
X *	and smuse of the current SM (which is not moved or anything)
X *	remain the same.
X */
Xsbm_mmrg(smp)
Xstruct smblk *smp;
X{       register struct smblk *sm, *sm2;
X
X	sm = smp;
X	sm->smlen += (sm2 = sm->smforw)->smlen;	/* Add succ's len */
X	if(sm->smforw = sm2->smforw)            /* and fix linkages */
X		sm->smforw->smback = sm;
X	sbm_nfre(sm2);                          /* now can flush succ node */
X}
X
X/* SBM_SPLIT - Split up an area (gets a new smblk to point to split-off
X *	portion.)
X * Note returned value is ptr to 2nd smblk, since this is a new one.
X * Ptr to 1st remains valid since original smblk stays where it is.
X * NOTE: Beware of splitting up free mem (SM_USE == 0) since sbm_nget may
X * steal it out from under unless precautions are taken!  See comments
X * at sbm_mget related to this.
X */
Xstruct smblk *
Xsbm_split(smp,coff)
Xstruct smblk *smp;
XSBMO coff;
X{       register struct smblk *sm, *smx;
X	register SBMO csiz;
X
X	if((sm = smp)->smlen <= (csiz = coff))
X		return(0);
X	if((smx = sbm_nget()) == 0)
X		return(0);
X	smx->smlen = sm->smlen - csiz;          /* Set 2nd size */
X	smx->smaddr = sm->smaddr + csiz;        /* Set 2nd addr */
X	sm->smlen = csiz;			/* Take from 1st size */
X	smx->smflags = sm->smflags;             /* Copy flags */
X	if(smx->smforw = sm->smforw)            /* Splice 2nd after 1 */
X		smx->smforw->smback = smx;
X	smx->smback = sm;
X	sm->smforw = smx;                       /* Put 2nd into chain */
X	return(smx);                            /* Return ptr to 2nd smblk */
X}
X
X#if 0	/* Replaced by "bcopy" for system-dep efficiency */
X/* SBM_SCPY - Copy string of bytes.  Somewhat machine-dependent;
X *	Tries to be clever about using word moves instead of byte moves.
X */
Xsbm_scpy(from, to, count)       /* Copy count bytes from -> to */
Xchar *from, *to;
Xunsigned count;
X{       register char *s1, *s2;
X	register unsigned cnt;
X	int tmp;
X
X	if((cnt = count) == 0)
X		return;
X	s1 = from;
X	s2 = to;
X	while(rndrem((int)s1))		/* Get 1st ptr aligned */
X	  {     *s2++ = *s1++;
X		if(--cnt == 0) return;
X	  }
X	if(rndrem((int)s2) == 0)	/* Do wd move if ptr 2 now aligned */
X	  {
X#ifdef DUMBPCC /* Code for dumber (Portable C type) compiler */
X		register WORD *ap, *bp;
X		tmp = cnt;
X		ap = (WORD *) s1;
X		bp = (WORD *) s2;
X		if(cnt = rnddiv(cnt))
X			do { *bp++ = *ap++; }
X			while(--cnt);
X		if ((cnt = rndrem(tmp)) ==0)
X			return;
X		s1 = (char *) ap;
X		s2 = (char *) bp;
X#else
X	/* Tight loop for efficient copying on 11s */
X		tmp = cnt;
X		if(cnt = rnddiv(cnt))
X			do { *((WORD *)s2)++ = *((WORD *)s1)++; }
X			while(--cnt);
X		if((cnt = rndrem(tmp)) == 0)
X			return;
X#endif /*-DUMBPCC*/
X	  }                             
X	do { *s2++ = *s1++; }	/* Finish up with byte loop */
X	while(--cnt);
X}
X#endif /*COMMENT*/
X
Xstruct smblk *		/* If it returns at all, this is most common type */
Xsbm_err(val,str,a0,a1,a2,a3)
Xchar *str;
Xstruct smblk *val;
X{	int *sptr;
X
X	sptr = (int *) &sptr;	/* Point to self on stack */
X	sptr += 5;		/* Point to return addr */
X	if((int)sbm_debug==1)
X		abort();
X	if(sbm_debug)
X		(*sbm_debug)(0,*sptr,str,a0,a1,a2,a3);
X	return(val);
X}
X
X/* These routines correspond to the V7 LIBC routines as described
X * in the V7 UPM (3).  They should provide satisfactory emulation
X * if the documentation is correct.  Replacement is necessary since
X * the SBM routines are jealous and cannot tolerate competition for
X * calls of SBRK; i.e. the memory being managed must be contiguous.
X */
X
X/* Guaranteed to return word-aligned pointer to area of AT LEAST
X * requested size.  Area size is rounded up to word boundary.
X */
X
Xchar *
Xmalloc(size)
Xunsigned size;
X{       register struct smblk *sm, **sma;
X	register SBMO siz;
X
X	siz = rndup(size + sizeof (struct smblk *));   /* Make room for ptr */
X	if((sm = sbm_mget(siz,siz)) == 0)
X		return(0);
X	*(sma = (struct smblk **)sm->smaddr) = sm; /* Store ptr in addr-1 */
X	return((char *)++sma);
X}
X
Xchar *
Xalloc(size)     /* For V6 programs - note different failure value! */
Xunsigned size;
X{       register char *addr;
X	return((addr = malloc(size)) ? addr : (char *) -1);
X}
X
Xfree(ptr)
Xchar *ptr;
X{       register struct smblk *sm, **smp;
X
X	smp = &((struct smblk **)ptr)[-1];	/* Point to addr-1 */
X	sm = *smp;				/* Pluck SM ptr therefrom */
X	if(((sm->smflags&0377) != SM_NID) || sm->smaddr != (SBMA)smp)
X		return((int)sbm_err(0,"free: bad arg %o", ptr));
X	sbm_mfree(sm);
X	return(1);
X}
X
Xchar *
Xrealloc(ptr,size)
Xchar *ptr;
Xunsigned size;
X{       register struct smblk *sm, **smp;
X
X	smp = &((struct smblk **)ptr)[-1];	/* Point to addr-1 */
X	sm = *smp;				/* Pluck SM ptr therefrom */
X	if(((sm->smflags&0377) != SM_NID) || (sm->smaddr != (SBMA)smp))
X		return((char *)sbm_err(0,"realloc: bad arg %o",ptr));
X	if((sm = sbm_exp(sm, rndup(size+(sizeof(struct smblk *))))) == 0)
X		return(0);
X	*(smp = (struct smblk **)sm->smaddr) = sm;      /* Save smblk ptr */
X	return((char *)++smp);
X}
X
Xchar *
Xcalloc(nelem,elsize)
Xunsigned nelem, elsize;
X{       register SBMO cmin;
X	register WORD *ip;                     /* Clear in units of words */
X	register char *addr;
X
X	if((cmin = nelem*elsize) == 0           /* Find # bytes to get */
X	  || (addr = malloc(cmin)) == 0)        /* Get it */
X		return(0);
X	ip = (WORD *) addr;			/* Set up ptr to area */
X	cmin = rnddiv(cmin+WDSIZE-1);		/* Find # words to clear */
X	do { *ip++ = 0; } while (--cmin);       /* Zap the area */
X	return(addr);
X}
X
X/* SBM_NGC() - Specific routine for GC'ing SMBLK nodes.
X *
X * SBM_XNGC(begp, elsize, type) - Compact nodes of specified type.
X *      Scans allocated mem from low to high to find chunks with nodes of
X *	the specified type.
X *      Flushes current freelist and rebuilds it as scan progresses,
X *      such that 1st thing on list is lowest-addr node.  When a node is
X *      seen that can be moved, new node is acquired from freelist if
X *      it exists, otherwise no move is made.  If a chunk has been scanned
X *      and no active nodes remain, it is flushed and freelist updated.
X *      NOTE: This has not yet been verified to work with nodes of any
X *		type other than SMBLK.
X */
X
Xsbm_ngc()
X{	register struct smblk *sm;
X	if(!(sm = sbm_nxtra))
X		return((int)sbm_err(0,"Zero sbm_nxtra"));
X	sm->smflags |= SM_USE;		/* Ensure this one isn't GC'd */
X	sbm_xngc(&sbm_nfl, sizeof(struct smblk), SM_MNODS);
X	sm->smflags = 0;		/* Flush temporary crock */
X}
Xsbm_xngc(begp, elsize, flag)
Xstruct smblk **begp;
Xunsigned elsize, flag;
X{       register struct smblk *sm, *chk, *smf;
X	struct smblk *ftail, *savtail;
X	int cnt, inuse;
X
X	*begp = ftail = 0;		/* Flush node freelist */
X	for(chk = sbm_list; chk; chk = chk->smforw)
X	  if(chk->smflags&flag)
X	    {   sm = (struct smblk *) chk->smaddr;
X		cnt = (chk->smuse)/elsize;
X		savtail = ftail;
X		inuse = 0;
X		smf = *begp;
X					 /* Set up ptr to 1st freelist node */
X		while(--cnt >= 0)
X		  {     /* Here decide if movable */
X			if(sm->smflags && smf   /* Live and have copy place */
X			  && (
X				(sm->smflags&SM_USE) == 0       /* Free mem? */
X			    ||  (sm->smflags&(SM_MNODS|SM_DNODS))
X			     )
X			  && sm->smback)        /* has backptr (see ncpy) */
X			  {                             /* Move the node */
X				*begp = smf->smforw;	/* Get free node */
X				if(smf == ftail)
X					ftail = 0;
X				if(smf == savtail)
X					savtail = 0;
X				/* Move node.  Already checked for back ptr
X				 * of 0 since no obvious way to tell where
X				 * the ptr to list is kept.  Sigh.
X				 */
X				sbm_nmov(sm,smf,(struct smblk **)0,elsize);
X				/* Get ptr to new freelist node.  Note
X				 * check to ensure that it is not in this
X				 * same chunk (if it is, no point in moving
X				 * any nodes!)
X				 */
X				if((smf = *begp) >= chk)
X					smf = 0;        /* Zero if same chk */
X				sm->smflags = 0;        /* Make node free */
X			  }
X			/* At this point, not movable */
X			if(sm->smflags == 0)            /* Free node? */
X			  {     if(ftail)               /* Add to freelist */
X					ftail->smforw = sm;
X				ftail = sm;
X				if(*begp == 0)
X					*begp = sm;
X				sm->smforw = 0;
X			  }
X			else inuse++;
X			sm = (struct smblk *)((SBMA)sm + elsize);
X		  }
X		if(inuse == 0                           /* All free? */
X		  && (sm = chk->smback))		/* & not 1st? */
X		  {     if(savtail)                     /* Edit freelist */
X				(ftail = savtail)->smforw = 0;
X			else *begp = ftail = 0;
X			sbm_mfree(chk);
X			chk = sm;
X		  }
X	    }
X}
X
X/*
X *      Note that proc must return a zero value, or loop aborts and
X *      returns that selfsame value.
X */
Xsbm_nfor(flag,nodsiz,proc,arg)
Xint flag;
Xint (*proc)();
Xint nodsiz;
Xstruct sbfile *arg;
X{       register struct smblk *sm, *np;
X	register int cnt;
X	int res;
X
X	for(sm = sbm_list; sm; sm = sm->smforw)
X	  if(sm->smflags&flag)
X	    {   np = (struct smblk *) sm->smaddr;
X		cnt = sm->smuse/nodsiz;
X		do {
X			if(np->smflags)
X				if(res = (*proc)(np,arg))
X					return(res);
X			np = (struct smblk *)((SBMA)np + nodsiz);
X		  } while(--cnt);
X	    }
X	return(0);
X}
/
echo x - sbproto.h
sed '/^X/s///' > sbproto.h << '/'
X#ifndef _ANSI
X#include <ansi.h>
X#endif
X
X/* sbbcpy.c */
X_PROTOTYPE( int bcopy, (SBMA from, SBMA to, unsigned cnt) );
X_PROTOTYPE( int sbm_wcpy, (int *from, int *to, unsigned cnt) );
X
X/* sberr.c */
X_PROTOTYPE( int sbe_mem, (void) );
X_PROTOTYPE( char *sbe_mvfy, (void) );
X_PROTOTYPE( char *sbe_mfl, (int p) );
X_PROTOTYPE( char *sbe_mlst, (int p) );
X_PROTOTYPE( int sbe_smp, (struct smblk *sm, int type) );
X_PROTOTYPE( char *sbe_sfl, (int p) );
X_PROTOTYPE( int sbe_sds, (void) );
X_PROTOTYPE( int sbe_psd, (struct sdblk *sd) );
X_PROTOTYPE( char *sbe_svfy, (void) );
X_PROTOTYPE( char *sbe_sdlist, (int p, int phys) );
X_PROTOTYPE( int sbe_dsk, (SBFILE *sfp) );
X_PROTOTYPE( char *sbe_sbvfy, (SBBUF *sbp) );
X_PROTOTYPE( char *sbe_sbs, (SBBUF *sbp, int p) );
X
X/* sbm.c */
X_PROTOTYPE( struct smblk *sbm_init, (SBMA xaddr, SBMO xlen) );
X_PROTOTYPE( struct smblk *sbm_nget, (void) );
X_PROTOTYPE( int sbm_nfre, (struct smblk *smp) );
X_PROTOTYPE( struct smblk *sbm_nmak, (SBMO elsize, unsigned flag) );
X_PROTOTYPE( struct smblk *sbm_lmak, (SBMA addr, SBMO elsize, int num) );
X_PROTOTYPE( int sbm_nmov, (struct smblk *smp1, struct smblk *smp2, struct smblk **begp, int elsize) );
X_PROTOTYPE( struct smblk *sbm_mget, (SBMO cmin, SBMO cmax) );
X_PROTOTYPE( char *sbm_brk, (unsigned size) );
X_PROTOTYPE( int sbm_mfree, (struct smblk *sm) );
X_PROTOTYPE( struct smblk *sbm_exp, (struct smblk *sm, SBMO size) );
X_PROTOTYPE( int sbm_mmrg, (struct smblk *smp) );
X_PROTOTYPE( struct smblk *sbm_split, (struct smblk *smp, SBMO coff) );
X_PROTOTYPE( int sbm_scpy, (char *from, char *to, unsigned count) );
X#if 0
X_PROTOTYPE( struct smblk *sbm_err, (struct smblk *val, char *str, int a0, int a1, int a2, int a3) );
X#else
Xstruct smblk *sbm_err();
X#endif
X_PROTOTYPE( char *malloc, (unsigned size) );
X_PROTOTYPE( char *alloc, (unsigned size) );
X_PROTOTYPE( int free, (char *ptr) );
X_PROTOTYPE( char *realloc, (char *ptr, unsigned size) );
X_PROTOTYPE( char *calloc, (unsigned nelem, unsigned elsize) );
X_PROTOTYPE( int sbm_ngc, (void) );
X_PROTOTYPE( int sbm_xngc, (struct smblk **begp, unsigned elsize, unsigned flag) );
X_PROTOTYPE( int sbm_nfor, (int flag, int nodsiz, int (*proc )(), struct sbfile *arg) );
X
X/* sbstr.c */
X_PROTOTYPE( SBSTR *sb_close, (SBBUF *sbp) );
X_PROTOTYPE( int sb_setovw, (SBBUF *sbp) );
X_PROTOTYPE( int sb_clrovw, (SBBUF *sbp) );
X_PROTOTYPE( chroff sbx_fdlen, (int fd) );
X_PROTOTYPE( SBSTR *sb_fduse, (int ifd) );
X_PROTOTYPE( int sb_fdcls, (int ifd) );
X_PROTOTYPE( int sbx_fcls, (struct sbfile *sfp) );
X_PROTOTYPE( int sb_fdinp, (SBBUF *sb, int fd) );
X_PROTOTYPE( int sb_fsave, (SBBUF *sb, int fd) );
X_PROTOTYPE( int sb_sgetc, (SBBUF *sb) );
X_PROTOTYPE( int sb_sputc, (SBBUF *sb, int ch) );
X_PROTOTYPE( int sb_speekc, (SBBUF *sb) );
X_PROTOTYPE( int sb_rgetc, (SBBUF *sb) );
X_PROTOTYPE( int sb_rdelc, (SBBUF *sbp) );
X_PROTOTYPE( int sb_deln, (SBBUF *sbp, chroff num) );
X_PROTOTYPE( struct sdblk *sb_killn, (SBBUF *sbp, chroff num) );
X_PROTOTYPE( SBSTR *sb_cpyn, (SBBUF *sbp, chroff num) );
X_PROTOTYPE( int sb_sins, (SBBUF *sbp, struct sdblk *sdp) );
X_PROTOTYPE( SBSTR *sbs_cpy, (SBSTR *sdp) );
X_PROTOTYPE( int sbs_del, (SBSTR *sdp) );
X_PROTOTYPE( SBSTR *sbs_app, (struct sdblk *sdp, struct sdblk *sdp2) );
X_PROTOTYPE( chroff sbs_len, (SBSTR *sdp) );
X_PROTOTYPE( int sb_seek, (SBBUF *sbp, chroff coff, int flg) );
X_PROTOTYPE( int sb_rewind, (SBBUF *sbp) );
X_PROTOTYPE( chroff sb_tell, (SBBUF *sbp) );
X_PROTOTYPE( chroff sb_ztell, (SBBUF *sbp) );
X#if 0
X_PROTOTYPE( struct sdblk *sbx_ready, (SBBUF *sbp, int type, SBMO cmin, SBMO cmax) );
X#else
Xstruct sdblk *sbx_ready();
X#endif
X_PROTOTYPE( struct sdblk *sbx_next, (SBBUF *sbp) );
X_PROTOTYPE( struct sdblk *sbx_norm, (SBBUF *sbp, int mode) );
X_PROTOTYPE( struct sdblk *sbx_beg, (struct sdblk *sdp) );
X_PROTOTYPE( int sbx_smdisc, (SBBUF *sbp) );
X_PROTOTYPE( int sbx_sbrdy, (SBBUF *sbp) );
X_PROTOTYPE( struct sdblk *sbx_scpy, (struct sdblk *sdp, struct sdblk *sdlast) );
X_PROTOTYPE( struct sdblk *sbx_sdcpy, (struct sdblk *sdp) );
X_PROTOTYPE( struct sdblk *sbx_xcis, (SBBUF *sbp, chroff num, struct sdblk **asd2, chroff *adot) );
X_PROTOTYPE( struct sdblk *sbx_split, (struct sdblk *sdp, chroff coff) );
X_PROTOTYPE( struct smblk *sbx_msplit, (struct smblk *smp, SBMO size) );
X_PROTOTYPE( struct sdblk *sbx_ndel, (struct sdblk *sdp) );
X_PROTOTYPE( int sbx_npdel, (struct sdblk *sdp) );
X_PROTOTYPE( struct sdblk *sbx_ndget, (void) );
X_PROTOTYPE( int sbx_ndfre, (struct sdblk *sdp) );
X_PROTOTYPE( SBMA sbx_malloc, (unsigned size) );
X_PROTOTYPE( struct smblk *sbx_mget, (SBMO cmin, SBMO cmax) );
X_PROTOTYPE( int sbx_comp, (int cmin, int lev) );
X_PROTOTYPE( int sbx_sdgc, (struct sdblk *sdp, int lev) );
X#if 0
X_PROTOTYPE( int sbx_aout, (struct sdblk *sdp, int flag, int fd) );
X#else
Xint sbx_aout();
X#endif
X_PROTOTYPE( chroff sbx_qlen, (struct sdblk *sdp) );
X_PROTOTYPE( int sbx_tset, (chroff loff, int align) );
X_PROTOTYPE( struct sdblk *sbx_ffnd, (SBFILE *sfp, chroff size, chroff *aloc) );
X_PROTOTYPE( int sbx_rdf, (int fd, char *addr, int cnt, int skflg, chroff loc) );
X_PROTOTYPE( int sbx_rugpull, (int fd) );
X_PROTOTYPE( int sbx_unpur, (struct sdblk *sd, struct sbfile *sf) );
X#if 0
X_PROTOTYPE( int sbx_err, (int val, char *str, int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12) );
X#else
Xint sbx_err();
X#endif
X
X/* sbvall.c */
X_PROTOTYPE( char *valloc, (unsigned size) );
/
echo x - sbstr.c
sed '/^X/s///' > sbstr.c << '/'
X/* SB - Copyright 1982 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.  In all cases
X *	the source code and any modifications thereto must remain
X *	available to any user.
X *
X *	This is part of the SB library package.
X *	Any software using the SB library must likewise be made
X *	quasi-public, with freely available sources.
X */
X
X#if 0
XTodo stuff:
X	New definitions:
X		sbbuffer - old sbstr.  Abbrev & struct "sbbuff".  Macro SBBUFF
X			(or SBBUF?)
X		sbstring - list of sds.  Abbrev sbstr.  Macro SBSTR.
X			Should *sbstr == *sdblk?  Yeah.
X		sbfile - as before.  Macro SBFILE. (or SBFIL?)
X
X	Try to get zero-length sdblks flushed on the fly,
X		rather than waiting for moby GC.  Also, need to set
X		up compaction of SD freelist, as well as SM freelist.
X		Make SM freelist compact self-invoked by SBM_MGET?
X	Any need for phys disk ptrs other than for tempfile?
X		Can do sbm_forn through SDblks to find active sdfiles
X		so list isn''t needed for that.
X	Can sdback be flushed?  (not needed for keeping list sorted,
X		or for searching it -- only used when linking
X		blocks in or out of list.)  Perhaps use circular list?
X		If list only used for tmpfile, then to link in/out could
X		always start from sfptr1 of tmpfile? Sure, but slow?
X		Last SD on phys list could belong to no logical list,
X		and denote free space on tmpfile?
X
X	--------------------------
X
X	An "open" SBBUFFER will allow one to read, write, insert into,
Xand delete from a sbstring (a logical character string).  "Dot" refers
Xto the current logical character position, which is where all
Xoperations must happen; sb_fseek must be used to change this location.
XThere are several states that the I/O can be in:
X!SBCUR		----CLOSED----
X		All other elements, including SBIOP, should also be 0.
X		Dot is 0.
XSBCUR && !SBIOP	----OPEN/IDLE----
X		SBCUR points to a SD block (its SDMEM may or may not exist)
X		SBIOP==0 (otherwise it would be open/ready)
X		Dot is SBDOT + SBOFF.
X		R/Wleft must be 0.
XSBCUR && SBIOP	----OPEN/READY----
X		SBCUR points to a SDBLK (SDMEM must exist!)
X		SBIOP exists.
X		Dot is SBDOT + offset into SMBLK.  SBOFF is ignored!
X		SB_WRIT flag is set if "smuse" must be updated.
X		The R/Wleft counts are set up:
X		1. Rleft 0, Wleft 0 -- Since SBIOP is set, must assume
X			counts are too.
X			So this means at end of text, no room left.
X			Otherwise would imply that setup needs doing.
X		2. Rleft N, Wleft 0 -- At beg or middle of text
X		3. Rleft 0, Wleft N -- At end of text
X		4. Rleft N, Wleft N -- Shouldn''t ever happen
X
X		Note that Rleft is always correct.  Wleft is sometimes
X		set 0 in order to force a call to determine real state.
X
XNote that SBIOP alone is a sufficient test for being OPEN/READY.
X
XThe important thing about updating the smblk is to ensure that the "smuse"
Xfield is correct.  This can only be changed by writing or deleting.  We assume
Xthat deletions always update immediately, thus to determine if an update
Xis necessary, see if SB_WRIT is set.  If so, update smuse before doing
Xanything but more writing!!!!
X
XThe SDBLK must be marked "modified" whenever a write operation is
Xdone.  We try to do this only the first time, by keeping Wleft zero
Xuntil after the first write.  This is also when SB_WRIT gets set.
XHowever, if in overwrite mode, Wleft must be kept zero in order to
Xforce the proper actions; SB_WRIT is also not turned on since smuse
Xwill not change.  Note that at EOF, overwrite becomes the same thing
Xas insert and is treated identically...
X
X	If a SBLK has an in-core copy but no disk copy, it can be
Xfreely modified.  Otherwise, modifications should preferably split
Xthe block so as to retain "pure" blocks as long as possible.  "Pure" blocks
Xcan always have their in-core versions flushed immediately (unless for
Xcompaction purposes they''ll need to be written out in the same GC pass).
XAlternatively, mods can simply mark the disk copy "free" and go
Xahead as if no such copy existed.
X	No additions or changes to a pure block are allowed, but
Xdeletions from the end or beginning are always allowed.  All other
Xchanges must split or insert new blocks to accomplish the changes.
X
XLocking:
X	SDBLKs are subject to unpredictable relocation, compaction,
Xand garbage collecting.  There are three ways in which a SDBLK can
Xremain fixed:
X
X	1. The SDBLK has the SD_LOCK flag set.  This flag is used whenever
X		a SBBUF''s SBCUR is pointing to this SDBLK.
X	2. The SDBLK has the SD_LCK2 flag set.  This flag is used only
X		during execution of various internal routines and should
X		not be seen anywhere during execution of user code.
X	3. The SDBLK has no back-pointer (is first block in a sbstring).
X		Such SDBLKs cannot be relocated (since it is not known
X		what may be pointing to them) but unlike the other 2 cases
X		they are still subject to compaction with succeeding SDBLKs.
X
XThe SDBLK must be locked with SD_LOCK for as long as it is being
Xpointed to by SBCUR.  The sole exception is when a SBBUF in the
XOPEN/IDLE state is pointing to the first SDBLK of a sbstring; this
Xsdblk is guaranteed not to be moved, since sdblks without a
Xback-pointer are never moved.  SD_LOCK is asserted as soon as the state
Xchanges to OPEN/READY, of course.  The internal routines take pains to
Xalways move SD_LOCK as appropriate.  Note that only one SD in a
Xsbstring can ever have SD_LOCK turned on.  SD_LCK2 is an auxiliary flag
Xwhich may appear in more than one SDBLK, for use by low-level routines
Xfor various temporary reasons; either will prevent the SDBLK from being
Xmodified in any way by the storage compactor.
X
XSEEKs are a problem because it''s unclear at seek time what will happen
Xnext, so the excision of the smblk can''t be optimized.  If the seek
Xhappens to land in a sdblk with an existing smblk, there''s no problem;
Xbut if it''s a sdblk alone, how to decide which part of it to read in???
XIf next action is:
X	write - split up sdblk and create new one.  Read nothing in.
X	read - read in 512 bytes starting at disk blk boundary if possible
X		else read in 128 bytes starting with selected char
X		(include beg of sdblk if less than 64 chars away)
X	overwrite - as for read.
X	backread - like read but position at end of sdblk.
X	delete - split up sdblk, read nothing in.
X
XWe solve this through the OPEN/IDLE state, where SBIOP == 0 means SBOFF
Xpoints to logical offset from start of current sdblk, so that the seek
Xneed not take any action.  Only when a specific operation is requested
Xwill the transition to OPEN/READY take place, at which time we''ll know
Xwhat the optimal excision strategy is.  The routine SBX_READY performs
Xthis function.
X
XThe physical links (SDFORW and SDBACK) are only valid when SDFILE is
Xset (likewise for SDLEN and SDADDR).  In other words, mungs to a sdblk
Xmust check SDFILE to see whether or not the phys links should be
Xaltered.  Normally they aren''t except during sdblk creation, deletion,
Xor swapout, no matter how much the sdblk gets shuffled around
Xlogically.  The disk physical list is kept sorted in order of starting
Xaddresses.  The text blocks indicated can overlap.  When a GC is
Xnecessary, the code must figure out how much space is actually free.
X
X-------------- Old woolgathering, ignore rest of this page ---------------
X
XQuestion: should 512-byte buffers be maintained, one for each SBFILE?
XOr should the in-core text be hacked up to serve for buffering?
XQuestion is where to point the READ/WRITE system calls.  Currently,
Xthey are pointed directly at the in-core text, and there are no
Xauxiliary buffers.
X
XIf use auxiliary buffers:
X	How to handle flushing, when changing location etc?
X	Could be clever about reading from large disk block, only
X	get part of it into buffer instead of splitting up in order to
X	read a "whole" block.
X	Problem: sbstrings can include pieces of several different files.
X		Hard to maintain just one buffer per FD without hacking
X		done on one sbstring screwing that on another.
XIf don''t use buffers:
X	Need to have a "chars-left" field in mem blocks, so know how
X	much more can be added.  Will need heuristics for how much
X	extra space to allocate.
X#endif /*COMMENT*/
X
X/* Includes, initial definitions */
X
X#include <stdio.h>
X#include "sb.h"
X
X#ifndef V6
X#define V6 0
X#endif
X
X#if V6
X#include <stat.h>
X#else
X#include <sys/types.h>
X#include <sys/stat.h>
X#if MINIX
X#include <fcntl.h>	/* For open() flags */
X#else
X#include <sys/file.h>	/* For open() flags */
X#endif /* MINIX */
X#endif /*-V6*/
X
Xextern int errno;
Xextern char *strerror();	/* From ANSI <string.h> */
X
X/* Allocation decls */
XSBFILE sbv_tf;		/* SBFILE for temp swapout file */
Xint (*sbv_debug)();	/* Error handler address */
X
X
X/* SBX_READY argument flags (internal to SBSTR routines only)
X * The following values should all be unique; the exact value
X * doesn't matter as long as the right SKM flags are given.
X */
X#define SK_READF 0			/* 0-skip fwd,  align BOB */
X#define SK_READB (0|SKM_0BACK|SKM_EOB)	/* 0-skip bkwd, align EOB */
X#define SK_WRITEF (0|SKM_EOB)		/* 0-skip fwd,  align EOB */
X#define SK_DELF (4|SKM_0BACK)		/* 0-skip bkwd, align BOB */
X#define SK_DELB (4|SKM_EOB)		/* 0-skip fwd,  align EOB */
X#define SKM_0BACK 01	/* Zero-skip direction: 0 = fwd, set = backwd
X			 * Don't ever change this value! See SBX_NORM. */
X#define SKM_EOB	  02	/* Alignment: 0 = Beg-Of-Buf, set = End-Of-Buf */
X
X/* Note on routine names:
X *	"SB_" 	User callable, deals with sbbufs (usually).
X *	"SBS_"	User callable, deals with sbstrings only.
X *	"SBX_"	Internal routine, not meant for external use.
X *	"SBM_"	Routine handling mem alloc, usually user callable.
X */
X
X/* SBBUF Opening, Closing, Mode setting */
X
X/* SB_OPEN(sb,sd) - Sets up SBBUF given pointer to first SD of a sbstring.
X * 	If SD == 0 then creates null sbstring.
X *	Any previous contents of SBBUF are totally ignored!!!  If you
X *		want to save the stuff, use SB_UNSET.
X *	Sets I/O ptr to start of sbstring.
X *	Returns 0 if error, else the given SB.
X */
XSBBUF *
Xsb_open(sbp,sdp)
XSBBUF *sbp;
XSBSTR *sdp;
X{	register struct sdblk *sd;
X	register int cnt;
X	register WORD *clrp;
X
X	if(!sbp) return((SBBUF *)0);
X	if((sd = sdp) == 0)
X	  {	sd = sbx_ndget();	/* Get a fresh node */
X		clrp = (WORD *) sd;	/* Clear it all */
X		cnt = rnddiv(sizeof(struct sdblk));
X		do { *clrp++ = 0; } while(--cnt);
X		sd->sdflags = SD_NID;	/* Except flags of course */
X	  }
X	else if(sd->slback)		/* Must be first thing in sbstring */
X		return((SBBUF *)0);		/* Perhaps could normalize tho */
X
X	clrp = (WORD *) sbp;		/* Clear sbbuffer stuff */
X	cnt = rnddiv(sizeof(SBBUF));
X	do { *clrp++ = 0; } while(--cnt);
X
X	sbp->sbcur = sd;
X	/* Note that SD_LOCK need not be set, because first SDBLK has no
X	 * backptr.  This is desirable to allow storage compactor maximum
X	 * freedom in merging sdblks.
X	 */
X	/*	sd->sdflags |= SD_LOCK; */	/* Lock this one */
X	return(sbp);
X}
X
X
X/* SB_CLOSE(sb)	- Close a SBBUF.
X *	Returns pointer to start of sbstring (first SD).
X *	Returns 0 if error.
X */
XSBSTR *
Xsb_close(sbp)
XSBBUF *sbp;
X{	register SBBUF *sb;
X	register struct sdblk *sd;
X
X	if((sb = sbp) == 0)	/* Verify pointer */
X		return((SBSTR *)0);
X	sb_rewind(sb);		/* Do most of the work, including unlock */
X	sd = sb->sbcur;		/* Save ptr to sbstring */
X	sb->sbcur = 0;		/* Now reset the sbbuffer structure */
X	sb->sbflags = 0;
X	return(sd);
X}
X
X
X/* SB_SETOVW(sbp) - Set SBBUF Over-write mode for PUTC's.
X * SB_CLROVW(sbp) - Clear ditto.
X */
Xsb_setovw(sbp)
XSBBUF *sbp;
X{	register SBBUF *sb;
X	if(sb=sbp)
X	  {	sb->sbflags |= SB_OVW;
X		sb->sbwleft = 0;
X	  }
X}
X
Xsb_clrovw(sbp)
XSBBUF *sbp;
X{	register SBBUF *sb;
X	if(sb=sbp) sb->sbflags &= ~SB_OVW;
X}
X
X/* SBSTRING file system operations (see also sb_fsave) */
X
X/* SB_FDUSE(fd) - Make a sbstring for given file.
X * 	FD is an open file descriptor.
X *	Returns pointer to a SBSTR containing the given file, or 0 if error.
X *	The FD must not be closed until a SB_FDCLS is done to
X *	purge memory of any blocks pointing at the file.
X * ** Maybe allocate sbfile structs with sbx_ndget, i.e. overlay on
X * ** top of sdblk node??  Wd this screw verify, GC, etc? Maybe not if
X * ** SD_LCK2 set...
X */
X
Xstruct sbfile *sbv_ftab[SB_NFILES];
X
Xchroff
Xsbx_fdlen(fd)
Xint fd;
X{
X#if !V6
X	struct stat statb;
X#else
X	struct statb statb;
X	chroff len;
X	struct {int hiwd ; int lowd;} foo;
X#endif /*V6*/
X
X	if(fstat(fd,&statb) < 0) return((chroff)-1);
X#if V6
X	len = statb.i_size1;
X	len.hiwd = statb.i_size0 & 0377;
X	return(len);
X#else
X	return((chroff)statb.st_size);
X#endif /*-V6*/
X}
X
XSBSTR *
Xsb_fduse(ifd)
Xint ifd;
X{	register struct sdblk *sd;
X	register struct sbfile *sf;
X	register int fd;
X	chroff len;
X
X	if((fd = ifd) < 0 || SB_NFILES <= fd	/* Check for absurd FD */
X	  || sbv_ftab[fd])			/* and slot already in use */
X		return((SBSTR *)0);
X	if((len = sbx_fdlen(fd)) < 0) return((SBSTR *)0);
X	sbv_ftab[fd]= sf = (struct sbfile *)sbx_malloc(sizeof(struct sbfile));
X	sf->sffd = fd;
X	sf->sfptr1 = sd = sbx_ndget();
X	sf->sflen = len;
X	sd->slforw = 0;
X	sd->slback = 0;
X	sd->sdforw = 0;
X	sd->sdback = 0;
X	sd->sdmem = 0;
X	sd->sdfile = sf;
X	sd->sdlen = len;
X	sd->sdaddr = 0;
X	return(sd);
X}
X
X/* SB_FDCLS(fd) - Close a file descriptor being used by sbstrings.
X *	If arg is -1, closes all FD's that are unused (a "sweep").
X *	For specific arg, returns 0 if couldn't close FD because still in use.
X *	Perhaps later version of routine could have option to copy
X *	still-used SD's to tempfile, and force the FD closed?
X */
Xsb_fdcls(ifd)
Xint ifd;
X{	register int fd;
X
X	if((fd = ifd) >= 0)
X	  {	if(fd >= SB_NFILES) return(0);	/* Error of sorts */
X		return(sbx_fcls(sbv_ftab[fd]));
X	  }
X	fd = SB_NFILES-1;
X	do {
X		sbx_fcls(sbv_ftab[fd]);
X	  } while(--fd);	/* Doesn't try FD 0 ! */
X	return(1);
X}
X
Xsbx_fcls(sfp)
Xstruct sbfile *sfp;
X{	register struct sbfile *sf;
X	register int fd;
X
X	if((sf = sfp)==0		/* Ignore null args */
X	  || sf == &sbv_tf)		/* and never close our tempfile! */
X		return(0);
X	fd = sf->sffd;			/* Find sys file descriptor */
X	if(sbv_ftab[fd] != sf)		/* Ensure consistency */
X		return(sbx_err(0,"SF table inconsistency"));
X	if(sf->sfptr1)			/* Any phys list still exists? */
X		return(0);		/* Yes, still in use, can't close */
X	close(fd);			/* Maybe do this when list gone? */
X	sbv_ftab[fd] = 0;		/* Remove from table */
X	free(sf);			/* Remove sbfile struct from mem */
X}
X
X/* SB_FDINP(sb,fd) - Returns TRUE if specified fd is still in use
X *	by specified sbbuffer.
X */
Xsb_fdinp(sb, fd)
Xregister SBBUF *sb;
Xint fd;
X{	register struct sdblk *sd;
X	register struct sbfile *sf;
X
X	if((sf = sbv_ftab[fd]) == 0
X	  || (sd = sb->sbcur) == 0)
X		return(0);
X	sd = sbx_beg(sd);		/* Move to beginning of sbstring */
X	for(; sd; sd = sd->slforw)	/* Scan thru all blocks in string */
X		if(sd->sdfile == sf)	/* If any of them match, */
X			return(1);	/* Return tally-ho */
X	return(0); 
X}
X
X/* SB_FSAVE(sb,fd) - Write entire SBBUF out to specified FD.
X *	Returns 0 if successful, else system call error number.
X */
Xsb_fsave(sb,fd)		/* Write all of given sbbuf to given fd */
Xregister SBBUF *sb;
Xint fd;
X{
X	sbx_smdisc(sb);
X	return(sbx_aout(sbx_beg(sb->sbcur), 2, fd));
X}
X
X/* SBBUF Character Operations */
X
X/* SB_GETC(sb) - Get next char from sbstring.
X *	Returns char at current location and advances I/O ptr.
X *	Returns EOF on error or end-of-string.
X */
Xint
Xsb_sgetc(sb)
Xregister SBBUF *sb;
X{
X	if(--(sb->sbrleft) >= 0)
X		return sb_uchartoint(*sb->sbiop++);
X
X	/* Must do hard stuff -- check ptrs, get next blk */
X	sb->sbrleft = 0;			/* Reset cnt to zero */
X	if(sb->sbcur == 0			/* Make sure sbbuffer there */
X	  || (int)sbx_ready(sb,SK_READF,0,SB_BUFSIZ) <= 0)  /* Normalize & gobble */
X		return(EOF);
X	return(sb_sgetc(sb));			/* Try again */
X}	/* Loop wd be faster, but PDL OV will catch infinite-loop bugs */
X
X
X/* SB_PUTC(sb,ch) - Put char into sbstring.
X *	Inserts char at current location.
X *	Returns EOF on error, else the char value.
X */
Xint
Xsb_sputc(sb,ch)
Xregister SBBUF *sb;
Xint ch;
X{
X	register struct sdblk *sd;
X
X	if(--(sb->sbwleft) >= 0) return(*sb->sbiop++ = ch);
X
X	sb->sbwleft = 0;		/* Reset cnt to avoid overflow */
X	if((sd = sb->sbcur) == 0)	/* Verify string is there */
X		return(EOF);		/* Could perhaps create it?? */
X	if(sb->sbflags&SB_OVW)		/* If overwriting, handle std case */
X	  {	if(sb->sbiop &&
X		  --sb->sbrleft >= 0)		/* Use this for real count */
X		  {	sd->sdflags |= SD_MOD;	/* Win, munging... */
X			return(*sb->sbiop++ = ch);
X		  }
X		/* Overwriting and hit end of this block. */
X		if((int)sbx_ready(sb,SK_READF,0,SB_BUFSIZ) > 0) /* Re-normalize */
X			return(sb_sputc(sb,ch));
X
X		/*  No blks left, fall through to insert stuff at end */
X	  }
X
X	/* Do canonical setup with heavy artillery */
X	if((int)sbx_ready(sb,SK_WRITEF,SB_SLOP,SB_BUFSIZ) <= 0)	/* Get room */
X		return(EOF);		/* Should never happen, but... */
X	sb->sbflags |= SB_WRIT;
X	sb->sbcur->sdflags |= SD_MOD;
X	return(sb_sputc(sb,ch));	/* Try again */
X}	/* Loop wd be faster, but PDL OV will catch infinite-loop bugs */
X
X
X/* SB_PEEKC(sb) - Peek at next char from sbstring.
X *	Returns char that sb_getc would next return, but without
X *	changing I/O ptr.
X *	Returns EOF on error or end-of-string.
X */
Xint
Xsb_speekc(sb)
Xregister SBBUF *sb;
X{
X	if (sb->sbrleft <= 0)			/* See if OK to read */
X	  {	if (sb_sgetc(sb) == EOF)	/* No, try hard to get next */
X			return EOF;		/* Failed, return EOF */
X		sb_backc(sb);			/* Won, back up */
X	  }
X	return sb_uchartoint(*sb->sbiop);
X}
X
X/* SB_RGETC(sb) - Get previous char from sbstring.
X *	Returns char prior to current location and backs up I/O ptr.
X *	Returns EOF on error or beginning-of-string.
X */
Xint
Xsb_rgetc(sb)
Xregister SBBUF *sb;
X{
X	register struct smblk *sm;
X	register struct sdblk *sd;
X
X	if((sd=sb->sbcur) && (sm = sd->sdmem)
X	  && sb->sbiop > sm->smaddr)
X	  {	if(sb->sbflags&SB_WRIT)
X		  {	sm->smuse = sb->sbiop - sm->smaddr;
X			sb->sbwleft = 0;
X			sb->sbflags &= ~SB_WRIT;
X		  }
X		sb->sbrleft++;
X		return sb_uchartoint(*--sb->sbiop);	/* Return char */ 
X	  }
X	if((int)sbx_ready(sb,SK_READB,SB_BUFSIZ,0) <= 0)
X		return(EOF);
X	return(sb_rgetc(sb));
X}
X
X/* SB_RDELC(sb) - Delete backwards one char.
X *	Returns nothing.
X */
Xsb_rdelc(sbp)
XSBBUF *sbp;
X{	register SBBUF *sb;
X	register struct sdblk *sd;
X
X	if(((sb=sbp)->sbflags&SB_WRIT)	/* Handle simple case fast */
X	  && sb->sbiop > (sd = sb->sbcur)->sdmem->smaddr)
X		  {	sb->sbwleft++;
X			sb->sbiop--;
X			sd->sdflags |= SD_MOD;
X			return;
X		  }
X	else sb_deln(sb,(chroff) -1);	/* Else punt... */
X}
X
X/* SB_DELC(sb) - Delete one char forward? */
X/* SB_INSC(sb,ch) - Insert char?  (instead of or in addition to PUTC) */
X
X
X/* SBBUF string or N-char operations */
X
X/* SB_DELN(sb,chroff) - delete N chars.  Negative N means backwards.
X *	Differs from sb_killn in that it flushes the text forever,
X *	and doesn't return anything.
X */
X
Xsb_deln(sbp, num)
XSBBUF *sbp;
Xchroff num;
X{
X	register struct sdblk *sd;
X
X	if(sd = sb_killn(sbp,num))
X		sbs_del(sd);	/* Punt */
X}
X
X/* SB_KILLN(sb,chroff) - delete N chars, saving.  Negative N means backwards.
X *	Returns SD pointer to beginning of saved sbstring.
X */
Xstruct sdblk *
Xsb_killn(sbp, num)
XSBBUF *sbp;
Xchroff num;
X{	register SBBUF *sb;
X	register struct sdblk *sd, *sd2;
X	struct sdblk *sdr, *sdx;
X	chroff savdot;
X
X	if((sd = sbx_xcis((sb=sbp),num,&sdr,&savdot)) == 0)
X		return((struct sdblk *)0);
X
X	sb->sbcur->sdflags &= ~SD_LOCK;	/* Now can flush sbcur lock */
X
X	/* SD and SD2 now delimit bounds of stuff to excise.
X	 * First do direction dependent fixups
X	 */
X	if(num >= 0)			/* If deleting forward, */
X		sb->sbdot = savdot;	/* must reset dot to initial loc */
X
X	/* SD and SD2 now in first/last order.  Complete SBCUR fixup. */
X	sd2 = sdr;			/* sdr has ptr to end of stuff */
X	if(sd2 = sd2->slforw)		/* More stuff after killed list? */
X	  {	sb->sbcur = sd2;	/* Yes, point at it */
X		sb->sboff = 0;		/* Dot already set right */
X	  }
X	else if(sdx = sd->slback)	/* See if any prior to killed list */
X	  {	sb->sbcur = sdx;		/* Yes, point at it */
X		sb->sboff = (sdx->sdmem ?	/* Get len of prev blk */
X			sdx->sdmem->smuse : sdx->sdlen);
X		sb->sbdot -= sb->sboff;
X	  }
X	else sb_open(sb,(SBSTR *)0);	/* No stuff left!  Create null sbstring */
X
X	/* Fix up logical links.  Note SD2 points to succ of killed stuff */
X	if(sd->slback)			/* If previous exists */
X	  {	if(sd->slback->slforw = sd2)	/* Point it to succ, and */
X			sd2->slback = sd->slback; /* thence to self */
X		sd->slback = 0;			/* Now init killed list */
X	  }
X	else if(sd2) sd2->slback = 0;		/* No prev, clean rest */
X	(sd2 = sdr)->slforw = 0;		/* Finish killed list */
X
X	sb->sbcur->sdflags |= SD_LOCK;	/* Ensure current SD now locked */
X	sd->sdflags &= ~SD_LCK2;	/* And unlock killed list */
X	sd2->sdflags &= ~SD_LCK2;
X	return(sd);
X}
X
X/* SB_CPYN(sbp,num) - Copy num characters, returns SD to sbstring.
X *	Like SB_KILLN but doesn't take chars out of original sbstring.
X */
XSBSTR *
Xsb_cpyn(sbp,num)
XSBBUF *sbp;
Xchroff num;
X{	register SBBUF *sb;
X	register struct sdblk *sd, *sd2;
X	struct sdblk *sdr;
X	chroff savloc;
X
X	sb = sbp;
X	if((sd = sbx_xcis(sb,num,&sdr,&savloc)) == 0)
X		return((SBSTR *)0);
X	sd2 = sbx_scpy(sd,sdr);
X	sb_seek(sb,-num,1);		/* Return to original loc */
X	return(sd2);		/* Return val is ptr to head of copy.
X				 * It needn't be locked, because GC will
X				 * never move list heads!
X				 */
X}
X
X/* SB_SINS(sb,sd) - Insert sbstring at current location
X *
X */
Xsb_sins(sbp,sdp)
XSBBUF *sbp;
Xstruct sdblk *sdp;
X{	register SBBUF *sb;
X	register struct sdblk *sd, *sdx;
X	chroff inslen;
X
X	if((sb = sbp)==0
X	  || (sd = sdp) == 0)
X		return(0);
X	if(sd->slback)		/* Perhaps normalize to beg? */
X		return(0);
X	if((sdx = (struct sdblk *)sbx_ready(sb,SK_DELB)) == 0)	/* Get cur pos ready */
X		return(0);
X	inslen = sbs_len(sd);		/* Save length of inserted stuff */
X
X	sd->slback = sdx;		/* Fix up links */
X	if(sdx->slforw)
X	  {	while(sd->slforw)	/* Hunt for end of inserted sbstring */
X			sd = sd->slforw;
X		sd->slforw = sdx->slforw;
X		sd->slforw->slback = sd;
X	  }
X	sdx->slforw = sdp;
X	sb->sboff += inslen;		/* Set IO ptr to end of new stuff */
X	return(1);
X}
X
X/* SBSTRING routines - operate on "bare" sbstrings. */
X
X/* SBS_CPY(sd) - Copies given sbstring, returns ptr to new sbstring.
X */
XSBSTR *
Xsbs_cpy(sdp)
XSBSTR *sdp;
X{	return(sbx_scpy(sdp,(struct sdblk *)0));
X}
X
X/* SBS_DEL(sd) - Flush a sbstring.
X */
Xsbs_del(sdp)
XSBSTR *sdp;
X{	register struct sdblk *sd;
X
X	if(sd = sdp)
X		while(sd = sbx_ndel(sd));
X}
X
X
X/* SBS_APP(sd1,sd2) - Appends sbstring sd2 at end of sbstring sd1.
X *	Returns sd1 (pointer to new sbstring).
X */
X
XSBSTR *
Xsbs_app(sdp,sdp2)
Xstruct sdblk *sdp,*sdp2;
X{	register struct sdblk *sd, *sdx;
X
X	if(sd = sdp)
X	  {	while(sdx = sd->slforw)
X			sd = sdx;
X		if(sd->slforw = sdx = sdp2)
X			sdx->slback = sd;
X	  }
X	return(sdp);
X}
X
X/* SBS_LEN(sd) - Find length of sbstring.
X */
Xchroff
Xsbs_len(sdp)
XSBSTR *sdp;
X{	register struct sdblk *sd;
X	register struct smblk *sm;
X	chroff len;
X
X	if((sd = sdp)==0) return((chroff)0);
X	len = 0;
X	for(; sd ; sd = sd->slforw)
X	  {	if(sm = sd->sdmem)
X			len += (chroff)sm->smuse;
X		else len += sd->sdlen;
X	  }
X	return(len);
X}
X
X/* SBBUF I/O pointer ("dot") routines */
X
X/* SB_SEEK(sb,chroff,flag) - Like FSEEK.  Changes I/O ptr value as
X *	indicated by "flag":
X * 		0 - offset from beg
X *		1 - offset from current pos
X *		2 - offset from EOF
X *	Returns -1 on errors.
X *	Seeking beyond beginning or end of sbbuf will leave pointer
X *	at the beginning or end respectively.
X *	Returns 0 unless error (then returns -1).
X */
Xsb_seek(sbp, coff, flg)
XSBBUF *sbp;
Xchroff coff;
Xint flg;
X{	register SBBUF *sb;
X	register struct smblk *sm;
X	register struct sdblk *sd;
X	SBMO moff;
X
X	sb = sbp;
X	if((sd = sb->sbcur) == 0) return(-1);
X	if(sb->sbiop == 0)
X	  {	switch(flg)
X		  {	case 0:	if(coff == 0)	/* Optimize common case */
X					return(sb_rewind(sb));
X				sb->sboff = coff - sb->sbdot;	/* Abs */
X				break;
X			case 1:	sb->sboff += coff;		/* Rel */
X				break;
X			case 2:	sb->sboff += sb_ztell(sb) + coff;
X				break;
X			default: return(-1);
X		  }
X		sbx_norm(sb,0);
X		return(0);
X	  }
X	if((sm = sd->sdmem) == 0)
X		return(sbx_err(-1,"SDMEM 0"));
X	moff = sb->sbiop - sm->smaddr;	/* Get cur smblk offset */
X	if(sb->sbflags&SB_WRIT)		/* Update since moving out */
X	  {	sm->smuse = moff;
X		sb->sbflags &= ~SB_WRIT;
X	  }
X	sb->sbwleft = 0;		/* Always gets zapped */
X	switch(flg)
X	  {	case 0:		/* Offset from beginning */
X			coff -= sb->sbdot + (chroff)moff; /* Make rel */
X
X		case 1:		/* Offset from current loc */
X			break;
X
X		case 2:		/* Offset from end */
X			coff += sb_ztell(sb);
X			break;
X		default: return(-1);
X	  }
X
X	/* COFF now has relative offset from current location */
X	if (-(chroff)moff <= coff && coff <= sb->sbrleft)
X	  {				/* Win! Handle repos-within-smblk */
X		sb->sbiop += coff;
X		sb->sbrleft -= coff;	/* Set r; wleft already 0 */
X		return(0);
X	  }
X
X	/* Come here when moving to a different sdblk. */
X	sb->sbrleft = 0;
X	sb->sbiop = 0;
X	sb->sboff = coff + (chroff)moff;
X	sbx_norm(sb,0);
X	return(0);
X}
X
X/* SB_REWIND(sb) - Go to beginning of sbbuffer.
X *	Much faster than using sb_seek.  Note that this leaves the sbbuffer
X *	in an open/idle state which is maximally easy to compact.
X */
Xsb_rewind(sbp)
XSBBUF *sbp;
X{	register SBBUF *sb;
X	register struct sdblk *sd;
X
X	if((sb = sbp)==0) return;
X	sbx_smdisc(sb);			/* Ensure I/O disconnected */
X	(sd = sb->sbcur)->sdflags &= ~SD_LOCK;	/* Unlock current blk */
X	sd = sbx_beg(sd);		/* Move to beg of sbstring */
X	/* Need not lock - see sb_open comments, also sb_close */
X	/*	sd->sdflags |= SD_LOCK; */	/* Lock onto this one */
X	sb->sbcur = sd;
X	sb->sbdot = 0;
X	sb->sboff = 0;
X}
X
X/* SB_TELL(sb) - Get I/O ptr value for SBBUF.
X *	Returns -1 on errors.
X */
X
Xchroff
Xsb_tell(sbp)
XSBBUF *sbp;
X{	register SBBUF *sb;
X	register struct smblk *sm;
X	register struct sdblk *sd;
X
X	if((sd = (sb=sbp)->sbcur) == 0)
X		return((chroff)-1);
X	if(sb->sbiop == 0)
X		return(sb->sbdot + sb->sboff);
X	if((sm = sd->sdmem) == 0)
X		return(sbx_err(0,"SDMEM 0"));
X	return(sb->sbdot + (unsigned)(sb->sbiop - sm->smaddr));
X}
X
X/* SB_ZTELL(sb) - Get I/O ptr relative to "Z" (EOF).
X *	Returns # chars from current location to EOF; 0 if any errors.
X */
Xchroff
Xsb_ztell(sbp)
XSBBUF *sbp;
X{	register SBBUF *sb;
X	register struct smblk *sm;
X	register struct sdblk *sd;
X
X	if((sd = (sb=sbp)->sbcur) == 0)
X		return((chroff)0);
X	if(sb->sbiop && (sm = sd->sdmem))
X	  {	if(sb->sbflags&SB_WRIT)		/* If actively writing, */
X			return(sbs_len(sd->slforw));	/* ignore this blk. */
X			/* Note that previous code makes it unnecessary
X			 * to invoke sbx_smdisc.  (otherwise wrong
X			 * smuse would confuse sbs_len).
X			 */
X		return(sbs_len(sd) - (sb->sbiop - sm->smaddr));
X	  }
X	else
X		return(sbs_len(sd) - sb->sboff);
X}
X
X/* Code past this point should insofar as possible be INTERNAL. */
X
X/* SBX_READY(sb,type,cmin,cmax) - Set up SBBUF for reading or writing.
X *
X * If no current smblk:
X *	reading - set up for reading
X *	writing - set up for splitting?
X * If current smblk:
X *	reading - if can read, OK.  Else position at beg of next sdblk
X *	writing - if can write, OK.  Else position at end of prev sdblk,
X *		or set up for splitting?
X * Types:
X *	0 - Read forward (BOB)
X *	1 - Read backward (EOB)
X *	3 - Write (insert forward) (EOB)
X *	4 - Delete forward (return SD, force BOB-aligned)
X *	5 - Delete backward (return SD, force EOB-aligned)
X * Connected SD is always locked.
X * Returns 0 if error, -1 if EOF-type error, 1 for success.
X *
X * For types 0,1:
X *	CMIN,CMAX represent max # chars to read in to left and right of
X *		I/O ptr (prev and post).  Actual amount read in may be
X *		much less, but will never be zero.
X *	Successful return guarantees that SBIOP etc. are ready.
X * For type 3:
X *	If new block is allocated, CMIN and CMAX represent min, max sizes
X *		of the block.
X *	Successful return guarantees that SBIOP etc. are ready, but
X *	NOTE that SB_WRIT and SD_MOD are not set!  If not going to use
X *	for writing, be sure to clear sbwleft on return!
X * For types 4,5:
X *	CMIN, CMAX are ignored.
X *	SBIOP is always cleared.  SBOFF is guaranteed to be 0 for
X *	type 4, SMUSE for type 5.
X *	Return value is a SD ptr; 0 indicates error.  -1 isn't used.
X */
X
Xstruct sdblk *
Xsbx_ready(sbp,type,cmin,cmax)
XSBBUF *sbp;
Xint type;
XSBMO cmin,cmax;
X{	register SBBUF *sb;
X	register struct sdblk *sd;
X	register struct smblk *sm;
X	int cnt, slop, rem;
X	SBMO moff;
X
X	if((sd = (sb=sbp)->sbcur) == 0)
X		return(0);
X	if(sb->sbiop)		/* Canonicalize for given operation */
X	  {	if((sm = sd->sdmem)==0)
X			return(0);
X		moff = sb->sbiop - sm->smaddr;	/* Current block offset */
X	  switch(type)
X	  {
X	case SK_READF:		/* Read Forward */
X		if(sb->sbrleft > 0)	/* Already set up? */
X			return(1);	/* Yup, fast return */
X		sbx_smdisc(sb);		/* None left, disc to get next */
X		if((sd = sbx_next(sb)) == 0)	/* Try to get next blk */
X			return(-1);	/* At EOF */
X		break;
X
X	case SK_READB:		/* Read Backward */
X		if(moff)		/* Stuff there to read? */
X		  {	if(sb->sbflags&SB_WRIT)	/* Yup, turn writes off */
X			  {	sm->smuse = moff;
X				sb->sbflags &= ~SB_WRIT;
X			  }
X			sb->sbwleft = 0;
X			return(1);
X		  }
X		sbx_smdisc(sb);
X		break;
X
X	case SK_WRITEF:		/* Writing */
X		if(sb->sbrleft <= 0)
X			sb->sbwleft = sm->smlen - moff;
X		if(sb->sbwleft > 0)
X			return(1);	/* OK to write now */
X					/* NOTE: flags not set!!! */
X		sbx_smdisc(sb);
X		break;
X
X	case SK_DELF:		/* Delete forward - force BOB */
X		if(sb->sbrleft <= 0)		/* At end of blk? */
X		  {	sbx_smdisc(sb);		/* Win, unhook */
X			return(sbx_next(sb));   /* Return next or 0 if EOF */
X		  }
X		sbx_smdisc(sb);			/* Not at end, but see if */
X		if(moff == 0)			/* at beg of blk? */
X			return(sd);	/* Fast win! */
X		break;
X
X	case SK_DELB:		/* Delete backward - force EOB */
X		if(sb->sbrleft <= 0)		/* Win if already EOB */
X		  {	sbx_smdisc(sb);
X			return(sd);
X		  }
X		sbx_smdisc(sb);
X		break;
X
X	default:
X		return(0);
X	  }
X	  }
X
X	/* Schnarf in the text, or whatever.
X	 * SD points to current sdblk (must be SD_LOCKed)
X	 * SBDOT must have correct value for this SD
X	 * SBOFF has offset from there to put I/O ptr at.
X	 *
X	 * After normalization, SBOFF is guaranteed to point within
X	 * the SD.  Other guarantees apply to boundary cases, depending
X	 * on the mode (type) bits.
X	 */
X	sd = sbx_norm(sb,type);	/* Normalize I/O pos appropriately */
X	sm = sd->sdmem;
X	switch(type)
X	  {
X	case SK_READB:		/* Read Backward */
X		if(sb->sboff == 0)	/* Due to normalize, if 0 seen */
X			return(-1);	/* then we know it's BOF */
X		if(sm) goto sekr2;
X		else goto sekr1;
X
X	case SK_READF:		/* Read Forward */
X		if(sm) goto sekr2;
X		if(sb->sboff == sd->sdlen)	/* Normalize means if EOB */
X			return(-1);		/* then at EOF. */
X	sekr1:	slop = SB_SLOP;
X	sekr3:	if(sb->sboff > cmin+slop)	/* Too much leading text? */
X		  {				/* Split off leading txt */
X			sbx_split(sd,(chroff)(sb->sboff - cmin));
X			sd = sbx_next(sb);	/* Point to next sdblk */
X			sb->sboff = cmin;	/* Set correct offset */
X						/* (sbx_next assumes 0) */
X		  }
X		if(sd->sdlen > sb->sboff+cmax+slop) /* Too much trailing txt? */
X			sbx_split(sd,(chroff)(sb->sboff+cmax));
X
X		/* ----- Try to get mem blk to read stuff into ----- */
X		/* Note alignment hack for extra efficiency.  This ensures
X		 * that all reads from disk to memory are made with the same
X		 * source and destination word alignment, so the system kernel
X		 * only needs byte-moves for the first or last bytes; all
X		 * others can be word-moves.
X		 * This works because sbx_mget always returns word-aligned
X		 * storage, and we use sbx_msplit to trim off the right number
X		 * of bytes from the start.
X		 */
X		cnt = sd->sdlen;		/* Get # bytes we'd like */
X		if(rem = rndrem(sd->sdaddr))	/* If disk not word-aligned */
X			cnt += rem;		/* allow extra for aligning.*/
X		if(sm == 0)			/* Always true 1st time */
X		  {	sm = sbx_mget(SB_SLOP,cnt); /* Get room (may GC!)*/
X			if(sm->smlen < cnt)	/* Got what we wanted? */
X			  {	slop = 0;	/* NO!!	 Impose stricter */
X				cmin = 0;	/* limits.  Allow for new */
X				cmax = sm->smlen - (WDSIZE-1); /* rem. */
X				if(type == SK_READB)
X				  {	cmin = cmax; cmax = 0; }
X				goto sekr3;	/* Go try again, sigh. */
X			  }
X		  }
X		else if(sm->smlen < cnt)	/* 2nd time shd always win */
X		  {	sbx_err(0,"Readin blksiz err");	/* Internal error, */
X			if((cmax /= 2) > 0) goto sekr3;	/* w/crude recovery */
X			return(0);
X		  }
X		if(rem)		/* If disk not word-aligned, hack stuff */
X		  {	sm = sbx_msplit(sm, (SBMO)rem);	/* Trim off from beg*/
X			sbm_mfree(sm->smback);		/* Free the excess */
X		  }
X		sd->sdmem = sm;
X		sm->smuse = sd->sdlen;
X
X		if(sd->sdfile == 0)
X			return(sbx_err(0,"No file"));	/* Gasp? */
X		if(!sbx_rdf(sd->sdfile->sffd, sm->smaddr, sm->smuse,
X				1, sd->sdaddr))
X			return(sbx_err(0,"Readin SD: %o", sd));
X		/* ------- */
X
X	sekr2:	sbx_sbrdy(sb);		/* Make it current, pt to beg */
X		sb->sbwleft = 0;	/* Ensure not set (esp if READB) */
X		break;
X
X	case SK_WRITEF:		/* Write-type seek */
X		if(sm == 0)
X		  {	/* Block is on disk, so always split (avoid readin) */
X			if(sd->sdlen)			/* May be empty */
X			  {	sbx_split(sd, sb->sboff); /* Split at IO ptr */
X				sd = sbx_next(sb);	/* Move to 2nd part */
X				if(sd->sdlen)		/* If stuff there, */
X							/* split it again. */
X					sbx_split(sd, (chroff) 0);
X			  }
X			goto sekwget;
X		  }
X
X		/* Block in memory */
X		moff = sm->smuse;
X		if(sb->sboff == moff)		/* At end of the block? */
X		  {	if(sm->smlen > moff)	/* Yes, have room? */
X				goto sekw;	/* Win, go setup and ret */
X			if(sm->smforw			/* If next mem blk */
X			  && (sm->smforw->smflags	/* Can have bytes */
X				& (SM_USE|SM_NXM))==0	/* stolen from it */
X			  && (sd->sdflags&SD_MOD)	/* and we ain't pure*/
X			  && sm->smlen < cmax)		/* and not too big */
X			  {	/* Then steal some core!!  Note that without
X				 * the size test, a stream of putc's could
X				 * create a monster block gobbling all mem.
X				 */
X				cmin = cmax - sm->smlen;
X				if(cmin&01) cmin++;	/* Ensure wd-align */
X				if(sm->smforw->smlen <= cmin)
X				  {	sbm_mmrg(sm);
X					goto sekw;
X				  }
X				sm->smforw->smlen -= cmin;
X				sm->smforw->smaddr += cmin;
X				sm->smlen += cmin;
X				goto sekw;
X			  }
X			/* Last try... check next logical blk for room */
X			if(sd->slforw && (sm = sd->slforw->sdmem)
X			  && sm->smuse == 0
X			  && sm->smlen)
X			  {	sd = sbx_next(sb);	/* Yup, go there */
X				goto sekw;
X			  }
X		  }
X
X		/* Middle of block, split up to insert */
X		sbx_split(sd, sb->sboff);	/* Split at IO ptr */
X		if(sd->sdmem)			/* Unless blk now empty, */
X		  {	sd = sbx_next(sb);	/* move to next. */
X			if(sd->sdmem)		/* If not empty either */
X			  sbx_split(sd, (chroff) 0);	/* Split again */
X		  }
X
X		/* Have empty SD block, get some mem for it */
X  sekwget:	sd->sdmem = sm = sbx_mget(cmin,cmax);
X		sm->smuse = 0;
X   sekw:	sbx_sbrdy(sb);		/* Sets up sbwleft... */
X		return(1);
X
X	case SK_DELF:		/* Delete forward */
X		if(sb->sboff == 0)	/* At block beg already? */
X			return(sd);	/* Win, return it */
X		sbx_split(sd, sb->sboff);	/* No, split up and */
X		return(sbx_next(sb));	/* return ptr to 2nd part */
X
X	case SK_DELB:		/* Delete backward (force EOB align) */
X		if(sb->sboff !=		/* If not at EOB already, */
X		  (sm ? (chroff)(sm->smuse) : sd->sdlen))
X			sbx_split(sd, sb->sboff);	/* Then split */
X		return(sd);	/* And return ptr to 1st part */
X		break;
X
X	default:
X		return(0);
X	  }	/* End of switch */
X	return(1);
X}
X
Xstruct sdblk *
Xsbx_next (sbp)
XSBBUF *sbp;
X{	register SBBUF *sb;
X	register struct sdblk *sd, *sdf;
X	if((sdf = (sd = (sb=sbp)->sbcur)->slforw) == 0)
X		return((struct sdblk *)0);
X	sb->sbdot += (sd->sdmem ? (chroff)sd->sdmem->smuse : sd->sdlen);
X	sb->sboff = 0;
X	sd->sdflags &= ~SD_LOCK;	/* Unlock current */
X	sdf->sdflags |= SD_LOCK;	/* Lock next */
X	sb->sbcur = sdf;
X	return(sdf);
X}
X
X/* SBX_NORM(sb,mode) - Normalizes I/O position as desired.
X *	The SBBUF must have I/O disconnected (SBIOP==0).
X *	Adjusts SBCUR, SBDOT, and SBOFF so that SBOFF is guaranteed
X *	to point to a location in the current SD block.
X *	The mode flags determine action when there is more than
X *	one possible SD that could be pointed to, as is the case
X *	when the I/O pos falls on a block boundary (possibly with
X *	adjacent zero-length blocks as well).
X *	SKM_0BACK - Zero-skip direction.
X *		  0 = Skip forward over zero-length blocks.
X *		set = Skip backward over zero-length blocks.
X *	SKM_EOB	  - Block-end selection (applies after skipping done).
X *		  0 = Point to BOB (Beginning Of Block).
X *		set = Point to EOB (End Of Block).
X * Returns the new current SD as a convenience.
X * Notes:
X *	The SKM_0BACK flag value is a special hack to search in
X *		the right direction when SBOFF is initially 0.
X *	None of the mode flags have any effect if the I/O pos falls
X *		within a block.
X *	Perhaps this routine should flush the zero-length blks it
X *		finds, if they're not locked??
X */
Xstruct sdblk *
Xsbx_norm(sbp,mode)
XSBBUF *sbp;
Xint mode;
X{	register struct sdblk *sd;
X	register struct smblk *sm;
X	register SBBUF *sb;
X	chroff len;
X
X	if((sd = (sb=sbp)->sbcur) == 0)
X	  {	sb->sbdot = 0;
X		sb->sboff = 0;
X		return(sd);
X	  }
X	sd->sdflags &= ~SD_LOCK;	/* Unlock current blk */
X
X	if(sb->sboff >= (mode&01))	/* Hack hack to get right skip */
X	  for(;;)			/* Scan forwards */
X	  {	if(sm = sd->sdmem)		/* Get length of this blk */
X			len = sm->smuse;
X		else len = sd->sdlen;
X		if(sb->sboff <= len)
X		  if(sb->sboff < len	/* If == and fwd 0-skip, continue */
X		  || (mode&SKM_0BACK))
X		    {	if((mode&SKM_EOB)	/* Done, adjust to EOB? */
X			  && sb->sboff == 0	/* Yes, are we at BOB? */
X			  && sd->slback)	/* and can do it? */
X			  {	sd = sd->slback;	/* Move to EOB */
X				sb->sboff = (sm = sd->sdmem) 
X					? (chroff)(sm->smuse) : sd->sdlen;
X				sb->sbdot -= sb->sboff;
X			  }
X			break;
X		    }
X		if(sd->slforw == 0)	/* At EOF? */
X		  {	sb->sboff = len;
X			break;
X		  }
X		sd = sd->slforw;
X		sb->sboff -= len;
X		sb->sbdot += len;
X	  }
X	else				/* Scan backwards */
X	 for(;;)
X	  {	if(sd->slback == 0)	/* At BOF? */
X		  {	sb->sboff = 0;
X			sb->sbdot = 0;	/* Should already be 0, but... */
X			break;
X		  }
X		sd = sd->slback;
X		if(sm = sd->sdmem)		/* Get length of this blk */
X			len = sm->smuse;
X		else len = sd->sdlen;
X		sb->sbdot -= len;
X		if((sb->sboff += len) >= 0)
X		  if(sb->sboff > 0	/* If == 0 and bkwd 0-skip, continue */
X		    || !(mode&SKM_0BACK))
X		    {	if((mode&SKM_EOB) == 0	/* Done, adjust to BOB? */
X			  && sb->sboff == len	/* Yes, are we at EOB? */
X			  && sd->slforw)	/* and can do it? */
X			  {	sd = sd->slforw;	/* Move to BOB */
X				sb->sboff = 0;
X				sb->sbdot += len;
X			  }
X			break;
X		    }
X	  }
X	sb->sbcur = sd;
X	sd->sdflags |= SD_LOCK;
X	return(sd);
X}
X
X
Xstruct sdblk *
Xsbx_beg(sdp)
Xstruct sdblk *sdp;
X{	register struct sdblk *sd, *sdx;
X	if(sd = sdp)
X		while(sdx = sd->slback)
X			sd = sdx;
X	return(sd);
X}
X
X
Xsbx_smdisc(sbp)
XSBBUF *sbp;
X{	register SBBUF *sb;
X	register struct smblk *sm;
X	register struct sdblk *sd;
X
X	sb = sbp;
X	if((sd = sb->sbcur) == 0
X	  || (sm = sd->sdmem) == 0)
X		return;
X	if(sb->sbflags&SB_WRIT)
X	  {	sm->smuse = sb->sbiop - sm->smaddr;
X		sb->sbflags &= ~SB_WRIT;
X	  }
X	sb->sboff = sb->sbiop - sm->smaddr;
X	sb->sbiop = 0;
X	sb->sbrleft = sb->sbwleft = 0;
X}
X
Xsbx_sbrdy(sbp)		/* Sets up SBIOP, SBRLEFT, SBWLEFT */
XSBBUF *sbp;
X{	register SBBUF *sb;
X	register struct sdblk *sd;
X	register struct smblk *sm;
X
X	if((sd = (sb=sbp)->sbcur) == 0
X	  || (sm = sd->sdmem) == 0)
X		return;
X	sd->sdflags |= SD_LOCK;
X	sb->sbiop = sm->smaddr + sb->sboff;
X	if(sb->sbrleft = sm->smuse - sb->sboff)
X		sb->sbwleft = 0;
X	else sb->sbwleft = sm->smlen - sm->smuse;
X}
X
X
X/* SBX_SCPY(sd,sdl) - Copies given sbstring, returns ptr to new sbstring.
X *	Only goes as far as sdl (last copied blk); 0 for entire sbstring.
X */
Xstruct sdblk *
Xsbx_scpy(sdp,sdlast)
Xstruct sdblk *sdp, *sdlast;
X{	register struct sdblk *sd, *sd2, *sdn;
X	struct sdblk *sdr;
X
X	if((sd = sdp) == 0) return((struct sdblk *)0);
X	sdn = 0;
X	do {
X		sd->sdflags |= SD_LCK2;
X		sd2 = sbx_sdcpy(sd);
X		if(sd2->slback = sdn)
X		  {	sdn->slforw = sd2;
X			sdn->sdflags &= ~SD_LOCKS;
X		  }
X		else sdr = sd2;		/* Save 1st */
X		sdn = sd2;
X		sd->sdflags &= ~SD_LCK2;
X	  } while(sd != sdlast && (sd = sd->slforw));
X	sd2->slforw = 0;
X	sd2->sdflags &= ~SD_LOCKS;
X	return(sdr);
X}
X
X
X/* SBX_SDCPY(sd) - Copies given sdblk, returns ptr to new blk.
X *	Does not set locks, assumes caller does this (which it MUST,
X *	to avoid compaction lossage!)
X */
X
Xstruct sdblk *
Xsbx_sdcpy(sdp)
Xstruct sdblk *sdp;
X{	register struct sdblk *sd, *sd2;
X	register struct smblk *sm, *sm2;
X
X	if((sd = sdp) == 0) return((struct sdblk *)0);
X	sd2 = sbx_ndget();		/* Get a free sdblk */
X	bcopy((SBMA)sd, (SBMA)sd2, sizeof(struct sdblk));	/* Copy sdblk data */
X	sd2->slforw = 0;		/* Don't let it think it's on a list */
X	sd2->slback = 0;
X	if(sd2->sdfile)			/* If has disk copy, */
X	  {	sd->sdforw = sd2;	/* Fix phys list ptrs */
X		sd2->sdback = sd;
X		if(sd2->sdforw)
X			sd2->sdforw->sdback = sd2;
X	  }
X	if(sm = sd2->sdmem)		/* If has in-core copy, try to */
X	  {	if(sm2 = sbm_mget(sm->smuse,sm->smuse))	/* get mem for it */
X		  {	bcopy(sm->smaddr,sm2->smaddr,sm->smuse);
X			sm2->smuse = sm->smuse;
X			sd2->sdmem = sm2;	/* Point new sd to copy */
X		  }
X		else				/* Can't get mem... */
X		  {	if(sd2->sdflags&SD_MOD)
X				sbx_aout(sd2,1);	/* Swap out the blk */
X			sd2->sdmem = 0;		/* Don't have incore copy */
X		  }
X	  }
X	return(sd2);
X}
X
X/* SBX_XCIS(sbp,coff,&sdp2,adot) - Internal routine to excise a sbstring,
X *	defined as everything between current location and given offset.
X *	SD to first sdblk is returned (0 if error)
X *	SD2 (address passed as 3rd arg) is set to last sdblk.
X *	Both are locked with LCK2 to ensure that pointers are valid.
X *	The current location at time of call is also returned via adot.
X */
Xstruct sdblk *
Xsbx_xcis(sbp,num,asd2,adot)
XSBBUF *sbp;
Xchroff num, *adot;
Xstruct sdblk **asd2;
X{	register SBBUF *sb;
X	register struct sdblk *sd, *sd2;
X	int dirb;
X
X	if((sb = sbp) == 0) return((struct sdblk *)0);
X	dirb = 0;		/* Delete forward */
X	if(num == 0) return((struct sdblk *)0);	/* Delete nothing */
X	if(num < 0) dirb++;	/* Delete backward */
X
X	if((sd = (struct sdblk *)
X			sbx_ready(sb, (dirb ? SK_DELB : SK_DELF))) == 0)
X		return((struct sdblk *)0);		/* Maybe nothing there */
X	sd->sdflags |= SD_LCK2;		/* Lock up returned SD */
X	*adot = sb->sbdot;		/* Save current location */
X	sb->sboff += num;		/* Move to other end of range */
X
X	if((sd2 = (struct sdblk *)
X			sbx_ready(sb,(dirb ? SK_DELF : SK_DELB))) == 0)
X	  {	sd->sdflags &= ~SD_LCK2;	/* This shd never happen if */
X		return(				/* we got this far, but...  */
X		  (struct sdblk *)sbx_err(0,"KILLN SD2 failed"));
X	  }
X	sd2->sdflags |= SD_LCK2;	/* Lock up other end of stuff */
X
X	/* SD and SD2 now delimit bounds of stuff to excise.
X	 * Now do direction dependent fixups
X	 */
X	if(dirb)
X	  {	/* Backward, current sbdot is ok but must get SD/SD2
X		 * into first/last order.  Also, due to nature of block
X		 * splitups, a backward delete within single block will leave
X		 * SD actually pointing at predecessor block.
X		 */
X		if(sd->slforw == sd2)	/* If SD became pred, fix things. */
X		  {	sd->sdflags &= ~SD_LCK2;	/* Oops, unlock! */
X			sd = sd2;
X		  }
X		else	/* Just need to swap SD, SD2 ptrs. */
X		  {	/* Goddamit why doesn't C have an */
X			/* exchange operator??? */
X			*asd2 = sd;
X			return(sd2);
X		  }
X	  }
X	*asd2 = sd2;
X	return(sd);
X}
X
X/* SBX_SPLIT(sd,chroff) - Splits block SD at point CHROFF (offset from
X *	start of block).  SD remains valid; it is left locked.
X *	The smblk is split too, if one exists, and SMUSE adjusted.
X *	If offset 0, or equal to block length, the 1st or 2nd SD respectively
X *	will not have a smblk and its sdlen will be 0.
X *	(Note that if a smblk exists, a zero sdlen doesn't indicate much)
X */
Xstruct sdblk *
Xsbx_split(sdp, coff)
Xstruct sdblk *sdp;
Xchroff coff;
X{	register struct sdblk *sd, *sdf, *sdx;
X
X	if((sd=sdp) == 0)
X		return((struct sdblk *)0);
X	sd->sdflags |= SD_LOCK;
X	if(sd->sdflags&SD_MOD)		/* If block has been munged, */
X		sbx_npdel(sd);		/* Flush from phys list now. */
X	sdf = sbx_ndget();		/* Get a sdblk node */
X	bcopy((SBMA)sd, (SBMA)sdf, (sizeof (struct sdblk)));	/* Copy node */
X	/* Note that the flags are copied, so both sdblks are locked and
X	 * safe from possible GC compaction during call to sbx_msplit...
X	 */
X	if(coff == 0)			/* If offset was 0, */
X	  {				/* then 1st SD becomes null */
X		if(sdf->sdfile)		/* Fix up phys links here */
X		  {	if(sdx = sdf->sdback)
X				sdx->sdforw = sdf;
X			else sdf->sdfile->sfptr1 = sdf;
X			if(sdx = sdf->sdforw)
X				sdx->sdback = sdf;
X		  }
X		sdx = sd;
X		goto nulsdx;
X	  }
X	else if(sd->sdmem)
X		if(coff >= sd->sdmem->smuse)
X			goto nulsdf;
X		else sdf->sdmem = sbx_msplit(sd->sdmem, (SBMO)coff);
X	else if(coff >= sd->sdlen)
Xnulsdf:	  {	sdx = sdf;
Xnulsdx:		sdx->sdforw = 0;
X		sdx->sdback = 0;
X		sdx->sdmem = 0;
X		sdx->sdfile = 0;
X		sdx->sdlen = 0;
X		sdx->sdaddr = 0;
X		goto nulskp;
X	  }
X	if(sd->sdfile)
X	  {	sdf->sdlen -= coff;		/* Set size of remainder */
X		sdf->sdaddr += coff;		/* and address */
X		sd->sdlen = coff;		/* Set size of 1st part */
X
X	/* Link 2nd block into proper place in physical sequence.
X	 * 1st block is already in right place.	 Search forward until
X	 * find a block with same or higher disk address, and insert
X	 * in front of it.  If sdlen is zero, just flush the links,
X	 * which is OK since the 1st block is what's pointed to anyway.
X	 */
X		if(sdf->sdlen > 0)
X		  {	while((sdx = sd->sdforw) /* Find place to insert */
X			  && sdf->sdaddr > sdx->sdaddr)
X				sd = sdx;
X			sdf->sdback = sd;	/* Link following sd. */
X			if(sdf->sdforw = sd->sdforw)
X				sdf->sdforw->sdback = sdf;
X			sd->sdforw = sdf;
X			sd = sdp;		/* Restore pointer */
X		  }
X		else
X		  {	sdf->sdforw = 0;
X			sdf->sdback = 0;
X			sdf->sdfile = 0;	/* Say no disk */
X		  }
X	  }
X
Xnulskp:	sdf->slback = sd;		/* Link in logical sequence */
X	if(sd->slforw)
X		sd->slforw->slback = sdf;
X	sd->slforw = sdf;
X
X	sdf->sdflags &= ~SD_LOCKS;	/* Unlock 2nd but not 1st */
X	return(sd);			/* Note sd, not sdf */
X}
X
X/* SBX_MSPLIT - Like sbm_split but never fails, and sets
X *	SMUSE values appropriately
X */
Xstruct smblk *
Xsbx_msplit(smp, size)
Xstruct smblk *smp;
XSBMO size;
X{	register struct smblk *sm, *smx;
X	register int lev;
X
X	lev = 0;
X	while((smx = sbm_split((sm = smp), size)) == 0)
X		sbx_comp(SB_BUFSIZ,lev++); /* Need to get some smblk nodes */
X	if(sm->smlen >= sm->smuse)	/* Split across used portion? */
X		smx->smuse = 0;		/* Nope, new blk is all free */
X	else
X	  {	smx->smuse = sm->smuse - sm->smlen;
X		sm->smuse = sm->smlen;
X	  }
X	return(smx);
X}
X
X/* SBX_NDEL - flush a SD and associated SM.  Fixes up logical
X * and physical links properly.  Returns ptr to next logical SD.
X * NOTE: if sd->slback does not exist, the returned SD is your
X * only hold on the list, since the SD gets flushed anyway!
X */
Xstruct sdblk *
Xsbx_ndel(sdp)
Xstruct sdblk *sdp;
X{	register struct sdblk *sd, *sdx;
X	register struct smblk *sm;
X
X	sd = sdp;
X	if(sm = sd->sdmem)		/* If smblk exists, */
X	  {	sbm_mfree(sm);		/* flush it. */
X		sd->sdmem = 0;
X	  }
X	if(sdx = sd->slback)
X		sdx->slforw = sd->slforw;
X	if(sd->slforw)
X		sd->slforw->slback = sdx;	/* May be zero */
X
X	/* Logical links done, now hack phys links */
X	if(sd->sdfile)			/* Have phys links? */
X		sbx_npdel(sd);		/* Yes, flush from phys list */
X
X	sdx = sd->slforw;
X	sbx_ndfre(sd);
X	return(sdx);
X}
X
Xsbx_npdel(sdp)
Xstruct sdblk *sdp;
X{	register struct sdblk *sd, *sdx;
X	register struct sbfile *sf;
X
X	if((sf = (sd=sdp)->sdfile) == 0)
X		return;
X	if(sdx = sd->sdback)	/* Start of disk file? */
X		sdx->sdforw = sd->sdforw;
X	else
X		sf->sfptr1 = sd->sdforw;
X	if(sdx = sd->sdforw)
X		sdx->sdback = sd->sdback;
X	sd->sdfile = 0;
X	sd->sdlen = 0;
X}
X
X
Xstruct sdblk *sbx_nfl;	/* Pointer to sdblk node freelist */
X
Xstruct sdblk *
Xsbx_ndget()		/* Like sbm_nget but never fails! */
X{	register struct sdblk *sd;
X	register int lev;
X
X	lev = 0;
X	while((sd = sbx_nfl) == 0		/* Get a node */
X						/* If fail, make more */
X		&& (sd = sbm_nmak((sizeof (struct sdblk)),SM_DNODS)) == 0)
X						/* If still fail, try GC */
X			sbx_comp(sizeof(struct sdblk)*SM_DNODS,lev++);
X
X	sbx_nfl = sd->slforw;		/* Take it off freelist */
X	sd->sdflags = SD_NID;
X	return(sd);			/* Return ptr to it */
X}
X
Xsbx_ndfre(sdp)
Xstruct sdblk *sdp;
X{	register struct sdblk *sd;
X	(sd = sdp)->sdflags = 0;
X	sd->slforw = sbx_nfl;
X	sbx_nfl = sd;
X}
X
XSBMA
Xsbx_malloc(size)
Xunsigned size;
X{
X	register int lev;
X	register SBMA res;
X
X	lev = 0;
X	while((res = (SBMA)malloc(size)) == 0)
X		sbx_comp(size,lev++);
X	return(res);
X}
X
Xstruct smblk *
Xsbx_mget(cmin,cmax)     /* like sbm_mget but never fails! */
XSBMO cmin, cmax;
X{	register struct smblk *sm;
X	register int lev, csiz;
X
X	lev = 0;
X	csiz = cmax;
X	for(;;)
X	  {	if(sm = sbm_mget(csiz,cmax))
X			return(sm);		/* Won right off... */
X		sbx_comp(csiz,lev++);		/* Barf, invoke GC */
X		if(sm = sbm_mget(csiz,cmax))	/* Then try again */
X			return(sm);
X		if((csiz >>= 1) < cmin)		/* If still short, reduce */
X			csiz = cmin;		/* request down to min */
X	  }
X}
X
Xchroff sbv_taddr;	/* Disk addr of place to write into (set by TSET) */
Xstruct sdblk *sbv_tsd;	/* SD that disk addr comes after (set by TSET) */
X
X#define sbx_qlk(sd)  (sd->sdflags&SD_LOCKS)
X
X#if 0
X	This is the compaction routine, which is the key to the
Xentire scheme.	Paging text to and from disk is trivial, but the
Xability to merge blocks is the important thing since it allows
Xflushing the pointer information as well as the actual text!  This
Xeliminates fragmentation as a fatal problem.
X	There are a variety of ways that storage can be reclaimed:
X
X- "pure" in-core blocks can be flushed instantly.
X- "impure" incore blocks can be written to tempfile storage and flushed.
X- The SM node freelist can be compacted so as to flush memory which is
X	used for nothing but holding free nodes.
X- The SD node freelist can be compacted, ditto.
X- SBBUFs can be compacted, by:
X	- Merging logically & physically adjacent on-disk pieces
X	- merging logically & physically adjacent in-core pieces
X	- merging logically adjacent in-core pieces
X	- merging logically adjacent disk pieces, by reading in
X		and then writing out to tempfile storage.
X		Worst case would reduce whole sbstr to single tempfile block.
X
XProblems:
X	What is "optimal" algorithm for typical usage?
X	Must go over all code to make sure right things get locked
X		and unlocked to avoid having rug pulled out from under.
X	Could have optional "registration table" for sbstruc; if exist
X		in table, can check during GC.	If find one, can first
X		do sbx_smdisc and then repoint sbcur to 1st block,
X		with sbdot of 0 and sboff of sb_tell().	 This allows
X		reducing whole thing to one block even tho "locked".
X		Never touch stuff locked with SD_LCK2, though.
X		Also may need way to protect the sbstr SD actually being
X		pointed to by current sbx routine processing.
X	Could have count of # nodes free for SM and SD; don''t GC 
X		unless # is some number greater than size of a node block!
X	Have different levels of compaction; pass level # down thru calls
X		so as to invoke progressively sterner compaction measures.
X		Can invoke sbx_comp with any particular level!
X	Must have list somewhere of SBBUFs?  or maybe OK to scan core
X		for SM_DNODS, then scan sdblks?
X	Screw: could happen that stuff gets flushed (cuz pure) or even
X		written out to tempfile, and then we have to read it back
X		in so as to compact more stuff into tempfile... how to avoid?
X		If pure stuff small and next to impure stuff, merge?
X	Some calls just want to get another free node and don''t need
X		new core.  How to indicate this?  How to decide between
X		freeing up used nodes, and creating new node freelist?
X#endif /*COMMENT*/
X/* Compact stuff.
X * General algorithm for getting storage is:
X *	1) allocate from freelist if enough there
X *	2) find unlocked pure smblk to free up
X *	3) find unlocked impure smblks, write out.
X *	4) Compact stuff by reducing # of sdblks.  This is key to scheme!
X *		Otherwise fragmentation will kill program.
X * Maybe put age cnt in each sbstr?  Bump global and set cntr each time
X * sbstr gets major hacking (not just getc/putc).
X */
Xextern struct smblk *sbm_list;
Xsbx_comp(cmin,lev)
Xint cmin, lev;
X{	int sbx_sdgc();
X
X	if(lev > 100)		/* If program has no way to handle this, */
X		abort();	/* then simply blow up. */
X	if(lev > 10)		/* Too many iterations? Try to warn. */
X		return(sbx_err(0,"GC loop, cannot free block of size %d",
X				cmin));
X
X	/* Step thru core hunting for SD node blocks */
X	sbm_nfor(SM_DNODS,sizeof(struct sdblk),sbx_sdgc,lev);
X}
X
X/* Do GC stuff on a sdblk.  Guaranteed to exist, but may be locked */
Xsbx_sdgc(sdp,lev)
Xstruct sdblk *sdp;
Xint lev;
X{	register struct sdblk *sd, *sdf;
X	register struct smblk *sm;
X	struct smblk *smf, *sbm_exp ();
X	SBMO more;
X
X	sd = sdp;
X	if(sbx_qlk(sd)) return(0);
X	sm = sd->sdmem;
X	sdf = sd->slforw;
X	if (lev < 4) goto lev3;
X
X	/* Level 4 - write out everything possible */
X	/* Back up to start of sbstr */
X	while((sdf = sd->slback) && !sbx_qlk(sdf))
X		sd = sdf;
X	if((sdf = sd->slforw) == 0	/* If only 1 blk, ensure on disk */
X	  || sbx_qlk(sdf))
X	  {	if(sm = sd->sdmem)
X		  {	if(sd->sdflags&SD_MOD)		/* If impure, */
X				sbx_aout(sd, 1);	/* swap out the SD */
X			sbm_mfree(sm);
X			sd->sdmem = 0;
X		  }
X		return(0);
X	  }
X	/* At least two blocks in string.  Set up for flushout. */
X	sbx_aout(sd, 0);	/* Swapout as much of sbstring as possible */
X	return(0);
X
Xlev3:			/* Level 3 - write out more */
Xlev2:			/* Level 2 - write out all impure & small pure */
Xlev1:	if(lev >= 1)	/* Level 1 - merge small impure & small pure */
X	  {	if(!sm || !sdf) return(0);
X		while(((smf = sdf->sdmem) && !(sdf->sdflags&SD_LOCKS)
X		  && (more = smf->smuse + sm->smuse) < SB_BUFSIZ) )
X		  {	if(sm->smforw != smf
X			  && more > sm->smlen)		/* If need more rm */
X			  {	sm = sbm_exp(sm,more);	/* Get it */
X				if(!sm) return(0);	/* If none, stop */
X				sd->sdmem = sm;
X			  }
X			bcopy(smf->smaddr,
X			     sm->smaddr + sm->smuse, smf->smuse);
X			sm->smuse = more;
X			if(sm->smforw == smf)
X			  {	sdf->sdmem = 0;
X				sbm_mmrg(sm);	/* Merge */
X				if(sm->smlen > more+SB_SLOP)
X					sbm_mfree(sbm_split(sm, more));
X					/* Guaranteed to win since mmrg
X					 * just freed a mem node */
X			  }
X			sd->sdflags |= SD_MOD;
X			if(sdf = sbx_ndel(sdf))
X				continue;
X			return(0);
X		  }
X	  }
X
X	if(lev <= 0)	/* Level 0 - free up large pure blocks */
X			/* Also merge blocks which are adjacent on disk */
X	  {	if(sm)
X		  {	if(sm->smuse == 0)
X				sd->sdlen = 0;
X			else if((sd->sdflags&SD_MOD) == 0
X			    && sm->smuse > 64)
X			  {	sbm_mfree(sm);
X				sd->sdmem = 0;
X				goto lev0adj;
X			  }
X			else goto lev0adj;
X		  }
X
X		if(sd->sdlen == 0	/* Free zero blocks */
X		  && sd->slback)	/* Make sure don't lose list */
X		  {	sbx_ndel(sd);
X			if((sd = sdf) == 0)
X				return(0);
X			sdf = sd->slforw;
X		  }
X	lev0adj:	/* Merge blocks if adjacent on disk */
X			/* This is common after reading thru large chunks
X			* of a file but not modifying it much.
X			*/
X		if((sd->sdflags&SD_MOD) == 0	/* Pure */
X		  && sdf && (sdf->sdflags&(SD_LOCKS|SD_MOD)) == 0
X		  && sd->sdfile && (sd->sdfile == sdf->sdfile)
X		  && (sd->sdaddr + sd->sdlen) == sdf->sdaddr )
X		  {	sd->sdlen += sdf->sdlen;
X			sbx_ndel(sdf);		/* Flush 2nd */
X			if(sm = sd->sdmem)
X			  {	sbm_mfree(sm);
X				sd->sdmem = 0;
X			  }
X		  }
X		return(0);
X	  }
X	return(0);
X}
X
X/* SBX_AOUT - output ALL of a hackable sbstring starting at given sdblk.
X *	Note that code is careful to do things so that an abort at any
X *	time (e.g. write error) will still leave sbstring in valid state.
X * Flag value:
X *	0 - Writes out as many unlocked sdblks as possible, and merges
X *		so that resulting sdblk (same one pointed to by arg)
X *		incorporates all stuff written out.
X *	1 - Writes out single sdblk indicated, whether unlocked or not.
X *		Doesn't free mem or merge anything; does update physlist
X *		and flags.
X *	2 - Writes out all sdblks to specified FD/offset, no mods at all,
X *		not even to physlist or flags.	Good for saving files
X *		when something seems wrong.  (How to pass fd/off args?)
X *		(offset arg not implemented, no need yet; 0 assumed)
X * Returns 0 if successful, else UNIX system call error number.
X */
X
Xsbx_aout(sdp,flag,fd)
Xstruct sdblk *sdp;
Xint flag, fd;
X{	register struct sdblk *sd;
X	register struct smblk *sm;
X	register int cnt;
X	int ifd, ofd, skflg, rem;
X	chroff inlen;
X	extern SBMA sbm_lowaddr;	/* Need this from SBM for rndrem */
X	char buf[SB_BUFSIZ+16];	/* Get buffer space from stack! */
X				/* Allow extra for word-align reads. */
X				/* This should be +WDSIZE, but some */
X				/* C compilers (eg XENIX) can't handle */
X				/* "sizeof" arith in allocation stmts! */
X
X	/* This flag and the two ptrs below are needed because UNIX
X	 * maintains only one I/O ptr per open file, and we can sometimes
X	 * be reading from/writing to the swapout file at same time.
X	 * Using DUP() to get a new FD (to avoid seeking back and forth)
X	 * won't help since both FD's will use the same I/O ptr!!!
X	 * Lastly, can't depend on returned value of LSEEK to push/pop
X	 * ptr, since V6 systems don't implement tell() or lseek() directly.
X	 * So we have to do it by hand...
X	 */
X	int botchflg;
X	chroff outptr, inptr;
X
X	if((sd = sdp)==0) return;
X	ofd = sbv_tf.sffd;		/* Default output FD */
X	if(flag==0)
X	  {	sbx_tset(sbx_qlen(sd),0);/* Find place for whole string */
X		outptr = sbv_taddr;	/* We'll have to update wrt ptr */
X	  }
X	else if (flag==1)	/* Single SD block, so it's reasonable to 
X				 * try aligning the output with the input. */
X	  {	if(sm = sd->sdmem)
X		  {	cnt = rndrem(sm->smaddr - sbm_lowaddr);
X			sbx_tset((chroff)(sm->smuse),cnt);
X		  }
X		else
X		  {	cnt = rndrem(sd->sdaddr);
X			sbx_tset(sd->sdlen, cnt);
X		  }
X		outptr = sbv_taddr;	/* We'll have to update wrt ptr */
X	  }
X	else		/* Outputting a whole sbstring to a file */
X	  {	ofd = fd;
X		outptr = 0;
X	  }
X
X	for(; sd;)
X	  {	if(flag==0 && sbx_qlk(sd))
X			break;		/* Stop when hit locked sdblk */
X		if(sm = sd->sdmem)
X		  {	if(cnt = sm->smuse)
X				if(write(ofd, sm->smaddr, cnt) != cnt)
X					return(sbx_err(errno,"Swapout wrt err"));
X			outptr += cnt;
X			if(flag==0)
X			  {	sd->sdmem = 0;	/* Flush the mem used */
X				sbm_mfree(sm);
X			  }
X			inlen = cnt;
X		  }
X		else if(inlen = sd->sdlen)
X		  {	if(sd->sdfile == 0)
X				return(sbx_err(errno,"Sdfile 0, SD %o",sd));
X			/* Foo on UNIX */
X			botchflg = ((ifd = sd->sdfile->sffd) == ofd) ? 1 : 0;
X			skflg = 1;		/* Always seek first time */
X			inptr = sd->sdaddr;
X			/* Efficiency hack - set up for first read so that
X			 * transfer is word-aligned and terminates at end
X			 * of a disk block.
X			 */
X			rem = rndrem(inptr);		/* Get alignment */
X			cnt = SB_BUFSIZ - (int)(inptr%SB_BUFSIZ);
X			while(inlen > 0)
X			  {
X				if(inlen < cnt) cnt = inlen;
X				if(!sbx_rdf(ifd, buf+rem, cnt, skflg, inptr))
X					return(sbx_err(errno,"Swapout err, SD %o",sd));
X				/* Further seeks depend on botch setting */
X				if(skflg = botchflg)
X				  {	if(lseek(ofd,outptr,0) < 0)
X						return(sbx_err(errno,
X							"Swapout sk err"));
X					inptr += cnt;
X				  }
X				if(write(ofd, buf+rem, cnt) != cnt)
X					return(sbx_err(errno,
X						"Swapout wrt err"));
X				outptr += cnt;
X				inlen -= cnt;
X				cnt = SB_BUFSIZ; /* Now can use full blocks */
X				rem = 0;	/* Aligned nicely, too! */
X			  }
X			inlen = sd->sdlen;
X		  }
X
X		/* Text written out, now merge block in */
X		if(flag == 2)			/* No merge if saving file */
X			goto donxt;
X		if(sd != sdp)			/* First block? */
X		  {	sdp->sdlen += inlen;	/* No, simple merge */
X			sd = sbx_ndel(sd);	/* Flush, get next */
X			continue;
X		  }
X
X		/* Handle 1st block specially */
X		if(sd->sdfile		/* Unlink from phys list */
X		  && sd != sbv_tsd)	/* Don't unlink if self */
X			sbx_npdel(sd);
X		sd->sdlen = inlen;
X		sd->sdfile = &sbv_tf;
X		sd->sdaddr = sbv_taddr;	/* Set from sbx_tset val */
X		sd->sdflags &= ~SD_MOD;	/* On disk, no longer modified */
X
X		/* Now insert into phys list at specified place */
X		if(sd == sbv_tsd)	/* If already same place */
X			goto next;	/* Skip linkin. */
X		if(sd->sdback = sbv_tsd)
X		  {	sd->sdforw = sbv_tsd->sdforw;
X			sd->sdback->sdforw = sd;
X		  }
X		else
X		  {	sd->sdforw = sbv_tf.sfptr1;
X			sbv_tf.sfptr1 = sd;
X		  }
X		if(sd->sdforw)
X			sd->sdforw->sdback = sd;
X
X	next:	if(flag==1)		/* If only doing 1 sdblk, */
X			break;		/* stop here. */
X	donxt:	sd = sd->slforw;	/* Done with 1st, get next */
X	  }
X	return(0);			/* Win return, no errors */
X}
X
X/* Returns hackable length of a sbstring (ends at EOF or locked block) */
Xchroff
Xsbx_qlen(sdp)
Xstruct sdblk *sdp;
X{	register struct sdblk *sd;
X	register struct smblk *sm;
X	chroff len;
X
X	len = 0;
X	for(sd = sdp; sd && !sbx_qlk(sd); sd = sd->slforw)
X		if(sm = sd->sdmem)
X			len += (chroff)sm->smuse;
X		else len += sd->sdlen;
X	return(len);
X}
X
X
X/* SBX_TSET - finds a place on temp swapout file big enough to hold
X *	given # of chars.  Sets SBV_TADDR to that location, as well
X *	as seeking to it so the next write call will output there.
X *	This location is guaranteed to have the requested
X *	byte alignment (0 = word-aligned).
X */
Xsbx_tset(loff, align)
Xchroff loff;
Xint align;
X{	register int fd;
X	register char *fcp;
X
X	if(sbv_tf.sffd <= 0)
X	  {		/* Must open the temp file! */
X/* Temporary file mechanism is system-dependent.  Eventually this
X** will probably require inclusion of a true c-env header file; for the
X** time being, we can cheat a little by checking O_T20_WILD, which will
X** be defined by <sys/file.h> on TOPS-20.  Otherwise, we assume we are
X** on a real Unix.
X*/
X#ifdef O_T20_WILD
X		extern char *tmpnam();	/* Use ANSI function */
X		fd = open(tmpnam((char *)NULL),
X				O_RDWR | O_CREAT | O_TRUNC | O_BINARY);
X		if(fd < 0)
X			return(sbx_err(0,"Swapout creat err"));		
X#else /* Real Unix */
X		fcp = "/tmp/sbd.XXXXXX";
X		if((fd = creat(mktemp(fcp),0600)) < 0)
X			return(sbx_err(0,"Swapout creat err"));
X		/* Must re-open so that we can both read and write to it */
X		close(fd);
X		if((fd = open(fcp,2)) < 0)
X			return(sbx_err(0,"Swapout open err"));
X		unlink(fcp);	/* Set so it vanishes when we do */
X#endif
X
X		sbv_tf.sffd = fd;	/* Initialize the sbfile struct */
X		sbv_tf.sfptr1 = 0;
X		sbv_ftab[fd] = &sbv_tf;	/* Record in table of all sbfiles */
X		sbv_taddr = 0;		/* "Return" this value */
X		return;		/* Ignore alignment for now */
X	  }
X	sbv_tsd = sbx_ffnd(&sbv_tf, loff+align, &sbv_taddr);
X	sbv_taddr += align;
X	if(lseek(sbv_tf.sffd, sbv_taddr, 0) < 0)
X		return(sbx_err(0,"Swapout seek err: (%d,%ld,0) %d %s",
X			sbv_tf.sffd, sbv_taddr, errno, strerror(errno)));
X
X}
X
X/* SBX_FFND - searches disk list of given file for free space of
X *	at least size chars.  Note that list must be sorted by ascending
X *	disk addrs in order for this to work!  If sdaddrs are only
X *	changed in SBX_SPLIT this will be true.
X *	Sets "aloc" to disk address for writing (this is guaranteed to
X *	be word-aligned, for efficiency), and returns SD ptr to
X *	block which this addr should follow in the physical list.  If ptr
X *	is 0, it means addr should be 1st thing in list.
X */
Xstruct sdblk *
Xsbx_ffnd(sfp, size, aloc)
XSBFILE *sfp;
Xchroff size, *aloc;
X{	register struct sdblk *sd, *sds, *sdl;
X	chroff cur;
X
X	cur = 0;
X	sds = 0;
X	sd = sfp->sfptr1;
Xredo:	for(; sd ; sd = (sds=sd)->sdforw)
X	  {	if(cur < sd->sdaddr)		/* Gap seen? */
X		  {	if(size <= (sd->sdaddr - cur))	/* Yes, big enuf? */
X				break;			/* Yup! */
X		  }					/* No, bump. */
X		else if(cur >=(sd->sdaddr + sd->sdlen))	/* No gap but chk */
X			continue;			/* No overlap, ok */
X		/* Bump to next possible gap. */
X		cur = sd->sdaddr + sd->sdlen;
X		cur = (long)rndup(cur);	/* Round up to word boundary! */
X	  }
X	*aloc = cur;		/* Return winning addr */
X
X	/* Perform verification check -- make sure this really is OK
X	 * and complain if not.	 If this never blows up, eventually can
X	 * take the check out.
X	 */
X	sdl = sd;
X	for(sd = sfp->sfptr1; sd; sd = sd->sdforw)
X	  {	if(cur < sd->sdaddr)
X		  {	if(size <= (sd->sdaddr - cur))
X				continue;
X		  }
X		else if(cur >= (sd->sdaddr + sd->sdlen))
X			continue;
X
X		sbx_err(0,"FFND blew it, but recovered. SD %o siz %ld",
X			sd, size);
X		sd = (sds = sdl)->sdforw;
X		goto redo;
X	  }
X
X
X	return(sds);		/* Return ptr to block this addr follows */
X}
X
Xsbx_rdf(fd,addr,cnt,skflg,loc)
Xregister int fd;
Xchar *addr;
Xint skflg;
Xchroff loc;
X{	register int rres, eres;
X	long lres;
X	char *errtyp, *ftyp;
X	chroff curlen;
X
X	errno = 0;
X	if(skflg && (lres = lseek(fd, (long)loc, 0)) == -1)
X	  {	errtyp = "Sk err";
X		goto errhan;
X	  }
X	if((rres = read(fd, addr, cnt)) != cnt)
X	  {	lres = rres;
X		errtyp = "Rd err";
X		goto errhan;
X	  }
X	return(rres);
Xerrhan:				/* Handle read or seek error */
X	eres = errno;
X	if(fd == sbv_tf.sffd)	/* See if dealing with swapout file */
X	  {	ftyp = "(swap)";
X		curlen = 0;
X	  }
X	else {			/* No, normal buffer file. */
X		ftyp = "";
X		curlen = sbx_fdlen(fd);
X		if(sbv_ftab[fd] &&
X		  (curlen != sbv_ftab[fd]->sflen))	/* File changed? */
X			if(sbx_rugpull(fd))	/* Yes, handle special case */
X				return(cnt);	/* Allow "win" return */
X	  }
X	sbx_err(0,"%s %d:%s, %ld:(%d%s,%o,%d)=%ld (fl %ld)",
X			errtyp,	eres, strerror(eres),
X			loc, fd, ftyp, addr, cnt, lres,
X			curlen);
X	return(0);
X}
X
X/* SBX_RUGPULL(fd) - Special routine called when package detects that
X *	the file indicated by the fd has changed since its original
X *	opening.  This can happen when a file is over-written by some
X *	other program (ED, for example).
X *	This means that all sdblks which reference this fd
X *	are probably bad.  Pass special error back up to the calling
X *	program to give it a chance at doing something.
X *	Extra credit: scan all sdblks and unpurify all which point to this
X *	file, so as to protect everything we still remember about it.
X *	Otherwise a GC could flush pure in-core portions.
X */
Xsbx_rugpull(fd)		/* FD already known to have entry in sbv_ftab */
Xregister int fd;
X{	int sbx_unpur();
X
X	/* First scan all sdblks to save what we still have. */
X	/* This does NOT remove the sdfile pointer, so we can still
X	 * find blocks that are affected. */
X	sbm_nfor(SM_DNODS, sizeof(struct sdblk), sbx_unpur, sbv_ftab[fd]);
X
X	if((int)sbv_debug == 1 || !sbv_debug)
X		return(0);			/* Nothing else we can do */
X	return((*sbv_debug)(2,(int *)0,"",fd));	/* Let caller handle it */
X}
Xsbx_unpur(sd, sf)		/* Auxiliary routine for SBX_RUGPULL */
Xregister struct sdblk *sd;
Xregister struct sbfile *sf;
X{	if(sd->sdfile == sf	/* If sdblk belongs to affected file */
X	  && sd->sdmem)		/* and has in-core version of text, */
X		sd->sdflags |= SD_MOD;	/* then ensure core version is used */
X}
X
Xsbx_err(val,str,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12)
Xchar *str;
X{	int *sptr;
X
X	sptr = (int *) &sptr;	/* Point to self on stack */
X	sptr += 5;		/* Point to return addr */
X	if((int)sbv_debug == 1)
X	  {	abort();
X	  }
X	if(sbv_debug)
X		(*sbv_debug)(1,*sptr,str,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12);
X	return(val);
X}
/
echo x - sbvall.c
sed '/^X/s///' > sbvall.c << '/'
X/* VALLOC - Aligned memory allocator
X *	Emulation of the 4.2BSD library routine of the same name.
X *	Copyright 1985 by Ken Harrenstien, SRI International
X *	This software is quasi-public; it may be used freely with
X *	like software, but may NOT be sold or made part of licensed
X *	products without permission of the author.  In all cases
X *	the source code and any modifications thereto must remain
X *	available to any user.
X *
X *	This is part of the SB library package.
X *	Any software using the SB library must likewise be made
X *	quasi-public, with freely available sources.
X */
X
X#include "sb.h"
X
Xchar *
Xvalloc(size)
Xunsigned size;
X{	register int pagmsk;
X	register SBMO i;
X	register struct smblk *sm, *smr;
X	struct smblk *sbm_mget(), *sbm_split();
X
X	pagmsk = getpagesize() - 1;	/* Get page size in bytes, less 1 */
X	if(!(sm = sbm_mget(size+pagmsk, size+pagmsk))) /* Get area big enuf */
X		return(0);
X	/* Now find # bytes prior to 1st page boundary.
X	 * This expression gives 0 if already at boundary, else #-1.
X	 */
X	i = pagmsk - ((int)(sm->smaddr) & pagmsk);
X	if(i)		/* If need to split off preceding stuff, */
X	  {	smr = sbm_split(sm, i+1);	/* do so (note i adjusted) */
X		sbm_mfree(sm);			/* Release preceding mem */
X		if(!(sm = smr)) return(0);	/* If couldn't split, fail */
X	  }
X	if(i = (sm->smlen - size))	/* See if any trailing stuff */
X	  {	smr = sbm_split(sm, size);	/* Yeah, split it off too */
X		if(smr) sbm_mfree(smr);	/* If couldn't split, excess OK. */
X	  }
X	return((char *)(sm->smaddr));
X}
/
echo x - sgtty.h
sed '/^X/s///' > sgtty.h << '/'
X/* The <sgtty.h> header contains data structures for ioctl(). */
X
X#ifndef _SGTTY_H
X#define _SGTTY_H
X
Xstruct sgttyb {
X  char sg_ispeed;		/* input speed */
X  char sg_ospeed;		/* output speed */
X  char sg_erase;		/* erase character */
X  char sg_kill;			/* kill character */
X  short  sg_flags;		/* mode flags */
X};
X
Xstruct tchars {
X  char t_intrc;			/* SIGINT char */
X  char t_quitc;			/* SIGQUIT char */
X  char t_startc;		/* start output (initially CTRL-Q) */
X  char t_stopc;			/* stop output	(initially CTRL-S) */
X  char t_eofc;			/* EOF (initially CTRL-D) */
X  char t_brkc;			/* input delimiter (like nl) */
X};
X
X/* Field names */
X#define XTABS	     0006000	/* do tab expansion */
X#define BITS8        0001400	/* 8 bits/char */
X#define BITS7        0001000	/* 7 bits/char */
X#define BITS6        0000400	/* 6 bits/char */
X#define BITS5        0000000	/* 5 bits/char */
X#define EVENP        0000200	/* even parity */
X#define ODDP         0000100	/* odd parity */
X#define RAW	     0000040	/* enable raw mode */
X#define CRMOD	     0000020	/* map lf to cr + lf */
X#define ECHO	     0000010	/* echo input */
X#define CBREAK	     0000002	/* enable cbreak mode */
X#define COOKED       0000000	/* neither CBREAK nor RAW */
X
X#define DCD	     010000	/* Data Carrier Detect */
X
X/* Line speeds */
X#define B0		   0	/* code for line-hangup */
X#define B110		   1
X#define B300		   3
X#define B1200		  12
X#define B2400		  24
X#define B4800		  48
X#define B9600 		  96
X
X#define TIOCGETP (('t'<<8) | 8)
X#define TIOCSETP (('t'<<8) | 9)
X#define TIOCGETC (('t'<<8) | 18)
X#define TIOCSETC (('t'<<8) | 17)
X#define TIOCFLUSH (('t'<<8) | 16)
X
X/* Things Minix supports but not properly */
X/* the divide-by-100 encoding ain't too hot */
X#define ANYP         0000300
X#define B0                 0
X#define B50                0
X#define B75                0
X#define B134               0
X#define B150               0
X#define B200               2
X#define B600               6
X#define B1800             18
X#define B3600             36
X#define B7200             72
X#define EXTA             192
X#define EXTB               0
X
X/* Things Minix doesn't support but are fairly harmless if used */
X#define NLDELAY      0001400
X#define TBDELAY      0006000
X#define CRDELAY      0030000
X#define VTDELAY      0040000
X#define BSDELAY      0100000
X#define ALLDELAY     0177400
X
X#if MACHINE == ATARI
X/* ST specific clock stuff */
X
X#define	 DCLOCK	('D'<<8)
X
X#define	DC_RBMS100	(DCLOCK|1)
X#define	DC_RBMS200	(DCLOCK|2)
X#define	DC_RSUPRA	(DCLOCK|3)
X#define	DC_RICD  	(DCLOCK|4)
X#define	DC_WBMS100	(DCLOCK|8)
X#define	DC_WBMS200	(DCLOCK|9)
X#endif
X
X#include <ansi.h>
X
X_PROTOTYPE( int gtty, (int fd, struct sgttyb *argp)			);
X_PROTOTYPE( int ioctl, (int fd, int request, struct sgttyb *argp)	);
X_PROTOTYPE( int stty, (int fd, struct sgttyb *argp)			);
X
X#endif /* _SGTTY_H */
/
