diff --git a/Makefile.am b/Makefile.am index 942a9e28..4f267a23 100644 --- a/Makefile.am +++ b/Makefile.am @@ -112,7 +112,6 @@ dist_tmux_SOURCES = \ cmd-send-keys.c \ cmd-set-buffer.c \ cmd-set-environment.c \ - cmd-set-hook.c \ cmd-set-option.c \ cmd-show-environment.c \ cmd-show-messages.c \ @@ -135,7 +134,6 @@ dist_tmux_SOURCES = \ format-draw.c \ grid-view.c \ grid.c \ - hooks.c \ input-keys.c \ input.c \ job.c \ diff --git a/cmd-new-session.c b/cmd-new-session.c index f0a353d8..6818ce9c 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -329,7 +329,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) } cmd_find_from_session(&fs, s, 0); - hooks_insert(s->hooks, item, &fs, "after-new-session"); + cmdq_insert_hook(s, item, &fs, "after-new-session"); free(cwd); free(newname); diff --git a/cmd-new-window.c b/cmd-new-window.c index e3bacff8..1007c597 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -105,7 +105,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) } cmd_find_from_winlink(&fs, new_wl, 0); - hooks_insert(s->hooks, item, &fs, "after-new-window"); + cmdq_insert_hook(s, item, &fs, "after-new-window"); return (CMD_RETURN_NORMAL); } diff --git a/cmd-queue.c b/cmd-queue.c index 03fd5f10..9ce25f5f 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -98,6 +98,60 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) } while (item != NULL); } + +/* Insert a hook. */ +void +cmdq_insert_hook(struct session *s, struct cmdq_item *item, + struct cmd_find_state *fs, const char *fmt, ...) +{ + struct options *oo; + va_list ap; + char *name; + struct cmdq_item *new_item; + struct options_entry *o; + struct options_array_item *a; + struct cmd_list *cmdlist; + + if (item->flags & CMDQ_NOHOOKS) + return; + if (s == NULL) + oo = global_s_options; + else + oo = s->options; + + va_start(ap, fmt); + xvasprintf(&name, fmt, ap); + va_end(ap); + + o = options_get(oo, name); + if (o == NULL) { + free(name); + return; + } + log_debug("running hook %s (parent %p)", name, item); + + a = options_array_first(o); + while (a != NULL) { + cmdlist = options_array_item_value(a)->cmdlist; + if (cmdlist == NULL) { + a = options_array_next(a); + continue; + } + + new_item = cmdq_get_command(cmdlist, fs, NULL, CMDQ_NOHOOKS); + cmdq_format(new_item, "hook", "%s", name); + if (item != NULL) { + cmdq_insert_after(item, new_item); + item = new_item; + } else + cmdq_append(NULL, new_item); + + a = options_array_next(a); + } + + free(name); +} + /* Remove an item. */ static void cmdq_remove(struct cmdq_item *item) @@ -245,7 +299,7 @@ cmdq_fire_command(struct cmdq_item *item) fsp = &fs; else goto out; - hooks_insert(fsp->s->hooks, item, fsp, "after-%s", entry->name); + cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name); } out: diff --git a/cmd-select-pane.c b/cmd-select-pane.c index af0f033b..89c6fb20 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -196,7 +196,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) window_redraw_active_switch(w, wp); if (window_set_active_pane(w, wp, 1)) { cmd_find_from_winlink_pane(current, wl, wp, 0); - hooks_insert(s->hooks, item, current, "after-select-pane"); + cmdq_insert_hook(s, item, current, "after-select-pane"); cmd_select_pane_redraw(w); } diff --git a/cmd-select-window.c b/cmd-select-window.c index f35b8202..54965e89 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -119,7 +119,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) } cmd_find_from_session(current, s, 0); server_redraw_session(s); - hooks_insert(s->hooks, item, current, "after-select-window"); + cmdq_insert_hook(s, item, current, "after-select-window"); } else { /* * If -T and select-window is invoked on same window as @@ -137,7 +137,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) cmd_find_from_session(current, s, 0); server_redraw_session(s); } - hooks_insert(s->hooks, item, current, "after-select-window"); + cmdq_insert_hook(s, item, current, "after-select-window"); } recalculate_sizes(); diff --git a/cmd-set-hook.c b/cmd-set-hook.c deleted file mode 100644 index b04e6335..00000000 --- a/cmd-set-hook.c +++ /dev/null @@ -1,133 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2012 Thomas Adam - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include -#include - -#include "tmux.h" - -/* - * Set or show global or session hooks. - */ - -static enum cmd_retval cmd_set_hook_exec(struct cmd *, struct cmdq_item *); - -const struct cmd_entry cmd_set_hook_entry = { - .name = "set-hook", - .alias = NULL, - - .args = { "gRt:u", 1, 2 }, - .usage = "[-gRu] " CMD_TARGET_SESSION_USAGE " hook-name [command]", - - .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, - - .flags = CMD_AFTERHOOK, - .exec = cmd_set_hook_exec -}; - -const struct cmd_entry cmd_show_hooks_entry = { - .name = "show-hooks", - .alias = NULL, - - .args = { "gt:", 0, 1 }, - .usage = "[-g] " CMD_TARGET_SESSION_USAGE, - - .target = { 't', CMD_FIND_SESSION, 0 }, - - .flags = CMD_AFTERHOOK, - .exec = cmd_set_hook_exec -}; - -static enum cmd_retval -cmd_set_hook_exec(struct cmd *self, struct cmdq_item *item) -{ - struct args *args = self->args; - struct cmd_list *cmdlist; - struct hooks *hooks; - struct hook *hook; - char *cause, *tmp; - const char *name, *cmd, *target; - - if (args_has(args, 'g')) - hooks = global_hooks; - else { - if (item->target.s == NULL) { - target = args_get(args, 't'); - if (target != NULL) - cmdq_error(item, "no such session: %s", target); - else - cmdq_error(item, "no current session"); - return (CMD_RETURN_ERROR); - } - hooks = item->target.s->hooks; - } - - if (self->entry == &cmd_show_hooks_entry) { - hook = hooks_first(hooks); - while (hook != NULL) { - tmp = cmd_list_print(hook->cmdlist); - cmdq_print(item, "%s -> %s", hook->name, tmp); - free(tmp); - - hook = hooks_next(hook); - } - return (CMD_RETURN_NORMAL); - } - - name = args->argv[0]; - if (*name == '\0') { - cmdq_error(item, "invalid hook name"); - return (CMD_RETURN_ERROR); - } - if (args->argc < 2) - cmd = NULL; - else - cmd = args->argv[1]; - - if (cmd != NULL && (args_has(args, 'R') || args_has(args, 'u'))) { - cmdq_error(item, "no command allowed"); - return (CMD_RETURN_ERROR); - } - if (args_has(args, 'R')) { - notify_hook(item, name); - return (CMD_RETURN_NORMAL); - } - if (args_has(args, 'u')) { - hooks_remove(hooks, name); - return (CMD_RETURN_NORMAL); - } - - if (cmd == NULL) { - cmdq_error(item, "no command given"); - return (CMD_RETURN_ERROR); - } - cmdlist = cmd_string_parse(cmd, NULL, 0, &cause); - if (cmdlist == NULL) { - if (cause != NULL) { - cmdq_error(item, "%s", cause); - free(cause); - } - return (CMD_RETURN_ERROR); - } - hooks_add(hooks, name, cmdlist); - cmd_list_free(cmdlist); - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-set-option.c b/cmd-set-option.c index edeb8385..7be561f2 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -65,6 +65,19 @@ const struct cmd_entry cmd_set_window_option_entry = { .exec = cmd_set_option_exec }; +const struct cmd_entry cmd_set_hook_entry = { + .name = "set-hook", + .alias = NULL, + + .args = { "agRt:u", 1, 2 }, + .usage = "[-agRu] " CMD_TARGET_SESSION_USAGE " hook [command]", + + .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, + + .flags = CMD_AFTERHOOK, + .exec = cmd_set_option_exec +}; + static enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) { @@ -87,6 +100,11 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) c = cmd_find_client(item, NULL, 1); argument = format_single(item, args->argv[0], c, s, wl, NULL); + if (self->entry == &cmd_set_hook_entry && args_has(args, 'R')) { + notify_hook(item, argument); + return (CMD_RETURN_NORMAL); + } + /* Parse option name and index. */ name = options_match(argument, &idx, &ambiguous); if (name == NULL) { @@ -200,8 +218,11 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) options_default(oo, options_table_entry(o)); else options_remove(o); - } else - options_array_set(o, idx, NULL, 0); + } else if (options_array_set(o, idx, NULL, 0, &cause) != 0) { + cmdq_error(item, "%s", cause); + free(cause); + goto fail; + } } else if (*name == '@') { if (value == NULL) { cmdq_error(item, "empty value"); @@ -222,9 +243,15 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) if (idx == -1) { if (!append) options_array_clear(o); - options_array_assign(o, value); - } else if (options_array_set(o, idx, value, append) != 0) { - cmdq_error(item, "invalid index: %s", argument); + if (options_array_assign(o, value, &cause) != 0) { + cmdq_error(item, "%s", cause); + free(cause); + goto fail; + } + } else if (options_array_set(o, idx, value, append, + &cause) != 0) { + cmdq_error(item, "%s", cause); + free(cause); goto fail; } } @@ -366,6 +393,8 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, return (-1); } return (0); + case OPTIONS_TABLE_COMMAND: + break; } return (-1); } diff --git a/cmd-show-options.c b/cmd-show-options.c index 642b3f89..4948ff79 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -38,8 +38,8 @@ const struct cmd_entry cmd_show_options_entry = { .name = "show-options", .alias = "show", - .args = { "gqst:vw", 0, 1 }, - .usage = "[-gqsvw] [-t target-session|target-window] [option]", + .args = { "gHqst:vw", 0, 1 }, + .usage = "[-gHqsvw] [-t target-session|target-window] [option]", .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, @@ -60,6 +60,19 @@ const struct cmd_entry cmd_show_window_options_entry = { .exec = cmd_show_options_exec }; +const struct cmd_entry cmd_show_hooks_entry = { + .name = "show-hooks", + .alias = NULL, + + .args = { "gt:", 0, 1 }, + .usage = "[-g] " CMD_TARGET_SESSION_USAGE, + + .target = { 't', CMD_FIND_SESSION, 0 }, + + .flags = CMD_AFTERHOOK, + .exec = cmd_show_options_exec +}; + static enum cmd_retval cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) { @@ -161,15 +174,20 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, struct options_entry *o, int idx) { struct options_array_item *a; - const char *name; - char *value, *tmp, *escaped; + const char *name = options_name(o); + char *value, *tmp = NULL, *escaped; if (idx != -1) { - xasprintf(&tmp, "%s[%d]", options_name(o), idx); + xasprintf(&tmp, "%s[%d]", name, idx); name = tmp; } else { if (options_isarray(o)) { a = options_array_first(o); + if (a == NULL) { + if (!args_has(self->args, 'v')) + cmdq_print(item, "%s", name); + return; + } while (a != NULL) { idx = options_array_item_index(a); cmd_show_options_print(self, item, o, idx); @@ -177,8 +195,6 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, } return; } - tmp = NULL; - name = options_name(o); } value = options_tostring(o, idx, 0); @@ -199,16 +215,28 @@ static enum cmd_retval cmd_show_options_all(struct cmd *self, struct cmdq_item *item, struct options *oo) { - struct options_entry *o; - struct options_array_item *a; - u_int idx; + struct options_entry *o; + struct options_array_item *a; + u_int idx; + int flags; o = options_first(oo); while (o != NULL) { + flags = options_table_entry(o)->flags; + if ((self->entry != &cmd_show_hooks_entry && + !args_has(self->args, 'H') && + (flags & OPTIONS_TABLE_IS_HOOK)) || + (self->entry == &cmd_show_hooks_entry && + (~flags & OPTIONS_TABLE_IS_HOOK))) { + o = options_next(o); + continue; + } if (!options_isarray(o)) cmd_show_options_print(self, item, o, -1); - else { - a = options_array_first(o); + else if ((a = options_array_first(o)) == NULL) { + if (!args_has(self->args, 'v')) + cmdq_print(item, "%s", options_name(o)); + } else { while (a != NULL) { idx = options_array_item_index(a); cmd_show_options_print(self, item, o, idx); diff --git a/cmd-split-window.c b/cmd-split-window.c index e64e9247..e9da1f3e 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -143,7 +143,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) } cmd_find_from_winlink_pane(&fs, wl, new_wp, 0); - hooks_insert(s->hooks, item, &fs, "after-split-window"); + cmdq_insert_hook(s, item, &fs, "after-split-window"); return (CMD_RETURN_NORMAL); } diff --git a/cmd-string.c b/cmd-string.c index 440a0231..058f997c 100644 --- a/cmd-string.c +++ b/cmd-string.c @@ -174,7 +174,10 @@ cmd_string_parse(const char *s, const char *file, u_int line, char **cause) int argc; char **argv; - *cause = NULL; + if (cause != NULL) + *cause = NULL; + log_debug ("%s: %s", __func__, s); + if (cmd_string_split(s, &argc, &argv) != 0) { xasprintf(cause, "invalid or unknown command: %s", s); return (NULL); diff --git a/hooks.c b/hooks.c deleted file mode 100644 index 361f3522..00000000 --- a/hooks.c +++ /dev/null @@ -1,173 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2012 Thomas Adam - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include -#include - -#include "tmux.h" - -struct hooks { - RB_HEAD(hooks_tree, hook) tree; - struct hooks *parent; -}; - -static int hooks_cmp(struct hook *, struct hook *); -RB_GENERATE_STATIC(hooks_tree, hook, entry, hooks_cmp); - -static struct hook *hooks_find1(struct hooks *, const char *); -static void hooks_free1(struct hooks *, struct hook *); - -static int -hooks_cmp(struct hook *hook1, struct hook *hook2) -{ - return (strcmp(hook1->name, hook2->name)); -} - -struct hooks * -hooks_get(struct session *s) -{ - if (s != NULL) - return (s->hooks); - return (global_hooks); -} - -struct hooks * -hooks_create(struct hooks *parent) -{ - struct hooks *hooks; - - hooks = xcalloc(1, sizeof *hooks); - RB_INIT(&hooks->tree); - hooks->parent = parent; - return (hooks); -} - -static void -hooks_free1(struct hooks *hooks, struct hook *hook) -{ - RB_REMOVE(hooks_tree, &hooks->tree, hook); - cmd_list_free(hook->cmdlist); - free((char *)hook->name); - free(hook); -} - -void -hooks_free(struct hooks *hooks) -{ - struct hook *hook, *hook1; - - RB_FOREACH_SAFE(hook, hooks_tree, &hooks->tree, hook1) - hooks_free1(hooks, hook); - free(hooks); -} - -struct hook * -hooks_first(struct hooks *hooks) -{ - return (RB_MIN(hooks_tree, &hooks->tree)); -} - -struct hook * -hooks_next(struct hook *hook) -{ - return (RB_NEXT(hooks_tree, &hooks->tree, hook)); -} - -void -hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist) -{ - struct hook *hook; - - if ((hook = hooks_find1(hooks, name)) != NULL) - hooks_free1(hooks, hook); - - hook = xcalloc(1, sizeof *hook); - hook->name = xstrdup(name); - hook->cmdlist = cmdlist; - hook->cmdlist->references++; - RB_INSERT(hooks_tree, &hooks->tree, hook); -} - -void -hooks_remove(struct hooks *hooks, const char *name) -{ - struct hook *hook; - - if ((hook = hooks_find1(hooks, name)) != NULL) - hooks_free1(hooks, hook); -} - -static struct hook * -hooks_find1(struct hooks *hooks, const char *name) -{ - struct hook hook; - - hook.name = name; - return (RB_FIND(hooks_tree, &hooks->tree, &hook)); -} - -struct hook * -hooks_find(struct hooks *hooks, const char *name) -{ - struct hook hook0, *hook; - - hook0.name = name; - hook = RB_FIND(hooks_tree, &hooks->tree, &hook0); - while (hook == NULL) { - hooks = hooks->parent; - if (hooks == NULL) - break; - hook = RB_FIND(hooks_tree, &hooks->tree, &hook0); - } - return (hook); -} - -void -hooks_insert(struct hooks *hooks, struct cmdq_item *item, - struct cmd_find_state *fs, const char *fmt, ...) -{ - struct hook *hook; - va_list ap; - char *name; - struct cmdq_item *new_item; - - if (item->flags & CMDQ_NOHOOKS) - return; - - va_start(ap, fmt); - xvasprintf(&name, fmt, ap); - va_end(ap); - - hook = hooks_find(hooks, name); - if (hook == NULL) { - free(name); - return; - } - log_debug("running hook %s (parent %p)", name, item); - - new_item = cmdq_get_command(hook->cmdlist, fs, NULL, CMDQ_NOHOOKS); - cmdq_format(new_item, "hook", "%s", name); - if (item != NULL) - cmdq_insert_after(item, new_item); - else - cmdq_append(NULL, new_item); - - free(name); -} diff --git a/notify.c b/notify.c index adef3d4e..163aa1a9 100644 --- a/notify.c +++ b/notify.c @@ -35,13 +35,34 @@ struct notify_entry { }; static void -notify_hook1(struct cmdq_item *item, struct notify_entry *ne) +notify_hook_formats(struct cmdq_item *item, struct session *s, struct window *w, + int pane) { - struct cmd_find_state fs; - struct hook *hook; - struct cmdq_item *new_item; - struct session *s = ne->session; - struct window *w = ne->window; + if (s != NULL) { + cmdq_format(item, "hook_session", "$%u", s->id); + cmdq_format(item, "hook_session_name", "%s", s->name); + } + if (w != NULL) { + cmdq_format(item, "hook_window", "@%u", w->id); + cmdq_format(item, "hook_window_name", "%s", w->name); + } + if (pane != -1) + cmdq_format(item, "hook_pane", "%%%d", pane); +} + +static void +notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) +{ + struct cmd_find_state fs; + struct options *oo; + struct cmdq_item *new_item; + struct session *s = ne->session; + struct window *w = ne->window; + struct options_entry *o; + struct options_array_item *a; + struct cmd_list *cmdlist; + + log_debug("%s: %s", __func__, ne->name); cmd_find_clear_state(&fs, 0); if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs)) @@ -49,26 +70,31 @@ notify_hook1(struct cmdq_item *item, struct notify_entry *ne) else cmd_find_copy_state(&fs, &ne->fs); - hook = hooks_find(hooks_get(fs.s), ne->name); - if (hook == NULL) + if (fs.s == NULL) + oo = global_s_options; + else + oo = fs.s->options; + o = options_get(oo, ne->name); + if (o == NULL) return; - log_debug("notify hook %s", ne->name); - new_item = cmdq_get_command(hook->cmdlist, &fs, NULL, CMDQ_NOHOOKS); - cmdq_format(new_item, "hook", "%s", ne->name); + a = options_array_first(o); + while (a != NULL) { + cmdlist = options_array_item_value(a)->cmdlist; + if (cmdlist == NULL) { + a = options_array_next(a); + continue; + } - if (s != NULL) { - cmdq_format(new_item, "hook_session", "$%u", s->id); - cmdq_format(new_item, "hook_session_name", "%s", s->name); + new_item = cmdq_get_command(cmdlist, &fs, NULL, CMDQ_NOHOOKS); + cmdq_format(new_item, "hook", "%s", ne->name); + notify_hook_formats(new_item, s, w, ne->pane); + + cmdq_insert_after(item, new_item); + item = new_item; + + a = options_array_next(a); } - if (w != NULL) { - cmdq_format(new_item, "hook_window", "@%u", w->id); - cmdq_format(new_item, "hook_window_name", "%s", w->name); - } - if (ne->pane != -1) - cmdq_format(new_item, "hook_pane", "%%%d", ne->pane); - - cmdq_insert_after(item, new_item); } static enum cmd_retval @@ -101,7 +127,7 @@ notify_callback(struct cmdq_item *item, void *data) if (strcmp(ne->name, "session-window-changed") == 0) control_notify_session_window_changed(ne->session); - notify_hook1(item, ne); + notify_insert_hook(item, ne); if (ne->client != NULL) server_client_unref(ne->client); @@ -168,7 +194,7 @@ notify_hook(struct cmdq_item *item, const char *name) ne.window = item->target.w; ne.pane = item->target.wp->id; - notify_hook1(item, &ne); + notify_insert_hook(item, &ne); } void diff --git a/options-table.c b/options-table.c index a8beea06..19cf39f4 100644 --- a/options-table.c +++ b/options-table.c @@ -129,8 +129,19 @@ static const char *options_table_status_format_default[] = { OPTIONS_TABLE_STATUS_FORMAT1, OPTIONS_TABLE_STATUS_FORMAT2, NULL }; +/* Helper for hook options. */ +#define OPTIONS_TABLE_HOOK(hook_name, default_value) \ + { .name = hook_name, \ + .type = OPTIONS_TABLE_COMMAND, \ + .scope = OPTIONS_TABLE_SESSION, \ + .flags = OPTIONS_TABLE_IS_ARRAY|OPTIONS_TABLE_IS_HOOK, \ + .default_str = default_value, \ + .separator = "" \ + } + /* Top-level options. */ const struct options_table_entry options_table[] = { + /* Server options. */ { .name = "buffer-limit", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SERVER, @@ -223,6 +234,7 @@ const struct options_table_entry options_table[] = { .separator = "," }, + /* Session options. */ { .name = "activity-action", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, @@ -541,6 +553,7 @@ const struct options_table_entry options_table[] = { .default_str = " -_@" }, + /* Window options. */ { .name = "aggressive-resize", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, @@ -775,5 +788,66 @@ const struct options_table_entry options_table[] = { .default_num = 1 }, + /* Hook options. */ + OPTIONS_TABLE_HOOK("after-bind-key", ""), + OPTIONS_TABLE_HOOK("after-capture-pane", ""), + OPTIONS_TABLE_HOOK("after-copy-mode", ""), + OPTIONS_TABLE_HOOK("after-display-message", ""), + OPTIONS_TABLE_HOOK("after-display-panes", ""), + OPTIONS_TABLE_HOOK("after-list-buffers", ""), + OPTIONS_TABLE_HOOK("after-list-clients", ""), + OPTIONS_TABLE_HOOK("after-list-keys", ""), + OPTIONS_TABLE_HOOK("after-list-panes", ""), + OPTIONS_TABLE_HOOK("after-list-sessions", ""), + OPTIONS_TABLE_HOOK("after-list-windows", ""), + OPTIONS_TABLE_HOOK("after-load-buffer", ""), + OPTIONS_TABLE_HOOK("after-lock-server", ""), + OPTIONS_TABLE_HOOK("after-new-session", ""), + OPTIONS_TABLE_HOOK("after-new-window", ""), + OPTIONS_TABLE_HOOK("after-paste-buffer", ""), + OPTIONS_TABLE_HOOK("after-pipe-pane", ""), + OPTIONS_TABLE_HOOK("after-queue", ""), + OPTIONS_TABLE_HOOK("after-refresh-client", ""), + OPTIONS_TABLE_HOOK("after-rename-session", ""), + OPTIONS_TABLE_HOOK("after-rename-window", ""), + OPTIONS_TABLE_HOOK("after-resize-pane", ""), + OPTIONS_TABLE_HOOK("after-resize-window", ""), + OPTIONS_TABLE_HOOK("after-save-buffer", ""), + OPTIONS_TABLE_HOOK("after-select-layout", ""), + OPTIONS_TABLE_HOOK("after-select-pane", ""), + OPTIONS_TABLE_HOOK("after-select-window", ""), + OPTIONS_TABLE_HOOK("after-send-keys", ""), + OPTIONS_TABLE_HOOK("after-set-buffer", ""), + OPTIONS_TABLE_HOOK("after-set-environment", ""), + OPTIONS_TABLE_HOOK("after-set-hook", ""), + OPTIONS_TABLE_HOOK("after-set-option", ""), + OPTIONS_TABLE_HOOK("after-show-environment", ""), + OPTIONS_TABLE_HOOK("after-show-messages", ""), + OPTIONS_TABLE_HOOK("after-show-options", ""), + OPTIONS_TABLE_HOOK("after-split-window", ""), + OPTIONS_TABLE_HOOK("after-unbind-key", ""), + OPTIONS_TABLE_HOOK("alert-activity", ""), + OPTIONS_TABLE_HOOK("alert-bell", ""), + OPTIONS_TABLE_HOOK("alert-silence", ""), + OPTIONS_TABLE_HOOK("client-attached", ""), + OPTIONS_TABLE_HOOK("client-detached", ""), + OPTIONS_TABLE_HOOK("client-resized", ""), + OPTIONS_TABLE_HOOK("client-session-changed", ""), + OPTIONS_TABLE_HOOK("pane-died", ""), + OPTIONS_TABLE_HOOK("pane-exited", ""), + OPTIONS_TABLE_HOOK("pane-focus-in", ""), + OPTIONS_TABLE_HOOK("pane-focus-out", ""), + OPTIONS_TABLE_HOOK("pane-mode-changed", ""), + OPTIONS_TABLE_HOOK("pane-set-clipboard", ""), + OPTIONS_TABLE_HOOK("session-closed", ""), + OPTIONS_TABLE_HOOK("session-created", ""), + OPTIONS_TABLE_HOOK("session-renamed", ""), + OPTIONS_TABLE_HOOK("session-window-changed", ""), + OPTIONS_TABLE_HOOK("window-layout-changed", ""), + OPTIONS_TABLE_HOOK("window-linked", ""), + OPTIONS_TABLE_HOOK("window-pane-changed", ""), + OPTIONS_TABLE_HOOK("window-renamed", ""), + OPTIONS_TABLE_HOOK("window-unlinked", ""), + { .name = NULL } }; diff --git a/options.c b/options.c index fb271d5e..c5a776e5 100644 --- a/options.c +++ b/options.c @@ -76,6 +76,9 @@ static struct options_entry *options_add(struct options *, const char *); #define OPTIONS_IS_STYLE(o) \ ((o)->tableentry != NULL && \ (o)->tableentry->type == OPTIONS_TABLE_STYLE) +#define OPTIONS_IS_COMMAND(o) \ + ((o)->tableentry != NULL && \ + (o)->tableentry->type == OPTIONS_TABLE_COMMAND) #define OPTIONS_IS_ARRAY(o) \ ((o)->tableentry != NULL && \ @@ -108,6 +111,8 @@ options_value_free(struct options_entry *o, union options_value *ov) { if (OPTIONS_IS_STRING(o)) free(ov->string); + if (OPTIONS_IS_COMMAND(o) && ov->cmdlist != NULL) + cmd_list_free(ov->cmdlist); } static char * @@ -116,6 +121,8 @@ options_value_tostring(struct options_entry *o, union options_value *ov, { char *s; + if (OPTIONS_IS_COMMAND(o)) + return (cmd_list_print(ov->cmdlist)); if (OPTIONS_IS_STYLE(o)) return (xstrdup(style_tostring(&ov->style))); if (OPTIONS_IS_NUMBER(o)) { @@ -140,6 +147,7 @@ options_value_tostring(struct options_entry *o, union options_value *ov, break; case OPTIONS_TABLE_STRING: case OPTIONS_TABLE_STYLE: + case OPTIONS_TABLE_COMMAND: fatalx("not a number option type"); } return (s); @@ -231,11 +239,12 @@ options_default(struct options *oo, const struct options_table_entry *oe) ov = &o->value; if (oe->flags & OPTIONS_TABLE_IS_ARRAY) { - if (oe->default_arr != NULL) { - for (i = 0; oe->default_arr[i] != NULL; i++) - options_array_set(o, i, oe->default_arr[i], 0); - } else - options_array_assign(o, oe->default_str); + if (oe->default_arr == NULL) { + options_array_assign(o, oe->default_str, NULL); + return (o); + } + for (i = 0; oe->default_arr[i] != NULL; i++) + options_array_set(o, i, oe->default_arr[i], 0, NULL); return (o); } @@ -340,13 +349,22 @@ options_array_get(struct options_entry *o, u_int idx) int options_array_set(struct options_entry *o, u_int idx, const char *value, - int append) + int append, char **cause) { struct options_array_item *a; char *new; + struct cmd_list *cmdlist; - if (!OPTIONS_IS_ARRAY(o)) + if (!OPTIONS_IS_ARRAY(o)) { + *cause = xstrdup("not an array"); return (-1); + } + + if (OPTIONS_IS_COMMAND(o)) { + cmdlist = cmd_string_parse(value, NULL, 0, cause); + if (cmdlist == NULL && *cause != NULL) + return (-1); + } a = options_array_item(o, idx); if (value == NULL) { @@ -355,25 +373,29 @@ options_array_set(struct options_entry *o, u_int idx, const char *value, return (0); } - if (a == NULL) { - a = xcalloc(1, sizeof *a); - a->index = idx; - a->value.string = xstrdup(value); - RB_INSERT(options_array, &o->value.array, a); - } else { - options_value_free(o, &a->value); + if (OPTIONS_IS_STRING(o)) { if (a != NULL && append) xasprintf(&new, "%s%s", a->value.string, value); else new = xstrdup(value); - a->value.string = new; } + if (a == NULL) { + a = xcalloc(1, sizeof *a); + a->index = idx; + RB_INSERT(options_array, &o->value.array, a); + } else + options_value_free(o, &a->value); + + if (OPTIONS_IS_STRING(o)) + a->value.string = new; + else if (OPTIONS_IS_COMMAND(o)) + a->value.cmdlist = cmdlist; return (0); } -void -options_array_assign(struct options_entry *o, const char *s) +int +options_array_assign(struct options_entry *o, const char *s, char **cause) { const char *separator; char *copy, *next, *string; @@ -382,7 +404,18 @@ options_array_assign(struct options_entry *o, const char *s) separator = o->tableentry->separator; if (separator == NULL) separator = " ,"; + if (*separator == '\0') { + if (*s == '\0') + return (0); + for (i = 0; i < UINT_MAX; i++) { + if (options_array_item(o, i) == NULL) + break; + } + return (options_array_set(o, i, s, 0, cause)); + } + if (*s == '\0') + return (0); copy = string = xstrdup(s); while ((next = strsep(&string, separator)) != NULL) { if (*next == '\0') @@ -393,9 +426,13 @@ options_array_assign(struct options_entry *o, const char *s) } if (i == UINT_MAX) break; - options_array_set(o, i, next, 0); + if (options_array_set(o, i, next, 0, cause) != 0) { + free(copy); + return (-1); + } } free(copy); + return (0); } struct options_array_item * diff --git a/session.c b/session.c index 9a32990e..b361bde3 100644 --- a/session.c +++ b/session.c @@ -128,7 +128,6 @@ session_create(const char *prefix, const char *name, const char *cwd, s->environ = env; s->options = oo; - s->hooks = hooks_create(global_hooks); status_update_cache(s); @@ -192,9 +191,7 @@ session_free(__unused int fd, __unused short events, void *arg) if (s->references == 0) { environ_free(s->environ); - options_free(s->options); - hooks_free(s->hooks); free(s->name); free(s); diff --git a/status.c b/status.c index 34ed3778..89912114 100644 --- a/status.c +++ b/status.c @@ -1323,7 +1323,7 @@ status_prompt_complete_list(u_int *size, const char *s) while (a != NULL) { value = options_array_item_value(a)->string; if ((cp = strchr(value, '=')) == NULL) - goto next; + goto next; valuelen = cp - value; if (slen > valuelen || strncmp(value, s, slen) != 0) goto next; diff --git a/tmux.1 b/tmux.1 index 8a5caf09..fa810f7b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3461,7 +3461,7 @@ function key sequences; these have a number included to indicate modifiers such as Shift, Alt or Ctrl. .El .It Xo Ic show-options -.Op Fl gqsvw +.Op Fl gHqsvw .Op Fl t Ar target-session | Ar target-window .Op Ar option .Xc @@ -3493,6 +3493,8 @@ If is set, no error will be returned if .Ar option is unset. +.Fl H +includes hooks (omitted by default). .It Xo Ic show-window-options .Op Fl gv .Op Fl t Ar target-window @@ -3517,6 +3519,26 @@ commands have an .Em after hook and there are a number of hooks not associated with commands. .Pp +Hooks are stored as array options, members of the array are executed in +order when the hook is triggered. +Hooks may be configured with the +.Ic set-hook +or +.Ic set-option +commands and displayed with +.Ic show-hooks +or +.Ic show-options +.Fl H . +The following two commands are equivalent: +.Bd -literal -offset indent. +set-hook -g pane-mode-changed[42] 'set -g status-left-style bg=red' +set-option -g pane-mode-changed[42] 'set -g status-left-style bg=red' +.Ed +.Pp +Setting a hook without specifying an array index clears the hook and sets the +first member of the array. +.Pp A command's after hook is run after it completes, except when the command is run as part of a hook itself. @@ -3527,7 +3549,7 @@ For example, the following command adds a hook to select the even-vertical layout after every .Ic split-window : .Bd -literal -offset indent -set-hook after-split-window "selectl even-vertical" +set-hook -g after-split-window "selectl even-vertical" .Ed .Pp All the notifications listed in the @@ -3591,7 +3613,7 @@ Run when a window is unlinked from a session. Hooks are managed with these commands: .Bl -tag -width Ds .It Xo Ic set-hook -.Op Fl gRu +.Op Fl agRu .Op Fl t Ar target-session .Ar hook-name .Ar command @@ -3613,6 +3635,8 @@ hooks (for .Ar target-session with .Fl t ) . +.Fl a +appends to a hook. Like options, session hooks inherit from the global ones. .Pp With diff --git a/tmux.c b/tmux.c index f4265e0a..685827d4 100644 --- a/tmux.c +++ b/tmux.c @@ -36,7 +36,6 @@ struct options *global_options; /* server options */ struct options *global_s_options; /* session options */ struct options *global_w_options; /* window options */ struct environ *global_environ; -struct hooks *global_hooks; struct timeval start_time; const char *socket_path; @@ -312,8 +311,6 @@ main(int argc, char **argv) flags |= CLIENT_UTF8; } - global_hooks = hooks_create(NULL); - global_environ = environ_create(); for (var = environ; *var != NULL; var++) environ_put(global_environ, *var); diff --git a/tmux.h b/tmux.h index 03ed41af..d6f8a376 100644 --- a/tmux.h +++ b/tmux.h @@ -685,15 +685,6 @@ struct style { u_int range_argument; }; -/* Hook data structures. */ -struct hook { - const char *name; - - struct cmd_list *cmdlist; - - RB_ENTRY(hook) entry; -}; - /* Virtual screen. */ struct screen_sel; struct screen_titles; @@ -997,7 +988,6 @@ struct session { int statusat; u_int statuslines; - struct hooks *hooks; struct options *options; #define SESSION_PASTING 0x1 @@ -1523,6 +1513,7 @@ union options_value { long long number; struct style style; struct options_array array; + struct cmd_list *cmdlist; }; /* Option table entries. */ @@ -1533,17 +1524,19 @@ enum options_table_type { OPTIONS_TABLE_COLOUR, OPTIONS_TABLE_FLAG, OPTIONS_TABLE_CHOICE, - OPTIONS_TABLE_STYLE + OPTIONS_TABLE_STYLE, + OPTIONS_TABLE_COMMAND }; enum options_table_scope { OPTIONS_TABLE_NONE, OPTIONS_TABLE_SERVER, OPTIONS_TABLE_SESSION, - OPTIONS_TABLE_WINDOW, + OPTIONS_TABLE_WINDOW }; #define OPTIONS_TABLE_IS_ARRAY 0x1 +#define OPTIONS_TABLE_IS_HOOK 0x2 struct options_table_entry { const char *name; @@ -1601,7 +1594,6 @@ struct spawn_context { }; /* tmux.c */ -extern struct hooks *global_hooks; extern struct options *global_options; extern struct options *global_s_options; extern struct options *global_w_options; @@ -1695,20 +1687,6 @@ u_int format_width(const char *); char *format_trim_left(const char *, u_int); char *format_trim_right(const char *, u_int); -/* hooks.c */ -struct hook; -struct hooks *hooks_get(struct session *); -struct hooks *hooks_create(struct hooks *); -void hooks_free(struct hooks *); -struct hook *hooks_first(struct hooks *); -struct hook *hooks_next(struct hook *); -void hooks_add(struct hooks *, const char *, struct cmd_list *); -void hooks_copy(struct hooks *, struct hooks *); -void hooks_remove(struct hooks *, const char *); -struct hook *hooks_find(struct hooks *, const char *); -void printflike(4, 5) hooks_insert(struct hooks *, struct cmdq_item *, - struct cmd_find_state *, const char *, ...); - /* notify.c */ void notify_hook(struct cmdq_item *, const char *); void notify_input(struct window_pane *, struct evbuffer *); @@ -1736,8 +1714,9 @@ void options_remove(struct options_entry *); void options_array_clear(struct options_entry *); union options_value *options_array_get(struct options_entry *, u_int); int options_array_set(struct options_entry *, u_int, const char *, - int); -void options_array_assign(struct options_entry *, const char *); + int, char **); +int options_array_assign(struct options_entry *, const char *, + char **); struct options_array_item *options_array_first(struct options_entry *); struct options_array_item *options_array_next(struct options_array_item *); u_int options_array_item_index(struct options_array_item *); @@ -1958,6 +1937,8 @@ struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmd_find_state *, struct cmdq_item *cmdq_get_callback1(const char *, cmdq_cb, void *); void cmdq_insert_after(struct cmdq_item *, struct cmdq_item *); void cmdq_append(struct client *, struct cmdq_item *); +void cmdq_insert_hook(struct session *, struct cmdq_item *, + struct cmd_find_state *, const char *, ...); void printflike(3, 4) cmdq_format(struct cmdq_item *, const char *, const char *, ...); u_int cmdq_next(struct client *);