1 /*
2  * This code contains changes by
3  *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
4  *
5  * Conditions 1, 2, and 4 and the no-warranty notice below apply
6  * to these changes.
7  *
8  *
9  * Copyright (c) 1980, 1993
10  * 	The Regents of the University of California.  All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  * 	This product includes software developed by the University of
23  * 	California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  *
41  * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  *   Redistributions of source code and documentation must retain the
47  *    above copyright notice, this list of conditions and the following
48  *    disclaimer.
49  *   Redistributions in binary form must reproduce the above copyright
50  *    notice, this list of conditions and the following disclaimer in the
51  *    documentation and/or other materials provided with the distribution.
52  *   All advertising materials mentioning features or use of this software
53  *    must display the following acknowledgement:
54  *      This product includes software developed or owned by Caldera
55  *      International, Inc.
56  *   Neither the name of Caldera International, Inc. nor the names of
57  *    other contributors may be used to endorse or promote products
58  *    derived from this software without specific prior written permission.
59  *
60  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
61  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
62  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
63  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
64  * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
65  * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
66  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
67  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
68  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
69  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
70  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
71  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
72  */
73 
74 #ifndef	lint
75 #ifdef	DOSCCS
76 static char sccsid[] = "@(#)ex_vget.c	1.29 (gritter) 2/15/05";
77 #endif
78 #endif
79 
80 /* from ex_vget.c	6.8.1 (2.11BSD GTE) 12/9/94 */
81 
82 #include "ex.h"
83 #include "ex_tty.h"
84 #include "ex_vis.h"
85 
86 /*
87  * Input routines for open/visual.
88  * We handle reading from the echo area here as well as notification on
89  * large changes which appears in the echo area.
90  */
91 
92 /*
93  * Return the key.
94  */
95 void
ungetkey(int c)96 ungetkey (
97     int c		/* mjm: char --> int */
98 )
99 {
100 
101 	if (Peekkey != ATTN)
102 		Peekkey = c;
103 }
104 
105 /*
106  * Return a keystroke, but never a ^@.
107  */
108 int
getkey(void)109 getkey(void)
110 {
111 	register int c;		/* mjm: char --> int */
112 
113 	do {
114 		c = getbr();
115 		if (c==0)
116 			beep();
117 	} while (c == 0);
118 	return (c);
119 }
120 
121 /*
122  * Tell whether next keystroke would be a ^@.
123  */
124 int
peekbr(void)125 peekbr(void)
126 {
127 
128 	Peekkey = getbr();
129 	return (Peekkey == 0);
130 }
131 
132 short	precbksl;
133 JMP_BUF	readbuf;
134 int	doingread = 0;
135 
136 static int
readwc(int fd,int * cp)137 readwc(int fd, int *cp)
138 {
139 	int	c;
140 	char	b;
141 
142 #ifdef	MB
143 	if (mb_cur_max > 1) {
144 		static char	pbuf[2][MB_LEN_MAX], *pend[2], *pcur[2];
145 		static mbstate_t	state[2];
146 		static int	incompl[2];
147 		int	i, rest;
148 		int	idx = fd ? 1 : 0;
149 		wchar_t	wc;
150 		size_t	sz;
151 
152 		i = 0;
153 		rest = pend[idx] - pcur[idx];
154 		if (rest && pcur[idx] > pbuf[idx]) {
155 			do
156 				pbuf[idx][i] = pcur[idx][i];
157 			while (i++, --rest);
158 		} else if (incompl[idx]) {
159 			pend[idx] = pcur[idx] = NULL;
160 			return -1;
161 		}
162 		if (i == 0) {
163 			if ((c = read(fd, &b, 1)) <= 0) {
164 				pend[idx] = pcur[idx] = NULL;
165 				return c;
166 			}
167 			pbuf[idx][i++] = b;
168 		}
169 		if (pbuf[idx][0] & 0200) {
170 			sz = 1;
171 			while ((sz = mbrtowc(&wc, pbuf[idx], i, &state[idx]))
172 					== (size_t)-2 && i < mb_cur_max) {
173 				if ((c = read(fd, &b, 1)) <= 0) {
174 					incompl[idx] = 1;
175 					break;
176 				} else
177 					pbuf[idx][i++] = b;
178 				memset(&state[idx], 0, sizeof state[idx]);
179 			}
180 			if (sz == (size_t)-2 || sz == (size_t)-1 ||
181 					!widthok(wc)) {
182 				memset(&state[idx], 0, sizeof state[idx]);
183 				c = 1;
184 				*cp = pbuf[idx][0] | INVBIT;
185 			} else if (sz == 0) {
186 				c = 1;
187 				*cp = wc;
188 			} else {
189 				c = sz;
190 				*cp = wc;
191 			}
192 		} else {
193 			c = 1;
194 			*cp = pbuf[idx][0];
195 		}
196 		pcur[idx] = &pbuf[idx][c];
197 		pend[idx] = &pcur[idx][i-c];
198 		return c;
199 	} else
200 #endif	/* MB */
201 	{
202 		c = read(fd, &b, 1);
203 		*cp = b;
204 		return c;
205 	}
206 }
207 
208 /*
209  * Get a keystroke, including a ^@.
210  * If an key was returned with ungetkey, that
211  * comes back first.  Next comes unread input (e.g.
212  * from repeating commands with .), and finally new
213  * keystrokes.
214  */
215 int
getbr(void)216 getbr(void)
217 {
218 	int ch;
219 	register int c;
220 #ifdef	UCVISUAL
221 	register int d;
222 	register char *colp;
223 #endif
224 #ifdef BEEHIVE
225 	int cnt;
226 	static char Peek2key;
227 #endif
228 	extern short slevel, ttyindes;
229 
230 getATTN:
231 	if (Peekkey) {
232 		c = Peekkey;
233 		Peekkey = 0;
234 		return (c);
235 	}
236 #ifdef BEEHIVE
237 	if (Peek2key) {
238 		c = Peek2key;
239 		Peek2key = 0;
240 		return (c);
241 	}
242 #endif
243 	if (vglobp) {
244 		if (*vglobp)
245 			return (lastvgk = *vglobp++);
246 		lastvgk = 0;
247 		return (ESCAPE);
248 	}
249 	if (vmacp) {
250 		if (*vmacp) {
251 			int	n;
252 			nextc(ch, vmacp, n);
253 			vmacp += n;
254 			return (ch);
255 		}
256 		/* End of a macro or set of nested macros */
257 		vmacp = 0;
258 		if (inopen == -1)	/* don't screw up undo for esc esc */
259 			vundkind = VMANY;
260 		inopen = 1;	/* restore old setting now that macro done */
261 		vch_mac = VC_NOTINMAC;
262 	}
263 	flusho();
264 	for (c =0; abbrevs[c].mapto; c++)
265 		abbrevs[c].hadthis = 0;
266 #ifdef	UCVISUAL
267 again:
268 #endif
269 	if (SETJMP(readbuf))
270 		goto getATTN;
271 	doingread = 1;
272 	c = readwc(slevel == 0 ? 0 : ttyindes, &ch);
273 	doingread = 0;
274 	if (c < 1) {
275 		if (errno == EINTR)
276 			goto getATTN;
277 		error(catgets(catd, 1, 222, "Input read error"));
278 	}
279 	c = ch & TRIM;
280 #ifdef BEEHIVE
281 	if (XB && slevel==0 && c == ESCAPE) {
282 		if (readwc(0, &Peek2key) < 1)
283 			goto getATTN;
284 		Peek2key &= TRIM;
285 		switch (Peek2key) {
286 		case 'C':	/* SPOW mode sometimes sends \EC for space */
287 			c = ' ';
288 			Peek2key = 0;
289 			break;
290 		case 'q':	/* f2 -> ^C */
291 			c = CTRL('c');
292 			Peek2key = 0;
293 			break;
294 		case 'p':	/* f1 -> esc */
295 			Peek2key = 0;
296 			break;
297 		}
298 	}
299 #endif
300 
301 #ifdef UCVISUAL
302         /*
303          * The algorithm here is that of the UNIX kernel.
304          * See the description in the programmers manual.
305          */
306         if (UPPERCASE) {
307                 if (xisupper(c))
308                         c = xtolower(c);
309                 if (c == '\\') {
310                         if (precbksl < 2)
311                                 precbksl++;
312                         if (precbksl == 1)
313                                 goto again;
314                 } else if (precbksl) {
315                         d = 0;
316                         if (xislower(c))
317                                 d = xtoupper(c);
318                         else {
319                                 colp = "({)}!|^~'~";
320                                 while (d = *colp++)
321                                         if (d == c) {
322                                                 d = *colp++;
323                                                 break;
324                                         } else
325                                                 colp++;
326                         }
327                         if (precbksl == 2) {
328                                 if (!d) {
329                                         Peekkey = c;
330                                         precbksl = 0;
331                                         c = '\\';
332                                 }
333                         } else if (d)
334                                 c = d;
335                         else {
336                                 Peekkey = c;
337                                 precbksl = 0;
338                                 c = '\\';
339                         }
340                 }
341                 if (c != '\\')
342                         precbksl = 0;
343         }
344 #endif
345 
346 #ifdef TRACE
347 	if (trace) {
348 		if (!techoin) {
349 			tfixnl();
350 			techoin = 1;
351 			fprintf(trace, "*** Input: ");
352 		}
353 		tracec(c);
354 	}
355 #endif
356 	lastvgk = 0;
357 	return (c);
358 }
359 
360 /*
361  * Get a key, but if a delete, quit or attention
362  * is typed return 0 so we will abort a partial command.
363  */
364 int
getesc(void)365 getesc(void)
366 {
367 	register int c;
368 
369 	c = getkey();
370 	if (c == ATTN)
371 		goto case_ATTN;
372 	switch (c) {
373 
374 	case CTRL('v'):
375 	case CTRL('q'):
376 		c = getkey();
377 		return (c);
378 
379 	case QUIT:
380 case_ATTN:
381 		ungetkey(c);
382 		return (0);
383 
384 	case ESCAPE:
385 		return (0);
386 	}
387 	return (c);
388 }
389 
390 /*
391  * Peek at the next keystroke.
392  */
393 int
peekkey(void)394 peekkey(void)
395 {
396 
397 	Peekkey = getkey();
398 	return (Peekkey);
399 }
400 
401 /*
402  * Read a line from the echo area, with single character prompt c.
403  * A return value of 1 means the user blewit or blewit away.
404  */
405 int
readecho(int c)406 readecho(int c)
407 {
408 	register char *sc = cursor;
409 	register void (*OP)(int);
410 	bool waste;
411 	register int OPeek;
412 
413 	if (WBOT == WECHO)
414 		vclean();
415 	else
416 		vclrech(0);
417 	splitw++;
418 	vgoto(WECHO, 0);
419 	putchar(c);
420 	vclreol();
421 	vgoto(WECHO, 1);
422 	cursor = linebuf; linebuf[0] = 0; genbuf[0] = c;
423 	if (peekbr()) {
424 		if (!INS[0] || (INS[0] & (QUOTE|TRIM)) == OVERBUF)
425 			goto blewit;
426 		vglobp = INS;
427 	}
428 	OP = Pline; Pline = normline;
429 	ignore(vgetline(0, genbuf + 1, &waste, c));
430 	if (Outchar == termchar)
431 		putchar('\n');
432 	vscrap();
433 	Pline = OP;
434 	if (Peekkey != ATTN && Peekkey != QUIT && Peekkey != CTRL('h')) {
435 		cursor = sc;
436 		vclreol();
437 		return (0);
438 	}
439 blewit:
440 	OPeek = Peekkey==CTRL('h') ? 0 : Peekkey; Peekkey = 0;
441 	splitw = 0;
442 	vclean();
443 	vshow(dot, NOLINE);
444 	vnline(sc);
445 	Peekkey = OPeek;
446 	return (1);
447 }
448 
449 /*
450  * A complete command has been defined for
451  * the purposes of repeat, so copy it from
452  * the working to the previous command buffer.
453  */
454 void
setLAST(void)455 setLAST(void)
456 {
457 
458 	if (vglobp || vmacp)
459 		return;
460 	lastreg = vreg;
461 	lasthad = Xhadcnt;
462 	lastcnt = Xcnt;
463 	*lastcp = 0;
464 	cellcpy(lastcmd, workcmd);
465 }
466 
467 /*
468  * Gather up some more text from an insert.
469  * If the insertion buffer oveflows, then destroy
470  * the repeatability of the insert.
471  */
472 void
addtext(char * cp)473 addtext(char *cp)
474 {
475 
476 	if (vglobp)
477 		return;
478 	addto(INS, cp);
479 	if ((INS[0] & (QUOTE|TRIM)) == OVERBUF)
480 		lastcmd[0] = 0;
481 }
482 
483 void
setDEL(void)484 setDEL(void)
485 {
486 
487 	setBUF(DEL);
488 }
489 
490 /*
491  * Put text from cursor upto wcursor in BUF.
492  */
493 void
setBUF(register cell * BUF)494 setBUF(register cell *BUF)
495 {
496 	register int c;
497 	register char *wp = wcursor;
498 
499 	c = *wp;
500 	*wp = 0;
501 	BUF[0] = 0;
502 	addto(BUF, cursor);
503 	*wp = c;
504 }
505 
506 void
addto(register cell * buf,register char * str)507 addto(register cell *buf, register char *str)
508 {
509 
510 	if ((buf[0] & (QUOTE|TRIM)) == OVERBUF)
511 		return;
512 	if (cellen(buf) + strlen(str) + 1 >= VBSIZE) {
513 		buf[0] = OVERBUF;
514 		return;
515 	}
516 	while (*buf)
517 		buf++;
518 	str2cell(buf, str);
519 }
520 
521 /*
522  * Note a change affecting a lot of lines, or non-visible
523  * lines.  If the parameter must is set, then we only want
524  * to do this for open modes now; return and save for later
525  * notification in visual.
526  */
527 int
noteit(int must)528 noteit(int must)
529 {
530 	register int sdl = destline, sdc = destcol;
531 
532 	if (notecnt < 2 || !must && state == VISUAL)
533 		return (0);
534 	splitw++;
535 	if (WBOT == WECHO)
536 		vmoveitup(1, 1);
537 	vigoto(WECHO, 0);
538 	printf(catgets(catd, 1, 223, "%d %sline"), notecnt, notesgn);
539 	if (notecnt > 1)
540 		putchar('s');
541 	if (*notenam) {
542 		printf(" %s", notenam);
543 		if (*(strend(notenam) - 1) != 'e')
544 			putchar('e');
545 		putchar('d');
546 	}
547 	vclreol();
548 	notecnt = 0;
549 	if (state != VISUAL)
550 		vcnt = vcline = 0;
551 	splitw = 0;
552 	if (state == ONEOPEN || state == CRTOPEN)
553 		vup1();
554 	destline = sdl; destcol = sdc;
555 	return (1);
556 }
557 
558 /*
559  * Rrrrringgggggg.
560  * If possible, use flash (VB).
561  */
562 void
beep(void)563 beep(void)
564 {
565 
566 	if (VB && value(FLASH))
567 		vputp(VB, 0);
568 	else
569 		vputc(CTRL('g'));
570 }
571 
572 /*
573  * Push an integer string as a macro.
574  */
575 static void
imacpush(int * ip,int canundo)576 imacpush(int *ip, int canundo)
577 {
578 	char	buf[BUFSIZ], *bp = buf;
579 
580 #ifdef	MB
581 	do {
582 		int	n;
583 		n = wctomb(bp, *ip&TRIM);
584 		bp += n;
585 	} while (*ip++);
586 #else	/* !MB */
587 	while (*bp++ = *ip++);
588 #endif	/* !MB */
589 	macpush(buf, canundo);
590 }
591 
592 /*
593  * Map the command input character c,
594  * for keypads and labelled keys which do cursor
595  * motions.  I.e. on an adm3a we might map ^K to ^P.
596  * DM1520 for example has a lot of mappable characters.
597  */
598 
599 int
map(register int c,register struct maps * maps)600 map(register int c, register struct maps *maps)
601 {
602 	register int d;
603 	register int *p, *q;
604 	int b[10+MB_LEN_MAX];	/* Assumption: no keypad sends string longer than 10 */
605 
606 	/*
607 	 * Mapping for special keys on the terminal only.
608 	 * BUG: if there's a long sequence and it matches
609 	 * some chars and then misses, we lose some chars.
610 	 *
611 	 * For this to work, some conditions must be met.
612 	 * 1) Keypad sends SHORT (2 or 3 char) strings
613 	 * 2) All strings sent are same length & similar
614 	 * 3) The user is unlikely to type the first few chars of
615 	 *    one of these strings very fast.
616 	 * Note: some code has been fixed up since the above was laid out,
617 	 * so conditions 1 & 2 are probably not required anymore.
618 	 * However, this hasn't been tested with any first char
619 	 * that means anything else except escape.
620 	 */
621 #ifdef MDEBUG
622 	if (trace)
623 		fprintf(trace,"map(%c): ",c);
624 #endif
625 	/*
626 	 * If c==0, the char came from getesc typing escape.  Pass it through
627 	 * unchanged.  0 messes up the following code anyway.
628 	 */
629 	if (c==0)
630 		return(0);
631 
632 	b[0] = c;
633 	b[1] = 0;
634 	for (d=0; maps[d].mapto; d++) {
635 #ifdef MDEBUG
636 		if (trace)
637 			fprintf(trace,"\ntry '%s', ",maps[d].cap);
638 #endif
639 		if (p = maps[d].icap) {
640 			for (q=b; *p; p++, q++) {
641 #ifdef MDEBUG
642 				if (trace)
643 					fprintf(trace,"q->b[%d], ",q-b);
644 #endif
645 				if (*q==0) {
646 					/*
647 					 * Is there another char waiting?
648 					 *
649 					 * This test is oversimplified, but
650 					 * should work mostly. It handles the
651 					 * case where we get an ESCAPE that
652 					 * wasn't part of a keypad string.
653 					 */
654 					if ((c=='#' ? peekkey() : fastpeekkey()) == 0) {
655 #ifdef MDEBUG
656 						if (trace)
657 							fprintf(trace,"fpk=0: will return '%c'",c);
658 #endif
659 						/*
660 						 * Nothing waiting.  Push back
661 						 * what we peeked at & return
662 						 * failure (c).
663 						 *
664 						 * We want to be able to undo
665 						 * commands, but it's nonsense
666 						 * to undo part of an insertion
667 						 * so if in input mode don't.
668 						 */
669 #ifdef MDEBUG
670 						if (trace)
671 							fprintf(trace, "Call macpush, b %d %d %d\n", b[0], b[1], b[2]);
672 #endif
673 						imacpush(&b[1],maps == arrows);
674 #ifdef MDEBUG
675 						if (trace)
676 							fprintf(trace, "return %d\n", c);
677 #endif
678 						return(c);
679 					}
680 					*q = getkey();
681 					q[1] = 0;
682 				}
683 				if (*p != *q)
684 					goto contin;
685 			}
686 			macpush(maps[d].mapto,maps == arrows);
687 			c = getkey();
688 #ifdef MDEBUG
689 			if (trace)
690 				fprintf(trace,"Success: push(%s), return %c",maps[d].mapto, c);
691 #endif
692 			return(c);	/* first char of map string */
693 			contin:;
694 		}
695 	}
696 #ifdef MDEBUG
697 	if (trace)
698 		fprintf(trace,"Fail: push(%s), return %c", &b[1], c);
699 #endif
700 	imacpush(&b[1],0);
701 	return(c);
702 }
703 
704 /*
705  * Push st onto the front of vmacp. This is tricky because we have to
706  * worry about where vmacp was previously pointing. We also have to
707  * check for overflow (which is typically from a recursive macro)
708  * Finally we have to set a flag so the whole thing can be undone.
709  * canundo is 1 iff we want to be able to undo the macro.  This
710  * is false for, for example, pushing back lookahead from fastpeekkey(),
711  * since otherwise two fast escapes can clobber our undo.
712  */
713 void
macpush(char * st,int canundo)714 macpush(char *st, int canundo)
715 {
716 	char tmpbuf[BUFSIZ];
717 
718 	if (st==0 || *st==0)
719 		return;
720 #ifdef MDEBUG
721 	if (trace)
722 		fprintf(trace, "macpush(%s), canundo=%d\n",st,canundo);
723 #endif
724 	if ((vmacp ? strlen(vmacp) : 0) + strlen(st) > BUFSIZ)
725 		error(catgets(catd, 1, 224,
726 				"Macro too long@ - maybe recursive?"));
727 	if (vmacp) {
728 		strcpy(tmpbuf, vmacp);
729 		if (!FIXUNDO)
730 			canundo = 0;	/* can't undo inside a macro anyway */
731 	}
732 	strcpy(vmacbuf, st);
733 	if (vmacp)
734 		strcat(vmacbuf, tmpbuf);
735 	vmacp = vmacbuf;
736 	/* arrange to be able to undo the whole macro */
737 	if (canundo) {
738 #ifdef notdef
739 		otchng = tchng;
740 		vsave();
741 		saveall();
742 		inopen = -1;	/* no need to save since it had to be 1 or -1 before */
743 		vundkind = VMANY;
744 #endif
745 		vch_mac = VC_NOCHANGE;
746 	}
747 }
748 
749 #ifdef TRACE
750 void
visdump(char * s)751 visdump(char *s)
752 {
753 	register int i;
754 
755 	if (!trace) return;
756 
757 	fprintf(trace, "\n%s: basWTOP=%d, basWLINES=%d, WTOP=%d, WBOT=%d, WLINES=%d, WCOLS=%d, WECHO=%d\n",
758 		s, basWTOP, basWLINES, WTOP, WBOT, WLINES, WCOLS, WECHO);
759 	fprintf(trace, "   vcnt=%d, vcline=%d, cursor=%d, wcursor=%d, wdot=%d\n",
760 		vcnt, vcline, cursor-linebuf, wcursor-linebuf, wdot-zero);
761 	for (i=0; i<TUBELINES; i++)
762 		if (vtube[i] && *vtube[i])
763 			fprintf(trace, "%d: '%s'\n", i, vtube[i]);
764 	tvliny();
765 }
766 
767 void
vudump(char * s)768 vudump(char *s)
769 {
770 	register line *p;
771 	char savelb[1024];
772 
773 	if (!trace) return;
774 
775 	fprintf(trace, "\n%s: undkind=%d, vundkind=%d, unddel=%d, undap1=%d, undap2=%d,\n",
776 		s, undkind, vundkind, lineno(unddel), lineno(undap1), lineno(undap2));
777 	fprintf(trace, "  undadot=%d, dot=%d, dol=%d, unddol=%d, truedol=%d\n",
778 		lineno(undadot), lineno(dot), lineno(dol), lineno(unddol), lineno(truedol));
779 	fprintf(trace, "  [\n");
780 	CP(savelb, linebuf);
781 	fprintf(trace, "linebuf = '%s'\n", linebuf);
782 	for (p=zero+1; p<=truedol; p++) {
783 		fprintf(trace, "%o ", *p);
784 		getline(*p);
785 		fprintf(trace, "'%s'\n", linebuf);
786 	}
787 	fprintf(trace, "]\n");
788 	CP(linebuf, savelb);
789 }
790 #endif
791 
792 /*
793  * Get a count from the keyed input stream.
794  * A zero count is indistinguishable from no count.
795  */
796 int
vgetcnt(void)797 vgetcnt(void)
798 {
799 	register int c, cnt;
800 
801 	cnt = 0;
802 	for (;;) {
803 		c = getkey();
804 		if (!xisdigit(c))
805 			break;
806 		cnt *= 10, cnt += c - '0';
807 	}
808 	ungetkey(c);
809 	Xhadcnt = 1;
810 	Xcnt = cnt;
811 	return(cnt);
812 }
813 
814 void
trapalarm(int signum)815 trapalarm(int signum) {
816 	alarm(0);
817 	if (vcatch)
818 		LONGJMP(vreslab,1);
819 }
820 
821 /*
822  * fastpeekkey is just like peekkey but insists the character come in
823  * fast (within 1 second). This will succeed if it is the 2nd char of
824  * a machine generated sequence (such as a function pad from an escape
825  * flavor terminal) but fail for a human hitting escape then waiting.
826  */
827 int
fastpeekkey(void)828 fastpeekkey(void)
829 {
830 	shand Oint;
831 	register int c;
832 
833 	/*
834 	 * If the user has set notimeout, we wait forever for a key.
835 	 * If we are in a macro we do too, but since it's already
836 	 * buffered internally it will return immediately.
837 	 * In other cases we force this to die in 1 second.
838 	 * This is pretty reliable (VMUNIX rounds it to .5 - 1.5 secs,
839 	 * but UNIX truncates it to 0 - 1 secs) but due to system delays
840 	 * there are times when arrow keys or very fast typing get counted
841 	 * as separate.  notimeout is provided for people who dislike such
842 	 * nondeterminism.
843 	 */
844 #ifdef MDEBUG
845 	if (trace)
846 		fprintf(trace,"\nfastpeekkey: ",c);
847 #endif
848 	Oint = signal(SIGINT, trapalarm);
849 	if (value(TIMEOUT) && inopen >= 0) {
850 		signal(SIGALRM, trapalarm);
851 #ifdef MDEBUG
852 		alarm(10);
853 		if (trace)
854 			fprintf(trace, "set alarm ");
855 #else
856 		alarm(1);
857 #endif
858 	}
859 	CATCH
860 		c = peekkey();
861 #ifdef MDEBUG
862 	if (trace)
863 		fprintf(trace,"[OK]",c);
864 #endif
865 		alarm(0);
866 	ONERR
867 		c = 0;
868 #ifdef MDEBUG
869 	if (trace)
870 		fprintf(trace,"[TIMEOUT]",c);
871 #endif
872 	ENDCATCH
873 #ifdef MDEBUG
874 	if (trace)
875 		fprintf(trace,"[fpk:%o]",c);
876 #endif
877 	signal(SIGINT,Oint);
878 	return(c);
879 }
880