ftp.nice.ch/pub/next/unix/editor/vim.3.0.s.tar.gz#/vim-3.0/src/cmdline.c

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

/* vi:ts=4:sw=4
 *
 * VIM - Vi IMproved		by Bram Moolenaar
 *
 * Read the file "credits.txt" for a list of people who contributed.
 * Read the file "uganda.txt" for copying and usage conditions.
 */

/*
 * cmdline.c: functions for reading in the command line and executing it
 */

#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "param.h"
#include "cmdtab.h"
#include "ops.h"			/* included because we call functions in ops.c */
#include "fcntl.h"			/* for chdir() */

/*
 * variables shared between getcmdline() and redrawcmdline()
 */
static int		 cmdlen;		/* number of chars on command line */
static int		 cmdpos;		/* current cursor position */
static int		 cmdspos;		/* cursor column on screen */
static int		 cmdfirstc; 	/* ':', '/' or '?' */
static char_u	*cmdbuff;		/* pointer to command line buffer */

/*
 * The next two variables contain the bounds of any range given in a command.
 * They are set by docmdline().
 */
static linenr_t 	line1, line2;

static int			forceit;
static int			regname;
static int			quitmore = 0;
static int  		cmd_numfiles = -1;	  /* number of files found by
													filename completion */

static void		putcmdline __ARGS((int, char_u *));
static void		cursorcmd __ARGS((void));
static int		ccheck_abbr __ARGS((int));
static char_u	*DoOneCmd __ARGS((char_u *));
static int		buf_write_all __ARGS((BUF *));
static int		dowrite __ARGS((char_u *, int));
static char_u	*getargcmd __ARGS((char_u **));
static char_u	*checknextcomm __ARGS((char_u *));
static void		domake __ARGS((char_u *));
static int		doarglist __ARGS((char_u *));
static int		check_readonly __ARGS((void));
static int		check_changed __ARGS((BUF *, int, int));
static int		check_changed_any __ARGS((int));
static int		check_more __ARGS((int));
#ifdef WEBB_COMPLETE
static void		vim_strncpy __ARGS((char_u *, char_u *, int));
static int		nextwild __ARGS((char_u *, int));
static int		showmatches __ARGS((char_u *));
static void		set_expand_context __ARGS((int, char_u *));
static char_u	*set_one_cmd_context __ARGS((int, char_u *));
static int		ExpandFromContext __ARGS((char_u *, int *, char_u ***, int, int));
#else
static void		nextwild __ARGS((char_u *, int));
static void		showmatches __ARGS((char_u *, int));
#endif /* WEBB_COMPLETE */
static char_u	*addstar __ARGS((char_u *, int));
static linenr_t get_address __ARGS((char_u **));

/*
 * getcmdline() - accept a command line starting with ':', '!', '/', or '?'
 *
 * For searches the optional matching '?' or '/' is removed.
 *
 * Return OK if there is a commandline, FAIL if not
 */

	int
