ftp.nice.ch/pub/next/unix/editor/vim-5.0f.s.tar.gz#/vim-5.0f/src/misc2.c

This is misc2.c in view mode; [Download] [Up]

/* 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.
 */

/*
 * misc2.c: Various functions.
 */

#include "vim.h"
#include "globals.h"
#include "proto.h"

/*
 * coladvance(col)
 *
 * Try to advance the Cursor to the specified column.
 *
 * return OK if desired column is reached, FAIL if not
 */
	int
coladvance(wcol)
	colnr_t 		wcol;
{
	int 		idx;
	char_u		*ptr;
	colnr_t		col;

	ptr = ml_get_curline();

	/* try to advance to the specified column */
	idx = -1;
	col = 0;
	while (col <= wcol && *ptr)
	{
		++idx;
		/* Count a tab for what it's worth (if list mode not on) */
		col += lbr_chartabsize(ptr, col);
		++ptr;
	}
	/*
	 * in insert mode it is allowed to be one char beyond the end of the line
	 */
	if ((State & INSERT) && col <= wcol)
		++idx;
	if (idx < 0)
		curwin->w_cursor.col = 0;
	else
		curwin->w_cursor.col = idx;
	if (col <= wcol)
		return FAIL;		/* Couldn't reach column */
	else
		return OK;			/* Reached column */
}

/*
 * inc(p)
 *
 * Increment the line pointer 'p' crossing line boundaries as necessary.
 * Return 1 when crossing a line, -1 when at end of file, 0 otherwise.
 */
	int
inc_cursor()
{
	return inc(&curwin->w_cursor);
}

	int
inc(lp)
	FPOS  *lp;
{
	char_u  *p = ml_get_pos(lp);

	if (*p != NUL)		/* still within line, move to next char (may be NUL) */
	{
		lp->col++;
		return ((p[1] != NUL) ? 0 : 1);
	}
	if (lp->lnum != curbuf->b_ml.ml_line_count)		/* there is a next line */
	{
		lp->col = 0;
		lp->lnum++;
		return 1;
	}
	return -1;
}

/*
 * incl(lp): same as inc(), but skip the NUL at the end of non-empty lines
 */
	int
incl(lp)
	FPOS 	*lp;
{
	int 	r;

	if ((r = inc(lp)) == 1 && lp->col)
		r = inc(lp);
	return r;
}

/*
 * dec(p)
 *
 * Decrement the line pointer 'p' crossing line boundaries as necessary.
 * Return 1 when crossing a line, -1 when at start of file, 0 otherwise.
 */
	int
dec_cursor()
{
	return dec(&curwin->w_cursor);
}

	int
dec(lp)
	FPOS  *lp;
{
	if (lp->col > 0)
	{			/* still within line */
		lp->col--;
		return 0;
	}
	if (lp->lnum > 1)
	{			/* there is a prior line */
		lp->lnum--;
		lp->col = STRLEN(ml_get(lp->lnum));
		return 1;
	}
	return -1;					/* at start of file */
}

/*
 * decl(lp): same as dec(), but skip the NUL at the end of non-empty lines
 */
	int
decl(lp)
	FPOS 	*lp;
{
	int 	r;

	if ((r = dec(lp)) == 1 && lp->col)
		r = dec(lp);
	return r;
}

/*
 * Make sure curwin->w_cursor.lnum is valid.
 */
	void
check_cursor_lnum()
{
	if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
		curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
	if (curwin->w_cursor.lnum <= 0)
		curwin->w_cursor.lnum = 1;
}

/*
 * make sure curwin->w_cursor in on a valid character
 */
	void
adjust_cursor()
{
	colnr_t len;

	check_cursor_lnum();

	len = STRLEN(ml_get_curline());
	if (len == 0)
		curwin->w_cursor.col = 0;
	else if (curwin->w_cursor.col >= len)
		curwin->w_cursor.col = len - 1;
}

/*
 * When curwin->w_leftcol has changed, adjust the cursor position.
 * Return TRUE if the cursor was moved.
 */
	int
leftcol_changed()
{
	long		lastcol;
	colnr_t		s, e;
	int			retval = FALSE;

	changed_cline_bef_curs();
	lastcol = curwin->w_leftcol + Columns - (curwin->w_p_nu ? 8 : 0) - 1;
	validate_virtcol();

	/*
	 * If the cursor is right or left of the screen, move it to last or first
	 * character.
	 */
	if (curwin->w_virtcol > (colnr_t)lastcol)
	{
		retval = TRUE;
		coladvance((colnr_t)lastcol);
	}
	else if (curwin->w_virtcol < curwin->w_leftcol)
	{
		retval = TRUE;
		(void)coladvance(curwin->w_leftcol);
	}

	/*
	 * If the start of the character under the cursor is not on the screen,
	 * advance the cursor one more char.  If this fails (last char of the
	 * line) adjust the scrolling.
	 */
	getvcol(curwin, &curwin->w_cursor, &s, NULL, &e);
	if (e > (colnr_t)lastcol)
	{
		retval = TRUE;
		coladvance(s - 1);
	}
	else if (s < curwin->w_leftcol)
	{
		retval = TRUE;
		if (coladvance(e + 1) == FAIL)	/* there isn't another character */
		{
			curwin->w_leftcol = s;		/* adjust w_leftcol instead */
			changed_cline_bef_curs();
		}
	}

	redraw_later(NOT_VALID);
	return retval;
}

