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