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

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

Revision 1.1.1.1 (vendor branch), Sat Sep 7 21:40:26 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: getchar.c,v 1.1.1.1 1996/09/07 21:40:26 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.
 */

/*
 * getchar.c
 *
 * functions related with getting a character from the user/mapping/redo/...
 *
 * manipulations with redo buffer and stuff buffer
 * mappings and abbreviations
 */

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

/*
 * structure used to store one block of the stuff/redo/macro buffers
 */
struct bufblock
{
		struct bufblock *b_next;		/* pointer to next bufblock */
		char_u			b_str[1];		/* contents (actually longer) */
};

#define MINIMAL_SIZE 20 				/* minimal size for b_str */

/*
 * header used for the stuff buffer and the redo buffer
 */
struct buffheader
{
		struct bufblock bh_first;		/* first (dummy) block of list */
		struct bufblock *bh_curr;		/* bufblock for appending */
		int 			bh_index;		/* index for reading */
		int 			bh_space;		/* space in bh_curr for appending */
};

static struct buffheader stuffbuff = {{NULL, {NUL}}, NULL, 0, 0};
static struct buffheader redobuff = {{NULL, {NUL}}, NULL, 0, 0};
static struct buffheader old_redobuff = {{NULL, {NUL}}, NULL, 0, 0};
static struct buffheader recordbuff = {{NULL, {NUL}}, NULL, 0, 0};

/*
 * when block_redo is TRUE redo buffer will not be changed
 * used by edit() to repeat insertions and 'V' command for redoing
 */
static int		block_redo = FALSE;

/*
 * structure used for mapping
 */
struct mapblock
{
	struct mapblock *m_next;		/* next mapblock */
	char_u			*m_keys;		/* mapped from */
	int				 m_keylen;		/* strlen(m_keys) */
	char_u			*m_str; 		/* mapped to */
	int 			 m_mode;		/* valid mode */
	int				 m_noremap;		/* if non-zero no re-mapping for m_str */
};

static struct mapblock maplist = {NULL, NULL, 0, NULL, 0, 0};
									/* first dummy entry in maplist */

/*
 * variables used by vgetorpeek() and flush_buffers()
 *
 * typebuf[] contains all characters that are not consumed yet.
 * typebuf[typeoff] is the first valid character in typebuf[].
 * typebuf[typeoff + typelen - 1] is the last valid char.
 * typebuf[typeoff + typelen] must be NUL.
 * The part in front may contain the result of mappings, abbreviations and
 * @a commands. The length of this part is typemaplen.
 * After it are characters that come from the terminal.
 * no_abbr_cnt is the number of characters in typebuf that should not be
 * considered for abbreviations.
 * Some parts of typebuf may not be mapped. These parts are remembered in
 * noremapbuf, which is the same length as typebuf and contains TRUE for the
 * characters that are not to be remapped. noremapbuf[typeoff] is the first
 * valid flag.
 * (typebuf has been put in globals.h, because check_termcode() needs it).
 */
static char_u	*noremapbuf = NULL;       /* flags for typeahead characters */
#define TYPELEN_INIT	(3 * (MAXMAPLEN + 3))
static char_u	typebuf_init[TYPELEN_INIT];			/* initial typebuf */
static char_u	noremapbuf_init[TYPELEN_INIT];		/* initial noremapbuf */

static int		typemaplen = 0;		/* nr of mapped characters in typebuf */
static int		no_abbr_cnt = 0;	/* nr of chars without abbrev. in typebuf */
static int		last_recorded_len = 0;	/* number of last recorded chars */

static void		free_buff __ARGS((struct buffheader *));
static char_u	*get_bufcont __ARGS((struct buffheader *, int));
static void		add_buff __ARGS((struct buffheader *, char_u *));
static void		add_num_buff __ARGS((struct buffheader *, long));
static void		add_char_buff __ARGS((struct buffheader *, int));
static int		read_stuff __ARGS((int));
static void		start_stuff __ARGS((void));
static int		read_redo __ARGS((int, int));
static void		copy_redo __ARGS((int));
static void		init_typebuf __ARGS((void));
static void		gotchars __ARGS((char_u *, int));
static int		vgetorpeek __ARGS((int));
static int		inchar __ARGS((char_u *, int, long));
static void		map_free __ARGS((struct mapblock *));
static void		showmap __ARGS((struct mapblock *));

/*
 * free and clear a buffer
 */
	static void
free_buff(buf)
	struct buffheader *buf;
{
		register struct bufblock *p, *np;

		for (p = buf->bh_first.b_next; p != NULL; p = np)
		{
				np = p->b_next;
				vim_free(p);
		}
		buf->bh_first.b_next = NULL;
}

/*
 * return the contents of a buffer as a single string
 */
	static char_u *
get_bufcont(buffer, dozero)
	struct buffheader	*buffer;
	int					dozero;		/* count == zero is not an error */
{
	long_u			count = 0;
	char_u			*p = NULL;
	char_u			*p2;
	char_u			*str;
	struct bufblock	*bp;

/* compute the total length of the string */
	for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next)
		count += STRLEN(bp->b_str);

	if ((count || dozero) && (p = lalloc(count + 1, TRUE)) != NULL)
	{
		p2 = p;
		for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next)
			for (str = bp->b_str; *str; )
				*p2++ = *str++;
		*p2 = NUL;
	}
	return (p);
}

/*
 * return the contents of the record buffer as a single string
 *	and clear the record buffer
 */
	char_u *
get_recorded()
{
	char_u *p;
	size_t	len;

	p = get_bufcont(&recordbuff, TRUE);
	free_buff(&recordbuff);
	/*
	 * Remove the characters that were added the last time, these must be the
	 * (possibly mapped) characters that stopped recording.
	 */
	len = STRLEN(p);
	if ((int)len >= last_recorded_len)
		p[len - last_recorded_len] = NUL;
	return (p);
}

/*
 * return the contents of the redo buffer as a single string
 */
	char_u *
get_inserted()
{
		return(get_bufcont(&redobuff, FALSE));
}

/*
 * add string "s" after the current block of buffer "buf"
 */
	static void
add_buff(buf, s)
	register struct buffheader	*buf;
	char_u						*s;
{
	struct bufblock *p;
	long_u 			n;
	long_u 			len;

	if ((n = STRLEN(s)) == 0)				/* don't add empty strings */
		return;

	if (buf->bh_first.b_next == NULL)		/* first add to list */
	{
		buf->bh_space = 0;
		buf->bh_curr = &(buf->bh_first);
	}
	else if (buf->bh_curr == NULL)			/* buffer has already been read */
	{
		EMSG("Add to read buffer");
		return;
	}
	else if (buf->bh_index != 0)
		STRCPY(buf->bh_first.b_next->b_str,
								 buf->bh_first.b_next->b_str + buf->bh_index);
	buf->bh_index = 0;

	if (buf->bh_space >= (int)n)
	{
		strcat((char *)buf->bh_curr->b_str, (char *)s);
		buf->bh_space -= n;
	}
	else
	{
		if (n < MINIMAL_SIZE)
			len = MINIMAL_SIZE;
		else
			len = n;
		p = (struct bufblock *)lalloc((long_u)(sizeof(struct bufblock) + len), TRUE);
		if (p == NULL)
			return; /* no space, just forget it */
		buf->bh_space = len - n;
		STRCPY(p->b_str, s);

		p->b_next = buf->bh_curr->b_next;
		buf->bh_curr->b_next = p;
		buf->bh_curr = p;
	}
	return;
}

	static void
add_num_buff(buf, n)
	struct buffheader *buf;
	long 			  n;
{
		char_u	number[32];

		sprintf((char *)number, "%ld", n);
		add_buff(buf, number);
}

	static void