/**********************************************************************
 * Various routines dealing with allocation and deallocation of memory.
 */

/*
 * Some memory is reserved for error messages and for being able to
 * call mf_release_all(), which needs some memory for mf_trans_add().
 */
#define KEEP_ROOM 8192L

/*
 * Note: if unsinged is 16 bits we can only allocate up to 64K with alloc().
 * Use lalloc for larger blocks.
 */
	char_u *
alloc(size)
	unsigned		size;
{
	return (lalloc((long_u)size, TRUE));
}

/*
 * Allocate memory and set all bytes to zero.
 */
	char_u *
alloc_clear(size)
	unsigned		size;
{
	char_u *p;

	p = (lalloc((long_u)size, TRUE));
	if (p != NULL)
		(void)vim_memset(p, 0, (size_t)size);
	return p;
}

/*
 * alloc() with check for maximum line length
 */
	char_u *
alloc_check(size)
	unsigned		size;
{
#if !defined(UNIX) && !defined(__EMX__)
	if (sizeof(int) == 2 && size > 0x7fff)
	{
		EMSG("Line is becoming too long");
		return NULL;
	}
#endif
	return (lalloc((long_u)size, TRUE));
}

	char_u *
lalloc(size, message)
	long_u			size;
	int				message;
{
	char_u   	*p;					/* pointer to new storage space */
	static int	releasing = FALSE;	/* don't do mf_release_all() recursive */
	int			try_again;

	if (size <= 0)
	{
		EMSGN("Internal error: lalloc(%ld, )", size);
		return NULL;
	}
#if defined(MSDOS) && !defined(DJGPP)
	if (size >= 0xfff0)			/* in MSDOS we can't deal with >64K blocks */
		p = NULL;
	else
#endif

	/*
	 * If out of memory, try to release some memfile blocks.
	 * If some blocks are released call malloc again.
	 */
	for (;;)
	{
		if ((p = (char_u *)malloc(size)) != NULL)
		{
			if (mch_avail_mem(TRUE) < KEEP_ROOM && !releasing)
			{ 								/* System is low... no go! */
					vim_free((char *)p);
					p = NULL;
			}
		}
	/*
	 * Remember that mf_release_all() is being called to avoid an endless loop,
	 * because mf_release_all() may call alloc() recursively.
	 */
		if (p != NULL || releasing)
			break;
		releasing = TRUE;
		try_again = mf_release_all();
		releasing = FALSE;
		if (!try_again)
			break;
	}

	/*
	 * Avoid repeating the error message many times (they take 1 second each).
	 * Did_outofmem_msg is reset when a character is read.
	 */
	if (message && p == NULL)
		do_outofmem_msg();
	return (p);
}

	void
do_outofmem_msg()
{
	if (!did_outofmem_msg)
	{
		emsg(e_outofmem);
		did_outofmem_msg = TRUE;
	}
}

/*
 * copy a string into newly allocated memory
 */
	char_u *
vim_strsave(string)
	char_u		   *string;
{
	char_u *p;

	p = alloc((unsigned) (STRLEN(string) + 1));
	if (p != NULL)
		STRCPY(p, string);
	return p;
}

	char_u *
vim_strnsave(string, len)
	char_u		*string;
	int 		len;
{
	char_u *p;

	p = alloc((unsigned) (len + 1));
	if (p != NULL)
	{
		STRNCPY(p, string, len);
		p[len] = NUL;
	}
	return p;
}

/*
 * like vim_strnsave(), but remove backslashes from the string.
 */
	char_u *
vim_strnsave_esc(string, len)
	char_u		*string;
	int 		len;
{
	char_u *p1, *p2;

	p1 = alloc((unsigned) (len + 1));
	if (p1 != NULL)
	{
		STRNCPY(p1, string, len);
		p1[len] = NUL;
		for (p2 = p1; *p2; ++p2)
			if (*p2 == '\\' && *(p2 + 1) != NUL)
				STRCPY(p2, p2 + 1);
	}
	return p1;
}

/*
 * Same as vim_strsave(), but any characters found in esc_chars are preceded
 * by a backslash.
 */
	char_u *
