From 1764f66b7d1ed0e494cfa8967c78ace8728ee86c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Dec 2019 09:22:33 +0000 Subject: [PATCH 01/15] When adding a list with multiple commands to the queue, the next item to insert after needs to be the last one added, not the first. Reported by Jason Kim in GitHub issue 2023. --- cfg.c | 8 ++++---- cmd-queue.c | 23 ++++++++++++++++------- cmd-source-file.c | 1 + key-bindings.c | 4 ++-- notify.c | 4 +--- tmux.h | 4 ++-- 6 files changed, 26 insertions(+), 18 deletions(-) diff --git a/cfg.c b/cfg.c index 8509ad1b..92c87225 100644 --- a/cfg.c +++ b/cfg.c @@ -185,9 +185,9 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); if (item != NULL) - cmdq_insert_after(item, new_item0); + new_item0 = cmdq_insert_after(item, new_item0); else - cmdq_append(NULL, new_item0); + new_item0 = cmdq_append(NULL, new_item0); cmd_list_free(pr->cmdlist); if (new_item != NULL) @@ -231,9 +231,9 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path, new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); if (item != NULL) - cmdq_insert_after(item, new_item0); + new_item0 = cmdq_insert_after(item, new_item0); else - cmdq_append(NULL, new_item0); + new_item0 = cmdq_append(NULL, new_item0); cmd_list_free(pr->cmdlist); if (new_item != NULL) diff --git a/cmd-queue.c b/cmd-queue.c index 69e4f6b2..75b5d8f9 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -53,12 +53,16 @@ cmdq_get(struct client *c) } /* Append an item. */ -void +struct cmdq_item * cmdq_append(struct client *c, struct cmdq_item *item) { struct cmdq_list *queue = cmdq_get(c); struct cmdq_item *next; + TAILQ_FOREACH(next, queue, entry) { + log_debug("%s %s: queue %s (%u)", __func__, cmdq_name(c), + next->name, next->group); + } do { next = item->next; item->next = NULL; @@ -73,16 +77,21 @@ cmdq_append(struct client *c, struct cmdq_item *item) item = next; } while (item != NULL); + return (TAILQ_LAST(queue, cmdq_list)); } /* Insert an item. */ -void +struct cmdq_item * cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) { struct client *c = after->client; struct cmdq_list *queue = after->queue; struct cmdq_item *next; + TAILQ_FOREACH(next, queue, entry) { + log_debug("%s %s: queue %s (%u)", __func__, cmdq_name(c), + next->name, next->group); + } do { next = item->next; item->next = after->next; @@ -100,6 +109,7 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) after = item; item = next; } while (item != NULL); + return (after); } /* Insert a hook. */ @@ -143,11 +153,10 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, 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); + if (item != NULL) + item = cmdq_insert_after(item, new_item); + else + item = cmdq_append(NULL, new_item); a = options_array_next(a); } diff --git a/cmd-source-file.c b/cmd-source-file.c index 6af1a6d0..c34cdf41 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -114,6 +114,7 @@ cmd_source_file_done(struct client *c, const char *path, int error, static void cmd_source_file_add(struct cmd_source_file_data *cdata, const char *path) { + log_debug("%s: %s", __func__, path); cdata->files = xreallocarray(cdata->files, cdata->nfiles + 1, sizeof *cdata->files); cdata->files[cdata->nfiles++] = xstrdup(path); diff --git a/key-bindings.c b/key-bindings.c index d4fada6a..7cd834a2 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -520,8 +520,8 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item, new_item->shared->flags |= CMDQ_SHARED_REPEAT; } if (item != NULL) - cmdq_insert_after(item, new_item); + new_item = cmdq_insert_after(item, new_item); else - cmdq_append(c, new_item); + new_item = cmdq_append(c, new_item); return (new_item); } diff --git a/notify.c b/notify.c index 3079f0eb..c91a4399 100644 --- a/notify.c +++ b/notify.c @@ -90,9 +90,7 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) 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; + item = cmdq_insert_after(item, new_item); a = options_array_next(a); } diff --git a/tmux.h b/tmux.h index 8b23bfae..72be101b 100644 --- a/tmux.h +++ b/tmux.h @@ -2134,8 +2134,8 @@ struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmd_find_state *, #define cmdq_get_callback(cb, data) cmdq_get_callback1(#cb, cb, data) struct cmdq_item *cmdq_get_callback1(const char *, cmdq_cb, void *); struct cmdq_item *cmdq_get_error(const char *); -void cmdq_insert_after(struct cmdq_item *, struct cmdq_item *); -void cmdq_append(struct client *, struct cmdq_item *); +struct cmdq_item *cmdq_insert_after(struct cmdq_item *, struct cmdq_item *); +struct cmdq_item *cmdq_append(struct client *, struct cmdq_item *); void cmdq_insert_hook(struct session *, struct cmdq_item *, struct cmd_find_state *, const char *, ...); void cmdq_continue(struct cmdq_item *); From 5cd00eda0b7f1775bd3c8ce9497c72ddfada9592 Mon Sep 17 00:00:00 2001 From: tim Date: Sat, 21 Dec 2019 17:30:48 +0000 Subject: [PATCH 02/15] Restore source-file -q behaviour, broken in r1.42; OK nicm@ --- cmd-source-file.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd-source-file.c b/cmd-source-file.c index c34cdf41..46dc6d94 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -125,7 +125,6 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmd_source_file_data *cdata; - int flags = 0; struct client *c = item->client; enum cmd_retval retval = CMD_RETURN_NORMAL; char *pattern, *cwd; @@ -161,7 +160,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) if ((result = glob(pattern, 0, NULL, &g)) != 0) { if (result != GLOB_NOMATCH || - (~flags & CMD_PARSE_QUIET)) { + (~cdata->flags & CMD_PARSE_QUIET)) { if (result == GLOB_NOMATCH) error = strerror(ENOENT); else if (result == GLOB_NOSPACE) From 07e37479c214023f5d0107414a6e5f414d37197a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Dec 2019 09:57:11 +0000 Subject: [PATCH 03/15] Fix name of option, GitHub issue 2030. --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 56ebeb87..ce882193 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4360,7 +4360,7 @@ interface, for example .Ic status-style for the status line. In addition, embedded styles may be specified in format options, such as -.Ic status-left-format , +.Ic status-left , by enclosing them in .Ql #[ and From 817d199cbb6720f1848452824f7b93cdc1a24111 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 26 Dec 2019 11:04:58 +0000 Subject: [PATCH 04/15] Add a number of new formats to inspect what sessions and clients a window is present or active in. From Tyler Culp in GitHub issue 2034. --- format.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ session.c | 15 +++- tmux.1 | 10 +++ tmux.h | 1 + 4 files changed, 238 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index e74de9c1..8ee4fa61 100644 --- a/format.c +++ b/format.c @@ -456,6 +456,35 @@ format_cb_pid(__unused struct format_tree *ft, struct format_entry *fe) xasprintf(&fe->value, "%ld", (long)getpid()); } +/* Callback for session_attached_list. */ +static void +format_cb_session_attached_list(struct format_tree *ft, struct format_entry *fe) +{ + struct session *s = ft->s; + struct client *loop; + struct evbuffer *buffer; + int size; + + if (s == NULL) + return; + + buffer = evbuffer_new(); + if (buffer == NULL) + fatalx("out of memory"); + + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->session == s) { + if (EVBUFFER_LENGTH(buffer) > 0) + evbuffer_add(buffer, ",", 1); + evbuffer_add_printf(buffer, "%s", loop->name); + } + } + + if ((size = EVBUFFER_LENGTH(buffer)) != 0) + xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + evbuffer_free(buffer); +} + /* Callback for session_alerts. */ static void format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe) @@ -528,6 +557,128 @@ format_cb_window_stack_index(struct format_tree *ft, struct format_entry *fe) fe->value = xstrdup("0"); } +/* Callback for window_linked_sessions_list. */ +static void +format_cb_window_linked_sessions_list(struct format_tree *ft, + struct format_entry *fe) +{ + struct window *w = ft->wl->window; + struct winlink *wl; + struct evbuffer *buffer; + int size; + + buffer = evbuffer_new(); + if (buffer == NULL) + fatalx("out of memory"); + + TAILQ_FOREACH(wl, &w->winlinks, wentry) { + if (EVBUFFER_LENGTH(buffer) > 0) + evbuffer_add(buffer, ",", 1); + evbuffer_add_printf(buffer, "%s", wl->session->name); + } + + if ((size = EVBUFFER_LENGTH(buffer)) != 0) + xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + evbuffer_free(buffer); +} + +/* Callback for window_active_sessions. */ +static void +format_cb_window_active_sessions(struct format_tree *ft, + struct format_entry *fe) +{ + struct window *w = ft->wl->window; + struct winlink *wl; + u_int n = 0; + + TAILQ_FOREACH(wl, &w->winlinks, wentry) { + if (wl->session->curw == wl) + n++; + } + + xasprintf(&fe->value, "%u", n); +} + +/* Callback for window_active_sessions_list. */ +static void +format_cb_window_active_sessions_list(struct format_tree *ft, + struct format_entry *fe) +{ + struct window *w = ft->wl->window; + struct winlink *wl; + struct evbuffer *buffer; + int size; + + buffer = evbuffer_new(); + if (buffer == NULL) + fatalx("out of memory"); + + TAILQ_FOREACH(wl, &w->winlinks, wentry) { + if (wl->session->curw == wl) { + if (EVBUFFER_LENGTH(buffer) > 0) + evbuffer_add(buffer, ",", 1); + evbuffer_add_printf(buffer, "%s", wl->session->name); + } + } + + if ((size = EVBUFFER_LENGTH(buffer)) != 0) + xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + evbuffer_free(buffer); +} + +/* Callback for window_active_clients. */ +static void +format_cb_window_active_clients(struct format_tree *ft, struct format_entry *fe) +{ + struct window *w = ft->wl->window; + struct client *loop; + struct session *client_session; + u_int n = 0; + + TAILQ_FOREACH(loop, &clients, entry) { + client_session = loop->session; + if (client_session == NULL) + continue; + + if (w == client_session->curw->window) + n++; + } + + xasprintf(&fe->value, "%u", n); +} + +/* Callback for window_active_clients_list. */ +static void +format_cb_window_active_clients_list(struct format_tree *ft, + struct format_entry *fe) +{ + struct window *w = ft->wl->window; + struct client *loop; + struct session *client_session; + struct evbuffer *buffer; + int size; + + buffer = evbuffer_new(); + if (buffer == NULL) + fatalx("out of memory"); + + TAILQ_FOREACH(loop, &clients, entry) { + client_session = loop->session; + if (client_session == NULL) + continue; + + if (w == client_session->curw->window) { + if (EVBUFFER_LENGTH(buffer) > 0) + evbuffer_add(buffer, ",", 1); + evbuffer_add_printf(buffer, "%s", loop->name); + } + } + + if ((size = EVBUFFER_LENGTH(buffer)) != 0) + xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + evbuffer_free(buffer); +} + /* Callback for window_layout. */ static void format_cb_window_layout(struct format_tree *ft, struct format_entry *fe) @@ -662,11 +813,52 @@ format_cb_session_group_list(struct format_tree *ft, struct format_entry *fe) buffer = evbuffer_new(); if (buffer == NULL) fatalx("out of memory"); + TAILQ_FOREACH(loop, &sg->sessions, gentry) { if (EVBUFFER_LENGTH(buffer) > 0) evbuffer_add(buffer, ",", 1); evbuffer_add_printf(buffer, "%s", loop->name); } + + if ((size = EVBUFFER_LENGTH(buffer)) != 0) + xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + evbuffer_free(buffer); +} + +/* Callback for session_group_attached_list. */ +static void +format_cb_session_group_attached_list(struct format_tree *ft, + struct format_entry *fe) +{ + struct session *s = ft->s, *client_session, *session_loop; + struct session_group *sg; + struct client *loop; + struct evbuffer *buffer; + int size; + + if (s == NULL) + return; + sg = session_group_contains(s); + if (sg == NULL) + return; + + buffer = evbuffer_new(); + if (buffer == NULL) + fatalx("out of memory"); + + TAILQ_FOREACH(loop, &clients, entry) { + client_session = loop->session; + if (client_session == NULL) + continue; + TAILQ_FOREACH(session_loop, &sg->sessions, gentry) { + if (session_loop == client_session){ + if (EVBUFFER_LENGTH(buffer) > 0) + evbuffer_add(buffer, ",", 1); + evbuffer_add_printf(buffer, "%s", loop->name); + } + } + } + if ((size = EVBUFFER_LENGTH(buffer)) != 0) xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); evbuffer_free(buffer); @@ -2125,8 +2317,14 @@ format_defaults_session(struct format_tree *ft, struct session *s) format_add(ft, "session_group", "%s", sg->name); format_add(ft, "session_group_size", "%u", session_group_count (sg)); + format_add(ft, "session_group_attached", "%u", + session_group_attached_count (sg)); + format_add(ft, "session_group_many_attached", "%u", + session_group_attached_count (sg) > 1); format_add_cb(ft, "session_group_list", format_cb_session_group_list); + format_add_cb(ft, "session_group_attached_list", + format_cb_session_group_attached_list); } format_add_tv(ft, "session_created", &s->creation_time); @@ -2135,6 +2333,8 @@ format_defaults_session(struct format_tree *ft, struct session *s) format_add(ft, "session_attached", "%u", s->attached); format_add(ft, "session_many_attached", "%d", s->attached > 1); + format_add_cb(ft, "session_attached_list", + format_cb_session_attached_list); format_add_cb(ft, "session_alerts", format_cb_session_alerts); format_add_cb(ft, "session_stack", format_cb_session_stack); @@ -2249,6 +2449,14 @@ format_defaults_winlink(struct format_tree *ft, struct winlink *wl) format_add_cb(ft, "window_stack_index", format_cb_window_stack_index); format_add(ft, "window_flags", "%s", window_printable_flags(wl)); format_add(ft, "window_active", "%d", wl == s->curw); + format_add_cb(ft, "window_active_sessions", + format_cb_window_active_sessions); + format_add_cb(ft, "window_active_sessions_list", + format_cb_window_active_sessions_list); + format_add_cb(ft, "window_active_clients", + format_cb_window_active_clients); + format_add_cb(ft, "window_active_clients_list", + format_cb_window_active_clients_list); format_add(ft, "window_start_flag", "%d", !!(wl == RB_MIN(winlinks, &s->windows))); @@ -2269,6 +2477,11 @@ format_defaults_winlink(struct format_tree *ft, struct winlink *wl) format_add(ft, "window_last_flag", "%d", !!(wl == TAILQ_FIRST(&s->lastw))); format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window)); + + format_add_cb(ft, "window_linked_sessions_list", + format_cb_window_linked_sessions_list); + format_add(ft, "window_linked_sessions", "%u", + wl->window->references); } /* Set default format keys for a window pane. */ diff --git a/session.c b/session.c index b1f49e41..be9c8e07 100644 --- a/session.c +++ b/session.c @@ -569,7 +569,20 @@ session_group_count(struct session_group *sg) n = 0; TAILQ_FOREACH(s, &sg->sessions, gentry) - n++; + n++; + return (n); +} + +/* Count number of clients attached to sessions in session group. */ +u_int +session_group_attached_count(struct session_group *sg) +{ + struct session *s; + u_int n; + + n = 0; + TAILQ_FOREACH(s, &sg->sessions, gentry) + n += s->attached; return (n); } diff --git a/tmux.1 b/tmux.1 index ce882193..ef288e5a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4310,10 +4310,14 @@ The following variables are available, where appropriate: .It Li "session_activity" Ta "" Ta "Time of session last activity" .It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" .It Li "session_attached" Ta "" Ta "Number of clients session is attached to" +.It Li "session_attached_list" Ta "" Ta "List of clients session is attached to" .It Li "session_created" Ta "" Ta "Time session created" .It Li "session_format" Ta "" Ta "1 if format is for a session" .It Li "session_group" Ta "" Ta "Name of session group" +.It Li "session_group_attached" Ta "" Ta "Number of clients sessions in group are attached to" +.It Li "session_group_attached_list" Ta "" Ta "List of clients sessions in group are attached to" .It Li "session_group_list" Ta "" Ta "List of sessions in group" +.It Li "session_group_many_attached" Ta "" Ta "1 if multiple clients attached to sessions in group" .It Li "session_group_size" Ta "" Ta "Size of session group" .It Li "session_grouped" Ta "" Ta "1 if session in a group" .It Li "session_id" Ta "" Ta "Unique session ID" @@ -4325,6 +4329,10 @@ The following variables are available, where appropriate: .It Li "socket_path" Ta "" Ta "Server socket path" .It Li "start_time" Ta "" Ta "Server start time" .It Li "window_active" Ta "" Ta "1 if window active" +.It Li "window_active_clients" Ta "" Ta "Number of clients viewing this window" +.It Li "window_active_clients_list" Ta "" Ta "List of clients viewing this window" +.It Li "window_active_sessions" Ta "" Ta "Number of sessions on which this window is active" +.It Li "window_active_sessions_list" Ta "" Ta "List of sessions on which this window is active" .It Li "window_activity" Ta "" Ta "Time of window last activity" .It Li "window_activity_flag" Ta "" Ta "1 if window has activity" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell" @@ -4340,6 +4348,8 @@ The following variables are available, where appropriate: .It Li "window_last_flag" Ta "" Ta "1 if window is the last used" .It Li "window_layout" Ta "" Ta "Window layout description, ignoring zoomed window panes" .It Li "window_linked" Ta "" Ta "1 if window is linked across sessions" +.It Li "window_linked_sessions" Ta "" Ta "Number of sessions this window is linked to" +.It Li "window_linked_sessions_list" Ta "" Ta "List of sessions this window is linked to" .It Li "window_marked_flag" Ta "" Ta "1 if window contains the marked pane" .It Li "window_name" Ta "#W" Ta "Name of window" .It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client" diff --git a/tmux.h b/tmux.h index 72be101b..2315f643 100644 --- a/tmux.h +++ b/tmux.h @@ -2689,6 +2689,7 @@ void session_group_add(struct session_group *, struct session *); void session_group_synchronize_to(struct session *); void session_group_synchronize_from(struct session *); u_int session_group_count(struct session_group *); +u_int session_group_attached_count(struct session_group *); void session_renumber_windows(struct session *); /* utf8.c */ From 88ee5b1a73c33c396580a1eb0a0605de98a79fa3 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 26 Dec 2019 14:48:29 +0000 Subject: [PATCH 05/15] Pass correct value into iterator callback for time formats. --- format.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/format.c b/format.c index 8ee4fa61..eaff6115 100644 --- a/format.c +++ b/format.c @@ -1150,12 +1150,12 @@ format_each(struct format_tree *ft, void (*cb)(const char *, const char *, void *), void *arg) { struct format_entry *fe; - static char s[64]; + char s[64]; RB_FOREACH(fe, format_entry_tree, &ft->tree) { if (fe->t != 0) { xsnprintf(s, sizeof s, "%lld", (long long)fe->t); - cb(fe->key, fe->value, s); + cb(fe->key, s, arg); } else { if (fe->value == NULL && fe->cb != NULL) { fe->cb(ft, fe); @@ -1198,8 +1198,7 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) static void format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) { - struct format_entry *fe; - struct format_entry *fe_now; + struct format_entry *fe, *fe_now; fe = xmalloc(sizeof *fe); fe->key = xstrdup(key); From 4ea07716de541aea5b8118ac0cfc4bc3bd30cb80 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 27 Dec 2019 18:42:49 +0000 Subject: [PATCH 06/15] Support regex search in copy mode, from Anindya Mukherjee in GitHub issue 2038. --- window-copy.c | 397 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 359 insertions(+), 38 deletions(-) diff --git a/window-copy.c b/window-copy.c index 4787fb27..6e80daad 100644 --- a/window-copy.c +++ b/window-copy.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -57,18 +58,29 @@ static int window_copy_search_lr(struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); static int window_copy_search_rl(struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); +static int window_copy_search_lr_regex(struct grid *, struct grid *, + u_int *, u_int *, u_int, u_int, u_int, int); +static int window_copy_search_rl_regex(struct grid *, struct grid *, + u_int *, u_int *, u_int, u_int, u_int, int); +static int window_copy_last_regex(struct grid *gd, u_int py, u_int first, + u_int last, u_int len, u_int *ppx, u_int *psx, + const char *buf, const regex_t *preg, int eflags); +static char *window_copy_stringify(struct grid *, u_int, u_int, u_int, + char *, u_int *); +static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *, u_int *, + const char *str); static int window_copy_search_marks(struct window_mode_entry *, - struct screen *); + struct screen *, int); static void window_copy_clear_marks(struct window_mode_entry *); static void window_copy_move_left(struct screen *, u_int *, u_int *, int); static void window_copy_move_right(struct screen *, u_int *, u_int *, int); static int window_copy_is_lowercase(const char *); static int window_copy_search_jump(struct window_mode_entry *, struct grid *, struct grid *, u_int, u_int, u_int, int, int, - int); -static int window_copy_search(struct window_mode_entry *, int); -static int window_copy_search_up(struct window_mode_entry *); -static int window_copy_search_down(struct window_mode_entry *); + int, int); +static int window_copy_search(struct window_mode_entry *, int, int); +static int window_copy_search_up(struct window_mode_entry *, int); +static int window_copy_search_down(struct window_mode_entry *, int); static void window_copy_goto_line(struct window_mode_entry *, const char *); static void window_copy_update_cursor(struct window_mode_entry *, u_int, u_int); @@ -622,7 +634,7 @@ window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) screen_write_stop(&ctx); if (search) - window_copy_search_marks(wme, NULL); + window_copy_search_marks(wme, NULL, 1); data->searchx = data->cx; data->searchy = data->cy; data->searcho = data->oy; @@ -1458,10 +1470,10 @@ window_copy_cmd_search_again(struct window_copy_cmd_state *cs) if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) - window_copy_search_up(wme); + window_copy_search_up(wme, 1); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_down(wme); + window_copy_search_down(wme, 1); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1475,10 +1487,10 @@ window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) - window_copy_search_down(wme); + window_copy_search_down(wme, 1); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_up(wme); + window_copy_search_up(wme, 1); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1696,7 +1708,7 @@ window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; for (; np != 0; np--) - window_copy_search_up(wme); + window_copy_search_up(wme, 1); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1731,7 +1743,7 @@ window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) if (data->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHDOWN; for (; np != 0; np--) - window_copy_search_down(wme); + window_copy_search_down(wme, 1); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1767,7 +1779,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) data->searchtype = WINDOW_COPY_SEARCHUP; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_up(wme)) { + if (!window_copy_search_up(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -1776,7 +1788,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) data->searchtype = WINDOW_COPY_SEARCHDOWN; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_down(wme)) { + if (!window_copy_search_down(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -1816,7 +1828,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) data->searchtype = WINDOW_COPY_SEARCHDOWN; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_down(wme)) { + if (!window_copy_search_down(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -1825,7 +1837,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) data->searchtype = WINDOW_COPY_SEARCHUP; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_up(wme)) { + if (!window_copy_search_up(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2152,6 +2164,297 @@ window_copy_search_rl(struct grid *gd, return (0); } +static int +window_copy_search_lr_regex(struct grid *gd, struct grid *sgd, + u_int *ppx, u_int *psx, u_int py, u_int first, u_int last, int cis) +{ + int cflags = REG_EXTENDED, eflags = 0; + u_int endline, foundx, foundy, len, pywrap, size = 1; + u_int ssize = 1; + char *buf, *sbuf; + regex_t reg; + regmatch_t regmatch; + struct grid_line *gl; + + /* + * This can happen during search if the last match was the last + * character on a line. + */ + if (first >= last) + return (0); + + sbuf = xmalloc(ssize); + sbuf[0] = '\0'; + sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize); + if (sbuf == NULL) + return (0); + + /* Set flags for regex search. */ + if (cis) + cflags |= REG_ICASE; + if (regcomp(®, sbuf, cflags) != 0) { + free(sbuf); + return (0); + } + if (first != 0) + eflags |= REG_NOTBOL; + + /* Need to look at the entire string. */ + buf = xmalloc(size); + buf[0] = '\0'; + buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size); + len = gd->sx - first; + endline = gd->hsize + gd->sy - 1; + pywrap = py; + while (buf != NULL && pywrap <= endline) { + gl = grid_get_line(gd, pywrap); + if (~gl->flags & GRID_LINE_WRAPPED) + break; + pywrap++; + buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size); + len += gd->sx; + } + + if (regexec(®, buf, 1, ®match, eflags) == 0) { + foundx = first; + foundy = py; + window_copy_cstrtocellpos(gd, len, &foundx, &foundy, + buf + regmatch.rm_so); + if (foundy == py && foundx < last) { + *ppx = foundx; + len -= foundx - first; + window_copy_cstrtocellpos(gd, len, &foundx, &foundy, + buf + regmatch.rm_eo); + *psx = foundx; + while (foundy > py) { + *psx += gd->sx; + foundy--; + } + *psx -= *ppx; + regfree(®); + free(sbuf); + free(buf); + return (1); + } + } + + regfree(®); + free(sbuf); + free(buf); + *ppx = 0; + *psx = 0; + return (0); +} + +static int +window_copy_search_rl_regex(struct grid *gd, struct grid *sgd, + u_int *ppx, u_int *psx, u_int py, u_int first, u_int last, int cis) +{ + int cflags = REG_EXTENDED, eflags = 0; + u_int endline, len, pywrap, size = 1, ssize = 1; + char *buf, *sbuf; + regex_t reg; + struct grid_line *gl; + + sbuf = xmalloc(ssize); + sbuf[0] = '\0'; + sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize); + if (sbuf == NULL) + return (0); + + /* Set flags for regex search. */ + if (cis) + cflags |= REG_ICASE; + if (regcomp(®, sbuf, cflags) != 0) { + free(sbuf); + return (0); + } + if (first != 0) + eflags |= REG_NOTBOL; + + /* Need to look at the entire string. */ + buf = xmalloc(size); + buf[0] = '\0'; + buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size); + len = gd->sx - first; + endline = gd->hsize + gd->sy - 1; + pywrap = py; + while (buf != NULL && (pywrap <= endline)) { + gl = grid_get_line(gd, pywrap); + if (~gl->flags & GRID_LINE_WRAPPED) + break; + pywrap++; + buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size); + len += gd->sx; + } + + if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf, + ®, eflags)) + { + regfree(®); + free(sbuf); + free(buf); + return (1); + } + + regfree(®); + free(sbuf); + free(buf); + *ppx = 0; + *psx = 0; + return (0); +} + +/* Find last match in given range. */ +static int +window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last, + u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg, + int eflags) +{ + u_int foundx, foundy, oldx, px = 0, savepx, savesx = 0; + regmatch_t regmatch; + + foundx = first; + foundy = py; + oldx = first; + while (regexec(preg, buf + px, 1, ®match, eflags) == 0) { + window_copy_cstrtocellpos(gd, len, &foundx, &foundy, + buf + px + regmatch.rm_so); + if (foundy > py || foundx >= last) + break; + len -= foundx - oldx; + savepx = foundx; + window_copy_cstrtocellpos(gd, len, &foundx, &foundy, + buf + px + regmatch.rm_eo); + if (foundy > py || foundx >= last) { + *ppx = savepx; + *psx = foundx; + while (foundy > py) { + *psx += gd->sx; + foundy--; + } + *psx -= *ppx; + return (1); + } else { + savesx = foundx - savepx; + len -= savesx; + oldx = foundx; + } + px += regmatch.rm_eo; + } + + if (savesx > 0) { + *ppx = savepx; + *psx = savesx; + return (1); + } else { + *ppx = 0; + *psx = 0; + return (0); + } +} + +/* Stringify line and append to input buffer. Caller frees. */ +static char * +window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last, + char *buf, u_int *size) +{ + u_int ax, bx, newsize; + struct grid_cell gc; + + bx = *size - 1; + newsize = *size; + for (ax = first; ax < last; ax++) { + grid_get_cell(gd, ax, py, &gc); + newsize += gc.data.size; + buf = xrealloc(buf, newsize); + memcpy(buf + bx, gc.data.data, gc.data.size); + bx += gc.data.size; + } + + buf[newsize - 1] = '\0'; + *size = newsize; + return (buf); +} + +/* Map start of C string containing UTF-8 data to grid cell position. */ +static void +window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy, + const char *str) +{ + u_int cell, ccell, px, pywrap; + int match; + const char *cstr; + char *celldata, **cells; + struct grid_cell gc; + + /* Set up staggered array of cell contents. This speeds up search. */ + cells = xreallocarray(NULL, ncells, sizeof cells[0]); + + /* Populate the array of cell data. */ + cell = 0; + px = *ppx; + pywrap = *ppy; + while (cell < ncells) { + grid_get_cell(gd, px, pywrap, &gc); + celldata = xmalloc(gc.data.size + 1); + memcpy(celldata, gc.data.data, gc.data.size); + celldata[gc.data.size] = '\0'; + cells[cell] = celldata; + cell++; + px = (px + 1) % gd->sx; + if (px == 0) + pywrap++; + } + + /* Locate starting cell. */ + cell = 0; + while (cell < ncells) { + ccell = cell; + cstr = str; + match = 1; + while (ccell < ncells) { + /* Anchor found to the end. */ + if (*cstr == '\0') { + match = 0; + break; + } + + celldata = cells[ccell]; + while (*celldata != '\0' && *cstr != '\0') { + if (*celldata++ != *cstr++) { + match = 0; + break; + } + } + + if (!match) + break; + ccell++; + } + + if (match) + break; + cell++; + } + + /* If not found this will be one past the end. */ + px = *ppx + cell; + pywrap = *ppy; + while (px >= gd->sx) { + px -= gd->sx; + pywrap++; + } + + *ppx = px; + *ppy = pywrap; + + /* Free cell data. */ + for (cell = 0; cell < ncells; cell++) + free(cells[cell]); + free(cells); +} + static void window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag) { @@ -2206,24 +2509,31 @@ window_copy_is_lowercase(const char *ptr) static int window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap, - int direction) + int direction, int regex) { - u_int i, px; - int found; + u_int i, px, sx; + int found = 0; - found = 0; if (direction) { for (i = fy; i <= endline; i++) { - found = window_copy_search_lr(gd, sgd, &px, i, fx, - gd->sx, cis); + if (regex) + found = window_copy_search_lr_regex(gd, sgd, + &px, &sx, i, fx, gd->sx, cis); + else + found = window_copy_search_lr(gd, sgd, + &px, i, fx, gd->sx, cis); if (found) break; fx = 0; } } else { for (i = fy + 1; endline < i; i--) { - found = window_copy_search_rl(gd, sgd, &px, i - 1, 0, - fx + 1, cis); + if (regex) + found = window_copy_search_rl_regex(gd, sgd, + &px, &sx, i - 1, 0, fx + 1, cis); + else + found = window_copy_search_rl(gd, sgd, + &px, i - 1, 0, fx + 1, cis); if (found) { i--; break; @@ -2240,7 +2550,7 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, return (window_copy_search_jump(wme, gd, sgd, direction ? 0 : gd->sx - 1, direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0, - direction)); + direction, regex)); } return (0); } @@ -2250,7 +2560,7 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, * down. */ static int -window_copy_search(struct window_mode_entry *wme, int direction) +window_copy_search(struct window_mode_entry *wme, int direction, int regex) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; @@ -2281,10 +2591,11 @@ window_copy_search(struct window_mode_entry *wme, int direction) window_copy_move_left(s, &fx, &fy, wrapflag); endline = 0; } - found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, - wrapflag, direction); - if (window_copy_search_marks(wme, &ss)) + found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, + wrapflag, direction, regex); + + if (window_copy_search_marks(wme, &ss, regex)) window_copy_redraw_screen(wme); screen_free(&ss); @@ -2292,7 +2603,8 @@ window_copy_search(struct window_mode_entry *wme, int direction) } static int -window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp) +window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, + int regex) { struct window_copy_mode_data *data = wme->data; struct screen *s = data->backing, ss; @@ -2320,10 +2632,19 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp) for (py = 0; py < gd->hsize + gd->sy; py++) { px = 0; for (;;) { - found = window_copy_search_lr(gd, ssp->grid, &px, py, - px, gd->sx, cis); - if (!found) - break; + if (regex) { + found = window_copy_search_lr_regex(gd, + ssp->grid, &px, &width, py, px, + gd->sx, cis); + if (!found) + break; + } + else { + found = window_copy_search_lr(gd, ssp->grid, + &px, py, px, gd->sx, cis); + if (!found) + break; + } nfound++; if (px == data->cx && py == gd->hsize + data->cy - data->oy) @@ -2357,15 +2678,15 @@ window_copy_clear_marks(struct window_mode_entry *wme) } static int -window_copy_search_up(struct window_mode_entry *wme) +window_copy_search_up(struct window_mode_entry *wme, int regex) { - return (window_copy_search(wme, 0)); + return (window_copy_search(wme, 0, regex)); } static int -window_copy_search_down(struct window_mode_entry *wme) +window_copy_search_down(struct window_mode_entry *wme, int regex) { - return (window_copy_search(wme, 1)); + return (window_copy_search(wme, 1, regex)); } static void From 206d878127b4ba4aedb5255b03d71272fa06bcab Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 30 Dec 2019 21:24:55 +0000 Subject: [PATCH 07/15] Do not let readonly clients limit the size, suggested by Max Barraclough in GitHub issue 2042. --- tmux.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tmux.h b/tmux.h index 2315f643..0a0fc1b5 100644 --- a/tmux.h +++ b/tmux.h @@ -1599,7 +1599,8 @@ struct client { #define CLIENT_NOSIZEFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ - CLIENT_DETACHING) + CLIENT_DETACHING| \ + CLIENT_READONLY) int flags; struct key_table *keytable; From 9cc603cbad33878dc0d988d3266a1589e74d7303 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Jan 2020 21:51:33 +0000 Subject: [PATCH 08/15] Fix format expansion in window names, reported by Suraj N Kurapati. --- spawn.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spawn.c b/spawn.c index 6cf7c73b..75995221 100644 --- a/spawn.c +++ b/spawn.c @@ -80,6 +80,8 @@ spawn_log(const char *from, struct spawn_context *sc) struct winlink * spawn_window(struct spawn_context *sc, char **cause) { + struct cmdq_item *item = sc->item; + struct client *c = item->client; struct session *s = sc->s; struct window *w; struct window_pane *wp; @@ -182,7 +184,8 @@ spawn_window(struct spawn_context *sc, char **cause) /* Set the name of the new window. */ if (~sc->flags & SPAWN_RESPAWN) { if (sc->name != NULL) { - w->name = xstrdup(sc->name); + w->name = format_single(item, sc->name, c, s, NULL, + NULL); options_set_number(w->options, "automatic-rename", 0); } else w->name = xstrdup(default_window_name(w)); From ac85a3e0d37bc75a9ca0416898636a544af8eeb4 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 1 Jan 2020 22:12:05 +0000 Subject: [PATCH 09/15] Document client exit messages. --- tmux.1 | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index ef288e5a..1e8c13af 100644 --- a/tmux.1 +++ b/tmux.1 @@ -933,7 +933,9 @@ If is specified, any other clients attached to the session are detached. If .Fl x -is given, send SIGHUP to the parent process of the client as well as +is given, send +.Dv SIGHUP +to the parent process of the client as well as detaching the client, typically causing it to exit. .Fl r signifies the client is read-only (only keys bound to the @@ -984,7 +986,9 @@ option kills all but the client given with .Fl t . If .Fl P -is given, send SIGHUP to the parent process of the client, typically causing it +is given, send +.Dv SIGHUP +to the parent process of the client, typically causing it to exit. With .Fl E , @@ -5148,6 +5152,37 @@ channel are made to wait until the channel is unlocked with .Ic wait-for .Fl U . .El +.Sh EXIT MESSAGES +When a +.Nm +client detaches, it prints a message. +This may be one of: +.Bl -tag -width Ds +.It [detached (from session ...)] +The client was detached normally. +.It [detached and SIGHUP] +The client was detached and its parent sent the +.Dv SIGHUP +signal (for example with +.Ic detach-client +.Fl P ) . +.It [lost tty] +The client's +.Xr tty 4 +or +.Xr pty 4 +was unexpectedly destroyed. +.It [terminated] +The client was killed with +.Dv SIGTERM . +.It [exited] +The server exited when it had no sessions. +.It [server exited] +The server exited when it received +.Dv SIGTERM . +.It [server exited unexpectedly] +The server crashed or otherwise exited without telling the client the reason. +.El .Sh TERMINFO EXTENSIONS .Nm understands some unofficial extensions to From a770a3bf7ea8370b850b19a50f2d4839b44c4a1f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 2 Jan 2020 13:44:17 +0000 Subject: [PATCH 10/15] Add CMD_FIND_DEFAULT_MARKED to join-pane like move-pane, from davidegirardi in GitHub issue 2046. --- cmd-join-pane.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 5344f3ec..94f3ae13 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -53,7 +53,7 @@ const struct cmd_entry cmd_move_pane_entry = { .args = { "bdhvp:l:s:t:", 0, 0 }, .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, - .source = { 's', CMD_FIND_PANE, 0 }, + .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED }, .target = { 't', CMD_FIND_PANE, 0 }, .flags = 0, From 1870cc70ef6eded1ac3a809124ec21de1b98dbd9 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 4 Jan 2020 18:01:56 +0000 Subject: [PATCH 11/15] Add ~ to quoted characters for %%%, reported by tb@. --- cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd.c b/cmd.c index 163db73b..03a17ab1 100644 --- a/cmd.c +++ b/cmd.c @@ -661,7 +661,7 @@ char * cmd_template_replace(const char *template, const char *s, int idx) { char ch, *buf; - const char *ptr, *cp, quote[] = "\"\\$;"; + const char *ptr, *cp, quote[] = "\"\\$;~"; int replaced, quoted; size_t len; From 7c6c66cc3cc40b663fc2ba8573f26a6aa4cb291e Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 5 Jan 2020 12:51:43 +0000 Subject: [PATCH 12/15] Send errors to stdout in control mode so they don't get reordered with other output, reported by George Nachman in GitHub issue 2048. --- cmd-queue.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd-queue.c b/cmd-queue.c index 75b5d8f9..59c7a35c 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -551,7 +551,10 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) msg = utf8_sanitize(tmp); free(tmp); } - file_error(c, "%s\n", msg); + if (c->flags & CLIENT_CONTROL) + file_print(c, "%s\n", msg); + else + file_error(c, "%s\n", msg); c->retval = 1; } else { *msg = toupper((u_char) *msg); From 73b8c2ef3ca011fd7e7e46549c9640e9e123d59a Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 5 Jan 2020 20:39:25 +0000 Subject: [PATCH 13/15] Common function to free key bindings. --- key-bindings.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index 7cd834a2..94110a49 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -85,6 +85,14 @@ key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) return (0); } +static void +key_bindings_free(struct key_table *table, struct key_binding *bd) +{ + RB_REMOVE(key_bindings, &table->key_bindings, bd); + cmd_list_free(bd->cmdlist); + free(bd); +} + struct key_table * key_bindings_get_table(const char *name, int create) { @@ -126,11 +134,8 @@ key_bindings_unref_table(struct key_table *table) if (--table->references != 0) return; - RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) { - RB_REMOVE(key_bindings, &table->key_bindings, bd); - cmd_list_free(bd->cmdlist); - free(bd); - } + RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) + key_bindings_free(table, bd); free((void *)table->name); free(table); @@ -162,17 +167,13 @@ key_bindings_add(const char *name, key_code key, int repeat, struct cmd_list *cmdlist) { struct key_table *table; - struct key_binding bd_find, *bd; + struct key_binding *bd; table = key_bindings_get_table(name, 1); - bd_find.key = (key & ~KEYC_XTERM); - bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); - if (bd != NULL) { - RB_REMOVE(key_bindings, &table->key_bindings, bd); - cmd_list_free(bd->cmdlist); - free(bd); - } + bd = key_bindings_get(table, key & ~KEYC_XTERM); + if (bd != NULL) + key_bindings_free(table, bd); bd = xcalloc(1, sizeof *bd); bd->key = key; @@ -187,20 +188,16 @@ void key_bindings_remove(const char *name, key_code key) { struct key_table *table; - struct key_binding bd_find, *bd; + struct key_binding *bd; table = key_bindings_get_table(name, 0); if (table == NULL) return; - bd_find.key = (key & ~KEYC_XTERM); - bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); + bd = key_bindings_get(table, key & ~KEYC_XTERM); if (bd == NULL) return; - - RB_REMOVE(key_bindings, &table->key_bindings, bd); - cmd_list_free(bd->cmdlist); - free(bd); + key_bindings_free(table, bd); if (RB_EMPTY(&table->key_bindings)) { RB_REMOVE(key_tables, &key_tables, table); From 6628e542b5a0fabbd02800e68d19aecaf530fece Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 8 Jan 2020 06:38:55 +0000 Subject: [PATCH 14/15] Add -Z to default switch-client command in tree mode, matches previous behaviour. --- window-tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window-tree.c b/window-tree.c index e0aa4314..d163dd9e 100644 --- a/window-tree.c +++ b/window-tree.c @@ -33,7 +33,7 @@ static void window_tree_key(struct window_mode_entry *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); -#define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'" +#define WINDOW_TREE_DEFAULT_COMMAND "switch-client -Zt '%%'" #define WINDOW_TREE_DEFAULT_FORMAT \ "#{?pane_format," \ From 36eb16ce7db2db71b510964a2e453e6b349b03f0 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 8 Jan 2020 14:40:52 +0000 Subject: [PATCH 15/15] Do not hang in format_trim_* on invalid UTF-8 characters. --- format-draw.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/format-draw.c b/format-draw.c index 6cced9fd..bb16e0dd 100644 --- a/format-draw.c +++ b/format-draw.c @@ -849,8 +849,10 @@ format_trim_left(const char *expanded, u_int limit) out += ud.size; } width += ud.width; - } else + } else { cp -= ud.have; + cp++; + } } else if (*cp > 0x1f && *cp < 0x7f) { if (width + 1 <= limit) *out++ = *cp; @@ -896,8 +898,10 @@ format_trim_right(const char *expanded, u_int limit) out += ud.size; } width += ud.width; - } else + } else { cp -= ud.have; + cp++; + } } else if (*cp > 0x1f && *cp < 0x7f) { if (width >= skip) *out++ = *cp;