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