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