/*
 * BASIC by Phil Cockcroft
 */
#include        "bas.h"
#include	<errno.h>

/*
 * these variables should be set in the terminal configuration routines.
 */
#ifndef	CTRL
#define	CTRL(c)	((c) & 037)
#endif

int	ch_erase = CTRL('h');
int	ch_kill = CTRL('u');
int	ch_eof = CTRL('d');
int	ch_rprnt = CTRL('r');
int	ch_werase = CTRL('w');
int	ch_lnext = CTRL('v');
int	ch_susp = CTRL('z');
int	line_len = 80;
extern	int	ter_width;

/*      read a single character */

int
readc()
{
	char    c='\r';

#ifdef  SIG_JMP
	if(!setjmp(ecall)){
		ecalling = 1;
		if(read(0,&c,1) <= 0){
			ecalling = 0;
			VOID quit();
		}
		ecalling = 0;
	}
#else
	switch(read(0,&c,1)){		/* reading from a pipe exit on eof */
	case 1:
		break;
	case 0:
		VOID quit();
		break;
	case -1:
		if(errno != EINTR)
			VOID quit();
		break;
	}
#endif
	return( ((int)c) & 0177);
}

/*      sets up the terminal structures so that the editor is in rare
 *    with no paging or line boundries and no echo
 *      Also sets up the user modes so that they are sensible when
 *    we exit. ( friendly ).
 */

void
setupmyterm()
{
/*
	set_cap();
*/
	setu_term();
	if(ter_width > 10)
		line_len = ter_width;
}

/*   the actual editor pretty straight forward but.. */

#define	NORMAL_CHAR(c)	((c) >= ' ' && (c) <= '~')

#define	HIST_SIZ	500

#define	NORMAL_EDIT	0
#define	COMMAND_EDIT	1
#define	INSERT_EDIT	2
#define	TRAPPED_EDIT	3

static	CHAR	*xpbuf;
static	int	xpbuflen;

static	CHAR	*eline;

static	int	edit_mode;

static	jmp_buf	edit_chg;
static	int	cur_elnumb;
static	int	hist_numb;

static	int	cursr;
static	int	scstart;
static	int	scend;
static	int	scrlim;
static	int	line_end;
static	int	lastxm;
static	int	llim;

typedef	struct	{
	CHAR	*bufp;
	int	slen;
} savl_t;

int	Hist_Siz = HIST_SIZ;
static	savl_t	*edit_history;

#define	DATAVALID(buf)	((buf)->bufp != 0 && (buf)->slen > 0)

static	savl_t	ubuf;

static	savl_t	clbuf;
static	int	clcur;

static	savl_t	srbuf;

static	savl_t	Ubuf;

static	struct	undo	{
	savl_t	dels;
	savl_t	ins;
	int	posn;
	int	ocur;
	int	ncur;
	int	ucmd;
	int	uop;
	int	orepcnt;
	int	uswaped;
} undo;

static	int	last_ftFT;
static	int	last_ftFTc;
static	int	curcmd;
static	int	lastsrch;

static	CHAR	lastgtcmd[4];
static	CHAR	*lastgtcmdp;

static	void	putchs(const CHAR *, int);
static	void	putch(int);
static	void	pflush(void);
static	void	mv_cursr(int);
static	void	sav_ubuf(savl_t *, int, CHAR *);
static	void	setundo(void);
static	void	insert_mode(int, int);
static	void	redraw_line(int);
static	void	mvto_lin(int);
static	int	dosrch(int);
static	int	substr(CHAR *, int, CHAR *, int, int);
static	void	kill_line(void);
static	void	yank_line(void);
static	void	chng_line(void);
static	void	recov_line(savl_t *);
static	void	free_ubuf(savl_t *);
static	void	setscend(void);
static	int	mtchbrkt(void);
static	void	del_c(int, int);
static	int	gtword(int, int, int);
static	void	sav_curline(void);
static	void	draw_from_cursr(int);
static	int	normal_edit(void);
static	int	command_mode(ival);
static	int	find_c(int, int, int, int);
static	int	rd_xcmd(int, int, void (*)(void), int);
static	void	beep(void);
static	void	set_sc(void);
static	int	putxch(int);
static	int	vsize(int);
static	void	addnchars(int, CHAR *, CHAR **, CHAR **);
static	void	savustr(CHAR *, int, int, int);
static	void	dodotcmd(int);
static	void	doundo(void);
static	void	dotilde(int);
static	void	dopcmd(int, int);
static	CHAR    *do_ctrl_d(CHAR *, CHAR *);

static	const	char	*xemsgs[] = {
	"  ",
	"> ",
	"< ",
	"* "
};

static	const	char	delstr[] = "\b \b\b \b";
static	const	char	crlf[] = "\r\n";

/*
erase = ^h; kill = ^u;
eof = ^d; eol = <undef>; eol2 = <undef>; swtch = <undef>;
start = ^q; stop = ^s; susp = ^z; dsusp = ^y;
rprnt = ^r; flush = ^o; werase = ^w; lnext = ^v;
*/

/*ARGSUSED*/
int
edit(fl,fi,fc)
ival	fl, fi, fc;
{
	register CHAR   *q;
	register CHAR   *p;
	int     c = 0;
	int     special;
	int	llen;
	savl_t	*cur_line;
	CHAR	xeline[MAXLIN+1];
	CHAR	_xpbuf[MAXLIN+1];

	xpbuf = _xpbuf;
	xpbuflen = 0;

	eline = xeline;

	set_term();
	
	if(!edit_history){
		llen = sizeof(savl_t) * Hist_Siz;
		edit_history = (savl_t *)mmalloc(llen);
		clr_mem( (memp)edit_history, (ival)llen);
	}
	cur_elnumb = ++hist_numb;

	cur_line = &edit_history[cur_elnumb % Hist_Siz];
	free_ubuf(cur_line);

	scrlim = line_len - 4;
	
	llim = fl;
	curcmd = 0;
	setundo();

	if(fi){
		VOID strmov(eline, line, fi);
		putchs(eline, fi);
	}
	eline[fi] = 0;
	cursr=(int)fi;
	line_end = fi;
	lastxm = 0;
	edit_mode = NORMAL_EDIT;

	switch(setjmp(edit_chg)){
	case 0:		/* normal edit */
		c = normal_edit();
		if(c == ESCAPE && !noedit)
			c = command_mode(fc);
		break;
	case INSERT_EDIT:
		c = '\n';
		break;
	case TRAPPED_EDIT:
		free_ubuf(&clbuf);
		free_ubuf(&Ubuf);
		setundo();
		putchs(crlf, 2);
		pflush();
		hist_numb--;
		cursor=0;
		rset_term(0);
		return(c);
	}

	if(!noedit)
		putchs(crlf, 2);
	pflush();

	eline[line_end] = 0;

/*   special characters are dealt with here- null is never returned */
	for(p=eline,q=line,special=0;*p;p++){
		if(special){
			special=0;
			if(*p >= CTRL('a') && *p < ' ')
				*q++ = *p;
			else {
				*q++ = '\\';
				*q++ = *p;
			}
		}
		else if(*p=='\\')
			special++;
		else *q++ = *p;
	}
	*q=0;
	llen = q - (line +llim);
	if(llen > 0)
		sav_ubuf(cur_line, llen, line + llim);
	else
		hist_numb--;

	free_ubuf(&clbuf);
	free_ubuf(&Ubuf);
	setundo();
	cursor=0;
	rset_term(0);
	return(c);
}

