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