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

This is ex_getln.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.
 */

/*
 * ex_getln.c: Functions for entering and editing an Ex command line.
 */

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

/*
 * Variables shared between getcmdline(), redrawcmdline() and others.
 * These need to be saved when using CTRL-R |, that's why they are in a
 * structure.
 */
struct cmdline_info
{
	char_u	*cmdbuff;		/* pointer to command line buffer */
	int		 cmdbufflen;	/* length of cmdbuff */
	int		 cmdlen;		/* number of chars on command line */
	int		 cmdpos;		/* current cursor position */
	int		 cmdspos;		/* cursor column on screen */
	int		 cmdfirstc; 	/* ':', '/', '?', '=' or NUL */
	int		 cmdindent; 	/* number of spaces before cmdline */
	int		 overstrike;	/* Typing mode on the command line.  Shared by
							   getcmdline() and put_on_cmdline(). */
};

static struct cmdline_info ccline;		/* current cmdline_info */

static int  	cmd_numfiles = -1;		/* number of files found by
													filename completion */
/*
 * There are three history tables:
 */
#define HIST_CMD	0		/* colon commands */
#define HIST_SEARCH	1		/* search commands */
#define HIST_EXPR	2		/* expressions (from entering | register) */
#define HIST_COUNT	3		/* number of history tables */

static char_u	**(history[HIST_COUNT]) = {NULL, NULL, NULL};
static int		hisidx[HIST_COUNT] = {-1, -1, -1};	/* last entered entry */
static int		hislen = 0; 			/* actual length of history tables */

#ifdef RIGHTLEFT
static int		cmd_hkmap = 0;		/* Hebrew mapping during command line */
#endif

static int		hist_char2type __ARGS((int c));
static int		hist_type2char __ARGS((int type, int use_question));
static void		init_history __ARGS((void));

static int		is_in_history __ARGS((int, char_u *, int));
static void		alloc_cmdbuff __ARGS((int len));
static int		realloc_cmdbuff __ARGS((int len));
static void		putcmdline __ARGS((int));
static void		redrawcmd __ARGS((void));
static void		cursorcmd __ARGS((void));
static int		ccheck_abbr __ARGS((int));
static int		nextwild __ARGS((int));
static int		showmatches __ARGS((void));
static void		set_expand_context __ARGS((void));
static int		ExpandFromContext __ARGS((char_u *, int *, char_u ***, int, int));

/*
 * getcmdline() - accept a command line starting with firstc.
 *
 * firstc == ':'			get ":" command line.
 * firstc == '/' or '?'		get search pattern
 * firstc == '='			get expression
 * firstc == NUL			get text for :insert command
 *
 * The line is collected in ccline.cmdbuff, which is reallocated to fit the
 * command line.
 *
 * Careful: getcmdline() can be called recursively!
 *
 * Return pointer to allocated string if there is a commandline, NULL
 * otherwise.
 */
	char_u *
