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

1.96    ! nicm        1: /* $OpenBSD: cmd-queue.c,v 1.95 2020/05/16 16:16:07 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);
                    270: }
                    271:
1.83      nicm      272: /* Merge formats from item. */
                    273: void
                    274: cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft)
                    275: {
                    276:        const struct cmd_entry  *entry;
                    277:
                    278:        if (item->cmd != NULL) {
                    279:                entry = cmd_get_entry (item->cmd);
                    280:                format_add(ft, "command", "%s", entry->name);
                    281:        }
1.86      nicm      282:        if (item->state->formats != NULL)
                    283:                format_merge(ft, item->state->formats);
1.43      nicm      284: }
                    285:
                    286: /* Append an item. */
1.78      nicm      287: struct cmdq_item *
1.44      nicm      288: cmdq_append(struct client *c, struct cmdq_item *item)
1.43      nicm      289: {
1.44      nicm      290:        struct cmdq_list        *queue = cmdq_get(c);
                    291:        struct cmdq_item        *next;
1.43      nicm      292:
                    293:        do {
1.44      nicm      294:                next = item->next;
                    295:                item->next = NULL;
1.43      nicm      296:
                    297:                if (c != NULL)
                    298:                        c->references++;
1.44      nicm      299:                item->client = c;
1.43      nicm      300:
1.44      nicm      301:                item->queue = queue;
1.90      nicm      302:                TAILQ_INSERT_TAIL(&queue->list, item, entry);
1.64      nicm      303:                log_debug("%s %s: %s", __func__, cmdq_name(c), item->name);
1.43      nicm      304:
1.44      nicm      305:                item = next;
                    306:        } while (item != NULL);
1.90      nicm      307:        return (TAILQ_LAST(&queue->list, cmdq_item_list));
1.43      nicm      308: }
                    309:
                    310: /* Insert an item. */
1.78      nicm      311: struct cmdq_item *
1.44      nicm      312: cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
1.43      nicm      313: {
                    314:        struct client           *c = after->client;
1.44      nicm      315:        struct cmdq_list        *queue = after->queue;
                    316:        struct cmdq_item        *next;
1.43      nicm      317:
                    318:        do {
1.44      nicm      319:                next = item->next;
1.64      nicm      320:                item->next = after->next;
                    321:                after->next = item;
1.43      nicm      322:
                    323:                if (c != NULL)
                    324:                        c->references++;
1.44      nicm      325:                item->client = c;
1.43      nicm      326:
1.44      nicm      327:                item->queue = queue;
1.90      nicm      328:                TAILQ_INSERT_AFTER(&queue->list, after, item, entry);
1.64      nicm      329:                log_debug("%s %s: %s after %s", __func__, cmdq_name(c),
                    330:                    item->name, after->name);
1.43      nicm      331:
1.65      nicm      332:                after = item;
1.44      nicm      333:                item = next;
                    334:        } while (item != NULL);
1.78      nicm      335:        return (after);
1.43      nicm      336: }
1.63      nicm      337:
                    338: /* Insert a hook. */
                    339: void
                    340: cmdq_insert_hook(struct session *s, struct cmdq_item *item,
1.88      nicm      341:     struct cmd_find_state *current, const char *fmt, ...)
1.63      nicm      342: {
1.88      nicm      343:        struct cmdq_state               *state = item->state;
1.92      nicm      344:        struct cmd                      *cmd = item->cmd;
                    345:        struct args                     *args = cmd_get_args(cmd);
                    346:        struct args_entry               *entryp;
                    347:        struct args_value               *valuep;
1.63      nicm      348:        struct options                  *oo;
                    349:        va_list                          ap;
1.92      nicm      350:        char                            *name, tmp[32], flag, *arguments;
                    351:        int                              i;
                    352:        const char                      *value;
1.63      nicm      353:        struct cmdq_item                *new_item;
1.88      nicm      354:        struct cmdq_state               *new_state;
1.63      nicm      355:        struct options_entry            *o;
                    356:        struct options_array_item       *a;
                    357:        struct cmd_list                 *cmdlist;
                    358:
1.86      nicm      359:        if (item->state->flags & CMDQ_STATE_NOHOOKS)
1.63      nicm      360:                return;
                    361:        if (s == NULL)
                    362:                oo = global_s_options;
                    363:        else
                    364:                oo = s->options;
                    365:
                    366:        va_start(ap, fmt);
                    367:        xvasprintf(&name, fmt, ap);
                    368:        va_end(ap);
                    369:
                    370:        o = options_get(oo, name);
                    371:        if (o == NULL) {
                    372:                free(name);
                    373:                return;
                    374:        }
                    375:        log_debug("running hook %s (parent %p)", name, item);
                    376:
1.88      nicm      377:        /*
                    378:         * The hooks get a new state because they should not update the current
                    379:         * target or formats for any subsequent commands.
                    380:         */
                    381:        new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS);
                    382:        cmdq_add_format(new_state, "hook", "%s", name);