static	int
normal_edit()
{
	register CHAR	*pcursr;
	register int	c;
	register int	i;
	int	lastescaped = 0;
	int	inword;
	CHAR	*plim;
	CHAR	*tmp;

	pcursr = eline + cursr;
	plim = eline + llim;
	for(;;){
		pflush();
		c = readc();
		if(trapped){
			*pcursr = 0;
			longjmp(edit_chg, TRAPPED_EDIT);
		}
		if(!c)
			continue;
		if(NORMAL_CHAR(c) || lastescaped){
			lastescaped = 0;
			if(pcursr >= eline + MAXLIN){
				beep();
				continue;
			}
			if(!noedit)
				VOID putxch(c);
			*pcursr++ = (CHAR)c;
			continue;
		}
		if(c == ch_eof){
#ifdef	MDUMP
			mdump();
#endif
			if( (tmp = do_ctrl_d(pcursr, plim)) != 0){
				pcursr = tmp;
				continue;
			}
			c = readc();
			if(c != ch_eof){
				beep();
				continue;
			}
			putchs(crlf, 2);
			pflush();
			VOID quit();
			break;
		} else if(c == '\r' || c == '\n' || c == ESCAPE){
			break;
		} else if(c == '\t'){
			i = pcursr - eline;
			do {
				if(i >= MAXLIN){
					beep();
					break;
				}
				*pcursr++ = (CHAR)' ';
				if(!noedit)
					putch(' ');
			} while(++i & 7);
			continue;
		} 
		if(noedit){
			if(pcursr < eline + MAXLIN)
				*pcursr++ = (CHAR)c;
			continue;
		}
		if(c == ch_kill){
			i = pcursr - eline;
			if(i > line_len)
				i %= line_len;
			else
				i -= llim;
			while(i-- > 0)
				putchs(delstr, 3 * vsize(*--pcursr));
			pcursr = plim;
			continue;
		} else if(c == ch_erase){
			if(pcursr > plim)
				putchs(delstr, 3 * vsize(*--pcursr));
			else
				beep();
			continue;
		} else if(c == ch_rprnt){
			putchs("^R\r\n", 4);
			for(i = pcursr - plim, pcursr = plim; i ; i--)
				VOID putxch(*pcursr++);
			continue;
		} else if(c == ch_werase){
			if(pcursr <= plim){
				beep();
				continue;
			}
			inword = 0;
			while(pcursr > plim){
				if(pcursr[-1] == ' '){
					if(inword)
						break;
				}
				else
					inword = 1;
				putchs(delstr, 3*vsize(*--pcursr));
			}
			continue;
		} else if(c == ch_lnext){
			lastescaped = 1;
			putchs("^\b", 2); 
			continue;
#ifdef	SIGTSTP
		} else if(c == ch_susp){
			putchs(crlf, 2);
			pflush();	/* flush it out */
			rset_term(0);
			VOID kill(0, SIGTSTP);
			set_term();
			if(llim)
				putchs(eline, llim);
			for(i = pcursr - plim, pcursr = plim; i ; i--)
				VOID putxch(*pcursr++);
			continue;
#endif
		}
		if(pcursr >= eline + MAXLIN){
			beep();
			continue;
		}
		*pcursr++ = (CHAR)c;
		VOID putxch(c);
	}
	*pcursr = 0;
	line_end = cursr = pcursr - eline;
	return(c);
}

