[BACK]Return to term.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / vim

File: [local] / src / usr.bin / vim / Attic / term.c (download)

Revision 1.1.1.1 (vendor branch), Sat Sep 7 21:40:24 1996 UTC (27 years, 9 months ago) by downsj
Branch: VIM
CVS Tags: VIM42
Changes since 1.1: +0 -0 lines

Initial import of vim 4.2.

This is meant to replace nvi in the tree.  Vim, in general, works better,
provides more features, and does not suffer from the license problems
being imposed upon nvi.

On the other hand, vim lacks a non-visual ex mode, in addition to open mode.

This includes the GUI (X11) code, but doesn't try to compile it.


/*	$OpenBSD: term.c,v 1.1.1.1 1996/09/07 21:40:24 downsj Exp $	*/
/* vi:set ts=4 sw=4:
 *
 * 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.
 */
/*
 *
 * term.c: functions for controlling the terminal
 *
 * primitive termcap support for Amiga, MSDOS, and 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 "vim.h"

#include "globals.h"
#include "option.h"
#include "proto.h"

#ifdef HAVE_TGETENT
# ifdef HAVE_TERMCAP_H
#  include <termcap.h>
# endif

/*
 * A few linux systems define outfuntype in termcap.h to be used as the third
 * argument for tputs().
 */
# ifdef VMS
#  define TPUTSFUNCAST
# else
#  ifdef HAVE_OUTFUNTYPE
#   define TPUTSFUNCAST (outfuntype)
#  else
#   define TPUTSFUNCAST (int (*)())
#  endif
# endif
#endif

#undef tgetstr

/*
 * Here are the builtin termcap entries.  They are not stored as complete
 * Tcarr structures, 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_amiga", "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 (< 0x100), or a K_xxx code.
 */
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

static struct builtin_term *find_builtin_term __ARGS((char_u *name));
static void parse_builtin_tcap __ARGS((char_u *s));
static void gather_termleader __ARGS((void));
static int get_bytes_from_buf __ARGS((char_u *, char_u *, int));
static int is_builtin_term __ARGS((char_u *));

#ifdef HAVE_TGETENT
static char_u *tgetent_error __ARGS((char_u *, char_u *));

/*
 * Here is our own prototype for tgetstr(), any prototypes from the include
 * files have been disabled by the define at the start of this file.
 */
char			*tgetstr __PARMS((char *, char **));

/*
 * Don't declare these variables if termcap.h contains them.
 * Autoconf checks if these variables should be declared extern (not all
 * systems have them).
 * Some versions define ospeed to be speed_t, but that is incompatible with
 * BSD, where ospeed is short and speed_t is long.
 */
# ifndef HAVE_OSPEED
#  ifdef OSPEED_EXTERN
extern short ospeed;
#   else
short ospeed;
#   endif
# endif
# ifndef HAVE_UP_BC_PC
#  ifdef UP_BC_PC_EXTERN
extern char *UP, *BC, PC;
#  else
char *UP, *BC, PC;
#  endif
# endif

# define TGETSTR(s, p)	(char_u *)tgetstr((s), (char **)(p))
# define TGETENT(b, t)	tgetent((char *)(b), (char *)(t))

#endif /* HAVE_TGETENT */

struct builtin_term builtin_termcaps[] =
{

#if defined(USE_GUI)
/*
 * Motif/Athena pseudo term-cap.
 */
	{KS_NAME,		"gui"},
	{KS_CE,			"\033|$"},
	{KS_AL,			"\033|i"},
# ifdef TERMINFO
	{KS_CAL,		"\033|%p1%dI"},
# else
	{KS_CAL,		"\033|%dI"},
# endif
	{KS_DL,			"\033|d"},
# ifdef TERMINFO
	{KS_CDL,		"\033|%p1%dD"},
	{KS_CS,			"\033|%p1%d;%p2%dR"},
# else
	{KS_CDL,		"\033|%dD"},
	{KS_CS,			"\033|%d;%dR"},
# endif
	{KS_CL,			"\033|C"},
	{KS_ME,			"\033|63H"},	/* 63 = HL_ALL,			H = off */
	{KS_MR,			"\033|1h"},		/* 1  = HL_INVERSE,		h = on */
	{KS_MD,			"\033|2h"},		/* 2  = HL_BOLD,		h = on */
	{KS_SE,			"\033|16H"},	/* 16 = HL_STANDOUT,	H = off */
	{KS_SO,			"\033|16h"},	/* 16 = HL_STANDOUT,	h = on */
	{KS_UE,			"\033|8H"},		/* 8  = HL_UNDERLINE,	H = off */
	{KS_US,			"\033|8h"},		/* 8  = HL_UNDERLINE,	h = on */
	{KS_CZR,		"\033|4H"},		/* 4  = HL_ITAL,		H = off */
	{KS_CZH,		"\033|4h"},		/* 4  = HL_ITAL,		h = on */
	{KS_VB,			"\033|f"},
# ifdef TERMINFO
	{KS_CM,			"\033|%p1%d;%p2%dM"},
# else
	{KS_CM,			"\033|%d;%dM"},
# endif
		/* there are no key sequences here, the GUI sequences are recognized
		 * in check_termcodes() */
#endif

#ifndef NO_BUILTIN_TCAPS

# if defined(AMIGA) || defined(ALL_BUILTIN_TCAPS)
/*
 * Amiga console window, default for Amiga
 */
	{KS_NAME,		"amiga"},
	{KS_CE,			"\033[K"},
	{KS_CD,			"\033[J"},
	{KS_AL,			"\033[L"},
#  ifdef TERMINFO
	{KS_CAL,		"\033[%p1%dL"},
#  else
	{KS_CAL,		"\033[%dL"},
#  endif
	{KS_DL,			"\033[M"},
#  ifdef TERMINFO
	{KS_CDL,		"\033[%p1%dM"},
#  else
	{KS_CDL,		"\033[%dM"},
#  endif
	{KS_CL,			"\014"},
	{KS_VI,			"\033[0 p"},
	{KS_VE,			"\033[1 p"},
	{KS_ME,			"\033[0m"},
	{KS_MR,			"\033[7m"},
	{KS_MD,			"\033[1m"},
	{KS_SE,			"\033[0m"},
	{KS_SO,			"\033[33m"},
	{KS_US,			"\033[4m"},
	{KS_UE,			"\033[0m"},
	{KS_CZH,		"\033[3m"},
	{KS_CZR,		"\033[0m"},
	{KS_MS,			"\001"},
#  ifdef TERMINFO
	{KS_CM,			"\033[%i%p1%d;%p2%dH"},
#  else
	{KS_CM,			"\033[%i%d;%dH"},
#  endif
#  ifdef TERMINFO
	{KS_CRI,		"\033[%p1%dC"},
#  else
	{KS_CRI,		"\033[%dC"},
#  endif
	{K_UP,			"\233A"},
	{K_DOWN,		"\233B"},
	{K_LEFT,		"\233D"},
	{K_RIGHT,		"\233C"},
	{K_S_UP,		"\233T"},
	{K_S_DOWN,		"\233S"},
	{K_S_LEFT,		"\233 A"},
	{K_S_RIGHT,		"\233 @"},
	{K_S_TAB,		"\233Z"},
	{K_F1,			"\233\060~"},/* some compilers don't understand "\2330" */
	{K_F2,			"\233\061~"},
	{K_F3,			"\233\062~"},
	{K_F4,			"\233\063~"},
	{K_F5,			"\233\064~"},
	{K_F6,			"\233\065~"},
	{K_F7,			"\233\066~"},
	{K_F8,			"\233\067~"},
	{K_F9,			"\233\070~"},
	{K_F10,			"\233\071~"},
	{K_S_F1,		"\233\061\060~"},
	{K_S_F2,		"\233\061\061~"},
	{K_S_F3,		"\233\061\062~"},
	{K_S_F4,		"\233\061\063~"},
	{K_S_F5,		"\233\061\064~"},
	{K_S_F6,		"\233\061\065~"},
	{K_S_F7,		"\233\061\066~"},
	{K_S_F8,		"\233\061\067~"},
	{K_S_F9,		"\233\061\070~"},
	{K_S_F10,		"\233\061\071~"},
	{K_HELP,		"\233?~"},
	{K_INS,			"\233\064\060~"},	/* 101 key keyboard */
	{K_PAGEUP,		"\233\064\061~"},	/* 101 key keyboard */
	{K_PAGEDOWN,	"\233\064\062~"},	/* 101 key keyboard */
	{K_HOME,		"\233\064\064~"},	/* 101 key keyboard */
	{K_END,			"\233\064\065~"},	/* 101 key keyboard */

