refresh.c revision 1.30 1 /* $NetBSD: refresh.c,v 1.30 2009/03/31 17:38: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.30 2009/03/31 17:38: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_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", (size_t)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(el, '\n');
327 re_clear_display(el);
328 term__flush(el);
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 size_t len;
481
482 /*
483 * find first diff
484 */
485 for (o = old, n = new; *o && (*o == *n); o++, n++)
486 continue;
487 ofd = o;
488 nfd = n;
489
490 /*
491 * Find the end of both old and new
492 */
493 while (*o)
494 o++;
495 /*
496 * Remove any trailing blanks off of the end, being careful not to
497 * back up past the beginning.
498 */
499 while (ofd < o) {
500 if (o[-1] != ' ')
501 break;
502 o--;
503 }
504 oe = o;
505 *oe = '\0';
506
507 while (*n)
508 n++;
509
510 /* remove blanks from end of new */
511 while (nfd < n) {
512 if (n[-1] != ' ')
513 break;
514 n--;
515 }
516 ne = n;
517 *ne = '\0';
518
519 /*
520 * if no diff, continue to next line of redraw
521 */
522 if (*ofd == '\0' && *nfd == '\0') {
523 ELRE_DEBUG(1, (__F, "no difference.\r\n"));
524 return;
525 }
526 /*
527 * find last same pointer
528 */
529 while ((o > ofd) && (n > nfd) && (*--o == *--n))
530 continue;
531 ols = ++o;
532 nls = ++n;
533
534 /*
535 * find same begining and same end
536 */
537 osb = ols;
538 nsb = nls;
539 ose = ols;
540 nse = nls;
541
542 /*
543 * case 1: insert: scan from nfd to nls looking for *ofd
544 */
545 if (*ofd) {
546 for (c = *ofd, n = nfd; n < nls; n++) {
547 if (c == *n) {
548 for (o = ofd, p = n;
549 p < nls && o < ols && *o == *p;
550 o++, p++)
551 continue;
552 /*
553 * if the new match is longer and it's worth
554 * keeping, then we take it
555 */
556 if (((nse - nsb) < (p - n)) &&
557 (2 * (p - n) > n - nfd)) {
558 nsb = n;
559 nse = p;
560 osb = ofd;
561 ose = o;
562 }
563 }
564 }
565 }
566 /*
567 * case 2: delete: scan from ofd to ols looking for *nfd
568 */
569 if (*nfd) {
570 for (c = *nfd, o = ofd; o < ols; o++) {
571 if (c == *o) {
572 for (n = nfd, p = o;
573 p < ols && n < nls && *p == *n;
574 p++, n++)
575 continue;
576 /*
577 * if the new match is longer and it's worth
578 * keeping, then we take it
579 */
580 if (((ose - osb) < (p - o)) &&
581 (2 * (p - o) > o - ofd)) {
582 nsb = nfd;
583 nse = n;
584 osb = o;
585 ose = p;
586 }
587 }
588 }
589 }
590 /*
591 * Pragmatics I: If old trailing whitespace or not enough characters to
592 * save to be worth it, then don't save the last same info.
593 */
594 if ((oe - ols) < MIN_END_KEEP) {
595 ols = oe;
596 nls = ne;
597 }
598 /*
599 * Pragmatics II: if the terminal isn't smart enough, make the data
600 * dumber so the smart update doesn't try anything fancy
601 */
602
603 /*
604 * fx is the number of characters we need to insert/delete: in the
605 * beginning to bring the two same begins together
606 */
607 fx = (int)((nsb - nfd) - (osb - ofd));
608 /*
609 * sx is the number of characters we need to insert/delete: in the
610 * end to bring the two same last parts together
611 */
612 sx = (int)((nls - nse) - (ols - ose));
613
614 if (!EL_CAN_INSERT) {
615 if (fx > 0) {
616 osb = ols;
617 ose = ols;
618 nsb = nls;
619 nse = nls;
620 }
621 if (sx > 0) {
622 ols = oe;
623 nls = ne;
624 }
625 if ((ols - ofd) < (nls - nfd)) {
626 ols = oe;
627 nls = ne;
628 }
629 }
630 if (!EL_CAN_DELETE) {
631 if (fx < 0) {
632 osb = ols;
633 ose = ols;
634 nsb = nls;
635 nse = nls;
636 }
637 if (sx < 0) {
638 ols = oe;
639 nls = ne;
640 }
641 if ((ols - ofd) > (nls - nfd)) {
642 ols = oe;
643 nls = ne;
644 }
645 }
646 /*
647 * Pragmatics III: make sure the middle shifted pointers are correct if
648 * they don't point to anything (we may have moved ols or nls).
649 */
650 /* if the change isn't worth it, don't bother */
651 /* was: if (osb == ose) */
652 if ((ose - osb) < MIN_END_KEEP) {
653 osb = ols;
654 ose = ols;
655 nsb = nls;
656 nse = nls;
657 }
658 /*
659 * Now that we are done with pragmatics we recompute fx, sx
660 */
661 fx = (int)((nsb - nfd) - (osb - ofd));
662 sx = (int)((nls - nse) - (ols - ose));
663
664 ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx));
665 ELRE_DEBUG(1, (__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n",
666 ofd - old, osb - old, ose - old, ols - old, oe - old));
667 ELRE_DEBUG(1, (__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
668 nfd - new, nsb - new, nse - new, nls - new, ne - new));
669 ELRE_DEBUG(1, (__F,
670 "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
671 ELRE_DEBUG(1, (__F,
672 "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
673 #ifdef DEBUG_REFRESH
674 re_printstr(el, "old- oe", old, oe);
675 re_printstr(el, "new- ne", new, ne);
676 re_printstr(el, "old-ofd", old, ofd);
677 re_printstr(el, "new-nfd", new, nfd);
678 re_printstr(el, "ofd-osb", ofd, osb);
679 re_printstr(el, "nfd-nsb", nfd, nsb);
680 re_printstr(el, "osb-ose", osb, ose);
681 re_printstr(el, "nsb-nse", nsb, nse);
682 re_printstr(el, "ose-ols", ose, ols);
683 re_printstr(el, "nse-nls", nse, nls);
684 re_printstr(el, "ols- oe", ols, oe);
685 re_printstr(el, "nls- ne", nls, ne);
686 #endif /* DEBUG_REFRESH */
687
688 /*
689 * el_cursor.v to this line i MUST be in this routine so that if we
690 * don't have to change the line, we don't move to it. el_cursor.h to
691 * first diff char
692 */
693 term_move_to_line(el, i);
694
695 /*
696 * at this point we have something like this:
697 *
698 * /old /ofd /osb /ose /ols /oe
699 * v.....................v v..................v v........v
700 * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
701 * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
702 * ^.....................^ ^..................^ ^........^
703 * \new \nfd \nsb \nse \nls \ne
704 *
705 * fx is the difference in length between the chars between nfd and
706 * nsb, and the chars between ofd and osb, and is thus the number of
707 * characters to delete if < 0 (new is shorter than old, as above),
708 * or insert (new is longer than short).
709 *
710 * sx is the same for the second differences.
711 */
712
713 /*
714 * if we have a net insert on the first difference, AND inserting the
715 * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
716 * character (which is ne if nls != ne, otherwise is nse) off the edge
717 * of the screen (el->el_term.t_size.h) else we do the deletes first
718 * so that we keep everything we need to.
719 */
720
721 /*
722 * if the last same is the same like the end, there is no last same
723 * part, otherwise we want to keep the last same part set p to the
724 * last useful old character
725 */
726 p = (ols != oe) ? oe : ose;
727
728 /*
729 * if (There is a diffence in the beginning) && (we need to insert
730 * characters) && (the number of characters to insert is less than
731 * the term width)
732 * We need to do an insert!
733 * else if (we need to delete characters)
734 * We need to delete characters!
735 * else
736 * No insert or delete
737 */
738 if ((nsb != nfd) && fx > 0 &&
739 ((p - old) + fx <= el->el_term.t_size.h)) {
740 ELRE_DEBUG(1,
741 (__F, "first diff insert at %d...\r\n", nfd - new));
742 /*
743 * Move to the first char to insert, where the first diff is.
744 */
745 term_move_to_char(el, (int)(nfd - new));
746 /*
747 * Check if we have stuff to keep at end
748 */
749 if (nsb != ne) {
750 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
751 /*
752 * insert fx chars of new starting at nfd
753 */
754 if (fx > 0) {
755 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
756 "ERROR: cannot insert in early first diff\n"));
757 term_insertwrite(el, nfd, fx);
758 re_insert(el, old, (int)(ofd - old),
759 el->el_term.t_size.h, nfd, fx);
760 }
761 /*
762 * write (nsb-nfd) - fx chars of new starting at
763 * (nfd + fx)
764 */
765 len = (size_t) ((nsb - nfd) - fx);
766 term_overwrite(el, (nfd + fx), len);
767 re__strncopy(ofd + fx, nfd + fx, len);
768 } else {
769 ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
770 len = (size_t)(nsb - nfd);
771 term_overwrite(el, nfd, len);
772 re__strncopy(ofd, nfd, len);
773 /*
774 * Done
775 */
776 return;
777 }
778 } else if (fx < 0) {
779 ELRE_DEBUG(1,
780 (__F, "first diff delete at %d...\r\n", ofd - old));
781 /*
782 * move to the first char to delete where the first diff is
783 */
784 term_move_to_char(el, (int)(ofd - old));
785 /*
786 * Check if we have stuff to save
787 */
788 if (osb != oe) {
789 ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
790 /*
791 * fx is less than zero *always* here but we check
792 * for code symmetry
793 */
794 if (fx < 0) {
795 ELRE_DEBUG(!EL_CAN_DELETE, (__F,
796 "ERROR: cannot delete in first diff\n"));
797 term_deletechars(el, -fx);
798 re_delete(el, old, (int)(ofd - old),
799 el->el_term.t_size.h, -fx);
800 }
801 /*
802 * write (nsb-nfd) chars of new starting at nfd
803 */
804 len = (size_t) (nsb - nfd);
805 term_overwrite(el, nfd, len);
806 re__strncopy(ofd, nfd, len);
807
808 } else {
809 ELRE_DEBUG(1, (__F,
810 "but with nothing left to save\r\n"));
811 /*
812 * write (nsb-nfd) chars of new starting at nfd
813 */
814 term_overwrite(el, nfd, (size_t)(nsb - nfd));
815 re_clear_eol(el, fx, sx,
816 (int)((oe - old) - (ne - new)));
817 /*
818 * Done
819 */
820 return;
821 }
822 } else
823 fx = 0;
824
825 if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) {
826 ELRE_DEBUG(1, (__F,
827 "second diff delete at %d...\r\n", (ose - old) + fx));
828 /*
829 * Check if we have stuff to delete
830 */
831 /*
832 * fx is the number of characters inserted (+) or deleted (-)
833 */
834
835 term_move_to_char(el, (int)((ose - old) + fx));
836 /*
837 * Check if we have stuff to save
838 */
839 if (ols != oe) {
840 ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
841 /*
842 * Again a duplicate test.
843 */
844 if (sx < 0) {
845 ELRE_DEBUG(!EL_CAN_DELETE, (__F,
846 "ERROR: cannot delete in second diff\n"));
847 term_deletechars(el, -sx);
848 }
849 /*
850 * write (nls-nse) chars of new starting at nse
851 */
852 term_overwrite(el, nse, (size_t)(nls - nse));
853 } else {
854 ELRE_DEBUG(1, (__F,
855 "but with nothing left to save\r\n"));
856 term_overwrite(el, nse, (size_t)(nls - nse));
857 re_clear_eol(el, fx, sx,
858 (int)((oe - old) - (ne - new)));
859 }
860 }
861 /*
862 * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
863 */
864 if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
865 ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n",
866 nfd - new));
867
868 term_move_to_char(el, (int)(nfd - new));
869 /*
870 * Check if we have stuff to keep at the end
871 */
872 if (nsb != ne) {
873 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
874 /*
875 * We have to recalculate fx here because we set it
876 * to zero above as a flag saying that we hadn't done
877 * an early first insert.
878 */
879 fx = (int)((nsb - nfd) - (osb - ofd));
880 if (fx > 0) {
881 /*
882 * insert fx chars of new starting at nfd
883 */
884 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
885 "ERROR: cannot insert in late first diff\n"));
886 term_insertwrite(el, nfd, fx);
887 re_insert(el, old, (int)(ofd - old),
888 el->el_term.t_size.h, nfd, fx);
889 }
890 /*
891 * write (nsb-nfd) - fx chars of new starting at
892 * (nfd + fx)
893 */
894 len = (size_t) ((nsb - nfd) - fx);
895 term_overwrite(el, (nfd + fx), len);
896 re__strncopy(ofd + fx, nfd + fx, len);
897 } else {
898 ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
899 len = (size_t) (nsb - nfd);
900 term_overwrite(el, nfd, len);
901 re__strncopy(ofd, nfd, len);
902 }
903 }
904 /*
905 * line is now NEW up to nse
906 */
907 if (sx >= 0) {
908 ELRE_DEBUG(1, (__F,
909 "second diff insert at %d...\r\n", (int)(nse - new)));
910 term_move_to_char(el, (int)(nse - new));
911 if (ols != oe) {
912 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
913 if (sx > 0) {
914 /* insert sx chars of new starting at nse */
915 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
916 "ERROR: cannot insert in second diff\n"));
917 term_insertwrite(el, nse, sx);
918 }
919 /*
920 * write (nls-nse) - sx chars of new starting at
921 * (nse + sx)
922 */
923 term_overwrite(el, (nse + sx),
924 (size_t)((nls - nse) - sx));
925 } else {
926 ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
927 term_overwrite(el, nse, (size_t)(nls - nse));
928
929 /*
930 * No need to do a clear-to-end here because we were
931 * doing a second insert, so we will have over
932 * written all of the old string.
933 */
934 }
935 }
936 ELRE_DEBUG(1, (__F, "done.\r\n"));
937 }
938
939
940 /* re__copy_and_pad():
941 * Copy string and pad with spaces
942 */
943 private void
944 re__copy_and_pad(char *dst, const char *src, size_t width)
945 {
946 size_t i;
947
948 for (i = 0; i < width; i++) {
949 if (*src == '\0')
950 break;
951 *dst++ = *src++;
952 }
953
954 for (; i < width; i++)
955 *dst++ = ' ';
956
957 *dst = '\0';
958 }
959
960
961 /* re_refresh_cursor():
962 * Move to the new cursor position
963 */
964 protected void
965 re_refresh_cursor(EditLine *el)
966 {
967 char *cp, c;
968 int h, v, th;
969
970 if (el->el_line.cursor >= el->el_line.lastchar) {
971 if (el->el_map.current == el->el_map.alt
972 && el->el_line.lastchar != el->el_line.buffer)
973 el->el_line.cursor = el->el_line.lastchar - 1;
974 else
975 el->el_line.cursor = el->el_line.lastchar;
976 }
977
978 /* first we must find where the cursor is... */
979 h = el->el_prompt.p_pos.h;
980 v = el->el_prompt.p_pos.v;
981 th = el->el_term.t_size.h; /* optimize for speed */
982
983 /* do input buffer to el->el_line.cursor */
984 for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
985 c = *cp;
986 h++; /* all chars at least this long */
987
988 if (c == '\n') {/* handle newline in data part too */
989 h = 0;
990 v++;
991 } else {
992 if (c == '\t') { /* if a tab, to next tab stop */
993 while (h & 07) {
994 h++;
995 }
996 } else if (iscntrl((unsigned char) c)) {
997 /* if control char */
998 h++;
999 if (h > th) { /* if overflow, compensate */
1000 h = 1;
1001 v++;
1002 }
1003 } else if (!isprint((unsigned char) c)) {
1004 h += 3;
1005 if (h > th) { /* if overflow, compensate */
1006 h = h - th;
1007 v++;
1008 }
1009 }
1010 }
1011
1012 if (h >= th) { /* check, extra long tabs picked up here also */
1013 h = 0;
1014 v++;
1015 }
1016 }
1017
1018 /* now go there */
1019 term_move_to_line(el, v);
1020 term_move_to_char(el, h);
1021 term__flush(el);
1022 }
1023
1024
1025 /* re_fastputc():
1026 * Add a character fast.
1027 */
1028 private void
1029 re_fastputc(EditLine *el, int c)
1030 {
1031
1032 term__putc(el, c);
1033 el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
1034 if (el->el_cursor.h >= el->el_term.t_size.h) {
1035 /* if we must overflow */
1036 el->el_cursor.h = 0;
1037
1038 /*
1039 * If we would overflow (input is longer than terminal size),
1040 * emulate scroll by dropping first line and shuffling the rest.
1041 * We do this via pointer shuffling - it's safe in this case
1042 * and we avoid memcpy().
1043 */
1044 if (el->el_cursor.v + 1 >= el->el_term.t_size.v) {
1045 int i, lins = el->el_term.t_size.v;
1046 char *firstline = el->el_display[0];
1047
1048 for(i=1; i < lins; i++)
1049 el->el_display[i-1] = el->el_display[i];
1050
1051 re__copy_and_pad(firstline, "", 0);
1052 el->el_display[i-1] = firstline;
1053 } else {
1054 el->el_cursor.v++;
1055 el->el_refresh.r_oldcv++;
1056 }
1057 if (EL_HAS_AUTO_MARGINS) {
1058 if (EL_HAS_MAGIC_MARGINS) {
1059 term__putc(el, ' ');
1060 term__putc(el, '\b');
1061 }
1062 } else {
1063 term__putc(el, '\r');
1064 term__putc(el, '\n');
1065 }
1066 }
1067 }
1068
1069
1070 /* re_fastaddc():
1071 * we added just one char, handle it fast.
1072 * Assumes that screen cursor == real cursor
1073 */
1074 protected void
1075 re_fastaddc(EditLine *el)
1076 {
1077 char c;
1078 int rhdiff;
1079
1080 c = el->el_line.cursor[-1];
1081
1082 if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
1083 re_refresh(el); /* too hard to handle */
1084 return;
1085 }
1086 rhdiff = el->el_term.t_size.h - el->el_cursor.h -
1087 el->el_rprompt.p_pos.h;
1088 if (el->el_rprompt.p_pos.h && rhdiff < 3) {
1089 re_refresh(el); /* clear out rprompt if less than 1 char gap */
1090 return;
1091 } /* else (only do at end of line, no TAB) */
1092 if (iscntrl((unsigned char) c)) { /* if control char, do caret */
1093 char mc = (c == '\177') ? '?' : (c | 0100);
1094 re_fastputc(el, '^');
1095 re_fastputc(el, mc);
1096 } else if (isprint((unsigned char) c)) { /* normal char */
1097 re_fastputc(el, c);
1098 } else {
1099 re_fastputc(el, '\\');
1100 re_fastputc(el, (int)(((((unsigned int)c) >> 6) & 3) + '0'));
1101 re_fastputc(el, (int)(((((unsigned int)c) >> 3) & 7) + '0'));
1102 re_fastputc(el, (c & 7) + '0');
1103 }
1104 term__flush(el);
1105 }
1106
1107
1108 /* re_clear_display():
1109 * clear the screen buffers so that new new prompt starts fresh.
1110 */
1111 protected void
1112 re_clear_display(EditLine *el)
1113 {
1114 int i;
1115
1116 el->el_cursor.v = 0;
1117 el->el_cursor.h = 0;
1118 for (i = 0; i < el->el_term.t_size.v; i++)
1119 el->el_display[i][0] = '\0';
1120 el->el_refresh.r_oldcv = 0;
1121 }
1122
1123
1124 /* re_clear_lines():
1125 * Make sure all lines are *really* blank
1126 */
1127 protected void
1128 re_clear_lines(EditLine *el)
1129 {
1130
1131 if (EL_CAN_CEOL) {
1132 int i;
1133 term_move_to_char(el, 0);
1134 for (i = 0; i <= el->el_refresh.r_oldcv; i++) {
1135 /* for each line on the screen */
1136 term_move_to_line(el, i);
1137 term_clear_EOL(el, el->el_term.t_size.h);
1138 }
1139 term_move_to_line(el, 0);
1140 } else {
1141 term_move_to_line(el, el->el_refresh.r_oldcv);
1142 /* go to last line */
1143 term__putc(el, '\r'); /* go to BOL */
1144 term__putc(el, '\n'); /* go to new line */
1145 }
1146 }
1147