xref: /original-bsd/usr.bin/ex/ex_vget.c (revision 6c57d260)
1 /* Copyright (c) 1980 Regents of the University of California */
2 static char *sccsid = "@(#)ex_vget.c	6.2 10/23/80";
3 #include "ex.h"
4 #include "ex_tty.h"
5 #include "ex_vis.h"
6 
7 /*
8  * Input routines for open/visual.
9  * We handle upper case only terminals in visual and reading from the
10  * echo area here as well as notification on large changes
11  * which appears in the echo area.
12  */
13 
14 /*
15  * Return the key.
16  */
17 ungetkey(c)
18 	char c;
19 {
20 
21 	if (Peekkey != ATTN)
22 		Peekkey = c;
23 }
24 
25 /*
26  * Return a keystroke, but never a ^@.
27  */
28 getkey()
29 {
30 	register char c;
31 
32 	do {
33 		c = getbr();
34 		if (c==0)
35 			beep();
36 	} while (c == 0);
37 	return (c);
38 }
39 
40 /*
41  * Tell whether next keystroke would be a ^@.
42  */
43 peekbr()
44 {
45 
46 	Peekkey = getbr();
47 	return (Peekkey == 0);
48 }
49 
50 short	precbksl;
51 
52 /*
53  * Get a keystroke, including a ^@.
54  * If an key was returned with ungetkey, that
55  * comes back first.  Next comes unread input (e.g.
56  * from repeating commands with .), and finally new
57  * keystrokes.
58  *
59  * The hard work here is in mapping of \ escaped
60  * characters on upper case only terminals.
61  */
62 getbr()
63 {
64 	char ch;
65 	register int c, d;
66 	register char *colp;
67 #define BEEHIVE
68 #ifdef BEEHIVE
69 	static char Peek2key;
70 #endif
71 	extern short slevel, ttyindes;
72 
73 getATTN:
74 	if (Peekkey) {
75 		c = Peekkey;
76 		Peekkey = 0;
77 		return (c);
78 	}
79 #ifdef BEEHIVE
80 	if (Peek2key) {
81 		c = Peek2key;
82 		Peek2key = 0;
83 		return (c);
84 	}
85 #endif
86 	if (vglobp) {
87 		if (*vglobp)
88 			return (lastvgk = *vglobp++);
89 		lastvgk = 0;
90 		return (ESCAPE);
91 	}
92 	if (vmacp) {
93 		if (*vmacp)
94 			return(*vmacp++);
95 		/* End of a macro or set of nested macros */
96 		vmacp = 0;
97 		if (inopen == -1)	/* don't screw up undo for esc esc */
98 			vundkind = VMANY;
99 		inopen = 1;	/* restore old setting now that macro done */
100 		vch_mac = VC_NOTINMAC;
101 	}
102 	flusho();
103 again:
104 	if (read(slevel == 0 ? 0 : ttyindes, &ch, 1) != 1) {
105 		if (errno == EINTR)
106 			goto getATTN;
107 		error("Input read error");
108 	}
109 	c = ch & TRIM;
110 #ifdef BEEHIVE
111 	if (XB && slevel==0 && c == ESCAPE) {
112 		if (read(0, &Peek2key, 1) != 1)
113 			goto getATTN;
114 		Peek2key &= TRIM;
115 		switch (Peek2key) {
116 		case 'C':	/* SPOW mode sometimes sends \EC for space */
117 			c = ' ';
118 			Peek2key = 0;
119 			break;
120 		case 'q':	/* f2 -> ^C */
121 			c = CTRL(c);
122 			Peek2key = 0;
123 			break;
124 		case 'p':	/* f1 -> esc */
125 			Peek2key = 0;
126 			break;
127 		}
128 	}
129 #endif
130 
131 #ifdef UCVISUAL
132 	/*
133 	 * The algorithm here is that of the UNIX kernel.
134 	 * See the description in the programmers manual.
135 	 */
136 	if (UPPERCASE) {
137 		if (isupper(c))
138 			c = tolower(c);
139 		if (c == '\\') {
140 			if (precbksl < 2)
141 				precbksl++;
142 			if (precbksl == 1)
143 				goto again;
144 		} else if (precbksl) {
145 			d = 0;
146 			if (islower(c))
147 				d = toupper(c);
148 			else {
149 				colp = "({)}!|^~'~";
150 				while (d = *colp++)
151 					if (d == c) {
152 						d = *colp++;
153 						break;
154 					} else
155 						colp++;
156 			}
157 			if (precbksl == 2) {
158 				if (!d) {
159 					Peekkey = c;
160 					precbksl = 0;
161 					c = '\\';
162 				}
163 			} else if (d)
164 				c = d;
165 			else {
166 				Peekkey = c;
167 				precbksl = 0;
168 				c = '\\';
169 			}
170 		}
171 		if (c != '\\')
172 			precbksl = 0;
173 	}
174 #endif
175 #ifdef TRACE
176 	if (trace) {
177 		if (!techoin) {
178 			tfixnl();
179 			techoin = 1;
180 			fprintf(trace, "*** Input: ");
181 		}
182 		tracec(c);
183 	}
184 #endif
185 	lastvgk = 0;
186 	return (c);
187 }
188 
189 /*
190  * Get a key, but if a delete, quit or attention
191  * is typed return 0 so we will abort a partial command.
192  */
193 getesc()
194 {
195 	register int c;
196 
197 	c = getkey();
198 	switch (c) {
199 
200 	case CTRL(v):
201 	case CTRL(q):
202 		c = getkey();
203 		return (c);
204 
205 	case ATTN:
206 	case QUIT:
207 		ungetkey(c);
208 		return (0);
209 
210 	case ESCAPE:
211 		return (0);
212 	}
213 	return (c);
214 }
215 
216 /*
217  * Peek at the next keystroke.
218  */
219 peekkey()
220 {
221 
222 	Peekkey = getkey();
223 	return (Peekkey);
224 }
225 
226 /*
227  * Read a line from the echo area, with single character prompt c.
228  * A return value of 1 means the user blewit or blewit away.
229  */
230 readecho(c)
231 	char c;
232 {
233 	register char *sc = cursor;
234 	register int (*OP)();
235 	bool waste;
236 	register int OPeek;
237 
238 	if (WBOT == WECHO)
239 		vclean();
240 	else
241 		vclrech(0);
242 	splitw++;
243 	vgoto(WECHO, 0);
244 	putchar(c);
245 	vclreol();
246 	vgoto(WECHO, 1);
247 	cursor = linebuf; linebuf[0] = 0; genbuf[0] = c;
248 	if (peekbr()) {
249 		if (!INS[0] || (INS[0] & (QUOTE|TRIM)) == OVERBUF)
250 			goto blewit;
251 		vglobp = INS;
252 	}
253 	OP = Pline; Pline = normline;
254 	ignore(vgetline(0, genbuf + 1, &waste, c));
255 	if (Outchar == termchar)
256 		putchar('\n');
257 	vscrap();
258 	Pline = OP;
259 	if (Peekkey != ATTN && Peekkey != QUIT && Peekkey != CTRL(h)) {
260 		cursor = sc;
261 		vclreol();
262 		return (0);
263 	}
264 blewit:
265 	OPeek = Peekkey==CTRL(h) ? 0 : Peekkey; Peekkey = 0;
266 	splitw = 0;
267 	vclean();
268 	vshow(dot, NOLINE);
269 	vnline(sc);
270 	Peekkey = OPeek;
271 	return (1);
272 }
273 
274 /*
275  * A complete command has been defined for
276  * the purposes of repeat, so copy it from
277  * the working to the previous command buffer.
278  */
279 setLAST()
280 {
281 
282 	if (vglobp || vmacp)
283 		return;
284 	lastreg = vreg;
285 	lasthad = Xhadcnt;
286 	lastcnt = Xcnt;
287 	*lastcp = 0;
288 	CP(lastcmd, workcmd);
289 }
290 
291 /*
292  * Gather up some more text from an insert.
293  * If the insertion buffer oveflows, then destroy
294  * the repeatability of the insert.
295  */
296 addtext(cp)
297 	char *cp;
298 {
299 
300 	if (vglobp)
301 		return;
302 	addto(INS, cp);
303 	if ((INS[0] & (QUOTE|TRIM)) == OVERBUF)
304 		lastcmd[0] = 0;
305 }
306 
307 setDEL()
308 {
309 
310 	setBUF(DEL);
311 }
312 
313 /*
314  * Put text from cursor upto wcursor in BUF.
315  */
316 setBUF(BUF)
317 	register char *BUF;
318 {
319 	register int c;
320 	register char *wp = wcursor;
321 
322 	c = *wp;
323 	*wp = 0;
324 	BUF[0] = 0;
325 	addto(BUF, cursor);
326 	*wp = c;
327 }
328 
329 addto(buf, str)
330 	register char *buf, *str;
331 {
332 
333 	if ((buf[0] & (QUOTE|TRIM)) == OVERBUF)
334 		return;
335 	if (strlen(buf) + strlen(str) + 1 >= VBSIZE) {
336 		buf[0] = OVERBUF;
337 		return;
338 	}
339 	ignore(strcat(buf, str));
340 }
341 
342 /*
343  * Note a change affecting a lot of lines, or non-visible
344  * lines.  If the parameter must is set, then we only want
345  * to do this for open modes now; return and save for later
346  * notification in visual.
347  */
348 noteit(must)
349 	bool must;
350 {
351 	register int sdl = destline, sdc = destcol;
352 
353 	if (notecnt < 2 || !must && state == VISUAL)
354 		return (0);
355 	splitw++;
356 	if (WBOT == WECHO)
357 		vmoveitup(1, 1);
358 	vigoto(WECHO, 0);
359 	printf("%d %sline", notecnt, notesgn);
360 	if (notecnt > 1)
361 		putchar('s');
362 	if (*notenam) {
363 		printf(" %s", notenam);
364 		if (*(strend(notenam) - 1) != 'e')
365 			putchar('e');
366 		putchar('d');
367 	}
368 	vclreol();
369 	notecnt = 0;
370 	if (state != VISUAL)
371 		vcnt = vcline = 0;
372 	splitw = 0;
373 	if (state == ONEOPEN || state == CRTOPEN)
374 		vup1();
375 	destline = sdl; destcol = sdc;
376 	return (1);
377 }
378 
379 /*
380  * Rrrrringgggggg.
381  * If possible, use flash (VB).
382  */
383 beep()
384 {
385 
386 	if (VB)
387 		vputp(VB, 0);
388 	else
389 		vputc(CTRL(g));
390 }
391 
392 /*
393  * Map the command input character c,
394  * for keypads and labelled keys which do cursor
395  * motions.  I.e. on an adm3a we might map ^K to ^P.
396  * DM1520 for example has a lot of mappable characters.
397  */
398 
399 map(c,maps)
400 	register int c;
401 	register struct maps *maps;
402 {
403 	register int d;
404 	register char *p, *q;
405 	char b[10];	/* Assumption: no keypad sends string longer than 10 */
406 
407 	/*
408 	 * Mapping for special keys on the terminal only.
409 	 * BUG: if there's a long sequence and it matches
410 	 * some chars and then misses, we lose some chars.
411 	 *
412 	 * For this to work, some conditions must be met.
413 	 * 1) Keypad sends SHORT (2 or 3 char) strings
414 	 * 2) All strings sent are same length & similar
415 	 * 3) The user is unlikely to type the first few chars of
416 	 *    one of these strings very fast.
417 	 * Note: some code has been fixed up since the above was laid out,
418 	 * so conditions 1 & 2 are probably not required anymore.
419 	 * However, this hasn't been tested with any first char
420 	 * that means anything else except escape.
421 	 */
422 #ifdef MDEBUG
423 	if (trace)
424 		fprintf(trace,"map(%c): ",c);
425 #endif
426 	/*
427 	 * If c==0, the char came from getesc typing escape.  Pass it through
428 	 * unchanged.  0 messes up the following code anyway.
429 	 */
430 	if (c==0)
431 		return(0);
432 
433 	b[0] = c;
434 	b[1] = 0;
435 	for (d=0; maps[d].mapto; d++) {
436 #ifdef MDEBUG
437 		if (trace)
438 			fprintf(trace,"\ntry '%s', ",maps[d].cap);
439 #endif
440 		if (p = maps[d].cap) {
441 			for (q=b; *p; p++, q++) {
442 #ifdef MDEBUG
443 				if (trace)
444 					fprintf(trace,"q->b[%d], ",q-b);
445 #endif
446 				if (*q==0) {
447 					/*
448 					 * Is there another char waiting?
449 					 *
450 					 * This test is oversimplified, but
451 					 * should work mostly. It handles the
452 					 * case where we get an ESCAPE that
453 					 * wasn't part of a keypad string.
454 					 */
455 					if ((c=='#' ? peekkey() : fastpeekkey()) == 0) {
456 #ifdef MDEBUG
457 						if (trace)
458 							fprintf(trace,"fpk=0: return '%c'",c);
459 #endif
460 						/*
461 						 * Nothing waiting.  Push back
462 						 * what we peeked at & return
463 						 * failure (c).
464 						 *
465 						 * We want to be able to undo
466 						 * commands, but it's nonsense
467 						 * to undo part of an insertion
468 						 * so if in input mode don't.
469 						 */
470 						macpush(&b[1],maps == arrows);
471 						return(c);
472 					}
473 					*q = getkey();
474 					q[1] = 0;
475 				}
476 				if (*p != *q)
477 					goto contin;
478 			}
479 			macpush(maps[d].mapto,maps == arrows);
480 			c = getkey();
481 #ifdef MDEBUG
482 			if (trace)
483 				fprintf(trace,"Success: push(%s), return %c",maps[d].mapto, c);
484 #endif
485 			return(c);	/* first char of map string */
486 			contin:;
487 		}
488 	}
489 #ifdef MDEBUG
490 	if (trace)
491 		fprintf(trace,"Fail: push(%s), return %c", &b[1], c);
492 #endif
493 	macpush(&b[1],0);
494 	return(c);
495 }
496 
497 /*
498  * Push st onto the front of vmacp. This is tricky because we have to
499  * worry about where vmacp was previously pointing. We also have to
500  * check for overflow (which is typically from a recursive macro)
501  * Finally we have to set a flag so the whole thing can be undone.
502  * canundo is 1 iff we want to be able to undo the macro.  This
503  * is false for, for example, pushing back lookahead from fastpeekkey(),
504  * since otherwise two fast escapes can clobber our undo.
505  */
506 macpush(st, canundo)
507 char *st;
508 int canundo;
509 {
510 	char tmpbuf[BUFSIZ];
511 
512 	if (st==0 || *st==0)
513 		return;
514 #ifdef notdef
515 	if (!value(UNDOMACRO))
516 		canundo = 0;
517 #endif
518 #ifdef TRACE
519 	if (trace)
520 		fprintf(trace, "macpush(%s), canundo=%d\n",st,canundo);
521 #endif
522 	if ((vmacp ? strlen(vmacp) : 0) + strlen(st) > BUFSIZ)
523 		error("Macro too long@ - maybe recursive?");
524 	if (vmacp) {
525 		strcpy(tmpbuf, vmacp);
526 		if (!FIXUNDO)
527 			canundo = 0;	/* can't undo inside a macro anyway */
528 	}
529 	strcpy(vmacbuf, st);
530 	if (vmacp)
531 		strcat(vmacbuf, tmpbuf);
532 	vmacp = vmacbuf;
533 	/* arrange to be able to undo the whole macro */
534 	if (canundo) {
535 #ifdef notdef
536 		otchng = tchng;
537 		vsave();
538 		saveall();
539 		inopen = -1;	/* no need to save since it had to be 1 or -1 before */
540 		vundkind = VMANY;
541 #endif
542 		vch_mac = VC_NOCHANGE;
543 	}
544 }
545 
546 #ifdef TRACE
547 visdump(s)
548 char *s;
549 {
550 	register int i;
551 
552 	if (!trace) return;
553 
554 	fprintf(trace, "\n%s: basWTOP=%d, basWLINES=%d, WTOP=%d, WBOT=%d, WLINES=%d, WCOLS=%d, WECHO=%d\n",
555 		s, basWTOP, basWLINES, WTOP, WBOT, WLINES, WCOLS, WECHO);
556 	fprintf(trace, "   vcnt=%d, vcline=%d, cursor=%d, wcursor=%d, wdot=%d\n",
557 		vcnt, vcline, cursor-linebuf, wcursor-linebuf, wdot-zero);
558 	for (i=0; i<TUBELINES; i++)
559 		if (vtube[i] && *vtube[i])
560 			fprintf(trace, "%d: '%s'\n", i, vtube[i]);
561 	tvliny();
562 }
563 
564 vudump(s)
565 char *s;
566 {
567 	register line *p;
568 	char savelb[1024];
569 
570 	if (!trace) return;
571 
572 	fprintf(trace, "\n%s: undkind=%d, vundkind=%d, unddel=%d, undap1=%d, undap2=%d,\n",
573 		s, undkind, vundkind, lineno(unddel), lineno(undap1), lineno(undap2));
574 	fprintf(trace, "  undadot=%d, dot=%d, dol=%d, unddol=%d, truedol=%d\n",
575 		lineno(undadot), lineno(dot), lineno(dol), lineno(unddol), lineno(truedol));
576 	fprintf(trace, "  [\n");
577 	CP(savelb, linebuf);
578 	fprintf(trace, "linebuf = '%s'\n", linebuf);
579 	for (p=zero+1; p<=truedol; p++) {
580 		fprintf(trace, "%o ", *p);
581 		getline(*p);
582 		fprintf(trace, "'%s'\n", linebuf);
583 	}
584 	fprintf(trace, "]\n");
585 	CP(linebuf, savelb);
586 }
587 #endif
588 
589 /*
590  * Get a count from the keyed input stream.
591  * A zero count is indistinguishable from no count.
592  */
593 vgetcnt()
594 {
595 	register int c, cnt;
596 
597 	cnt = 0;
598 	for (;;) {
599 		c = getkey();
600 		if (!isdigit(c))
601 			break;
602 		cnt *= 10, cnt += c - '0';
603 	}
604 	ungetkey(c);
605 	Xhadcnt = 1;
606 	Xcnt = cnt;
607 	return(cnt);
608 }
609 
610 /*
611  * fastpeekkey is just like peekkey but insists the character come in
612  * fast (within 1 second). This will succeed if it is the 2nd char of
613  * a machine generated sequence (such as a function pad from an escape
614  * flavor terminal) but fail for a human hitting escape then waiting.
615  */
616 fastpeekkey()
617 {
618 	int trapalarm();
619 	register int c;
620 
621 	/*
622 	 * If the user has set notimeout, we wait forever for a key.
623 	 * If we are in a macro we do too, but since it's already
624 	 * buffered internally it will return immediately.
625 	 * In other cases we force this to die in 1 second.
626 	 * This is pretty reliable (VMUNIX rounds it to .5 - 1.5 secs,
627 	 * but UNIX truncates it to 0 - 1 secs) but due to system delays
628 	 * there are times when arrow keys or very fast typing get counted
629 	 * as separate.  notimeout is provided for people who dislike such
630 	 * nondeterminism.
631 	 */
632 	if (value(TIMEOUT) && inopen >= 0) {
633 		signal(SIGALRM, trapalarm);
634 		alarm(1);
635 	}
636 	CATCH
637 		c = peekkey();
638 #ifdef MDEBUG
639 	if (trace)
640 		fprintf(trace,"[OK]",c);
641 #endif
642 		alarm(0);
643 	ONERR
644 		c = 0;
645 #ifdef MDEBUG
646 	if (trace)
647 		fprintf(trace,"[TOUT]",c);
648 #endif
649 	ENDCATCH
650 #ifdef MDEBUG
651 	if (trace)
652 		fprintf(trace,"[fpk:%o]",c);
653 #endif
654 	return(c);
655 }
656 
657 trapalarm() {
658 	alarm(0);
659 	longjmp(vreslab,1);
660 }
661