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