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

Annotation of src/usr.bin/tmux/mode-tree.c, Revision 1.50

1.50    ! nicm        1: /* $OpenBSD: mode-tree.c,v 1.49 2020/05/16 16:35:13 nicm Exp $ */
1.1       nicm        2:
                      3: /*
                      4:  * Copyright (c) 2017 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 <ctype.h>
                     22: #include <stdio.h>
                     23: #include <stdlib.h>
                     24: #include <string.h>
                     25:
                     26: #include "tmux.h"
                     27:
                     28: struct mode_tree_item;
                     29: TAILQ_HEAD(mode_tree_list, mode_tree_item);
                     30:
                     31: struct mode_tree_data {
1.3       nicm       32:        int                       dead;
                     33:        u_int                     references;
1.23      nicm       34:        int                       zoomed;
1.3       nicm       35:
1.1       nicm       36:        struct window_pane       *wp;
                     37:        void                     *modedata;
1.33      nicm       38:        const struct menu_item   *menu;
1.1       nicm       39:
                     40:        const char              **sort_list;
                     41:        u_int                     sort_size;
1.36      nicm       42:        struct mode_tree_sort_criteria sort_crit;
1.1       nicm       43:
1.17      nicm       44:        mode_tree_build_cb        buildcb;
                     45:        mode_tree_draw_cb         drawcb;
                     46:        mode_tree_search_cb       searchcb;
1.28      nicm       47:        mode_tree_menu_cb         menucb;
1.46      nicm       48:        mode_tree_height_cb       heightcb;
1.1       nicm       49:
                     50:        struct mode_tree_list     children;
                     51:        struct mode_tree_list     saved;
                     52:
                     53:        struct mode_tree_line    *line_list;
                     54:        u_int                     line_size;
                     55:
                     56:        u_int                     depth;
                     57:
                     58:        u_int                     width;
                     59:        u_int                     height;
                     60:
                     61:        u_int                     offset;
                     62:        u_int                     current;
                     63:
                     64:        struct screen             screen;
1.3       nicm       65:
1.9       nicm       66:        int                       preview;
1.6       nicm       67:        char                     *search;
                     68:        char                     *filter;
1.21      nicm       69:        int                       no_matches;
1.1       nicm       70: };
                     71:
                     72: struct mode_tree_item {
                     73:        struct mode_tree_item           *parent;
                     74:        void                            *itemdata;
                     75:        u_int                            line;
                     76:
                     77:        uint64_t                         tag;
                     78:        const char                      *name;
                     79:        const char                      *text;
                     80:
                     81:        int                              expanded;
                     82:        int                              tagged;
1.50    ! nicm       83:
1.46      nicm       84:        int                              draw_as_parent;
1.50    ! nicm       85:        int                              no_tag;
1.1       nicm       86:
                     87:        struct mode_tree_list            children;
                     88:        TAILQ_ENTRY(mode_tree_item)      entry;
                     89: };
                     90:
                     91: struct mode_tree_line {
                     92:        struct mode_tree_item           *item;
                     93:        u_int                            depth;
                     94:        int                              last;
                     95:        int                              flat;
                     96: };
                     97:
1.28      nicm       98: struct mode_tree_menu {
                     99:        struct mode_tree_data           *data;
                    100:        struct client                   *c;
                    101:        u_int                            line;
                    102:        void                            *itemdata;
                    103: };
                    104:
1.1       nicm      105: static void mode_tree_free_items(struct mode_tree_list *);
                    106:
1.33      nicm      107: static const struct menu_item mode_tree_menu_items[] = {
                    108:        { "Scroll Left", '<', NULL },
                    109:        { "Scroll Right", '>', NULL },
                    110:        { "", KEYC_NONE, NULL },
                    111:        { "Cancel", 'q', NULL },
                    112:
                    113:        { NULL, KEYC_NONE, NULL }
                    114: };
1.28      nicm      115:
1.1       nicm      116: static struct mode_tree_item *
                    117: mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
                    118: {
                    119:        struct mode_tree_item   *mti, *child;
                    120:
                    121:        TAILQ_FOREACH(mti, mtl, entry) {
                    122:                if (mti->tag == tag)
                    123:                        return (mti);
                    124:                child = mode_tree_find_item(&mti->children, tag);
                    125:                if (child != NULL)
                    126:                        return (child);
                    127:        }
                    128:        return (NULL);
                    129: }
                    130:
                    131: static void
                    132: mode_tree_free_item(struct mode_tree_item *mti)
                    133: {
                    134:        mode_tree_free_items(&mti->children);
                    135:
                    136:        free((void *)mti->name);
                    137:        free((void *)mti->text);
                    138:
                    139:        free(mti);
                    140: }
                    141:
                    142: static void
                    143: mode_tree_free_items(struct mode_tree_list *mtl)
                    144: {
                    145:        struct mode_tree_item   *mti, *mti1;
                    146:
                    147:        TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1) {
                    148:                TAILQ_REMOVE(mtl, mti, entry);
                    149:                mode_tree_free_item(mti);
                    150:        }
                    151: }
                    152:
                    153: static void
1.11      nicm      154: mode_tree_check_selected(struct mode_tree_data *mtd)
                    155: {
                    156:        /*
                    157:         * If the current line would now be off screen reset the offset to the
                    158:         * last visible line.
                    159:         */
                    160:        if (mtd->current > mtd->height - 1)
                    161:                mtd->offset = mtd->current - mtd->height + 1;
                    162: }
                    163:
                    164: static void
