Esempio n. 1
0
/*
 * v_subst -- [buffer][count]s
 *	Substitute characters.
 *
 * PUBLIC: int v_subst __P((SCR *, VICMD *));
 */
int
v_subst(SCR *sp, VICMD *vp)
{
	size_t len;
	u_int32_t flags;
	int isempty;
	CHAR_T *p;

	flags = set_txt_std(sp, vp, 0);
	sp->showmode = SM_CHANGE;

	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (!isempty)
			return (1);
		len = 0;
		LF_SET(TXT_APPENDEOL);
	} else {
		if (len == 0)
			LF_SET(TXT_APPENDEOL);
		LF_SET(TXT_EMARK | TXT_OVERWRITE);
	}

	vp->m_stop.lno = vp->m_start.lno;
	vp->m_stop.cno =
	    vp->m_start.cno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0);
	if (vp->m_stop.cno > len - 1)
		vp->m_stop.cno = len - 1;

	if (p != NULL && cut(sp,
	    F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
	    &vp->m_start, &vp->m_stop, 0))
		return (1);

	return (v_txt(sp, vp, &vp->m_stop, p, len, 0, OOBLNO, 1, flags));
}
Esempio n. 2
0
/*
 * nonblank --
 *	Set the column number of the first non-blank character
 *	including or after the starting column.  On error, set
 *	the column to 0, it's safest.
 *
 * PUBLIC: int nonblank(SCR *, recno_t, size_t *);
 */
int
nonblank(SCR *sp, recno_t lno, size_t *cnop)
{
	char *p;
	size_t cnt, len, off;
	int isempty;

	/* Default. */
	off = *cnop;
	*cnop = 0;

	/* Get the line, succeeding in an empty file. */
	if (db_eget(sp, lno, &p, &len, &isempty))
		return (!isempty);

	/* Set the offset. */
	if (len == 0 || off >= len)
		return (0);

	for (cnt = off, p = &p[off],
	    len -= off; len && isblank(*p); ++cnt, ++p, --len);

	/* Set the return. */
	*cnop = len ? cnt : cnt - 1;
	return (0);
}
Esempio n. 3
0
/*
 * v_Replace -- [count]R
 *	Overwrite multiple characters.
 *
 * PUBLIC: int v_Replace __P((SCR *, VICMD *));
 */