add_char_buff(buf, c)
	struct buffheader *buf;
	int 			  c;
{
	char_u	temp[4];

	/*
	 * translate special key code into three byte sequence
	 */
	if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL)
	{
		temp[0] = K_SPECIAL;
		temp[1] = K_SECOND(c);
		temp[2] = K_THIRD(c);
		temp[3] = NUL;
	}
	else
	{
		temp[0] = c;
		temp[1] = NUL;
	}
	add_buff(buf, temp);
}

/*
 * get one character from the stuff buffer
 * If advance == TRUE go to the next char.
 */
	static int
read_stuff(advance)
	int			advance;
{
	register char_u c;
	register struct bufblock *curr;


	if (stuffbuff.bh_first.b_next == NULL)	/* buffer is empty */
		return NUL;

	curr = stuffbuff.bh_first.b_next;
	c = curr->b_str[stuffbuff.bh_index];

	if (advance)
	{
		if (curr->b_str[++stuffbuff.bh_index] == NUL)
		{
			stuffbuff.bh_first.b_next = curr->b_next;
			vim_free(curr);
			stuffbuff.bh_index = 0;
		}
	}
	return c;
}

/*
 * prepare stuff buffer for reading (if it contains something)
 */
	static void
start_stuff()
{
	if (stuffbuff.bh_first.b_next != NULL)
	{
		stuffbuff.bh_curr = &(stuffbuff.bh_first);
		stuffbuff.bh_space = 0;
	}
}

/*
 * check if the stuff buffer is empty
 */
	int
stuff_empty()
{
	return (stuffbuff.bh_first.b_next == NULL);
}

/*
 * Remove the contents of the stuff buffer and the mapped characters in the
 * typeahead buffer (used in case of an error). If 'typeahead' is true,
 * flush all typeahead characters (used when interrupted by a CTRL-C).
 */
	void
flush_buffers(typeahead)
	int typeahead;
{
	init_typebuf();

	start_stuff();
	while (read_stuff(TRUE) != NUL)
		;

	if (typeahead)			/* remove all typeahead */
	{
			/*
			 * We have to get all characters, because we may delete the first
			 * part of an escape sequence.
			 * In an xterm we get one char at a time and we have to get them
			 * all.
			 */
		while (inchar(typebuf, MAXMAPLEN, 10L))	
			;
		typeoff = MAXMAPLEN;
		typelen = 0;
	}
	else					/* remove mapped characters only */
	{
		typeoff += typemaplen;
		typelen -= typemaplen;
	}
	typemaplen = 0;
	no_abbr_cnt = 0;
}

/*
 * The previous contents of the redo buffer is kept in old_redobuffer.
 * This is used for the CTRL-O <.> command in insert mode.
 */
	void
ResetRedobuff()
{
	if (!block_redo)
	{
		free_buff(&old_redobuff);
		old_redobuff = redobuff;
		redobuff.bh_first.b_next = NULL;
	}
}

	void
AppendToRedobuff(s)
	char_u		   *s;
{
	if (!block_redo)
		add_buff(&redobuff, s);
}

	void
AppendCharToRedobuff(c)
	int			   c;
{
	if (!block_redo)
		add_char_buff(&redobuff, c);
}

	void
AppendNumberToRedobuff(n)
	long 			n;
{
	if (!block_redo)
		add_num_buff(&redobuff, n);
}

	void
stuffReadbuff(s)
	char_u		   *s;
{
	add_buff(&stuffbuff, s);
}

	void
stuffcharReadbuff(c)
	int			   c;
{
	add_char_buff(&stuffbuff, c);
}

	void
stuffnumReadbuff(n)
	long	n;
{
	add_num_buff(&stuffbuff, n);
}

/*
 * Read a character from the redo buffer.
 * The redo buffer is left as it is.
 * if init is TRUE, prepare for redo, return FAIL if nothing to redo, OK
 * otherwise
 * if old is TRUE, use old_redobuff instead of redobuff
 */
	static int
read_redo(init, old_redo)
	int			init;
	int			old_redo;
{
	static struct bufblock	*bp;
	static char_u			*p;
	int						c;

	if (init)
	{
		if (old_redo)
			bp = old_redobuff.bh_first.b_next;
		else
			bp = redobuff.bh_first.b_next;
		if (bp == NULL)
			return FAIL;
		p = bp->b_str;
		return OK;
	}
	if ((c = *p) != NUL)
	{
		if (c == K_SPECIAL)
		{
			c = TO_SPECIAL(p[1], p[2]);
			p += 2;
		}
		if (*++p == NUL && bp->b_next != NULL)
		{
			bp = bp->b_next;
			p = bp->b_str;
		}
	}
	return c;
}

/*
 * copy the rest of the redo buffer into the stuff buffer (could be done faster)
 * if old_redo is TRUE, use old_redobuff instead of redobuff
 */
	static void
copy_redo(old_redo)
	int		old_redo;
{
	register int c;

	while ((c = read_redo(FALSE, old_redo)) != NUL)
		stuffcharReadbuff(c);
}

/*
 * Stuff the redo buffer into the stuffbuff.
 * Insert the redo count into the command.
 * If 'old_redo' is TRUE, the last but one command is repeated
 * instead of the last command (inserting text). This is used for
 * CTRL-O <.> in insert mode
 *
 * return FAIL for failure, OK otherwise
 */
	int
start_redo(count, old_redo)
	long	count;
	int		old_redo;
{
	register int c;

	if (read_redo(TRUE, old_redo) == FAIL)	/* init the pointers; return if nothing to redo */
		return FAIL;

	c = read_redo(FALSE, old_redo);

/* copy the buffer name, if present */
	if (c == '"')
	{
		add_buff(&stuffbuff, (char_u *)"\"");
		c = read_redo(FALSE, old_redo);

/* if a numbered buffer is used, increment the number */
		if (c >= '1' && c < '9')
			++c;
		add_char_buff(&stuffbuff, c);
		c = read_redo(FALSE, old_redo);
	}

	if (c == 'v')	/* redo Visual */
	{
		VIsual = curwin->w_cursor;
		VIsual_active = TRUE;
		redo_VIsual_busy = TRUE;
		c = read_redo(FALSE, old_redo);
	}

/* try to enter the count (in place of a previous count) */
	if (count)
	{
		while (isdigit(c))		/* skip "old" count */
			c = read_redo(FALSE, old_redo);
		add_num_buff(&stuffbuff, count);
	}

/* copy from the redo buffer into the stuff buffer */
	add_char_buff(&stuffbuff, c);
	copy_redo(old_redo);
	return OK;
}

/*
 * Repeat the last insert (R, o, O, a, A, i or I command) by stuffing
 * the redo buffer into the stuffbuff.
 * return FAIL for failure, OK otherwise
 */
	int
start_redo_ins()
{
	register int c;

	if (read_redo(TRUE, FALSE) == FAIL)
		return FAIL;
	start_stuff();

/* skip the count and the command character */
	while ((c = read_redo(FALSE, FALSE)) != NUL)
	{
		c = TO_UPPER(c);
		if (vim_strchr((char_u *)"AIRO", c) != NULL)
		{
			if (c == 'O')
				stuffReadbuff(NL_STR);
			break;
		}
	}

/* copy the typed text from the redo buffer into the stuff buffer */
	copy_redo(FALSE);
	block_redo = TRUE;
	return OK;
}

	void
set_redo_ins()
{
	block_redo = TRUE;
}

	void
stop_redo_ins()
{
	block_redo = FALSE;
}

/*
 * Initialize typebuf to point to typebuf_init.
 * Alloc() cannot be used here: In out-of-memory situations it would
 * be impossible to type anything.
 */
	static void