	{BT_EXTRA_KEYS,	""},
	{TERMCAP2KEY('#', '2'),	"\233\065\064~"},	/* shifted home key */
	{TERMCAP2KEY('#', '3'),	"\233\065\060~"},	/* shifted insert key */
	{TERMCAP2KEY('*', '7'),	"\233\065\065~"},	/* shifted end key */
# endif

# if defined(UNIX) || defined(ALL_BUILTIN_TCAPS) || defined(SOME_BUILTIN_TCAPS) || defined(__EMX__)
/*
 * standard ANSI terminal, default for unix
 */
	{KS_NAME,		"ansi"},
	{KS_CE,			"\033[K"},
	{KS_AL,			"\033[L"},
#  ifdef TERMINFO
	{KS_CAL,		"\033[%p1%dL"},
#  else
	{KS_CAL,		"\033[%dL"},
#  endif
	{KS_DL,			"\033[M"},
#  ifdef TERMINFO
	{KS_CDL,		"\033[%p1%dM"},
#  else
	{KS_CDL,		"\033[%dM"},
#  endif
	{KS_CL,			"\033[H\033[2J"},
	{KS_ME,			"\033[0m"},
	{KS_MR,			"\033[7m"},
	{KS_MS,			"\001"},
#  ifdef TERMINFO
	{KS_CM,			"\033[%i%p1%d;%p2%dH"},
#  else
	{KS_CM,			"\033[%i%d;%dH"},
#  endif
#  ifdef TERMINFO
	{KS_CRI,		"\033[%p1%dC"},
#  else
	{KS_CRI,		"\033[%dC"},
#  endif
# endif

# if defined(MSDOS) || defined(ALL_BUILTIN_TCAPS) || defined(__EMX__)
/*
 * These codes are valid when nansi.sys or equivalent has been installed.
 * Function keys on a PC are preceded with a NUL. These are converted into
 * K_NUL '\316' in mch_inchar(), because we cannot handle NULs in key codes.
 * CTRL-arrow is used instead of SHIFT-arrow.
 */
#ifdef __EMX__
	{KS_NAME,		"os2ansi"},
#else
	{KS_NAME,		"pcansi"},
	{KS_DL,			"\033[M"},
	{KS_AL,			"\033[L"},
#endif
	{KS_CE,			"\033[K"},
	{KS_CL,			"\033[2J"},
	{KS_ME,			"\033[0m"},
	{KS_MR,			"\033[7m"},
	{KS_MS,			"\001"},
#  ifdef TERMINFO
	{KS_CM,			"\033[%i%p1%d;%p2%dH"},
#  else
	{KS_CM,			"\033[%i%d;%dH"},
#  endif
#  ifdef TERMINFO
	{KS_CRI,		"\033[%p1%dC"},
#  else
	{KS_CRI,		"\033[%dC"},
#  endif
	{K_UP,			"\316H"},
	{K_DOWN,		"\316P"},
	{K_LEFT,		"\316K"},
	{K_RIGHT,		"\316M"},
	{K_S_LEFT,		"\316s"},
	{K_S_RIGHT,		"\316t"},
	{K_F1,			"\316;"},
	{K_F2,			"\316<"},
	{K_F3,			"\316="},
	{K_F4,			"\316>"},
	{K_F5,			"\316?"},
	{K_F6,			"\316@"},
	{K_F7,			"\316A"},
	{K_F8,			"\316B"},
	{K_F9,			"\316C"},
	{K_F10,			"\316D"},
	{K_F11,			"\316\205"},	/* guessed */
	{K_F12,			"\316\206"},	/* guessed */
	{K_S_F1,		"\316T"},
	{K_S_F2,		"\316U"},
	{K_S_F3,		"\316V"},
	{K_S_F4,		"\316W"},
	{K_S_F5,		"\316X"},
	{K_S_F6,		"\316Y"},
	{K_S_F7,		"\316Z"},
	{K_S_F8,		"\316["},
	{K_S_F9,		"\316\\"},
	{K_S_F10,		"\316]"},
	{K_S_F11,		"\316\207"},	/* guessed */
	{K_S_F12,		"\316\210"},	/* guessed */
	{K_INS,			"\316R"},
	{K_DEL,			"\316S"},
	{K_HOME,		"\316G"},
	{K_END,			"\316O"},
	{K_PAGEDOWN,	"\316Q"},
	{K_PAGEUP,		"\316I"},
# endif

# if defined(MSDOS)
/*
 * These codes are valid for the pc video.	The entries that start with ESC |
 * are translated into conio calls in msdos.c. Default for MSDOS.
 */
	{KS_NAME,		"pcterm"},
	{KS_CE,			"\033|K"},
	{KS_AL,			"\033|L"},
	{KS_DL,			"\033|M"},
#  ifdef TERMINFO
	{KS_CS,			"\033|%i%p1%d;%p2%dr"},
#  else
	{KS_CS,			"\033|%i%d;%dr"},
#  endif
	{KS_CL,			"\033|J"},
	{KS_ME,			"\033|0m"},
	{KS_MR,			"\033|112m"},
	{KS_MD,			"\033|63m"},
	{KS_SE,			"\033|0m"},
	{KS_SO,			"\033|31m"},
	{KS_MS,			"\001"},
#  ifdef TERMINFO
	{KS_CM,			"\033|%i%p1%d;%p2%dH"},
#  else
	{KS_CM,			"\033|%i%d;%dH"},
#  endif
	{K_UP,			"\316H"},
	{K_DOWN,		"\316P"},
	{K_LEFT,		"\316K"},
	{K_RIGHT,		"\316M"},
	{K_S_LEFT,		"\316s"},
	{K_S_RIGHT,		"\316t"},
	{K_S_TAB,		"\316\017"},
	{K_F1,			"\316;"},
	{K_F2,			"\316<"},
	{K_F3,			"\316="},
	{K_F4,			"\316>"},
	{K_F5,			"\316?"},
	{K_F6,			"\316@"},
	{K_F7,			"\316A"},
	{K_F8,			"\316B"},
	{K_F9,			"\316C"},
	{K_F10,			"\316D"},
	{K_F11,			"\316\205"},	/* only when nobioskey */
	{K_F12,			"\316\206"},	/* only when nobioskey */
	{K_S_F1,		"\316T"},
	{K_S_F2,		"\316U"},
	{K_S_F3,		"\316V"},
	{K_S_F4,		"\316W"},
	{K_S_F5,		"\316X"},
	{K_S_F6,		"\316Y"},
	{K_S_F7,		"\316Z"},
	{K_S_F8,		"\316["},
	{K_S_F9,		"\316\\"},
	{K_S_F10,		"\316]"},
	{K_S_F11,		"\316\207"},	/* only when nobioskey */
	{K_S_F12,		"\316\210"},	/* only when nobioskey */
	{K_INS,			"\316R"},
	{K_DEL,			"\316S"},
	{K_HOME,		"\316G"},
	{K_END,			"\316O"},
	{K_PAGEDOWN,	"\316Q"},
	{K_PAGEUP,		"\316I"},
# endif

# if defined(WIN32) || defined(ALL_BUILTIN_TCAPS) || defined(__EMX__)
/*
 * These codes are valid for the Win32 Console .  The entries that start with
 * ESC | are translated into console calls in win32.c.  The function keys
 * are also translated in win32.c.
 */
	{KS_NAME,		"win32"},
	{KS_CE,			"\033|K"},		/* clear to end of line */
	{KS_AL,			"\033|L"},		/* add new blank line */
#  ifdef TERMINFO
	{KS_CAL,		"\033|%p1%dL"},	/* add number of new blank lines */
#  else
	{KS_CAL,		"\033|%dL"},	/* add number of new blank lines */
#  endif
	{KS_DL,			"\033|M"},		/* delete line */
#  ifdef TERMINFO
	{KS_CDL,		"\033|%p1%dM"},	/* delete number of lines */
#  else
	{KS_CDL,		"\033|%dM"},	/* delete number of lines */
#  endif
	{KS_CL,			"\033|J"},		/* clear screen */
	{KS_CD,			"\033|j"},		/* clear to end of display */
	{KS_VI,			"\033|v"},		/* cursor invisible */
	{KS_VE,			"\033|V"},		/* cursor visible */

	{KS_ME,			"\033|0m"},		/* normal mode */
	{KS_MR,			"\033|112m"},	/* reverse mode: black text on lightgray */
	{KS_MD,			"\033|63m"},	/* bold mode: white text on cyan */
#if 1
	{KS_SO,			"\033|31m"},	/* standout mode: white text on blue */
	{KS_SE,			"\033|0m"},		/* standout mode end */
#else
	{KS_SO,			"\033|F"},		/* standout mode: high intensity text */
	{KS_SE,			"\033|f"},		/* standout mode end */
#endif
	{KS_CZH,		"\033|225m"},	/* italic mode: blue text on yellow */
	{KS_CZR,		"\033|0m"},		/* italic mode end */
	{KS_US,			"\033|67m"},	/* underscore mode: cyan text on red */
	{KS_UE,			"\033|0m"},		/* underscore mode end */

	{KS_MS,			"\001"},		/* save to move cur in reverse mode */
#  ifdef TERMINFO
	{KS_CM,			"\033|%i%p1%d;%p2%dH"},	/* cursor motion */
#  else
	{KS_CM,			"\033|%i%d;%dH"},		/* cursor motion */
#  endif
	{KS_VB,			"\033|B"},		/* visual bell */
	{KS_TI,			"\033|S"},		/* put terminal in termcap mode */
	{KS_TE,			"\033|E"},		/* out of termcap mode */
	{KS_CS,         "\033|%i%d;%dr"},	/* scroll region */

