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