getcmdline(firstc, buff)
	int			firstc; 	/* either ':', '/', or '?' */
	char_u		*buff;	 	/* buffer for command string */
{
	register char_u 	c;
			 int		cc;
			 int		nextc = 0;
	register int		i;
			 int		retval;
			 int		hiscnt;				/* current history line in use */
	static	 char_u		**history = NULL;	/* history table */
	static	 int		hislen = 0; 		/* actual lengt of history table */
			 int		newlen;				/* new length of history table */
	static	 int		hisidx = -1;		/* last entered entry */
			 char_u		**temp;
			 char_u		*lookfor = NULL;	/* string to match */
			 int		j = -1;
			 int		gotesc = FALSE;		/* TRUE when last char typed was <ESC> */
			 int		do_abbr;			/* when TRUE check for abbr. */

/*
 * set some variables for redrawcmd()
 */
	cmdfirstc = firstc;
	cmdbuff = buff;
	cmdlen = cmdpos = 0;
	cmdspos = 1;
	State = CMDLINE;
	gotocmdline(TRUE, firstc);

/*
 * if size of history table changed, reallocate it
 */
	newlen = (int)p_hi;
	if (newlen != hislen)						/* history length changed */
	{
		if (newlen)
			temp = (char_u **)lalloc((long_u)(newlen * sizeof(char_u *)), TRUE);
		else
			temp = NULL;
		if (newlen == 0 || temp != NULL)
		{
			if (newlen > hislen)			/* array becomes bigger */
			{
				for (i = 0; i <= hisidx; ++i)
					temp[i] = history[i];
				j = i;
				for ( ; i <= newlen - (hislen - hisidx); ++i)
					temp[i] = NULL;
				for ( ; j < hislen; ++i, ++j)
					temp[i] = history[j];
			}
			else							/* array becomes smaller */
			{
				j = hisidx;
				for (i = newlen - 1; ; --i)
				{
					if (i >= 0)
						temp[i] = history[j];	/* copy newest entries */
					else
						free(history[j]);		/* remove older entries */
					if (--j < 0)
						j = hislen - 1;
					if (j == hisidx)
						break;
				}
				hisidx = newlen - 1;
			}
			free(history);
			history = temp;
			hislen = newlen;
		}
	}
	hiscnt = hislen;			/* set hiscnt to impossible history value */

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

	/* collect the command string, handling '\b', @ and much more */
	for (;;)
	{
		cursorcmd();	/* set the cursor on the right spot */
		if (nextc)		/* character remaining from CTRL-V */
		{
			c = nextc;
			nextc = 0;
		}
		else
		{
			c = vgetc();
			if (c == Ctrl('C'))
				got_int = FALSE;
		}

		if (lookfor && c != K_SDARROW && c != K_SUARROW)
		{
			free(lookfor);
			lookfor = NULL;
		}

		if (cmd_numfiles != -1 && !(c == p_wc && KeyTyped) && c != Ctrl('N') &&
						c != Ctrl('P') && c != Ctrl('A') && c != Ctrl('L'))
			(void)ExpandOne(NULL, FALSE, -2);	/* may free expanded file names */

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

		if (c == '\n' || c == '\r' || (c == ESC && !KeyTyped))
		{
			if (ccheck_abbr(c + 0x100))
				continue;
			outchar('\r');		/* show that we got the return */
			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)
		{
#ifdef WEBB_COMPLETE
			if (cmd_numfiles > 0)	/* typed p_wc twice */
				i = nextwild(buff, 3);
			else					/* typed p_wc first time */
				i = nextwild(buff, 0);
			if (c == ESC)
				gotesc = TRUE;
			if (i)
				continue;
#else
			if (cmd_numfiles > 0)	/* typed p_wc twice */
				nextwild(buff, 3);
			else					/* typed p_wc first time */
				nextwild(buff, 0);
			if (c == ESC)
				gotesc = TRUE;
			continue;
#endif /* WEBB_COMPLETE */
		}
		gotesc = FALSE;

		if (c == K_ZERO)		/* NUL is stored as NL */
			c = '\n';

		do_abbr = TRUE;			/* default: check for abbreviation */
		switch (c)
		{
		case BS:
		case DEL:
		case Ctrl('W'):
				/*
				 * delete current character is the same as backspace on next
				 * character, except at end of line
				 */
				if (c == DEL && cmdpos != cmdlen)
					++cmdpos;
				if (cmdpos > 0)
				{
					j = cmdpos;
					if (c == Ctrl('W'))
					{
						while (cmdpos && isspace(buff[cmdpos - 1]))
							--cmdpos;
						i = isidchar(buff[cmdpos - 1]);
						while (cmdpos && !isspace(buff[cmdpos - 1]) && isidchar(buff[cmdpos - 1]) == i)
							--cmdpos;
					}
					else
						--cmdpos;
					cmdlen -= j - cmdpos;
					i = cmdpos;
					while (i < cmdlen)
						buff[i++] = buff[j++];
					redrawcmd();
				}
				else if (cmdlen == 0 && c != Ctrl('W'))
				{
					retval = FAIL;
					msg_pos(-1, 0);
					msg_outchar(' ');	/* delete ':' */
					goto returncmd; 	/* back to cmd mode */
				}
				continue;

/*		case '@':	only in very old vi */
		case Ctrl('U'):
clearline:
				cmdpos = 0;
				cmdlen = 0;
				cmdspos = 1;
				redrawcmd();
				continue;

		case ESC:			/* get here if p_wc != ESC or when ESC typed twice */
		case Ctrl('C'):
do_esc:
				retval = FAIL;
				MSG("");
				goto returncmd; 	/* back to cmd mode */

		case Ctrl('D'):
			{
#ifdef WEBB_COMPLETE
				/* set_expand_context() now finds start of the pattern, so
				 * don't do it here -- webb
				 */
				if (showmatches(buff) == FAIL)
					break;		/* Use ^D as normal char instead */
#else
				for (i = cmdpos; i > 0 && buff[i - 1] != ' '; --i)
						;
				showmatches(&buff[i], cmdpos - i);
#endif /* WEBB_COMPLETE */

				redrawcmd();
				continue;
			}

		case K_RARROW:
		case K_SRARROW:
				do
				{
						if (cmdpos >= cmdlen)
								break;
						cmdspos += charsize(buff[cmdpos]);
						++cmdpos;
				}
				while (c == K_SRARROW && buff[cmdpos] != ' ');
				continue;

		case K_LARROW:
		case K_SLARROW:
				do
				{
						if (cmdpos <= 0)
								break;
						--cmdpos;
						cmdspos -= charsize(buff[cmdpos]);
				}
				while (c == K_SLARROW && buff[cmdpos - 1] != ' ');
				continue;

		case Ctrl('B'):		/* begin of command line */
				cmdpos = 0;
				cmdspos = 1;
				continue;

		case Ctrl('E'):		/* end of command line */
				cmdpos = cmdlen;
				buff[cmdlen] = NUL;
				cmdspos = strsize(buff) + 1;
				continue;

		case Ctrl('A'):		/* all matches */
#ifdef WEBB_COMPLETE
				if (!nextwild(buff, 4))
					break;
#else
				nextwild(buff, 4);
#endif /* WEBB_COMPLETE */
				continue;

		case Ctrl('L'):		/* longest common part */
#ifdef WEBB_COMPLETE
				if (!nextwild(buff, 5))
					break;
#else
				nextwild(buff, 5);
#endif /* WEBB_COMPLETE */
				continue;

		case Ctrl('N'):		/* next match */
		case Ctrl('P'):		/* previous match */
				if (cmd_numfiles > 0)
				{
#ifdef WEBB_COMPLETE
					if (!nextwild(buff, (c == Ctrl('P')) ? 2 : 1))
						break;
#else
					nextwild(buff, (c == Ctrl('P')) ? 2 : 1);
#endif /* WEBB_COMPLETE */
					continue;
				}

		case K_UARROW:
		case K_DARROW:
		case K_SUARROW:
		case K_SDARROW:
				if (hislen == 0)		/* no history */
					continue;

				i = hiscnt;
			
					/* save current command string */
				if (c == K_SUARROW || c == K_SDARROW)
				{
					buff[cmdpos] = NUL;
					if (lookfor == NULL && (lookfor = strsave(buff)) == NULL)
						continue;

					j = STRLEN(lookfor);
				}
				for (;;)
				{
						/* one step backwards */
					if (c == K_UARROW || c == K_SUARROW || c == Ctrl('P'))
					{
						if (hiscnt == hislen)	/* first time */
							hiscnt = hisidx;
						else if (hiscnt == 0 && hisidx != hislen - 1)
							hiscnt = hislen - 1;
						else if (hiscnt != hisidx + 1)
							--hiscnt;
						else					/* at top of list */
							break;
					}
					else	/* one step forwards */
					{
						if (hiscnt == hisidx)	/* on last entry, clear the line */
						{
							hiscnt = hislen;
							goto clearline;
						}
						if (hiscnt == hislen)	/* not on a history line, nothing to do */
							break;
						if (hiscnt == hislen - 1)	/* wrap around */
							hiscnt = 0;
						else
							++hiscnt;
					}
					if (hiscnt < 0 || history[hiscnt] == NULL)
					{
						hiscnt = i;
						break;
					}
					if ((c != K_SUARROW && c != K_SDARROW) || hiscnt == i ||
							STRNCMP(history[hiscnt], lookfor, (size_t)j) == 0)
						break;
				}

				if (hiscnt != i)		/* jumped to other entry */
				{
					STRCPY(buff, history[hiscnt]);
					cmdpos = cmdlen = STRLEN(buff);
					redrawcmd();
				}
				continue;

		case Ctrl('V'):
				putcmdline('^', buff);
				c = get_literal(&nextc);	/* get next (two) character(s) */
				do_abbr = FALSE;			/* don't do abbreviation now */
				break;

#ifdef DIGRAPHS
		case Ctrl('K'):
				putcmdline('?', buff);
			  	c = vgetc();
				if (c == ESC)
					goto do_esc;
				if (charsize(c) == 1)
					putcmdline(c, buff);
				cc = vgetc();
				if (cc == ESC)
					goto do_esc;
				c = getdigraph(c, cc, TRUE);
				break;
#endif /* DIGRAPHS */
		}

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

		if (do_abbr && !isidchar(c) && ccheck_abbr(c))
			continue;

		if (cmdlen < CMDBUFFSIZE - 2)
		{
				for (i = cmdlen++; i > cmdpos; --i)
						buff[i] = buff[i - 1];
				buff[cmdpos] = c;
				msg_outtrans(buff + cmdpos, cmdlen - cmdpos);
				++cmdpos;
				i = charsize(c);
				cmdspos += i;
		}
		msg_check();
	}
	retval = OK;				/* when we get here we have a valid command line */

returncmd:
	buff[cmdlen] = NUL;
	/*
	 * put line in history buffer
	 */
	if (cmdlen != 0)
	{
		if (hislen != 0)
		{
			if (++hisidx == hislen)
				hisidx = 0;
			free(history[hisidx]);
			history[hisidx] = strsave(buff);
		}
		if (firstc == ':')
		{
			free(new_last_cmdline);
			new_last_cmdline = strsave(buff);
		}
	}

	/*
	 * 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();
	State = NORMAL;
	return retval;
}

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

	buf[0] = c;
	buf[1] = 0;
	msg_outtrans(buf, 1);
	msg_outtrans(buff + cmdpos, cmdlen - cmdpos);
	cursorcmd();
}

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

	void
compute_cmdrow()
{
	cmdline_row = lastwin->w_winpos + lastwin->w_height + lastwin->w_status_height;
}

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

	msg_start();
	msg_outchar(cmdfirstc);
	msg_outtrans(cmdbuff, cmdlen);
	msg_ceol();

	cmdspos = 1;
	for (i = 0; i < cmdlen && i < cmdpos; ++i)
		cmdspos += charsize(cmdbuff[i]);
}

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

/*
 * 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, cmdbuff, cmdpos, 0);
}

/*
 * docmdline(): execute an Ex command line
 *
 * 1. If no line given, get one.
 * 2. Split up in parts separated with '|'.
 *
 * This function may be called recursively!
 *
 * return FAIL if commandline could not be executed, OK otherwise
 */
	int
docmdline(cmdline)
	char_u		*cmdline;
{
	char_u		buff[CMDBUFFSIZE];		/* command line */
	char_u		*nextcomm;

/*
 * 1. If no line given: get one.
 */
	if (cmdline == NULL)
	{
		if (getcmdline(':', buff) == FAIL)
			return FAIL;
	}
	else
	{
		if (STRLEN(cmdline) > (size_t)(CMDBUFFSIZE - 2))
		{
			emsg(e_toolong);
			return FAIL;
		}
		/* Make a copy of the command so we can mess with it. */
		STRCPY(buff, cmdline);
	}

/*
 * 2. Loop for each '|' separated command.
 *    DoOneCmd will set nextcommand to NULL if there is no trailing '|'.
 */
	for (;;)
	{
		nextcomm = DoOneCmd(buff);
		if (nextcomm == NULL)
			break;
		STRCPY(buff, nextcomm);
	}
/*
 * If the command was typed, remember it for register :
 * Do this AFTER executing the command to make :@: work.
 */
	if (cmdline == NULL && new_last_cmdline != NULL)
	{
		free(last_cmdline);
		last_cmdline = new_last_cmdline;
		new_last_cmdline = NULL;
	}
	return OK;
}

/*
 * Execute one Ex command.
 *
 * 2. skip comment lines and leading space
 * 3. parse range
 * 4. parse command
 * 5. parse arguments
 * 6. switch on command name
 *
 * This function may be called recursively!
 */
	static char_u *
DoOneCmd(buff)
	char_u *buff;
{
	char_u				cmdbuf[CMDBUFFSIZE];	/* for '%' and '#' expansion */
	char_u				c;
	register char_u		*p;
	char_u				*q;
	char_u				*cmd, *arg;
	char_u				*editcmd = NULL;		/* +command arg. for doecmd() */
	linenr_t 			doecmdlnum = 0;			/* lnum in new file for doecmd() */
	int 				i = 0;					/* init to shut up gcc */
	int					cmdidx;
	int					argt;
	register linenr_t	lnum;
	long				n;
	int					addr_count;	/* number of address specifications */
	FPOS				pos;
	int					append = FALSE;			/* write with append */
	int					usefilter = FALSE;		/* filter instead of file name */
	char_u				*nextcomm = NULL;		/* no next command yet */
	int					amount = 0;				/* for ":>" and ":<"; init for gcc */

	if (quitmore)
		--quitmore;		/* when not editing the last file :q has to be typed twice */
/*
 * 2. skip comment lines and leading space, colons or bars
 */
	for (cmd = buff; *cmd && strchr(" \t:|", *cmd) != NULL; cmd++)
		;

	if (*cmd == '"' || *cmd == NUL)	/* ignore comment and empty lines */
		goto doend;

/*
 * 3. parse a range specifier of the form: addr [,addr] [;addr] ..
 *
 * where 'addr' is:
 *
 * %		  (entire file)
 * $  [+-NUM]
 * 'x [+-NUM] (where x denotes a currently defined mark)
 * .  [+-NUM]
 * [+-NUM]..
 * NUM
 *
 * The cmd pointer is updated to point to the first character following the
 * range spec. If an initial address is found, but no second, the upper bound
 * is equal to the lower.
 */

	addr_count = 0;
	--cmd;
	do
	{
		++cmd;							/* skip ',' or ';' */
		line1 = line2;
		line2 = curwin->w_cursor.lnum;			/* default is current line number */
		skipspace(&cmd);
		lnum = get_address(&cmd);
		if (lnum == MAXLNUM)
		{
			if (*cmd == '%')            /* '%' - all lines */
			{
				++cmd;
				line1 = 1;
				line2 = curbuf->b_ml.ml_line_count;
				++addr_count;
			}
		}
		else
			line2 = lnum;
		addr_count++;

		if (*cmd == ';')
		{
			if (line2 == 0)
				curwin->w_cursor.lnum = 1;
			else
				curwin->w_cursor.lnum = line2;
		}
	} while (*cmd == ',' || *cmd == ';');

	/* One address given: set start and end lines */
	if (addr_count == 1)
	{
		line1 = line2;
			/* ... but only implicit: really no address given */
		if (lnum == MAXLNUM)
			addr_count = 0;
	}

/*
 * 4. parse command
 */

	skipspace(&cmd);

	/*
	 * If we got a line, but no command, then go to the line.
	 * If we find a '|' or '\n' we set nextcomm.
	 */
	if (*cmd == NUL || *cmd == '"' ||
			((*cmd == '|' || *cmd == '\n') &&
					(nextcomm = cmd + 1) != NULL))		/* just an assignment */
	{
		if (addr_count != 0)
		{
			/*
			 * strange vi behaviour: ":3" jumps to line 3
			 * ":3|..." prints line 3
			 */
			if (*cmd == '|')
			{
				cmdidx = CMD_print;
				goto cmdswitch;			/* UGLY goto */
			}
			if (line2 == 0)
				curwin->w_cursor.lnum = 1;
			else if (line2 > curbuf->b_ml.ml_line_count)
				curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
			else
				curwin->w_cursor.lnum = line2;
			curwin->w_cursor.col = 0;
			cursupdate();
		}
		goto doend;
	}

	/*
	 * Isolate the command and search for it in the command table.
	 * Exeptions:
	 * - the 'k' command can directly be followed by any character.
	 * - the 's' command can be followed directly by 'c', 'g' or 'r'
	 *		but :sre[wind] is another command.
	 */
	if (*cmd == 'k')
	{
		cmdidx = CMD_k;
		p = cmd + 1;
	}
	else if (*cmd == 's' && strchr("cgr", cmd[1]) != NULL && STRNCMP("sre", cmd, (size_t)3) != 0)
	{
		cmdidx = CMD_substitute;
		p = cmd + 1;
	}
	else
	{
		p = cmd;
		while (isalpha(*p))
			++p;
		if (p == cmd && strchr("@!=><&~#", *p) != NULL)	/* non-alpha command */
			++p;
		i = (int)(p - cmd);

		for (cmdidx = 0; cmdidx < CMD_SIZE; ++cmdidx)
			if (STRNCMP(cmdnames[cmdidx].cmd_name, (char *)cmd, (size_t)i) == 0)
				break;
		if (i == 0 || cmdidx == CMD_SIZE)
		{
			emsg(e_invcmd);
			goto doend;
		}
	}

	if (*p == '!')					/* forced commands */
	{
		++p;
		forceit = TRUE;
	}
	else
		forceit = FALSE;

/*
 * 5. parse arguments
 */
	argt = cmdnames[cmdidx].cmd_argt;

	if (!(argt & RANGE) && addr_count)
	{
		emsg(e_norange);
		goto doend;
	}

/*
 * If the range is backwards, ask for confirmation and, if given, swap
 * line1 & line2 so it's forwards again.
 * When global command is busy, don't ask, will fail below.
 */
	if (!global_busy && line1 > line2)
	{
		if (ask_yesno((char_u *)"Backwards range given, OK to swap") != 'y')
			goto doend;
		lnum = line1;
		line1 = line2;
		line2 = lnum;
	}
	/*
	 * don't complain about the range if it is not used
	 * (could happen if line_count is accidently set to 0)
	 */
	if (line1 < 0 || line2 < 0  || line1 > line2 || ((argt & RANGE) &&
					!(argt & NOTADR) && line2 > curbuf->b_ml.ml_line_count))
	{
		emsg(e_invrange);
		goto doend;
	}

	if ((argt & NOTADR) && addr_count == 0)		/* default is 1, not cursor */
		line2 = 1;

	if (!(argt & ZEROR))			/* zero in range not allowed */
	{
		if (line1 == 0)
			line1 = 1;
		if (line2 == 0)
			line2 = 1;
	}

	/*
	 * for the :make command we insert the 'makeprg' option here,
	 * so things like % get expanded
	 */
	if (cmdidx == CMD_make)
	{
		if (STRLEN(p_mp) + STRLEN(p) + 2 >= (unsigned)CMDBUFFSIZE)
		{
			emsg(e_toolong);
			goto doend;
		}
		STRCPY(cmdbuf, p_mp);
		STRCAT(cmdbuf, " ");
		STRCAT(cmdbuf, p);
		STRCPY(buff,   cmdbuf);
		p = buff;
	}

	arg = p;						/* remember start of argument */
	skipspace(&arg);

	if ((argt & NEEDARG) && *arg == NUL)
	{
		emsg(e_argreq);
		goto doend;
	}

	if (cmdidx == CMD_write)
	{
		if (*arg == '>')						/* append */
		{
			if (*++arg != '>')				/* typed wrong */
			{
				EMSG("Use w or w>>");
				goto doend;
			}
			++arg;
			skipspace(&arg);
			append = TRUE;
		}
		else if (*arg == '!')					/* :w !filter */
		{
			++arg;
			usefilter = TRUE;
		}
	}

	if (cmdidx == CMD_read)
	{
		usefilter = forceit;					/* :r! filter if forceit */
		if (*arg == '!')						/* :r !filter */
		{
			++arg;
			usefilter = TRUE;
		}
	}

	if (cmdidx == CMD_lshift || cmdidx == CMD_rshift)
	{
		amount = 1;
		while (*arg == *cmd)		/* count number of '>' or '<' */
		{
			++arg;
			++amount;
		}
		skipspace(&arg);
	}

	/*
	 * Check for '|' to separate commands and '"' to start comments.
	 * Don't do this for ":read !cmd" and ":write !cmd".
	 */
	if ((argt & TRLBAR) && !usefilter)
	{
		p = arg;
		while (*p)
		{
			if (*p == Ctrl('V'))
			{
				if ((argt & USECTRLV) && p[1] != NUL)	/* skip CTRL-V and next char */
					++p;
				else					/* remove CTRL-V and skip next char */
					STRCPY(p, p + 1);
			}
			else if ((*p == '"' && !(argt & NOTRLCOM)) || *p == '|' || *p == '\n')
			{
				if (*(p - 1) == '\\')	/* remove the backslash */
				{
					STRCPY(p - 1, p);
					--p;
				}
				else
				{
					if (*p == '|' || *p == '\n')
						nextcomm = p + 1;
					*p = NUL;
					break;
				}
			}
			++p;
		}
		if (!(argt & NOTRLCOM))			/* remove trailing spaces */
			del_spaces(arg);
	}

	if ((argt & DFLALL) && addr_count == 0)
	{
		line1 = 1;
		line2 = curbuf->b_ml.ml_line_count;
	}

	regname = 0;
		/* accept numbered register only when no count allowed (:put) */
	if ((argt & REGSTR) && *arg != NUL && is_yank_buffer(*arg, FALSE) && !((argt & COUNT) && isdigit(*arg)))
	{
		regname = *arg;
		++arg;
		skipspace(&arg);
	}

	if ((argt & COUNT) && isdigit(*arg))
	{
		n = getdigits(&arg);
		skipspace(&arg);
		if (n <= 0)
		{
			emsg(e_zerocount);
			goto doend;
		}
		if (argt & NOTADR)		/* e.g. :buffer 2, :sleep 3 */
		{
			line2 = n;
			if (addr_count == 0)
				addr_count = 1;
		}
		else
		{
			line1 = line2;
			line2 += n - 1;
			++addr_count;
		}
	}

	if (!(argt & EXTRA) && strchr("|\"", *arg) == NULL)	/* no arguments allowed */
	{
		emsg(e_trailing);
		goto doend;
	}

	/*
	 * change '%' to curbuf->b_filename, '#' to curwin->w_altfile
	 */
	if (argt & XFILE)
	{
		for (p = arg; *p; ++p)
		{
			c = *p;
			if (c != '%' && c != '#')	/* nothing to expand */
				continue;
			if (*(p - 1) == '\\')		/* remove escaped char */
			{
				STRCPY(p - 1, p);
				--p;
				continue;
			}

			if (c == '%')				/* current file */
			{
				if (check_fname() == FAIL)
					goto doend;
				q = curbuf->b_xfilename;
				n = 1;					/* length of what we expand */
			}
			else						/* '#': alternate file */
			{
				q = p + 1;
				i = (int)getdigits(&q);
				n = q - p;				/* length of what we expand */

				if (buflist_name_nr(i, &q, &doecmdlnum) == FAIL)
				{
					emsg(e_noalt);
					goto doend;
				}
			}
			i = STRLEN(arg) + STRLEN(q) + 3;
			if (nextcomm)
				i += STRLEN(nextcomm);
			if (i > CMDBUFFSIZE)
			{
				emsg(e_toolong);
				goto doend;
			}
			/*
			 * we built the new argument in cmdbuf[], then copy it back to buff[]
			 */
			*p = NUL;							/* truncate at the '#' or '%' */
			STRCPY(cmdbuf, arg);				/* copy up to there */
			i = p - arg;						/* remember the lenght */
			STRCAT(cmdbuf, q);					/* append the file name */
			if (*(p + n) == '<')				/* may remove extension */
			{
				++n;
				if ((arg = (char_u *)strrchr((char *)q, '.')) != NULL &&
								arg >= gettail(q))
					*(cmdbuf + (arg - q) + i) = NUL;
			}
			i = STRLEN(cmdbuf);					/* remember the end of the filename */
			STRCAT(cmdbuf, p+n);				/* append what is after '#' or '%' */
			p = buff + i - 1;					/* remember where to continue */
			if (nextcomm)						/* append next command */
			{
				i = STRLEN(cmdbuf) + 1;
				STRCPY(cmdbuf + i, nextcomm);
				nextcomm = buff + i;
			}
			STRCPY(buff, cmdbuf);				/* copy back to buff[] */
			arg = buff;
		}

		/*
		 * One file argument: expand wildcards.
		 * Don't do this with ":r !command" or ":w !command".
		 */
		if (argt & NOSPC)
		{
			if (has_wildcard(arg) && !usefilter)
			{
				if ((p = ExpandOne(arg, TRUE, -1)) == NULL)
					goto doend;
				if (STRLEN(p) + arg - buff < CMDBUFFSIZE - 2)
					STRCPY(arg, p);
				else
					emsg(e_toolong);
				free(p);
			}
		}
	}

/*
 * 6. switch on command name
 */
cmdswitch:
	switch (cmdidx)
	{
		/*
		 * quit current window, quit Vim if closed the last window
		 */
		case CMD_quit:
						/* if more files or windows we won't exit */
				if (check_more(FALSE) == OK && firstwin == lastwin)
					exiting = TRUE;
				if (check_changed(curbuf, FALSE, FALSE) ||
							check_more(TRUE) == FAIL ||
							(firstwin == lastwin && check_changed_any(FALSE)))
				{
					exiting = FALSE;
					settmode(1);
					break;
				}
				if (firstwin == lastwin)	/* quit last window */
					getout(0);
				close_window(TRUE);			/* may free buffer */
				break;

		/*
		 * try to quit all windows
		 */
		case CMD_qall:
				exiting = TRUE;
				if (!check_changed_any(FALSE))
					getout(0);
				exiting = FALSE;
				settmode(1);
				break;

		/*
		 * close current window, unless it is the last one
		 */
		case CMD_close:
				close_window(FALSE);		/* don't free buffer */
				break;

		/*
		 * close all but current window, unless it is the last one
		 */
		case CMD_only:
				close_others(TRUE);
				break;

		case CMD_stop:
		case CMD_suspend:
				if (!forceit)
					autowrite_all();
				gotocmdend();
				flushbuf();
				stoptermcap();
				mch_restore_title(3);	/* restore window titles */
				mch_suspend();			/* call machine specific function */
				maketitle();
				starttermcap();
				if (T_CVV != NULL && *T_CVV)
				{
					/* Scroll screen down before drawing over it */
					outstr(T_CVV);
					outstr(T_CV);
				}
				must_redraw = CLEAR;
				break;

		case CMD_exit:
		case CMD_xit:
		case CMD_wq:
							/* if more files or windows we won't exit */
				if (check_more(FALSE) == OK && firstwin == lastwin)
					exiting = TRUE;
				if (((cmdidx == CMD_wq ||
						(curbuf->b_nwindows == 1 && curbuf->b_changed)) &&
						(check_readonly() || dowrite(arg, FALSE) == FAIL)) ||
					check_more(TRUE) == FAIL || 
					(firstwin == lastwin && check_changed_any(FALSE)))
				{
					exiting = FALSE;
					settmode(1);
					break;
				}
				if (firstwin == lastwin)	/* quit last window, exit Vim */
					getout(0);
				close_window(TRUE);			/* quit current window, may free buffer */
				break;

		case CMD_xall:		/* write all changed files and exit */
		case CMD_wqall:		/* write all changed files and quit */
				exiting = TRUE;
				/* FALLTHROUGH */

		case CMD_wall:		/* write all changed files */
				{
					BUF		*buf;
					int		error = 0;

					for (buf = firstbuf; buf != NULL; buf = buf->b_next)
					{
						if (buf->b_changed)
						{
							if (buf->b_filename == NULL)
							{
								emsg(e_noname);
								++error;
							}
							else if (!forceit && buf->b_p_ro)
							{
								EMSG2("\"%s\" is readonly, use ! to write anyway", buf->b_xfilename);
								++error;
							}
							else if (buf_write_all(buf) == FAIL)
								++error;
						}
					}
					if (exiting)
					{
						if (!error)
							getout(0);			/* exit Vim */
						exiting = FALSE;
						settmode(1);
					}
				}
				break;

		case CMD_preserve:					/* put everything in .swp file */
				ml_preserve(curbuf, TRUE);
				break;

		case CMD_args:		
					/*
					 * ":args file": handle like :next
					 */
				if (*arg != NUL && *arg != '|' && *arg != '\n')
					goto do_next;

				nextcomm = checknextcomm(arg);	/* check for trailing command */
				if (arg_count == 0)			/* no file name list */
				{
					if (check_fname() == OK)		/* check for no file name at all */
						smsg((char_u *)"[%s]", curbuf->b_filename);
					break;
				}
				gotocmdline(TRUE, NUL);
				for (i = 0; i < arg_count; ++i)
				{
					if (i == curwin->w_arg_idx)
						msg_outchar('[');
					msg_outstr(arg_files[i]);
					if (i == curwin->w_arg_idx)
						msg_outchar(']');
					msg_outchar(' ');
				}
				if (msg_check())		/* if message too long */
				{
					msg_outchar('\n');
					wait_return(FALSE);
				}
				break;

		case CMD_wnext:
		case CMD_wNext:
		case CMD_wprevious:
				if (cmd[1] == 'n')
					i = curwin->w_arg_idx + (int)line2;
				else
					i = curwin->w_arg_idx - (int)line2;
				line1 = 1;
				line2 = curbuf->b_ml.ml_line_count;
				if (dowrite(arg, FALSE) == FAIL)
					break;
				goto donextfile;

		case CMD_next:
		case CMD_snext:
do_next:
					/*
					 * check for changed buffer now, if this fails the
					 * argument list is not redefined.
					 */
				if (!(p_hid || cmdidx == CMD_snext) &&
								check_changed(curbuf, TRUE, FALSE))
					break;

				editcmd = getargcmd(&arg);		/* get +command argument */
				nextcomm = checknextcomm(arg);	/* check for trailing command */
				if (*arg != NUL)				/* redefine file list */
				{
					if (doarglist(arg) == FAIL)
						break;
					i = 0;
				}
				else
					i = curwin->w_arg_idx + (int)line2;

donextfile:		if (i < 0 || i >= arg_count)
				{
					if (arg_count == 1)
						EMSG("There is only one file to edit");
					else if (i < 0)
						EMSG("Cannot go before first file");
					else
						EMSG2("No more than %ld files to edit", (char_u *)(long)arg_count);
					break;
				}
				if (*cmd == 's')		/* split window first */
				{
					if (win_split(0L, FALSE) == FAIL)
						break;
				}
				else
				{
					register int other = FALSE;

					/*
					 * if 'hidden' set, only check for changed file when re-editing
					 * the same buffer
					 */
					other = TRUE;
					if (p_hid)
						other = otherfile(fix_fname(arg_files[i]));
					if ((!p_hid || !other) && check_changed(curbuf, TRUE, !other))
					break;
				}
				curwin->w_arg_idx = i;
				(void)doecmd(arg_files[curwin->w_arg_idx], NULL, editcmd, p_hid, (linenr_t)0);
				break;

		case CMD_previous:
		case CMD_sprevious:
		case CMD_Next:
		case CMD_sNext:
				i = curwin->w_arg_idx - (int)line2;
				goto doargument;

		case CMD_rewind:
		case CMD_srewind:
				i = 0;
				goto doargument;

		case CMD_last:
		case CMD_slast:
				i = arg_count - 1;
				goto doargument;

		case CMD_argument:
		case CMD_sargument:
				if (addr_count)
					i = line2 - 1;
				else
					i = curwin->w_arg_idx;
doargument:
				editcmd = getargcmd(&arg);		/* get +command argument */
				nextcomm = checknextcomm(arg);	/* check for trailing command */
				goto donextfile;

		case CMD_all:
		case CMD_sall:
				do_arg_all();		/* open a window for each argument */
				break;

		case CMD_buffer:			/* :[N]buffer [N]	 to buffer N */
		case CMD_sbuffer:			/* :[N]sbuffer [N]	 to buffer N */
				if (addr_count == 0)		/* default is current buffer */
					(void)do_buffer(*cmd == 's', 0, FORWARD, 0, 0);
				else
					(void)do_buffer(*cmd == 's', 1, FORWARD, (int)line2, 0);
				break;

		case CMD_bmodified:			/* :[N]bmod	[N]		 to next modified buffer */
		case CMD_sbmodified:		/* :[N]sbmod [N]	  to next modified buffer */
				(void)do_buffer(*cmd == 's', 3, FORWARD, (int)line2, 0);
				break;

		case CMD_bnext:				/* :[N]bnext [N]	 to next buffer */
		case CMD_sbnext:			/* :[N]sbnext [N]	  to next buffer */
				(void)do_buffer(*cmd == 's', 0, FORWARD, (int)line2, 0);
				break;

		case CMD_bNext:				/* :[N]bNext [N]	 to previous buffer */
		case CMD_bprevious:			/* :[N]bprevious [N] to previous buffer */
		case CMD_sbNext:			/* :[N]sbNext [N]	  to previous buffer */
		case CMD_sbprevious:		/* :[N]sbprevious [N] to previous buffer */
				(void)do_buffer(*cmd == 's', 0, BACKWARD, (int)line2, 0);
				break;

		case CMD_brewind:			/* :brewind			 to first buffer */
		case CMD_sbrewind:			/* :sbrewind		 to first buffer */
				(void)do_buffer(*cmd == 's', 1, FORWARD, 0, 0);
				break;

		case CMD_blast:				/* :blast        	 to last buffer */
		case CMD_sblast:			/* :sblast        	 to last buffer */
				(void)do_buffer(*cmd == 's', 2, FORWARD, 0, 0);
				break;

		case CMD_bunload:			/* :[N]bunload[!] [N] unload buffer */
				i = 2;
		case CMD_bdelete:			/* :[N]bdelete[!] [N] delete buffer */
				if (cmdidx == CMD_bdelete)
					i = 3;
				/*
				 * addr_count == 0: ":bdel" - delete current buffer
				 * addr_count == 1: ":N bdel" or ":bdel N [N ..] - first delete
				 *					buffer 'line2', then any other arguments.
				 * addr_count == 2: ":N,N bdel" - delete buffers in range
				 */
				if (addr_count == 0)
					(void)do_buffer(i, 0, FORWARD, 0, forceit);
				else
				{
					int do_current = FALSE;		/* delete current buffer? */

					if (addr_count == 2)
						n = line1;
					else
						n = line2;
					for ( ;!got_int; breakcheck())
					{
						/*
						 * delete the current buffer last, otherwise when the
						 * current buffer is deleted, the next buffer becomes
						 * the current one and will be loaded, which may then
						 * also be deleted, etc.
						 */
						if (n == curbuf->b_fnum)
							do_current = TRUE;
						else
							(void)do_buffer(i, 1, FORWARD, (int)n, forceit);
						if (addr_count == 2)
						{
							if (++n > line2)
								break;
						}
						else
						{
							skipspace(&arg);
							if (*arg == NUL)
								break;
							if (!isdigit(*arg))
							{
								emsg(e_trailing);
								break;
							}
							n = getdigits(&arg);
						}
					}
					if (!got_int && do_current)
						(void)do_buffer(i, 1, FORWARD, (int)curbuf->b_fnum, forceit);
				}
				break;

		case CMD_unhide:
		case CMD_sunhide:
				(void)do_buffer_all(FALSE);	/* open a window for loaded buffers */
				break;

		case CMD_ball:
		case CMD_sball:
				(void)do_buffer_all(TRUE);	/* open a window for every buffer */
				break;

		case CMD_buffers:
		case CMD_files:
				buflist_list();
				break;

		case CMD_write:
				if (usefilter)		/* input lines to shell command */
					dofilter(line1, line2, arg, TRUE, FALSE);
				else
					(void)dowrite(arg, append);
				break;

			/*
			 * set screen mode
			 * if no argument given, just get the screen size and redraw
			 */
		case CMD_mode:
				if (*arg == NUL || mch_screenmode(arg) != FAIL)
					set_winsize(0, 0, FALSE);
				break;

				/*
				 * set, increment or decrement current window height
				 */
		case CMD_resize:
				n = atol((char *)arg);
				if (*arg == '-' || *arg == '+')
					win_setheight(curwin->w_height + (int)n);
				else
				{
					if (n == 0)		/* default is very high */
						n = 9999;
					win_setheight((int)n);
				}
				break;

				/*
				 * :split [[+command] file]  split window with current or new file
				 * :new [[+command] file]    split window with no or new file
				 */
		case CMD_split:
		case CMD_new:
				if (win_split(addr_count ? line2 : 0L, FALSE) == FAIL)
					break;
				/*FALLTHROUGH*/

		case CMD_edit:
		case CMD_ex:
		case CMD_visual:
				editcmd = getargcmd(&arg);		/* get +command argument */
				nextcomm = checknextcomm(arg);	/* check for trailing command */
				if ((cmdidx == CMD_new) && *arg == NUL)
					(void)doecmd(NULL, NULL, editcmd, TRUE, (linenr_t)1);
				else if (cmdidx != CMD_split || *arg != NUL)
					(void)doecmd(arg, NULL, editcmd, p_hid, doecmdlnum);
				else
					updateScreen(NOT_VALID);
				break;

		case CMD_file:
				if (*arg != NUL)
				{
					if (setfname(arg, NULL, TRUE) == FAIL)
						break;
					curbuf->b_notedited = TRUE;
					maketitle();
				}
				fileinfo(did_cd);		/* print full filename if :cd used */
				break;

		case CMD_swapname:
				p = curbuf->b_ml.ml_mfp->mf_fname;
				if (p == NULL)
					MSG("No swap file");
				else
					msg(p);
				break;

		case CMD_mfstat:		/* print memfile statistics, for debugging */
				mf_statistics();
				break;

		case CMD_read:
				if (usefilter)
				{
					dofilter(line1, line2, arg, FALSE, TRUE);			/* :r!cmd */
					break;
				}
				if (!u_save(line2, (linenr_t)(line2 + 1)))
					break;
				if (*arg == NUL)
				{
					if (check_fname() == FAIL)	/* check for no file name at all */
						break;
					i = readfile(curbuf->b_filename, curbuf->b_sfilename, line2, FALSE, (linenr_t)0, MAXLNUM);
				}
				else
					i = readfile(arg, NULL, line2, FALSE, (linenr_t)0, MAXLNUM);
				if (i == FAIL)
				{
					emsg2(e_notopen, arg);
					break;
				}
				updateScreen(NOT_VALID);
				break;

		case CMD_cd:
		case CMD_chdir:
#ifdef UNIX
				/*
				 * for UNIX ":cd" means: go to home directory
				 */
				if (*arg == NUL)	 /* use cmdbuf for home directory name */
				{
					expand_env("$HOME", cmdbuf, CMDBUFFSIZE);
					arg = cmdbuf;
				}
#endif
				if (*arg != NUL)
				{
					if (!did_cd)
					{
						BUF		*buf;

							/* use full path from now on for names of files
							 * being edited and swap files */
						for (buf = firstbuf; buf != NULL; buf = buf->b_next)
						{
							buf->b_xfilename = buf->b_filename;
							mf_fullname(buf->b_ml.ml_mfp);
						}
						status_redraw_all();
					}
					did_cd = TRUE;
					if (chdir((char *)arg))
						emsg(e_failed);
					break;
				}
				/*FALLTHROUGH*/

		case CMD_pwd:
				if (vim_dirname(NameBuff, MAXPATHL) == OK)
					msg(NameBuff);
				else
					emsg(e_unknown);
				break;

		case CMD_equal:
				smsg((char_u *)"line %ld", (long)line2);
				break;

		case CMD_list:
				i = curwin->w_p_list;
				curwin->w_p_list = 1;
		case CMD_number:				/* :nu */
		case CMD_pound:					/* :# */
		case CMD_print:					/* :p */
				gotocmdline(TRUE, NUL);
				cursor_off();
				for ( ;!got_int; breakcheck())
				{
					if (curwin->w_p_nu || cmdidx == CMD_number || cmdidx == CMD_pound)
					{
						sprintf((char *)IObuff, "%7ld ", (long)line1);
						msg_outstr(IObuff);
					}
					msg_prt_line(ml_get(line1));
					if (++line1 > line2)
						break;
					msg_outchar('\n');
					flushbuf();			/* show one line at a time */
				}

				if (cmdidx == CMD_list)
					curwin->w_p_list = i;

					/*
					 * if we have one line that runs into the shown command,
					 * or more than one line, call wait_return()
					 * also do this when global_busy, so we remember to call
					 * wait_return at the end of the global command.
					 */
				if (msg_check() || global_busy)
				{
					msg_outchar('\n');
					wait_return(FALSE);
				}
				break;

		case CMD_shell:
				doshell(NULL);
				break;

		case CMD_sleep:
				sleep((int)line2);
				break;

		case CMD_tag:
				dotag(arg, 0, addr_count ? (int)line2 : 1);
				break;

		case CMD_pop:
				dotag((char_u *)"", 1, addr_count ? (int)line2 : 1);
				break;

		case CMD_tags:
				dotags();
				break;

		case CMD_marks:
				domarks();
				break;

		case CMD_jumps:
				dojumps();
				break;

		case CMD_digraphs:
#ifdef DIGRAPHS
				if (*arg)
					putdigraph(arg);
				else
					listdigraphs();
#else
				EMSG("No digraphs in this version");
#endif /* DIGRAPHS */
				break;

		case CMD_set:
				(void)doset(arg);
				break;

		case CMD_abbreviate:
		case CMD_cabbrev:
		case CMD_iabbrev:
		case CMD_cnoreabbrev:
		case CMD_inoreabbrev:
		case CMD_noreabbrev:
		case CMD_unabbreviate:
		case CMD_cunabbrev:
		case CMD_iunabbrev:
				i = ABBREV;
				goto doabbr;		/* almost the same as mapping */

		case CMD_cmap:
		case CMD_imap:
		case CMD_map:
		case CMD_cnoremap:
		case CMD_inoremap:
		case CMD_noremap:
				/*
				 * If we are sourcing .exrc or .vimrc in current directory we
				 * print the mappings for security reasons.
				 */
				if (secure)
				{
					secure = 2;
					msg_outtrans(cmd, -1);
					msg_outchar('\n');
				}
		case CMD_cunmap:
		case CMD_iunmap:
		case CMD_unmap:
				i = 0;
doabbr:
				if (*cmd == 'c')		/* cmap, cunmap, cnoremap, etc. */
				{
					i += CMDLINE;
					++cmd;
				}
				else if (*cmd == 'i')	/* imap, iunmap, inoremap, etc. */
				{
					i += INSERT;
					++cmd;
				}
				else if (forceit || i)	/* map!, unmap!, noremap!, abbrev */
					i += INSERT + CMDLINE;
				else
					i += NORMAL;			/* map, unmap, noremap */
				switch (domap((*cmd == 'n') ? 2 : (*cmd == 'u'), arg, i))
				{
					case 1: emsg(e_invarg);
							break;
					case 2: emsg(e_nomap);
							break;
					case 3: emsg(e_ambmap);
							break;
				}
				break;

		case CMD_display:
				dodis();		/* display buffer contents */
				break;

		case CMD_help:
				help();
				break;

		case CMD_version:
				msg(longVersion);
				break;

		case CMD_winsize:					/* obsolete command */
				line1 = getdigits(&arg);
				skipspace(&arg);
				line2 = getdigits(&arg);
				set_winsize((int)line1, (int)line2, TRUE);
				break;

		case CMD_delete:
		case CMD_yank:
		case CMD_rshift:
		case CMD_lshift:
				yankbuffer = regname;
				curbuf->b_startop.lnum = line1;
				curbuf->b_endop.lnum = line2;
				nlines = line2 - line1 + 1;
				mtype = MLINE;
				curwin->w_cursor.lnum = line1;
				switch (cmdidx)
				{
				case CMD_delete:
					dodelete();
					break;
				case CMD_yank:
					(void)doyank(FALSE);
					curwin->w_cursor.lnum = line2;		/* put cursor on last line */
					break;
				case CMD_rshift:
					doshift(RSHIFT, FALSE, amount);
					break;
				case CMD_lshift:
					doshift(LSHIFT, FALSE, amount);
					break;
				}
				break;

		case CMD_put:
				yankbuffer = regname;
				curwin->w_cursor.lnum = line2;
				doput(forceit ? BACKWARD : FORWARD, -1L, FALSE);
				break;

		case CMD_t:
		case CMD_copy:
		case CMD_move:
				n = get_address(&arg);
				/*
				 * move or copy lines from 'line1'-'line2' to below line 'n'
				 */
				if (n == MAXLNUM || n < 0 || n > curbuf->b_ml.ml_line_count)
				{
					emsg(e_invaddr);
					break;
				}

				if (cmdidx == CMD_move)
				{
					if (do_move(line1, line2, n) == FAIL)
						break;
				}
				else
					do_copy(line1, line2, n);
				u_clearline();
				curwin->w_cursor.col = 0;
				updateScreen(NOT_VALID);
				break;

		case CMD_and:			/* :& */
		case CMD_tilde:			/* :~ */
		case CMD_substitute:	/* :s */
				dosub(line1, line2, arg, &nextcomm,
							cmdidx == CMD_substitute ? 0 :
							cmdidx == CMD_and ? 1 : 2);
				break;

		case CMD_join:
				curwin->w_cursor.lnum = line1;
				if (line1 == line2)
				{
					if (addr_count >= 2)	/* :2,2join does nothing */
						break;
					if (line2 == curbuf->b_ml.ml_line_count)
					{
						beep();
						break;
					}
					++line2;
				}
				dodojoin(line2 - line1 + 1, !forceit, TRUE);
				break;

		case CMD_global:
				if (forceit)
					*cmd = 'v';
		case CMD_vglobal:
				doglob(*cmd, line1, line2, arg);
				break;

		case CMD_at:				/* :[addr]@r */
				curwin->w_cursor.lnum = line2;
				if (doexecbuf(*arg) == FAIL)			/* put the register in mapbuf */
					beep();
				else
					(void)docmdline((char_u *)NULL);	/* execute from the mapbuf */
				break;

		case CMD_bang:
				dobang(addr_count, line1, line2, forceit, arg);
				break;

		case CMD_undo:
				u_undo(1);
				break;

		case CMD_redo:
				u_redo(1);
				break;

		case CMD_source:
				if (forceit)	/* :so! read vi commands */
					(void)openscript(arg);
				else
				{
					++no_wait_return;
					if (dosource(arg) == FAIL)		/* :so read ex commands */
						emsg2(e_notopen, arg);
					--no_wait_return;
					if (need_wait_return)
						wait_return(FALSE);
				}
				break;

		case CMD_mkvimrc:
				if (*arg == NUL)
					arg = (char_u *)VIMRC_FILE;
				/*FALLTHROUGH*/

		case CMD_mkexrc:
				{
					FILE	*fd;

					if (*arg == NUL)
						arg = (char_u *)EXRC_FILE;
#ifdef UNIX
						/* with Unix it is possible to open a directory */
					if (isdir(arg) == TRUE)
					{
						EMSG2("\"%s\" is a directory", arg);
						break;
					}
#endif
					if (!forceit && (fd = fopen((char *)arg, "r")) != NULL)
					{
						fclose(fd);
						EMSG2("\"%s\" exists (use ! to override)", arg);
						break;
					}

					if ((fd = fopen((char *)arg, "w")) == NULL)
					{
						EMSG2("Cannot open \"%s\" for writing", arg);
						break;
					}
					if (makemap(fd) == FAIL || makeset(fd) == FAIL || fclose(fd))
						emsg(e_write);
					break;
				}

		case CMD_cc:
					qf_jump(0, atoi((char *)arg));
					break;

		case CMD_cf:
					if (*arg != NUL)
					{
						/*
						 * Great trick: Insert 'ef=' before arg.
						 * Always ok, because "cf " must be there.
						 */
						arg -= 3;
						arg[0] = 'e';
						arg[1] = 'f';
						arg[2] = '=';
						(void)doset(arg);
					}
					(void)qf_init();
					break;

		case CMD_cl:
					qf_list();
					break;

		case CMD_cn:
					qf_jump(FORWARD, *arg == NUL ? 1 : atoi((char *)arg));
					break;

		case CMD_cp:
					qf_jump(BACKWARD, *arg == NUL ? 1 : atoi((char *)arg));
					break;

		case CMD_cq:
					getout(1);		/* this does not always work. why? */

		case CMD_mark:
		case CMD_k:
					pos = curwin->w_cursor;			/* save curwin->w_cursor */
					curwin->w_cursor.lnum = line2;
					curwin->w_cursor.col = 0;
					(void)setmark(*arg);			/* set mark */
					curwin->w_cursor = pos;			/* restore curwin->w_cursor */
					break;

#ifdef SETKEYMAP
		case CMD_setkeymap:
					set_keymap(arg);
					break;
#endif

		case CMD_center:
		case CMD_right:
		case CMD_left:
					do_align(line1, line2, atoi((char *)arg),
							cmdidx == CMD_center ? 0 : cmdidx == CMD_right ? 1 : -1);
					break;

		case CMD_make:
					domake(arg);
					break;

		default:
					emsg(e_invcmd);
	}


doend:
	forceit = FALSE;		/* reset now so it can be used in getfile() */
	if (nextcomm && *nextcomm == NUL)		/* not really a next command */
		nextcomm = NULL;
	return nextcomm;
}

/*
 * if 'autowrite' option set, try to write the file
 *
 * return FAIL for failure, OK otherwise
 */
	int
autowrite(buf)
	BUF		*buf;
{
	if (!p_aw || (!forceit && buf->b_p_ro) || buf->b_filename == NULL)
		return FAIL;
	return buf_write_all(buf);
}

/*
 * flush all buffers, except the ones that are readonly
 */
	void
autowrite_all()
{
	BUF		*buf;

	if (!p_aw)
		return;
	for (buf = firstbuf; buf; buf = buf->b_next)
		if (buf->b_changed && !buf->b_p_ro)
			(void)buf_write_all(buf);
}

/*
 * flush the contents of a buffer, unless it has no file name
 *
 * return FAIL for failure, OK otherwise
 */
	static int
buf_write_all(buf)
	BUF		*buf;
{
	return (buf_write(buf, buf->b_filename, buf->b_sfilename, (linenr_t)1, buf->b_ml.ml_line_count, 0, 0, TRUE));
}

/*
 * write current buffer to file 'fname'
 * if 'append' is TRUE, append to the file
 *
 * if *fname == NUL write to current file
 * if b_notedited is TRUE, check for overwriting current file
 *
 * return FAIL for failure, OK otherwise
 */
	static int
dowrite(fname, append)
	char_u	*fname;
	int		append;
{
	FILE	*fd;
	int		other;
	char_u	*sfname = NULL;				/* init to shut up gcc */

	if (*fname == NUL)
		other = FALSE;
	else
	{
		sfname = fname;
		fname = fix_fname(fname);
		other = otherfile(fname);
	}

	/*
	 * if we have a new file name put it in the list of alternate file names
	 */
	if (other)
		setaltfname(fname, sfname, (linenr_t)1);

	/*
	 * writing to the current file is not allowed in readonly mode
	 * and need a file name
	 */
	if (!other && (check_readonly() || check_fname() == FAIL))
		return FAIL;

	if (!other)
	{
		fname = curbuf->b_filename;
		sfname = curbuf->b_sfilename;
	}

	/*
	 * write to other file or b_notedited set: overwriting only allowed with '!'
	 */
	if ((other || curbuf->b_notedited) && !forceit && !append && !p_wa && (fd = fopen((char *)fname, "r")) != NULL)
	{								/* don't overwrite existing file */
		fclose(fd);
#ifdef UNIX
			/* with UNIX it is possible to open a directory */
		if (isdir(fname) == TRUE)
			EMSG2("\"%s\" is a directory", fname);
		else
#endif
			emsg(e_exists);
		return FAIL;
	}
	return (buf_write(curbuf, fname, sfname, line1, line2, append, forceit, TRUE));
}

/*
 * start editing a new file
 *
 *    fname: the file name
 *				- full path if sfname used,
 *				- any file name if sfname is NULL
 *				- empty string to re-edit with the same file name (but may be
 *					in a different directory)
 *				- NULL to start an empty buffer
 *   sfname: the short file name (or NULL)
 *  command: the command to be executed after loading the file
 *     hide: if TRUE don't free the current buffer
 *  newlnum: put cursor on this line number (if possible)
 *
 * return FAIL for failure, OK otherwise
 */
	int
doecmd(fname, sfname, command, hide, newlnum)
	char_u		*fname;
	char_u		*sfname;
	char_u		*command;
	int			hide;
	linenr_t	newlnum;
{
	int			other_file;				/* TRUE if editing another file */
	int			oldbuf = FALSE;			/* TRUE if using existing buffer */
	BUF			*buf;

		/* if no short name given, use fname for short name */
	if (sfname == NULL)
		sfname = fname;

	if (fname == NULL)
		other_file = TRUE;
	else if (*fname == NUL && curbuf->b_filename == NULL)	/* there is no file name */
		other_file = FALSE;
	else
	{
		if (*fname == NUL)				/* re-edit with same file name */
		{
			fname = curbuf->b_filename;
			sfname = curbuf->b_sfilename;
		}
		fname = fix_fname(fname);		/* may expand to full path name */
		other_file = otherfile(fname);
	}
/*
 * if the file was changed we may not be allowed to abandon it
 * - if we are going to re-edit the same file
 * - or if we are the only window on this file and if hide is FALSE
 */
	if ((!other_file || (curbuf->b_nwindows == 1 && !hide)) &&
						check_changed(curbuf, FALSE, !other_file))
	{
		if (other_file && fname != NULL)
			setaltfname(fname, sfname, (linenr_t)1);
		return FAIL;
	}
/*
 * If we are starting to edit another file, open a (new) buffer.
 * Otherwise we re-use the current buffer.
 */
	if (other_file)
	{
		curwin->w_alt_fnum = curbuf->b_fnum;
		buflist_altlnum();

		buf = buflist_new(fname, sfname, 1L, TRUE);
		if (buf == NULL)
			return FAIL;
		if (buf->b_ml.ml_mfp == NULL)		/* no memfile yet */
		{
			oldbuf = FALSE;
			buf->b_nwindows = 1;
		}
		else								/* existing memfile */
		{
			oldbuf = TRUE;
			++buf->b_nwindows;
		}
		/*
		 * make the (new) buffer the one used by the current window
		 * if the old buffer becomes unused, free it if hide is FALSE
		 * If the current buffer was empty and has no file name, curbuf
		 * is returned by buflist_new().
		 */
		if (buf != curbuf)
		{
			close_buffer(curbuf, !hide, FALSE);
			curwin->w_buffer = buf;
			curbuf = buf;
		}

		curwin->w_pcmark.lnum = 1;
		curwin->w_pcmark.col = 0;
	}
	else if (check_fname() == FAIL)
		return FAIL;

/*
 * If we get here we are sure to start editing
 */
		/* don't redraw until the cursor is in the right line */
	++RedrawingDisabled;

/*
 * other_file	oldbuf
 *	FALSE		FALSE		re-edit same file, buffer is re-used
 *	FALSE		TRUE		not posible
 *  TRUE		FALSE		start editing new file, new buffer
 *  TRUE		TRUE		start editing in existing buffer (nothing to do)
 */
	if (!other_file)					/* re-use the buffer */
	{
		if (newlnum == 0)
			newlnum = curwin->w_cursor.lnum;
		buf_freeall(curbuf);			/* free all things for buffer */
		buf_clear(curbuf);
		curbuf->b_startop.lnum = 0;		/* clear '[ and '] marks */
		curbuf->b_endop.lnum = 0;
	}

	if (!oldbuf)						/* need to read the file */
		(void)open_buffer();
	win_init(curwin);
	maketitle();

	if (newlnum && command == NULL)
	{
		curwin->w_cursor.lnum = newlnum;
		curwin->w_cursor.col = 0;
	}
	check_cursor();

	/*
	 * Did not read the file, need to show some info about the file.
	 * Do this after setting the cursor.
	 */
	if (oldbuf)
		fileinfo(did_cd);

	if (command != NULL)
		docmdline(command);
	--RedrawingDisabled;
	if (!skip_redraw)
		updateScreen(CURSUPD);			/* redraw now */

	if (p_im)
		stuffReadbuff((char_u *)"i");	/* start editing in insert mode */
	return OK;
}

/*
 * get + command from ex argument
 */
	static char_u *
getargcmd(argp)
	char_u **argp;
{
	char_u *arg = *argp;
	char_u *command = NULL;

	if (*arg == '+')		/* +[command] */
	{
		++arg;
		if (isspace(*arg))
			command = (char_u *)"$";
		else
		{
			command = arg;
			/*
			 * should check for "\ " (but vi has a bug that prevents it to work)
			 */
			skiptospace(&arg);
		}
		if (*arg)
			*arg++ = NUL;	/* terminate command with NUL */
		
		skipspace(&arg);	/* skip over spaces */
		*argp = arg;
	}
	return command;
}

/*
 * look for command separator '|' or '\n'
 */
	static char_u *
checknextcomm(arg)
	char_u *arg;
{
	char_u *p;
	char_u *nextcomm = NULL;

	for (p = arg; *p; ++p)
	{
		if (*p == '\\' && p[1])
			++p;
		else if (*p == '|' || *p == '\n')
		{
			nextcomm = p + 1;	/* remember start of next command */
			*p = NUL;			/* delete '|' or '\n' */
			del_spaces(arg);	/* delete spaces in front of '|' or '\n' */
			break;
		}
	}
	return nextcomm;
}

	static void
domake(arg)
	char_u *arg;
{
	if (*p_ef == NUL)
	{
		EMSG("errorfile option not set");
		return;
	}
	if (curbuf->b_changed)
		(void)autowrite(curbuf);
	remove((char *)p_ef);
	outchar(':');
	outstr(arg);		/* show what we are doing */
	sprintf((char *)IObuff, "%s %s %s", arg, p_sp, p_ef);
	doshell(IObuff);
#ifdef AMIGA
	flushbuf();
	vpeekc();		/* read window status report and redraw before message */
#endif
	(void)qf_init();
	remove((char *)p_ef);
}

/* 
 * Redefine the argument list to 'str'.
 *
 * Return FAIL for failure, OK otherwise.
 */
	static int
doarglist(str)
	char_u *str;
{
	int		new_count = 0;
	char_u	**new_files = NULL;
	int		exp_count;
	char_u	**exp_files;
	char_u	**t;
	char_u	*p;
	int		inquote;
	int		i;

	while (*str)
	{
		/*
		 * create a new entry in new_files[]
		 */
		t = (char_u **)lalloc((long_u)(sizeof(char_u *) * (new_count + 1)), TRUE);
		if (t != NULL)
			for (i = new_count; --i >= 0; )
				t[i] = new_files[i];
		free(new_files);
		if (t == NULL)
			return FAIL;
		new_files = t;
		new_files[new_count++] = str;

		/*
		 * isolate one argument, taking quotes
		 */
		inquote = FALSE;
		for (p = str; *str; ++str)
		{
			/*
			 * for MSDOS a backslash is part of a file name.
			 * Only skip ", space and tab.
			 */
#ifdef MSDOS
			if (*str == '\\' && (str[1] == '"' || str[1] == ' ' || str[1] == '\t'))
#else
			if (*str == '\\' && str[1] != NUL)
#endif
				*p++ = *++str;
			else
			{
				if (!inquote && isspace(*str))
					break;
				if (*str == '"')
					inquote ^= TRUE;
				else
					*p++ = *str;
			}
		}
		skipspace(&str);
		*p = NUL;
	}
	
	i = ExpandWildCards(new_count, new_files, &exp_count, &exp_files, FALSE, TRUE);
	free(new_files);
	if (i == FAIL)
		return FAIL;
	if (exp_count == 0)
	{
		emsg(e_nomatch);
		return FAIL;
	}
	if (arg_exp)				/* arg_files[] has been allocated, free it */
		FreeWild(arg_count, arg_files);
	else
		arg_exp = TRUE;
	arg_files = exp_files;
	arg_count = exp_count;

	/*
	 * put all file names in the buffer list
	 */
	for (i = 0; i < arg_count; ++i)
		(void)buflist_add(arg_files[i]);

	return OK;
}

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

	void
gotocmdend()
{
	windgoto((int)Rows - 1, 0);
	outchar('\n');
}

	static int
check_readonly()
{
	if (!forceit && curbuf->b_p_ro)
	{
		emsg(e_readonly);
		return TRUE;
	}
	return FALSE;
}

/*
 * return TRUE if buffer was changed and cannot be abandoned.
 */
	static int
check_changed(buf, checkaw, mult_win)
	BUF		*buf;
	int		checkaw;		/* do autowrite if buffer was changed */
	int		mult_win;		/* check also when several windows for this buffer */
{
	if (	!forceit &&
			buf->b_changed && (mult_win || buf->b_nwindows <= 1) &&
			(!checkaw || autowrite(buf) == FAIL))
	{
		emsg(e_nowrtmsg);
		return TRUE;
	}
	return FALSE;
}

/*
 * return TRUE if any buffer was changed and cannot be abandoned.
 */
	static int
check_changed_any(checkaw)
	int		checkaw;		/* do autowrite if buffer was changed */
{
	BUF		*buf;

	if (!forceit)
	{
		for (buf = firstbuf; buf != NULL; buf = buf->b_next)
		{
			if (buf->b_changed && (!checkaw || autowrite(buf) == FAIL))
			{
				EMSG2("No write since last change for buffer \"%s\"",
						buf->b_xfilename == NULL ? (char_u *)"No File" : buf->b_xfilename);
				return TRUE;
			}
		}
	}
	return FALSE;
}

/*
 * return FAIL if there is no filename, OK if there is one
 * give error message for FAIL
 */
	int
check_fname()
{
	if (curbuf->b_filename == NULL)
	{
		emsg(e_noname);
		return FAIL;
	}
	return OK;
}

/*
 * - if there are more files to edit
 * - and this is the last window
 * - and forceit not used
 * - and not repeated twice on a row
 *	  return FAIL and give error message if 'message' TRUE
 * return OK otherwise
 */
	static int
check_more(message)
	int message;			/* when FALSE check only, no messages */
{
	if (!forceit && firstwin == lastwin && curwin->w_arg_idx + 1 < arg_count &&
									quitmore == 0)
	{
		if (message)
		{
			emsg2((char_u *)"%ld more files to edit", (char_u *)(long)(arg_count - curwin->w_arg_idx - 1));
			quitmore = 2;			/* next try to quit is allowed */
		}
		return FAIL;
	}
	return OK;
}

/*
 * try to abandon current file and edit "fname"
 * return 1 for "normal" error, 2 for "not written" error, 0 for success
 * -1 for succesfully opening another file
 * 'lnum' is the line number for the cursor in the new file (if non-zero).
 */
	int
getfile(fname, sfname, setpm, lnum)
	char_u		*fname;
	char_u		*sfname;
	int			setpm;
	linenr_t	lnum;
{
	int other;

	fname_expand(&fname, &sfname);	/* make fname full path and set sfname */
	other = otherfile(fname);

	if (other && !forceit && curbuf->b_nwindows == 1 &&
			!p_hid && curbuf->b_changed && autowrite(curbuf) == FAIL)
	{
		emsg(e_nowrtmsg);
		return 2;		/* file has been changed */
	}
	if (setpm)
		setpcmark();
	if (!other)
	{
		if (lnum != 0)
			curwin->w_cursor.lnum = lnum;
		check_cursor();
		curwin->w_cursor.col = 0;

		return 0;		/* it's in the same file */
	}
	if (doecmd(fname, sfname, NULL, p_hid, lnum) == OK)
		return -1;		/* opened another file */
	return 1;			/* error encountered */
}

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

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

/* 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
#else
	static void
#endif /* WEBB_COMPLETE */
nextwild(buff, type)
	char_u *buff;
	int		type;
{
	int		i;
	char_u	*p1;
	char_u	*p2 = NULL;
	int		oldlen;
	int		difflen;

#ifdef WEBB_COMPLETE
	if (cmd_numfiles == -1)
		set_expand_context(cmdfirstc, cmdbuff);
	if (expand_context == EXPAND_UNSUCCESSFUL)
	{
		beep();
		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;

#endif /* WEBB_COMPLETE */
	msg_outstr((char_u *)"...");		/* show that we are busy */
	flushbuf();

#ifdef WEBB_COMPLETE
	i = expand_pattern - buff;
#else
	for (i = cmdpos; i > 0 && buff[i - 1] != ' '; --i)
		;
#endif /* WEBB_COMPLETE */
	oldlen = cmdpos - i;

		/* add a "*" to the file name and expand it */
	if ((p1 = addstar(&buff[i], oldlen)) != NULL)
	{
		if ((p2 = ExpandOne(p1, FALSE, type)) != NULL)
		{
			if (cmdlen + (difflen = STRLEN(p2) - oldlen) > CMDBUFFSIZE - 4)
				emsg(e_toolong);
			else
			{
#ifdef WEBB_COMPLETE
				vim_strncpy(&buff[cmdpos + difflen], &buff[cmdpos], cmdlen - cmdpos);
#else
				STRNCPY(&buff[cmdpos + difflen], &buff[cmdpos], (size_t)(cmdlen - cmdpos));
#endif /* WEBB_COMPLETE */
				STRNCPY(&buff[i], p2, STRLEN(p2));
				cmdlen += difflen;
				cmdpos += difflen;
			}
			free(p2);
		}
		free(p1);
	}
	redrawcmd();
#ifdef WEBB_COMPLETE
	if (cmd_numfiles <= 0 && p2 == NULL)
		beep();
	else if (cmd_numfiles == 1)
	{
		(void)ExpandOne(NULL, FALSE, -2);	/* free expanded "file" names */
		cmd_numfiles = -1;
	}
	expand_interactively = FALSE;
	return OK;
#endif /* WEBB_COMPLETE */
}

/*
 * Do wildcard expansion on the string 'str'.
 * Return a pointer to alloced memory containing the new string.
 * Return NULL for failure.
 *
 * mode = -2: only release file names
 * mode = -1: normal expansion, do not keep file names
 * mode =  0: normal expansion, keep file names
 * mode =  1: use next match in multiple match
 * mode =  2: use previous match in multiple match
 * mode =  3: use next match in multiple match and wrap to first
 * mode =  4: return all matches concatenated
 * mode =  5: return longest matched part
 */
	char_u *
ExpandOne(str, list_notfound, mode)
	char_u	*str;
	int		list_notfound;
	int		mode;
{
	char_u		*ss = NULL;
	static char_u **cmd_files = NULL;	  /* list of input files */
	static int	findex;
	int			i, found = 0;
	int			multmatch = FALSE;
	long_u		len;
	char_u		*filesuf, *setsuf, *nextsetsuf;
	int			filesuflen, setsuflen;

/*
 * first handle the case of using an old match
 */
	if (mode >= 1 && mode < 4)
	{
		if (cmd_numfiles > 0)
		{
			if (mode == 2)
				--findex;
			else	/* mode == 1 || mode == 3 */
				++findex;
			if (findex < 0)
				findex = 0;
			if (findex > cmd_numfiles - 1)
			{
				if (mode == 3)
					findex = 0;
				else
					findex = cmd_numfiles - 1;
			}
			return strsave(cmd_files[findex]);
		}
		else
			return NULL;
	}

/* free old names */
	if (cmd_numfiles != -1 && mode < 4)
	{
		FreeWild(cmd_numfiles, cmd_files);
		cmd_numfiles = -1;
	}
	findex = 0;

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

	if (cmd_numfiles == -1)
	{
#ifdef WEBB_COMPLETE
		if (ExpandFromContext((char_u *)str, &cmd_numfiles, &cmd_files, FALSE,
														list_notfound) == FAIL)
			/* error: do nothing */;
		else if (cmd_numfiles == 0)
		{
			if (!expand_interactively)
				emsg(e_nomatch);
		}
#else
		if (ExpandWildCards(1, (char_u **)&str, &cmd_numfiles, &cmd_files, FALSE, list_notfound) == FAIL)
			/* error: do nothing */;
		else if (cmd_numfiles == 0)
			emsg(e_nomatch);
#endif /* WEBB_COMPLETE */
		else if (mode < 4)
		{
			if (cmd_numfiles > 1)		/* more than one match; check suffixes */
			{
				found = -2;
				for (i = 0; i < cmd_numfiles; ++i)
				{
					if ((filesuf = STRRCHR(cmd_files[i], '.')) != NULL)
					{
						filesuflen = STRLEN(filesuf);
						for (setsuf = p_su; *setsuf; setsuf = nextsetsuf)
						{
							if ((nextsetsuf = STRCHR(setsuf + 1, '.')) == NULL)
								nextsetsuf = setsuf + STRLEN(setsuf);
							setsuflen = (int)(nextsetsuf - setsuf);
							if (filesuflen == setsuflen &&
										STRNCMP(setsuf, filesuf, (size_t)setsuflen) == 0)
								break;
						}
						if (*setsuf)				/* suffix matched: ignore file */
							continue;
					}
					if (found >= 0)
					{
						multmatch = TRUE;
						break;
					}
					found = i;
				}
			}
			if (multmatch || found < 0)
			{
#ifdef WEBB_COMPLETE
				/* 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)
#endif /* WEBB_COMPLETE */
					emsg(e_toomany);
#ifdef WEBB_COMPLETE
				else
					beep();
#endif /* WEBB_COMPLETE */
				found = 0;				/* return first one */
				multmatch = TRUE;		/* for found < 0 */
			}
			if (found >= 0 && !(multmatch && mode == -1))
				ss = strsave(cmd_files[found]);
		}
	}

	if (mode == 5 && cmd_numfiles > 0)		/* find longest common part */
	{
		for (len = 0; cmd_files[0][len]; ++len)
		{
			for (i = 0; i < cmd_numfiles; ++i)
			{
#ifdef AMIGA
				if (toupper(cmd_files[i][len]) != toupper(cmd_files[0][len]))
#else
				if (cmd_files[i][len] != cmd_files[0][len])
#endif
					break;
			}
			if (i < cmd_numfiles)
				break;
		}
		ss = alloc((unsigned)len + 1);
		if (ss)
		{
			STRNCPY(ss, cmd_files[0], (size_t)len);
			ss[len] = NUL;
		}
		multmatch = TRUE;					/* don't free the names */
		findex = -1;						/* next p_wc gets first one */
	}

	if (mode == 4 && cmd_numfiles > 0)		/* concatenate all file names */
	{
		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, " ");
			}
		}
	}