getcmdline(firstc, count, indent)
	int			firstc;
	long		count;			/* only used for incremental search */
	int			indent;			/* indent for inside conditionals */
{
	int	 		c;
#ifdef DIGRAPHS
	int			cc;
#endif
	int			i;
	int			j;
	char_u		*p;
	int			hiscnt;					/* current history line in use */
	char_u		*lookfor = NULL;		/* string to match */
	int			gotesc = FALSE;			/* TRUE when <ESC> just typed */
	int			do_abbr;				/* when TRUE check for abbr. */
	int			histype;				/* history type to be used */
	FPOS		old_cursor;
	colnr_t		old_curswant;
	int			did_incsearch = FALSE;
	int			incsearch_postponed = FALSE;
	int			save_msg_scroll = msg_scroll;
	int			save_State = State;		/* remember State when called */
	int			some_key_typed = FALSE;	/* one of the keys was typed */
#ifdef USE_MOUSE
	/* mouse drag and release events are ignored, unless they are
	 * preceded with a mouse down event */
	int			ignore_drag_release = TRUE;
#endif

	ccline.overstrike = FALSE;				/* always start in insert mode */
	old_cursor = curwin->w_cursor;			/* needs to be restored later */
	old_curswant = curwin->w_curswant;

	/*
	 * set some variables for redrawcmd()
	 */
	ccline.cmdfirstc = firstc;
	ccline.cmdindent = indent;
	alloc_cmdbuff(exmode_active ? 250 : 0);	/* alloc initial ccline.cmdbuff */
	if (ccline.cmdbuff == NULL)
		return NULL;					/* out of memory */
	ccline.cmdlen = ccline.cmdpos = 0;
	if (firstc)
		ccline.cmdspos = 1 + indent;
	else
		ccline.cmdspos = 0 + indent;

	i = msg_scrolled;
	msg_scrolled = FALSE;		/* avoid wait_return message */
	gotocmdline(TRUE);
	msg_scrolled = i;
	if (firstc)
		msg_putchar(firstc);
	while (indent-- > 0)
		msg_putchar(' ');

	/*
	 * Avoid scrolling when called by a recursive do_cmdline(), e.g. when doing
	 * ":@0" when register 0 doesn't contain a CR.
	 */
	msg_scroll = FALSE;

	State = CMDLINE;
#ifdef USE_MOUSE
	setmouse();
#endif

	init_history();
	hiscnt = hislen;			/* set hiscnt to impossible history value */
	histype = hist_char2type(firstc);

#ifdef DIGRAPHS
	do_digraph(-1);				/* init digraph typahead */
#endif

	/* collect the command string, handling editing keys */
	for (;;)
	{
		cursorcmd();			/* set the cursor on the right spot */
		c = vgetc();
		if (KeyTyped)
		{
			some_key_typed = TRUE;
#ifdef RIGHTLEFT
			if (cmd_hkmap)
				c = hkmap(c);
#endif
		}

		/*
		 * Ignore got_int when CTRL-C was typed here.
		 * Don't ignore it in :global, we really need to break then, e.g., for
		 * ":g/pat/normal /pat" (without the <CR>).
		 */
		if (c == Ctrl('C') && !global_busy)
			got_int = FALSE;

		/* free old command line when finished moving around in the history
		 * list */
		if (lookfor && c != K_S_DOWN && c != K_S_UP &&
				c != K_DOWN && c != K_UP &&
				c != K_PAGEDOWN && c != K_PAGEUP &&
				c != K_KPAGEDOWN && c != K_KPAGEUP &&
				(cmd_numfiles > 0 || (c != Ctrl('P') && c != Ctrl('N'))))
		{
			vim_free(lookfor);
			lookfor = NULL;
		}

		/*
		 * <S-Tab> works like CTRL-P (unless 'wc' is <S-Tab>).
		 */
		if (c != p_wc && c == K_S_TAB)
			c = Ctrl('P');

			/* free expanded names when finished walking through matches */
		if (cmd_numfiles != -1 && !(c == p_wc && KeyTyped) && c != Ctrl('N') &&
						c != Ctrl('P') && c != Ctrl('A') && c != Ctrl('L'))
			(void)ExpandOne(NULL, NULL, 0, WILD_FREE);

#ifdef DIGRAPHS
		c = do_digraph(c);
#endif

		if (c == '\n' || c == '\r' || (c == ESC && (!KeyTyped ||
										 vim_strchr(p_cpo, CPO_ESC) != NULL)))
		{
			gotesc = FALSE;		/* Might have typed ESC previously, don't
								   truncate the cmdline now. */
			if (ccheck_abbr(c + ABBR_OFF))
				goto cmdline_changed;
			windgoto(msg_row, 0);
			flushbuf();
			break;
		}

			/* hitting <ESC> twice means: abandon command line */
			/* wildcard expansion is only done when the key is really typed,
			 * not when it comes from a macro */
		if (c == p_wc && !gotesc && KeyTyped)
		{
			if (cmd_numfiles > 0)	/* typed p_wc twice */
				i = nextwild(WILD_NEXT);
			else					/* typed p_wc first time */
				i = nextwild(WILD_EXPAND_KEEP);
			if (c == ESC)
				gotesc = TRUE;
			if (i)
				goto cmdline_changed;
		}
		gotesc = FALSE;

		if (c == NUL || c == K_ZERO)		/* NUL is stored as NL */
			c = NL;

		do_abbr = TRUE;			/* default: check for abbreviation */
		switch (c)
		{
		case K_BS:
		case Ctrl('H'):
		case K_DEL:
		case Ctrl('W'):
				/*
				 * delete current character is the same as backspace on next
				 * character, except at end of line
				 */
				if (c == K_DEL && ccline.cmdpos != ccline.cmdlen)
					++ccline.cmdpos;
				if (ccline.cmdpos > 0)
				{
					j = ccline.cmdpos;
					if (c == Ctrl('W'))
					{
						while (ccline.cmdpos &&
							   vim_isspace(ccline.cmdbuff[ccline.cmdpos - 1]))
							--ccline.cmdpos;
						i = iswordchar(ccline.cmdbuff[ccline.cmdpos - 1]);
						while (ccline.cmdpos && !vim_isspace(
										 ccline.cmdbuff[ccline.cmdpos - 1]) &&
								iswordchar(
									  ccline.cmdbuff[ccline.cmdpos - 1]) == i)
							--ccline.cmdpos;
					}
					else
						--ccline.cmdpos;
					ccline.cmdlen -= j - ccline.cmdpos;
					i = ccline.cmdpos;
					while (i < ccline.cmdlen)
						ccline.cmdbuff[i++] = ccline.cmdbuff[j++];
					redrawcmd();
				}
				else if (ccline.cmdlen == 0 && c != Ctrl('W'))
				{
					vim_free(ccline.cmdbuff);	/* no commandline to return */
					ccline.cmdbuff = NULL;
					msg_pos(-1, 0);
					msg_putchar(' ');			/* delete ':' */
					redraw_cmdline = TRUE;
					goto returncmd; 			/* back to cmd mode */
				}
				goto cmdline_changed;

		case K_INS:
				ccline.overstrike = !ccline.overstrike;
#ifdef USE_GUI
				if (gui.in_use)
					gui_update_cursor(TRUE);	/* change shape of cursor */
#endif
				goto cmdline_not_changed;

/*		case '@':	only in very old vi */
		case Ctrl('U'):
				ccline.cmdpos = 0;
				ccline.cmdlen = 0;
				if (firstc)
					ccline.cmdspos = 1 + ccline.cmdindent;
				else
					ccline.cmdspos = 0 + ccline.cmdindent;
				redrawcmd();
				goto cmdline_changed;

		case ESC:		/* get here if p_wc != ESC or when ESC typed twice */
		case Ctrl('C'):
				gotesc = TRUE;		/* will free ccline.cmdbuff after putting
									   it in history */
				goto returncmd; 	/* back to cmd mode */

		case Ctrl('R'):				/* insert register */
				putcmdline('"');
				++no_mapping;
			  	c = vgetc();
				--no_mapping;
				/*
				 * Insert the result of an expression.
				 * Need to save the current command line, to be able to enter
				 * a new one...
				 */
				if (c == '=')
				{
					struct cmdline_info		save_ccline;

					save_ccline = ccline;
					c = get_expr_register();
					ccline = save_ccline;
				}
				if (c != ESC)		/* use ESC to cancel inserting register */
					cmdline_paste(c);
				redrawcmd();
				goto cmdline_changed;

		case Ctrl('D'):
			{
				if (showmatches() == FAIL)
					break;		/* Use ^D as normal char instead */

				redrawcmd();
				continue;		/* don't do incremental search now */
			}

		case K_RIGHT:
		case K_S_RIGHT:
				do
				{
					if (ccline.cmdpos >= ccline.cmdlen)
						break;
					ccline.cmdspos += charsize(ccline.cmdbuff[ccline.cmdpos]);
					++ccline.cmdpos;
				}
				while (c == K_S_RIGHT && ccline.cmdbuff[ccline.cmdpos] != ' ');
				goto cmdline_not_changed;

		case K_LEFT:
		case K_S_LEFT:
				do
				{
					if (ccline.cmdpos <= 0)
						break;
					--ccline.cmdpos;
					ccline.cmdspos -= charsize(ccline.cmdbuff[ccline.cmdpos]);
				}
				while (c == K_S_LEFT &&
									ccline.cmdbuff[ccline.cmdpos - 1] != ' ');
				goto cmdline_not_changed;

#ifdef USE_MOUSE
		case K_MIDDLEDRAG:
		case K_MIDDLERELEASE:
		case K_IGNORE:
				goto cmdline_not_changed;	/* Ignore mouse */

		case K_MIDDLEMOUSE:
# ifdef USE_GUI
				/* When GUI is active, also paste when 'mouse' is empty */
				if (!gui.in_use)
# endif
					if (!mouse_has(MOUSE_COMMAND))
						goto cmdline_not_changed;	/* Ignore mouse */
# ifdef USE_GUI
				if (gui.in_use)
					cmdline_paste('*');
				else
# endif
					cmdline_paste(0);
				redrawcmd();
				goto cmdline_changed;

		case K_LEFTDRAG:
		case K_LEFTRELEASE:
		case K_RIGHTDRAG:
		case K_RIGHTRELEASE:
				if (ignore_drag_release)
					goto cmdline_not_changed;
				/* FALLTHROUGH */
		case K_LEFTMOUSE:
		case K_RIGHTMOUSE:
				if (c == K_LEFTRELEASE || c == K_RIGHTRELEASE)
					ignore_drag_release = TRUE;
				else
					ignore_drag_release = FALSE;
# ifdef USE_GUI
				/* When GUI is active, also move when 'mouse' is empty */
				if (!gui.in_use)
# endif
					if (!mouse_has(MOUSE_COMMAND))
						goto cmdline_not_changed;	/* Ignore mouse */
				if (firstc)
					ccline.cmdspos = 1 + ccline.cmdindent;
				else
					ccline.cmdspos = 0 + ccline.cmdindent;
				for (ccline.cmdpos = 0; ccline.cmdpos < ccline.cmdlen;
															  ++ccline.cmdpos)
				{
					i = charsize(ccline.cmdbuff[ccline.cmdpos]);
					if (mouse_row <= cmdline_row + ccline.cmdspos / Columns &&
									 mouse_col < ccline.cmdspos % Columns + i)
						break;
					ccline.cmdspos += i;
				}
				goto cmdline_not_changed;
#endif	/* USE_MOUSE */

#ifdef USE_GUI
		case K_SCROLLBAR:
				if (!msg_scrolled)
				{
					gui_do_scroll();
					redrawcmd();
				}
				goto cmdline_not_changed;

		case K_HORIZ_SCROLLBAR:
				if (!msg_scrolled)
				{
					gui_do_horiz_scroll();
					redrawcmd();
				}
				goto cmdline_not_changed;
#endif

		case Ctrl('B'):		/* begin of command line */
		case K_HOME:
		case K_KHOME:
				ccline.cmdpos = 0;
				if (firstc)
					ccline.cmdspos = 1 + ccline.cmdindent;
				else
					ccline.cmdspos = 0 + ccline.cmdindent;
				goto cmdline_not_changed;

		case Ctrl('E'):		/* end of command line */
		case K_END:
		case K_KEND:
				ccline.cmdpos = ccline.cmdlen;
				ccline.cmdbuff[ccline.cmdlen] = NUL;
				if (firstc)
					ccline.cmdspos = 1 + ccline.cmdindent;
				else
					ccline.cmdspos = 0 + ccline.cmdindent;
				ccline.cmdspos += strsize(ccline.cmdbuff);
				goto cmdline_not_changed;

		case Ctrl('A'):		/* all matches */
				if (!nextwild(WILD_ALL))
					break;
				goto cmdline_changed;

		case Ctrl('L'):		/* longest common part */
				if (!nextwild(WILD_LONGEST))
					break;
				goto cmdline_changed;

		case Ctrl('N'):		/* next match */
		case Ctrl('P'):		/* previous match */
				if (cmd_numfiles > 0)
				{
					if (!nextwild((c == Ctrl('P')) ? WILD_PREV : WILD_NEXT))
						break;
					goto cmdline_changed;
				}

		case K_UP:
		case K_DOWN:
		case K_S_UP:
		case K_S_DOWN:
		case K_PAGEUP:
		case K_KPAGEUP:
		case K_PAGEDOWN:
		case K_KPAGEDOWN:
				if (hislen == 0 || firstc == NUL)		/* no history */
					goto cmdline_not_changed;

				i = hiscnt;

				/* save current command string so it can be restored later */
				ccline.cmdbuff[ccline.cmdpos] = NUL;
				if (lookfor == NULL &&
							  (lookfor = vim_strsave(ccline.cmdbuff)) == NULL)
					goto cmdline_not_changed;

				j = STRLEN(lookfor);
				for (;;)
				{
					/* one step backwards */
					if (c == K_UP || c == K_S_UP || c == Ctrl('P') ||
							c == K_PAGEUP || c == K_KPAGEUP)
					{
						if (hiscnt == hislen)	/* first time */
							hiscnt = hisidx[histype];
						else if (hiscnt == 0 && hisidx[histype] != hislen - 1)
							hiscnt = hislen - 1;
						else if (hiscnt != hisidx[histype] + 1)
							--hiscnt;
						else					/* at top of list */
						{
							hiscnt = i;
							break;
						}
					}
					else	/* one step forwards */
					{
						/* on last entry, clear the line */
						if (hiscnt == hisidx[histype])
						{
							hiscnt = hislen;
							break;
						}

						/* not on a history line, nothing to do */
						if (hiscnt == hislen)
							break;
						if (hiscnt == hislen - 1)	/* wrap around */
							hiscnt = 0;
						else
							++hiscnt;
					}
					if (hiscnt < 0 || history[histype][hiscnt] == NULL)
					{
						hiscnt = i;
						break;
					}
					if ((c != K_UP && c != K_DOWN) || hiscnt == i ||
							STRNCMP(history[histype][hiscnt],
													lookfor, (size_t)j) == 0)
						break;
				}

				if (hiscnt != i)		/* jumped to other entry */
				{
					vim_free(ccline.cmdbuff);
					if (hiscnt == hislen)
						p = lookfor;	/* back to the old one */
					else
						p = history[histype][hiscnt];

					alloc_cmdbuff((int)STRLEN(p));
					if (ccline.cmdbuff == NULL)
						goto returncmd;
					STRCPY(ccline.cmdbuff, p);

					ccline.cmdpos = ccline.cmdlen = STRLEN(ccline.cmdbuff);
					redrawcmd();
					goto cmdline_changed;
				}
				beep_flush();
				goto cmdline_not_changed;

		case Ctrl('V'):
		case Ctrl('Q'):
#ifdef USE_MOUSE
				ignore_drag_release = TRUE;
#endif
				putcmdline('^');
				c = get_literal();			/* get next (two) character(s) */
				do_abbr = FALSE;			/* don't do abbreviation now */
				break;

#ifdef DIGRAPHS
		case Ctrl('K'):
#ifdef USE_MOUSE
				ignore_drag_release = TRUE;
#endif
				putcmdline('?');
				++no_mapping;
				++allow_keys;
			  	c = vgetc();
				--no_mapping;
				--allow_keys;
				if (c != ESC)				/* ESC cancels CTRL-K */
				{
					if (IS_SPECIAL(c))			/* insert special key code */
						break;
					if (charsize(c) == 1)
						putcmdline(c);
					++no_mapping;
					++allow_keys;
					cc = vgetc();
					--no_mapping;
					--allow_keys;
					if (cc != ESC)			/* ESC cancels CTRL-K */
					{
						c = getdigraph(c, cc, TRUE);
						break;
					}
				}
				redrawcmd();
				goto cmdline_not_changed;
#endif /* DIGRAPHS */

#ifdef RIGHTLEFT
		case Ctrl('_'):		/* CTRL-_: switch language mode */
				cmd_hkmap = !cmd_hkmap;
				goto cmdline_not_changed;
#endif

		default:
				/*
				 * Normal character with no special meaning.  Just set mod_mask
				 * to 0x0 so that typing Shift-Space in the GUI doesn't enter
				 * the string <S-Space>.  This should only happen after ^V.
				 */
				if (!IS_SPECIAL(c))
					mod_mask = 0x0;
				break;
		}

		/* we come here if we have a normal character */

		if (do_abbr && (IS_SPECIAL(c) || !iswordchar(c)) && ccheck_abbr(c))
			goto cmdline_changed;

		/*
		 * put the character in the command line
		 */
		if (IS_SPECIAL(c) || mod_mask != 0x0)
			put_on_cmdline(get_special_key_name(c, mod_mask), -1, TRUE);
		else
		{
			IObuff[0] = c;
			put_on_cmdline(IObuff, 1, TRUE);
		}
		goto cmdline_changed;

/*
 * This part implements incremental searches for "/" and "?"
 * Jump to cmdline_not_changed when a character has been read but the command
 * line did not change. Then we only search and redraw if something changed in
 * the past.
 * Jump to cmdline_changed when the command line did change.
 * (Sorry for the goto's, I know it is ugly).
 */
cmdline_not_changed:
		if (!incsearch_postponed)
			continue;

cmdline_changed:
		if (p_is && (firstc == '/' || firstc == '?'))
		{
				/* if there is a character waiting, search and redraw later */
			if (char_avail())
			{
				incsearch_postponed = TRUE;
				continue;
			}
			incsearch_postponed = FALSE;
			curwin->w_cursor = old_cursor;	/* start at old position */

				/* If there is no command line, don't do anything */
			if (ccline.cmdlen == 0)
				i = 0;
			else
			{
				ccline.cmdbuff[ccline.cmdlen] = NUL;
				emsg_off = TRUE;	/* So it doesn't beep if bad expr */
				i = do_search(NULL, firstc, ccline.cmdbuff, count,
									  SEARCH_KEEP + SEARCH_OPT + SEARCH_NOOF);
				emsg_off = FALSE;
			}
			if (i)
				highlight_match = TRUE;			/* highlight position */
			else
				highlight_match = FALSE;			/* don't highlight */
			update_topline();
			update_screen(NOT_VALID);
			redrawcmdline();
			did_incsearch = TRUE;
		}
	}

returncmd:
	if (did_incsearch)
	{
		curwin->w_cursor = old_cursor;
		curwin->w_curswant = old_curswant;
		highlight_match = FALSE;
		redraw_later(NOT_VALID);
	}
	if (ccline.cmdbuff != NULL)
	{
		/*
		 * Put line in history buffer (":" and "=" only when it was typed).
		 */
		ccline.cmdbuff[ccline.cmdlen] = NUL;
		if (ccline.cmdlen && firstc &&
								   (some_key_typed || histype == HIST_SEARCH))
		{
			add_to_history(histype, ccline.cmdbuff);
			if (firstc == ':')
			{
				vim_free(new_last_cmdline);
				new_last_cmdline = vim_strsave(ccline.cmdbuff);
			}
		}

		if (gotesc)			/* abandon command line */
		{
			vim_free(ccline.cmdbuff);
			ccline.cmdbuff = NULL;
			MSG("");
			redraw_cmdline = TRUE;
		}
	}

	/*
	 * If the screen was shifted up, redraw the whole screen (later).
	 * If the line is too long, clear it, so ruler and shown command do
	 * not get printed in the middle of it.
	 */
	msg_check();
	msg_scroll = save_msg_scroll;

	State = save_State;
#ifdef USE_MOUSE
	setmouse();
#endif

	return ccline.cmdbuff;
}

