1 |
|
|
/* $OpenBSD: v_replace.c,v 1.9 2016/01/06 22:28:52 millert Exp $ */ |
2 |
|
|
|
3 |
|
|
/*- |
4 |
|
|
* Copyright (c) 1992, 1993, 1994 |
5 |
|
|
* The Regents of the University of California. All rights reserved. |
6 |
|
|
* Copyright (c) 1992, 1993, 1994, 1995, 1996 |
7 |
|
|
* Keith Bostic. All rights reserved. |
8 |
|
|
* |
9 |
|
|
* See the LICENSE file for redistribution information. |
10 |
|
|
*/ |
11 |
|
|
|
12 |
|
|
#include "config.h" |
13 |
|
|
|
14 |
|
|
#include <sys/types.h> |
15 |
|
|
#include <sys/queue.h> |
16 |
|
|
#include <sys/time.h> |
17 |
|
|
|
18 |
|
|
#include <bitstring.h> |
19 |
|
|
#include <ctype.h> |
20 |
|
|
#include <errno.h> |
21 |
|
|
#include <limits.h> |
22 |
|
|
#include <stdio.h> |
23 |
|
|
#include <stdlib.h> |
24 |
|
|
#include <string.h> |
25 |
|
|
|
26 |
|
|
#include "../common/common.h" |
27 |
|
|
#include "vi.h" |
28 |
|
|
|
29 |
|
|
/* |
30 |
|
|
* v_replace -- [count]r<char> |
31 |
|
|
* |
32 |
|
|
* !!! |
33 |
|
|
* The r command in historic vi was almost beautiful in its badness. For |
34 |
|
|
* example, "r<erase>" and "r<word erase>" beeped the terminal and deleted |
35 |
|
|
* a single character. "Nr<carriage return>", where N was greater than 1, |
36 |
|
|
* inserted a single carriage return. "r<escape>" did cancel the command, |
37 |
|
|
* but "r<literal><escape>" erased a single character. To enter a literal |
38 |
|
|
* <literal> character, it required three <literal> characters after the |
39 |
|
|
* command. This may not be right, but at least it's not insane. |
40 |
|
|
* |
41 |
|
|
* PUBLIC: int v_replace(SCR *, VICMD *); |
42 |
|
|
*/ |
43 |
|
|
int |
44 |
|
|
v_replace(SCR *sp, VICMD *vp) |
45 |
|
|
{ |
46 |
|
|
EVENT ev; |
47 |
|
|
VI_PRIVATE *vip; |
48 |
|
|
TEXT *tp; |
49 |
|
|
size_t blen, len; |
50 |
|
|
u_long cnt; |
51 |
|
|
int quote, rval; |
52 |
|
|
char *bp, *p; |
53 |
|
|
|
54 |
|
|
vip = VIP(sp); |
55 |
|
|
|
56 |
|
|
/* |
57 |
|
|
* If the line doesn't exist, or it's empty, replacement isn't |
58 |
|
|
* allowed. It's not hard to implement, but: |
59 |
|
|
* |
60 |
|
|
* 1: It's historic practice (vi beeped before the replacement |
61 |
|
|
* character was even entered). |
62 |
|
|
* 2: For consistency, this change would require that the more |
63 |
|
|
* general case, "Nr", when the user is < N characters from |
64 |
|
|
* the end of the line, also work, which would be a bit odd. |
65 |
|
|
* 3: Replacing with a <newline> has somewhat odd semantics. |
66 |
|
|
*/ |
67 |
|
|
if (db_get(sp, vp->m_start.lno, DBG_FATAL, &p, &len)) |
68 |
|
|
return (1); |
69 |
|
|
if (len == 0) { |
70 |
|
|
msgq(sp, M_BERR, "No characters to replace"); |
71 |
|
|
return (1); |
72 |
|
|
} |
73 |
|
|
|
74 |
|
|
/* |
75 |
|
|
* Figure out how many characters to be replace. For no particular |
76 |
|
|
* reason (other than that the semantics of replacing the newline |
77 |
|
|
* are confusing) only permit the replacement of the characters in |
78 |
|
|
* the current line. I suppose we could append replacement characters |
79 |
|
|
* to the line, but I see no compelling reason to do so. Check this |
80 |
|
|
* before we get the character to match historic practice, where Nr |
81 |
|
|
* failed immediately if there were less than N characters from the |
82 |
|
|
* cursor to the end of the line. |
83 |
|
|
*/ |
84 |
|
|
cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; |
85 |
|
|
vp->m_stop.lno = vp->m_start.lno; |
86 |
|
|
vp->m_stop.cno = vp->m_start.cno + cnt - 1; |
87 |
|
|
if (vp->m_stop.cno > len - 1) { |
88 |
|
|
v_eol(sp, &vp->m_start); |
89 |
|
|
return (1); |
90 |
|
|
} |
91 |
|
|
|
92 |
|
|
/* |
93 |
|
|
* If it's not a repeat, reset the current mode and get a replacement |
94 |
|
|
* character. |
95 |
|
|
*/ |
96 |
|
|
quote = 0; |
97 |
|
|
if (!F_ISSET(vp, VC_ISDOT)) { |
98 |
|
|
sp->showmode = SM_REPLACE; |
99 |
|
|
if (vs_refresh(sp, 0)) |
100 |
|
|
return (1); |
101 |
|
|
next: if (v_event_get(sp, &ev, 0, 0)) |
102 |
|
|
return (1); |
103 |
|
|
|
104 |
|
|
switch (ev.e_event) { |
105 |
|
|
case E_CHARACTER: |
106 |
|
|
/* |
107 |
|
|
* <literal_next> means escape the next character. |
108 |
|
|
* <escape> means they changed their minds. |
109 |
|
|
*/ |
110 |
|
|
if (!quote) { |
111 |
|
|
if (ev.e_value == K_VLNEXT) { |
112 |
|
|
quote = 1; |
113 |
|
|
goto next; |
114 |
|
|
} |
115 |
|
|
if (ev.e_value == K_ESCAPE) |
116 |
|
|
return (0); |
117 |
|
|
} |
118 |
|
|
vip->rlast = ev.e_c; |
119 |
|
|
vip->rvalue = ev.e_value; |
120 |
|
|
break; |
121 |
|
|
case E_ERR: |
122 |
|
|
case E_EOF: |
123 |
|
|
F_SET(sp, SC_EXIT_FORCE); |
124 |
|
|
return (1); |
125 |
|
|
case E_INTERRUPT: |
126 |
|
|
/* <interrupt> means they changed their minds. */ |
127 |
|
|
return (0); |
128 |
|
|
case E_WRESIZE: |
129 |
|
|
/* <resize> interrupts the input mode. */ |
130 |
|
|
v_emsg(sp, NULL, VIM_WRESIZE); |
131 |
|
|
return (0); |
132 |
|
|
case E_REPAINT: |
133 |
|
|
if (vs_repaint(sp, &ev)) |
134 |
|
|
return (1); |
135 |
|
|
goto next; |
136 |
|
|
default: |
137 |
|
|
v_event_err(sp, &ev); |
138 |
|
|
return (0); |
139 |
|
|
} |
140 |
|
|
} |
141 |
|
|
|
142 |
|
|
/* Copy the line. */ |
143 |
|
|
GET_SPACE_RET(sp, bp, blen, len); |
144 |
|
|
memmove(bp, p, len); |
145 |
|
|
p = bp; |
146 |
|
|
|
147 |
|
|
/* |
148 |
|
|
* Versions of nvi before 1.57 created N new lines when they replaced |
149 |
|
|
* N characters with <carriage-return> or <newline> characters. This |
150 |
|
|
* is different from the historic vi, which replaced N characters with |
151 |
|
|
* a single new line. Users complained, so we match historic practice. |
152 |
|
|
*/ |
153 |
|
|
if ((!quote && vip->rvalue == K_CR) || vip->rvalue == K_NL) { |
154 |
|
|
/* Set return line. */ |
155 |
|
|
vp->m_stop.lno = vp->m_start.lno + 1; |
156 |
|
|
vp->m_stop.cno = 0; |
157 |
|
|
|
158 |
|
|
/* The first part of the current line. */ |
159 |
|
|
if (db_set(sp, vp->m_start.lno, p, vp->m_start.cno)) |
160 |
|
|
goto err_ret; |
161 |
|
|
|
162 |
|
|
/* |
163 |
|
|
* The rest of the current line. And, of course, now it gets |
164 |
|
|
* tricky. If there are characters left in the line and if |
165 |
|
|
* the autoindent edit option is set, white space after the |
166 |
|
|
* replaced character is discarded, autoindent is applied, and |
167 |
|
|
* the cursor moves to the last indent character. |
168 |
|
|
*/ |
169 |
|
|
p += vp->m_start.cno + cnt; |
170 |
|
|
len -= vp->m_start.cno + cnt; |
171 |
|
|
if (len != 0 && O_ISSET(sp, O_AUTOINDENT)) |
172 |
|
|
for (; len && isblank(*p); --len, ++p); |
173 |
|
|
|
174 |
|
|
if ((tp = text_init(sp, p, len, len)) == NULL) |
175 |
|
|
goto err_ret; |
176 |
|
|
|
177 |
|
|
if (len != 0 && O_ISSET(sp, O_AUTOINDENT)) { |
178 |
|
|
if (v_txt_auto(sp, vp->m_start.lno, NULL, 0, tp)) |
179 |
|
|
goto err_ret; |
180 |
|
|
vp->m_stop.cno = tp->ai ? tp->ai - 1 : 0; |
181 |
|
|
} else |
182 |
|
|
vp->m_stop.cno = 0; |
183 |
|
|
|
184 |
|
|
vp->m_stop.cno = tp->ai ? tp->ai - 1 : 0; |
185 |
|
|
if (db_append(sp, 1, vp->m_start.lno, tp->lb, tp->len)) |
186 |
|
|
err_ret: rval = 1; |
187 |
|
|
else { |
188 |
|
|
text_free(tp); |
189 |
|
|
rval = 0; |
190 |
|
|
} |
191 |
|
|
} else { |
192 |
|
|
memset(bp + vp->m_start.cno, vip->rlast, cnt); |
193 |
|
|
rval = db_set(sp, vp->m_start.lno, bp, len); |
194 |
|
|
} |
195 |
|
|
FREE_SPACE(sp, bp, blen); |
196 |
|
|
|
197 |
|
|
vp->m_final = vp->m_stop; |
198 |
|
|
return (rval); |
199 |
|
|
} |