#ifdef WEBB_COMPLETE
	if (mode == -1 || mode == 4)
#else
	if (!multmatch || mode == -1 || mode == 4)
#endif /* WEBB_COMPLETE */
	{
		FreeWild(cmd_numfiles, cmd_files);
		cmd_numfiles = -1;
	}
	return ss;
}

/*
 * show all filenames that match the string "file" with length "len"
 */
#ifdef WEBB_COMPLETE
	static int
showmatches(buff)
	char_u *buff;
#else
	static void
showmatches(file, len)
	char_u *file;
	int	len;
#endif /* WEBB_COMPLETE */
{
	char_u *file_str;
	int num_files;
	char_u **files_found;
	int i, j, k;
	int maxlen;
	int lines;
	int columns;

#ifdef WEBB_COMPLETE
	set_expand_context(cmdfirstc, cmdbuff);
	if (expand_context == EXPAND_UNSUCCESSFUL)
	{
		beep();
		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)(buff + cmdpos - expand_pattern));
	if (file_str == NULL)
	{
		expand_interactively = FALSE;
		return OK;
	}
#else
	file_str = addstar(file, len);		/* add star to file name */
	if (file_str == NULL)
		return;
#endif /* WEBB_COMPLETE */

	msg_outchar('\n');
	flushbuf();

	/* find all files that match the description */
