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