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