Annotation of embedaddon/tmux/cmd-queue.c, revision 1.1

1.1     ! misho       1: /* $OpenBSD$ */
        !             2: 
        !             3: /*
        !             4:  * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
        !             5:  *
        !             6:  * Permission to use, copy, modify, and distribute this software for any
        !             7:  * purpose with or without fee is hereby granted, provided that the above
        !             8:  * copyright notice and this permission notice appear in all copies.
        !             9:  *
        !            10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !            11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            14:  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
        !            15:  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
        !            16:  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            17:  */
        !            18: 
        !            19: #include <sys/types.h>
        !            20: 
        !            21: #include <ctype.h>
        !            22: #include <stdlib.h>
        !            23: #include <string.h>
        !            24: #include <time.h>
        !            25: 
        !            26: #include "tmux.h"
        !            27: 
        !            28: /* Global command queue. */
        !            29: static struct cmdq_list global_queue = TAILQ_HEAD_INITIALIZER(global_queue);
        !            30: 
        !            31: /* Get command queue name. */
        !            32: static const char *
        !            33: cmdq_name(struct client *c)
        !            34: {
        !            35:        static char     s[32];
        !            36: 
        !            37:        if (c == NULL)
        !            38:                return ("<global>");
        !            39:        xsnprintf(s, sizeof s, "<%p>", c);
        !            40:        return (s);
        !            41: }
        !            42: 
        !            43: /* Get command queue from client. */
        !            44: static struct cmdq_list *
        !            45: cmdq_get(struct client *c)
        !            46: {
        !            47:        if (c == NULL)
        !            48:                return (&global_queue);
        !            49:        return (&c->queue);
        !            50: }
        !            51: 
        !            52: /* Append an item. */
        !            53: void
        !            54: cmdq_append(struct client *c, struct cmdq_item *item)
        !            55: {
        !            56:        struct cmdq_list        *queue = cmdq_get(c);
        !            57:        struct cmdq_item        *next;
        !            58: 
        !            59:        do {
        !            60:                next = item->next;
        !            61:                item->next = NULL;
        !            62: 
        !            63:                if (c != NULL)
        !            64:                        c->references++;
        !            65:                item->client = c;
        !            66: 
        !            67:                item->queue = queue;
        !            68:                TAILQ_INSERT_TAIL(queue, item, entry);
        !            69: 
        !            70:                item = next;
        !            71:        } while (item != NULL);
        !            72: }
        !            73: 
        !            74: /* Insert an item. */
        !            75: void
        !            76: cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
        !            77: {
        !            78:        struct client           *c = after->client;
        !            79:        struct cmdq_list        *queue = after->queue;
        !            80:        struct cmdq_item        *next;
        !            81: 
        !            82:        do {
        !            83:                next = item->next;
        !            84:                item->next = NULL;
        !            85: 
        !            86:                if (c != NULL)
        !            87:                        c->references++;
        !            88:                item->client = c;
        !            89: 
        !            90:                item->queue = queue;
        !            91:                if (after->next != NULL)
        !            92:                        TAILQ_INSERT_AFTER(queue, after->next, item, entry);
        !            93:                else
        !            94:                        TAILQ_INSERT_AFTER(queue, after, item, entry);
        !            95:                after->next = item;
        !            96: 
        !            97:                item = next;
        !            98:        } while (item != NULL);
        !            99: }
        !           100: 
        !           101: /* Remove an item. */
        !           102: static void
        !           103: cmdq_remove(struct cmdq_item *item)
        !           104: {
        !           105:        if (item->formats != NULL)
        !           106:                format_free(item->formats);
        !           107: 
        !           108:        if (item->client != NULL)
        !           109:                server_client_unref(item->client);
        !           110: 
        !           111:        if (item->type == CMDQ_COMMAND)
        !           112:                cmd_list_free(item->cmdlist);
        !           113: 
        !           114:        TAILQ_REMOVE(item->queue, item, entry);
        !           115: 
        !           116:        free((void *)item->name);
        !           117:        free(item);
        !           118: }
        !           119: 
        !           120: /* Set command group. */
        !           121: static u_int
        !           122: cmdq_next_group(void)
        !           123: {
        !           124:        static u_int    group;
        !           125: 
        !           126:        return (++group);
        !           127: }
        !           128: 
        !           129: /* Remove all subsequent items that match this item's group. */
        !           130: static void
        !           131: cmdq_remove_group(struct cmdq_item *item)
        !           132: {
        !           133:        struct cmdq_item        *this, *next;
        !           134: 
        !           135:        this = TAILQ_NEXT(item, entry);
        !           136:        while (this != NULL) {
        !           137:                next = TAILQ_NEXT(this, entry);
        !           138:                if (this->group == item->group)
        !           139:                        cmdq_remove(this);
        !           140:                this = next;
        !           141:        }
        !           142: }
        !           143: 
        !           144: /* Get a command for the command queue. */
        !           145: struct cmdq_item *
        !           146: cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current,
        !           147:     struct mouse_event *m, int flags)
        !           148: {
        !           149:        struct cmdq_item        *item, *first = NULL, *last = NULL;
        !           150:        struct cmd              *cmd;
        !           151:        u_int                    group = cmdq_next_group();
        !           152:        char                    *tmp;
        !           153: 
        !           154:        TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
        !           155:                xasprintf(&tmp, "command[%s]", cmd->entry->name);
        !           156: 
        !           157:                item = xcalloc(1, sizeof *item);
        !           158:                item->name = tmp;
        !           159:                item->type = CMDQ_COMMAND;
        !           160: 
        !           161:                item->group = group;
        !           162:                item->flags = flags;
        !           163: 
        !           164:                item->cmdlist = cmdlist;
        !           165:                item->cmd = cmd;
        !           166: 
        !           167:                if (current != NULL)
        !           168:                        cmd_find_copy_state(&item->current, current);
        !           169:                if (m != NULL)
        !           170:                        memcpy(&item->mouse, m, sizeof item->mouse);
        !           171:                cmdlist->references++;
        !           172: 
        !           173:                if (first == NULL)
        !           174:                        first = item;
        !           175:                if (last != NULL)
        !           176:                        last->next = item;
        !           177:                last = item;
        !           178:        }
        !           179:        return (first);
        !           180: }
        !           181: 
        !           182: /* Fire command on command queue. */
        !           183: static enum cmd_retval
        !           184: cmdq_fire_command(struct cmdq_item *item)
        !           185: {
        !           186:        struct client           *c = item->client;
        !           187:        struct cmd              *cmd = item->cmd;
        !           188:        enum cmd_retval          retval;
        !           189:        const char              *name;
        !           190:        struct cmd_find_state   *fsp, fs;
        !           191:        int                      flags;
        !           192: 
        !           193:        flags = !!(cmd->flags & CMD_CONTROL);
        !           194:        cmdq_guard(item, "begin", flags);
        !           195: 
        !           196:        if (cmd_prepare_state(cmd, item) != 0) {
        !           197:                retval = CMD_RETURN_ERROR;
        !           198:                goto out;
        !           199:        }
        !           200:        if (item->client == NULL)
        !           201:                item->client = cmd_find_client(item, NULL, CMD_FIND_QUIET);
        !           202: 
        !           203:        retval = cmd->entry->exec(cmd, item);
        !           204:        if (retval == CMD_RETURN_ERROR)
        !           205:                goto out;
        !           206: 
        !           207:        if (cmd->entry->flags & CMD_AFTERHOOK) {
        !           208:                name = cmd->entry->name;
        !           209:                if (cmd_find_valid_state(&item->state.tflag))
        !           210:                        fsp = &item->state.tflag;
        !           211:                else {
        !           212:                        if (cmd_find_current(&fs, item, CMD_FIND_QUIET) != 0)
        !           213:                                goto out;
        !           214:                        fsp = &fs;
        !           215:                }
        !           216:                hooks_insert(fsp->s->hooks, item, fsp, "after-%s", name);
        !           217:        }
        !           218: 
        !           219: out:
        !           220:        item->client = c;
        !           221:        if (retval == CMD_RETURN_ERROR)
        !           222:                cmdq_guard(item, "error", flags);
        !           223:        else
        !           224:                cmdq_guard(item, "end", flags);
        !           225:        return (retval);
        !           226: }
        !           227: 
        !           228: /* Get a callback for the command queue. */
        !           229: struct cmdq_item *
        !           230: cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
        !           231: {
        !           232:        struct cmdq_item        *item;
        !           233:        char                    *tmp;
        !           234: 
        !           235:        xasprintf(&tmp, "callback[%s]", name);
        !           236: 
        !           237:        item = xcalloc(1, sizeof *item);
        !           238:        item->name = tmp;
        !           239:        item->type = CMDQ_CALLBACK;
        !           240: 
        !           241:        item->group = 0;
        !           242:        item->flags = 0;
        !           243: 
        !           244:        item->cb = cb;
        !           245:        item->data = data;
        !           246: 
        !           247:        return (item);
        !           248: }
        !           249: 
        !           250: /* Fire callback on callback queue. */
        !           251: static enum cmd_retval
        !           252: cmdq_fire_callback(struct cmdq_item *item)
        !           253: {
        !           254:        return (item->cb(item, item->data));
        !           255: }
        !           256: 
        !           257: /* Add a format to command queue. */
        !           258: void
        !           259: cmdq_format(struct cmdq_item *item, const char *key, const char *fmt, ...)
        !           260: {
        !           261:        va_list                  ap;
        !           262:        struct cmdq_item        *loop;
        !           263:        char                    *value;
        !           264: 
        !           265:        va_start(ap, fmt);
        !           266:        xvasprintf(&value, fmt, ap);
        !           267:        va_end(ap);
        !           268: 
        !           269:        for (loop = item; loop != NULL; loop = item->next) {
        !           270:                if (loop->formats == NULL)
        !           271:                        loop->formats = format_create(NULL, FORMAT_NONE, 0);
        !           272:                format_add(loop->formats, key, "%s", value);
        !           273:        }
        !           274: 
        !           275:        free(value);
        !           276: }
        !           277: 
        !           278: /* Process next item on command queue. */
        !           279: u_int
        !           280: cmdq_next(struct client *c)
        !           281: {
        !           282:        struct cmdq_list        *queue = cmdq_get(c);
        !           283:        const char              *name = cmdq_name(c);
        !           284:        struct cmdq_item        *item;
        !           285:        enum cmd_retval          retval;
        !           286:        u_int                    items = 0;
        !           287:        static u_int             number;
        !           288: 
        !           289:        if (TAILQ_EMPTY(queue)) {
        !           290:                log_debug("%s %s: empty", __func__, name);
        !           291:                return (0);
        !           292:        }
        !           293:        if (TAILQ_FIRST(queue)->flags & CMDQ_WAITING) {
        !           294:                log_debug("%s %s: waiting", __func__, name);
        !           295:                return (0);
        !           296:        }
        !           297: 
        !           298:        log_debug("%s %s: enter", __func__, name);
        !           299:        for (;;) {
        !           300:                item = TAILQ_FIRST(queue);
        !           301:                if (item == NULL)
        !           302:                        break;
        !           303:                log_debug("%s %s: %s (%d), flags %x", __func__, name,
        !           304:                    item->name, item->type, item->flags);
        !           305: 
        !           306:                /*
        !           307:                 * Any item with the waiting flag set waits until an external
        !           308:                 * event clears the flag (for example, a job - look at
        !           309:                 * run-shell).
        !           310:                 */
        !           311:                if (item->flags & CMDQ_WAITING)
        !           312:                        goto waiting;
        !           313: 
        !           314:                /*
        !           315:                 * Items are only fired once, once the fired flag is set, a
        !           316:                 * waiting flag can only be cleared by an external event.
        !           317:                 */
        !           318:                if (~item->flags & CMDQ_FIRED) {
        !           319:                        item->time = time(NULL);
        !           320:                        item->number = ++number;
        !           321: 
        !           322:                        switch (item->type)
        !           323:                        {
        !           324:                        case CMDQ_COMMAND:
        !           325:                                retval = cmdq_fire_command(item);
        !           326: 
        !           327:                                /*
        !           328:                                 * If a command returns an error, remove any
        !           329:                                 * subsequent commands in the same group.
        !           330:                                 */
        !           331:                                if (retval == CMD_RETURN_ERROR)
        !           332:                                        cmdq_remove_group(item);
        !           333:                                break;
        !           334:                        case CMDQ_CALLBACK:
        !           335:                                retval = cmdq_fire_callback(item);
        !           336:                                break;
        !           337:                        default:
        !           338:                                retval = CMD_RETURN_ERROR;
        !           339:                                break;
        !           340:                        }
        !           341:                        item->flags |= CMDQ_FIRED;
        !           342: 
        !           343:                        if (retval == CMD_RETURN_WAIT) {
        !           344:                                item->flags |= CMDQ_WAITING;
        !           345:                                goto waiting;
        !           346:                        }
        !           347:                        items++;
        !           348:                }
        !           349:                cmdq_remove(item);
        !           350:        }
        !           351: 
        !           352:        log_debug("%s %s: exit (empty)", __func__, name);
        !           353:        return (items);
        !           354: 
        !           355: waiting:
        !           356:        log_debug("%s %s: exit (wait)", __func__, name);
        !           357:        return (items);
        !           358: }
        !           359: 
        !           360: /* Print a guard line. */
        !           361: void
        !           362: cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
        !           363: {
        !           364:        struct client   *c = item->client;
        !           365: 
        !           366:        if (c == NULL || !(c->flags & CLIENT_CONTROL))
        !           367:                return;
        !           368: 
        !           369:        evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
        !           370:            (long)item->time, item->number, flags);
        !           371:        server_client_push_stdout(c);
        !           372: }
        !           373: 
        !           374: /* Show message from command. */
        !           375: void
        !           376: cmdq_print(struct cmdq_item *item, const char *fmt, ...)
        !           377: {
        !           378:        struct client   *c = item->client;
        !           379:        struct window   *w;
        !           380:        va_list          ap;
        !           381:        char            *tmp, *msg;
        !           382: 
        !           383:        va_start(ap, fmt);
        !           384: 
        !           385:        if (c == NULL)
        !           386:                /* nothing */;
        !           387:        else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
        !           388:                if (~c->flags & CLIENT_UTF8) {
        !           389:                        xvasprintf(&tmp, fmt, ap);
        !           390:                        msg = utf8_sanitize(tmp);
        !           391:                        free(tmp);
        !           392:                        evbuffer_add(c->stdout_data, msg, strlen(msg));
        !           393:                        free(msg);
        !           394:                } else
        !           395:                        evbuffer_add_vprintf(c->stdout_data, fmt, ap);
        !           396:                evbuffer_add(c->stdout_data, "\n", 1);
        !           397:                server_client_push_stdout(c);
        !           398:        } else {
        !           399:                w = c->session->curw->window;
        !           400:                if (w->active->mode != &window_copy_mode) {
        !           401:                        window_pane_reset_mode(w->active);
        !           402:                        window_pane_set_mode(w->active, &window_copy_mode);
        !           403:                        window_copy_init_for_output(w->active);
        !           404:                }
        !           405:                window_copy_vadd(w->active, fmt, ap);
        !           406:        }
        !           407: 
        !           408:        va_end(ap);
        !           409: }
        !           410: 
        !           411: /* Show error from command. */
        !           412: void
        !           413: cmdq_error(struct cmdq_item *item, const char *fmt, ...)
        !           414: {
        !           415:        struct client   *c = item->client;
        !           416:        struct cmd      *cmd = item->cmd;
        !           417:        va_list          ap;
        !           418:        char            *msg;
        !           419:        size_t           msglen;
        !           420:        char            *tmp;
        !           421: 
        !           422:        va_start(ap, fmt);
        !           423:        msglen = xvasprintf(&msg, fmt, ap);
        !           424:        va_end(ap);
        !           425: 
        !           426:        if (c == NULL)
        !           427:                cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg);
        !           428:        else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
        !           429:                if (~c->flags & CLIENT_UTF8) {
        !           430:                        tmp = msg;
        !           431:                        msg = utf8_sanitize(tmp);
        !           432:                        free(tmp);
        !           433:                        msglen = strlen(msg);
        !           434:                }
        !           435:                evbuffer_add(c->stderr_data, msg, msglen);
        !           436:                evbuffer_add(c->stderr_data, "\n", 1);
        !           437:                server_client_push_stderr(c);
        !           438:                c->retval = 1;
        !           439:        } else {
        !           440:                *msg = toupper((u_char) *msg);
        !           441:                status_message_set(c, "%s", msg);
        !           442:        }
        !           443: 
        !           444:        free(msg);
        !           445: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>