1.1       nicm      165: mode_tree_clear_lines(struct mode_tree_data *mtd)
                    166: {
                    167:        free(mtd->line_list);
                    168:        mtd->line_list = NULL;
                    169:        mtd->line_size = 0;
                    170: }
                    171:
                    172: static void
                    173: mode_tree_build_lines(struct mode_tree_data *mtd,
                    174:     struct mode_tree_list *mtl, u_int depth)
                    175: {
                    176:        struct mode_tree_item   *mti;
                    177:        struct mode_tree_line   *line;
                    178:        u_int                    i;
                    179:        int                      flat = 1;
                    180:
                    181:        mtd->depth = depth;
                    182:        TAILQ_FOREACH(mti, mtl, entry) {
                    183:                mtd->line_list = xreallocarray(mtd->line_list,
                    184:                    mtd->line_size + 1, sizeof *mtd->line_list);
                    185:
                    186:                line = &mtd->line_list[mtd->line_size++];
                    187:                line->item = mti;
                    188:                line->depth = depth;
                    189:                line->last = (mti == TAILQ_LAST(mtl, mode_tree_list));
                    190:
                    191:                mti->line = (mtd->line_size - 1);
                    192:                if (!TAILQ_EMPTY(&mti->children))
                    193:                        flat = 0;
                    194:                if (mti->expanded)
                    195:                        mode_tree_build_lines(mtd, &mti->children, depth + 1);
                    196:        }
                    197:        TAILQ_FOREACH(mti, mtl, entry) {
                    198:                for (i = 0; i < mtd->line_size; i++) {
                    199:                        line = &mtd->line_list[i];
                    200:                        if (line->item == mti)
                    201:                                line->flat = flat;
                    202:                }
                    203:        }
                    204: }
                    205:
                    206: static void
                    207: mode_tree_clear_tagged(struct mode_tree_list *mtl)
                    208: {
                    209:        struct mode_tree_item   *mti;
                    210:
                    211:        TAILQ_FOREACH(mti, mtl, entry) {
                    212:                mti->tagged = 0;
                    213:                mode_tree_clear_tagged(&mti->children);
                    214:        }
                    215: }
                    216:
1.46      nicm      217: void
1.1       nicm      218: mode_tree_up(struct mode_tree_data *mtd, int wrap)
                    219: {
                    220:        if (mtd->current == 0) {
                    221:                if (wrap) {
                    222:                        mtd->current = mtd->line_size - 1;
                    223:                        if (mtd->line_size >= mtd->height)
                    224:                                mtd->offset = mtd->line_size - mtd->height;
                    225:                }
                    226:        } else {
                    227:                mtd->current--;
                    228:                if (mtd->current < mtd->offset)
                    229:                        mtd->offset--;
                    230:        }
                    231: }
                    232:
                    233: void
                    234: mode_tree_down(struct mode_tree_data *mtd, int wrap)
                    235: {
                    236:        if (mtd->current == mtd->line_size - 1) {
                    237:                if (wrap) {
                    238:                        mtd->current = 0;
                    239:                        mtd->offset = 0;
                    240:                }
                    241:        } else {
                    242:                mtd->current++;
                    243:                if (mtd->current > mtd->offset + mtd->height - 1)
                    244:                        mtd->offset++;
                    245:        }
                    246: }
                    247:
                    248: void *
                    249: mode_tree_get_current(struct mode_tree_data *mtd)
                    250: {
                    251:        return (mtd->line_list[mtd->current].item->itemdata);
                    252: }
                    253:
1.46      nicm      254: const char *
                    255: mode_tree_get_current_name(struct mode_tree_data *mtd)
                    256: {
                    257:        return (mtd->line_list[mtd->current].item->name);
                    258: }
                    259:
1.19      nicm      260: void
                    261: mode_tree_expand_current(struct mode_tree_data *mtd)
                    262: {
                    263:        if (!mtd->line_list[mtd->current].item->expanded) {
                    264:                mtd->line_list[mtd->current].item->expanded = 1;
                    265:                mode_tree_build(mtd);
                    266:        }
                    267: }
                    268:
1.46      nicm      269: void
                    270: mode_tree_collapse_current(struct mode_tree_data *mtd)
                    271: {
                    272:        if (mtd->line_list[mtd->current].item->expanded) {
                    273:                mtd->line_list[mtd->current].item->expanded = 0;
                    274:                mode_tree_build(mtd);
                    275:        }
                    276: }
                    277:
1.41      nicm      278: static int
                    279: mode_tree_get_tag(struct mode_tree_data *mtd, uint64_t tag, u_int *found)
1.19      nicm      280: {
                    281:        u_int   i;
                    282:
                    283:        for (i = 0; i < mtd->line_size; i++) {
                    284:                if (mtd->line_list[i].item->tag == tag)
                    285:                        break;
                    286:        }
                    287:        if (i != mtd->line_size) {
1.41      nicm      288:                *found = i;
                    289:                return (1);
                    290:        }
                    291:        return (0);
                    292: }
                    293:
                    294: void
                    295: mode_tree_expand(struct mode_tree_data *mtd, uint64_t tag)
                    296: {
                    297:        u_int   found;
                    298:
                    299:        if (!mode_tree_get_tag(mtd, tag, &found))
                    300:            return;
                    301:        if (!mtd->line_list[found].item->expanded) {
                    302:                mtd->line_list[found].item->expanded = 1;
                    303:                mode_tree_build(mtd);
                    304:        }
                    305: }
                    306:
                    307: int
                    308: mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag)
                    309: {
                    310:        u_int   found;
                    311:
                    312:        if (mode_tree_get_tag(mtd, tag, &found)) {
                    313:                mtd->current = found;
1.19      nicm      314:                if (mtd->current > mtd->height - 1)
                    315:                        mtd->offset = mtd->current - mtd->height + 1;
                    316:                else
                    317:                        mtd->offset = 0;
1.41      nicm      318:                return (1);
1.19      nicm      319:        }
1.41      nicm      320:        mtd->current = 0;
                    321:        mtd->offset = 0;
                    322:        return (0);
1.19      nicm      323: }
                    324:
1.1       nicm      325: u_int
                    326: mode_tree_count_tagged(struct mode_tree_data *mtd)
                    327: {
                    328:        struct mode_tree_item   *mti;
                    329:        u_int                    i, tagged;
                    330:
                    331:        tagged = 0;
                    332:        for (i = 0; i < mtd->line_size; i++) {
                    333:                mti = mtd->line_list[i].item;
                    334:                if (mti->tagged)
                    335:                        tagged++;
                    336:        }
                    337:        return (tagged);
                    338: }
                    339:
                    340: void
