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

1.28    ! nicm        1: /* $OpenBSD: format-draw.c,v 1.27 2023/08/07 10:04:29 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;
1.28    ! nicm       36:        char                             string[16];
1.1       nicm       37:
                     38:        TAILQ_ENTRY(format_range)        entry;
                     39: };
                     40: TAILQ_HEAD(format_ranges, format_range);
                     41:
                     42: /* Does this range match this style? */
                     43: static int
                     44: format_is_type(struct format_range *fr, struct style *sy)
                     45: {
                     46:        if (fr->type != sy->range_type)
                     47:                return (0);
1.28    ! nicm       48:        switch (fr->type) {
        !            49:        case STYLE_RANGE_NONE:
        !            50:        case STYLE_RANGE_LEFT:
        !            51:        case STYLE_RANGE_RIGHT:
        !            52:                return (1);
        !            53:        case STYLE_RANGE_PANE:
        !            54:        case STYLE_RANGE_WINDOW:
        !            55:        case STYLE_RANGE_SESSION:
        !            56:                return (fr->argument == sy->range_argument);
        !            57:        case STYLE_RANGE_USER:
        !            58:                return (strcmp(fr->string, sy->range_string) == 0);
        !            59:        }
1.1       nicm       60:        return (1);
                     61: }
                     62:
                     63: /* Free a range. */
                     64: static void
                     65: format_free_range(struct format_ranges *frs, struct format_range *fr)
                     66: {
                     67:        TAILQ_REMOVE(frs, fr, entry);
                     68:        free(fr);
                     69: }
                     70:
                     71: /* Fix range positions. */
                     72: static void
                     73: format_update_ranges(struct format_ranges *frs, struct screen *s, u_int offset,
                     74:     u_int start, u_int width)
                     75: {
                     76:        struct format_range     *fr, *fr1;
                     77:
                     78:        if (frs == NULL)
                     79:                return;
                     80:
                     81:        TAILQ_FOREACH_SAFE(fr, frs, entry, fr1) {
                     82:                if (fr->s != s)
                     83:                        continue;
                     84:
                     85:                if (fr->end <= start || fr->start >= start + width) {
                     86:                        format_free_range(frs, fr);
                     87:                        continue;
                     88:                }
                     89:
                     90:                if (fr->start < start)
                     91:                        fr->start = start;
                     92:                if (fr->end > start + width)
                     93:                        fr->end = start + width;
                     94:                if (fr->start == fr->end) {
                     95:                        format_free_range(frs, fr);
                     96:                        continue;
                     97:                }
1.7       nicm       98:
                     99:                fr->start -= start;
                    100:                fr->end -= start;
1.1       nicm      101:
                    102:                fr->start += offset;
                    103:                fr->end += offset;
                    104:        }
                    105: }
                    106:
                    107: /* Draw a part of the format. */
                    108: static void
                    109: format_draw_put(struct screen_write_ctx *octx, u_int ocx, u_int ocy,
                    110:     struct screen *s, struct format_ranges *frs, u_int offset, u_int start,
                    111:     u_int width)
                    112: {
                    113:        /*
                    114:         * The offset is how far from the cursor on the target screen; start
                    115:         * and width how much to copy from the source screen.
                    116:         */
                    117:        screen_write_cursormove(octx, ocx + offset, ocy, 0);
                    118:        screen_write_fast_copy(octx, s, start, 0, width, 1);
                    119:        format_update_ranges(frs, s, offset, start, width);
                    120: }
                    121:
                    122: /* Draw list part of format. */
                    123: static void
                    124: format_draw_put_list(struct screen_write_ctx *octx,
                    125:     u_int ocx, u_int ocy, u_int offset, u_int width, struct screen *list,
                    126:     struct screen *list_left, struct screen *list_right, int focus_start,
                    127:     int focus_end, struct format_ranges *frs)
                    128: {
                    129:        u_int   start, focus_centre;
                    130:
                    131:        /* If there is enough space for the list, draw it entirely. */
                    132:        if (width >= list->cx) {
                    133:                format_draw_put(octx, ocx, ocy, list, frs, offset, 0, width);
                    134:                return;
                    135:        }
                    136:
                    137:        /* The list needs to be trimmed. Try to keep the focus visible. */
                    138:        focus_centre = focus_start + (focus_end - focus_start) / 2;
                    139:        if (focus_centre < width / 2)
                    140:                start = 0;
                    141:        else
                    142:                start = focus_centre - width / 2;
                    143:        if (start + width > list->cx)
                    144:                start = list->cx - width;
                    145:
                    146:        /* Draw <> markers at either side if needed. */
                    147:        if (start != 0 && width > list_left->cx) {
                    148:                screen_write_cursormove(octx, ocx + offset, ocy, 0);
                    149:                screen_write_fast_copy(octx, list_left, 0, 0, list_left->cx, 1);
                    150:                offset += list_left->cx;
                    151:                start += list_left->cx;
                    152:                width -= list_left->cx;
                    153:        }
                    154:        if (start + width < list->cx && width > list_right->cx) {
1.15      nicm      155:                screen_write_cursormove(octx, ocx + offset + width -
                    156:                    list_right->cx, ocy, 0);
1.1       nicm      157:                screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx,
                    158:                    1);
                    159:                width -= list_right->cx;
                    160:        }
                    161:
                    162:        /* Draw the list screen itself. */
                    163:        format_draw_put(octx, ocx, ocy, list, frs, offset, start, width);
                    164: }
                    165:
                    166: /* Draw format with no list. */
                    167: static void
                    168: format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx,
                    169:     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