1.92      nicm      383:
                    384:        arguments = args_print(args);
                    385:        cmdq_add_format(new_state, "hook_arguments", "%s", arguments);
                    386:        free(arguments);
                    387:
                    388:        for (i = 0; i < args->argc; i++) {
                    389:                xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i);
                    390:                cmdq_add_format(new_state, tmp, "%s", args->argv[i]);
                    391:        }
                    392:        flag = args_first(args, &entryp);
                    393:        while (flag != 0) {
                    394:                value = args_get(args, flag);
                    395:                if (value == NULL) {
                    396:                        xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
                    397:                        cmdq_add_format(new_state, tmp, "1");
                    398:                } else {
                    399:                        xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
                    400:                        cmdq_add_format(new_state, tmp, "%s", value);
                    401:                }
                    402:
                    403:                i = 0;
                    404:                value = args_first_value(args, flag, &valuep);
                    405:                while (value != NULL) {
                    406:                        xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i);
                    407:                        cmdq_add_format(new_state, tmp, "%s", value);
                    408:                        i++;
                    409:                        value = args_next_value(&valuep);
                    410:                }
                    411:
                    412:                flag = args_next(&entryp);
                    413:        }
1.88      nicm      414:
1.63      nicm      415:        a = options_array_first(o);
                    416:        while (a != NULL) {
                    417:                cmdlist = options_array_item_value(a)->cmdlist;
1.88      nicm      418:                if (cmdlist != NULL) {
                    419:                        new_item = cmdq_get_command(cmdlist, new_state);
                    420:                        if (item != NULL)
                    421:                                item = cmdq_insert_after(item, new_item);
                    422:                        else
                    423:                                item = cmdq_append(NULL, new_item);
1.63      nicm      424:                }
                    425:                a = options_array_next(a);
                    426:        }
                    427:
1.88      nicm      428:        cmdq_free_state(new_state);
1.63      nicm      429:        free(name);
1.74      nicm      430: }
                    431:
                    432: /* Continue processing command queue. */
                    433: void
                    434: cmdq_continue(struct cmdq_item *item)
                    435: {
                    436:        item->flags &= ~CMDQ_WAITING;
1.63      nicm      437: }
                    438:
1.43      nicm      439: /* Remove an item. */
                    440: static void
1.44      nicm      441: cmdq_remove(struct cmdq_item *item)
1.43      nicm      442: {
1.44      nicm      443:        if (item->client != NULL)
                    444:                server_client_unref(item->client);
1.62      nicm      445:        if (item->cmdlist != NULL)
1.44      nicm      446:                cmd_list_free(item->cmdlist);
1.88      nicm      447:        cmdq_free_state(item->state);
1.43      nicm      448:
1.90      nicm      449:        TAILQ_REMOVE(&item->queue->list, item, entry);
1.46      nicm      450:
1.64      nicm      451:        free(item->name);
1.44      nicm      452:        free(item);
1.43      nicm      453: }
                    454:
                    455: /* Remove all subsequent items that match this item's group. */
                    456: static void
1.44      nicm      457: cmdq_remove_group(struct cmdq_item *item)
1.43      nicm      458: {
1.44      nicm      459:        struct cmdq_item        *this, *next;
1.43      nicm      460:
1.69      nicm      461:        if (item->group == 0)
                    462:                return;
1.44      nicm      463:        this = TAILQ_NEXT(item, entry);
1.43      nicm      464:        while (this != NULL) {
                    465:                next = TAILQ_NEXT(this, entry);
1.44      nicm      466:                if (this->group == item->group)
1.43      nicm      467:                        cmdq_remove(this);
                    468:                this = next;
                    469:        }
                    470: }
                    471:
                    472: /* Get a command for the command queue. */