static	int
command_mode(fc)
ival	fc;
{
	register int	mcursr;
	register int	c = 0;
	int	lnum;
	int	nchars;
	register int	repcnt = 1;
	int	repset;

	edit_mode = COMMAND_EDIT;
	redraw_line(1);

	sav_ubuf(&Ubuf, line_end - llim, eline + llim);

	while(!trapped){
		pflush();
		c = readc();
		if(trapped)
			break;
		if(c >= '1' && c <= '9'){
			repset = 1;
			repcnt = c - '0';
			for(;;){
				c = readc();
				if(trapped)
					break;
				if(c < '0' || c > '9'){
					if(repcnt < 0 || repcnt > MAXLIN)
						repcnt = MAXLIN;
					break;
				}
				repcnt = repcnt * 10 + c - '0';
			}
		}
		else {
			repset = 0;
			repcnt = 1;
		}
		if(c == '\r' || c == '\n' || trapped)
			break;

		lastgtcmdp = 0;

		mcursr = cursr;
		
		switch(curcmd = c){
		case '\b':
		case 'h':
			if( (mcursr -= repcnt) >= llim)
				mv_cursr(mcursr);
			else
				beep();
			break;
		case 'l':
		case ' ':
			if((mcursr += repcnt) < line_end)
				mv_cursr(mcursr);
			else
				beep();
			break;
		case '$':
			if(mcursr >= line_end || line_end <= llim)
				beep();
			else
				mv_cursr(line_end);
			break;
		case 'j':
		case '+':
			setundo();
			undo.posn = cur_elnumb;
			undo.uop = 4;
			mvto_lin(cur_elnumb + repcnt);
			undo.ocur = cur_elnumb;
			break;
		case '-':
		case 'k':
			setundo();
			undo.posn = cur_elnumb;
			undo.uop = 4;
			mvto_lin(cur_elnumb - repcnt);
			undo.ocur = cur_elnumb;
			break;
		case 'G':
			if(!repset){
				repcnt = cur_elnumb - Hist_Siz - 1;
				if(repcnt < 1)
					repcnt = 1;
			}
			setundo();
			undo.posn = cur_elnumb;
			mvto_lin(repcnt);
			undo.ocur = cur_elnumb;
			if(cur_elnumb != undo.posn)
				undo.uop = 4;
			break;
		case CTRL('L'):
			putch('\n');
			redraw_line(0);
			break;
		case 'w':
		case 'e':
		case 'b':
		case 'W':
		case 'E':
		case 'B':
			mcursr = gtword(c, repcnt, 0);
			if(mcursr < 0 || (cursr >= line_end-1 &&
							mcursr >= cursr))
				beep();
			else
				mv_cursr(mcursr);
			break;
		case '0':
			mv_cursr(llim);
			break;
		case '^':
			for(mcursr = llim ; mcursr < line_end ; mcursr++)
				if(eline[mcursr] != ' ')
					break;
			mv_cursr(mcursr);
			break;
		case '|':
			mv_cursr(llim + repcnt - 1);
			break;
		case ';':
			if(!last_ftFT){
				beep();
				break;
			}
			mcursr = find_c(last_ftFT, repcnt, last_ftFTc, 0);
			if(mcursr >= 0)
				mv_cursr(mcursr);
			else
				beep();
			break;
		case ',':
			switch(last_ftFT){
			case 't':
				c = 'T';
				break;
			case 'f':
				c = 'F';
				break;
			case 'F':
				c = 'f';
				break;
			case 'T':
				c = 't';
				break;
			default:
				c = 0;
				break;
			}
			if(!c){
				beep();
				break;
			}
			mcursr = find_c(c, repcnt, last_ftFTc, 0);
			if(mcursr >= 0)
				mv_cursr(mcursr);
			else
				beep();
			break;
		case 't':
		case 'f':
		case 'F':
		case 'T':
			mcursr = find_c(c, repcnt, 0, 0);
			if(mcursr >= 0)
				mv_cursr(mcursr);
			else
				beep();
			break;
		case '%':
			mcursr = mtchbrkt();
			if(mcursr >= 0)
				mv_cursr(mcursr);
			else
				beep();
			break;
		case 'A':
		case 'a':
		case 'I':
		case 'i':
		case 'R':
		case 'r':
		case 'S':
			setundo();
			insert_mode(c, repcnt);
			break;
		case 's':
			setundo();
			if(mcursr + repcnt <= line_end)
				insert_mode(c, repcnt);
			else
				beep();
			break;
		case 'd':
			setundo();
			mcursr = rd_xcmd(repcnt, c, kill_line, 0);
			if(mcursr < 0)
				break;
			nchars = mcursr - cursr;
			if(!nchars){
				beep();
				break;
			}
			if(nchars < 0){
				mv_cursr(mcursr);
				nchars = -nchars;
			}
			del_c(nchars, 0);
			break;
		case 'c':
			setundo();
			mcursr = rd_xcmd(repcnt, c, chng_line, 0);
			if(mcursr < 0)
				break;
			nchars = mcursr - cursr;
			if(!nchars){
				beep();
				break;
			}
			if(nchars < 0){
				mv_cursr(mcursr);
				nchars = -nchars;
			}
			insert_mode(c, nchars);
			break;
		case 'C':
			nchars = line_end - mcursr;
			if(!nchars){
				beep();
				break;
			}
			setundo();
			insert_mode(c, nchars);
			break;
		case 'D':
			nchars = line_end - mcursr;
			if(!nchars)
				beep();
			else {
				setundo();
				del_c(nchars, 0);
			}
			break;
		case 'x':
			setundo();
			del_c(repcnt, 0);
			break;
		case 'X':
			setundo();
			if(mcursr - repcnt < llim){
				beep();
				break;
			}
			mv_cursr(mcursr - repcnt);
			del_c(repcnt, 0);
			break;
		case '.':
			if(undo.ucmd == 0)
				beep();
			else
				dodotcmd(undo.ucmd);
			break;
		case 'u':
			if(undo.ucmd && undo.uop > 0)
				doundo();
			else
				beep();
			break;
		case 'U':
			setundo();
			kill_line();
			undo.uop = 3;
			if(DATAVALID(&Ubuf))
				addnchars(Ubuf.slen, Ubuf.bufp, (CHAR **)0,
								(CHAR **)0);
			savustr(Ubuf.bufp, Ubuf.slen, llim, 0);
			mv_cursr(llim);
			break;
			/*
			 * search operations
			 */
		case '/':
		case '?':
			/*
			 * do a save cur line.
			 * go into insert mode with a special flag set.
			 * when returned from routine, text is in eline
			 * do a search for this line. If found, do a move
			 * to this line. If not found, do a move to current
			 * line and beep. 
			 */
			lastsrch = c;
			if(cur_elnumb == hist_numb)
				sav_curline();
			
			free_ubuf(&srbuf);
			insert_mode(c, 1);

			if(trapped)
				break;

			if(!DATAVALID(&srbuf) || *srbuf.bufp != c){
				free_ubuf(&srbuf);
				lnum = hist_numb;
				beep();
			}
			else {
				lnum = dosrch(c);
				if(lnum < 0)
					lnum = hist_numb;
			}
			cur_elnumb = -1;	/* force movement */
			mvto_lin(lnum);
			break;
		case 'n':
		case 'N':
			/* repeat search with known search string. */
			if( (lastsrch != '/' && lastsrch != '?') ||
							srbuf.slen <= 0){
				beep();
				break;
			}
			lnum = dosrch( (c == 'n') ? lastsrch : 
					((lastsrch == '/') ? '?' : '/'));
			if(lnum < 0)
				break;
			mvto_lin(lnum);
			break;
		case 'y':
			setundo();
			mcursr = rd_xcmd(repcnt, c, yank_line, 0);
			if(mcursr < 0)
				break;
			nchars = mcursr - cursr;
			if(!nchars){
				beep();
				break;
			}
			if(nchars < 0)
				nchars = -nchars;
			else
				mcursr = cursr;
			sav_ubuf(&ubuf, nchars, eline + mcursr);
			break;
		case 'Y':
			setundo();
			yank_line();
			break;
		case 'p':
		case 'P':
			dopcmd(c, repcnt);
			break;
		case '~':
			setundo();
			dotilde(repcnt);
			break;
		case ESCAPE:
			if(!fc){
				beep();
				break;
			}
			eline[line_end] = 0;
			return(c);
		default:
			beep();
			break;
		}
	}
	if(trapped)
		longjmp(edit_chg, TRAPPED_EDIT);
	eline[line_end] = 0;
	return(c);
}

