Marshall says it works finally.
[unix-history] / usr / src / usr.bin / ex / ex_vget.c
CommitLineData
299f2784 1/* Copyright (c) 1981 Regents of the University of California */
47bdd628 2static char *sccsid = "@(#)ex_vget.c 6.5 %G%";
e213a660
MH
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 */
17ungetkey(c)
299f2784 18 int c; /* mjm: char --> int */
e213a660
MH
19{
20
21 if (Peekkey != ATTN)
22 Peekkey = c;
23}
24
25/*
26 * Return a keystroke, but never a ^@.
27 */
28getkey()
29{
299f2784 30 register int c; /* mjm: char --> int */
e213a660
MH
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 */
43peekbr()
44{
45
46 Peekkey = getbr();
47 return (Peekkey == 0);
48}
49
50short precbksl;
1013c773 51jmp_buf readbuf;
47bdd628 52int doingread = 0;
e213a660
MH
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 */
64getbr()
65{
66 char ch;
67 register int c, d;
68 register char *colp;
299f2784 69 int cnt;
d266c416
MH
70#define BEEHIVE
71#ifdef BEEHIVE
72 static char Peek2key;
73#endif
74 extern short slevel, ttyindes;
e213a660
MH
75
76getATTN:
77 if (Peekkey) {
78 c = Peekkey;
79 Peekkey = 0;
80 return (c);
81 }
d266c416
MH
82#ifdef BEEHIVE
83 if (Peek2key) {
84 c = Peek2key;
85 Peek2key = 0;
86 return (c);
87 }
88#endif
e213a660
MH
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;
44232d5b
MH
100 if (inopen == -1) /* don't screw up undo for esc esc */
101 vundkind = VMANY;
e213a660 102 inopen = 1; /* restore old setting now that macro done */
d266c416 103 vch_mac = VC_NOTINMAC;
e213a660 104 }
e213a660
MH
105 flusho();
106again:
1013c773
SL
107 if (setjmp(readbuf))
108 goto getATTN;
47bdd628
SL
109 doingread = 1;
110 c = read(slevel == 0 ? 0 : ttyindes, &ch, 1);
111 doingread = 0;
112 if (c != 1) {
e213a660
MH
113 if (errno == EINTR)
114 goto getATTN;
115 error("Input read error");
116 }
117 c = ch & TRIM;
d266c416
MH
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
e213a660
MH
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 */
201getesc()
202{
203 register int c;
204
205 c = getkey();
206 switch (c) {
207
887e3e0d
MH
208 case CTRL(v):
209 case CTRL(q):
210 c = getkey();
211 return (c);
212
e213a660
MH
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 */
227peekkey()
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 */
238readecho(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;
04379bab 262 ignore(vgetline(0, genbuf + 1, &waste, c));
d266c416
MH
263 if (Outchar == termchar)
264 putchar('\n');
e213a660
MH
265 vscrap();
266 Pline = OP;
267 if (Peekkey != ATTN && Peekkey != QUIT && Peekkey != CTRL(h)) {
268 cursor = sc;
269 vclreol();
270 return (0);
271 }
272blewit:
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 */
287setLAST()
288{
289
d266c416 290 if (vglobp || vmacp)
e213a660
MH
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 */
304addtext(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
315setDEL()
316{
317
318 setBUF(DEL);
319}
320
321/*
322 * Put text from cursor upto wcursor in BUF.
323 */
324setBUF(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
337addto(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 */
356noteit(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 */
391beep()
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
407map(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
d266c416
MH
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
e213a660
MH
441 b[0] = c;
442 b[1] = 0;
443 for (d=0; maps[d].mapto; d++) {
444#ifdef MDEBUG
445 if (trace)
d266c416 446 fprintf(trace,"\ntry '%s', ",maps[d].cap);
e213a660
MH
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 /*
d266c416
MH
456 * Is there another char waiting?
457 *
e213a660
MH
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
887e3e0d 465 if (trace)
299f2784 466 fprintf(trace,"fpk=0: will return '%c'",c);
e213a660 467#endif
887e3e0d 468 /*
d266c416
MH
469 * Nothing waiting. Push back
470 * what we peeked at & return
471 * failure (c).
472 *
887e3e0d
MH
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 */
299f2784
MH
478#ifdef MDEBUG
479 if (trace)
480 fprintf(trace, "Call macpush, b %d %d %d\n", b[0], b[1], b[2]);
481#endif
887e3e0d 482 macpush(&b[1],maps == arrows);
299f2784
MH
483#ifdef MDEBUG
484 if (trace)
485 fprintf(trace, "return %d\n", c);
486#endif
e213a660
MH
487 return(c);
488 }
489 *q = getkey();
490 q[1] = 0;
491 }
492 if (*p != *q)
493 goto contin;
494 }
d266c416 495 macpush(maps[d].mapto,maps == arrows);
e213a660
MH
496 c = getkey();
497#ifdef MDEBUG
d266c416
MH
498 if (trace)
499 fprintf(trace,"Success: push(%s), return %c",maps[d].mapto, c);
e213a660
MH
500#endif
501 return(c); /* first char of map string */
502 contin:;
503 }
504 }
505#ifdef MDEBUG
506 if (trace)
d266c416 507 fprintf(trace,"Fail: push(%s), return %c", &b[1], c);
e213a660 508#endif
44232d5b 509 macpush(&b[1],0);
e213a660
MH
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.
44232d5b
MH
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.
e213a660 521 */
44232d5b 522macpush(st, canundo)
e213a660 523char *st;
44232d5b 524int canundo;
e213a660
MH
525{
526 char tmpbuf[BUFSIZ];
527
528 if (st==0 || *st==0)
529 return;
299f2784 530#ifdef MDEBUG
e213a660 531 if (trace)
d266c416 532 fprintf(trace, "macpush(%s), canundo=%d\n",st,canundo);
e213a660 533#endif
887e3e0d 534 if ((vmacp ? strlen(vmacp) : 0) + strlen(st) > BUFSIZ)
e213a660 535 error("Macro too long@ - maybe recursive?");
44232d5b 536 if (vmacp) {
e213a660 537 strcpy(tmpbuf, vmacp);
887e3e0d
MH
538 if (!FIXUNDO)
539 canundo = 0; /* can't undo inside a macro anyway */
44232d5b 540 }
e213a660
MH
541 strcpy(vmacbuf, st);
542 if (vmacp)
543 strcat(vmacbuf, tmpbuf);
544 vmacp = vmacbuf;
545 /* arrange to be able to undo the whole macro */
44232d5b 546 if (canundo) {
d266c416 547#ifdef notdef
44232d5b
MH
548 otchng = tchng;
549 vsave();
550 saveall();
887e3e0d 551 inopen = -1; /* no need to save since it had to be 1 or -1 before */
44232d5b 552 vundkind = VMANY;
d266c416
MH
553#endif
554 vch_mac = VC_NOCHANGE;
44232d5b 555 }
887e3e0d
MH
556}
557
e213a660 558#ifdef TRACE
d266c416
MH
559visdump(s)
560char *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
887e3e0d
MH
576vudump(s)
577char *s;
578{
d266c416 579 register line *p;
f0f2d980 580 char savelb[1024];
d266c416
MH
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));
f0f2d980
MH
588 fprintf(trace, " [\n");
589 CP(savelb, linebuf);
590 fprintf(trace, "linebuf = '%s'\n", linebuf);
591 for (p=zero+1; p<=truedol; p++) {
d266c416 592 fprintf(trace, "%o ", *p);
f0f2d980
MH
593 getline(*p);
594 fprintf(trace, "'%s'\n", linebuf);
595 }
d266c416 596 fprintf(trace, "]\n");
f0f2d980 597 CP(linebuf, savelb);
e213a660 598}
887e3e0d 599#endif
e213a660
MH
600
601/*
602 * Get a count from the keyed input stream.
603 * A zero count is indistinguishable from no count.
604 */
605vgetcnt()
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 */
628fastpeekkey()
629{
630 int trapalarm();
631 register int c;
632
887e3e0d
MH
633 /*
634 * If the user has set notimeout, we wait forever for a key.
635 * If we are in a macro we do too, but since it's already
636 * buffered internally it will return immediately.
637 * In other cases we force this to die in 1 second.
638 * This is pretty reliable (VMUNIX rounds it to .5 - 1.5 secs,
639 * but UNIX truncates it to 0 - 1 secs) but due to system delays
640 * there are times when arrow keys or very fast typing get counted
641 * as separate. notimeout is provided for people who dislike such
642 * nondeterminism.
643 */
299f2784
MH
644#ifdef MDEBUG
645 if (trace)
646 fprintf(trace,"\nfastpeekkey: ",c);
647#endif
887e3e0d
MH
648 if (value(TIMEOUT) && inopen >= 0) {
649 signal(SIGALRM, trapalarm);
299f2784
MH
650#ifdef MDEBUG
651 alarm(10);
652 if (trace)
653 fprintf(trace, "set alarm ");
654#else
887e3e0d 655 alarm(1);
299f2784 656#endif
887e3e0d 657 }
e213a660
MH
658 CATCH
659 c = peekkey();
660#ifdef MDEBUG
661 if (trace)
662 fprintf(trace,"[OK]",c);
663#endif
664 alarm(0);
665 ONERR
666 c = 0;
667#ifdef MDEBUG
668 if (trace)
299f2784 669 fprintf(trace,"[TIMEOUT]",c);
e213a660
MH
670#endif
671 ENDCATCH
672#ifdef MDEBUG
673 if (trace)
674 fprintf(trace,"[fpk:%o]",c);
675#endif
676 return(c);
677}
678
679trapalarm() {
680 alarm(0);
681 longjmp(vreslab,1);
682}