PageRenderTime 91ms CodeModel.GetById 27ms app.highlight 53ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/tcsh/ed.refresh.c

https://bitbucket.org/freebsd/freebsd-head/
C | 1330 lines | 935 code | 97 blank | 298 comment | 231 complexity | a83bf12dc0987e34d6f90bddb3ec40ad MD5 | raw file
   1/* $Header: /p/tcsh/cvsroot/tcsh/ed.refresh.c,v 3.47 2011/02/27 00:14:51 christos Exp $ */
   2/*
   3 * ed.refresh.c: Lower level screen refreshing functions
   4 */
   5/*-
   6 * Copyright (c) 1980, 1991 The Regents of the University of California.
   7 * All rights reserved.
   8 *
   9 * Redistribution and use in source and binary forms, with or without
  10 * modification, are permitted provided that the following conditions
  11 * are met:
  12 * 1. Redistributions of source code must retain the above copyright
  13 *    notice, this list of conditions and the following disclaimer.
  14 * 2. Redistributions in binary form must reproduce the above copyright
  15 *    notice, this list of conditions and the following disclaimer in the
  16 *    documentation and/or other materials provided with the distribution.
  17 * 3. Neither the name of the University nor the names of its contributors
  18 *    may be used to endorse or promote products derived from this software
  19 *    without specific prior written permission.
  20 *
  21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31 * SUCH DAMAGE.
  32 */
  33#include "sh.h"
  34
  35RCSID("$tcsh: ed.refresh.c,v 3.47 2011/02/27 00:14:51 christos Exp $")
  36
  37#include "ed.h"
  38/* #define DEBUG_UPDATE */
  39/* #define DEBUG_REFRESH */
  40/* #define DEBUG_LITERAL */
  41
  42/* refresh.c -- refresh the current set of lines on the screen */
  43
  44Char   *litptr;
  45static int vcursor_h, vcursor_v;
  46static int rprompt_h, rprompt_v;
  47
  48static	int	MakeLiteral		(Char *, int, Char);
  49static	int	Draw 			(Char *, int);
  50static	void	Vdraw 			(Char, int);
  51static	void	RefreshPromptpart	(Char *);
  52static	void	update_line 		(Char *, Char *, int);
  53static	void	str_insert		(Char *, int, int, Char *, int);
  54static	void	str_delete		(Char *, int, int, int);
  55static	void	str_cp			(Char *, Char *, int);
  56#ifndef WINNT_NATIVE
  57static
  58#else
  59extern
  60#endif
  61	void    PutPlusOne      (Char, int);
  62static	void	cpy_pad_spaces		(Char *, Char *, int);
  63#if defined(DEBUG_UPDATE) || defined(DEBUG_REFRESH) || defined(DEBUG_LITERAL)
  64static	void	reprintf			(char *, ...);
  65#ifdef DEBUG_UPDATE
  66static	void	dprintstr		(char *, const Char *, const Char *);
  67
  68static void
  69dprintstr(char *str, const Char *f, const Char *t)
  70{
  71    reprintf("%s:\"", str);
  72    while (f < t) {
  73	if (ASC(*f) & ~ASCII)
  74	  reprintf("[%x]", *f++);
  75	else
  76	  reprintf("%c", CTL_ESC(ASCII & ASC(*f++)));
  77    }
  78    reprintf("\"\r\n");
  79}
  80#endif /* DEBUG_UPDATE */
  81
  82/* reprintf():
  83 *	Print to $DEBUGTTY, so that we can test editing on one pty, and 
  84 *      print debugging stuff on another. Don't interrupt the shell while
  85 *	debugging cause you'll mangle up the file descriptors!
  86 */
  87static void
  88reprintf(char *fmt, ...)
  89{
  90    static int fd = -1;
  91    char *dtty;
  92
  93    if ((dtty = getenv("DEBUGTTY"))) {
  94	int o;
  95	va_list va;
  96	va_start(va, fmt);
  97
  98	if (fd == -1)
  99	    fd = xopen(dtty, O_RDWR);
 100	o = SHOUT;
 101	flush();
 102	SHOUT = fd;
 103	xvprintf(fmt, va);
 104	va_end(va);
 105	flush();
 106	SHOUT = o;
 107    }
 108}
 109#endif  /* DEBUG_UPDATE || DEBUG_REFRESH || DEBUG_LITERAL */
 110
 111static int litlen = 0, litalloc = 0;
 112
 113static int MakeLiteral(Char *str, int len, Char addlit)
 114{
 115    int i, addlitlen = 0;
 116    Char *addlitptr = 0;
 117    if (addlit) {
 118	if ((addlit & LITERAL) != 0) {
 119	    addlitptr = litptr + (addlit & ~LITERAL) * LIT_FACTOR;
 120	    addlitlen = Strlen(addlitptr);
 121	} else {
 122	    addlitptr = &addlit;
 123	    addlitlen = 1;
 124	}
 125	for (i = 0; i < litlen; i += LIT_FACTOR)
 126	    if (!Strncmp(addlitptr, litptr + i, addlitlen) && !Strncmp(str, litptr + i + addlitlen, len) && litptr[i + addlitlen + len] == 0)
 127		return (i / LIT_FACTOR) | LITERAL;
 128    } else {
 129	addlitlen = 0;
 130	for (i = 0; i < litlen; i += LIT_FACTOR)
 131	    if (!Strncmp(str, litptr + i, len) && litptr[i + len] == 0)
 132		return (i / LIT_FACTOR) | LITERAL;
 133    }
 134    if (litlen + addlitlen + len + 1 + (LIT_FACTOR - 1) > litalloc) {
 135	Char *newlitptr;
 136	int add = 256;
 137	while (len + addlitlen + 1 + (LIT_FACTOR - 1) > add)
 138	    add *= 2;
 139	newlitptr = xrealloc(litptr, (litalloc + add) * sizeof(Char));
 140	if (!newlitptr)
 141	    return '?';
 142	litptr = newlitptr;
 143	litalloc += add;
 144	if (addlitptr && addlitptr != &addlit)
 145	    addlitptr = litptr + (addlit & ~LITERAL) * LIT_FACTOR;
 146    }
 147    i = litlen / LIT_FACTOR;
 148    if (i >= LITERAL || i == CHAR_DBWIDTH)
 149	return '?';
 150    if (addlitptr) {
 151	Strncpy(litptr + litlen, addlitptr, addlitlen);
 152	litlen += addlitlen;
 153    }
 154    Strncpy(litptr + litlen, str, len);
 155    litlen += len;
 156    do
 157	litptr[litlen++] = 0;
 158    while (litlen % LIT_FACTOR);
 159    return i | LITERAL;
 160}
 161
 162static int
 163Draw(Char *cp, int nocomb)	/* draw char at cp, expand tabs, ctl chars */
 164{
 165    int w, i, lv, lh;
 166    Char c, attr;
 167
 168    attr = *cp & ~CHAR;
 169    c = *cp & CHAR;
 170    w = NLSClassify(c, nocomb);
 171    switch (w) {
 172	case NLSCLASS_NL:
 173	    Vdraw('\0', 0);		/* assure end of line	 */
 174	    vcursor_h = 0;		/* reset cursor pos	 */
 175	    vcursor_v++;
 176	    break;
 177	case NLSCLASS_TAB:
 178	    do {
 179		Vdraw(' ', 1);
 180	    } while ((vcursor_h & 07) != 0);
 181	    break;
 182	case NLSCLASS_CTRL:
 183	    Vdraw('^' | attr, 1);
 184	    if (c == CTL_ESC('\177')) {
 185		Vdraw('?' | attr, 1);
 186	    } else {
 187#ifdef IS_ASCII
 188		/* uncontrolify it; works only for iso8859-1 like sets */
 189		Vdraw(c | 0100 | attr, 1);
 190#else
 191		Vdraw(_toebcdic[_toascii[c]|0100] | attr, 1);
 192#endif
 193	    }
 194	    break;
 195	case NLSCLASS_ILLEGAL:
 196	    Vdraw('\\' | attr, 1);
 197	    Vdraw((((c >> 6) & 7) + '0') | attr, 1);
 198	    Vdraw((((c >> 3) & 7) + '0') | attr, 1);
 199	    Vdraw(((c & 7) + '0') | attr, 1);
 200	    break;
 201	case NLSCLASS_ILLEGAL2:
 202	case NLSCLASS_ILLEGAL3:
 203	case NLSCLASS_ILLEGAL4:
 204	    Vdraw('\\' | attr, 1);
 205	    Vdraw('U' | attr, 1);
 206	    Vdraw('+' | attr, 1);
 207	    for (i = 8 * NLSCLASS_ILLEGAL_SIZE(w) - 4; i >= 0; i -= 4)
 208		Vdraw("0123456789ABCDEF"[(c >> i) & 15] | attr, 1);
 209	    break;
 210	case 0:
 211	    lv = vcursor_v;
 212	    lh = vcursor_h;
 213	    for (;;) {
 214		lh--;
 215		if (lh < 0) {
 216		    lv--;
 217		    if (lv < 0)
 218			break;
 219		    lh = Strlen(Vdisplay[lv]) - 1;
 220		}
 221		if (Vdisplay[lv][lh] != CHAR_DBWIDTH)
 222		    break;
 223	    }
 224	    if (lv < 0) {
 225		Vdraw('\\' | attr, 1);
 226		Vdraw((((c >> 6) & 7) + '0') | attr, 1);
 227		Vdraw((((c >> 3) & 7) + '0') | attr, 1);
 228		Vdraw(((c & 7) + '0') | attr, 1);
 229		break;
 230	    }
 231	    Vdisplay[lv][lh] = MakeLiteral(cp, 1, Vdisplay[lv][lh]);
 232	    break;
 233	default:
 234	    Vdraw(*cp, w);
 235	    break;
 236    }
 237    return 1;
 238}
 239
 240static void
 241Vdraw(Char c, int width)	/* draw char c onto V lines */
 242{
 243#ifdef DEBUG_REFRESH
 244# ifdef SHORT_STRINGS
 245    reprintf("Vdrawing %6.6o '%c' %d\r\n", (unsigned)c, (int)(c & ASCII), width);
 246# else
 247    reprintf("Vdrawing %3.3o '%c' %d\r\n", (unsigned)c, (int)c, width);
 248# endif /* SHORT_STRNGS */
 249#endif  /* DEBUG_REFRESH */
 250
 251    /* Hopefully this is what all the terminals do with multi-column characters
 252       that "span line breaks". */
 253    while (vcursor_h + width > TermH)
 254	Vdraw(' ', 1);
 255    Vdisplay[vcursor_v][vcursor_h] = c;
 256    if (width)
 257	vcursor_h++;		/* advance to next place */
 258    while (--width > 0)
 259	Vdisplay[vcursor_v][vcursor_h++] = CHAR_DBWIDTH;
 260    if (vcursor_h >= TermH) {
 261	Vdisplay[vcursor_v][TermH] = '\0';	/* assure end of line */
 262	vcursor_h = 0;		/* reset it. */
 263	vcursor_v++;
 264#ifdef DEBUG_REFRESH
 265	if (vcursor_v >= TermV) {	/* should NEVER happen. */
 266	    reprintf("\r\nVdraw: vcursor_v overflow! Vcursor_v == %d > %d\r\n",
 267		    vcursor_v, TermV);
 268	    abort();
 269	}
 270#endif /* DEBUG_REFRESH */
 271    }
 272}
 273
 274/*
 275 *  RefreshPromptpart()
 276 *	draws a prompt element, expanding literals (we know it's ASCIZ)
 277 */
 278static void
 279RefreshPromptpart(Char *buf)
 280{
 281    Char *cp;
 282    int w;
 283
 284    if (buf == NULL)
 285	return;
 286    for (cp = buf; *cp; ) {
 287	if (*cp & LITERAL) {
 288	    Char *litstart = cp;
 289	    while (*cp & LITERAL)
 290		cp++;
 291	    if (*cp) {
 292		w = NLSWidth(*cp & CHAR);
 293		Vdraw(MakeLiteral(litstart, cp + 1 - litstart, 0), w);
 294		cp++;
 295	    }
 296	    else {
 297		/*
 298		 * XXX: This is a bug, we lose the last literal, if it is not
 299		 * followed by a normal character, but it is too hard to fix
 300		 */
 301		break;
 302	    }
 303	}
 304	else
 305	    cp += Draw(cp, cp == buf);
 306    }
 307}
 308
 309/*
 310 *  Refresh()
 311 *	draws the new virtual screen image from the current input
 312 *  	line, then goes line-by-line changing the real image to the new
 313 *	virtual image. The routine to re-draw a line can be replaced
 314 *	easily in hopes of a smarter one being placed there.
 315 */
 316#ifndef WINNT_NATIVE
 317static
 318#endif
 319int OldvcV = 0;
 320
 321void
 322Refresh(void)
 323{
 324    int cur_line;
 325    Char *cp;
 326    int     cur_h, cur_v = 0, new_vcv;
 327    int     rhdiff;
 328    Char    oldgetting;
 329
 330#ifdef DEBUG_REFRESH
 331    reprintf("Prompt = :%s:\r\n", short2str(Prompt));
 332    reprintf("InputBuf = :%s:\r\n", short2str(InputBuf));
 333#endif /* DEBUG_REFRESH */
 334    oldgetting = GettingInput;
 335    GettingInput = 0;		/* avoid re-entrance via SIGWINCH */
 336
 337    /* reset the Vdraw cursor, temporarily draw rprompt to calculate its size */
 338    vcursor_h = 0;
 339    vcursor_v = 0;
 340    RefreshPromptpart(RPrompt);
 341    rprompt_h = vcursor_h;
 342    rprompt_v = vcursor_v;
 343
 344    /* reset the Vdraw cursor, draw prompt */
 345    vcursor_h = 0;
 346    vcursor_v = 0;
 347    RefreshPromptpart(Prompt);
 348    cur_h = -1;			/* set flag in case I'm not set */
 349
 350    /* draw the current input buffer */
 351    for (cp = InputBuf; (cp < LastChar); ) {
 352	if (cp >= Cursor && cur_h == -1) {
 353	    cur_h = vcursor_h;	/* save for later */
 354	    cur_v = vcursor_v;
 355	    Cursor = cp;
 356	}
 357	cp += Draw(cp, cp == InputBuf);
 358    }
 359
 360    if (cur_h == -1) {		/* if I haven't been set yet, I'm at the end */
 361	cur_h = vcursor_h;
 362	cur_v = vcursor_v;
 363    }
 364
 365    rhdiff = TermH - vcursor_h - rprompt_h;
 366    if (rprompt_h != 0 && rprompt_v == 0 && vcursor_v == 0 && rhdiff > 1) {
 367			/*
 368			 * have a right-hand side prompt that will fit on
 369			 * the end of the first line with at least one
 370			 * character gap to the input buffer.
 371			 */
 372	while (--rhdiff > 0)		/* pad out with spaces */
 373	    Vdraw(' ', 1);
 374	RefreshPromptpart(RPrompt);
 375    }
 376    else {
 377	rprompt_h = 0;			/* flag "not using rprompt" */
 378	rprompt_v = 0;
 379    }
 380
 381    new_vcv = vcursor_v;	/* must be done BEFORE the NUL is written */
 382    Vdraw('\0', 1);		/* put NUL on end */
 383
 384#if defined (DEBUG_REFRESH)
 385    reprintf("TermH=%d, vcur_h=%d, vcur_v=%d, Vdisplay[0]=\r\n:%80.80s:\r\n",
 386	    TermH, vcursor_h, vcursor_v, short2str(Vdisplay[0]));
 387#endif /* DEBUG_REFRESH */
 388
 389#ifdef DEBUG_UPDATE
 390    reprintf("updating %d lines.\r\n", new_vcv);
 391#endif  /* DEBUG_UPDATE */
 392    for (cur_line = 0; cur_line <= new_vcv; cur_line++) {
 393	/* NOTE THAT update_line MAY CHANGE Display[cur_line] */
 394	update_line(Display[cur_line], Vdisplay[cur_line], cur_line);
 395#ifdef WINNT_NATIVE
 396	flush();
 397#endif /* WINNT_NATIVE */
 398
 399	/*
 400	 * Copy the new line to be the current one, and pad out with spaces
 401	 * to the full width of the terminal so that if we try moving the
 402	 * cursor by writing the character that is at the end of the
 403	 * screen line, it won't be a NUL or some old leftover stuff.
 404	 */
 405	cpy_pad_spaces(Display[cur_line], Vdisplay[cur_line], TermH);
 406    }
 407#ifdef DEBUG_REFRESH
 408    reprintf("\r\nvcursor_v = %d, OldvcV = %d, cur_line = %d\r\n",
 409	    vcursor_v, OldvcV, cur_line);
 410#endif /* DEBUG_REFRESH */
 411    if (OldvcV > new_vcv) {
 412	for (; cur_line <= OldvcV; cur_line++) {
 413	    update_line(Display[cur_line], STRNULL, cur_line);
 414	    *Display[cur_line] = '\0';
 415	}
 416    }
 417    OldvcV = new_vcv;		/* set for next time */
 418#ifdef DEBUG_REFRESH
 419    reprintf("\r\nCursorH = %d, CursorV = %d, cur_h = %d, cur_v = %d\r\n",
 420	    CursorH, CursorV, cur_h, cur_v);
 421#endif /* DEBUG_REFRESH */
 422#ifdef WINNT_NATIVE
 423    flush();
 424#endif /* WINNT_NATIVE */
 425    MoveToLine(cur_v);		/* go to where the cursor is */
 426    MoveToChar(cur_h);
 427    SetAttributes(0);		/* Clear all attributes */
 428    flush();			/* send the output... */
 429    GettingInput = oldgetting;	/* reset to old value */
 430}
 431
 432#ifdef notdef
 433GotoBottom(void)
 434{				/* used to go to last used screen line */
 435    MoveToLine(OldvcV);
 436}
 437
 438#endif 
 439
 440void
 441PastBottom(void)
 442{				/* used to go to last used screen line */
 443    MoveToLine(OldvcV);
 444    (void) putraw('\r');
 445    (void) putraw('\n');
 446    ClearDisp();
 447    flush();
 448}
 449
 450
 451/* insert num characters of s into d (in front of the character) at dat,
 452   maximum length of d is dlen */
 453static void
 454str_insert(Char *d, int dat, int dlen, Char *s, int num)
 455{
 456    Char *a, *b;
 457
 458    if (num <= 0)
 459	return;
 460    if (num > dlen - dat)
 461	num = dlen - dat;
 462
 463#ifdef DEBUG_REFRESH
 464    reprintf("str_insert() starting: %d at %d max %d, d == \"%s\"\n",
 465	    num, dat, dlen, short2str(d));
 466    reprintf("s == \"%s\"n", short2str(s));
 467#endif /* DEBUG_REFRESH */
 468
 469    /* open up the space for num chars */
 470    if (num > 0) {
 471	b = d + dlen - 1;
 472	a = b - num;
 473	while (a >= &d[dat])
 474	    *b-- = *a--;
 475	d[dlen] = '\0';		/* just in case */
 476    }
 477#ifdef DEBUG_REFRESH
 478    reprintf("str_insert() after insert: %d at %d max %d, d == \"%s\"\n",
 479	    num, dat, dlen, short2str(d));
 480    reprintf("s == \"%s\"n", short2str(s));
 481#endif /* DEBUG_REFRESH */
 482
 483    /* copy the characters */
 484    for (a = d + dat; (a < d + dlen) && (num > 0); num--)
 485	*a++ = *s++;
 486
 487#ifdef DEBUG_REFRESH
 488    reprintf("str_insert() after copy: %d at %d max %d, d == \"%s\"\n",
 489	    num, dat, dlen, d, short2str(s));
 490    reprintf("s == \"%s\"n", short2str(s));
 491#endif /* DEBUG_REFRESH */
 492}
 493
 494/* delete num characters d at dat, maximum length of d is dlen */
 495static void
 496str_delete(Char *d, int dat, int dlen, int num)
 497{
 498    Char *a, *b;
 499
 500    if (num <= 0)
 501	return;
 502    if (dat + num >= dlen) {
 503	d[dat] = '\0';
 504	return;
 505    }
 506
 507#ifdef DEBUG_REFRESH
 508    reprintf("str_delete() starting: %d at %d max %d, d == \"%s\"\n",
 509	    num, dat, dlen, short2str(d));
 510#endif /* DEBUG_REFRESH */
 511
 512    /* open up the space for num chars */
 513    if (num > 0) {
 514	b = d + dat;
 515	a = b + num;
 516	while (a < &d[dlen])
 517	    *b++ = *a++;
 518	d[dlen] = '\0';		/* just in case */
 519    }
 520#ifdef DEBUG_REFRESH
 521    reprintf("str_delete() after delete: %d at %d max %d, d == \"%s\"\n",
 522	    num, dat, dlen, short2str(d));
 523#endif /* DEBUG_REFRESH */
 524}
 525
 526static void
 527str_cp(Char *a, Char *b, int n)
 528{
 529    while (n-- && *b)
 530	*a++ = *b++;
 531}
 532
 533
 534/* ****************************************************************
 535    update_line() is based on finding the middle difference of each line
 536    on the screen; vis:
 537
 538			     /old first difference
 539	/beginning of line   |              /old last same       /old EOL
 540	v		     v              v                    v
 541old:	eddie> Oh, my little gruntle-buggy is to me, as lurgid as
 542new:	eddie> Oh, my little buggy says to me, as lurgid as
 543	^		     ^        ^			   ^
 544	\beginning of line   |        \new last same	   \new end of line
 545			     \new first difference
 546
 547    all are character pointers for the sake of speed.  Special cases for
 548    no differences, as well as for end of line additions must be handled.
 549**************************************************************** */
 550
 551/* Minimum at which doing an insert it "worth it".  This should be about
 552 * half the "cost" of going into insert mode, inserting a character, and
 553 * going back out.  This should really be calculated from the termcap
 554 * data...  For the moment, a good number for ANSI terminals.
 555 */
 556#define MIN_END_KEEP	4
 557
 558static void			/* could be changed to make it smarter */
 559update_line(Char *old, Char *new, int cur_line)
 560{
 561    Char *o, *n, *p, c;
 562    Char  *ofd, *ols, *oe, *nfd, *nls, *ne;
 563    Char  *osb, *ose, *nsb, *nse;
 564    int     fx, sx;
 565
 566    /*
 567     * find first diff (won't be CHAR_DBWIDTH in either line)
 568     */
 569    for (o = old, n = new; *o && (*o == *n); o++, n++)
 570	continue;
 571    ofd = o;
 572    nfd = n;
 573
 574    /*
 575     * Find the end of both old and new
 576     */
 577    o = Strend(o);
 578
 579    /* 
 580     * Remove any trailing blanks off of the end, being careful not to
 581     * back up past the beginning.
 582     */
 583    if (!(adrof(STRhighlight) && MarkIsSet)) {
 584    while (ofd < o) {
 585	if (o[-1] != ' ')
 586	    break;
 587	o--;
 588    }
 589    }
 590    oe = o;
 591    *oe = (Char) 0;
 592
 593    n = Strend(n);
 594
 595    /* remove blanks from end of new */
 596    if (!(adrof(STRhighlight) && MarkIsSet)) {
 597    while (nfd < n) {
 598	if (n[-1] != ' ')
 599	    break;
 600	n--;
 601    }
 602    }
 603    ne = n;
 604    *ne = (Char) 0;
 605  
 606    /*
 607     * if no diff, continue to next line of redraw
 608     */
 609    if (*ofd == '\0' && *nfd == '\0') {
 610#ifdef DEBUG_UPDATE
 611	reprintf("no difference.\r\n");
 612#endif /* DEBUG_UPDATE */
 613	return;
 614    }
 615
 616    /*
 617     * find last same pointer
 618     */
 619    while ((o > ofd) && (n > nfd) && (*--o == *--n))
 620	continue;
 621    if (*o != *n) {
 622	o++;
 623	n++;
 624    }
 625    while (*o == CHAR_DBWIDTH) {
 626	o++;
 627	n++;
 628    }
 629    ols = o;
 630    nls = n;
 631
 632    /*
 633     * find same begining and same end
 634     */
 635    osb = ols;
 636    nsb = nls;
 637    ose = ols;
 638    nse = nls;
 639
 640    /*
 641     * case 1: insert: scan from nfd to nls looking for *ofd
 642     */
 643    if (*ofd) {
 644	for (c = *ofd, n = nfd; n < nls; n++) {
 645	    if (c == *n) {
 646		for (o = ofd, p = n; p < nls && o < ols && *o == *p; o++, p++)
 647		    continue;
 648		/*
 649		 * if the new match is longer and it's worth keeping, then we
 650		 * take it
 651		 */
 652		if (((nse - nsb) < (p - n)) && (2 * (p - n) > n - nfd)) {
 653		    nsb = n;
 654		    nse = p;
 655		    osb = ofd;
 656		    ose = o;
 657		}
 658	    }
 659	}
 660    }
 661
 662    /*
 663     * case 2: delete: scan from ofd to ols looking for *nfd
 664     */
 665    if (*nfd) {
 666	for (c = *nfd, o = ofd; o < ols; o++) {
 667	    if (c == *o) {
 668		for (n = nfd, p = o; p < ols && n < nls && *p == *n; p++, n++)
 669		    continue;
 670		/*
 671		 * if the new match is longer and it's worth keeping, then we
 672		 * take it
 673		 */
 674		if (((ose - osb) < (p - o)) && (2 * (p - o) > o - ofd)) {
 675		    nsb = nfd;
 676		    nse = n;
 677		    osb = o;
 678		    ose = p;
 679		}
 680	    }
 681	}
 682    }
 683#ifdef notdef
 684    /*
 685     * If `last same' is before `same end' re-adjust
 686     */
 687    if (ols < ose)
 688	ols = ose;
 689    if (nls < nse)
 690	nls = nse;
 691#endif
 692
 693    /*
 694     * Pragmatics I: If old trailing whitespace or not enough characters to
 695     * save to be worth it, then don't save the last same info.
 696     */
 697    if ((oe - ols) < MIN_END_KEEP) {
 698	ols = oe;
 699	nls = ne;
 700    }
 701
 702    /*
 703     * Pragmatics II: if the terminal isn't smart enough, make the data dumber
 704     * so the smart update doesn't try anything fancy
 705     */
 706
 707    /*
 708     * fx is the number of characters we need to insert/delete: in the
 709     * beginning to bring the two same begins together
 710     */
 711    fx = (int) ((nsb - nfd) - (osb - ofd));
 712    /*
 713     * sx is the number of characters we need to insert/delete: in the end to
 714     * bring the two same last parts together
 715     */
 716    sx = (int) ((nls - nse) - (ols - ose));
 717
 718    if (!T_CanIns) {
 719	if (fx > 0) {
 720	    osb = ols;
 721	    ose = ols;
 722	    nsb = nls;
 723	    nse = nls;
 724	}
 725	if (sx > 0) {
 726	    ols = oe;
 727	    nls = ne;
 728	}
 729	if ((ols - ofd) < (nls - nfd)) {
 730	    ols = oe;
 731	    nls = ne;
 732	}
 733    }
 734    if (!T_CanDel) {
 735	if (fx < 0) {
 736	    osb = ols;
 737	    ose = ols;
 738	    nsb = nls;
 739	    nse = nls;
 740	}
 741	if (sx < 0) {
 742	    ols = oe;
 743	    nls = ne;
 744	}
 745	if ((ols - ofd) > (nls - nfd)) {
 746	    ols = oe;
 747	    nls = ne;
 748	}
 749    }
 750
 751    /*
 752     * Pragmatics III: make sure the middle shifted pointers are correct if
 753     * they don't point to anything (we may have moved ols or nls).
 754     */
 755    /* if the change isn't worth it, don't bother */
 756    /* was: if (osb == ose) */
 757    if ((ose - osb) < MIN_END_KEEP) {
 758	osb = ols;
 759	ose = ols;
 760	nsb = nls;
 761	nse = nls;
 762    }
 763
 764    /*
 765     * Now that we are done with pragmatics we recompute fx, sx
 766     */
 767    fx = (int) ((nsb - nfd) - (osb - ofd));
 768    sx = (int) ((nls - nse) - (ols - ose));
 769
 770#ifdef DEBUG_UPDATE
 771    reprintf("\n");
 772    reprintf("ofd %d, osb %d, ose %d, ols %d, oe %d\n",
 773	    ofd - old, osb - old, ose - old, ols - old, oe - old);
 774    reprintf("nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
 775	    nfd - new, nsb - new, nse - new, nls - new, ne - new);
 776    reprintf("xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n");
 777    reprintf("xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n");
 778    dprintstr("old- oe", old, oe);
 779    dprintstr("new- ne", new, ne);
 780    dprintstr("old-ofd", old, ofd);
 781    dprintstr("new-nfd", new, nfd);
 782    dprintstr("ofd-osb", ofd, osb);
 783    dprintstr("nfd-nsb", nfd, nsb);
 784    dprintstr("osb-ose", osb, ose);
 785    dprintstr("nsb-nse", nsb, nse);
 786    dprintstr("ose-ols", ose, ols);
 787    dprintstr("nse-nls", nse, nls);
 788    dprintstr("ols- oe", ols, oe);
 789    dprintstr("nls- ne", nls, ne);
 790#endif /* DEBUG_UPDATE */
 791
 792    /*
 793     * CursorV to this line cur_line MUST be in this routine so that if we
 794     * don't have to change the line, we don't move to it. CursorH to first
 795     * diff char
 796     */
 797    MoveToLine(cur_line);
 798
 799    /*
 800     * at this point we have something like this:
 801     * 
 802     * /old                  /ofd    /osb               /ose    /ols     /oe
 803     * v.....................v       v..................v       v........v
 804     * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
 805     * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
 806     * ^.....................^     ^..................^       ^........^ 
 807     * \new                  \nfd  \nsb               \nse     \nls    \ne
 808     * 
 809     * fx is the difference in length between the the chars between nfd and
 810     * nsb, and the chars between ofd and osb, and is thus the number of
 811     * characters to delete if < 0 (new is shorter than old, as above),
 812     * or insert (new is longer than short).
 813     *
 814     * sx is the same for the second differences.
 815     */
 816
 817    /*
 818     * if we have a net insert on the first difference, AND inserting the net
 819     * amount ((nsb-nfd) - (osb-ofd)) won't push the last useful character
 820     * (which is ne if nls != ne, otherwise is nse) off the edge of the screen
 821     * (TermH - 1) else we do the deletes first so that we keep everything we
 822     * need to.
 823     */
 824
 825    /*
 826     * if the last same is the same like the end, there is no last same part,
 827     * otherwise we want to keep the last same part set p to the last useful
 828     * old character
 829     */
 830    p = (ols != oe) ? oe : ose;
 831
 832    /*
 833     * if (There is a diffence in the beginning) && (we need to insert
 834     * characters) && (the number of characters to insert is less than the term
 835     * width) We need to do an insert! else if (we need to delete characters)
 836     * We need to delete characters! else No insert or delete
 837     */
 838    if ((nsb != nfd) && fx > 0 && ((p - old) + fx < TermH)) {
 839#ifdef DEBUG_UPDATE
 840	reprintf("first diff insert at %d...\r\n", nfd - new);
 841#endif  /* DEBUG_UPDATE */
 842	/*
 843	 * Move to the first char to insert, where the first diff is.
 844	 */
 845	MoveToChar(nfd - new);
 846	/*
 847	 * Check if we have stuff to keep at end
 848	 */
 849	if (nsb != ne) {
 850#ifdef DEBUG_UPDATE
 851	    reprintf("with stuff to keep at end\r\n");
 852#endif  /* DEBUG_UPDATE */
 853	    /*
 854	     * insert fx chars of new starting at nfd
 855	     */
 856	    if (fx > 0) {
 857#ifdef DEBUG_UPDATE
 858		if (!T_CanIns)
 859		    reprintf("   ERROR: cannot insert in early first diff\n");
 860#endif  /* DEBUG_UPDATE */
 861		Insert_write(nfd, fx);
 862		str_insert(old, (int) (ofd - old), TermH, nfd, fx);
 863	    }
 864	    /*
 865	     * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
 866	     */
 867	    so_write(nfd + fx, (nsb - nfd) - fx);
 868	    str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx));
 869	}
 870	else {
 871#ifdef DEBUG_UPDATE
 872	    reprintf("without anything to save\r\n");
 873#endif  /* DEBUG_UPDATE */
 874	    so_write(nfd, (nsb - nfd));
 875	    str_cp(ofd, nfd, (int) (nsb - nfd));
 876	    /*
 877	     * Done
 878	     */
 879	    return;
 880	}
 881    }
 882    else if (fx < 0) {
 883#ifdef DEBUG_UPDATE
 884	reprintf("first diff delete at %d...\r\n", ofd - old);
 885#endif  /* DEBUG_UPDATE */
 886	/*
 887	 * move to the first char to delete where the first diff is
 888	 */
 889	MoveToChar(ofd - old);
 890	/*
 891	 * Check if we have stuff to save
 892	 */
 893	if (osb != oe) {
 894#ifdef DEBUG_UPDATE
 895	    reprintf("with stuff to save at end\r\n");
 896#endif  /* DEBUG_UPDATE */
 897	    /*
 898	     * fx is less than zero *always* here but we check for code
 899	     * symmetry
 900	     */
 901	    if (fx < 0) {
 902#ifdef DEBUG_UPDATE
 903		if (!T_CanDel)
 904		    reprintf("   ERROR: cannot delete in first diff\n");
 905#endif /* DEBUG_UPDATE */
 906		DeleteChars(-fx);
 907		str_delete(old, (int) (ofd - old), TermH, -fx);
 908	    }
 909	    /*
 910	     * write (nsb-nfd) chars of new starting at nfd
 911	     */
 912	    so_write(nfd, (nsb - nfd));
 913	    str_cp(ofd, nfd, (int) (nsb - nfd));
 914
 915	}
 916	else {
 917#ifdef DEBUG_UPDATE
 918	    reprintf("but with nothing left to save\r\n");
 919#endif  /* DEBUG_UPDATE */
 920	    /*
 921	     * write (nsb-nfd) chars of new starting at nfd
 922	     */
 923	    so_write(nfd, (nsb - nfd));
 924#ifdef DEBUG_REFRESH
 925	    reprintf("cleareol %d\n", (oe - old) - (ne - new));
 926#endif  /* DEBUG_UPDATE */
 927#ifndef WINNT_NATIVE
 928	    ClearEOL((oe - old) - (ne - new));
 929#else
 930	    /*
 931	     * The calculation above does not work too well on NT
 932	     */
 933	    ClearEOL(TermH - CursorH);
 934#endif /*WINNT_NATIVE*/
 935	    /*
 936	     * Done
 937	     */
 938	    return;
 939	}
 940    }
 941    else
 942	fx = 0;
 943
 944    if (sx < 0) {
 945#ifdef DEBUG_UPDATE
 946	reprintf("second diff delete at %d...\r\n", (ose - old) + fx);
 947#endif  /* DEBUG_UPDATE */
 948	/*
 949	 * Check if we have stuff to delete
 950	 */
 951	/*
 952	 * fx is the number of characters inserted (+) or deleted (-)
 953	 */
 954
 955	MoveToChar((ose - old) + fx);
 956	/*
 957	 * Check if we have stuff to save
 958	 */
 959	if (ols != oe) {
 960#ifdef DEBUG_UPDATE
 961	    reprintf("with stuff to save at end\r\n");
 962#endif  /* DEBUG_UPDATE */
 963	    /*
 964	     * Again a duplicate test.
 965	     */
 966	    if (sx < 0) {
 967#ifdef DEBUG_UPDATE
 968		if (!T_CanDel)
 969		    reprintf("   ERROR: cannot delete in second diff\n");
 970#endif  /* DEBUG_UPDATE */
 971		DeleteChars(-sx);
 972	    }
 973
 974	    /*
 975	     * write (nls-nse) chars of new starting at nse
 976	     */
 977	    so_write(nse, (nls - nse));
 978	}
 979	else {
 980	    int olen = (int) (oe - old + fx);
 981	    if (olen > TermH)
 982		olen = TermH;
 983#ifdef DEBUG_UPDATE
 984	    reprintf("but with nothing left to save\r\n");
 985#endif /* DEBUG_UPDATE */
 986	    so_write(nse, (nls - nse));
 987#ifdef DEBUG_REFRESH
 988	    reprintf("cleareol %d\n", olen - (ne - new));
 989#endif /* DEBUG_UPDATE */
 990#ifndef WINNT_NATIVE
 991	    ClearEOL(olen - (ne - new));
 992#else
 993	    /*
 994	     * The calculation above does not work too well on NT
 995	     */
 996	    ClearEOL(TermH - CursorH);
 997#endif /*WINNT_NATIVE*/
 998	}
 999    }