1.44      nicm      473: struct cmdq_item *
1.88      nicm      474: cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state)
1.43      nicm      475: {
1.44      nicm      476:        struct cmdq_item        *item, *first = NULL, *last = NULL;
                    477:        struct cmd              *cmd;
1.82      nicm      478:        const struct cmd_entry  *entry;
1.88      nicm      479:        int                      created = 0;
1.49      nicm      480:
1.88      nicm      481:        if (state == NULL) {
                    482:                state = cmdq_new_state(NULL, NULL, 0);
                    483:                created = 1;
                    484:        }
                    485:
                    486:        cmd = cmd_list_first(cmdlist);
1.82      nicm      487:        while (cmd != NULL) {
                    488:                entry = cmd_get_entry(cmd);
1.43      nicm      489:
1.44      nicm      490:                item = xcalloc(1, sizeof *item);
1.82      nicm      491:                xasprintf(&item->name, "[%s/%p]", entry->name, item);
1.44      nicm      492:                item->type = CMDQ_COMMAND;
1.43      nicm      493:
1.88      nicm      494:                item->group = cmd_get_group(cmd);
                    495:                item->state = cmdq_link_state(state);
                    496:
1.44      nicm      497:                item->cmdlist = cmdlist;
                    498:                item->cmd = cmd;
1.68      nicm      499:
1.88      nicm      500:                cmdlist->references++;
1.68      nicm      501:                log_debug("%s: %s group %u", __func__, item->name, item->group);
1.43      nicm      502:
                    503:                if (first == NULL)
1.44      nicm      504:                        first = item;
1.43      nicm      505:                if (last != NULL)
1.44      nicm      506:                        last->next = item;
                    507:                last = item;
1.82      nicm      508:
1.88      nicm      509:                cmd = cmd_list_next(cmd);
1.43      nicm      510:        }
1.88      nicm      511:
                    512:        if (created)
                    513:                cmdq_free_state(state);
1.43      nicm      514:        return (first);
                    515: }
                    516:
1.54      nicm      517: /* Fill in flag for a command. */
                    518: static enum cmd_retval
                    519: cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
                    520:     const struct cmd_entry_flag *flag)
                    521: {
                    522:        const char      *value;
                    523:
                    524:        if (flag->flag == 0) {
1.95      nicm      525:                cmd_find_from_client(fs, item->target_client, 0);
1.54      nicm      526:                return (CMD_RETURN_NORMAL);
                    527:        }
                    528:
1.82      nicm      529:        value = args_get(cmd_get_args(item->cmd), flag->flag);
1.54      nicm      530:        if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) {
                    531:                cmd_find_clear_state(fs, 0);
                    532:                return (CMD_RETURN_ERROR);
                    533:        }
                    534:        return (CMD_RETURN_NORMAL);
                    535: }
                    536:
1.93      nicm      537: /* Add message with command. */
                    538: static void
                    539: cmdq_add_message(struct cmdq_item *item)
                    540: {
                    541:        struct client           *c = item->client;
                    542:        struct cmdq_state       *state = item->state;
                    543:        const char              *name, *key;
                    544:        char                    *tmp;
                    545:
                    546:        tmp = cmd_print(item->cmd);
                    547:        if (c != NULL) {
                    548:                name = c->name;
                    549:                if (c->session != NULL && state->event.key != KEYC_NONE) {
                    550:                        key = key_string_lookup_key(state->event.key);
                    551:                        server_add_message("%s key %s: %s", name, key, tmp);
                    552:                } else
                    553:                        server_add_message("%s command: %s", name, tmp);
                    554:        } else
                    555:                server_add_message("command: %s", tmp);
                    556:        free(tmp);
                    557: }
                    558:
1.43      nicm      559: /* Fire command on command queue. */
                    560: static enum cmd_retval
1.44      nicm      561: cmdq_fire_command(struct cmdq_item *item)
1.43      nicm      562: {
1.89      nicm      563:        const char              *name = cmdq_name(item->client);
1.86      nicm      564:        struct cmdq_state       *state = item->state;
1.44      nicm      565:        struct cmd              *cmd = item->cmd;
1.89      nicm      566:        struct args             *args = cmd_get_args(cmd);
1.82      nicm      567:        const struct cmd_entry  *entry = cmd_get_entry(cmd);
1.89      nicm      568:        struct client           *tc, *saved = item->client;
1.43      nicm      569:        enum cmd_retval          retval;
                    570:        struct cmd_find_state   *fsp, fs;
1.89      nicm      571:        int                      flags, quiet = 0;
1.72      nicm      572:        char                    *tmp;
                    573:
1.93      nicm      574:        if (cfg_finished)
                    575:                cmdq_add_message(item);
1.72      nicm      576:        if (log_get_level() > 1) {
                    577:                tmp = cmd_print(cmd);
                    578:                log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp);
                    579:                free(tmp);
                    580:        }
