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

Annotation of src/usr.bin/tmux/cmd-queue.c, Revision 1.103

1.103   ! nicm        1: /* $OpenBSD: cmd-queue.c,v 1.102 2021/04/12 09:36:12 nicm Exp $ */
1.1       nicm        2:
                      3: /*
1.34      nicm        4:  * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1       nicm        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 <stdlib.h>
1.23      nicm       23: #include <string.h>
1.7       nicm       24: #include <time.h>
1.1       nicm       25:
                     26: #include "tmux.h"
                     27:
1.84      nicm       28: /* Command queue flags. */
                     29: #define CMDQ_FIRED 0x1
                     30: #define CMDQ_WAITING 0x2
                     31:
1.83      nicm       32: /* Command queue item type. */
                     33: enum cmdq_type {
                     34:        CMDQ_COMMAND,
                     35:        CMDQ_CALLBACK,
                     36: };
                     37:
                     38: /* Command queue item. */
                     39: struct cmdq_item {
                     40:        char                    *name;
                     41:        struct cmdq_list        *queue;
                     42:        struct cmdq_item        *next;
                     43:
                     44:        struct client           *client;
1.89      nicm       45:        struct client           *target_client;
1.83      nicm       46:
                     47:        enum cmdq_type           type;
                     48:        u_int                    group;
                     49:
                     50:        u_int                    number;
                     51:        time_t                   time;
                     52:
                     53:        int                      flags;
                     54:
1.86      nicm       55:        struct cmdq_state       *state;
1.83      nicm       56:        struct cmd_find_state    source;
                     57:        struct cmd_find_state    target;
                     58:
                     59:        struct cmd_list         *cmdlist;
                     60:        struct cmd              *cmd;
                     61:
                     62:        cmdq_cb                  cb;
                     63:        void                    *data;
                     64:
                     65:        TAILQ_ENTRY(cmdq_item)   entry;
                     66: };
1.90      nicm       67: TAILQ_HEAD(cmdq_item_list, cmdq_item);
1.24      nicm       68:
1.87      nicm       69: /*
                     70:  * Command queue state. This is the context for commands on the command queue.
                     71:  * It holds information about how the commands were fired (the key and flags),
                     72:  * any additional formats for the commands, and the current default target.
                     73:  * Multiple commands can share the same state and a command may update the
                     74:  * default target.
                     75:  */
                     76: struct cmdq_state {
                     77:        int                      references;
                     78:        int                      flags;
                     79:
                     80:        struct format_tree      *formats;
                     81:
                     82:        struct key_event         event;
                     83:        struct cmd_find_state    current;
                     84: };
                     85:
1.90      nicm       86: /* Command queue. */
                     87: struct cmdq_list {
                     88:        struct cmdq_item        *item;
                     89:        struct cmdq_item_list    list;
                     90: };
                     91:
1.43      nicm       92: /* Get command queue name. */
                     93: static const char *
                     94: cmdq_name(struct client *c)
                     95: {
1.70      nicm       96:        static char     s[256];
1.43      nicm       97:
                     98:        if (c == NULL)
                     99:                return ("<global>");
1.71      nicm      100:        if (c->name != NULL)
                    101:                xsnprintf(s, sizeof s, "<%s>", c->name);
                    102:        else
                    103:                xsnprintf(s, sizeof s, "<%p>", c);
1.43      nicm      104:        return (s);
                    105: }
                    106:
                    107: /* Get command queue from client. */
1.44      nicm      108: static struct cmdq_list *
1.43      nicm      109: cmdq_get(struct client *c)
                    110: {
1.83      nicm      111:        static struct cmdq_list *global_queue;
                    112:
                    113:        if (c == NULL) {
                    114:                if (global_queue == NULL)
                    115:                        global_queue = cmdq_new();
                    116:                return (global_queue);
                    117:        }
                    118:        return (c->queue);
                    119: }
                    120:
                    121: /* Create a queue. */
                    122: struct cmdq_list *
                    123: cmdq_new(void)
                    124: {
                    125:        struct cmdq_list        *queue;
                    126:
                    127:        queue = xcalloc (1, sizeof *queue);
1.90      nicm      128:        TAILQ_INIT (&queue->list);
1.83      nicm      129:        return (queue);
                    130: }
                    131:
                    132: /* Free a queue. */
                    133: void
                    134: cmdq_free(struct cmdq_list *queue)
                    135: {
1.90      nicm      136:        if (!TAILQ_EMPTY(&queue->list))
1.83      nicm      137:                fatalx("queue not empty");
                    138:        free(queue);
                    139: }
                    140:
                    141: /* Get item name. */
                    142: const char *
                    143: cmdq_get_name(struct cmdq_item *item)
                    144: {
                    145:        return (item->name);
                    146: }
                    147:
                    148: /* Get item client. */
                    149: struct client *
                    150: cmdq_get_client(struct cmdq_item *item)
                    151: {
                    152:        return (item->client);
                    153: }
                    154:
1.89      nicm      155: /* Get item target client. */
                    156: struct client *
                    157: cmdq_get_target_client(struct cmdq_item *item)
                    158: {
                    159:        return (item->target_client);
                    160: }
                    161:
1.88      nicm      162: /* Get item state. */
                    163: struct cmdq_state *
                    164: cmdq_get_state(struct cmdq_item *item)
                    165: {
                    166:        return (item->state);
                    167: }
                    168:
1.83      nicm      169: /* Get item target. */
                    170: struct cmd_find_state *
                    171: cmdq_get_target(struct cmdq_item *item)
                    172: {
                    173:        return (&item->target);
                    174: }
                    175:
                    176: /* Get item source. */
                    177: struct cmd_find_state *
                    178: cmdq_get_source(struct cmdq_item *item)
                    179: {
                    180:        return (&item->source);
                    181: }
                    182:
1.87      nicm      183: /* Get state event. */
                    184: struct key_event *
                    185: cmdq_get_event(struct cmdq_item *item)
                    186: {
                    187:        return (&item->state->event);
                    188: }
                    189:
                    190: /* Get state current target. */
                    191: struct cmd_find_state *
                    192: cmdq_get_current(struct cmdq_item *item)
                    193: {
                    194:        return (&item->state->current);
                    195: }
                    196:
                    197: /* Get state flags. */
                    198: int
                    199: cmdq_get_flags(struct cmdq_item *item)
1.83      nicm      200: {
1.87      nicm      201:        return (item->state->flags);
1.83      nicm      202: }
                    203:
1.88      nicm      204: /* Create a new state. */
                    205: struct cmdq_state *
                    206: cmdq_new_state(struct cmd_find_state *current, struct key_event *event,
                    207:     int flags)
                    208: {
                    209:        struct cmdq_state       *state;
                    210:
                    211:        state = xcalloc(1, sizeof *state);
                    212:        state->references = 1;
                    213:        state->flags = flags;
                    214:
                    215:        if (event != NULL)
                    216:                memcpy(&state->event, event, sizeof state->event);
                    217:        else
                    218:                state->event.key = KEYC_NONE;
                    219:        if (current != NULL && cmd_find_valid_state(current))
                    220:                cmd_find_copy_state(&state->current, current);
                    221:        else
                    222:                cmd_find_clear_state(&state->current, 0);
                    223:
                    224:        return (state);
                    225: }
                    226:
                    227: /* Add a reference to a state. */
                    228: struct cmdq_state *
                    229: cmdq_link_state(struct cmdq_state *state)
                    230: {
                    231:        state->references++;
                    232:        return (state);
                    233: }
                    234:
                    235: /* Make a copy of a state. */
                    236: struct cmdq_state *
                    237: cmdq_copy_state(struct cmdq_state *state)
                    238: {
                    239:        return (cmdq_new_state(&state->current, &state->event, state->flags));
                    240: }
                    241:
                    242: /* Free a state. */
                    243: void
                    244: cmdq_free_state(struct cmdq_state *state)
                    245: {
1.91      nicm      246:        if (--state->references != 0)
                    247:                return;
                    248:
                    249:        if (state->formats != NULL)
                    250:                format_free(state->formats);
                    251:        free(state);
1.88      nicm      252: }
                    253:
                    254: /* Add a format to command queue. */
                    255: void
                    256: cmdq_add_format(struct cmdq_state *state, const char *key, const char *fmt, ...)
                    257: {
                    258:        va_list  ap;
                    259:        char    *value;
                    260:
                    261:        va_start(ap, fmt);
                    262:        xvasprintf(&value, fmt, ap);
                    263:        va_end(ap);
                    264:
                    265:        if (state->formats == NULL)
                    266:                state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
                    267:        format_add(state->formats, key, "%s", value);
                    268:
                    269:        free(value);
1.103   ! nicm      270: }
        !           271:
        !           272: /* Add formats to command queue. */
        !           273: void
        !           274: cmdq_add_formats(struct cmdq_state *state, struct format_tree *ft)
        !           275: {
        !           276:        if (state->formats == NULL)
        !           277:                state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
        !           278:        format_merge(state->formats, ft);
1.88      nicm      279: }
                    280:
1.83      nicm      281: /* Merge formats from item. */
                    282: void
                    283: cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft)
                    284: {
                    285:        const struct cmd_entry  *entry;
                    286:
                    287:        if (item->cmd != NULL) {
1.101     nicm      288:                entry = cmd_get_entry(item->cmd);
1.83      nicm      289:                format_add(ft, "command", "%s", entry->name);
                    290:        }
1.86      nicm      291:        if (item->state->formats != NULL)
                    292:                format_merge(ft, item->state->formats);
1.43      nicm      293: }
                    294:
                    295: /* Append an item. */
1.78      nicm      296: struct cmdq_item *
1.44      nicm      297: cmdq_append(struct client *c, struct cmdq_item *item)
1.43      nicm      298: {
1.44      nicm      299:        struct cmdq_list        *queue = cmdq_get(c);
                    300:        struct cmdq_item        *next;
1.43      nicm      301:
                    302:        do {
1.44      nicm      303:                next = item->next;
                    304:                item->next = NULL;
1.43      nicm      305:
                    306:                if (c != NULL)
                    307:                        c->references++;
1.44      nicm      308:                item->client = c;
1.43      nicm      309:
1.44      nicm      310:                item->queue = queue;
1.90      nicm      311:                TAILQ_INSERT_TAIL(&queue->list, item, entry);
1.64      nicm      312:                log_debug("%s %s: %s", __func__, cmdq_name(c), item->name);
1.43      nicm      313:
1.44      nicm      314:                item = next;
                    315:        } while (item != NULL);
1.90      nicm      316:        return (TAILQ_LAST(&queue->list, cmdq_item_list));
1.43      nicm      317: }
                    318:
                    319: /* Insert an item. */
1.78      nicm      320: struct cmdq_item *
1.44      nicm      321: cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
1.43      nicm      322: {
                    323:        struct client           *c = after->client;
1.44      nicm      324:        struct cmdq_list        *queue = after->queue;
                    325:        struct cmdq_item        *next;
1.43      nicm      326:
                    327:        do {
1.44      nicm      328:                next = item->next;
1.64      nicm      329:                item->next = after->next;
                    330:                after->next = item;
1.43      nicm      331:
                    332:                if (c != NULL)
                    333:                        c->references++;
1.44      nicm      334:                item->client = c;
1.43      nicm      335:
1.44      nicm      336:                item->queue = queue;
1.90      nicm      337:                TAILQ_INSERT_AFTER(&queue->list, after, item, entry);
1.64      nicm      338:                log_debug("%s %s: %s after %s", __func__, cmdq_name(c),
                    339:                    item->name, after->name);
1.43      nicm      340:
1.65      nicm      341:                after = item;
1.44      nicm      342:                item = next;
                    343:        } while (item != NULL);
1.78      nicm      344:        return (after);
1.43      nicm      345: }
1.63      nicm      346:
                    347: /* Insert a hook. */
                    348: void
                    349: cmdq_insert_hook(struct session *s, struct cmdq_item *item,
1.88      nicm      350:     struct cmd_find_state *current, const char *fmt, ...)
1.63      nicm      351: {
1.88      nicm      352:        struct cmdq_state               *state = item->state;
1.92      nicm      353:        struct cmd                      *cmd = item->cmd;
                    354:        struct args                     *args = cmd_get_args(cmd);
                    355:        struct args_entry               *entryp;
                    356:        struct args_value               *valuep;
1.63      nicm      357:        struct options                  *oo;
                    358:        va_list                          ap;
1.92      nicm      359:        char                            *name, tmp[32], flag, *arguments;
                    360:        int                              i;
                    361:        const char                      *value;
1.63      nicm      362:        struct cmdq_item                *new_item;
1.88      nicm      363:        struct cmdq_state               *new_state;
1.63      nicm      364:        struct options_entry            *o;
                    365:        struct options_array_item       *a;
                    366:        struct cmd_list                 *cmdlist;
                    367:
1.86      nicm      368:        if (item->state->flags & CMDQ_STATE_NOHOOKS)
1.63      nicm      369:                return;
                    370:        if (s == NULL)
                    371:                oo = global_s_options;
                    372:        else
                    373:                oo = s->options;
                    374:
                    375:        va_start(ap, fmt);
                    376:        xvasprintf(&name, fmt, ap);
                    377:        va_end(ap);
                    378:
                    379:        o = options_get(oo, name);
                    380:        if (o == NULL) {
                    381:                free(name);
                    382:                return;
                    383:        }
                    384:        log_debug("running hook %s (parent %p)", name, item);
                    385:
1.88      nicm      386:        /*
                    387:         * The hooks get a new state because they should not update the current
                    388:         * target or formats for any subsequent commands.
                    389:         */
                    390:        new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS);
                    391:        cmdq_add_format(new_state, "hook", "%s", name);