static void
dodotcmd(c)
int	c;
{
	register int	ndels = 0;
	register int	ndelp = 0;
	register int	doins = 0;
	int	mcursr = cursr;
	struct	undo	xundo;
	CHAR	in_text[MAXLIN+1];
	CHAR	*itext = in_text;

	/* copy undo buf */
	xundo = undo;
	if(undo.uswaped){
		/* unswap undo buf */
		xundo.dels = undo.ins;
		xundo.ins = undo.dels;
	}
	undo.dels.bufp = 0;
	undo.ins.bufp = 0;
	setundo();
	undo.ucmd = c;
	edit_mode = INSERT_EDIT;
	switch(c){
	case 'A':
		undo.uop = 1;
		mv_cursr(line_end);
		doins++;
		break;
	case 'a':
	case 'p':
		undo.uop = 1;
		if(line_end != llim)
			mv_cursr(cursr+1);
		doins++;
		break;
	case 'i':
	case 'P':
		undo.uop = 1;
		doins++;
		break;
	case 'I':
		undo.uop = 1;
		mv_cursr(llim);
		doins++;
		break;
	case 'R':
	case 'r':
	case 'S':
	case 's':
		ndels = xundo.dels.slen;
		undo.uop = 3;
		ndelp = 2;
		doins++;
		break;
	case 'c':
		/*
		 * BUG here: what should we do if we get cc for a dotcmd?
		 * we do what ksh does which is to do what we have here.
		 * vi does something different - i.e. it effectively
		 * does nothing.
		 */
		mcursr = rd_xcmd(1, c, chng_line, 1);
		if(mcursr < 0)
			break;
		ndels = mcursr - cursr;
		if(!ndels)
			beep();
		else if(ndels < 0){
			mv_cursr(mcursr);
			ndels = -ndels;
		}
		
		ndelp = 2;
		undo.uop = 3;
		doins++;
		break;
	case 'C':
		ndels = line_end - mcursr;
		if(!ndels){
			beep();
			break;
		}
		undo.uop = 3;
		ndelp = 2;
		doins++;
		break;
	case 'd':
		mcursr = rd_xcmd(1, c, kill_line, 1);
		if(mcursr < 0){
			break;
		}
		ndels = mcursr - cursr;
		if(!ndels)
			beep();
		else if(ndels < 0){
			mv_cursr(mcursr);
			ndels = -ndels;
		}
		break;
	case 'D':
		ndels = line_end - mcursr;
		if(!ndels)
			beep();
		break;
	case 'x':
		ndels = xundo.dels.slen;
		if(ndels <= 0)
			beep();
		break;
	case 'X':
		ndels = xundo.dels.slen;
		if(mcursr - ndels < llim){
			beep();
			ndels = 0;
		}
		else
			mv_cursr(mcursr - ndels);
		break;
	case '~':
		dotilde(xundo.orepcnt);
		break;
	default:
		beep();
		break;
	}
	if(ndels > 0)
		del_c(ndels, ndelp);
	if(doins){
		mcursr = cursr;
		if(DATAVALID(&xundo.ins))
			addnchars(xundo.ins.slen, xundo.ins.bufp,
							&itext, (CHAR **)0);
		savustr(in_text, itext - in_text, mcursr, 0);
		if(cursr > llim)
			mv_cursr(cursr - 1);
	}
	free_ubuf(&xundo.dels);
	free_ubuf(&xundo.ins);
	edit_mode = COMMAND_EDIT;
	mv_cursr(cursr);
}

static void
doundo()
{
	if(undo.uop & 8){
		mv_cursr(undo.ocur);
		dotilde(undo.orepcnt);
		if(undo.uswaped)
			undo.uswaped = 0;
		else {
			undo.uswaped = 1;
			mv_cursr(undo.ocur);
		}
		return;
	}
	if(undo.uop & 4){
		if(undo.uswaped){
			undo.uswaped = 0;
			mvto_lin(undo.ocur);
		}
		else {
			undo.uswaped = 1;
			mvto_lin(undo.posn);
		}
		return;
	}
	edit_mode = INSERT_EDIT;
	if(undo.uswaped){
		undo.uswaped = 0;
		mv_cursr(undo.posn);
		if( (undo.uop & 2) && DATAVALID(&undo.dels))
			del_c(undo.dels.slen, 1);
		if( (undo.uop & 1) && DATAVALID(&undo.ins))
			addnchars(undo.ins.slen, undo.ins.bufp,
							(CHAR **)0, (CHAR **)0);
		mv_cursr(undo.ncur);
	}
	else {
		undo.ncur = cursr;
		mv_cursr(undo.posn);
		if( (undo.uop & 1) && DATAVALID(&undo.ins))
			del_c(undo.ins.slen, 1);
		if( (undo.uop & 2) && DATAVALID(&undo.dels))
			addnchars(undo.dels.slen, undo.dels.bufp,
						(CHAR **)0, (CHAR **)0);
		mv_cursr(undo.ocur);
		undo.uswaped = 1;
	}
	edit_mode = COMMAND_EDIT;
	mv_cursr(cursr);
}

static void
dopcmd(c, repcnt)
int	c, repcnt;
{
	int	mcursr;
	CHAR	in_text[MAXLIN+1];
	CHAR	*itext = in_text;

	setundo();
	if(!DATAVALID(&ubuf)){
		beep();
		return;
	}
	edit_mode = INSERT_EDIT;
	if(c == 'p' && line_end != llim)
		mv_cursr(cursr+1);
	undo.uop = 1;
	mcursr = cursr;
			
	while(repcnt-- > 0)
		addnchars(ubuf.slen, ubuf.bufp, &itext, (CHAR **)0);
	savustr(in_text, itext - in_text, mcursr, 0);
	if(cursr > llim)
		mv_cursr(cursr - 1);
	edit_mode = COMMAND_EDIT;
}

void
dotilde(repcnt)
register int	repcnt;
{
	register int	c;

	if(llim == line_end){
		undo.orepcnt = 0;
		beep();
		return;
	}
	undo.orepcnt = repcnt;
	if(cursr + repcnt >= line_end)
		repcnt = line_end - cursr;
	while(repcnt > 0){
		repcnt--;
		c = eline[cursr];
		if(c >= 'A' && c <= 'Z')
			c += 'a' - 'A';
		else if(c >= 'a' && c <= 'z')
			c += 'A' - 'a';
		VOID putxch(c);
		eline[cursr] = (CHAR)c;
		mv_cursr(++cursr);
	}
	undo.uop = 8;
}