vim_strsave_escaped(string, esc_chars)
	char_u		*string;
	char_u		*esc_chars;
{
	char_u		*p;
	char_u		*p2;
	char_u		*escaped_string;
	unsigned	length;

	/*
	 * First count the number of backslashes required.
	 * Then allocate the memory and insert them.
	 */
	length = 1;							/* count the trailing '/' and NUL */
	for (p = string; *p; p++)
	{
		if (vim_strchr(esc_chars, *p) != NULL)
			++length;					/* count a backslash */
		++length;						/* count an ordinary char */
	}
	escaped_string = alloc(length);
	if (escaped_string != NULL)
	{
		p2 = escaped_string;
		for (p = string; *p; p++)
		{
			if (vim_strchr(esc_chars, *p) != NULL)
				*p2++ = '\\';
			*p2++ = *p;
		}
		*p2 = NUL;
	}
	return escaped_string;
}

/*
 * copy a number of spaces
 */
	void
copy_spaces(ptr, count)
	char_u	*ptr;
	size_t	count;
{
	size_t	i = count;
	char_u	*p = ptr;

	while (i--)
		*p++ = ' ';
}

/*
 * delete spaces at the end of a string
 */
	void
del_trailing_spaces(ptr)
	char_u *ptr;
{
	char_u	*q;

	q = ptr + STRLEN(ptr);
	while (--q > ptr && vim_iswhite(q[0]) && q[-1] != '\\' &&
														   q[-1] != Ctrl('V'))
		*q = NUL;
}

/*
 * vim_strncpy()
 *
 * This is here because strncpy() does not guarantee successful results when
 * the to and from strings overlap.  It is only currently called from nextwild()
 * which copies part of the command line to another part of the command line.
 * This produced garbage when expanding files etc in the middle of the command
 * line (on my terminal, anyway) -- webb.
 */
	void
vim_strncpy(to, from, len)
	char_u *to;
	char_u *from;
	int len;
{
	int i;

	if (to <= from)
	{
		while (len-- && *from)
			*to++ = *from++;
		if (len >= 0)
			*to = *from;	/* Copy NUL */
	}
	else
	{
		for (i = 0; i < len; i++)
		{
			to++;
			if (*from++ == NUL)
			{
				i++;
				break;
			}
		}
		for (; i > 0; i--)
			*--to = *--from;
	}
}

/*
 * Isolate one part of a string option where parts are separated with commas.
 * The part is copied into buf[maxlen].
 * "*option" is advanced to the next part.
 * The length is returned.
 */
	int
copy_option_part(option, buf, maxlen, sep_chars)
	char_u		**option;
	char_u		*buf;
	int			maxlen;
	char		*sep_chars;
{
	int		len = 0;
	char_u	*p = *option;

	/* skip '.' at start of option part, for 'suffixes' */
	if (*p == '.')
		buf[len++] = *p++;
	while (*p && vim_strchr((char_u *)sep_chars, *p) == NULL)
	{
		/*
		 * Skip backslash before a separator character and space.
		 */
		if (p[0] == '\\' && vim_strchr((char_u *)sep_chars, p[1]) != NULL)
			++p;
		if (len < maxlen - 1)
			buf[len++] = *p;
		++p;
	}
	buf[len] = NUL;

	p = skip_to_option_part(p);	/* p points to next file name */

	*option = p;
	return len;
}

/*
 * replacement for free() that ignores NULL pointers
 */
	void
vim_free(x)
	void *x;
{
	if (x != NULL)
		free(x);
}

#ifndef HAVE_MEMSET
	void *
vim_memset(ptr, c, size)
	void	*ptr;
	int		c;
	size_t	size;
{
	char *p = ptr;

	while (size-- > 0)
		*p++ = c;
	return ptr;
}
#endif

#ifdef VIM_MEMMOVE
/*
 * Version of memmove that handles overlapping source and destination.
 * For systems that don't have a function that is guaranteed to do that (SYSV).
 */
	void
vim_memmove(dst_arg, src_arg, len)
	void	*src_arg, *dst_arg;
	size_t	len;
{
	/*
	 * A void doesn't have a size, we use char pointers.
	 */
	char *dst = dst_arg, *src = src_arg;

										/* overlap, copy backwards */
	if (dst > src && dst < src + len)
	{
		src +=len;
		dst +=len;
		while (len-- > 0)
			*--dst = *--src;
	}
	else								/* copy forwards */
		while (len-- > 0)
			*dst++ = *src++;
}
#endif

#if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) || defined(PROTO)
/*
 * Compare two strings, ignoring case.
 * return 0 for match, 1 for difference
 */
	int
vim_stricmp(s1, s2)
	char	*s1;
	char	*s2;
{
	for (;;)
	{
		if (TO_UPPER(*s1) != TO_UPPER(*s2))
			return 1;						/* this character different */
		if (*s1 == NUL)
			break;							/* strings match until NUL */
		++s1;
		++s2;
	}
	return 0;								/* strings match */
}
#endif