1.92      nicm      392:
                    393:        arguments = args_print(args);
                    394:        cmdq_add_format(new_state, "hook_arguments", "%s", arguments);
                    395:        free(arguments);
                    396:
                    397:        for (i = 0; i < args->argc; i++) {
                    398:                xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i);
                    399:                cmdq_add_format(new_state, tmp, "%s", args->argv[i]);
                    400:        }
                    401:        flag = args_first(args, &entryp);
                    402:        while (flag != 0) {
                    403:                value = args_get(args, flag);
                    404:                if (value == NULL) {
                    405:                        xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
                    406:                        cmdq_add_format(new_state, tmp, "1");
                    407:                } else {
                    408:                        xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
                    409:                        cmdq_add_format(new_state, tmp, "%s", value);
                    410:                }
                    411:
                    412:                i = 0;
                    413:                value = args_first_value(args, flag, &valuep);
                    414:                while (value != NULL) {
                    415:                        xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i);
                    416:                        cmdq_add_format(new_state, tmp, "%s", value);
                    417:                        i++;
                    418:                        value = args_next_value(&valuep);
                    419:                }
                    420:
                    421:                flag = args_next(&entryp);
                    422:        }
1.88      nicm      423:
1.63      nicm      424:        a = options_array_first(o);
                    425:        while (a != NULL) {
                    426:                cmdlist = options_array_item_value(a)->cmdlist;
1.88      nicm      427:                if (cmdlist != NULL) {
                    428:                        new_item = cmdq_get_command(cmdlist, new_state);
                    429:                        if (item != NULL)
                    430:                                item = cmdq_insert_after(item, new_item);
                    431:                        else
                    432:                                item = cmdq_append(NULL, new_item);
1.63      nicm      433:                }
                    434:                a = options_array_next(a);
                    435:        }
                    436:
1.88      nicm      437:        cmdq_free_state(new_state);
1.63      nicm      438:        free(name);
1.74      nicm      439: }
                    440:
                    441: /* Continue processing command queue. */
                    442: void
                    443: cmdq_continue(struct cmdq_item *item)
                    444: {
                    445:        item->flags &= ~CMDQ_WAITING;
1.63      nicm      446: }
                    447:
1.43      nicm      448: /* Remove an item. */
                    449: static void
1.44      nicm      450: cmdq_remove(struct cmdq_item *item)
1.43      nicm      451: {
1.44      nicm      452:        if (item->client != NULL)
                    453:                server_client_unref(item->client);
1.62      nicm      454:        if (item->cmdlist != NULL)
1.44      nicm      455:                cmd_list_free(item->cmdlist);
1.88      nicm      456:        cmdq_free_state(item->state);
1.43      nicm      457:
1.90      nicm      458:        TAILQ_REMOVE(&item->queue->list, item, entry);
1.46      nicm      459:
1.64      nicm      460:        free(item->name);
1.44      nicm      461:        free(item);
1.43      nicm      462: }
                    463:
                    464: /* Remove all subsequent items that match this item's group. */
                    465: static void
1.44      nicm      466: cmdq_remove_group(struct cmdq_item *item)
1.43      nicm      467: {
1.44      nicm      468:        struct cmdq_item        *this, *next;
1.43      nicm      469:
1.69      nicm      470:        if (item->group == 0)
                    471:                return;
1.44      nicm      472:        this = TAILQ_NEXT(item, entry);
1.43      nicm      473:        while (this != NULL) {
                    474:                next = TAILQ_NEXT(this, entry);
1.44      nicm      475:                if (this->group == item->group)
1.43      nicm      476:                        cmdq_remove(this);
                    477:                this = next;
                    478:        }
                    479: }
                    480:
                    481: /* Get a command for the command queue. */
