Must distinguish between "ambiguous" and "unknown" commands.
[unix-history] / usr / src / usr.bin / ex / ex_vget.c
CommitLineData
dac571dd
DF
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
5a6c967e 8static char sccsid[] = "@(#)ex_vget.c 6.9 (Berkeley) %G%";
dac571dd
DF
9#endif not lint
10
e213a660
MH
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 */
25ungetkey(c)
299f2784 26 int c; /* mjm: char --> int */
e213a660
MH
27{
28
5a6c967e
CH
29 if (Peek_key != ATTN)
30 Peek_key = c;
e213a660
MH
31}
32
33/*
34 * Return a keystroke, but never a ^@.
35 */
36getkey()
37{
299f2784 38 register int c; /* mjm: char --> int */
e213a660
MH
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 */
51peekbr()
52{
53
5a6c967e
CH
54 Peek_key = getbr();
55 return (Peek_key == 0);
e213a660
MH
56}
57
58short precbksl;
1013c773 59jmp_buf readbuf;
47bdd628 60int doingread = 0;
e213a660
MH
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 */
72getbr()
73{
74 char ch;
75 register int c, d;
76 register char *colp;
d266c416
MH
77#define BEEHIVE
78#ifdef BEEHIVE
79 static char Peek2key;
80#endif
81 extern short slevel, ttyindes;
e213a660
MH
82
83getATTN:
5a6c967e
CH
84 if (Peek_key) {
85 c = Peek_key;
86 Peek_key = 0;
e213a660
MH
87 return (c);
88 }
d266c416
MH
89#ifdef BEEHIVE
90 if (Peek2key) {
91 c = Peek2key;
92 Peek2key = 0;
93 return (c);
94 }
95#endif
e213a660
MH
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;
44232d5b
MH
107 if (inopen == -1) /* don't screw up undo for esc esc */
108 vundkind = VMANY;
e213a660 109 inopen = 1; /* restore old setting now that macro done */
d266c416 110 vch_mac = VC_NOTINMAC;
e213a660 111 }
e213a660
MH
112 flusho();
113again:
1013c773
SL
114 if (setjmp(readbuf))
115 goto getATTN;
47bdd628 116 doingread = 1;
5a6c967e 117#ifndef vms
47bdd628 118 c = read(slevel == 0 ? 0 : ttyindes, &ch, 1);
5a6c967e
CH
119#else
120 c = vms_read(slevel == 0 ? 0 : ttyindes, &ch, 1);
121#endif
47bdd628
SL
122 doingread = 0;
123 if (c != 1) {
e213a660
MH
124 if (errno == EINTR)
125 goto getATTN;
126 error("Input read error");
127 }
128 c = ch & TRIM;
d266c416
MH
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
e213a660
MH
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) {
5a6c967e 178 Peek_key = c;
e213a660
MH
179 precbksl = 0;
180 c = '\\';
181 }
182 } else if (d)
183 c = d;
184 else {
5a6c967e 185 Peek_key = c;
e213a660
MH
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 */
212getesc()
213{
214 register int c;
215
216 c = getkey();
217 switch (c) {
218
887e3e0d
MH
219 case CTRL(v):
220 case CTRL(q):
221 c = getkey();
222 return (c);
223
e213a660
MH
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 */
238peekkey()
239{
240
5a6c967e
CH
241 Peek_key = getkey();
242 return (Peek_key);
e213a660
MH
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 */
249readecho(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);
5a6c967e 263 ex_putchar(c);
e213a660
MH
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;
04379bab 273 ignore(vgetline(0, genbuf + 1, &waste, c));
d266c416 274 if (Outchar == termchar)
5a6c967e 275 ex_putchar('\n');
e213a660
MH
276 vscrap();
277 Pline = OP;
5a6c967e 278 if (Peek_key != ATTN && Peek_key != QUIT && Peek_key != CTRL(h)) {
e213a660
MH
279 cursor = sc;
280 vclreol();
281 return (0);
282 }
283blewit:
5a6c967e 284 OPeek = Peek_key==CTRL(h) ? 0 : Peek_key; Peek_key = 0;
e213a660
MH
285 splitw = 0;
286 vclean();
287 vshow(dot, NOLINE);
288 vnline(sc);
5a6c967e 289 Peek_key = OPeek;
e213a660
MH
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 */
298setLAST()
299{
300
d266c416 301 if (vglobp || vmacp)
e213a660
MH
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 */
315addtext(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
326setDEL()
327{
328
5a6c967e 329 ex_setBUF(DEL);
e213a660
MH
330}
331
332/*
333 * Put text from cursor upto wcursor in BUF.
334 */
5a6c967e 335ex_setBUF(BUF)
e213a660
MH
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
348addto(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 */
367noteit(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);
5a6c967e 378 ex_printf("%d %sline", notecnt, notesgn);
e213a660 379 if (notecnt > 1)
5a6c967e 380 ex_putchar('s');
e213a660 381 if (*notenam) {
5a6c967e 382 ex_printf(" %s", notenam);
e213a660 383 if (*(strend(notenam) - 1) != 'e')
5a6c967e
CH
384 ex_putchar('e');
385 ex_putchar('d');
e213a660
MH
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 */
402beep()
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
418map(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
d266c416
MH
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
e213a660
MH
452 b[0] = c;
453 b[1] = 0;
454 for (d=0; maps[d].mapto; d++) {
455#ifdef MDEBUG
456 if (trace)
d266c416 457 fprintf(trace,"\ntry '%s', ",maps[d].cap);
e213a660
MH
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 /*
d266c416
MH
467 * Is there another char waiting?
468 *
e213a660
MH
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
887e3e0d 476 if (trace)
299f2784 477 fprintf(trace,"fpk=0: will return '%c'",c);
e213a660 478#endif
887e3e0d 479 /*
d266c416
MH
480 * Nothing waiting. Push back
481 * what we peeked at & return
482 * failure (c).
483 *
887e3e0d
MH
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 */
299f2784
MH
489#ifdef MDEBUG
490 if (trace)
491 fprintf(trace, "Call macpush, b %d %d %d\n", b[0], b[1], b[2]);
492#endif
887e3e0d 493 macpush(&b[1],maps == arrows);
299f2784
MH
494#ifdef MDEBUG
495 if (trace)
496 fprintf(trace, "return %d\n", c);
497#endif
e213a660
MH
498 return(c);
499 }
500 *q = getkey();
501 q[1] = 0;
502 }
503 if (*p != *q)
504 goto contin;
505 }
d266c416 506 macpush(maps[d].mapto,maps == arrows);
e213a660
MH
507 c = getkey();
508#ifdef MDEBUG
d266c416
MH
509 if (trace)
510 fprintf(trace,"Success: push(%s), return %c",maps[d].mapto, c);
e213a660
MH
511#endif
512 return(c); /* first char of map string */
513 contin:;
514 }
515 }
516#ifdef MDEBUG
517 if (trace)
d266c416 518 fprintf(trace,"Fail: push(%s), return %c", &b[1], c);
e213a660 519#endif
44232d5b 520 macpush(&b[1],0);
e213a660
MH
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.
44232d5b
MH
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.
e213a660 532 */
44232d5b 533macpush(st, canundo)
e213a660 534char *st;
44232d5b 535int canundo;
e213a660
MH
536{
537 char tmpbuf[BUFSIZ];
538
539 if (st==0 || *st==0)
540 return;
299f2784 541#ifdef MDEBUG
e213a660 542 if (trace)
d266c416 543 fprintf(trace, "macpush(%s), canundo=%d\n",st,canundo);
e213a660 544#endif
887e3e0d 545 if ((vmacp ? strlen(vmacp) : 0) + strlen(st) > BUFSIZ)
e213a660 546 error("Macro too long@ - maybe recursive?");
44232d5b 547 if (vmacp) {
e213a660 548 strcpy(tmpbuf, vmacp);
887e3e0d
MH
549 if (!FIXUNDO)
550 canundo = 0; /* can't undo inside a macro anyway */
44232d5b 551 }
e213a660
MH
552 strcpy(vmacbuf, st);
553 if (vmacp)
554 strcat(vmacbuf, tmpbuf);
555 vmacp = vmacbuf;
556 /* arrange to be able to undo the whole macro */
44232d5b 557 if (canundo) {
d266c416 558#ifdef notdef
44232d5b
MH
559 otchng = tchng;
560 vsave();
561 saveall();
887e3e0d 562 inopen = -1; /* no need to save since it had to be 1 or -1 before */
44232d5b 563 vundkind = VMANY;
d266c416
MH
564#endif
565 vch_mac = VC_NOCHANGE;
44232d5b 566 }
887e3e0d
MH
567}
568
e213a660 569#ifdef TRACE
d266c416
MH
570visdump(s)
571char *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
887e3e0d
MH
587vudump(s)
588char *s;
589{
d266c416 590 register line *p;
f0f2d980 591 char savelb[1024];
d266c416
MH
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));
f0f2d980
MH
599 fprintf(trace, " [\n");
600 CP(savelb, linebuf);
601 fprintf(trace, "linebuf = '%s'\n", linebuf);
602 for (p=zero+1; p<=truedol; p++) {
d266c416 603 fprintf(trace, "%o ", *p);
f0f2d980
MH
604 getline(*p);
605 fprintf(trace, "'%s'\n", linebuf);
606 }
d266c416 607 fprintf(trace, "]\n");
f0f2d980 608 CP(linebuf, savelb);
e213a660 609}
887e3e0d 610#endif
e213a660
MH
611
612/*
613 * Get a count from the keyed input stream.
614 * A zero count is indistinguishable from no count.
615 */
616vgetcnt()
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 */
639fastpeekkey()
640{
641 int trapalarm();
6a5f98c7 642 int (*Oint)();
e213a660
MH
643 register int c;
644
887e3e0d
MH
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 */
299f2784
MH
656#ifdef MDEBUG
657 if (trace)
658 fprintf(trace,"\nfastpeekkey: ",c);
659#endif
6a5f98c7 660 Oint = signal(SIGINT, trapalarm);
887e3e0d
MH
661 if (value(TIMEOUT) && inopen >= 0) {
662 signal(SIGALRM, trapalarm);
299f2784
MH
663#ifdef MDEBUG
664 alarm(10);
665 if (trace)
666 fprintf(trace, "set alarm ");
667#else
887e3e0d 668 alarm(1);
299f2784 669#endif
887e3e0d 670 }
e213a660
MH
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)
299f2784 682 fprintf(trace,"[TIMEOUT]",c);
e213a660
MH
683#endif
684 ENDCATCH
685#ifdef MDEBUG
686 if (trace)
687 fprintf(trace,"[fpk:%o]",c);
688#endif
6a5f98c7 689 signal(SIGINT,Oint);
e213a660
MH
690 return(c);
691}
692
693trapalarm() {
694 alarm(0);
6a5f98c7
RC
695 if (vcatch)
696 longjmp(vreslab,1);
e213a660 697}