コード例 #1
0
ファイル: ex_move.c プロジェクト: Hooman3/minix
/*
 * ex_copy -- :[line [,line]] co[py] line [flags]
 *	Copy selected lines.
 *
 * PUBLIC: int ex_copy __P((SCR *, EXCMD *));
 */
int
ex_copy(SCR *sp, EXCMD *cmdp)
{
	CB cb;
	MARK fm1, fm2, m, tm;
	db_recno_t cnt;
	int rval;

	rval = 0;

	NEEDFILE(sp, cmdp);

	/*
	 * It's possible to copy things into the area that's being
	 * copied, e.g. "2,5copy3" is legitimate.  Save the text to
	 * a cut buffer.
	 */
	fm1 = cmdp->addr1;
	fm2 = cmdp->addr2;
	memset(&cb, 0, sizeof(cb));
	TAILQ_INIT(&cb.textq);
	for (cnt = fm1.lno; cnt <= fm2.lno; ++cnt)
		if (cut_line(sp, cnt, 0, ENTIRE_LINE, &cb)) {
			rval = 1;
			goto err;
		}
	cb.flags |= CB_LMODE;

	/* Put the text into place. */
	tm.lno = cmdp->lineno;
	tm.cno = 0;
	if (put(sp, &cb, NULL, &tm, &m, 1))
		rval = 1;
	else {
		/*
		 * Copy puts the cursor on the last line copied.  The cursor
		 * returned by the put routine is the first line put, not the
		 * last, because that's the historic semantic of vi.
		 */
		cnt = (fm2.lno - fm1.lno) + 1;
		sp->lno = m.lno + (cnt - 1);
		sp->cno = 0;
	}
err:	text_lfree(&cb.textq);
	return (rval);
}
コード例 #2
0
ファイル: ex_write.c プロジェクト: alexandermerritt/dragonfly
/*
 * ex_xit -- :x[it]! [file]
 *	Write out any modifications and quit.
 *
 * PUBLIC: int ex_xit __P((SCR *, EXCMD *));
 */
int
ex_xit(SCR *sp, EXCMD *cmdp)
{
	int force;

	NEEDFILE(sp, cmdp);

	if (F_ISSET(sp->ep, F_MODIFIED) && exwr(sp, cmdp, XIT))
		return (1);
	if (file_m3(sp, 0))
		return (1);

	force = FL_ISSET(cmdp->iflags, E_C_FORCE);

	if (ex_ncheck(sp, force))
		return (1);

	F_SET(sp, force ? SC_EXIT_FORCE : SC_EXIT);
	return (0);
}
コード例 #3
0
ファイル: ex_z.c プロジェクト: coyizumi/cs111
/*
 * ex_z -- :[line] z [^-.+=] [count] [flags]
 *	Adjust window.
 *
 * PUBLIC: int ex_z __P((SCR *, EXCMD *));
 */
int
ex_z(SCR *sp, EXCMD *cmdp)
{
    MARK abs;
    recno_t cnt, equals, lno;
    int eofcheck;

    NEEDFILE(sp, cmdp);

    /*
     * !!!
     * If no count specified, use either two times the size of the
     * scrolling region, or the size of the window option.  POSIX
     * 1003.2 claims that the latter is correct, but historic ex/vi
     * documentation and practice appear to use the scrolling region.
     * I'm using the window size as it means that the entire screen
     * is used instead of losing a line to roundoff.  Note, we drop
     * a line from the cnt if using the window size to leave room for
     * the next ex prompt.
     */
    if (FL_ISSET(cmdp->iflags, E_C_COUNT))
        cnt = cmdp->count;
    else
#ifdef HISTORICAL_PRACTICE
        cnt = O_VAL(sp, O_SCROLL) * 2;
#else
        cnt = O_VAL(sp, O_WINDOW) - 1;
#endif

    equals = 0;
    eofcheck = 0;
    lno = cmdp->addr1.lno;

    switch (FL_ISSET(cmdp->iflags,
                     E_C_CARAT | E_C_DASH | E_C_DOT | E_C_EQUAL | E_C_PLUS)) {
    case E_C_CARAT:		/* Display cnt * 2 before the line. */
        eofcheck = 1;
        if (lno > cnt * 2)
            cmdp->addr1.lno = (lno - cnt * 2) + 1;
        else
            cmdp->addr1.lno = 1;
        cmdp->addr2.lno = (cmdp->addr1.lno + cnt) - 1;
        break;
    case E_C_DASH:		/* Line goes at the bottom of the screen. */
        cmdp->addr1.lno = lno > cnt ? (lno - cnt) + 1 : 1;
        cmdp->addr2.lno = lno;
        break;
    case E_C_DOT:		/* Line goes in the middle of the screen. */
        /*
         * !!!
         * Historically, the "middleness" of the line overrode the
         * count, so that "3z.19" or "3z.20" would display the first
         * 12 lines of the file, i.e. (N - 1) / 2 lines before and
         * after the specified line.
         */
        eofcheck = 1;
        cnt = (cnt - 1) / 2;
        cmdp->addr1.lno = lno > cnt ? lno - cnt : 1;
        cmdp->addr2.lno = lno + cnt;

        /*
         * !!!
         * Historically, z. set the absolute cursor mark.
         */
        abs.lno = sp->lno;
        abs.cno = sp->cno;
        (void)mark_set(sp, ABSMARK1, &abs, 1);
        break;
    case E_C_EQUAL:		/* Center with hyphens. */
        /*
         * !!!
         * Strangeness.  The '=' flag is like the '.' flag (see the
         * above comment, it applies here as well) but with a special
         * little hack.  Print out lines of hyphens before and after
         * the specified line.  Additionally, the cursor remains set
         * on that line.
         */
        eofcheck = 1;
        cnt = (cnt - 1) / 2;
        cmdp->addr1.lno = lno > cnt ? lno - cnt : 1;
        cmdp->addr2.lno = lno - 1;
        if (ex_pr(sp, cmdp))
            return (1);
        (void)ex_puts(sp, "----------------------------------------\n");
        cmdp->addr2.lno = cmdp->addr1.lno = equals = lno;
        if (ex_pr(sp, cmdp))
            return (1);
        (void)ex_puts(sp, "----------------------------------------\n");
        cmdp->addr1.lno = lno + 1;
        cmdp->addr2.lno = (lno + cnt) - 1;
        break;
    default:
        /* If no line specified, move to the next one. */
        if (F_ISSET(cmdp, E_ADDR_DEF))
            ++lno;
    /* FALLTHROUGH */
    case E_C_PLUS:		/* Line goes at the top of the screen. */
        eofcheck = 1;
        cmdp->addr1.lno = lno;
        cmdp->addr2.lno = (lno + cnt) - 1;
        break;
    }

    if (eofcheck) {
        if (db_last(sp, &lno))
            return (1);
        if (cmdp->addr2.lno > lno)
            cmdp->addr2.lno = lno;
    }

    if (ex_pr(sp, cmdp))
        return (1);
    if (equals)
        sp->lno = equals;
    return (0);
}
コード例 #4
0
/*
 * ex_aci --
 *	Append, change, insert in ex.
 */