static	void
insert_mode(c, repcnt)
register int	c;
int	repcnt;
{
	register int	mcursr;
	int	nchanged;
	int	iposn;
	int	inword;
	int	escaped;
	CHAR	in_text[MAXLIN+1];
	CHAR	*itext = in_text;
	CHAR	rep_text[MAXLIN+1];
	CHAR	*rtext = 0;
	int	srch = 0;
	CHAR	xc;

	edit_mode = INSERT_EDIT;

	iposn = cursr;
	switch(c){
	case '/':
	case '?':
		eline[llim] = (char)c;
		*itext++ = (char)c;
		line_end = llim + 1;
		cursr = line_end;
		eline[line_end] = 0;
		iposn = cursr;
		redraw_line(3);
		srch = 1;
		break;
		/*
		 * c and S are a problem, since we also have to save what
		 * we changed it to. so that we can do a change back again.
		 * this is not nice.
		 */
	case 'C':
	case 'c':
	case 's':
		if(repcnt)
			del_c(repcnt, 2);
		undo.uop = 3;
		/* must copy ubuf here */
		break;
	case 'S':
		nchanged = line_end - llim;
		sav_ubuf(&ubuf, nchanged, eline + llim);
		savustr(eline + llim, nchanged, llim, 1);
		undo.uop = 3;
		line_end = llim;
		cursr = llim;
		eline[line_end] = 0;
		redraw_line(3);
		/* must copy ubuf here */
		break;
	case 'I':
		undo.uop = 1;
		mv_cursr(llim);
		break;
	case 'R':
		undo.uop = 3;
		rtext = rep_text;
		break;
	case 'r':
		undo.uop = 3;
		do {
			c = readc();
			if(trapped)
				longjmp(edit_chg, TRAPPED_EDIT);
		} while(c == 0);
		if(repcnt > MAXLIN)
			repcnt = MAXLIN;
		set_mem(itext, repcnt, c);
		rtext = rep_text;
		addnchars(repcnt, itext, &itext, &rtext);
		savustr(in_text, itext - in_text, iposn, 0);
		savustr(rep_text, rtext - rep_text, iposn, 1);
		if(cursr > llim)
			mv_cursr(cursr - 1);
		edit_mode = COMMAND_EDIT;
		return;
	case 'A':
		undo.uop = 1;
		mv_cursr(line_end);
		break;
	case 'a':
		undo.uop = 1;
		if(line_end != llim)
			mv_cursr(cursr+1);
		break;
	default:
		undo.uop = 1;
		break;
	}
	iposn = cursr;
	escaped = 0;
	for(;;){
		pflush();
		c = readc();
		if(trapped)
			longjmp(edit_chg, TRAPPED_EDIT);
		if(c == 0)
			continue;

		if(escaped){
			escaped = 0;
			xc = (CHAR)c;
			addnchars(1, &xc, &itext, &rtext);
			continue;
		}

		if(c == ESCAPE)
			break;

		if(c == '\n' || c == '\r')
			if(srch)
				break;
			else
				longjmp(edit_chg, INSERT_EDIT);

		if(c == ch_kill){
			iposn = llim;
			itext = in_text;
			if(rtext)
				rtext = rep_text;
			if(!srch)
				kill_line();
			else {
				line_end = llim;
				cursr = llim;
				eline[line_end] = 0;
				redraw_line(3);
			}
		} else if(c == '\b' || c == ch_erase){
			if(itext > in_text){
				mv_cursr(cursr-1);
				del_c(1, 1);
				itext--;
				if(rtext)
					rtext--;
			}
			else
				beep();
		}
		else if(c == CTRL('w')){
			if(itext <= in_text){
				beep();
				continue;
			}
			inword = 0;
			
			for(mcursr = cursr; mcursr > iposn ; mcursr--)
				if(eline[mcursr-1] == ' '){
					if(inword)
						break;
				}
				else
					inword = 1;
			nchanged = cursr - mcursr;
			itext -= nchanged;
			if(rtext)
				rtext -= nchanged;
			mv_cursr(mcursr);
			del_c(nchanged, 1);
		}
		else if(c == ch_eof){
			beep();
		}
		else if(c == CTRL('v')){
			escaped = 1;
		}
		else if(c == '\t'){
			addnchars(8 - (cursr & 7), "        ", &itext, &rtext);
		}
		else {
			xc = (CHAR)c;
			addnchars(1, &xc, &itext, &rtext);
		}
	}
	if(srch){
		sav_ubuf(&srbuf, itext - in_text, in_text);
		edit_mode = COMMAND_EDIT;
		return;
	}
		
	savustr(in_text, itext - in_text, iposn, 0);
	if(rtext)
		savustr(rep_text, rtext - rep_text, iposn, 1);
	if(cursr > llim)
		mv_cursr(cursr - 1);
	edit_mode = COMMAND_EDIT;
}

static void
addnchars(nchars, buf, ibuf, rbuf)
int	nchars;
CHAR	*buf;
CHAR	**ibuf, **rbuf;
{
	register CHAR	*p;
	int	nc, mcursr, nchanged;

	if(!nchars)
		return;
	if(cursr >= MAXLIN -1){
		beep();
		return;
	}
	p = eline + cursr;
	if(vsize(*p) > 1)
		putch('\b');

	if(rbuf && *rbuf){
		if(cursr + nchars >= MAXLIN)
			nchars = MAXLIN - cursr;

		/* do an overwrite */
		nc = nchars;
		if(cursr + nc > line_end)
			nc = line_end - cursr;
		if(nc)
			*rbuf = strmov(*rbuf, p, nc);
		
		if(ibuf)
			*ibuf = strmov(*ibuf, buf, nchars);
		VOID strmov(p, buf, nchars);
		mcursr = cursr + nchars;
		if(mcursr > line_end)
			line_end = mcursr;
	}
	else {
		if(line_end + nchars >= MAXLIN)
			nchars = MAXLIN - line_end;

		/* not at end of line */
		if(cursr < line_end){
			register CHAR	*sp, *q;
			sp = eline + line_end;
			q = sp + nchars;
			while(sp >= p)
				*q-- = *sp--;
		}
		if(ibuf)
			*ibuf = strmov(*ibuf, buf, nchars);
		VOID strmov(p, buf, nchars);
		line_end += nchars;
		mcursr = cursr + nchars;
	}
	eline[line_end] = 0;
	setscend();
	nchanged = line_end - cursr + 1;
	if(nchanged < 1)
		nchanged = 1;
	if(cursr + nchanged >= scend){
		cursr = mcursr;
		redraw_line(0);
	}
	else {
		draw_from_cursr(nchanged);
		mv_cursr(mcursr);
	}
}