1.23      nicm      170:     struct screen *abs_centre, struct format_ranges *frs)
1.1       nicm      171: {
1.23      nicm      172:        u_int   width_left, width_centre, width_right, width_abs_centre;
1.1       nicm      173:
                    174:        width_left = left->cx;
                    175:        width_centre = centre->cx;
                    176:        width_right = right->cx;
1.23      nicm      177:        width_abs_centre = abs_centre->cx;
1.1       nicm      178:
                    179:        /*
                    180:         * Try to keep as much of the left and right as possible at the expense
                    181:         * of the centre.
                    182:         */
                    183:        while (width_left + width_centre + width_right > available) {
                    184:                if (width_centre > 0)
                    185:                        width_centre--;
                    186:                else if (width_right > 0)
                    187:                        width_right--;
                    188:                else
                    189:                        width_left--;
                    190:        }
                    191:
                    192:        /* Write left. */
                    193:        format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
                    194:
                    195:        /* Write right at available - width_right. */
                    196:        format_draw_put(octx, ocx, ocy, right, frs,
                    197:            available - width_right,
                    198:            right->cx - width_right,
                    199:            width_right);
                    200:
                    201:        /*
                    202:         * Write centre halfway between
                    203:         *     width_left
                    204:         * and
                    205:         *     available - width_right.
                    206:         */
                    207:        format_draw_put(octx, ocx, ocy, centre, frs,
                    208:            width_left
                    209:            + ((available - width_right) - width_left) / 2
                    210:            - width_centre / 2,
                    211:            centre->cx / 2 - width_centre / 2,
                    212:            width_centre);
1.23      nicm      213:
                    214:        /*
                    215:         * Write abs_centre in the perfect centre of all horizontal space.
                    216:         */
                    217:        if (width_abs_centre > available)
                    218:                width_abs_centre = available;
                    219:        format_draw_put(octx, ocx, ocy, abs_centre, frs,
                    220:            (available - width_abs_centre) / 2,
                    221:            0,
                    222:            width_abs_centre);
1.1       nicm      223: }
                    224:
                    225: /* Draw format with list on the left. */
                    226: static void
                    227: format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
                    228:     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
1.23      nicm      229:     struct screen *abs_centre, struct screen *list, struct screen *list_left,
                    230:     struct screen *list_right, struct screen *after, int focus_start,
                    231:     int focus_end, struct format_ranges *frs)
1.1       nicm      232: {
                    233:        u_int                   width_left, width_centre, width_right;
1.23      nicm      234:        u_int                   width_list, width_after, width_abs_centre;
1.1       nicm      235:        struct screen_write_ctx ctx;
                    236:
                    237:        width_left = left->cx;
                    238:        width_centre = centre->cx;
                    239:        width_right = right->cx;
1.23      nicm      240:        width_abs_centre = abs_centre->cx;
1.1       nicm      241:        width_list = list->cx;
                    242:        width_after = after->cx;
                    243:
                    244:        /*
                    245:         * Trim first the centre, then the list, then the right, then after the
                    246:         * list, then the left.
                    247:         */
                    248:        while (width_left +
                    249:            width_centre +
                    250:            width_right +
                    251:            width_list +
                    252:            width_after > available) {
                    253:                if (width_centre > 0)
                    254:                        width_centre--;
                    255:                else if (width_list > 0)
                    256:                        width_list--;
                    257:                else if (width_right > 0)
                    258:                        width_right--;
                    259:                else if (width_after > 0)
                    260:                        width_after--;
                    261:                else
                    262:                        width_left--;
                    263:        }
                    264:
                    265:        /* If there is no list left, pass off to the no list function. */
                    266:        if (width_list == 0) {
1.18      nicm      267:                screen_write_start(&ctx, left);
1.1       nicm      268:                screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
                    269:                screen_write_stop(&ctx);
                    270:
                    271:                format_draw_none(octx, available, ocx, ocy, left, centre,
1.23      nicm      272:                    right, abs_centre, frs);
1.1       nicm      273:                return;
                    274:        }
                    275:
                    276:        /* Write left at 0. */
                    277:        format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
                    278:
                    279:        /* Write right at available - width_right. */
                    280:        format_draw_put(octx, ocx, ocy, right, frs,
                    281:            available - width_right,
                    282:            right->cx - width_right,
                    283:            width_right);
                    284:
                    285:        /* Write after at width_left + width_list. */
                    286:        format_draw_put(octx, ocx, ocy, after, frs,
                    287:            width_left + width_list,
                    288:            0,
                    289:            width_after);
                    290:
                    291:        /*
                    292:         * Write centre halfway between
                    293:         *     width_left + width_list + width_after
                    294:         * and
                    295:         *     available - width_right.
                    296:         */
                    297:        format_draw_put(octx, ocx, ocy, centre, frs,
                    298:            (width_left + width_list + width_after)
                    299:            + ((available - width_right)
                    300:                - (width_left + width_list + width_after)) / 2
                    301:            - width_centre / 2,
                    302:            centre->cx / 2 - width_centre / 2,
                    303:            width_centre);
                    304:
                    305:        /*
                    306:         * The list now goes from
                    307:         *     width_left
                    308:         * to
                    309:         *     width_left + width_list.
                    310:         * If there is no focus given, keep the left in focus.
                    311:         */
                    312:        if (focus_start == -1 || focus_end == -1)
                    313:                focus_start = focus_end = 0;
                    314:        format_draw_put_list(octx, ocx, ocy, width_left, width_list, list,
                    315:            list_left, list_right, focus_start, focus_end, frs);
1.23      nicm      316:
                    317:        /*
                    318:         * Write abs_centre in the perfect centre of all horizontal space.
                    319:         */
                    320:        if (width_abs_centre > available)
                    321:                width_abs_centre = available;
                    322:        format_draw_put(octx, ocx, ocy, abs_centre, frs,
                    323:            (available - width_abs_centre) / 2,
                    324:            0,
                    325:            width_abs_centre);
1.1       nicm      326: }
                    327:
                    328: /* Draw format with list in the centre. */
                    329: static void
                    330: format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
                    331:     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
1.23      nicm      332:     struct screen *abs_centre, struct screen *list, struct screen *list_left,
                    333:     struct screen *list_right, struct screen *after, int focus_start,
                    334:     int focus_end, struct format_ranges *frs)
