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