723 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			723 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* ex.c */
 | 
						|
 | 
						|
/* Author:
 | 
						|
 *	Steve Kirkendall
 | 
						|
 *	14407 SW Teal Blvd. #C
 | 
						|
 *	Beaverton, OR 97005
 | 
						|
 *	kirkenda@cs.pdx.edu
 | 
						|
 */
 | 
						|
 | 
						|
 | 
						|
/* This file contains the code for reading ex commands. */
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
#include "ctype.h"
 | 
						|
#include "vi.h"
 | 
						|
 | 
						|
/* This data type is used to describe the possible argument combinations */
 | 
						|
typedef short ARGT;
 | 
						|
#define FROM	1		/* allow a linespec */
 | 
						|
#define	TO	2		/* allow a second linespec */
 | 
						|
#define BANG	4		/* allow a ! after the command name */
 | 
						|
#define EXTRA	8		/* allow extra args after command name */
 | 
						|
#define XFILE	16		/* expand wildcards in extra part */
 | 
						|
#define NOSPC	32		/* no spaces allowed in the extra part */
 | 
						|
#define	DFLALL	64		/* default file range is 1,$ */
 | 
						|
#define DFLNONE	128		/* no default file range */
 | 
						|
#define NODFL	256		/* do not default to the current file name */
 | 
						|
#define EXRCOK	512		/* can be in a .exrc file */
 | 
						|
#define NL	1024		/* if mode!=MODE_EX, then write a newline first */
 | 
						|
#define PLUS	2048		/* allow a line number, as in ":e +32 foo" */
 | 
						|
#define ZERO	4096		/* allow 0 to be given as a line number */
 | 
						|
#define NOBAR	8192		/* treat following '|' chars as normal */
 | 
						|
#define FILES	(XFILE + EXTRA)	/* multiple extra files allowed */
 | 
						|
#define WORD1	(EXTRA + NOSPC)	/* one extra word allowed */
 | 
						|
#define FILE1	(FILES + NOSPC)	/* 1 file allowed, defaults to current file */
 | 
						|
#define NAMEDF	(FILE1 + NODFL)	/* 1 file allowed, defaults to "" */
 | 
						|
#define NAMEDFS	(FILES + NODFL)	/* multiple files allowed, default is "" */
 | 
						|
#define RANGE	(FROM + TO)	/* range of linespecs allowed */
 | 
						|
#define NONE	0		/* no args allowed at all */
 | 
						|
 | 
						|
/* This array maps ex command names to command codes. The order in which
 | 
						|
 * command names are listed below is significant -- ambiguous abbreviations
 | 
						|
 * are always resolved to be the first possible match.  (e.g. "r" is taken
 | 
						|
 * to mean "read", not "rewind", because "read" comes before "rewind")
 | 
						|
 */
 | 
						|
