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    int rprompt_off;		/* Offset of rprompt from right of screen    */
981    struct rparams rpms;
982#ifdef MULTIBYTE_SUPPORT
983    int width;			/* width of wide character		     */
984#endif
985
986
987    /* If this is called from listmatches() (indirectly via trashzle()), and *
988     * that was called from the end of zrefresh(), then we don't need to do  *
989     * anything.  All this `inlist' code is actually unnecessary, but it     *
990     * improves speed a little in a common case.                             */
991    if (inlist)
992	return;
993
994    /*
995     * zrefresh() is called from all over the place, so we can't
996     * be sure if the line is metafied for completion or not.
997     */
998    if (zlemetaline != NULL) {
999	remetafy = 1;
1000	unmetafy_line();
1001    }
1002    else
1003	remetafy = 0;
1004
1005    if (predisplaylen || postdisplaylen) {
1006	/* There is extra text to display at the start or end of the line */
1007	tmpline = zalloc((zlell + predisplaylen + postdisplaylen)*sizeof(*tmpline));
1008	if (predisplaylen)
1009	    ZS_memcpy(tmpline, predisplay, predisplaylen);
1010	if (zlell)
1011	    ZS_memcpy(tmpline+predisplaylen, zleline, zlell);
1012	if (postdisplaylen)
1013	    ZS_memcpy(tmpline+predisplaylen+zlell, postdisplay,
1014		      postdisplaylen);
1015
1016	tmpcs = zlecs + predisplaylen;
1017	tmpll = predisplaylen + zlell + postdisplaylen;
1018	tmpalloced = 1;
1019    } else {
1020	tmpline = zleline;
1021	tmpcs = zlecs;
1022	tmpll = zlell;
1023	tmpalloced = 0;
1024    }
1025
1026    /* this will create region_highlights if it's still NULL */
1027    zle_set_highlight();
1028
1029    /* check for region between point ($CURSOR) and mark ($MARK) */
1030    if (region_active) {
1031	if (zlecs <= mark) {
1032	    region_highlights[0].start = zlecs;
1033	    region_highlights[0].end = mark;
1034	} else {
1035	    region_highlights[0].start = mark;
1036	    region_highlights[0].end = zlecs;
1037	}
1038    } else {
1039	region_highlights[0].start = region_highlights[0].end = -1;
1040    }
1041    /* check for isearch string to highlight */
1042    if (isearch_active) {
1043	region_highlights[1].start = isearch_startpos;
1044	region_highlights[1].end = isearch_endpos;
1045    } else {
1046	region_highlights[1].start = region_highlights[1].end = -1;
1047    }
1048    /* check for an active completion suffix */
1049    if (suffixnoinslen) {
1050	region_highlights[2].start = zlecs - suffixnoinslen;
1051	region_highlights[2].end = zlecs;
1052    } else {
1053	region_highlights[2].start = region_highlights[2].end = -1;
1054    }
1055
1056    if (clearlist && listshown > 0) {
1057	if (tccan(TCCLEAREOD)) {
1058	    int ovln = vln, ovcs = vcs;
1059	    REFRESH_STRING nb = nbuf[vln];
1060
1061	    nbuf[vln] = obuf[vln];
1062	    moveto(nlnct, 0);
1063	    tcoutclear(TCCLEAREOD);
1064	    moveto(ovln, ovcs);
1065	    nbuf[vln] = nb;
1066	} else {
1067	    invalidatelist();
1068	    moveto(0, 0);
1069	    clearflag = 0;
1070	    resetneeded = 1;
1071	}
1072	listshown = lastlistlen = 0;
1073	if (showinglist != -2)
1074	    showinglist = 0;
1075    }
1076    clearlist = 0;
1077
1078#ifdef HAVE_SELECT
1079    cost = 0;			/* reset */
1080#endif
1081
1082/* Nov 96: <mason>  I haven't checked how complete this is.  sgtty stuff may
1083   or may not work */
1084#if defined(SGTABTYPE)
1085    oxtabs = ((SGTTYFLAG & SGTABTYPE) == SGTABTYPE);
1086#else
1087    oxtabs = 0;
1088#endif
1089
1090    cleareol = 0;		/* unset */
1091    more_start = more_end = 0;	/* unset */
1092    if (isset(SINGLELINEZLE) || zterm_lines < 3
1093	|| (termflags & (TERM_NOUP | TERM_BAD | TERM_UNKNOWN)))
1094	termflags |= TERM_SHORT;
1095    else
1096	termflags &= ~TERM_SHORT;
1097    if (resetneeded) {
1098	onumscrolls = 0;
1099	zsetterm();
1100#ifdef TIOCGWINSZ
1101	if (winchanged) {
1102	    moveto(0, 0);
1103	    t0 = olnct;		/* this is to clear extra lines even when */
1104	    winchanged = 0;	/* the terminal cannot TCCLEAREOD	  */
1105	    listshown = 0;
1106	}
1107#endif
1108	/* we probably should only have explicitly set attributes */
1109	tsetcap(TCALLATTRSOFF, 0);
1110	tsetcap(TCSTANDOUTEND, 0);
1111	tsetcap(TCUNDERLINEEND, 0);
1112	/* cheat on attribute unset */
1113	txtunset(TXTBOLDFACE|TXTSTANDOUT|TXTUNDERLINE);
1114
1115	if (trashedzle && !clearflag)
1116	    reexpandprompt();
1117	resetvideo();
1118	resetneeded = 0;	/* unset */
1119	oput_rpmpt = 0;		/* no right-prompt currently on screen */
1120
1121        if (!clearflag) {
1122            if (tccan(TCCLEAREOD))
1123                tcoutclear(TCCLEAREOD);
1124            else
1125                cleareol = 1;   /* request: clear to end of line */
1126	    if (listshown > 0)
1127		listshown = 0;
1128	}
1129        if (t0 > -1)
1130            olnct = (t0 < winh) ? t0 : winh;
1131        if (termflags & TERM_SHORT)
1132            vcs = 0;
1133	else if (!clearflag && lpromptbuf[0]) {
1134            zputs(lpromptbuf, shout);
1135	    if (lpromptwof == winw)
1136		zputs("\n", shout);	/* works with both hasam and !hasam */
1137	} else {
1138	    txtchange = pmpt_attr;
1139	    settextattributes(txtchange);
1140	}
1141	if (clearflag) {
1142	    zputc(&zr_cr);
1143	    vcs = 0;
1144	    moveto(0, lpromptw);
1145	}
1146	fflush(shout);
1147	clearf = clearflag;
1148    } else if (winw != zterm_columns || rwinh != zterm_lines)
1149	resetvideo();
1150
1151/* now winw equals columns and winh equals lines
1152   width comparisons can be made with winw, height comparisons with winh */
1153
1154    if (termflags & TERM_SHORT) {
1155	singlerefresh(tmpline, tmpll, tmpcs);
1156	goto singlelineout;
1157    }
1158
1159    if (tmpcs < 0) {
1160#ifdef DEBUG
1161	fprintf(stderr, "BUG: negative cursor position\n");
1162	fflush(stderr);
1163#endif
1164	tmpcs = 0;
1165    }
1166    scs = tmpline + tmpcs;
1167    numscrolls = 0;
1168
1169/* first, we generate the video line buffers so we know what to put on
1170   the screen - also determine final cursor position (nvln, nvcs) */
1171
1172    /* Deemed necessary by PWS 1995/05/15 due to kill-line problems */
1173    if (!*nbuf)
1174	*nbuf = (REFRESH_STRING)zalloc((winw + 2) * sizeof(**nbuf));
1175
1176    memset(&rpms, 0, sizeof(rpms));
1177    rpms.nvln = -1;
1178
1179    rpms.s = nbuf[rpms.ln = 0] + lpromptw;
1180    rpms.sen = *nbuf + winw;
1181    for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) {
1182	int base_atr_on = default_atr_on, base_atr_off = 0, ireg;
1183	int all_atr_on, all_atr_off;
1184	struct region_highlight *rhp;
1185	/*
1186	 * Calculate attribute based on region.
1187	 */
1188	for (ireg = 0, rhp = region_highlights;
1189	     ireg < n_region_highlights;
1190	     ireg++, rhp++) {
1191	    int offset;
1192	    if (rhp->flags & ZRH_PREDISPLAY)
1193		offset = 0;	/* include predisplay in start end */
1194	    else
1195		offset = predisplaylen; /* increment over it */
1196	    if (rhp->start + offset <= tmppos &&
1197		tmppos < rhp->end + offset) {
1198		if (rhp->atr & (TXTFGCOLOUR|TXTBGCOLOUR)) {
1199		    /* override colour with later entry */
1200		    base_atr_on = (base_atr_on & ~TXT_ATTR_ON_VALUES_MASK) |
1201			rhp->atr;
1202		} else {
1203		    /* no colour set yet */
1204		    base_atr_on |= rhp->atr;
1205		}
1206		if (tmppos == rhp->end + offset - 1 ||
1207		    tmppos == tmpll - 1)
1208		    base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr);
1209	    }
1210	}
1211	if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
1212	    /* keep colours from special attributes */
1213	    all_atr_on = special_atr_on |
1214		(base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK);
1215	} else {
1216	    /* keep colours from standard attributes */
1217	    all_atr_on = special_atr_on | base_atr_on;
1218	}
1219	all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
1220
1221	if (t == scs)			/* if cursor is here, remember it */
1222	    rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln];
1223
1224	if (*t == ZWC('\n')){		/* newline */
1225	    /* text not wrapped */
1226	    if (nextline(&rpms, 0))
1227		break;
1228	} else if (*t == ZWC('\t')) {		/* tab */
1229	    t0 = rpms.s - nbuf[rpms.ln];
1230	    if ((t0 | 7) + 1 >= winw) {
1231		/* text wrapped */
1232		if (nextline(&rpms, 1))
1233		    break;
1234	    } else {
1235		do {
1236		    rpms.s->chr = ZWC(' ');
1237		    rpms.s->atr = base_atr_on;
1238		    rpms.s++;
1239		} while ((++t0) & 7);
1240		rpms.s[-1].atr |= base_atr_off;
1241	    }
1242	}
1243#ifdef MULTIBYTE_SUPPORT
1244	else if (
1245#ifdef __STDC_ISO_10646__
1246		 !ZSH_INVALID_WCHAR_TEST(*t) &&
1247#endif
1248		 iswprint(*t) && (width = WCWIDTH(*t)) > 0) {
1249	    int ichars;
1250	    if (width > rpms.sen - rpms.s) {
1251		int started = 0;
1252		/*
1253		 * Too wide to fit.  Insert spaces to end of current line.
1254		 */
1255		do {
1256		    rpms.s->chr = ZWC(' ');
1257		    if (!started)
1258			started = 1;
1259		    rpms.s->atr = all_atr_on;
1260		    rpms.s++;
1261		} while (rpms.s < rpms.sen);
1262		if (started)
1263		    rpms.s[-1].atr |= all_atr_off;
1264		if (nextline(&rpms, 1))
1265		    break;
1266		if (t == scs) {
1267		    /* Update cursor to this point */
1268		    rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln];
1269		}
1270	    }
1271	    if (isset(COMBININGCHARS) && IS_BASECHAR(*t)) {
1272		/*
1273		 * Look for combining characters.
1274		 */
1275		for (ichars = 1; tmppos + ichars < tmpll; ichars++) {
1276		    if (!IS_COMBINING(t[ichars]))
1277			break;
1278		}
1279	    } else
1280		ichars = 1;
1281	    if (width > rpms.sen - rpms.s || width == 0) {
1282		/*
1283		 * The screen width is too small to fit even one
1284		 * occurrence.
1285		 */
1286		rpms.s->chr = ZWC('?');
1287		rpms.s->atr = all_atr_on | all_atr_off;
1288		rpms.s++;
1289	    } else {
1290		/* We can fit it without reaching the end of the line. */
1291		/*
1292		 * As we don't actually output the WEOF, we attach
1293		 * any off attributes to the character itself.
1294		 */
1295		rpms.s->atr = base_atr_on | base_atr_off;
1296		if (ichars > 1) {
1297		    /*
1298		     * Glyph includes combining characters.
1299		     * Write these into the multiword buffer and put
1300		     * the index into the value at the screen location.
1301		     */
1302		    addmultiword(rpms.s, t, ichars);
1303		} else {
1304		    /* Single wide character */
1305		    rpms.s->chr = *t;
1306		}
1307		rpms.s++;
1308		while (--width > 0) {
1309		    rpms.s->chr = WEOF;
1310		    /* Not used, but be consistent... */
1311		    rpms.s->atr = base_atr_on | base_atr_off;
1312		    rpms.s++;
1313		}
1314	    }
1315	    if (ichars > 1) {
1316		/* allow for normal increment */
1317		tmppos += ichars - 1;
1318		t += ichars - 1;
1319	    }
1320	}
1321#endif
1322	else if (ZC_icntrl(*t)
1323#ifdef MULTIBYTE_SUPPORT
1324		 && (unsigned)*t <= 0xffU
1325#endif
1326	    ) {	/* other control character */
1327	    rpms.s->chr = ZWC('^');
1328	    rpms.s->atr = all_atr_on;
1329	    rpms.s++;
1330	    if (rpms.s == rpms.sen) {
1331		/* text wrapped */
1332		rpms.s[-1].atr |= all_atr_off;
1333		if (nextline(&rpms, 1))
1334		    break;
1335	    }
1336	    rpms.s->chr = (((unsigned int)*t & ~0x80u) > 31) ?
1337		ZWC('?') : (*t | ZWC('@'));
1338	    rpms.s->atr = all_atr_on | all_atr_off;
1339	    rpms.s++;
1340	}
1341#ifdef MULTIBYTE_SUPPORT
1342	else {
1343	    /*
1344	     * Not printable or zero width.
1345	     * Resort to hackery.
1346	     */
1347	    char dispchars[11];
1348	    char *dispptr = dispchars;
1349	    wchar_t wc;
1350	    int started = 0;
1351
1352#ifdef __STDC_ISO_10646__
1353	    if (ZSH_INVALID_WCHAR_TEST(*t)) {
1354		int c = ZSH_INVALID_WCHAR_TO_INT(*t);
1355		sprintf(dispchars, "<%.02x>", c);
1356	    } else
1357#endif
1358	    if ((unsigned)*t > 0xffffU) {
1359		sprintf(dispchars, "<%.08x>", (unsigned)*t);
1360	    } else {
1361		sprintf(dispchars, "<%.04x>", (unsigned)*t);
1362	    }
1363	    while (*dispptr) {
1364		if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */)
1365		{
1366		    rpms.s->chr = wc;
1367		    if (!started)
1368			started = 1;
1369		    rpms.s->atr = all_atr_on;
1370		    rpms.s++;
1371		    if (rpms.s == rpms.sen) {
1372			/* text wrapped */
1373			if (started) {
1374			    rpms.s[-1].atr |= all_atr_off;
1375			    started = 0;
1376			}
1377			if (nextline(&rpms, 1))
1378			    break;
1379		    }
1380		}
1381		dispptr++;
1382	    }
1383	    if (started)
1384		rpms.s[-1].atr |= all_atr_off;
1385	    if (*dispptr) /* nextline said stop processing */
1386		break;
1387	}
1388#else
1389	else {			/* normal character */
1390	    rpms.s->chr = *t;
1391	    rpms.s->atr = base_atr_on | base_atr_off;
1392	    rpms.s++;
1393	}
1394#endif
1395	if (rpms.s == rpms.sen) {
1396	    /* text wrapped */
1397	    if (nextline(&rpms, 1))
1398		break;
1399	}
1400    }
1401
1402/* if we're really on the next line, don't fake it; do everything properly */
1403    if (t == scs &&
1404	(rpms.nvcs = rpms.s - (nbuf[rpms.nvln = rpms.ln])) == winw) {
1405	/* text wrapped */
1406	(void)nextline(&rpms, 1);
1407	*rpms.s = zr_zr;
1408	rpms.nvcs = 0;
1409	rpms.nvln++;
1410    }
1411
1412    if (t != tmpline + tmpll)
1413	more_end = 1;
1414
1415    if (statusline) {
1416	int outll, outsz, all_atr_on, all_atr_off;
1417	char *statusdup = ztrdup(statusline);
1418	ZLE_STRING_T outputline =
1419	    stringaszleline(statusdup, 0, &outll, &outsz, NULL);
1420
1421	all_atr_on = special_atr_on;
1422	all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
1423
1424	rpms.tosln = rpms.ln + 1;
1425	nbuf[rpms.ln][winw + 1] = zr_zr;	/* text not wrapped */
1426	snextline(&rpms);
1427	u = outputline;
1428	for (; u < outputline + outll; u++) {
1429#ifdef MULTIBYTE_SUPPORT
1430	    if (iswprint(*u)) {
1431		int width = WCWIDTH(*u);
1432		/* Handle wide characters as above */
1433		if (width > rpms.sen - rpms.s) {
1434		    do {
1435			*rpms.s++ = zr_sp;
1436		    } while (rpms.s < rpms.sen);
1437		    nbuf[rpms.ln][winw + 1] = zr_nl;
1438		    snextline(&rpms);
1439		}
1440		if (width > rpms.sen - rpms.s) {
1441		    rpms.s->chr = ZWC('?');
1442		    rpms.s->atr = all_atr_on | all_atr_off;
1443		    rpms.s++;
1444		} else {
1445		    rpms.s->chr = *u;
1446		    rpms.s->atr = 0;
1447		    rpms.s++;
1448		    while (--width > 0) {
1449			rpms.s->chr = WEOF;
1450			rpms.s->atr = 0;
1451			rpms.s++;
1452		    }
1453		}
1454	    }
1455	    else
1456#endif
1457	    if (ZC_icntrl(*u)) { /* simplified processing in the status line */
1458		rpms.s->chr = ZWC('^');
1459		rpms.s->atr = all_atr_on;
1460		rpms.s++;
1461		if (rpms.s == rpms.sen) {
1462		    nbuf[rpms.ln][winw + 1] = zr_nl;/* text wrapped */
1463		    snextline(&rpms);
1464		}
1465		rpms.s->chr = (((unsigned int)*u & ~0x80u) > 31)
1466		    ? ZWC('?') : (*u | ZWC('@'));
1467		rpms.s->atr = all_atr_on | all_atr_off;
1468		rpms.s++;
1469	    } else {
1470		rpms.s->chr = *u;
1471		rpms.s->atr = 0;
1472		rpms.s++;
1473	    }
1474	    if (rpms.s == rpms.sen) {
1475		nbuf[rpms.ln][winw + 1] = zr_nl;	/* text wrapped */
1476		snextline(&rpms);
1477	    }
1478	}
1479	if (rpms.s == rpms.sen) {
1480	    /*
1481	     * I suppose we don't modify nbuf[rpms.ln][winw+1] here
1482	     * since we're right at the end?
1483	     */
1484	    snextline(&rpms);
1485	}
1486	zfree(outputline, outsz);
1487	free(statusdup);
1488    }
1489    *rpms.s = zr_zr;
1490
1491/* insert <.... at end of last line if there is more text past end of screen */
1492    if (more_end) {
1493#ifdef MULTIBYTE_SUPPORT
1494	int extra_ellipsis = 0;
1495#endif
1496	if (!statusline)
1497	    rpms.tosln = winh;
1498	rpms.s = nbuf[rpms.tosln - 1];
1499	rpms.sen = rpms.s + winw - 7;
1500	for (; rpms.s < rpms.sen; rpms.s++) {
1501	    if (rpms.s->chr == ZWC('\0')) {
1502		ZR_memset(rpms.s, zr_sp, rpms.sen - rpms.s);
1503		/* make sure we don't trigger the WEOF test */
1504		rpms.sen->chr = ZWC('\0');
1505		break;
1506	    }
1507	}
1508	/* rpms.s is no longer needed */
1509#ifdef MULTIBYTE_SUPPORT
1510	/*
1511	 * Ensure we don't start overwriting in the middle of a wide
1512	 * character.
1513	 */
1514	while(rpms.sen > nbuf[rpms.tosln - 1] && rpms.sen->chr == WEOF) {
1515	    extra_ellipsis++;
1516	    rpms.sen--;
1517	}
1518#endif
1519	ZR_memcpy(rpms.sen, zr_end_ellipsis, ZR_END_ELLIPSIS_SIZE);
1520#ifdef MULTIBYTE_SUPPORT
1521	/* Extend to the end if we backed off for a wide character */
1522	if (extra_ellipsis) {
1523	    rpms.sen += ZR_END_ELLIPSIS_SIZE;
1524	    ZR_memset(rpms.sen, zr_dt, extra_ellipsis);
1525	}
1526#endif
1527	nbuf[rpms.tosln - 1][winw] = nbuf[rpms.tosln - 1][winw + 1] = zr_zr;
1528    }
1529
1530/* insert <....> at end of first status line if status is too big */
1531    if (rpms.more_status) {
1532#ifdef MULTIBYTE_SUPPORT
1533	int extra_ellipsis = 0;
1534#endif
1535	rpms.s = nbuf[rpms.tosln];
1536	rpms.sen = rpms.s + winw - 8;
1537	for (; rpms.s < rpms.sen; rpms.s++) {
1538	    if (rpms.s->chr == ZWC('\0')) {
1539		ZR_memset(rpms.s, zr_sp, rpms.sen - rpms.s);
1540		break;
1541	    }
1542	}
1543	/* rpms.s is no longer needed */
1544#ifdef MULTIBYTE_SUPPORT
1545	/*
1546	 * Ensure we don't start overwriting in the middle of a wide
1547	 * character.
1548	 */
1549	while(rpms.sen > nbuf[rpms.tosln - 1] && rpms.sen->chr == WEOF) {
1550	    extra_ellipsis++;
1551	    rpms.sen--;
1552	}
1553#endif
1554	ZR_memcpy(rpms.sen, zr_mid_ellipsis1, ZR_MID_ELLIPSIS1_SIZE);
1555	rpms.sen += ZR_MID_ELLIPSIS1_SIZE;
1556#ifdef MULTIBYTE_SUPPORT
1557	/* Extend if we backed off for a wide character */
1558	if (extra_ellipsis) {
1559	    ZR_memset(rpms.sen, zr_dt, extra_ellipsis);
1560	    rpms.sen += extra_ellipsis;
1561	}
1562#endif
1563	ZR_memcpy(rpms.sen, zr_mid_ellipsis2, ZR_MID_ELLIPSIS2_SIZE);
1564	nbuf[rpms.tosln][winw] = nbuf[rpms.tosln][winw + 1] = zr_zr;
1565    }
1566
1567    nlnct = rpms.ln + 1;
1568    for (iln = nlnct; iln < winh; iln++) {
1569	zfree(nbuf[iln], (winw + 2) * sizeof(**nbuf));
1570	nbuf[iln] = NULL;
1571    }
1572
1573/* determine whether the right-prompt exists and can fit on the screen */
1574    if (!more_start) {
1575	if (trashedzle && opts[TRANSIENTRPROMPT])
1576	    put_rpmpt = 0;
1577	else {
1578	    put_rpmpt = rprompth == 1 && rpromptbuf[0] &&
1579		!strchr(rpromptbuf, '\t');
1580	    if (put_rpmpt)
1581	    {
1582	      rprompt_off = rprompt_indent;
1583	      /* sanity to avoid horrible things happening */
1584	      if (rprompt_off < 0)
1585		rprompt_off = 0;
1586	      put_rpmpt =
1587		(int)ZR_strlen(nbuf[0]) + rpromptw < winw - rprompt_off;
1588	    }
1589	}
1590    } else {
1591/* insert >.... on first line if there is more text before start of screen */
1592	ZR_memset(nbuf[0], zr_sp, lpromptw);
1593	t0 = winw - lpromptw;
1594	t0 = t0 > ZR_START_ELLIPSIS_SIZE ? ZR_START_ELLIPSIS_SIZE : t0;
1595	ZR_memcpy(nbuf[0] + lpromptw, zr_start_ellipsis, t0);
1596	ZR_memset(nbuf[0] + lpromptw + t0, zr_sp, winw - t0 - lpromptw);
1597	nbuf[0][winw] = nbuf[0][winw + 1] = zr_zr;
1598    }
1599
1600    for (iln = 0; iln < nlnct; iln++) {
1601	/* if we have more lines than last time, clear the newly-used lines */
1602	if (iln >= olnct)
1603	    cleareol = 1;
1604
1605    /* if old line and new line are different,
1606       see if we can insert/delete a line to speed up update */
1607
1608	if (!clearf && iln > 0 && iln < olnct - 1 &&
1609	    !(hasam && vcs == winw) &&
1610	    nbuf[iln] && obuf[iln] &&
1611	    ZR_strncmp(obuf[iln], nbuf[iln], 16)) {
1612	    if (tccan(TCDELLINE) && obuf[iln + 1] &&
1613		obuf[iln + 1][0].chr && nbuf[iln] &&
1614		!ZR_strncmp(obuf[iln + 1], nbuf[iln], 16)) {
1615		moveto(iln, 0);
1616		tcout(TCDELLINE);
1617		zfree(obuf[iln], (winw + 2) * sizeof(**obuf));
1618		for (t0 = iln; t0 != olnct; t0++)
1619		    obuf[t0] = obuf[t0 + 1];
1620		obuf[--olnct] = NULL;
1621	    }
1622	/* don't try to insert a line if olnct = vmaxln (vmaxln is the number
1623	   of lines that have been displayed by this routine) so that we don't
1624	   go off the end of the screen. */
1625
1626	    else if (tccan(TCINSLINE) && olnct < vmaxln && nbuf[iln + 1] &&
1627		     obuf[iln] && !ZR_strncmp(obuf[iln], nbuf[iln + 1], 16)) {
1628		moveto(iln, 0);
1629		tcout(TCINSLINE);
1630		for (t0 = olnct; t0 != iln; t0--)
1631		    obuf[t0] = obuf[t0 - 1];
1632		obuf[iln] = NULL;
1633		olnct++;
1634	    }
1635	}
1636
1637    /* update the single line */
1638	refreshline(iln);
1639
1640    /* output the right-prompt if appropriate */
1641	if (put_rpmpt && !iln && !oput_rpmpt) {
1642	    int attrchange;
1643
1644	    moveto(0, winw - rprompt_off - rpromptw);
1645	    zputs(rpromptbuf, shout);
1646	    vcs = winw - rprompt_off;
1647	/* reset character attributes to that set by the main prompt */
1648	    txtchange = pmpt_attr;
1649	    /*
1650	     * Keep attributes that have actually changed,
1651	     * which are ones off in rpmpt_attr and on in
1652	     * pmpt_attr, and vice versa.
1653	     */
1654	    attrchange = txtchange &
1655		(TXT_ATTR_OFF_FROM_ON(rpmpt_attr) |
1656		 TXT_ATTR_ON_FROM_OFF(rpmpt_attr));
1657	    /*
1658	     * Careful in case the colour changed.
1659	     */
1660	    if (txtchangeisset(txtchange, TXTFGCOLOUR) &&
1661		(!txtchangeisset(rpmpt_attr, TXTFGCOLOUR) ||
1662		 ((txtchange ^ rpmpt_attr) & TXT_ATTR_FG_COL_MASK)))
1663	    {
1664		attrchange |=
1665		    txtchange & (TXTFGCOLOUR | TXT_ATTR_FG_COL_MASK);
1666	    }
1667	    if (txtchangeisset(txtchange, TXTBGCOLOUR) &&
1668		(!txtchangeisset(rpmpt_attr, TXTBGCOLOUR) ||
1669		 ((txtchange ^ rpmpt_attr) & TXT_ATTR_BG_COL_MASK)))
1670	    {
1671		attrchange |=
1672		    txtchange & (TXTBGCOLOUR | TXT_ATTR_BG_COL_MASK);
1673	    }
1674	    /*
1675	     * Now feed these changes into the usual function,
1676	     * if necessary.
1677	     */
1678	    if (attrchange)
1679		settextattributes(attrchange);
1680	}
1681    }
1682
1683/* if old buffer had extra lines, set them to be cleared and refresh them
1684individually */
1685
1686    if (olnct > nlnct) {
1687	cleareol = 1;
1688	for (iln = nlnct; iln < olnct; iln++)
1689	    refreshline(iln);
1690    }
1691
1692/* reset character attributes */
1693    if (clearf && postedit) {
1694	if ((txtchange = pmpt_attr ? pmpt_attr : rpmpt_attr))
1695	    settextattributes(txtchange);
1696    }
1697    clearf = 0;
1698    oput_rpmpt = put_rpmpt;
1699
1700/* move to the new cursor position */
1701    moveto(rpms.nvln, rpms.nvcs);
1702
1703/* swap old and new buffers - better than freeing/allocating every time */
1704    bufswap();
1705
1706/* store current values so we can use them next time */
1707    ovln = rpms.nvln;
1708    olnct = nlnct;
1709    onumscrolls = numscrolls;
1710    if (nlnct > vmaxln)
1711	vmaxln = nlnct;
1712singlelineout:
1713    fflush(shout);		/* make sure everything is written out */
1714
1715    if (tmpalloced)
1716	zfree(tmpline, tmpll * sizeof(*tmpline));
1717
1718    zle_free_highlight();
1719
1720    /* if we have a new list showing, note it; if part of the list has been
1721    overwritten, redisplay it. We have to metafy line back before calling
1722    completion code */
1723    if (showinglist == -2 || (showinglist > 0 && showinglist < nlnct)) {
1724	if (remetafy) {
1725	    metafy_line();
1726	    remetafy = 0;
1727	}
1728	inlist = 1;
1729	listmatches();
1730	inlist = 0;
1731	zrefresh();
1732    }
1733    if (showinglist == -1)
1734	showinglist = nlnct;
1735
1736    if (remetafy)
1737	metafy_line();
1738}
1739
1740#define tcinscost(X)   (tccan(TCMULTINS) ? tclen[TCMULTINS] : (X)*tclen[TCINS])
1741#define tcdelcost(X)   (tccan(TCMULTDEL) ? tclen[TCMULTDEL] : (X)*tclen[TCDEL])
1742#define tc_delchars(X)	(void) tcmultout(TCDEL, TCMULTDEL, (X))
1743#define tc_inschars(X)	(void) tcmultout(TCINS, TCMULTINS, (X))
1744#define tc_upcurs(X)	(void) tcmultout(TCUP, TCMULTUP, (X))
1745#define tc_leftcurs(X)	(void) tcmultout(TCLEFT, TCMULTLEFT, (X))
1746
1747/*
1748 * Once again, in the multibyte case the arguments must be in the
1749 * order:  element of old video array, element of new video array.
1750 */
1751static int
1752wpfxlen(const REFRESH_ELEMENT *olds, const REFRESH_ELEMENT *news)
1753{
1754    int i = 0;
1755
1756    while (olds->chr && ZR_equal(*olds, *news))
1757	olds++, news++, i++;
1758    return i;
1759}
1760
1761/* refresh one line, using whatever speed-up tricks are provided by the tty */
1762
1763/**/
1764static void
1765refreshline(int ln)
1766{
1767    REFRESH_STRING nl, ol, p1;	/* line buffer pointers			 */
1768    int ccs = 0,		/* temporary count for cursor position	 */
1769	char_ins = 0,		/* number of characters inserted/deleted */
1770	col_cleareol,		/* clear to end-of-line from this column */
1771	i, j,			/* tmp					 */
1772	ins_last,		/* insert pushed last character off line */
1773	nllen, ollen,		/* new and old line buffer lengths	 */
1774	rnllen;			/* real new line buffer length		 */
1775
1776/* 0: setup */
1777    nl = nbuf[ln];
1778    rnllen = nllen = nl ? ZR_strlen(nl) : 0;
1779    if (ln < olnct && obuf[ln]) {
1780	ol = obuf[ln];
1781	ollen = ZR_strlen(ol);
1782    }
1783    else {
1784	static REFRESH_ELEMENT nullchr = { ZWC('\0'), 0 };
1785	ol = &nullchr;
1786	ollen = 0;
1787    }
1788
1789/* optimisation: can easily happen for clearing old lines.  If the terminal has
1790   the capability, then this is the easiest way to skip unnecessary stuff */
1791    if (cleareol && !nllen && !(hasam && ln < nlnct - 1)
1792	&& tccan(TCCLEAREOL)) {
1793	moveto(ln, 0);
1794	tcoutclear(TCCLEAREOL);
1795	return;
1796    }
1797
1798/* 1: pad out the new buffer with spaces to contain _all_ of the characters
1799      which need to be written. do this now to allow some pre-processing */
1800
1801    if (cleareol 		/* request to clear to end of line */
1802	|| (!nllen && (ln != 0 || !put_rpmpt))	/* no line buffer given */
1803	|| (ln == 0 && (put_rpmpt != oput_rpmpt))) {	/* prompt changed */
1804	p1 = zhalloc((winw + 2) * sizeof(*p1));
1805	if (nllen)
1806	    ZR_memcpy(p1, nl, nllen);
1807	ZR_memset(p1 + nllen, zr_sp, winw - nllen);
1808	p1[winw] = zr_zr;
1809	if (nllen < winw)
1810	    p1[winw + 1] = zr_zr;
1811	else
1812	    p1[winw + 1] = nl[winw + 1];
1813	if (ln && nbuf[ln])
1814	    ZR_memcpy(nl, p1, winw + 2);	/* next time obuf will be up-to-date */
1815	else
1816	    nl = p1;		/* don't keep the padding for prompt line */
1817	nllen = winw;
1818    } else if (ollen > nllen) { /* make new line at least as long as old */
1819	p1 = zhalloc((ollen + 1) * sizeof(*p1));
1820	ZR_memcpy(p1, nl, nllen);
1821	ZR_memset(p1 + nllen, zr_sp, ollen - nllen);
1822	p1[ollen] = zr_zr;
1823	nl = p1;
1824	nllen = ollen;
1825    }
1826
1827/* 2: see if we can clear to end-of-line, and if it's faster, work out where
1828   to do it from - we can normally only do so if there's no right-prompt.
1829   With automatic margins, we shouldn't do it if there is another line, in
1830   case it messes up cut and paste. */
1831
1832    if (hasam && ln < nlnct - 1 && rnllen == winw)
1833	col_cleareol = -2;	/* clearing eol would be evil so don't */
1834    else {
1835	col_cleareol = -1;
1836	if (tccan(TCCLEAREOL) && (nllen == winw || put_rpmpt != oput_rpmpt)) {
1837	    for (i = nllen; i && ZR_equal(zr_sp, nl[i - 1]); i--)
1838		;
1839	    for (j = ollen; j && ZR_equal(ol[j - 1], zr_sp); j--)
1840		;
1841	    if ((j > i + tclen[TCCLEAREOL])	/* new buf has enough spaces */
1842		|| (nllen == winw && ZR_equal(zr_sp, nl[winw - 1])))
1843		col_cleareol = i;
1844	}
1845    }
1846
1847/* 2b: first a new trick for automargin niceness - good for cut and paste */
1848
1849    if (hasam && vcs == winw) {
1850	if (nbuf[vln] && nbuf[vln][vcs + 1].chr == ZWC('\n')) {
1851	    vln++, vcs = 1;
1852            if (nbuf[vln]  && nbuf[vln]->chr) {
1853		zputc(nbuf[vln]);
1854	    } else
1855		zputc(&zr_sp);  /* I don't think this should happen */
1856	    if (ln == vln) {	/* better safe than sorry */
1857		nl++;
1858		if (ol->chr)
1859		    ol++;
1860		ccs = 1;
1861	    }			/* else  hmmm... I wonder what happened */
1862	} else {
1863	    vln++, vcs = 0;
1864	    zputc(&zr_nl);
1865	}
1866    }
1867    ins_last = 0;
1868
1869/* 2c: if we're on the first line, start checking at the end of the prompt;
1870   we shouldn't be doing anything within the prompt */
1871
1872    if (ln == 0 && lpromptw) {
1873	i = lpromptw - ccs;
1874	j = ZR_strlen(ol);
1875	nl += i;
1876	ol += (i > j ? j : i);	/* if ol is too short, point it to '\0' */
1877	ccs = lpromptw;
1878    }
1879
1880#ifdef MULTIBYTE_SUPPORT
1881    /*
1882     * Realign to a real character after any jiggery pokery at
1883     * the start of the line.
1884     */
1885    while (nl->chr == WEOF) {
1886	nl++, ccs++, vcs++;
1887	if (ol->chr)
1888	    ol++;
1889    }
1890#endif
1891
1892/* 3: main display loop - write out the buffer using whatever tricks we can */
1893
1894    for (;;) {
1895	int now_off;
1896
1897#ifdef MULTIBYTE_SUPPORT
1898	if ((!nl->chr || nl->chr != WEOF) && (!ol->chr || ol->chr != WEOF)) {
1899#endif
1900	    if (nl->chr && ol->chr && ZR_equal(ol[1], nl[1])) {
1901		/* skip only if second chars match */
1902#ifdef MULTIBYTE_SUPPORT
1903		int ccs_was = ccs;
1904#endif
1905		/* skip past all matching characters */
1906		for (; nl->chr && ZR_equal(*ol, *nl); nl++, ol++, ccs++)
1907		    ;
1908#ifdef MULTIBYTE_SUPPORT
1909		/* Make sure ol and nl are pointing to real characters */
1910		while ((nl->chr == WEOF || ol->chr == WEOF) && ccs > ccs_was) {
1911		    nl--;
1912		    ol--;
1913		    ccs--;
1914		}
1915#endif
1916	    }
1917
1918	    if (!nl->chr) {
1919		if (ccs == winw && hasam && char_ins > 0 && ins_last
1920		    && vcs != winw) {
1921		    nl--;           /* we can assume we can go back here */
1922		    moveto(ln, winw - 1);
1923		    zputc(nl);
1924		    vcs++;
1925		    return;         /* write last character in line */
1926		}
1927		if ((char_ins <= 0) || (ccs >= winw))    /* written everything */
1928		    return;
1929		if (tccan(TCCLEAREOL) && (char_ins >= tclen[TCCLEAREOL])
1930		    && col_cleareol != -2)
1931		    /* we've got junk on the right yet to clear */
1932		    col_cleareol = 0;	/* force a clear to end of line */
1933	    }
1934
1935	    moveto(ln, ccs);	/* move to where we do all output from */
1936
1937	    /* if we can finish quickly, do so */
1938	    if ((col_cleareol >= 0) && (ccs >= col_cleareol)) {
1939		tcoutclear(TCCLEAREOL);
1940		return;
1941	    }
1942
1943	    /* we've written out the new but yet to clear rubbish due to inserts */
1944	    if (!nl->chr) {
1945		i = (winw - ccs < char_ins) ? (winw - ccs) : char_ins;
1946		if (tccan(TCDEL) && (tcdelcost(i) <= i + 1))
1947		    tc_delchars(i);
1948		else {
1949		    vcs += i;
1950		    while (i-- > 0)
1951			zputc(&zr_sp);
1952		}
1953		return;
1954	    }
1955
1956	    /* if we've reached the end of the old buffer, then there are few tricks
1957	       we can do, so we just dump out what we must and clear if we can */
1958	    if (!ol->chr) {
1959		i = (col_cleareol >= 0) ? col_cleareol : nllen;
1960		i -= vcs;
1961		if (i < 0) {
1962		    /*
1963		     * This shouldn't be necessary, but it's better
1964		     * than a crash if there's a bug somewhere else,
1965		     * so report in debug mode.
1966		     */
1967		    DPUTS(1, "BUG: badly calculated old line width in refresh");
1968		    i = 0;
1969		}
1970		zwrite(nl, i);
1971		vcs += i;
1972		if (col_cleareol >= 0)
1973		    tcoutclear(TCCLEAREOL);
1974		return;
1975	    }
1976
1977	    /* inserting & deleting chars: we can if there's no right-prompt */
1978	    if ((ln || !put_rpmpt || !oput_rpmpt)
1979#ifdef MULTIBYTE_SUPPORT
1980		&& ol->chr != WEOF && nl->chr != WEOF
1981#endif
1982		&& nl[1].chr && ol[1].chr && !ZR_equal(ol[1], nl[1])) {
1983
1984		/* deleting characters - see if we can find a match series that
1985		   makes it cheaper to delete intermediate characters
1986		   eg. oldline: hifoobar \ hopefully cheaper here to delete two
1987		   newline: foobar	 / characters, then we have six matches */
1988		if (tccan(TCDEL)) {
1989		    int first = 1;
1990		    for (i = 1; ol[i].chr; i++) {
1991			if (tcdelcost(i) < wpfxlen(ol + i, nl)) {
1992			    /*
1993			     * Some terminals will output the current
1994			     * attributes into cells added at the end by
1995			     * deletions, so turn off text attributes.
1996			     */
1997			    if (first) {
1998				clearattributes();
1999				first = 0;
2000			    }
2001			    tc_delchars(i);
2002			    ol += i;
2003			    char_ins -= i;
2004#ifdef MULTIBYTE_SUPPORT
2005			    while (ol->chr == WEOF) {
2006				ol++;
2007				char_ins--;
2008			    }
2009#endif
2010			    i = 0;
2011			    break;
2012			}
2013		    }
2014		    if (!i)
2015			continue;
2016		}
2017		/*
2018		 * inserting characters - characters pushed off the right
2019		 * should be annihilated, but we don't do this if we're on the
2020		 * last line lest undesired scrolling occurs due to `illegal'
2021		 * characters on screen
2022		 */
2023		if (tccan(TCINS) && (vln != zterm_lines - 1)) {
2024		    /* not on last line */
2025		    for (i = 1; nl[i].chr; i++) {
2026			if (tcinscost(i) < wpfxlen(ol, nl + i)) {
2027			    tc_inschars(i);
2028			    zwrite(nl, i);
2029			    nl += i;
2030#ifdef MULTIBYTE_SUPPORT
2031			    while (nl->chr == WEOF) {
2032				nl++;
2033				i++;
2034			    }
2035#endif
2036			    char_ins += i;
2037			    ccs = (vcs += i);
2038			    /*
2039			     * if we've pushed off the right, truncate
2040			     * oldline
2041			     */
2042			    for (i = 0; ol[i].chr && i < winw - ccs; i++)
2043				;
2044#ifdef MULTIBYTE_SUPPORT
2045			    while (ol[i].chr == WEOF)
2046				i++;
2047			    if (i >= winw - ccs) {
2048				/*
2049				 * Yes, we're over the right.
2050				 * Make sure we truncate at the real
2051				 * character, not a WEOF added to
2052				 * make up the width.
2053				 */
2054				while (ol[i-1].chr == WEOF)
2055				    i--;
2056				ol[i] = zr_zr;
2057				ins_last = 1;
2058			    }
2059#else
2060			    if (i >= winw - ccs) {
2061				ol[i] = zr_zr;
2062				ins_last = 1;
2063			    }
2064#endif
2065			    i = 0;
2066			    break;
2067			}
2068		    }
2069		    if (!i)
2070			continue;
2071		}
2072	    }
2073#ifdef MULTIBYTE_SUPPORT
2074	}
2075#endif
2076    /* we can't do any fancy tricks, so just dump the single character
2077       and keep on trying */
2078#ifdef MULTIBYTE_SUPPORT
2079	/*
2080	 * in case we were tidying up a funny-width character when we
2081	 * reached the end of the new line...
2082	 */
2083	if (!nl->chr)
2084	    break;
2085	do {
2086#endif
2087	    /*
2088	     * If an attribute was on here but isn't any more,
2089	     * output the sequence to turn it off.
2090	     */
2091	    now_off = ol->atr & ~nl->atr & TXT_ATTR_ON_MASK;
2092	    if (now_off)
2093		settextattributes(TXT_ATTR_OFF_FROM_ON(now_off));
2094
2095	    /*
2096	     * This is deliberately called if nl->chr is WEOF
2097	     * in order to keep text attributes consistent.
2098	     * We check for WEOF inside.
2099	     */
2100	    zputc(nl);
2101	    nl++;
2102	    if (ol->chr)
2103	      ol++;
2104	    ccs++, vcs++;
2105#ifdef MULTIBYTE_SUPPORT
2106	    /*
2107	     * Make sure we always overwrite the complete width of
2108	     * a character that was there before.
2109	     */
2110	} while ((ol->chr == WEOF && nl->chr) ||
2111		 (nl->chr == WEOF && ol->chr));
2112#endif
2113    }
2114}
2115
2116/* move the cursor to line ln (relative to the prompt line),
2117   absolute column cl; update vln, vcs - video line and column */
2118
2119/**/
2120void
2121moveto(int ln, int cl)
2122{
2123    const REFRESH_ELEMENT *rep;
2124
2125    if (vcs == winw) {
2126	if (rprompt_indent == 0 && tccan(TCLEFT)) {
2127	  tc_leftcurs(1);
2128	  vcs--;
2129	} else {
2130	    vln++, vcs = 0;
2131	    if (!hasam) {
2132		zputc(&zr_cr);
2133		zputc(&zr_nl);
2134	    } else {
2135		if ((vln < nlnct) && nbuf[vln] && nbuf[vln]->chr)
2136		    rep = nbuf[vln];
2137		else
2138		    rep = &zr_sp;
2139		zputc(rep);
2140		zputc(&zr_cr);
2141		if ((vln < olnct) && obuf[vln] && obuf[vln]->chr)
2142		    *obuf[vln] = *rep;
2143	    }
2144	}
2145    }
2146
2147    if (ln == vln && cl == vcs)
2148	return;
2149
2150/* move up */
2151    if (ln < vln) {
2152	tc_upcurs(vln - ln);
2153	vln = ln;
2154    }
2155/* move down; if we might go off the end of the screen, use newlines
2156   instead of TCDOWN */
2157
2158    while (ln > vln) {
2159	if (vln < vmaxln - 1) {
2160	    if (ln > vmaxln - 1) {
2161		if (tc_downcurs(vmaxln - 1 - vln))
2162		    vcs = 0;
2163		vln = vmaxln - 1;
2164	    } else {
2165		if (tc_downcurs(ln - vln))
2166		    vcs = 0;
2167		vln = ln;
2168		continue;
2169	    }
2170	}
2171	zputc(&zr_cr), vcs = 0; /* safety precaution */
2172	while (ln > vln) {
2173	    zputc(&zr_nl);
2174	    vln++;
2175	}
2176    }
2177
2178    if (cl != vcs)
2179        singmoveto(cl);
2180}
2181
2182/**/
2183mod_export int
2184tcmultout(int cap, int multcap, int ct)
2185{
2186    if (tccan(multcap) && (!tccan(cap) || tclen[multcap] <= tclen[cap] * ct)) {
2187	tcoutarg(multcap, ct);
2188	return 1;
2189    } else if (tccan(cap)) {
2190	while (ct--)
2191	    tcout(cap);
2192	return 1;
2193    }
2194    return 0;
2195}
2196
2197/* ct: number of characters to move across */
2198/**/
2199static void
2200tc_rightcurs(int ct)
2201{
2202    int cl,			/* ``desired'' absolute horizontal position */
2203	i = vcs,		/* cursor position after initial movements  */
2204	j;
2205    REFRESH_STRING t;
2206
2207    cl = ct + vcs;
2208
2209/* do a multright if we can - it's the most reliable */
2210    if (tccan(TCMULTRIGHT)) {
2211	tcoutarg(TCMULTRIGHT, ct);
2212	return;
2213    }
2214
2215/* do an absolute horizontal position if we can */
2216    if (tccan(TCHORIZPOS)) {
2217	tcoutarg(TCHORIZPOS, cl);
2218	return;
2219    }
2220
2221/* XXX: should really check "it" in termcap and use / and % */
2222/* try tabs if tabs are non destructive and multright is not possible */
2223    if (!oxtabs && tccan(TCNEXTTAB) && ((vcs | 7) < cl)) {
2224	i = (vcs | 7) + 1;
2225	tcout(TCNEXTTAB);
2226	for ( ; i + 8 <= cl; i += 8)
2227	    tcout(TCNEXTTAB);
2228	if ((ct = cl - i) == 0) /* number of chars still to move across */
2229	    return;
2230    }
2231
2232/* otherwise _carefully_ write the contents of the video buffer.
2233   if we're anywhere in the prompt, goto the left column and write the whole
2234   prompt out.
2235
2236   If strlen(lpromptbuf) == lpromptw, we can cheat and output
2237   the appropriate chunk of the string.  This test relies on the
2238   fact that any funny business will always make the length of
2239   the string larger than the printing width, so if they're the same
2240   we have only ASCII characters or a single-byte extension of ASCII.
2241   Unfortunately this trick won't work if there are potentially
2242   characters occupying more than one column.  We could flag that
2243   this has happened (since it's not that common to have characters
2244   wider than one column), but for now it's easier not to use the
2245   trick if we are using WCWIDTH() on the prompt.  It's not that
2246   common to be editing in the middle of the prompt anyway, I would
2247   think.
2248   */
2249    if (vln == 0 && i < lpromptw && !(termflags & TERM_SHORT)) {
2250#ifndef MULTIBYTE_SUPPORT
2251	if ((int)strlen(lpromptbuf) == lpromptw)
2252	    fputs(lpromptbuf + i, shout);
2253	else
2254#endif
2255	if (tccan(TCRIGHT) && (tclen[TCRIGHT] * ct <= ztrlen(lpromptbuf)))
2256	    /* it is cheaper to send TCRIGHT than reprint the whole prompt */
2257	    for (ct = lpromptw - i; ct--; )
2258		tcout(TCRIGHT);
2259        else {
2260	    if (i != 0)
2261		zputc(&zr_cr);
2262	    tc_upcurs(lprompth - 1);
2263	    zputs(lpromptbuf, shout);
2264	    if (lpromptwof == winw)
2265		zputs("\n", shout);	/* works with both hasam and !hasam */
2266	}
2267	i = lpromptw;
2268	ct = cl - i;
2269    }
2270
2271    if (nbuf[vln]) {
2272	for (j = 0, t = nbuf[vln]; t->chr && (j < i); j++, t++);
2273	if (j == i)
2274	    for ( ; t->chr && ct; ct--, t++)
2275		zputc(t);
2276    }
2277    while (ct--)
2278	zputc(&zr_sp);	/* not my fault your terminal can't go right */
2279}
2280
2281/**/
2282mod_export int
2283tc_downcurs(int ct)
2284{
2285    int ret = 0;
2286
2287    if (ct && !tcmultout(TCDOWN, TCMULTDOWN, ct)) {
2288	while (ct--)
2289	    zputc(&zr_nl);
2290	zputc(&zr_cr), ret = -1;
2291    }
2292    return ret;
2293}
2294
2295/*
2296 * Output a termcap value using a function defined by "zle -T tc".
2297 * Loosely inspired by subst_string_by_func().
2298 *
2299 * cap is the internal index for the capability; it will be looked up
2300 * in the table and the string passed to the function.
2301 *
2302 * arg is eithr an argument to the capability or -1 if there is none;
2303 * if it is not -1 it will be passed as an additional argument to the
2304 * function.
2305 *
2306 * outc is the output function; currently this is always putshout
2307 * but in principle it may be used to output to a string.
2308 */
2309
2310/**/
2311static void
2312tcout_via_func(int cap, int arg, int (*outc)(int))
2313{
2314    Shfunc tcout_func;
2315    int osc, osm, old_incompfunc;
2316
2317    osc = sfcontext;
2318    osm = stopmsg;
2319    old_incompfunc = incompfunc;
2320
2321    sfcontext = SFC_SUBST;
2322    incompfunc = 0;
2323
2324    if ((tcout_func = getshfunc(tcout_func_name))) {
2325	LinkList l = newlinklist();
2326	char buf[DIGBUFSIZE], *str;
2327
2328	addlinknode(l, tcout_func_name);
2329	addlinknode(l, tccap_get_name(cap));
2330
2331	if (arg != -1) {
2332	    sprintf(buf, "%d", arg);
2333	    addlinknode(l, buf);
2334	}
2335
2336	(void)doshfunc(tcout_func, l, 1);
2337
2338	str = getsparam("REPLY");
2339	if (str) {
2340	    while (*str) {
2341		int chr;
2342		if (*str == Meta) {
2343		    chr = str[1] ^ 32;
2344		    str += 2;
2345		} else {
2346		    chr = *str++;
2347		}
2348		(void)outc(chr);
2349	    }
2350	}
2351    }
2352
2353    sfcontext = osc;
2354    stopmsg = osm;
2355    incompfunc = old_incompfunc;
2356}
2357
2358/**/
2359mod_export void
2360tcout(int cap)
2361{
2362    if (tcout_func_name) {
2363	tcout_via_func(cap, -1, putshout);
2364    } else {
2365	tputs(tcstr[cap], 1, putshout);
2366    }
2367    SELECT_ADD_COST(tclen[cap]);
2368}
2369
2370/**/
2371static void
2372tcoutarg(int cap, int arg)
2373{
2374    char *result;
2375
2376    result = tgoto(tcstr[cap], arg, arg);
2377    if (tcout_func_name) {
2378	tcout_via_func(cap, arg, putshout);
2379    } else {
2380	tputs(result, 1, putshout);
2381    }
2382    SELECT_ADD_COST(strlen(result));
2383}
2384
2385/**/
2386mod_export int
2387clearscreen(UNUSED(char **args))
2388{
2389    tcoutclear(TCCLEARSCREEN);
2390    resetneeded = 1;
2391    clearflag = 0;
2392    return 0;
2393}
2394
2395/**/
2396mod_export int
2397redisplay(UNUSED(char **args))
2398{
2399    moveto(0, 0);
2400    zputc(&zr_cr);		/* extra care */
2401    tc_upcurs(lprompth - 1);
2402    resetneeded = 1;
2403    clearflag = 0;
2404    return 0;
2405}
2406
2407/*
2408 * Show as much of the line buffer as we can in single line mode.
2409 * TBD: all termcap effects are turned off in this mode, so
2410 * there's no point in using character attributes.  We should
2411 * decide what we're going to do and either remove the handling
2412 * from here or enable it in tsetcap().
2413 */
2414
2415/**/
2416static void
2417singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
2418{
2419    REFRESH_STRING vbuf, vp,	/* video buffer and pointer    */
2420	refreshop;	        /* pointer to old video buffer */
2421    int t0,			/* tmp			       */
2422	vsiz,			/* size of new video buffer    */
2423	nvcs = 0,		/* new video cursor column     */
2424	owinpos = winpos,	/* previous window position    */
2425	owinprompt = winprompt;	/* previous winprompt          */
2426#ifdef MULTIBYTE_SUPPORT
2427    int width;			/* width of multibyte character */
2428#endif
2429
2430    nlnct = 1;
2431/* generate the new line buffer completely */
2432    for (vsiz = 1 + lpromptw, t0 = 0; t0 != tmpll; t0++) {
2433	if (tmpline[t0] == ZWC('\t'))
2434	    vsiz = (vsiz | 7) + 2;
2435#ifdef MULTIBYTE_SUPPORT
2436	else if (iswprint(tmpline[t0]) && ((width = WCWIDTH(tmpline[t0])) > 0)) {
2437	    vsiz += width;
2438	    if (isset(COMBININGCHARS) && IS_BASECHAR(tmpline[t0])) {
2439		while (t0 < tmpll-1 && IS_COMBINING(tmpline[t0+1]))
2440		    t0++;
2441	    }
2442	}
2443#endif
2444	else if (ZC_icntrl(tmpline[t0])
2445#ifdef MULTIBYTE_SUPPORT
2446		 && (unsigned)tmpline[t0] <= 0xffU
2447#endif
2448		 )
2449	    vsiz += 2;
2450#ifdef MULTIBYTE_SUPPORT
2451	else
2452	    vsiz += 10;
2453#else
2454	else
2455	    vsiz++;
2456#endif
2457    }
2458    vbuf = (REFRESH_STRING)zalloc(vsiz * sizeof(*vbuf));
2459
2460    if (tmpcs < 0) {
2461#ifdef DEBUG
2462	fprintf(stderr, "BUG: negative cursor position\n");
2463	fflush(stderr);
2464#endif
2465	tmpcs = 0;
2466    }
2467
2468    /* prompt is not directly copied into the video buffer */
2469    ZR_memset(vbuf, zr_sp, lpromptw);
2470    vp = vbuf + lpromptw;
2471    *vp = zr_zr;
2472
2473    for (t0 = 0; t0 < tmpll; t0++) {
2474	int base_atr_on = 0, base_atr_off = 0, ireg;
2475	int all_atr_on, all_atr_off;
2476	struct region_highlight *rhp;
2477	/*
2478	 * Calculate attribute based on region.
2479	 */
2480	for (ireg = 0, rhp = region_highlights;
2481	     ireg < n_region_highlights;
2482	     ireg++, rhp++) {
2483	    int offset;
2484	    if (rhp->flags & ZRH_PREDISPLAY)
2485		offset = 0;	/* include predisplay in start end */
2486	    else
2487		offset = predisplaylen; /* increment over it */
2488	    if (rhp->start + offset <= t0 &&
2489		t0 < rhp->end + offset) {
2490		if (base_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
2491		    /* keep colour already set */
2492		    base_atr_on |= rhp->atr & ~TXT_ATTR_COLOUR_ON_MASK;
2493		} else {
2494		    /* no colour set yet */
2495		    base_atr_on |= rhp->atr;
2496		}
2497		if (t0 == rhp->end + offset - 1 ||
2498		    t0 == tmpll - 1)
2499		    base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr);
2500	    }
2501	}
2502	if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
2503	    /* keep colours from special attributes */
2504	    all_atr_on = special_atr_on |
2505		(base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK);
2506	} else {
2507	    /* keep colours from standard attributes */
2508	    all_atr_on = special_atr_on | base_atr_on;
2509	}
2510	all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
2511
2512	if (tmpline[t0] == ZWC('\t')) {
2513	    for (*vp++ = zr_sp; (vp - vbuf) & 7; )
2514		*vp++ = zr_sp;
2515	    vp[-1].atr |= base_atr_off;
2516	} else if (tmpline[t0] == ZWC('\n')) {
2517	    vp->chr = ZWC('\\');
2518	    vp->atr = all_atr_on;
2519	    vp++;
2520	    vp->chr = ZWC('n');
2521	    vp->atr = all_atr_on | all_atr_off;
2522	    vp++;
2523#ifdef MULTIBYTE_SUPPORT
2524	} else if (iswprint(tmpline[t0]) &&
2525		   (width = WCWIDTH(tmpline[t0])) > 0) {
2526	    int ichars;
2527	    if (isset(COMBININGCHARS) && IS_BASECHAR(tmpline[t0])) {
2528		/*
2529		 * Look for combining characters.
2530		 */
2531		for (ichars = 1; t0 + ichars < tmpll; ichars++) {
2532		    if (!IS_COMBINING(tmpline[t0+ichars]))
2533			break;
2534		}
2535	    } else
2536		ichars = 1;
2537	    vp->atr = base_atr_on | base_atr_off;
2538	    if (ichars > 1)
2539		addmultiword(vp, tmpline+t0, ichars);
2540	    else
2541		vp->chr = tmpline[t0];
2542	    vp++;
2543	    while (--width > 0) {
2544		vp->chr = WEOF;
2545		vp->atr = base_atr_on | base_atr_off;
2546		vp++;
2547	    }
2548	    t0 += ichars - 1;
2549#endif
2550	} else if (ZC_icntrl(tmpline[t0])
2551#ifdef MULTIBYTE_SUPPORT
2552		   && (unsigned)tmpline[t0] <= 0xffU
2553#endif
2554		   ) {
2555	    ZLE_INT_T t = tmpline[++t0];
2556
2557	    vp->chr = ZWC('^');
2558	    vp->atr = all_atr_on;
2559	    vp++;
2560	    vp->chr = (((unsigned int)t & ~0x80u) > 31) ?
2561		ZWC('?') : (t | ZWC('@'));
2562	    vp->atr = all_atr_on | all_atr_off;
2563	    vp++;
2564	}
2565#ifdef MULTIBYTE_SUPPORT
2566	else {
2567	    char dispchars[11];
2568	    char *dispptr = dispchars;
2569	    wchar_t wc;
2570	    int started = 0;
2571
2572	    if ((unsigned)tmpline[t0] > 0xffffU) {
2573		sprintf(dispchars, "<%.08x>", (unsigned)tmpline[t0]);
2574	    } else {
2575		sprintf(dispchars, "<%.04x>", (unsigned)tmpline[t0]);
2576	    }
2577	    while (*dispptr) {
2578		if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */) {
2579		    vp->chr = wc;
2580		    if (!started)
2581			started = 1;
2582		    vp->atr = all_atr_on;
2583		    vp++;
2584		}
2585		dispptr++;
2586	    }
2587	    if (started)
2588		vp[-1].atr |= all_atr_off;
2589	}
2590#else
2591	else {
2592	    vp->chr = tmpline[t0];
2593	    vp->atr = base_atr_on | base_atr_off;
2594	    vp++;
2595	}
2596#endif
2597	if (t0 == tmpcs)
2598	    nvcs = vp - vbuf - 1;
2599    }
2600    if (t0 == tmpcs)
2601	nvcs = vp - vbuf;
2602    *vp = zr_zr;
2603
2604/* determine which part of the new line buffer we want for the display */
2605    if (winpos == -1)
2606	winpos = 0;
2607    if ((winpos && nvcs < winpos + 1) || (nvcs > winpos + winw - 2)) {
2608	if ((winpos = nvcs - ((winw - hasam) / 2)) < 0)
2609	    winpos = 0;
2610    }
2611    if (winpos) {
2612	vbuf[winpos].chr = ZWC('<');	/* line continues to the left */
2613	vbuf[winpos].atr = 0;
2614    }
2615    if ((int)ZR_strlen(vbuf + winpos) > (winw - hasam)) {
2616	vbuf[winpos + winw - hasam - 1].chr = ZWC('>');	/* line continues to right */
2617	vbuf[winpos + winw - hasam - 1].atr = 0;
2618	vbuf[winpos + winw - hasam] = zr_zr;
2619    }
2620    ZR_strcpy(nbuf[0], vbuf + winpos);
2621    zfree(vbuf, vsiz * sizeof(*vbuf));
2622    nvcs -= winpos;
2623
2624    if (winpos < lpromptw) {
2625	/* skip start of buffer corresponding to prompt */
2626	winprompt = lpromptw - winpos;
2627    } else {
2628	/* don't */
2629	winprompt = 0;
2630    }
2631    if (winpos != owinpos && winprompt) {
2632	char *pptr;
2633	int skipping = 0, skipchars = winpos;
2634	/*
2635	 * Need to output such part of the left prompt as fits.
2636	 * Skip the first winpos characters, outputting
2637	 * any characters marked with %{...%}.
2638	 */
2639	singmoveto(0);
2640	MB_METACHARINIT();
2641	for (pptr = lpromptbuf; *pptr; ) {
2642	    if (*pptr == Inpar) {
2643		skipping = 1;
2644		pptr++;
2645	    } else if (*pptr == Outpar) {
2646		skipping = 0;
2647		pptr++;
2648	    } else {
2649		convchar_t cc;
2650		int mblen = MB_METACHARLENCONV(pptr, &cc);
2651		if (skipping || skipchars == 0)
2652		{
2653		    while (mblen) {
2654#ifdef MULTIBYTE_SUPPORT
2655			if (cc == WEOF)
2656			    fputc('?', shout);
2657			else
2658#endif
2659			    if (*pptr == Meta) {
2660				mblen--;
2661				fputc(*++pptr ^ 32, shout);
2662			    } else {
2663				fputc(*pptr, shout);
2664			    }
2665			pptr++;
2666			mblen--;
2667		    }
2668		} else {
2669		    skipchars--;
2670		    pptr += mblen;
2671		}
2672	    }
2673	}
2674	vcs = winprompt;
2675    }
2676
2677/* display the `visible' portion of the line buffer */
2678    t0 = winprompt;
2679    vp = *nbuf + winprompt;
2680    refreshop = *obuf + winprompt;
2681    for (;;) {
2682	/*
2683	 * Skip past all matching characters, but if there used
2684	 * to be a prompt here be careful since all manner of
2685	 * nastiness may be around.
2686	 */
2687	if (vp - *nbuf >= owinprompt)
2688	    for (; vp->chr && ZR_equal(*refreshop, *vp);
2689		 t0++, vp++, refreshop++)
2690		;
2691
2692	if (!vp->chr && !refreshop->chr)
2693	    break;
2694
2695	singmoveto(t0);		/* move to where we do all output from */
2696
2697	if (!refreshop->chr) {
2698	    if ((t0 = ZR_strlen(vp)))
2699		zwrite(vp, t0);
2700	    vcs += t0;
2701	    break;
2702	}
2703	if (!vp->chr) {
2704	    if (tccan(TCCLEAREOL))
2705		tcoutclear(TCCLEAREOL);
2706	    else
2707		for (; refreshop++->chr; vcs++)
2708		    zputc(&zr_sp);
2709	    break;
2710	}
2711	zputc(vp);
2712	vcs++, t0++;
2713	vp++, refreshop++;
2714    }
2715/* move to the new cursor position */
2716    singmoveto(nvcs);
2717
2718    bufswap();
2719}
2720
2721/**/
2722static void
2723singmoveto(int pos)
2724{
2725    if (pos == vcs)
2726	return;
2727
2728/* choose cheapest movements for ttys without multiple movement capabilities -
2729   do this now because it's easier (to code) */
2730
2731    if ((!tccan(TCMULTLEFT) || pos == 0) && (pos <= vcs / 2)) {
2732	zputc(&zr_cr);
2733	vcs = 0;
2734    }
2735
2736    if (pos < vcs)
2737	tc_leftcurs(vcs - pos);
2738    else if (pos > vcs)
2739	tc_rightcurs(pos - vcs);
2740
2741    vcs = pos;
2742}
2743
2744/* Provided for loading the module in a modular fashion */
2745
2746/**/
2747void
2748zle_refresh_boot(void)
2749{
2750}
2751
2752/* Provided for unloading the module in a modular fashion */
2753
2754/**/
2755void
2756zle_refresh_finish(void)
2757{
2758    freevideo();
2759
2760    if (region_highlights)
2761    {
2762	zfree(region_highlights,
2763	      sizeof(struct region_highlight) * n_region_highlights);
2764	region_highlights = NULL;
2765	n_region_highlights = 0;
2766    }
2767}
2768