-WARNS6 cleanup (229 warnings)
[dragonfly/port-amd64.git] / lib / libedit / refresh.c
blobb207513eae59bb99daa48d61848d057bf7a77a6c
1 /*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Christos Zoulas of Cornell University.
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. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
32 * @(#)refresh.c 8.1 (Berkeley) 6/4/93
33 * $NetBSD: refresh.c,v 1.26 2003/08/07 16:44:33 agc Exp $
34 * $DragonFly: src/lib/libedit/refresh.c,v 1.5 2005/11/13 11:58:30 corecode Exp $
37 #include "config.h"
40 * refresh.c: Lower level screen refreshing functions
42 #include <stdio.h>
43 #include <ctype.h>
44 #include <unistd.h>
45 #include <string.h>
47 #include "el.h"
49 private void re_addc(EditLine *, int);
50 private void re_update_line(EditLine *, char *, char *, int);
51 private void re_insert (EditLine *, char *, int, int, char *, int);
52 private void re_delete(EditLine *, char *, int, int, int);
53 private void re_fastputc(EditLine *, int);
54 private void re__strncopy(char *, char *, size_t);
55 private void re__copy_and_pad(char *, const char *, size_t);
57 #ifdef DEBUG_REFRESH
58 private void re_printstr(EditLine *, const char *, char *, char *);
59 #define __F el->el_errfile
60 #define ELRE_ASSERT(a, b, c) do \
61 if (/*CONSTCOND*/ a) { \
62 (void) fprintf b; \
63 c; \
64 } \
65 while (/*CONSTCOND*/0)
66 #define ELRE_DEBUG(a, b) ELRE_ASSERT(a,b,;)
68 /* re_printstr():
69 * Print a string on the debugging pty
71 private void
72 re_printstr(EditLine *el, const char *str, char *f, char *t)
75 ELRE_DEBUG(1, (__F, "%s:\"", str));
76 while (f < t)
77 ELRE_DEBUG(1, (__F, "%c", *f++ & 0177));
78 ELRE_DEBUG(1, (__F, "\"\r\n"));
80 #else
81 #define ELRE_ASSERT(a, b, c)
82 #define ELRE_DEBUG(a, b)
83 #endif
86 /* re_addc():
87 * Draw c, expanding tabs, control chars etc.
89 private void
90 re_addc(EditLine *el, int c)
93 if (isprint(c)) {
94 re_putc(el, c, 1);
95 return;
97 if (c == '\n') { /* expand the newline */
98 int oldv = el->el_refresh.r_cursor.v;
99 re_putc(el, '\0', 0); /* assure end of line */
100 if (oldv == el->el_refresh.r_cursor.v) { /* XXX */
101 el->el_refresh.r_cursor.h = 0; /* reset cursor pos */
102 el->el_refresh.r_cursor.v++;
104 return;
106 if (c == '\t') { /* expand the tab */
107 for (;;) {
108 re_putc(el, ' ', 1);
109 if ((el->el_refresh.r_cursor.h & 07) == 0)
110 break; /* go until tab stop */
112 } else if (iscntrl(c)) {
113 re_putc(el, '^', 1);
114 if (c == '\177')
115 re_putc(el, '?', 1);
116 else
117 /* uncontrolify it; works only for iso8859-1 like sets */
118 re_putc(el, (c | 0100), 1);
119 } else {
120 re_putc(el, '\\', 1);
121 re_putc(el, (int) ((((unsigned int) c >> 6) & 07) + '0'), 1);
122 re_putc(el, (int) ((((unsigned int) c >> 3) & 07) + '0'), 1);
123 re_putc(el, (c & 07) + '0', 1);
128 /* re_putc():
129 * Draw the character given
131 protected void
132 re_putc(EditLine *el, int c, int shift)
135 ELRE_DEBUG(1, (__F, "printing %3.3o '%c'\r\n", c, c));
137 el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_refresh.r_cursor.h] = c;
138 if (!shift)
139 return;
141 el->el_refresh.r_cursor.h++; /* advance to next place */
142 if (el->el_refresh.r_cursor.h >= el->el_term.t_size.h) {
143 el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_term.t_size.h] = '\0';
144 /* assure end of line */
145 el->el_refresh.r_cursor.h = 0; /* reset it. */
148 * If we would overflow (input is longer than terminal size),
149 * emulate scroll by dropping first line and shuffling the rest.
150 * We do this via pointer shuffling - it's safe in this case
151 * and we avoid memcpy().
153 if (el->el_refresh.r_cursor.v + 1 >= el->el_term.t_size.v) {
154 int i, lins = el->el_term.t_size.v;
155 char *firstline = el->el_vdisplay[0];
157 for(i=1; i < lins; i++)
158 el->el_vdisplay[i-1] = el->el_vdisplay[i];
160 firstline[0] = '\0'; /* empty the string */
161 el->el_vdisplay[i-1] = firstline;
162 } else
163 el->el_refresh.r_cursor.v++;
165 ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_term.t_size.v,
166 (__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n",
167 el->el_refresh.r_cursor.v, el->el_term.t_size.v),
168 abort());
173 /* re_refresh():
174 * draws the new virtual screen image from the current input
175 * line, then goes line-by-line changing the real image to the new
176 * virtual image. The routine to re-draw a line can be replaced
177 * easily in hopes of a smarter one being placed there.
179 protected void
180 re_refresh(EditLine *el)
182 int i, rhdiff;
183 char *cp, *st;
184 coord_t cur;
185 #ifdef notyet
186 size_t termsz;
187 #endif
189 ELRE_DEBUG(1, (__F, "el->el_line.buffer = :%s:\r\n",
190 el->el_line.buffer));
192 /* reset the Drawing cursor */
193 el->el_refresh.r_cursor.h = 0;
194 el->el_refresh.r_cursor.v = 0;
196 /* temporarily draw rprompt to calculate its size */
197 prompt_print(el, EL_RPROMPT);
199 /* reset the Drawing cursor */
200 el->el_refresh.r_cursor.h = 0;
201 el->el_refresh.r_cursor.v = 0;
203 if (el->el_line.cursor >= el->el_line.lastchar) {
204 if (el->el_map.current == el->el_map.alt
205 && el->el_line.lastchar != el->el_line.buffer)
206 el->el_line.cursor = el->el_line.lastchar - 1;
207 else
208 el->el_line.cursor = el->el_line.lastchar;
211 cur.h = -1; /* set flag in case I'm not set */
212 cur.v = 0;
214 prompt_print(el, EL_PROMPT);
216 /* draw the current input buffer */
217 #if notyet
218 termsz = el->el_term.t_size.h * el->el_term.t_size.v;
219 if (el->el_line.lastchar - el->el_line.buffer > termsz) {
221 * If line is longer than terminal, process only part
222 * of line which would influence display.
224 size_t rem = (el->el_line.lastchar-el->el_line.buffer)%termsz;
226 st = el->el_line.lastchar - rem
227 - (termsz - (((rem / el->el_term.t_size.v) - 1)
228 * el->el_term.t_size.v));
229 } else
230 #endif
231 st = el->el_line.buffer;
233 for (cp = st; cp < el->el_line.lastchar; cp++) {
234 if (cp == el->el_line.cursor) {
235 /* save for later */
236 cur.h = el->el_refresh.r_cursor.h;
237 cur.v = el->el_refresh.r_cursor.v;
239 re_addc(el, (unsigned char) *cp);
242 if (cur.h == -1) { /* if I haven't been set yet, I'm at the end */
243 cur.h = el->el_refresh.r_cursor.h;
244 cur.v = el->el_refresh.r_cursor.v;
246 rhdiff = el->el_term.t_size.h - el->el_refresh.r_cursor.h -
247 el->el_rprompt.p_pos.h;
248 if (el->el_rprompt.p_pos.h && !el->el_rprompt.p_pos.v &&
249 !el->el_refresh.r_cursor.v && rhdiff > 1) {
251 * have a right-hand side prompt that will fit
252 * on the end of the first line with at least
253 * one character gap to the input buffer.
255 while (--rhdiff > 0) /* pad out with spaces */
256 re_putc(el, ' ', 1);
257 prompt_print(el, EL_RPROMPT);
258 } else {
259 el->el_rprompt.p_pos.h = 0; /* flag "not using rprompt" */
260 el->el_rprompt.p_pos.v = 0;
263 re_putc(el, '\0', 0); /* make line ended with NUL, no cursor shift */
265 el->el_refresh.r_newcv = el->el_refresh.r_cursor.v;
267 ELRE_DEBUG(1, (__F,
268 "term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n",
269 el->el_term.t_size.h, el->el_refresh.r_cursor.h,
270 el->el_refresh.r_cursor.v, el->el_vdisplay[0]));
272 ELRE_DEBUG(1, (__F, "updating %d lines.\r\n", el->el_refresh.r_newcv));
273 for (i = 0; i <= el->el_refresh.r_newcv; i++) {
274 /* NOTE THAT re_update_line MAY CHANGE el_display[i] */
275 re_update_line(el, el->el_display[i], el->el_vdisplay[i], i);
278 * Copy the new line to be the current one, and pad out with
279 * spaces to the full width of the terminal so that if we try
280 * moving the cursor by writing the character that is at the
281 * end of the screen line, it won't be a NUL or some old
282 * leftover stuff.
284 re__copy_and_pad(el->el_display[i], el->el_vdisplay[i],
285 (size_t) el->el_term.t_size.h);
287 ELRE_DEBUG(1, (__F,
288 "\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n",
289 el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i));
291 if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv)
292 for (; i <= el->el_refresh.r_oldcv; i++) {
293 term_move_to_line(el, i);
294 term_move_to_char(el, 0);
295 term_clear_EOL(el, (int) strlen(el->el_display[i]));
296 #ifdef DEBUG_REFRESH
297 term_overwrite(el, "C\b", 2);
298 #endif /* DEBUG_REFRESH */
299 el->el_display[i][0] = '\0';
302 el->el_refresh.r_oldcv = el->el_refresh.r_newcv; /* set for next time */
303 ELRE_DEBUG(1, (__F,
304 "\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n",
305 el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v,
306 cur.h, cur.v));
307 term_move_to_line(el, cur.v); /* go to where the cursor is */
308 term_move_to_char(el, cur.h);
312 /* re_goto_bottom():
313 * used to go to last used screen line
315 protected void
316 re_goto_bottom(EditLine *el)
319 term_move_to_line(el, el->el_refresh.r_oldcv);
320 term__putc('\n');
321 re_clear_display(el);
322 term__flush();
326 /* re_insert():
327 * insert num characters of s into d (in front of the character)
328 * at dat, maximum length of d is dlen
330 private void
331 /*ARGSUSED*/
332 re_insert(EditLine *el __attribute__((__unused__)),
333 char *d, int dat, int dlen, char *s, int num)
335 char *a, *b;
337 if (num <= 0)
338 return;
339 if (num > dlen - dat)
340 num = dlen - dat;
342 ELRE_DEBUG(1,
343 (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n",
344 num, dat, dlen, d));
345 ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
347 /* open up the space for num chars */
348 if (num > 0) {
349 b = d + dlen - 1;
350 a = b - num;
351 while (a >= &d[dat])
352 *b-- = *a--;
353 d[dlen] = '\0'; /* just in case */
355 ELRE_DEBUG(1, (__F,
356 "re_insert() after insert: %d at %d max %d, d == \"%s\"\n",
357 num, dat, dlen, d));
358 ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
360 /* copy the characters */
361 for (a = d + dat; (a < d + dlen) && (num > 0); num--)
362 *a++ = *s++;
364 ELRE_DEBUG(1,
365 (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n",
366 num, dat, dlen, d, s));
367 ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
371 /* re_delete():
372 * delete num characters d at dat, maximum length of d is dlen
374 private void
375 /*ARGSUSED*/
376 re_delete(EditLine *el __attribute__((__unused__)),
377 char *d, int dat, int dlen, int num)
379 char *a, *b;
381 if (num <= 0)
382 return;
383 if (dat + num >= dlen) {
384 d[dat] = '\0';
385 return;
387 ELRE_DEBUG(1,
388 (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n",
389 num, dat, dlen, d));
391 /* open up the space for num chars */
392 if (num > 0) {
393 b = d + dat;
394 a = b + num;
395 while (a < &d[dlen])
396 *b++ = *a++;
397 d[dlen] = '\0'; /* just in case */
399 ELRE_DEBUG(1,
400 (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n",
401 num, dat, dlen, d));
405 /* re__strncopy():
406 * Like strncpy without padding.
408 private void
409 re__strncopy(char *a, char *b, size_t n)
412 while (n-- && *b)
413 *a++ = *b++;
417 /*****************************************************************
418 re_update_line() is based on finding the middle difference of each line
419 on the screen; vis:
421 /old first difference
422 /beginning of line | /old last same /old EOL
423 v v v v
424 old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as
425 new: eddie> Oh, my little buggy says to me, as lurgid as
426 ^ ^ ^ ^
427 \beginning of line | \new last same \new end of line
428 \new first difference
430 all are character pointers for the sake of speed. Special cases for
431 no differences, as well as for end of line additions must be handled.
432 **************************************************************** */
434 /* Minimum at which doing an insert it "worth it". This should be about
435 * half the "cost" of going into insert mode, inserting a character, and
436 * going back out. This should really be calculated from the termcap
437 * data... For the moment, a good number for ANSI terminals.
439 #define MIN_END_KEEP 4
441 private void
442 re_update_line(EditLine *el, char *old, char *new, int i)
444 char *o, *n, *p, c;
445 char *ofd, *ols, *oe, *nfd, *nls, *ne;
446 char *osb, *ose, *nsb, *nse;
447 int fx, sx;
450 * find first diff
452 for (o = old, n = new; *o && (*o == *n); o++, n++)
453 continue;
454 ofd = o;
455 nfd = n;
458 * Find the end of both old and new
460 while (*o)
461 o++;
463 * Remove any trailing blanks off of the end, being careful not to
464 * back up past the beginning.
466 while (ofd < o) {
467 if (o[-1] != ' ')
468 break;
469 o--;
471 oe = o;
472 *oe = '\0';
474 while (*n)
475 n++;
477 /* remove blanks from end of new */
478 while (nfd < n) {
479 if (n[-1] != ' ')
480 break;
481 n--;
483 ne = n;
484 *ne = '\0';
487 * if no diff, continue to next line of redraw
489 if (*ofd == '\0' && *nfd == '\0') {
490 ELRE_DEBUG(1, (__F, "no difference.\r\n"));
491 return;
494 * find last same pointer
496 while ((o > ofd) && (n > nfd) && (*--o == *--n))
497 continue;
498 ols = ++o;
499 nls = ++n;
502 * find same begining and same end
504 osb = ols;
505 nsb = nls;
506 ose = ols;
507 nse = nls;
510 * case 1: insert: scan from nfd to nls looking for *ofd
512 if (*ofd) {
513 for (c = *ofd, n = nfd; n < nls; n++) {
514 if (c == *n) {
515 for (o = ofd, p = n;
516 p < nls && o < ols && *o == *p;
517 o++, p++)
518 continue;
520 * if the new match is longer and it's worth
521 * keeping, then we take it
523 if (((nse - nsb) < (p - n)) &&
524 (2 * (p - n) > n - nfd)) {
525 nsb = n;
526 nse = p;
527 osb = ofd;
528 ose = o;
534 * case 2: delete: scan from ofd to ols looking for *nfd
536 if (*nfd) {
537 for (c = *nfd, o = ofd; o < ols; o++) {
538 if (c == *o) {
539 for (n = nfd, p = o;
540 p < ols && n < nls && *p == *n;
541 p++, n++)
542 continue;
544 * if the new match is longer and it's worth
545 * keeping, then we take it
547 if (((ose - osb) < (p - o)) &&
548 (2 * (p - o) > o - ofd)) {
549 nsb = nfd;
550 nse = n;
551 osb = o;
552 ose = p;
558 * Pragmatics I: If old trailing whitespace or not enough characters to
559 * save to be worth it, then don't save the last same info.
561 if ((oe - ols) < MIN_END_KEEP) {
562 ols = oe;
563 nls = ne;
566 * Pragmatics II: if the terminal isn't smart enough, make the data
567 * dumber so the smart update doesn't try anything fancy
571 * fx is the number of characters we need to insert/delete: in the
572 * beginning to bring the two same begins together
574 fx = (nsb - nfd) - (osb - ofd);
576 * sx is the number of characters we need to insert/delete: in the
577 * end to bring the two same last parts together
579 sx = (nls - nse) - (ols - ose);
581 if (!EL_CAN_INSERT) {
582 if (fx > 0) {
583 osb = ols;
584 ose = ols;
585 nsb = nls;
586 nse = nls;
588 if (sx > 0) {
589 ols = oe;
590 nls = ne;
592 if ((ols - ofd) < (nls - nfd)) {
593 ols = oe;
594 nls = ne;
597 if (!EL_CAN_DELETE) {
598 if (fx < 0) {
599 osb = ols;
600 ose = ols;
601 nsb = nls;
602 nse = nls;
604 if (sx < 0) {
605 ols = oe;
606 nls = ne;
608 if ((ols - ofd) > (nls - nfd)) {
609 ols = oe;
610 nls = ne;
614 * Pragmatics III: make sure the middle shifted pointers are correct if
615 * they don't point to anything (we may have moved ols or nls).
617 /* if the change isn't worth it, don't bother */
618 /* was: if (osb == ose) */
619 if ((ose - osb) < MIN_END_KEEP) {
620 osb = ols;
621 ose = ols;
622 nsb = nls;
623 nse = nls;
626 * Now that we are done with pragmatics we recompute fx, sx
628 fx = (nsb - nfd) - (osb - ofd);
629 sx = (nls - nse) - (ols - ose);
631 ELRE_DEBUG(1, (__F, "\n"));
632 ELRE_DEBUG(1, (__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n",
633 ofd - old, osb - old, ose - old, ols - old, oe - old));
634 ELRE_DEBUG(1, (__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
635 nfd - new, nsb - new, nse - new, nls - new, ne - new));
636 ELRE_DEBUG(1, (__F,
637 "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
638 ELRE_DEBUG(1, (__F,
639 "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
640 #ifdef DEBUG_REFRESH
641 re_printstr(el, "old- oe", old, oe);
642 re_printstr(el, "new- ne", new, ne);
643 re_printstr(el, "old-ofd", old, ofd);
644 re_printstr(el, "new-nfd", new, nfd);
645 re_printstr(el, "ofd-osb", ofd, osb);
646 re_printstr(el, "nfd-nsb", nfd, nsb);
647 re_printstr(el, "osb-ose", osb, ose);
648 re_printstr(el, "nsb-nse", nsb, nse);
649 re_printstr(el, "ose-ols", ose, ols);
650 re_printstr(el, "nse-nls", nse, nls);
651 re_printstr(el, "ols- oe", ols, oe);
652 re_printstr(el, "nls- ne", nls, ne);
653 #endif /* DEBUG_REFRESH */
656 * el_cursor.v to this line i MUST be in this routine so that if we
657 * don't have to change the line, we don't move to it. el_cursor.h to
658 * first diff char
660 term_move_to_line(el, i);
663 * at this point we have something like this:
665 * /old /ofd /osb /ose /ols /oe
666 * v.....................v v..................v v........v
667 * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
668 * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
669 * ^.....................^ ^..................^ ^........^
670 * \new \nfd \nsb \nse \nls \ne
672 * fx is the difference in length between the chars between nfd and
673 * nsb, and the chars between ofd and osb, and is thus the number of
674 * characters to delete if < 0 (new is shorter than old, as above),
675 * or insert (new is longer than short).
677 * sx is the same for the second differences.
681 * if we have a net insert on the first difference, AND inserting the
682 * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
683 * character (which is ne if nls != ne, otherwise is nse) off the edge
684 * of the screen (el->el_term.t_size.h) else we do the deletes first
685 * so that we keep everything we need to.
689 * if the last same is the same like the end, there is no last same
690 * part, otherwise we want to keep the last same part set p to the
691 * last useful old character
693 p = (ols != oe) ? oe : ose;
696 * if (There is a diffence in the beginning) && (we need to insert
697 * characters) && (the number of characters to insert is less than
698 * the term width)
699 * We need to do an insert!
700 * else if (we need to delete characters)
701 * We need to delete characters!
702 * else
703 * No insert or delete
705 if ((nsb != nfd) && fx > 0 &&
706 ((p - old) + fx <= el->el_term.t_size.h)) {
707 ELRE_DEBUG(1,
708 (__F, "first diff insert at %d...\r\n", nfd - new));
710 * Move to the first char to insert, where the first diff is.
712 term_move_to_char(el, nfd - new);
714 * Check if we have stuff to keep at end
716 if (nsb != ne) {
717 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
719 * insert fx chars of new starting at nfd
721 if (fx > 0) {
722 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
723 "ERROR: cannot insert in early first diff\n"));
724 term_insertwrite(el, nfd, fx);
725 re_insert(el, old, ofd - old,
726 el->el_term.t_size.h, nfd, fx);
729 * write (nsb-nfd) - fx chars of new starting at
730 * (nfd + fx)
732 term_overwrite(el, nfd + fx, (nsb - nfd) - fx);
733 re__strncopy(ofd + fx, nfd + fx,
734 (size_t) ((nsb - nfd) - fx));
735 } else {
736 ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
737 term_overwrite(el, nfd, (nsb - nfd));
738 re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
740 * Done
742 return;
744 } else if (fx < 0) {
745 ELRE_DEBUG(1,
746 (__F, "first diff delete at %d...\r\n", ofd - old));
748 * move to the first char to delete where the first diff is
750 term_move_to_char(el, ofd - old);
752 * Check if we have stuff to save
754 if (osb != oe) {
755 ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
757 * fx is less than zero *always* here but we check
758 * for code symmetry
760 if (fx < 0) {
761 ELRE_DEBUG(!EL_CAN_DELETE, (__F,
762 "ERROR: cannot delete in first diff\n"));
763 term_deletechars(el, -fx);
764 re_delete(el, old, ofd - old,
765 el->el_term.t_size.h, -fx);
768 * write (nsb-nfd) chars of new starting at nfd
770 term_overwrite(el, nfd, (nsb - nfd));
771 re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
773 } else {
774 ELRE_DEBUG(1, (__F,
775 "but with nothing left to save\r\n"));
777 * write (nsb-nfd) chars of new starting at nfd
779 term_overwrite(el, nfd, (nsb - nfd));
780 ELRE_DEBUG(1, (__F,
781 "cleareol %d\n", (oe - old) - (ne - new)));
782 term_clear_EOL(el, (oe - old) - (ne - new));
784 * Done
786 return;
788 } else
789 fx = 0;
791 if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) {
792 ELRE_DEBUG(1, (__F,
793 "second diff delete at %d...\r\n", (ose - old) + fx));
795 * Check if we have stuff to delete
798 * fx is the number of characters inserted (+) or deleted (-)
801 term_move_to_char(el, (ose - old) + fx);
803 * Check if we have stuff to save
805 if (ols != oe) {
806 ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
808 * Again a duplicate test.
810 if (sx < 0) {
811 ELRE_DEBUG(!EL_CAN_DELETE, (__F,
812 "ERROR: cannot delete in second diff\n"));
813 term_deletechars(el, -sx);
816 * write (nls-nse) chars of new starting at nse
818 term_overwrite(el, nse, (nls - nse));
819 } else {
820 ELRE_DEBUG(1, (__F,
821 "but with nothing left to save\r\n"));
822 term_overwrite(el, nse, (nls - nse));
823 ELRE_DEBUG(1, (__F,
824 "cleareol %d\n", (oe - old) - (ne - new)));
825 if ((oe - old) - (ne - new) != 0)
826 term_clear_EOL(el, (oe - old) - (ne - new));
830 * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
832 if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
833 ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n",
834 nfd - new));
836 term_move_to_char(el, nfd - new);
838 * Check if we have stuff to keep at the end
840 if (nsb != ne) {
841 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
843 * We have to recalculate fx here because we set it
844 * to zero above as a flag saying that we hadn't done
845 * an early first insert.
847 fx = (nsb - nfd) - (osb - ofd);
848 if (fx > 0) {
850 * insert fx chars of new starting at nfd
852 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
853 "ERROR: cannot insert in late first diff\n"));
854 term_insertwrite(el, nfd, fx);
855 re_insert(el, old, ofd - old,
856 el->el_term.t_size.h, nfd, fx);
859 * write (nsb-nfd) - fx chars of new starting at
860 * (nfd + fx)
862 term_overwrite(el, nfd + fx, (nsb - nfd) - fx);
863 re__strncopy(ofd + fx, nfd + fx,
864 (size_t) ((nsb - nfd) - fx));
865 } else {
866 ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
867 term_overwrite(el, nfd, (nsb - nfd));
868 re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
872 * line is now NEW up to nse
874 if (sx >= 0) {
875 ELRE_DEBUG(1, (__F,
876 "second diff insert at %d...\r\n", nse - new));
877 term_move_to_char(el, nse - new);
878 if (ols != oe) {
879 ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
880 if (sx > 0) {
881 /* insert sx chars of new starting at nse */
882 ELRE_DEBUG(!EL_CAN_INSERT, (__F,
883 "ERROR: cannot insert in second diff\n"));
884 term_insertwrite(el, nse, sx);
887 * write (nls-nse) - sx chars of new starting at
888 * (nse + sx)
890 term_overwrite(el, nse + sx, (nls - nse) - sx);
891 } else {
892 ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
893 term_overwrite(el, nse, (nls - nse));
896 * No need to do a clear-to-end here because we were
897 * doing a second insert, so we will have over
898 * written all of the old string.
902 ELRE_DEBUG(1, (__F, "done.\r\n"));
906 /* re__copy_and_pad():
907 * Copy string and pad with spaces
909 private void
910 re__copy_and_pad(char *dst, const char *src, size_t width)
912 size_t i;
914 for (i = 0; i < width; i++) {
915 if (*src == '\0')
916 break;
917 *dst++ = *src++;
920 for (; i < width; i++)
921 *dst++ = ' ';
923 *dst = '\0';
927 /* re_refresh_cursor():
928 * Move to the new cursor position
930 protected void
931 re_refresh_cursor(EditLine *el)
933 char *cp, c;
934 int h, v, th;
936 if (el->el_line.cursor >= el->el_line.lastchar) {
937 if (el->el_map.current == el->el_map.alt
938 && el->el_line.lastchar != el->el_line.buffer)
939 el->el_line.cursor = el->el_line.lastchar - 1;
940 else
941 el->el_line.cursor = el->el_line.lastchar;
944 /* first we must find where the cursor is... */
945 h = el->el_prompt.p_pos.h;
946 v = el->el_prompt.p_pos.v;
947 th = el->el_term.t_size.h; /* optimize for speed */
949 /* do input buffer to el->el_line.cursor */
950 for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
951 c = *cp;
952 h++; /* all chars at least this long */
954 if (c == '\n') {/* handle newline in data part too */
955 h = 0;
956 v++;
957 } else {
958 if (c == '\t') { /* if a tab, to next tab stop */
959 while (h & 07) {
960 h++;
962 } else if (iscntrl((unsigned char) c)) {
963 /* if control char */
964 h++;
965 if (h > th) { /* if overflow, compensate */
966 h = 1;
967 v++;
969 } else if (!isprint((unsigned char) c)) {
970 h += 3;
971 if (h > th) { /* if overflow, compensate */
972 h = h - th;
973 v++;
978 if (h >= th) { /* check, extra long tabs picked up here also */
979 h = 0;
980 v++;
984 /* now go there */
985 term_move_to_line(el, v);
986 term_move_to_char(el, h);
987 term__flush();
991 /* re_fastputc():
992 * Add a character fast.
994 private void
995 re_fastputc(EditLine *el, int c)
998 term__putc(c);
999 el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
1000 if (el->el_cursor.h >= el->el_term.t_size.h) {
1001 /* if we must overflow */
1002 el->el_cursor.h = 0;
1005 * If we would overflow (input is longer than terminal size),
1006 * emulate scroll by dropping first line and shuffling the rest.
1007 * We do this via pointer shuffling - it's safe in this case
1008 * and we avoid memcpy().
1010 if (el->el_cursor.v + 1 >= el->el_term.t_size.v) {
1011 int i, lins = el->el_term.t_size.v;
1012 char *firstline = el->el_display[0];
1014 for(i=1; i < lins; i++)
1015 el->el_display[i-1] = el->el_display[i];
1017 re__copy_and_pad(firstline, "", 0);
1018 el->el_display[i-1] = firstline;
1019 } else {
1020 el->el_cursor.v++;
1021 el->el_refresh.r_oldcv++;
1023 if (EL_HAS_AUTO_MARGINS) {
1024 if (EL_HAS_MAGIC_MARGINS) {
1025 term__putc(' ');
1026 term__putc('\b');
1028 } else {
1029 term__putc('\r');
1030 term__putc('\n');
1036 /* re_fastaddc():
1037 * we added just one char, handle it fast.
1038 * Assumes that screen cursor == real cursor
1040 protected void
1041 re_fastaddc(EditLine *el)
1043 char c;
1044 int rhdiff;
1046 c = el->el_line.cursor[-1];
1048 if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
1049 re_refresh(el); /* too hard to handle */
1050 return;
1052 rhdiff = el->el_term.t_size.h - el->el_cursor.h -
1053 el->el_rprompt.p_pos.h;
1054 if (el->el_rprompt.p_pos.h && rhdiff < 3) {
1055 re_refresh(el); /* clear out rprompt if less than 1 char gap */
1056 return;
1057 } /* else (only do at end of line, no TAB) */
1058 if (iscntrl((unsigned char) c)) { /* if control char, do caret */
1059 char mc = (c == '\177') ? '?' : (c | 0100);
1060 re_fastputc(el, '^');
1061 re_fastputc(el, mc);
1062 } else if (isprint((unsigned char) c)) { /* normal char */
1063 re_fastputc(el, c);
1064 } else {
1065 re_fastputc(el, '\\');
1066 re_fastputc(el, (int)(((((unsigned int)c) >> 6) & 3) + '0'));
1067 re_fastputc(el, (int)(((((unsigned int)c) >> 3) & 7) + '0'));
1068 re_fastputc(el, (c & 7) + '0');
1070 term__flush();
1074 /* re_clear_display():
1075 * clear the screen buffers so that new new prompt starts fresh.
1077 protected void
1078 re_clear_display(EditLine *el)
1080 int i;
1082 el->el_cursor.v = 0;
1083 el->el_cursor.h = 0;
1084 for (i = 0; i < el->el_term.t_size.v; i++)
1085 el->el_display[i][0] = '\0';
1086 el->el_refresh.r_oldcv = 0;
1090 /* re_clear_lines():
1091 * Make sure all lines are *really* blank
1093 protected void
1094 re_clear_lines(EditLine *el)
1097 if (EL_CAN_CEOL) {
1098 int i;
1099 term_move_to_char(el, 0);
1100 for (i = 0; i <= el->el_refresh.r_oldcv; i++) {
1101 /* for each line on the screen */
1102 term_move_to_line(el, i);
1103 term_clear_EOL(el, el->el_term.t_size.h);
1105 term_move_to_line(el, 0);
1106 } else {
1107 term_move_to_line(el, el->el_refresh.r_oldcv);
1108 /* go to last line */
1109 term__putc('\r'); /* go to BOL */
1110 term__putc('\n'); /* go to new line */