/*
 * Get an Ex command line for the ":" command.
 */
	char_u *
getexline(c, dummy, indent)
	int			c;				/* normally ':', NUL for ":append" */
	void		*dummy;			/* cookie not used */
	int			indent;			/* indent for inside conditionals */
{
	return getcmdline(c, 1L, indent);
}

/*
 * Get an Ex command line for Ex mode.
 * In Ex mode we only use the OS supplied line editing features and no
 * mappings or abbreviations.
 */
	char_u *
getexmodeline(c, dummy, indent)
	int			c;				/* normally ':', NUL for ":append" */
	void		*dummy;			/* cookie not used */
	int			indent;			/* indent for inside conditionals */
{
	struct growarray	line_ga;
	int					len;
	char_u				*p;
	int					finished = FALSE;
	int					startcol = 0;
#if defined(USE_GUI) || defined(NO_COOKED_INPUT)
	int					c1;
	int					escaped = FALSE;		/* CTRL-V typed */
#endif

	/* always start in column 0; write a newline if necessary */
	compute_cmdrow();
	if (msg_col)
		msg_putchar('\n');
	if (c == ':')
	{
		msg_putchar(':');
		while (indent-- > 0)
			msg_putchar(' ');
		startcol = msg_col;
	}

	line_ga.ga_itemsize = 1;
	line_ga.ga_growsize = 30;
	line_ga.ga_data = NULL;
	line_ga.ga_len = 0;
	line_ga.ga_room = 0;

	/*
	 * Get the line, a few characters at a time.
	 */
	got_int = FALSE;
	while (!got_int && !finished)
	{
		if (ga_grow(&line_ga, 40) == FAIL)
			break;

		len = inchar((char_u *)(line_ga.ga_data) + line_ga.ga_len, 38, -1);

		/*
		 * When using the GUI, and for systems that don't have cooked input,
		 * handle line editing here.
		 */
#if defined(USE_GUI) || defined(NO_COOKED_INPUT)
# ifndef NO_COOKED_INPUT
		if (gui.in_use)
# endif
		{
			if (got_int)
			{
				msg_putchar('\n');
				break;
			}
			
			p = (char_u *)line_ga.ga_data + line_ga.ga_len;
			while (len > 0)
			{
				c1 = *p++;
				--len;
# ifndef NO_COOKED_INPUT
				if ((c1 == K_SPECIAL || c1 == CSI) && len >= 2)
# else
#  ifdef USE_GUI
				if ((c1 == K_SPECIAL || (c1 == CSI && gui.in_use)) && len >= 2)
#  else
				if (c1 == K_SPECIAL && len >= 2)
#  endif
# endif
				{
					c1 = TO_SPECIAL(p[0], p[1]);
					p += 2;
					len -= 2;
				}

				if (!escaped)
				{
					if (c1 == BS || c1 == K_BS || c1 == DEL || c1 == K_DEL)
					{
						if (line_ga.ga_len > 0)
						{
							msg_putchar('\b');
							msg_putchar(' ');
							msg_putchar('\b');
							--line_ga.ga_len;
							++line_ga.ga_room;
						}
						continue;
					}

					if (c1 == Ctrl('U'))
					{
						msg_col = startcol;
						msg_clr_eos();
						line_ga.ga_room += line_ga.ga_len;
						line_ga.ga_len = 0;
						continue;
					}

					if (c1 == Ctrl('V'))
					{
						escaped = TRUE;
						continue;
					}
				}

				((char_u *)line_ga.ga_data)[line_ga.ga_len] = c1;
				if (c1 == '\r')
					msg_putchar('\n');
				else
					msg_putchar(c1);
				++line_ga.ga_len;
				--line_ga.ga_room;
				escaped = FALSE;
			}
			windgoto(msg_row, msg_col);
		}
# ifndef NO_COOKED_INPUT
		else
# endif
#endif
#ifndef NO_COOKED_INPUT
		{
			line_ga.ga_len += len;
			line_ga.ga_room -= len;
		}
#endif
		p = (char_u *)(line_ga.ga_data) + line_ga.ga_len;
		while (line_ga.ga_len && (p[-1] == '\n' || p[-1] == '\r'))
		{
			finished = TRUE;
			--line_ga.ga_len;
			--p;
			*p = NUL;
		}
	}

	/* note that cursor has moved, because of the echoed <CR> */
	screen_down();
	/* make following messages go to the next line */
	msg_didout = FALSE;
	msg_col = 0;
	if (msg_row < Rows - 1)
		++msg_row;
	emsg_on_display = FALSE;			/* don't want ui_delay() */

	if (got_int)
		ga_clear(&line_ga);

	return (char_u *)line_ga.ga_data;
}

