aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/CONTRIBUTING.md9
-rw-r--r--.github/ISSUE_TEMPLATE.md3
-rw-r--r--.github/README.md12
-rw-r--r--CHANGES22
-rw-r--r--Makefile.am1
-rw-r--r--arguments.c2
-rw-r--r--cfg.c3
-rw-r--r--cmd-attach-session.c27
-rw-r--r--cmd-display-message.c3
-rw-r--r--cmd-display-panes.c2
-rw-r--r--cmd-find.c73
-rw-r--r--cmd-if-shell.c15
-rw-r--r--cmd-load-buffer.c2
-rw-r--r--cmd-new-session.c7
-rw-r--r--cmd-parse.y61
-rw-r--r--cmd-queue.c7
-rw-r--r--cmd-run-shell.c2
-rw-r--r--cmd-source-file.c6
-rw-r--r--cmd-wait-for.c8
-rw-r--r--configure.ac2
-rw-r--r--format.c110
-rw-r--r--layout-custom.c10
-rw-r--r--menu.c2
-rw-r--r--mode-tree.c2
-rw-r--r--options.c3
-rw-r--r--regress/command-order.sh6
-rw-r--r--regress/conf/21867280ff7e99631046f9cc669b80d2.conf8
-rw-r--r--regress/conf/99749670b62bcb99a9b2e3d59708e357.conf93
-rw-r--r--regress/xenl-terminal.sh29
-rw-r--r--regsub.c98
-rw-r--r--server-client.c15
-rw-r--r--server.c10
-rw-r--r--tmux.172
-rw-r--r--tmux.h12
-rw-r--r--tty-term.c2
-rw-r--r--tty.c11
-rw-r--r--window-buffer.c2
-rw-r--r--window-copy.c8
-rw-r--r--window.c70
39 files changed, 614 insertions, 216 deletions
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 49e2b90b..3a589484 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -2,13 +2,20 @@
Before opening an issue, please ensure that:
+- Your problem is a specific problem or question or suggestion, not a general
+ complaint.
+
- `$TERM` inside tmux is screen, screen-256color, tmux or tmux-256color. Check
by running `echo $TERM` inside tmux.
- You can reproduce the problem with the latest tmux release, or a build from
Git master.
-- Your question or issue is not covered in the manual (run man tmux).
+- Your question or issue is not covered [in the
+ manual](https://man.openbsd.org/tmux.1) (run `man tmux`).
+
+- Your problem is not mentioned in the [CHANGES
+ file](https://raw.githubusercontent.com/tmux/tmux/master/CHANGES) file.
- Nobody else has opened the same issue recently.
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index ee457237..8bf1e66a 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -3,6 +3,9 @@
Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
before opening an issue.
+If you have upgraded, make sure your issue is not covered in the CHANGES file
+for your version: https://raw.githubusercontent.com/tmux/tmux/master/CHANGES
+
Describe the problem and the steps to reproduce. Add a minimal tmux config if
necessary. Screenshots can be helpful, but no more than one or two.
diff --git a/.github/README.md b/.github/README.md
index 6e262ef5..ce12f6aa 100644
--- a/.github/README.md
+++ b/.github/README.md
@@ -50,14 +50,12 @@ welcome. Please send by email to:
tmux-users@googlegroups.com
-Or open a GitHub issue or pull request.
+Or open a GitHub issue or pull request. **Please read [this
+document](CONTRIBUTING.md) before opening an issue.**
-There is [a TODO list](https://github.com/tmux/tmux/wiki/Contributing) which
-explains some ideas for tmux not yet developed. Please feel free to ask for
-clarifications on the mailing list if you're thinking of working on these or
-need further information.
-
-Please read the CONTRIBUTING file before opening an issue.
+There is [a list of suggestions for contributions](https://github.com/tmux/tmux/wiki/Contributing).
+Please feel free to ask on the mailing list if you're thinking of working on something or need
+further information.
## Documentation
diff --git a/CHANGES b/CHANGES
index 3563bcbc..9129fa3b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,25 @@
+CHANGES FROM 3.0 to X.X
+
+* Resize panes lazily - do not resize unless they are in an attached, active
+ window.
+
+* Add regular expression support for the format search, match and substitute
+ modifiers and make them able to ignore case.
+
+* Do not use $TMUX to find the session because for windows in multiple sessions
+ it is wrong as often as it is right, and for windows in one session it is
+ pointless. Instead use TMUX_PANE if it is present.
+
+* Do not always resize the window back to its original size after applying a
+ layout, keep it at the layout size until it must be resized (for example when
+ attached and window-size is not manual).
+
+* Add new-session -X and attach-session -x to send SIGHUP to parent when
+ detaching (like detach-client -P).
+
+* Support for octal escapes in strings (such as \007) and improve list-keys
+ output so it parses correctly if copied into a configuration file.
+
CHANGES FROM 2.9 to 3.0
* INCOMPATIBLE: Add a new {} syntax to the configuration file. This is a string
diff --git a/Makefile.am b/Makefile.am
index 2b88892f..94b1564b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -151,6 +151,7 @@ dist_tmux_SOURCES = \
options.c \
paste.c \
proc.c \
+ regsub.c \
resize.c \
screen-redraw.c \
screen-write.c \
diff --git a/arguments.c b/arguments.c
index 38e50829..c8a6ab45 100644
--- a/arguments.c
+++ b/arguments.c
@@ -217,7 +217,7 @@ args_escape(const char *s)
return (escaped);
}
- flags = VIS_OCTAL|VIS_TAB|VIS_NL;
+ flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
if (s[strcspn(s, quoted)] != '\0')
flags |= VIS_DQ;
utf8_stravis(&escaped, s, flags);
diff --git a/cfg.c b/cfg.c
index 18386b56..f781e825 100644
--- a/cfg.c
+++ b/cfg.c
@@ -52,7 +52,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
cfg_show_causes(RB_MIN(sessions, &sessions));
if (cfg_item != NULL)
- cfg_item->flags &= ~CMDQ_WAITING;
+ cmdq_continue(cfg_item);
status_prompt_load_history();
@@ -126,6 +126,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
pi.flags = flags;
pi.file = path;
pi.line = 1;
+ pi.item = item;
pr = cmd_parse_from_file(f, &pi);
fclose(f);
diff --git a/cmd-attach-session.c b/cmd-attach-session.c
index bcd6d895..6de734e5 100644
--- a/cmd-attach-session.c
+++ b/cmd-attach-session.c
@@ -37,8 +37,8 @@ const struct cmd_entry cmd_attach_session_entry = {
.name = "attach-session",
.alias = "attach",
- .args = { "c:dErt:", 0, 0 },
- .usage = "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE,
+ .args = { "c:dErt:x", 0, 0 },
+ .usage = "[-dErx] [-c working-directory] " CMD_TARGET_SESSION_USAGE,
/* -t is special */
@@ -48,7 +48,7 @@ const struct cmd_entry cmd_attach_session_entry = {
enum cmd_retval
cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
- int rflag, const char *cflag, int Eflag)
+ int xflag, int rflag, const char *cflag, int Eflag)
{
struct cmd_find_state *current = &item->shared->current;
enum cmd_find_type type;
@@ -58,6 +58,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
struct winlink *wl;
struct window_pane *wp;
char *cause;
+ enum msgtype msgtype;
if (RB_EMPTY(&sessions)) {
cmdq_error(item, "no sessions");
@@ -102,11 +103,15 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
c->last_session = c->session;
if (c->session != NULL) {
- if (dflag) {
+ if (dflag || xflag) {
+ if (xflag)
+ msgtype = MSG_DETACHKILL;
+ else
+ msgtype = MSG_DETACH;
TAILQ_FOREACH(c_loop, &clients, entry) {
if (c_loop->session != s || c == c_loop)
continue;
- server_client_detach(c_loop, MSG_DETACH);
+ server_client_detach(c_loop, msgtype);
}
}
if (!Eflag)
@@ -131,11 +136,15 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (rflag)
c->flags |= CLIENT_READONLY;
- if (dflag) {
+ if (dflag || xflag) {
+ if (xflag)
+ msgtype = MSG_DETACHKILL;
+ else
+ msgtype = MSG_DETACH;
TAILQ_FOREACH(c_loop, &clients, entry) {
if (c_loop->session != s || c == c_loop)
continue;
- server_client_detach(c_loop, MSG_DETACH);
+ server_client_detach(c_loop, msgtype);
}
}
if (!Eflag)
@@ -169,6 +178,6 @@ cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = self->args;
return (cmd_attach_session(item, args_get(args, 't'),
- args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'),
- args_has(args, 'E')));
+ args_has(args, 'd'), args_has(args, 'x'), args_has(args, 'r'),
+ args_get(args, 'c'), args_has(args, 'E')));
}
diff --git a/cmd-display-message.c b/cmd-display-message.c
index 8c1ad5f6..4d9bccb6 100644
--- a/cmd-display-message.c
+++ b/cmd-display-message.c
@@ -109,8 +109,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
format_defaults(ft, target_c, s, wl, wp);
if (args_has(args, 'a')) {
- if (item != NULL)
- format_each(ft, cmd_display_message_each, item);
+ format_each(ft, cmd_display_message_each, item);
return (CMD_RETURN_NORMAL);
}
diff --git a/cmd-display-panes.c b/cmd-display-panes.c
index aeeb6936..aa665f08 100644
--- a/cmd-display-panes.c
+++ b/cmd-display-panes.c
@@ -188,7 +188,7 @@ cmd_display_panes_free(struct client *c)
struct cmd_display_panes_data *cdata = c->overlay_data;
if (cdata->item != NULL)
- cdata->item->flags &= ~CMDQ_WAITING;
+ cmdq_continue(cdata->item);
free(cdata->command);
free(cdata);
}
diff --git a/cmd-find.c b/cmd-find.c
index 7ad8ad7a..154842ab 100644
--- a/cmd-find.c
+++ b/cmd-find.c
@@ -75,38 +75,12 @@ static const char *cmd_find_pane_table[][2] = {
{ NULL, NULL }
};
-/* Get session from TMUX if present. */
-static struct session *
-cmd_find_try_TMUX(struct client *c)
-{
- struct environ_entry *envent;
- char tmp[256];
- long long pid;
- u_int session;
- struct session *s;
-
- envent = environ_find(c->environ, "TMUX");
- if (envent == NULL)
- return (NULL);
-
- if (sscanf(envent->value, "%255[^,],%lld,%d", tmp, &pid, &session) != 3)
- return (NULL);
- if (pid != getpid())
- return (NULL);
- log_debug("%s: client %p TMUX %s (session $%u)", __func__, c,
- envent->value, session);
-
- s = session_find_by_id(session);
- if (s != NULL)
- log_debug("%s: session $%u still exists", __func__, s->id);
- return (s);
-}
-
/* Find pane containing client if any. */
static struct window_pane *
cmd_find_inside_pane(struct client *c)
{
struct window_pane *wp;
+ struct environ_entry *envent;
if (c == NULL)
return (NULL);
@@ -115,6 +89,11 @@ cmd_find_inside_pane(struct client *c)
if (wp->fd != -1 && strcmp(wp->tty, c->ttyname) == 0)
break;
}
+ if (wp == NULL) {
+ envent = environ_find(c->environ, "TMUX_PANE");
+ if (envent != NULL)
+ wp = window_pane_find_by_id_str(envent->value);
+ }
if (wp != NULL)
log_debug("%s: got pane %%%u (%s)", __func__, wp->id, wp->tty);
return (wp);
@@ -879,8 +858,6 @@ cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m, int flags)
int
cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
{
- struct session *s;
- struct winlink *wl;
struct window_pane *wp;
/* If no client, treat as from nothing. */
@@ -902,30 +879,6 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
if (wp == NULL)
goto unknown_pane;
- /* If we have a session in TMUX, see if it has this pane. */
- s = cmd_find_try_TMUX(c);
- if (s != NULL) {
- RB_FOREACH(wl, winlinks, &s->windows) {
- if (window_has_pane(wl->window, wp))
- break;
- }
- if (wl != NULL) {
- log_debug("%s: session $%u has pane %%%u", __func__,
- s->id, wp->id);
-
- fs->s = s;
- fs->wl = s->curw; /* use current session */
- fs->w = fs->wl->window;
- fs->wp = fs->w->active; /* use active pane */
-
- cmd_find_log_state(__func__, fs);
- return (0);
- } else {
- log_debug("%s: session $%u does not have pane %%%u",
- __func__, s->id, wp->id);
- }
- }
-
/*
* Don't have a session, or it doesn't have this pane. Try all
* sessions.
@@ -947,17 +900,7 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
return (0);
unknown_pane:
- /*
- * We're not running in a known pane, but maybe this client has TMUX
- * in the environment. That'd give us a session.
- */
- s = cmd_find_try_TMUX(c);
- if (s != NULL) {
- cmd_find_from_session(fs, s, flags);
- return (0);
- }
-
- /* Otherwise we need to guess. */
+ /* We can't find the pane so need to guess. */
return (cmd_find_from_nothing(fs, flags));
}
@@ -1005,6 +948,8 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
strlcat(tmp, "CANFAIL,", sizeof tmp);
if (*tmp != '\0')
tmp[strlen(tmp) - 1] = '\0';
+ else
+ strlcat(tmp, "NONE", sizeof tmp);
log_debug("%s: target %s, type %s, item %p, flags %s", __func__,
target == NULL ? "none" : target, s, item, tmp);
diff --git a/cmd-if-shell.c b/cmd-if-shell.c
index 84f66657..a992602c 100644
--- a/cmd-if-shell.c
+++ b/cmd-if-shell.c
@@ -67,10 +67,11 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_if_shell_data *cdata;
char *shellcmd, *cmd;
struct cmdq_item *new_item;
+ 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 window_pane *wp = item->target.wp;
+ struct session *s = fs->s;
+ struct winlink *wl = fs->wl;
+ struct window_pane *wp = fs->wp;
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
@@ -92,7 +93,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
pi.line = self->line;
pi.item = item;
pi.c = c;
- cmd_find_copy_state(&pi.fs, &item->target);
+ cmd_find_copy_state(&pi.fs, fs);
pr = cmd_parse_from_string(cmd, &pi);
switch (pr->status) {
@@ -103,7 +104,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
free(pr->error);
return (CMD_RETURN_ERROR);
case CMD_PARSE_SUCCESS:
- new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0);
+ new_item = cmdq_get_command(pr->cmdlist, fs, m, 0);
cmdq_insert_after(item, new_item);
cmd_list_free(pr->cmdlist);
break;
@@ -137,7 +138,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->input.c = c;
if (cdata->input.c != NULL)
cdata->input.c->references++;
- cmd_find_copy_state(&cdata->input.fs, &item->target);
+ cmd_find_copy_state(&cdata->input.fs, fs);
if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL,
cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) {
@@ -195,7 +196,7 @@ cmd_if_shell_callback(struct job *job)
out:
if (cdata->item != NULL)
- cdata->item->flags &= ~CMDQ_WAITING;
+ cmdq_continue(cdata->item);
}
static void
diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c
index 3e669093..cdf44bf7 100644
--- a/cmd-load-buffer.c
+++ b/cmd-load-buffer.c
@@ -176,7 +176,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data)
free(cause);
}
out:
- cdata->item->flags &= ~CMDQ_WAITING;
+ cmdq_continue(cdata->item);
free(cdata->bufname);
free(cdata);
diff --git a/cmd-new-session.c b/cmd-new-session.c
index 559c268c..e0540815 100644
--- a/cmd-new-session.c
+++ b/cmd-new-session.c
@@ -39,8 +39,8 @@ const struct cmd_entry cmd_new_session_entry = {
.name = "new-session",
.alias = "new",
- .args = { "Ac:dDEF:n:Ps:t:x:y:", 0, -1 },
- .usage = "[-AdDEP] [-c start-directory] [-F format] [-n window-name] "
+ .args = { "Ac:dDEF:n:Ps:t:x:Xy:", 0, -1 },
+ .usage = "[-AdDEPX] [-c start-directory] [-F format] [-n window-name] "
"[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] "
"[-y height] [command]",
@@ -105,7 +105,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'A')) {
retval = cmd_attach_session(item,
newname, args_has(args, 'D'),
- 0, NULL, args_has(args, 'E'));
+ args_has(args, 'X'), 0, NULL,
+ args_has(args, 'E'));
free(newname);
return (retval);
}
diff --git a/cmd-parse.y b/cmd-parse.y
index ce7b344b..6d2b970c 100644
--- a/cmd-parse.y
+++ b/cmd-parse.y
@@ -77,6 +77,8 @@ static char *cmd_parse_get_error(const char *, u_int, const char *);
static void cmd_parse_free_command(struct cmd_parse_command *);
static struct cmd_parse_commands *cmd_parse_new_commands(void);
static void cmd_parse_free_commands(struct cmd_parse_commands *);
+static void cmd_parse_print_commands(struct cmd_parse_input *, u_int,
+ struct cmd_list *);
%}
@@ -508,6 +510,22 @@ cmd_parse_get_error(const char *file, u_int line, const char *error)
}
static void
+cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line,
+ struct cmd_list *cmdlist)
+{
+ char *s;
+
+ if (pi->item != NULL && (pi->flags & CMD_PARSE_VERBOSE)) {
+ s = cmd_list_print(cmdlist, 0);
+ if (pi->file != NULL)
+ cmdq_print(pi->item, "%s:%u: %s", pi->file, line, s);
+ else
+ cmdq_print(pi->item, "%u: %s", line, s);
+ free(s);
+ }
+}
+
+static void
cmd_parse_free_command(struct cmd_parse_command *cmd)
{
free(cmd->name);
@@ -663,6 +681,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
if (cmdlist == NULL || cmd->line != line) {
if (cmdlist != NULL) {
+ cmd_parse_print_commands(pi, line, cmdlist);
cmd_list_move(result, cmdlist);
cmd_list_free(cmdlist);
}
@@ -682,6 +701,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
cmd_list_append(cmdlist, add);
}
if (cmdlist != NULL) {
+ cmd_parse_print_commands(pi, line, cmdlist);
cmd_list_move(result, cmdlist);
cmd_list_free(cmdlist);
}
@@ -1124,17 +1144,54 @@ error:
static int
yylex_token_escape(char **buf, size_t *len)
{
- int ch, type;
+ int ch, type, o2, o3;
u_int size, i, tmp;
char s[9];
struct utf8_data ud;
- switch (ch = yylex_getc()) {
+ ch = yylex_getc();
+
+ if (ch >= '4' && ch <= '7') {
+ yyerror("invalid octal escape");
+ return (0);
+ }
+ if (ch >= '0' && ch <= '3') {
+ o2 = yylex_getc();
+ if (o2 >= '0' && o2 <= '7') {
+ o3 = yylex_getc();
+ if (o3 >= '0' && o3 <= '7') {
+ ch = 64 * (ch - '0') +
+ 8 * (o2 - '0') +
+ (o3 - '0');
+ yylex_append1(buf, len, ch);
+ return (1);
+ }
+ }
+ yyerror("invalid octal escape");
+ return (0);
+ }
+
+ switch (ch) {
case EOF:
return (0);
+ case 'a':
+ ch = '\a';
+ break;
+ case 'b':
+ ch = '\b';
+ break;
case 'e':
ch = '\033';
break;
+ case 'f':
+ ch = '\f';
+ break;
+ case 's':
+ ch = ' ';
+ break;
+ case 'v':
+ ch = '\v';
+ break;
case 'r':
ch = '\r';
break;
diff --git a/cmd-queue.c b/cmd-queue.c
index 1ee43508..ef68d5d5 100644
--- a/cmd-queue.c
+++ b/cmd-queue.c
@@ -156,6 +156,13 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item,
free(name);
}
+/* Continue processing command queue. */
+void
+cmdq_continue(struct cmdq_item *item)
+{
+ item->flags &= ~CMDQ_WAITING;
+}
+
/* Remove an item. */
static void
cmdq_remove(struct cmdq_item *item)
diff --git a/cmd-run-shell.c b/cmd-run-shell.c
index c9a478c7..2f45f492 100644
--- a/cmd-run-shell.c
+++ b/cmd-run-shell.c
@@ -155,7 +155,7 @@ cmd_run_shell_callback(struct job *job)
free(msg);
if (cdata->item != NULL)
- cdata->item->flags &= ~CMDQ_WAITING;
+ cmdq_continue(cdata->item);
}
static void
diff --git a/cmd-source-file.c b/cmd-source-file.c
index 38aede0c..2e01ae67 100644
--- a/cmd-source-file.c
+++ b/cmd-source-file.c
@@ -37,8 +37,8 @@ const struct cmd_entry cmd_source_file_entry = {
.name = "source-file",
.alias = "source",
- .args = { "nq", 1, -1 },
- .usage = "[-nq] path ...",
+ .args = { "nqv", 1, -1 },
+ .usage = "[-nqv] path ...",
.flags = 0,
.exec = cmd_source_file_exec
@@ -62,6 +62,8 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
flags |= CMD_PARSE_QUIET;
if (args_has(args, 'n'))
flags |= CMD_PARSE_PARSEONLY;
+ if (args_has(args, 'v'))
+ flags |= CMD_PARSE_VERBOSE;
utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB);
retval = CMD_RETURN_NORMAL;
diff --git a/cmd-wait-for.c b/cmd-wait-for.c
index 33600eda..4f438a7f 100644
--- a/cmd-wait-for.c
+++ b/cmd-wait-for.c
@@ -153,7 +153,7 @@ cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name,
log_debug("signal wait channel %s, with waiters", wc->name);
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
- wi->item->flags &= ~CMDQ_WAITING;
+ cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->waiters, wi, entry);
free(wi);
@@ -229,7 +229,7 @@ cmd_wait_for_unlock(struct cmdq_item *item, const char *name,
}
if ((wi = TAILQ_FIRST(&wc->lockers)) != NULL) {
- wi->item->flags &= ~CMDQ_WAITING;
+ cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->lockers, wi, entry);
free(wi);
} else {
@@ -248,13 +248,13 @@ cmd_wait_for_flush(void)
RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
- wi->item->flags &= ~CMDQ_WAITING;
+ cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->waiters, wi, entry);
free(wi);
}
wc->woken = 1;
TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
- wi->item->flags &= ~CMDQ_WAITING;
+ cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->lockers, wi, entry);
free(wi);
}
diff --git a/configure.ac b/configure.ac
index 8473a0d2..5fba1eaf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
# configure.ac
-AC_INIT([tmux], 3.0-rc3)
+AC_INIT([tmux], next-3.1)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
diff --git a/format.c b/format.c
index 3d649a29..f5754f0d 100644
--- a/format.c
+++ b/format.c
@@ -23,6 +23,7 @@
#include <errno.h>
#include <fnmatch.h>
#include <libgen.h>
+#include <regex.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
@@ -1263,7 +1264,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
cp++;
/* Check single character modifiers with no arguments. */
- if (strchr("lmCbdtqETSWP<>", cp[0]) != NULL &&
+ if (strchr("lbdtqETSWP<>", cp[0]) != NULL &&
format_is_end(cp[1])) {
format_add_modifier(&list, count, cp, 1, NULL, 0);
cp++;
@@ -1284,7 +1285,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
}
/* Now try single character with arguments. */
- if (strchr("s=", cp[0]) == NULL)
+ if (strchr("mCs=", cp[0]) == NULL)
break;
c = cp[0];
@@ -1345,39 +1346,67 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
return list;
}
-/* Perform substitution in string. */
+/* Match against an fnmatch(3) pattern or regular expression. */
static char *
-format_substitute(const char *source, const char *from, const char *to)
+format_match(struct format_modifier *fm, const char *pattern, const char *text)
{
- char *copy, *new;
- const char *cp;
- size_t fromlen, tolen, newlen, used;
-
- fromlen = strlen(from);
- tolen = strlen(to);
-
- newlen = strlen(source) + 1;
- copy = new = xmalloc(newlen);
-
- for (cp = source; *cp != '\0'; /* nothing */) {
- if (strncmp(cp, from, fromlen) != 0) {
- *new++ = *cp++;
- continue;
+ const char *s = "";
+ regex_t r;
+ int flags = 0;
+
+ if (fm->argc >= 1)
+ s = fm->argv[0];
+ if (strchr(s, 'r') == NULL) {
+ if (strchr(s, 'i') != NULL)
+ flags |= FNM_CASEFOLD;
+ if (fnmatch(pattern, text, flags) != 0)
+ return (xstrdup("0"));
+ } else {
+ flags = REG_EXTENDED|REG_NOSUB;
+ if (strchr(s, 'i') != NULL)
+ flags |= REG_ICASE;
+ if (regcomp(&r, pattern, flags) != 0)
+ return (xstrdup("0"));
+ if (regexec(&r, text, 0, NULL, 0) != 0) {
+ regfree(&r);
+ return (xstrdup("0"));
}
- used = new - copy;
-
- newlen += tolen;
- copy = xrealloc(copy, newlen);
+ regfree(&r);
+ }
+ return (xstrdup("1"));
+}
- new = copy + used;
- memcpy(new, to, tolen);
+/* Perform substitution in string. */
+static char *
+format_sub(struct format_modifier *fm, const char *text, const char *pattern,
+ const char *with)
+{
+ char *value;
+ int flags = REG_EXTENDED;
+
+ if (fm->argc >= 3 && strchr(fm->argv[2], 'i') != NULL)
+ flags |= REG_ICASE;
+ value = regsub(pattern, with, text, flags);
+ if (value == NULL)
+ return (xstrdup(text));
+ return (value);
+}
- new += tolen;
- cp += fromlen;
+/* Search inside pane. */
+static char *
+format_search(struct format_modifier *fm, struct window_pane *wp, const char *s)
+{
+ int ignore = 0, regex = 0;
+ char *value;
+
+ if (fm->argc >= 1) {
+ if (strchr(fm->argv[0], 'i') != NULL)
+ ignore = 1;
+ if (strchr(fm->argv[0], 'r') != NULL)
+ regex = 1;
}
-
- *new = '\0';
- return (copy);
+ xasprintf(&value, "%u", window_pane_search(wp, s, regex, ignore));
+ return (value);
}
/* Loop over sessions. */
@@ -1522,11 +1551,10 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
char *copy0, *condition, *found, *new;
char *value, *left, *right;
size_t valuelen;
- int modifiers = 0, limit = 0;
+ int modifiers = 0, limit = 0, j;
struct format_modifier *list, *fm, *cmp = NULL, *search = NULL;
struct format_modifier *sub = NULL;
u_int i, count;
- int j;
/* Make a copy of the key. */
copy = copy0 = xstrndup(key, keylen);
@@ -1553,18 +1581,18 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
search = fm;
break;
case 's':
- if (fm->argc != 2)
+ if (fm->argc < 2)
break;
sub = fm;
break;
case '=':
- if (fm->argc != 1 && fm->argc != 2)
+ if (fm->argc < 1)
break;
limit = strtonum(fm->argv[0], INT_MIN, INT_MAX,
&errptr);
if (errptr != NULL)
limit = 0;
- if (fm->argc == 2 && fm->argv[1] != NULL)
+ if (fm->argc >= 2 && fm->argv[1] != NULL)
marker = fm->argv[1];
break;
case 'l':
@@ -1635,7 +1663,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
value = xstrdup("0");
} else {
format_log(ft, "search '%s' pane %%%u", copy, wp->id);
- xasprintf(&value, "%u", window_pane_search(wp, copy));
+ value = format_search(fm, wp, copy);
}
} else if (cmp != NULL) {
/* Comparison of left and right. */
@@ -1687,12 +1715,8 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
value = xstrdup("1");
else
value = xstrdup("0");
- } else if (strcmp(cmp->modifier, "m") == 0) {
- if (fnmatch(left, right, 0) == 0)
- value = xstrdup("1");
- else
- value = xstrdup("0");
- }
+ } else if (strcmp(cmp->modifier, "m") == 0)
+ value = format_match(cmp, left, right);
free(right);
free(left);
@@ -1770,8 +1794,8 @@ done:
/* Perform substitution if any. */
if (sub != NULL) {
- new = format_substitute(value, sub->argv[0], sub->argv[1]);
- format_log(ft, "substituted '%s' to '%s: %s", sub->argv[0],
+ new = format_sub(sub, value, sub->argv[0], sub->argv[1]);
+ format_log(ft, "substituted '%s' to '%s': %s", sub->argv[0],
sub->argv[1], new);
free(value);
value = new;
diff --git a/layout-custom.c b/layout-custom.c
index 9886afe1..bedcaf10 100644
--- a/layout-custom.c
+++ b/layout-custom.c
@@ -122,7 +122,7 @@ layout_parse(struct window *w, const char *layout)
{
struct layout_cell *lc, *lcchild;
struct window_pane *wp;
- u_int npanes, ncells, sx, sy;
+ u_int npanes, ncells;
u_short csum;
/* Check validity. */
@@ -153,8 +153,7 @@ layout_parse(struct window *w, const char *layout)
layout_destroy_cell(w, lcchild, &lc);
}
- /* Save the old window size and resize to the layout size. */
- sx = w->sx; sy = w->sy;
+ /* Resize to the layout size. */
window_resize(w, lc->sx, lc->sy);
/* Destroy the old layout and swap to the new. */
@@ -168,10 +167,7 @@ layout_parse(struct window *w, const char *layout)
/* Update pane offsets and sizes. */
layout_fix_offsets(lc);
layout_fix_panes(w);
-
- /* Then resize the layout back to the original window size. */
- layout_resize(w, sx, sy);
- window_resize(w, sx, sy);
+ recalculate_sizes();
layout_print_cell(lc, __func__, 0);
diff --git a/menu.c b/menu.c
index fd35399b..cc21c796 100644
--- a/menu.c
+++ b/menu.c
@@ -161,7 +161,7 @@ menu_free_cb(struct client *c)
struct menu_data *md = c->overlay_data;
if (md->item != NULL)
- md->item->flags &= ~CMDQ_WAITING;
+ cmdq_continue(md->item);
if (md->cb != NULL)
md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data);
diff --git a/mode-tree.c b/mode-tree.c
index 75034675..20ac3226 100644
--- a/mode-tree.c
+++ b/mode-tree.c
@@ -480,7 +480,7 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
saved = mode_tree_find_item(&mtd->saved, tag);
if (saved != NULL) {
- if (parent == NULL || (parent != NULL && parent->expanded))
+ if (parent == NULL || parent->expanded)
mti->tagged = saved->tagged;
mti->expanded = saved->expanded;
} else if (expanded == -1)
diff --git a/options.c b/options.c
index 26ce3b6f..619c27ed 100644
--- a/options.c
+++ b/options.c
@@ -365,7 +365,8 @@ options_array_set(struct options_entry *o, u_int idx, const char *value,
pr = cmd_parse_from_string(value, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
- *cause = xstrdup("empty command");
+ if (cause != NULL)
+ *cause = xstrdup("empty command");
return (-1);
case CMD_PARSE_ERROR:
if (cause != NULL)
diff --git a/regress/command-order.sh b/regress/command-order.sh
index fb4e0324..04046f0d 100644
--- a/regress/command-order.sh
+++ b/regress/command-order.sh
@@ -1,7 +1,5 @@
#!/bin/sh
-# new-session without clients should be the right size
-
PATH=/bin:/usr/bin
TERM=screen
@@ -19,6 +17,7 @@ EOF
$TMUX -f$TMP start </dev/null || exit 1
sleep 1
$TMUX lsw -aF '#{session_name},#{window_name}'|sort >$TMP || exit 1
+$TMUX kill-server 2>/dev/null
cat <<EOF|cmp -s $TMP - || exit 1
bar,bar0
bar,bar1
@@ -27,7 +26,6 @@ foo,foo0
foo,foo1
foo,foo2
EOF
-$TMUX kill-server 2>/dev/null
cat <<EOF >$TMP
new -sfoo -nfoo0
@@ -40,6 +38,7 @@ EOF
$TMUX -f$TMP start </dev/null || exit 1
sleep 1
$TMUX lsw -aF '#{session_name},#{window_name}'|sort >$TMP || exit 1
+$TMUX kill-server 2>/dev/null
cat <<EOF|cmp -s $TMP - || exit 1
bar,bar0
bar,bar1
@@ -48,6 +47,5 @@ foo,foo0
foo,foo1
foo,foo2
EOF
-$TMUX kill-server 2>/dev/null
exit 0
diff --git a/regress/conf/21867280ff7e99631046f9cc669b80d2.conf b/regress/conf/21867280ff7e99631046f9cc669b80d2.conf
new file mode 100644
index 00000000..43b142b4
--- /dev/null
+++ b/regress/conf/21867280ff7e99631046f9cc669b80d2.conf
@@ -0,0 +1,8 @@
+%if #{l:1}
+set -g status-style fg=cyan,bg='#001040'
+%elif #{l:1}
+set -g status-style fg=white,bg='#400040'
+%else
+set -g status-style fg=white,bg='#800000'
+%endif
+bind ^X last-window
diff --git a/regress/conf/99749670b62bcb99a9b2e3d59708e357.conf b/regress/conf/99749670b62bcb99a9b2e3d59708e357.conf
new file mode 100644
index 00000000..dd1700b0
--- /dev/null
+++ b/regress/conf/99749670b62bcb99a9b2e3d59708e357.conf
@@ -0,0 +1,93 @@
+# -----------------------------------------------------------------------------
+# This config is targeted for tmux 2.1+ and should be placed in $HOME.
+#
+# Read the "Plugin Manager" section (bottom) before trying to use this config!
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# Global options
+# -----------------------------------------------------------------------------
+
+# Set a new prefix / leader key.
+set -g prefix `
+bind ` send-prefix
+
+# Allow opening multiple terminals to view the same session at different sizes.
+setw -g aggressive-resize on
+
+# Remove delay when switching between Vim modes.
+set -s escape-time 0
+
+# Allow Vim's FocusGained to work when your terminal gains focus.
+# Requires Vim plugin: https://github.com/tmux-plugins/vim-tmux-focus-events
+set -g focus-events on
+
+# Add a bit more scroll history in the buffer.
+set -g history-limit 50000
+
+# Enable color support inside of tmux.
+set -g default-terminal "screen-256color"
+
+# Ensure window titles get renamed automatically.
+setw -g automatic-rename
+
+# Start windows and panes index at 1, not 0.
+set -g base-index 1
+setw -g pane-base-index 1
+
+# Enable full mouse support.
+set -g mouse on
+
+# Status bar optimized for Gruvbox.
+set -g status-fg colour244
+set -g status-bg default
+set -g status-left ''
+set -g status-right-length 0
+#set -g status-right-length 20
+#set -g status-right '%a %Y-%m-%d %H:%M'
+
+set -g pane-border-fg default
+set -g pane-border-bg default
+set -g pane-active-border-fg colour250
+set -g pane-active-border-bg default
+
+set-window-option -g window-status-current-attr bold
+set-window-option -g window-status-current-fg colour223
+
+# -----------------------------------------------------------------------------
+# Key bindings
+# -----------------------------------------------------------------------------
+
+# Unbind default keys
+unbind C-b
+unbind '"'
+unbind %
+
+# Reload the tmux config.
+bind-key r source-file ~/.tmux.conf
+
+# Split panes.
+bind-key h split-window -v
+bind-key v split-window -h
+
+# Move around panes with ALT + arrow keys.
+bind-key -n M-Up select-pane -U
+bind-key -n M-Left select-pane -L
+bind-key -n M-Down select-pane -D
+bind-key -n M-Right select-pane -R
+
+# -----------------------------------------------------------------------------
+# Plugin Manager - https://github.com/tmux-plugins/tpm
+# In order to use the plugins below you need to install TPM and the plugins.
+# Step 1) git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
+# Step 2) Reload tmux if it's already started with `r
+# Step 3) Launch tmux and hit `I (capital i) to fetch any plugins
+# -----------------------------------------------------------------------------
+
+# List of plugins.
+set -g @plugin 'tmux-plugins/tpm'
+set -g @plugin 'tmux-plugins/tmux-resurrect'
+set -g @plugin 'tmux-plugins/tmux-yank'
+
+# Initialize TPM (keep this line at the very bottom of your tmux.conf).
+run -b '~/.tmux/plugins/tpm/tpm'
diff --git a/regress/xenl-terminal.sh b/regress/xenl-terminal.sh
new file mode 100644
index 00000000..07469ceb
--- /dev/null
+++ b/regress/xenl-terminal.sh
@@ -0,0 +1,29 @@
+#!/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
+TMUX2="$TEST_TMUX -Ltest2"
+$TMUX2 kill-server 2>/dev/null
+
+TMP=$(mktemp)
+trap "rm -f $TMP" 0 1 15
+
+$TMUX2 -f/dev/null new -d || exit 1
+$TMUX2 set -as terminal-overrides ',*:xenl@' || exit 1
+$TMUX2 set -g status-right 'RRR' || exit 1
+$TMUX2 set -g status-left 'LLL' || exit 1
+$TMUX2 set -g window-status-current-format 'WWW' || exit 1
+$TMUX -f/dev/null new -x20 -y2 -d "$TMUX2 attach" || exit 1
+sleep 1
+$TMUX capturep -p|tail -1 >$TMP || exit 1
+$TMUX kill-server 2>/dev/null
+$TMUX2 kill-server 2>/dev/null
+cat <<EOF|cmp -s $TMP - || exit 1
+LLLWWW RR
+EOF
+
+exit 0
diff --git a/regsub.c b/regsub.c
new file mode 100644
index 00000000..53c83a8f
--- /dev/null
+++ b/regsub.c
@@ -0,0 +1,98 @@
+/* $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 <regex.h>
+#include <string.h>
+
+#include "tmux.h"
+
+static void
+regsub_copy(char **buf, size_t *len, const char *text, size_t start,
+ size_t end)
+{
+ size_t add = end - start;
+
+ *buf = xrealloc(*buf, (*len) + add + 1);
+ memcpy((*buf) + *len, text + start, add);
+ (*len) += add;
+}
+
+static void
+regsub_expand(char **buf, size_t *len, const char *with, const char *text,
+ regmatch_t *m, u_int n)
+{
+ const char *cp;
+ u_int i;
+
+ for (cp = with; *cp != '\0'; cp++) {
+ if (*cp == '\\') {
+ cp++;
+ if (*cp >= '0' && *cp <= '9') {
+ i = *cp - '0';
+ if (i < n && m[i].rm_so != m[i].rm_eo) {
+ regsub_copy(buf, len, text, m[i].rm_so,
+ m[i].rm_eo);
+ continue;
+ }
+ }
+ }
+ *buf = xrealloc(*buf, (*len) + 2);
+ (*buf)[(*len)++] = *cp;
+ }
+}
+
+char *
+regsub(const char *pattern, const char *with, const char *text, int flags)
+{
+ regex_t r;
+ regmatch_t m[10];
+ size_t start, end, len = 0;
+ char *buf = NULL;
+
+ if (*text == '\0')
+ return (xstrdup(""));
+ if (regcomp(&r, pattern, flags) != 0)
+ return (NULL);
+
+ start = 0;
+ end = strlen(text);
+
+ while (start != end) {
+ m[0].rm_so = start;
+ m[0].rm_eo = end;
+
+ if (regexec(&r, text, nitems(m), m, REG_STARTEND) != 0) {
+ regsub_copy(&buf, &len, text, start, end);
+ break;
+ }
+ if (m[0].rm_so == m[0].rm_eo) {
+ regsub_copy(&buf, &len, text, start, end);
+ break;
+ }
+
+ regsub_copy(&buf, &len, text, start, m[0].rm_so);
+ regsub_expand(&buf, &len, with, text, m, nitems(m));
+ start = m[0].rm_eo;
+ }
+ buf[len] = '\0';
+
+ regfree(&r);
+ return (buf);
+}
diff --git a/server-client.c b/server-client.c
index 7381eb52..ae41278a 100644
--- a/server-client.c
+++ b/server-client.c
@@ -1239,6 +1239,8 @@ server_client_loop(void)
struct client *c;
struct window *w;
struct window_pane *wp;
+ struct winlink *wl;
+ struct session *s;
int focus;
TAILQ_FOREACH(c, &clients, entry) {
@@ -1255,8 +1257,13 @@ server_client_loop(void)
*/
focus = options_get_number(global_options, "focus-events");
RB_FOREACH(w, windows, &windows) {
+ TAILQ_FOREACH(wl, &w->winlinks, wentry) {
+ s = wl->session;
+ if (s->attached != 0 && s->curw == wl)
+ break;
+ }
TAILQ_FOREACH(wp, &w->panes, entry) {
- if (wp->fd != -1) {
+ if (wl != NULL && wp->fd != -1) {
if (focus)
server_client_check_focus(wp);
server_client_check_resize(wp);
@@ -1521,7 +1528,9 @@ server_client_click_timer(__unused int fd, __unused short events, void *data)
static void
server_client_check_exit(struct client *c)
{
- if (!(c->flags & CLIENT_EXIT))
+ if (~c->flags & CLIENT_EXIT)
+ return;
+ if (c->flags & CLIENT_EXITED)
return;
if (EVBUFFER_LENGTH(c->stdin_data) != 0)
@@ -1534,7 +1543,7 @@ server_client_check_exit(struct client *c)
if (c->flags & CLIENT_ATTACHED)
notify_client("client-detached", c);
proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval);
- c->flags &= ~CLIENT_EXIT;
+ c->flags |= CLIENT_EXITED;
}
/* Redraw timer callback. */
diff --git a/server.c b/server.c
index af966501..eceb83e5 100644
--- a/server.c
+++ b/server.c
@@ -43,7 +43,7 @@
struct clients clients;
struct tmuxproc *server_proc;
-static int server_fd;
+static int server_fd = -1;
static int server_exit;
static struct event server_ev_accept;
@@ -207,11 +207,10 @@ server_start(struct tmuxproc *client, struct event_base *base, int lockfd,
cmdq_append(c, cmdq_get_error(cause));
free(cause);
c->flags |= CLIENT_EXIT;
- }
+ } else
+ start_cfg();
- start_cfg();
server_add_accept(0);
-
proc_loop(server_proc, server_loop);
job_kill_all();
@@ -363,6 +362,9 @@ server_add_accept(int timeout)
{
struct timeval tv = { timeout, 0 };
+ if (server_fd == -1)
+ return;
+
if (event_initialized(&server_ev_accept))
event_del(&server_ev_accept);
diff --git a/tmux.1 b/tmux.1
index 286192a3..9188ba47 100644
--- a/tmux.1
+++ b/tmux.1
@@ -520,7 +520,11 @@ the given four or eight digit hexadecimal number.
When preceded (escaped) by a \e, the following characters are replaced: \ee by
the escape character; \er by a carriage return; \en by a newline; and \et by a
tab.
-.Pp
+.It
+\eooo is replaced by a character of the octal value ooo.
+Three octal digits are required, for example \e001.
+The largest valid character is \e377.
+.It
Any other characters preceded by \e are replaced by themselves (that is, the \e
is removed) and are not treated as having any special meaning - so for example
\e; will not mark a command sequence and \e$ will not expand an environment
@@ -919,7 +923,7 @@ section.
The following commands are available to manage clients and sessions:
.Bl -tag -width Ds
.It Xo Ic attach-session
-.Op Fl dEr
+.Op Fl dErx
.Op Fl c Ar working-directory
.Op Fl t Ar target-session
.Xc
@@ -932,6 +936,10 @@ If used from inside, switch the current client.
If
.Fl d
is specified, any other clients attached to the session are detached.
+If
+.Fl x
+is given, send SIGHUP to the parent process of the client as well as
+detaching the client, typically causing it to exit.
.Fl r
signifies the client is read-only (only keys bound to the
.Ic detach-client
@@ -1049,7 +1057,7 @@ command.
Lock all clients attached to
.Ar target-session .
.It Xo Ic new-session
-.Op Fl AdDEP
+.Op Fl AdDEPX
.Op Fl c Ar start-directory
.Op Fl F Ar format
.Op Fl n Ar window-name
@@ -1106,6 +1114,12 @@ already exists; in this case,
behaves like
.Fl d
to
+.Ic attach-session ,
+and
+.Fl X
+behaves like
+.Fl x
+to
.Ic attach-session .
.Pp
If
@@ -1242,7 +1256,7 @@ and
.Fl T
show debugging information about jobs and terminals.
.It Xo Ic source-file
-.Op Fl nq
+.Op Fl nqv
.Ar path
.Ar ...
.Xc
@@ -1260,6 +1274,8 @@ does not exist.
With
.Fl n ,
the file is parsed but no commands are executed.
+.Fl v
+shows the parsed commands and line numbers if possible.
.It Ic start-server
.D1 (alias: Ic start )
Start the
@@ -4001,25 +4017,45 @@ if running on
.Ql myhost ,
otherwise by
.Ql 0 .
-An
-.Ql m
-specifies an
-.Xr fnmatch 3
-comparison where the first argument is the pattern and the second the string to
-compare, for example
-.Ql #{m:*foo*,#{host}} .
.Ql ||
and
.Ql &&
evaluate to true if either or both of two comma-separated alternatives are
true, for example
.Ql #{||:#{pane_in_mode},#{alternate_on}} .
+.Pp
+An
+.Ql m
+specifies an
+.Xr fnmatch 3
+or regular expression comparison.
+The first argument is the pattern and the second the string to compare.
+An optional third argument specifies flags:
+.Ql r
+means the pattern is a regular expression instead of the default
+.Xr fnmatch 3
+pattern, and
+.Ql i
+means to ignore case.
+For example:
+.Ql #{m:*foo*,#{host}}
+or
+.Ql #{m/ri:^A,MYVAR} .
A
.Ql C
performs a search for an
.Xr fnmatch 3
-pattern in the pane content and evaluates to zero if not found, or a line
-number if found.
+pattern or regular expression in the pane content and evaluates to zero if not
+found, or a line number if found.
+Like
+.Ql m ,
+an
+.Ql r
+flag means search for a regular expression and
+.Ql i
+ignores case.
+For example:
+.Ql #{C/r:^Start}
.Pp
A limit may be placed on the length of the resultant string by prefixing it
by an
@@ -4092,6 +4128,14 @@ will substitute
with
.Ql bar
throughout.
+The first argument may be an extended regular expression and a final argument may be
+.Ql i
+to ignore case, for example
+.Ql s/a(.)/\e1x/i:
+would change
+.Ql abABab
+into
+.Ql bxBxbx .
.Pp
In addition, the first line of a shell command's output may be inserted using
.Ql #() .
@@ -4328,7 +4372,7 @@ Align text to the left, centre or right of the available space if appropriate.
.It Xo Ic list=on ,
.Ic list=focus ,
.Ic list=left-marker ,
-.Ic list=right=marker ,
+.Ic list=right-marker ,
.Ic nolist
.Xc
Mark the position of the various window list components in the
diff --git a/tmux.h b/tmux.h
index 3aa6aa81..3473366e 100644
--- a/tmux.h
+++ b/tmux.h
@@ -1326,6 +1326,7 @@ struct cmd_parse_input {
#define CMD_PARSE_QUIET 0x1
#define CMD_PARSE_PARSEONLY 0x2
#define CMD_PARSE_NOALIAS 0x4
+#define CMD_PARSE_VERBOSE 0x8
const char *file;
u_int line;
@@ -1491,7 +1492,7 @@ struct client {
#define CLIENT_REPEAT 0x20
#define CLIENT_SUSPENDED 0x40
#define CLIENT_ATTACHED 0x80
-/* 0x100 unused */
+#define CLIENT_EXITED 0x100
#define CLIENT_DEAD 0x200
#define CLIENT_REDRAWBORDERS 0x400
#define CLIENT_READONLY 0x800
@@ -2011,7 +2012,7 @@ extern const struct cmd_entry *cmd_table[];
/* cmd-attach-session.c */
enum cmd_retval cmd_attach_session(struct cmdq_item *, const char *, int, int,
- const char *, int);
+ int, const char *, int);
/* cmd-parse.c */
void cmd_parse_empty(struct cmd_parse_input *);
@@ -2038,6 +2039,7 @@ 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 cmdq_continue(struct cmdq_item *);
void printflike(3, 4) cmdq_format(struct cmdq_item *, const char *,
const char *, ...);
u_int cmdq_next(struct client *);
@@ -2397,7 +2399,8 @@ void window_pane_key(struct window_pane *, struct client *,
struct session *, struct winlink *, key_code,
struct mouse_event *);
int window_pane_visible(struct window_pane *);
-u_int window_pane_search(struct window_pane *, const char *);
+u_int window_pane_search(struct window_pane *, const char *, int,
+ int);
const char *window_printable_flags(struct winlink *);
struct window_pane *window_pane_find_up(struct window_pane *);
struct window_pane *window_pane_find_down(struct window_pane *);
@@ -2634,4 +2637,7 @@ int style_is_default(struct style *);
struct winlink *spawn_window(struct spawn_context *, char **);
struct window_pane *spawn_pane(struct spawn_context *, char **);
+/* regsub.c */
+char *regsub(const char *, const char *, const char *, int);
+
#endif /* TMUX_H */
diff --git a/tty-term.c b/tty-term.c
index b3fc8e0d..d2cdf86a 100644
--- a/tty-term.c
+++ b/tty-term.c
@@ -691,7 +691,7 @@ tty_term_describe(struct tty_term *term, enum tty_code_code code)
break;
case TTYCODE_STRING:
strnvis(out, term->codes[code].value.string, sizeof out,
- VIS_OCTAL|VIS_TAB|VIS_NL);
+ VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
xsnprintf(s, sizeof s, "%4u: %s: (string) %s",
code, tty_term_codes[code].name,
out);
diff --git a/tty.c b/tty.c
index 34403a1f..05df4f0f 100644
--- a/tty.c
+++ b/tty.c
@@ -527,6 +527,12 @@ tty_putc(struct tty *tty, u_char ch)
{
const char *acs;
+ if ((tty->term->flags & TERM_EARLYWRAP) &&
+ ch >= 0x20 && ch != 0x7f &&
+ tty->cy == tty->sy - 1 &&
+ tty->cx + 1 >= tty->sx)
+ return;
+
if (tty->cell.attr & GRID_ATTR_CHARSET) {
acs = tty_acs_get(tty, ch);
if (acs != NULL)
@@ -557,6 +563,11 @@ tty_putc(struct tty *tty, u_char ch)
void
tty_putn(struct tty *tty, const void *buf, size_t len, u_int width)
{
+ if ((tty->term->flags & TERM_EARLYWRAP) &&
+ tty->cy == tty->sy - 1 &&
+ tty->cx + len >= tty->sx)
+ len = tty->sx - tty->cx - 1;
+
tty_add(tty, buf, len);
if (tty->cx + width > tty->sx) {
tty->cx = (tty->cx + width) - tty->sx;
diff --git a/window-buffer.c b/window-buffer.c
index d65916b5..224dfedb 100644
--- a/window-buffer.c
+++ b/window-buffer.c
@@ -245,7 +245,7 @@ window_buffer_draw(__unused void *modedata, void *itemdata,
at = 0;
while (end != pdata + psize && *end != '\n') {
if ((sizeof line) - at > 5) {
- cp = vis(line + at, *end, VIS_TAB|VIS_OCTAL, 0);
+ cp = vis(line + at, *end, VIS_OCTAL|VIS_TAB, 0);
at = cp - line;
}
end++;
diff --git a/window-copy.c b/window-copy.c
index d81073bf..a7cb5dfa 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -3027,8 +3027,8 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only)
if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
window_copy_other_end(wme);
- data->cx = data->lastcx;
if (scroll_only || data->cy == 0) {
+ data->cx = data->lastcx;
window_copy_scroll_down(wme, 1);
if (scroll_only) {
if (data->cy == screen_size_y(s) - 1)
@@ -3037,7 +3037,7 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only)
window_copy_redraw_lines(wme, data->cy, 2);
}
} else {
- window_copy_update_cursor(wme, data->cx, data->cy - 1);
+ window_copy_update_cursor(wme, data->lastcx, data->cy - 1);
if (window_copy_update_selection(wme, 1)) {
if (data->cy == screen_size_y(s) - 1)
window_copy_redraw_lines(wme, data->cy, 1);
@@ -3077,13 +3077,13 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only)
if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
window_copy_other_end(wme);
- data->cx = data->lastcx;
if (scroll_only || data->cy == screen_size_y(s) - 1) {
+ data->cx = data->lastcx;
window_copy_scroll_up(wme, 1);
if (scroll_only && data->cy > 0)
window_copy_redraw_lines(wme, data->cy - 1, 2);
} else {
- window_copy_update_cursor(wme, data->cx, data->cy + 1);
+ window_copy_update_cursor(wme, data->lastcx, data->cy + 1);
if (window_copy_update_selection(wme, 1))
window_copy_redraw_lines(wme, data->cy - 1, 2);
}
diff --git a/window.c b/window.c
index 1b30c793..e90c4e09 100644
--- a/window.c
+++ b/window.c
@@ -22,6 +22,7 @@
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
+#include <regex.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
@@ -802,6 +803,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
wp->pipe_event = NULL;
wp->saved_grid = NULL;
+ wp->saved_cx = UINT_MAX;
+ wp->saved_cy = UINT_MAX;
style_set(&wp->style, &grid_default_cell);
@@ -963,10 +966,25 @@ window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc,
struct screen *s = &wp->base;
u_int sx, sy;
- if (wp->saved_grid == NULL)
- return;
if (!options_get_number(wp->window->options, "alternate-screen"))
return;
+
+ /*
+ * Restore the cursor position and cell. This happens even if not
+ * currently in the alternate screen.
+ */
+ if (cursor && wp->saved_cx != UINT_MAX && wp->saved_cy != UINT_MAX) {
+ s->cx = wp->saved_cx;
+ if (s->cx > screen_size_x(s) - 1)
+ s->cx = screen_size_x(s) - 1;
+ s->cy = wp->saved_cy;
+ if (s->cy > screen_size_y(s) - 1)
+ s->cy = screen_size_y(s) - 1;
+ memcpy(gc, &wp->saved_cell, sizeof *gc);
+ }
+
+ if (wp->saved_grid == NULL)
+ return;
sx = screen_size_x(s);
sy = screen_size_y(s);
@@ -977,17 +995,8 @@ window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc,
if (sy > wp->saved_grid->sy)
screen_resize(s, sx, wp->saved_grid->sy, 1);
- /* Restore the grid, cursor position and cell. */
+ /* Restore the saved grid. */
grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy);
- if (cursor)
- s->cx = wp->saved_cx;
- if (s->cx > screen_size_x(s) - 1)
- s->cx = screen_size_x(s) - 1;
- if (cursor)
- s->cy = wp->saved_cy;
- if (s->cy > screen_size_y(s) - 1)
- s->cy = screen_size_y(s) - 1;
- memcpy(gc, &wp->saved_cell, sizeof *gc);
/*
* Turn history back on (so resize can use it) and then resize back to
@@ -1137,7 +1146,7 @@ window_pane_reset_mode(struct window_pane *wp)
} else {
log_debug("%s: next mode is %s", __func__, next->mode->name);
wp->screen = next->screen;
- if (next != NULL && next->mode->resize != NULL)
+ if (next->mode->resize != NULL)
next->mode->resize(next, wp->sx, wp->sy);
}
wp->flags |= (PANE_REDRAW|PANE_CHANGED);
@@ -1199,24 +1208,41 @@ window_pane_visible(struct window_pane *wp)
}
u_int
-window_pane_search(struct window_pane *wp, const char *searchstr)
+window_pane_search(struct window_pane *wp, const char *term, int regex,
+ int ignore)
{
struct screen *s = &wp->base;
- char *newsearchstr, *line;
+ regex_t r;
+ char *new = NULL, *line;
u_int i;
+ int flags = 0, found;
- xasprintf(&newsearchstr, "*%s*", searchstr);
+ if (!regex) {
+ if (ignore)
+ flags |= FNM_CASEFOLD;
+ xasprintf(&new, "*%s*", term);
+ } else {
+ if (ignore)
+ flags |= REG_ICASE;
+ if (regcomp(&r, term, flags|REG_EXTENDED) != 0)
+ return (0);
+ }
for (i = 0; i < screen_size_y(s); i++) {
line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s));
- if (fnmatch(newsearchstr, line, 0) == 0) {
- free(line);
- break;
- }
+ if (!regex)
+ found = (fnmatch(new, line, 0) == 0);
+ else
+ found = (regexec(&r, line, 0, NULL, 0) == 0);
free(line);
+ if (found)
+ break;
}
+ if (!regex)
+ free(new);
+ else
+ regfree(&r);
- free(newsearchstr);
if (i == screen_size_y(s))
return (0);
return (i + 1);
@@ -1487,7 +1513,7 @@ window_pane_input_callback(struct client *c, int closed, void *data)
c->stdin_callback = NULL;
server_client_unref(c);
- cdata->item->flags &= ~CMDQ_WAITING;
+ cmdq_continue(cdata->item);
free(cdata);
return;