aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/term.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/term.c')
-rw-r--r--src/nvim/term.c1514
1 files changed, 0 insertions, 1514 deletions
diff --git a/src/nvim/term.c b/src/nvim/term.c
deleted file mode 100644
index a16c1a59ca..0000000000
--- a/src/nvim/term.c
+++ /dev/null
@@ -1,1514 +0,0 @@
-/*
- * VIM - Vi IMproved by Bram Moolenaar
- *
- * Do ":help uganda" in Vim to read copying and usage conditions.
- * Do ":help credits" in Vim to see a list of people who contributed.
- * See README.txt for an overview of the Vim source code.
- */
-/*
- *
- * term.c: functions for controlling the terminal
- *
- * primitive termcap support for Win32 included
- *
- * NOTE: padding and variable substitution is not performed,
- * when compiling without HAVE_TGETENT, we use tputs() and tgoto() dummies.
- */
-
-/*
- * Some systems have a prototype for tgetstr() with (char *) instead of
- * (char **). This define removes that prototype. We include our own prototype
- * below.
- */
-
-#define tgetstr tgetstr_defined_wrong
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <string.h>
-
-#include "nvim/vim.h"
-#include "nvim/ascii.h"
-#include "nvim/term.h"
-#include "nvim/buffer.h"
-#include "nvim/charset.h"
-#include "nvim/edit.h"
-#include "nvim/eval.h"
-#include "nvim/ex_getln.h"
-#include "nvim/fileio.h"
-#include "nvim/getchar.h"
-#include "nvim/message.h"
-#include "nvim/misc2.h"
-#include "nvim/garray.h"
-#include "nvim/keymap.h"
-#include "nvim/memory.h"
-#include "nvim/move.h"
-#include "nvim/mouse.h"
-#include "nvim/normal.h"
-#include "nvim/option.h"
-#include "nvim/os_unix.h"
-#include "nvim/popupmnu.h"
-#include "nvim/screen.h"
-#include "nvim/strings.h"
-#include "nvim/syntax.h"
-#include "nvim/ui.h"
-#include "nvim/window.h"
-#include "nvim/os/os.h"
-#include "nvim/os/time.h"
-#include "nvim/os/input.h"
-
-
-#undef tgetstr
-
-/*
- * Here are the builtin termcap entries. They are not stored as complete
- * structures with all entries, as such a structure is too big.
- *
- * The entries are compact, therefore they normally are included even when
- * HAVE_TGETENT is defined. When HAVE_TGETENT is defined, the builtin entries
- * can be accessed with "builtin_ansi", "builtin_debug", etc.
- *
- * Each termcap is a list of builtin_term structures. It always starts with
- * KS_NAME, which separates the entries. See parse_builtin_tcap() for all
- * details.
- * bt_entry is either a KS_xxx code (>= 0), or a K_xxx code.
- *
- * Entries marked with "guessed" may be wrong.
- */
-struct builtin_term {
- int bt_entry;
- char *bt_string;
-};
-
-/* start of keys that are not directly used by Vim but can be mapped */
-#define BT_EXTRA_KEYS 0x101
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "term.c.generated.h"
-#endif
-
-
-static bool detected_8bit = false; // detected 8-bit terminal
-
-static struct builtin_term builtin_termcaps[] =
-{
- // abstract UI pseudo termcap, based on vim's "builtin_gui" termcap
- {(int)KS_NAME, "abstract_ui"},
- {(int)KS_CE, "\033|$"},
- {(int)KS_AL, "\033|i"},
- {(int)KS_CAL, "\033|%p1%dI"},
- {(int)KS_DL, "\033|d"},
- {(int)KS_CDL, "\033|%p1%dD"},
- {(int)KS_CS, "\033|%p1%d;%p2%dR"},
- {(int)KS_CSV, "\033|%p1%d;%p2%dV"},
- {(int)KS_CL, "\033|C"},
- // attributes switched on with 'h', off with * 'H'
- {(int)KS_ME, "\033|31H"}, // HL_ALL
- {(int)KS_MR, "\033|1h"}, // HL_INVERSE
- {(int)KS_MD, "\033|2h"}, // HL_BOLD
- {(int)KS_SE, "\033|16H"}, // HL_STANDOUT
- {(int)KS_SO, "\033|16h"}, // HL_STANDOUT
- {(int)KS_UE, "\033|8H"}, // HL_UNDERLINE
- {(int)KS_US, "\033|8h"}, // HL_UNDERLINE
- {(int)KS_CZR, "\033|4H"}, // HL_ITALIC
- {(int)KS_CZH, "\033|4h"}, // HL_ITALIC
- {(int)KS_VB, "\033|f"},
- {(int)KS_MS, "y"},
- {(int)KS_UT, "y"},
- {(int)KS_LE, "\b"}, // cursor-left = BS
- {(int)KS_ND, "\014"}, // cursor-right = CTRL-L
- {(int)KS_CM, "\033|%p1%d;%p2%dM"},
- // there are no key sequences here, for "abstract_ui" vim key codes are
- // parsed directly in input_enqueue()
- {(int)KS_NAME, NULL}
-
-}; /* end of builtin_termcaps */
-
-/*
- * DEFAULT_TERM is used, when no terminal is specified with -T option or $TERM.
- */
-
-
-
-#if defined(UNIX)
-# define DEFAULT_TERM (char_u *)"ansi"
-#endif
-
-
-
-
-
-#ifndef DEFAULT_TERM
-# define DEFAULT_TERM (char_u *)"dumb"
-#endif
-
-/// Sets up the terminal window for use.
-///
-/// This must be done after resetting full_screen, otherwise it may move the
-/// cursor.
-///
-/// @remark We may call mch_exit() before calling this.
-void term_init(void)
-{
- Columns = 80;
- Rows = 24;
-
- // Prevent buffering output.
- // Output gets explicitly buffered and flushed by out_flush() at times like,
- // for example, when the user presses a key. Without this line, vim will not
- // render the screen correctly.
- setbuf(stdout, NULL);
-
- out_flush();
-}
-
-/*
- * Term_strings contains currently used terminal output strings.
- * It is initialized with the default values by parse_builtin_tcap().
- * The values can be changed by setting the option with the same name.
- */
-char_u *(term_strings[(int)KS_LAST + 1]);
-
-static bool need_gather = false; // need to fill termleader[]
-
-static struct builtin_term *find_builtin_term(char_u *term)
-{
- struct builtin_term *p = builtin_termcaps;
- while (p->bt_string != NULL) {
- if (p->bt_entry == (int)KS_NAME) {
-#ifdef UNIX
- if (STRCMP(p->bt_string, "xterm") == 0 && vim_is_xterm(term))
- return p;
- else
-#endif
- if (STRCMP(term, p->bt_string) == 0)
- return p;
- }
- ++p;
- }
- return p;
-}
-
-/*
- * Parsing of the builtin termcap entries.
- * Caller should check if 'name' is a valid builtin term.
- * The terminal's name is not set, as this is already done in termcapinit().
- */
-static void parse_builtin_tcap(char_u *term)
-{
- char_u name[2];
-
- struct builtin_term *p = find_builtin_term(term);
- bool term_8bit = term_is_8bit(term);
-
- /* Do not parse if builtin term not found */
- if (p->bt_string == NULL)
- return;
-
- for (++p; p->bt_entry != (int)KS_NAME && p->bt_entry != BT_EXTRA_KEYS; ++p) {
- if ((int)p->bt_entry >= 0) { /* KS_xx entry */
- /* Only set the value if it wasn't set yet. */
- if (term_strings[p->bt_entry] == NULL
- || term_strings[p->bt_entry] == empty_option) {
- /* 8bit terminal: use CSI instead of <Esc>[ */
- if (term_8bit && term_7to8bit((char_u *)p->bt_string) != 0) {
- char_u *s, *t;
-
- s = vim_strsave((char_u *)p->bt_string);
- for (t = s; *t; ++t)
- if (term_7to8bit(t)) {
- *t = term_7to8bit(t);
- STRCPY(t + 1, t + 2);
- }
- term_strings[p->bt_entry] = s;
- set_term_option_alloced(&term_strings[p->bt_entry]);
- } else
- term_strings[p->bt_entry] = (char_u *)p->bt_string;
- }
- } else {
- name[0] = (char_u)KEY2TERMCAP0(p->bt_entry);
- name[1] = (char_u)KEY2TERMCAP1(p->bt_entry);
- if (find_termcode(name) == NULL)
- add_termcode(name, (char_u *)p->bt_string, term_8bit);
- }
- }
-}
-
-/*
- * Set terminal options for terminal "term".
- * Return OK if terminal 'term' was found in a termcap, FAIL otherwise.
- *
- * While doing this, until ttest(), some options may be NULL, be careful.
- */
-int set_termname(char_u *term)
-{
- int width = 0, height = 0;
- char_u *error_msg = NULL;
- char_u *bs_p, *del_p;
-
- /* In silect mode (ex -s) we don't use the 'term' option. */
- if (silent_mode)
- return OK;
-
- term = (uint8_t *)"abstract_ui";
- detected_8bit = false; // reset 8-bit detection
-
- if (term_is_builtin(term)) {
- term += 8;
- }
-
- /*
- * If HAVE_TGETENT is not defined, only the builtin termcap is used, otherwise:
- * If builtin_first is TRUE:
- * 0. try builtin termcap
- * 1. try external termcap
- * 2. if both fail default to a builtin terminal
- * If builtin_first is FALSE:
- * 1. try external termcap
- * 2. try builtin termcap, if both fail default to a builtin terminal
- */
- /*
- * Use builtin termcap
- */
- {
- /*
- * search for 'term' in builtin_termcaps[]
- */
- struct builtin_term *termp = find_builtin_term(term);
- if (termp->bt_string == NULL) { /* did not find it */
-
- mch_errmsg("\r\n");
- if (error_msg != NULL) {
- mch_errmsg((char *)error_msg);
- mch_errmsg("\r\n");
- }
- mch_errmsg("'");
- mch_errmsg((char *)term);
- mch_errmsg(_("' not known. Available builtin terminals are:"));
- mch_errmsg("\r\n");
- for (termp = &(builtin_termcaps[0]); termp->bt_string != NULL;
- ++termp) {
- if (termp->bt_entry == (int)KS_NAME) {
- mch_errmsg(" ");
- mch_errmsg(termp->bt_string);
- mch_errmsg("\r\n");
- }
- }
- /* when user typed :set term=xxx, quit here */
- if (starting != NO_SCREEN) {
- screen_start(); /* don't know where cursor is now */
- wait_return(TRUE);
- return FAIL;
- }
- term = DEFAULT_TERM;
- mch_errmsg(_("defaulting to '"));
- mch_errmsg((char *)term);
- mch_errmsg("'\r\n");
- if (emsg_silent == 0) {
- screen_start(); /* don't know where cursor is now */
- out_flush();
- os_delay(2000L, true);
- }
- set_string_option_direct((char_u *)"term", -1, term,
- OPT_FREE, 0);
- display_errors();
- }
- out_flush();
- clear_termoptions(); /* clear old options */
- parse_builtin_tcap(term);
- }
-
- /*
- * special: There is no info in the termcap about whether the cursor
- * positioning is relative to the start of the screen or to the start of the
- * scrolling region. We just guess here. Only msdos pcterm is known to do it
- * relative.
- */
- if (STRCMP(term, "pcterm") == 0)
- T_CCS = (char_u *)"yes";
- else
- T_CCS = empty_option;
-
- /*
- * If the termcap has no entry for 'bs' and/or 'del' and the ioctl() also
- * didn't work, use the default CTRL-H
- * The default for t_kD is DEL, unless t_kb is DEL.
- * The vim_strsave'd strings are probably lost forever, well it's only two
- * bytes. Don't do this when the GUI is active, it uses "t_kb" and "t_kD"
- * directly.
- */
- {
- bs_p = find_termcode((char_u *)"kb");
- del_p = find_termcode((char_u *)"kD");
- if (bs_p == NULL || *bs_p == NUL)
- add_termcode((char_u *)"kb", (bs_p = (char_u *)CTRL_H_STR), FALSE);
- if ((del_p == NULL || *del_p == NUL) &&
- (bs_p == NULL || *bs_p != DEL))
- add_termcode((char_u *)"kD", (char_u *)DEL_STR, FALSE);
- }
-
-#if defined(UNIX)
- term_is_xterm = vim_is_xterm(term);
-#endif
-
- ttest(TRUE); /* make sure we have a valid set of terminal codes */
-
- full_screen = TRUE; /* we can use termcap codes from now on */
- set_term_defaults(); /* use current values as defaults */
-
- /*
- * Initialize the terminal with the appropriate termcap codes.
- * Set the mouse and window title if possible.
- * Don't do this when starting, need to parse the .vimrc first, because it
- * may redefine t_TI etc.
- */
- if (starting != NO_SCREEN) {
- setmouse(); /* may start using the mouse */
- maketitle(); /* may display window title */
- }
-
- /* display initial screen after ttest() checking. jw. */
- if (width <= 0 || height <= 0) {
- /* termcap failed to report size */
- /* set defaults, in case ui_get_shellsize() also fails */
- width = 80;
- height = 24; /* most terminals are 24 lines */
- }
- screen_resize(width, height); // may change Rows
- if (starting != NO_SCREEN) {
- if (scroll_region)
- scroll_region_reset(); /* In case Rows changed */
- check_map_keycodes(); /* check mappings for terminal codes used */
-
- {
- /*
- * Execute the TermChanged autocommands for each buffer that is
- * loaded.
- */
- buf_T *old_curbuf = curbuf;
- for (curbuf = firstbuf; curbuf != NULL; curbuf = curbuf->b_next) {
- if (curbuf->b_ml.ml_mfp != NULL)
- apply_autocmds(EVENT_TERMCHANGED, NULL, NULL, FALSE,
- curbuf);
- }
- if (buf_valid(old_curbuf))
- curbuf = old_curbuf;
- }
- }
-
- return OK;
-}
-
-
-# define HMT_NORMAL 1
-# define HMT_NETTERM 2
-# define HMT_DEC 4
-# define HMT_URXVT 16
-# define HMT_SGR 32
-
-/*
- * Get a string entry from the termcap and add it to the list of termcodes.
- * Used for <t_xx> special keys.
- * Give an error message for failure when not sourcing.
- * If force given, replace an existing entry.
- * Return FAIL if the entry was not found, OK if the entry was added.
- */
-int add_termcap_entry(char_u *name, int force)
-{
- char_u *term;
- int key;
- struct builtin_term *termp;
-
- /*
- * If the GUI is running or will start in a moment, we only support the keys
- * that the GUI can produce.
- */
-
- if (!force && find_termcode(name) != NULL) /* it's already there */
- return OK;
-
- term = T_NAME;
- if (term == NULL || *term == NUL) /* 'term' not defined yet */
- return FAIL;
-
- if (term_is_builtin(term)) { /* name starts with "builtin_" */
- term += 8;
- }
-
- /*
- * Search in builtin termcap
- */
- {
- termp = find_builtin_term(term);
- if (termp->bt_string != NULL) { /* found it */
- key = TERMCAP2KEY(name[0], name[1]);
- while (termp->bt_entry != (int)KS_NAME) {
- if ((int)termp->bt_entry == key) {
- add_termcode(name, (char_u *)termp->bt_string,
- term_is_8bit(term));
- return OK;
- }
- ++termp;
- }
- }
- }
-
- if (sourcing_name == NULL) {
- EMSG2(_("E436: No \"%s\" entry in termcap"), name);
- }
- return FAIL;
-}
-
-static int term_is_builtin(char_u *name)
-{
- return STRNCMP(name, "builtin_", (size_t)8) == 0;
-}
-
-/*
- * Return true if terminal "name" uses CSI instead of <Esc>[.
- * Assume that the terminal is using 8-bit controls when the name contains
- * "8bit", like in "xterm-8bit".
- */
-bool term_is_8bit(char_u *name)
-{
- return detected_8bit || strstr((char *)name, "8bit") != NULL;
-}
-
-/*
- * Translate terminal control chars from 7-bit to 8-bit:
- * <Esc>[ -> CSI
- * <Esc>] -> <M-C-]>
- * <Esc>O -> <M-C-O>
- */
-static char_u term_7to8bit(char_u *p)
-{
- if (*p == ESC) {
- if (p[1] == '[')
- return CSI;
- if (p[1] == ']')
- return 0x9d;
- if (p[1] == 'O')
- return 0x8f;
- }
- return 0;
-}
-
-
-char_u *tltoa(unsigned long i)
-{
- static char_u buf[16];
- char_u *p = buf + 15;
- *p = '\0';
- do {
- --p;
- *p = (char_u) (i % 10 + '0');
- i /= 10;
- } while (i > 0 && p > buf);
- return p;
-}
-
-/*
- * Set the terminal name and initialize the terminal options.
- * If "name" is NULL or empty, get the terminal name from the environment.
- * If that fails, use the default terminal name.
- */
-void termcapinit(char_u *name)
-{
- if (name != NULL && *name == NUL)
- name = NULL; /* empty name is equal to no name */
- char_u *term = name;
-
- if (term == NULL)
- term = (char_u *)os_getenv("TERM");
- if (term == NULL || *term == NUL)
- term = DEFAULT_TERM;
- set_string_option_direct((char_u *)"term", -1, term, OPT_FREE, 0);
-
- /* Set the default terminal name. */
- set_string_default("term", term);
- set_string_default("ttytype", term);
-
- /*
- * Avoid using "term" here, because the next os_getenv() may overwrite it.
- */
- set_termname(T_NAME != NULL ? T_NAME : term);
-}
-
-/*
- * the number of calls to ui_write is reduced by using the buffer "out_buf"
- */
-# define OUT_SIZE 2047
-// Add one to allow term_write() in os_win32.c to append a NUL
-static char_u out_buf[OUT_SIZE + 1];
-static size_t out_pos = 0; /* number of chars in out_buf */
-
-// Clear the output buffer
-void out_buf_clear(void)
-{
- out_pos = 0;
-}
-
-/*
- * out_flush(): flush the output buffer
- */
-void out_flush(void)
-{
- size_t len = out_pos;
- out_pos = 0;
- ui_write(out_buf, len);
-}
-
-/*
- * Sometimes a byte out of a multi-byte character is written with out_char().
- * To avoid flushing half of the character, call this function first.
- */
-void out_flush_check(void)
-{
- if (enc_dbcs != 0 && out_pos >= OUT_SIZE - MB_MAXBYTES)
- out_flush();
-}
-
-/*
- * out_char(c): put a byte into the output buffer.
- * Flush it if it becomes full.
- * This should not be used for outputting text on the screen (use functions
- * like msg_puts() and screen_putchar() for that).
- */
-void out_char(char_u c)
-{
-#if defined(UNIX) || defined(MACOS_X_UNIX)
- if (c == '\n') /* turn LF into CR-LF (CRMOD doesn't seem to do this) */
- out_char('\r');
-#endif
-
- out_buf[out_pos++] = c;
-
- /* For testing we flush each time. */
- if (out_pos >= OUT_SIZE || p_wd)
- out_flush();
-}
-
-
-/*
- * out_char_nf(c): like out_char(), but don't flush when p_wd is set
- */
-static void out_char_nf(char_u c)
-{
-#if defined(UNIX) || defined(MACOS_X_UNIX)
- if (c == '\n') /* turn LF into CR-LF (CRMOD doesn't seem to do this) */
- out_char_nf('\r');
-#endif
-
- out_buf[out_pos++] = c;
-
- if (out_pos >= OUT_SIZE)
- out_flush();
-}
-
-/*
- * A never-padding out_str.
- * use this whenever you don't want to run the string through tputs.
- * tputs above is harmless, but tputs from the termcap library
- * is likely to strip off leading digits, that it mistakes for padding
- * information, and "%i", "%d", etc.
- * This should only be used for writing terminal codes, not for outputting
- * normal text (use functions like msg_puts() and screen_putchar() for that).
- */
-void out_str_nf(char_u *s)
-{
- if (out_pos > OUT_SIZE - 20) /* avoid terminal strings being split up */
- out_flush();
- while (*s)
- out_char_nf(*s++);
-
- /* For testing we write one string at a time. */
- if (p_wd)
- out_flush();
-}
-
-/*
- * out_str(s): Put a character string a byte at a time into the output buffer.
- * If HAVE_TGETENT is defined use the termcap parser. (jw)
- * This should only be used for writing terminal codes, not for outputting
- * normal text (use functions like msg_puts() and screen_putchar() for that).
- */
-void out_str(char_u *s)
-{
- if (s != NULL && *s) {
- /* avoid terminal strings being split up */
- if (out_pos > OUT_SIZE - 20)
- out_flush();
- while (*s)
- out_char_nf(*s++);
-
- /* For testing we write one string at a time. */
- if (p_wd)
- out_flush();
- }
-}
-
-/*
- * cursor positioning using termcap parser. (jw)
- */
-void term_windgoto(int row, int col)
-{
- char buf[32];
- snprintf(buf, sizeof(buf), "\033|%d;%dM", row, col);
- OUT_STR(buf);
-}
-
-void term_cursor_right(int i)
-{
- abort();
-}
-
-void term_append_lines(int line_count)
-{
- char buf[32];
- snprintf(buf, sizeof(buf), "\033|%dI", line_count);
- OUT_STR(buf);
-}
-
-void term_delete_lines(int line_count)
-{
- char buf[32];
- snprintf(buf, sizeof(buf), "\033|%dD", line_count);
- OUT_STR(buf);
-}
-
-/*
- * Make sure we have a valid set or terminal options.
- * Replace all entries that are NULL by empty_option
- */
-void ttest(int pairs)
-{
- check_options(); /* make sure no options are NULL */
-
- /*
- * MUST have "cm": cursor motion.
- */
- if (*T_CM == NUL)
- EMSG(_("E437: terminal capability \"cm\" required"));
-
- /*
- * if "cs" defined, use a scroll region, it's faster.
- */
- if (*T_CS != NUL)
- scroll_region = TRUE;
- else
- scroll_region = FALSE;
-
- if (pairs) {
- /*
- * optional pairs
- */
- /* TP goes to normal mode for TI (invert) and TB (bold) */
- if (*T_ME == NUL)
- T_ME = T_MR = T_MD = T_MB = empty_option;
- if (*T_SO == NUL || *T_SE == NUL)
- T_SO = T_SE = empty_option;
- if (*T_US == NUL || *T_UE == NUL)
- T_US = T_UE = empty_option;
- if (*T_CZH == NUL || *T_CZR == NUL)
- T_CZH = T_CZR = empty_option;
-
- /* T_VE is needed even though T_VI is not defined */
- if (*T_VE == NUL)
- T_VI = empty_option;
-
- /* if 'mr' or 'me' is not defined use 'so' and 'se' */
- if (*T_ME == NUL) {
- T_ME = T_SE;
- T_MR = T_SO;
- T_MD = T_SO;
- }
-
- /* if 'so' or 'se' is not defined use 'mr' and 'me' */
- if (*T_SO == NUL) {
- T_SE = T_ME;
- if (*T_MR == NUL)
- T_SO = T_MD;
- else
- T_SO = T_MR;
- }
-
- /* if 'ZH' or 'ZR' is not defined use 'mr' and 'me' */
- if (*T_CZH == NUL) {
- T_CZR = T_ME;
- if (*T_MR == NUL)
- T_CZH = T_MD;
- else
- T_CZH = T_MR;
- }
-
- /* "Sb" and "Sf" come in pairs */
- if (*T_CSB == NUL || *T_CSF == NUL) {
- T_CSB = empty_option;
- T_CSF = empty_option;
- }
-
- /* "AB" and "AF" come in pairs */
- if (*T_CAB == NUL || *T_CAF == NUL) {
- T_CAB = empty_option;
- T_CAF = empty_option;
- }
-
- /* if 'Sb' and 'AB' are not defined, reset "Co" */
- if (*T_CSB == NUL && *T_CAB == NUL)
- free_one_termoption(T_CCO);
-
- /* Set 'weirdinvert' according to value of 't_xs' */
- p_wiv = (*T_XS != NUL);
- }
- need_gather = true;
-
- /* Set t_colors to the value of t_Co. */
- t_colors = atoi((char *)T_CCO);
-}
-
-/*
- * Check if the new shell size is valid, correct it if it's too small or way
- * too big.
- */
-void check_shellsize(void)
-{
- if (Rows < min_rows()) /* need room for one window and command line */
- Rows = min_rows();
- limit_screen_size();
-}
-
-/*
- * Limit Rows and Columns to avoid an overflow in Rows * Columns.
- */
-void limit_screen_size(void)
-{
- if (Columns < MIN_COLUMNS)
- Columns = MIN_COLUMNS;
- else if (Columns > 10000)
- Columns = 10000;
- if (Rows > 1000)
- Rows = 1000;
-}
-
-/*
- * Invoked just before the screen structures are going to be (re)allocated.
- */
-void win_new_shellsize(void)
-{
- static long old_Rows = 0;
- static long old_Columns = 0;
-
- if (old_Rows != Rows) {
- /* if 'window' uses the whole screen, keep it using that */
- if (p_window == old_Rows - 1 || old_Rows == 0)
- p_window = Rows - 1;
- old_Rows = Rows;
- shell_new_rows(); /* update window sizes */
- }
- if (old_Columns != Columns) {
- old_Columns = Columns;
- shell_new_columns(); /* update window sizes */
- }
-}
-
-/*
- * Call this function when the Vim shell has been resized in any way.
- * Will obtain the current size and redraw (also when size didn't change).
- */
-void shell_resized(void)
-{
- ui_refresh();
-}
-
-/*
- * Check if the shell size changed. Handle a resize.
- * When the size didn't change, nothing happens.
- */
-void shell_resized_check(void)
-{
- long old_Rows = Rows;
- long old_Columns = Columns;
-
- if (!exiting) {
- check_shellsize();
- if (old_Rows != Rows || old_Columns != Columns)
- shell_resized();
- }
-}
-
-/*
- * Return TRUE when saving and restoring the screen.
- */
-int swapping_screen(void)
-{
- return full_screen && *T_TI != NUL;
-}
-
-/*
- * By outputting the 'cursor very visible' termcap code, for some windowed
- * terminals this makes the screen scrolled to the correct position.
- * Used when starting Vim or returning from a shell.
- */
-void scroll_start(void)
-{
- if (*T_VS != NUL) {
- out_str(T_VS);
- out_str(T_VE);
- screen_start(); /* don't know where cursor is now */
- }
-}
-
-/*
- * Enable the cursor.
- */
-void cursor_on(void)
-{
- ui_cursor_on();
-}
-
-/*
- * Disable the cursor.
- */
-void cursor_off(void)
-{
- ui_cursor_off();
-}
-
-/*
- * Set scrolling region for window 'wp'.
- * The region starts 'off' lines from the start of the window.
- * Also set the vertical scroll region for a vertically split window. Always
- * the full width of the window, excluding the vertical separator.
- */
-void scroll_region_set(win_T *wp, int off)
-{
- char buf[32];
-
- snprintf(buf, sizeof(buf), "\033|%d;%dR", wp->w_winrow + wp->w_height - 1,
- wp->w_winrow + off);
- OUT_STR(buf);
-
- if (wp->w_width != Columns) {
- snprintf(buf, sizeof(buf), "\033|%d;%dV", wp->w_wincol + wp->w_width - 1,
- wp->w_wincol);
- OUT_STR(buf);
- }
-
- screen_start(); /* don't know where cursor is now */
-}
-
-/*
- * Reset scrolling region to the whole screen.
- */
-void scroll_region_reset(void)
-{
- char buf[32];
-
- snprintf(buf, sizeof(buf), "\033|%d;%dR", (int)Rows - 1, 0);
- OUT_STR(buf);
- snprintf(buf, sizeof(buf), "\033|%d;%dV", (int)Columns - 1, 0);
- OUT_STR(buf);
-
- screen_start(); /* don't know where cursor is now */
-}
-
-/*
- * List of terminal codes that are currently recognized.
- */
-
-static struct termcode {
- char_u name[2]; /* termcap name of entry */
- char_u *code; /* terminal code (in allocated memory) */
- int len; /* STRLEN(code) */
- int modlen; /* length of part before ";*~". */
-} *termcodes = NULL;
-
-static size_t tc_max_len = 0; /* number of entries that termcodes[] can hold */
-static size_t tc_len = 0; /* current number of entries in termcodes[] */
-
-
-void clear_termcodes(void)
-{
- while (tc_len != 0)
- free(termcodes[--tc_len].code);
- free(termcodes);
- termcodes = NULL;
- tc_max_len = 0;
-
-
- need_gather = true; // need to fill termleader[]
-}
-
-#define ATC_FROM_TERM 55
-
-/*
- * Add a new entry to the list of terminal codes.
- * The list is kept alphabetical for ":set termcap"
- * "flags" is TRUE when replacing 7-bit by 8-bit controls is desired.
- * "flags" can also be ATC_FROM_TERM for got_code_from_term().
- */
-void add_termcode(char_u *name, char_u *string, int flags)
-{
- struct termcode *new_tc;
- size_t i, j;
-
- if (string == NULL || *string == NUL) {
- del_termcode(name);
- return;
- }
-
- char_u *s = vim_strsave(string);
-
- /* Change leading <Esc>[ to CSI, change <Esc>O to <M-O>. */
- if (flags != 0 && flags != ATC_FROM_TERM && term_7to8bit(string) != 0) {
- STRMOVE(s, s + 1);
- s[0] = term_7to8bit(string);
- }
- size_t len = STRLEN(s);
-
- need_gather = true; // need to fill termleader[]
-
- /*
- * need to make space for more entries
- */
- if (tc_len == tc_max_len) {
- tc_max_len += 20;
- new_tc = xmalloc(tc_max_len * sizeof(struct termcode));
- for (i = 0; i < tc_len; ++i)
- new_tc[i] = termcodes[i];
- free(termcodes);
- termcodes = new_tc;
- }
-
- /*
- * Look for existing entry with the same name, it is replaced.
- * Look for an existing entry that is alphabetical higher, the new entry
- * is inserted in front of it.
- */
- for (i = 0; i < tc_len; ++i) {
- if (termcodes[i].name[0] < name[0])
- continue;
- if (termcodes[i].name[0] == name[0]) {
- if (termcodes[i].name[1] < name[1])
- continue;
- /*
- * Exact match: May replace old code.
- */
- if (termcodes[i].name[1] == name[1]) {
- if (flags == ATC_FROM_TERM
- && (j = termcode_star(termcodes[i].code, termcodes[i].len)) > 0) {
- /* Don't replace ESC[123;*X or ESC O*X with another when
- * invoked from got_code_from_term(). */
- assert(termcodes[i].len >= 0);
- if (len == (size_t)termcodes[i].len - j
- && STRNCMP(s, termcodes[i].code, len - 1) == 0
- && s[len - 1] == termcodes[i].code[termcodes[i].len - 1]) {
- /* They are equal but for the ";*": don't add it. */
- free(s);
- return;
- }
- } else {
- /* Replace old code. */
- free(termcodes[i].code);
- --tc_len;
- break;
- }
- }
- }
- /*
- * Found alphabetical larger entry, move rest to insert new entry
- */
- for (j = tc_len; j > i; --j)
- termcodes[j] = termcodes[j - 1];
- break;
- }
-
- termcodes[i].name[0] = name[0];
- termcodes[i].name[1] = name[1];
- termcodes[i].code = s;
- assert(len <= INT_MAX);
- termcodes[i].len = (int)len;
-
- /* For xterm we recognize special codes like "ESC[42;*X" and "ESC O*X" that
- * accept modifiers. */
- termcodes[i].modlen = 0;
- j = termcode_star(s, (int)len);
- if (j > 0)
- termcodes[i].modlen = (int)(len - 1 - j);
- ++tc_len;
-}
-
-/*
- * Check termcode "code[len]" for ending in ;*X, <Esc>O*X or <M-O>*X.
- * The "X" can be any character.
- * Return 0 if not found, 2 for ;*X and 1 for O*X and <M-O>*X.
- */
-static unsigned int termcode_star(char_u *code, int len)
-{
- /* Shortest is <M-O>*X. With ; shortest is <CSI>1;*X */
- if (len >= 3 && code[len - 2] == '*') {
- if (len >= 5 && code[len - 3] == ';')
- return 2;
- if ((len >= 4 && code[len - 3] == 'O') || code[len - 3] == 'O' + 128)
- return 1;
- }
- return 0;
-}
-
-char_u *find_termcode(char_u *name)
-{
- for (size_t i = 0; i < tc_len; ++i)
- if (termcodes[i].name[0] == name[0] && termcodes[i].name[1] == name[1])
- return termcodes[i].code;
- return NULL;
-}
-
-char_u *get_termcode(size_t i)
-{
- if (i >= tc_len)
- return NULL;
- return &termcodes[i].name[0];
-}
-
-void del_termcode(char_u *name)
-{
- if (termcodes == NULL) /* nothing there yet */
- return;
-
- need_gather = true; // need to fill termleader[]
-
- for (size_t i = 0; i < tc_len; ++i)
- if (termcodes[i].name[0] == name[0] && termcodes[i].name[1] == name[1]) {
- del_termcode_idx(i);
- return;
- }
- /* not found. Give error message? */
-}
-
-static void del_termcode_idx(size_t idx)
-{
- free(termcodes[idx].code);
- --tc_len;
- for (size_t i = idx; i < tc_len; ++i)
- termcodes[i] = termcodes[i + 1];
-}
-
-static linenr_T orig_topline = 0;
-static int orig_topfill = 0;
-
-/*
- * Checking for double clicks ourselves.
- * "orig_topline" is used to avoid detecting a double-click when the window
- * contents scrolled (e.g., when 'scrolloff' is non-zero).
- */
-/*
- * Set orig_topline. Used when jumping to another window, so that a double
- * click still works.
- */
-void set_mouse_topline(win_T *wp)
-{
- orig_topline = wp->w_topline;
- orig_topfill = wp->w_topfill;
-}
-
-/*
- * Replace any terminal code strings in from[] with the equivalent internal
- * vim representation. This is used for the "from" and "to" part of a
- * mapping, and the "to" part of a menu command.
- * Any strings like "<C-UP>" are also replaced, unless 'cpoptions' contains
- * '<'.
- * K_SPECIAL by itself is replaced by K_SPECIAL KS_SPECIAL KE_FILLER.
- *
- * The replacement is done in result[] and finally copied into allocated
- * memory. If this all works well *bufp is set to the allocated memory and a
- * pointer to it is returned. If something fails *bufp is set to NULL and from
- * is returned.
- *
- * CTRL-V characters are removed. When "from_part" is TRUE, a trailing CTRL-V
- * is included, otherwise it is removed (for ":map xx ^V", maps xx to
- * nothing). When 'cpoptions' does not contain 'B', a backslash can be used
- * instead of a CTRL-V.
- */
-char_u *
-replace_termcodes (
- char_u *from,
- char_u **bufp,
- int from_part,
- int do_lt, /* also translate <lt> */
- int special /* always accept <key> notation */
-)
-{
- ssize_t i;
- size_t slen;
- char_u key;
- size_t dlen = 0;
- char_u *src;
- int do_backslash; /* backslash is a special character */
- int do_special; /* recognize <> key codes */
- int do_key_code; /* recognize raw key codes */
- char_u *result; /* buffer for resulting string */
-
- do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
- do_special = (vim_strchr(p_cpo, CPO_SPECI) == NULL) || special;
- do_key_code = (vim_strchr(p_cpo, CPO_KEYCODE) == NULL);
-
- /*
- * Allocate space for the translation. Worst case a single character is
- * replaced by 6 bytes (shifted special key), plus a NUL at the end.
- */
- result = xmalloc(STRLEN(from) * 6 + 1);
-
- src = from;
-
- /*
- * Check for #n at start only: function key n
- */
- if (from_part && src[0] == '#' && VIM_ISDIGIT(src[1])) { /* function key */
- result[dlen++] = K_SPECIAL;
- result[dlen++] = 'k';
- if (src[1] == '0')
- result[dlen++] = ';'; /* #0 is F10 is "k;" */
- else
- result[dlen++] = src[1]; /* #3 is F3 is "k3" */
- src += 2;
- }
-
- /*
- * Copy each byte from *from to result[dlen]
- */
- while (*src != NUL) {
- /*
- * If 'cpoptions' does not contain '<', check for special key codes,
- * like "<C-S-LeftMouse>"
- */
- if (do_special && (do_lt || STRNCMP(src, "<lt>", 4) != 0)) {
- /*
- * Replace <SID> by K_SNR <script-nr> _.
- * (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14)
- */
- if (STRNICMP(src, "<SID>", 5) == 0) {
- if (current_SID <= 0)
- EMSG(_(e_usingsid));
- else {
- src += 5;
- result[dlen++] = K_SPECIAL;
- result[dlen++] = (int)KS_EXTRA;
- result[dlen++] = (int)KE_SNR;
- sprintf((char *)result + dlen, "%" PRId64, (int64_t)current_SID);
- dlen += STRLEN(result + dlen);
- result[dlen++] = '_';
- continue;
- }
- }
-
- slen = trans_special(&src, result + dlen, TRUE);
- if (slen) {
- dlen += slen;
- continue;
- }
- }
-
- /*
- * If 'cpoptions' does not contain 'k', see if it's an actual key-code.
- * Note that this is also checked after replacing the <> form.
- * Single character codes are NOT replaced (e.g. ^H or DEL), because
- * it could be a character in the file.
- */
- if (do_key_code) {
- i = find_term_bykeys(src);
- if (i >= 0) {
- result[dlen++] = K_SPECIAL;
- result[dlen++] = termcodes[i].name[0];
- result[dlen++] = termcodes[i].name[1];
- src += termcodes[i].len;
- /* If terminal code matched, continue after it. */
- continue;
- }
- }
-
- if (do_special) {
- char_u *p, *s, len;
-
- /*
- * Replace <Leader> by the value of "mapleader".
- * Replace <LocalLeader> by the value of "maplocalleader".
- * If "mapleader" or "maplocalleader" isn't set use a backslash.
- */
- if (STRNICMP(src, "<Leader>", 8) == 0) {
- len = 8;
- p = get_var_value((char_u *)"g:mapleader");
- } else if (STRNICMP(src, "<LocalLeader>", 13) == 0) {
- len = 13;
- p = get_var_value((char_u *)"g:maplocalleader");
- } else {
- len = 0;
- p = NULL;
- }
- if (len != 0) {
- /* Allow up to 8 * 6 characters for "mapleader". */
- if (p == NULL || *p == NUL || STRLEN(p) > 8 * 6)
- s = (char_u *)"\\";
- else
- s = p;
- while (*s != NUL)
- result[dlen++] = *s++;
- src += len;
- continue;
- }
- }
-
- /*
- * Remove CTRL-V and ignore the next character.
- * For "from" side the CTRL-V at the end is included, for the "to"
- * part it is removed.
- * If 'cpoptions' does not contain 'B', also accept a backslash.
- */
- key = *src;
- if (key == Ctrl_V || (do_backslash && key == '\\')) {
- ++src; /* skip CTRL-V or backslash */
- if (*src == NUL) {
- if (from_part)
- result[dlen++] = key;
- break;
- }
- }
-
- /* skip multibyte char correctly */
- for (i = (*mb_ptr2len)(src); i > 0; --i) {
- /*
- * If the character is K_SPECIAL, replace it with K_SPECIAL
- * KS_SPECIAL KE_FILLER.
- * If compiled with the GUI replace CSI with K_CSI.
- */
- if (*src == K_SPECIAL) {
- result[dlen++] = K_SPECIAL;
- result[dlen++] = KS_SPECIAL;
- result[dlen++] = KE_FILLER;
- } else
- result[dlen++] = *src;
- ++src;
- }
- }
- result[dlen] = NUL;
-
- *bufp = xrealloc(result, dlen + 1);
-
- return *bufp;
-}
-
-/*
- * Find a termcode with keys 'src' (must be NUL terminated).
- * Return the index in termcodes[], or -1 if not found.
- */
-ssize_t find_term_bykeys(char_u *src)
-{
- size_t slen = STRLEN(src);
-
- for (size_t i = 0; i < tc_len; ++i) {
- assert(termcodes[i].len >= 0);
- if (slen == (size_t)termcodes[i].len
- && STRNCMP(termcodes[i].code, src, slen) == 0) {
- assert(i <= SSIZE_MAX);
- return (ssize_t)i;
- }
- }
- return -1;
-}
-
-/*
- * Show all termcodes (for ":set termcap")
- * This code looks a lot like showoptions(), but is different.
- */
-void show_termcodes(void)
-{
-#define INC3 27 /* try to make three columns */
-#define INC2 40 /* try to make two columns */
-#define GAP 2 /* spaces between columns */
-
- if (tc_len == 0) /* no terminal codes (must be GUI) */
- return;
-
- size_t *items = xmalloc(sizeof(size_t) * tc_len);
-
- /* Highlight title */
- MSG_PUTS_TITLE(_("\n--- Terminal keys ---"));
-
- /*
- * do the loop two times:
- * 1. display the short items (non-strings and short strings)
- * 2. display the medium items (medium length strings)
- * 3. display the long items (remaining strings)
- */
- for (int run = 1; run <= 3 && !got_int; ++run) {
- /*
- * collect the items in items[]
- */
- size_t item_count = 0;
- for (size_t i = 0; i < tc_len; i++) {
- int len = show_one_termcode(termcodes[i].name,
- termcodes[i].code, FALSE);
- if (len <= INC3 - GAP ? run == 1
- : len <= INC2 - GAP ? run == 2
- : run == 3)
- items[item_count++] = i;
- }
-
- /*
- * display the items
- */
- size_t rows, cols;
- if (run <= 2) {
- cols = (size_t)(Columns + GAP) / (run == 1 ? INC3 : INC2);
- if (cols == 0)
- cols = 1;
- rows = (item_count + cols - 1) / cols;
- } else /* run == 3 */
- rows = item_count;
- for (size_t row = 0; row < rows && !got_int; ++row) {
- msg_putchar('\n'); /* go to next line */
- if (got_int) /* 'q' typed in more */
- break;
- size_t col = 0;
- for (size_t i = row; i < item_count; i += rows) {
- assert(col <= INT_MAX);
- msg_col = (int)col; /* make columns */
- show_one_termcode(termcodes[items[i]].name,
- termcodes[items[i]].code, TRUE);
- if (run == 2)
- col += INC2;
- else
- col += INC3;
- }
- out_flush();
- os_breakcheck();
- }
- }
- free(items);
-}
-
-/*
- * Show one termcode entry.
- * Output goes into IObuff[]
- */
-int show_one_termcode(char_u *name, char_u *code, int printit)
-{
- char_u *p;
- int len;
-
- if (name[0] > '~') {
- IObuff[0] = ' ';
- IObuff[1] = ' ';
- IObuff[2] = ' ';
- IObuff[3] = ' ';
- } else {
- IObuff[0] = 't';
- IObuff[1] = '_';
- IObuff[2] = name[0];
- IObuff[3] = name[1];
- }
- IObuff[4] = ' ';
-
- p = get_special_key_name(TERMCAP2KEY(name[0], name[1]), 0);
- if (p[1] != 't')
- STRCPY(IObuff + 5, p);
- else
- IObuff[5] = NUL;
- len = (int)STRLEN(IObuff);
- do
- IObuff[len++] = ' ';
- while (len < 17);
- IObuff[len] = NUL;
- if (code == NULL)
- len += 4;
- else
- len += vim_strsize(code);
-
- if (printit) {
- msg_puts(IObuff);
- if (code == NULL)
- msg_puts((char_u *)"NULL");
- else
- msg_outtrans(code);
- }
- return len;
-}
-
-/*
- * Translate an internal mapping/abbreviation representation into the
- * corresponding external one recognized by :map/:abbrev commands;
- * respects the current B/k/< settings of 'cpoption'.
- *
- * This function is called when expanding mappings/abbreviations on the
- * command-line, and for building the "Ambiguous mapping..." error message.
- *
- * It uses a growarray to build the translation string since the
- * latter can be wider than the original description. The caller has to
- * free the string afterwards.
- *
- * Returns NULL when there is a problem.
- */
-char_u *
-translate_mapping (
- char_u *str,
- int expmap /* TRUE when expanding mappings on command-line */
-)
-{
- garray_T ga;
- ga_init(&ga, 1, 40);
-
- int cpo_bslash = (vim_strchr(p_cpo, CPO_BSLASH) != NULL);
- int cpo_special = (vim_strchr(p_cpo, CPO_SPECI) != NULL);
- int cpo_keycode = (vim_strchr(p_cpo, CPO_KEYCODE) == NULL);
-
- for (; *str; ++str) {
- int c = *str;
- if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) {
- int modifiers = 0;
- if (str[1] == KS_MODIFIER) {
- str++;
- modifiers = *++str;
- c = *++str;
- }
- if (cpo_special && cpo_keycode && c == K_SPECIAL && !modifiers) {
- /* try to find special key in termcodes */
- size_t i;
- for (i = 0; i < tc_len; ++i)
- if (termcodes[i].name[0] == str[1]
- && termcodes[i].name[1] == str[2])
- break;
- if (i < tc_len) {
- ga_concat(&ga, termcodes[i].code);
- str += 2;
- continue; /* for (str) */
- }
- }
- if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) {
- if (expmap && cpo_special) {
- ga_clear(&ga);
- return NULL;
- }
- c = TO_SPECIAL(str[1], str[2]);
- if (c == K_ZERO) /* display <Nul> as ^@ */
- c = NUL;
- str += 2;
- }
- if (IS_SPECIAL(c) || modifiers) { /* special key */
- if (expmap && cpo_special) {
- ga_clear(&ga);
- return NULL;
- }
- ga_concat(&ga, get_special_key_name(c, modifiers));
- continue; /* for (str) */
- }
- }
- if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V
- || (c == '<' && !cpo_special) || (c == '\\' && !cpo_bslash))
- ga_append(&ga, cpo_bslash ? Ctrl_V : '\\');
- if (c)
- ga_append(&ga, (char)c);
- }
- ga_append(&ga, NUL);
- return (char_u *)(ga.ga_data);
-}
-