1.44      nicm      482: struct cmdq_item *
1.88      nicm      483: cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state)
1.43      nicm      484: {
1.44      nicm      485:        struct cmdq_item        *item, *first = NULL, *last = NULL;
                    486:        struct cmd              *cmd;
1.82      nicm      487:        const struct cmd_entry  *entry;
1.88      nicm      488:        int                      created = 0;
1.49      nicm      489:
1.88      nicm      490:        if (state == NULL) {
                    491:                state = cmdq_new_state(NULL, NULL, 0);
                    492:                created = 1;
                    493:        }
                    494:
                    495:        cmd = cmd_list_first(cmdlist);
1.82      nicm      496:        while (cmd != NULL) {
                    497:                entry = cmd_get_entry(cmd);
1.43      nicm      498:
1.44      nicm      499:                item = xcalloc(1, sizeof *item);
1.82      nicm      500:                xasprintf(&item->name, "[%s/%p]", entry->name, item);
1.44      nicm      501:                item->type = CMDQ_COMMAND;
1.43      nicm      502:
1.88      nicm      503:                item->group = cmd_get_group(cmd);
                    504:                item->state = cmdq_link_state(state);
                    505:
1.44      nicm      506:                item->cmdlist = cmdlist;
                    507:                item->cmd = cmd;
1.68      nicm      508:
1.88      nicm      509:                cmdlist->references++;
1.68      nicm      510:                log_debug("%s: %s group %u", __func__, item->name, item->group);
1.43      nicm      511:
                    512:                if (first == NULL)
1.44      nicm      513:                        first = item;
1.43      nicm      514:                if (last != NULL)
1.44      nicm      515:                        last->next = item;
                    516:                last = item;
1.82      nicm      517:
1.88      nicm      518:                cmd = cmd_list_next(cmd);
1.43      nicm      519:        }
1.88      nicm      520:
                    521:        if (created)
                    522:                cmdq_free_state(state);
1.43      nicm      523:        return (first);
                    524: }
                    525:
1.54      nicm      526: /* Fill in flag for a command. */
                    527: static enum cmd_retval
                    528: cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
                    529:     const struct cmd_entry_flag *flag)
                    530: {
                    531:        const char      *value;
                    532:
                    533:        if (flag->flag == 0) {
1.95      nicm      534:                cmd_find_from_client(fs, item->target_client, 0);
1.54      nicm      535:                return (CMD_RETURN_NORMAL);
                    536:        }
                    537:
1.82      nicm      538:        value = args_get(cmd_get_args(item->cmd), flag->flag);
1.54      nicm      539:        if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) {
                    540:                cmd_find_clear_state(fs, 0);
                    541:                return (CMD_RETURN_ERROR);
                    542:        }
                    543:        return (CMD_RETURN_NORMAL);
                    544: }
                    545:
1.93      nicm      546: /* Add message with command. */
                    547: static void
                    548: cmdq_add_message(struct cmdq_item *item)
                    549: {
                    550:        struct client           *c = item->client;
                    551:        struct cmdq_state       *state = item->state;
                    552:        const char              *name, *key;
                    553:        char                    *tmp;
                    554:
                    555:        tmp = cmd_print(item->cmd);
                    556:        if (c != NULL) {
                    557:                name = c->name;
                    558:                if (c->session != NULL && state->event.key != KEYC_NONE) {
1.97      nicm      559:                        key = key_string_lookup_key(state->event.key, 0);
1.93      nicm      560:                        server_add_message("%s key %s: %s", name, key, tmp);
                    561:                } else
                    562:                        server_add_message("%s command: %s", name, tmp);
                    563:        } else
                    564:                server_add_message("command: %s", tmp);
                    565:        free(tmp);
                    566: }
                    567:
1.43      nicm      568: /* Fire command on command queue. */
                    569: static enum cmd_retval
1.44      nicm      570: cmdq_fire_command(struct cmdq_item *item)
1.43      nicm      571: {
1.89      nicm      572:        const char              *name = cmdq_name(item->client);
1.86      nicm      573:        struct cmdq_state       *state = item->state;
1.44      nicm      574:        struct cmd              *cmd = item->cmd;
1.89      nicm      575:        struct args             *args = cmd_get_args(cmd);
1.82      nicm      576:        const struct cmd_entry  *entry = cmd_get_entry(cmd);
1.89      nicm      577:        struct client           *tc, *saved = item->client;
1.43      nicm      578:        enum cmd_retval          retval;
                    579:        struct cmd_find_state   *fsp, fs;
1.89      nicm      580:        int                      flags, quiet = 0;
1.72      nicm      581:        char                    *tmp;
                    582:
1.93      nicm      583:        if (cfg_finished)
                    584:                cmdq_add_message(item);
1.72      nicm      585:        if (log_get_level() > 1) {
                    586:                tmp = cmd_print(cmd);
                    587:                log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp);
                    588:                free(tmp);
                    589:        }
