diff --git a/format.c b/format.c index 361ae20bf..e391323f4 100644 --- a/format.c +++ b/format.c @@ -1,4 +1,4 @@ -/* $OpenBSD: format.c,v 1.427 2026/10/12 12:22:42 nicm Exp $ */ +/* $OpenBSD: format.c,v 1.428 2026/10/13 10:21:58 nicm Exp $ */ /* * Copyright (c) 2011 Nicholas Marriott @@ -2340,6 +2340,17 @@ format_cb_pane_last_prompt_time(struct format_tree *ft) return (NULL); } +/* Callback for pane_command_line. */ +static void * +format_cb_pane_command_line(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + + if (wp != NULL && wp->cmd_line != NULL) + return (xstrdup(wp->cmd_line)); + return (NULL); +} + /* Callback for pane_command_start_time. */ static void * format_cb_pane_command_start_time(struct format_tree *ft) @@ -3825,6 +3836,9 @@ static const struct format_table_entry format_table[] = { { "pane_command_end_time", FORMAT_TABLE_TIME, format_cb_pane_command_end_time }, + { "pane_command_line", FORMAT_TABLE_STRING, + format_cb_pane_command_line + }, { "pane_command_running", FORMAT_TABLE_STRING, format_cb_pane_command_running }, diff --git a/input.c b/input.c index 8ab63f5f6..062945ec0 100644 --- a/input.c +++ b/input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: input.c,v 1.275 2026/10/12 18:47:52 nicm Exp $ */ +/* $OpenBSD: input.c,v 1.276 2026/10/13 10:21:58 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -3186,6 +3186,52 @@ input_osc_133_exit_status(const char *p) return (status); } +/* Parse and clean the URL-encoded command line from OSC 133 C. */ +static char * +input_osc_133_command_line(const char *p) +{ + const char *end; + char line[16385], h[3] = { 0 }; + size_t size = 0; + u_char ch; + + if (p[1] != ';') + return (NULL); + p += 2; + for (;;) { + end = strchr(p, ';'); + if (end == NULL) + end = p + strlen(p); + if (end - p >= 12 && strncmp(p, "cmdline_url=", 12) == 0) + break; + if (*end == '\0') + return (NULL); + p = end + 1; + } + p += 12; + while (p < end) { + if (size == sizeof line - 1) + return (NULL); + ch = *p++; + if (ch == '%') { + if (end - p < 2 || + !isxdigit((u_char)p[0]) || + !isxdigit((u_char)p[1])) + return (NULL); + h[0] = p[0]; + h[1] = p[1]; + + ch = strtoul(h, NULL, 16); + if (ch == '\0') + return (NULL); + p += 2; + } + line[size++] = ch; + } + line[size] = '\0'; + return (clean_name(line, 1)); +} + /* Fire an OSC 133 command event. */ static void input_fire_command_event(struct window_pane *wp, const char *name) @@ -3205,6 +3251,8 @@ input_fire_command_event(struct window_pane *wp, const char *name) event_payload_set_window(ep, "window", wp->window); event_payload_set_pane(ep, "pane", wp); + if (wp->cmd_line != NULL) + event_payload_set_string(ep, "command_line", "%s", wp->cmd_line); if (wp->cmd_status != -1) event_payload_set_int(ep, "command_status", wp->cmd_status); if (tstart != 0) @@ -3276,6 +3324,8 @@ input_osc_133(struct input_ctx *ictx, const char *p) gl->osc133_data.out_start_col = s->cx; } if (wp != NULL) { + free(wp->cmd_line); + wp->cmd_line = input_osc_133_command_line(p); wp->cmd_start_time = time(NULL); wp->cmd_end_time = 0; wp->flags |= PANE_CMDRUNNING; diff --git a/tmux.1 b/tmux.1 index b19c2bb16..ee852c221 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.1184 2026/10/12 12:35:03 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.1185 2026/10/13 10:21:58 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -14,7 +14,7 @@ .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: October 12 2026 $ +.Dd $Mdocdate: October 13 2026 $ .Dt TMUX 1 .Os .Sh NAME @@ -7445,6 +7445,7 @@ The following variables are available, where appropriate: .It Li "history_size" Ta "" Ta "Size of history in lines" .It Li "hook" Ta "" Ta "Name of running hook, if any" .It Li "hook_client" Ta "" Ta "Name of client where hook was run, if any" +.It Li "hook_command_line" Ta "" Ta "Command line reported for an OSC 133 command hook, if any" .It Li "hook_fire_count" Ta "" Ta "Number of times hook has fired" .It Li "hook_fire_time" Ta "" Ta "Time hook last fired" .It Li "hook_last" Ta "" Ta "Previous value for a monitor hook" @@ -7513,6 +7514,7 @@ The following variables are available, where appropriate: .It Li "pane_bottom" Ta "" Ta "Bottom of pane" .It Li "pane_command_duration" Ta "" Ta "Current or most recent OSC 133 command duration in seconds" .It Li "pane_command_end_time" Ta "" Ta "Time most recent OSC 133 command ended" +.It Li "pane_command_line" Ta "" Ta "Current or most recent OSC 133 command line, if reported" .It Li "pane_command_running" Ta "" Ta "1 if an OSC 133 command is running" .It Li "pane_command_start_time" Ta "" Ta "Time current or most recent OSC 133 command started" .It Li "pane_command_status" Ta "" Ta "Exit status from most recent OSC 133 command" diff --git a/tmux.h b/tmux.h index 8011b26e7..aff0779ee 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1458 2026/10/12 18:47:52 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1459 2026/10/13 10:21:58 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1355,6 +1355,7 @@ struct window_pane { time_t cmd_start_time; time_t cmd_end_time; int cmd_status; + char *cmd_line; int fd; struct bufferevent *event; diff --git a/utf8.c b/utf8.c index 569b5c02a..607a740ff 100644 --- a/utf8.c +++ b/utf8.c @@ -1,4 +1,4 @@ -/* $OpenBSD: utf8.c,v 1.74 2026/10/05 08:32:24 nicm Exp $ */ +/* $OpenBSD: utf8.c,v 1.75 2026/10/13 10:21:58 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -822,7 +822,7 @@ utf8_isvalid(const char *s) continue; return (0); } - if (*s < 0x20 || *s > 0x7e) + if (*s != '\t' && *s != '\n' && (*s < 0x20 || *s > 0x7e)) return (0); s++; } diff --git a/window.c b/window.c index 0b280f19a..2d0e7ff0e 100644 --- a/window.c +++ b/window.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window.c,v 1.385 2026/10/08 07:06:26 nicm Exp $ */ +/* $OpenBSD: window.c,v 1.386 2026/10/13 10:21:58 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1622,6 +1622,7 @@ window_pane_free(struct window_pane *wp) log_debug("pane %%%u freed (%d references)", wp->id, wp->references); free(wp->searchstr); + free(wp->cmd_line); screen_free(&wp->status_screen); screen_free(&wp->base); free(wp->r.ranges);