static	void
redraw_line(force)
int	force;
{
	register CHAR	*p;
	register int	i;
	register int	scursr;
	int	savscr;
	int	xem = 0;

	if(cursr >= line_end){
		if(edit_mode == COMMAND_EDIT)
			cursr = line_end - 1;
		else if(cursr > line_end)
			cursr = line_end;
	}
	if(cursr < llim)
		cursr = llim;

	if(cursr < scstart || cursr >= scend || force)
		set_sc();
	putch('\r');

	putchs(eline, llim);
	if(scstart > llim)
		xem |= 2;
	savscr = llim;
	p = eline + scstart;
	for(i = scstart, scursr = llim ; scursr < scrlim ; i++, p++){
		if(i == cursr)
			savscr = scursr;
		if(i >= line_end){
			if(!xem && !lastxm && !(force & 2))
				break;
			scursr += putxch(' ');
		}
		else
			scursr += putxch(*p);
	}
	if(i < line_end)
		xem |= 1;
	if(xem || lastxm || (force & 2)){
		putchs(xemsgs[xem], 2);
		scursr += 2;
	}
	while(scursr > savscr){
		putch('\b');
		scursr--;
	}
	if(vsize(eline[cursr]) > 1)
		putch('^');
	lastxm = xem;
}

static	void
draw_from_cursr(nchanged)
int	nchanged;
{
	register CHAR	*p;
	register int	i;
	int	xem = 0;
	int	savscr;
	int	scursr;
	int	ocursr;

	ocursr = cursr;
	if(cursr >= line_end){
		if(edit_mode == COMMAND_EDIT)
			cursr = line_end - 1;
		else if(cursr > line_end)
			cursr = line_end;
	}
	if(cursr < llim)
		cursr = llim;
	if(scstart > llim)
		xem |= 2;
	scursr = llim;
	p = eline + scstart;
	for(i = scstart ; i < cursr ; i++, p++)
		scursr += vsize(*p);

	savscr = scursr;
	if(cursr != ocursr)
		for(; i < ocursr ; i++, p++)
			scursr += vsize(*p);

	for(; scursr < scrlim ; p++, i++, nchanged--){
		if(i >= line_end){
			if(!xem && !lastxm && !nchanged)
				break;
			scursr += putxch(' ');
		}
		else
			scursr += putxch(*p);
	}
		
	if(i < line_end)
		xem |= 1;
	if(xem || lastxm || nchanged){
		putchs(xemsgs[xem], 2);
		scursr += 2;
	}
	while(scursr > savscr){
		putch('\b');
		scursr--;
	}
	if(vsize(eline[cursr]) > 1)
		putch('^');
	lastxm = xem;
}

static	void
mv_cursr(ncursr)
int	ncursr;
{
	register CHAR	*p;
	register int	i;

	if(ncursr >= line_end){
		if(edit_mode == COMMAND_EDIT)
			ncursr = line_end - 1;
		else if(ncursr > line_end)
			ncursr = line_end;
	}
	if(ncursr < llim)
		ncursr = llim;
	if(ncursr < scstart || ncursr >= scend){
		cursr = ncursr;
		redraw_line(0);
		return;
	}
	i = ncursr - cursr;
	if(!i)
		return;
	p = eline + cursr;
	cursr = ncursr;
	if(vsize(*p) > 1)
		putch('\b');
	if(i < 0){
		i = -i;
		do {
			putchs("\b\b", vsize(*--p));
		}while(--i);
	}
	else {
		do {
			VOID putxch(*p++);
		} while(--i);
	}
	if(vsize(*p) > 1)
		putch('^');
}

static	void
mvto_lin(lnum)
register int	lnum;
{
	register savl_t	*savlp;

	if(lnum == cur_elnumb){
		redraw_line(3);
		return;
	}
	if(lnum < 0 || lnum > hist_numb || lnum <= hist_numb - Hist_Siz){
		beep();
		redraw_line(3);
		return;
	}
	if(lnum == hist_numb){		/* back to current line. */
		recov_line(&clbuf);
		cursr = clcur;
		free_ubuf(&clbuf);
	}
	else {				/* moveing off of current line. */
		savlp = &edit_history[lnum % Hist_Siz];
		if(!DATAVALID(savlp)){
			beep();
			redraw_line(3);
			return;
		}
		cursr = llim;
		if(cur_elnumb == hist_numb)
			sav_curline();
		recov_line(savlp);
	}
	cur_elnumb = lnum;
	redraw_line(3);
}

static	int
dosrch(cmdc)
int	cmdc;
{
	register int	i, lnum;
	int	incr = (cmdc == '?') ? 1 : -1;
	CHAR	*mstr = srbuf.bufp + 1;
	int	nchrs = srbuf.slen - 1;
	register savl_t	*ptr;
	int	bol = 0;

	if(*mstr == '^'){
		mstr++;
		nchrs--;
		bol++;
	}
	lnum = cur_elnumb + incr + Hist_Siz;
	for(i = 1 ; i < Hist_Siz ; i++, lnum += incr){
		ptr = &edit_history[lnum % Hist_Siz];
		if(!DATAVALID(ptr))
			break;
		if(ptr->slen < nchrs)
			continue;
		if(substr(mstr, nchrs, ptr->bufp, ptr->slen, bol)){
			while(lnum < hist_numb - Hist_Siz)
				lnum += Hist_Siz;
			while(lnum > hist_numb)
				lnum -= Hist_Siz;
			return(lnum);
		}
	}
	beep();
	return(-1);
}

static int
substr(str, slen, ofstr, oflen, bol)
CHAR	*str, *ofstr;
int	slen, oflen, bol;
{
	int	retrys;
	register CHAR	*p, *q;
	register int	icnt;

	retrys = oflen - slen;
	do  {
		for(p = str, q = ofstr, icnt = slen ; icnt ; icnt--, p++, q++)
			if(*p != *q)
				break;
		if(!icnt)
			return(1);
		ofstr++;
	} while(--retrys >= 0 && !bol);
	return(0);
}

static	void
del_c(nchars, ubufupdate)
int	nchars;
int	ubufupdate;
{
	register CHAR	*p, *q;
	register CHAR	*eq;
	int	nchanged;

	if(cursr >= line_end){
		beep();
		return;
	}
	nchanged = line_end - cursr;
	if(nchars > nchanged)
		nchars = nchanged;
	if(line_end - nchars < llim){
		beep();
		return;
	}
	p = &eline[cursr];
	q = p + nchars;
	switch(ubufupdate){
	case 0:	/* normal case */
		sav_ubuf(&ubuf, nchars, p);
		savustr(p, nchars, cursr, 1);
		undo.uop = 2;
		break;
	case 1:	/* no update */
		break;
	case 2:	/* a change update */
		savustr(p, nchars, cursr, 1);
		break;
	}
	eq = eline + line_end;
	if(vsize(*p) > 1)
		putch('\b');
	while(q <= eq){
		if(!NORMAL_CHAR(*p))
			nchanged++;
		*p++ = *q++;
	}
	line_end -= nchars;
	eline[line_end] = 0;
	setscend();
	if(cursr < scstart || cursr + nchanged >= scend)
		redraw_line(2);
	else
		draw_from_cursr(nchanged);
}

