/*
* v_yank -- [buffer][count]y[count][motion]
* [buffer][count]Y
* Yank text (or lines of text) into a cut buffer.
*
* !!!
* Historic vi moved the cursor to the from MARK if it was before the current
* cursor and on a different line, e.g., "yk" moves the cursor but "yj" and
* "yl" do not. Unfortunately, it's too late to change this now. Matching
* the historic semantics isn't easy. The line number was always changed and
* column movement was usually relative. However, "y'a" moved the cursor to
* the first non-blank of the line marked by a, while "y`a" moved the cursor
* to the line and column marked by a. Hopefully, the motion component code
* got it right... Unlike delete, we make no adjustments here.
*
* PUBLIC: int v_yank(SCR *, VICMD *);
*/
int
v_yank(SCR *sp, VICMD *vp)
{
size_t len;
if (cut(sp,
F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, &vp->m_start,
&vp->m_stop, F_ISSET(vp, VM_LMODE) ? CUT_LINEMODE : 0))
return (1);
sp->rptlines[L_YANKED] += (vp->m_stop.lno - vp->m_start.lno) + 1;
/*
* One special correction, in case we've deleted the current line or
* character. We check it here instead of checking in every command
* that can be a motion component.
*/
if (db_get(sp, vp->m_final.lno, DBG_FATAL, NULL, &len))
return (1);
/*
* !!!
* Cursor movements, other than those caused by a line mode command
* moving to another line, historically reset the relative position.
*
* This currently matches the check made in v_delete(), I'm hoping
* that they should be consistent...
*/
if (!F_ISSET(vp, VM_LMODE)) {
F_CLR(vp, VM_RCM_MASK);
F_SET(vp, VM_RCM_SET);
/* Make sure the set cursor position exists. */
if (vp->m_final.cno >= len)
vp->m_final.cno = len ? len - 1 : 0;
}
return (0);
}
/*
* v_replace -- [count]r<char>
*
* !!!
* The r command in historic vi was almost beautiful in its badness. For
* example, "r<erase>" and "r<word erase>" beeped the terminal and deleted
* a single character. "Nr<carriage return>", where N was greater than 1,
* inserted a single carriage return. "r<escape>" did cancel the command,
* but "r<literal><escape>" erased a single character. To enter a literal
* <literal> character, it required three <literal> characters after the
* command. This may not be right, but at least it's not insane.
*
* PUBLIC: int v_replace __P((SCR *, VICMD *));
*/
int
v_replace(SCR *sp, VICMD *vp)
{
EVENT ev;
VI_PRIVATE *vip;
TEXT *tp;
size_t blen, len;
u_long cnt;
int quote, rval;
CHAR_T *bp;
CHAR_T *p;
vip = VIP(sp);
/*
* If the line doesn't exist, or it's empty, replacement isn't
* allowed. It's not hard to implement, but:
*
* 1: It's historic practice (vi beeped before the replacement
* character was even entered).
* 2: For consistency, this change would require that the more
* general case, "Nr", when the user is < N characters from
* the end of the line, also work, which would be a bit odd.
* 3: Replacing with a <newline> has somewhat odd semantics.
*/
if (db_get(sp, vp->m_start.lno, DBG_FATAL, &p, &len))
return (1);
if (len == 0) {
msgq(sp, M_BERR, "186|No characters to replace");
return (1);
}
/*
* Figure out how many characters to be replace. For no particular
* reason (other than that the semantics of replacing the newline
* are confusing) only permit the replacement of the characters in
* the current line. I suppose we could append replacement characters
* to the line, but I see no compelling reason to do so. Check this
* before we get the character to match historic practice, where Nr
* failed immediately if there were less than N characters from the
* cursor to the end of the line.
*/
cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
vp->m_stop.lno = vp->m_start.lno;
vp->m_stop.cno = vp->m_start.cno + cnt - 1;
if (vp->m_stop.cno > len - 1) {
v_eol(sp, &vp->m_start);
return (1);
}
/*
* If it's not a repeat, reset the current mode and get a replacement
* character.
*/
quote = 0;
if (!F_ISSET(vp, VC_ISDOT)) {
sp->showmode = SM_REPLACE;
if (vs_refresh(sp, 0))
return (1);
next: if (v_event_get(sp, &ev, 0, 0))
return (1);
switch (ev.e_event) {
case E_CHARACTER:
/*
* <literal_next> means escape the next character.
* <escape> means they changed their minds.
*/
if (!quote) {
if (ev.e_value == K_VLNEXT) {
quote = 1;
goto next;
}
if (ev.e_value == K_ESCAPE)
return (0);
}
vip->rlast = ev.e_c;
vip->rvalue = ev.e_value;
break;
case E_ERR:
case E_EOF:
F_SET(sp, SC_EXIT_FORCE);
return (1);
case E_INTERRUPT:
/* <interrupt> means they changed their minds. */
return (0);
//.........这里部分代码省略.........
/*
* f_search --
* Do a forward search.
*
* PUBLIC: int f_search __P((SCR *,
* PUBLIC: MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
*/
int
f_search(
SCR *sp,
MARK *fm,
MARK *rm,
CHAR_T *ptrn,
size_t plen,
CHAR_T **eptrn,
u_int flags)
{
busy_t btype;
recno_t lno;
regmatch_t match[1];
size_t coff, len;
int cnt, eval, rval, wrapped;
CHAR_T *l;
if (search_init(sp, FORWARD, ptrn, plen, eptrn, flags))
return (1);
if (LF_ISSET(SEARCH_FILE)) {
lno = 1;
coff = 0;
} else {
if (db_get(sp, fm->lno, DBG_FATAL, &l, &len))
return (1);
lno = fm->lno;
/*
* If doing incremental search, start searching at the previous
* column, so that we search a minimal distance and still match
* special patterns, e.g., \< for beginning of a word.
*
* Otherwise, start searching immediately after the cursor. If
* at the end of the line, start searching on the next line.
* This is incompatible (read bug fix) with the historic vi --
* searches for the '$' pattern never moved forward, and the
* "-t foo" didn't work if the 'f' was the first character in
* the file.
*/
if (LF_ISSET(SEARCH_INCR)) {
if ((coff = fm->cno) != 0)
--coff;
} else if (fm->cno + 1 >= len) {
coff = 0;
lno = fm->lno + 1;
if (db_get(sp, lno, 0, &l, &len)) {
if (!O_ISSET(sp, O_WRAPSCAN)) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_EOF);
return (1);
}
lno = 1;
}
} else
coff = fm->cno + 1;
}
btype = BUSY_ON;
for (cnt = INTERRUPT_CHECK, rval = 1, wrapped = 0;; ++lno, coff = 0) {
if (cnt-- == 0) {
if (INTERRUPTED(sp))
break;
if (LF_ISSET(SEARCH_MSG)) {
search_busy(sp, btype);
btype = BUSY_UPDATE;
}
cnt = INTERRUPT_CHECK;
}
if ((wrapped && lno > fm->lno) || db_get(sp, lno, 0, &l, &len)) {
if (wrapped) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_NOTFOUND);
break;
}
if (!O_ISSET(sp, O_WRAPSCAN)) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_EOF);
break;
}
lno = 0;
wrapped = 1;
continue;
}
/* If already at EOL, just keep going. */
if (len != 0 && coff == len)
continue;
/* Set the termination. */
match[0].rm_so = coff;
match[0].rm_eo = len;
//.........这里部分代码省略.........
/*
* v_sectionb -- [count][[
* Move backward count sections/functions.
*
* PUBLIC: int v_sectionb(SCR *, VICMD *);
*/
int
v_sectionb(SCR *sp, VICMD *vp)
{
size_t len;
recno_t cnt, lno;
CHAR_T *p;
char *list, *lp;
/* An empty file or starting from line 1 is always illegal. */
if (vp->m_start.lno <= 1) {
v_sof(sp, NULL);
return (1);
}
/* Get the macro list. */
if ((list = O_STR(sp, O_SECTIONS)) == NULL)
return (1);
cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
for (lno = vp->m_start.lno; !db_get(sp, --lno, 0, &p, &len);) {
if (len == 0)
continue;
if (p[0] == '{') {
if (!--cnt)
goto adjust1;
continue;
}
/*
* !!!
* Historic documentation (USD:15-11, 4.2) said that formfeed
* characters (^L) in the first column delimited sections.
* The historic code mentions formfeed characters, but never
* implements them. Seems reasonable, do it.
*/
if (p[0] == '\014') {
if (!--cnt)
goto adjust1;
continue;
}
if (p[0] != '.' || len < 2)
continue;
for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp))
if (lp[0] == p[1] &&
((lp[1] == ' ' && len == 2) || lp[1] == p[2]) &&
!--cnt) {
adjust1: vp->m_stop.lno = lno;
vp->m_stop.cno = 0;
goto ret1;
}
}
/*
* If moving backward, reached SOF, which is a movement sink.
* We already checked for starting there.
*/
vp->m_stop.lno = 1;
vp->m_stop.cno = 0;
/*
* All commands move to the end of the range.
*
* !!!
* Historic practice is the section cut was in line mode if it started
* from column 0 and was in the backward direction. Otherwise, left
* motion commands adjust the starting point to the character before
* the current one. What makes this worse is that if it cut to line
* mode it also went to the first non-<blank>.
*/
ret1: if (vp->m_start.cno == 0) {
F_CLR(vp, VM_RCM_MASK);
F_SET(vp, VM_RCM_SETFNB);
--vp->m_start.lno;
F_SET(vp, VM_LMODE);
} else
--vp->m_start.cno;
vp->m_final = vp->m_stop;
return (0);
}
/*
* b_search --
* Do a backward search.
*
* PUBLIC: int b_search __P((SCR *,
* PUBLIC: MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
*/
int
b_search(
SCR *sp,
MARK *fm,
MARK *rm,
CHAR_T *ptrn,
size_t plen,
CHAR_T **eptrn,
u_int flags)
{
busy_t btype;
recno_t lno;
regmatch_t match[1];
size_t coff, last, len;
int cnt, eval, rval, wrapped;
CHAR_T *l;
if (search_init(sp, BACKWARD, ptrn, plen, eptrn, flags))
return (1);
/*
* If doing incremental search, set the "starting" position past the
* current column, so that we search a minimal distance and still
* match special patterns, e.g., \> for the end of a word. This is
* safe when the cursor is at the end of a line because we only use
* it for comparison with the location of the match.
*
* Otherwise, start searching immediately before the cursor. If in
* the first column, start search on the previous line.
*/
if (LF_ISSET(SEARCH_INCR)) {
lno = fm->lno;
coff = fm->cno + 1;
} else {
if (fm->cno == 0) {
if (fm->lno == 1 && !O_ISSET(sp, O_WRAPSCAN)) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_SOF);
return (1);
}
lno = fm->lno - 1;
} else
lno = fm->lno;
coff = fm->cno;
}
btype = BUSY_ON;
for (cnt = INTERRUPT_CHECK, rval = 1, wrapped = 0;; --lno, coff = 0) {
if (cnt-- == 0) {
if (INTERRUPTED(sp))
break;
if (LF_ISSET(SEARCH_MSG)) {
search_busy(sp, btype);
btype = BUSY_UPDATE;
}
cnt = INTERRUPT_CHECK;
}
if ((wrapped && lno < fm->lno) || lno == 0) {
if (wrapped) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_NOTFOUND);
break;
}
if (!O_ISSET(sp, O_WRAPSCAN)) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_SOF);
break;
}
if (db_last(sp, &lno))
break;
if (lno == 0) {
if (LF_ISSET(SEARCH_MSG))
search_msg(sp, S_EMPTY);
break;
}
++lno;
wrapped = 1;
continue;
}
if (db_get(sp, lno, 0, &l, &len))
break;
/* Set the termination. */
match[0].rm_so = 0;
match[0].rm_eo = len;
#if defined(DEBUG) && 0
TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo);
#endif
/* Search the line. */
eval = regexec(&sp->re_c, l, 1, match,
(match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND);
//.........这里部分代码省略.........
开发者ID:Alkzndr,项目名称:freebsd,代码行数:101,代码来源:search.c
示例15: v_sectionf
/*
* v_sectionf -- [count]]]
* Move forward count sections/functions.
*
* !!!
* Using ]] as a motion command was a bit special, historically. It could
* match } as well as the usual { and section values. If it matched a { or
* a section, it did NOT include the matched line. If it matched a }, it
* did include the line. No clue why.
*
* PUBLIC: int v_sectionf(SCR *, VICMD *);
*/
int
v_sectionf(SCR *sp, VICMD *vp)
{
recno_t cnt, lno;
size_t len;
CHAR_T *p;
char *list, *lp;
/* Get the macro list. */
if ((list = O_STR(sp, O_SECTIONS)) == NULL)
return (1);
/*
* !!!
* If the starting 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, the buffer is in line mode. It's a lot easier to
* check here, because we know that the end is going to be the start
* or end of a line.
*/
if (ISMOTION(vp))
if (vp->m_start.cno == 0)
F_SET(vp, VM_LMODE);
else {
vp->m_stop = vp->m_start;
vp->m_stop.cno = 0;
if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
return (1);
if (vp->m_start.cno <= vp->m_stop.cno)
F_SET(vp, VM_LMODE);
}
cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
for (lno = vp->m_start.lno; !db_get(sp, ++lno, 0, &p, &len);) {
if (len == 0)
continue;
if (p[0] == '{' || (ISMOTION(vp) && p[0] == '}')) {
if (!--cnt) {
if (p[0] == '{')
goto adjust1;
goto adjust2;
}
continue;
}
/*
* !!!
* Historic documentation (USD:15-11, 4.2) said that formfeed
* characters (^L) in the first column delimited sections.
* The historic code mentions formfeed characters, but never
* implements them. Seems reasonable, do it.
*/
if (p[0] == '\014') {
if (!--cnt)
goto adjust1;
continue;
}
if (p[0] != '.' || len < 2)
continue;
for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp))
if (lp[0] == p[1] &&
((lp[1] == ' ' && len == 2) || lp[1] == p[2]) &&
!--cnt) {
/*
* !!!
* If not cutting this line, adjust to the end
* of the previous one. Otherwise, position to
* column 0.
*/
adjust1: if (ISMOTION(vp))
goto ret1;
adjust2: vp->m_stop.lno = lno;
vp->m_stop.cno = 0;
goto ret2;
}
}
/* If moving forward, reached EOF, check to see if we started there. */
if (vp->m_start.lno == lno - 1) {
v_eof(sp, NULL);
return (1);
}
ret1: if (db_get(sp, --lno, DBG_FATAL, NULL, &len))
return (1);
vp->m_stop.lno = lno;
vp->m_stop.cno = len ? len - 1 : 0;
//.........这里部分代码省略.........
/*
* del --
* Delete a range of text.
*
* PUBLIC: int del __P((SCR *, MARK *, MARK *, int));
*/
int
del(
SCR *sp,
MARK *fm,
MARK *tm,
int lmode)
{
recno_t lno;
size_t blen, len, nlen, tlen;
CHAR_T *bp, *p;
int eof, rval;
bp = NULL;
/* Case 1 -- delete in line mode. */
if (lmode) {
for (lno = tm->lno; lno >= fm->lno; --lno) {
if (db_delete(sp, lno))
return (1);
++sp->rptlines[L_DELETED];
if (lno % INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
break;
}
goto done;
}
/*
* Case 2 -- delete to EOF. This is a special case because it's
* easier to pick it off than try and find it in the other cases.
*/
if (db_last(sp, &lno))
return (1);
if (tm->lno >= lno) {
if (tm->lno == lno) {
if (db_get(sp, lno, DBG_FATAL, &p, &len))
return (1);
eof = tm->cno != ENTIRE_LINE && tm->cno >= len ? 1 : 0;
} else
eof = 1;
if (eof) {
for (lno = tm->lno; lno > fm->lno; --lno) {
if (db_delete(sp, lno))
return (1);
++sp->rptlines[L_DELETED];
if (lno %
INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
break;
}
if (db_get(sp, fm->lno, DBG_FATAL, &p, &len))
return (1);
GET_SPACE_RETW(sp, bp, blen, fm->cno);
MEMCPY(bp, p, fm->cno);
if (db_set(sp, fm->lno, bp, fm->cno))
return (1);
goto done;
}
}
/* Case 3 -- delete within a single line. */
if (tm->lno == fm->lno) {
if (db_get(sp, fm->lno, DBG_FATAL, &p, &len))
return (1);
GET_SPACE_RETW(sp, bp, blen, len);
if (fm->cno != 0)
MEMCPY(bp, p, fm->cno);
MEMCPY(bp + fm->cno, p + (tm->cno + 1),
len - (tm->cno + 1));
if (db_set(sp, fm->lno,
bp, len - ((tm->cno - fm->cno) + 1)))
goto err;
goto done;
}
/*
* Case 4 -- delete over multiple lines.
*
* Copy the start partial line into place.
*/
if ((tlen = fm->cno) != 0) {
if (db_get(sp, fm->lno, DBG_FATAL, &p, NULL))
return (1);
GET_SPACE_RETW(sp, bp, blen, tlen + 256);
MEMCPY(bp, p, tlen);
}
/* Copy the end partial line into place. */
if (db_get(sp, tm->lno, DBG_FATAL, &p, &len))
goto err;
if (len != 0 && tm->cno != len - 1) {
/*
* XXX
* We can overflow memory here, if the total length is greater
* than SIZE_T_MAX. The only portable way I've found to test
* is depending on the overflow being less than the value.
//.........这里部分代码省略.........
请发表评论