1 |
|
|
/* $OpenBSD: v_txt.c,v 1.33 2016/05/27 09:18:12 martijn Exp $ */ |
2 |
|
|
|
3 |
|
|
/*- |
4 |
|
|
* Copyright (c) 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/queue.h> |
15 |
|
|
#include <sys/stat.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 |
|
|
#include <unistd.h> |
26 |
|
|
|
27 |
|
|
#include "../common/common.h" |
28 |
|
|
#include "vi.h" |
29 |
|
|
|
30 |
|
|
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) |
31 |
|
|
|
32 |
|
|
static int txt_abbrev(SCR *, TEXT *, CHAR_T *, int, int *, int *); |
33 |
|
|
static void txt_ai_resolve(SCR *, TEXT *, int *); |
34 |
|
|
static TEXT *txt_backup(SCR *, TEXTH *, TEXT *, u_int32_t *); |
35 |
|
|
static int txt_dent(SCR *, TEXT *, int); |
36 |
|
|
static int txt_emark(SCR *, TEXT *, size_t); |
37 |
|
|
static void txt_err(SCR *, TEXTH *); |
38 |
|
|
static int txt_fc(SCR *, TEXT *, int *); |
39 |
|
|
static int txt_fc_col(SCR *, int, ARGS **); |
40 |
|
|
static int txt_hex(SCR *, TEXT *); |
41 |
|
|
static int txt_insch(SCR *, TEXT *, CHAR_T *, u_int); |
42 |
|
|
static int txt_isrch(SCR *, VICMD *, TEXT *, u_int8_t *); |
43 |
|
|
static int txt_map_end(SCR *); |
44 |
|
|
static int txt_map_init(SCR *); |
45 |
|
|
static int txt_margin(SCR *, TEXT *, TEXT *, int *, u_int32_t); |
46 |
|
|
static void txt_nomorech(SCR *); |
47 |
|
|
static void txt_Rresolve(SCR *, TEXTH *, TEXT *, const size_t); |
48 |
|
|
static int txt_resolve(SCR *, TEXTH *, u_int32_t); |
49 |
|
|
static int txt_showmatch(SCR *, TEXT *); |
50 |
|
|
static void txt_unmap(SCR *, TEXT *, u_int32_t *); |
51 |
|
|
|
52 |
|
|
/* Cursor character (space is hard to track on the screen). */ |
53 |
|
|
#if defined(DEBUG) && 0 |
54 |
|
|
#undef CH_CURSOR |
55 |
|
|
#define CH_CURSOR '+' |
56 |
|
|
#endif |
57 |
|
|
|
58 |
|
|
/* |
59 |
|
|
* v_tcmd -- |
60 |
|
|
* Fill a buffer from the terminal for vi. |
61 |
|
|
* |
62 |
|
|
* PUBLIC: int v_tcmd(SCR *, VICMD *, CHAR_T, u_int); |
63 |
|
|
*/ |
64 |
|
|
int |
65 |
|
|
v_tcmd(SCR *sp, VICMD *vp, CHAR_T prompt, u_int flags) |
66 |
|
|
{ |
67 |
|
|
/* Normally, we end up where we started. */ |
68 |
|
|
vp->m_final.lno = sp->lno; |
69 |
|
|
vp->m_final.cno = sp->cno; |
70 |
|
|
|
71 |
|
|
/* Initialize the map. */ |
72 |
|
|
if (txt_map_init(sp)) |
73 |
|
|
return (1); |
74 |
|
|
|
75 |
|
|
/* Move to the last line. */ |
76 |
|
|
sp->lno = TMAP[0].lno; |
77 |
|
|
sp->cno = 0; |
78 |
|
|
|
79 |
|
|
/* Don't update the modeline for now. */ |
80 |
|
|
F_SET(sp, SC_TINPUT_INFO); |
81 |
|
|
|
82 |
|
|
/* Set the input flags. */ |
83 |
|
|
LF_SET(TXT_APPENDEOL | |
84 |
|
|
TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT); |
85 |
|
|
if (O_ISSET(sp, O_ALTWERASE)) |
86 |
|
|
LF_SET(TXT_ALTWERASE); |
87 |
|
|
if (O_ISSET(sp, O_TTYWERASE)) |
88 |
|
|
LF_SET(TXT_TTYWERASE); |
89 |
|
|
|
90 |
|
|
/* Do the input thing. */ |
91 |
|
|
if (v_txt(sp, vp, NULL, NULL, 0, prompt, 0, 1, flags)) |
92 |
|
|
return (1); |
93 |
|
|
|
94 |
|
|
/* Reenable the modeline updates. */ |
95 |
|
|
F_CLR(sp, SC_TINPUT_INFO); |
96 |
|
|
|
97 |
|
|
/* Clean up the map. */ |
98 |
|
|
if (txt_map_end(sp)) |
99 |
|
|
return (1); |
100 |
|
|
|
101 |
|
|
if (IS_ONELINE(sp)) |
102 |
|
|
F_SET(sp, SC_SCR_REDRAW); /* XXX */ |
103 |
|
|
|
104 |
|
|
/* Set the cursor to the resulting position. */ |
105 |
|
|
sp->lno = vp->m_final.lno; |
106 |
|
|
sp->cno = vp->m_final.cno; |
107 |
|
|
|
108 |
|
|
return (0); |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
/* |
112 |
|
|
* txt_map_init |
113 |
|
|
* Initialize the screen map for colon command-line input. |
114 |
|
|
*/ |
115 |
|
|
static int |
116 |
|
|
txt_map_init(SCR *sp) |
117 |
|
|
{ |
118 |
|
|
SMAP *esmp; |
119 |
|
|
VI_PRIVATE *vip; |
120 |
|
|
|
121 |
|
|
vip = VIP(sp); |
122 |
|
|
if (!IS_ONELINE(sp)) { |
123 |
|
|
/* |
124 |
|
|
* Fake like the user is doing input on the last line of the |
125 |
|
|
* screen. This makes all of the scrolling work correctly, |
126 |
|
|
* and allows us the use of the vi text editing routines, not |
127 |
|
|
* to mention practically infinite length ex commands. |
128 |
|
|
* |
129 |
|
|
* Save the current location. |
130 |
|
|
*/ |
131 |
|
|
vip->sv_tm_lno = TMAP->lno; |
132 |
|
|
vip->sv_tm_soff = TMAP->soff; |
133 |
|
|
vip->sv_tm_coff = TMAP->coff; |
134 |
|
|
vip->sv_t_maxrows = sp->t_maxrows; |
135 |
|
|
vip->sv_t_minrows = sp->t_minrows; |
136 |
|
|
vip->sv_t_rows = sp->t_rows; |
137 |
|
|
|
138 |
|
|
/* |
139 |
|
|
* If it's a small screen, TMAP may be small for the screen. |
140 |
|
|
* Fix it, filling in fake lines as we go. |
141 |
|
|
*/ |
142 |
|
|
if (IS_SMALL(sp)) |
143 |
|
|
for (esmp = |
144 |
|
|
HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) { |
145 |
|
|
TMAP[1].lno = TMAP[0].lno + 1; |
146 |
|
|
TMAP[1].coff = HMAP->coff; |
147 |
|
|
TMAP[1].soff = 1; |
148 |
|
|
} |
149 |
|
|
|
150 |
|
|
/* Build the fake entry. */ |
151 |
|
|
TMAP[1].lno = TMAP[0].lno + 1; |
152 |
|
|
TMAP[1].soff = 1; |
153 |
|
|
TMAP[1].coff = 0; |
154 |
|
|
SMAP_FLUSH(&TMAP[1]); |
155 |
|
|
++TMAP; |
156 |
|
|
|
157 |
|
|
/* Reset the screen information. */ |
158 |
|
|
sp->t_rows = sp->t_minrows = ++sp->t_maxrows; |
159 |
|
|
} |
160 |
|
|
return (0); |
161 |
|
|
} |
162 |
|
|
|
163 |
|
|
/* |
164 |
|
|
* txt_map_end |
165 |
|
|
* Reset the screen map for colon command-line input. |
166 |
|
|
*/ |
167 |
|
|
static int |
168 |
|
|
txt_map_end(SCR *sp) |
169 |
|
|
{ |
170 |
|
|
VI_PRIVATE *vip; |
171 |
|
|
size_t cnt; |
172 |
|
|
|
173 |
|
|
vip = VIP(sp); |
174 |
|
|
if (!IS_ONELINE(sp)) { |
175 |
|
|
/* Restore the screen information. */ |
176 |
|
|
sp->t_rows = vip->sv_t_rows; |
177 |
|
|
sp->t_minrows = vip->sv_t_minrows; |
178 |
|
|
sp->t_maxrows = vip->sv_t_maxrows; |
179 |
|
|
|
180 |
|
|
/* |
181 |
|
|
* If it's a small screen, TMAP may be wrong. Clear any |
182 |
|
|
* lines that might have been overwritten. |
183 |
|
|
*/ |
184 |
|
|
if (IS_SMALL(sp)) { |
185 |
|
|
for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) { |
186 |
|
|
(void)sp->gp->scr_move(sp, cnt, 0); |
187 |
|
|
(void)sp->gp->scr_clrtoeol(sp); |
188 |
|
|
} |
189 |
|
|
TMAP = HMAP + (sp->t_rows - 1); |
190 |
|
|
} else |
191 |
|
|
--TMAP; |
192 |
|
|
|
193 |
|
|
/* |
194 |
|
|
* The map may be wrong if the user entered more than one |
195 |
|
|
* (logical) line. Fix it. If the user entered a whole |
196 |
|
|
* screen, this will be slow, but we probably don't care. |
197 |
|
|
*/ |
198 |
|
|
if (!O_ISSET(sp, O_LEFTRIGHT)) |
199 |
|
|
while (vip->sv_tm_lno != TMAP->lno || |
200 |
|
|
vip->sv_tm_soff != TMAP->soff) |
201 |
|
|
if (vs_sm_1down(sp)) |
202 |
|
|
return (1); |
203 |
|
|
} |
204 |
|
|
|
205 |
|
|
/* |
206 |
|
|
* Invalidate the cursor and the line size cache, the line never |
207 |
|
|
* really existed. This fixes bugs where the user searches for |
208 |
|
|
* the last line on the screen + 1 and the refresh routine thinks |
209 |
|
|
* that's where we just were. |
210 |
|
|
*/ |
211 |
|
|
VI_SCR_CFLUSH(vip); |
212 |
|
|
F_SET(vip, VIP_CUR_INVALID); |
213 |
|
|
|
214 |
|
|
return (0); |
215 |
|
|
} |
216 |
|
|
|
217 |
|
|
/* |
218 |
|
|
* If doing input mapping on the colon command line, may need to unmap |
219 |
|
|
* based on the command. |
220 |
|
|
*/ |
221 |
|
|
#define UNMAP_TST \ |
222 |
|
|
FL_ISSET(ec_flags, EC_MAPINPUT) && LF_ISSET(TXT_INFOLINE) |
223 |
|
|
|
224 |
|
|
/* |
225 |
|
|
* Internally, we maintain tp->lno and tp->cno, externally, everyone uses |
226 |
|
|
* sp->lno and sp->cno. Make them consistent as necessary. |
227 |
|
|
*/ |
228 |
|
|
#define UPDATE_POSITION(sp, tp) { \ |
229 |
|
|
(sp)->lno = (tp)->lno; \ |
230 |
|
|
(sp)->cno = (tp)->cno; \ |
231 |
|
|
} |
232 |
|
|
|
233 |
|
|
/* |
234 |
|
|
* v_txt -- |
235 |
|
|
* Vi text input. |
236 |
|
|
* |
237 |
|
|
* PUBLIC: int v_txt(SCR *, VICMD *, MARK *, |
238 |
|
|
* PUBLIC: const char *, size_t, CHAR_T, recno_t, u_long, u_int32_t); |
239 |
|
|
*/ |
240 |
|
|
int |
241 |
|
|
v_txt(SCR *sp, VICMD *vp, MARK *tm, const char *lp, size_t len, |
242 |
|
|
CHAR_T prompt, recno_t ai_line, u_long rcount, u_int32_t flags) |
243 |
|
|
{ |
244 |
|
|
EVENT ev, *evp = NULL; /* Current event. */ |
245 |
|
|
EVENT fc; /* File name completion event. */ |
246 |
|
|
GS *gp; |
247 |
|
|
TEXT *ntp, *tp; /* Input text structures. */ |
248 |
|
|
TEXT ait; /* Autoindent text structure. */ |
249 |
|
|
TEXT wmt; /* Wrapmargin text structure. */ |
250 |
|
|
TEXTH *tiqh; |
251 |
|
|
VI_PRIVATE *vip; |
252 |
|
|
abb_t abb; /* State of abbreviation checks. */ |
253 |
|
|
carat_t carat; /* State of the "[^0]^D" sequences. */ |
254 |
|
|
quote_t quote; /* State of quotation. */ |
255 |
|
|
size_t owrite, insert; /* Temporary copies of TEXT fields. */ |
256 |
|
|
size_t margin; /* Wrapmargin value. */ |
257 |
|
|
size_t rcol; /* 0-N: insert offset in the replay buffer. */ |
258 |
|
|
size_t tcol; /* Temporary column. */ |
259 |
|
|
u_int32_t ec_flags; /* Input mapping flags. */ |
260 |
|
|
#define IS_RESTART 0x01 /* Reset the incremental search. */ |
261 |
|
|
#define IS_RUNNING 0x02 /* Incremental search turned on. */ |
262 |
|
|
u_int8_t is_flags; |
263 |
|
|
int abcnt, ab_turnoff; /* Abbreviation character count, switch. */ |
264 |
|
|
int filec_redraw; /* Redraw after the file completion routine. */ |
265 |
|
|
int hexcnt; /* Hex character count. */ |
266 |
|
|
int showmatch; /* Showmatch set on this character. */ |
267 |
|
|
int wm_set, wm_skip; /* Wrapmargin happened, blank skip flags. */ |
268 |
|
|
int max, tmp; |
269 |
|
|
char *p; |
270 |
|
|
|
271 |
|
|
gp = sp->gp; |
272 |
|
|
vip = VIP(sp); |
273 |
|
|
|
274 |
|
|
/* |
275 |
|
|
* Set the input flag, so tabs get displayed correctly |
276 |
|
|
* and everyone knows that the text buffer is in use. |
277 |
|
|
*/ |
278 |
|
|
F_SET(sp, SC_TINPUT); |
279 |
|
|
|
280 |
|
|
/* |
281 |
|
|
* Get one TEXT structure with some initial buffer space, reusing |
282 |
|
|
* the last one if it's big enough. (All TEXT bookkeeping fields |
283 |
|
|
* default to 0 -- text_init() handles this.) If changing a line, |
284 |
|
|
* copy it into the TEXT buffer. |
285 |
|
|
*/ |
286 |
|
|
tiqh = &sp->tiq; |
287 |
|
|
if (!TAILQ_EMPTY(tiqh)) { |
288 |
|
|
tp = TAILQ_FIRST(tiqh); |
289 |
|
|
if (TAILQ_NEXT(tp, q) || tp->lb_len < len + 32) { |
290 |
|
|
text_lfree(tiqh); |
291 |
|
|
goto newtp; |
292 |
|
|
} |
293 |
|
|
tp->ai = tp->insert = tp->offset = tp->owrite = 0; |
294 |
|
|
if (lp != NULL) { |
295 |
|
|
tp->len = len; |
296 |
|
|
memmove(tp->lb, lp, len); |
297 |
|
|
} else |
298 |
|
|
tp->len = 0; |
299 |
|
|
} else { |
300 |
|
|
newtp: if ((tp = text_init(sp, lp, len, len + 32)) == NULL) |
301 |
|
|
return (1); |
302 |
|
|
TAILQ_INSERT_HEAD(tiqh, tp, q); |
303 |
|
|
} |
304 |
|
|
|
305 |
|
|
/* Set default termination condition. */ |
306 |
|
|
tp->term = TERM_OK; |
307 |
|
|
|
308 |
|
|
/* Set the starting line, column. */ |
309 |
|
|
tp->lno = sp->lno; |
310 |
|
|
tp->cno = sp->cno; |
311 |
|
|
|
312 |
|
|
/* |
313 |
|
|
* Set the insert and overwrite counts. If overwriting characters, |
314 |
|
|
* do insertion afterward. If not overwriting characters, assume |
315 |
|
|
* doing insertion. If change is to a mark, emphasize it with an |
316 |
|
|
* CH_ENDMARK character. |
317 |
|
|
*/ |
318 |
|
|
if (len) { |
319 |
|
|
if (LF_ISSET(TXT_OVERWRITE)) { |
320 |
|
|
tp->owrite = (tm->cno - tp->cno) + 1; |
321 |
|
|
tp->insert = (len - tm->cno) - 1; |
322 |
|
|
} else |
323 |
|
|
tp->insert = len - tp->cno; |
324 |
|
|
|
325 |
|
|
if (LF_ISSET(TXT_EMARK) && txt_emark(sp, tp, tm->cno)) |
326 |
|
|
return (1); |
327 |
|
|
} |
328 |
|
|
|
329 |
|
|
/* |
330 |
|
|
* Many of the special cases in text input are to handle autoindent |
331 |
|
|
* support. Somebody decided that it would be a good idea if "^^D" |
332 |
|
|
* and "0^D" deleted all of the autoindented characters. In an editor |
333 |
|
|
* that takes single character input from the user, this beggars the |
334 |
|
|
* imagination. Note also, "^^D" resets the next lines' autoindent, |
335 |
|
|
* but "0^D" doesn't. |
336 |
|
|
* |
337 |
|
|
* We assume that autoindent only happens on empty lines, so insert |
338 |
|
|
* and overwrite will be zero. If doing autoindent, figure out how |
339 |
|
|
* much indentation we need and fill it in. Update input column and |
340 |
|
|
* screen cursor as necessary. |
341 |
|
|
*/ |
342 |
|
|
if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) { |
343 |
|
|
if (v_txt_auto(sp, ai_line, NULL, 0, tp)) |
344 |
|
|
return (1); |
345 |
|
|
tp->cno = tp->ai; |
346 |
|
|
} else { |
347 |
|
|
/* |
348 |
|
|
* The cc and S commands have a special feature -- leading |
349 |
|
|
* <blank> characters are handled as autoindent characters. |
350 |
|
|
* Beauty! |
351 |
|
|
*/ |
352 |
|
|
if (LF_ISSET(TXT_AICHARS)) { |
353 |
|
|
tp->offset = 0; |
354 |
|
|
tp->ai = tp->cno; |
355 |
|
|
} else |
356 |
|
|
tp->offset = tp->cno; |
357 |
|
|
} |
358 |
|
|
|
359 |
|
|
/* If getting a command buffer from the user, there may be a prompt. */ |
360 |
|
|
if (LF_ISSET(TXT_PROMPT)) { |
361 |
|
|
tp->lb[tp->cno++] = prompt; |
362 |
|
|
++tp->len; |
363 |
|
|
++tp->offset; |
364 |
|
|
} |
365 |
|
|
|
366 |
|
|
/* |
367 |
|
|
* If appending after the end-of-line, add a space into the buffer |
368 |
|
|
* and move the cursor right. This space is inserted, i.e. pushed |
369 |
|
|
* along, and then deleted when the line is resolved. Assumes that |
370 |
|
|
* the cursor is already positioned at the end of the line. This |
371 |
|
|
* avoids the nastiness of having the cursor reside on a magical |
372 |
|
|
* column, i.e. a column that doesn't really exist. The only down |
373 |
|
|
* side is that we may wrap lines or scroll the screen before it's |
374 |
|
|
* strictly necessary. Not a big deal. |
375 |
|
|
*/ |
376 |
|
|
if (LF_ISSET(TXT_APPENDEOL)) { |
377 |
|
|
tp->lb[tp->cno] = CH_CURSOR; |
378 |
|
|
++tp->len; |
379 |
|
|
++tp->insert; |
380 |
|
|
(void)vs_change(sp, tp->lno, LINE_RESET); |
381 |
|
|
} |
382 |
|
|
|
383 |
|
|
/* |
384 |
|
|
* Historic practice is that the wrapmargin value was a distance |
385 |
|
|
* from the RIGHT-HAND margin, not the left. It's more useful to |
386 |
|
|
* us as a distance from the left-hand margin, i.e. the same as |
387 |
|
|
* the wraplen value. The wrapmargin option is historic practice. |
388 |
|
|
* Nvi added the wraplen option so that it would be possible to |
389 |
|
|
* edit files with consistent margins without knowing the number of |
390 |
|
|
* columns in the window. |
391 |
|
|
* |
392 |
|
|
* XXX |
393 |
|
|
* Setting margin causes a significant performance hit. Normally |
394 |
|
|
* we don't update the screen if there are keys waiting, but we |
395 |
|
|
* have to if margin is set, otherwise the screen routines don't |
396 |
|
|
* know where the cursor is. |
397 |
|
|
* |
398 |
|
|
* !!! |
399 |
|
|
* Abbreviated keys were affected by the wrapmargin option in the |
400 |
|
|
* historic 4BSD vi. Mapped keys were usually, but sometimes not. |
401 |
|
|
* See the comment in vi/v_text():set_txt_std for more information. |
402 |
|
|
* |
403 |
|
|
* !!! |
404 |
|
|
* One more special case. If an inserted <blank> character causes |
405 |
|
|
* wrapmargin to split the line, the next user entered character is |
406 |
|
|
* discarded if it's a <space> character. |
407 |
|
|
*/ |
408 |
|
|
wm_set = wm_skip = 0; |
409 |
|
|
if (LF_ISSET(TXT_WRAPMARGIN)) |
410 |
|
|
if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0) |
411 |
|
|
margin = sp->cols - margin; |
412 |
|
|
else |
413 |
|
|
margin = O_VAL(sp, O_WRAPLEN); |
414 |
|
|
else |
415 |
|
|
margin = 0; |
416 |
|
|
|
417 |
|
|
/* Initialize abbreviation checks. */ |
418 |
|
|
abcnt = ab_turnoff = 0; |
419 |
|
|
abb = F_ISSET(gp, G_ABBREV) && |
420 |
|
|
LF_ISSET(TXT_MAPINPUT) ? AB_INWORD : AB_NOTSET; |
421 |
|
|
|
422 |
|
|
/* |
423 |
|
|
* Set up the dot command. Dot commands are done by saving the actual |
424 |
|
|
* characters and then reevaluating them so that things like wrapmargin |
425 |
|
|
* can change between the insert and the replay. |
426 |
|
|
* |
427 |
|
|
* !!! |
428 |
|
|
* Historically, vi did not remap or reabbreviate replayed input. (It |
429 |
|
|
* did beep at you if you changed an abbreviation and then replayed the |
430 |
|
|
* input. We're not that compatible.) We don't have to do anything to |
431 |
|
|
* avoid remapping, as we're not getting characters from the terminal |
432 |
|
|
* routines. Turn the abbreviation check off. |
433 |
|
|
* |
434 |
|
|
* XXX |
435 |
|
|
* It would be nice if we could swallow backspaces and such, but it's |
436 |
|
|
* not all that easy to do. What we can do is turn off the common |
437 |
|
|
* error messages during the replay. Otherwise, when the user enters |
438 |
|
|
* an illegal command, e.g., "Ia<erase><erase><erase><erase>b<escape>", |
439 |
|
|
* and then does a '.', they get a list of error messages after command |
440 |
|
|
* completion. |
441 |
|
|
*/ |
442 |
|
|
rcol = 0; |
443 |
|
|
if (LF_ISSET(TXT_REPLAY)) { |
444 |
|
|
abb = AB_NOTSET; |
445 |
|
|
LF_CLR(TXT_RECORD); |
446 |
|
|
} |
447 |
|
|
|
448 |
|
|
/* Other text input mode setup. */ |
449 |
|
|
quote = Q_NOTSET; |
450 |
|
|
carat = C_NOTSET; |
451 |
|
|
FL_INIT(is_flags, |
452 |
|
|
LF_ISSET(TXT_SEARCHINCR) ? IS_RESTART | IS_RUNNING : 0); |
453 |
|
|
filec_redraw = hexcnt = showmatch = 0; |
454 |
|
|
|
455 |
|
|
/* Initialize input flags. */ |
456 |
|
|
ec_flags = LF_ISSET(TXT_MAPINPUT) ? EC_MAPINPUT : 0; |
457 |
|
|
|
458 |
|
|
/* Refresh the screen. */ |
459 |
|
|
UPDATE_POSITION(sp, tp); |
460 |
|
|
if (vs_refresh(sp, 1)) |
461 |
|
|
return (1); |
462 |
|
|
|
463 |
|
|
/* If it's dot, just do it now. */ |
464 |
|
|
if (F_ISSET(vp, VC_ISDOT)) |
465 |
|
|
goto replay; |
466 |
|
|
|
467 |
|
|
/* Get an event. */ |
468 |
|
|
evp = &ev; |
469 |
|
|
next: if (v_event_get(sp, evp, 0, ec_flags)) |
470 |
|
|
return (1); |
471 |
|
|
|
472 |
|
|
/* |
473 |
|
|
* If file completion overwrote part of the screen and nothing else has |
474 |
|
|
* been displayed, clean up. We don't do this as part of the normal |
475 |
|
|
* message resolution because we know the user is on the colon command |
476 |
|
|
* line and there's no reason to enter explicit characters to continue. |
477 |
|
|
*/ |
478 |
|
|
if (filec_redraw && !F_ISSET(sp, SC_SCR_EXWROTE)) { |
479 |
|
|
filec_redraw = 0; |
480 |
|
|
|
481 |
|
|
fc.e_event = E_REPAINT; |
482 |
|
|
fc.e_flno = vip->totalcount >= |
483 |
|
|
sp->rows ? 1 : sp->rows - vip->totalcount; |
484 |
|
|
fc.e_tlno = sp->rows; |
485 |
|
|
vip->linecount = vip->lcontinue = vip->totalcount = 0; |
486 |
|
|
(void)vs_repaint(sp, &fc); |
487 |
|
|
(void)vs_refresh(sp, 1); |
488 |
|
|
} |
489 |
|
|
|
490 |
|
|
/* Deal with all non-character events. */ |
491 |
|
|
switch (evp->e_event) { |
492 |
|
|
case E_CHARACTER: |
493 |
|
|
break; |
494 |
|
|
case E_ERR: |
495 |
|
|
case E_EOF: |
496 |
|
|
F_SET(sp, SC_EXIT_FORCE); |
497 |
|
|
return (1); |
498 |
|
|
case E_REPAINT: |
499 |
|
|
if (vs_repaint(sp, &ev)) |
500 |
|
|
return (1); |
501 |
|
|
goto next; |
502 |
|
|
case E_WRESIZE: |
503 |
|
|
/* <resize> interrupts the input mode. */ |
504 |
|
|
v_emsg(sp, NULL, VIM_WRESIZE); |
505 |
|
|
/* FALLTHROUGH */ |
506 |
|
|
default: |
507 |
|
|
if (evp->e_event != E_INTERRUPT && evp->e_event != E_WRESIZE) |
508 |
|
|
v_event_err(sp, evp); |
509 |
|
|
/* |
510 |
|
|
* !!! |
511 |
|
|
* Historically, <interrupt> exited the user from text input |
512 |
|
|
* mode or cancelled a colon command, and returned to command |
513 |
|
|
* mode. It also beeped the terminal, but that seems a bit |
514 |
|
|
* excessive. |
515 |
|
|
*/ |
516 |
|
|
/* |
517 |
|
|
* If we are recording, morph into <escape> key so that |
518 |
|
|
* we can repeat the command safely: there is no way to |
519 |
|
|
* invalidate the repetition of an instance of a command, |
520 |
|
|
* which would be the alternative possibility. |
521 |
|
|
* If we are not recording (most likely on the command line), |
522 |
|
|
* simply discard the input and return to command mode |
523 |
|
|
* so that an INTERRUPT doesn't become for example a file |
524 |
|
|
* completion request. -aymeric |
525 |
|
|
*/ |
526 |
|
|
if (LF_ISSET(TXT_RECORD)) { |
527 |
|
|
evp->e_event = E_CHARACTER; |
528 |
|
|
evp->e_c = 033; |
529 |
|
|
evp->e_flags = 0; |
530 |
|
|
evp->e_value = K_ESCAPE; |
531 |
|
|
break; |
532 |
|
|
} else { |
533 |
|
|
tp->term = TERM_ESC; |
534 |
|
|
goto k_escape; |
535 |
|
|
} |
536 |
|
|
} |
537 |
|
|
|
538 |
|
|
/* |
539 |
|
|
* !!! |
540 |
|
|
* If the first character of the input is a nul, replay the previous |
541 |
|
|
* input. (Historically, it's okay to replay non-existent input.) |
542 |
|
|
* This was not documented as far as I know, and is a great test of vi |
543 |
|
|
* clones. |
544 |
|
|
*/ |
545 |
|
|
if (LF_ISSET(TXT_RECORD) && rcol == 0 && evp->e_c == '\0') { |
546 |
|
|
if (vip->rep == NULL) |
547 |
|
|
goto done; |
548 |
|
|
|
549 |
|
|
abb = AB_NOTSET; |
550 |
|
|
LF_CLR(TXT_RECORD); |
551 |
|
|
LF_SET(TXT_REPLAY); |
552 |
|
|
goto replay; |
553 |
|
|
} |
554 |
|
|
|
555 |
|
|
/* |
556 |
|
|
* File name completion and colon command-line editing. We don't |
557 |
|
|
* have enough meta characters, so we expect people to overload |
558 |
|
|
* them. If the two characters are the same, then we do file name |
559 |
|
|
* completion if the cursor is past the first column, and do colon |
560 |
|
|
* command-line editing if it's not. |
561 |
|
|
*/ |
562 |
|
|
if (quote == Q_NOTSET) { |
563 |
|
|
int L__cedit, L__filec; |
564 |
|
|
|
565 |
|
|
L__cedit = L__filec = 0; |
566 |
|
|
if (LF_ISSET(TXT_CEDIT) && O_STR(sp, O_CEDIT) != NULL && |
567 |
|
|
O_STR(sp, O_CEDIT)[0] == evp->e_c) |
568 |
|
|
L__cedit = 1; |
569 |
|
|
if (LF_ISSET(TXT_FILEC) && O_STR(sp, O_FILEC) != NULL && |
570 |
|
|
O_STR(sp, O_FILEC)[0] == evp->e_c) |
571 |
|
|
L__filec = 1; |
572 |
|
|
if (L__cedit == 1 && (L__filec == 0 || tp->cno == tp->offset)) { |
573 |
|
|
tp->term = TERM_CEDIT; |
574 |
|
|
goto k_escape; |
575 |
|
|
} |
576 |
|
|
if (L__filec == 1) { |
577 |
|
|
if (txt_fc(sp, tp, &filec_redraw)) |
578 |
|
|
goto err; |
579 |
|
|
goto resolve; |
580 |
|
|
} |
581 |
|
|
} |
582 |
|
|
|
583 |
|
|
/* Abbreviation overflow check. See comment in txt_abbrev(). */ |
584 |
|
|
#define MAX_ABBREVIATION_EXPANSION 256 |
585 |
|
|
if (F_ISSET(&evp->e_ch, CH_ABBREVIATED)) { |
586 |
|
|
if (++abcnt > MAX_ABBREVIATION_EXPANSION) { |
587 |
|
|
if (v_event_flush(sp, CH_ABBREVIATED)) |
588 |
|
|
msgq(sp, M_ERR, |
589 |
|
|
"Abbreviation exceeded expansion limit: characters discarded"); |
590 |
|
|
abcnt = 0; |
591 |
|
|
if (LF_ISSET(TXT_REPLAY)) |
592 |
|
|
goto done; |
593 |
|
|
goto resolve; |
594 |
|
|
} |
595 |
|
|
} else |
596 |
|
|
abcnt = 0; |
597 |
|
|
|
598 |
|
|
/* Check to see if the character fits into the replay buffers. */ |
599 |
|
|
if (LF_ISSET(TXT_RECORD)) { |
600 |
|
|
BINC_GOTO(sp, vip->rep, |
601 |
|
|
vip->rep_len, (rcol + 1) * sizeof(EVENT)); |
602 |
|
|
vip->rep[rcol++] = *evp; |
603 |
|
|
} |
604 |
|
|
|
605 |
|
|
replay: if (LF_ISSET(TXT_REPLAY)) |
606 |
|
|
evp = vip->rep + rcol++; |
607 |
|
|
|
608 |
|
|
/* Wrapmargin check for leading space. */ |
609 |
|
|
if (wm_skip) { |
610 |
|
|
wm_skip = 0; |
611 |
|
|
if (evp->e_c == ' ') |
612 |
|
|
goto resolve; |
613 |
|
|
} |
614 |
|
|
|
615 |
|
|
/* If quoted by someone else, simply insert the character. */ |
616 |
|
|
if (F_ISSET(&evp->e_ch, CH_QUOTED)) |
617 |
|
|
goto insq_ch; |
618 |
|
|
|
619 |
|
|
/* |
620 |
|
|
* !!! |
621 |
|
|
* If this character was quoted by a K_VLNEXT, replace the placeholder |
622 |
|
|
* (a carat) with the new character. We've already adjusted the cursor |
623 |
|
|
* because it has to appear on top of the placeholder character. |
624 |
|
|
* Historic practice. |
625 |
|
|
* |
626 |
|
|
* Skip tests for abbreviations; ":ab xa XA" followed by "ixa^V<space>" |
627 |
|
|
* doesn't perform an abbreviation. Special case, ^V^J (not ^V^M) is |
628 |
|
|
* the same as ^J, historically. |
629 |
|
|
*/ |
630 |
|
|
if (quote == Q_VTHIS) { |
631 |
|
|
FL_CLR(ec_flags, EC_QUOTED); |
632 |
|
|
if (LF_ISSET(TXT_MAPINPUT)) |
633 |
|
|
FL_SET(ec_flags, EC_MAPINPUT); |
634 |
|
|
|
635 |
|
|
if (evp->e_value != K_NL) { |
636 |
|
|
quote = Q_NOTSET; |
637 |
|
|
goto insl_ch; |
638 |
|
|
} |
639 |
|
|
quote = Q_NOTSET; |
640 |
|
|
} |
641 |
|
|
|
642 |
|
|
/* |
643 |
|
|
* !!! |
644 |
|
|
* Translate "<CH_HEX>[isxdigit()]*" to a character with a hex value: |
645 |
|
|
* this test delimits the value by any non-hex character. Offset by |
646 |
|
|
* one, we use 0 to mean that we've found <CH_HEX>. |
647 |
|
|
*/ |
648 |
|
|
if (hexcnt > 1 && !isxdigit(evp->e_c)) { |
649 |
|
|
hexcnt = 0; |
650 |
|
|
if (txt_hex(sp, tp)) |
651 |
|
|
goto err; |
652 |
|
|
} |
653 |
|
|
|
654 |
|
|
switch (evp->e_value) { |
655 |
|
|
case K_CR: /* Carriage return. */ |
656 |
|
|
case K_NL: /* New line. */ |
657 |
|
|
/* Return in script windows and the command line. */ |
658 |
|
|
k_cr: if (LF_ISSET(TXT_CR)) { |
659 |
|
|
/* |
660 |
|
|
* If this was a map, we may have not displayed |
661 |
|
|
* the line. Display it, just in case. |
662 |
|
|
* |
663 |
|
|
* If a script window and not the colon line, |
664 |
|
|
* push a <cr> so it gets executed. |
665 |
|
|
*/ |
666 |
|
|
if (LF_ISSET(TXT_INFOLINE)) { |
667 |
|
|
if (vs_change(sp, tp->lno, LINE_RESET)) |
668 |
|
|
goto err; |
669 |
|
|
} else if (F_ISSET(sp, SC_SCRIPT)) |
670 |
|
|
(void)v_event_push(sp, NULL, "\r", 1, CH_NOMAP); |
671 |
|
|
|
672 |
|
|
/* Set term condition: if empty. */ |
673 |
|
|
if (tp->cno <= tp->offset) |
674 |
|
|
tp->term = TERM_CR; |
675 |
|
|
/* |
676 |
|
|
* Set term condition: if searching incrementally and |
677 |
|
|
* the user entered a pattern, return a completed |
678 |
|
|
* search, regardless if the entire pattern was found. |
679 |
|
|
*/ |
680 |
|
|
if (FL_ISSET(is_flags, IS_RUNNING) && |
681 |
|
|
tp->cno >= tp->offset + 1) |
682 |
|
|
tp->term = TERM_SEARCH; |
683 |
|
|
|
684 |
|
|
goto k_escape; |
685 |
|
|
} |
686 |
|
|
|
687 |
|
|
#define LINE_RESOLVE { \ |
688 |
|
|
/* \ |
689 |
|
|
* Handle abbreviations. If there was one, discard the \ |
690 |
|
|
* replay characters. \ |
691 |
|
|
*/ \ |
692 |
|
|
if (abb == AB_INWORD && \ |
693 |
|
|
!LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) { \ |
694 |
|
|
if (txt_abbrev(sp, tp, &evp->e_c, \ |
695 |
|
|
LF_ISSET(TXT_INFOLINE), &tmp, \ |
696 |
|
|
&ab_turnoff)) \ |
697 |
|
|
goto err; \ |
698 |
|
|
if (tmp) { \ |
699 |
|
|
if (LF_ISSET(TXT_RECORD)) \ |
700 |
|
|
rcol -= tmp + 1; \ |
701 |
|
|
goto resolve; \ |
702 |
|
|
} \ |
703 |
|
|
} \ |
704 |
|
|
if (abb != AB_NOTSET) \ |
705 |
|
|
abb = AB_NOTWORD; \ |
706 |
|
|
if (UNMAP_TST) \ |
707 |
|
|
txt_unmap(sp, tp, &ec_flags); \ |
708 |
|
|
/* \ |
709 |
|
|
* Delete any appended cursor. It's possible to get in \ |
710 |
|
|
* situations where TXT_APPENDEOL is set but tp->insert \ |
711 |
|
|
* is 0 when using the R command and all the characters \ |
712 |
|
|
* are tp->owrite characters. \ |
713 |
|
|
*/ \ |
714 |
|
|
if (LF_ISSET(TXT_APPENDEOL) && tp->insert > 0) { \ |
715 |
|
|
--tp->len; \ |
716 |
|
|
--tp->insert; \ |
717 |
|
|
} \ |
718 |
|
|
} |
719 |
|
|
LINE_RESOLVE; |
720 |
|
|
|
721 |
|
|
/* |
722 |
|
|
* Save the current line information for restoration in |
723 |
|
|
* txt_backup(), and set the line final length. |
724 |
|
|
*/ |
725 |
|
|
tp->sv_len = tp->len; |
726 |
|
|
tp->sv_cno = tp->cno; |
727 |
|
|
tp->len = tp->cno; |
728 |
|
|
|
729 |
|
|
/* Update the old line. */ |
730 |
|
|
if (vs_change(sp, tp->lno, LINE_RESET)) |
731 |
|
|
goto err; |
732 |
|
|
|
733 |
|
|
/* |
734 |
|
|
* Historic practice, when the autoindent edit option was set, |
735 |
|
|
* was to delete <blank> characters following the inserted |
736 |
|
|
* newline. This affected the 'R', 'c', and 's' commands; 'c' |
737 |
|
|
* and 's' retained the insert characters only, 'R' moved the |
738 |
|
|
* overwrite and insert characters into the next TEXT structure. |
739 |
|
|
* We keep track of the number of characters erased for the 'R' |
740 |
|
|
* command so that the final resolution of the line is correct. |
741 |
|
|
*/ |
742 |
|
|
tp->R_erase = 0; |
743 |
|
|
owrite = tp->owrite; |
744 |
|
|
insert = tp->insert; |
745 |
|
|
if (LF_ISSET(TXT_REPLACE) && owrite != 0) { |
746 |
|
|
for (p = tp->lb + tp->cno; owrite > 0 && isblank(*p); |
747 |
|
|
++p, --owrite, ++tp->R_erase); |
748 |
|
|
if (owrite == 0) |
749 |
|
|
for (; insert > 0 && isblank(*p); |
750 |
|
|
++p, ++tp->R_erase, --insert); |
751 |
|
|
} else { |
752 |
|
|
p = tp->lb + tp->cno + owrite; |
753 |
|
|
if (O_ISSET(sp, O_AUTOINDENT)) |
754 |
|
|
for (; insert > 0 && |
755 |
|
|
isblank(*p); ++p, --insert); |
756 |
|
|
owrite = 0; |
757 |
|
|
} |
758 |
|
|
|
759 |
|
|
/* |
760 |
|
|
* !!! |
761 |
|
|
* Create a new line and insert the new TEXT into the queue. |
762 |
|
|
* DON'T insert until the old line has been updated, or the |
763 |
|
|
* inserted line count in line.c:db_get() will be wrong. |
764 |
|
|
*/ |
765 |
|
|
if ((ntp = text_init(sp, p, |
766 |
|
|
insert + owrite, insert + owrite + 32)) == NULL) |
767 |
|
|
goto err; |
768 |
|
|
TAILQ_INSERT_TAIL(&sp->tiq, ntp, q); |
769 |
|
|
|
770 |
|
|
/* Set up bookkeeping for the new line. */ |
771 |
|
|
ntp->insert = insert; |
772 |
|
|
ntp->owrite = owrite; |
773 |
|
|
ntp->lno = tp->lno + 1; |
774 |
|
|
|
775 |
|
|
/* |
776 |
|
|
* Reset the autoindent line value. 0^D keeps the autoindent |
777 |
|
|
* line from changing, ^D changes the level, even if there were |
778 |
|
|
* no characters in the old line. Note, if using the current |
779 |
|
|
* tp structure, use the cursor as the length, the autoindent |
780 |
|
|
* characters may have been erased. |
781 |
|
|
*/ |
782 |
|
|
if (LF_ISSET(TXT_AUTOINDENT)) { |
783 |
|
|
if (carat == C_NOCHANGE) { |
784 |
|
|
if (v_txt_auto(sp, OOBLNO, &ait, ait.ai, ntp)) |
785 |
|
|
goto err; |
786 |
|
|
FREE_SPACE(sp, ait.lb, ait.lb_len); |
787 |
|
|
} else |
788 |
|
|
if (v_txt_auto(sp, OOBLNO, tp, tp->cno, ntp)) |
789 |
|
|
goto err; |
790 |
|
|
carat = C_NOTSET; |
791 |
|
|
} |
792 |
|
|
|
793 |
|
|
/* Reset the cursor. */ |
794 |
|
|
ntp->cno = ntp->ai; |
795 |
|
|
|
796 |
|
|
/* |
797 |
|
|
* If we're here because wrapmargin was set and we've broken a |
798 |
|
|
* line, there may be additional information (i.e. the start of |
799 |
|
|
* a line) in the wmt structure. |
800 |
|
|
*/ |
801 |
|
|
if (wm_set) { |
802 |
|
|
if (wmt.offset != 0 || |
803 |
|
|
wmt.owrite != 0 || wmt.insert != 0) { |
804 |
|
|
#define WMTSPACE wmt.offset + wmt.owrite + wmt.insert |
805 |
|
|
BINC_GOTO(sp, ntp->lb, |
806 |
|
|
ntp->lb_len, ntp->len + WMTSPACE + 32); |
807 |
|
|
memmove(ntp->lb + ntp->cno, wmt.lb, WMTSPACE); |
808 |
|
|
ntp->len += WMTSPACE; |
809 |
|
|
ntp->cno += wmt.offset; |
810 |
|
|
ntp->owrite = wmt.owrite; |
811 |
|
|
ntp->insert = wmt.insert; |
812 |
|
|
} |
813 |
|
|
wm_set = 0; |
814 |
|
|
} |
815 |
|
|
|
816 |
|
|
/* New lines are TXT_APPENDEOL. */ |
817 |
|
|
if (ntp->owrite == 0 && ntp->insert == 0) { |
818 |
|
|
BINC_GOTO(sp, ntp->lb, ntp->lb_len, ntp->len + 1); |
819 |
|
|
LF_SET(TXT_APPENDEOL); |
820 |
|
|
ntp->lb[ntp->cno] = CH_CURSOR; |
821 |
|
|
++ntp->insert; |
822 |
|
|
++ntp->len; |
823 |
|
|
} |
824 |
|
|
|
825 |
|
|
/* Swap old and new TEXT's, and update the new line. */ |
826 |
|
|
tp = ntp; |
827 |
|
|
if (vs_change(sp, tp->lno, LINE_INSERT)) |
828 |
|
|
goto err; |
829 |
|
|
|
830 |
|
|
goto resolve; |
831 |
|
|
case K_ESCAPE: /* Escape. */ |
832 |
|
|
if (!LF_ISSET(TXT_ESCAPE)) |
833 |
|
|
goto ins_ch; |
834 |
|
|
|
835 |
|
|
/* If we have a count, start replaying the input. */ |
836 |
|
|
if (rcount > 1) { |
837 |
|
|
--rcount; |
838 |
|
|
|
839 |
|
|
rcol = 0; |
840 |
|
|
abb = AB_NOTSET; |
841 |
|
|
LF_CLR(TXT_RECORD); |
842 |
|
|
LF_SET(TXT_REPLAY); |
843 |
|
|
|
844 |
|
|
/* |
845 |
|
|
* Some commands (e.g. 'o') need a <newline> for each |
846 |
|
|
* repetition. |
847 |
|
|
*/ |
848 |
|
|
if (LF_ISSET(TXT_ADDNEWLINE)) |
849 |
|
|
goto k_cr; |
850 |
|
|
|
851 |
|
|
/* |
852 |
|
|
* The R command turns into the 'a' command after the |
853 |
|
|
* first repetition. |
854 |
|
|
*/ |
855 |
|
|
if (LF_ISSET(TXT_REPLACE)) { |
856 |
|
|
tp->insert = tp->owrite; |
857 |
|
|
tp->owrite = 0; |
858 |
|
|
LF_CLR(TXT_REPLACE); |
859 |
|
|
} |
860 |
|
|
goto replay; |
861 |
|
|
} |
862 |
|
|
|
863 |
|
|
/* Set term condition: if empty. */ |
864 |
|
|
if (tp->cno <= tp->offset) |
865 |
|
|
tp->term = TERM_ESC; |
866 |
|
|
/* |
867 |
|
|
* Set term condition: if searching incrementally and the user |
868 |
|
|
* entered a pattern, return a completed search, regardless if |
869 |
|
|
* the entire pattern was found. |
870 |
|
|
*/ |
871 |
|
|
if (FL_ISSET(is_flags, IS_RUNNING) && tp->cno >= tp->offset + 1) |
872 |
|
|
tp->term = TERM_SEARCH; |
873 |
|
|
|
874 |
|
|
k_escape: LINE_RESOLVE; |
875 |
|
|
|
876 |
|
|
/* |
877 |
|
|
* Clean up for the 'R' command, restoring overwrite |
878 |
|
|
* characters, and making them into insert characters. |
879 |
|
|
*/ |
880 |
|
|
if (LF_ISSET(TXT_REPLACE)) |
881 |
|
|
txt_Rresolve(sp, &sp->tiq, tp, len); |
882 |
|
|
|
883 |
|
|
/* |
884 |
|
|
* If there are any overwrite characters, copy down |
885 |
|
|
* any insert characters, and decrement the length. |
886 |
|
|
*/ |
887 |
|
|
if (tp->owrite) { |
888 |
|
|
if (tp->insert) |
889 |
|
|
memmove(tp->lb + tp->cno, |
890 |
|
|
tp->lb + tp->cno + tp->owrite, tp->insert); |
891 |
|
|
tp->len -= tp->owrite; |
892 |
|
|
} |
893 |
|
|
|
894 |
|
|
/* |
895 |
|
|
* Optionally resolve the lines into the file. If not |
896 |
|
|
* resolving the lines into the file, end the line with |
897 |
|
|
* a nul. If the line is empty, then set the length to |
898 |
|
|
* 0, the termination condition has already been set. |
899 |
|
|
* |
900 |
|
|
* XXX |
901 |
|
|
* This is wrong, should pass back a length. |
902 |
|
|
*/ |
903 |
|
|
if (LF_ISSET(TXT_RESOLVE)) { |
904 |
|
|
if (txt_resolve(sp, &sp->tiq, flags)) |
905 |
|
|
goto err; |
906 |
|
|
} else { |
907 |
|
|
BINC_GOTO(sp, tp->lb, tp->lb_len, tp->len + 1); |
908 |
|
|
tp->lb[tp->len] = '\0'; |
909 |
|
|
} |
910 |
|
|
|
911 |
|
|
/* |
912 |
|
|
* Set the return cursor position to rest on the last |
913 |
|
|
* inserted character. |
914 |
|
|
*/ |
915 |
|
|
if (tp->cno != 0) |
916 |
|
|
--tp->cno; |
917 |
|
|
|
918 |
|
|
/* Update the last line. */ |
919 |
|
|
if (vs_change(sp, tp->lno, LINE_RESET)) |
920 |
|
|
return (1); |
921 |
|
|
goto done; |
922 |
|
|
case K_CARAT: /* Delete autoindent chars. */ |
923 |
|
|
if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT)) |
924 |
|
|
carat = C_CARATSET; |
925 |
|
|
goto ins_ch; |
926 |
|
|
case K_ZERO: /* Delete autoindent chars. */ |
927 |
|
|
if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT)) |
928 |
|
|
carat = C_ZEROSET; |
929 |
|
|
goto ins_ch; |
930 |
|
|
case K_CNTRLD: /* Delete autoindent char. */ |
931 |
|
|
/* |
932 |
|
|
* If in the first column or no characters to erase, ignore |
933 |
|
|
* the ^D (this matches historic practice). If not doing |
934 |
|
|
* autoindent or already inserted non-ai characters, it's a |
935 |
|
|
* literal. The latter test is done in the switch, as the |
936 |
|
|
* CARAT forms are N + 1, not N. |
937 |
|
|
*/ |
938 |
|
|
if (!LF_ISSET(TXT_AUTOINDENT)) |
939 |
|
|
goto ins_ch; |
940 |
|
|
if (tp->cno == 0) |
941 |
|
|
goto resolve; |
942 |
|
|
|
943 |
|
|
switch (carat) { |
944 |
|
|
case C_CARATSET: /* ^^D */ |
945 |
|
|
if (tp->ai == 0 || tp->cno > tp->ai + tp->offset + 1) |
946 |
|
|
goto ins_ch; |
947 |
|
|
|
948 |
|
|
/* Save the ai string for later. */ |
949 |
|
|
ait.lb = NULL; |
950 |
|
|
ait.lb_len = 0; |
951 |
|
|
BINC_GOTO(sp, ait.lb, ait.lb_len, tp->ai); |
952 |
|
|
memmove(ait.lb, tp->lb, tp->ai); |
953 |
|
|
ait.ai = ait.len = tp->ai; |
954 |
|
|
|
955 |
|
|
carat = C_NOCHANGE; |
956 |
|
|
goto leftmargin; |
957 |
|
|
case C_ZEROSET: /* 0^D */ |
958 |
|
|
if (tp->ai == 0 || tp->cno > tp->ai + tp->offset + 1) |
959 |
|
|
goto ins_ch; |
960 |
|
|
|
961 |
|
|
carat = C_NOTSET; |
962 |
|
|
leftmargin: tp->lb[tp->cno - 1] = ' '; |
963 |
|
|
tp->owrite += tp->cno - tp->offset; |
964 |
|
|
tp->ai = 0; |
965 |
|
|
tp->cno = tp->offset; |
966 |
|
|
break; |
967 |
|
|
case C_NOTSET: /* ^D */ |
968 |
|
|
if (tp->ai == 0 || tp->cno > tp->ai + tp->offset) |
969 |
|
|
goto ins_ch; |
970 |
|
|
|
971 |
|
|
(void)txt_dent(sp, tp, 0); |
972 |
|
|
break; |
973 |
|
|
default: |
974 |
|
|
abort(); |
975 |
|
|
} |
976 |
|
|
break; |
977 |
|
|
case K_VERASE: /* Erase the last character. */ |
978 |
|
|
/* If can erase over the prompt, return. */ |
979 |
|
|
if (tp->cno <= tp->offset && LF_ISSET(TXT_BS)) { |
980 |
|
|
tp->term = TERM_BS; |
981 |
|
|
goto done; |
982 |
|
|
} |
983 |
|
|
|
984 |
|
|
/* |
985 |
|
|
* If at the beginning of the line, try and drop back to a |
986 |
|
|
* previously inserted line. |
987 |
|
|
*/ |
988 |
|
|
if (tp->cno == 0) { |
989 |
|
|
if ((ntp = |
990 |
|
|
txt_backup(sp, &sp->tiq, tp, &flags)) == NULL) |
991 |
|
|
goto err; |
992 |
|
|
tp = ntp; |
993 |
|
|
break; |
994 |
|
|
} |
995 |
|
|
|
996 |
|
|
/* If nothing to erase, bell the user. */ |
997 |
|
|
if (tp->cno <= tp->offset) { |
998 |
|
|
if (!LF_ISSET(TXT_REPLAY)) |
999 |
|
|
txt_nomorech(sp); |
1000 |
|
|
break; |
1001 |
|
|
} |
1002 |
|
|
|
1003 |
|
|
/* Drop back one character. */ |
1004 |
|
|
--tp->cno; |
1005 |
|
|
|
1006 |
|
|
/* |
1007 |
|
|
* Historically, vi didn't replace the erased characters with |
1008 |
|
|
* <blank>s, presumably because it's easier to fix a minor |
1009 |
|
|
* typing mistake and continue on if the previous letters are |
1010 |
|
|
* already there. This is a problem for incremental searching, |
1011 |
|
|
* because the user can no longer tell where they are in the |
1012 |
|
|
* colon command line because the cursor is at the last search |
1013 |
|
|
* point in the screen. So, if incrementally searching, erase |
1014 |
|
|
* the erased characters from the screen. |
1015 |
|
|
*/ |
1016 |
|
|
if (FL_ISSET(is_flags, IS_RUNNING)) |
1017 |
|
|
tp->lb[tp->cno] = ' '; |
1018 |
|
|
|
1019 |
|
|
/* |
1020 |
|
|
* Increment overwrite, decrement ai if deleted. |
1021 |
|
|
* |
1022 |
|
|
* !!! |
1023 |
|
|
* Historic vi did not permit users to use erase characters |
1024 |
|
|
* to delete autoindent characters. We do. Eat hot death, |
1025 |
|
|
* POSIX. |
1026 |
|
|
*/ |
1027 |
|
|
++tp->owrite; |
1028 |
|
|
if (tp->cno < tp->ai) |
1029 |
|
|
--tp->ai; |
1030 |
|
|
|
1031 |
|
|
/* Reset if we deleted an incremental search character. */ |
1032 |
|
|
if (FL_ISSET(is_flags, IS_RUNNING)) |
1033 |
|
|
FL_SET(is_flags, IS_RESTART); |
1034 |
|
|
break; |
1035 |
|
|
case K_VWERASE: /* Skip back one word. */ |
1036 |
|
|
/* |
1037 |
|
|
* If at the beginning of the line, try and drop back to a |
1038 |
|
|
* previously inserted line. |
1039 |
|
|
*/ |
1040 |
|
|
if (tp->cno == 0) { |
1041 |
|
|
if ((ntp = |
1042 |
|
|
txt_backup(sp, &sp->tiq, tp, &flags)) == NULL) |
1043 |
|
|
goto err; |
1044 |
|
|
tp = ntp; |
1045 |
|
|
} |
1046 |
|
|
|
1047 |
|
|
/* |
1048 |
|
|
* If at offset, nothing to erase so bell the user. |
1049 |
|
|
*/ |
1050 |
|
|
if (tp->cno <= tp->offset) { |
1051 |
|
|
if (!LF_ISSET(TXT_REPLAY)) |
1052 |
|
|
txt_nomorech(sp); |
1053 |
|
|
break; |
1054 |
|
|
} |
1055 |
|
|
|
1056 |
|
|
/* |
1057 |
|
|
* The first werase goes back to any autoindent column and the |
1058 |
|
|
* second werase goes back to the offset. |
1059 |
|
|
* |
1060 |
|
|
* !!! |
1061 |
|
|
* Historic vi did not permit users to use erase characters to |
1062 |
|
|
* delete autoindent characters. |
1063 |
|
|
*/ |
1064 |
|
|
if (tp->ai && tp->cno > tp->ai) |
1065 |
|
|
max = tp->ai; |
1066 |
|
|
else { |
1067 |
|
|
tp->ai = 0; |
1068 |
|
|
max = tp->offset; |
1069 |
|
|
} |
1070 |
|
|
|
1071 |
|
|
/* Skip over trailing space characters. */ |
1072 |
|
|
while (tp->cno > max && isblank(tp->lb[tp->cno - 1])) { |
1073 |
|
|
--tp->cno; |
1074 |
|
|
++tp->owrite; |
1075 |
|
|
} |
1076 |
|
|
if (tp->cno == max) |
1077 |
|
|
break; |
1078 |
|
|
/* |
1079 |
|
|
* There are three types of word erase found on UNIX systems. |
1080 |
|
|
* They can be identified by how the string /a/b/c is treated |
1081 |
|
|
* -- as 1, 3, or 6 words. Historic vi had two classes of |
1082 |
|
|
* characters, and strings were delimited by them and |
1083 |
|
|
* <blank>'s, so, 6 words. The historic tty interface used |
1084 |
|
|
* <blank>'s to delimit strings, so, 1 word. The algorithm |
1085 |
|
|
* offered in the 4.4BSD tty interface (as stty altwerase) |
1086 |
|
|
* treats it as 3 words -- there are two classes of |
1087 |
|
|
* characters, and strings are delimited by them and |
1088 |
|
|
* <blank>'s. The difference is that the type of the first |
1089 |
|
|
* erased character erased is ignored, which is exactly right |
1090 |
|
|
* when erasing pathname components. The edit options |
1091 |
|
|
* TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD tty |
1092 |
|
|
* interface and the historic tty driver behavior, |
1093 |
|
|
* respectively, and the default is the same as the historic |
1094 |
|
|
* vi behavior. |
1095 |
|
|
* |
1096 |
|
|
* Overwrite erased characters if doing incremental search; |
1097 |
|
|
* see comment above. |
1098 |
|
|
*/ |
1099 |
|
|
if (LF_ISSET(TXT_TTYWERASE)) |
1100 |
|
|
while (tp->cno > max) { |
1101 |
|
|
if (isblank(tp->lb[tp->cno - 1])) |
1102 |
|
|
break; |
1103 |
|
|
--tp->cno; |
1104 |
|
|
++tp->owrite; |
1105 |
|
|
if (FL_ISSET(is_flags, IS_RUNNING)) |
1106 |
|
|
tp->lb[tp->cno] = ' '; |
1107 |
|
|
} |
1108 |
|
|
else { |
1109 |
|
|
if (LF_ISSET(TXT_ALTWERASE)) { |
1110 |
|
|
--tp->cno; |
1111 |
|
|
++tp->owrite; |
1112 |
|
|
if (FL_ISSET(is_flags, IS_RUNNING)) |
1113 |
|
|
tp->lb[tp->cno] = ' '; |
1114 |
|
|
} |
1115 |
|
|
if (tp->cno > max) |
1116 |
|
|
tmp = inword(tp->lb[tp->cno - 1]); |
1117 |
|
|
while (tp->cno > max) { |
1118 |
|
|
if (tmp != inword(tp->lb[tp->cno - 1]) |
1119 |
|
|
|| isblank(tp->lb[tp->cno - 1])) |
1120 |
|
|
break; |
1121 |
|
|
--tp->cno; |
1122 |
|
|
++tp->owrite; |
1123 |
|
|
if (FL_ISSET(is_flags, IS_RUNNING)) |
1124 |
|
|
tp->lb[tp->cno] = ' '; |
1125 |
|
|
} |
1126 |
|
|
} |
1127 |
|
|
|
1128 |
|
|
/* Reset if we deleted an incremental search character. */ |
1129 |
|
|
if (FL_ISSET(is_flags, IS_RUNNING)) |
1130 |
|
|
FL_SET(is_flags, IS_RESTART); |
1131 |
|
|
break; |
1132 |
|
|
case K_VKILL: /* Restart this line. */ |
1133 |
|
|
/* |
1134 |
|
|
* !!! |
1135 |
|
|
* If at the beginning of the line, try and drop back to a |
1136 |
|
|
* previously inserted line. Historic vi did not permit |
1137 |
|
|
* users to go back to previous lines. |
1138 |
|
|
*/ |
1139 |
|
|
if (tp->cno == 0) { |
1140 |
|
|
if ((ntp = |
1141 |
|
|
txt_backup(sp, &sp->tiq, tp, &flags)) == NULL) |
1142 |
|
|
goto err; |
1143 |
|
|
tp = ntp; |
1144 |
|
|
} |
1145 |
|
|
|
1146 |
|
|
/* If at offset, nothing to erase so bell the user. */ |
1147 |
|
|
if (tp->cno <= tp->offset) { |
1148 |
|
|
if (!LF_ISSET(TXT_REPLAY)) |
1149 |
|
|
txt_nomorech(sp); |
1150 |
|
|
break; |
1151 |
|
|
} |
1152 |
|
|
|
1153 |
|
|
/* |
1154 |
|
|
* First kill goes back to any autoindent and second kill goes |
1155 |
|
|
* back to the offset. |
1156 |
|
|
* |
1157 |
|
|
* !!! |
1158 |
|
|
* Historic vi did not permit users to use erase characters to |
1159 |
|
|
* delete autoindent characters. |
1160 |
|
|
*/ |
1161 |
|
|
if (tp->ai && tp->cno > tp->ai) |
1162 |
|
|
max = tp->ai; |
1163 |
|
|
else { |
1164 |
|
|
tp->ai = 0; |
1165 |
|
|
max = tp->offset; |
1166 |
|
|
} |
1167 |
|
|
tp->owrite += tp->cno - max; |
1168 |
|
|
|
1169 |
|
|
/* |
1170 |
|
|
* Overwrite erased characters if doing incremental search; |
1171 |
|
|
* see comment above. |
1172 |
|
|
*/ |
1173 |
|
|
if (FL_ISSET(is_flags, IS_RUNNING)) |
1174 |
|
|
do { |
1175 |
|
|
tp->lb[--tp->cno] = ' '; |
1176 |
|
|
} while (tp->cno > max); |
1177 |
|
|
else |
1178 |
|
|
tp->cno = max; |
1179 |
|
|
|
1180 |
|
|
/* Reset if we deleted an incremental search character. */ |
1181 |
|
|
if (FL_ISSET(is_flags, IS_RUNNING)) |
1182 |
|
|
FL_SET(is_flags, IS_RESTART); |
1183 |
|
|
break; |
1184 |
|
|
case K_CNTRLT: /* Add autoindent characters. */ |
1185 |
|
|
if (!LF_ISSET(TXT_CNTRLT)) |
1186 |
|
|
goto ins_ch; |
1187 |
|
|
if (txt_dent(sp, tp, 1)) |
1188 |
|
|
goto err; |
1189 |
|
|
goto ebuf_chk; |
1190 |
|
|
case K_RIGHTBRACE: |
1191 |
|
|
case K_RIGHTPAREN: |
1192 |
|
|
if (LF_ISSET(TXT_SHOWMATCH)) |
1193 |
|
|
showmatch = 1; |
1194 |
|
|
goto ins_ch; |
1195 |
|
|
case K_VLNEXT: /* Quote next character. */ |
1196 |
|
|
evp->e_c = '^'; |
1197 |
|
|
quote = Q_VNEXT; |
1198 |
|
|
/* |
1199 |
|
|
* Turn on the quote flag so that the underlying routines |
1200 |
|
|
* quote the next character where it's possible. Turn off |
1201 |
|
|
* the input mapbiting flag so that we don't remap the next |
1202 |
|
|
* character. |
1203 |
|
|
*/ |
1204 |
|
|
FL_SET(ec_flags, EC_QUOTED); |
1205 |
|
|
FL_CLR(ec_flags, EC_MAPINPUT); |
1206 |
|
|
|
1207 |
|
|
/* |
1208 |
|
|
* !!! |
1209 |
|
|
* Skip the tests for abbreviations, so ":ab xa XA", |
1210 |
|
|
* "ixa^V<space>" doesn't perform the abbreviation. |
1211 |
|
|
*/ |
1212 |
|
|
goto insl_ch; |
1213 |
|
|
case K_HEXCHAR: |
1214 |
|
|
hexcnt = 1; |
1215 |
|
|
goto insq_ch; |
1216 |
|
|
default: /* Insert the character. */ |
1217 |
|
|
ins_ch: /* |
1218 |
|
|
* Historically, vi eliminated nul's out of hand. If the |
1219 |
|
|
* beautify option was set, it also deleted any unknown |
1220 |
|
|
* ASCII value less than space (040) and the del character |
1221 |
|
|
* (0177), except for tabs. Unknown is a key word here. |
1222 |
|
|
* Most vi documentation claims that it deleted everything |
1223 |
|
|
* but <tab>, <nl> and <ff>, as that's what the original |
1224 |
|
|
* 4BSD documentation said. This is obviously wrong, |
1225 |
|
|
* however, as <esc> would be included in that list. What |
1226 |
|
|
* we do is eliminate any unquoted, iscntrl() character that |
1227 |
|
|
* wasn't a replay and wasn't handled specially, except |
1228 |
|
|
* <tab> or <ff>. |
1229 |
|
|
*/ |
1230 |
|
|
if (LF_ISSET(TXT_BEAUTIFY) && iscntrl(evp->e_c) && |
1231 |
|
|
evp->e_value != K_FORMFEED && evp->e_value != K_TAB) { |
1232 |
|
|
msgq(sp, M_BERR, |
1233 |
|
|
"Illegal character; quote to enter"); |
1234 |
|
|
if (LF_ISSET(TXT_REPLAY)) |
1235 |
|
|
goto done; |
1236 |
|
|
break; |
1237 |
|
|
} |
1238 |
|
|
|
1239 |
|
|
insq_ch: /* |
1240 |
|
|
* If entering a non-word character after a word, check for |
1241 |
|
|
* abbreviations. If there was one, discard replay characters. |
1242 |
|
|
* If entering a blank character, check for unmap commands, |
1243 |
|
|
* as well. |
1244 |
|
|
*/ |
1245 |
|
|
if (!inword(evp->e_c)) { |
1246 |
|
|
if (abb == AB_INWORD && |
1247 |
|
|
!LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) { |
1248 |
|
|
if (txt_abbrev(sp, tp, &evp->e_c, |
1249 |
|
|
LF_ISSET(TXT_INFOLINE), &tmp, &ab_turnoff)) |
1250 |
|
|
goto err; |
1251 |
|
|
if (tmp) { |
1252 |
|
|
if (LF_ISSET(TXT_RECORD)) |
1253 |
|
|
rcol -= tmp + 1; |
1254 |
|
|
goto resolve; |
1255 |
|
|
} |
1256 |
|
|
} |
1257 |
|
|
if (isblank(evp->e_c) && UNMAP_TST) |
1258 |
|
|
txt_unmap(sp, tp, &ec_flags); |
1259 |
|
|
} |
1260 |
|
|
if (abb != AB_NOTSET) |
1261 |
|
|
abb = inword(evp->e_c) ? AB_INWORD : AB_NOTWORD; |
1262 |
|
|
|
1263 |
|
|
insl_ch: if (txt_insch(sp, tp, &evp->e_c, flags)) |
1264 |
|
|
goto err; |
1265 |
|
|
|
1266 |
|
|
/* |
1267 |
|
|
* If we're using K_VLNEXT to quote the next character, then |
1268 |
|
|
* we want the cursor to position itself on the ^ placeholder |
1269 |
|
|
* we're displaying, to match historic practice. |
1270 |
|
|
*/ |
1271 |
|
|
if (quote == Q_VNEXT) { |
1272 |
|
|
--tp->cno; |
1273 |
|
|
++tp->owrite; |
1274 |
|
|
} |
1275 |
|
|
|
1276 |
|
|
/* |
1277 |
|
|
* !!! |
1278 |
|
|
* Translate "<CH_HEX>[isxdigit()]*" to a character with |
1279 |
|
|
* a hex value: this test delimits the value by the max |
1280 |
|
|
* number of hex bytes. Offset by one, we use 0 to mean |
1281 |
|
|
* that we've found <CH_HEX>. |
1282 |
|
|
*/ |
1283 |
|
|
if (hexcnt != 0 && hexcnt++ == sizeof(CHAR_T) * 2 + 1) { |
1284 |
|
|
hexcnt = 0; |
1285 |
|
|
if (txt_hex(sp, tp)) |
1286 |
|
|
goto err; |
1287 |
|
|
} |
1288 |
|
|
|
1289 |
|
|
/* |
1290 |
|
|
* Check to see if we've crossed the margin. |
1291 |
|
|
* |
1292 |
|
|
* !!! |
1293 |
|
|
* In the historic vi, the wrapmargin value was figured out |
1294 |
|
|
* using the display widths of the characters, i.e. <tab> |
1295 |
|
|
* characters were counted as two characters if the list edit |
1296 |
|
|
* option is set, but as the tabstop edit option number of |
1297 |
|
|
* characters otherwise. That's what the vs_column() function |
1298 |
|
|
* gives us, so we use it. |
1299 |
|
|
*/ |
1300 |
|
|
if (margin != 0) { |
1301 |
|
|
if (vs_column(sp, &tcol)) |
1302 |
|
|
goto err; |
1303 |
|
|
if (tcol >= margin) { |
1304 |
|
|
if (txt_margin(sp, tp, &wmt, &tmp, flags)) |
1305 |
|
|
goto err; |
1306 |
|
|
if (tmp) { |
1307 |
|
|
if (isblank(evp->e_c)) |
1308 |
|
|
wm_skip = 1; |
1309 |
|
|
wm_set = 1; |
1310 |
|
|
goto k_cr; |
1311 |
|
|
} |
1312 |
|
|
} |
1313 |
|
|
} |
1314 |
|
|
|
1315 |
|
|
/* |
1316 |
|
|
* If we've reached the end of the buffer, then we need to |
1317 |
|
|
* switch into insert mode. This happens when there's a |
1318 |
|
|
* change to a mark and the user puts in more characters than |
1319 |
|
|
* the length of the motion. |
1320 |
|
|
*/ |
1321 |
|
|
ebuf_chk: if (tp->cno >= tp->len) { |
1322 |
|
|
BINC_GOTO(sp, tp->lb, tp->lb_len, tp->len + 1); |
1323 |
|
|
LF_SET(TXT_APPENDEOL); |
1324 |
|
|
|
1325 |
|
|
tp->lb[tp->cno] = CH_CURSOR; |
1326 |
|
|
++tp->insert; |
1327 |
|
|
++tp->len; |
1328 |
|
|
} |
1329 |
|
|
|
1330 |
|
|
/* Step the quote state forward. */ |
1331 |
|
|
if (quote != Q_NOTSET) { |
1332 |
|
|
if (quote == Q_VNEXT) |
1333 |
|
|
quote = Q_VTHIS; |
1334 |
|
|
} |
1335 |
|
|
break; |
1336 |
|
|
} |
1337 |
|
|
|
1338 |
|
|
#ifdef DEBUG |
1339 |
|
|
if (tp->cno + tp->insert + tp->owrite != tp->len) { |
1340 |
|
|
msgq(sp, M_ERR, |
1341 |
|
|
"len %u != cno: %u ai: %u insert %u overwrite %u", |
1342 |
|
|
tp->len, tp->cno, tp->ai, tp->insert, tp->owrite); |
1343 |
|
|
if (LF_ISSET(TXT_REPLAY)) |
1344 |
|
|
goto done; |
1345 |
|
|
tp->len = tp->cno + tp->insert + tp->owrite; |
1346 |
|
|
} |
1347 |
|
|
#endif |
1348 |
|
|
|
1349 |
|
|
resolve:/* |
1350 |
|
|
* 1: If we don't need to know where the cursor really is and we're |
1351 |
|
|
* replaying text, keep going. |
1352 |
|
|
*/ |
1353 |
|
|
if (margin == 0 && LF_ISSET(TXT_REPLAY)) |
1354 |
|
|
goto replay; |
1355 |
|
|
|
1356 |
|
|
/* |
1357 |
|
|
* 2: Reset the line. Don't bother unless we're about to wait on |
1358 |
|
|
* a character or we need to know where the cursor really is. |
1359 |
|
|
* We have to do this before showing matching characters so the |
1360 |
|
|
* user can see what they're matching. |
1361 |
|
|
*/ |
1362 |
|
|
if ((margin != 0 || !KEYS_WAITING(sp)) && |
1363 |
|
|
vs_change(sp, tp->lno, LINE_RESET)) |
1364 |
|
|
return (1); |
1365 |
|
|
|
1366 |
|
|
/* |
1367 |
|
|
* 3: If there aren't keys waiting, display the matching character. |
1368 |
|
|
* We have to do this before resolving any messages, otherwise |
1369 |
|
|
* the error message from a missing match won't appear correctly. |
1370 |
|
|
*/ |
1371 |
|
|
if (showmatch) { |
1372 |
|
|
if (!KEYS_WAITING(sp) && txt_showmatch(sp, tp)) |
1373 |
|
|
return (1); |
1374 |
|
|
showmatch = 0; |
1375 |
|
|
} |
1376 |
|
|
|
1377 |
|
|
/* |
1378 |
|
|
* 4: If there have been messages and we're not editing on the colon |
1379 |
|
|
* command line or doing file name completion, resolve them. |
1380 |
|
|
*/ |
1381 |
|
|
if ((vip->totalcount != 0 || F_ISSET(gp, G_BELLSCHED)) && |
1382 |
|
|
!F_ISSET(sp, SC_TINPUT_INFO) && !filec_redraw && |
1383 |
|
|
vs_resolve(sp, NULL, 0)) |
1384 |
|
|
return (1); |
1385 |
|
|
|
1386 |
|
|
/* |
1387 |
|
|
* 5: Refresh the screen if we're about to wait on a character or we |
1388 |
|
|
* need to know where the cursor really is. |
1389 |
|
|
*/ |
1390 |
|
|
if (margin != 0 || !KEYS_WAITING(sp)) { |
1391 |
|
|
UPDATE_POSITION(sp, tp); |
1392 |
|
|
if (vs_refresh(sp, margin != 0)) |
1393 |
|
|
return (1); |
1394 |
|
|
} |
1395 |
|
|
|
1396 |
|
|
/* 6: Proceed with the incremental search. */ |
1397 |
|
|
if (FL_ISSET(is_flags, IS_RUNNING) && txt_isrch(sp, vp, tp, &is_flags)) |
1398 |
|
|
return (1); |
1399 |
|
|
|
1400 |
|
|
/* 7: Next character... */ |
1401 |
|
|
if (LF_ISSET(TXT_REPLAY)) |
1402 |
|
|
goto replay; |
1403 |
|
|
goto next; |
1404 |
|
|
|
1405 |
|
|
done: /* Leave input mode. */ |
1406 |
|
|
F_CLR(sp, SC_TINPUT); |
1407 |
|
|
|
1408 |
|
|
/* If recording for playback, save it. */ |
1409 |
|
|
if (LF_ISSET(TXT_RECORD)) |
1410 |
|
|
vip->rep_cnt = rcol; |
1411 |
|
|
|
1412 |
|
|
/* |
1413 |
|
|
* If not working on the colon command line, set the final cursor |
1414 |
|
|
* position. |
1415 |
|
|
*/ |
1416 |
|
|
if (!F_ISSET(sp, SC_TINPUT_INFO)) { |
1417 |
|
|
vp->m_final.lno = tp->lno; |
1418 |
|
|
vp->m_final.cno = tp->cno; |
1419 |
|
|
} |
1420 |
|
|
return (0); |
1421 |
|
|
|
1422 |
|
|
err: |
1423 |
|
|
alloc_err: |
1424 |
|
|
F_CLR(sp, SC_TINPUT); |
1425 |
|
|
txt_err(sp, &sp->tiq); |
1426 |
|
|
return (1); |
1427 |
|
|
} |
1428 |
|
|
|
1429 |
|
|
/* |
1430 |
|
|
* txt_abbrev -- |
1431 |
|
|
* Handle abbreviations. |
1432 |
|
|
*/ |
1433 |
|
|
static int |
1434 |
|
|
txt_abbrev(SCR *sp, TEXT *tp, CHAR_T *pushcp, int isinfoline, int *didsubp, |
1435 |
|
|
int *turnoffp) |
1436 |
|
|
{ |
1437 |
|
|
CHAR_T ch, *p; |
1438 |
|
|
SEQ *qp; |
1439 |
|
|
size_t len, off; |
1440 |
|
|
|
1441 |
|
|
/* Check to make sure we're not at the start of an append. */ |
1442 |
|
|
*didsubp = 0; |
1443 |
|
|
if (tp->cno == tp->offset) |
1444 |
|
|
return (0); |
1445 |
|
|
|
1446 |
|
|
/* |
1447 |
|
|
* Find the start of the "word". |
1448 |
|
|
* |
1449 |
|
|
* !!! |
1450 |
|
|
* We match historic practice, which, as far as I can tell, had an |
1451 |
|
|
* off-by-one error. The way this worked was that when the inserted |
1452 |
|
|
* text switched from a "word" character to a non-word character, |
1453 |
|
|
* vi would check for possible abbreviations. It would then take the |
1454 |
|
|
* type (i.e. word/non-word) of the character entered TWO characters |
1455 |
|
|
* ago, and move backward in the text until reaching a character that |
1456 |
|
|
* was not that type, or the beginning of the insert, the line, or |
1457 |
|
|
* the file. For example, in the string "abc<space>", when the <space> |
1458 |
|
|
* character triggered the abbreviation check, the type of the 'b' |
1459 |
|
|
* character was used for moving through the string. Maybe there's a |
1460 |
|
|
* reason for not using the first (i.e. 'c') character, but I can't |
1461 |
|
|
* think of one. |
1462 |
|
|
* |
1463 |
|
|
* Terminate at the beginning of the insert or the character after the |
1464 |
|
|
* offset character -- both can be tested for using tp->offset. |
1465 |
|
|
*/ |
1466 |
|
|
off = tp->cno - 1; /* Previous character. */ |
1467 |
|
|
p = tp->lb + off; |
1468 |
|
|
len = 1; /* One character test. */ |
1469 |
|
|
if (off == tp->offset || isblank(p[-1])) |
1470 |
|
|
goto search; |
1471 |
|
|
if (inword(p[-1])) /* Move backward to change. */ |
1472 |
|
|
for (;;) { |
1473 |
|
|
--off; --p; ++len; |
1474 |
|
|
if (off == tp->offset || !inword(p[-1])) |
1475 |
|
|
break; |
1476 |
|
|
} |
1477 |
|
|
else |
1478 |
|
|
for (;;) { |
1479 |
|
|
--off; --p; ++len; |
1480 |
|
|
if (off == tp->offset || |
1481 |
|
|
inword(p[-1]) || isblank(p[-1])) |
1482 |
|
|
break; |
1483 |
|
|
} |
1484 |
|
|
|
1485 |
|
|
/* |
1486 |
|
|
* !!! |
1487 |
|
|
* Historic vi exploded abbreviations on the command line. This has |
1488 |
|
|
* obvious problems in that unabbreviating the string can be extremely |
1489 |
|
|
* tricky, particularly if the string has, say, an embedded escape |
1490 |
|
|
* character. Personally, I think it's a stunningly bad idea. Other |
1491 |
|
|
* examples of problems this caused in historic vi are: |
1492 |
|
|
* :ab foo bar |
1493 |
|
|
* :ab foo baz |
1494 |
|
|
* results in "bar" being abbreviated to "baz", which wasn't what the |
1495 |
|
|
* user had in mind at all. Also, the commands: |
1496 |
|
|
* :ab foo bar |
1497 |
|
|
* :unab foo<space> |
1498 |
|
|
* resulted in an error message that "bar" wasn't mapped. Finally, |
1499 |
|
|
* since the string was already exploded by the time the unabbreviate |
1500 |
|
|
* command got it, all it knew was that an abbreviation had occurred. |
1501 |
|
|
* Cleverly, it checked the replacement string for its unabbreviation |
1502 |
|
|
* match, which meant that the commands: |
1503 |
|
|
* :ab foo1 bar |
1504 |
|
|
* :ab foo2 bar |
1505 |
|
|
* :unab foo2 |
1506 |
|
|
* unabbreviate "foo1", and the commands: |
1507 |
|
|
* :ab foo bar |
1508 |
|
|
* :ab bar baz |
1509 |
|
|
* unabbreviate "foo"! |
1510 |
|
|
* |
1511 |
|
|
* Anyway, people neglected to first ask my opinion before they wrote |
1512 |
|
|
* macros that depend on this stuff, so, we make this work as follows. |
1513 |
|
|
* When checking for an abbreviation on the command line, if we get a |
1514 |
|
|
* string which is <blank> terminated and which starts at the beginning |
1515 |
|
|
* of the line, we check to see it is the abbreviate or unabbreviate |
1516 |
|
|
* commands. If it is, turn abbreviations off and return as if no |
1517 |
|
|
* abbreviation was found. Note also, minor trickiness, so that if |
1518 |
|
|
* the user erases the line and starts another command, we turn the |
1519 |
|
|
* abbreviations back on. |
1520 |
|
|
* |
1521 |
|
|
* This makes the layering look like a Nachos Supreme. |
1522 |
|
|
*/ |
1523 |
|
|
search: if (isinfoline) { |
1524 |
|
|
if (off == tp->ai || off == tp->offset) |
1525 |
|
|
if (ex_is_abbrev(p, len)) { |
1526 |
|
|
*turnoffp = 1; |
1527 |
|
|
return (0); |
1528 |
|
|
} else |
1529 |
|
|
*turnoffp = 0; |
1530 |
|
|
else |
1531 |
|
|
if (*turnoffp) |
1532 |
|
|
return (0); |
1533 |
|
|
} |
1534 |
|
|
|
1535 |
|
|
/* Check for any abbreviations. */ |
1536 |
|
|
if ((qp = seq_find(sp, NULL, NULL, p, len, SEQ_ABBREV, NULL)) == NULL) |
1537 |
|
|
return (0); |
1538 |
|
|
|
1539 |
|
|
/* |
1540 |
|
|
* Push the abbreviation onto the tty stack. Historically, characters |
1541 |
|
|
* resulting from an abbreviation expansion were themselves subject to |
1542 |
|
|
* map expansions, O_SHOWMATCH matching etc. This means the expanded |
1543 |
|
|
* characters will be re-tested for abbreviations. It's difficult to |
1544 |
|
|
* know what historic practice in this case was, since abbreviations |
1545 |
|
|
* were applied to :colon command lines, so entering abbreviations that |
1546 |
|
|
* looped was tricky, although possible. In addition, obvious loops |
1547 |
|
|
* didn't work as expected. (The command ':ab a b|ab b c|ab c a' will |
1548 |
|
|
* silently only implement and/or display the last abbreviation.) |
1549 |
|
|
* |
1550 |
|
|
* This implementation doesn't recover well from such abbreviations. |
1551 |
|
|
* The main input loop counts abbreviated characters, and, when it |
1552 |
|
|
* reaches a limit, discards any abbreviated characters on the queue. |
1553 |
|
|
* It's difficult to back up to the original position, as the replay |
1554 |
|
|
* queue would have to be adjusted, and the line state when an initial |
1555 |
|
|
* abbreviated character was received would have to be saved. |
1556 |
|
|
*/ |
1557 |
|
|
ch = *pushcp; |
1558 |
|
|
if (v_event_push(sp, NULL, &ch, 1, CH_ABBREVIATED)) |
1559 |
|
|
return (1); |
1560 |
|
|
if (v_event_push(sp, NULL, qp->output, qp->olen, CH_ABBREVIATED)) |
1561 |
|
|
return (1); |
1562 |
|
|
|
1563 |
|
|
/* |
1564 |
|
|
* If the size of the abbreviation is larger than or equal to the size |
1565 |
|
|
* of the original text, move to the start of the replaced characters, |
1566 |
|
|
* and add their length to the overwrite count. |
1567 |
|
|
* |
1568 |
|
|
* If the abbreviation is smaller than the original text, we have to |
1569 |
|
|
* delete the additional overwrite characters and copy down any insert |
1570 |
|
|
* characters. |
1571 |
|
|
*/ |
1572 |
|
|
tp->cno -= len; |
1573 |
|
|
if (qp->olen >= len) |
1574 |
|
|
tp->owrite += len; |
1575 |
|
|
else { |
1576 |
|
|
if (tp->insert) |
1577 |
|
|
memmove(tp->lb + tp->cno + qp->olen, |
1578 |
|
|
tp->lb + tp->cno + tp->owrite + len, tp->insert); |
1579 |
|
|
tp->owrite += qp->olen; |
1580 |
|
|
tp->len -= len - qp->olen; |
1581 |
|
|
} |
1582 |
|
|
|
1583 |
|
|
/* |
1584 |
|
|
* We return the length of the abbreviated characters. This is so |
1585 |
|
|
* the calling routine can replace the replay characters with the |
1586 |
|
|
* abbreviation. This means that subsequent '.' commands will produce |
1587 |
|
|
* the same text, regardless of intervening :[un]abbreviate commands. |
1588 |
|
|
* This is historic practice. |
1589 |
|
|
*/ |
1590 |
|
|
*didsubp = len; |
1591 |
|
|
return (0); |
1592 |
|
|
} |
1593 |
|
|
|
1594 |
|
|
/* |
1595 |
|
|
* txt_unmap -- |
1596 |
|
|
* Handle the unmap command. |
1597 |
|
|
*/ |
1598 |
|
|
static void |
1599 |
|
|
txt_unmap(SCR *sp, TEXT *tp, u_int32_t *ec_flagsp) |
1600 |
|
|
{ |
1601 |
|
|
size_t len, off; |
1602 |
|
|
char *p; |
1603 |
|
|
|
1604 |
|
|
/* Find the beginning of this "word". */ |
1605 |
|
|
for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) { |
1606 |
|
|
if (isblank(*p)) { |
1607 |
|
|
++p; |
1608 |
|
|
break; |
1609 |
|
|
} |
1610 |
|
|
++len; |
1611 |
|
|
if (off == tp->ai || off == tp->offset) |
1612 |
|
|
break; |
1613 |
|
|
} |
1614 |
|
|
|
1615 |
|
|
/* |
1616 |
|
|
* !!! |
1617 |
|
|
* Historic vi exploded input mappings on the command line. See the |
1618 |
|
|
* txt_abbrev() routine for an explanation of the problems inherent |
1619 |
|
|
* in this. |
1620 |
|
|
* |
1621 |
|
|
* We make this work as follows. If we get a string which is <blank> |
1622 |
|
|
* terminated and which starts at the beginning of the line, we check |
1623 |
|
|
* to see it is the unmap command. If it is, we return that the input |
1624 |
|
|
* mapping should be turned off. Note also, minor trickiness, so that |
1625 |
|
|
* if the user erases the line and starts another command, we go ahead |
1626 |
|
|
* an turn mapping back on. |
1627 |
|
|
*/ |
1628 |
|
|
if ((off == tp->ai || off == tp->offset) && ex_is_unmap(p, len)) |
1629 |
|
|
FL_CLR(*ec_flagsp, EC_MAPINPUT); |
1630 |
|
|
else |
1631 |
|
|
FL_SET(*ec_flagsp, EC_MAPINPUT); |
1632 |
|
|
} |
1633 |
|
|
|
1634 |
|
|
/* |
1635 |
|
|
* txt_ai_resolve -- |
1636 |
|
|
* When a line is resolved by <esc>, review autoindent characters. |
1637 |
|
|
*/ |
1638 |
|
|
static void |
1639 |
|
|
txt_ai_resolve(SCR *sp, TEXT *tp, int *changedp) |
1640 |
|
|
{ |
1641 |
|
|
u_long ts; |
1642 |
|
|
int del; |
1643 |
|
|
size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs; |
1644 |
|
|
char *p; |
1645 |
|
|
|
1646 |
|
|
*changedp = 0; |
1647 |
|
|
|
1648 |
|
|
/* |
1649 |
|
|
* If the line is empty, has an offset, or no autoindent |
1650 |
|
|
* characters, we're done. |
1651 |
|
|
*/ |
1652 |
|
|
if (!tp->len || tp->offset || !tp->ai) |
1653 |
|
|
return; |
1654 |
|
|
|
1655 |
|
|
/* |
1656 |
|
|
* If the length is less than or equal to the autoindent |
1657 |
|
|
* characters, delete them. |
1658 |
|
|
*/ |
1659 |
|
|
if (tp->len <= tp->ai) { |
1660 |
|
|
tp->ai = tp->cno = tp->len = 0; |
1661 |
|
|
return; |
1662 |
|
|
} |
1663 |
|
|
|
1664 |
|
|
/* |
1665 |
|
|
* The autoindent characters plus any leading <blank> characters |
1666 |
|
|
* in the line are resolved into the minimum number of characters. |
1667 |
|
|
* Historic practice. |
1668 |
|
|
*/ |
1669 |
|
|
ts = O_VAL(sp, O_TABSTOP); |
1670 |
|
|
|
1671 |
|
|
/* Figure out the last <blank> screen column. */ |
1672 |
|
|
for (p = tp->lb, scno = 0, len = tp->len, |
1673 |
|
|
spaces = tab_after_sp = 0; len-- && isblank(*p); ++p) |
1674 |
|
|
if (*p == '\t') { |
1675 |
|
|
if (spaces) |
1676 |
|
|
tab_after_sp = 1; |
1677 |
|
|
scno += COL_OFF(scno, ts); |
1678 |
|
|
} else { |
1679 |
|
|
++spaces; |
1680 |
|
|
++scno; |
1681 |
|
|
} |
1682 |
|
|
|
1683 |
|
|
/* |
1684 |
|
|
* If there are no spaces, or no tabs after spaces and less than |
1685 |
|
|
* ts spaces, it's already minimal. |
1686 |
|
|
*/ |
1687 |
|
|
if (!spaces || (!tab_after_sp && spaces < ts)) |
1688 |
|
|
return; |
1689 |
|
|
|
1690 |
|
|
/* Count up spaces/tabs needed to get to the target. */ |
1691 |
|
|
for (cno = 0, tabs = 0; cno + COL_OFF(cno, ts) <= scno; ++tabs) |
1692 |
|
|
cno += COL_OFF(cno, ts); |
1693 |
|
|
spaces = scno - cno; |
1694 |
|
|
|
1695 |
|
|
/* |
1696 |
|
|
* Figure out how many characters we're dropping -- if we're not |
1697 |
|
|
* dropping any, it's already minimal, we're done. |
1698 |
|
|
*/ |
1699 |
|
|
old = p - tp->lb; |
1700 |
|
|
new = spaces + tabs; |
1701 |
|
|
if (old == new) |
1702 |
|
|
return; |
1703 |
|
|
|
1704 |
|
|
/* Shift the rest of the characters down, adjust the counts. */ |
1705 |
|
|
del = old - new; |
1706 |
|
|
memmove(p - del, p, tp->len - old); |
1707 |
|
|
tp->len -= del; |
1708 |
|
|
tp->cno -= del; |
1709 |
|
|
|
1710 |
|
|
/* Fill in space/tab characters. */ |
1711 |
|
|
for (p = tp->lb; tabs--;) |
1712 |
|
|
*p++ = '\t'; |
1713 |
|
|
while (spaces--) |
1714 |
|
|
*p++ = ' '; |
1715 |
|
|
*changedp = 1; |
1716 |
|
|
} |
1717 |
|
|
|
1718 |
|
|
/* |
1719 |
|
|
* v_txt_auto -- |
1720 |
|
|
* Handle autoindent. If aitp isn't NULL, use it, otherwise, |
1721 |
|
|
* retrieve the line. |
1722 |
|
|
* |
1723 |
|
|
* PUBLIC: int v_txt_auto(SCR *, recno_t, TEXT *, size_t, TEXT *); |
1724 |
|
|
*/ |
1725 |
|
|
int |
1726 |
|
|
v_txt_auto(SCR *sp, recno_t lno, TEXT *aitp, size_t len, TEXT *tp) |
1727 |
|
|
{ |
1728 |
|
|
size_t nlen; |
1729 |
|
|
char *p, *t; |
1730 |
|
|
|
1731 |
|
|
if (aitp == NULL) { |
1732 |
|
|
/* |
1733 |
|
|
* If the ex append command is executed with an address of 0, |
1734 |
|
|
* it's possible to get here with a line number of 0. Return |
1735 |
|
|
* an indent of 0. |
1736 |
|
|
*/ |
1737 |
|
|
if (lno == 0) { |
1738 |
|
|
tp->ai = 0; |
1739 |
|
|
return (0); |
1740 |
|
|
} |
1741 |
|
|
if (db_get(sp, lno, DBG_FATAL, &t, &len)) |
1742 |
|
|
return (1); |
1743 |
|
|
} else |
1744 |
|
|
t = aitp->lb; |
1745 |
|
|
|
1746 |
|
|
/* Count whitespace characters. */ |
1747 |
|
|
for (p = t; len > 0; ++p, --len) |
1748 |
|
|
if (!isblank(*p)) |
1749 |
|
|
break; |
1750 |
|
|
|
1751 |
|
|
/* Set count, check for no indentation. */ |
1752 |
|
|
if ((nlen = (p - t)) == 0) |
1753 |
|
|
return (0); |
1754 |
|
|
|
1755 |
|
|
/* Make sure the buffer's big enough. */ |
1756 |
|
|
BINC_RET(sp, tp->lb, tp->lb_len, tp->len + nlen); |
1757 |
|
|
|
1758 |
|
|
/* Copy the buffer's current contents up. */ |
1759 |
|
|
if (tp->len != 0) |
1760 |
|
|
memmove(tp->lb + nlen, tp->lb, tp->len); |
1761 |
|
|
tp->len += nlen; |
1762 |
|
|
|
1763 |
|
|
/* Copy the indentation into the new buffer. */ |
1764 |
|
|
memmove(tp->lb, t, nlen); |
1765 |
|
|
|
1766 |
|
|
/* Set the autoindent count. */ |
1767 |
|
|
tp->ai = nlen; |
1768 |
|
|
return (0); |
1769 |
|
|
} |
1770 |
|
|
|
1771 |
|
|
/* |
1772 |
|
|
* txt_backup -- |
1773 |
|
|
* Back up to the previously edited line. |
1774 |
|
|
*/ |
1775 |
|
|
static TEXT * |
1776 |
|
|
txt_backup(SCR *sp, TEXTH *tiqh, TEXT *tp, u_int32_t *flagsp) |
1777 |
|
|
{ |
1778 |
|
|
TEXT *ntp; |
1779 |
|
|
|
1780 |
|
|
/* Get a handle on the previous TEXT structure. */ |
1781 |
|
|
if ((ntp = TAILQ_PREV(tp, _texth, q)) == NULL) { |
1782 |
|
|
if (!FL_ISSET(*flagsp, TXT_REPLAY)) |
1783 |
|
|
msgq(sp, M_BERR, |
1784 |
|
|
"Already at the beginning of the insert"); |
1785 |
|
|
return (tp); |
1786 |
|
|
} |
1787 |
|
|
|
1788 |
|
|
/* Bookkeeping. */ |
1789 |
|
|
ntp->len = ntp->sv_len; |
1790 |
|
|
|
1791 |
|
|
/* Handle appending to the line. */ |
1792 |
|
|
if (ntp->owrite == 0 && ntp->insert == 0) { |
1793 |
|
|
ntp->lb[ntp->len] = CH_CURSOR; |
1794 |
|
|
++ntp->insert; |
1795 |
|
|
++ntp->len; |
1796 |
|
|
FL_SET(*flagsp, TXT_APPENDEOL); |
1797 |
|
|
} else |
1798 |
|
|
FL_CLR(*flagsp, TXT_APPENDEOL); |
1799 |
|
|
|
1800 |
|
|
/* Release the current TEXT. */ |
1801 |
|
|
TAILQ_REMOVE(tiqh, tp, q); |
1802 |
|
|
text_free(tp); |
1803 |
|
|
|
1804 |
|
|
/* Update the old line on the screen. */ |
1805 |
|
|
if (vs_change(sp, ntp->lno + 1, LINE_DELETE)) |
1806 |
|
|
return (NULL); |
1807 |
|
|
|
1808 |
|
|
/* Return the new/current TEXT. */ |
1809 |
|
|
return (ntp); |
1810 |
|
|
} |
1811 |
|
|
|
1812 |
|
|
/* |
1813 |
|
|
* Text indentation is truly strange. ^T and ^D do movements to the next or |
1814 |
|
|
* previous shiftwidth value, i.e. for a 1-based numbering, with shiftwidth=3, |
1815 |
|
|
* ^T moves a cursor on the 7th, 8th or 9th column to the 10th column, and ^D |
1816 |
|
|
* moves it back. |
1817 |
|
|
* |
1818 |
|
|
* !!! |
1819 |
|
|
* The ^T and ^D characters in historical vi had special meaning only when they |
1820 |
|
|
* were the first characters entered after entering text input mode. As normal |
1821 |
|
|
* erase characters couldn't erase autoindent characters (^T in this case), it |
1822 |
|
|
* meant that inserting text into previously existing text was strange -- ^T |
1823 |
|
|
* only worked if it was the first keystroke(s), and then could only be erased |
1824 |
|
|
* using ^D. This implementation treats ^T specially anywhere it occurs in the |
1825 |
|
|
* input, and permits the standard erase characters to erase the characters it |
1826 |
|
|
* inserts. |
1827 |
|
|
* |
1828 |
|
|
* !!! |
1829 |
|
|
* A fun test is to try: |
1830 |
|
|
* :se sw=4 ai list |
1831 |
|
|
* i<CR>^Tx<CR>^Tx<CR>^Tx<CR>^Dx<CR>^Dx<CR>^Dx<esc> |
1832 |
|
|
* Historic vi loses some of the '$' marks on the line ends, but otherwise gets |
1833 |
|
|
* it right. |
1834 |
|
|
* |
1835 |
|
|
* XXX |
1836 |
|
|
* Technically, txt_dent should be part of the screen interface, as it requires |
1837 |
|
|
* knowledge of character sizes, including <space>s, on the screen. It's here |
1838 |
|
|
* because it's a complicated little beast, and I didn't want to shove it down |
1839 |
|
|
* into the screen. It's probable that KEY_LEN will call into the screen once |
1840 |
|
|
* there are screens with different character representations. |
1841 |
|
|
* |
1842 |
|
|
* txt_dent -- |
1843 |
|
|
* Handle ^T indents, ^D outdents. |
1844 |
|
|
* |
1845 |
|
|
* If anything changes here, check the ex version to see if it needs similar |
1846 |
|
|
* changes. |
1847 |
|
|
*/ |
1848 |
|
|
static int |
1849 |
|
|
txt_dent(SCR *sp, TEXT *tp, int isindent) |
1850 |
|
|
{ |
1851 |
|
|
CHAR_T ch; |
1852 |
|
|
u_long sw, ts; |
1853 |
|
|
size_t cno, current, spaces, target, tabs; |
1854 |
|
|
int ai_reset; |
1855 |
|
|
|
1856 |
|
|
ts = O_VAL(sp, O_TABSTOP); |
1857 |
|
|
sw = O_VAL(sp, O_SHIFTWIDTH); |
1858 |
|
|
|
1859 |
|
|
/* |
1860 |
|
|
* Since we don't know what precedes the character(s) being inserted |
1861 |
|
|
* (or deleted), the preceding whitespace characters must be resolved. |
1862 |
|
|
* An example is a <tab>, which doesn't need a full shiftwidth number |
1863 |
|
|
* of columns because it's preceded by <space>s. This is easy to get |
1864 |
|
|
* if the user sets shiftwidth to a value less than tabstop (or worse, |
1865 |
|
|
* something for which tabstop isn't a multiple) and then uses ^T to |
1866 |
|
|
* indent, and ^D to outdent. |
1867 |
|
|
* |
1868 |
|
|
* Figure out the current and target screen columns. In the historic |
1869 |
|
|
* vi, the autoindent column was NOT determined using display widths |
1870 |
|
|
* of characters as was the wrapmargin column. For that reason, we |
1871 |
|
|
* can't use the vs_column() function, but have to calculate it here. |
1872 |
|
|
* This is slow, but it's normally only on the first few characters of |
1873 |
|
|
* a line. |
1874 |
|
|
*/ |
1875 |
|
|
for (current = cno = 0; cno < tp->cno; ++cno) |
1876 |
|
|
current += tp->lb[cno] == '\t' ? |
1877 |
|
|
COL_OFF(current, ts) : KEY_LEN(sp, tp->lb[cno]); |
1878 |
|
|
|
1879 |
|
|
target = current; |
1880 |
|
|
if (isindent) |
1881 |
|
|
target += COL_OFF(target, sw); |
1882 |
|
|
else { |
1883 |
|
|
--target; |
1884 |
|
|
target -= target % sw; |
1885 |
|
|
} |
1886 |
|
|
|
1887 |
|
|
/* |
1888 |
|
|
* The AI characters will be turned into overwrite characters if the |
1889 |
|
|
* cursor immediately follows them. We test both the cursor position |
1890 |
|
|
* and the indent flag because there's no single test. (^T can only |
1891 |
|
|
* be detected by the cursor position, and while we know that the test |
1892 |
|
|
* is always true for ^D, the cursor can be in more than one place, as |
1893 |
|
|
* "0^D" and "^D" are different.) |
1894 |
|
|
*/ |
1895 |
|
|
ai_reset = !isindent || tp->cno == tp->ai + tp->offset; |
1896 |
|
|
|
1897 |
|
|
/* |
1898 |
|
|
* Back up over any previous <blank> characters, changing them into |
1899 |
|
|
* overwrite characters (including any ai characters). Then figure |
1900 |
|
|
* out the current screen column. |
1901 |
|
|
*/ |
1902 |
|
|
for (; tp->cno > tp->offset && |
1903 |
|
|
(tp->lb[tp->cno - 1] == ' ' || tp->lb[tp->cno - 1] == '\t'); |
1904 |
|
|
--tp->cno, ++tp->owrite); |
1905 |
|
|
for (current = cno = 0; cno < tp->cno; ++cno) |
1906 |
|
|
current += tp->lb[cno] == '\t' ? |
1907 |
|
|
COL_OFF(current, ts) : KEY_LEN(sp, tp->lb[cno]); |
1908 |
|
|
|
1909 |
|
|
/* |
1910 |
|
|
* If we didn't move up to or past the target, it's because there |
1911 |
|
|
* weren't enough characters to delete, e.g. the first character |
1912 |
|
|
* of the line was a tp->offset character, and the user entered |
1913 |
|
|
* ^D to move to the beginning of a line. An example of this is: |
1914 |
|
|
* |
1915 |
|
|
* :set ai sw=4<cr>i<space>a<esc>i^T^D |
1916 |
|
|
* |
1917 |
|
|
* Otherwise, count up the total spaces/tabs needed to get from the |
1918 |
|
|
* beginning of the line (or the last non-<blank> character) to the |
1919 |
|
|
* target. |
1920 |
|
|
*/ |
1921 |
|
|
if (current >= target) |
1922 |
|
|
spaces = tabs = 0; |
1923 |
|
|
else { |
1924 |
|
|
for (cno = current, |
1925 |
|
|
tabs = 0; cno + COL_OFF(cno, ts) <= target; ++tabs) |
1926 |
|
|
cno += COL_OFF(cno, ts); |
1927 |
|
|
spaces = target - cno; |
1928 |
|
|
} |
1929 |
|
|
|
1930 |
|
|
/* If we overwrote ai characters, reset the ai count. */ |
1931 |
|
|
if (ai_reset) |
1932 |
|
|
tp->ai = tabs + spaces; |
1933 |
|
|
|
1934 |
|
|
/* |
1935 |
|
|
* Call txt_insch() to insert each character, so that we get the |
1936 |
|
|
* correct effect when we add a <tab> to replace N <spaces>. |
1937 |
|
|
*/ |
1938 |
|
|
for (ch = '\t'; tabs > 0; --tabs) |
1939 |
|
|
(void)txt_insch(sp, tp, &ch, 0); |
1940 |
|
|
for (ch = ' '; spaces > 0; --spaces) |
1941 |
|
|
(void)txt_insch(sp, tp, &ch, 0); |
1942 |
|
|
return (0); |
1943 |
|
|
} |
1944 |
|
|
|
1945 |
|
|
/* |
1946 |
|
|
* txt_fc -- |
1947 |
|
|
* File name completion. |
1948 |
|
|
*/ |
1949 |
|
|
static int |
1950 |
|
|
txt_fc(SCR *sp, TEXT *tp, int *redrawp) |
1951 |
|
|
{ |
1952 |
|
|
struct stat sb; |
1953 |
|
|
ARGS **argv; |
1954 |
|
|
CHAR_T s_ch; |
1955 |
|
|
EXCMD cmd; |
1956 |
|
|
size_t indx, len, nlen, off; |
1957 |
|
|
int argc, trydir; |
1958 |
|
|
char *p, *t; |
1959 |
|
|
|
1960 |
|
|
trydir = 0; |
1961 |
|
|
*redrawp = 0; |
1962 |
|
|
|
1963 |
|
|
/* |
1964 |
|
|
* Find the beginning of this "word" -- if we're at the beginning |
1965 |
|
|
* of the line, it's a special case. |
1966 |
|
|
*/ |
1967 |
|
|
if (tp->cno == 1) { |
1968 |
|
|
len = 0; |
1969 |
|
|
p = tp->lb; |
1970 |
|
|
} else |
1971 |
|
|
retry: for (len = 0, |
1972 |
|
|
off = tp->cno - 1, p = tp->lb + off;; --off, --p) { |
1973 |
|
|
if (isblank(*p)) { |
1974 |
|
|
++p; |
1975 |
|
|
break; |
1976 |
|
|
} |
1977 |
|
|
++len; |
1978 |
|
|
if (off == tp->ai || off == tp->offset) |
1979 |
|
|
break; |
1980 |
|
|
} |
1981 |
|
|
|
1982 |
|
|
/* |
1983 |
|
|
* Get enough space for a wildcard character. |
1984 |
|
|
* |
1985 |
|
|
* XXX |
1986 |
|
|
* This won't work for "foo\", since the \ will escape the expansion |
1987 |
|
|
* character. I'm not sure if that's a bug or not... |
1988 |
|
|
*/ |
1989 |
|
|
off = p - tp->lb; |
1990 |
|
|
BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1); |
1991 |
|
|
p = tp->lb + off; |
1992 |
|
|
|
1993 |
|
|
s_ch = p[len]; |
1994 |
|
|
p[len] = '*'; |
1995 |
|
|
|
1996 |
|
|
/* Build an ex command, and call the ex expansion routines. */ |
1997 |
|
|
ex_cinit(&cmd, 0, 0, OOBLNO, OOBLNO, 0, NULL); |
1998 |
|
|
if (argv_init(sp, &cmd)) |
1999 |
|
|
return (1); |
2000 |
|
|
if (argv_exp2(sp, &cmd, p, len + 1)) { |
2001 |
|
|
p[len] = s_ch; |
2002 |
|
|
return (0); |
2003 |
|
|
} |
2004 |
|
|
argc = cmd.argc; |
2005 |
|
|
argv = cmd.argv; |
2006 |
|
|
|
2007 |
|
|
p[len] = s_ch; |
2008 |
|
|
|
2009 |
|
|
switch (argc) { |
2010 |
|
|
case 0: /* No matches. */ |
2011 |
|
|
if (!trydir) |
2012 |
|
|
(void)sp->gp->scr_bell(sp); |
2013 |
|
|
return (0); |
2014 |
|
|
case 1: /* One match. */ |
2015 |
|
|
/* If something changed, do the exchange. */ |
2016 |
|
|
nlen = strlen(cmd.argv[0]->bp); |
2017 |
|
|
if (len != nlen || memcmp(cmd.argv[0]->bp, p, len)) |
2018 |
|
|
break; |
2019 |
|
|
|
2020 |
|
|
/* If haven't done a directory test, do it now. */ |
2021 |
|
|
if (!trydir && |
2022 |
|
|
!stat(cmd.argv[0]->bp, &sb) && S_ISDIR(sb.st_mode)) { |
2023 |
|
|
p += len; |
2024 |
|
|
goto isdir; |
2025 |
|
|
} |
2026 |
|
|
|
2027 |
|
|
/* If nothing changed, period, ring the bell. */ |
2028 |
|
|
if (!trydir) |
2029 |
|
|
(void)sp->gp->scr_bell(sp); |
2030 |
|
|
return (0); |
2031 |
|
|
default: /* Multiple matches. */ |
2032 |
|
|
*redrawp = 1; |
2033 |
|
|
if (txt_fc_col(sp, argc, argv)) |
2034 |
|
|
return (1); |
2035 |
|
|
|
2036 |
|
|
/* Find the length of the shortest match. */ |
2037 |
|
|
for (nlen = cmd.argv[0]->len; --argc > 0;) { |
2038 |
|
|
if (cmd.argv[argc]->len < nlen) |
2039 |
|
|
nlen = cmd.argv[argc]->len; |
2040 |
|
|
for (indx = 0; indx < nlen && |
2041 |
|
|
cmd.argv[argc]->bp[indx] == cmd.argv[0]->bp[indx]; |
2042 |
|
|
++indx); |
2043 |
|
|
nlen = indx; |
2044 |
|
|
} |
2045 |
|
|
break; |
2046 |
|
|
} |
2047 |
|
|
|
2048 |
|
|
/* Overwrite the expanded text first. */ |
2049 |
|
|
for (t = cmd.argv[0]->bp; len > 0 && nlen > 0; --len, --nlen) |
2050 |
|
|
*p++ = *t++; |
2051 |
|
|
|
2052 |
|
|
/* If lost text, make the remaining old text overwrite characters. */ |
2053 |
|
|
if (len) { |
2054 |
|
|
tp->cno -= len; |
2055 |
|
|
tp->owrite += len; |
2056 |
|
|
} |
2057 |
|
|
|
2058 |
|
|
/* Overwrite any overwrite characters next. */ |
2059 |
|
|
for (; nlen > 0 && tp->owrite > 0; --nlen, --tp->owrite, ++tp->cno) |
2060 |
|
|
*p++ = *t++; |
2061 |
|
|
|
2062 |
|
|
/* Shift remaining text up, and move the cursor to the end. */ |
2063 |
|
|
if (nlen) { |
2064 |
|
|
off = p - tp->lb; |
2065 |
|
|
BINC_RET(sp, tp->lb, tp->lb_len, tp->len + nlen); |
2066 |
|
|
p = tp->lb + off; |
2067 |
|
|
|
2068 |
|
|
tp->cno += nlen; |
2069 |
|
|
tp->len += nlen; |
2070 |
|
|
|
2071 |
|
|
if (tp->insert != 0) |
2072 |
|
|
(void)memmove(p + nlen, p, tp->insert); |
2073 |
|
|
while (nlen--) |
2074 |
|
|
*p++ = *t++; |
2075 |
|
|
} |
2076 |
|
|
|
2077 |
|
|
/* If a single match and it's a directory, retry it. */ |
2078 |
|
|
if (argc == 1 && !stat(cmd.argv[0]->bp, &sb) && S_ISDIR(sb.st_mode)) { |
2079 |
|
|
isdir: if (tp->owrite == 0) { |
2080 |
|
|
off = p - tp->lb; |
2081 |
|
|
BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1); |
2082 |
|
|
p = tp->lb + off; |
2083 |
|
|
if (tp->insert != 0) |
2084 |
|
|
(void)memmove(p + 1, p, tp->insert); |
2085 |
|
|
++tp->len; |
2086 |
|
|
} else |
2087 |
|
|
--tp->owrite; |
2088 |
|
|
|
2089 |
|
|
++tp->cno; |
2090 |
|
|
*p++ = '/'; |
2091 |
|
|
|
2092 |
|
|
trydir = 1; |
2093 |
|
|
goto retry; |
2094 |
|
|
} |
2095 |
|
|
return (0); |
2096 |
|
|
} |
2097 |
|
|
|
2098 |
|
|
/* |
2099 |
|
|
* txt_fc_col -- |
2100 |
|
|
* Display file names for file name completion. |
2101 |
|
|
*/ |
2102 |
|
|
static int |
2103 |
|
|
txt_fc_col(SCR *sp, int argc, ARGS **argv) |
2104 |
|
|
{ |
2105 |
|
|
ARGS **av; |
2106 |
|
|
CHAR_T *p; |
2107 |
|
|
GS *gp; |
2108 |
|
|
size_t base, cnt, col, colwidth, numrows, numcols, prefix, row; |
2109 |
|
|
int ac, nf, reset; |
2110 |
|
|
|
2111 |
|
|
gp = sp->gp; |
2112 |
|
|
|
2113 |
|
|
/* Trim any directory prefix common to all of the files. */ |
2114 |
|
|
if ((p = strrchr(argv[0]->bp, '/')) == NULL) |
2115 |
|
|
prefix = 0; |
2116 |
|
|
else { |
2117 |
|
|
prefix = (p - argv[0]->bp) + 1; |
2118 |
|
|
for (ac = argc - 1, av = argv + 1; ac > 0; --ac, ++av) |
2119 |
|
|
if (av[0]->len < prefix || |
2120 |
|
|
memcmp(av[0]->bp, argv[0]->bp, prefix)) { |
2121 |
|
|
prefix = 0; |
2122 |
|
|
break; |
2123 |
|
|
} |
2124 |
|
|
} |
2125 |
|
|
|
2126 |
|
|
/* |
2127 |
|
|
* Figure out the column width for the longest name. Output is done on |
2128 |
|
|
* 6 character "tab" boundaries for no particular reason. (Since we |
2129 |
|
|
* don't output tab characters, we ignore the terminal's tab settings.) |
2130 |
|
|
* Ignore the user's tab setting because we have no idea how reasonable |
2131 |
|
|
* it is. |
2132 |
|
|
*/ |
2133 |
|
|
for (ac = argc, av = argv, colwidth = 0; ac > 0; --ac, ++av) { |
2134 |
|
|
for (col = 0, p = av[0]->bp + prefix; *p != '\0'; ++p) |
2135 |
|
|
col += KEY_LEN(sp, *p); |
2136 |
|
|
if (col > colwidth) |
2137 |
|
|
colwidth = col; |
2138 |
|
|
} |
2139 |
|
|
colwidth += COL_OFF(colwidth, 6); |
2140 |
|
|
|
2141 |
|
|
/* |
2142 |
|
|
* Writing to the bottom line of the screen is always turned off when |
2143 |
|
|
* SC_TINPUT_INFO is set. Turn it back on, we know what we're doing. |
2144 |
|
|
*/ |
2145 |
|
|
if (F_ISSET(sp, SC_TINPUT_INFO)) { |
2146 |
|
|
reset = 1; |
2147 |
|
|
F_CLR(sp, SC_TINPUT_INFO); |
2148 |
|
|
} else |
2149 |
|
|
reset = 0; |
2150 |
|
|
|
2151 |
|
|
#define CHK_INTR \ |
2152 |
|
|
if (F_ISSET(gp, G_INTERRUPTED)) \ |
2153 |
|
|
goto intr; |
2154 |
|
|
|
2155 |
|
|
/* If the largest file name is too large, just print them. */ |
2156 |
|
|
if (colwidth > sp->cols) { |
2157 |
|
|
for (ac = argc, av = argv; ac > 0; --ac, ++av) { |
2158 |
|
|
p = msg_print(sp, av[0]->bp + prefix, &nf); |
2159 |
|
|
(void)ex_printf(sp, "%s\n", p); |
2160 |
|
|
if (F_ISSET(gp, G_INTERRUPTED)) |
2161 |
|
|
break; |
2162 |
|
|
} |
2163 |
|
|
if (nf) |
2164 |
|
|
FREE_SPACE(sp, (char *) p, 0); |
2165 |
|
|
CHK_INTR; |
2166 |
|
|
} else { |
2167 |
|
|
/* Figure out the number of columns. */ |
2168 |
|
|
numcols = (sp->cols - 1) / colwidth; |
2169 |
|
|
if (argc > numcols) { |
2170 |
|
|
numrows = argc / numcols; |
2171 |
|
|
if (argc % numcols) |
2172 |
|
|
++numrows; |
2173 |
|
|
} else |
2174 |
|
|
numrows = 1; |
2175 |
|
|
|
2176 |
|
|
/* Display the files in sorted order. */ |
2177 |
|
|
for (row = 0; row < numrows; ++row) { |
2178 |
|
|
for (base = row, col = 0; col < numcols; ++col) { |
2179 |
|
|
p = msg_print(sp, argv[base]->bp + prefix, &nf); |
2180 |
|
|
cnt = ex_printf(sp, "%s", p); |
2181 |
|
|
if (nf) |
2182 |
|
|
FREE_SPACE(sp, (char *) p, 0); |
2183 |
|
|
CHK_INTR; |
2184 |
|
|
if ((base += numrows) >= argc) |
2185 |
|
|
break; |
2186 |
|
|
(void)ex_printf(sp, |
2187 |
|
|
"%*s", (int)(colwidth - cnt), ""); |
2188 |
|
|
CHK_INTR; |
2189 |
|
|
} |
2190 |
|
|
(void)ex_puts(sp, "\n"); |
2191 |
|
|
CHK_INTR; |
2192 |
|
|
} |
2193 |
|
|
(void)ex_puts(sp, "\n"); |
2194 |
|
|
CHK_INTR; |
2195 |
|
|
} |
2196 |
|
|
(void)ex_fflush(sp); |
2197 |
|
|
|
2198 |
|
|
if (0) { |
2199 |
|
|
intr: F_CLR(gp, G_INTERRUPTED); |
2200 |
|
|
} |
2201 |
|
|
if (reset) |
2202 |
|
|
F_SET(sp, SC_TINPUT_INFO); |
2203 |
|
|
|
2204 |
|
|
return (0); |
2205 |
|
|
} |
2206 |
|
|
|
2207 |
|
|
/* |
2208 |
|
|
* txt_emark -- |
2209 |
|
|
* Set the end mark on the line. |
2210 |
|
|
*/ |
2211 |
|
|
static int |
2212 |
|
|
txt_emark(SCR *sp, TEXT *tp, size_t cno) |
2213 |
|
|
{ |
2214 |
|
|
CHAR_T ch, *kp; |
2215 |
|
|
size_t chlen, nlen, olen; |
2216 |
|
|
char *p; |
2217 |
|
|
|
2218 |
|
|
ch = CH_ENDMARK; |
2219 |
|
|
|
2220 |
|
|
/* |
2221 |
|
|
* The end mark may not be the same size as the current character. |
2222 |
|
|
* Don't let the line shift. |
2223 |
|
|
*/ |
2224 |
|
|
nlen = KEY_LEN(sp, ch); |
2225 |
|
|
if (tp->lb[cno] == '\t') |
2226 |
|
|
(void)vs_columns(sp, tp->lb, tp->lno, &cno, &olen); |
2227 |
|
|
else |
2228 |
|
|
olen = KEY_LEN(sp, tp->lb[cno]); |
2229 |
|
|
|
2230 |
|
|
/* |
2231 |
|
|
* If the line got longer, well, it's weird, but it's easy. If |
2232 |
|
|
* it's the same length, it's easy. If it got shorter, we have |
2233 |
|
|
* to fix it up. |
2234 |
|
|
*/ |
2235 |
|
|
if (olen > nlen) { |
2236 |
|
|
BINC_RET(sp, tp->lb, tp->lb_len, tp->len + olen); |
2237 |
|
|
chlen = olen - nlen; |
2238 |
|
|
if (tp->insert != 0) |
2239 |
|
|
memmove(tp->lb + cno + 1 + chlen, |
2240 |
|
|
tp->lb + cno + 1, tp->insert); |
2241 |
|
|
|
2242 |
|
|
tp->len += chlen; |
2243 |
|
|
tp->owrite += chlen; |
2244 |
|
|
p = tp->lb + cno; |
2245 |
|
|
if (tp->lb[cno] == '\t') |
2246 |
|
|
for (cno += chlen; chlen--;) |
2247 |
|
|
*p++ = ' '; |
2248 |
|
|
else |
2249 |
|
|
for (kp = KEY_NAME(sp, tp->lb[cno]), |
2250 |
|
|
cno += chlen; chlen--;) |
2251 |
|
|
*p++ = *kp++; |
2252 |
|
|
} |
2253 |
|
|
tp->lb[cno] = ch; |
2254 |
|
|
return (vs_change(sp, tp->lno, LINE_RESET)); |
2255 |
|
|
} |
2256 |
|
|
|
2257 |
|
|
/* |
2258 |
|
|
* txt_err -- |
2259 |
|
|
* Handle an error during input processing. |
2260 |
|
|
*/ |
2261 |
|
|
static void |
2262 |
|
|
txt_err(SCR *sp, TEXTH *tiqh) |
2263 |
|
|
{ |
2264 |
|
|
recno_t lno; |
2265 |
|
|
|
2266 |
|
|
/* |
2267 |
|
|
* The problem with input processing is that the cursor is at an |
2268 |
|
|
* indeterminate position since some input may have been lost due |
2269 |
|
|
* to a malloc error. So, try to go back to the place from which |
2270 |
|
|
* the cursor started, knowing that it may no longer be available. |
2271 |
|
|
* |
2272 |
|
|
* We depend on at least one line number being set in the text |
2273 |
|
|
* chain. |
2274 |
|
|
*/ |
2275 |
|
|
for (lno = TAILQ_FIRST(tiqh)->lno; |
2276 |
|
|
!db_exist(sp, lno) && lno > 0; --lno); |
2277 |
|
|
|
2278 |
|
|
sp->lno = lno == 0 ? 1 : lno; |
2279 |
|
|
sp->cno = 0; |
2280 |
|
|
|
2281 |
|
|
/* Redraw the screen, just in case. */ |
2282 |
|
|
F_SET(sp, SC_SCR_REDRAW); |
2283 |
|
|
} |
2284 |
|
|
|
2285 |
|
|
/* |
2286 |
|
|
* txt_hex -- |
2287 |
|
|
* Let the user insert any character value they want. |
2288 |
|
|
* |
2289 |
|
|
* !!! |
2290 |
|
|
* This is an extension. The pattern "^X[0-9a-fA-F]*" is a way |
2291 |
|
|
* for the user to specify a character value which their keyboard |
2292 |
|
|
* may not be able to enter. |
2293 |
|
|
*/ |
2294 |
|
|
static int |
2295 |
|
|
txt_hex(SCR *sp, TEXT *tp) |
2296 |
|
|
{ |
2297 |
|
|
CHAR_T savec; |
2298 |
|
|
size_t len, off; |
2299 |
|
|
u_long value; |
2300 |
|
|
char *p, *wp; |
2301 |
|
|
|
2302 |
|
|
/* |
2303 |
|
|
* Null-terminate the string. Since nul isn't a legal hex value, |
2304 |
|
|
* this should be okay, and lets us use a local routine, which |
2305 |
|
|
* presumably understands the character set, to convert the value. |
2306 |
|
|
*/ |
2307 |
|
|
savec = tp->lb[tp->cno]; |
2308 |
|
|
tp->lb[tp->cno] = 0; |
2309 |
|
|
|
2310 |
|
|
/* Find the previous CH_HEX character. */ |
2311 |
|
|
for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off, ++len) { |
2312 |
|
|
if (*p == CH_HEX) { |
2313 |
|
|
wp = p + 1; |
2314 |
|
|
break; |
2315 |
|
|
} |
2316 |
|
|
/* Not on this line? Shouldn't happen. */ |
2317 |
|
|
if (off == tp->ai || off == tp->offset) |
2318 |
|
|
goto nothex; |
2319 |
|
|
} |
2320 |
|
|
|
2321 |
|
|
/* If length of 0, then it wasn't a hex value. */ |
2322 |
|
|
if (len == 0) |
2323 |
|
|
goto nothex; |
2324 |
|
|
|
2325 |
|
|
/* Get the value. */ |
2326 |
|
|
errno = 0; |
2327 |
|
|
value = strtol(wp, NULL, 16); |
2328 |
|
|
if (errno || value > MAX_CHAR_T) { |
2329 |
|
|
nothex: tp->lb[tp->cno] = savec; |
2330 |
|
|
return (0); |
2331 |
|
|
} |
2332 |
|
|
|
2333 |
|
|
/* Restore the original character. */ |
2334 |
|
|
tp->lb[tp->cno] = savec; |
2335 |
|
|
|
2336 |
|
|
/* Adjust the bookkeeping. */ |
2337 |
|
|
tp->cno -= len; |
2338 |
|
|
tp->len -= len; |
2339 |
|
|
tp->lb[tp->cno - 1] = value; |
2340 |
|
|
|
2341 |
|
|
/* Copy down any overwrite characters. */ |
2342 |
|
|
if (tp->owrite) |
2343 |
|
|
memmove(tp->lb + tp->cno, tp->lb + tp->cno + len, tp->owrite); |
2344 |
|
|
|
2345 |
|
|
/* Copy down any insert characters. */ |
2346 |
|
|
if (tp->insert) |
2347 |
|
|
memmove(tp->lb + tp->cno + tp->owrite, |
2348 |
|
|
tp->lb + tp->cno + tp->owrite + len, tp->insert); |
2349 |
|
|
|
2350 |
|
|
return (0); |
2351 |
|
|
} |
2352 |
|
|
|
2353 |
|
|
/* |
2354 |
|
|
* txt_insch -- |
2355 |
|
|
* |
2356 |
|
|
* !!! |
2357 |
|
|
* Historic vi did a special screen optimization for tab characters. As an |
2358 |
|
|
* example, for the keystrokes "iabcd<esc>0C<tab>", the tab overwrote the |
2359 |
|
|
* rest of the string when it was displayed. |
2360 |
|
|
* |
2361 |
|
|
* Because early versions of this implementation redisplayed the entire line |
2362 |
|
|
* on each keystroke, the "bcd" was pushed to the right as it ignored that |
2363 |
|
|
* the user had "promised" to change the rest of the characters. However, |
2364 |
|
|
* the historic vi implementation had an even worse bug: given the keystrokes |
2365 |
|
|
* "iabcd<esc>0R<tab><esc>", the "bcd" disappears, and magically reappears |
2366 |
|
|
* on the second <esc> key. |
2367 |
|
|
* |
2368 |
|
|
* POSIX 1003.2 requires (will require) that this be fixed, specifying that |
2369 |
|
|
* vi overwrite characters the user has committed to changing, on the basis |
2370 |
|
|
* of the screen space they require, but that it not overwrite other characters. |
2371 |
|
|
*/ |
2372 |
|
|
static int |
2373 |
|
|
txt_insch(SCR *sp, TEXT *tp, CHAR_T *chp, u_int flags) |
2374 |
|
|
{ |
2375 |
|
|
CHAR_T *kp, savech; |
2376 |
|
|
size_t chlen, cno, copydown, olen, nlen; |
2377 |
|
|
char *p; |
2378 |
|
|
|
2379 |
|
|
/* |
2380 |
|
|
* The 'R' command does one-for-one replacement, because there's |
2381 |
|
|
* no way to know how many characters the user intends to replace. |
2382 |
|
|
*/ |
2383 |
|
|
if (LF_ISSET(TXT_REPLACE)) { |
2384 |
|
|
if (tp->owrite) { |
2385 |
|
|
--tp->owrite; |
2386 |
|
|
tp->lb[tp->cno++] = *chp; |
2387 |
|
|
return (0); |
2388 |
|
|
} |
2389 |
|
|
} else if (tp->owrite) { /* Overwrite a character. */ |
2390 |
|
|
cno = tp->cno; |
2391 |
|
|
|
2392 |
|
|
/* |
2393 |
|
|
* If the old or new characters are tabs, then the length of the |
2394 |
|
|
* display depends on the character position in the display. We |
2395 |
|
|
* don't even try to handle this here, just ask the screen. |
2396 |
|
|
*/ |
2397 |
|
|
if (*chp == '\t') { |
2398 |
|
|
savech = tp->lb[cno]; |
2399 |
|
|
tp->lb[cno] = '\t'; |
2400 |
|
|
(void)vs_columns(sp, tp->lb, tp->lno, &cno, &nlen); |
2401 |
|
|
tp->lb[cno] = savech; |
2402 |
|
|
} else |
2403 |
|
|
nlen = KEY_LEN(sp, *chp); |
2404 |
|
|
|
2405 |
|
|
/* |
2406 |
|
|
* Eat overwrite characters until we run out of them or we've |
2407 |
|
|
* handled the length of the new character. If we only eat |
2408 |
|
|
* part of an overwrite character, break it into its component |
2409 |
|
|
* elements and display the remaining components. |
2410 |
|
|
*/ |
2411 |
|
|
for (copydown = 0; nlen != 0 && tp->owrite != 0;) { |
2412 |
|
|
--tp->owrite; |
2413 |
|
|
|
2414 |
|
|
if (tp->lb[cno] == '\t') |
2415 |
|
|
(void)vs_columns(sp, |
2416 |
|
|
tp->lb, tp->lno, &cno, &olen); |
2417 |
|
|
else |
2418 |
|
|
olen = KEY_LEN(sp, tp->lb[cno]); |
2419 |
|
|
|
2420 |
|
|
if (olen == nlen) { |
2421 |
|
|
nlen = 0; |
2422 |
|
|
break; |
2423 |
|
|
} |
2424 |
|
|
if (olen < nlen) { |
2425 |
|
|
++copydown; |
2426 |
|
|
nlen -= olen; |
2427 |
|
|
} else { |
2428 |
|
|
BINC_RET(sp, |
2429 |
|
|
tp->lb, tp->lb_len, tp->len + olen); |
2430 |
|
|
chlen = olen - nlen; |
2431 |
|
|
memmove(tp->lb + cno + 1 + chlen, |
2432 |
|
|
tp->lb + cno + 1, tp->owrite + tp->insert); |
2433 |
|
|
|
2434 |
|
|
tp->len += chlen; |
2435 |
|
|
tp->owrite += chlen; |
2436 |
|
|
if (tp->lb[cno] == '\t') |
2437 |
|
|
for (p = tp->lb + cno + 1; chlen--;) |
2438 |
|
|
*p++ = ' '; |
2439 |
|
|
else |
2440 |
|
|
for (kp = |
2441 |
|
|
KEY_NAME(sp, tp->lb[cno]) + nlen, |
2442 |
|
|
p = tp->lb + cno + 1; chlen--;) |
2443 |
|
|
*p++ = *kp++; |
2444 |
|
|
nlen = 0; |
2445 |
|
|
break; |
2446 |
|
|
} |
2447 |
|
|
} |
2448 |
|
|
|
2449 |
|
|
/* |
2450 |
|
|
* If had to erase several characters, we adjust the total |
2451 |
|
|
* count, and if there are any characters left, shift them |
2452 |
|
|
* into position. |
2453 |
|
|
*/ |
2454 |
|
|
if (copydown != 0 && (tp->len -= copydown) != 0) |
2455 |
|
|
memmove(tp->lb + cno, tp->lb + cno + copydown, |
2456 |
|
|
tp->owrite + tp->insert + copydown); |
2457 |
|
|
|
2458 |
|
|
/* If we had enough overwrite characters, we're done. */ |
2459 |
|
|
if (nlen == 0) { |
2460 |
|
|
tp->lb[tp->cno++] = *chp; |
2461 |
|
|
return (0); |
2462 |
|
|
} |
2463 |
|
|
} |
2464 |
|
|
|
2465 |
|
|
/* Check to see if the character fits into the input buffer. */ |
2466 |
|
|
BINC_RET(sp, tp->lb, tp->lb_len, tp->len + 1); |
2467 |
|
|
|
2468 |
|
|
++tp->len; |
2469 |
|
|
if (tp->insert) { /* Insert a character. */ |
2470 |
|
|
if (tp->insert == 1) |
2471 |
|
|
tp->lb[tp->cno + 1] = tp->lb[tp->cno]; |
2472 |
|
|
else |
2473 |
|
|
memmove(tp->lb + tp->cno + 1, |
2474 |
|
|
tp->lb + tp->cno, tp->owrite + tp->insert); |
2475 |
|
|
} |
2476 |
|
|
tp->lb[tp->cno++] = *chp; |
2477 |
|
|
return (0); |
2478 |
|
|
} |
2479 |
|
|
|
2480 |
|
|
/* |
2481 |
|
|
* txt_isrch -- |
2482 |
|
|
* Do an incremental search. |
2483 |
|
|
*/ |
2484 |
|
|
static int |
2485 |
|
|
txt_isrch(SCR *sp, VICMD *vp, TEXT *tp, u_int8_t *is_flagsp) |
2486 |
|
|
{ |
2487 |
|
|
MARK start; |
2488 |
|
|
recno_t lno; |
2489 |
|
|
u_int sf; |
2490 |
|
|
|
2491 |
|
|
/* If it's a one-line screen, we don't do incrementals. */ |
2492 |
|
|
if (IS_ONELINE(sp)) { |
2493 |
|
|
FL_CLR(*is_flagsp, IS_RUNNING); |
2494 |
|
|
return (0); |
2495 |
|
|
} |
2496 |
|
|
|
2497 |
|
|
/* |
2498 |
|
|
* If the user erases back to the beginning of the buffer, there's |
2499 |
|
|
* nothing to search for. Reset the cursor to the starting point. |
2500 |
|
|
*/ |
2501 |
|
|
if (tp->cno <= 1) { |
2502 |
|
|
vp->m_final = vp->m_start; |
2503 |
|
|
return (0); |
2504 |
|
|
} |
2505 |
|
|
|
2506 |
|
|
/* |
2507 |
|
|
* If it's an RE quote character, and not quoted, ignore it until |
2508 |
|
|
* we get another character. |
2509 |
|
|
*/ |
2510 |
|
|
if (tp->lb[tp->cno - 1] == '\\' && |
2511 |
|
|
(tp->cno == 2 || tp->lb[tp->cno - 2] != '\\')) |
2512 |
|
|
return (0); |
2513 |
|
|
|
2514 |
|
|
/* |
2515 |
|
|
* If it's a magic shell character, and not quoted, reset the cursor |
2516 |
|
|
* to the starting point. |
2517 |
|
|
*/ |
2518 |
|
|
if (strchr(O_STR(sp, O_SHELLMETA), tp->lb[tp->cno - 1]) != NULL && |
2519 |
|
|
(tp->cno == 2 || tp->lb[tp->cno - 2] != '\\')) |
2520 |
|
|
vp->m_final = vp->m_start; |
2521 |
|
|
|
2522 |
|
|
/* |
2523 |
|
|
* If we see the search pattern termination character, then quit doing |
2524 |
|
|
* an incremental search. There may be more, e.g., ":/foo/;/bar/", |
2525 |
|
|
* and we can't handle that incrementally. Also, reset the cursor to |
2526 |
|
|
* the original location, the ex search routines don't know anything |
2527 |
|
|
* about incremental searches. |
2528 |
|
|
*/ |
2529 |
|
|
if (tp->lb[0] == tp->lb[tp->cno - 1] && |
2530 |
|
|
(tp->cno == 2 || tp->lb[tp->cno - 2] != '\\')) { |
2531 |
|
|
vp->m_final = vp->m_start; |
2532 |
|
|
FL_CLR(*is_flagsp, IS_RUNNING); |
2533 |
|
|
return (0); |
2534 |
|
|
} |
2535 |
|
|
|
2536 |
|
|
/* |
2537 |
|
|
* Remember the input line and discard the special input map, |
2538 |
|
|
* but don't overwrite the input line on the screen. |
2539 |
|
|
*/ |
2540 |
|
|
lno = tp->lno; |
2541 |
|
|
F_SET(VIP(sp), VIP_S_MODELINE); |
2542 |
|
|
F_CLR(sp, SC_TINPUT | SC_TINPUT_INFO); |
2543 |
|
|
if (txt_map_end(sp)) |
2544 |
|
|
return (1); |
2545 |
|
|
|
2546 |
|
|
/* |
2547 |
|
|
* Specify a starting point and search. If we find a match, move to |
2548 |
|
|
* it and refresh the screen. If we didn't find the match, then we |
2549 |
|
|
* beep the screen. When searching from the original cursor position, |
2550 |
|
|
* we have to move the cursor, otherwise, we don't want to move the |
2551 |
|
|
* cursor in case the text at the current position continues to match. |
2552 |
|
|
*/ |
2553 |
|
|
if (FL_ISSET(*is_flagsp, IS_RESTART)) { |
2554 |
|
|
start = vp->m_start; |
2555 |
|
|
sf = SEARCH_SET; |
2556 |
|
|
} else { |
2557 |
|
|
start = vp->m_final; |
2558 |
|
|
sf = SEARCH_INCR | SEARCH_SET; |
2559 |
|
|
} |
2560 |
|
|
|
2561 |
|
|
if (tp->lb[0] == '/' ? |
2562 |
|
|
!f_search(sp, |
2563 |
|
|
&start, &vp->m_final, tp->lb + 1, tp->cno - 1, NULL, sf) : |
2564 |
|
|
!b_search(sp, |
2565 |
|
|
&start, &vp->m_final, tp->lb + 1, tp->cno - 1, NULL, sf)) { |
2566 |
|
|
sp->lno = vp->m_final.lno; |
2567 |
|
|
sp->cno = vp->m_final.cno; |
2568 |
|
|
FL_CLR(*is_flagsp, IS_RESTART); |
2569 |
|
|
|
2570 |
|
|
if (!KEYS_WAITING(sp) && vs_refresh(sp, 0)) |
2571 |
|
|
return (1); |
2572 |
|
|
} else |
2573 |
|
|
FL_SET(*is_flagsp, IS_RESTART); |
2574 |
|
|
|
2575 |
|
|
/* Reinstantiate the special input map. */ |
2576 |
|
|
if (txt_map_init(sp)) |
2577 |
|
|
return (1); |
2578 |
|
|
F_CLR(VIP(sp), VIP_S_MODELINE); |
2579 |
|
|
F_SET(sp, SC_TINPUT | SC_TINPUT_INFO); |
2580 |
|
|
|
2581 |
|
|
/* Reset the line number of the input line. */ |
2582 |
|
|
tp->lno = TMAP[0].lno; |
2583 |
|
|
|
2584 |
|
|
/* |
2585 |
|
|
* If the colon command-line moved, i.e. the screen scrolled, |
2586 |
|
|
* refresh the input line. |
2587 |
|
|
* |
2588 |
|
|
* XXX |
2589 |
|
|
* We shouldn't be calling vs_line, here -- we need dirty bits |
2590 |
|
|
* on entries in the SMAP array. |
2591 |
|
|
*/ |
2592 |
|
|
if (lno != TMAP[0].lno) { |
2593 |
|
|
if (vs_line(sp, &TMAP[0], NULL, NULL)) |
2594 |
|
|
return (1); |
2595 |
|
|
(void)sp->gp->scr_refresh(sp, 0); |
2596 |
|
|
} |
2597 |
|
|
return (0); |
2598 |
|
|
} |
2599 |
|
|
|
2600 |
|
|
/* |
2601 |
|
|
* txt_resolve -- |
2602 |
|
|
* Resolve the input text chain into the file. |
2603 |
|
|
*/ |
2604 |
|
|
static int |
2605 |
|
|
txt_resolve(SCR *sp, TEXTH *tiqh, u_int32_t flags) |
2606 |
|
|
{ |
2607 |
|
|
TEXT *tp; |
2608 |
|
|
recno_t lno; |
2609 |
|
|
int changed; |
2610 |
|
|
|
2611 |
|
|
/* |
2612 |
|
|
* The first line replaces a current line, and all subsequent lines |
2613 |
|
|
* are appended into the file. Resolve autoindented characters for |
2614 |
|
|
* each line before committing it. If the latter causes the line to |
2615 |
|
|
* change, we have to redisplay it, otherwise the information cached |
2616 |
|
|
* about the line will be wrong. |
2617 |
|
|
*/ |
2618 |
|
|
tp = TAILQ_FIRST(tiqh); |
2619 |
|
|
|
2620 |
|
|
if (LF_ISSET(TXT_AUTOINDENT)) |
2621 |
|
|
txt_ai_resolve(sp, tp, &changed); |
2622 |
|
|
else |
2623 |
|
|
changed = 0; |
2624 |
|
|
if (db_set(sp, tp->lno, tp->lb, tp->len) || |
2625 |
|
|
(changed && vs_change(sp, tp->lno, LINE_RESET))) |
2626 |
|
|
return (1); |
2627 |
|
|
|
2628 |
|
|
for (lno = tp->lno; (tp = TAILQ_NEXT(tp, q)); ++lno) { |
2629 |
|
|
if (LF_ISSET(TXT_AUTOINDENT)) |
2630 |
|
|
txt_ai_resolve(sp, tp, &changed); |
2631 |
|
|
else |
2632 |
|
|
changed = 0; |
2633 |
|
|
if (db_append(sp, 0, lno, tp->lb, tp->len) || |
2634 |
|
|
(changed && vs_change(sp, tp->lno, LINE_RESET))) |
2635 |
|
|
return (1); |
2636 |
|
|
} |
2637 |
|
|
|
2638 |
|
|
/* |
2639 |
|
|
* Clear the input flag, the look-aside buffer is no longer valid. |
2640 |
|
|
* Has to be done as part of text resolution, or upon return we'll |
2641 |
|
|
* be looking at incorrect data. |
2642 |
|
|
*/ |
2643 |
|
|
F_CLR(sp, SC_TINPUT); |
2644 |
|
|
|
2645 |
|
|
return (0); |
2646 |
|
|
} |
2647 |
|
|
|
2648 |
|
|
/* |
2649 |
|
|
* txt_showmatch -- |
2650 |
|
|
* Show a character match. |
2651 |
|
|
* |
2652 |
|
|
* !!! |
2653 |
|
|
* Historic vi tried to display matches even in the :colon command line. |
2654 |
|
|
* I think not. |
2655 |
|
|
*/ |
2656 |
|
|
static int |
2657 |
|
|
txt_showmatch(SCR *sp, TEXT *tp) |
2658 |
|
|
{ |
2659 |
|
|
VCS cs; |
2660 |
|
|
MARK m; |
2661 |
|
|
int cnt, endc, startc; |
2662 |
|
|
|
2663 |
|
|
/* |
2664 |
|
|
* Do a refresh first, in case we haven't done one in awhile, |
2665 |
|
|
* so the user can see what we're complaining about. |
2666 |
|
|
*/ |
2667 |
|
|
UPDATE_POSITION(sp, tp); |
2668 |
|
|
if (vs_refresh(sp, 1)) |
2669 |
|
|
return (1); |
2670 |
|
|
|
2671 |
|
|
/* |
2672 |
|
|
* We don't display the match if it's not on the screen. Find |
2673 |
|
|
* out what the first character on the screen is. |
2674 |
|
|
*/ |
2675 |
|
|
if (vs_sm_position(sp, &m, 0, P_TOP)) |
2676 |
|
|
return (1); |
2677 |
|
|
|
2678 |
|
|
/* Initialize the getc() interface. */ |
2679 |
|
|
cs.cs_lno = tp->lno; |
2680 |
|
|
cs.cs_cno = tp->cno - 1; |
2681 |
|
|
if (cs_init(sp, &cs)) |
2682 |
|
|
return (1); |
2683 |
|
|
startc = (endc = cs.cs_ch) == ')' ? '(' : '{'; |
2684 |
|
|
|
2685 |
|
|
/* Search for the match. */ |
2686 |
|
|
for (cnt = 1;;) { |
2687 |
|
|
if (cs_prev(sp, &cs)) |
2688 |
|
|
return (1); |
2689 |
|
|
if (cs.cs_flags != 0) { |
2690 |
|
|
if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) { |
2691 |
|
|
msgq(sp, M_BERR, |
2692 |
|
|
"Unmatched %s", KEY_NAME(sp, endc)); |
2693 |
|
|
return (0); |
2694 |
|
|
} |
2695 |
|
|
continue; |
2696 |
|
|
} |
2697 |
|
|
if (cs.cs_ch == endc) |
2698 |
|
|
++cnt; |
2699 |
|
|
else if (cs.cs_ch == startc && --cnt == 0) |
2700 |
|
|
break; |
2701 |
|
|
} |
2702 |
|
|
|
2703 |
|
|
/* If the match is on the screen, move to it. */ |
2704 |
|
|
if (cs.cs_lno < m.lno || (cs.cs_lno == m.lno && cs.cs_cno < m.cno)) |
2705 |
|
|
return (0); |
2706 |
|
|
sp->lno = cs.cs_lno; |
2707 |
|
|
sp->cno = cs.cs_cno; |
2708 |
|
|
if (vs_refresh(sp, 1)) |
2709 |
|
|
return (1); |
2710 |
|
|
|
2711 |
|
|
/* Wait for timeout or character arrival. */ |
2712 |
|
|
return (v_event_get(sp, |
2713 |
|
|
NULL, O_VAL(sp, O_MATCHTIME) * 100, EC_TIMEOUT)); |
2714 |
|
|
} |
2715 |
|
|
|
2716 |
|
|
/* |
2717 |
|
|
* txt_margin -- |
2718 |
|
|
* Handle margin wrap. |
2719 |
|
|
*/ |
2720 |
|
|
static int |
2721 |
|
|
txt_margin(SCR *sp, TEXT *tp, TEXT *wmtp, int *didbreak, u_int32_t flags) |
2722 |
|
|
{ |
2723 |
|
|
size_t len, off; |
2724 |
|
|
char *p; |
2725 |
|
|
|
2726 |
|
|
/* Find the nearest previous blank. */ |
2727 |
|
|
for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --off, --p, ++len) { |
2728 |
|
|
if (isblank(*p)) |
2729 |
|
|
break; |
2730 |
|
|
|
2731 |
|
|
/* |
2732 |
|
|
* If reach the start of the line, there's nowhere to break. |
2733 |
|
|
* |
2734 |
|
|
* !!! |
2735 |
|
|
* Historic vi belled each time a character was entered after |
2736 |
|
|
* crossing the margin until a space was entered which could |
2737 |
|
|
* be used to break the line. I don't as it tends to wake the |
2738 |
|
|
* cats. |
2739 |
|
|
*/ |
2740 |
|
|
if (off == tp->ai || off == tp->offset) { |
2741 |
|
|
*didbreak = 0; |
2742 |
|
|
return (0); |
2743 |
|
|
} |
2744 |
|
|
} |
2745 |
|
|
|
2746 |
|
|
/* |
2747 |
|
|
* Store saved information about the rest of the line in the |
2748 |
|
|
* wrapmargin TEXT structure. |
2749 |
|
|
* |
2750 |
|
|
* !!! |
2751 |
|
|
* The offset field holds the length of the current characters |
2752 |
|
|
* that the user entered, but which are getting split to the new |
2753 |
|
|
* line -- it's going to be used to set the cursor value when we |
2754 |
|
|
* move to the new line. |
2755 |
|
|
*/ |
2756 |
|
|
wmtp->lb = p + 1; |
2757 |
|
|
wmtp->offset = len; |
2758 |
|
|
wmtp->insert = LF_ISSET(TXT_APPENDEOL) ? tp->insert - 1 : tp->insert; |
2759 |
|
|
wmtp->owrite = tp->owrite; |
2760 |
|
|
|
2761 |
|
|
/* Correct current bookkeeping information. */ |
2762 |
|
|
tp->cno -= len; |
2763 |
|
|
if (LF_ISSET(TXT_APPENDEOL)) { |
2764 |
|
|
tp->len -= len + tp->owrite + (tp->insert - 1); |
2765 |
|
|
tp->insert = 1; |
2766 |
|
|
} else { |
2767 |
|
|
tp->len -= len + tp->owrite + tp->insert; |
2768 |
|
|
tp->insert = 0; |
2769 |
|
|
} |
2770 |
|
|
tp->owrite = 0; |
2771 |
|
|
|
2772 |
|
|
/* |
2773 |
|
|
* !!! |
2774 |
|
|
* Delete any trailing whitespace from the current line. |
2775 |
|
|
*/ |
2776 |
|
|
for (;; --p, --off) { |
2777 |
|
|
if (!isblank(*p)) |
2778 |
|
|
break; |
2779 |
|
|
--tp->cno; |
2780 |
|
|
--tp->len; |
2781 |
|
|
if (off == tp->ai || off == tp->offset) |
2782 |
|
|
break; |
2783 |
|
|
} |
2784 |
|
|
*didbreak = 1; |
2785 |
|
|
return (0); |
2786 |
|
|
} |
2787 |
|
|
|
2788 |
|
|
/* |
2789 |
|
|
* txt_Rresolve -- |
2790 |
|
|
* Resolve the input line for the 'R' command. |
2791 |
|
|
*/ |
2792 |
|
|
static void |
2793 |
|
|
txt_Rresolve(SCR *sp, TEXTH *tiqh, TEXT *tp, const size_t orig_len) |
2794 |
|
|
{ |
2795 |
|
|
TEXT *ttp; |
2796 |
|
|
size_t input_len, retain; |
2797 |
|
|
char *p; |
2798 |
|
|
|
2799 |
|
|
/* |
2800 |
|
|
* Check to make sure that the cursor hasn't moved beyond |
2801 |
|
|
* the end of the line. |
2802 |
|
|
*/ |
2803 |
|
|
if (tp->owrite == 0) |
2804 |
|
|
return; |
2805 |
|
|
|
2806 |
|
|
/* |
2807 |
|
|
* Calculate how many characters the user has entered, |
2808 |
|
|
* plus the blanks erased by <carriage-return>/<newline>s. |
2809 |
|
|
*/ |
2810 |
|
|
input_len = 0; |
2811 |
|
|
TAILQ_FOREACH(ttp, tiqh, q) { |
2812 |
|
|
input_len += ttp == tp ? tp->cno : ttp->len + ttp->R_erase; |
2813 |
|
|
} |
2814 |
|
|
|
2815 |
|
|
/* |
2816 |
|
|
* If the user has entered less characters than the original line |
2817 |
|
|
* was long, restore any overwriteable characters to the original |
2818 |
|
|
* characters. These characters are entered as "insert characters", |
2819 |
|
|
* because they're after the cursor and we don't want to lose them. |
2820 |
|
|
* (This is okay because the R command has no insert characters.) |
2821 |
|
|
* We set owrite to 0 so that the insert characters don't get copied |
2822 |
|
|
* to somewhere else, which means that the line and the length have |
2823 |
|
|
* to be adjusted here as well. |
2824 |
|
|
* |
2825 |
|
|
* We have to retrieve the original line because the original pinned |
2826 |
|
|
* page has long since been discarded. If it doesn't exist, that's |
2827 |
|
|
* okay, the user just extended the file. |
2828 |
|
|
*/ |
2829 |
|
|
if (input_len < orig_len) { |
2830 |
|
|
retain = MINIMUM(tp->owrite, orig_len - input_len); |
2831 |
|
|
if (db_get(sp, |
2832 |
|
|
TAILQ_FIRST(tiqh)->lno, DBG_FATAL | DBG_NOCACHE, &p, NULL)) |
2833 |
|
|
return; |
2834 |
|
|
memcpy(tp->lb + tp->cno, p + input_len, retain); |
2835 |
|
|
tp->len -= tp->owrite - retain; |
2836 |
|
|
tp->owrite = 0; |
2837 |
|
|
tp->insert += retain; |
2838 |
|
|
} |
2839 |
|
|
} |
2840 |
|
|
|
2841 |
|
|
/* |
2842 |
|
|
* txt_nomorech -- |
2843 |
|
|
* No more characters message. |
2844 |
|
|
*/ |
2845 |
|
|
static void |
2846 |
|
|
txt_nomorech(SCR *sp) |
2847 |
|
|
{ |
2848 |
|
|
msgq(sp, M_BERR, "No more characters to erase"); |
2849 |
|
|
} |