1.17      nicm      341: mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb,
                    342:     struct client *c, key_code key, int current)
1.1       nicm      343: {
                    344:        struct mode_tree_item   *mti;
                    345:        u_int                    i;
                    346:        int                      fired;
                    347:
                    348:        fired = 0;
                    349:        for (i = 0; i < mtd->line_size; i++) {
                    350:                mti = mtd->line_list[i].item;
                    351:                if (mti->tagged) {
                    352:                        fired = 1;
1.14      nicm      353:                        cb(mtd->modedata, mti->itemdata, c, key);
1.1       nicm      354:                }
                    355:        }
                    356:        if (!fired && current) {
                    357:                mti = mtd->line_list[mtd->current].item;
1.14      nicm      358:                cb(mtd->modedata, mti->itemdata, c, key);
1.1       nicm      359:        }
                    360: }
                    361:
                    362: struct mode_tree_data *
1.5       nicm      363: mode_tree_start(struct window_pane *wp, struct args *args,
1.17      nicm      364:     mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
1.46      nicm      365:     mode_tree_search_cb searchcb, mode_tree_menu_cb menucb,
                    366:     mode_tree_height_cb heightcb, void *modedata,
1.33      nicm      367:     const struct menu_item *menu, const char **sort_list, u_int sort_size,
1.28      nicm      368:     struct screen **s)
1.1       nicm      369: {
                    370:        struct mode_tree_data   *mtd;
1.5       nicm      371:        const char              *sort;
                    372:        u_int                    i;
1.1       nicm      373:
                    374:        mtd = xcalloc(1, sizeof *mtd);
1.3       nicm      375:        mtd->references = 1;
                    376:
1.1       nicm      377:        mtd->wp = wp;
                    378:        mtd->modedata = modedata;
1.28      nicm      379:        mtd->menu = menu;
1.1       nicm      380:
                    381:        mtd->sort_list = sort_list;
                    382:        mtd->sort_size = sort_size;
1.5       nicm      383:
1.9       nicm      384:        mtd->preview = !args_has(args, 'N');
                    385:
1.5       nicm      386:        sort = args_get(args, 'O');
                    387:        if (sort != NULL) {
                    388:                for (i = 0; i < sort_size; i++) {
                    389:                        if (strcasecmp(sort, sort_list[i]) == 0)
1.36      nicm      390:                                mtd->sort_crit.field = i;
1.5       nicm      391:                }
                    392:        }
1.36      nicm      393:        mtd->sort_crit.reversed = args_has(args, 'r');
1.1       nicm      394:
1.6       nicm      395:        if (args_has(args, 'f'))
                    396:                mtd->filter = xstrdup(args_get(args, 'f'));
                    397:        else
                    398:                mtd->filter = NULL;
                    399:
1.1       nicm      400:        mtd->buildcb = buildcb;
                    401:        mtd->drawcb = drawcb;
1.3       nicm      402:        mtd->searchcb = searchcb;
1.28      nicm      403:        mtd->menucb = menucb;
1.46      nicm      404:        mtd->heightcb = heightcb;
1.1       nicm      405:
                    406:        TAILQ_INIT(&mtd->children);
                    407:
                    408:        *s = &mtd->screen;
                    409:        screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
                    410:        (*s)->mode &= ~MODE_CURSOR;
                    411:
                    412:        return (mtd);
                    413: }
                    414:
                    415: void
1.23      nicm      416: mode_tree_zoom(struct mode_tree_data *mtd, struct args *args)
                    417: {
                    418:        struct window_pane      *wp = mtd->wp;
                    419:
                    420:        if (args_has(args, 'Z')) {
                    421:                mtd->zoomed = (wp->window->flags & WINDOW_ZOOMED);
                    422:                if (!mtd->zoomed && window_zoom(wp) == 0)
                    423:                        server_redraw_window(wp->window);
                    424:        } else
                    425:                mtd->zoomed = -1;
                    426: }
                    427:
1.46      nicm      428: static void
                    429: mode_tree_set_height(struct mode_tree_data *mtd)
                    430: {
                    431:        struct screen   *s = &mtd->screen;
                    432:        u_int            height;
                    433:
                    434:        if (mtd->heightcb != NULL) {
                    435:                height = mtd->heightcb(mtd, screen_size_y(s));
                    436:                if (height < screen_size_y(s))
                    437:                    mtd->height = screen_size_y(s) - height;
                    438:        } else {
                    439:                mtd->height = (screen_size_y(s) / 3) * 2;
                    440:                if (mtd->height > mtd->line_size)
                    441:                        mtd->height = screen_size_y(s) / 2;
                    442:        }
                    443:        if (mtd->height < 10)
                    444:                mtd->height = screen_size_y(s);
                    445:        if (screen_size_y(s) - mtd->height < 2)
                    446:                mtd->height = screen_size_y(s);
                    447: }
                    448:
1.23      nicm      449: void
1.1       nicm      450: mode_tree_build(struct mode_tree_data *mtd)
                    451: {
                    452:        struct screen   *s = &mtd->screen;
                    453:        uint64_t         tag;
                    454:
                    455:        if (mtd->line_list != NULL)
                    456:                tag = mtd->line_list[mtd->current].item->tag;
                    457:        else
1.32      nicm      458:                tag = UINT64_MAX;
1.1       nicm      459:
                    460:        TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
                    461:        TAILQ_INIT(&mtd->children);
                    462:
1.36      nicm      463:        mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, mtd->filter);
1.21      nicm      464:        mtd->no_matches = TAILQ_EMPTY(&mtd->children);
                    465:        if (mtd->no_matches)
1.36      nicm      466:                mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, NULL);
1.1       nicm      467:
                    468:        mode_tree_free_items(&mtd->saved);
                    469:        TAILQ_INIT(&mtd->saved);
                    470:
                    471:        mode_tree_clear_lines(mtd);
                    472:        mode_tree_build_lines(mtd, &mtd->children, 0);
                    473:
1.32      nicm      474:        if (tag == UINT64_MAX)
                    475:                tag = mtd->line_list[mtd->current].item->tag;
