|
|
1.1 root 1: /* tio.c */
2:
3: /* Author:
4: * Steve Kirkendall
5: * 16820 SW Tallac Way
6: * Beaverton, OR 97006
7: * [email protected], or ...uunet!tektronix!psueea!jove!kirkenda
8: */
9:
10:
11: /* This file contains terminal I/O functions */
12:
13: #include "config.h"
14: #if BSD || COHERENT
15: # include <setjmp.h>
16: #endif
17: #include <signal.h>
18: #include "vi.h"
19:
20:
21: /* This function reads in a line from the terminal. */
22: int vgets(prompt, buf, bsize)
23: char prompt; /* the prompt character, or '\0' for none */
24: char *buf; /* buffer into which the string is read */
25: int bsize; /* size of the buffer */
26: {
27: int len; /* how much we've read so far */
28: int ch; /* a character from the user */
29: int quoted; /* is the next char quoted? */
30: int tab; /* column position of cursor */
31: char widths[132]; /* widths of characters */
32: #ifndef NO_DIGRAPH
33: int erased; /* 0, or first char of a digraph */
34: #endif
35:
36: /* show the prompt */
37: move(LINES - 1, 0);
38: tab = 0;
39: if (prompt)
40: {
41: addch(prompt);
42: tab = 1;
43: }
44: clrtoeol();
45: refresh();
46:
47: /* read in the line */
48: #ifndef NO_DIGRAPH
49: erased =
50: #endif
51: quoted = len = 0;
52: for (;;)
53: {
54: ch = getkey(quoted ? 0 : WHEN_EX);
55:
56: /* some special conversions */
57: if (ch == ctrl('D') && len == 0)
58: ch = ctrl('[');
59: #ifndef NO_DIGRAPH
60: if (*o_digraph && erased != 0 && ch != '\b')
61: {
62: ch = digraph(erased, ch);
63: erased = 0;
64: }
65: #endif
66:
67: /* inhibit detection of special chars (except ^J) after a ^V */
68: if (quoted && ch != '\n')
69: {
70: ch |= 256;
71: }
72:
73: /* process the character */
74: switch(ch)
75: {
76: case ctrl('V'):
77: qaddch('^');
78: qaddch('\b');
79: quoted = TRUE;
80: break;
81:
82: case ctrl('['):
83: return -1;
84:
85: case '\n':
86: #if OSK
87: case '\l':
88: #else
89: case '\r':
90: #endif
91: clrtoeol();
92: goto BreakBreak;
93:
94: case '\b':
95: if (len > 0)
96: {
97: len--;
98: #ifndef NO_DIGRAPH
99: erased = buf[len];
100: #endif
101: for (ch = widths[len]; ch > 0; ch--)
102: addch('\b');
103: if (mode == MODE_EX)
104: {
105: clrtoeol();
106: }
107: tab -= widths[len];
108: }
109: else
110: {
111: return -1;
112: }
113: break;
114:
115: default:
116: /* strip off quotation bit */
117: if (ch & 256)
118: {
119: ch &= ~256;
120: quoted = FALSE;
121: qaddch(' ');
122: qaddch('\b');
123: }
124: /* add & echo the char */
125: if (len < bsize - 1)
126: {
127: if (ch == '\t')
128: {
129: widths[len] = *o_tabstop - (tab % *o_tabstop);
130: addstr(" " + 8 - widths[len]);
131: tab += widths[len];
132: }
133: else if (ch > 0 && ch < ' ') /* > 0 by GB */
134: {
135: addch('^');
136: addch(ch + '@');
137: widths[len] = 2;
138: tab += 2;
139: }
140: else if (ch == '\177')
141: {
142: addch('^');
143: addch('?');
144: widths[len] = 2;
145: tab += 2;
146: }
147: else
148: {
149: addch(ch);
150: widths[len] = 1;
151: tab++;
152: }
153: buf[len++] = ch;
154: }
155: else
156: {
157: beep();
158: }
159: }
160: }
161: BreakBreak:
162: refresh();
163: buf[len] = '\0';
164: return len;
165: }
166:
167:
168: /* ring the terminal's bell */
169: void beep()
170: {
171: if (*o_vbell)
172: {
173: do_VB();
174: refresh();
175: }
176: else if (*o_errorbells)
177: {
178: ttywrite("\007", 1);
179: }
180: }
181:
182: static int manymsgs; /* This variable keeps msgs from overwriting each other */
183: static char pmsg[80]; /* previous message (waiting to be displayed) */
184:
185:
186: static int showmsg()
187: {
188: /* if there is no message to show, then don't */
189: if (!manymsgs)
190: return FALSE;
191:
192: /* display the message */
193: move(LINES - 1, 0);
194: if (*pmsg)
195: {
196: standout();
197: qaddch(' ');
198: qaddstr(pmsg);
199: qaddch(' ');
200: standend();
201: }
202: clrtoeol();
203:
204: manymsgs = FALSE;
205: return TRUE;
206: }
207:
208:
209: void endmsgs()
210: {
211: if (manymsgs)
212: {
213: showmsg();
214: addch('\n');
215: }
216: }
217:
218: /* Write a message in an appropriate way. This should really be a varargs
219: * function, but there is no such thing as vwprintw. Hack!!!
220: *
221: * In MODE_EX or MODE_COLON, the message is written immediately, with a
222: * newline at the end.
223: *
224: * In MODE_VI, the message is stored in a character buffer. It is not
225: * displayed until getkey() is called. msg() will call getkey() itself,
226: * if necessary, to prevent messages from being lost.
227: *
228: * msg("") - clears the message line
229: * msg("%s %d", ...) - does a printf onto the message line
230: */
231: /*VARARGS1*/
232: void msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
233: char *fmt;
234: long arg1, arg2, arg3, arg4, arg5, arg6, arg7;
235: {
236: if (mode != MODE_VI)
237: {
238: sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
239: qaddstr(pmsg);
240: addch('\n');
241: exrefresh();
242: }
243: else
244: {
245: /* wait for keypress between consecutive msgs */
246: if (manymsgs)
247: {
248: getkey(WHEN_MSG);
249: }
250:
251: /* real message */
252: sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
253: manymsgs = TRUE;
254: }
255: }
256:
257:
258: /* This function calls refresh() if the option exrefresh is set */
259: void exrefresh()
260: {
261: char *scan;
262:
263: /* If this ex command wrote ANYTHING set exwrote so vi's : command
264: * can tell that it must wait for a user keystroke before redrawing.
265: */
266: for (scan=kbuf; scan<stdscr; scan++)
267: if (*scan == '\n')
268: exwrote = TRUE;
269:
270: #if MICROSOFT /* avoid compiler bug */
271: scan = stdscr;
272: #define stdscr scan
273: #endif
274: /* now we do the refresh thing */
275: if (*o_exrefresh)
276: {
277: refresh();
278: }
279: else
280: {
281: wqrefresh(stdscr);
282: }
283: #if MICROSOFT
284: #undef stdscr
285: stdscr = scan;
286: #endif
287: if (mode != MODE_VI)
288: {
289: manymsgs = FALSE;
290: }
291: }
292:
293:
294:
295: /* These are used for typeahead, and also for fudging the visual @ command */
296: static char keybuf[100]; /* array of already-read keys */
297: static int nkeys; /* total number of keys in keybuf */
298: static int next; /* index of next key to return */
299: #ifndef NO_AT
300: static int atnext; /* index of next key for "@", or 0 normally */
301: int fromcutbuf(cbname)
302: int cbname;
303: {
304: int len;
305:
306: /* fail if we're already doing an @ macro */
307: if (atnext > 0 && keybuf[atnext])
308: {
309: msg("Can't nest @ commands");
310: return FALSE;
311: }
312:
313: /* use the empty portion of keybuf[] to get chars from the cut buffer */
314: len = cb2str(cbname, keybuf + nkeys, sizeof keybuf - nkeys);
315: if (len < 0)
316: {
317: msg("Invalid cut buffer name. Must be a-z");
318: return FALSE;
319: }
320: if (len == 0)
321: {
322: msg("Cut buffer \"%c is empty", cbname);
323: return FALSE;
324: }
325: else if (len >= sizeof keybuf - nkeys)
326: {
327: msg("Cut buffer \"%c is too large to execute", cbname);
328: return FALSE;
329: }
330:
331: /* prepare to "read" those keys on subsequent getkey() calls */
332: atnext = nkeys;
333: return TRUE;
334: }
335: #endif
336:
337: /* This array describes mapped key sequences */
338: static struct _keymap
339: {
340: char *name; /* name of the key, or NULL */
341: char rawin[LONGKEY]; /* the unmapped version of input */
342: char cooked[80]; /* the mapped version of input */
343: int len; /* length of the unmapped version */
344: int when; /* when is this key mapped? */
345: }
346: mapped[MAXMAPS];
347:
348: #if !MSDOS && !TOS
349: # if BSD || COHERENT
350: static jmp_buf env_timeout;
351: static int dummy()
352: {
353: longjmp(env_timeout, 1);
354: return 0;
355: }
356: # else
357: static int dummy()
358: {
359: }
360: # endif
361: #endif
362:
363: /* This function reads in a keystroke for VI mode. It automatically handles
364: * key mapping.
365: */
366: int getkey(when)
367: int when; /* which bits must be ON? */
368: {
369: static char *cooked; /* rawin, or pointer to converted key */
370: static int oldwhen; /* "when" from last time */
371: static int oldleft;
372: static long oldtop;
373: static long oldnlines;
374: static char *cshape; /* current cursor shape */
375: REG char *kptr; /* &keybuf[next] */
376: REG struct _keymap *km; /* used to count through keymap */
377: REG int i, j, k;
378:
379: #ifdef DEBUG
380: watch();
381: #endif
382:
383: /* if this key is needed for delay between multiple error messages,
384: * then reset the manymsgs flag and abort any mapped key sequence.
385: */
386: if (showmsg())
387: {
388: if (when == WHEN_MSG)
389: {
390: qaddstr("[More...]");
391: refresh();
392: cooked = (char *)0;
393: }
394: else if (when == WHEN_VIINP || when == WHEN_VIREP)
395: {
396: redraw(cursor, TRUE);
397: }
398: }
399:
400: #ifndef NO_AT
401: /* if we're in the middle of a visual @ macro, take atnext */
402: if (atnext > 0)
403: {
404: if (keybuf[atnext])
405: {
406: return keybuf[atnext++];
407: }
408: atnext = 0;
409: }
410: #endif
411:
412: /* if we're doing a mapped key, get the next char */
413: if (cooked && *cooked)
414: {
415: return *cooked++;
416: }
417:
418: /* if keybuf is empty, fill it */
419: if (next == nkeys)
420: {
421: #ifndef NO_CURSORSHAPE
422: /* make sure the cursor is the right shape */
423: if (has_CQ)
424: {
425: cooked = cshape;
426: switch (when)
427: {
428: case WHEN_EX: cooked = CX; break;
429: case WHEN_VICMD: cooked = CV; break;
430: case WHEN_VIINP: cooked = CI; break;
431: case WHEN_VIREP: cooked = CR; break;
432: }
433: if (cooked != cshape)
434: {
435: cshape = cooked;
436: switch (when)
437: {
438: case WHEN_EX: do_CX(); break;
439: case WHEN_VICMD: do_CV(); break;
440: case WHEN_VIINP: do_CI(); break;
441: case WHEN_VIREP: do_CR(); break;
442: }
443: }
444: cooked = (char *)0;
445: }
446: #endif
447:
448: #ifndef NO_SHOWMODE
449: /* if "showmode" then say which mode we're in */
450: if (*o_smd
451: && mode == MODE_VI
452: && (when != oldwhen || topline != oldtop || leftcol != oldleft || nlines != oldnlines))
453: {
454: oldwhen = when;
455: oldtop = topline;
456: oldleft = leftcol;
457: oldnlines = nlines;
458:
459: if (when & WHEN_VICMD)
460: {
461: redraw(cursor, FALSE);
462: move(LINES - 1, COLS - 10);
463: standout();
464: addstr("Command");
465: standend();
466: redraw(cursor, FALSE);
467: }
468: else if (when & WHEN_VIINP)
469: {
470: redraw(cursor, TRUE);
471: move(LINES - 1, COLS - 10);
472: standout();
473: addstr(" Input ");
474: standend();
475: redraw(cursor, TRUE);
476: }
477: else if (when & WHEN_VIREP)
478: {
479: redraw(cursor, TRUE);
480: move(LINES - 1, COLS - 10);
481: standout();
482: addstr("Replace");
483: standend();
484: redraw(cursor, TRUE);
485: }
486: }
487: else
488: #endif
489:
490: /* redraw if getting a VI command */
491: if (when & WHEN_VICMD)
492: {
493: redraw(cursor, FALSE);
494: }
495:
496: /* read the rawin keystrokes */
497: refresh();
498: while ((nkeys = ttyread(keybuf, sizeof keybuf)) < 0)
499: {
500: /* terminal was probably resized */
501: *o_lines = LINES;
502: *o_columns = COLS;
503: if (when & (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP))
504: {
505: redraw(MARK_UNSET, FALSE);
506: redraw(cursor, (when & WHEN_VICMD) == 0);
507: refresh();
508: }
509: }
510: next = 0;
511:
512: /* if nkeys == 0 then we've reached EOF of an ex script. */
513: if (nkeys == 0)
514: {
515: tmpabort(TRUE);
516: move(LINES - 1, 0);
517: clrtoeol();
518: refresh();
519: endwin();
520: exit(1);
521: }
522: }
523:
524: /* see how many mapped keys this might be */
525: kptr = &keybuf[next];
526: for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
527: {
528: if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
529: {
530: if (km->len > nkeys - next)
531: {
532: if (!strncmp(km->rawin, kptr, nkeys - next))
533: {
534: j++;
535: }
536: }
537: else
538: {
539: if (!strncmp(km->rawin, kptr, km->len))
540: {
541: j++;
542: k = i;
543: }
544: }
545: }
546: }
547:
548: /* if more than one, try to read some more */
549: while (j > 1)
550: {
551: #if BSD || COHERENT
552: if (setjmp(env_timeout))
553: {
554: /* we timed out - assume no mapping */
555: j = 0;
556: break;
557: }
558: #endif
559: #if ANY_UNIX
560: signal(SIGALRM, dummy);
561: #endif
562: #if OSK
563: signal(SIGQUIT, dummy);
564: #endif
565: alarm((unsigned)*o_keytime);
566: i = nkeys;
567: if ((k = ttyread(keybuf + nkeys, sizeof keybuf - nkeys)) >= 0)
568: {
569: nkeys += k;
570: }
571: alarm(0);
572: #if OSK
573: # ifndef DEBUG
574: signal(SIGQUIT, SIG_IGN);
575: # endif
576: #endif
577:
578: /* if we couldn't read any more, pretend 0 mapped keys */
579: if (i == nkeys)
580: {
581: j = 0;
582: }
583: else /* else we got some more - try again */
584: {
585: for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
586: {
587: if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
588: {
589: if (km->len > nkeys - next)
590: {
591: if (!strncmp(km->rawin, kptr, nkeys - next))
592: {
593: j++;
594: }
595: }
596: else
597: {
598: if (!strncmp(km->rawin, kptr, km->len))
599: {
600: j++;
601: k = i;
602: }
603: }
604: }
605: }
606: }
607: }
608:
609: /* if unambiguously mapped key, use it! */
610: if (j == 1 && k >= 0)
611: {
612: next += mapped[k].len;
613: cooked = mapped[k].cooked;
614: #ifndef NO_EXTENSIONS
615: if ((when & (WHEN_VIINP|WHEN_VIREP))
616: && (mapped[k].when & WHEN_INMV))
617: {
618: return 0; /* special case, means "a movement char follows" */
619: }
620: else
621: #endif
622: {
623: return *cooked++;
624: }
625: }
626: else
627: /* assume key is unmapped, but still translate weird erase key to '\b' */
628: if (keybuf[next] == ERASEKEY && when != 0)
629: {
630: next++;
631: return '\b';
632: }
633: else if (keybuf[next] == '\0')
634: {
635: next++;
636: return ('A' & 0x1f);
637: }
638: else
639: {
640: return keybuf[next++];
641: }
642: }
643:
644:
645: /* This function maps or unmaps a key */
646: void mapkey(rawin, cooked, when, name)
647: char *rawin; /* the input key sequence, before mapping */
648: char *cooked;/* after mapping */
649: short when; /* bitmap of when mapping should happen */
650: char *name; /* name of the key, if any */
651: {
652: int i, j;
653:
654: #ifndef NO_EXTENSIONS
655: /* if the mapped version starts with the word "visual" then set WHEN_INMV */
656: if (!strncmp(cooked, "visual ", 7))
657: {
658: when |= WHEN_INMV;
659: cooked += 7;
660: }
661: /* if WHEN_INMV is set, then WHEN_VIINP and WHEN_VIREP must be set */
662: if (when & WHEN_INMV)
663: {
664: when |= (WHEN_VIINP | WHEN_VIREP);
665: }
666: #endif
667:
668: /* see if the key sequence was mapped before */
669: j = strlen(rawin);
670: for (i = 0; i < MAXMAPS; i++)
671: {
672: if (mapped[i].len == j
673: && !strncmp(mapped[i].rawin, rawin, j)
674: && (mapped[i].when & when))
675: {
676: break;
677: }
678: }
679:
680: /* if not already mapped, then try to find a new slot to use */
681: if (i == MAXMAPS)
682: {
683: for (i = 0; i < MAXMAPS && mapped[i].len > 0; i++)
684: {
685: }
686: }
687:
688: /* no room for the new key? */
689: if (i == MAXMAPS)
690: {
691: msg("No room left in the key map table");
692: return;
693: }
694:
695: /* map the key */
696: if (cooked && *cooked)
697: {
698: /* Map the key */
699: mapped[i].len = j;
700: strncpy(mapped[i].rawin, rawin, j);
701: strcpy(mapped[i].cooked, cooked);
702: mapped[i].when = when;
703: mapped[i].name = name;
704: }
705: else /* unmap the key */
706: {
707: mapped[i].len = 0;
708: }
709: }
710:
711: /* Dump keys of a given type - WHEN_VICMD dumps the ":map" keys, and
712: * WHEN_VIINP|WHEN_VIREP dumps the ":map!" keys
713: */
714: void dumpkey(when)
715: int when; /* WHEN_XXXX of mappings to be dumped */
716: {
717: int i, len, mlen;
718: char *scan;
719: char *mraw;
720:
721: for (i = 0; i < MAXMAPS; i++)
722: {
723: /* skip unused entries, or entries that don't match "when" */
724: if (mapped[i].len <= 0 || !(mapped[i].when & when))
725: {
726: continue;
727: }
728:
729: /* dump the key label, if any */
730: len = 8;
731: if (mapped[i].name)
732: {
733: qaddstr(mapped[i].name);
734: len -= strlen(mapped[i].name);
735: }
736: do
737: {
738: qaddch(' ');
739: } while (len-- > 0);
740:
741: /* dump the raw version */
742: len = 0;
743: mlen = mapped[i].len;
744: mraw = mapped[i].rawin;
745: for (scan = mraw; scan < mraw + mlen; scan++)
746: {
747: if (UCHAR(*scan) < ' ' || *scan == '\177')
748: {
749: qaddch('^');
750: qaddch(*scan ^ '@');
751: len += 2;
752: }
753: else
754: {
755: qaddch(*scan);
756: len++;
757: }
758: }
759: do
760: {
761: qaddch(' ');
762: } while (++len < 8);
763:
764: /* dump the mapped version */
765: #ifndef NO_EXTENSIONS
766: if ((mapped[i].when & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
767: {
768: qaddstr("visual ");
769: }
770: #endif
771: for (scan = mapped[i].cooked; *scan; scan++)
772: {
773: if (UCHAR(*scan) < ' ' || *scan == '\177')
774: {
775: qaddch('^');
776: qaddch(*scan ^ '@');
777: }
778: else
779: {
780: qaddch(*scan);
781: }
782: }
783:
784: addch('\n');
785: exrefresh();
786: }
787: }
788:
789:
790:
791: #ifndef MKEXRC
792: /* This function saves the current configuration of mapped keys to a file */
793: void savekeys(fd)
794: int fd; /* file descriptor to save them to */
795: {
796: int i;
797: char buf[80];
798:
799: /* now write a map command for each key other than the arrows */
800: for (i = 0; i < MAXMAPS; i++)
801: {
802: /* ignore keys that came from termcap */
803: if (mapped[i].name)
804: {
805: continue;
806: }
807:
808: /* If this isn't used, ignore it */
809: if (mapped[i].len <= 0)
810: {
811: continue;
812: }
813:
814: /* write the map command */
815: #ifndef NO_EXTENSIONS
816: if (mapped[i].when & WHEN_INMV)
817: {
818: #if OSK
819: char fmt[80];
820: sprintf(fmt, "map%%s %%.%ds %%s\n", mapped[i].len);
821: sprintf(buf, fmt,
822: (mapped[i].when & WHEN_VICMD) ? "" : "!",
823: #else
824: sprintf(buf, "map%s %.*s visual %s\n",
825: (mapped[i].when & WHEN_VICMD) ? "" : "!",
826: mapped[i].len,
827: #endif
828: mapped[i].rawin,
829: mapped[i].cooked);
830: twrite(fd, buf, strlen(buf));
831: }
832: else
833: #endif
834: {
835: if (mapped[i].when & WHEN_VICMD)
836: {
837: #if OSK
838: char fmt[80];
839: sprintf(fmt, "map %%.%ds %%s\n", mapped[i].len);
840: sprintf(buf, fmt,
841: #else
842: sprintf(buf, "map %.*s %s\n", mapped[i].len,
843: #endif
844: mapped[i].rawin,
845: mapped[i].cooked);
846: twrite(fd, buf, strlen(buf));
847: }
848: if (mapped[i].when & (WHEN_VIINP | WHEN_VIREP))
849: {
850: #if OSK
851: char fmt[80];
852: sprintf(fmt, "map! %%.%ds %%s\n", mapped[i].len);
853: sprintf(buf, fmt,
854: #else
855: sprintf(buf, "map! %.*s %s\n", mapped[i].len,
856: #endif
857: mapped[i].rawin,
858: mapped[i].cooked);
859: twrite(fd, buf, strlen(buf));
860: }
861: }
862: }
863: }
864: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.