	{K_UP,			"\316H"},
	{K_DOWN,		"\316P"},
	{K_LEFT,		"\316K"},
	{K_RIGHT,		"\316M"},
	{K_S_UP,		"\316\304"},
	{K_S_DOWN,		"\316\317"},
	{K_S_LEFT,		"\316\311"},
	{K_S_RIGHT,		"\316\313"},
	{K_S_TAB,		"\316\017"},
	{K_F1,			"\316;"},
	{K_F2,			"\316<"},
	{K_F3,			"\316="},
	{K_F4,			"\316>"},
	{K_F5,			"\316?"},
	{K_F6,			"\316@"},
	{K_F7,			"\316A"},
	{K_F8,			"\316B"},
	{K_F9,			"\316C"},
	{K_F10,			"\316D"},
	{K_F11,			"\316\205"},
	{K_F12,			"\316\206"},
	{K_S_F1,		"\316T"},
	{K_S_F2,		"\316U"},
	{K_S_F3,		"\316V"},
	{K_S_F4,		"\316W"},
	{K_S_F5,		"\316X"},
	{K_S_F6,		"\316Y"},
	{K_S_F7,		"\316Z"},
	{K_S_F8,		"\316["},
	{K_S_F9,		"\316\\"},
	{K_S_F10,		"\316]"},
	{K_S_F11,		"\316\207"},
	{K_S_F12,		"\316\210"},
	{K_INS,			"\316R"},
	{K_DEL,			"\316S"},
	{K_HOME,		"\316G"},
	{K_END,			"\316O"},
	{K_PAGEDOWN,	"\316Q"},
	{K_PAGEUP,		"\316I"},
# endif

# if defined(ALL_BUILTIN_TCAPS) || defined(MINT)
/*
 * Ordinary vt52
 */
	{KS_NAME,	 	"vt52"},
	{KS_CE,			"\033K"},
	{KS_CD,			"\033J"},
	{KS_CM,			"\033Y%+ %+ "},
#  ifdef MINT
	{KS_AL,			"\033L"},
	{KS_DL,			"\033M"},
	{KS_CL,			"\033E"},
	{KS_SR,			"\033I"},
	{KS_VE,			"\033e"},
	{KS_VI,			"\033f"},
	{KS_SO,			"\033p"},
	{KS_SE,			"\033q"},
	{K_UP,			"\033A"},
	{K_DOWN,		"\033B"},
	{K_LEFT,		"\033D"},
	{K_RIGHT,		"\033C"},
	{K_S_UP,		"\033a"},
	{K_S_DOWN,		"\033b"},
	{K_S_LEFT,		"\033d"},
	{K_S_RIGHT,		"\033c"},
	{K_F1,			"\033P"},
	{K_F2,			"\033Q"},
	{K_F3,			"\033R"},
	{K_F4,			"\033S"},
	{K_F5,			"\033T"},
	{K_F6,			"\033U"},
	{K_F7,			"\033V"},
	{K_F8,			"\033W"},
	{K_F9,			"\033X"},
	{K_F10,			"\033Y"},
	{K_S_F1,		"\033p"},
	{K_S_F2,		"\033q"},
	{K_S_F3,		"\033r"},
	{K_S_F4,		"\033s"},
	{K_S_F5,		"\033t"},
	{K_S_F6,		"\033u"},
	{K_S_F7,		"\033v"},
	{K_S_F8,		"\033w"},
	{K_S_F9,		"\033x"},
	{K_S_F10,		"\033y"},
	{K_INS,			"\033I"},
	{K_HOME,		"\033E"},
	{K_PAGEDOWN,	"\033b"},
	{K_PAGEUP,		"\033a"},
#  else
	{KS_AL,			"\033T"},
	{KS_DL,			"\033U"},
	{KS_CL,			"\033H\033J"},
	{KS_ME,			"\033SO"},
	{KS_MR,			"\033S2"},
	{KS_MS,			"\001"},
#  endif
# endif

# if defined(UNIX) || defined(ALL_BUILTIN_TCAPS) || defined(SOME_BUILTIN_TCAPS) || defined(__EMX__)
/*
 * The xterm termcap is missing F14 and F15, because they send the same
 * codes as the undo and help key, although they don't work on all keyboards.
 */
	{KS_NAME,		"xterm"},
	{KS_CE,			"\033[K"},
	{KS_AL,			"\033[L"},
#  ifdef TERMINFO
	{KS_CAL,		"\033[%p1%dL"},
#  else
	{KS_CAL,		"\033[%dL"},
#  endif
	{KS_DL,			"\033[M"},
#  ifdef TERMINFO
	{KS_CDL,		"\033[%p1%dM"},
#  else
	{KS_CDL,		"\033[%dM"},
#  endif
#  ifdef TERMINFO
	{KS_CS,			"\033[%i%p1%d;%p2%dr"},
#  else
	{KS_CS,			"\033[%i%d;%dr"},
#  endif
	{KS_CL,			"\033[H\033[2J"},
	{KS_CD,			"\033[J"},
	{KS_ME,			"\033[m"},
	{KS_MR,			"\033[7m"},
	{KS_MD,			"\033[1m"},
	{KS_UE,			"\033[m"},
	{KS_US,			"\033[4m"},
	{KS_MS,			"\001"},
#  ifdef TERMINFO
	{KS_CM,			"\033[%i%p1%d;%p2%dH"},
#  else
	{KS_CM,			"\033[%i%d;%dH"},
#  endif
	{KS_SR,			"\033M"},
#  ifdef TERMINFO
	{KS_CRI,		"\033[%p1%dC"},
#  else
	{KS_CRI,		"\033[%dC"},
#  endif
	{KS_KS,			"\033[?1h\033="},
	{KS_KE,			"\033[?1l\033>"},
#  ifdef SAVE_XTERM_SCREEN
	{KS_TI,			"\0337\033[?47h"},
	{KS_TE,			"\033[2J\033[?47l\0338"},
#  endif
	{K_UP,			"\033OA"},
	{K_DOWN,		"\033OB"},
	{K_LEFT,		"\033OD"},
	{K_RIGHT,		"\033OC"},
	{K_S_UP,		"\033Ox"},
	{K_S_DOWN,		"\033Or"},
	{K_S_LEFT,		"\033Ot"},
	{K_S_RIGHT,		"\033Ov"},
	{K_F1,			"\033[11~"},
	{K_F2,			"\033[12~"},
	{K_F3,			"\033[13~"},
	{K_F4,			"\033[14~"},
	{K_F5,			"\033[15~"},
	{K_F6,			"\033[17~"},
	{K_F7,			"\033[18~"},
	{K_F8,			"\033[19~"},
	{K_F9,			"\033[20~"},
	{K_F10,			"\033[21~"},
	{K_F11,			"\033[23~"},
	{K_F12,			"\033[24~"},
	{K_HELP,		"\033[28~"},
	{K_UNDO,		"\033[26~"},
	{K_INS,			"\033[2~"},
	{K_HOME,		"\033[7~"},		/* also seen: "\033[1~" */
	{K_END,			"\033[8~"},		/* also seen: "\033[4~" */
	{K_PAGEUP,		"\033[5~"},
	{K_PAGEDOWN,	"\033[6~"},
	/* {K_DEL,			"\033[3~"}, not used */

	{BT_EXTRA_KEYS,	""},
	{TERMCAP2KEY('k', '0'),	"\033[10~"},	/* F0 */
	{TERMCAP2KEY('F', '3'),	"\033[25~"},	/* F13 */
	{TERMCAP2KEY('F', '6'),	"\033[29~"},	/* F16 */
	{TERMCAP2KEY('F', '7'),	"\033[31~"},	/* F17 */
	{TERMCAP2KEY('F', '8'),	"\033[32~"},	/* F18 */
	{TERMCAP2KEY('F', '9'),	"\033[33~"},	/* F19 */
	{TERMCAP2KEY('F', 'A'),	"\033[34~"},	/* F20 */
# endif

# if defined(UNIX) || defined(ALL_BUILTIN_TCAPS)
/*
 * iris-ansi for Silicon Graphics machines.
 */
	{KS_NAME,		"iris-ansi"},
	{KS_CE,			"\033[K"},
	{KS_CD,			"\033[J"},
	{KS_AL,			"\033[L"},
#  ifdef TERMINFO
	{KS_CAL,		"\033[%p1%dL"},
#  else
	{KS_CAL,		"\033[%dL"},
#  endif
	{KS_DL,			"\033[M"},
#  ifdef TERMINFO
	{KS_CDL,		"\033[%p1%dM"},
#  else
	{KS_CDL,		"\033[%dM"},
#  endif
/*
 * This "cs" is not working correctly. What is the right one?
 */
#if 0
#  ifdef TERMINFO
	{KS_CS,			"\033[%i%p1%d;%p2%dr"},
#  else
	 {KS_CS,		"\033[%i%d;%dr"},
#  endif
#endif
	{KS_CL,			"\033[H\033[2J"},
	{KS_VE,			"\033[9/y\033[12/y\033[=6l"},
	{KS_VS,			"\033[10/y\033[=1h\033[=2l\033[=6h"},
	{KS_SE,			"\033[m"},
	{KS_SO,			"\033[1;7m"},
	{KS_ME,			"\033[m"},
	{KS_MR,			"\033[7m"},
	{KS_MD,			"\033[1m"},
	{KS_UE,			"\033[m"},
	{KS_US,			"\033[4m"},
	{KS_MS,			"\001"}, /* does this really work? */
#  ifdef TERMINFO
	{KS_CM,			"\033[%i%p1%d;%p2%dH"},
#  else
	{KS_CM,			"\033[%i%d;%dH"},
#  endif
	{KS_SR,			"\033M"},
#  ifdef TERMINFO
	{KS_CRI,		"\033[%p1%dC"},
#  else
	{KS_CRI,		"\033[%dC"},
#  endif
	{K_UP,			"\033[A"},
	{K_DOWN,		"\033[B"},
	{K_LEFT,		"\033[D"},
	{K_RIGHT,		"\033[C"},
	{K_S_UP,		"\033[161q"},
	{K_S_DOWN,		"\033[164q"},
	{K_S_LEFT,		"\033[158q"},
	{K_S_RIGHT,		"\033[167q"},
	{K_F1,			"\033[001q"},
	{K_F2,			"\033[002q"},
	{K_F3,			"\033[003q"},
	{K_F4,			"\033[004q"},
	{K_F5,			"\033[005q"},
	{K_F6,			"\033[006q"},
	{K_F7,			"\033[007q"},
	{K_F8,			"\033[008q"},
	{K_F9,			"\033[009q"},
	{K_F10,			"\033[010q"},
	{K_F11,			"\033[011q"},
	{K_F12,			"\033[012q"},
	{K_S_F1,		"\033[013q"},
	{K_S_F2,		"\033[014q"},
	{K_S_F3,		"\033[015q"},
	{K_S_F4,		"\033[016q"},
	{K_S_F5,		"\033[017q"},
	{K_S_F6,		"\033[018q"},
	{K_S_F7,		"\033[019q"},
	{K_S_F8,		"\033[020q"},
	{K_S_F9,		"\033[021q"},
	{K_S_F10,		"\033[022q"},
	{K_S_F11,		"\033[023q"},
	{K_S_F12,		"\033[024q"},
	{K_INS,			"\033[139q"},
	{K_HOME,		"\033[H"},
	{K_END,			"\033[146q"},
	{K_PAGEUP,		"\033[150q"},
	{K_PAGEDOWN,	"\033[154q"},
# endif

# if defined(DEBUG) || defined(ALL_BUILTIN_TCAPS)
/*
 * for debugging
 */
	{KS_NAME,		"debug"},
	{KS_CE,			"[CE]"},
	{KS_CD,			"[CD]"},
	{KS_AL,			"[AL]"},
#  ifdef TERMINFO
	{KS_CAL,		"[CAL%p1%d]"},
#  else
	{KS_CAL,		"[CAL%d]"},
#  endif
	{KS_DL,			"[DL]"},
#  ifdef TERMINFO
	{KS_CDL,		"[CDL%p1%d]"},
#  else
	{KS_CDL,		"[CDL%d]"},
#  endif
#  ifdef TERMINFO
	{KS_CS,			"[%dCS%p1%d]"},
#  else
	{KS_CS,			"[%dCS%d]"},
#  endif
	{KS_CL,			"[CL]"},
	{KS_VI,			"[VI]"},
	{KS_VE,			"[VE]"},
	{KS_VS,			"[VS]"},
	{KS_ME,			"[ME]"},
	{KS_MR,			"[MR]"},
	{KS_MD,			"[MD]"},
	{KS_SE,			"[SE]"},
	{KS_SO,			"[SO]"},
	{KS_UE,			"[UE]"},
	{KS_US,			"[US]"},
	{KS_MS,			"[MS]"},
#  ifdef TERMINFO
	{KS_CM,			"[%p1%dCM%p2%d]"},
#  else
	{KS_CM,			"[%dCM%d]"},
#  endif
	{KS_SR,			"[SR]"},
#  ifdef TERMINFO
	{KS_CRI,		"[CRI%p1%d]"},
#  else
	{KS_CRI,		"[CRI%d]"},
#  endif
	{KS_VB,			"[VB]"},
	{KS_KS,			"[KS]"},
	{KS_KE,			"[KE]"},
	{KS_TI,			"[TI]"},
	{KS_TE,			"[TE]"},
	{K_UP,			"[KU]"},
	{K_DOWN,		"[KD]"},
	{K_LEFT,		"[KL]"},
	{K_RIGHT,		"[KR]"},
	{K_S_UP,		"[S-KU]"},
	{K_S_DOWN,		"[S-KD]"},
	{K_S_LEFT,		"[S-KL]"},
	{K_S_RIGHT,		"[S-KR]"},
	{K_F1,			"[F1]"},
	{K_F2,			"[F2]"},
	{K_F3,			"[F3]"},
	{K_F4,			"[F4]"},
	{K_F5,			"[F5]"},
	{K_F6,			"[F6]"},
	{K_F7,			"[F7]"},
	{K_F8,			"[F8]"},
	{K_F9,			"[F9]"},
	{K_F10,			"[F10]"},
	{K_F11,			"[F11]"},
	{K_F12,			"[F12]"},
	{K_S_F1,		"[S-F1]"},
	{K_S_F2,		"[S-F2]"},
	{K_S_F3,		"[S-F3]"},
	{K_S_F4,		"[S-F4]"},
	{K_S_F5,		"[S-F5]"},
	{K_S_F6,		"[S-F6]"},
	{K_S_F7,		"[S-F7]"},
	{K_S_F8,		"[S-F8]"},
	{K_S_F9,		"[S-F9]"},
	{K_S_F10,		"[S-F10]"},
	{K_S_F11,		"[S-F11]"},
	{K_S_F12,		"[S-F12]"},
	{K_HELP,		"[HELP]"},
	{K_UNDO,		"[UNDO]"},
	{K_BS,			"[BS]"},
	{K_INS,			"[INS]"},
	{K_DEL,			"[DEL]"},
	{K_HOME,		"[HOME]"},
	{K_END,			"[END]"},
	{K_PAGEUP,		"[PAGEUP]"},
	{K_PAGEDOWN,	"[PAGEDOWN]"},
	{K_MOUSE,		"[MOUSE]"},
# endif

#endif /* NO_BUILTIN_TCAPS */

/*
 * The most minimal terminal: only clear screen and cursor positioning
 * Always included.
 */
	{KS_NAME,		"dumb"},
	{KS_CL,			"\014"},
#ifdef TERMINFO
	{KS_CM,			"\033[%i%p1%d;%p2%dH"},
#else
	{KS_CM,			"\033[%i%d;%dH"},
#endif

/*
 * end marker
 */
	{KS_NAME,		NULL}

};		/* end of builtin_termcaps */