1.1       nicm      335: {
1.23      nicm      336:        u_int                   width_left, width_centre, width_right, middle;
                    337:        u_int                   width_list, width_after, width_abs_centre;
1.1       nicm      338:        struct screen_write_ctx ctx;
                    339:
                    340:        width_left = left->cx;
                    341:        width_centre = centre->cx;
                    342:        width_right = right->cx;
1.23      nicm      343:        width_abs_centre = abs_centre->cx;
1.1       nicm      344:        width_list = list->cx;
                    345:        width_after = after->cx;
                    346:
                    347:        /*
                    348:         * Trim first the list, then after the list, then the centre, then the
                    349:         * right, then the left.
                    350:         */
                    351:        while (width_left +
                    352:            width_centre +
                    353:            width_right +
                    354:            width_list +
                    355:            width_after > available) {
                    356:                if (width_list > 0)
                    357:                        width_list--;
                    358:                else if (width_after > 0)
                    359:                        width_after--;
                    360:                else if (width_centre > 0)
                    361:                        width_centre--;
                    362:                else if (width_right > 0)
                    363:                        width_right--;
                    364:                else
                    365:                        width_left--;
                    366:        }
                    367:
                    368:        /* If there is no list left, pass off to the no list function. */
                    369:        if (width_list == 0) {
1.18      nicm      370:                screen_write_start(&ctx, centre);
1.1       nicm      371:                screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
                    372:                screen_write_stop(&ctx);
                    373:
                    374:                format_draw_none(octx, available, ocx, ocy, left, centre,
1.23      nicm      375:                    right, abs_centre, frs);
1.1       nicm      376:                return;
                    377:        }
                    378:
                    379:        /* Write left at 0. */
                    380:        format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
                    381:
                    382:        /* Write right at available - width_right. */
                    383:        format_draw_put(octx, ocx, ocy, right, frs,
                    384:            available - width_right,
                    385:            right->cx - width_right,
                    386:            width_right);
                    387:
                    388:        /*
                    389:         * All three centre sections are offset from the middle of the
                    390:         * available space.
                    391:         */
                    392:        middle = (width_left + ((available - width_right) - width_left) / 2);
                    393:
                    394:        /*
                    395:         * Write centre at
                    396:         *     middle - width_list / 2 - width_centre.
                    397:         */
                    398:        format_draw_put(octx, ocx, ocy, centre, frs,
                    399:            middle - width_list / 2 - width_centre,
                    400:            0,
                    401:            width_centre);
                    402:
                    403:        /*
                    404:         * Write after at
1.9       nicm      405:         *     middle - width_list / 2 + width_list
1.1       nicm      406:         */
                    407:        format_draw_put(octx, ocx, ocy, after, frs,
1.9       nicm      408:            middle - width_list / 2 + width_list,
1.1       nicm      409:            0,
                    410:            width_after);
                    411:
                    412:        /*
                    413:         * The list now goes from
                    414:         *     middle - width_list / 2
                    415:         * to
                    416:         *     middle + width_list / 2
                    417:         * If there is no focus given, keep the centre in focus.
                    418:         */
                    419:        if (focus_start == -1 || focus_end == -1)
                    420:                focus_start = focus_end = list->cx / 2;
                    421:        format_draw_put_list(octx, ocx, ocy, middle - width_list / 2,
                    422:            width_list, list, list_left, list_right, focus_start, focus_end,
                    423:            frs);
1.23      nicm      424:
                    425:        /*
                    426:         * Write abs_centre in the perfect centre of all horizontal space.
                    427:         */
                    428:        if (width_abs_centre > available)
                    429:                width_abs_centre = available;
                    430:        format_draw_put(octx, ocx, ocy, abs_centre, frs,
                    431:            (available - width_abs_centre) / 2,
                    432:            0,
                    433:            width_abs_centre);
1.1       nicm      434: }
                    435:
                    436: /* Draw format with list on the right. */
                    437: static void
                    438: format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
                    439:     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
1.23      nicm      440:     struct screen *abs_centre,     struct screen *list,
                    441:     struct screen *list_left, struct screen *list_right, struct screen *after,
                    442:     int focus_start, int focus_end, struct format_ranges *frs)
