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

1.107   ! nicm        1: /* $OpenBSD: cmd-queue.c,v 1.106 2021/08/21 10:28:05 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);
1.104     nicm      355:        struct args_entry               *ae;
                    356:        struct args_value               *av;
1.63      nicm      357:        struct options                  *oo;
                    358:        va_list                          ap;
1.92      nicm      359:        char                            *name, tmp[32], flag, *arguments;
1.105     nicm      360:        u_int                            i;
1.92      nicm      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:
1.105     nicm      397:        for (i = 0; i < args_count(args); i++) {
1.92      nicm      398:                xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i);
1.105     nicm      399:                cmdq_add_format(new_state, tmp, "%s", args_string(args, i));
1.92      nicm      400:        }
1.104     nicm      401:        flag = args_first(args, &ae);
1.92      nicm      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;
1.104     nicm      413:                av = args_first_value(args, flag);
                    414:                while (av != NULL) {
1.92      nicm      415:                        xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i);
1.106     nicm      416:                        cmdq_add_format(new_state, tmp, "%s", av->string);
1.92      nicm      417:                        i++;
1.104     nicm      418:                        av = args_next_value(av);
1.92      nicm      419:                }
                    420:
1.104     nicm      421:                flag = args_next(&ae);
1.92      nicm      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:
1.107   ! nicm      481: /* Empty command callback. */
        !           482: static enum cmd_retval
        !           483: cmdq_empty_command(__unused struct cmdq_item *item, __unused void *data)
        !           484: {
        !           485:        return (CMD_RETURN_NORMAL);
        !           486: }
        !           487:
1.43      nicm      488: /* Get a command for the command queue. */
1.44      nicm      489: struct cmdq_item *
1.88      nicm      490: cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state)
1.43      nicm      491: {
1.44      nicm      492:        struct cmdq_item        *item, *first = NULL, *last = NULL;
                    493:        struct cmd              *cmd;
1.82      nicm      494:        const struct cmd_entry  *entry;
1.88      nicm      495:        int                      created = 0;
1.49      nicm      496:
1.107   ! nicm      497:        if ((cmd = cmd_list_first(cmdlist)) == NULL)
        !           498:                return (cmdq_get_callback(cmdq_empty_command, NULL));
        !           499:
1.88      nicm      500:        if (state == NULL) {
                    501:                state = cmdq_new_state(NULL, NULL, 0);
                    502:                created = 1;
                    503:        }
                    504:
1.82      nicm      505:        while (cmd != NULL) {
                    506:                entry = cmd_get_entry(cmd);
1.43      nicm      507:
1.44      nicm      508:                item = xcalloc(1, sizeof *item);
1.82      nicm      509:                xasprintf(&item->name, "[%s/%p]", entry->name, item);
1.44      nicm      510:                item->type = CMDQ_COMMAND;
1.43      nicm      511:
1.88      nicm      512:                item->group = cmd_get_group(cmd);
                    513:                item->state = cmdq_link_state(state);
                    514:
1.44      nicm      515:                item->cmdlist = cmdlist;
                    516:                item->cmd = cmd;
1.68      nicm      517:
1.88      nicm      518:                cmdlist->references++;
1.68      nicm      519:                log_debug("%s: %s group %u", __func__, item->name, item->group);
1.43      nicm      520:
                    521:                if (first == NULL)
1.44      nicm      522:                        first = item;
1.43      nicm      523:                if (last != NULL)
1.44      nicm      524:                        last->next = item;
                    525:                last = item;
1.82      nicm      526:
1.88      nicm      527:                cmd = cmd_list_next(cmd);
1.43      nicm      528:        }
1.88      nicm      529:
                    530:        if (created)
                    531:                cmdq_free_state(state);
1.43      nicm      532:        return (first);
                    533: }
                    534:
1.54      nicm      535: /* Fill in flag for a command. */
                    536: static enum cmd_retval
                    537: cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
                    538:     const struct cmd_entry_flag *flag)
                    539: {
                    540:        const char      *value;
                    541:
                    542:        if (flag->flag == 0) {
1.95      nicm      543:                cmd_find_from_client(fs, item->target_client, 0);
1.54      nicm      544:                return (CMD_RETURN_NORMAL);
                    545:        }
                    546:
1.82      nicm      547:        value = args_get(cmd_get_args(item->cmd), flag->flag);
1.54      nicm      548:        if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) {
                    549:                cmd_find_clear_state(fs, 0);
                    550:                return (CMD_RETURN_ERROR);
                    551:        }
                    552:        return (CMD_RETURN_NORMAL);
                    553: }
                    554:
1.93      nicm      555: /* Add message with command. */
                    556: static void
                    557: cmdq_add_message(struct cmdq_item *item)
                    558: {
                    559:        struct client           *c = item->client;
                    560:        struct cmdq_state       *state = item->state;
                    561:        const char              *name, *key;
                    562:        char                    *tmp;
                    563:
                    564:        tmp = cmd_print(item->cmd);
                    565:        if (c != NULL) {
                    566:                name = c->name;
                    567:                if (c->session != NULL && state->event.key != KEYC_NONE) {
1.97      nicm      568:                        key = key_string_lookup_key(state->event.key, 0);
1.93      nicm      569:                        server_add_message("%s key %s: %s", name, key, tmp);
                    570:                } else
                    571:                        server_add_message("%s command: %s", name, tmp);
                    572:        } else
                    573:                server_add_message("command: %s", tmp);
                    574:        free(tmp);
                    575: }
                    576:
1.43      nicm      577: /* Fire command on command queue. */
                    578: static enum cmd_retval
1.44      nicm      579: cmdq_fire_command(struct cmdq_item *item)
1.43      nicm      580: {
1.89      nicm      581:        const char              *name = cmdq_name(item->client);
1.86      nicm      582:        struct cmdq_state       *state = item->state;
1.44      nicm      583:        struct cmd              *cmd = item->cmd;
1.89      nicm      584:        struct args             *args = cmd_get_args(cmd);
1.82      nicm      585:        const struct cmd_entry  *entry = cmd_get_entry(cmd);
1.89      nicm      586:        struct client           *tc, *saved = item->client;
1.43      nicm      587:        enum cmd_retval          retval;
                    588:        struct cmd_find_state   *fsp, fs;
1.89      nicm      589:        int                      flags, quiet = 0;
1.72      nicm      590:        char                    *tmp;
                    591:
1.93      nicm      592:        if (cfg_finished)
                    593:                cmdq_add_message(item);
1.72      nicm      594:        if (log_get_level() > 1) {
                    595:                tmp = cmd_print(cmd);
                    596:                log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp);
                    597:                free(tmp);
                    598:        }
1.43      nicm      599:
1.86      nicm      600:        flags = !!(state->flags & CMDQ_STATE_CONTROL);
1.44      nicm      601:        cmdq_guard(item, "begin", flags);
1.43      nicm      602:
1.53      nicm      603:        if (item->client == NULL)
                    604:                item->client = cmd_find_client(item, NULL, 1);
1.89      nicm      605:
                    606:        if (entry->flags & CMD_CLIENT_CANFAIL)
                    607:                quiet = 1;
                    608:        if (entry->flags & CMD_CLIENT_CFLAG) {
                    609:                tc = cmd_find_client(item, args_get(args, 'c'), quiet);
                    610:                if (tc == NULL && !quiet) {
                    611:                        retval = CMD_RETURN_ERROR;
                    612:                        goto out;
                    613:                }
                    614:        } else if (entry->flags & CMD_CLIENT_TFLAG) {
                    615:                tc = cmd_find_client(item, args_get(args, 't'), quiet);
                    616:                if (tc == NULL && !quiet) {
                    617:                        retval = CMD_RETURN_ERROR;
                    618:                        goto out;
                    619:                }
                    620:        } else
                    621:                tc = cmd_find_client(item, NULL, 1);
                    622:        item->target_client = tc;
                    623:
1.54      nicm      624:        retval = cmdq_find_flag(item, &item->source, &entry->source);
                    625:        if (retval == CMD_RETURN_ERROR)
                    626:                goto out;
                    627:        retval = cmdq_find_flag(item, &item->target, &entry->target);
                    628:        if (retval == CMD_RETURN_ERROR)
1.43      nicm      629:                goto out;
1.89      nicm      630:
1.54      nicm      631:        retval = entry->exec(cmd, item);
1.43      nicm      632:        if (retval == CMD_RETURN_ERROR)
                    633:                goto out;
                    634:
1.54      nicm      635:        if (entry->flags & CMD_AFTERHOOK) {
                    636:                if (cmd_find_valid_state(&item->target))
                    637:                        fsp = &item->target;
1.86      nicm      638:                else if (cmd_find_valid_state(&item->state->current))
                    639:                        fsp = &item->state->current;
1.58      nicm      640:                else if (cmd_find_from_client(&fs, item->client, 0) == 0)
1.43      nicm      641:                        fsp = &fs;
1.51      nicm      642:                else
                    643:                        goto out;
1.63      nicm      644:                cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name);
1.43      nicm      645:        }
                    646:
                    647: out:
1.89      nicm      648:        item->client = saved;
1.43      nicm      649:        if (retval == CMD_RETURN_ERROR)
1.44      nicm      650:                cmdq_guard(item, "error", flags);
1.43      nicm      651:        else
1.44      nicm      652:                cmdq_guard(item, "end", flags);
1.43      nicm      653:        return (retval);
                    654: }
                    655:
                    656: /* Get a callback for the command queue. */
1.44      nicm      657: struct cmdq_item *
1.46      nicm      658: cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
1.1       nicm      659: {
1.44      nicm      660:        struct cmdq_item        *item;
1.1       nicm      661:
1.44      nicm      662:        item = xcalloc(1, sizeof *item);
1.64      nicm      663:        xasprintf(&item->name, "[%s/%p]", name, item);
1.44      nicm      664:        item->type = CMDQ_CALLBACK;
1.88      nicm      665:
1.44      nicm      666:        item->group = 0;
1.88      nicm      667:        item->state = cmdq_new_state(NULL, NULL, 0);
1.1       nicm      668:
1.44      nicm      669:        item->cb = cb;
                    670:        item->data = data;
1.1       nicm      671:
1.44      nicm      672:        return (item);
1.67      nicm      673: }
                    674:
                    675: /* Generic error callback. */
                    676: static enum cmd_retval
                    677: cmdq_error_callback(struct cmdq_item *item, void *data)
                    678: {
                    679:        char    *error = data;
                    680:
                    681:        cmdq_error(item, "%s", error);
                    682:        free(error);
                    683:
                    684:        return (CMD_RETURN_NORMAL);
                    685: }
                    686:
                    687: /* Get an error callback for the command queue. */
                    688: struct cmdq_item *
                    689: cmdq_get_error(const char *error)
                    690: {
                    691:        return (cmdq_get_callback(cmdq_error_callback, xstrdup(error)));
1.43      nicm      692: }
1.1       nicm      693:
1.43      nicm      694: /* Fire callback on callback queue. */
                    695: static enum cmd_retval