/*
 * DEFAULT_TERM is used, when no terminal is specified with -T option or $TERM.
 */
#ifdef AMIGA
# define DEFAULT_TERM	(char_u *)"amiga"
#endif /* AMIGA */

#ifdef WIN32
# define DEFAULT_TERM	(char_u *)"win32"
#endif /* WIN32 */
  
#ifdef MSDOS
# define DEFAULT_TERM	(char_u *)"pcterm"
#endif /* MSDOS */

#if defined(UNIX) && !defined(MINT)
# define DEFAULT_TERM	(char_u *)"ansi"
#endif /* UNIX */

#ifdef MINT
# define DEFAULT_TERM	(char_u *)"vt52"
#endif /* MINT */

#ifdef __EMX__
# define DEFAULT_TERM	(char_u *)"os2ansi"
#endif /* __EMX__ */

#ifdef VMS
# define DEFAULT_TERM	(char_u *)"ansi"
#endif /* VMS */

/*
 * 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[KS_LAST + 1]);

static int		need_gather = FALSE;			/* need to fill termleader[] */
static char_u	termleader[256 + 1];			/* for check_termcode() */

	static struct builtin_term *
find_builtin_term(term)
	char_u		*term;
{
	struct builtin_term *p;

	p = builtin_termcaps;
	while (p->bt_string != NULL)
	{
		if (p->bt_entry == KS_NAME)
		{
#ifdef UNIX
			if (STRCMP(p->bt_string, "iris-ansi") == 0 && is_iris_ansi(term))
				return p;
			else if (STRCMP(p->bt_string, "xterm") == 0 && 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(term)
	char_u	*term;
{
	struct builtin_term		*p;
	char_u					name[2];

	p = find_builtin_term(term);
	for (++p; p->bt_entry != KS_NAME && p->bt_entry != BT_EXTRA_KEYS; ++p)
	{
		if (p->bt_entry < 0x100)	/* KS_xx entry */
		{
			if (term_strings[p->bt_entry] == NULL ||
									term_strings[p->bt_entry] == empty_option)
				term_strings[p->bt_entry] = (char_u *)p->bt_string;
		}
		else
		{
			name[0] = KEY2TERMCAP0(p->bt_entry);
			name[1] = KEY2TERMCAP1(p->bt_entry);
			if (find_termcode(name) == NULL)
				add_termcode(name, (char_u *)p->bt_string);
		}
	}
}

/*
 * 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(term)
	char_u *term;
{
	struct builtin_term *termp;
#ifdef HAVE_TGETENT
	int			builtin_first = p_tbi;
	int			try;
	int			termcap_cleared = FALSE;
#endif
	int			width = 0, height = 0;
	char_u		*error_msg = NULL;
	char_u		*bs_p, *del_p;

	if (is_builtin_term(term))
	{
		term += 8;
#ifdef HAVE_TGETENT
		builtin_first = 1;
#endif
	}

/*
 * 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
 */
#ifdef HAVE_TGETENT
	for (try = builtin_first ? 0 : 1; try < 3; ++try)
	{
		/*
		 * Use external termcap
		 */
		if (try == 1)
		{
			char_u			*p;
			static char_u	tstrbuf[TBUFSZ];
			int				i;
			char_u			tbuf[TBUFSZ];
			char_u			*tp = tstrbuf;
			static char 	*(key_names[]) =
							{
							"ku", "kd", "kr",	/* "kl" is a special case */
# ifdef ARCHIE
							"su", "sd",			/* Termcap code made up! */
# endif
							"#4", "%i",
							"k1", "k2", "k3", "k4", "k5", "k6",
							"k7", "k8", "k9", "k;", "F1", "F2",
							"%1", "&8", "kb", "kI", "kD", "kh", 
							"@7", "kP", "kN",
							NULL
							};
			static struct {
							int dest;		/* index in term_strings[] */
							char *name;		/* termcap name for string */
						  } string_names[] =
							{	{KS_CE, "ce"}, {KS_AL, "al"}, {KS_CAL, "AL"},
								{KS_DL, "dl"}, {KS_CDL, "DL"}, {KS_CS, "cs"},
								{KS_CL, "cl"}, {KS_CD, "cd"},
								{KS_VI, "vi"}, {KS_VE, "ve"},
								{KS_VS, "vs"}, {KS_ME, "me"}, {KS_MR, "mr"},
								{KS_MD, "md"}, {KS_SE, "se"}, {KS_SO, "so"},
								{KS_CZH, "ZH"}, {KS_CZR, "ZR"}, {KS_UE, "ue"},
								{KS_US, "us"}, {KS_CM, "cm"}, {KS_SR, "sr"},
								{KS_CRI, "RI"}, {KS_VB, "vb"}, {KS_KS, "ks"},
								{KS_KE, "ke"}, {KS_TI, "ti"}, {KS_TE, "te"},
								{0, NULL}
							};

			/*
			 * If the external termcap does not have a matching entry, try the
			 * builtin ones.
			 */
			if ((error_msg = tgetent_error(tbuf, term)) == NULL)
			{
				if (!termcap_cleared)
				{
					clear_termoptions();		/* clear old options */
					termcap_cleared = TRUE;
				}

			/* get output strings */
				for (i = 0; string_names[i].name != NULL; ++i)
				{
					if (term_strings[string_names[i].dest] == NULL ||
							   term_strings[string_names[i].dest] == empty_option)
						term_strings[string_names[i].dest] =
											   TGETSTR(string_names[i].name, &tp);
				}

				if ((T_MS == NULL || T_MS == empty_option) && tgetflag("ms"))
					T_MS = (char_u *)"yes";
				if ((T_DB == NULL || T_DB == empty_option) && tgetflag("db"))
					T_DB = (char_u *)"yes";
				if ((T_DA == NULL || T_DA == empty_option) && tgetflag("da"))
					T_DA = (char_u *)"yes";


			/* get key codes */

				for (i = 0; key_names[i] != NULL; ++i)
				{
					if (find_termcode((char_u *)key_names[i]) == NULL)
						add_termcode((char_u *)key_names[i],
													  TGETSTR(key_names[i], &tp));
				}

					/* if cursor-left == backspace, ignore it (televideo 925) */
				if (find_termcode((char_u *)"kl") == NULL)
				{
					p = TGETSTR("kl", &tp);
					if (p != NULL && *p != Ctrl('H'))
						add_termcode((char_u *)"kl", p);
				}

				if (height == 0)
					height = tgetnum("li");
				if (width == 0)
					width = tgetnum("co");

# ifndef hpux
				BC = (char *)TGETSTR("bc", &tp);
				UP = (char *)TGETSTR("up", &tp);
				p = TGETSTR("pc", &tp);
				if (p)
					PC = *p;
# endif /* hpux */
			}
		}
		else		/* try == 0 || try == 2 */
#endif /* HAVE_TGETENT */
		/*
		 * Use builtin termcap
		 */
		{
#ifdef HAVE_TGETENT
			/*
			 * If builtin termcap was already used, there is no need to search
			 * for the builtin termcap again, quit now.
			 */
			if (try == 2 && builtin_first && termcap_cleared)
				break;
#endif
			/*
			 * search for 'term' in builtin_termcaps[]
			 */
			termp = find_builtin_term(term);
			if (termp->bt_string == NULL)		/* did not find it */
			{
#ifdef HAVE_TGETENT
				/*
				 * If try == 0, first try the external termcap. If that is not
				 * found we'll get back here with try == 2.
				 * If termcap_cleared is set we used the external termcap,
				 * don't complain about not finding the term in the builtin
				 * termcap.
				 */
				if (try == 0)					/* try external one */
					continue;
				if (termcap_cleared)			/* found in external termcap */
					break;
#endif

				fprintf(stderr, "\r\n");
				if (error_msg != NULL)
				{
					fprintf(stderr, (char *)error_msg);
					fprintf(stderr, "\r\n");
				}
				fprintf(stderr, "'%s' not known. Available builtin terminals are:\r\n", term);
				for (termp = &(builtin_termcaps[0]); termp->bt_string != NULL;
																	  ++termp)
				{
					if (termp->bt_entry == KS_NAME)
#ifdef HAVE_TGETENT
						fprintf(stderr, "    builtin_%s\r\n", termp->bt_string);
#else
						fprintf(stderr, "    %s\r\n", termp->bt_string);
#endif
				}
				if (!starting)	/* when user typed :set term=xxx, quit here */
				{
					screen_start();		/* don't know where cursor is now */
					wait_return(TRUE);
					return FAIL;
				}
				term = DEFAULT_TERM;
				fprintf(stderr, "defaulting to '%s'\r\n", term);
				screen_start();			/* don't know where cursor is now */
				mch_delay(2000L, TRUE);
				set_string_option((char_u *)"term", -1, term, TRUE);
			}
			flushbuf();
#ifdef HAVE_TGETENT
			if (!termcap_cleared)
			{
#endif
				clear_termoptions();		/* clear old options */
#ifdef HAVE_TGETENT
				termcap_cleared = TRUE;
			}
#endif
			parse_builtin_tcap(term);
#ifdef USE_GUI
			if (STRCMP(term, "gui") == 0)
			{
				flushbuf();
				settmode(0);
				gui_init();
				if (!gui.in_use)		/* failed to start GUI */
					settmode(1);
			}
#endif /* USE_GUI */
		}
#ifdef HAVE_TGETENT
	}
#endif

/*
 * 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_CSC = (char_u *)"yes";
	else
		T_CSC = empty_option;

#ifdef UNIX
/*
 * Any "stty" settings override the default for t_kb from the termcap.
 * This is in unix.c, because it depends a lot on the version of unix that is
 * being used.
 * Don't do this when the GUI is active, it uses "t_kb" and "t_kD" directly.
 */
#ifdef USE_GUI
	if (!gui.in_use)
#endif
		get_stty();
#endif

/*
 * 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 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.
 */
#ifdef USE_GUI
	if (!gui.in_use)
#endif
	{
		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 *)"\010"));
		if ((del_p == NULL || *del_p == NUL) &&
											(bs_p == NULL || *bs_p != '\177'))
			add_termcode((char_u *)"kD", (char_u *)"\177");
	}

#ifdef USE_MOUSE
	/*
	 * recognize mouse events in the input stream for xterm, msdos and win32
	 */
	{	
		char_u	name[2];

		name[0] = KS_MOUSE;
		name[1] = K_FILLER;
# ifdef UNIX
		if (is_xterm(term))
			add_termcode(name, (char_u *)"\033[M");
# else
		add_termcode(name, (char_u *)"\233M");
# endif
	}
#endif

#if defined(AMIGA) || defined(MSDOS) || defined(WIN32) || defined(OS2)
		/* DEFAULT_TERM indicates that it is the machine console. */
	if (STRCMP(term, DEFAULT_TERM))
		term_console = FALSE;
	else
	{
		term_console = TRUE;
# ifdef AMIGA
		win_resize_on();		/* enable window resizing reports */
# endif
	}
#endif

#ifdef UNIX
/*
 * 'ttyfast' is default on for xterm, iris-ansi and a few others.
 */
	if (is_fastterm(term))
		p_tf = TRUE;
#endif
#if defined(AMIGA) || defined(MSDOS) || defined(WIN32) || defined(OS2)
/*
 * 'ttyfast' is default on Amiga, MSDOS, Win32, and OS/2 consoles
 */
	if (term_console)
		p_tf = TRUE;
#endif

	ttest(TRUE);		/* make sure we have a valid set of terminal codes */
	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)
	{
		starttermcap();			/* may change terminal mode */
#ifdef USE_MOUSE
		setmouse();				/* may start using the mouse */
#endif
		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 mch_get_winsize also fails */
		width = 80;
#if defined MSDOS  ||  defined WIN32
		height = 25;		/* console is often 25 lines */
#else
		height = 24;		/* most terminals are 24 lines */
#endif
	}
	set_winsize(width, height, FALSE);	/* may change Rows */
	if (!starting)
	{
		if (scroll_region)
			scroll_region_reset();			/* In case Rows changed */
		check_map_keycodes();	/* check mappings for terminal codes used */
	}

	return OK;
}