#ifdef WEBB_COMPLETE
	if (ExpandFromContext(file_str, &num_files, &files_found, FALSE, FALSE) == FAIL)
	{
		num_files = 0;
		files_found = (char_u **)"";
	}
#else
	if (ExpandWildCards(1, &file_str, &num_files, &files_found, FALSE, FALSE) == FAIL)
		return;
#endif /* WEBB_COMPLETE */

	/* find the maximum length of the file names */
	maxlen = 0;
	for (i = 0; i < num_files; ++i)
	{
		j = STRLEN(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;

	(void)set_highlight('d');	/* find out highlight mode for directories */

	/* list the files line by line */
	for (i = 0; i < lines; ++i)
	{
		for (k = i; k < num_files; k += lines)
		{
			if (k > i)
				for (j = maxlen - STRLEN(files_found[k - lines]); --j >= 0; )
					msg_outchar(' ');
#ifdef WEBB_COMPLETE
			if (expand_context == EXPAND_FILES)
				j = isdir(files_found[k]);	/* highlight directories */
			else
				j = FALSE;
#else
			j = isdir(files_found[k]);	/* highlight directories */
#endif /* WEBB_COMPLETE */
			if (j)
			{
				start_highlight();
				screen_start();		/* don't output spaces to position cursor */
			}
			msg_outstr(files_found[k]);
			if (j)
				stop_highlight();
		}
		msg_outchar('\n');
		flushbuf();					/* show one line at a time */
	}
	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 */
#ifdef WEBB_COMPLETE

	expand_interactively = FALSE;
	return OK;
#endif /* WEBB_COMPLETE */
}

/*
 * copy the file name into allocated memory and add a '*' at the end
 */
	static char_u *
addstar(fname, len)
	char_u	*fname;
	int		len;
{
	char_u	*retval;
#ifdef WEBB_COMPLETE
	int		i, j;
	int		new_len;
	char_u	save_char;

	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 regcomp().  First work out how long it will be:
		 */
		new_len = len + 2;				/* +2 for '^' at start, NUL at end */
		for (i = 0; i < len; i++)
			if (fname[i] == '*')
				new_len++;				/* '*' needs to be replaced by '.*' */
		retval = alloc(new_len);
		if (retval != NULL)
		{
			retval[0] = '^';
			for (i = 0, j = 1; i < len; i++, j++)
				if (fname[i] == '*')
				{
					retval[j++] = '.';
					retval[j] = '*';
				}
				else if (fname[i] == '?')
					retval[j] = '.';
				else
					retval[j] = fname[i];
			retval[j] = NUL;
		}
	}
	else
	{
		retval = alloc(len + 4);
		if (retval != NULL)
		{
			STRNCPY(retval, fname, (size_t)len);
			/*
			 * Don't add a star to ~ or ~user
			 */
			save_char = fname[j = len];
			fname[j] = NUL;
			if (gettail(fname)[0] != '~')
			{
#ifdef MSDOS
			/*
			 * if there is no dot in the file name, add "*.*" instead of "*".
			 */
				for (i = len - 1; i >= 0; --i)
					if (strchr(".\\/:", retval[i]))
						break;
				if (i < 0 || retval[i] != '.')
				{
					retval[len++] = '*';
					retval[len++] = '.';
				}
#endif
				retval[len++] = '*';
			}
			retval[len] = NUL;
			fname[j] = save_char;
		}
	}
#else /* WEBB_COMPLETE */
#ifdef MSDOS
	int		i;
#endif

	retval = alloc(len + 4);
	if (retval != NULL)
	{
		STRNCPY(retval, fname, (size_t)len);
#ifdef MSDOS
	/*
	 * if there is no dot in the file name, add "*.*" instead of "*".
	 */
		for (i = len - 1; i >= 0; --i)
			if (strchr(".\\/:", retval[i]))
				break;
		if (i < 0 || retval[i] != '.')
		{
			retval[len++] = '*';
			retval[len++] = '.';
		}
#endif
		retval[len] = '*';
		retval[len + 1] = 0;
	}
#endif /* WEBB_COMPLETE */
	return retval;
}

