aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES31
-rw-r--r--COPYING7
-rw-r--r--Makefile.am3
-rw-r--r--TODO7
-rw-r--r--arguments.c157
-rw-r--r--cfg.c33
-rw-r--r--cmd-attach-session.c2
-rw-r--r--cmd-join-pane.c10
-rw-r--r--cmd-kill-session.c4
-rw-r--r--cmd-new-session.c107
-rw-r--r--cmd-new-window.c124
-rw-r--r--cmd-queue.c58
-rw-r--r--cmd-respawn-pane.c74
-rw-r--r--cmd-respawn-window.c86
-rw-r--r--cmd-rotate-window.c4
-rw-r--r--cmd-select-pane.c6
-rw-r--r--cmd-select-window.c4
-rw-r--r--cmd-set-hook.c133
-rw-r--r--cmd-set-option.c39
-rw-r--r--cmd-show-options.c179
-rw-r--r--cmd-split-window.c152
-rw-r--r--cmd-string.c5
-rw-r--r--cmd-swap-pane.c10
-rw-r--r--cmd-switch-client.c4
-rw-r--r--cmd.c17
-rw-r--r--compat.h1
-rw-r--r--configure.ac2
-rw-r--r--environ.c12
-rw-r--r--format.c44
-rw-r--r--hooks.c173
-rw-r--r--input.c2
-rw-r--r--key-bindings.c3
-rw-r--r--layout.c20
-rw-r--r--notify.c74
-rw-r--r--options-table.c89
-rw-r--r--options.c273
-rw-r--r--osdep-darwin.c3
-rw-r--r--paste.c9
-rw-r--r--regress/control-client-sanity.sh42
-rw-r--r--screen-write.c2
-rw-r--r--server-client.c1
-rw-r--r--server-fn.c4
-rw-r--r--server.c2
-rw-r--r--session.c71
-rw-r--r--spawn.c434
-rw-r--r--status.c13
-rw-r--r--style.c2
-rw-r--r--tmux.1111
-rw-r--r--tmux.c3
-rw-r--r--tmux.h120
-rw-r--r--tools/24-bit-color.sh20
-rw-r--r--tty-keys.c10
-rw-r--r--tty-term.c6
-rw-r--r--tty.c16
-rw-r--r--window-buffer.c16
-rw-r--r--window-copy.c1825
-rw-r--r--window-tree.c9
-rw-r--r--window.c231
58 files changed, 3121 insertions, 1778 deletions
diff --git a/CHANGES b/CHANGES
index a3da3ef0..82fb21a4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,34 @@
+CHANGES FROM 2.9 to X.X
+
+* Add keys to jump between matching brackets (emacs C-M-f and C-M-b, vi %).
+
+* Add a -e flag to new-window, split-window, respawn-window, respawn-pane to
+ pass environment variables into the newly created process.
+
+* Hooks are now stored in the options tree as array options, allowing them to
+ have multiple separate commands. set-hook and show-hooks remain but
+ set-option and show-options can now also be used (show-options will only show
+ hooks if given the -H flag). Hooks with multiple commands are run in index
+ order.
+
+* Automatically scroll if dragging to create a selection with the mouse and the
+ cursor reaches the top or bottom line.
+
+* Add -no-clear variants of copy-selection and copy-pipe which do not clear the
+ selection after copying. Make copy-pipe clear the selection by default to be
+ consistent with copy-selection.
+
+* Add an argument to copy commands to set the prefix for the buffer name, this
+ (for example) allows buffers for different sessions to be named separately.
+
+* Update session activity on focus event.
+
+* Pass target from source-file into the config file parser so formats in %if
+ and %endif have access to more useful variables.
+
+* Add the ability to infer an option type (server, session, window) from its
+ name to show-options (it was already present in set-option).
+
CHANGES FROM 2.9 to 2.9a
* Fix bugs in select-pane and the main-horizontal and main-vertical layouts.
diff --git a/COPYING b/COPYING
index 9d96b3e6..fd02a999 100644
--- a/COPYING
+++ b/COPYING
@@ -1,10 +1,7 @@
THIS IS FOR INFORMATION ONLY, CODE IS UNDER THE LICENCE AT THE TOP OF ITS FILE.
-The README, CHANGES, FAQ and TODO files are licensed under the ISC
-license. Files under examples/ remain copyright their authors unless otherwise
-stated in the file but permission has been received to distribute them with
-tmux. All other files have a license and copyright notice at their start,
-typically:
+The README, CHANGES, FAQ and TODO files are licensed under the ISC license. All
+other files have a license and copyright notice at their start, typically:
Copyright (c) <author>
diff --git a/Makefile.am b/Makefile.am
index 16c842c8..4f267a23 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -112,7 +112,6 @@ dist_tmux_SOURCES = \
cmd-send-keys.c \
cmd-set-buffer.c \
cmd-set-environment.c \
- cmd-set-hook.c \
cmd-set-option.c \
cmd-show-environment.c \
cmd-show-messages.c \
@@ -135,7 +134,6 @@ dist_tmux_SOURCES = \
format-draw.c \
grid-view.c \
grid.c \
- hooks.c \
input-keys.c \
input.c \
job.c \
@@ -160,6 +158,7 @@ dist_tmux_SOURCES = \
server-fn.c \
server.c \
session.c \
+ spawn.c \
status.c \
style.c \
tmux.c \
diff --git a/TODO b/TODO
index 4e252b5f..655f869c 100644
--- a/TODO
+++ b/TODO
@@ -52,7 +52,6 @@
need to be suspended
* allow the prefix for automatic buffers to be specified as part of the
key binding to allow session buffers or similar (GitHub issue 1501)
- * copy-pipe should be synchronous (GitHub issue 1517)
- layout stuff
* way to tag a layout as a number/name
@@ -148,14 +147,8 @@
TODO soonish maybe:
-- Store hooks as options, issue 1619.
-- Support buffer prefixes, issue 1501.
-- copy-pipe should be synchronous, issue 1517.
-- -E flag to pass environment to new-*, issue 1498.
- Copy mode searching is slow when there is a big history, issue 1545.
- Grid "block" stuff, issue 1269. Can be used potentially for compression of
history (bit silly really though), reflow performance (can reflow blocks on
demand). It would possibly be good if history-limit could be global and
collected LRU.
-- Command aliases should be able to override builtin commands in order to add
- default flags (some mechanism needed to avoid recursion). GitHub issue 1630.
diff --git a/arguments.c b/arguments.c
index 451ccc42..ebb746a0 100644
--- a/arguments.c
+++ b/arguments.c
@@ -28,9 +28,15 @@
* Manipulate command arguments.
*/
+struct args_value {
+ char *value;
+ TAILQ_ENTRY(args_value) entry;
+};
+TAILQ_HEAD(args_values, args_value);
+
struct args_entry {
u_char flag;
- char *value;
+ struct args_values values;
RB_ENTRY(args_entry) entry;
};
@@ -92,12 +98,18 @@ args_free(struct args *args)
{
struct args_entry *entry;
struct args_entry *entry1;
+ struct args_value *value;
+ struct args_value *value1;
cmd_free_argv(args->argc, args->argv);
RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
RB_REMOVE(args_tree, &args->tree, entry);
- free(entry->value);
+ TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
+ TAILQ_REMOVE(&entry->values, value, entry);
+ free(value->value);
+ free(value);
+ }
free(entry);
}
@@ -123,22 +135,69 @@ args_print_add(char **buf, size_t *len, const char *fmt, ...)
free(s);
}
+/* Add value to string. */
+static void
+args_print_add_value(char **buf, size_t *len, struct args_entry *entry,
+ struct args_value *value)
+{
+ static const char quoted[] = " #\"';$";
+ char *escaped;
+ int flags;
+
+ if (**buf != '\0')
+ args_print_add(buf, len, " -%c ", entry->flag);
+ else
+ args_print_add(buf, len, "-%c ", entry->flag);
+
+ flags = VIS_OCTAL|VIS_TAB|VIS_NL;
+ if (value->value[strcspn(value->value, quoted)] != '\0')
+ flags |= VIS_DQ;
+ utf8_stravis(&escaped, value->value, flags);
+ if (flags & VIS_DQ)
+ args_print_add(buf, len, "\"%s\"", escaped);
+ else
+ args_print_add(buf, len, "%s", escaped);
+ free(escaped);
+}
+
+/* Add argument to string. */
+static void
+args_print_add_argument(char **buf, size_t *len, const char *argument)
+{
+ static const char quoted[] = " #\"';$";
+ char *escaped;
+ int flags;
+
+ if (**buf != '\0')
+ args_print_add(buf, len, " ");
+
+ flags = VIS_OCTAL|VIS_TAB|VIS_NL;
+ if (argument[strcspn(argument, quoted)] != '\0')
+ flags |= VIS_DQ;
+ utf8_stravis(&escaped, argument, flags);
+ if (flags & VIS_DQ)
+ args_print_add(buf, len, "\"%s\"", escaped);
+ else
+ args_print_add(buf, len, "%s", escaped);
+ free(escaped);
+}
+
/* Print a set of arguments. */
char *
args_print(struct args *args)
{
size_t len;
- char *buf, *escaped;
- int i, flags;
+ char *buf;
+ int i;
struct args_entry *entry;
- static const char quoted[] = " #\"';$";
+ struct args_value *value;
len = 1;
buf = xcalloc(1, len);
/* Process the flags first. */
RB_FOREACH(entry, args_tree, &args->tree) {
- if (entry->value != NULL)
+ if (!TAILQ_EMPTY(&entry->values))
continue;
if (*buf == '\0')
@@ -148,40 +207,13 @@ args_print(struct args *args)
/* Then the flags with arguments. */
RB_FOREACH(entry, args_tree, &args->tree) {
- if (entry->value == NULL)
- continue;
-
- if (*buf != '\0')
- args_print_add(&buf, &len, " -%c ", entry->flag);
- else
- args_print_add(&buf, &len, "-%c ", entry->flag);
-
- flags = VIS_OCTAL|VIS_TAB|VIS_NL;
- if (entry->value[strcspn(entry->value, quoted)] != '\0')
- flags |= VIS_DQ;
- utf8_stravis(&escaped, entry->value, flags);
- if (flags & VIS_DQ)
- args_print_add(&buf, &len, "\"%s\"", escaped);
- else
- args_print_add(&buf, &len, "%s", escaped);
- free(escaped);
+ TAILQ_FOREACH(value, &entry->values, entry)
+ args_print_add_value(&buf, &len, entry, value);
}
/* And finally the argument vector. */
- for (i = 0; i < args->argc; i++) {
- if (*buf != '\0')
- args_print_add(&buf, &len, " ");
-
- flags = VIS_OCTAL|VIS_TAB|VIS_NL;
- if (args->argv[i][strcspn(args->argv[i], quoted)] != '\0')
- flags |= VIS_DQ;
- utf8_stravis(&escaped, args->argv[i], flags);
- if (flags & VIS_DQ)
- args_print_add(&buf, &len, "\"%s\"", escaped);
- else
- args_print_add(&buf, &len, "%s", escaped);
- free(escaped);
- }
+ for (i = 0; i < args->argc; i++)
+ args_print_add_argument(&buf, &len, args->argv[i]);
return (buf);
}
@@ -195,22 +227,24 @@ args_has(struct args *args, u_char ch)
/* Set argument value in the arguments tree. */
void
-args_set(struct args *args, u_char ch, const char *value)
+args_set(struct args *args, u_char ch, const char *s)
{
struct args_entry *entry;
+ struct args_value *value;
- /* Replace existing argument. */
- if ((entry = args_find(args, ch)) != NULL) {
- free(entry->value);
- entry->value = NULL;
- } else {
+ entry = args_find(args, ch);
+ if (entry == NULL) {
entry = xcalloc(1, sizeof *entry);
entry->flag = ch;
+ TAILQ_INIT(&entry->values);
RB_INSERT(args_tree, &args->tree, entry);
}
- if (value != NULL)
- entry->value = xstrdup(value);
+ if (s != NULL) {
+ value = xcalloc(1, sizeof *value);
+ value->value = xstrdup(s);
+ TAILQ_INSERT_TAIL(&entry->values, value, entry);
+ }
}
/* Get argument value. Will be NULL if it isn't present. */
@@ -221,7 +255,34 @@ args_get(struct args *args, u_char ch)
if ((entry = args_find(args, ch)) == NULL)
return (NULL);
- return (entry->value);
+ return (TAILQ_LAST(&entry->values, args_values)->value);
+}
+
+/* Get first value in argument. */
+const char *
+args_first_value(struct args *args, u_char ch, struct args_value **value)
+{
+ struct args_entry *entry;
+
+ if ((entry = args_find(args, ch)) == NULL)
+ return (NULL);
+
+ *value = TAILQ_FIRST(&entry->values);
+ if (*value == NULL)
+ return (NULL);
+ return ((*value)->value);
+}
+
+/* Get next value in argument. */
+const char *
+args_next_value(struct args_value **value)
+{
+ if (*value == NULL)
+ return (NULL);
+ *value = TAILQ_NEXT(*value, entry);
+ if (*value == NULL)
+ return (NULL);
+ return ((*value)->value);
}
/* Convert an argument value to a number. */
@@ -232,13 +293,15 @@ args_strtonum(struct args *args, u_char ch, long long minval, long long maxval,
const char *errstr;
long long ll;
struct args_entry *entry;
+ struct args_value *value;
if ((entry = args_find(args, ch)) == NULL) {
*cause = xstrdup("missing");
return (0);
}
+ value = TAILQ_LAST(&entry->values, args_values);
- ll = strtonum(entry->value, minval, maxval, &errstr);
+ ll = strtonum(value->value, minval, maxval, &errstr);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
diff --git a/cfg.c b/cfg.c
index 84db9fb3..60735046 100644
--- a/cfg.c
+++ b/cfg.c
@@ -101,7 +101,8 @@ start_cfg(void)
cmdq_append(c, cfg_item);
}
- load_cfg(TMUX_CONF, NULL, NULL, 1);
+ if (cfg_file == NULL)
+ load_cfg(TMUX_CONF, NULL, NULL, 1);
if (cfg_file == NULL && (home = find_home()) != NULL) {
xasprintf(&cfg_file, "%s/.tmux.conf", home);
@@ -114,7 +115,8 @@ start_cfg(void)
}
static int
-cfg_check_condition(const char *path, size_t line, const char *p, int *skip)
+cfg_check_cond(const char *path, size_t line, const char *p, int *skip,
+ struct client *c, struct cmd_find_state *fs)
{
struct format_tree *ft;
char *s;
@@ -129,6 +131,10 @@ cfg_check_condition(const char *path, size_t line, const char *p, int *skip)
}
ft = format_create(NULL, NULL, FORMAT_NONE, FORMAT_NOJOBS);
+ if (fs != NULL)
+ format_defaults(ft, c, fs->s, fs->wl, fs->wp);
+ else
+ format_defaults(ft, c, NULL, NULL, NULL);
s = format_expand(ft, p);
result = format_true(s);
free(s);
@@ -140,7 +146,7 @@ cfg_check_condition(const char *path, size_t line, const char *p, int *skip)
static void
cfg_handle_if(const char *path, size_t line, struct cfg_conds *conds,
- const char *p)
+ const char *p, struct client *c, struct cmd_find_state *fs)
{
struct cfg_cond *cond;
struct cfg_cond *parent = TAILQ_FIRST(conds);
@@ -152,7 +158,7 @@ cfg_handle_if(const char *path, size_t line, struct cfg_conds *conds,
cond = xcalloc(1, sizeof *cond);
cond->line = line;
if (parent == NULL || parent->met)
- cond->met = cfg_check_condition(path, line, p, &cond->skip);
+ cond->met = cfg_check_cond(path, line, p, &cond->skip, c, fs);
else
cond->skip = 1;
cond->saw_else = 0;
@@ -161,7 +167,7 @@ cfg_handle_if(const char *path, size_t line, struct cfg_conds *conds,
static void
cfg_handle_elif(const char *path, size_t line, struct cfg_conds *conds,
- const char *p)
+ const char *p, struct client *c, struct cmd_find_state *fs)
{
struct cfg_cond *cond = TAILQ_FIRST(conds);
@@ -172,7 +178,7 @@ cfg_handle_elif(const char *path, size_t line, struct cfg_conds *conds,
if (cond == NULL || cond->saw_else)
cfg_add_cause("%s:%zu: unexpected %%elif", path, line);
else if (!cond->skip)
- cond->met = cfg_check_condition(path, line, p, &cond->skip);
+ cond->met = cfg_check_cond(path, line, p, &cond->skip, c, fs);
else
cond->met = 0;
}
@@ -213,16 +219,16 @@ cfg_handle_endif(const char *path, size_t line, struct cfg_conds *conds)
static void
cfg_handle_directive(const char *p, const char *path, size_t line,
- struct cfg_conds *conds)
+ struct cfg_conds *conds, struct client *c, struct cmd_find_state *fs)
{
int n = 0;
while (p[n] != '\0' && !isspace((u_char)p[n]))
n++;
if (strncmp(p, "%if", n) == 0)
- cfg_handle_if(path, line, conds, p + n);
+ cfg_handle_if(path, line, conds, p + n, c, fs);
else if (strncmp(p, "%elif", n) == 0)
- cfg_handle_elif(path, line, conds, p + n);
+ cfg_handle_elif(path, line, conds, p + n, c, fs);
else if (strcmp(p, "%else") == 0)
cfg_handle_else(path, line, conds);
else if (strcmp(p, "%endif") == 0)
@@ -243,6 +249,13 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet)
struct cmdq_item *new_item;
struct cfg_cond *cond, *cond1;
struct cfg_conds conds;
+ struct cmd_find_state *fs = NULL;
+ struct client *fc = NULL;
+
+ if (item != NULL) {
+ fs = &item->target;
+ fc = cmd_find_client(item, NULL, 1);
+ }
TAILQ_INIT(&conds);
@@ -269,7 +282,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet)
*q-- = '\0';
if (*p == '%') {
- cfg_handle_directive(p, path, line, &conds);
+ cfg_handle_directive(p, path, line, &conds, fc, fs);
continue;
}
cond = TAILQ_FIRST(&conds);
diff --git a/cmd-attach-session.c b/cmd-attach-session.c
index 73ff530d..bcd6d895 100644
--- a/cmd-attach-session.c
+++ b/cmd-attach-session.c
@@ -87,7 +87,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (wl != NULL) {
if (wp != NULL)
- window_set_active_pane(wp->window, wp);
+ window_set_active_pane(wp->window, wp, 1);
session_set_current(s, wl);
if (wp != NULL)
cmd_find_from_winlink_pane(current, wl, wp, 0);
diff --git a/cmd-join-pane.c b/cmd-join-pane.c
index 947b2499..3d798e09 100644
--- a/cmd-join-pane.c
+++ b/cmd-join-pane.c
@@ -71,7 +71,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
int size, percentage, dst_idx;
enum layout_type type;
struct layout_cell *lc;
- int not_same_window;
+ int not_same_window, flags;
if (self->entry == &cmd_join_pane_entry)
not_same_window = 1;
@@ -123,7 +123,11 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
else
size = (dst_wp->sx * percentage) / 100;
}
- lc = layout_split_pane(dst_wp, type, size, args_has(args, 'b'), 0);
+ if (args_has(args, 'b'))
+ flags = SPAWN_BEFORE;
+ else
+ flags = 0;
+ lc = layout_split_pane(dst_wp, type, size, flags);
if (lc == NULL) {
cmdq_error(item, "create pane failed: pane too small");
return (CMD_RETURN_ERROR);
@@ -144,7 +148,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
server_redraw_window(dst_w);
if (!args_has(args, 'd')) {
- window_set_active_pane(dst_w, src_wp);
+ window_set_active_pane(dst_w, src_wp, 1);
session_select(dst_s, dst_idx);
cmd_find_from_session(current, dst_s, 0);
server_redraw_session(dst_s);
diff --git a/cmd-kill-session.c b/cmd-kill-session.c
index 00ea7789..dcef8097 100644
--- a/cmd-kill-session.c
+++ b/cmd-kill-session.c
@@ -61,12 +61,12 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) {
if (sloop != s) {
server_destroy_session(sloop);
- session_destroy(sloop, __func__);
+ session_destroy(sloop, 1, __func__);
}
}
} else {
server_destroy_session(s);
- session_destroy(s, __func__);
+ session_destroy(s, 1, __func__);
}
return (CMD_RETURN_NORMAL);
}
diff --git a/cmd-new-session.c b/cmd-new-session.c
index e1be350b..6818ce9c 100644
--- a/cmd-new-session.c
+++ b/cmd-new-session.c
@@ -69,20 +69,17 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = self->args;
struct client *c = item->client;
struct session *s, *as, *groupwith;
- struct window *w;
struct environ *env;
struct options *oo;
struct termios tio, *tiop;
struct session_group *sg;
- const char *errstr, *template, *group, *prefix;
- const char *path, *cmd, *tmp, *value;
- char **argv, *cause, *cp, *newname, *cwd = NULL;
- int detached, already_attached, idx, argc;
- int is_control = 0;
- u_int sx, sy, dsx = 80, dsy = 24;
- struct environ_entry *envent;
- struct cmd_find_state fs;
+ const char *errstr, *template, *group, *prefix, *tmp;
+ char *cause, *cwd = NULL, *cp, *newname = NULL;
+ int detached, already_attached, is_control = 0;
+ u_int sx, sy, dsx, dsy;
+ struct spawn_context sc;
enum cmd_retval retval;
+ struct cmd_find_state fs;
if (self->entry == &cmd_has_session_entry) {
/*
@@ -97,13 +94,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
- newname = NULL;
if (args_has(args, 's')) {
newname = format_single(item, args_get(args, 's'), c, NULL,
NULL, NULL);
if (!session_check_name(newname)) {
cmdq_error(item, "bad session name: %s", newname);
- goto error;
+ goto fail;
}
if ((as = session_find(newname)) != NULL) {
if (args_has(args, 'A')) {
@@ -114,7 +110,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
return (retval);
}
cmdq_error(item, "duplicate session: %s", newname);
- goto error;
+ goto fail;
}
}
@@ -125,7 +121,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (groupwith == NULL) {
if (!session_check_name(group)) {
cmdq_error(item, "bad group name: %s", group);
- goto error;
+ goto fail;
}
sg = session_group_find(group);
} else
@@ -173,7 +169,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (server_client_check_nested(item->client)) {
cmdq_error(item, "sessions should be nested with care, "
"unset $TMUX to force");
- return (CMD_RETURN_ERROR);
+ goto fail;
}
if (tcgetattr(c->tty.fd, &tio) != 0)
fatal("tcgetattr failed");
@@ -186,7 +182,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (server_client_open(c, &cause) != 0) {
cmdq_error(item, "open terminal failed: %s", cause);
free(cause);
- goto error;
+ goto fail;
}
}
@@ -200,7 +196,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
dsx = strtonum(tmp, 1, USHRT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "width %s", errstr);
- goto error;
+ goto fail;
}
}
}
@@ -213,7 +209,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
dsy = strtonum(tmp, 1, USHRT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "height %s", errstr);
- goto error;
+ goto fail;
}
}
}
@@ -225,8 +221,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (sy > 0 && options_get_number(global_s_options, "status"))
sy--;
} else {
- value = options_get_string(global_s_options, "default-size");
- if (sscanf(value, "%ux%u", &sx, &sy) != 2) {
+ tmp = options_get_string(global_s_options, "default-size");
+ if (sscanf(tmp, "%ux%u", &sx, &sy) != 2) {
sx = 80;
sy = 24;
}
@@ -240,59 +236,34 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (sy == 0)
sy = 1;
- /* Figure out the command for the new window. */
- argc = -1;
- argv = NULL;
- if (!args_has(args, 't') && args->argc != 0) {
- argc = args->argc;
- argv = args->argv;
- } else if (sg == NULL && groupwith == NULL) {
- cmd = options_get_string(global_s_options, "default-command");
- if (cmd != NULL && *cmd != '\0') {
- argc = 1;
- argv = (char **)&cmd;
- } else {
- argc = 0;
- argv = NULL;
- }
- }
-
- path = NULL;
- if (c != NULL && c->session == NULL)
- envent = environ_find(c->environ, "PATH");
- else
- envent = environ_find(global_environ, "PATH");
- if (envent != NULL)
- path = envent->value;
-
- /* Construct the environment. */
+ /* Create the new session. */
+ oo = options_create(global_s_options);
+ if (args_has(args, 'x') || args_has(args, 'y'))
+ options_set_string(oo, "default-size", 0, "%ux%u", dsx, dsy);
env = environ_create();
if (c != NULL && !args_has(args, 'E'))
environ_update(global_s_options, c->environ, env);
+ s = session_create(prefix, newname, cwd, env, oo, tiop);
- /* Set up the options. */
- oo = options_create(global_s_options);
- if (args_has(args, 'x') || args_has(args, 'y'))
- options_set_string(oo, "default-size", 0, "%ux%u", dsx, dsy);
+ /* Spawn the initial window. */
+ memset(&sc, 0, sizeof sc);
+ sc.item = item;
+ sc.s = s;
- /* Create the new session. */
- idx = -1 - options_get_number(global_s_options, "base-index");
- s = session_create(prefix, newname, argc, argv, path, cwd, env, oo,
- tiop, idx, &cause);
- environ_free(env);
- if (s == NULL) {
- cmdq_error(item, "create session failed: %s", cause);
- free(cause);
- goto error;
- }
+ sc.name = args_get(args, 'n');
+ sc.argc = args->argc;
+ sc.argv = args->argv;
- /* Set the initial window name if one given. */
- if (argc >= 0 && (tmp = args_get(args, 'n')) != NULL) {
- cp = format_single(item, tmp, c, s, NULL, NULL);
- w = s->curw->window;
- window_set_name(w, cp);
- options_set_number(w->options, "automatic-rename", 0);
- free(cp);
+ sc.idx = -1;
+ sc.cwd = args_get(args, 'c');
+
+ sc.flags = 0;
+
+ if (spawn_window(&sc, &cause) == NULL) {
+ session_destroy(s, 0, __func__);
+ cmdq_error(item, "create window failed: %s", cause);
+ free(cause);
+ goto fail;
}
/*
@@ -358,13 +329,13 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
}
cmd_find_from_session(&fs, s, 0);
- hooks_insert(s->hooks, item, &fs, "after-new-session");
+ cmdq_insert_hook(s, item, &fs, "after-new-session");
free(cwd);
free(newname);
return (CMD_RETURN_NORMAL);
-error:
+fail:
free(cwd);
free(newname);
return (CMD_RETURN_ERROR);
diff --git a/cmd-new-window.c b/cmd-new-window.c
index 5baeff65..6cb33dd9 100644
--- a/cmd-new-window.c
+++ b/cmd-new-window.c
@@ -38,9 +38,9 @@ const struct cmd_entry cmd_new_window_entry = {
.name = "new-window",
.alias = "neww",
- .args = { "ac:dF:kn:Pt:", 0, -1 },
- .usage = "[-adkP] [-c start-directory] [-F format] [-n window-name] "
- CMD_TARGET_WINDOW_USAGE " [command]",
+ .args = { "ac:de:F:kn:Pt:", 0, -1 },
+ .usage = "[-adkP] [-c start-directory] [-e environment] [-F format] "
+ "[-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
@@ -53,87 +53,53 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_find_state *current = &item->shared->current;
+ struct spawn_context sc;
+ struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
- struct client *c = cmd_find_client(item, NULL, 1);
int idx = item->target.idx;
- const char *cmd, *path, *template, *tmp;
- char **argv, *cause, *cp, *cwd, *name;
- int argc, detached;
- struct environ_entry *envent;
+ struct winlink *new_wl;
+ char *cause = NULL, *cp;
+ const char *template, *add;
struct cmd_find_state fs;
+ struct args_value *value;
- if (args_has(args, 'a') && wl != NULL) {
- if ((idx = winlink_shuffle_up(s, wl)) == -1) {
- cmdq_error(item, "no free window indexes");
- return (CMD_RETURN_ERROR);
- }
- }
- detached = args_has(args, 'd');
-
- if (args->argc == 0) {
- cmd = options_get_string(s->options, "default-command");
- if (cmd != NULL && *cmd != '\0') {
- argc = 1;
- argv = (char **)&cmd;
- } else {
- argc = 0;
- argv = NULL;
- }
- } else {
- argc = args->argc;
- argv = args->argv;
+ if (args_has(args, 'a') && (idx = winlink_shuffle_up(s, wl)) == -1) {
+ cmdq_error(item, "couldn't get a window index");
+ return (CMD_RETURN_ERROR);
}
- path = NULL;
- if (item->client != NULL && item->client->session == NULL)
- envent = environ_find(item->client->environ, "PATH");
- else
- envent = environ_find(s->environ, "PATH");
- if (envent != NULL)
- path = envent->value;
-
- if ((tmp = args_get(args, 'c')) != NULL)
- cwd = format_single(item, tmp, c, s, NULL, NULL);
- else
- cwd = xstrdup(server_client_get_cwd(item->client, s));
-
- if ((tmp = args_get(args, 'n')) != NULL)
- name = format_single(item, tmp, c, s, NULL, NULL);
- else
- name = NULL;
-
- if (idx != -1)
- wl = winlink_find_by_index(&s->windows, idx);
- if (wl != NULL && args_has(args, 'k')) {
- /*
- * Can't use session_detach as it will destroy session if this
- * makes it empty.
- */
- notify_session_window("window-unlinked", s, wl->window);
- wl->flags &= ~WINLINK_ALERTFLAGS;
- winlink_stack_remove(&s->lastw, wl);
- winlink_remove(&s->windows, wl);
-
- /* Force select/redraw if current. */
- if (wl == s->curw) {
- detached = 0;
- s->curw = NULL;
- }
+ memset(&sc, 0, sizeof sc);
+ sc.item = item;
+ sc.s = s;
+
+ sc.name = args_get(args, 'n');
+ sc.argc = args->argc;
+ sc.argv = args->argv;
+ sc.environ = environ_create();
+
+ add = args_first_value(args, 'e', &value);
+ while (add != NULL) {
+ environ_put(sc.environ, add);
+ add = args_next_value(&value);
}
- if (idx == -1)
- idx = -1 - options_get_number(s->options, "base-index");
- wl = session_new(s, name, argc, argv, path, cwd, idx,
- &cause);
- if (wl == NULL) {
+ sc.idx = idx;
+ sc.cwd = args_get(args, 'c');
+
+ sc.flags = 0;
+ if (args_has(args, 'd'))
+ sc.flags |= SPAWN_DETACHED;
+ if (args_has(args, 'k'))
+ sc.flags |= SPAWN_KILL;
+
+ if ((new_wl = spawn_window(&sc, &cause)) == NULL) {
cmdq_error(item, "create window failed: %s", cause);
free(cause);
- goto error;
+ return (CMD_RETURN_ERROR);
}
- if (!detached) {
- session_select(s, wl->idx);
- cmd_find_from_winlink(current, wl, 0);
+ if (!args_has(args, 'd') || new_wl == s->curw) {
+ cmd_find_from_winlink(current, new_wl, 0);
server_redraw_session_group(s);
} else
server_status_session_group(s);
@@ -141,20 +107,14 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
template = NEW_WINDOW_TEMPLATE;
- cp = format_single(item, template, c, s, wl, NULL);
+ cp = format_single(item, template, c, s, new_wl, NULL);
cmdq_print(item, "%s", cp);
free(cp);
}
- cmd_find_from_winlink(&fs, wl, 0);
- hooks_insert(s->hooks, item, &fs, "after-new-window");
+ cmd_find_from_winlink(&fs, new_wl, 0);
+ cmdq_insert_hook(s, item, &fs, "after-new-window");
- free(name);
- free(cwd);
+ environ_free(sc.environ);
return (CMD_RETURN_NORMAL);
-
-error:
- free(name);
- free(cwd);
- return (CMD_RETURN_ERROR);
}
diff --git a/cmd-queue.c b/cmd-queue.c
index 97b3c1c9..9ce25f5f 100644
--- a/cmd-queue.c
+++ b/cmd-queue.c
@@ -98,6 +98,60 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
} while (item != NULL);
}
+
+/* Insert a hook. */
+void
+cmdq_insert_hook(struct session *s, struct cmdq_item *item,
+ struct cmd_find_state *fs, const char *fmt, ...)
+{
+ struct options *oo;
+ va_list ap;
+ char *name;
+ struct cmdq_item *new_item;
+ struct options_entry *o;
+ struct options_array_item *a;
+ struct cmd_list *cmdlist;
+
+ if (item->flags & CMDQ_NOHOOKS)
+ return;
+ if (s == NULL)
+ oo = global_s_options;
+ else
+ oo = s->options;
+
+ va_start(ap, fmt);
+ xvasprintf(&name, fmt, ap);
+ va_end(ap);
+
+ o = options_get(oo, name);
+ if (o == NULL) {
+ free(name);
+ return;
+ }
+ log_debug("running hook %s (parent %p)", name, item);
+
+ a = options_array_first(o);
+ while (a != NULL) {
+ cmdlist = options_array_item_value(a)->cmdlist;
+ if (cmdlist == NULL) {
+ a = options_array_next(a);
+ continue;
+ }
+
+ 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);
+
+ a = options_array_next(a);
+ }
+
+ free(name);
+}
+
/* Remove an item. */
static void
cmdq_remove(struct cmdq_item *item)
@@ -111,7 +165,7 @@ cmdq_remove(struct cmdq_item *item)
if (item->client != NULL)
server_client_unref(item->client);
- if (item->type == CMDQ_COMMAND)
+ if (item->cmdlist != NULL)
cmd_list_free(item->cmdlist);
TAILQ_REMOVE(item->queue, item, entry);
@@ -245,7 +299,7 @@ cmdq_fire_command(struct cmdq_item *item)
fsp = &fs;
else
goto out;
- hooks_insert(fsp->s->hooks, item, fsp, "after-%s", entry->name);
+ cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name);
}
out:
diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c
index eb4a7e09..5e23fa15 100644
--- a/cmd-respawn-pane.c
+++ b/cmd-respawn-pane.c
@@ -20,7 +20,7 @@
#include <sys/types.h>
#include <stdlib.h>
-#include <unistd.h>
+#include <string.h>
#include "tmux.h"
@@ -34,9 +34,9 @@ const struct cmd_entry cmd_respawn_pane_entry = {
.name = "respawn-pane",
.alias = "respawnp",
- .args = { "c:kt:", 0, -1 },
- .usage = "[-c start-directory] [-k] " CMD_TARGET_PANE_USAGE
- " [command]",
+ .args = { "c:e:kt:", 0, -1 },
+ .usage = "[-k] [-c start-directory] [-e environment] "
+ CMD_TARGET_PANE_USAGE " [command]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -48,53 +48,49 @@ static enum cmd_retval
cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
+ struct spawn_context sc;
+ struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
- struct window *w = wl->window;
struct window_pane *wp = item->target.wp;
- struct client *c = cmd_find_client(item, NULL, 1);
- struct session *s = item->target.s;
- struct environ *env;
- const char *path = NULL, *cp;
- char *cause, *cwd = NULL;
- u_int idx;
- struct environ_entry *envent;
-
- if (!args_has(self->args, 'k') && wp->fd != -1) {
- if (window_pane_index(wp, &idx) != 0)
- fatalx("index not found");
- cmdq_error(item, "pane still active: %s:%d.%u",
- s->name, wl->idx, idx);
- return (CMD_RETURN_ERROR);
+ char *cause = NULL;
+ const char *add;
+ struct args_value *value;
+
+ memset(&sc, 0, sizeof sc);
+ sc.item = item;
+ sc.s = s;
+ sc.wl = wl;
+
+ sc.wp0 = wp;
+ sc.lc = NULL;
+
+ sc.name = NULL;
+ sc.argc = args->argc;
+ sc.argv = args->argv;
+ sc.environ = environ_create();
+
+ add = args_first_value(args, 'e', &value);
+ while (add != NULL) {
+ environ_put(sc.environ, add);
+ add = args_next_value(&value);
}
- window_pane_reset_mode_all(wp);
- screen_reinit(&wp->base);
- input_init(wp);
-
- if (item->client != NULL && item->client->session == NULL)
- envent = environ_find(item->client->environ, "PATH");
- else
- envent = environ_find(s->environ, "PATH");
- if (envent != NULL)
- path = envent->value;
+ sc.idx = -1;
+ sc.cwd = args_get(args, 'c');
- if ((cp = args_get(args, 'c')) != NULL)
- cwd = format_single(item, cp, c, s, NULL, NULL);
+ sc.flags = SPAWN_RESPAWN;
+ if (args_has(args, 'k'))
+ sc.flags |= SPAWN_KILL;
- env = environ_for_session(s, 0);
- if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, cwd, env,
- s->tio, &cause) != 0) {
+ if (spawn_pane(&sc, &cause) == NULL) {
cmdq_error(item, "respawn pane failed: %s", cause);
free(cause);
- environ_free(env);
- free(cwd);
return (CMD_RETURN_ERROR);
}
- environ_free(env);
- free(cwd);
wp->flags |= PANE_REDRAW;
- server_status_window(w);
+ server_status_window(wp->window);
+ environ_free(sc.environ);
return (CMD_RETURN_NORMAL);
}
diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c
index 68791990..aec22912 100644
--- a/cmd-respawn-window.c
+++ b/cmd-respawn-window.c
@@ -19,7 +19,7 @@
#include <sys/types.h>
#include <stdlib.h>
-#include <unistd.h>
+#include <string.h>
#include "tmux.h"
@@ -34,9 +34,9 @@ const struct cmd_entry cmd_respawn_window_entry = {
.name = "respawn-window",
.alias = "respawnw",
- .args = { "c:kt:", 0, -1 },
- .usage = "[-c start-directory] [-k] " CMD_TARGET_WINDOW_USAGE
- " [command]",
+ .args = { "c:e:kt:", 0, -1 },
+ .usage = "[-k] [-c start-directory] [-e environment] "
+ CMD_TARGET_WINDOW_USAGE " [command]",
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -48,64 +48,44 @@ static enum cmd_retval
cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
+ struct spawn_context sc;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
- struct window *w = wl->window;
- struct window_pane *wp;
- struct client *c = cmd_find_client(item, NULL, 1);
- struct environ *env;
- const char *path = NULL, *cp;
- char *cause, *cwd = NULL;
- struct environ_entry *envent;
-
- if (!args_has(self->args, 'k')) {
- TAILQ_FOREACH(wp, &w->panes, entry) {
- if (wp->fd == -1)
- continue;
- cmdq_error(item, "window still active: %s:%d", s->name,
- wl->idx);
- return (CMD_RETURN_ERROR);
- }
+ char *cause = NULL;
+ const char *add;
+ struct args_value *value;
+
+ memset(&sc, 0, sizeof sc);
+ sc.item = item;
+ sc.s = s;
+ sc.wl = wl;
+
+ sc.name = NULL;
+ sc.argc = args->argc;
+ sc.argv = args->argv;
+ sc.environ = environ_create();
+
+ add = args_first_value(args, 'e', &value);
+ while (add != NULL) {
+ environ_put(sc.environ, add);
+ add = args_next_value(&value);
}
- wp = TAILQ_FIRST(&w->panes);
- TAILQ_REMOVE(&w->panes, wp, entry);
- layout_free(w);
- window_destroy_panes(w);
- TAILQ_INSERT_HEAD(&w->panes, wp, entry);
- window_pane_resize(wp, w->sx, w->sy);
-
- if (item->client != NULL && item->client->session == NULL)
- envent = environ_find(item->client->environ, "PATH");
- else
- envent = environ_find(s->environ, "PATH");
- if (envent != NULL)
- path = envent->value;
-
- if ((cp = args_get(args, 'c')) != NULL)
- cwd = format_single(item, cp, c, s, NULL, NULL);
-
- env = environ_for_session(s, 0);
- if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, cwd, env,
- s->tio, &cause) != 0) {
+ sc.idx = -1;
+ sc.cwd = args_get(args, 'c');
+
+ sc.flags = SPAWN_RESPAWN;
+ if (args_has(args, 'k'))
+ sc.flags |= SPAWN_KILL;
+
+ if (spawn_window(&sc, &cause) == NULL) {
cmdq_error(item, "respawn window failed: %s", cause);
free(cause);
- environ_free(env);
- free(cwd);
- server_destroy_pane(wp, 0);
return (CMD_RETURN_ERROR);
}
- environ_free(env);
- free(cwd);
-
- layout_init(w, wp);
- window_pane_reset_mode_all(wp);
- screen_reinit(&wp->base);
- input_init(wp);
- window_set_active_pane(w, wp);
- recalculate_sizes();
- server_redraw_window(w);
+ server_redraw_window(wl->window);
+ environ_free(sc.environ);
return (CMD_RETURN_NORMAL);
}
diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c
index 5a900a13..6dc0f2a8 100644
--- a/cmd-rotate-window.c
+++ b/cmd-rotate-window.c
@@ -77,7 +77,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL)
wp = TAILQ_LAST(&w->panes, window_panes);
- window_set_active_pane(w, wp);
+ window_set_active_pane(w, wp, 1);
cmd_find_from_winlink_pane(current, wl, wp, 0);
server_redraw_window(w);
} else {
@@ -105,7 +105,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
if ((wp = TAILQ_NEXT(w->active, entry)) == NULL)
wp = TAILQ_FIRST(&w->panes);
- window_set_active_pane(w, wp);
+ window_set_active_pane(w, wp, 1);
cmd_find_from_winlink_pane(current, wl, wp, 0);
server_redraw_window(w);
}
diff --git a/cmd-select-pane.c b/cmd-select-pane.c
index 747f0822..92ecb734 100644
--- a/cmd-select-pane.c
+++ b/cmd-select-pane.c
@@ -112,7 +112,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
else {
server_unzoom_window(w);
window_redraw_active_switch(w, lastwp);
- if (window_set_active_pane(w, lastwp)) {
+ if (window_set_active_pane(w, lastwp, 1)) {
cmd_find_from_winlink(current, wl, 0);
cmd_select_pane_redraw(w);
}
@@ -194,9 +194,9 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
server_unzoom_window(wp->window);
window_redraw_active_switch(w, wp);
- if (window_set_active_pane(w, wp)) {
+ if (window_set_active_pane(w, wp, 1)) {
cmd_find_from_winlink_pane(current, wl, wp, 0);
- hooks_insert(s->hooks, item, current, "after-select-pane");
+ cmdq_insert_hook(s, item, current, "after-select-pane");
cmd_select_pane_redraw(w);
}
diff --git a/cmd-select-window.c b/cmd-select-window.c
index f35b8202..54965e89 100644
--- a/cmd-select-window.c
+++ b/cmd-select-window.c
@@ -119,7 +119,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
}
cmd_find_from_session(current, s, 0);
server_redraw_session(s);
- hooks_insert(s->hooks, item, current, "after-select-window");
+ cmdq_insert_hook(s, item, current, "after-select-window");
} else {
/*
* If -T and select-window is invoked on same window as
@@ -137,7 +137,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
cmd_find_from_session(current, s, 0);
server_redraw_session(s);
}
- hooks_insert(s->hooks, item, current, "after-select-window");
+ cmdq_insert_hook(s, item, current, "after-select-window");
}
recalculate_sizes();
diff --git a/cmd-set-hook.c b/cmd-set-hook.c
deleted file mode 100644
index b04e6335..00000000
--- a/cmd-set-hook.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/* $OpenBSD$ */
-
-/*
- * Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
- * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "tmux.h"
-
-/*
- * Set or show global or session hooks.
- */
-
-static enum cmd_retval cmd_set_hook_exec(struct cmd *, struct cmdq_item *);
-
-const struct cmd_entry cmd_set_hook_entry = {
- .name = "set-hook",
- .alias = NULL,
-
- .args = { "gRt:u", 1, 2 },
- .usage = "[-gRu] " CMD_TARGET_SESSION_USAGE " hook-name [command]",
-
- .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
-
- .flags = CMD_AFTERHOOK,
- .exec = cmd_set_hook_exec
-};
-
-const struct cmd_entry cmd_show_hooks_entry = {
- .name = "show-hooks",
- .alias = NULL,
-
- .args = { "gt:", 0, 1 },
- .usage = "[-g] " CMD_TARGET_SESSION_USAGE,
-
- .target = { 't', CMD_FIND_SESSION, 0 },
-
- .flags = CMD_AFTERHOOK,
- .exec = cmd_set_hook_exec
-};
-
-static enum cmd_retval
-cmd_set_hook_exec(struct cmd *self, struct cmdq_item *item)
-{
- struct args *args = self->args;
- struct cmd_list *cmdlist;
- struct hooks *hooks;
- struct hook *hook;
- char *cause, *tmp;
- const char *name, *cmd, *target;
-
- if (args_has(args, 'g'))
- hooks = global_hooks;
- else {
- if (item->target.s == NULL) {
- target = args_get(args, 't');
- if (target != NULL)
- cmdq_error(item, "no such session: %s", target);
- else
- cmdq_error(item, "no current session");
- return (CMD_RETURN_ERROR);
- }
- hooks = item->target.s->hooks;
- }
-
- if (self->entry == &cmd_show_hooks_entry) {
- hook = hooks_first(hooks);
- while (hook != NULL) {
- tmp = cmd_list_print(hook->cmdlist);
- cmdq_print(item, "%s -> %s", hook->name, tmp);
- free(tmp);
-
- hook = hooks_next(hook);
- }
- return (CMD_RETURN_NORMAL);
- }
-
- name = args->argv[0];
- if (*name == '\0') {
- cmdq_error(item, "invalid hook name");
- return (CMD_RETURN_ERROR);
- }
- if (args->argc < 2)
- cmd = NULL;
- else
- cmd = args->argv[1];
-
- if (cmd != NULL && (args_has(args, 'R') || args_has(args, 'u'))) {
- cmdq_error(item, "no command allowed");
- return (CMD_RETURN_ERROR);
- }
- if (args_has(args, 'R')) {
- notify_hook(item, name);
- return (CMD_RETURN_NORMAL);
- }
- if (args_has(args, 'u')) {
- hooks_remove(hooks, name);
- return (CMD_RETURN_NORMAL);
- }
-
- if (cmd == NULL) {
- cmdq_error(item, "no command given");
- return (CMD_RETURN_ERROR);
- }
- cmdlist = cmd_string_parse(cmd, NULL, 0, &cause);
- if (cmdlist == NULL) {
- if (cause != NULL) {
- cmdq_error(item, "%s", cause);
- free(cause);
- }
- return (CMD_RETURN_ERROR);
- }
- hooks_add(hooks, name, cmdlist);
- cmd_list_free(cmdlist);
-
- return (CMD_RETURN_NORMAL);
-}
diff --git a/cmd-set-option.c b/cmd-set-option.c
index 96428d73..7be561f2 100644
--- a/cmd-set-option.c
+++ b/cmd-set-option.c
@@ -65,6 +65,19 @@ const struct cmd_entry cmd_set_window_option_entry = {
.exec = cmd_set_option_exec
};
+const struct cmd_entry cmd_set_hook_entry = {
+ .name = "set-hook",
+ .alias = NULL,
+
+ .args = { "agRt:u", 1, 2 },
+ .usage = "[-agRu] " CMD_TARGET_SESSION_USAGE " hook [command]",
+
+ .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
+
+ .flags = CMD_AFTERHOOK,
+ .exec = cmd_set_option_exec
+};
+
static enum cmd_retval
cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -87,6 +100,11 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
c = cmd_find_client(item, NULL, 1);
argument = format_single(item, args->argv[0], c, s, wl, NULL);
+ if (self->entry == &cmd_set_hook_entry && args_has(args, 'R')) {
+ notify_hook(item, argument);
+ return (CMD_RETURN_NORMAL);
+ }
+
/* Parse option name and index. */
name = options_match(argument, &idx, &ambiguous);
if (name == NULL) {
@@ -200,8 +218,11 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
options_default(oo, options_table_entry(o));
else
options_remove(o);
- } else
- options_array_set(o, idx, NULL, 0);
+ } else if (options_array_set(o, idx, NULL, 0, &cause) != 0) {
+ cmdq_error(item, "%s", cause);
+ free(cause);
+ goto fail;
+ }
} else if (*name == '@') {
if (value == NULL) {
cmdq_error(item, "empty value");
@@ -222,9 +243,15 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
if (idx == -1) {
if (!append)
options_array_clear(o);
- options_array_assign(o, value);
- } else if (options_array_set(o, idx, value, append) != 0) {
- cmdq_error(item, "invalid index: %s", argument);
+ if (options_array_assign(o, value, &cause) != 0) {
+ cmdq_error(item, "%s", cause);
+ free(cause);
+ goto fail;
+ }
+ } else if (options_array_set(o, idx, value, append,
+ &cause) != 0) {
+ cmdq_error(item, "%s", cause);
+ free(cause);
goto fail;
}
}
@@ -366,7 +393,7 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo,
return (-1);
}
return (0);
- case OPTIONS_TABLE_ARRAY:
+ case OPTIONS_TABLE_COMMAND:
break;
}
return (-1);
diff --git a/cmd-show-options.c b/cmd-show-options.c
index 5d480664..e3cfbc54 100644
--- a/cmd-show-options.c
+++ b/cmd-show-options.c
@@ -29,8 +29,8 @@
static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *);
-static enum cmd_retval cmd_show_options_one(struct cmd *, struct cmdq_item *,
- struct options *);
+static void cmd_show_options_print(struct cmd *, struct cmdq_item *,
+ struct options_entry *, int);
static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *,
struct options *);
@@ -38,8 +38,8 @@ const struct cmd_entry cmd_show_options_entry = {
.name = "show-options",
.alias = "show",
- .args = { "gqst:vw", 0, 1 },
- .usage = "[-gqsvw] [-t target-session|target-window] [option]",
+ .args = { "gHqst:vw", 0, 1 },
+ .usage = "[-gHqsvw] [-t target-session|target-window] [option]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL },
@@ -60,28 +60,113 @@ const struct cmd_entry cmd_show_window_options_entry = {
.exec = cmd_show_options_exec
};
+const struct cmd_entry cmd_show_hooks_entry = {
+ .name = "show-hooks",
+ .alias = NULL,
+
+ .args = { "gt:", 0, 1 },
+ .usage = "[-g] " CMD_TARGET_SESSION_USAGE,
+
+ .target = { 't', CMD_FIND_SESSION, 0 },
+
+ .flags = CMD_AFTERHOOK,
+ .exec = cmd_show_options_exec
+};
+
static enum cmd_retval
cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_find_state *fs = &item->target;
+ struct client *c = cmd_find_client(item, NULL, 1);
+ struct session *s = item->target.s;
+ struct winlink *wl = item->target.wl;
struct options *oo;
enum options_table_scope scope;
- char *cause;
- int window;
+ char *argument, *name = NULL, *cause;
+ const char *target;
+ int window, idx, ambiguous;
+ struct options_entry *o;
window = (self->entry == &cmd_show_window_options_entry);
- scope = options_scope_from_flags(args, window, fs, &oo, &cause);
+ if (args->argc == 0) {
+ scope = options_scope_from_flags(args, window, fs, &oo, &cause);
+ return (cmd_show_options_all(self, item, oo));
+ }
+ argument = format_single(item, args->argv[0], c, s, wl, NULL);
+
+ name = options_match(argument, &idx, &ambiguous);
+ if (name == NULL) {
+ if (args_has(args, 'q'))
+ goto fail;
+ if (ambiguous)
+ cmdq_error(item, "ambiguous option: %s", argument);
+ else
+ cmdq_error(item, "invalid option: %s", argument);
+ goto fail;
+ }
+ if (*name == '@')
+ scope = options_scope_from_flags(args, window, fs, &oo, &cause);
+ else {
+ if (options_get_only(global_options, name) != NULL)
+ scope = OPTIONS_TABLE_SERVER;
+ else if (options_get_only(global_s_options, name) != NULL)
+ scope = OPTIONS_TABLE_SESSION;
+ else if (options_get_only(global_w_options, name) != NULL)
+ scope = OPTIONS_TABLE_WINDOW;
+ else {
+ scope = OPTIONS_TABLE_NONE;
+ xasprintf(&cause, "unknown option: %s", argument);
+ }
+ if (scope == OPTIONS_TABLE_SERVER)
+ oo = global_options;
+ else if (scope == OPTIONS_TABLE_SESSION) {
+ if (args_has(self->args, 'g'))
+ oo = global_s_options;
+ else if (s == NULL) {
+ target = args_get(args, 't');
+ if (target != NULL) {
+ cmdq_error(item, "no such session: %s",
+ target);
+ } else
+ cmdq_error(item, "no current session");
+ goto fail;
+ } else
+ oo = s->options;
+ } else if (scope == OPTIONS_TABLE_WINDOW) {
+ if (args_has(self->args, 'g'))
+ oo = global_w_options;
+ else if (wl == NULL) {
+ target = args_get(args, 't');
+ if (target != NULL) {
+ cmdq_error(item, "no such window: %s",
+ target);
+ } else
+ cmdq_error(item, "no current window");
+ goto fail;
+ } else
+ oo = wl->window->options;
+ }
+ }
if (scope == OPTIONS_TABLE_NONE) {
+ if (args_has(args, 'q'))
+ goto fail;
cmdq_error(item, "%s", cause);
free(cause);
- return (CMD_RETURN_ERROR);
+ goto fail;
}
+ o = options_get_only(oo, name);
+ if (o != NULL)
+ cmd_show_options_print(self, item, o, idx);
- if (args->argc == 0)
- return (cmd_show_options_all(self, item, oo));
- else
- return (cmd_show_options_one(self, item, oo));
+ free(name);
+ free(argument);
+ return (CMD_RETURN_NORMAL);
+
+fail:
+ free(name);
+ free(argument);
+ return (CMD_RETURN_ERROR);
}
static void
@@ -89,15 +174,20 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
struct options_entry *o, int idx)
{
struct options_array_item *a;
- const char *name, *value;
- char *tmp, *escaped;
+ const char *name = options_name(o);
+ char *value, *tmp = NULL, *escaped;
if (idx != -1) {
- xasprintf(&tmp, "%s[%d]", options_name(o), idx);
+ xasprintf(&tmp, "%s[%d]", name, idx);
name = tmp;
} else {
if (options_isarray(o)) {
a = options_array_first(o);
+ if (a == NULL) {
+ if (!args_has(self->args, 'v'))
+ cmdq_print(item, "%s", name);
+ return;
+ }
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx);
@@ -105,8 +195,6 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
}
return;
}
- tmp = NULL;
- name = options_name(o);
}
value = options_tostring(o, idx, 0);
@@ -118,64 +206,39 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
free(escaped);
} else
cmdq_print(item, "%s %s", name, value);
+ free(value);
free(tmp);
}
static enum cmd_retval
-cmd_show_options_one(struct cmd *self, struct cmdq_item *item,
- struct options *oo)
-{
- struct args *args = self->args;
- struct client *c = cmd_find_client(item, NULL, 1);
- struct session *s = item->target.s;
- struct winlink *wl = item->target.wl;
- struct options_entry *o;
- int idx, ambiguous;
- char *name;
-
- name = format_single(item, args->argv[0], c, s, wl, NULL);
- o = options_match_get(oo, name, &idx, 1, &ambiguous);
- if (o == NULL) {
- if (args_has(args, 'q')) {
- free(name);
- return (CMD_RETURN_NORMAL);
- }
- if (ambiguous) {
- cmdq_error(item, "ambiguous option: %s", name);
- free(name);
- return (CMD_RETURN_ERROR);
- }
- if (*name != '@' &&
- options_match_get(oo, name, &idx, 0, &ambiguous) != NULL) {
- free(name);
- return (CMD_RETURN_NORMAL);
- }
- cmdq_error(item, "unknown option: %s", name);
- free(name);
- return (CMD_RETURN_ERROR);
- }
- cmd_show_options_print(self, item, o, idx);
- free(name);
- return (CMD_RETURN_NORMAL);
-}
-
-static enum cmd_retval
cmd_show_options_all(struct cmd *self, struct cmdq_item *item,
struct options *oo)
{
struct options_entry *o;
- const struct options_table_entry *oe;
struct options_array_item *a;
u_int idx;
+ const struct options_table_entry *oe;
o = options_first(oo);
while (o != NULL) {
oe = options_table_entry(o);
+ if ((self->entry != &cmd_show_hooks_entry &&
+ !args_has(self->args, 'H') &&
+ oe != NULL &&
+ (oe->flags & OPTIONS_TABLE_IS_HOOK)) ||
+ (self->entry == &cmd_show_hooks_entry &&
+ (oe == NULL ||
+ (~oe->flags & OPTIONS_TABLE_IS_HOOK)))) {
+ o = options_next(o);
+ continue;
+ }
if (!options_isarray(o))
cmd_show_options_print(self, item, o, -1);
- else {
- a = options_array_first(o);
+ else if ((a = options_array_first(o)) == NULL) {
+ if (!args_has(self->args, 'v'))
+ cmdq_print(item, "%s", options_name(o));
+ } else {
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx);
diff --git a/cmd-split-window.c b/cmd-split-window.c
index f5dde751..b06c48f2 100644
--- a/cmd-split-window.c
+++ b/cmd-split-window.c
@@ -39,8 +39,8 @@ const struct cmd_entry cmd_split_window_entry = {
.name = "split-window",
.alias = "splitw",
- .args = { "bc:dfF:l:hp:Pt:v", 0, -1 },
- .usage = "[-bdfhvP] [-c start-directory] [-F format] "
+ .args = { "bc:de:fF:l:hp:Pt:v", 0, -1 },
+ .usage = "[-bdefhvP] [-c start-directory] [-e environment] [-F format] "
"[-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -52,111 +52,95 @@ const struct cmd_entry cmd_split_window_entry = {
static enum cmd_retval
cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
{
- struct cmd_find_state *current = &item->shared->current;
struct args *args = self->args;
+ struct cmd_find_state *current = &item->shared->current;
+ struct spawn_context sc;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
- struct window *w = wl->window;
- struct window_pane *wp = item->target.wp, *new_wp = NULL;
- struct environ *env;
- const char *cmd, *path, *shell, *template, *tmp;
- char **argv, *cause, *new_cause, *cp, *cwd;
- u_int hlimit;
- int argc, size, percentage, before;
+ struct window_pane *wp = item->target.wp, *new_wp;
enum layout_type type;
struct layout_cell *lc;
- struct environ_entry *envent;
- struct cmd_find_state fs;
-
- server_unzoom_window(w);
-
- if (args->argc == 0) {
- cmd = options_get_string(s->options, "default-command");
- if (cmd != NULL && *cmd != '\0') {
- argc = 1;
- argv = (char **)&cmd;
- } else {
- argc = 0;
- argv = NULL;
- }
- } else {
- argc = args->argc;
- argv = args->argv;
- }
-
- if ((tmp = args_get(args, 'c')) != NULL)
- cwd = format_single(item, tmp, c, s, NULL, NULL);
- else
- cwd = xstrdup(server_client_get_cwd(item->client, s));
+ struct cmd_find_state fs;
+ int size, percentage, flags;
+ const char *template, *add;
+ char *cause, *cp;
+ struct args_value *value;
- type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT;
- before = args_has(args, 'b');
-
- size = -1;
+ else
+ type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'l')) {
size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
if (cause != NULL) {
- xasprintf(&new_cause, "size %s", cause);
+ cmdq_error(item, "create pane failed: -l %s", cause);
free(cause);
- cause = new_cause;
- goto error;
+ return (CMD_RETURN_ERROR);
}
} else if (args_has(args, 'p')) {
percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause);
if (cause != NULL) {
- xasprintf(&new_cause, "percentage %s", cause);
+ cmdq_error(item, "create pane failed: -p %s", cause);
free(cause);
- cause = new_cause;
- goto error;
+ return (CMD_RETURN_ERROR);
}
if (type == LAYOUT_TOPBOTTOM)
size = (wp->sy * percentage) / 100;
else
size = (wp->sx * percentage) / 100;
- }
- hlimit = options_get_number(s->options, "history-limit");
+ } else
+ size = -1;
+
+ server_unzoom_window(wp->window);
- shell = options_get_string(s->options, "default-shell");
- if (*shell == '\0' || areshell(shell))
- shell = _PATH_BSHELL;
+ flags = 0;
+ if (args_has(args, 'b'))
+ flags |= SPAWN_BEFORE;
+ if (args_has(args, 'f'))
+ flags |= SPAWN_FULLSIZE;
- lc = layout_split_pane(wp, type, size, before, args_has(args, 'f'));
+ lc = layout_split_pane(wp, type, size, flags);
if (lc == NULL) {
- cause = xstrdup("pane too small");
- goto error;
+ cmdq_error(item, "no space for new pane");
+ return (CMD_RETURN_ERROR);
}
- new_wp = window_add_pane(w, wp, before, args_has(args, 'f'), hlimit);
- layout_make_leaf(lc, new_wp);
- path = NULL;
- if (item->client != NULL && item->client->session == NULL)
- envent = environ_find(item->client->environ, "PATH");
- else
- envent = environ_find(s->environ, "PATH");
- if (envent != NULL)
- path = envent->value;
-
- env = environ_for_session(s, 0);
- if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, env,
- s->tio, &cause) != 0) {
- environ_free(env);
- goto error;
+ memset(&sc, 0, sizeof sc);
+ sc.item = item;
+ sc.s = s;
+ sc.wl = wl;
+
+ sc.wp0 = wp;
+ sc.lc = lc;
+
+ sc.name = NULL;
+ sc.argc = args->argc;
+ sc.argv = args->argv;
+ sc.environ = environ_create();
+
+ add = args_first_value(args, 'e', &value);
+ while (add != NULL) {
+ environ_put(sc.environ, add);
+ add = args_next_value(&value);
}
- environ_free(env);
- layout_fix_panes(w);
- server_redraw_window(w);
+ sc.idx = -1;
+ sc.cwd = args_get(args, 'c');
- if (!args_has(args, 'd')) {
- window_set_active_pane(w, new_wp);
- session_select(s, wl->idx);
- cmd_find_from_session(current, s, 0);
- server_redraw_session(s);
- } else
- server_status_session(s);
+ sc.flags = flags;
+ if (args_has(args, 'd'))
+ sc.flags |= SPAWN_DETACHED;
+
+ if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
+ cmdq_error(item, "create pane failed: %s", cause);
+ free(cause);
+ return (CMD_RETURN_ERROR);
+ }
+ if (!args_has(args, 'd'))
+ cmd_find_from_winlink_pane(current, wl, new_wp, 0);
+ server_redraw_window(wp->window);
+ server_status_session(s);
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
@@ -165,22 +149,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
cmdq_print(item, "%s", cp);
free(cp);
}
- notify_window("window-layout-changed", w);
cmd_find_from_winlink_pane(&fs, wl, new_wp, 0);
- hooks_insert(s->hooks, item, &fs, "after-split-window");
+ cmdq_insert_hook(s, item, &fs, "after-split-window");
- free(cwd);
+ environ_free(sc.environ);
return (CMD_RETURN_NORMAL);
-
-error:
- if (new_wp != NULL) {
- layout_close_pane(new_wp);
- window_remove_pane(w, new_wp);
- }
- cmdq_error(item, "create pane failed: %s", cause);
- free(cause);
-
- free(cwd);
- return (CMD_RETURN_ERROR);
}
diff --git a/cmd-string.c b/cmd-string.c
index 440a0231..058f997c 100644
--- a/cmd-string.c
+++ b/cmd-string.c
@@ -174,7 +174,10 @@ cmd_string_parse(const char *s, const char *file, u_int line, char **cause)
int argc;
char **argv;
- *cause = NULL;
+ if (cause != NULL)
+ *cause = NULL;
+ log_debug ("%s: %s", __func__, s);
+
if (cmd_string_split(s, &argc, &argv) != 0) {
xasprintf(cause, "invalid or unknown command: %s", s);
return (NULL);
diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c
index 1de272c4..2ad05561 100644
--- a/cmd-swap-pane.c
+++ b/cmd-swap-pane.c
@@ -101,17 +101,17 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
if (!args_has(self->args, 'd')) {
if (src_w != dst_w) {
- window_set_active_pane(src_w, dst_wp);
- window_set_active_pane(dst_w, src_wp);
+ window_set_active_pane(src_w, dst_wp, 1);
+ window_set_active_pane(dst_w, src_wp, 1);
} else {
tmp_wp = dst_wp;
- window_set_active_pane(src_w, tmp_wp);
+ window_set_active_pane(src_w, tmp_wp, 1);
}
} else {
if (src_w->active == src_wp)
- window_set_active_pane(src_w, dst_wp);
+ window_set_active_pane(src_w, dst_wp, 1);
if (dst_w->active == dst_wp)
- window_set_active_pane(dst_w, src_wp);
+ window_set_active_pane(dst_w, src_wp, 1);
}
if (src_w != dst_w) {
if (src_w->last == src_wp)
diff --git a/cmd-switch-client.c b/cmd-switch-client.c
index 3e19346e..8f51d0fe 100644
--- a/cmd-switch-client.c
+++ b/cmd-switch-client.c
@@ -61,7 +61,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL)
return (CMD_RETURN_ERROR);
- if (tflag != NULL && tflag[strcspn(tflag, ":.")] != '\0') {
+ if (tflag != NULL && tflag[strcspn(tflag, ":.%")] != '\0') {
type = CMD_FIND_PANE;
flags = 0;
} else {
@@ -116,7 +116,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
server_unzoom_window(wl->window);
if (wp != NULL) {
window_redraw_active_switch(wp->window, wp);
- window_set_active_pane(wp->window, wp);
+ window_set_active_pane(wp->window, wp, 1);
}
session_set_current(s, wl);
cmd_find_from_session(&item->shared->current, s, 0);
diff --git a/cmd.c b/cmd.c
index 877cd10c..8d61a572 100644
--- a/cmd.c
+++ b/cmd.c
@@ -320,10 +320,11 @@ cmd_try_alias(int *argc, char ***argv)
{
struct options_entry *o;
struct options_array_item *a;
+ union options_value *ov;
int old_argc = *argc, new_argc, i;
char **old_argv = *argv, **new_argv;
size_t wanted;
- const char *s, *cp = NULL;
+ const char *cp = NULL;
o = options_get_only(global_options, "command-alias");
if (o == NULL)
@@ -332,14 +333,12 @@ cmd_try_alias(int *argc, char ***argv)
a = options_array_first(o);
while (a != NULL) {
- s = options_array_item_value(a);
- if (s != NULL) {
- cp = strchr(s, '=');
- if (cp != NULL &&
- (size_t)(cp - s) == wanted &&
- strncmp(old_argv[0], s, wanted) == 0)
- break;
- }
+ ov = options_array_item_value(a);
+ cp = strchr(ov->string, '=');
+ if (cp != NULL &&
+ (size_t)(cp - ov->string) == wanted &&
+ strncmp(old_argv[0], ov->string, wanted) == 0)
+ break;
a = options_array_next(a);
}
if (a == NULL)
diff --git a/compat.h b/compat.h
index b83e6189..402ffb90 100644
--- a/compat.h
+++ b/compat.h
@@ -67,6 +67,7 @@ void warnx(const char *, ...);
#define _PATH_DEVNULL "/dev/null"
#define _PATH_TTY "/dev/tty"
#define _PATH_DEV "/dev/"
+#define _PATH_DEFPATH "/usr/bin:/bin"
#endif
#ifndef __OpenBSD__
diff --git a/configure.ac b/configure.ac
index c55f55c5..1e7578ee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
# configure.ac
-AC_INIT([tmux], 2.9a)
+AC_INIT([tmux], next-3.0)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
diff --git a/environ.c b/environ.c
index 2764e027..25968f7b 100644
--- a/environ.c
+++ b/environ.c
@@ -177,20 +177,16 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst)
struct environ_entry *envent;
struct options_entry *o;
struct options_array_item *a;
- const char *value;
+ union options_value *ov;
o = options_get(oo, "update-environment");
if (o == NULL)
return;
a = options_array_first(o);
while (a != NULL) {
- value = options_array_item_value(a);
- if (value == NULL) {
- a = options_array_next(a);
- continue;
- }
- if ((envent = environ_find(src, value)) == NULL)
- environ_clear(dst, value);
+ ov = options_array_item_value(a);
+ if ((envent = environ_find(src, ov->string)) == NULL)
+ environ_clear(dst, ov->string);
else
environ_set(dst, envent->name, "%s", envent->value);
a = options_array_next(a);
diff --git a/format.c b/format.c
index 485a3bc7..db642f63 100644
--- a/format.c
+++ b/format.c
@@ -920,9 +920,8 @@ format_find(struct format_tree *ft, const char *key, int modifiers)
struct environ_entry *envent;
static char s[64];
struct options_entry *o;
- const char *found;
int idx;
- char *copy, *saved;
+ char *found, *saved;
if (~modifiers & FORMAT_TIMESTRING) {
o = options_parse_get(global_options, key, &idx, 0);
@@ -949,12 +948,11 @@ format_find(struct format_tree *ft, const char *key, int modifiers)
return (NULL);
ctime_r(&fe->t, s);
s[strcspn(s, "\n")] = '\0';
- found = s;
+ found = xstrdup(s);
goto found;
}
if (fe->t != 0) {
- xsnprintf(s, sizeof s, "%lld", (long long)fe->t);
- found = s;
+ xasprintf(&found, "%lld", (long long)fe->t);
goto found;
}
if (fe->value == NULL && fe->cb != NULL) {
@@ -962,7 +960,7 @@ format_find(struct format_tree *ft, const char *key, int modifiers)
if (fe->value == NULL)
fe->value = xstrdup("");
}
- found = fe->value;
+ found = xstrdup(fe->value);
goto found;
}
@@ -973,7 +971,7 @@ format_find(struct format_tree *ft, const char *key, int modifiers)
if (envent == NULL)
envent = environ_find(global_environ, key);
if (envent != NULL) {
- found = envent->value;
+ found = xstrdup(envent->value);
goto found;
}
}
@@ -983,23 +981,22 @@ format_find(struct format_tree *ft, const char *key, int modifiers)
found:
if (found == NULL)
return (NULL);
- copy = xstrdup(found);
if (modifiers & FORMAT_BASENAME) {
- saved = copy;
- copy = xstrdup(basename(saved));
+ saved = found;
+ found = xstrdup(basename(saved));
free(saved);
}
if (modifiers & FORMAT_DIRNAME) {
- saved = copy;
- copy = xstrdup(dirname(saved));
+ saved = found;
+ found = xstrdup(dirname(saved));
free(saved);
}
if (modifiers & FORMAT_QUOTE) {
- saved = copy;
- copy = xstrdup(format_quote(saved));
+ saved = found;
+ found = xstrdup(format_quote(saved));
free(saved);
}
- return (copy);
+ return (found);
}
/* Skip until end. */
@@ -1822,6 +1819,23 @@ void
format_defaults(struct format_tree *ft, struct client *c, struct session *s,
struct winlink *wl, struct window_pane *wp)
{
+ if (c != NULL)
+ log_debug("%s: c=%s", __func__, c->name);
+ else
+ log_debug("%s: s=none", __func__);
+ if (s != NULL)
+ log_debug("%s: s=$%u", __func__, s->id);
+ else
+ log_debug("%s: s=none", __func__);
+ if (wl != NULL)
+ log_debug("%s: wl=%u w=@%u", __func__, wl->idx, wl->window->id);
+ else
+ log_debug("%s: wl=none", __func__);
+ if (wp != NULL)
+ log_debug("%s: wp=%%%u", __func__, wp->id);
+ else
+ log_debug("%s: wp=none", __func__);
+
if (c != NULL && s != NULL && c->session != s)
log_debug("%s: session does not match", __func__);
diff --git a/hooks.c b/hooks.c
deleted file mode 100644
index 361f3522..00000000
--- a/hooks.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/* $OpenBSD$ */
-
-/*
- * Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
- * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "tmux.h"
-
-struct hooks {
- RB_HEAD(hooks_tree, hook) tree;
- struct hooks *parent;
-};
-
-static int hooks_cmp(struct hook *, struct hook *);
-RB_GENERATE_STATIC(hooks_tree, hook, entry, hooks_cmp);
-
-static struct hook *hooks_find1(struct hooks *, const char *);
-static void hooks_free1(struct hooks *, struct hook *);
-
-static int
-hooks_cmp(struct hook *hook1, struct hook *hook2)
-{
- return (strcmp(hook1->name, hook2->name));
-}
-
-struct hooks *
-hooks_get(struct session *s)
-{
- if (s != NULL)
- return (s->hooks);
- return (global_hooks);
-}
-
-struct hooks *
-hooks_create(struct hooks *parent)
-{
- struct hooks *hooks;
-
- hooks = xcalloc(1, sizeof *hooks);
- RB_INIT(&hooks->tree);
- hooks->parent = parent;
- return (hooks);
-}
-
-static void
-hooks_free1(struct hooks *hooks, struct hook *hook)
-{
- RB_REMOVE(hooks_tree, &hooks->tree, hook);
- cmd_list_free(hook->cmdlist);
- free((char *)hook->name);
- free(hook);
-}
-
-void
-hooks_free(struct hooks *hooks)
-{
- struct hook *hook, *hook1;
-
- RB_FOREACH_SAFE(hook, hooks_tree, &hooks->tree, hook1)
- hooks_free1(hooks, hook);
- free(hooks);
-}
-
-struct hook *
-hooks_first(struct hooks *hooks)
-{
- return (RB_MIN(hooks_tree, &hooks->tree));
-}
-
-struct hook *
-hooks_next(struct hook *hook)
-{
- return (RB_NEXT(hooks_tree, &hooks->tree, hook));
-}
-
-void
-hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist)
-{
- struct hook *hook;
-
- if ((hook = hooks_find1(hooks, name)) != NULL)
- hooks_free1(hooks, hook);
-
- hook = xcalloc(1, sizeof *hook);
- hook->name = xstrdup(name);
- hook->cmdlist = cmdlist;
- hook->cmdlist->references++;
- RB_INSERT(hooks_tree, &hooks->tree, hook);
-}
-
-void
-hooks_remove(struct hooks *hooks, const char *name)
-{
- struct hook *hook;
-
- if ((hook = hooks_find1(hooks, name)) != NULL)
- hooks_free1(hooks, hook);
-}
-
-static struct hook *
-hooks_find1(struct hooks *hooks, const char *name)
-{
- struct hook hook;
-
- hook.name = name;
- return (RB_FIND(hooks_tree, &hooks->tree, &hook));
-}
-
-struct hook *
-hooks_find(struct hooks *hooks, const char *name)
-{
- struct hook hook0, *hook;
-
- hook0.name = name;
- hook = RB_FIND(hooks_tree, &hooks->tree, &hook0);
- while (hook == NULL) {
- hooks = hooks->parent;
- if (hooks == NULL)
- break;
- hook = RB_FIND(hooks_tree, &hooks->tree, &hook0);
- }
- return (hook);
-}
-
-void
-hooks_insert(struct hooks *hooks, struct cmdq_item *item,
- struct cmd_find_state *fs, const char *fmt, ...)
-{
- struct hook *hook;
- va_list ap;
- char *name;
- struct cmdq_item *new_item;
-
- if (item->flags & CMDQ_NOHOOKS)
- return;
-
- va_start(ap, fmt);
- xvasprintf(&name, fmt, ap);
- va_end(ap);
-
- hook = hooks_find(hooks, name);
- if (hook == NULL) {
- free(name);
- return;
- }
- log_debug("running hook %s (parent %p)", name, item);
-
- new_item = cmdq_get_command(hook->cmdlist, fs, NULL, CMDQ_NOHOOKS);
- cmdq_format(new_item, "hook", "%s", name);
- if (item != NULL)
- cmdq_insert_after(item, new_item);
- else
- cmdq_append(NULL, new_item);
-
- free(name);
-}
diff --git a/input.c b/input.c
index 96793674..07341403 100644
--- a/input.c
+++ b/input.c
@@ -2432,7 +2432,7 @@ input_osc_52(struct input_ctx *ictx, const char *p)
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
- paste_add(out, outlen);
+ paste_add(NULL, out, outlen);
}
/* Handle the OSC 104 sequence for unsetting (multiple) palette entries. */
diff --git a/key-bindings.c b/key-bindings.c
index fbc54fb8..5af8f3ed 100644
--- a/key-bindings.c
+++ b/key-bindings.c
@@ -335,7 +335,9 @@ key_bindings_init(void)
"bind -Tcopy-mode M-> send -X history-bottom",
"bind -Tcopy-mode M-R send -X top-line",
"bind -Tcopy-mode M-b send -X previous-word",
+ "bind -Tcopy-mode C-M-b send -X previous-matching-bracket",
"bind -Tcopy-mode M-f send -X next-word-end",
+ "bind -Tcopy-mode C-M-f send -X next-matching-bracket",
"bind -Tcopy-mode M-m send -X back-to-indentation",
"bind -Tcopy-mode M-r send -X middle-line",
"bind -Tcopy-mode M-v send -X page-up",
@@ -408,6 +410,7 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi w send -X next-word",
"bind -Tcopy-mode-vi { send -X previous-paragraph",
"bind -Tcopy-mode-vi } send -X next-paragraph",
+ "bind -Tcopy-mode-vi % send -X next-matching-bracket",
"bind -Tcopy-mode-vi MouseDown1Pane select-pane",
"bind -Tcopy-mode-vi MouseDrag1Pane select-pane\\; send -X begin-selection",
"bind -Tcopy-mode-vi MouseDragEnd1Pane send -X copy-selection-and-cancel",
diff --git a/layout.c b/layout.c
index fa6f0613..86d307ef 100644
--- a/layout.c
+++ b/layout.c
@@ -414,9 +414,9 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
lcother = TAILQ_NEXT(lc, entry);
else
lcother = TAILQ_PREV(lc, layout_cells, entry);
- if (lcparent->type == LAYOUT_LEFTRIGHT)
+ if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT)
layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1);
- else
+ else if (lcother != NULL)
layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1);
/* Remove this from the parent's list. */
@@ -831,12 +831,12 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc)
*/
struct layout_cell *
layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
- int insert_before, int full_size)
+ int flags)
{
struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
u_int sx, sy, xoff, yoff, size1, size2, minimum;
u_int new_size, saved_size, resize_first = 0;
- int status;
+ int full_size = (flags & SPAWN_FULLSIZE), status;
/*
* If full_size is specified, add a new cell at the top of the window
@@ -881,7 +881,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
saved_size = sy;
if (size < 0)
size2 = ((saved_size + 1) / 2) - 1;
- else if (insert_before)
+ else if (flags & SPAWN_BEFORE)
size2 = saved_size - size - 1;
else
size2 = size;
@@ -892,7 +892,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
size1 = saved_size - 1 - size2;
/* Which size are we using? */
- if (insert_before)
+ if (flags & SPAWN_BEFORE)
new_size = size2;
else
new_size = size1;
@@ -908,7 +908,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
*/
lcparent = lc->parent;
lcnew = layout_create_cell(lcparent);
- if (insert_before)
+ if (flags & SPAWN_BEFORE)
TAILQ_INSERT_BEFORE(lc, lcnew, entry);
else
TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry);
@@ -937,7 +937,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
layout_set_size(lcnew, size, sy, 0, 0);
else if (lc->type == LAYOUT_TOPBOTTOM)
layout_set_size(lcnew, sx, size, 0, 0);
- if (insert_before)
+ if (flags & SPAWN_BEFORE)
TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry);
else
TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
@@ -961,12 +961,12 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
/* Create the new child cell. */
lcnew = layout_create_cell(lcparent);
- if (insert_before)
+ if (flags & SPAWN_BEFORE)
TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry);
else
TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
}
- if (insert_before) {
+ if (flags & SPAWN_BEFORE) {
lc1 = lcnew;
lc2 = lc;
} else {
diff --git a/notify.c b/notify.c
index adef3d4e..163aa1a9 100644
--- a/notify.c
+++ b/notify.c
@@ -35,13 +35,34 @@ struct notify_entry {
};
static void
-notify_hook1(struct cmdq_item *item, struct notify_entry *ne)
+notify_hook_formats(struct cmdq_item *item, struct session *s, struct window *w,
+ int pane)
{
- struct cmd_find_state fs;
- struct hook *hook;
- struct cmdq_item *new_item;
- struct session *s = ne->session;
- struct window *w = ne->window;
+ if (s != NULL) {
+ cmdq_format(item, "hook_session", "$%u", s->id);
+ cmdq_format(item, "hook_session_name", "%s", s->name);
+ }
+ if (w != NULL) {
+ cmdq_format(item, "hook_window", "@%u", w->id);
+ cmdq_format(item, "hook_window_name", "%s", w->name);
+ }
+ if (pane != -1)
+ cmdq_format(item, "hook_pane", "%%%d", pane);
+}
+
+static void
+notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne)
+{
+ struct cmd_find_state fs;
+ struct options *oo;
+ struct cmdq_item *new_item;
+ struct session *s = ne->session;
+ struct window *w = ne->window;
+ struct options_entry *o;
+ struct options_array_item *a;
+ struct cmd_list *cmdlist;
+
+ log_debug("%s: %s", __func__, ne->name);
cmd_find_clear_state(&fs, 0);
if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs))
@@ -49,26 +70,31 @@ notify_hook1(struct cmdq_item *item, struct notify_entry *ne)
else
cmd_find_copy_state(&fs, &ne->fs);
- hook = hooks_find(hooks_get(fs.s), ne->name);
- if (hook == NULL)
+ if (fs.s == NULL)
+ oo = global_s_options;
+ else
+ oo = fs.s->options;
+ o = options_get(oo, ne->name);
+ if (o == NULL)
return;
- log_debug("notify hook %s", ne->name);
- new_item = cmdq_get_command(hook->cmdlist, &fs, NULL, CMDQ_NOHOOKS);
- cmdq_format(new_item, "hook", "%s", ne->name);
+ a = options_array_first(o);
+ while (a != NULL) {
+ cmdlist = options_array_item_value(a)->cmdlist;
+ if (cmdlist == NULL) {
+ a = options_array_next(a);
+ continue;
+ }
- if (s != NULL) {
- cmdq_format(new_item, "hook_session", "$%u", s->id);
- cmdq_format(new_item, "hook_session_name", "%s", s->name);
- }
- if (w != NULL) {
- cmdq_format(new_item, "hook_window", "@%u", w->id);
- cmdq_format(new_item, "hook_window_name", "%s", w->name);
- }
- if (ne->pane != -1)
- cmdq_format(new_item, "hook_pane", "%%%d", ne->pane);
+ 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);
+ cmdq_insert_after(item, new_item);
+ item = new_item;
+
+ a = options_array_next(a);
+ }
}
static enum cmd_retval
@@ -101,7 +127,7 @@ notify_callback(struct cmdq_item *item, void *data)
if (strcmp(ne->name, "session-window-changed") == 0)
control_notify_session_window_changed(ne->session);
- notify_hook1(item, ne);
+ notify_insert_hook(item, ne);
if (ne->client != NULL)
server_client_unref(ne->client);
@@ -168,7 +194,7 @@ notify_hook(struct cmdq_item *item, const char *name)
ne.window = item->target.w;
ne.pane = item->target.wp->id;
- notify_hook1(item, &ne);
+ notify_insert_hook(item, &ne);
}
void
diff --git a/options-table.c b/options-table.c
index 40639aff..19cf39f4 100644
--- a/options-table.c
+++ b/options-table.c
@@ -129,8 +129,19 @@ static const char *options_table_status_format_default[] = {
OPTIONS_TABLE_STATUS_FORMAT1, OPTIONS_TABLE_STATUS_FORMAT2, NULL
};
+/* Helper for hook options. */
+#define OPTIONS_TABLE_HOOK(hook_name, default_value) \
+ { .name = hook_name, \
+ .type = OPTIONS_TABLE_COMMAND, \
+ .scope = OPTIONS_TABLE_SESSION, \
+ .flags = OPTIONS_TABLE_IS_ARRAY|OPTIONS_TABLE_IS_HOOK, \
+ .default_str = default_value, \
+ .separator = "" \
+ }
+
/* Top-level options. */
const struct options_table_entry options_table[] = {
+ /* Server options. */
{ .name = "buffer-limit",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SERVER,
@@ -140,8 +151,9 @@ const struct options_table_entry options_table[] = {
},
{ .name = "command-alias",
- .type = OPTIONS_TABLE_ARRAY,
+ .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
+ .flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "split-pane=split-window,"
"splitp=split-window,"
"server-info=show-messages -JT,"
@@ -205,8 +217,9 @@ const struct options_table_entry options_table[] = {
},
{ .name = "terminal-overrides",
- .type = OPTIONS_TABLE_ARRAY,
+ .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
+ .flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007"
":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007"
":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT",
@@ -214,12 +227,14 @@ const struct options_table_entry options_table[] = {
},
{ .name = "user-keys",
- .type = OPTIONS_TABLE_ARRAY,
+ .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
+ .flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "",
.separator = ","
},
+ /* Session options. */
{ .name = "activity-action",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
@@ -420,8 +435,9 @@ const struct options_table_entry options_table[] = {
},
{ .name = "status-format",
- .type = OPTIONS_TABLE_ARRAY,
+ .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
+ .flags = OPTIONS_TABLE_IS_ARRAY,
.default_arr = options_table_status_format_default,
},
@@ -503,8 +519,9 @@ const struct options_table_entry options_table[] = {
},
{ .name = "update-environment",
- .type = OPTIONS_TABLE_ARRAY,
+ .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
+ .flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "DISPLAY KRB5CCNAME SSH_ASKPASS SSH_AUTH_SOCK "
"SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY"
},
@@ -536,6 +553,7 @@ const struct options_table_entry options_table[] = {
.default_str = " -_@"
},
+ /* Window options. */
{ .name = "aggressive-resize",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
@@ -770,5 +788,66 @@ const struct options_table_entry options_table[] = {
.default_num = 1
},
+ /* Hook options. */
+ OPTIONS_TABLE_HOOK("after-bind-key", ""),
+ OPTIONS_TABLE_HOOK("after-capture-pane", ""),
+ OPTIONS_TABLE_HOOK("after-copy-mode", ""),
+ OPTIONS_TABLE_HOOK("after-display-message", ""),
+ OPTIONS_TABLE_HOOK("after-display-panes", ""),
+ OPTIONS_TABLE_HOOK("after-list-buffers", ""),
+ OPTIONS_TABLE_HOOK("after-list-clients", ""),
+ OPTIONS_TABLE_HOOK("after-list-keys", ""),
+ OPTIONS_TABLE_HOOK("after-list-panes", ""),
+ OPTIONS_TABLE_HOOK("after-list-sessions", ""),
+ OPTIONS_TABLE_HOOK("after-list-windows", ""),
+ OPTIONS_TABLE_HOOK("after-load-buffer", ""),
+ OPTIONS_TABLE_HOOK("after-lock-server", ""),
+ OPTIONS_TABLE_HOOK("after-new-session", ""),
+ OPTIONS_TABLE_HOOK("after-new-window", ""),
+ OPTIONS_TABLE_HOOK("after-paste-buffer", ""),
+ OPTIONS_TABLE_HOOK("after-pipe-pane", ""),
+ OPTIONS_TABLE_HOOK("after-queue", ""),
+ OPTIONS_TABLE_HOOK("after-refresh-client", ""),
+ OPTIONS_TABLE_HOOK("after-rename-session", ""),
+ OPTIONS_TABLE_HOOK("after-rename-window", ""),
+ OPTIONS_TABLE_HOOK("after-resize-pane", ""),
+ OPTIONS_TABLE_HOOK("after-resize-window", ""),
+ OPTIONS_TABLE_HOOK("after-save-buffer", ""),
+ OPTIONS_TABLE_HOOK("after-select-layout", ""),
+ OPTIONS_TABLE_HOOK("after-select-pane", ""),
+ OPTIONS_TABLE_HOOK("after-select-window", ""),
+ OPTIONS_TABLE_HOOK("after-send-keys", ""),
+ OPTIONS_TABLE_HOOK("after-set-buffer", ""),
+ OPTIONS_TABLE_HOOK("after-set-environment", ""),
+ OPTIONS_TABLE_HOOK("after-set-hook", ""),
+ OPTIONS_TABLE_HOOK("after-set-option", ""),
+ OPTIONS_TABLE_HOOK("after-show-environment", ""),
+ OPTIONS_TABLE_HOOK("after-show-messages", ""),
+ OPTIONS_TABLE_HOOK("after-show-options", ""),
+ OPTIONS_TABLE_HOOK("after-split-window", ""),
+ OPTIONS_TABLE_HOOK("after-unbind-key", ""),
+ OPTIONS_TABLE_HOOK("alert-activity", ""),
+ OPTIONS_TABLE_HOOK("alert-bell", ""),
+ OPTIONS_TABLE_HOOK("alert-silence", ""),
+ OPTIONS_TABLE_HOOK("client-attached", ""),
+ OPTIONS_TABLE_HOOK("client-detached", ""),
+ OPTIONS_TABLE_HOOK("client-resized", ""),
+ OPTIONS_TABLE_HOOK("client-session-changed", ""),
+ OPTIONS_TABLE_HOOK("pane-died", ""),
+ OPTIONS_TABLE_HOOK("pane-exited", ""),
+ OPTIONS_TABLE_HOOK("pane-focus-in", ""),
+ OPTIONS_TABLE_HOOK("pane-focus-out", ""),
+ OPTIONS_TABLE_HOOK("pane-mode-changed", ""),
+ OPTIONS_TABLE_HOOK("pane-set-clipboard", ""),
+ OPTIONS_TABLE_HOOK("session-closed", ""),
+ OPTIONS_TABLE_HOOK("session-created", ""),
+ OPTIONS_TABLE_HOOK("session-renamed", ""),
+ OPTIONS_TABLE_HOOK("session-window-changed", ""),
+ OPTIONS_TABLE_HOOK("window-layout-changed", ""),
+ OPTIONS_TABLE_HOOK("window-linked", ""),
+ OPTIONS_TABLE_HOOK("window-pane-changed", ""),
+ OPTIONS_TABLE_HOOK("window-renamed", ""),
+ OPTIONS_TABLE_HOOK("window-unlinked", ""),
+
{ .name = NULL }
};
diff --git a/options.c b/options.c
index 369776e3..c5a776e5 100644
--- a/options.c
+++ b/options.c
@@ -32,10 +32,9 @@
struct options_array_item {
u_int index;
- char *value;
+ union options_value value;
RB_ENTRY(options_array_item) entry;
};
-RB_HEAD(options_array, options_array_item);
static int
options_array_cmp(struct options_array_item *a1, struct options_array_item *a2)
{
@@ -48,19 +47,13 @@ options_array_cmp(struct options_array_item *a1, struct options_array_item *a2)
RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp);
struct options_entry {
- struct options *owner;
+ struct options *owner;
- const char *name;
- const struct options_table_entry *tableentry;
+ const char *name;
+ const struct options_table_entry *tableentry;
+ union options_value value;
- union {
- char *string;
- long long number;
- struct style style;
- struct options_array array;
- };
-
- RB_ENTRY(options_entry) entry;
+ RB_ENTRY(options_entry) entry;
};
struct options {
@@ -83,9 +76,13 @@ static struct options_entry *options_add(struct options *, const char *);
#define OPTIONS_IS_STYLE(o) \
((o)->tableentry != NULL && \
(o)->tableentry->type == OPTIONS_TABLE_STYLE)
-#define OPTIONS_IS_ARRAY(o) \
+#define OPTIONS_IS_COMMAND(o) \
+ ((o)->tableentry != NULL && \
+ (o)->tableentry->type == OPTIONS_TABLE_COMMAND)
+
+#define OPTIONS_IS_ARRAY(o) \
((o)->tableentry != NULL && \
- (o)->tableentry->type == OPTIONS_TABLE_ARRAY)
+ ((o)->tableentry->flags & OPTIONS_TABLE_IS_ARRAY))
static int options_cmp(struct options_entry *, struct options_entry *);
RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp);
@@ -109,6 +106,57 @@ options_parent_table_entry(struct options *oo, const char *s)
return (o->tableentry);
}
+static void
+options_value_free(struct options_entry *o, union options_value *ov)
+{
+ if (OPTIONS_IS_STRING(o))
+ free(ov->string);
+ if (OPTIONS_IS_COMMAND(o) && ov->cmdlist != NULL)
+ cmd_list_free(ov->cmdlist);
+}
+
+static char *
+options_value_tostring(struct options_entry *o, union options_value *ov,
+ int numeric)
+{
+ char *s;
+
+ if (OPTIONS_IS_COMMAND(o))
+ return (cmd_list_print(ov->cmdlist));
+ if (OPTIONS_IS_STYLE(o))
+ return (xstrdup(style_tostring(&ov->style)));
+ if (OPTIONS_IS_NUMBER(o)) {
+ switch (o->tableentry->type) {
+ case OPTIONS_TABLE_NUMBER:
+ xasprintf(&s, "%lld", ov->number);
+ break;
+ case OPTIONS_TABLE_KEY:
+ s = xstrdup(key_string_lookup_key(ov->number));
+ break;
+ case OPTIONS_TABLE_COLOUR:
+ s = xstrdup(colour_tostring(ov->number));
+ break;
+ case OPTIONS_TABLE_FLAG:
+ if (numeric)
+ xasprintf(&s, "%lld", ov->number);
+ else
+ s = xstrdup(ov->number ? "on" : "off");
+ break;
+ case OPTIONS_TABLE_CHOICE:
+ s = xstrdup(o->tableentry->choices[ov->number]);
+ break;
+ case OPTIONS_TABLE_STRING:
+ case OPTIONS_TABLE_STYLE:
+ case OPTIONS_TABLE_COMMAND:
+ fatalx("not a number option type");
+ }
+ return (s);
+ }
+ if (OPTIONS_IS_STRING(o))
+ return (xstrdup(ov->string));
+ return (xstrdup(""));
+}
+
struct options *
options_create(struct options *parent)
{
@@ -174,8 +222,8 @@ options_empty(struct options *oo, const struct options_table_entry *oe)
o = options_add(oo, oe->name);
o->tableentry = oe;
- if (oe->type == OPTIONS_TABLE_ARRAY)
- RB_INIT(&o->array);
+ if (oe->flags & OPTIONS_TABLE_IS_ARRAY)
+ RB_INIT(&o->value.array);
return (o);
}
@@ -183,23 +231,35 @@ options_empty(struct options *oo, const struct options_table_entry *oe)
struct options_entry *
options_default(struct options *oo, const struct options_table_entry *oe)
{
- struct options_entry *o;
- u_int i;
+ struct options_entry *o;
+ union options_value *ov;
+ u_int i;
o = options_empty(oo, oe);
- if (oe->type == OPTIONS_TABLE_ARRAY) {
- if (oe->default_arr != NULL) {
- for (i = 0; oe->default_arr[i] != NULL; i++)
- options_array_set(o, i, oe->default_arr[i], 0);
- } else
- options_array_assign(o, oe->default_str);
- } else if (oe->type == OPTIONS_TABLE_STRING)
- o->string = xstrdup(oe->default_str);
- else if (oe->type == OPTIONS_TABLE_STYLE) {
- style_set(&o->style, &grid_default_cell);
- style_parse(&o->style, &grid_default_cell, oe->default_str);
- } else
- o->number = oe->default_num;
+ ov = &o->value;
+
+ if (oe->flags & OPTIONS_TABLE_IS_ARRAY) {
+ if (oe->default_arr == NULL) {
+ options_array_assign(o, oe->default_str, NULL);
+ return (o);
+ }
+ for (i = 0; oe->default_arr[i] != NULL; i++)
+ options_array_set(o, i, oe->default_arr[i], 0, NULL);
+ return (o);
+ }
+
+ switch (oe->type) {
+ case OPTIONS_TABLE_STRING:
+ ov->string = xstrdup(oe->default_str);
+ break;
+ case OPTIONS_TABLE_STYLE:
+ style_set(&ov->style, &grid_default_cell);
+ style_parse(&ov->style, &grid_default_cell, oe->default_str);
+ break;
+ default:
+ ov->number = oe->default_num;
+ break;
+ }
return (o);
}
@@ -225,11 +285,10 @@ options_remove(struct options_entry *o)
{
struct options *oo = o->owner;
- if (OPTIONS_IS_STRING(o))
- free(o->string);
- else if (OPTIONS_IS_ARRAY(o))
+ if (OPTIONS_IS_ARRAY(o))
options_array_clear(o);
-
+ else
+ options_value_free(o, &o->value);
RB_REMOVE(options_tree, &oo->tree, o);
free(o);
}
@@ -252,14 +311,14 @@ options_array_item(struct options_entry *o, u_int idx)
struct options_array_item a;
a.index = idx;
- return (RB_FIND(options_array, &o->array, &a));
+ return (RB_FIND(options_array, &o->value.array, &a));
}
static void
options_array_free(struct options_entry *o, struct options_array_item *a)
{
- free(a->value);
- RB_REMOVE(options_array, &o->array, a);
+ options_value_free(o, &a->value);
+ RB_REMOVE(options_array, &o->value.array, a);
free(a);
}
@@ -271,11 +330,11 @@ options_array_clear(struct options_entry *o)
if (!OPTIONS_IS_ARRAY(o))
return;
- RB_FOREACH_SAFE(a, options_array, &o->array, a1)
+ RB_FOREACH_SAFE(a, options_array, &o->value.array, a1)
options_array_free(o, a);
}
-const char *
+union options_value *
options_array_get(struct options_entry *o, u_int idx)
{
struct options_array_item *a;
@@ -285,18 +344,27 @@ options_array_get(struct options_entry *o, u_int idx)
a = options_array_item(o, idx);
if (a == NULL)
return (NULL);
- return (a->value);
+ return (&a->value);
}
int
options_array_set(struct options_entry *o, u_int idx, const char *value,
- int append)
+ int append, char **cause)
{
struct options_array_item *a;
char *new;
+ struct cmd_list *cmdlist;
- if (!OPTIONS_IS_ARRAY(o))
+ if (!OPTIONS_IS_ARRAY(o)) {
+ *cause = xstrdup("not an array");
return (-1);
+ }
+
+ if (OPTIONS_IS_COMMAND(o)) {
+ cmdlist = cmd_string_parse(value, NULL, 0, cause);
+ if (cmdlist == NULL && *cause != NULL)
+ return (-1);
+ }
a = options_array_item(o, idx);
if (value == NULL) {
@@ -305,25 +373,29 @@ options_array_set(struct options_entry *o, u_int idx, const char *value,
return (0);
}
- if (a == NULL) {
- a = xcalloc(1, sizeof *a);
- a->index = idx;
- a->value = xstrdup(value);
- RB_INSERT(options_array, &o->array, a);
- } else {
- free(a->value);
+ if (OPTIONS_IS_STRING(o)) {
if (a != NULL && append)
- xasprintf(&new, "%s%s", a->value, value);
+ xasprintf(&new, "%s%s", a->value.string, value);
else
new = xstrdup(value);
- a->value = new;
}
+ if (a == NULL) {
+ a = xcalloc(1, sizeof *a);
+ a->index = idx;
+ RB_INSERT(options_array, &o->value.array, a);
+ } else
+ options_value_free(o, &a->value);
+
+ if (OPTIONS_IS_STRING(o))
+ a->value.string = new;
+ else if (OPTIONS_IS_COMMAND(o))
+ a->value.cmdlist = cmdlist;
return (0);
}
-void
-options_array_assign(struct options_entry *o, const char *s)
+int
+options_array_assign(struct options_entry *o, const char *s, char **cause)
{
const char *separator;
char *copy, *next, *string;
@@ -332,7 +404,18 @@ options_array_assign(struct options_entry *o, const char *s)
separator = o->tableentry->separator;
if (separator == NULL)
separator = " ,";
+ if (*separator == '\0') {
+ if (*s == '\0')
+ return (0);
+ for (i = 0; i < UINT_MAX; i++) {
+ if (options_array_item(o, i) == NULL)
+ break;
+ }
+ return (options_array_set(o, i, s, 0, cause));
+ }
+ if (*s == '\0')
+ return (0);
copy = string = xstrdup(s);
while ((next = strsep(&string, separator)) != NULL) {
if (*next == '\0')
@@ -343,9 +426,13 @@ options_array_assign(struct options_entry *o, const char *s)
}
if (i == UINT_MAX)
break;
- options_array_set(o, i, next, 0);
+ if (options_array_set(o, i, next, 0, cause) != 0) {
+ free(copy);
+ return (-1);
+ }
}
free(copy);
+ return (0);
}
struct options_array_item *
@@ -353,13 +440,13 @@ options_array_first(struct options_entry *o)
{
if (!OPTIONS_IS_ARRAY(o))
return (NULL);
- return (RB_MIN(options_array, &o->array));
+ return (RB_MIN(options_array, &o->value.array));
}
struct options_array_item *
options_array_next(struct options_array_item *a)
{
- return (RB_NEXT(options_array, &o->array, a));
+ return (RB_NEXT(options_array, &o->value.array, a));
}
u_int
@@ -368,10 +455,10 @@ options_array_item_index(struct options_array_item *a)
return (a->index);
}
-const char *
+union options_value *
options_array_item_value(struct options_array_item *a)
{
- return (a->value);
+ return (&a->value);
}
int
@@ -383,59 +470,23 @@ options_isarray(struct options_entry *o)
int
options_isstring(struct options_entry *o)
{
- return (OPTIONS_IS_STRING(o) || OPTIONS_IS_ARRAY(o));
+ return (OPTIONS_IS_STRING(o));
}
-const char *
+char *
options_tostring(struct options_entry *o, int idx, int numeric)
{
- static char s[1024];
- const char *tmp;
struct options_array_item *a;
if (OPTIONS_IS_ARRAY(o)) {
if (idx == -1)
- return (NULL);
+ return (xstrdup(""));
a = options_array_item(o, idx);
if (a == NULL)
- return ("");
- return (a->value);
+ return (xstrdup(""));
+ return (options_value_tostring(o, &a->value, numeric));
}
- if (OPTIONS_IS_STYLE(o))
- return (style_tostring(&o->style));
- if (OPTIONS_IS_NUMBER(o)) {
- tmp = NULL;
- switch (o->tableentry->type) {
- case OPTIONS_TABLE_NUMBER:
- xsnprintf(s, sizeof s, "%lld", o->number);
- break;
- case OPTIONS_TABLE_KEY:
- tmp = key_string_lookup_key(o->number);
- break;
- case OPTIONS_TABLE_COLOUR:
- tmp = colour_tostring(o->number);
- break;
- case OPTIONS_TABLE_FLAG:
- if (numeric)
- xsnprintf(s, sizeof s, "%lld", o->number);
- else
- tmp = (o->number ? "on" : "off");
- break;
- case OPTIONS_TABLE_CHOICE:
- tmp = o->tableentry->choices[o->number];
- break;
- case OPTIONS_TABLE_STRING:
- case OPTIONS_TABLE_STYLE:
- case OPTIONS_TABLE_ARRAY:
- break;
- }
- if (tmp != NULL)
- xsnprintf(s, sizeof s, "%s", tmp);
- return (s);
- }
- if (OPTIONS_IS_STRING(o))
- return (o->string);
- return (NULL);
+ return (options_value_tostring(o, &o->value, numeric));
}
char *
@@ -549,7 +600,7 @@ options_get_string(struct options *oo, const char *name)
fatalx("missing option %s", name);
if (!OPTIONS_IS_STRING(o))
fatalx("option %s is not a string", name);
- return (o->string);
+ return (o->value.string);
}
long long
@@ -562,7 +613,7 @@ options_get_number(struct options *oo, const char *name)
fatalx("missing option %s", name);
if (!OPTIONS_IS_NUMBER(o))
fatalx("option %s is not a number", name);
- return (o->number);
+ return (o->value.number);
}
struct style *
@@ -575,7 +626,7 @@ options_get_style(struct options *oo, const char *name)
fatalx("missing option %s", name);
if (!OPTIONS_IS_STYLE(o))
fatalx("option %s is not a style", name);
- return (&o->style);
+ return (&o->value.style);
}
struct options_entry *
@@ -592,7 +643,7 @@ options_set_string(struct options *oo, const char *name, int append,
o = options_get_only(oo, name);
if (o != NULL && append && OPTIONS_IS_STRING(o)) {
- xasprintf(&value, "%s%s", o->string, s);
+ xasprintf(&value, "%s%s", o->value.string, s);
free(s);
} else
value = s;
@@ -606,8 +657,8 @@ options_set_string(struct options *oo, const char *name, int append,
if (!OPTIONS_IS_STRING(o))
fatalx("option %s is not a string", name);
- free(o->string);
- o->string = value;
+ free(o->value.string);
+ o->value.string = value;
return (o);
}
@@ -628,7 +679,7 @@ options_set_number(struct options *oo, const char *name, long long value)
if (!OPTIONS_IS_NUMBER(o))
fatalx("option %s is not a number", name);
- o->number = value;
+ o->value.number = value;
return (o);
}
@@ -644,7 +695,7 @@ options_set_style(struct options *oo, const char *name, int append,
o = options_get_only(oo, name);
if (o != NULL && append && OPTIONS_IS_STYLE(o))
- style_copy(&sy, &o->style);
+ style_copy(&sy, &o->value.style);
else
style_set(&sy, &grid_default_cell);
if (style_parse(&sy, &grid_default_cell, value) == -1)
@@ -657,7 +708,7 @@ options_set_style(struct options *oo, const char *name, int append,
if (!OPTIONS_IS_STYLE(o))
fatalx("option %s is not a style", name);
- style_copy(&o->style, &sy);
+ style_copy(&o->value.style, &sy);
return (o);
}
diff --git a/osdep-darwin.c b/osdep-darwin.c
index 5d69cdda..d4a88028 100644
--- a/osdep-darwin.c
+++ b/osdep-darwin.c
@@ -30,7 +30,9 @@ char *osdep_get_name(int, char *);
char *osdep_get_cwd(int);
struct event_base *osdep_event_init(void);
+#ifndef __unused
#define __unused __attribute__ ((__unused__))
+#endif
char *
osdep_get_name(int fd, __unused char *tty)
@@ -47,6 +49,7 @@ osdep_get_name(int fd, __unused char *tty)
&bsdinfo, sizeof bsdinfo);
if (ret == sizeof bsdinfo && *bsdinfo.pbsi_comm != '\0')
return (strdup(bsdinfo.pbsi_comm));
+ return (NULL);
#else
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 };
size_t size;
diff --git a/paste.c b/paste.c
index 2f2c043b..c1036ad9 100644
--- a/paste.c
+++ b/paste.c
@@ -157,11 +157,14 @@ paste_free(struct paste_buffer *pb)
* that the caller is responsible for allocating data.
*/
void
-paste_add(char *data, size_t size)
+paste_add(const char *prefix, char *data, size_t size)
{
struct paste_buffer *pb, *pb1;
u_int limit;
+ if (prefix == NULL)
+ prefix = "buffer";
+
if (size == 0) {
free(data);
return;
@@ -180,7 +183,7 @@ paste_add(char *data, size_t size)
pb->name = NULL;
do {
free(pb->name);
- xasprintf(&pb->name, "buffer%04u", paste_next_index);
+ xasprintf(&pb->name, "%s%u", prefix, paste_next_index);
paste_next_index++;
} while (paste_get_name(pb->name) != NULL);
@@ -262,7 +265,7 @@ paste_set(char *data, size_t size, const char *name, char **cause)
return (0);
}
if (name == NULL) {
- paste_add(data, size);
+ paste_add(NULL, data, size);
return (0);
}
diff --git a/regress/control-client-sanity.sh b/regress/control-client-sanity.sh
new file mode 100644
index 00000000..bf76b4d5
--- /dev/null
+++ b/regress/control-client-sanity.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+PATH=/bin:/usr/bin
+TERM=screen
+
+[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
+TMUX="$TEST_TMUX -Ltest"
+$TMUX kill-server 2>/dev/null
+
+TMP=$(mktemp)
+OUT=$(mktemp)
+trap "rm -f $TMP $OUT" 0 1 15
+
+$TMUX -f/dev/null new -d -x200 -y200 || exit 1
+$TMUX -f/dev/null splitw || exit 1
+sleep 1
+cat <<EOF|$TMUX -C a >$TMP
+selectp -t%0
+splitw
+neww
+splitw
+selectp -t%0
+killp -t%1
+swapp -t%2 -s%3
+neww
+splitw
+splitw
+selectl tiled
+killw
+EOF
+sleep 1
+$TMUX has || exit 1
+$TMUX lsp -aF '#{pane_id} #{window_layout}' >$TMP || exit 1
+cat <<EOF|cmp -s $TMP - || exit 1
+%0 f5ab,200x200,0,0[200x50,0,0,0,200x149,0,51,3]
+%3 f5ab,200x200,0,0[200x50,0,0,0,200x149,0,51,3]
+%2 dcbd,200x200,0,0[200x100,0,0,2,200x99,0,101,4]
+%4 dcbd,200x200,0,0[200x100,0,0,2,200x99,0,101,4]
+EOF
+$TMUX kill-server 2>/dev/null
+
+exit 0
diff --git a/screen-write.c b/screen-write.c
index 566d4d14..237b6359 100644
--- a/screen-write.c
+++ b/screen-write.c
@@ -1139,7 +1139,7 @@ screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
u_int i;
size_t size;
- for (i = y ; i < y + n; i++) {
+ for (i = y; i < y + n; i++) {
if (TAILQ_EMPTY(&ctx->list[i].items))
continue;
size = 0;
diff --git a/server-client.c b/server-client.c
index aea090c7..bf7da577 100644
--- a/server-client.c
+++ b/server-client.c
@@ -1379,6 +1379,7 @@ focused:
if (wp->base.mode & MODE_FOCUSON)
bufferevent_write(wp->event, "\033[I", 3);
notify_pane("pane-focus-in", wp);
+ session_update_activity(c->session, NULL);
}
wp->flags |= PANE_FOCUSED;
}
diff --git a/server-fn.c b/server-fn.c
index 90db7d51..8051fe4f 100644
--- a/server-fn.c
+++ b/server-fn.c
@@ -369,7 +369,7 @@ server_destroy_session_group(struct session *s)
else {
TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
server_destroy_session(s);
- session_destroy(s, __func__);
+ session_destroy(s, 1, __func__);
}
}
}
@@ -436,7 +436,7 @@ server_check_unattached(void)
if (s->attached != 0)
continue;
if (options_get_number (s->options, "destroy-unattached"))
- session_destroy(s, __func__);
+ session_destroy(s, 1, __func__);
}
}
diff --git a/server.c b/server.c
index 04f907f3..41bc492e 100644
--- a/server.c
+++ b/server.c
@@ -296,7 +296,7 @@ server_send_exit(void)
}
RB_FOREACH_SAFE(s, sessions, &sessions, s1)
- session_destroy(s, __func__);
+ session_destroy(s, 1, __func__);
}
/* Update socket execute permissions based on whether sessions are attached. */
diff --git a/session.c b/session.c
index fe4a8875..b361bde3 100644
--- a/session.c
+++ b/session.c
@@ -111,12 +111,10 @@ session_find_by_id(u_int id)
/* Create a new session. */
struct session *
-session_create(const char *prefix, const char *name, int argc, char **argv,
- const char *path, const char *cwd, struct environ *env, struct options *oo,
- struct termios *tio, int idx, char **cause)
+session_create(const char *prefix, const char *name, const char *cwd,
+ struct environ *env, struct options *oo, struct termios *tio)
{
struct session *s;
- struct winlink *wl;
s = xcalloc(1, sizeof *s);
s->references = 1;
@@ -128,12 +126,8 @@ session_create(const char *prefix, const char *name, int argc, char **argv,
TAILQ_INIT(&s->lastw);
RB_INIT(&s->windows);
- s->environ = environ_create();
- if (env != NULL)
- environ_copy(env, s->environ);
-
+ s->environ = env;
s->options = oo;
- s->hooks = hooks_create(global_hooks);
status_update_cache(s);
@@ -165,17 +159,6 @@ session_create(const char *prefix, const char *name, int argc, char **argv,
fatal("gettimeofday failed");
session_update_activity(s, &s->creation_time);
- if (argc >= 0) {
- wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause);
- if (wl == NULL) {
- session_destroy(s, __func__);
- return (NULL);
- }
- session_select(s, RB_ROOT(&s->windows)->idx);
- }
-
- log_debug("session %s created", s->name);
-
return (s);
}
@@ -208,9 +191,7 @@ session_free(__unused int fd, __unused short events, void *arg)
if (s->references == 0) {
environ_free(s->environ);
-
options_free(s->options);
- hooks_free(s->hooks);
free(s->name);
free(s);
@@ -219,7 +200,7 @@ session_free(__unused int fd, __unused short events, void *arg)
/* Destroy a session. */
void
-session_destroy(struct session *s, const char *from)
+session_destroy(struct session *s, int notify, const char *from)
{
struct winlink *wl;
@@ -227,7 +208,8 @@ session_destroy(struct session *s, const char *from)
s->curw = NULL;
RB_REMOVE(sessions, &sessions, s);
- notify_session("session-closed", s);
+ if (notify)
+ notify_session("session-closed", s);
free(s->tio);
@@ -337,45 +319,6 @@ session_previous_session(struct session *s)
return (s2);
}
-/* Create a new window on a session. */
-struct winlink *
-session_new(struct session *s, const char *name, int argc, char **argv,
- const char *path, const char *cwd, int idx, char **cause)
-{
- struct window *w;
- struct winlink *wl;
- struct environ *env;
- const char *shell;
- u_int hlimit, sx, sy;
-
- if ((wl = winlink_add(&s->windows, idx)) == NULL) {
- xasprintf(cause, "index in use: %d", idx);
- return (NULL);
- }
- wl->session = s;
-
- shell = options_get_string(s->options, "default-shell");
- if (*shell == '\0' || areshell(shell))
- shell = _PATH_BSHELL;
-
- default_window_size(s, NULL, &sx, &sy, -1);
- hlimit = options_get_number(s->options, "history-limit");
- env = environ_for_session(s, 0);
- w = window_create_spawn(name, argc, argv, path, shell, cwd, env, s->tio,
- sx, sy, hlimit, cause);
- if (w == NULL) {
- winlink_remove(&s->windows, wl);
- environ_free(env);
- return (NULL);
- }
- winlink_set_window(wl, w);
- environ_free(env);
- notify_session_window("window-linked", s, w);
-
- session_group_synchronize_from(s);
- return (wl);
-}
-
/* Attach a window to a session. */
struct winlink *
session_attach(struct session *s, struct window *w, int idx, char **cause)
@@ -411,7 +354,7 @@ session_detach(struct session *s, struct winlink *wl)
session_group_synchronize_from(s);
if (RB_EMPTY(&s->windows)) {
- session_destroy(s, __func__);
+ session_destroy(s, 1, __func__);
return (1);
}
return (0);
diff --git a/spawn.c b/spawn.c
new file mode 100644
index 00000000..6892e578
--- /dev/null
+++ b/spawn.c
@@ -0,0 +1,434 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Set up the environment and create a new window and pane or a new pane.
+ *
+ * We need to set up the following items:
+ *
+ * - history limit, comes from the session;
+ *
+ * - base index, comes from the session;
+ *
+ * - current working directory, may be specified - if it isn't it comes from
+ * either the client or the session;
+ *
+ * - PATH variable, comes from the client if any, otherwise from the session
+ * environment;
+ *
+ * - shell, comes from default-shell;
+ *
+ * - termios, comes from the session;
+ *
+ * - remaining environment, comes from the session.
+ */
+
+static void
+spawn_log(const char *from, struct spawn_context *sc)
+{
+ struct session *s = sc->s;
+ struct winlink *wl = sc->wl;
+ struct window_pane *wp0 = sc->wp0;
+ char tmp[128];
+ const char *name;
+
+ log_debug("%s: %s, flags=%#x", from, sc->item->name, sc->flags);
+
+ if (wl != NULL && wp0 != NULL)
+ xsnprintf(tmp, sizeof tmp, "wl=%d wp0=%%%u", wl->idx, wp0->id);
+ else if (wl != NULL)
+ xsnprintf(tmp, sizeof tmp, "wl=%d wp0=none", wl->idx);
+ else if (wp0 != NULL)
+ xsnprintf(tmp, sizeof tmp, "wl=none wp0=%%%u", wp0->id);
+ else
+ xsnprintf(tmp, sizeof tmp, "wl=none wp0=none");
+ log_debug("%s: s=$%u %s idx=%d", from, s->id, tmp, sc->idx);
+
+ name = sc->name;
+ if (name == NULL)
+ name = "none";
+ log_debug("%s: name=%s", from, name);
+}
+
+struct winlink *
+spawn_window(struct spawn_context *sc, char **cause)
+{
+ struct session *s = sc->s;
+ struct window *w;
+ struct window_pane *wp;
+ struct winlink *wl;
+ int idx = sc->idx;
+ u_int sx, sy;
+
+ spawn_log(__func__, sc);
+
+ /*
+ * If the window already exists, we are respawning, so destroy all the
+ * panes except one.
+ */
+ if (sc->flags & SPAWN_RESPAWN) {
+ w = sc->wl->window;
+ if (~sc->flags & SPAWN_KILL) {
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp->fd != -1)
+ break;
+ }
+ if (wp != NULL) {
+ xasprintf(cause, "window %s:%d still active",
+ s->name, sc->wl->idx);
+ return (NULL);
+ }
+ }
+
+ sc->wp0 = TAILQ_FIRST(&w->panes);
+ TAILQ_REMOVE(&w->panes, sc->wp0, entry);
+
+ layout_free(w);
+ window_destroy_panes(w);
+
+ TAILQ_INSERT_HEAD(&w->panes, sc->wp0, entry);
+ window_pane_resize(sc->wp0, w->sx, w->sy);
+
+ layout_init(w, sc->wp0);
+ window_set_active_pane(w, sc->wp0, 0);
+ }
+
+ /*
+ * Otherwise we have no window so we will need to create one. First
+ * check if the given index already exists and destroy it if so.
+ */
+ if ((~sc->flags & SPAWN_RESPAWN) && idx != -1) {
+ wl = winlink_find_by_index(&s->windows, idx);
+ if (wl != NULL && (~sc->flags & SPAWN_KILL)) {
+ xasprintf(cause, "index %d in use", idx);
+ return (NULL);
+ }
+ if (wl != NULL) {
+ /*
+ * Can't use session_detach as it will destroy session
+ * if this makes it empty.
+ */
+ wl->flags &= ~WINLINK_ALERTFLAGS;
+ notify_session_window("window-unlinked", s, wl->window);
+ winlink_stack_remove(&s->lastw, wl);
+ winlink_remove(&s->windows, wl);
+
+ if (s->curw == wl) {
+ s->curw = NULL;
+ sc->flags &= ~SPAWN_DETACHED;
+ }
+ }
+ }
+
+ /* Then create a window if needed. */
+ if (~sc->flags & SPAWN_RESPAWN) {
+ if (idx == -1)
+ idx = -1 - options_get_number(s->options, "base-index");
+ if ((sc->wl = winlink_add(&s->windows, idx)) == NULL) {
+ xasprintf(cause, "couldn't add window %d", idx);
+ return (NULL);
+ }
+ default_window_size(s, NULL, &sx, &sy, -1);
+ if ((w = window_create(sx, sy)) == NULL) {
+ winlink_remove(&s->windows, sc->wl);
+ xasprintf(cause, "couldn't create window %d", idx);
+ return (NULL);
+ }
+ sc->wl->session = s;
+ winlink_set_window(sc->wl, w);
+ } else
+ w = NULL;
+ sc->flags |= SPAWN_NONOTIFY;
+
+ /* Spawn the pane. */
+ wp = spawn_pane(sc, cause);
+ if (wp == NULL) {
+ if (~sc->flags & SPAWN_RESPAWN) {
+ window_destroy(w);
+ winlink_remove(&s->windows, sc->wl);
+ }
+ return (NULL);
+ }
+
+ /* Set the name of the new window. */
+ if (~sc->flags & SPAWN_RESPAWN) {
+ if (sc->name != NULL) {
+ w->name = xstrdup(sc->name);
+ options_set_number(w->options, "automatic-rename", 0);
+ } else
+ w->name = xstrdup(default_window_name(w));
+ }
+
+ /* Switch to the new window if required. */
+ if (~sc->flags & SPAWN_DETACHED)
+ session_select(s, sc->wl->idx);
+
+ /* Fire notification if new window. */
+ if (~sc->flags & SPAWN_RESPAWN)
+ notify_session_window("window-linked", s, w);
+
+ session_group_synchronize_from(s);
+ return (sc->wl);
+}
+
+struct window_pane *
+spawn_pane(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 = sc->wl->window;
+ struct window_pane *new_wp;
+ struct environ *child;
+ struct environ_entry *ee;
+ char **argv, *cp, **argvp, *argv0, *cwd;
+ const char *cmd, *tmp;
+ int argc;
+ u_int idx;
+ struct termios now;
+ u_int hlimit;
+ struct winsize ws;
+ sigset_t set, oldset;
+
+ spawn_log(__func__, sc);
+
+ /*
+ * If we are respawning then get rid of the old process. Otherwise
+ * either create a new cell or assign to the one we are given.
+ */
+ hlimit = options_get_number(s->options, "history-limit");
+ if (sc->flags & SPAWN_RESPAWN) {
+ if (sc->wp0->fd != -1 && (~sc->flags & SPAWN_KILL)) {
+ window_pane_index(sc->wp0, &idx);
+ xasprintf(cause, "pane %s:%d.%u still active",
+ s->name, sc->wl->idx, idx);
+ return (NULL);
+ }
+ if (sc->wp0->fd != -1) {
+ bufferevent_free(sc->wp0->event);
+ close(sc->wp0->fd);
+ }
+ window_pane_reset_mode_all(sc->wp0);
+ screen_reinit(&sc->wp0->base);
+ input_init(sc->wp0);
+ new_wp = sc->wp0;
+ new_wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN);
+ } else if (sc->lc == NULL) {
+ new_wp = window_add_pane(w, NULL, hlimit, sc->flags);
+ layout_init(w, new_wp);
+ } else {
+ new_wp = window_add_pane(w, sc->wp0, hlimit, sc->flags);
+ layout_assign_pane(sc->lc, new_wp);
+ }
+
+ /*
+ * Now we have a pane with nothing running in it ready for the new
+ * process. Work out the command and arguments.
+ */
+ if (sc->argc == 0) {
+ cmd = options_get_string(s->options, "default-command");
+ if (cmd != NULL && *cmd != '\0') {
+ argc = 1;
+ argv = (char **)&cmd;
+ } else {
+ argc = 0;
+ argv = NULL;
+ }
+ } else {
+ argc = sc->argc;
+ argv = sc->argv;
+ }
+
+ /*
+ * Replace the stored arguments if there are new ones. If not, the
+ * existing ones will be used (they will only exist for respawn).
+ */
+ if (argc > 0) {
+ cmd_free_argv(new_wp->argc, new_wp->argv);
+ new_wp->argc = argc;
+ new_wp->argv = cmd_copy_argv(argc, argv);
+ }
+
+ /*
+ * Work out the current working directory. If respawning, use
+ * the pane's stored one unless specified.
+ */
+ if (sc->cwd != NULL)
+ cwd = format_single(item, sc->cwd, c, s, NULL, NULL);
+ else if (~sc->flags & SPAWN_RESPAWN)
+ cwd = xstrdup(server_client_get_cwd(c, s));
+ else
+ cwd = NULL;
+ if (cwd != NULL) {
+ free(new_wp->cwd);
+ new_wp->cwd = cwd;
+ }
+
+ /* Create an environment for this pane. */
+ child = environ_for_session(s, 0);
+ if (sc->environ != NULL)
+ environ_copy(sc->environ, child);
+ environ_set(child, "TMUX_PANE", "%%%u", new_wp->id);
+
+ /*
+ * Then the PATH environment variable. The session one is replaced from
+ * the client if there is one because otherwise running "tmux new
+ * myprogram" wouldn't work if myprogram isn't in the session's path.
+ */
+ if (c != NULL && c->session == NULL) { /* only unattached clients */
+ ee = environ_find(c->environ, "PATH");
+ if (ee != NULL)
+ environ_set(child, "PATH", "%s", ee->value);
+ }
+ if (environ_find(child, "PATH") == NULL)
+ environ_set(child, "%s", _PATH_DEFPATH);
+
+ /* Then the shell. If respawning, use the old one. */
+ if (~sc->flags & SPAWN_RESPAWN) {
+ tmp = options_get_string(s->options, "default-shell");
+ if (*tmp == '\0' || areshell(tmp))
+ tmp = _PATH_BSHELL;
+ free(new_wp->shell);
+ new_wp->shell = xstrdup(tmp);
+ }
+ environ_set(child, "SHELL", "%s", new_wp->shell);
+
+ /* Log the arguments we are going to use. */
+ log_debug("%s: shell=%s", __func__, new_wp->shell);
+ if (new_wp->argc != 0) {
+ cp = cmd_stringify_argv(new_wp->argc, new_wp->argv);
+ log_debug("%s: cmd=%s", __func__, cp);
+ free(cp);
+ }
+ if (cwd != NULL)
+ log_debug("%s: cwd=%s", __func__, cwd);
+ cmd_log_argv(new_wp->argc, new_wp->argv, __func__);
+ environ_log(child, "%s: environment ", __func__);
+
+ /* Initialize the window size. */
+ memset(&ws, 0, sizeof ws);
+ ws.ws_col = screen_size_x(&new_wp->base);
+ ws.ws_row = screen_size_y(&new_wp->base);
+
+ /* Block signals until fork has completed. */
+ sigfillset(&set);
+ sigprocmask(SIG_BLOCK, &set, &oldset);
+
+ /* Fork the new process. */
+ new_wp->pid = fdforkpty(ptm_fd, &new_wp->fd, new_wp->tty, NULL, &ws);
+ if (new_wp->pid == -1) {
+ xasprintf(cause, "fork failed: %s", strerror(errno));
+ new_wp->fd = -1;
+ if (~sc->flags & SPAWN_RESPAWN) {
+ layout_close_pane(new_wp);
+ window_remove_pane(w, new_wp);
+ }
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ return (NULL);
+ }
+
+ /* In the parent process, everything is done now. */
+ if (new_wp->pid != 0) {
+ new_wp->pipe_off = 0;
+ new_wp->flags &= ~PANE_EXITED;
+
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ window_pane_set_event(new_wp);
+
+ if (sc->flags & SPAWN_RESPAWN)
+ return (new_wp);
+ if ((~sc->flags & SPAWN_DETACHED) || w->active == NULL) {
+ if (sc->flags & SPAWN_NONOTIFY)
+ window_set_active_pane(w, new_wp, 0);
+ else
+ window_set_active_pane(w, new_wp, 1);
+ }
+ if (~sc->flags & SPAWN_NONOTIFY)
+ notify_window("window-layout-changed", w);
+ return (new_wp);
+ }
+
+ /*
+ * Child process. Change to the working directory or home if that
+ * fails.
+ */
+ if (chdir(new_wp->cwd) != 0) {
+ if ((tmp = find_home()) == NULL || chdir(tmp) != 0)
+ chdir("/");
+ }
+
+ /*
+ * Update terminal escape characters from the session if available and
+ * force VERASE to tmux's \177.
+ */
+ if (tcgetattr(STDIN_FILENO, &now) != 0)
+ _exit(1);
+ if (s->tio != NULL)
+ memcpy(now.c_cc, s->tio->c_cc, sizeof now.c_cc);
+ now.c_cc[VERASE] = '\177';
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &now) != 0)
+ _exit(1);
+
+ /* Clean up file descriptors and signals and update the environment. */
+ closefrom(STDERR_FILENO + 1);
+ proc_clear_signals(server_proc, 1);
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ log_close();
+ environ_push(child);
+
+ /*
+ * If given multiple arguments, use execvp(). Copy the arguments to
+ * ensure they end in a NULL.
+ */
+ if (new_wp->argc != 0 && new_wp->argc != 1) {
+ argvp = cmd_copy_argv(new_wp->argc, new_wp->argv);
+ execvp(argvp[0], argvp);
+ _exit(1);
+ }
+
+ /*
+ * If one argument, pass it to $SHELL -c. Otherwise create a login
+ * shell.
+ */
+ cp = strrchr(new_wp->shell, '/');
+ if (new_wp->argc == 1) {
+ tmp = new_wp->argv[0];
+ if (cp != NULL && cp[1] != '\0')
+ xasprintf(&argv0, "%s", cp + 1);
+ else
+ xasprintf(&argv0, "%s", new_wp->shell);
+ execl(new_wp->shell, argv0, "-c", tmp, (char *)NULL);
+ _exit(1);
+ }
+ if (cp != NULL && cp[1] != '\0')
+ xasprintf(&argv0, "-%s", cp + 1);
+ else
+ xasprintf(&argv0, "-%s", new_wp->shell);
+ execl(new_wp->shell, argv0, (char *)NULL);
+ _exit(1);
+}
diff --git a/status.c b/status.c
index 467db6ad..89912114 100644
--- a/status.c
+++ b/status.c
@@ -323,8 +323,8 @@ status_redraw(struct client *c)
u_int lines, i, width = c->tty.sx;
int flags, force = 0, changed = 0;
struct options_entry *o;
+ union options_value *ov;
struct format_tree *ft;
- const char *fmt;
char *expanded;
log_debug("%s enter", __func__);
@@ -370,14 +370,14 @@ status_redraw(struct client *c)
for (i = 0; i < lines; i++) {
screen_write_cursormove(&ctx, 0, i, 0);
- fmt = options_array_get(o, i);
- if (fmt == NULL) {
+ ov = options_array_get(o, i);
+ if (ov == NULL) {
screen_write_clearline(&ctx, gc.bg);
continue;
}
sle = &sl->entries[i];
- expanded = format_expand_time(ft, fmt);
+ expanded = format_expand_time(ft, ov->string);
if (!force &&
sle->expanded != NULL &&
strcmp(expanded, sle->expanded) == 0) {
@@ -1321,10 +1321,9 @@ status_prompt_complete_list(u_int *size, const char *s)
if (o != NULL) {
a = options_array_first(o);
while (a != NULL) {
- value = options_array_item_value(a);;
- if (value == NULL || (cp = strchr(value, '=')) == NULL)
+ value = options_array_item_value(a)->string;
+ if ((cp = strchr(value, '=')) == NULL)
goto next;
-
valuelen = cp - value;
if (slen > valuelen || strncmp(value, s, slen) != 0)
goto next;
diff --git a/style.c b/style.c
index c3d2960d..92941dbd 100644
--- a/style.c
+++ b/style.c
@@ -172,7 +172,7 @@ style_tostring(struct style *sy)
{
struct grid_cell *gc = &sy->gc;
int off = 0;
- const char *comma = "", *tmp;
+ const char *comma = "", *tmp = "";
static char s[256];
char b[16];
diff --git a/tmux.1 b/tmux.1
index 3ba70770..04e08e6e 100644
--- a/tmux.1
+++ b/tmux.1
@@ -1096,6 +1096,14 @@ Switch the current session for client
.Ar target-client
to
.Ar target-session .
+As a special case,
+.Fl t
+may refer to a pane (a target that contains
+.Ql : ,
+.Ql .
+or
+.Ql % )
+in which case the session, window and pane are all changed.
If
.Fl l ,
.Fl n
@@ -1175,12 +1183,14 @@ The following commands are supported in copy mode:
.It Li "bottom-line" Ta "L" Ta ""
.It Li "cancel" Ta "q" Ta "Escape"
.It Li "clear-selection" Ta "Escape" Ta "C-g"
-.It Li "copy-end-of-line" Ta "D" Ta "C-k"
-.It Li "copy-line" Ta "" Ta ""
-.It Li "copy-pipe <command>" Ta "" Ta ""
-.It Li "copy-pipe-and-cancel <command>" Ta "" Ta ""
-.It Li "copy-selection" Ta "" Ta ""
-.It Li "copy-selection-and-cancel" Ta "Enter" Ta "M-w"
+.It Li "copy-end-of-line [<prefix>]" Ta "D" Ta "C-k"
+.It Li "copy-line [<prefix>]" Ta "" Ta ""
+.It Li "copy-pipe <command> [<prefix>]" Ta "" Ta ""
+.It Li "copy-pipe-no-clear <command> [<prefix>]" Ta "" Ta ""
+.It Li "copy-pipe-and-cancel <command> [<prefix>]" Ta "" Ta ""
+.It Li "copy-selection [<prefix>]" Ta "" Ta ""
+.It Li "copy-selection-no-clear [<prefix>]" Ta "" Ta ""
+.It Li "copy-selection-and-cancel [<prefix>]" Ta "Enter" Ta "M-w"
.It Li "cursor-down" Ta "j" Ta "Down"
.It Li "cursor-left" Ta "h" Ta "Left"
.It Li "cursor-right" Ta "l" Ta "Right"
@@ -1222,15 +1232,27 @@ The following commands are supported in copy mode:
.It Li "search-forward-incremental <for>" Ta "" Ta "C-s"
.It Li "search-reverse" Ta "N" Ta "N"
.It Li "select-line" Ta "V" Ta ""
+.It Li "select-word" Ta "" Ta ""
.It Li "start-of-line" Ta "0" Ta "C-a"
.It Li "stop-selection" Ta "" Ta ""
.It Li "top-line" Ta "H" Ta "M-R"
.El
.Pp
+Copy commands may take an optional buffer prefix argument which is used
+to generate the buffer name (the default is
+.Ql buffer
+so buffers are named
+.Ql buffer0 ,
+.Ql buffer1
+and so on).
+Pipe commands take a command argument which is the command to which the
+copied text is piped.
The
.Ql -and-cancel
variants of some commands exit copy mode after they have completed (for copy
commands) or when the cursor reaches the bottom (for scrolling commands).
+.Ql -no-clear
+variants do not clear the selection.
.Pp
The next and previous word keys use space and the
.Ql - ,
@@ -1805,6 +1827,7 @@ option.
.It Xo Ic new-window
.Op Fl adkP
.Op Fl c Ar start-directory
+.Op Fl e Ar environment
.Op Fl F Ar format
.Op Fl n Ar window-name
.Op Fl t Ar target-window
@@ -1844,6 +1867,12 @@ See the
.Ic remain-on-exit
option to change this behaviour.
.Pp
+.Fl e
+takes the form
+.Ql VARIABLE=value
+and sets an environment variable for the newly created window; it may be
+specified multiple times.
+.Pp
The
.Ev TERM
environment variable must be set to
@@ -1856,7 +1885,9 @@ for all programs running
New windows will automatically have
.Ql TERM=screen
added to their environment, but care must be taken not to reset this in shell
-start-up files.
+start-up files or by the
+.Fl e
+option.
.Pp
The
.Fl P
@@ -2015,8 +2046,9 @@ This command will automatically set
.Ic window-size
to manual in the window options.
.It Xo Ic respawn-pane
-.Op Fl c Ar start-directory
.Op Fl k
+.Op Fl c Ar start-directory
+.Op Fl e Ar environment
.Op Fl t Ar target-pane
.Op Ar shell-command
.Xc
@@ -2032,9 +2064,15 @@ The pane must be already inactive, unless
is given, in which case any existing command is killed.
.Fl c
specifies a new working directory for the pane.
+The
+.Fl e
+option has the same meaning as for the
+.Ic new-window
+command.
.It Xo Ic respawn-window
-.Op Fl c Ar start-directory
.Op Fl k
+.Op Fl c Ar start-directory
+.Op Fl e Ar environment
.Op Fl t Ar target-window
.Op Ar shell-command
.Xc
@@ -2050,6 +2088,11 @@ The window must be already inactive, unless
is given, in which case any existing command is killed.
.Fl c
specifies a new working directory for the window.
+The
+.Fl e
+option has the same meaning as for the
+.Ic new-window
+command.
.It Xo Ic rotate-window
.Op Fl DU
.Op Fl t Ar target-window
@@ -2168,6 +2211,7 @@ the command behaves like
.It Xo Ic split-window
.Op Fl bdfhvP
.Op Fl c Ar start-directory
+.Op Fl e Ar environment
.Oo Fl l
.Ar size |
.Fl p Ar percentage Oc
@@ -2513,9 +2557,17 @@ command),
a server option with
.Fl s ,
otherwise a session option.
+If the option is not a user option,
+.Fl w
+and
+.Fl s
+are unnecessary -
+.Nm
+will infer the type from the option name.
If
.Fl g
is given, the global session or window option is set.
+.Pp
.Fl F
expands formats in the option value.
The
@@ -3431,7 +3483,7 @@ function key sequences; these have a number included to indicate modifiers such
as Shift, Alt or Ctrl.
.El
.It Xo Ic show-options
-.Op Fl gqsvw
+.Op Fl gHqsvw
.Op Fl t Ar target-session | Ar target-window
.Op Ar option
.Xc
@@ -3444,6 +3496,15 @@ the server options with
.Fl s ,
otherwise the session options for
.Ar target session .
+If
+.Ar option
+is given and is not a user option,
+.Fl w
+and
+.Fl s
+are unnecessary -
+.Nm
+will infer the type from the option name.
Global session or window options are listed if
.Fl g
is used.
@@ -3454,6 +3515,8 @@ If
is set, no error will be returned if
.Ar option
is unset.
+.Fl H
+includes hooks (omitted by default).
.It Xo Ic show-window-options
.Op Fl gv
.Op Fl t Ar target-window
@@ -3478,6 +3541,26 @@ commands have an
.Em after
hook and there are a number of hooks not associated with commands.
.Pp
+Hooks are stored as array options, members of the array are executed in
+order when the hook is triggered.
+Hooks may be configured with the
+.Ic set-hook
+or
+.Ic set-option
+commands and displayed with
+.Ic show-hooks
+or
+.Ic show-options
+.Fl H .
+The following two commands are equivalent:
+.Bd -literal -offset indent.
+set-hook -g pane-mode-changed[42] 'set -g status-left-style bg=red'
+set-option -g pane-mode-changed[42] 'set -g status-left-style bg=red'
+.Ed
+.Pp
+Setting a hook without specifying an array index clears the hook and sets the
+first member of the array.
+.Pp
A command's after
hook is run after it completes, except when the command is run as part of a hook
itself.
@@ -3488,7 +3571,7 @@ For example, the following command adds a hook to select the even-vertical
layout after every
.Ic split-window :
.Bd -literal -offset indent
-set-hook after-split-window "selectl even-vertical"
+set-hook -g after-split-window "selectl even-vertical"
.Ed
.Pp
All the notifications listed in the
@@ -3552,7 +3635,7 @@ Run when a window is unlinked from a session.
Hooks are managed with these commands:
.Bl -tag -width Ds
.It Xo Ic set-hook
-.Op Fl gRu
+.Op Fl agRu
.Op Fl t Ar target-session
.Ar hook-name
.Ar command
@@ -3574,6 +3657,8 @@ hooks (for
.Ar target-session
with
.Fl t ) .
+.Fl a
+appends to a hook.
Like options, session hooks inherit from the global ones.
.Pp
With
@@ -3975,7 +4060,7 @@ In addition, embedded styles may be specified in format options, such as
by enclosing them in
.Ql #[
and
-.Ql ] .
+.Ql \&] .
.Pp
A style may be the single term
.Ql default
diff --git a/tmux.c b/tmux.c
index f4265e0a..685827d4 100644
--- a/tmux.c
+++ b/tmux.c
@@ -36,7 +36,6 @@ struct options *global_options; /* server options */
struct options *global_s_options; /* session options */
struct options *global_w_options; /* window options */
struct environ *global_environ;
-struct hooks *global_hooks;
struct timeval start_time;
const char *socket_path;
@@ -312,8 +311,6 @@ main(int argc, char **argv)
flags |= CLIENT_UTF8;
}
- global_hooks = hooks_create(NULL);
-
global_environ = environ_create();
for (var = environ; *var != NULL; var++)
environ_put(global_environ, *var);
diff --git a/tmux.h b/tmux.h
index d7f38199..6384b42a 100644
--- a/tmux.h
+++ b/tmux.h
@@ -39,6 +39,7 @@
extern char **environ;
struct args;
+struct args_value;
struct client;
struct cmd_find_state;
struct cmdq_item;
@@ -235,8 +236,8 @@ enum {
/* Termcap codes. */
enum tty_code_code {
- TTYC_AX = 0,
TTYC_ACSC,
+ TTYC_AX,
TTYC_BCE,
TTYC_BEL,
TTYC_BLINK,
@@ -685,15 +686,6 @@ struct style {
u_int range_argument;
};
-/* Hook data structures. */
-struct hook {
- const char *name;
-
- struct cmd_list *cmdlist;
-
- RB_ENTRY(hook) entry;
-};
-
/* Virtual screen. */
struct screen_sel;
struct screen_titles;
@@ -816,7 +808,7 @@ struct window_pane {
int argc;
char **argv;
char *shell;
- const char *cwd;
+ char *cwd;
pid_t pid;
char tty[TTY_NAME_MAX];
@@ -997,7 +989,6 @@ struct session {
int statusat;
u_int statuslines;
- struct hooks *hooks;
struct options *options;
#define SESSION_PASTING 0x1
@@ -1516,6 +1507,16 @@ struct key_table {
};
RB_HEAD(key_tables, key_table);
+/* Option data. */
+RB_HEAD(options_array, options_array_item);
+union options_value {
+ char *string;
+ long long number;
+ struct style style;
+ struct options_array array;
+ struct cmd_list *cmdlist;
+};
+
/* Option table entries. */
enum options_table_type {
OPTIONS_TABLE_STRING,
@@ -1525,20 +1526,24 @@ enum options_table_type {
OPTIONS_TABLE_FLAG,
OPTIONS_TABLE_CHOICE,
OPTIONS_TABLE_STYLE,
- OPTIONS_TABLE_ARRAY,
+ OPTIONS_TABLE_COMMAND
};
enum options_table_scope {
OPTIONS_TABLE_NONE,
OPTIONS_TABLE_SERVER,
OPTIONS_TABLE_SESSION,
- OPTIONS_TABLE_WINDOW,
+ OPTIONS_TABLE_WINDOW
};
+#define OPTIONS_TABLE_IS_ARRAY 0x1
+#define OPTIONS_TABLE_IS_HOOK 0x2
+
struct options_table_entry {
const char *name;
enum options_table_type type;
enum options_table_scope scope;
+ int flags;
u_int minimum;
u_int maximum;
@@ -1563,8 +1568,34 @@ struct options_table_entry {
#define CMD_SRCDST_CLIENT_USAGE "[-s src-client] [-t dst-client]"
#define CMD_BUFFER_USAGE "[-b buffer-name]"
+/* Spawn common context. */
+struct spawn_context {
+ struct cmdq_item *item;
+
+ struct session *s;
+ struct winlink *wl;
+
+ struct window_pane *wp0;
+ struct layout_cell *lc;
+
+ const char *name;
+ char **argv;
+ int argc;
+ struct environ *environ;
+
+ int idx;
+ const char *cwd;
+
+ int flags;
+#define SPAWN_KILL 0x1
+#define SPAWN_DETACHED 0x2
+#define SPAWN_RESPAWN 0x4
+#define SPAWN_BEFORE 0x8
+#define SPAWN_NONOTIFY 0x10
+#define SPAWN_FULLSIZE 0x20
+};
+
/* tmux.c */
-extern struct hooks *global_hooks;
extern struct options *global_options;
extern struct options *global_s_options;
extern struct options *global_w_options;
@@ -1613,7 +1644,7 @@ struct paste_buffer *paste_walk(struct paste_buffer *);
struct paste_buffer *paste_get_top(const char **);
struct paste_buffer *paste_get_name(const char *);
void paste_free(struct paste_buffer *);
-void paste_add(char *, size_t);
+void paste_add(const char *, char *, size_t);
int paste_rename(const char *, const char *, char **);
int paste_set(char *, size_t, const char *, char **);
char *paste_make_sample(struct paste_buffer *);
@@ -1658,20 +1689,6 @@ u_int format_width(const char *);
char *format_trim_left(const char *, u_int);
char *format_trim_right(const char *, u_int);
-/* hooks.c */
-struct hook;
-struct hooks *hooks_get(struct session *);
-struct hooks *hooks_create(struct hooks *);
-void hooks_free(struct hooks *);
-struct hook *hooks_first(struct hooks *);
-struct hook *hooks_next(struct hook *);
-void hooks_add(struct hooks *, const char *, struct cmd_list *);
-void hooks_copy(struct hooks *, struct hooks *);
-void hooks_remove(struct hooks *, const char *);
-struct hook *hooks_find(struct hooks *, const char *);
-void printflike(4, 5) hooks_insert(struct hooks *, struct cmdq_item *,
- struct cmd_find_state *, const char *, ...);
-
/* notify.c */
void notify_hook(struct cmdq_item *, const char *);
void notify_input(struct window_pane *, struct evbuffer *);
@@ -1697,17 +1714,18 @@ struct options_entry *options_get_only(struct options *, const char *);
struct options_entry *options_get(struct options *, const char *);
void options_remove(struct options_entry *);
void options_array_clear(struct options_entry *);
-const char *options_array_get(struct options_entry *, u_int);
+union options_value *options_array_get(struct options_entry *, u_int);
int options_array_set(struct options_entry *, u_int, const char *,
- int);
-void options_array_assign(struct options_entry *, const char *);
+ int, char **);
+int options_array_assign(struct options_entry *, const char *,
+ char **);
struct options_array_item *options_array_first(struct options_entry *);
struct options_array_item *options_array_next(struct options_array_item *);
u_int options_array_item_index(struct options_array_item *);
-const char *options_array_item_value(struct options_array_item *);
+union options_value *options_array_item_value(struct options_array_item *);
int options_isarray(struct options_entry *);
int options_isstring(struct options_entry *);
-const char *options_tostring(struct options_entry *, int, int);
+char *options_tostring(struct options_entry *, int, int);
char *options_parse(const char *, int *);
struct options_entry *options_parse_get(struct options *, const char *, int *,
int);
@@ -1857,6 +1875,8 @@ void args_free(struct args *);
char *args_print(struct args *);
int args_has(struct args *, u_char);
const char *args_get(struct args *, u_char);
+const char *args_first_value(struct args *, u_char, struct args_value **);
+const char *args_next_value(struct args_value **);
long long args_strtonum(struct args *, u_char, long long, long long,
char **);
@@ -1921,6 +1941,8 @@ struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmd_find_state *,
struct cmdq_item *cmdq_get_callback1(const char *, cmdq_cb, void *);
void cmdq_insert_after(struct cmdq_item *, struct cmdq_item *);
void cmdq_append(struct client *, struct cmdq_item *);
+void cmdq_insert_hook(struct session *, struct cmdq_item *,
+ struct cmd_find_state *, const char *, ...);
void printflike(3, 4) cmdq_format(struct cmdq_item *, const char *,
const char *, ...);
u_int cmdq_next(struct client *);
@@ -2234,17 +2256,17 @@ struct window *window_find_by_id_str(const char *);
struct window *window_find_by_id(u_int);
void window_update_activity(struct window *);
struct window *window_create(u_int, u_int);
-struct window *window_create_spawn(const char *, int, char **, const char *,
- const char *, const char *, struct environ *,
- struct termios *, u_int, u_int, u_int, char **);
+void window_destroy(struct window *);
+void window_pane_set_event(struct window_pane *);
struct window_pane *window_get_active_at(struct window *, u_int, u_int);
struct window_pane *window_find_string(struct window *, const char *);
int window_has_pane(struct window *, struct window_pane *);
-int window_set_active_pane(struct window *, struct window_pane *);
+int window_set_active_pane(struct window *, struct window_pane *,
+ int);
void window_redraw_active_switch(struct window *,
struct window_pane *);
-struct window_pane *window_add_pane(struct window *, struct window_pane *, int,
- int, u_int);
+struct window_pane *window_add_pane(struct window *, struct window_pane *,
+ u_int, int);
void window_resize(struct window *, u_int, u_int);
int window_zoom(struct window_pane *);
int window_unzoom(struct window *);
@@ -2261,9 +2283,6 @@ void window_destroy_panes(struct window *);
struct window_pane *window_pane_find_by_id_str(const char *);
struct window_pane *window_pane_find_by_id(u_int);
int window_pane_destroy_ready(struct window_pane *);
-int window_pane_spawn(struct window_pane *, int, char **,
- const char *, const char *, const char *, struct environ *,
- struct termios *, char **);
void window_pane_resize(struct window_pane *, u_int, u_int);
void window_pane_alternate_on(struct window_pane *,
struct grid_cell *, int);
@@ -2321,7 +2340,7 @@ void layout_resize_pane_to(struct window_pane *, enum layout_type,
u_int);
void layout_assign_pane(struct layout_cell *, struct window_pane *);
struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type,
- int, int, int);
+ int, int);
void layout_close_pane(struct window_pane *);
int layout_spread_cell(struct window *, struct layout_cell *);
void layout_spread_out(struct window_pane *);
@@ -2420,10 +2439,9 @@ int session_alive(struct session *);
struct session *session_find(const char *);
struct session *session_find_by_id_str(const char *);
struct session *session_find_by_id(u_int);
-struct session *session_create(const char *, const char *, int, char **,
- const char *, const char *, struct environ *,
- struct options *, struct termios *, int, char **);
-void session_destroy(struct session *, const char *);
+struct session *session_create(const char *, const char *, const char *,
+ struct environ *, struct options *, struct termios *);
+void session_destroy(struct session *, int, const char *);
void session_add_ref(struct session *, const char *);
void session_remove_ref(struct session *, const char *);
int session_check_name(const char *);
@@ -2497,4 +2515,8 @@ void style_set(struct style *, const struct grid_cell *);
void style_copy(struct style *, struct style *);
int style_is_default(struct style *);
+/* spawn.c */
+struct winlink *spawn_window(struct spawn_context *, char **);
+struct window_pane *spawn_pane(struct spawn_context *, char **);
+
#endif /* TMUX_H */
diff --git a/tools/24-bit-color.sh b/tools/24-bit-color.sh
index b83ef51f..3e91da20 100644
--- a/tools/24-bit-color.sh
+++ b/tools/24-bit-color.sh
@@ -14,9 +14,23 @@
# https://github.com/gnachman/iTerm2/blob/master/LICENSE
#
-if which gseq >/dev/null 2>&1
-then
- SEQ=gseq
+SEQ1=
+if which gseq >/dev/null 2>&1; then
+ SEQ1=gseq
+elif seq --version|grep -q GNU; then
+ SEQ1=seq
+fi
+if [ -n "$SEQ1" ]; then
+ # GNU seq requires a -ve increment if going backwards
+ seq1()
+ {
+ if [ $1 -gt $2 ]; then
+ $SEQ1 $1 -1 $2
+ else
+ $SEQ1 $1 $2
+ fi
+ }
+ SEQ=seq1
else
SEQ=seq
fi
diff --git a/tty-keys.c b/tty-keys.c
index da84077b..850c9119 100644
--- a/tty-keys.c
+++ b/tty-keys.c
@@ -399,9 +399,10 @@ tty_keys_build(struct tty *tty)
const struct tty_default_key_raw *tdkr;
const struct tty_default_key_code *tdkc;
u_int i;
- const char *s, *value;
+ const char *s;
struct options_entry *o;
struct options_array_item *a;
+ union options_value *ov;
if (tty->key_tree != NULL)
tty_keys_free(tty);
@@ -427,9 +428,8 @@ tty_keys_build(struct tty *tty)
if (o != NULL) {
a = options_array_first(o);
while (a != NULL) {
- value = options_array_item_value(a);
- if (value != NULL)
- tty_keys_add(tty, value, KEYC_USER + i);
+ ov = options_array_item_value(a);
+ tty_keys_add(tty, ov->string, KEYC_USER + i);
a = options_array_next(a);
}
}
@@ -976,7 +976,7 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len,
/* Create a new paste buffer. */
log_debug("%s: %.*s", __func__, outlen, out);
- paste_add(out, outlen);
+ paste_add(NULL, out, outlen);
return (0);
}
diff --git a/tty-term.c b/tty-term.c
index def0feec..c458752d 100644
--- a/tty-term.c
+++ b/tty-term.c
@@ -420,6 +420,7 @@ tty_term_find(char *name, int fd, char **cause)
struct tty_code *code;
struct options_entry *o;
struct options_array_item *a;
+ union options_value *ov;
u_int i;
int n, error;
const char *s, *acs;
@@ -497,9 +498,8 @@ tty_term_find(char *name, int fd, char **cause)
o = options_get_only(global_options, "terminal-overrides");
a = options_array_first(o);
while (a != NULL) {
- s = options_array_item_value(a);
- if (s != NULL)
- tty_term_override(term, s);
+ ov = options_array_item_value(a);
+ tty_term_override(term, ov->string);
a = options_array_next(a);
}
diff --git a/tty.c b/tty.c
index 9ed2b3a9..cc528e8e 100644
--- a/tty.c
+++ b/tty.c
@@ -2361,7 +2361,7 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc)
if (gc->bg & 8) {
gc->bg &= 7;
if (colours >= 16)
- gc->fg += 90;
+ gc->bg += 90;
}
}
return;
@@ -2388,8 +2388,11 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
/* Is this an aixterm bright colour? */
if (gc->fg >= 90 && gc->fg <= 97) {
- xsnprintf(s, sizeof s, "\033[%dm", gc->fg);
- tty_puts(tty, s);
+ if (tty->term_flags & TERM_256COLOURS) {
+ xsnprintf(s, sizeof s, "\033[%dm", gc->fg);
+ tty_puts(tty, s);
+ } else
+ tty_putcode1(tty, TTYC_SETAF, gc->fg - 90 + 8);
goto save_fg;
}
@@ -2417,8 +2420,11 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
/* Is this an aixterm bright colour? */
if (gc->bg >= 90 && gc->bg <= 97) {
- xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10);
- tty_puts(tty, s);
+ if (tty->term_flags & TERM_256COLOURS) {
+ xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10);
+ tty_puts(tty, s);
+ } else
+ tty_putcode1(tty, TTYC_SETAB, gc->bg - 90 + 8);
goto save_bg;
}
diff --git a/window-buffer.c b/window-buffer.c
index 79b02e9b..07cdd80d 100644
--- a/window-buffer.c
+++ b/window-buffer.c
@@ -66,6 +66,7 @@ struct window_buffer_itemdata {
};
struct window_buffer_modedata {
+ struct cmd_find_state fs;
struct mode_tree_data *data;
char *command;
char *format;
@@ -137,6 +138,9 @@ window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag,
struct paste_buffer *pb;
char *text, *cp;
struct format_tree *ft;
+ struct session *s = NULL;
+ struct winlink *wl = NULL;
+ struct window_pane *wp = NULL;
for (i = 0; i < data->item_size; i++)
window_buffer_free_item(data->item_list[i]);
@@ -167,6 +171,12 @@ window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag,
break;
}
+ if (cmd_find_valid_state(&data->fs)) {
+ s = data->fs.s;
+ wl = data->fs.wl;
+ wp = data->fs.wp;
+ }
+
for (i = 0; i < data->item_size; i++) {
item = data->item_list[i];
@@ -174,6 +184,7 @@ window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag,
if (pb == NULL)
continue;
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
+ format_defaults(ft, NULL, s, wl, wp);
format_defaults_paste_buffer(ft, pb);
if (filter != NULL) {
@@ -253,14 +264,15 @@ window_buffer_search(__unused void *modedata, void *itemdata, const char *ss)
}
static struct screen *
-window_buffer_init(struct window_mode_entry *wme,
- __unused struct cmd_find_state *fs, struct args *args)
+window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
+ struct args *args)
{
struct window_pane *wp = wme->wp;
struct window_buffer_modedata *data;
struct screen *s;
wme->data = data = xcalloc(1, sizeof *data);
+ cmd_find_copy_state(&data->fs, fs);
if (args == NULL || !args_has(args, 'F'))
data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT);
diff --git a/window-copy.c b/window-copy.c
index 062d687f..82b4765c 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -79,11 +79,12 @@ static int window_copy_set_selection(struct window_mode_entry *, int);
static int window_copy_update_selection(struct window_mode_entry *, int);
static void window_copy_synchronize_cursor(struct window_mode_entry *);
static void *window_copy_get_selection(struct window_mode_entry *, size_t *);
-static void window_copy_copy_buffer(struct window_mode_entry *, void *,
- size_t);
+static void window_copy_copy_buffer(struct window_mode_entry *,
+ const char *, void *, size_t);
static void window_copy_copy_pipe(struct window_mode_entry *,
- struct session *, const char *);
-static void window_copy_copy_selection(struct window_mode_entry *);
+ struct session *, const char *, const char *);
+static void window_copy_copy_selection(struct window_mode_entry *,
+ const char *);
static void window_copy_append_selection(struct window_mode_entry *);
static void window_copy_clear_selection(struct window_mode_entry *);
static void window_copy_copy_line(struct window_mode_entry *, char **,
@@ -115,6 +116,7 @@ static void window_copy_scroll_down(struct window_mode_entry *, u_int);
static void window_copy_rectangle_toggle(struct window_mode_entry *);
static void window_copy_move_mouse(struct mouse_event *);
static void window_copy_drag_update(struct client *, struct mouse_event *);
+static void window_copy_drag_release(struct client *, struct mouse_event *);
const struct window_mode window_copy_mode = {
.name = "copy-mode",
@@ -154,6 +156,22 @@ enum {
WINDOW_COPY_REL_POS_BELOW,
};
+enum window_copy_cmd_action {
+ WINDOW_COPY_CMD_NOTHING,
+ WINDOW_COPY_CMD_REDRAW,
+ WINDOW_COPY_CMD_CANCEL,
+};
+
+struct window_copy_cmd_state {
+ struct window_mode_entry *wme;
+ struct args *args;
+ struct mouse_event *m;
+
+ struct client *c;
+ struct session *s;
+ struct winlink *wl;
+};
+
/*
* Copy mode's visible screen (the "screen" field) is filled from one of two
* sources: the original contents of the pane (used when we actually enter via
@@ -205,7 +223,7 @@ struct window_copy_mode_data {
int searchtype;
char *searchstr;
- bitstr_t *searchmark;
+ bitstr_t *searchmark;
u_int searchcount;
int searchthis;
int searchx;
@@ -214,8 +232,35 @@ struct window_copy_mode_data {
int jumptype;
char jumpchar;
+
+ struct event dragtimer;
+#define WINDOW_COPY_DRAG_REPEAT_TIME 50000
};
+static void
+window_copy_scroll_timer(__unused int fd, __unused short events, void *arg)
+{
+ struct window_mode_entry *wme = arg;
+ struct window_pane *wp = wme->wp;
+ struct window_copy_mode_data *data = wme->data;
+ struct timeval tv = {
+ .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME
+ };
+
+ evtimer_del(&data->dragtimer);
+
+ if (TAILQ_FIRST(&wp->modes) != wme)
+ return;
+
+ if (data->cy == 0) {
+ evtimer_add(&data->dragtimer, &tv);
+ window_copy_cursor_up(wme, 1);
+ } else if (data->cy == screen_size_y(&data->screen) - 1) {
+ evtimer_add(&data->dragtimer, &tv);
+ window_copy_cursor_down(wme, 1);
+ }
+}
+
static struct window_copy_mode_data *
window_copy_common_init(struct window_mode_entry *wme)
{
@@ -244,6 +289,8 @@ window_copy_common_init(struct window_mode_entry *wme)
screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0);
data->modekeys = options_get_number(wp->window->options, "mode-keys");
+ evtimer_set(&data->dragtimer, window_copy_scroll_timer, wme);
+
return (data);
}
@@ -302,6 +349,8 @@ window_copy_free(struct window_mode_entry *wme)
struct window_pane *wp = wme->wp;
struct window_copy_mode_data *data = wme->data;
+ evtimer_del(&data->dragtimer);
+
if (wp->fd != -1 && --wp->disabled == 0)
bufferevent_enable(wp->event, EV_READ|EV_WRITE);
@@ -565,451 +614,1325 @@ window_copy_key_table(struct window_mode_entry *wme)
return ("copy-mode");
}
-static void
-window_copy_command(struct window_mode_entry *wme, struct client *c,
- struct session *s, __unused struct winlink *wl, struct args *args,
- struct mouse_event *m)
+static enum window_copy_cmd_action
+window_copy_cmd_append_selection(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct session *s = cs->s;
+
+ if (s != NULL)
+ window_copy_append_selection(wme);
+ window_copy_clear_selection(wme);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_append_selection_and_cancel(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct session *s = cs->s;
+
+ if (s != NULL)
+ window_copy_append_selection(wme);
+ window_copy_clear_selection(wme);
+ return (WINDOW_COPY_CMD_CANCEL);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_back_to_indentation(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+
+ window_copy_cursor_back_to_indentation(wme);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct client *c = cs->c;
+ struct mouse_event *m = cs->m;
+ struct window_copy_mode_data *data = wme->data;
+
+ if (m != NULL) {
+ window_copy_start_drag(c, m);
+ return (WINDOW_COPY_CMD_NOTHING);
+ }
+
+ data->lineflag = LINE_SEL_NONE;
+ window_copy_start_selection(wme);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+
+ data->cursordrag = CURSORDRAG_NONE;
+ data->lineflag = LINE_SEL_NONE;
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+
+ data->cx = 0;
+ data->cy = screen_size_y(&data->screen) - 1;
+
+ window_copy_update_selection(wme, 1);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_cancel(__unused struct window_copy_cmd_state *cs)
+{
+ return (WINDOW_COPY_CMD_CANCEL);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_clear_selection(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+
+ window_copy_clear_selection(wme);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct client *c = cs->c;
+ struct session *s = cs->s;
+ struct winlink *wl = cs->wl;
+ struct window_pane *wp = wme->wp;
+ u_int np = wme->prefix;
+ char *prefix = NULL;
+
+ if (cs->args->argc == 2)
+ prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp);
+
+ window_copy_start_selection(wme);
+ for (; np > 1; np--)
+ window_copy_cursor_down(wme, 0);
+ window_copy_cursor_end_of_line(wme);
+
+ if (s != NULL) {
+ window_copy_copy_selection(wme, prefix);
+
+ free(prefix);
+ return (WINDOW_COPY_CMD_CANCEL);
+ }
+
+ free(prefix);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_copy_line(struct window_copy_cmd_state *cs)
{
+ struct window_mode_entry *wme = cs->wme;
+ struct client *c = cs->c;
+ struct session *s = cs->s;
+ struct winlink *wl = cs->wl;
struct window_pane *wp = wme->wp;
+ u_int np = wme->prefix;
+ char *prefix = NULL;
+
+ if (cs->args->argc == 2)
+ prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp);
+
+ window_copy_cursor_start_of_line(wme);
+ window_copy_start_selection(wme);
+ for (; np > 1; np--)
+ window_copy_cursor_down(wme, 0);
+ window_copy_cursor_end_of_line(wme);
+
+ if (s != NULL) {
+ window_copy_copy_selection(wme, prefix);
+
+ free(prefix);
+ return (WINDOW_COPY_CMD_CANCEL);
+ }
+
+ free(prefix);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct client *c = cs->c;
+ struct session *s = cs->s;
+ struct winlink *wl = cs->wl;
+ struct window_pane *wp = wme->wp;
+ char *prefix = NULL;
+
+ if (cs->args->argc == 2)
+ prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp);
+
+ if (s != NULL)
+ window_copy_copy_selection(wme, prefix);
+
+ free(prefix);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_copy_selection(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+
+ window_copy_cmd_copy_selection_no_clear(cs);
+ window_copy_clear_selection(wme);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+
+ window_copy_cmd_copy_selection_no_clear(cs);
+ window_copy_clear_selection(wme);
+ return (WINDOW_COPY_CMD_CANCEL);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--)
+ window_copy_cursor_down(wme, 0);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_cursor_left(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--)
+ window_copy_cursor_left(wme);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--)
+ window_copy_cursor_right(wme);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--)
+ window_copy_cursor_up(wme, 0);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_end_of_line(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+
+ window_copy_cursor_end_of_line(wme);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_halfpage_down(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data;
- struct screen *sn = &data->screen;
- const char *command, *argument, *ws;
u_int np = wme->prefix;
- int cancel = 0, redraw = 0, scroll_exit;
- char prefix;
- if (args->argc == 0)
- return;
- command = args->argv[0];
+ for (; np != 0; np--) {
+ if (window_copy_pagedown(wme, 1, data->scroll_exit))
+ return (WINDOW_COPY_CMD_CANCEL);
+ }
+ return (WINDOW_COPY_CMD_NOTHING);
+}
- if (m != NULL && m->valid && !MOUSE_WHEEL(m->b))
- window_copy_move_mouse(m);
+static enum window_copy_cmd_action
+window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state *cs)
+{
- if (args->argc == 1) {
- if (strcmp(command, "append-selection") == 0) {
- if (s != NULL)
- window_copy_append_selection(wme);
- window_copy_clear_selection(wme);
- redraw = 1;
- }
- if (strcmp(command, "append-selection-and-cancel") == 0) {
- if (s != NULL)
- window_copy_append_selection(wme);
- window_copy_clear_selection(wme);
- redraw = 1;
- cancel = 1;
- }
- if (strcmp(command, "back-to-indentation") == 0)
- window_copy_cursor_back_to_indentation(wme);
- if (strcmp(command, "begin-selection") == 0) {
- if (m != NULL)
- window_copy_start_drag(c, m);
- else {
- data->lineflag = LINE_SEL_NONE;
- window_copy_start_selection(wme);
- redraw = 1;
- }
- }
- if (strcmp(command, "stop-selection") == 0)
- data->cursordrag = CURSORDRAG_NONE;
- if (strcmp(command, "bottom-line") == 0) {
- data->cx = 0;
- data->cy = screen_size_y(sn) - 1;
- window_copy_update_selection(wme, 1);
- redraw = 1;
- }
- if (strcmp(command, "cancel") == 0)
- cancel = 1;
- if (strcmp(command, "clear-selection") == 0) {
- window_copy_clear_selection(wme);
- redraw = 1;
- }
- if (strcmp(command, "copy-end-of-line") == 0) {
- window_copy_start_selection(wme);
- for (; np > 1; np--)
- window_copy_cursor_down(wme, 0);
- window_copy_cursor_end_of_line(wme);
- redraw = 1;
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
- if (s != NULL) {
- window_copy_copy_selection(wme);
- cancel = 1;
- }
- }
- if (strcmp(command, "copy-line") == 0) {
- window_copy_cursor_start_of_line(wme);
- window_copy_start_selection(wme);
- for (; np > 1; np--)
- window_copy_cursor_down(wme, 0);
- window_copy_cursor_end_of_line(wme);
- redraw = 1;
+ for (; np != 0; np--) {
+ if (window_copy_pagedown(wme, 1, 1))
+ return (WINDOW_COPY_CMD_CANCEL);
+ }
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_halfpage_up(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--)
+ window_copy_pageup1(wme, 1);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+
+ data->cx = 0;
+ data->cy = screen_size_y(&data->screen) - 1;
+ data->oy = 0;
+
+ window_copy_update_selection(wme, 1);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_history_top(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+
+ data->cx = 0;
+ data->cy = 0;
+ data->oy = screen_hsize(data->backing);
+
+ window_copy_update_selection(wme, 1);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_jump_again(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int np = wme->prefix;
+
+ switch (data->jumptype) {
+ case WINDOW_COPY_JUMPFORWARD:
+ for (; np != 0; np--)
+ window_copy_cursor_jump(wme);
+ break;
+ case WINDOW_COPY_JUMPBACKWARD:
+ for (; np != 0; np--)
+ window_copy_cursor_jump_back(wme);
+ break;
+ case WINDOW_COPY_JUMPTOFORWARD:
+ for (; np != 0; np--)
+ window_copy_cursor_jump_to(wme);
+ break;
+ case WINDOW_COPY_JUMPTOBACKWARD:
+ for (; np != 0; np--)
+ window_copy_cursor_jump_to_back(wme);
+ break;
+ }
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int np = wme->prefix;
- if (s != NULL) {
- window_copy_copy_selection(wme);
- cancel = 1;
+ switch (data->jumptype) {
+ case WINDOW_COPY_JUMPFORWARD:
+ for (; np != 0; np--)
+ window_copy_cursor_jump_back(wme);
+ break;
+ case WINDOW_COPY_JUMPBACKWARD:
+ for (; np != 0; np--)
+ window_copy_cursor_jump(wme);
+ break;
+ case WINDOW_COPY_JUMPTOFORWARD:
+ for (; np != 0; np--)
+ window_copy_cursor_jump_to_back(wme);
+ break;
+ case WINDOW_COPY_JUMPTOBACKWARD:
+ for (; np != 0; np--)
+ window_copy_cursor_jump_to(wme);
+ break;
+ }
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_middle_line(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+
+ data->cx = 0;
+ data->cy = (screen_size_y(&data->screen) - 1) / 2;
+
+ window_copy_update_selection(wme, 1);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+ struct window_copy_mode_data *data = wme->data;
+ struct screen *s = data->backing;
+ char open[] = "{[(", close[] = "}])";
+ char tried, found, start, *cp;
+ u_int px, py, xx, yy, n;
+ struct grid_cell gc;
+ int failed;
+
+ for (; np != 0; np--) {
+ /* Get cursor position and line length. */
+ px = data->cx;
+ py = screen_hsize(s) + data->cy - data->oy;
+ xx = window_copy_find_length(wme, py);
+ yy = screen_hsize(s) + screen_size_y(s) - 1;
+ if (xx == 0)
+ break;
+
+ /*
+ * Get the current character. If not on a bracket, try the
+ * previous. If still not, then behave like previous-word.
+ */
+ tried = 0;
+ retry:
+ grid_get_cell(s->grid, px, py, &gc);
+ if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
+ cp = NULL;
+ else {
+ found = *gc.data.data;
+ cp = strchr(close, found);
+ }
+ if (cp == NULL) {
+ if (data->modekeys == MODEKEY_EMACS) {
+ if (!tried && px > 0) {
+ px--;
+ tried = 1;
+ goto retry;
+ }
+ window_copy_cursor_previous_word(wme, "}]) ");
+ px = data->cx;
+ continue;
}
+ continue;
}
- if (strcmp(command, "copy-selection") == 0) {
- if (s != NULL)
- window_copy_copy_selection(wme);
- window_copy_clear_selection(wme);
- redraw = 1;
- }
- if (strcmp(command, "copy-selection-and-cancel") == 0) {
- if (s != NULL)
- window_copy_copy_selection(wme);
- window_copy_clear_selection(wme);
- redraw = 1;
- cancel = 1;
- }
- if (strcmp(command, "cursor-down") == 0) {
- for (; np != 0; np--)
- window_copy_cursor_down(wme, 0);
- }
- if (strcmp(command, "cursor-left") == 0) {
- for (; np != 0; np--)
- window_copy_cursor_left(wme);
- }
- if (strcmp(command, "cursor-right") == 0) {
- for (; np != 0; np--)
- window_copy_cursor_right(wme);
- }
- if (strcmp(command, "cursor-up") == 0) {
- for (; np != 0; np--)
- window_copy_cursor_up(wme, 0);
- }
- if (strcmp(command, "end-of-line") == 0)
- window_copy_cursor_end_of_line(wme);
- if (strcmp(command, "halfpage-down") == 0 ||
- strcmp(command, "halfpage-down-and-cancel") == 0) {
- if (strcmp(command, "halfpage-down-and-cancel") == 0)
- scroll_exit = 1;
- else
- scroll_exit = data->scroll_exit;
- for (; np != 0; np--) {
- if (window_copy_pagedown(wme, 1, scroll_exit)) {
- cancel = 1;
+ start = open[cp - close];
+
+ /* Walk backward until the matching bracket is reached. */
+ n = 1;
+ failed = 0;
+ do {
+ if (px == 0) {
+ if (py == 0) {
+ failed = 1;
break;
}
+ do {
+ py--;
+ xx = window_copy_find_length(wme, py);
+ } while (xx == 0 && py > 0);
+ if (xx == 0 && py == 0) {
+ failed = 1;
+ break;
+ }
+ px = xx - 1;
+ } else
+ px--;
+
+ grid_get_cell(s->grid, px, py, &gc);
+ if (gc.data.size == 1 &&
+ (~gc.flags & GRID_FLAG_PADDING)) {
+ if (*gc.data.data == found)
+ n++;
+ else if (*gc.data.data == start)
+ n--;
}
- }
- if (strcmp(command, "halfpage-up") == 0) {
- for (; np != 0; np--)
- window_copy_pageup1(wme, 1);
- }
- if (strcmp(command, "history-bottom") == 0) {
- data->cx = 0;
- data->cy = screen_size_y(sn) - 1;
- data->oy = 0;
- window_copy_update_selection(wme, 1);
- redraw = 1;
- }
- if (strcmp(command, "history-top") == 0) {
- data->cx = 0;
- data->cy = 0;
- data->oy = screen_hsize(data->backing);
- window_copy_update_selection(wme, 1);
- redraw = 1;
- }
- if (strcmp(command, "jump-again") == 0) {
- switch (data->jumptype) {
- case WINDOW_COPY_JUMPFORWARD:
- for (; np != 0; np--)
- window_copy_cursor_jump(wme);
- break;
- case WINDOW_COPY_JUMPBACKWARD:
- for (; np != 0; np--)
- window_copy_cursor_jump_back(wme);
- break;
- case WINDOW_COPY_JUMPTOFORWARD:
- for (; np != 0; np--)
- window_copy_cursor_jump_to(wme);
- break;
- case WINDOW_COPY_JUMPTOBACKWARD:
- for (; np != 0; np--)
- window_copy_cursor_jump_to_back(wme);
+ } while (n != 0);
+
+ /* Move the cursor to the found location if any. */
+ if (!failed)
+ window_copy_scroll_to(wme, px, py);
+ }
+
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+
+static enum window_copy_cmd_action
+window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+ struct window_copy_mode_data *data = wme->data;
+ struct screen *s = data->backing;
+ char open[] = "{[(", close[] = "}])";
+ char tried, found, end, *cp;
+ u_int px, py, xx, yy, sx, sy, n;
+ struct grid_cell gc;
+ int failed;
+ struct grid_line *gl;
+
+ for (; np != 0; np--) {
+ /* Get cursor position and line length. */
+ px = data->cx;
+ py = screen_hsize(s) + data->cy - data->oy;
+ xx = window_copy_find_length(wme, py);
+ yy = screen_hsize(s) + screen_size_y(s) - 1;
+ if (xx == 0)
+ break;
+
+ /*
+ * Get the current character. If not on a bracket, try the
+ * next. If still not, then behave like next-word.
+ */
+ tried = 0;
+ retry:
+ grid_get_cell(s->grid, px, py, &gc);
+ if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
+ cp = NULL;
+ else {
+ found = *gc.data.data;
+
+ /*
+ * In vi mode, attempt to move to previous bracket if a
+ * closing bracket is found first. If this fails,
+ * return to the original cursor position.
+ */
+ cp = strchr(close, found);
+ if (cp != NULL && data->modekeys == MODEKEY_VI) {
+ sx = data->cx;
+ sy = screen_hsize(s) + data->cy - data->oy;
+
+ window_copy_scroll_to(wme, px, py);
+ window_copy_cmd_previous_matching_bracket(cs);
+
+ px = data->cx;
+ py = screen_hsize(s) + data->cy - data->oy;
+ grid_get_cell(s->grid, px, py, &gc);
+ if (gc.data.size != 1 ||
+ (gc.flags & GRID_FLAG_PADDING) ||
+ strchr(close, *gc.data.data) == NULL)
+ window_copy_scroll_to(wme, sx, sy);
break;
}
+
+ cp = strchr(open, found);
}
- if (strcmp(command, "jump-reverse") == 0) {
- switch (data->jumptype) {
- case WINDOW_COPY_JUMPFORWARD:
- for (; np != 0; np--)
- window_copy_cursor_jump_back(wme);
- break;
- case WINDOW_COPY_JUMPBACKWARD:
- for (; np != 0; np--)
- window_copy_cursor_jump(wme);
- break;
- case WINDOW_COPY_JUMPTOFORWARD:
- for (; np != 0; np--)
- window_copy_cursor_jump_to_back(wme);
- break;
- case WINDOW_COPY_JUMPTOBACKWARD:
- for (; np != 0; np--)
- window_copy_cursor_jump_to(wme);
- break;
+ if (cp == NULL) {
+ if (data->modekeys == MODEKEY_EMACS) {
+ if (!tried && px <= xx) {
+ px++;
+ tried = 1;
+ goto retry;
+ }
+ window_copy_cursor_next_word_end(wme, "{[( ");
+ px = data->cx;
+ continue;
}
+ /* For vi, continue searching for bracket until EOL. */
+ if (px > xx) {
+ if (py == yy)
+ continue;
+ gl = grid_get_line(s->grid, py);
+ if (~gl->flags & GRID_LINE_WRAPPED)
+ continue;
+ if (gl->cellsize > s->grid->sx)
+ continue;
+ px = 0;
+ py++;
+ xx = window_copy_find_length(wme, py);
+ } else
+ px++;
+ goto retry;
}
- if (strcmp(command, "middle-line") == 0) {
- data->cx = 0;
- data->cy = (screen_size_y(sn) - 1) / 2;
- window_copy_update_selection(wme, 1);
- redraw = 1;
- }
- if (strcmp(command, "next-paragraph") == 0) {
- for (; np != 0; np--)
- window_copy_next_paragraph(wme);
- }
- if (strcmp(command, "next-space") == 0) {
- for (; np != 0; np--)
- window_copy_cursor_next_word(wme, " ");
- }
- if (strcmp(command, "next-space-end") == 0) {
- for (; np != 0; np--)
- window_copy_cursor_next_word_end(wme, " ");
- }
- if (strcmp(command, "next-word") == 0) {
- ws = options_get_string(s->options, "word-separators");
- for (; np != 0; np--)
- window_copy_cursor_next_word(wme, ws);
- }
- if (strcmp(command, "next-word-end") == 0) {
- ws = options_get_string(s->options, "word-separators");
- for (; np != 0; np--)
- window_copy_cursor_next_word_end(wme, ws);
- }
- if (strcmp(command, "other-end") == 0) {
- if ((np % 2) != 0)
- window_copy_other_end(wme);
- }
- if (strcmp(command, "page-down") == 0 ||
- strcmp(command, "page-down-and-cancel") == 0) {
- if (strcmp(command, "page-down-and-cancel") == 0)
- scroll_exit = 1;
- else
- scroll_exit = data->scroll_exit;
- for (; np != 0; np--) {
- if (window_copy_pagedown(wme, 0, scroll_exit)) {
- cancel = 1;
+ end = close[cp - open];
+
+ /* Walk forward until the matching bracket is reached. */
+ n = 1;
+ failed = 0;
+ do {
+ if (px > xx) {
+ if (py == yy) {
+ failed = 1;
break;
}
+ px = 0;
+ py++;
+ xx = window_copy_find_length(wme, py);
+ } else
+ px++;
+
+ grid_get_cell(s->grid, px, py, &gc);
+ if (gc.data.size == 1 &&
+ (~gc.flags & GRID_FLAG_PADDING)) {
+ if (*gc.data.data == found)
+ n++;
+ else if (*gc.data.data == end)
+ n--;
}
+ } while (n != 0);
+
+ /* Move the cursor to the found location if any. */
+ if (!failed)
+ window_copy_scroll_to(wme, px, py);
+ }
+
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--)
+ window_copy_next_paragraph(wme);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_next_space(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--)
+ window_copy_cursor_next_word(wme, " ");
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--)
+ window_copy_cursor_next_word_end(wme, " ");
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_next_word(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct session *s = cs->s;
+ u_int np = wme->prefix;
+ const char *ws;
+
+ ws = options_get_string(s->options, "word-separators");
+ for (; np != 0; np--)
+ window_copy_cursor_next_word(wme, ws);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct session *s = cs->s;
+ u_int np = wme->prefix;
+ const char *ws;
+
+ ws = options_get_string(s->options, "word-separators");
+ for (; np != 0; np--)
+ window_copy_cursor_next_word_end(wme, ws);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_other_end(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+
+ if ((np % 2) != 0)
+ window_copy_other_end(wme);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_page_down(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--) {
+ if (window_copy_pagedown(wme, 0, data->scroll_exit))
+ return (WINDOW_COPY_CMD_CANCEL);
+ }
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--) {
+ if (window_copy_pagedown(wme, 0, 1))
+ return (WINDOW_COPY_CMD_CANCEL);
+ }
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_page_up(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--)
+ window_copy_pageup1(wme, 0);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_previous_paragraph(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--)
+ window_copy_previous_paragraph(wme);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_previous_space(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--)
+ window_copy_cursor_previous_word(wme, " ");
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_previous_word(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct session *s = cs->s;
+ u_int np = wme->prefix;
+ const char *ws;
+
+ ws = options_get_string(s->options, "word-separators");
+ for (; np != 0; np--)
+ window_copy_cursor_previous_word(wme, ws);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+
+ data->lineflag = LINE_SEL_NONE;
+ window_copy_rectangle_toggle(wme);
+
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--)
+ window_copy_cursor_down(wme, 1);
+ if (data->scroll_exit && data->oy == 0)
+ return (WINDOW_COPY_CMD_CANCEL);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--)
+ window_copy_cursor_down(wme, 1);
+ if (data->oy == 0)
+ return (WINDOW_COPY_CMD_CANCEL);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ u_int np = wme->prefix;
+
+ for (; np != 0; np--)
+ window_copy_cursor_up(wme, 1);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_search_again(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int np = wme->prefix;
+
+ if (data->searchtype == WINDOW_COPY_SEARCHUP) {
+ for (; np != 0; np--)
+ window_copy_search_up(wme);
+ } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
+ for (; np != 0; np--)
+ window_copy_search_down(wme);
+ }
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int np = wme->prefix;
+
+ if (data->searchtype == WINDOW_COPY_SEARCHUP) {
+ for (; np != 0; np--)
+ window_copy_search_down(wme);
+ } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
+ for (; np != 0; np--)
+ window_copy_search_up(wme);
+ }
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_select_line(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int np = wme->prefix;
+
+ data->lineflag = LINE_SEL_LEFT_RIGHT;
+ data->rectflag = 0;
+
+ window_copy_cursor_start_of_line(wme);
+ window_copy_start_selection(wme);
+ for (; np > 1; np--)
+ window_copy_cursor_down(wme, 0);
+ window_copy_cursor_end_of_line(wme);
+
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_select_word(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct session *s = cs->s;
+ struct window_copy_mode_data *data = wme->data;
+ const char *ws;
+
+ data->lineflag = LINE_SEL_LEFT_RIGHT;
+ data->rectflag = 0;
+
+ ws = options_get_string(s->options, "word-separators");
+ window_copy_cursor_previous_word(wme, ws);
+ window_copy_start_selection(wme);
+ window_copy_cursor_next_word_end(wme, ws);
+
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+
+ window_copy_cursor_start_of_line(wme);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_top_line(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+
+ data->cx = 0;
+ data->cy = 0;
+
+ window_copy_update_selection(wme, 1);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct client *c = cs->c;
+ struct session *s = cs->s;
+ struct winlink *wl = cs->wl;
+ struct window_pane *wp = wme->wp;
+ char *command = NULL;
+ char *prefix = NULL;
+
+ if (cs->args->argc == 3)
+ prefix = format_single(NULL, cs->args->argv[2], c, s, wl, wp);
+
+ if (s != NULL && *cs->args->argv[1] != '\0') {
+ command = format_single(NULL, cs->args->argv[1], c, s, wl, wp);
+ window_copy_copy_pipe(wme, s, prefix, command);
+ free(command);
+ }
+
+ free(prefix);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_copy_pipe(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+
+ window_copy_cmd_copy_pipe_no_clear(cs);
+ window_copy_clear_selection(wme);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+
+ window_copy_cmd_copy_pipe_no_clear(cs);
+ window_copy_clear_selection(wme);
+ return (WINDOW_COPY_CMD_CANCEL);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_goto_line(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ const char *argument = cs->args->argv[1];
+
+ if (*argument != '\0')
+ window_copy_goto_line(wme, argument);
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int np = wme->prefix;
+ const char *argument = cs->args->argv[1];
+
+ if (*argument != '\0') {
+ data->jumptype = WINDOW_COPY_JUMPBACKWARD;
+ data->jumpchar = *argument;
+ for (; np != 0; np--)
+ window_copy_cursor_jump_back(wme);
+ }
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int np = wme->prefix;
+ const char *argument = cs->args->argv[1];
+
+ if (*argument != '\0') {
+ data->jumptype = WINDOW_COPY_JUMPFORWARD;
+ data->jumpchar = *argument;
+ for (; np != 0; np--)
+ window_copy_cursor_jump(wme);
+ }
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int np = wme->prefix;
+ const char *argument = cs->args->argv[1];
+
+ if (*argument != '\0') {
+ data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
+ data->jumpchar = *argument;
+ for (; np != 0; np--)
+ window_copy_cursor_jump_to_back(wme);
+ }
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int np = wme->prefix;
+ const char *argument = cs->args->argv[1];
+
+ if (*argument != '\0') {
+ data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
+ data->jumpchar = *argument;
+ for (; np != 0; np--)
+ window_copy_cursor_jump_to(wme);
+ }
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_search_backward(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int np = wme->prefix;
+ const char *argument = cs->args->argv[1];
+
+ if (*argument != '\0') {
+ data->searchtype = WINDOW_COPY_SEARCHUP;
+ free(data->searchstr);
+ data->searchstr = xstrdup(argument);
+ for (; np != 0; np--)
+ window_copy_search_up(wme);
+ }
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_search_forward(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ u_int np = wme->prefix;
+ const char *argument = cs->args->argv[1];
+
+ if (*argument != '\0') {
+ data->searchtype = WINDOW_COPY_SEARCHDOWN;
+ free(data->searchstr);
+ data->searchstr = xstrdup(argument);
+ for (; np != 0; np--)
+ window_copy_search_down(wme);
+ }
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ const char *argument = cs->args->argv[1];
+ enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING;
+ const char *ss = data->searchstr;
+
+ if (data->searchx == -1 || data->searchy == -1) {
+ data->searchx = data->cx;
+ data->searchy = data->cy;
+ data->searcho = data->oy;
+ } else if (ss != NULL && strcmp(argument, ss) != 0) {
+ data->cx = data->searchx;
+ data->cy = data->searchy;
+ data->oy = data->searcho;
+ action = WINDOW_COPY_CMD_REDRAW;
+ }
+
+ if (*argument == '\0') {
+ window_copy_clear_marks(wme);
+ return (WINDOW_COPY_CMD_REDRAW);
+ }
+
+ switch (*argument++) {
+ case '=':
+ case '-':
+ data->searchtype = WINDOW_COPY_SEARCHUP;
+ free(data->searchstr);
+ data->searchstr = xstrdup(argument);
+ if (!window_copy_search_up(wme)) {
+ window_copy_clear_marks(wme);
+ return (WINDOW_COPY_CMD_REDRAW);
}
- if (strcmp(command, "page-up") == 0) {
- for (; np != 0; np--)
- window_copy_pageup1(wme, 0);
- }
- if (strcmp(command, "previous-paragraph") == 0) {
- for (; np != 0; np--)
- window_copy_previous_paragraph(wme);
- }
- if (strcmp(command, "previous-space") == 0) {
- for (; np != 0; np--)
- window_copy_cursor_previous_word(wme, " ");
- }
- if (strcmp(command, "previous-word") == 0) {
- ws = options_get_string(s->options, "word-separators");
- for (; np != 0; np--)
- window_copy_cursor_previous_word(wme, ws);
- }
- if (strcmp(command, "rectangle-toggle") == 0) {
- data->lineflag = LINE_SEL_NONE;
- window_copy_rectangle_toggle(wme);
- }
- if (strcmp(command, "scroll-down") == 0 ||
- strcmp(command, "scroll-down-and-cancel") == 0) {
- if (strcmp(command, "scroll-down-and-cancel") == 0)
- scroll_exit = 1;
- else
- scroll_exit = data->scroll_exit;
- for (; np != 0; np--)
- window_copy_cursor_down(wme, 1);
- if (scroll_exit && data->oy == 0)
- cancel = 1;
- }
- if (strcmp(command, "scroll-up") == 0) {
- for (; np != 0; np--)
- window_copy_cursor_up(wme, 1);
- }
- if (strcmp(command, "search-again") == 0) {
- if (data->searchtype == WINDOW_COPY_SEARCHUP) {
- for (; np != 0; np--)
- window_copy_search_up(wme);
- } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
- for (; np != 0; np--)
- window_copy_search_down(wme);
- }
- }
- if (strcmp(command, "search-reverse") == 0) {
- if (data->searchtype == WINDOW_COPY_SEARCHUP) {
- for (; np != 0; np--)
- window_copy_search_down(wme);
- } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
- for (; np != 0; np--)
- window_copy_search_up(wme);
- }
- }
- if (strcmp(command, "select-line") == 0) {
- data->lineflag = LINE_SEL_LEFT_RIGHT;
- data->rectflag = 0;
- window_copy_cursor_start_of_line(wme);
- window_copy_start_selection(wme);
- for (; np > 1; np--)
- window_copy_cursor_down(wme, 0);
- window_copy_cursor_end_of_line(wme);
- redraw = 1;
- }
- if (strcmp(command, "select-word") == 0) {
- data->lineflag = LINE_SEL_LEFT_RIGHT;
- data->rectflag = 0;
- ws = options_get_string(s->options, "word-separators");
- window_copy_cursor_previous_word(wme, ws);
- window_copy_start_selection(wme);
- window_copy_cursor_next_word_end(wme, ws);
- redraw = 1;
- }
- if (strcmp(command, "start-of-line") == 0)
- window_copy_cursor_start_of_line(wme);
- if (strcmp(command, "top-line") == 0) {
- data->cx = 0;
- data->cy = 0;
- window_copy_update_selection(wme, 1);
- redraw = 1;
- }
- } else if (args->argc == 2 && *args->argv[1] != '\0') {
- argument = args->argv[1];
- if (strcmp(command, "copy-pipe") == 0) {
- if (s != NULL)
- window_copy_copy_pipe(wme, s, argument);
- }
- if (strcmp(command, "copy-pipe-and-cancel") == 0) {
- if (s != NULL) {
- window_copy_copy_pipe(wme, s, argument);
- cancel = 1;
- }
- }
- if (strcmp(command, "goto-line") == 0)
- window_copy_goto_line(wme, argument);
- if (strcmp(command, "jump-backward") == 0) {
- data->jumptype = WINDOW_COPY_JUMPBACKWARD;
- data->jumpchar = *argument;
- for (; np != 0; np--)
- window_copy_cursor_jump_back(wme);
- }
- if (strcmp(command, "jump-forward") == 0) {
- data->jumptype = WINDOW_COPY_JUMPFORWARD;
- data->jumpchar = *argument;
- for (; np != 0; np--)
- window_copy_cursor_jump(wme);
- }
- if (strcmp(command, "jump-to-backward") == 0) {
- data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
- data->jumpchar = *argument;
- for (; np != 0; np--)
- window_copy_cursor_jump_to_back(wme);
- }
- if (strcmp(command, "jump-to-forward") == 0) {
- data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
- data->jumpchar = *argument;
- for (; np != 0; np--)
- window_copy_cursor_jump_to(wme);
- }
- if (strcmp(command, "search-backward") == 0) {
- data->searchtype = WINDOW_COPY_SEARCHUP;
- free(data->searchstr);
- data->searchstr = xstrdup(argument);
- for (; np != 0; np--)
- window_copy_search_up(wme);
+ break;
+ case '+':
+ data->searchtype = WINDOW_COPY_SEARCHDOWN;
+ free(data->searchstr);
+ data->searchstr = xstrdup(argument);
+ if (!window_copy_search_down(wme)) {
+ window_copy_clear_marks(wme);
+ return (WINDOW_COPY_CMD_REDRAW);
}
- if (strcmp(command, "search-forward") == 0) {
- data->searchtype = WINDOW_COPY_SEARCHDOWN;
- free(data->searchstr);
- data->searchstr = xstrdup(argument);
- for (; np != 0; np--)
- window_copy_search_down(wme);
+ break;
+ }
+ return (action);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+ const char *argument = cs->args->argv[1];
+ enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING;
+ const char *ss = data->searchstr;
+
+ if (data->searchx == -1 || data->searchy == -1) {
+ data->searchx = data->cx;
+ data->searchy = data->cy;
+ data->searcho = data->oy;
+ } else if (ss != NULL && strcmp(argument, ss) != 0) {
+ data->cx = data->searchx;
+ data->cy = data->searchy;
+ data->oy = data->searcho;
+ action = WINDOW_COPY_CMD_REDRAW;
+ }
+
+ if (*argument == '\0') {
+ window_copy_clear_marks(wme);
+ return (WINDOW_COPY_CMD_REDRAW);
+ }
+
+ switch (*argument++) {
+ case '=':
+ case '+':
+ data->searchtype = WINDOW_COPY_SEARCHDOWN;
+ free(data->searchstr);
+ data->searchstr = xstrdup(argument);
+ if (!window_copy_search_down(wme)) {
+ window_copy_clear_marks(wme);
+ return (WINDOW_COPY_CMD_REDRAW);
}
- if (strcmp(command, "search-backward-incremental") == 0) {
- prefix = *argument++;
- if (data->searchx == -1 || data->searchy == -1) {
- data->searchx = data->cx;
- data->searchy = data->cy;
- data->searcho = data->oy;
- } else if (data->searchstr != NULL &&
- strcmp(argument, data->searchstr) != 0) {
- data->cx = data->searchx;
- data->cy = data->searchy;
- data->oy = data->searcho;
- redraw = 1;
- }
- if (*argument == '\0') {
- window_copy_clear_marks(wme);
- redraw = 1;
- } else if (prefix == '=' || prefix == '-') {
- data->searchtype = WINDOW_COPY_SEARCHUP;
- free(data->searchstr);
- data->searchstr = xstrdup(argument);
- if (!window_copy_search_up(wme)) {
- window_copy_clear_marks(wme);
- redraw = 1;
- }
- } else if (prefix == '+') {
- data->searchtype = WINDOW_COPY_SEARCHDOWN;
- free(data->searchstr);
- data->searchstr = xstrdup(argument);
- if (!window_copy_search_down(wme)) {
- window_copy_clear_marks(wme);
- redraw = 1;
- }
- }
+ break;
+ case '-':
+ data->searchtype = WINDOW_COPY_SEARCHUP;
+ free(data->searchstr);
+ data->searchstr = xstrdup(argument);
+ if (!window_copy_search_up(wme)) {
+ window_copy_clear_marks(wme);
+ return (WINDOW_COPY_CMD_REDRAW);
}
- if (strcmp(command, "search-forward-incremental") == 0) {
- prefix = *argument++;
- if (data->searchx == -1 || data->searchy == -1) {
- data->searchx = data->cx;
- data->searchy = data->cy;
- data->searcho = data->oy;
- } else if (data->searchstr != NULL &&
- strcmp(argument, data->searchstr) != 0) {
- data->cx = data->searchx;
- data->cy = data->searchy;
- data->oy = data->searcho;
- redraw = 1;
- }
- if (*argument == '\0') {
- window_copy_clear_marks(wme);
- redraw = 1;
- } else if (prefix == '=' || prefix == '+') {
- data->searchtype = WINDOW_COPY_SEARCHDOWN;
- free(data->searchstr);
- data->searchstr = xstrdup(argument);
- if (!window_copy_search_down(wme)) {
- window_copy_clear_marks(wme);
- redraw = 1;
- }
- } else if (prefix == '-') {
- data->searchtype = WINDOW_COPY_SEARCHUP;
- free(data->searchstr);
- data->searchstr = xstrdup(argument);
- if (!window_copy_search_up(wme)) {
- window_copy_clear_marks(wme);
- redraw = 1;
- }
- }
+ }
+ return (action);
+}
+
+static const struct {
+ const char *command;
+ int minargs;
+ int maxargs;
+ enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *);
+} window_copy_cmd_table[] = {
+ { "append-selection", 0, 0,
+ window_copy_cmd_append_selection },
+ { "append-selection-and-cancel", 0, 0,
+ window_copy_cmd_append_selection_and_cancel },
+ { "back-to-indentation", 0, 0,
+ window_copy_cmd_back_to_indentation },
+ { "begin-selection", 0, 0,
+ window_copy_cmd_begin_selection },
+ { "bottom-line", 0, 0,
+ window_copy_cmd_bottom_line },
+ { "cancel", 0, 0,
+ window_copy_cmd_cancel },
+ { "clear-selection", 0, 0,
+ window_copy_cmd_clear_selection },
+ { "copy-end-of-line", 0, 1,
+ window_copy_cmd_copy_end_of_line },
+ { "copy-line", 0, 1,
+ window_copy_cmd_copy_line },
+ { "copy-pipe-no-clear", 1, 2,
+ window_copy_cmd_copy_pipe_no_clear },
+ { "copy-pipe", 1, 2,
+ window_copy_cmd_copy_pipe },
+ { "copy-pipe-and-cancel", 1, 2,
+ window_copy_cmd_copy_pipe_and_cancel },
+ { "copy-selection-no-clear", 0, 1,
+ window_copy_cmd_copy_selection_no_clear },
+ { "copy-selection", 0, 1,
+ window_copy_cmd_copy_selection },
+ { "copy-selection-and-cancel", 0, 1,
+ window_copy_cmd_copy_selection_and_cancel },
+ { "cursor-down", 0, 0,
+ window_copy_cmd_cursor_down },
+ { "cursor-left", 0, 0,
+ window_copy_cmd_cursor_left },
+ { "cursor-right", 0, 0,
+ window_copy_cmd_cursor_right },
+ { "cursor-up", 0, 0,
+ window_copy_cmd_cursor_up },
+ { "end-of-line", 0, 0,
+ window_copy_cmd_end_of_line },
+ { "goto-line", 1, 1,
+ window_copy_cmd_goto_line },
+ { "halfpage-down", 0, 0,
+ window_copy_cmd_halfpage_down },
+ { "halfpage-down-and-cancel", 0, 0,
+ window_copy_cmd_halfpage_down_and_cancel },
+ { "halfpage-up", 0, 0,
+ window_copy_cmd_halfpage_up },
+ { "history-bottom", 0, 0,
+ window_copy_cmd_history_bottom },
+ { "history-top", 0, 0,
+ window_copy_cmd_history_top },
+ { "jump-again", 0, 0,
+ window_copy_cmd_jump_again },
+ { "jump-backward", 1, 1,
+ window_copy_cmd_jump_backward },
+ { "jump-forward", 1, 1,
+ window_copy_cmd_jump_forward },
+ { "jump-reverse", 0, 0,
+ window_copy_cmd_jump_reverse },
+ { "jump-to-backward", 1, 1,
+ window_copy_cmd_jump_to_backward },
+ { "jump-to-forward", 1, 1,
+ window_copy_cmd_jump_to_forward },
+ { "middle-line", 0, 0,
+ window_copy_cmd_middle_line },
+ { "next-matching-bracket", 0, 0,
+ window_copy_cmd_next_matching_bracket },
+ { "next-paragraph", 0, 0,
+ window_copy_cmd_next_paragraph },
+ { "next-space", 0, 0,
+ window_copy_cmd_next_space },
+ { "next-space-end", 0, 0,
+ window_copy_cmd_next_space_end },
+ { "next-word", 0, 0,
+ window_copy_cmd_next_word },
+ { "next-word-end", 0, 0,
+ window_copy_cmd_next_word_end },
+ { "other-end", 0, 0,
+ window_copy_cmd_other_end },
+ { "page-down", 0, 0,
+ window_copy_cmd_page_down },
+ { "page-down-and-cancel", 0, 0,
+ window_copy_cmd_page_down_and_cancel },
+ { "page-up", 0, 0,
+ window_copy_cmd_page_up },
+ { "previous-matching-bracket", 0, 0,
+ window_copy_cmd_previous_matching_bracket },
+ { "previous-paragraph", 0, 0,
+ window_copy_cmd_previous_paragraph },
+ { "previous-space", 0, 0,
+ window_copy_cmd_previous_space },
+ { "previous-word", 0, 0,
+ window_copy_cmd_previous_word },
+ { "rectangle-toggle", 0, 0,
+ window_copy_cmd_rectangle_toggle },
+ { "scroll-down", 0, 0,
+ window_copy_cmd_scroll_down },
+ { "scroll-down-and-cancel", 0, 0,
+ window_copy_cmd_scroll_down_and_cancel },
+ { "scroll-up", 0, 0,
+ window_copy_cmd_scroll_up },
+ { "search-again", 0, 0,
+ window_copy_cmd_search_again },
+ { "search-backward", 1, 1,
+ window_copy_cmd_search_backward },
+ { "search-backward-incremental", 1, 1,
+ window_copy_cmd_search_backward_incremental },
+ { "search-forward", 1, 1,
+ window_copy_cmd_search_forward },
+ { "search-forward-incremental", 1, 1,
+ window_copy_cmd_search_forward_incremental },
+ { "search-reverse", 0, 0,
+ window_copy_cmd_search_reverse },
+ { "select-line", 0, 0,
+ window_copy_cmd_select_line },
+ { "select-word", 0, 0,
+ window_copy_cmd_select_word },
+ { "start-of-line", 0, 0,
+ window_copy_cmd_start_of_line },
+ { "stop-selection", 0, 0,
+ window_copy_cmd_stop_selection },
+ { "top-line", 0, 0,
+ window_copy_cmd_top_line },
+};
+
+static void
+window_copy_command(struct window_mode_entry *wme, struct client *c,
+ struct session *s, struct winlink *wl, struct args *args,
+ struct mouse_event *m)
+{
+ struct window_copy_mode_data *data = wme->data;
+ struct window_copy_cmd_state cs;
+ enum window_copy_cmd_action action;
+ const char *command;
+ u_int i;
+
+ if (args->argc == 0)
+ return;
+ command = args->argv[0];
+
+ if (m != NULL && m->valid && !MOUSE_WHEEL(m->b))
+ window_copy_move_mouse(m);
+
+ cs.wme = wme;
+ cs.args = args;
+ cs.m = m;
+
+ cs.c = c;
+ cs.s = s;
+ cs.wl = wl;
+
+ action = WINDOW_COPY_CMD_NOTHING;
+ for (i = 0; i < nitems(window_copy_cmd_table); i++) {
+ if (strcmp(window_copy_cmd_table[i].command, command) == 0) {
+ if (args->argc - 1 < window_copy_cmd_table[i].minargs ||
+ args->argc - 1 > window_copy_cmd_table[i].maxargs)
+ break;
+ action = window_copy_cmd_table[i].f (&cs);
+ break;
}
}
if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) {
window_copy_clear_marks(wme);
- redraw = 1;
+ if (action == WINDOW_COPY_CMD_NOTHING)
+ action = WINDOW_COPY_CMD_REDRAW;
data->searchx = data->searchy = -1;
}
-
wme->prefix = 1;
- if (cancel)
- window_pane_reset_mode(wp);
- else if (redraw)
+ if (action == WINDOW_COPY_CMD_CANCEL)
+ window_pane_reset_mode(wme->wp);
+ else if (action == WINDOW_COPY_CMD_REDRAW)
window_copy_redraw_screen(wme);
}
@@ -1721,7 +2644,8 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
}
static void
-window_copy_copy_buffer(struct window_mode_entry *wme, void *buf, size_t len)
+window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix,
+ void *buf, size_t len)
{
struct window_pane *wp = wme->wp;
struct screen_write_ctx ctx;
@@ -1733,41 +2657,35 @@ window_copy_copy_buffer(struct window_mode_entry *wme, void *buf, size_t len)
notify_pane("pane-set-clipboard", wp);
}
- if (paste_set(buf, len, NULL, NULL) != 0)
- free(buf);
+ paste_add(prefix, buf, len);
}
static void
window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s,
- const char *fmt)
+ const char *prefix, const char *command)
{
- struct window_pane *wp = wme->wp;
- void *buf;
- size_t len;
- struct job *job;
- char *expanded;
+ void *buf;
+ size_t len;
+ struct job *job;
buf = window_copy_get_selection(wme, &len);
if (buf == NULL)
return;
- expanded = format_single(NULL, fmt, NULL, s, NULL, wp);
- job = job_run(expanded, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT);
+ job = job_run(command, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT);
bufferevent_write(job_get_event(job), buf, len);
-
- free(expanded);
- window_copy_copy_buffer(wme, buf, len);
+ window_copy_copy_buffer(wme, prefix, buf, len);
}
static void
-window_copy_copy_selection(struct window_mode_entry *wme)
+window_copy_copy_selection(struct window_mode_entry *wme, const char *prefix)
{
char *buf;
size_t len;
buf = window_copy_get_selection(wme, &len);
if (buf != NULL)
- window_copy_copy_buffer(wme, buf, len);
+ window_copy_copy_buffer(wme, prefix, buf, len);
}
static void
@@ -2445,6 +3363,11 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme,
py = screen_hsize(data->backing) + data->cy - data->oy;
px = window_copy_find_length(wme, py);
+
+ /* Stop if separator at EOL. */
+ if (px > 0 &&
+ window_copy_in_set(wme, px - 1, py, separators))
+ break;
}
}
@@ -2578,7 +3501,7 @@ window_copy_start_drag(struct client *c, struct mouse_event *m)
return;
c->tty.mouse_drag_update = window_copy_drag_update;
- c->tty.mouse_drag_release = NULL; /* will fire MouseDragEnd key */
+ c->tty.mouse_drag_release = window_copy_drag_release;
window_copy_update_cursor(wme, x, y);
window_copy_start_selection(wme);
@@ -2591,7 +3514,10 @@ window_copy_drag_update(struct client *c, struct mouse_event *m)
struct window_pane *wp;
struct window_mode_entry *wme;
struct window_copy_mode_data *data;
- u_int x, y, old_cy;
+ u_int x, y, old_cx, old_cy;
+ struct timeval tv = {
+ .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME
+ };
if (c == NULL)
return;
@@ -2602,13 +3528,46 @@ window_copy_drag_update(struct client *c, struct mouse_event *m)
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_copy_mode)
return;
+
data = wme->data;
+ evtimer_del(&data->dragtimer);
if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
return;
+ old_cx = data->cx;
old_cy = data->cy;
window_copy_update_cursor(wme, x, y);
if (window_copy_update_selection(wme, 1))
window_copy_redraw_selection(wme, old_cy);
+ if (old_cy != data->cy || old_cx == data->cx) {
+ if (y == 0) {
+ evtimer_add(&data->dragtimer, &tv);
+ window_copy_cursor_up(wme, 1);
+ } else if (y == screen_size_y(&data->screen) - 1) {
+ evtimer_add(&data->dragtimer, &tv);
+ window_copy_cursor_down(wme, 1);
+ }
+ }
+}
+
+static void
+window_copy_drag_release(struct client *c, struct mouse_event *m)
+{
+ struct window_pane *wp;
+ struct window_mode_entry *wme;
+ struct window_copy_mode_data *data;
+
+ if (c == NULL)
+ return;
+
+ wp = cmd_mouse_pane(m, NULL, NULL);
+ if (wp == NULL)
+ return;
+ wme = TAILQ_FIRST(&wp->modes);
+ if (wme == NULL || wme->mode != &window_copy_mode)
+ return;
+
+ data = wme->data;
+ evtimer_del(&data->dragtimer);
}
diff --git a/window-tree.c b/window-tree.c
index 55dda694..2aebc6ce 100644
--- a/window-tree.c
+++ b/window-tree.c
@@ -785,7 +785,8 @@ window_tree_search(__unused void *modedata, void *itemdata, const char *ss)
struct session *s;
struct winlink *wl;
struct window_pane *wp;
- const char *cmd;
+ char *cmd;
+ int retval;
window_tree_pull_item(item, &s, &wl, &wp);
@@ -806,7 +807,9 @@ window_tree_search(__unused void *modedata, void *itemdata, const char *ss)
cmd = osdep_get_name(wp->fd, wp->tty);
if (cmd == NULL || *cmd == '\0')
return (0);
- return (strstr(cmd, ss) != NULL);
+ retval = (strstr(cmd, ss) != NULL);
+ free(cmd);
+ return retval;
}
return (0);
}
@@ -1006,7 +1009,7 @@ window_tree_kill_each(__unused void* modedata, void* itemdata,
case WINDOW_TREE_SESSION:
if (s != NULL) {
server_destroy_session(s);
- session_destroy(s, __func__);
+ session_destroy(s, 1, __func__);
}
break;
case WINDOW_TREE_WINDOW:
diff --git a/window.c b/window.c
index 4ed766ad..795bc26c 100644
--- a/window.c
+++ b/window.c
@@ -26,7 +26,6 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
-#include <termios.h>
#include <time.h>
#include <unistd.h>
@@ -71,20 +70,10 @@ const struct window_mode *all_window_modes[] = {
NULL
};
-static void window_destroy(struct window *);
-
static struct window_pane *window_pane_create(struct window *, u_int, u_int,
u_int);
static void window_pane_destroy(struct window_pane *);
-static void window_pane_read_callback(struct bufferevent *, void *);
-static void window_pane_error_callback(struct bufferevent *, short, void *);
-
-static int winlink_next_index(struct winlinks *, int);
-
-static struct window_pane *window_pane_choose_best(struct window_pane **,
- u_int);
-
RB_GENERATE(windows, window, entry, window_cmp);
RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp);
@@ -340,37 +329,7 @@ window_create(u_int sx, u_int sy)
return (w);
}
-struct window *
-window_create_spawn(const char *name, int argc, char **argv, const char *path,
- const char *shell, const char *cwd, struct environ *env,
- struct termios *tio, u_int sx, u_int sy, u_int hlimit, char **cause)
-{
- struct window *w;
- struct window_pane *wp;
-
- w = window_create(sx, sy);
- wp = window_add_pane(w, NULL, 0, 0, hlimit);
- layout_init(w, wp);
-
- if (window_pane_spawn(wp, argc, argv, path, shell, cwd,
- env, tio, cause) != 0) {
- window_destroy(w);
- return (NULL);
- }
-
- w->active = TAILQ_FIRST(&w->panes);
- if (name != NULL) {
- w->name = xstrdup(name);
- options_set_number(w->options, "automatic-rename", 0);
- } else
- w->name = default_window_name(w);
-
- notify_window("window-pane-changed", w);
-
- return (w);
-}
-
-static void
+void
window_destroy(struct window *w)
{
log_debug("window @%u destroyed (%d references)", w->id, w->references);
@@ -383,6 +342,8 @@ window_destroy(struct window *w)
layout_free_cell(w->saved_layout_root);
free(w->old_layout);
+ window_destroy_panes(w);
+
if (event_initialized(&w->name_event))
evtimer_del(&w->name_event);
@@ -393,8 +354,6 @@ window_destroy(struct window *w)
options_free(w->options);
- window_destroy_panes(w);
-
free(w->name);
free(w);
}
@@ -461,17 +420,22 @@ window_has_pane(struct window *w, struct window_pane *wp)
}
int
-window_set_active_pane(struct window *w, struct window_pane *wp)
+window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
{
- log_debug("%s: pane %%%u (was %%%u)", __func__, wp->id, w->active->id);
+ log_debug("%s: pane %%%u", __func__, wp->id);
+
if (wp == w->active)
return (0);
w->last = w->active;
+
w->active = wp;
w->active->active_point = next_active_point++;
w->active->flags |= PANE_CHANGED;
+
tty_update_window_offset(w);
- notify_window("window-pane-changed", w);
+
+ if (notify)
+ notify_window("window-pane-changed", w);
return (1);
}
@@ -569,7 +533,7 @@ window_zoom(struct window_pane *wp)
return (-1);
if (w->active != wp)
- window_set_active_pane(w, wp);
+ window_set_active_pane(w, wp, 1);
TAILQ_FOREACH(wp1, &w->panes, entry) {
wp1->saved_layout_cell = wp1->layout_cell;
@@ -608,8 +572,8 @@ window_unzoom(struct window *w)
}
struct window_pane *
-window_add_pane(struct window *w, struct window_pane *other, int before,
- int full_size, u_int hlimit)
+window_add_pane(struct window *w, struct window_pane *other, u_int hlimit,
+ int flags)
{
struct window_pane *wp;
@@ -620,15 +584,15 @@ window_add_pane(struct window *w, struct window_pane *other, int before,
if (TAILQ_EMPTY(&w->panes)) {
log_debug("%s: @%u at start", __func__, w->id);
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
- } else if (before) {
+ } else if (flags & SPAWN_BEFORE) {
log_debug("%s: @%u before %%%u", __func__, w->id, wp->id);
- if (full_size)
+ if (flags & SPAWN_FULLSIZE)
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
else
TAILQ_INSERT_BEFORE(other, wp, entry);
} else {
log_debug("%s: @%u after %%%u", __func__, w->id, wp->id);
- if (full_size)
+ if (flags & SPAWN_FULLSIZE)
TAILQ_INSERT_TAIL(&w->panes, wp, entry);
else
TAILQ_INSERT_AFTER(&w->panes, other, wp, entry);
@@ -888,153 +852,6 @@ window_pane_destroy(struct window_pane *wp)
free(wp);
}
-int
-window_pane_spawn(struct window_pane *wp, int argc, char **argv,
- const char *path, const char *shell, const char *cwd, struct environ *env,
- struct termios *tio, char **cause)
-{
- struct winsize ws;
- char *argv0, *cmd, **argvp;
- const char *ptr, *first, *home;
- struct termios tio2;
-#ifdef HAVE_UTEMPTER
- char s[32];
-#endif
- sigset_t set, oldset;
-
- if (wp->fd != -1) {
- bufferevent_free(wp->event);
- close(wp->fd);
- }
- 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);
- wp->shell = xstrdup(shell);
- }
- if (cwd != NULL) {
- free((void *)wp->cwd);
- wp->cwd = xstrdup(cwd);
- }
- wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN);
-
- cmd = cmd_stringify_argv(wp->argc, wp->argv);
- log_debug("%s: shell=%s", __func__, wp->shell);
- log_debug("%s: cmd=%s", __func__, cmd);
- log_debug("%s: cwd=%s", __func__, cwd);
- cmd_log_argv(wp->argc, wp->argv, __func__);
- environ_log(env, "%s: environment ", __func__);
-
- memset(&ws, 0, sizeof ws);
- ws.ws_col = screen_size_x(&wp->base);
- ws.ws_row = screen_size_y(&wp->base);
-
- sigfillset(&set);
- sigprocmask(SIG_BLOCK, &set, &oldset);
- switch (wp->pid = fdforkpty(ptm_fd, &wp->fd, wp->tty, NULL, &ws)) {
- case -1:
- wp->event = NULL;
- wp->fd = -1;
-
- xasprintf(cause, "%s: %s", cmd, strerror(errno));
- free(cmd);
-
- sigprocmask(SIG_SETMASK, &oldset, NULL);
- return (-1);
- case 0:
- proc_clear_signals(server_proc, 1);
- sigprocmask(SIG_SETMASK, &oldset, NULL);
-
- cwd = NULL;
- if (chdir(wp->cwd) == 0)
- cwd = wp->cwd;
- else if ((home = find_home()) != NULL && chdir(home) == 0)
- cwd = home;
- else
- chdir("/");
-
- if (tcgetattr(STDIN_FILENO, &tio2) != 0)
- fatal("tcgetattr failed");
- if (tio != NULL)
- memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc);
- tio2.c_cc[VERASE] = '\177';
-#ifdef IUTF8
- tio2.c_iflag |= IUTF8;
-#endif
- if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0)
- fatal("tcgetattr failed");
-
- log_close();
- closefrom(STDERR_FILENO + 1);
-
- if (path != NULL)
- environ_set(env, "PATH", "%s", path);
- if (cwd != NULL)
- environ_set(env, "PWD", "%s", cwd);
- environ_set(env, "TMUX_PANE", "%%%u", wp->id);
- environ_push(env);
-
- setenv("SHELL", wp->shell, 1);
- ptr = strrchr(wp->shell, '/');
-
- /*
- * If given one argument, assume it should be passed to sh -c;
- * with more than one argument, use execvp(). If there is no
- * arguments, create a login shell.
- */
- if (wp->argc > 0) {
- if (wp->argc != 1) {
- /* Copy to ensure argv ends in NULL. */
- argvp = cmd_copy_argv(wp->argc, wp->argv);
- execvp(argvp[0], argvp);
- fatal("execvp failed");
- }
- first = wp->argv[0];
-
- if (ptr != NULL && *(ptr + 1) != '\0')
- xasprintf(&argv0, "%s", ptr + 1);
- else
- xasprintf(&argv0, "%s", wp->shell);
- execl(wp->shell, argv0, "-c", first, (char *)NULL);
- fatal("execl failed");
- }
- if (ptr != NULL && *(ptr + 1) != '\0')
- xasprintf(&argv0, "-%s", ptr + 1);
- else
- xasprintf(&argv0, "-%s", wp->shell);
- execl(wp->shell, argv0, (char *)NULL);
- fatal("execl failed");
- }
- log_debug("%s: master=%s", __func__, ttyname(wp->fd));
- log_debug("%s: slave=%s", __func__, wp->tty);
-
-#ifdef HAVE_UTEMPTER
- xsnprintf(s, sizeof s, "tmux(%lu).%%%u", (long) getpid(), wp->id);
- utempter_add_record(wp->fd, s);
- kill(getpid(), SIGCHLD);
-#endif
-
- sigprocmask(SIG_SETMASK, &oldset, NULL);
- setblocking(wp->fd, 0);
-
- wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL,
- window_pane_error_callback, wp);
- if (wp->event == NULL)
- fatalx("out of memory");
-
- wp->pipe_off = 0;
- wp->flags &= ~PANE_EXITED;
-
- bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE);
- bufferevent_enable(wp->event, EV_READ|EV_WRITE);
-
- free(cmd);
- return (0);
-}
-
static void
window_pane_read_callback(__unused struct bufferevent *bufev, void *data)
{
@@ -1070,6 +887,18 @@ window_pane_error_callback(__unused struct bufferevent *bufev,
}
void
+window_pane_set_event(struct window_pane *wp)
+{
+ setblocking(wp->fd, 0);
+
+ wp->event = bufferevent_new(wp->fd, window_pane_read_callback,
+ NULL, window_pane_error_callback, wp);
+
+ bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE);
+ bufferevent_enable(wp->event, EV_READ|EV_WRITE);
+}
+
+void
window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
{
struct window_mode_entry *wme;
@@ -1617,6 +1446,8 @@ winlink_shuffle_up(struct session *s, struct winlink *wl)
{
int idx, last;
+ if (wl == NULL)
+ return (-1);
idx = wl->idx + 1;
/* Find the next free index. */