#ifdef HAVE_TGETENT
/*
 * Call tgetent()
 * Return error message if it fails, NULL if it's OK.
 */
	static char_u *
tgetent_error(tbuf, term)
	char_u	*tbuf;
	char_u	*term;
{
	int		i;

	i = TGETENT(tbuf, term);
	if (i == -1)
		return (char_u *)"Cannot open termcap file";
	if (i == 0)
#ifdef TERMINFO
		return (char_u *)"Terminal entry not found in terminfo";
#else
		return (char_u *)"Terminal entry not found in termcap";
#endif
	return NULL;
}
#endif /* HAVE_TGETENT */

#if defined(HAVE_TGETENT) && (defined(UNIX) || defined(__EMX__))
/*
 * Get Columns and Rows from the termcap. Used after a window signal if the
 * ioctl() fails. It doesn't make sense to call tgetent each time if the "co"
 * and "li" entries never change. But on some systems this works.
 * Errors while getting the entries are ignored.
 */
	void
getlinecol()
{
	char_u			tbuf[TBUFSZ];

	if (term_strings[KS_NAME] != NULL && TGETENT(tbuf, term_strings[KS_NAME]) > 0)
	{
		if (Columns == 0)
			Columns = tgetnum("co");
		if (Rows == 0)
			Rows = tgetnum("li");
	}
}
#endif /* defined(HAVE_TGETENT) && defined(UNIX) */

/*
 * 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(name, force)
	char_u	*name;
	int		force;
{
	char_u	*term;
	int		key;
	struct builtin_term *termp;
#ifdef HAVE_TGETENT
	char_u	*string;
	int		i;
	int		builtin_first;
	char_u	tbuf[TBUFSZ];
	char_u	tstrbuf[TBUFSZ];
	char_u	*tp = tstrbuf;
	char_u	*error_msg = NULL;
#endif

/*
 * If the GUI is running or will start in a moment, we only support the keys
 * that the GUI can produce.
 */
#ifdef USE_GUI
	if (gui.in_use || gui.starting)
		return gui_mch_haskey(name);
#endif

	if (!force && find_termcode(name) != NULL)		/* it's already there */
		return OK;

	term = term_strings[KS_NAME];
	if (term == NULL)						/* just in case */
		return FAIL;

	if (is_builtin_term(term))				/* name starts with "builtin_" */
	{
		term += 8;
#ifdef HAVE_TGETENT
		builtin_first = TRUE;
#endif
	}
#ifdef HAVE_TGETENT
	else
		builtin_first = p_tbi;
#endif

#ifdef HAVE_TGETENT
/*
 * We can get the entry from the builtin termcap and from the external one.
 * If 'ttybuiltin' is on or the terminal name starts with "builtin_", try
 * builtin termcap first.
 * If 'ttybuiltin' is off, try external termcap first.
 */
	for (i = 0; i < 2; ++i)
	{
		if (!builtin_first == i)
#endif
		/*
		 * 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 != KS_NAME)
				{
					if (termp->bt_entry == key)
					{
						add_termcode(name, (char_u *)termp->bt_string);
						return OK;
					}
					++termp;
				}
			}
		}
#ifdef HAVE_TGETENT
		else
		/*
		 * Search in external termcap
		 */
		{
			error_msg = tgetent_error(tbuf, term);
			if (error_msg == NULL)
			{
				string = TGETSTR((char *)name, &tp);
				if (string != NULL && *string != NUL)
				{
					add_termcode(name, string);
					return OK;
				}
			}
		}
	}
#endif

	if (sourcing_name == NULL)
	{
#ifdef HAVE_TGETENT
		if (error_msg != NULL)
			EMSG(error_msg);
		else
#endif
			EMSG2("No \"%s\" entry in termcap", name);
	}
	return FAIL;
}

	static int
is_builtin_term(name)
	char_u	*name;
{
	return (STRNCMP(name, "builtin_", (size_t)8) == 0);
}

static char_u *tltoa __PARMS((unsigned long));

	static char_u *
tltoa(i)
	unsigned long i;
{
	static char_u buf[16];
	char_u		*p;

	p = buf + 15;
	*p = '\0';
	do
	{
		--p;
		*p = (char_u) (i % 10 + '0');
		i /= 10;
	}
	while (i > 0 && p > buf);
	return p;
}

#ifndef HAVE_TGETENT

/*
 * minimal tgoto() implementation.
 * no padding and we only parse for %i %d and %+char
 */
char *tgoto __ARGS((char *, int, int));

	char *
tgoto(cm, x, y)
	char *cm;
	int x, y;
{
	static char buf[30];
	char *p, *s, *e;

	if (!cm)
		return "OOPS";
	e = buf + 29;
	for (s = buf; s < e && *cm; cm++)
	{
		if (*cm != '%')
		{
			*s++ = *cm;
			continue;
		}
		switch (*++cm)
		{
		case 'd':
			p = (char *)tltoa((unsigned long)y);
			y = x;
			while (*p)
				*s++ = *p++;
			break;
		case 'i':
			x++;
			y++;
			break;
		case '+':
			*s++ = (char)(*++cm + y);
			y = x;
			break;
		case '%':
			*s++ = *cm;
			break;
		default:
			return "OOPS";
		}
	}
	*s = '\0';
	return buf;
}

#endif /* HAVE_TGETENT */

/*
 * Set the terminal name to "term" and initialize the terminal options.
 * If "term" is NULL or empty, get the terminal name from the environment.
 * If that fails, use the default terminal name.
 */
	void
termcapinit(term)
	char_u *term;
{
	if (!term || !*term)
		term = vim_getenv((char_u *)"TERM");
	if (!term || !*term)
		term = DEFAULT_TERM;
	set_string_option((char_u *)"term", -1, term, TRUE);
	/*
	 * Avoid using "term" here, because the next vim_getenv() may overwrite it.
	 */
	set_termname(term_strings[KS_NAME] != NULL ? term_strings[KS_NAME] : term);
}

/*
 * the number of calls to mch_write is reduced by using the buffer "outbuf"
 */
#undef BSIZE			/* hpux has BSIZE in sys/option.h */
#define BSIZE	2048
static char_u			outbuf[BSIZE];
static int				bpos = 0;		/* number of chars in outbuf */

/*
 * flushbuf(): flush the output buffer
 */
	void
flushbuf()
{
	if (bpos != 0)
	{
		mch_write(outbuf, bpos);
		bpos = 0;
	}
}

#ifdef USE_GUI
/*
 * trash_output_buf(): Throw away the contents of the output buffer
 */
	void
trash_output_buf()
{
	bpos = 0;
}
#endif

/*
 * outchar(c): put a character 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_outstr() and screen_outchar() for that).
 */
	void
outchar(c)
	unsigned	c;
{
#if defined(UNIX) || defined(VMS) || defined(AMIGA)
	if (c == '\n')		/* turn LF into CR-LF (CRMOD doesn't seem to do this) */
		outchar('\r');
#endif

	outbuf[bpos++] = c;

	/* For testing we flush each time. */
	if (bpos >= BSIZE || p_wd)
		flushbuf();
}

static void outchar_nf __ARGS((unsigned));

/*
 * outchar_nf(c): like outchar(), but don't flush when p_wd is set
 */
	static void
outchar_nf(c)
	unsigned	c;
{
#if defined(UNIX) || defined(VMS) || defined(AMIGA)
	if (c == '\n')		/* turn LF into CR-LF (CRMOD doesn't seem to do this) */
		outchar_nf('\r');
#endif

	outbuf[bpos++] = c;

	/* For testing we flush each time. */
	if (bpos >= BSIZE)
		flushbuf();
}

/*
 * a never-padding outstr.
 * 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. (jw)
 * This should only be used for writing terminal codes, not for outputting
 * normal text (use functions like msg_outstr() and screen_outchar() for that).
 */
	void
outstrn(s)
	char_u *s;
{
	if (bpos > BSIZE - 20)		/* avoid terminal strings being split up */
		flushbuf();
	while (*s)
		outchar_nf(*s++);

	/* For testing we write one string at a time. */
	if (p_wd)
		flushbuf();
}

/*
 * outstr(s): put a string character 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_outstr() and screen_outchar() for that).
 */
	void
