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