static int
ex_aci(SCR *sp, EXCMD *cmdp, enum which cmd)
{
	CHAR_T *p, *t;
	GS *gp;
	TEXT *tp;
	TEXTH tiq;
	db_recno_t cnt, lno;
	size_t len;
	u_int32_t flags;
	int need_newline;

	gp = sp->gp;
	NEEDFILE(sp, cmdp);

	/*
	 * If doing a change, replace lines for as long as possible.  Then,
	 * append more lines or delete remaining lines.  Changes to an empty
	 * file are appends, inserts are the same as appends to the previous
	 * line.
	 *
	 * !!!
	 * Set the address to which we'll append.  We set sp->lno to this
	 * address as well so that autoindent works correctly when get text
	 * from the user.
	 */
	lno = cmdp->addr1.lno;
	sp->lno = lno;
	if ((cmd == CHANGE || cmd == INSERT) && lno != 0)
		--lno;

	/*
	 * !!!
	 * If the file isn't empty, cut changes into the unnamed buffer.
	 */
	if (cmd == CHANGE && cmdp->addr1.lno != 0 &&
	    (cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE) ||
	    del(sp, &cmdp->addr1, &cmdp->addr2, 1)))
		return (1);

	/*
	 * !!!
	 * Anything that was left after the command separator becomes part
	 * of the inserted text.  Apparently, it was common usage to enter:
	 *
	 *	:g/pattern/append|stuff1
	 *
	 * and append the line of text "stuff1" to the lines containing the
	 * pattern.  It was also historically legal to enter:
	 *
	 *	:append|stuff1
	 *	stuff2
	 *	.
	 *
	 * and the text on the ex command line would be appended as well as
	 * the text inserted after it.  There was an historic bug however,
	 * that the user had to enter *two* terminating lines (the '.' lines)
	 * to terminate text input mode, in this case.  This whole thing
	 * could be taken too far, however.  Entering:
	 *
	 *	:append|stuff1\
	 *	stuff2
	 *	stuff3
	 *	.
	 *
	 * i.e. mixing and matching the forms confused the historic vi, and,
	 * not only did it take two terminating lines to terminate text input
	 * mode, but the trailing backslashes were retained on the input.  We
	 * match historic practice except that we discard the backslashes.
	 *
	 * Input lines specified on the ex command line lines are separated by
	 * <newline>s.  If there is a trailing delimiter an empty line was
	 * inserted.  There may also be a leading delimiter, which is ignored
	 * unless it's also a trailing delimiter.  It is possible to encounter
	 * a termination line, i.e. a single '.', in a global command, but not
	 * necessary if the text insert command was the last of the global
	 * commands.
	 */
	if (cmdp->save_cmdlen != 0) {
		for (p = cmdp->save_cmd,
		    len = cmdp->save_cmdlen; len > 0; p = t) {
			for (t = p; len > 0 && t[0] != '\n'; ++t, --len);
			if (t != p || len == 0) {
				if (F_ISSET(sp, SC_EX_GLOBAL) &&
				    t - p == 1 && p[0] == '.') {
					++t;
					if (len > 0)
						--len;
					break;
				}
				if (db_append(sp, 1, lno++, p, t - p))
					return (1);
			}
			if (len != 0) {
				++t;
				if (--len == 0 &&
				    db_append(sp, 1, lno++, NULL, 0))
					return (1);
			}
		}
		/*
		 * If there's any remaining text, we're in a global, and
		 * there's more command to parse.
		 *
		 * !!!
		 * We depend on the fact that non-global commands will eat the
		 * rest of the command line as text input, and before getting
		 * any text input from the user.  Otherwise, we'd have to save
		 * off the command text before or during the call to the text
		 * input function below.
		 */
		if (len != 0)
			cmdp->save_cmd = t;
		cmdp->save_cmdlen = len;
	}

	if (F_ISSET(sp, SC_EX_GLOBAL)) {
		if ((sp->lno = lno) == 0 && db_exist(sp, 1))
			sp->lno = 1;
		return (0);
	}

	/*
	 * If not in a global command, read from the terminal.
	 *
	 * If this code is called by vi, we want to reset the terminal and use
	 * ex's line get routine.  It actually works fine if we use vi's get
	 * routine, but it doesn't look as nice.  Maybe if we had a separate
	 * window or something, but getting a line at a time looks awkward.
	 * However, depending on the screen that we're using, that may not
	 * be possible.
	 */
	if (F_ISSET(sp, SC_VI)) {
		if (gp->scr_screen(sp, SC_EX)) {
			ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON);
			return (1);
		}

		/* If we're still in the vi screen, move out explicitly. */
		need_newline = !F_ISSET(sp, SC_SCR_EXWROTE);
		F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
		if (need_newline)
			(void)ex_puts(sp, "\n");

		/*
		 * !!!
		 * Users of historical versions of vi sometimes get confused
		 * when they enter append mode, and can't seem to get out of
		 * it.  Give them an informational message.
		 */
		(void)ex_puts(sp,
		    msg_cat(sp, "273|Entering ex input mode.", NULL));
		(void)ex_puts(sp, "\n");
		(void)ex_fflush(sp);
	}

	/*
	 * Set input flags; the ! flag turns off autoindent for append,
	 * change and insert.
	 */
	LF_INIT(TXT_DOTTERM | TXT_NUMBER);
	if (!FL_ISSET(cmdp->iflags, E_C_FORCE) && O_ISSET(sp, O_AUTOINDENT))
		LF_SET(TXT_AUTOINDENT);
	if (O_ISSET(sp, O_BEAUTIFY))
		LF_SET(TXT_BEAUTIFY);

	/*
	 * This code can't use the common screen TEXTH structure (sp->tiq),
	 * as it may already be in use, e.g. ":append|s/abc/ABC/" would fail
	 * as we are only halfway through the text when the append code fires.
	 * Use a local structure instead.  (The ex code would have to use a
	 * local structure except that we're guaranteed to finish remaining
	 * characters in the common TEXTH structure when they were inserted
	 * into the file, above.)
	 */
	memset(&tiq, 0, sizeof(TEXTH));
	TAILQ_INIT(&tiq);

	if (ex_txt(sp, &tiq, 0, flags))
		return (1);

	for (cnt = 0, tp = TAILQ_FIRST(&tiq); tp != NULL;
	    ++cnt, tp = TAILQ_NEXT(tp, q))
		if (db_append(sp, 1, lno++, tp->lb, tp->len))
			return (1);

	/*
	 * Set sp->lno to the final line number value (correcting for a
	 * possible 0 value) as that's historically correct for the final
	 * line value, whether or not the user entered any text.
	 */
	if ((sp->lno = lno) == 0 && db_exist(sp, 1))
		sp->lno = 1;

	return (0);
}
コード例 #5
0
/*
 * shift --
 *	Ex shift support.
 */