#ifdef USE_GUI
/*
 * Return TRUE if ccline.overstrike is on.
 */
	int
cmdline_overstrike()
{
	return ccline.overstrike;
}

/*
 * Return TRUE if the cursor is at the end of the cmdline.
 */
	int
cmdline_at_end()
{
	return (ccline.cmdpos >= ccline.cmdlen);
}
#endif

/*
 * Allocate a new command line buffer.
 * Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen.
 * Returns the new value of ccline.cmdbuff and ccline.cmdbufflen.
 */
	static void
alloc_cmdbuff(len)
	int		len;
{
	/*
	 * give some extra space to avoid having to allocate all the time
	 */
	if (len < 80)
		len = 100;
	else
		len += 20;

	ccline.cmdbuff = alloc(len);	/* caller should check for out-of-memory */
	ccline.cmdbufflen = len;
}

/*
 * Re-allocate the command line to length len + something extra.
 * return FAIL for failure, OK otherwise
 */
	static int
realloc_cmdbuff(len)
	int		len;
{
	char_u		*p;

	p = ccline.cmdbuff;
	alloc_cmdbuff(len);					/* will get some more */
	if (ccline.cmdbuff == NULL)			/* out of memory */
	{
		ccline.cmdbuff = p;				/* keep the old one */
		return FAIL;
	}
	vim_memmove(ccline.cmdbuff, p, (size_t)ccline.cmdlen);
	vim_free(p);
	return OK;
}

/*
 * put a character on the command line.
 * Used for CTRL-V and CTRL-K
 */
	static void
putcmdline(c)
	int		c;
{
	char_u	buf[1];

	buf[0] = c;
	msg_outtrans_len(buf, 1);
	msg_outtrans_len(ccline.cmdbuff + ccline.cmdpos,
											   ccline.cmdlen - ccline.cmdpos);
	cursorcmd();
}

/*
 * Put the given string, of the given length, onto the command line.
 * If len is -1, then STRLEN() is used to calculate the length.
 * If 'redraw' is TRUE then the new part of the command line, and the remaining
 * part will be redrawn, otherwise it will not.  If this function is called
 * twice in a row, then 'redraw' should be FALSE and redrawcmd() should be
 * called afterwards.
 */
	int
put_on_cmdline(str, len, redraw)
	char_u	*str;
	int		len;
	int		redraw;
{
	int		i;

	if (len < 0)
		len = STRLEN(str);

	/* Check if ccline.cmdbuff needs to be longer */
	if (ccline.cmdlen + len + 1 >= ccline.cmdbufflen)
		i = realloc_cmdbuff(ccline.cmdlen + len);
	else
		i = OK;
	if (i == OK)
	{
		if (!ccline.overstrike)
		{
			vim_memmove(ccline.cmdbuff + ccline.cmdpos + len,
											   ccline.cmdbuff + ccline.cmdpos,
									 (size_t)(ccline.cmdlen - ccline.cmdpos));
			ccline.cmdlen += len;
		}
		else if (ccline.cmdpos + len > ccline.cmdlen)
			ccline.cmdlen = ccline.cmdpos + len;
		vim_memmove(ccline.cmdbuff + ccline.cmdpos, str, (size_t)len);
		if (redraw)
			msg_outtrans_len(ccline.cmdbuff + ccline.cmdpos,
											   ccline.cmdlen - ccline.cmdpos);
		ccline.cmdpos += len;
		while (len--)
			ccline.cmdspos += charsize(str[len]);
	}
	if (redraw)
		msg_check();
	return i;
}

/*
 * this fuction is called when the screen size changes and with incremental
 * search
 */
	void
redrawcmdline()
{
	msg_scrolled = 0;
	need_wait_return = FALSE;
	compute_cmdrow();
	redrawcmd();
	cursorcmd();
}

