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