init_typebuf()
{
	if (typebuf == NULL)
	{
		typebuf = typebuf_init;
		noremapbuf = noremapbuf_init;
		typebuflen = TYPELEN_INIT;
		typelen = 0;
		typeoff = 0;
	}
}

/*
 * insert a string in position 'offset' in the typeahead buffer (for "@r"
 * and ":normal" command, vgetorpeek() and check_termcode())
 *
 * If noremap is 0, new string can be mapped again.
 * If noremap is -1, new string cannot be mapped again.
 * If noremap is >0, that many characters of the new string cannot be mapped.
 *
 * If nottyped is TRUE, the string does not return KeyTyped (don't use when
 * offset is non-zero!).
 *
 * return FAIL for failure, OK otherwise
 */
	int
ins_typebuf(str, noremap, offset, nottyped)
	char_u	*str;
	int		noremap;
	int		offset;
	int		nottyped;
{
	register char_u	*s1, *s2;
	register int	newlen;
	register int	addlen;
	register int	i;
	register int	newoff;

	init_typebuf();

	addlen = STRLEN(str);
	/*
	 * Easy case: there is room in front of typebuf[typeoff]
	 */
	if (offset == 0 && addlen <= typeoff)
	{
		typeoff -= addlen;
		vim_memmove(typebuf + typeoff, str, (size_t)addlen);
	}
	/*
	 * Need to allocate new buffer.
	 * In typebuf there must always be room for MAXMAPLEN + 4 characters.
	 * We add some extra room to avoid having to allocate too often.
	 */
	else
	{
		newoff = MAXMAPLEN + 4;
		newlen = typelen + addlen + newoff + 2 * (MAXMAPLEN + 4);
		if (newlen < 0)				/* string is getting too long */
		{
			emsg(e_toocompl);		/* also calls flush_buffers */
			setcursor();
			return FAIL;
		}
		s1 = alloc(newlen);
		if (s1 == NULL)				/* out of memory */
			return FAIL;
		s2 = alloc(newlen);
		if (s2 == NULL)				/* out of memory */
		{
			vim_free(s1);
			return FAIL;
		}
		typebuflen = newlen;

				/* copy the old chars, before the insertion point */
		vim_memmove(s1 + newoff, typebuf + typeoff, (size_t)offset);
				/* copy the new chars */
		vim_memmove(s1 + newoff + offset, str, (size_t)addlen);
				/* copy the old chars, after the insertion point, including
				 * the  NUL at the end */
		vim_memmove(s1 + newoff + offset + addlen, typebuf + typeoff + offset,
											  (size_t)(typelen - offset + 1));
		if (typebuf != typebuf_init)
			vim_free(typebuf);
		typebuf = s1;

		vim_memmove(s2 + newoff, noremapbuf + typeoff, (size_t)offset);
		vim_memmove(s2 + newoff + offset + addlen,
				   noremapbuf + typeoff + offset, (size_t)(typelen - offset));
		if (noremapbuf != noremapbuf_init)
			vim_free(noremapbuf);
		noremapbuf = s2;

		typeoff = newoff;
	}
	typelen += addlen;

	/*
	 * Adjust noremapbuf[] for the new characters:
	 * If noremap  < 0: all the new characters are flagged not remappable
	 * If noremap == 0: all the new characters are flagged mappable
	 * If noremap  > 0: 'noremap' characters are flagged not remappable, the
	 *					rest mappable
	 */
	if (noremap < 0)		/* length not specified */
		noremap = addlen;
	for (i = 0; i < addlen; ++i)
		noremapbuf[typeoff + i + offset] = (noremap-- > 0);

					/* this is only correct for offset == 0! */
	if (nottyped)						/* the inserted string is not typed */
		typemaplen += addlen;
	if (no_abbr_cnt && offset == 0)		/* and not used for abbreviations */
		no_abbr_cnt += addlen;

	return OK;
}

/*
 * Return TRUE if there are no characters in the typeahead buffer that have
 * not been typed (result from a mapping or come from ":normal").
 */
	int
typebuf_typed()
{
	return typemaplen == 0;
}

/*
 * remove "len" characters from typebuf[typeoff + offset]
 */
	void
del_typebuf(len, offset)
	int	len;
	int	offset;
{
	int		i;

	typelen -= len;
	/*
	 * Easy case: Just increase typeoff.
	 */
	if (offset == 0 && typebuflen - (typeoff + len) >= MAXMAPLEN + 3)
		typeoff += len;
	/*
	 * Have to move the characters in typebuf[] and noremapbuf[]
	 */
	else
	{
		i = typeoff + offset;
		/*
		 * Leave some extra room at the end to avoid reallocation.
		 */
		if (typeoff > MAXMAPLEN)
		{
			vim_memmove(typebuf + MAXMAPLEN, typebuf + typeoff, (size_t)offset);
			vim_memmove(noremapbuf + MAXMAPLEN, noremapbuf + typeoff,
															  (size_t)offset);
			typeoff = MAXMAPLEN;
		}
			/* adjust typebuf (include the NUL at the end) */
		vim_memmove(typebuf + typeoff + offset, typebuf + i + len,
											  (size_t)(typelen - offset + 1));
			/* adjust noremapbuf[] */
		vim_memmove(noremapbuf + typeoff + offset, noremapbuf + i + len,
												  (size_t)(typelen - offset));
	}

	if (typemaplen > offset)			/* adjust typemaplen */
	{
		if (typemaplen < offset + len)
			typemaplen = offset;
		else
			typemaplen -= len;
	}
	if (no_abbr_cnt > offset)			/* adjust no_abbr_cnt */
	{
		if (no_abbr_cnt < offset + len)
			no_abbr_cnt = offset;
		else
			no_abbr_cnt -= len;
	}
}

/*
 * Write typed characters to script file.
 * If recording is on put the character in the recordbuffer.
 */
	static void
gotchars(s, len)
	char_u	*s;
	int		len;
{
	int		c;
	char_u	buf[2];

	/* remember how many chars were last recorded */
	if (Recording)
		last_recorded_len += len;

	buf[1] = NUL;
	while (len--)
	{
		c = *s++;
		updatescript(c);

		if (Recording)
		{
			buf[0] = c;
			add_buff(&recordbuff, buf);
		}
	}

	/*
	 * Do not sync in insert mode, unless cursor key has been used.
	 * Also don't sync while reading a script file.
	 */
	if ((!(State & (INSERT + CMDLINE)) || arrow_used) &&
												  scriptin[curscript] == NULL)
		u_sync();
}

/*
 * open new script file for ":so!" command
 * return OK on success, FAIL on error
 */
	int
openscript(name)
	char_u *name;
{
	int oldcurscript;

	if (curscript + 1 == NSCRIPT)
	{
		emsg(e_nesting);
		return FAIL;
	}
	else
	{
		if (scriptin[curscript] != NULL)	/* already reading script */
			++curscript;
									/* use NameBuff for expanded name */
		expand_env(name, NameBuff, MAXPATHL);
		if ((scriptin[curscript] = fopen((char *)NameBuff, READBIN)) == NULL)
		{
			emsg2(e_notopen, name);
			if (curscript)
				--curscript;
			return FAIL;
		}
		/*
		 * With command ":g/pat/so! file" we have to execute the
		 * commands from the file now.
		 */
		if (global_busy)
		{
			State = NORMAL;
			oldcurscript = curscript;
			do
			{
				normal();
				vpeekc();			/* check for end of file */
			}
			while (scriptin[oldcurscript]);
			State = CMDLINE;
		}
	}
	return OK;
}

/*
 * updatescipt() is called when a character can be written into the script file
 * or when we have waited some time for a character (c == 0)
 *
 * All the changed memfiles are synced if c == 0 or when the number of typed
 * characters reaches 'updatecount' and 'updatecount' is non-zero.
 */
	void