1.43      nicm      581:
1.86      nicm      582:        flags = !!(state->flags & CMDQ_STATE_CONTROL);
1.44      nicm      583:        cmdq_guard(item, "begin", flags);
1.43      nicm      584:
1.53      nicm      585:        if (item->client == NULL)
                    586:                item->client = cmd_find_client(item, NULL, 1);
1.89      nicm      587:
                    588:        if (entry->flags & CMD_CLIENT_CANFAIL)
                    589:                quiet = 1;
                    590:        if (entry->flags & CMD_CLIENT_CFLAG) {
                    591:                tc = cmd_find_client(item, args_get(args, 'c'), quiet);
                    592:                if (tc == NULL && !quiet) {
                    593:                        retval = CMD_RETURN_ERROR;
                    594:                        goto out;
                    595:                }
                    596:        } else if (entry->flags & CMD_CLIENT_TFLAG) {
                    597:                tc = cmd_find_client(item, args_get(args, 't'), quiet);
                    598:                if (tc == NULL && !quiet) {
                    599:                        retval = CMD_RETURN_ERROR;
                    600:                        goto out;
                    601:                }
                    602:        } else
                    603:                tc = cmd_find_client(item, NULL, 1);
                    604:        item->target_client = tc;
                    605:
1.54      nicm      606:        retval = cmdq_find_flag(item, &item->source, &entry->source);
                    607:        if (retval == CMD_RETURN_ERROR)
                    608:                goto out;
                    609:        retval = cmdq_find_flag(item, &item->target, &entry->target);
                    610:        if (retval == CMD_RETURN_ERROR)
1.43      nicm      611:                goto out;
1.89      nicm      612:
1.54      nicm      613:        retval = entry->exec(cmd, item);
1.43      nicm      614:        if (retval == CMD_RETURN_ERROR)
                    615:                goto out;
                    616:
1.54      nicm      617:        if (entry->flags & CMD_AFTERHOOK) {
                    618:                if (cmd_find_valid_state(&item->target))
                    619:                        fsp = &item->target;
1.86      nicm      620:                else if (cmd_find_valid_state(&item->state->current))
                    621:                        fsp = &item->state->current;
1.58      nicm      622:                else if (cmd_find_from_client(&fs, item->client, 0) == 0)
1.43      nicm      623:                        fsp = &fs;
1.51      nicm      624:                else
                    625:                        goto out;
1.63      nicm      626:                cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name);
1.43      nicm      627:        }
                    628:
                    629: out:
1.89      nicm      630:        item->client = saved;
1.43      nicm      631:        if (retval == CMD_RETURN_ERROR)
1.44      nicm      632:                cmdq_guard(item, "error", flags);
1.43      nicm      633:        else
1.44      nicm      634:                cmdq_guard(item, "end", flags);
1.43      nicm      635:        return (retval);
                    636: }
                    637:
                    638: /* Get a callback for the command queue. */
1.44      nicm      639: struct cmdq_item *
1.46      nicm      640: cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
1.1       nicm      641: {
1.44      nicm      642:        struct cmdq_item        *item;
1.1       nicm      643:
1.44      nicm      644:        item = xcalloc(1, sizeof *item);
1.64      nicm      645:        xasprintf(&item->name, "[%s/%p]", name, item);
1.44      nicm      646:        item->type = CMDQ_CALLBACK;
1.88      nicm      647:
1.44      nicm      648:        item->group = 0;
1.88      nicm      649:        item->state = cmdq_new_state(NULL, NULL, 0);
1.1       nicm      650:
1.44      nicm      651:        item->cb = cb;
                    652:        item->data = data;
1.1       nicm      653:
1.44      nicm      654:        return (item);
1.67      nicm      655: }
                    656:
                    657: /* Generic error callback. */
                    658: static enum cmd_retval
                    659: cmdq_error_callback(struct cmdq_item *item, void *data)
                    660: {
                    661:        char    *error = data;
                    662:
                    663:        cmdq_error(item, "%s", error);
                    664:        free(error);
                    665:
                    666:        return (CMD_RETURN_NORMAL);
                    667: }
                    668:
                    669: /* Get an error callback for the command queue. */
                    670: struct cmdq_item *
                    671: cmdq_get_error(const char *error)
                    672: {
                    673:        return (cmdq_get_callback(cmdq_error_callback, xstrdup(error)));
1.43      nicm      674: }
1.1       nicm      675:
1.43      nicm      676: /* Fire callback on callback queue. */
                    677: static enum cmd_retval