/*
 * Redraw what is currently on the command line.
 */
	static void
redrawcmd()
{
	int		i;

	msg_start();
	if (ccline.cmdfirstc)
		msg_putchar(ccline.cmdfirstc);
	for (i = ccline.cmdindent; i > 0; --i)
		msg_putchar(' ');
	msg_outtrans_len(ccline.cmdbuff, ccline.cmdlen);
	msg_clr_eos();

	if (ccline.cmdfirstc)
		ccline.cmdspos = 1 + ccline.cmdindent;
	else
		ccline.cmdspos = 0 + ccline.cmdindent;
	for (i = 0; i < ccline.cmdlen && i < ccline.cmdpos; ++i)
		ccline.cmdspos += charsize(ccline.cmdbuff[i]);
	/*
	 * An emsg() before may have set msg_scroll and need_sleep. These are used
	 * in normal mode, in cmdline mode we can reset them now.
	 */
	msg_scroll = FALSE;			/* next message overwrites cmdline */
#ifdef SLEEP_IN_EMSG
	need_sleep = FALSE;			/* don't sleep */
#endif
}

	void
compute_cmdrow()
{
	if (exmode_active)
		cmdline_row = Rows - 1;
	else
		cmdline_row = lastwin->w_winpos + lastwin->w_height +
										lastwin->w_status_height;
}

	static void
cursorcmd()
{
	msg_pos(cmdline_row + (ccline.cmdspos / (int)Columns),
											   ccline.cmdspos % (int)Columns);
	windgoto(msg_row, msg_col);
}

	void
gotocmdline(clr)
	int				clr;
{
	msg_start();
	if (clr)				/* clear the bottom line(s) */
		msg_clr_eos();		/* will reset clear_cmdline */
	windgoto(cmdline_row, 0);
}

/*
 * Check the word in front of the cursor for an abbreviation.
 * Called when the non-id character "c" has been entered.
 * When an abbreviation is recognized it is removed from the text with
 * backspaces and the replacement string is inserted, followed by "c".
 */
	static int
ccheck_abbr(c)
	int c;
{
	if (p_paste || no_abbr)			/* no abbreviations or in paste mode */
		return FALSE;

	return check_abbr(c, ccline.cmdbuff, ccline.cmdpos, 0);
}

/*
 * Return FALSE if this is not an appropriate context in which to do
 * completion of anything, & TRUE if it is (even if there are no matches).
 * For the caller, this means that the character is just passed through like a
 * normal character (instead of being expanded).  This allows :s/^I^D etc.
 */
	static int
nextwild(type)
	int		type;
{
	int		i;
	char_u	*p1;
	char_u	*p2;
	int		oldlen;
	int		difflen;
	int		v;

	if (cmd_numfiles == -1)
		set_expand_context();
	if (expand_context == EXPAND_UNSUCCESSFUL)
	{
		beep_flush();
		return OK;	/* Something illegal on command line */
	}
	if (expand_context == EXPAND_NOTHING)
	{
		/* Caller can use the character as a normal char instead */
		return FAIL;
	}
	expand_interactively = TRUE;

	MSG_PUTS("...");		/* show that we are busy */
	flushbuf();

	i = expand_pattern - ccline.cmdbuff;
	oldlen = ccline.cmdpos - i;

	if (type == WILD_NEXT || type == WILD_PREV)
	{
		/*
		 * Get next/previous match for a previous expanded pattern.
		 */
		p2 = ExpandOne(NULL, NULL, 0, type);
	}
	else
	{
		/*
		 * Translate string into pattern and expand it.
		 */
		if ((p1 = addstar(&ccline.cmdbuff[i], oldlen)) == NULL)
			p2 = NULL;
		else
		{
			p2 = ExpandOne(p1, vim_strnsave(&ccline.cmdbuff[i], oldlen),
													 WILD_HOME_REPLACE, type);
			vim_free(p1);
		}
	}

	if (p2 != NULL)
	{
		if (ccline.cmdlen + (difflen = STRLEN(p2) - oldlen) >
														ccline.cmdbufflen - 4)
			v = realloc_cmdbuff(ccline.cmdlen + difflen);
		else
			v = OK;
		if (v == OK)
		{
			vim_strncpy(&ccline.cmdbuff[ccline.cmdpos + difflen],
											   &ccline.cmdbuff[ccline.cmdpos],
					ccline.cmdlen - ccline.cmdpos);
			STRNCPY(&ccline.cmdbuff[i], p2, STRLEN(p2));
			ccline.cmdlen += difflen;
			ccline.cmdpos += difflen;
		}
		vim_free(p2);
	}

	redrawcmd();
	if (cmd_numfiles <= 0 && p2 == NULL)
		beep_flush();
	else if (cmd_numfiles == 1)
		(void)ExpandOne(NULL, NULL, 0, WILD_FREE);	/* free expanded pattern */

	expand_interactively = FALSE;			/* reset for next call */
	return OK;
}

#define MAXSUFLEN 30		/* maximum length of a file suffix */

/*
 * Do wildcard expansion on the string 'str'.
 * Return a pointer to alloced memory containing the new string.
 * Return NULL for failure.
 *
 * mode = WILD_FREE:		just free previously expanded matches
 * mode = WILD_EXPAND_FREE:	normal expansion, do not keep matches
 * mode = WILD_EXPAND_KEEP:	normal expansion, keep matches
 * mode = WILD_NEXT:		use next match in multiple match, wrap to first
 * mode = WILD_PREV:		use previous match in multiple match, wrap to first
 * mode = WILD_ALL:			return all matches concatenated
 * mode = WILD_LONGEST:		return longest matched part
 *
 * options = WILD_LIST_NOTFOUND:	list entries without a match
 * options = WILD_HOME_REPLACE:		do home_replace() for buffer names
 */
	char_u *