1.1       nicm      443: {
                    444:        u_int                   width_left, width_centre, width_right;
1.23      nicm      445:        u_int                   width_list, width_after, width_abs_centre;
1.1       nicm      446:        struct screen_write_ctx ctx;
                    447:
                    448:        width_left = left->cx;
                    449:        width_centre = centre->cx;
                    450:        width_right = right->cx;
1.23      nicm      451:        width_abs_centre = abs_centre->cx;
1.1       nicm      452:        width_list = list->cx;
                    453:        width_after = after->cx;
                    454:
                    455:        /*
                    456:         * Trim first the centre, then the list, then the right, then
                    457:         * after the list, then the left.
                    458:         */
                    459:        while (width_left +
                    460:            width_centre +
                    461:            width_right +
                    462:            width_list +
                    463:            width_after > available) {
                    464:                if (width_centre > 0)
                    465:                        width_centre--;
                    466:                else if (width_list > 0)
                    467:                        width_list--;
                    468:                else if (width_right > 0)
                    469:                        width_right--;
                    470:                else if (width_after > 0)
                    471:                        width_after--;
                    472:                else
                    473:                        width_left--;
                    474:        }
                    475:
                    476:        /* If there is no list left, pass off to the no list function. */
                    477:        if (width_list == 0) {
1.18      nicm      478:                screen_write_start(&ctx, right);
1.1       nicm      479:                screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
                    480:                screen_write_stop(&ctx);
                    481:
                    482:                format_draw_none(octx, available, ocx, ocy, left, centre,
1.23      nicm      483:                    right, abs_centre, frs);
1.1       nicm      484:                return;
                    485:        }
                    486:
                    487:        /* Write left at 0. */
                    488:        format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
                    489:
                    490:        /* Write after at available - width_after. */
                    491:        format_draw_put(octx, ocx, ocy, after, frs,
                    492:            available - width_after,
                    493:            after->cx - width_after,
                    494:            width_after);
                    495:
                    496:        /*
                    497:         * Write right at
                    498:         *     available - width_right - width_list - width_after.
                    499:         */
                    500:        format_draw_put(octx, ocx, ocy, right, frs,
                    501:            available - width_right - width_list - width_after,
                    502:            0,
                    503:            width_right);
                    504:
                    505:        /*
                    506:         * Write centre halfway between
                    507:         *     width_left
                    508:         * and
                    509:         *     available - width_right - width_list - width_after.
                    510:         */
                    511:        format_draw_put(octx, ocx, ocy, centre, frs,
                    512:            width_left
                    513:            + ((available - width_right - width_list - width_after)
                    514:                - width_left) / 2
                    515:            - width_centre / 2,
                    516:            centre->cx / 2 - width_centre / 2,
                    517:            width_centre);
                    518:
                    519:        /*
                    520:         * The list now goes from
                    521:         *     available - width_list - width_after
                    522:         * to
                    523:         *     available - width_after
                    524:         * If there is no focus given, keep the right in focus.
                    525:         */
                    526:        if (focus_start == -1 || focus_end == -1)
                    527:                focus_start = focus_end = 0;
                    528:        format_draw_put_list(octx, ocx, ocy, available - width_list -
                    529:            width_after, width_list, list, list_left, list_right, focus_start,
                    530:            focus_end, frs);
1.23      nicm      531:
                    532:        /*
                    533:         * Write abs_centre in the perfect centre of all horizontal space.
                    534:         */
                    535:        if (width_abs_centre > available)
                    536:                width_abs_centre = available;
                    537:        format_draw_put(octx, ocx, ocy, abs_centre, frs,
                    538:            (available - width_abs_centre) / 2,
                    539:            0,
                    540:            width_abs_centre);
                    541: }
                    542:
                    543: static void
                    544: format_draw_absolute_centre(struct screen_write_ctx *octx, u_int available,
                    545:     u_int ocx, u_int ocy, struct screen *left, struct screen *centre,
                    546:     struct screen *right, struct screen *abs_centre, struct screen *list,
                    547:     struct screen *list_left, struct screen *list_right, struct screen *after,
                    548:     int focus_start, int focus_end, struct format_ranges *frs)
                    549: {
                    550:        u_int   width_left, width_centre, width_right, width_abs_centre;
                    551:        u_int   width_list, width_after, middle, abs_centre_offset;
                    552:
                    553:        width_left = left->cx;
                    554:        width_centre = centre->cx;
                    555:        width_right = right->cx;
                    556:        width_abs_centre = abs_centre->cx;
                    557:        width_list = list->cx;
                    558:        width_after = after->cx;
                    559:
                    560:        /*
                    561:         * Trim first centre, then the right, then the left.
                    562:         */
                    563:        while (width_left +
                    564:            width_centre +
                    565:            width_right > available) {
                    566:                if (width_centre > 0)
                    567:                        width_centre--;
                    568:                else if (width_right > 0)
                    569:                        width_right--;
                    570:                else
                    571:                        width_left--;
                    572:        }
                    573:
                    574:        /*
                    575:         * We trim list after and abs_centre independently, as we are drawing
                    576:         * them over the rest. Trim first the list, then after the list, then
                    577:         * abs_centre.
                    578:         */
                    579:        while (width_list + width_after + width_abs_centre > available) {
                    580:                if (width_list > 0)
                    581:                        width_list--;
                    582:                else if (width_after > 0)
                    583:                        width_after--;
                    584:                else
                    585:                        width_abs_centre--;
                    586:        }
                    587:
                    588:        /* Write left at 0. */
                    589:        format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
                    590:
                    591:        /* Write right at available - width_right. */
                    592:        format_draw_put(octx, ocx, ocy, right, frs,
                    593:            available - width_right,
                    594:            right->cx - width_right,
                    595:            width_right);
                    596:
                    597:        /*
                    598:         * Keep writing centre at the relative centre. Only the list is written
                    599:         * in the absolute centre of the horizontal space.
                    600:         */
                    601:        middle = (width_left + ((available - width_right) - width_left) / 2);
                    602:
                    603:        /*
                    604:         * Write centre at
                    605:         *     middle - width_centre.
                    606:         */
                    607:        format_draw_put(octx, ocx, ocy, centre, frs,
                    608:                middle - width_centre,
                    609:                0,
                    610:                width_centre);
                    611:
                    612:        /*
                    613:         * If there is no focus given, keep the centre in focus.
                    614:         */
                    615:        if (focus_start == -1 || focus_end == -1)
                    616:                focus_start = focus_end = list->cx / 2;
                    617:
                    618:        /*
                    619:         * We centre abs_centre and the list together, so their shared centre is
                    620:         * in the perfect centre of horizontal space.
                    621:         */
                    622:        abs_centre_offset = (available - width_list - width_abs_centre) / 2;
                    623:
                    624:        /*
                    625:         * Write abs_centre before the list.
                    626:         */
                    627:        format_draw_put(octx, ocx, ocy, abs_centre, frs, abs_centre_offset,
                    628:            0, width_abs_centre);
                    629:        abs_centre_offset += width_abs_centre;
                    630:
                    631:        /*
                    632:         * Draw the list in the absolute centre
                    633:         */
                    634:        format_draw_put_list(octx, ocx, ocy, abs_centre_offset, width_list,
                    635:            list, list_left, list_right, focus_start, focus_end, frs);
                    636:        abs_centre_offset += width_list;
                    637:
                    638:        /*
                    639:         * Write after at the end of the centre
                    640:         */
                    641:        format_draw_put(octx, ocx, ocy, after, frs, abs_centre_offset, 0,
                    642:            width_after);
1.1       nicm      643: }
                    644:
1.24      nicm      645: /* Get width and count of any leading #s. */
                    646: static const char *
                    647: format_leading_hashes(const char *cp, u_int *n, u_int *width)
                    648: {
                    649:        for (*n = 0; cp[*n] == '#'; (*n)++)
                    650:                /* nothing */;
                    651:        if (*n == 0) {
                    652:                *width = 0;
                    653:                return (cp);
                    654:        }
                    655:        if (cp[*n] != '[') {
                    656:                if ((*n % 2) == 0)
                    657:                        *width = (*n / 2);
                    658:                else
                    659:                        *width = (*n / 2) + 1;
                    660:                return (cp + *n);
                    661:        }
                    662:        *width = (*n / 2);
                    663:        if ((*n % 2) == 0) {
                    664:                /*
                    665:                 * An even number of #s means that all #s are escaped, so not a
                    666:                 * style. The caller should not skip this. Return pointing to
                    667:                 * the [.
                    668:                 */
                    669:                return (cp + *n);
                    670:        }
                    671:        /* This is a style, so return pointing to the #. */
                    672:        return (cp + *n - 1);
                    673: }
                    674:
1.21      nicm      675: /* Draw multiple characters. */
                    676: static void
                    677: format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch,
                    678:     u_int n)
                    679: {
                    680:        u_int   i;
                    681:
                    682:        utf8_set(&sy->gc.data, ch);
                    683:        for (i = 0; i < n; i++)
                    684:                screen_write_cell(ctx, &sy->gc);
                    685: }
                    686:
1.1       nicm      687: /* Draw a format to a screen. */
                    688: void
                    689: format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
1.25      nicm      690:     u_int available, const char *expanded, struct style_ranges *srs,
                    691:     int default_colours)