#if (!defined(HAVE_STRNCASECMP) && !defined(HAVE_STRNICMP)) || defined(PROTO)
/*
 * Compare two strings, for length "len", ignoring case.
 * return 0 for match, 1 for difference
 */
	int
vim_strnicmp(s1, s2, len)
	char	*s1;
	char	*s2;
	size_t	len;
{
	while (len)
	{
		if (TO_UPPER(*s1) != TO_UPPER(*s2))
			return 1;						/* this character different */
		if (*s1 == NUL)
			break;							/* strings match until NUL */
		++s1;
		++s2;
		--len;
	}
	return 0;								/* strings match */
}
#endif

/*
 * Version of strchr() and strrchr() that handle unsigned char strings
 * with characters above 128 correctly. Also it doesn't return a pointer to
 * the NUL at the end of the string.
 */
	char_u	*
vim_strchr(string, n)
	char_u	*string;
	int		n;
{
	while (*string)
	{
		if (*string == n)
			return string;
		++string;
	}
	return NULL;
}

	char_u	*
vim_strrchr(string, n)
	char_u	*string;
	int		n;
{
	char_u	*retval = NULL;

	while (*string)
	{
		if (*string == n)
			retval = string;
		++string;
	}
	return retval;
}

/*
 * Vim's version of strpbrk(), in case it's missing.
 */
#if !defined(HAVE_STRPBRK) || defined(PROTO)
# ifdef vim_strpbrk
#  undef vim_strpbrk
# endif
	char_u *
vim_strpbrk(s, charset)
	char_u	*s;
	char_u	*charset;
{
	while (*s)
	{
		if (vim_strchr(charset, *s) != NULL)
			return s;
		++s;
	}
	return NULL;
}
#endif

/*
 * Vim has its own isspace() function, because on some machines isspace()
 * can't handle characters above 128.
 */
	int
vim_isspace(x)
	int		x;
{
	return ((x >= 9 && x <= 13) || x == ' ');
}

/************************************************************************
 * Functions for hanlding growing arrays.
 */

/*
 * Clear an allocated growing array.
 */
	void
ga_clear(ga)
	struct growarray *ga;
{
	vim_free(ga->ga_data);
	ga->ga_data = NULL;
	ga->ga_room = 0;
	ga->ga_len = 0;
}

/*
 * Make room in a growing array for at least "n" items.
 * Return FAIL for failure, OK otherwise.
 */
	int
ga_grow(ga, n)
	struct growarray	*ga;
	int					n;			/* number of items to grow the array */
{
	size_t			len;
	char_u			*pp;

	if (ga->ga_room < n)
	{
		if (n < ga->ga_growsize)
			n = ga->ga_growsize;
		len = ga->ga_itemsize * (ga->ga_len + n);
		pp = alloc_clear((unsigned)len);
		if (pp == NULL)
			return FAIL;
		ga->ga_room = ga->ga_growsize;
		if (ga->ga_data != NULL)
			vim_memmove(pp, ga->ga_data,
									  (size_t)(ga->ga_itemsize * ga->ga_len));
		ga->ga_data = pp;
	}
	return OK;
}

/************************************************************************
 * functions that use lookup tables for various things, generally to do with
 * special key codes.
 */

/*
 * Some useful tables.
 */

static struct
{
	int		mod_mask;		/* Bit-mask for particular key modifier */
	char_u	name;			/* Single letter name of modifier */
} mod_mask_table[] =
{
	{MOD_MASK_ALT,		(char_u)'M'},
	{MOD_MASK_CTRL,		(char_u)'C'},
	{MOD_MASK_SHIFT,	(char_u)'S'},
	{MOD_MASK_2CLICK,	(char_u)'2'},
	{MOD_MASK_3CLICK,	(char_u)'3'},
	{MOD_MASK_4CLICK,	(char_u)'4'},
	{0x0,				NUL}
};

/*
 * Shifted key terminal codes and their unshifted equivalent.
 * Don't add mouse codes here, they are handled seperately!
 */