1.44      nicm      696: cmdq_fire_callback(struct cmdq_item *item)
1.43      nicm      697: {
1.44      nicm      698:        return (item->cb(item, item->data));
1.45      nicm      699: }
1.33      nicm      700:
1.43      nicm      701: /* Process next item on command queue. */
                    702: u_int
                    703: cmdq_next(struct client *c)
                    704: {
1.44      nicm      705:        struct cmdq_list        *queue = cmdq_get(c);
1.43      nicm      706:        const char              *name = cmdq_name(c);
1.44      nicm      707:        struct cmdq_item        *item;
1.43      nicm      708:        enum cmd_retval          retval;
                    709:        u_int                    items = 0;
                    710:        static u_int             number;
1.1       nicm      711:
1.90      nicm      712:        if (TAILQ_EMPTY(&queue->list)) {
1.43      nicm      713:                log_debug("%s %s: empty", __func__, name);
1.25      nicm      714:                return (0);
                    715:        }
1.90      nicm      716:        if (TAILQ_FIRST(&queue->list)->flags & CMDQ_WAITING) {
1.43      nicm      717:                log_debug("%s %s: waiting", __func__, name);
                    718:                return (0);
                    719:        }
                    720:
                    721:        log_debug("%s %s: enter", __func__, name);
                    722:        for (;;) {
1.90      nicm      723:                item = queue->item = TAILQ_FIRST(&queue->list);
1.44      nicm      724:                if (item == NULL)
1.43      nicm      725:                        break;
1.46      nicm      726:                log_debug("%s %s: %s (%d), flags %x", __func__, name,
                    727:                    item->name, item->type, item->flags);
1.43      nicm      728:
                    729:                /*
                    730:                 * Any item with the waiting flag set waits until an external
                    731:                 * event clears the flag (for example, a job - look at
                    732:                 * run-shell).
                    733:                 */
1.44      nicm      734:                if (item->flags & CMDQ_WAITING)
1.43      nicm      735:                        goto waiting;
                    736:
                    737:                /*
                    738:                 * Items are only fired once, once the fired flag is set, a
                    739:                 * waiting flag can only be cleared by an external event.
                    740:                 */
1.44      nicm      741:                if (~item->flags & CMDQ_FIRED) {
                    742:                        item->time = time(NULL);
                    743:                        item->number = ++number;
1.43      nicm      744:
1.50      nicm      745:                        switch (item->type) {
1.44      nicm      746:                        case CMDQ_COMMAND:
                    747:                                retval = cmdq_fire_command(item);
1.43      nicm      748:
                    749:                                /*
                    750:                                 * If a command returns an error, remove any
                    751:                                 * subsequent commands in the same group.
                    752:                                 */
                    753:                                if (retval == CMD_RETURN_ERROR)
1.44      nicm      754:                                        cmdq_remove_group(item);
1.43      nicm      755:                                break;
1.44      nicm      756:                        case CMDQ_CALLBACK:
                    757:                                retval = cmdq_fire_callback(item);
1.43      nicm      758:                                break;
                    759:                        default:
                    760:                                retval = CMD_RETURN_ERROR;
                    761:                                break;
                    762:                        }
1.44      nicm      763:                        item->flags |= CMDQ_FIRED;
1.43      nicm      764:
                    765:                        if (retval == CMD_RETURN_WAIT) {
1.44      nicm      766:                                item->flags |= CMDQ_WAITING;
1.43      nicm      767:                                goto waiting;
                    768:                        }
                    769:                        items++;
                    770:                }
1.44      nicm      771:                cmdq_remove(item);
1.43      nicm      772:        }
1.90      nicm      773:        queue->item = NULL;
1.43      nicm      774:
                    775:        log_debug("%s %s: exit (empty)", __func__, name);
                    776:        return (items);
1.1       nicm      777:
1.43      nicm      778: waiting:
                    779:        log_debug("%s %s: exit (wait)", __func__, name);
                    780:        return (items);
1.90      nicm      781: }
                    782:
                    783: /* Get running item if any. */
                    784: struct cmdq_item *
                    785: cmdq_running(struct client *c)
                    786: {
                    787:        struct cmdq_list        *queue = cmdq_get(c);
                    788:
1.100     nicm      789:        if (queue->item == NULL)
                    790:         return (NULL);
                    791:     if (queue->item->flags & CMDQ_WAITING)
                    792:         return (NULL);
                    793:     return (queue->item);
1.43      nicm      794: }
                    795:
                    796: /* Print a guard line. */
                    797: void
