| 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 | } |