outstr(s)
	register char_u			 *s;
{
	if (bpos > BSIZE - 20)		/* avoid terminal strings being split up */
		flushbuf();
	if (s)
#ifdef HAVE_TGETENT
		tputs((char *)s, 1, TPUTSFUNCAST outchar_nf);
#else
		while (*s)
			outchar_nf(*s++);
#endif

	/* For testing we write one string at a time. */
	if (p_wd)
		flushbuf();
}

/*
 * cursor positioning using termcap parser. (jw)
 */
	void
windgoto(row, col)
	int		row;
	int		col;
{
	if (col != screen_cur_col || row != screen_cur_row)
	{
		/*
		 * When positioning on the same row, column 0, use CR, it's just one
		 * character.  Don't do this when the cursor has moved past the end of
		 * the line, the cursor position is undefined then.
		 */
		if (row == screen_cur_row && col == 0 && screen_cur_col < Columns)
		{
			outchar('\r');
			screen_cur_col = 0;
		}
		else
		{
			OUTSTR(tgoto((char *)T_CM, col, row));
			screen_cur_col = col;
			screen_cur_row = row;
		}
	}
}

/*
 * Set cursor to current position.
 */

	void
setcursor()
{
	if (!RedrawingDisabled)
		windgoto(curwin->w_winpos + curwin->w_row,
#ifdef RIGHTLEFT
				curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
#endif
															curwin->w_col);
}

/*
 * Make sure we have a valid set or terminal options.
 * Replace all entries that are NULL by empty_option
 */
	void
ttest(pairs)
	int	pairs;
{
	char *t = NULL;

	check_options();				/* make sure no options are NULL */

  /* hard requirements */
	if (*T_CL == NUL)				/* erase display */
		t = "cl";
	if (*T_CM == NUL)				/* cursor motion */
		t = "cm";

	if (t)
		EMSG2("terminal capability %s required", t);

/*
 * 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 = 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;
		}
	}
	need_gather = TRUE;
}

/*
 * Represent the given long_u as individual bytes, with the most significant
 * byte first, and store them in dst.
 */
	void
add_long_to_buf(val, dst)
	long_u	val;
	char_u	*dst;
{
	int		i;
	int		shift;

	for (i = 1; i <= sizeof(long_u); i++)
	{
		shift = 8 * (sizeof(long_u) - i);
		dst[i - 1] = (char_u) ((val >> shift) & 0xff);
	}
}

/*
 * Interpret the next string of bytes in buf as a long integer, with the most
 * significant byte first.	Note that it is assumed that buf has been through
 * inchar(), so that NUL and K_SPECIAL will be represented as three bytes each.
 * Puts result in val, and returns the number of bytes read from buf
 * (between sizeof(long_u) and 2 * sizeof(long_u)), or -1 if not enough bytes
 * were present.
 */
	int
get_long_from_buf(buf, val)
	char_u	*buf;
	long_u	*val;
{
	int		len;
	char_u	bytes[sizeof(long_u)];
	int		i;
	int		shift;

	*val = 0;
	len = get_bytes_from_buf(buf, bytes, (int)sizeof(long_u));
	if (len != -1)
	{
		for (i = 0; i < sizeof(long_u); i++)
		{
			shift = 8 * (sizeof(long_u) - 1 - i);
			*val += (long_u)bytes[i] << shift;
		}
	}
	return len;
}

/*
 * Read the next num_bytes bytes from buf, and store them in bytes.  Assume
 * that buf has been through inchar().	Returns the actual number of bytes used
 * from buf (between num_bytes and num_bytes*2), or -1 if not enough bytes were
 * available.
 */
	static int
get_bytes_from_buf(buf, bytes, num_bytes)
	char_u	*buf;
	char_u	*bytes;
	int		num_bytes;
{
	int		len = 0;
	int		i;
	char_u	c;

	for (i = 0; i < num_bytes; i++)
	{
		if ((c = buf[len++]) == NUL)
			return -1;
		if (c == K_SPECIAL)
		{
			if (buf[len] == NUL || buf[len + 1] == NUL)		/* cannot happen? */
				return -1;
			if (buf[len++] == KS_ZERO)
				c = NUL;
			++len;		/* skip K_FILLER */
			/* else it should be KS_SPECIAL, and c already equals K_SPECIAL */
		}
		bytes[i] = c;
	}
	return len;
}

/*
 * outnum - output a (big) number fast
 */
	void
outnum(n)
	register long n;
{
	OUTSTRN(tltoa((unsigned long)n));
}

	void
check_winsize()
{
	static int	old_Rows = 0;

	if (Columns < MIN_COLUMNS)
		Columns = MIN_COLUMNS;
	if (Rows < min_rows())		/* need room for one window and command line */
		Rows = min_rows();

	if (old_Rows != Rows)
	{
		old_Rows = Rows;
		screen_new_rows();			/* may need to update window sizes */
	}
}

/*
 * set window size
 * If 'mustset' is TRUE, we must set Rows and Columns, do not get real
 * window size (this is used for the :win command).
 * If 'mustset' is FALSE, we may try to get the real window size and if
 * it fails use 'width' and 'height'.
 */
	void
set_winsize(width, height, mustset)
	int		width, height;
	int		mustset;
{
	register int		tmp;

	if (width < 0 || height < 0)	/* just checking... */
		return;

									/* postpone the resizing */
	if (State == HITRETURN || State == SETWSIZE)
	{
		State = SETWSIZE;
		return;
	}
	if (State != ASKMORE && State != EXTERNCMD)
		screenclear();
	else
		screen_start();					/* don't know where cursor is now */
#ifdef AMIGA
	flushbuf();			/* must do this before mch_get_winsize for some
							obscure reason */
#endif /* AMIGA */
	if (mustset || (mch_get_winsize() == FAIL && height != 0))
	{
		Rows = height;
		Columns = width;
		check_winsize();		/* always check, to get p_scroll right */
		mch_set_winsize();
	}
	else
		check_winsize();		/* always check, to get p_scroll right */
	if (!starting)
	{
		comp_Botline_all();
		if (State == ASKMORE || State == EXTERNCMD)
		{
			screenalloc(FALSE);	/* don't redraw, just adjust screen size */
			if (State == ASKMORE)
			{
				msg_moremsg(FALSE);	/* display --more-- message again */
				msg_row = Rows - 1;
			}
			else
				windgoto(msg_row, msg_col);	/* put cursor back */
		}
		else
		{
			tmp = RedrawingDisabled;
			RedrawingDisabled = FALSE;
			updateScreen(CURSUPD);
			RedrawingDisabled = tmp;
			if (State == CMDLINE)
				redrawcmdline();
			else
				setcursor();
		}
	}
	flushbuf();
}

	void
settmode(raw)
	int	 raw;
{
	static int	oldraw = FALSE;

#ifdef USE_GUI
	/* don't set the term where gvim was started in raw mode */
	if (gui.in_use)
		return;
#endif
	if (full_screen)
	{
		/*
		 * When returning after calling a shell we want to really set the
		 * terminal to raw mode, even though we think it already is, because
		 * the shell program may have reset the terminal mode.
		 * When we think the terminal is not-raw, don't try to set it to
		 * not-raw again, because that causes problems (logout!) on some
		 * machines.
		 */
		if (raw || oldraw)
		{
			flushbuf();
			mch_settmode(raw);	/* machine specific function */
#ifdef USE_MOUSE
			if (!raw)
				mch_setmouse(FALSE);			/* switch mouse off */
			else
				setmouse();						/* may switch mouse on */
#endif
			flushbuf();
			oldraw = raw;
		}
	}
}

	void
starttermcap()
{
	if (full_screen && !termcap_active)
	{
		outstr(T_TI);					/* start termcap mode */
		outstr(T_KS);					/* start "keypad transmit" mode */
		flushbuf();
		termcap_active = TRUE;
		screen_start();					/* don't know where cursor is now */
	}
}

	void
stoptermcap()
{
	if (full_screen && termcap_active)
	{
		outstr(T_KE);					/* stop "keypad transmit" mode */
		flushbuf();
		termcap_active = FALSE;
		cursor_on();					/* just in case it is still off */
		outstr(T_TE);					/* stop termcap mode */
		screen_start();					/* don't know where cursor is now */
	}
}

#ifdef USE_MOUSE
/*
 * setmouse() - switch mouse on/off depending on current mode and 'mouse'
 */
	void
setmouse()
{
	int		checkfor;

# ifdef USE_GUI
	if (gui.in_use)
		return;
# endif
	if (*p_mouse == NUL)			/* be quick when mouse is off */
		return;

	if (VIsual_active)
		checkfor = MOUSE_VISUAL;
	else if (State == HITRETURN)
		checkfor = MOUSE_RETURN;
	else if (State & INSERT)
		checkfor = MOUSE_INSERT;
	else if (State & CMDLINE)
		checkfor = MOUSE_COMMAND;
	else
		checkfor = MOUSE_NORMAL;				/* assume normal mode */
	
	if (mouse_has(checkfor))
		mch_setmouse(TRUE);
	else
		mch_setmouse(FALSE);
}

/*
 * Return TRUE if
 * - "c" is in 'mouse', or
 * - 'a' is in 'mouse' and "c" is in MOUSE_A, or
 * - the current buffer is a help file and 'h' is in 'mouse' and we are in a
 *   normal editing mode (not at hit-return message).
 */
	int
mouse_has(c)
	int		c;
{
	return (vim_strchr(p_mouse, c) != NULL ||
					(vim_strchr(p_mouse, 'a') != NULL &&
								  vim_strchr((char_u *)MOUSE_A, c) != NULL) ||
					(c != MOUSE_RETURN && curbuf->b_help &&
											vim_strchr(p_mouse, MOUSE_HELP)));
}
#endif

/*
 * 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()
{
	if (*T_VS != NUL)
	{
		outstr(T_VS);
		outstr(T_VE);
		screen_start();					/* don't know where cursor is now */
	}
}

/*
 * enable cursor, unless in Visual mode or no inversion possible
 */
static int cursor_is_off = FALSE;

	void
cursor_on()
{
	if (full_screen)
	{
		if (cursor_is_off && (!VIsual_active || highlight == NULL))
		{
			outstr(T_VE);
			cursor_is_off = FALSE;
		}
	}
}

	void
cursor_off()
{
	if (full_screen)
	{
		if (!cursor_is_off)
			outstr(T_VI);			/* disable cursor */
		cursor_is_off = TRUE;
	}
}

/*
 * Set scrolling region for window 'wp'.
 * The region starts 'off' lines from the start of the window.
 */
	void
scroll_region_set(wp, off)
	WIN		*wp;
	int		off;
{
	OUTSTR(tgoto((char *)T_CS, wp->w_winpos + wp->w_height - 1,
														 wp->w_winpos + off));
	screen_start();					/* don't know where cursor is now */
}

/*
 * Reset scrolling region to the whole screen.
 */
	void
