1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
|
/*-
* Copyright (c) 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1992, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "@(#)v_replace.c 10.17 (Berkeley) 6/30/96";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../common/common.h"
#include "vi.h"
/*
* 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(sp, vp)
SCR *sp;
VICMD *vp;
{
EVENT ev;
VI_PRIVATE *vip;
TEXT *tp;
size_t blen, len;
u_long cnt;
int quote, rval;
char *bp, *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);
case E_WRESIZE:
/* <resize> interrupts the input mode. */
v_emsg(sp, NULL, VIM_WRESIZE);
return (0);
case E_REPAINT:
if (vs_repaint(sp, &ev))
return (1);
goto next;
default:
v_event_err(sp, &ev);
return (0);
}
}
/* Copy the line. */
GET_SPACE_RET(sp, bp, blen, len);
memmove(bp, p, len);
p = bp;
/*
* Versions of nvi before 1.57 created N new lines when they replaced
* N characters with <carriage-return> or <newline> characters. This
* is different from the historic vi, which replaced N characters with
* a single new line. Users complained, so we match historic practice.
*/
if (!quote && vip->rvalue == K_CR || vip->rvalue == K_NL) {
/* Set return line. */
vp->m_stop.lno = vp->m_start.lno + 1;
vp->m_stop.cno = 0;
/* The first part of the current line. */
if (db_set(sp, vp->m_start.lno, p, vp->m_start.cno))
goto err_ret;
/*
* The rest of the current line. And, of course, now it gets
* tricky. If there are characters left in the line and if
* the autoindent edit option is set, white space after the
* replaced character is discarded, autoindent is applied, and
* the cursor moves to the last indent character.
*/
p += vp->m_start.cno + cnt;
len -= vp->m_start.cno + cnt;
if (len != 0 && O_ISSET(sp, O_AUTOINDENT))
for (; len && isblank(*p); --len, ++p);
if ((tp = text_init(sp, p, len, len)) == NULL)
goto err_ret;
if (len != 0 && O_ISSET(sp, O_AUTOINDENT)) {
if (v_txt_auto(sp, vp->m_start.lno, NULL, 0, tp))
goto err_ret;
vp->m_stop.cno = tp->ai ? tp->ai - 1 : 0;
} else
vp->m_stop.cno = 0;
vp->m_stop.cno = tp->ai ? tp->ai - 1 : 0;
if (db_append(sp, 1, vp->m_start.lno, tp->lb, tp->len))
err_ret: rval = 1;
else {
text_free(tp);
rval = 0;
}
} else {
memset(bp + vp->m_start.cno, vip->rlast, cnt);
rval = db_set(sp, vp->m_start.lno, bp, len);
}
FREE_SPACE(sp, bp, blen);
vp->m_final = vp->m_stop;
return (rval);
}
|