static char_u shifted_keys_table[] =
{
/*  shifted     			unshifted  */
	'&', '9',				'@', '1',			/* begin */
	'&', '0',				'@', '2',			/* cancel */
	'*', '1',				'@', '4',			/* command */
	'*', '2',				'@', '5',			/* copy */
	'*', '3',				'@', '6',			/* create */
	'*', '4',				'k', 'D',			/* delete char */
	'*', '5',				'k', 'L',			/* delete line */
	'*', '7',				'@', '7',			/* end */
	'*', '9',				'@', '9',			/* exit */
	'*', '0',				'@', '0',			/* find */
	'#', '1',				'%', '1',			/* help */
	'#', '2',				'k', 'h',			/* home */
	'#', '3',				'k', 'I',			/* insert */
	'#', '4',				'k', 'l',			/* left arrow */
	'%', 'a',				'%', '3',			/* message */
	'%', 'b',				'%', '4',			/* move */
	'%', 'c',				'%', '5',			/* next */
	'%', 'd',				'%', '7',			/* options */
	'%', 'e',				'%', '8',			/* previous */
	'%', 'f',				'%', '9',			/* print */
	'%', 'g',				'%', '0',			/* redo */
	'%', 'h',				'&', '3',			/* replace */
	'%', 'i',				'k', 'r',			/* right arrow */
	'%', 'j',				'&', '5',			/* resume */
	'!', '1',				'&', '6',			/* save */
	'!', '2',				'&', '7',			/* suspend */
	'!', '3',				'&', '8',			/* undo */
	KS_EXTRA, KE_S_UP,		'k', 'u',			/* up arrow */
	KS_EXTRA, KE_S_DOWN,    'k', 'd',			/* down arrow */

	KS_EXTRA, KE_S_F1,      'k', '1',    		/* F1 */
	KS_EXTRA, KE_S_F2,      'k', '2',
	KS_EXTRA, KE_S_F3,      'k', '3',
	KS_EXTRA, KE_S_F4,      'k', '4',
	KS_EXTRA, KE_S_F5,      'k', '5',
	KS_EXTRA, KE_S_F6,      'k', '6',
	KS_EXTRA, KE_S_F7,      'k', '7',
	KS_EXTRA, KE_S_F8,      'k', '8',
	KS_EXTRA, KE_S_F9,      'k', '9',
	KS_EXTRA, KE_S_F10,     'k', ';',			/* F10 */

	KS_EXTRA, KE_S_F11,     'F', '1',
	KS_EXTRA, KE_S_F12,     'F', '2',
	KS_EXTRA, KE_S_F13,     'F', '3',
	KS_EXTRA, KE_S_F14,     'F', '4',
	KS_EXTRA, KE_S_F15,     'F', '5',
	KS_EXTRA, KE_S_F16,     'F', '6',
	KS_EXTRA, KE_S_F17,     'F', '7',
	KS_EXTRA, KE_S_F18,     'F', '8',
	KS_EXTRA, KE_S_F19,     'F', '9',
	KS_EXTRA, KE_S_F20,     'F', 'A',

	KS_EXTRA, KE_S_F21,     'F', 'B',
	KS_EXTRA, KE_S_F22,     'F', 'C',
	KS_EXTRA, KE_S_F23,     'F', 'D',
	KS_EXTRA, KE_S_F24,     'F', 'E',
	KS_EXTRA, KE_S_F25,     'F', 'F',
	KS_EXTRA, KE_S_F26,     'F', 'G',
	KS_EXTRA, KE_S_F27,     'F', 'H',
	KS_EXTRA, KE_S_F28,     'F', 'I',
	KS_EXTRA, KE_S_F29,     'F', 'J',
	KS_EXTRA, KE_S_F30,     'F', 'K',

	KS_EXTRA, KE_S_F31,     'F', 'L',
	KS_EXTRA, KE_S_F32,     'F', 'M',
	KS_EXTRA, KE_S_F33,     'F', 'N',
	KS_EXTRA, KE_S_F34,     'F', 'O',
	KS_EXTRA, KE_S_F35,     'F', 'P',

	KS_EXTRA, KE_S_TAB,     KS_EXTRA, KE_TAB,	/* TAB */
	NUL
};

