Thanks - will look at this soon. Only bit I don't like now is passing return from xstrdup() to xrealloc() in cmd_stringify_argv, better just to start len = 1 and x = 0.
On Wed, Apr 23, 2014 at 11:13:32PM -0500, J Raynor wrote: > I've attached the 2nd part of the "--" patch. > > > > On Fri, Apr 18, 2014 at 12:39 PM, J Raynor <jxray...@gmail.com> wrote: > > On Thu, Apr 17, 2014 at 8:04 AM, Nicholas Marriott > > <nicholas.marri...@gmail.com> wrote: > >> Applied this to OpenBSD now with the const char * thing fixed. Thanks > > > > Ok. I'll wait until this shows up in the git repository and then send > > in the 2nd part of the "--" patch. > diff --git a/cmd-new-session.c b/cmd-new-session.c > index 3071bd6..c907a0c 100644 > --- a/cmd-new-session.c > +++ b/cmd-new-session.c > @@ -35,7 +35,7 @@ enum cmd_retval cmd_new_session_exec(struct cmd *, > struct cmd_q *); > > const struct cmd_entry cmd_new_session_entry = { > "new-session", "new", > - "Ac:dDF:n:Ps:t:x:y:", 0, 1, > + "Ac:dDF:n:Ps:t:x:y:", 0, -1, > "[-AdDP] [-c start-directory] [-F format] [-n window-name] " > "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] [-y height] " > "[command]", > @@ -55,8 +55,9 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) > struct termios tio, *tiop; > const char *newname, *target, *update, *errstr, *template; > const char *path; > - char *cmd, *cause, *cp; > + char **argv, *cmd, *cause, *cp; > int detached, already_attached, idx, cwd, fd = -1; > + int argc; > u_int sx, sy; > struct format_tree *ft; > struct environ_entry *envent; > @@ -183,12 +184,22 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q > *cmdq) > sy = 1; > > /* Figure out the command for the new window. */ > - if (target != NULL) > - cmd = NULL; > - else if (args->argc != 0) > - cmd = args->argv[0]; > - else > - cmd = options_get_string(&global_s_options, "default-command"); > + argc = -1; > + argv = NULL; > + if (target == NULL) { > + if (args->argc != 0) { > + argc = args->argc; > + argv = args->argv; > + } else { > + argc = 0; > + cmd = options_get_string(&global_s_options, > + "default-command"); > + if (cmd != NULL && *cmd != '\0') { > + argc = 1; > + argv = &cmd; > + } > + } > + } > > path = NULL; > if (c != NULL && c->session == NULL) > @@ -206,8 +217,8 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) > > /* Create the new session. */ > idx = -1 - options_get_number(&global_s_options, "base-index"); > - s = session_create(newname, cmd, path, cwd, &env, tiop, idx, sx, sy, > - &cause); > + s = session_create(newname, argc, argv, path, cwd, &env, tiop, idx, sx, > + sy, &cause); > if (s == NULL) { > cmdq_error(cmdq, "create session failed: %s", cause); > free(cause); > @@ -216,7 +227,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) > environ_free(&env); > > /* Set the initial window name if one given. */ > - if (cmd != NULL && args_has(args, 'n')) { > + if (argc >= 0 && args_has(args, 'n')) { > w = s->curw->window; > window_set_name(w, args_get(args, 'n')); > options_set_number(&w->options, "automatic-rename", 0); > diff --git a/cmd-new-window.c b/cmd-new-window.c > index 6e9fea5..a6b6b63 100644 > --- a/cmd-new-window.c > +++ b/cmd-new-window.c > @@ -34,7 +34,7 @@ enum cmd_retval cmd_new_window_exec(struct cmd *, > struct cmd_q *); > > const struct cmd_entry cmd_new_window_entry = { > "new-window", "neww", > - "ac:dF:kn:Pt:", 0, 1, > + "ac:dF:kn:Pt:", 0, -1, > "[-adkP] [-c start-directory] [-F format] [-n window-name] " > CMD_TARGET_WINDOW_USAGE " [command]", > 0, > @@ -50,8 +50,8 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) > struct winlink *wl; > struct client *c; > const char *cmd, *path, *template; > - char *cause, *cp; > - int idx, last, detached, cwd, fd = -1; > + char **argv, *cause, *cp; > + int argc, idx, last, detached, cwd, fd = -1; > struct format_tree *ft; > struct environ_entry *envent; > > @@ -84,10 +84,18 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) > } > detached = args_has(args, 'd'); > > - if (args->argc == 0) > + if (args->argc == 0) { > + argc = 0; > + argv = NULL; > cmd = options_get_string(&s->options, "default-command"); > - else > - cmd = args->argv[0]; > + if (cmd != NULL && *cmd != '\0') { > + argc = 1; > + argv = &cmd; > + } > + } else { > + argc = args->argc; > + argv = args->argv; > + } > > path = NULL; > if (cmdq->client != NULL && cmdq->client->session == NULL) > @@ -145,7 +153,8 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) > > if (idx == -1) > idx = -1 - options_get_number(&s->options, "base-index"); > - wl = session_new(s, args_get(args, 'n'), cmd, path, cwd, idx, &cause); > + wl = session_new(s, args_get(args, 'n'), argc, argv, path, cwd, idx, > + &cause); > if (wl == NULL) { > cmdq_error(cmdq, "create window failed: %s", cause); > free(cause); > diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c > index 9ac5b0b..f951685 100644 > --- a/cmd-respawn-pane.c > +++ b/cmd-respawn-pane.c > @@ -32,7 +32,7 @@ enum cmd_retval cmd_respawn_pane_exec(struct cmd *, > struct cmd_q *); > > const struct cmd_entry cmd_respawn_pane_entry = { > "respawn-pane", "respawnp", > - "kt:", 0, 1, > + "kt:", 0, -1, > "[-k] " CMD_TARGET_PANE_USAGE " [command]", > 0, > NULL, > @@ -48,7 +48,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) > struct window_pane *wp; > struct session *s; > struct environ env; > - const char *cmd, *path; > + const char *path; > char *cause; > u_int idx; > struct environ_entry *envent; > @@ -74,11 +74,6 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) > screen_reinit(&wp->base); > input_init(wp); > > - if (args->argc != 0) > - cmd = args->argv[0]; > - else > - cmd = NULL; > - > path = NULL; > if (cmdq->client != NULL && cmdq->client->session == NULL) > envent = environ_find(&cmdq->client->environ, "PATH"); > @@ -87,8 +82,8 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) > if (envent != NULL) > path = envent->value; > > - if (window_pane_spawn(wp, cmd, path, NULL, -1, &env, s->tio, > - &cause) != 0) { > + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, &env, > + s->tio, &cause) != 0) { > cmdq_error(cmdq, "respawn pane failed: %s", cause); > free(cause); > environ_free(&env); > diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c > index d891eff..307560b 100644 > --- a/cmd-respawn-window.c > +++ b/cmd-respawn-window.c > @@ -31,7 +31,7 @@ enum cmd_retval cmd_respawn_window_exec(struct cmd *, > struct cmd_q *); > > const struct cmd_entry cmd_respawn_window_entry = { > "respawn-window", "respawnw", > - "kt:", 0, 1, > + "kt:", 0, -1, > "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", > 0, > NULL, > @@ -47,7 +47,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q > *cmdq) > struct window_pane *wp; > struct session *s; > struct environ env; > - const char *cmd, *path; > + const char *path; > char *cause; > struct environ_entry *envent; > > @@ -76,10 +76,6 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q > *cmdq) > window_destroy_panes(w); > TAILQ_INSERT_HEAD(&w->panes, wp, entry); > window_pane_resize(wp, w->sx, w->sy); > - if (args->argc != 0) > - cmd = args->argv[0]; > - else > - cmd = NULL; > > path = NULL; > if (cmdq->client != NULL && cmdq->client->session == NULL) > @@ -89,8 +85,8 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q > *cmdq) > if (envent != NULL) > path = envent->value; > > - if (window_pane_spawn(wp, cmd, path, NULL, -1, &env, s->tio, > - &cause) != 0) { > + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, &env, > + s->tio, &cause) != 0) { > cmdq_error(cmdq, "respawn window failed: %s", cause); > free(cause); > environ_free(&env); > diff --git a/cmd-split-window.c b/cmd-split-window.c > index 9c4734b..9263e69 100644 > --- a/cmd-split-window.c > +++ b/cmd-split-window.c > @@ -35,7 +35,7 @@ enum cmd_retval cmd_split_window_exec(struct cmd *, > struct cmd_q *); > > const struct cmd_entry cmd_split_window_entry = { > "split-window", "splitw", > - "c:dF:l:hp:Pt:v", 0, 1, > + "c:dF:l:hp:Pt:v", 0, -1, > "[-dhvP] [-c start-directory] [-F format] [-p percentage|-l size] " > CMD_TARGET_PANE_USAGE " [command]", > 0, > @@ -61,9 +61,9 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) > struct window_pane *wp, *new_wp = NULL; > struct environ env; > const char *cmd, *path, *shell, *template; > - char *cause, *new_cause, *cp; > + char **argv, *cause, *new_cause, *cp; > u_int hlimit; > - int size, percentage, cwd, fd = -1; > + int argc, size, percentage, cwd, fd = -1; > enum layout_type type; > struct layout_cell *lc; > struct client *c; > @@ -80,10 +80,18 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q > *cmdq) > environ_copy(&s->environ, &env); > server_fill_environ(s, &env); > > - if (args->argc == 0) > + if (args->argc == 0) { > + argc = 0; > + argv = NULL; > cmd = options_get_string(&s->options, "default-command"); > - else > - cmd = args->argv[0]; > + if (cmd != NULL && *cmd != '\0') { > + argc = 1; > + argv = &cmd; > + } > + } else { > + argc = args->argc; > + argv = args->argv; > + } > > if (args_has(args, 'c')) { > ft = format_create(); > @@ -158,7 +166,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q > *cmdq) > path = envent->value; > > if (window_pane_spawn( > - new_wp, cmd, path, shell, cwd, &env, s->tio, &cause) != 0) > + new_wp, argc, argv, path, shell, cwd, &env, s->tio, &cause) != 0) > goto error; > layout_assign_pane(lc, new_wp); > > diff --git a/cmd.c b/cmd.c > index f2d88c0..ee42d12 100644 > --- a/cmd.c > +++ b/cmd.c > @@ -206,6 +206,28 @@ cmd_free_argv(int argc, char **argv) > free(argv); > } > > +char * > +cmd_stringify_argv(int argc, char *const *argv) > +{ > + char *buf; > + int x; > + size_t len; > + > + if (argc == 0) > + return (xstrdup("")); > + > + buf = xstrdup(argv[0]); > + len = strlen(buf) + 1; > + for (x = 1; x < argc; x++) { > + len += strlen(argv[x]) + 1; > + buf = xrealloc(buf, 1, len); > + strlcat(buf, " ", len); > + strlcat(buf, argv[x], len); > + } > + > + return (buf); > +} > + > struct cmd * > cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) > { > diff --git a/format.c b/format.c > index 6f988b9..4a1a171 100644 > --- a/format.c > +++ b/format.c > @@ -368,7 +368,7 @@ format_get_command(struct window_pane *wp) > cmd = osdep_get_name(wp->fd, wp->tty); > if (cmd == NULL || *cmd == '\0') { > free(cmd); > - cmd = xstrdup(wp->cmd); > + cmd = cmd_stringify_argv(wp->argc, wp->argv); > if (cmd == NULL || *cmd == '\0') { > free(cmd); > cmd = xstrdup(wp->shell); > @@ -559,8 +559,11 @@ format_window_pane(struct format_tree *ft, struct > window_pane *wp) > if (wp->tty != NULL) > format_add(ft, "pane_tty", "%s", wp->tty); > format_add(ft, "pane_pid", "%ld", (long) wp->pid); > - if (wp->cmd != NULL) > - format_add(ft, "pane_start_command", "%s", wp->cmd); > + > + cmd = cmd_stringify_argv(wp->argc, wp->argv); > + format_add(ft, "pane_start_command", "%s", cmd); > + free(cmd); > + > if ((cwd = osdep_get_cwd(wp->fd)) != NULL) > format_add(ft, "pane_current_path", "%s", cwd); > if ((cmd = format_get_command(wp)) != NULL) { > diff --git a/names.c b/names.c > index 7c02961..0ed8836 100644 > --- a/names.c > +++ b/names.c > @@ -68,8 +68,15 @@ window_name_callback(unused int fd, unused short events, > void *data) > char * > default_window_name(struct window *w) > { > - if (w->active->cmd != NULL && *w->active->cmd != '\0') > - return (parse_window_name(w->active->cmd)); > + char *wpcmd, *s; > + > + wpcmd = cmd_stringify_argv(w->active->argc, w->active->argv); > + if (wpcmd != NULL && *wpcmd != '\0') { > + s = parse_window_name(wpcmd); > + free(wpcmd); > + return (s); > + } > + free(wpcmd); > return (parse_window_name(w->active->shell)); > } > > diff --git a/session.c b/session.c > index ae6f35c..f8d0a14 100644 > --- a/session.c > +++ b/session.c > @@ -84,9 +84,9 @@ session_find_by_id(u_int id) > > /* Create a new session. */ > struct session * > -session_create(const char *name, const char *cmd, const char *path, int cwd, > - struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy, > - char **cause) > +session_create(const char *name, int argc, char *const *argv, const char > *path, > + int cwd, struct environ *env, struct termios *tio, int idx, u_int sx, > + u_int sy, char **cause) > { > struct session *s; > > @@ -131,8 +131,9 @@ session_create(const char *name, const char *cmd, const > char *path, int cwd, > } > RB_INSERT(sessions, &sessions, s); > > - if (cmd != NULL) { > - if (session_new(s, NULL, cmd, path, cwd, idx, cause) == NULL) { > + if (argc >= 0) { > + if (session_new( > + s, NULL, argc, argv, path, cwd, idx, cause) == NULL) { > session_destroy(s); > return (NULL); > } > @@ -226,7 +227,7 @@ session_previous_session(struct session *s) > > /* Create a new window on a session. */ > struct winlink * > -session_new(struct session *s, const char *name, const char *cmd, > +session_new(struct session *s, const char *name, int argc, char *const *argv, > const char *path, int cwd, int idx, char **cause) > { > struct window *w; > @@ -250,8 +251,8 @@ session_new(struct session *s, const char *name, const > char *cmd, > shell = _PATH_BSHELL; > > hlimit = options_get_number(&s->options, "history-limit"); > - w = window_create(name, cmd, path, shell, cwd, &env, s->tio, s->sx, > - s->sy, hlimit, cause); > + w = window_create(name, argc, argv, path, shell, cwd, &env, s->tio, > + s->sx, s->sy, hlimit, cause); > if (w == NULL) { > winlink_remove(&s->windows, wl); > environ_free(&env); > diff --git a/tmux.h b/tmux.h > index 6f29126..7fcead7 100644 > --- a/tmux.h > +++ b/tmux.h > @@ -908,7 +908,8 @@ struct window_pane { > #define PANE_RESIZE 0x8 > #define PANE_FOCUSPUSH 0x10 > > - char *cmd; > + int argc; > + char **argv; > char *shell; > int cwd; > > @@ -1735,6 +1736,7 @@ int cmd_pack_argv(int, char **, char *, > size_t); > int cmd_unpack_argv(char *, size_t, int, char ***); > char **cmd_copy_argv(int, char *const *); > void cmd_free_argv(int, char **); > +char *cmd_stringify_argv(int, char *const *); > struct cmd *cmd_parse(int, char **, const char *, u_int, char **); > size_t cmd_print(struct cmd *, char *, size_t); > struct session *cmd_current_session(struct cmd_q *, int); > @@ -2126,7 +2128,7 @@ void winlink_stack_remove(struct > winlink_stack *, struct winlink *); > int window_index(struct window *, u_int *); > struct window *window_find_by_id(u_int); > struct window *window_create1(u_int, u_int); > -struct window *window_create(const char *, const char *, const char *, > +struct window *window_create(const char *, int, char *const *, const > char *, > const char *, int, struct environ *, struct termios *, > u_int, u_int, u_int, char **); > void window_destroy(struct window *); > @@ -2152,7 +2154,7 @@ struct window_pane *window_pane_find_by_id(u_int); > struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); > void window_pane_destroy(struct window_pane *); > void window_pane_timer_start(struct window_pane *); > -int window_pane_spawn(struct window_pane *, const char *, > +int window_pane_spawn(struct window_pane *, int, char *const *, > const char *, const char *, int, struct environ *, > struct termios *, char **); > void window_pane_resize(struct window_pane *, u_int, u_int); > @@ -2290,16 +2292,16 @@ RB_PROTOTYPE(sessions, session, entry, session_cmp); > int session_alive(struct session *); > struct session *session_find(const char *); > struct session *session_find_by_id(u_int); > -struct session *session_create(const char *, const char *, const char > *, int, > - struct environ *, struct termios *, int, u_int, u_int, > +struct session *session_create(const char *, int, char *const *, const > char *, > + int, struct environ *, struct termios *, int, u_int, u_int, > char **); > void session_destroy(struct session *); > int session_check_name(const char *); > void session_update_activity(struct session *); > struct session *session_next_session(struct session *); > struct session *session_previous_session(struct session *); > -struct winlink *session_new(struct session *, const char *, const char > *, > - const char *, int, int, char **); > +struct winlink *session_new(struct session *, const char *, int, > + char *const *, const char *, int, int, char **); > struct winlink *session_attach( > struct session *, struct window *, int, char **); > int session_detach(struct session *, struct winlink *); > diff --git a/window.c b/window.c > index ee851bd..196832f 100644 > --- a/window.c > +++ b/window.c > @@ -307,7 +307,7 @@ window_create1(u_int sx, u_int sy) > } > > struct window * > -window_create(const char *name, const char *cmd, const char *path, > +window_create(const char *name, int argc, char *const *argv, const char > *path, > const char *shell, int cwd, struct environ *env, struct termios *tio, > u_int sx, u_int sy, u_int hlimit, char **cause) > { > @@ -318,7 +318,7 @@ window_create(const char *name, const char *cmd, const > char *path, > wp = window_add_pane(w, hlimit); > layout_init(w, wp); > > - if (window_pane_spawn(wp, cmd, path, shell, cwd, env, tio, > + if (window_pane_spawn(wp, argc, argv, path, shell, cwd, env, tio, > cause) != 0) { > window_destroy(w); > return (NULL); > @@ -736,7 +736,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, > u_int hlimit) > wp->id = next_window_pane_id++; > RB_INSERT(window_pane_tree, &all_window_panes, wp); > > - wp->cmd = NULL; > + wp->argc = 0; > + wp->argv = NULL; > wp->shell = NULL; > wp->cwd = -1; > > @@ -808,17 +809,17 @@ window_pane_destroy(struct window_pane *wp) > > close(wp->cwd); > free(wp->shell); > - free(wp->cmd); > + cmd_free_argv(wp->argc, wp->argv); > free(wp); > } > > int > -window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path, > - const char *shell, int cwd, struct environ *env, struct termios *tio, > - char **cause) > +window_pane_spawn(struct window_pane *wp, int argc, char *const *argv, > + const char *path, const char *shell, int cwd, struct environ *env, > + struct termios *tio, char **cause) > { > struct winsize ws; > - char *argv0, paneid[16]; > + char *argv0, *cmd, *wpcmd, **exec_argv, paneid[16]; > const char *ptr; > struct termios tio2; > #ifdef HAVE_UTEMPTER > @@ -829,9 +830,10 @@ window_pane_spawn(struct window_pane *wp, const char > *cmd, const char *path, > bufferevent_free(wp->event); > close(wp->fd); > } > - if (cmd != NULL) { > - free(wp->cmd); > - wp->cmd = xstrdup(cmd); > + if (argc > 0) { > + cmd_free_argv(wp->argc, wp->argv); > + wp->argc = argc; > + wp->argv = cmd_copy_argv(argc, argv); > } > if (shell != NULL) { > free(wp->shell); > @@ -842,7 +844,8 @@ window_pane_spawn(struct window_pane *wp, const char > *cmd, const char *path, > wp->cwd = dup(cwd); > } > > - log_debug("spawn: %s -- %s", wp->shell, wp->cmd); > + wpcmd = cmd_stringify_argv(wp->argc, wp->argv); > + log_debug("spawn: %s -- %s", wp->shell, wpcmd); > > memset(&ws, 0, sizeof ws); > ws.ws_col = screen_size_x(&wp->base); > @@ -851,7 +854,10 @@ window_pane_spawn(struct window_pane *wp, const char > *cmd, const char *path, > switch (wp->pid = forkpty(&wp->fd, wp->tty, NULL, &ws)) { > case -1: > wp->fd = -1; > + cmd = cmd_stringify_argv(argc, argv); > xasprintf(cause, "%s: %s", cmd, strerror(errno)); > + free(cmd); > + free(wpcmd); > return (-1); > case 0: > if (fchdir(wp->cwd) != 0) > @@ -883,14 +889,24 @@ window_pane_spawn(struct window_pane *wp, const char > *cmd, const char *path, > setenv("SHELL", wp->shell, 1); > ptr = strrchr(wp->shell, '/'); > > - if (*wp->cmd != '\0') { > + if (wp->argc > 0) { > /* Use the command. */ > - if (ptr != NULL && *(ptr + 1) != '\0') > - xasprintf(&argv0, "%s", ptr + 1); > - else > - xasprintf(&argv0, "%s", wp->shell); > - execl(wp->shell, argv0, "-c", wp->cmd, (char *) NULL); > - fatal("execl failed"); > + if (wp->argc == 1) { > + if (ptr != NULL && *(ptr + 1) != '\0') > + xasprintf(&argv0, "%s", ptr + 1); > + else > + xasprintf(&argv0, "%s", wp->shell); > + execl(wp->shell, argv0, "-c", wp->argv[0], > + (char *) NULL); > + fatal("execl failed"); > + } > + > + exec_argv = cmd_copy_argv(wp->argc, wp->argv); > + exec_argv = xrealloc(exec_argv, wp->argc + 1, > + sizeof *wp->argv); > + exec_argv[wp->argc] = NULL; > + execvp(exec_argv[0], exec_argv); > + fatal("execvp failed"); > } > > /* No command; fork a login shell. */ > @@ -913,6 +929,7 @@ window_pane_spawn(struct window_pane *wp, const char > *cmd, const char *path, > window_pane_read_callback, NULL, window_pane_error_callback, wp); > bufferevent_enable(wp->event, EV_READ|EV_WRITE); > > + free(wpcmd); > return (0); > } > ------------------------------------------------------------------------------ Start Your Social Network Today - Download eXo Platform Build your Enterprise Intranet with eXo Platform Software Java Based Open Source Intranet - Social, Extensible, Cloud Ready Get Started Now And Turn Your Intranet Into A Collaboration Platform http://p.sf.net/sfu/ExoPlatform _______________________________________________ tmux-users mailing list tmux-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/tmux-users