aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/README.md11
-rw-r--r--CHANGES36
-rw-r--r--alerts.c10
-rw-r--r--client.c27
-rw-r--r--cmd-attach-session.c5
-rw-r--r--cmd-bind-key.c42
-rw-r--r--cmd-display-menu.c6
-rw-r--r--cmd-display-message.c16
-rw-r--r--cmd-display-panes.c91
-rw-r--r--cmd-list-keys.c2
-rw-r--r--cmd-load-buffer.c24
-rw-r--r--cmd-queue.c2
-rw-r--r--cmd-select-pane.c1
-rw-r--r--cmd-set-buffer.c10
-rw-r--r--cmd-set-environment.c25
-rw-r--r--cmd-source-file.c14
-rw-r--r--cmd-unbind-key.c54
-rw-r--r--colour.c6
-rw-r--r--compat.h8
-rw-r--r--compat/getdtablesize.c29
-rw-r--r--compat/imsg-buffer.c4
-rw-r--r--compat/imsg.h10
-rw-r--r--configure.ac34
-rw-r--r--control.c4
-rw-r--r--environ.c7
-rw-r--r--format.c471
-rw-r--r--grid.c24
-rw-r--r--input-keys.c2
-rw-r--r--input.c51
-rw-r--r--key-bindings.c10
-rw-r--r--menu.c25
-rw-r--r--mode-tree.c2
-rw-r--r--options-table.c9
-rw-r--r--options.c42
-rw-r--r--popup.c2
-rw-r--r--proc.c1
-rw-r--r--regress/style-trim.sh93
-rw-r--r--resize.c375
-rw-r--r--screen.c2
-rw-r--r--server-client.c20
-rw-r--r--server-fn.c1
-rw-r--r--server.c8
-rw-r--r--status.c12
-rw-r--r--tmux.189
-rw-r--r--tmux.c4
-rw-r--r--tmux.h19
-rw-r--r--tty-features.c3
-rw-r--r--tty-term.c4
-rw-r--r--tty.c54
-rw-r--r--window-copy.c156
-rw-r--r--window-customize.c10
51 files changed, 1278 insertions, 689 deletions
diff --git a/.github/README.md b/.github/README.md
index c1c35534..5590d0b2 100644
--- a/.github/README.md
+++ b/.github/README.md
@@ -19,6 +19,12 @@ suitable yacc (yacc or bison) are needed.
## Installation
+### Binary packages
+
+Some platforms provide binary packages for tmux, although these are sometimes
+out of date. Examples are listed on
+[this page](https://github.com/tmux/tmux/wiki/Installing).
+
### From release tarball
To build and install tmux from a release tarball, use:
@@ -31,6 +37,9 @@ sudo make install
tmux can use the utempter library to update utmp(5), if it is installed - run
configure with `--enable-utempter` to enable this.
+For more detailed instructions on building and installing tmux, see
+[this page](https://github.com/tmux/tmux/wiki/Installing).
+
### From version control
To get and build the latest from version control - note that this requires
@@ -72,7 +81,7 @@ And a bash(1) completion file at:
https://github.com/imomaliev/tmux-bash-completion
-For debugging, run tmux with `-v` or `-vv` to generate server and client log
+For debugging, run tmux with `-v` or `-vv` to generate server and client log
files in the current directory.
## Support
diff --git a/CHANGES b/CHANGES
index cf00a5b0..d385ed16 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,33 @@
-CHANGES FROM 3.1b TO 3.2
+CHANGES FROM 3.2 TO 3.3
+
+* Fire focus events even when the pane is in a mode.
+
+* Add -O flag to display-menu to not automatically close when all mouse buttons
+ are released.
+
+* Allow fnmatch(3) wildcards in update-environment.
+
+* Disable nested job expansion so that the result of #() is not expanded again.
+
+* Use the setal capability as well as (tmux's) Setulc.
+
+* Add -q flag to unbind-key to hide errors.
+
+* Allow -N without a command to change or add a note to an existing key.
+
+* Add a -w flag to set- and load-buffer to send to clipboard using OSC 52.
+
+* Add -F to set-environment and source-file.
+
+* Allow colour to be spelt as color in various places.
+
+* Add n: modifier to get length of a format.
+
+* Respond to OSC colour requests if a colour is available.
+
+* Add a -d option to display-message to set delay.
+
+CHANGES FROM 3.1c TO 3.2
* Add a way for control mode clients to subscribe to a format and be notified
of changes rather than having to poll.
@@ -265,6 +294,11 @@ CHANGES FROM 3.1b TO 3.2
* Add number operators for formats (+, -, *, / and m),
+CHANGED FROM 3.1b TO 3.1c
+
+* Do not write after the end of the array and overwrite the stack when
+ colon-separated SGR sequences contain empty arguments.
+
CHANGES FROM 3.1a TO 3.1b
* Fix build on systems without sys/queue.h.
diff --git a/alerts.c b/alerts.c
index 9675934a..0f2eb179 100644
--- a/alerts.c
+++ b/alerts.c
@@ -200,7 +200,7 @@ alerts_check_bell(struct window *w)
* not check WINLINK_BELL).
*/
s = wl->session;
- if (s->curw != wl) {
+ if (s->curw != wl || s->attached == 0) {
wl->flags |= WINLINK_BELL;
server_status_session(s);
}
@@ -236,7 +236,7 @@ alerts_check_activity(struct window *w)
if (wl->flags & WINLINK_ACTIVITY)
continue;
s = wl->session;
- if (s->curw != wl) {
+ if (s->curw != wl || s->attached == 0) {
wl->flags |= WINLINK_ACTIVITY;
server_status_session(s);
}
@@ -272,7 +272,7 @@ alerts_check_silence(struct window *w)
if (wl->flags & WINLINK_SILENCE)
continue;
s = wl->session;
- if (s->curw != wl) {
+ if (s->curw != wl || s->attached == 0) {
wl->flags |= WINLINK_SILENCE;
server_status_session(s);
}
@@ -316,9 +316,9 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option)
if (visual == VISUAL_OFF)
continue;
if (c->session->curw == wl)
- status_message_set(c, 1, "%s in current window", type);
+ status_message_set(c, -1, 1, "%s in current window", type);
else {
- status_message_set(c, 1, "%s in window %d", type,
+ status_message_set(c, -1, 1, "%s in window %d", type,
wl->idx);
}
}
diff --git a/client.c b/client.c
index b652754b..757e4aa8 100644
--- a/client.c
+++ b/client.c
@@ -36,6 +36,7 @@
static struct tmuxproc *client_proc;
static struct tmuxpeer *client_peer;
static uint64_t client_flags;
+static int client_suspended;
static enum {
CLIENT_EXIT_NONE,
CLIENT_EXIT_DETACHED,
@@ -59,7 +60,8 @@ static struct client_files client_files = RB_INITIALIZER(&client_files);
static __dead void client_exec(const char *,const char *);
static int client_get_lock(char *);
-static int client_connect(struct event_base *, const char *, int);
+static int client_connect(struct event_base *, const char *,
+ uint64_t);
static void client_send_identify(const char *, const char *, int);
static void client_signal(int);
static void client_dispatch(struct imsg *, void *);
@@ -100,7 +102,7 @@ client_get_lock(char *lockfile)
/* Connect client to server. */
static int
-client_connect(struct event_base *base, const char *path, int flags)
+client_connect(struct event_base *base, const char *path, uint64_t flags)
{
struct sockaddr_un sa;
size_t size;
@@ -220,7 +222,7 @@ static void
client_exit(void)
{
struct client_file *cf;
- size_t left;
+ size_t left;
int waiting = 0;
RB_FOREACH (cf, client_files, &client_files) {
@@ -238,7 +240,8 @@ client_exit(void)
/* Client main loop. */
int
-client_main(struct event_base *base, int argc, char **argv, int flags, int feat)
+client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
+ int feat)
{
struct cmd_parse_result *pr;
struct msg_command *data;
@@ -284,7 +287,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags, int feat)
/* Save the flags. */
client_flags = flags;
- log_debug("flags are %#llx", client_flags);
+ log_debug("flags are %#llx", (unsigned long long)client_flags);
/* Initialize the client socket and start the server. */
fd = client_connect(base, socket_path, client_flags);
@@ -442,6 +445,8 @@ client_send_identify(const char *ttynam, const char *cwd, int feat)
pid_t pid;
proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags);
+ proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &client_flags,
+ sizeof client_flags);
if ((s = getenv("TERM")) == NULL)
s = "";
@@ -761,6 +766,7 @@ client_signal(int sig)
struct sigaction sigact;
int status;
+ log_debug("%s: %s", __func__, strsignal(sig));
if (sig == SIGCHLD)
waitpid(WAIT_ANY, &status, WNOHANG);
else if (!client_attached) {
@@ -774,7 +780,8 @@ client_signal(int sig)
proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
break;
case SIGTERM:
- client_exitreason = CLIENT_EXIT_TERMINATED;
+ if (!client_suspended)
+ client_exitreason = CLIENT_EXIT_TERMINATED;
client_exitval = 1;
proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
break;
@@ -789,6 +796,7 @@ client_signal(int sig)
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
fatal("sigaction failed");
proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0);
+ client_suspended = 0;
break;
}
}
@@ -891,7 +899,8 @@ client_dispatch_wait(struct imsg *imsg)
fatalx("bad MSG_FLAGS string");
memcpy(&client_flags, data, sizeof client_flags);
- log_debug("new flags are %#llx", client_flags);
+ log_debug("new flags are %#llx",
+ (unsigned long long)client_flags);
break;
case MSG_SHELL:
if (datalen == 0 || data[datalen - 1] != '\0')
@@ -944,7 +953,8 @@ client_dispatch_attached(struct imsg *imsg)
fatalx("bad MSG_FLAGS string");
memcpy(&client_flags, data, sizeof client_flags);
- log_debug("new flags are %#llx", client_flags);
+ log_debug("new flags are %#llx",
+ (unsigned long long)client_flags);
break;
case MSG_DETACH:
case MSG_DETACHKILL:
@@ -999,6 +1009,7 @@ client_dispatch_attached(struct imsg *imsg)
sigact.sa_handler = SIG_DFL;
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
fatal("sigaction failed");
+ client_suspended = 1;
kill(getpid(), SIGTSTP);
break;
case MSG_LOCK:
diff --git a/cmd-attach-session.c b/cmd-attach-session.c
index 38d9c024..6a7ebba7 100644
--- a/cmd-attach-session.c
+++ b/cmd-attach-session.c
@@ -59,7 +59,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
struct session *s;
struct winlink *wl;
struct window_pane *wp;
- char *cause;
+ char *cwd, *cause;
enum msgtype msgtype;
if (RB_EMPTY(&sessions)) {
@@ -99,8 +99,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
}
if (cflag != NULL) {
+ cwd = format_single(item, cflag, c, s, wl, wp);
free((void *)s->cwd);
- s->cwd = format_single(item, cflag, c, s, wl, wp);
+ s->cwd = cwd;
}
if (fflag)
server_client_set_flags(c, fflag);
diff --git a/cmd-bind-key.c b/cmd-bind-key.c
index dcb56c06..b4e4167c 100644
--- a/cmd-bind-key.c
+++ b/cmd-bind-key.c
@@ -33,9 +33,9 @@ const struct cmd_entry cmd_bind_key_entry = {
.name = "bind-key",
.alias = "bind",
- .args = { "nrN:T:", 2, -1 },
+ .args = { "nrN:T:", 1, -1 },
.usage = "[-nr] [-T key-table] [-N note] key "
- "command [arguments]",
+ "[command [arguments]]",
.flags = CMD_AFTERHOOK,
.exec = cmd_bind_key_exec
@@ -46,7 +46,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
key_code key;
- const char *tablename, *note;
+ const char *tablename, *note = args_get(args, 'N');
struct cmd_parse_result *pr;
char **argv = args->argv;
int argc = args->argc, repeat;
@@ -65,22 +65,24 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
tablename = "prefix";
repeat = args_has(args, 'r');
- if (argc == 2)
- pr = cmd_parse_from_string(argv[1], NULL);
- else
- pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL);
- switch (pr->status) {
- case CMD_PARSE_EMPTY:
- cmdq_error(item, "empty command");
- return (CMD_RETURN_ERROR);
- case CMD_PARSE_ERROR:
- cmdq_error(item, "%s", pr->error);
- free(pr->error);
- return (CMD_RETURN_ERROR);
- case CMD_PARSE_SUCCESS:
- break;
- }
- note = args_get(args, 'N');
- key_bindings_add(tablename, key, note, repeat, pr->cmdlist);
+ if (argc != 1) {
+ if (argc == 2)
+ pr = cmd_parse_from_string(argv[1], NULL);
+ else
+ pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL);
+ switch (pr->status) {
+ case CMD_PARSE_EMPTY:
+ cmdq_error(item, "empty command");
+ return (CMD_RETURN_ERROR);
+ case CMD_PARSE_ERROR:
+ cmdq_error(item, "%s", pr->error);
+ free(pr->error);
+ return (CMD_RETURN_ERROR);
+ case CMD_PARSE_SUCCESS:
+ break;
+ }
+ key_bindings_add(tablename, key, note, repeat, pr->cmdlist);
+ } else
+ key_bindings_add(tablename, key, note, repeat, NULL);
return (CMD_RETURN_NORMAL);
}
diff --git a/cmd-display-menu.c b/cmd-display-menu.c
index ae322444..205d1243 100644
--- a/cmd-display-menu.c
+++ b/cmd-display-menu.c
@@ -36,8 +36,8 @@ const struct cmd_entry cmd_display_menu_entry = {
.name = "display-menu",
.alias = "menu",
- .args = { "c:t:T:x:y:", 1, -1 },
- .usage = "[-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] "
+ .args = { "c:t:OT:x:y:", 1, -1 },
+ .usage = "[-O] [-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] "
"[-x position] [-y position] name key command ...",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -229,6 +229,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
cmd_display_menu_get_position(tc, item, args, &px, &py, menu->width + 4,
menu->count + 2);
+ if (args_has(args, 'O'))
+ flags |= MENU_STAYOPEN;
if (!event->m.valid)
flags |= MENU_NOMOUSE;
if (menu_display(menu, flags, item, px, py, tc, target, NULL,
diff --git a/cmd-display-message.c b/cmd-display-message.c
index 634f0a93..fc9c4851 100644
--- a/cmd-display-message.c
+++ b/cmd-display-message.c
@@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = {
.name = "display-message",
.alias = "display",
- .args = { "ac:Ipt:F:v", 0, 1 },
- .usage = "[-aIpv] [-c target-client] [-F format] "
+ .args = { "acd:Ipt:F:v", 0, 1 },
+ .usage = "[-aIpv] [-c target-client] [-d delay] [-F format] "
CMD_TARGET_PANE_USAGE " [message]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -68,6 +68,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *wp = target->wp;
const char *template;
char *msg, *cause;
+ int delay = -1;
struct format_tree *ft;
int flags;
@@ -85,6 +86,15 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
+ if (args_has(args, 'd')) {
+ delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause);
+ if (cause != NULL) {
+ cmdq_error(item, "delay %s", cause);
+ free(cause);
+ return (CMD_RETURN_ERROR);
+ }
+ }
+
template = args_get(args, 'F');
if (args->argc != 0)
template = args->argv[0];
@@ -117,7 +127,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'p'))
cmdq_print(item, "%s", msg);
else if (tc != NULL)
- status_message_set(tc, 0, "%s", msg);
+ status_message_set(tc, delay, 0, "%s", msg);
free(msg);
format_free(ft);
diff --git a/cmd-display-panes.c b/cmd-display-panes.c
index a13a06ae..352b2e4d 100644
--- a/cmd-display-panes.c
+++ b/cmd-display-panes.c
@@ -55,11 +55,11 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
struct session *s = c->session;
struct options *oo = s->options;
struct window *w = wp->window;
- struct grid_cell gc;
- u_int idx, px, py, i, j, xoff, yoff, sx, sy;
+ struct grid_cell fgc, bgc;
+ u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy;
int colour, active_colour;
- char buf[16], *ptr;
- size_t len;
+ char buf[16], lbuf[16], rbuf[16], *ptr;
+ size_t len, llen, rlen;
if (wp->xoff + wp->sx <= ctx->ox ||
wp->xoff >= ctx->ox + ctx->sx ||
@@ -109,29 +109,50 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
px = sx / 2;
py = sy / 2;
- if (window_pane_index(wp, &idx) != 0)
+ if (window_pane_index(wp, &pane) != 0)
fatalx("index not found");
- len = xsnprintf(buf, sizeof buf, "%u", idx);
+ len = xsnprintf(buf, sizeof buf, "%u", pane);
if (sx < len)
return;
colour = options_get_number(oo, "display-panes-colour");
active_colour = options_get_number(oo, "display-panes-active-colour");
+ memcpy(&fgc, &grid_default_cell, sizeof fgc);
+ memcpy(&bgc, &grid_default_cell, sizeof bgc);
+ if (w->active == wp) {
+ fgc.fg = active_colour;
+ bgc.bg = active_colour;
+ } else {
+ fgc.fg = colour;
+ bgc.bg = colour;
+ }
+
+ rlen = xsnprintf(rbuf, sizeof rbuf, "%ux%u", wp->sx, wp->sy);
+ if (pane > 9 && pane < 35)
+ llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10));
+ else
+ llen = 0;
+
if (sx < len * 6 || sy < 5) {
- tty_cursor(tty, xoff + px - len / 2, yoff + py);
- goto draw_text;
+ tty_attributes(tty, &fgc, &grid_default_cell, NULL);
+ if (sx >= len + llen + 1) {
+ len += llen + 1;
+ tty_cursor(tty, xoff + px - len / 2, yoff + py);
+ tty_putn(tty, buf, len, len);
+ tty_putn(tty, " ", 1, 1);
+ tty_putn(tty, lbuf, llen, llen);
+ } else {
+ tty_cursor(tty, xoff + px - len / 2, yoff + py);
+ tty_putn(tty, buf, len, len);
+ }
+ goto out;
}
px -= len * 3;
py -= 2;
- memcpy(&gc, &grid_default_cell, sizeof gc);
- if (w->active == wp)
- gc.bg = active_colour;
- else
- gc.bg = colour;
- tty_attributes(tty, &gc, &grid_default_cell, NULL);
+ tty_attributes(tty, &bgc, &grid_default_cell, NULL);
for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9')
continue;
@@ -147,20 +168,20 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
px += 6;
}
- len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy);
- if (sx < len || sy < 6)
- return;
- tty_cursor(tty, xoff + sx - len, yoff);
-
-draw_text:
- memcpy(&gc, &grid_default_cell, sizeof gc);
- if (w->active == wp)
- gc.fg = active_colour;
- else
- gc.fg = colour;
- tty_attributes(tty, &gc, &grid_default_cell, NULL);
- tty_puts(tty, buf);
+ if (sy <= 6)
+ goto out;
+ tty_attributes(tty, &fgc, &grid_default_cell, NULL);
+ if (rlen != 0 && sx >= rlen) {
+ tty_cursor(tty, xoff + sx - rlen, yoff);
+ tty_putn(tty, rbuf, rlen, rlen);
+ }
+ if (llen != 0) {
+ tty_cursor(tty, xoff + sx / 2 + len * 3 - llen - 1,
+ yoff + py + 5);
+ tty_putn(tty, lbuf, llen, llen);
+ }
+out:
tty_cursor(tty, 0, 0);
}
@@ -197,11 +218,21 @@ cmd_display_panes_key(struct client *c, struct key_event *event)
struct window *w = c->session->curw->window;
struct window_pane *wp;
enum cmd_parse_status status;
-
- if (event->key < '0' || event->key > '9')
+ u_int index;
+ key_code key;
+
+ if (event->key >= '0' && event->key <= '9')
+ index = event->key - '0';
+ else if ((event->key & KEYC_MASK_MODIFIERS) == 0) {
+ key = (event->key & KEYC_MASK_KEY);
+ if (key >= 'a' && key <= 'z')
+ index = 10 + (key - 'a');
+ else
+ return (-1);
+ } else
return (-1);
- wp = window_pane_at_index(w, event->key - '0');
+ wp = window_pane_at_index(w, index);
if (wp == NULL)
return (1);
window_unzoom(w);
diff --git a/cmd-list-keys.c b/cmd-list-keys.c
index b3bdbd12..dd82e57e 100644
--- a/cmd-list-keys.c
+++ b/cmd-list-keys.c
@@ -114,7 +114,7 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
note = xstrdup(bd->note);
tmp = utf8_padcstr(key, keywidth + 1);
if (args_has(args, '1') && tc != NULL)
- status_message_set(tc, 1, "%s%s%s", prefix, tmp, note);
+ status_message_set(tc, -1, 1, "%s%s%s", prefix, tmp, note);
else
cmdq_print(item, "%s%s%s", prefix, tmp, note);
free(tmp);
diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c
index 49e834d6..bca9a860 100644
--- a/cmd-load-buffer.c
+++ b/cmd-load-buffer.c
@@ -37,14 +37,15 @@ const struct cmd_entry cmd_load_buffer_entry = {
.name = "load-buffer",
.alias = "loadb",
- .args = { "b:", 1, 1 },
- .usage = CMD_BUFFER_USAGE " path",
+ .args = { "b:t:w", 1, 1 },
+ .usage = CMD_BUFFER_USAGE " " CMD_TARGET_CLIENT_USAGE " path",
- .flags = CMD_AFTERHOOK,
+ .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_load_buffer_exec
};
struct cmd_load_buffer_data {
+ struct client *client;
struct cmdq_item *item;
char *name;
};
@@ -54,6 +55,7 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
int closed, struct evbuffer *buffer, void *data)
{
struct cmd_load_buffer_data *cdata = data;
+ struct client *tc = cdata->client;
struct cmdq_item *item = cdata->item;
void *bdata = EVBUFFER_DATA(buffer);
size_t bsize = EVBUFFER_LENGTH(buffer);
@@ -72,7 +74,12 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
cmdq_error(item, "%s", cause);
free(cause);
free(copy);
- }
+ } else if (tc != NULL &&
+ tc->session != NULL &&
+ (~tc->flags & CLIENT_DEAD))
+ tty_set_selection(&tc->tty, copy, bsize);
+ if (tc != NULL)
+ server_client_unref(tc);
}
cmdq_continue(item);
@@ -84,16 +91,19 @@ static enum cmd_retval
cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
+ struct client *tc = cmdq_get_target_client(item);
struct cmd_load_buffer_data *cdata;
const char *bufname = args_get(args, 'b');
char *path;
- cdata = xmalloc(sizeof *cdata);
+ cdata = xcalloc(1, sizeof *cdata);
cdata->item = item;
if (bufname != NULL)
cdata->name = xstrdup(bufname);
- else
- cdata->name = NULL;
+ if (args_has(args, 'w') && tc != NULL) {
+ cdata->client = tc;
+ cdata->client->references++;
+ }
path = format_single_from_target(item, args->argv[0]);
file_read(cmdq_get_client(item), path, cmd_load_buffer_done, cdata);
diff --git a/cmd-queue.c b/cmd-queue.c
index 693f7d90..36f1c9be 100644
--- a/cmd-queue.c
+++ b/cmd-queue.c
@@ -858,7 +858,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
c->retval = 1;
} else {
*msg = toupper((u_char) *msg);
- status_message_set(c, 1, "%s", msg);
+ status_message_set(c, -1, 1, "%s", msg);
}
free(msg);
diff --git a/cmd-select-pane.c b/cmd-select-pane.c
index b0c78d74..30529722 100644
--- a/cmd-select-pane.c
+++ b/cmd-select-pane.c
@@ -198,6 +198,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'T')) {
title = format_single_from_target(item, args_get(args, 'T'));
if (screen_set_title(&wp->base, title)) {
+ notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}
diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c
index 0f3fffce..94d8cd52 100644
--- a/cmd-set-buffer.c
+++ b/cmd-set-buffer.c
@@ -33,10 +33,11 @@ const struct cmd_entry cmd_set_buffer_entry = {
.name = "set-buffer",
.alias = "setb",
- .args = { "ab:n:", 0, 1 },
- .usage = "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data",
+ .args = { "ab:t:n:w", 0, 1 },
+ .usage = "[-aw] " CMD_BUFFER_USAGE " [-n new-buffer-name] "
+ CMD_TARGET_CLIENT_USAGE " data",
- .flags = CMD_AFTERHOOK,
+ .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_set_buffer_exec
};
@@ -55,6 +56,7 @@ static enum cmd_retval
cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
+ struct client *tc = cmdq_get_target_client(item);
struct paste_buffer *pb;
char *bufdata, *cause;
const char *bufname, *olddata;
@@ -118,6 +120,8 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
free(cause);
return (CMD_RETURN_ERROR);
}
+ if (args_has(args, 'w') && tc != NULL)
+ tty_set_selection(&tc->tty, bufdata, bufsize);
return (CMD_RETURN_NORMAL);
}
diff --git a/cmd-set-environment.c b/cmd-set-environment.c
index 3c43b635..f142df53 100644
--- a/cmd-set-environment.c
+++ b/cmd-set-environment.c
@@ -34,8 +34,8 @@ const struct cmd_entry cmd_set_environment_entry = {
.name = "set-environment",
.alias = "setenv",
- .args = { "hgrt:u", 1, 2 },
- .usage = "[-hgru] " CMD_TARGET_SESSION_USAGE " name [value]",
+ .args = { "Fhgrt:u", 1, 2 },
+ .usage = "[-Fhgru] " CMD_TARGET_SESSION_USAGE " name [value]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
@@ -50,6 +50,8 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item);
struct environ *env;
const char *name, *value, *tflag;
+ char *expand = NULL;
+ enum cmd_retval retval = CMD_RETURN_NORMAL;
name = args->argv[0];
if (*name == '\0') {
@@ -63,6 +65,8 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item)
if (args->argc < 2)
value = NULL;
+ else if (args_has(args, 'F'))
+ value = expand = format_single_from_target(item, args->argv[1]);
else
value = args->argv[1];
@@ -75,7 +79,8 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "no such session: %s", tflag);
else
cmdq_error(item, "no current session");
- return (CMD_RETURN_ERROR);
+ retval = CMD_RETURN_ERROR;
+ goto out;
}
env = target->s->environ;
}
@@ -83,25 +88,31 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'u')) {
if (value != NULL) {
cmdq_error(item, "can't specify a value with -u");
- return (CMD_RETURN_ERROR);
+ retval = CMD_RETURN_ERROR;
+ goto out;
}
environ_unset(env, name);
} else if (args_has(args, 'r')) {
if (value != NULL) {
cmdq_error(item, "can't specify a value with -r");
- return (CMD_RETURN_ERROR);
+ retval = CMD_RETURN_ERROR;
+ goto out;
}
environ_clear(env, name);
} else {
if (value == NULL) {
cmdq_error(item, "no value specified");
- return (CMD_RETURN_ERROR);
+ retval = CMD_RETURN_ERROR;
+ goto out;
}
+
if (args_has(args, 'h'))
environ_set(env, name, ENVIRON_HIDDEN, "%s", value);
else
environ_set(env, name, 0, "%s", value);
}
- return (CMD_RETURN_NORMAL);
+out:
+ free(expand);
+ return (retval);
}
diff --git a/cmd-source-file.c b/cmd-source-file.c
index f5a0ca4b..1da59193 100644
--- a/cmd-source-file.c
+++ b/cmd-source-file.c
@@ -35,8 +35,8 @@ const struct cmd_entry cmd_source_file_entry = {
.name = "source-file",
.alias = "source",
- .args = { "nqv", 1, -1 },
- .usage = "[-nqv] path ...",
+ .args = { "Fnqv", 1, -1 },
+ .usage = "[-Fnqv] path ...",
.flags = 0,
.exec = cmd_source_file_exec
@@ -126,7 +126,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_source_file_data *cdata;
struct client *c = cmdq_get_client(item);
enum cmd_retval retval = CMD_RETURN_NORMAL;
- char *pattern, *cwd;
+ char *pattern, *cwd, *expand = NULL;
const char *path, *error;
glob_t g;
int i, result;
@@ -145,7 +145,12 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB);
for (i = 0; i < args->argc; i++) {
- path = args->argv[i];
+ if (args_has(args, 'F')) {
+ free(expand);
+ expand = format_single_from_target(item, args->argv[i]);
+ path = expand;
+ } else
+ path = args->argv[i];
if (strcmp(path, "-") == 0) {
cmd_source_file_add(cdata, "-");
continue;
@@ -172,6 +177,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
free(pattern);
continue;
}
+ free(expand);
free(pattern);
for (j = 0; j < g.gl_pathc; j++)
diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c
index 4b9f39a6..a29831af 100644
--- a/cmd-unbind-key.c
+++ b/cmd-unbind-key.c
@@ -32,8 +32,8 @@ const struct cmd_entry cmd_unbind_key_entry = {
.name = "unbind-key",
.alias = "unbind",
- .args = { "anT:", 0, 1 },
- .usage = "[-an] [-T key-table] key",
+ .args = { "anqT:", 0, 1 },
+ .usage = "[-anq] [-T key-table] key",
.flags = CMD_AFTERHOOK,
.exec = cmd_unbind_key_exec
@@ -45,44 +45,54 @@ cmd_unbind_key_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = cmd_get_args(self);
key_code key;
const char *tablename;
+ int quiet = args_has(args, 'q');
- if (!args_has(args, 'a')) {
- if (args->argc != 1) {
- cmdq_error(item, "missing key");
- return (CMD_RETURN_ERROR);
- }
- key = key_string_lookup_string(args->argv[0]);
- if (key == KEYC_NONE || key == KEYC_UNKNOWN) {
- cmdq_error(item, "unknown key: %s", args->argv[0]);
- return (CMD_RETURN_ERROR);
- }
- } else {
+ if (args_has(args, 'a')) {
if (args->argc != 0) {
- cmdq_error(item, "key given with -a");
+ if (!quiet)
+ cmdq_error(item, "key given with -a");
return (CMD_RETURN_ERROR);
}
- key = KEYC_UNKNOWN;
- }
- if (key == KEYC_UNKNOWN) {
tablename = args_get(args, 'T');
if (tablename == NULL) {
- key_bindings_remove_table("root");
- key_bindings_remove_table("prefix");
- return (CMD_RETURN_NORMAL);
+ if (args_has(args, 'n'))
+ tablename = "root";
+ else
+ tablename = "prefix";
}
if (key_bindings_get_table(tablename, 0) == NULL) {
- cmdq_error(item, "table %s doesn't exist", tablename);
+ if (!quiet) {
+ cmdq_error(item, "table %s doesn't exist" ,
+ tablename);
+ }
return (CMD_RETURN_ERROR);
}
+
key_bindings_remove_table(tablename);
return (CMD_RETURN_NORMAL);
}
+ if (args->argc != 1) {
+ if (!quiet)
+ cmdq_error(item, "missing key");
+ return (CMD_RETURN_ERROR);
+ }
+
+ key = key_string_lookup_string(args->argv[0]);
+ if (key == KEYC_NONE || key == KEYC_UNKNOWN) {
+ if (!quiet)
+ cmdq_error(item, "unknown key: %s", args->argv[0]);
+ return (CMD_RETURN_ERROR);
+ }
+
if (args_has(args, 'T')) {
tablename = args_get(args, 'T');
if (key_bindings_get_table(tablename, 0) == NULL) {
- cmdq_error(item, "table %s doesn't exist", tablename);
+ if (!quiet) {
+ cmdq_error(item, "table %s doesn't exist" ,
+ tablename);
+ }
return (CMD_RETURN_ERROR);
}
} else if (args_has(args, 'n'))
diff --git a/colour.c b/colour.c
index c7972878..ee4b95db 100644
--- a/colour.c
+++ b/colour.c
@@ -189,6 +189,12 @@ colour_fromstring(const char *s)
return (-1);
return (n | COLOUR_FLAG_256);
}
+ if (strncasecmp(s, "color", (sizeof "color") - 1) == 0) {
+ n = strtonum(s + (sizeof "color") - 1, 0, 255, &errstr);
+ if (errstr != NULL)
+ return (-1);
+ return (n | COLOUR_FLAG_256);
+ }
if (strcasecmp(s, "default") == 0)
return (8);
diff --git a/compat.h b/compat.h
index 5b23b178..b213336f 100644
--- a/compat.h
+++ b/compat.h
@@ -27,6 +27,10 @@
#include <termios.h>
#include <wchar.h>
+#ifdef HAVE_MALLOC_TRIM
+#include <malloc.h>
+#endif
+
#ifdef HAVE_UTF8PROC
#include <utf8proc.h>
#endif
@@ -35,6 +39,10 @@
#define __attribute__(a)
#endif
+#ifdef BROKEN___DEAD
+#undef __dead
+#endif
+
#ifndef __unused
#define __unused __attribute__ ((__unused__))
#endif
diff --git a/compat/getdtablesize.c b/compat/getdtablesize.c
new file mode 100644
index 00000000..fa82577f
--- /dev/null
+++ b/compat/getdtablesize.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020 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 <unistd.h>
+
+#include "compat.h"
+
+#ifdef HAVE_SYSCONF
+int
+getdtablesize(void)
+{
+ return (sysconf(_SC_OPEN_MAX));
+}
+#endif
diff --git a/compat/imsg-buffer.c b/compat/imsg-buffer.c
index 814591f4..67d4c705 100644
--- a/compat/imsg-buffer.c
+++ b/compat/imsg-buffer.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: imsg-buffer.c,v 1.11 2017/12/14 09:27:44 kettenis Exp $ */
+/* $OpenBSD: imsg-buffer.c,v 1.12 2019/01/20 02:50:03 bcook Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -70,7 +70,7 @@ ibuf_dynamic(size_t len, size_t max)
static int
ibuf_realloc(struct ibuf *buf, size_t len)
{
- u_char *b;
+ unsigned char *b;
/* on static buffers max is eq size and so the following fails */
if (buf->wpos + len > buf->max) {
diff --git a/compat/imsg.h b/compat/imsg.h
index 8bf94147..5b092cfc 100644
--- a/compat/imsg.h
+++ b/compat/imsg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: imsg.h,v 1.4 2017/03/24 09:34:12 nicm Exp $ */
+/* $OpenBSD: imsg.h,v 1.5 2019/01/20 02:50:03 bcook Exp $ */
/*
* Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -21,13 +21,15 @@
#ifndef _IMSG_H_
#define _IMSG_H_
+#include <stdint.h>
+
#define IBUF_READ_SIZE 65535
#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
#define MAX_IMSGSIZE 16384
struct ibuf {
TAILQ_ENTRY(ibuf) entry;
- u_char *buf;
+ unsigned char *buf;
size_t size;
size_t max;
size_t wpos;
@@ -42,8 +44,8 @@ struct msgbuf {
};
struct ibuf_read {
- u_char buf[IBUF_READ_SIZE];
- u_char *rptr;
+ unsigned char buf[IBUF_READ_SIZE];
+ unsigned char *rptr;
size_t wpos;
};
diff --git a/configure.ac b/configure.ac
index 22a48c8d..93246fc8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
# configure.ac
-AC_INIT([tmux], 3.2-rc2)
+AC_INIT([tmux], next-3.3)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@@ -113,6 +113,7 @@ AC_REPLACE_FUNCS([ \
fgetln \
freezero \
getdtablecount \
+ getdtablesize \
getline \
getprogname \
memmem \
@@ -328,6 +329,31 @@ AC_SEARCH_LIBS(inet_ntoa, nsl)
AC_SEARCH_LIBS(socket, socket)
AC_CHECK_LIB(xnet, socket)
+# Check if using glibc and have malloc_trim(3). The glibc free(3) is pretty bad
+# about returning memory to the kernel unless the application tells it when to
+# with malloc_trim(3).
+AC_MSG_CHECKING(if free doesn't work very well)
+AC_LINK_IFELSE([AC_LANG_SOURCE(
+ [
+ #include <stdlib.h>
+ #ifdef __GLIBC__
+ #include <malloc.h>
+ int main(void) {
+ malloc_trim (0);
+ exit(0);
+ }
+ #else
+ no
+ #endif
+ ])],
+ found_malloc_trim=yes,
+ found_malloc_trim=no
+)
+AC_MSG_RESULT($found_malloc_trim)
+if test "x$found_malloc_trim" = xyes; then
+ AC_DEFINE(HAVE_MALLOC_TRIM)
+fi
+
# Check for CMSG_DATA. On some platforms like HP-UX this requires UNIX 95
# (_XOPEN_SOURCE and _XOPEN_SOURCE_EXTENDED) (see xopen_networking(7)). On
# others, UNIX 03 (_XOPEN_SOURCE 600, see standards(7) on Solaris).
@@ -570,6 +596,12 @@ case "$host_os" in
AC_MSG_RESULT(darwin)
PLATFORM=darwin
#
+ # OS X uses __dead2 instead of __dead, like FreeBSD. But it
+ # defines __dead away so it needs to be removed before we can
+ # replace it.
+ #
+ AC_DEFINE(BROKEN___DEAD)
+ #
# OS X CMSG_FIRSTHDR is broken, so redefine it with a working
# one. daemon works but has some stupid side effects, so use
# our internal version which has a workaround.
diff --git a/control.c b/control.c
index c52f2020..e86429cf 100644
--- a/control.c
+++ b/control.c
@@ -688,8 +688,8 @@ control_write_pending(struct client *c, struct control_pane *cp, size_t limit)
else
age = 0;
log_debug("%s: %s: output block %zu (age %llu) for %%%u "
- "(used %zu/%zu)", __func__, c->name, cb->size, age,
- cp->pane, used, limit);
+ "(used %zu/%zu)", __func__, c->name, cb->size,
+ (unsigned long long)age, cp->pane, used, limit);
size = cb->size;
if (size > limit - used)
diff --git a/environ.c b/environ.c
index 940109b0..74d672e0 100644
--- a/environ.c
+++ b/environ.c
@@ -18,6 +18,7 @@
#include <sys/types.h>
+#include <fnmatch.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -191,7 +192,11 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst)
a = options_array_first(o);
while (a != NULL) {
ov = options_array_item_value(a);
- if ((envent = environ_find(src, ov->string)) == NULL)
+ RB_FOREACH(envent, environ, src) {
+ if (fnmatch(ov->string, envent->name, 0) == 0)
+ break;
+ }
+ if (envent == NULL)
environ_clear(dst, ov->string);
else
environ_set(dst, envent->name, 0, "%s", envent->value);
diff --git a/format.c b/format.c
index ced7b515..dd3af400 100644
--- a/format.c
+++ b/format.c
@@ -38,15 +38,18 @@
* string.
*/
-static char *format_job_get(struct format_tree *, const char *);
-static void format_job_timer(int, short, void *);
+struct format_expand_state;
-static int format_replace(struct format_tree *, const char *, size_t,
- char **, size_t *, size_t *);
+static char *format_job_get(struct format_expand_state *, const char *);
+static void format_job_timer(int, short, void *);
+static char *format_expand1(struct format_expand_state *, const char *);
+static int format_replace(struct format_expand_state *, const char *,
+ size_t, char **, size_t *, size_t *);
static void format_defaults_session(struct format_tree *,
struct session *);
static void format_defaults_client(struct format_tree *, struct client *);
-static void format_defaults_winlink(struct format_tree *, struct winlink *);
+static void format_defaults_winlink(struct format_tree *,
+ struct winlink *);
/* Entry in format job tree. */
struct format_job {
@@ -94,15 +97,20 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
#define FORMAT_WINDOWS 0x100
#define FORMAT_PANES 0x200
#define FORMAT_PRETTY 0x400
+#define FORMAT_LENGTH 0x800
/* Limit on recursion. */
#define FORMAT_LOOP_LIMIT 10
+/* Format expand flags. */
+#define FORMAT_EXPAND_TIME 0x1
+#define FORMAT_EXPAND_NOJOBS 0x2
+
/* Entry in format tree. */
struct format_entry {
char *key;
char *value;
- time_t t;
+ time_t time;
format_cb cb;
RB_ENTRY(format_entry) entry;
};
@@ -119,8 +127,6 @@ struct format_tree {
struct client *client;
int flags;
u_int tag;
- time_t time;
- u_int loop;
struct mouse_event m;
@@ -129,6 +135,14 @@ struct format_tree {
static int format_entry_cmp(struct format_entry *, struct format_entry *);
RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp);
+/* Format expand state. */
+struct format_expand_state {
+ struct format_tree *ft;
+ u_int loop;
+ time_t time;
+ int flags;
+};
+
/* Format modifier. */
struct format_modifier {
char modifier[3];
@@ -214,8 +228,10 @@ format_logging(struct format_tree *ft)
/* Log a message if verbose. */
static void printflike(3, 4)
-format_log1(struct format_tree *ft, const char *from, const char *fmt, ...)
+format_log1(struct format_expand_state *es, const char *from, const char *fmt,
+ ...)
{
+ struct format_tree *ft = es->ft;
va_list ap;
char *s;
static const char spaces[] = " ";
@@ -229,11 +245,22 @@ format_log1(struct format_tree *ft, const char *from, const char *fmt, ...)
log_debug("%s: %s", from, s);
if (ft->item != NULL && (ft->flags & FORMAT_VERBOSE))
- cmdq_print(ft->item, "#%.*s%s", ft->loop, spaces, s);
+ cmdq_print(ft->item, "#%.*s%s", es->loop, spaces, s);
free(s);
}
-#define format_log(ft, fmt, ...) format_log1(ft, __func__, fmt, ##__VA_ARGS__)
+#define format_log(es, fmt, ...) format_log1(es, __func__, fmt, ##__VA_ARGS__)
+
+/* Copy expand state. */
+static void
+format_copy_state(struct format_expand_state *to,
+ struct format_expand_state *from, int flags)
+{
+ to->ft = from->ft;
+ to->loop = from->loop;
+ to->time = from->time;
+ to->flags = from->flags|flags;
+}
/* Format job update callback. */
static void
@@ -303,13 +330,15 @@ format_job_complete(struct job *job)
/* Find a job. */
static char *
-format_job_get(struct format_tree *ft, const char *cmd)
+format_job_get(struct format_expand_state *es, const char *cmd)
{
- struct format_job_tree *jobs;
- struct format_job fj0, *fj;
- time_t t;
- char *expanded;
- int force;
+ struct format_tree *ft = es->ft;
+ struct format_job_tree *jobs;
+ struct format_job fj0, *fj;
+ time_t t;
+ char *expanded;
+ int force;
+ struct format_expand_state next;
if (ft->client == NULL)
jobs = &format_jobs;
@@ -334,7 +363,7 @@ format_job_get(struct format_tree *ft, const char *cmd)
RB_INSERT(format_job_tree, jobs, fj);
}
- expanded = format_expand(ft, cmd);
+ expanded = format_expand1(es, cmd);
if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) {
free((void *)fj->expanded);
fj->expanded = xstrdup(expanded);
@@ -356,12 +385,12 @@ format_job_get(struct format_tree *ft, const char *cmd)
fj->last = t;
fj->updated = 0;
}
+ free(expanded);
if (ft->flags & FORMAT_STATUS)
fj->status = 1;
-
- free(expanded);
- return (format_expand(ft, fj->out));
+ format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS);
+ return (format_expand1(&next, fj->out));
}
/* Remove old jobs. */
@@ -1210,7 +1239,6 @@ format_create(struct client *c, struct cmdq_item *item, int tag, int flags)
ft->tag = tag;
ft->flags = flags;
- ft->time = time(NULL);
format_add(ft, "version", "%s", getversion());
format_add_cb(ft, "host", format_cb_host);
@@ -1260,8 +1288,8 @@ format_each(struct format_tree *ft, void (*cb)(const char *, const char *,
char s[64];
RB_FOREACH(fe, format_entry_tree, &ft->tree) {
- if (fe->t != 0) {
- xsnprintf(s, sizeof s, "%lld", (long long)fe->t);
+ if (fe->time != 0) {
+ xsnprintf(s, sizeof s, "%lld", (long long)fe->time);
cb(fe->key, s, arg);
} else {
if (fe->value == NULL && fe->cb != NULL) {
@@ -1294,7 +1322,7 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
}
fe->cb = NULL;
- fe->t = 0;
+ fe->time = 0;
va_start(ap, fmt);
xvasprintf(&fe->value, fmt, ap);
@@ -1319,7 +1347,7 @@ format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv)
}
fe->cb = NULL;
- fe->t = tv->tv_sec;
+ fe->time = tv->tv_sec;
fe->value = NULL;
}
@@ -1343,7 +1371,7 @@ format_add_cb(struct format_tree *ft, const char *key, format_cb cb)
}
fe->cb = cb;
- fe->t = 0;
+ fe->time = 0;
fe->value = NULL;
}
@@ -1439,8 +1467,8 @@ format_find(struct format_tree *ft, const char *key, int modifiers,
fe_find.key = (char *)key;
fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find);
if (fe != NULL) {
- if (fe->t != 0) {
- t = fe->t;
+ if (fe->time != 0) {
+ t = fe->time;
goto found;
}
if (fe->value == NULL && fe->cb != NULL) {
@@ -1562,8 +1590,8 @@ format_skip(const char *s, const char *end)
/* Return left and right alternatives separated by commas. */
static int
-format_choose(struct format_tree *ft, const char *s, char **left, char **right,
- int expand)
+format_choose(struct format_expand_state *es, const char *s, char **left,
+ char **right, int expand)
{
const char *cp;
char *left0, *right0;
@@ -1575,9 +1603,9 @@ format_choose(struct format_tree *ft, const char *s, char **left, char **right,
right0 = xstrdup(cp + 1);
if (expand) {
- *left = format_expand(ft, left0);
+ *left = format_expand1(es, left0);
free(left0);
- *right = format_expand(ft, right0);
+ *right = format_expand1(es, right0);
free(right0);
} else {
*left = left0;
@@ -1633,7 +1661,8 @@ format_free_modifiers(struct format_modifier *list, u_int count)
/* Build modifier list. */
static struct format_modifier *
-format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
+format_build_modifiers(struct format_expand_state *es, const char **s,
+ u_int *count)
{
const char *cp = *s, *end;
struct format_modifier *list = NULL;
@@ -1642,7 +1671,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
/*
* Modifiers are a ; separated list of the forms:
- * l,m,C,b,d,t,q,E,T,S,W,P,<,>
+ * l,m,C,b,d,n,t,q,E,T,S,W,P,<,>
* =a
* =/a
* =/a/
@@ -1659,7 +1688,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
cp++;
/* Check single character modifiers with no arguments. */
- if (strchr("lbdqETSWP<>", cp[0]) != NULL &&
+ if (strchr("lbdnqETSWP<>", cp[0]) != NULL &&
format_is_end(cp[1])) {
format_add_modifier(&list, count, cp, 1, NULL, 0);
cp++;
@@ -1701,7 +1730,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
argv = xcalloc(1, sizeof *argv);
value = xstrndup(cp + 1, end - (cp + 1));
- argv[0] = format_expand(ft, value);
+ argv[0] = format_expand1(es, value);
free(value);
argc = 1;
@@ -1725,7 +1754,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count)
argv = xreallocarray (argv, argc + 1, sizeof *argv);
value = xstrndup(cp, end - cp);
- argv[argc++] = format_expand(ft, value);
+ argv[argc++] = format_expand1(es, value);
free(value);
cp = end;
@@ -1806,25 +1835,28 @@ format_search(struct format_modifier *fm, struct window_pane *wp, const char *s)
/* Loop over sessions. */
static char *
-format_loop_sessions(struct format_tree *ft, const char *fmt)
+format_loop_sessions(struct format_expand_state *es, const char *fmt)
{
- struct client *c = ft->client;
- struct cmdq_item *item = ft->item;
- struct format_tree *nft;
- char *expanded, *value;
- size_t valuelen;
- struct session *s;
+ struct format_tree *ft = es->ft;
+ struct client *c = ft->client;
+ struct cmdq_item *item = ft->item;
+ struct format_tree *nft;
+ struct format_expand_state next;
+ char *expanded, *value;
+ size_t valuelen;
+ struct session *s;
value = xcalloc(1, 1);
valuelen = 1;
RB_FOREACH(s, sessions, &sessions) {
- format_log(ft, "session loop: $%u", s->id);
+ format_log(es, "session loop: $%u", s->id);
nft = format_create(c, item, FORMAT_NONE, ft->flags);
- nft->loop = ft->loop;
- format_defaults(nft, ft->c, s, NULL, NULL);
- expanded = format_expand(nft, fmt);
- format_free(nft);
+ format_defaults(next.ft, ft->c, s, NULL, NULL);
+ format_copy_state(&next, es, 0);
+ next.ft = nft;
+ expanded = format_expand1(&next, fmt);
+ format_free(next.ft);
valuelen += strlen(expanded);
value = xrealloc(value, valuelen);
@@ -1838,22 +1870,24 @@ format_loop_sessions(struct format_tree *ft, const char *fmt)
/* Loop over windows. */
static char *
-format_loop_windows(struct format_tree *ft, const char *fmt)
-{
- struct client *c = ft->client;
- struct cmdq_item *item = ft->item;
- struct format_tree *nft;
- char *all, *active, *use, *expanded, *value;
- size_t valuelen;
- struct winlink *wl;
- struct window *w;
+format_loop_windows(struct format_expand_state *es, const char *fmt)
+{
+ struct format_tree *ft = es->ft;
+ struct client *c = ft->client;
+ struct cmdq_item *item = ft->item;
+ struct format_tree *nft;
+ struct format_expand_state next;
+ char *all, *active, *use, *expanded, *value;
+ size_t valuelen;
+ struct winlink *wl;
+ struct window *w;
if (ft->s == NULL) {
- format_log(ft, "window loop but no session");
+ format_log(es, "window loop but no session");
return (NULL);
}
- if (format_choose(ft, fmt, &all, &active, 0) != 0) {
+ if (format_choose(es, fmt, &all, &active, 0) != 0) {
all = xstrdup(fmt);
active = NULL;
}
@@ -1863,15 +1897,16 @@ format_loop_windows(struct format_tree *ft, const char *fmt)
RB_FOREACH(wl, winlinks, &ft->s->windows) {
w = wl->window;
- format_log(ft, "window loop: %u @%u", wl->idx, w->id);
+ format_log(es, "window loop: %u @%u", wl->idx, w->id);
if (active != NULL && wl == ft->s->curw)
use = active;
else
use = all;
nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags);
- nft->loop = ft->loop;
format_defaults(nft, ft->c, ft->s, wl, NULL);
- expanded = format_expand(nft, use);
+ format_copy_state(&next, es, 0);
+ next.ft = nft;
+ expanded = format_expand1(&next, use);
format_free(nft);
valuelen += strlen(expanded);
@@ -1889,21 +1924,23 @@ format_loop_windows(struct format_tree *ft, const char *fmt)
/* Loop over panes. */
static char *
-format_loop_panes(struct format_tree *ft, const char *fmt)
+format_loop_panes(struct format_expand_state *es, const char *fmt)
{
- struct client *c = ft->client;
- struct cmdq_item *item = ft->item;
- struct format_tree *nft;
- char *all, *active, *use, *expanded, *value;
- size_t valuelen;
- struct window_pane *wp;
+ struct format_tree *ft = es->ft;
+ struct client *c = ft->client;
+ struct cmdq_item *item = ft->item;
+ struct format_tree *nft;
+ struct format_expand_state next;
+ char *all, *active, *use, *expanded, *value;
+ size_t valuelen;
+ struct window_pane *wp;
if (ft->w == NULL) {
- format_log(ft, "pane loop but no window");
+ format_log(es, "pane loop but no window");
return (NULL);
}
- if (format_choose(ft, fmt, &all, &active, 0) != 0) {
+ if (format_choose(es, fmt, &all, &active, 0) != 0) {
all = xstrdup(fmt);
active = NULL;
}
@@ -1912,15 +1949,16 @@ format_loop_panes(struct format_tree *ft, const char *fmt)
valuelen = 1;
TAILQ_FOREACH(wp, &ft->w->panes, entry) {
- format_log(ft, "pane loop: %%%u", wp->id);
+ format_log(es, "pane loop: %%%u", wp->id);
if (active != NULL && wp == ft->w->active)
use = active;
else
use = all;
nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags);
- nft->loop = ft->loop;
format_defaults(nft, ft->c, ft->s, ft->wl, wp);
- expanded = format_expand(nft, use);
+ format_copy_state(&next, es, 0);
+ next.ft = nft;
+ expanded = format_expand1(&next, use);
format_free(nft);
valuelen += strlen(expanded);
@@ -1937,16 +1975,26 @@ format_loop_panes(struct format_tree *ft, const char *fmt)
}
static char *
-format_replace_expression(struct format_modifier *mexp, struct format_tree *ft,
- const char *copy)
+format_replace_expression(struct format_modifier *mexp,
+ struct format_expand_state *es, const char *copy)
{
- int argc = mexp->argc;
- const char *errstr;
- char *endch, *value, *left = NULL, *right = NULL;
- int use_fp = 0;
- u_int prec = 0;
- double mleft, mright, result;
- enum { ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULUS } operator;
+ int argc = mexp->argc;
+ const char *errstr;
+ char *endch, *value, *left = NULL, *right = NULL;
+ int use_fp = 0;
+ u_int prec = 0;
+ double mleft, mright, result;
+ enum { ADD,
+ SUBTRACT,
+ MULTIPLY,
+ DIVIDE,
+ MODULUS,
+ EQUAL,
+ NOT_EQUAL,
+ GREATER_THAN,
+ GREATER_THAN_EQUAL,
+ LESS_THAN,
+ LESS_THAN_EQUAL } operator;
if (strcmp(mexp->argv[0], "+") == 0)
operator = ADD;
@@ -1959,8 +2007,20 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft,
else if (strcmp(mexp->argv[0], "%") == 0 ||
strcmp(mexp->argv[0], "m") == 0)
operator = MODULUS;
+ else if (strcmp(mexp->argv[0], "==") == 0)
+ operator = EQUAL;
+ else if (strcmp(mexp->argv[0], "!=") == 0)
+ operator = NOT_EQUAL;
+ else if (strcmp(mexp->argv[0], ">") == 0)
+ operator = GREATER_THAN;
+ else if (strcmp(mexp->argv[0], "<") == 0)
+ operator = LESS_THAN;
+ else if (strcmp(mexp->argv[0], ">=") == 0)
+ operator = GREATER_THAN_EQUAL;
+ else if (strcmp(mexp->argv[0], "<=") == 0)
+ operator = LESS_THAN_EQUAL;
else {
- format_log(ft, "expression has no valid operator: '%s'",
+ format_log(es, "expression has no valid operator: '%s'",
mexp->argv[0]);
goto fail;
}
@@ -1975,26 +2035,26 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft,
if (argc >= 3) {
prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr);
if (errstr != NULL) {
- format_log (ft, "expression precision %s: %s", errstr,
+ format_log(es, "expression precision %s: %s", errstr,
mexp->argv[2]);
goto fail;
}
}
- if (format_choose(ft, copy, &left, &right, 1) != 0) {
- format_log(ft, "expression syntax error");
+ if (format_choose(es, copy, &left, &right, 1) != 0) {
+ format_log(es, "expression syntax error");
goto fail;
}
mleft = strtod(left, &endch);
if (*endch != '\0') {
- format_log(ft, "expression left side is invalid: %s", left);
+ format_log(es, "expression left side is invalid: %s", left);
goto fail;
}
mright = strtod(right, &endch);
if (*endch != '\0') {
- format_log(ft, "expression right side is invalid: %s", right);
+ format_log(es, "expression right side is invalid: %s", right);
goto fail;
}
@@ -2002,8 +2062,8 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft,
mleft = (long long)mleft;
mright = (long long)mright;
}
- format_log(ft, "expression left side is: %.*f", prec, mleft);
- format_log(ft, "expression right side is: %.*f", prec, mright);
+ format_log(es, "expression left side is: %.*f", prec, mleft);
+ format_log(es, "expression right side is: %.*f", prec, mright);
switch (operator) {
case ADD:
@@ -2021,12 +2081,30 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft,
case MODULUS:
result = fmod(mleft, mright);
break;
+ case EQUAL:
+ result = fabs(mleft - mright) < 1e-9;
+ break;
+ case NOT_EQUAL:
+ result = fabs(mleft - mright) > 1e-9;
+ break;
+ case GREATER_THAN:
+ result = (mleft > mright);
+ break;
+ case GREATER_THAN_EQUAL:
+ result = (mleft >= mright);
+ break;
+ case LESS_THAN:
+ result = (mleft < mright);
+ break;
+ case LESS_THAN_EQUAL:
+ result = (mleft > mright);
+ break;
}
if (use_fp)
xasprintf(&value, "%.*f", prec, result);
else
xasprintf(&value, "%.*f", prec, (double)(long long)result);
- format_log(ft, "expression result is %s", value);
+ format_log(es, "expression result is %s", value);
free(right);
free(left);
@@ -2040,31 +2118,34 @@ fail:
/* Replace a key. */
static int
-format_replace(struct format_tree *ft, const char *key, size_t keylen,
+format_replace(struct format_expand_state *es, const char *key, size_t keylen,
char **buf, size_t *len, size_t *off)
{
- struct window_pane *wp = ft->wp;
- const char *errptr, *copy, *cp, *marker = NULL;
- const char *time_format = NULL;
- char *copy0, *condition, *found, *new;
- char *value, *left, *right;
- size_t valuelen;
- int modifiers = 0, limit = 0, width = 0, j;
- struct format_modifier *list, *fm, *cmp = NULL, *search = NULL;
- struct format_modifier **sub = NULL, *mexp = NULL;
- u_int i, count, nsub = 0;
+ struct format_tree *ft = es->ft;
+ struct window_pane *wp = ft->wp;
+ const char *errptr, *copy, *cp, *marker = NULL;
+ const char *time_format = NULL;
+ char *copy0, *condition, *found, *new;
+ char *value, *left, *right;
+ size_t valuelen;
+ int modifiers = 0, limit = 0, width = 0;
+ int j;
+ struct format_modifier *list, *cmp = NULL, *search = NULL;
+ struct format_modifier **sub = NULL, *mexp = NULL, *fm;
+ u_int i, count, nsub = 0;
+ struct format_expand_state next;
/* Make a copy of the key. */
copy = copy0 = xstrndup(key, keylen);
/* Process modifier list. */
- list = format_build_modifiers(ft, &copy, &count);
+ list = format_build_modifiers(es, &copy, &count);
for (i = 0; i < count; i++) {
fm = &list[i];
if (format_logging(ft)) {
- format_log(ft, "modifier %u is %s", i, fm->modifier);
+ format_log(es, "modifier %u is %s", i, fm->modifier);
for (j = 0; j < fm->argc; j++) {
- format_log(ft, "modifier %u argument %d: %s", i,
+ format_log(es, "modifier %u argument %d: %s", i,
j, fm->argv[j]);
}
}
@@ -2117,6 +2198,9 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
case 'd':
modifiers |= FORMAT_DIRNAME;
break;
+ case 'n':
+ modifiers |= FORMAT_LENGTH;
+ break;
case 't':
modifiers |= FORMAT_TIMESTRING;
if (fm->argc < 1)
@@ -2165,37 +2249,37 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
/* Is this a loop, comparison or condition? */
if (modifiers & FORMAT_SESSIONS) {
- value = format_loop_sessions(ft, copy);
+ value = format_loop_sessions(es, copy);
if (value == NULL)
goto fail;
} else if (modifiers & FORMAT_WINDOWS) {
- value = format_loop_windows(ft, copy);
+ value = format_loop_windows(es, copy);
if (value == NULL)
goto fail;
} else if (modifiers & FORMAT_PANES) {
- value = format_loop_panes(ft, copy);
+ value = format_loop_panes(es, copy);
if (value == NULL)
goto fail;
} else if (search != NULL) {
/* Search in pane. */
- new = format_expand(ft, copy);
+ new = format_expand1(es, copy);
if (wp == NULL) {
- format_log(ft, "search '%s' but no pane", new);
+ format_log(es, "search '%s' but no pane", new);
value = xstrdup("0");
} else {
- format_log(ft, "search '%s' pane %%%u", new, wp->id);
+ format_log(es, "search '%s' pane %%%u", new, wp->id);
value = format_search(fm, wp, new);
}
free(new);
} else if (cmp != NULL) {
/* Comparison of left and right. */
- if (format_choose(ft, copy, &left, &right, 1) != 0) {
- format_log(ft, "compare %s syntax error: %s",
+ if (format_choose(es, copy, &left, &right, 1) != 0) {
+ format_log(es, "compare %s syntax error: %s",
cmp->modifier, copy);
goto fail;
}
- format_log(ft, "compare %s left is: %s", cmp->modifier, left);
- format_log(ft, "compare %s right is: %s", cmp->modifier, right);
+ format_log(es, "compare %s left is: %s", cmp->modifier, left);
+ format_log(es, "compare %s right is: %s", cmp->modifier, right);
if (strcmp(cmp->modifier, "||") == 0) {
if (format_true(left) || format_true(right))
@@ -2246,11 +2330,11 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
/* Conditional: check first and choose second or third. */
cp = format_skip(copy + 1, ",");
if (cp == NULL) {
- format_log(ft, "condition syntax error: %s", copy + 1);
+ format_log(es, "condition syntax error: %s", copy + 1);
goto fail;
}
condition = xstrndup(copy + 1, cp - (copy + 1));
- format_log(ft, "condition is: %s", condition);
+ format_log(es, "condition is: %s", condition);
found = format_find(ft, condition, modifiers, time_format);
if (found == NULL) {
@@ -2259,32 +2343,32 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
* the expansion doesn't have any effect, then assume
* false.
*/
- found = format_expand(ft, condition);
+ found = format_expand1(es, condition);
if (strcmp(found, condition) == 0) {
free(found);
found = xstrdup("");
- format_log(ft, "condition '%s' found: %s",
+ format_log(es, "condition '%s' found: %s",
condition, found);
} else {
- format_log(ft,
+ format_log(es,
"condition '%s' not found; assuming false",
condition);
}
} else
- format_log(ft, "condition '%s' found", condition);
+ format_log(es, "condition '%s' found", condition);
- if (format_choose(ft, cp + 1, &left, &right, 0) != 0) {
- format_log(ft, "condition '%s' syntax error: %s",
+ if (format_choose(es, cp + 1, &left, &right, 0) != 0) {
+ format_log(es, "condition '%s' syntax error: %s",
condition, cp + 1);
free(found);
goto fail;
}
if (format_true(found)) {
- format_log(ft, "condition '%s' is true", condition);
- value = format_expand(ft, left);
+ format_log(es, "condition '%s' is true", condition);
+ value = format_expand1(es, left);
} else {
- format_log(ft, "condition '%s' is false", condition);
- value = format_expand(ft, right);
+ format_log(es, "condition '%s' is false", condition);
+ value = format_expand1(es, right);
}
free(right);
free(left);
@@ -2292,38 +2376,44 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen,
free(condition);
free(found);
} else if (mexp != NULL) {
- value = format_replace_expression(mexp, ft, copy);
+ value = format_replace_expression(mexp, es, copy);
if (value == NULL)
value = xstrdup("");
} else {
- /* Neither: look up directly. */
- value = format_find(ft, copy, modifiers, time_format);
- if (value == NULL) {
- format_log(ft, "format '%s' not found", copy);
- value = xstrdup("");
- } else
- format_log(ft, "format '%s' found: %s", copy, value);
+ if (strstr(copy, "#{") != 0) {
+ format_log(es, "expanding inner format '%s'", copy);
+ value = format_expand1(es, copy);
+ } else {
+ value = format_find(ft, copy, modifiers, time_format);
+ if (value == NULL) {
+ format_log(es, "format '%s' not found", copy);
+ value = xstrdup("");
+ } else {
+ format_log(es, "format '%s' found: %s", copy,
+ value);
+ }
+ }
}
done:
/* Expand again if required. */
if (modifiers & FORMAT_EXPAND) {
- new = format_expand(ft, value);
+ new = format_expand1(es, value);
free(value);
value = new;
- }
- else if (modifiers & FORMAT_EXPANDTIME) {
- new = format_expand_time(ft, value);
+ } else if (modifiers & FORMAT_EXPANDTIME) {
+ format_copy_state(&next, es, FORMAT_EXPAND_TIME);
+ new = format_expand1(&next, value);
free(value);
value = new;
}
/* Perform substitution if any. */
for (i = 0; i < nsub; i++) {
- left = format_expand(ft, sub[i]->argv[0]);
- right = format_expand(ft, sub[i]->argv[1]);
+ left = format_expand1(es, sub[i]->argv[0]);
+ right = format_expand1(es, sub[i]->argv[1]);
new = format_sub(sub[i], value, left, right);
- format_log(ft, "substitute '%s' to '%s': %s", left, right, new);
+ format_log(es, "substitute '%s' to '%s': %s", left, right, new);
free(value);
value = new;
free(right);
@@ -2340,7 +2430,7 @@ done:
free(value);
value = new;
}
- format_log(ft, "applied length limit %d: %s", limit, value);
+ format_log(es, "applied length limit %d: %s", limit, value);
} else if (limit < 0) {
new = format_trim_right(value, -limit);
if (marker != NULL && strcmp(new, value) != 0) {
@@ -2350,7 +2440,7 @@ done:
free(value);
value = new;
}
- format_log(ft, "applied length limit %d: %s", limit, value);
+ format_log(es, "applied length limit %d: %s", limit, value);
}
/* Pad the value if needed. */
@@ -2358,12 +2448,20 @@ done:
new = utf8_padcstr(value, width);
free(value);
value = new;
- format_log(ft, "applied padding width %d: %s", width, value);
+ format_log(es, "applied padding width %d: %s", width, value);
} else if (width < 0) {
new = utf8_rpadcstr(value, -width);
free(value);
value = new;
- format_log(ft, "applied padding width %d: %s", width, value);
+ format_log(es, "applied padding width %d: %s", width, value);
+ }
+
+ /* Replace with the length if needed. */
+ if (modifiers & FORMAT_LENGTH) {
+ xasprintf(&new, "%zu", strlen(value));
+ free(value);
+ value = new;
+ format_log(es, "replacing with length: %s", new);
}
/* Expand the buffer and copy in the value. */
@@ -2375,7 +2473,7 @@ done:
memcpy(*buf + *off, value, valuelen);
*off += valuelen;
- format_log(ft, "replaced '%s' with '%s'", copy0, value);
+ format_log(es, "replaced '%s' with '%s'", copy0, value);
free(value);
free(sub);
@@ -2384,7 +2482,7 @@ done:
return (0);
fail:
- format_log(ft, "failed %s", copy0);
+ format_log(es, "failed %s", copy0);
free(sub);
format_free_modifiers(list, count);
@@ -2394,32 +2492,35 @@ fail:
/* Expand keys in a template. */
static char *
-format_expand1(struct format_tree *ft, const char *fmt, int time)
+format_expand1(struct format_expand_state *es, const char *fmt)
{
- char *buf, *out, *name;
- const char *ptr, *s;
- size_t off, len, n, outlen;
- int ch, brackets;
- struct tm *tm;
- char expanded[8192];
+ struct format_tree *ft = es->ft;
+ char *buf, *out, *name;
+ const char *ptr, *s;
+ size_t off, len, n, outlen;
+ int ch, brackets;
+ struct tm *tm;
+ char expanded[8192];
if (fmt == NULL || *fmt == '\0')
return (xstrdup(""));
- if (ft->loop == FORMAT_LOOP_LIMIT)
+ if (es->loop == FORMAT_LOOP_LIMIT)
return (xstrdup(""));
- ft->loop++;
+ es->loop++;
- format_log(ft, "expanding format: %s", fmt);
+ format_log(es, "expanding format: %s", fmt);
- if (time) {
- tm = localtime(&ft->time);
+ if (es->flags & FORMAT_EXPAND_TIME) {
+ if (es->time == 0)
+ es->time = time(NULL);
+ tm = localtime(&es->time);
if (strftime(expanded, sizeof expanded, fmt, tm) == 0) {
- format_log(ft, "format is too long");
+ format_log(es, "format is too long");
return (xstrdup(""));
}
if (format_logging(ft) && strcmp(expanded, fmt) != 0)
- format_log(ft, "after time expanded: %s", expanded);
+ format_log(es, "after time expanded: %s", expanded);
fmt = expanded;
}
@@ -2453,14 +2554,15 @@ format_expand1(struct format_tree *ft, const char *fmt, int time)
n = ptr - fmt;
name = xstrndup(fmt, n);
- format_log(ft, "found #(): %s", name);
+ format_log(es, "found #(): %s", name);
- if (ft->flags & FORMAT_NOJOBS) {
+ if ((ft->flags & FORMAT_NOJOBS) ||
+ (es->flags & FORMAT_EXPAND_NOJOBS)) {
out = xstrdup("");
- format_log(ft, "#() is disabled");
+ format_log(es, "#() is disabled");
} else {
- out = format_job_get(ft, name);
- format_log(ft, "#() result: %s", out);
+ out = format_job_get(es, name);
+ format_log(es, "#() result: %s", out);
}
free(name);
@@ -2482,15 +2584,15 @@ format_expand1(struct format_tree *ft, const char *fmt, int time)
break;
n = ptr - fmt;
- format_log(ft, "found #{}: %.*s", (int)n, fmt);
- if (format_replace(ft, fmt, n, &buf, &len, &off) != 0)
+ format_log(es, "found #{}: %.*s", (int)n, fmt);
+ if (format_replace(es, fmt, n, &buf, &len, &off) != 0)
break;
fmt += n + 1;
continue;
case '}':
case '#':
case ',':
- format_log(ft, "found #%c", ch);
+ format_log(es, "found #%c", ch);
while (len - off < 2) {
buf = xreallocarray(buf, 2, len);
len *= 2;
@@ -2513,8 +2615,8 @@ format_expand1(struct format_tree *ft, const char *fmt, int time)
continue;
}
n = strlen(s);
- format_log(ft, "found #%c: %s", ch, s);
- if (format_replace(ft, s, n, &buf, &len, &off) != 0)
+ format_log(es, "found #%c: %s", ch, s);
+ if (format_replace(es, s, n, &buf, &len, &off) != 0)
break;
continue;
}
@@ -2523,8 +2625,8 @@ format_expand1(struct format_tree *ft, const char *fmt, int time)
}
buf[off] = '\0';
- format_log(ft, "result is: %s", buf);
- ft->loop--;
+ format_log(es, "result is: %s", buf);
+ es->loop--;
return (buf);
}
@@ -2533,14 +2635,24 @@ format_expand1(struct format_tree *ft, const char *fmt, int time)
char *
format_expand_time(struct format_tree *ft, const char *fmt)
{
- return (format_expand1(ft, fmt, 1));
+ struct format_expand_state es;
+
+ memset(&es, 0, sizeof es);
+ es.ft = ft;
+ es.flags = FORMAT_EXPAND_TIME;
+ return (format_expand1(&es, fmt));
}
/* Expand keys in a template. */
char *
format_expand(struct format_tree *ft, const char *fmt)
{
- return (format_expand1(ft, fmt, 0));
+ struct format_expand_state es;
+
+ memset(&es, 0, sizeof es);
+ es.ft = ft;
+ es.flags = 0;
+ return (format_expand1(&es, fmt));
}
/* Expand a single string. */
@@ -2889,6 +3001,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_dead", "%d", wp->fd == -1);
else
format_add(ft, "pane_dead", "0");
+ format_add(ft, "pane_last", "%d", wp == w->last);
if (server_check_marked() && marked_pane.wp == wp)
format_add(ft, "pane_marked", "1");
diff --git a/grid.c b/grid.c
index 5ea5bf62..90e1a0a5 100644
--- a/grid.c
+++ b/grid.c
@@ -265,6 +265,9 @@ grid_free_lines(struct grid *gd, u_int py, u_int ny)
for (yy = py; yy < py + ny; yy++)
grid_free_line(gd, yy);
+#ifdef HAVE_MALLOC_TRIM
+ malloc_trim(0);
+#endif
}
/* Create a new grid. */
@@ -463,7 +466,7 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
sx = gd->sx / 4;
else if (sx < gd->sx / 2)
sx = gd->sx / 2;
- else
+ else if (gd->sx > sx)
sx = gd->sx;
gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata);
@@ -1277,7 +1280,7 @@ grid_reflow(struct grid *gd, u_int sx)
struct grid *target;
struct grid_line *gl;
struct grid_cell gc;
- u_int yy, width, i, at, first;
+ u_int yy, width, i, at;
/*
* Create a destination grid. This is just used as a container for the
@@ -1294,13 +1297,12 @@ grid_reflow(struct grid *gd, u_int sx)
continue;
/*
- * Work out the width of this line. first is the width of the
- * first character, at is the point at which the available
- * width is hit, and width is the full line width.
+ * Work out the width of this line. at is the point at which
+ * the available width is hit, and width is the full line
+ * width.
*/
- first = at = width = 0;
+ at = width = 0;
if (~gl->flags & GRID_LINE_EXTENDED) {
- first = 1;
width = gl->cellused;
if (width > sx)
at = sx;
@@ -1309,8 +1311,6 @@ grid_reflow(struct grid *gd, u_int sx)
} else {
for (i = 0; i < gl->cellused; i++) {
grid_get_cell1(gl, i, &gc);
- if (i == 0)
- first = gc.data.width;
if (at == 0 && width + gc.data.width > sx)
at = i;
width += gc.data.width;
@@ -1318,10 +1318,10 @@ grid_reflow(struct grid *gd, u_int sx)
}
/*
- * If the line is exactly right or the first character is wider
- * than the target width, just move it across unchanged.
+ * If the line is exactly right, just move it across
+ * unchanged.
*/
- if (width == sx || first > sx) {
+ if (width == sx) {
grid_reflow_move(target, gl);
continue;
}
diff --git a/input-keys.c b/input-keys.c
index ff402503..32ca4b97 100644
--- a/input-keys.c
+++ b/input-keys.c
@@ -555,6 +555,8 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
case KEYC_SHIFT|KEYC_META|KEYC_CTRL:
modifier = '8';
break;
+ default:
+ fatalx("invalid key modifiers: %llx", key);
}
xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier);
bufferevent_write(bev, tmp, strlen(tmp));
diff --git a/input.c b/input.c
index 60bfa8e2..8b7ba08a 100644
--- a/input.c
+++ b/input.c
@@ -65,7 +65,7 @@ struct input_param {
INPUT_MISSING,
INPUT_NUMBER,
INPUT_STRING
- } type;
+ } type;
union {
int num;
char *str;
@@ -81,7 +81,7 @@ struct input_ctx {
struct input_cell cell;
struct input_cell old_cell;
- u_int old_cx;
+ u_int old_cx;
u_int old_cy;
int old_mode;
@@ -121,7 +121,7 @@ struct input_ctx {
* All input received since we were last in the ground state. Sent to
* control clients on connection.
*/
- struct evbuffer *since_ground;
+ struct evbuffer *since_ground;
};
/* Helper functions. */
@@ -1545,6 +1545,10 @@ input_csi_dispatch(struct input_ctx *ictx)
if (n == -1)
break;
+ m = screen_size_x(s) - s->cx;
+ if (n > m)
+ n = m;
+
if (ictx->last == -1)
break;
ictx->ch = ictx->last;
@@ -1867,6 +1871,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
case 2:
screen_pop_title(sctx->s);
if (wp != NULL) {
+ notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}
@@ -2266,6 +2271,7 @@ input_exit_osc(struct input_ctx *ictx)
case 0:
case 2:
if (screen_set_title(sctx->s, p) && wp != NULL) {
+ notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}
@@ -2331,6 +2337,7 @@ input_exit_apc(struct input_ctx *ictx)
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
if (screen_set_title(sctx->s, ictx->input_buf) && wp != NULL) {
+ notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}
@@ -2461,13 +2468,31 @@ input_osc_parse_colour(const char *p, u_int *r, u_int *g, u_int *b)
return (1);
}
+/* Reply to a colour request. */
+static void
+input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c)
+{
+ u_char r, g, b;
+ const char *end;
+
+ if (c == 8 || (~c & COLOUR_FLAG_RGB))
+ return;
+ colour_split_rgb(c, &r, &g, &b);
+
+ if (ictx->input_end == INPUT_END_BEL)
+ end = "\007";
+ else
+ end = "\033\\";
+ input_reply(ictx, "\033]%u;rgb:%02hhx/%02hhx/%02hhx%s", n, r, g, b, end);
+}
+
/* Handle the OSC 4 sequence for setting (multiple) palette entries. */
static void
input_osc_4(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
char *copy, *s, *next = NULL;
- long idx;
+ long idx;
u_int r, g, b;
if (wp == NULL)
@@ -2499,17 +2524,22 @@ bad:
free(copy);
}
-/* Handle the OSC 10 sequence for setting foreground colour. */
+/* Handle the OSC 10 sequence for setting and querying foreground colour. */
static void
input_osc_10(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
+ struct grid_cell defaults;
u_int r, g, b;
if (wp == NULL)
return;
- if (strcmp(p, "?") == 0)
+
+ if (strcmp(p, "?") == 0) {
+ tty_default_colours(&defaults, wp);
+ input_osc_colour_reply(ictx, 10, defaults.fg);
return;
+ }
if (!input_osc_parse_colour(p, &r, &g, &b))
goto bad;
@@ -2522,17 +2552,22 @@ bad:
log_debug("bad OSC 10: %s", p);
}
-/* Handle the OSC 11 sequence for setting background colour. */
+/* Handle the OSC 11 sequence for setting and querying background colour. */
static void
input_osc_11(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
+ struct grid_cell defaults;
u_int r, g, b;
if (wp == NULL)
return;
- if (strcmp(p, "?") == 0)
+
+ if (strcmp(p, "?") == 0) {
+ tty_default_colours(&defaults, wp);
+ input_osc_colour_reply(ictx, 11, defaults.bg);
return;
+ }
if (!input_osc_parse_colour(p, &r, &g, &b))
goto bad;
diff --git a/key-bindings.c b/key-bindings.c
index 4c4d4ba5..b47f6ff7 100644
--- a/key-bindings.c
+++ b/key-bindings.c
@@ -191,6 +191,16 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat,
table = key_bindings_get_table(name, 1);
bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS);
+ if (cmdlist == NULL) {
+ if (bd != NULL) {
+ free((void *)bd->note);
+ if (note != NULL)
+ bd->note = xstrdup(note);
+ else
+ bd->note = NULL;
+ }
+ return;
+ }
if (bd != NULL) {
RB_REMOVE(key_bindings, &table->key_bindings, bd);
key_bindings_free(bd);
diff --git a/menu.c b/menu.c
index 4fcf660a..48c9ed2f 100644
--- a/menu.c
+++ b/menu.c
@@ -203,16 +203,28 @@ menu_key_cb(struct client *c, struct key_event *event)
m->x > md->px + 4 + menu->width ||
m->y < md->py + 1 ||
m->y > md->py + 1 + count - 1) {
- if (MOUSE_RELEASE(m->b))
- return (1);
+ if (~md->flags & MENU_STAYOPEN) {
+ if (MOUSE_RELEASE(m->b))
+ return (1);
+ } else {
+ if (!MOUSE_RELEASE(m->b) &&
+ MOUSE_WHEEL(m->b) == 0 &&
+ !MOUSE_DRAG(m->b))
+ return (1);
+ }
if (md->choice != -1) {
md->choice = -1;
c->flags |= CLIENT_REDRAWOVERLAY;
}
return (0);
}
- if (MOUSE_RELEASE(m->b))
- goto chosen;
+ if (~md->flags & MENU_STAYOPEN) {
+ if (MOUSE_RELEASE(m->b))
+ goto chosen;
+ } else {
+ if (MOUSE_WHEEL(m->b) == 0 && !MOUSE_DRAG(m->b))
+ goto chosen;
+ }
md->choice = m->y - (md->py + 1);
if (md->choice != old)
c->flags |= CLIENT_REDRAWOVERLAY;
@@ -303,8 +315,11 @@ chosen:
if (md->choice == -1)
return (1);
item = &menu->items[md->choice];
- if (item->name == NULL || *item->name == '-')
+ if (item->name == NULL || *item->name == '-') {
+ if (md->flags & MENU_STAYOPEN)
+ return (0);
return (1);
+ }
if (md->cb != NULL) {
md->cb(md->menu, md->choice, item->key, md->data);
md->cb = NULL;
diff --git a/mode-tree.c b/mode-tree.c
index c4b776f9..a47c0c06 100644
--- a/mode-tree.c
+++ b/mode-tree.c
@@ -1176,7 +1176,7 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
if (status == CMD_PARSE_ERROR) {
if (c != NULL) {
*error = toupper((u_char)*error);
- status_message_set(c, 1, "%s", error);
+ status_message_set(c, -1, 1, "%s", error);
}
free(error);
}
diff --git a/options-table.c b/options-table.c
index 0555d43e..873f8d67 100644
--- a/options-table.c
+++ b/options-table.c
@@ -170,6 +170,14 @@ static const char *options_table_status_format_default[] = {
.separator = "" \
}
+/* Map of name conversions. */
+const struct options_name_map options_other_names[] = {
+ { "display-panes-color", "display-panes-colour" },
+ { "display-panes-active-color", "display-panes-active-colour" },
+ { "clock-mode-color", "clock-mode-colour" },
+ { NULL, NULL }
+};
+
/* Top-level options. */
const struct options_table_entry options_table[] = {
/* Server options. */
@@ -1120,6 +1128,7 @@ const struct options_table_entry options_table[] = {
OPTIONS_TABLE_PANE_HOOK("pane-focus-out", ""),
OPTIONS_TABLE_PANE_HOOK("pane-mode-changed", ""),
OPTIONS_TABLE_PANE_HOOK("pane-set-clipboard", ""),
+ OPTIONS_TABLE_PANE_HOOK("pane-title-changed", ""),
OPTIONS_TABLE_HOOK("session-closed", ""),
OPTIONS_TABLE_HOOK("session-created", ""),
OPTIONS_TABLE_HOOK("session-renamed", ""),
diff --git a/options.c b/options.c
index 336eb732..09850f7e 100644
--- a/options.c
+++ b/options.c
@@ -95,6 +95,18 @@ options_cmp(struct options_entry *lhs, struct options_entry *rhs)
return (strcmp(lhs->name, rhs->name));
}
+static const char *
+options_map_name(const char *name)
+{
+ const struct options_name_map *map;
+
+ for (map = options_other_names; map->from != NULL; map++) {
+ if (strcmp(map->from, name) == 0)
+ return (map->to);
+ }
+ return (name);
+}
+
static const struct options_table_entry *
options_parent_table_entry(struct options *oo, const char *s)
{
@@ -204,10 +216,14 @@ options_next(struct options_entry *o)
struct options_entry *
options_get_only(struct options *oo, const char *name)
{
- struct options_entry o;
+ struct options_entry o = { .name = name }, *found;
- o.name = name;
- return (RB_FIND(options_tree, &oo->tree, &o));
+ found = RB_FIND(options_tree, &oo->tree, &o);
+ if (found == NULL) {
+ o.name = options_map_name(name);
+ return (RB_FIND(options_tree, &oo->tree, &o));
+ }
+ return (found);
}
struct options_entry *
@@ -608,19 +624,21 @@ char *
options_match(const char *s, int *idx, int *ambiguous)
{
const struct options_table_entry *oe, *found;
- char *name;
+ char *parsed;
+ const char *name;
size_t namelen;
- name = options_parse(s, idx);
- if (name == NULL)
+ parsed = options_parse(s, idx);
+ if (parsed == NULL)
return (NULL);
- namelen = strlen(name);
-
- if (*name == '@') {
+ if (*parsed == '@') {
*ambiguous = 0;
- return (name);
+ return (parsed);
}
+ name = options_map_name(parsed);
+ namelen = strlen(name);
+
found = NULL;
for (oe = options_table; oe->name != NULL; oe++) {
if (strcmp(oe->name, name) == 0) {
@@ -630,13 +648,13 @@ options_match(const char *s, int *idx, int *ambiguous)
if (strncmp(oe->name, name, namelen) == 0) {
if (found != NULL) {
*ambiguous = 1;
- free(name);
+ free(parsed);
return (NULL);
}
found = oe;
}
}
- free(name);
+ free(parsed);
if (found == NULL) {
*ambiguous = 0;
return (NULL);
diff --git a/popup.c b/popup.c
index 6f2ab101..0ad20c5f 100644
--- a/popup.c
+++ b/popup.c
@@ -261,7 +261,7 @@ popup_handle_drag(struct client *c, struct popup_data *pd,
pd->sx = m->x - pd->px;
pd->sy = m->y - pd->py;
- screen_resize(&pd->s, pd->sx, pd->sy, 0);
+ screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0);
if (pd->ictx == NULL)
popup_write_screen(c, pd);
else if (pd->job != NULL)
diff --git a/proc.c b/proc.c
index a9aa070a..592d8e05 100644
--- a/proc.c
+++ b/proc.c
@@ -239,6 +239,7 @@ proc_set_signals(struct tmuxproc *tp, void (*signalcb)(int))
sigaction(SIGTSTP, &sa, NULL);
sigaction(SIGTTIN, &sa, NULL);
sigaction(SIGTTOU, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
signal_set(&tp->ev_sigint, SIGINT, proc_signal_cb, tp);
signal_add(&tp->ev_sigint, NULL);
diff --git a/regress/style-trim.sh b/regress/style-trim.sh
new file mode 100644
index 00000000..56f93656
--- /dev/null
+++ b/regress/style-trim.sh
@@ -0,0 +1,93 @@
+#!/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
+
+$TMUX2 -f/dev/null new -d "$TMUX -f/dev/null new"
+sleep 2
+$TMUX set -g status-style fg=default,bg=default
+
+check() {
+ v=$($TMUX display -p "$1")
+ $TMUX set -g status-format[0] "$1"
+ sleep 1
+ r=$($TMUX2 capturep -Cep|tail -1|sed 's|\\033\[||g')
+
+ #printf "$1 = [$v = $2] [$r = $3]"
+ if [ "$v" = "$2" -a "$r" = "$3" ]; then
+ : #printf " good\n"
+ else
+ #printf " \033[31mbad\033[0m\n"
+ exit 1
+ fi
+}
+
+# drawn as #0
+$TMUX setenv -g V '#0'
+check '#{V} #{w:V}' '#0 2' '#0 2'
+check '#{=3:V}' '#0' '#0'
+check '#{=-3:V}' '#0' '#0'
+
+# drawn as #0
+$TMUX setenv -g V '###[bg=yellow]0'
+check '#{V} #{w:V}' '###[bg=yellow]0 2' '#43m0 249m'
+check '#{=3:V}' '###[bg=yellow]0' '#43m049m'
+check '#{=-3:V}' '###[bg=yellow]0' '#43m049m'
+
+# drawn as #0123456
+$TMUX setenv -g V '#0123456'
+check '#{V} #{w:V}' '#0123456 8' '#0123456 8'
+check '#{=3:V}' '#01' '#01'
+check '#{=-3:V}' '456' '456'
+
+# drawn as ##0123456
+$TMUX setenv -g V '##0123456'
+check '#{V} #{w:V}' '##0123456 9' '##0123456 9'
+check '#{=3:V}' '##0' '##0'
+check '#{=-3:V}' '456' '456'
+
+# drawn as ###0123456
+$TMUX setenv -g V '###0123456'
+check '#{V} #{w:V}' '###0123456 10' '###0123456 10'
+check '#{=3:V}' '###' '###'
+check '#{=-3:V}' '456' '456'
+
+# drawn as 0123456
+$TMUX setenv -g V '#[bg=yellow]0123456'
+check '#{V} #{w:V}' '#[bg=yellow]0123456 7' '43m0123456 749m'
+check '#{=3:V}' '#[bg=yellow]012' '43m01249m'
+check '#{=-3:V}' '#[bg=yellow]456' '43m45649m'
+
+# drawn as #[bg=yellow]0123456
+$TMUX setenv -g V '##[bg=yellow]0123456'
+check '#{V} #{w:V}' '##[bg=yellow]0123456 19' '#[bg=yellow]0123456 19'
+check '#{=3:V}' '##[b' '#[b'
+check '#{=-3:V}' '456' '456'
+
+# drawn as #0123456
+$TMUX setenv -g V '###[bg=yellow]0123456'
+check '#{V} #{w:V}' '###[bg=yellow]0123456 8' '#43m0123456 849m'
+check '#{=3:V}' '###[bg=yellow]01' '#43m0149m'
+check '#{=-3:V}' '#[bg=yellow]456' '43m45649m'
+
+# drawn as ##[bg=yellow]0123456
+$TMUX setenv -g V '####[bg=yellow]0123456'
+check '#{V} #{w:V}' '####[bg=yellow]0123456 20' '##[bg=yellow]0123456 20'
+check '#{=3:V}' '####[' '##['
+check '#{=-3:V}' '456' '456'
+
+# drawn as ###0123456
+$TMUX setenv -g V '#####[bg=yellow]0123456'
+check '#{V} #{w:V}' '#####[bg=yellow]0123456 9' '##43m0123456 949m'
+check '#{=3:V}' '#####[bg=yellow]0' '##43m049m'
+check '#{=-3:V}' '#[bg=yellow]456' '43m45649m'
+
+$TMUX kill-server 2>/dev/null
+$TMUX2 kill-server 2>/dev/null
+exit 0
diff --git a/resize.c b/resize.c
index d6e6dce2..172cbcb5 100644
--- a/resize.c
+++ b/resize.c
@@ -92,130 +92,156 @@ ignore_client_size(struct client *c)
return (0);
}
-void
-default_window_size(struct client *c, struct session *s, struct window *w,
- u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type)
+static u_int
+clients_with_window(struct window *w)
{
struct client *loop;
- u_int cx, cy, n;
- const char *value;
+ u_int n = 0;
- if (type == -1)
- type = options_get_number(global_w_options, "window-size");
- switch (type) {
- case WINDOW_SIZE_LARGEST:
+ TAILQ_FOREACH(loop, &clients, entry) {
+ if (ignore_client_size(loop) || !session_has(loop->session, w))
+ continue;
+ if (++n > 1)
+ break;
+ }
+ return (n);
+}
+
+static int
+clients_calculate_size(int type, int current, struct session *s,
+ struct window *w, int (*skip_client)(struct client *, int, int,
+ struct session *, struct window *), u_int *sx, u_int *sy, u_int *xpixel,
+ u_int *ypixel)
+{
+ struct client *loop;
+ u_int cx, cy, n = 0;
+
+ /* Manual windows do not have their size changed based on a client. */
+ if (type == WINDOW_SIZE_MANUAL)
+ return (0);
+
+ /*
+ * Start comparing with 0 for largest and UINT_MAX for smallest or
+ * latest.
+ */
+ if (type == WINDOW_SIZE_LARGEST)
*sx = *sy = 0;
- *xpixel = *ypixel = 0;
- TAILQ_FOREACH(loop, &clients, entry) {
- if (ignore_client_size(loop))
- continue;
- if (w != NULL && !session_has(loop->session, w))
- continue;
- if (w == NULL && loop->session != s)
- continue;
+ else
+ *sx = *sy = UINT_MAX;
+ *xpixel = *ypixel = 0;
+
+ /*
+ * For latest, count the number of clients with this window. We only
+ * care if there is more than one.
+ */
+ if (type == WINDOW_SIZE_LATEST)
+ n = clients_with_window(w);
+
+ /* Loop over the clients and work out the size. */
+ TAILQ_FOREACH(loop, &clients, entry) {
+ if (ignore_client_size(loop))
+ continue;
+ if (skip_client(loop, type, current, s, w))
+ continue;
+
+ /*
+ * If there are multiple clients attached, only accept the
+ * latest client; otherwise let the only client be chosen as
+ * for smallest.
+ */
+ if (type == WINDOW_SIZE_LATEST && n > 1 && loop != w->latest)
+ continue;
- cx = loop->tty.sx;
- cy = loop->tty.sy - status_line_size(loop);
+ /* Work out this client's size. */
+ cx = loop->tty.sx;
+ cy = loop->tty.sy - status_line_size(loop);
+ /*
+ * If it is larger or smaller than the best so far, update the
+ * new size.
+ */
+ if (type == WINDOW_SIZE_LARGEST) {
if (cx > *sx)
*sx = cx;
if (cy > *sy)
*sy = cy;
-
- if (loop->tty.xpixel > *xpixel &&
- loop->tty.ypixel > *ypixel) {
- *xpixel = loop->tty.xpixel;
- *ypixel = loop->tty.ypixel;
- }
- }
- if (*sx == 0 || *sy == 0)
- goto manual;
- break;
- case WINDOW_SIZE_SMALLEST:
- *sx = *sy = UINT_MAX;
- *xpixel = *ypixel = 0;
- TAILQ_FOREACH(loop, &clients, entry) {
- if (ignore_client_size(loop))
- continue;
- if (w != NULL && !session_has(loop->session, w))
- continue;
- if (w == NULL && loop->session != s)
- continue;
-
- cx = loop->tty.sx;
- cy = loop->tty.sy - status_line_size(loop);
-
+ } else {
if (cx < *sx)
*sx = cx;
if (cy < *sy)
*sy = cy;
-
- if (loop->tty.xpixel > *xpixel &&
- loop->tty.ypixel > *ypixel) {
- *xpixel = loop->tty.xpixel;
- *ypixel = loop->tty.ypixel;
- }
}
- if (*sx == UINT_MAX || *sy == UINT_MAX)
- goto manual;
- break;
- case WINDOW_SIZE_LATEST:
+ if (loop->tty.xpixel > *xpixel && loop->tty.ypixel > *ypixel) {
+ *xpixel = loop->tty.xpixel;
+ *ypixel = loop->tty.ypixel;
+ }
+ }
+
+ /* Return whether a suitable size was found. */
+ if (type == WINDOW_SIZE_LARGEST)
+ return (*sx != 0 && *sy != 0);
+ return (*sx != UINT_MAX && *sy != UINT_MAX);
+}
+
+static int
+default_window_size_skip_client (struct client *loop, int type,
+ __unused int current, struct session *s, struct window *w)
+{
+ /*
+ * Latest checks separately, so do not check here. Otherwise only
+ * include clients where the session contains the window or where the
+ * session is the given session.
+ */
+ if (type == WINDOW_SIZE_LATEST)
+ return (0);
+ if (w != NULL && !session_has(loop->session, w))
+ return (1);
+ if (w == NULL && loop->session != s)
+ return (1);
+ return (0);
+}
+
+void
+default_window_size(struct client *c, struct session *s, struct window *w,
+ u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type)
+{
+ const char *value;
+
+ /* Get type if not provided. */
+ if (type == -1)
+ type = options_get_number(global_w_options, "window-size");
+
+ /*
+ * Latest clients can use the given client if suitable. If there is no
+ * client and no window, use the default size as for manual type.
+ */
+ if (type == WINDOW_SIZE_LATEST) {
if (c != NULL && !ignore_client_size(c)) {
*sx = c->tty.sx;
*sy = c->tty.sy - status_line_size(c);
*xpixel = c->tty.xpixel;
- *ypixel = c->tty.ypixel;
- } else {
- if (w == NULL)
- goto manual;
- n = 0;
- TAILQ_FOREACH(loop, &clients, entry) {
- if (!ignore_client_size(loop) &&
- session_has(loop->session, w)) {
- if (++n > 1)
- break;
- }
- }
- *sx = *sy = UINT_MAX;
- *xpixel = *ypixel = 0;
- TAILQ_FOREACH(loop, &clients, entry) {
- if (ignore_client_size(loop))
- continue;
- if (n > 1 && loop != w->latest)
- continue;
- s = loop->session;
-
- cx = loop->tty.sx;
- cy = loop->tty.sy - status_line_size(loop);
-
- if (cx < *sx)
- *sx = cx;
- if (cy < *sy)
- *sy = cy;
-
- if (loop->tty.xpixel > *xpixel &&
- loop->tty.ypixel > *ypixel) {
- *xpixel = loop->tty.xpixel;
- *ypixel = loop->tty.ypixel;
- }
- }
- if (*sx == UINT_MAX || *sy == UINT_MAX)
- goto manual;
+ *ypixel = c->tty.ypixel;
+ goto done;
}
- break;
- case WINDOW_SIZE_MANUAL:
- goto manual;
+ if (w == NULL)
+ type = WINDOW_SIZE_MANUAL;
}
- goto done;
-manual:
- value = options_get_string(s->options, "default-size");
- if (sscanf(value, "%ux%u", sx, sy) != 2) {
- *sx = 80;
- *sy = 24;
+ /*
+ * Look for a client to base the size on. If none exists (or the type
+ * is manual), use the default-size option.
+ */
+ if (!clients_calculate_size(type, 0, s, w,
+ default_window_size_skip_client, sx, sy, xpixel, ypixel)) {
+ value = options_get_string(s->options, "default-size");
+ if (sscanf(value, "%ux%u", sx, sy) != 2) {
+ *sx = 80;
+ *sy = 24;
+ }
}
done:
+ /* Make sure the limits are enforced. */
if (*sx < WINDOW_MINIMUM)
*sx = WINDOW_MINIMUM;
if (*sx > WINDOW_MAXIMUM)
@@ -226,127 +252,50 @@ done:
*sy = WINDOW_MAXIMUM;
}
+static int
+recalculate_size_skip_client(struct client *loop, __unused int type,
+ int current, __unused struct session *s, struct window *w)
+{
+ /*
+ * If the current flag is set, then skip any client where this window
+ * is not the current window - this is used for aggressive-resize.
+ * Otherwise skip any session that doesn't contain the window.
+ */
+ if (current)
+ return (loop->session->curw->window != w);
+ return (session_has(loop->session, w) == 0);
+}
+
void
recalculate_size(struct window *w, int now)
{
- struct session *s;
- struct client *c;
- u_int sx, sy, cx, cy, xpixel = 0, ypixel = 0, n;
- int type, current, has, changed;
+ u_int sx, sy, xpixel = 0, ypixel = 0;
+ int type, current, changed;
+ /*
+ * Do not attempt to resize windows which have no pane, they must be on
+ * the way to destruction.
+ */
if (w->active == NULL)
return;
log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy);
+ /*
+ * Type is manual, smallest, largest, latest. Current is the
+ * aggressive-resize option (do not resize based on clients where the
+ * window is not the current window).
+ */
type = options_get_number(w->options, "window-size");
current = options_get_number(w->options, "aggressive-resize");
- changed = 1;
- switch (type) {
- case WINDOW_SIZE_LARGEST:
- sx = sy = 0;
- TAILQ_FOREACH(c, &clients, entry) {
- if (ignore_client_size(c))
- continue;
- s = c->session;
-
- if (current)
- has = (s->curw->window == w);
- else
- has = session_has(s, w);
- if (!has)
- continue;
-
- cx = c->tty.sx;
- cy = c->tty.sy - status_line_size(c);
-
- if (cx > sx)
- sx = cx;
- if (cy > sy)
- sy = cy;
+ /* Look for a suitable client and get the new size. */
+ changed = clients_calculate_size(type, current, NULL, w,
+ recalculate_size_skip_client, &sx, &sy, &xpixel, &ypixel);
- if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) {
- xpixel = c->tty.xpixel;
- ypixel = c->tty.ypixel;
- }
- }
- if (sx == 0 || sy == 0)
- changed = 0;
- break;
- case WINDOW_SIZE_SMALLEST:
- sx = sy = UINT_MAX;
- TAILQ_FOREACH(c, &clients, entry) {
- if (ignore_client_size(c))
- continue;
- s = c->session;
-
- if (current)
- has = (s->curw->window == w);
- else
- has = session_has(s, w);
- if (!has)
- continue;
-
- cx = c->tty.sx;
- cy = c->tty.sy - status_line_size(c);
-
- if (cx < sx)
- sx = cx;
- if (cy < sy)
- sy = cy;
-
- if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) {
- xpixel = c->tty.xpixel;
- ypixel = c->tty.ypixel;
- }
- }
- if (sx == UINT_MAX || sy == UINT_MAX)
- changed = 0;
- break;
- case WINDOW_SIZE_LATEST:
- n = 0;
- TAILQ_FOREACH(c, &clients, entry) {
- if (!ignore_client_size(c) &&
- session_has(c->session, w)) {
- if (++n > 1)
- break;
- }
- }
- sx = sy = UINT_MAX;
- TAILQ_FOREACH(c, &clients, entry) {
- if (ignore_client_size(c))
- continue;
- if (n > 1 && c != w->latest)
- continue;
- s = c->session;
-
- if (current)
- has = (s->curw->window == w);
- else
- has = session_has(s, w);
- if (!has)
- continue;
-
- cx = c->tty.sx;
- cy = c->tty.sy - status_line_size(c);
-
- if (cx < sx)
- sx = cx;
- if (cy < sy)
- sy = cy;
-
- if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) {
- xpixel = c->tty.xpixel;
- ypixel = c->tty.ypixel;
- }
- }
- if (sx == UINT_MAX || sy == UINT_MAX)
- changed = 0;
- break;
- case WINDOW_SIZE_MANUAL:
- changed = 0;
- break;
- }
+ /*
+ * Make sure the size has actually changed. If the window has already
+ * got a resize scheduled, then use the new size; otherwise the old.
+ */
if (w->flags & WINDOW_RESIZE) {
if (!now && changed && w->new_sx == sx && w->new_sy == sy)
changed = 0;
@@ -355,10 +304,20 @@ recalculate_size(struct window *w, int now)
changed = 0;
}
+ /*
+ * If the size hasn't changed, update the window offset but not the
+ * size.
+ */
if (!changed) {
tty_update_window_offset(w);
return;
}
+
+ /*
+ * If the now flag is set or if the window is sized manually, change
+ * the size immediately. Otherwise set the flag and it will be done
+ * later.
+ */
log_debug("%s: @%u new size %u,%u", __func__, w->id, sx, sy);
if (now || type == WINDOW_SIZE_MANUAL)
resize_window(w, sx, sy, xpixel, ypixel);
diff --git a/screen.c b/screen.c
index 95299306..b55c9f87 100644
--- a/screen.c
+++ b/screen.c
@@ -78,6 +78,7 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
s->title = xstrdup("");
s->titles = NULL;
+ s->path = NULL;
s->cstyle = 0;
s->ccolour = xstrdup("");
@@ -120,6 +121,7 @@ screen_free(struct screen *s)
{
free(s->sel);
free(s->tabs);
+ free(s->path);
free(s->title);
free(s->ccolour);
diff --git a/server-client.c b/server-client.c
index e9587e4c..66d0d577 100644
--- a/server-client.c
+++ b/server-client.c
@@ -1588,10 +1588,6 @@ server_client_check_pane_focus(struct window_pane *wp)
if (wp->window->active != wp)
goto not_focused;
- /* If we're in a mode, we're not focused. */
- if (wp->screen != &wp->base)
- goto not_focused;
-
/*
* If our window is the current window in any focused clients with an
* attached session, we're focused.
@@ -1660,8 +1656,6 @@ server_client_reset_state(struct client *c)
s = wp->screen;
if (s != NULL)
mode = s->mode;
- if (c->prompt_string != NULL || c->message_string != NULL)
- mode &= ~MODE_CURSOR;
log_debug("%s: client %s mode %x", __func__, c->name, mode);
/* Reset region and margin. */
@@ -1693,8 +1687,8 @@ server_client_reset_state(struct client *c)
* mode.
*/
if (options_get_number(oo, "mouse")) {
- mode &= ~ALL_MOUSE_MODES;
if (c->overlay_draw == NULL) {
+ mode &= ~ALL_MOUSE_MODES;
TAILQ_FOREACH(loop, &w->panes, entry) {
if (loop->screen->mode & MODE_MOUSE_ALL)
mode |= MODE_MOUSE_ALL;
@@ -1985,6 +1979,7 @@ server_client_dispatch(struct imsg *imsg, void *arg)
switch (imsg->hdr.type) {
case MSG_IDENTIFY_FEATURES:
case MSG_IDENTIFY_FLAGS:
+ case MSG_IDENTIFY_LONGFLAGS:
case MSG_IDENTIFY_TERM:
case MSG_IDENTIFY_TTYNAME:
case MSG_IDENTIFY_CWD:
@@ -2028,7 +2023,7 @@ server_client_dispatch(struct imsg *imsg, void *arg)
break;
c->flags &= ~CLIENT_SUSPENDED;
- if (c->fd == -1) /* exited in the meantime */
+ if (c->fd == -1 || c->session == NULL) /* exited already */
break;
s = c->session;
@@ -2143,6 +2138,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
const char *data, *home;
size_t datalen;
int flags, feat;
+ uint64_t longflags;
char *name;
if (c->flags & CLIENT_IDENTIFIED)
@@ -2167,6 +2163,14 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
c->flags |= flags;
log_debug("client %p IDENTIFY_FLAGS %#x", c, flags);
break;
+ case MSG_IDENTIFY_LONGFLAGS:
+ if (datalen != sizeof longflags)
+ fatalx("bad MSG_IDENTIFY_LONGFLAGS size");
+ memcpy(&longflags, data, sizeof longflags);
+ c->flags |= longflags;
+ log_debug("client %p IDENTIFY_LONGFLAGS %#llx", c,
+ (unsigned long long)longflags);
+ break;
case MSG_IDENTIFY_TERM:
if (datalen == 0 || data[datalen - 1] != '\0')
fatalx("bad MSG_IDENTIFY_TERM string");
diff --git a/server-fn.c b/server-fn.c
index d5e7cbd7..e3cc181f 100644
--- a/server-fn.c
+++ b/server-fn.c
@@ -342,6 +342,7 @@ server_destroy_pane(struct window_pane *wp, int notify)
time(&t);
ctime_r(&t, tim);
+ tim[strcspn(tim, "\n")] = '\0';
if (WIFEXITED(wp->status)) {
screen_write_nputs(&ctx, -1, &gc,
diff --git a/server.c b/server.c
index ffbd28c1..9a1875f4 100644
--- a/server.c
+++ b/server.c
@@ -157,7 +157,7 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base,
{
int pair[2];
sigset_t set, oldset;
- struct client *c;
+ struct client *c = NULL;
char *cause = NULL;
sigfillset(&set);
@@ -223,9 +223,11 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base,
}
if (cause != NULL) {
- cmdq_append(c, cmdq_get_error(cause));
+ if (c != NULL) {
+ cmdq_append(c, cmdq_get_error(cause));
+ c->flags |= CLIENT_EXIT;
+ }
free(cause);
- c->flags |= CLIENT_EXIT;
}
server_add_accept(0);
diff --git a/status.c b/status.c
index 7313d2a0..668c0a3b 100644
--- a/status.c
+++ b/status.c
@@ -423,11 +423,11 @@ status_redraw(struct client *c)
/* Set a status line message. */
void
-status_message_set(struct client *c, int ignore_styles, const char *fmt, ...)
+status_message_set(struct client *c, int delay, int ignore_styles,
+ const char *fmt, ...)
{
struct timeval tv;
va_list ap;
- int delay;
status_message_clear(c);
status_push_screen(c);
@@ -439,7 +439,12 @@ status_message_set(struct client *c, int ignore_styles, const char *fmt, ...)
server_add_message("%s message: %s", c->name, c->message_string);
- delay = options_get_number(c->session->options, "display-time");
+ /*
+ * With delay -1, the display-time option is used; zero means wait for
+ * key press; more than zero is the actual delay time in milliseconds.
+ */
+ if (delay == -1)
+ delay = options_get_number(c->session->options, "display-time");
if (delay > 0) {
tv.tv_sec = delay / 1000;
tv.tv_usec = (delay % 1000) * 1000L;
@@ -447,6 +452,7 @@ status_message_set(struct client *c, int ignore_styles, const char *fmt, ...)
if (event_initialized(&c->message_timer))
evtimer_del(&c->message_timer);
evtimer_set(&c->message_timer, status_message_callback, c);
+
evtimer_add(&c->message_timer, &tv);
}
diff --git a/tmux.1 b/tmux.1
index 65ce211f..53687194 100644
--- a/tmux.1
+++ b/tmux.1
@@ -1003,7 +1003,7 @@ wait for an empty line input before exiting in control mode
.El
.Pp
A leading
-.Ql !
+.Ql \&!
turns a flag off if the client is already attached.
.Fl r
is an alias for
@@ -1407,7 +1407,7 @@ and
.Fl T
show debugging information about jobs and terminals.
.It Xo Ic source-file
-.Op Fl nqv
+.Op Fl Fnqv
.Ar path
.Ar ...
.Xc
@@ -1418,6 +1418,11 @@ Execute commands from one or more files specified by
.Xr glob 7
patterns).
If
+.Fl F
+is present, then
+.Ar path
+is expanded as a format.
+If
.Fl q
is given, no error will be returned if
.Ar path
@@ -1853,7 +1858,7 @@ The
.Fl P
option prints information about the new window after it has been created.
By default, it uses the format
-.Ql #{session_name}:#{window_index}
+.Ql #{session_name}:#{window_index}.#{pane_index}
but a different format may be specified with
.Fl F .
.It Xo Ic capture-pane
@@ -3034,7 +3039,7 @@ Send the prefix key, or with
.Fl 2
the secondary prefix key, to a window as if it was pressed.
.It Xo Ic unbind-key
-.Op Fl an
+.Op Fl anq
.Op Fl T Ar key-table
.Ar key
.Xc
@@ -3049,6 +3054,9 @@ are the same as for
If
.Fl a
is present, all key bindings are removed.
+The
+.Fl q
+option prevents errors being returned.
.El
.Sh OPTIONS
The appearance and behaviour of
@@ -3159,6 +3167,9 @@ flag unsets an option, so a session inherits the option from the global
options (or with
.Fl g ,
restores a global option to the default).
+.Ar value
+depends on the option and may be a number, a string, or a flag (on, off, or
+omitted to toggle).
.Pp
The
.Fl o
@@ -3230,9 +3241,6 @@ includes hooks (omitted by default).
.Fl A
includes options inherited from a parent set of options, such options are
marked with an asterisk.
-.Ar value
-depends on the option and may be a number, a string, or a flag (on, off, or
-omitted to toggle).
.El
.Pp
Available server options are:
@@ -4536,7 +4544,7 @@ multiplication
.Ql * ,
division
.Ql / ,
-and modulus
+modulus
.Ql m
or
.Ql %
@@ -4545,7 +4553,15 @@ or
must be escaped as
.Ql %%
in formats which are also expanded by
-.Xr strftime 3 ) .
+.Xr strftime 3 )
+and numeric comparison operators
+.Ql == ,
+.Ql != ,
+.Ql < ,
+.Ql <= ,
+.Ql >
+and
+.Ql >= .
For example,
.Ql #{e|*|f|4:5.5,3}
multiplies 5.5 by 3 for a result with four decimal places and
@@ -4574,6 +4590,9 @@ pads the string to a given width, for example
.Ql #{p10:pane_title}
will result in a width of at least 10 characters.
A positive width pads on the left, a negative on the right.
+.Ql n
+expands to the length of the variable, for example
+.Ql #{n:window_name} .
.Pp
Prefixing a time variable with
.Ql t:\&
@@ -4770,6 +4789,7 @@ The following variables are available, where appropriate:
.It Li "pane_in_mode" Ta "" Ta "1 if pane is in a mode"
.It Li "pane_index" Ta "#P" Ta "Index of pane"
.It Li "pane_input_off" Ta "" Ta "1 if input to pane is disabled"
+.It Li "pane_last" Ta "" Ta "1 if last pane"
.It Li "pane_left" Ta "" Ta "Left of pane"
.It Li "pane_marked" Ta "" Ta "1 if this is the marked pane"
.It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set"
@@ -5098,7 +5118,7 @@ section).
Commands to alter and view the environment are:
.Bl -tag -width Ds
.It Xo Ic set-environment
-.Op Fl hgru
+.Op Fl Fhgru
.Op Fl t Ar target-session
.Ar name Op Ar value
.Xc
@@ -5109,6 +5129,11 @@ If
is used, the change is made in the global environment; otherwise, it is applied
to the session environment for
.Ar target-session .
+If
+.Fl F
+is present, then
+.Ar value
+is expanded as a format.
The
.Fl u
flag unsets a variable.
@@ -5314,6 +5339,7 @@ option.
This command works only from inside
.Nm .
.It Xo Ic display-menu
+.Op Fl O
.Op Fl c Ar target-client
.Op Fl t Ar target-pane
.Op Fl T Ar title
@@ -5365,8 +5391,14 @@ Both may be a row or column number, or one of the following special values:
Each menu consists of items followed by a key shortcut shown in brackets.
If the menu is too large to fit on the terminal, it is not displayed.
Pressing the key shortcut chooses the corresponding item.
-If the mouse is enabled and the menu is opened from a mouse key binding, releasing
-the mouse button with an item selected will choose that item.
+If the mouse is enabled and the menu is opened from a mouse key binding,
+releasing the mouse button with an item selected chooses that item and
+releasing the mouse button without an item selected closes the menu.
+.Fl O
+changes this behaviour so that the menu does not close when the mouse button is
+released without an item selected the menu is not closed and a mouse button
+must be clicked to choose an item.
+.Pp
The following keys are also available:
.Bl -column "Key" "Function" -offset indent
.It Sy "Key" Ta Sy "Function"
@@ -5378,6 +5410,7 @@ The following keys are also available:
.It Xo Ic display-message
.Op Fl aIpv
.Op Fl c Ar target-client
+.Op Fl d Ar delay
.Op Fl t Ar target-pane
.Op Ar message
.Xc
@@ -5387,7 +5420,14 @@ If
.Fl p
is given, the output is printed to stdout, otherwise it is displayed in the
.Ar target-client
-status line.
+status line for up to
+.Ar delay
+milliseconds.
+If
+.Ar delay
+is not given, the
+.Ic message-time
+option is used; a delay of zero waits for a key press.
The format of
.Ar message
is described in the
@@ -5629,12 +5669,21 @@ See the
.Sx FORMATS
section.
.It Xo Ic load-buffer
+.Op Fl w
.Op Fl b Ar buffer-name
+.Op Fl t Ar target-client
.Ar path
.Xc
.D1 (alias: Ic loadb )
Load the contents of the specified paste buffer from
.Ar path .
+If
+.Fl w
+is given, the buffer is also sent to the clipboard for
+.Ar target-client
+using the
+.Xr xterm 1
+escape sequence, if possible.
.It Xo Ic paste-buffer
.Op Fl dpr
.Op Fl b Ar buffer-name
@@ -5671,14 +5720,22 @@ The
.Fl a
option appends to rather than overwriting the file.
.It Xo Ic set-buffer
-.Op Fl a
+.Op Fl aw
.Op Fl b Ar buffer-name
+.Op Fl t Ar target-client
.Op Fl n Ar new-buffer-name
.Ar data
.Xc
.D1 (alias: Ic setb )
Set the contents of the specified buffer to
.Ar data .
+If
+.Fl w
+is given, the buffer is also sent to the clipboard for
+.Ar target-client
+using the
+.Xr xterm 1
+escape sequence, if possible.
The
.Fl a
option appends to rather than overwriting the buffer.
@@ -5855,8 +5912,8 @@ Set a styled underscore.
The single parameter is one of: 0 for no underscore, 1 for normal
underscore, 2 for double underscore, 3 for curly underscore, 4 for dotted
underscore and 5 for dashed underscore.
-.It Em \&Setulc
-Set the underscore colour.
+.It Em \&Setulc , \&ol
+Set the underscore colour or reset to the default.
The argument is (red * 65536) + (green * 256) + blue where each is between 0
and 255.
.It Em \&Ss , Se
diff --git a/tmux.c b/tmux.c
index b9a676a2..066714df 100644
--- a/tmux.c
+++ b/tmux.c
@@ -322,8 +322,8 @@ main(int argc, char **argv)
char *path = NULL, *label = NULL;
char *cause, **var;
const char *s, *shell, *cwd;
- int opt, flags = 0, keys;
- int feat = 0;
+ int opt, keys, feat = 0;
+ uint64_t flags = 0;
const struct options_table_entry *oe;
if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL &&
diff --git a/tmux.h b/tmux.h
index a17ae5c6..44ba53f5 100644
--- a/tmux.h
+++ b/tmux.h
@@ -450,6 +450,7 @@ enum tty_code_code {
TTYC_KUP6,
TTYC_KUP7,
TTYC_MS,
+ TTYC_OL,
TTYC_OP,
TTYC_REV,
TTYC_RGB,
@@ -461,6 +462,7 @@ enum tty_code_code {
TTYC_SE,
TTYC_SETAB,
TTYC_SETAF,
+ TTYC_SETAL,
TTYC_SETRGBB,
TTYC_SETRGBF,
TTYC_SETULC,
@@ -498,6 +500,7 @@ enum msgtype {
MSG_IDENTIFY_CWD,
MSG_IDENTIFY_FEATURES,
MSG_IDENTIFY_STDOUT,
+ MSG_IDENTIFY_LONGFLAGS,
MSG_COMMAND = 200,
MSG_DETACH,
@@ -1790,6 +1793,7 @@ enum options_table_type {
struct options_table_entry {
const char *name;
+ const char *alternative_name;
enum options_table_type type;
int scope;
int flags;
@@ -1809,6 +1813,11 @@ struct options_table_entry {
const char *unit;
};
+struct options_name_map {
+ const char *from;
+ const char *to;
+};
+
/* Common command usages. */
#define CMD_TARGET_PANE_USAGE "[-t target-pane]"
#define CMD_TARGET_WINDOW_USAGE "[-t target-window]"
@@ -2041,7 +2050,8 @@ int options_remove_or_default(struct options_entry *, int,
char **);
/* options-table.c */
-extern const struct options_table_entry options_table[];
+extern const struct options_table_entry options_table[];
+extern const struct options_name_map options_other_names[];
/* job.c */
typedef void (*job_update_cb) (struct job *);
@@ -2121,6 +2131,7 @@ int tty_open(struct tty *, char **);
void tty_close(struct tty *);
void tty_free(struct tty *);
void tty_update_features(struct tty *);
+void tty_set_selection(struct tty *, const char *, size_t);
void tty_write(void (*)(struct tty *, const struct tty_ctx *),
struct tty_ctx *);
void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *);
@@ -2325,7 +2336,7 @@ void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...);
void cmd_wait_for_flush(void);
/* client.c */
-int client_main(struct event_base *, int, char **, int, int);
+int client_main(struct event_base *, int, char **, uint64_t, int);
/* key-bindings.c */
struct key_table *key_bindings_get_table(const char *, int);
@@ -2452,8 +2463,7 @@ struct style_range *status_get_range(struct client *, u_int, u_int);
void status_init(struct client *);
void status_free(struct client *);
int status_redraw(struct client *);
-void printflike(3, 4) status_message_set(struct client *, int, const char *,
- ...);
+void status_message_set(struct client *, int, int, const char *, ...);
void status_message_clear(struct client *);
int status_message_redraw(struct client *);
void status_prompt_set(struct client *, struct cmd_find_state *,
@@ -2971,6 +2981,7 @@ __dead void printflike(1, 2) fatalx(const char *, ...);
/* menu.c */
#define MENU_NOMOUSE 0x1
#define MENU_TAB 0x2
+#define MENU_STAYOPEN 0x4
struct menu *menu_create(const char *);
void menu_add_items(struct menu *, const struct menu_item *,
struct cmdq_item *, struct client *,
diff --git a/tty-features.c b/tty-features.c
index 5891e2c3..f167a2d3 100644
--- a/tty-features.c
+++ b/tty-features.c
@@ -112,6 +112,7 @@ static const struct tty_feature tty_feature_overline = {
static const char *tty_feature_usstyle_capabilities[] = {
"Smulx=\\E[4::%p1%dm",
"Setulc=\\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m",
+ "ol=\\E[59m",
NULL
};
static const struct tty_feature tty_feature_usstyle = {
@@ -336,7 +337,7 @@ tty_default_features(int *feat, const char *name, u_int version)
"256,RGB,bpaste,clipboard,strikethrough,title"
{ .name = "mintty",
.features = TTY_FEATURES_BASE_MODERN_XTERM
- ",ccolour,cstyle,extkeys,margins,overline"
+ ",ccolour,cstyle,extkeys,margins,overline,usstyle"
},
{ .name = "tmux",
.features = TTY_FEATURES_BASE_MODERN_XTERM
diff --git a/tty-term.c b/tty-term.c
index f320db89..ec8302a6 100644
--- a/tty-term.c
+++ b/tty-term.c
@@ -248,6 +248,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_KUP6] = { TTYCODE_STRING, "kUP6" },
[TTYC_KUP7] = { TTYCODE_STRING, "kUP7" },
[TTYC_MS] = { TTYCODE_STRING, "Ms" },
+ [TTYC_OL] = { TTYCODE_STRING, "ol" },
[TTYC_OP] = { TTYCODE_STRING, "op" },
[TTYC_REV] = { TTYCODE_STRING, "rev" },
[TTYC_RGB] = { TTYCODE_FLAG, "RGB" },
@@ -258,6 +259,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_RMKX] = { TTYCODE_STRING, "rmkx" },
[TTYC_SETAB] = { TTYCODE_STRING, "setab" },
[TTYC_SETAF] = { TTYCODE_STRING, "setaf" },
+ [TTYC_SETAL] = { TTYCODE_STRING, "setal" },
[TTYC_SETRGBB] = { TTYCODE_STRING, "setrgbb" },
[TTYC_SETRGBF] = { TTYCODE_STRING, "setrgbf" },
[TTYC_SETULC] = { TTYCODE_STRING, "Setulc" },
@@ -305,6 +307,8 @@ tty_term_strip(const char *s)
ptr++;
if (*ptr == '>')
ptr++;
+ if (*ptr == '\0')
+ break;
}
buf[len++] = *ptr;
diff --git a/tty.c b/tty.c
index 14b770e2..bcbccca6 100644
--- a/tty.c
+++ b/tty.c
@@ -1896,19 +1896,27 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx)
{
- char *buf;
- size_t off;
+ tty_set_selection(tty, ctx->ptr, ctx->num);
+}
+void
+tty_set_selection(struct tty *tty, const char *buf, size_t len)
+{
+ char *encoded;
+ size_t size;
+
+ if (~tty->flags & TTY_STARTED)
+ return;
if (!tty_term_has(tty->term, TTYC_MS))
return;
- off = 4 * ((ctx->num + 2) / 3) + 1; /* storage for base64 */
- buf = xmalloc(off);
+ size = 4 * ((len + 2) / 3) + 1; /* storage for base64 */
+ encoded = xmalloc(size);
- b64_ntop(ctx->ptr, ctx->num, buf, off);
- tty_putcode_ptr2(tty, TTYC_MS, "", buf);
+ b64_ntop(buf, len, encoded, size);
+ tty_putcode_ptr2(tty, TTYC_MS, "", encoded);
- free(buf);
+ free(encoded);
}
void
@@ -2535,7 +2543,7 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
/* Is this a 24-bit or 256-colour colour? */
if (gc->fg & COLOUR_FLAG_RGB || gc->fg & COLOUR_FLAG_256) {
if (tty_try_colour(tty, gc->fg, "38") == 0)
- goto save_fg;
+ goto save;
/* Should not get here, already converted in tty_check_fg. */
return;
}
@@ -2547,13 +2555,13 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
tty_puts(tty, s);
} else
tty_putcode1(tty, TTYC_SETAF, gc->fg - 90 + 8);
- goto save_fg;
+ goto save;
}
/* Otherwise set the foreground colour. */
tty_putcode1(tty, TTYC_SETAF, gc->fg);
-save_fg:
+save:
/* Save the new values in the terminal current cell. */
tc->fg = gc->fg;
}
@@ -2567,7 +2575,7 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
/* Is this a 24-bit or 256-colour colour? */
if (gc->bg & COLOUR_FLAG_RGB || gc->bg & COLOUR_FLAG_256) {
if (tty_try_colour(tty, gc->bg, "48") == 0)
- goto save_bg;
+ goto save;
/* Should not get here, already converted in tty_check_bg. */
return;
}
@@ -2579,13 +2587,13 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
tty_puts(tty, s);
} else
tty_putcode1(tty, TTYC_SETAB, gc->bg - 90 + 8);
- goto save_bg;
+ goto save;
}
/* Otherwise set the background colour. */
tty_putcode1(tty, TTYC_SETAB, gc->bg);
-save_bg:
+save:
/* Save the new values in the terminal current cell. */
tc->bg = gc->bg;
}
@@ -2597,20 +2605,34 @@ tty_colours_us(struct tty *tty, const struct grid_cell *gc)
u_int c;
u_char r, g, b;
+ /* Clear underline colour. */
+ if (gc->us == 0) {
+ tty_putcode(tty, TTYC_OL);
+ goto save;
+ }
+
/* Must be an RGB colour - this should never happen. */
if (~gc->us & COLOUR_FLAG_RGB)
return;
/*
- * Setulc follows the ncurses(3) one argument "direct colour"
+ * Setulc and setal follows the ncurses(3) one argument "direct colour"
* capability format. Calculate the colour value.
*/
colour_split_rgb(gc->us, &r, &g, &b);
c = (65536 * r) + (256 * g) + b;
- /* Write the colour. */
- tty_putcode1(tty, TTYC_SETULC, c);
+ /*
+ * Write the colour. Only use setal if the RGB flag is set because the
+ * non-RGB version may be wrong.
+ */
+ if (tty_term_has(tty->term, TTYC_SETULC))
+ tty_putcode1(tty, TTYC_SETULC, c);
+ else if (tty_term_has(tty->term, TTYC_SETAL) &&
+ tty_term_has(tty->term, TTYC_RGB))
+ tty_putcode1(tty, TTYC_SETAL, c);
+save:
/* Save the new values in the terminal current cell. */
tc->us = gc->us;
}
diff --git a/window-copy.c b/window-copy.c
index 7103131d..1dc0c293 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -72,14 +72,13 @@ static int window_copy_search_marks(struct window_mode_entry *,
struct screen *, int, int);
static void window_copy_clear_marks(struct window_mode_entry *);
static void window_copy_move_left(struct screen *, u_int *, u_int *, int);
-static void window_copy_move_right(struct screen *, u_int *, u_int *, int);
static int window_copy_is_lowercase(const char *);
static int window_copy_search_jump(struct window_mode_entry *,
struct grid *, struct grid *, u_int, u_int, u_int, int, int,
- int, int);
-static int window_copy_search(struct window_mode_entry *, int, int);
-static int window_copy_search_up(struct window_mode_entry *, int);
-static int window_copy_search_down(struct window_mode_entry *, int);
+ int, int, u_int *);
+static int window_copy_search(struct window_mode_entry *, int, int, int);
+static int window_copy_search_up(struct window_mode_entry *, int, int);
+static int window_copy_search_down(struct window_mode_entry *, int, int);
static void window_copy_goto_line(struct window_mode_entry *, const char *);
static void window_copy_update_cursor(struct window_mode_entry *, u_int,
u_int);
@@ -110,7 +109,7 @@ static void window_copy_cursor_back_to_indentation(
static void window_copy_cursor_end_of_line(struct window_mode_entry *);
static void window_copy_other_end(struct window_mode_entry *);
static void window_copy_cursor_left(struct window_mode_entry *);
-static void window_copy_cursor_right(struct window_mode_entry *);
+static void window_copy_cursor_right(struct window_mode_entry *, int);
static void window_copy_cursor_up(struct window_mode_entry *, int);
static void window_copy_cursor_down(struct window_mode_entry *, int);
static void window_copy_cursor_jump(struct window_mode_entry *);
@@ -1094,7 +1093,7 @@ window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs)
u_int np = wme->prefix;
for (; np != 0; np--)
- window_copy_cursor_right(wme);
+ window_copy_cursor_right(wme, 0);
return (WINDOW_COPY_CMD_NOTHING);
}
@@ -1685,10 +1684,10 @@ window_copy_cmd_search_again(struct window_copy_cmd_state *cs)
if (data->searchtype == WINDOW_COPY_SEARCHUP) {
for (; np != 0; np--)
- window_copy_search_up(wme, data->searchregex);
+ window_copy_search_up(wme, data->searchregex, 1);
} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
for (; np != 0; np--)
- window_copy_search_down(wme, data->searchregex);
+ window_copy_search_down(wme, data->searchregex, 1);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@@ -1702,10 +1701,10 @@ window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs)
if (data->searchtype == WINDOW_COPY_SEARCHUP) {
for (; np != 0; np--)
- window_copy_search_down(wme, data->searchregex);
+ window_copy_search_down(wme, data->searchregex, 1);
} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
for (; np != 0; np--)
- window_copy_search_up(wme, data->searchregex);
+ window_copy_search_up(wme, data->searchregex, 1);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@@ -1953,7 +1952,7 @@ window_copy_cmd_search_backward(struct window_copy_cmd_state *cs)
data->searchregex = 1;
data->timeout = 0;
for (; np != 0; np--)
- window_copy_search_up(wme, 1);
+ window_copy_search_up(wme, 1, 0);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@@ -1973,7 +1972,7 @@ window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs)
data->searchregex = 0;
data->timeout = 0;
for (; np != 0; np--)
- window_copy_search_up(wme, 0);
+ window_copy_search_up(wme, 0, 0);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@@ -1993,7 +1992,7 @@ window_copy_cmd_search_forward(struct window_copy_cmd_state *cs)
data->searchregex = 1;
data->timeout = 0;
for (; np != 0; np--)
- window_copy_search_down(wme, 1);
+ window_copy_search_down(wme, 1, 0);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@@ -2013,7 +2012,7 @@ window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs)
data->searchregex = 0;
data->timeout = 0;
for (; np != 0; np--)
- window_copy_search_down(wme, 0);
+ window_copy_search_down(wme, 0, 0);
}
return (WINDOW_COPY_CMD_NOTHING);
}
@@ -2052,7 +2051,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
data->searchregex = 0;
free(data->searchstr);
data->searchstr = xstrdup(argument);
- if (!window_copy_search_up(wme, 0)) {
+ if (!window_copy_search_up(wme, 0, 1)) {
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW);
}
@@ -2062,7 +2061,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
data->searchregex = 0;
free(data->searchstr);
data->searchstr = xstrdup(argument);
- if (!window_copy_search_down(wme, 0)) {
+ if (!window_copy_search_down(wme, 0, 0)) {
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW);
}
@@ -2105,7 +2104,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
data->searchregex = 0;
free(data->searchstr);
data->searchstr = xstrdup(argument);
- if (!window_copy_search_down(wme, 0)) {
+ if (!window_copy_search_down(wme, 0, 1)) {
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW);
}
@@ -2115,7 +2114,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
data->searchregex = 0;
free(data->searchstr);
data->searchstr = xstrdup(argument);
- if (!window_copy_search_up(wme, 0)) {
+ if (!window_copy_search_up(wme, 0, 1)) {
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW);
}
@@ -2406,8 +2405,8 @@ window_copy_search_compare(struct grid *gd, u_int px, u_int py,
}
static int
-window_copy_search_lr(struct grid *gd,
- struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
+window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py,
+ u_int first, u_int last, int cis)
{
u_int ax, bx, px, pywrap, endline;
int matched;
@@ -2817,23 +2816,6 @@ window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
*fx = *fx - 1;
}
-static void
-window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
-{
- if (*fx == screen_size_x(s) - 1) { /* right */
- if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */
- if (wrapflag) {
- *fx = 0;
- *fy = 0;
- }
- return;
- }
- *fx = 0;
- *fy = *fy + 1;
- } else
- *fx = *fx + 1;
-}
-
static int
window_copy_is_lowercase(const char *ptr)
{
@@ -2854,7 +2836,7 @@ window_copy_is_lowercase(const char *ptr)
static int
window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
- int direction, int regex)
+ int direction, int regex, u_int *foundlen)
{
u_int i, px, sx, ssize = 1;
int found = 0, cflags = REG_EXTENDED;
@@ -2871,6 +2853,7 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
free(sbuf);
return (0);
}
+ free(sbuf);
}
if (direction) {
@@ -2878,15 +2861,20 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
if (regex) {
found = window_copy_search_lr_regex(gd,
&px, &sx, i, fx, gd->sx, &reg);
+ if (found)
+ *foundlen = sx;
} else {
found = window_copy_search_lr(gd, sgd,
&px, i, fx, gd->sx, cis);
+ if (found)
+ *foundlen = sgd->sx;
}
if (found)
break;
fx = 0;
}
} else {
+ *foundlen = 0;
for (i = fy + 1; endline < i; i--) {
if (regex) {
found = window_copy_search_rl_regex(gd,
@@ -2902,10 +2890,8 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
fx = gd->sx - 1;
}
}
- if (regex) {
- free(sbuf);
+ if (regex)
regfree(&reg);
- }
if (found) {
window_copy_scroll_to(wme, px, i, 1);
@@ -2915,7 +2901,7 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
return (window_copy_search_jump(wme, gd, sgd,
direction ? 0 : gd->sx - 1,
direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0,
- direction, regex));
+ direction, regex, foundlen));
}
return (0);
}
@@ -2925,7 +2911,8 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
* down.
*/
static int
-window_copy_search(struct window_mode_entry *wme, int direction, int regex)
+window_copy_search(struct window_mode_entry *wme, int direction, int regex,
+ int again)
{
struct window_pane *wp = wme->wp;
struct window_copy_mode_data *data = wme->data;
@@ -2933,7 +2920,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
struct screen_write_ctx ctx;
struct grid *gd = s->grid;
const char *str = data->searchstr;
- u_int fx, fy, endline;
+ u_int fx, fy, endline, i, foundlen;
int wrapflag, cis, found, visible_only;
if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0')
@@ -2961,18 +2948,23 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
wrapflag = options_get_number(wp->window->options, "wrap-search");
cis = window_copy_is_lowercase(str);
- if (direction) {
- window_copy_move_right(s, &fx, &fy, wrapflag);
+ if (direction)
endline = gd->hsize + gd->sy - 1;
- } else {
- window_copy_move_left(s, &fx, &fy, wrapflag);
+ else {
+ if (again)
+ window_copy_move_left(s, &fx, &fy, wrapflag);
endline = 0;
}
found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis,
- wrapflag, direction, regex);
- if (found)
+ wrapflag, direction, regex, &foundlen);
+ if (found) {
window_copy_search_marks(wme, &ss, regex, visible_only);
+ if (foundlen != 0) {
+ for (i = 0; i < foundlen; i++)
+ window_copy_cursor_right(wme, 1);
+ }
+ }
window_copy_redraw_screen(wme);
screen_free(&ss);
@@ -2995,8 +2987,8 @@ window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start,
}
static int
-window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px, u_int py,
- u_int *at)
+window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px,
+ u_int py, u_int *at)
{
struct screen *s = data->backing;
struct grid *gd = s->grid;
@@ -3049,6 +3041,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp,
free(sbuf);
return (0);
}
+ free(sbuf);
}
tstart = get_timer();
@@ -3096,7 +3089,7 @@ again:
data->searchgen++;
}
- px++;
+ px += width;
}
t = get_timer();
@@ -3146,10 +3139,8 @@ again:
out:
if (ssp == &ss)
screen_free(&ss);
- if (regex) {
- free(sbuf);
+ if (regex)
regfree(&reg);
- }
return (1);
}
@@ -3163,15 +3154,15 @@ window_copy_clear_marks(struct window_mode_entry *wme)
}
static int
-window_copy_search_up(struct window_mode_entry *wme, int regex)
+window_copy_search_up(struct window_mode_entry *wme, int regex, int again)
{
- return (window_copy_search(wme, 0, regex));
+ return (window_copy_search(wme, 0, regex, again));
}
static int
-window_copy_search_down(struct window_mode_entry *wme, int regex)
+window_copy_search_down(struct window_mode_entry *wme, int regex, int again)
{
- return (window_copy_search(wme, 1, regex));
+ return (window_copy_search(wme, 1, regex, again));
}
static void
@@ -3256,7 +3247,7 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
{
struct window_copy_mode_data *data = wme->data;
u_int mark, start, end, cy, cursor, current;
- int inv = 0;
+ int inv = 0, found = 0;
if (data->showmark && fy == data->my) {
gc->attr = mkgc->attr;
@@ -3282,20 +3273,28 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
return;
cy = screen_hsize(data->backing) - data->oy + data->cy;
- if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0 &&
- data->searchmark[cursor] == mark) {
- window_copy_match_start_end(data, cursor, &start, &end);
- if (current >= start && current <= end) {
- gc->attr = cgc->attr;
- if (inv) {
- gc->fg = cgc->bg;
- gc->bg = cgc->fg;
- }
- else {
- gc->fg = cgc->fg;
- gc->bg = cgc->bg;
+ if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) {
+ if (data->searchmark[cursor] == mark)
+ found = 1;
+ else if (cursor != 0) {
+ cursor--;
+ if (data->searchmark[cursor] == mark)
+ found = 1;
+ }
+ if (found) {
+ window_copy_match_start_end(data, cursor, &start, &end);
+ if (current >= start && current <= end) {
+ gc->attr = cgc->attr;
+ if (inv) {
+ gc->fg = cgc->bg;
+ gc->bg = cgc->fg;
+ }
+ else {
+ gc->fg = cgc->fg;
+ gc->bg = cgc->bg;
+ }
+ return;
}
- return;
}
}
@@ -3370,7 +3369,8 @@ window_copy_write_line(struct window_mode_entry *wme,
} else if (data->searchthis == -1) {
size = xsnprintf(hdr, sizeof hdr,
"(%d%s results) [%u/%u]", data->searchcount,
- data->searchmore ? "+" : "", data->oy, hsize);
+ data->searchmore ? "+" : "", data->oy,
+ hsize);
} else {
size = xsnprintf(hdr, sizeof hdr,
"(%d/%d results) [%u/%u]", data->searchthis,
@@ -4142,7 +4142,7 @@ window_copy_cursor_left(struct window_mode_entry *wme)
}
static void
-window_copy_cursor_right(struct window_mode_entry *wme)
+window_copy_cursor_right(struct window_mode_entry *wme, int all)
{
struct window_copy_mode_data *data = wme->data;
u_int px, py, yy, cx, cy;
@@ -4150,7 +4150,7 @@ window_copy_cursor_right(struct window_mode_entry *wme)
py = screen_hsize(data->backing) + data->cy - data->oy;
yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1;
- if (data->screen.sel != NULL && data->rectflag)
+ if (all || (data->screen.sel != NULL && data->rectflag))
px = screen_size_x(&data->screen);
else
px = window_copy_find_length(wme, py);
diff --git a/window-customize.c b/window-customize.c
index ecafc776..1dad07cd 100644
--- a/window-customize.c
+++ b/window-customize.c
@@ -380,7 +380,7 @@ window_customize_build_options(struct window_customize_modedata *data,
struct format_tree *ft, const char *filter, struct cmd_find_state *fs)
{
struct mode_tree_item *top;
- struct options_entry *o, *loop;
+ struct options_entry *o = NULL, *loop;
const char **list = NULL, *name;
u_int size = 0, i;
enum window_customize_scope scope;
@@ -1003,7 +1003,7 @@ window_customize_set_option_callback(struct client *c, void *itemdata,
fail:
*cause = toupper((u_char)*cause);
- status_message_set(c, 1, "%s", cause);
+ status_message_set(c, -1, 1, "%s", cause);
free(cause);
return (0);
}
@@ -1018,7 +1018,7 @@ window_customize_set_option(struct client *c,
struct options *oo;
struct window_customize_itemdata *new_item;
int flag, idx = item->idx;
- enum window_customize_scope scope;
+ enum window_customize_scope scope = WINDOW_CUSTOMIZE_NONE;
u_int choice;
const char *name = item->name, *space = "";
char *prompt, *value, *text;
@@ -1031,7 +1031,7 @@ window_customize_set_option(struct client *c,
return;
oe = options_table_entry(o);
- if (~oe->scope & OPTIONS_TABLE_PANE)
+ if (oe != NULL && ~oe->scope & OPTIONS_TABLE_PANE)
pane = 0;
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
scope = item->scope;
@@ -1209,7 +1209,7 @@ window_customize_set_command_callback(struct client *c, void *itemdata,
fail:
*error = toupper((u_char)*error);
- status_message_set(c, 1, "%s", error);
+ status_message_set(c, -1, 1, "%s", error);
free(error);
return (0);
}