📄 term.c
字号:
/*- * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)term.c 8.64 (Berkeley) 4/17/94";#endif /* not lint */#include <sys/types.h>#include <sys/queue.h>#include <sys/time.h>#include <bitstring.h>#include <ctype.h>#include <errno.h>#include <limits.h>#include <locale.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <termios.h>#include <unistd.h>#include "compat.h"#include <curses.h>#include <db.h>#include <regex.h>#include "vi.h"#include "seq.h"static int keycmp __P((const void *, const void *));static int term_key_queue __P((SCR *));static void term_key_set __P((GS *, int, int));/* * If we're reading less than 20 characters, up the size of the tty buffer. * This shouldn't ever happen, other than the first time through, but it's * possible if a map is large enough. */#define term_read_grow(sp, tty) \ (tty)->nelem - (tty)->cnt >= 20 ? 0 : __term_read_grow(sp, tty, 64)static int __term_read_grow __P((SCR *, IBUF *, int));/* * XXX * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE. */typedef struct _tklist { char *ts; /* Key's termcap string. */ char *output; /* Corresponding vi command. */ char *name; /* Name. */ u_char value; /* Special value (for lookup). */} TKLIST;static TKLIST const c_tklist[] = { /* Command mappings. */ {"kA", "O", "insert line"}, {"kD", "x", "delete character"}, {"kd", "j", "cursor down"}, {"kE", "D", "delete to eol"}, {"kF", "\004", "scroll down"}, {"kH", "$", "go to eol"}, {"kh", "^", "go to sol"}, {"kI", "i", "insert at cursor"}, {"kL", "dd", "delete line"}, {"kl", "h", "cursor left"}, {"kN", "\006", "page down"}, {"kP", "\002", "page up"}, {"kR", "\025", "scroll up"}, {"kS", "dG", "delete to end of screen"}, {"kr", "l", "cursor right"}, {"ku", "k", "cursor up"}, {NULL},};static TKLIST const m1_tklist[] = { /* Input mappings (lookup). */ {"kl", NULL, "cursor erase", K_VERASE}, {NULL},};static TKLIST const m2_tklist[] = { /* Input mappings (set or delete). */ {"kd", NULL, "cursor down"}, {"ku", NULL, "cursor up"}, {"kr", " ", "cursor space"}, {NULL},};/* * !!! * Historic ex/vi always used: * * ^D: autoindent deletion * ^H: last character deletion * ^W: last word deletion * ^Q: quote the next character (if not used in flow control). * ^V: quote the next character * * regardless of the user's choices for these characters. The user's erase * and kill characters worked in addition to these characters. Ex was not * completely consistent with this scheme, as it did map the scroll command * to the user's current EOF character. This implementation wires down the * above characters, but in addition uses the VERASE, VINTR, VKILL and VWERASE * characters described by the user's termios structure. We don't do the EOF * mapping for ex, but I think I'm unlikely to get caught on that one. * * XXX * THIS REQUIRES THAT ALL SCREENS SHARE A SPECIAL KEY SET. */typedef struct _keylist { u_char value; /* Special value. */ CHAR_T ch; /* Key. */} KEYLIST;static KEYLIST keylist[] = { {K_CARAT, '^'}, /* ^ */ {K_CNTRLD, '\004'}, /* ^D */ {K_CNTRLR, '\022'}, /* ^R */ {K_CNTRLT, '\024'}, /* ^T */ {K_CNTRLZ, '\032'}, /* ^Z */ {K_COLON, ':'}, /* : */ {K_CR, '\r'}, /* \r */ {K_ESCAPE, '\033'}, /* ^[ */ {K_FORMFEED, '\f'}, /* \f */ {K_HEXCHAR, '\030'}, /* ^X */ {K_NL, '\n'}, /* \n */ {K_RIGHTBRACE, '}'}, /* } */ {K_RIGHTPAREN, ')'}, /* ) */ {K_TAB, '\t'}, /* \t */ {K_VERASE, '\b'}, /* \b */ {K_VINTR, '\003'}, /* ^C */ {K_VKILL, '\025'}, /* ^U */ {K_VLNEXT, '\021'}, /* ^Q */ {K_VLNEXT, '\026'}, /* ^V */ {K_VWERASE, '\027'}, /* ^W */ {K_ZERO, '0'}, /* 0 */ {K_NOTUSED, 0}, /* VERASE, VINTR, VKILL, VWERASE */ {K_NOTUSED, 0}, {K_NOTUSED, 0}, {K_NOTUSED, 0},};static int nkeylist = (sizeof(keylist) / sizeof(keylist[0])) - 4;/* * term_init -- * Initialize the special key lookup table, and the special keys * defined by the terminal's termcap entry. */intterm_init(sp) SCR *sp;{ GS *gp; KEYLIST *kp; TKLIST const *tkp; int cnt; char *sbp, *t, buf[2 * 1024], sbuf[128]; /* * XXX * 8-bit only, for now. Recompilation should get you any * 8-bit character set, as long as nul isn't a character. */ (void)setlocale(LC_ALL, ""); key_init(sp); gp = sp->gp;#ifdef VERASE term_key_set(gp, VERASE, K_VERASE);#endif#ifdef VINTR term_key_set(gp, VINTR, K_VINTR);#endif#ifdef VKILL term_key_set(gp, VKILL, K_VKILL);#endif#ifdef VWERASE term_key_set(gp, VWERASE, K_VWERASE);#endif /* Sort the special key list. */ qsort(keylist, nkeylist, sizeof(keylist[0]), keycmp); /* Initialize the fast lookup table. */ for (gp->max_special = 0, kp = keylist, cnt = nkeylist; cnt--; ++kp) { if (gp->max_special < kp->value) gp->max_special = kp->value; if (kp->ch <= MAX_FAST_KEY) gp->special_key[kp->ch] = kp->value; } /* Set key sequences found in the termcap entry. */#ifndef SYSV_CURSES if (term_tgetent(sp, buf, O_STR(sp, O_TERM))) return (0);#endif /* Command mappings. */ for (tkp = c_tklist; tkp->name != NULL; ++tkp) {#ifdef SYSV_CURSES if ((t = tigetstr(tkp->ts)) == (char *)-1) continue;#else sbp = sbuf; if ((t = tgetstr(tkp->ts, &sbp)) == NULL) continue;#endif if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t), tkp->output, strlen(tkp->output), SEQ_COMMAND, 0)) return (1); } /* Input mappings needing to be looked up. */ for (tkp = m1_tklist; tkp->name != NULL; ++tkp) {#ifdef SYSV_CURSES if ((t = tigetstr(tkp->ts)) == (char *)-1) continue;#else sbp = sbuf; if ((t = tgetstr(tkp->ts, &sbp)) == NULL) continue;#endif for (kp = keylist;; ++kp) if (kp->value == tkp->value) break; if (kp == NULL) continue; if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t), &kp->ch, 1, SEQ_INPUT, 0)) return (1); } /* Input mappings that are already set or are text deletions. */ for (tkp = m2_tklist; tkp->name != NULL; ++tkp) {#ifdef SYSV_CURSES if ((t = tigetstr(tkp->ts)) == (char *)-1) continue;#else sbp = sbuf; if ((t = tgetstr(tkp->ts, &sbp)) == NULL) continue;#endif if (tkp->output == NULL) { if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t), NULL, 0, SEQ_INPUT, 0)) return (1); } else if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t), tkp->output, strlen(tkp->output), SEQ_INPUT, 0)) return (1); } return (0);}#ifndef SYSV_CURSES/* * term_tgetent -- * Routine to fill in the tgetent buffer, broken out so that * the error code isn't endlessly repeated. */intterm_tgetent(sp, buf, term) SCR *sp; char *buf, *term;{ if (term == NULL) { msgq(sp, M_ERR, "No terminal type set."); return (1); } switch (tgetent(buf, term)) { case -1: msgq(sp, M_SYSERR, "tgetent: %s", term); return (1); case 0: msgq(sp, M_ERR, "%s: unknown terminal type.", term); return (1); } return (0);}#endif/* * term_key_set -- * Set keys found in the termios structure. VERASE, VINTR and VKILL are * required by POSIX 1003.1-1990, VWERASE is a 4.4BSD extension. We've * left four open slots in the keylist table, if these values exist, put * them into place. Note, they may reset (or duplicate) values already * in the table, so we check for that first. */static voidterm_key_set(gp, name, val) GS *gp; int name, val;{ KEYLIST *kp; cc_t ch; if (!F_ISSET(gp, G_TERMIOS_SET)) return; if ((ch = gp->original_termios.c_cc[(name)]) == _POSIX_VDISABLE) return; /* Check for duplication. */ for (kp = keylist; kp->value != K_NOTUSED; ++kp) if (kp->ch == ch) { kp->value = val; return; } /* Add a new entry. */ if (kp->value == K_NOTUSED) { keylist[nkeylist].ch = ch; keylist[nkeylist].value = val; ++nkeylist; }}/* * key_init -- * Build the fast-lookup key display array. */voidkey_init(sp) SCR *sp;{ CHAR_T ch; /* * XXX * Assume that nul is not a legal character. */ for (ch = 0; ch <= MAX_FAST_KEY; ++ch) { (void)__key_name(sp, ch); (void)memmove(sp->gp->cname[ch].name, sp->cname, sp->gp->cname[ch].len = sp->clen); }}/* * __key_len -- * Return the length of the string that will display the key. * This routine is the backup for the KEY_LEN() macro. */size_t__key_len(sp, ch) SCR *sp; ARG_CHAR_T ch;{ (void)__key_name(sp, ch); return (sp->clen);}/* * __key_name -- * Return the string that will display the key. This routine * is the backup for the KEY_NAME() macro. */CHAR_T *__key_name(sp, ach) SCR *sp; ARG_CHAR_T ach;{ static const CHAR_T hexdigit[] = "0123456789abcdef"; static const CHAR_T octdigit[] = "01234567"; CHAR_T ch, *chp, mask; size_t len; int cnt, shift; /* * Historical mappings. Printable characters are left alone. Control * characters less than '\177' are represented as '^' followed by the * character offset from the '@' character in the ASCII map. '\177' is * represented as '^' followed by '?'. * * XXX * The following code depends on the current locale being identical to * the ASCII map from '\100' to '\076' (\076 since that's the largest * character for which we can offset from '@' and get something that's * a printable character in ASCII. I'm told that this is a reasonable * assumption... * * XXX * This code will only work with CHAR_T's that are multiples of 8-bit * bytes. * * XXX * NB: There's an assumption here that all printable characters take * up a single column on the screen. This is not always correct. */ ch = ach; if (isprint(ch)) { sp->cname[0] = ch; len = 1; } else if (ch <= '\076' && iscntrl(ch)) { sp->cname[0] = '^'; sp->cname[1] = ch == '\177' ? '?' : '@' + ch; len = 2; } else if (O_ISSET(sp, O_OCTAL)) {#define BITS (sizeof(CHAR_T) * 8)#define SHIFT (BITS - BITS % 3)#define TOPMASK (BITS % 3 == 2 ? 3 : 1) << BITS - BITS % 3 sp->cname[0] = '\\'; sp->cname[1] = octdigit[(ch & TOPMASK) >> SHIFT]; shift = SHIFT - 3; for (len = 2, mask = 7 << SHIFT - 3, cnt = BITS / 3; cnt-- > 0; mask >>= 3, shift -= 3) sp->cname[len++] = octdigit[(ch & mask) >> shift]; } else { sp->cname[0] = '0'; sp->cname[1] = 'x'; for (len = 2, chp = (u_int8_t *)&ch, cnt = sizeof(CHAR_T); cnt-- > 0; ++chp) { sp->cname[len++] = hexdigit[(*chp & 0xf0) >> 4]; sp->cname[len++] = hexdigit[*chp & 0x0f]; } } sp->cname[sp->clen = len] = '\0'; return (sp->cname);}/* * term_push -- * Push keys onto the front of a buffer. * * There is a single input buffer in ex/vi. Characters are read onto the * end of the buffer by the terminal input routines, and pushed onto the * front of the buffer by various other functions in ex/vi. Each key has * an associated flag value, which indicates if it has already been quoted,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -