xref: /original-bsd/usr.bin/ex/ex_vops2.c (revision ca303e1f)
1cd8169f0Sbostic /*-
2*ca303e1fSbostic  * Copyright (c) 1980, 1993
3*ca303e1fSbostic  *	The Regents of the University of California.  All rights reserved.
4cd8169f0Sbostic  *
5cd8169f0Sbostic  * %sccs.include.proprietary.c%
6035a5d32Sdist  */
7035a5d32Sdist 
8035a5d32Sdist #ifndef lint
9*ca303e1fSbostic static char sccsid[] = "@(#)ex_vops2.c	8.1 (Berkeley) 06/09/93";
10cd8169f0Sbostic #endif /* not lint */
11035a5d32Sdist 
1264137b10Smark #include "ex.h"
1364137b10Smark #include "ex_tty.h"
1464137b10Smark #include "ex_vis.h"
1564137b10Smark 
1664137b10Smark /*
1764137b10Smark  * Low level routines for operations sequences,
1864137b10Smark  * and mostly, insert mode (and a subroutine
1964137b10Smark  * to read an input line, including in the echo area.)
2064137b10Smark  */
21562c8f51Sdist extern char	*vUA1, *vUA2;		/* mjm: extern; also in ex_vops.c */
22562c8f51Sdist extern char	*vUD1, *vUD2;		/* mjm: extern; also in ex_vops.c */
2364137b10Smark 
2464137b10Smark /*
2564137b10Smark  * Obleeperate characters in hardcopy
2664137b10Smark  * open with \'s.
2764137b10Smark  */
bleep(i,cp)2864137b10Smark bleep(i, cp)
2964137b10Smark 	register int i;
3064137b10Smark 	char *cp;
3164137b10Smark {
3264137b10Smark 
3364137b10Smark 	i -= column(cp);
3464137b10Smark 	do
35c0a5300eSconrad 		ex_putchar('\\' | QUOTE);
3664137b10Smark 	while (--i >= 0);
3764137b10Smark 	rubble = 1;
3864137b10Smark }
3964137b10Smark 
4064137b10Smark /*
4164137b10Smark  * Common code for middle part of delete
4264137b10Smark  * and change operating on parts of lines.
4364137b10Smark  */
vdcMID()4464137b10Smark vdcMID()
4564137b10Smark {
4664137b10Smark 	register char *cp;
4764137b10Smark 
4864137b10Smark 	squish();
4964137b10Smark 	setLAST();
5025da3ac7Smark 	if (FIXUNDO)
5164137b10Smark 		vundkind = VCHNG, CP(vutmp, linebuf);
5264137b10Smark 	if (wcursor < cursor)
5364137b10Smark 		cp = wcursor, wcursor = cursor, cursor = cp;
5464137b10Smark 	vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor;
5564137b10Smark 	return (column(wcursor - 1));
5664137b10Smark }
5764137b10Smark 
5864137b10Smark /*
5964137b10Smark  * Take text from linebuf and stick it
6064137b10Smark  * in the VBSIZE buffer BUF.  Used to save
6164137b10Smark  * deleted text of part of line.
6264137b10Smark  */
takeout(BUF)6364137b10Smark takeout(BUF)
6464137b10Smark 	char *BUF;
6564137b10Smark {
6664137b10Smark 	register char *cp;
6764137b10Smark 
6864137b10Smark 	if (wcursor < linebuf)
6964137b10Smark 		wcursor = linebuf;
7064137b10Smark 	if (cursor == wcursor) {
7164137b10Smark 		beep();
7264137b10Smark 		return;
7364137b10Smark 	}
7464137b10Smark 	if (wcursor < cursor) {
7564137b10Smark 		cp = wcursor;
7664137b10Smark 		wcursor = cursor;
7764137b10Smark 		cursor = cp;
7864137b10Smark 	}
79c0a5300eSconrad 	ex_setBUF(BUF);
8064137b10Smark 	if ((BUF[0] & (QUOTE|TRIM)) == OVERBUF)
8164137b10Smark 		beep();
8264137b10Smark }
8364137b10Smark 
8464137b10Smark /*
8564137b10Smark  * Are we at the end of the printed representation of the
8664137b10Smark  * line?  Used internally in hardcopy open.
8764137b10Smark  */
ateopr()8864137b10Smark ateopr()
8964137b10Smark {
9064137b10Smark 	register int i, c;
9164137b10Smark 	register char *cp = vtube[destline] + destcol;
9264137b10Smark 
9364137b10Smark 	for (i = WCOLS - destcol; i > 0; i--) {
9464137b10Smark 		c = *cp++;
9564137b10Smark 		if (c == 0)
9664137b10Smark 			return (1);
9764137b10Smark 		if (c != ' ' && (c & QUOTE) == 0)
9864137b10Smark 			return (0);
9964137b10Smark 	}
10064137b10Smark 	return (1);
10164137b10Smark }
10264137b10Smark 
10364137b10Smark /*
10464137b10Smark  * Append.
10564137b10Smark  *
10664137b10Smark  * This routine handles the top level append, doing work
10764137b10Smark  * as each new line comes in, and arranging repeatability.
10864137b10Smark  * It also handles append with repeat counts, and calculation
10964137b10Smark  * of autoindents for new lines.
11064137b10Smark  */
11164137b10Smark bool	vaifirst;
11264137b10Smark bool	gobbled;
11364137b10Smark char	*ogcursor;
11464137b10Smark 
vappend(ch,cnt,indent)11564137b10Smark vappend(ch, cnt, indent)
116562c8f51Sdist 	int ch;		/* mjm: char --> int */
11764137b10Smark 	int cnt, indent;
11864137b10Smark {
11964137b10Smark 	register int i;
12064137b10Smark 	register char *gcursor;
12164137b10Smark 	bool escape;
122562c8f51Sdist 	int repcnt, savedoomed;
12364137b10Smark 	short oldhold = hold;
124c0a5300eSconrad #ifdef	SIGWINCH
125562c8f51Sdist 	int oldmask;
126c0a5300eSconrad #endif
12764137b10Smark 
12864137b10Smark 	/*
12964137b10Smark 	 * Before a move in hardopen when the line is dirty
13064137b10Smark 	 * or we are in the middle of the printed representation,
13164137b10Smark 	 * we retype the line to the left of the cursor so the
13264137b10Smark 	 * insert looks clean.
13364137b10Smark 	 */
13464137b10Smark 	if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) {
13564137b10Smark 		rubble = 1;
13664137b10Smark 		gcursor = cursor;
13764137b10Smark 		i = *gcursor;
13864137b10Smark 		*gcursor = ' ';
13964137b10Smark 		wcursor = gcursor;
14064137b10Smark 		vmove();
14164137b10Smark 		*gcursor = i;
14264137b10Smark 	}
14364137b10Smark 	vaifirst = indent == 0;
14464137b10Smark 
14564137b10Smark 	/*
14664137b10Smark 	 * Handle replace character by (eventually)
14764137b10Smark 	 * limiting the number of input characters allowed
14864137b10Smark 	 * in the vgetline routine.
14964137b10Smark 	 */
15064137b10Smark 	if (ch == 'r')
15164137b10Smark 		repcnt = 2;
15264137b10Smark 	else
15364137b10Smark 		repcnt = 0;
15464137b10Smark 
15564137b10Smark 	/*
15664137b10Smark 	 * If an autoindent is specified, then
15764137b10Smark 	 * generate a mixture of blanks to tabs to implement
15864137b10Smark 	 * it and place the cursor after the indent.
15964137b10Smark 	 * Text read by the vgetline routine will be placed in genbuf,
16064137b10Smark 	 * so the indent is generated there.
16164137b10Smark 	 */
16264137b10Smark 	if (value(AUTOINDENT) && indent != 0) {
16364137b10Smark 		gcursor = genindent(indent);
16464137b10Smark 		*gcursor = 0;
16564137b10Smark 		vgotoCL(qcolumn(cursor - 1, genbuf));
16664137b10Smark 	} else {
16764137b10Smark 		gcursor = genbuf;
16864137b10Smark 		*gcursor = 0;
16964137b10Smark 		if (ch == 'o')
17064137b10Smark 			vfixcurs();
17164137b10Smark 	}
17264137b10Smark 
17364137b10Smark 	/*
17464137b10Smark 	 * Prepare for undo.  Pointers delimit inserted portion of line.
17564137b10Smark 	 */
17664137b10Smark 	vUA1 = vUA2 = cursor;
17764137b10Smark 
17864137b10Smark 	/*
17964137b10Smark 	 * If we are not in a repeated command and a ^@ comes in
18064137b10Smark 	 * then this means the previous inserted text.
18164137b10Smark 	 * If there is none or it was too long to be saved,
18264137b10Smark 	 * then beep() and also arrange to undo any damage done
18364137b10Smark 	 * so far (e.g. if we are a change.)
18464137b10Smark 	 */
18564137b10Smark 	if ((vglobp && *vglobp == 0) || peekbr()) {
18664137b10Smark 		if ((INS[0] & (QUOTE|TRIM)) == OVERBUF) {
18764137b10Smark 			beep();
18864137b10Smark 			if (!splitw)
18964137b10Smark 				ungetkey('u');
19064137b10Smark 			doomed = 0;
19164137b10Smark 			hold = oldhold;
19264137b10Smark 			return;
19364137b10Smark 		}
19464137b10Smark 		/*
19564137b10Smark 		 * Unread input from INS.
19664137b10Smark 		 * An escape will be generated at end of string.
19764137b10Smark 		 * Hold off n^^2 type update on dumb terminals.
19864137b10Smark 		 */
19964137b10Smark 		vglobp = INS;
20064137b10Smark 		hold |= HOLDQIK;
20164137b10Smark 	} else if (vglobp == 0)
20264137b10Smark 		/*
20364137b10Smark 		 * Not a repeated command, get
20464137b10Smark 		 * a new inserted text for repeat.
20564137b10Smark 		 */
20664137b10Smark 		INS[0] = 0;
20764137b10Smark 
20864137b10Smark 	/*
20964137b10Smark 	 * For wrapmargin to hack away second space after a '.'
21064137b10Smark 	 * when the first space caused a line break we keep
21164137b10Smark 	 * track that this happened in gobblebl, which says
21264137b10Smark 	 * to gobble up a blank silently.
21364137b10Smark 	 */
21464137b10Smark 	gobblebl = 0;
21564137b10Smark 
216c0a5300eSconrad #ifdef	SIGWINCH
217562c8f51Sdist 	oldmask = sigblock(sigmask(SIGWINCH));
218c0a5300eSconrad #endif
21964137b10Smark 	/*
22064137b10Smark 	 * Text gathering loop.
22164137b10Smark 	 * New text goes into genbuf starting at gcursor.
22264137b10Smark 	 * cursor preserves place in linebuf where text will eventually go.
22364137b10Smark 	 */
22464137b10Smark 	if (*cursor == 0 || state == CRTOPEN)
22564137b10Smark 		hold |= HOLDROL;
22664137b10Smark 	for (;;) {
22764137b10Smark 		if (ch == 'r' && repcnt == 0)
22864137b10Smark 			escape = 0;
22964137b10Smark 		else {
230562c8f51Sdist 			gcursor = vgetline(repcnt, gcursor, &escape, ch);
23164137b10Smark 
23264137b10Smark 			/*
23364137b10Smark 			 * After an append, stick information
23464137b10Smark 			 * about the ^D's and ^^D's and 0^D's in
23564137b10Smark 			 * the repeated text buffer so repeated
23664137b10Smark 			 * inserts of stuff indented with ^D as backtab's
23764137b10Smark 			 * can work.
23864137b10Smark 			 */
23964137b10Smark 			if (HADUP)
24064137b10Smark 				addtext("^");
24164137b10Smark 			else if (HADZERO)
24264137b10Smark 				addtext("0");
24364137b10Smark 			while (CDCNT > 0)
24464137b10Smark 				addtext("\204"), CDCNT--;
24564137b10Smark 			if (gobbled)
24664137b10Smark 				addtext(" ");
24764137b10Smark 			addtext(ogcursor);
24864137b10Smark 		}
24964137b10Smark 		repcnt = 0;
25064137b10Smark 
25164137b10Smark 		/*
25264137b10Smark 		 * Smash the generated and preexisting indents together
25364137b10Smark 		 * and generate one cleanly made out of tabs and spaces
25464137b10Smark 		 * if we are using autoindent.
25564137b10Smark 		 */
25664137b10Smark 		if (!vaifirst && value(AUTOINDENT)) {
25764137b10Smark 			i = fixindent(indent);
25864137b10Smark 			if (!HADUP)
25964137b10Smark 				indent = i;
26064137b10Smark 			gcursor = strend(genbuf);
26164137b10Smark 		}
26264137b10Smark 
26364137b10Smark 		/*
26464137b10Smark 		 * Limit the repetition count based on maximum
26564137b10Smark 		 * possible line length; do output implied
26664137b10Smark 		 * by further count (> 1) and cons up the new line
26764137b10Smark 		 * in linebuf.
26864137b10Smark 		 */
26964137b10Smark 		cnt = vmaxrep(ch, cnt);
27064137b10Smark 		CP(gcursor + 1, cursor);
27164137b10Smark 		do {
27264137b10Smark 			CP(cursor, genbuf);
27364137b10Smark 			if (cnt > 1) {
27464137b10Smark 				int oldhold = hold;
27564137b10Smark 
27664137b10Smark 				Outchar = vinschar;
27764137b10Smark 				hold |= HOLDQIK;
278c0a5300eSconrad 				ex_printf("%s", genbuf);
27964137b10Smark 				hold = oldhold;
28064137b10Smark 				Outchar = vputchar;
28164137b10Smark 			}
28264137b10Smark 			cursor += gcursor - genbuf;
28364137b10Smark 		} while (--cnt > 0);
28464137b10Smark 		endim();
28564137b10Smark 		vUA2 = cursor;
28664137b10Smark 		if (escape != '\n')
28764137b10Smark 			CP(cursor, gcursor + 1);
28864137b10Smark 
28964137b10Smark 		/*
29064137b10Smark 		 * If doomed characters remain, clobber them,
29164137b10Smark 		 * and reopen the line to get the display exact.
29264137b10Smark 		 */
29364137b10Smark 		if (state != HARDOPEN) {
29464137b10Smark 			DEPTH(vcline) = 0;
295562c8f51Sdist 			savedoomed = doomed;
29664137b10Smark 			if (doomed > 0) {
29764137b10Smark 				register int cind = cindent();
29864137b10Smark 
29964137b10Smark 				physdc(cind, cind + doomed);
30064137b10Smark 				doomed = 0;
30164137b10Smark 			}
30264137b10Smark 			i = vreopen(LINE(vcline), lineDOT(), vcline);
303562c8f51Sdist #ifdef TRACE
304562c8f51Sdist 			if (trace)
305562c8f51Sdist 				fprintf(trace, "restoring doomed from %d to %d\n", doomed, savedoomed);
306562c8f51Sdist #endif
307562c8f51Sdist 			if (ch == 'R')
308562c8f51Sdist 				doomed = savedoomed;
30964137b10Smark 		}
31064137b10Smark 
31164137b10Smark 		/*
31264137b10Smark 		 * All done unless we are continuing on to another line.
31364137b10Smark 		 */
31464137b10Smark 		if (escape != '\n')
31564137b10Smark 			break;
31664137b10Smark 
31764137b10Smark 		/*
31864137b10Smark 		 * Set up for the new line.
31964137b10Smark 		 * First save the current line, then construct a new
32064137b10Smark 		 * first image for the continuation line consisting
32164137b10Smark 		 * of any new autoindent plus the pushed ahead text.
32264137b10Smark 		 */
32364137b10Smark 		killU();
32464137b10Smark 		addtext(gobblebl ? " " : "\n");
32564137b10Smark 		vsave();
32664137b10Smark 		cnt = 1;
32764137b10Smark 		if (value(AUTOINDENT)) {
32864137b10Smark #ifdef LISPCODE
32964137b10Smark 			if (value(LISP))
33064137b10Smark 				indent = lindent(dot + 1);
33164137b10Smark 			else
33264137b10Smark #endif
33364137b10Smark 			     if (!HADUP && vaifirst)
33464137b10Smark 				indent = whitecnt(linebuf);
33564137b10Smark 			vaifirst = 0;
33664137b10Smark 			strcLIN(vpastwh(gcursor + 1));
33764137b10Smark 			gcursor = genindent(indent);
33864137b10Smark 			*gcursor = 0;
33964137b10Smark 			if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2])
34064137b10Smark 				gcursor = genbuf;
34164137b10Smark 			CP(gcursor, linebuf);
34264137b10Smark 		} else {
34364137b10Smark 			CP(genbuf, gcursor + 1);
34464137b10Smark 			gcursor = genbuf;
34564137b10Smark 		}
34664137b10Smark 
34764137b10Smark 		/*
34864137b10Smark 		 * If we started out as a single line operation and are now
34964137b10Smark 		 * turning into a multi-line change, then we had better yank
35064137b10Smark 		 * out dot before it changes so that undo will work
35164137b10Smark 		 * correctly later.
35264137b10Smark 		 */
35325da3ac7Smark 		if (FIXUNDO && vundkind == VCHNG) {
35464137b10Smark 			vremote(1, yank, 0);
35564137b10Smark 			undap1--;
35664137b10Smark 		}
35764137b10Smark 
35864137b10Smark 		/*
35964137b10Smark 		 * Now do the append of the new line in the buffer,
36064137b10Smark 		 * and update the display.  If slowopen
36164137b10Smark 		 * we don't do very much.
36264137b10Smark 		 */
36364137b10Smark 		vdoappend(genbuf);
36464137b10Smark 		vundkind = VMANYINS;
36564137b10Smark 		vcline++;
36664137b10Smark 		if (state != VISUAL)
36764137b10Smark 			vshow(dot, NOLINE);
36864137b10Smark 		else {
36964137b10Smark 			i += LINE(vcline - 1);
37064137b10Smark 			vopen(dot, i);
37164137b10Smark 			if (value(SLOWOPEN))
37264137b10Smark 				vscrap();
37364137b10Smark 			else
37464137b10Smark 				vsync1(LINE(vcline));
37564137b10Smark 		}
37664137b10Smark 		strcLIN(gcursor);
37764137b10Smark 		*gcursor = 0;
37864137b10Smark 		cursor = linebuf;
37964137b10Smark 		vgotoCL(qcolumn(cursor - 1, genbuf));
38064137b10Smark 	}
38164137b10Smark 
38264137b10Smark 	/*
38364137b10Smark 	 * All done with insertion, position the cursor
38464137b10Smark 	 * and sync the screen.
38564137b10Smark 	 */
38664137b10Smark 	hold = oldhold;
38764137b10Smark 	if (cursor > linebuf)
38864137b10Smark 		cursor--;
38964137b10Smark 	if (state != HARDOPEN)
39064137b10Smark 		vsyncCL();
39164137b10Smark 	else if (cursor > linebuf)
39264137b10Smark 		back1();
39364137b10Smark 	doomed = 0;
39464137b10Smark 	wcursor = cursor;
39564137b10Smark 	vmove();
396c0a5300eSconrad #ifdef	SIGWINCH
397562c8f51Sdist 	(void)sigsetmask(oldmask);
398c0a5300eSconrad #endif
39964137b10Smark }
40064137b10Smark 
40164137b10Smark /*
40264137b10Smark  * Subroutine for vgetline to back up a single character position,
40364137b10Smark  * backwards around end of lines (vgoto can't hack columns which are
40464137b10Smark  * less than 0 in general).
40564137b10Smark  */
back1()40664137b10Smark back1()
40764137b10Smark {
40864137b10Smark 
40964137b10Smark 	vgoto(destline - 1, WCOLS + destcol - 1);
41064137b10Smark }
41164137b10Smark 
41264137b10Smark /*
41364137b10Smark  * Get a line into genbuf after gcursor.
41464137b10Smark  * Cnt limits the number of input characters
41564137b10Smark  * accepted and is used for handling the replace
41664137b10Smark  * single character command.  Aescaped is the location
41764137b10Smark  * where we stick a termination indicator (whether we
41864137b10Smark  * ended with an ESCAPE or a newline/return.
41964137b10Smark  *
42064137b10Smark  * We do erase-kill type processing here and also
42164137b10Smark  * are careful about the way we do this so that it is
42264137b10Smark  * repeatable.  (I.e. so that your kill doesn't happen,
42364137b10Smark  * when you repeat an insert if it was escaped with \ the
424562c8f51Sdist  * first time you did it.  commch is the command character
425562c8f51Sdist  * involved, including the prompt for readline.
42664137b10Smark  */
42764137b10Smark char *
vgetline(cnt,gcursor,aescaped,commch)428562c8f51Sdist vgetline(cnt, gcursor, aescaped, commch)
42964137b10Smark 	int cnt;
43064137b10Smark 	register char *gcursor;
43164137b10Smark 	bool *aescaped;
432562c8f51Sdist 	char commch;
43364137b10Smark {
43464137b10Smark 	register int c, ch;
43564137b10Smark 	register char *cp;
436562c8f51Sdist 	int x, y, iwhite, backsl=0;
43764137b10Smark 	char *iglobp;
438db30859cSmark 	char cstr[2];
43964137b10Smark 	int (*OO)() = Outchar;
44064137b10Smark 
44164137b10Smark 	/*
44264137b10Smark 	 * Clear the output state and counters
44364137b10Smark 	 * for autoindent backwards motion (counts of ^D, etc.)
44464137b10Smark 	 * Remember how much white space at beginning of line so
44564137b10Smark 	 * as not to allow backspace over autoindent.
44664137b10Smark 	 */
44764137b10Smark 	*aescaped = 0;
44864137b10Smark 	ogcursor = gcursor;
44964137b10Smark 	flusho();
45064137b10Smark 	CDCNT = 0;
45164137b10Smark 	HADUP = 0;
45264137b10Smark 	HADZERO = 0;
45364137b10Smark 	gobbled = 0;
45464137b10Smark 	iwhite = whitecnt(genbuf);
45564137b10Smark 	iglobp = vglobp;
45664137b10Smark 
45764137b10Smark 	/*
45864137b10Smark 	 * Carefully avoid using vinschar in the echo area.
45964137b10Smark 	 */
46064137b10Smark 	if (splitw)
46164137b10Smark 		Outchar = vputchar;
46264137b10Smark 	else {
46364137b10Smark 		Outchar = vinschar;
46464137b10Smark 		vprepins();
46564137b10Smark 	}
46664137b10Smark 	for (;;) {
467562c8f51Sdist 		backsl = 0;
46864137b10Smark 		if (gobblebl)
46964137b10Smark 			gobblebl--;
47064137b10Smark 		if (cnt != 0) {
47164137b10Smark 			cnt--;
47264137b10Smark 			if (cnt == 0)
47364137b10Smark 				goto vadone;
47464137b10Smark 		}
475db30859cSmark 		c = getkey();
476db30859cSmark 		if (c != ATTN)
477db30859cSmark 			c &= (QUOTE|TRIM);
478db30859cSmark 		ch = c;
479db30859cSmark 		maphopcnt = 0;
480c0a5300eSconrad 		if (vglobp == 0 && Peek_key == 0 && commch != 'r')
48125da3ac7Smark 			while ((ch = map(c, immacs)) != c) {
48264137b10Smark 				c = ch;
48325da3ac7Smark 				if (!value(REMAP))
48425da3ac7Smark 					break;
485db30859cSmark 				if (++maphopcnt > 256)
486db30859cSmark 					error("Infinite macro loop");
48725da3ac7Smark 			}
48864137b10Smark 		if (!iglobp) {
48964137b10Smark 
49064137b10Smark 			/*
49164137b10Smark 			 * Erase-kill type processing.
49264137b10Smark 			 * Only happens if we were not reading
49364137b10Smark 			 * from untyped input when we started.
49464137b10Smark 			 * Map users erase to ^H, kill to -1 for switch.
49564137b10Smark 			 */
496db30859cSmark #ifndef USG3TTY
49764137b10Smark 			if (c == tty.sg_erase)
4982949201cSbostic 				c = CTRL('h');
49964137b10Smark 			else if (c == tty.sg_kill)
50064137b10Smark 				c = -1;
501db30859cSmark #else
502db30859cSmark 			if (c == tty.c_cc[VERASE])
5032949201cSbostic 				c = CTRL('h');
504db30859cSmark 			else if (c == tty.c_cc[VKILL])
505db30859cSmark 				c = -1;
506db30859cSmark #endif
50764137b10Smark 			switch (c) {
50864137b10Smark 
50964137b10Smark 			/*
51064137b10Smark 			 * ^?		Interrupt drops you back to visual
51164137b10Smark 			 *		command mode with an unread interrupt
51264137b10Smark 			 *		still in the input buffer.
51364137b10Smark 			 *
51464137b10Smark 			 * ^\		Quit does the same as interrupt.
51564137b10Smark 			 *		If you are a ex command rather than
51664137b10Smark 			 *		a vi command this will drop you
51764137b10Smark 			 *		back to command mode for sure.
51864137b10Smark 			 */
51964137b10Smark 			case ATTN:
52064137b10Smark 			case QUIT:
52164137b10Smark 				ungetkey(c);
52264137b10Smark 				goto vadone;
52364137b10Smark 
52464137b10Smark 			/*
52564137b10Smark 			 * ^H		Backs up a character in the input.
52664137b10Smark 			 *
52764137b10Smark 			 * BUG:		Can't back around line boundaries.
52864137b10Smark 			 *		This is hard because stuff has
52964137b10Smark 			 *		already been saved for repeat.
53064137b10Smark 			 */
5312949201cSbostic 			case CTRL('h'):
53264137b10Smark bakchar:
53364137b10Smark 				cp = gcursor - 1;
53464137b10Smark 				if (cp < ogcursor) {
53564137b10Smark 					if (splitw) {
53664137b10Smark 						/*
53764137b10Smark 						 * Backspacing over readecho
53864137b10Smark 						 * prompt. Pretend delete but
53964137b10Smark 						 * don't beep.
54064137b10Smark 						 */
54164137b10Smark 						ungetkey(c);
54264137b10Smark 						goto vadone;
54364137b10Smark 					}
54464137b10Smark 					beep();
54564137b10Smark 					continue;
54664137b10Smark 				}
54764137b10Smark 				goto vbackup;
54864137b10Smark 
54964137b10Smark 			/*
55064137b10Smark 			 * ^W		Back up a white/non-white word.
55164137b10Smark 			 */
5522949201cSbostic 			case CTRL('w'):
55364137b10Smark 				wdkind = 1;
55464137b10Smark 				for (cp = gcursor; cp > ogcursor && isspace(cp[-1]); cp--)
55564137b10Smark 					continue;
55664137b10Smark 				for (c = wordch(cp - 1);
55764137b10Smark 				    cp > ogcursor && wordof(c, cp - 1); cp--)
55864137b10Smark 					continue;
55964137b10Smark 				goto vbackup;
56064137b10Smark 
56164137b10Smark 			/*
56264137b10Smark 			 * users kill	Kill input on this line, back to
56364137b10Smark 			 *		the autoindent.
56464137b10Smark 			 */
56564137b10Smark 			case -1:
56664137b10Smark 				cp = ogcursor;
56764137b10Smark vbackup:
56864137b10Smark 				if (cp == gcursor) {
56964137b10Smark 					beep();
57064137b10Smark 					continue;
57164137b10Smark 				}
57264137b10Smark 				endim();
57364137b10Smark 				*cp = 0;
57464137b10Smark 				c = cindent();
57564137b10Smark 				vgotoCL(qcolumn(cursor - 1, genbuf));
57664137b10Smark 				if (doomed >= 0)
57764137b10Smark 					doomed += c - cindent();
57864137b10Smark 				gcursor = cp;
57964137b10Smark 				continue;
58064137b10Smark 
58164137b10Smark 			/*
58264137b10Smark 			 * \		Followed by erase or kill
58364137b10Smark 			 *		maps to just the erase or kill.
58464137b10Smark 			 */
58564137b10Smark 			case '\\':
58664137b10Smark 				x = destcol, y = destline;
587c0a5300eSconrad 				ex_putchar('\\');
58864137b10Smark 				vcsync();
58964137b10Smark 				c = getkey();
590db30859cSmark #ifndef USG3TTY
591562c8f51Sdist 				if (c == tty.sg_erase || c == tty.sg_kill)
592db30859cSmark #else
593db30859cSmark 				if (c == tty.c_cc[VERASE]
594562c8f51Sdist 				    || c == tty.c_cc[VKILL])
595db30859cSmark #endif
596562c8f51Sdist 				{
59764137b10Smark 					vgoto(y, x);
59864137b10Smark 					if (doomed >= 0)
59964137b10Smark 						doomed++;
60064137b10Smark 					goto def;
60164137b10Smark 				}
60264137b10Smark 				ungetkey(c), c = '\\';
603562c8f51Sdist 				backsl = 1;
604562c8f51Sdist 				break;
60564137b10Smark 
60664137b10Smark 			/*
60764137b10Smark 			 * ^Q		Super quote following character
60864137b10Smark 			 *		Only ^@ is verboten (trapped at
60964137b10Smark 			 *		a lower level) and \n forces a line
61064137b10Smark 			 *		split so doesn't really go in.
61164137b10Smark 			 *
61264137b10Smark 			 * ^V		Synonym for ^Q
61364137b10Smark 			 */
6142949201cSbostic 			case CTRL('q'):
6152949201cSbostic 			case CTRL('v'):
61664137b10Smark 				x = destcol, y = destline;
617c0a5300eSconrad 				ex_putchar('^');
61864137b10Smark 				vgoto(y, x);
61964137b10Smark 				c = getkey();
62064137b10Smark #ifdef TIOCSETC
62164137b10Smark 				if (c == ATTN)
62264137b10Smark 					c = nttyc.t_intrc;
62364137b10Smark #endif
62464137b10Smark 				if (c != NL) {
62564137b10Smark 					if (doomed >= 0)
62664137b10Smark 						doomed++;
62764137b10Smark 					goto def;
62864137b10Smark 				}
62964137b10Smark 				break;
63064137b10Smark 			}
63164137b10Smark 		}
63264137b10Smark 
63364137b10Smark 		/*
63464137b10Smark 		 * If we get a blank not in the echo area
63564137b10Smark 		 * consider splitting the window in the wrapmargin.
63664137b10Smark 		 */
637db30859cSmark 		if (c != NL && !splitw) {
638db30859cSmark 			if (c == ' ' && gobblebl) {
63964137b10Smark 				gobbled = 1;
64064137b10Smark 				continue;
64164137b10Smark 			}
642562c8f51Sdist 			if (value(WRAPMARGIN) &&
643562c8f51Sdist 				(outcol >= OCOLUMNS - value(WRAPMARGIN) ||
644562c8f51Sdist 				 backsl && outcol==0) &&
645562c8f51Sdist 				commch != 'r') {
646db30859cSmark 				/*
647db30859cSmark 				 * At end of word and hit wrapmargin.
648db30859cSmark 				 * Move the word to next line and keep going.
649db30859cSmark 				 */
650db30859cSmark 				wdkind = 1;
651db30859cSmark 				*gcursor++ = c;
652562c8f51Sdist 				if (backsl)
653562c8f51Sdist 					*gcursor++ = getkey();
654db30859cSmark 				*gcursor = 0;
655db30859cSmark 				/*
656db30859cSmark 				 * Find end of previous word if we are past it.
657db30859cSmark 				 */
658db30859cSmark 				for (cp=gcursor; cp>ogcursor && isspace(cp[-1]); cp--)
659db30859cSmark 					;
660562c8f51Sdist 				if (outcol+(backsl?OCOLUMNS:0) - (gcursor-cp) >= OCOLUMNS - value(WRAPMARGIN)) {
661db30859cSmark 					/*
662db30859cSmark 					 * Find beginning of previous word.
663db30859cSmark 					 */
664db30859cSmark 					for (; cp>ogcursor && !isspace(cp[-1]); cp--)
665db30859cSmark 						;
666db30859cSmark 					if (cp <= ogcursor) {
667db30859cSmark 						/*
668db30859cSmark 						 * There is a single word that
669db30859cSmark 						 * is too long to fit.  Just
670db30859cSmark 						 * let it pass, but beep for
671db30859cSmark 						 * each new letter to warn
672db30859cSmark 						 * the luser.
673db30859cSmark 						 */
674db30859cSmark 						c = *--gcursor;
675db30859cSmark 						*gcursor = 0;
676db30859cSmark 						beep();
677db30859cSmark 						goto dontbreak;
678db30859cSmark 					}
679db30859cSmark 					/*
680db30859cSmark 					 * Save it for next line.
681db30859cSmark 					 */
682db30859cSmark 					macpush(cp, 0);
683db30859cSmark 					cp--;
684db30859cSmark 				}
685db30859cSmark 				macpush("\n", 0);
686db30859cSmark 				/*
687db30859cSmark 				 * Erase white space before the word.
688db30859cSmark 				 */
689db30859cSmark 				while (cp > ogcursor && isspace(cp[-1]))
690db30859cSmark 					cp--;	/* skip blank */
691db30859cSmark 				gobblebl = 3;
692db30859cSmark 				goto vbackup;
693db30859cSmark 			}
694db30859cSmark 		dontbreak:;
695db30859cSmark 		}
696db30859cSmark 
697db30859cSmark 		/*
698db30859cSmark 		 * Word abbreviation mode.
699db30859cSmark 		 */
700db30859cSmark 		cstr[0] = c;
701db30859cSmark 		if (anyabbrs && gcursor > ogcursor && !wordch(cstr) && wordch(gcursor-1)) {
702db30859cSmark 			int wdtype, abno;
703db30859cSmark 
704db30859cSmark 			cstr[1] = 0;
705db30859cSmark 			wdkind = 1;
706db30859cSmark 			cp = gcursor - 1;
707db30859cSmark 			for (wdtype = wordch(cp - 1);
708db30859cSmark 			    cp > ogcursor && wordof(wdtype, cp - 1); cp--)
709db30859cSmark 				;
710db30859cSmark 			*gcursor = 0;
711db30859cSmark 			for (abno=0; abbrevs[abno].mapto; abno++) {
712db30859cSmark 				if (eq(cp, abbrevs[abno].cap)) {
713db30859cSmark 					macpush(cstr, 0);
714c0a5300eSconrad 					macpush(abbrevs[abno].mapto, 1);
715db30859cSmark 					goto vbackup;
71664137b10Smark 				}
71764137b10Smark 			}
718db30859cSmark 		}
719db30859cSmark 
72064137b10Smark 		switch (c) {
72164137b10Smark 
72264137b10Smark 		/*
72364137b10Smark 		 * ^M		Except in repeat maps to \n.
72464137b10Smark 		 */
72564137b10Smark 		case CR:
72664137b10Smark 			if (vglobp)
72764137b10Smark 				goto def;
72864137b10Smark 			c = '\n';
72964137b10Smark 			/* presto chango ... */
73064137b10Smark 
73164137b10Smark 		/*
73264137b10Smark 		 * \n		Start new line.
73364137b10Smark 		 */
73464137b10Smark 		case NL:
73564137b10Smark 			*aescaped = c;
73664137b10Smark 			goto vadone;
73764137b10Smark 
73864137b10Smark 		/*
73964137b10Smark 		 * escape	End insert unless repeat and more to repeat.
74064137b10Smark 		 */
74164137b10Smark 		case ESCAPE:
74264137b10Smark 			if (lastvgk)
74364137b10Smark 				goto def;
74464137b10Smark 			goto vadone;
74564137b10Smark 
74664137b10Smark 		/*
74764137b10Smark 		 * ^D		Backtab.
74864137b10Smark 		 * ^T		Software forward tab.
74964137b10Smark 		 *
75064137b10Smark 		 *		Unless in repeat where this means these
75164137b10Smark 		 *		were superquoted in.
75264137b10Smark 		 */
7532949201cSbostic 		case CTRL('d'):
7542949201cSbostic 		case CTRL('t'):
75564137b10Smark 			if (vglobp)
75664137b10Smark 				goto def;
75764137b10Smark 			/* fall into ... */
75864137b10Smark 
75964137b10Smark 		/*
76064137b10Smark 		 * ^D|QUOTE	Is a backtab (in a repeated command).
76164137b10Smark 		 */
7622949201cSbostic 		case CTRL('d') | QUOTE:
76364137b10Smark 			*gcursor = 0;
76464137b10Smark 			cp = vpastwh(genbuf);
76564137b10Smark 			c = whitecnt(genbuf);
7662949201cSbostic 			if (ch == CTRL('t')) {
76764137b10Smark 				/*
76864137b10Smark 				 * ^t just generates new indent replacing
76964137b10Smark 				 * current white space rounded up to soft
77064137b10Smark 				 * tab stop increment.
77164137b10Smark 				 */
77264137b10Smark 				if (cp != gcursor)
77364137b10Smark 					/*
77464137b10Smark 					 * BUG:		Don't hack ^T except
77564137b10Smark 					 *		right after initial
77664137b10Smark 					 *		white space.
77764137b10Smark 					 */
77864137b10Smark 					continue;
77964137b10Smark 				cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1));
78064137b10Smark 				ogcursor = cp;
78164137b10Smark 				goto vbackup;
78264137b10Smark 			}
78364137b10Smark 			/*
78464137b10Smark 			 * ^D works only if we are at the (end of) the
78564137b10Smark 			 * generated autoindent.  We count the ^D for repeat
78664137b10Smark 			 * purposes.
78764137b10Smark 			 */
78864137b10Smark 			if (c == iwhite && c != 0)
78964137b10Smark 				if (cp == gcursor) {
79064137b10Smark 					iwhite = backtab(c);
79164137b10Smark 					CDCNT++;
79264137b10Smark 					ogcursor = cp = genindent(iwhite);
79364137b10Smark 					goto vbackup;
79464137b10Smark 				} else if (&cp[1] == gcursor &&
79564137b10Smark 				    (*cp == '^' || *cp == '0')) {
79664137b10Smark 					/*
79764137b10Smark 					 * ^^D moves to margin, then back
79864137b10Smark 					 * to current indent on next line.
79964137b10Smark 					 *
80064137b10Smark 					 * 0^D moves to margin and then
80164137b10Smark 					 * stays there.
80264137b10Smark 					 */
80364137b10Smark 					HADZERO = *cp == '0';
80464137b10Smark 					ogcursor = cp = genbuf;
80564137b10Smark 					HADUP = 1 - HADZERO;
80664137b10Smark 					CDCNT = 1;
80764137b10Smark 					endim();
80864137b10Smark 					back1();
80925da3ac7Smark 					vputchar(' ');
81064137b10Smark 					goto vbackup;
81164137b10Smark 				}
81264137b10Smark 			if (vglobp && vglobp - iglobp >= 2 &&
81364137b10Smark 			    (vglobp[-2] == '^' || vglobp[-2] == '0')
81464137b10Smark 			    && gcursor == ogcursor + 1)
81564137b10Smark 				goto bakchar;
81664137b10Smark 			continue;
81764137b10Smark 
81864137b10Smark 		default:
81964137b10Smark 			/*
82064137b10Smark 			 * Possibly discard control inputs.
82164137b10Smark 			 */
82264137b10Smark 			if (!vglobp && junk(c)) {
82364137b10Smark 				beep();
82464137b10Smark 				continue;
82564137b10Smark 			}
82664137b10Smark def:
827562c8f51Sdist 			if (!backsl) {
828c0a5300eSconrad 				ex_putchar(c);
829db30859cSmark 				flush();
830562c8f51Sdist 			}
83164137b10Smark 			if (gcursor > &genbuf[LBSIZE - 2])
83264137b10Smark 				error("Line too long");
83364137b10Smark 			*gcursor++ = c & TRIM;
83464137b10Smark 			vcsync();
83564137b10Smark 			if (value(SHOWMATCH) && !iglobp)
83664137b10Smark 				if (c == ')' || c == '}')
83764137b10Smark 					lsmatch(gcursor);
83864137b10Smark 			continue;
83964137b10Smark 		}
84064137b10Smark 	}
84164137b10Smark vadone:
84264137b10Smark 	*gcursor = 0;
843db30859cSmark 	if (Outchar != termchar)
84464137b10Smark 		Outchar = OO;
84564137b10Smark 	endim();
84664137b10Smark 	return (gcursor);
84764137b10Smark }
84864137b10Smark 
84964137b10Smark int	vgetsplit();
85064137b10Smark char	*vsplitpt;
85164137b10Smark 
85264137b10Smark /*
85364137b10Smark  * Append the line in buffer at lp
85464137b10Smark  * to the buffer after dot.
85564137b10Smark  */
vdoappend(lp)85664137b10Smark vdoappend(lp)
85764137b10Smark 	char *lp;
85864137b10Smark {
85964137b10Smark 	register int oing = inglobal;
86064137b10Smark 
86164137b10Smark 	vsplitpt = lp;
86264137b10Smark 	inglobal = 1;
86364137b10Smark 	ignore(append(vgetsplit, dot));
86464137b10Smark 	inglobal = oing;
86564137b10Smark }
86664137b10Smark 
86764137b10Smark /*
86864137b10Smark  * Subroutine for vdoappend to pass to append.
86964137b10Smark  */
vgetsplit()87064137b10Smark vgetsplit()
87164137b10Smark {
87264137b10Smark 
87364137b10Smark 	if (vsplitpt == 0)
87464137b10Smark 		return (EOF);
87564137b10Smark 	strcLIN(vsplitpt);
87664137b10Smark 	vsplitpt = 0;
87764137b10Smark 	return (0);
87864137b10Smark }
87964137b10Smark 
88064137b10Smark /*
88164137b10Smark  * Vmaxrep determines the maximum repetitition factor
88264137b10Smark  * allowed that will yield total line length less than
883e41600afSmark  * LBSIZE characters and also does hacks for the R command.
88464137b10Smark  */
vmaxrep(ch,cnt)88564137b10Smark vmaxrep(ch, cnt)
88664137b10Smark 	char ch;
88764137b10Smark 	register int cnt;
88864137b10Smark {
88964137b10Smark 	register int len, replen;
89064137b10Smark 
89164137b10Smark 	if (cnt > LBSIZE - 2)
89264137b10Smark 		cnt = LBSIZE - 2;
89364137b10Smark 	replen = strlen(genbuf);
89464137b10Smark 	if (ch == 'R') {
89564137b10Smark 		len = strlen(cursor);
89664137b10Smark 		if (replen < len)
89764137b10Smark 			len = replen;
89864137b10Smark 		CP(cursor, cursor + len);
89964137b10Smark 		vUD2 += len;
90064137b10Smark 	}
90164137b10Smark 	len = strlen(linebuf);
90264137b10Smark 	if (len + cnt * replen <= LBSIZE - 2)
90364137b10Smark 		return (cnt);
90464137b10Smark 	cnt = (LBSIZE - 2 - len) / replen;
90564137b10Smark 	if (cnt == 0) {
90664137b10Smark 		vsave();
90764137b10Smark 		error("Line too long");
90864137b10Smark 	}
90964137b10Smark 	return (cnt);
91064137b10Smark }
911