⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ex.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 3 页
字号:
/*- * Copyright (c) 1992, 1993, 1994 *	The Regents of the University of California.  All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software *    must display the following acknowledgement: *	This product includes software developed by the University of *	California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)ex.c	8.117 (Berkeley) 4/13/94";#endif /* not lint */#include <sys/types.h>#include <sys/queue.h>#include <sys/stat.h>#include <sys/time.h>#include <bitstring.h>#include <ctype.h>#include <errno.h>#include <fcntl.h>#include <limits.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <termios.h>#include <unistd.h>#include "compat.h"#include <db.h>#include <regex.h>#include "vi.h"#include "excmd.h"static inline EXCMDLIST const *		ex_comm_search __P((char *, size_t));static int	ep_line __P((SCR *, EXF *, MARK *, char **, size_t *, int *));static int	ep_range __P((SCR *, EXF *, EXCMDARG *, char **, size_t *));#define	DEFCOM	".+1"/* * ex -- *	Read an ex command and execute it. */intex(sp, ep)	SCR *sp;	EXF *ep;{	enum input irval;	TEXT *tp;	u_int flags, saved_mode;	int eval;	char defcom[sizeof(DEFCOM)];	if (ex_init(sp, ep))		return (1);	if (sp->s_refresh(sp, ep))		return (ex_end(sp));	/* If reading from a file, messages should have line info. */	if (!F_ISSET(sp->gp, G_STDIN_TTY)) {		sp->if_lno = 1;		sp->if_name = strdup("input");	}	/*	 * !!!	 * Historically, the beautify option applies to ex command input read	 * from a file.  In addition, the first time a ^H was discarded from	 * the input, a message "^H discarded" was displayed.  We don't bother.	 */	LF_INIT(TXT_BACKSLASH | TXT_CNTRLD | TXT_CR);	if (O_ISSET(sp, O_BEAUTIFY))		LF_SET(TXT_BEAUTIFY);	if (O_ISSET(sp, O_PROMPT))		LF_SET(TXT_PROMPT);	for (eval = 0;; ++sp->if_lno) {		/*		 * Get the next command.  Interrupt flag manipulation is safe		 * because ex_icmd clears them all.		 */		F_CLR(sp, S_INTERRUPTED);		F_SET(sp, S_INTERRUPTIBLE);		irval = sp->s_get(sp, ep, sp->tiqp, ':', flags);		if (F_ISSET(sp, S_INTERRUPTED)) {			(void)fputc('\n', stdout);			(void)fflush(stdout);			goto refresh;		}		switch (irval) {		case INP_OK:			break;		case INP_EOF:		case INP_ERR:			F_SET(sp, S_EXIT_FORCE);			goto ret;		}		saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);		tp = sp->tiqp->cqh_first;		if (tp->len == 0) {			if (F_ISSET(sp->gp, G_STDIN_TTY)) {				/* Special case \r command. */				(void)fputc('\r', stdout);				(void)fflush(stdout);			}			memmove(defcom, DEFCOM, sizeof(DEFCOM));			if (ex_icmd(sp, ep, defcom, sizeof(DEFCOM) - 1) &&			    !F_ISSET(sp->gp, G_STDIN_TTY))				F_SET(sp, S_EXIT_FORCE);		} else {			if (F_ISSET(sp->gp, G_STDIN_TTY))				/* Special case ^D command. */				if (tp->len == 1 && tp->lb[0] == '\004') {					(void)fputc('\r', stdout);					(void)fflush(stdout);				} else					(void)fputc('\n', stdout);			if (ex_icmd(sp, ep, tp->lb, tp->len) &&			    !F_ISSET(sp->gp, G_STDIN_TTY))				F_SET(sp, S_EXIT_FORCE);		}		(void)msg_rpt(sp, 0);		if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE))			break;refresh:	if (sp->s_refresh(sp, ep)) {			eval = 1;			break;		}	}ret:	if (sp->if_name != NULL) {		FREE(sp->if_name, strlen(sp->if_name) + 1);		sp->if_name = NULL;	}	return (ex_end(sp) || eval);}/* * ex_cfile -- *	Execute ex commands from a file. */intex_cfile(sp, ep, filename)	SCR *sp;	EXF *ep;	char *filename;{	struct stat sb;	int fd, len, rval;	char *bp;	bp = NULL;	if ((fd = open(filename, O_RDONLY, 0)) < 0 || fstat(fd, &sb))		goto err;	/*	 * XXX	 * We'd like to test if the file is too big to malloc.  Since we don't	 * know what size or type off_t's or size_t's are, what the largest	 * unsigned integral type is, or what random insanity the local C	 * compiler will perpetrate, doing the comparison in a portable way	 * is flatly impossible.  Hope that malloc fails if the file is too	 * large.	 */	MALLOC(sp, bp, char *, (size_t)sb.st_size + 1);	if (bp == NULL)		goto err;	len = read(fd, bp, (int)sb.st_size);	if (len == -1 || len != sb.st_size) {		if (len != sb.st_size)			errno = EIO;err:		rval = 1;		msgq(sp, M_SYSERR, filename);	} else {		bp[sb.st_size] = '\0';		/* XXX */		/*		 * Run the command.  Messages include file/line information,		 * but we don't care if we can't get space.		 */		sp->if_lno = 1;		sp->if_name = strdup(filename);		F_SET(sp, S_VLITONLY);		rval = ex_icmd(sp, ep, bp, len);		F_CLR(sp, S_VLITONLY);		free(sp->if_name);		sp->if_name = NULL;	}	/*	 * !!!	 * THE UNDERLYING EXF MAY HAVE CHANGED.	 */	if (bp != NULL)		FREE(bp, sb.st_size);	if (fd >= 0)		(void)close(fd);	return (rval);}/* * ex_icmd -- *	Call ex_cmd() after turning off interruptible bits. */intex_icmd(sp, ep, cmd, len)	SCR *sp;	EXF *ep;	char *cmd;	size_t len;{	/*	 * Ex goes through here for each vi :colon command and for each ex	 * command, however, globally executed commands don't go through	 * here, instead, they call ex_cmd directly.  So, reset all of the	 * interruptible flags now.	 */	F_CLR(sp, S_INTERRUPTED | S_INTERRUPTIBLE);	return (ex_cmd(sp, ep, cmd, len));}/* Special command structure for :s as a repeat substitution command. */static EXCMDLIST const cmd_subagain =	{"s",		ex_subagain,	E_ADDR2|E_NORC,	    "s",	    "[line [,line]] s [cgr] [count] [#lp]",	    "repeat the last subsitution"};/* * ex_cmd -- *	Parse and execute a string containing ex commands. */intex_cmd(sp, ep, cmd, cmdlen)	SCR *sp;	EXF *ep;	char *cmd;	size_t cmdlen;{	EX_PRIVATE *exp;	EXCMDARG exc;	EXCMDLIST const *cp;	MARK cur;	recno_t lno, num;	size_t arg1_len, len, save_cmdlen;	long flagoff;	u_int saved_mode;	int ch, cnt, delim, flags, namelen, nl, uselastcmd, tmp;	char *arg1, *save_cmd, *p, *t;	/* Init. */	nl = 0;loop:	if (nl) {		nl = 0;		++sp->if_lno;	}	arg1 = NULL;	save_cmdlen = 0;	/*	 * It's possible that we've been interrupted during a	 * command.	 */	if (F_ISSET(sp, S_INTERRUPTED))		return (0);	/* Skip whitespace, separators, newlines. */	for (; cmdlen > 0; ++cmd, --cmdlen)		if ((ch = *cmd) == '\n')			++sp->if_lno;		else if (!isblank(ch))			break;	if (cmdlen == 0)		return (0);	/* Command lines that start with a double-quote are comments. */	if (ch == '"') {		while (--cmdlen > 0 && *++cmd != '\n');		if (*cmd == '\n') {			++cmd;			--cmdlen;			++sp->if_lno;		}		goto loop;	}	/*	 * !!!	 * Permit extra colons at the start of the line.  Historically,	 * ex/vi allowed a single extra one.  It's simpler not to count.	 * The stripping is done here because, historically, any command	 * could have preceding colons, e.g. ":g/pattern/:p" worked.	 */	if (ch == ':')		while (--cmdlen > 0 && *++cmd == ':');	/* Skip whitespace. */	for (; cmdlen > 0; ++cmd, --cmdlen) {		ch = *cmd;		if (!isblank(ch))			break;	}	/* The last point at which an empty line means do nothing. */	if (cmdlen == 0)		return (0);	/* Initialize the structure passed to underlying functions. */	memset(&exc, 0, sizeof(EXCMDARG));	exp = EXP(sp);	if (argv_init(sp, ep, &exc))		goto err;	/* Parse command addresses. */	if (ep_range(sp, ep, &exc, &cmd, &cmdlen))		goto err;	/* Skip whitespace. */	for (; cmdlen > 0; ++cmd, --cmdlen) {		ch = *cmd;		if (!isblank(ch))			break;	}	/*	 * If no command, ex does the last specified of p, l, or #, and vi	 * moves to the line.  Otherwise, determine the length of the command	 * name by looking for the first non-alphabetic character.  (There	 * are a few non-alphabetic characters in command names, but they're	 * all single character commands.)  This isn't a great test, because	 * it means that, for the command ":e +cut.c file", we'll report that	 * the command "cut" wasn't known.  However, it makes ":e+35 file" work	 * correctly.	 */#define	SINGLE_CHAR_COMMANDS	"\004!#&<=>@~"	if (cmdlen != 0 && cmd[0] != '|' && cmd[0] != '\n') {		if (strchr(SINGLE_CHAR_COMMANDS, *cmd)) {			p = cmd;			++cmd;			--cmdlen;			namelen = 1;		} else {			for (p = cmd; cmdlen > 0; --cmdlen, ++cmd)				if (!isalpha(*cmd))					break;			if ((namelen = cmd - p) == 0) {				msgq(sp, M_ERR, "Unknown command name.");				goto err;			}		}		/*		 * Search the table for the command.		 *		 * !!!		 * Historic vi permitted the mark to immediately follow the		 * 'k' in the 'k' command.  Make it work.		 *		 * !!!		 * Historic vi permitted pretty much anything to follow the		 * substitute command, e.g. "s/e/E/|s|sgc3p" was fine.  Make		 * the command "sgc" work.		 */		if ((cp = ex_comm_search(p, namelen)) == NULL)			if (p[0] == 'k' && p[1] && !p[2]) {				cmd -= namelen - 1;				cmdlen += namelen - 1;				cp = &cmds[C_K];			} else if (p[0] == 's') {				cmd -= namelen - 1;				cmdlen += namelen - 1;				cp = &cmd_subagain;			} else {				msgq(sp, M_ERR,				    "The %.*s command is unknown.", namelen, p);				goto err;			}		/* Some commands are either not implemented or turned off. */		if (F_ISSET(cp, E_NOPERM)) {			msgq(sp, M_ERR,			    "The %s command is not currently supported.",			    cp->name);			goto err;		}		/* Some commands aren't okay in globals. */		if (F_ISSET(sp, S_GLOBAL) && F_ISSET(cp, E_NOGLOBAL)) {			msgq(sp, M_ERR,		"The %s command can't be used as part of a global command.",			    cp->name);			goto err;		}		/*		 * Multiple < and > characters; another "feature".  Note,		 * The string passed to the underlying function may not be		 * nul terminated in this case.		 */		if ((cp == &cmds[C_SHIFTL] && *p == '<') ||		    (cp == &cmds[C_SHIFTR] && *p == '>')) {			for (ch = *p; cmdlen > 0; --cmdlen, ++cmd)				if (*cmd != ch)					break;			if (argv_exp0(sp, ep, &exc, p, cmd - p))				goto err;		}		/*		 * The visual command has a different syntax when called		 * from ex than when called from a vi colon command.  FMH.		 */		if (cp == &cmds[C_VISUAL_EX] && IN_VI_MODE(sp))			cp = &cmds[C_VISUAL_VI];		uselastcmd = 0;	} else {		cp = exp->lastcmd;		uselastcmd = 1;	}	/* Initialize local flags to the command flags. */	LF_INIT(cp->flags);	/*	 * File state must be checked throughout this code, because it is	 * called when reading the .exrc file and similar things.  There's	 * this little chicken and egg problem -- if we read the file first,	 * we won't know how to display it.  If we read/set the exrc stuff	 * first, we can't allow any command that requires file state.  We	 * don't have a "reading an rc" bit, because we want the commands	 * to work when the user source's the rc file later.  Historic vi	 * generally took the easy way out and dropped core. 	 */	if (LF_ISSET(E_NORC) && ep == NULL) {		msgq(sp, M_ERR,	"The %s command requires that a file have already been read in.",		    cp->name);		goto err;	}	/*	 * There are three normal termination cases for an ex command.  They	 * are the end of the string (cmdlen), or unescaped (by literal next	 * characters) newline or '|' characters.  As we're past any addresses,	 * we can now determine how long the command is, so we don't have to	 * look for all the possible terminations.  There are three exciting	 * special cases:	 *	 * 1: The bang, global, vglobal and the filter versions of the read and	 *    write commands are delimited by newlines (they can contain shell	 *    pipes).	 * 2: The ex, edit and visual in vi mode commands take ex commands as	 *    their first arguments.	 * 3: The substitute command takes an RE as its first argument, and	 *    wants it to be specially delimited.	 *	 * Historically, '|' characters in the first argument of the ex, edit,	 * and substitute commands did not delimit the command.  And, in the	 * filter cases for read and write, and the bang, global and vglobal	 * commands, they did not delimit the command at all.	 *	 * For example, the following commands were legal:	 *	 *	:edit +25|s/abc/ABC/ file.c	 *	:substitute s/|/PIPE/	 *	:read !spell % | columnate	 *	:global/pattern/p|l	 *	 * It's not quite as simple as it sounds, however.  The command:	 *	 *	:substitute s/a/b/|s/c/d|set	 *	 * was also legal, i.e. the historic ex parser (using the word loosely,	 * since "parser" implies some regularity) delimited the RE's based on	 * its delimiter and not anything so irretrievably vulgar as a command	 * syntax.	 *	 * One thing that makes this easier is that we can ignore most of the	 * command termination conditions for the commands that want to take	 * the command up to the next newline.  None of them are legal in .exrc	 * files, so if we're here, we only dealing with a single line, and we	 * can just eat it.	 *	 * Anyhow, the following code makes this all work.  First, for the	 * special cases we move past their special argument(s).  Then, we	 * do normal command processing on whatever is left.  Barf-O-Rama.	 */	arg1_len = 0;	save_cmd = cmd;	if (cp == &cmds[C_EDIT] ||	    cp == &cmds[C_EX] || cp == &cmds[C_VISUAL_VI]) {		/*		 * Move to the next non-whitespace character.		 * The first '!' is eaten as a force flag.		 */		for (tmp = 0; cmdlen > 0; --cmdlen, ++cmd) {			ch = *cmd;			if (!isblank(ch)) {				if (!tmp && ch == '!') {					tmp = 1;					F_SET(&exc, E_FORCE);					/* Reset, don't reparse. */					save_cmd = cmd + 1;					continue;				}				break;			}		}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -