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