ExpandOne(str, orig, options, mode)
	char_u	*str;
	char_u	*orig;			/* original string which is expanded */
	int		options;
	int		mode;
{
	char_u		*ss = NULL;
	static char_u **cmd_files = NULL;	/* list of input files */
	static int	findex;
	static char_u *orig_save = NULL;	/* kept value of orig */
	int			i, j;
	int			non_suf_match;			/* number without matching suffix */
	long_u		len;
	char_u		*setsuf;
	int			fnamelen, setsuflen;
	char_u		suf_buf[MAXSUFLEN];
	char_u		*p;

/*
 * first handle the case of using an old match
 */
	if (mode == WILD_NEXT || mode == WILD_PREV)
	{
		if (cmd_numfiles > 0)
		{
			if (mode == WILD_PREV)
			{
				if (findex == -1)
					findex = cmd_numfiles;
				--findex;
			}
			else	/* mode == WILD_NEXT */
				++findex;

			/*
			 * When wrapping around, return the original string, set findex to
			 * -1.
			 */
			if (findex < 0)
			{
				if (orig_save == NULL)
					findex = cmd_numfiles - 1;
				else
					findex = -1;
			}
			if (findex >= cmd_numfiles)
			{
				if (orig_save == NULL)
					findex = 0;
				else
					findex = -1;
			}
			if (findex == -1)
				return vim_strsave(orig_save);
			return vim_strsave(cmd_files[findex]);
		}
		else
			return NULL;
	}

/* free old names */
	if (cmd_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST)
	{
		FreeWild(cmd_numfiles, cmd_files);
		cmd_numfiles = -1;
		vim_free(orig_save);
		orig_save = NULL;
	}
	findex = 0;

	if (mode == WILD_FREE)		/* only release file name */
		return NULL;

	if (cmd_numfiles == -1)
	{
		vim_free(orig_save);
		orig_save = orig;
		if (ExpandFromContext(str, &cmd_numfiles, &cmd_files, FALSE,
															 options) == FAIL)
			/* error: do nothing */;
		else if (cmd_numfiles == 0)
		{
			if (!expand_interactively)
				emsg(e_nomatch);
		}
		else
		{
			/*
			 * May change home directory back to "~"
			 */
			if (options & WILD_HOME_REPLACE)
				tilde_replace(str, cmd_numfiles, cmd_files);

			/*
			 * Insert backslashes into a file name before a space, \, %, # and
			 * wildmatch characters, except '~'.
			 */
			if (expand_interactively &&
					(expand_context == EXPAND_FILES ||
					 expand_context == EXPAND_BUFFERS ||
					 expand_context == EXPAND_DIRECTORIES))
			{
				for (i = 0; i < cmd_numfiles; ++i)
				{
					p = vim_strsave_escaped(cmd_files[i],
#ifdef BACKSLASH_IN_FILENAME
													(char_u *)" *?[{`$%#");
#else
													(char_u *)" *?[{`$\\%#");
#endif
					if (p != NULL)
					{
						vim_free(cmd_files[i]);
						cmd_files[i] = p;
					}
				}
			}

			if (mode != WILD_ALL && mode != WILD_LONGEST)
			{
				non_suf_match = 1;
				if (cmd_numfiles > 1)	/* more than one match; check suffix */
				{
					non_suf_match = 0;
					for (i = 0; i < cmd_numfiles; ++i)
					{
						fnamelen = STRLEN(cmd_files[i]);
						setsuflen = 0;
						for (setsuf = p_su; *setsuf; )
						{
							setsuflen = copy_option_part(&setsuf, suf_buf,
															  MAXSUFLEN, ".,");
							if (fnamelen >= setsuflen && STRNCMP(suf_buf,
										  cmd_files[i] + fnamelen - setsuflen,
													  (size_t)setsuflen) == 0)
								break;
							setsuflen = 0;
						}
						if (setsuflen)		/* suffix matched: ignore file */
							continue;
						/*
						 * Move the name without matching suffix to the front
						 * of the list.  This makes CTRL-N work nice.
						 */
						p = cmd_files[i];
						for (j = i; j > non_suf_match; --j)
							cmd_files[j] = cmd_files[j - 1];
						cmd_files[non_suf_match++] = p;
					}
				}
				if (non_suf_match != 1)
				{
					/* Can we ever get here unless it's while expanding
					 * interactively?  If not, we can get rid of this all
					 * together. Don't really want to wait for this message
					 * (and possibly have to hit return to continue!).
					 */
					if (!expand_interactively)
						emsg(e_toomany);
					else
						beep_flush();
				}
				if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE))
					ss = vim_strsave(cmd_files[0]);
			}
		}
	}

	/* Find longest common part */
	if (mode == WILD_LONGEST && cmd_numfiles > 0)
	{
		for (len = 0; cmd_files[0][len]; ++len)
		{
			for (i = 0; i < cmd_numfiles; ++i)
			{
#ifdef CASE_INSENSITIVE_FILENAME
				if ((expand_context == EXPAND_DIRECTORIES ||
											 expand_context == EXPAND_FILES ||
										  expand_context == EXPAND_BUFFERS) &&
											TO_UPPER(cmd_files[i][len]) !=
												   TO_UPPER(cmd_files[0][len]))
					break;
				else
#endif
				if (cmd_files[i][len] != cmd_files[0][len])
					break;
			}
			if (i < cmd_numfiles)
			{
				vim_beep();
				break;
			}
		}
		ss = alloc((unsigned)len + 1);
		if (ss)
		{
			STRNCPY(ss, cmd_files[0], len);
			ss[len] = NUL;
		}
		findex = -1;						/* next p_wc gets first one */
	}

	/* Concatenate all matching names */
	if (mode == WILD_ALL && cmd_numfiles > 0)
	{
		len = 0;
		for (i = 0; i < cmd_numfiles; ++i)
			len += STRLEN(cmd_files[i]) + 1;
		ss = lalloc(len, TRUE);
		if (ss)
		{
			*ss = NUL;
			for (i = 0; i < cmd_numfiles; ++i)
			{
				STRCAT(ss, cmd_files[i]);
				if (i != cmd_numfiles - 1)
					STRCAT(ss, " ");
			}
		}
	}

	if (mode == WILD_EXPAND_FREE || mode == WILD_ALL)
	{
		FreeWild(cmd_numfiles, cmd_files);
		cmd_numfiles = -1;
	}
	return ss;
}

/*
 * For each file name in files[num_files]:
 * If 'orig_pat' starts with "~/", replace the home directory with "~".
 */
	void
tilde_replace(orig_pat, num_files, files)
	char_u	*orig_pat;
	int		num_files;
	char_u	**files;
{
	int		i;
	char_u	*p;

	if (orig_pat[0] == '~' && ispathsep(orig_pat[1]))
	{
		for (i = 0; i < num_files; ++i)
		{
			p = home_replace_save(NULL, files[i]);
			if (p != NULL)
			{
				vim_free(files[i]);
				files[i] = p;
			}
		}
	}
}

/*
 * show all matches for completion on the command line
 */
	static int
showmatches()
{
	char_u		*file_str;
	int			num_files;
	char_u		**files_found;
	int			i, j, k;
	int			maxlen;
	int			lines;
	int			columns;
	char_u		*p;
	int			lastlen;
	int			attr;

	set_expand_context();
	if (expand_context == EXPAND_UNSUCCESSFUL)
	{
		beep_flush();
		return OK;	/* Something illegal on command line */
	}
	if (expand_context == EXPAND_NOTHING)
	{
		/* Caller can use the character as a normal char instead */
		return FAIL;
	}
	expand_interactively = TRUE;

	/* add star to file name, or convert to regexp if not expanding files! */
	file_str = addstar(expand_pattern,
					  (int)(ccline.cmdbuff + ccline.cmdpos - expand_pattern));
	if (file_str == NULL)
	{
		expand_interactively = FALSE;
		return OK;
	}

	msg_didany = FALSE;					/* lines_left will be set */
	msg_start();						/* prepare for paging */
	msg_putchar('\n');
	flushbuf();
	cmdline_row = msg_row;
	msg_didany = FALSE;					/* lines_left will be set again */
	msg_start();						/* prepare for paging */

	/* find all files that match the description */
	if (ExpandFromContext(file_str, &num_files, &files_found, FALSE, 0) == FAIL)
	{
		num_files = 0;
		files_found = (char_u **)"";
	}

	/* find the length of the longest file name */
	maxlen = 0;
	for (i = 0; i < num_files; ++i)
	{
		if (expand_context == EXPAND_FILES || expand_context == EXPAND_BUFFERS)
		{
			home_replace(NULL, files_found[i], NameBuff, MAXPATHL);
			j = strsize(NameBuff);
		}
		else
			j = strsize(files_found[i]);
		if (j > maxlen)
			maxlen = j;
	}

	/* compute the number of columns and lines for the listing */
	maxlen += 2;	/* two spaces between file names */
	columns = ((int)Columns + 2) / maxlen;
	if (columns < 1)
		columns = 1;
	lines = (num_files + columns - 1) / columns;

	attr = highlight_attr[HLF_D];	/* find out highlighting for directories */

	/* list the files line by line */
	for (i = 0; i < lines; ++i)
	{
		lastlen = 999;
		for (k = i; k < num_files; k += lines)
		{
			for (j = maxlen - lastlen; --j >= 0; )
				msg_putchar(' ');
			if (expand_context == EXPAND_FILES ||
											 expand_context == EXPAND_BUFFERS)
			{
						/* highlight directories */
				j = (mch_isdir(files_found[k]));
				home_replace(NULL, files_found[k], NameBuff, MAXPATHL);
				p = NameBuff;
			}
			else
			{
				j = FALSE;
				p = files_found[k];
			}
			lastlen = msg_outtrans_attr(p, j ? attr : 0);
		}
		msg_putchar('\n');
		flushbuf();					/* show one line at a time */
		if (got_int)
		{
			got_int = FALSE;
			break;
		}
	}
	vim_free(file_str);
	FreeWild(num_files, files_found);

/*
 * we redraw the command below the lines that we have just listed
 * This is a bit tricky, but it saves a lot of screen updating.
 */
	cmdline_row = msg_row;		/* will put it back later */

	expand_interactively = FALSE;
	return OK;
}

