[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.21

1.21    ! nicm        1: /* $OpenBSD: format-draw.c,v 1.20 2020/05/16 16:26:34 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:                }
1.7       nicm       88:
                     89:                fr->start -= start;
                     90:                fr->end -= start;
1.1       nicm       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) {
1.15      nicm      145:                screen_write_cursormove(octx, ocx + offset + width -
                    146:                    list_right->cx, ocy, 0);
1.1       nicm      147:                screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx,
                    148:                    1);
                    149:                width -= list_right->cx;
                    150:        }
                    151:
                    152:        /* Draw the list screen itself. */
                    153:        format_draw_put(octx, ocx, ocy, list, frs, offset, start, width);
                    154: }
                    155:
                    156: /* Draw format with no list. */
                    157: static void
                    158: format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx,
                    159:     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
                    160:     struct format_ranges *frs)
                    161: {
                    162:        u_int   width_left, width_centre, width_right;
                    163:
                    164:        width_left = left->cx;
                    165:        width_centre = centre->cx;
                    166:        width_right = right->cx;
                    167:
                    168:        /*
                    169:         * Try to keep as much of the left and right as possible at the expense
                    170:         * of the centre.
                    171:         */
                    172:        while (width_left + width_centre + width_right > available) {
                    173:                if (width_centre > 0)
                    174:                        width_centre--;
                    175:                else if (width_right > 0)
                    176:                        width_right--;
                    177:                else
                    178:                        width_left--;
                    179:        }
                    180:
                    181:        /* Write left. */
                    182:        format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
                    183:
                    184:        /* Write right at available - width_right. */
                    185:        format_draw_put(octx, ocx, ocy, right, frs,
                    186:            available - width_right,
                    187:            right->cx - width_right,
                    188:            width_right);
                    189:
                    190:        /*
                    191:         * Write centre halfway between
                    192:         *     width_left
                    193:         * and
                    194:         *     available - width_right.
                    195:         */
                    196:        format_draw_put(octx, ocx, ocy, centre, frs,
                    197:            width_left
                    198:            + ((available - width_right) - width_left) / 2
                    199:            - width_centre / 2,
                    200:            centre->cx / 2 - width_centre / 2,
                    201:            width_centre);
                    202: }
                    203:
                    204: /* Draw format with list on the left. */
                    205: static void
                    206: format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
                    207:     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
                    208:     struct screen *list, struct screen *list_left, struct screen *list_right,
                    209:     struct screen *after, int focus_start, int focus_end,
                    210:     struct format_ranges *frs)
                    211: {
                    212:        u_int                   width_left, width_centre, width_right;
                    213:        u_int                   width_list, width_after;
                    214:        struct screen_write_ctx ctx;
                    215:
                    216:        width_left = left->cx;
                    217:        width_centre = centre->cx;
                    218:        width_right = right->cx;
                    219:        width_list = list->cx;
                    220:        width_after = after->cx;
                    221:
                    222:        /*
                    223:         * Trim first the centre, then the list, then the right, then after the
                    224:         * list, then the left.
                    225:         */
                    226:        while (width_left +
                    227:            width_centre +
                    228:            width_right +
                    229:            width_list +
                    230:            width_after > available) {
                    231:                if (width_centre > 0)
                    232:                        width_centre--;
                    233:                else if (width_list > 0)
                    234:                        width_list--;
                    235:                else if (width_right > 0)
                    236:                        width_right--;
                    237:                else if (width_after > 0)
                    238:                        width_after--;
                    239:                else
                    240:                        width_left--;
                    241:        }
                    242:
                    243:        /* If there is no list left, pass off to the no list function. */
                    244:        if (width_list == 0) {
1.18      nicm      245:                screen_write_start(&ctx, left);
1.1       nicm      246:                screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
                    247:                screen_write_stop(&ctx);
                    248:
                    249:                format_draw_none(octx, available, ocx, ocy, left, centre,
                    250:                    right, frs);
                    251:                return;
                    252:        }
                    253:
                    254:        /* Write left at 0. */
                    255:        format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
                    256:
                    257:        /* Write right at available - width_right. */
                    258:        format_draw_put(octx, ocx, ocy, right, frs,
                    259:            available - width_right,
                    260:            right->cx - width_right,
                    261:            width_right);
                    262:
                    263:        /* Write after at width_left + width_list. */
                    264:        format_draw_put(octx, ocx, ocy, after, frs,
                    265:            width_left + width_list,
                    266:            0,
                    267:            width_after);
                    268:
                    269:        /*
                    270:         * Write centre halfway between
                    271:         *     width_left + width_list + width_after
                    272:         * and
                    273:         *     available - width_right.
                    274:         */
                    275:        format_draw_put(octx, ocx, ocy, centre, frs,
                    276:            (width_left + width_list + width_after)
                    277:            + ((available - width_right)
                    278:                - (width_left + width_list + width_after)) / 2
                    279:            - width_centre / 2,
                    280:            centre->cx / 2 - width_centre / 2,
                    281:            width_centre);
                    282:
                    283:        /*
                    284:         * The list now goes from
                    285:         *     width_left
                    286:         * to
                    287:         *     width_left + width_list.
                    288:         * If there is no focus given, keep the left in focus.
                    289:         */
                    290:        if (focus_start == -1 || focus_end == -1)
                    291:                focus_start = focus_end = 0;
                    292:        format_draw_put_list(octx, ocx, ocy, width_left, width_list, list,
                    293:            list_left, list_right, focus_start, focus_end, frs);
                    294: }
                    295:
                    296: /* Draw format with list in the centre. */
                    297: static void
                    298: format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
                    299:     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
                    300:     struct screen *list, struct screen *list_left, struct screen *list_right,
                    301:     struct screen *after, int focus_start, int focus_end,
                    302:     struct format_ranges *frs)
                    303: {
                    304:        u_int                   width_left, width_centre, width_right;
                    305:        u_int                   width_list, width_after, middle;
                    306:        struct screen_write_ctx ctx;
                    307:
                    308:        width_left = left->cx;
                    309:        width_centre = centre->cx;
                    310:        width_right = right->cx;
                    311:        width_list = list->cx;
                    312:        width_after = after->cx;
                    313:
                    314:        /*
                    315:         * Trim first the list, then after the list, then the centre, then the
                    316:         * right, then the left.
                    317:         */
                    318:        while (width_left +
                    319:            width_centre +
                    320:            width_right +
                    321:            width_list +
                    322:            width_after > available) {
                    323:                if (width_list > 0)
                    324:                        width_list--;
                    325:                else if (width_after > 0)
                    326:                        width_after--;
                    327:                else if (width_centre > 0)
                    328:                        width_centre--;
                    329:                else if (width_right > 0)
                    330:                        width_right--;
                    331:                else
                    332:                        width_left--;
                    333:        }
                    334:
                    335:        /* If there is no list left, pass off to the no list function. */
                    336:        if (width_list == 0) {
1.18      nicm      337:                screen_write_start(&ctx, centre);
1.1       nicm      338:                screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
                    339:                screen_write_stop(&ctx);
                    340:
                    341:                format_draw_none(octx, available, ocx, ocy, left, centre,
                    342:                    right, frs);
                    343:                return;
                    344:        }
                    345:
                    346:        /* Write left at 0. */
                    347:        format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
                    348:
                    349:        /* Write right at available - width_right. */
                    350:        format_draw_put(octx, ocx, ocy, right, frs,
                    351:            available - width_right,
                    352:            right->cx - width_right,
                    353:            width_right);
                    354:
                    355:        /*
                    356:         * All three centre sections are offset from the middle of the
                    357:         * available space.
                    358:         */
                    359:        middle = (width_left + ((available - width_right) - width_left) / 2);
                    360:
                    361:        /*
                    362:         * Write centre at
                    363:         *     middle - width_list / 2 - width_centre.
                    364:         */
                    365:        format_draw_put(octx, ocx, ocy, centre, frs,
                    366:            middle - width_list / 2 - width_centre,
                    367:            0,
                    368:            width_centre);
                    369:
                    370:        /*
                    371:         * Write after at
1.9       nicm      372:         *     middle - width_list / 2 + width_list
1.1       nicm      373:         */
                    374:        format_draw_put(octx, ocx, ocy, after, frs,
1.9       nicm      375:            middle - width_list / 2 + width_list,
1.1       nicm      376:            0,
                    377:            width_after);
                    378:
                    379:        /*
                    380:         * The list now goes from
                    381:         *     middle - width_list / 2
                    382:         * to
                    383:         *     middle + width_list / 2
                    384:         * If there is no focus given, keep the centre in focus.
                    385:         */
                    386:        if (focus_start == -1 || focus_end == -1)
                    387:                focus_start = focus_end = list->cx / 2;
                    388:        format_draw_put_list(octx, ocx, ocy, middle - width_list / 2,
                    389:            width_list, list, list_left, list_right, focus_start, focus_end,
                    390:            frs);
                    391: }
                    392:
                    393: /* Draw format with list on the right. */
                    394: static void
                    395: format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
                    396:     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
                    397:     struct screen *list, struct screen *list_left, struct screen *list_right,
                    398:     struct screen *after, int focus_start, int focus_end,
                    399:     struct format_ranges *frs)
                    400: {
                    401:        u_int                   width_left, width_centre, width_right;
                    402:        u_int                   width_list, width_after;
                    403:        struct screen_write_ctx ctx;
                    404:
                    405:        width_left = left->cx;
                    406:        width_centre = centre->cx;
                    407:        width_right = right->cx;
                    408:        width_list = list->cx;
                    409:        width_after = after->cx;
                    410:
                    411:        /*
                    412:         * Trim first the centre, then the list, then the right, then
                    413:         * after the list, then the left.
                    414:         */
                    415:        while (width_left +
                    416:            width_centre +
                    417:            width_right +
                    418:            width_list +
                    419:            width_after > available) {
                    420:                if (width_centre > 0)
                    421:                        width_centre--;
                    422:                else if (width_list > 0)
                    423:                        width_list--;
                    424:                else if (width_right > 0)
                    425:                        width_right--;
                    426:                else if (width_after > 0)
                    427:                        width_after--;
                    428:                else
                    429:                        width_left--;
                    430:        }
                    431:
                    432:        /* If there is no list left, pass off to the no list function. */
                    433:        if (width_list == 0) {
1.18      nicm      434:                screen_write_start(&ctx, right);
1.1       nicm      435:                screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
                    436:                screen_write_stop(&ctx);
                    437:
                    438:                format_draw_none(octx, available, ocx, ocy, left, centre,
                    439:                    right, frs);
                    440:                return;
                    441:        }
                    442:
                    443:        /* Write left at 0. */
                    444:        format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
                    445:
                    446:        /* Write after at available - width_after. */
                    447:        format_draw_put(octx, ocx, ocy, after, frs,
                    448:            available - width_after,
                    449:            after->cx - width_after,
                    450:            width_after);
                    451:
                    452:        /*
                    453:         * Write right at
                    454:         *     available - width_right - width_list - width_after.
                    455:         */
                    456:        format_draw_put(octx, ocx, ocy, right, frs,
                    457:            available - width_right - width_list - width_after,
                    458:            0,
                    459:            width_right);
                    460:
                    461:        /*
                    462:         * Write centre halfway between
                    463:         *     width_left
                    464:         * and
                    465:         *     available - width_right - width_list - width_after.
                    466:         */
                    467:        format_draw_put(octx, ocx, ocy, centre, frs,
                    468:            width_left
                    469:            + ((available - width_right - width_list - width_after)
                    470:                - width_left) / 2
                    471:            - width_centre / 2,
                    472:            centre->cx / 2 - width_centre / 2,
                    473:            width_centre);
                    474:
                    475:        /*
                    476:         * The list now goes from
                    477:         *     available - width_list - width_after
                    478:         * to
                    479:         *     available - width_after
                    480:         * If there is no focus given, keep the right in focus.
                    481:         */
                    482:        if (focus_start == -1 || focus_end == -1)
                    483:                focus_start = focus_end = 0;
                    484:        format_draw_put_list(octx, ocx, ocy, available - width_list -
                    485:            width_after, width_list, list, list_left, list_right, focus_start,
                    486:            focus_end, frs);
                    487: }
                    488:
1.21    ! nicm      489: /* Draw multiple characters. */
        !           490: static void
        !           491: format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch,
        !           492:     u_int n)
        !           493: {
        !           494:        u_int   i;
        !           495:
        !           496:        utf8_set(&sy->gc.data, ch);
        !           497:        for (i = 0; i < n; i++)
        !           498:                screen_write_cell(ctx, &sy->gc);
        !           499: }
        !           500:
1.1       nicm      501: /* Draw a format to a screen. */
                    502: void
                    503: format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
                    504:     u_int available, const char *expanded, struct style_ranges *srs)
                    505: {
                    506:        enum { LEFT,
                    507:               CENTRE,
                    508:               RIGHT,
                    509:               LIST,
                    510:               LIST_LEFT,
                    511:               LIST_RIGHT,
                    512:               AFTER,
                    513:               TOTAL } current = LEFT, last = LEFT;
                    514:        const char              *names[] = { "LEFT",
                    515:                                             "CENTRE",
                    516:                                             "RIGHT",
                    517:                                             "LIST",
                    518:                                             "LIST_LEFT",
                    519:                                             "LIST_RIGHT",
                    520:                                             "AFTER" };
                    521:        size_t                   size = strlen(expanded);
                    522:        struct screen           *os = octx->s, s[TOTAL];
                    523:        struct screen_write_ctx  ctx[TOTAL];
1.21    ! nicm      524:        u_int                    ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
1.1       nicm      525:        u_int                    map[] = { LEFT, LEFT, CENTRE, RIGHT };
                    526:        int                      focus_start = -1, focus_end = -1;
1.21    ! nicm      527:        int                      list_state = -1, fill = -1, even;
1.1       nicm      528:        enum style_align         list_align = STYLE_ALIGN_DEFAULT;
1.13      nicm      529:        struct grid_cell         gc, current_default;
                    530:        struct style             sy, saved_sy;
1.1       nicm      531:        struct utf8_data        *ud = &sy.gc.data;
                    532:        const char              *cp, *end;
                    533:        enum utf8_state          more;
                    534:        char                    *tmp;
                    535:        struct format_range     *fr = NULL, *fr1;
                    536:        struct format_ranges     frs;
                    537:        struct style_range      *sr;
                    538:
1.13      nicm      539:        memcpy(&current_default, base, sizeof current_default);
                    540:        style_set(&sy, &current_default);
1.1       nicm      541:        TAILQ_INIT(&frs);
1.3       nicm      542:        log_debug("%s: %s", __func__, expanded);
1.1       nicm      543:
                    544:        /*
                    545:         * We build three screens for left, right, centre alignment, one for
                    546:         * the list, one for anything after the list and two for the list left
                    547:         * and right markers.
                    548:         */
                    549:        for (i = 0; i < TOTAL; i++) {
                    550:                screen_init(&s[i], size, 1, 0);
1.18      nicm      551:                screen_write_start(&ctx[i], &s[i]);
1.13      nicm      552:                screen_write_clearendofline(&ctx[i], current_default.bg);
1.1       nicm      553:                width[i] = 0;
                    554:        }
                    555:
                    556:        /*
                    557:         * Walk the string and add to the corresponding screens,
                    558:         * parsing styles as we go.
                    559:         */
                    560:        cp = expanded;
                    561:        while (*cp != '\0') {
1.21    ! nicm      562:                /* Handle sequences of #. */
        !           563:                if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') {
        !           564:                        for (n = 1; cp[n] == '#'; n++)
        !           565:                                 /* nothing */;
        !           566:                        if (cp[n] != '[') {
        !           567:                                width[current] += n;
        !           568:                                cp += n;
        !           569:                                format_draw_many(&ctx[current], &sy, '#', n);
        !           570:                                continue;
        !           571:                        }
        !           572:                        even = ((n % 2) == 0);
        !           573:                        if (even)
        !           574:                                cp += (n + 1);
        !           575:                        else
        !           576:                                cp += (n - 1);
        !           577:                        if (sy.ignore)
        !           578:                                continue;
        !           579:                        format_draw_many(&ctx[current], &sy, '#', n / 2);
        !           580:                        width[current] += (n / 2);
        !           581:                        if (even) {
        !           582:                                utf8_set(ud, '[');
        !           583:                                screen_write_cell(&ctx[current], &sy.gc);
        !           584:                                width[current]++;
        !           585:                        }
        !           586:                        continue;
        !           587:                }
        !           588:
        !           589:                /* Is this not a style? */
1.19      nicm      590:                if (cp[0] != '#' || cp[1] != '[' || sy.ignore) {
1.1       nicm      591:                        /* See if this is a UTF-8 character. */
                    592:                        if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
                    593:                                while (*++cp != '\0' && more == UTF8_MORE)
                    594:                                        more = utf8_append(ud, *cp);
                    595:                                if (more != UTF8_DONE)
                    596:                                        cp -= ud->have;
                    597:                        }
                    598:
                    599:                        /* Not a UTF-8 character - ASCII or not valid. */
                    600:                        if (more != UTF8_DONE) {
                    601:                                if (*cp < 0x20 || *cp > 0x7e) {
                    602:                                        /* Ignore nonprintable characters. */
                    603:                                        cp++;
                    604:                                        continue;
                    605:                                }
                    606:                                utf8_set(ud, *cp);
                    607:                                cp++;
                    608:                        }
                    609:
1.12      nicm      610:                        /* Draw the cell to the current screen. */
1.1       nicm      611:                        screen_write_cell(&ctx[current], &sy.gc);
                    612:                        width[current] += ud->width;
                    613:                        continue;
                    614:                }
                    615:
                    616:                /* This is a style. Work out where the end is and parse it. */
                    617:                end = format_skip(cp + 2, "]");
