1 1.60 christos /* $NetBSD: refresh.c,v 1.60 2024/12/05 22:21:53 christos Exp $ */ 2 1.2 lukem 3 1.1 cgd /*- 4 1.1 cgd * Copyright (c) 1992, 1993 5 1.1 cgd * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * This code is derived from software contributed to Berkeley by 8 1.1 cgd * Christos Zoulas of Cornell University. 9 1.1 cgd * 10 1.1 cgd * Redistribution and use in source and binary forms, with or without 11 1.1 cgd * modification, are permitted provided that the following conditions 12 1.1 cgd * are met: 13 1.1 cgd * 1. Redistributions of source code must retain the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer. 15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cgd * notice, this list of conditions and the following disclaimer in the 17 1.1 cgd * documentation and/or other materials provided with the distribution. 18 1.26 agc * 3. Neither the name of the University nor the names of its contributors 19 1.1 cgd * may be used to endorse or promote products derived from this software 20 1.1 cgd * without specific prior written permission. 21 1.1 cgd * 22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.1 cgd * SUCH DAMAGE. 33 1.1 cgd */ 34 1.1 cgd 35 1.18 christos #include "config.h" 36 1.1 cgd #if !defined(lint) && !defined(SCCSID) 37 1.2 lukem #if 0 38 1.1 cgd static char sccsid[] = "@(#)refresh.c 8.1 (Berkeley) 6/4/93"; 39 1.2 lukem #else 40 1.60 christos __RCSID("$NetBSD: refresh.c,v 1.60 2024/12/05 22:21:53 christos Exp $"); 41 1.2 lukem #endif 42 1.1 cgd #endif /* not lint && not SCCSID */ 43 1.1 cgd 44 1.1 cgd /* 45 1.1 cgd * refresh.c: Lower level screen refreshing functions 46 1.1 cgd */ 47 1.1 cgd #include <stdio.h> 48 1.57 ryo #include <stdlib.h> 49 1.44 christos #include <string.h> 50 1.1 cgd #include <unistd.h> 51 1.1 cgd 52 1.1 cgd #include "el.h" 53 1.1 cgd 54 1.49 christos static void re_nextline(EditLine *); 55 1.49 christos static void re_addc(EditLine *, wint_t); 56 1.49 christos static void re_update_line(EditLine *, wchar_t *, wchar_t *, int); 57 1.49 christos static void re_insert (EditLine *, wchar_t *, int, int, wchar_t *, int); 58 1.49 christos static void re_delete(EditLine *, wchar_t *, int, int, int); 59 1.49 christos static void re_fastputc(EditLine *, wint_t); 60 1.49 christos static void re_clear_eol(EditLine *, int, int, int); 61 1.49 christos static void re__strncopy(wchar_t *, wchar_t *, size_t); 62 1.49 christos static void re__copy_and_pad(wchar_t *, const wchar_t *, size_t); 63 1.1 cgd 64 1.1 cgd #ifdef DEBUG_REFRESH 65 1.49 christos static void re_printstr(EditLine *, const char *, wchar_t *, wchar_t *); 66 1.15 lukem #define __F el->el_errfile 67 1.44 christos #define ELRE_ASSERT(a, b, c) do \ 68 1.20 christos if (/*CONSTCOND*/ a) { \ 69 1.1 cgd (void) fprintf b; \ 70 1.1 cgd c; \ 71 1.1 cgd } \ 72 1.20 christos while (/*CONSTCOND*/0) 73 1.17 lukem #define ELRE_DEBUG(a, b) ELRE_ASSERT(a,b,;) 74 1.15 lukem 75 1.1 cgd /* re_printstr(): 76 1.1 cgd * Print a string on the debugging pty 77 1.1 cgd */ 78 1.49 christos static void 79 1.48 christos re_printstr(EditLine *el, const char *str, wchar_t *f, wchar_t *t) 80 1.1 cgd { 81 1.15 lukem 82 1.17 lukem ELRE_DEBUG(1, (__F, "%s:\"", str)); 83 1.15 lukem while (f < t) 84 1.17 lukem ELRE_DEBUG(1, (__F, "%c", *f++ & 0177)); 85 1.17 lukem ELRE_DEBUG(1, (__F, "\"\r\n")); 86 1.8 simonb } 87 1.1 cgd #else 88 1.17 lukem #define ELRE_ASSERT(a, b, c) 89 1.17 lukem #define ELRE_DEBUG(a, b) 90 1.1 cgd #endif 91 1.1 cgd 92 1.31 christos /* re_nextline(): 93 1.31 christos * Move to the next line or scroll 94 1.31 christos */ 95 1.49 christos static void 96 1.31 christos re_nextline(EditLine *el) 97 1.31 christos { 98 1.31 christos el->el_refresh.r_cursor.h = 0; /* reset it. */ 99 1.31 christos 100 1.31 christos /* 101 1.31 christos * If we would overflow (input is longer than terminal size), 102 1.31 christos * emulate scroll by dropping first line and shuffling the rest. 103 1.31 christos * We do this via pointer shuffling - it's safe in this case 104 1.31 christos * and we avoid memcpy(). 105 1.31 christos */ 106 1.36 christos if (el->el_refresh.r_cursor.v + 1 >= el->el_terminal.t_size.v) { 107 1.36 christos int i, lins = el->el_terminal.t_size.v; 108 1.58 christos wint_t *firstline = el->el_vdisplay[0]; 109 1.31 christos 110 1.31 christos for(i = 1; i < lins; i++) 111 1.31 christos el->el_vdisplay[i - 1] = el->el_vdisplay[i]; 112 1.31 christos 113 1.44 christos firstline[0] = '\0'; /* empty the string */ 114 1.31 christos el->el_vdisplay[i - 1] = firstline; 115 1.31 christos } else 116 1.31 christos el->el_refresh.r_cursor.v++; 117 1.31 christos 118 1.36 christos ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_terminal.t_size.v, 119 1.31 christos (__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n", 120 1.36 christos el->el_refresh.r_cursor.v, el->el_terminal.t_size.v), 121 1.31 christos abort()); 122 1.31 christos } 123 1.1 cgd 124 1.1 cgd /* re_addc(): 125 1.1 cgd * Draw c, expanding tabs, control chars etc. 126 1.1 cgd */ 127 1.49 christos static void 128 1.39 christos re_addc(EditLine *el, wint_t c) 129 1.1 cgd { 130 1.48 christos switch (ct_chr_class(c)) { 131 1.35 christos case CHTYPE_TAB: /* expand the tab */ 132 1.35 christos for (;;) { 133 1.35 christos re_putc(el, ' ', 1); 134 1.35 christos if ((el->el_refresh.r_cursor.h & 07) == 0) 135 1.35 christos break; /* go until tab stop */ 136 1.35 christos } 137 1.35 christos break; 138 1.35 christos case CHTYPE_NL: { 139 1.15 lukem int oldv = el->el_refresh.r_cursor.v; 140 1.16 jdolecek re_putc(el, '\0', 0); /* assure end of line */ 141 1.31 christos if (oldv == el->el_refresh.r_cursor.v) /* XXX */ 142 1.31 christos re_nextline(el); 143 1.35 christos break; 144 1.35 christos } 145 1.35 christos case CHTYPE_PRINT: 146 1.35 christos re_putc(el, c, 1); 147 1.35 christos break; 148 1.35 christos default: { 149 1.48 christos wchar_t visbuf[VISUAL_WIDTH_MAX]; 150 1.35 christos ssize_t i, n = 151 1.48 christos ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c); 152 1.35 christos for (i = 0; n-- > 0; ++i) 153 1.35 christos re_putc(el, visbuf[i], 1); 154 1.35 christos break; 155 1.15 lukem } 156 1.15 lukem } 157 1.15 lukem } 158 1.1 cgd 159 1.53 christos /* re_putliteral(): 160 1.52 christos * Place the literal string given 161 1.52 christos */ 162 1.52 christos libedit_private void 163 1.52 christos re_putliteral(EditLine *el, const wchar_t *begin, const wchar_t *end) 164 1.52 christos { 165 1.52 christos coord_t *cur = &el->el_refresh.r_cursor; 166 1.52 christos wint_t c; 167 1.52 christos int sizeh = el->el_terminal.t_size.h; 168 1.54 kre int i, w; 169 1.52 christos 170 1.54 kre c = literal_add(el, begin, end, &w); 171 1.60 christos if (c == 0 || w < 0) 172 1.52 christos return; 173 1.52 christos el->el_vdisplay[cur->v][cur->h] = c; 174 1.54 kre 175 1.54 kre i = w; 176 1.54 kre if (i > sizeh - cur->h) /* avoid overflow */ 177 1.54 kre i = sizeh - cur->h; 178 1.54 kre while (--i > 0) 179 1.54 kre el->el_vdisplay[cur->v][cur->h + i] = MB_FILL_CHAR; 180 1.54 kre 181 1.60 christos cur->h += w ? w : 1; 182 1.52 christos if (cur->h >= sizeh) { 183 1.52 christos /* assure end of line */ 184 1.52 christos el->el_vdisplay[cur->v][sizeh] = '\0'; 185 1.52 christos re_nextline(el); 186 1.52 christos } 187 1.52 christos } 188 1.1 cgd 189 1.1 cgd /* re_putc(): 190 1.1 cgd * Draw the character given 191 1.1 cgd */ 192 1.51 christos libedit_private void 193 1.39 christos re_putc(EditLine *el, wint_t c, int shift) 194 1.1 cgd { 195 1.52 christos coord_t *cur = &el->el_refresh.r_cursor; 196 1.46 christos int i, w = wcwidth(c); 197 1.52 christos int sizeh = el->el_terminal.t_size.h; 198 1.52 christos 199 1.39 christos ELRE_DEBUG(1, (__F, "printing %5x '%lc'\r\n", c, c)); 200 1.46 christos if (w == -1) 201 1.46 christos w = 0; 202 1.35 christos 203 1.52 christos while (shift && (cur->h + w > sizeh)) 204 1.35 christos re_putc(el, ' ', 1); 205 1.1 cgd 206 1.52 christos el->el_vdisplay[cur->v][cur->h] = c; 207 1.35 christos /* assumes !shift is only used for single-column chars */ 208 1.35 christos i = w; 209 1.35 christos while (--i > 0) 210 1.52 christos el->el_vdisplay[cur->v][cur->h + i] = MB_FILL_CHAR; 211 1.15 lukem 212 1.16 jdolecek if (!shift) 213 1.16 jdolecek return; 214 1.16 jdolecek 215 1.60 christos cur->h += w ? w : 1; /* advance to next place */ 216 1.52 christos if (cur->h >= sizeh) { 217 1.15 lukem /* assure end of line */ 218 1.52 christos el->el_vdisplay[cur->v][sizeh] = '\0'; 219 1.31 christos re_nextline(el); 220 1.31 christos } 221 1.15 lukem } 222 1.15 lukem 223 1.1 cgd 224 1.1 cgd /* re_refresh(): 225 1.1 cgd * draws the new virtual screen image from the current input 226 1.44 christos * line, then goes line-by-line changing the real image to the new 227 1.1 cgd * virtual image. The routine to re-draw a line can be replaced 228 1.1 cgd * easily in hopes of a smarter one being placed there. 229 1.1 cgd */ 230 1.51 christos libedit_private void 231 1.15 lukem re_refresh(EditLine *el) 232 1.1 cgd { 233 1.15 lukem int i, rhdiff; 234 1.48 christos wchar_t *cp, *st; 235 1.15 lukem coord_t cur; 236 1.16 jdolecek #ifdef notyet 237 1.16 jdolecek size_t termsz; 238 1.16 jdolecek #endif 239 1.15 lukem 240 1.46 christos ELRE_DEBUG(1, (__F, "el->el_line.buffer = :%ls:\r\n", 241 1.17 lukem el->el_line.buffer)); 242 1.15 lukem 243 1.52 christos literal_clear(el); 244 1.15 lukem /* reset the Drawing cursor */ 245 1.15 lukem el->el_refresh.r_cursor.h = 0; 246 1.15 lukem el->el_refresh.r_cursor.v = 0; 247 1.15 lukem 248 1.52 christos terminal_move_to_char(el, 0); 249 1.52 christos 250 1.15 lukem /* temporarily draw rprompt to calculate its size */ 251 1.15 lukem prompt_print(el, EL_RPROMPT); 252 1.15 lukem 253 1.15 lukem /* reset the Drawing cursor */ 254 1.15 lukem el->el_refresh.r_cursor.h = 0; 255 1.15 lukem el->el_refresh.r_cursor.v = 0; 256 1.15 lukem 257 1.22 christos if (el->el_line.cursor >= el->el_line.lastchar) { 258 1.22 christos if (el->el_map.current == el->el_map.alt 259 1.22 christos && el->el_line.lastchar != el->el_line.buffer) 260 1.22 christos el->el_line.cursor = el->el_line.lastchar - 1; 261 1.22 christos else 262 1.22 christos el->el_line.cursor = el->el_line.lastchar; 263 1.22 christos } 264 1.22 christos 265 1.15 lukem cur.h = -1; /* set flag in case I'm not set */ 266 1.15 lukem cur.v = 0; 267 1.15 lukem 268 1.15 lukem prompt_print(el, EL_PROMPT); 269 1.15 lukem 270 1.15 lukem /* draw the current input buffer */ 271 1.16 jdolecek #if notyet 272 1.36 christos termsz = el->el_terminal.t_size.h * el->el_terminal.t_size.v; 273 1.16 jdolecek if (el->el_line.lastchar - el->el_line.buffer > termsz) { 274 1.16 jdolecek /* 275 1.16 jdolecek * If line is longer than terminal, process only part 276 1.16 jdolecek * of line which would influence display. 277 1.16 jdolecek */ 278 1.16 jdolecek size_t rem = (el->el_line.lastchar-el->el_line.buffer)%termsz; 279 1.16 jdolecek 280 1.16 jdolecek st = el->el_line.lastchar - rem 281 1.36 christos - (termsz - (((rem / el->el_terminal.t_size.v) - 1) 282 1.36 christos * el->el_terminal.t_size.v)); 283 1.16 jdolecek } else 284 1.16 jdolecek #endif 285 1.16 jdolecek st = el->el_line.buffer; 286 1.16 jdolecek 287 1.16 jdolecek for (cp = st; cp < el->el_line.lastchar; cp++) { 288 1.15 lukem if (cp == el->el_line.cursor) { 289 1.46 christos int w = wcwidth(*cp); 290 1.16 jdolecek /* save for later */ 291 1.15 lukem cur.h = el->el_refresh.r_cursor.h; 292 1.15 lukem cur.v = el->el_refresh.r_cursor.v; 293 1.35 christos /* handle being at a linebroken doublewidth char */ 294 1.35 christos if (w > 1 && el->el_refresh.r_cursor.h + w > 295 1.36 christos el->el_terminal.t_size.h) { 296 1.35 christos cur.h = 0; 297 1.35 christos cur.v++; 298 1.35 christos } 299 1.15 lukem } 300 1.35 christos re_addc(el, *cp); 301 1.15 lukem } 302 1.15 lukem 303 1.15 lukem if (cur.h == -1) { /* if I haven't been set yet, I'm at the end */ 304 1.15 lukem cur.h = el->el_refresh.r_cursor.h; 305 1.15 lukem cur.v = el->el_refresh.r_cursor.v; 306 1.15 lukem } 307 1.36 christos rhdiff = el->el_terminal.t_size.h - el->el_refresh.r_cursor.h - 308 1.15 lukem el->el_rprompt.p_pos.h; 309 1.15 lukem if (el->el_rprompt.p_pos.h && !el->el_rprompt.p_pos.v && 310 1.15 lukem !el->el_refresh.r_cursor.v && rhdiff > 1) { 311 1.15 lukem /* 312 1.15 lukem * have a right-hand side prompt that will fit 313 1.15 lukem * on the end of the first line with at least 314 1.15 lukem * one character gap to the input buffer. 315 1.15 lukem */ 316 1.15 lukem while (--rhdiff > 0) /* pad out with spaces */ 317 1.16 jdolecek re_putc(el, ' ', 1); 318 1.15 lukem prompt_print(el, EL_RPROMPT); 319 1.15 lukem } else { 320 1.15 lukem el->el_rprompt.p_pos.h = 0; /* flag "not using rprompt" */ 321 1.15 lukem el->el_rprompt.p_pos.v = 0; 322 1.15 lukem } 323 1.15 lukem 324 1.16 jdolecek re_putc(el, '\0', 0); /* make line ended with NUL, no cursor shift */ 325 1.16 jdolecek 326 1.15 lukem el->el_refresh.r_newcv = el->el_refresh.r_cursor.v; 327 1.15 lukem 328 1.15 lukem ELRE_DEBUG(1, (__F, 329 1.15 lukem "term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n", 330 1.36 christos el->el_terminal.t_size.h, el->el_refresh.r_cursor.h, 331 1.45 christos el->el_refresh.r_cursor.v, ct_encode_string(el->el_vdisplay[0], 332 1.45 christos &el->el_scratch))); 333 1.15 lukem 334 1.17 lukem ELRE_DEBUG(1, (__F, "updating %d lines.\r\n", el->el_refresh.r_newcv)); 335 1.15 lukem for (i = 0; i <= el->el_refresh.r_newcv; i++) { 336 1.15 lukem /* NOTE THAT re_update_line MAY CHANGE el_display[i] */ 337 1.58 christos re_update_line(el, (wchar_t *)el->el_display[i], 338 1.58 christos (wchar_t *)el->el_vdisplay[i], i); 339 1.15 lukem 340 1.15 lukem /* 341 1.15 lukem * Copy the new line to be the current one, and pad out with 342 1.15 lukem * spaces to the full width of the terminal so that if we try 343 1.15 lukem * moving the cursor by writing the character that is at the 344 1.15 lukem * end of the screen line, it won't be a NUL or some old 345 1.15 lukem * leftover stuff. 346 1.15 lukem */ 347 1.58 christos re__copy_and_pad((wchar_t *)el->el_display[i], 348 1.58 christos (wchar_t *)el->el_vdisplay[i], 349 1.36 christos (size_t) el->el_terminal.t_size.h); 350 1.15 lukem } 351 1.15 lukem ELRE_DEBUG(1, (__F, 352 1.15 lukem "\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n", 353 1.17 lukem el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i)); 354 1.15 lukem 355 1.15 lukem if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv) 356 1.15 lukem for (; i <= el->el_refresh.r_oldcv; i++) { 357 1.36 christos terminal_move_to_line(el, i); 358 1.36 christos terminal_move_to_char(el, 0); 359 1.47 christos /* This wcslen should be safe even with MB_FILL_CHARs */ 360 1.58 christos terminal_clear_EOL(el, 361 1.58 christos (int) wcslen((const wchar_t *)el->el_display[i])); 362 1.1 cgd #ifdef DEBUG_REFRESH 363 1.47 christos terminal_overwrite(el, L"C\b", 2); 364 1.1 cgd #endif /* DEBUG_REFRESH */ 365 1.16 jdolecek el->el_display[i][0] = '\0'; 366 1.15 lukem } 367 1.8 simonb 368 1.15 lukem el->el_refresh.r_oldcv = el->el_refresh.r_newcv; /* set for next time */ 369 1.15 lukem ELRE_DEBUG(1, (__F, 370 1.15 lukem "\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n", 371 1.15 lukem el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v, 372 1.17 lukem cur.h, cur.v)); 373 1.36 christos terminal_move_to_line(el, cur.v); /* go to where the cursor is */ 374 1.36 christos terminal_move_to_char(el, cur.h); 375 1.15 lukem } 376 1.1 cgd 377 1.1 cgd 378 1.1 cgd /* re_goto_bottom(): 379 1.8 simonb * used to go to last used screen line 380 1.1 cgd */ 381 1.51 christos libedit_private void 382 1.15 lukem re_goto_bottom(EditLine *el) 383 1.1 cgd { 384 1.15 lukem 385 1.36 christos terminal_move_to_line(el, el->el_refresh.r_oldcv); 386 1.36 christos terminal__putc(el, '\n'); 387 1.15 lukem re_clear_display(el); 388 1.36 christos terminal__flush(el); 389 1.15 lukem } 390 1.1 cgd 391 1.1 cgd 392 1.1 cgd /* re_insert(): 393 1.1 cgd * insert num characters of s into d (in front of the character) 394 1.8 simonb * at dat, maximum length of d is dlen 395 1.1 cgd */ 396 1.49 christos static void 397 1.1 cgd /*ARGSUSED*/ 398 1.25 christos re_insert(EditLine *el __attribute__((__unused__)), 399 1.48 christos wchar_t *d, int dat, int dlen, wchar_t *s, int num) 400 1.1 cgd { 401 1.48 christos wchar_t *a, *b; 402 1.1 cgd 403 1.15 lukem if (num <= 0) 404 1.15 lukem return; 405 1.15 lukem if (num > dlen - dat) 406 1.15 lukem num = dlen - dat; 407 1.1 cgd 408 1.15 lukem ELRE_DEBUG(1, 409 1.15 lukem (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n", 410 1.45 christos num, dat, dlen, ct_encode_string(d, &el->el_scratch))); 411 1.45 christos ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s, 412 1.45 christos &el->el_scratch))); 413 1.1 cgd 414 1.15 lukem /* open up the space for num chars */ 415 1.15 lukem if (num > 0) { 416 1.15 lukem b = d + dlen - 1; 417 1.15 lukem a = b - num; 418 1.15 lukem while (a >= &d[dat]) 419 1.15 lukem *b-- = *a--; 420 1.15 lukem d[dlen] = '\0'; /* just in case */ 421 1.15 lukem } 422 1.35 christos 423 1.15 lukem ELRE_DEBUG(1, (__F, 424 1.1 cgd "re_insert() after insert: %d at %d max %d, d == \"%s\"\n", 425 1.45 christos num, dat, dlen, ct_encode_string(d, &el->el_scratch))); 426 1.45 christos ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s, 427 1.45 christos &el->el_scratch))); 428 1.1 cgd 429 1.15 lukem /* copy the characters */ 430 1.15 lukem for (a = d + dat; (a < d + dlen) && (num > 0); num--) 431 1.15 lukem *a++ = *s++; 432 1.15 lukem 433 1.35 christos #ifdef notyet 434 1.35 christos /* ct_encode_string() uses a static buffer, so we can't conveniently 435 1.35 christos * encode both d & s here */ 436 1.15 lukem ELRE_DEBUG(1, 437 1.15 lukem (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n", 438 1.17 lukem num, dat, dlen, d, s)); 439 1.27 christos ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s)); 440 1.35 christos #endif 441 1.15 lukem } 442 1.1 cgd 443 1.1 cgd 444 1.1 cgd /* re_delete(): 445 1.8 simonb * delete num characters d at dat, maximum length of d is dlen 446 1.1 cgd */ 447 1.49 christos static void 448 1.1 cgd /*ARGSUSED*/ 449 1.25 christos re_delete(EditLine *el __attribute__((__unused__)), 450 1.48 christos wchar_t *d, int dat, int dlen, int num) 451 1.1 cgd { 452 1.48 christos wchar_t *a, *b; 453 1.1 cgd 454 1.15 lukem if (num <= 0) 455 1.15 lukem return; 456 1.15 lukem if (dat + num >= dlen) { 457 1.15 lukem d[dat] = '\0'; 458 1.15 lukem return; 459 1.15 lukem } 460 1.15 lukem ELRE_DEBUG(1, 461 1.15 lukem (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n", 462 1.45 christos num, dat, dlen, ct_encode_string(d, &el->el_scratch))); 463 1.1 cgd 464 1.15 lukem /* open up the space for num chars */ 465 1.15 lukem if (num > 0) { 466 1.15 lukem b = d + dat; 467 1.15 lukem a = b + num; 468 1.15 lukem while (a < &d[dlen]) 469 1.15 lukem *b++ = *a++; 470 1.15 lukem d[dlen] = '\0'; /* just in case */ 471 1.15 lukem } 472 1.15 lukem ELRE_DEBUG(1, 473 1.15 lukem (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n", 474 1.45 christos num, dat, dlen, ct_encode_string(d, &el->el_scratch))); 475 1.15 lukem } 476 1.1 cgd 477 1.1 cgd 478 1.1 cgd /* re__strncopy(): 479 1.1 cgd * Like strncpy without padding. 480 1.1 cgd */ 481 1.49 christos static void 482 1.48 christos re__strncopy(wchar_t *a, wchar_t *b, size_t n) 483 1.1 cgd { 484 1.15 lukem 485 1.15 lukem while (n-- && *b) 486 1.15 lukem *a++ = *b++; 487 1.15 lukem } 488 1.1 cgd 489 1.27 christos /* re_clear_eol(): 490 1.27 christos * Find the number of characters we need to clear till the end of line 491 1.27 christos * in order to make sure that we have cleared the previous contents of 492 1.27 christos * the line. fx and sx is the number of characters inserted or deleted 493 1.35 christos * in the first or second diff, diff is the difference between the 494 1.44 christos * number of characters between the new and old line. 495 1.27 christos */ 496 1.49 christos static void 497 1.27 christos re_clear_eol(EditLine *el, int fx, int sx, int diff) 498 1.27 christos { 499 1.27 christos 500 1.27 christos ELRE_DEBUG(1, (__F, "re_clear_eol sx %d, fx %d, diff %d\n", 501 1.27 christos sx, fx, diff)); 502 1.27 christos 503 1.27 christos if (fx < 0) 504 1.27 christos fx = -fx; 505 1.27 christos if (sx < 0) 506 1.27 christos sx = -sx; 507 1.27 christos if (fx > diff) 508 1.27 christos diff = fx; 509 1.27 christos if (sx > diff) 510 1.27 christos diff = sx; 511 1.27 christos 512 1.27 christos ELRE_DEBUG(1, (__F, "re_clear_eol %d\n", diff)); 513 1.36 christos terminal_clear_EOL(el, diff); 514 1.27 christos } 515 1.1 cgd 516 1.15 lukem /***************************************************************** 517 1.1 cgd re_update_line() is based on finding the middle difference of each line 518 1.1 cgd on the screen; vis: 519 1.1 cgd 520 1.1 cgd /old first difference 521 1.1 cgd /beginning of line | /old last same /old EOL 522 1.1 cgd v v v v 523 1.1 cgd old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as 524 1.1 cgd new: eddie> Oh, my little buggy says to me, as lurgid as 525 1.1 cgd ^ ^ ^ ^ 526 1.1 cgd \beginning of line | \new last same \new end of line 527 1.1 cgd \new first difference 528 1.1 cgd 529 1.1 cgd all are character pointers for the sake of speed. Special cases for 530 1.1 cgd no differences, as well as for end of line additions must be handled. 531 1.1 cgd **************************************************************** */ 532 1.1 cgd 533 1.1 cgd /* Minimum at which doing an insert it "worth it". This should be about 534 1.1 cgd * half the "cost" of going into insert mode, inserting a character, and 535 1.1 cgd * going back out. This should really be calculated from the termcap 536 1.1 cgd * data... For the moment, a good number for ANSI terminals. 537 1.1 cgd */ 538 1.15 lukem #define MIN_END_KEEP 4 539 1.1 cgd 540 1.49 christos static void 541 1.48 christos re_update_line(EditLine *el, wchar_t *old, wchar_t *new, int i) 542 1.1 cgd { 543 1.48 christos wchar_t *o, *n, *p, c; 544 1.48 christos wchar_t *ofd, *ols, *oe, *nfd, *nls, *ne; 545 1.48 christos wchar_t *osb, *ose, *nsb, *nse; 546 1.15 lukem int fx, sx; 547 1.30 christos size_t len; 548 1.15 lukem 549 1.15 lukem /* 550 1.15 lukem * find first diff 551 1.15 lukem */ 552 1.15 lukem for (o = old, n = new; *o && (*o == *n); o++, n++) 553 1.15 lukem continue; 554 1.15 lukem ofd = o; 555 1.15 lukem nfd = n; 556 1.15 lukem 557 1.15 lukem /* 558 1.15 lukem * Find the end of both old and new 559 1.15 lukem */ 560 1.15 lukem while (*o) 561 1.15 lukem o++; 562 1.15 lukem /* 563 1.15 lukem * Remove any trailing blanks off of the end, being careful not to 564 1.15 lukem * back up past the beginning. 565 1.15 lukem */ 566 1.15 lukem while (ofd < o) { 567 1.15 lukem if (o[-1] != ' ') 568 1.15 lukem break; 569 1.15 lukem o--; 570 1.15 lukem } 571 1.15 lukem oe = o; 572 1.15 lukem *oe = '\0'; 573 1.15 lukem 574 1.15 lukem while (*n) 575 1.15 lukem n++; 576 1.15 lukem 577 1.15 lukem /* remove blanks from end of new */ 578 1.15 lukem while (nfd < n) { 579 1.15 lukem if (n[-1] != ' ') 580 1.15 lukem break; 581 1.15 lukem n--; 582 1.15 lukem } 583 1.15 lukem ne = n; 584 1.15 lukem *ne = '\0'; 585 1.15 lukem 586 1.15 lukem /* 587 1.15 lukem * if no diff, continue to next line of redraw 588 1.15 lukem */ 589 1.15 lukem if (*ofd == '\0' && *nfd == '\0') { 590 1.17 lukem ELRE_DEBUG(1, (__F, "no difference.\r\n")); 591 1.15 lukem return; 592 1.15 lukem } 593 1.15 lukem /* 594 1.15 lukem * find last same pointer 595 1.15 lukem */ 596 1.15 lukem while ((o > ofd) && (n > nfd) && (*--o == *--n)) 597 1.15 lukem continue; 598 1.15 lukem ols = ++o; 599 1.15 lukem nls = ++n; 600 1.15 lukem 601 1.15 lukem /* 602 1.50 christos * find same beginning and same end 603 1.15 lukem */ 604 1.1 cgd osb = ols; 605 1.15 lukem nsb = nls; 606 1.1 cgd ose = ols; 607 1.1 cgd nse = nls; 608 1.1 cgd 609 1.15 lukem /* 610 1.15 lukem * case 1: insert: scan from nfd to nls looking for *ofd 611 1.15 lukem */ 612 1.15 lukem if (*ofd) { 613 1.15 lukem for (c = *ofd, n = nfd; n < nls; n++) { 614 1.15 lukem if (c == *n) { 615 1.15 lukem for (o = ofd, p = n; 616 1.15 lukem p < nls && o < ols && *o == *p; 617 1.15 lukem o++, p++) 618 1.15 lukem continue; 619 1.15 lukem /* 620 1.15 lukem * if the new match is longer and it's worth 621 1.15 lukem * keeping, then we take it 622 1.15 lukem */ 623 1.15 lukem if (((nse - nsb) < (p - n)) && 624 1.15 lukem (2 * (p - n) > n - nfd)) { 625 1.15 lukem nsb = n; 626 1.15 lukem nse = p; 627 1.15 lukem osb = ofd; 628 1.15 lukem ose = o; 629 1.15 lukem } 630 1.15 lukem } 631 1.15 lukem } 632 1.15 lukem } 633 1.15 lukem /* 634 1.15 lukem * case 2: delete: scan from ofd to ols looking for *nfd 635 1.15 lukem */ 636 1.15 lukem if (*nfd) { 637 1.15 lukem for (c = *nfd, o = ofd; o < ols; o++) { 638 1.15 lukem if (c == *o) { 639 1.15 lukem for (n = nfd, p = o; 640 1.15 lukem p < ols && n < nls && *p == *n; 641 1.15 lukem p++, n++) 642 1.15 lukem continue; 643 1.15 lukem /* 644 1.15 lukem * if the new match is longer and it's worth 645 1.15 lukem * keeping, then we take it 646 1.15 lukem */ 647 1.15 lukem if (((ose - osb) < (p - o)) && 648 1.15 lukem (2 * (p - o) > o - ofd)) { 649 1.15 lukem nsb = nfd; 650 1.15 lukem nse = n; 651 1.15 lukem osb = o; 652 1.15 lukem ose = p; 653 1.15 lukem } 654 1.15 lukem } 655 1.15 lukem } 656 1.15 lukem } 657 1.15 lukem /* 658 1.15 lukem * Pragmatics I: If old trailing whitespace or not enough characters to 659 1.15 lukem * save to be worth it, then don't save the last same info. 660 1.15 lukem */ 661 1.15 lukem if ((oe - ols) < MIN_END_KEEP) { 662 1.15 lukem ols = oe; 663 1.15 lukem nls = ne; 664 1.15 lukem } 665 1.15 lukem /* 666 1.15 lukem * Pragmatics II: if the terminal isn't smart enough, make the data 667 1.15 lukem * dumber so the smart update doesn't try anything fancy 668 1.15 lukem */ 669 1.15 lukem 670 1.15 lukem /* 671 1.15 lukem * fx is the number of characters we need to insert/delete: in the 672 1.15 lukem * beginning to bring the two same begins together 673 1.15 lukem */ 674 1.29 christos fx = (int)((nsb - nfd) - (osb - ofd)); 675 1.15 lukem /* 676 1.15 lukem * sx is the number of characters we need to insert/delete: in the 677 1.15 lukem * end to bring the two same last parts together 678 1.15 lukem */ 679 1.29 christos sx = (int)((nls - nse) - (ols - ose)); 680 1.15 lukem 681 1.15 lukem if (!EL_CAN_INSERT) { 682 1.15 lukem if (fx > 0) { 683 1.15 lukem osb = ols; 684 1.15 lukem ose = ols; 685 1.15 lukem nsb = nls; 686 1.15 lukem nse = nls; 687 1.15 lukem } 688 1.15 lukem if (sx > 0) { 689 1.15 lukem ols = oe; 690 1.15 lukem nls = ne; 691 1.15 lukem } 692 1.15 lukem if ((ols - ofd) < (nls - nfd)) { 693 1.15 lukem ols = oe; 694 1.15 lukem nls = ne; 695 1.15 lukem } 696 1.15 lukem } 697 1.15 lukem if (!EL_CAN_DELETE) { 698 1.15 lukem if (fx < 0) { 699 1.15 lukem osb = ols; 700 1.15 lukem ose = ols; 701 1.15 lukem nsb = nls; 702 1.15 lukem nse = nls; 703 1.15 lukem } 704 1.15 lukem if (sx < 0) { 705 1.15 lukem ols = oe; 706 1.15 lukem nls = ne; 707 1.15 lukem } 708 1.15 lukem if ((ols - ofd) > (nls - nfd)) { 709 1.15 lukem ols = oe; 710 1.15 lukem nls = ne; 711 1.15 lukem } 712 1.15 lukem } 713 1.15 lukem /* 714 1.15 lukem * Pragmatics III: make sure the middle shifted pointers are correct if 715 1.15 lukem * they don't point to anything (we may have moved ols or nls). 716 1.15 lukem */ 717 1.15 lukem /* if the change isn't worth it, don't bother */ 718 1.15 lukem /* was: if (osb == ose) */ 719 1.15 lukem if ((ose - osb) < MIN_END_KEEP) { 720 1.15 lukem osb = ols; 721 1.15 lukem ose = ols; 722 1.15 lukem nsb = nls; 723 1.15 lukem nse = nls; 724 1.15 lukem } 725 1.15 lukem /* 726 1.15 lukem * Now that we are done with pragmatics we recompute fx, sx 727 1.15 lukem */ 728 1.29 christos fx = (int)((nsb - nfd) - (osb - ofd)); 729 1.29 christos sx = (int)((nls - nse) - (ols - ose)); 730 1.15 lukem 731 1.27 christos ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx)); 732 1.45 christos ELRE_DEBUG(1, (__F, "ofd %td, osb %td, ose %td, ols %td, oe %td\n", 733 1.17 lukem ofd - old, osb - old, ose - old, ols - old, oe - old)); 734 1.45 christos ELRE_DEBUG(1, (__F, "nfd %td, nsb %td, nse %td, nls %td, ne %td\n", 735 1.17 lukem nfd - new, nsb - new, nse - new, nls - new, ne - new)); 736 1.15 lukem ELRE_DEBUG(1, (__F, 737 1.17 lukem "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n")); 738 1.15 lukem ELRE_DEBUG(1, (__F, 739 1.17 lukem "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n")); 740 1.1 cgd #ifdef DEBUG_REFRESH 741 1.15 lukem re_printstr(el, "old- oe", old, oe); 742 1.15 lukem re_printstr(el, "new- ne", new, ne); 743 1.15 lukem re_printstr(el, "old-ofd", old, ofd); 744 1.15 lukem re_printstr(el, "new-nfd", new, nfd); 745 1.15 lukem re_printstr(el, "ofd-osb", ofd, osb); 746 1.15 lukem re_printstr(el, "nfd-nsb", nfd, nsb); 747 1.15 lukem re_printstr(el, "osb-ose", osb, ose); 748 1.15 lukem re_printstr(el, "nsb-nse", nsb, nse); 749 1.15 lukem re_printstr(el, "ose-ols", ose, ols); 750 1.15 lukem re_printstr(el, "nse-nls", nse, nls); 751 1.15 lukem re_printstr(el, "ols- oe", ols, oe); 752 1.15 lukem re_printstr(el, "nls- ne", nls, ne); 753 1.1 cgd #endif /* DEBUG_REFRESH */ 754 1.1 cgd 755 1.15 lukem /* 756 1.15 lukem * el_cursor.v to this line i MUST be in this routine so that if we 757 1.15 lukem * don't have to change the line, we don't move to it. el_cursor.h to 758 1.15 lukem * first diff char 759 1.15 lukem */ 760 1.36 christos terminal_move_to_line(el, i); 761 1.15 lukem 762 1.15 lukem /* 763 1.15 lukem * at this point we have something like this: 764 1.15 lukem * 765 1.15 lukem * /old /ofd /osb /ose /ols /oe 766 1.15 lukem * v.....................v v..................v v........v 767 1.15 lukem * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as 768 1.15 lukem * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as 769 1.15 lukem * ^.....................^ ^..................^ ^........^ 770 1.15 lukem * \new \nfd \nsb \nse \nls \ne 771 1.15 lukem * 772 1.15 lukem * fx is the difference in length between the chars between nfd and 773 1.15 lukem * nsb, and the chars between ofd and osb, and is thus the number of 774 1.15 lukem * characters to delete if < 0 (new is shorter than old, as above), 775 1.15 lukem * or insert (new is longer than short). 776 1.15 lukem * 777 1.15 lukem * sx is the same for the second differences. 778 1.15 lukem */ 779 1.15 lukem 780 1.15 lukem /* 781 1.15 lukem * if we have a net insert on the first difference, AND inserting the 782 1.15 lukem * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful 783 1.15 lukem * character (which is ne if nls != ne, otherwise is nse) off the edge 784 1.36 christos * of the screen (el->el_terminal.t_size.h) else we do the deletes first 785 1.15 lukem * so that we keep everything we need to. 786 1.15 lukem */ 787 1.15 lukem 788 1.15 lukem /* 789 1.15 lukem * if the last same is the same like the end, there is no last same 790 1.15 lukem * part, otherwise we want to keep the last same part set p to the 791 1.15 lukem * last useful old character 792 1.15 lukem */ 793 1.15 lukem p = (ols != oe) ? oe : ose; 794 1.15 lukem 795 1.15 lukem /* 796 1.15 lukem * if (There is a diffence in the beginning) && (we need to insert 797 1.15 lukem * characters) && (the number of characters to insert is less than 798 1.15 lukem * the term width) 799 1.15 lukem * We need to do an insert! 800 1.15 lukem * else if (we need to delete characters) 801 1.15 lukem * We need to delete characters! 802 1.15 lukem * else 803 1.15 lukem * No insert or delete 804 1.15 lukem */ 805 1.15 lukem if ((nsb != nfd) && fx > 0 && 806 1.36 christos ((p - old) + fx <= el->el_terminal.t_size.h)) { 807 1.15 lukem ELRE_DEBUG(1, 808 1.45 christos (__F, "first diff insert at %td...\r\n", nfd - new)); 809 1.1 cgd /* 810 1.15 lukem * Move to the first char to insert, where the first diff is. 811 1.15 lukem */ 812 1.36 christos terminal_move_to_char(el, (int)(nfd - new)); 813 1.15 lukem /* 814 1.15 lukem * Check if we have stuff to keep at end 815 1.15 lukem */ 816 1.15 lukem if (nsb != ne) { 817 1.17 lukem ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); 818 1.15 lukem /* 819 1.15 lukem * insert fx chars of new starting at nfd 820 1.15 lukem */ 821 1.15 lukem if (fx > 0) { 822 1.15 lukem ELRE_DEBUG(!EL_CAN_INSERT, (__F, 823 1.17 lukem "ERROR: cannot insert in early first diff\n")); 824 1.36 christos terminal_insertwrite(el, nfd, fx); 825 1.29 christos re_insert(el, old, (int)(ofd - old), 826 1.36 christos el->el_terminal.t_size.h, nfd, fx); 827 1.15 lukem } 828 1.15 lukem /* 829 1.15 lukem * write (nsb-nfd) - fx chars of new starting at 830 1.15 lukem * (nfd + fx) 831 1.15 lukem */ 832 1.30 christos len = (size_t) ((nsb - nfd) - fx); 833 1.36 christos terminal_overwrite(el, (nfd + fx), len); 834 1.30 christos re__strncopy(ofd + fx, nfd + fx, len); 835 1.15 lukem } else { 836 1.17 lukem ELRE_DEBUG(1, (__F, "without anything to save\r\n")); 837 1.30 christos len = (size_t)(nsb - nfd); 838 1.36 christos terminal_overwrite(el, nfd, len); 839 1.30 christos re__strncopy(ofd, nfd, len); 840 1.15 lukem /* 841 1.15 lukem * Done 842 1.15 lukem */ 843 1.15 lukem return; 844 1.15 lukem } 845 1.15 lukem } else if (fx < 0) { 846 1.15 lukem ELRE_DEBUG(1, 847 1.45 christos (__F, "first diff delete at %td...\r\n", ofd - old)); 848 1.15 lukem /* 849 1.15 lukem * move to the first char to delete where the first diff is 850 1.15 lukem */ 851 1.36 christos terminal_move_to_char(el, (int)(ofd - old)); 852 1.15 lukem /* 853 1.15 lukem * Check if we have stuff to save 854 1.15 lukem */ 855 1.15 lukem if (osb != oe) { 856 1.17 lukem ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); 857 1.15 lukem /* 858 1.15 lukem * fx is less than zero *always* here but we check 859 1.15 lukem * for code symmetry 860 1.15 lukem */ 861 1.15 lukem if (fx < 0) { 862 1.15 lukem ELRE_DEBUG(!EL_CAN_DELETE, (__F, 863 1.17 lukem "ERROR: cannot delete in first diff\n")); 864 1.36 christos terminal_deletechars(el, -fx); 865 1.29 christos re_delete(el, old, (int)(ofd - old), 866 1.36 christos el->el_terminal.t_size.h, -fx); 867 1.15 lukem } 868 1.15 lukem /* 869 1.15 lukem * write (nsb-nfd) chars of new starting at nfd 870 1.15 lukem */ 871 1.30 christos len = (size_t) (nsb - nfd); 872 1.36 christos terminal_overwrite(el, nfd, len); 873 1.30 christos re__strncopy(ofd, nfd, len); 874 1.15 lukem 875 1.15 lukem } else { 876 1.15 lukem ELRE_DEBUG(1, (__F, 877 1.17 lukem "but with nothing left to save\r\n")); 878 1.15 lukem /* 879 1.15 lukem * write (nsb-nfd) chars of new starting at nfd 880 1.15 lukem */ 881 1.36 christos terminal_overwrite(el, nfd, (size_t)(nsb - nfd)); 882 1.29 christos re_clear_eol(el, fx, sx, 883 1.29 christos (int)((oe - old) - (ne - new))); 884 1.15 lukem /* 885 1.15 lukem * Done 886 1.15 lukem */ 887 1.15 lukem return; 888 1.15 lukem } 889 1.15 lukem } else 890 1.15 lukem fx = 0; 891 1.15 lukem 892 1.36 christos if (sx < 0 && (ose - old) + fx < el->el_terminal.t_size.h) { 893 1.15 lukem ELRE_DEBUG(1, (__F, 894 1.45 christos "second diff delete at %td...\r\n", (ose - old) + fx)); 895 1.15 lukem /* 896 1.15 lukem * Check if we have stuff to delete 897 1.15 lukem */ 898 1.15 lukem /* 899 1.15 lukem * fx is the number of characters inserted (+) or deleted (-) 900 1.1 cgd */ 901 1.15 lukem 902 1.36 christos terminal_move_to_char(el, (int)((ose - old) + fx)); 903 1.15 lukem /* 904 1.15 lukem * Check if we have stuff to save 905 1.15 lukem */ 906 1.15 lukem if (ols != oe) { 907 1.17 lukem ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); 908 1.15 lukem /* 909 1.15 lukem * Again a duplicate test. 910 1.15 lukem */ 911 1.15 lukem if (sx < 0) { 912 1.15 lukem ELRE_DEBUG(!EL_CAN_DELETE, (__F, 913 1.17 lukem "ERROR: cannot delete in second diff\n")); 914 1.36 christos terminal_deletechars(el, -sx); 915 1.15 lukem } 916 1.15 lukem /* 917 1.15 lukem * write (nls-nse) chars of new starting at nse 918 1.15 lukem */ 919 1.36 christos terminal_overwrite(el, nse, (size_t)(nls - nse)); 920 1.15 lukem } else { 921 1.15 lukem ELRE_DEBUG(1, (__F, 922 1.17 lukem "but with nothing left to save\r\n")); 923 1.36 christos terminal_overwrite(el, nse, (size_t)(nls - nse)); 924 1.29 christos re_clear_eol(el, fx, sx, 925 1.29 christos (int)((oe - old) - (ne - new))); 926 1.15 lukem } 927 1.15 lukem } 928 1.15 lukem /* 929 1.15 lukem * if we have a first insert AND WE HAVEN'T ALREADY DONE IT... 930 1.15 lukem */ 931 1.15 lukem if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) { 932 1.45 christos ELRE_DEBUG(1, (__F, "late first diff insert at %td...\r\n", 933 1.17 lukem nfd - new)); 934 1.15 lukem 935 1.36 christos terminal_move_to_char(el, (int)(nfd - new)); 936 1.15 lukem /* 937 1.15 lukem * Check if we have stuff to keep at the end 938 1.15 lukem */ 939 1.15 lukem if (nsb != ne) { 940 1.17 lukem ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); 941 1.15 lukem /* 942 1.15 lukem * We have to recalculate fx here because we set it 943 1.15 lukem * to zero above as a flag saying that we hadn't done 944 1.15 lukem * an early first insert. 945 1.15 lukem */ 946 1.29 christos fx = (int)((nsb - nfd) - (osb - ofd)); 947 1.15 lukem if (fx > 0) { 948 1.15 lukem /* 949 1.15 lukem * insert fx chars of new starting at nfd 950 1.15 lukem */ 951 1.15 lukem ELRE_DEBUG(!EL_CAN_INSERT, (__F, 952 1.17 lukem "ERROR: cannot insert in late first diff\n")); 953 1.36 christos terminal_insertwrite(el, nfd, fx); 954 1.29 christos re_insert(el, old, (int)(ofd - old), 955 1.36 christos el->el_terminal.t_size.h, nfd, fx); 956 1.15 lukem } 957 1.15 lukem /* 958 1.15 lukem * write (nsb-nfd) - fx chars of new starting at 959 1.15 lukem * (nfd + fx) 960 1.15 lukem */ 961 1.30 christos len = (size_t) ((nsb - nfd) - fx); 962 1.36 christos terminal_overwrite(el, (nfd + fx), len); 963 1.30 christos re__strncopy(ofd + fx, nfd + fx, len); 964 1.15 lukem } else { 965 1.17 lukem ELRE_DEBUG(1, (__F, "without anything to save\r\n")); 966 1.30 christos len = (size_t) (nsb - nfd); 967 1.36 christos terminal_overwrite(el, nfd, len); 968 1.30 christos re__strncopy(ofd, nfd, len); 969 1.15 lukem } 970 1.15 lukem } 971 1.15 lukem /* 972 1.15 lukem * line is now NEW up to nse 973 1.15 lukem */ 974 1.15 lukem if (sx >= 0) { 975 1.15 lukem ELRE_DEBUG(1, (__F, 976 1.29 christos "second diff insert at %d...\r\n", (int)(nse - new))); 977 1.36 christos terminal_move_to_char(el, (int)(nse - new)); 978 1.15 lukem if (ols != oe) { 979 1.17 lukem ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); 980 1.15 lukem if (sx > 0) { 981 1.15 lukem /* insert sx chars of new starting at nse */ 982 1.15 lukem ELRE_DEBUG(!EL_CAN_INSERT, (__F, 983 1.17 lukem "ERROR: cannot insert in second diff\n")); 984 1.36 christos terminal_insertwrite(el, nse, sx); 985 1.15 lukem } 986 1.15 lukem /* 987 1.15 lukem * write (nls-nse) - sx chars of new starting at 988 1.15 lukem * (nse + sx) 989 1.15 lukem */ 990 1.36 christos terminal_overwrite(el, (nse + sx), 991 1.30 christos (size_t)((nls - nse) - sx)); 992 1.15 lukem } else { 993 1.17 lukem ELRE_DEBUG(1, (__F, "without anything to save\r\n")); 994 1.36 christos terminal_overwrite(el, nse, (size_t)(nls - nse)); 995 1.15 lukem 996 1.15 lukem /* 997 1.15 lukem * No need to do a clear-to-end here because we were 998 1.15 lukem * doing a second insert, so we will have over 999 1.15 lukem * written all of the old string. 1000 1.15 lukem */ 1001 1.15 lukem } 1002 1.15 lukem } 1003 1.17 lukem ELRE_DEBUG(1, (__F, "done.\r\n")); 1004 1.15 lukem } 1005 1.1 cgd 1006 1.1 cgd 1007 1.1 cgd /* re__copy_and_pad(): 1008 1.1 cgd * Copy string and pad with spaces 1009 1.1 cgd */ 1010 1.49 christos static void 1011 1.48 christos re__copy_and_pad(wchar_t *dst, const wchar_t *src, size_t width) 1012 1.1 cgd { 1013 1.21 thorpej size_t i; 1014 1.1 cgd 1015 1.15 lukem for (i = 0; i < width; i++) { 1016 1.15 lukem if (*src == '\0') 1017 1.15 lukem break; 1018 1.15 lukem *dst++ = *src++; 1019 1.15 lukem } 1020 1.15 lukem 1021 1.16 jdolecek for (; i < width; i++) 1022 1.15 lukem *dst++ = ' '; 1023 1.16 jdolecek 1024 1.15 lukem *dst = '\0'; 1025 1.15 lukem } 1026 1.1 cgd 1027 1.1 cgd 1028 1.1 cgd /* re_refresh_cursor(): 1029 1.1 cgd * Move to the new cursor position 1030 1.1 cgd */ 1031 1.51 christos libedit_private void 1032 1.15 lukem re_refresh_cursor(EditLine *el) 1033 1.1 cgd { 1034 1.48 christos wchar_t *cp; 1035 1.35 christos int h, v, th, w; 1036 1.15 lukem 1037 1.22 christos if (el->el_line.cursor >= el->el_line.lastchar) { 1038 1.22 christos if (el->el_map.current == el->el_map.alt 1039 1.22 christos && el->el_line.lastchar != el->el_line.buffer) 1040 1.22 christos el->el_line.cursor = el->el_line.lastchar - 1; 1041 1.22 christos else 1042 1.22 christos el->el_line.cursor = el->el_line.lastchar; 1043 1.22 christos } 1044 1.22 christos 1045 1.15 lukem /* first we must find where the cursor is... */ 1046 1.15 lukem h = el->el_prompt.p_pos.h; 1047 1.15 lukem v = el->el_prompt.p_pos.v; 1048 1.36 christos th = el->el_terminal.t_size.h; /* optimize for speed */ 1049 1.15 lukem 1050 1.15 lukem /* do input buffer to el->el_line.cursor */ 1051 1.15 lukem for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) { 1052 1.35 christos switch (ct_chr_class(*cp)) { 1053 1.35 christos case CHTYPE_NL: /* handle newline in data part too */ 1054 1.15 lukem h = 0; 1055 1.15 lukem v++; 1056 1.32 christos break; 1057 1.35 christos case CHTYPE_TAB: /* if a tab, to next tab stop */ 1058 1.32 christos while (++h & 07) 1059 1.32 christos continue; 1060 1.32 christos break; 1061 1.32 christos default: 1062 1.46 christos w = wcwidth(*cp); 1063 1.35 christos if (w > 1 && h + w > th) { /* won't fit on line */ 1064 1.35 christos h = 0; 1065 1.35 christos v++; 1066 1.35 christos } 1067 1.35 christos h += ct_visual_width(*cp); 1068 1.32 christos break; 1069 1.35 christos } 1070 1.1 cgd 1071 1.15 lukem if (h >= th) { /* check, extra long tabs picked up here also */ 1072 1.32 christos h -= th; 1073 1.15 lukem v++; 1074 1.15 lukem } 1075 1.15 lukem } 1076 1.35 christos /* if we have a next character, and it's a doublewidth one, we need to 1077 1.35 christos * check whether we need to linebreak for it to fit */ 1078 1.46 christos if (cp < el->el_line.lastchar && (w = wcwidth(*cp)) > 1) 1079 1.35 christos if (h + w > th) { 1080 1.35 christos h = 0; 1081 1.35 christos v++; 1082 1.35 christos } 1083 1.15 lukem 1084 1.15 lukem /* now go there */ 1085 1.36 christos terminal_move_to_line(el, v); 1086 1.36 christos terminal_move_to_char(el, h); 1087 1.36 christos terminal__flush(el); 1088 1.15 lukem } 1089 1.1 cgd 1090 1.1 cgd 1091 1.1 cgd /* re_fastputc(): 1092 1.1 cgd * Add a character fast. 1093 1.1 cgd */ 1094 1.49 christos static void 1095 1.39 christos re_fastputc(EditLine *el, wint_t c) 1096 1.1 cgd { 1097 1.58 christos wint_t *lastline; 1098 1.55 christos int w; 1099 1.55 christos 1100 1.55 christos w = wcwidth(c); 1101 1.36 christos while (w > 1 && el->el_cursor.h + w > el->el_terminal.t_size.h) 1102 1.35 christos re_fastputc(el, ' '); 1103 1.15 lukem 1104 1.36 christos terminal__putc(el, c); 1105 1.48 christos el->el_display[el->el_cursor.v][el->el_cursor.h++] = c; 1106 1.35 christos while (--w > 0) 1107 1.35 christos el->el_display[el->el_cursor.v][el->el_cursor.h++] 1108 1.35 christos = MB_FILL_CHAR; 1109 1.35 christos 1110 1.36 christos if (el->el_cursor.h >= el->el_terminal.t_size.h) { 1111 1.15 lukem /* if we must overflow */ 1112 1.15 lukem el->el_cursor.h = 0; 1113 1.16 jdolecek 1114 1.16 jdolecek /* 1115 1.16 jdolecek * If we would overflow (input is longer than terminal size), 1116 1.16 jdolecek * emulate scroll by dropping first line and shuffling the rest. 1117 1.16 jdolecek * We do this via pointer shuffling - it's safe in this case 1118 1.16 jdolecek * and we avoid memcpy(). 1119 1.16 jdolecek */ 1120 1.36 christos if (el->el_cursor.v + 1 >= el->el_terminal.t_size.v) { 1121 1.36 christos int i, lins = el->el_terminal.t_size.v; 1122 1.44 christos 1123 1.55 christos lastline = el->el_display[0]; 1124 1.31 christos for(i = 1; i < lins; i++) 1125 1.31 christos el->el_display[i - 1] = el->el_display[i]; 1126 1.16 jdolecek 1127 1.55 christos el->el_display[i - 1] = lastline; 1128 1.16 jdolecek } else { 1129 1.16 jdolecek el->el_cursor.v++; 1130 1.55 christos lastline = el->el_display[++el->el_refresh.r_oldcv]; 1131 1.16 jdolecek } 1132 1.58 christos re__copy_and_pad((wchar_t *)lastline, L"", 1133 1.58 christos (size_t)el->el_terminal.t_size.h); 1134 1.55 christos 1135 1.15 lukem if (EL_HAS_AUTO_MARGINS) { 1136 1.15 lukem if (EL_HAS_MAGIC_MARGINS) { 1137 1.36 christos terminal__putc(el, ' '); 1138 1.36 christos terminal__putc(el, '\b'); 1139 1.15 lukem } 1140 1.15 lukem } else { 1141 1.36 christos terminal__putc(el, '\r'); 1142 1.36 christos terminal__putc(el, '\n'); 1143 1.15 lukem } 1144 1.12 christos } 1145 1.15 lukem } 1146 1.1 cgd 1147 1.1 cgd 1148 1.1 cgd /* re_fastaddc(): 1149 1.1 cgd * we added just one char, handle it fast. 1150 1.8 simonb * Assumes that screen cursor == real cursor 1151 1.1 cgd */ 1152 1.51 christos libedit_private void 1153 1.15 lukem re_fastaddc(EditLine *el) 1154 1.1 cgd { 1155 1.48 christos wchar_t c; 1156 1.15 lukem int rhdiff; 1157 1.1 cgd 1158 1.59 christos if (el->el_line.cursor == el->el_line.buffer) { 1159 1.59 christos re_refresh(el); 1160 1.59 christos return; 1161 1.59 christos } 1162 1.15 lukem c = el->el_line.cursor[-1]; 1163 1.1 cgd 1164 1.15 lukem if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) { 1165 1.15 lukem re_refresh(el); /* too hard to handle */ 1166 1.15 lukem return; 1167 1.15 lukem } 1168 1.36 christos rhdiff = el->el_terminal.t_size.h - el->el_cursor.h - 1169 1.15 lukem el->el_rprompt.p_pos.h; 1170 1.15 lukem if (el->el_rprompt.p_pos.h && rhdiff < 3) { 1171 1.15 lukem re_refresh(el); /* clear out rprompt if less than 1 char gap */ 1172 1.15 lukem return; 1173 1.15 lukem } /* else (only do at end of line, no TAB) */ 1174 1.35 christos switch (ct_chr_class(c)) { 1175 1.35 christos case CHTYPE_TAB: /* already handled, should never happen here */ 1176 1.35 christos break; 1177 1.35 christos case CHTYPE_NL: 1178 1.35 christos case CHTYPE_PRINT: 1179 1.15 lukem re_fastputc(el, c); 1180 1.35 christos break; 1181 1.35 christos case CHTYPE_ASCIICTL: 1182 1.35 christos case CHTYPE_NONPRINT: { 1183 1.48 christos wchar_t visbuf[VISUAL_WIDTH_MAX]; 1184 1.35 christos ssize_t i, n = 1185 1.48 christos ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c); 1186 1.35 christos for (i = 0; n-- > 0; ++i) 1187 1.35 christos re_fastputc(el, visbuf[i]); 1188 1.35 christos break; 1189 1.35 christos } 1190 1.15 lukem } 1191 1.36 christos terminal__flush(el); 1192 1.15 lukem } 1193 1.1 cgd 1194 1.1 cgd 1195 1.1 cgd /* re_clear_display(): 1196 1.8 simonb * clear the screen buffers so that new new prompt starts fresh. 1197 1.1 cgd */ 1198 1.51 christos libedit_private void 1199 1.15 lukem re_clear_display(EditLine *el) 1200 1.1 cgd { 1201 1.15 lukem int i; 1202 1.1 cgd 1203 1.15 lukem el->el_cursor.v = 0; 1204 1.15 lukem el->el_cursor.h = 0; 1205 1.36 christos for (i = 0; i < el->el_terminal.t_size.v; i++) 1206 1.15 lukem el->el_display[i][0] = '\0'; 1207 1.15 lukem el->el_refresh.r_oldcv = 0; 1208 1.15 lukem } 1209 1.1 cgd 1210 1.1 cgd 1211 1.1 cgd /* re_clear_lines(): 1212 1.8 simonb * Make sure all lines are *really* blank 1213 1.1 cgd */ 1214 1.51 christos libedit_private void 1215 1.15 lukem re_clear_lines(EditLine *el) 1216 1.1 cgd { 1217 1.15 lukem 1218 1.15 lukem if (EL_CAN_CEOL) { 1219 1.15 lukem int i; 1220 1.33 christos for (i = el->el_refresh.r_oldcv; i >= 0; i--) { 1221 1.15 lukem /* for each line on the screen */ 1222 1.36 christos terminal_move_to_line(el, i); 1223 1.36 christos terminal_move_to_char(el, 0); 1224 1.36 christos terminal_clear_EOL(el, el->el_terminal.t_size.h); 1225 1.15 lukem } 1226 1.15 lukem } else { 1227 1.36 christos terminal_move_to_line(el, el->el_refresh.r_oldcv); 1228 1.15 lukem /* go to last line */ 1229 1.36 christos terminal__putc(el, '\r'); /* go to BOL */ 1230 1.36 christos terminal__putc(el, '\n'); /* go to new line */ 1231 1.15 lukem } 1232 1.15 lukem } 1233