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