static struct key_name_entry
{
	int		key;		/* Special key code or ascii value */
	char_u	*name;		/* Name of key */
} key_names_table[] =
{
	{' ',				(char_u *)"Space"},
	{TAB,				(char_u *)"Tab"},
	{K_TAB,				(char_u *)"Tab"},
	{NL,				(char_u *)"NL"},
	{NL,				(char_u *)"NewLine"},	/* Alternative name */
	{NL,				(char_u *)"LineFeed"},	/* Alternative name */
	{NL,				(char_u *)"LF"},		/* Alternative name */
	{CR,				(char_u *)"CR"},
	{CR,				(char_u *)"Return"},	/* Alternative name */
	{ESC,				(char_u *)"Esc"},
	{'|',				(char_u *)"Bar"},
	{K_UP,				(char_u *)"Up"},
	{K_DOWN,			(char_u *)"Down"},
	{K_LEFT,			(char_u *)"Left"},
	{K_RIGHT,			(char_u *)"Right"},

	{K_F1,	 			(char_u *)"F1"},
	{K_F2,	 			(char_u *)"F2"},
	{K_F3,	 			(char_u *)"F3"},
	{K_F4,	 			(char_u *)"F4"},
	{K_F5,	 			(char_u *)"F5"},
	{K_F6,	 			(char_u *)"F6"},
	{K_F7,	 			(char_u *)"F7"},
	{K_F8,	 			(char_u *)"F8"},
	{K_F9,	 			(char_u *)"F9"},
	{K_F10,				(char_u *)"F10"},

	{K_F11,				(char_u *)"F11"},
	{K_F12,				(char_u *)"F12"},
	{K_F13,				(char_u *)"F13"},
	{K_F14,				(char_u *)"F14"},
	{K_F15,				(char_u *)"F15"},
	{K_F16,				(char_u *)"F16"},
	{K_F17,				(char_u *)"F17"},
	{K_F18,				(char_u *)"F18"},
	{K_F19,				(char_u *)"F19"},
	{K_F20,				(char_u *)"F20"},

	{K_F21,				(char_u *)"F21"},
	{K_F22,				(char_u *)"F22"},
	{K_F23,				(char_u *)"F23"},
	{K_F24,				(char_u *)"F24"},
	{K_F25,				(char_u *)"F25"},
	{K_F26,				(char_u *)"F26"},
	{K_F27,				(char_u *)"F27"},
	{K_F28,				(char_u *)"F28"},
	{K_F29,				(char_u *)"F29"},
	{K_F30,				(char_u *)"F30"},

	{K_F31,				(char_u *)"F31"},
	{K_F32,				(char_u *)"F32"},
	{K_F33,				(char_u *)"F33"},
	{K_F34,				(char_u *)"F34"},
	{K_F35,				(char_u *)"F35"},

	{K_HELP,			(char_u *)"Help"},
	{K_UNDO,			(char_u *)"Undo"},
	{K_BS,				(char_u *)"BS"},
	{K_BS,				(char_u *)"BackSpace"},	/* Alternative name */
	{K_INS,				(char_u *)"Insert"},
	{K_INS,				(char_u *)"Ins"},		/* Alternative name */
	{K_DEL,				(char_u *)"Del"},
	{K_DEL,				(char_u *)"Delete"},	/* Alternative name */
	{K_HOME,			(char_u *)"Home"},
	{K_END,				(char_u *)"End"},
	{K_PAGEUP,			(char_u *)"PageUp"},
	{K_PAGEDOWN,		(char_u *)"PageDown"},
	{K_KHOME,			(char_u *)"kHome"},
	{K_KEND,			(char_u *)"kEnd"},
	{K_KPAGEUP,			(char_u *)"kPageUp"},
	{K_KPAGEDOWN,		(char_u *)"kPageDown"},

	{K_MOUSE,			(char_u *)"Mouse"},
	{K_LEFTMOUSE,		(char_u *)"LeftMouse"},
	{K_LEFTDRAG,		(char_u *)"LeftDrag"},
	{K_LEFTRELEASE,		(char_u *)"LeftRelease"},
	{K_MIDDLEMOUSE,		(char_u *)"MiddleMouse"},
	{K_MIDDLEDRAG,		(char_u *)"MiddleDrag"},
	{K_MIDDLERELEASE,	(char_u *)"MiddleRelease"},
	{K_RIGHTMOUSE,		(char_u *)"RightMouse"},
	{K_RIGHTDRAG,		(char_u *)"RightDrag"},
	{K_RIGHTRELEASE,	(char_u *)"RightRelease"},
	{K_ZERO,			(char_u *)"Nul"},
	{0,					NULL}
};

#define KEY_NAMES_TABLE_LEN (sizeof(key_names_table) / sizeof(struct key_name_entry))

#ifdef USE_MOUSE
static struct
{
	int		pseudo_code;		/* Code for pseudo mouse event */
	int		button;				/* Which mouse button is it? */
	int		is_click;			/* Is it a mouse button click event? */
	int		is_drag;			/* Is it a mouse drag event? */
} mouse_table[] =
{
	{KE_LEFTMOUSE,		MOUSE_LEFT,		TRUE,	FALSE},
	{KE_LEFTDRAG,		MOUSE_LEFT,		FALSE,	TRUE},
	{KE_LEFTRELEASE,	MOUSE_LEFT,		FALSE,	FALSE},
	{KE_MIDDLEMOUSE,	MOUSE_MIDDLE,	TRUE,	FALSE},
	{KE_MIDDLEDRAG,		MOUSE_MIDDLE,	FALSE,	TRUE},
	{KE_MIDDLERELEASE,	MOUSE_MIDDLE,	FALSE,	FALSE},
	{KE_RIGHTMOUSE,		MOUSE_RIGHT,	TRUE,	FALSE},
	{KE_RIGHTDRAG,		MOUSE_RIGHT,	FALSE,	TRUE},
	{KE_RIGHTRELEASE,	MOUSE_RIGHT,	FALSE,	FALSE},
	{KE_IGNORE,			MOUSE_RELEASE,	FALSE,	TRUE},	/* DRAG without CLICK */
	{KE_IGNORE,			MOUSE_RELEASE,	FALSE,	FALSE}, /* RELEASE without CLICK */
	{0,					0,				0,		0},
};
#endif /* USE_MOUSE */

