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