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