1.44      nicm      678: cmdq_fire_callback(struct cmdq_item *item)
1.43      nicm      679: {
1.44      nicm      680:        return (item->cb(item, item->data));
1.45      nicm      681: }
1.33      nicm      682:
1.43      nicm      683: /* Process next item on command queue. */
                    684: u_int
                    685: cmdq_next(struct client *c)
                    686: {
1.44      nicm      687:        struct cmdq_list        *queue = cmdq_get(c);
1.43      nicm      688:        const char              *name = cmdq_name(c);
1.44      nicm      689:        struct cmdq_item        *item;
1.43      nicm      690:        enum cmd_retval          retval;
                    691:        u_int                    items = 0;
                    692:        static u_int             number;
1.1       nicm      693:
1.90      nicm      694:        if (TAILQ_EMPTY(&queue->list)) {
1.43      nicm      695:                log_debug("%s %s: empty", __func__, name);
1.25      nicm      696:                return (0);
                    697:        }
1.90      nicm      698:        if (TAILQ_FIRST(&queue->list)->flags & CMDQ_WAITING) {
1.43      nicm      699:                log_debug("%s %s: waiting", __func__, name);
                    700:                return (0);
                    701:        }
                    702:
                    703:        log_debug("%s %s: enter", __func__, name);
                    704:        for (;;) {
1.90      nicm      705:                item = queue->item = TAILQ_FIRST(&queue->list);
1.44      nicm      706:                if (item == NULL)
1.43      nicm      707:                        break;
1.46      nicm      708:                log_debug("%s %s: %s (%d), flags %x", __func__, name,
                    709:                    item->name, item->type, item->flags);
1.43      nicm      710:
                    711:                /*
                    712:                 * Any item with the waiting flag set waits until an external
                    713:                 * event clears the flag (for example, a job - look at
                    714:                 * run-shell).
                    715:                 */
1.44      nicm      716:                if (item->flags & CMDQ_WAITING)
1.43      nicm      717:                        goto waiting;
                    718:
                    719:                /*
                    720:                 * Items are only fired once, once the fired flag is set, a
                    721:                 * waiting flag can only be cleared by an external event.
                    722:                 */
1.44      nicm      723:                if (~item->flags & CMDQ_FIRED) {
                    724:                        item->time = time(NULL);
                    725:                        item->number = ++number;
1.43      nicm      726:
1.50      nicm      727:                        switch (item->type) {
1.44      nicm      728:                        case CMDQ_COMMAND:
                    729:                                retval = cmdq_fire_command(item);
1.43      nicm      730:
                    731:                                /*
                    732:                                 * If a command returns an error, remove any
                    733:                                 * subsequent commands in the same group.
                    734:                                 */
                    735:                                if (retval == CMD_RETURN_ERROR)
1.44      nicm      736:                                        cmdq_remove_group(item);
1.43      nicm      737:                                break;
1.44      nicm      738:                        case CMDQ_CALLBACK:
                    739:                                retval = cmdq_fire_callback(item);
1.43      nicm      740:                                break;
                    741:                        default:
                    742:                                retval = CMD_RETURN_ERROR;
                    743:                                break;
                    744:                        }
1.44      nicm      745:                        item->flags |= CMDQ_FIRED;
1.43      nicm      746:
                    747:                        if (retval == CMD_RETURN_WAIT) {
1.44      nicm      748:                                item->flags |= CMDQ_WAITING;
1.43      nicm      749:                                goto waiting;
                    750:                        }
                    751:                        items++;
                    752:                }
1.44      nicm      753:                cmdq_remove(item);
1.43      nicm      754:        }
1.90      nicm      755:        queue->item = NULL;
1.43      nicm      756:
                    757:        log_debug("%s %s: exit (empty)", __func__, name);
                    758:        return (items);
1.1       nicm      759:
1.43      nicm      760: waiting:
                    761:        log_debug("%s %s: exit (wait)", __func__, name);
                    762:        return (items);
1.90      nicm      763: }
                    764:
                    765: /* Get running item if any. */
                    766: struct cmdq_item *
                    767: cmdq_running(struct client *c)
                    768: {
                    769:        struct cmdq_list        *queue = cmdq_get(c);
                    770:
                    771:        return (queue->item);
1.43      nicm      772: }
                    773:
                    774: /* Print a guard line. */
                    775: void