updatescript(c)
	int c;
{
	static int		count = 0;

	if (c && scriptout)
		putc(c, scriptout);
	if (c == 0 || (p_uc > 0 && ++count >= p_uc))
	{
		ml_sync_all(c == 0, TRUE);
		count = 0;
	}
}

#define K_NEEDMORET -1			/* keylen value for incomplete key-code */
#define M_NEEDMORET -2			/* keylen value for incomplete mapping */

static int old_char = -1;		/* ungotten character */

	int
vgetc()
{
	int		c, c2;

	mod_mask = 0x0;
	last_recorded_len = 0;
	for (;;)					/* this is done twice if there are modifiers */
	{
		if (mod_mask)			/* no mapping after modifier has been read */
		{
			++no_mapping;
			++allow_keys;
		}
		c = vgetorpeek(TRUE);
		if (mod_mask)
		{
			--no_mapping;
			--allow_keys;
		}

		/* Get two extra bytes for special keys */
		if (c == K_SPECIAL)
		{
			++no_mapping;
			c2 = vgetorpeek(TRUE);		/* no mapping for these chars */
			c = vgetorpeek(TRUE);
			--no_mapping;
			if (c2 == KS_MODIFIER)
			{
				mod_mask = c;
				continue;
			}
			c = TO_SPECIAL(c2, c);
		}
#ifdef MSDOS
		/*
		 * If K_NUL was typed, it is replaced by K_NUL, 3 in mch_inchar().
		 * Delete the 3 here.
		 */
		else if (c == K_NUL && vpeekc() == 3)
			(void)vgetorpeek(TRUE);
#endif

		return check_shifted_spec_key(c);
	}
}

	int
vpeekc()
{
	return (vgetorpeek(FALSE));
}

/*
 * Call vpeekc() without causing anything to be mapped.
 * Return TRUE if a character is available, FALSE otherwise.
 */
	int
char_avail()
{
	int		retval;

	++no_mapping;
	retval = vgetorpeek(FALSE);
	--no_mapping;
	return (retval != NUL);
}

	void
vungetc(c)		/* unget one character (can only be done once!) */
	int		c;
{
	old_char = c;
}

/*
 * get a character: 1. from a previously ungotten character
 *					2. from the stuffbuffer
 *					3. from the typeahead buffer
 *					4. from the user
 *
 * if "advance" is TRUE (vgetc()):
 *		really get the character.
 *		KeyTyped is set to TRUE in the case the user typed the key.
 *		KeyStuffed is TRUE if the character comes from the stuff buffer.
 * if "advance" is FALSE (vpeekc()):
 *		just look whether there is a character available.
 */

	static int