int
v_Replace(SCR *sp, VICMD *vp)
{
	size_t len;
	u_int32_t flags;
	int isempty;
	CHAR_T *p;

	flags = set_txt_std(sp, vp, 0);
	sp->showmode = SM_REPLACE;

	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (!isempty)
			return (1);
		len = 0;
		LF_SET(TXT_APPENDEOL);
	} else {
		if (len == 0)
			LF_SET(TXT_APPENDEOL);
		LF_SET(TXT_OVERWRITE | TXT_REPLACE);
	}
	vp->m_stop.lno = vp->m_start.lno;
	vp->m_stop.cno = len ? len - 1 : 0;

	return (v_txt(sp, vp, &vp->m_stop, p, len,
	    0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
}
Esempio n. 4
0
/*
 * v_ia -- [count]a
 *	   [count]A
 *	Append text to the cursor position.
 *
 * PUBLIC: int v_ia __P((SCR *, VICMD *));
 */
int
v_ia(SCR *sp, VICMD *vp)
{
	size_t len;
	u_int32_t flags;
	int isempty;
	CHAR_T *p;

	flags = set_txt_std(sp, vp, 0);
	sp->showmode = SM_APPEND;
	sp->lno = vp->m_start.lno;

	/* Move the cursor one column to the right and repaint the screen. */
	if (db_eget(sp, sp->lno, &p, &len, &isempty)) {
		if (!isempty)
			return (1);
		len = 0;
		LF_SET(TXT_APPENDEOL);
	} else if (len) {
		if (len == sp->cno + 1) {
			sp->cno = len;
			LF_SET(TXT_APPENDEOL);
		} else
			++sp->cno;
	} else
		LF_SET(TXT_APPENDEOL);

	return (v_txt(sp, vp, NULL, p, len,
	    0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
}
Esempio n. 5
0
/*
 * v_dollar -- [count]$
 *	Move to the last column.
 *
 * PUBLIC: int v_dollar __P((SCR *, VICMD *));
 */
int
v_dollar(SCR *sp, VICMD *vp)
{
	size_t len;
	int isempty;

	/*
	 * !!!
	 * A count moves down count - 1 rows, so, "3$" is the same as "2j$".
	 */
	if ((F_ISSET(vp, VC_C1SET) ? vp->count : 1) != 1) {
		/*
		 * !!!
		 * Historically, if the $ is a motion, and deleting from
		 * at or before the first non-blank of the line, it's a
		 * line motion, and the line motion flag is set.
		 */
		vp->m_stop.cno = 0;
		if (nonblank(sp, vp->m_start.lno, &vp->m_stop.cno))
			return (1);
		if (ISMOTION(vp) && vp->m_start.cno <= vp->m_stop.cno)
			F_SET(vp, VM_LMODE);

		--vp->count;
		if (v_down(sp, vp))
			return (1);
	}

	/*
	 * !!!
	 * Historically, it was illegal to use $ as a motion command on
	 * an empty line.  Unfortunately, even though C was historically
	 * aliased to c$, it (and not c$) was special cased to work on
	 * empty lines.  Since we alias C to c$ too, we have a problem.
	 * To fix it, we let c$ go through, on the assumption that it's
	 * not a problem for it to work.
	 */
	if (db_eget(sp, vp->m_stop.lno, NULL, &len, &isempty)) {
		if (!isempty)
			return (1);
		len = 0;
	}

	if (len == 0) {
		if (ISMOTION(vp) && !ISCMD(vp->rkp, 'c')) {
			v_eol(sp, NULL);
			return (1);
		}
		return (0);
	}

	/*
	 * Non-motion commands move to the end of the range.  Delete
	 * and yank stay at the start.  Ignore others.
	 */
	vp->m_stop.cno = len ? len - 1 : 0;
	vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
	return (0);
}
Esempio n. 6
0
/*
 * v_chF -- [count]Fc
 *	Search backward in the line for the next occurrence of the
 *	specified character.
 *
 * PUBLIC: int v_chF __P((SCR *, VICMD *));
 */
int
v_chF(SCR *sp, VICMD *vp)
{
	size_t len;
	u_long cnt;
	int isempty;
	ARG_CHAR_T key;
	CHAR_T *endp, *p;

	/*
	 * !!!
	 * If it's a dot command, it doesn't reset the key for which
	 * we're searching, e.g. in "df1|f2|.|;", the ';' searches
	 * for a '2'.
	 */
	key = vp->character;
	if (!F_ISSET(vp, VC_ISDOT))
		VIP(sp)->lastckey = key;
	VIP(sp)->csearchdir = FSEARCH;

	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (isempty)
			goto empty;
		return (1);
	}

	if (len == 0) {
empty:		notfound(sp, key);
		return (1);
	}

	endp = p - 1;
	p += vp->m_start.cno;
	for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
		while (--p > endp && *p != key);
		if (p == endp) {
			notfound(sp, key);
			return (1);
		}
	}

	vp->m_stop.cno = (p - endp) - 1;

	/*
	 * All commands move to the end of the range.  Motion commands
	 * adjust the starting point to the character before the current
	 * one.
	 */
	vp->m_final = vp->m_stop;
	if (ISMOTION(vp))
		--vp->m_start.cno;
	return (0);
}
Esempio n. 7
0
/*
 * api_gline --
 *	Get a line.
 *
 * PUBLIC: int api_gline __P((SCR *, db_recno_t, CHAR_T **, size_t *));
 */
int
api_gline(SCR *sp, db_recno_t lno, CHAR_T **linepp, size_t *lenp)
{
	int isempty;

	if (db_eget(sp, lno, linepp, lenp, &isempty)) {
		if (isempty)
			msgq(sp, M_ERR, "209|The file is empty");
		return (1);
	}
	return (0);
}
Esempio n. 8
0
/*
 * v_chf -- [count]fc
 *	Search forward in the line for the next occurrence of the
 *	specified character.
 *
 * PUBLIC: int v_chf __P((SCR *, VICMD *));
 */
int
v_chf(SCR *sp, VICMD *vp)
{
	size_t len;
	u_long cnt;
	int isempty;
	ARG_CHAR_T key;
	CHAR_T *endp, *p, *startp;

	/*
	 * !!!
	 * If it's a dot command, it doesn't reset the key for which we're
	 * searching, e.g. in "df1|f2|.|;", the ';' searches for a '2'.
	 */
	key = vp->character;
	if (!F_ISSET(vp, VC_ISDOT))
		VIP(sp)->lastckey = key;
	VIP(sp)->csearchdir = fSEARCH;

	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (isempty)
			goto empty;
		return (1);
	}

	if (len == 0) {
empty:		notfound(sp, key);
		return (1);
	}

	endp = (startp = p) + len;
	p += vp->m_start.cno;
	for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
		while (++p < endp && *p != key);
		if (p == endp) {
			notfound(sp, key);
			return (1);
		}
	}

	vp->m_stop.cno = p - startp;

	/*
	 * Non-motion commands move to the end of the range.
	 * Delete and yank stay at the start, ignore others.
	 */
	vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
	return (0);
}
Esempio n. 9
0
/*
 * cs_init --
 *	Initialize character stream routines.
 *
 * PUBLIC: int cs_init __P((SCR *, VCS *));
 */