1.3       nicm      476:        mode_tree_set_current(mtd, tag);
1.1       nicm      477:
                    478:        mtd->width = screen_size_x(s);
1.46      nicm      479:        if (mtd->preview)
                    480:                mode_tree_set_height(mtd);
                    481:        else
1.1       nicm      482:                mtd->height = screen_size_y(s);
1.11      nicm      483:        mode_tree_check_selected(mtd);
1.1       nicm      484: }
                    485:
1.3       nicm      486: static void
                    487: mode_tree_remove_ref(struct mode_tree_data *mtd)
                    488: {
                    489:        if (--mtd->references == 0)
                    490:                free(mtd);
                    491: }
                    492:
1.1       nicm      493: void
                    494: mode_tree_free(struct mode_tree_data *mtd)
                    495: {
1.23      nicm      496:        struct window_pane      *wp = mtd->wp;
                    497:
                    498:        if (mtd->zoomed == 0)
                    499:                server_unzoom_window(wp->window);
                    500:
1.1       nicm      501:        mode_tree_free_items(&mtd->children);
                    502:        mode_tree_clear_lines(mtd);
                    503:        screen_free(&mtd->screen);
1.3       nicm      504:
1.6       nicm      505:        free(mtd->search);
                    506:        free(mtd->filter);
                    507:
1.3       nicm      508:        mtd->dead = 1;
                    509:        mode_tree_remove_ref(mtd);
1.1       nicm      510: }
                    511:
                    512: void
                    513: mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
                    514: {
                    515:        struct screen   *s = &mtd->screen;
                    516:
                    517:        screen_resize(s, sx, sy, 0);
                    518:
                    519:        mode_tree_build(mtd);
                    520:        mode_tree_draw(mtd);
                    521:
                    522:        mtd->wp->flags |= PANE_REDRAW;
                    523: }
                    524:
                    525: struct mode_tree_item *
                    526: mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
                    527:     void *itemdata, uint64_t tag, const char *name, const char *text,
                    528:     int expanded)
                    529: {
                    530:        struct mode_tree_item   *mti, *saved;
                    531:
                    532:        log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
1.46      nicm      533:            name, (text == NULL ? "" : text));
1.1       nicm      534:
                    535:        mti = xcalloc(1, sizeof *mti);
                    536:        mti->parent = parent;
                    537:        mti->itemdata = itemdata;
                    538:
                    539:        mti->tag = tag;
                    540:        mti->name = xstrdup(name);
1.46      nicm      541:        if (text != NULL)
                    542:                mti->text = xstrdup(text);
1.1       nicm      543:
                    544:        saved = mode_tree_find_item(&mtd->saved, tag);
                    545:        if (saved != NULL) {
1.34      nicm      546:                if (parent == NULL || parent->expanded)
1.1       nicm      547:                        mti->tagged = saved->tagged;
                    548:                mti->expanded = saved->expanded;
                    549:        } else if (expanded == -1)
                    550:                mti->expanded = 1;
                    551:        else
                    552:                mti->expanded = expanded;
                    553:
                    554:        TAILQ_INIT(&mti->children);
                    555:
                    556:        if (parent != NULL)
                    557:                TAILQ_INSERT_TAIL(&parent->children, mti, entry);
                    558:        else
                    559:                TAILQ_INSERT_TAIL(&mtd->children, mti, entry);
                    560:
                    561:        return (mti);
                    562: }
                    563:
                    564: void
1.46      nicm      565: mode_tree_draw_as_parent(struct mode_tree_item *mti)
                    566: {
                    567:        mti->draw_as_parent = 1;
                    568: }
                    569:
                    570: void
1.50    ! nicm      571: mode_tree_no_tag(struct mode_tree_item *mti)
        !           572: {
        !           573:        mti->no_tag = 1;
        !           574: }
        !           575:
        !           576: void
1.1       nicm      577: mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
                    578: {
                    579:        struct mode_tree_item   *parent = mti->parent;
                    580:
                    581:        if (parent != NULL)
                    582:                TAILQ_REMOVE(&parent->children, mti, entry);
                    583:        else
                    584:                TAILQ_REMOVE(&mtd->children, mti, entry);
                    585:        mode_tree_free_item(mti);
                    586: }
                    587:
                    588: void
                    589: mode_tree_draw(struct mode_tree_data *mtd)
                    590: {
                    591:        struct window_pane      *wp = mtd->wp;
1.17      nicm      592:        struct screen           *s = &mtd->screen;
1.1       nicm      593:        struct mode_tree_line   *line;
                    594:        struct mode_tree_item   *mti;
                    595:        struct options          *oo = wp->window->options;
                    596:        struct screen_write_ctx  ctx;
                    597:        struct grid_cell         gc0, gc;
1.27      nicm      598:        u_int                    w, h, i, j, sy, box_x, box_y, width;
1.1       nicm      599:        char                    *text, *start, key[7];
                    600:        const char              *tag, *symbol;
1.21      nicm      601:        size_t                   size, n;
1.1       nicm      602:        int                      keylen;
                    603:
                    604:        if (mtd->line_size == 0)
                    605:                return;
                    606:
                    607:        memcpy(&gc0, &grid_default_cell, sizeof gc0);
                    608:        memcpy(&gc, &grid_default_cell, sizeof gc);
1.42      nicm      609:        style_apply(&gc, oo, "mode-style", NULL);
1.1       nicm      610:
                    611:        w = mtd->width;
                    612:        h = mtd->height;
                    613:
1.43      nicm      614:        screen_write_start(&ctx, s);
1.1       nicm      615:        screen_write_clearscreen(&ctx, 8);
                    616:
                    617:        if (mtd->line_size > 10)
                    618:                keylen = 6;
                    619:        else
                    620:                keylen = 4;
                    621:
                    622:        for (i = 0; i < mtd->line_size; i++) {
                    623:                if (i < mtd->offset)
                    624:                        continue;
                    625:                if (i > mtd->offset + h - 1)
                    626:                        break;
                    627:
                    628:                line = &mtd->line_list[i];
                    629:                mti = line->item;
                    630:
1.26      nicm      631:                screen_write_cursormove(&ctx, 0, i - mtd->offset, 0);
1.1       nicm      632:
                    633:                if (i < 10)
1.8       nicm      634:                        snprintf(key, sizeof key, "(%c)  ", '0' + i);
1.1       nicm      635:                else if (i < 36)
                    636:                        snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10));
                    637:                else
                    638:                        *key = '\0';
                    639:
                    640:                if (line->flat)
                    641:                        symbol = "";
                    642:                else if (TAILQ_EMPTY(&mti->children))
                    643:                        symbol = "  ";
                    644:                else if (mti->expanded)
                    645:                        symbol = "- ";
                    646:                else
                    647:                        symbol = "+ ";
                    648:
                    649:                if (line->depth == 0)
                    650:                        start = xstrdup(symbol);
                    651:                else {
                    652:                        size = (4 * line->depth) + 32;
                    653:
                    654:                        start = xcalloc(1, size);
                    655:                        for (j = 1; j < line->depth; j++) {
                    656:                                if (mti->parent != NULL &&
                    657:                                    mtd->line_list[mti->parent->line].last)
                    658:                                        strlcat(start, "    ", size);
                    659:                                else
                    660:                                        strlcat(start, "\001x\001   ", size);
                    661:                        }
                    662:                        if (line->last)
                    663:                                strlcat(start, "\001mq\001> ", size);
                    664:                        else
                    665:                                strlcat(start, "\001tq\001> ", size);
                    666:                        strlcat(start, symbol, size);
                    667:                }
                    668:
                    669:                if (mti->tagged)
                    670:                        tag = "*";
                    671:                else
                    672:                        tag = "";