/*
 * dosource: read the file "fname" and execute its lines as EX commands
 *
 * This function may be called recursively!
 *
 * return FAIL if file could not be opened, OK otherwise
 */
	int
dosource(fname)
	register char_u *fname;
{
	register FILE	*fp;
	register int	len;
#ifdef MSDOS
	int				error = FALSE;
#endif

	expand_env(fname, NameBuff, MAXPATHL);		/* use NameBuff for expanded name */
	if ((fp = fopen((char *)NameBuff, READBIN)) == NULL)
		return FAIL;

	++dont_sleep;			/* don't call sleep() in emsg() */
	len = 0;
	while (fgets((char *)IObuff + len, IOSIZE - len, fp) != NULL && !got_int)
	{
		len = STRLEN(IObuff) - 1;
		if (len >= 0 && IObuff[len] == '\n')	/* remove trailing newline */
		{
#ifdef MSDOS
			if (len > 0 && IObuff[len - 1] == '\r') /* trailing CR-LF */
				--len;
			else
			{
				if (!error)
					EMSG("Warning: Wrong line separator, ^M may be missing");
				error = TRUE;		/* lines like ":map xx yy^M" will fail */
			}
#endif
				/* escaped newline, read more */
			if (len > 0 && len < IOSIZE && IObuff[len - 1] == Ctrl('V'))
			{
				IObuff[len - 1] = '\n';		/* remove CTRL-V */
				continue;
			}
			IObuff[len] = NUL;
		}
		breakcheck();		/* check for ^C here, so recursive :so will be broken */
		docmdline(IObuff);
		len = 0;
	}
	fclose(fp);
	if (got_int)
		emsg(e_interr);
	--dont_sleep;
	return OK;
}