static	void
yank_line()
{
	sav_ubuf(&ubuf, line_end - llim, eline + llim);
}

static	void
chng_line()
{
	if(line_end == llim){
		beep();
		return;
	}
	mv_cursr(llim);
	insert_mode('c', line_end - llim);
}

static	void
sav_ubuf(savl, nchars, sbuf)
register savl_t	*savl;
int	nchars;
CHAR	*sbuf;
{
	if(savl->bufp)
		mfree( (MEMP)savl->bufp);
	savl->slen = nchars;
	savl->bufp = (CHAR *)mmalloc( (ival)(nchars + 1));
	if(nchars)
		VOID strmov(savl->bufp, sbuf, nchars);
	savl->bufp[nchars] = 0;
}

static	void
free_ubuf(savl)
register savl_t	*savl;
{
	if(savl->bufp){
		mfree( (MEMP)savl->bufp);
		savl->bufp = 0;
	}
	savl->slen = 0;
}

static	void
setundo()
{
	free_ubuf(&undo.dels);
	free_ubuf(&undo.ins);
	undo.dels.slen = -1;
	undo.ins.slen = -1;
	undo.ocur = cursr;
	undo.ncur = -1;
	undo.ucmd = curcmd;
	undo.uop = 0;
	undo.uswaped = 0;
}

static	void
savustr(buf, len, posn, isdel)
CHAR *buf;
int len, posn, isdel;
{
	sav_ubuf(isdel ? &undo.dels : &undo.ins, len, buf);
	if(posn >= 0)
		undo.posn = posn;
}

static	void
sav_curline()
{
	sav_ubuf(&clbuf, line_end - llim, eline + llim);
	clcur = cursr;
}

static	void
recov_line(savl)
register savl_t	*savl;
{
	if(llim)
		VOID strmov(eline, line, llim);

	line_end = llim;
	if(DATAVALID(savl)){
		VOID strmov(eline + llim, savl->bufp, savl->slen);
		line_end += savl->slen;
	}
	eline[line_end] = 0;
}

static	void
set_sc()
{
	register int	i, sc;
	register int	ncur;
	register CHAR	*p;

	ncur = cursr;

	for(i = sc = llim, p = eline + i ; i < ncur ; i++, p++)
		sc += vsize(*p);

	if(sc >= scrlim){
		/* cursor is beyond end of line. */
		sc = (scrlim - llim) / 2;
		while(sc > 0){
			sc -= vsize(*p--);
			i--;
		}
		scstart = i;
	}
	else
		scstart = llim;
	setscend();
}

static	void
setscend()
{
	register int	i;
	register int	scrwidth = scrlim - llim;
	register CHAR	*p;

	scend = scstart;
	p = eline + scstart;
	for(i = 0 ; i < scrwidth && scend < line_end; scend++, p++)
		i += vsize(*p);
	scend += (scrwidth - i);	/* this assumes space = 1 char width */
}

static	void
kill_line()
{						/* save the whole line */
	register int	nchars;

	nchars = line_end - llim;
	sav_ubuf(&ubuf, nchars, eline + llim);
	savustr(eline + llim, nchars, llim, 1);
	undo.uop = 2;
	line_end = llim;
	cursr = llim;
	eline[line_end] = 0;
	redraw_line(3);
}

static	int
rd_xcmd(repcnt, cmdc, dfunc, dotcmd)
int	repcnt, cmdc;
void	(*dfunc)(void);
int	dotcmd;
{
	register int	mcursr;
	int	c;
	int	xrepcnt;

	lastgtcmdp = lastgtcmd;
	if(!dotcmd){
		c = readc();
		if(trapped)
			return(-1);
		if(c >= '1' && c <= '9'){
			xrepcnt = c - '0';
			for(;;){
				c = readc();
				if(trapped)
					return(-1);
				if(c < '0' || c > '9'){
					repcnt *= xrepcnt;
					if(repcnt < 0 || repcnt > MAXLIN)
						repcnt = MAXLIN;
					break;
				}
				xrepcnt = xrepcnt * 10 + c - '0';
			}
		}
		*lastgtcmdp++ = (CHAR)c;
		undo.orepcnt = repcnt;
	}
	else {
		c = *lastgtcmdp++;
		repcnt = undo.orepcnt;
	}
	if(c == cmdc && dfunc){
		/* 't'is a duplicate. do it to the whole line. */
		/* and the only thing that you can do is a cc or a dd or a yy */
		(*dfunc)();
		return(-1);
	}
	switch(c){
	case 'f':
	case 'F':
	case 't':
	case 'T':
		mcursr = find_c(c, repcnt, 0, dotcmd);
		if(mcursr >= cursr)
			mcursr++;
		break;
	case 'W':
	case 'w':
		if(cmdc == 'c'){
			if(eline[cursr] == ' '){
				if(--repcnt == 0){
					mcursr = cursr + 1;
					break;
				}
			}
			c = ((c == 'w') ? 'e' : 'E');
		}
		mcursr = gtword(c, repcnt, 1);
		break;
	case 'e':
	case 'b':
	case 'E':
	case 'B':
		mcursr = gtword(c, repcnt, 1);
		break;
	case 'l':
		mcursr = cursr + repcnt;
		if(mcursr > line_end)
			mcursr = -1;
		break;
	case 'h':
		mcursr = cursr - repcnt;
		if(mcursr <= llim)
			mcursr = -1;
		break;
	case '0':
		mcursr = llim;
		break;
	case '$':
		mcursr = line_end;
		break;
	case '%':
		mcursr = mtchbrkt();
		if(mcursr > cursr)
			mcursr++;
		break;
	default:
		lastgtcmdp = lastgtcmd;
		mcursr = -1;
		break;
	}
	if(mcursr < 0)
		beep();
	if(!dotcmd)
		*lastgtcmdp = 0;
	return(mcursr);
}