static int
shift(SCR *sp, EXCMD *cmdp, enum which rl)
{
	db_recno_t from, to;
	size_t blen, len, newcol, newidx, oldcol, oldidx, sw;
	int curset;
	CHAR_T *p;
	CHAR_T *bp, *tbp;

	NEEDFILE(sp, cmdp);

	if (O_VAL(sp, O_SHIFTWIDTH) == 0) {
		msgq(sp, M_INFO, "152|shiftwidth option set to 0");
		return (0);
	}

	/* Copy the lines being shifted into the unnamed buffer. */
	if (cut(sp, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE))
		return (1);

	/*
	 * The historic version of vi permitted the user to string any number
	 * of '>' or '<' characters together, resulting in an indent of the
	 * appropriate levels.  There's a special hack in ex_cmd() so that
	 * cmdp->argv[0] points to the string of '>' or '<' characters.
	 *
	 * Q: What's the difference between the people adding features
	 *    to vi and the Girl Scouts?
	 * A: The Girl Scouts have mint cookies and adult supervision.
	 */
	for (p = cmdp->argv[0]->bp, sw = 0; *p == '>' || *p == '<'; ++p)
		sw += O_VAL(sp, O_SHIFTWIDTH);

	GET_SPACE_RETW(sp, bp, blen, 256);

	curset = 0;
	for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; from <= to; ++from) {
		if (db_get(sp, from, DBG_FATAL, &p, &len))
			goto err;
		if (!len) {
			if (sp->lno == from)
				curset = 1;
			continue;
		}

		/*
		 * Calculate the old indent amount and the number of
		 * characters it used.
		 */
		for (oldidx = 0, oldcol = 0; oldidx < len; ++oldidx)
			if (p[oldidx] == ' ')
				++oldcol;
			else if (p[oldidx] == '\t')
				oldcol += O_VAL(sp, O_TABSTOP) -
				    oldcol % O_VAL(sp, O_TABSTOP);
			else
				break;

		/* Calculate the new indent amount. */
		if (rl == RIGHT)
			newcol = oldcol + sw;
		else {
			newcol = oldcol < sw ? 0 : oldcol - sw;
			if (newcol == oldcol) {
				if (sp->lno == from)
					curset = 1;
				continue;
			}
		}

		/* Get a buffer that will hold the new line. */
		ADD_SPACE_RETW(sp, bp, blen, newcol + len);

		/*
		 * Build a new indent string and count the number of
		 * characters it uses.
		 */
		tbp = bp;
		newidx = 0;
		if (!O_ISSET(sp, O_EXPANDTAB)) {
			for (; newcol >= O_VAL(sp, O_TABSTOP); ++newidx) {
				*tbp++ = '\t';
				newcol -= O_VAL(sp, O_TABSTOP);
			}
		}
		for (; newcol > 0; --newcol, ++newidx)
			*tbp++ = ' ';

		/* Add the original line. */
		MEMCPYW(tbp, p + oldidx, len - oldidx);

		/* Set the replacement line. */
		if (db_set(sp, from, bp, (tbp + (len - oldidx)) - bp)) {
err:			FREE_SPACEW(sp, bp, blen);
			return (1);
		}

		/*
		 * !!!
		 * The shift command in historic vi had the usual bizarre
		 * collection of cursor semantics.  If called from vi, the
		 * cursor was repositioned to the first non-blank character
		 * of the lowest numbered line shifted.  If called from ex,
		 * the cursor was repositioned to the first non-blank of the
		 * highest numbered line shifted.  Here, if the cursor isn't
		 * part of the set of lines that are moved, move it to the
		 * first non-blank of the last line shifted.  (This makes
		 * ":3>>" in vi work reasonably.)  If the cursor is part of
		 * the shifted lines, it doesn't get moved at all.  This
		 * permits shifting of marked areas, i.e. ">'a." shifts the
		 * marked area twice, something that couldn't be done with
		 * historic vi.
		 */
		if (sp->lno == from) {
			curset = 1;
			if (newidx > oldidx)
				sp->cno += newidx - oldidx;
			else if (sp->cno >= oldidx - newidx)
				sp->cno -= oldidx - newidx;
		}
	}
	if (!curset) {
		sp->lno = to;
		sp->cno = 0;
		(void)nonblank(sp, to, &sp->cno);
	}

	FREE_SPACEW(sp, bp, blen);

	sp->rptlines[L_SHIFT] += cmdp->addr2.lno - cmdp->addr1.lno + 1;
	return (0);
}
コード例 #6
0
ファイル: ex_subst.c プロジェクト: mosconi/openbsd
static int
s(SCR *sp, EXCMD *cmdp, char *s, regex_t *re, u_int flags)
{
	EVENT ev;
	MARK from, to;
	TEXTH tiq;
	recno_t elno, lno, slno;
	regmatch_t match[10];
	size_t blen, cnt, last, lbclen, lblen, len, llen;
	size_t offset, saved_offset, scno;
	int lflag, nflag, pflag, rflag;
	int didsub, do_eol_match, eflags, empty_ok, eval;
	int linechanged, matched, quit, rval;
	unsigned long ul;
	char *bp, *lb;

	NEEDFILE(sp, cmdp);

	slno = sp->lno;
	scno = sp->cno;

	/*
	 * !!!
	 * Historically, the 'g' and 'c' suffices were always toggled as flags,
	 * so ":s/A/B/" was the same as ":s/A/B/ccgg".  If O_EDCOMPATIBLE was
	 * not set, they were initialized to 0 for all substitute commands.  If
	 * O_EDCOMPATIBLE was set, they were initialized to 0 only if the user
	 * specified substitute/replacement patterns (see ex_s()).
	 */
	if (!O_ISSET(sp, O_EDCOMPATIBLE))
		sp->c_suffix = sp->g_suffix = 0;

	/*
	 * Historic vi permitted the '#', 'l' and 'p' options in vi mode, but
	 * it only displayed the last change.  I'd disallow them, but they are
	 * useful in combination with the [v]global commands.  In the current
	 * model the problem is combining them with the 'c' flag -- the screen
	 * would have to flip back and forth between the confirm screen and the
	 * ex print screen, which would be pretty awful.  We do display all
	 * changes, though, for what that's worth.
	 *
	 * !!!
	 * Historic vi was fairly strict about the order of "options", the
	 * count, and "flags".  I'm somewhat fuzzy on the difference between
	 * options and flags, anyway, so this is a simpler approach, and we
	 * just take it them in whatever order the user gives them.  (The ex
	 * usage statement doesn't reflect this.)
	 */
	lflag = nflag = pflag = rflag = 0;
	if (s == NULL)
		goto noargs;
	for (lno = OOBLNO; *s != '\0'; ++s)
		switch (*s) {
		case ' ':
		case '\t':
			continue;
		case '+':
			++cmdp->flagoff;
			break;
		case '-':
			--cmdp->flagoff;
			break;
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			if (lno != OOBLNO)
				goto usage;
			errno = 0;
			if ((ul = strtoul(s, &s, 10)) >= UINT_MAX)
				errno = ERANGE;
			if (*s == '\0')		/* Loop increment correction. */
				--s;
			if (errno == ERANGE) {
				if (ul >= UINT_MAX)
					msgq(sp, M_ERR, "Count overflow");
				else
					msgq(sp, M_SYSERR, NULL);
				return (1);
			}
			lno = (recno_t)ul;
			/*
			 * In historic vi, the count was inclusive from the
			 * second address.
			 */
			cmdp->addr1.lno = cmdp->addr2.lno;
			cmdp->addr2.lno += lno - 1;
			if (!db_exist(sp, cmdp->addr2.lno) &&
			    db_last(sp, &cmdp->addr2.lno))
				return (1);
			break;
		case '#':
			nflag = 1;
			break;
		case 'c':
			sp->c_suffix = !sp->c_suffix;

			/* Ex text structure initialization. */
			if (F_ISSET(sp, SC_EX)) {
				memset(&tiq, 0, sizeof(TEXTH));
				TAILQ_INIT(&tiq);
			}
			break;
		case 'g':
			sp->g_suffix = !sp->g_suffix;
			break;
		case 'l':
			lflag = 1;
			break;
		case 'p':
			pflag = 1;
			break;
		case 'r':
			if (LF_ISSET(SUB_FIRST)) {
				msgq(sp, M_ERR,
		    "Regular expression specified; r flag meaningless");
				return (1);
			}
			if (!F_ISSET(sp, SC_RE_SEARCH)) {
				ex_emsg(sp, NULL, EXM_NOPREVRE);
				return (1);
			}
			rflag = 1;
			re = &sp->re_c;
			break;
		default:
			goto usage;
		}

	if (*s != '\0' || (!rflag && LF_ISSET(SUB_MUSTSETR))) {
usage:		ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
		return (1);
	}

noargs:	if (F_ISSET(sp, SC_VI) && sp->c_suffix && (lflag || nflag || pflag)) {
		msgq(sp, M_ERR,
"The #, l and p flags may not be combined with the c flag in vi mode");
		return (1);
	}

	/*
	 * bp:		if interactive, line cache
	 * blen:	if interactive, line cache length
	 * lb:		build buffer pointer.
	 * lbclen:	current length of built buffer.
	 * lblen;	length of build buffer.
	 */
	bp = lb = NULL;
	blen = lbclen = lblen = 0;

	/* For each line... */
	for (matched = quit = 0, lno = cmdp->addr1.lno,
	    elno = cmdp->addr2.lno; !quit && lno <= elno; ++lno) {

		/* Someone's unhappy, time to stop. */
		if (INTERRUPTED(sp))
			break;

		/* Get the line. */
		if (db_get(sp, lno, DBG_FATAL, &s, &llen))
			goto err;

		/*
		 * Make a local copy if doing confirmation -- when calling
		 * the confirm routine we're likely to lose the cached copy.
		 */
		if (sp->c_suffix) {
			if (bp == NULL) {
				GET_SPACE_RET(sp, bp, blen, llen);
			} else
				ADD_SPACE_RET(sp, bp, blen, llen);
			memcpy(bp, s, llen);
			s = bp;
		}

		/* Start searching from the beginning. */
		offset = 0;
		len = llen;

		/* Reset the build buffer offset. */
		lbclen = 0;

		/* Reset empty match flag. */
		empty_ok = 1;

		/*
		 * We don't want to have to do a setline if the line didn't
		 * change -- keep track of whether or not this line changed.
		 * If doing confirmations, don't want to keep setting the
		 * line if change is refused -- keep track of substitutions.
		 */
		didsub = linechanged = 0;

		/* New line, do an EOL match. */
		do_eol_match = 1;

		/* It's not nul terminated, but we pretend it is. */
		eflags = REG_STARTEND;

		/*
		 * The search area is from s + offset to the EOL.
		 *
		 * Generally, match[0].rm_so is the offset of the start
		 * of the match from the start of the search, and offset
		 * is the offset of the start of the last search.
		 */
nextmatch:	match[0].rm_so = 0;
		match[0].rm_eo = len;

		/* Get the next match. */
		eval = regexec(re, (char *)s + offset, 10, match, eflags);

		/*
		 * There wasn't a match or if there was an error, deal with
		 * it.  If there was a previous match in this line, resolve
		 * the changes into the database.  Otherwise, just move on.
		 */
		if (eval == REG_NOMATCH)
			goto endmatch;
		if (eval != 0) {
			re_error(sp, eval, re);
			goto err;
		}
		matched = 1;

		/* Only the first search can match an anchored expression. */
		eflags |= REG_NOTBOL;

		/*
		 * !!!
		 * It's possible to match 0-length strings -- for example, the
		 * command s;a*;X;, when matched against the string "aabb" will
		 * result in "XbXbX", i.e. the matches are "aa", the space
		 * between the b's and the space between the b's and the end of
		 * the string.  There is a similar space between the beginning
		 * of the string and the a's.  The rule that we use (because vi
		 * historically used it) is that any 0-length match, occurring
		 * immediately after a match, is ignored.  Otherwise, the above
		 * example would have resulted in "XXbXbX".  Another example is
		 * incorrectly using " *" to replace groups of spaces with one
		 * space.
		 *
		 * The way we do this is that if we just had a successful match,
		 * the starting offset does not skip characters, and the match
		 * is empty, ignore the match and move forward.  If there's no
		 * more characters in the string, we were attempting to match
		 * after the last character, so quit.
		 */
		if (!empty_ok && match[0].rm_so == 0 && match[0].rm_eo == 0) {
			empty_ok = 1;
			if (len == 0)
				goto endmatch;
			BUILD(sp, s + offset, 1)
			++offset;
			--len;
			goto nextmatch;
		}

		/* Confirm change. */
		if (sp->c_suffix) {
			/*
			 * Set the cursor position for confirmation.  Note,
			 * if we matched on a '$', the cursor may be past
			 * the end of line.
			 */
			from.lno = to.lno = lno;
			from.cno = match[0].rm_so + offset;
			to.cno = match[0].rm_eo + offset;
			/*
			 * Both ex and vi have to correct for a change before
			 * the first character in the line.
			 */
			if (llen == 0)
				from.cno = to.cno = 0;
			if (F_ISSET(sp, SC_VI)) {
				/*
				 * Only vi has to correct for a change after
				 * the last character in the line.
				 *
				 * XXX
				 * It would be nice to change the vi code so
				 * that we could display a cursor past EOL.
				 */
				if (to.cno >= llen)
					to.cno = llen - 1;
				if (from.cno >= llen)
					from.cno = llen - 1;

				sp->lno = from.lno;
				sp->cno = from.cno;
				if (vs_refresh(sp, 1))
					goto err;

				vs_update(sp, "Confirm change? [n]", NULL);

				if (v_event_get(sp, &ev, 0, 0))
					goto err;
				switch (ev.e_event) {
				case E_CHARACTER:
					break;
				case E_EOF:
				case E_ERR:
				case E_INTERRUPT:
					goto lquit;
				default:
					v_event_err(sp, &ev);
					goto lquit;
				}
			} else {
				if (ex_print(sp, cmdp, &from, &to, 0) ||
				    ex_scprint(sp, &from, &to))
					goto lquit;
				if (ex_txt(sp, &tiq, 0, TXT_CR))
					goto err;
				ev.e_c = TAILQ_FIRST(&tiq)->lb[0];
			}

			switch (ev.e_c) {
			case CH_YES:
				break;
			default:
			case CH_NO:
				didsub = 0;
				BUILD(sp, s +offset, match[0].rm_eo);
				goto skip;
			case CH_QUIT:
				/* Set the quit/interrupted flags. */
lquit:				quit = 1;
				F_SET(sp->gp, G_INTERRUPTED);

				/*
				 * Resolve any changes, then return to (and
				 * exit from) the main loop.
				 */
				goto endmatch;
			}
		}

		/*
		 * Set the cursor to the last position changed, converting
		 * from 1-based to 0-based.
		 */
		sp->lno = lno;
		sp->cno = match[0].rm_so;

		/* Copy the bytes before the match into the build buffer. */
		BUILD(sp, s + offset, match[0].rm_so);

		/* Substitute the matching bytes. */
		didsub = 1;
		if (re_sub(sp, s + offset, &lb, &lbclen, &lblen, match))
			goto err;

		/* Set the change flag so we know this line was modified. */
		linechanged = 1;

		/* Move past the matched bytes. */
skip:		offset += match[0].rm_eo;
		len -= match[0].rm_eo;

		/* A match cannot be followed by an empty pattern. */
		empty_ok = 0;

		/*
		 * If doing a global change with confirmation, we have to
		 * update the screen.  The basic idea is to store the line
		 * so the screen update routines can find it, and restart.
		 */
		if (didsub && sp->c_suffix && sp->g_suffix) {
			/*
			 * The new search offset will be the end of the
			 * modified line.
			 */
			saved_offset = lbclen;

			/* Copy the rest of the line. */
			if (len)
				BUILD(sp, s + offset, len)

			/* Set the new offset. */
			offset = saved_offset;

			/* Store inserted lines, adjusting the build buffer. */
			last = 0;
			if (sp->newl_cnt) {
				for (cnt = 0;
				    cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
					if (db_insert(sp, lno,
					    lb + last, sp->newl[cnt] - last))
						goto err;
					last = sp->newl[cnt] + 1;
					++sp->rptlines[L_ADDED];
				}
				lbclen -= last;
				offset -= last;
				sp->newl_cnt = 0;
			}

			/* Store and retrieve the line. */
			if (db_set(sp, lno, lb + last, lbclen))
				goto err;
			if (db_get(sp, lno, DBG_FATAL, &s, &llen))
				goto err;
			ADD_SPACE_RET(sp, bp, blen, llen)
			memcpy(bp, s, llen);
			s = bp;
			len = llen - offset;

			/* Restart the build. */
			lbclen = 0;
			BUILD(sp, s, offset);

			/*
			 * If we haven't already done the after-the-string
			 * match, do one.  Set REG_NOTEOL so the '$' pattern
			 * only matches once.
			 */
			if (!do_eol_match)
				goto endmatch;
			if (offset == len) {
				do_eol_match = 0;
				eflags |= REG_NOTEOL;
			}
			goto nextmatch;
		}

		/*
		 * If it's a global:
		 *
		 * If at the end of the string, do a test for the after
		 * the string match.  Set REG_NOTEOL so the '$' pattern
		 * only matches once.
		 */
		if (sp->g_suffix && do_eol_match) {
			if (len == 0) {
				do_eol_match = 0;
				eflags |= REG_NOTEOL;
			}
			goto nextmatch;
		}

endmatch:	if (!linechanged)
			continue;

		/* Copy any remaining bytes into the build buffer. */
		if (len)
			BUILD(sp, s + offset, len)

		/* Store inserted lines, adjusting the build buffer. */
		last = 0;
		if (sp->newl_cnt) {
			for (cnt = 0;
			    cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
				if (db_insert(sp,
				    lno, lb + last, sp->newl[cnt] - last))
					goto err;
				last = sp->newl[cnt] + 1;
				++sp->rptlines[L_ADDED];
			}
			lbclen -= last;
			sp->newl_cnt = 0;
		}

		/* Store the changed line. */
		if (db_set(sp, lno, lb + last, lbclen))
			goto err;

		/* Update changed line counter. */
		if (sp->rptlchange != lno) {
			sp->rptlchange = lno;
			++sp->rptlines[L_CHANGED];
		}

		/*
		 * !!!
		 * Display as necessary.  Historic practice is to only
		 * display the last line of a line split into multiple
		 * lines.
		 */
		if (lflag || nflag || pflag) {
			from.lno = to.lno = lno;
			from.cno = to.cno = 0;
			if (lflag)
				(void)ex_print(sp, cmdp, &from, &to, E_C_LIST);
			if (nflag)
				(void)ex_print(sp, cmdp, &from, &to, E_C_HASH);
			if (pflag)
				(void)ex_print(sp, cmdp, &from, &to, E_C_PRINT);
		}
	}

	/*
	 * !!!
	 * Historically, vi attempted to leave the cursor at the same place if
	 * the substitution was done at the current cursor position.  Otherwise
	 * it moved it to the first non-blank of the last line changed.  There
	 * were some problems: for example, :s/$/foo/ with the cursor on the
	 * last character of the line left the cursor on the last character, or
	 * the & command with multiple occurrences of the matching string in the
	 * line usually left the cursor in a fairly random position.
	 *
	 * We try to do the same thing, with the exception that if the user is
	 * doing substitution with confirmation, we move to the last line about
	 * which the user was consulted, as opposed to the last line that they
	 * actually changed.  This prevents a screen flash if the user doesn't
	 * change many of the possible lines.
	 */
	if (!sp->c_suffix && (sp->lno != slno || sp->cno != scno)) {
		sp->cno = 0;
		(void)nonblank(sp, sp->lno, &sp->cno);
	}

	/*
	 * If not in a global command, and nothing matched, say so.
	 * Else, if none of the lines displayed, put something up.
	 */
	rval = 0;
	if (!matched) {
		if (!F_ISSET(sp, SC_EX_GLOBAL)) {
			msgq(sp, M_ERR, "No match found");
			goto err;
		}
	} else if (!lflag && !nflag && !pflag)
		F_SET(cmdp, E_AUTOPRINT);

	if (0) {
err:		rval = 1;
	}

	if (bp != NULL)
		FREE_SPACE(sp, bp, blen);
	if (lb != NULL)
		free(lb);
	return (rval);
}
コード例 #7
0
ファイル: ex_write.c プロジェクト: alexandermerritt/dragonfly
/*
 * exwr --
 *	The guts of the ex write commands.
 */
