home *** CD-ROM | disk | FTP | other *** search
- /* tio.c */
-
- /* Author:
- * Steve Kirkendall
- * 16820 SW Tallac Way
- * Beaverton, OR 97006
- * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
- */
-
-
- /* This file contains terminal I/O functions */
-
- #include <signal.h>
- #include "vi.h"
-
-
- /* This function reads in a line from the terminal. */
- int vgets(prompt, buf, bsize)
- char prompt; /* the prompt character, or '\0' for none */
- char *buf; /* buffer into which the string is read */
- int bsize; /* size of the buffer */
- {
- int len; /* how much we've read so far */
- int ch; /* a character from the user */
- int quoted; /* is the next char quoted? */
- int tab; /* column position of cursor */
- char widths[132]; /* widths of characters */
-
- /* show the prompt */
- move(LINES - 1, 0);
- tab = 0;
- if (prompt)
- {
- addch(prompt);
- tab = 1;
- }
- clrtoeol();
- refresh();
-
- /* read in the line */
- quoted = len = 0;
- for (;;)
- {
- ch = getkey(quoted ? 0 : WHEN_EX);
-
- /* some special conversions */
- if (ch == ctrl('D') && len == 0)
- ch = ctrl('[');
-
- /* inhibit detection of special chars (except ^J) after a ^V */
- if (quoted && ch != '\n')
- {
- ch |= 256;
- }
-
- /* process the character */
- switch(ch)
- {
- case ctrl('V'):
- qaddch('^');
- qaddch('\b');
- quoted = TRUE;
- break;
-
- case ctrl('['):
- return -1;
-
- case '\n':
- case '\r':
- clrtoeol();
- goto BreakBreak;
-
- case '\b':
- if (len > 0)
- {
- len--;
- addstr("\b\b\b\b\b\b\b\b" + 8 - widths[len]);
- tab -= widths[len];
- }
- else
- {
- return -1;
- }
- break;
-
- default:
- /* strip off quotation bit */
- if (ch & 256)
- {
- ch &= ~256;
- quoted = FALSE;
- qaddch(' ');
- qaddch('\b');
- }
- /* add & echo the char */
- if (len < bsize - 1)
- {
- if (ch == '\t')
- {
- widths[len] = *o_tabstop - (tab % *o_tabstop);
- addstr(" " + 8 - widths[len]);
- tab += widths[len];
- }
- else if (ch < ' ')
- {
- addch('^');
- addch(ch + '@');
- widths[len] = 2;
- tab += 2;
- }
- else if (ch == '\177')
- {
- addch('^');
- addch('?');
- widths[len] = 2;
- tab += 2;
- }
- else
- {
- addch(ch);
- widths[len] = 1;
- tab++;
- }
- buf[len++] = ch;
- }
- else
- {
- beep();
- }
- }
- }
- BreakBreak:
- refresh();
- buf[len] = '\0';
- return len;
- }
-
-
- /* ring the terminal's bell */
- beep()
- {
- if (*o_vbell)
- {
- tputs(VB, 1, faddch);
- refresh();
- }
- else
- {
- write(1, "\007", 1);
- }
- }
-
- static manymsgs; /* This variable keeps msgs from overwriting each other */
-
- /* Write a message in an appropriate way. This should really be a varargs
- * function, but there is no such thing as vwprintw. Hack!!! Also uses a
- * little sleaze in the way it saves messages for repetition later.
- *
- * msg((char *)0) - repeats the previous message
- * msg("") - clears the message line
- * msg("%s %d", ...) - does a printf onto the message line
- */
- msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
- char *fmt;
- char *arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7;
- {
- static char pmsg[80]; /* previous message */
- char *start; /* start of current message */
-
- if (mode != MODE_VI)
- {
- wprintw(stdscr, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
- addch('\n');
- exrefresh();
- }
- else
- {
- /* redrawing previous message? */
- if (!fmt)
- {
- move(LINES - 1, 0);
- standout();
- qaddch(' ');
- addstr(pmsg);
- qaddch(' ');
- standend();
- clrtoeol();
- return;
- }
-
- /* just blanking out message line? */
- if (!*fmt)
- {
- if (!*pmsg) return;
- *pmsg = '\0';
- move(LINES - 1, 0);
- clrtoeol();
- return;
- }
-
- /* wait for keypress between consecutive msgs */
- if (manymsgs)
- {
- qaddstr("[More...]");
- wqrefresh(stdscr);
- getkey(0);
- }
-
- /* real message */
- move(LINES - 1, 0);
- standout();
- qaddch(' ');
- start = stdscr;
- wprintw(stdscr, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
- strcpy(pmsg, start);
- qaddch(' ');
- standend();
- clrtoeol();
- refresh();
- }
- manymsgs = TRUE;
- }
-
-
- /* This function calls refresh() if the option exrefresh is set */
- exrefresh()
- {
- /* If this ex command wrote ANYTHING set exwrote so vi's : command
- * can tell that it must wait for a user keystroke before redrawing.
- */
- if (stdscr > kbuf)
- {
- exwrote = TRUE;
- }
-
- /* now we do the refresh thing */
- if (*o_exrefresh)
- {
- refresh();
- }
- else
- {
- wqrefresh(stdscr);
- }
- manymsgs = FALSE;
- }
-
-
- /* This variable holds a single ungotten key, or 0 for no key */
- static int ungotten;
- ungetkey(key)
- int key;
- {
- ungotten = key;
- }
-
- /* This array describes mapped key sequences */
- static struct _keymap
- {
- char rawin[LONGKEY]; /* the unmapped version of input */
- char cooked[80]; /* the mapped version of input */
- int len; /* length of the unmapped version */
- int when; /* when is this key mapped? */
- }
- mapped[MAXMAPS];
-
- /* This function reads in a keystroke for VI mode. It automatically handles
- * key mapping.
- */
- static int dummy(){} /* for timeout */
- int getkey(when)
- int when; /* which bits must be ON? */
- {
- static char keybuf[100]; /* array of already-read keys */
- static int nkeys; /* total number of keys in keybuf */
- static int next; /* index of next key to return */
- static char *cooked; /* rawin, or pointer to converted key */
- register char *kptr; /* &keybuf[next] */
- register struct _keymap *km; /* used to count through keymap */
- register int i, j, k;
-
- /* if this key is needed for delay between multiple error messages,
- * then reset the manymsgs flag and abort any mapped key sequence.
- */
- if (manymsgs)
- {
- manymsgs = FALSE;
- cooked = (char *)0;
- ungotten = 0;
- }
-
- /* if we have an ungotten key, use it */
- if (ungotten != 0)
- {
- k = ungotten;
- ungotten = 0;
- return k;
- }
-
- /* if we're doing a mapped key, get the next char */
- if (cooked && *cooked)
- {
- return *cooked++;
- }
-
- /* if keybuf is empty, fill it */
- if (next == nkeys)
- {
- /* redraw if getting a VI command, refresh always */
- if (when & WHEN_VICMD)
- {
- redraw(cursor, FALSE);
- }
- refresh();
-
- /* read the rawin keystrokes */
- while ((nkeys = read(0, keybuf, sizeof keybuf)) < 0)
- {
- /* terminal was probably resized */
- *o_lines = LINES;
- *o_columns = COLS;
- if (when & (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP))
- {
- redraw(MARK_UNSET, FALSE);
- redraw(cursor, (when & WHEN_VICMD) == 0);
- refresh();
- }
- }
- next = 0;
- }
-
- /* see how many mapped keys this might be */
- kptr = &keybuf[next];
- for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
- {
- if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
- {
- if (km->len > nkeys - next
- && !strncmp(km->rawin, kptr, nkeys - next))
- {
- j++;
- }
- else if (km->len <= nkeys - next
- && !strncmp(km->rawin, kptr, km->len))
- {
- j++;
- k = i;
- }
- }
- }
-
- /* if more than one, try to read some more */
- if (j > 1 && *o_keytime > 0)
- {
- alarm((unsigned)*o_keytime);
- k = read(0, keybuf + nkeys, sizeof keybuf - nkeys);
- alarm(0);
-
- /* if we couldn't read any more, pretend 0 mapped keys */
- if (k < 1)
- {
- j = 0;
- }
- else /* else we got some more - try again */
- {
- nkeys += k;
- for (i = j = 0, km = mapped; i < MAXMAPS; i++, km++)
- {
- if ((km->when & when)
- && km->len <= nkeys - next
- && *km->rawin == *kptr
- && !strncmp(km->rawin, kptr, km->len))
- {
- j++;
- k = i;
- }
- }
- }
- }
-
- /* if unambiguously mapped key, use it! */
- if (j == 1 && k >= 0)
- {
- next += mapped[k].len;
- cooked = mapped[k].cooked;
- return *cooked++;
- }
- else
- /* assume key is unmapped, but still translate weird erase key to '\b' */
- if (keybuf[next] == ERASEKEY && when != 0)
- {
- next++;
- return '\b';
- }
- else
- {
- return keybuf[next++];
- }
- }
-
-
- mapkey(rawin, cooked, when)
- char *rawin; /* the input key sequence, before mapping */
- char *cooked;/* after mapping */
- {
- int i, j;
-
- /* see if the key sequence was mapped before */
- j = strlen(rawin);
- for (i = 0; i < MAXMAPS; i++)
- {
- if (mapped[i].len == j && !strncmp(mapped[i].rawin, rawin, j))
- {
- break;
- }
- }
-
- /* if not, then try to find a new slot to use */
- if (i == MAXMAPS)
- {
- for (i = 0; i < MAXMAPS && mapped[i].len > 0; i++)
- {
- }
- }
-
- /* no room for the new key? */
- if (i == MAXMAPS)
- {
- msg("No room left in the key map table");
- return;
- }
-
- if (cooked && *cooked)
- {
- /* Map the key */
- mapped[i].len = j;
- strncpy(mapped[i].rawin, rawin, j);
- strcpy(mapped[i].cooked, cooked);
- mapped[i].when = when;
- }
- else
- {
- mapped[i].len = 0;
- }
- }
-
-
- dumpkey()
- {
- int i;
- char *scan;
-
- for (i = 0; i < MAXMAPS; i++)
- {
- /* skip unused entries */
- if (mapped[i].len <= 0)
- {
- continue;
- }
-
- /* dump the key label, if any */
- if (KU && !strncmp(KU, mapped[i].rawin, mapped[i].len))
- {
- qaddstr("<up>");
- }
- else if (KD && !strncmp(KD, mapped[i].rawin, mapped[i].len))
- {
- qaddstr("<down>");
- }
- else if (KL && !strncmp(KL, mapped[i].rawin, mapped[i].len))
- {
- qaddstr("<left>");
- }
- else if (KR && !strncmp(KR, mapped[i].rawin, mapped[i].len))
- {
- qaddstr("<right>");
- }
- qaddch('\t');
-
- /* write a '!' if defined with map! */
- if (mapped[i].when & (WHEN_EX|WHEN_VIINP))
- {
- qaddch('!');
- }
- else
- {
- qaddch(' ');
- }
- qaddch(' ');
-
- /* dump the raw version */
- for (scan = mapped[i].rawin; scan < mapped[i].rawin + mapped[i].len; scan++)
- {
- if (*scan >= 0 && *scan < ' ' || *scan == '\177')
- {
- qaddch('^');
- qaddch(*scan ^ '@');
- }
- else
- {
- qaddch(*scan);
- }
- }
-
- qaddch('\t');
-
- /* dump the mapped version */
- for (scan = mapped[i].cooked; *scan; scan++)
- {
- if (*scan >= 0 && *scan < ' ' || *scan == '\177')
- {
- qaddch('^');
- qaddch(*scan ^ '@');
- }
- else
- {
- qaddch(*scan);
- }
- }
-
- addch('\n');
- }
- exrefresh();
- }
-
-
-
- /* This function saves the current configuration of mapped keys to a file */
- savekeys(fd)
- int fd; /* file descriptor to save them to */
- {
- int i;
-
- /* HACK! refresh the screen now, so the output buffer is empty. */
- refresh();
-
- /* now write a map command for each key other thna the arrows */
- for (i = 4; i < MAXMAPS; i++)
- {
- /* If this isn't used, ignore it */
- if (mapped[i].len <= 0)
- {
- continue;
- }
-
- /* write the map command */
- wprintw(stdscr, "map%c %.*s %s",
- (mapped[i].when & (WHEN_EX|WHEN_VIINP)) ? '!' : ' ',
- mapped[i].len, mapped[i].rawin,
- mapped[i].cooked);
- qaddch('\n');
- }
-
- /* now write the buffer to the file */
- if (stdscr != kbuf)
- {
- write(fd, kbuf, (stdscr - kbuf));
- stdscr = kbuf;
- }
- }
-