|
|
1.1 root 1: /* Copyright (c) 1981 Regents of the University of California */
2: static char *sccsid = "@(#)ex_vget.c 6.3 7/8/81";
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: */
17: ungetkey(c)
18: int c; /* mjm: char --> int */
19: {
20:
21: if (Peekkey != ATTN)
22: Peekkey = c;
23: }
24:
25: /*
26: * Return a keystroke, but never a ^@.
27: */
28: getkey()
29: {
30: register int c; /* mjm: char --> int */
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: */
43: peekbr()
44: {
45:
46: Peekkey = getbr();
47: return (Peekkey == 0);
48: }
49:
50: short 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: */
62: getbr()
63: {
64: char ch;
65: register int c, d;
66: register char *colp;
67: int cnt;
68: #define BEEHIVE
69: #ifdef BEEHIVE
70: static char Peek2key;
71: #endif
72: extern short slevel, ttyindes;
73:
74: getATTN:
75: if (Peekkey) {
76: c = Peekkey;
77: Peekkey = 0;
78: return (c);
79: }
80: #ifdef BEEHIVE
81: if (Peek2key) {
82: c = Peek2key;
83: Peek2key = 0;
84: return (c);
85: }
86: #endif
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;
98: if (inopen == -1) /* don't screw up undo for esc esc */
99: vundkind = VMANY;
100: inopen = 1; /* restore old setting now that macro done */
101: vch_mac = VC_NOTINMAC;
102: }
103: flusho();
104: again:
105: if ((c=read(slevel == 0 ? 0 : ttyindes, &ch, 1)) != 1) {
106: if (errno == EINTR)
107: goto getATTN;
108: error("Input read error");
109: }
110: c = ch & TRIM;
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
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: */
194: getesc()
195: {
196: register int c;
197:
198: c = getkey();
199: switch (c) {
200:
201: case CTRL(v):
202: case CTRL(q):
203: c = getkey();
204: return (c);
205:
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: */
220: peekkey()
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: */
231: readecho(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;
255: ignore(vgetline(0, genbuf + 1, &waste, c));
256: if (Outchar == termchar)
257: putchar('\n');
258: vscrap();
259: Pline = OP;
260: if (Peekkey != ATTN && Peekkey != QUIT && Peekkey != CTRL(h)) {
261: cursor = sc;
262: vclreol();
263: return (0);
264: }
265: blewit:
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: */
280: setLAST()
281: {
282:
283: if (vglobp || vmacp)
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: */
297: addtext(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:
308: setDEL()
309: {
310:
311: setBUF(DEL);
312: }
313:
314: /*
315: * Put text from cursor upto wcursor in BUF.
316: */
317: setBUF(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:
330: addto(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: */
349: noteit(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: */
384: beep()
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:
400: map(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
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:
434: b[0] = c;
435: b[1] = 0;
436: for (d=0; maps[d].mapto; d++) {
437: #ifdef MDEBUG
438: if (trace)
439: fprintf(trace,"\ntry '%s', ",maps[d].cap);
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: /*
449: * Is there another char waiting?
450: *
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
458: if (trace)
459: fprintf(trace,"fpk=0: will return '%c'",c);
460: #endif
461: /*
462: * Nothing waiting. Push back
463: * what we peeked at & return
464: * failure (c).
465: *
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: */
471: #ifdef MDEBUG
472: if (trace)
473: fprintf(trace, "Call macpush, b %d %d %d\n", b[0], b[1], b[2]);
474: #endif
475: macpush(&b[1],maps == arrows);
476: #ifdef MDEBUG
477: if (trace)
478: fprintf(trace, "return %d\n", c);
479: #endif
480: return(c);
481: }
482: *q = getkey();
483: q[1] = 0;
484: }
485: if (*p != *q)
486: goto contin;
487: }
488: macpush(maps[d].mapto,maps == arrows);
489: c = getkey();
490: #ifdef MDEBUG
491: if (trace)
492: fprintf(trace,"Success: push(%s), return %c",maps[d].mapto, c);
493: #endif
494: return(c); /* first char of map string */
495: contin:;
496: }
497: }
498: #ifdef MDEBUG
499: if (trace)
500: fprintf(trace,"Fail: push(%s), return %c", &b[1], c);
501: #endif
502: macpush(&b[1],0);
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.
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.
514: */
515: macpush(st, canundo)
516: char *st;
517: int canundo;
518: {
519: char tmpbuf[BUFSIZ];
520:
521: if (st==0 || *st==0)
522: return;
523: #ifdef MDEBUG
524: if (trace)
525: fprintf(trace, "macpush(%s), canundo=%d\n",st,canundo);
526: #endif
527: if ((vmacp ? strlen(vmacp) : 0) + strlen(st) > BUFSIZ)
528: error("Macro too long@ - maybe recursive?");
529: if (vmacp) {
530: strcpy(tmpbuf, vmacp);
531: if (!FIXUNDO)
532: canundo = 0; /* can't undo inside a macro anyway */
533: }
534: strcpy(vmacbuf, st);
535: if (vmacp)
536: strcat(vmacbuf, tmpbuf);
537: vmacp = vmacbuf;
538: /* arrange to be able to undo the whole macro */
539: if (canundo) {
540: #ifdef notdef
541: otchng = tchng;
542: vsave();
543: saveall();
544: inopen = -1; /* no need to save since it had to be 1 or -1 before */
545: vundkind = VMANY;
546: #endif
547: vch_mac = VC_NOCHANGE;
548: }
549: }
550:
551: #ifdef TRACE
552: visdump(s)
553: char *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:
569: vudump(s)
570: char *s;
571: {
572: register line *p;
573: char savelb[1024];
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));
581: fprintf(trace, " [\n");
582: CP(savelb, linebuf);
583: fprintf(trace, "linebuf = '%s'\n", linebuf);
584: for (p=zero+1; p<=truedol; p++) {
585: fprintf(trace, "%o ", *p);
586: getline(*p);
587: fprintf(trace, "'%s'\n", linebuf);
588: }
589: fprintf(trace, "]\n");
590: CP(linebuf, savelb);
591: }
592: #endif
593:
594: /*
595: * Get a count from the keyed input stream.
596: * A zero count is indistinguishable from no count.
597: */
598: vgetcnt()
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: */
621: fastpeekkey()
622: {
623: int trapalarm();
624: register int c;
625:
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: */
637: #ifdef MDEBUG
638: if (trace)
639: fprintf(trace,"\nfastpeekkey: ",c);
640: #endif
641: if (value(TIMEOUT) && inopen >= 0) {
642: signal(SIGALRM, trapalarm);
643: #ifdef MDEBUG
644: alarm(10);
645: if (trace)
646: fprintf(trace, "set alarm ");
647: #else
648: alarm(1);
649: #endif
650: }
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)
662: fprintf(trace,"[TIMEOUT]",c);
663: #endif
664: ENDCATCH
665: #ifdef MDEBUG
666: if (trace)
667: fprintf(trace,"[fpk:%o]",c);
668: #endif
669: return(c);
670: }
671:
672: trapalarm() {
673: alarm(0);
674: longjmp(vreslab,1);
675: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.