1.3       nicm      618:                if (end == NULL) {
1.6       nicm      619:                        log_debug("%s: no terminating ] at '%s'", __func__,
                    620:                            cp + 2);
1.5       nicm      621:                        TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1)
                    622:                            format_free_range(&frs, fr);
                    623:                        goto out;
1.3       nicm      624:                }
1.1       nicm      625:                tmp = xstrndup(cp + 2, end - (cp + 2));
1.13      nicm      626:                style_copy(&saved_sy, &sy);
                    627:                if (style_parse(&sy, &current_default, tmp) != 0) {
1.6       nicm      628:                        log_debug("%s: invalid style '%s'", __func__, tmp);
1.1       nicm      629:                        free(tmp);
1.4       nicm      630:                        cp = end + 1;
                    631:                        continue;
1.1       nicm      632:                }
1.6       nicm      633:                log_debug("%s: style '%s' -> '%s'", __func__, tmp,
                    634:                    style_tostring(&sy));
1.1       nicm      635:                free(tmp);
                    636:
1.11      nicm      637:                /* If this style has a fill colour, store it for later. */
                    638:                if (sy.fill != 8)
                    639:                        fill = sy.fill;
1.13      nicm      640:
                    641:                /* If this style pushed or popped the default, update it. */
                    642:                if (sy.default_type == STYLE_DEFAULT_PUSH) {
1.20      nicm      643:                        memcpy(&current_default, &saved_sy.gc,
                    644:                            sizeof current_default);
1.13      nicm      645:                        sy.default_type = STYLE_DEFAULT_BASE;
                    646:                } else if (sy.default_type == STYLE_DEFAULT_POP) {
                    647:                        memcpy(&current_default, base, sizeof current_default);
                    648:                        sy.default_type = STYLE_DEFAULT_BASE;
                    649:                }