static int
exwr(SCR *sp, EXCMD *cmdp, enum which cmd)
{
	MARK rm;
	int flags;
	char *name;
	CHAR_T *p = NULL;
	size_t nlen;
	char *n;
	int rc;
	EX_PRIVATE *exp;

	NEEDFILE(sp, cmdp);

	/* All write commands can have an associated '!'. */
	LF_INIT(FS_POSSIBLE);
	if (FL_ISSET(cmdp->iflags, E_C_FORCE))
		LF_SET(FS_FORCE);

	/* Skip any leading whitespace. */
	if (cmdp->argc != 0)
		for (p = cmdp->argv[0]->bp; *p != '\0' && cmdskip(*p); ++p);

	/* If "write !" it's a pipe to a utility. */
	if (cmdp->argc != 0 && cmd == WRITE && *p == '!') {
		/* Secure means no shell access. */
		if (O_ISSET(sp, O_SECURE)) {
			ex_wemsg(sp, cmdp->cmd->name, EXM_SECURE_F);
			return (1);
		}

		/* Expand the argument. */
		for (++p; *p && cmdskip(*p); ++p);
		if (*p == '\0') {
			ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
			return (1);
		}
		if (argv_exp1(sp, cmdp, p, STRLEN(p), 1))
			return (1);

		/* Set the last bang command */
		exp = EXP(sp);
		free(exp->lastbcomm);
		exp->lastbcomm = v_wstrdup(sp, cmdp->argv[1]->bp,
		    cmdp->argv[1]->len);

		/*
		 * Historically, vi waited after a write filter even if there
		 * wasn't any output from the command.  People complained when
		 * nvi waited only if there was output, wanting the visual cue
		 * that the program hadn't written anything.
		 */
		F_SET(sp, SC_EX_WAIT_YES);

		/*
		 * !!!
		 * Ignore the return cursor position, the cursor doesn't
		 * move.
		 */
		if (ex_filter(sp, cmdp, &cmdp->addr1,
		    &cmdp->addr2, &rm, cmdp->argv[1]->bp, FILTER_WRITE))
			return (1);

		/* Ex terminates with a bang, even if the command fails. */
		if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT))
			(void)ex_puts(sp, "!\n");

		return (0);
	}

	/* Set the FS_ALL flag if we're writing the entire file. */
	if (cmdp->addr1.lno <= 1 && !db_exist(sp, cmdp->addr2.lno + 1))
		LF_SET(FS_ALL);

	/* If "write >>" it's an append to a file. */
	if (cmdp->argc != 0 && cmd != XIT && p[0] == '>' && p[1] == '>') {
		LF_SET(FS_APPEND);

		/* Skip ">>" and whitespace. */
		for (p += 2; *p && cmdskip(*p); ++p);
	}

	/* If no other arguments, just write the file back. */
	if (cmdp->argc == 0 || *p == '\0')
		return (file_write(sp,
		    &cmdp->addr1, &cmdp->addr2, NULL, flags));

	/* Build an argv so we get an argument count and file expansion. */
	if (argv_exp2(sp, cmdp, p, STRLEN(p)))
		return (1);

	/*
	 *  0 args: impossible.
	 *  1 args: impossible (I hope).
	 *  2 args: read it.
	 * >2 args: object, too many args.
	 *
	 * The 1 args case depends on the argv_sexp() function refusing
	 * to return success without at least one non-blank character.
	 */
	switch (cmdp->argc) {
	case 0:
	case 1:
		abort();
		/* NOTREACHED */
	case 2:
		INT2CHAR(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len+1,
			 n, nlen);
		name = v_strdup(sp, n, nlen - 1);

		/*
		 * !!!
		 * Historically, the read and write commands renamed
		 * "unnamed" files, or, if the file had a name, set
		 * the alternate file name.
		 */
		if (F_ISSET(sp->frp, FR_TMPFILE) &&
		    !F_ISSET(sp->frp, FR_EXNAMED)) {
			if ((n = v_strdup(sp, name, nlen - 1)) != NULL) {
				free(sp->frp->name);
				sp->frp->name = n;
			}
			/*
			 * The file has a real name, it's no longer a
			 * temporary, clear the temporary file flags.
			 *
			 * !!!
			 * If we're writing the whole file, FR_NAMECHANGE
			 * will be cleared by the write routine -- this is
			 * historic practice.
			 */
			F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE);
			F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED);

			/* Notify the screen. */
			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
		} else
			set_alt_name(sp, name);
		break;
	default:
		INT2CHAR(sp, p, STRLEN(p) + 1, n, nlen);
		ex_emsg(sp, n, EXM_FILECOUNT);
		return (1);
	}

	rc = file_write(sp, &cmdp->addr1, &cmdp->addr2, name, flags);

	free(name);

	return rc;
}
コード例 #8
0
ファイル: ex_move.c プロジェクト: ajinkya93/OpenBSD
/*
 * ex_move -- :[line [,line]] mo[ve] line
 *	Move selected lines.
 *
 * PUBLIC: int ex_move(SCR *, EXCMD *);
 */
