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