| @@ -1,2145 +1,2143 @@ | | | @@ -1,2145 +1,2143 @@ |
1 | /* $NetBSD: v_txt.c,v 1.6 2018/08/07 08:05:47 rin Exp $ */ | | 1 | /* $NetBSD: v_txt.c,v 1.7 2020/07/07 10:58:43 rin Exp $ */ |
2 | /*- | | 2 | /*- |
3 | * Copyright (c) 1993, 1994 | | 3 | * Copyright (c) 1993, 1994 |
4 | * The Regents of the University of California. All rights reserved. | | 4 | * The Regents of the University of California. All rights reserved. |
5 | * Copyright (c) 1992, 1993, 1994, 1995, 1996 | | 5 | * Copyright (c) 1992, 1993, 1994, 1995, 1996 |
6 | * Keith Bostic. All rights reserved. | | 6 | * Keith Bostic. All rights reserved. |
7 | * | | 7 | * |
8 | * See the LICENSE file for redistribution information. | | 8 | * See the LICENSE file for redistribution information. |
9 | */ | | 9 | */ |
10 | | | 10 | |
11 | #include "config.h" | | 11 | #include "config.h" |
12 | | | 12 | |
13 | #include <sys/cdefs.h> | | 13 | #include <sys/cdefs.h> |
14 | #if 0 | | 14 | #if 0 |
15 | #ifndef lint | | 15 | #ifndef lint |
16 | static const char sccsid[] = "Id: v_txt.c,v 10.108 2003/07/18 21:27:42 skimo Exp (Berkeley) Date: 2003/07/18 21:27:42 "; | | 16 | static const char sccsid[] = "Id: v_txt.c,v 10.108 2003/07/18 21:27:42 skimo Exp (Berkeley) Date: 2003/07/18 21:27:42 "; |
17 | #endif /* not lint */ | | 17 | #endif /* not lint */ |
18 | #else | | 18 | #else |
19 | __RCSID("$NetBSD: v_txt.c,v 1.6 2018/08/07 08:05:47 rin Exp $"); | | 19 | __RCSID("$NetBSD: v_txt.c,v 1.7 2020/07/07 10:58:43 rin Exp $"); |
20 | #endif | | 20 | #endif |
21 | | | 21 | |
22 | #include <sys/types.h> | | 22 | #include <sys/types.h> |
23 | #include <sys/queue.h> | | 23 | #include <sys/queue.h> |
24 | #include <sys/stat.h> | | 24 | #include <sys/stat.h> |
25 | #include <sys/time.h> | | 25 | #include <sys/time.h> |
26 | | | 26 | |
27 | #include <bitstring.h> | | 27 | #include <bitstring.h> |
28 | #include <ctype.h> | | 28 | #include <ctype.h> |
29 | #include <errno.h> | | 29 | #include <errno.h> |
30 | #include <limits.h> | | 30 | #include <limits.h> |
31 | #include <stdio.h> | | 31 | #include <stdio.h> |
32 | #include <stdlib.h> | | 32 | #include <stdlib.h> |
33 | #include <string.h> | | 33 | #include <string.h> |
34 | #include <unistd.h> | | 34 | #include <unistd.h> |
35 | | | 35 | |
36 | #include "../common/common.h" | | 36 | #include "../common/common.h" |
37 | #include "vi.h" | | 37 | #include "vi.h" |
38 | | | 38 | |
39 | static int txt_abbrev __P((SCR *, TEXT *, ARG_CHAR_T *, int, int *, int *)); | | 39 | static int txt_abbrev __P((SCR *, TEXT *, ARG_CHAR_T *, int, int *, int *)); |
40 | static void txt_ai_resolve __P((SCR *, TEXT *, int *)); | | 40 | static void txt_ai_resolve __P((SCR *, TEXT *, int *)); |
41 | static TEXT *txt_backup __P((SCR *, TEXTH *, TEXT *, u_int32_t *)); | | 41 | static TEXT *txt_backup __P((SCR *, TEXTH *, TEXT *, u_int32_t *)); |
42 | static int txt_dent __P((SCR *, TEXT *, int)); | | 42 | static int txt_dent __P((SCR *, TEXT *, int)); |
43 | static int txt_emark __P((SCR *, TEXT *, size_t)); | | 43 | static int txt_emark __P((SCR *, TEXT *, size_t)); |
44 | static void txt_err __P((SCR *, TEXTH *)); | | 44 | static void txt_err __P((SCR *, TEXTH *)); |
45 | static int txt_fc __P((SCR *, TEXT *, int *)); | | 45 | static int txt_fc __P((SCR *, TEXT *, int *)); |
46 | static int txt_fc_col __P((SCR *, int, ARGS **)); | | 46 | static int txt_fc_col __P((SCR *, int, ARGS **)); |
47 | static int txt_hex __P((SCR *, TEXT *)); | | 47 | static int txt_hex __P((SCR *, TEXT *)); |
48 | static int txt_insch __P((SCR *, TEXT *, ARG_CHAR_T *, u_int)); | | 48 | static int txt_insch __P((SCR *, TEXT *, ARG_CHAR_T *, u_int)); |
49 | static int txt_isrch __P((SCR *, VICMD *, TEXT *, u_int8_t *)); | | 49 | static int txt_isrch __P((SCR *, VICMD *, TEXT *, u_int8_t *)); |
50 | static int txt_map_end __P((SCR *)); | | 50 | static int txt_map_end __P((SCR *)); |
51 | static int txt_map_init __P((SCR *)); | | 51 | static int txt_map_init __P((SCR *)); |
52 | static int txt_margin __P((SCR *, TEXT *, TEXT *, int *, u_int32_t)); | | 52 | static int txt_margin __P((SCR *, TEXT *, TEXT *, int *, u_int32_t)); |
53 | static void txt_nomorech __P((SCR *)); | | 53 | static void txt_nomorech __P((SCR *)); |
54 | static void txt_Rresolve __P((SCR *, TEXTH *, TEXT *, const size_t)); | | 54 | static void txt_Rresolve __P((SCR *, TEXTH *, TEXT *, const size_t)); |
55 | static int txt_resolve __P((SCR *, TEXTH *, u_int32_t)); | | 55 | static int txt_resolve __P((SCR *, TEXTH *, u_int32_t)); |
56 | static int txt_showmatch __P((SCR *, TEXT *)); | | 56 | static int txt_showmatch __P((SCR *, TEXT *)); |
57 | static void txt_unmap __P((SCR *, TEXT *, u_int32_t *)); | | 57 | static void txt_unmap __P((SCR *, TEXT *, u_int32_t *)); |
58 | | | 58 | |
59 | /* Cursor character (space is hard to track on the screen). */ | | 59 | /* Cursor character (space is hard to track on the screen). */ |
60 | #if defined(DEBUG) && 0 | | 60 | #if defined(DEBUG) && 0 |
61 | #undef CH_CURSOR | | 61 | #undef CH_CURSOR |
62 | #define CH_CURSOR '+' | | 62 | #define CH_CURSOR '+' |
63 | #endif | | 63 | #endif |
64 | | | 64 | |
65 | /* | | 65 | /* |
66 | * v_tcmd -- | | 66 | * v_tcmd -- |
67 | * Fill a buffer from the terminal for vi. | | 67 | * Fill a buffer from the terminal for vi. |
68 | * | | 68 | * |
69 | * PUBLIC: int v_tcmd __P((SCR *, VICMD *, ARG_CHAR_T, u_int)); | | 69 | * PUBLIC: int v_tcmd __P((SCR *, VICMD *, ARG_CHAR_T, u_int)); |
70 | */ | | 70 | */ |
71 | int | | 71 | int |
72 | v_tcmd(SCR *sp, VICMD *vp, ARG_CHAR_T prompt, u_int flags) | | 72 | v_tcmd(SCR *sp, VICMD *vp, ARG_CHAR_T prompt, u_int flags) |
73 | { | | 73 | { |
74 | /* Normally, we end up where we started. */ | | 74 | /* Normally, we end up where we started. */ |
75 | vp->m_final.lno = sp->lno; | | 75 | vp->m_final.lno = sp->lno; |
76 | vp->m_final.cno = sp->cno; | | 76 | vp->m_final.cno = sp->cno; |
77 | | | 77 | |
78 | /* Initialize the map. */ | | 78 | /* Initialize the map. */ |
79 | if (txt_map_init(sp)) | | 79 | if (txt_map_init(sp)) |
80 | return (1); | | 80 | return (1); |
81 | | | 81 | |
82 | /* Move to the last line. */ | | 82 | /* Move to the last line. */ |
83 | sp->lno = TMAP[0].lno; | | 83 | sp->lno = TMAP[0].lno; |
84 | sp->cno = 0; | | 84 | sp->cno = 0; |
85 | | | 85 | |
86 | /* Don't update the modeline for now. */ | | 86 | /* Don't update the modeline for now. */ |
87 | F_SET(sp, SC_TINPUT_INFO); | | 87 | F_SET(sp, SC_TINPUT_INFO); |
88 | | | 88 | |
89 | /* Set the input flags. */ | | 89 | /* Set the input flags. */ |
90 | LF_SET(TXT_APPENDEOL | | | 90 | LF_SET(TXT_APPENDEOL | |
91 | TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT); | | 91 | TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT); |
92 | if (O_ISSET(sp, O_ALTWERASE)) | | 92 | if (O_ISSET(sp, O_ALTWERASE)) |
93 | LF_SET(TXT_ALTWERASE); | | 93 | LF_SET(TXT_ALTWERASE); |
94 | if (O_ISSET(sp, O_TTYWERASE)) | | 94 | if (O_ISSET(sp, O_TTYWERASE)) |
95 | LF_SET(TXT_TTYWERASE); | | 95 | LF_SET(TXT_TTYWERASE); |
96 | | | 96 | |
97 | /* Do the input thing. */ | | 97 | /* Do the input thing. */ |
98 | if (v_txt(sp, vp, NULL, NULL, 0, prompt, 0, 1, flags)) | | 98 | if (v_txt(sp, vp, NULL, NULL, 0, prompt, 0, 1, flags)) |
99 | return (1); | | 99 | return (1); |
100 | | | 100 | |
101 | /* Reenable the modeline updates. */ | | 101 | /* Reenable the modeline updates. */ |
102 | F_CLR(sp, SC_TINPUT_INFO); | | 102 | F_CLR(sp, SC_TINPUT_INFO); |
103 | | | 103 | |
104 | /* Clean up the map. */ | | 104 | /* Clean up the map. */ |
105 | if (txt_map_end(sp)) | | 105 | if (txt_map_end(sp)) |
106 | return (1); | | 106 | return (1); |
107 | | | 107 | |
108 | if (IS_ONELINE(sp)) | | 108 | if (IS_ONELINE(sp)) |
109 | F_SET(sp, SC_SCR_REDRAW); /* XXX */ | | 109 | F_SET(sp, SC_SCR_REDRAW); /* XXX */ |
110 | | | 110 | |
111 | /* Set the cursor to the resulting position. */ | | 111 | /* Set the cursor to the resulting position. */ |
112 | sp->lno = vp->m_final.lno; | | 112 | sp->lno = vp->m_final.lno; |
113 | sp->cno = vp->m_final.cno; | | 113 | sp->cno = vp->m_final.cno; |
114 | | | 114 | |
115 | return (0); | | 115 | return (0); |
116 | } | | 116 | } |
117 | | | 117 | |
118 | /* | | 118 | /* |
119 | * txt_map_init | | 119 | * txt_map_init |
120 | * Initialize the screen map for colon command-line input. | | 120 | * Initialize the screen map for colon command-line input. |
121 | */ | | 121 | */ |
122 | static int | | 122 | static int |
123 | txt_map_init(SCR *sp) | | 123 | txt_map_init(SCR *sp) |
124 | { | | 124 | { |
125 | SMAP *esmp; | | 125 | SMAP *esmp; |
126 | VI_PRIVATE *vip; | | 126 | VI_PRIVATE *vip; |
127 | | | 127 | |
128 | vip = VIP(sp); | | 128 | vip = VIP(sp); |
129 | if (!IS_ONELINE(sp)) { | | 129 | if (!IS_ONELINE(sp)) { |
130 | /* | | 130 | /* |
131 | * Fake like the user is doing input on the last line of the | | 131 | * Fake like the user is doing input on the last line of the |
132 | * screen. This makes all of the scrolling work correctly, | | 132 | * screen. This makes all of the scrolling work correctly, |
133 | * and allows us the use of the vi text editing routines, not | | 133 | * and allows us the use of the vi text editing routines, not |
134 | * to mention practically infinite length ex commands. | | 134 | * to mention practically infinite length ex commands. |
135 | * | | 135 | * |
136 | * Save the current location. | | 136 | * Save the current location. |
137 | */ | | 137 | */ |
138 | vip->sv_tm_lno = TMAP->lno; | | 138 | vip->sv_tm_lno = TMAP->lno; |
139 | vip->sv_tm_soff = TMAP->soff; | | 139 | vip->sv_tm_soff = TMAP->soff; |
140 | vip->sv_tm_coff = TMAP->coff; | | 140 | vip->sv_tm_coff = TMAP->coff; |
141 | vip->sv_t_maxrows = sp->t_maxrows; | | 141 | vip->sv_t_maxrows = sp->t_maxrows; |
142 | vip->sv_t_minrows = sp->t_minrows; | | 142 | vip->sv_t_minrows = sp->t_minrows; |
143 | vip->sv_t_rows = sp->t_rows; | | 143 | vip->sv_t_rows = sp->t_rows; |
144 | | | 144 | |
145 | /* | | 145 | /* |
146 | * If it's a small screen, TMAP may be small for the screen. | | 146 | * If it's a small screen, TMAP may be small for the screen. |
147 | * Fix it, filling in fake lines as we go. | | 147 | * Fix it, filling in fake lines as we go. |
148 | */ | | 148 | */ |
149 | if (IS_SMALL(sp)) | | 149 | if (IS_SMALL(sp)) |
150 | for (esmp = | | 150 | for (esmp = |
151 | HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) { | | 151 | HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) { |
152 | TMAP[1].lno = TMAP[0].lno + 1; | | 152 | TMAP[1].lno = TMAP[0].lno + 1; |
153 | TMAP[1].coff = HMAP->coff; | | 153 | TMAP[1].coff = HMAP->coff; |
154 | TMAP[1].soff = 1; | | 154 | TMAP[1].soff = 1; |
155 | } | | 155 | } |
156 | | | 156 | |
157 | /* Build the fake entry. */ | | 157 | /* Build the fake entry. */ |
158 | TMAP[1].lno = TMAP[0].lno + 1; | | 158 | TMAP[1].lno = TMAP[0].lno + 1; |
159 | TMAP[1].soff = 1; | | 159 | TMAP[1].soff = 1; |
160 | TMAP[1].coff = 0; | | 160 | TMAP[1].coff = 0; |
161 | SMAP_FLUSH(&TMAP[1]); | | 161 | SMAP_FLUSH(&TMAP[1]); |
162 | ++TMAP; | | 162 | ++TMAP; |
163 | | | 163 | |
164 | /* Reset the screen information. */ | | 164 | /* Reset the screen information. */ |
165 | sp->t_rows = sp->t_minrows = ++sp->t_maxrows; | | 165 | sp->t_rows = sp->t_minrows = ++sp->t_maxrows; |
166 | } | | 166 | } |
167 | return (0); | | 167 | return (0); |
168 | } | | 168 | } |
169 | | | 169 | |
170 | /* | | 170 | /* |
171 | * txt_map_end | | 171 | * txt_map_end |
172 | * Reset the screen map for colon command-line input. | | 172 | * Reset the screen map for colon command-line input. |
173 | */ | | 173 | */ |
174 | static int | | 174 | static int |
175 | txt_map_end(SCR *sp) | | 175 | txt_map_end(SCR *sp) |
176 | { | | 176 | { |
177 | VI_PRIVATE *vip; | | 177 | VI_PRIVATE *vip; |
178 | size_t cnt; | | 178 | size_t cnt; |
179 | | | 179 | |
180 | vip = VIP(sp); | | 180 | vip = VIP(sp); |
181 | if (!IS_ONELINE(sp)) { | | 181 | if (!IS_ONELINE(sp)) { |
182 | /* Restore the screen information. */ | | 182 | /* Restore the screen information. */ |
183 | sp->t_rows = vip->sv_t_rows; | | 183 | sp->t_rows = vip->sv_t_rows; |
184 | sp->t_minrows = vip->sv_t_minrows; | | 184 | sp->t_minrows = vip->sv_t_minrows; |
185 | sp->t_maxrows = vip->sv_t_maxrows; | | 185 | sp->t_maxrows = vip->sv_t_maxrows; |
186 | | | 186 | |
187 | /* | | 187 | /* |
188 | * If it's a small screen, TMAP may be wrong. Clear any | | 188 | * If it's a small screen, TMAP may be wrong. Clear any |
189 | * lines that might have been overwritten. | | 189 | * lines that might have been overwritten. |
190 | */ | | 190 | */ |
191 | if (IS_SMALL(sp)) { | | 191 | if (IS_SMALL(sp)) { |
192 | for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) { | | 192 | for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) { |
193 | (void)sp->gp->scr_move(sp, cnt, 0); | | 193 | (void)sp->gp->scr_move(sp, cnt, 0); |
194 | (void)sp->gp->scr_clrtoeol(sp); | | 194 | (void)sp->gp->scr_clrtoeol(sp); |
195 | } | | 195 | } |
196 | TMAP = HMAP + (sp->t_rows - 1); | | 196 | TMAP = HMAP + (sp->t_rows - 1); |
197 | } else | | 197 | } else |
198 | --TMAP; | | 198 | --TMAP; |
199 | | | 199 | |
200 | /* | | 200 | /* |
201 | * The map may be wrong if the user entered more than one | | 201 | * The map may be wrong if the user entered more than one |
202 | * (logical) line. Fix it. If the user entered a whole | | 202 | * (logical) line. Fix it. If the user entered a whole |
203 | * screen, this will be slow, but we probably don't care. | | 203 | * screen, this will be slow, but we probably don't care. |
204 | */ | | 204 | */ |
205 | if (!O_ISSET(sp, O_LEFTRIGHT)) | | 205 | if (!O_ISSET(sp, O_LEFTRIGHT)) |
206 | while (vip->sv_tm_lno != TMAP->lno || | | 206 | while (vip->sv_tm_lno != TMAP->lno || |
207 | vip->sv_tm_soff != TMAP->soff) | | 207 | vip->sv_tm_soff != TMAP->soff) |
208 | if (vs_sm_1down(sp)) | | 208 | if (vs_sm_1down(sp)) |
209 | return (1); | | 209 | return (1); |
210 | } | | 210 | } |
211 | | | 211 | |
212 | /* | | 212 | /* |
213 | * Invalidate the cursor and the line size cache, the line never | | 213 | * Invalidate the cursor and the line size cache, the line never |
214 | * really existed. This fixes bugs where the user searches for | | 214 | * really existed. This fixes bugs where the user searches for |
215 | * the last line on the screen + 1 and the refresh routine thinks | | 215 | * the last line on the screen + 1 and the refresh routine thinks |
216 | * that's where we just were. | | 216 | * that's where we just were. |
217 | */ | | 217 | */ |
218 | VI_SCR_CFLUSH(vip); | | 218 | VI_SCR_CFLUSH(vip); |
219 | F_SET(vip, VIP_CUR_INVALID); | | 219 | F_SET(vip, VIP_CUR_INVALID); |
220 | | | 220 | |
221 | return (0); | | 221 | return (0); |
222 | } | | 222 | } |
223 | | | 223 | |
224 | /* | | 224 | /* |
225 | * If doing input mapping on the colon command line, may need to unmap | | 225 | * If doing input mapping on the colon command line, may need to unmap |
226 | * based on the command. | | 226 | * based on the command. |
227 | */ | | 227 | */ |
228 | #define UNMAP_TST \ | | 228 | #define UNMAP_TST \ |
229 | FL_ISSET(ec_flags, EC_MAPINPUT) && LF_ISSET(TXT_INFOLINE) | | 229 | FL_ISSET(ec_flags, EC_MAPINPUT) && LF_ISSET(TXT_INFOLINE) |
230 | | | 230 | |
231 | /* | | 231 | /* |
232 | * Internally, we maintain tp->lno and tp->cno, externally, everyone uses | | 232 | * Internally, we maintain tp->lno and tp->cno, externally, everyone uses |
233 | * sp->lno and sp->cno. Make them consistent as necessary. | | 233 | * sp->lno and sp->cno. Make them consistent as necessary. |
234 | */ | | 234 | */ |
235 | #define UPDATE_POSITION(sp, tp) { \ | | 235 | #define UPDATE_POSITION(sp, tp) { \ |
236 | (sp)->lno = (tp)->lno; \ | | 236 | (sp)->lno = (tp)->lno; \ |
237 | (sp)->cno = (tp)->cno; \ | | 237 | (sp)->cno = (tp)->cno; \ |
238 | } | | 238 | } |
239 | | | 239 | |
240 | /* | | 240 | /* |
241 | * v_txt -- | | 241 | * v_txt -- |
242 | * Vi text input. | | 242 | * Vi text input. |
243 | * | | 243 | * |
244 | * PUBLIC: int v_txt __P((SCR *, VICMD *, MARK *, | | 244 | * PUBLIC: int v_txt __P((SCR *, VICMD *, MARK *, |
245 | * PUBLIC: const CHAR_T *, size_t, ARG_CHAR_T, db_recno_t, u_long, u_int32_t)); | | 245 | * PUBLIC: const CHAR_T *, size_t, ARG_CHAR_T, db_recno_t, u_long, u_int32_t)); |
246 | */ | | 246 | */ |
247 | int | | 247 | int |
248 | v_txt(SCR *sp, VICMD *vp, MARK *tm, const CHAR_T *lp, size_t len, ARG_CHAR_T prompt, db_recno_t ai_line, u_long rcount, u_int32_t flags) | | 248 | v_txt(SCR *sp, VICMD *vp, MARK *tm, const CHAR_T *lp, size_t len, ARG_CHAR_T prompt, db_recno_t ai_line, u_long rcount, u_int32_t flags) |
249 | | | 249 | |
250 | | | 250 | |
251 | /* To MARK. */ | | 251 | /* To MARK. */ |
252 | /* Input line. */ | | 252 | /* Input line. */ |
253 | /* Input line length. */ | | 253 | /* Input line length. */ |
254 | /* Prompt to display. */ | | 254 | /* Prompt to display. */ |
255 | /* Line number to use for autoindent count. */ | | 255 | /* Line number to use for autoindent count. */ |
256 | /* Replay count. */ | | 256 | /* Replay count. */ |
257 | /* TXT_* flags. */ | | 257 | /* TXT_* flags. */ |
258 | { | | 258 | { |
259 | EVENT ev, *evp = NULL; /* Current event. */ | | 259 | EVENT ev, *evp = NULL; /* Current event. */ |
260 | EVENT fc; /* File name completion event. */ | | 260 | EVENT fc; /* File name completion event. */ |
261 | GS *gp; | | 261 | GS *gp; |
262 | TEXT *ntp, *tp; /* Input text structures. */ | | 262 | TEXT *ntp, *tp; /* Input text structures. */ |
263 | TEXT ait; /* Autoindent text structure. */ | | 263 | TEXT ait; /* Autoindent text structure. */ |
264 | TEXT wmt; /* Wrapmargin text structure. */ | | 264 | TEXT wmt; /* Wrapmargin text structure. */ |
265 | TEXTH *tiqh; | | 265 | TEXTH *tiqh; |
266 | VI_PRIVATE *vip; | | 266 | VI_PRIVATE *vip; |
267 | abb_t abb; /* State of abbreviation checks. */ | | 267 | abb_t abb; /* State of abbreviation checks. */ |
268 | carat_t carat; /* State of the "[^0]^D" sequences. */ | | 268 | carat_t carat; /* State of the "[^0]^D" sequences. */ |
269 | quote_t quote; /* State of quotation. */ | | 269 | quote_t quote; /* State of quotation. */ |
270 | size_t owrite, insert; /* Temporary copies of TEXT fields. */ | | 270 | size_t owrite, insert; /* Temporary copies of TEXT fields. */ |
271 | size_t margin; /* Wrapmargin value. */ | | 271 | size_t margin; /* Wrapmargin value. */ |
272 | size_t rcol; /* 0-N: insert offset in the replay buffer. */ | | 272 | size_t rcol; /* 0-N: insert offset in the replay buffer. */ |
273 | size_t tcol; /* Temporary column. */ | | 273 | size_t tcol; /* Temporary column. */ |
274 | u_int32_t ec_flags; /* Input mapping flags. */ | | 274 | u_int32_t ec_flags; /* Input mapping flags. */ |
275 | #define IS_RESTART 0x01 /* Reset the incremental search. */ | | 275 | #define IS_RESTART 0x01 /* Reset the incremental search. */ |
276 | #define IS_RUNNING 0x02 /* Incremental search turned on. */ | | 276 | #define IS_RUNNING 0x02 /* Incremental search turned on. */ |
277 | u_int8_t is_flags; | | 277 | u_int8_t is_flags; |
278 | int abcnt, ab_turnoff; /* Abbreviation character count, switch. */ | | 278 | int abcnt, ab_turnoff; /* Abbreviation character count, switch. */ |
279 | int filec_redraw; /* Redraw after the file completion routine. */ | | 279 | int filec_redraw; /* Redraw after the file completion routine. */ |
280 | int hexcnt; /* Hex character count. */ | | 280 | int hexcnt; /* Hex character count. */ |
281 | int showmatch; /* Showmatch set on this character. */ | | 281 | int showmatch; /* Showmatch set on this character. */ |
282 | int wm_set, wm_skip; /* Wrapmargin happened, blank skip flags. */ | | 282 | int wm_set, wm_skip; /* Wrapmargin happened, blank skip flags. */ |
283 | size_t max; | | 283 | size_t max; |
284 | int tmp; | | 284 | int tmp; |
285 | int nochange; | | 285 | int nochange; |
286 | CHAR_T *p; | | 286 | CHAR_T *p; |
287 | | | 287 | |
288 | gp = sp->gp; | | 288 | gp = sp->gp; |
289 | vip = VIP(sp); | | 289 | vip = VIP(sp); |
290 | memset(&wmt, 0, sizeof(wmt)); | | 290 | memset(&wmt, 0, sizeof(wmt)); |
291 | | | 291 | |
292 | /* | | 292 | /* |
293 | * Set the input flag, so tabs get displayed correctly | | 293 | * Set the input flag, so tabs get displayed correctly |
294 | * and everyone knows that the text buffer is in use. | | 294 | * and everyone knows that the text buffer is in use. |
295 | */ | | 295 | */ |
296 | F_SET(sp, SC_TINPUT); | | 296 | F_SET(sp, SC_TINPUT); |
297 | | | 297 | |
298 | /* | | 298 | /* |
299 | * Get one TEXT structure with some initial buffer space, reusing | | 299 | * Get one TEXT structure with some initial buffer space, reusing |
300 | * the last one if it's big enough. (All TEXT bookkeeping fields | | 300 | * the last one if it's big enough. (All TEXT bookkeeping fields |
301 | * default to 0 -- text_init() handles this.) If changing a line, | | 301 | * default to 0 -- text_init() handles this.) If changing a line, |
302 | * copy it into the TEXT buffer. | | 302 | * copy it into the TEXT buffer. |
303 | */ | | 303 | */ |
304 | tiqh = &sp->tiq; | | 304 | tiqh = &sp->tiq; |
305 | if (!TAILQ_EMPTY(tiqh)) { | | 305 | if (!TAILQ_EMPTY(tiqh)) { |
306 | tp = TAILQ_FIRST(tiqh); | | 306 | tp = TAILQ_FIRST(tiqh); |
307 | if (TAILQ_NEXT(tp, q) != NULL || tp->lb_len < len + 32) { | | 307 | if (TAILQ_NEXT(tp, q) != NULL || tp->lb_len < len + 32) { |
308 | text_lfree(tiqh); | | 308 | text_lfree(tiqh); |
309 | goto newtp; | | 309 | goto newtp; |
310 | } | | 310 | } |
311 | tp->ai = tp->insert = tp->offset = tp->owrite = 0; | | 311 | tp->ai = tp->insert = tp->offset = tp->owrite = 0; |
312 | if (lp != NULL) { | | 312 | if (lp != NULL) { |
313 | tp->len = len; | | 313 | tp->len = len; |
314 | BINC_RETW(sp, tp->lb, tp->lb_len, len); | | 314 | BINC_RETW(sp, tp->lb, tp->lb_len, len); |
315 | MEMMOVEW(tp->lb, lp, len); | | 315 | MEMMOVEW(tp->lb, lp, len); |
316 | } else | | 316 | } else |
317 | tp->len = 0; | | 317 | tp->len = 0; |
318 | } else { | | 318 | } else { |
319 | newtp: if ((tp = text_init(sp, lp, len, len + 32)) == NULL) | | 319 | newtp: if ((tp = text_init(sp, lp, len, len + 32)) == NULL) |
320 | return (1); | | 320 | return (1); |
321 | TAILQ_INSERT_HEAD(tiqh, tp, q); | | 321 | TAILQ_INSERT_HEAD(tiqh, tp, q); |
322 | } | | 322 | } |
323 | | | 323 | |
324 | /* Set default termination condition. */ | | 324 | /* Set default termination condition. */ |
325 | tp->term = TERM_OK; | | 325 | tp->term = TERM_OK; |
326 | | | 326 | |
327 | /* Set the starting line, column. */ | | 327 | /* Set the starting line, column. */ |
328 | tp->lno = sp->lno; | | 328 | tp->lno = sp->lno; |
329 | tp->cno = sp->cno; | | 329 | tp->cno = sp->cno; |
330 | | | 330 | |
331 | /* | | 331 | /* |
332 | * Set the insert and overwrite counts. If overwriting characters, | | 332 | * Set the insert and overwrite counts. If overwriting characters, |
333 | * do insertion afterward. If not overwriting characters, assume | | 333 | * do insertion afterward. If not overwriting characters, assume |
334 | * doing insertion. If change is to a mark, emphasize it with an | | 334 | * doing insertion. If change is to a mark, emphasize it with an |
335 | * CH_ENDMARK character. | | 335 | * CH_ENDMARK character. |
336 | */ | | 336 | */ |
337 | if (len) { | | 337 | if (len) { |
338 | if (LF_ISSET(TXT_OVERWRITE)) { | | 338 | if (LF_ISSET(TXT_OVERWRITE)) { |
339 | tp->owrite = (tm->cno - tp->cno) + 1; | | 339 | tp->owrite = (tm->cno - tp->cno) + 1; |
340 | tp->insert = (len - tm->cno) - 1; | | 340 | tp->insert = (len - tm->cno) - 1; |
341 | } else | | 341 | } else |
342 | tp->insert = len - tp->cno; | | 342 | tp->insert = len - tp->cno; |
343 | | | 343 | |
344 | if (LF_ISSET(TXT_EMARK) && txt_emark(sp, tp, tm->cno)) | | 344 | if (LF_ISSET(TXT_EMARK) && txt_emark(sp, tp, tm->cno)) |
345 | return (1); | | 345 | return (1); |
346 | } | | 346 | } |
347 | | | 347 | |
348 | /* | | 348 | /* |
349 | * Many of the special cases in text input are to handle autoindent | | 349 | * Many of the special cases in text input are to handle autoindent |
350 | * support. Somebody decided that it would be a good idea if "^^D" | | 350 | * support. Somebody decided that it would be a good idea if "^^D" |
351 | * and "0^D" deleted all of the autoindented characters. In an editor | | 351 | * and "0^D" deleted all of the autoindented characters. In an editor |
352 | * that takes single character input from the user, this beggars the | | 352 | * that takes single character input from the user, this beggars the |
353 | * imagination. Note also, "^^D" resets the next lines' autoindent, | | 353 | * imagination. Note also, "^^D" resets the next lines' autoindent, |
354 | * but "0^D" doesn't. | | 354 | * but "0^D" doesn't. |
355 | * | | 355 | * |
356 | * We assume that autoindent only happens on empty lines, so insert | | 356 | * We assume that autoindent only happens on empty lines, so insert |
357 | * and overwrite will be zero. If doing autoindent, figure out how | | 357 | * and overwrite will be zero. If doing autoindent, figure out how |
358 | * much indentation we need and fill it in. Update input column and | | 358 | * much indentation we need and fill it in. Update input column and |
359 | * screen cursor as necessary. | | 359 | * screen cursor as necessary. |
360 | */ | | 360 | */ |
361 | if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) { | | 361 | if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) { |
362 | if (v_txt_auto(sp, ai_line, NULL, 0, tp)) | | 362 | if (v_txt_auto(sp, ai_line, NULL, 0, tp)) |
363 | return (1); | | 363 | return (1); |
364 | tp->cno = tp->ai; | | 364 | tp->cno = tp->ai; |
365 | } else { | | 365 | } else { |
366 | /* | | 366 | /* |
367 | * The cc and S commands have a special feature -- leading | | 367 | * The cc and S commands have a special feature -- leading |
368 | * <blank> characters are handled as autoindent characters. | | 368 | * <blank> characters are handled as autoindent characters. |
369 | * Beauty! | | 369 | * Beauty! |
370 | */ | | 370 | */ |
371 | if (LF_ISSET(TXT_AICHARS)) { | | 371 | if (LF_ISSET(TXT_AICHARS)) { |
372 | tp->offset = 0; | | 372 | tp->offset = 0; |
373 | tp->ai = tp->cno; | | 373 | tp->ai = tp->cno; |
374 | } else | | 374 | } else |
375 | tp->offset = tp->cno; | | 375 | tp->offset = tp->cno; |
376 | } | | 376 | } |
377 | | | 377 | |
378 | /* If getting a command buffer from the user, there may be a prompt. */ | | 378 | /* If getting a command buffer from the user, there may be a prompt. */ |
379 | if (LF_ISSET(TXT_PROMPT)) { | | 379 | if (LF_ISSET(TXT_PROMPT)) { |
380 | tp->lb[tp->cno++] = prompt; | | 380 | tp->lb[tp->cno++] = prompt; |
381 | ++tp->len; | | 381 | ++tp->len; |
382 | ++tp->offset; | | 382 | ++tp->offset; |
383 | } | | 383 | } |
384 | | | 384 | |
385 | /* | | 385 | /* |
386 | * If appending after the end-of-line, add a space into the buffer | | 386 | * If appending after the end-of-line, add a space into the buffer |
387 | * and move the cursor right. This space is inserted, i.e. pushed | | 387 | * and move the cursor right. This space is inserted, i.e. pushed |
388 | * along, and then deleted when the line is resolved. Assumes that | | 388 | * along, and then deleted when the line is resolved. Assumes that |
389 | * the cursor is already positioned at the end of the line. This | | 389 | * the cursor is already positioned at the end of the line. This |
390 | * avoids the nastiness of having the cursor reside on a magical | | 390 | * avoids the nastiness of having the cursor reside on a magical |
391 | * column, i.e. a column that doesn't really exist. The only down | | 391 | * column, i.e. a column that doesn't really exist. The only down |
392 | * side is that we may wrap lines or scroll the screen before it's | | 392 | * side is that we may wrap lines or scroll the screen before it's |
393 | * strictly necessary. Not a big deal. | | 393 | * strictly necessary. Not a big deal. |
394 | */ | | 394 | */ |
395 | if (LF_ISSET(TXT_APPENDEOL)) { | | 395 | if (LF_ISSET(TXT_APPENDEOL)) { |
396 | tp->lb[tp->cno] = CH_CURSOR; | | 396 | tp->lb[tp->cno] = CH_CURSOR; |
397 | ++tp->len; | | 397 | ++tp->len; |
398 | ++tp->insert; | | 398 | ++tp->insert; |
399 | (void)vs_change(sp, tp->lno, LINE_RESET); | | 399 | (void)vs_change(sp, tp->lno, LINE_RESET); |
400 | } | | 400 | } |
401 | | | 401 | |
402 | /* | | 402 | /* |
403 | * Historic practice is that the wrapmargin value was a distance | | 403 | * Historic practice is that the wrapmargin value was a distance |
404 | * from the RIGHT-HAND margin, not the left. It's more useful to | | 404 | * from the RIGHT-HAND margin, not the left. It's more useful to |
405 | * us as a distance from the left-hand margin, i.e. the same as | | 405 | * us as a distance from the left-hand margin, i.e. the same as |
406 | * the wraplen value. The wrapmargin option is historic practice. | | 406 | * the wraplen value. The wrapmargin option is historic practice. |
407 | * Nvi added the wraplen option so that it would be possible to | | 407 | * Nvi added the wraplen option so that it would be possible to |
408 | * edit files with consistent margins without knowing the number of | | 408 | * edit files with consistent margins without knowing the number of |
409 | * columns in the window. | | 409 | * columns in the window. |
410 | * | | 410 | * |
411 | * XXX | | 411 | * XXX |
412 | * Setting margin causes a significant performance hit. Normally | | 412 | * Setting margin causes a significant performance hit. Normally |
413 | * we don't update the screen if there are keys waiting, but we | | 413 | * we don't update the screen if there are keys waiting, but we |
414 | * have to if margin is set, otherwise the screen routines don't | | 414 | * have to if margin is set, otherwise the screen routines don't |
415 | * know where the cursor is. | | 415 | * know where the cursor is. |
416 | * | | 416 | * |
417 | * !!! | | 417 | * !!! |
418 | * Abbreviated keys were affected by the wrapmargin option in the | | 418 | * Abbreviated keys were affected by the wrapmargin option in the |
419 | * historic 4BSD vi. Mapped keys were usually, but sometimes not. | | 419 | * historic 4BSD vi. Mapped keys were usually, but sometimes not. |
420 | * See the comment in vi/v_text():set_txt_std for more information. | | 420 | * See the comment in vi/v_text():set_txt_std for more information. |
421 | * | | 421 | * |
422 | * !!! | | 422 | * !!! |
423 | * One more special case. If an inserted <blank> character causes | | 423 | * One more special case. If an inserted <blank> character causes |
424 | * wrapmargin to split the line, the next user entered character is | | 424 | * wrapmargin to split the line, the next user entered character is |
425 | * discarded if it's a <space> character. | | 425 | * discarded if it's a <space> character. |
426 | */ | | 426 | */ |
427 | wm_set = wm_skip = 0; | | 427 | wm_set = wm_skip = 0; |
428 | if (LF_ISSET(TXT_WRAPMARGIN)) | | 428 | if (LF_ISSET(TXT_WRAPMARGIN)) |
429 | if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0) | | 429 | if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0) |
430 | margin = sp->cols - margin; | | 430 | margin = sp->cols - margin; |
431 | else | | 431 | else |
432 | margin = O_VAL(sp, O_WRAPLEN); | | 432 | margin = O_VAL(sp, O_WRAPLEN); |
433 | else | | 433 | else |
434 | margin = 0; | | 434 | margin = 0; |
435 | | | 435 | |
436 | /* Initialize abbreviation checks. */ | | 436 | /* Initialize abbreviation checks. */ |
437 | abcnt = ab_turnoff = 0; | | 437 | abcnt = ab_turnoff = 0; |
438 | abb = F_ISSET(gp, G_ABBREV) && | | 438 | abb = F_ISSET(gp, G_ABBREV) && |
439 | LF_ISSET(TXT_MAPINPUT) ? AB_INWORD : AB_NOTSET; | | 439 | LF_ISSET(TXT_MAPINPUT) ? AB_INWORD : AB_NOTSET; |
440 | | | 440 | |
441 | /* | | 441 | /* |
442 | * Set up the dot command. Dot commands are done by saving the actual | | 442 | * Set up the dot command. Dot commands are done by saving the actual |
443 | * characters and then reevaluating them so that things like wrapmargin | | 443 | * characters and then reevaluating them so that things like wrapmargin |
444 | * can change between the insert and the replay. | | 444 | * can change between the insert and the replay. |
445 | * | | 445 | * |
446 | * !!! | | 446 | * !!! |
447 | * Historically, vi did not remap or reabbreviate replayed input. (It | | 447 | * Historically, vi did not remap or reabbreviate replayed input. (It |
448 | * did beep at you if you changed an abbreviation and then replayed the | | 448 | * did beep at you if you changed an abbreviation and then replayed the |
449 | * input. We're not that compatible.) We don't have to do anything to | | 449 | * input. We're not that compatible.) We don't have to do anything to |
450 | * avoid remapping, as we're not getting characters from the terminal | | 450 | * avoid remapping, as we're not getting characters from the terminal |
451 | * routines. Turn the abbreviation check off. | | 451 | * routines. Turn the abbreviation check off. |
452 | * | | 452 | * |
453 | * XXX | | 453 | * XXX |
454 | * It would be nice if we could swallow backspaces and such, but it's | | 454 | * It would be nice if we could swallow backspaces and such, but it's |
455 | * not all that easy to do. What we can do is turn off the common | | 455 | * not all that easy to do. What we can do is turn off the common |
456 | * error messages during the replay. Otherwise, when the user enters | | 456 | * error messages during the replay. Otherwise, when the user enters |
457 | * an illegal command, e.g., "Ia<erase><erase><erase><erase>b<escape>", | | 457 | * an illegal command, e.g., "Ia<erase><erase><erase><erase>b<escape>", |
458 | * and then does a '.', they get a list of error messages after command | | 458 | * and then does a '.', they get a list of error messages after command |
459 | * completion. | | 459 | * completion. |
460 | */ | | 460 | */ |
461 | rcol = 0; | | 461 | rcol = 0; |
462 | if (LF_ISSET(TXT_REPLAY)) { | | 462 | if (LF_ISSET(TXT_REPLAY)) { |
463 | abb = AB_NOTSET; | | 463 | abb = AB_NOTSET; |
464 | LF_CLR(TXT_RECORD); | | 464 | LF_CLR(TXT_RECORD); |
465 | } | | 465 | } |
466 | | | 466 | |
467 | /* Other text input mode setup. */ | | 467 | /* Other text input mode setup. */ |
468 | quote = Q_NOTSET; | | 468 | quote = Q_NOTSET; |
469 | carat = C_NOTSET; | | 469 | carat = C_NOTSET; |
470 | nochange = 0; | | 470 | nochange = 0; |
471 | FL_INIT(is_flags, | | 471 | FL_INIT(is_flags, |
472 | LF_ISSET(TXT_SEARCHINCR) ? IS_RESTART | IS_RUNNING : 0); | | 472 | LF_ISSET(TXT_SEARCHINCR) ? IS_RESTART | IS_RUNNING : 0); |
473 | filec_redraw = hexcnt = showmatch = 0; | | 473 | filec_redraw = hexcnt = showmatch = 0; |
474 | | | 474 | |
475 | /* Initialize input flags. */ | | 475 | /* Initialize input flags. */ |
476 | ec_flags = LF_ISSET(TXT_MAPINPUT) ? EC_MAPINPUT : 0; | | 476 | ec_flags = LF_ISSET(TXT_MAPINPUT) ? EC_MAPINPUT : 0; |
477 | | | 477 | |
478 | /* Refresh the screen. */ | | 478 | /* Refresh the screen. */ |
479 | UPDATE_POSITION(sp, tp); | | 479 | UPDATE_POSITION(sp, tp); |
480 | if (vs_refresh(sp, 1)) | | 480 | if (vs_refresh(sp, 1)) |
481 | return (1); | | 481 | return (1); |
482 | | | 482 | |
483 | /* If it's dot, just do it now. */ | | 483 | /* If it's dot, just do it now. */ |
484 | if (F_ISSET(vp, VC_ISDOT)) | | 484 | if (F_ISSET(vp, VC_ISDOT)) |
485 | goto replay; | | 485 | goto replay; |
486 | | | 486 | |
487 | /* Get an event. */ | | 487 | /* Get an event. */ |
488 | evp = &ev; | | 488 | evp = &ev; |
489 | next: if (v_event_get(sp, evp, 0, ec_flags)) | | 489 | next: if (v_event_get(sp, evp, 0, ec_flags)) |
490 | return (1); | | 490 | return (1); |
491 | | | 491 | |
492 | /* | | 492 | /* |
493 | * If file completion overwrote part of the screen and nothing else has | | 493 | * If file completion overwrote part of the screen and nothing else has |
494 | * been displayed, clean up. We don't do this as part of the normal | | 494 | * been displayed, clean up. We don't do this as part of the normal |
495 | * message resolution because we know the user is on the colon command | | 495 | * message resolution because we know the user is on the colon command |
496 | * line and there's no reason to enter explicit characters to continue. | | 496 | * line and there's no reason to enter explicit characters to continue. |
497 | */ | | 497 | */ |
498 | if (filec_redraw && !F_ISSET(sp, SC_SCR_EXWROTE)) { | | 498 | if (filec_redraw && !F_ISSET(sp, SC_SCR_EXWROTE)) { |
499 | filec_redraw = 0; | | 499 | filec_redraw = 0; |
500 | | | 500 | |
501 | fc.e_event = E_REPAINT; | | 501 | fc.e_event = E_REPAINT; |
502 | fc.e_flno = vip->totalcount >= | | 502 | fc.e_flno = vip->totalcount >= |
503 | sp->rows ? 1 : sp->rows - vip->totalcount; | | 503 | sp->rows ? 1 : sp->rows - vip->totalcount; |
504 | fc.e_tlno = sp->rows; | | 504 | fc.e_tlno = sp->rows; |
505 | vip->linecount = vip->lcontinue = vip->totalcount = 0; | | 505 | vip->linecount = vip->lcontinue = vip->totalcount = 0; |
506 | (void)v_erepaint(sp, &fc); | | 506 | (void)v_erepaint(sp, &fc); |
507 | (void)vs_refresh(sp, 1); | | 507 | (void)vs_refresh(sp, 1); |
508 | } | | 508 | } |
509 | | | 509 | |
510 | /* Deal with all non-character events. */ | | 510 | /* Deal with all non-character events. */ |
511 | switch (evp->e_event) { | | 511 | switch (evp->e_event) { |
512 | case E_CHARACTER: | | 512 | case E_CHARACTER: |
513 | break; | | 513 | break; |
514 | case E_ERR: | | 514 | case E_ERR: |
515 | case E_EOF: | | 515 | case E_EOF: |
516 | F_SET(sp, SC_EXIT_FORCE); | | 516 | F_SET(sp, SC_EXIT_FORCE); |
517 | return (1); | | 517 | return (1); |
518 | case E_INTERRUPT: | | 518 | case E_INTERRUPT: |
519 | /* | | 519 | /* |
520 | * !!! | | 520 | * !!! |
521 | * Historically, <interrupt> exited the user from text input | | 521 | * Historically, <interrupt> exited the user from text input |
522 | * mode or cancelled a colon command, and returned to command | | 522 | * mode or cancelled a colon command, and returned to command |
523 | * mode. It also beeped the terminal, but that seems a bit | | 523 | * mode. It also beeped the terminal, but that seems a bit |
524 | * excessive. | | 524 | * excessive. |
525 | */ | | 525 | */ |
526 | goto k_escape; | | 526 | goto k_escape; |
527 | case E_REPAINT: | | 527 | case E_REPAINT: |
528 | if (v_erepaint(sp, &ev)) | | 528 | if (v_erepaint(sp, &ev)) |
529 | return (1); | | 529 | return (1); |
530 | goto next; | | 530 | goto next; |
531 | case E_WRESIZE: | | 531 | case E_WRESIZE: |
532 | /* <resize> interrupts the input mode. */ | | 532 | /* <resize> interrupts the input mode. */ |
533 | #ifdef IMCTRL | | 533 | #ifdef IMCTRL |
534 | sp->gp->scr_imctrl(sp, IMCTRL_OFF); | | 534 | sp->gp->scr_imctrl(sp, IMCTRL_OFF); |
535 | #endif | | 535 | #endif |
536 | v_emsg(sp, NULL, VIM_WRESIZE); | | 536 | v_emsg(sp, NULL, VIM_WRESIZE); |
537 | goto k_escape; | | 537 | goto k_escape; |
538 | default: | | 538 | default: |
539 | v_event_err(sp, evp); | | 539 | v_event_err(sp, evp); |
540 | goto k_escape; | | 540 | goto k_escape; |
541 | } | | 541 | } |
542 | | | 542 | |
543 | /* | | 543 | /* |
544 | * !!! | | 544 | * !!! |
545 | * If the first character of the input is a nul, replay the previous | | 545 | * If the first character of the input is a nul, replay the previous |
546 | * input. (Historically, it's okay to replay non-existent input.) | | 546 | * input. (Historically, it's okay to replay non-existent input.) |
547 | * This was not documented as far as I know, and is a great test of vi | | 547 | * This was not documented as far as I know, and is a great test of vi |
548 | * clones. | | 548 | * clones. |
549 | */ | | 549 | */ |
550 | if (rcol == 0 && !LF_ISSET(TXT_REPLAY) && evp->e_c == '\0') { | | 550 | if (rcol == 0 && !LF_ISSET(TXT_REPLAY) && evp->e_c == '\0') { |
551 | if (vip->rep == NULL) | | 551 | if (vip->rep == NULL) |
552 | goto done; | | 552 | goto done; |
553 | | | 553 | |
554 | abb = AB_NOTSET; | | 554 | abb = AB_NOTSET; |
555 | LF_CLR(TXT_RECORD); | | 555 | LF_CLR(TXT_RECORD); |
556 | LF_SET(TXT_REPLAY); | | 556 | LF_SET(TXT_REPLAY); |
557 | goto replay; | | 557 | goto replay; |
558 | } | | 558 | } |
559 | | | 559 | |
560 | /* | | 560 | /* |
561 | * File name completion and colon command-line editing. We don't | | 561 | * File name completion and colon command-line editing. We don't |
562 | * have enough meta characters, so we expect people to overload | | 562 | * have enough meta characters, so we expect people to overload |
563 | * them. If the two characters are the same, then we do file name | | 563 | * them. If the two characters are the same, then we do file name |
564 | * completion if the cursor is past the first column, and do colon | | 564 | * completion if the cursor is past the first column, and do colon |
565 | * command-line editing if it's not. | | 565 | * command-line editing if it's not. |
566 | */ | | 566 | */ |
567 | if (quote == Q_NOTSET) { | | 567 | if (quote == Q_NOTSET) { |
568 | int L__cedit, L__filec; | | 568 | int L__cedit, L__filec; |
569 | | | 569 | |
570 | L__cedit = L__filec = 0; | | 570 | L__cedit = L__filec = 0; |
571 | if (LF_ISSET(TXT_CEDIT) && O_STR(sp, O_CEDIT) != NULL && | | 571 | if (LF_ISSET(TXT_CEDIT) && O_STR(sp, O_CEDIT) != NULL && |
572 | O_STR(sp, O_CEDIT)[0] == evp->e_c) | | 572 | O_STR(sp, O_CEDIT)[0] == evp->e_c) |
573 | L__cedit = 1; | | 573 | L__cedit = 1; |
574 | if (LF_ISSET(TXT_FILEC) && O_STR(sp, O_FILEC) != NULL && | | 574 | if (LF_ISSET(TXT_FILEC) && O_STR(sp, O_FILEC) != NULL && |
575 | O_STR(sp, O_FILEC)[0] == evp->e_c) | | 575 | O_STR(sp, O_FILEC)[0] == evp->e_c) |
576 | L__filec = 1; | | 576 | L__filec = 1; |
577 | if (L__cedit == 1 && (L__filec == 0 || tp->cno == tp->offset)) { | | 577 | if (L__cedit == 1 && (L__filec == 0 || tp->cno == tp->offset)) { |
578 | tp->term = TERM_CEDIT; | | 578 | tp->term = TERM_CEDIT; |
579 | goto k_escape; | | 579 | goto k_escape; |
580 | } | | 580 | } |
581 | if (L__filec == 1) { | | 581 | if (L__filec == 1) { |
582 | if (txt_fc(sp, tp, &filec_redraw)) | | 582 | if (txt_fc(sp, tp, &filec_redraw)) |
583 | goto err; | | 583 | goto err; |
584 | goto resolve; | | 584 | goto resolve; |
585 | } | | 585 | } |
586 | } | | 586 | } |
587 | | | 587 | |
588 | /* Abbreviation overflow check. See comment in txt_abbrev(). */ | | 588 | /* Abbreviation overflow check. See comment in txt_abbrev(). */ |
589 | #define MAX_ABBREVIATION_EXPANSION 256 | | 589 | #define MAX_ABBREVIATION_EXPANSION 256 |
590 | if (FL_ISSET(evp->e_flags, CH_ABBREVIATED)) { | | 590 | if (FL_ISSET(evp->e_flags, CH_ABBREVIATED)) { |
591 | if (++abcnt > MAX_ABBREVIATION_EXPANSION) { | | 591 | if (++abcnt > MAX_ABBREVIATION_EXPANSION) { |
592 | if (v_event_flush(sp, CH_ABBREVIATED)) | | 592 | if (v_event_flush(sp, CH_ABBREVIATED)) |
593 | msgq(sp, M_ERR, | | 593 | msgq(sp, M_ERR, |
594 | "191|Abbreviation exceeded expansion limit: characters discarded"); | | 594 | "191|Abbreviation exceeded expansion limit: characters discarded"); |
595 | abcnt = 0; | | 595 | abcnt = 0; |
596 | if (LF_ISSET(TXT_REPLAY)) | | 596 | if (LF_ISSET(TXT_REPLAY)) |
597 | goto done; | | 597 | goto done; |
598 | goto resolve; | | 598 | goto resolve; |
599 | } | | 599 | } |
600 | } else | | 600 | } else |
601 | abcnt = 0; | | 601 | abcnt = 0; |
602 | | | 602 | |
603 | /* Check to see if the character fits into the replay buffers. */ | | 603 | /* Check to see if the character fits into the replay buffers. */ |
604 | if (LF_ISSET(TXT_RECORD)) { | | 604 | if (LF_ISSET(TXT_RECORD)) { |
605 | BINC_GOTO(sp, EVENT, vip->rep, | | 605 | BINC_GOTO(sp, EVENT, vip->rep, |
606 | vip->rep_len, (rcol + 1) * sizeof(EVENT)); | | 606 | vip->rep_len, (rcol + 1) * sizeof(EVENT)); |
607 | vip->rep[rcol++] = *evp; | | 607 | vip->rep[rcol++] = *evp; |
608 | } | | 608 | } |
609 | | | 609 | |
610 | replay: if (LF_ISSET(TXT_REPLAY)) { | | 610 | replay: if (LF_ISSET(TXT_REPLAY)) { |
611 | if (rcol == vip->rep_cnt) | | 611 | if (rcol == vip->rep_cnt) |
612 | goto k_escape; | | 612 | goto k_escape; |
613 | evp = vip->rep + rcol++; | | 613 | evp = vip->rep + rcol++; |
614 | } | | 614 | } |
615 | | | 615 | |
616 | /* Wrapmargin check for leading space. */ | | 616 | /* Wrapmargin check for leading space. */ |
617 | if (wm_skip) { | | 617 | if (wm_skip) { |
618 | wm_skip = 0; | | 618 | wm_skip = 0; |
619 | if (evp->e_c == ' ') | | 619 | if (evp->e_c == ' ') |
620 | goto resolve; | | 620 | goto resolve; |
621 | } | | 621 | } |
622 | | | 622 | |
623 | /* If quoted by someone else, simply insert the character. */ | | 623 | /* If quoted by someone else, simply insert the character. */ |
624 | if (FL_ISSET(evp->e_flags, CH_QUOTED)) | | 624 | if (FL_ISSET(evp->e_flags, CH_QUOTED)) |
625 | goto insq_ch; | | 625 | goto insq_ch; |
626 | | | 626 | |
627 | /* | | 627 | /* |
628 | * !!! | | 628 | * !!! |
629 | * If this character was quoted by a K_VLNEXT or a backslash, replace | | 629 | * If this character was quoted by a K_VLNEXT or a backslash, replace |
630 | * the placeholder (a carat or a backslash) with the new character. | | 630 | * the placeholder (a carat or a backslash) with the new character. |
631 | * If it was quoted by a K_VLNEXT, we've already adjusted the cursor | | 631 | * If it was quoted by a K_VLNEXT, we've already adjusted the cursor |
632 | * because it has to appear on top of the placeholder character. If | | 632 | * because it has to appear on top of the placeholder character. If |
633 | * it was quoted by a backslash, adjust the cursor now, the cursor | | 633 | * it was quoted by a backslash, adjust the cursor now, the cursor |
634 | * doesn't appear on top of it. Historic practice in both cases. | | 634 | * doesn't appear on top of it. Historic practice in both cases. |
635 | * | | 635 | * |
636 | * Skip tests for abbreviations; ":ab xa XA" followed by "ixa^V<space>" | | 636 | * Skip tests for abbreviations; ":ab xa XA" followed by "ixa^V<space>" |
637 | * doesn't perform an abbreviation. Special case, ^V^J (not ^V^M) is | | 637 | * doesn't perform an abbreviation. Special case, ^V^J (not ^V^M) is |
638 | * the same as ^J, historically. | | 638 | * the same as ^J, historically. |
639 | */ | | 639 | */ |
640 | if (quote == Q_BTHIS || quote == Q_VTHIS) { | | 640 | if (quote == Q_BTHIS || quote == Q_VTHIS) { |
641 | FL_CLR(ec_flags, EC_QUOTED); | | 641 | FL_CLR(ec_flags, EC_QUOTED); |
642 | if (LF_ISSET(TXT_MAPINPUT)) | | 642 | if (LF_ISSET(TXT_MAPINPUT)) |
643 | FL_SET(ec_flags, EC_MAPINPUT); | | 643 | FL_SET(ec_flags, EC_MAPINPUT); |
644 | | | 644 | |
645 | if (quote == Q_BTHIS && | | 645 | if (quote == Q_BTHIS && |
646 | (evp->e_value == K_VERASE || evp->e_value == K_VKILL)) { | | 646 | (evp->e_value == K_VERASE || evp->e_value == K_VKILL)) { |
647 | quote = Q_NOTSET; | | 647 | quote = Q_NOTSET; |
648 | --tp->cno; | | 648 | --tp->cno; |
649 | ++tp->owrite; | | 649 | ++tp->owrite; |
650 | goto insl_ch; | | 650 | goto insl_ch; |
651 | } | | 651 | } |
652 | if (quote == Q_VTHIS && evp->e_value != K_NL) { | | 652 | if (quote == Q_VTHIS && evp->e_value != K_NL) { |
653 | quote = Q_NOTSET; | | 653 | quote = Q_NOTSET; |
654 | goto insl_ch; | | 654 | goto insl_ch; |
655 | } | | 655 | } |
656 | quote = Q_NOTSET; | | 656 | quote = Q_NOTSET; |
657 | } | | 657 | } |
658 | | | 658 | |
659 | /* | | 659 | /* |
660 | * !!! | | 660 | * !!! |
661 | * Translate "<CH_HEX>[isxdigit()]*" to a character with a hex value: | | 661 | * Translate "<CH_HEX>[isxdigit()]*" to a character with a hex value: |
662 | * this test delimits the value by any non-hex character. Offset by | | 662 | * this test delimits the value by any non-hex character. Offset by |
663 | * one, we use 0 to mean that we've found <CH_HEX>. | | 663 | * one, we use 0 to mean that we've found <CH_HEX>. |
664 | */ | | 664 | */ |
665 | if (hexcnt > 1 && !ISXDIGIT(evp->e_c)) { | | 665 | if (hexcnt > 1 && !ISXDIGIT(evp->e_c)) { |
666 | hexcnt = 0; | | 666 | hexcnt = 0; |
667 | if (txt_hex(sp, tp)) | | 667 | if (txt_hex(sp, tp)) |
668 | goto err; | | 668 | goto err; |
669 | } | | 669 | } |
670 | | | 670 | |
671 | switch (evp->e_value) { | | 671 | switch (evp->e_value) { |
672 | case K_CR: /* Carriage return. */ | | 672 | case K_CR: /* Carriage return. */ |
673 | case K_NL: /* New line. */ | | 673 | case K_NL: /* New line. */ |
674 | /* Return in script windows and the command line. */ | | 674 | /* Return in script windows and the command line. */ |
675 | k_cr: if (LF_ISSET(TXT_CR)) { | | 675 | k_cr: if (LF_ISSET(TXT_CR)) { |
676 | static CHAR_T cr[] = { '\r', 0 }; | | 676 | static CHAR_T cr[] = { '\r', 0 }; |
677 | /* | | 677 | /* |
678 | * If this was a map, we may have not displayed | | 678 | * If this was a map, we may have not displayed |
679 | * the line. Display it, just in case. | | 679 | * the line. Display it, just in case. |
680 | * | | 680 | * |
681 | * If a script window and not the colon line, | | 681 | * If a script window and not the colon line, |
682 | * push a <cr> so it gets executed. | | 682 | * push a <cr> so it gets executed. |
683 | */ | | 683 | */ |
684 | if (LF_ISSET(TXT_INFOLINE)) { | | 684 | if (LF_ISSET(TXT_INFOLINE)) { |
685 | if (vs_change(sp, tp->lno, LINE_RESET)) | | 685 | if (vs_change(sp, tp->lno, LINE_RESET)) |
686 | goto err; | | 686 | goto err; |
687 | } else if (F_ISSET(sp, SC_SCRIPT)) | | 687 | } else if (F_ISSET(sp, SC_SCRIPT)) |
688 | (void)v_event_push(sp, NULL, cr, 1, CH_NOMAP); | | 688 | (void)v_event_push(sp, NULL, cr, 1, CH_NOMAP); |
689 | | | 689 | |
690 | /* Set term condition: if empty. */ | | 690 | /* Set term condition: if empty. */ |
691 | if (tp->cno <= tp->offset) | | 691 | if (tp->cno <= tp->offset) |
692 | tp->term = TERM_CR; | | 692 | tp->term = TERM_CR; |
693 | /* | | 693 | /* |
694 | * Set term condition: if searching incrementally and | | 694 | * Set term condition: if searching incrementally and |
695 | * the user entered a pattern, return a completed | | 695 | * the user entered a pattern, return a completed |
696 | * search, regardless if the entire pattern was found. | | 696 | * search, regardless if the entire pattern was found. |
697 | */ | | 697 | */ |
698 | if (FL_ISSET(is_flags, IS_RUNNING) && | | 698 | if (FL_ISSET(is_flags, IS_RUNNING) && |
699 | tp->cno >= tp->offset + 1) | | 699 | tp->cno >= tp->offset + 1) |
700 | tp->term = TERM_SEARCH; | | 700 | tp->term = TERM_SEARCH; |
701 | | | 701 | |
702 | goto k_escape; | | 702 | goto k_escape; |
703 | } | | 703 | } |
704 | | | 704 | |
705 | #define LINE_RESOLVE { \ | | 705 | #define LINE_RESOLVE { \ |
706 | /* \ | | 706 | /* \ |
707 | * Handle abbreviations. If there was one, discard the \ | | 707 | * Handle abbreviations. If there was one, discard the \ |
708 | * replay characters. \ | | 708 | * replay characters. \ |
709 | */ \ | | 709 | */ \ |
710 | if (abb == AB_INWORD && \ | | 710 | if (abb == AB_INWORD && \ |
711 | !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) { \ | | 711 | !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) { \ |
712 | if (txt_abbrev(sp, tp, &evp->e_c, \ | | 712 | if (txt_abbrev(sp, tp, &evp->e_c, \ |
713 | LF_ISSET(TXT_INFOLINE), &tmp, \ | | 713 | LF_ISSET(TXT_INFOLINE), &tmp, \ |
714 | &ab_turnoff)) \ | | 714 | &ab_turnoff)) \ |
715 | goto err; \ | | 715 | goto err; \ |
716 | if (tmp) { \ | | 716 | if (tmp) { \ |
717 | if (LF_ISSET(TXT_RECORD)) \ | | 717 | if (LF_ISSET(TXT_RECORD)) \ |
718 | rcol -= tmp + 1; \ | | 718 | rcol -= tmp + 1; \ |
719 | goto resolve; \ | | 719 | goto resolve; \ |
720 | } \ | | 720 | } \ |
721 | } \ | | 721 | } \ |
722 | if (abb != AB_NOTSET) \ | | 722 | if (abb != AB_NOTSET) \ |
723 | abb = AB_NOTWORD; \ | | 723 | abb = AB_NOTWORD; \ |
724 | if (UNMAP_TST) \ | | 724 | if (UNMAP_TST) \ |
725 | txt_unmap(sp, tp, &ec_flags); \ | | 725 | txt_unmap(sp, tp, &ec_flags); \ |
726 | /* \ | | 726 | /* \ |
727 | * Delete any appended cursor. It's possible to get in \ | | 727 | * Delete any appended cursor. It's possible to get in \ |
728 | * situations where TXT_APPENDEOL is set but tp->insert \ | | 728 | * situations where TXT_APPENDEOL is set but tp->insert \ |
729 | * is 0 when using the R command and all the characters \ | | 729 | * is 0 when using the R command and all the characters \ |
730 | * are tp->owrite characters. \ | | 730 | * are tp->owrite characters. \ |
731 | */ \ | | 731 | */ \ |
732 | if (LF_ISSET(TXT_APPENDEOL) && tp->insert > 0) { \ | | 732 | if (LF_ISSET(TXT_APPENDEOL) && tp->insert > 0) { \ |
733 | --tp->len; \ | | 733 | --tp->len; \ |
734 | --tp->insert; \ | | 734 | --tp->insert; \ |
735 | } \ | | 735 | } \ |
736 | } | | 736 | } |
737 | LINE_RESOLVE; | | 737 | LINE_RESOLVE; |
738 | | | 738 | |
739 | /* | | 739 | /* |
740 | * Save the current line information for restoration in | | 740 | * Save the current line information for restoration in |
741 | * txt_backup(), and set the line final length. | | 741 | * txt_backup(), and set the line final length. |
742 | */ | | 742 | */ |
743 | tp->sv_len = tp->len; | | 743 | tp->sv_len = tp->len; |
744 | tp->sv_cno = tp->cno; | | 744 | tp->sv_cno = tp->cno; |
745 | tp->len = tp->cno; | | 745 | tp->len = tp->cno; |
746 | | | 746 | |
747 | /* Update the old line. */ | | 747 | /* Update the old line. */ |
748 | if (vs_change(sp, tp->lno, LINE_RESET)) | | 748 | if (vs_change(sp, tp->lno, LINE_RESET)) |
749 | goto err; | | 749 | goto err; |
750 | | | 750 | |
751 | /* | | 751 | /* |
752 | * Historic practice, when the autoindent edit option was set, | | 752 | * Historic practice, when the autoindent edit option was set, |
753 | * was to delete <blank> characters following the inserted | | 753 | * was to delete <blank> characters following the inserted |
754 | * newline. This affected the 'R', 'c', and 's' commands; 'c' | | 754 | * newline. This affected the 'R', 'c', and 's' commands; 'c' |
755 | * and 's' retained the insert characters only, 'R' moved the | | 755 | * and 's' retained the insert characters only, 'R' moved the |
756 | * overwrite and insert characters into the next TEXT structure. | | 756 | * overwrite and insert characters into the next TEXT structure. |
757 | * We keep track of the number of characters erased for the 'R' | | 757 | * We keep track of the number of characters erased for the 'R' |
758 | * command so that the final resolution of the line is correct. | | 758 | * command so that the final resolution of the line is correct. |
759 | */ | | 759 | */ |
760 | tp->R_erase = 0; | | 760 | tp->R_erase = 0; |
761 | owrite = tp->owrite; | | 761 | owrite = tp->owrite; |
762 | insert = tp->insert; | | 762 | insert = tp->insert; |
763 | if (LF_ISSET(TXT_REPLACE) && owrite != 0) { | | 763 | if (LF_ISSET(TXT_REPLACE) && owrite != 0) { |
764 | for (p = tp->lb + tp->cno; owrite > 0 && ISBLANK((UCHAR_T)*p); | | 764 | for (p = tp->lb + tp->cno; owrite > 0 && ISBLANK((UCHAR_T)*p); |
765 | ++p, --owrite, ++tp->R_erase); | | 765 | ++p, --owrite, ++tp->R_erase); |
766 | if (owrite == 0) | | 766 | if (owrite == 0) |
767 | for (; insert > 0 && ISBLANK((UCHAR_T)*p); | | 767 | for (; insert > 0 && ISBLANK((UCHAR_T)*p); |
768 | ++p, ++tp->R_erase, --insert); | | 768 | ++p, ++tp->R_erase, --insert); |
769 | } else { | | 769 | } else { |
770 | p = tp->lb + tp->cno + owrite; | | 770 | p = tp->lb + tp->cno + owrite; |
771 | if (O_ISSET(sp, O_AUTOINDENT)) | | 771 | if (O_ISSET(sp, O_AUTOINDENT)) |
772 | for (; insert > 0 && | | 772 | for (; insert > 0 && |
773 | ISBLANK((UCHAR_T)*p); ++p, --insert); | | 773 | ISBLANK((UCHAR_T)*p); ++p, --insert); |
774 | owrite = 0; | | 774 | owrite = 0; |
775 | } | | 775 | } |
776 | | | 776 | |
777 | /* | | 777 | /* |
778 | * !!! | | 778 | * !!! |
779 | * Create a new line and insert the new TEXT into the queue. | | 779 | * Create a new line and insert the new TEXT into the queue. |
780 | * DON'T insert until the old line has been updated, or the | | 780 | * DON'T insert until the old line has been updated, or the |
781 | * inserted line count in line.c:db_get() will be wrong. | | 781 | * inserted line count in line.c:db_get() will be wrong. |
782 | */ | | 782 | */ |
783 | if ((ntp = text_init(sp, p, | | 783 | if ((ntp = text_init(sp, p, |
784 | insert + owrite, insert + owrite + 32)) == NULL) | | 784 | insert + owrite, insert + owrite + 32)) == NULL) |
785 | goto err; | | 785 | goto err; |
786 | TAILQ_INSERT_TAIL(&sp->tiq, ntp, q); | | 786 | TAILQ_INSERT_TAIL(&sp->tiq, ntp, q); |
787 | | | 787 | |
788 | /* Set up bookkeeping for the new line. */ | | 788 | /* Set up bookkeeping for the new line. */ |
789 | ntp->insert = insert; | | 789 | ntp->insert = insert; |
790 | ntp->owrite = owrite; | | 790 | ntp->owrite = owrite; |
791 | ntp->lno = tp->lno + 1; | | 791 | ntp->lno = tp->lno + 1; |
792 | | | 792 | |
793 | /* | | 793 | /* |
794 | * Reset the autoindent line value. 0^D keeps the autoindent | | 794 | * Reset the autoindent line value. 0^D keeps the autoindent |
795 | * line from changing, ^D changes the level, even if there were | | 795 | * line from changing, ^D changes the level, even if there were |
796 | * no characters in the old line. Note, if using the current | | 796 | * no characters in the old line. Note, if using the current |
797 | * tp structure, use the cursor as the length, the autoindent | | 797 | * tp structure, use the cursor as the length, the autoindent |
798 | * characters may have been erased. | | 798 | * characters may have been erased. |
799 | */ | | 799 | */ |
800 | if (LF_ISSET(TXT_AUTOINDENT)) { | | 800 | if (LF_ISSET(TXT_AUTOINDENT)) { |
801 | if (nochange) { | | 801 | if (nochange) { |
802 | nochange = 0; | | 802 | nochange = 0; |
803 | if (v_txt_auto(sp, OOBLNO, &ait, ait.ai, ntp)) | | 803 | if (v_txt_auto(sp, OOBLNO, &ait, ait.ai, ntp)) |
804 | goto err; | | 804 | goto err; |
805 | FREE_SPACEW(sp, ait.lb, ait.lb_len); | | 805 | FREE_SPACEW(sp, ait.lb, ait.lb_len); |
806 | } else | | 806 | } else |
807 | if (v_txt_auto(sp, OOBLNO, tp, tp->cno, ntp)) | | 807 | if (v_txt_auto(sp, OOBLNO, tp, tp->cno, ntp)) |
808 | goto err; | | 808 | goto err; |
809 | carat = C_NOTSET; | | 809 | carat = C_NOTSET; |
810 | } | | 810 | } |
811 | | | 811 | |
812 | /* Reset the cursor. */ | | 812 | /* Reset the cursor. */ |
813 | ntp->cno = ntp->ai; | | 813 | ntp->cno = ntp->ai; |
814 | | | 814 | |
815 | /* | | 815 | /* |
816 | * If we're here because wrapmargin was set and we've broken a | | 816 | * If we're here because wrapmargin was set and we've broken a |
817 | * line, there may be additional information (i.e. the start of | | 817 | * line, there may be additional information (i.e. the start of |
818 | * a line) in the wmt structure. | | 818 | * a line) in the wmt structure. |
819 | */ | | 819 | */ |
820 | if (wm_set) { | | 820 | if (wm_set) { |
821 | if (wmt.offset != 0 || | | 821 | if (wmt.offset != 0 || |
822 | wmt.owrite != 0 || wmt.insert != 0) { | | 822 | wmt.owrite != 0 || wmt.insert != 0) { |
823 | #define WMTSPACE wmt.offset + wmt.owrite + wmt.insert | | 823 | #define WMTSPACE wmt.offset + wmt.owrite + wmt.insert |
824 | BINC_GOTOW(sp, ntp->lb, | | 824 | BINC_GOTOW(sp, ntp->lb, |
825 | ntp->lb_len, ntp->len + WMTSPACE + 32); | | 825 | ntp->lb_len, ntp->len + WMTSPACE + 32); |
826 | MEMMOVEW(ntp->lb + ntp->cno, wmt.lb, WMTSPACE); | | 826 | MEMMOVEW(ntp->lb + ntp->cno, wmt.lb, WMTSPACE); |
827 | ntp->len += WMTSPACE; | | 827 | ntp->len += WMTSPACE; |
828 | ntp->cno += wmt.offset; | | 828 | ntp->cno += wmt.offset; |
829 | ntp->owrite = wmt.owrite; | | 829 | ntp->owrite = wmt.owrite; |
830 | ntp->insert = wmt.insert; | | 830 | ntp->insert = wmt.insert; |
831 | } | | 831 | } |
832 | wm_set = 0; | | 832 | wm_set = 0; |
833 | } | | 833 | } |
834 | | | 834 | |
835 | /* New lines are TXT_APPENDEOL. */ | | 835 | /* New lines are TXT_APPENDEOL. */ |
836 | if (ntp->owrite == 0 && ntp->insert == 0) { | | 836 | if (ntp->owrite == 0 && ntp->insert == 0) { |
837 | BINC_GOTOW(sp, ntp->lb, ntp->lb_len, ntp->len + 1); | | 837 | BINC_GOTOW(sp, ntp->lb, ntp->lb_len, ntp->len + 1); |
838 | LF_SET(TXT_APPENDEOL); | | 838 | LF_SET(TXT_APPENDEOL); |
839 | ntp->lb[ntp->cno] = CH_CURSOR; | | 839 | ntp->lb[ntp->cno] = CH_CURSOR; |
840 | ++ntp->insert; | | 840 | ++ntp->insert; |
841 | ++ntp->len; | | 841 | ++ntp->len; |
842 | } | | 842 | } |
843 | | | 843 | |
844 | /* Swap old and new TEXT's, and update the new line. */ | | 844 | /* Swap old and new TEXT's, and update the new line. */ |
845 | tp = ntp; | | 845 | tp = ntp; |
846 | if (vs_change(sp, tp->lno, LINE_INSERT)) | | 846 | if (vs_change(sp, tp->lno, LINE_INSERT)) |
847 | goto err; | | 847 | goto err; |
848 | | | 848 | |
849 | goto resolve; | | 849 | goto resolve; |
850 | case K_ESCAPE: /* Escape. */ | | 850 | case K_ESCAPE: /* Escape. */ |
851 | if (!LF_ISSET(TXT_ESCAPE)) | | 851 | if (!LF_ISSET(TXT_ESCAPE)) |
852 | goto ins_ch; | | 852 | goto ins_ch; |
853 | | | 853 | |
854 | /* If we have a count, start replaying the input. */ | | 854 | /* If we have a count, start replaying the input. */ |
855 | if (rcount > 1) { | | 855 | if (rcount > 1) { |
856 | --rcount; | | 856 | --rcount; |
857 | | | 857 | |
858 | vip->rep_cnt = rcol; | | 858 | vip->rep_cnt = rcol; |
859 | rcol = 0; | | 859 | rcol = 0; |
860 | abb = AB_NOTSET; | | 860 | abb = AB_NOTSET; |
861 | LF_CLR(TXT_RECORD); | | 861 | LF_CLR(TXT_RECORD); |
862 | LF_SET(TXT_REPLAY); | | 862 | LF_SET(TXT_REPLAY); |
863 | | | 863 | |
864 | /* | | 864 | /* |
865 | * Some commands (e.g. 'o') need a <newline> for each | | 865 | * Some commands (e.g. 'o') need a <newline> for each |
866 | * repetition. | | 866 | * repetition. |
867 | */ | | 867 | */ |
868 | if (LF_ISSET(TXT_ADDNEWLINE)) | | 868 | if (LF_ISSET(TXT_ADDNEWLINE)) |
869 | goto k_cr; | | 869 | goto k_cr; |
870 | | | 870 | |
871 | /* | | 871 | /* |
872 | * The R command turns into the 'a' command after the | | 872 | * The R command turns into the 'a' command after the |
873 | * first repetition. | | 873 | * first repetition. |
874 | */ | | 874 | */ |
875 | if (LF_ISSET(TXT_REPLACE)) { | | 875 | if (LF_ISSET(TXT_REPLACE)) { |
876 | tp->insert = tp->owrite; | | 876 | tp->insert = tp->owrite; |
877 | tp->owrite = 0; | | 877 | tp->owrite = 0; |
878 | LF_CLR(TXT_REPLACE); | | 878 | LF_CLR(TXT_REPLACE); |
879 | } | | 879 | } |
880 | goto replay; | | 880 | goto replay; |
881 | } | | 881 | } |
882 | | | 882 | |
883 | /* Set term condition: if empty. */ | | 883 | /* Set term condition: if empty. */ |
884 | if (tp->cno <= tp->offset) | | 884 | if (tp->cno <= tp->offset) |
885 | tp->term = TERM_ESC; | | 885 | tp->term = TERM_ESC; |
886 | /* | | 886 | /* |
887 | * Set term condition: if searching incrementally and the user | | 887 | * Set term condition: if searching incrementally and the user |
888 | * entered a pattern, return a completed search, regardless if | | 888 | * entered a pattern, return a completed search, regardless if |
889 | * the entire pattern was found. | | 889 | * the entire pattern was found. |
890 | */ | | 890 | */ |
891 | if (FL_ISSET(is_flags, IS_RUNNING) && tp->cno >= tp->offset + 1) | | 891 | if (FL_ISSET(is_flags, IS_RUNNING) && tp->cno >= tp->offset + 1) |
892 | tp->term = TERM_SEARCH; | | 892 | tp->term = TERM_SEARCH; |
893 | | | 893 | |
894 | k_escape: LINE_RESOLVE; | | 894 | k_escape: LINE_RESOLVE; |
895 | | | 895 | |
896 | /* | | 896 | /* |
897 | * Clean up for the 'R' command, restoring overwrite | | 897 | * Clean up for the 'R' command, restoring overwrite |
898 | * characters, and making them into insert characters. | | 898 | * characters, and making them into insert characters. |
899 | */ | | 899 | */ |
900 | if (LF_ISSET(TXT_REPLACE)) | | 900 | if (LF_ISSET(TXT_REPLACE)) |
901 | txt_Rresolve(sp, &sp->tiq, tp, len); | | 901 | txt_Rresolve(sp, &sp->tiq, tp, len); |
902 | | | 902 | |
903 | /* | | 903 | /* |
904 | * If there are any overwrite characters, copy down | | 904 | * If there are any overwrite characters, copy down |
905 | * any insert characters, and decrement the length. | | 905 | * any insert characters, and decrement the length. |
906 | */ | | 906 | */ |
907 | if (tp->owrite) { | | 907 | if (tp->owrite) { |
908 | if (tp->insert) | | 908 | if (tp->insert) |
909 | MEMMOVEW(tp->lb + tp->cno, | | 909 | MEMMOVEW(tp->lb + tp->cno, |
910 | tp->lb + tp->cno + tp->owrite, tp->insert); | | 910 | tp->lb + tp->cno + tp->owrite, tp->insert); |
911 | tp->len -= tp->owrite; | | 911 | tp->len -= tp->owrite; |
912 | } | | 912 | } |
913 | | | 913 | |
914 | /* | | 914 | /* |
915 | * Optionally resolve the lines into the file. If not | | 915 | * Optionally resolve the lines into the file. If not |
916 | * resolving the lines into the file, end the line with | | 916 | * resolving the lines into the file, end the line with |
917 | * a nul. If the line is empty, then set the length to | | 917 | * a nul. If the line is empty, then set the length to |
918 | * 0, the termination condition has already been set. | | 918 | * 0, the termination condition has already been set. |
919 | * | | 919 | * |
920 | * XXX | | 920 | * XXX |
921 | * This is wrong, should pass back a length. | | 921 | * This is wrong, should pass back a length. |
922 | */ | | 922 | */ |
923 | if (LF_ISSET(TXT_RESOLVE)) { | | 923 | if (LF_ISSET(TXT_RESOLVE)) { |
924 | if (txt_resolve(sp, &sp->tiq, flags)) | | 924 | if (txt_resolve(sp, &sp->tiq, flags)) |
925 | goto err; | | 925 | goto err; |
926 | } else { | | 926 | } else { |
927 | BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1); | | 927 | BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1); |
928 | tp->lb[tp->len] = '\0'; | | 928 | tp->lb[tp->len] = '\0'; |
929 | } | | 929 | } |
930 | | | 930 | |
931 | /* | | 931 | /* |
932 | * Set the return cursor position to rest on the last | | 932 | * Set the return cursor position to rest on the last |
933 | * inserted character. | | 933 | * inserted character. |
934 | */ | | 934 | */ |
935 | if (tp->cno != 0) | | 935 | if (tp->cno != 0) |
936 | --tp->cno; | | 936 | --tp->cno; |
937 | | | 937 | |
938 | /* Update the last line. */ | | 938 | /* Update the last line. */ |
939 | if (vs_change(sp, tp->lno, LINE_RESET)) | | 939 | if (vs_change(sp, tp->lno, LINE_RESET)) |
940 | return (1); | | 940 | return (1); |
941 | goto done; | | 941 | goto done; |
942 | case K_CARAT: /* Delete autoindent chars. */ | | 942 | case K_CARAT: /* Delete autoindent chars. */ |
943 | if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT)) | | 943 | if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT)) |
944 | carat = C_CARATSET; | | 944 | carat = C_CARATSET; |
945 | goto ins_ch; | | 945 | goto ins_ch; |
946 | case K_ZERO: /* Delete autoindent chars. */ | | 946 | case K_ZERO: /* Delete autoindent chars. */ |
947 | if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT)) | | 947 | if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT)) |
948 | carat = C_ZEROSET; | | 948 | carat = C_ZEROSET; |
949 | goto ins_ch; | | 949 | goto ins_ch; |
950 | case K_CNTRLD: /* Delete autoindent char. */ | | 950 | case K_CNTRLD: /* Delete autoindent char. */ |
951 | /* | | 951 | /* |
952 | * If in the first column or no characters to erase, ignore | | 952 | * If in the first column or no characters to erase, ignore |
953 | * the ^D (this matches historic practice). If not doing | | 953 | * the ^D (this matches historic practice). If not doing |
954 | * autoindent or already inserted non-ai characters, it's a | | 954 | * autoindent or already inserted non-ai characters, it's a |
955 | * literal. The latter test is done in the switch, as the | | 955 | * literal. The latter test is done in the switch, as the |
956 | * CARAT forms are N + 1, not N. | | 956 | * CARAT forms are N + 1, not N. |
957 | */ | | 957 | */ |
958 | if (!LF_ISSET(TXT_AUTOINDENT)) | | 958 | if (!LF_ISSET(TXT_AUTOINDENT)) |
959 | goto ins_ch; | | 959 | goto ins_ch; |
960 | if (tp->cno == 0) | | 960 | if (tp->cno == 0) |
961 | goto resolve; | | 961 | goto resolve; |
962 | | | 962 | |
963 | switch (carat) { | | 963 | switch (carat) { |
964 | case C_CARATSET: /* ^^D */ | | 964 | case C_CARATSET: /* ^^D */ |
965 | if (tp->ai == 0 || tp->cno != tp->ai + tp->offset + 1) | | 965 | if (tp->ai == 0 || tp->cno != tp->ai + tp->offset + 1) |
966 | goto ins_ch; | | 966 | goto ins_ch; |
967 | | | 967 | |
968 | /* Save the ai string for later. */ | | 968 | /* Save the ai string for later. */ |
969 | ait.lb = NULL; | | 969 | ait.lb = NULL; |
970 | ait.lb_len = 0; | | 970 | ait.lb_len = 0; |
971 | BINC_GOTOW(sp, ait.lb, ait.lb_len, tp->ai); | | 971 | BINC_GOTOW(sp, ait.lb, ait.lb_len, tp->ai); |
972 | MEMMOVEW(ait.lb, tp->lb, tp->ai); | | 972 | MEMMOVEW(ait.lb, tp->lb, tp->ai); |
973 | ait.ai = ait.len = tp->ai; | | 973 | ait.ai = ait.len = tp->ai; |
974 | | | 974 | |
975 | carat = C_NOTSET; | | 975 | carat = C_NOTSET; |
976 | nochange = 1; | | 976 | nochange = 1; |
977 | goto leftmargin; | | 977 | goto leftmargin; |
978 | case C_ZEROSET: /* 0^D */ | | 978 | case C_ZEROSET: /* 0^D */ |
979 | if (tp->ai == 0 || tp->cno != tp->ai + tp->offset + 1) | | 979 | if (tp->ai == 0 || tp->cno != tp->ai + tp->offset + 1) |
980 | goto ins_ch; | | 980 | goto ins_ch; |
981 | | | 981 | |
982 | carat = C_NOTSET; | | 982 | carat = C_NOTSET; |
983 | leftmargin: tp->lb[tp->cno - 1] = ' '; | | 983 | leftmargin: tp->lb[tp->cno - 1] = ' '; |
984 | tp->owrite += tp->cno - tp->offset; | | 984 | tp->owrite += tp->cno - tp->offset; |
985 | tp->cno = tp->offset; | | 985 | tp->cno = tp->offset; |
986 | break; | | 986 | break; |
987 | case C_NOTSET: /* ^D */ | | 987 | case C_NOTSET: /* ^D */ |
988 | if (tp->ai == 0 || tp->cno != tp->ai + tp->offset) | | 988 | if (tp->ai == 0 || tp->cno != tp->ai + tp->offset) |
989 | goto ins_ch; | | 989 | goto ins_ch; |
990 | | | 990 | |
991 | (void)txt_dent(sp, tp, 0); | | 991 | (void)txt_dent(sp, tp, 0); |
992 | break; | | 992 | break; |
993 | default: | | 993 | default: |
994 | abort(); | | 994 | abort(); |
995 | } | | 995 | } |
996 | break; | | 996 | break; |
997 | case K_VERASE: /* Erase the last character. */ | | 997 | case K_VERASE: /* Erase the last character. */ |
998 | /* If can erase over the prompt, return. */ | | 998 | /* If can erase over the prompt, return. */ |
999 | if (tp->cno <= tp->offset && LF_ISSET(TXT_BS)) { | | 999 | if (tp->cno <= tp->offset && LF_ISSET(TXT_BS)) { |
1000 | tp->term = TERM_BS; | | 1000 | tp->term = TERM_BS; |
1001 | goto done; | | 1001 | goto done; |
1002 | } | | 1002 | } |
1003 | | | 1003 | |
1004 | /* | | 1004 | /* |
1005 | * If at the beginning of the line, try and drop back to a | | 1005 | * If at the beginning of the line, try and drop back to a |
1006 | * previously inserted line. | | 1006 | * previously inserted line. |
1007 | */ | | 1007 | */ |
1008 | if (tp->cno == 0) { | | 1008 | if (tp->cno == 0) { |
1009 | if ((ntp = | | 1009 | if ((ntp = |
1010 | txt_backup(sp, &sp->tiq, tp, &flags)) == NULL) | | 1010 | txt_backup(sp, &sp->tiq, tp, &flags)) == NULL) |
1011 | goto err; | | 1011 | goto err; |
1012 | tp = ntp; | | 1012 | tp = ntp; |
1013 | break; | | 1013 | break; |
1014 | } | | 1014 | } |
1015 | | | 1015 | |
1016 | /* If nothing to erase, bell the user. */ | | 1016 | /* If nothing to erase, bell the user. */ |
1017 | if (tp->cno <= tp->offset) { | | 1017 | if (tp->cno <= tp->offset) { |
1018 | if (!LF_ISSET(TXT_REPLAY)) | | 1018 | if (!LF_ISSET(TXT_REPLAY)) |
1019 | txt_nomorech(sp); | | 1019 | txt_nomorech(sp); |
1020 | break; | | 1020 | break; |
1021 | } | | 1021 | } |
1022 | | | 1022 | |
1023 | /* Drop back one character. */ | | 1023 | /* Drop back one character. */ |
1024 | --tp->cno; | | 1024 | --tp->cno; |
1025 | | | 1025 | |
1026 | /* | | 1026 | /* |
1027 | * Historically, vi didn't replace the erased characters with | | 1027 | * Historically, vi didn't replace the erased characters with |
1028 | * <blank>s, presumably because it's easier to fix a minor | | 1028 | * <blank>s, presumably because it's easier to fix a minor |
1029 | * typing mistake and continue on if the previous letters are | | 1029 | * typing mistake and continue on if the previous letters are |
1030 | * already there. This is a problem for incremental searching, | | 1030 | * already there. This is a problem for incremental searching, |
1031 | * because the user can no longer tell where they are in the | | 1031 | * because the user can no longer tell where they are in the |
1032 | * colon command line because the cursor is at the last search | | 1032 | * colon command line because the cursor is at the last search |
1033 | * point in the screen. So, if incrementally searching, erase | | 1033 | * point in the screen. So, if incrementally searching, erase |
1034 | * the erased characters from the screen. | | 1034 | * the erased characters from the screen. |
1035 | */ | | 1035 | */ |
1036 | if (FL_ISSET(is_flags, IS_RUNNING)) | | 1036 | if (FL_ISSET(is_flags, IS_RUNNING)) |
1037 | tp->lb[tp->cno] = ' '; | | 1037 | tp->lb[tp->cno] = ' '; |
1038 | | | 1038 | |
1039 | /* | | 1039 | /* |
1040 | * Increment overwrite, decrement ai if deleted. | | 1040 | * Increment overwrite, decrement ai if deleted. |
1041 | * | | 1041 | * |
1042 | * !!! | | 1042 | * !!! |
1043 | * Historic vi did not permit users to use erase characters | | 1043 | * Historic vi did not permit users to use erase characters |
1044 | * to delete autoindent characters. We do. Eat hot death, | | 1044 | * to delete autoindent characters. We do. Eat hot death, |
1045 | * POSIX. | | 1045 | * POSIX. |
1046 | */ | | 1046 | */ |
1047 | ++tp->owrite; | | 1047 | ++tp->owrite; |
1048 | if (tp->cno < tp->ai) | | 1048 | if (tp->cno < tp->ai) |
1049 | --tp->ai; | | 1049 | --tp->ai; |
1050 | | | 1050 | |
1051 | /* Reset if we deleted an incremental search character. */ | | 1051 | /* Reset if we deleted an incremental search character. */ |
1052 | if (FL_ISSET(is_flags, IS_RUNNING)) | | 1052 | if (FL_ISSET(is_flags, IS_RUNNING)) |
1053 | FL_SET(is_flags, IS_RESTART); | | 1053 | FL_SET(is_flags, IS_RESTART); |
1054 | break; | | 1054 | break; |
1055 | case K_VWERASE: /* Skip back one word. */ | | 1055 | case K_VWERASE: /* Skip back one word. */ |
1056 | /* | | 1056 | /* |
1057 | * If at the beginning of the line, try and drop back to a | | 1057 | * If at the beginning of the line, try and drop back to a |
1058 | * previously inserted line. | | 1058 | * previously inserted line. |
1059 | */ | | 1059 | */ |
1060 | if (tp->cno == 0) { | | 1060 | if (tp->cno == 0) { |
1061 | if ((ntp = | | 1061 | if ((ntp = |
1062 | txt_backup(sp, &sp->tiq, tp, &flags)) == NULL) | | 1062 | txt_backup(sp, &sp->tiq, tp, &flags)) == NULL) |
1063 | goto err; | | 1063 | goto err; |
1064 | tp = ntp; | | 1064 | tp = ntp; |
1065 | } | | 1065 | } |
1066 | | | 1066 | |
1067 | /* | | 1067 | /* |
1068 | * If at offset, nothing to erase so bell the user. | | 1068 | * If at offset, nothing to erase so bell the user. |
1069 | */ | | 1069 | */ |
1070 | if (tp->cno <= tp->offset) { | | 1070 | if (tp->cno <= tp->offset) { |
1071 | if (!LF_ISSET(TXT_REPLAY)) | | 1071 | if (!LF_ISSET(TXT_REPLAY)) |
1072 | txt_nomorech(sp); | | 1072 | txt_nomorech(sp); |
1073 | break; | | 1073 | break; |
1074 | } | | 1074 | } |
1075 | | | 1075 | |
1076 | /* | | 1076 | /* |
1077 | * The first werase goes back to any autoindent column and the | | 1077 | * The first werase goes back to any autoindent column and the |
1078 | * second werase goes back to the offset. | | 1078 | * second werase goes back to the offset. |
1079 | * | | 1079 | * |
1080 | * !!! | | 1080 | * !!! |
1081 | * Historic vi did not permit users to use erase characters to | | 1081 | * Historic vi did not permit users to use erase characters to |
1082 | * delete autoindent characters. | | 1082 | * delete autoindent characters. |
1083 | */ | | 1083 | */ |
1084 | if (tp->ai && tp->cno > tp->ai) | | 1084 | if (tp->ai && tp->cno > tp->ai) |
1085 | max = tp->ai; | | 1085 | max = tp->ai; |
1086 | else { | | 1086 | else { |
1087 | tp->ai = 0; | | 1087 | tp->ai = 0; |
1088 | max = tp->offset; | | 1088 | max = tp->offset; |
1089 | } | | 1089 | } |
1090 | | | 1090 | |
1091 | /* Skip over trailing space characters. */ | | 1091 | /* Skip over trailing space characters. */ |
1092 | while (tp->cno > max && ISBLANK((UCHAR_T)tp->lb[tp->cno - 1])) { | | 1092 | while (tp->cno > max && ISBLANK((UCHAR_T)tp->lb[tp->cno - 1])) { |
1093 | --tp->cno; | | 1093 | --tp->cno; |
1094 | ++tp->owrite; | | 1094 | ++tp->owrite; |
1095 | } | | 1095 | } |
1096 | if (tp->cno == max) | | 1096 | if (tp->cno == max) |
1097 | break; | | 1097 | break; |
1098 | /* | | 1098 | /* |
1099 | * There are three types of word erase found on UNIX systems. | | 1099 | * There are three types of word erase found on UNIX systems. |
1100 | * They can be identified by how the string /a/b/c is treated | | 1100 | * They can be identified by how the string /a/b/c is treated |
1101 | * -- as 1, 3, or 6 words. Historic vi had two classes of | | 1101 | * -- as 1, 3, or 6 words. Historic vi had two classes of |
1102 | * characters, and strings were delimited by them and | | 1102 | * characters, and strings were delimited by them and |
1103 | * <blank>'s, so, 6 words. The historic tty interface used | | 1103 | * <blank>'s, so, 6 words. The historic tty interface used |
1104 | * <blank>'s to delimit strings, so, 1 word. The algorithm | | 1104 | * <blank>'s to delimit strings, so, 1 word. The algorithm |
1105 | * offered in the 4.4BSD tty interface (as stty altwerase) | | 1105 | * offered in the 4.4BSD tty interface (as stty altwerase) |
1106 | * treats it as 3 words -- there are two classes of | | 1106 | * treats it as 3 words -- there are two classes of |
1107 | * characters, and strings are delimited by them and | | 1107 | * characters, and strings are delimited by them and |
1108 | * <blank>'s. The difference is that the type of the first | | 1108 | * <blank>'s. The difference is that the type of the first |
1109 | * erased character erased is ignored, which is exactly right | | 1109 | * erased character erased is ignored, which is exactly right |
1110 | * when erasing pathname components. The edit options | | 1110 | * when erasing pathname components. The edit options |
1111 | * TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD tty | | 1111 | * TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD tty |
1112 | * interface and the historic tty driver behavior, | | 1112 | * interface and the historic tty driver behavior, |
1113 | * respectively, and the default is the same as the historic | | 1113 | * respectively, and the default is the same as the historic |
1114 | * vi behavior. | | 1114 | * vi behavior. |
1115 | * | | 1115 | * |
1116 | * Overwrite erased characters if doing incremental search; | | 1116 | * Overwrite erased characters if doing incremental search; |
1117 | * see comment above. | | 1117 | * see comment above. |
1118 | */ | | 1118 | */ |
1119 | if (LF_ISSET(TXT_TTYWERASE)) | | 1119 | if (LF_ISSET(TXT_TTYWERASE)) |
1120 | while (tp->cno > max) { | | 1120 | while (tp->cno > max) { |
| | | 1121 | if (ISBLANK((UCHAR_T)tp->lb[tp->cno - 1])) |
| | | 1122 | break; |
1121 | --tp->cno; | | 1123 | --tp->cno; |
1122 | ++tp->owrite; | | 1124 | ++tp->owrite; |
1123 | if (FL_ISSET(is_flags, IS_RUNNING)) | | 1125 | if (FL_ISSET(is_flags, IS_RUNNING)) |
1124 | tp->lb[tp->cno] = ' '; | | 1126 | tp->lb[tp->cno] = ' '; |
1125 | if (ISBLANK((UCHAR_T)tp->lb[tp->cno - 1])) | | | |
1126 | break; | | | |
1127 | } | | 1127 | } |
1128 | else { | | 1128 | else { |
1129 | if (LF_ISSET(TXT_ALTWERASE)) { | | 1129 | if (LF_ISSET(TXT_ALTWERASE)) { |
1130 | --tp->cno; | | 1130 | --tp->cno; /* No worry for out of bounds. */ |
1131 | ++tp->owrite; | | 1131 | ++tp->owrite; |
1132 | if (FL_ISSET(is_flags, IS_RUNNING)) | | 1132 | if (FL_ISSET(is_flags, IS_RUNNING)) |
1133 | tp->lb[tp->cno] = ' '; | | 1133 | tp->lb[tp->cno] = ' '; |
1134 | if (ISBLANK((UCHAR_T)tp->lb[tp->cno - 1])) | | | |
1135 | break; | | | |
1136 | } | | 1134 | } |
1137 | if (tp->cno > max) | | 1135 | if (tp->cno > max) |
1138 | tmp = inword((UCHAR_T)tp->lb[tp->cno - 1]); | | 1136 | tmp = inword((UCHAR_T)tp->lb[tp->cno - 1]); |
1139 | while (tp->cno > max) { | | 1137 | while (tp->cno > max) { |
| | | 1138 | if (tmp != inword((UCHAR_T)tp->lb[tp->cno - 1]) |
| | | 1139 | || ISBLANK((UCHAR_T)tp->lb[tp->cno - 1])) |
| | | 1140 | break; |
1140 | --tp->cno; | | 1141 | --tp->cno; |
1141 | ++tp->owrite; | | 1142 | ++tp->owrite; |
1142 | if (FL_ISSET(is_flags, IS_RUNNING)) | | 1143 | if (FL_ISSET(is_flags, IS_RUNNING)) |
1143 | tp->lb[tp->cno] = ' '; | | 1144 | tp->lb[tp->cno] = ' '; |
1144 | if (tmp != inword((UCHAR_T)tp->lb[tp->cno - 1]) | | | |
1145 | || ISBLANK((UCHAR_T)tp->lb[tp->cno - 1])) | | | |
1146 | break; | | | |
1147 | } | | 1145 | } |
1148 | } | | 1146 | } |
1149 | | | 1147 | |
1150 | /* Reset if we deleted an incremental search character. */ | | 1148 | /* Reset if we deleted an incremental search character. */ |
1151 | if (FL_ISSET(is_flags, IS_RUNNING)) | | 1149 | if (FL_ISSET(is_flags, IS_RUNNING)) |
1152 | FL_SET(is_flags, IS_RESTART); | | 1150 | FL_SET(is_flags, IS_RESTART); |
1153 | break; | | 1151 | break; |
1154 | case K_VKILL: /* Restart this line. */ | | 1152 | case K_VKILL: /* Restart this line. */ |
1155 | /* | | 1153 | /* |
1156 | * !!! | | 1154 | * !!! |
1157 | * If at the beginning of the line, try and drop back to a | | 1155 | * If at the beginning of the line, try and drop back to a |
1158 | * previously inserted line. Historic vi did not permit | | 1156 | * previously inserted line. Historic vi did not permit |
1159 | * users to go back to previous lines. | | 1157 | * users to go back to previous lines. |
1160 | */ | | 1158 | */ |
1161 | if (tp->cno == 0) { | | 1159 | if (tp->cno == 0) { |
1162 | if ((ntp = | | 1160 | if ((ntp = |
1163 | txt_backup(sp, &sp->tiq, tp, &flags)) == NULL) | | 1161 | txt_backup(sp, &sp->tiq, tp, &flags)) == NULL) |
1164 | goto err; | | 1162 | goto err; |
1165 | tp = ntp; | | 1163 | tp = ntp; |
1166 | } | | 1164 | } |
1167 | | | 1165 | |
1168 | /* If at offset, nothing to erase so bell the user. */ | | 1166 | /* If at offset, nothing to erase so bell the user. */ |
1169 | if (tp->cno <= tp->offset) { | | 1167 | if (tp->cno <= tp->offset) { |
1170 | if (!LF_ISSET(TXT_REPLAY)) | | 1168 | if (!LF_ISSET(TXT_REPLAY)) |
1171 | txt_nomorech(sp); | | 1169 | txt_nomorech(sp); |
1172 | break; | | 1170 | break; |
1173 | } | | 1171 | } |
1174 | | | 1172 | |
1175 | /* | | 1173 | /* |
1176 | * First kill goes back to any autoindent and second kill goes | | 1174 | * First kill goes back to any autoindent and second kill goes |
1177 | * back to the offset. | | 1175 | * back to the offset. |
1178 | * | | 1176 | * |
1179 | * !!! | | 1177 | * !!! |
1180 | * Historic vi did not permit users to use erase characters to | | 1178 | * Historic vi did not permit users to use erase characters to |
1181 | * delete autoindent characters. | | 1179 | * delete autoindent characters. |
1182 | */ | | 1180 | */ |
1183 | if (tp->ai && tp->cno > tp->ai) | | 1181 | if (tp->ai && tp->cno > tp->ai) |
1184 | max = tp->ai; | | 1182 | max = tp->ai; |
1185 | else { | | 1183 | else { |
1186 | tp->ai = 0; | | 1184 | tp->ai = 0; |
1187 | max = tp->offset; | | 1185 | max = tp->offset; |
1188 | } | | 1186 | } |
1189 | tp->owrite += tp->cno - max; | | 1187 | tp->owrite += tp->cno - max; |
1190 | | | 1188 | |
1191 | /* | | 1189 | /* |
1192 | * Overwrite erased characters if doing incremental search; | | 1190 | * Overwrite erased characters if doing incremental search; |
1193 | * see comment above. | | 1191 | * see comment above. |
1194 | */ | | 1192 | */ |
1195 | if (FL_ISSET(is_flags, IS_RUNNING)) | | 1193 | if (FL_ISSET(is_flags, IS_RUNNING)) |
1196 | do { | | 1194 | do { |
1197 | tp->lb[--tp->cno] = ' '; | | 1195 | tp->lb[--tp->cno] = ' '; |
1198 | } while (tp->cno > max); | | 1196 | } while (tp->cno > max); |
1199 | else | | 1197 | else |
1200 | tp->cno = max; | | 1198 | tp->cno = max; |
1201 | | | 1199 | |
1202 | /* Reset if we deleted an incremental search character. */ | | 1200 | /* Reset if we deleted an incremental search character. */ |
1203 | if (FL_ISSET(is_flags, IS_RUNNING)) | | 1201 | if (FL_ISSET(is_flags, IS_RUNNING)) |
1204 | FL_SET(is_flags, IS_RESTART); | | 1202 | FL_SET(is_flags, IS_RESTART); |
1205 | break; | | 1203 | break; |
1206 | case K_CNTRLT: /* Add autoindent characters. */ | | 1204 | case K_CNTRLT: /* Add autoindent characters. */ |
1207 | if (!LF_ISSET(TXT_CNTRLT)) | | 1205 | if (!LF_ISSET(TXT_CNTRLT)) |
1208 | goto ins_ch; | | 1206 | goto ins_ch; |
1209 | if (txt_dent(sp, tp, 1)) | | 1207 | if (txt_dent(sp, tp, 1)) |
1210 | goto err; | | 1208 | goto err; |
1211 | goto ebuf_chk; | | 1209 | goto ebuf_chk; |
1212 | case K_RIGHTBRACE: | | 1210 | case K_RIGHTBRACE: |
1213 | case K_RIGHTPAREN: | | 1211 | case K_RIGHTPAREN: |
1214 | if (LF_ISSET(TXT_SHOWMATCH)) | | 1212 | if (LF_ISSET(TXT_SHOWMATCH)) |
1215 | showmatch = 1; | | 1213 | showmatch = 1; |
1216 | goto ins_ch; | | 1214 | goto ins_ch; |
1217 | case K_BACKSLASH: /* Quote next erase/kill. */ | | 1215 | case K_BACKSLASH: /* Quote next erase/kill. */ |
1218 | /* | | 1216 | /* |
1219 | * !!! | | 1217 | * !!! |
1220 | * Historic vi tried to make abbreviations after a backslash | | 1218 | * Historic vi tried to make abbreviations after a backslash |
1221 | * escape work. If you did ":ab x y", and inserted "x\^H", | | 1219 | * escape work. If you did ":ab x y", and inserted "x\^H", |
1222 | * (assuming the erase character was ^H) you got "x^H", and | | 1220 | * (assuming the erase character was ^H) you got "x^H", and |
1223 | * no abbreviation was done. If you inserted "x\z", however, | | 1221 | * no abbreviation was done. If you inserted "x\z", however, |
1224 | * it tried to back up and do the abbreviation, i.e. replace | | 1222 | * it tried to back up and do the abbreviation, i.e. replace |
1225 | * 'x' with 'y'. The problem was it got it wrong, and you | | 1223 | * 'x' with 'y'. The problem was it got it wrong, and you |
1226 | * ended up with "zy\". | | 1224 | * ended up with "zy\". |
1227 | * | | 1225 | * |
1228 | * This is really hard to do (you have to remember the | | 1226 | * This is really hard to do (you have to remember the |
1229 | * word/non-word state, for example), and doesn't make any | | 1227 | * word/non-word state, for example), and doesn't make any |
1230 | * sense to me. Both backslash and the characters it | | 1228 | * sense to me. Both backslash and the characters it |
1231 | * (usually) escapes will individually trigger the | | 1229 | * (usually) escapes will individually trigger the |
1232 | * abbreviation, so I don't see why the combination of them | | 1230 | * abbreviation, so I don't see why the combination of them |
1233 | * wouldn't. I don't expect to get caught on this one, | | 1231 | * wouldn't. I don't expect to get caught on this one, |
1234 | * particularly since it never worked right, but I've been | | 1232 | * particularly since it never worked right, but I've been |
1235 | * wrong before. | | 1233 | * wrong before. |
1236 | * | | 1234 | * |
1237 | * Do the tests for abbreviations, so ":ab xa XA", | | 1235 | * Do the tests for abbreviations, so ":ab xa XA", |
1238 | * "ixa\<K_VERASE>" performs the abbreviation. | | 1236 | * "ixa\<K_VERASE>" performs the abbreviation. |
1239 | */ | | 1237 | */ |
1240 | quote = Q_BNEXT; | | 1238 | quote = Q_BNEXT; |
1241 | goto insq_ch; | | 1239 | goto insq_ch; |
1242 | case K_VLNEXT: /* Quote next character. */ | | 1240 | case K_VLNEXT: /* Quote next character. */ |
1243 | evp->e_c = '^'; | | 1241 | evp->e_c = '^'; |
1244 | quote = Q_VNEXT; | | 1242 | quote = Q_VNEXT; |
1245 | /* | | 1243 | /* |
1246 | * Turn on the quote flag so that the underlying routines | | 1244 | * Turn on the quote flag so that the underlying routines |
1247 | * quote the next character where it's possible. Turn off | | 1245 | * quote the next character where it's possible. Turn off |
1248 | * the input mapbiting flag so that we don't remap the next | | 1246 | * the input mapbiting flag so that we don't remap the next |
1249 | * character. | | 1247 | * character. |
1250 | */ | | 1248 | */ |
1251 | FL_SET(ec_flags, EC_QUOTED); | | 1249 | FL_SET(ec_flags, EC_QUOTED); |
1252 | FL_CLR(ec_flags, EC_MAPINPUT); | | 1250 | FL_CLR(ec_flags, EC_MAPINPUT); |
1253 | | | 1251 | |
1254 | /* | | 1252 | /* |
1255 | * !!! | | 1253 | * !!! |
1256 | * Skip the tests for abbreviations, so ":ab xa XA", | | 1254 | * Skip the tests for abbreviations, so ":ab xa XA", |
1257 | * "ixa^V<space>" doesn't perform the abbreviation. | | 1255 | * "ixa^V<space>" doesn't perform the abbreviation. |
1258 | */ | | 1256 | */ |
1259 | goto insl_ch; | | 1257 | goto insl_ch; |
1260 | case K_HEXCHAR: | | 1258 | case K_HEXCHAR: |
1261 | hexcnt = 1; | | 1259 | hexcnt = 1; |
1262 | goto insq_ch; | | 1260 | goto insq_ch; |
1263 | default: /* Insert the character. */ | | 1261 | default: /* Insert the character. */ |
1264 | ins_ch: /* | | 1262 | ins_ch: /* |
1265 | * Historically, vi eliminated nul's out of hand. If the | | 1263 | * Historically, vi eliminated nul's out of hand. If the |
1266 | * beautify option was set, it also deleted any unknown | | 1264 | * beautify option was set, it also deleted any unknown |
1267 | * ASCII value less than space (040) and the del character | | 1265 | * ASCII value less than space (040) and the del character |
1268 | * (0177), except for tabs. Unknown is a key word here. | | 1266 | * (0177), except for tabs. Unknown is a key word here. |
1269 | * Most vi documentation claims that it deleted everything | | 1267 | * Most vi documentation claims that it deleted everything |
1270 | * but <tab>, <nl> and <ff>, as that's what the original | | 1268 | * but <tab>, <nl> and <ff>, as that's what the original |
1271 | * 4BSD documentation said. This is obviously wrong, | | 1269 | * 4BSD documentation said. This is obviously wrong, |
1272 | * however, as <esc> would be included in that list. What | | 1270 | * however, as <esc> would be included in that list. What |
1273 | * we do is eliminate any unquoted, iscntrl() character that | | 1271 | * we do is eliminate any unquoted, iscntrl() character that |
1274 | * wasn't a replay and wasn't handled specially, except | | 1272 | * wasn't a replay and wasn't handled specially, except |
1275 | * <tab> or <ff>. | | 1273 | * <tab> or <ff>. |
1276 | */ | | 1274 | */ |
1277 | if (LF_ISSET(TXT_BEAUTIFY) && ISCNTRL(evp->e_c) && | | 1275 | if (LF_ISSET(TXT_BEAUTIFY) && ISCNTRL(evp->e_c) && |
1278 | evp->e_value != K_FORMFEED && evp->e_value != K_TAB) { | | 1276 | evp->e_value != K_FORMFEED && evp->e_value != K_TAB) { |
1279 | msgq(sp, M_BERR, | | 1277 | msgq(sp, M_BERR, |
1280 | "192|Illegal character; quote to enter"); | | 1278 | "192|Illegal character; quote to enter"); |
1281 | if (LF_ISSET(TXT_REPLAY)) | | 1279 | if (LF_ISSET(TXT_REPLAY)) |
1282 | goto done; | | 1280 | goto done; |
1283 | break; | | 1281 | break; |
1284 | } | | 1282 | } |
1285 | | | 1283 | |
1286 | insq_ch: /* | | 1284 | insq_ch: /* |
1287 | * If entering a non-word character after a word, check for | | 1285 | * If entering a non-word character after a word, check for |
1288 | * abbreviations. If there was one, discard replay characters. | | 1286 | * abbreviations. If there was one, discard replay characters. |
1289 | * If entering a blank character, check for unmap commands, | | 1287 | * If entering a blank character, check for unmap commands, |
1290 | * as well. | | 1288 | * as well. |
1291 | */ | | 1289 | */ |
1292 | if (!inword((UCHAR_T)evp->e_c)) { | | 1290 | if (!inword((UCHAR_T)evp->e_c)) { |
1293 | if (abb == AB_INWORD && | | 1291 | if (abb == AB_INWORD && |
1294 | !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) { | | 1292 | !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) { |
1295 | if (txt_abbrev(sp, tp, &evp->e_c, | | 1293 | if (txt_abbrev(sp, tp, &evp->e_c, |
1296 | LF_ISSET(TXT_INFOLINE), &tmp, &ab_turnoff)) | | 1294 | LF_ISSET(TXT_INFOLINE), &tmp, &ab_turnoff)) |
1297 | goto err; | | 1295 | goto err; |
1298 | if (tmp) { | | 1296 | if (tmp) { |
1299 | if (LF_ISSET(TXT_RECORD)) | | 1297 | if (LF_ISSET(TXT_RECORD)) |
1300 | rcol -= tmp + 1; | | 1298 | rcol -= tmp + 1; |
1301 | goto resolve; | | 1299 | goto resolve; |
1302 | } | | 1300 | } |
1303 | } | | 1301 | } |
1304 | if (ISBLANK((UCHAR_T)evp->e_c) && UNMAP_TST) | | 1302 | if (ISBLANK((UCHAR_T)evp->e_c) && UNMAP_TST) |
1305 | txt_unmap(sp, tp, &ec_flags); | | 1303 | txt_unmap(sp, tp, &ec_flags); |
1306 | } | | 1304 | } |
1307 | if (abb != AB_NOTSET) | | 1305 | if (abb != AB_NOTSET) |
1308 | abb = inword((UCHAR_T)evp->e_c) ? AB_INWORD : AB_NOTWORD; | | 1306 | abb = inword((UCHAR_T)evp->e_c) ? AB_INWORD : AB_NOTWORD; |
1309 | | | 1307 | |
1310 | insl_ch: if (txt_insch(sp, tp, &evp->e_c, flags)) | | 1308 | insl_ch: if (txt_insch(sp, tp, &evp->e_c, flags)) |
1311 | goto err; | | 1309 | goto err; |
1312 | | | 1310 | |
1313 | /* | | 1311 | /* |
1314 | * If we're using K_VLNEXT to quote the next character, then | | 1312 | * If we're using K_VLNEXT to quote the next character, then |
1315 | * we want the cursor to position itself on the ^ placeholder | | 1313 | * we want the cursor to position itself on the ^ placeholder |
1316 | * we're displaying, to match historic practice. | | 1314 | * we're displaying, to match historic practice. |
1317 | */ | | 1315 | */ |
1318 | if (quote == Q_VNEXT) { | | 1316 | if (quote == Q_VNEXT) { |
1319 | --tp->cno; | | 1317 | --tp->cno; |
1320 | ++tp->owrite; | | 1318 | ++tp->owrite; |
1321 | } | | 1319 | } |
1322 | | | 1320 | |
1323 | /* | | 1321 | /* |
1324 | * !!! | | 1322 | * !!! |
1325 | * Translate "<CH_HEX>[isxdigit()]*" to a character with | | 1323 | * Translate "<CH_HEX>[isxdigit()]*" to a character with |
1326 | * a hex value: this test delimits the value by the max | | 1324 | * a hex value: this test delimits the value by the max |
1327 | * number of hex bytes. Offset by one, we use 0 to mean | | 1325 | * number of hex bytes. Offset by one, we use 0 to mean |
1328 | * that we've found <CH_HEX>. | | 1326 | * that we've found <CH_HEX>. |
1329 | */ | | 1327 | */ |
1330 | if (hexcnt != 0 && hexcnt++ == sizeof(CHAR_T) * 2 + 1) { | | 1328 | if (hexcnt != 0 && hexcnt++ == sizeof(CHAR_T) * 2 + 1) { |
1331 | hexcnt = 0; | | 1329 | hexcnt = 0; |
1332 | if (txt_hex(sp, tp)) | | 1330 | if (txt_hex(sp, tp)) |
1333 | goto err; | | 1331 | goto err; |
1334 | } | | 1332 | } |
1335 | | | 1333 | |
1336 | /* | | 1334 | /* |
1337 | * Check to see if we've crossed the margin. | | 1335 | * Check to see if we've crossed the margin. |
1338 | * | | 1336 | * |
1339 | * !!! | | 1337 | * !!! |
1340 | * In the historic vi, the wrapmargin value was figured out | | 1338 | * In the historic vi, the wrapmargin value was figured out |
1341 | * using the display widths of the characters, i.e. <tab> | | 1339 | * using the display widths of the characters, i.e. <tab> |
1342 | * characters were counted as two characters if the list edit | | 1340 | * characters were counted as two characters if the list edit |
1343 | * option is set, but as the tabstop edit option number of | | 1341 | * option is set, but as the tabstop edit option number of |
1344 | * characters otherwise. That's what the vs_column() function | | 1342 | * characters otherwise. That's what the vs_column() function |
1345 | * gives us, so we use it. | | 1343 | * gives us, so we use it. |
1346 | */ | | 1344 | */ |
1347 | if (margin != 0) { | | 1345 | if (margin != 0) { |
1348 | if (vs_column(sp, &tcol)) | | 1346 | if (vs_column(sp, &tcol)) |
1349 | goto err; | | 1347 | goto err; |
1350 | if (tcol >= margin) { | | 1348 | if (tcol >= margin) { |
1351 | if (txt_margin(sp, tp, &wmt, &tmp, flags)) | | 1349 | if (txt_margin(sp, tp, &wmt, &tmp, flags)) |
1352 | goto err; | | 1350 | goto err; |
1353 | if (tmp) { | | 1351 | if (tmp) { |
1354 | if (ISBLANK((UCHAR_T)evp->e_c)) | | 1352 | if (ISBLANK((UCHAR_T)evp->e_c)) |
1355 | wm_skip = 1; | | 1353 | wm_skip = 1; |
1356 | wm_set = 1; | | 1354 | wm_set = 1; |
1357 | goto k_cr; | | 1355 | goto k_cr; |
1358 | } | | 1356 | } |
1359 | } | | 1357 | } |
1360 | } | | 1358 | } |
1361 | | | 1359 | |
1362 | /* | | 1360 | /* |
1363 | * If we've reached the end of the buffer, then we need to | | 1361 | * If we've reached the end of the buffer, then we need to |
1364 | * switch into insert mode. This happens when there's a | | 1362 | * switch into insert mode. This happens when there's a |
1365 | * change to a mark and the user puts in more characters than | | 1363 | * change to a mark and the user puts in more characters than |
1366 | * the length of the motion. | | 1364 | * the length of the motion. |
1367 | */ | | 1365 | */ |
1368 | ebuf_chk: if (tp->cno >= tp->len) { | | 1366 | ebuf_chk: if (tp->cno >= tp->len) { |
1369 | BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1); | | 1367 | BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1); |
1370 | LF_SET(TXT_APPENDEOL); | | 1368 | LF_SET(TXT_APPENDEOL); |
1371 | | | 1369 | |
1372 | tp->lb[tp->cno] = CH_CURSOR; | | 1370 | tp->lb[tp->cno] = CH_CURSOR; |
1373 | ++tp->insert; | | 1371 | ++tp->insert; |
1374 | ++tp->len; | | 1372 | ++tp->len; |
1375 | } | | 1373 | } |
1376 | | | 1374 | |
1377 | /* Step the quote state forward. */ | | 1375 | /* Step the quote state forward. */ |
1378 | if (quote != Q_NOTSET) { | | 1376 | if (quote != Q_NOTSET) { |
1379 | if (quote == Q_BNEXT) | | 1377 | if (quote == Q_BNEXT) |
1380 | quote = Q_BTHIS; | | 1378 | quote = Q_BTHIS; |
1381 | if (quote == Q_VNEXT) | | 1379 | if (quote == Q_VNEXT) |
1382 | quote = Q_VTHIS; | | 1380 | quote = Q_VTHIS; |
1383 | } | | 1381 | } |
1384 | break; | | 1382 | break; |
1385 | } | | 1383 | } |
1386 | | | 1384 | |
1387 | #ifdef DEBUG | | 1385 | #ifdef DEBUG |
1388 | if (tp->cno + tp->insert + tp->owrite != tp->len) { | | 1386 | if (tp->cno + tp->insert + tp->owrite != tp->len) { |
1389 | msgq(sp, M_ERR, | | 1387 | msgq(sp, M_ERR, |
1390 | "len %u != cno: %u ai: %u insert %u overwrite %u", | | 1388 | "len %u != cno: %u ai: %u insert %u overwrite %u", |
1391 | tp->len, tp->cno, tp->ai, tp->insert, tp->owrite); | | 1389 | tp->len, tp->cno, tp->ai, tp->insert, tp->owrite); |
1392 | if (LF_ISSET(TXT_REPLAY)) | | 1390 | if (LF_ISSET(TXT_REPLAY)) |
1393 | goto done; | | 1391 | goto done; |
1394 | tp->len = tp->cno + tp->insert + tp->owrite; | | 1392 | tp->len = tp->cno + tp->insert + tp->owrite; |
1395 | } | | 1393 | } |
1396 | #endif | | 1394 | #endif |
1397 | | | 1395 | |
1398 | resolve:/* | | 1396 | resolve:/* |
1399 | * 1: If we don't need to know where the cursor really is and we're | | 1397 | * 1: If we don't need to know where the cursor really is and we're |
1400 | * replaying text, keep going. | | 1398 | * replaying text, keep going. |
1401 | */ | | 1399 | */ |
1402 | if (margin == 0 && LF_ISSET(TXT_REPLAY)) | | 1400 | if (margin == 0 && LF_ISSET(TXT_REPLAY)) |
1403 | goto replay; | | 1401 | goto replay; |
1404 | | | 1402 | |
1405 | /* | | 1403 | /* |
1406 | * 2: Reset the line. Don't bother unless we're about to wait on | | 1404 | * 2: Reset the line. Don't bother unless we're about to wait on |
1407 | * a character or we need to know where the cursor really is. | | 1405 | * a character or we need to know where the cursor really is. |
1408 | * We have to do this before showing matching characters so the | | 1406 | * We have to do this before showing matching characters so the |
1409 | * user can see what they're matching. | | 1407 | * user can see what they're matching. |
1410 | */ | | 1408 | */ |
1411 | if ((margin != 0 || !KEYS_WAITING(sp)) && | | 1409 | if ((margin != 0 || !KEYS_WAITING(sp)) && |
1412 | vs_change(sp, tp->lno, LINE_RESET)) | | 1410 | vs_change(sp, tp->lno, LINE_RESET)) |
1413 | return (1); | | 1411 | return (1); |
1414 | | | 1412 | |
1415 | /* | | 1413 | /* |
1416 | * 3: If there aren't keys waiting, display the matching character. | | 1414 | * 3: If there aren't keys waiting, display the matching character. |
1417 | * We have to do this before resolving any messages, otherwise | | 1415 | * We have to do this before resolving any messages, otherwise |
1418 | * the error message from a missing match won't appear correctly. | | 1416 | * the error message from a missing match won't appear correctly. |
1419 | */ | | 1417 | */ |
1420 | if (showmatch) { | | 1418 | if (showmatch) { |
1421 | if (!KEYS_WAITING(sp) && txt_showmatch(sp, tp)) | | 1419 | if (!KEYS_WAITING(sp) && txt_showmatch(sp, tp)) |
1422 | return (1); | | 1420 | return (1); |
1423 | showmatch = 0; | | 1421 | showmatch = 0; |
1424 | } | | 1422 | } |
1425 | | | 1423 | |
1426 | /* | | 1424 | /* |
1427 | * 4: If there have been messages and we're not editing on the colon | | 1425 | * 4: If there have been messages and we're not editing on the colon |
1428 | * command line or doing file name completion, resolve them. | | 1426 | * command line or doing file name completion, resolve them. |
1429 | */ | | 1427 | */ |
1430 | if ((vip->totalcount != 0 || F_ISSET(gp, G_BELLSCHED)) && | | 1428 | if ((vip->totalcount != 0 || F_ISSET(gp, G_BELLSCHED)) && |
1431 | !F_ISSET(sp, SC_TINPUT_INFO) && !filec_redraw && | | 1429 | !F_ISSET(sp, SC_TINPUT_INFO) && !filec_redraw && |
1432 | vs_resolve(sp, NULL, 0)) | | 1430 | vs_resolve(sp, NULL, 0)) |
1433 | return (1); | | 1431 | return (1); |
1434 | | | 1432 | |
1435 | /* | | 1433 | /* |
1436 | * 5: Refresh the screen if we're about to wait on a character or we | | 1434 | * 5: Refresh the screen if we're about to wait on a character or we |
1437 | * need to know where the cursor really is. | | 1435 | * need to know where the cursor really is. |
1438 | */ | | 1436 | */ |
1439 | if (margin != 0 || !KEYS_WAITING(sp)) { | | 1437 | if (margin != 0 || !KEYS_WAITING(sp)) { |
1440 | UPDATE_POSITION(sp, tp); | | 1438 | UPDATE_POSITION(sp, tp); |
1441 | if (vs_refresh(sp, margin != 0)) | | 1439 | if (vs_refresh(sp, margin != 0)) |
1442 | return (1); | | 1440 | return (1); |
1443 | } | | 1441 | } |
1444 | | | 1442 | |
1445 | /* 6: Proceed with the incremental search. */ | | 1443 | /* 6: Proceed with the incremental search. */ |
1446 | if (FL_ISSET(is_flags, IS_RUNNING) && txt_isrch(sp, vp, tp, &is_flags)) | | 1444 | if (FL_ISSET(is_flags, IS_RUNNING) && txt_isrch(sp, vp, tp, &is_flags)) |
1447 | return (1); | | 1445 | return (1); |
1448 | | | 1446 | |
1449 | /* 7: Next character... */ | | 1447 | /* 7: Next character... */ |
1450 | if (LF_ISSET(TXT_REPLAY)) | | 1448 | if (LF_ISSET(TXT_REPLAY)) |
1451 | goto replay; | | 1449 | goto replay; |
1452 | goto next; | | 1450 | goto next; |
1453 | | | 1451 | |
1454 | done: /* Leave input mode. */ | | 1452 | done: /* Leave input mode. */ |
1455 | F_CLR(sp, SC_TINPUT); | | 1453 | F_CLR(sp, SC_TINPUT); |
1456 | | | 1454 | |
1457 | /* If recording for playback, save it. */ | | 1455 | /* If recording for playback, save it. */ |
1458 | if (LF_ISSET(TXT_RECORD)) | | 1456 | if (LF_ISSET(TXT_RECORD)) |
1459 | vip->rep_cnt = rcol; | | 1457 | vip->rep_cnt = rcol; |
1460 | | | 1458 | |
1461 | /* | | 1459 | /* |
1462 | * If not working on the colon command line, set the final cursor | | 1460 | * If not working on the colon command line, set the final cursor |
1463 | * position. | | 1461 | * position. |
1464 | */ | | 1462 | */ |
1465 | if (!F_ISSET(sp, SC_TINPUT_INFO)) { | | 1463 | if (!F_ISSET(sp, SC_TINPUT_INFO)) { |
1466 | vp->m_final.lno = tp->lno; | | 1464 | vp->m_final.lno = tp->lno; |
1467 | vp->m_final.cno = tp->cno; | | 1465 | vp->m_final.cno = tp->cno; |
1468 | } | | 1466 | } |
1469 | return (0); | | 1467 | return (0); |
1470 | | | 1468 | |
1471 | err: | | 1469 | err: |
1472 | alloc_err: | | 1470 | alloc_err: |
1473 | F_CLR(sp, SC_TINPUT); | | 1471 | F_CLR(sp, SC_TINPUT); |
1474 | txt_err(sp, &sp->tiq); | | 1472 | txt_err(sp, &sp->tiq); |
1475 | return (1); | | 1473 | return (1); |
1476 | } | | 1474 | } |
1477 | | | 1475 | |
1478 | /* | | 1476 | /* |
1479 | * txt_abbrev -- | | 1477 | * txt_abbrev -- |
1480 | * Handle abbreviations. | | 1478 | * Handle abbreviations. |
1481 | */ | | 1479 | */ |
1482 | static int | | 1480 | static int |
1483 | txt_abbrev(SCR *sp, TEXT *tp, ARG_CHAR_T *pushcp, int isinfoline, int *didsubp, int *turnoffp) | | 1481 | txt_abbrev(SCR *sp, TEXT *tp, ARG_CHAR_T *pushcp, int isinfoline, int *didsubp, int *turnoffp) |
1484 | { | | 1482 | { |
1485 | CHAR_T ch, *p; | | 1483 | CHAR_T ch, *p; |
1486 | SEQ *qp; | | 1484 | SEQ *qp; |
1487 | size_t len, off; | | 1485 | size_t len, off; |
1488 | | | 1486 | |
1489 | /* Check to make sure we're not at the start of an append. */ | | 1487 | /* Check to make sure we're not at the start of an append. */ |
1490 | *didsubp = 0; | | 1488 | *didsubp = 0; |
1491 | if (tp->cno == tp->offset) | | 1489 | if (tp->cno == tp->offset) |
1492 | return (0); | | 1490 | return (0); |
1493 | | | 1491 | |
1494 | /* | | 1492 | /* |
1495 | * Find the start of the "word". | | 1493 | * Find the start of the "word". |
1496 | * | | 1494 | * |
1497 | * !!! | | 1495 | * !!! |
1498 | * We match historic practice, which, as far as I can tell, had an | | 1496 | * We match historic practice, which, as far as I can tell, had an |
1499 | * off-by-one error. The way this worked was that when the inserted | | 1497 | * off-by-one error. The way this worked was that when the inserted |
1500 | * text switched from a "word" character to a non-word character, | | 1498 | * text switched from a "word" character to a non-word character, |
1501 | * vi would check for possible abbreviations. It would then take the | | 1499 | * vi would check for possible abbreviations. It would then take the |
1502 | * type (i.e. word/non-word) of the character entered TWO characters | | 1500 | * type (i.e. word/non-word) of the character entered TWO characters |
1503 | * ago, and move backward in the text until reaching a character that | | 1501 | * ago, and move backward in the text until reaching a character that |
1504 | * was not that type, or the beginning of the insert, the line, or | | 1502 | * was not that type, or the beginning of the insert, the line, or |
1505 | * the file. For example, in the string "abc<space>", when the <space> | | 1503 | * the file. For example, in the string "abc<space>", when the <space> |
1506 | * character triggered the abbreviation check, the type of the 'b' | | 1504 | * character triggered the abbreviation check, the type of the 'b' |
1507 | * character was used for moving through the string. Maybe there's a | | 1505 | * character was used for moving through the string. Maybe there's a |
1508 | * reason for not using the first (i.e. 'c') character, but I can't | | 1506 | * reason for not using the first (i.e. 'c') character, but I can't |
1509 | * think of one. | | 1507 | * think of one. |
1510 | * | | 1508 | * |
1511 | * Terminate at the beginning of the insert or the character after the | | 1509 | * Terminate at the beginning of the insert or the character after the |
1512 | * offset character -- both can be tested for using tp->offset. | | 1510 | * offset character -- both can be tested for using tp->offset. |
1513 | */ | | 1511 | */ |
1514 | off = tp->cno - 1; /* Previous character. */ | | 1512 | off = tp->cno - 1; /* Previous character. */ |
1515 | p = tp->lb + off; | | 1513 | p = tp->lb + off; |
1516 | len = 1; /* One character test. */ | | 1514 | len = 1; /* One character test. */ |
1517 | if (off == tp->offset || ISBLANK((UCHAR_T)p[-1])) | | 1515 | if (off == tp->offset || ISBLANK((UCHAR_T)p[-1])) |
1518 | goto search; | | 1516 | goto search; |
1519 | if (inword((UCHAR_T)p[-1])) /* Move backward to change. */ | | 1517 | if (inword((UCHAR_T)p[-1])) /* Move backward to change. */ |
1520 | for (;;) { | | 1518 | for (;;) { |
1521 | --off; --p; ++len; | | 1519 | --off; --p; ++len; |
1522 | if (off == tp->offset || !inword((UCHAR_T)p[-1])) | | 1520 | if (off == tp->offset || !inword((UCHAR_T)p[-1])) |
1523 | break; | | 1521 | break; |
1524 | } | | 1522 | } |
1525 | else | | 1523 | else |
1526 | for (;;) { | | 1524 | for (;;) { |
1527 | --off; --p; ++len; | | 1525 | --off; --p; ++len; |
1528 | if (off == tp->offset || | | 1526 | if (off == tp->offset || |
1529 | inword((UCHAR_T)p[-1]) || ISBLANK((UCHAR_T)p[-1])) | | 1527 | inword((UCHAR_T)p[-1]) || ISBLANK((UCHAR_T)p[-1])) |
1530 | break; | | 1528 | break; |
1531 | } | | 1529 | } |
1532 | | | 1530 | |
1533 | /* | | 1531 | /* |
1534 | * !!! | | 1532 | * !!! |
1535 | * Historic vi exploded abbreviations on the command line. This has | | 1533 | * Historic vi exploded abbreviations on the command line. This has |
1536 | * obvious problems in that unabbreviating the string can be extremely | | 1534 | * obvious problems in that unabbreviating the string can be extremely |
1537 | * tricky, particularly if the string has, say, an embedded escape | | 1535 | * tricky, particularly if the string has, say, an embedded escape |
1538 | * character. Personally, I think it's a stunningly bad idea. Other | | 1536 | * character. Personally, I think it's a stunningly bad idea. Other |
1539 | * examples of problems this caused in historic vi are: | | 1537 | * examples of problems this caused in historic vi are: |
1540 | * :ab foo bar | | 1538 | * :ab foo bar |
1541 | * :ab foo baz | | 1539 | * :ab foo baz |
1542 | * results in "bar" being abbreviated to "baz", which wasn't what the | | 1540 | * results in "bar" being abbreviated to "baz", which wasn't what the |
1543 | * user had in mind at all. Also, the commands: | | 1541 | * user had in mind at all. Also, the commands: |
1544 | * :ab foo bar | | 1542 | * :ab foo bar |
1545 | * :unab foo<space> | | 1543 | * :unab foo<space> |
1546 | * resulted in an error message that "bar" wasn't mapped. Finally, | | 1544 | * resulted in an error message that "bar" wasn't mapped. Finally, |
1547 | * since the string was already exploded by the time the unabbreviate | | 1545 | * since the string was already exploded by the time the unabbreviate |
1548 | * command got it, all it knew was that an abbreviation had occurred. | | 1546 | * command got it, all it knew was that an abbreviation had occurred. |
1549 | * Cleverly, it checked the replacement string for its unabbreviation | | 1547 | * Cleverly, it checked the replacement string for its unabbreviation |
1550 | * match, which meant that the commands: | | 1548 | * match, which meant that the commands: |
1551 | * :ab foo1 bar | | 1549 | * :ab foo1 bar |
1552 | * :ab foo2 bar | | 1550 | * :ab foo2 bar |
1553 | * :unab foo2 | | 1551 | * :unab foo2 |
1554 | * unabbreviate "foo1", and the commands: | | 1552 | * unabbreviate "foo1", and the commands: |
1555 | * :ab foo bar | | 1553 | * :ab foo bar |
1556 | * :ab bar baz | | 1554 | * :ab bar baz |
1557 | * unabbreviate "foo"! | | 1555 | * unabbreviate "foo"! |
1558 | * | | 1556 | * |
1559 | * Anyway, people neglected to first ask my opinion before they wrote | | 1557 | * Anyway, people neglected to first ask my opinion before they wrote |
1560 | * macros that depend on this stuff, so, we make this work as follows. | | 1558 | * macros that depend on this stuff, so, we make this work as follows. |
1561 | * When checking for an abbreviation on the command line, if we get a | | 1559 | * When checking for an abbreviation on the command line, if we get a |
1562 | * string which is <blank> terminated and which starts at the beginning | | 1560 | * string which is <blank> terminated and which starts at the beginning |
1563 | * of the line, we check to see it is the abbreviate or unabbreviate | | 1561 | * of the line, we check to see it is the abbreviate or unabbreviate |
1564 | * commands. If it is, turn abbreviations off and return as if no | | 1562 | * commands. If it is, turn abbreviations off and return as if no |
1565 | * abbreviation was found. Note also, minor trickiness, so that if | | 1563 | * abbreviation was found. Note also, minor trickiness, so that if |
1566 | * the user erases the line and starts another command, we turn the | | 1564 | * the user erases the line and starts another command, we turn the |
1567 | * abbreviations back on. | | 1565 | * abbreviations back on. |
1568 | * | | 1566 | * |
1569 | * This makes the layering look like a Nachos Supreme. | | 1567 | * This makes the layering look like a Nachos Supreme. |
1570 | */ | | 1568 | */ |
1571 | search: if (isinfoline) { | | 1569 | search: if (isinfoline) { |
1572 | if (off == tp->ai || off == tp->offset) | | 1570 | if (off == tp->ai || off == tp->offset) |
1573 | if (ex_is_abbrev(sp, p, len)) { | | 1571 | if (ex_is_abbrev(sp, p, len)) { |
1574 | *turnoffp = 1; | | 1572 | *turnoffp = 1; |
1575 | return (0); | | 1573 | return (0); |
1576 | } else | | 1574 | } else |
1577 | *turnoffp = 0; | | 1575 | *turnoffp = 0; |
1578 | else | | 1576 | else |
1579 | if (*turnoffp) | | 1577 | if (*turnoffp) |
1580 | return (0); | | 1578 | return (0); |
1581 | } | | 1579 | } |
1582 | | | 1580 | |
1583 | /* Check for any abbreviations. */ | | 1581 | /* Check for any abbreviations. */ |
1584 | if ((qp = seq_find(sp, NULL, NULL, p, len, SEQ_ABBREV, NULL)) == NULL) | | 1582 | if ((qp = seq_find(sp, NULL, NULL, p, len, SEQ_ABBREV, NULL)) == NULL) |
1585 | return (0); | | 1583 | return (0); |
1586 | | | 1584 | |
1587 | /* | | 1585 | /* |
1588 | * Push the abbreviation onto the tty stack. Historically, characters | | 1586 | * Push the abbreviation onto the tty stack. Historically, characters |
1589 | * resulting from an abbreviation expansion were themselves subject to | | 1587 | * resulting from an abbreviation expansion were themselves subject to |
1590 | * map expansions, O_SHOWMATCH matching etc. This means the expanded | | 1588 | * map expansions, O_SHOWMATCH matching etc. This means the expanded |
1591 | * characters will be re-tested for abbreviations. It's difficult to | | 1589 | * characters will be re-tested for abbreviations. It's difficult to |
1592 | * know what historic practice in this case was, since abbreviations | | 1590 | * know what historic practice in this case was, since abbreviations |
1593 | * were applied to :colon command lines, so entering abbreviations that | | 1591 | * were applied to :colon command lines, so entering abbreviations that |
1594 | * looped was tricky, although possible. In addition, obvious loops | | 1592 | * looped was tricky, although possible. In addition, obvious loops |
1595 | * didn't work as expected. (The command ':ab a b|ab b c|ab c a' will | | 1593 | * didn't work as expected. (The command ':ab a b|ab b c|ab c a' will |
1596 | * silently only implement and/or display the last abbreviation.) | | 1594 | * silently only implement and/or display the last abbreviation.) |
1597 | * | | 1595 | * |
1598 | * This implementation doesn't recover well from such abbreviations. | | 1596 | * This implementation doesn't recover well from such abbreviations. |
1599 | * The main input loop counts abbreviated characters, and, when it | | 1597 | * The main input loop counts abbreviated characters, and, when it |
1600 | * reaches a limit, discards any abbreviated characters on the queue. | | 1598 | * reaches a limit, discards any abbreviated characters on the queue. |
1601 | * It's difficult to back up to the original position, as the replay | | 1599 | * It's difficult to back up to the original position, as the replay |
1602 | * queue would have to be adjusted, and the line state when an initial | | 1600 | * queue would have to be adjusted, and the line state when an initial |
1603 | * abbreviated character was received would have to be saved. | | 1601 | * abbreviated character was received would have to be saved. |
1604 | */ | | 1602 | */ |
1605 | ch = (UCHAR_T)*pushcp; | | 1603 | ch = (UCHAR_T)*pushcp; |
1606 | if (v_event_push(sp, NULL, &ch, 1, CH_ABBREVIATED)) | | 1604 | if (v_event_push(sp, NULL, &ch, 1, CH_ABBREVIATED)) |
1607 | return (1); | | 1605 | return (1); |
1608 | if (v_event_push(sp, NULL, qp->output, qp->olen, CH_ABBREVIATED)) | | 1606 | if (v_event_push(sp, NULL, qp->output, qp->olen, CH_ABBREVIATED)) |
1609 | return (1); | | 1607 | return (1); |
1610 | | | 1608 | |
1611 | /* | | 1609 | /* |
1612 | * If the size of the abbreviation is larger than or equal to the size | | 1610 | * If the size of the abbreviation is larger than or equal to the size |
1613 | * of the original text, move to the start of the replaced characters, | | 1611 | * of the original text, move to the start of the replaced characters, |
1614 | * and add their length to the overwrite count. | | 1612 | * and add their length to the overwrite count. |
1615 | * | | 1613 | * |
1616 | * If the abbreviation is smaller than the original text, we have to | | 1614 | * If the abbreviation is smaller than the original text, we have to |
1617 | * delete the additional overwrite characters and copy down any insert | | 1615 | * delete the additional overwrite characters and copy down any insert |
1618 | * characters. | | 1616 | * characters. |
1619 | */ | | 1617 | */ |
1620 | tp->cno -= len; | | 1618 | tp->cno -= len; |
1621 | if (qp->olen >= len) | | 1619 | if (qp->olen >= len) |
1622 | tp->owrite += len; | | 1620 | tp->owrite += len; |
1623 | else { | | 1621 | else { |
1624 | if (tp->insert) | | 1622 | if (tp->insert) |
1625 | MEMMOVEW(tp->lb + tp->cno + qp->olen, | | 1623 | MEMMOVEW(tp->lb + tp->cno + qp->olen, |
1626 | tp->lb + tp->cno + tp->owrite + len, tp->insert); | | 1624 | tp->lb + tp->cno + tp->owrite + len, tp->insert); |
1627 | tp->owrite += qp->olen; | | 1625 | tp->owrite += qp->olen; |
1628 | tp->len -= len - qp->olen; | | 1626 | tp->len -= len - qp->olen; |
1629 | } | | 1627 | } |
1630 | | | 1628 | |
1631 | /* | | 1629 | /* |
1632 | * We return the length of the abbreviated characters. This is so | | 1630 | * We return the length of the abbreviated characters. This is so |
1633 | * the calling routine can replace the replay characters with the | | 1631 | * the calling routine can replace the replay characters with the |
1634 | * abbreviation. This means that subsequent '.' commands will produce | | 1632 | * abbreviation. This means that subsequent '.' commands will produce |
1635 | * the same text, regardless of intervening :[un]abbreviate commands. | | 1633 | * the same text, regardless of intervening :[un]abbreviate commands. |
1636 | * This is historic practice. | | 1634 | * This is historic practice. |
1637 | */ | | 1635 | */ |
1638 | *didsubp = len; | | 1636 | *didsubp = len; |
1639 | return (0); | | 1637 | return (0); |
1640 | } | | 1638 | } |
1641 | | | 1639 | |
1642 | /* | | 1640 | /* |
1643 | * txt_unmap -- | | 1641 | * txt_unmap -- |
1644 | * Handle the unmap command. | | 1642 | * Handle the unmap command. |
1645 | */ | | 1643 | */ |
1646 | static void | | 1644 | static void |
1647 | txt_unmap(SCR *sp, TEXT *tp, u_int32_t *ec_flagsp) | | 1645 | txt_unmap(SCR *sp, TEXT *tp, u_int32_t *ec_flagsp) |
1648 | { | | 1646 | { |
1649 | size_t len, off; | | 1647 | size_t len, off; |
1650 | CHAR_T *p; | | 1648 | CHAR_T *p; |
1651 | | | 1649 | |
1652 | /* Find the beginning of this "word". */ | | 1650 | /* Find the beginning of this "word". */ |
1653 | for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) { | | 1651 | for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) { |
1654 | if (ISBLANK((UCHAR_T)*p)) { | | 1652 | if (ISBLANK((UCHAR_T)*p)) { |
1655 | ++p; | | 1653 | ++p; |
1656 | break; | | 1654 | break; |
1657 | } | | 1655 | } |
1658 | ++len; | | 1656 | ++len; |
1659 | if (off == tp->ai || off == tp->offset) | | 1657 | if (off == tp->ai || off == tp->offset) |
1660 | break; | | 1658 | break; |
1661 | } | | 1659 | } |
1662 | | | 1660 | |
1663 | /* | | 1661 | /* |
1664 | * !!! | | 1662 | * !!! |
1665 | * Historic vi exploded input mappings on the command line. See the | | 1663 | * Historic vi exploded input mappings on the command line. See the |
1666 | * txt_abbrev() routine for an explanation of the problems inherent | | 1664 | * txt_abbrev() routine for an explanation of the problems inherent |
1667 | * in this. | | 1665 | * in this. |
1668 | * | | 1666 | * |
1669 | * We make this work as follows. If we get a string which is <blank> | | 1667 | * We make this work as follows. If we get a string which is <blank> |
1670 | * terminated and which starts at the beginning of the line, we check | | 1668 | * terminated and which starts at the beginning of the line, we check |
1671 | * to see it is the unmap command. If it is, we return that the input | | 1669 | * to see it is the unmap command. If it is, we return that the input |
1672 | * mapping should be turned off. Note also, minor trickiness, so that | | 1670 | * mapping should be turned off. Note also, minor trickiness, so that |
1673 | * if the user erases the line and starts another command, we go ahead | | 1671 | * if the user erases the line and starts another command, we go ahead |
1674 | * an turn mapping back on. | | 1672 | * an turn mapping back on. |
1675 | */ | | 1673 | */ |
1676 | if ((off == tp->ai || off == tp->offset) && ex_is_unmap(sp, p, len)) | | 1674 | if ((off == tp->ai || off == tp->offset) && ex_is_unmap(sp, p, len)) |
1677 | FL_CLR(*ec_flagsp, EC_MAPINPUT); | | 1675 | FL_CLR(*ec_flagsp, EC_MAPINPUT); |
1678 | else | | 1676 | else |
1679 | FL_SET(*ec_flagsp, EC_MAPINPUT); | | 1677 | FL_SET(*ec_flagsp, EC_MAPINPUT); |
1680 | } | | 1678 | } |
1681 | | | 1679 | |
1682 | /* | | 1680 | /* |
1683 | * txt_ai_resolve -- | | 1681 | * txt_ai_resolve -- |
1684 | * When a line is resolved by <esc>, review autoindent characters. | | 1682 | * When a line is resolved by <esc>, review autoindent characters. |
1685 | */ | | 1683 | */ |
1686 | static void | | 1684 | static void |
1687 | txt_ai_resolve(SCR *sp, TEXT *tp, int *changedp) | | 1685 | txt_ai_resolve(SCR *sp, TEXT *tp, int *changedp) |
1688 | { | | 1686 | { |
1689 | u_long ts; | | 1687 | u_long ts; |
1690 | int delc; | | 1688 | int delc; |
1691 | size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs; | | 1689 | size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs; |
1692 | CHAR_T *p; | | 1690 | CHAR_T *p; |
1693 | | | 1691 | |
1694 | *changedp = 0; | | 1692 | *changedp = 0; |
1695 | | | 1693 | |
1696 | /* | | 1694 | /* |
1697 | * If the line is empty, has an offset, or no autoindent | | 1695 | * If the line is empty, has an offset, or no autoindent |
1698 | * characters, we're done. | | 1696 | * characters, we're done. |
1699 | */ | | 1697 | */ |
1700 | if (!tp->len || tp->offset || !tp->ai) | | 1698 | if (!tp->len || tp->offset || !tp->ai) |
1701 | return; | | 1699 | return; |
1702 | | | 1700 | |
1703 | /* | | 1701 | /* |
1704 | * If the length is less than or equal to the autoindent | | 1702 | * If the length is less than or equal to the autoindent |
1705 | * characters, delete them. | | 1703 | * characters, delete them. |
1706 | */ | | 1704 | */ |
1707 | if (tp->len <= tp->ai) { | | 1705 | if (tp->len <= tp->ai) { |
1708 | tp->ai = tp->cno = tp->len = 0; | | 1706 | tp->ai = tp->cno = tp->len = 0; |
1709 | return; | | 1707 | return; |
1710 | } | | 1708 | } |
1711 | | | 1709 | |
1712 | /* | | 1710 | /* |
1713 | * The autoindent characters plus any leading <blank> characters | | 1711 | * The autoindent characters plus any leading <blank> characters |
1714 | * in the line are resolved into the minimum number of characters. | | 1712 | * in the line are resolved into the minimum number of characters. |
1715 | * Historic practice. | | 1713 | * Historic practice. |
1716 | */ | | 1714 | */ |
1717 | ts = O_VAL(sp, O_TABSTOP); | | 1715 | ts = O_VAL(sp, O_TABSTOP); |
1718 | | | 1716 | |
1719 | /* Figure out the last <blank> screen column. */ | | 1717 | /* Figure out the last <blank> screen column. */ |
1720 | for (p = tp->lb, scno = 0, len = tp->len, | | 1718 | for (p = tp->lb, scno = 0, len = tp->len, |
1721 | spaces = tab_after_sp = 0; len-- && ISBLANK((UCHAR_T)*p); ++p) | | 1719 | spaces = tab_after_sp = 0; len-- && ISBLANK((UCHAR_T)*p); ++p) |
1722 | if (*p == '\t') { | | 1720 | if (*p == '\t') { |
1723 | if (spaces) | | 1721 | if (spaces) |
1724 | tab_after_sp = 1; | | 1722 | tab_after_sp = 1; |
1725 | scno += COL_OFF(scno, ts); | | 1723 | scno += COL_OFF(scno, ts); |
1726 | } else { | | 1724 | } else { |
1727 | ++spaces; | | 1725 | ++spaces; |
1728 | ++scno; | | 1726 | ++scno; |
1729 | } | | 1727 | } |
1730 | | | 1728 | |
1731 | /* | | 1729 | /* |
1732 | * If there are no spaces, or no tabs after spaces and less than | | 1730 | * If there are no spaces, or no tabs after spaces and less than |
1733 | * ts spaces, it's already minimal. | | 1731 | * ts spaces, it's already minimal. |
1734 | * Keep analysing if expandtab is set. | | 1732 | * Keep analysing if expandtab is set. |
1735 | */ | | 1733 | */ |
1736 | if ((!spaces || (!tab_after_sp && spaces < ts)) && | | 1734 | if ((!spaces || (!tab_after_sp && spaces < ts)) && |
1737 | !O_ISSET(sp, O_EXPANDTAB)) | | 1735 | !O_ISSET(sp, O_EXPANDTAB)) |
1738 | return; | | 1736 | return; |
1739 | | | 1737 | |
1740 | /* Count up spaces/tabs needed to get to the target. */ | | 1738 | /* Count up spaces/tabs needed to get to the target. */ |
1741 | cno = 0; | | 1739 | cno = 0; |
1742 | tabs = 0; | | 1740 | tabs = 0; |
1743 | if (!O_ISSET(sp, O_EXPANDTAB)) { | | 1741 | if (!O_ISSET(sp, O_EXPANDTAB)) { |
1744 | for (; cno + COL_OFF(cno, ts) <= scno; ++tabs) | | 1742 | for (; cno + COL_OFF(cno, ts) <= scno; ++tabs) |
1745 | cno += COL_OFF(cno, ts); | | 1743 | cno += COL_OFF(cno, ts); |
1746 | } | | 1744 | } |
1747 | spaces = scno - cno; | | 1745 | spaces = scno - cno; |
1748 | | | 1746 | |
1749 | /* | | 1747 | /* |
1750 | * Figure out how many characters we're dropping -- if we're not | | 1748 | * Figure out how many characters we're dropping -- if we're not |
1751 | * dropping any, it's already minimal, we're done. | | 1749 | * dropping any, it's already minimal, we're done. |
1752 | */ | | 1750 | */ |
1753 | old = p - tp->lb; | | 1751 | old = p - tp->lb; |
1754 | new = spaces + tabs; | | 1752 | new = spaces + tabs; |
1755 | if (old == new) | | 1753 | if (old == new) |
1756 | return; | | 1754 | return; |
1757 | | | 1755 | |
1758 | /* Shift the rest of the characters down, adjust the counts. */ | | 1756 | /* Shift the rest of the characters down, adjust the counts. */ |
1759 | delc = old - new; | | 1757 | delc = old - new; |
1760 | MEMMOVEW(p - delc, p, tp->len - old); | | 1758 | MEMMOVEW(p - delc, p, tp->len - old); |
1761 | tp->len -= delc; | | 1759 | tp->len -= delc; |
1762 | tp->cno -= delc; | | 1760 | tp->cno -= delc; |
1763 | | | 1761 | |
1764 | /* Fill in space/tab characters. */ | | 1762 | /* Fill in space/tab characters. */ |
1765 | for (p = tp->lb; tabs--;) | | 1763 | for (p = tp->lb; tabs--;) |
1766 | *p++ = '\t'; | | 1764 | *p++ = '\t'; |
1767 | while (spaces--) | | 1765 | while (spaces--) |
1768 | *p++ = ' '; | | 1766 | *p++ = ' '; |
1769 | *changedp = 1; | | 1767 | *changedp = 1; |
1770 | } | | 1768 | } |
1771 | | | 1769 | |
1772 | /* | | 1770 | /* |
1773 | * v_txt_auto -- | | 1771 | * v_txt_auto -- |
1774 | * Handle autoindent. If aitp isn't NULL, use it, otherwise, | | 1772 | * Handle autoindent. If aitp isn't NULL, use it, otherwise, |
1775 | * retrieve the line. | | 1773 | * retrieve the line. |
1776 | * | | 1774 | * |
1777 | * PUBLIC: int v_txt_auto __P((SCR *, db_recno_t, TEXT *, size_t, TEXT *)); | | 1775 | * PUBLIC: int v_txt_auto __P((SCR *, db_recno_t, TEXT *, size_t, TEXT *)); |
1778 | */ | | 1776 | */ |
1779 | int | | 1777 | int |
1780 | v_txt_auto(SCR *sp, db_recno_t lno, TEXT *aitp, size_t len, TEXT *tp) | | 1778 | v_txt_auto(SCR *sp, db_recno_t lno, TEXT *aitp, size_t len, TEXT *tp) |
1781 | { | | 1779 | { |
1782 | size_t nlen; | | 1780 | size_t nlen; |
1783 | CHAR_T *p, *t; | | 1781 | CHAR_T *p, *t; |
1784 | | | 1782 | |
1785 | if (aitp == NULL) { | | 1783 | if (aitp == NULL) { |
1786 | /* | | 1784 | /* |
1787 | * If the ex append command is executed with an address of 0, | | 1785 | * If the ex append command is executed with an address of 0, |
1788 | * it's possible to get here with a line number of 0. Return | | 1786 | * it's possible to get here with a line number of 0. Return |
1789 | * an indent of 0. | | 1787 | * an indent of 0. |
1790 | */ | | 1788 | */ |
1791 | if (lno == 0) { | | 1789 | if (lno == 0) { |
1792 | tp->ai = 0; | | 1790 | tp->ai = 0; |
1793 | return (0); | | 1791 | return (0); |
1794 | } | | 1792 | } |
1795 | if (db_get(sp, lno, DBG_FATAL, &t, &len)) | | 1793 | if (db_get(sp, lno, DBG_FATAL, &t, &len)) |
1796 | return (1); | | 1794 | return (1); |
1797 | } else | | 1795 | } else |
1798 | t = aitp->lb; | | 1796 | t = aitp->lb; |
1799 | | | 1797 | |
1800 | /* Count whitespace characters. */ | | 1798 | /* Count whitespace characters. */ |
1801 | for (p = t; len > 0; ++p, --len) | | 1799 | for (p = t; len > 0; ++p, --len) |
1802 | if (!ISBLANK((UCHAR_T)*p)) | | 1800 | if (!ISBLANK((UCHAR_T)*p)) |
1803 | break; | | 1801 | break; |
1804 | | | 1802 | |
1805 | /* Set count, check for no indentation. */ | | 1803 | /* Set count, check for no indentation. */ |
1806 | if ((nlen = (p - t)) == 0) | | 1804 | if ((nlen = (p - t)) == 0) |
1807 | return (0); | | 1805 | return (0); |
1808 | | | 1806 | |
1809 | /* Make sure the buffer's big enough. */ | | 1807 | /* Make sure the buffer's big enough. */ |
1810 | BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + nlen); | | 1808 | BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + nlen); |
1811 | | | 1809 | |
1812 | /* Copy the buffer's current contents up. */ | | 1810 | /* Copy the buffer's current contents up. */ |
1813 | if (tp->len != 0) | | 1811 | if (tp->len != 0) |
1814 | MEMMOVEW(tp->lb + nlen, tp->lb, tp->len); | | 1812 | MEMMOVEW(tp->lb + nlen, tp->lb, tp->len); |
1815 | tp->len += nlen; | | 1813 | tp->len += nlen; |
1816 | | | 1814 | |
1817 | /* Copy the indentation into the new buffer. */ | | 1815 | /* Copy the indentation into the new buffer. */ |
1818 | MEMMOVEW(tp->lb, t, nlen); | | 1816 | MEMMOVEW(tp->lb, t, nlen); |
1819 | | | 1817 | |
1820 | /* Set the autoindent count. */ | | 1818 | /* Set the autoindent count. */ |
1821 | tp->ai = nlen; | | 1819 | tp->ai = nlen; |
1822 | return (0); | | 1820 | return (0); |
1823 | } | | 1821 | } |
1824 | | | 1822 | |
1825 | /* | | 1823 | /* |
1826 | * txt_backup -- | | 1824 | * txt_backup -- |
1827 | * Back up to the previously edited line. | | 1825 | * Back up to the previously edited line. |
1828 | */ | | 1826 | */ |
1829 | static TEXT * | | 1827 | static TEXT * |
1830 | txt_backup(SCR *sp, TEXTH *tiqh, TEXT *tp, u_int32_t *flagsp) | | 1828 | txt_backup(SCR *sp, TEXTH *tiqh, TEXT *tp, u_int32_t *flagsp) |
1831 | { | | 1829 | { |
1832 | TEXT *ntp; | | 1830 | TEXT *ntp; |
1833 | | | 1831 | |
1834 | /* Get a handle on the previous TEXT structure. */ | | 1832 | /* Get a handle on the previous TEXT structure. */ |
1835 | if ((ntp = TAILQ_PREV(tp, _texth, q)) == NULL) { | | 1833 | if ((ntp = TAILQ_PREV(tp, _texth, q)) == NULL) { |
1836 | if (!FL_ISSET(*flagsp, TXT_REPLAY)) | | 1834 | if (!FL_ISSET(*flagsp, TXT_REPLAY)) |
1837 | msgq(sp, M_BERR, | | 1835 | msgq(sp, M_BERR, |
1838 | "193|Already at the beginning of the insert"); | | 1836 | "193|Already at the beginning of the insert"); |
1839 | return (tp); | | 1837 | return (tp); |
1840 | } | | 1838 | } |
1841 | | | 1839 | |
1842 | /* Bookkeeping. */ | | 1840 | /* Bookkeeping. */ |
1843 | ntp->len = ntp->sv_len; | | 1841 | ntp->len = ntp->sv_len; |
1844 | | | 1842 | |
1845 | /* Handle appending to the line. */ | | 1843 | /* Handle appending to the line. */ |
1846 | if (ntp->owrite == 0 && ntp->insert == 0) { | | 1844 | if (ntp->owrite == 0 && ntp->insert == 0) { |
1847 | ntp->lb[ntp->len] = CH_CURSOR; | | 1845 | ntp->lb[ntp->len] = CH_CURSOR; |
1848 | ++ntp->insert; | | 1846 | ++ntp->insert; |
1849 | ++ntp->len; | | 1847 | ++ntp->len; |
1850 | FL_SET(*flagsp, TXT_APPENDEOL); | | 1848 | FL_SET(*flagsp, TXT_APPENDEOL); |
1851 | } else | | 1849 | } else |
1852 | FL_CLR(*flagsp, TXT_APPENDEOL); | | 1850 | FL_CLR(*flagsp, TXT_APPENDEOL); |
1853 | | | 1851 | |
1854 | /* Release the current TEXT. */ | | 1852 | /* Release the current TEXT. */ |
1855 | TAILQ_REMOVE(tiqh, tp, q); | | 1853 | TAILQ_REMOVE(tiqh, tp, q); |
1856 | text_free(tp); | | 1854 | text_free(tp); |
1857 | | | 1855 | |
1858 | /* Update the old line on the screen. */ | | 1856 | /* Update the old line on the screen. */ |
1859 | if (vs_change(sp, ntp->lno + 1, LINE_DELETE)) | | 1857 | if (vs_change(sp, ntp->lno + 1, LINE_DELETE)) |
1860 | return (NULL); | | 1858 | return (NULL); |
1861 | | | 1859 | |
1862 | /* Return the new/current TEXT. */ | | 1860 | /* Return the new/current TEXT. */ |
1863 | return (ntp); | | 1861 | return (ntp); |
1864 | } | | 1862 | } |
1865 | | | 1863 | |
1866 | /* | | 1864 | /* |
1867 | * Text indentation is truly strange. ^T and ^D do movements to the next or | | 1865 | * Text indentation is truly strange. ^T and ^D do movements to the next or |
1868 | * previous shiftwidth value, i.e. for a 1-based numbering, with shiftwidth=3, | | 1866 | * previous shiftwidth value, i.e. for a 1-based numbering, with shiftwidth=3, |
1869 | * ^T moves a cursor on the 7th, 8th or 9th column to the 10th column, and ^D | | 1867 | * ^T moves a cursor on the 7th, 8th or 9th column to the 10th column, and ^D |
1870 | * moves it back. | | 1868 | * moves it back. |
1871 | * | | 1869 | * |
1872 | * !!! | | 1870 | * !!! |
1873 | * The ^T and ^D characters in historical vi had special meaning only when they | | 1871 | * The ^T and ^D characters in historical vi had special meaning only when they |
1874 | * were the first characters entered after entering text input mode. As normal | | 1872 | * were the first characters entered after entering text input mode. As normal |
1875 | * erase characters couldn't erase autoindent characters (^T in this case), it | | 1873 | * erase characters couldn't erase autoindent characters (^T in this case), it |
1876 | * meant that inserting text into previously existing text was strange -- ^T | | 1874 | * meant that inserting text into previously existing text was strange -- ^T |
1877 | * only worked if it was the first keystroke(s), and then could only be erased | | 1875 | * only worked if it was the first keystroke(s), and then could only be erased |
1878 | * using ^D. This implementation treats ^T specially anywhere it occurs in the | | 1876 | * using ^D. This implementation treats ^T specially anywhere it occurs in the |
1879 | * input, and permits the standard erase characters to erase the characters it | | 1877 | * input, and permits the standard erase characters to erase the characters it |
1880 | * inserts. | | 1878 | * inserts. |
1881 | * | | 1879 | * |
1882 | * !!! | | 1880 | * !!! |
1883 | * A fun test is to try: | | 1881 | * A fun test is to try: |
1884 | * :se sw=4 ai list | | 1882 | * :se sw=4 ai list |
1885 | * i<CR>^Tx<CR>^Tx<CR>^Tx<CR>^Dx<CR>^Dx<CR>^Dx<esc> | | 1883 | * i<CR>^Tx<CR>^Tx<CR>^Tx<CR>^Dx<CR>^Dx<CR>^Dx<esc> |
1886 | * Historic vi loses some of the '$' marks on the line ends, but otherwise gets | | 1884 | * Historic vi loses some of the '$' marks on the line ends, but otherwise gets |
1887 | * it right. | | 1885 | * it right. |
1888 | * | | 1886 | * |
1889 | * XXX | | 1887 | * XXX |
1890 | * Technically, txt_dent should be part of the screen interface, as it requires | | 1888 | * Technically, txt_dent should be part of the screen interface, as it requires |
1891 | * knowledge of character sizes, including <space>s, on the screen. It's here | | 1889 | * knowledge of character sizes, including <space>s, on the screen. It's here |
1892 | * because it's a complicated little beast, and I didn't want to shove it down | | 1890 | * because it's a complicated little beast, and I didn't want to shove it down |
1893 | * into the screen. It's probable that KEY_LEN will call into the screen once | | 1891 | * into the screen. It's probable that KEY_LEN will call into the screen once |
1894 | * there are screens with different character representations. | | 1892 | * there are screens with different character representations. |
1895 | * | | 1893 | * |
1896 | * txt_dent -- | | 1894 | * txt_dent -- |
1897 | * Handle ^T indents, ^D outdents. | | 1895 | * Handle ^T indents, ^D outdents. |
1898 | * | | 1896 | * |
1899 | * If anything changes here, check the ex version to see if it needs similar | | 1897 | * If anything changes here, check the ex version to see if it needs similar |
1900 | * changes. | | 1898 | * changes. |
1901 | */ | | 1899 | */ |
1902 | static int | | 1900 | static int |
1903 | txt_dent(SCR *sp, TEXT *tp, int isindent) | | 1901 | txt_dent(SCR *sp, TEXT *tp, int isindent) |
1904 | { | | 1902 | { |
1905 | ARG_CHAR_T ch; | | 1903 | ARG_CHAR_T ch; |
1906 | u_long sw, ts; | | 1904 | u_long sw, ts; |
1907 | size_t cno, current, spaces, target, tabs; | | 1905 | size_t cno, current, spaces, target, tabs; |
1908 | | | 1906 | |
1909 | ts = O_VAL(sp, O_TABSTOP); | | 1907 | ts = O_VAL(sp, O_TABSTOP); |
1910 | sw = O_VAL(sp, O_SHIFTWIDTH); | | 1908 | sw = O_VAL(sp, O_SHIFTWIDTH); |
1911 | | | 1909 | |
1912 | /* | | 1910 | /* |
1913 | * Since we don't know what precedes the character(s) being inserted | | 1911 | * Since we don't know what precedes the character(s) being inserted |
1914 | * (or deleted), the preceding whitespace characters must be resolved. | | 1912 | * (or deleted), the preceding whitespace characters must be resolved. |
1915 | * An example is a <tab>, which doesn't need a full shiftwidth number | | 1913 | * An example is a <tab>, which doesn't need a full shiftwidth number |
1916 | * of columns because it's preceded by <space>s. This is easy to get | | 1914 | * of columns because it's preceded by <space>s. This is easy to get |
1917 | * if the user sets shiftwidth to a value less than tabstop (or worse, | | 1915 | * if the user sets shiftwidth to a value less than tabstop (or worse, |
1918 | * something for which tabstop isn't a multiple) and then uses ^T to | | 1916 | * something for which tabstop isn't a multiple) and then uses ^T to |
1919 | * indent, and ^D to outdent. | | 1917 | * indent, and ^D to outdent. |
1920 | * | | 1918 | * |
1921 | * Figure out the current and target screen columns. In the historic | | 1919 | * Figure out the current and target screen columns. In the historic |
1922 | * vi, the autoindent column was NOT determined using display widths | | 1920 | * vi, the autoindent column was NOT determined using display widths |
1923 | * of characters as was the wrapmargin column. For that reason, we | | 1921 | * of characters as was the wrapmargin column. For that reason, we |
1924 | * can't use the vs_column() function, but have to calculate it here. | | 1922 | * can't use the vs_column() function, but have to calculate it here. |
1925 | * This is slow, but it's normally only on the first few characters of | | 1923 | * This is slow, but it's normally only on the first few characters of |
1926 | * a line. | | 1924 | * a line. |
1927 | */ | | 1925 | */ |
1928 | for (current = cno = 0; cno < tp->cno; ++cno) | | 1926 | for (current = cno = 0; cno < tp->cno; ++cno) |
1929 | current += tp->lb[cno] == '\t' ? | | 1927 | current += tp->lb[cno] == '\t' ? |
1930 | COL_OFF(current, ts) : KEY_COL(sp, tp->lb[cno]); | | 1928 | COL_OFF(current, ts) : KEY_COL(sp, tp->lb[cno]); |
1931 | | | 1929 | |
1932 | target = current; | | 1930 | target = current; |
1933 | if (isindent) | | 1931 | if (isindent) |
1934 | target += COL_OFF(target, sw); | | 1932 | target += COL_OFF(target, sw); |
1935 | else { | | 1933 | else { |
1936 | --target; | | 1934 | --target; |
1937 | target -= target % sw; | | 1935 | target -= target % sw; |
1938 | } | | 1936 | } |
1939 | | | 1937 | |
1940 | /* | | 1938 | /* |
1941 | * Back up over any previous <blank> characters, changing them into | | 1939 | * Back up over any previous <blank> characters, changing them into |
1942 | * overwrite characters (including any ai characters). Then figure | | 1940 | * overwrite characters (including any ai characters). Then figure |
1943 | * out the current screen column. | | 1941 | * out the current screen column. |
1944 | */ | | 1942 | */ |
1945 | for (; tp->cno > tp->offset && | | 1943 | for (; tp->cno > tp->offset && |
1946 | (tp->lb[tp->cno - 1] == ' ' || tp->lb[tp->cno - 1] == '\t'); | | 1944 | (tp->lb[tp->cno - 1] == ' ' || tp->lb[tp->cno - 1] == '\t'); |
1947 | --tp->cno, ++tp->owrite); | | 1945 | --tp->cno, ++tp->owrite); |
1948 | for (current = cno = 0; cno < tp->cno; ++cno) | | 1946 | for (current = cno = 0; cno < tp->cno; ++cno) |
1949 | current += tp->lb[cno] == '\t' ? | | 1947 | current += tp->lb[cno] == '\t' ? |
1950 | COL_OFF(current, ts) : KEY_COL(sp, tp->lb[cno]); | | 1948 | COL_OFF(current, ts) : KEY_COL(sp, tp->lb[cno]); |
1951 | | | 1949 | |
1952 | /* | | 1950 | /* |
1953 | * If we didn't move up to or past the target, it's because there | | 1951 | * If we didn't move up to or past the target, it's because there |
1954 | * weren't enough characters to delete, e.g. the first character | | 1952 | * weren't enough characters to delete, e.g. the first character |
1955 | * of the line was a tp->offset character, and the user entered | | 1953 | * of the line was a tp->offset character, and the user entered |
1956 | * ^D to move to the beginning of a line. An example of this is: | | 1954 | * ^D to move to the beginning of a line. An example of this is: |
1957 | * | | 1955 | * |
1958 | * :set ai sw=4<cr>i<space>a<esc>i^T^D | | 1956 | * :set ai sw=4<cr>i<space>a<esc>i^T^D |
1959 | * | | 1957 | * |
1960 | * Otherwise, count up the total spaces/tabs needed to get from the | | 1958 | * Otherwise, count up the total spaces/tabs needed to get from the |
1961 | * beginning of the line (or the last non-<blank> character) to the | | 1959 | * beginning of the line (or the last non-<blank> character) to the |
1962 | * target. | | 1960 | * target. |
1963 | */ | | 1961 | */ |
1964 | if (current >= target) | | 1962 | if (current >= target) |
1965 | spaces = tabs = 0; | | 1963 | spaces = tabs = 0; |
1966 | else { | | 1964 | else { |
1967 | cno = current; | | 1965 | cno = current; |
1968 | tabs = 0; | | 1966 | tabs = 0; |
1969 | if (!O_ISSET(sp, O_EXPANDTAB)) { | | 1967 | if (!O_ISSET(sp, O_EXPANDTAB)) { |
1970 | for (; cno + COL_OFF(cno, ts) <= target; ++tabs) | | 1968 | for (; cno + COL_OFF(cno, ts) <= target; ++tabs) |
1971 | cno += COL_OFF(cno, ts); | | 1969 | cno += COL_OFF(cno, ts); |
1972 | } | | 1970 | } |
1973 | spaces = target - cno; | | 1971 | spaces = target - cno; |
1974 | } | | 1972 | } |
1975 | | | 1973 | |
1976 | tp->ai = tabs + spaces; | | 1974 | tp->ai = tabs + spaces; |
1977 | | | 1975 | |
1978 | /* | | 1976 | /* |
1979 | * Call txt_insch() to insert each character, so that we get the | | 1977 | * Call txt_insch() to insert each character, so that we get the |
1980 | * correct effect when we add a <tab> to replace N <spaces>. | | 1978 | * correct effect when we add a <tab> to replace N <spaces>. |
1981 | */ | | 1979 | */ |
1982 | for (ch = '\t'; tabs > 0; --tabs) | | 1980 | for (ch = '\t'; tabs > 0; --tabs) |
1983 | (void)txt_insch(sp, tp, &ch, 0); | | 1981 | (void)txt_insch(sp, tp, &ch, 0); |
1984 | for (ch = ' '; spaces > 0; --spaces) | | 1982 | for (ch = ' '; spaces > 0; --spaces) |
1985 | (void)txt_insch(sp, tp, &ch, 0); | | 1983 | (void)txt_insch(sp, tp, &ch, 0); |
1986 | return (0); | | 1984 | return (0); |
1987 | } | | 1985 | } |
1988 | | | 1986 | |
1989 | /* | | 1987 | /* |
1990 | * txt_fc -- | | 1988 | * txt_fc -- |
1991 | * File name completion. | | 1989 | * File name completion. |
1992 | */ | | 1990 | */ |
1993 | static int | | 1991 | static int |
1994 | txt_fc(SCR *sp, TEXT *tp, int *redrawp) | | 1992 | txt_fc(SCR *sp, TEXT *tp, int *redrawp) |
1995 | { | | 1993 | { |
1996 | struct stat sb; | | 1994 | struct stat sb; |
1997 | ARGS **argv; | | 1995 | ARGS **argv; |
1998 | CHAR_T s_ch; | | 1996 | CHAR_T s_ch; |
1999 | EXCMD cmd; | | 1997 | EXCMD cmd; |
2000 | size_t indx, len, nlen, off; | | 1998 | size_t indx, len, nlen, off; |
2001 | int argc, trydir; | | 1999 | int argc, trydir; |
2002 | CHAR_T *p, *t; | | 2000 | CHAR_T *p, *t; |
2003 | const char *np; | | 2001 | const char *np; |
2004 | size_t nplen; | | 2002 | size_t nplen; |
2005 | | | 2003 | |
2006 | trydir = 0; | | 2004 | trydir = 0; |
2007 | *redrawp = 0; | | 2005 | *redrawp = 0; |
2008 | | | 2006 | |
2009 | /* | | 2007 | /* |
2010 | * Find the beginning of this "word" -- if we're at the beginning | | 2008 | * Find the beginning of this "word" -- if we're at the beginning |
2011 | * of the line, it's a special case. | | 2009 | * of the line, it's a special case. |
2012 | */ | | 2010 | */ |
2013 | if (tp->cno == 1) { | | 2011 | if (tp->cno == 1) { |
2014 | len = 0; | | 2012 | len = 0; |
2015 | p = tp->lb; | | 2013 | p = tp->lb; |
2016 | } else | | 2014 | } else |
2017 | retry: for (len = 0, | | 2015 | retry: for (len = 0, |
2018 | off = tp->cno - 1, p = tp->lb + off;; --off, --p) { | | 2016 | off = tp->cno - 1, p = tp->lb + off;; --off, --p) { |
2019 | if (ISBLANK((UCHAR_T)*p)) { | | 2017 | if (ISBLANK((UCHAR_T)*p)) { |
2020 | ++p; | | 2018 | ++p; |
2021 | break; | | 2019 | break; |
2022 | } | | 2020 | } |
2023 | ++len; | | 2021 | ++len; |
2024 | if (off == tp->ai || off == tp->offset) | | 2022 | if (off == tp->ai || off == tp->offset) |
2025 | break; | | 2023 | break; |
2026 | } | | 2024 | } |
2027 | | | 2025 | |
2028 | /* | | 2026 | /* |
2029 | * Get enough space for a wildcard character. | | 2027 | * Get enough space for a wildcard character. |
2030 | * | | 2028 | * |
2031 | * XXX | | 2029 | * XXX |
2032 | * This won't work for "foo\", since the \ will escape the expansion | | 2030 | * This won't work for "foo\", since the \ will escape the expansion |
2033 | * character. I'm not sure if that's a bug or not... | | 2031 | * character. I'm not sure if that's a bug or not... |
2034 | */ | | 2032 | */ |
2035 | off = p - tp->lb; | | 2033 | off = p - tp->lb; |
2036 | BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1); | | 2034 | BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1); |
2037 | p = tp->lb + off; | | 2035 | p = tp->lb + off; |
2038 | | | 2036 | |
2039 | s_ch = p[len]; | | 2037 | s_ch = p[len]; |
2040 | p[len] = '*'; | | 2038 | p[len] = '*'; |
2041 | | | 2039 | |
2042 | /* Build an ex command, and call the ex expansion routines. */ | | 2040 | /* Build an ex command, and call the ex expansion routines. */ |
2043 | ex_cinit(sp, &cmd, 0, 0, OOBLNO, OOBLNO, 0); | | 2041 | ex_cinit(sp, &cmd, 0, 0, OOBLNO, OOBLNO, 0); |
2044 | if (argv_exp2(sp, &cmd, p, len + 1)) { | | 2042 | if (argv_exp2(sp, &cmd, p, len + 1)) { |
2045 | p[len] = s_ch; | | 2043 | p[len] = s_ch; |
2046 | return (0); | | 2044 | return (0); |
2047 | } | | 2045 | } |
2048 | argc = cmd.argc; | | 2046 | argc = cmd.argc; |
2049 | argv = cmd.argv; | | 2047 | argv = cmd.argv; |
2050 | | | 2048 | |
2051 | p[len] = s_ch; | | 2049 | p[len] = s_ch; |
2052 | | | 2050 | |
2053 | switch (argc) { | | 2051 | switch (argc) { |
2054 | case 0: /* No matches. */ | | 2052 | case 0: /* No matches. */ |
2055 | if (!trydir) | | 2053 | if (!trydir) |
2056 | (void)sp->gp->scr_bell(sp); | | 2054 | (void)sp->gp->scr_bell(sp); |
2057 | return (0); | | 2055 | return (0); |
2058 | case 1: /* One match. */ | | 2056 | case 1: /* One match. */ |
2059 | /* If something changed, do the exchange. */ | | 2057 | /* If something changed, do the exchange. */ |
2060 | nlen = STRLEN(cmd.argv[0]->bp); | | 2058 | nlen = STRLEN(cmd.argv[0]->bp); |
2061 | if (len != nlen || MEMCMP(cmd.argv[0]->bp, p, len)) | | 2059 | if (len != nlen || MEMCMP(cmd.argv[0]->bp, p, len)) |
2062 | break; | | 2060 | break; |
2063 | | | 2061 | |
2064 | /* If haven't done a directory test, do it now. */ | | 2062 | /* If haven't done a directory test, do it now. */ |
2065 | INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, | | 2063 | INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, |
2066 | np, nplen); | | 2064 | np, nplen); |
2067 | if (!trydir && | | 2065 | if (!trydir && |
2068 | !stat(np, &sb) && S_ISDIR(sb.st_mode)) { | | 2066 | !stat(np, &sb) && S_ISDIR(sb.st_mode)) { |
2069 | p += len; | | 2067 | p += len; |
2070 | goto isdir; | | 2068 | goto isdir; |
2071 | } | | 2069 | } |
2072 | | | 2070 | |
2073 | /* If nothing changed, period, ring the bell. */ | | 2071 | /* If nothing changed, period, ring the bell. */ |
2074 | if (!trydir) | | 2072 | if (!trydir) |
2075 | (void)sp->gp->scr_bell(sp); | | 2073 | (void)sp->gp->scr_bell(sp); |
2076 | return (0); | | 2074 | return (0); |
2077 | default: /* Multiple matches. */ | | 2075 | default: /* Multiple matches. */ |
2078 | *redrawp = 1; | | 2076 | *redrawp = 1; |
2079 | if (txt_fc_col(sp, argc, argv)) | | 2077 | if (txt_fc_col(sp, argc, argv)) |
2080 | return (1); | | 2078 | return (1); |
2081 | | | 2079 | |
2082 | /* Find the length of the shortest match. */ | | 2080 | /* Find the length of the shortest match. */ |
2083 | for (nlen = cmd.argv[0]->len; --argc > 0;) { | | 2081 | for (nlen = cmd.argv[0]->len; --argc > 0;) { |
2084 | if (cmd.argv[argc]->len < nlen) | | 2082 | if (cmd.argv[argc]->len < nlen) |
2085 | nlen = cmd.argv[argc]->len; | | 2083 | nlen = cmd.argv[argc]->len; |
2086 | for (indx = 0; indx < nlen && | | 2084 | for (indx = 0; indx < nlen && |
2087 | cmd.argv[argc]->bp[indx] == cmd.argv[0]->bp[indx]; | | 2085 | cmd.argv[argc]->bp[indx] == cmd.argv[0]->bp[indx]; |
2088 | ++indx); | | 2086 | ++indx); |
2089 | nlen = indx; | | 2087 | nlen = indx; |
2090 | } | | 2088 | } |
2091 | break; | | 2089 | break; |
2092 | } | | 2090 | } |
2093 | | | 2091 | |
2094 | /* Overwrite the expanded text first. */ | | 2092 | /* Overwrite the expanded text first. */ |
2095 | for (t = cmd.argv[0]->bp; len > 0 && nlen > 0; --len, --nlen) | | 2093 | for (t = cmd.argv[0]->bp; len > 0 && nlen > 0; --len, --nlen) |
2096 | *p++ = *t++; | | 2094 | *p++ = *t++; |
2097 | | | 2095 | |
2098 | /* If lost text, make the remaining old text overwrite characters. */ | | 2096 | /* If lost text, make the remaining old text overwrite characters. */ |
2099 | if (len) { | | 2097 | if (len) { |
2100 | tp->cno -= len; | | 2098 | tp->cno -= len; |
2101 | tp->owrite += len; | | 2099 | tp->owrite += len; |
2102 | } | | 2100 | } |
2103 | | | 2101 | |
2104 | /* Overwrite any overwrite characters next. */ | | 2102 | /* Overwrite any overwrite characters next. */ |
2105 | for (; nlen > 0 && tp->owrite > 0; --nlen, --tp->owrite, ++tp->cno) | | 2103 | for (; nlen > 0 && tp->owrite > 0; --nlen, --tp->owrite, ++tp->cno) |
2106 | *p++ = *t++; | | 2104 | *p++ = *t++; |
2107 | | | 2105 | |
2108 | /* Shift remaining text up, and move the cursor to the end. */ | | 2106 | /* Shift remaining text up, and move the cursor to the end. */ |
2109 | if (nlen) { | | 2107 | if (nlen) { |
2110 | off = p - tp->lb; | | 2108 | off = p - tp->lb; |
2111 | BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + nlen); | | 2109 | BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + nlen); |
2112 | p = tp->lb + off; | | 2110 | p = tp->lb + off; |
2113 | | | 2111 | |
2114 | tp->cno += nlen; | | 2112 | tp->cno += nlen; |
2115 | tp->len += nlen; | | 2113 | tp->len += nlen; |
2116 | | | 2114 | |
2117 | if (tp->insert != 0) | | 2115 | if (tp->insert != 0) |
2118 | (void)MEMMOVEW(p + nlen, p, tp->insert); | | 2116 | (void)MEMMOVEW(p + nlen, p, tp->insert); |
2119 | while (nlen--) | | 2117 | while (nlen--) |
2120 | *p++ = *t++; | | 2118 | *p++ = *t++; |
2121 | } | | 2119 | } |
2122 | | | 2120 | |
2123 | /* If a single match and it's a directory, retry it. */ | | 2121 | /* If a single match and it's a directory, retry it. */ |
2124 | INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, np, nplen); | | 2122 | INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, np, nplen); |
2125 | if (argc == 1 && !stat(np, &sb) && S_ISDIR(sb.st_mode)) { | | 2123 | if (argc == 1 && !stat(np, &sb) && S_ISDIR(sb.st_mode)) { |
2126 | isdir: if (tp->owrite == 0) { | | 2124 | isdir: if (tp->owrite == 0) { |
2127 | off = p - tp->lb; | | 2125 | off = p - tp->lb; |
2128 | BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1); | | 2126 | BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1); |
2129 | p = tp->lb + off; | | 2127 | p = tp->lb + off; |
2130 | if (tp->insert != 0) | | 2128 | if (tp->insert != 0) |
2131 | (void)MEMMOVEW(p + 1, p, tp->insert); | | 2129 | (void)MEMMOVEW(p + 1, p, tp->insert); |
2132 | ++tp->len; | | 2130 | ++tp->len; |
2133 | } else | | 2131 | } else |
2134 | --tp->owrite; | | 2132 | --tp->owrite; |
2135 | | | 2133 | |
2136 | ++tp->cno; | | 2134 | ++tp->cno; |
2137 | *p++ = '/'; | | 2135 | *p++ = '/'; |
2138 | | | 2136 | |
2139 | trydir = 1; | | 2137 | trydir = 1; |
2140 | goto retry; | | 2138 | goto retry; |
2141 | } | | 2139 | } |
2142 | return (0); | | 2140 | return (0); |
2143 | } | | 2141 | } |
2144 | | | 2142 | |
2145 | /* | | 2143 | /* |