1.46      nicm      673:                xasprintf(&text, "%-*s%s%s%s%s", keylen, key, start, mti->name,
                    674:                    tag, (mti->text != NULL) ? ": " : "" );
1.27      nicm      675:                width = utf8_cstrwidth(text);
1.37      nicm      676:                if (width > w)
                    677:                        width = w;
1.1       nicm      678:                free(start);
                    679:
                    680:                if (mti->tagged) {
                    681:                        gc.attr ^= GRID_ATTR_BRIGHT;
                    682:                        gc0.attr ^= GRID_ATTR_BRIGHT;
                    683:                }
                    684:
                    685:                if (i != mtd->current) {
                    686:                        screen_write_clearendofline(&ctx, 8);
1.37      nicm      687:                        screen_write_nputs(&ctx, w, &gc0, "%s", text);
1.46      nicm      688:                        if (mti->text != NULL) {
                    689:                                format_draw(&ctx, &gc0, w - width, mti->text,
                    690:                                    NULL);
                    691:                        }
1.13      nicm      692:                } else {
                    693:                        screen_write_clearendofline(&ctx, gc.bg);
1.37      nicm      694:                        screen_write_nputs(&ctx, w, &gc, "%s", text);
1.46      nicm      695:                        if (mti->text != NULL) {
                    696:                                format_draw(&ctx, &gc, w - width, mti->text,
                    697:                                    NULL);
                    698:                        }
1.13      nicm      699:                }
1.1       nicm      700:                free(text);
                    701:
                    702:                if (mti->tagged) {
                    703:                        gc.attr ^= GRID_ATTR_BRIGHT;
                    704:                        gc0.attr ^= GRID_ATTR_BRIGHT;
                    705:                }
                    706:        }
                    707:
                    708:        sy = screen_size_y(s);
1.9       nicm      709:        if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
1.1       nicm      710:                screen_write_stop(&ctx);
                    711:                return;
                    712:        }
                    713:
                    714:        line = &mtd->line_list[mtd->current];
                    715:        mti = line->item;
1.46      nicm      716:        if (mti->draw_as_parent)
                    717:                mti = mti->parent;
1.1       nicm      718:
1.26      nicm      719:        screen_write_cursormove(&ctx, 0, h, 0);
1.1       nicm      720:        screen_write_box(&ctx, w, sy - h);
                    721:
1.46      nicm      722:        if (mtd->sort_list != NULL) {
                    723:                xasprintf(&text, " %s (sort: %s%s)", mti->name,
                    724:                    mtd->sort_list[mtd->sort_crit.field],
                    725:                    mtd->sort_crit.reversed ? ", reversed" : "");
                    726:        } else
                    727:                xasprintf(&text, " %s", mti->name);
1.1       nicm      728:        if (w - 2 >= strlen(text)) {
1.26      nicm      729:                screen_write_cursormove(&ctx, 1, h, 0);
1.1       nicm      730:                screen_write_puts(&ctx, &gc0, "%s", text);
1.21      nicm      731:
                    732:                if (mtd->no_matches)
                    733:                        n = (sizeof "no matches") - 1;
                    734:                else
                    735:                        n = (sizeof "active") - 1;
                    736:                if (mtd->filter != NULL && w - 2 >= strlen(text) + 10 + n + 2) {
                    737:                        screen_write_puts(&ctx, &gc0, " (filter: ");
                    738:                        if (mtd->no_matches)
                    739:                                screen_write_puts(&ctx, &gc, "no matches");
                    740:                        else
                    741:                                screen_write_puts(&ctx, &gc0, "active");
                    742:                        screen_write_puts(&ctx, &gc0, ") ");
1.46      nicm      743:                } else
                    744:                        screen_write_puts(&ctx, &gc0, " ");
1.1       nicm      745:        }
                    746:        free(text);
                    747:
                    748:        box_x = w - 4;
                    749:        box_y = sy - h - 2;
                    750:
1.17      nicm      751:        if (box_x != 0 && box_y != 0) {
1.26      nicm      752:                screen_write_cursormove(&ctx, 2, h + 1, 0);
1.17      nicm      753:                mtd->drawcb(mtd->modedata, mti->itemdata, &ctx, box_x, box_y);
1.1       nicm      754:        }
                    755:
                    756:        screen_write_stop(&ctx);
                    757: }
                    758:
1.3       nicm      759: static struct mode_tree_item *
                    760: mode_tree_search_for(struct mode_tree_data *mtd)
                    761: {
                    762:        struct mode_tree_item   *mti, *last, *next;
                    763:
1.6       nicm      764:        if (mtd->search == NULL)
1.3       nicm      765:                return (NULL);
                    766:
                    767:        mti = last = mtd->line_list[mtd->current].item;
                    768:        for (;;) {
                    769:                if (!TAILQ_EMPTY(&mti->children))
                    770:                        mti = TAILQ_FIRST(&mti->children);
                    771:                else if ((next = TAILQ_NEXT(mti, entry)) != NULL)
                    772:                        mti = next;
                    773:                else {
                    774:                        for (;;) {
                    775:                                mti = mti->parent;
                    776:                                if (mti == NULL)
                    777:                                        break;
                    778:                                if ((next = TAILQ_NEXT(mti, entry)) != NULL) {
                    779:                                        mti = next;
                    780:                                        break;
                    781:                                }
                    782:                        }
                    783:                }
                    784:                if (mti == NULL)
                    785:                        mti = TAILQ_FIRST(&mtd->children);
                    786:                if (mti == last)
                    787:                        break;
                    788:
                    789:                if (mtd->searchcb == NULL) {
1.6       nicm      790:                        if (strstr(mti->name, mtd->search) != NULL)
1.3       nicm      791:                                return (mti);
                    792:                        continue;
                    793:                }
1.6       nicm      794:                if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
1.4       nicm      795:                        return (mti);
1.3       nicm      796:        }
                    797:        return (NULL);
                    798: }
                    799:
                    800: static void
                    801: mode_tree_search_set(struct mode_tree_data *mtd)
                    802: {
                    803:        struct mode_tree_item   *mti, *loop;
                    804:        uint64_t                 tag;
                    805:
                    806:        mti = mode_tree_search_for(mtd);
                    807:        if (mti == NULL)
                    808:                return;
                    809:        tag = mti->tag;
                    810:
                    811:        loop = mti->parent;
                    812:        while (loop != NULL) {
                    813:                loop->expanded = 1;
                    814:                loop = loop->parent;
                    815:        }
1.6       nicm      816:
1.3       nicm      817:        mode_tree_build(mtd);
                    818:        mode_tree_set_current(mtd, tag);
                    819:        mode_tree_draw(mtd);
1.6       nicm      820:        mtd->wp->flags |= PANE_REDRAW;
1.3       nicm      821: }
                    822:
                    823: static int
                    824: mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
                    825:     __unused int done)
                    826: {
                    827:        struct mode_tree_data   *mtd = data;
                    828:
                    829:        if (mtd->dead)
                    830:                return (0);
                    831:
1.6       nicm      832:        free(mtd->search);
                    833:        if (s == NULL || *s == '\0') {
                    834:                mtd->search = NULL;
1.3       nicm      835:                return (0);
                    836:        }
1.6       nicm      837:        mtd->search = xstrdup(s);
1.3       nicm      838:        mode_tree_search_set(mtd);
                    839:
                    840:        return (0);
                    841: }
                    842:
                    843: static void
                    844: mode_tree_search_free(void *data)
                    845: {
                    846:        mode_tree_remove_ref(data);
                    847: }
                    848:
1.6       nicm      849: static int
                    850: mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
                    851:     __unused int done)
                    852: {
                    853:        struct mode_tree_data   *mtd = data;
                    854:
                    855:        if (mtd->dead)
                    856:                return (0);
                    857:
                    858:        if (mtd->filter != NULL)
                    859:                free(mtd->filter);
                    860:        if (s == NULL || *s == '\0')
                    861:                mtd->filter = NULL;
                    862:        else
                    863:                mtd->filter = xstrdup(s);
                    864:
                    865:        mode_tree_build(mtd);
                    866:        mode_tree_draw(mtd);
                    867:        mtd->wp->flags |= PANE_REDRAW;
                    868:
                    869:        return (0);
                    870: }
                    871:
                    872: static void
                    873: mode_tree_filter_free(void *data)
                    874: {
                    875:        mode_tree_remove_ref(data);
                    876: }
                    877:
1.28      nicm      878: static void
                    879: mode_tree_menu_callback(__unused struct menu *menu, __unused u_int idx,
                    880:     key_code key, void *data)
                    881: {
                    882:        struct mode_tree_menu           *mtm = data;
                    883:        struct mode_tree_data           *mtd = mtm->data;
                    884:        struct mode_tree_item           *mti;
                    885:
                    886:        if (mtd->dead || key == KEYC_NONE)
                    887:                goto out;
                    888:
                    889:        if (mtm->line >= mtd->line_size)
                    890:                goto out;
                    891:        mti = mtd->line_list[mtm->line].item;
                    892:        if (mti->itemdata != mtm->itemdata)
                    893:                goto out;
                    894:        mtd->current = mtm->line;
                    895:        mtd->menucb (mtd->modedata, mtm->c, key);
                    896:
                    897: out:
                    898:        mode_tree_remove_ref(mtd);
                    899:        free(mtm);
                    900: }
                    901:
                    902: static void
                    903: mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
                    904:     u_int y, int outside)
                    905: {
                    906:        struct mode_tree_item   *mti;
                    907:        struct menu             *menu;
1.33      nicm      908:        const struct menu_item  *items;
1.28      nicm      909:        struct mode_tree_menu   *mtm;
                    910:        char                    *title;
                    911:        u_int                    line;
                    912:
                    913:        if (mtd->offset + y > mtd->line_size - 1)
                    914:                line = mtd->current;
                    915:        else
                    916:                line = mtd->offset + y;
1.29      nicm      917:        mti = mtd->line_list[line].item;
1.28      nicm      918:
                    919:        if (!outside) {
1.33      nicm      920:                items = mtd->menu;
1.28      nicm      921:                xasprintf(&title, "#[align=centre]%s", mti->name);
                    922:        } else {
1.33      nicm      923:                items = mode_tree_menu_items;
1.28      nicm      924:                title = xstrdup("");
                    925:        }
1.33      nicm      926:        menu = menu_create(title);
                    927:        menu_add_items(menu, items, NULL, NULL, NULL);
1.28      nicm      928:        free(title);
                    929:
                    930:        mtm = xmalloc(sizeof *mtm);
                    931:        mtm->data = mtd;
                    932:        mtm->c = c;
                    933:        mtm->line = line;
                    934:        mtm->itemdata = mti->itemdata;
                    935:        mtd->references++;
                    936:
1.38      nicm      937:        if (x >= (menu->width + 4) / 2)
                    938:                x -= (menu->width + 4) / 2;
                    939:        else
                    940:                x = 0;
1.28      nicm      941:        if (menu_display(menu, 0, NULL, x, y, c, NULL, mode_tree_menu_callback,
                    942:            mtm) != 0)
                    943:                menu_free(menu);
                    944: }
                    945:
1.1       nicm      946: int
1.3       nicm      947: mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
1.19      nicm      948:     struct mouse_event *m, u_int *xp, u_int *yp)
1.1       nicm      949: {
                    950:        struct mode_tree_line   *line;
1.44      nicm      951:        struct mode_tree_item   *current, *parent, *mti;
1.1       nicm      952:        u_int                    i, x, y;
                    953:        int                      choice;
                    954:        key_code                 tmp;
                    955:
1.28      nicm      956:        if (KEYC_IS_MOUSE(*key) && m != NULL) {
1.1       nicm      957:                if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
                    958:                        *key = KEYC_NONE;
                    959:                        return (0);
                    960:                }
1.19      nicm      961:                if (xp != NULL)
                    962:                        *xp = x;
                    963:                if (yp != NULL)
                    964:                        *yp = y;
1.1       nicm      965:                if (x > mtd->width || y > mtd->height) {
1.28      nicm      966:                        if (*key == KEYC_MOUSEDOWN3_PANE)
                    967:                                mode_tree_display_menu(mtd, c, x, y, 1);
1.20      nicm      968:                        if (!mtd->preview)
1.19      nicm      969:                                *key = KEYC_NONE;
1.1       nicm      970:                        return (0);
                    971:                }
                    972:                if (mtd->offset + y < mtd->line_size) {
1.18      nicm      973:                        if (*key == KEYC_MOUSEDOWN1_PANE ||
1.28      nicm      974:                            *key == KEYC_MOUSEDOWN3_PANE ||
1.18      nicm      975:                            *key == KEYC_DOUBLECLICK1_PANE)
                    976:                                mtd->current = mtd->offset + y;
                    977:                        if (*key == KEYC_DOUBLECLICK1_PANE)
                    978:                                *key = '\r';
1.28      nicm      979:                        else {
                    980:                                if (*key == KEYC_MOUSEDOWN3_PANE)
                    981:                                        mode_tree_display_menu(mtd, c, x, y, 0);
1.20      nicm      982:                                *key = KEYC_NONE;
1.28      nicm      983:                        }
                    984:                } else {
                    985:                        if (*key == KEYC_MOUSEDOWN3_PANE)
                    986:                                mode_tree_display_menu(mtd, c, x, y, 0);
1.20      nicm      987:                        *key = KEYC_NONE;
1.28      nicm      988:                }
1.20      nicm      989:                return (0);
1.1       nicm      990:        }
                    991:
                    992:        line = &mtd->line_list[mtd->current];
                    993:        current = line->item;
                    994:
                    995:        choice = -1;
                    996:        if (*key >= '0' && *key <= '9')
                    997:                choice = (*key) - '0';
