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