Home | History | Annotate | Line # | Download | only in dist
format-draw.c revision 1.1
      1 /* $OpenBSD$ */
      2 
      3 /*
      4  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott (at) gmail.com>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
     15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <sys/types.h>
     20 
     21 #include <stdlib.h>
     22 #include <string.h>
     23 
     24 #include "tmux.h"
     25 
     26 /* Format range. */
     27 struct format_range {
     28 	u_int				 index;
     29 	struct screen			*s;
     30 
     31 	u_int				 start;
     32 	u_int				 end;
     33 
     34 	enum style_range_type		 type;
     35 	u_int				 argument;
     36 
     37 	TAILQ_ENTRY(format_range)	 entry;
     38 };
     39 TAILQ_HEAD(format_ranges, format_range);
     40 
     41 /* Does this range match this style? */
     42 static int
     43 format_is_type(struct format_range *fr, struct style *sy)
     44 {
     45 	if (fr->type != sy->range_type)
     46 		return (0);
     47 	if (fr->type == STYLE_RANGE_WINDOW &&
     48 	    fr->argument != sy->range_argument)
     49 		return (0);
     50 	return (1);
     51 }
     52 
     53 /* Free a range. */
     54 static void
     55 format_free_range(struct format_ranges *frs, struct format_range *fr)
     56 {
     57 	TAILQ_REMOVE(frs, fr, entry);
     58 	free(fr);
     59 }
     60 
     61 /* Fix range positions. */
     62 static void
     63 format_update_ranges(struct format_ranges *frs, struct screen *s, u_int offset,
     64     u_int start, u_int width)
     65 {
     66 	struct format_range	*fr, *fr1;
     67 
     68 	if (frs == NULL)
     69 		return;
     70 
     71 	TAILQ_FOREACH_SAFE(fr, frs, entry, fr1) {
     72 		if (fr->s != s)
     73 			continue;
     74 
     75 		if (fr->end <= start || fr->start >= start + width) {
     76 			format_free_range(frs, fr);
     77 			continue;
     78 		}
     79 
     80 		if (fr->start < start)
     81 			fr->start = start;
     82 		if (fr->end > start + width)
     83 			fr->end = start + width;
     84 		if (fr->start == fr->end) {
     85 			format_free_range(frs, fr);
     86 			continue;
     87 		}
     88 
     89 		fr->start -= start;
     90 		fr->end -= start;
     91 
     92 		fr->start += offset;
     93 		fr->end += offset;
     94 	}
     95 }
     96 
     97 /* Draw a part of the format. */
     98 static void
     99 format_draw_put(struct screen_write_ctx *octx, u_int ocx, u_int ocy,
    100     struct screen *s, struct format_ranges *frs, u_int offset, u_int start,
    101     u_int width)
    102 {
    103 	/*
    104 	 * The offset is how far from the cursor on the target screen; start
    105 	 * and width how much to copy from the source screen.
    106 	 */
    107 	screen_write_cursormove(octx, ocx + offset, ocy, 0);
    108 	screen_write_fast_copy(octx, s, start, 0, width, 1);
    109 	format_update_ranges(frs, s, offset, start, width);
    110 }
    111 
    112 /* Draw list part of format. */
    113 static void
    114 format_draw_put_list(struct screen_write_ctx *octx,
    115     u_int ocx, u_int ocy, u_int offset, u_int width, struct screen *list,
    116     struct screen *list_left, struct screen *list_right, int focus_start,
    117     int focus_end, struct format_ranges *frs)
    118 {
    119 	u_int	start, focus_centre;
    120 
    121 	/* If there is enough space for the list, draw it entirely. */
    122 	if (width >= list->cx) {
    123 		format_draw_put(octx, ocx, ocy, list, frs, offset, 0, width);
    124 		return;
    125 	}
    126 
    127 	/* The list needs to be trimmed. Try to keep the focus visible. */
    128 	focus_centre = focus_start + (focus_end - focus_start) / 2;
    129 	if (focus_centre < width / 2)
    130 		start = 0;
    131 	else
    132 		start = focus_centre - width / 2;
    133 	if (start + width > list->cx)
    134 		start = list->cx - width;
    135 
    136 	/* Draw <> markers at either side if needed. */
    137 	if (start != 0 && width > list_left->cx) {
    138 		screen_write_cursormove(octx, ocx + offset, ocy, 0);
    139 		screen_write_fast_copy(octx, list_left, 0, 0, list_left->cx, 1);
    140 		offset += list_left->cx;
    141 		start += list_left->cx;
    142 		width -= list_left->cx;
    143 	}
    144 	if (start + width < list->cx && width > list_right->cx) {
    145 		screen_write_cursormove(octx, ocx + offset + width - 1, ocy, 0);
    146 		screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx,
    147 		    1);
    148 		width -= list_right->cx;
    149 	}
    150 
    151 	/* Draw the list screen itself. */
    152 	format_draw_put(octx, ocx, ocy, list, frs, offset, start, width);
    153 }
    154 
    155 /* Draw format with no list. */
    156 static void
    157 format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx,
    158     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
    159     struct format_ranges *frs)
    160 {
    161 	u_int	width_left, width_centre, width_right;
    162 
    163 	width_left = left->cx;
    164 	width_centre = centre->cx;
    165 	width_right = right->cx;
    166 
    167 	/*
    168 	 * Try to keep as much of the left and right as possible at the expense
    169 	 * of the centre.
    170 	 */
    171 	while (width_left + width_centre + width_right > available) {
    172 		if (width_centre > 0)
    173 			width_centre--;
    174 		else if (width_right > 0)
    175 			width_right--;
    176 		else
    177 			width_left--;
    178 	}
    179 
    180 	/* Write left. */
    181 	format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
    182 
    183 	/* Write right at available - width_right. */
    184 	format_draw_put(octx, ocx, ocy, right, frs,
    185 	    available - width_right,
    186 	    right->cx - width_right,
    187 	    width_right);
    188 
    189 	/*
    190 	 * Write centre halfway between
    191 	 *     width_left
    192 	 * and
    193 	 *     available - width_right.
    194 	 */
    195 	format_draw_put(octx, ocx, ocy, centre, frs,
    196 	    width_left
    197 	    + ((available - width_right) - width_left) / 2
    198 	    - width_centre / 2,
    199 	    centre->cx / 2 - width_centre / 2,
    200 	    width_centre);
    201 }
    202 
    203 /* Draw format with list on the left. */
    204 static void
    205 format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
    206     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
    207     struct screen *list, struct screen *list_left, struct screen *list_right,
    208     struct screen *after, int focus_start, int focus_end,
    209     struct format_ranges *frs)
    210 {
    211 	u_int			width_left, width_centre, width_right;
    212 	u_int			width_list, width_after;
    213 	struct screen_write_ctx	ctx;
    214 
    215 	width_left = left->cx;
    216 	width_centre = centre->cx;
    217 	width_right = right->cx;
    218 	width_list = list->cx;
    219 	width_after = after->cx;
    220 
    221 	/*
    222 	 * Trim first the centre, then the list, then the right, then after the
    223 	 * list, then the left.
    224 	 */
    225 	while (width_left +
    226 	    width_centre +
    227 	    width_right +
    228 	    width_list +
    229 	    width_after > available) {
    230 		if (width_centre > 0)
    231 			width_centre--;
    232 		else if (width_list > 0)
    233 			width_list--;
    234 		else if (width_right > 0)
    235 			width_right--;
    236 		else if (width_after > 0)
    237 			width_after--;
    238 		else
    239 			width_left--;
    240 	}
    241 
    242 	/* If there is no list left, pass off to the no list function. */
    243 	if (width_list == 0) {
    244 		screen_write_start(&ctx, NULL, left);
    245 		screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
    246 		screen_write_stop(&ctx);
    247 
    248 		format_draw_none(octx, available, ocx, ocy, left, centre,
    249 		    right, frs);
    250 		return;
    251 	}
    252 
    253 	/* Write left at 0. */
    254 	format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
    255 
    256 	/* Write right at available - width_right. */
    257 	format_draw_put(octx, ocx, ocy, right, frs,
    258 	    available - width_right,
    259 	    right->cx - width_right,
    260 	    width_right);
    261 
    262 	/* Write after at width_left + width_list. */
    263 	format_draw_put(octx, ocx, ocy, after, frs,
    264 	    width_left + width_list,
    265 	    0,
    266 	    width_after);
    267 
    268 	/*
    269 	 * Write centre halfway between
    270 	 *     width_left + width_list + width_after
    271 	 * and
    272 	 *     available - width_right.
    273 	 */
    274 	format_draw_put(octx, ocx, ocy, centre, frs,
    275 	    (width_left + width_list + width_after)
    276 	    + ((available - width_right)
    277 		- (width_left + width_list + width_after)) / 2
    278 	    - width_centre / 2,
    279 	    centre->cx / 2 - width_centre / 2,
    280 	    width_centre);
    281 
    282 	/*
    283 	 * The list now goes from
    284 	 *     width_left
    285 	 * to
    286 	 *     width_left + width_list.
    287 	 * If there is no focus given, keep the left in focus.
    288 	 */
    289 	if (focus_start == -1 || focus_end == -1)
    290 		focus_start = focus_end = 0;
    291 	format_draw_put_list(octx, ocx, ocy, width_left, width_list, list,
    292 	    list_left, list_right, focus_start, focus_end, frs);
    293 }
    294 
    295 /* Draw format with list in the centre. */
    296 static void
    297 format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
    298     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
    299     struct screen *list, struct screen *list_left, struct screen *list_right,
    300     struct screen *after, int focus_start, int focus_end,
    301     struct format_ranges *frs)
    302 {
    303 	u_int			width_left, width_centre, width_right;
    304 	u_int			width_list, width_after, middle;
    305 	struct screen_write_ctx	ctx;
    306 
    307 	width_left = left->cx;
    308 	width_centre = centre->cx;
    309 	width_right = right->cx;
    310 	width_list = list->cx;
    311 	width_after = after->cx;
    312 
    313 	/*
    314 	 * Trim first the list, then after the list, then the centre, then the
    315 	 * right, then the left.
    316 	 */
    317 	while (width_left +
    318 	    width_centre +
    319 	    width_right +
    320 	    width_list +
    321 	    width_after > available) {
    322 		if (width_list > 0)
    323 			width_list--;
    324 		else if (width_after > 0)
    325 			width_after--;
    326 		else if (width_centre > 0)
    327 			width_centre--;
    328 		else if (width_right > 0)
    329 			width_right--;
    330 		else
    331 			width_left--;
    332 	}
    333 
    334 	/* If there is no list left, pass off to the no list function. */
    335 	if (width_list == 0) {
    336 		screen_write_start(&ctx, NULL, centre);
    337 		screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
    338 		screen_write_stop(&ctx);
    339 
    340 		format_draw_none(octx, available, ocx, ocy, left, centre,
    341 		    right, frs);
    342 		return;
    343 	}
    344 
    345 	/* Write left at 0. */
    346 	format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
    347 
    348 	/* Write after at available - width_after. */
    349 	format_draw_put(octx, ocx, ocy, after, frs,
    350 	    available - width_after,
    351 	    after->cx - width_after,
    352 	    width_after);
    353 
    354 	/* Write right at available - width_right. */
    355 	format_draw_put(octx, ocx, ocy, right, frs,
    356 	    available - width_right,
    357 	    right->cx - width_right,
    358 	    width_right);
    359 
    360 	/*
    361 	 * All three centre sections are offset from the middle of the
    362 	 * available space.
    363 	 */
    364 	middle = (width_left + ((available - width_right) - width_left) / 2);
    365 
    366 	/*
    367 	 * Write centre at
    368 	 *     middle - width_list / 2 - width_centre.
    369 	 */
    370 	format_draw_put(octx, ocx, ocy, centre, frs,
    371 	    middle - width_list / 2 - width_centre,
    372 	    0,
    373 	    width_centre);
    374 
    375 	/*
    376 	 * Write after at
    377 	 *     middle + width_list / 2 - width_centre.
    378 	 */
    379 	format_draw_put(octx, ocx, ocy, after, frs,
    380 	    middle + width_list / 2,
    381 	    0,
    382 	    width_after);
    383 
    384 	/*
    385 	 * The list now goes from
    386 	 *     middle - width_list / 2
    387 	 * to
    388 	 *     middle + width_list / 2
    389 	 * If there is no focus given, keep the centre in focus.
    390 	 */
    391 	if (focus_start == -1 || focus_end == -1)
    392 		focus_start = focus_end = list->cx / 2;
    393 	format_draw_put_list(octx, ocx, ocy, middle - width_list / 2,
    394 	    width_list, list, list_left, list_right, focus_start, focus_end,
    395 	    frs);
    396 }
    397 
    398 /* Draw format with list on the right. */
    399 static void
    400 format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
    401     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
    402     struct screen *list, struct screen *list_left, struct screen *list_right,
    403     struct screen *after, int focus_start, int focus_end,
    404     struct format_ranges *frs)
    405 {
    406 	u_int			width_left, width_centre, width_right;
    407 	u_int			width_list, width_after;
    408 	struct screen_write_ctx	ctx;
    409 
    410 	width_left = left->cx;
    411 	width_centre = centre->cx;
    412 	width_right = right->cx;
    413 	width_list = list->cx;
    414 	width_after = after->cx;
    415 
    416 	/*
    417 	 * Trim first the centre, then the list, then the right, then
    418 	 * after the list, then the left.
    419 	 */
    420 	while (width_left +
    421 	    width_centre +
    422 	    width_right +
    423 	    width_list +
    424 	    width_after > available) {
    425 		if (width_centre > 0)
    426 			width_centre--;
    427 		else if (width_list > 0)
    428 			width_list--;
    429 		else if (width_right > 0)
    430 			width_right--;
    431 		else if (width_after > 0)
    432 			width_after--;
    433 		else
    434 			width_left--;
    435 	}
    436 
    437 	/* If there is no list left, pass off to the no list function. */
    438 	if (width_list == 0) {
    439 		screen_write_start(&ctx, NULL, right);
    440 		screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
    441 		screen_write_stop(&ctx);
    442 
    443 		format_draw_none(octx, available, ocx, ocy, left, centre,
    444 		    right, frs);
    445 		return;
    446 	}
    447 
    448 	/* Write left at 0. */
    449 	format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
    450 
    451 	/* Write after at available - width_after. */
    452 	format_draw_put(octx, ocx, ocy, after, frs,
    453 	    available - width_after,
    454 	    after->cx - width_after,
    455 	    width_after);
    456 
    457 	/*
    458 	 * Write right at
    459 	 *     available - width_right - width_list - width_after.
    460 	 */
    461 	format_draw_put(octx, ocx, ocy, right, frs,
    462 	    available - width_right - width_list - width_after,
    463 	    0,
    464 	    width_right);
    465 
    466 	/*
    467 	 * Write centre halfway between
    468 	 *     width_left
    469 	 * and
    470 	 *     available - width_right - width_list - width_after.
    471 	 */
    472 	format_draw_put(octx, ocx, ocy, centre, frs,
    473 	    width_left
    474 	    + ((available - width_right - width_list - width_after)
    475 		- width_left) / 2
    476 	    - width_centre / 2,
    477 	    centre->cx / 2 - width_centre / 2,
    478 	    width_centre);
    479 
    480 	/*
    481 	 * The list now goes from
    482 	 *     available - width_list - width_after
    483 	 * to
    484 	 *     available - width_after
    485 	 * If there is no focus given, keep the right in focus.
    486 	 */
    487 	if (focus_start == -1 || focus_end == -1)
    488 		focus_start = focus_end = 0;
    489 	format_draw_put_list(octx, ocx, ocy, available - width_list -
    490 	    width_after, width_list, list, list_left, list_right, focus_start,
    491 	    focus_end, frs);
    492 }
    493 
    494 /* Draw a format to a screen. */
    495 void
    496 format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
    497     u_int available, const char *expanded, struct style_ranges *srs)
    498 {
    499 	enum { LEFT,
    500 	       CENTRE,
    501 	       RIGHT,
    502 	       LIST,
    503 	       LIST_LEFT,
    504 	       LIST_RIGHT,
    505 	       AFTER,
    506 	       TOTAL } current = LEFT, last = LEFT;
    507 	const char	        *names[] = { "LEFT",
    508 					     "CENTRE",
    509 					     "RIGHT",
    510 					     "LIST",
    511 					     "LIST_LEFT",
    512 					     "LIST_RIGHT",
    513 					     "AFTER" };
    514 	size_t			 size = strlen(expanded);
    515 	struct screen		*os = octx->s, s[TOTAL];
    516 	struct screen_write_ctx	 ctx[TOTAL];
    517 	u_int			 ocx = os->cx, ocy = os->cy, i, width[TOTAL];
    518 	u_int			 map[] = { LEFT, LEFT, CENTRE, RIGHT };
    519 	int			 focus_start = -1, focus_end = -1;
    520 	int			 list_state = -1;
    521 	enum style_align	 list_align = STYLE_ALIGN_DEFAULT;
    522 	struct style		 sy;
    523 	struct utf8_data	*ud = &sy.gc.data;
    524 	const char		*cp, *end;
    525 	enum utf8_state		 more;
    526 	char			*tmp;
    527 	struct format_range	*fr = NULL, *fr1;
    528 	struct format_ranges	 frs;
    529 	struct style_range	*sr;
    530 
    531 	style_set(&sy, base);
    532 	TAILQ_INIT(&frs);
    533 	log_debug("%s: %s", __func__, expanded);
    534 
    535 	/*
    536 	 * We build three screens for left, right, centre alignment, one for
    537 	 * the list, one for anything after the list and two for the list left
    538 	 * and right markers.
    539 	 */
    540 	for (i = 0; i < TOTAL; i++) {
    541 		screen_init(&s[i], size, 1, 0);
    542 		screen_write_start(&ctx[i], NULL, &s[i]);
    543 		screen_write_clearendofline(&ctx[i], base->bg);
    544 		width[i] = 0;
    545 	}
    546 
    547 	/*
    548 	 * Walk the string and add to the corresponding screens,
    549 	 * parsing styles as we go.
    550 	 */
    551 	cp = expanded;
    552 	while (*cp != '\0') {
    553 		if (cp[0] != '#' || cp[1] != '[') {
    554 			/* See if this is a UTF-8 character. */
    555 			if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
    556 				while (*++cp != '\0' && more == UTF8_MORE)
    557 					more = utf8_append(ud, *cp);
    558 				if (more != UTF8_DONE)
    559 					cp -= ud->have;
    560 			}
    561 
    562 			/* Not a UTF-8 character - ASCII or not valid. */
    563 			if (more != UTF8_DONE) {
    564 				if (*cp < 0x20 || *cp > 0x7e) {
    565 					/* Ignore nonprintable characters. */
    566 					cp++;
    567 					continue;
    568 				}
    569 				utf8_set(ud, *cp);
    570 				cp++;
    571 			}
    572 
    573 			/* Draw the cell to th current screen. */
    574 			screen_write_cell(&ctx[current], &sy.gc);
    575 			width[current] += ud->width;
    576 			continue;
    577 		}
    578 
    579 		/* This is a style. Work out where the end is and parse it. */
    580 		end = format_skip(cp + 2, "]");
    581 		if (end == NULL) {
    582 			log_debug("%s: no terminating ] at '%s'", __func__,
    583 			    cp + 2);
    584 			TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1)
    585 			    format_free_range(&frs, fr);
    586 			goto out;
    587 		}
    588 		tmp = xstrndup(cp + 2, end - (cp + 2));
    589 		if (style_parse(&sy, base, tmp) != 0) {
    590 			log_debug("%s: invalid style '%s'", __func__, tmp);
    591 			free(tmp);
    592 			cp = end + 1;
    593 			continue;
    594 		}
    595 		log_debug("%s: style '%s' -> '%s'", __func__, tmp,
    596 		    style_tostring(&sy));
    597 		free(tmp);
    598 
    599 		/* Check the list state. */
    600 		switch (sy.list) {
    601 		case STYLE_LIST_ON:
    602 			/*
    603 			 * Entering the list, exiting a marker, or exiting the
    604 			 * focus.
    605 			 */
    606 			if (list_state != 0) {
    607 				if (fr != NULL) { /* abort any region */
    608 					free(fr);
    609 					fr = NULL;
    610 				}
    611 				list_state = 0;
    612 				list_align = sy.align;
    613 			}
    614 
    615 			/* End the focus if started. */
    616 			if (focus_start != -1 && focus_end == -1)
    617 				focus_end = s[LIST].cx;
    618 
    619 			current = LIST;
    620 			break;
    621 		case STYLE_LIST_FOCUS:
    622 			/* Entering the focus. */
    623 			if (list_state != 0) /* not inside the list */
    624 				break;
    625 			if (focus_start == -1) /* focus already started */
    626 				focus_start = s[LIST].cx;
    627 			break;
    628 		case STYLE_LIST_OFF:
    629 			/* Exiting or outside the list. */
    630 			if (list_state == 0) {
    631 				if (fr != NULL) { /* abort any region */
    632 					free(fr);
    633 					fr = NULL;
    634 				}
    635 				if (focus_start != -1 && focus_end == -1)
    636 					focus_end = s[LIST].cx;
    637 
    638 				map[list_align] = AFTER;
    639 				if (list_align == STYLE_ALIGN_LEFT)
    640 					map[STYLE_ALIGN_DEFAULT] = AFTER;
    641 				list_state = 1;
    642 			}
    643 			current = map[sy.align];
    644 			break;
    645 		case STYLE_LIST_LEFT_MARKER:
    646 			/* Entering left marker. */
    647 			if (list_state != 0) /* not inside the list */
    648 				break;
    649 			if (s[LIST_LEFT].cx != 0) /* already have marker */
    650 				break;
    651 			if (fr != NULL) { /* abort any region */
    652 				free(fr);
    653 				fr = NULL;
    654 			}
    655 			if (focus_start != -1 && focus_end == -1)
    656 				focus_start = focus_end = -1;
    657 			current = LIST_LEFT;
    658 			break;
    659 		case STYLE_LIST_RIGHT_MARKER:
    660 			/* Entering right marker. */
    661 			if (list_state != 0) /* not inside the list */
    662 				break;
    663 			if (s[LIST_RIGHT].cx != 0) /* already have marker */
    664 				break;
    665 			if (fr != NULL) { /* abort any region */
    666 				free(fr);
    667 				fr = NULL;
    668 			}
    669 			if (focus_start != -1 && focus_end == -1)
    670 				focus_start = focus_end = -1;
    671 			current = LIST_RIGHT;
    672 			break;
    673 		}
    674 		if (current != last) {
    675 			log_debug("%s: change %s -> %s", __func__,
    676 			    names[last], names[current]);
    677 			last = current;
    678 		}
    679 
    680 		/*
    681 		 * Check if the range style has changed and if so end the
    682 		 * current range and start a new one if needed.
    683 		 */
    684 		if (srs != NULL) {
    685 			if (fr != NULL && !format_is_type(fr, &sy)) {
    686 				if (s[current].cx != fr->start) {
    687 					fr->end = s[current].cx + 1;
    688 					TAILQ_INSERT_TAIL(&frs, fr, entry);
    689 				} else
    690 					free(fr);
    691 				fr = NULL;
    692 			}
    693 			if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) {
    694 				fr = xcalloc(1, sizeof *fr);
    695 				fr->index = current;
    696 
    697 				fr->s = &s[current];
    698 				fr->start = s[current].cx;
    699 
    700 				fr->type = sy.range_type;
    701 				fr->argument = sy.range_argument;
    702 			}
    703 		}
    704 
    705 		cp = end + 1;
    706 	}
    707 	free(fr);
    708 
    709 	for (i = 0; i < TOTAL; i++) {
    710 		screen_write_stop(&ctx[i]);
    711 		log_debug("%s: width %s is %u", __func__, names[i], width[i]);
    712 	}
    713 	if (focus_start != -1 && focus_end != -1)
    714 		log_debug("%s: focus %d-%d", __func__, focus_start, focus_end);
    715 	TAILQ_FOREACH(fr, &frs, entry) {
    716 		log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type,
    717 		    fr->argument, names[fr->index], fr->start, fr->end);
    718 	}
    719 
    720 	/*
    721 	 * Draw the screens. How they are arranged depends on where the list
    722 	 * appearsq.
    723 	 */
    724 	switch (list_align) {
    725 	case STYLE_ALIGN_DEFAULT:
    726 		/* No list. */
    727 		format_draw_none(octx, available, ocx, ocy, &s[LEFT],
    728 		    &s[CENTRE], &s[RIGHT], &frs);
    729 		break;
    730 	case STYLE_ALIGN_LEFT:
    731 		/* List is part of the left. */
    732 		format_draw_left(octx, available, ocx, ocy, &s[LEFT],
    733 		    &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
    734 		    &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
    735 		break;
    736 	case STYLE_ALIGN_CENTRE:
    737 		/* List is part of the centre. */
    738 		format_draw_centre(octx, available, ocx, ocy, &s[LEFT],
    739 		    &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
    740 		    &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
    741 		break;
    742 	case STYLE_ALIGN_RIGHT:
    743 		/* List is part of the right. */
    744 		format_draw_right(octx, available, ocx, ocy, &s[LEFT],
    745 		    &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
    746 		    &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
    747 		break;
    748 	}
    749 
    750 	/* Create ranges to return. */
    751 	TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) {
    752 		sr = xcalloc(1, sizeof *sr);
    753 		sr->type = fr->type;
    754 		sr->argument = fr->argument;
    755 		sr->start = fr->start;
    756 		sr->end = fr->end;
    757 		TAILQ_INSERT_TAIL(srs, sr, entry);
    758 
    759 		log_debug("%s: range %d|%u at %u-%u", __func__, sr->type,
    760 		    sr->argument, sr->start, sr->end);
    761 
    762 		format_free_range(&frs, fr);
    763 	}
    764 
    765 out:
    766 	/* Free the screens. */
    767 	for (i = 0; i < TOTAL; i++)
    768 		screen_free(&s[i]);
    769 
    770 	/* Restore the original cursor position. */
    771 	screen_write_cursormove(octx, ocx, ocy, 0);
    772 }
    773 
    774 /* Get width, taking #[] into account. */
    775 u_int
    776 format_width(const char *expanded)
    777 {
    778 	const char		*cp, *end;
    779 	u_int			 width = 0;
    780 	struct utf8_data	 ud;
    781 	enum utf8_state		 more;
    782 
    783 	cp = expanded;
    784 	while (*cp != '\0') {
    785 		if (cp[0] == '#' && cp[1] == '[') {
    786 			end = format_skip(cp + 2, "]");
    787 			if (end == NULL)
    788 				return 0;
    789 			cp = end + 1;
    790 		} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
    791 			while (*++cp != '\0' && more == UTF8_MORE)
    792 				more = utf8_append(&ud, *cp);
    793 			if (more == UTF8_DONE)
    794 				width += ud.width;
    795 			else
    796 				cp -= ud.have;
    797 		} else if (*cp > 0x1f && *cp < 0x7f) {
    798 			width++;
    799 			cp++;
    800 		}
    801 	}
    802 	return (width);
    803 }
    804 
    805 /* Trim on the left, taking #[] into account. */
    806 char *
    807 format_trim_left(const char *expanded, u_int limit)
    808 {
    809 	char			*copy, *out;
    810 	const char		*cp = expanded, *end;
    811 	u_int			 width = 0;
    812 	struct utf8_data	 ud;
    813 	enum utf8_state		 more;
    814 
    815 	out = copy = xmalloc(strlen(expanded) + 1);
    816 	while (*cp != '\0') {
    817 		if (cp[0] == '#' && cp[1] == '[') {
    818 			end = format_skip(cp + 2, "]");
    819 			if (end == NULL)
    820 				break;
    821 			memcpy(out, cp, end + 1 - cp);
    822 			out += (end + 1 - cp);
    823 			cp = end + 1;
    824 		} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
    825 			while (*++cp != '\0' && more == UTF8_MORE)
    826 				more = utf8_append(&ud, *cp);
    827 			if (more == UTF8_DONE) {
    828 				if (width + ud.width <= limit) {
    829 					memcpy(out, ud.data, ud.size);
    830 					out += ud.size;
    831 				}
    832 				width += ud.width;
    833 			} else
    834 				cp -= ud.have;
    835 		} else if (*cp > 0x1f && *cp < 0x7f) {
    836 			if (width + 1 <= limit)
    837 				*out++ = *cp;
    838 			width++;
    839 			cp++;
    840 		} else
    841 			cp++;
    842 	}
    843 	*out = '\0';
    844 	return (copy);
    845 }
    846 
    847 /* Trim on the right, taking #[] into account. */
    848 char *
    849 format_trim_right(const char *expanded, u_int limit)
    850 {
    851 	char			*copy, *out;
    852 	const char		*cp = expanded, *end;
    853 	u_int			 width = 0, total_width, skip;
    854 	struct utf8_data	 ud;
    855 	enum utf8_state		 more;
    856 
    857 	total_width = format_width(expanded);
    858 	if (total_width <= limit)
    859 		return (xstrdup(expanded));
    860 	skip = total_width - limit;
    861 
    862 	out = copy = xmalloc(strlen(expanded) + 1);
    863 	while (*cp != '\0') {
    864 		if (cp[0] == '#' && cp[1] == '[') {
    865 			end = format_skip(cp + 2, "]");
    866 			if (end == NULL)
    867 				break;
    868 			memcpy(out, cp, end + 1 - cp);
    869 			out += (end + 1 - cp);
    870 			cp = end + 1;
    871 		} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
    872 			while (*++cp != '\0' && more == UTF8_MORE)
    873 				more = utf8_append(&ud, *cp);
    874 			if (more == UTF8_DONE) {
    875 				if (width >= skip) {
    876 					memcpy(out, ud.data, ud.size);
    877 					out += ud.size;
    878 				}
    879 				width += ud.width;
    880 			} else
    881 				cp -= ud.have;
    882 		} else if (*cp > 0x1f && *cp < 0x7f) {
    883 			if (width >= skip)
    884 				*out++ = *cp;
    885 			width++;
    886 			cp++;
    887 		} else
    888 			cp++;
    889 	}
    890 	*out = '\0';
    891 	return (copy);
    892 }
    893