vgetorpeek(advance)
	int		advance;
{
	register int	c, c1;
	int				keylen = 0;				/* init for gcc */
#ifdef AMIGA
	char_u			*s;
#endif
	register struct mapblock *mp;
	int				timedout = FALSE;		/* waited for more than 1 second
												for mapping to complete */
	int				mapdepth = 0;			/* check for recursive mapping */
	int				mode_deleted = FALSE;	/* set when mode has been deleted */
	int				local_State;
	register int	mlen;
	int				max_mlen;
	int				i;
#ifdef USE_GUI
	int				idx;
#endif

	/*
	 * VISUAL state is never set, it is used only here, therefore a check is
	 * made if NORMAL state is actually VISUAL state.
	 */
	local_State = State;
	if ((State & NORMAL) && VIsual_active)
		local_State = VISUAL;

/*
 * get a character: 1. from a previously ungotten character
 */
	if (old_char >= 0)
	{
		c = old_char;
		if (advance)
			old_char = -1;
		return c;
	}

	if (advance)
		KeyStuffed = FALSE;

	init_typebuf();
	start_stuff();
	if (advance && typemaplen == 0)
		Exec_reg = FALSE;
	do
	{
/*
 * get a character: 2. from the stuffbuffer
 */
		c = read_stuff(advance);
		if (c != NUL && !got_int)
		{
			if (advance)
			{
				KeyTyped = FALSE;
				KeyStuffed = TRUE;
			}
			if (no_abbr_cnt == 0)
				no_abbr_cnt = 1;		/* no abbreviations now */
		}
		else
		{
			/*
			 * Loop until we either find a matching mapped key, or we
			 * are sure that it is not a mapped key.
			 * If a mapped key sequence is found we go back to the start to
			 * try re-mapping.
			 */

			for (;;)
			{
				mch_breakcheck();			/* check for CTRL-C */
				if (got_int)
				{
					c = inchar(typebuf, MAXMAPLEN, 0L);	/* flush all input */
					/*
					 * If inchar returns TRUE (script file was active) or we are
					 * inside a mapping, get out of insert mode.
					 * Otherwise we behave like having gotten a CTRL-C.
					 * As a result typing CTRL-C in insert mode will
					 * really insert a CTRL-C.
					 */
					if ((c || typemaplen) && (State & (INSERT + CMDLINE)))
						c = ESC;
					else
						c = Ctrl('C');
					flush_buffers(TRUE);		/* flush all typeahead */
					break;
				}
				else if (typelen > 0)	/* check for a mappable key sequence */
				{
					/*
					 * walk through the maplist until we find an
					 * entry that matches.
					 *
					 * Don't look for mappings if:
					 * - timed out
					 * - no_mapping set: mapping disabled (e.g. for CTRL-V)
					 * - typebuf[typeoff] should not be remapped
					 * - in insert or cmdline mode and 'paste' option set
					 * - waiting for "hit return to continue" and CR or SPACE
					 *   typed
					 * - waiting for a char with --more--
					 * - in Ctrl-X mode, and we get a valid char for that mode
					 */
					mp = NULL;
					max_mlen = 0;
					if (!timedout && no_mapping == 0 && (typemaplen == 0 ||
								(p_remap && noremapbuf[typeoff] == FALSE))
							&& !(p_paste && (State & (INSERT + CMDLINE)))
							&& !(State == HITRETURN && (typebuf[typeoff] == CR
								|| typebuf[typeoff] == ' '))
							&& State != ASKMORE
#ifdef INSERT_EXPAND
							&& !(ctrl_x_mode && is_ctrl_x_key(typebuf[typeoff]))
#endif
							)
					{
						c1 = typebuf[typeoff];
#ifdef HAVE_LANGMAP
						LANGMAP_ADJUST(c1, TRUE);
#endif
						for (mp = maplist.m_next; mp; mp = mp->m_next)
						{
							/*
							 * Only consider an entry if
							 * - the first character matches and
							 * - it is not an abbreviation and
							 * - it is for the current state
							 */
							if (		mp->m_keys[0] == c1 &&
										!(mp->m_mode & ABBREV) &&
										(mp->m_mode & local_State))
							{
								int		n;
#ifdef HAVE_LANGMAP
								int		c2;
#endif

									/* find the match length of this mapping */
								for (mlen = 1; mlen < typelen; ++mlen)
								{
#ifdef HAVE_LANGMAP
									c2 = typebuf[typeoff + mlen];
									LANGMAP_ADJUST(c2, TRUE);
									if (mp->m_keys[mlen] != c2)
#else
									if (mp->m_keys[mlen] !=
													typebuf[typeoff + mlen])
#endif
										break;
								}

									/* if one of the typed keys cannot be
									 * remapped, skip the entry */
								for (n = 0; n < mlen; ++n)
									if (noremapbuf[typeoff + n] == TRUE)
										break;
								if (n != mlen)
									continue;

									/* (partly) match found */
								keylen = mp->m_keylen;
								if (mlen == (keylen > typelen ?
													typelen : keylen))
								{
										/* partial match, need more chars */
									if (keylen > typelen)
										keylen = M_NEEDMORET;
									break;	
								}
									/* no match, may have to check for
									 * termcode at next character */
								if (max_mlen < mlen)
									max_mlen = mlen;
							}
						}
					}
					if (mp == NULL)					/* no match found */
					{
							/*
							 * check if we have a terminal code, when
							 *	mapping is allowed,
							 *  keys have not been mapped,
							 *	and not an ESC sequence, not in insert mode or
							 *		p_ek is on,
							 *	and when not timed out,
							 */
						if ((no_mapping == 0 || allow_keys != 0) &&
														   (typemaplen == 0 ||
								(p_remap && noremapbuf[typeoff] == FALSE)) &&
											!timedout)
							keylen = check_termcode(max_mlen + 1);
						else
							keylen = 0;
						if (keylen == 0)		/* no matching terminal code */
						{
#ifdef AMIGA					/* check for window bounds report */
							if (typemaplen == 0 &&
											(typebuf[typeoff] & 0xff) == CSI)
							{
								for (s = typebuf + typeoff + 1;
										s < typebuf + typeoff + typelen &&
										(isdigit(*s) || *s == ';' || *s == ' ');
										++s)
									;
								if (*s == 'r' || *s == '|')	/* found one */
								{
									del_typebuf((int)(s + 1 -
													   (typebuf + typeoff)), 0);
										/* get size and redraw screen */
									set_winsize(0, 0, FALSE);
									continue;
								}
								if (*s == NUL)		/* need more characters */
									keylen = K_NEEDMORET;
							}
							if (keylen >= 0)
#endif
							{
/*
 * get a character: 3. from the typeahead buffer
 */
								c = typebuf[typeoff] & 255;
								if (advance)	/* remove chars from typebuf */
								{
									if (typemaplen)
										KeyTyped = FALSE;
									else
									{
										KeyTyped = TRUE;
										/* write char to script file(s) */
										gotchars(typebuf + typeoff, 1);
									}
									del_typebuf(1, 0);
								}
								break;		/* got character, break for loop */
							}
						}
						if (keylen > 0)		/* full matching terminal code */
						{
#ifdef USE_GUI
							if (typebuf[typeoff] == K_SPECIAL &&
											  typebuf[typeoff + 1] == KS_MENU)
							{
								/*
								 * Using a menu causes a break in undo!
								 */
								u_sync();
								del_typebuf(3, 0);
								idx = gui_get_menu_index(current_menu,
																 local_State);
								if (idx != MENU_INDEX_INVALID)
								{
									ins_typebuf(current_menu->strings[idx],
										current_menu->noremap[idx] ? -1 : 0,
										0, TRUE);
								}
							}
#endif /* USE_GUI */
							continue;	/* try mapping again */
						}

						/* partial match: get some more characters */
						keylen = K_NEEDMORET;
					}
						/* complete match */
					if (keylen >= 0 && keylen <= typelen)
					{
										/* write chars to script file(s) */
						if (keylen > typemaplen)
							gotchars(typebuf + typeoff + typemaplen,
														keylen - typemaplen);

						del_typebuf(keylen, 0);	/* remove the mapped keys */

						/*
						 * Put the replacement string in front of mapstr.
						 * The depth check catches ":map x y" and ":map y x".
						 */
						if (++mapdepth >= p_mmd)
						{
							EMSG("recursive mapping");
							if (State == CMDLINE)
								redrawcmdline();
							else
								setcursor();
							flush_buffers(FALSE);
							mapdepth = 0;		/* for next one */
							c = -1;
							break;
						}
						/*
						 * Insert the 'to' part in the typebuf.
						 * If 'from' field is the same as the start of the
						 * 'to' field, don't remap this part.
						 * If m_noremap is set, don't remap the whole 'to'
						 * part.
						 */
						if (ins_typebuf(mp->m_str, mp->m_noremap ? -1 :
												  STRNCMP(mp->m_str, mp->m_keys,
												   (size_t)keylen) ? 0 : keylen,
															   0, TRUE) == FAIL)
						{
							c = -1;
							break;
						}
						continue;
					}
				}
				/*
				 * special case: if we get an <ESC> in insert mode and there
				 * are no more characters at once, we pretend to go out of
				 * insert mode.  This prevents the one second delay after
				 * typing an <ESC>.  If we get something after all, we may
				 * have to redisplay the mode. That the cursor is in the wrong
				 * place does not matter.
				 */
				c = 0;
				if (advance && typelen == 1 && typebuf[typeoff] == ESC &&
						 !no_mapping && typemaplen == 0 && (State & INSERT) &&
					   (p_timeout || (keylen == K_NEEDMORET && p_ttimeout)) &&
						(c = inchar(typebuf + typeoff + typelen, 3, 0L)) == 0)
				{
					if (p_smd)
					{
						delmode();
						mode_deleted = TRUE;
					}
					if (curwin->w_cursor.col != 0)	/* move cursor one left if
														possible */
					{
						if (curwin->w_col)
						{
							if (did_ai)
							{
								/*
								 * We are expecting to truncate the trailing
								 * white-space, so find the last non-white
								 * character -- webb
								 */
								colnr_t		col, vcol;
								char_u		*ptr;

								col = vcol = curwin->w_col = 0;
								ptr = ml_get_curline();
								while (col < curwin->w_cursor.col)
								{
									if (!vim_iswhite(ptr[col]))
										curwin->w_col = vcol;
									vcol += lbr_chartabsize(ptr + col,
															   (colnr_t)vcol);
									++col;
								}
  								if (curwin->w_p_nu)
									curwin->w_col += 8;
							}
							else
								--curwin->w_col;
						}
						else if (curwin->w_p_wrap && curwin->w_row)
						{
								--curwin->w_row;
								curwin->w_col = Columns - 1;
						}
					}
					setcursor();
					flushbuf();
				}
				typelen += c;
													/* buffer full, don't map */
				if (typelen >= typemaplen + MAXMAPLEN)
				{
					timedout = TRUE;
					continue;
				}
/*
 * get a character: 4. from the user
 */
				/*
				 * If we have a partial match (and are going to wait for more
				 * input from the user), show the partially matched characters
				 * to the user with showcmd -- webb.
				 */
				i = 0;
				if (typelen > 0 && (State & (NORMAL | INSERT)) && advance)
				{
					push_showcmd();
					while (i < typelen)
						(void)add_to_showcmd(typebuf[typeoff + i++], TRUE);
				}

				c = inchar(typebuf + typeoff + typelen,
						typemaplen + MAXMAPLEN - typelen,
						!advance
							? 0
							: ((typelen == 0 || !(p_timeout || (p_ttimeout &&
										keylen == K_NEEDMORET)))
									? -1L
									: ((keylen == K_NEEDMORET && p_ttm >= 0)
											? p_ttm
											: p_tm)));

				if (i)
					pop_showcmd();

				if (c <= NUL)		/* no character available */
				{
					if (!advance)
						break;
					if (typelen)				/* timed out */
					{
						timedout = TRUE;
						continue;
					}
				}
				else
				{			/* allow mapping for just typed characters */
					while (typebuf[typeoff + typelen] != NUL)
						noremapbuf[typeoff + typelen++] = FALSE;
				}
			}		/* for (;;) */
		}		/* if (!character from stuffbuf) */

						/* if advance is FALSE don't loop on NULs */
	} while (c < 0 || (advance && c == NUL));

	/*
	 * The "INSERT" message is taken care of here:
	 *   if we return an ESC to exit insert mode, the message is deleted
	 *   if we don't return an ESC but deleted the message before, redisplay it
	 */
	if (p_smd && (State & INSERT))
	{
		if (c == ESC && !mode_deleted && !no_mapping)
			delmode();
		else if (c != ESC && mode_deleted)
			showmode();
	}

	return c;
}

