[BACK]Return to format-draw.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/format-draw.c, Revision 1.2

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