1.43      nicm      590:
1.86      nicm      591:        flags = !!(state->flags & CMDQ_STATE_CONTROL);
1.44      nicm      592:        cmdq_guard(item, "begin", flags);
1.43      nicm      593:
1.53      nicm      594:        if (item->client == NULL)
                    595:                item->client = cmd_find_client(item, NULL, 1);
1.89      nicm      596:
                    597:        if (entry->flags & CMD_CLIENT_CANFAIL)
                    598:                quiet = 1;
                    599:        if (entry->flags & CMD_CLIENT_CFLAG) {
                    600:                tc = cmd_find_client(item, args_get(args, 'c'), quiet);
                    601:                if (tc == NULL && !quiet) {
                    602:                        retval = CMD_RETURN_ERROR;
                    603:                        goto out;
                    604:                }
                    605:        } else if (entry->flags & CMD_CLIENT_TFLAG) {
                    606:                tc = cmd_find_client(item, args_get(args, 't'), quiet);
                    607:                if (tc == NULL && !quiet) {
                    608:                        retval = CMD_RETURN_ERROR;
                    609:                        goto out;
                    610:                }
                    611:        } else
                    612:                tc = cmd_find_client(item, NULL, 1);
                    613:        item->target_client = tc;
                    614:
1.54      nicm      615:        retval = cmdq_find_flag(item, &item->source, &entry->source);
                    616:        if (retval == CMD_RETURN_ERROR)
                    617:                goto out;
                    618:        retval = cmdq_find_flag(item, &item->target, &entry->target);
                    619:        if (retval == CMD_RETURN_ERROR)
1.43      nicm      620:                goto out;
1.89      nicm      621:
1.54      nicm      622:        retval = entry->exec(cmd, item);
1.43      nicm      623:        if (retval == CMD_RETURN_ERROR)
                    624:                goto out;
                    625:
1.54      nicm      626:        if (entry->flags & CMD_AFTERHOOK) {
                    627:                if (cmd_find_valid_state(&item->target))
                    628:                        fsp = &item->target;
1.86      nicm      629:                else if (cmd_find_valid_state(&item->state->current))
                    630:                        fsp = &item->state->current;
1.58      nicm      631:                else if (cmd_find_from_client(&fs, item->client, 0) == 0)
1.43      nicm      632:                        fsp = &fs;
1.51      nicm      633:                else
                    634:                        goto out;
1.63      nicm      635:                cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name);
1.43      nicm      636:        }
                    637:
                    638: out:
1.89      nicm      639:        item->client = saved;
1.43      nicm      640:        if (retval == CMD_RETURN_ERROR)
1.44      nicm      641:                cmdq_guard(item, "error", flags);
1.43      nicm      642:        else
1.44      nicm      643:                cmdq_guard(item, "end", flags);
1.43      nicm      644:        return (retval);
                    645: }
                    646:
                    647: /* Get a callback for the command queue. */
1.44      nicm      648: struct cmdq_item *
1.46      nicm      649: cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
1.1       nicm      650: {
1.44      nicm      651:        struct cmdq_item        *item;
1.1       nicm      652:
1.44      nicm      653:        item = xcalloc(1, sizeof *item);
1.64      nicm      654:        xasprintf(&item->name, "[%s/%p]", name, item);
1.44      nicm      655:        item->type = CMDQ_CALLBACK;
1.88      nicm      656:
1.44      nicm      657:        item->group = 0;
1.88      nicm      658:        item->state = cmdq_new_state(NULL, NULL, 0);
1.1       nicm      659:
1.44      nicm      660:        item->cb = cb;
                    661:        item->data = data;
1.1       nicm      662:
1.44      nicm      663:        return (item);
1.67      nicm      664: }
                    665:
                    666: /* Generic error callback. */
                    667: static enum cmd_retval
                    668: cmdq_error_callback(struct cmdq_item *item, void *data)
                    669: {
                    670:        char    *error = data;
                    671:
                    672:        cmdq_error(item, "%s", error);
                    673:        free(error);
                    674:
                    675:        return (CMD_RETURN_NORMAL);
                    676: }
                    677:
                    678: /* Get an error callback for the command queue. */
                    679: struct cmdq_item *
                    680: cmdq_get_error(const char *error)
                    681: {
                    682:        return (cmdq_get_callback(cmdq_error_callback, xstrdup(error)));
1.43      nicm      683: }
1.1       nicm      684:
1.43      nicm      685: /* Fire callback on callback queue. */
                    686: static enum cmd_retval
