1/*
2 * zle_refresh.c - screen update
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1992-1997 Paul Falstad
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Paul Falstad or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Paul Falstad and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Paul Falstad and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose.  The software
24 * provided hereunder is on an "as is" basis, and Paul Falstad and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "zle.mdh"
31
32#ifdef MULTIBYTE_SUPPORT
33/*
34 * Handling for glyphs that contain more than one wide character,
35 * if ZLE_COMBINING_CHARS is set.  Each glyph is one character with
36 * non-zero width followed by an arbitrary (but typically small)
37 * number of characters that have zero width (combining characters).
38 *
39 * The allocated size for each array is given by ?mw_size; nmw_ind
40 * is the next free element, i.e. nmwbuf[nmw_ind] will be the next
41 * element to be written (we never insert into omwbuf).  We initialise
42 * nmw_ind to 1 to avoid the index stored in the character looking like a
43 * NULL.  This wastees a word but it's safer than messing with pointers.
44 *
45 * The layout of the buffer is as a string of entries that consist of multiple
46 * elements of the allocated array with no boundary (the code keeps track of
47 * where each entry starts).  Note distinction between (logical) entries and
48 * (array) elements.  Each entry consists of an element giving the total
49 * number of wide characters for the entry (there are N+1 wide characters,
50 * where N >= 1 is the number of trailing zero width characters), followed by
51 * those characters.
52 */
53static REFRESH_CHAR
54    *omwbuf = NULL,		/* old multiword glyph buffer */
55    *nmwbuf = NULL;		/* new multiword glyph buffer */
56#endif
57
58/*
59 * Compare if two characters are equal.
60 */
61#ifdef MULTIBYTE_SUPPORT
62/*
63 * We may need to compare values in multiword arrays.  As the arrays are
64 * different for the old and new video arrays, it is vital that the comparison
65 * always be done in the correct order: an element of the old video array,
66 * followed by an element of the new one.  In this case, having ascertained
67 * that both elements are multiword (because they have the some attributes),
68 * we do the character comparison in two stages: first we check that the
69 * lengths are the same, then we check that the characters stored are the
70 * same.  This ensures we can't read past the end of either array.  If either
71 * character is a constant, then TXT_MULTIWORD_MASK is guaranteed not to be
72 * set and this doesn't matter.
73 */
74#define ZR_equal(oldzr, newzr)					   \
75    ((oldzr).atr == (newzr).atr &&				   \
76     (((oldzr).atr & TXT_MULTIWORD_MASK) ?			   \
77      (omwbuf[(oldzr).chr] == nmwbuf[(newzr).chr] &&		   \
78       !memcmp(omwbuf + (oldzr).chr + 1, nmwbuf + (newzr).chr + 1, \
79	       omwbuf[(oldzr).chr] * sizeof(*omwbuf))) :	   \
80      (oldzr).chr == (newzr).chr))
81#else
82#define ZR_equal(zr1, zr2) ((zr1).chr == (zr2).chr && (zr1).atr == (zr2).atr)
83#endif
84
85static void
86ZR_memset(REFRESH_ELEMENT *dst, REFRESH_ELEMENT rc, int len)
87{
88    while (len--)
89	*dst++ = rc;
90}
91
92#define ZR_memcpy(d, s, l)  memcpy((d), (s), (l)*sizeof(REFRESH_ELEMENT))
93
94static void
95ZR_strcpy(REFRESH_ELEMENT *dst, const REFRESH_ELEMENT *src)
96{
97    while ((*dst++ = *src++).chr != ZWC('\0'))
98	;
99}
100
101static size_t
102ZR_strlen(const REFRESH_ELEMENT *wstr)
103{
104    int len = 0;
105
106    while (wstr++->chr != ZWC('\0'))
107	len++;
108
109    return len;
110}
111
112/*
113 * Simplified strcmp: we don't need the sign, just whether
114 * the strings and their attributes are equal.
115 *
116 * In the multibyte case, the two elements must be in the order
117 * element from old video array, element from new video array.
118 */
119static int
120ZR_strncmp(const REFRESH_ELEMENT *oldwstr, const REFRESH_ELEMENT *newwstr,
121	   int len)
122{
123    while (len--) {
124	if ((!(oldwstr->atr & TXT_MULTIWORD_MASK) && !oldwstr->chr) ||
125	    (!(newwstr->atr & TXT_MULTIWORD_MASK) && !newwstr->chr))
126	    return !ZR_equal(*oldwstr, *newwstr);
127	if (!ZR_equal(*oldwstr, *newwstr))
128	    return 1;
129	oldwstr++;
130	newwstr++;
131    }
132
133    return 0;
134}
135
136#include "zle_refresh.pro"
137
138/*
139 * Expanded prompts.
140 *
141 * These are always output from the start, except in the special
142 * case where we are sure each character in the prompt corresponds
143 * to a character on screen.
144 */
145
146/**/
147char *lpromptbuf, *rpromptbuf;
148
149/* Text attributes after displaying prompts */
150
151/**/
152unsigned pmpt_attr, rpmpt_attr;
153
154/* number of lines displayed */
155
156/**/
157mod_export int nlnct;
158
159/* Most lines of the buffer we've shown at once with the current list *
160 * showing.  == 0 if there is no list.  == -1 if a new list has just  *
161 * been put on the screen.  == -2 if zrefresh() needs to put up a new *
162 * list.                                                              */
163
164/**/
165mod_export int showinglist;
166
167/* > 0 if a completion list is displayed below the prompt,
168 * < 0 if a list is displayed above the prompt. */
169
170/**/
171mod_export int listshown;
172
173/* Length of last list displayed (if it is below the prompt). */
174
175/**/
176mod_export int lastlistlen;
177
178/* Non-zero if ALWAYS_LAST_PROMPT has been used, meaning that the *
179 * screen below the buffer display should not be cleared by       *
180 * zrefresh(), but should be by trashzle().                       */
181
182/**/
183mod_export int clearflag;
184
185/* Non-zero if zrefresh() should clear the list below the prompt. */
186
187/**/
188mod_export int clearlist;
189
190/* Zle in trashed state - updates may be subtly altered */
191
192/**/
193int trashedzle;
194
195/*
196 * Information used by PREDISPLAY and POSTDISPLAY parameters which
197 * add non-editable text to that being displayed.
198 */
199/**/
200ZLE_STRING_T predisplay, postdisplay;
201/**/
202int predisplaylen, postdisplaylen;
203
204
205/*
206 * Attributes used by default on the command line, and
207 * attributes for highlighting special (unprintable) characters
208 * displayed on screen.
209 */
210
211static int default_atr_on, special_atr_on;
212
213/*
214 * Array of region highlights, no special termination.
215 * The first element (0) always describes the region between
216 * point and mark.  Any other elements are set by the user
217 * via the parameter region_highlight.
218 */
219
220/**/
221struct region_highlight *region_highlights;
222
223/*
224 * Number of elements in region_highlights.
225 * This includes the special elements above.
226 */
227/**/
228int n_region_highlights;
229
230/*
231 * Flag that highlighting of the region is active.
232 */
233/**/
234int region_active;
235
236/*
237 * Name of function to use to output termcap values, if defined.
238 */
239/**/
240char *tcout_func_name;
241
242#ifdef HAVE_SELECT
243/* cost of last update */
244/**/
245int cost;
246
247# define SELECT_ADD_COST(X)	(cost += X)
248# define zputc(a)		(zwcputc(a, NULL), cost++)
249# define zwrite(a, b)		(zwcwrite((a), (b)), \
250				 cost += ((b) * ZLE_CHAR_SIZE))
251#else
252# define SELECT_ADD_COST(X)
253# define zputc(a)		zwcputc(a, NULL)
254# define zwrite(a, b)		zwcwrite((a), (b))
255#endif
256
257static const REFRESH_ELEMENT zr_cr = { ZWC('\r'), 0 };
258static const REFRESH_ELEMENT zr_dt = { ZWC('.'), 0 };
259static const REFRESH_ELEMENT zr_nl = { ZWC('\n'), 0 };
260static const REFRESH_ELEMENT zr_sp = { ZWC(' '), 0 };
261static const REFRESH_ELEMENT zr_ht = { ZWC('\t'), 0 };
262static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), 0 };
263
264/*
265 * Constant arrays to be copied into place: these are memcpy'd,
266 * so don't have terminating NULLs.
267 */
268static const REFRESH_ELEMENT zr_end_ellipsis[] = {
269    { ZWC(' '), 0 },
270    { ZWC('<'), 0 },
271    { ZWC('.'), 0 },
272    { ZWC('.'), 0 },
273    { ZWC('.'), 0 },
274    { ZWC('.'), 0 },
275    { ZWC(' '), 0 },
276};
277#define ZR_END_ELLIPSIS_SIZE	\
278    ((int)(sizeof(zr_end_ellipsis)/sizeof(zr_end_ellipsis[0])))
279
280static const REFRESH_ELEMENT zr_mid_ellipsis1[] = {
281    { ZWC(' '), 0 },
282    { ZWC('<'), 0 },
283    { ZWC('.'), 0 },
284    { ZWC('.'), 0 },
285    { ZWC('.'), 0 },
286    { ZWC('.'), 0 },
287};
288#define ZR_MID_ELLIPSIS1_SIZE	\
289    ((int)(sizeof(zr_mid_ellipsis1)/sizeof(zr_mid_ellipsis1[0])))
290
291static const REFRESH_ELEMENT zr_mid_ellipsis2[] = {
292    { ZWC('>'), 0 },
293    { ZWC(' '), 0 },
294};
295#define ZR_MID_ELLIPSIS2_SIZE	\
296    ((int)(sizeof(zr_mid_ellipsis2)/sizeof(zr_mid_ellipsis2[0])))
297
298static const REFRESH_ELEMENT zr_start_ellipsis[] = {
299    { ZWC('>'), 0 },
300    { ZWC('.'), 0 },
301    { ZWC('.'), 0 },
302    { ZWC('.'), 0 },
303    { ZWC('.'), 0 },
304};
305#define ZR_START_ELLIPSIS_SIZE	\
306    ((int)(sizeof(zr_start_ellipsis)/sizeof(zr_start_ellipsis[0])))
307
308/*
309 * Parse the variable zle_highlight to decide how to highlight characters
310 * and regions.  Set defaults for anything not explicitly covered.
311 */
312
313/**/
314static void
315zle_set_highlight(void)
316{
317    char **atrs = getaparam("zle_highlight");
318    int special_atr_on_set = 0;
319    int region_atr_on_set = 0;
320    int isearch_atr_on_set = 0;
321    int suffix_atr_on_set = 0;
322    struct region_highlight *rhp;
323
324    special_atr_on = default_atr_on = 0;
325    if (!region_highlights) {
326	region_highlights = (struct region_highlight *)
327	    zshcalloc(N_SPECIAL_HIGHLIGHTS*sizeof(struct region_highlight));
328	n_region_highlights = N_SPECIAL_HIGHLIGHTS;
329    } else {
330	for (rhp = region_highlights;
331	     rhp < region_highlights + N_SPECIAL_HIGHLIGHTS;
332	     rhp++) {
333	    rhp->atr = 0;
334	}
335    }
336
337    if (atrs) {
338	for (; *atrs; atrs++) {
339	    if (!strcmp(*atrs, "none")) {
340		/* reset attributes for consistency... usually unnecessary */
341		special_atr_on = default_atr_on = 0;
342		special_atr_on_set = region_atr_on_set =
343		    isearch_atr_on_set = suffix_atr_on_set = 1;
344	    } else if (strpfx("default:", *atrs)) {
345		match_highlight(*atrs + 8, &default_atr_on);
346	    } else if (strpfx("special:", *atrs)) {
347		match_highlight(*atrs + 8, &special_atr_on);
348		special_atr_on_set = 1;
349	    } else if (strpfx("region:", *atrs)) {
350		match_highlight(*atrs + 7, &region_highlights[0].atr);
351		region_atr_on_set = 1;
352	    } else if (strpfx("isearch:", *atrs)) {
353		match_highlight(*atrs + 8, &(region_highlights[1].atr));
354		isearch_atr_on_set = 1;
355	    } else if (strpfx("suffix:", *atrs)) {
356		match_highlight(*atrs + 7, &(region_highlights[2].atr));
357		suffix_atr_on_set = 1;
358	    }
359	}
360    }
361
362    /* Defaults */
363    if (!special_atr_on_set)
364	special_atr_on = TXTSTANDOUT;
365    if (!region_atr_on_set)
366	region_highlights[0].atr = TXTSTANDOUT;
367    if (!isearch_atr_on_set)
368	region_highlights[1].atr = TXTUNDERLINE;
369    if (!suffix_atr_on_set)
370	region_highlights[2].atr = TXTBOLDFACE;
371
372    allocate_colour_buffer();
373}
374
375
376/**/
377static void
378zle_free_highlight(void)
379{
380    free_colour_buffer();
381}
382
383/*
384 * Interface to the region_highlight ZLE parameter.
385 * Converts betwen a format like "P32 42 underline,bold" to
386 * the format in the region_highlights variable.  Note that
387 * the region_highlights variable stores the internal (point/mark)
388 * region in element zero.
389 */
390
391/**/
392char **
393get_region_highlight(UNUSED(Param pm))
394{
395    int arrsize = n_region_highlights;
396    char **retarr, **arrp;
397    struct region_highlight *rhp;
398
399    /* region_highlights may not have been set yet */
400    if (arrsize)
401	arrsize -= N_SPECIAL_HIGHLIGHTS;
402    arrp = retarr = (char **)zhalloc((arrsize+1)*sizeof(char *));
403
404    /* ignore special highlighting */
405    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
406	 arrsize--;
407	 rhp++, arrp++) {
408	char digbuf1[DIGBUFSIZE], digbuf2[DIGBUFSIZE];
409	int atrlen = 0, alloclen;
410
411	sprintf(digbuf1, "%d", rhp->start);
412	sprintf(digbuf2, "%d", rhp->end);
413
414	atrlen = output_highlight(rhp->atr, NULL);
415	alloclen = atrlen + strlen(digbuf1) + strlen(digbuf2) +
416	    3; /* 2 spaces, 1 0 */
417	if (rhp->flags & ZRH_PREDISPLAY)
418	    alloclen += 2; /* "P " */
419	*arrp = (char *)zhalloc(alloclen * sizeof(char));
420	/*
421	 * On input we allow a space after the flags.
422	 * I haven't put a space here because I think it's
423	 * marginally easier to have the output always split
424	 * into three words, and then check the first to
425	 * see if there are flags.  However, it's arguable.
426	 */
427	sprintf(*arrp, "%s%s %s ",
428		(rhp->flags & ZRH_PREDISPLAY) ? "P" : "",
429		digbuf1, digbuf2);
430	(void)output_highlight(rhp->atr, *arrp + strlen(*arrp));
431    }
432    *arrp = '\0';
433    return retarr;
434}
435
436
437/*
438 * The parameter system requires the pm argument, but this
439 * may be NULL if called directly.
440 */
441
442/**/
443void
444set_region_highlight(UNUSED(Param pm), char **aval)
445{
446    int len;
447    struct region_highlight *rhp;
448
449    len = aval ? arrlen(aval) : 0;
450    if (n_region_highlights != len + N_SPECIAL_HIGHLIGHTS) {
451	/* no null termination, but include special highlighting at start */
452	n_region_highlights = len + N_SPECIAL_HIGHLIGHTS;
453	region_highlights = (struct region_highlight *)
454	    zrealloc(region_highlights,
455		     sizeof(struct region_highlight) * n_region_highlights);
456    }
457
458    if (!aval)
459	return;
460
461    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
462	 *aval;
463	 rhp++, aval++) {
464	char *strp, *oldstrp;
465
466	oldstrp = *aval;
467	if (*oldstrp == 'P') {
468	    rhp->flags = ZRH_PREDISPLAY;
469	    oldstrp++;
470	}
471	else
472	    rhp->flags = 0;
473	while (inblank(*oldstrp))
474	    oldstrp++;
475
476	rhp->start = (int)zstrtol(oldstrp, &strp, 10);
477	if (strp == oldstrp)
478	    rhp->start = -1;
479
480	while (inblank(*strp))
481	    strp++;
482
483	oldstrp = strp;
484	rhp->end = (int)zstrtol(strp, &strp, 10);
485	if (strp == oldstrp)
486	    rhp->end = -1;
487
488	while (inblank(*strp))
489	    strp++;
490
491	match_highlight(strp, &rhp->atr);
492    }
493}
494
495
496/**/
497void
498unset_region_highlight(Param pm, int exp)
499{
500    if (exp) {
501	set_region_highlight(pm, NULL);
502	stdunsetfn(pm, exp);
503    }
504}
505
506
507/* The last attributes that were on. */
508static int lastatr;
509
510/*
511 * Clear the last attributes that we set:  used when we're going
512 * to be outputting stuff that shouldn't show up as text.
513 */
514static void
515clearattributes(void)
516{
517    if (lastatr) {
518	settextattributes(TXT_ATTR_OFF_FROM_ON(lastatr));
519	lastatr = 0;
520    }
521}
522
523/*
524 * Output a termcap capability, clearing any text attributes so
525 * as not to mess up the display.
526 */
527
528static void
529tcoutclear(int cap)
530{
531    clearattributes();
532    tcout(cap);
533}
534
535/*
536 * Output the character.  This must come from the new video
537 * buffer, nbuf, since we access the multiword buffer nmwbuf
538 * directly.
539 *
540 * curatrp may be NULL, otherwise points to an integer specifying
541 * what attributes were turned on for a character output immediately
542 * before, in order to optimise output of attribute changes.
543 */
544
545/**/
546void
547zwcputc(const REFRESH_ELEMENT *c, int *curatrp)
548{
549    /*
550     * Safety: turn attributes off if last heard of turned on.
551     * This differs from *curatrp, which is an optimisation for
552     * writing lots of stuff at once.
553     */
554#ifdef MULTIBYTE_SUPPORT
555    mbstate_t mbstate;
556    int i;
557    VARARR(char, mbtmp, MB_CUR_MAX + 1);
558#endif
559
560    if (lastatr & ~c->atr) {
561	/* Stuff on we don't want, turn it off */
562	settextattributes(TXT_ATTR_OFF_FROM_ON(lastatr & ~c->atr));
563	lastatr = 0;
564    }
565
566    /*
567     * Don't output "on" attributes in a string of characters with
568     * the same attributes.  Be careful in case a different colour
569     * needs setting.
570     */
571    if ((c->atr & TXT_ATTR_ON_MASK) &&
572	(!curatrp ||
573	 ((*curatrp & TXT_ATTR_ON_VALUES_MASK) !=
574	  (c->atr & TXT_ATTR_ON_VALUES_MASK)))) {
575	/* Record just the control flags we might need to turn off... */
576	lastatr = c->atr & TXT_ATTR_ON_MASK;
577	/* ...but set including the values for colour attributes */
578	settextattributes(c->atr & TXT_ATTR_ON_VALUES_MASK);
579    }
580
581#ifdef MULTIBYTE_SUPPORT
582    if (c->atr & TXT_MULTIWORD_MASK) {
583	/* Multiword glyph stored in nmwbuf */
584	int nchars = nmwbuf[c->chr];
585	REFRESH_CHAR *wcptr = nmwbuf + c->chr + 1;
586
587	memset(&mbstate, 0, sizeof(mbstate_t));
588	while (nchars--) {
589	    if ((i = wcrtomb(mbtmp, (wchar_t)*wcptr++, &mbstate)) > 0)
590		fwrite(mbtmp, i, 1, shout);
591	}
592    } else if (c->chr != WEOF) {
593	memset(&mbstate, 0, sizeof(mbstate_t));
594	if ((i = wcrtomb(mbtmp, (wchar_t)c->chr, &mbstate)) > 0)
595	    fwrite(mbtmp, i, 1, shout);
596    }
597#else
598    fputc(c->chr, shout);
599#endif
600
601    /*
602     * Always output "off" attributes since we only turn off at
603     * the end of a chunk of highlighted text.
604     */
605    if (c->atr & TXT_ATTR_OFF_MASK) {
606	settextattributes(c->atr & TXT_ATTR_OFF_MASK);
607	lastatr &= ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT);
608    }
609    if (curatrp) {
610	/*
611	 * Remember the current attributes:  those that are turned
612	 * on, less those that are turned off again.  Include
613	 * colour attributes here in case the colour changes to
614	 * another non-default one.
615	 */
616	*curatrp = (c->atr & TXT_ATTR_ON_VALUES_MASK) &
617	    ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT);
618    }
619}
620
621static int
622zwcwrite(const REFRESH_STRING s, size_t i)
623{
624    size_t j;
625    int curatr = 0;
626
627    for (j = 0; j < i; j++)
628	zwcputc(s + j, &curatr);
629    return i; /* TODO something better for error indication */
630}
631
632/* Oct/Nov 94: <mason> some code savagely redesigned to fix several bugs -
633   refreshline() & tc_rightcurs() majorly rewritten; zrefresh() fixed -
634   I've put my fingers into just about every routine in here -
635   any queries about updates to mason@primenet.com.au */
636
637static REFRESH_STRING
638    *nbuf = NULL,		/* new video buffer line-by-line array */
639    *obuf = NULL;		/* old video buffer line-by-line array */
640static int more_start,		/* more text before start of screen?	    */
641    more_end,			/* more stuff after end of screen?	    */
642    olnct,			/* previous number of lines		    */
643    ovln,			/* previous video cursor position line	    */
644    lpromptw, rpromptw,		/* prompt widths on screen                  */
645    lpromptwof,			/* left prompt width with real end position */
646    lprompth,			/* lines taken up by the prompt		    */
647    rprompth,			/* right prompt height                      */
648    vcs, vln,			/* video cursor position column & line	    */
649    vmaxln,			/* video maximum number of lines	    */
650    winw, winh, rwinh,		/* window width & height		    */
651    winpos,			/* singlelinezle: line's position in window */
652    winprompt,			/* singlelinezle: part of lprompt showing   */
653    winw_alloc = -1,		/* allocated window width */
654    winh_alloc = -1;		/* allocates window height */
655#ifdef MULTIBYTE_SUPPORT
656static int
657    omw_size,			/* allocated size of omwbuf */
658    nmw_size,			/* allocated size of nmwbuf */
659    nmw_ind;			/* next insert point in nmw_ind */
660#endif
661
662/*
663 * Number of words to allocate in one go for the multiword buffers.
664 */
665#define DEF_MWBUF_ALLOC	(32)
666
667static void
668freevideo(void)
669{
670    if (nbuf) {
671	int ln;
672	for (ln = 0; ln != winh_alloc; ln++) {
673	    zfree(nbuf[ln], (winw_alloc + 2) * sizeof(**nbuf));
674	    zfree(obuf[ln], (winw_alloc + 2) * sizeof(**obuf));
675	}
676	free(nbuf);
677	free(obuf);
678#ifdef MULTIBYTE_SUPPORT
679	zfree(nmwbuf, nmw_size * sizeof(*nmwbuf));
680	zfree(omwbuf, omw_size * sizeof(*omwbuf));
681	omw_size = nmw_size = 0;
682	nmw_ind = 1;
683#endif
684	nbuf = NULL;
685	obuf = NULL;
686	winw_alloc = -1;
687	winh_alloc = -1;
688    }
689}
690
691/**/
692void
693resetvideo(void)
694{
695    int ln;
696
697    winw = zterm_columns;  /* terminal width */
698    if (termflags & TERM_SHORT)
699	winh = 1;
700    else
701	winh = (zterm_lines < 2) ? 24 : zterm_lines;
702    rwinh = zterm_lines;		/* keep the real number of lines */
703    vln = vmaxln = winprompt = 0;
704    winpos = -1;
705    if (winw_alloc != winw || winh_alloc != winh) {
706	freevideo();
707	nbuf = (REFRESH_STRING *)zshcalloc((winh + 1) * sizeof(*nbuf));
708	obuf = (REFRESH_STRING *)zshcalloc((winh + 1) * sizeof(*obuf));
709	nbuf[0] = (REFRESH_STRING)zalloc((winw + 2) * sizeof(**nbuf));
710	obuf[0] = (REFRESH_STRING)zalloc((winw + 2) * sizeof(**obuf));
711
712#ifdef MULTIBYTE_SUPPORT
713	nmw_size = DEF_MWBUF_ALLOC;
714	nmw_ind = 1;
715	nmwbuf = (REFRESH_CHAR *)zalloc(nmw_size * sizeof(*nmwbuf));
716
717	omw_size = DEF_MWBUF_ALLOC;
718	omwbuf = (REFRESH_CHAR *)zalloc(omw_size * sizeof(*omwbuf));
719#endif
720
721	winw_alloc = winw;
722	winh_alloc = winh;
723    }
724    for (ln = 0; ln != winh + 1; ln++) {
725	if (nbuf[ln]) {
726	    nbuf[ln][0] = zr_nl;
727	    nbuf[ln][1] = zr_zr;
728	}
729	if (obuf[ln]) {
730	    obuf[ln][0] = zr_nl;
731	    obuf[ln][1] = zr_zr;
732	}
733    }
734
735    /*
736     * countprompt() now correctly handles multibyte input.
737     */
738    countprompt(lpromptbuf, &lpromptwof, &lprompth, 1);
739    countprompt(rpromptbuf, &rpromptw, &rprompth, 0);
740    if (lpromptwof != winw)
741	lpromptw = lpromptwof;
742    else {
743	lpromptw = 0;
744	lprompth++;
745    }
746
747    if (lpromptw) {
748    	ZR_memset(nbuf[0], zr_sp, lpromptw);
749	ZR_memset(obuf[0], zr_sp, lpromptw);
750	nbuf[0][lpromptw] = obuf[0][lpromptw] = zr_zr;
751    }
752
753    vcs = lpromptw;
754    olnct = nlnct = 0;
755    if (showinglist > 0)
756	showinglist = -2;
757    trashedzle = 0;
758}
759
760/*
761 * Nov 96: <mason> changed to single line scroll
762 */
763
764/**/
765static void
766scrollwindow(int tline)
767{
768    int t0;
769    REFRESH_STRING s;
770
771    s = nbuf[tline];
772    for (t0 = tline; t0 < winh - 1; t0++)
773	nbuf[t0] = nbuf[t0 + 1];
774    nbuf[winh - 1] = s;
775    if (!tline)
776	more_start = 1;
777    return;
778}
779
780/*
781 * Parameters in zrefresh used for communicating with next-line functions.
782 */
783struct rparams {
784    int canscroll;		/* number of lines we are allowed to scroll */
785    int ln;			/* current line we're working on */
786    int more_status;		/* more stuff in status line */
787    int nvcs;			/* video cursor column */
788    int nvln;			/* video cursor line */
789    int tosln;			/* tmp in statusline stuff */
790    REFRESH_STRING s;		/* pointer into the video buffer */
791    REFRESH_STRING sen;		/* pointer to end of the video buffer (eol) */
792};
793typedef struct rparams *Rparams;
794
795static int cleareol,		/* clear to end-of-line (if can't cleareod) */
796    clearf,			/* alwayslastprompt used immediately before */
797    put_rpmpt,			/* whether we should display right-prompt   */
798    oput_rpmpt,			/* whether displayed right-prompt last time */
799    oxtabs,			/* oxtabs - tabs expand to spaces if set    */
800    numscrolls, onumscrolls;
801
802/*
803 * Go to the next line in the main display area.  Return 1 if we should abort
804 * processing the line loop at this point, else 0.
805 *
806 * If wrapped is non-zero, text wrapped, so output newline.
807 * Otherwise, text not wrapped, so output null.
808 */
809static int
810nextline(Rparams rpms, int wrapped)
811{
812    nbuf[rpms->ln][winw+1] = wrapped ? zr_nl : zr_zr;
813    *rpms->s = zr_zr;
814    if (rpms->ln != winh - 1)
815	rpms->ln++;
816    else {
817	if (!rpms->canscroll)	{
818	    if (rpms->nvln != -1 && rpms->nvln != winh - 1
819		&& (numscrolls != onumscrolls - 1
820		    || rpms->nvln <= winh / 2))
821	        return 1;
822	    numscrolls++;
823	    rpms->canscroll = winh / 2;
824	}
825	rpms->canscroll--;
826	scrollwindow(0);
827	if (rpms->nvln != -1)
828	    rpms->nvln--;
829    }
830    if (!nbuf[rpms->ln])
831	nbuf[rpms->ln] = (REFRESH_STRING)zalloc((winw + 2) * sizeof(**nbuf));
832    rpms->s = nbuf[rpms->ln];
833    rpms->sen = rpms->s + winw;
834
835    return 0;
836}
837
838
839/*
840 * Go to the next line in the status area.
841 */
842static void
843snextline(Rparams rpms)
844{
845    *rpms->s = zr_zr;
846    if (rpms->ln != winh - 1)
847	rpms->ln++;
848    else
849	if (rpms->tosln > rpms->ln) {
850	    rpms->tosln--;
851	    if (rpms->nvln > 1) {
852		scrollwindow(0);
853		rpms->nvln--;
854	    } else
855		more_end = 1;
856	} else if (rpms->tosln > 2 && rpms->nvln > 1) {
857	    rpms->tosln--;
858	    if (rpms->tosln <= rpms->nvln) {
859		scrollwindow(0);
860		rpms->nvln--;
861	    } else {
862		scrollwindow(rpms->tosln);
863		more_end = 1;
864	    }
865	} else {
866	    rpms->more_status = 1;
867	    scrollwindow(rpms->tosln + 1);
868	}
869    if (!nbuf[rpms->ln])
870	nbuf[rpms->ln] = (REFRESH_STRING)zalloc((winw + 2) * sizeof(**nbuf));
871    rpms->s = nbuf[rpms->ln];
872    rpms->sen = rpms->s + winw;
873}
874
875
876/**/
877static void
878settextattributes(int atr)
879{
880    if (txtchangeisset(atr, TXTNOBOLDFACE))
881	tsetcap(TCALLATTRSOFF, 0);
882    if (txtchangeisset(atr, TXTNOSTANDOUT))
883	tsetcap(TCSTANDOUTEND, 0);
884    if (txtchangeisset(atr, TXTNOUNDERLINE))
885	tsetcap(TCUNDERLINEEND, 0);
886    if (txtchangeisset(atr, TXTBOLDFACE))
887	tsetcap(TCBOLDFACEBEG, 0);
888    if (txtchangeisset(atr, TXTSTANDOUT))
889	tsetcap(TCSTANDOUTBEG, 0);
890    if (txtchangeisset(atr, TXTUNDERLINE))
891	tsetcap(TCUNDERLINEBEG, 0);
892    if (txtchangeisset(atr, TXTFGCOLOUR|TXTNOFGCOLOUR))
893	set_colour_attribute(atr, COL_SEQ_FG, 0);
894    if (txtchangeisset(atr, TXTBGCOLOUR|TXTNOBGCOLOUR))
895	set_colour_attribute(atr, COL_SEQ_BG, 0);
896}
897
898#ifdef MULTIBYTE_SUPPORT
899/*
900 * Add a multiword glyph at the screen location base.
901 * tptr points to the source and there are ichars characters.
902 */
903static void
904addmultiword(REFRESH_ELEMENT *base, ZLE_STRING_T tptr, int ichars)
905{
906    /* Number of characters needed in buffer incl. count */
907    int iadd = ichars + 1, icnt;
908    REFRESH_CHAR *nmwptr;
909    base->atr |= TXT_MULTIWORD_MASK;
910    /* check allocation */
911    if (nmw_ind + iadd > nmw_size) {
912	/* need more space in buffer */
913	int mw_more = (iadd > DEF_MWBUF_ALLOC) ? iadd :
914	    DEF_MWBUF_ALLOC;
915	nmwbuf = (REFRESH_CHAR *)
916	    zrealloc(nmwbuf, (nmw_size += mw_more) *
917		     sizeof(*nmwbuf));
918    }
919    /* make buffer entry: count, then characters */
920    nmwptr = nmwbuf + nmw_ind;
921    *nmwptr++ = ichars;
922    for (icnt = 0; icnt < ichars; icnt++)
923	*nmwptr++ = tptr[icnt];
924    /* save index and update */
925    base->chr = (wint_t)nmw_ind;
926    nmw_ind += iadd;
927}
928#endif
929
930
931/*
932 * Swap the old and new video buffers, plus any associated multiword
933 * buffers.  The new buffer becomes the old one; the new new buffer
934 * will be filled with the command line next time.
935 */
936static void
937bufswap(void)
938{
939    REFRESH_STRING	*qbuf;
940#ifdef MULTIBYTE_SUPPORT
941    REFRESH_CHAR *qmwbuf;
942    int itmp;
943#endif
944
945    qbuf = nbuf;
946    nbuf = obuf;
947    obuf = qbuf;
948
949#ifdef MULTIBYTE_SUPPORT
950/* likewise multiword buffers */
951    qmwbuf = nmwbuf;
952    nmwbuf = omwbuf;
953    omwbuf = qmwbuf;
954
955    itmp = nmw_size;
956    nmw_size = omw_size;
957    omw_size = itmp;
958
959    nmw_ind = 1;
960#endif
961}
962
963
964/**/
965mod_export void
966zrefresh(void)
967{
968    static int inlist;		/* avoiding recursion			     */
969    int iln;			/* current line as index in loops	     */
970    int t0 = -1;		/* tmp					     */
971    ZLE_STRING_T tmpline,	/* line with added pre/post text	     */
972	t,			/* pointer into the real buffer		     */
973	scs,			/* pointer to cursor position in real buffer */
974	u;			/* pointer for status line stuff	     */
975    int tmpcs, tmpll;		/* ditto cursor position and line length     */
976    int tmppos;			/* t - tmpline				     */
977    int tmpalloced;		/* flag to free tmpline when finished	     */
978    int remetafy;		/* flag that zle line is metafied	     */
979    int txtchange;		/* attributes set after prompts */
980    struct rparams rpms;
981#ifdef MULTIBYTE_SUPPORT
982    int width;			/* width of wide character		     */
983#endif
984
985
986    /* If this is called from listmatches() (indirectly via trashzle()), and *
987     * that was called from the end of zrefresh(), then we don't need to do  *
988     * anything.  All this `inlist' code is actually unnecessary, but it     *
989     * improves speed a little in a common case.                             */
990    if (inlist)
991	return;
992
993    /*
994     * zrefresh() is called from all over the place, so we can't
995     * be sure if the line is metafied for completion or not.
996     */
997    if (zlemetaline != NULL) {
998	remetafy = 1;
999	unmetafy_line();
1000    }
1001    else
1002	remetafy = 0;
1003
1004    if (predisplaylen || postdisplaylen) {
1005	/* There is extra text to display at the start or end of the line */
1006	tmpline = zalloc((zlell + predisplaylen + postdisplaylen)*sizeof(*tmpline));
1007	if (predisplaylen)
1008	    ZS_memcpy(tmpline, predisplay, predisplaylen);
1009	if (zlell)
1010	    ZS_memcpy(tmpline+predisplaylen, zleline, zlell);
1011	if (postdisplaylen)
1012	    ZS_memcpy(tmpline+predisplaylen+zlell, postdisplay,
1013		      postdisplaylen);
1014
1015	tmpcs = zlecs + predisplaylen;
1016	tmpll = predisplaylen + zlell + postdisplaylen;
1017	tmpalloced = 1;
1018    } else {
1019	tmpline = zleline;
1020	tmpcs = zlecs;
1021	tmpll = zlell;
1022	tmpalloced = 0;
1023    }
1024
1025    /* this will create region_highlights if it's still NULL */
1026    zle_set_highlight();
1027
1028    /* check for region between point ($CURSOR) and mark ($MARK) */
1029    if (region_active) {
1030	if (zlecs <= mark) {
1031	    region_highlights[0].start = zlecs;
1032	    region_highlights[0].end = mark;
1033	} else {
1034	    region_highlights[0].start = mark;
1035	    region_highlights[0].end = zlecs;
1036	}
1037    } else {
1038	region_highlights[0].start = region_highlights[0].end = -1;
1039    }
1040    /* check for isearch string to highlight */
1041    if (isearch_active) {
1042	region_highlights[1].start = isearch_startpos;
1043	region_highlights[1].end = isearch_endpos;
1044    } else {
1045	region_highlights[1].start = region_highlights[1].end = -1;
1046    }
1047    /* check for an active completion suffix */
1048    if (suffixnoinslen) {
1049	region_highlights[2].start = zlecs - suffixnoinslen;
1050	region_highlights[2].end = zlecs;
1051    } else {
1052	region_highlights[2].start = region_highlights[2].end = -1;
1053    }
1054
1055    if (clearlist && listshown > 0) {
1056	if (tccan(TCCLEAREOD)) {
1057	    int ovln = vln, ovcs = vcs;
1058	    REFRESH_STRING nb = nbuf[vln];
1059
1060	    nbuf[vln] = obuf[vln];
1061	    moveto(nlnct, 0);
1062	    tcoutclear(TCCLEAREOD);
1063	    moveto(ovln, ovcs);
1064	    nbuf[vln] = nb;
1065	} else {
1066	    invalidatelist();
1067	    moveto(0, 0);
1068	    clearflag = 0;
1069	    resetneeded = 1;
1070	}
1071	listshown = lastlistlen = 0;
1072	if (showinglist != -2)
1073	    showinglist = 0;
1074    }
1075    clearlist = 0;
1076
1077#ifdef HAVE_SELECT
1078    cost = 0;			/* reset */
1079#endif
1080
1081/* Nov 96: <mason>  I haven't checked how complete this is.  sgtty stuff may
1082   or may not work */
1083#if defined(SGTABTYPE)
1084    oxtabs = ((SGTTYFLAG & SGTABTYPE) == SGTABTYPE);
1085#else
1086    oxtabs = 0;
1087#endif
1088
1089    cleareol = 0;		/* unset */
1090    more_start = more_end = 0;	/* unset */
1091    if (isset(SINGLELINEZLE) || zterm_lines < 3
1092	|| (termflags & (TERM_NOUP | TERM_BAD | TERM_UNKNOWN)))
1093	termflags |= TERM_SHORT;
1094    else
1095	termflags &= ~TERM_SHORT;
1096    if (resetneeded) {
1097	onumscrolls = 0;
1098	zsetterm();
1099#ifdef TIOCGWINSZ
1100	if (winchanged) {
1101	    moveto(0, 0);
1102	    t0 = olnct;		/* this is to clear extra lines even when */
1103	    winchanged = 0;	/* the terminal cannot TCCLEAREOD	  */
1104	    listshown = 0;
1105	}
1106#endif
1107	/* we probably should only have explicitly set attributes */
1108	tsetcap(TCALLATTRSOFF, 0);
1109	tsetcap(TCSTANDOUTEND, 0);
1110	tsetcap(TCUNDERLINEEND, 0);
1111	/* cheat on attribute unset */
1112	txtunset(TXTBOLDFACE|TXTSTANDOUT|TXTUNDERLINE);
1113
1114	if (trashedzle && !clearflag)
1115	    reexpandprompt();
1116	resetvideo();
1117	resetneeded = 0;	/* unset */
1118	oput_rpmpt = 0;		/* no right-prompt currently on screen */
1119
1120        if (!clearflag) {
1121            if (tccan(TCCLEAREOD))
1122                tcoutclear(TCCLEAREOD);
1123            else
1124                cleareol = 1;   /* request: clear to end of line */
1125	    if (listshown > 0)
1126		listshown = 0;
1127	}
1128        if (t0 > -1)
1129            olnct = (t0 < winh) ? t0 : winh;
1130        if (termflags & TERM_SHORT)
1131            vcs = 0;
1132	else if (!clearflag && lpromptbuf[0]) {
1133            zputs(lpromptbuf, shout);
1134	    if (lpromptwof == winw)
1135		zputs("\n", shout);	/* works with both hasam and !hasam */
1136	} else {
1137	    txtchange = pmpt_attr;
1138	    settextattributes(txtchange);
1139	}
1140	if (clearflag) {
1141	    zputc(&zr_cr);
1142	    vcs = 0;
1143	    moveto(0, lpromptw);
1144	}
1145	fflush(shout);
1146	clearf = clearflag;
1147    } else if (winw != zterm_columns || rwinh != zterm_lines)
1148	resetvideo();
1149
1150/* now winw equals columns and winh equals lines
1151   width comparisons can be made with winw, height comparisons with winh */
1152
1153    if (termflags & TERM_SHORT) {
1154	singlerefresh(tmpline, tmpll, tmpcs);
1155	goto singlelineout;
1156    }
1157
1158    if (tmpcs < 0) {
1159#ifdef DEBUG
1160	fprintf(stderr, "BUG: negative cursor position\n");
1161	fflush(stderr);
1162#endif
1163	tmpcs = 0;
1164    }
1165    scs = tmpline + tmpcs;
1166    numscrolls = 0;
1167
1168/* first, we generate the video line buffers so we know what to put on
1169   the screen - also determine final cursor position (nvln, nvcs) */
1170
1171    /* Deemed necessary by PWS 1995/05/15 due to kill-line problems */
1172    if (!*nbuf)
1173	*nbuf = (REFRESH_STRING)zalloc((winw + 2) * sizeof(**nbuf));
1174
1175    memset(&rpms, 0, sizeof(rpms));
1176    rpms.nvln = -1;
1177
1178    rpms.s = nbuf[rpms.ln = 0] + lpromptw;
1179    rpms.sen = *nbuf + winw;
1180    for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) {
1181	int base_atr_on = default_atr_on, base_atr_off = 0, ireg;
1182	int all_atr_on, all_atr_off;
1183	struct region_highlight *rhp;
1184	/*
1185	 * Calculate attribute based on region.
1186	 */
1187	for (ireg = 0, rhp = region_highlights;
1188	     ireg < n_region_highlights;
1189	     ireg++, rhp++) {
1190	    int offset;
1191	    if (rhp->flags & ZRH_PREDISPLAY)
1192		offset = 0;	/* include predisplay in start end */
1193	    else
1194		offset = predisplaylen; /* increment over it */
1195	    if (rhp->start + offset <= tmppos &&
1196		tmppos < rhp->end + offset) {
1197		if (rhp->atr & (TXTFGCOLOUR|TXTBGCOLOUR)) {
1198		    /* override colour with later entry */
1199		    base_atr_on = (base_atr_on & ~TXT_ATTR_ON_VALUES_MASK) |
1200			rhp->atr;
1201		} else {
1202		    /* no colour set yet */
1203		    base_atr_on |= rhp->atr;
1204		}
1205		if (tmppos == rhp->end + offset - 1 ||
1206		    tmppos == tmpll - 1)
1207		    base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr);
1208	    }
1209	}
1210	if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
1211	    /* keep colours from special attributes */
1212	    all_atr_on = special_atr_on |
1213		(base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK);
1214	} else {
1215	    /* keep colours from standard attributes */
1216	    all_atr_on = special_atr_on | base_atr_on;
1217	}
1218	all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
1219
1220	if (t == scs)			/* if cursor is here, remember it */
1221	    rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln];
1222
1223	if (*t == ZWC('\n')){		/* newline */
1224	    /* text not wrapped */
1225	    if (nextline(&rpms, 0))
1226		break;
1227	} else if (*t == ZWC('\t')) {		/* tab */
1228	    t0 = rpms.s - nbuf[rpms.ln];
1229	    if ((t0 | 7) + 1 >= winw) {
1230		/* text wrapped */
1231		if (nextline(&rpms, 1))
1232		    break;
1233	    } else {
1234		do {
1235		    rpms.s->chr = ZWC(' ');
1236		    rpms.s->atr = base_atr_on;
1237		    rpms.s++;
1238		} while ((++t0) & 7);
1239		rpms.s[-1].atr |= base_atr_off;
1240	    }
1241	}
1242#ifdef MULTIBYTE_SUPPORT
1243	else if (
1244#ifdef __STDC_ISO_10646__
1245		 !ZSH_INVALID_WCHAR_TEST(*t) &&
1246#endif
1247		 iswprint(*t) && (width = WCWIDTH(*t)) > 0) {
1248	    int ichars;
1249	    if (width > rpms.sen - rpms.s) {
1250		int started = 0;
1251		/*
1252		 * Too wide to fit.  Insert spaces to end of current line.
1253		 */
1254		do {
1255		    rpms.s->chr = ZWC(' ');
1256		    if (!started)
1257			started = 1;
1258		    rpms.s->atr = all_atr_on;
1259		    rpms.s++;
1260		} while (rpms.s < rpms.sen);
1261		if (started)
1262		    rpms.s[-1].atr |= all_atr_off;
1263		if (nextline(&rpms, 1))
1264		    break;
1265		if (t == scs) {
1266		    /* Update cursor to this point */
1267		    rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln];
1268		}
1269	    }
1270	    if (isset(COMBININGCHARS) && IS_BASECHAR(*t)) {
1271		/*
1272		 * Look for combining characters.
1273		 */
1274		for (ichars = 1; tmppos + ichars < tmpll; ichars++) {
1275		    if (!IS_COMBINING(t[ichars]))
1276			break;
1277		}
1278	    } else
1279		ichars = 1;
1280	    if (width > rpms.sen - rpms.s || width == 0) {
1281		/*
1282		 * The screen width is too small to fit even one
1283		 * occurrence.
1284		 */
1285		rpms.s->chr = ZWC('?');
1286		rpms.s->atr = all_atr_on | all_atr_off;
1287		rpms.s++;
1288	    } else {
1289		/* We can fit it without reaching the end of the line. */
1290		/*
1291		 * As we don't actually output the WEOF, we attach
1292		 * any off attributes to the character itself.
1293		 */
1294		rpms.s->atr = base_atr_on | base_atr_off;
1295		if (ichars > 1) {
1296		    /*
1297		     * Glyph includes combining characters.
1298		     * Write these into the multiword buffer and put
1299		     * the index into the value at the screen location.
1300		     */
1301		    addmultiword(rpms.s, t, ichars);
1302		} else {
1303		    /* Single wide character */
1304		    rpms.s->chr = *t;
1305		}
1306		rpms.s++;
1307		while (--width > 0) {
1308		    rpms.s->chr = WEOF;
1309		    /* Not used, but be consistent... */
1310		    rpms.s->atr = base_atr_on | base_atr_off;
1311		    rpms.s++;
1312		}
1313	    }
1314	    if (ichars > 1) {
1315		/* allow for normal increment */
1316		tmppos += ichars - 1;
1317		t += ichars - 1;
1318	    }
1319	}
1320#endif
1321	else if (ZC_icntrl(*t)
1322#ifdef MULTIBYTE_SUPPORT
1323		 && (unsigned)*t <= 0xffU
1324#endif
1325	    ) {	/* other control character */
1326	    rpms.s->chr = ZWC('^');
1327	    rpms.s->atr = all_atr_on;
1328	    rpms.s++;
1329	    if (rpms.s == rpms.sen) {
1330		/* text wrapped */
1331		rpms.s[-1].atr |= all_atr_off;
1332		if (nextline(&rpms, 1))
1333		    break;
1334	    }
1335	    rpms.s->chr = (((unsigned int)*t & ~0x80u) > 31) ?
1336		ZWC('?') : (*t | ZWC('@'));
1337	    rpms.s->atr = all_atr_on | all_atr_off;
1338	    rpms.s++;
1339	}
1340#ifdef MULTIBYTE_SUPPORT
1341	else {
1342	    /*
1343	     * Not printable or zero width.
1344	     * Resort to hackery.
1345	     */
1346	    char dispchars[11];
1347	    char *dispptr = dispchars;
1348	    wchar_t wc;
1349	    int started = 0;
1350
1351#ifdef __STDC_ISO_10646__
1352	    if (ZSH_INVALID_WCHAR_TEST(*t)) {
1353		int c = ZSH_INVALID_WCHAR_TO_INT(*t);
1354		sprintf(dispchars, "<%.02x>", c);
1355	    } else
1356#endif
1357	    if ((unsigned)*t > 0xffffU) {
1358		sprintf(dispchars, "<%.08x>", (unsigned)*t);
1359	    } else {
1360		sprintf(dispchars, "<%.04x>", (unsigned)*t);
1361	    }
1362	    while (*dispptr) {
1363		if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */)
1364		{
1365		    rpms.s->chr = wc;
1366		    if (!started)
1367			started = 1;
1368		    rpms.s->atr = all_atr_on;
1369		    rpms.s++;
1370		    if (rpms.s == rpms.sen) {
1371			/* text wrapped */
1372			if (started) {
1373			    rpms.s[-1].atr |= all_atr_off;
1374			    started = 0;
1375			}
1376			if (nextline(&rpms, 1))
1377			    break;
1378		    }
1379		}
1380		dispptr++;
1381	    }
1382	    if (started)
1383		rpms.s[-1].atr |= all_atr_off;
1384	    if (*dispptr) /* nextline said stop processing */
1385		break;
1386	}
1387#else
1388	else {			/* normal character */
1389	    rpms.s->chr = *t;
1390	    rpms.s->atr = base_atr_on | base_atr_off;
1391	    rpms.s++;
1392	}
1393#endif
1394	if (rpms.s == rpms.sen) {
1395	    /* text wrapped */
1396	    if (nextline(&rpms, 1))
1397		break;
1398	}
1399    }
1400
1401/* if we're really on the next line, don't fake it; do everything properly */
1402    if (t == scs &&
1403	(rpms.nvcs = rpms.s - (nbuf[rpms.nvln = rpms.ln])) == winw) {
1404	/* text wrapped */
1405	(void)nextline(&rpms, 1);
1406	*rpms.s = zr_zr;
1407	rpms.nvcs = 0;
1408	rpms.nvln++;
1409    }
1410
1411    if (t != tmpline + tmpll)
1412	more_end = 1;
1413
1414    if (statusline) {
1415	int outll, outsz, all_atr_on, all_atr_off;
1416	char *statusdup = ztrdup(statusline);
1417	ZLE_STRING_T outputline =
1418	    stringaszleline(statusdup, 0, &outll, &outsz, NULL);
1419
1420	all_atr_on = special_atr_on;
1421	all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
1422
1423	rpms.tosln = rpms.ln + 1;
1424	nbuf[rpms.ln][winw + 1] = zr_zr;	/* text not wrapped */
1425	snextline(&rpms);
1426	u = outputline;
1427	for (; u < outputline + outll; u++) {
1428#ifdef MULTIBYTE_SUPPORT
1429	    if (iswprint(*u)) {
1430		int width = WCWIDTH(*u);
1431		/* Handle wide characters as above */
1432		if (width > rpms.sen - rpms.s) {
1433		    do {
1434			*rpms.s++ = zr_sp;
1435		    } while (rpms.s < rpms.sen);
1436		    nbuf[rpms.ln][winw + 1] = zr_nl;
1437		    snextline(&rpms);
1438		}
1439		if (width > rpms.sen - rpms.s) {
1440		    rpms.s->chr = ZWC('?');
1441		    rpms.s->atr = all_atr_on | all_atr_off;
1442		    rpms.s++;
1443		} else {
1444		    rpms.s->chr = *u;
1445		    rpms.s->atr = 0;
1446		    rpms.s++;
1447		    while (--width > 0) {
1448			rpms.s->chr = WEOF;
1449			rpms.s->atr = 0;
1450			rpms.s++;
1451		    }
1452		}
1453	    }
1454	    else
1455#endif
1456	    if (ZC_icntrl(*u)) { /* simplified processing in the status line */
1457		rpms.s->chr = ZWC('^');
1458		rpms.s->atr = all_atr_on;
1459		rpms.s++;
1460		if (rpms.s == rpms.sen) {
1461		    nbuf[rpms.ln][winw + 1] = zr_nl;/* text wrapped */
1462		    snextline(&rpms);
1463		}
1464		rpms.s->chr = (((unsigned int)*u & ~0x80u) > 31)
1465		    ? ZWC('?') : (*u | ZWC('@'));
1466		rpms.s->atr = all_atr_on | all_atr_off;
1467		rpms.s++;
1468	    } else {
1469		rpms.s->chr = *u;
1470		rpms.s->atr = 0;
1471		rpms.s++;
1472	    }
1473	    if (rpms.s == rpms.sen) {
1474		nbuf[rpms.ln][winw + 1] = zr_nl;	/* text wrapped */
1475		snextline(&rpms);
1476	    }
1477	}
1478	if (rpms.s == rpms.sen) {
1479	    /*
1480	     * I suppose we don't modify nbuf[rpms.ln][winw+1] here
1481	     * since we're right at the end?
1482	     */
1483	    snextline(&rpms);
1484	}
1485	zfree(outputline, outsz);
1486	free(statusdup);
1487    }
1488    *rpms.s = zr_zr;
1489
1490/* insert <.... at end of last line if there is more text past end of screen */
1491    if (more_end) {
1492#ifdef MULTIBYTE_SUPPORT
1493	int extra_ellipsis = 0;
1494#endif
1495	if (!statusline)
1496	    rpms.tosln = winh;
1497	rpms.s = nbuf[rpms.tosln - 1];
1498	rpms.sen = rpms.s + winw - 7;
1499	for (; rpms.s < rpms.sen; rpms.s++) {
1500	    if (rpms.s->chr == ZWC('\0')) {
1501		ZR_memset(rpms.s, zr_sp, rpms.sen - rpms.s);
1502		/* make sure we don't trigger the WEOF test */
1503		rpms.sen->chr = ZWC('\0');
1504		break;
1505	    }
1506	}
1507	/* rpms.s is no longer needed */
1508#ifdef MULTIBYTE_SUPPORT
1509	/*
1510	 * Ensure we don't start overwriting in the middle of a wide
1511	 * character.
1512	 */
1513	while(rpms.sen > nbuf[rpms.tosln - 1] && rpms.sen->chr == WEOF) {
1514	    extra_ellipsis++;
1515	    rpms.sen--;
1516	}
1517#endif
1518	ZR_memcpy(rpms.sen, zr_end_ellipsis, ZR_END_ELLIPSIS_SIZE);
1519#ifdef MULTIBYTE_SUPPORT
1520	/* Extend to the end if we backed off for a wide character */
1521	if (extra_ellipsis) {
1522	    rpms.sen += ZR_END_ELLIPSIS_SIZE;
1523	    ZR_memset(rpms.sen, zr_dt, extra_ellipsis);
1524	}
1525#endif
1526	nbuf[rpms.tosln - 1][winw] = nbuf[rpms.tosln - 1][winw + 1] = zr_zr;
1527    }
1528
1529/* insert <....> at end of first status line if status is too big */
1530    if (rpms.more_status) {
1531#ifdef MULTIBYTE_SUPPORT
1532	int extra_ellipsis = 0;
1533#endif
1534	rpms.s = nbuf[rpms.tosln];
1535	rpms.sen = rpms.s + winw - 8;
1536	for (; rpms.s < rpms.sen; rpms.s++) {
1537	    if (rpms.s->chr == ZWC('\0')) {
1538		ZR_memset(rpms.s, zr_sp, rpms.sen - rpms.s);
1539		break;
1540	    }
1541	}
1542	/* rpms.s is no longer needed */
1543#ifdef MULTIBYTE_SUPPORT
1544	/*
1545	 * Ensure we don't start overwriting in the middle of a wide
1546	 * character.
1547	 */
1548	while(rpms.sen > nbuf[rpms.tosln - 1] && rpms.sen->chr == WEOF) {
1549	    extra_ellipsis++;
1550	    rpms.sen--;
1551	}
1552#endif
1553	ZR_memcpy(rpms.sen, zr_mid_ellipsis1, ZR_MID_ELLIPSIS1_SIZE);
1554	rpms.sen += ZR_MID_ELLIPSIS1_SIZE;
1555#ifdef MULTIBYTE_SUPPORT
1556	/* Extend if we backed off for a wide character */
1557	if (extra_ellipsis) {
1558	    ZR_memset(rpms.sen, zr_dt, extra_ellipsis);
1559	    rpms.sen += extra_ellipsis;
1560	}
1561#endif
1562	ZR_memcpy(rpms.sen, zr_mid_ellipsis2, ZR_MID_ELLIPSIS2_SIZE);
1563	nbuf[rpms.tosln][winw] = nbuf[rpms.tosln][winw + 1] = zr_zr;
1564    }
1565
1566    nlnct = rpms.ln + 1;
1567    for (iln = nlnct; iln < winh; iln++) {
1568	zfree(nbuf[iln], (winw + 2) * sizeof(**nbuf));
1569	nbuf[iln] = NULL;
1570    }
1571
1572/* determine whether the right-prompt exists and can fit on the screen */
1573    if (!more_start) {
1574	if (trashedzle && opts[TRANSIENTRPROMPT])
1575	    put_rpmpt = 0;
1576	else
1577	    put_rpmpt = rprompth == 1 && rpromptbuf[0] &&
1578		!strchr(rpromptbuf, '\t') &&
1579		(int)ZR_strlen(nbuf[0]) + rpromptw < winw - 1;
1580    } else {
1581/* insert >.... on first line if there is more text before start of screen */
1582	ZR_memset(nbuf[0], zr_sp, lpromptw);
1583	t0 = winw - lpromptw;
1584	t0 = t0 > ZR_START_ELLIPSIS_SIZE ? ZR_START_ELLIPSIS_SIZE : t0;
1585	ZR_memcpy(nbuf[0] + lpromptw, zr_start_ellipsis, t0);
1586	ZR_memset(nbuf[0] + lpromptw + t0, zr_sp, winw - t0 - lpromptw);
1587	nbuf[0][winw] = nbuf[0][winw + 1] = zr_zr;
1588    }
1589
1590    for (iln = 0; iln < nlnct; iln++) {
1591	/* if we have more lines than last time, clear the newly-used lines */
1592	if (iln >= olnct)
1593	    cleareol = 1;
1594
1595    /* if old line and new line are different,
1596       see if we can insert/delete a line to speed up update */
1597
1598	if (!clearf && iln > 0 && iln < olnct - 1 &&
1599	    !(hasam && vcs == winw) &&
1600	    nbuf[iln] && obuf[iln] &&
1601	    ZR_strncmp(obuf[iln], nbuf[iln], 16)) {
1602	    if (tccan(TCDELLINE) && obuf[iln + 1] &&
1603		obuf[iln + 1][0].chr && nbuf[iln] &&
1604		!ZR_strncmp(obuf[iln + 1], nbuf[iln], 16)) {
1605		moveto(iln, 0);
1606		tcout(TCDELLINE);
1607		zfree(obuf[iln], (winw + 2) * sizeof(**obuf));
1608		for (t0 = iln; t0 != olnct; t0++)
1609		    obuf[t0] = obuf[t0 + 1];
1610		obuf[--olnct] = NULL;
1611	    }
1612	/* don't try to insert a line if olnct = vmaxln (vmaxln is the number
1613	   of lines that have been displayed by this routine) so that we don't
1614	   go off the end of the screen. */
1615
1616	    else if (tccan(TCINSLINE) && olnct < vmaxln && nbuf[iln + 1] &&
1617		     obuf[iln] && !ZR_strncmp(obuf[iln], nbuf[iln + 1], 16)) {
1618		moveto(iln, 0);
1619		tcout(TCINSLINE);
1620		for (t0 = olnct; t0 != iln; t0--)
1621		    obuf[t0] = obuf[t0 - 1];
1622		obuf[iln] = NULL;
1623		olnct++;
1624	    }
1625	}
1626
1627    /* update the single line */
1628	refreshline(iln);
1629
1630    /* output the right-prompt if appropriate */
1631	if (put_rpmpt && !iln && !oput_rpmpt) {
1632	    int attrchange;
1633
1634	    moveto(0, winw - 1 - rpromptw);
1635	    zputs(rpromptbuf, shout);
1636	    vcs = winw - 1;
1637	/* reset character attributes to that set by the main prompt */
1638	    txtchange = pmpt_attr;
1639	    /*
1640	     * Keep attributes that have actually changed,
1641	     * which are ones off in rpmpt_attr and on in
1642	     * pmpt_attr, and vice versa.
1643	     */
1644	    attrchange = txtchange &
1645		(TXT_ATTR_OFF_FROM_ON(rpmpt_attr) |
1646		 TXT_ATTR_ON_FROM_OFF(rpmpt_attr));
1647	    /*
1648	     * Careful in case the colour changed.
1649	     */
1650	    if (txtchangeisset(txtchange, TXTFGCOLOUR) &&
1651		(!txtchangeisset(rpmpt_attr, TXTFGCOLOUR) ||
1652		 ((txtchange ^ rpmpt_attr) & TXT_ATTR_FG_COL_MASK)))
1653	    {
1654		attrchange |=
1655		    txtchange & (TXTFGCOLOUR | TXT_ATTR_FG_COL_MASK);
1656	    }
1657	    if (txtchangeisset(txtchange, TXTBGCOLOUR) &&
1658		(!txtchangeisset(rpmpt_attr, TXTBGCOLOUR) ||
1659		 ((txtchange ^ rpmpt_attr) & TXT_ATTR_BG_COL_MASK)))
1660	    {
1661		attrchange |=
1662		    txtchange & (TXTBGCOLOUR | TXT_ATTR_BG_COL_MASK);
1663	    }
1664	    /*
1665	     * Now feed these changes into the usual function,
1666	     * if necessary.
1667	     */
1668	    if (attrchange)
1669		settextattributes(attrchange);
1670	}
1671    }
1672
1673/* if old buffer had extra lines, set them to be cleared and refresh them
1674individually */
1675
1676    if (olnct > nlnct) {
1677	cleareol = 1;
1678	for (iln = nlnct; iln < olnct; iln++)
1679	    refreshline(iln);
1680    }
1681
1682/* reset character attributes */
1683    if (clearf && postedit) {
1684	if ((txtchange = pmpt_attr ? pmpt_attr : rpmpt_attr))
1685	    settextattributes(txtchange);
1686    }
1687    clearf = 0;
1688    oput_rpmpt = put_rpmpt;
1689
1690/* move to the new cursor position */
1691    moveto(rpms.nvln, rpms.nvcs);
1692
1693/* swap old and new buffers - better than freeing/allocating every time */
1694    bufswap();
1695
1696/* store current values so we can use them next time */
1697    ovln = rpms.nvln;
1698    olnct = nlnct;
1699    onumscrolls = numscrolls;
1700    if (nlnct > vmaxln)
1701	vmaxln = nlnct;
1702singlelineout:
1703    fflush(shout);		/* make sure everything is written out */
1704
1705    if (tmpalloced)
1706	zfree(tmpline, tmpll * sizeof(*tmpline));
1707
1708    zle_free_highlight();
1709
1710    /* if we have a new list showing, note it; if part of the list has been
1711    overwritten, redisplay it. We have to metafy line back before calling
1712    completion code */
1713    if (showinglist == -2 || (showinglist > 0 && showinglist < nlnct)) {
1714	if (remetafy) {
1715	    metafy_line();
1716	    remetafy = 0;
1717	}
1718	inlist = 1;
1719	listmatches();
1720	inlist = 0;
1721	zrefresh();
1722    }
1723    if (showinglist == -1)
1724	showinglist = nlnct;
1725
1726    if (remetafy)
1727	metafy_line();
1728}
1729
1730#define tcinscost(X)   (tccan(TCMULTINS) ? tclen[TCMULTINS] : (X)*tclen[TCINS])
1731#define tcdelcost(X)   (tccan(TCMULTDEL) ? tclen[TCMULTDEL] : (X)*tclen[TCDEL])
1732#define tc_delchars(X)	(void) tcmultout(TCDEL, TCMULTDEL, (X))
1733#define tc_inschars(X)	(void) tcmultout(TCINS, TCMULTINS, (X))
1734#define tc_upcurs(X)	(void) tcmultout(TCUP, TCMULTUP, (X))
1735#define tc_leftcurs(X)	(void) tcmultout(TCLEFT, TCMULTLEFT, (X))
1736
1737/*
1738 * Once again, in the multibyte case the arguments must be in the
1739 * order:  element of old video array, element of new video array.
1740 */
1741static int
1742wpfxlen(const REFRESH_ELEMENT *olds, const REFRESH_ELEMENT *news)
1743{
1744    int i = 0;
1745
1746    while (olds->chr && ZR_equal(*olds, *news))
1747	olds++, news++, i++;
1748    return i;
1749}
1750
1751/* refresh one line, using whatever speed-up tricks are provided by the tty */
1752
1753/**/
1754static void
1755refreshline(int ln)
1756{
1757    REFRESH_STRING nl, ol, p1;	/* line buffer pointers			 */
1758    int ccs = 0,		/* temporary count for cursor position	 */
1759	char_ins = 0,		/* number of characters inserted/deleted */
1760	col_cleareol,		/* clear to end-of-line from this column */
1761	i, j,			/* tmp					 */
1762	ins_last,		/* insert pushed last character off line */
1763	nllen, ollen,		/* new and old line buffer lengths	 */
1764	rnllen;			/* real new line buffer length		 */
1765
1766/* 0: setup */
1767    nl = nbuf[ln];
1768    rnllen = nllen = nl ? ZR_strlen(nl) : 0;
1769    if (ln < olnct && obuf[ln]) {
1770	ol = obuf[ln];
1771	ollen = ZR_strlen(ol);
1772    }
1773    else {
1774	static REFRESH_ELEMENT nullchr = { ZWC('\0'), 0 };
1775	ol = &nullchr;
1776	ollen = 0;
1777    }
1778
1779/* optimisation: can easily happen for clearing old lines.  If the terminal has
1780   the capability, then this is the easiest way to skip unnecessary stuff */
1781    if (cleareol && !nllen && !(hasam && ln < nlnct - 1)
1782	&& tccan(TCCLEAREOL)) {
1783	moveto(ln, 0);
1784	tcoutclear(TCCLEAREOL);
1785	return;
1786    }
1787
1788/* 1: pad out the new buffer with spaces to contain _all_ of the characters
1789      which need to be written. do this now to allow some pre-processing */
1790
1791    if (cleareol 		/* request to clear to end of line */
1792	|| (!nllen && (ln != 0 || !put_rpmpt))	/* no line buffer given */
1793	|| (ln == 0 && (put_rpmpt != oput_rpmpt))) {	/* prompt changed */
1794	p1 = zhalloc((winw + 2) * sizeof(*p1));
1795	if (nllen)
1796	    ZR_memcpy(p1, nl, nllen);
1797	ZR_memset(p1 + nllen, zr_sp, winw - nllen);
1798	p1[winw] = zr_zr;
1799	if (nllen < winw)
1800	    p1[winw + 1] = zr_zr;
1801	else
1802	    p1[winw + 1] = nl[winw + 1];
1803	if (ln && nbuf[ln])
1804	    ZR_memcpy(nl, p1, winw + 2);	/* next time obuf will be up-to-date */
1805	else
1806	    nl = p1;		/* don't keep the padding for prompt line */
1807	nllen = winw;
1808    } else if (ollen > nllen) { /* make new line at least as long as old */
1809	p1 = zhalloc((ollen + 1) * sizeof(*p1));
1810	ZR_memcpy(p1, nl, nllen);
1811	ZR_memset(p1 + nllen, zr_sp, ollen - nllen);
1812	p1[ollen] = zr_zr;
1813	nl = p1;
1814	nllen = ollen;
1815    }
1816
1817/* 2: see if we can clear to end-of-line, and if it's faster, work out where
1818   to do it from - we can normally only do so if there's no right-prompt.
1819   With automatic margins, we shouldn't do it if there is another line, in
1820   case it messes up cut and paste. */
1821
1822    if (hasam && ln < nlnct - 1 && rnllen == winw)
1823	col_cleareol = -2;	/* clearing eol would be evil so don't */
1824    else {
1825	col_cleareol = -1;
1826	if (tccan(TCCLEAREOL) && (nllen == winw || put_rpmpt != oput_rpmpt)) {
1827	    for (i = nllen; i && ZR_equal(zr_sp, nl[i - 1]); i--)
1828		;
1829	    for (j = ollen; j && ZR_equal(ol[j - 1], zr_sp); j--)
1830		;
1831	    if ((j > i + tclen[TCCLEAREOL])	/* new buf has enough spaces */
1832		|| (nllen == winw && ZR_equal(zr_sp, nl[winw - 1])))
1833		col_cleareol = i;
1834	}
1835    }
1836
1837/* 2b: first a new trick for automargin niceness - good for cut and paste */
1838
1839    if (hasam && vcs == winw) {
1840	if (nbuf[vln] && nbuf[vln][vcs + 1].chr == ZWC('\n')) {
1841	    vln++, vcs = 1;
1842            if (nbuf[vln]  && nbuf[vln]->chr) {
1843		zputc(nbuf[vln]);
1844	    } else
1845		zputc(&zr_sp);  /* I don't think this should happen */
1846	    if (ln == vln) {	/* better safe than sorry */
1847		nl++;
1848		if (ol->chr)
1849		    ol++;
1850		ccs = 1;
1851	    }			/* else  hmmm... I wonder what happened */
1852	} else {
1853	    vln++, vcs = 0;
1854	    zputc(&zr_nl);
1855	}
1856    }
1857    ins_last = 0;
1858
1859/* 2c: if we're on the first line, start checking at the end of the prompt;
1860   we shouldn't be doing anything within the prompt */
1861
1862    if (ln == 0 && lpromptw) {
1863	i = lpromptw - ccs;
1864	j = ZR_strlen(ol);
1865	nl += i;
1866	ol += (i > j ? j : i);	/* if ol is too short, point it to '\0' */
1867	ccs = lpromptw;
1868    }
1869
1870#ifdef MULTIBYTE_SUPPORT
1871    /*
1872     * Realign to a real character after any jiggery pokery at
1873     * the start of the line.
1874     */
1875    while (nl->chr == WEOF) {
1876	nl++, ccs++, vcs++;
1877	if (ol->chr)
1878	    ol++;
1879    }
1880#endif
1881
1882/* 3: main display loop - write out the buffer using whatever tricks we can */
1883
1884    for (;;) {
1885	int now_off;
1886
1887#ifdef MULTIBYTE_SUPPORT
1888	if ((!nl->chr || nl->chr != WEOF) && (!ol->chr || ol->chr != WEOF)) {
1889#endif
1890	    if (nl->chr && ol->chr && ZR_equal(ol[1], nl[1])) {
1891		/* skip only if second chars match */
1892#ifdef MULTIBYTE_SUPPORT
1893		int ccs_was = ccs;
1894#endif
1895		/* skip past all matching characters */
1896		for (; nl->chr && ZR_equal(*ol, *nl); nl++, ol++, ccs++)
1897		    ;
1898#ifdef MULTIBYTE_SUPPORT
1899		/* Make sure ol and nl are pointing to real characters */
1900		while ((nl->chr == WEOF || ol->chr == WEOF) && ccs > ccs_was) {
1901		    nl--;
1902		    ol--;
1903		    ccs--;
1904		}
1905#endif
1906	    }
1907
1908	    if (!nl->chr) {
1909		if (ccs == winw && hasam && char_ins > 0 && ins_last
1910		    && vcs != winw) {
1911		    nl--;           /* we can assume we can go back here */
1912		    moveto(ln, winw - 1);
1913		    zputc(nl);
1914		    vcs++;
1915		    return;         /* write last character in line */
1916		}
1917		if ((char_ins <= 0) || (ccs >= winw))    /* written everything */
1918		    return;
1919		if (tccan(TCCLEAREOL) && (char_ins >= tclen[TCCLEAREOL])
1920		    && col_cleareol != -2)
1921		    /* we've got junk on the right yet to clear */
1922		    col_cleareol = 0;	/* force a clear to end of line */
1923	    }
1924
1925	    moveto(ln, ccs);	/* move to where we do all output from */
1926
1927	    /* if we can finish quickly, do so */
1928	    if ((col_cleareol >= 0) && (ccs >= col_cleareol)) {
1929		tcoutclear(TCCLEAREOL);
1930		return;
1931	    }
1932
1933	    /* we've written out the new but yet to clear rubbish due to inserts */
1934	    if (!nl->chr) {
1935		i = (winw - ccs < char_ins) ? (winw - ccs) : char_ins;
1936		if (tccan(TCDEL) && (tcdelcost(i) <= i + 1))
1937		    tc_delchars(i);
1938		else {
1939		    vcs += i;
1940		    while (i-- > 0)
1941			zputc(&zr_sp);
1942		}
1943		return;
1944	    }
1945
1946	    /* if we've reached the end of the old buffer, then there are few tricks
1947	       we can do, so we just dump out what we must and clear if we can */
1948	    if (!ol->chr) {
1949		i = (col_cleareol >= 0) ? col_cleareol : nllen;
1950		i -= vcs;
1951		if (i < 0) {
1952		    /*
1953		     * This shouldn't be necessary, but it's better
1954		     * than a crash if there's a bug somewhere else,
1955		     * so report in debug mode.
1956		     */
1957		    DPUTS(1, "BUG: badly calculated old line width in refresh");
1958		    i = 0;
1959		}
1960		zwrite(nl, i);
1961		vcs += i;
1962		if (col_cleareol >= 0)
1963		    tcoutclear(TCCLEAREOL);
1964		return;
1965	    }
1966
1967	    /* inserting & deleting chars: we can if there's no right-prompt */
1968	    if ((ln || !put_rpmpt || !oput_rpmpt)
1969#ifdef MULTIBYTE_SUPPORT
1970		&& ol->chr != WEOF && nl->chr != WEOF
1971#endif
1972		&& nl[1].chr && ol[1].chr && !ZR_equal(ol[1], nl[1])) {
1973
1974		/* deleting characters - see if we can find a match series that
1975		   makes it cheaper to delete intermediate characters
1976		   eg. oldline: hifoobar \ hopefully cheaper here to delete two
1977		   newline: foobar	 / characters, then we have six matches */
1978		if (tccan(TCDEL)) {
1979		    int first = 1;
1980		    for (i = 1; ol[i].chr; i++) {
1981			if (tcdelcost(i) < wpfxlen(ol + i, nl)) {
1982			    /*
1983			     * Some terminals will output the current
1984			     * attributes into cells added at the end by
1985			     * deletions, so turn off text attributes.
1986			     */
1987			    if (first) {
1988				clearattributes();
1989				first = 0;
1990			    }
1991			    tc_delchars(i);
1992			    ol += i;
1993			    char_ins -= i;
1994#ifdef MULTIBYTE_SUPPORT
1995			    while (ol->chr == WEOF) {
1996				ol++;
1997				char_ins--;
1998			    }
1999#endif
2000			    i = 0;
2001			    break;
2002			}
2003		    }
2004		    if (!i)
2005			continue;
2006		}
2007		/*
2008		 * inserting characters - characters pushed off the right
2009		 * should be annihilated, but we don't do this if we're on the
2010		 * last line lest undesired scrolling occurs due to `illegal'
2011		 * characters on screen
2012		 */
2013		if (tccan(TCINS) && (vln != zterm_lines - 1)) {
2014		    /* not on last line */
2015		    for (i = 1; nl[i].chr; i++) {
2016			if (tcinscost(i) < wpfxlen(ol, nl + i)) {
2017			    tc_inschars(i);
2018			    zwrite(nl, i);
2019			    nl += i;
2020#ifdef MULTIBYTE_SUPPORT
2021			    while (nl->chr == WEOF) {
2022				nl++;
2023				i++;
2024			    }
2025#endif
2026			    char_ins += i;
2027			    ccs = (vcs += i);
2028			    /*
2029			     * if we've pushed off the right, truncate
2030			     * oldline
2031			     */
2032			    for (i = 0; ol[i].chr && i < winw - ccs; i++)
2033				;
2034#ifdef MULTIBYTE_SUPPORT
2035			    while (ol[i].chr == WEOF)
2036				i++;
2037			    if (i >= winw - ccs) {
2038				/*
2039				 * Yes, we're over the right.
2040				 * Make sure we truncate at the real
2041				 * character, not a WEOF added to
2042				 * make up the width.
2043				 */
2044				while (ol[i-1].chr == WEOF)
2045				    i--;
2046				ol[i] = zr_zr;
2047				ins_last = 1;
2048			    }
2049#else
2050			    if (i >= winw - ccs) {
2051				ol[i] = zr_zr;
2052				ins_last = 1;
2053			    }
2054#endif
2055			    i = 0;
2056			    break;
2057			}
2058		    }
2059		    if (!i)
2060			continue;
2061		}
2062	    }
2063#ifdef MULTIBYTE_SUPPORT
2064	}
2065#endif
2066    /* we can't do any fancy tricks, so just dump the single character
2067       and keep on trying */
2068#ifdef MULTIBYTE_SUPPORT
2069	/*
2070	 * in case we were tidying up a funny-width character when we
2071	 * reached the end of the new line...
2072	 */
2073	if (!nl->chr)
2074	    break;
2075	do {
2076#endif
2077	    /*
2078	     * If an attribute was on here but isn't any more,
2079	     * output the sequence to turn it off.
2080	     */
2081	    now_off = ol->atr & ~nl->atr & TXT_ATTR_ON_MASK;
2082	    if (now_off)
2083		settextattributes(TXT_ATTR_OFF_FROM_ON(now_off));
2084
2085	    /*
2086	     * This is deliberately called if nl->chr is WEOF
2087	     * in order to keep text attributes consistent.
2088	     * We check for WEOF inside.
2089	     */
2090	    zputc(nl);
2091	    nl++;
2092	    if (ol->chr)
2093	      ol++;
2094	    ccs++, vcs++;
2095#ifdef MULTIBYTE_SUPPORT
2096	    /*
2097	     * Make sure we always overwrite the complete width of
2098	     * a character that was there before.
2099	     */
2100	} while ((ol->chr == WEOF && nl->chr) ||
2101		 (nl->chr == WEOF && ol->chr));
2102#endif
2103    }
2104}
2105
2106/* move the cursor to line ln (relative to the prompt line),
2107   absolute column cl; update vln, vcs - video line and column */
2108
2109/**/
2110void
2111moveto(int ln, int cl)
2112{
2113    const REFRESH_ELEMENT *rep;
2114
2115    if (vcs == winw) {
2116	vln++, vcs = 0;
2117	if (!hasam) {
2118	    zputc(&zr_cr);
2119	    zputc(&zr_nl);
2120	} else {
2121	    if ((vln < nlnct) && nbuf[vln] && nbuf[vln]->chr)
2122		rep = nbuf[vln];
2123	    else
2124		rep = &zr_sp;
2125	    zputc(rep);
2126	    zputc(&zr_cr);
2127	    if ((vln < olnct) && obuf[vln] && obuf[vln]->chr)
2128		*obuf[vln] = *rep;
2129	}
2130    }
2131
2132    if (ln == vln && cl == vcs)
2133	return;
2134
2135/* move up */
2136    if (ln < vln) {
2137	tc_upcurs(vln - ln);
2138	vln = ln;
2139    }
2140/* move down; if we might go off the end of the screen, use newlines
2141   instead of TCDOWN */
2142
2143    while (ln > vln) {
2144	if (vln < vmaxln - 1) {
2145	    if (ln > vmaxln - 1) {
2146		if (tc_downcurs(vmaxln - 1 - vln))
2147		    vcs = 0;
2148		vln = vmaxln - 1;
2149	    } else {
2150		if (tc_downcurs(ln - vln))
2151		    vcs = 0;
2152		vln = ln;
2153		continue;
2154	    }
2155	}
2156	zputc(&zr_cr), vcs = 0; /* safety precaution */
2157	while (ln > vln) {
2158	    zputc(&zr_nl);
2159	    vln++;
2160	}
2161    }
2162
2163    if (cl != vcs)
2164        singmoveto(cl);
2165}
2166
2167/**/
2168mod_export int
2169tcmultout(int cap, int multcap, int ct)
2170{
2171    if (tccan(multcap) && (!tccan(cap) || tclen[multcap] <= tclen[cap] * ct)) {
2172	tcoutarg(multcap, ct);
2173	return 1;
2174    } else if (tccan(cap)) {
2175	while (ct--)
2176	    tcout(cap);
2177	return 1;
2178    }
2179    return 0;
2180}
2181
2182/* ct: number of characters to move across */
2183/**/
2184static void
2185tc_rightcurs(int ct)
2186{
2187    int cl,			/* ``desired'' absolute horizontal position */
2188	i = vcs,		/* cursor position after initial movements  */
2189	j;
2190    REFRESH_STRING t;
2191
2192    cl = ct + vcs;
2193
2194/* do a multright if we can - it's the most reliable */
2195    if (tccan(TCMULTRIGHT)) {
2196	tcoutarg(TCMULTRIGHT, ct);
2197	return;
2198    }
2199
2200/* do an absolute horizontal position if we can */
2201    if (tccan(TCHORIZPOS)) {
2202	tcoutarg(TCHORIZPOS, cl);
2203	return;
2204    }
2205
2206/* XXX: should really check "it" in termcap and use / and % */
2207/* try tabs if tabs are non destructive and multright is not possible */
2208    if (!oxtabs && tccan(TCNEXTTAB) && ((vcs | 7) < cl)) {
2209	i = (vcs | 7) + 1;
2210	tcout(TCNEXTTAB);
2211	for ( ; i + 8 <= cl; i += 8)
2212	    tcout(TCNEXTTAB);
2213	if ((ct = cl - i) == 0) /* number of chars still to move across */
2214	    return;
2215    }
2216
2217/* otherwise _carefully_ write the contents of the video buffer.
2218   if we're anywhere in the prompt, goto the left column and write the whole
2219   prompt out.
2220
2221   If strlen(lpromptbuf) == lpromptw, we can cheat and output
2222   the appropriate chunk of the string.  This test relies on the
2223   fact that any funny business will always make the length of
2224   the string larger than the printing width, so if they're the same
2225   we have only ASCII characters or a single-byte extension of ASCII.
2226   Unfortunately this trick won't work if there are potentially
2227   characters occupying more than one column.  We could flag that
2228   this has happened (since it's not that common to have characters
2229   wider than one column), but for now it's easier not to use the
2230   trick if we are using WCWIDTH() on the prompt.  It's not that
2231   common to be editing in the middle of the prompt anyway, I would
2232   think.
2233   */
2234    if (vln == 0 && i < lpromptw && !(termflags & TERM_SHORT)) {
2235#ifndef MULTIBYTE_SUPPORT
2236	if ((int)strlen(lpromptbuf) == lpromptw)
2237	    fputs(lpromptbuf + i, shout);
2238	else
2239#endif
2240	if (tccan(TCRIGHT) && (tclen[TCRIGHT] * ct <= ztrlen(lpromptbuf)))
2241	    /* it is cheaper to send TCRIGHT than reprint the whole prompt */
2242	    for (ct = lpromptw - i; ct--; )
2243		tcout(TCRIGHT);
2244        else {
2245	    if (i != 0)
2246		zputc(&zr_cr);
2247	    tc_upcurs(lprompth - 1);
2248	    zputs(lpromptbuf, shout);
2249	    if (lpromptwof == winw)
2250		zputs("\n", shout);	/* works with both hasam and !hasam */
2251	}
2252	i = lpromptw;
2253	ct = cl - i;
2254    }
2255
2256    if (nbuf[vln]) {
2257	for (j = 0, t = nbuf[vln]; t->chr && (j < i); j++, t++);
2258	if (j == i)
2259	    for ( ; t->chr && ct; ct--, t++)
2260		zputc(t);
2261    }
2262    while (ct--)
2263	zputc(&zr_sp);	/* not my fault your terminal can't go right */
2264}
2265
2266/**/
2267mod_export int
2268tc_downcurs(int ct)
2269{
2270    int ret = 0;
2271
2272    if (ct && !tcmultout(TCDOWN, TCMULTDOWN, ct)) {
2273	while (ct--)
2274	    zputc(&zr_nl);
2275	zputc(&zr_cr), ret = -1;
2276    }
2277    return ret;
2278}
2279
2280/*
2281 * Output a termcap value using a function defined by "zle -T tc".
2282 * Loosely inspired by subst_string_by_func().
2283 *
2284 * cap is the internal index for the capability; it will be looked up
2285 * in the table and the string passed to the function.
2286 *
2287 * arg is eithr an argument to the capability or -1 if there is none;
2288 * if it is not -1 it will be passed as an additional argument to the
2289 * function.
2290 *
2291 * outc is the output function; currently this is always putshout
2292 * but in principle it may be used to output to a string.
2293 */
2294
2295/**/
2296static void
2297tcout_via_func(int cap, int arg, int (*outc)(int))
2298{
2299    Shfunc tcout_func;
2300    int osc, osm, old_incompfunc;
2301
2302    osc = sfcontext;
2303    osm = stopmsg;
2304    old_incompfunc = incompfunc;
2305
2306    sfcontext = SFC_SUBST;
2307    incompfunc = 0;
2308
2309    if ((tcout_func = getshfunc(tcout_func_name))) {
2310	LinkList l = newlinklist();
2311	char buf[DIGBUFSIZE], *str;
2312
2313	addlinknode(l, tcout_func_name);
2314	addlinknode(l, tccap_get_name(cap));
2315
2316	if (arg != -1) {
2317	    sprintf(buf, "%d", arg);
2318	    addlinknode(l, buf);
2319	}
2320
2321	(void)doshfunc(tcout_func, l, 1);
2322
2323	str = getsparam("REPLY");
2324	if (str) {
2325	    while (*str) {
2326		int chr;
2327		if (*str == Meta) {
2328		    chr = str[1] ^ 32;
2329		    str += 2;
2330		} else {
2331		    chr = *str++;
2332		}
2333		(void)outc(chr);
2334	    }
2335	}
2336    }
2337
2338    sfcontext = osc;
2339    stopmsg = osm;
2340    incompfunc = old_incompfunc;
2341}
2342
2343/**/
2344mod_export void
2345tcout(int cap)
2346{
2347    if (tcout_func_name) {
2348	tcout_via_func(cap, -1, putshout);
2349    } else {
2350	tputs(tcstr[cap], 1, putshout);
2351    }
2352    SELECT_ADD_COST(tclen[cap]);
2353}
2354
2355/**/
2356static void
2357tcoutarg(int cap, int arg)
2358{
2359    char *result;
2360
2361    result = tgoto(tcstr[cap], arg, arg);
2362    if (tcout_func_name) {
2363	tcout_via_func(cap, arg, putshout);
2364    } else {
2365	tputs(result, 1, putshout);
2366    }
2367    SELECT_ADD_COST(strlen(result));
2368}
2369
2370/**/
2371mod_export int
2372clearscreen(UNUSED(char **args))
2373{
2374    tcoutclear(TCCLEARSCREEN);
2375    resetneeded = 1;
2376    clearflag = 0;
2377    return 0;
2378}
2379
2380/**/
2381mod_export int
2382redisplay(UNUSED(char **args))
2383{
2384    moveto(0, 0);
2385    zputc(&zr_cr);		/* extra care */
2386    tc_upcurs(lprompth - 1);
2387    resetneeded = 1;
2388    clearflag = 0;
2389    return 0;
2390}
2391
2392/*
2393 * Show as much of the line buffer as we can in single line mode.
2394 * TBD: all termcap effects are turned off in this mode, so
2395 * there's no point in using character attributes.  We should
2396 * decide what we're going to do and either remove the handling
2397 * from here or enable it in tsetcap().
2398 */
2399
2400/**/
2401static void
2402singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
2403{
2404    REFRESH_STRING vbuf, vp,	/* video buffer and pointer    */
2405	refreshop;	        /* pointer to old video buffer */
2406    int t0,			/* tmp			       */
2407	vsiz,			/* size of new video buffer    */
2408	nvcs = 0,		/* new video cursor column     */
2409	owinpos = winpos,	/* previous window position    */
2410	owinprompt = winprompt;	/* previous winprompt          */
2411#ifdef MULTIBYTE_SUPPORT
2412    int width;			/* width of multibyte character */
2413#endif
2414
2415    nlnct = 1;
2416/* generate the new line buffer completely */
2417    for (vsiz = 1 + lpromptw, t0 = 0; t0 != tmpll; t0++) {
2418	if (tmpline[t0] == ZWC('\t'))
2419	    vsiz = (vsiz | 7) + 2;
2420#ifdef MULTIBYTE_SUPPORT
2421	else if (iswprint(tmpline[t0]) && ((width = WCWIDTH(tmpline[t0])) > 0)) {
2422	    vsiz += width;
2423	    if (isset(COMBININGCHARS) && IS_BASECHAR(tmpline[t0])) {
2424		while (t0 < tmpll-1 && IS_COMBINING(tmpline[t0+1]))
2425		    t0++;
2426	    }
2427	}
2428#endif
2429	else if (ZC_icntrl(tmpline[t0])
2430#ifdef MULTIBYTE_SUPPORT
2431		 && (unsigned)tmpline[t0] <= 0xffU
2432#endif
2433		 )
2434	    vsiz += 2;
2435#ifdef MULTIBYTE_SUPPORT
2436	else
2437	    vsiz += 10;
2438#else
2439	else
2440	    vsiz++;
2441#endif
2442    }
2443    vbuf = (REFRESH_STRING)zalloc(vsiz * sizeof(*vbuf));
2444
2445    if (tmpcs < 0) {
2446#ifdef DEBUG
2447	fprintf(stderr, "BUG: negative cursor position\n");
2448	fflush(stderr);
2449#endif
2450	tmpcs = 0;
2451    }
2452
2453    /* prompt is not directly copied into the video buffer */
2454    ZR_memset(vbuf, zr_sp, lpromptw);
2455    vp = vbuf + lpromptw;
2456    *vp = zr_zr;
2457
2458    for (t0 = 0; t0 < tmpll; t0++) {
2459	int base_atr_on = 0, base_atr_off = 0, ireg;
2460	int all_atr_on, all_atr_off;
2461	struct region_highlight *rhp;
2462	/*
2463	 * Calculate attribute based on region.
2464	 */
2465	for (ireg = 0, rhp = region_highlights;
2466	     ireg < n_region_highlights;
2467	     ireg++, rhp++) {
2468	    int offset;
2469	    if (rhp->flags & ZRH_PREDISPLAY)
2470		offset = 0;	/* include predisplay in start end */
2471	    else
2472		offset = predisplaylen; /* increment over it */
2473	    if (rhp->start + offset <= t0 &&
2474		t0 < rhp->end + offset) {
2475		if (base_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
2476		    /* keep colour already set */
2477		    base_atr_on |= rhp->atr & ~TXT_ATTR_COLOUR_ON_MASK;
2478		} else {
2479		    /* no colour set yet */
2480		    base_atr_on |= rhp->atr;
2481		}
2482		if (t0 == rhp->end + offset - 1 ||
2483		    t0 == tmpll - 1)
2484		    base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr);
2485	    }
2486	}
2487	if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
2488	    /* keep colours from special attributes */
2489	    all_atr_on = special_atr_on |
2490		(base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK);
2491	} else {
2492	    /* keep colours from standard attributes */
2493	    all_atr_on = special_atr_on | base_atr_on;
2494	}
2495	all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
2496
2497	if (tmpline[t0] == ZWC('\t')) {
2498	    for (*vp++ = zr_sp; (vp - vbuf) & 7; )
2499		*vp++ = zr_sp;
2500	    vp[-1].atr |= base_atr_off;
2501	} else if (tmpline[t0] == ZWC('\n')) {
2502	    vp->chr = ZWC('\\');
2503	    vp->atr = all_atr_on;
2504	    vp++;
2505	    vp->chr = ZWC('n');
2506	    vp->atr = all_atr_on | all_atr_off;
2507	    vp++;
2508#ifdef MULTIBYTE_SUPPORT
2509	} else if (iswprint(tmpline[t0]) &&
2510		   (width = WCWIDTH(tmpline[t0])) > 0) {
2511	    int ichars;
2512	    if (isset(COMBININGCHARS) && IS_BASECHAR(tmpline[t0])) {
2513		/*
2514		 * Look for combining characters.
2515		 */
2516		for (ichars = 1; t0 + ichars < tmpll; ichars++) {
2517		    if (!IS_COMBINING(tmpline[t0+ichars]))
2518			break;
2519		}
2520	    } else
2521		ichars = 1;
2522	    vp->atr = base_atr_on | base_atr_off;
2523	    if (ichars > 1)
2524		addmultiword(vp, tmpline+t0, ichars);
2525	    else
2526		vp->chr = tmpline[t0];
2527	    vp++;
2528	    while (--width > 0) {
2529		vp->chr = WEOF;
2530		vp->atr = base_atr_on | base_atr_off;
2531		vp++;
2532	    }
2533	    t0 += ichars - 1;
2534#endif
2535	} else if (ZC_icntrl(tmpline[t0])
2536#ifdef MULTIBYTE_SUPPORT
2537		   && (unsigned)tmpline[t0] <= 0xffU
2538#endif
2539		   ) {
2540	    ZLE_INT_T t = tmpline[++t0];
2541
2542	    vp->chr = ZWC('^');
2543	    vp->atr = all_atr_on;
2544	    vp++;
2545	    vp->chr = (((unsigned int)t & ~0x80u) > 31) ?
2546		ZWC('?') : (t | ZWC('@'));
2547	    vp->atr = all_atr_on | all_atr_off;
2548	    vp++;
2549	}
2550#ifdef MULTIBYTE_SUPPORT
2551	else {
2552	    char dispchars[11];
2553	    char *dispptr = dispchars;
2554	    wchar_t wc;
2555	    int started = 0;
2556
2557	    if ((unsigned)tmpline[t0] > 0xffffU) {
2558		sprintf(dispchars, "<%.08x>", (unsigned)tmpline[t0]);
2559	    } else {
2560		sprintf(dispchars, "<%.04x>", (unsigned)tmpline[t0]);
2561	    }
2562	    while (*dispptr) {
2563		if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */) {
2564		    vp->chr = wc;
2565		    if (!started)
2566			started = 1;
2567		    vp->atr = all_atr_on;
2568		    vp++;
2569		}
2570		dispptr++;
2571	    }
2572	    if (started)
2573		vp[-1].atr |= all_atr_off;
2574	}
2575#else
2576	else {
2577	    vp->chr = tmpline[t0];
2578	    vp->atr = base_atr_on | base_atr_off;
2579	    vp++;
2580	}
2581#endif
2582	if (t0 == tmpcs)
2583	    nvcs = vp - vbuf - 1;
2584    }
2585    if (t0 == tmpcs)
2586	nvcs = vp - vbuf;
2587    *vp = zr_zr;
2588
2589/* determine which part of the new line buffer we want for the display */
2590    if (winpos == -1)
2591	winpos = 0;
2592    if ((winpos && nvcs < winpos + 1) || (nvcs > winpos + winw - 2)) {
2593	if ((winpos = nvcs - ((winw - hasam) / 2)) < 0)
2594	    winpos = 0;
2595    }
2596    if (winpos) {
2597	vbuf[winpos].chr = ZWC('<');	/* line continues to the left */
2598	vbuf[winpos].atr = 0;
2599    }
2600    if ((int)ZR_strlen(vbuf + winpos) > (winw - hasam)) {
2601	vbuf[winpos + winw - hasam - 1].chr = ZWC('>');	/* line continues to right */
2602	vbuf[winpos + winw - hasam - 1].atr = 0;
2603	vbuf[winpos + winw - hasam] = zr_zr;
2604    }
2605    ZR_strcpy(nbuf[0], vbuf + winpos);
2606    zfree(vbuf, vsiz * sizeof(*vbuf));
2607    nvcs -= winpos;
2608
2609    if (winpos < lpromptw) {
2610	/* skip start of buffer corresponding to prompt */
2611	winprompt = lpromptw - winpos;
2612    } else {
2613	/* don't */
2614	winprompt = 0;
2615    }
2616    if (winpos != owinpos && winprompt) {
2617	char *pptr;
2618	int skipping = 0, skipchars = winpos;
2619	/*
2620	 * Need to output such part of the left prompt as fits.
2621	 * Skip the first winpos characters, outputting
2622	 * any characters marked with %{...%}.
2623	 */
2624	singmoveto(0);
2625	MB_METACHARINIT();
2626	for (pptr = lpromptbuf; *pptr; ) {
2627	    if (*pptr == Inpar) {
2628		skipping = 1;
2629		pptr++;
2630	    } else if (*pptr == Outpar) {
2631		skipping = 0;
2632		pptr++;
2633	    } else {
2634		convchar_t cc;
2635		int mblen = MB_METACHARLENCONV(pptr, &cc);
2636		if (skipping || skipchars == 0)
2637		{
2638		    while (mblen) {
2639#ifdef MULTIBYTE_SUPPORT
2640			if (cc == WEOF)
2641			    fputc('?', shout);
2642			else
2643#endif
2644			    if (*pptr == Meta) {
2645				mblen--;
2646				fputc(*++pptr ^ 32, shout);
2647			    } else {
2648				fputc(*pptr, shout);
2649			    }
2650			pptr++;
2651			mblen--;
2652		    }
2653		} else {
2654		    skipchars--;
2655		    pptr += mblen;
2656		}
2657	    }
2658	}
2659	vcs = winprompt;
2660    }
2661
2662/* display the `visible' portion of the line buffer */
2663    t0 = winprompt;
2664    vp = *nbuf + winprompt;
2665    refreshop = *obuf + winprompt;
2666    for (;;) {
2667	/*
2668	 * Skip past all matching characters, but if there used
2669	 * to be a prompt here be careful since all manner of
2670	 * nastiness may be around.
2671	 */
2672	if (vp - *nbuf >= owinprompt)
2673	    for (; vp->chr && ZR_equal(*refreshop, *vp);
2674		 t0++, vp++, refreshop++)
2675		;
2676
2677	if (!vp->chr && !refreshop->chr)
2678	    break;
2679
2680	singmoveto(t0);		/* move to where we do all output from */
2681
2682	if (!refreshop->chr) {
2683	    if ((t0 = ZR_strlen(vp)))
2684		zwrite(vp, t0);
2685	    vcs += t0;
2686	    break;
2687	}
2688	if (!vp->chr) {
2689	    if (tccan(TCCLEAREOL))
2690		tcoutclear(TCCLEAREOL);
2691	    else
2692		for (; refreshop++->chr; vcs++)
2693		    zputc(&zr_sp);
2694	    break;
2695	}
2696	zputc(vp);
2697	vcs++, t0++;
2698	vp++, refreshop++;
2699    }
2700/* move to the new cursor position */
2701    singmoveto(nvcs);
2702
2703    bufswap();
2704}
2705
2706/**/
2707static void
2708singmoveto(int pos)
2709{
2710    if (pos == vcs)
2711	return;
2712
2713/* choose cheapest movements for ttys without multiple movement capabilities -
2714   do this now because it's easier (to code) */
2715
2716    if ((!tccan(TCMULTLEFT) || pos == 0) && (pos <= vcs / 2)) {
2717	zputc(&zr_cr);
2718	vcs = 0;
2719    }
2720
2721    if (pos < vcs)
2722	tc_leftcurs(vcs - pos);
2723    else if (pos > vcs)
2724	tc_rightcurs(pos - vcs);
2725
2726    vcs = pos;
2727}
2728
2729/* Provided for loading the module in a modular fashion */
2730
2731/**/
2732void
2733zle_refresh_boot(void)
2734{
2735}
2736
2737/* Provided for unloading the module in a modular fashion */
2738
2739/**/
2740void
2741zle_refresh_finish(void)
2742{
2743    freevideo();
2744
2745    if (region_highlights)
2746    {
2747	zfree(region_highlights,
2748	      sizeof(struct region_highlight) * n_region_highlights);
2749	region_highlights = NULL;
2750	n_region_highlights = 0;
2751    }
2752}
2753