/*
 * Return the modifier mask bit (MOD_MASK_*) which corresponds to the given
 * modifier name ('S' for Shift, 'C' for Ctrl etc).
 */
	int
name_to_mod_mask(c)
	int		c;
{
	int		i;

	for (i = 0; mod_mask_table[i].mod_mask; i++)
		if (TO_LOWER(c) == TO_LOWER(mod_mask_table[i].name))
			return mod_mask_table[i].mod_mask;
	return 0x0;
}

/*
 * Decide whether the given key code (K_*) is a shifted special
 * key (by looking at mod_mask).  If it is, then return the appropriate shifted
 * key code, otherwise just return the character as is.
 */
	int
check_shifted_spec_key(c)
	int		c;
{
	int		i;
	int		key0;
	int		key1;

	if (mod_mask & MOD_MASK_SHIFT)
	{
		if (c == TAB)			/* TAB is not in the table, K_TAB is */
			return K_S_TAB;
		key0 = KEY2TERMCAP0(c);
		key1 = KEY2TERMCAP1(c);
		for (i = 0; shifted_keys_table[i] != NUL; i += 4)
			if (key0 == shifted_keys_table[i + 2] &&
											key1 == shifted_keys_table[i + 3])
				return TERMCAP2KEY(shifted_keys_table[i],
												   shifted_keys_table[i + 1]);
	}
	return c;
}

/*
 * Decide whether the given special key is shifted or not.  If it is we
 * return OK and change it to the equivalent unshifted special key code,
 * otherwise we leave it as is and return FAIL.
 */
	int
unshift_special_key(p)
	char_u	*p;
{
	int		i;

	for (i = 0; shifted_keys_table[i]; i += 4)
		if (p[0] == shifted_keys_table[i] && p[1] == shifted_keys_table[i + 1])
		{
			p[0] = shifted_keys_table[i + 2];
			p[1] = shifted_keys_table[i + 3];
			return OK;
		}
	return FAIL;
}

/*
 * Return a string which contains the name of the given key when the given
 * modifiers are down.
 */
	char_u *
get_special_key_name(c, modifiers)
	int		c;
	int		modifiers;
{
	static char_u string[MAX_KEY_NAME_LEN + 1];

	int		i, idx;
	int		table_idx;
	char_u	*s;
	char_u	name[2];

	string[0] = '<';
	idx = 1;

	/* translate shifted keys into unshifted keys and set modifier */
	if (IS_SPECIAL(c))
	{
		name[0] = KEY2TERMCAP0(c);
		name[1] = KEY2TERMCAP1(c);
		if (unshift_special_key(&name[0]))
			modifiers |= MOD_MASK_SHIFT;
		c = TERMCAP2KEY(name[0], name[1]);
	}

	/* try to find the key in the special key table */
	table_idx = find_special_key_in_table(c);

	/*
	 * When not a known special key, and not a printable character, try to
	 * extract modifiers.
	 */
	if (table_idx < 0 && !isprintchar(c) && (c & 0x80))
	{
		c &= 0x7f;
		modifiers |= MOD_MASK_ALT;
		/* try again to find the un-modified key in the special key table */
		table_idx = find_special_key_in_table(c);
	}
	if (table_idx < 0 && !isprintchar(c) && c < ' ')
	{
		c += '@';
		modifiers |= MOD_MASK_CTRL;
	}

	/* translate the modifier into a string */
	for (i = 0; mod_mask_table[i].mod_mask; i++)
		if (modifiers & mod_mask_table[i].mod_mask)
		{
			string[idx++] = mod_mask_table[i].name;
			string[idx++] = (char_u)'-';
		}

	if (table_idx < 0)			/* unknown special key, output t_xx */
	{
		if (IS_SPECIAL(c))
		{
			string[idx++] = 't';
			string[idx++] = '_';
			string[idx++] = KEY2TERMCAP0(c);
			string[idx++] = KEY2TERMCAP1(c);
		}
		/* Not a special key, only modifiers, output directly */
		else
		{
			if (isprintchar(c))
				string[idx++] = c;
			else
			{
				s = transchar(c);
				while (*s)
					string[idx++] = *s++;
			}
		}
	}
	else				/* use name of special key */
	{
		STRCPY(string + idx, key_names_table[table_idx].name);
		idx = STRLEN(string);
	}
	string[idx++] = '>';
	string[idx] = NUL;
	return string;
}

/*
 * Try translating a <> name at (*srcp)[] to dst[].
 * Return the number of characters added to dst[].
 * srcp is advanced to after the <> name.
 * dst[] must be big enough to hold the result!
 */
	int
