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

📄 ex.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 3 页
字号:
	}	/* If doing a default command, vi just moves to the line. */	if (IN_VI_MODE(sp) && uselastcmd) {		switch (exc.addrcnt) {		case 2:			sp->lno = exc.addr2.lno ? exc.addr2.lno : 1;			sp->cno = exc.addr2.cno;			break;		case 1:			sp->lno = exc.addr1.lno ? exc.addr1.lno : 1;			sp->cno = exc.addr1.cno;			break;		}		cmd = save_cmd;		cmdlen = save_cmdlen;		goto loop;	}	/* Reset "last" command. */	if (LF_ISSET(E_SETLAST))		exp->lastcmd = cp;	/* Final setup for the command. */	exc.cmd = cp;#if defined(DEBUG) && 0	TRACE(sp, "ex_cmd: %s", exc.cmd->name);	if (exc.addrcnt > 0) {		TRACE(sp, "\taddr1 %d", exc.addr1.lno);		if (exc.addrcnt > 1)			TRACE(sp, " addr2: %d", exc.addr2.lno);		TRACE(sp, "\n");	}	if (exc.lineno)		TRACE(sp, "\tlineno %d", exc.lineno);	if (exc.flags)		TRACE(sp, "\tflags %0x", exc.flags);	if (F_ISSET(&exc, E_BUFFER))		TRACE(sp, "\tbuffer %c", exc.buffer);	TRACE(sp, "\n");	if (exc.argc) {		for (cnt = 0; cnt < exc.argc; ++cnt)			TRACE(sp, "\targ %d: {%s}", cnt, exc.argv[cnt]);		TRACE(sp, "\n");	}#endif	/* Clear autoprint flag. */	F_CLR(exp, EX_AUTOPRINT);	/* Increment the command count if not called from vi. */	if (!IN_VI_MODE(sp))		++sp->ccnt;	/*	 * If file state and not doing a global command, log the start of	 * an action.	 */	if (ep != NULL && !F_ISSET(sp, S_GLOBAL))		(void)log_cursor(sp, ep);	/* Save the current mode. */	saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);	/* Do the command. */	if ((cp->fn)(sp, ep, &exc))		goto err;#ifdef DEBUG	/* Make sure no function left the temporary space locked. */	if (F_ISSET(sp->gp, G_TMP_INUSE)) {		F_CLR(sp->gp, G_TMP_INUSE);		msgq(sp, M_ERR, "Error: ex: temporary buffer not released.");		goto err;	}#endif	if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE)) {		/*		 * Only here if the mode of the underlying file changed, e.g.		 * the user switched files or is exiting.  Two things that we		 * might have to save: first, any "+cmd" field set up for an		 * ex/edit command will have to be saved for later, also, any		 * part of the current ex command that hasn't been executed		 * yet.  For example:		 *		 *	:edit +25 file.c|s/abc/ABC/|1		 *		 * The historic vi just hung, of course; nvi handle's it by		 * pushing the keys onto the tty queue.  If we're switching		 * modes to vi, since the commands are intended as ex commands,		 * add the extra characters to make it work.		 *		 * For the fun of it, if you want to see if a vi clone got the		 * ex argument parsing right, try: 		 *		 *	echo 'foo|bar' > file1; echo 'foo/bar' > file2;		 *	vi		 *	:edit +1|s/|/PIPE/|w file1| e file2|1 | s/\//SLASH/|wq		 */		if (arg1_len == NULL && save_cmdlen == 0)			return (0);		if (IN_VI_MODE(sp) && term_push(sp, "\n", 1, 0, 0))			goto err;		if (save_cmdlen != 0)			if (term_push(sp, save_cmd, save_cmdlen, 0, 0))				goto err;		if (arg1 != NULL) {			if (IN_VI_MODE(sp) && save_cmdlen != 0 &&			    term_push(sp, "|", 1, 0, 0))				goto err;			if (term_push(sp, arg1, arg1_len, 0, 0))				goto err;		}		if (IN_VI_MODE(sp) && term_push(sp, ":", 1, 0, 0))			goto err;		return (0);	}	if (IN_EX_MODE(sp) && ep != NULL) {		/*		 * The print commands have already handled the `print' flags.		 * If so, clear them.  Don't return, autoprint may still have		 * stuff to print out.		 */		 if (LF_ISSET(E_F_PRCLEAR))			F_CLR(&exc, E_F_HASH | E_F_LIST | E_F_PRINT);		/*		 * If the command was successful, and there was an explicit		 * flag to display the new cursor line, or we're in ex mode,		 * autoprint is set, and a change was made, display the line.		 */		if (flagoff) {			if (flagoff < 0) {				if (sp->lno < -flagoff) {					msgq(sp, M_ERR,					    "Flag offset before line 1.");					goto err;				}			} else {				if (file_lline(sp, ep, &lno))					goto err;				if (sp->lno + flagoff > lno) {					msgq(sp, M_ERR,					    "Flag offset past end-of-file.");					goto err;				}			}			sp->lno += flagoff;		}		if (O_ISSET(sp, O_AUTOPRINT) &&		    (F_ISSET(exp, EX_AUTOPRINT) || F_ISSET(cp, E_AUTOPRINT)))			LF_INIT(E_F_PRINT);		else			LF_INIT(F_ISSET(&exc, E_F_HASH | E_F_LIST | E_F_PRINT));		memset(&exc, 0, sizeof(EXCMDARG));		exc.addrcnt = 2;		exc.addr1.lno = exc.addr2.lno = sp->lno;		exc.addr1.cno = exc.addr2.cno = sp->cno;		switch (LF_ISSET(E_F_HASH | E_F_LIST | E_F_PRINT)) {		case E_F_HASH:			exc.cmd = &cmds[C_HASH];			ex_number(sp, ep, &exc);			break;		case E_F_LIST:			exc.cmd = &cmds[C_LIST];			ex_list(sp, ep, &exc);			break;		case E_F_PRINT:			exc.cmd = &cmds[C_PRINT];			ex_pr(sp, ep, &exc);			break;		}	}	cmd = save_cmd;	cmdlen = save_cmdlen;	goto loop;	/* NOTREACHED */	/*	 * On error, we discard any keys we have left, as well as any keys	 * that were mapped.  The test of save_cmdlen isn't necessarily	 * correct.  If we fail early enough we don't know if the entire	 * string was a single command or not.  Try and guess, it's useful	 * to know if part of the command was discarded.	 */err:	if (save_cmdlen == 0)		for (; cmdlen; --cmdlen) {			ch = *cmd++;			if (IS_ESCAPE(sp, ch) && cmdlen > 1) {				--cmdlen;				++cmd;			} else if (ch == '\n' || ch == '|') {				if (cmdlen > 1)					save_cmdlen = 1;				break;			}		}	if (save_cmdlen != 0)		msgq(sp, M_ERR,		    "Ex command failed: remaining command input discarded.");	term_map_flush(sp, "Ex command failed");	return (1);}/* * ep_range -- *	Get a line range for ex commands. */static intep_range(sp, ep, excp, cmdp, cmdlenp)	SCR *sp;	EXF *ep;	EXCMDARG *excp;	char **cmdp;	size_t *cmdlenp;{	MARK cur, savecursor;	size_t cmdlen;	int savecursor_set, tmp;	char *cmd;	/* Percent character is all lines in the file. */	cmd = *cmdp;	cmdlen = *cmdlenp;	if (*cmd == '%') {		excp->addr1.lno = 1;		if (file_lline(sp, ep, &excp->addr2.lno))			return (1);		/* If an empty file, then the first line is 0, not 1. */		if (excp->addr2.lno == 0)			excp->addr1.lno = 0;		excp->addr1.cno = excp->addr2.cno = 0;		excp->addrcnt = 2;		++*cmdp;		--*cmdlenp;		return (0);	}	/* Parse comma or semi-colon delimited line specs. */	for (savecursor_set = 0, excp->addrcnt = 0; cmdlen > 0;)		switch (*cmd) {		case ';':		/* Semi-colon delimiter. */			/*			 * Comma delimiters delimit; semi-colon delimiters			 * change the current address for the 2nd address			 * to be the first address.  Trailing or multiple			 * delimiters are discarded.			 */			if (excp->addrcnt == 0)				goto done;			if (!savecursor_set) {				savecursor.lno = sp->lno;				savecursor.cno = sp->cno;				sp->lno = excp->addr1.lno;				sp->cno = excp->addr1.cno;				savecursor_set = 1;			}			++cmd;			--cmdlen;			break;		case ',':		/* Comma delimiter. */			/* If no addresses yet, defaults to ".". */			if (excp->addrcnt == 0) {				excp->addr1.lno = sp->lno;				excp->addr1.cno = sp->cno;				excp->addrcnt = 1;			}			/* FALLTHROUGH */		case ' ':		/* Whitespace. */		case '\t':		/* Whitespace. */			++cmd;			--cmdlen;			break;		default:			if (ep_line(sp, ep, &cur, &cmd, &cmdlen, &tmp))				return (1);			if (!tmp)				goto done;			/*			 * Extra addresses are discarded, starting with			 * the first.			 */			switch (excp->addrcnt) {			case 0:				excp->addr1 = cur;				excp->addrcnt = 1;				break;			case 1:				excp->addr2 = cur;				excp->addrcnt = 2;				break;			case 2:				excp->addr1 = excp->addr2;				excp->addr2 = cur;				break;			}			break;		}	/*	 * XXX	 * This is probably not right behavior for savecursor -- need	 * to figure out what the historical ex did for ";,;,;5p" or	 * similar stupidity.	 */done:	if (savecursor_set) {		sp->lno = savecursor.lno;		sp->cno = savecursor.cno;	}	if (excp->addrcnt == 2 &&	    (excp->addr2.lno < excp->addr1.lno ||	    excp->addr2.lno == excp->addr1.lno &&	    excp->addr2.cno < excp->addr1.cno)) {		msgq(sp, M_ERR,		    "The second address is smaller than the first.");		return (1);	}	*cmdp = cmd;	*cmdlenp = cmdlen;	return (0);}/* * Get a single line address specifier. */static intep_line(sp, ep, cur, cmdp, cmdlenp, addr_found)	SCR *sp;	EXF *ep;	MARK *cur;	char **cmdp;	size_t *cmdlenp;	int *addr_found;{	MARK m;	long total;	u_int flags;	size_t cmdlen;	int (*sf) __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *));	char *cmd, *endp;	*addr_found = 0;	cmd = *cmdp;	cmdlen = *cmdlenp;	switch (*cmd) {	case '$':				/* Last line in the file. */		*addr_found = 1;		cur->cno = 0;		if (file_lline(sp, ep, &cur->lno))			return (1);		++cmd;		--cmdlen;		break;				/* Absolute line number. */	case '0': case '1': case '2': case '3': case '4':	case '5': case '6': case '7': case '8': case '9':		*addr_found = 1;		/*		 * The way the vi "previous context" mark worked was that		 * "non-relative" motions set it.  While vi wasn't totally		 * consistent about this, ANY numeric address was considered		 * non-relative, and set the value.  Which is why we're		 * hacking marks down here.		 */		if (IN_VI_MODE(sp)) {			m.lno = sp->lno;			m.cno = sp->cno;			if (mark_set(sp, ep, ABSMARK1, &m, 1))				return (1);		}		cur->cno = 0;/* 8-bit XXX */	cur->lno = strtol(cmd, &endp, 10);		cmdlen -= (endp - cmd);		cmd = endp;		break;	case '\'':				/* Use a mark. */		*addr_found = 1;		if (cmdlen == 1) {			msgq(sp, M_ERR, "No mark name supplied.");			return (1);		}		if (mark_get(sp, ep, cmd[1], cur))			return (1);		cmd += 2;		cmdlen -= 2;		break;	case '\\':				/* Search: forward/backward. */		/*		 * !!!		 * I can't find any difference between // and \/ or		 * between ?? and \?.  Mark Horton doesn't remember		 * there being any difference.  C'est la vie.		 */		if (cmdlen < 2 || cmd[1] != '/' && cmd[1] != '?') {			msgq(sp, M_ERR, "\\ not followed by / or ?.");			return (1);		}		++cmd;		--cmdlen;		sf = cmd[0] == '/' ? f_search : b_search;		goto search;	case '/':				/* Search forward. */		sf = f_search;		goto search;	case '?':				/* Search backward. */		sf = b_search;search:		if (ep == NULL) {			msgq(sp, M_ERR,	"A search address requires that a file have already been read in.");			return (1);		}		*addr_found = 1;		m.lno = sp->lno;		m.cno = sp->cno;		flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET;		if (sf(sp, ep, &m, &m, cmd, &endp, &flags))			return (1);		cur->lno = m.lno;		cur->cno = m.cno;		cmdlen -= (endp - cmd);		cmd = endp;		break;	case '.':				/* Current position. */		*addr_found = 1;		cur->cno = sp->cno;		/* If an empty file, then '.' is 0, not 1. */		if (sp->lno == 1) {			if (file_lline(sp, ep, &cur->lno))				return (1);			if (cur->lno != 0)				cur->lno = 1;		} else			cur->lno = sp->lno;		++cmd;		--cmdlen;		break;	}	/*	 * Evaluate any offset.  Offsets are +/- any number, or any number	 * of +/- signs, or any combination thereof.  If no address found	 * yet, offset is relative to ".".	 */	for (total = 0; cmdlen > 0 && (cmd[0] == '-' || cmd[0] == '+');) {		if (!*addr_found) {			cur->lno = sp->lno;			cur->cno = sp->cno;			*addr_found = 1;		}		if (cmdlen > 1 && isdigit(cmd[1])) {/* 8-bit XXX */		total += strtol(cmd, &endp, 10);			cmdlen -= (endp - cmd);			cmd = endp;		} else {			total += cmd[0] == '-' ? -1 : 1;			--cmdlen;			++cmd;		}	}	if (*addr_found) {		if (total < 0 && -total > cur->lno) {			msgq(sp, M_ERR,			    "Reference to a line number less than 0.");			return (1);		}		cur->lno += total;		*cmdp = cmd;		*cmdlenp = cmdlen;	}	return (0);}/* * ex_is_abbrev - *	The vi text input routine needs to know if ex thinks this is *	an [un]abbreviate command, so it can turn off abbreviations. *	Usual ranting in the vi/v_ntext:txt_abbrev() routine. */intex_is_abbrev(name, len)	char *name;	size_t len;{	EXCMDLIST const *cp;	return ((cp = ex_comm_search(name, len)) != NULL &&	    (cp == &cmds[C_ABBR] || cp == &cmds[C_UNABBREVIATE]));}/* * ex_is_unmap - *	The vi text input routine needs to know if ex thinks this is *	an unmap command, so it can turn off input mapping.  Usual *	ranting in the vi/v_ntext:txt_unmap() routine. */intex_is_unmap(name, len)	char *name;	size_t len;{	EXCMDLIST const *cp;	/*	 * The command the vi input routines are really interested in	 * is "unmap!", not just unmap.	 */	if (name[len - 1] != '!')		return (0);	--len;	return ((cp = ex_comm_search(name, len)) != NULL &&	    cp == &cmds[C_UNMAP]);}static inline EXCMDLIST const *ex_comm_search(name, len)	char *name;	size_t len;{	EXCMDLIST const *cp;	for (cp = cmds; cp->name != NULL; ++cp) {		if (cp->name[0] > name[0])			return (NULL);		if (cp->name[0] != name[0])			continue;		if (!memcmp(name, cp->name, len))			return (cp);	}	return (NULL);}

⌨️ 快捷键说明

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