/*
 * Prepare a string for expansion.
 * When expanding file names:  The string will be used with ExpandWildCards().
 * Copy the file name into allocated memory and add a '*' at the end.
 * When expanding other names:  The string will be used with regcomp().  Copy
 * the name into allocated memory and add ".*" at the end.
 */
	char_u *
addstar(fname, len)
	char_u	*fname;
	int		len;
{
	char_u	*retval;
	int		i, j;
	int		new_len;
	char_u	*tail;

	if (expand_interactively && expand_context != EXPAND_FILES &&
										 expand_context != EXPAND_DIRECTORIES)
	{
		/*
		 * Matching will be done internally (on something other than files).
		 * So we convert the file-matching-type wildcards into our kind for
		 * use with vim_regcomp().  First work out how long it will be:
		 */

		/* for help tags the translation is done in find_help_tags() */
		if (expand_context == EXPAND_HELP)
			retval = vim_strnsave(fname, len);
		else
		{
			new_len = len + 2;			/* +2 for '^' at start, NUL at end */
			for (i = 0; i < len; i++)
			{
				if (fname[i] == '*' || fname[i] == '~')
					new_len++;			/* '*' needs to be replaced by ".*"
										   '~' needs to be replaced by "\~" */

				/* Buffer names are like file names.  "." should be literal */
				if (expand_context == EXPAND_BUFFERS && fname[i] == '.')
					new_len++;			/* "." becomes "\." */
			}
			retval = alloc(new_len);
			if (retval != NULL)
			{
				retval[0] = '^';
				j = 1;
				for (i = 0; i < len; i++, j++)
				{
					if (fname[i] == '\\' && ++i == len)	/* skip backslash */
						break;

					switch (fname[i])
					{
						case '*':	retval[j++] = '.';
									break;
						case '~':	retval[j++] = '\\';
									break;
						case '?':	retval[j] = '.';
									continue;
						case '.':	if (expand_context == EXPAND_BUFFERS)
										retval[j++] = '\\';
									break;
					}
					retval[j] = fname[i];
				}
				retval[j] = NUL;
			}
		}
	}
	else
	{
		retval = alloc(len + 4);
		if (retval != NULL)
		{
			STRNCPY(retval, fname, len);
			retval[len] = NUL;
			backslash_halve(retval, TRUE);		/* remove some backslashes */
			len = STRLEN(retval);

			/*
			 * Don't add a star to ~, ~user, $var or `cmd`.
			 * ~ would be at the start of the tail.
			 * $ could be anywhere in the tail.
			 * ` could be anywhere in the file name.
			 */
			tail = gettail(retval);
			if (*tail != '~' && vim_strchr(tail, '$') == NULL
										   && vim_strchr(retval, '`') == NULL)
			{
#ifdef MSDOS
				/*
				 * if there is no dot in the file name, add "*.*" instead of
				 * "*".
				 */
				for (i = len - 1; i >= 0; --i)
					if (vim_strchr((char_u *)".\\/:", retval[i]) != NULL)
						break;
				if (i < 0 || retval[i] != '.')
				{
					retval[len++] = '*';
					retval[len++] = '.';
				}
#endif
				retval[len++] = '*';
			}
			retval[len] = NUL;
		}
	}
	return retval;
}

/*
 * Must parse the command line so far to work out what context we are in.
 * Completion can then be done based on that context.
 * This routine sets two global variables:
 *	char_u *expand_pattern	The start of the pattern to be expanded within
 *								the command line (ends at the cursor).
 *	int expand_context		The type of thing to expand.  Will be one of:
 *
 *	EXPAND_UNSUCCESSFUL		Used sometimes when there is something illegal on
 *							the command line, like an unknown command.  Caller
 *							should beep.
 *	EXPAND_NOTHING			Unrecognised context for completion, use char like
 *							a normal char, rather than for completion.  eg
 *							:s/^I/
 *	EXPAND_COMMANDS			Cursor is still touching the command, so complete
 *							it.
 *	EXPAND_BUFFERS			Complete file names for :buf and :sbuf commands.
 *	EXPAND_FILES			After command with XFILE set, or after setting
 *	  						with P_EXPAND set.  eg :e ^I, :w>>^I
 *	EXPAND_DIRECTORIES		In some cases this is used instead of the latter
 *	  						when we know only directories are of interest.  eg
 *	  						:set dir=^I
 *	EXPAND_SETTINGS			Complete variable names.  eg :set d^I
 *	EXPAND_BOOL_SETTINGS	Complete boolean variables only,  eg :set no^I
 *	EXPAND_TAGS				Complete tags from the files in p_tags.  eg :ta a^I
 *	EXPAND_HELP				Complete tags from the file 'helpfile'/tags
 *	EXPAND_EVENTS			Complete event names
 *
 * -- webb.
 */
	static void
set_expand_context()
{
	char_u		*nextcomm;
	int			old_char = NUL;

	if (ccline.cmdfirstc != ':')		/* only expansion for ':' commands */
	{
		expand_context = EXPAND_NOTHING;
		return;
	}

	/*
	 * Avoid a UMR warning from Purify, only save the character if it has been
	 * written before.
	 */
	if (ccline.cmdpos < ccline.cmdlen)
		old_char = ccline.cmdbuff[ccline.cmdpos];
	ccline.cmdbuff[ccline.cmdpos] = NUL;
	nextcomm = ccline.cmdbuff;
	while (nextcomm != NULL)
		nextcomm = set_one_cmd_context(nextcomm);
	ccline.cmdbuff[ccline.cmdpos] = old_char;
}

/*
 * Do the expansion based on the global variables expand_context and
 * expand_pattern -- webb.
 */
	static int
ExpandFromContext(pat, num_file, file, files_only, options)
	char_u	*pat;
	int		*num_file;
	char_u	***file;
	int		files_only;
	int		options;
{
	vim_regexp	*prog;
	int			ret;
	int			i;
	int			count;

	if (!expand_interactively || expand_context == EXPAND_FILES)
		return ExpandWildCards(1, &pat, num_file, file, files_only,
											  (options & WILD_LIST_NOTFOUND));
	else if (expand_context == EXPAND_DIRECTORIES)
	{
		if (ExpandWildCards(1, &pat, num_file, file, files_only,
									  (options & WILD_LIST_NOTFOUND)) == FAIL)
			return FAIL;
		count = 0;
		for (i = 0; i < *num_file; i++)
			if (mch_isdir((*file)[i]))
				(*file)[count++] = (*file)[i];
			else
				vim_free((*file)[i]);
		if (count == 0)
		{
			vim_free(*file);
			*file = (char_u **)"";
			*num_file = -1;
			return FAIL;
		}
		*num_file = count;
		return OK;
	}
	*file = (char_u **)"";
	*num_file = 0;
	if (expand_context == EXPAND_OLD_SETTING)
		return ExpandOldSetting(num_file, file);

	if (expand_context == EXPAND_HELP)
		return find_help_tags(pat, num_file, file);

	set_reg_ic(pat);		/* set reg_ic according to p_ic, p_scs and pat */
#ifdef AUTOCMD
	if (expand_context == EXPAND_EVENTS)
		reg_ic = TRUE;		/* always ignore case for events */
#endif

	if (expand_context == EXPAND_BUFFERS)
		return ExpandBufnames(pat, num_file, file, options);

	prog = vim_regcomp(pat, (int)p_magic);
	if (prog == NULL)
		return FAIL;

	if (expand_context == EXPAND_COMMANDS)
		ret = ExpandCommands(prog, num_file, file);
	else if (expand_context == EXPAND_SETTINGS ||
									   expand_context == EXPAND_BOOL_SETTINGS)
		ret = ExpandSettings(prog, num_file, file);
	else if (expand_context == EXPAND_TAGS)
		ret = find_tags(NULL, prog, num_file, file, FALSE, FALSE);
#ifdef AUTOCMD
	else if (expand_context == EXPAND_EVENTS)
		ret = ExpandEvents(prog, num_file, file);
#endif
#ifdef USE_GUI
	else if (expand_context == EXPAND_MENUS)
		ret = gui_ExpandMenuNames(prog, num_file, file);
#endif
	else
		ret = FAIL;

	vim_free(prog);
	return ret;
}