int
cs_init(SCR *sp, VCS *csp)
{
	int isempty;

	if (db_eget(sp, csp->cs_lno, &csp->cs_bp, &csp->cs_len, &isempty)) {
		if (isempty)
			msgq(sp, M_BERR, "177|Empty file");
		return (1);
	}
	if (csp->cs_len == 0 || v_isempty(csp->cs_bp, csp->cs_len)) {
		csp->cs_cno = 0;
		csp->cs_flags = CS_EMP;
	} else {
		csp->cs_flags = 0;
		csp->cs_ch = csp->cs_bp[csp->cs_cno];
	}
	return (0);
}
Esempio n. 10
0
/*
 * v_right -- [count]' ', [count]l
 *	Move right by columns.
 *
 * PUBLIC: int v_right __P((SCR *, VICMD *));
 */
int
v_right(SCR *sp, VICMD *vp)
{
	size_t len;
	int isempty;

	if (db_eget(sp, vp->m_start.lno, NULL, &len, &isempty)) {
		if (isempty)
			goto eol;
		return (1);
	}

	/* It's always illegal to move right on empty lines. */
	if (len == 0) {
eol:		v_eol(sp, NULL);
		return (1);
	}

	/*
	 * Non-motion commands move to the end of the range.  Delete and
	 * yank stay at the start.  Ignore others.  Adjust the end of the
	 * range for motion commands.
	 *
	 * !!!
	 * Historically, "[cdsy]l" worked at the end of a line.  Also,
	 * EOL is a count sink.
	 */
	vp->m_stop.cno = vp->m_start.cno +
	    (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
	if (vp->m_start.cno == len - 1 && !ISMOTION(vp)) {
		v_eol(sp, NULL);
		return (1);
	}
	if (vp->m_stop.cno >= len) {
		vp->m_stop.cno = len - 1;
		vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
	} else if (ISMOTION(vp)) {
		--vp->m_stop.cno;
		vp->m_final = vp->m_start;
	} else
		vp->m_final = vp->m_stop;
	return (0);
}
Esempio n. 11
0
/*
 * v_ii -- [count]i
 *	   [count]I
 *	Insert text at the cursor position.
 *
 * PUBLIC: int v_ii __P((SCR *, VICMD *));
 */
int
v_ii(SCR *sp, VICMD *vp)
{
	size_t len;
	u_int32_t flags;
	int isempty;
	CHAR_T *p;

	flags = set_txt_std(sp, vp, 0);
	sp->showmode = SM_INSERT;
	sp->lno = vp->m_start.lno;

	if (db_eget(sp, sp->lno, &p, &len, &isempty)) {
		if (!isempty)
			return (1);
		len = 0;
	}

	if (len == 0)
		LF_SET(TXT_APPENDEOL);
	return (v_txt(sp, vp, NULL, p, len,
	    0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
}
Esempio n. 12
0
/*
 * v_increment -- [count]#[#+-]
 *	Increment/decrement a keyword number.
 *
 * PUBLIC: int v_increment __P((SCR *, VICMD *));
 */
int
v_increment(SCR *sp, VICMD *vp)
{
	enum nresult nret;
	u_long ulval;
	long change, ltmp, lval;
	size_t beg, blen, end, len, nlen, wlen;
	int base, isempty, rval;
	CHAR_T *ntype, nbuf[100];
	CHAR_T *bp, *p, *t;

	/* Validate the operator. */
	if (vp->character == '#')
		vp->character = '+';
	if (vp->character != '+' && vp->character != '-') {
		v_emsg(sp, vp->kp->usage, VIM_USAGE);
		return (1);
	}

	/* If new value set, save it off, but it has to fit in a long. */
	if (F_ISSET(vp, VC_C1SET)) {
		if (vp->count > LONG_MAX) {
			inc_err(sp, NUM_OVER);
			return (1);
		}
		change = vp->count;
	} else
		change = 1;

	/* Get the line. */
	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (isempty)
			goto nonum;
		return (1);
	}

	/*
	 * Skip any leading space before the number.  Getting a cursor word
	 * implies moving the cursor to its beginning, if we moved, refresh
	 * now.
	 */
	for (beg = vp->m_start.cno; beg < len && ISSPACE(p[beg]); ++beg);
	if (beg >= len)
		goto nonum;
	if (beg != vp->m_start.cno) {
		sp->cno = beg;
		(void)vs_refresh(sp, 0);
	}

#undef	ishex
#define	ishex(c)	(ISXDIGIT(c))
#undef	isoctal
#define	isoctal(c)	((c) >= '0' && (c) <= '7')

	/*
	 * Look for 0[Xx], or leading + or - signs, guess at the base.
	 * The character after that must be a number.  Wlen is set to
	 * the remaining characters in the line that could be part of
	 * the number.
	 */
	wlen = len - beg;
	if (p[beg] == '0' && wlen > 2 &&
	    (p[beg + 1] == 'X' || p[beg + 1] == 'x')) {
		base = 16;
		end = beg + 2;
		if (!ishex(p[end]))
			goto decimal;
		ntype = p[beg + 1] == 'X' ? fmt[HEXC] : fmt[HEXL];
	} else if (p[beg] == '0' && wlen > 1) {
		base = 8;
		end = beg + 1;
		if (!isoctal(p[end]))
			goto decimal;
		ntype = fmt[OCTAL];
	} else if (wlen >= 1 && (p[beg] == '+' || p[beg] == '-')) {
		base = 10;
		end = beg + 1;
		ntype = fmt[SDEC];
		if (!isdigit(p[end]))
			goto nonum;
	} else {
decimal:	base = 10;
		end = beg;
		ntype = fmt[DEC];
		if (!isdigit(p[end])) {
nonum:			msgq(sp, M_ERR, "181|Cursor not in a number");
			return (1);
		}
	}

	/* Find the end of the word, possibly correcting the base. */
	while (++end < len) {
		switch (base) {
		case 8:
			if (isoctal(p[end]))
				continue;
			if (p[end] == '8' || p[end] == '9') {
				base = 10;
				ntype = fmt[DEC];
				continue;
			}
			break;
		case 10:
			if (isdigit(p[end]))
				continue;
			break;
		case 16:
			if (ishex(p[end]))
				continue;
			break;
		default:
			abort();
			/* NOTREACHED */
		}
		break;
	}
	wlen = (end - beg);

	/*
	 * XXX
	 * If the line was at the end of the buffer, we have to copy it
	 * so we can guarantee that it's NULL-terminated.  We make the
	 * buffer big enough to fit the line changes as well, and only
	 * allocate once.
	 */
	GET_SPACE_RETW(sp, bp, blen, len + 50);
	if (end == len) {
		MEMMOVE(bp, &p[beg], wlen);
		bp[wlen] = '\0';
		t = bp;
	} else
		t = &p[beg];

	/*
	 * Octal or hex deal in unsigned longs, everything else is done
	 * in signed longs.
	 */
	if (base == 10) {
		if ((nret = nget_slong(&lval, t, NULL, 10)) != NUM_OK)
			goto err;
		ltmp = vp->character == '-' ? -change : change;
		if (lval > 0 && ltmp > 0 && !NPFITS(LONG_MAX, lval, ltmp)) {
			nret = NUM_OVER;
			goto err;
		}
		if (lval < 0 && ltmp < 0 && !NNFITS(LONG_MIN, lval, ltmp)) {
			nret = NUM_UNDER;
			goto err;
		}
		lval += ltmp;
		/* If we cross 0, signed numbers lose their sign. */
		if (lval == 0 && ntype == fmt[SDEC])
			ntype = fmt[DEC];
		nlen = SPRINTF(nbuf, sizeof(nbuf), ntype, lval);
	} else {
		if ((nret = nget_uslong(&ulval, t, NULL, base)) != NUM_OK)
			goto err;
		if (vp->character == '+') {
			if (!NPFITS(ULONG_MAX, ulval, change)) {
				nret = NUM_OVER;
				goto err;
			}
			ulval += change;
		} else {
			if (ulval < change) {
				nret = NUM_UNDER;
				goto err;
			}
			ulval -= change;
		}

		/* Correct for literal "0[Xx]" in format. */
		if (base == 16)
			wlen -= 2;

		nlen = SPRINTF(nbuf, sizeof(nbuf), ntype, wlen, ulval);
	}

	/* Build the new line. */
	MEMMOVE(bp, p, beg);
	MEMMOVE(bp + beg, nbuf, nlen);
	MEMMOVE(bp + beg + nlen, p + end, len - beg - (end - beg));
	len = beg + nlen + (len - beg - (end - beg));

	nret = NUM_OK;
	rval = db_set(sp, vp->m_start.lno, bp, len);

	if (0) {
err:		rval = 1;
		inc_err(sp, nret);
	}
	if (bp != NULL)
		FREE_SPACEW(sp, bp, blen);
	return (rval);
}
Esempio n. 13
0
/*
 * sscr_exec --
 *	Take a line and hand it off to the shell.
 *
 * PUBLIC: int sscr_exec __P((SCR *, db_recno_t));
 */
int
sscr_exec(SCR *sp, db_recno_t lno)
{
	SCRIPT *sc;
	db_recno_t last_lno;
	size_t blen, len, last_len;
	int isempty, matchprompt, rval;
	ssize_t nw;
	char *bp = NULL;
	const char *p;
	const CHAR_T *ip;
	size_t ilen;

	sc = sp->script;

	/* If there's a prompt on the last line, append the command. */
	if (db_last(sp, &last_lno))
		return (1);
	if (db_get(sp, last_lno, DBG_FATAL, __UNCONST(&ip), &ilen))
		return (1);
	INT2CHAR(sp, ip, ilen, p, last_len);
	if (last_len == sc->sh_prompt_len &&
	    memcmp(p, sc->sh_prompt, last_len) == 0) {
		matchprompt = 1;
		GET_SPACE_RETC(sp, bp, blen, last_len + 128);
		memmove(bp, p, last_len);
	} else
		matchprompt = 0;

	/* Get something to execute. */
	if (db_eget(sp, lno, __UNCONST(&ip), &ilen, &isempty)) {
		if (isempty)
			goto empty;
		goto err1;
	}

	/* Empty lines aren't interesting. */
	if (ilen == 0)
		goto empty;
	INT2CHAR(sp, ip, ilen, p, len);

	/* Delete any prompt. */
	if (len >= sc->sh_prompt_len &&
	    memcmp(p, sc->sh_prompt, sc->sh_prompt_len) == 0) {
		len -= sc->sh_prompt_len;
		if (len == 0) {
empty:			msgq(sp, M_BERR, "151|No command to execute");
			goto err1;
		}
		p += sc->sh_prompt_len;
	}

	/* Push the line to the shell. */
	if ((size_t)(nw = write(sc->sh_master, p, len)) != len)
		goto err2;
	rval = 0;
	if (write(sc->sh_master, "\n", 1) != 1) {
err2:		if (nw == 0)
			errno = EIO;
		msgq(sp, M_SYSERR, "shell");
		goto err1;
	}

	if (matchprompt) {
		ADD_SPACE_GOTO(sp, char, bp, blen, last_len + len);
		memmove(bp + last_len, p, len);
		CHAR2INT(sp, bp, last_len + len, ip, ilen);
		if (db_set(sp, last_lno, ip, ilen))
err1:			rval = 1;
	}
	if (matchprompt)
alloc_err:	FREE_SPACE(sp, bp, blen);
	return (rval);
}
Esempio n. 14
0
/*
 * sscr_exec --
 *	Take a line and hand it off to the shell.
 *
 * PUBLIC: int sscr_exec(SCR *, recno_t);
 */
int
sscr_exec(SCR *sp, recno_t lno)
{
	SCRIPT *sc;
	recno_t last_lno;
	size_t blen, len, last_len, tlen;
	int isempty, matchprompt, nw, rval;
	char *bp, *p;

	/* If there's a prompt on the last line, append the command. */
	if (db_last(sp, &last_lno))
		return (1);
	if (db_get(sp, last_lno, DBG_FATAL, &p, &last_len))
		return (1);
	if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) {
		matchprompt = 1;
		GET_SPACE_RET(sp, bp, blen, last_len + 128);
		memmove(bp, p, last_len);
	} else
		matchprompt = 0;

	/* Get something to execute. */
	if (db_eget(sp, lno, &p, &len, &isempty)) {
		if (isempty)
			goto empty;
		goto err1;
	}

	/* Empty lines aren't interesting. */
	if (len == 0)
		goto empty;

	/* Delete any prompt. */
	if (sscr_matchprompt(sp, p, len, &tlen)) {
		if (tlen == len) {
empty:			msgq(sp, M_BERR, "No command to execute");
			goto err1;
		}
		p += (len - tlen);
		len = tlen;
	}

	/* Push the line to the shell. */
	sc = sp->script;
	if ((nw = write(sc->sh_master, p, len)) != len)
		goto err2;
	rval = 0;
	if (write(sc->sh_master, "\n", 1) != 1) {
err2:		if (nw == 0)
			errno = EIO;
		msgq(sp, M_SYSERR, "shell");
		goto err1;
	}

	if (matchprompt) {
		ADD_SPACE_RET(sp, bp, blen, last_len + len);
		memmove(bp + last_len, p, len);
		if (db_set(sp, last_lno, bp, last_len + len))
err1:			rval = 1;
	}
	if (matchprompt)
		FREE_SPACE(sp, bp, blen);
	return (rval);
}
Esempio n. 15
0
/*
 * v_change -- [buffer][count]c[count]motion
 *	       [buffer][count]C
 *	       [buffer][count]S
 *	Change command.
 *
 * PUBLIC: int v_change __P((SCR *, VICMD *));
 */
int
v_change(SCR *sp, VICMD *vp)
{
	size_t blen, len;
	u_int32_t flags;
	int isempty, lmode, rval;
	CHAR_T *bp;
	CHAR_T *p;

	/*
	 * 'c' can be combined with motion commands that set the resulting
	 * cursor position, i.e. "cG".  Clear the VM_RCM flags and make the
	 * resulting cursor position stick, inserting text has its own rules
	 * for cursor positioning.
	 */
	F_CLR(vp, VM_RCM_MASK);
	F_SET(vp, VM_RCM_SET);

	/*
	 * Find out if the file is empty, it's easier to handle it as a
	 * special case.
	 */
	if (vp->m_start.lno == vp->m_stop.lno &&
	    db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (!isempty)
			return (1);
		return (v_ia(sp, vp));
	}

	flags = set_txt_std(sp, vp, 0);
	sp->showmode = SM_CHANGE;

	/*
	 * Move the cursor to the start of the change.  Note, if autoindent
	 * is turned on, the cc command in line mode changes from the first
	 * *non-blank* character of the line, not the first character.  And,
	 * to make it just a bit more exciting, the initial space is handled
	 * as auto-indent characters.
	 */
	lmode = F_ISSET(vp, VM_LMODE) ? CUT_LINEMODE : 0;
	if (lmode) {
		vp->m_start.cno = 0;
		if (O_ISSET(sp, O_AUTOINDENT)) {
			if (nonblank(sp, vp->m_start.lno, &vp->m_start.cno))
				return (1);
			LF_SET(TXT_AICHARS);
		}
	}
	sp->lno = vp->m_start.lno;
	sp->cno = vp->m_start.cno;

	LOG_CORRECT;

	/*
	 * If not in line mode and changing within a single line, copy the
	 * text and overwrite it.
	 */
	if (!lmode && vp->m_start.lno == vp->m_stop.lno) {
		/*
		 * !!!
		 * Historic practice, c did not cut into the numeric buffers,
		 * only the unnamed one.
		 */
		if (cut(sp,
		    F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
		    &vp->m_start, &vp->m_stop, lmode))
			return (1);
		if (len == 0)
			LF_SET(TXT_APPENDEOL);
		LF_SET(TXT_EMARK | TXT_OVERWRITE);
		return (v_txt(sp, vp, &vp->m_stop, p, len,
		    0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
	}

	/*
	 * It's trickier if in line mode or changing over multiple lines.  If
	 * we're in line mode delete all of the lines and insert a replacement
	 * line which the user edits.  If there was leading whitespace in the
	 * first line being changed, we copy it and use it as the replacement.
	 * If we're not in line mode, we delete the text and start inserting.
	 *
	 * !!!
	 * Copy the text.  Historic practice, c did not cut into the numeric
	 * buffers, only the unnamed one.
	 */
	if (cut(sp,
	    F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
	    &vp->m_start, &vp->m_stop, lmode))
		return (1);

	/* If replacing entire lines and there's leading text. */
	if (lmode && vp->m_start.cno) {
		/*
		 * Get a copy of the first line changed, and copy out the
		 * leading text.
		 */
		if (db_get(sp, vp->m_start.lno, DBG_FATAL, &p, &len))
			return (1);
		GET_SPACE_RETW(sp, bp, blen, vp->m_start.cno);
		MEMMOVEW(bp, p, vp->m_start.cno);
	} else
		bp = NULL;

	/* Delete the text. */
	if (del(sp, &vp->m_start, &vp->m_stop, lmode))
		return (1);

	/* If replacing entire lines, insert a replacement line. */
	if (lmode) {
		if (db_insert(sp, vp->m_start.lno, bp, vp->m_start.cno))
			return (1);
		sp->lno = vp->m_start.lno;
		len = sp->cno = vp->m_start.cno;
	}

	/* Get the line we're editing. */
	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (!isempty)
			return (1);
		len = 0;
	}

	/* Check to see if we're appending to the line. */
	if (vp->m_start.cno >= len)
		LF_SET(TXT_APPENDEOL);

	rval = v_txt(sp, vp, NULL, p, len,
	    0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags);

	if (bp != NULL)
		FREE_SPACEW(sp, bp, blen);
	return (rval);
}
/*
 * v_match -- %
 *	Search to matching character.
 *
 * PUBLIC: int v_match __P((SCR *, VICMD *));
 */
int
v_match(SCR *sp, VICMD *vp)
{
	VCS cs;
	MARK *mp;
	size_t cno, len, off;
	int cnt, isempty, matchc, startc, (*gc)__P((SCR *, VCS *));
	CHAR_T *p;
	char *cp;
	const char *match_chars;

	static MARK match = { 0, 0 };
	static int match_dir;

	/*
	 * Historically vi would match (), {} and [] however
	 * an update included <>.  This is ok for editing HTML
	 * but a pain in the butt for C source.
	 * Making it an option lets the user decide what is 'right'.
	 * Also fixed to do something sensible with "".
	 */
	match_chars = O_STR(sp, O_MATCHCHARS);

	/*
	 * !!!
	 * Historic practice; ignore the count.
	 *
	 * !!!
	 * Historical practice was to search for the initial character in the
	 * forward direction only.
	 */
	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (isempty)
			goto nomatch;
		return (1);
	}
	for (off = vp->m_start.cno;; ++off) {
		if (off >= len) {
nomatch:		msgq(sp, M_BERR, "184|No match character on this line");
			return (1);
		}
		startc = p[off];
		cp = strchr(match_chars, startc);
		if (cp != NULL)
			break;
	}
	cnt = cp - match_chars;
	matchc = match_chars[cnt ^ 1];

	/* Alternate back-forward search if startc and matchc the same */
	if (startc == matchc) {
		/* are we continuing from where last match finished? */
		if (match.lno == vp->m_start.lno && match.cno ==vp->m_start.cno)
			/* yes - continue in sequence */
			match_dir++;
		else
			/* no - go forward, back, back, forward */
			match_dir = 1;
		if (match_dir & 2)
			cnt++;
	}
	gc = cnt & 1 ? cs_prev : cs_next;

	cs.cs_lno = vp->m_start.lno;
	cs.cs_cno = off;
	if (cs_init(sp, &cs))
		return (1);
	for (cnt = 1;;) {
		if (gc(sp, &cs))
			return (1);
		if (cs.cs_flags != 0) {
			if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF)
				break;
			continue;
		}
		if (cs.cs_ch == matchc && --cnt == 0)
			break;
		if (cs.cs_ch == startc)
			++cnt;
	}
	if (cnt) {
		msgq(sp, M_BERR, "185|Matching character not found");
		return (1);
	}

	vp->m_stop.lno = cs.cs_lno;
	vp->m_stop.cno = cs.cs_cno;

	/*
	 * If moving right, non-motion commands move to the end of the range.
	 * Delete and yank stay at the start.
	 *
	 * If moving left, all commands move to the end of the range.
	 *
	 * !!!
	 * Don't correct for leftward movement -- historic vi deleted the
	 * starting cursor position when deleting to a match.
	 */
	if (vp->m_start.lno < vp->m_stop.lno ||
	    (vp->m_start.lno == vp->m_stop.lno &&
	    vp->m_start.cno < vp->m_stop.cno))
		vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
	else
		vp->m_final = vp->m_stop;

	match.lno = vp->m_final.lno;
	match.cno = vp->m_final.cno;

	/*
	 * !!!
	 * If the motion is across lines, and the earliest cursor position
	 * is at or before any non-blank characters in the line, i.e. the
	 * movement is cutting all of the line's text, and the later cursor
	 * position has nothing other than whitespace characters between it
	 * and the end of its line, the buffer is in line mode.
	 */
	if (!ISMOTION(vp) || vp->m_start.lno == vp->m_stop.lno)
		return (0);
	mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_start : &vp->m_stop;
	if (mp->cno != 0) {
		cno = 0;
		if (nonblank(sp, mp->lno, &cno))
			return (1);
		if (cno < mp->cno)
			return (0);
	}
	mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_stop : &vp->m_start;
	if (db_get(sp, mp->lno, DBG_FATAL, &p, &len))
		return (1);
	for (p += mp->cno + 1, len -= mp->cno; --len; ++p)
		if (!ISBLANK((UCHAR_T)*p))
			return (0);
	F_SET(vp, VM_LMODE);
	return (0);
}
Esempio n. 17
0
/*
 * v_match -- %
 *	Search to matching character.
 *
 * PUBLIC: int v_match(SCR *, VICMD *);
 */
