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