1.11      nicm      650:
1.1       nicm      651:                /* Check the list state. */
                    652:                switch (sy.list) {
                    653:                case STYLE_LIST_ON:
                    654:                        /*
                    655:                         * Entering the list, exiting a marker, or exiting the
                    656:                         * focus.
                    657:                         */
                    658:                        if (list_state != 0) {
                    659:                                if (fr != NULL) { /* abort any region */
                    660:                                        free(fr);
                    661:                                        fr = NULL;
                    662:                                }
                    663:                                list_state = 0;
                    664:                                list_align = sy.align;
                    665:                        }
                    666:
                    667:                        /* End the focus if started. */
                    668:                        if (focus_start != -1 && focus_end == -1)
                    669:                                focus_end = s[LIST].cx;
                    670:
                    671:                        current = LIST;
                    672:                        break;
                    673:                case STYLE_LIST_FOCUS:
                    674:                        /* Entering the focus. */
                    675:                        if (list_state != 0) /* not inside the list */
                    676:                                break;
                    677:                        if (focus_start == -1) /* focus already started */
                    678:                                focus_start = s[LIST].cx;
                    679:                        break;
                    680:                case STYLE_LIST_OFF:
                    681:                        /* Exiting or outside the list. */
                    682:                        if (list_state == 0) {
                    683:                                if (fr != NULL) { /* abort any region */
                    684:                                        free(fr);
                    685:                                        fr = NULL;
                    686:                                }
                    687:                                if (focus_start != -1 && focus_end == -1)
                    688:                                        focus_end = s[LIST].cx;
                    689:
                    690:                                map[list_align] = AFTER;
                    691:                                if (list_align == STYLE_ALIGN_LEFT)
                    692:                                        map[STYLE_ALIGN_DEFAULT] = AFTER;
                    693:                                list_state = 1;
                    694:                        }
                    695:                        current = map[sy.align];
                    696:                        break;
                    697:                case STYLE_LIST_LEFT_MARKER:
                    698:                        /* Entering left marker. */
                    699:                        if (list_state != 0) /* not inside the list */
                    700:                                break;
                    701:                        if (s[LIST_LEFT].cx != 0) /* already have marker */
                    702:                                break;
                    703:                        if (fr != NULL) { /* abort any region */
                    704:                                free(fr);
                    705:                                fr = NULL;
                    706:                        }
                    707:                        if (focus_start != -1 && focus_end == -1)
                    708:                                focus_start = focus_end = -1;
                    709:                        current = LIST_LEFT;
                    710:                        break;
                    711:                case STYLE_LIST_RIGHT_MARKER:
                    712:                        /* Entering right marker. */
                    713:                        if (list_state != 0) /* not inside the list */
                    714:                                break;
                    715:                        if (s[LIST_RIGHT].cx != 0) /* already have marker */
                    716:                                break;
                    717:                        if (fr != NULL) { /* abort any region */
                    718:                                free(fr);
                    719:                                fr = NULL;
                    720:                        }
                    721:                        if (focus_start != -1 && focus_end == -1)
                    722:                                focus_start = focus_end = -1;
                    723:                        current = LIST_RIGHT;
                    724:                        break;
                    725:                }
                    726:                if (current != last) {
                    727:                        log_debug("%s: change %s -> %s", __func__,
                    728:                            names[last], names[current]);
                    729:                        last = current;
                    730:                }
                    731:
                    732:                /*
                    733:                 * Check if the range style has changed and if so end the
                    734:                 * current range and start a new one if needed.
                    735:                 */
                    736:                if (srs != NULL) {
                    737:                        if (fr != NULL && !format_is_type(fr, &sy)) {
                    738:                                if (s[current].cx != fr->start) {
                    739:                                        fr->end = s[current].cx + 1;
                    740:                                        TAILQ_INSERT_TAIL(&frs, fr, entry);
                    741:                                } else
                    742:                                        free(fr);
                    743:                                fr = NULL;
                    744:                        }
                    745:                        if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) {
                    746:                                fr = xcalloc(1, sizeof *fr);
                    747:                                fr->index = current;
                    748:
                    749:                                fr->s = &s[current];
                    750:                                fr->start = s[current].cx;
                    751:
                    752:                                fr->type = sy.range_type;
                    753:                                fr->argument = sy.range_argument;
                    754:                        }
                    755:                }
                    756:
                    757:                cp = end + 1;
                    758:        }
                    759:        free(fr);
                    760:
1.2       nicm      761:        for (i = 0; i < TOTAL; i++) {
                    762:                screen_write_stop(&ctx[i]);
1.1       nicm      763:                log_debug("%s: width %s is %u", __func__, names[i], width[i]);
1.2       nicm      764:        }
1.1       nicm      765:        if (focus_start != -1 && focus_end != -1)
1.6       nicm      766:                log_debug("%s: focus %d-%d", __func__, focus_start, focus_end);
1.1       nicm      767:        TAILQ_FOREACH(fr, &frs, entry) {
                    768:                log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type,
                    769:                    fr->argument, names[fr->index], fr->start, fr->end);
1.11      nicm      770:        }
                    771:
                    772:        /* Clear the available area. */
                    773:        if (fill != -1) {
                    774:                memcpy(&gc, &grid_default_cell, sizeof gc);
                    775:                gc.bg = fill;
                    776:                for (i = 0; i < available; i++)
                    777:                        screen_write_putc(octx, &gc, ' ');
1.1       nicm      778:        }
                    779:
                    780:        /*
                    781:         * Draw the screens. How they are arranged depends on where the list
1.17      nicm      782:         * appears.
1.1       nicm      783:         */
                    784:        switch (list_align) {
                    785:        case STYLE_ALIGN_DEFAULT:
                    786:                /* No list. */
                    787:                format_draw_none(octx, available, ocx, ocy, &s[LEFT],
                    788:                    &s[CENTRE], &s[RIGHT], &frs);
                    789:                break;
                    790:        case STYLE_ALIGN_LEFT:
                    791:                /* List is part of the left. */
                    792:                format_draw_left(octx, available, ocx, ocy, &s[LEFT],
                    793:                    &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
                    794:                    &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
                    795:                break;
                    796:        case STYLE_ALIGN_CENTRE:
                    797:                /* List is part of the centre. */
                    798:                format_draw_centre(octx, available, ocx, ocy, &s[LEFT],
                    799:                    &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
                    800:                    &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
                    801:                break;
                    802:        case STYLE_ALIGN_RIGHT:
                    803:                /* List is part of the right. */
                    804:                format_draw_right(octx, available, ocx, ocy, &s[LEFT],
                    805:                    &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
                    806:                    &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
                    807:                break;
                    808:        }
                    809:
                    810:        /* Create ranges to return. */
                    811:        TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) {
                    812:                sr = xcalloc(1, sizeof *sr);
                    813:                sr->type = fr->type;
                    814:                sr->argument = fr->argument;
                    815:                sr->start = fr->start;
                    816:                sr->end = fr->end;
                    817:                TAILQ_INSERT_TAIL(srs, sr, entry);
                    818:
                    819:                log_debug("%s: range %d|%u at %u-%u", __func__, sr->type,
                    820:                    sr->argument, sr->start, sr->end);
                    821:
                    822:                format_free_range(&frs, fr);
                    823:        }
1.2       nicm      824:
1.5       nicm      825: out:
1.2       nicm      826:        /* Free the screens. */
                    827:        for (i = 0; i < TOTAL; i++)
                    828:                screen_free(&s[i]);
1.1       nicm      829:
                    830:        /* Restore the original cursor position. */
                    831:        screen_write_cursormove(octx, ocx, ocy, 0);
                    832: }
                    833:
                    834: /* Get width, taking #[] into account. */
                    835: u_int
                    836: format_width(const char *expanded)
                    837: {
                    838:        const char              *cp, *end;
1.21    ! nicm      839:        u_int                    n, width = 0;
1.1       nicm      840:        struct utf8_data         ud;
                    841:        enum utf8_state          more;
                    842:
                    843:        cp = expanded;
                    844:        while (*cp != '\0') {
1.21    ! nicm      845:                if (*cp == '#') {
        !           846:                        for (n = 1; cp[n] == '#'; n++)
        !           847:                                /* nothing */;
        !           848:                        if (cp[n] != '[') {
        !           849:                                width += n;
        !           850:                                cp += n;
        !           851:                                continue;
        !           852:                        }
        !           853:                        width += (n / 2); /* one for each ## */
        !           854:
        !           855:                        if ((n % 2) == 0) {
        !           856:                                /*
        !           857:                                 * An even number of #s means that all #s are
        !           858:                                 * escaped, so not a style.
        !           859:                                 */
        !           860:                                width++; /* one for the [ */
        !           861:                                cp += (n + 1);
        !           862:                                continue;
        !           863:                        }
        !           864:                        cp += (n - 1); /* point to the [ */
        !           865:
1.1       nicm      866:                        end = format_skip(cp + 2, "]");
                    867:                        if (end == NULL)
1.16      nicm      868:                                return (0);
1.1       nicm      869:                        cp = end + 1;
                    870:                } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
                    871:                        while (*++cp != '\0' && more == UTF8_MORE)
                    872:                                more = utf8_append(&ud, *cp);
                    873:                        if (more == UTF8_DONE)
                    874:                                width += ud.width;
                    875:                        else
                    876:                                cp -= ud.have;
                    877:                } else if (*cp > 0x1f && *cp < 0x7f) {
                    878:                        width++;
                    879:                        cp++;
1.10      nicm      880:                } else
                    881:                        cp++;
1.1       nicm      882:        }
                    883:        return (width);
                    884: }
                    885:
1.21    ! nicm      886: /*
        !           887:  * Trim on the left, taking #[] into account.  Note, we copy the whole set of
        !           888:  * unescaped #s, but only add their escaped size to width. This is because the
        !           889:  * format_draw function will actually do the escaping when it runs
        !           890:  */
1.1       nicm      891: char *
                    892: format_trim_left(const char *expanded, u_int limit)
                    893: {
                    894:        char                    *copy, *out;
                    895:        const char              *cp = expanded, *end;
1.21    ! nicm      896:        u_int                    even, n, width = 0;
1.1       nicm      897:        struct utf8_data         ud;
                    898:        enum utf8_state          more;
                    899:
1.21    ! nicm      900:        out = copy = xcalloc(1, strlen(expanded) + 1);
1.1       nicm      901:        while (*cp != '\0') {
1.21    ! nicm      902:                if (width >= limit)
        !           903:                        break;
        !           904:                if (*cp == '#') {
        !           905:                        for (end = cp + 1; *end == '#'; end++)
        !           906:                                /* nothing */;
        !           907:                        n = end - cp;
        !           908:                        if (*end != '[') {
        !           909:                                if (n > limit - width)
        !           910:                                        n = limit - width;
        !           911:                                memcpy(out, cp, n);
        !           912:                                out += n;
        !           913:                                width += n;
        !           914:                                cp = end;
        !           915:                                continue;
        !           916:                        }
        !           917:                        even = ((n % 2) == 0);
        !           918:
        !           919:                        n /= 2;
        !           920:                        if (n > limit - width)
        !           921:                                n = limit - width;
        !           922:                        width += n;
        !           923:                        n *= 2;
        !           924:                        memcpy(out, cp, n);
        !           925:                        out += n;
        !           926:
        !           927:                        if (even) {
        !           928:                                if (width + 1 <= limit) {
        !           929:                                        *out++ = '[';
        !           930:                                        width++;
        !           931:                                }
        !           932:                                cp = end + 1;
        !           933:                                continue;
        !           934:                        }
        !           935:                        cp = end - 1;
        !           936:
1.1       nicm      937:                        end = format_skip(cp + 2, "]");
                    938:                        if (end == NULL)
                    939:                                break;
                    940:                        memcpy(out, cp, end + 1 - cp);
                    941:                        out += (end + 1 - cp);
                    942:                        cp = end + 1;
                    943:                } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
                    944:                        while (*++cp != '\0' && more == UTF8_MORE)
                    945:                                more = utf8_append(&ud, *cp);
                    946:                        if (more == UTF8_DONE) {
                    947:                                if (width + ud.width <= limit) {
                    948:                                        memcpy(out, ud.data, ud.size);
                    949:                                        out += ud.size;
                    950:                                }
                    951:                                width += ud.width;
1.14      nicm      952:                        } else {
1.1       nicm      953:                                cp -= ud.have;
1.14      nicm      954:                                cp++;
                    955:                        }
1.1       nicm      956:                } else if (*cp > 0x1f && *cp < 0x7f) {
                    957:                        if (width + 1 <= limit)
                    958:                                *out++ = *cp;
                    959:                        width++;
                    960:                        cp++;
1.8       nicm      961:                } else
                    962:                        cp++;
1.1       nicm      963:        }
                    964:        *out = '\0';
                    965:        return (copy);
                    966: }
                    967:
                    968: /* Trim on the right, taking #[] into account. */
                    969: char *
                    970: format_trim_right(const char *expanded, u_int limit)
                    971: {
                    972:        char                    *copy, *out;
                    973:        const char              *cp = expanded, *end;
1.21    ! nicm      974:        u_int                    width = 0, total_width, skip, old_n, even, n;
1.1       nicm      975:        struct utf8_data         ud;
                    976:        enum utf8_state          more;
                    977:
                    978:        total_width = format_width(expanded);
                    979:        if (total_width <= limit)
                    980:                return (xstrdup(expanded));
                    981:        skip = total_width - limit;
                    982:
1.21    ! nicm      983:        out = copy = xcalloc(1, strlen(expanded) + 1);
1.1       nicm      984:        while (*cp != '\0') {
1.21    ! nicm      985:                if (*cp == '#') {
        !           986:                        for (end = cp + 1; *end == '#'; end++)
        !           987:                                /* nothing */;
        !           988:                        old_n = n = end - cp;
        !           989:                        if (*end != '[') {
        !           990:                                if (width <= skip) {
        !           991:                                        if (skip - width >= n)
        !           992:                                                n = 0;
        !           993:                                        else
        !           994:                                                n -= (skip - width);
        !           995:                                }
        !           996:                                if (n != 0) {
        !           997:                                        memcpy(out, cp, n);
        !           998:                                        out += n;
        !           999:                                }
        !          1000:
        !          1001:                                /*
        !          1002:                                 * The width always increases by the full
        !          1003:                                 * amount even if we can't copy anything yet.
        !          1004:                                 */
        !          1005:                                width += old_n;
        !          1006:                                cp = end;
        !          1007:                                continue;
        !          1008:                        }
        !          1009:                        even = ((n % 2) == 0);
        !          1010:
        !          1011:                        n /= 2;
        !          1012:                        if (width <= skip) {
        !          1013:                                if (skip - width >= n)
        !          1014:                                        n = 0;
        !          1015:                                else
        !          1016:                                        n -= (skip - width);
        !          1017:                        }
        !          1018:                        if (n != 0) {
        !          1019:                                /*
        !          1020:                                 * Copy the full amount because it hasn't been
        !          1021:                                 * escaped yet.
        !          1022:                                 */
        !          1023:                                memcpy(out, cp, old_n);
        !          1024:                                out += old_n;
        !          1025:                        }
        !          1026:                        cp += old_n;
        !          1027:                        width += (old_n / 2) - even;
        !          1028:
        !          1029:                        if (even) {
        !          1030:                                if (width > skip)
        !          1031:                                        *out++ = '[';
        !          1032:                                width++;
        !          1033:                                continue;
        !          1034:                        }
        !          1035:                        cp = end - 1;
        !          1036:
1.1       nicm     1037:                        end = format_skip(cp + 2, "]");
1.21    ! nicm     1038:                        if (end == NULL) {
1.1       nicm     1039:                                break;
1.21    ! nicm     1040:                        }
1.1       nicm     1041:                        memcpy(out, cp, end + 1 - cp);
                   1042:                        out += (end + 1 - cp);
                   1043:                        cp = end + 1;
                   1044:                } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
                   1045:                        while (*++cp != '\0' && more == UTF8_MORE)
                   1046:                                more = utf8_append(&ud, *cp);
                   1047:                        if (more == UTF8_DONE) {
                   1048:                                if (width >= skip) {
                   1049:                                        memcpy(out, ud.data, ud.size);
                   1050:                                        out += ud.size;
                   1051:                                }
                   1052:                                width += ud.width;
1.14      nicm     1053:                        } else {
1.1       nicm     1054:                                cp -= ud.have;
1.14      nicm     1055:                                cp++;
                   1056:                        }
1.1       nicm     1057:                } else if (*cp > 0x1f && *cp < 0x7f) {
                   1058:                        if (width >= skip)
                   1059:                                *out++ = *cp;
                   1060:                        width++;
                   1061:                        cp++;
1.8       nicm     1062:                } else
                   1063:                        cp++;
1.1       nicm     1064:        }
                   1065:        *out = '\0';
                   1066:        return (copy);
                   1067: }