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