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, ®ion_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