26763: fix problem on failed cd -s to relative path
[zsh.git] / Src / Zle / zle_refresh.c
blob8fb2dff349d0086d226df09cf42fe70c19b5cb87
1 /*
2 * zle_refresh.c - screen update
4 * This file is part of zsh, the Z shell.
6 * Copyright (c) 1992-1997 Paul Falstad
7 * All rights reserved.
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.
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.
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.
30 #include "zle.mdh"
32 #ifdef MULTIBYTE_SUPPORT
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).
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.
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.
53 static REFRESH_CHAR
54 *omwbuf = NULL, /* old multiword glyph buffer */
55 *nmwbuf = NULL; /* new multiword glyph buffer */
56 #endif
59 * Compare if two characters are equal.
61 #ifdef MULTIBYTE_SUPPORT
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.
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
85 static void
86 ZR_memset(REFRESH_ELEMENT *dst, REFRESH_ELEMENT rc, int len)
88 while (len--)
89 *dst++ = rc;
92 #define ZR_memcpy(d, s, l) memcpy((d), (s), (l)*sizeof(REFRESH_ELEMENT))
94 static void
95 ZR_strcpy(REFRESH_ELEMENT *dst, const REFRESH_ELEMENT *src)
97 while ((*dst++ = *src++).chr != ZWC('\0'))
101 static size_t
102 ZR_strlen(const REFRESH_ELEMENT *wstr)
104 int len = 0;
106 while (wstr++->chr != ZWC('\0'))
107 len++;
109 return len;
113 * Simplified strcmp: we don't need the sign, just whether
114 * the strings and their attributes are equal.
116 * In the multibyte case, the two elements must be in the order
117 * element from old video array, element from new video array.
119 static int
120 ZR_strncmp(const REFRESH_ELEMENT *oldwstr, const REFRESH_ELEMENT *newwstr,
121 int len)
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++;
133 return 0;
136 #include "zle_refresh.pro"
139 * Expanded prompts.
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.
146 /**/
147 char *lpromptbuf, *rpromptbuf;
149 /* Text attributes after displaying prompts */
151 /**/
152 unsigned pmpt_attr, rpmpt_attr;
154 /* number of lines displayed */
156 /**/
157 mod_export int nlnct;
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. */
164 /**/
165 mod_export int showinglist;
167 /* > 0 if a completion list is displayed below the prompt,
168 * < 0 if a list is displayed above the prompt. */
170 /**/
171 mod_export int listshown;
173 /* Length of last list displayed (if it is below the prompt). */
175 /**/
176 mod_export int lastlistlen;
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(). */
182 /**/
183 mod_export int clearflag;
185 /* Non-zero if zrefresh() should clear the list below the prompt. */
187 /**/
188 mod_export int clearlist;
190 /* Zle in trashed state - updates may be subtly altered */
192 /**/
193 int trashedzle;
196 * Information used by PREDISPLAY and POSTDISPLAY parameters which
197 * add non-editable text to that being displayed.
199 /**/
200 ZLE_STRING_T predisplay, postdisplay;
201 /**/
202 int predisplaylen, postdisplaylen;
206 * Attributes used by default on the command line, and
207 * attributes for highlighting special (unprintable) characters
208 * displayed on screen.
211 static int default_atr_on, special_atr_on;
213 /* Flags for the region_highlight structure */
214 enum {
215 /* Offsets include predisplay */
216 ZRH_PREDISPLAY = 1
220 * Attributes used for highlighting regions.
221 * and mark.
223 struct region_highlight {
224 /* Attributes turned on in the region */
225 int atr;
226 /* Start of the region */
227 int start;
229 * End of the region: position of the first character not highlighted
230 * (the same system as for point and mark).
232 int end;
234 * Any of the flags defined above.
236 int flags;
239 * Array of region highlights, no special termination.
240 * The first element (0) always describes the region between
241 * point and mark. Any other elements are set by the user
242 * via the parameter region_highlight.
244 struct region_highlight *region_highlights;
246 * Count of special uses of region highlighting, which account
247 * for the first few elements of region_highlights.
248 * 0: region between point and mark
249 * 1: isearch region
251 #define N_SPECIAL_HIGHLIGHTS (2)
253 * Number of elements in region_highlights.
254 * This includes the special elements above.
256 int n_region_highlights;
259 * Flag that highlighting of the region is active.
261 /**/
262 int region_active;
264 #ifdef HAVE_SELECT
265 /* cost of last update */
266 /**/
267 int cost;
269 # define SELECT_ADD_COST(X) (cost += X)
270 # define zputc(a) (zwcputc(a, NULL), cost++)
271 # define zwrite(a, b) (zwcwrite((a), (b)), \
272 cost += ((b) * ZLE_CHAR_SIZE))
273 #else
274 # define SELECT_ADD_COST(X)
275 # define zputc(a) zwcputc(a, NULL)
276 # define zwrite(a, b) zwcwrite((a), (b))
277 #endif
279 static const REFRESH_ELEMENT zr_cr = { ZWC('\r'), 0 };
280 static const REFRESH_ELEMENT zr_dt = { ZWC('.'), 0 };
281 static const REFRESH_ELEMENT zr_nl = { ZWC('\n'), 0 };
282 static const REFRESH_ELEMENT zr_sp = { ZWC(' '), 0 };
283 static const REFRESH_ELEMENT zr_ht = { ZWC('\t'), 0 };
284 static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), 0 };
287 * Constant arrays to be copied into place: these are memcpy'd,
288 * so don't have terminating NULLs.
290 static const REFRESH_ELEMENT zr_end_ellipsis[] = {
291 { ZWC(' '), 0 },
292 { ZWC('<'), 0 },
293 { ZWC('.'), 0 },
294 { ZWC('.'), 0 },
295 { ZWC('.'), 0 },
296 { ZWC('.'), 0 },
297 { ZWC(' '), 0 },
299 #define ZR_END_ELLIPSIS_SIZE \
300 ((int)(sizeof(zr_end_ellipsis)/sizeof(zr_end_ellipsis[0])))
302 static const REFRESH_ELEMENT zr_mid_ellipsis1[] = {
303 { ZWC(' '), 0 },
304 { ZWC('<'), 0 },
305 { ZWC('.'), 0 },
306 { ZWC('.'), 0 },
307 { ZWC('.'), 0 },
308 { ZWC('.'), 0 },
310 #define ZR_MID_ELLIPSIS1_SIZE \
311 ((int)(sizeof(zr_mid_ellipsis1)/sizeof(zr_mid_ellipsis1[0])))
313 static const REFRESH_ELEMENT zr_mid_ellipsis2[] = {
314 { ZWC('>'), 0 },
315 { ZWC(' '), 0 },
317 #define ZR_MID_ELLIPSIS2_SIZE \
318 ((int)(sizeof(zr_mid_ellipsis2)/sizeof(zr_mid_ellipsis2[0])))
320 static const REFRESH_ELEMENT zr_start_ellipsis[] = {
321 { ZWC('>'), 0 },
322 { ZWC('.'), 0 },
323 { ZWC('.'), 0 },
324 { ZWC('.'), 0 },
325 { ZWC('.'), 0 },
327 #define ZR_START_ELLIPSIS_SIZE \
328 ((int)(sizeof(zr_start_ellipsis)/sizeof(zr_start_ellipsis[0])))
331 * Parse the variable zle_highlight to decide how to highlight characters
332 * and regions. Set defaults for anything not explicitly covered.
335 /**/
336 static void
337 zle_set_highlight(void)
339 char **atrs = getaparam("zle_highlight");
340 int special_atr_on_set = 0;
341 int region_atr_on_set = 0;
342 int isearch_atr_on_set = 0;
343 struct region_highlight *rhp;
345 special_atr_on = default_atr_on = 0;
346 if (!region_highlights) {
347 region_highlights = (struct region_highlight *)
348 zshcalloc(N_SPECIAL_HIGHLIGHTS*sizeof(struct region_highlight));
349 n_region_highlights = N_SPECIAL_HIGHLIGHTS;
350 } else {
351 for (rhp = region_highlights;
352 rhp < region_highlights + N_SPECIAL_HIGHLIGHTS;
353 rhp++) {
354 rhp->atr = 0;
358 if (atrs) {
359 for (; *atrs; atrs++) {
360 if (!strcmp(*atrs, "none")) {
361 /* reset attributes for consistency... usually unnecessary */
362 special_atr_on = default_atr_on = 0;
363 special_atr_on_set = region_atr_on_set =
364 isearch_atr_on_set = 1;
365 } else if (strpfx("default:", *atrs)) {
366 match_highlight(*atrs + 8, &default_atr_on);
367 } else if (strpfx("special:", *atrs)) {
368 match_highlight(*atrs + 8, &special_atr_on);
369 special_atr_on_set = 1;
370 } else if (strpfx("region:", *atrs)) {
371 match_highlight(*atrs + 7, &region_highlights->atr);
372 region_atr_on_set = 1;
373 } else if (strpfx("isearch:", *atrs)) {
374 match_highlight(*atrs + 8, &(region_highlights[1].atr));
375 isearch_atr_on_set = 1;
380 /* Defaults */
381 if (!special_atr_on_set)
382 special_atr_on = TXTSTANDOUT;
383 if (!region_atr_on_set)
384 region_highlights->atr = TXTSTANDOUT;
385 if (!isearch_atr_on_set)
386 region_highlights[1].atr = TXTUNDERLINE;
388 allocate_colour_buffer();
392 /**/
393 static void
394 zle_free_highlight(void)
396 free_colour_buffer();
400 * Interface to the region_highlight ZLE parameter.
401 * Converts betwen a format like "P32 42 underline,bold" to
402 * the format in the region_highlights variable. Note that
403 * the region_highlights variable stores the internal (point/mark)
404 * region in element zero.
407 /**/
408 char **
409 get_region_highlight(UNUSED(Param pm))
411 int arrsize = n_region_highlights;
412 char **retarr, **arrp;
413 struct region_highlight *rhp;
415 /* region_highlights may not have been set yet */
416 if (arrsize)
417 arrsize -= N_SPECIAL_HIGHLIGHTS;
418 arrp = retarr = (char **)zhalloc((arrsize+1)*sizeof(char *));
420 /* ignore special highlighting */
421 for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
422 arrsize--;
423 rhp++, arrp++) {
424 char digbuf1[DIGBUFSIZE], digbuf2[DIGBUFSIZE];
425 int atrlen = 0, alloclen;
427 sprintf(digbuf1, "%d", rhp->start);
428 sprintf(digbuf2, "%d", rhp->end);
430 atrlen = output_highlight(rhp->atr, NULL);
431 alloclen = atrlen + strlen(digbuf1) + strlen(digbuf2) +
432 3; /* 2 spaces, 1 0 */
433 if (rhp->flags & ZRH_PREDISPLAY)
434 alloclen += 2; /* "P " */
435 *arrp = (char *)zhalloc(alloclen * sizeof(char));
437 * On input we allow a space after the flags.
438 * I haven't put a space here because I think it's
439 * marginally easier to have the output always split
440 * into three words, and then check the first to
441 * see if there are flags. However, it's arguable.
443 sprintf(*arrp, "%s%s %s ",
444 (rhp->flags & ZRH_PREDISPLAY) ? "P" : "",
445 digbuf1, digbuf2);
446 (void)output_highlight(rhp->atr, *arrp + strlen(*arrp));
448 *arrp = '\0';
449 return retarr;
454 * The parameter system requires the pm argument, but this
455 * may be NULL if called directly.
458 /**/
459 void
460 set_region_highlight(UNUSED(Param pm), char **aval)
462 int len;
463 struct region_highlight *rhp;
465 len = aval ? arrlen(aval) : 0;
466 if (n_region_highlights != len + N_SPECIAL_HIGHLIGHTS) {
467 /* no null termination, but include special highlighting at start */
468 n_region_highlights = len + N_SPECIAL_HIGHLIGHTS;
469 region_highlights = (struct region_highlight *)
470 zrealloc(region_highlights,
471 sizeof(struct region_highlight) * n_region_highlights);
474 if (!aval)
475 return;
477 for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
478 *aval;
479 rhp++, aval++) {
480 char *strp, *oldstrp;
482 oldstrp = *aval;
483 if (*oldstrp == 'P') {
484 rhp->flags = ZRH_PREDISPLAY;
485 oldstrp++;
487 else
488 rhp->flags = 0;
489 while (inblank(*oldstrp))
490 oldstrp++;
492 rhp->start = (int)zstrtol(oldstrp, &strp, 10);
493 if (strp == oldstrp)
494 rhp->start = -1;
496 while (inblank(*strp))
497 strp++;
499 oldstrp = strp;
500 rhp->end = (int)zstrtol(strp, &strp, 10);
501 if (strp == oldstrp)
502 rhp->end = -1;
504 while (inblank(*strp))
505 strp++;
507 match_highlight(strp, &rhp->atr);
512 /**/
513 void
514 unset_region_highlight(Param pm, int exp)
516 if (exp) {
517 set_region_highlight(pm, NULL);
518 stdunsetfn(pm, exp);
523 /* The last attributes that were on. */
524 static int lastatr;
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.
530 static void
531 clearattributes(void)
533 if (lastatr) {
534 settextattributes(TXT_ATTR_OFF_FROM_ON(lastatr));
535 lastatr = 0;
540 * Output a termcap capability, clearing any text attributes so
541 * as not to mess up the display.
544 static void
545 tcoutclear(int cap)
547 clearattributes();
548 tcout(cap);
552 * Output the character. This must come from the new video
553 * buffer, nbuf, since we access the multiword buffer nmwbuf
554 * directly.
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.
561 /**/
562 void
563 zwcputc(const REFRESH_ELEMENT *c, int *curatrp)
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.
570 #ifdef MULTIBYTE_SUPPORT
571 mbstate_t mbstate;
572 int i;
573 VARARR(char, mbtmp, MB_CUR_MAX + 1);
574 #endif
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;
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.
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);
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;
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);
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);
613 #else
614 fputc(c->chr, shout);
615 #endif
618 * Always output "off" attributes since we only turn off at
619 * the end of a chunk of highlighted text.
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);
625 if (curatrp) {
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.
632 *curatrp = (c->atr & TXT_ATTR_ON_VALUES_MASK) &
633 ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT);
637 static int
638 zwcwrite(const REFRESH_STRING s, size_t i)
640 size_t j;
641 int curatr = 0;
643 for (j = 0; j < i; j++)
644 zwcputc(s + j, &curatr);
645 return i; /* TODO something better for error indication */
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 */
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
679 * Number of words to allocate in one go for the multiword buffers.
681 #define DEF_MWBUF_ALLOC (32)
683 static void
684 freevideo(void)
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));
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;
705 /**/
706 void
707 resetvideo(void)
709 int ln;
711 winw = columns; /* terminal width */
712 if (termflags & TERM_SHORT)
713 winh = 1;
714 else
715 winh = (lines < 2) ? 24 : lines;
716 rwinh = lines; /* keep the real number of lines */
717 vln = vmaxln = winprompt = 0;
718 winpos = -1;
719 if (winw_alloc != winw || winh_alloc != winh) {
720 freevideo();
721 nbuf = (REFRESH_STRING *)zshcalloc((winh + 1) * sizeof(*nbuf));
722 obuf = (REFRESH_STRING *)zshcalloc((winh + 1) * sizeof(*obuf));
723 nbuf[0] = (REFRESH_STRING)zalloc((winw + 2) * sizeof(**nbuf));
724 obuf[0] = (REFRESH_STRING)zalloc((winw + 2) * sizeof(**obuf));
726 #ifdef MULTIBYTE_SUPPORT
727 nmw_size = DEF_MWBUF_ALLOC;
728 nmw_ind = 1;
729 nmwbuf = (REFRESH_CHAR *)zalloc(nmw_size * sizeof(*nmwbuf));
731 omw_size = DEF_MWBUF_ALLOC;
732 omwbuf = (REFRESH_CHAR *)zalloc(omw_size * sizeof(*omwbuf));
733 #endif
735 winw_alloc = winw;
736 winh_alloc = winh;
738 for (ln = 0; ln != winh + 1; ln++) {
739 if (nbuf[ln]) {
740 nbuf[ln][0] = zr_nl;
741 nbuf[ln][1] = zr_zr;
743 if (obuf[ln]) {
744 obuf[ln][0] = zr_nl;
745 obuf[ln][1] = zr_zr;
750 * countprompt() now correctly handles multibyte input.
752 countprompt(lpromptbuf, &lpromptwof, &lprompth, 1);
753 countprompt(rpromptbuf, &rpromptw, &rprompth, 0);
754 if (lpromptwof != winw)
755 lpromptw = lpromptwof;
756 else {
757 lpromptw = 0;
758 lprompth++;
761 if (lpromptw) {
762 ZR_memset(nbuf[0], zr_sp, lpromptw);
763 ZR_memset(obuf[0], zr_sp, lpromptw);
764 nbuf[0][lpromptw] = obuf[0][lpromptw] = zr_zr;
767 vcs = lpromptw;
768 olnct = nlnct = 0;
769 if (showinglist > 0)
770 showinglist = -2;
771 trashedzle = 0;
775 * Nov 96: <mason> changed to single line scroll
778 /**/
779 static void
780 scrollwindow(int tline)
782 int t0;
783 REFRESH_STRING s;
785 s = nbuf[tline];
786 for (t0 = tline; t0 < winh - 1; t0++)
787 nbuf[t0] = nbuf[t0 + 1];
788 nbuf[winh - 1] = s;
789 if (!tline)
790 more_start = 1;
791 return;
795 * Parameters in zrefresh used for communicating with next-line functions.
797 struct rparams {
798 int canscroll; /* number of lines we are allowed to scroll */
799 int ln; /* current line we're working on */
800 int more_status; /* more stuff in status line */
801 int nvcs; /* video cursor column */
802 int nvln; /* video cursor line */
803 int tosln; /* tmp in statusline stuff */
804 REFRESH_STRING s; /* pointer into the video buffer */
805 REFRESH_STRING sen; /* pointer to end of the video buffer (eol) */
807 typedef struct rparams *Rparams;
809 static int cleareol, /* clear to end-of-line (if can't cleareod) */
810 clearf, /* alwayslastprompt used immediately before */
811 put_rpmpt, /* whether we should display right-prompt */
812 oput_rpmpt, /* whether displayed right-prompt last time */
813 oxtabs, /* oxtabs - tabs expand to spaces if set */
814 numscrolls, onumscrolls;
817 * Go to the next line in the main display area. Return 1 if we should abort
818 * processing the line loop at this point, else 0.
820 * If wrapped is non-zero, text wrapped, so output newline.
821 * Otherwise, text not wrapped, so output null.
823 static int
824 nextline(Rparams rpms, int wrapped)
826 nbuf[rpms->ln][winw+1] = wrapped ? zr_nl : zr_zr;
827 *rpms->s = zr_zr;
828 if (rpms->ln != winh - 1)
829 rpms->ln++;
830 else {
831 if (!rpms->canscroll) {
832 if (rpms->nvln != -1 && rpms->nvln != winh - 1
833 && (numscrolls != onumscrolls - 1
834 || rpms->nvln <= winh / 2))
835 return 1;
836 numscrolls++;
837 rpms->canscroll = winh / 2;
839 rpms->canscroll--;
840 scrollwindow(0);
841 if (rpms->nvln != -1)
842 rpms->nvln--;
844 if (!nbuf[rpms->ln])
845 nbuf[rpms->ln] = (REFRESH_STRING)zalloc((winw + 2) * sizeof(**nbuf));
846 rpms->s = nbuf[rpms->ln];
847 rpms->sen = rpms->s + winw;
849 return 0;
854 * Go to the next line in the status area.
856 static void
857 snextline(Rparams rpms)
859 *rpms->s = zr_zr;
860 if (rpms->ln != winh - 1)
861 rpms->ln++;
862 else
863 if (rpms->tosln > rpms->ln) {
864 rpms->tosln--;
865 if (rpms->nvln > 1) {
866 scrollwindow(0);
867 rpms->nvln--;
868 } else
869 more_end = 1;
870 } else if (rpms->tosln > 2 && rpms->nvln > 1) {
871 rpms->tosln--;
872 if (rpms->tosln <= rpms->nvln) {
873 scrollwindow(0);
874 rpms->nvln--;
875 } else {
876 scrollwindow(rpms->tosln);
877 more_end = 1;
879 } else {
880 rpms->more_status = 1;
881 scrollwindow(rpms->tosln + 1);
883 if (!nbuf[rpms->ln])
884 nbuf[rpms->ln] = (REFRESH_STRING)zalloc((winw + 2) * sizeof(**nbuf));
885 rpms->s = nbuf[rpms->ln];
886 rpms->sen = rpms->s + winw;
890 /**/
891 static void
892 settextattributes(int atr)
894 if (txtchangeisset(atr, TXTNOBOLDFACE))
895 tsetcap(TCALLATTRSOFF, 0);
896 if (txtchangeisset(atr, TXTNOSTANDOUT))
897 tsetcap(TCSTANDOUTEND, 0);
898 if (txtchangeisset(atr, TXTNOUNDERLINE))
899 tsetcap(TCUNDERLINEEND, 0);
900 if (txtchangeisset(atr, TXTBOLDFACE))
901 tsetcap(TCBOLDFACEBEG, 0);
902 if (txtchangeisset(atr, TXTSTANDOUT))
903 tsetcap(TCSTANDOUTBEG, 0);
904 if (txtchangeisset(atr, TXTUNDERLINE))
905 tsetcap(TCUNDERLINEBEG, 0);
906 if (txtchangeisset(atr, TXTFGCOLOUR|TXTNOFGCOLOUR))
907 set_colour_attribute(atr, COL_SEQ_FG, 0);
908 if (txtchangeisset(atr, TXTBGCOLOUR|TXTNOBGCOLOUR))
909 set_colour_attribute(atr, COL_SEQ_BG, 0);
912 #ifdef MULTIBYTE_SUPPORT
914 * Add a multiword glyph at the screen location base.
915 * tptr points to the source and there are ichars characters.
917 static void
918 addmultiword(REFRESH_ELEMENT *base, ZLE_STRING_T tptr, int ichars)
920 /* Number of characters needed in buffer incl. count */
921 int iadd = ichars + 1, icnt;
922 REFRESH_CHAR *nmwptr;
923 base->atr |= TXT_MULTIWORD_MASK;
924 /* check allocation */
925 if (nmw_ind + iadd > nmw_size) {
926 /* need more space in buffer */
927 int mw_more = (iadd > DEF_MWBUF_ALLOC) ? iadd :
928 DEF_MWBUF_ALLOC;
929 nmwbuf = (REFRESH_CHAR *)
930 zrealloc(nmwbuf, (nmw_size += mw_more) *
931 sizeof(*nmwbuf));
933 /* make buffer entry: count, then characters */
934 nmwptr = nmwbuf + nmw_ind;
935 *nmwptr++ = ichars;
936 for (icnt = 0; icnt < ichars; icnt++)
937 *nmwptr++ = tptr[icnt];
938 /* save index and update */
939 base->chr = (wint_t)nmw_ind;
940 nmw_ind += iadd;
942 #endif
946 * Swap the old and new video buffers, plus any associated multiword
947 * buffers. The new buffer becomes the old one; the new new buffer
948 * will be filled with the command line next time.
950 static void
951 bufswap(void)
953 REFRESH_STRING *qbuf;
954 #ifdef MULTIBYTE_SUPPORT
955 REFRESH_CHAR *qmwbuf;
956 int itmp;
957 #endif
959 qbuf = nbuf;
960 nbuf = obuf;
961 obuf = qbuf;
963 #ifdef MULTIBYTE_SUPPORT
964 /* likewise multiword buffers */
965 qmwbuf = nmwbuf;
966 nmwbuf = omwbuf;
967 omwbuf = qmwbuf;
969 itmp = nmw_size;
970 nmw_size = omw_size;
971 omw_size = itmp;
973 nmw_ind = 1;
974 #endif
978 /**/
979 mod_export void
980 zrefresh(void)
982 static int inlist; /* avoiding recursion */
983 int iln; /* current line as index in loops */
984 int t0 = -1; /* tmp */
985 ZLE_STRING_T tmpline, /* line with added pre/post text */
986 t, /* pointer into the real buffer */
987 scs, /* pointer to cursor position in real buffer */
988 u; /* pointer for status line stuff */
989 int tmpcs, tmpll; /* ditto cursor position and line length */
990 int tmppos; /* t - tmpline */
991 int tmpalloced; /* flag to free tmpline when finished */
992 int remetafy; /* flag that zle line is metafied */
993 int txtchange; /* attributes set after prompts */
994 struct rparams rpms;
995 #ifdef MULTIBYTE_SUPPORT
996 int width; /* width of wide character */
997 #endif
1000 /* If this is called from listmatches() (indirectly via trashzle()), and *
1001 * that was called from the end of zrefresh(), then we don't need to do *
1002 * anything. All this `inlist' code is actually unnecessary, but it *
1003 * improves speed a little in a common case. */
1004 if (inlist)
1005 return;
1008 * zrefresh() is called from all over the place, so we can't
1009 * be sure if the line is metafied for completion or not.
1011 if (zlemetaline != NULL) {
1012 remetafy = 1;
1013 unmetafy_line();
1015 else
1016 remetafy = 0;
1018 if (predisplaylen || postdisplaylen) {
1019 /* There is extra text to display at the start or end of the line */
1020 tmpline = zalloc((zlell + predisplaylen + postdisplaylen)*sizeof(*tmpline));
1021 if (predisplaylen)
1022 ZS_memcpy(tmpline, predisplay, predisplaylen);
1023 if (zlell)
1024 ZS_memcpy(tmpline+predisplaylen, zleline, zlell);
1025 if (postdisplaylen)
1026 ZS_memcpy(tmpline+predisplaylen+zlell, postdisplay,
1027 postdisplaylen);
1029 tmpcs = zlecs + predisplaylen;
1030 tmpll = predisplaylen + zlell + postdisplaylen;
1031 tmpalloced = 1;
1032 } else {
1033 tmpline = zleline;
1034 tmpcs = zlecs;
1035 tmpll = zlell;
1036 tmpalloced = 0;
1039 /* this will create region_highlights if it's still NULL */
1040 zle_set_highlight();
1042 /* check for region between point ($CURSOR) and mark ($MARK) */
1043 if (region_active) {
1044 if (zlecs <= mark) {
1045 region_highlights->start = zlecs;
1046 region_highlights->end = mark;
1047 } else {
1048 region_highlights->start = mark;
1049 region_highlights->end = zlecs;
1051 } else {
1052 region_highlights->start = region_highlights->end = -1;
1054 /* check for isearch string to highlight */
1055 if (isearch_active) {
1056 region_highlights[1].start = isearch_startpos;
1057 region_highlights[1].end = isearch_endpos;
1058 } else {
1059 region_highlights[1].start = region_highlights[1].end = -1;
1062 if (clearlist && listshown > 0) {
1063 if (tccan(TCCLEAREOD)) {
1064 int ovln = vln, ovcs = vcs;
1065 REFRESH_STRING nb = nbuf[vln];
1067 nbuf[vln] = obuf[vln];
1068 moveto(nlnct, 0);
1069 tcoutclear(TCCLEAREOD);
1070 moveto(ovln, ovcs);
1071 nbuf[vln] = nb;
1072 } else {
1073 invalidatelist();
1074 moveto(0, 0);
1075 clearflag = 0;
1076 resetneeded = 1;
1078 listshown = lastlistlen = 0;
1079 if (showinglist != -2)
1080 showinglist = 0;
1082 clearlist = 0;
1084 #ifdef HAVE_SELECT
1085 cost = 0; /* reset */
1086 #endif
1088 /* Nov 96: <mason> I haven't checked how complete this is. sgtty stuff may
1089 or may not work */
1090 #if defined(SGTABTYPE)
1091 oxtabs = ((SGTTYFLAG & SGTABTYPE) == SGTABTYPE);
1092 #else
1093 oxtabs = 0;
1094 #endif
1096 cleareol = 0; /* unset */
1097 more_start = more_end = 0; /* unset */
1098 if (isset(SINGLELINEZLE) || lines < 3
1099 || (termflags & (TERM_NOUP | TERM_BAD | TERM_UNKNOWN)))
1100 termflags |= TERM_SHORT;
1101 else
1102 termflags &= ~TERM_SHORT;
1103 if (resetneeded) {
1104 onumscrolls = 0;
1105 zsetterm();
1106 #ifdef TIOCGWINSZ
1107 if (winchanged) {
1108 moveto(0, 0);
1109 t0 = olnct; /* this is to clear extra lines even when */
1110 winchanged = 0; /* the terminal cannot TCCLEAREOD */
1111 listshown = 0;
1113 #endif
1114 /* we probably should only have explicitly set attributes */
1115 tsetcap(TCALLATTRSOFF, 0);
1116 tsetcap(TCSTANDOUTEND, 0);
1117 tsetcap(TCUNDERLINEEND, 0);
1118 /* cheat on attribute unset */
1119 txtunset(TXTBOLDFACE|TXTSTANDOUT|TXTUNDERLINE);
1121 if (trashedzle && !clearflag)
1122 reexpandprompt();
1123 resetvideo();
1124 resetneeded = 0; /* unset */
1125 oput_rpmpt = 0; /* no right-prompt currently on screen */
1127 if (!clearflag) {
1128 if (tccan(TCCLEAREOD))
1129 tcoutclear(TCCLEAREOD);
1130 else
1131 cleareol = 1; /* request: clear to end of line */
1132 if (listshown > 0)
1133 listshown = 0;
1135 if (t0 > -1)
1136 olnct = (t0 < winh) ? t0 : winh;
1137 if (termflags & TERM_SHORT)
1138 vcs = 0;
1139 else if (!clearflag && lpromptbuf[0]) {
1140 zputs(lpromptbuf, shout);
1141 if (lpromptwof == winw)
1142 zputs("\n", shout); /* works with both hasam and !hasam */
1143 } else {
1144 txtchange = pmpt_attr;
1145 settextattributes(txtchange);
1147 if (clearflag) {
1148 zputc(&zr_cr);
1149 vcs = 0;
1150 moveto(0, lpromptw);
1152 fflush(shout);
1153 clearf = clearflag;
1154 } else if (winw != columns || rwinh != lines)
1155 resetvideo();
1157 /* now winw equals columns and winh equals lines
1158 width comparisons can be made with winw, height comparisons with winh */
1160 if (termflags & TERM_SHORT) {
1161 singlerefresh(tmpline, tmpll, tmpcs);
1162 goto singlelineout;
1165 if (tmpcs < 0) {
1166 #ifdef DEBUG
1167 fprintf(stderr, "BUG: negative cursor position\n");
1168 fflush(stderr);
1169 #endif
1170 tmpcs = 0;
1172 scs = tmpline + tmpcs;
1173 numscrolls = 0;
1175 /* first, we generate the video line buffers so we know what to put on
1176 the screen - also determine final cursor position (nvln, nvcs) */
1178 /* Deemed necessary by PWS 1995/05/15 due to kill-line problems */
1179 if (!*nbuf)
1180 *nbuf = (REFRESH_STRING)zalloc((winw + 2) * sizeof(**nbuf));
1182 memset(&rpms, 0, sizeof(rpms));
1183 rpms.nvln = -1;
1185 rpms.s = nbuf[rpms.ln = 0] + lpromptw;
1186 rpms.sen = *nbuf + winw;
1187 for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) {
1188 int base_atr_on = default_atr_on, base_atr_off = 0, ireg;
1189 int all_atr_on, all_atr_off;
1190 struct region_highlight *rhp;
1192 * Calculate attribute based on region.
1194 for (ireg = 0, rhp = region_highlights;
1195 ireg < n_region_highlights;
1196 ireg++, rhp++) {
1197 int offset;
1198 if (rhp->flags & ZRH_PREDISPLAY)
1199 offset = 0; /* include predisplay in start end */
1200 else
1201 offset = predisplaylen; /* increment over it */
1202 if (rhp->start + offset <= tmppos &&
1203 tmppos < rhp->end + offset) {
1204 if (rhp->atr & (TXTFGCOLOUR|TXTBGCOLOUR)) {
1205 /* override colour with later entry */
1206 base_atr_on = (base_atr_on & ~TXT_ATTR_ON_VALUES_MASK) |
1207 rhp->atr;
1208 } else {
1209 /* no colour set yet */
1210 base_atr_on |= rhp->atr;
1212 if (tmppos == rhp->end + offset - 1 ||
1213 tmppos == tmpll - 1)
1214 base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr);
1217 if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
1218 /* keep colours from special attributes */
1219 all_atr_on = special_atr_on |
1220 (base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK);
1221 } else {
1222 /* keep colours from standard attributes */
1223 all_atr_on = special_atr_on | base_atr_on;
1225 all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
1227 if (t == scs) /* if cursor is here, remember it */
1228 rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln];
1230 if (*t == ZWC('\n')){ /* newline */
1231 /* text not wrapped */
1232 if (nextline(&rpms, 0))
1233 break;
1234 } else if (*t == ZWC('\t')) { /* tab */
1235 t0 = rpms.s - nbuf[rpms.ln];
1236 if ((t0 | 7) + 1 >= winw) {
1237 /* text wrapped */
1238 if (nextline(&rpms, 1))
1239 break;
1240 } else {
1241 do {
1242 rpms.s->chr = ZWC(' ');
1243 rpms.s->atr = base_atr_on;
1244 rpms.s++;
1245 } while ((++t0) & 7);
1246 rpms.s[-1].atr |= base_atr_off;
1249 #ifdef MULTIBYTE_SUPPORT
1250 else if (iswprint(*t) && (width = WCWIDTH(*t)) > 0) {
1251 int ichars;
1252 if (width > rpms.sen - rpms.s) {
1253 int started = 0;
1255 * Too wide to fit. Insert spaces to end of current line.
1257 do {
1258 rpms.s->chr = ZWC(' ');
1259 if (!started)
1260 started = 1;
1261 rpms.s->atr = all_atr_on;
1262 rpms.s++;
1263 } while (rpms.s < rpms.sen);
1264 if (started)
1265 rpms.s[-1].atr |= all_atr_off;
1266 if (nextline(&rpms, 1))
1267 break;
1268 if (t == scs) {
1269 /* Update cursor to this point */
1270 rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln];
1273 if (isset(COMBININGCHARS) && IS_BASECHAR(*t)) {
1275 * Look for combining characters.
1277 for (ichars = 1; tmppos + ichars < tmpll; ichars++) {
1278 if (!IS_COMBINING(t[ichars]))
1279 break;
1281 } else
1282 ichars = 1;
1283 if (width > rpms.sen - rpms.s || width == 0) {
1285 * The screen width is too small to fit even one
1286 * occurrence.
1288 rpms.s->chr = ZWC('?');
1289 rpms.s->atr = all_atr_on | all_atr_off;
1290 rpms.s++;
1291 } else {
1292 /* We can fit it without reaching the end of the line. */
1294 * As we don't actually output the WEOF, we attach
1295 * any off attributes to the character itself.
1297 rpms.s->atr = base_atr_on | base_atr_off;
1298 if (ichars > 1) {
1300 * Glyph includes combining characters.
1301 * Write these into the multiword buffer and put
1302 * the index into the value at the screen location.
1304 addmultiword(rpms.s, t, ichars);
1305 } else {
1306 /* Single wide character */
1307 rpms.s->chr = *t;
1309 rpms.s++;
1310 while (--width > 0) {
1311 rpms.s->chr = WEOF;
1312 /* Not used, but be consistent... */
1313 rpms.s->atr = base_atr_on | base_atr_off;
1314 rpms.s++;
1317 if (ichars > 1) {
1318 /* allow for normal increment */
1319 tmppos += ichars - 1;
1320 t += ichars - 1;
1323 #endif
1324 else if (ZC_icntrl(*t)
1325 #ifdef MULTIBYTE_SUPPORT
1326 && (unsigned)*t <= 0xffU
1327 #endif
1328 ) { /* other control character */
1329 rpms.s->chr = ZWC('^');
1330 rpms.s->atr = all_atr_on;
1331 rpms.s++;
1332 if (rpms.s == rpms.sen) {
1333 /* text wrapped */
1334 rpms.s[-1].atr |= all_atr_off;
1335 if (nextline(&rpms, 1))
1336 break;
1338 rpms.s->chr = (((unsigned int)*t & ~0x80u) > 31) ?
1339 ZWC('?') : (*t | ZWC('@'));
1340 rpms.s->atr = all_atr_on | all_atr_off;
1341 rpms.s++;
1343 #ifdef MULTIBYTE_SUPPORT
1344 else {
1346 * Not printable or zero width.
1347 * Resort to hackery.
1349 char dispchars[11];
1350 char *dispptr = dispchars;
1351 wchar_t wc;
1352 int started = 0;
1354 if ((unsigned)*t > 0xffffU) {
1355 sprintf(dispchars, "<%.08x>", (unsigned)*t);
1356 } else {
1357 sprintf(dispchars, "<%.04x>", (unsigned)*t);
1359 while (*dispptr) {
1360 if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */)
1362 rpms.s->chr = wc;
1363 if (!started)
1364 started = 1;
1365 rpms.s->atr = all_atr_on;
1366 rpms.s++;
1367 if (rpms.s == rpms.sen) {
1368 /* text wrapped */
1369 if (started) {
1370 rpms.s[-1].atr |= all_atr_off;
1371 started = 0;
1373 if (nextline(&rpms, 1))
1374 break;
1377 dispptr++;
1379 if (started)
1380 rpms.s[-1].atr |= all_atr_off;
1381 if (*dispptr) /* nextline said stop processing */
1382 break;
1384 #else
1385 else { /* normal character */
1386 rpms.s->chr = *t;
1387 rpms.s->atr = base_atr_on | base_atr_off;
1388 rpms.s++;
1390 #endif
1391 if (rpms.s == rpms.sen) {
1392 /* text wrapped */
1393 if (nextline(&rpms, 1))
1394 break;
1398 /* if we're really on the next line, don't fake it; do everything properly */
1399 if (t == scs &&
1400 (rpms.nvcs = rpms.s - (nbuf[rpms.nvln = rpms.ln])) == winw) {
1401 /* text wrapped */
1402 (void)nextline(&rpms, 1);
1403 *rpms.s = zr_zr;
1404 rpms.nvcs = 0;
1405 rpms.nvln++;
1408 if (t != tmpline + tmpll)
1409 more_end = 1;
1411 if (statusline) {
1412 int outll, outsz, all_atr_on, all_atr_off;
1413 char *statusdup = ztrdup(statusline);
1414 ZLE_STRING_T outputline =
1415 stringaszleline(statusdup, 0, &outll, &outsz, NULL);
1417 all_atr_on = special_atr_on;
1418 all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
1420 rpms.tosln = rpms.ln + 1;
1421 nbuf[rpms.ln][winw + 1] = zr_zr; /* text not wrapped */
1422 snextline(&rpms);
1423 u = outputline;
1424 for (; u < outputline + outll; u++) {
1425 #ifdef MULTIBYTE_SUPPORT
1426 if (iswprint(*u)) {
1427 int width = WCWIDTH(*u);
1428 /* Handle wide characters as above */
1429 if (width > rpms.sen - rpms.s) {
1430 do {
1431 *rpms.s++ = zr_sp;
1432 } while (rpms.s < rpms.sen);
1433 nbuf[rpms.ln][winw + 1] = zr_nl;
1434 snextline(&rpms);
1436 if (width > rpms.sen - rpms.s) {
1437 rpms.s->chr = ZWC('?');
1438 rpms.s->atr = all_atr_on | all_atr_off;
1439 rpms.s++;
1440 } else {
1441 rpms.s->chr = *u;
1442 rpms.s->atr = 0;
1443 rpms.s++;
1444 while (--width > 0) {
1445 rpms.s->chr = WEOF;
1446 rpms.s->atr = 0;
1447 rpms.s++;
1451 else
1452 #endif
1453 if (ZC_icntrl(*u)) { /* simplified processing in the status line */
1454 rpms.s->chr = ZWC('^');
1455 rpms.s->atr = all_atr_on;
1456 rpms.s++;
1457 if (rpms.s == rpms.sen) {
1458 nbuf[rpms.ln][winw + 1] = zr_nl;/* text wrapped */
1459 snextline(&rpms);
1461 rpms.s->chr = (((unsigned int)*u & ~0x80u) > 31)
1462 ? ZWC('?') : (*u | ZWC('@'));
1463 rpms.s->atr = all_atr_on | all_atr_off;
1464 rpms.s++;
1465 } else {
1466 rpms.s->chr = *u;
1467 rpms.s->atr = 0;
1468 rpms.s++;
1470 if (rpms.s == rpms.sen) {
1471 nbuf[rpms.ln][winw + 1] = zr_nl; /* text wrapped */
1472 snextline(&rpms);
1475 if (rpms.s == rpms.sen) {
1477 * I suppose we don't modify nbuf[rpms.ln][winw+1] here
1478 * since we're right at the end?
1480 snextline(&rpms);
1482 zfree(outputline, outsz);
1483 free(statusdup);
1485 *rpms.s = zr_zr;
1487 /* insert <.... at end of last line if there is more text past end of screen */
1488 if (more_end) {
1489 #ifdef MULTIBYTE_SUPPORT
1490 int extra_ellipsis = 0;
1491 #endif
1492 if (!statusline)
1493 rpms.tosln = winh;
1494 rpms.s = nbuf[rpms.tosln - 1];
1495 rpms.sen = rpms.s + winw - 7;
1496 for (; rpms.s < rpms.sen; rpms.s++) {
1497 if (rpms.s->chr == ZWC('\0')) {
1498 ZR_memset(rpms.s, zr_sp, rpms.sen - rpms.s);
1499 /* make sure we don't trigger the WEOF test */
1500 rpms.sen->chr = ZWC('\0');
1501 break;
1504 /* rpms.s is no longer needed */
1505 #ifdef MULTIBYTE_SUPPORT
1507 * Ensure we don't start overwriting in the middle of a wide
1508 * character.
1510 while(rpms.sen > nbuf[rpms.tosln - 1] && rpms.sen->chr == WEOF) {
1511 extra_ellipsis++;
1512 rpms.sen--;
1514 #endif
1515 ZR_memcpy(rpms.sen, zr_end_ellipsis, ZR_END_ELLIPSIS_SIZE);
1516 #ifdef MULTIBYTE_SUPPORT
1517 /* Extend to the end if we backed off for a wide character */
1518 if (extra_ellipsis) {
1519 rpms.sen += ZR_END_ELLIPSIS_SIZE;
1520 ZR_memset(rpms.sen, zr_dt, extra_ellipsis);
1522 #endif
1523 nbuf[rpms.tosln - 1][winw] = nbuf[rpms.tosln - 1][winw + 1] = zr_zr;
1526 /* insert <....> at end of first status line if status is too big */
1527 if (rpms.more_status) {
1528 #ifdef MULTIBYTE_SUPPORT
1529 int extra_ellipsis = 0;
1530 #endif
1531 rpms.s = nbuf[rpms.tosln];
1532 rpms.sen = rpms.s + winw - 8;
1533 for (; rpms.s < rpms.sen; rpms.s++) {
1534 if (rpms.s->chr == ZWC('\0')) {
1535 ZR_memset(rpms.s, zr_sp, rpms.sen - rpms.s);
1536 break;
1539 /* rpms.s is no longer needed */
1540 #ifdef MULTIBYTE_SUPPORT
1542 * Ensure we don't start overwriting in the middle of a wide
1543 * character.
1545 while(rpms.sen > nbuf[rpms.tosln - 1] && rpms.sen->chr == WEOF) {
1546 extra_ellipsis++;
1547 rpms.sen--;
1549 #endif
1550 ZR_memcpy(rpms.sen, zr_mid_ellipsis1, ZR_MID_ELLIPSIS1_SIZE);
1551 rpms.sen += ZR_MID_ELLIPSIS1_SIZE;
1552 #ifdef MULTIBYTE_SUPPORT
1553 /* Extend if we backed off for a wide character */
1554 if (extra_ellipsis) {
1555 ZR_memset(rpms.sen, zr_dt, extra_ellipsis);
1556 rpms.sen += extra_ellipsis;
1558 #endif
1559 ZR_memcpy(rpms.sen, zr_mid_ellipsis2, ZR_MID_ELLIPSIS2_SIZE);
1560 nbuf[rpms.tosln][winw] = nbuf[rpms.tosln][winw + 1] = zr_zr;
1563 nlnct = rpms.ln + 1;
1564 for (iln = nlnct; iln < winh; iln++) {
1565 zfree(nbuf[iln], (winw + 2) * sizeof(**nbuf));
1566 nbuf[iln] = NULL;
1569 /* determine whether the right-prompt exists and can fit on the screen */
1570 if (!more_start) {
1571 if (trashedzle && opts[TRANSIENTRPROMPT])
1572 put_rpmpt = 0;
1573 else
1574 put_rpmpt = rprompth == 1 && rpromptbuf[0] &&
1575 !strchr(rpromptbuf, '\t') &&
1576 (int)ZR_strlen(nbuf[0]) + rpromptw < winw - 1;
1577 } else {
1578 /* insert >.... on first line if there is more text before start of screen */
1579 ZR_memset(nbuf[0], zr_sp, lpromptw);
1580 t0 = winw - lpromptw;
1581 t0 = t0 > ZR_START_ELLIPSIS_SIZE ? ZR_START_ELLIPSIS_SIZE : t0;
1582 ZR_memcpy(nbuf[0] + lpromptw, zr_start_ellipsis, t0);
1583 ZR_memset(nbuf[0] + lpromptw + t0, zr_sp, winw - t0 - lpromptw);
1584 nbuf[0][winw] = nbuf[0][winw + 1] = zr_zr;
1587 for (iln = 0; iln < nlnct; iln++) {
1588 /* if we have more lines than last time, clear the newly-used lines */
1589 if (iln >= olnct)
1590 cleareol = 1;
1592 /* if old line and new line are different,
1593 see if we can insert/delete a line to speed up update */
1595 if (!clearf && iln > 0 && iln < olnct - 1 &&
1596 !(hasam && vcs == winw) &&
1597 nbuf[iln] && obuf[iln] &&
1598 ZR_strncmp(obuf[iln], nbuf[iln], 16)) {
1599 if (tccan(TCDELLINE) && obuf[iln + 1] &&
1600 obuf[iln + 1][0].chr && nbuf[iln] &&
1601 !ZR_strncmp(obuf[iln + 1], nbuf[iln], 16)) {
1602 moveto(iln, 0);
1603 tcout(TCDELLINE);
1604 zfree(obuf[iln], (winw + 2) * sizeof(**obuf));
1605 for (t0 = iln; t0 != olnct; t0++)
1606 obuf[t0] = obuf[t0 + 1];
1607 obuf[--olnct] = NULL;
1609 /* don't try to insert a line if olnct = vmaxln (vmaxln is the number
1610 of lines that have been displayed by this routine) so that we don't
1611 go off the end of the screen. */
1613 else if (tccan(TCINSLINE) && olnct < vmaxln && nbuf[iln + 1] &&
1614 obuf[iln] && !ZR_strncmp(obuf[iln], nbuf[iln + 1], 16)) {
1615 moveto(iln, 0);
1616 tcout(TCINSLINE);
1617 for (t0 = olnct; t0 != iln; t0--)
1618 obuf[t0] = obuf[t0 - 1];
1619 obuf[iln] = NULL;
1620 olnct++;
1624 /* update the single line */
1625 refreshline(iln);
1627 /* output the right-prompt if appropriate */
1628 if (put_rpmpt && !iln && !oput_rpmpt) {
1629 int attrchange;
1631 moveto(0, winw - 1 - rpromptw);
1632 zputs(rpromptbuf, shout);
1633 vcs = winw - 1;
1634 /* reset character attributes to that set by the main prompt */
1635 txtchange = pmpt_attr;
1637 * Keep attributes that have actually changed,
1638 * which are ones off in rpmpt_attr and on in
1639 * pmpt_attr, and vice versa.
1641 attrchange = txtchange &
1642 (TXT_ATTR_OFF_FROM_ON(rpmpt_attr) |
1643 TXT_ATTR_ON_FROM_OFF(rpmpt_attr));
1645 * Careful in case the colour changed.
1647 if (txtchangeisset(txtchange, TXTFGCOLOUR) &&
1648 (!txtchangeisset(rpmpt_attr, TXTFGCOLOUR) ||
1649 ((txtchange ^ rpmpt_attr) & TXT_ATTR_FG_COL_MASK)))
1651 attrchange |=
1652 txtchange & (TXTFGCOLOUR | TXT_ATTR_FG_COL_MASK);
1654 if (txtchangeisset(txtchange, TXTBGCOLOUR) &&
1655 (!txtchangeisset(rpmpt_attr, TXTBGCOLOUR) ||
1656 ((txtchange ^ rpmpt_attr) & TXT_ATTR_BG_COL_MASK)))
1658 attrchange |=
1659 txtchange & (TXTBGCOLOUR | TXT_ATTR_BG_COL_MASK);
1662 * Now feed these changes into the usual function,
1663 * if necessary.
1665 if (attrchange)
1666 settextattributes(attrchange);
1670 /* if old buffer had extra lines, set them to be cleared and refresh them
1671 individually */
1673 if (olnct > nlnct) {
1674 cleareol = 1;
1675 for (iln = nlnct; iln < olnct; iln++)
1676 refreshline(iln);
1679 /* reset character attributes */
1680 if (clearf && postedit) {
1681 if ((txtchange = pmpt_attr ? pmpt_attr : rpmpt_attr))
1682 settextattributes(txtchange);
1684 clearf = 0;
1685 oput_rpmpt = put_rpmpt;
1687 /* move to the new cursor position */
1688 moveto(rpms.nvln, rpms.nvcs);
1690 /* swap old and new buffers - better than freeing/allocating every time */
1691 bufswap();
1693 /* store current values so we can use them next time */
1694 ovln = rpms.nvln;
1695 olnct = nlnct;
1696 onumscrolls = numscrolls;
1697 if (nlnct > vmaxln)
1698 vmaxln = nlnct;
1699 singlelineout:
1700 fflush(shout); /* make sure everything is written out */
1702 if (tmpalloced)
1703 zfree(tmpline, tmpll * sizeof(*tmpline));
1705 zle_free_highlight();
1707 /* if we have a new list showing, note it; if part of the list has been
1708 overwritten, redisplay it. We have to metafy line back before calling
1709 completion code */
1710 if (showinglist == -2 || (showinglist > 0 && showinglist < nlnct)) {
1711 if (remetafy) {
1712 metafy_line();
1713 remetafy = 0;
1715 inlist = 1;
1716 listmatches();
1717 inlist = 0;
1718 zrefresh();
1720 if (showinglist == -1)
1721 showinglist = nlnct;
1723 if (remetafy)
1724 metafy_line();
1727 #define tcinscost(X) (tccan(TCMULTINS) ? tclen[TCMULTINS] : (X)*tclen[TCINS])
1728 #define tcdelcost(X) (tccan(TCMULTDEL) ? tclen[TCMULTDEL] : (X)*tclen[TCDEL])
1729 #define tc_delchars(X) (void) tcmultout(TCDEL, TCMULTDEL, (X))
1730 #define tc_inschars(X) (void) tcmultout(TCINS, TCMULTINS, (X))
1731 #define tc_upcurs(X) (void) tcmultout(TCUP, TCMULTUP, (X))
1732 #define tc_leftcurs(X) (void) tcmultout(TCLEFT, TCMULTLEFT, (X))
1735 * Once again, in the multibyte case the arguments must be in the
1736 * order: element of old video array, element of new video array.
1738 static int
1739 wpfxlen(const REFRESH_ELEMENT *olds, const REFRESH_ELEMENT *news)
1741 int i = 0;
1743 while (olds->chr && ZR_equal(*olds, *news))
1744 olds++, news++, i++;
1745 return i;
1748 /* refresh one line, using whatever speed-up tricks are provided by the tty */
1750 /**/
1751 static void
1752 refreshline(int ln)
1754 REFRESH_STRING nl, ol, p1; /* line buffer pointers */
1755 int ccs = 0, /* temporary count for cursor position */
1756 char_ins = 0, /* number of characters inserted/deleted */
1757 col_cleareol, /* clear to end-of-line from this column */
1758 i, j, /* tmp */
1759 ins_last, /* insert pushed last character off line */
1760 nllen, ollen, /* new and old line buffer lengths */
1761 rnllen; /* real new line buffer length */
1763 /* 0: setup */
1764 nl = nbuf[ln];
1765 rnllen = nllen = nl ? ZR_strlen(nl) : 0;
1766 if (obuf[ln]) {
1767 ol = obuf[ln];
1768 ollen = ZR_strlen(ol);
1770 else {
1771 static REFRESH_ELEMENT nullchr = { ZWC('\0'), 0 };
1772 ol = &nullchr;
1773 ollen = 0;
1776 /* optimisation: can easily happen for clearing old lines. If the terminal has
1777 the capability, then this is the easiest way to skip unnecessary stuff */
1778 if (cleareol && !nllen && !(hasam && ln < nlnct - 1)
1779 && tccan(TCCLEAREOL)) {
1780 moveto(ln, 0);
1781 tcoutclear(TCCLEAREOL);
1782 return;
1785 /* 1: pad out the new buffer with spaces to contain _all_ of the characters
1786 which need to be written. do this now to allow some pre-processing */
1788 if (cleareol /* request to clear to end of line */
1789 || (!nllen && (ln != 0 || !put_rpmpt)) /* no line buffer given */
1790 || (ln == 0 && (put_rpmpt != oput_rpmpt))) { /* prompt changed */
1791 p1 = zhalloc((winw + 2) * sizeof(*p1));
1792 if (nllen)
1793 ZR_memcpy(p1, nl, nllen);
1794 ZR_memset(p1 + nllen, zr_sp, winw - nllen);
1795 p1[winw] = zr_zr;
1796 if (nllen < winw)
1797 p1[winw + 1] = zr_zr;
1798 else
1799 p1[winw + 1] = nl[winw + 1];
1800 if (ln && nbuf[ln])
1801 ZR_memcpy(nl, p1, winw + 2); /* next time obuf will be up-to-date */
1802 else
1803 nl = p1; /* don't keep the padding for prompt line */
1804 nllen = winw;
1805 } else if (ollen > nllen) { /* make new line at least as long as old */
1806 p1 = zhalloc((ollen + 1) * sizeof(*p1));
1807 ZR_memcpy(p1, nl, nllen);
1808 ZR_memset(p1 + nllen, zr_sp, ollen - nllen);
1809 p1[ollen] = zr_zr;
1810 nl = p1;
1811 nllen = ollen;
1814 /* 2: see if we can clear to end-of-line, and if it's faster, work out where
1815 to do it from - we can normally only do so if there's no right-prompt.
1816 With automatic margins, we shouldn't do it if there is another line, in
1817 case it messes up cut and paste. */
1819 if (hasam && ln < nlnct - 1 && rnllen == winw)
1820 col_cleareol = -2; /* clearing eol would be evil so don't */
1821 else {
1822 col_cleareol = -1;
1823 if (tccan(TCCLEAREOL) && (nllen == winw || put_rpmpt != oput_rpmpt)) {
1824 for (i = nllen; i && ZR_equal(zr_sp, nl[i - 1]); i--)
1826 for (j = ollen; j && ZR_equal(ol[j - 1], zr_sp); j--)
1828 if ((j > i + tclen[TCCLEAREOL]) /* new buf has enough spaces */
1829 || (nllen == winw && ZR_equal(zr_sp, nl[winw - 1])))
1830 col_cleareol = i;
1834 /* 2b: first a new trick for automargin niceness - good for cut and paste */
1836 if (hasam && vcs == winw) {
1837 if (nbuf[vln] && nbuf[vln][vcs + 1].chr == ZWC('\n')) {
1838 vln++, vcs = 1;
1839 if (nbuf[vln] && nbuf[vln]->chr) {
1840 zputc(nbuf[vln]);
1841 } else
1842 zputc(&zr_sp); /* I don't think this should happen */
1843 if (ln == vln) { /* better safe than sorry */
1844 nl++;
1845 if (ol->chr)
1846 ol++;
1847 ccs = 1;
1848 } /* else hmmm... I wonder what happened */
1849 } else {
1850 vln++, vcs = 0;
1851 zputc(&zr_nl);
1854 ins_last = 0;
1856 /* 2c: if we're on the first line, start checking at the end of the prompt;
1857 we shouldn't be doing anything within the prompt */
1859 if (ln == 0 && lpromptw) {
1860 i = lpromptw - ccs;
1861 j = ZR_strlen(ol);
1862 nl += i;
1863 ol += (i > j ? j : i); /* if ol is too short, point it to '\0' */
1864 ccs = lpromptw;
1867 #ifdef MULTIBYTE_SUPPORT
1869 * Realign to a real character after any jiggery pokery at
1870 * the start of the line.
1872 while (nl->chr == WEOF) {
1873 nl++, ccs++, vcs++;
1874 if (ol->chr)
1875 ol++;
1877 #endif
1879 /* 3: main display loop - write out the buffer using whatever tricks we can */
1881 for (;;) {
1882 int now_off;
1884 #ifdef MULTIBYTE_SUPPORT
1885 if ((!nl->chr || nl->chr != WEOF) && (!ol->chr || ol->chr != WEOF)) {
1886 #endif
1887 if (nl->chr && ol->chr && ZR_equal(ol[1], nl[1])) {
1888 /* skip only if second chars match */
1889 #ifdef MULTIBYTE_SUPPORT
1890 int ccs_was = ccs;
1891 #endif
1892 /* skip past all matching characters */
1893 for (; nl->chr && ZR_equal(*ol, *nl); nl++, ol++, ccs++)
1895 #ifdef MULTIBYTE_SUPPORT
1896 /* Make sure ol and nl are pointing to real characters */
1897 while ((nl->chr == WEOF || ol->chr == WEOF) && ccs > ccs_was) {
1898 nl--;
1899 ol--;
1900 ccs--;
1902 #endif
1905 if (!nl->chr) {
1906 if (ccs == winw && hasam && char_ins > 0 && ins_last
1907 && vcs != winw) {
1908 nl--; /* we can assume we can go back here */
1909 moveto(ln, winw - 1);
1910 zputc(nl);
1911 vcs++;
1912 return; /* write last character in line */
1914 if ((char_ins <= 0) || (ccs >= winw)) /* written everything */
1915 return;
1916 if (tccan(TCCLEAREOL) && (char_ins >= tclen[TCCLEAREOL])
1917 && col_cleareol != -2)
1918 /* we've got junk on the right yet to clear */
1919 col_cleareol = 0; /* force a clear to end of line */
1922 moveto(ln, ccs); /* move to where we do all output from */
1924 /* if we can finish quickly, do so */
1925 if ((col_cleareol >= 0) && (ccs >= col_cleareol)) {
1926 tcoutclear(TCCLEAREOL);
1927 return;
1930 /* we've written out the new but yet to clear rubbish due to inserts */
1931 if (!nl->chr) {
1932 i = (winw - ccs < char_ins) ? (winw - ccs) : char_ins;
1933 if (tccan(TCDEL) && (tcdelcost(i) <= i + 1))
1934 tc_delchars(i);
1935 else {
1936 vcs += i;
1937 while (i-- > 0)
1938 zputc(&zr_sp);
1940 return;
1943 /* if we've reached the end of the old buffer, then there are few tricks
1944 we can do, so we just dump out what we must and clear if we can */
1945 if (!ol->chr) {
1946 i = (col_cleareol >= 0) ? col_cleareol : nllen;
1947 i -= vcs;
1948 if (i < 0) {
1950 * This shouldn't be necessary, but it's better
1951 * than a crash if there's a bug somewhere else,
1952 * so report in debug mode.
1954 DPUTS(1, "BUG: badly calculated old line width in refresh");
1955 i = 0;
1957 zwrite(nl, i);
1958 vcs += i;
1959 if (col_cleareol >= 0)
1960 tcoutclear(TCCLEAREOL);
1961 return;
1964 /* inserting & deleting chars: we can if there's no right-prompt */
1965 if ((ln || !put_rpmpt || !oput_rpmpt)
1966 #ifdef MULTIBYTE_SUPPORT
1967 && ol->chr != WEOF && nl->chr != WEOF
1968 #endif
1969 && nl[1].chr && ol[1].chr && !ZR_equal(ol[1], nl[1])) {
1971 /* deleting characters - see if we can find a match series that
1972 makes it cheaper to delete intermediate characters
1973 eg. oldline: hifoobar \ hopefully cheaper here to delete two
1974 newline: foobar / characters, then we have six matches */
1975 if (tccan(TCDEL)) {
1976 int first = 1;
1977 for (i = 1; ol[i].chr; i++)
1978 if (tcdelcost(i) < wpfxlen(ol + i, nl)) {
1980 * Some terminals will output the current
1981 * attributes into cells added at the end by
1982 * deletions, so turn off text attributes.
1984 if (first) {
1985 clearattributes();
1986 first = 0;
1988 tc_delchars(i);
1989 ol += i;
1990 char_ins -= i;
1991 #ifdef MULTIBYTE_SUPPORT
1992 while (ol->chr == WEOF) {
1993 ol++;
1994 char_ins--;
1996 #endif
1997 i = 0;
1998 break;
2000 if (!i)
2001 continue;
2003 /* inserting characters - characters pushed off the right should be
2004 annihilated, but we don't do this if we're on the last line lest
2005 undesired scrolling occurs due to `illegal' characters on screen */
2007 if (tccan(TCINS) && (vln != lines - 1)) { /* not on last line */
2008 for (i = 1; nl[i].chr; i++)
2009 if (tcinscost(i) < wpfxlen(ol, nl + i)) {
2010 tc_inschars(i);
2011 zwrite(nl, i);
2012 nl += i;
2013 #ifdef MULTIBYTE_SUPPORT
2014 while (nl->chr == WEOF) {
2015 nl++;
2016 i++;
2018 #endif
2019 char_ins += i;
2020 ccs = (vcs += i);
2021 /* if we've pushed off the right, truncate oldline */
2022 for (i = 0; ol[i].chr && i < winw - ccs; i++);
2023 #ifdef MULTIBYTE_SUPPORT
2024 while (ol[i].chr == WEOF)
2025 i++;
2026 #endif
2027 if (i >= winw - ccs) {
2028 ol[i] = zr_zr;
2029 ins_last = 1;
2031 i = 0;
2032 break;
2034 if (!i)
2035 continue;
2038 #ifdef MULTIBYTE_SUPPORT
2040 #endif
2041 /* we can't do any fancy tricks, so just dump the single character
2042 and keep on trying */
2043 #ifdef MULTIBYTE_SUPPORT
2045 * in case we were tidying up a funny-width character when we
2046 * reached the end of the new line...
2048 if (!nl->chr)
2049 break;
2050 do {
2051 #endif
2053 * If an attribute was on here but isn't any more,
2054 * output the sequence to turn it off.
2056 now_off = ol->atr & ~nl->atr & TXT_ATTR_ON_MASK;
2057 if (now_off)
2058 settextattributes(TXT_ATTR_OFF_FROM_ON(now_off));
2060 zputc(nl);
2061 nl++, ol++;
2062 ccs++, vcs++;
2063 #ifdef MULTIBYTE_SUPPORT
2065 * Make sure we always overwrite the complete width of
2066 * a character that was there before.
2068 } while ((ol->chr == WEOF && nl->chr) ||
2069 (nl->chr == WEOF && ol->chr));
2070 #endif
2074 /* move the cursor to line ln (relative to the prompt line),
2075 absolute column cl; update vln, vcs - video line and column */
2077 /**/
2078 void
2079 moveto(int ln, int cl)
2081 const REFRESH_ELEMENT *rep;
2083 if (vcs == winw) {
2084 vln++, vcs = 0;
2085 if (!hasam) {
2086 zputc(&zr_cr);
2087 zputc(&zr_nl);
2088 } else {
2089 if ((vln < nlnct) && nbuf[vln] && nbuf[vln]->chr)
2090 rep = nbuf[vln];
2091 else
2092 rep = &zr_sp;
2093 zputc(rep);
2094 zputc(&zr_cr);
2095 if ((vln < olnct) && obuf[vln] && obuf[vln]->chr)
2096 *obuf[vln] = *rep;
2100 if (ln == vln && cl == vcs)
2101 return;
2103 /* move up */
2104 if (ln < vln) {
2105 tc_upcurs(vln - ln);
2106 vln = ln;
2108 /* move down; if we might go off the end of the screen, use newlines
2109 instead of TCDOWN */
2111 while (ln > vln) {
2112 if (vln < vmaxln - 1) {
2113 if (ln > vmaxln - 1) {
2114 if (tc_downcurs(vmaxln - 1 - vln))
2115 vcs = 0;
2116 vln = vmaxln - 1;
2117 } else {
2118 if (tc_downcurs(ln - vln))
2119 vcs = 0;
2120 vln = ln;
2121 continue;
2124 zputc(&zr_cr), vcs = 0; /* safety precaution */
2125 while (ln > vln) {
2126 zputc(&zr_nl);
2127 vln++;
2131 if (cl != vcs)
2132 singmoveto(cl);
2135 /**/
2136 mod_export int
2137 tcmultout(int cap, int multcap, int ct)
2139 if (tccan(multcap) && (!tccan(cap) || tclen[multcap] <= tclen[cap] * ct)) {
2140 tcoutarg(multcap, ct);
2141 return 1;
2142 } else if (tccan(cap)) {
2143 while (ct--)
2144 tcout(cap);
2145 return 1;
2147 return 0;
2150 /* ct: number of characters to move across */
2151 /**/
2152 static void
2153 tc_rightcurs(int ct)
2155 int cl, /* ``desired'' absolute horizontal position */
2156 i = vcs, /* cursor position after initial movements */
2158 REFRESH_STRING t;
2160 cl = ct + vcs;
2162 /* do a multright if we can - it's the most reliable */
2163 if (tccan(TCMULTRIGHT)) {
2164 tcoutarg(TCMULTRIGHT, ct);
2165 return;
2168 /* do an absolute horizontal position if we can */
2169 if (tccan(TCHORIZPOS)) {
2170 tcoutarg(TCHORIZPOS, cl);
2171 return;
2174 /* XXX: should really check "it" in termcap and use / and % */
2175 /* try tabs if tabs are non destructive and multright is not possible */
2176 if (!oxtabs && tccan(TCNEXTTAB) && ((vcs | 7) < cl)) {
2177 i = (vcs | 7) + 1;
2178 tcout(TCNEXTTAB);
2179 for ( ; i + 8 <= cl; i += 8)
2180 tcout(TCNEXTTAB);
2181 if ((ct = cl - i) == 0) /* number of chars still to move across */
2182 return;
2185 /* otherwise _carefully_ write the contents of the video buffer.
2186 if we're anywhere in the prompt, goto the left column and write the whole
2187 prompt out.
2189 If strlen(lpromptbuf) == lpromptw, we can cheat and output
2190 the appropriate chunk of the string. This test relies on the
2191 fact that any funny business will always make the length of
2192 the string larger than the printing width, so if they're the same
2193 we have only ASCII characters or a single-byte extension of ASCII.
2194 Unfortunately this trick won't work if there are potentially
2195 characters occupying more than one column. We could flag that
2196 this has happened (since it's not that common to have characters
2197 wider than one column), but for now it's easier not to use the
2198 trick if we are using WCWIDTH() on the prompt. It's not that
2199 common to be editing in the middle of the prompt anyway, I would
2200 think.
2202 if (vln == 0 && i < lpromptw && !(termflags & TERM_SHORT)) {
2203 #ifndef MULTIBYTE_SUPPORT
2204 if ((int)strlen(lpromptbuf) == lpromptw)
2205 fputs(lpromptbuf + i, shout);
2206 else
2207 #endif
2208 if (tccan(TCRIGHT) && (tclen[TCRIGHT] * ct <= ztrlen(lpromptbuf)))
2209 /* it is cheaper to send TCRIGHT than reprint the whole prompt */
2210 for (ct = lpromptw - i; ct--; )
2211 tcout(TCRIGHT);
2212 else {
2213 if (i != 0)
2214 zputc(&zr_cr);
2215 tc_upcurs(lprompth - 1);
2216 zputs(lpromptbuf, shout);
2217 if (lpromptwof == winw)
2218 zputs("\n", shout); /* works with both hasam and !hasam */
2220 i = lpromptw;
2221 ct = cl - i;
2224 if (nbuf[vln]) {
2225 for (j = 0, t = nbuf[vln]; t->chr && (j < i); j++, t++);
2226 if (j == i)
2227 for ( ; t->chr && ct; ct--, t++)
2228 zputc(t);
2230 while (ct--)
2231 zputc(&zr_sp); /* not my fault your terminal can't go right */
2234 /**/
2235 mod_export int
2236 tc_downcurs(int ct)
2238 int ret = 0;
2240 if (ct && !tcmultout(TCDOWN, TCMULTDOWN, ct)) {
2241 while (ct--)
2242 zputc(&zr_nl);
2243 zputc(&zr_cr), ret = -1;
2245 return ret;
2248 /**/
2249 mod_export void
2250 tcout(int cap)
2252 tputs(tcstr[cap], 1, putshout);
2253 SELECT_ADD_COST(tclen[cap]);
2256 /**/
2257 static void
2258 tcoutarg(int cap, int arg)
2260 char *result;
2262 result = tgoto(tcstr[cap], arg, arg);
2263 tputs(result, 1, putshout);
2264 SELECT_ADD_COST(strlen(result));
2267 /**/
2268 mod_export int
2269 clearscreen(UNUSED(char **args))
2271 tcoutclear(TCCLEARSCREEN);
2272 resetneeded = 1;
2273 clearflag = 0;
2274 return 0;
2277 /**/
2278 mod_export int
2279 redisplay(UNUSED(char **args))
2281 moveto(0, 0);
2282 zputc(&zr_cr); /* extra care */
2283 tc_upcurs(lprompth - 1);
2284 resetneeded = 1;
2285 clearflag = 0;
2286 return 0;
2290 * Show as much of the line buffer as we can in single line mode.
2291 * TBD: all termcap effects are turned off in this mode, so
2292 * there's no point in using character attributes. We should
2293 * decide what we're going to do and either remove the handling
2294 * from here or enable it in tsetcap().
2297 /**/
2298 static void
2299 singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
2301 REFRESH_STRING vbuf, vp, /* video buffer and pointer */
2302 refreshop; /* pointer to old video buffer */
2303 int t0, /* tmp */
2304 vsiz, /* size of new video buffer */
2305 nvcs = 0, /* new video cursor column */
2306 owinpos = winpos, /* previous window position */
2307 owinprompt = winprompt; /* previous winprompt */
2308 #ifdef MULTIBYTE_SUPPORT
2309 int width; /* width of multibyte character */
2310 #endif
2312 nlnct = 1;
2313 /* generate the new line buffer completely */
2314 for (vsiz = 1 + lpromptw, t0 = 0; t0 != tmpll; t0++) {
2315 if (tmpline[t0] == ZWC('\t'))
2316 vsiz = (vsiz | 7) + 2;
2317 #ifdef MULTIBYTE_SUPPORT
2318 else if (iswprint(tmpline[t0]) && (width = WCWIDTH(tmpline[t0]) > 0)) {
2319 vsiz += width;
2320 if (isset(COMBININGCHARS) && IS_BASECHAR(tmpline[t0])) {
2321 while (t0 < tmpll-1 && IS_COMBINING(tmpline[t0+1]))
2322 t0++;
2325 #endif
2326 else if (ZC_icntrl(tmpline[t0])
2327 #ifdef MULTIBYTE_SUPPORT
2328 && (unsigned)tmpline[t0] <= 0xffU
2329 #endif
2331 vsiz += 2;
2332 #ifdef MULTIBYTE_SUPPORT
2333 else
2334 vsiz += 10;
2335 #else
2336 else
2337 vsiz++;
2338 #endif
2340 vbuf = (REFRESH_STRING)zalloc(vsiz * sizeof(*vbuf));
2342 if (tmpcs < 0) {
2343 #ifdef DEBUG
2344 fprintf(stderr, "BUG: negative cursor position\n");
2345 fflush(stderr);
2346 #endif
2347 tmpcs = 0;
2350 /* prompt is not directly copied into the video buffer */
2351 ZR_memset(vbuf, zr_sp, lpromptw);
2352 vp = vbuf + lpromptw;
2353 *vp = zr_zr;
2355 for (t0 = 0; t0 < tmpll; t0++) {
2356 int base_atr_on = 0, base_atr_off = 0, ireg;
2357 int all_atr_on, all_atr_off;
2358 struct region_highlight *rhp;
2360 * Calculate attribute based on region.
2362 for (ireg = 0, rhp = region_highlights;
2363 ireg < n_region_highlights;
2364 ireg++, rhp++) {
2365 int offset;
2366 if (rhp->flags & ZRH_PREDISPLAY)
2367 offset = 0; /* include predisplay in start end */
2368 else
2369 offset = predisplaylen; /* increment over it */
2370 if (rhp->start + offset <= t0 &&
2371 t0 < rhp->end + offset) {
2372 if (base_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
2373 /* keep colour already set */
2374 base_atr_on |= rhp->atr & ~TXT_ATTR_COLOUR_ON_MASK;
2375 } else {
2376 /* no colour set yet */
2377 base_atr_on |= rhp->atr;
2379 if (t0 == rhp->end + offset - 1 ||
2380 t0 == tmpll - 1)
2381 base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr);
2384 if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
2385 /* keep colours from special attributes */
2386 all_atr_on = special_atr_on |
2387 (base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK);
2388 } else {
2389 /* keep colours from standard attributes */
2390 all_atr_on = special_atr_on | base_atr_on;
2392 all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
2394 if (tmpline[t0] == ZWC('\t')) {
2395 REFRESH_ELEMENT sp = zr_sp;
2396 sp.atr = base_atr_on;
2397 for (*vp++ = zr_sp; (vp - vbuf) & 7; )
2398 *vp++ = zr_sp;
2399 vp[-1].atr |= base_atr_off;
2400 } else if (tmpline[t0] == ZWC('\n')) {
2401 vp->chr = ZWC('\\');
2402 vp->atr = all_atr_on;
2403 vp++;
2404 vp->chr = ZWC('n');
2405 vp->atr = all_atr_on | all_atr_off;
2406 vp++;
2407 #ifdef MULTIBYTE_SUPPORT
2408 } else if (iswprint(tmpline[t0]) &&
2409 (width = WCWIDTH(tmpline[t0])) > 0) {
2410 int ichars;
2411 if (isset(COMBININGCHARS) && IS_BASECHAR(tmpline[t0])) {
2413 * Look for combining characters.
2415 for (ichars = 1; t0 + ichars < tmpll; ichars++) {
2416 if (!IS_COMBINING(tmpline[t0+ichars]))
2417 break;
2419 } else
2420 ichars = 1;
2421 vp->atr = base_atr_on | base_atr_off;
2422 if (ichars > 1)
2423 addmultiword(vp, tmpline+t0, ichars);
2424 else
2425 vp->chr = tmpline[t0];
2426 vp++;
2427 while (--width > 0) {
2428 vp->chr = WEOF;
2429 vp->atr = base_atr_on | base_atr_off;
2430 vp++;
2432 t0 += ichars - 1;
2433 #endif
2434 } else if (ZC_icntrl(tmpline[t0])
2435 #ifdef MULTIBYTE_SUPPORT
2436 && (unsigned)tmpline[t0] <= 0xffU
2437 #endif
2439 ZLE_INT_T t = tmpline[++t0];
2441 vp->chr = ZWC('^');
2442 vp->atr = all_atr_on;
2443 vp++;
2444 vp->chr = (((unsigned int)t & ~0x80u) > 31) ?
2445 ZWC('?') : (t | ZWC('@'));
2446 vp->atr = all_atr_on | all_atr_off;
2447 vp++;
2449 #ifdef MULTIBYTE_SUPPORT
2450 else {
2451 char dispchars[11];
2452 char *dispptr = dispchars;
2453 wchar_t wc;
2454 int started = 0;
2456 if ((unsigned)tmpline[t0] > 0xffffU) {
2457 sprintf(dispchars, "<%.08x>", (unsigned)tmpline[t0]);
2458 } else {
2459 sprintf(dispchars, "<%.04x>", (unsigned)tmpline[t0]);
2461 while (*dispptr) {
2462 if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */) {
2463 vp->chr = wc;
2464 if (!started)
2465 started = 1;
2466 vp->atr = all_atr_on;
2467 vp++;
2469 dispptr++;
2471 if (started)
2472 vp[-1].atr |= all_atr_off;
2474 #else
2475 else {
2476 vp->chr = tmpline[t0];
2477 vp->atr = base_atr_on | base_atr_off;
2478 vp++;
2480 #endif
2481 if (t0 == tmpcs)
2482 nvcs = vp - vbuf - 1;
2484 if (t0 == tmpcs)
2485 nvcs = vp - vbuf;
2486 *vp = zr_zr;
2488 /* determine which part of the new line buffer we want for the display */
2489 if (winpos == -1)
2490 winpos = 0;
2491 if ((winpos && nvcs < winpos + 1) || (nvcs > winpos + winw - 2)) {
2492 if ((winpos = nvcs - ((winw - hasam) / 2)) < 0)
2493 winpos = 0;
2495 if (winpos) {
2496 vbuf[winpos].chr = ZWC('<'); /* line continues to the left */
2497 vbuf[winpos].atr = 0;
2499 if ((int)ZR_strlen(vbuf + winpos) > (winw - hasam)) {
2500 vbuf[winpos + winw - hasam - 1].chr = ZWC('>'); /* line continues to right */
2501 vbuf[winpos + winw - hasam - 1].atr = 0;
2502 vbuf[winpos + winw - hasam] = zr_zr;
2504 ZR_strcpy(nbuf[0], vbuf + winpos);
2505 zfree(vbuf, vsiz * sizeof(*vbuf));
2506 nvcs -= winpos;
2508 if (winpos < lpromptw) {
2509 /* skip start of buffer corresponding to prompt */
2510 winprompt = lpromptw - winpos;
2511 } else {
2512 /* don't */
2513 winprompt = 0;
2515 if (winpos != owinpos && winprompt) {
2516 char *pptr;
2517 int skipping = 0, skipchars = winpos;
2519 * Need to output such part of the left prompt as fits.
2520 * Skip the first winpos characters, outputting
2521 * any characters marked with %{...%}.
2523 singmoveto(0);
2524 MB_METACHARINIT();
2525 for (pptr = lpromptbuf; *pptr; ) {
2526 if (*pptr == Inpar) {
2527 skipping = 1;
2528 pptr++;
2529 } else if (*pptr == Outpar) {
2530 skipping = 0;
2531 pptr++;
2532 } else {
2533 convchar_t cc;
2534 int mblen = MB_METACHARLENCONV(pptr, &cc);
2535 if (skipping || skipchars == 0)
2537 while (mblen) {
2538 #ifdef MULTIBYTE_SUPPORT
2539 if (cc == WEOF)
2540 fputc('?', shout);
2541 else
2542 #endif
2543 if (*pptr == Meta) {
2544 mblen--;
2545 fputc(*++pptr ^ 32, shout);
2546 } else {
2547 fputc(*pptr, shout);
2549 pptr++;
2550 mblen--;
2552 } else {
2553 skipchars--;
2554 pptr += mblen;
2558 vcs = winprompt;
2561 /* display the `visible' portion of the line buffer */
2562 t0 = winprompt;
2563 vp = *nbuf + winprompt;
2564 refreshop = *obuf + winprompt;
2565 for (;;) {
2567 * Skip past all matching characters, but if there used
2568 * to be a prompt here be careful since all manner of
2569 * nastiness may be around.
2571 if (vp - *nbuf >= owinprompt)
2572 for (; vp->chr && ZR_equal(*refreshop, *vp);
2573 t0++, vp++, refreshop++)
2576 if (!vp->chr && !refreshop->chr)
2577 break;
2579 singmoveto(t0); /* move to where we do all output from */
2581 if (!refreshop->chr) {
2582 if ((t0 = ZR_strlen(vp)))
2583 zwrite(vp, t0);
2584 vcs += t0;
2585 break;
2587 if (!vp->chr) {
2588 if (tccan(TCCLEAREOL))
2589 tcoutclear(TCCLEAREOL);
2590 else
2591 for (; refreshop++->chr; vcs++)
2592 zputc(&zr_sp);
2593 break;
2595 zputc(vp);
2596 vcs++, t0++;
2597 vp++, refreshop++;
2599 /* move to the new cursor position */
2600 singmoveto(nvcs);
2602 bufswap();
2605 /**/
2606 static void
2607 singmoveto(int pos)
2609 if (pos == vcs)
2610 return;
2612 /* choose cheapest movements for ttys without multiple movement capabilities -
2613 do this now because it's easier (to code) */
2615 if ((!tccan(TCMULTLEFT) || pos == 0) && (pos <= vcs / 2)) {
2616 zputc(&zr_cr);
2617 vcs = 0;
2620 if (pos < vcs)
2621 tc_leftcurs(vcs - pos);
2622 else if (pos > vcs)
2623 tc_rightcurs(pos - vcs);
2625 vcs = pos;
2628 /* Provided for loading the module in a modular fashion */
2630 /**/
2631 void
2632 zle_refresh_boot(void)
2636 /* Provided for unloading the module in a modular fashion */
2638 /**/
2639 void
2640 zle_refresh_finish(void)
2642 freevideo();
2644 if (region_highlights)
2645 zfree(region_highlights,
2646 sizeof(struct region_highlight) * n_region_highlights);