diff --git a/cmd-new-window.c b/cmd-new-window.c index bb6113fd6..6e6d70ccc 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -38,8 +38,8 @@ const struct cmd_entry cmd_new_window_entry = { .name = "new-window", .alias = "neww", - .args = { "abc:de:F:kn:PSt:", 0, -1, NULL }, - .usage = "[-abdkPS] [-c start-directory] [-e environment] [-F format] " + .args = { "abc:de:EF:kn:PSt:", 0, -1, NULL }, + .usage = "[-abdEkPS] [-c start-directory] [-e environment] [-F format] " "[-n window-name] " CMD_TARGET_WINDOW_USAGE " [shell-command [argument ...]]", @@ -60,12 +60,19 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct winlink *wl = target->wl, *new_wl = NULL; - int idx = target->idx, before; + int idx = target->idx, before, count = args_count(args); char *cause = NULL, *cp, *expanded, *wname = NULL; const char *template, *name; struct cmd_find_state fs; struct args_value *av; + if (args_has(args, 'E') && + count != 0 && + (count != 1 || *args_string(args, 0) != '\0')) { + cmdq_error(item, "command cannot be given for empty pane"); + return (CMD_RETURN_ERROR); + } + /* * If -S and -n are given and -t is not and a single window with this * name already exists, select it. @@ -134,6 +141,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) sc.cwd = args_get(args, 'c'); sc.flags = 0; + if (args_has(args, 'E')) + sc.flags |= SPAWN_EMPTY; if (args_has(args, 'd')) sc.flags |= SPAWN_DETACHED; if (args_has(args, 'k')) diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 1e1dd4c6c..bf058a267 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -34,8 +34,8 @@ const struct cmd_entry cmd_respawn_pane_entry = { .name = "respawn-pane", .alias = "respawnp", - .args = { "c:e:kt:", 0, -1, NULL }, - .usage = "[-k] [-c start-directory] [-e environment] " + .args = { "c:e:Ekt:", 0, -1, NULL }, + .usage = "[-Ek] [-c start-directory] [-e environment] " CMD_TARGET_PANE_USAGE " [shell-command [argument ...]]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -75,6 +75,8 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) sc.cwd = args_get(args, 'c'); sc.flags = SPAWN_RESPAWN; + if (args_has(args, 'E')) + sc.flags |= SPAWN_EMPTY; if (args_has(args, 'k')) sc.flags |= SPAWN_KILL; diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index e1eae0afc..e67757e1b 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -34,8 +34,8 @@ const struct cmd_entry cmd_respawn_window_entry = { .name = "respawn-window", .alias = "respawnw", - .args = { "c:e:kt:", 0, -1, NULL }, - .usage = "[-k] [-c start-directory] [-e environment] " + .args = { "c:e:Ekt:", 0, -1, NULL }, + .usage = "[-Ek] [-c start-directory] [-e environment] " CMD_TARGET_WINDOW_USAGE " [shell-command [argument ...]]", .target = { 't', CMD_FIND_WINDOW, 0 }, @@ -74,6 +74,8 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) sc.cwd = args_get(args, 'c'); sc.flags = SPAWN_RESPAWN; + if (args_has(args, 'E')) + sc.flags |= SPAWN_EMPTY; if (args_has(args, 'k')) sc.flags |= SPAWN_KILL; diff --git a/format.c b/format.c index 71e4c35ce..4a33f8265 100644 --- a/format.c +++ b/format.c @@ -42,6 +42,7 @@ struct format_expand_state; static char *format_job_get(struct format_expand_state *, const char *); +static char *format_quote_shell_single(const char *); static char *format_expand1(struct format_expand_state *, const char *); static int format_replace(struct format_expand_state *, const char *, size_t, char **, size_t *, size_t *); @@ -879,6 +880,37 @@ format_cb_start_command(struct format_tree *ft) return (cmd_stringify_argv(wp->argc, wp->argv)); } +/* Callback for pane_start_command_list. */ +static void * +format_cb_start_command_list(struct format_tree *ft) +{ + struct window_pane *wp = ft->wp; + char *buf = NULL, *s; + size_t len = 0; + int i; + + if (wp == NULL) + return (NULL); + if (wp->argc == 0) + return (xstrdup("")); + + for (i = 0; i < wp->argc; i++) { + s = format_quote_shell_single(wp->argv[i]); + + len += strlen(s) + 1; + buf = xrealloc(buf, len); + + if (i == 0) + *buf = '\0'; + else + strlcat(buf, " ", len); + strlcat(buf, s, len); + + free(s); + } + return (buf); +} + /* Callback for pane_start_path. */ static void * format_cb_start_path(struct format_tree *ft) @@ -2307,7 +2339,7 @@ format_cb_pane_path(struct format_tree *ft) static void * format_cb_pane_pid(struct format_tree *ft) { - if (ft->wp != NULL) + if (ft->wp != NULL && ft->wp->fd != -1) return (format_printf("%ld", (long)ft->wp->pid)); return (NULL); } @@ -3558,6 +3590,9 @@ static const struct format_table_entry format_table[] = { { "pane_start_command", FORMAT_TABLE_STRING, format_cb_start_command }, + { "pane_start_command_list", FORMAT_TABLE_STRING, + format_cb_start_command_list + }, { "pane_start_path", FORMAT_TABLE_STRING, format_cb_start_path }, diff --git a/spawn.c b/spawn.c index 91010d775..e1ef0090b 100644 --- a/spawn.c +++ b/spawn.c @@ -264,14 +264,20 @@ spawn_pane(struct spawn_context *sc, char **cause) free(cwd); return (NULL); } - if (sc->wp0->fd != -1) { + if (sc->wp0->event != NULL) { bufferevent_free(sc->wp0->event); + sc->wp0->event = NULL; + } + if (sc->wp0->fd != -1) { close(sc->wp0->fd); + sc->wp0->fd = -1; } window_pane_reset_mode_all(sc->wp0); screen_reinit(&sc->wp0->base); - input_free(sc->wp0->ictx); - sc->wp0->ictx = NULL; + if (sc->wp0->ictx != NULL) { + input_free(sc->wp0->ictx); + sc->wp0->ictx = NULL; + } new_wp = sc->wp0; new_wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN); } else { @@ -387,6 +393,7 @@ spawn_pane(struct spawn_context *sc, char **cause) new_wp->base.mode |= MODE_CRLF; goto complete; } + new_wp->flags &= ~PANE_EMPTY; /* Store current working directory and change to new one. */ if (getcwd(path, sizeof path) != NULL) { diff --git a/tmux.1 b/tmux.1 index ac0709c8f..a14b2ec69 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3525,7 +3525,7 @@ the option. .Tg neww .It Xo Ic new\-window -.Op Fl abdkPS +.Op Fl abdEkPS .Op Fl c Ar start\-directory .Op Fl e Ar environment .Op Fl F Ar format @@ -3549,6 +3549,9 @@ is the new window location. If .Fl d is given, the session does not make the new window the current window. +If +.Fl E +is given, the initial pane is created without a running command. .Ar target\-window represents the window to be created; if the target already exists an error is shown, unless the @@ -3848,7 +3851,7 @@ This command will automatically set to manual in the window options. .Tg respawnp .It Xo Ic respawn\-pane -.Op Fl k +.Op Fl Ek .Op Fl c Ar start\-directory .Op Fl e Ar environment .Op Fl t Ar target\-pane @@ -3865,6 +3868,9 @@ executed. The pane must be already inactive, unless .Fl k is given, in which case any existing command is killed. +If +.Fl E +is given, the pane is left without a running command. .Fl c specifies a new working directory for the pane. The @@ -3874,7 +3880,7 @@ option has the same meaning as for the command. .Tg respawnw .It Xo Ic respawn\-window -.Op Fl k +.Op Fl Ek .Op Fl c Ar start\-directory .Op Fl e Ar environment .Op Fl t Ar target\-window @@ -3891,6 +3897,9 @@ executed. The window must be already inactive, unless .Fl k is given, in which case any existing command is killed. +If +.Fl E +is given, the window is left with one pane and without a running command. .Fl c specifies a new working directory for the window. The @@ -7103,6 +7112,7 @@ The following variables are available, where appropriate: .It Li "pane_right" Ta "" Ta "Right of pane" .It Li "pane_search_string" Ta "" Ta "Last search string in copy mode" .It Li "pane_start_command" Ta "" Ta "Command pane started with" +.It Li "pane_start_command_list" Ta "" Ta "Command pane started with, quoted" .It Li "pane_start_path" Ta "" Ta "Path pane started with" .It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" .It Li "pane_tabs" Ta "" Ta "Pane tab positions"