1.1       nicm      692: {
                    693:        enum { LEFT,
                    694:               CENTRE,
                    695:               RIGHT,
1.23      nicm      696:               ABSOLUTE_CENTRE,
1.1       nicm      697:               LIST,
                    698:               LIST_LEFT,
                    699:               LIST_RIGHT,
                    700:               AFTER,
                    701:               TOTAL } current = LEFT, last = LEFT;
                    702:        const char              *names[] = { "LEFT",
                    703:                                             "CENTRE",
                    704:                                             "RIGHT",
1.23      nicm      705:                                             "ABSOLUTE_CENTRE",
1.1       nicm      706:                                             "LIST",
                    707:                                             "LIST_LEFT",
                    708:                                             "LIST_RIGHT",
                    709:                                             "AFTER" };
                    710:        size_t                   size = strlen(expanded);
                    711:        struct screen           *os = octx->s, s[TOTAL];
                    712:        struct screen_write_ctx  ctx[TOTAL];
1.21      nicm      713:        u_int                    ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
1.23      nicm      714:        u_int                    map[] = { LEFT,
                    715:                                           LEFT,
                    716:                                           CENTRE,
                    717:                                           RIGHT,
                    718:                                           ABSOLUTE_CENTRE };
1.1       nicm      719:        int                      focus_start = -1, focus_end = -1;
1.21      nicm      720:        int                      list_state = -1, fill = -1, even;
1.1       nicm      721:        enum style_align         list_align = STYLE_ALIGN_DEFAULT;
1.13      nicm      722:        struct grid_cell         gc, current_default;
                    723:        struct style             sy, saved_sy;
1.1       nicm      724:        struct utf8_data        *ud = &sy.gc.data;
                    725:        const char              *cp, *end;
                    726:        enum utf8_state          more;
                    727:        char                    *tmp;
                    728:        struct format_range     *fr = NULL, *fr1;
                    729:        struct format_ranges     frs;
                    730:        struct style_range      *sr;
                    731:
1.13      nicm      732:        memcpy(&current_default, base, sizeof current_default);
                    733:        style_set(&sy, &current_default);
1.1       nicm      734:        TAILQ_INIT(&frs);
1.3       nicm      735:        log_debug("%s: %s", __func__, expanded);
1.1       nicm      736:
                    737:        /*
                    738:         * We build three screens for left, right, centre alignment, one for
                    739:         * the list, one for anything after the list and two for the list left
                    740:         * and right markers.
                    741:         */
                    742:        for (i = 0; i < TOTAL; i++) {
                    743:                screen_init(&s[i], size, 1, 0);
1.18      nicm      744:                screen_write_start(&ctx[i], &s[i]);
1.13      nicm      745:                screen_write_clearendofline(&ctx[i], current_default.bg);
1.1       nicm      746:                width[i] = 0;
                    747:        }
                    748:
                    749:        /*
                    750:         * Walk the string and add to the corresponding screens,
                    751:         * parsing styles as we go.
                    752:         */
                    753:        cp = expanded;
                    754:        while (*cp != '\0') {
1.21      nicm      755:                /* Handle sequences of #. */
                    756:                if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') {
                    757:                        for (n = 1; cp[n] == '#'; n++)
                    758:                                 /* nothing */;
1.22      nicm      759:                        even = ((n % 2) == 0);
1.21      nicm      760:                        if (cp[n] != '[') {
1.22      nicm      761:                                cp += n;
                    762:                                if (even)
                    763:                                        n = (n / 2);
                    764:                                else
                    765:                                        n = (n / 2) + 1;
1.21      nicm      766:                                width[current] += n;
                    767:                                format_draw_many(&ctx[current], &sy, '#', n);
                    768:                                continue;
                    769:                        }
                    770:                        if (even)
                    771:                                cp += (n + 1);
                    772:                        else
                    773:                                cp += (n - 1);
                    774:                        if (sy.ignore)
                    775:                                continue;
                    776:                        format_draw_many(&ctx[current], &sy, '#', n / 2);
                    777:                        width[current] += (n / 2);
                    778:                        if (even) {
                    779:                                utf8_set(ud, '[');
                    780:                                screen_write_cell(&ctx[current], &sy.gc);
                    781:                                width[current]++;
                    782:                        }
                    783:                        continue;
                    784:                }
                    785:
                    786:                /* Is this not a style? */
1.19      nicm      787:                if (cp[0] != '#' || cp[1] != '[' || sy.ignore) {
1.1       nicm      788:                        /* See if this is a UTF-8 character. */
                    789:                        if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
                    790:                                while (*++cp != '\0' && more == UTF8_MORE)
                    791:                                        more = utf8_append(ud, *cp);
                    792:                                if (more != UTF8_DONE)
                    793:                                        cp -= ud->have;
                    794:                        }
                    795:
                    796:                        /* Not a UTF-8 character - ASCII or not valid. */
                    797:                        if (more != UTF8_DONE) {
                    798:                                if (*cp < 0x20 || *cp > 0x7e) {
                    799:                                        /* Ignore nonprintable characters. */
                    800:                                        cp++;
                    801:                                        continue;
                    802:                                }
                    803:                                utf8_set(ud, *cp);
                    804:                                cp++;
                    805:                        }
                    806:
1.12      nicm      807:                        /* Draw the cell to the current screen. */
1.1       nicm      808:                        screen_write_cell(&ctx[current], &sy.gc);
                    809:                        width[current] += ud->width;
                    810:                        continue;
                    811:                }
                    812:
                    813:                /* This is a style. Work out where the end is and parse it. */
                    814:                end = format_skip(cp + 2, "]");
1.3       nicm      815:                if (end == NULL) {
1.6       nicm      816:                        log_debug("%s: no terminating ] at '%s'", __func__,
                    817:                            cp + 2);
1.5       nicm      818:                        TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1)
                    819:                            format_free_range(&frs, fr);
                    820:                        goto out;
1.3       nicm      821:                }
1.1       nicm      822:                tmp = xstrndup(cp + 2, end - (cp + 2));
1.13      nicm      823:                style_copy(&saved_sy, &sy);
                    824:                if (style_parse(&sy, &current_default, tmp) != 0) {
1.6       nicm      825:                        log_debug("%s: invalid style '%s'", __func__, tmp);
1.1       nicm      826:                        free(tmp);
1.4       nicm      827:                        cp = end + 1;
                    828:                        continue;
1.1       nicm      829:                }
1.6       nicm      830:                log_debug("%s: style '%s' -> '%s'", __func__, tmp,
                    831:                    style_tostring(&sy));