/*
 * inchar() - get one character from
 *		1. a scriptfile
 *		2. the keyboard
 *
 *  As much characters as we can get (upto 'maxlen') are put in buf and
 *  NUL terminated (buffer length must be 'maxlen' + 1).
 *  Minimum for 'maxlen' is 3!!!!
 *
 *	If we got an interrupt all input is read until none is available.
 *
 *  If wait_time == 0  there is no waiting for the char.
 *  If wait_time == n  we wait for n msec for a character to arrive.
 *  If wait_time == -1 we wait forever for a character to arrive.
 *
 *  Return the number of obtained characters.
 */

	static int
inchar(buf, maxlen, wait_time)
	char_u	*buf;
	int		maxlen;
	long	wait_time;						/* milli seconds */
{
	int				len = 0;			/* init for GCC */
	int				retesc = FALSE;		/* return ESC with gotint */
	register int 	c;
	register int	i;

	if (wait_time == -1L || wait_time > 100L)  /* flush output before waiting */
	{
		cursor_on();
		flushbuf();
	}
	/*
	 * Don't reset these when at the hit-return prompt, otherwise a endless
	 * recursive loop may result (write error in swapfile, hit-return, timeout
	 * on char wait, flush swapfile, write error....).
	 */
	if (State != HITRETURN)
	{
		did_outofmem_msg = FALSE;	/* display out of memory message (again) */
		did_swapwrite_msg = FALSE;	/* display swap file write error again */
	}
	undo_off = FALSE;			/* restart undo now */

/*
 * first try script file
 *	If interrupted: Stop reading script files.
 */
	c = -1;
	while (scriptin[curscript] != NULL && c < 0)
	{
		if (got_int || (c = getc(scriptin[curscript])) < 0)	/* reached EOF */
		{
				/* when reading script file is interrupted, return an ESC to
									get back to normal mode */
			if (got_int)
				retesc = TRUE;
			fclose(scriptin[curscript]);
			scriptin[curscript] = NULL;
			if (curscript > 0)
				--curscript;
		}
		else
		{
			buf[0] = c;
			len = 1;
		}
	}

	if (c < 0)			/* did not get a character from script */
	{
	/*
	 * If we got an interrupt, skip all previously typed characters and
	 * return TRUE if quit reading script file.
	 */
		if (got_int)			/* skip typed characters */
		{
			while (mch_inchar(buf, maxlen, 0L))
				;
			return retesc;
		}
			/* fill up to a third of the buffer, because each character may be
			 * tripled below */
		len = mch_inchar(buf, maxlen / 3, wait_time);
	}

	/*
	 * Two characters are special: NUL and K_SPECIAL.
	 * Replace       NUL by K_SPECIAL KS_ZERO	 K_FILLER
	 * Replace K_SPECIAL by K_SPECIAL KS_SPECIAL K_FILLER
	 * Don't replace K_SPECIAL when reading a script file.
	 */
	for (i = len; --i >= 0; ++buf)
	{
		if (buf[0] == NUL || (buf[0] == K_SPECIAL && c < 0))
		{
			vim_memmove(buf + 3, buf + 1, (size_t)i);
			buf[2] = K_THIRD(buf[0]);
			buf[1] = K_SECOND(buf[0]);
			buf[0] = K_SPECIAL;
			buf += 2;
			len += 2;
		}
	}
	*buf = NUL;								/* add trailing NUL */
	return len;
}

/*
 * map[!]					: show all key mappings
 * map[!] {lhs}				: show key mapping for {lhs}
 * map[!] {lhs} {rhs}		: set key mapping for {lhs} to {rhs}
 * noremap[!] {lhs} {rhs}	: same, but no remapping for {rhs}
 * unmap[!] {lhs}			: remove key mapping for {lhs}
 * abbr						: show all abbreviations
 * abbr {lhs}				: show abbreviations for {lhs}
 * abbr {lhs} {rhs}			: set abbreviation for {lhs} to {rhs}
 * noreabbr {lhs} {rhs}		: same, but no remapping for {rhs}
 * unabbr {lhs}				: remove abbreviation for {lhs}
 *
 * maptype == 1 for unmap command, 2 for noremap command.
 *
 * keys is pointer to any arguments. Note: keys cannot be a read-only string,
 * it will be modified.
 *
 * for :map	  mode is NORMAL + VISUAL
 * for :map!  mode is INSERT + CMDLINE
 * for :cmap  mode is CMDLINE
 * for :imap  mode is INSERT 
 * for :nmap  mode is NORMAL
 * for :vmap  mode is VISUAL
 * for :abbr  mode is INSERT + CMDLINE + ABBREV
 * for :iabbr mode is INSERT + ABBREV
 * for :cabbr mode is CMDLINE + ABBREV
 * 
 * Return 0 for success
 *		  1 for invalid arguments
 *		  2 for no match
 *		  3 for ambiguety
 *		  4 for out of mem
 */
	int
