File: | src/usr.bin/vi/build/../ex/ex_txt.c |
Warning: | line 390, column 2 Value stored to 'cno' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: ex_txt.c,v 1.17 2020/04/30 10:40:21 millert Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1992, 1993, 1994 |
5 | * The Regents of the University of California. All rights reserved. |
6 | * Copyright (c) 1992, 1993, 1994, 1995, 1996 |
7 | * Keith Bostic. All rights reserved. |
8 | * |
9 | * See the LICENSE file for redistribution information. |
10 | */ |
11 | |
12 | #include "config.h" |
13 | |
14 | #include <sys/types.h> |
15 | #include <sys/queue.h> |
16 | |
17 | #include <bitstring.h> |
18 | #include <ctype.h> |
19 | #include <limits.h> |
20 | #include <stdio.h> |
21 | #include <stdlib.h> |
22 | #include <string.h> |
23 | |
24 | #include "../common/common.h" |
25 | #include "../vi/vi.h" |
26 | |
27 | /* |
28 | * !!! |
29 | * The backslash characters was special when it preceded a newline as part of |
30 | * a substitution replacement pattern. For example, the input ":a\<cr>" would |
31 | * failed immediately with an error, as the <cr> wasn't part of a substitution |
32 | * replacement pattern. This implies a frightening integration of the editor |
33 | * and the parser and/or the RE engine. There's no way I'm going to reproduce |
34 | * those semantics. |
35 | * |
36 | * So, if backslashes are special, this code inserts the backslash and the next |
37 | * character into the string, without regard for the character or the command |
38 | * being entered. Since "\<cr>" was illegal historically (except for the one |
39 | * special case), and the command will fail eventually, no historical scripts |
40 | * should break (presuming they didn't depend on the failure mode itself or the |
41 | * characters remaining when failure occurred. |
42 | */ |
43 | |
44 | static int txt_dent(SCR *, TEXT *); |
45 | static void txt_prompt(SCR *, TEXT *, CHAR_T, u_int32_t); |
46 | |
47 | /* |
48 | * ex_txt -- |
49 | * Get lines from the terminal for ex. |
50 | * |
51 | * PUBLIC: int ex_txt(SCR *, TEXTH *, CHAR_T, u_int32_t); |
52 | */ |
53 | int |
54 | ex_txt(SCR *sp, TEXTH *tiqh, CHAR_T prompt, u_int32_t flags) |
55 | { |
56 | EVENT ev; |
57 | GS *gp; |
58 | TEXT ait, *ntp, *tp; |
59 | carat_t carat_st; |
60 | size_t cnt; |
61 | int rval; |
62 | |
63 | rval = 0; |
64 | |
65 | /* |
66 | * Get a TEXT structure with some initial buffer space, reusing the |
67 | * last one if it's big enough. (All TEXT bookkeeping fields default |
68 | * to 0 -- text_init() handles this.) |
69 | */ |
70 | if (!TAILQ_EMPTY(tiqh)(((tiqh)->tqh_first) == ((void *)0))) { |
71 | tp = TAILQ_FIRST(tiqh)((tiqh)->tqh_first); |
72 | if (TAILQ_NEXT(tp, q)((tp)->q.tqe_next) || tp->lb_len < 32) { |
73 | text_lfree(tiqh); |
74 | goto newtp; |
75 | } |
76 | tp->len = 0; |
77 | } else { |
78 | newtp: if ((tp = text_init(sp, NULL((void *)0), 0, 32)) == NULL((void *)0)) |
79 | goto err; |
80 | TAILQ_INSERT_HEAD(tiqh, tp, q)do { if (((tp)->q.tqe_next = (tiqh)->tqh_first) != ((void *)0)) (tiqh)->tqh_first->q.tqe_prev = &(tp)->q. tqe_next; else (tiqh)->tqh_last = &(tp)->q.tqe_next ; (tiqh)->tqh_first = (tp); (tp)->q.tqe_prev = &(tiqh )->tqh_first; } while (0); |
81 | } |
82 | |
83 | /* Set the starting line number. */ |
84 | tp->lno = sp->lno + 1; |
85 | |
86 | /* |
87 | * If it's a terminal, set up autoindent, put out the prompt, and |
88 | * set it up so we know we were suspended. Otherwise, turn off |
89 | * the autoindent flag, as that requires less special casing below. |
90 | * |
91 | * XXX |
92 | * Historic practice is that ^Z suspended command mode (but, because |
93 | * it ran in cooked mode, it was unaffected by the autowrite option.) |
94 | * On restart, any "current" input was discarded, whether in insert |
95 | * mode or not, and ex was in command mode. This code matches historic |
96 | * practice, but not 'cause it's easier. |
97 | */ |
98 | gp = sp->gp; |
99 | if (F_ISSET(gp, G_SCRIPTED)(((gp)->flags) & ((0x0010)))) |
100 | LF_CLR(TXT_AUTOINDENT)((flags) &= ~((0x00000010))); |
101 | else { |
102 | if (LF_ISSET(TXT_AUTOINDENT)((flags) & ((0x00000010)))) { |
103 | LF_SET(TXT_EOFCHAR)((flags) |= ((0x00004000))); |
104 | if (v_txt_auto(sp, sp->lno, NULL((void *)0), 0, tp)) |
105 | goto err; |
106 | } |
107 | txt_prompt(sp, tp, prompt, flags); |
108 | } |
109 | |
110 | for (carat_st = C_NOTSET;;) { |
111 | if (v_event_get(sp, &ev, 0, 0)) |
112 | goto err; |
113 | |
114 | /* Deal with all non-character events. */ |
115 | switch (ev.e_event) { |
116 | case E_CHARACTER: |
117 | break; |
118 | case E_ERR: |
119 | goto err; |
120 | case E_REPAINT: |
121 | case E_WRESIZE: |
122 | continue; |
123 | case E_EOF: |
124 | rval = 1; |
125 | /* FALLTHROUGH */ |
126 | case E_INTERRUPT: |
127 | /* |
128 | * Handle EOF/SIGINT events by discarding partially |
129 | * entered text and returning. EOF returns failure, |
130 | * E_INTERRUPT returns success. |
131 | */ |
132 | goto notlast; |
133 | default: |
134 | v_event_err(sp, &ev); |
135 | goto notlast; |
136 | } |
137 | |
138 | /* |
139 | * Deal with character events. |
140 | * |
141 | * Check to see if the character fits into the input buffer. |
142 | * (Use tp->len, ignore overwrite and non-printable chars.) |
143 | */ |
144 | BINC_GOTO(sp, tp->lb, tp->lb_len, tp->len + 1){ void *L__bincp; if ((tp->len + 1) > (tp->lb_len)) { if ((L__bincp = binc((sp), (tp->lb), &(tp->lb_len) , (tp->len + 1))) == ((void *)0)) goto alloc_err; (tp-> lb) = L__bincp; } }; |
145 | |
146 | switch (ev.e_value_u_event._e_ch.value) { |
147 | case K_CR: |
148 | /* |
149 | * !!! |
150 | * Historically, <carriage-return>'s in the command |
151 | * weren't special, so the ex parser would return an |
152 | * unknown command error message. However, if they |
153 | * terminated the command if they were in a map. I'm |
154 | * pretty sure this still isn't right, but it handles |
155 | * what I've seen so far. |
156 | */ |
157 | if (!F_ISSET(&ev.e_ch, CH_MAPPED)(((&ev._u_event._e_ch)->flags) & ((0x02)))) |
158 | goto ins_ch; |
159 | /* FALLTHROUGH */ |
160 | case K_NL: |
161 | /* |
162 | * '\' can escape <carriage-return>/<newline>. We |
163 | * don't discard the backslash because we need it |
164 | * to get the <newline> through the ex parser. |
165 | */ |
166 | if (LF_ISSET(TXT_BACKSLASH)((flags) & ((0x00000020))) && |
167 | tp->len != 0 && tp->lb[tp->len - 1] == '\\') |
168 | goto ins_ch; |
169 | |
170 | /* |
171 | * CR returns from the ex command line. |
172 | * |
173 | * XXX |
174 | * Terminate with a nul, needed by filter. |
175 | */ |
176 | if (LF_ISSET(TXT_CR)((flags) & ((0x00000800)))) { |
177 | tp->lb[tp->len] = '\0'; |
178 | goto done; |
179 | } |
180 | |
181 | /* |
182 | * '.' may terminate text input mode; free the current |
183 | * TEXT. |
184 | */ |
185 | if (LF_ISSET(TXT_DOTTERM)((flags) & ((0x00001000))) && tp->len == tp->ai + 1 && |
186 | tp->lb[tp->len - 1] == '.') { |
187 | notlast: TAILQ_REMOVE(tiqh, tp, q)do { if (((tp)->q.tqe_next) != ((void *)0)) (tp)->q.tqe_next ->q.tqe_prev = (tp)->q.tqe_prev; else (tiqh)->tqh_last = (tp)->q.tqe_prev; *(tp)->q.tqe_prev = (tp)->q.tqe_next ; ; ; } while (0); |
188 | text_free(tp); |
189 | goto done; |
190 | } |
191 | |
192 | /* Set up bookkeeping for the new line. */ |
193 | if ((ntp = text_init(sp, NULL((void *)0), 0, 32)) == NULL((void *)0)) |
194 | goto err; |
195 | ntp->lno = tp->lno + 1; |
196 | |
197 | /* |
198 | * Reset the autoindent line value. 0^D keeps the ai |
199 | * line from changing, ^D changes the level, even if |
200 | * there were no characters in the old line. Note, if |
201 | * using the current tp structure, use the cursor as |
202 | * the length, the autoindent characters may have been |
203 | * erased. |
204 | */ |
205 | if (LF_ISSET(TXT_AUTOINDENT)((flags) & ((0x00000010)))) { |
206 | if (carat_st == C_NOCHANGE) { |
207 | if (v_txt_auto(sp, |
208 | OOBLNO0, &ait, ait.ai, ntp)) |
209 | goto err; |
210 | free(ait.lb); |
211 | } else |
212 | if (v_txt_auto(sp, |
213 | OOBLNO0, tp, tp->len, ntp)) |
214 | goto err; |
215 | carat_st = C_NOTSET; |
216 | } |
217 | txt_prompt(sp, ntp, prompt, flags); |
218 | |
219 | /* |
220 | * Swap old and new TEXT's, and insert the new TEXT |
221 | * into the queue. |
222 | */ |
223 | tp = ntp; |
224 | TAILQ_INSERT_TAIL(tiqh, tp, q)do { (tp)->q.tqe_next = ((void *)0); (tp)->q.tqe_prev = (tiqh)->tqh_last; *(tiqh)->tqh_last = (tp); (tiqh)-> tqh_last = &(tp)->q.tqe_next; } while (0); |
225 | break; |
226 | case K_CARAT: /* Delete autoindent chars. */ |
227 | if (tp->len <= tp->ai && LF_ISSET(TXT_AUTOINDENT)((flags) & ((0x00000010)))) |
228 | carat_st = C_CARATSET; |
229 | goto ins_ch; |
230 | case K_ZERO: /* Delete autoindent chars. */ |
231 | if (tp->len <= tp->ai && LF_ISSET(TXT_AUTOINDENT)((flags) & ((0x00000010)))) |
232 | carat_st = C_ZEROSET; |
233 | goto ins_ch; |
234 | case K_CNTRLD: /* Delete autoindent char. */ |
235 | /* |
236 | * !!! |
237 | * Historically, the ^D command took (but then ignored) |
238 | * a count. For simplicity, we don't return it unless |
239 | * it's the first character entered. The check for len |
240 | * equal to 0 is okay, TXT_AUTOINDENT won't be set. |
241 | */ |
242 | if (LF_ISSET(TXT_CNTRLD)((flags) & ((0x00000200)))) { |
243 | for (cnt = 0; cnt < tp->len; ++cnt) |
244 | if (!isblank(tp->lb[cnt])) |
245 | break; |
246 | if (cnt == tp->len) { |
247 | tp->len = 1; |
248 | tp->lb[0] = ev.e_c_u_event._e_ch.c; |
249 | tp->lb[1] = '\0'; |
250 | |
251 | /* |
252 | * Put out a line separator, in case |
253 | * the command fails. |
254 | */ |
255 | (void)putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n', (&__sF[1]))); |
256 | goto done; |
257 | } |
258 | } |
259 | |
260 | /* |
261 | * POSIX 1003.1b-1993, paragraph 7.1.1.9, states that |
262 | * the EOF characters are discarded if there are other |
263 | * characters to process in the line, i.e. if the EOF |
264 | * is not the first character in the line. For this |
265 | * reason, historic ex discarded the EOF characters, |
266 | * even if occurring in the middle of the input line. |
267 | * We match that historic practice. |
268 | * |
269 | * !!! |
270 | * The test for discarding in the middle of the line is |
271 | * done in the switch, because the CARAT forms are N+1, |
272 | * not N. |
273 | * |
274 | * !!! |
275 | * There's considerable magic to make the terminal code |
276 | * return the EOF character at all. See that code for |
277 | * details. |
278 | */ |
279 | if (!LF_ISSET(TXT_AUTOINDENT)((flags) & ((0x00000010))) || tp->len == 0) |
280 | continue; |
281 | switch (carat_st) { |
282 | case C_CARATSET: /* ^^D */ |
283 | if (tp->len > tp->ai + 1) |
284 | continue; |
285 | |
286 | /* Save the ai string for later. */ |
287 | ait.lb = NULL((void *)0); |
288 | ait.lb_len = 0; |
289 | BINC_GOTO(sp, ait.lb, ait.lb_len, tp->ai){ void *L__bincp; if ((tp->ai) > (ait.lb_len)) { if ((L__bincp = binc((sp), (ait.lb), &(ait.lb_len), (tp->ai))) == ( (void *)0)) goto alloc_err; (ait.lb) = L__bincp; } }; |
290 | memcpy(ait.lb, tp->lb, tp->ai); |
291 | ait.ai = ait.len = tp->ai; |
292 | |
293 | carat_st = C_NOCHANGE; |
294 | goto leftmargin; |
295 | case C_ZEROSET: /* 0^D */ |
296 | if (tp->len > tp->ai + 1) |
297 | continue; |
298 | |
299 | carat_st = C_NOTSET; |
300 | leftmargin: (void)gp->scr_ex_adjust(sp, EX_TERM_CE); |
301 | tp->ai = tp->len = 0; |
302 | break; |
303 | case C_NOTSET: /* ^D */ |
304 | if (tp->len > tp->ai) |
305 | continue; |
306 | |
307 | if (txt_dent(sp, tp)) |
308 | goto err; |
309 | break; |
310 | default: |
311 | abort(); |
312 | } |
313 | |
314 | /* Clear and redisplay the line. */ |
315 | (void)gp->scr_ex_adjust(sp, EX_TERM_CE); |
316 | txt_prompt(sp, tp, prompt, flags); |
317 | break; |
318 | default: |
319 | /* |
320 | * See the TXT_BEAUTIFY comment in vi/v_txt_ev.c. |
321 | * |
322 | * Silently eliminate any iscntrl() character that was |
323 | * not already handled specially, except for <tab> and |
324 | * <ff>. |
325 | */ |
326 | ins_ch: if (LF_ISSET(TXT_BEAUTIFY)((flags) & ((0x00000040))) && iscntrl(ev.e_c_u_event._e_ch.c) && |
327 | ev.e_value_u_event._e_ch.value != K_FORMFEED && ev.e_value_u_event._e_ch.value != K_TAB) |
328 | break; |
329 | |
330 | tp->lb[tp->len++] = ev.e_c_u_event._e_ch.c; |
331 | break; |
332 | } |
333 | } |
334 | /* NOTREACHED */ |
335 | |
336 | done: return (rval); |
337 | |
338 | err: |
339 | alloc_err: |
340 | return (1); |
341 | } |
342 | |
343 | /* |
344 | * txt_prompt -- |
345 | * Display the ex prompt, line number, ai characters. Characters had |
346 | * better be printable by the terminal driver, but that's its problem, |
347 | * not ours. |
348 | */ |
349 | static void |
350 | txt_prompt(SCR *sp, TEXT *tp, CHAR_T prompt, u_int32_t flags) |
351 | { |
352 | /* Display the prompt. */ |
353 | if (LF_ISSET(TXT_PROMPT)((flags) & ((0x00400000)))) |
354 | (void)printf("%c", prompt); |
355 | |
356 | /* Display the line number. */ |
357 | if (LF_ISSET(TXT_NUMBER)((flags) & ((0x00100000))) && O_ISSET(sp, O_NUMBER)((((&(((sp)))->opts[(((O_NUMBER)))])->flags) & ( (0x01))) ? (((sp)))->gp->opts[(((sp)))->opts[(((O_NUMBER )))].o_cur.val].o_cur.val : (((sp)))->opts[(((O_NUMBER)))] .o_cur.val)) |
358 | (void)printf("%6lu ", (u_long)tp->lno); |
359 | |
360 | /* Print out autoindent string. */ |
361 | if (LF_ISSET(TXT_AUTOINDENT)((flags) & ((0x00000010)))) |
362 | (void)printf("%.*s", (int)tp->ai, tp->lb); |
363 | (void)fflush(stdout(&__sF[1])); |
364 | } |
365 | |
366 | /* |
367 | * txt_dent -- |
368 | * Handle ^D outdents. |
369 | * |
370 | * Ex version of vi/v_ntext.c:txt_dent(). See that code for the (usual) |
371 | * ranting and raving. This is a fair bit simpler as ^T isn't special. |
372 | */ |
373 | static int |
374 | txt_dent(SCR *sp, TEXT *tp) |
375 | { |
376 | u_long sw, ts; |
377 | size_t cno, off, scno, spaces, tabs; |
378 | |
379 | ts = O_VAL(sp, O_TABSTOP)((((&((sp))->opts[((O_TABSTOP))])->flags) & ((0x01 ))) ? ((sp))->gp->opts[((sp))->opts[((O_TABSTOP))].o_cur .val].o_cur.val : ((sp))->opts[((O_TABSTOP))].o_cur.val); |
380 | sw = O_VAL(sp, O_SHIFTWIDTH)((((&((sp))->opts[((O_SHIFTWIDTH))])->flags) & ( (0x01))) ? ((sp))->gp->opts[((sp))->opts[((O_SHIFTWIDTH ))].o_cur.val].o_cur.val : ((sp))->opts[((O_SHIFTWIDTH))]. o_cur.val); |
381 | |
382 | /* Get the current screen column. */ |
383 | for (off = scno = 0; off < tp->len; ++off) |
384 | if (tp->lb[off] == '\t') |
385 | scno += COL_OFF(scno, ts)((ts) - ((scno) % (ts))); |
386 | else |
387 | ++scno; |
388 | |
389 | /* Get the previous shiftwidth column. */ |
390 | cno = scno--; |
Value stored to 'cno' is never read | |
391 | scno -= scno % sw; |
392 | |
393 | /* |
394 | * Since we don't know what comes before the character(s) being |
395 | * deleted, we have to resolve the autoindent characters . The |
396 | * example is a <tab>, which doesn't take up a full shiftwidth |
397 | * number of columns because it's preceded by <space>s. This is |
398 | * easy to get if the user sets shiftwidth to a value less than |
399 | * tabstop, and then uses ^T to indent, and ^D to outdent. |
400 | * |
401 | * Count up spaces/tabs needed to get to the target. |
402 | */ |
403 | cno = 0; |
404 | tabs = 0; |
405 | if (!O_ISSET(sp, O_EXPANDTAB)((((&(((sp)))->opts[(((O_EXPANDTAB)))])->flags) & ((0x01))) ? (((sp)))->gp->opts[(((sp)))->opts[(((O_EXPANDTAB )))].o_cur.val].o_cur.val : (((sp)))->opts[(((O_EXPANDTAB) ))].o_cur.val)) { |
406 | for (; cno + COL_OFF(cno, ts)((ts) - ((cno) % (ts))) <= scno; ++tabs) |
407 | cno += COL_OFF(cno, ts)((ts) - ((cno) % (ts))); |
408 | } |
409 | spaces = scno - cno; |
410 | |
411 | /* Make sure there's enough room. */ |
412 | BINC_RET(sp, tp->lb, tp->lb_len, tabs + spaces + 1){ void *L__bincp; if ((tabs + spaces + 1) > (tp->lb_len )) { if ((L__bincp = binc((sp), (tp->lb), &(tp->lb_len ), (tabs + spaces + 1))) == ((void *)0)) return (1); (tp-> lb) = L__bincp; } }; |
413 | |
414 | /* Adjust the final ai character count. */ |
415 | tp->ai = tabs + spaces; |
416 | |
417 | /* Enter the replacement characters. */ |
418 | for (tp->len = 0; tabs > 0; --tabs) |
419 | tp->lb[tp->len++] = '\t'; |
420 | for (; spaces > 0; --spaces) |
421 | tp->lb[tp->len++] = ' '; |
422 | return (0); |
423 | } |