1.1       nicm      832:                free(tmp);
1.25      nicm      833:                if (default_colours) {
                    834:                        sy.gc.bg = base->bg;
                    835:                        sy.gc.fg = base->fg;
                    836:                }
1.1       nicm      837:
1.11      nicm      838:                /* If this style has a fill colour, store it for later. */
                    839:                if (sy.fill != 8)
                    840:                        fill = sy.fill;
1.13      nicm      841:
                    842:                /* If this style pushed or popped the default, update it. */
                    843:                if (sy.default_type == STYLE_DEFAULT_PUSH) {
1.20      nicm      844:                        memcpy(&current_default, &saved_sy.gc,
                    845:                            sizeof current_default);
1.13      nicm      846:                        sy.default_type = STYLE_DEFAULT_BASE;
                    847:                } else if (sy.default_type == STYLE_DEFAULT_POP) {
                    848:                        memcpy(&current_default, base, sizeof current_default);
                    849:                        sy.default_type = STYLE_DEFAULT_BASE;
                    850:                }
1.11      nicm      851:
1.1       nicm      852:                /* Check the list state. */
                    853:                switch (sy.list) {
                    854:                case STYLE_LIST_ON:
                    855:                        /*
                    856:                         * Entering the list, exiting a marker, or exiting the
                    857:                         * focus.
                    858:                         */
                    859:                        if (list_state != 0) {
                    860:                                if (fr != NULL) { /* abort any region */
                    861:                                        free(fr);
                    862:                                        fr = NULL;
                    863:                                }
                    864:                                list_state = 0;
                    865:                                list_align = sy.align;
                    866:                        }
                    867:
                    868:                        /* End the focus if started. */
                    869:                        if (focus_start != -1 && focus_end == -1)
                    870:                                focus_end = s[LIST].cx;
                    871:
                    872:                        current = LIST;
                    873:                        break;
                    874:                case STYLE_LIST_FOCUS:
                    875:                        /* Entering the focus. */
                    876:                        if (list_state != 0) /* not inside the list */
                    877:                                break;
                    878:                        if (focus_start == -1) /* focus already started */
                    879:                                focus_start = s[LIST].cx;
                    880:                        break;
                    881:                case STYLE_LIST_OFF:
                    882:                        /* Exiting or outside the list. */
                    883:                        if (list_state == 0) {
                    884:                                if (fr != NULL) { /* abort any region */
                    885:                                        free(fr);
                    886:                                        fr = NULL;
                    887:                                }
                    888:                                if (focus_start != -1 && focus_end == -1)
                    889:                                        focus_end = s[LIST].cx;
                    890:
                    891:                                map[list_align] = AFTER;
                    892:                                if (list_align == STYLE_ALIGN_LEFT)
                    893:                                        map[STYLE_ALIGN_DEFAULT] = AFTER;
                    894:                                list_state = 1;
                    895:                        }
                    896:                        current = map[sy.align];
                    897:                        break;
                    898:                case STYLE_LIST_LEFT_MARKER:
                    899:                        /* Entering left marker. */
                    900:                        if (list_state != 0) /* not inside the list */
                    901:                                break;
                    902:                        if (s[LIST_LEFT].cx != 0) /* already have marker */
                    903:                                break;
                    904:                        if (fr != NULL) { /* abort any region */
                    905:                                free(fr);
                    906:                                fr = NULL;
                    907:                        }
                    908:                        if (focus_start != -1 && focus_end == -1)
                    909:                                focus_start = focus_end = -1;
                    910:                        current = LIST_LEFT;
                    911:                        break;
                    912:                case STYLE_LIST_RIGHT_MARKER:
                    913:                        /* Entering right marker. */
                    914:                        if (list_state != 0) /* not inside the list */
                    915:                                break;
                    916:                        if (s[LIST_RIGHT].cx != 0) /* already have marker */
                    917:                                break;
                    918:                        if (fr != NULL) { /* abort any region */
                    919:                                free(fr);
                    920:                                fr = NULL;
                    921:                        }
                    922:                        if (focus_start != -1 && focus_end == -1)
                    923:                                focus_start = focus_end = -1;
                    924:                        current = LIST_RIGHT;
                    925:                        break;
                    926:                }
                    927:                if (current != last) {
                    928:                        log_debug("%s: change %s -> %s", __func__,
                    929:                            names[last], names[current]);
                    930:                        last = current;
                    931:                }
                    932:
                    933:                /*
                    934:                 * Check if the range style has changed and if so end the
                    935:                 * current range and start a new one if needed.
                    936:                 */
                    937:                if (srs != NULL) {
                    938:                        if (fr != NULL && !format_is_type(fr, &sy)) {
                    939:                                if (s[current].cx != fr->start) {
                    940:                                        fr->end = s[current].cx + 1;
                    941:                                        TAILQ_INSERT_TAIL(&frs, fr, entry);
                    942:                                } else
                    943:                                        free(fr);
                    944:                                fr = NULL;
                    945:                        }
                    946:                        if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) {
                    947:                                fr = xcalloc(1, sizeof *fr);
                    948:                                fr->index = current;
                    949:
                    950:                                fr->s = &s[current];
                    951:                                fr->start = s[current].cx;
                    952:
                    953:                                fr->type = sy.range_type;
                    954:                                fr->argument = sy.range_argument;
1.28    ! nicm      955:                                strlcpy(fr->string, sy.range_string,
        !           956:                                    sizeof fr->string);
1.1       nicm      957:                        }
                    958:                }
                    959:
                    960:                cp = end + 1;
                    961:        }
                    962:        free(fr);
                    963:
1.2       nicm      964:        for (i = 0; i < TOTAL; i++) {
                    965:                screen_write_stop(&ctx[i]);
1.1       nicm      966:                log_debug("%s: width %s is %u", __func__, names[i], width[i]);
1.2       nicm      967:        }
1.1       nicm      968:        if (focus_start != -1 && focus_end != -1)
1.6       nicm      969:                log_debug("%s: focus %d-%d", __func__, focus_start, focus_end);
1.1       nicm      970:        TAILQ_FOREACH(fr, &frs, entry) {
                    971:                log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type,
                    972:                    fr->argument, names[fr->index], fr->start, fr->end);
