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