1.44      nicm      687: cmdq_fire_callback(struct cmdq_item *item)
1.43      nicm      688: {
1.44      nicm      689:        return (item->cb(item, item->data));
1.45      nicm      690: }
1.33      nicm      691:
1.43      nicm      692: /* Process next item on command queue. */
                    693: u_int
                    694: cmdq_next(struct client *c)
                    695: {
1.44      nicm      696:        struct cmdq_list        *queue = cmdq_get(c);
1.43      nicm      697:        const char              *name = cmdq_name(c);
1.44      nicm      698:        struct cmdq_item        *item;
1.43      nicm      699:        enum cmd_retval          retval;
                    700:        u_int                    items = 0;
                    701:        static u_int             number;
1.1       nicm      702:
1.90      nicm      703:        if (TAILQ_EMPTY(&queue->list)) {
1.43      nicm      704:                log_debug("%s %s: empty", __func__, name);
1.25      nicm      705:                return (0);
                    706:        }
1.90      nicm      707:        if (TAILQ_FIRST(&queue->list)->flags & CMDQ_WAITING) {
1.43      nicm      708:                log_debug("%s %s: waiting", __func__, name);
                    709:                return (0);
                    710:        }
                    711:
                    712:        log_debug("%s %s: enter", __func__, name);
                    713:        for (;;) {
1.90      nicm      714:                item = queue->item = TAILQ_FIRST(&queue->list);
1.44      nicm      715:                if (item == NULL)
1.43      nicm      716:                        break;
1.46      nicm      717:                log_debug("%s %s: %s (%d), flags %x", __func__, name,
                    718:                    item->name, item->type, item->flags);
1.43      nicm      719:
                    720:                /*
                    721:                 * Any item with the waiting flag set waits until an external
                    722:                 * event clears the flag (for example, a job - look at
                    723:                 * run-shell).
                    724:                 */
1.44      nicm      725:                if (item->flags & CMDQ_WAITING)
1.43      nicm      726:                        goto waiting;
                    727:
                    728:                /*
                    729:                 * Items are only fired once, once the fired flag is set, a
                    730:                 * waiting flag can only be cleared by an external event.
                    731:                 */
1.44      nicm      732:                if (~item->flags & CMDQ_FIRED) {
                    733:                        item->time = time(NULL);
                    734:                        item->number = ++number;
1.43      nicm      735:
1.50      nicm      736:                        switch (item->type) {
1.44      nicm      737:                        case CMDQ_COMMAND:
                    738:                                retval = cmdq_fire_command(item);
1.43      nicm      739:
                    740:                                /*
                    741:                                 * If a command returns an error, remove any
                    742:                                 * subsequent commands in the same group.
                    743:                                 */
                    744:                                if (retval == CMD_RETURN_ERROR)
1.44      nicm      745:                                        cmdq_remove_group(item);
1.43      nicm      746:                                break;
1.44      nicm      747:                        case CMDQ_CALLBACK:
                    748:                                retval = cmdq_fire_callback(item);
1.43      nicm      749:                                break;
                    750:                        default:
                    751:                                retval = CMD_RETURN_ERROR;
                    752:                                break;
                    753:                        }
1.44      nicm      754:                        item->flags |= CMDQ_FIRED;
1.43      nicm      755:
                    756:                        if (retval == CMD_RETURN_WAIT) {
1.44      nicm      757:                                item->flags |= CMDQ_WAITING;
1.43      nicm      758:                                goto waiting;
                    759:                        }
                    760:                        items++;
                    761:                }
1.44      nicm      762:                cmdq_remove(item);
1.43      nicm      763:        }
1.90      nicm      764:        queue->item = NULL;
1.43      nicm      765:
                    766:        log_debug("%s %s: exit (empty)", __func__, name);
                    767:        return (items);
1.1       nicm      768:
1.43      nicm      769: waiting:
                    770:        log_debug("%s %s: exit (wait)", __func__, name);
                    771:        return (items);
1.90      nicm      772: }
                    773:
                    774: /* Get running item if any. */
                    775: struct cmdq_item *
                    776: cmdq_running(struct client *c)
                    777: {
                    778:        struct cmdq_list        *queue = cmdq_get(c);
                    779:
1.100     nicm      780:        if (queue->item == NULL)
                    781:         return (NULL);
                    782:     if (queue->item->flags & CMDQ_WAITING)
                    783:         return (NULL);
                    784:     return (queue->item);
1.43      nicm      785: }
                    786:
                    787: /* Print a guard line. */
                    788: void