static	int
find_c(c, repcnt, last_c, dotcmd)
int	c, repcnt;
int	last_c, dotcmd;
{
	register int	mcursr = cursr;
	register int	xc;
	register int	found = 0;
	
	if( (xc = last_c) <= 0){
		if(!dotcmd || !lastgtcmdp){
			xc = readc();
			if(trapped)
				return(-1);
			last_ftFT = c;
			last_ftFTc = xc;
			if(lastgtcmdp)
				*lastgtcmdp++ = (CHAR)xc;
		}
		else
			xc = *lastgtcmdp++;
	}

	if(c == 'f' || c == 't'){
		for(found = 0; mcursr < line_end && found < repcnt;)
			if(eline[++mcursr] == xc)
				found++;
		if(c == 't'){
			if(mcursr > llim)
				mcursr--;
			else
				found = 0;
		}
	}
	else {
		for(; mcursr > llim && found < repcnt;)
			if(eline[--mcursr] == xc)
				found++;
		if(c == 'T'){
			if(mcursr < line_end - 1)
				mcursr++;
			else
				found = 0;
		}
	}
	if(found >= repcnt)
		return(mcursr);
	return(-1);
}

static	int
is_inword(c, ascw)
int	c, ascw;
{
	if(c == ' ')
		return(2);
	if(!ascw)
		return(1);
	return((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
							(c >= '0' && c <= '9'));
}

static	int
gtword(c, repcnt, cw)
int	c, repcnt, cw;
{
	register int	mcursr = cursr;
	register int	wtype, rtype;
	int	ascword;

	ascword = (c < 'A' || c > 'Z');
	switch(c){
	case 'W':
	case 'w':
		do {
			if(mcursr >= line_end)
				return(-1);
			wtype = is_inword(eline[mcursr], ascword);
			while(mcursr < line_end){
				rtype = is_inword(eline[++mcursr], ascword);
				if(rtype != wtype){
					if(rtype != 2)
						break;
					wtype = rtype;
				}
			}
		} while(--repcnt > 0);
		break;
	case 'E':
	case 'e':
		do {
			wtype = is_inword(eline[mcursr+1], ascword);
			for(;mcursr < line_end;mcursr++){
				rtype = is_inword(eline[mcursr+1], ascword);
				if(rtype != wtype){
					if(wtype != 2)
						break;
					wtype = rtype;
				}
			}
		}while(--repcnt > 0);
		if(cw && mcursr < line_end)
			mcursr++;
		break;
	case 'B':
	case 'b':
		do {
			if(mcursr <= llim)
				return(-1);
			wtype = is_inword(eline[mcursr-1], ascword);
			for(; mcursr > llim ; mcursr--){
				rtype = is_inword(eline[mcursr-1], ascword);
				if(rtype != wtype){
					if(wtype != 2)
						break;
					wtype = rtype;
				}
			}
		} while(--repcnt > 0);
		break;
	}
	return(mcursr);
}

static	int
mtchbrkt()
{
	register int	mcursr = cursr;
	register int	nc = 0, rbseen = 0;

	/*
	 * search forward for matching '('
	 */
	while(mcursr < line_end){
		if(eline[mcursr] == ')'){
			if(--nc <= 0)
				break;
		}
		else if(eline[mcursr] == '('){
			rbseen++;
			nc++;
		}
		mcursr++;
	}
	if(mcursr >= line_end)
		return(-1);
	if(nc < 0 && !rbseen){
		/* must search back for previous bracket */
		nc = 0;
		while(mcursr > llim){
			if(eline[mcursr] == ')')
				nc++;
			else if(eline[mcursr] == '('){
				if(--nc <= 0)
					return(mcursr);
			}
			mcursr--;
		}
	}
	return( (nc <= 0) ? mcursr : -1);
}
			
static CHAR *
do_ctrl_d(pcursr, plim)
CHAR	*pcursr, *plim;
{
	CHAR	*savc;
	const	struct	tabl	*l;
	const	CHAR	*flist = 0;
	int	ndone = 0;
	int	i = 0;
	int	sslen;
	int	force = 0;

	savc = pcursr - 1;
	if(savc < plim || noedit)
		return( (CHAR *)0);

	if(*savc == '.' && savc > plim){
		force = 1;
		savc--;
	}
	if(is_inword( (int)*savc, 1) != 1)
		return( (CHAR *)0);

	while(savc >= plim && is_inword(*savc, 1) == 1){
		savc--;
		i++;
	}
	savc++;
	for(l = table ; l->string ; l++){
		if(*l->string != *savc)
			continue;
		sslen = slen( (char *)l->string);
		if(sslen < i)
			continue;
		if(!substr(savc, i, (CHAR *)l->string, sslen, 1))
			continue;
		if(!force || flist == 0)
			flist = l->string;
		putchs(crlf, 2);
		putchs("        ", 8);
		putchs(l->string, sslen);
		ndone++;
	}
	
	if(ndone){
		putchs(crlf, 2);
		if(ndone == 1 || force){
			while(*flist && savc < eline + MAXLIN)
				*savc++ = *flist++;
			pcursr = savc;
		}
		for(i = pcursr - eline, pcursr = eline; i ; i--)
			VOID putxch(*pcursr++);
	}
	else
		beep();
	return(pcursr);
}

/*      put out a character uses buffere output of up to 256 characters
 *    It used to use a static buffer but this is a waste of space so
 *    it now uses gblock as this is never used during an edit.
 *      A value of zero for the parameter will flush the buffer.
 */

static void
putch(c)
int	c;
{
#ifdef	MSDOS
	if(c &= MASK)
		bdos(6, (char)c);
#else
	if(xpbuflen >= MAXLIN)
		pflush();
	xpbuf[xpbuflen++] = (CHAR) c;
#endif
}

static void
pflush()
{
	if(xpbuflen > 0)
		VOID write(1, (char *)xpbuf, xpbuflen);
	xpbuflen = 0;
}

static void
putchs(sp, len)
const	char	*sp;
register int	len;
{
	register CHAR	*s = (CHAR *)sp;

	if(len + xpbuflen < MAXLIN){
		VOID strmov(xpbuf + xpbuflen, s, len);
		xpbuflen += len;
	}
	else {
		for(;*s && len > 0 ; s++, len--)
			putch(*s);
	}
}

static	int
putxch(c)
int	c;
{
	if(NORMAL_CHAR(c)){
		putch(c);
		return(1);
	}
	putch('^');
	if(c == '\177')
		putch('?');
	else
		putch(c + 'A'-1);
	return(2);
}

static	int
vsize(c)
int	c;
{
	return(NORMAL_CHAR(c) ? 1 : (c ? 2 : 0));
}

static	void
beep()
{
	putch('\007');
}


syntax highlighted by Code2HTML, v. 0.9.1