/*
 * get single EX address
 */
	static linenr_t
get_address(ptr)
	char_u		**ptr;
{
	linenr_t	cursor_lnum = curwin->w_cursor.lnum;
	int			c;
	int			i;
	long		n;
	char_u  	*cmd;
	FPOS		pos;
	FPOS		*fp;
	linenr_t	lnum;

	cmd = *ptr;
	skipspace(&cmd);
	lnum = MAXLNUM;
	do
	{
		switch (*cmd)
		{
			case '.': 						/* '.' - Cursor position */
						++cmd;
						lnum = cursor_lnum;
						break;

			case '$': 						/* '$' - last line */
						++cmd;
						lnum = curbuf->b_ml.ml_line_count;
						break;

			case '\'': 						/* ''' - mark */
						if (*++cmd == NUL || (fp = getmark(*cmd++, FALSE)) == NULL)
						{
							emsg(e_umark);
							goto error;
						}
						lnum = fp->lnum;
						break;

			case '/':
			case '?':						/* '/' or '?' - search */
						c = *cmd++;
						pos = curwin->w_cursor;		/* save curwin->w_cursor */
						curwin->w_cursor.col = -1;	/* searchit() will increment the col */
						if (c == '/')
						{
						 	if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)	/* :/pat on last line */
								curwin->w_cursor.lnum = 1;
							else
								++curwin->w_cursor.lnum;
						}
						searchcmdlen = 0;
						if (dosearch(c, cmd, FALSE, (long)1, FALSE, TRUE))
							lnum = curwin->w_cursor.lnum;
						curwin->w_cursor = pos;
				
						cmd += searchcmdlen;	/* adjust command string pointer */
						break;

			default:
						if (isdigit(*cmd))				/* absolute line number */
							lnum = getdigits(&cmd);
		}
		
		while (*cmd == '-' || *cmd == '+')
		{
			if (lnum == MAXLNUM)
				lnum = cursor_lnum;
			i = *cmd++;
			if (!isdigit(*cmd))	/* '+' is '+1', but '+0' is not '+1' */
				n = 1;
			else 
				n = getdigits(&cmd);
			if (i == '-')
				lnum -= n;
			else
				lnum += n;
		}

		cursor_lnum = lnum;
	} while (*cmd == '/' || *cmd == '?');