do_map(maptype, keys, mode)
	int		maptype;
	char_u	*keys;
	int		mode;
{
	struct mapblock		*mp, *mprev;
	char_u				*arg;
	char_u				*p;
	int					n;
	int					len = 0;		/* init for GCC */
	char_u				*newstr;
	int					hasarg;
	int					haskey;
	int					did_it = FALSE;
	int					abbrev = 0;
	int					round;
	char_u				*keys_buf = NULL;
	char_u				*arg_buf = NULL;
	int					retval = 0;
	int					do_backslash;

	if (mode & ABBREV)		/* not a mapping but an abbreviation */
	{
		abbrev = ABBREV;
		mode &= ~ABBREV;
	}
/*
 * find end of keys and skip CTRL-Vs (and backslashes) in it
 * Accept backslash like CTRL-V when 'cpoptions' does not contain 'B'.
 * with :unmap white space is included in the keys, no argument possible
 */
	p = keys;
	do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
	while (*p && (maptype == 1 || !vim_iswhite(*p)))
	{
		if ((p[0] == Ctrl('V') || (do_backslash && p[0] == '\\')) &&
																  p[1] != NUL)
			++p;				/* skip CTRL-V or backslash */
		++p;
	}
	if (*p != NUL)
		*p++ = NUL;
	p = skipwhite(p);
	arg = p;
	hasarg = (*arg != NUL);
	haskey = (*keys != NUL);

		/* check for :unmap without argument */
	if (maptype == 1 && !haskey)	
	{
		retval = 1;
		goto theend;
	}

	/*
	 * If mapping has been given as ^V<C_UP> say, then replace the term codes
	 * with the appropriate two bytes. If it is a shifted special key, unshift
	 * it too, giving another two bytes.
	 * replace_termcodes() may move the result to allocated memory, which
	 * needs to be freed later (*keys_buf and *arg_buf).
	 * replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
	 */
	if (haskey)
		keys = replace_termcodes(keys, &keys_buf, TRUE);
	if (hasarg)
		arg = replace_termcodes(arg, &arg_buf, FALSE);

/*
 * check arguments and translate function keys
 */
	if (haskey)
	{
		len = STRLEN(keys);
		if (len > MAXMAPLEN)			/* maximum length of MAXMAPLEN chars */
		{
			retval = 1;
			goto theend;
		}

		if (abbrev)
		{
			/*
			 * If an abbreviation ends in a keyword character, the
			 * rest must be all keyword-char or all non-keyword-char.
			 * Otherwise we won't be able to find the start of it in a
			 * vi-compatible way.
			 * An abbrevation cannot contain white space.
			 */
			if (iswordchar(keys[len - 1]))  	/* ends in keyword char */
				for (n = 0; n < len - 2; ++n)
					if (iswordchar(keys[n]) != iswordchar(keys[len - 2]))
					{
						retval = 1;
						goto theend;
					}
			for (n = 0; n < len; ++n)
				if (vim_iswhite(keys[n]))
				{
					retval = 1;
					goto theend;
				}
		}
	}

	if (haskey && hasarg && abbrev)		/* if we will add an abbreviation */
		no_abbr = FALSE;				/* reset flag that indicates there are
															no abbreviations */

	if (!haskey || (maptype != 1 && !hasarg))
		msg_start();
/*
 * Find an entry in the maplist that matches.
 * For :unmap we may loop two times: once to try to unmap an entry with a
 * matching 'from' part, a second time, if the first fails, to unmap an
 * entry with a matching 'to' part. This was done to allow ":ab foo bar" to be
 * unmapped by typing ":unab foo", where "foo" will be replaced by "bar" because
 * of the abbreviation.
 */
	for (round = 0; (round == 0 || maptype == 1) && round <= 1 &&
												 !did_it && !got_int; ++round)
	{
		for (mp = maplist.m_next, mprev = &maplist; mp && !got_int;
												  mprev = mp, mp = mp->m_next)
		{
										/* skip entries with wrong mode */
			if (!(mp->m_mode & mode) || (mp->m_mode & ABBREV) != abbrev)
				continue;
			if (!haskey)						/* show all entries */
			{
				showmap(mp);
				did_it = TRUE;
			}
			else								/* do we have a match? */
			{
				if (round)		/* second round: try 'to' string for unmap */
				{
					n = STRLEN(mp->m_str);
					p = mp->m_str;
				}
				else
				{
					n = mp->m_keylen;
					p = mp->m_keys;
				}
				if (!STRNCMP(p, keys, (size_t)(n < len ? n : len)))
				{
					if (maptype == 1)			/* delete entry */
					{
						if (n != len)			/* not a full match */
							continue;
						/*
						 * We reset the indicated mode bits. If nothing is
						 * left the entry is deleted below.
						 */
						mp->m_mode &= (~mode | ABBREV);
						did_it = TRUE;			/* remember that we did something */
					}
					else if (!hasarg)			/* show matching entry */
					{
						showmap(mp);
						did_it = TRUE;
					}
					else if (n != len)			/* new entry is ambigious */
					{
						if (abbrev)				/* for abbreviations that's ok */
							continue;
						retval = 3;
						goto theend;
					}
					else
					{
						mp->m_mode &= (~mode | ABBREV);		/* remove mode bits */
						if (!(mp->m_mode & ~ABBREV) && !did_it)	/* reuse existing entry */
						{
							newstr = strsave(arg);
							if (newstr == NULL)
							{
								retval = 4;			/* no mem */
								goto theend;
							}
							vim_free(mp->m_str);
							mp->m_str = newstr;
							mp->m_noremap = maptype;
							mp->m_mode = mode + abbrev;
							did_it = TRUE;
						}
					}
					if (!(mp->m_mode & ~ABBREV))	/* entry can be deleted */
					{
						map_free(mprev);
						mp = mprev;				/* continue with next entry */
					}
				}
			}
		}
	}

	if (maptype == 1)						/* delete entry */
	{
		if (!did_it)
			retval = 2;						/* no match */
		goto theend;
	}

	if (!haskey || !hasarg)					/* print entries */
	{
		if (!did_it)
		{
			if (abbrev)
				MSG("No abbreviation found");
			else
				MSG("No mapping found");
		}
		goto theend;						/* listing finished */
	}

	if (did_it)					/* have added the new entry already */
		goto theend;
/*
 * get here when we have to add a new entry
 */
		/* allocate a new entry for the maplist */
	mp = (struct mapblock *)alloc((unsigned)sizeof(struct mapblock));
	if (mp == NULL)
	{
		retval = 4;			/* no mem */
		goto theend;
	}
	mp->m_keys = strsave(keys);
	mp->m_str = strsave(arg);
	if (mp->m_keys == NULL || mp->m_str == NULL)
	{
		vim_free(mp->m_keys);
		vim_free(mp->m_str);
		vim_free(mp);
		retval = 4;		/* no mem */
		goto theend;
	}
	mp->m_keylen = STRLEN(mp->m_keys);
	mp->m_noremap = maptype;
	mp->m_mode = mode + abbrev;

	/* add the new entry in front of the maplist */
	mp->m_next = maplist.m_next;
	maplist.m_next = mp;

theend:
	vim_free(keys_buf);
	vim_free(arg_buf);
	return retval;
}

/*
 * Delete one entry from the maplist.
 * The argument is a pointer to the PREVIOUS entry!
 */
	static void
map_free(mprev)
	struct mapblock		*mprev;
{
	struct mapblock		*mp;
	
	mp = mprev->m_next;
	vim_free(mp->m_keys);
	vim_free(mp->m_str);
	mprev->m_next = mp->m_next;
	vim_free(mp);
}

/*
 * Clear all mappings or abbreviations.
 * 'abbr' should be FALSE for mappings, TRUE for abbreviations.
 */
	void
map_clear(modec, force, abbr)
	int		modec;
	int		force;
	int		abbr;
{
	struct mapblock		*mp;
	int		mode;

	if (force)			/* :mapclear! */
		mode = INSERT + CMDLINE;
	else if (modec == 'i')
		mode = INSERT;
	else if (modec == 'n')
		mode = NORMAL;
	else if (modec == 'c')
		mode = CMDLINE;
	else if (modec == 'v')
		mode = VISUAL;
	else
		mode = VISUAL + NORMAL;

	for (mp = &maplist; mp->m_next != NULL; )
	{
		if (abbr != !(mp->m_next->m_mode & ABBREV) && mp->m_next->m_mode & mode)
		{
			mp->m_next->m_mode &= ~mode;
			if ((mp->m_next->m_mode & ~ABBREV) == 0) /* entry can be deleted */
			{
				map_free(mp);
				continue;
			}
		}
		mp = mp->m_next;
	}
}

	static void
showmap(mp)
	struct mapblock *mp;
{
	int len;

	if (msg_didout)
		msg_outchar('\n');
	if ((mp->m_mode & (INSERT + CMDLINE)) == INSERT + CMDLINE)
		MSG_OUTSTR("! ");
	else if (mp->m_mode & INSERT)
		MSG_OUTSTR("i ");
	else if (mp->m_mode & CMDLINE)
		MSG_OUTSTR("c ");
	else if (!(mp->m_mode & VISUAL))
		MSG_OUTSTR("n ");
	else if (!(mp->m_mode & NORMAL))
		MSG_OUTSTR("v ");
	else
		MSG_OUTSTR("  ");
	/* Get length of what we write */
	len = msg_outtrans_special(mp->m_keys, TRUE);
	do
	{
		msg_outchar(' ');				/* padd with blanks */
		++len;
	} while (len < 12);
	if (mp->m_noremap)
		msg_outchar('*');
	else
		msg_outchar(' ');
	/* Use FALSE below if we only want things like <Up> to show up as such on
	 * the rhs, and not M-x etc, TRUE gets both -- webb
	 */
	msg_outtrans_special(mp->m_str, TRUE);
	flushbuf();							/* show one line at a time */
}

/*
 * Check for an abbreviation.
 * Cursor is at ptr[col]. When inserting, mincol is where insert started.
 * "c" is the character typed before check_abbr was called.
 *
 * Historic vi practice: The last character of an abbreviation must be an id
 * character ([a-zA-Z0-9_]). The characters in front of it must be all id
 * characters or all non-id characters. This allows for abbr. "#i" to
 * "#include".
 *
 * Vim addition: Allow for abbreviations that end in a non-keyword character.
 * Then there must be white space before the abbr.
 *
 * return TRUE if there is an abbreviation, FALSE if not
 */
	int