static struct
 | 
						|
{
 | 
						|
	char	*name;	/* name of the command */
 | 
						|
	CMD	code;	/* enum code of the command */
 | 
						|
	void	(*fn)();/* function which executes the command */
 | 
						|
	ARGT	argt;	/* command line arguments permitted/needed/used */
 | 
						|
}
 | 
						|
	cmdnames[] =
 | 
						|
{   /*	cmd name	cmd code	function	arguments */
 | 
						|
	{"append",	CMD_APPEND,	cmd_append,	FROM+ZERO+BANG	},
 | 
						|
#ifdef DEBUG
 | 
						|
	{"bug",		CMD_DEBUG,	cmd_debug,	RANGE+BANG+EXTRA+NL},
 | 
						|
#endif
 | 
						|
	{"change",	CMD_CHANGE,	cmd_append,	RANGE+BANG	},
 | 
						|
	{"delete",	CMD_DELETE,	cmd_delete,	RANGE+WORD1	},
 | 
						|
	{"edit",	CMD_EDIT,	cmd_edit,	BANG+FILE1+PLUS	},
 | 
						|
	{"file",	CMD_FILE,	cmd_file,	NAMEDF		},
 | 
						|
	{"global",	CMD_GLOBAL,	cmd_global,	RANGE+BANG+EXTRA+DFLALL+NOBAR},
 | 
						|
	{"insert",	CMD_INSERT,	cmd_append,	FROM+BANG	},
 | 
						|
	{"join",	CMD_INSERT,	cmd_join,	RANGE+BANG	},
 | 
						|
	{"k",		CMD_MARK,	cmd_mark,	FROM+WORD1	},
 | 
						|
	{"list",	CMD_LIST,	cmd_print,	RANGE+NL	},
 | 
						|
	{"move",	CMD_MOVE,	cmd_move,	RANGE+EXTRA	},
 | 
						|
	{"next",	CMD_NEXT,	cmd_next,	BANG+NAMEDFS	},
 | 
						|
	{"Next",	CMD_PREVIOUS,	cmd_next,	BANG		},
 | 
						|
	{"print",	CMD_PRINT,	cmd_print,	RANGE+NL	},
 | 
						|
	{"quit",	CMD_QUIT,	cmd_xit,	BANG		},
 | 
						|
	{"read",	CMD_READ,	cmd_read,	FROM+ZERO+NAMEDF},
 | 
						|
	{"substitute",	CMD_SUBSTITUTE,	cmd_substitute,	RANGE+EXTRA	},
 | 
						|
	{"to",		CMD_COPY,	cmd_move,	RANGE+EXTRA	},
 | 
						|
	{"undo",	CMD_UNDO,	cmd_undo,	NONE		},
 | 
						|
	{"vglobal",	CMD_VGLOBAL,	cmd_global,	RANGE+EXTRA+DFLALL+NOBAR},
 | 
						|
	{"write",	CMD_WRITE,	cmd_write,	RANGE+BANG+FILE1+DFLALL},
 | 
						|
	{"xit",		CMD_XIT,	cmd_xit,	BANG+NL		},
 | 
						|
	{"yank",	CMD_YANK,	cmd_delete,	RANGE+WORD1	},
 | 
						|
 | 
						|
	{"!",		CMD_BANG,	cmd_shell,	EXRCOK+RANGE+NAMEDFS+DFLNONE+NL+NOBAR},
 | 
						|
	{"#",		CMD_NUMBER,	cmd_print,	RANGE+NL	},
 | 
						|
	{"<",		CMD_SHIFTL,	cmd_shift,	RANGE		},
 | 
						|
	{">",		CMD_SHIFTR,	cmd_shift,	RANGE		},
 | 
						|
	{"=",		CMD_EQUAL,	cmd_file,	RANGE		},
 | 
						|
	{"&",		CMD_SUBAGAIN,	cmd_substitute,	RANGE		},
 | 
						|
#ifndef NO_AT
 | 
						|
	{"@",		CMD_AT,		cmd_at,		EXTRA		},
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef NO_ABBR
 | 
						|
	{"abbreviate",	CMD_ABBR,	cmd_map,	EXRCOK+BANG+EXTRA},
 | 
						|
#endif
 | 
						|
	{"args",	CMD_ARGS,	cmd_args,	EXRCOK+NAMEDFS	},
 | 
						|
#ifndef NO_ERRLIST
 | 
						|
	{"cc",		CMD_CC,		cmd_make,	BANG+FILES	},
 | 
						|
#endif
 | 
						|
	{"cd",		CMD_CD,		cmd_cd,		EXRCOK+BANG+NAMEDF},
 | 
						|
	{"copy",	CMD_COPY,	cmd_move,	RANGE+EXTRA	},
 | 
						|
#ifndef NO_DIGRAPH
 | 
						|
	{"digraph",	CMD_DIGRAPH,	cmd_digraph,	EXRCOK+BANG+EXTRA},
 | 
						|
#endif
 | 
						|
#ifndef NO_ERRLIST
 | 
						|
	{"errlist",	CMD_ERRLIST,	cmd_errlist,	BANG+NAMEDF	},
 | 
						|
#endif
 | 
						|
	{"ex",		CMD_EDIT,	cmd_edit,	BANG+FILE1	},
 | 
						|
	{"mark",	CMD_MARK,	cmd_mark,	FROM+WORD1	},
 | 
						|
#ifndef NO_MKEXRC
 | 
						|
	{"mkexrc",	CMD_MKEXRC,	cmd_mkexrc,	NAMEDF		},
 | 
						|
#endif
 | 
						|
	{"number",	CMD_NUMBER,	cmd_print,	RANGE+NL	},
 | 
						|
	{"put",		CMD_PUT,	cmd_put,	FROM+ZERO+WORD1	},
 | 
						|
	{"set",		CMD_SET,	cmd_set,	EXRCOK+EXTRA	},
 | 
						|
	{"shell",	CMD_SHELL,	cmd_shell,	NL		},
 | 
						|
	{"source",	CMD_SOURCE,	cmd_source,	EXRCOK+NAMEDF	},
 | 
						|
#ifdef SIGTSTP
 | 
						|
	{"stop",	CMD_STOP,	cmd_suspend,	NONE		},
 | 
						|
#endif
 | 
						|
	{"tag",		CMD_TAG,	cmd_tag,	BANG+WORD1	},
 | 
						|
	{"version",	CMD_VERSION,	cmd_version,	EXRCOK+NONE	},
 | 
						|
	{"visual",	CMD_VISUAL,	cmd_edit,	BANG+NAMEDF	},
 | 
						|
	{"wq",		CMD_WQUIT,	cmd_xit,	NL		},
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
	{"debug",	CMD_DEBUG,	cmd_debug,	RANGE+BANG+EXTRA+NL},
 | 
						|
	{"validate",	CMD_VALIDATE,	cmd_validate,	BANG+NL		},
 | 
						|
#endif
 | 
						|
	{"chdir",	CMD_CD,		cmd_cd,		EXRCOK+BANG+NAMEDF},
 | 
						|
#ifndef NO_COLOR
 | 
						|
	{"color",	CMD_COLOR,	cmd_color,	EXRCOK+EXTRA	},
 | 
						|
#endif
 | 
						|
#ifndef NO_ERRLIST
 | 
						|
	{"make",	CMD_MAKE,	cmd_make,	BANG+NAMEDFS	},
 | 
						|
#endif
 | 
						|
	{"map",		CMD_MAP,	cmd_map,	EXRCOK+BANG+EXTRA},
 | 
						|
	{"previous",	CMD_PREVIOUS,	cmd_next,	BANG		},
 | 
						|
	{"rewind",	CMD_REWIND,	cmd_next,	BANG		},
 | 
						|
#ifdef SIGTSTP
 | 
						|
	{"suspend",	CMD_SUSPEND,	cmd_suspend,	NONE		},
 | 
						|
#endif
 | 
						|
	{"unmap",	CMD_UNMAP,	cmd_map,	EXRCOK+BANG+EXTRA},
 | 
						|
#ifndef NO_ABBR
 | 
						|
	{"unabbreviate",CMD_UNABBR,	cmd_map,	EXRCOK+WORD1	},
 | 
						|
#endif
 | 
						|
 | 
						|
	{(char *)0}
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/* This function parses a search pattern - given a pointer to a / or ?,
 | 
						|
 * it replaces the ending / or ? with a \0, and returns a pointer to the
 | 
						|
 * stuff that came after the pattern.
 | 
						|
 */
 | 
						|
char	*parseptrn(ptrn)
 | 
						|
	REG char	*ptrn;
 | 
						|
{
 | 
						|
	REG char 	*scan;
 | 
						|
 | 
						|
	for (scan = ptrn + 1;
 | 
						|
	     *scan && *scan != *ptrn;
 | 
						|
	     scan++)
 | 
						|
	{
 | 
						|
		/* allow backslashed versions of / and ? in the pattern */
 | 
						|
		if (*scan == '\\' && scan[1] != '\0')
 | 
						|
		{
 | 
						|
			scan++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (*scan)
 | 
						|
	{
 | 
						|
		*scan++ = '\0';
 | 
						|
	}
 | 
						|
 | 
						|
	return scan;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* This function parses a line specifier for ex commands */
 | 
						|
char *linespec(s, markptr)
 | 
						|
	REG char	*s;		/* start of the line specifier */
 | 
						|
	MARK		*markptr;	/* where to store the mark's value */
 | 
						|
{
 | 
						|
	long		num;
 | 
						|
	REG char	*t;
 | 
						|
 | 
						|
	/* parse each ;-delimited clause of this linespec */
 | 
						|
	do
 | 
						|
	{
 | 
						|
		/* skip an initial ';', if any */
 | 
						|
		if (*s == ';')
 | 
						|
		{
 | 
						|
			s++;
 | 
						|
		}
 | 
						|
 | 
						|
		/* skip leading spaces */
 | 
						|
		while (isspace(*s))
 | 
						|
		{
 | 
						|
			s++;
 | 
						|
		}
 | 
						|
 | 
						|
		/* dot means current position */
 | 
						|
		if (*s == '.')
 | 
						|
		{
 | 
						|
			s++;
 | 
						|
			*markptr = cursor;
 | 
						|
		}
 | 
						|
		/* '$' means the last line */
 | 
						|
		else if (*s == '$')
 | 
						|
		{
 | 
						|
			s++;
 | 
						|
			*markptr = MARK_LAST;
 | 
						|
		}
 | 
						|
		/* digit means an absolute line number */
 | 
						|
		else if (isdigit(*s))
 | 
						|
		{
 | 
						|
			for (num = 0; isdigit(*s); s++)
 | 
						|
			{
 | 
						|
				num = num * 10 + *s - '0';
 | 
						|
			}
 | 
						|
			*markptr = MARK_AT_LINE(num);
 | 
						|
		}
 | 
						|
		/* appostrophe means go to a set mark */
 | 
						|
		else if (*s == '\'')
 | 
						|
		{
 | 
						|
			s++;
 | 
						|
			*markptr = m_tomark(cursor, 1L, (int)*s);
 | 
						|
			s++;
 | 
						|
		}
 | 
						|
		/* slash means do a search */
 | 
						|
		else if (*s == '/' || *s == '?')
 | 
						|
		{
 | 
						|
			/* put a '\0' at the end of the search pattern */
 | 
						|
			t = parseptrn(s);
 | 
						|
 | 
						|
			/* search for the pattern */
 | 
						|
			*markptr &= ~(BLKSIZE - 1);
 | 
						|
			if (*s == '/')
 | 
						|
			{
 | 
						|
				pfetch(markline(*markptr));
 | 
						|
				if (plen > 0)
 | 
						|
					*markptr += plen - 1;
 | 
						|
				*markptr = m_fsrch(*markptr, s);
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				*markptr = m_bsrch(*markptr, s);
 | 
						|
			}
 | 
						|
 | 
						|
			/* adjust command string pointer */
 | 
						|
			s = t;
 | 
						|
		}
 | 
						|
 | 
						|
		/* if linespec was faulty, quit now */
 | 
						|
		if (!*markptr)
 | 
						|
		{
 | 
						|
			return s;
 | 
						|
		}
 | 
						|
 | 
						|
		/* maybe add an offset */
 | 
						|
		t = s;
 | 
						|
		if (*t == '-' || *t == '+')
 | 
						|
		{
 | 
						|
			s++;
 | 
						|
			for (num = 0; isdigit(*s); s++)
 | 
						|
			{
 | 
						|
				num = num * 10 + *s - '0';
 | 
						|
			}
 | 
						|
			if (num == 0)
 | 
						|
			{
 | 
						|
				num = 1;
 | 
						|
			}
 | 
						|
			*markptr = m_updnto(*markptr, num, *t);
 | 
						|
		}
 | 
						|
	} while (*s == ';' || *s == '+' || *s == '-');
 | 
						|
 | 
						|
	/* protect against invalid line numbers */
 | 
						|
	num = markline(*markptr);
 | 
						|
	if (num < 1L || num > nlines)
 | 
						|
	{
 | 
						|
		msg("Invalid line number -- must be from 1 to %ld", nlines);
 | 
						|
		*markptr = MARK_UNSET;
 | 
						|
	}
 | 
						|
 | 
						|
	return s;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/* This function reads an ex command and executes it. */
 | 
						|
void ex()
 | 
						|
{
 | 
						|
	char		cmdbuf[150];
 | 
						|
	REG int		cmdlen;
 | 
						|
	static long	oldline;
 | 
						|
 | 
						|
	significant = FALSE;
 | 
						|
	oldline = markline(cursor);
 | 
						|
 | 
						|
	while (mode == MODE_EX)
 | 
						|
	{
 | 
						|
		/* read a line */
 | 
						|
#ifdef CRUNCH
 | 
						|
		cmdlen = vgets(':', cmdbuf, sizeof(cmdbuf));
 | 
						|
#else
 | 
						|
		cmdlen = vgets(*o_prompt ? ':' : '\0', cmdbuf, sizeof(cmdbuf));
 | 
						|
#endif
 | 
						|
		if (cmdlen < 0)
 | 
						|
		{
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		/* if empty line, assume ".+1" */
 | 
						|
		if (cmdlen == 0)
 | 
						|
		{
 | 
						|
			strcpy(cmdbuf, ".+1");
 | 
						|
			qaddch('\r');
 | 
						|
			clrtoeol();
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			addch('\n');
 | 
						|
		}
 | 
						|
		refresh();
 | 
						|
 | 
						|
		/* parse & execute the command */
 | 
						|
		doexcmd(cmdbuf);
 | 
						|
 | 
						|
		/* handle autoprint */
 | 
						|
		if (significant || markline(cursor) != oldline)
 | 
						|
		{
 | 
						|
			significant = FALSE;
 | 
						|
			oldline = markline(cursor);
 | 
						|
			if (*o_autoprint && mode == MODE_EX)
 | 
						|
			{
 | 
						|
				cmd_print(cursor, cursor, CMD_PRINT, FALSE, "");
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void doexcmd(cmdbuf)
 | 
						|
	char		*cmdbuf;	/* string containing an ex command */
 | 
						|
{
 | 
						|
	REG char	*scan;		/* used to scan thru cmdbuf */
 | 
						|
	MARK		frommark;	/* first linespec */
 | 
						|
	MARK		tomark;		/* second linespec */
 | 
						|
	REG int		cmdlen;		/* length of the command name given */
 | 
						|
	CMD		cmd;		/* what command is this? */
 | 
						|
	ARGT		argt;		/* argument types for this command */
 | 
						|
	short		forceit;	/* bang version of a command? */
 | 
						|
	REG int		cmdidx;		/* index of command */
 | 
						|
	REG char	*build;		/* used while copying filenames */
 | 
						|
	int		iswild;		/* boolean: filenames use wildcards? */
 | 
						|
	int		isdfl;		/* using default line ranges? */
 | 
						|
	int		didsub;		/* did we substitute file names for % or # */
 | 
						|
 | 
						|
	/* ex commands can't be undone via the shift-U command */
 | 
						|
	U_line = 0L;
 | 
						|
 | 
						|
	/* permit extra colons at the start of the line */
 | 
						|
	for (; *cmdbuf == ':'; cmdbuf++)
 | 
						|
	{
 | 
						|
	}
 | 
						|
 | 
						|
	/* ignore command lines that start with a double-quote */
 | 
						|
	if (*cmdbuf == '"')
 | 
						|
	{
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	scan = cmdbuf;
 | 
						|
 | 
						|
	/* parse the line specifier */
 | 
						|
	if (nlines < 1)
 | 
						|
	{
 | 
						|
		/* no file, so don't allow addresses */
 | 
						|
	}
 | 
						|
	else if (*scan == '%')
 | 
						|
	{
 | 
						|
		/* '%' means all lines */
 | 
						|
		frommark = MARK_FIRST;
 | 
						|
		tomark = MARK_LAST;
 | 
						|
		scan++;
 | 
						|
	}
 | 
						|
	else if (*scan == '0')
 | 
						|
	{
 | 
						|
		frommark = tomark = MARK_UNSET;
 | 
						|
		scan++;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		frommark = cursor;
 | 
						|
		scan = linespec(scan, &frommark);
 | 
						|
		tomark = frommark;
 | 
						|
		if (frommark && *scan == ',')
 | 
						|
		{
 | 
						|
			scan++;
 | 
						|
			scan = linespec(scan, &tomark);
 | 
						|
		}
 | 
						|
		if (!tomark)
 | 
						|
		{
 | 
						|
			/* faulty line spec -- fault already described */
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		if (frommark > tomark)
 | 
						|
		{
 | 
						|
			msg("first address exceeds the second");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	isdfl = (scan == cmdbuf);
 | 
						|
 | 
						|
	/* skip whitespace */
 | 
						|
	while (isspace(*scan))
 | 
						|
	{
 | 
						|
		scan++;
 | 
						|
	}
 | 
						|
 | 
						|
	/* if no command, then just move the cursor to the mark */
 | 
						|
	if (!*scan)
 | 
						|
	{
 | 
						|
		if (tomark != MARK_UNSET)
 | 
						|
			cursor = tomark;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* figure out how long the command name is */
 | 
						|
	if (!isalpha(*scan))
 | 
						|
	{
 | 
						|
		cmdlen = 1;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		for (cmdlen = 1;
 | 
						|
		     isalpha(scan[cmdlen]);
 | 
						|
		     cmdlen++)
 | 
						|
		{
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* lookup the command code */
 | 
						|
	for (cmdidx = 0;
 | 
						|
	     cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen);
 | 
						|
	     cmdidx++)
 | 
						|
	{
 | 
						|
	}
 | 
						|
	argt = cmdnames[cmdidx].argt;
 | 
						|
	cmd = cmdnames[cmdidx].code;
 | 
						|
	if (cmd == CMD_NULL)
 | 
						|
	{
 | 
						|
		msg("Unknown command \"%.*s\"", cmdlen, scan);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* !!! if the command doesn't have NOBAR set, then replace | with \0 */
 | 
						|
 | 
						|
	/* if the command ended with a bang, set the forceit flag */
 | 
						|
	scan += cmdlen;
 | 
						|
	if ((argt & BANG) && *scan == '!')
 | 
						|
	{
 | 
						|
		scan++;
 | 
						|
		forceit = 1;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		forceit = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	/* skip any more whitespace, to leave scan pointing to arguments */
 | 
						|
	while (isspace(*scan))
 | 
						|
	{
 | 
						|
		scan++;
 | 
						|
	}
 | 
						|
 | 
						|
	/* a couple of special cases for filenames */
 | 
						|
	if (argt & XFILE)
 | 
						|
	{
 | 
						|
		/* if names were given, process them */
 | 
						|
		if (*scan)
 | 
						|
		{
 | 
						|
			for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++)
 | 
						|
			{
 | 
						|
				switch (*scan)
 | 
						|
				{
 | 
						|
				  case '\\':
 | 
						|
					if (scan[1] == '\\' || scan[1] == '%' || scan[1] == '#')
 | 
						|
					{
 | 
						|
						*build++ = *++scan;
 | 
						|
					}
 | 
						|
					else
 | 
						|
					{
 | 
						|
						*build++ = '\\';
 | 
						|
					}
 | 
						|
					break;
 | 
						|
 | 
						|
				  case '%':
 | 
						|
					if (!*origname)
 | 
						|
					{
 | 
						|
						msg("No filename to substitute for %%");
 | 
						|
						return;
 | 
						|
					}
 | 
						|
					strcpy(build, origname);
 | 
						|
					while (*build)
 | 
						|
					{
 | 
						|
						build++;
 | 
						|
					}
 | 
						|
					didsub = TRUE;
 | 
						|
					break;
 | 
						|
 | 
						|
				  case '#':
 | 
						|
					if (!*prevorig)
 | 
						|
					{
 | 
						|
						msg("No filename to substitute for #");
 | 
						|
						return;
 | 
						|
					}
 | 
						|
					strcpy(build, prevorig);
 | 
						|
					while (*build)
 | 
						|
					{
 | 
						|
						build++;
 | 
						|
					}
 | 
						|
					didsub = TRUE;
 | 
						|
					break;
 | 
						|
 | 
						|
				  case '*':
 | 
						|
				  case '?':
 | 
						|
#if !(MSDOS || TOS)
 | 
						|
				  case '[':
 | 
						|
				  case '`':
 | 
						|
				  case '{': /* } */
 | 
						|
				  case '$':
 | 
						|
				  case '~':
 | 
						|
#endif
 | 
						|
					*build++ = *scan;
 | 
						|
					iswild = TRUE;
 | 
						|
					break;
 | 
						|
 | 
						|
				  default:
 | 
						|
					*build++ = *scan;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			*build = '\0';
 | 
						|
 | 
						|
			if (cmd == CMD_BANG
 | 
						|
			 || cmd == CMD_READ && tmpblk.c[0] == '!'
 | 
						|
			 || cmd == CMD_WRITE && tmpblk.c[0] == '!')
 | 
						|
			{
 | 
						|
				if (didsub)
 | 
						|
				{
 | 
						|
					if (mode != MODE_EX)
 | 
						|
					{
 | 
						|
						addch('\n');
 | 
						|
					}
 | 
						|
					addstr(tmpblk.c);
 | 
						|
					addch('\n');
 | 
						|
					exrefresh();
 | 
						|
				}
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				if (iswild && tmpblk.c[0] != '>')
 | 
						|
				{
 | 
						|
					scan = wildcard(tmpblk.c);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else /* no names given, maybe assume origname */
 | 
						|
		{
 | 
						|
			if (!(argt & NODFL))
 | 
						|
			{
 | 
						|
				strcpy(tmpblk.c, origname);
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				*tmpblk.c = '\0';
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		scan = tmpblk.c;
 | 
						|
	}
 | 
						|
 | 
						|
	/* bad arguments? */
 | 
						|
	if (!(argt & EXRCOK) && nlines < 1L)
 | 
						|
	{
 | 
						|
		msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (!(argt & (ZERO | EXRCOK)) && frommark == MARK_UNSET)
 | 
						|
	{
 | 
						|
		msg("Can't use address 0 with \"%s\" command.", cmdnames[cmdidx].name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (!(argt & FROM) && frommark != cursor && nlines >= 1L)
 | 
						|
	{
 | 
						|
		msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (!(argt & TO) && tomark != frommark && nlines >= 1L)
 | 
						|
	{
 | 
						|
		msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (!(argt & EXTRA) && *scan)
 | 
						|
	{
 | 
						|
		msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!')))
 | 
						|
	{
 | 
						|
		build = scan;
 | 
						|
#ifndef CRUNCH
 | 
						|
		if ((argt & PLUS) && *build == '+')
 | 
						|
		{
 | 
						|
			while (*build && !isspace(*build))
 | 
						|
			{
 | 
						|
				build++;
 | 
						|
			}
 | 
						|
			while (*build && isspace(*build))
 | 
						|
			{
 | 
						|
				build++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
#endif /* not CRUNCH */
 | 
						|
		for (; *build; build++)
 | 
						|
		{
 | 
						|
			if (isspace(*build))
 | 
						|
			{
 | 
						|
				msg("Too many %s to \"%s\" command.",
 | 
						|
					(argt & XFILE) ? "filenames" : "arguments",
 | 
						|
					cmdnames[cmdidx].name);
 | 
						|
				return;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* some commands have special default ranges */
 | 
						|
	if (isdfl && (argt & DFLALL))
 | 
						|
	{
 | 
						|
		frommark = MARK_FIRST;
 | 
						|
		tomark = MARK_LAST;
 | 
						|
	}
 | 
						|
	else if (isdfl && (argt & DFLNONE))
 | 
						|
	{
 | 
						|
		frommark = tomark = 0L;
 | 
						|
	}
 | 
						|
 | 
						|
	/* write a newline if called from visual mode */
 | 
						|
	if ((argt & NL) && mode != MODE_EX && !exwrote)
 | 
						|
	{
 | 
						|
		addch('\n');
 | 
						|
		exrefresh();
 | 
						|
	}
 | 
						|
 | 
						|
	/* act on the command */
 | 
						|
	(*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* This function executes EX commands from a file.  It returns 1 normally, or
 | 
						|
 * 0 if the file could not be opened for reading.
 | 
						|
 */
 | 
						|
int doexrc(filename)
 | 
						|
	char	*filename;	/* name of a ".exrc" file */
 | 
						|
{
 | 
						|
	int	fd;		/* file descriptor */
 | 
						|
	int	len;		/* length of the ".exrc" file */
 | 
						|
 | 
						|
	/* !!! kludge: we use U_text as the buffer.  This has the side-effect
 | 
						|
	 * of interfering with the shift-U visual command.  Disable shift-U.
 | 
						|
	 */
 | 
						|
	U_line = 0L;
 | 
						|
 | 
						|
	/* open the file, read it, and close */
 | 
						|
	fd = open(filename, O_RDONLY);
 | 
						|
	if (fd < 0)
 | 
						|
	{
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	len = tread(fd, U_text, BLKSIZE);
 | 
						|
	close(fd);
 | 
						|
 | 
						|
	/* execute the string */
 | 
						|
	exstring(U_text, len, ctrl('V'));
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
/* This function executes EX commands from a string.  The commands may be
 | 
						|
 * separated by newlines or by | characters.  It also handles quoting.
 | 
						|
 * Each individual command is limited to 132 bytes, but the total string
 | 
						|
 * may be longer.
 | 
						|
 */
 | 
						|
void exstring(buf, len, qchar)
 | 
						|
	char	*buf;	/* the commands to execute */
 | 
						|
	int	len;	/* the length of the string */
 | 
						|
	int	qchar;	/* the quote character -- ^V for file, or \ for kbd */
 | 
						|
{
 | 
						|
	char	single[133];	/* a single command */
 | 
						|
	char	*src, *dest;
 | 
						|
	int	i;
 | 
						|
 | 
						|
	/* find & do each command */
 | 
						|
	for (src = buf; src < &buf[len]; src++)
 | 
						|
	{
 | 
						|
		/* Copy a single command into single[].  Convert any quoted |
 | 
						|
		 * into a normal |, and stop at a newline or unquoted |.
 | 
						|
		 */
 | 
						|
		for (dest = single, i = 0;
 | 
						|
		     i < 132 && src < &buf[len] && *src != '\n' && *src != '|';
 | 
						|
		     src++, i++)
 | 
						|
		{
 | 
						|
			if (src[0] == qchar && src[1] == '|')
 | 
						|
			{
 | 
						|
				src++;
 | 
						|
			}
 | 
						|
			*dest++ = *src;
 | 
						|
		}
 | 
						|
		*dest = '\0';
 | 
						|
 | 
						|
		/* do it */
 | 
						|
		doexcmd(single);
 | 
						|
	}
 | 
						|
}
 |