diff --git a/usr.bin/tmux/cmd-kill-pane.c b/usr.bin/tmux/cmd-kill-pane.c index f4ce62cc891..a0ca98b90fb 100644 --- a/usr.bin/tmux/cmd-kill-pane.c +++ b/usr.bin/tmux/cmd-kill-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-kill-pane.c,v 1.32 2026/05/19 12:16:25 nicm Exp $ */ +/* $OpenBSD: cmd-kill-pane.c,v 1.33 2026/06/09 12:24:59 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -27,13 +27,17 @@ */ static enum cmd_retval cmd_kill_pane_exec(struct cmd *, struct cmdq_item *); +static enum cmd_retval cmd_kill_pane_all(struct cmdq_item *, const char *); +static int cmd_kill_pane_filter(struct cmdq_item *, + struct session *, struct winlink *, + struct window_pane *, const char *); const struct cmd_entry cmd_kill_pane_entry = { .name = "kill-pane", .alias = "killp", - .args = { "at:", 0, 0, NULL }, - .usage = "[-a] " CMD_TARGET_PANE_USAGE, + .args = { "af:t:", 0, 0, NULL }, + .usage = "[-a] [-f filter] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -46,22 +50,19 @@ cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); + struct session *s = target->s; struct winlink *wl = target->wl; - struct window_pane *loopwp, *tmpwp, *wp = target->wp; + struct window_pane *wp = target->wp; + const char *filter = args_get(args, 'f'); - if (args_has(args, 'a')) { - server_unzoom_window(wl->window); - TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) { - if (loopwp == wp) - continue; - server_client_remove_pane(loopwp); - layout_close_pane(loopwp); - window_remove_pane(wl->window, loopwp); - } - server_redraw_window(wl->window); - return (CMD_RETURN_NORMAL); + if (filter != NULL && !args_has(args, 'a')) { + cmdq_error(item, "-f only valid with -a"); + return (CMD_RETURN_ERROR); } + if (args_has(args, 'a')) + return (cmd_kill_pane_all(item, filter)); + if (wp == NULL) { cmdq_error(item, "no active pane to kill"); return (CMD_RETURN_ERROR); @@ -69,3 +70,48 @@ cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item) server_kill_pane(wp); return (CMD_RETURN_NORMAL); } + +static enum cmd_retval +cmd_kill_pane_all(struct cmdq_item *item, const char *filter) +{ + struct cmd_find_state *target = cmdq_get_target(item); + struct session *s = target->s; + struct winlink *wl = target->wl; + struct window_pane *wp = target->wp; + struct window_pane *loopwp, *tmpwp; + + server_unzoom_window(wl->window); + TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) { + if (loopwp == wp) + continue; + if (!cmd_kill_pane_filter(item, s, wl, loopwp, filter)) + continue; + server_client_remove_pane(loopwp); + layout_close_pane(loopwp); + window_remove_pane(wl->window, loopwp); + } + server_redraw_window(wl->window); + return (CMD_RETURN_NORMAL); +} + +static int +cmd_kill_pane_filter(struct cmdq_item *item, struct session *s, + struct winlink *wl, struct window_pane *wp, const char *filter) +{ + struct format_tree *ft; + char *expanded; + int flag; + + if (filter == NULL) + return (1); + + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); + format_defaults(ft, NULL, s, wl, wp); + + expanded = format_expand(ft, filter); + flag = format_true(expanded); + free(expanded); + + format_free(ft); + return (flag); +} diff --git a/usr.bin/tmux/cmd-kill-session.c b/usr.bin/tmux/cmd-kill-session.c index ed406f2bf7e..45555b3405e 100644 --- a/usr.bin/tmux/cmd-kill-session.c +++ b/usr.bin/tmux/cmd-kill-session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-kill-session.c,v 1.29 2026/06/07 13:51:42 nicm Exp $ */ +/* $OpenBSD: cmd-kill-session.c,v 1.30 2026/06/09 12:24:59 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -28,13 +28,16 @@ */ static enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmdq_item *); +static enum cmd_retval cmd_kill_session_all(struct cmdq_item *, const char *); +static int cmd_kill_session_filter(struct cmdq_item *, + struct session *, const char *); const struct cmd_entry cmd_kill_session_entry = { .name = "kill-session", .alias = NULL, - .args = { "aCgt:", 0, 0, NULL }, - .usage = "[-aCg] " CMD_TARGET_SESSION_USAGE, + .args = { "aCgf:t:", 0, 0, NULL }, + .usage = "[-aCg] [-f filter] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, @@ -50,6 +53,12 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item) struct session *s = target->s, *sloop, *stmp; struct session_group *sg; struct winlink *wl; + const char *filter = args_get(args, 'f'); + + if (filter != NULL && (!args_has(args, 'a') || args_has(args, 'C'))) { + cmdq_error(item, "-f only valid with -a"); + return (CMD_RETURN_ERROR); + } if (args_has(args, 'C')) { RB_FOREACH(wl, winlinks, &s->windows) { @@ -57,14 +66,9 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item) wl->flags &= ~WINLINK_ALERTFLAGS; } server_redraw_session(s); - } else if (args_has(args, 'a')) { - RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) { - if (sloop != s) { - server_destroy_session(sloop); - session_destroy(sloop, 1, __func__); - } - } - } else if (args_has(args, 'g') && + } else if (args_has(args, 'a')) + return (cmd_kill_session_all(item, filter)); + else if (args_has(args, 'g') && (sg = session_group_contains(s)) != NULL) { TAILQ_FOREACH_SAFE(sloop, &sg->sessions, gentry, stmp) { server_destroy_session(sloop); @@ -76,3 +80,42 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item) } return (CMD_RETURN_NORMAL); } + +static enum cmd_retval +cmd_kill_session_all(struct cmdq_item *item, const char *filter) +{ + struct session *s = cmdq_get_target(item)->s; + struct session *sloop, *stmp; + + RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) { + if (sloop == s) + continue; + if (!cmd_kill_session_filter(item, sloop, filter)) + continue; + server_destroy_session(sloop); + session_destroy(sloop, 1, __func__); + } + return (CMD_RETURN_NORMAL); +} + +static int +cmd_kill_session_filter(struct cmdq_item *item, struct session *s, + const char *filter) +{ + struct format_tree *ft; + char *expanded; + int flag; + + if (filter == NULL) + return (1); + + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); + format_defaults(ft, NULL, s, NULL, NULL); + + expanded = format_expand(ft, filter); + flag = format_true(expanded); + free(expanded); + + format_free(ft); + return (flag); +} diff --git a/usr.bin/tmux/cmd-kill-window.c b/usr.bin/tmux/cmd-kill-window.c index f0ba5e63e13..cfb293f2a3c 100644 --- a/usr.bin/tmux/cmd-kill-window.c +++ b/usr.bin/tmux/cmd-kill-window.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-kill-window.c,v 1.28 2021/08/21 10:22:39 nicm Exp $ */ +/* $OpenBSD: cmd-kill-window.c,v 1.29 2026/06/09 12:24:59 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -25,13 +25,16 @@ */ static enum cmd_retval cmd_kill_window_exec(struct cmd *, struct cmdq_item *); +static enum cmd_retval cmd_kill_window_all(struct cmdq_item *, const char *); +static int cmd_kill_window_filter(struct cmdq_item *, + struct session *, struct winlink *, const char *); const struct cmd_entry cmd_kill_window_entry = { .name = "kill-window", .alias = "killw", - .args = { "at:", 0, 0, NULL }, - .usage = "[-a] " CMD_TARGET_WINDOW_USAGE, + .args = { "af:t:", 0, 0, NULL }, + .usage = "[-a] [-f filter] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, @@ -57,10 +60,15 @@ cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct winlink *wl = target->wl, *loop; + struct winlink *wl = target->wl; struct window *w = wl->window; struct session *s = target->s; - u_int found; + const char *filter = args_get(args, 'f'); + + if (filter != NULL && !args_has(args, 'a')) { + cmdq_error(item, "-f only valid with -a"); + return (CMD_RETURN_ERROR); + } if (cmd_get_entry(self) == &cmd_unlink_window_entry) { if (!args_has(args, 'k') && !session_is_linked(s, w)) { @@ -72,39 +80,76 @@ cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(args, 'a')) { - if (RB_PREV(winlinks, &s->windows, wl) == NULL && - RB_NEXT(winlinks, &s->windows, wl) == NULL) - return (CMD_RETURN_NORMAL); - - /* Kill all windows except the current one. */ - do { - found = 0; - RB_FOREACH(loop, winlinks, &s->windows) { - if (loop->window != wl->window) { - server_kill_window(loop->window, 0); - found++; - break; - } - } - } while (found != 0); - - /* - * If the current window appears in the session more than once, - * kill it as well. - */ - found = 0; - RB_FOREACH(loop, winlinks, &s->windows) { - if (loop->window == wl->window) - found++; - } - if (found > 1) - server_kill_window(wl->window, 0); - - server_renumber_all(); - return (CMD_RETURN_NORMAL); - } + if (args_has(args, 'a')) + return (cmd_kill_window_all(item, filter)); server_kill_window(wl->window, 1); return (CMD_RETURN_NORMAL); } + +static enum cmd_retval +cmd_kill_window_all(struct cmdq_item *item, const char *filter) +{ + struct cmd_find_state *target = cmdq_get_target(item); + struct session *s = target->s; + struct winlink *wl = target->wl; + struct winlink *loop; + u_int found, kill_current; + + if (RB_PREV(winlinks, &s->windows, wl) == NULL && + RB_NEXT(winlinks, &s->windows, wl) == NULL) + return (CMD_RETURN_NORMAL); + + /* Kill all windows except the current one. */ + do { + found = 0; + RB_FOREACH(loop, winlinks, &s->windows) { + if (loop->window != wl->window && + cmd_kill_window_filter(item, s, loop, filter)) { + server_kill_window(loop->window, 0); + found++; + break; + } + } + } while (found != 0); + + /* + * If the current window appears in the session more than once, kill it + * as well if it matches the filter. + */ + found = kill_current = 0; + RB_FOREACH(loop, winlinks, &s->windows) { + if (loop->window == wl->window) { + found++; + if (cmd_kill_window_filter(item, s, loop, filter)) + kill_current = 1; + } + } + if (kill_current && found > 1) + server_kill_window(wl->window, 0); + + server_renumber_all(); + return (CMD_RETURN_NORMAL); +} + +static int +cmd_kill_window_filter(struct cmdq_item *item, struct session *s, + struct winlink *wl, const char *filter) +{ + struct format_tree *ft; + char *expanded; + int flag; + + if (filter == NULL) + return (1); + + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); + format_defaults(ft, NULL, s, wl, NULL); + + expanded = format_expand(ft, filter); + flag = format_true(expanded); + free(expanded); + + format_free(ft); + return (flag); +} diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1 index e0804a10bc6..73b857ff469 100644 --- a/usr.bin/tmux/tmux.1 +++ b/usr.bin/tmux/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.1075 2026/06/09 09:11:05 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.1076 2026/06/09 12:24:59 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -1203,6 +1203,7 @@ Kill the server and clients and destroy all sessions. .It Xo Ic kill\-session .Op Fl aCg +.Op Fl f Ar filter .Op Fl t Ar target\-session .Xc Destroy the given session, closing any windows linked to it and no other @@ -1210,6 +1211,12 @@ sessions, and detaching all clients attached to it. If .Fl a is given, all sessions but the specified one is killed. +When +.Fl a +is given, +.Fl f +specifies a filter. +Only sessions for which the filter is true are killed. If .Fl g is given and the session is in a session group, all sessions in the group are @@ -3115,6 +3122,7 @@ the marked pane is used rather than the current pane. .Tg killp .It Xo Ic kill\-pane .Op Fl a +.Op Fl f Ar filter .Op Fl t Ar target\-pane .Xc .D1 Pq alias: Ic killp @@ -3124,9 +3132,16 @@ The .Fl a option kills all but the pane given with .Fl t . +When +.Fl a +is given, +.Fl f +specifies a filter. +Only panes for which the filter is true are killed. .Tg killw .It Xo Ic kill\-window .Op Fl a +.Op Fl f Ar filter .Op Fl t Ar target\-window .Xc .D1 Pq alias: Ic killw @@ -3137,6 +3152,12 @@ The .Fl a option kills all but the window given with .Fl t . +When +.Fl a +is given, +.Fl f +specifies a filter. +Only windows for which the filter is true are killed. .Tg lastp .It Xo Ic last\-pane .Op Fl deZ