1.11      nicm      973:        }
                    974:
                    975:        /* Clear the available area. */
                    976:        if (fill != -1) {
                    977:                memcpy(&gc, &grid_default_cell, sizeof gc);
                    978:                gc.bg = fill;
                    979:                for (i = 0; i < available; i++)
                    980:                        screen_write_putc(octx, &gc, ' ');
1.1       nicm      981:        }
                    982:
                    983:        /*
                    984:         * Draw the screens. How they are arranged depends on where the list
1.17      nicm      985:         * appears.
1.1       nicm      986:         */
                    987:        switch (list_align) {
                    988:        case STYLE_ALIGN_DEFAULT:
                    989:                /* No list. */
                    990:                format_draw_none(octx, available, ocx, ocy, &s[LEFT],
1.23      nicm      991:                    &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &frs);
1.1       nicm      992:                break;
                    993:        case STYLE_ALIGN_LEFT:
                    994:                /* List is part of the left. */
                    995:                format_draw_left(octx, available, ocx, ocy, &s[LEFT],
1.23      nicm      996:                    &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
                    997:                    &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
                    998:                    focus_start, focus_end, &frs);
1.1       nicm      999:                break;
                   1000:        case STYLE_ALIGN_CENTRE:
                   1001:                /* List is part of the centre. */
                   1002:                format_draw_centre(octx, available, ocx, ocy, &s[LEFT],
1.23      nicm     1003:                    &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
                   1004:                    &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
                   1005:                    focus_start, focus_end, &frs);
1.1       nicm     1006:                break;
                   1007:        case STYLE_ALIGN_RIGHT:
                   1008:                /* List is part of the right. */
                   1009:                format_draw_right(octx, available, ocx, ocy, &s[LEFT],
1.23      nicm     1010:                    &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
                   1011:                    &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
                   1012:                    focus_start, focus_end, &frs);
                   1013:                break;
                   1014:        case STYLE_ALIGN_ABSOLUTE_CENTRE:
                   1015:                /* List is in the centre of the entire horizontal space. */
                   1016:                format_draw_absolute_centre(octx, available, ocx, ocy, &s[LEFT],
                   1017:                    &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
                   1018:                    &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
                   1019:                    focus_start, focus_end, &frs);
1.1       nicm     1020:                break;
                   1021:        }
                   1022:
                   1023:        /* Create ranges to return. */
                   1024:        TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) {
                   1025:                sr = xcalloc(1, sizeof *sr);
                   1026:                sr->type = fr->type;
                   1027:                sr->argument = fr->argument;
1.28    ! nicm     1028:                strlcpy(sr->string, fr->string, sizeof sr->string);
1.1       nicm     1029:                sr->start = fr->start;
                   1030:                sr->end = fr->end;
                   1031:                TAILQ_INSERT_TAIL(srs, sr, entry);
                   1032:
1.28    ! nicm     1033:                switch (sr->type) {
        !          1034:                case STYLE_RANGE_NONE:
        !          1035:                        break;
        !          1036:                case STYLE_RANGE_LEFT:
        !          1037:                        log_debug("%s: range left at %u-%u", __func__,
        !          1038:                            sr->start, sr->end);
        !          1039:                        break;
        !          1040:                case STYLE_RANGE_RIGHT:
        !          1041:                        log_debug("%s: range right at %u-%u", __func__,
        !          1042:                            sr->start, sr->end);
        !          1043:                        break;
        !          1044:                case STYLE_RANGE_PANE:
        !          1045:                        log_debug("%s: range pane|%%%u at %u-%u", __func__,
        !          1046:                            sr->argument, sr->start, sr->end);
        !          1047:                        break;
        !          1048:                case STYLE_RANGE_WINDOW:
        !          1049:                        log_debug("%s: range window|%u at %u-%u", __func__,
        !          1050:                            sr->argument, sr->start, sr->end);
        !          1051:                        break;
        !          1052:                case STYLE_RANGE_SESSION:
        !          1053:                        log_debug("%s: range session|$%u at %u-%u", __func__,
        !          1054:                            sr->argument, sr->start, sr->end);
        !          1055:                        break;
        !          1056:                case STYLE_RANGE_USER:
        !          1057:                        log_debug("%s: range user|%u at %u-%u", __func__,
        !          1058:                            sr->argument, sr->start, sr->end);
        !          1059:                        break;
        !          1060:                }
1.1       nicm     1061:                format_free_range(&frs, fr);
                   1062:        }
1.2       nicm     1063:
1.5       nicm     1064: out:
1.2       nicm     1065:        /* Free the screens. */
                   1066:        for (i = 0; i < TOTAL; i++)
                   1067:                screen_free(&s[i]);
1.1       nicm     1068:
                   1069:        /* Restore the original cursor position. */
                   1070:        screen_write_cursormove(octx, ocx, ocy, 0);
                   1071: }
                   1072:
                   1073: /* Get width, taking #[] into account. */
                   1074: u_int
                   1075: format_width(const char *expanded)
                   1076: {
                   1077:        const char              *cp, *end;
1.24      nicm     1078:        u_int                    n, leading_width, width = 0;
1.1       nicm     1079:        struct utf8_data         ud;
                   1080:        enum utf8_state          more;
                   1081:
                   1082:        cp = expanded;
                   1083:        while (*cp != '\0') {
1.21      nicm     1084:                if (*cp == '#') {
1.24      nicm     1085:                        end = format_leading_hashes(cp, &n, &leading_width);
                   1086:                        width += leading_width;
                   1087:                        cp = end;
                   1088:                        if (*cp == '#') {
                   1089:                                end = format_skip(cp + 2, "]");
                   1090:                                if (end == NULL)
                   1091:                                        return (0);
                   1092:                                cp = end + 1;
1.21      nicm     1093:                        }
1.1       nicm     1094:                } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
                   1095:                        while (*++cp != '\0' && more == UTF8_MORE)
                   1096:                                more = utf8_append(&ud, *cp);
                   1097:                        if (more == UTF8_DONE)
                   1098:                                width += ud.width;
                   1099:                        else
                   1100:                                cp -= ud.have;
                   1101:                } else if (*cp > 0x1f && *cp < 0x7f) {
                   1102:                        width++;
                   1103:                        cp++;
1.10      nicm     1104:                } else
                   1105:                        cp++;
1.1       nicm     1106:        }
                   1107:        return (width);
                   1108: }
                   1109:
1.21      nicm     1110: /*
                   1111:  * Trim on the left, taking #[] into account.  Note, we copy the whole set of
                   1112:  * unescaped #s, but only add their escaped size to width. This is because the
                   1113:  * format_draw function will actually do the escaping when it runs
                   1114:  */
1.1       nicm     1115: char *
                   1116: format_trim_left(const char *expanded, u_int limit)
                   1117: {
                   1118:        char                    *copy, *out;
                   1119:        const char              *cp = expanded, *end;
1.24      nicm     1120:        u_int                    n, width = 0, leading_width;
1.1       nicm     1121:        struct utf8_data         ud;
                   1122:        enum utf8_state          more;
                   1123:
1.27      nicm     1124:        out = copy = xcalloc(2, strlen(expanded) + 1);
1.1       nicm     1125:        while (*cp != '\0') {
1.21      nicm     1126:                if (width >= limit)
                   1127:                        break;
                   1128:                if (*cp == '#') {
1.24      nicm     1129:                        end = format_leading_hashes(cp, &n, &leading_width);
                   1130:                        if (leading_width > limit - width)
                   1131:                                leading_width = limit - width;
                   1132:                        if (leading_width != 0) {
                   1133:                                if (n == 1)
                   1134:                                        *out++ = '#';
                   1135:                                else {
                   1136:                                        memset(out, '#', 2 * leading_width);
                   1137:                                        out += 2 * leading_width;
                   1138:                                }
                   1139:                                width += leading_width;
1.21      nicm     1140:                        }
1.24      nicm     1141:                        cp = end;
                   1142:                        if (*cp == '#') {
                   1143:                                end = format_skip(cp + 2, "]");
                   1144:                                if (end == NULL)
                   1145:                                        break;
                   1146:                                memcpy(out, cp, end + 1 - cp);
                   1147:                                out += (end + 1 - cp);
1.21      nicm     1148:                                cp = end + 1;
                   1149:                        }
1.1       nicm     1150:                } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
                   1151:                        while (*++cp != '\0' && more == UTF8_MORE)
                   1152:                                more = utf8_append(&ud, *cp);
                   1153:                        if (more == UTF8_DONE) {
                   1154:                                if (width + ud.width <= limit) {
                   1155:                                        memcpy(out, ud.data, ud.size);
                   1156:                                        out += ud.size;
                   1157:                                }
                   1158:                                width += ud.width;
1.14      nicm     1159:                        } else {
1.1       nicm     1160:                                cp -= ud.have;
1.14      nicm     1161:                                cp++;
                   1162:                        }
1.1       nicm     1163:                } else if (*cp > 0x1f && *cp < 0x7f) {
                   1164:                        if (width + 1 <= limit)
                   1165:                                *out++ = *cp;
                   1166:                        width++;
                   1167:                        cp++;
1.8       nicm     1168:                } else
                   1169:                        cp++;
1.1       nicm     1170:        }
                   1171:        *out = '\0';
                   1172:        return (copy);
                   1173: }
                   1174:
                   1175: /* Trim on the right, taking #[] into account. */
                   1176: char *
                   1177: format_trim_right(const char *expanded, u_int limit)
                   1178: {
                   1179:        char                    *copy, *out;
                   1180:        const char              *cp = expanded, *end;
1.24      nicm     1181:        u_int                    width = 0, total_width, skip, n;
                   1182:        u_int                    leading_width, copy_width;
1.1       nicm     1183:        struct utf8_data         ud;
                   1184:        enum utf8_state          more;
                   1185:
                   1186:        total_width = format_width(expanded);
                   1187:        if (total_width <= limit)
                   1188:                return (xstrdup(expanded));
                   1189:        skip = total_width - limit;
                   1190:
1.27      nicm     1191:        out = copy = xcalloc(2, strlen(expanded) + 1);
1.1       nicm     1192:        while (*cp != '\0') {
1.21      nicm     1193:                if (*cp == '#') {
1.24      nicm     1194:                        end = format_leading_hashes(cp, &n, &leading_width);
1.26      nicm     1195:                        copy_width = leading_width;
1.21      nicm     1196:                        if (width <= skip) {
1.26      nicm     1197:                                if (skip - width >= copy_width)
1.24      nicm     1198:                                        copy_width = 0;
1.21      nicm     1199:                                else
1.24      nicm     1200:                                        copy_width -= (skip - width);
1.26      nicm     1201:                        }
1.24      nicm     1202:                        if (copy_width != 0) {
                   1203:                                if (n == 1)
                   1204:                                        *out++ = '#';
                   1205:                                else {
                   1206:                                        memset(out, '#', 2 * copy_width);
                   1207:                                        out += 2 * copy_width;
                   1208:                                }
1.21      nicm     1209:                        }
1.24      nicm     1210:                        width += leading_width;
                   1211:                        cp = end;
                   1212:                        if (*cp == '#') {
                   1213:                                end = format_skip(cp + 2, "]");
                   1214:                                if (end == NULL)
                   1215:                                        break;
                   1216:                                memcpy(out, cp, end + 1 - cp);
                   1217:                                out += (end + 1 - cp);
                   1218:                                cp = end + 1;
1.21      nicm     1219:                        }
1.1       nicm     1220:                } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
                   1221:                        while (*++cp != '\0' && more == UTF8_MORE)
                   1222:                                more = utf8_append(&ud, *cp);
                   1223:                        if (more == UTF8_DONE) {
                   1224:                                if (width >= skip) {
                   1225:                                        memcpy(out, ud.data, ud.size);
                   1226:                                        out += ud.size;
                   1227:                                }
                   1228:                                width += ud.width;
1.14      nicm     1229:                        } else {
1.1       nicm     1230:                                cp -= ud.have;
1.14      nicm     1231:                                cp++;
                   1232:                        }
1.1       nicm     1233:                } else if (*cp > 0x1f && *cp < 0x7f) {
                   1234:                        if (width >= skip)
                   1235:                                *out++ = *cp;
                   1236:                        width++;
                   1237:                        cp++;
1.8       nicm     1238:                } else
                   1239:                        cp++;
1.1       nicm     1240:        }
                   1241:        *out = '\0';
                   1242:        return (copy);
                   1243: }