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