scroll_region_reset()
{
	OUTSTR(tgoto((char *)T_CS, (int)Rows - 1, 0));
	screen_start();					/* don't know where cursor is now */
}


/*
 * List of terminal codes that are currently recognized.
 */

struct termcode
{
	char_u	name[2];		/* termcap name of entry */
	char_u	*code;			/* terminal code (in allocated memory) */
	int		len;			/* STRLEN(code) */
} *termcodes = NULL;

static int	tc_max_len = 0;	/* number of entries that termcodes[] can hold */
static int	tc_len = 0;		/* current number of entries in termcodes[] */

	void
clear_termcodes()
{
	while (tc_len > 0)
		vim_free(termcodes[--tc_len].code);
	vim_free(termcodes);
	termcodes = NULL;
	tc_max_len = 0;

#ifdef HAVE_TGETENT
	BC = (char *)empty_option;
	UP = (char *)empty_option;
	PC = ' ';					/* set pad character to space */
	ospeed = 0;
#endif

	need_gather = TRUE;			/* need to fill termleader[] */
}

/*
 * Add a new entry to the list of terminal codes.
 * The list is kept alphabetical for ":set termcap"
 */
	void
add_termcode(name, string)
	char_u	*name;
	char_u	*string;
{
	struct termcode *new_tc;
	int				i, j;
	char_u			*s;

	if (string == NULL || *string == NUL)
	{
		del_termcode(name);
		return;
	}

	s = strsave(string);
	if (s == NULL)
		return;

	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 = (struct termcode *)alloc(
							(unsigned)(tc_max_len * sizeof(struct termcode)));
		if (new_tc == NULL)
		{
			tc_max_len -= 20;
			return;
		}
		for (i = 0; i < tc_len; ++i)
			new_tc[i] = termcodes[i];
		vim_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: Replace old code.
			 */
			if (termcodes[i].name[1] == name[1])
			{
				vim_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;
	termcodes[i].len = STRLEN(s);
	++tc_len;
}

	char_u	*
find_termcode(name)
	char_u	*name;
{
	int		i;

	for (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(i)
	int		i;
{
	if (i >= tc_len)
		return NULL;
	return &termcodes[i].name[0];
}

	void
del_termcode(name)
	char_u	*name;
{
	int		i;

	if (termcodes == NULL)		/* nothing there yet */
		return;

	need_gather = TRUE;			/* need to fill termleader[] */

	for (i = 0; i < tc_len; ++i)
		if (termcodes[i].name[0] == name[0] && termcodes[i].name[1] == name[1])
		{
			vim_free(termcodes[i].code);
			--tc_len;
			while (i < tc_len)
			{
				termcodes[i] = termcodes[i + 1];
				++i;
			}
			return;
		}
	/* not found. Give error message? */
}

/*
 * Check if typebuf[] contains a terminal key code.
 * Check from typebuf[typeoff] to typebuf[typeoff + max_offset].
 * Return 0 for no match, -1 for partial match, > 0 for full match.
 * With a match, the match is removed, the replacement code is inserted in
 * typebuf[] and the number of characters in typebuf[] is returned.
 */
	int
check_termcode(max_offset)
	int		max_offset;
{
	register char_u		*tp;
	register char_u		*p;
	int			slen = 0;		/* init for GCC */
	int			len;
	int			offset;
	char_u		key_name[2];
	int			new_slen;
	int			extra;
	char_u		string[MAX_KEY_CODE_LEN + 1];
	int			i;
#ifdef USE_GUI
	long_u		val;
#endif
#ifdef USE_MOUSE
	char_u		bytes[3];
	int			num_bytes;
	int			mouse_code;
	int			modifiers;
	int			is_click, is_drag;
	int			current_button;
	static int	held_button = MOUSE_RELEASE;
	static int	orig_num_clicks = 1;
	static int	orig_mouse_code = 0x0;
# if defined(UNIX) && defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
	static int	orig_mouse_col = 0;
	static int	orig_mouse_row = 0;
	static linenr_t	orig_topline = 0;
	static struct timeval  orig_mouse_time = {0, 0};
										/* time of previous mouse click */
	struct timeval  mouse_time;			/* time of current mouse click */
	long		timediff;				/* elapsed time in msec */
# endif
#endif

	/*
	 * Speed up the checks for terminal codes by gathering all first bytes
	 * used in termleader[].  Often this is just a single <Esc>.
	 */
	if (need_gather)
		gather_termleader();

	/*
	 * Check at several positions in typebuf[], to catch something like
	 * "x<Up>" that can be mapped. Stop at max_offset, because characters
	 * after that cannot be used for mapping, and with @r commands typebuf[]
	 * can become very long.
	 * This is used often, KEEP IT FAST!
	 */
	for (offset = 0; offset < typelen && offset < max_offset; ++offset)
	{
		tp = typebuf + typeoff + offset;

		/*
		 * Don't check characters after K_SPECIAL, those are already
		 * translated terminal chars (avoid translating ~@^Hx).
		 */
		if (*tp == K_SPECIAL)
		{
			offset += 2;		/* there are always 2 extra characters */
			continue;
		}

		/*
		 * Skip this position if the character does not appear as the first
		 * character in term_strings. This speeds up a lot, since most
		 * termcodes start with the same character (ESC or CSI).
		 */
		i = *tp;
		for (p = termleader; *p && *p != i; ++p)
			;
		if (*p == NUL)
			continue;

		/*
		 * Skip this position if p_ek is not set and
		 * typebuf[typeoff + offset] is an ESC and we are in insert mode
		 */
		if (*tp == ESC && !p_ek && (State & INSERT))
			continue;

		len = typelen - offset;	/* length of the input */
		new_slen = 0;			/* Length of what will replace the termcode */
		key_name[0] = NUL;		/* no key name found yet */

#ifdef USE_GUI
		if (gui.in_use)
		{
			/*
			 * GUI special key codes are all of the form [CSI xx].
			 */
			if (*tp == CSI)			/* Special key from GUI */
			{
				if (len < 3)
					return -1;		/* Shouldn't happen */
				slen = 3;
				key_name[0] = tp[1];
				key_name[1] = tp[2];
			}
		}
		else
#endif /* USE_GUI */
		{
			for (i = 0; i < tc_len; ++i)
			{
				/*
				 * Ignore the entry if we are not at the start of typebuf[]
				 * and there are not enough characters to make a match.
				 */
				slen = termcodes[i].len;
				if (offset && len < slen)
					continue;
				if (STRNCMP(termcodes[i].code, tp,
									 (size_t)(slen > len ? len : slen)) == 0)
				{
					if (len < slen)				/* got a partial sequence */
						return -1;				/* need to get more chars */

					key_name[0] = termcodes[i].name[0];
					key_name[1] = termcodes[i].name[1];

					/*
					 * If it's a shifted special key, then include the SHIFT
					 * modifier
					 */
					if (unshift_special_key(&key_name[0]))
					{
						string[new_slen++] = K_SPECIAL;
						string[new_slen++] = KS_MODIFIER;
						string[new_slen++] = MOD_MASK_SHIFT;
					}
					break;
				}
			}
		}

		if (key_name[0] == NUL)
			continue;		/* No match at this position, try next one */

		/* We only get here when we have a complete termcode match */

#ifdef USE_MOUSE
		/*
		 * If it is a mouse click, get the coordinates.
		 * we get "<t_mouse>scr", where
		 *	s == encoded mouse button state (0x20 = left, 0x22 = right, etc)
		 *	c == column + ' ' + 1 == column + 33
		 *	r == row + ' ' + 1 == row + 33
		 *
		 * The coordinates are passed on through global variables. Ugly,
		 * but this avoids trouble with mouse clicks at an unexpected
		 * moment and allows for mapping them.
		 */
		if (key_name[0] == KS_MOUSE)
		{
			num_bytes = get_bytes_from_buf(tp + slen, bytes, 3);
			if (num_bytes == -1)	/* not enough coordinates */
				return -1;
			mouse_code = bytes[0];
			mouse_col = bytes[1] - ' ' - 1;
			mouse_row = bytes[2] - ' ' - 1;
			slen += num_bytes;

			/* Interpret the mouse code */
			is_click = is_drag = FALSE;
			current_button = (mouse_code & MOUSE_CLICK_MASK);
			if (current_button == MOUSE_RELEASE)
			{
				/*
				 * If we get a mouse drag or release event when
				 * there is no mouse button held down (held_button ==
				 * MOUSE_RELEASE), produce a K_IGNORE below.
				 * (can happen when you hold down two buttons
				 * and then let them go, or click in the menu bar, but not
				 * on a menu, and drag into the text).
				 */
				if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG)
					is_drag = TRUE;
				current_button = held_button;
			}
			else
			{
#if defined(UNIX) && defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
# ifdef USE_GUI
				/*
				 * Only for Unix, when GUI is not active, we handle
				 * multi-clicks here.
				 */
				if (!gui.in_use)
# endif
				{
					/*
					 * Compute the time elapsed since the previous mouse click.
					 */
					gettimeofday(&mouse_time, NULL);
					timediff = (mouse_time.tv_usec -
											  orig_mouse_time.tv_usec) / 1000;
					if (timediff < 0)
						--orig_mouse_time.tv_sec;
					timediff += (mouse_time.tv_sec -
											   orig_mouse_time.tv_sec) * 1000;
					orig_mouse_time = mouse_time;
					if (mouse_code == orig_mouse_code &&
							timediff < p_mouset &&
							orig_num_clicks != 4 &&
							orig_mouse_col == mouse_col &&
							orig_mouse_row == mouse_row &&
							orig_topline == curwin->w_topline)
						++orig_num_clicks;
					else
						orig_num_clicks = 1;
					orig_mouse_col = mouse_col;
					orig_mouse_row = mouse_row;
					orig_topline = curwin->w_topline;
				}
# ifdef USE_GUI
				else
					orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
# endif
#else
				orig_num_clicks = NUM_MOUSE_CLICKS(mouse_code);
#endif
				is_click = TRUE;
				orig_mouse_code = mouse_code;
			}
			if (!is_drag)
				held_button = mouse_code & MOUSE_CLICK_MASK;

			/*
			 * Translate the actual mouse event into a pseudo mouse event.
			 * First work out what modifiers are to be used.
			 */
			modifiers = 0x0;
			if (orig_mouse_code & MOUSE_SHIFT)
				modifiers |= MOD_MASK_SHIFT;
			if (orig_mouse_code & MOUSE_CTRL)
				modifiers |= MOD_MASK_CTRL;
			if (orig_mouse_code & MOUSE_ALT)
				modifiers |= MOD_MASK_ALT;
			if (orig_num_clicks == 2)
				modifiers |= MOD_MASK_2CLICK;
			else if (orig_num_clicks == 3)
				modifiers |= MOD_MASK_3CLICK;
			else if (orig_num_clicks == 4)
				modifiers |= MOD_MASK_4CLICK;

			/* Add the modifier codes to our string */
			if (modifiers != 0)
			{
				string[new_slen++] = K_SPECIAL;
				string[new_slen++] = KS_MODIFIER;
				string[new_slen++] = modifiers;
			}

			/* Work out our pseudo mouse event */
			key_name[0] = KS_EXTRA;
			key_name[1] = get_pseudo_mouse_code(current_button,
														   is_click, is_drag);
		}
#endif /* USE_MOUSE */
#ifdef USE_GUI
		/*
		 * If using the GUI, then we get menu and scrollbar events.
		 * 
		 * A menu event is encoded as K_SPECIAL, KS_MENU, K_FILLER followed by
		 * four bytes which are to be taken as a pointer to the GuiMenu
		 * structure.
		 *
		 * A scrollbar event is K_SPECIAL, KS_SCROLLBAR, K_FILLER followed by
		 * one byte representing the scrollbar number, and then four bytes
		 * representing a long_u which is the new value of the scrollbar.
		 *
		 * A horizontal scrollbar event is K_SPECIAL, KS_HORIZ_SCROLLBAR,
		 * K_FILLER followed by four bytes representing a long_u which is the
		 * new value of the scrollbar.
		 */
		else if (key_name[0] == KS_MENU)
		{
			num_bytes = get_long_from_buf(tp + slen, &val);
			if (num_bytes == -1)
				return -1;
			current_menu = (GuiMenu *)val;
			slen += num_bytes;
		}
		else if (key_name[0] == KS_SCROLLBAR)
		{
			num_bytes = get_bytes_from_buf(tp + slen, bytes, 1);
			if (num_bytes == -1)
				return -1;
			current_scrollbar = (int)bytes[0];
			slen += num_bytes;
			num_bytes = get_long_from_buf(tp + slen, &val);
			if (num_bytes == -1)
				return -1;
			scrollbar_value = val;
			slen += num_bytes;
		}
		else if (key_name[0] == KS_HORIZ_SCROLLBAR)
		{
			num_bytes = get_long_from_buf(tp + slen, &val);
			if (num_bytes == -1)
				return -1;
			scrollbar_value = val;
			slen += num_bytes;
		}
#endif /* USE_GUI */
		/* Finally, add the special key code to our string */
		string[new_slen++] = K_SPECIAL;
		string[new_slen++] = key_name[0];
		string[new_slen++] = key_name[1];
		string[new_slen] = NUL;
		extra = new_slen - slen;
		if (extra < 0)
				/* remove matched chars, taking care of noremap */
			del_typebuf(-extra, offset);
		else if (extra > 0)
				/* insert the extra space we need */
			ins_typebuf(string + slen, FALSE, offset, FALSE);

		/*
		 * Careful: del_typebuf() and ins_typebuf() may have
		 * reallocated typebuf[]
		 */
		vim_memmove(typebuf + typeoff + offset, string, (size_t)new_slen);
		return (len + extra + offset);
	}
	return 0;						/* no match found */
}

/*
 * 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
 * '<'.  Also unshifts shifted special keys.
 * K_SPECIAL by itself is replaced by K_SPECIAL KS_SPECIAL K_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(from, bufp, from_part)
	char_u	*from;
	char_u	**bufp;
	int		from_part;
{
	int		i;
	char_u	key_name[2];
	char_u	*bp;
	char_u	*last_dash;
	char_u	*end_of_name;
	int		slen;
	int		modifiers;
	int		bit;
	int		key;
	int		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);
	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 = alloc((unsigned)STRLEN(from) * 6 + 1);
	if (result == NULL)			/* out of memory */
	{
		*bufp = NULL;
		return from;
	}

	src = from;

	/*
	 * Check for #n at start only: function key n
	 */
	if (from_part && src[0] == '#' && 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.
		 */
		if (do_special)
		{
			/*
			 * See if it's a string like "<C-S-MouseLeft>"
			 */
			if (src[0] == '<')
			{
				/* Find end of modifier list */
				last_dash = src;
				for (bp = src + 1; *bp == '-' || isidchar(*bp); bp++)
				{
					if (*bp == '-')
					{
						last_dash = bp;
						if (bp[1] != NUL && bp[2] == '>')
							++bp;	/* anything accepted, like <C-?> */
					}
					if (bp[0] == 't' && bp[1] == '_' && bp[2] && bp[3])
						bp += 3;	/* skip t_xx, xx may be '-' or '>' */
				}

				if (*bp == '>')		/* found matching '>' */
				{
					end_of_name = bp + 1;

					/* Which modifiers are given? */
					modifiers = 0x0;
					for (bp = src + 1; bp < last_dash; bp++)
					{
						if (*bp != '-')
						{
							bit = name_to_mod_mask(*bp);
							if (bit == 0x0)
								break;		/* Illegal modifier name */
							modifiers |= bit;
						}
					}

					/*
					 * Legal modifier name.
					 */
					if (bp >= last_dash)
					{
						/*
						 * Modifier with single letter
						 */
						if (modifiers != 0 && last_dash[2] == '>')
						{
							key = last_dash[1];
							if (modifiers & MOD_MASK_SHIFT)
								key = TO_UPPER(key);
							if (modifiers & MOD_MASK_CTRL)
								key &= 0x1f;
							if (modifiers & MOD_MASK_ALT)
								key |= 0x80;
							src = end_of_name;
							result[dlen++] = key;
							continue;
						}

						/*
						 * Key name with or without modifier.
						 */
						else if ((key = get_special_key_code(last_dash + 1))
																		 != 0)
						{
							/* Put the appropriate modifier in a string */
							if (modifiers != 0)
							{
								result[dlen++] = K_SPECIAL;
								result[dlen++] = KS_MODIFIER;
								result[dlen++] = modifiers;
								/*
								 * Special trick: for <S-TAB>  K_TAB is used
								 * instead of TAB (there are two keys for the
								 * same thing).
								 */
								if (key == TAB)
									key = K_TAB;
							}

							if (IS_SPECIAL(key))
							{
								result[dlen++] = K_SPECIAL;
								result[dlen++] = KEY2TERMCAP0(key);
								result[dlen++] = KEY2TERMCAP1(key);
							}
							else
								result[dlen++] = key;		/* only modifiers */
							src = end_of_name;
							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.
		 */
		if (do_key_code)
		{
			for (i = 0; i < tc_len; ++i)
			{
				slen = termcodes[i].len;
				if (STRNCMP(termcodes[i].code, src, (size_t)slen) == 0)
				{
					key_name[0] = termcodes[i].name[0];
					key_name[1] = termcodes[i].name[1];

					/*
					 * If it's a shifted special key, then include the SHIFT
					 * modifier
					 */
					if (unshift_special_key(&key_name[0]))
					{
						result[dlen++] = K_SPECIAL;
						result[dlen++] = KS_MODIFIER;
						result[dlen++] = MOD_MASK_SHIFT;
					}
					result[dlen++] = K_SPECIAL;
					result[dlen++] = key_name[0];
					result[dlen++] = key_name[1];
					src += slen;
					break;
				}
			}

			/*
			 * If terminal code matched, continue after it.
			 * If no terminal code matched and the character is K_SPECIAL,
			 * replace it with K_SPECIAL KS_SPECIAL K_FILLER
			 */
			if (i != tc_len)
				continue;
		}

		if (*src == K_SPECIAL)
		{
			result[dlen++] = K_SPECIAL;
			result[dlen++] = KS_SPECIAL;
			result[dlen++] = K_FILLER;
			++src;
			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;
			}
		}
		result[dlen++] = *src++;
	}
	result[dlen] = NUL;

	/*
	 * Copy the new string to allocated memory.
	 * If this fails, just return from.
	 */
	if ((*bufp = strsave(result)) != NULL)
		from = *bufp;
	vim_free(result);
	return from;
}

/*
 * Gather the first characters in the terminal key codes into a string.
 * Used to speed up check_termcode().
 */
	static void
gather_termleader()
{
	int		i;
	int		len = 0;

#ifdef USE_GUI
	if (gui.in_use)
		termleader[len++] = CSI;	/* the GUI codes are not in termcodes[] */
#endif
	termleader[len] = NUL;

	for (i = 0; i < tc_len; ++i)
		if (vim_strchr(termleader, termcodes[i].code[0]) == NULL)
		{
			termleader[len++] = termcodes[i].code[0];
			termleader[len] = NUL;
		}

	need_gather = FALSE;
}

/*
 * Show all termcodes (for ":set termcap")
 * This code looks a lot like showoptions(), but is different.
 */
	void
show_termcodes()
{
	int				col;
	int				*items;
	int				item_count;
	int				run;
	int				row, rows;
	int				cols;
	int				i;
	int				len;

#define INC	27		/* try to make three columns */
#define GAP 2		/* spaces between columns */

	if (tc_len == 0)		/* no terminal codes (must be GUI) */
		return;
	items = (int *)alloc((unsigned)(sizeof(int) * tc_len));
	if (items == NULL)
		return;

	set_highlight('t');		/* Highlight title */
	start_highlight();
	MSG_OUTSTR("\n--- Terminal keys ---");
	stop_highlight();

	/*
	 * do the loop two times:
	 * 1. display the short items (non-strings and short strings)
	 * 2. display the long items (strings)
	 */
	for (run = 1; run <= 2 && !got_int; ++run)
	{
		/*
		 * collect the items in items[]
		 */
		item_count = 0;
		for (i = 0; i < tc_len; i++)
		{
			len = show_one_termcode(termcodes[i].name,
													termcodes[i].code, FALSE);
			if ((len <= INC - GAP && run == 1) || (len > INC - GAP && run == 2))
				items[item_count++] = i;
		}

		/*
		 * display the items
		 */
		if (run == 1)
		{
			cols = (Columns + GAP) / INC;
			if (cols == 0)
				cols = 1;
			rows = (item_count + cols - 1) / cols;
		}
		else	/* run == 2 */
			rows = item_count;
		for (row = 0; row < rows && !got_int; ++row)
		{
			msg_outchar('\n');						/* go to next line */
			if (got_int)							/* 'q' typed in more */
				break;
			col = 0;
			for (i = row; i < item_count; i += rows)
			{
				msg_pos(-1, col);					/* make columns */
				show_one_termcode(termcodes[items[i]].name,
											  termcodes[items[i]].code, TRUE);
				col += INC;
			}
			flushbuf();
			mch_breakcheck();
		}
	}
	vim_free(items);
}

/*
 * Show one termcode entry.
 * Output goes into IObuff[]
 */
	int
show_one_termcode(name, code, printit)
	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 = STRLEN(IObuff);
	do
		IObuff[len++] = ' ';
	while (len < 17);
	IObuff[len] = NUL;
	if (code == NULL)
		len += 4;
	else
		len += strsize(code);

	if (printit)
	{
		msg_outstr(IObuff);
		if (code == NULL)
			msg_outstr((char_u *)"NULL");
		else
			msg_outtrans(code);
	}
	return len;
}