int
ex_move(SCR *sp, EXCMD *cmdp)
{
	LMARK *lmp;
	MARK fm1, fm2;
	recno_t cnt, diff, fl, tl, mfl, mtl;
	size_t blen, len;
	int mark_reset;
	char *bp, *p;

	NEEDFILE(sp, cmdp);

	/*
	 * It's not possible to move things into the area that's being
	 * moved.
	 */
	fm1 = cmdp->addr1;
	fm2 = cmdp->addr2;
	if (cmdp->lineno >= fm1.lno && cmdp->lineno <= fm2.lno) {
		msgq(sp, M_ERR, "Destination line is inside move range");
		return (1);
	}

	/*
	 * Log the positions of any marks in the to-be-deleted lines.  This
	 * has to work with the logging code.  What happens is that we log
	 * the old mark positions, make the changes, then log the new mark
	 * positions.  Then the marks end up in the right positions no matter
	 * which way the log is traversed.
	 *
	 * XXX
	 * Reset the MARK_USERSET flag so that the log can undo the mark.
	 * This isn't very clean, and should probably be fixed.
	 */
	fl = fm1.lno;
	tl = cmdp->lineno;

	/* Log the old positions of the marks. */
	mark_reset = 0;
	LIST_FOREACH(lmp, &sp->ep->marks, q)
		if (lmp->name != ABSMARK1 &&
		    lmp->lno >= fl && lmp->lno <= tl) {
			mark_reset = 1;
			F_CLR(lmp, MARK_USERSET);
			(void)log_mark(sp, lmp);
		}

	/* Get memory for the copy. */
	GET_SPACE_RET(sp, bp, blen, 256);

	/* Move the lines. */
	diff = (fm2.lno - fm1.lno) + 1;
	if (tl > fl) {				/* Destination > source. */
		mfl = tl - diff;
		mtl = tl;
		for (cnt = diff; cnt--;) {
			if (db_get(sp, fl, DBG_FATAL, &p, &len))
				return (1);
			BINC_RET(sp, bp, blen, len);
			memcpy(bp, p, len);
			if (db_append(sp, 1, tl, bp, len))
				return (1);
			if (mark_reset)
				LIST_FOREACH(lmp, &sp->ep->marks, q)
					if (lmp->name != ABSMARK1 &&
					    lmp->lno == fl)
						lmp->lno = tl + 1;
			if (db_delete(sp, fl))
				return (1);
		}
	} else {				/* Destination < source. */
コード例 #9
0
ファイル: ex_join.c プロジェクト: ajinkya93/OpenBSD
/*
 * ex_join -- :[line [,line]] j[oin][!] [count] [flags]
 *	Join lines.
 *
 * PUBLIC: int ex_join(SCR *, EXCMD *);
 */
int
ex_join(SCR *sp, EXCMD *cmdp)
{
	recno_t from, to;
	size_t blen, clen, len, tlen;
	int echar, extra, first;
	char *bp, *p, *tbp;

	NEEDFILE(sp, cmdp);

	from = cmdp->addr1.lno;
	to = cmdp->addr2.lno;

	/* Check for no lines to join. */
	if (!db_exist(sp, from + 1)) {
		msgq(sp, M_ERR, "No following lines to join");
		return (1);
	}

	GET_SPACE_RET(sp, bp, blen, 256);

	/*
	 * The count for the join command was off-by-one,
	 * historically, to other counts for other commands.
	 */
	if (FL_ISSET(cmdp->iflags, E_C_COUNT))
		++cmdp->addr2.lno;

	/*
	 * If only a single address specified, or, the same address
	 * specified twice, the from/two addresses will be the same.
	 */
	if (cmdp->addr1.lno == cmdp->addr2.lno)
		++cmdp->addr2.lno;

	clen = tlen = 0;
        for (first = 1,
	    from = cmdp->addr1.lno, to = cmdp->addr2.lno; from <= to; ++from) {
		/*
		 * Get next line.  Historic versions of vi allowed "10J" while
		 * less than 10 lines from the end-of-file, so we do too.
		 */
		if (db_get(sp, from, 0, &p, &len)) {
			cmdp->addr2.lno = from - 1;
			break;
		}

		/* Empty lines just go away. */
		if (len == 0)
			continue;

		/*
		 * Get more space if necessary.  Note, tlen isn't the length
		 * of the new line, it's roughly the amount of space needed.
		 * tbp - bp is the length of the new line.
		 */
		tlen += len + 2;
		ADD_SPACE_RET(sp, bp, blen, tlen);
		tbp = bp + clen;

		/*
		 * Historic practice:
		 *
		 * If force specified, join without modification.
		 * If the current line ends with whitespace, strip leading
		 *    whitespace from the joined line.
		 * If the next line starts with a ), do nothing.
		 * If the current line ends with ., insert two spaces.
		 * Else, insert one space.
		 *
		 * One change -- add ? and ! to the list of characters for
		 * which we insert two spaces.  I expect that POSIX 1003.2
		 * will require this as well.
		 *
		 * Echar is the last character in the last line joined.
		 */
		extra = 0;
		if (!first && !FL_ISSET(cmdp->iflags, E_C_FORCE)) {
			if (isblank(echar))
				for (; len && isblank(*p); --len, ++p);
			else if (p[0] != ')') {
				if (strchr(".?!", echar)) {
					*tbp++ = ' ';
					++clen;
					extra = 1;
				}
				*tbp++ = ' ';
				++clen;
				for (; len && isblank(*p); --len, ++p);
			}
		}

		if (len != 0) {
			memcpy(tbp, p, len);
			tbp += len;
			clen += len;
			echar = p[len - 1];
		} else
			echar = ' ';

		/*
		 * Historic practice for vi was to put the cursor at the first
		 * inserted whitespace character, if there was one, or the
		 * first character of the joined line, if there wasn't, or the
		 * last character of the line if joined to an empty line.  If
		 * a count was specified, the cursor was moved as described
		 * for the first line joined, ignoring subsequent lines.  If
		 * the join was a ':' command, the cursor was placed at the
		 * first non-blank character of the line unless the cursor was
		 * "attracted" to the end of line when the command was executed
		 * in which case it moved to the new end of line.  There are
		 * probably several more special cases, but frankly, my dear,
		 * I don't give a damn.  This implementation puts the cursor
		 * on the first inserted whitespace character, the first
		 * character of the joined line, or the last character of the
		 * line regardless.  Note, if the cursor isn't on the joined
		 * line (possible with : commands), it is reset to the starting
		 * line.
		 */
		if (first) {
			sp->cno = (tbp - bp) - (1 + extra);
			first = 0;
		} else
			sp->cno = (tbp - bp) - len - (1 + extra);
	}
	sp->lno = cmdp->addr1.lno;

	/* Delete the joined lines. */
        for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; to > from; --to)
		if (db_delete(sp, to))
			goto err;

	/* If the original line changed, reset it. */
	if (!first && db_set(sp, from, bp, tbp - bp)) {
err:		FREE_SPACE(sp, bp, blen);
		return (1);
	}
	FREE_SPACE(sp, bp, blen);

	sp->rptlines[L_JOINED] += (cmdp->addr2.lno - cmdp->addr1.lno) + 1;
	return (0);
}
コード例 #10
0
ファイル: ex_bang.c プロジェクト: lichray/nvi2
/*
 * ex_bang -- :[line [,line]] ! command
 *
 * Pass the rest of the line after the ! character to the program named by
 * the O_SHELL option.
 *
 * Historical vi did NOT do shell expansion on the arguments before passing
 * them, only file name expansion.  This means that the O_SHELL program got
 * "$t" as an argument if that is what the user entered.  Also, there's a
 * special expansion done for the bang command.  Any exclamation points in
 * the user's argument are replaced by the last, expanded ! command.
 *
 * There's some fairly amazing slop in this routine to make the different
 * ways of getting here display the right things.  It took a long time to
 * get it right (wrong?), so be careful.
 *
 * PUBLIC: int ex_bang(SCR *, EXCMD *);
 */
int
ex_bang(SCR *sp, EXCMD *cmdp)
{
	enum filtertype ftype;
	ARGS *ap;
	EX_PRIVATE *exp;
	MARK rm;
	recno_t lno;
	int rval;
	const char *msg;
	char *np;
	size_t nlen;

	ap = cmdp->argv[0];
	if (ap->len == 0) {
		ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
		return (1);
	}

	/* Set the "last bang command" remembered value. */
	exp = EXP(sp);
	free(exp->lastbcomm);
	if ((exp->lastbcomm = v_wstrdup(sp, ap->bp, ap->len)) == NULL) {
		msgq(sp, M_SYSERR, NULL);
		return (1);
	}

	/*
	 * If the command was modified by the expansion, it was historically
	 * redisplayed.
	 */
	if (F_ISSET(cmdp, E_MODIFY) && !F_ISSET(sp, SC_EX_SILENT)) {
		/*
		 * Display the command if modified.  Historic ex/vi displayed
		 * the command if it was modified due to file name and/or bang
		 * expansion.  If piping lines in vi, it would be immediately
		 * overwritten by any error or line change reporting.
		 */
		if (F_ISSET(sp, SC_VI))
			vs_update(sp, "!", ap->bp);
		else {
			(void)ex_printf(sp, "!"WS"\n", ap->bp);
			(void)ex_fflush(sp);
		}
	}

	/*
	 * If no addresses were specified, run the command.  If there's an
	 * underlying file, it's been modified and autowrite is set, write
	 * the file back.  If the file has been modified, autowrite is not
	 * set and the warn option is set, tell the user about the file.
	 */
	if (cmdp->addrcnt == 0) {
		msg = NULL;
		if (sp->ep != NULL && F_ISSET(sp->ep, F_MODIFIED))
			if (O_ISSET(sp, O_AUTOWRITE)) {
				if (file_aw(sp, FS_ALL))
					return (0);
			} else if (O_ISSET(sp, O_WARN) &&
			    !F_ISSET(sp, SC_EX_SILENT))
				msg = msg_cat(sp,
				    "303|File modified since last write.",
				    NULL);

		/* If we're still in a vi screen, move out explicitly. */
		INT2CHAR(sp, ap->bp, ap->len+1, np, nlen);
		(void)ex_exec_proc(sp,
		    cmdp, np, msg, !F_ISSET(sp, SC_EX | SC_SCR_EXWROTE));
	}

	/*
	 * If addresses were specified, pipe lines from the file through the
	 * command.
	 *
	 * Historically, vi lines were replaced by both the stdout and stderr
	 * lines of the command, but ex lines by only the stdout lines.  This
	 * makes no sense to me, so nvi makes it consistent for both, and
	 * matches vi's historic behavior.
	 */
	else {
		NEEDFILE(sp, cmdp);

		/* Autoprint is set historically, even if the command fails. */
		F_SET(cmdp, E_AUTOPRINT);

		/*
		 * !!!
		 * Historical vi permitted "!!" in an empty file.  When this
		 * happens, we arrive here with two addresses of 1,1 and a
		 * bad attitude.  The simple solution is to turn it into a
		 * FILTER_READ operation, with the exception that stdin isn't
		 * opened for the utility, and the cursor position isn't the
		 * same.  The only historic glitch (I think) is that we don't
		 * put an empty line into the default cut buffer, as historic
		 * vi did.  Imagine, if you can, my disappointment.
		 */
		ftype = FILTER_BANG;
		if (cmdp->addr1.lno == 1 && cmdp->addr2.lno == 1) {
			if (db_last(sp, &lno))
				return (1);
			if (lno == 0) {
				cmdp->addr1.lno = cmdp->addr2.lno = 0;
				ftype = FILTER_RBANG;
			}
		}
		rval = ex_filter(sp, cmdp,
		    &cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype);

		/*
		 * If in vi mode, move to the first nonblank.
		 *
		 * !!!
		 * Historic vi wasn't consistent in this area -- if you used
		 * a forward motion it moved to the first nonblank, but if you
		 * did a backward motion it didn't.  And, if you followed a
		 * backward motion with a forward motion, it wouldn't move to
		 * the nonblank for either.  Going to the nonblank generally
		 * seems more useful and consistent, so we do it.
		 */
		sp->lno = rm.lno;
		if (F_ISSET(sp, SC_VI)) {
			sp->cno = 0;
			(void)nonblank(sp, sp->lno, &sp->cno);
		} else
			sp->cno = rm.cno;
	}

	/* Ex terminates with a bang, even if the command fails. */
	if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT))
		(void)ex_puts(sp, "!\n");

	/*
	 * XXX
	 * The ! commands never return an error, so that autoprint always
	 * happens in the ex parser.
	 */
	return (0);
}