1.44      nicm      789: cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
1.43      nicm      790: {
1.44      nicm      791:        struct client   *c = item->client;
1.76      nicm      792:        long             t = item->time;
                    793:        u_int            number = item->number;
1.43      nicm      794:
1.76      nicm      795:        if (c != NULL && (c->flags & CLIENT_CONTROL))
1.98      nicm      796:                control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
1.1       nicm      797: }
                    798:
                    799: /* Show message from command. */
1.18      nicm      800: void
1.44      nicm      801: cmdq_print(struct cmdq_item *item, const char *fmt, ...)
1.1       nicm      802: {
1.61      nicm      803:        struct client                   *c = item->client;
                    804:        struct window_pane              *wp;
                    805:        struct window_mode_entry        *wme;
                    806:        va_list                          ap;
                    807:        char                            *tmp, *msg;
1.1       nicm      808:
                    809:        va_start(ap, fmt);
1.76      nicm      810:        xvasprintf(&msg, fmt, ap);
                    811:        va_end(ap);
                    812:
                    813:        log_debug("%s: %s", __func__, msg);
1.1       nicm      814:
                    815:        if (c == NULL)
                    816:                /* nothing */;
                    817:        else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
1.28      nicm      818:                if (~c->flags & CLIENT_UTF8) {
1.76      nicm      819:                        tmp = msg;
1.28      nicm      820:                        msg = utf8_sanitize(tmp);
                    821:                        free(tmp);
1.76      nicm      822:                }
1.98      nicm      823:                if (c->flags & CLIENT_CONTROL)
                    824:                        control_write(c, "%s", msg);
                    825:                else
                    826:                        file_print(c, "%s\n", msg);
1.1       nicm      827:        } else {
1.96      nicm      828:                wp = server_client_get_pane(c);
1.61      nicm      829:                wme = TAILQ_FIRST(&wp->modes);
1.81      nicm      830:                if (wme == NULL || wme->mode != &window_view_mode) {
                    831:                        window_pane_set_mode(wp, NULL, &window_view_mode, NULL,
                    832:                            NULL);
                    833:                }
1.77      nicm      834:                window_copy_add(wp, "%s", msg);
1.1       nicm      835:        }
                    836:
1.76      nicm      837:        free(msg);
1.1       nicm      838: }
                    839:
                    840: /* Show error from command. */
1.18      nicm      841: void
1.44      nicm      842: cmdq_error(struct cmdq_item *item, const char *fmt, ...)
1.1       nicm      843: {
1.44      nicm      844:        struct client   *c = item->client;
                    845:        struct cmd      *cmd = item->cmd;
1.1       nicm      846:        va_list          ap;
1.82      nicm      847:        char            *msg, *tmp;
                    848:        const char      *file;
                    849:        u_int            line;
1.1       nicm      850:
                    851:        va_start(ap, fmt);
1.76      nicm      852:        xvasprintf(&msg, fmt, ap);
1.1       nicm      853:        va_end(ap);
1.57      nicm      854:
                    855:        log_debug("%s: %s", __func__, msg);
1.1       nicm      856:
1.82      nicm      857:        if (c == NULL) {
                    858:                cmd_get_source(cmd, &file, &line);
                    859:                cfg_add_cause("%s:%u: %s", file, line, msg);
                    860:        } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
1.93      nicm      861:                server_add_message("%s message: %s", c->name, msg);
1.28      nicm      862:                if (~c->flags & CLIENT_UTF8) {
                    863:                        tmp = msg;
                    864:                        msg = utf8_sanitize(tmp);
                    865:                        free(tmp);
                    866:                }
1.79      nicm      867:                if (c->flags & CLIENT_CONTROL)
1.98      nicm      868:                        control_write(c, "%s", msg);
1.79      nicm      869:                else
                    870:                        file_error(c, "%s\n", msg);
1.13      nicm      871:                c->retval = 1;
1.1       nicm      872:        } else {
                    873:                *msg = toupper((u_char) *msg);
1.102     nicm      874:                status_message_set(c, -1, 1, 0, "%s", msg);
1.1       nicm      875:        }
                    876:
                    877:        free(msg);
                    878: }