1000
1001    /*
1002     * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
1003     */
1004    if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
1005#ifdef DEBUG_UPDATE
1006	reprintf("late first diff insert at %d...\r\n", nfd - new);
1007#endif /* DEBUG_UPDATE */
1008
1009	MoveToChar(nfd - new);
1010	/*
1011	 * Check if we have stuff to keep at the end
1012	 */
1013	if (nsb != ne) {
1014#ifdef DEBUG_UPDATE
1015	    reprintf("with stuff to keep at end\r\n");
1016#endif /* DEBUG_UPDATE */
1017	    /* 
1018	     * We have to recalculate fx here because we set it
1019	     * to zero above as a flag saying that we hadn't done
1020	     * an early first insert.
1021	     */
1022	    fx = (int) ((nsb - nfd) - (osb - ofd));
1023	    if (fx > 0) {
1024		/*
1025		 * insert fx chars of new starting at nfd
1026		 */
1027#ifdef DEBUG_UPDATE
1028		if (!T_CanIns)
1029		    reprintf("   ERROR: cannot insert in late first diff\n");
1030#endif /* DEBUG_UPDATE */
1031		Insert_write(nfd, fx);
1032		str_insert(old, (int) (ofd - old), TermH, nfd, fx);
1033	    }
1034
1035	    /*
1036	     * write (nsb-nfd) - fx chars of new starting at (nfd + fx)
1037	     */
1038	    so_write(nfd + fx, (nsb - nfd) - fx);
1039	    str_cp(ofd + fx, nfd + fx, (int) ((nsb - nfd) - fx));
1040	}
1041	else {
1042#ifdef DEBUG_UPDATE
1043	    reprintf("without anything to save\r\n");
1044#endif /* DEBUG_UPDATE */
1045	    so_write(nfd, (nsb - nfd));
1046	    str_cp(ofd, nfd, (int) (nsb - nfd));
1047	}
1048    }
1049
1050    /*
1051     * line is now NEW up to nse
1052     */
1053    if (sx >= 0) {
1054#ifdef DEBUG_UPDATE
1055	reprintf("second diff insert at %d...\r\n", nse - new);
1056#endif /* DEBUG_UPDATE */
1057	MoveToChar(nse - new);
1058	if (ols != oe) {
1059#ifdef DEBUG_UPDATE
1060	    reprintf("with stuff to keep at end\r\n");
1061#endif /* DEBUG_UPDATE */
1062	    if (sx > 0) {
1063		/* insert sx chars of new starting at nse */
1064#ifdef DEBUG_UPDATE
1065		if (!T_CanIns)
1066		    reprintf("   ERROR: cannot insert in second diff\n");
1067#endif /* DEBUG_UPDATE */
1068		Insert_write(nse, sx);
1069	    }
1070
1071	    /*
1072	     * write (nls-nse) - sx chars of new starting at (nse + sx)
1073	     */
1074	    so_write(nse + sx, (nls - nse) - sx);
1075	}
1076	else {
1077#ifdef DEBUG_UPDATE
1078	    reprintf("without anything to save\r\n");
1079#endif /* DEBUG_UPDATE */
1080	    so_write(nse, (nls - nse));
1081
1082	    /*
1083             * No need to do a clear-to-end here because we were doing
1084	     * a second insert, so we will have over written all of the
1085	     * old string.
1086	     */
1087	}
1088    }
1089#ifdef DEBUG_UPDATE
1090    reprintf("done.\r\n");
1091#endif /* DEBUG_UPDATE */
1092}
1093
1094
1095static void
1096cpy_pad_spaces(Char *dst, Char *src, int width)
1097{
1098    int i;
1099
1100    for (i = 0; i < width; i++) {
1101	if (*src == (Char) 0)
1102	    break;
1103	*dst++ = *src++;
1104    }
1105
1106    while (i < width) {
1107	*dst++ = ' ';
1108	i++;
1109    }
1110    *dst = (Char) 0;
1111}
1112
1113void
1114RefCursor(void)
1115{				/* only move to new cursor pos */
1116    Char *cp;
1117    int w, h, th, v;
1118
1119    /* first we must find where the cursor is... */
1120    h = 0;
1121    v = 0;
1122    th = TermH;			/* optimize for speed */
1123
1124    for (cp = Prompt; cp != NULL && *cp; ) {	/* do prompt */
1125	if (*cp & LITERAL) {
1126	    cp++;
1127	    continue;
1128	}
1129	w = NLSClassify(*cp & CHAR, cp == Prompt);
1130	cp++;
1131	switch(w) {
1132	    case NLSCLASS_NL:
1133		h = 0;
1134		v++;
1135		break;
1136	    case NLSCLASS_TAB:
1137		while (++h & 07)
1138		    ;
1139		break;
1140	    case NLSCLASS_CTRL:
1141		h += 2;
1142		break;
1143	    case NLSCLASS_ILLEGAL:
1144		h += 4;
1145		break;
1146	    case NLSCLASS_ILLEGAL2:
1147	    case NLSCLASS_ILLEGAL3:
1148	    case NLSCLASS_ILLEGAL4:
1149		h += 3 + 2 * NLSCLASS_ILLEGAL_SIZE(w);
1150		break;
1151	    default:
1152		h += w;
1153	}
1154	if (h >= th) {		/* check, extra long tabs picked up here also */
1155	    h -= th;
1156	    v++;
1157	}
1158    }
1159
1160    for (cp = InputBuf; cp < Cursor;) {	/* do input buffer to Cursor */
1161	w = NLSClassify(*cp & CHAR, cp == InputBuf);
1162	cp++;
1163	switch(w) {
1164	    case NLSCLASS_NL:
1165		h = 0;
1166		v++;
1167		break;
1168	    case NLSCLASS_TAB:
1169		while (++h & 07)
1170		    ;
1171		break;
1172	    case NLSCLASS_CTRL:
1173		h += 2;
1174		break;
1175	    case NLSCLASS_ILLEGAL:
1176		h += 4;
1177		break;
1178	    case NLSCLASS_ILLEGAL2:
1179	    case NLSCLASS_ILLEGAL3:
1180	    case NLSCLASS_ILLEGAL4:
1181		h += 3 + 2 * NLSCLASS_ILLEGAL_SIZE(w);
1182		break;
1183	    default:
1184		h += w;
1185	}
1186	if (h >= th) {		/* check, extra long tabs picked up here also */
1187	    h -= th;
1188	    v++;
1189	}
1190    }
1191
1192    /* now go there */
1193    MoveToLine(v);
1194    MoveToChar(h);
1195    if (adrof(STRhighlight) && MarkIsSet) {
1196	ClearLines();
1197	ClearDisp();
1198	Refresh();
1199    }
1200    flush();
1201}
1202
1203#ifndef WINTT_NATIVE
1204static void
1205PutPlusOne(Char c, int width)
1206{
1207    while (width > 1 && CursorH + width > TermH)
1208	PutPlusOne(' ', 1);
1209    if ((c & LITERAL) != 0) {
1210	Char *d;
1211	for (d = litptr + (c & ~LITERAL) * LIT_FACTOR; *d; d++)
1212	    (void) putwraw(*d);
1213    } else {
1214	(void) putwraw(c);
1215    }
1216    Display[CursorV][CursorH++] = (Char) c;
1217    while (--width > 0)
1218	Display[CursorV][CursorH++] = CHAR_DBWIDTH;
1219    if (CursorH >= TermH) {	/* if we must overflow */
1220	CursorH = 0;
1221	CursorV++;
1222	OldvcV++;
1223	if (T_Margin & MARGIN_AUTO) {
1224	    if (T_Margin & MARGIN_MAGIC) {
1225		(void) putraw(' ');
1226		(void) putraw('\b');
1227	    }
1228	}
1229	else {
1230	    (void) putraw('\r');
1231	    (void) putraw('\n');
1232	}
1233    }
1234}
1235#endif
1236
1237void
1238RefPlusOne(int l)
1239{				/* we added just one char, handle it fast.
1240				 * assumes that screen cursor == real cursor */
1241    Char *cp, c;
1242    int w;
1243
1244    if (Cursor != LastChar) {
1245	Refresh();		/* too hard to handle */
1246	return;
1247    }
1248    if (rprompt_h != 0 && (TermH - CursorH - rprompt_h < 3)) {
1249	Refresh();		/* clear out rprompt if less than one char gap*/
1250	return;
1251    }
1252    cp = Cursor - l;
1253    c = *cp & CHAR;
1254    w = NLSClassify(c, cp == InputBuf);
1255    switch(w) {
1256	case NLSCLASS_CTRL:
1257	    PutPlusOne('^', 1);
1258	    if (c == CTL_ESC('\177')) {
1259		PutPlusOne('?', 1);
1260		break;
1261	    }
1262#ifdef IS_ASCII
1263	    /* uncontrolify it; works only for iso8859-1 like sets */
1264	    PutPlusOne((c | 0100), 1);
1265#else
1266	    PutPlusOne(_toebcdic[_toascii[c]|0100], 1);
1267#endif
1268	    break;
1269	case NLSCLASS_ILLEGAL:
1270	    PutPlusOne('\\', 1);
1271	    PutPlusOne(((c >> 6) & 7) + '0', 1);
1272	    PutPlusOne(((c >> 3) & 7) + '0', 1);
1273	    PutPlusOne((c & 7) + '0', 1);
1274	    break;
1275	case 1:
1276	    if (adrof(STRhighlight) && MarkIsSet)
1277		StartHighlight();
1278	    if (l > 1)
1279		PutPlusOne(MakeLiteral(cp, l, 0), 1);
1280	    else
1281		PutPlusOne(*cp, 1);
1282	    if (adrof(STRhighlight) && MarkIsSet)
1283		StopHighlight();
1284	    break;
1285	default:
1286	    Refresh();		/* too hard to handle */
1287	    return;
1288    }
1289    flush();
1290}
1291
1292/* clear the screen buffers so that new new prompt starts fresh. */
1293
1294void
1295ClearDisp(void)
1296{
1297    int i;
1298
1299    CursorV = 0;		/* clear the display buffer */
1300    CursorH = 0;
1301    for (i = 0; i < TermV; i++)
1302	(void) memset(Display[i], 0, TermH * sizeof(Display[0][0]));
1303    OldvcV = 0;
1304    litlen = 0;
1305}
1306
1307void
1308ClearLines(void)
1309{				/* Make sure all lines are *really* blank */
1310    int i;
1311
1312    if (T_CanCEOL) {
1313	/*
1314	 * Clear the lines from the bottom up so that if we try moving
1315	 * the cursor down by writing the character that is at the end
1316	 * of the screen line, we won't rewrite a character that shouldn't
1317	 * be there.
1318	 */
1319	for (i = OldvcV; i >= 0; i--) {	/* for each line on the screen */
1320	    MoveToLine(i);
1321	    MoveToChar(0);
1322	    ClearEOL(TermH);
1323	}
1324    }
1325    else {
1326	MoveToLine(OldvcV);	/* go to last line */
1327	(void) putraw('\r');	/* go to BOL */
1328	(void) putraw('\n');	/* go to new line */
1329    }
1330}