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

📄 ex.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 3 页
字号:
		/*		 * QUOTING NOTE:		 *		 * The historic implementation ignored all escape characters		 * so there was no way to put a space or newline into the +cmd		 * field.  We do a simplistic job of fixing it by moving to the		 * first whitespace character that isn't escaped by a literal		 * next character.  The literal next characters are stripped		 * as they're no longer useful.		 */		if (cmdlen > 0 && ch == '+') {			++cmd;			--cmdlen;			for (arg1 = p = cmd; cmdlen > 0; --cmdlen, ++cmd) {				ch = *cmd;				if (IS_ESCAPE(sp, ch) && cmdlen > 1) {					--cmdlen;					ch = *++cmd;				} else if (isblank(ch))					break;				*p++ = ch;			}			arg1_len = cmd - arg1;			/* Reset, so the first argument isn't reparsed. */			save_cmd = cmd;		}	} else if (cp == &cmds[C_BANG] ||	    cp == &cmds[C_GLOBAL] || cp == &cmds[C_VGLOBAL]) {		cmd += cmdlen;		cmdlen = 0;	} else if (cp == &cmds[C_READ] || cp == &cmds[C_WRITE]) {		/*		 * Move to the next character.  If it's a '!', it's a filter		 * command and we want to eat it all, otherwise, we're done.		 */		for (; cmdlen > 0; --cmdlen, ++cmd) {			ch = *cmd;			if (!isblank(ch))				break;		}		if (cmdlen > 0 && ch == '!') {			cmd += cmdlen;			cmdlen = 0;		}	} else if (cp == &cmds[C_SUBSTITUTE]) {		/*		 * Move to the next non-whitespace character, we'll use it as		 * the delimiter.  If the character isn't an alphanumeric or		 * a '|', it's the delimiter, so parse it.  Otherwise, we're		 * into something like ":s g", so use the special substitute		 * command.		 */		for (; cmdlen > 0; --cmdlen, ++cmd)			if (!isblank(cmd[0]))				break;		if (isalnum(cmd[0]) || cmd[0] == '|')			cp = &cmd_subagain;		else if (cmdlen > 0) {			/*			 * QUOTING NOTE:			 *			 * Backslashes quote delimiter characters for RE's.			 * The backslashes are NOT removed since they'll be			 * used by the RE code.  Move to the third delimiter			 * that's not escaped (or the end of the command).			 */			delim = *cmd;			++cmd;			--cmdlen;			for (cnt = 2; cmdlen > 0 && cnt; --cmdlen, ++cmd)				if (cmd[0] == '\\' && cmdlen > 1) {					++cmd;					--cmdlen;				} else if (cmd[0] == delim)					--cnt;		}	}	/*	 * Use normal quoting and termination rules to find the end	 * of this command.	 *	 * QUOTING NOTE:	 *	 * Historically, vi permitted ^V's to escape <newline>'s in the .exrc	 * file.  It was almost certainly a bug, but that's what bug-for-bug	 * compatibility means, Grasshopper.  Also, ^V's escape the command	 * delimiters.  Literal next quote characters in front of the newlines,	 * '|' characters or literal next characters are stripped as as they're	 * no longer useful.	 */	for (p = cmd, cnt = 0; cmdlen > 0; --cmdlen, ++cmd) {		ch = cmd[0];		if (IS_ESCAPE(sp, ch) && cmdlen > 1) {			tmp = cmd[1];			if (tmp == '\n' || tmp == '|') {				if (tmp == '\n')					++sp->if_lno;				--cmdlen;				++cmd;				++cnt;				ch = tmp;			}		} else if (ch == '\n' || ch == '|') {			if (ch == '\n')				nl = 1;			--cmdlen;			break;		}		*p++ = ch;	}	/*	 * Save off the next command information, go back to the	 * original start of the command.	 */	p = cmd + 1;	cmd = save_cmd;	save_cmd = p;	save_cmdlen = cmdlen;	cmdlen = ((save_cmd - cmd) - 1) - cnt;	/*	 * !!!	 * The "set tags" command historically used a backslash, not the	 * user's literal next character, to escape whitespace.  Handle	 * it here instead of complicating the argv_exp3() code.  Note,	 * this isn't a particularly complex trap, and if backslashes were	 * legal in set commands, this would have to be much more complicated.	 */	if (cp == &cmds[C_SET])		for (p = cmd, len = cmdlen; len > 0; --len, ++p)			if (*p == '\\')				*p = CH_LITERAL;	/*	 * Set the default addresses.  It's an error to specify an address for	 * a command that doesn't take them.  If two addresses are specified	 * for a command that only takes one, lose the first one.  Two special	 * cases here, some commands take 0 or 2 addresses.  For most of them	 * (the E_ADDR2_ALL flag), 0 defaults to the entire file.  For one	 * (the `!' command, the E_ADDR2_NONE flag), 0 defaults to no lines.	 *	 * Also, if the file is empty, some commands want to use an address of	 * 0, i.e. the entire file is 0 to 0, and the default first address is	 * 0.  Otherwise, an entire file is 1 to N and the default line is 1.	 * Note, we also add the E_ZERO flag to the command flags, for the case	 * where the 0 address is only valid if it's a default address.	 *	 * Also, set a flag if we set the default addresses.  Some commands	 * (ex: z) care if the user specified an address of if we just used	 * the current cursor.	 */	switch (LF_ISSET(E_ADDR1|E_ADDR2|E_ADDR2_ALL|E_ADDR2_NONE)) {	case E_ADDR1:				/* One address: */		switch (exc.addrcnt) {		case 0:				/* Default cursor/empty file. */			exc.addrcnt = 1;			F_SET(&exc, E_ADDRDEF);			if (LF_ISSET(E_ZERODEF)) {				if (file_lline(sp, ep, &lno))					goto err;				if (lno == 0) {					exc.addr1.lno = 0;					LF_SET(E_ZERO);				} else					exc.addr1.lno = sp->lno;			} else				exc.addr1.lno = sp->lno;			exc.addr1.cno = sp->cno;			break;		case 1:			break;		case 2:				/* Lose the first address. */			exc.addrcnt = 1;			exc.addr1 = exc.addr2;		}		break;	case E_ADDR2_NONE:			/* Zero/two addresses: */		if (exc.addrcnt == 0)		/* Default to nothing. */			break;		goto two;	case E_ADDR2_ALL:			/* Zero/two addresses: */		if (exc.addrcnt == 0) {		/* Default entire/empty file. */			exc.addrcnt = 2;			F_SET(&exc, E_ADDRDEF);			if (file_lline(sp, ep, &exc.addr2.lno))				goto err;			if (LF_ISSET(E_ZERODEF) && exc.addr2.lno == 0) {				exc.addr1.lno = 0;				LF_SET(E_ZERO);			} else				exc.addr1.lno = 1;			exc.addr1.cno = exc.addr2.cno = 0;			F_SET(&exc, E_ADDR2_ALL);			break;		}		/* FALLTHROUGH */	case E_ADDR2:				/* Two addresses: */two:		switch (exc.addrcnt) {		case 0:				/* Default cursor/empty file. */			exc.addrcnt = 2;			F_SET(&exc, E_ADDRDEF);			if (LF_ISSET(E_ZERODEF) && sp->lno == 1) {				if (file_lline(sp, ep, &lno))					goto err;				if (lno == 0) {					exc.addr1.lno = exc.addr2.lno = 0;					LF_SET(E_ZERO);				} else					exc.addr1.lno = exc.addr2.lno = sp->lno;			} else				exc.addr1.lno = exc.addr2.lno = sp->lno;			exc.addr1.cno = exc.addr2.cno = sp->cno;			break;		case 1:				/* Default to first address. */			exc.addrcnt = 2;			exc.addr2 = exc.addr1;			break;		case 2:			break;		}		break;	default:		if (exc.addrcnt)		/* Error. */			goto usage;	}	/*	 * !!!	 * The ^D scroll command historically scrolled half the screen size	 * rows down, rounded down, or to EOF.  It was an error if the cursor	 * was already at EOF.  (Leading addresses were permitted, but were	 * then ignored.)	 */	if (cp == &cmds[C_SCROLL]) {		exc.addrcnt = 2;		exc.addr1.lno = sp->lno + 1;		exc.addr2.lno = sp->lno + 1 + (O_VAL(sp, O_LINES) + 1) / 2;		exc.addr1.cno = exc.addr2.cno = sp->cno;		if (file_lline(sp, ep, &lno))			goto err;		if (lno != 0 && lno > sp->lno && exc.addr2.lno > lno)			exc.addr2.lno = lno;	}	flagoff = 0;	for (p = cp->syntax; *p != '\0'; ++p) {		/*		 * The write command is sensitive to leading whitespace, e.g.		 * "write !" is different from "write!".  If not the write		 * command, skip leading whitespace.		 */		if (cp != &cmds[C_WRITE])			for (; cmdlen > 0; --cmdlen, ++cmd) {				ch = *cmd;				if (!isblank(ch))					break;			}		/*		 * Quit when reach the end of the command, unless it's a		 * command that does its own parsing, in which case we want		 * to build a reasonable argv for it.  This code guarantees		 * that there will be an argv when the function gets called,		 * so the correct test is for a length of 0, not for the		 * argc > 0.		 */		if (cmdlen == 0 && *p != '!' && *p != 'S' && *p != 's')			break;		switch (*p) {		case '!':				/* ! */			if (*cmd == '!') {				++cmd;				--cmdlen;				F_SET(&exc, E_FORCE);			}			break;		case '1':				/* +, -, #, l, p */			/*			 * !!!			 * Historically, some flags were ignored depending			 * on where they occurred in the command line.  For			 * example, in the command, ":3+++p--#", historic vi			 * acted on the '#' flag, but ignored the '-' flags.			 * It's unambiguous what the flags mean, so we just			 * handle them regardless of the stupidity of their			 * location.			 */			for (; cmdlen; --cmdlen, ++cmd)				switch (*cmd) {				case '+':					++flagoff;					break;				case '-':					--flagoff;					break;				case '#':					F_SET(&exc, E_F_HASH);					break;				case 'l':					F_SET(&exc, E_F_LIST);					break;				case 'p':					F_SET(&exc, E_F_PRINT);					break;				default:					goto end1;				}end1:			break;		case '2':				/* -, ., +, ^ */		case '3':				/* -, ., +, ^, = */			for (; cmdlen; --cmdlen, ++cmd)				switch (*cmd) {				case '-':					F_SET(&exc, E_F_DASH);					break;				case '.':					F_SET(&exc, E_F_DOT);					break;				case '+':					F_SET(&exc, E_F_PLUS);					break;				case '^':					F_SET(&exc, E_F_CARAT);					break;				case '=':					if (*p == '3') {						F_SET(&exc, E_F_EQUAL);						break;					}					/* FALLTHROUGH */				default:					goto end2;				}end2:			break;		case 'b':				/* buffer */			/*			 * Digits can't be buffer names in ex commands, or the			 * command "d2" would be a delete into buffer '2', and			 * not a two-line deletion.			 */			if (!isdigit(cmd[0])) {				exc.buffer = *cmd;				++cmd;				--cmdlen;				F_SET(&exc, E_BUFFER);			}			break;		case 'c':				/* count [01+a] */			++p;			/* Validate any signed value. */			if (!isdigit(*cmd) &&			    (*p != '+' || (*cmd != '+' && *cmd != '-')))				break;			/* If a signed value, set appropriate flags. */			if (*cmd == '-')				F_SET(&exc, E_COUNT_NEG);			else if (*cmd == '+')				F_SET(&exc, E_COUNT_POS);/* 8-bit XXX */		if ((lno = strtol(cmd, &t, 10)) == 0 && *p != '0') {				msgq(sp, M_ERR, "Count may not be zero.");				goto err;			}			cmdlen -= (t - cmd);			cmd = t;			/*			 * Count as address offsets occur in commands taking			 * two addresses.  Historic vi practice was to use			 * the count as an offset from the *second* address.			 *			 * Set a count flag; some underlying commands (see			 * join) do different things with counts than with			 * line addresses.			 */			if (*p == 'a') {				exc.addr1 = exc.addr2;				exc.addr2.lno = exc.addr1.lno + lno - 1;			} else				exc.count = lno;			F_SET(&exc, E_COUNT);			break;		case 'f':				/* file */			if (argv_exp2(sp, ep,			    &exc, cmd, cmdlen, cp == &cmds[C_BANG]))				goto err;			goto countchk;		case 'l':				/* line */			if (ep_line(sp, ep, &cur, &cmd, &cmdlen, &tmp))				goto err;			/* Line specifications are always required. */			if (!tmp) {				msgq(sp, M_ERR,				     "%s: bad line specification", cmd);				goto err;			}			exc.lineno = cur.lno;			break;		case 'S':				/* string, file exp. */			if (argv_exp1(sp, ep,			    &exc, cmd, cmdlen, cp == &cmds[C_BANG]))				goto err;			goto addr2;		case 's':				/* string */			if (argv_exp0(sp, ep, &exc, cmd, cmdlen))				goto err;			goto addr2;		case 'W':				/* word string */			/*			 * QUOTING NOTE:			 *			 * Literal next characters escape the following			 * character.  Quoting characters are stripped			 * here since they are no longer useful.			 *			 * First there was the word.			 */			for (p = t = cmd; cmdlen > 0; --cmdlen, ++cmd) {				ch = *cmd;				if (IS_ESCAPE(sp, ch) && cmdlen > 1) {					--cmdlen;					*p++ = *++cmd;				} else if (isblank(ch)) {					++cmd;					--cmdlen;					break;				} else					*p++ = ch;			}			if (argv_exp0(sp, ep, &exc, t, p - t))				goto err;			/* Delete intervening whitespace. */			for (; cmdlen > 0; --cmdlen, ++cmd) {				ch = *cmd;				if (!isblank(ch))					break;			}			if (cmdlen == 0)				goto usage;			/* Followed by the string. */			for (p = t = cmd; cmdlen > 0; --cmdlen, ++cmd, ++p) {				ch = *cmd;				if (IS_ESCAPE(sp, ch) && cmdlen > 1) {					--cmdlen;					*p = *++cmd;				} else					*p = ch;			}			if (argv_exp0(sp, ep, &exc, t, p - t))				goto err;			goto addr2;		case 'w':				/* word */			if (argv_exp3(sp, ep, &exc, cmd, cmdlen))				goto err;countchk:		if (*++p != 'N') {		/* N */				/*				 * If a number is specified, must either be				 * 0 or that number, if optional, and that				 * number, if required.				 */				num = *p - '0';				if ((*++p != 'o' || exp->argsoff != 0) &&				    exp->argsoff != num)					goto usage;			}			goto addr2;		default:			msgq(sp, M_ERR,			    "Internal syntax table error (%s: %c).",			    cp->name, *p);		}	}	/* Skip trailing whitespace. */	for (; cmdlen; --cmdlen) {		ch = *cmd++;		if (!isblank(ch))			break;	}	/*	 * There shouldn't be anything left, and no more required	 * fields, i.e neither 'l' or 'r' in the syntax string.	 */	if (cmdlen || strpbrk(p, "lr")) {usage:		msgq(sp, M_ERR, "Usage: %s.", cp->usage);		goto err;	}	/* Verify that the addresses are legal. */addr2:	switch (exc.addrcnt) {	case 2:		if (file_lline(sp, ep, &lno))			goto err;		/*		 * Historic ex/vi permitted commands with counts to go past		 * EOF.  So, for example, if the file only had 5 lines, the		 * ex command "1,6>" would fail, but the command ">300"		 * would succeed.  Since we don't want to have to make all		 * of the underlying commands handle random line numbers,		 * fix it here.		 */		if (exc.addr2.lno > lno)			if (F_ISSET(&exc, E_COUNT))				exc.addr2.lno = lno;			else {				if (lno == 0)					msgq(sp, M_ERR, "The file is empty.");				else					msgq(sp, M_ERR,					    "Only %lu line%s in the file.",					    lno, lno > 1 ? "s" : "");				goto err;			}		/* FALLTHROUGH */	case 1:		num = exc.addr1.lno;		/*		 * If it's a "default vi command", zero is okay.  Historic		 * vi allowed this, note, it's also the hack that allows		 * "vi + nonexistent_file" to work.		 */		if (num == 0 && (!IN_VI_MODE(sp) || uselastcmd != 1) &&		    !LF_ISSET(E_ZERO)) {			msgq(sp, M_ERR,			    "The %s command doesn't permit an address of 0.",			    cp->name);			goto err;		}		if (file_lline(sp, ep, &lno))			goto err;		if (num > lno) {			if (lno == 0)				msgq(sp, M_ERR, "The file is empty.");			else				msgq(sp, M_ERR, "Only %lu line%s in the file.",				    lno, lno > 1 ? "s" : "");			goto err;		}		break;

⌨️ 快捷键说明

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