trans_special(srcp, dst, single)
	char_u	**srcp;
	char_u	*dst;
	int		single;				/* want a single byte code */
{
	int		dlen = 0;
	char_u	*last_dash;
	char_u	*end_of_name;
	char_u	*src;
	char_u	*bp;
	int		modifiers;
	int		bit;
	int		key;

	src = *srcp;
	if (src[0] != '<')
		return 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, or special key name.
			 */
			if (modifiers != 0 && last_dash[2] == '>')
				key = last_dash[1];
			else
				key = get_special_key_code(last_dash + 1);

			/*
			 * get_special_key_code() may return NUL for invalid
			 * special key name.
			 */
			if (key != NUL)
			{
				/*
				 * Special trick: for <S-TAB>  K_TAB is used
				 * instead of TAB (there are two codes for the
				 * same thing).
				 */
				if (key == TAB && modifiers == MOD_MASK_SHIFT)
					key = K_TAB;

				/*
				 * Special Key name with or without modifier.
				 */
				if (IS_SPECIAL(key))
				{
					/* Put the appropriate modifier in a string */
					if (modifiers != 0)
					{
						dst[dlen++] = K_SPECIAL;
						dst[dlen++] = KS_MODIFIER;
						dst[dlen++] = modifiers;
					}
					dst[dlen++] = K_SPECIAL;
					dst[dlen++] = KEY2TERMCAP0(key);
					dst[dlen++] = KEY2TERMCAP1(key);
				}

				/*
				 * Normal Key with or without modifier.
				 */
				else
				{
					if (modifiers & MOD_MASK_SHIFT)
						key = TO_UPPER(key);
					if (modifiers & MOD_MASK_CTRL)
						key &= 0x1f;
					if (modifiers & MOD_MASK_ALT)
						key |= 0x80;
					dst[dlen++] = key;
				}

				*srcp = end_of_name;
				return dlen;
			}
		}
	}
	return 0;
}

/*
 * Try to find key "c" in the special key table.
 * Return the index when found, -1 when not found.
 */
	int
find_special_key_in_table(c)
	int		c;
{
	int		i;

	for (i = 0; key_names_table[i].name != NULL; i++)
		if (c == key_names_table[i].key)
			break;
	if (key_names_table[i].name == NULL)
		i = -1;
	return i;
}

/*
 * Find the special key with the given name (the given string does not have to
 * end with NUL, the name is assumed to end before the first non-idchar).
 * If the name starts with "t_" the next two characters are interpreted as a
 * termcap name.
 * Return the key code, or 0 if not found.
 */
	int
get_special_key_code(name)
	char_u	*name;
{
	char_u	*table_name;
	char_u	string[3];
	int		i, j;

	/*
	 * If it's <t_xx> we get the code for xx from the termcap
	 */
	if (name[0] == 't' && name[1] == '_' && name[2] != NUL && name[3] != NUL)
	{
		string[0] = name[2];
		string[1] = name[3];
		string[2] = NUL;
		if (add_termcap_entry(string, FALSE) == OK)
			return TERMCAP2KEY(name[2], name[3]);
	}
	else
		for (i = 0; key_names_table[i].name != NULL; i++)
		{
			table_name = key_names_table[i].name;
			for (j = 0; isidchar(name[j]) && table_name[j] != NUL; j++)
				if (TO_LOWER(table_name[j]) != TO_LOWER(name[j]))
					break;
			if (!isidchar(name[j]) && table_name[j] == NUL)
				return key_names_table[i].key;
		}
	return 0;
}

	char_u *
get_key_name(i)
	int		i;
{
	if (i >= KEY_NAMES_TABLE_LEN)
		return NULL;
	return  key_names_table[i].name;
}

#ifdef USE_MOUSE
/*
 * Look up the given mouse code to return the relevant information in the other
 * arguments.  Return which button is down or was released.
 */
	int
get_mouse_button(code, is_click, is_drag)
	int		code;
	int		*is_click;
	int		*is_drag;
{
	int		i;

	for (i = 0; mouse_table[i].pseudo_code; i++)
		if (code == mouse_table[i].pseudo_code)
		{
			*is_click = mouse_table[i].is_click;
			*is_drag = mouse_table[i].is_drag;
			return mouse_table[i].button;
		}
	return 0;		/* Shouldn't get here */
}

/*
 * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on
 * the given information about which mouse button is down, and whether the
 * mouse was clicked, dragged or released.
 */
	int
get_pseudo_mouse_code(button, is_click, is_drag)
	int		button;		/* eg MOUSE_LEFT */
	int		is_click;
	int		is_drag;
{
	int		i;

	for (i = 0; mouse_table[i].pseudo_code; i++)
		if (button == mouse_table[i].button
			&& is_click == mouse_table[i].is_click
			&& is_drag == mouse_table[i].is_drag)
		{
			return mouse_table[i].pseudo_code;
		}
	return KE_IGNORE;		/* not recongnized, ignore it */
}
#endif /* USE_MOUSE */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.