1.44      nicm      776: cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
1.43      nicm      777: {
1.44      nicm      778:        struct client   *c = item->client;
1.76      nicm      779:        long             t = item->time;
                    780:        u_int            number = item->number;
1.43      nicm      781:
1.76      nicm      782:        if (c != NULL && (c->flags & CLIENT_CONTROL))
                    783:                file_print(c, "%%%s %ld %u %d\n", guard, t, number, flags);
1.1       nicm      784: }
                    785:
                    786: /* Show message from command. */
1.18      nicm      787: void
1.44      nicm      788: cmdq_print(struct cmdq_item *item, const char *fmt, ...)
1.1       nicm      789: {
1.61      nicm      790:        struct client                   *c = item->client;
                    791:        struct window_pane              *wp;
                    792:        struct window_mode_entry        *wme;
                    793:        va_list                          ap;
                    794:        char                            *tmp, *msg;
1.1       nicm      795:
                    796:        va_start(ap, fmt);
1.76      nicm      797:        xvasprintf(&msg, fmt, ap);
                    798:        va_end(ap);
                    799:
                    800:        log_debug("%s: %s", __func__, msg);
1.1       nicm      801:
                    802:        if (c == NULL)
                    803:                /* nothing */;
                    804:        else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
1.28      nicm      805:                if (~c->flags & CLIENT_UTF8) {
1.76      nicm      806:                        tmp = msg;
1.28      nicm      807:                        msg = utf8_sanitize(tmp);
                    808:                        free(tmp);
1.76      nicm      809:                }
                    810:                file_print(c, "%s\n", msg);
1.1       nicm      811:        } else {
1.96    ! nicm      812:                wp = server_client_get_pane(c);
1.61      nicm      813:                wme = TAILQ_FIRST(&wp->modes);
1.81      nicm      814:                if (wme == NULL || wme->mode != &window_view_mode) {
                    815:                        window_pane_set_mode(wp, NULL, &window_view_mode, NULL,
                    816:                            NULL);
                    817:                }
1.77      nicm      818:                window_copy_add(wp, "%s", msg);
1.1       nicm      819:        }
                    820:
1.76      nicm      821:        free(msg);
1.1       nicm      822: }
                    823:
                    824: /* Show error from command. */
1.18      nicm      825: void
1.44      nicm      826: cmdq_error(struct cmdq_item *item, const char *fmt, ...)
1.1       nicm      827: {
1.44      nicm      828:        struct client   *c = item->client;
                    829:        struct cmd      *cmd = item->cmd;
1.1       nicm      830:        va_list          ap;
1.82      nicm      831:        char            *msg, *tmp;
                    832:        const char      *file;
                    833:        u_int            line;
1.1       nicm      834:
                    835:        va_start(ap, fmt);
1.76      nicm      836:        xvasprintf(&msg, fmt, ap);
1.1       nicm      837:        va_end(ap);
1.57      nicm      838:
                    839:        log_debug("%s: %s", __func__, msg);
1.1       nicm      840:
1.82      nicm      841:        if (c == NULL) {
                    842:                cmd_get_source(cmd, &file, &line);
                    843:                cfg_add_cause("%s:%u: %s", file, line, msg);
                    844:        } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
1.93      nicm      845:                server_add_message("%s message: %s", c->name, msg);
1.28      nicm      846:                if (~c->flags & CLIENT_UTF8) {
                    847:                        tmp = msg;
                    848:                        msg = utf8_sanitize(tmp);
                    849:                        free(tmp);
                    850:                }
1.79      nicm      851:                if (c->flags & CLIENT_CONTROL)
                    852:                        file_print(c, "%s\n", msg);
                    853:                else
                    854:                        file_error(c, "%s\n", msg);
1.13      nicm      855:                c->retval = 1;
1.1       nicm      856:        } else {
                    857:                *msg = toupper((u_char) *msg);
1.94      nicm      858:                status_message_set(c, 1, "%s", msg);
1.1       nicm      859:        }
                    860:
                    861:        free(msg);
                    862: }