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