1.44      nicm      798: cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
1.43      nicm      799: {
1.44      nicm      800:        struct client   *c = item->client;
1.76      nicm      801:        long             t = item->time;
                    802:        u_int            number = item->number;
1.43      nicm      803:
1.76      nicm      804:        if (c != NULL && (c->flags & CLIENT_CONTROL))
1.98      nicm      805:                control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
1.1       nicm      806: }
                    807:
                    808: /* Show message from command. */
1.18      nicm      809: void
1.44      nicm      810: cmdq_print(struct cmdq_item *item, const char *fmt, ...)
1.1       nicm      811: {
1.61      nicm      812:        struct client                   *c = item->client;
                    813:        struct window_pane              *wp;
                    814:        struct window_mode_entry        *wme;
                    815:        va_list                          ap;
                    816:        char                            *tmp, *msg;
1.1       nicm      817:
                    818:        va_start(ap, fmt);
1.76      nicm      819:        xvasprintf(&msg, fmt, ap);
                    820:        va_end(ap);
                    821:
                    822:        log_debug("%s: %s", __func__, msg);
1.1       nicm      823:
                    824:        if (c == NULL)
                    825:                /* nothing */;
                    826:        else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
1.28      nicm      827:                if (~c->flags & CLIENT_UTF8) {
1.76      nicm      828:                        tmp = msg;
1.28      nicm      829:                        msg = utf8_sanitize(tmp);
                    830:                        free(tmp);
1.76      nicm      831:                }
1.98      nicm      832:                if (c->flags & CLIENT_CONTROL)
                    833:                        control_write(c, "%s", msg);
                    834:                else
                    835:                        file_print(c, "%s\n", msg);
1.1       nicm      836:        } else {
1.96      nicm      837:                wp = server_client_get_pane(c);
1.61      nicm      838:                wme = TAILQ_FIRST(&wp->modes);
1.81      nicm      839:                if (wme == NULL || wme->mode != &window_view_mode) {
                    840:                        window_pane_set_mode(wp, NULL, &window_view_mode, NULL,
                    841:                            NULL);
                    842:                }
1.77      nicm      843:                window_copy_add(wp, "%s", msg);
1.1       nicm      844:        }
                    845:
1.76      nicm      846:        free(msg);
1.1       nicm      847: }
                    848:
                    849: /* Show error from command. */
1.18      nicm      850: void
1.44      nicm      851: cmdq_error(struct cmdq_item *item, const char *fmt, ...)
1.1       nicm      852: {
1.44      nicm      853:        struct client   *c = item->client;
                    854:        struct cmd      *cmd = item->cmd;
1.1       nicm      855:        va_list          ap;
1.82      nicm      856:        char            *msg, *tmp;
                    857:        const char      *file;
                    858:        u_int            line;
1.1       nicm      859:
                    860:        va_start(ap, fmt);
1.76      nicm      861:        xvasprintf(&msg, fmt, ap);
1.1       nicm      862:        va_end(ap);
1.57      nicm      863:
                    864:        log_debug("%s: %s", __func__, msg);
1.1       nicm      865:
1.82      nicm      866:        if (c == NULL) {
                    867:                cmd_get_source(cmd, &file, &line);
                    868:                cfg_add_cause("%s:%u: %s", file, line, msg);
                    869:        } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
1.93      nicm      870:                server_add_message("%s message: %s", c->name, msg);
1.28      nicm      871:                if (~c->flags & CLIENT_UTF8) {
                    872:                        tmp = msg;
                    873:                        msg = utf8_sanitize(tmp);
                    874:                        free(tmp);
                    875:                }
1.79      nicm      876:                if (c->flags & CLIENT_CONTROL)
1.98      nicm      877:                        control_write(c, "%s", msg);
1.79      nicm      878:                else
                    879:                        file_error(c, "%s\n", msg);
1.13      nicm      880:                c->retval = 1;
1.1       nicm      881:        } else {
                    882:                *msg = toupper((u_char) *msg);
1.102     nicm      883:                status_message_set(c, -1, 1, 0, "%s", msg);
1.1       nicm      884:        }
                    885:
                    886:        free(msg);
                    887: }