error:
	*ptr = cmd;
	return lnum;
}


#ifdef WEBB_COMPLETE
/*
 * 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 somtimes 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_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 bollean variables only,  eg :set no^I
 *	  EXPAND_TAGS --- Complete tags from the files in p_tags.  eg :ta a^I
 *
 * -- webb.
 */
	static void
set_expand_context(firstc, buff)
	int			firstc; 	/* either ':', '/', or '?' */
	char_u		*buff;	 	/* buffer for command string */
{
	char_u		*nextcomm;
	char_u		old_char;

	old_char = cmdbuff[cmdpos];
	cmdbuff[cmdpos] = NUL;
	nextcomm = buff;
	while (nextcomm != NULL)
		nextcomm = set_one_cmd_context(firstc, nextcomm);
	cmdbuff[cmdpos] = old_char;
}

/*
 * This is all pretty much copied from DoOneCmd(), with all the extra stuff we
 * don't need/want deleted.  Maybe this could be done better if we didn't
 * repeat all this stuff.  The only problem is that they may not stay perfectly
 * compatible with each other, but then the command line syntax probably won't
 * change that much -- webb.
 */
	static char_u *
set_one_cmd_context(firstc, buff)
	int			firstc; 	/* either ':', '/', or '?' */
	char_u		*buff;	 	/* buffer for command string */
{
	register char_u		*p;
	char_u				*cmd, *arg;
	int 				i;
	int					cmdidx;
	int					argt;
	char_u				delim;
	int					forced = FALSE;
	int					usefilter = FALSE;	/* filter instead of file name */

	expand_pattern = buff;
	if (firstc != ':')
	{
		expand_context = EXPAND_NOTHING;
		return NULL;
	}
	expand_context = EXPAND_COMMANDS;	/* Default until we get past command */

/*
 * 2. skip comment lines and leading space, colons or bars
 */
	for (cmd = buff; *cmd && strchr(" \t:|", *cmd) != NULL; cmd++)
		;
	expand_pattern = cmd;

	if (*cmd == NUL)
		return NULL;
	if (*cmd == '"')		/* ignore comment lines */
	{
		expand_context = EXPAND_NOTHING;
		return NULL;
	}

/*
 * 3. parse a range specifier of the form: addr [,addr] [;addr] ..
 */

	--cmd;
	do
	{
		++cmd;							/* skip ',' or ';' */
		skipspace(&cmd);
		do
		{
			switch (*cmd)
			{
				case '.': 						/* '.' - Cursor position */
				case '$': 						/* '$' - last line */
				case '%': 						/* '%' - all lines */
					++cmd;
					break;

				case '\'': 						/* ''' - mark */
					if (*++cmd != NUL)
						++cmd;
					break;

				case '/':
				case '?':						/* '/' or '?' - search */
					delim = *cmd++;
					while (*cmd != NUL && *cmd != delim)
						cmd++;
					if (*cmd == delim)
						cmd++;
					break;

				default:
					while (isdigit((char)*cmd))
						++cmd;
					break;
			}
			
			while (*cmd == '-' || *cmd == '+')
			{
				cmd++;
				while (isdigit(*cmd))
					cmd++;
			}
		} while (*cmd == '/' || *cmd == '?');
	} while (*cmd == ',' || *cmd == ';');

/*
 * 4. parse command
 */

	skipspace(&cmd);
	expand_pattern = cmd;
	if (*cmd == NUL)
		return NULL;
	if (*cmd == '"')
	{
		expand_context = EXPAND_NOTHING;
		return NULL;
	}

	if (*cmd == '|' || *cmd == '\n')
		return cmd + 1;					/* There's another command */

	/*
	 * Isolate the command and search for it in the command table.
	 * Exeptions:
	 * - the 'k' command can directly be followed by any character.
	 * - the 's' command can be followed directly by 'c', 'g' or 'r'
	 */
	if (*cmd == 'k')
	{
		cmdidx = CMD_k;
		p = cmd + 1;
	}
	else
	{
		p = cmd;
		while (isalpha(*p) || *p == '*')	/* Allow * wild card */
			++p;
		if (p == cmd && strchr("@!=><&~#", *p) != NULL)	/* non-alpha command */
			++p;
		i = (int)(p - cmd);

		if (i == 0)
		{
			expand_context = EXPAND_UNSUCCESSFUL;
			return NULL;
		}
		for (cmdidx = 0; cmdidx < CMD_SIZE; ++cmdidx)
			if (STRNCMP(cmdnames[cmdidx].cmd_name, cmd, (size_t)i) == 0)
				break;
	}
	if (p == cmdbuff + cmdpos)		/* We are still touching the command */
		return NULL;				/* So complete it */

	if (cmdidx == CMD_SIZE)
	{
		if (*cmd == 's' && strchr("cgr", cmd[1]) != NULL)
		{
			cmdidx = CMD_substitute;
			p = cmd + 1;
		}
		else
		{
			/* Not still touching the command and it was an illegal command */
			expand_context = EXPAND_UNSUCCESSFUL;
			return NULL;
		}
	}

	expand_context = EXPAND_NOTHING; /* Default now that we're past command */

	if (*p == '!')					/* forced commands */
	{
		forced = TRUE;
		++p;
	}

/*
 * 5. parse arguments
 */
	argt = cmdnames[cmdidx].cmd_argt;

	arg = p;						/* remember start of argument */
	skipspace(&arg);

	if (cmdidx == CMD_write)
	{
		if (*arg == '>')						/* append */
		{
			if (*++arg == '>')				/* It should be */
				++arg;
			skipspace(&arg);
		}
		else if (*arg == '!')					/* :w !filter */
		{
			++arg;
			usefilter = TRUE;
		}
	}

	if (cmdidx == CMD_read)
	{
		usefilter = forced;					/* :r! filter if forced */
		if (*arg == '!')						/* :r !filter */
		{
			++arg;
			usefilter = TRUE;
		}
	}

	if (cmdidx == CMD_lshift || cmdidx == CMD_rshift)
	{
		while (*arg == *cmd)		/* allow any number of '>' or '<' */
			++arg;
		skipspace(&arg);
	}

	/*
	 * Check for '|' to separate commands and '"' to start comments.
	 * Don't do this for ":read !cmd" and ":write !cmd".
	 */
	if ((argt & TRLBAR) && !usefilter)
	{
		p = arg;
		while (*p)
		{
			if (*p == Ctrl('V'))
			{
				if (p[1] != NUL)
					++p;
			}
			else if ((*p == '"' && !(argt & NOTRLCOM)) || *p == '|' || *p == '\n')
			{
				if (*(p - 1) != '\\')
				{
					if (*p == '|' || *p == '\n')
						return p + 1;
					return NULL;	/* It's a comment */
				}
			}
			++p;
		}
	}

	if (!(argt & EXTRA) && strchr("|\"", *arg) == NULL)	/* no arguments allowed */
		return NULL;

	/* Find start of last argument (argument just before cursor): */
	p = cmdbuff + cmdpos;
	while (p != arg && *p != ' ' && *p != TAB)
		p--;
	if (*p == ' ' || *p == TAB)
		p++;
	expand_pattern = p;

	if (argt & XFILE)
		expand_context = EXPAND_FILES;

/*
 * 6. switch on command name
 */
	switch (cmdidx)
	{
		case CMD_cd:
		case CMD_chdir:
			expand_context = EXPAND_DIRECTORIES;
			break;
		case CMD_buffer:
		case CMD_wnext:
		case CMD_args:			/* args now takes arguments like :next */
		case CMD_next:
		case CMD_snext:
		case CMD_split:
		case CMD_new:
		case CMD_edit:
		case CMD_ex:
		case CMD_visual:
			for (p = arg; *p; ++p)
			{
				if (*p == '\\' && p[1])
					++p;
				else if (*p == '|' || *p == '\n')
					return p + 1;
			}
			break;
		case CMD_global:
		case CMD_vglobal:
			delim = *arg; 			/* get the delimiter */
			if (delim)
				++arg;				/* skip delimiter if there is one */

			while (arg[0] != NUL && arg[0] != delim)
			{
				if (arg[0] == '\\' && arg[1] != NUL)
					++arg;
				++arg;
			}
			if (arg[0] != NUL)
				return arg + 1;
			break;
		case CMD_and:
		case CMD_substitute:
			delim = *arg;
			if (delim)
				++arg;
			for (i = 0; i < 2; i++, arg++)
				while (arg[0] != NUL && arg[0] != delim)
				{
					if (arg[0] == '\\' && arg[1] != NUL)
						++arg;
					++arg;
				}
			while (arg[0] != NUL && strchr("|\"#", arg[0]) == NULL)
				++arg;
			if (arg[0] != NUL)
				return arg;
			break;
		case CMD_set:
			set_context_in_set_cmd(arg);
			break;
		case CMD_tag:
			expand_context = EXPAND_TAGS;
			expand_pattern = arg;
			break;
		default:
			break;
	}
	return NULL;
}

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

	if (!expand_interactively || expand_context == EXPAND_FILES)
		return ExpandWildCards(1, &pat, num_file, file, files_only, list_notfound);
	else if (expand_context == EXPAND_DIRECTORIES)
	{
		if (ExpandWildCards(1, &pat, num_file, file, files_only, list_notfound)
																	== FAIL)
			return FAIL;
		count = 0;
		for (i = 0; i < *num_file; i++)
			if (isdir((*file)[i]))
				(*file)[count++] = (*file)[i];
			else
				free((*file)[i]);
		if (count == 0)
		{
			free(*file);
			*file = (char_u **)"";
			*num_file = -1;
			return FAIL;
		}
		*num_file = count;
		return OK;
	}
	*file = (char_u **)"";
	*num_file = 0;
	ret = OK;
	reg_ic = FALSE;
	reg_magic = p_magic;
	prog = regcomp(pat);
	if (prog == NULL)
		return FAIL;
	if (expand_context == EXPAND_COMMANDS)
	{
		/* Count the matches: */
		count = 0;
		for (cmdidx = 0; cmdidx < CMD_SIZE; cmdidx++)
			if (regexec(prog, cmdnames[cmdidx].cmd_name, TRUE))
				count++;
		if (count == 0
		  || (*file = (char_u **) alloc((int)(count * sizeof(char_u *)))) == NULL)
			ret = FAIL;
		else
		{
			*num_file = count;
			count = 0;
			for (cmdidx = 0; cmdidx < CMD_SIZE; cmdidx++)
				if (regexec(prog, cmdnames[cmdidx].cmd_name, TRUE))
					(*file)[count++] = strsave(cmdnames[cmdidx].cmd_name);
		}
	}
	else if (expand_context == EXPAND_SETTINGS
	  || expand_context == EXPAND_BOOL_SETTINGS)
		ret = ExpandSettings(prog, num_file, file);
	else if (expand_context == EXPAND_TAGS)
		ret = ExpandTags(prog, num_file, file);
	else
		ret = FAIL;
	
	free(prog);
	return ret;
}
#endif /* WEBB_COMPLETE */

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