/*********************************
 *  Command line history stuff   *
 *********************************/

/*
 * Translate a history character to the associated type number.
 */
	static int
hist_char2type(c)
	int		c;
{
	if (c == ':')
		return HIST_CMD;
	if (c == '=')
		return HIST_EXPR;
	return HIST_SEARCH;		/* must be '?' or '/' */
}

/*
 * Translate a history type number to the associated character.
 */
	static int
hist_type2char(type, use_question)
	int		type;
	int		use_question;			/* use '?' instead of '/' */
{
	if (type == HIST_CMD)
		return ':';
	if (type == HIST_SEARCH)
	{
		if (use_question)
			return '?';
		else
			return '/';
	}
	return '=';
}

/*
 * init_history() - initialize the command line history
 */
	static void
init_history()
{
	int		newlen;			/* new length of history table */
	char_u	**temp;
	int 	i;
	int		j;
	int		type;

	/*
	 * If size of history table changed, reallocate it
	 */
	newlen = (int)p_hi;
	if (newlen != hislen)						/* history length changed */
	{
		for (type = 0; type < HIST_COUNT; ++type)	/* adjust three tables */
		{
			if (newlen)
				temp = (char_u **)lalloc((long_u)(newlen * sizeof(char_u *)),
									TRUE);
			else
				temp = NULL;
			if (newlen == 0 || temp != NULL)
			{
				if (hisidx[type] < 0)			/* there are no entries yet */
				{
					for (i = 0; i < newlen; ++i)
						temp[i] = NULL;
				}
				else if (newlen > hislen)		/* array becomes bigger */
				{
					for (i = 0; i <= hisidx[type]; ++i)
						temp[i] = history[type][i];
					j = i;
					for ( ; i <= newlen - (hislen - hisidx[type]); ++i)
						temp[i] = NULL;
					for ( ; j < hislen; ++i, ++j)
						temp[i] = history[type][j];
				}
				else							/* array becomes smaller or 0 */
				{
					j = hisidx[type];
					for (i = newlen - 1; ; --i)
					{
						if (i >= 0)				/* copy newest entries */
							temp[i] = history[type][j];
						else					/* remove older entries */
							vim_free(history[type][j]);
						if (--j < 0)
							j = hislen - 1;
						if (j == hisidx[type])
							break;
					}
					hisidx[type] = newlen - 1;
				}
				vim_free(history[type]);
				history[type] = temp;
			}
		}
		hislen = newlen;
	}
}

/*
 * Check if command line 'str' is already in history.
 * If 'move_to_front' is TRUE, matching entry is moved to end of history.
 */
	static int
is_in_history(type, str, move_to_front)
	int		type;
	char_u	*str;
	int		move_to_front;		/* Move the entry to the front if it exists */
{
	int		i;
	int		last_i = -1;

	if (hisidx[type] < 0)
		return FALSE;
	i = hisidx[type];
	do
	{
		if (history[type][i] == NULL)
			return FALSE;
		if (STRCMP(str, history[type][i]) == 0)
		{
			if (!move_to_front)
				return TRUE;
			last_i = i;
			break;
		}
		if (--i < 0)
			i = hislen - 1;
	} while (i != hisidx[type]);

	if (last_i >= 0)
	{
		str = history[type][i];
		while (i != hisidx[type])
		{
			if (++i >= hislen)
				i = 0;
			history[type][last_i] = history[type][i];
			last_i = i;
		}
		history[type][i] = str;
		return TRUE;
	}
	return FALSE;
}

/*
 * Add the given string to the given history.  If the string is already in the
 * history then it is moved to the front.  histype may be 0 for the ':'
 * history, or 1 for the '/' history.
 */
	void
add_to_history(histype, new_entry)
	int		histype;
	char_u	*new_entry;
{
	if (hislen != 0 && !is_in_history(histype, new_entry, TRUE))
	{
		if (++hisidx[histype] == hislen)
			hisidx[histype] = 0;
		vim_free(history[histype][hisidx[histype]]);
		history[histype][hisidx[histype]] = vim_strsave(new_entry);
	}
}

#ifdef VIMINFO
static char_u **viminfo_history[HIST_COUNT] = {NULL, NULL, NULL};
static int		viminfo_hisidx[HIST_COUNT] = {0, 0, 0};
static int		viminfo_hislen = 0;
static int		viminfo_add_at_front = FALSE;

	void
prepare_viminfo_history(len)
	int len;
{
	int i;
	int num;
	int	type;

	init_history();
	viminfo_add_at_front = (len != 0);
	if (len > hislen)
		len = hislen;

	for (type = 0; type < HIST_COUNT; ++type)
	{
		/*
		 * If there are more spaces available than we request, then fill them
		 * up.
		 */
		for (i = 0, num = 0; i < hislen; i++)
			if (history[type][i] == NULL)
				num++;
		if (num > len)
			len = num;
		viminfo_hisidx[type] = 0;
		if (len <= 0)
			viminfo_history[type] = NULL;
		else
			viminfo_history[type] =
							 (char_u **)lalloc(len * sizeof(char_u *), FALSE);
	}
	viminfo_hislen = len;
	for (type = 0; type < HIST_COUNT; ++type)
		if (viminfo_history[type] == NULL)
			viminfo_hislen = 0;
}

	int
read_viminfo_history(line, fp)
	char_u	*line;
	FILE	*fp;
{
	int		type;

	type = hist_char2type(line[0]);
	if (viminfo_hisidx[type] != viminfo_hislen)
	{
		viminfo_readstring(line);
		if (!is_in_history(type, line + 1, viminfo_add_at_front))
			viminfo_history[type][viminfo_hisidx[type]++] =
														vim_strsave(line + 1);
	}
	return vim_fgets(line, LSIZE, fp);
}

	void
finish_viminfo_history()
{
	int idx;
	int i;
	int	type;

	for (type = 0; type < HIST_COUNT; ++type)
	{
		if (history[type] == NULL)
			return;
		idx = hisidx[type] + viminfo_hisidx[type];
		if (idx >= hislen)
			idx -= hislen;
		if (viminfo_add_at_front)
			hisidx[type] = idx;
		else
		{
			if (hisidx[type] == -1)
				hisidx[type] = hislen - 1;
			do
			{
				if (history[type][idx] != NULL)
					break;
				if (++idx == hislen)
					idx = 0;
			} while (idx != hisidx[type]);
			if (idx != hisidx[type] && --idx < 0)
				idx = hislen - 1;
		}
		for (i = 0; i < viminfo_hisidx[type]; i++)
		{
			history[type][idx] = viminfo_history[type][i];
			if (--idx < 0)
				idx = hislen - 1;
		}
		vim_free(viminfo_history[type]);
		viminfo_history[type] = NULL;
	}
}

	void
write_viminfo_history(fp)
	FILE	*fp;
{
	int		i;
	int		type;
	int		num_saved;

	init_history();
	if (hislen == 0)
		return;
	for (type = 0; type < HIST_COUNT; ++type)
	{
		num_saved = get_viminfo_parameter(hist_type2char(type, FALSE));
		if (num_saved == 0)
			continue;
		if (num_saved < 0)	/* Use default */
			num_saved = hislen;
		fprintf(fp, "\n# %s History (newest to oldest):\n",
							type == HIST_CMD ? "Command Line" :
							type == HIST_SEARCH ? "Search String" :
													"Expression");
		if (num_saved > hislen)
			num_saved = hislen;
		i = hisidx[type];
		if (i >= 0)
			while (num_saved--)
			{
				if (history[type][i] != NULL)
				{
					putc(hist_type2char(type, TRUE), fp);
					viminfo_writestring(fp, history[type][i]);
				}
				if (--i < 0)
					i = hislen - 1;
			}
	}
}
#endif /* VIMINFO */

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