1.49      nicm      998:        else if (((*key) & KEYC_MASK_MODIFIERS) == KEYC_META) {
1.1       nicm      999:                tmp = (*key) & KEYC_MASK_KEY;
                   1000:                if (tmp >= 'a' && tmp <= 'z')
                   1001:                        choice = 10 + (tmp - 'a');
                   1002:        }
                   1003:        if (choice != -1) {
                   1004:                if ((u_int)choice > mtd->line_size - 1) {
                   1005:                        *key = KEYC_NONE;
                   1006:                        return (0);
                   1007:                }
                   1008:                mtd->current = choice;
                   1009:                *key = '\r';
                   1010:                return (0);
                   1011:        }
                   1012:
                   1013:        switch (*key) {
                   1014:        case 'q':
                   1015:        case '\033': /* Escape */
1.22      nicm     1016:        case '\007': /* C-g */
1.1       nicm     1017:                return (1);
                   1018:        case KEYC_UP:
                   1019:        case 'k':
                   1020:        case KEYC_WHEELUP_PANE:
1.12      nicm     1021:        case '\020': /* C-p */
1.1       nicm     1022:                mode_tree_up(mtd, 1);
                   1023:                break;
                   1024:        case KEYC_DOWN:
                   1025:        case 'j':
                   1026:        case KEYC_WHEELDOWN_PANE:
1.12      nicm     1027:        case '\016': /* C-n */
1.1       nicm     1028:                mode_tree_down(mtd, 1);
                   1029:                break;
1.35      nicm     1030:        case 'g':
1.1       nicm     1031:        case KEYC_PPAGE:
                   1032:        case '\002': /* C-b */
                   1033:                for (i = 0; i < mtd->height; i++) {
                   1034:                        if (mtd->current == 0)
                   1035:                                break;
                   1036:                        mode_tree_up(mtd, 1);
                   1037:                }
                   1038:                break;
1.35      nicm     1039:        case 'G':
1.1       nicm     1040:        case KEYC_NPAGE:
                   1041:        case '\006': /* C-f */
                   1042:                for (i = 0; i < mtd->height; i++) {
                   1043:                        if (mtd->current == mtd->line_size - 1)
                   1044:                                break;
                   1045:                        mode_tree_down(mtd, 1);
                   1046:                }
                   1047:                break;
                   1048:        case KEYC_HOME:
                   1049:                mtd->current = 0;
                   1050:                mtd->offset = 0;
                   1051:                break;
                   1052:        case KEYC_END:
                   1053:                mtd->current = mtd->line_size - 1;
                   1054:                if (mtd->current > mtd->height - 1)
1.11      nicm     1055:                        mtd->offset = mtd->current - mtd->height + 1;
1.1       nicm     1056:                else
                   1057:                        mtd->offset = 0;
                   1058:                break;
                   1059:        case 't':
                   1060:                /*
                   1061:                 * Do not allow parents and children to both be tagged: untag
                   1062:                 * all parents and children of current.
                   1063:                 */
1.50    ! nicm     1064:                if (current->no_tag)
        !          1065:                        break;
1.1       nicm     1066:                if (!current->tagged) {
                   1067:                        parent = current->parent;
                   1068:                        while (parent != NULL) {
                   1069:                                parent->tagged = 0;
                   1070:                                parent = parent->parent;
                   1071:                        }
                   1072:                        mode_tree_clear_tagged(&current->children);
                   1073:                        current->tagged = 1;
                   1074:                } else
                   1075:                        current->tagged = 0;
1.28      nicm     1076:                if (m != NULL)
                   1077:                        mode_tree_down(mtd, 0);
1.1       nicm     1078:                break;
                   1079:        case 'T':
                   1080:                for (i = 0; i < mtd->line_size; i++)
                   1081:                        mtd->line_list[i].item->tagged = 0;
                   1082:                break;
                   1083:        case '\024': /* C-t */
                   1084:                for (i = 0; i < mtd->line_size; i++) {
1.50    ! nicm     1085:                        if ((mtd->line_list[i].item->parent == NULL &&
        !          1086:                            !mtd->line_list[i].item->no_tag) ||
        !          1087:                            (mtd->line_list[i].item->parent != NULL &&
        !          1088:                            mtd->line_list[i].item->parent->no_tag))
1.1       nicm     1089:                                mtd->line_list[i].item->tagged = 1;
                   1090:                        else
                   1091:                                mtd->line_list[i].item->tagged = 0;
                   1092:                }
                   1093:                break;
                   1094:        case 'O':
1.36      nicm     1095:                mtd->sort_crit.field++;
1.46      nicm     1096:                if (mtd->sort_crit.field >= mtd->sort_size)
1.36      nicm     1097:                        mtd->sort_crit.field = 0;
                   1098:                mode_tree_build(mtd);
                   1099:                break;
                   1100:        case 'r':
                   1101:                mtd->sort_crit.reversed = !mtd->sort_crit.reversed;
1.1       nicm     1102:                mode_tree_build(mtd);
                   1103:                break;
                   1104:        case KEYC_LEFT:
1.15      nicm     1105:        case 'h':
1.1       nicm     1106:        case '-':
                   1107:                if (line->flat || !current->expanded)
                   1108:                        current = current->parent;
                   1109:                if (current == NULL)
                   1110:                        mode_tree_up(mtd, 0);
                   1111:                else {
                   1112:                        current->expanded = 0;
                   1113:                        mtd->current = current->line;
                   1114:                        mode_tree_build(mtd);
                   1115:                }
                   1116:                break;
                   1117:        case KEYC_RIGHT:
1.15      nicm     1118:        case 'l':
1.1       nicm     1119:        case '+':
                   1120:                if (line->flat || current->expanded)
                   1121:                        mode_tree_down(mtd, 0);
                   1122:                else if (!line->flat) {
                   1123:                        current->expanded = 1;
                   1124:                        mode_tree_build(mtd);
                   1125:                }
1.44      nicm     1126:                break;
1.48      nicm     1127:        case '-'|KEYC_META:
1.44      nicm     1128:                TAILQ_FOREACH(mti, &mtd->children, entry)
                   1129:                        mti->expanded = 0;
                   1130:                mode_tree_build(mtd);
                   1131:                break;
1.48      nicm     1132:        case '+'|KEYC_META:
1.44      nicm     1133:                TAILQ_FOREACH(mti, &mtd->children, entry)
                   1134:                        mti->expanded = 1;
                   1135:                mode_tree_build(mtd);
1.3       nicm     1136:                break;
1.35      nicm     1137:        case '?':
                   1138:        case '/':
1.3       nicm     1139:        case '\023': /* C-s */
                   1140:                mtd->references++;
1.47      nicm     1141:                status_prompt_set(c, NULL, "(search) ", "",
1.3       nicm     1142:                    mode_tree_search_callback, mode_tree_search_free, mtd,
                   1143:                    PROMPT_NOFORMAT);
                   1144:                break;
                   1145:        case 'n':
                   1146:                mode_tree_search_set(mtd);
1.6       nicm     1147:                break;
                   1148:        case 'f':
                   1149:                mtd->references++;
1.47      nicm     1150:                status_prompt_set(c, NULL, "(filter) ", mtd->filter,
1.6       nicm     1151:                    mode_tree_filter_callback, mode_tree_filter_free, mtd,
                   1152:                    PROMPT_NOFORMAT);
1.9       nicm     1153:                break;
                   1154:        case 'v':
                   1155:                mtd->preview = !mtd->preview;
                   1156:                mode_tree_build(mtd);
1.11      nicm     1157:                if (mtd->preview)
                   1158:                        mode_tree_check_selected(mtd);
1.1       nicm     1159:                break;
                   1160:        }
                   1161:        return (0);
                   1162: }
                   1163:
                   1164: void
                   1165: mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
                   1166:     const char *template, const char *name)
                   1167: {
1.40      nicm     1168:        struct cmdq_state       *state;
                   1169:        char                    *command, *error;
                   1170:        enum cmd_parse_status    status;
1.1       nicm     1171:
                   1172:        command = cmd_template_replace(template, name, 1);
1.40      nicm     1173:        if (command != NULL && *command != '\0') {
                   1174:                state = cmdq_new_state(fs, NULL, 0);
                   1175:                status = cmd_parse_and_append(command, NULL, c, state, &error);
                   1176:                if (status == CMD_PARSE_ERROR) {
                   1177:                        if (c != NULL) {
                   1178:                                *error = toupper((u_char)*error);
1.45      nicm     1179:                                status_message_set(c, 1, "%s", error);
1.40      nicm     1180:                        }
                   1181:                        free(error);
1.1       nicm     1182:                }
1.40      nicm     1183:                cmdq_free_state(state);
1.1       nicm     1184:        }
                   1185:        free(command);
                   1186: }