int
v_match(SCR *sp, VICMD *vp)
{
	VCS cs;
	MARK *mp;
	size_t cno, len, off;
	int cnt, isempty, matchc, startc, (*gc)(SCR *, VCS *);
	CHAR_T *p;
	CHAR_T *cp;
	const CHAR_T *match_chars;

	/*
	 * Historically vi would match (), {} and [] however
	 * an update included <>.  This is ok for editing HTML
	 * but a pain in the butt for C source.
	 * Making it an option lets the user decide what is 'right'.
	 */
	match_chars = VIP(sp)->mcs;

	/*
	 * !!!
	 * Historic practice; ignore the count.
	 *
	 * !!!
	 * Historical practice was to search for the initial character in the
	 * forward direction only.
	 */
	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (isempty)
			goto nomatch;
		return (1);
	}
	for (off = vp->m_start.cno;; ++off) {
		if (off >= len) {
nomatch:		msgq(sp, M_BERR, "184|No match character on this line");
			return (1);
		}
		startc = p[off];
		cp = STRCHR(match_chars, startc);
		if (cp != NULL) {
			cnt = cp - match_chars;
			matchc = match_chars[cnt ^ 1];
			gc = cnt & 1 ? cs_prev : cs_next;
			break;
		}
	}

	cs.cs_lno = vp->m_start.lno;
	cs.cs_cno = off;
	if (cs_init(sp, &cs))
		return (1);
	for (cnt = 1;;) {
		if (gc(sp, &cs))
			return (1);
		if (cs.cs_flags != 0) {
			if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF)
				break;
			continue;
		}
		if (cs.cs_ch == startc)
			++cnt;
		else if (cs.cs_ch == matchc && --cnt == 0)
			break;
	}
	if (cnt) {
		msgq(sp, M_BERR, "185|Matching character not found");
		return (1);
	}

	vp->m_stop.lno = cs.cs_lno;
	vp->m_stop.cno = cs.cs_cno;

	/*
	 * If moving right, non-motion commands move to the end of the range.
	 * Delete and yank stay at the start.
	 *
	 * If moving left, all commands move to the end of the range.
	 *
	 * !!!
	 * Don't correct for leftward movement -- historic vi deleted the
	 * starting cursor position when deleting to a match.
	 */
	if (vp->m_start.lno < vp->m_stop.lno ||
	    (vp->m_start.lno == vp->m_stop.lno &&
	    vp->m_start.cno < vp->m_stop.cno))
		vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
	else
		vp->m_final = vp->m_stop;

	/*
	 * !!!
	 * If the motion is across lines, and the earliest cursor position
	 * is at or before any non-blank characters in the line, i.e. the
	 * movement is cutting all of the line's text, and the later cursor
	 * position has nothing other than whitespace characters between it
	 * and the end of its line, the buffer is in line mode.
	 */
	if (!ISMOTION(vp) || vp->m_start.lno == vp->m_stop.lno)
		return (0);
	mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_start : &vp->m_stop;
	if (mp->cno != 0) {
		cno = 0;
		if (nonblank(sp, mp->lno, &cno))
			return (1);
		if (cno < mp->cno)
			return (0);
	}
	mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_stop : &vp->m_start;
	if (db_get(sp, mp->lno, DBG_FATAL, &p, &len))
		return (1);
	for (p += mp->cno + 1, len -= mp->cno; --len; ++p)
		if (!isblank(*p))
			return (0);
	F_SET(vp, VM_LMODE);
	return (0);
}