xref: /original-bsd/usr.bin/ex/ex_vget.c (revision e049d966)
1cd8169f0Sbostic /*-
2*e049d966Sbostic  * Copyright (c) 1980, 1993
3*e049d966Sbostic  *	The Regents of the University of California.  All rights reserved.
4cd8169f0Sbostic  *
5cd8169f0Sbostic  * %sccs.include.proprietary.c%
6767d0bbbSdist  */
7767d0bbbSdist 
8767d0bbbSdist #ifndef lint
9*e049d966Sbostic static char sccsid[] = "@(#)ex_vget.c	8.1 (Berkeley) 06/09/93";
10cd8169f0Sbostic #endif /* not lint */
11767d0bbbSdist 
124e1fedf7Smark #include "ex.h"
134e1fedf7Smark #include "ex_tty.h"
144e1fedf7Smark #include "ex_vis.h"
154e1fedf7Smark 
164e1fedf7Smark /*
174e1fedf7Smark  * Input routines for open/visual.
184e1fedf7Smark  * We handle upper case only terminals in visual and reading from the
194e1fedf7Smark  * echo area here as well as notification on large changes
204e1fedf7Smark  * which appears in the echo area.
214e1fedf7Smark  */
224e1fedf7Smark 
234e1fedf7Smark /*
244e1fedf7Smark  * Return the key.
254e1fedf7Smark  */
ungetkey(c)264e1fedf7Smark ungetkey(c)
27c93a6a15Sdist 	int c;		/* mjm: char --> int */
284e1fedf7Smark {
294e1fedf7Smark 
30c0a5300eSconrad 	if (Peek_key != ATTN)
31c0a5300eSconrad 		Peek_key = c;
324e1fedf7Smark }
334e1fedf7Smark 
344e1fedf7Smark /*
354e1fedf7Smark  * Return a keystroke, but never a ^@.
364e1fedf7Smark  */
getkey()374e1fedf7Smark getkey()
384e1fedf7Smark {
39c93a6a15Sdist 	register int c;		/* mjm: char --> int */
404e1fedf7Smark 
414e1fedf7Smark 	do {
424e1fedf7Smark 		c = getbr();
434e1fedf7Smark 		if (c==0)
444e1fedf7Smark 			beep();
454e1fedf7Smark 	} while (c == 0);
464e1fedf7Smark 	return (c);
474e1fedf7Smark }
484e1fedf7Smark 
494e1fedf7Smark /*
504e1fedf7Smark  * Tell whether next keystroke would be a ^@.
514e1fedf7Smark  */
peekbr()524e1fedf7Smark peekbr()
534e1fedf7Smark {
544e1fedf7Smark 
55c0a5300eSconrad 	Peek_key = getbr();
56c0a5300eSconrad 	return (Peek_key == 0);
574e1fedf7Smark }
584e1fedf7Smark 
594e1fedf7Smark short	precbksl;
60c93a6a15Sdist jmp_buf	readbuf;
61c93a6a15Sdist int	doingread = 0;
624e1fedf7Smark 
634e1fedf7Smark /*
644e1fedf7Smark  * Get a keystroke, including a ^@.
654e1fedf7Smark  * If an key was returned with ungetkey, that
664e1fedf7Smark  * comes back first.  Next comes unread input (e.g.
674e1fedf7Smark  * from repeating commands with .), and finally new
684e1fedf7Smark  * keystrokes.
694e1fedf7Smark  *
704e1fedf7Smark  * The hard work here is in mapping of \ escaped
714e1fedf7Smark  * characters on upper case only terminals.
724e1fedf7Smark  */
getbr()734e1fedf7Smark getbr()
744e1fedf7Smark {
754e1fedf7Smark 	char ch;
764e1fedf7Smark 	register int c, d;
774e1fedf7Smark 	register char *colp;
782f4283ecSmark #define BEEHIVE
792f4283ecSmark #ifdef BEEHIVE
802f4283ecSmark 	static char Peek2key;
812f4283ecSmark #endif
822f4283ecSmark 	extern short slevel, ttyindes;
834e1fedf7Smark 
844e1fedf7Smark getATTN:
85c0a5300eSconrad 	if (Peek_key) {
86c0a5300eSconrad 		c = Peek_key;
87c0a5300eSconrad 		Peek_key = 0;
884e1fedf7Smark 		return (c);
894e1fedf7Smark 	}
902f4283ecSmark #ifdef BEEHIVE
912f4283ecSmark 	if (Peek2key) {
922f4283ecSmark 		c = Peek2key;
932f4283ecSmark 		Peek2key = 0;
942f4283ecSmark 		return (c);
952f4283ecSmark 	}
962f4283ecSmark #endif
974e1fedf7Smark 	if (vglobp) {
984e1fedf7Smark 		if (*vglobp)
994e1fedf7Smark 			return (lastvgk = *vglobp++);
1004e1fedf7Smark 		lastvgk = 0;
1014e1fedf7Smark 		return (ESCAPE);
1024e1fedf7Smark 	}
1034e1fedf7Smark 	if (vmacp) {
1044e1fedf7Smark 		if (*vmacp)
1054e1fedf7Smark 			return(*vmacp++);
1064e1fedf7Smark 		/* End of a macro or set of nested macros */
1074e1fedf7Smark 		vmacp = 0;
108933430aaSmark 		if (inopen == -1)	/* don't screw up undo for esc esc */
1094e1fedf7Smark 			vundkind = VMANY;
110933430aaSmark 		inopen = 1;	/* restore old setting now that macro done */
1112f4283ecSmark 		vch_mac = VC_NOTINMAC;
1124e1fedf7Smark 	}
1134e1fedf7Smark 	flusho();
1144e1fedf7Smark again:
115c93a6a15Sdist 	if (setjmp(readbuf))
116c93a6a15Sdist 		goto getATTN;
117c93a6a15Sdist 	doingread = 1;
118c0a5300eSconrad #ifndef	vms
119c93a6a15Sdist 	c = read(slevel == 0 ? 0 : ttyindes, &ch, 1);
120c0a5300eSconrad #else
121c0a5300eSconrad 	c = vms_read(slevel == 0 ? 0 : ttyindes, &ch, 1);
122c0a5300eSconrad #endif
123c93a6a15Sdist 	doingread = 0;
124c93a6a15Sdist 	if (c != 1) {
1254e1fedf7Smark 		if (errno == EINTR)
1264e1fedf7Smark 			goto getATTN;
1274e1fedf7Smark 		error("Input read error");
1284e1fedf7Smark 	}
1294e1fedf7Smark 	c = ch & TRIM;
1302f4283ecSmark #ifdef BEEHIVE
1312f4283ecSmark 	if (XB && slevel==0 && c == ESCAPE) {
1322f4283ecSmark 		if (read(0, &Peek2key, 1) != 1)
1332f4283ecSmark 			goto getATTN;
1342f4283ecSmark 		Peek2key &= TRIM;
1352f4283ecSmark 		switch (Peek2key) {
1362f4283ecSmark 		case 'C':	/* SPOW mode sometimes sends \EC for space */
1372f4283ecSmark 			c = ' ';
1382f4283ecSmark 			Peek2key = 0;
1392f4283ecSmark 			break;
1402f4283ecSmark 		case 'q':	/* f2 -> ^C */
1412949201cSbostic 			c = CTRL('c');
1422f4283ecSmark 			Peek2key = 0;
1432f4283ecSmark 			break;
1442f4283ecSmark 		case 'p':	/* f1 -> esc */
1452f4283ecSmark 			Peek2key = 0;
1462f4283ecSmark 			break;
1472f4283ecSmark 		}
1482f4283ecSmark 	}
1492f4283ecSmark #endif
1504e1fedf7Smark 
1514e1fedf7Smark #ifdef UCVISUAL
1524e1fedf7Smark 	/*
1534e1fedf7Smark 	 * The algorithm here is that of the UNIX kernel.
1544e1fedf7Smark 	 * See the description in the programmers manual.
1554e1fedf7Smark 	 */
1564e1fedf7Smark 	if (UPPERCASE) {
1574e1fedf7Smark 		if (isupper(c))
1584e1fedf7Smark 			c = tolower(c);
1594e1fedf7Smark 		if (c == '\\') {
1604e1fedf7Smark 			if (precbksl < 2)
1614e1fedf7Smark 				precbksl++;
1624e1fedf7Smark 			if (precbksl == 1)
1634e1fedf7Smark 				goto again;
1644e1fedf7Smark 		} else if (precbksl) {
1654e1fedf7Smark 			d = 0;
1664e1fedf7Smark 			if (islower(c))
1674e1fedf7Smark 				d = toupper(c);
1684e1fedf7Smark 			else {
1694e1fedf7Smark 				colp = "({)}!|^~'~";
1704e1fedf7Smark 				while (d = *colp++)
1714e1fedf7Smark 					if (d == c) {
1724e1fedf7Smark 						d = *colp++;
1734e1fedf7Smark 						break;
1744e1fedf7Smark 					} else
1754e1fedf7Smark 						colp++;
1764e1fedf7Smark 			}
1774e1fedf7Smark 			if (precbksl == 2) {
1784e1fedf7Smark 				if (!d) {
179c0a5300eSconrad 					Peek_key = c;
1804e1fedf7Smark 					precbksl = 0;
1814e1fedf7Smark 					c = '\\';
1824e1fedf7Smark 				}
1834e1fedf7Smark 			} else if (d)
1844e1fedf7Smark 				c = d;
1854e1fedf7Smark 			else {
186c0a5300eSconrad 				Peek_key = c;
1874e1fedf7Smark 				precbksl = 0;
1884e1fedf7Smark 				c = '\\';
1894e1fedf7Smark 			}
1904e1fedf7Smark 		}
1914e1fedf7Smark 		if (c != '\\')
1924e1fedf7Smark 			precbksl = 0;
1934e1fedf7Smark 	}
1944e1fedf7Smark #endif
1954e1fedf7Smark #ifdef TRACE
1964e1fedf7Smark 	if (trace) {
1974e1fedf7Smark 		if (!techoin) {
1984e1fedf7Smark 			tfixnl();
1994e1fedf7Smark 			techoin = 1;
2004e1fedf7Smark 			fprintf(trace, "*** Input: ");
2014e1fedf7Smark 		}
2024e1fedf7Smark 		tracec(c);
2034e1fedf7Smark 	}
2044e1fedf7Smark #endif
2054e1fedf7Smark 	lastvgk = 0;
2064e1fedf7Smark 	return (c);
2074e1fedf7Smark }
2084e1fedf7Smark 
2094e1fedf7Smark /*
2104e1fedf7Smark  * Get a key, but if a delete, quit or attention
2114e1fedf7Smark  * is typed return 0 so we will abort a partial command.
2124e1fedf7Smark  */
getesc()2134e1fedf7Smark getesc()
2144e1fedf7Smark {
2154e1fedf7Smark 	register int c;
2164e1fedf7Smark 
2174e1fedf7Smark 	c = getkey();
2184e1fedf7Smark 	switch (c) {
2194e1fedf7Smark 
2202949201cSbostic 	case CTRL('v'):
2212949201cSbostic 	case CTRL('q'):
22214b70d95Smark 		c = getkey();
22314b70d95Smark 		return (c);
22414b70d95Smark 
2254e1fedf7Smark 	case ATTN:
2264e1fedf7Smark 	case QUIT:
2274e1fedf7Smark 		ungetkey(c);
2284e1fedf7Smark 		return (0);
2294e1fedf7Smark 
2304e1fedf7Smark 	case ESCAPE:
2314e1fedf7Smark 		return (0);
2324e1fedf7Smark 	}
2334e1fedf7Smark 	return (c);
2344e1fedf7Smark }
2354e1fedf7Smark 
2364e1fedf7Smark /*
2374e1fedf7Smark  * Peek at the next keystroke.
2384e1fedf7Smark  */
peekkey()2394e1fedf7Smark peekkey()
2404e1fedf7Smark {
2414e1fedf7Smark 
242c0a5300eSconrad 	Peek_key = getkey();
243c0a5300eSconrad 	return (Peek_key);
2444e1fedf7Smark }
2454e1fedf7Smark 
2464e1fedf7Smark /*
2474e1fedf7Smark  * Read a line from the echo area, with single character prompt c.
2484e1fedf7Smark  * A return value of 1 means the user blewit or blewit away.
2494e1fedf7Smark  */
readecho(c)2504e1fedf7Smark readecho(c)
2514e1fedf7Smark 	char c;
2524e1fedf7Smark {
2534e1fedf7Smark 	register char *sc = cursor;
2544e1fedf7Smark 	register int (*OP)();
2554e1fedf7Smark 	bool waste;
2564e1fedf7Smark 	register int OPeek;
2574e1fedf7Smark 
2584e1fedf7Smark 	if (WBOT == WECHO)
2594e1fedf7Smark 		vclean();
2604e1fedf7Smark 	else
2614e1fedf7Smark 		vclrech(0);
2624e1fedf7Smark 	splitw++;
2634e1fedf7Smark 	vgoto(WECHO, 0);
264c0a5300eSconrad 	ex_putchar(c);
2654e1fedf7Smark 	vclreol();
2664e1fedf7Smark 	vgoto(WECHO, 1);
2674e1fedf7Smark 	cursor = linebuf; linebuf[0] = 0; genbuf[0] = c;
2684e1fedf7Smark 	if (peekbr()) {
2694e1fedf7Smark 		if (!INS[0] || (INS[0] & (QUOTE|TRIM)) == OVERBUF)
2704e1fedf7Smark 			goto blewit;
2714e1fedf7Smark 		vglobp = INS;
2724e1fedf7Smark 	}
2734e1fedf7Smark 	OP = Pline; Pline = normline;
274c93a6a15Sdist 	ignore(vgetline(0, genbuf + 1, &waste, c));
2752f4283ecSmark 	if (Outchar == termchar)
276c0a5300eSconrad 		ex_putchar('\n');
2774e1fedf7Smark 	vscrap();
2784e1fedf7Smark 	Pline = OP;
2792949201cSbostic 	if (Peek_key != ATTN && Peek_key != QUIT && Peek_key != CTRL('h')) {
2804e1fedf7Smark 		cursor = sc;
2814e1fedf7Smark 		vclreol();
2824e1fedf7Smark 		return (0);
2834e1fedf7Smark 	}
2844e1fedf7Smark blewit:
2852949201cSbostic 	OPeek = Peek_key==CTRL('h') ? 0 : Peek_key; Peek_key = 0;
2864e1fedf7Smark 	splitw = 0;
2874e1fedf7Smark 	vclean();
2884e1fedf7Smark 	vshow(dot, NOLINE);
2894e1fedf7Smark 	vnline(sc);
290c0a5300eSconrad 	Peek_key = OPeek;
2914e1fedf7Smark 	return (1);
2924e1fedf7Smark }
2934e1fedf7Smark 
2944e1fedf7Smark /*
2954e1fedf7Smark  * A complete command has been defined for
2964e1fedf7Smark  * the purposes of repeat, so copy it from
2974e1fedf7Smark  * the working to the previous command buffer.
2984e1fedf7Smark  */
setLAST()2994e1fedf7Smark setLAST()
3004e1fedf7Smark {
3014e1fedf7Smark 
3022f4283ecSmark 	if (vglobp || vmacp)
3034e1fedf7Smark 		return;
3044e1fedf7Smark 	lastreg = vreg;
3054e1fedf7Smark 	lasthad = Xhadcnt;
3064e1fedf7Smark 	lastcnt = Xcnt;
3074e1fedf7Smark 	*lastcp = 0;
3084e1fedf7Smark 	CP(lastcmd, workcmd);
3094e1fedf7Smark }
3104e1fedf7Smark 
3114e1fedf7Smark /*
3124e1fedf7Smark  * Gather up some more text from an insert.
3134e1fedf7Smark  * If the insertion buffer oveflows, then destroy
3144e1fedf7Smark  * the repeatability of the insert.
3154e1fedf7Smark  */
addtext(cp)3164e1fedf7Smark addtext(cp)
3174e1fedf7Smark 	char *cp;
3184e1fedf7Smark {
3194e1fedf7Smark 
3204e1fedf7Smark 	if (vglobp)
3214e1fedf7Smark 		return;
3224e1fedf7Smark 	addto(INS, cp);
3234e1fedf7Smark 	if ((INS[0] & (QUOTE|TRIM)) == OVERBUF)
3244e1fedf7Smark 		lastcmd[0] = 0;
3254e1fedf7Smark }
3264e1fedf7Smark 
setDEL()3274e1fedf7Smark setDEL()
3284e1fedf7Smark {
3294e1fedf7Smark 
330c0a5300eSconrad 	ex_setBUF(DEL);
3314e1fedf7Smark }
3324e1fedf7Smark 
3334e1fedf7Smark /*
3344e1fedf7Smark  * Put text from cursor upto wcursor in BUF.
3354e1fedf7Smark  */
ex_setBUF(BUF)336c0a5300eSconrad ex_setBUF(BUF)
3374e1fedf7Smark 	register char *BUF;
3384e1fedf7Smark {
3394e1fedf7Smark 	register int c;
3404e1fedf7Smark 	register char *wp = wcursor;
3414e1fedf7Smark 
3424e1fedf7Smark 	c = *wp;
3434e1fedf7Smark 	*wp = 0;
3444e1fedf7Smark 	BUF[0] = 0;
3454e1fedf7Smark 	addto(BUF, cursor);
3464e1fedf7Smark 	*wp = c;
3474e1fedf7Smark }
3484e1fedf7Smark 
addto(buf,str)3494e1fedf7Smark addto(buf, str)
3504e1fedf7Smark 	register char *buf, *str;
3514e1fedf7Smark {
3524e1fedf7Smark 
3534e1fedf7Smark 	if ((buf[0] & (QUOTE|TRIM)) == OVERBUF)
3544e1fedf7Smark 		return;
3554e1fedf7Smark 	if (strlen(buf) + strlen(str) + 1 >= VBSIZE) {
3564e1fedf7Smark 		buf[0] = OVERBUF;
3574e1fedf7Smark 		return;
3584e1fedf7Smark 	}
3594e1fedf7Smark 	ignore(strcat(buf, str));
3604e1fedf7Smark }
3614e1fedf7Smark 
3624e1fedf7Smark /*
3634e1fedf7Smark  * Note a change affecting a lot of lines, or non-visible
3644e1fedf7Smark  * lines.  If the parameter must is set, then we only want
3654e1fedf7Smark  * to do this for open modes now; return and save for later
3664e1fedf7Smark  * notification in visual.
3674e1fedf7Smark  */
noteit(must)3684e1fedf7Smark noteit(must)
3694e1fedf7Smark 	bool must;
3704e1fedf7Smark {
3714e1fedf7Smark 	register int sdl = destline, sdc = destcol;
3724e1fedf7Smark 
3734e1fedf7Smark 	if (notecnt < 2 || !must && state == VISUAL)
3744e1fedf7Smark 		return (0);
3754e1fedf7Smark 	splitw++;
3764e1fedf7Smark 	if (WBOT == WECHO)
3774e1fedf7Smark 		vmoveitup(1, 1);
3784e1fedf7Smark 	vigoto(WECHO, 0);
379c0a5300eSconrad 	ex_printf("%d %sline", notecnt, notesgn);
3804e1fedf7Smark 	if (notecnt > 1)
381c0a5300eSconrad 		ex_putchar('s');
3824e1fedf7Smark 	if (*notenam) {
383c0a5300eSconrad 		ex_printf(" %s", notenam);
3844e1fedf7Smark 		if (*(strend(notenam) - 1) != 'e')
385c0a5300eSconrad 			ex_putchar('e');
386c0a5300eSconrad 		ex_putchar('d');
3874e1fedf7Smark 	}
3884e1fedf7Smark 	vclreol();
3894e1fedf7Smark 	notecnt = 0;
3904e1fedf7Smark 	if (state != VISUAL)
3914e1fedf7Smark 		vcnt = vcline = 0;
3924e1fedf7Smark 	splitw = 0;
3934e1fedf7Smark 	if (state == ONEOPEN || state == CRTOPEN)
3944e1fedf7Smark 		vup1();
3954e1fedf7Smark 	destline = sdl; destcol = sdc;
3964e1fedf7Smark 	return (1);
3974e1fedf7Smark }
3984e1fedf7Smark 
3994e1fedf7Smark /*
4004e1fedf7Smark  * Rrrrringgggggg.
4014e1fedf7Smark  * If possible, use flash (VB).
4024e1fedf7Smark  */
beep()4034e1fedf7Smark beep()
4044e1fedf7Smark {
4054e1fedf7Smark 
4064e1fedf7Smark 	if (VB)
4074e1fedf7Smark 		vputp(VB, 0);
4084e1fedf7Smark 	else
4092949201cSbostic 		vputc(CTRL('g'));
4104e1fedf7Smark }
4114e1fedf7Smark 
4124e1fedf7Smark /*
4134e1fedf7Smark  * Map the command input character c,
4144e1fedf7Smark  * for keypads and labelled keys which do cursor
4154e1fedf7Smark  * motions.  I.e. on an adm3a we might map ^K to ^P.
4164e1fedf7Smark  * DM1520 for example has a lot of mappable characters.
4174e1fedf7Smark  */
4184e1fedf7Smark 
map(c,maps)4194e1fedf7Smark map(c,maps)
4204e1fedf7Smark 	register int c;
4214e1fedf7Smark 	register struct maps *maps;
4224e1fedf7Smark {
4234e1fedf7Smark 	register int d;
4244e1fedf7Smark 	register char *p, *q;
4254e1fedf7Smark 	char b[10];	/* Assumption: no keypad sends string longer than 10 */
4264e1fedf7Smark 
4274e1fedf7Smark 	/*
4284e1fedf7Smark 	 * Mapping for special keys on the terminal only.
4294e1fedf7Smark 	 * BUG: if there's a long sequence and it matches
4304e1fedf7Smark 	 * some chars and then misses, we lose some chars.
4314e1fedf7Smark 	 *
4324e1fedf7Smark 	 * For this to work, some conditions must be met.
4334e1fedf7Smark 	 * 1) Keypad sends SHORT (2 or 3 char) strings
4344e1fedf7Smark 	 * 2) All strings sent are same length & similar
4354e1fedf7Smark 	 * 3) The user is unlikely to type the first few chars of
4364e1fedf7Smark 	 *    one of these strings very fast.
4374e1fedf7Smark 	 * Note: some code has been fixed up since the above was laid out,
4384e1fedf7Smark 	 * so conditions 1 & 2 are probably not required anymore.
4394e1fedf7Smark 	 * However, this hasn't been tested with any first char
4404e1fedf7Smark 	 * that means anything else except escape.
4414e1fedf7Smark 	 */
4424e1fedf7Smark #ifdef MDEBUG
4434e1fedf7Smark 	if (trace)
4444e1fedf7Smark 		fprintf(trace,"map(%c): ",c);
4454e1fedf7Smark #endif
4462f4283ecSmark 	/*
4472f4283ecSmark 	 * If c==0, the char came from getesc typing escape.  Pass it through
4482f4283ecSmark 	 * unchanged.  0 messes up the following code anyway.
4492f4283ecSmark 	 */
4502f4283ecSmark 	if (c==0)
4512f4283ecSmark 		return(0);
4522f4283ecSmark 
4534e1fedf7Smark 	b[0] = c;
4544e1fedf7Smark 	b[1] = 0;
4554e1fedf7Smark 	for (d=0; maps[d].mapto; d++) {
4564e1fedf7Smark #ifdef MDEBUG
4574e1fedf7Smark 		if (trace)
4582f4283ecSmark 			fprintf(trace,"\ntry '%s', ",maps[d].cap);
4594e1fedf7Smark #endif
4604e1fedf7Smark 		if (p = maps[d].cap) {
4614e1fedf7Smark 			for (q=b; *p; p++, q++) {
4624e1fedf7Smark #ifdef MDEBUG
4634e1fedf7Smark 				if (trace)
4644e1fedf7Smark 					fprintf(trace,"q->b[%d], ",q-b);
4654e1fedf7Smark #endif
4664e1fedf7Smark 				if (*q==0) {
4674e1fedf7Smark 					/*
4682f4283ecSmark 					 * Is there another char waiting?
4692f4283ecSmark 					 *
4704e1fedf7Smark 					 * This test is oversimplified, but
4714e1fedf7Smark 					 * should work mostly. It handles the
4724e1fedf7Smark 					 * case where we get an ESCAPE that
4734e1fedf7Smark 					 * wasn't part of a keypad string.
4744e1fedf7Smark 					 */
4754e1fedf7Smark 					if ((c=='#' ? peekkey() : fastpeekkey()) == 0) {
4764e1fedf7Smark #ifdef MDEBUG
4774e1fedf7Smark 						if (trace)
478c93a6a15Sdist 							fprintf(trace,"fpk=0: will return '%c'",c);
4794e1fedf7Smark #endif
48014b70d95Smark 						/*
4812f4283ecSmark 						 * Nothing waiting.  Push back
4822f4283ecSmark 						 * what we peeked at & return
4832f4283ecSmark 						 * failure (c).
4842f4283ecSmark 						 *
48514b70d95Smark 						 * We want to be able to undo
48614b70d95Smark 						 * commands, but it's nonsense
48714b70d95Smark 						 * to undo part of an insertion
48814b70d95Smark 						 * so if in input mode don't.
48914b70d95Smark 						 */
490c93a6a15Sdist #ifdef MDEBUG
491c93a6a15Sdist 						if (trace)
492c93a6a15Sdist 							fprintf(trace, "Call macpush, b %d %d %d\n", b[0], b[1], b[2]);
493c93a6a15Sdist #endif
49414b70d95Smark 						macpush(&b[1],maps == arrows);
495c93a6a15Sdist #ifdef MDEBUG
496c93a6a15Sdist 						if (trace)
497c93a6a15Sdist 							fprintf(trace, "return %d\n", c);
498c93a6a15Sdist #endif
4994e1fedf7Smark 						return(c);
5004e1fedf7Smark 					}
5014e1fedf7Smark 					*q = getkey();
5024e1fedf7Smark 					q[1] = 0;
5034e1fedf7Smark 				}
5044e1fedf7Smark 				if (*p != *q)
5054e1fedf7Smark 					goto contin;
5064e1fedf7Smark 			}
5072f4283ecSmark 			macpush(maps[d].mapto,maps == arrows);
5084e1fedf7Smark 			c = getkey();
5094e1fedf7Smark #ifdef MDEBUG
5104e1fedf7Smark 			if (trace)
5112f4283ecSmark 				fprintf(trace,"Success: push(%s), return %c",maps[d].mapto, c);
5124e1fedf7Smark #endif
5134e1fedf7Smark 			return(c);	/* first char of map string */
5144e1fedf7Smark 			contin:;
5154e1fedf7Smark 		}
5164e1fedf7Smark 	}
5174e1fedf7Smark #ifdef MDEBUG
5184e1fedf7Smark 	if (trace)
5192f4283ecSmark 		fprintf(trace,"Fail: push(%s), return %c", &b[1], c);
5204e1fedf7Smark #endif
521933430aaSmark 	macpush(&b[1],0);
5224e1fedf7Smark 	return(c);
5234e1fedf7Smark }
5244e1fedf7Smark 
5254e1fedf7Smark /*
5264e1fedf7Smark  * Push st onto the front of vmacp. This is tricky because we have to
5274e1fedf7Smark  * worry about where vmacp was previously pointing. We also have to
5284e1fedf7Smark  * check for overflow (which is typically from a recursive macro)
5294e1fedf7Smark  * Finally we have to set a flag so the whole thing can be undone.
530933430aaSmark  * canundo is 1 iff we want to be able to undo the macro.  This
531933430aaSmark  * is false for, for example, pushing back lookahead from fastpeekkey(),
532933430aaSmark  * since otherwise two fast escapes can clobber our undo.
5334e1fedf7Smark  */
macpush(st,canundo)534933430aaSmark macpush(st, canundo)
5354e1fedf7Smark char *st;
536933430aaSmark int canundo;
5374e1fedf7Smark {
5384e1fedf7Smark 	char tmpbuf[BUFSIZ];
5394e1fedf7Smark 
5404e1fedf7Smark 	if (st==0 || *st==0)
5414e1fedf7Smark 		return;
542c93a6a15Sdist #ifdef MDEBUG
5434e1fedf7Smark 	if (trace)
5442f4283ecSmark 		fprintf(trace, "macpush(%s), canundo=%d\n",st,canundo);
5454e1fedf7Smark #endif
54614b70d95Smark 	if ((vmacp ? strlen(vmacp) : 0) + strlen(st) > BUFSIZ)
5474e1fedf7Smark 		error("Macro too long@ - maybe recursive?");
548933430aaSmark 	if (vmacp) {
5494e1fedf7Smark 		strcpy(tmpbuf, vmacp);
55014b70d95Smark 		if (!FIXUNDO)
551933430aaSmark 			canundo = 0;	/* can't undo inside a macro anyway */
552933430aaSmark 	}
5534e1fedf7Smark 	strcpy(vmacbuf, st);
5544e1fedf7Smark 	if (vmacp)
5554e1fedf7Smark 		strcat(vmacbuf, tmpbuf);
5564e1fedf7Smark 	vmacp = vmacbuf;
5574e1fedf7Smark 	/* arrange to be able to undo the whole macro */
558933430aaSmark 	if (canundo) {
5592f4283ecSmark #ifdef notdef
5604e1fedf7Smark 		otchng = tchng;
561933430aaSmark 		vsave();
5624e1fedf7Smark 		saveall();
56314b70d95Smark 		inopen = -1;	/* no need to save since it had to be 1 or -1 before */
5644e1fedf7Smark 		vundkind = VMANY;
5652f4283ecSmark #endif
5662f4283ecSmark 		vch_mac = VC_NOCHANGE;
567933430aaSmark 	}
5684e1fedf7Smark }
5694e1fedf7Smark 
57014b70d95Smark #ifdef TRACE
visdump(s)5712f4283ecSmark visdump(s)
5722f4283ecSmark char *s;
5732f4283ecSmark {
5742f4283ecSmark 	register int i;
5752f4283ecSmark 
5762f4283ecSmark 	if (!trace) return;
5772f4283ecSmark 
5782f4283ecSmark 	fprintf(trace, "\n%s: basWTOP=%d, basWLINES=%d, WTOP=%d, WBOT=%d, WLINES=%d, WCOLS=%d, WECHO=%d\n",
5792f4283ecSmark 		s, basWTOP, basWLINES, WTOP, WBOT, WLINES, WCOLS, WECHO);
5802f4283ecSmark 	fprintf(trace, "   vcnt=%d, vcline=%d, cursor=%d, wcursor=%d, wdot=%d\n",
5812f4283ecSmark 		vcnt, vcline, cursor-linebuf, wcursor-linebuf, wdot-zero);
5822f4283ecSmark 	for (i=0; i<TUBELINES; i++)
5832f4283ecSmark 		if (vtube[i] && *vtube[i])
5842f4283ecSmark 			fprintf(trace, "%d: '%s'\n", i, vtube[i]);
5852f4283ecSmark 	tvliny();
5862f4283ecSmark }
5872f4283ecSmark 
vudump(s)58814b70d95Smark vudump(s)
58914b70d95Smark char *s;
59014b70d95Smark {
5912f4283ecSmark 	register line *p;
592c93a6a15Sdist 	char savelb[1024];
5932f4283ecSmark 
5942f4283ecSmark 	if (!trace) return;
5952f4283ecSmark 
5962f4283ecSmark 	fprintf(trace, "\n%s: undkind=%d, vundkind=%d, unddel=%d, undap1=%d, undap2=%d,\n",
5972f4283ecSmark 		s, undkind, vundkind, lineno(unddel), lineno(undap1), lineno(undap2));
5982f4283ecSmark 	fprintf(trace, "  undadot=%d, dot=%d, dol=%d, unddol=%d, truedol=%d\n",
5992f4283ecSmark 		lineno(undadot), lineno(dot), lineno(dol), lineno(unddol), lineno(truedol));
600c93a6a15Sdist 	fprintf(trace, "  [\n");
601c93a6a15Sdist 	CP(savelb, linebuf);
602c93a6a15Sdist 	fprintf(trace, "linebuf = '%s'\n", linebuf);
603c93a6a15Sdist 	for (p=zero+1; p<=truedol; p++) {
6042f4283ecSmark 		fprintf(trace, "%o ", *p);
605c93a6a15Sdist 		getline(*p);
606c93a6a15Sdist 		fprintf(trace, "'%s'\n", linebuf);
607c93a6a15Sdist 	}
6082f4283ecSmark 	fprintf(trace, "]\n");
609c93a6a15Sdist 	CP(linebuf, savelb);
61014b70d95Smark }
61114b70d95Smark #endif
61214b70d95Smark 
6134e1fedf7Smark /*
6144e1fedf7Smark  * Get a count from the keyed input stream.
6154e1fedf7Smark  * A zero count is indistinguishable from no count.
6164e1fedf7Smark  */
vgetcnt()6174e1fedf7Smark vgetcnt()
6184e1fedf7Smark {
6194e1fedf7Smark 	register int c, cnt;
6204e1fedf7Smark 
6214e1fedf7Smark 	cnt = 0;
6224e1fedf7Smark 	for (;;) {
6234e1fedf7Smark 		c = getkey();
6244e1fedf7Smark 		if (!isdigit(c))
6254e1fedf7Smark 			break;
6264e1fedf7Smark 		cnt *= 10, cnt += c - '0';
6274e1fedf7Smark 	}
6284e1fedf7Smark 	ungetkey(c);
6294e1fedf7Smark 	Xhadcnt = 1;
6304e1fedf7Smark 	Xcnt = cnt;
6314e1fedf7Smark 	return(cnt);
6324e1fedf7Smark }
6334e1fedf7Smark 
6344e1fedf7Smark /*
6354e1fedf7Smark  * fastpeekkey is just like peekkey but insists the character come in
6364e1fedf7Smark  * fast (within 1 second). This will succeed if it is the 2nd char of
6374e1fedf7Smark  * a machine generated sequence (such as a function pad from an escape
6384e1fedf7Smark  * flavor terminal) but fail for a human hitting escape then waiting.
6394e1fedf7Smark  */
fastpeekkey()6404e1fedf7Smark fastpeekkey()
6414e1fedf7Smark {
642b085448bSbostic 	void trapalarm();
643b085448bSbostic 	sig_t Oint;
6444e1fedf7Smark 	register int c;
6454e1fedf7Smark 
64614b70d95Smark 	/*
64714b70d95Smark 	 * If the user has set notimeout, we wait forever for a key.
64814b70d95Smark 	 * If we are in a macro we do too, but since it's already
64914b70d95Smark 	 * buffered internally it will return immediately.
65014b70d95Smark 	 * In other cases we force this to die in 1 second.
65114b70d95Smark 	 * This is pretty reliable (VMUNIX rounds it to .5 - 1.5 secs,
65214b70d95Smark 	 * but UNIX truncates it to 0 - 1 secs) but due to system delays
65314b70d95Smark 	 * there are times when arrow keys or very fast typing get counted
65414b70d95Smark 	 * as separate.  notimeout is provided for people who dislike such
65514b70d95Smark 	 * nondeterminism.
65614b70d95Smark 	 */
657c93a6a15Sdist #ifdef MDEBUG
658c93a6a15Sdist 	if (trace)
659dff40de8Sbostic 		fprintf(trace,"\nfastpeekkey: ");
660c93a6a15Sdist #endif
661c93a6a15Sdist 	Oint = signal(SIGINT, trapalarm);
662dff40de8Sbostic 	CATCH
66314b70d95Smark 		if (value(TIMEOUT) && inopen >= 0) {
6644e1fedf7Smark 			signal(SIGALRM, trapalarm);
665c93a6a15Sdist #ifdef MDEBUG
666c93a6a15Sdist 			alarm(10);
667c93a6a15Sdist 			if (trace)
668c93a6a15Sdist 				fprintf(trace, "set alarm ");
669c93a6a15Sdist #else
6704e1fedf7Smark 			alarm(1);
671c93a6a15Sdist #endif
67214b70d95Smark 		}
6734e1fedf7Smark 		c = peekkey();
674dff40de8Sbostic 		alarm(0);
6754e1fedf7Smark #ifdef MDEBUG
6764e1fedf7Smark 		if (trace)
677c8368a91Sbostic 			fprintf(trace,"[OK]");
6784e1fedf7Smark #endif
6794e1fedf7Smark 	ONERR
6804e1fedf7Smark 		c = 0;
6814e1fedf7Smark #ifdef MDEBUG
6824e1fedf7Smark 		if (trace)
683c8368a91Sbostic 			fprintf(trace,"[TIMEOUT]");
6844e1fedf7Smark #endif
6854e1fedf7Smark 	ENDCATCH
6864e1fedf7Smark #ifdef MDEBUG
6874e1fedf7Smark 	if (trace)
6884e1fedf7Smark 		fprintf(trace,"[fpk:%o]",c);
6894e1fedf7Smark #endif
690c93a6a15Sdist 	signal(SIGINT,Oint);
6914e1fedf7Smark 	return(c);
6924e1fedf7Smark }
6934e1fedf7Smark 
694b085448bSbostic void
trapalarm()6954e1fedf7Smark trapalarm() {
6964e1fedf7Smark 	alarm(0);
697c93a6a15Sdist 	if (vcatch)
6984e1fedf7Smark 		longjmp(vreslab,1);
6994e1fedf7Smark }
700