diff --git a/cfg.c b/cfg.c index ddc112a2..933eda4e 100644 --- a/cfg.c +++ b/cfg.c @@ -184,9 +184,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) @@ -230,9 +230,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-join-pane.c b/cmd-join-pane.c index 67207291..4b767e3e 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -52,7 +52,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, diff --git a/cmd-queue.c b/cmd-queue.c index 69e4f6b2..59c7a35c 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); } @@ -542,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); diff --git a/cmd-source-file.c b/cmd-source-file.c index bdcdd222..afe45a54 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -113,6 +113,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); @@ -123,7 +124,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; @@ -159,7 +159,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) diff --git a/cmd.c b/cmd.c index f77176c9..380eedcf 100644 --- a/cmd.c +++ b/cmd.c @@ -660,7 +660,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; 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; diff --git a/format.c b/format.c index ee507340..08e41271 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) @@ -677,11 +828,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); @@ -974,12 +1166,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); @@ -1022,8 +1214,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); @@ -2141,8 +2332,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); @@ -2151,6 +2348,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); @@ -2265,6 +2464,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))); @@ -2285,6 +2492,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/key-bindings.c b/key-bindings.c index d4fada6a..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); @@ -520,8 +517,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 8bd8f98f..200e23d6 100644 --- a/notify.c +++ b/notify.c @@ -89,9 +89,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/session.c b/session.c index b361bde3..eddafa2c 100644 --- a/session.c +++ b/session.c @@ -568,7 +568,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/spawn.c b/spawn.c index 51e4ae78..e7838816 100644 --- a/spawn.c +++ b/spawn.c @@ -78,6 +78,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; @@ -180,7 +182,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)); diff --git a/tmux.1 b/tmux.1 index aac4625c..23098344 100644 --- a/tmux.1 +++ b/tmux.1 @@ -938,7 +938,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 @@ -989,7 +991,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 , @@ -4316,10 +4320,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" @@ -4332,6 +4340,10 @@ The following variables are available, where appropriate: .It Li "start_time" Ta "" Ta "Server start time" .It Li "version" Ta "" Ta "Server version" .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" @@ -4347,6 +4359,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" @@ -4367,7 +4381,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 @@ -5145,6 +5159,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 diff --git a/tmux.h b/tmux.h index 64923c90..247cc4de 100644 --- a/tmux.h +++ b/tmux.h @@ -1620,7 +1620,8 @@ struct client { #define CLIENT_NOSIZEFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ - CLIENT_DETACHING) + CLIENT_DETACHING| \ + CLIENT_READONLY) int flags; struct key_table *keytable; @@ -2157,8 +2158,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 *); @@ -2714,6 +2715,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 */ 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 diff --git a/window-tree.c b/window-tree.c index 7ba14054..009e93c6 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," \