check_abbr(c, ptr, col, mincol)
	int		c;
	char_u	*ptr;
	int		col;
	int		mincol;
{
	int				len;
	int				j;
	char_u			tb[4];
	struct mapblock *mp;
	int				is_id = TRUE;
	int				vim_abbr;

	if (no_abbr_cnt)		/* abbrev. are not recursive */
		return FALSE;

	/*
	 * Check for word before the cursor: If it ends in a keyword char all
	 * chars before it must be al keyword chars or non-keyword chars, but not
	 * white space. If it ends in a non-keyword char we accept any characters
	 * before it except white space.
	 */
	if (col == 0)								/* cannot be an abbr. */
		return FALSE;

	if (!iswordchar(ptr[col - 1]))
		vim_abbr = TRUE;						/* Vim added abbr. */
	else
	{
		vim_abbr = FALSE;						/* vi compatible abbr. */
		if (col > 1)
			is_id = iswordchar(ptr[col - 2]);
	}
	for (len = col - 1; len > 0 && !vim_isspace(ptr[len - 1]) &&
					   (vim_abbr || is_id == iswordchar(ptr[len - 1])); --len)
		;

	if (len < mincol)
		len = mincol;
	if (len < col)				/* there is a word in front of the cursor */
	{
		ptr += len;
		len = col - len;
		for (mp = maplist.m_next; mp; mp = mp->m_next)
		{
					/* find entries with right mode and keys */
			if ((mp->m_mode & ABBREV) == ABBREV &&
						(mp->m_mode & State) &&
						mp->m_keylen == len &&
						!STRNCMP(mp->m_keys, ptr, (size_t)len))
				break;
		}
		if (mp)
		{
			/*
			 * Found a match:
			 * Insert the rest of the abbreviation in typebuf[].
			 * This goes from end to start.
			 *
			 * Characters 0x000 - 0x100: normal chars, may need CTRL-V,
			 * except K_SPECIAL: Becomes K_SPECIAL KS_SPECIAL K_FILLER
			 * Characters where IS_SPECIAL() == TRUE: key codes, need
			 * K_SPECIAL. Other characters (with ABBR_OFF): don't use CTRL-V.
			 */
			j = 0;
											/* special key code, split up */
			if (IS_SPECIAL(c) || c == K_SPECIAL)
			{
				tb[j++] = K_SPECIAL;
				tb[j++] = K_SECOND(c);
				c = K_THIRD(c);
			}
			else if (c < 0x100  && (c < ' ' || c > '~'))
				tb[j++] = Ctrl('V');		/* special char needs CTRL-V */
			tb[j++] = c;
			tb[j] = NUL;
											/* insert the last typed char */
			(void)ins_typebuf(tb, TRUE, 0, TRUE);
											/* insert the to string */
			(void)ins_typebuf(mp->m_str, mp->m_noremap, 0, TRUE);
											/* no abbrev. for these chars */
			no_abbr_cnt += STRLEN(mp->m_str) + j + 1;

			tb[0] = Ctrl('H');
			tb[1] = NUL;
			while (len--)					/* delete the from string */
				(void)ins_typebuf(tb, TRUE, 0, TRUE);
			return TRUE;
		}
	}
	return FALSE;
}

/*
 * Write map commands for the current mappings to an .exrc file.
 * Return FAIL on error, OK otherwise.
 */
	int
makemap(fd)
	FILE *fd;
{
	struct mapblock *mp;
	char_u			c1;
	char_u 			*p;

	for (mp = maplist.m_next; mp; mp = mp->m_next)
	{
		c1 = NUL;
		p = (char_u *)"map";
		switch (mp->m_mode)
		{
		case NORMAL + VISUAL:
			break;
		case NORMAL:
			c1 = 'n';
			break;
		case VISUAL:
			c1 = 'v';
			break;
		case CMDLINE + INSERT:
			p = (char_u *)"map!";
			break;
		case CMDLINE:
			c1 = 'c';
			break;
		case INSERT:
			c1 = 'i';
			break;
		case INSERT + CMDLINE + ABBREV:
			p = (char_u *)"abbr";
			break;
		case CMDLINE + ABBREV:
			c1 = 'c';
			p = (char_u *)"abbr";
			break;
		case INSERT + ABBREV:
			c1 = 'i';
			p = (char_u *)"abbr";
			break;
		default:
			EMSG("makemap: Illegal mode");
			return FAIL;
		}
		if (c1 && putc(c1, fd) < 0)
			return FAIL;
		if (mp->m_noremap && fprintf(fd, "nore") < 0)
			return FAIL;
		if (fprintf(fd, (char *)p) < 0)
			return FAIL;

		if (	putc(' ', fd) < 0 || putescstr(fd, mp->m_keys, FALSE) == FAIL ||
				putc(' ', fd) < 0 || putescstr(fd, mp->m_str, FALSE) == FAIL ||
#ifdef USE_CRNL
				putc('\r', fd) < 0 ||
#endif
				putc('\n', fd) < 0)
			return FAIL;
	}
	return OK;
}

/*
 * write escape string to file
 *
 * return FAIL for failure, OK otherwise
 */
	int
putescstr(fd, str, set)
	FILE		*fd;
	char_u		*str;
	int			set;		/* TRUE for makeset, FALSE for makemap */
{
	int		c;
	int		modifiers;

	for ( ; *str; ++str)
	{
		c = *str;
		/*
		 * Special key codes have to be translated to be able to make sense
		 * when they are read back.
		 */
		if (c == K_SPECIAL && !set)
		{
			modifiers = 0x0;
			if (str[1] == KS_MODIFIER)
			{
				modifiers = str[2];
				str += 3;
				c = *str;
			}
			if (c == K_SPECIAL)
			{
				c = TO_SPECIAL(str[1], str[2]);
				str += 2;
			}
			if (IS_SPECIAL(c) || modifiers)		/* special key */
			{
				fprintf(fd, (char *)get_special_key_name(c, modifiers));
				continue;
			}
		}
		/*
		 * A '\n' in a map command should be written as <NL>.
		 * A '\n' in a set command should be written as \^V^J.
		 */
		if (c == NL)
		{
			if (set)
				fprintf(fd, "\\\026\n");
			else
				fprintf(fd, "<NL>");
			continue;
		}
		/*
		 * some characters have to be escaped with CTRL-V to
		 * prevent them from misinterpreted in DoOneCmd().
		 * A space, Tab and '"' has to be escaped with a backslash to
		 * prevent it to be misinterpreted in do_set().
		 */
		if (set && (vim_iswhite(c) || c == '"' || c == '\\'))
		{
			if (putc('\\', fd) < 0)
				return FAIL;
		}
		else if (c < ' ' || c > '~' || c == '|')
		{
			if (putc(Ctrl('V'), fd) < 0)
				return FAIL;
		}
		if (putc(c, fd) < 0)
			return FAIL;
	}
	return OK;
}

/*
 * Check all mappings for the presence of special key codes.
 * Used after ":set term=xxx".
 */
	void
check_map_keycodes()
{
	struct mapblock *mp;
	char_u 			*p;
	int				i;
	char_u			buf[3];
	char_u			*save_name;

	save_name = sourcing_name;
	sourcing_name = (char_u *)"mappings";/* don't give error messages */
	for (mp = maplist.m_next; mp != NULL; mp = mp->m_next)
	{
		for (i = 0; i <= 1; ++i)		/* do this twice */
		{
			if (i == 0)
				p = mp->m_keys;			/* once for the "from" part */
			else
				p = mp->m_str;			/* and once for the "to" part */
			while (*p)
			{
				if (*p == K_SPECIAL)
				{
					++p;
					if (*p < 128)		/* only for "normal" termcap entries */
					{
						buf[0] = p[0];
						buf[1] = p[1];
						buf[2] = NUL;
						(void)add_termcap_entry(buf, FALSE);
					}
					++p;
				}
				++p;
			}
		}
	}
	sourcing_name = save_name;
}