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