aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Marriott <nicholas.marriott@gmail.com>2021-03-02 12:08:34 +0000
committerNicholas Marriott <nicholas.marriott@gmail.com>2021-03-02 12:08:34 +0000
commitc01251d02388efceca515c47c257e2b5342e3716 (patch)
treeca89407cb43bd2caa40a6854ba49ce1720e892ce
parent5c275c2a1a963876d4ac392067e42120417dbf43 (diff)
parent1466b570eedda0423d5a386d2b16b7ff0c0e477c (diff)
downloadrtmux-c01251d02388efceca515c47c257e2b5342e3716.tar.gz
rtmux-c01251d02388efceca515c47c257e2b5342e3716.tar.bz2
rtmux-c01251d02388efceca515c47c257e2b5342e3716.zip
Merge branch 'master' into 3.2-rc
-rw-r--r--.github/travis/build-all.sh2
-rw-r--r--.gitignore2
-rw-r--r--CHANGES70
-rw-r--r--Makefile.am14
-rw-r--r--alerts.c1
-rw-r--r--cfg.c30
-rw-r--r--client.c343
-rw-r--r--cmd-display-menu.c315
-rw-r--r--cmd-display-panes.c15
-rw-r--r--cmd-if-shell.c2
-rw-r--r--cmd-join-pane.c5
-rw-r--r--cmd-list-windows.c4
-rw-r--r--cmd-new-window.c38
-rw-r--r--cmd-parse.y6
-rw-r--r--cmd-queue.c6
-rw-r--r--cmd-run-shell.c85
-rw-r--r--cmd-save-buffer.c2
-rw-r--r--cmd-select-pane.c14
-rw-r--r--cmd-set-option.c21
-rw-r--r--cmd-show-options.c12
-rw-r--r--colour.c611
-rw-r--r--compat.h37
-rw-r--r--compat/clock_gettime.c37
-rw-r--r--compat/closefrom.c2
-rw-r--r--compat/forkpty-haiku.c82
-rw-r--r--configure.ac187
-rw-r--r--control-notify.c2
-rw-r--r--control.c1
-rw-r--r--file.c454
-rw-r--r--format-draw.c178
-rw-r--r--format.c2725
-rw-r--r--fuzz/input-fuzzer.c89
-rw-r--r--fuzz/input-fuzzer.dict8
-rw-r--r--fuzz/input-fuzzer.options2
-rw-r--r--grid-reader.c365
-rw-r--r--grid.c5
-rw-r--r--input.c144
-rw-r--r--job.c54
-rw-r--r--names.c2
-rw-r--r--options-table.c16
-rw-r--r--options.c7
-rw-r--r--osdep-cygwin.c1
-rw-r--r--osdep-darwin.c3
-rw-r--r--osdep-dragonfly.c1
-rw-r--r--osdep-freebsd.c1
-rw-r--r--osdep-haiku.c52
-rw-r--r--osdep-hpux.c2
-rw-r--r--osdep-linux.c1
-rw-r--r--osdep-netbsd.c1
-rw-r--r--osdep-openbsd.c3
-rw-r--r--osdep-sunos.c1
-rw-r--r--osdep-unknown.c2
-rw-r--r--popup.c211
-rw-r--r--proc.c38
-rw-r--r--regress/osc-11colours.sh243
-rw-r--r--screen-redraw.c34
-rw-r--r--screen-write.c375
-rw-r--r--screen.c41
-rw-r--r--server-client.c132
-rw-r--r--server-fn.c41
-rw-r--r--server.c30
-rw-r--r--spawn.c15
-rw-r--r--status.c58
-rw-r--r--tmux.1277
-rw-r--r--tmux.c57
-rw-r--r--tmux.h156
-rw-r--r--tty-term.c166
-rw-r--r--tty.c47
-rw-r--r--utf8.c8
-rw-r--r--window-buffer.c12
-rw-r--r--window-client.c12
-rw-r--r--window-copy.c763
-rw-r--r--window-customize.c10
-rw-r--r--window-tree.c12
-rw-r--r--window.c47
75 files changed, 6575 insertions, 2273 deletions
diff --git a/.github/travis/build-all.sh b/.github/travis/build-all.sh
index 00f8f522..883868e8 100644
--- a/.github/travis/build-all.sh
+++ b/.github/travis/build-all.sh
@@ -35,4 +35,4 @@ tar -zxf ncurses-*.tar.gz || exit 1
sh autogen.sh || exit 1
PKG_CONFIG_PATH=$BUILD/lib/pkgconfig ./configure --prefix=$BUILD "$@"
-make && make install || exit 1
+make && make install || (cat config.log; exit 1)
diff --git a/.gitignore b/.gitignore
index d01a0166..ec49a6de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,5 @@ configure
tmux.1.*
*.dSYM
cmd-parse.c
+fuzz/*-fuzzer
+.dirstamp
diff --git a/CHANGES b/CHANGES
index 6f3944c7..a81f160c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,69 @@
CHANGES FROM 3.1c TO 3.2
+* Improve performance of format evaluation.
+
+* Make jump command support UTF-8 in copy mode.
+
+* Support X11 colour names and other colour formats for OSC 10 and 11.
+
+* Add "pipe" variants of "copy-pipe" commands which do not copy.
+
+* Include "focused" in client flags.
+
+* Send Unicode directional isolate characters around horizontal pane borders if
+ the terminal supports UTF-8 and an extension terminfo(5) capability "Bidi" is
+ present.
+
+* Add a -S flag to new-window to make it select the existing window if one
+ with the given name already exists rather than failing with an error.
+
+* Addd a format modifier to check if a window or session name exists (N/w or
+ N/s).
+
+* Add compat clock_gettime for older macOS.
+
+* Add a no-detached choice to detach-on-destroy which detaches only if there
+ are no other detached sessions to switch to.
+
+* Add rectangle-on and rectangle-off copy mode commands.
+
+* Change so that window_flags escapes # automatically. A new format
+ window_raw_flags contains the old unescaped version.
+
+* Add -N flag to never start server even if command would normally do so.
+
+* With incremental search, start empty and only repeat the previous search if
+ the user tries to search again with an empty prompt.
+
+* Add a value for remain-on-exit that only keeps the pane if the program
+ failed.
+
+* Add a -C flag to run-shell to use a tmux command rather than a shell command.
+
+* Do not list user options with show-hooks.
+
+* Remove current match indicator in copy mode which can't work anymore since we
+ only search the visible region.
+
+* Make synchronize-panes a pane option and add -U flag to set-option to unset
+ an option on all panes.
+
+* Make replacement of ##s consistent when drawing formats, whether followed by
+ [ or not. Add a flag (e) to the q: format modifier to double up #s
+
+* Add -N flag to display-panes to ignore keys.
+
+* Change how escaping is processed for formats so that ## and # can be used in
+ styles.
+
+* Add a 'w' format modifier for string width.
+
+* Add support for Haiku.
+
+* Expand menu and popup -x and -y as formats.
+
+* Add numeric comparisons for formats.
+
* 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
@@ -267,9 +331,9 @@ CHANGES FROM 3.1c TO 3.2
* Change default position for display-menu -x and -y to centre rather than top
left.
-* Add support for per-client transient popups, similar to menus. These are
- created with new command display-popup. Popups may either show fixed text and
- trigger a tmux command when a key is pressed, or run a program (-R flag).
+* Add support for per-client transient popups, similar to menus but which are
+ connected to an external command (like a pane). These are created with new
+ command display-popup.
* Change double and triple click bindings so that only one is fired (previously
double click was fired on the way to triple click). Also add default double
diff --git a/Makefile.am b/Makefile.am
index 91d641fd..3e15204f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -28,7 +28,7 @@ AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare
AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align
AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes
-AM_CFLAGS += -Wno-unused-result
+AM_CFLAGS += -Wno-unused-result -Wno-format-y2k
AM_CPPFLAGS += -DDEBUG
endif
AM_CPPFLAGS += -iquote.
@@ -58,6 +58,11 @@ if IS_NETBSD
AM_CPPFLAGS += -D_OPENBSD_SOURCE
endif
+# Set flags for Haiku.
+if IS_HAIKU
+AM_CPPFLAGS += -D_BSD_SOURCE
+endif
+
# List of sources.
dist_tmux_SOURCES = \
alerts.c \
@@ -136,6 +141,7 @@ dist_tmux_SOURCES = \
file.c \
format.c \
format-draw.c \
+ grid-reader.c \
grid-view.c \
grid.c \
input-keys.c \
@@ -197,6 +203,12 @@ if HAVE_UTF8PROC
nodist_tmux_SOURCES += compat/utf8proc.c
endif
+if NEED_FUZZING
+check_PROGRAMS = fuzz/input-fuzzer
+fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS)
+fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
+endif
+
# Install tmux.1 in the right format.
install-exec-hook:
if test x@MANFORMAT@ = xmdoc; then \
diff --git a/alerts.c b/alerts.c
index 0f2eb179..4cc5c3eb 100644
--- a/alerts.c
+++ b/alerts.c
@@ -18,7 +18,6 @@
#include <sys/types.h>
-#include <event.h>
#include <stdlib.h>
#include "tmux.h"
diff --git a/cfg.c b/cfg.c
index 84be3967..3ef46e66 100644
--- a/cfg.c
+++ b/cfg.c
@@ -27,12 +27,15 @@
#include "tmux.h"
struct client *cfg_client;
-static char *cfg_file;
int cfg_finished;
static char **cfg_causes;
static u_int cfg_ncauses;
static struct cmdq_item *cfg_item;
+int cfg_quiet = 1;
+char **cfg_files;
+u_int cfg_nfiles;
+
static enum cmd_retval
cfg_client_done(__unused struct cmdq_item *item, __unused void *data)
{
@@ -60,18 +63,10 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
}
void
-set_cfg_file(const char *path)
-{
- free(cfg_file);
- cfg_file = xstrdup(path);
-}
-
-void
start_cfg(void)
{
struct client *c;
- char **paths;
- u_int i, n;
+ u_int i;
/*
* Configuration files are loaded without a client, so commands are run
@@ -89,15 +84,12 @@ start_cfg(void)
cmdq_append(c, cfg_item);
}
- if (cfg_file == NULL) {
- expand_paths(TMUX_CONF, &paths, &n);
- for (i = 0; i < n; i++) {
- load_cfg(paths[i], c, NULL, CMD_PARSE_QUIET, NULL);
- free(paths[i]);
- }
- free(paths);
- } else
- load_cfg(cfg_file, c, NULL, 0, NULL);
+ for (i = 0; i < cfg_nfiles; i++) {
+ if (cfg_quiet)
+ load_cfg(cfg_files[i], c, NULL, CMD_PARSE_QUIET, NULL);
+ else
+ load_cfg(cfg_files[i], c, NULL, 0, NULL);
+ }
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
}
diff --git a/client.c b/client.c
index 757e4aa8..fc0e1dde 100644
--- a/client.c
+++ b/client.c
@@ -24,7 +24,6 @@
#include <sys/file.h>
#include <errno.h>
-#include <event.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
@@ -62,7 +61,8 @@ static __dead void client_exec(const char *,const char *);
static int client_get_lock(char *);
static int client_connect(struct event_base *, const char *,
uint64_t);
-static void client_send_identify(const char *, const char *, int);
+static void client_send_identify(const char *, const char *,
+ char **, u_int, const char *, int);
static void client_signal(int);
static void client_dispatch(struct imsg *, void *);
static void client_dispatch_attached(struct imsg *);
@@ -127,6 +127,8 @@ retry:
log_debug("connect failed: %s", strerror(errno));
if (errno != ECONNREFUSED && errno != ENOENT)
goto failed;
+ if (flags & CLIENT_NOSTARTSERVER)
+ goto failed;
if (~flags & CLIENT_STARTSERVER)
goto failed;
close(fd);
@@ -221,20 +223,7 @@ client_exit_message(void)
static void
client_exit(void)
{
- struct client_file *cf;
- size_t left;
- int waiting = 0;
-
- RB_FOREACH (cf, client_files, &client_files) {
- if (cf->event == NULL)
- continue;
- left = EVBUFFER_LENGTH(cf->event->output);
- if (left != 0) {
- waiting++;
- log_debug("file %u %zu bytes left", cf->stream, left);
- }
- }
- if (waiting == 0)
+ if (!file_write_left(&client_files))
proc_exit(client_proc);
}
@@ -246,13 +235,14 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
struct cmd_parse_result *pr;
struct msg_command *data;
int fd, i;
- const char *ttynam, *cwd;
+ const char *ttynam, *termname, *cwd;
pid_t ppid;
enum msgtype msg;
struct termios tio, saved_tio;
size_t size, linesize = 0;
ssize_t linelen;
- char *line = NULL;
+ char *line = NULL, **caps = NULL, *cause;
+ u_int ncaps = 0;
/* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */
signal(SIGCHLD, SIG_IGN);
@@ -308,6 +298,8 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
cwd = "/";
if ((ttynam = ttyname(STDIN_FILENO)) == NULL)
ttynam = "";
+ if ((termname = getenv("TERM")) == NULL)
+ termname = "";
/*
* Drop privileges for client. "proc exec" is needed for -c and for
@@ -323,6 +315,16 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
NULL) != 0)
fatal("pledge failed");
+ /* Load terminfo entry if any. */
+ if (isatty(STDIN_FILENO) &&
+ *termname != '\0' &&
+ tty_term_read_list(termname, STDIN_FILENO, &caps, &ncaps,
+ &cause) != 0) {
+ fprintf(stderr, "%s\n", cause);
+ free(cause);
+ return (1);
+ }
+
/* Free stuff that is not used in the client. */
if (ptm_fd != -1)
close(ptm_fd);
@@ -353,7 +355,8 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
}
/* Send identify messages. */
- client_send_identify(ttynam, cwd, feat);
+ client_send_identify(ttynam, termname, caps, ncaps, cwd, feat);
+ tty_term_free_list(caps, ncaps);
/* Send first command. */
if (msg == MSG_COMMAND) {
@@ -436,27 +439,32 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
/* Send identify messages to server. */
static void
-client_send_identify(const char *ttynam, const char *cwd, int feat)
+client_send_identify(const char *ttynam, const char *termname, char **caps,
+ u_int ncaps, const char *cwd, int feat)
{
- const char *s;
- char **ss;
- size_t sslen;
- int fd, flags = client_flags;
- pid_t pid;
+ char **ss;
+ size_t sslen;
+ int fd, flags = client_flags;
+ pid_t pid;
+ u_int i;
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 = "";
- proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1);
+ proc_send(client_peer, MSG_IDENTIFY_TERM, -1, termname,
+ strlen(termname) + 1);
proc_send(client_peer, MSG_IDENTIFY_FEATURES, -1, &feat, sizeof feat);
proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam,
strlen(ttynam) + 1);
proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1);
+ for (i = 0; i < ncaps; i++) {
+ proc_send(client_peer, MSG_IDENTIFY_TERMINFO, -1,
+ caps[i], strlen(caps[i]) + 1);
+ }
+
if ((fd = dup(STDIN_FILENO)) == -1)
fatal("dup failed");
proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0);
@@ -477,257 +485,6 @@ client_send_identify(const char *ttynam, const char *cwd, int feat)
proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0);
}
-/* File write error callback. */
-static void
-client_write_error_callback(__unused struct bufferevent *bev,
- __unused short what, void *arg)
-{
- struct client_file *cf = arg;
-
- log_debug("write error file %d", cf->stream);
-
- bufferevent_free(cf->event);
- cf->event = NULL;
-
- close(cf->fd);
- cf->fd = -1;
-
- if (client_exitflag)
- client_exit();
-}
-
-/* File write callback. */
-static void
-client_write_callback(__unused struct bufferevent *bev, void *arg)
-{
- struct client_file *cf = arg;
-
- if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
- bufferevent_free(cf->event);
- close(cf->fd);
- RB_REMOVE(client_files, &client_files, cf);
- file_free(cf);
- }
-
- if (client_exitflag)
- client_exit();
-}
-
-/* Open write file. */
-static void
-client_write_open(void *data, size_t datalen)
-{
- struct msg_write_open *msg = data;
- const char *path;
- struct msg_write_ready reply;
- struct client_file find, *cf;
- const int flags = O_NONBLOCK|O_WRONLY|O_CREAT;
- int error = 0;
-
- if (datalen < sizeof *msg)
- fatalx("bad MSG_WRITE_OPEN size");
- if (datalen == sizeof *msg)
- path = "-";
- else
- path = (const char *)(msg + 1);
- log_debug("open write file %d %s", msg->stream, path);
-
- find.stream = msg->stream;
- if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) {
- cf = file_create(NULL, msg->stream, NULL, NULL);
- RB_INSERT(client_files, &client_files, cf);
- } else {
- error = EBADF;
- goto reply;
- }
- if (cf->closed) {
- error = EBADF;
- goto reply;
- }
-
- cf->fd = -1;
- if (msg->fd == -1)
- cf->fd = open(path, msg->flags|flags, 0644);
- else {
- if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
- errno = EBADF;
- else {
- cf->fd = dup(msg->fd);
- if (~client_flags & CLIENT_CONTROL)
- close(msg->fd); /* can only be used once */
- }
- }
- if (cf->fd == -1) {
- error = errno;
- goto reply;
- }
-
- cf->event = bufferevent_new(cf->fd, NULL, client_write_callback,
- client_write_error_callback, cf);
- bufferevent_enable(cf->event, EV_WRITE);
- goto reply;
-
-reply:
- reply.stream = msg->stream;
- reply.error = error;
- proc_send(client_peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
-}
-
-/* Write to client file. */
-static void
-client_write_data(void *data, size_t datalen)
-{
- struct msg_write_data *msg = data;
- struct client_file find, *cf;
- size_t size = datalen - sizeof *msg;
-
- if (datalen < sizeof *msg)
- fatalx("bad MSG_WRITE size");
- find.stream = msg->stream;
- if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL)
- fatalx("unknown stream number");
- log_debug("write %zu to file %d", size, cf->stream);
-
- if (cf->event != NULL)
- bufferevent_write(cf->event, msg + 1, size);
-}
-
-/* Close client file. */
-static void
-client_write_close(void *data, size_t datalen)
-{
- struct msg_write_close *msg = data;
- struct client_file find, *cf;
-
- if (datalen != sizeof *msg)
- fatalx("bad MSG_WRITE_CLOSE size");
- find.stream = msg->stream;
- if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL)
- fatalx("unknown stream number");
- log_debug("close file %d", cf->stream);
-
- if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
- if (cf->event != NULL)
- bufferevent_free(cf->event);
- if (cf->fd != -1)
- close(cf->fd);
- RB_REMOVE(client_files, &client_files, cf);
- file_free(cf);
- }
-}
-
-/* File read callback. */
-static void
-client_read_callback(__unused struct bufferevent *bev, void *arg)
-{
- struct client_file *cf = arg;
- void *bdata;
- size_t bsize;
- struct msg_read_data *msg;
- size_t msglen;
-
- msg = xmalloc(sizeof *msg);
- for (;;) {
- bdata = EVBUFFER_DATA(cf->event->input);
- bsize = EVBUFFER_LENGTH(cf->event->input);
-
- if (bsize == 0)
- break;
- if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
- bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
- log_debug("read %zu from file %d", bsize, cf->stream);
-
- msglen = (sizeof *msg) + bsize;
- msg = xrealloc(msg, msglen);
- msg->stream = cf->stream;
- memcpy(msg + 1, bdata, bsize);
- proc_send(client_peer, MSG_READ, -1, msg, msglen);
-
- evbuffer_drain(cf->event->input, bsize);
- }
- free(msg);
-}
-
-/* File read error callback. */
-static void
-client_read_error_callback(__unused struct bufferevent *bev,
- __unused short what, void *arg)
-{
- struct client_file *cf = arg;
- struct msg_read_done msg;
-
- log_debug("read error file %d", cf->stream);
-
- msg.stream = cf->stream;
- msg.error = 0;
- proc_send(client_peer, MSG_READ_DONE, -1, &msg, sizeof msg);
-
- bufferevent_free(cf->event);
- close(cf->fd);
- RB_REMOVE(client_files, &client_files, cf);
- file_free(cf);
-}
-
-/* Open read file. */
-static void
-client_read_open(void *data, size_t datalen)
-{
- struct msg_read_open *msg = data;
- const char *path;
- struct msg_read_done reply;
- struct client_file find, *cf;
- const int flags = O_NONBLOCK|O_RDONLY;
- int error;
-
- if (datalen < sizeof *msg)
- fatalx("bad MSG_READ_OPEN size");
- if (datalen == sizeof *msg)
- path = "-";
- else
- path = (const char *)(msg + 1);
- log_debug("open read file %d %s", msg->stream, path);
-
- find.stream = msg->stream;
- if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) {
- cf = file_create(NULL, msg->stream, NULL, NULL);
- RB_INSERT(client_files, &client_files, cf);
- } else {
- error = EBADF;
- goto reply;
- }
- if (cf->closed) {
- error = EBADF;
- goto reply;
- }
-
- cf->fd = -1;
- if (msg->fd == -1)
- cf->fd = open(path, flags);
- else {
- if (msg->fd != STDIN_FILENO)
- errno = EBADF;
- else {
- cf->fd = dup(msg->fd);
- if (~client_flags & CLIENT_CONTROL)
- close(msg->fd); /* can only be used once */
- }
- }
- if (cf->fd == -1) {
- error = errno;
- goto reply;
- }
-
- cf->event = bufferevent_new(cf->fd, client_read_callback, NULL,
- client_read_error_callback, cf);
- bufferevent_enable(cf->event, EV_READ);
- return;
-
-reply:
- reply.stream = msg->stream;
- reply.error = error;
- proc_send(client_peer, MSG_READ_DONE, -1, &reply, sizeof reply);
-}
-
/* Run command in shell; used for -c. */
static __dead void
client_exec(const char *shell, const char *shellcmd)
@@ -802,13 +559,25 @@ client_signal(int sig)
}
}
+/* Callback for file write error or close. */
+static void
+client_file_check_cb(__unused struct client *c, __unused const char *path,
+ __unused int error, __unused int closed, __unused struct evbuffer *buffer,
+ __unused void *data)
+{
+ if (client_exitflag)
+ client_exit();
+}
+
/* Callback for client read events. */
static void
client_dispatch(struct imsg *imsg, __unused void *arg)
{
if (imsg == NULL) {
- client_exitreason = CLIENT_EXIT_LOST_SERVER;
- client_exitval = 1;
+ if (!client_exitflag) {
+ client_exitreason = CLIENT_EXIT_LOST_SERVER;
+ client_exitval = 1;
+ }
proc_exit(client_proc);
return;
}
@@ -916,16 +685,20 @@ client_dispatch_wait(struct imsg *imsg)
proc_exit(client_proc);
break;
case MSG_READ_OPEN:
- client_read_open(data, datalen);
+ file_read_open(&client_files, client_peer, imsg, 1,
+ !(client_flags & CLIENT_CONTROL), client_file_check_cb,
+ NULL);
break;
case MSG_WRITE_OPEN:
- client_write_open(data, datalen);
+ file_write_open(&client_files, client_peer, imsg, 1,
+ !(client_flags & CLIENT_CONTROL), client_file_check_cb,
+ NULL);
break;
case MSG_WRITE:
- client_write_data(data, datalen);
+ file_write_data(&client_files, imsg);
break;
case MSG_WRITE_CLOSE:
- client_write_close(data, datalen);
+ file_write_close(&client_files, imsg);
break;
case MSG_OLDSTDERR:
case MSG_OLDSTDIN:
diff --git a/cmd-display-menu.c b/cmd-display-menu.c
index 205d1243..27a4c1d7 100644
--- a/cmd-display-menu.c
+++ b/cmd-display-menu.c
@@ -18,6 +18,7 @@
#include <sys/types.h>
+#include <paths.h>
#include <stdlib.h>
#include <string.h>
@@ -50,10 +51,10 @@ const struct cmd_entry cmd_display_popup_entry = {
.name = "display-popup",
.alias = "popup",
- .args = { "CEKc:d:h:R:t:w:x:y:", 0, -1 },
- .usage = "[-CEK] [-c target-client] [-d start-directory] [-h height] "
- "[-R shell-command] " CMD_TARGET_PANE_USAGE " [-w width] "
- "[-x position] [-y position] [command line ...]",
+ .args = { "Cc:d:Eh:t:w:x:y:", 0, -1 },
+ .usage = "[-CE] [-c target-client] [-d start-directory] [-h height] "
+ CMD_TARGET_PANE_USAGE " [-w width] "
+ "[-x position] [-y position] [command]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -61,7 +62,7 @@ const struct cmd_entry cmd_display_popup_entry = {
.exec = cmd_display_popup_exec
};
-static void
+static int
cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item,
struct args *args, u_int *px, u_int *py, u_int w, u_int h)
{
@@ -71,44 +72,46 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item,
struct session *s = tc->session;
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp;
- struct style_ranges *ranges;
- struct style_range *sr;
+ struct style_ranges *ranges = NULL;
+ struct style_range *sr = NULL;
const char *xp, *yp;
- u_int line, ox, oy, sx, sy, lines;
-
- lines = status_line_size(tc);
- for (line = 0; line < lines; line++) {
- ranges = &tc->status.entries[line].ranges;
- TAILQ_FOREACH(sr, ranges, entry) {
- if (sr->type == STYLE_RANGE_WINDOW)
- break;
- }
- if (sr != NULL)
- break;
+ char *p;
+ int top;
+ u_int line, ox, oy, sx, sy, lines, position;
+ long n;
+ struct format_tree *ft;
+
+ /*
+ * Work out the position from the -x and -y arguments. This is the
+ * bottom-left position.
+ */
+
+ /* If the popup is too big, stop now. */
+ if (w > tty->sx || h > tty->sy)
+ return (0);
+
+ /* Create format with mouse position if any. */
+ ft = format_create_from_target(item);
+ if (event->m.valid) {
+ format_add(ft, "popup_mouse_x", "%u", event->m.x);
+ format_add(ft, "popup_mouse_y", "%u", event->m.y);
}
- if (line == lines)
- ranges = &tc->status.entries[0].ranges;
- xp = args_get(args, 'x');
- if (xp == NULL || strcmp(xp, "C") == 0)
- *px = (tty->sx - 1) / 2 - w / 2;
- else if (strcmp(xp, "R") == 0)
- *px = tty->sx - 1;
- else if (strcmp(xp, "P") == 0) {
- tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
- if (wp->xoff >= ox)
- *px = wp->xoff - ox;
+ /*
+ * If there are any status lines, add this window position and the
+ * status line position.
+ */
+ top = status_at_line(tc);
+ if (top != -1) {
+ lines = status_line_size(tc);
+ if (top == 0)
+ top = lines;
else
- *px = 0;
- } else if (strcmp(xp, "M") == 0) {
- if (event->m.valid && event->m.x > w / 2)
- *px = event->m.x - w / 2;
- else
- *px = 0;
- } else if (strcmp(xp, "W") == 0) {
- if (status_at_line(tc) == -1)
- *px = 0;
- else {
+ top = 0;
+ position = options_get_number(s->options, "status-position");
+
+ for (line = 0; line < lines; line++) {
+ ranges = &tc->status.entries[line].ranges;
TAILQ_FOREACH(sr, ranges, entry) {
if (sr->type != STYLE_RANGE_WINDOW)
continue;
@@ -116,61 +119,137 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item,
break;
}
if (sr != NULL)
- *px = sr->start;
- else
- *px = 0;
+ break;
+ }
+ if (line == lines)
+ ranges = &tc->status.entries[0].ranges;
+
+ if (sr != NULL) {
+ format_add(ft, "popup_window_status_line_x", "%u",
+ sr->start);
+ if (position == 0) {
+ format_add(ft, "popup_window_status_line_y",
+ "%u", line + 1 + h);
+ } else {
+ format_add(ft, "popup_window_status_line_y",
+ "%u", tty->sy - lines + line);
+ }
+ }
+
+ if (position == 0)
+ format_add(ft, "popup_status_line_y", "%u", lines + h);
+ else {
+ format_add(ft, "popup_status_line_y", "%u",
+ tty->sy - lines);
}
} else
- *px = strtoul(xp, NULL, 10);
- if ((*px) + w >= tty->sx)
- *px = tty->sx - w;
+ top = 0;
- yp = args_get(args, 'y');
- if (yp == NULL || strcmp(yp, "C") == 0)
- *py = (tty->sy - 1) / 2 + h / 2;
- else if (strcmp(yp, "P") == 0) {
- tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
- if (wp->yoff + wp->sy >= oy)
- *py = wp->yoff + wp->sy - oy;
+ /* Popup width and height. */
+ format_add(ft, "popup_width", "%u", w);
+ format_add(ft, "popup_height", "%u", h);
+
+ /* Position so popup is in the centre. */
+ n = (long)(tty->sx - 1) / 2 - w / 2;
+ if (n < 0)
+ format_add(ft, "popup_centre_x", "%u", 0);
+ else
+ format_add(ft, "popup_centre_x", "%ld", n);
+ n = (tty->sy - 1) / 2 + h / 2;
+ if (n >= tty->sy)
+ format_add(ft, "popup_centre_y", "%u", tty->sy - h);
+ else
+ format_add(ft, "popup_centre_y", "%ld", n);
+
+ /* Position of popup relative to mouse. */
+ if (event->m.valid) {
+ n = (long)event->m.x - w / 2;
+ if (n < 0)
+ format_add(ft, "popup_mouse_centre_x", "%u", 0);
else
- *py = 0;
- } else if (strcmp(yp, "M") == 0) {
- if (event->m.valid)
- *py = event->m.y + h;
+ format_add(ft, "popup_mouse_centre_x", "%ld", n);
+ n = event->m.y - h / 2;
+ if (n + h >= tty->sy) {
+ format_add(ft, "popup_mouse_centre_y", "%u",
+ tty->sy - h);
+ } else
+ format_add(ft, "popup_mouse_centre_y", "%ld", n);
+ n = (long)event->m.y + h;
+ if (n + h >= tty->sy)
+ format_add(ft, "popup_mouse_top", "%u", tty->sy - h);
else
- *py = 0;
- } else if (strcmp(yp, "S") == 0) {
- if (options_get_number(s->options, "status-position") == 0) {
- if (lines != 0)
- *py = lines + h;
- else
- *py = 0;
- } else {
- if (lines != 0)
- *py = tty->sy - lines;
- else
- *py = tty->sy;
- }
- } else if (strcmp(yp, "W") == 0) {
- if (options_get_number(s->options, "status-position") == 0) {
- if (lines != 0)
- *py = line + 1 + h;
- else
- *py = 0;
- } else {
- if (lines != 0)
- *py = tty->sy - lines + line;
- else
- *py = tty->sy;
- }
- } else
- *py = strtoul(yp, NULL, 10);
- if (*py < h)
- *py = 0;
+ format_add(ft, "popup_mouse_top", "%ld", n);
+ n = event->m.y - h;
+ if (n < 0)
+ format_add(ft, "popup_mouse_bottom", "%u", 0);
+ else
+ format_add(ft, "popup_mouse_bottom", "%ld", n);
+ }
+
+ /* Position in pane. */
+ tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
+ n = top + wp->yoff - oy + h;
+ if (n >= tty->sy)
+ format_add(ft, "popup_pane_top", "%u", tty->sy - h);
+ else
+ format_add(ft, "popup_pane_top", "%ld", n);
+ format_add(ft, "popup_pane_bottom", "%u", top + wp->yoff + wp->sy - oy);
+ format_add(ft, "popup_pane_left", "%u", wp->xoff - ox);
+ n = (long)wp->xoff + wp->sx - ox - w;
+ if (n < 0)
+ format_add(ft, "popup_pane_right", "%u", 0);
+ else
+ format_add(ft, "popup_pane_right", "%ld", n);
+
+ /* Expand horizontal position. */
+ xp = args_get(args, 'x');
+ if (xp == NULL || strcmp(xp, "C") == 0)
+ xp = "#{popup_centre_x}";
+ else if (strcmp(xp, "R") == 0)
+ xp = "#{popup_right}";
+ else if (strcmp(xp, "P") == 0)
+ xp = "#{popup_pane_left}";
+ else if (strcmp(xp, "M") == 0)
+ xp = "#{popup_mouse_centre_x}";
+ else if (strcmp(xp, "W") == 0)
+ xp = "#{popup_window_status_line_x}";
+ p = format_expand(ft, xp);
+ n = strtol(p, NULL, 10);
+ if (n + w >= tty->sx)
+ n = tty->sx - w;
+ else if (n < 0)
+ n = 0;
+ *px = n;
+ log_debug("%s: -x: %s = %s = %u", __func__, xp, p, *px);
+ free(p);
+
+ /* Expand vertical position */
+ yp = args_get(args, 'y');
+ if (yp == NULL || strcmp(yp, "C") == 0)
+ yp = "#{popup_centre_y}";
+ else if (strcmp(yp, "P") == 0)
+ yp = "#{popup_pane_bottom}";
+ else if (strcmp(yp, "M") == 0)
+ yp = "#{popup_mouse_top}";
+ else if (strcmp(yp, "S") == 0)
+ yp = "#{popup_status_line_y}";
+ else if (strcmp(yp, "W") == 0)
+ yp = "#{popup_window_status_line_y}";
+ p = format_expand(ft, yp);
+ n = strtol(p, NULL, 10);
+ if (n < h)
+ n = 0;
else
- *py -= h;
- if ((*py) + h >= tty->sy)
- *py = tty->sy - h;
+ n -= h;
+ if (n + h >= tty->sy)
+ n = tty->sy - h;
+ else if (n < 0)
+ n = 0;
+ *py = n;
+ log_debug("%s: -y: %s = %s = %u", __func__, yp, p, *py);
+ free(p);
+
+ return (1);
}
static enum cmd_retval
@@ -226,8 +305,11 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
menu_free(menu);
return (CMD_RETURN_NORMAL);
}
- cmd_display_menu_get_position(tc, item, args, &px, &py, menu->width + 4,
- menu->count + 2);
+ if (!cmd_display_menu_get_position(tc, item, args, &px, &py,
+ menu->width + 4, menu->count + 2)) {
+ menu_free(menu);
+ return (CMD_RETURN_NORMAL);
+ }
if (args_has(args, 'O'))
flags |= MENU_STAYOPEN;
@@ -244,13 +326,14 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
+ struct session *s = target->s;
struct client *tc = cmdq_get_target_client(item);
struct tty *tty = &tc->tty;
- const char *value, *cmd = NULL, **lines = NULL;
+ const char *value, *shell[] = { NULL, NULL };
const char *shellcmd = NULL;
- char *cwd, *cause;
- int flags = 0;
- u_int px, py, w, h, nlines = 0;
+ char *cwd, *cause, **argv = args->argv;
+ int flags = 0, argc = args->argc;
+ u_int px, py, w, h;
if (args_has(args, 'C')) {
server_client_clear_overlay(tc);
@@ -259,17 +342,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
if (tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
- if (args->argc >= 1)
- cmd = args->argv[0];
- if (args->argc >= 2) {
- lines = (const char **)args->argv + 1;
- nlines = args->argc - 1;
- }
-
- if (nlines != 0)
- h = popup_height(nlines, lines) + 2;
- else
- h = tty->sy / 2;
+ h = tty->sy / 2;
if (args_has(args, 'h')) {
h = args_percentage(args, 'h', 1, tty->sy, tty->sy, &cause);
if (cause != NULL) {
@@ -279,10 +352,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
}
}
- if (nlines != 0)
- w = popup_width(item, nlines, lines, tc, target) + 2;
- else
- w = tty->sx / 2;
+ w = tty->sx / 2;
if (args_has(args, 'w')) {
w = args_percentage(args, 'w', 1, tty->sx, tty->sx, &cause);
if (cause != NULL) {
@@ -296,26 +366,33 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
w = tty->sx - 1;
if (h > tty->sy - 1)
h = tty->sy - 1;
- cmd_display_menu_get_position(tc, item, args, &px, &py, w, h);
+ if (!cmd_display_menu_get_position(tc, item, args, &px, &py, w, h))
+ return (CMD_RETURN_NORMAL);
value = args_get(args, 'd');
if (value != NULL)
cwd = format_single_from_target(item, value);
else
- cwd = xstrdup(server_client_get_cwd(tc, target->s));
-
- value = args_get(args, 'R');
- if (value != NULL)
- shellcmd = format_single_from_target(item, value);
+ cwd = xstrdup(server_client_get_cwd(tc, s));
+ if (argc == 0)
+ shellcmd = options_get_string(s->options, "default-command");
+ else if (argc == 1)
+ shellcmd = argv[0];
+ if (argc <= 1 && (shellcmd == NULL || *shellcmd == '\0')) {
+ shellcmd = NULL;
+ shell[0] = options_get_string(s->options, "default-shell");
+ if (!checkshell(shell[0]))
+ shell[0] = _PATH_BSHELL;
+ argc = 1;
+ argv = (char**)shell;
+ }
- if (args_has(args, 'K'))
- flags |= POPUP_WRITEKEYS;
if (args_has(args, 'E') > 1)
flags |= POPUP_CLOSEEXITZERO;
else if (args_has(args, 'E'))
flags |= POPUP_CLOSEEXIT;
- if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd,
- cmd, cwd, tc, target, NULL, NULL) != 0)
+ if (popup_display(flags, item, px, py, w, h, shellcmd, argc, argv, cwd,
+ tc, s, NULL, NULL) != 0)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
diff --git a/cmd-display-panes.c b/cmd-display-panes.c
index 352b2e4d..64efb89a 100644
--- a/cmd-display-panes.c
+++ b/cmd-display-panes.c
@@ -34,8 +34,8 @@ const struct cmd_entry cmd_display_panes_entry = {
.name = "display-panes",
.alias = "displayp",
- .args = { "bd:t:", 0, 1 },
- .usage = "[-b] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]",
+ .args = { "bd:Nt:", 0, 1 },
+ .usage = "[-bN] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]",
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
.exec = cmd_display_panes_exec
@@ -284,8 +284,15 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
else
cdata->item = item;
- server_client_set_overlay(tc, delay, NULL, NULL, cmd_display_panes_draw,
- cmd_display_panes_key, cmd_display_panes_free, cdata);
+ if (args_has(args, 'N')) {
+ server_client_set_overlay(tc, delay, NULL, NULL,
+ cmd_display_panes_draw, NULL, cmd_display_panes_free,
+ cdata);
+ } else {
+ server_client_set_overlay(tc, delay, NULL, NULL,
+ cmd_display_panes_draw, cmd_display_panes_key,
+ cmd_display_panes_free, cdata);
+ }
if (args_has(args, 'b'))
return (CMD_RETURN_NORMAL);
diff --git a/cmd-if-shell.c b/cmd-if-shell.c
index d980472a..65fbf19b 100644
--- a/cmd-if-shell.c
+++ b/cmd-if-shell.c
@@ -128,7 +128,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->input.c->references++;
cmd_find_copy_state(&cdata->input.fs, target);
- if (job_run(shellcmd, s,
+ if (job_run(shellcmd, 0, NULL, s,
server_client_get_cwd(cmdq_get_client(item), s), NULL,
cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1,
-1) == NULL) {
diff --git a/cmd-join-pane.c b/cmd-join-pane.c
index 306cf4bc..36805c46 100644
--- a/cmd-join-pane.c
+++ b/cmd-join-pane.c
@@ -142,7 +142,10 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options);
src_wp->flags |= PANE_STYLECHANGED;
- TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
+ if (flags & SPAWN_BEFORE)
+ TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry);
+ else
+ TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
layout_assign_pane(lc, src_wp);
recalculate_sizes();
diff --git a/cmd-list-windows.c b/cmd-list-windows.c
index 9c33c2d0..d6cc0b7a 100644
--- a/cmd-list-windows.c
+++ b/cmd-list-windows.c
@@ -28,14 +28,14 @@
*/
#define LIST_WINDOWS_TEMPLATE \
- "#{window_index}: #{window_name}#{window_flags} " \
+ "#{window_index}: #{window_name}#{window_raw_flags} " \
"(#{window_panes} panes) " \
"[#{window_width}x#{window_height}] " \
"[layout #{window_layout}] #{window_id}" \
"#{?window_active, (active),}";
#define LIST_WINDOWS_WITH_SESSION_TEMPLATE \
"#{session_name}:" \
- "#{window_index}: #{window_name}#{window_flags} " \
+ "#{window_index}: #{window_name}#{window_raw_flags} " \
"(#{window_panes} panes) " \
"[#{window_width}x#{window_height}] "
diff --git a/cmd-new-window.c b/cmd-new-window.c
index ca3e66c4..712e2a79 100644
--- a/cmd-new-window.c
+++ b/cmd-new-window.c
@@ -38,8 +38,8 @@ const struct cmd_entry cmd_new_window_entry = {
.name = "new-window",
.alias = "neww",
- .args = { "abc:de:F:kn:Pt:", 0, -1 },
- .usage = "[-abdkP] [-c start-directory] [-e environment] [-F format] "
+ .args = { "abc:de:F:kn:PSt:", 0, -1 },
+ .usage = "[-abdkPS] [-c start-directory] [-e environment] [-F format] "
"[-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
@@ -52,6 +52,7 @@ static enum cmd_retval
cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
+ struct client *c = cmdq_get_client(item);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct spawn_context sc;
@@ -59,12 +60,41 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = target->s;
struct winlink *wl = target->wl;
int idx = target->idx, before;
- struct winlink *new_wl;
+ struct winlink *new_wl = NULL;
char *cause = NULL, *cp;
- const char *template, *add;
+ const char *template, *add, *name;
struct cmd_find_state fs;
struct args_value *value;
+ /*
+ * If -S and -n are given and -t is not and a single window with this
+ * name already exists, select it.
+ */
+ name = args_get(args, 'n');
+ if (args_has(args, 'S') && name != NULL && target->idx == -1) {
+ RB_FOREACH(wl, winlinks, &s->windows) {
+ if (strcmp(wl->window->name, name) != 0)
+ continue;
+ if (new_wl == NULL) {
+ new_wl = wl;
+ continue;
+ }
+ cmdq_error(item, "multiple windows named %s", name);
+ return (CMD_RETURN_ERROR);
+ }
+ if (new_wl != NULL) {
+ if (args_has(args, 'd'))
+ return (CMD_RETURN_NORMAL);
+ if (session_set_current(s, new_wl) == 0)
+ server_redraw_session(s);
+ if (c != NULL && c->session != NULL)
+ s->curw->window->latest = c;
+ recalculate_sizes();
+ return (CMD_RETURN_NORMAL);
+ }
+ }
+
+
before = args_has(args, 'b');
if (args_has(args, 'a') || before) {
idx = winlink_shuffle_up(s, wl, before);
diff --git a/cmd-parse.y b/cmd-parse.y
index c8995d8b..7b42c621 100644
--- a/cmd-parse.y
+++ b/cmd-parse.y
@@ -1505,8 +1505,12 @@ yylex_token(int ch)
state == NONE)
break;
- /* Spaces and comments inside quotes after \n are removed. */
+ /*
+ * Spaces and comments inside quotes after \n are removed but
+ * the \n is left.
+ */
if (ch == '\n' && state != NONE) {
+ yylex_append1(&buf, &len, '\n');
while ((ch = yylex_getc()) == ' ' || ch == '\t')
/* nothing */;
if (ch != '#')
diff --git a/cmd-queue.c b/cmd-queue.c
index 36f1c9be..05f439f5 100644
--- a/cmd-queue.c
+++ b/cmd-queue.c
@@ -768,7 +768,11 @@ cmdq_running(struct client *c)
{
struct cmdq_list *queue = cmdq_get(c);
- return (queue->item);
+ if (queue->item == NULL)
+ return (NULL);
+ if (queue->item->flags & CMDQ_WAITING)
+ return (NULL);
+ return (queue->item);
}
/* Print a guard line. */
diff --git a/cmd-run-shell.c b/cmd-run-shell.c
index 647f533f..4f30d05d 100644
--- a/cmd-run-shell.c
+++ b/cmd-run-shell.c
@@ -40,8 +40,8 @@ const struct cmd_entry cmd_run_shell_entry = {
.name = "run-shell",
.alias = "run",
- .args = { "bd:t:", 0, 1 },
- .usage = "[-b] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]",
+ .args = { "bd:Ct:", 0, 1 },
+ .usage = "[-bC] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -50,13 +50,16 @@ const struct cmd_entry cmd_run_shell_entry = {
};
struct cmd_run_shell_data {
+ struct client *client;
char *cmd;
+ int shell;
char *cwd;
struct cmdq_item *item;
struct session *s;
int wp_id;
struct event timer;
int flags;
+ struct cmd_parse_input pi;
};
static void
@@ -93,49 +96,69 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct cmd_run_shell_data *cdata;
+ struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct window_pane *wp = target->wp;
const char *delay;
double d;
struct timeval tv;
char *end;
+ int wait = !args_has(args, 'b');
+
+ if ((delay = args_get(args, 'd')) != NULL) {
+ d = strtod(delay, &end);
+ if (*end != '\0') {
+ cmdq_error(item, "invalid delay time: %s", delay);
+ return (CMD_RETURN_ERROR);
+ }
+ } else if (args->argc == 0)
+ return (CMD_RETURN_NORMAL);
cdata = xcalloc(1, sizeof *cdata);
if (args->argc != 0)
cdata->cmd = format_single_from_target(item, args->argv[0]);
+ cdata->shell = !args_has(args, 'C');
+ if (!cdata->shell) {
+ memset(&cdata->pi, 0, sizeof cdata->pi);
+ cmd_get_source(self, &cdata->pi.file, &cdata->pi.line);
+ if (wait)
+ cdata->pi.item = item;
+ cdata->pi.c = tc;
+ cmd_find_copy_state(&cdata->pi.fs, target);
+ }
+
if (args_has(args, 't') && wp != NULL)
cdata->wp_id = wp->id;
else
cdata->wp_id = -1;
- if (!args_has(args, 'b'))
+ if (wait) {
+ cdata->client = cmdq_get_client(item);
cdata->item = item;
- else
+ } else {
+ cdata->client = tc;
cdata->flags |= JOB_NOWAIT;
+ }
+ if (cdata->client != NULL)
+ cdata->client->references++;
cdata->cwd = xstrdup(server_client_get_cwd(cmdq_get_client(item), s));
+
cdata->s = s;
if (s != NULL)
session_add_ref(s, __func__);
evtimer_set(&cdata->timer, cmd_run_shell_timer, cdata);
-
- if ((delay = args_get(args, 'd')) != NULL) {
- d = strtod(delay, &end);
- if (*end != '\0') {
- cmdq_error(item, "invalid delay time: %s", delay);
- cmd_run_shell_free(cdata);
- return (CMD_RETURN_ERROR);
- }
+ if (delay != NULL) {
timerclear(&tv);
tv.tv_sec = (time_t)d;
tv.tv_usec = (d - (double)tv.tv_sec) * 1000000U;
evtimer_add(&cdata->timer, &tv);
} else
- cmd_run_shell_timer(-1, 0, cdata);
+ event_active(&cdata->timer, EV_TIMEOUT, 1);
- if (args_has(args, 'b'))
+ if (!wait)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
@@ -144,17 +167,37 @@ static void
cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
{
struct cmd_run_shell_data *cdata = arg;
+ struct client *c = cdata->client;
+ const char *cmd = cdata->cmd;
+ char *error;
+ struct cmdq_item *item = cdata->item;
+ enum cmd_parse_status status;
- if (cdata->cmd != NULL) {
- if (job_run(cdata->cmd, cdata->s, cdata->cwd, NULL,
+ if (cmd != NULL && cdata->shell) {
+ if (job_run(cmd, 0, NULL, cdata->s, cdata->cwd, NULL,
cmd_run_shell_callback, cmd_run_shell_free, cdata,
cdata->flags, -1, -1) == NULL)
cmd_run_shell_free(cdata);
- } else {
- if (cdata->item != NULL)
- cmdq_continue(cdata->item);
- cmd_run_shell_free(cdata);
+ return;
}
+
+ if (cmd != NULL) {
+ if (item != NULL) {
+ status = cmd_parse_and_insert(cmd, &cdata->pi, item,
+ cmdq_get_state(item), &error);
+ } else {
+ status = cmd_parse_and_append(cmd, &cdata->pi, c, NULL,
+ &error);
+ }
+ if (status == CMD_PARSE_ERROR) {
+ cmdq_error(cdata->item, "%s", error);
+ free(error);
+ }
+ }
+
+ if (cdata->item != NULL)
+ cmdq_continue(cdata->item);
+ cmd_run_shell_free(cdata);
}
static void
@@ -215,6 +258,8 @@ cmd_run_shell_free(void *data)
evtimer_del(&cdata->timer);
if (cdata->s != NULL)
session_remove_ref(cdata->s, __func__);
+ if (cdata->client != NULL)
+ server_client_unref(cdata->client);
free(cdata->cwd);
free(cdata->cmd);
free(cdata);
diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c
index ef02c202..7faea926 100644
--- a/cmd-save-buffer.c
+++ b/cmd-save-buffer.c
@@ -108,7 +108,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'a'))
flags = O_APPEND;
else
- flags = 0;
+ flags = O_TRUNC;
file_write(cmdq_get_client(item), path, flags, bufdata, bufsize,
cmd_save_buffer_done, item);
free(path);
diff --git a/cmd-select-pane.c b/cmd-select-pane.c
index 30529722..fa388548 100644
--- a/cmd-select-pane.c
+++ b/cmd-select-pane.c
@@ -108,11 +108,15 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "no last pane");
return (CMD_RETURN_ERROR);
}
- if (args_has(args, 'e'))
+ if (args_has(args, 'e')) {
lastwp->flags &= ~PANE_INPUTOFF;
- else if (args_has(args, 'd'))
+ server_redraw_window_borders(lastwp->window);
+ server_status_window(lastwp->window);
+ } else if (args_has(args, 'd')) {
lastwp->flags |= PANE_INPUTOFF;
- else {
+ server_redraw_window_borders(lastwp->window);
+ server_status_window(lastwp->window);
+ } else {
if (window_push_zoom(w, args_has(args, 'Z')))
server_redraw_window(w);
window_redraw_active_switch(w, lastwp);
@@ -188,10 +192,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'e')) {
wp->flags &= ~PANE_INPUTOFF;
+ server_redraw_window_borders(wp->window);
+ server_status_window(wp->window);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'd')) {
wp->flags |= PANE_INPUTOFF;
+ server_redraw_window_borders(wp->window);
+ server_status_window(wp->window);
return (CMD_RETURN_NORMAL);
}
diff --git a/cmd-set-option.c b/cmd-set-option.c
index 0df12aa0..70e3c54d 100644
--- a/cmd-set-option.c
+++ b/cmd-set-option.c
@@ -33,8 +33,8 @@ const struct cmd_entry cmd_set_option_entry = {
.name = "set-option",
.alias = "set",
- .args = { "aFgopqst:uw", 1, 2 },
- .usage = "[-aFgopqsuw] " CMD_TARGET_PANE_USAGE " option [value]",
+ .args = { "aFgopqst:uUw", 1, 2 },
+ .usage = "[-aFgopqsuUw] " CMD_TARGET_PANE_USAGE " option [value]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -74,8 +74,9 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = cmd_get_args(self);
int append = args_has(args, 'a');
struct cmd_find_state *target = cmdq_get_target(item);
+ struct window_pane *loop;
struct options *oo;
- struct options_entry *parent, *o;
+ struct options_entry *parent, *o, *po;
char *name, *argument, *value = NULL, *cause;
int window, idx, already, error, ambiguous;
int scope;
@@ -148,7 +149,19 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
}
/* Change the option. */
- if (args_has(args, 'u')) {
+ if (args_has(args, 'U') && scope == OPTIONS_TABLE_WINDOW) {
+ TAILQ_FOREACH(loop, &target->w->panes, entry) {
+ po = options_get_only(loop->options, name);
+ if (po == NULL)
+ continue;
+ if (options_remove_or_default(po, idx, &cause) != 0) {
+ cmdq_error(item, "%s", cause);
+ free(cause);
+ goto fail;
+ }
+ }
+ }
+ if (args_has(args, 'u') || args_has(args, 'U')) {
if (o == NULL)
goto out;
if (options_remove_or_default(o, idx, &cause) != 0) {
diff --git a/cmd-show-options.c b/cmd-show-options.c
index ed93311a..e3ba821d 100644
--- a/cmd-show-options.c
+++ b/cmd-show-options.c
@@ -200,11 +200,13 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
u_int idx;
int parent;
- o = options_first(oo);
- while (o != NULL) {
- if (options_table_entry(o) == NULL)
- cmd_show_options_print(self, item, o, -1, 0);
- o = options_next(o);
+ if (cmd_get_entry(self) != &cmd_show_hooks_entry) {
+ o = options_first(oo);
+ while (o != NULL) {
+ if (options_table_entry(o) == NULL)
+ cmd_show_options_print(self, item, o, -1, 0);
+ o = options_next(o);
+ }
}
for (oe = options_table; oe->name != NULL; oe++) {
if (~oe->scope & scope)
diff --git a/colour.c b/colour.c
index ee4b95db..1d1729ca 100644
--- a/colour.c
+++ b/colour.c
@@ -22,6 +22,7 @@
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
+#include <math.h>
#include "tmux.h"
@@ -111,6 +112,9 @@ colour_tostring(int c)
static char s[32];
u_char r, g, b;
+ if (c == -1)
+ return ("invalid");
+
if (c & COLOUR_FLAG_RGB) {
colour_split_rgb(c, &r, &g, &b);
xsnprintf(s, sizeof s, "#%02x%02x%02x", r, g, b);
@@ -233,7 +237,7 @@ colour_fromstring(const char *s)
return (96);
if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0)
return (97);
- return (-1);
+ return (colour_byname(s));
}
/* Convert 256 colour to RGB colour. */
@@ -335,3 +339,608 @@ colour_256to16(int c)
return (table[c & 0xff]);
}
+
+/* Get colour by X11 colour name. */
+int
+colour_byname(const char *name)
+{
+ static const struct {
+ const char *name;
+ int c;
+ } colours[] = {
+ { "AliceBlue", 0xf0f8ff },
+ { "AntiqueWhite", 0xfaebd7 },
+ { "AntiqueWhite1", 0xffefdb },
+ { "AntiqueWhite2", 0xeedfcc },
+ { "AntiqueWhite3", 0xcdc0b0 },
+ { "AntiqueWhite4", 0x8b8378 },
+ { "BlanchedAlmond", 0xffebcd },
+ { "BlueViolet", 0x8a2be2 },
+ { "CadetBlue", 0x5f9ea0 },
+ { "CadetBlue1", 0x98f5ff },
+ { "CadetBlue2", 0x8ee5ee },
+ { "CadetBlue3", 0x7ac5cd },
+ { "CadetBlue4", 0x53868b },
+ { "CornflowerBlue", 0x6495ed },
+ { "DarkBlue", 0x00008b },
+ { "DarkCyan", 0x008b8b },
+ { "DarkGoldenrod", 0xb8860b },
+ { "DarkGoldenrod1", 0xffb90f },
+ { "DarkGoldenrod2", 0xeead0e },
+ { "DarkGoldenrod3", 0xcd950c },
+ { "DarkGoldenrod4", 0x8b6508 },
+ { "DarkGray", 0xa9a9a9 },
+ { "DarkGreen", 0x006400 },
+ { "DarkGrey", 0xa9a9a9 },
+ { "DarkKhaki", 0xbdb76b },
+ { "DarkMagenta", 0x8b008b },
+ { "DarkOliveGreen", 0x556b2f },
+ { "DarkOliveGreen1", 0xcaff70 },
+ { "DarkOliveGreen2", 0xbcee68 },
+ { "DarkOliveGreen3", 0xa2cd5a },
+ { "DarkOliveGreen4", 0x6e8b3d },
+ { "DarkOrange", 0xff8c00 },
+ { "DarkOrange1", 0xff7f00 },
+ { "DarkOrange2", 0xee7600 },
+ { "DarkOrange3", 0xcd6600 },
+ { "DarkOrange4", 0x8b4500 },
+ { "DarkOrchid", 0x9932cc },
+ { "DarkOrchid1", 0xbf3eff },
+ { "DarkOrchid2", 0xb23aee },
+ { "DarkOrchid3", 0x9a32cd },
+ { "DarkOrchid4", 0x68228b },
+ { "DarkRed", 0x8b0000 },
+ { "DarkSalmon", 0xe9967a },
+ { "DarkSeaGreen", 0x8fbc8f },
+ { "DarkSeaGreen1", 0xc1ffc1 },
+ { "DarkSeaGreen2", 0xb4eeb4 },
+ { "DarkSeaGreen3", 0x9bcd9b },
+ { "DarkSeaGreen4", 0x698b69 },
+ { "DarkSlateBlue", 0x483d8b },
+ { "DarkSlateGray", 0x2f4f4f },
+ { "DarkSlateGray1", 0x97ffff },
+ { "DarkSlateGray2", 0x8deeee },
+ { "DarkSlateGray3", 0x79cdcd },
+ { "DarkSlateGray4", 0x528b8b },
+ { "DarkSlateGrey", 0x2f4f4f },
+ { "DarkTurquoise", 0x00ced1 },
+ { "DarkViolet", 0x9400d3 },
+ { "DeepPink", 0xff1493 },
+ { "DeepPink1", 0xff1493 },
+ { "DeepPink2", 0xee1289 },
+ { "DeepPink3", 0xcd1076 },
+ { "DeepPink4", 0x8b0a50 },
+ { "DeepSkyBlue", 0x00bfff },
+ { "DeepSkyBlue1", 0x00bfff },
+ { "DeepSkyBlue2", 0x00b2ee },
+ { "DeepSkyBlue3", 0x009acd },
+ { "DeepSkyBlue4", 0x00688b },
+ { "DimGray", 0x696969 },
+ { "DimGrey", 0x696969 },
+ { "DodgerBlue", 0x1e90ff },
+ { "DodgerBlue1", 0x1e90ff },
+ { "DodgerBlue2", 0x1c86ee },
+ { "DodgerBlue3", 0x1874cd },
+ { "DodgerBlue4", 0x104e8b },
+ { "FloralWhite", 0xfffaf0 },
+ { "ForestGreen", 0x228b22 },
+ { "GhostWhite", 0xf8f8ff },
+ { "GreenYellow", 0xadff2f },
+ { "HotPink", 0xff69b4 },
+ { "HotPink1", 0xff6eb4 },
+ { "HotPink2", 0xee6aa7 },
+ { "HotPink3", 0xcd6090 },
+ { "HotPink4", 0x8b3a62 },
+ { "IndianRed", 0xcd5c5c },
+ { "IndianRed1", 0xff6a6a },
+ { "IndianRed2", 0xee6363 },
+ { "IndianRed3", 0xcd5555 },
+ { "IndianRed4", 0x8b3a3a },
+ { "LavenderBlush", 0xfff0f5 },
+ { "LavenderBlush1", 0xfff0f5 },
+ { "LavenderBlush2", 0xeee0e5 },
+ { "LavenderBlush3", 0xcdc1c5 },
+ { "LavenderBlush4", 0x8b8386 },
+ { "LawnGreen", 0x7cfc00 },
+ { "LemonChiffon", 0xfffacd },
+ { "LemonChiffon1", 0xfffacd },
+ { "LemonChiffon2", 0xeee9bf },
+ { "LemonChiffon3", 0xcdc9a5 },
+ { "LemonChiffon4", 0x8b8970 },
+ { "LightBlue", 0xadd8e6 },
+ { "LightBlue1", 0xbfefff },
+ { "LightBlue2", 0xb2dfee },
+ { "LightBlue3", 0x9ac0cd },
+ { "LightBlue4", 0x68838b },
+ { "LightCoral", 0xf08080 },
+ { "LightCyan", 0xe0ffff },
+ { "LightCyan1", 0xe0ffff },
+ { "LightCyan2", 0xd1eeee },
+ { "LightCyan3", 0xb4cdcd },
+ { "LightCyan4", 0x7a8b8b },
+ { "LightGoldenrod", 0xeedd82 },
+ { "LightGoldenrod1", 0xffec8b },
+ { "LightGoldenrod2", 0xeedc82 },
+ { "LightGoldenrod3", 0xcdbe70 },
+ { "LightGoldenrod4", 0x8b814c },
+ { "LightGoldenrodYellow", 0xfafad2 },
+ { "LightGray", 0xd3d3d3 },
+ { "LightGreen", 0x90ee90 },
+ { "LightGrey", 0xd3d3d3 },
+ { "LightPink", 0xffb6c1 },
+ { "LightPink1", 0xffaeb9 },
+ { "LightPink2", 0xeea2ad },
+ { "LightPink3", 0xcd8c95 },
+ { "LightPink4", 0x8b5f65 },
+ { "LightSalmon", 0xffa07a },
+ { "LightSalmon1", 0xffa07a },
+ { "LightSalmon2", 0xee9572 },
+ { "LightSalmon3", 0xcd8162 },
+ { "LightSalmon4", 0x8b5742 },
+ { "LightSeaGreen", 0x20b2aa },
+ { "LightSkyBlue", 0x87cefa },
+ { "LightSkyBlue1", 0xb0e2ff },
+ { "LightSkyBlue2", 0xa4d3ee },
+ { "LightSkyBlue3", 0x8db6cd },
+ { "LightSkyBlue4", 0x607b8b },
+ { "LightSlateBlue", 0x8470ff },
+ { "LightSlateGray", 0x778899 },
+ { "LightSlateGrey", 0x778899 },
+ { "LightSteelBlue", 0xb0c4de },
+ { "LightSteelBlue1", 0xcae1ff },
+ { "LightSteelBlue2", 0xbcd2ee },
+ { "LightSteelBlue3", 0xa2b5cd },
+ { "LightSteelBlue4", 0x6e7b8b },
+ { "LightYellow", 0xffffe0 },
+ { "LightYellow1", 0xffffe0 },
+ { "LightYellow2", 0xeeeed1 },
+ { "LightYellow3", 0xcdcdb4 },
+ { "LightYellow4", 0x8b8b7a },
+ { "LimeGreen", 0x32cd32 },
+ { "MediumAquamarine", 0x66cdaa },
+ { "MediumBlue", 0x0000cd },
+ { "MediumOrchid", 0xba55d3 },
+ { "MediumOrchid1", 0xe066ff },
+ { "MediumOrchid2", 0xd15fee },
+ { "MediumOrchid3", 0xb452cd },
+ { "MediumOrchid4", 0x7a378b },
+ { "MediumPurple", 0x9370db },
+ { "MediumPurple1", 0xab82ff },
+ { "MediumPurple2", 0x9f79ee },
+ { "MediumPurple3", 0x8968cd },
+ { "MediumPurple4", 0x5d478b },
+ { "MediumSeaGreen", 0x3cb371 },
+ { "MediumSlateBlue", 0x7b68ee },
+ { "MediumSpringGreen", 0x00fa9a },
+ { "MediumTurquoise", 0x48d1cc },
+ { "MediumVioletRed", 0xc71585 },
+ { "MidnightBlue", 0x191970 },
+ { "MintCream", 0xf5fffa },
+ { "MistyRose", 0xffe4e1 },
+ { "MistyRose1", 0xffe4e1 },
+ { "MistyRose2", 0xeed5d2 },
+ { "MistyRose3", 0xcdb7b5 },
+ { "MistyRose4", 0x8b7d7b },
+ { "NavajoWhite", 0xffdead },
+ { "NavajoWhite1", 0xffdead },
+ { "NavajoWhite2", 0xeecfa1 },
+ { "NavajoWhite3", 0xcdb38b },
+ { "NavajoWhite4", 0x8b795e },
+ { "NavyBlue", 0x000080 },
+ { "OldLace", 0xfdf5e6 },
+ { "OliveDrab", 0x6b8e23 },
+ { "OliveDrab1", 0xc0ff3e },
+ { "OliveDrab2", 0xb3ee3a },
+ { "OliveDrab3", 0x9acd32 },
+ { "OliveDrab4", 0x698b22 },
+ { "OrangeRed", 0xff4500 },
+ { "OrangeRed1", 0xff4500 },
+ { "OrangeRed2", 0xee4000 },
+ { "OrangeRed3", 0xcd3700 },
+ { "OrangeRed4", 0x8b2500 },
+ { "PaleGoldenrod", 0xeee8aa },
+ { "PaleGreen", 0x98fb98 },
+ { "PaleGreen1", 0x9aff9a },
+ { "PaleGreen2", 0x90ee90 },
+ { "PaleGreen3", 0x7ccd7c },
+ { "PaleGreen4", 0x548b54 },
+ { "PaleTurquoise", 0xafeeee },
+ { "PaleTurquoise1", 0xbbffff },
+ { "PaleTurquoise2", 0xaeeeee },
+ { "PaleTurquoise3", 0x96cdcd },
+ { "PaleTurquoise4", 0x668b8b },
+ { "PaleVioletRed", 0xdb7093 },
+ { "PaleVioletRed1", 0xff82ab },
+ { "PaleVioletRed2", 0xee799f },
+ { "PaleVioletRed3", 0xcd6889 },
+ { "PaleVioletRed4", 0x8b475d },
+ { "PapayaWhip", 0xffefd5 },
+ { "PeachPuff", 0xffdab9 },
+ { "PeachPuff1", 0xffdab9 },
+ { "PeachPuff2", 0xeecbad },
+ { "PeachPuff3", 0xcdaf95 },
+ { "PeachPuff4", 0x8b7765 },
+ { "PowderBlue", 0xb0e0e6 },
+ { "RebeccaPurple", 0x663399 },
+ { "RosyBrown", 0xbc8f8f },
+ { "RosyBrown1", 0xffc1c1 },
+ { "RosyBrown2", 0xeeb4b4 },
+ { "RosyBrown3", 0xcd9b9b },
+ { "RosyBrown4", 0x8b6969 },
+ { "RoyalBlue", 0x4169e1 },
+ { "RoyalBlue1", 0x4876ff },
+ { "RoyalBlue2", 0x436eee },
+ { "RoyalBlue3", 0x3a5fcd },
+ { "RoyalBlue4", 0x27408b },
+ { "SaddleBrown", 0x8b4513 },
+ { "SandyBrown", 0xf4a460 },
+ { "SeaGreen", 0x2e8b57 },
+ { "SeaGreen1", 0x54ff9f },
+ { "SeaGreen2", 0x4eee94 },
+ { "SeaGreen3", 0x43cd80 },
+ { "SeaGreen4", 0x2e8b57 },
+ { "SkyBlue", 0x87ceeb },
+ { "SkyBlue1", 0x87ceff },
+ { "SkyBlue2", 0x7ec0ee },
+ { "SkyBlue3", 0x6ca6cd },
+ { "SkyBlue4", 0x4a708b },
+ { "SlateBlue", 0x6a5acd },
+ { "SlateBlue1", 0x836fff },
+ { "SlateBlue2", 0x7a67ee },
+ { "SlateBlue3", 0x6959cd },
+ { "SlateBlue4", 0x473c8b },
+ { "SlateGray", 0x708090 },
+ { "SlateGray1", 0xc6e2ff },
+ { "SlateGray2", 0xb9d3ee },
+ { "SlateGray3", 0x9fb6cd },
+ { "SlateGray4", 0x6c7b8b },
+ { "SlateGrey", 0x708090 },
+ { "SpringGreen", 0x00ff7f },
+ { "SpringGreen1", 0x00ff7f },
+ { "SpringGreen2", 0x00ee76 },
+ { "SpringGreen3", 0x00cd66 },
+ { "SpringGreen4", 0x008b45 },
+ { "SteelBlue", 0x4682b4 },
+ { "SteelBlue1", 0x63b8ff },
+ { "SteelBlue2", 0x5cacee },
+ { "SteelBlue3", 0x4f94cd },
+ { "SteelBlue4", 0x36648b },
+ { "VioletRed", 0xd02090 },
+ { "VioletRed1", 0xff3e96 },
+ { "VioletRed2", 0xee3a8c },
+ { "VioletRed3", 0xcd3278 },
+ { "VioletRed4", 0x8b2252 },
+ { "WebGray", 0x808080 },
+ { "WebGreen", 0x008000 },
+ { "WebGrey", 0x808080 },
+ { "WebMaroon", 0x800000 },
+ { "WebPurple", 0x800080 },
+ { "WhiteSmoke", 0xf5f5f5 },
+ { "X11Gray", 0xbebebe },
+ { "X11Green", 0x00ff00 },
+ { "X11Grey", 0xbebebe },
+ { "X11Maroon", 0xb03060 },
+ { "X11Purple", 0xa020f0 },
+ { "YellowGreen", 0x9acd32 },
+ { "alice blue", 0xf0f8ff },
+ { "antique white", 0xfaebd7 },
+ { "aqua", 0x00ffff },
+ { "aquamarine", 0x7fffd4 },
+ { "aquamarine1", 0x7fffd4 },
+ { "aquamarine2", 0x76eec6 },
+ { "aquamarine3", 0x66cdaa },
+ { "aquamarine4", 0x458b74 },
+ { "azure", 0xf0ffff },
+ { "azure1", 0xf0ffff },
+ { "azure2", 0xe0eeee },
+ { "azure3", 0xc1cdcd },
+ { "azure4", 0x838b8b },
+ { "beige", 0xf5f5dc },
+ { "bisque", 0xffe4c4 },
+ { "bisque1", 0xffe4c4 },
+ { "bisque2", 0xeed5b7 },
+ { "bisque3", 0xcdb79e },
+ { "bisque4", 0x8b7d6b },
+ { "black", 0x000000 },
+ { "blanched almond", 0xffebcd },
+ { "blue violet", 0x8a2be2 },
+ { "blue", 0x0000ff },
+ { "blue1", 0x0000ff },
+ { "blue2", 0x0000ee },
+ { "blue3", 0x0000cd },
+ { "blue4", 0x00008b },
+ { "brown", 0xa52a2a },
+ { "brown1", 0xff4040 },
+ { "brown2", 0xee3b3b },
+ { "brown3", 0xcd3333 },
+ { "brown4", 0x8b2323 },
+ { "burlywood", 0xdeb887 },
+ { "burlywood1", 0xffd39b },
+ { "burlywood2", 0xeec591 },
+ { "burlywood3", 0xcdaa7d },
+ { "burlywood4", 0x8b7355 },
+ { "cadet blue", 0x5f9ea0 },
+ { "chartreuse", 0x7fff00 },
+ { "chartreuse1", 0x7fff00 },
+ { "chartreuse2", 0x76ee00 },
+ { "chartreuse3", 0x66cd00 },
+ { "chartreuse4", 0x458b00 },
+ { "chocolate", 0xd2691e },
+ { "chocolate1", 0xff7f24 },
+ { "chocolate2", 0xee7621 },
+ { "chocolate3", 0xcd661d },
+ { "chocolate4", 0x8b4513 },
+ { "coral", 0xff7f50 },
+ { "coral1", 0xff7256 },
+ { "coral2", 0xee6a50 },
+ { "coral3", 0xcd5b45 },
+ { "coral4", 0x8b3e2f },
+ { "cornflower blue", 0x6495ed },
+ { "cornsilk", 0xfff8dc },
+ { "cornsilk1", 0xfff8dc },
+ { "cornsilk2", 0xeee8cd },
+ { "cornsilk3", 0xcdc8b1 },
+ { "cornsilk4", 0x8b8878 },
+ { "crimson", 0xdc143c },
+ { "cyan", 0x00ffff },
+ { "cyan1", 0x00ffff },
+ { "cyan2", 0x00eeee },
+ { "cyan3", 0x00cdcd },
+ { "cyan4", 0x008b8b },
+ { "dark blue", 0x00008b },
+ { "dark cyan", 0x008b8b },
+ { "dark goldenrod", 0xb8860b },
+ { "dark gray", 0xa9a9a9 },
+ { "dark green", 0x006400 },
+ { "dark grey", 0xa9a9a9 },
+ { "dark khaki", 0xbdb76b },
+ { "dark magenta", 0x8b008b },
+ { "dark olive green", 0x556b2f },
+ { "dark orange", 0xff8c00 },
+ { "dark orchid", 0x9932cc },
+ { "dark red", 0x8b0000 },
+ { "dark salmon", 0xe9967a },
+ { "dark sea green", 0x8fbc8f },
+ { "dark slate blue", 0x483d8b },
+ { "dark slate gray", 0x2f4f4f },
+ { "dark slate grey", 0x2f4f4f },
+ { "dark turquoise", 0x00ced1 },
+ { "dark violet", 0x9400d3 },
+ { "deep pink", 0xff1493 },
+ { "deep sky blue", 0x00bfff },
+ { "dim gray", 0x696969 },
+ { "dim grey", 0x696969 },
+ { "dodger blue", 0x1e90ff },
+ { "firebrick", 0xb22222 },
+ { "firebrick1", 0xff3030 },
+ { "firebrick2", 0xee2c2c },
+ { "firebrick3", 0xcd2626 },
+ { "firebrick4", 0x8b1a1a },
+ { "floral white", 0xfffaf0 },
+ { "forest green", 0x228b22 },
+ { "fuchsia", 0xff00ff },
+ { "gainsboro", 0xdcdcdc },
+ { "ghost white", 0xf8f8ff },
+ { "gold", 0xffd700 },
+ { "gold1", 0xffd700 },
+ { "gold2", 0xeec900 },
+ { "gold3", 0xcdad00 },
+ { "gold4", 0x8b7500 },
+ { "goldenrod", 0xdaa520 },
+ { "goldenrod1", 0xffc125 },
+ { "goldenrod2", 0xeeb422 },
+ { "goldenrod3", 0xcd9b1d },
+ { "goldenrod4", 0x8b6914 },
+ { "green yellow", 0xadff2f },
+ { "green", 0x00ff00 },
+ { "green1", 0x00ff00 },
+ { "green2", 0x00ee00 },
+ { "green3", 0x00cd00 },
+ { "green4", 0x008b00 },
+ { "honeydew", 0xf0fff0 },
+ { "honeydew1", 0xf0fff0 },
+ { "honeydew2", 0xe0eee0 },
+ { "honeydew3", 0xc1cdc1 },
+ { "honeydew4", 0x838b83 },
+ { "hot pink", 0xff69b4 },
+ { "indian red", 0xcd5c5c },
+ { "indigo", 0x4b0082 },
+ { "ivory", 0xfffff0 },
+ { "ivory1", 0xfffff0 },
+ { "ivory2", 0xeeeee0 },
+ { "ivory3", 0xcdcdc1 },
+ { "ivory4", 0x8b8b83 },
+ { "khaki", 0xf0e68c },
+ { "khaki1", 0xfff68f },
+ { "khaki2", 0xeee685 },
+ { "khaki3", 0xcdc673 },
+ { "khaki4", 0x8b864e },
+ { "lavender blush", 0xfff0f5 },
+ { "lavender", 0xe6e6fa },
+ { "lawn green", 0x7cfc00 },
+ { "lemon chiffon", 0xfffacd },
+ { "light blue", 0xadd8e6 },
+ { "light coral", 0xf08080 },
+ { "light cyan", 0xe0ffff },
+ { "light goldenrod yellow", 0xfafad2 },
+ { "light goldenrod", 0xeedd82 },
+ { "light gray", 0xd3d3d3 },
+ { "light green", 0x90ee90 },
+ { "light grey", 0xd3d3d3 },
+ { "light pink", 0xffb6c1 },
+ { "light salmon", 0xffa07a },
+ { "light sea green", 0x20b2aa },
+ { "light sky blue", 0x87cefa },
+ { "light slate blue", 0x8470ff },
+ { "light slate gray", 0x778899 },
+ { "light slate grey", 0x778899 },
+ { "light steel blue", 0xb0c4de },
+ { "light yellow", 0xffffe0 },
+ { "lime green", 0x32cd32 },
+ { "lime", 0x00ff00 },
+ { "linen", 0xfaf0e6 },
+ { "magenta", 0xff00ff },
+ { "magenta1", 0xff00ff },
+ { "magenta2", 0xee00ee },
+ { "magenta3", 0xcd00cd },
+ { "magenta4", 0x8b008b },
+ { "maroon", 0xb03060 },
+ { "maroon1", 0xff34b3 },
+ { "maroon2", 0xee30a7 },
+ { "maroon3", 0xcd2990 },
+ { "maroon4", 0x8b1c62 },
+ { "medium aquamarine", 0x66cdaa },
+ { "medium blue", 0x0000cd },
+ { "medium orchid", 0xba55d3 },
+ { "medium purple", 0x9370db },
+ { "medium sea green", 0x3cb371 },
+ { "medium slate blue", 0x7b68ee },
+ { "medium spring green", 0x00fa9a },
+ { "medium turquoise", 0x48d1cc },
+ { "medium violet red", 0xc71585 },
+ { "midnight blue", 0x191970 },
+ { "mint cream", 0xf5fffa },
+ { "misty rose", 0xffe4e1 },
+ { "moccasin", 0xffe4b5 },
+ { "navajo white", 0xffdead },
+ { "navy blue", 0x000080 },
+ { "navy", 0x000080 },
+ { "old lace", 0xfdf5e6 },
+ { "olive drab", 0x6b8e23 },
+ { "olive", 0x808000 },
+ { "orange red", 0xff4500 },
+ { "orange", 0xffa500 },
+ { "orange1", 0xffa500 },
+ { "orange2", 0xee9a00 },
+ { "orange3", 0xcd8500 },
+ { "orange4", 0x8b5a00 },
+ { "orchid", 0xda70d6 },
+ { "orchid1", 0xff83fa },
+ { "orchid2", 0xee7ae9 },
+ { "orchid3", 0xcd69c9 },
+ { "orchid4", 0x8b4789 },
+ { "pale goldenrod", 0xeee8aa },
+ { "pale green", 0x98fb98 },
+ { "pale turquoise", 0xafeeee },
+ { "pale violet red", 0xdb7093 },
+ { "papaya whip", 0xffefd5 },
+ { "peach puff", 0xffdab9 },
+ { "peru", 0xcd853f },
+ { "pink", 0xffc0cb },
+ { "pink1", 0xffb5c5 },
+ { "pink2", 0xeea9b8 },
+ { "pink3", 0xcd919e },
+ { "pink4", 0x8b636c },
+ { "plum", 0xdda0dd },
+ { "plum1", 0xffbbff },
+ { "plum2", 0xeeaeee },
+ { "plum3", 0xcd96cd },
+ { "plum4", 0x8b668b },
+ { "powder blue", 0xb0e0e6 },
+ { "purple", 0xa020f0 },
+ { "purple1", 0x9b30ff },
+ { "purple2", 0x912cee },
+ { "purple3", 0x7d26cd },
+ { "purple4", 0x551a8b },
+ { "rebecca purple", 0x663399 },
+ { "red", 0xff0000 },
+ { "red1", 0xff0000 },
+ { "red2", 0xee0000 },
+ { "red3", 0xcd0000 },
+ { "red4", 0x8b0000 },
+ { "rosy brown", 0xbc8f8f },
+ { "royal blue", 0x4169e1 },
+ { "saddle brown", 0x8b4513 },
+ { "salmon", 0xfa8072 },
+ { "salmon1", 0xff8c69 },
+ { "salmon2", 0xee8262 },
+ { "salmon3", 0xcd7054 },
+ { "salmon4", 0x8b4c39 },
+ { "sandy brown", 0xf4a460 },
+ { "sea green", 0x2e8b57 },
+ { "seashell", 0xfff5ee },
+ { "seashell1", 0xfff5ee },
+ { "seashell2", 0xeee5de },
+ { "seashell3", 0xcdc5bf },
+ { "seashell4", 0x8b8682 },
+ { "sienna", 0xa0522d },
+ { "sienna1", 0xff8247 },
+ { "sienna2", 0xee7942 },
+ { "sienna3", 0xcd6839 },
+ { "sienna4", 0x8b4726 },
+ { "silver", 0xc0c0c0 },
+ { "sky blue", 0x87ceeb },
+ { "slate blue", 0x6a5acd },
+ { "slate gray", 0x708090 },
+ { "slate grey", 0x708090 },
+ { "snow", 0xfffafa },
+ { "snow1", 0xfffafa },
+ { "snow2", 0xeee9e9 },
+ { "snow3", 0xcdc9c9 },
+ { "snow4", 0x8b8989 },
+ { "spring green", 0x00ff7f },
+ { "steel blue", 0x4682b4 },
+ { "tan", 0xd2b48c },
+ { "tan1", 0xffa54f },
+ { "tan2", 0xee9a49 },
+ { "tan3", 0xcd853f },
+ { "tan4", 0x8b5a2b },
+ { "teal", 0x008080 },
+ { "thistle", 0xd8bfd8 },
+ { "thistle1", 0xffe1ff },
+ { "thistle2", 0xeed2ee },
+ { "thistle3", 0xcdb5cd },
+ { "thistle4", 0x8b7b8b },
+ { "tomato", 0xff6347 },
+ { "tomato1", 0xff6347 },
+ { "tomato2", 0xee5c42 },
+ { "tomato3", 0xcd4f39 },
+ { "tomato4", 0x8b3626 },
+ { "turquoise", 0x40e0d0 },
+ { "turquoise1", 0x00f5ff },
+ { "turquoise2", 0x00e5ee },
+ { "turquoise3", 0x00c5cd },
+ { "turquoise4", 0x00868b },
+ { "violet red", 0xd02090 },
+ { "violet", 0xee82ee },
+ { "web gray", 0x808080 },
+ { "web green", 0x008000 },
+ { "web grey", 0x808080 },
+ { "web maroon", 0x800000 },
+ { "web purple", 0x800080 },
+ { "wheat", 0xf5deb3 },
+ { "wheat1", 0xffe7ba },
+ { "wheat2", 0xeed8ae },
+ { "wheat3", 0xcdba96 },
+ { "wheat4", 0x8b7e66 },
+ { "white smoke", 0xf5f5f5 },
+ { "white", 0xffffff },
+ { "x11 gray", 0xbebebe },
+ { "x11 green", 0x00ff00 },
+ { "x11 grey", 0xbebebe },
+ { "x11 maroon", 0xb03060 },
+ { "x11 purple", 0xa020f0 },
+ { "yellow green", 0x9acd32 },
+ { "yellow", 0xffff00 },
+ { "yellow1", 0xffff00 },
+ { "yellow2", 0xeeee00 },
+ { "yellow3", 0xcdcd00 },
+ { "yellow4", 0x8b8b00 }
+ };
+ u_int i;
+ int c;
+
+ if (strncmp(name, "grey", 4) == 0 || strncmp(name, "gray", 4) == 0) {
+ if (!isdigit((u_char)name[4]))
+ return (0xbebebe|COLOUR_FLAG_RGB);
+ c = round(2.55 * atoi(name + 4));
+ if (c < 0 || c > 255)
+ return (-1);
+ return (colour_join_rgb(c, c, c));
+ }
+ for (i = 0; i < nitems(colours); i++) {
+ if (strcasecmp(colours[i].name, name) == 0)
+ return (colours[i].c|COLOUR_FLAG_RGB);
+ }
+ return (-1);
+}
diff --git a/compat.h b/compat.h
index b213336f..13334ad7 100644
--- a/compat.h
+++ b/compat.h
@@ -27,6 +27,19 @@
#include <termios.h>
#include <wchar.h>
+#ifdef HAVE_EVENT2_EVENT_H
+#include <event2/event.h>
+#include <event2/event_compat.h>
+#include <event2/event_struct.h>
+#include <event2/buffer.h>
+#include <event2/buffer_compat.h>
+#include <event2/bufferevent.h>
+#include <event2/bufferevent_struct.h>
+#include <event2/bufferevent_compat.h>
+#else
+#include <event.h>
+#endif
+
#ifdef HAVE_MALLOC_TRIM
#include <malloc.h>
#endif
@@ -52,6 +65,9 @@
#ifndef __packed
#define __packed __attribute__ ((__packed__))
#endif
+#ifndef __weak
+#define __weak __attribute__ ((__weak__))
+#endif
#ifndef ECHOPRT
#define ECHOPRT 0
@@ -110,6 +126,10 @@ void warnx(const char *, ...);
#define pledge(s, p) (0)
#endif
+#ifndef IMAXBEL
+#define IMAXBEL 0
+#endif
+
#ifdef HAVE_STDINT_H
#include <stdint.h>
#else
@@ -245,6 +265,13 @@ void warnx(const char *, ...);
#define HOST_NAME_MAX 255
#endif
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+#endif
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC CLOCK_REALTIME
+#endif
+
#ifndef HAVE_FLOCK
#define LOCK_SH 0
#define LOCK_EX 0
@@ -322,6 +349,11 @@ const char *getprogname(void);
void setproctitle(const char *, ...);
#endif
+#ifndef HAVE_CLOCK_GETTIME
+/* clock_gettime.c */
+int clock_gettime(int, struct timespec *);
+#endif
+
#ifndef HAVE_B64_NTOP
/* base64.c */
#undef b64_ntop
@@ -391,6 +423,11 @@ int utf8proc_mbtowc(wchar_t *, const char *, size_t);
int utf8proc_wctomb(char *, wchar_t);
#endif
+#ifdef NEED_FUZZING
+/* tmux.c */
+#define main __weak main
+#endif
+
/* getopt.c */
extern int BSDopterr;
extern int BSDoptind;
diff --git a/compat/clock_gettime.c b/compat/clock_gettime.c
new file mode 100644
index 00000000..8290e75c
--- /dev/null
+++ b/compat/clock_gettime.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2021 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 <sys/time.h>
+
+#include "compat.h"
+
+#ifndef TIMEVAL_TO_TIMESPEC
+#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
+ (ts)->tv_sec = (tv)->tv_sec; \
+ (ts)->tv_nsec = (tv)->tv_usec * 1000; \
+} while (0)
+#endif
+
+int
+clock_gettime(int clock, struct timespec *ts)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ TIMEVAL_TO_TIMESPEC(&tv, ts);
+ return 0;
+}
diff --git a/compat/closefrom.c b/compat/closefrom.c
index 28be3680..0175f364 100644
--- a/compat/closefrom.c
+++ b/compat/closefrom.c
@@ -50,6 +50,8 @@
# include <libproc.h>
#endif
+#include "compat.h"
+
#ifndef OPEN_MAX
# define OPEN_MAX 256
#endif
diff --git a/compat/forkpty-haiku.c b/compat/forkpty-haiku.c
new file mode 100644
index 00000000..6112164c
--- /dev/null
+++ b/compat/forkpty-haiku.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2008 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 <sys/ioctl.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "compat.h"
+
+void fatal(const char *, ...);
+void fatalx(const char *, ...);
+
+pid_t
+forkpty(int *master, char *name, struct termios *tio, struct winsize *ws)
+{
+ int slave = -1;
+ char *path;
+ pid_t pid;
+
+ if ((*master = open("/dev/ptmx", O_RDWR|O_NOCTTY)) == -1)
+ return (-1);
+ if (grantpt(*master) != 0)
+ goto out;
+ if (unlockpt(*master) != 0)
+ goto out;
+
+ if ((path = ptsname(*master)) == NULL)
+ goto out;
+ if (name != NULL)
+ strlcpy(name, path, TTY_NAME_MAX);
+ if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1)
+ goto out;
+
+ switch (pid = fork()) {
+ case -1:
+ goto out;
+ case 0:
+ close(*master);
+
+ setsid();
+ if (ioctl(slave, TIOCSCTTY, NULL) == -1)
+ fatal("ioctl failed");
+
+ if (tio != NULL && tcsetattr(slave, TCSAFLUSH, tio) == -1)
+ fatal("tcsetattr failed");
+ if (ioctl(slave, TIOCSWINSZ, ws) == -1)
+ fatal("ioctl failed");
+
+ dup2(slave, 0);
+ dup2(slave, 1);
+ dup2(slave, 2);
+ if (slave > 2)
+ close(slave);
+ return (0);
+ }
+
+ close(slave);
+ return (pid);
+
+out:
+ if (*master != -1)
+ close(*master);
+ if (slave != -1)
+ close(slave);
+ return (-1);
+}
diff --git a/configure.ac b/configure.ac
index 21123315..08671c71 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
# configure.ac
-AC_INIT([tmux], 3.2-rc3)
+AC_INIT([tmux], 3.2-rc4)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@@ -21,6 +21,26 @@ SAVED_CFLAGS="$CFLAGS"
SAVED_CPPFLAGS="$CPPFLAGS"
SAVED_LDFLAGS="$LDFLAGS"
+# Is this oss-fuzz build?
+AC_ARG_ENABLE(
+ fuzzing,
+ AC_HELP_STRING(--enable-fuzzing, build fuzzers)
+)
+AC_ARG_VAR(
+ FUZZING_LIBS,
+ AC_HELP_STRING(libraries to link fuzzing targets with)
+)
+
+# Set up convenient fuzzing defaults before initializing compiler.
+if test "x$enable_fuzzing" = xyes; then
+ AC_DEFINE(NEED_FUZZING)
+ test "x$CC" == x && CC=clang
+ test "x$FUZZING_LIBS" == x && \
+ FUZZING_LIBS="-fsanitize=fuzzer"
+ test "x$SAVED_CFLAGS" == x && \
+ AM_CFLAGS="-g -fsanitize=fuzzer-no-link,address"
+fi
+
# Set up the compiler in two different ways and say yes we may want to install.
AC_PROG_CC
AM_PROG_CC_C_O
@@ -54,8 +74,11 @@ if test "x$enable_static" = xyes; then
LDFLAGS="$AM_LDFLAGS $SAVED_LDFLAGS"
fi
+# Do we need fuzzers?
+AM_CONDITIONAL(NEED_FUZZING, test "x$enable_fuzzing" = xyes)
+
# Is this gcc?
-AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes)
+AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes -a "x$enable_fuzzing" != xyes)
# Is this Sun CC?
AC_EGREP_CPP(
@@ -110,6 +133,7 @@ AC_CHECK_FUNCS([ \
AC_REPLACE_FUNCS([ \
asprintf \
cfmakeraw \
+ clock_gettime \
closefrom \
explicit_bzero \
fgetln \
@@ -119,8 +143,6 @@ AC_REPLACE_FUNCS([ \
getline \
getprogname \
memmem \
- recallocarray \
- reallocarray \
setenv \
setproctitle \
strcasestr \
@@ -132,6 +154,26 @@ AC_REPLACE_FUNCS([ \
])
AC_FUNC_STRNLEN
+# Clang sanitizers wrap reallocarray even if it isn't available on the target
+# system. When compiled it always returns NULL and crashes the program. To
+# detect this we need a more complicated test.
+AC_MSG_CHECKING([for working reallocarray])
+AC_RUN_IFELSE([AC_LANG_PROGRAM(
+ [#include <stdlib.h>],
+ [return (reallocarray(NULL, 1, 1) == NULL);]
+ )],
+ AC_MSG_RESULT(yes),
+ [AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])]
+)
+AC_MSG_CHECKING([for working recallocarray])
+AC_RUN_IFELSE([AC_LANG_PROGRAM(
+ [#include <stdlib.h>],
+ [return (recallocarray(NULL, 1, 1, 1) == NULL);]
+ )],
+ AC_MSG_RESULT(yes),
+ [AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])]
+)
+
# Look for clock_gettime. Must come before event_init.
AC_SEARCH_LIBS(clock_gettime, rt)
@@ -141,88 +183,112 @@ AC_SEARCH_LIBS(clock_gettime, rt)
# implementations.
AC_LIBOBJ(getopt)
-# Look for libevent.
+# Look for libevent. Try libevent_core or libevent with pkg-config first then
+# look for the library.
PKG_CHECK_MODULES(
- LIBEVENT,
- libevent,
+ LIBEVENT_CORE,
+ [libevent_core >= 2],
[
- AM_CFLAGS="$LIBEVENT_CFLAGS $AM_CFLAGS"
- CFLAGS="$AM_CFLAGS $SAVED_CFLAGS"
- LIBS="$LIBEVENT_LIBS $LIBS"
+ AM_CPPFLAGS="$LIBEVENT_CORE_CFLAGS $AM_CPPFLAGS"
+ CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
+ LIBS="$LIBEVENT_CORE_LIBS $LIBS"
found_libevent=yes
],
+ found_libevent=no
+)
+if test x$found_libevent = xno; then
+ PKG_CHECK_MODULES(
+ LIBEVENT,
+ [libevent >= 2],
+ [
+ AM_CPPFLAGS="$LIBEVENT_CFLAGS $AM_CPPFLAGS"
+ CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
+ LIBS="$LIBEVENT_LIBS $LIBS"
+ found_libevent=yes
+ ],
+ found_libevent=no
+ )
+fi
+if test x$found_libevent = xno; then
+ AC_SEARCH_LIBS(
+ event_init,
+ [event_core event event-1.4],
+ found_libevent=yes,
+ found_libevent=no
+ )
+fi
+AC_CHECK_HEADER(
+ event2/event.h,
+ AC_DEFINE(HAVE_EVENT2_EVENT_H),
[
- AC_SEARCH_LIBS(
- event_init,
- [event event-1.4 event2],
- found_libevent=yes,
+ AC_CHECK_HEADER(
+ event.h,
+ AC_DEFINE(HAVE_EVENT_H),
found_libevent=no
)
]
)
-AC_CHECK_HEADER(
- event.h,
- ,
- found_libevent=no
-)
if test "x$found_libevent" = xno; then
AC_MSG_ERROR("libevent not found")
fi
-# Look for ncurses.
+# Look for ncurses or curses. Try pkg-config first then directly for the
+# library.
PKG_CHECK_MODULES(
LIBTINFO,
tinfo,
- found_ncurses=yes,
+ [
+ AM_CPPFLAGS="$LIBTINFO_CFLAGS $AM_CPPFLAGS"
+ CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS"
+ LIBS="$LIBTINFO_LIBS $LIBS"
+ found_ncurses=yes
+ ],
found_ncurses=no
)
if test "x$found_ncurses" = xno; then
PKG_CHECK_MODULES(
LIBNCURSES,
ncurses,
- found_ncurses=yes,
+ [
+ AM_CPPFLAGS="$LIBNCURSES_CFLAGS $AM_CPPFLAGS"
+ CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS"
+ LIBS="$LIBNCURSES_LIBS $LIBS"
+ found_ncurses=yes
+ ],
found_ncurses=no
)
fi
if test "x$found_ncurses" = xno; then
PKG_CHECK_MODULES(
- LIBNCURSES,
+ LIBNCURSESW,
ncursesw,
- found_ncurses=yes,
+ [
+ AM_CPPFLAGS="$LIBNCURSESW_CFLAGS $AM_CPPFLAGS"
+ CPPFLAGS="$LIBNCURSESW_CFLAGS $SAVED_CPPFLAGS"
+ LIBS="$LIBNCURSESW_LIBS $LIBS"
+ found_ncurses=yes
+ ],
found_ncurses=no
)
fi
-if test "x$found_ncurses" = xyes; then
- AM_CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CFLAGS"
- CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $CFLAGS"
- LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS"
-else
- # pkg-config didn't work, try ncurses.
- AC_CHECK_LIB(
- tinfo,
+if test "x$found_ncurses" = xno; then
+ AC_SEARCH_LIBS(
setupterm,
+ [tinfo ncurses ncursesw],
found_ncurses=yes,
found_ncurses=no
)
- if test "x$found_ncurses" = xno; then
- AC_CHECK_LIB(
- ncurses,
- setupterm,
- found_ncurses=yes,
- found_ncurses=no
- )
- fi
if test "x$found_ncurses" = xyes; then
AC_CHECK_HEADER(
ncurses.h,
LIBS="$LIBS -lncurses",
- found_ncurses=no)
+ found_ncurses=no
+ )
fi
fi
if test "x$found_ncurses" = xyes; then
AC_DEFINE(HAVE_NCURSES_H)
else
- # No ncurses, try curses.
AC_CHECK_LIB(
curses,
setupterm,
@@ -232,7 +298,8 @@ else
AC_CHECK_HEADER(
curses.h,
,
- found_curses=no)
+ found_curses=no
+ )
if test "x$found_curses" = xyes; then
LIBS="$LIBS -lcurses"
AC_DEFINE(HAVE_CURSES_H)
@@ -298,12 +365,11 @@ AC_TRY_LINK(
found_b64_ntop=yes,
found_b64_ntop=no
)
+AC_MSG_RESULT($found_b64_ntop)
+OLD_LIBS="$LIBS"
if test "x$found_b64_ntop" = xno; then
- AC_MSG_RESULT(no)
-
AC_MSG_CHECKING(for b64_ntop with -lresolv)
- OLD_LIBS="$LIBS"
- LIBS="$LIBS -lresolv"
+ LIBS="$OLD_LIBS -lresolv"
AC_TRY_LINK(
[
#include <sys/types.h>
@@ -314,15 +380,27 @@ if test "x$found_b64_ntop" = xno; then
found_b64_ntop=yes,
found_b64_ntop=no
)
- if test "x$found_b64_ntop" = xno; then
- LIBS="$OLD_LIBS"
- AC_MSG_RESULT(no)
- fi
+ AC_MSG_RESULT($found_b64_ntop)
+fi
+if test "x$found_b64_ntop" = xno; then
+ AC_MSG_CHECKING(for b64_ntop with -lnetwork)
+ LIBS="$OLD_LIBS -lnetwork"
+ AC_TRY_LINK(
+ [
+ #include <sys/types.h>
+ #include <netinet/in.h>
+ #include <resolv.h>
+ ],
+ [b64_ntop(NULL, 0, NULL, 0);],
+ found_b64_ntop=yes,
+ found_b64_ntop=no
+ )
+ AC_MSG_RESULT($found_b64_ntop)
fi
if test "x$found_b64_ntop" = xyes; then
AC_DEFINE(HAVE_B64_NTOP)
- AC_MSG_RESULT(yes)
else
+ LIBS="$OLD_LIBS"
AC_LIBOBJ(base64)
fi
@@ -658,6 +736,10 @@ case "$host_os" in
AC_MSG_RESULT(cygwin)
PLATFORM=cygwin
;;
+ *haiku*)
+ AC_MSG_RESULT(haiku)
+ PLATFORM=haiku
+ ;;
*)
AC_MSG_RESULT(unknown)
PLATFORM=unknown
@@ -673,6 +755,7 @@ AM_CONDITIONAL(IS_NETBSD, test "x$PLATFORM" = xnetbsd)
AM_CONDITIONAL(IS_OPENBSD, test "x$PLATFORM" = xopenbsd)
AM_CONDITIONAL(IS_SUNOS, test "x$PLATFORM" = xsunos)
AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux)
+AM_CONDITIONAL(IS_HAIKU, test "x$PLATFORM" = xhaiku)
AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown)
# Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user
diff --git a/control-notify.c b/control-notify.c
index a1735d57..cc706ac2 100644
--- a/control-notify.c
+++ b/control-notify.c
@@ -49,7 +49,7 @@ control_notify_window_layout_changed(struct window *w)
char *cp;
template = "%layout-change #{window_id} #{window_layout} "
- "#{window_visible_layout} #{window_flags}";
+ "#{window_visible_layout} #{window_raw_flags}";
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
diff --git a/control.c b/control.c
index e86429cf..7a5b9eb2 100644
--- a/control.c
+++ b/control.c
@@ -19,7 +19,6 @@
#include <sys/types.h>
-#include <event.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
diff --git a/file.c b/file.c
index f7f99111..baa2bd58 100644
--- a/file.c
+++ b/file.c
@@ -27,10 +27,17 @@
#include "tmux.h"
+/*
+ * IPC file handling. Both client and server use the same data structures
+ * (client_file and client_files) to store list of active files. Most functions
+ * are for use either in client or server but not both.
+ */
+
static int file_next_stream = 3;
RB_GENERATE(client_files, client_file, entry, file_cmp);
+/* Get path for file, either as given or from working directory. */
static char *
file_get_path(struct client *c, const char *file)
{
@@ -43,6 +50,7 @@ file_get_path(struct client *c, const char *file)
return (path);
}
+/* Tree comparison function. */
int
file_cmp(struct client_file *cf1, struct client_file *cf2)
{
@@ -53,11 +61,47 @@ file_cmp(struct client_file *cf1, struct client_file *cf2)
return (0);
}
+/*
+ * Create a file object in the client process - the peer is the server to send
+ * messages to. Check callback is fired when the file is finished with so the
+ * process can decide if it needs to exit (if it is waiting for files to
+ * flush).
+ */
+struct client_file *
+file_create_with_peer(struct tmuxpeer *peer, struct client_files *files,
+ int stream, client_file_cb cb, void *cbdata)
+{
+ struct client_file *cf;
+
+ cf = xcalloc(1, sizeof *cf);
+ cf->c = NULL;
+ cf->references = 1;
+ cf->stream = stream;
+
+ cf->buffer = evbuffer_new();
+ if (cf->buffer == NULL)
+ fatalx("out of memory");
+
+ cf->cb = cb;
+ cf->data = cbdata;
+
+ cf->peer = peer;
+ cf->tree = files;
+ RB_INSERT(client_files, files, cf);
+
+ return (cf);
+}
+
+/* Create a file object in the server, communicating with the given client. */
struct client_file *
-file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
+file_create_with_client(struct client *c, int stream, client_file_cb cb,
+ void *cbdata)
{
struct client_file *cf;
+ if (c != NULL && (c->flags & CLIENT_ATTACHED))
+ c = NULL;
+
cf = xcalloc(1, sizeof *cf);
cf->c = c;
cf->references = 1;
@@ -71,6 +115,8 @@ file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
cf->data = cbdata;
if (cf->c != NULL) {
+ cf->peer = cf->c->peer;
+ cf->tree = &cf->c->files;
RB_INSERT(client_files, &cf->c->files, cf);
cf->c->references++;
}
@@ -78,6 +124,7 @@ file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
return (cf);
}
+/* Free a file. */
void
file_free(struct client_file *cf)
{
@@ -87,13 +134,15 @@ file_free(struct client_file *cf)
evbuffer_free(cf->buffer);
free(cf->path);
- if (cf->c != NULL) {
- RB_REMOVE(client_files, &cf->c->files, cf);
+ if (cf->tree != NULL)
+ RB_REMOVE(client_files, cf->tree, cf);
+ if (cf->c != NULL)
server_client_unref(cf->c);
- }
+
free(cf);
}
+/* Event to fire the done callback. */
static void
file_fire_done_cb(__unused int fd, __unused short events, void *arg)
{
@@ -105,21 +154,22 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg)
file_free(cf);
}
+/* Add an event to fire the done callback (used by the server). */
void
file_fire_done(struct client_file *cf)
{
event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
}
+/* Fire the read callback. */
void
file_fire_read(struct client_file *cf)
{
- struct client *c = cf->c;
-
if (cf->cb != NULL)
- cf->cb(c, cf->path, cf->error, 0, cf->buffer, cf->data);
+ cf->cb(cf->c, cf->path, cf->error, 0, cf->buffer, cf->data);
}
+/* Can this file be printed to? */
int
file_can_print(struct client *c)
{
@@ -130,6 +180,7 @@ file_can_print(struct client *c)
return (1);
}
+/* Print a message to a file. */
void
file_print(struct client *c, const char *fmt, ...)
{
@@ -140,6 +191,7 @@ file_print(struct client *c, const char *fmt, ...)
va_end(ap);
}
+/* Print a message to a file. */
void
file_vprint(struct client *c, const char *fmt, va_list ap)
{
@@ -151,7 +203,7 @@ file_vprint(struct client *c, const char *fmt, va_list ap)
find.stream = 1;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
- cf = file_create(c, 1, NULL, NULL);
+ cf = file_create_with_client(c, 1, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add_vprintf(cf->buffer, fmt, ap);
@@ -166,6 +218,7 @@ file_vprint(struct client *c, const char *fmt, va_list ap)
}
}
+/* Print a buffer to a file. */
void
file_print_buffer(struct client *c, void *data, size_t size)
{
@@ -177,7 +230,7 @@ file_print_buffer(struct client *c, void *data, size_t size)
find.stream = 1;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
- cf = file_create(c, 1, NULL, NULL);
+ cf = file_create_with_client(c, 1, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add(cf->buffer, data, size);
@@ -192,6 +245,7 @@ file_print_buffer(struct client *c, void *data, size_t size)
}
}
+/* Report an error to a file. */
void
file_error(struct client *c, const char *fmt, ...)
{
@@ -206,7 +260,7 @@ file_error(struct client *c, const char *fmt, ...)
find.stream = 2;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
- cf = file_create(c, 2, NULL, NULL);
+ cf = file_create_with_client(c, 2, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add_vprintf(cf->buffer, fmt, ap);
@@ -223,19 +277,21 @@ file_error(struct client *c, const char *fmt, ...)
va_end(ap);
}
+/* Write data to a file. */
void
file_write(struct client *c, const char *path, int flags, const void *bdata,
size_t bsize, client_file_cb cb, void *cbdata)
{
struct client_file *cf;
- FILE *f;
struct msg_write_open *msg;
size_t msglen;
int fd = -1;
+ u_int stream = file_next_stream++;
+ FILE *f;
const char *mode;
if (strcmp(path, "-") == 0) {
- cf = file_create(c, file_next_stream++, cb, cbdata);
+ cf = file_create_with_client(c, stream, cb, cbdata);
cf->path = xstrdup("-");
fd = STDOUT_FILENO;
@@ -248,7 +304,7 @@ file_write(struct client *c, const char *path, int flags, const void *bdata,
goto skip;
}
- cf = file_create(c, file_next_stream++, cb, cbdata);
+ cf = file_create_with_client(c, stream, cb, cbdata);
cf->path = file_get_path(c, path);
if (c == NULL || c->flags & CLIENT_ATTACHED) {
@@ -283,7 +339,7 @@ skip:
msg->fd = fd;
msg->flags = flags;
memcpy(msg + 1, cf->path, msglen - sizeof *msg);
- if (proc_send(c->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
+ if (proc_send(cf->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
free(msg);
cf->error = EINVAL;
goto done;
@@ -295,18 +351,21 @@ done:
file_fire_done(cf);
}
+/* Read a file. */
void
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
{
struct client_file *cf;
- FILE *f;
struct msg_read_open *msg;
- size_t msglen, size;
+ size_t msglen;
int fd = -1;
+ u_int stream = file_next_stream++;
+ FILE *f;
+ size_t size;
char buffer[BUFSIZ];
if (strcmp(path, "-") == 0) {
- cf = file_create(c, file_next_stream++, cb, cbdata);
+ cf = file_create_with_client(c, stream, cb, cbdata);
cf->path = xstrdup("-");
fd = STDIN_FILENO;
@@ -319,7 +378,7 @@ file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
goto skip;
}
- cf = file_create(c, file_next_stream++, cb, cbdata);
+ cf = file_create_with_client(c, stream, cb, cbdata);
cf->path = file_get_path(c, path);
if (c == NULL || c->flags & CLIENT_ATTACHED) {
@@ -355,7 +414,7 @@ skip:
msg->stream = cf->stream;
msg->fd = fd;
memcpy(msg + 1, cf->path, msglen - sizeof *msg);
- if (proc_send(c->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
+ if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
free(msg);
cf->error = EINVAL;
goto done;
@@ -367,21 +426,21 @@ done:
file_fire_done(cf);
}
+/* Push event, fired if there is more writing to be done. */
static void
file_push_cb(__unused int fd, __unused short events, void *arg)
{
struct client_file *cf = arg;
- struct client *c = cf->c;
- if (~c->flags & CLIENT_DEAD)
+ if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD)
file_push(cf);
file_free(cf);
}
+/* Push uwritten data to the client for a file, if it will accept it. */
void
file_push(struct client_file *cf)
{
- struct client *c = cf->c;
struct msg_write_data *msg;
size_t msglen, sent, left;
struct msg_write_close close;
@@ -397,21 +456,364 @@ file_push(struct client_file *cf)
msg = xrealloc(msg, msglen);
msg->stream = cf->stream;
memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
- if (proc_send(c->peer, MSG_WRITE, -1, msg, msglen) != 0)
+ if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0)
break;
evbuffer_drain(cf->buffer, sent);
left = EVBUFFER_LENGTH(cf->buffer);
- log_debug("%s: file %d sent %zu, left %zu", c->name, cf->stream,
- sent, left);
+ log_debug("file %d sent %zu, left %zu", cf->stream, sent, left);
}
if (left != 0) {
cf->references++;
event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
} else if (cf->stream > 2) {
close.stream = cf->stream;
- proc_send(c->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
+ proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
file_fire_done(cf);
}
free(msg);
}
+
+/* Check if any files have data left to write. */
+int
+file_write_left(struct client_files *files)
+{
+ struct client_file *cf;
+ size_t left;
+ int waiting = 0;
+
+ RB_FOREACH(cf, client_files, files) {
+ if (cf->event == NULL)
+ continue;
+ left = EVBUFFER_LENGTH(cf->event->output);
+ if (left != 0) {
+ waiting++;
+ log_debug("file %u %zu bytes left", cf->stream, left);
+ }
+ }
+ return (waiting != 0);
+}
+
+/* Client file write error callback. */
+static void
+file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
+ void *arg)
+{
+ struct client_file *cf = arg;
+
+ log_debug("write error file %d", cf->stream);
+
+ if (cf->cb != NULL)
+ cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
+
+ bufferevent_free(cf->event);
+ cf->event = NULL;
+
+ close(cf->fd);
+ cf->fd = -1;
+}
+
+/* Client file write callback. */
+static void
+file_write_callback(__unused struct bufferevent *bev, void *arg)
+{
+ struct client_file *cf = arg;
+
+ log_debug("write check file %d", cf->stream);
+
+ if (cf->cb != NULL)
+ cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
+
+ if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
+ bufferevent_free(cf->event);
+ close(cf->fd);
+ RB_REMOVE(client_files, cf->tree, cf);
+ file_free(cf);
+ }
+}
+
+/* Handle a file write open message (client). */
+void
+file_write_open(struct client_files *files, struct tmuxpeer *peer,
+ struct imsg *imsg, int allow_streams, int close_received,
+ client_file_cb cb, void *cbdata)
+{
+ struct msg_write_open *msg = imsg->data;
+ size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ const char *path;
+ struct msg_write_ready reply;
+ struct client_file find, *cf;
+ const int flags = O_NONBLOCK|O_WRONLY|O_CREAT;
+ int error = 0;
+
+ if (msglen < sizeof *msg)
+ fatalx("bad MSG_WRITE_OPEN size");
+ if (msglen == sizeof *msg)
+ path = "-";
+ else
+ path = (const char *)(msg + 1);
+ log_debug("open write file %d %s", msg->stream, path);
+
+ find.stream = msg->stream;
+ if ((cf = RB_FIND(client_files, files, &find)) != NULL) {
+ error = EBADF;
+ goto reply;
+ }
+ cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
+ if (cf->closed) {
+ error = EBADF;
+ goto reply;
+ }
+
+ cf->fd = -1;
+ if (msg->fd == -1)
+ cf->fd = open(path, msg->flags|flags, 0644);
+ else if (allow_streams) {
+ if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
+ errno = EBADF;
+ else {
+ cf->fd = dup(msg->fd);
+ if (close_received)
+ close(msg->fd); /* can only be used once */
+ }
+ } else
+ errno = EBADF;
+ if (cf->fd == -1) {
+ error = errno;
+ goto reply;
+ }
+
+ cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
+ file_write_error_callback, cf);
+ bufferevent_enable(cf->event, EV_WRITE);
+ goto reply;
+
+reply:
+ reply.stream = msg->stream;
+ reply.error = error;
+ proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
+}
+
+/* Handle a file write data message (client). */
+void
+file_write_data(struct client_files *files, struct imsg *imsg)
+{
+ struct msg_write_data *msg = imsg->data;
+ size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ struct client_file find, *cf;
+ size_t size = msglen - sizeof *msg;
+
+ if (msglen < sizeof *msg)
+ fatalx("bad MSG_WRITE size");
+ find.stream = msg->stream;
+ if ((cf = RB_FIND(client_files, files, &find)) == NULL)
+ fatalx("unknown stream number");
+ log_debug("write %zu to file %d", size, cf->stream);
+
+ if (cf->event != NULL)
+ bufferevent_write(cf->event, msg + 1, size);
+}
+
+/* Handle a file write close message (client). */
+void
+file_write_close(struct client_files *files, struct imsg *imsg)
+{
+ struct msg_write_close *msg = imsg->data;
+ size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ struct client_file find, *cf;
+
+ if (msglen != sizeof *msg)
+ fatalx("bad MSG_WRITE_CLOSE size");
+ find.stream = msg->stream;
+ if ((cf = RB_FIND(client_files, files, &find)) == NULL)
+ fatalx("unknown stream number");
+ log_debug("close file %d", cf->stream);
+
+ if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
+ if (cf->event != NULL)
+ bufferevent_free(cf->event);
+ if (cf->fd != -1)
+ close(cf->fd);
+ RB_REMOVE(client_files, files, cf);
+ file_free(cf);
+ }
+}
+
+/* Client file read error callback. */
+static void
+file_read_error_callback(__unused struct bufferevent *bev, __unused short what,
+ void *arg)
+{
+ struct client_file *cf = arg;
+ struct msg_read_done msg;
+
+ log_debug("read error file %d", cf->stream);
+
+ msg.stream = cf->stream;
+ msg.error = 0;
+ proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg);
+
+ bufferevent_free(cf->event);
+ close(cf->fd);
+ RB_REMOVE(client_files, cf->tree, cf);
+ file_free(cf);
+}
+
+/* Client file read callback. */
+static void
+file_read_callback(__unused struct bufferevent *bev, void *arg)
+{
+ struct client_file *cf = arg;
+ void *bdata;
+ size_t bsize;
+ struct msg_read_data *msg;
+ size_t msglen;
+
+ msg = xmalloc(sizeof *msg);
+ for (;;) {
+ bdata = EVBUFFER_DATA(cf->event->input);
+ bsize = EVBUFFER_LENGTH(cf->event->input);
+
+ if (bsize == 0)
+ break;
+ if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
+ bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
+ log_debug("read %zu from file %d", bsize, cf->stream);
+
+ msglen = (sizeof *msg) + bsize;
+ msg = xrealloc(msg, msglen);
+ msg->stream = cf->stream;
+ memcpy(msg + 1, bdata, bsize);
+ proc_send(cf->peer, MSG_READ, -1, msg, msglen);
+
+ evbuffer_drain(cf->event->input, bsize);
+ }
+ free(msg);
+}
+
+/* Handle a file read open message (client). */
+void
+file_read_open(struct client_files *files, struct tmuxpeer *peer,
+ struct imsg *imsg, int allow_streams, int close_received, client_file_cb cb,
+ void *cbdata)
+{
+ struct msg_read_open *msg = imsg->data;
+ size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ const char *path;
+ struct msg_read_done reply;
+ struct client_file find, *cf;
+ const int flags = O_NONBLOCK|O_RDONLY;
+ int error;
+
+ if (msglen < sizeof *msg)
+ fatalx("bad MSG_READ_OPEN size");
+ if (msglen == sizeof *msg)
+ path = "-";
+ else
+ path = (const char *)(msg + 1);
+ log_debug("open read file %d %s", msg->stream, path);
+
+ find.stream = msg->stream;
+ if ((cf = RB_FIND(client_files, files, &find)) != NULL) {
+ error = EBADF;
+ goto reply;
+ }
+ cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
+ if (cf->closed) {
+ error = EBADF;
+ goto reply;
+ }
+
+ cf->fd = -1;
+ if (msg->fd == -1)
+ cf->fd = open(path, flags);
+ else if (allow_streams) {
+ if (msg->fd != STDIN_FILENO)
+ errno = EBADF;
+ else {
+ cf->fd = dup(msg->fd);
+ if (close_received)
+ close(msg->fd); /* can only be used once */
+ }
+ } else
+ errno = EBADF;
+ if (cf->fd == -1) {
+ error = errno;
+ goto reply;
+ }
+
+ cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
+ file_read_error_callback, cf);
+ bufferevent_enable(cf->event, EV_READ);
+ return;
+
+reply:
+ reply.stream = msg->stream;
+ reply.error = error;
+ proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
+}
+
+/* Handle a write ready message (server). */
+void
+file_write_ready(struct client_files *files, struct imsg *imsg)
+{
+ struct msg_write_ready *msg = imsg->data;
+ size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ struct client_file find, *cf;
+
+ if (msglen != sizeof *msg)
+ fatalx("bad MSG_WRITE_READY size");
+ find.stream = msg->stream;
+ if ((cf = RB_FIND(client_files, files, &find)) == NULL)
+ return;
+ if (msg->error != 0) {
+ cf->error = msg->error;
+ file_fire_done(cf);
+ } else
+ file_push(cf);
+}
+
+/* Handle read data message (server). */
+void
+file_read_data(struct client_files *files, struct imsg *imsg)
+{
+ struct msg_read_data *msg = imsg->data;
+ size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ struct client_file find, *cf;
+ void *bdata = msg + 1;
+ size_t bsize = msglen - sizeof *msg;
+
+ if (msglen < sizeof *msg)
+ fatalx("bad MSG_READ_DATA size");
+ find.stream = msg->stream;
+ if ((cf = RB_FIND(client_files, files, &find)) == NULL)
+ return;
+
+ log_debug("file %d read %zu bytes", cf->stream, bsize);
+ if (cf->error == 0) {
+ if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
+ cf->error = ENOMEM;
+ file_fire_done(cf);
+ } else
+ file_fire_read(cf);
+ }
+}
+
+/* Handle a read done message (server). */
+void
+file_read_done(struct client_files *files, struct imsg *imsg)
+{
+ struct msg_read_done *msg = imsg->data;
+ size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ struct client_file find, *cf;
+
+ if (msglen != sizeof *msg)
+ fatalx("bad MSG_READ_DONE size");
+ find.stream = msg->stream;
+ if ((cf = RB_FIND(client_files, files, &find)) == NULL)
+ return;
+
+ log_debug("file %d read done", cf->stream);
+ cf->error = msg->error;
+ file_fire_done(cf);
+}
diff --git a/format-draw.c b/format-draw.c
index ec98ba95..67b961d9 100644
--- a/format-draw.c
+++ b/format-draw.c
@@ -486,6 +486,18 @@ format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
focus_end, frs);
}
+/* Draw multiple characters. */
+static void
+format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch,
+ u_int n)
+{
+ u_int i;
+
+ utf8_set(&sy->gc.data, ch);
+ for (i = 0; i < n; i++)
+ screen_write_cell(ctx, &sy->gc);
+}
+
/* Draw a format to a screen. */
void
format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
@@ -509,10 +521,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
size_t size = strlen(expanded);
struct screen *os = octx->s, s[TOTAL];
struct screen_write_ctx ctx[TOTAL];
- u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL];
+ u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
u_int map[] = { LEFT, LEFT, CENTRE, RIGHT };
int focus_start = -1, focus_end = -1;
- int list_state = -1, fill = -1;
+ int list_state = -1, fill = -1, even;
enum style_align list_align = STYLE_ALIGN_DEFAULT;
struct grid_cell gc, current_default;
struct style sy, saved_sy;
@@ -547,6 +559,38 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
*/
cp = expanded;
while (*cp != '\0') {
+ /* Handle sequences of #. */
+ if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') {
+ for (n = 1; cp[n] == '#'; n++)
+ /* nothing */;
+ even = ((n % 2) == 0);
+ if (cp[n] != '[') {
+ cp += n;
+ if (even)
+ n = (n / 2);
+ else
+ n = (n / 2) + 1;
+ width[current] += n;
+ format_draw_many(&ctx[current], &sy, '#', n);
+ continue;
+ }
+ if (even)
+ cp += (n + 1);
+ else
+ cp += (n - 1);
+ if (sy.ignore)
+ continue;
+ format_draw_many(&ctx[current], &sy, '#', n / 2);
+ width[current] += (n / 2);
+ if (even) {
+ utf8_set(ud, '[');
+ screen_write_cell(&ctx[current], &sy.gc);
+ width[current]++;
+ }
+ continue;
+ }
+
+ /* Is this not a style? */
if (cp[0] != '#' || cp[1] != '[' || sy.ignore) {
/* See if this is a UTF-8 character. */
if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
@@ -796,13 +840,33 @@ u_int
format_width(const char *expanded)
{
const char *cp, *end;
- u_int width = 0;
+ u_int n, width = 0;
struct utf8_data ud;
enum utf8_state more;
cp = expanded;
while (*cp != '\0') {
- if (cp[0] == '#' && cp[1] == '[') {
+ if (*cp == '#') {
+ for (n = 1; cp[n] == '#'; n++)
+ /* nothing */;
+ if (cp[n] != '[') {
+ width += n;
+ cp += n;
+ continue;
+ }
+ width += (n / 2); /* one for each ## */
+
+ if ((n % 2) == 0) {
+ /*
+ * An even number of #s means that all #s are
+ * escaped, so not a style.
+ */
+ width++; /* one for the [ */
+ cp += (n + 1);
+ continue;
+ }
+ cp += (n - 1); /* point to the [ */
+
end = format_skip(cp + 2, "]");
if (end == NULL)
return (0);
@@ -823,19 +887,57 @@ format_width(const char *expanded)
return (width);
}
-/* Trim on the left, taking #[] into account. */
+/*
+ * Trim on the left, taking #[] into account. Note, we copy the whole set of
+ * unescaped #s, but only add their escaped size to width. This is because the
+ * format_draw function will actually do the escaping when it runs
+ */
char *
format_trim_left(const char *expanded, u_int limit)
{
char *copy, *out;
const char *cp = expanded, *end;
- u_int width = 0;
+ u_int even, n, width = 0;
struct utf8_data ud;
enum utf8_state more;
- out = copy = xmalloc(strlen(expanded) + 1);
+ out = copy = xcalloc(1, strlen(expanded) + 1);
while (*cp != '\0') {
- if (cp[0] == '#' && cp[1] == '[') {
+ if (width >= limit)
+ break;
+ if (*cp == '#') {
+ for (end = cp + 1; *end == '#'; end++)
+ /* nothing */;
+ n = end - cp;
+ if (*end != '[') {
+ if (n > limit - width)
+ n = limit - width;
+ memcpy(out, cp, n);
+ out += n;
+ width += n;
+ cp = end;
+ continue;
+ }
+ even = ((n % 2) == 0);
+
+ n /= 2;
+ if (n > limit - width)
+ n = limit - width;
+ width += n;
+ n *= 2;
+ memcpy(out, cp, n);
+ out += n;
+
+ if (even) {
+ if (width + 1 <= limit) {
+ *out++ = '[';
+ width++;
+ }
+ cp = end + 1;
+ continue;
+ }
+ cp = end - 1;
+
end = format_skip(cp + 2, "]");
if (end == NULL)
break;
@@ -873,7 +975,7 @@ format_trim_right(const char *expanded, u_int limit)
{
char *copy, *out;
const char *cp = expanded, *end;
- u_int width = 0, total_width, skip;
+ u_int width = 0, total_width, skip, old_n, even, n;
struct utf8_data ud;
enum utf8_state more;
@@ -882,12 +984,64 @@ format_trim_right(const char *expanded, u_int limit)
return (xstrdup(expanded));
skip = total_width - limit;
- out = copy = xmalloc(strlen(expanded) + 1);
+ out = copy = xcalloc(1, strlen(expanded) + 1);
while (*cp != '\0') {
- if (cp[0] == '#' && cp[1] == '[') {
+ if (*cp == '#') {
+ for (end = cp + 1; *end == '#'; end++)
+ /* nothing */;
+ old_n = n = end - cp;
+ if (*end != '[') {
+ if (width <= skip) {
+ if (skip - width >= n)
+ n = 0;
+ else
+ n -= (skip - width);
+ }
+ if (n != 0) {
+ memcpy(out, cp, n);
+ out += n;
+ }
+
+ /*
+ * The width always increases by the full
+ * amount even if we can't copy anything yet.
+ */
+ width += old_n;
+ cp = end;
+ continue;
+ }
+ even = ((n % 2) == 0);
+
+ n /= 2;
+ if (width <= skip) {
+ if (skip - width >= n)
+ n = 0;
+ else
+ n -= (skip - width);
+ }
+ if (n != 0) {
+ /*
+ * Copy the full amount because it hasn't been
+ * escaped yet.
+ */
+ memcpy(out, cp, old_n);
+ out += old_n;
+ }
+ cp += old_n;
+ width += (old_n / 2) - even;
+
+ if (even) {
+ if (width > skip)
+ *out++ = '[';
+ width++;
+ continue;
+ }
+ cp = end - 1;
+
end = format_skip(cp + 2, "]");
- if (end == NULL)
+ if (end == NULL) {
break;
+ }
memcpy(out, cp, end + 1 - cp);
out += (end + 1 - cp);
cp = end + 1;
diff --git a/format.c b/format.c
index dd3af400..d3c7508a 100644
--- a/format.c
+++ b/format.c
@@ -89,7 +89,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
#define FORMAT_TIMESTRING 0x1
#define FORMAT_BASENAME 0x2
#define FORMAT_DIRNAME 0x4
-#define FORMAT_QUOTE 0x8
+#define FORMAT_QUOTE_SHELL 0x8
#define FORMAT_LITERAL 0x10
#define FORMAT_EXPAND 0x20
#define FORMAT_EXPANDTIME 0x40
@@ -98,6 +98,10 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
#define FORMAT_PANES 0x200
#define FORMAT_PRETTY 0x400
#define FORMAT_LENGTH 0x800
+#define FORMAT_WIDTH 0x1000
+#define FORMAT_QUOTE_STYLE 0x2000
+#define FORMAT_WINDOW_NAME 0x4000
+#define FORMAT_SESSION_NAME 0x8000
/* Limit on recursion. */
#define FORMAT_LOOP_LIMIT 10
@@ -115,13 +119,23 @@ struct format_entry {
RB_ENTRY(format_entry) entry;
};
-/* Format entry tree. */
+/* Format type. */
+enum format_type {
+ FORMAT_TYPE_UNKNOWN,
+ FORMAT_TYPE_SESSION,
+ FORMAT_TYPE_WINDOW,
+ FORMAT_TYPE_PANE
+};
+
struct format_tree {
+ enum format_type type;
+
struct client *c;
struct session *s;
struct winlink *wl;
struct window *w;
struct window_pane *wp;
+ struct paste_buffer *pb;
struct cmdq_item *item;
struct client *client;
@@ -140,6 +154,7 @@ struct format_expand_state {
struct format_tree *ft;
u_int loop;
time_t time;
+ struct tm tm;
int flags;
};
@@ -259,6 +274,7 @@ format_copy_state(struct format_expand_state *to,
to->ft = from->ft;
to->loop = from->loop;
to->time = from->time;
+ memcpy(&to->tm, &from->tm, sizeof to->tm);
to->flags = from->flags|flags;
}
@@ -363,7 +379,10 @@ format_job_get(struct format_expand_state *es, const char *cmd)
RB_INSERT(format_job_tree, jobs, fj);
}
- expanded = format_expand1(es, cmd);
+ format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS);
+ next.flags &= ~FORMAT_EXPAND_TIME;
+
+ expanded = format_expand1(&next, cmd);
if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) {
free((void *)fj->expanded);
fj->expanded = xstrdup(expanded);
@@ -375,7 +394,7 @@ format_job_get(struct format_expand_state *es, const char *cmd)
if (force && fj->job != NULL)
job_free(fj->job);
if (force || (fj->job == NULL && fj->last != t)) {
- fj->job = job_run(expanded, NULL,
+ fj->job = job_run(expanded, 0, NULL, NULL,
server_client_get_cwd(ft->client, NULL), format_job_update,
format_job_complete, NULL, fj, JOB_NOWAIT, -1, -1);
if (fj->job == NULL) {
@@ -389,7 +408,6 @@ format_job_get(struct format_expand_state *es, const char *cmd)
if (ft->flags & FORMAT_STATUS)
fj->status = 1;
- format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS);
return (format_expand1(&next, fj->out));
}
@@ -445,8 +463,21 @@ format_job_timer(__unused int fd, __unused short events, __unused void *arg)
evtimer_add(&format_job_event, &tv);
}
+/* Wrapper for asprintf. */
+static char * printflike(1, 2)
+format_printf(const char *fmt, ...)
+{
+ va_list ap;
+ char *s;
+
+ va_start(ap, fmt);
+ xvasprintf(&s, fmt, ap);
+ va_end(ap);
+ return (s);
+}
+
/* Callback for host. */
-static char *
+static void *
format_cb_host(__unused struct format_tree *ft)
{
char host[HOST_NAME_MAX + 1];
@@ -457,7 +488,7 @@ format_cb_host(__unused struct format_tree *ft)
}
/* Callback for host_short. */
-static char *
+static void *
format_cb_host_short(__unused struct format_tree *ft)
{
char host[HOST_NAME_MAX + 1], *cp;
@@ -470,7 +501,7 @@ format_cb_host_short(__unused struct format_tree *ft)
}
/* Callback for pid. */
-static char *
+static void *
format_cb_pid(__unused struct format_tree *ft)
{
char *value;
@@ -480,7 +511,7 @@ format_cb_pid(__unused struct format_tree *ft)
}
/* Callback for session_attached_list. */
-static char *
+static void *
format_cb_session_attached_list(struct format_tree *ft)
{
struct session *s = ft->s;
@@ -511,7 +542,7 @@ format_cb_session_attached_list(struct format_tree *ft)
}
/* Callback for session_alerts. */
-static char *
+static void *
format_cb_session_alerts(struct format_tree *ft)
{
struct session *s = ft->s;
@@ -541,7 +572,7 @@ format_cb_session_alerts(struct format_tree *ft)
}
/* Callback for session_stack. */
-static char *
+static void *
format_cb_session_stack(struct format_tree *ft)
{
struct session *s = ft->s;
@@ -563,14 +594,18 @@ format_cb_session_stack(struct format_tree *ft)
}
/* Callback for window_stack_index. */
-static char *
+static void *
format_cb_window_stack_index(struct format_tree *ft)
{
- struct session *s = ft->wl->session;
+ struct session *s;
struct winlink *wl;
u_int idx;
char *value = NULL;
+ if (ft->wl == NULL)
+ return (NULL);
+ s = ft->wl->session;
+
idx = 0;
TAILQ_FOREACH(wl, &s->lastw, sentry) {
idx++;
@@ -584,15 +619,19 @@ format_cb_window_stack_index(struct format_tree *ft)
}
/* Callback for window_linked_sessions_list. */
-static char *
+static void *
format_cb_window_linked_sessions_list(struct format_tree *ft)
{
- struct window *w = ft->wl->window;
+ struct window *w;
struct winlink *wl;
struct evbuffer *buffer;
int size;
char *value = NULL;
+ if (ft->wl == NULL)
+ return (NULL);
+ w = ft->wl->window;
+
buffer = evbuffer_new();
if (buffer == NULL)
fatalx("out of memory");
@@ -610,14 +649,18 @@ format_cb_window_linked_sessions_list(struct format_tree *ft)
}
/* Callback for window_active_sessions. */
-static char *
+static void *
format_cb_window_active_sessions(struct format_tree *ft)
{
- struct window *w = ft->wl->window;
+ struct window *w;
struct winlink *wl;
u_int n = 0;
char *value;
+ if (ft->wl == NULL)
+ return (NULL);
+ w = ft->wl->window;
+
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
if (wl->session->curw == wl)
n++;
@@ -628,15 +671,19 @@ format_cb_window_active_sessions(struct format_tree *ft)
}
/* Callback for window_active_sessions_list. */
-static char *
+static void *
format_cb_window_active_sessions_list(struct format_tree *ft)
{
- struct window *w = ft->wl->window;
+ struct window *w;
struct winlink *wl;
struct evbuffer *buffer;
int size;
char *value = NULL;
+ if (ft->wl == NULL)
+ return (NULL);
+ w = ft->wl->window;
+
buffer = evbuffer_new();
if (buffer == NULL)
fatalx("out of memory");
@@ -656,15 +703,19 @@ format_cb_window_active_sessions_list(struct format_tree *ft)
}
/* Callback for window_active_clients. */
-static char *
+static void *
format_cb_window_active_clients(struct format_tree *ft)
{
- struct window *w = ft->wl->window;
+ struct window *w;
struct client *loop;
struct session *client_session;
u_int n = 0;
char *value;
+ if (ft->wl == NULL)
+ return (NULL);
+ w = ft->wl->window;
+
TAILQ_FOREACH(loop, &clients, entry) {
client_session = loop->session;
if (client_session == NULL)
@@ -679,16 +730,20 @@ format_cb_window_active_clients(struct format_tree *ft)
}
/* Callback for window_active_clients_list. */
-static char *
+static void *
format_cb_window_active_clients_list(struct format_tree *ft)
{
- struct window *w = ft->wl->window;
+ struct window *w;
struct client *loop;
struct session *client_session;
struct evbuffer *buffer;
int size;
char *value = NULL;
+ if (ft->wl == NULL)
+ return (NULL);
+ w = ft->wl->window;
+
buffer = evbuffer_new();
if (buffer == NULL)
fatalx("out of memory");
@@ -712,7 +767,7 @@ format_cb_window_active_clients_list(struct format_tree *ft)
}
/* Callback for window_layout. */
-static char *
+static void *
format_cb_window_layout(struct format_tree *ft)
{
struct window *w = ft->w;
@@ -726,7 +781,7 @@ format_cb_window_layout(struct format_tree *ft)
}
/* Callback for window_visible_layout. */
-static char *
+static void *
format_cb_window_visible_layout(struct format_tree *ft)
{
struct window *w = ft->w;
@@ -738,7 +793,7 @@ format_cb_window_visible_layout(struct format_tree *ft)
}
/* Callback for pane_start_command. */
-static char *
+static void *
format_cb_start_command(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
@@ -750,7 +805,7 @@ format_cb_start_command(struct format_tree *ft)
}
/* Callback for pane_current_command. */
-static char *
+static void *
format_cb_current_command(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
@@ -774,7 +829,7 @@ format_cb_current_command(struct format_tree *ft)
}
/* Callback for pane_current_path. */
-static char *
+static void *
format_cb_current_path(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
@@ -790,7 +845,7 @@ format_cb_current_path(struct format_tree *ft)
}
/* Callback for history_bytes. */
-static char *
+static void *
format_cb_history_bytes(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
@@ -816,7 +871,7 @@ format_cb_history_bytes(struct format_tree *ft)
}
/* Callback for history_all_bytes. */
-static char *
+static void *
format_cb_history_all_bytes(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
@@ -843,7 +898,7 @@ format_cb_history_all_bytes(struct format_tree *ft)
}
/* Callback for pane_tabs. */
-static char *
+static void *
format_cb_pane_tabs(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
@@ -872,8 +927,30 @@ format_cb_pane_tabs(struct format_tree *ft)
return (value);
}
+/* Callback for pane_fg. */
+static void *
+format_cb_pane_fg(struct format_tree *ft)
+{
+ struct window_pane *wp = ft->wp;
+ struct grid_cell gc;
+
+ tty_default_colours(&gc, wp);
+ return (xstrdup(colour_tostring(gc.fg)));
+}
+
+/* Callback for pane_bg. */
+static void *
+format_cb_pane_bg(struct format_tree *ft)
+{
+ struct window_pane *wp = ft->wp;
+ struct grid_cell gc;
+
+ tty_default_colours(&gc, wp);
+ return (xstrdup(colour_tostring(gc.bg)));
+}
+
/* Callback for session_group_list. */
-static char *
+static void *
format_cb_session_group_list(struct format_tree *ft)
{
struct session *s = ft->s;
@@ -906,7 +983,7 @@ format_cb_session_group_list(struct format_tree *ft)
}
/* Callback for session_group_attached_list. */
-static char *
+static void *
format_cb_session_group_attached_list(struct format_tree *ft)
{
struct session *s = ft->s, *client_session, *session_loop;
@@ -946,7 +1023,7 @@ format_cb_session_group_attached_list(struct format_tree *ft)
}
/* Callback for pane_in_mode. */
-static char *
+static void *
format_cb_pane_in_mode(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
@@ -958,13 +1035,13 @@ format_cb_pane_in_mode(struct format_tree *ft)
return (NULL);
TAILQ_FOREACH(wme, &wp->modes, entry)
- n++;
+ n++;
xasprintf(&value, "%u", n);
return (value);
}
/* Callback for pane_at_top. */
-static char *
+static void *
format_cb_pane_at_top(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
@@ -986,7 +1063,7 @@ format_cb_pane_at_top(struct format_tree *ft)
}
/* Callback for pane_at_bottom. */
-static char *
+static void *
format_cb_pane_at_bottom(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
@@ -1008,7 +1085,7 @@ format_cb_pane_at_bottom(struct format_tree *ft)
}
/* Callback for cursor_character. */
-static char *
+static void *
format_cb_cursor_character(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
@@ -1024,78 +1101,8 @@ format_cb_cursor_character(struct format_tree *ft)
return (value);
}
-/* Return word at given coordinates. Caller frees. */
-char *
-format_grid_word(struct grid *gd, u_int x, u_int y)
-{
- const struct grid_line *gl;
- struct grid_cell gc;
- const char *ws;
- struct utf8_data *ud = NULL;
- u_int end;
- size_t size = 0;
- int found = 0;
- char *s = NULL;
-
- ws = options_get_string(global_s_options, "word-separators");
-
- for (;;) {
- grid_get_cell(gd, x, y, &gc);
- if (gc.flags & GRID_FLAG_PADDING)
- break;
- if (utf8_cstrhas(ws, &gc.data)) {
- found = 1;
- break;
- }
-
- if (x == 0) {
- if (y == 0)
- break;
- gl = grid_peek_line(gd, y - 1);
- if (~gl->flags & GRID_LINE_WRAPPED)
- break;
- y--;
- x = grid_line_length(gd, y);
- if (x == 0)
- break;
- }
- x--;
- }
- for (;;) {
- if (found) {
- end = grid_line_length(gd, y);
- if (end == 0 || x == end - 1) {
- if (y == gd->hsize + gd->sy - 1)
- break;
- gl = grid_peek_line(gd, y);
- if (~gl->flags & GRID_LINE_WRAPPED)
- break;
- y++;
- x = 0;
- } else
- x++;
- }
- found = 1;
-
- grid_get_cell(gd, x, y, &gc);
- if (gc.flags & GRID_FLAG_PADDING)
- break;
- if (utf8_cstrhas(ws, &gc.data))
- break;
-
- ud = xreallocarray(ud, size + 2, sizeof *ud);
- memcpy(&ud[size++], &gc.data, sizeof *ud);
- }
- if (size != 0) {
- ud[size].size = 0;
- s = utf8_tocstr(ud);
- free(ud);
- }
- return (s);
-}
-
/* Callback for mouse_word. */
-static char *
+static void *
format_cb_mouse_word(struct format_tree *ft)
{
struct window_pane *wp;
@@ -1121,34 +1128,8 @@ format_cb_mouse_word(struct format_tree *ft)
return (format_grid_word(gd, x, gd->hsize + y));
}
-/* Return line at given coordinates. Caller frees. */
-char *
-format_grid_line(struct grid *gd, u_int y)
-{
- struct grid_cell gc;
- struct utf8_data *ud = NULL;
- u_int x;
- size_t size = 0;
- char *s = NULL;
-
- for (x = 0; x < grid_line_length(gd, y); x++) {
- grid_get_cell(gd, x, y, &gc);
- if (gc.flags & GRID_FLAG_PADDING)
- break;
-
- ud = xreallocarray(ud, size + 2, sizeof *ud);
- memcpy(&ud[size++], &gc.data, sizeof *ud);
- }
- if (size != 0) {
- ud[size].size = 0;
- s = utf8_tocstr(ud);
- free(ud);
- }
- return (s);
-}
-
/* Callback for mouse_line. */
-static char *
+static void *
format_cb_mouse_line(struct format_tree *ft)
{
struct window_pane *wp;
@@ -1173,6 +1154,1864 @@ format_cb_mouse_line(struct format_tree *ft)
return (format_grid_line(gd, gd->hsize + y));
}
+/* Callback for alternate_on. */
+static void *
+format_cb_alternate_on(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->base.saved_grid != NULL)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for alternate_saved_x. */
+static void *
+format_cb_alternate_saved_x(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%u", ft->wp->base.saved_cx));
+ return (NULL);
+}
+
+/* Callback for alternate_saved_y. */
+static void *
+format_cb_alternate_saved_y(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%u", ft->wp->base.saved_cy));
+ return (NULL);
+}
+
+/* Callback for buffer_name. */
+static void *
+format_cb_buffer_name(struct format_tree *ft)
+{
+ if (ft->pb != NULL)
+ return (xstrdup(paste_buffer_name(ft->pb)));
+ return (NULL);
+}
+
+/* Callback for buffer_sample. */
+static void *
+format_cb_buffer_sample(struct format_tree *ft)
+{
+ if (ft->pb != NULL)
+ return (paste_make_sample(ft->pb));
+ return (NULL);
+}
+
+/* Callback for buffer_size. */
+static void *
+format_cb_buffer_size(struct format_tree *ft)
+{
+ size_t size;
+
+ if (ft->pb != NULL) {
+ paste_buffer_data(ft->pb, &size);
+ return (format_printf("%zu", size));
+ }
+ return (NULL);
+}
+
+/* Callback for client_cell_height. */
+static void *
+format_cb_client_cell_height(struct format_tree *ft)
+{
+ if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
+ return (format_printf("%u", ft->c->tty.ypixel));
+ return (NULL);
+}
+
+/* Callback for client_cell_width. */
+static void *
+format_cb_client_cell_width(struct format_tree *ft)
+{
+ if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
+ return (format_printf("%u", ft->c->tty.xpixel));
+ return (NULL);
+}
+
+/* Callback for client_control_mode. */
+static void *
+format_cb_client_control_mode(struct format_tree *ft)
+{
+ if (ft->c != NULL) {
+ if (ft->c->flags & CLIENT_CONTROL)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for client_discarded. */
+static void *
+format_cb_client_discarded(struct format_tree *ft)
+{
+ if (ft->c != NULL)
+ return (format_printf("%zu", ft->c->discarded));
+ return (NULL);
+}
+
+/* Callback for client_flags. */
+static void *
+format_cb_client_flags(struct format_tree *ft)
+{
+ if (ft->c != NULL)
+ return (xstrdup(server_client_get_flags(ft->c)));
+ return (NULL);
+}
+
+/* Callback for client_height. */
+static void *
+format_cb_client_height(struct format_tree *ft)
+{
+ if (ft->c != NULL && (ft->c->tty.flags & TTY_STARTED))
+ return (format_printf("%u", ft->c->tty.sy));
+ return (NULL);
+}
+
+/* Callback for client_key_table. */
+static void *
+format_cb_client_key_table(struct format_tree *ft)
+{
+ if (ft->c != NULL)
+ return (xstrdup(ft->c->keytable->name));
+ return (NULL);
+}
+
+/* Callback for client_last_session. */
+static void *
+format_cb_client_last_session(struct format_tree *ft)
+{
+ if (ft->c != NULL &&
+ ft->c->last_session != NULL &&
+ session_alive(ft->c->last_session))
+ return (xstrdup(ft->c->last_session->name));
+ return (NULL);
+}
+
+/* Callback for client_name. */
+static void *
+format_cb_client_name(struct format_tree *ft)
+{
+ if (ft->c != NULL)
+ return (xstrdup(ft->c->name));
+ return (NULL);
+}
+
+/* Callback for client_pid. */
+static void *
+format_cb_client_pid(struct format_tree *ft)
+{
+ if (ft->c != NULL)
+ return (format_printf("%ld", (long)ft->c->pid));
+ return (NULL);
+}
+
+/* Callback for client_prefix. */
+static void *
+format_cb_client_prefix(struct format_tree *ft)
+{
+ const char *name;
+
+ if (ft->c != NULL) {
+ name = server_client_get_key_table(ft->c);
+ if (strcmp(ft->c->keytable->name, name) == 0)
+ return (xstrdup("0"));
+ return (xstrdup("1"));
+ }
+ return (NULL);
+}
+
+/* Callback for client_readonly. */
+static void *
+format_cb_client_readonly(struct format_tree *ft)
+{
+ if (ft->c != NULL) {
+ if (ft->c->flags & CLIENT_READONLY)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for client_session. */
+static void *
+format_cb_client_session(struct format_tree *ft)
+{
+ if (ft->c != NULL && ft->c->session != NULL)
+ return (xstrdup(ft->c->session->name));
+ return (NULL);
+}
+
+/* Callback for client_termfeatures. */
+static void *
+format_cb_client_termfeatures(struct format_tree *ft)
+{
+ if (ft->c != NULL)
+ return (xstrdup(tty_get_features(ft->c->term_features)));
+ return (NULL);
+}
+
+/* Callback for client_termname. */
+static void *
+format_cb_client_termname(struct format_tree *ft)
+{
+ if (ft->c != NULL)
+ return (xstrdup(ft->c->term_name));
+ return (NULL);
+}
+
+/* Callback for client_termtype. */
+static void *
+format_cb_client_termtype(struct format_tree *ft)
+{
+ if (ft->c != NULL) {
+ if (ft->c->term_type == NULL)
+ return (xstrdup(""));
+ return (xstrdup(ft->c->term_type));
+ }
+ return (NULL);
+}
+
+/* Callback for client_tty. */
+static void *
+format_cb_client_tty(struct format_tree *ft)
+{
+ if (ft->c != NULL)
+ return (xstrdup(ft->c->ttyname));
+ return (NULL);
+}
+
+/* Callback for client_utf8. */
+static void *
+format_cb_client_utf8(struct format_tree *ft)
+{
+ if (ft->c != NULL) {
+ if (ft->c->flags & CLIENT_UTF8)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for client_width. */
+static void *
+format_cb_client_width(struct format_tree *ft)
+{
+ if (ft->c != NULL)
+ return (format_printf("%u", ft->c->tty.sx));
+ return (NULL);
+}
+
+/* Callback for client_written. */
+static void *
+format_cb_client_written(struct format_tree *ft)
+{
+ if (ft->c != NULL)
+ return (format_printf("%zu", ft->c->written));
+ return (NULL);
+}
+
+/* Callback for config_files. */
+static void *
+format_cb_config_files(__unused struct format_tree *ft)
+{
+ char *s = NULL;
+ size_t slen = 0;
+ u_int i;
+ size_t n;
+
+ for (i = 0; i < cfg_nfiles; i++) {
+ n = strlen(cfg_files[i]) + 1;
+ s = xrealloc(s, slen + n + 1);
+ slen += xsnprintf(s + slen, n + 1, "%s,", cfg_files[i]);
+ }
+ if (s == NULL)
+ return (xstrdup(""));
+ s[slen - 1] = '\0';
+ return (s);
+}
+
+/* Callback for cursor_flag. */
+static void *
+format_cb_cursor_flag(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->base.mode & MODE_CURSOR)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for cursor_x. */
+static void *
+format_cb_cursor_x(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%u", ft->wp->base.cx));
+ return (NULL);
+}
+
+/* Callback for cursor_y. */
+static void *
+format_cb_cursor_y(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%u", ft->wp->base.cy));
+ return (NULL);
+}
+
+/* Callback for history_limit. */
+static void *
+format_cb_history_limit(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%u", ft->wp->base.grid->hlimit));
+ return (NULL);
+}
+
+/* Callback for history_size. */
+static void *
+format_cb_history_size(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%u", ft->wp->base.grid->hsize));
+ return (NULL);
+}
+
+/* Callback for insert_flag. */
+static void *
+format_cb_insert_flag(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->base.mode & MODE_INSERT)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for keypad_cursor_flag. */
+static void *
+format_cb_keypad_cursor_flag(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->base.mode & MODE_KCURSOR)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for keypad_flag. */
+static void *
+format_cb_keypad_flag(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->base.mode & MODE_KKEYPAD)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for mouse_all_flag. */
+static void *
+format_cb_mouse_all_flag(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->base.mode & MODE_MOUSE_ALL)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for mouse_any_flag. */
+static void *
+format_cb_mouse_any_flag(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->base.mode & ALL_MOUSE_MODES)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for mouse_button_flag. */
+static void *
+format_cb_mouse_button_flag(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->base.mode & MODE_MOUSE_BUTTON)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for mouse_pane. */
+static void *
+format_cb_mouse_pane(struct format_tree *ft)
+{
+ struct window_pane *wp;
+
+ if (ft->m.valid) {
+ wp = cmd_mouse_pane(&ft->m, NULL, NULL);
+ if (wp != NULL)
+ return (format_printf("%%%u", wp->id));
+ return (NULL);
+ }
+ return (NULL);
+}
+
+/* Callback for mouse_sgr_flag. */
+static void *
+format_cb_mouse_sgr_flag(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->base.mode & MODE_MOUSE_SGR)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for mouse_standard_flag. */
+static void *
+format_cb_mouse_standard_flag(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->base.mode & MODE_MOUSE_STANDARD)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for mouse_utf8_flag. */
+static void *
+format_cb_mouse_utf8_flag(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->base.mode & MODE_MOUSE_UTF8)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for mouse_x. */
+static void *
+format_cb_mouse_x(struct format_tree *ft)
+{
+ struct window_pane *wp;
+ u_int x, y;
+
+ if (ft->m.valid) {
+ wp = cmd_mouse_pane(&ft->m, NULL, NULL);
+ if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0)
+ return (format_printf("%u", x));
+ return (NULL);
+ }
+ return (NULL);
+}
+
+/* Callback for mouse_y. */
+static void *
+format_cb_mouse_y(struct format_tree *ft)
+{
+ struct window_pane *wp;
+ u_int x, y;
+
+ if (ft->m.valid) {
+ wp = cmd_mouse_pane(&ft->m, NULL, NULL);
+ if (wp != NULL && cmd_mouse_at(wp, &ft->m, &x, &y, 0) == 0)
+ return (format_printf("%u", y));
+ return (NULL);
+ }
+ return (NULL);
+}
+
+/* Callback for origin_flag. */
+static void *
+format_cb_origin_flag(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->base.mode & MODE_ORIGIN)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for pane_active. */
+static void *
+format_cb_pane_active(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp == ft->wp->window->active)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for pane_at_left. */
+static void *
+format_cb_pane_at_left(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->xoff == 0)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for pane_at_right. */
+static void *
+format_cb_pane_at_right(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->xoff + ft->wp->sx == ft->wp->window->sx)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for pane_bottom. */
+static void *
+format_cb_pane_bottom(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%u", ft->wp->yoff + ft->wp->sy - 1));
+ return (NULL);
+}
+
+/* Callback for pane_dead. */
+static void *
+format_cb_pane_dead(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->fd == -1)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for pane_dead_status. */
+static void *
+format_cb_pane_dead_status(struct format_tree *ft)
+{
+ struct window_pane *wp = ft->wp;
+
+ if (wp != NULL) {
+ if ((wp->flags & PANE_STATUSREADY) && WIFEXITED(wp->status))
+ return (format_printf("%d", WEXITSTATUS(wp->status)));
+ return (NULL);
+ }
+ return (NULL);
+}
+
+/* Callback for pane_format. */
+static void *
+format_cb_pane_format(struct format_tree *ft)
+{
+ if (ft->type == FORMAT_TYPE_PANE)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+}
+
+/* Callback for pane_height. */
+static void *
+format_cb_pane_height(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%u", ft->wp->sy));
+ return (NULL);
+}
+
+/* Callback for pane_id. */
+static void *
+format_cb_pane_id(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%%%u", ft->wp->id));
+ return (NULL);
+}
+
+/* Callback for pane_index. */
+static void *
+format_cb_pane_index(struct format_tree *ft)
+{
+ u_int idx;
+
+ if (ft->wp != NULL && window_pane_index(ft->wp, &idx) == 0)
+ return (format_printf("%u", idx));
+ return (NULL);
+}
+
+/* Callback for pane_input_off. */
+static void *
+format_cb_pane_input_off(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->flags & PANE_INPUTOFF)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for pane_last. */
+static void *
+format_cb_pane_last(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp == ft->wp->window->last)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for pane_left. */
+static void *
+format_cb_pane_left(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%u", ft->wp->xoff));
+ return (NULL);
+}
+
+/* Callback for pane_marked. */
+static void *
+format_cb_pane_marked(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (server_check_marked() && marked_pane.wp == ft->wp)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for pane_marked_set. */
+static void *
+format_cb_pane_marked_set(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (server_check_marked())
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for pane_mode. */
+static void *
+format_cb_pane_mode(struct format_tree *ft)
+{
+ struct window_mode_entry *wme;
+
+ if (ft->wp != NULL) {
+ wme = TAILQ_FIRST(&ft->wp->modes);
+ if (wme != NULL)
+ return (xstrdup(wme->mode->name));
+ return (NULL);
+ }
+ return (NULL);
+}
+
+/* Callback for pane_path. */
+static void *
+format_cb_pane_path(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->base.path == NULL)
+ return (xstrdup(""));
+ return (xstrdup(ft->wp->base.path));
+ }
+ return (NULL);
+}
+
+/* Callback for pane_pid. */
+static void *
+format_cb_pane_pid(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%ld", (long)ft->wp->pid));
+ return (NULL);
+}
+
+/* Callback for pane_pipe. */
+static void *
+format_cb_pane_pipe(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->pipe_fd != -1)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for pane_right. */
+static void *
+format_cb_pane_right(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%u", ft->wp->xoff + ft->wp->sx - 1));
+ return (NULL);
+}
+
+/* Callback for pane_search_string. */
+static void *
+format_cb_pane_search_string(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->searchstr == NULL)
+ return (xstrdup(""));
+ return (xstrdup(ft->wp->searchstr));
+ }
+ return (NULL);
+}
+
+/* Callback for pane_synchronized. */
+static void *
+format_cb_pane_synchronized(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (options_get_number(ft->wp->options, "synchronize-panes"))
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for pane_title. */
+static void *
+format_cb_pane_title(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (xstrdup(ft->wp->base.title));
+ return (NULL);
+}
+
+/* Callback for pane_top. */
+static void *
+format_cb_pane_top(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%u", ft->wp->yoff));
+ return (NULL);
+}
+
+/* Callback for pane_tty. */
+static void *
+format_cb_pane_tty(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (xstrdup(ft->wp->tty));
+ return (NULL);
+}
+
+/* Callback for pane_width. */
+static void *
+format_cb_pane_width(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%u", ft->wp->sx));
+ return (NULL);
+}
+
+/* Callback for scroll_region_lower. */
+static void *
+format_cb_scroll_region_lower(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%u", ft->wp->base.rlower));
+ return (NULL);
+}
+
+/* Callback for scroll_region_upper. */
+static void *
+format_cb_scroll_region_upper(struct format_tree *ft)
+{
+ if (ft->wp != NULL)
+ return (format_printf("%u", ft->wp->base.rupper));
+ return (NULL);
+}
+
+/* Callback for session_attached. */
+static void *
+format_cb_session_attached(struct format_tree *ft)
+{
+ if (ft->s != NULL)
+ return (format_printf("%u", ft->s->attached));
+ return (NULL);
+}
+
+/* Callback for session_format. */
+static void *
+format_cb_session_format(struct format_tree *ft)
+{
+ if (ft->type == FORMAT_TYPE_SESSION)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+}
+
+/* Callback for session_group. */
+static void *
+format_cb_session_group(struct format_tree *ft)
+{
+ struct session_group *sg;
+
+ if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
+ return (xstrdup(sg->name));
+ return (NULL);
+}
+
+/* Callback for session_group_attached. */
+static void *
+format_cb_session_group_attached(struct format_tree *ft)
+{
+ struct session_group *sg;
+
+ if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
+ return (format_printf("%u", session_group_attached_count (sg)));
+ return (NULL);
+}
+
+/* Callback for session_group_many_attached. */
+static void *
+format_cb_session_group_many_attached(struct format_tree *ft)
+{
+ struct session_group *sg;
+
+ if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL) {
+ if (session_group_attached_count (sg) > 1)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for session_group_size. */
+static void *
+format_cb_session_group_size(struct format_tree *ft)
+{
+ struct session_group *sg;
+
+ if (ft->s != NULL && (sg = session_group_contains(ft->s)) != NULL)
+ return (format_printf("%u", session_group_count (sg)));
+ return (NULL);
+}
+
+/* Callback for session_grouped. */
+static void *
+format_cb_session_grouped(struct format_tree *ft)
+{
+ if (ft->s != NULL) {
+ if (session_group_contains(ft->s) != NULL)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for session_id. */
+static void *
+format_cb_session_id(struct format_tree *ft)
+{
+ if (ft->s != NULL)
+ return (format_printf("$%u", ft->s->id));
+ return (NULL);
+}
+
+/* Callback for session_many_attached. */
+static void *
+format_cb_session_many_attached(struct format_tree *ft)
+{
+ if (ft->s != NULL) {
+ if (ft->s->attached > 1)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for session_marked. */
+static void *
+format_cb_session_marked(struct format_tree *ft)
+{
+ if (ft->s != NULL) {
+ if (server_check_marked() && marked_pane.s == ft->s)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for session_name. */
+static void *
+format_cb_session_name(struct format_tree *ft)
+{
+ if (ft->s != NULL)
+ return (xstrdup(ft->s->name));
+ return (NULL);
+}
+
+/* Callback for session_path. */
+static void *
+format_cb_session_path(struct format_tree *ft)
+{
+ if (ft->s != NULL)
+ return (xstrdup(ft->s->cwd));
+ return (NULL);
+}
+
+/* Callback for session_windows. */
+static void *
+format_cb_session_windows(struct format_tree *ft)
+{
+ if (ft->s != NULL)
+ return (format_printf ("%u", winlink_count(&ft->s->windows)));
+ return (NULL);
+}
+
+/* Callback for socket_path. */
+static void *
+format_cb_socket_path(__unused struct format_tree *ft)
+{
+ return (xstrdup(socket_path));
+}
+
+/* Callback for version. */
+static void *
+format_cb_version(__unused struct format_tree *ft)
+{
+ return (xstrdup(getversion()));
+}
+
+/* Callback for active_window_index. */
+static void *
+format_cb_active_window_index(struct format_tree *ft)
+{
+ if (ft->s != NULL)
+ return (format_printf("%u", ft->s->curw->idx));
+ return (NULL);
+}
+
+/* Callback for last_window_index. */
+static void *
+format_cb_last_window_index(struct format_tree *ft)
+{
+ struct winlink *wl;
+
+ if (ft->s != NULL) {
+ wl = RB_MAX(winlinks, &ft->s->windows);
+ return (format_printf("%u", wl->idx));
+ }
+ return (NULL);
+}
+
+/* Callback for window_active. */
+static void *
+format_cb_window_active(struct format_tree *ft)
+{
+ if (ft->wl != NULL) {
+ if (ft->wl == ft->wl->session->curw)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for window_activity_flag. */
+static void *
+format_cb_window_activity_flag(struct format_tree *ft)
+{
+ if (ft->wl != NULL) {
+ if (ft->wl->flags & WINLINK_ACTIVITY)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for window_bell_flag. */
+static void *
+format_cb_window_bell_flag(struct format_tree *ft)
+{
+ if (ft->wl != NULL) {
+ if (ft->wl->flags & WINLINK_BELL)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for window_bigger. */
+static void *
+format_cb_window_bigger(struct format_tree *ft)
+{
+ u_int ox, oy, sx, sy;
+
+ if (ft->c != NULL) {
+ if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for window_cell_height. */
+static void *
+format_cb_window_cell_height(struct format_tree *ft)
+{
+ if (ft->w != NULL)
+ return (format_printf("%u", ft->w->ypixel));
+ return (NULL);
+}
+
+/* Callback for window_cell_width. */
+static void *
+format_cb_window_cell_width(struct format_tree *ft)
+{
+ if (ft->w != NULL)
+ return (format_printf("%u", ft->w->xpixel));
+ return (NULL);
+}
+
+/* Callback for window_end_flag. */
+static void *
+format_cb_window_end_flag(struct format_tree *ft)
+{
+ if (ft->wl != NULL) {
+ if (ft->wl == RB_MAX(winlinks, &ft->wl->session->windows))
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for window_flags. */
+static void *
+format_cb_window_flags(struct format_tree *ft)
+{
+ if (ft->wl != NULL)
+ return (xstrdup(window_printable_flags(ft->wl, 1)));
+ return (NULL);
+}
+
+/* Callback for window_format. */
+static void *
+format_cb_window_format(struct format_tree *ft)
+{
+ if (ft->type == FORMAT_TYPE_WINDOW)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+}
+
+/* Callback for window_height. */
+static void *
+format_cb_window_height(struct format_tree *ft)
+{
+ if (ft->w != NULL)
+ return (format_printf("%u", ft->w->sy));
+ return (NULL);
+}
+
+/* Callback for window_id. */
+static void *
+format_cb_window_id(struct format_tree *ft)
+{
+ if (ft->w != NULL)
+ return (format_printf("@%u", ft->w->id));
+ return (NULL);
+}
+
+/* Callback for window_index. */
+static void *
+format_cb_window_index(struct format_tree *ft)
+{
+ if (ft->wl != NULL)
+ return (format_printf("%d", ft->wl->idx));
+ return (NULL);
+}
+
+/* Callback for window_last_flag. */
+static void *
+format_cb_window_last_flag(struct format_tree *ft)
+{
+ if (ft->wl != NULL) {
+ if (ft->wl == TAILQ_FIRST(&ft->wl->session->lastw))
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for window_linked. */
+static void *
+format_cb_window_linked(struct format_tree *ft)
+{
+ if (ft->wl != NULL) {
+ if (session_is_linked(ft->wl->session, ft->wl->window))
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for window_linked_sessions. */
+static void *
+format_cb_window_linked_sessions(struct format_tree *ft)
+{
+ if (ft->wl != NULL)
+ return (format_printf("%u", ft->wl->window->references));
+ return (NULL);
+}
+
+/* Callback for window_marked_flag. */
+static void *
+format_cb_window_marked_flag(struct format_tree *ft)
+{
+ if (ft->wl != NULL) {
+ if (server_check_marked() && marked_pane.wl == ft->wl)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for window_name. */
+static void *
+format_cb_window_name(struct format_tree *ft)
+{
+ if (ft->w != NULL)
+ return (format_printf("%s", ft->w->name));
+ return (NULL);
+}
+
+/* Callback for window_offset_x. */
+static void *
+format_cb_window_offset_x(struct format_tree *ft)
+{
+ u_int ox, oy, sx, sy;
+
+ if (ft->c != NULL) {
+ if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
+ return (format_printf("%u", ox));
+ return (NULL);
+ }
+ return (NULL);
+}
+
+/* Callback for window_offset_y. */
+static void *
+format_cb_window_offset_y(struct format_tree *ft)
+{
+ u_int ox, oy, sx, sy;
+
+ if (ft->c != NULL) {
+ if (tty_window_offset(&ft->c->tty, &ox, &oy, &sx, &sy))
+ return (format_printf("%u", oy));
+ return (NULL);
+ }
+ return (NULL);
+}
+
+/* Callback for window_panes. */
+static void *
+format_cb_window_panes(struct format_tree *ft)
+{
+ if (ft->w != NULL)
+ return (format_printf("%u", window_count_panes(ft->w)));
+ return (NULL);
+}
+
+/* Callback for window_raw_flags. */
+static void *
+format_cb_window_raw_flags(struct format_tree *ft)
+{
+ if (ft->wl != NULL)
+ return (xstrdup(window_printable_flags(ft->wl, 0)));
+ return (NULL);
+}
+
+/* Callback for window_silence_flag. */
+static void *
+format_cb_window_silence_flag(struct format_tree *ft)
+{
+ if (ft->wl != NULL) {
+ if (ft->wl->flags & WINLINK_SILENCE)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for window_start_flag. */
+static void *
+format_cb_window_start_flag(struct format_tree *ft)
+{
+ if (ft->wl != NULL) {
+ if (ft->wl == RB_MIN(winlinks, &ft->wl->session->windows))
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for window_width. */
+static void *
+format_cb_window_width(struct format_tree *ft)
+{
+ if (ft->w != NULL)
+ return (format_printf("%u", ft->w->sx));
+ return (NULL);
+}
+
+/* Callback for window_zoomed_flag. */
+static void *
+format_cb_window_zoomed_flag(struct format_tree *ft)
+{
+ if (ft->w != NULL) {
+ if (ft->w->flags & WINDOW_ZOOMED)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for wrap_flag. */
+static void *
+format_cb_wrap_flag(struct format_tree *ft)
+{
+ if (ft->wp != NULL) {
+ if (ft->wp->base.mode & MODE_WRAP)
+ return (xstrdup("1"));
+ return (xstrdup("0"));
+ }
+ return (NULL);
+}
+
+/* Callback for buffer_created. */
+static void *
+format_cb_buffer_created(struct format_tree *ft)
+{
+ static struct timeval tv;
+
+ if (ft->pb != NULL) {
+ timerclear(&tv);
+ tv.tv_sec = paste_buffer_created(ft->pb);
+ return (&tv);
+ }
+ return (NULL);
+}
+
+/* Callback for client_activity. */
+static void *
+format_cb_client_activity(struct format_tree *ft)
+{
+ if (ft->c != NULL)
+ return (&ft->c->activity_time);
+ return (NULL);
+}
+
+/* Callback for client_created. */
+static void *
+format_cb_client_created(struct format_tree *ft)
+{
+ if (ft->c != NULL)
+ return (&ft->c->creation_time);
+ return (NULL);
+}
+
+/* Callback for session_activity. */
+static void *
+format_cb_session_activity(struct format_tree *ft)
+{
+ if (ft->s != NULL)
+ return (&ft->s->activity_time);
+ return (NULL);
+}
+
+/* Callback for session_created. */
+static void *
+format_cb_session_created(struct format_tree *ft)
+{
+ if (ft->s != NULL)
+ return (&ft->s->creation_time);
+ return (NULL);
+}
+
+/* Callback for session_last_attached. */
+static void *
+format_cb_session_last_attached(struct format_tree *ft)
+{
+ if (ft->s != NULL)
+ return (&ft->s->last_attached_time);
+ return (NULL);
+}
+
+/* Callback for start_time. */
+static void *
+format_cb_start_time(__unused struct format_tree *ft)
+{
+ return (&start_time);
+}
+
+/* Callback for window_activity. */
+static void *
+format_cb_window_activity(struct format_tree *ft)
+{
+ if (ft->w != NULL)
+ return (&ft->w->activity_time);
+ return (NULL);
+}
+
+/* Callback for buffer_mode_format, */
+static void *
+format_cb_buffer_mode_format(__unused struct format_tree *ft)
+{
+ return (xstrdup(window_buffer_mode.default_format));
+}
+
+/* Callback for client_mode_format, */
+static void *
+format_cb_client_mode_format(__unused struct format_tree *ft)
+{
+ return (xstrdup(window_client_mode.default_format));
+}
+
+/* Callback for tree_mode_format, */
+static void *
+format_cb_tree_mode_format(__unused struct format_tree *ft)
+{
+ return (xstrdup(window_tree_mode.default_format));
+}
+
+/* Format table type. */
+enum format_table_type {
+ FORMAT_TABLE_STRING,
+ FORMAT_TABLE_TIME
+};
+
+/* Format table entry. */
+struct format_table_entry {
+ const char *key;
+ enum format_table_type type;
+ format_cb cb;
+};
+
+/*
+ * Format table. Default format variables (that are almost always in the tree
+ * and where the value is expanded by a callback in this file) are listed here.
+ * Only variables which are added by the caller go into the tree.
+ */
+static const struct format_table_entry format_table[] = {
+ { "active_window_index", FORMAT_TABLE_STRING,
+ format_cb_active_window_index
+ },
+ { "alternate_on", FORMAT_TABLE_STRING,
+ format_cb_alternate_on
+ },
+ { "alternate_saved_x", FORMAT_TABLE_STRING,
+ format_cb_alternate_saved_x
+ },
+ { "alternate_saved_y", FORMAT_TABLE_STRING,
+ format_cb_alternate_saved_y
+ },
+ { "buffer_created", FORMAT_TABLE_TIME,
+ format_cb_buffer_created
+ },
+ { "buffer_mode_format", FORMAT_TABLE_STRING,
+ format_cb_buffer_mode_format
+ },
+ { "buffer_name", FORMAT_TABLE_STRING,
+ format_cb_buffer_name
+ },
+ { "buffer_sample", FORMAT_TABLE_STRING,
+ format_cb_buffer_sample
+ },
+ { "buffer_size", FORMAT_TABLE_STRING,
+ format_cb_buffer_size
+ },
+ { "client_activity", FORMAT_TABLE_TIME,
+ format_cb_client_activity
+ },
+ { "client_cell_height", FORMAT_TABLE_STRING,
+ format_cb_client_cell_height
+ },
+ { "client_cell_width", FORMAT_TABLE_STRING,
+ format_cb_client_cell_width
+ },
+ { "client_control_mode", FORMAT_TABLE_STRING,
+ format_cb_client_control_mode
+ },
+ { "client_created", FORMAT_TABLE_TIME,
+ format_cb_client_created
+ },
+ { "client_discarded", FORMAT_TABLE_STRING,
+ format_cb_client_discarded
+ },
+ { "client_flags", FORMAT_TABLE_STRING,
+ format_cb_client_flags
+ },
+ { "client_height", FORMAT_TABLE_STRING,
+ format_cb_client_height
+ },
+ { "client_key_table", FORMAT_TABLE_STRING,
+ format_cb_client_key_table
+ },
+ { "client_last_session", FORMAT_TABLE_STRING,
+ format_cb_client_last_session
+ },
+ { "client_mode_format", FORMAT_TABLE_STRING,
+ format_cb_client_mode_format
+ },
+ { "client_name", FORMAT_TABLE_STRING,
+ format_cb_client_name
+ },
+ { "client_pid", FORMAT_TABLE_STRING,
+ format_cb_client_pid
+ },
+ { "client_prefix", FORMAT_TABLE_STRING,
+ format_cb_client_prefix
+ },
+ { "client_readonly", FORMAT_TABLE_STRING,
+ format_cb_client_readonly
+ },
+ { "client_session", FORMAT_TABLE_STRING,
+ format_cb_client_session
+ },
+ { "client_termfeatures", FORMAT_TABLE_STRING,
+ format_cb_client_termfeatures
+ },
+ { "client_termname", FORMAT_TABLE_STRING,
+ format_cb_client_termname
+ },
+ { "client_termtype", FORMAT_TABLE_STRING,
+ format_cb_client_termtype
+ },
+ { "client_tty", FORMAT_TABLE_STRING,
+ format_cb_client_tty
+ },
+ { "client_utf8", FORMAT_TABLE_STRING,
+ format_cb_client_utf8
+ },
+ { "client_width", FORMAT_TABLE_STRING,
+ format_cb_client_width
+ },
+ { "client_written", FORMAT_TABLE_STRING,
+ format_cb_client_written
+ },
+ { "config_files", FORMAT_TABLE_STRING,
+ format_cb_config_files
+ },
+ { "cursor_character", FORMAT_TABLE_STRING,
+ format_cb_cursor_character
+ },
+ { "cursor_flag", FORMAT_TABLE_STRING,
+ format_cb_cursor_flag
+ },
+ { "cursor_x", FORMAT_TABLE_STRING,
+ format_cb_cursor_x
+ },
+ { "cursor_y", FORMAT_TABLE_STRING,
+ format_cb_cursor_y
+ },
+ { "history_all_bytes", FORMAT_TABLE_STRING,
+ format_cb_history_all_bytes
+ },
+ { "history_bytes", FORMAT_TABLE_STRING,
+ format_cb_history_bytes
+ },
+ { "history_limit", FORMAT_TABLE_STRING,
+ format_cb_history_limit
+ },
+ { "history_size", FORMAT_TABLE_STRING,
+ format_cb_history_size
+ },
+ { "host", FORMAT_TABLE_STRING,
+ format_cb_host
+ },
+ { "host_short", FORMAT_TABLE_STRING,
+ format_cb_host_short
+ },
+ { "insert_flag", FORMAT_TABLE_STRING,
+ format_cb_insert_flag
+ },
+ { "keypad_cursor_flag", FORMAT_TABLE_STRING,
+ format_cb_keypad_cursor_flag
+ },
+ { "keypad_flag", FORMAT_TABLE_STRING,
+ format_cb_keypad_flag
+ },
+ { "last_window_index", FORMAT_TABLE_STRING,
+ format_cb_last_window_index
+ },
+ { "mouse_all_flag", FORMAT_TABLE_STRING,
+ format_cb_mouse_all_flag
+ },
+ { "mouse_any_flag", FORMAT_TABLE_STRING,
+ format_cb_mouse_any_flag
+ },
+ { "mouse_button_flag", FORMAT_TABLE_STRING,
+ format_cb_mouse_button_flag
+ },
+ { "mouse_line", FORMAT_TABLE_STRING,
+ format_cb_mouse_line
+ },
+ { "mouse_pane", FORMAT_TABLE_STRING,
+ format_cb_mouse_pane
+ },
+ { "mouse_sgr_flag", FORMAT_TABLE_STRING,
+ format_cb_mouse_sgr_flag
+ },
+ { "mouse_standard_flag", FORMAT_TABLE_STRING,
+ format_cb_mouse_standard_flag
+ },
+ { "mouse_utf8_flag", FORMAT_TABLE_STRING,
+ format_cb_mouse_utf8_flag
+ },
+ { "mouse_word", FORMAT_TABLE_STRING,
+ format_cb_mouse_word
+ },
+ { "mouse_x", FORMAT_TABLE_STRING,
+ format_cb_mouse_x
+ },
+ { "mouse_y", FORMAT_TABLE_STRING,
+ format_cb_mouse_y
+ },
+ { "origin_flag", FORMAT_TABLE_STRING,
+ format_cb_origin_flag
+ },
+ { "pane_active", FORMAT_TABLE_STRING,
+ format_cb_pane_active
+ },
+ { "pane_at_bottom", FORMAT_TABLE_STRING,
+ format_cb_pane_at_bottom
+ },
+ { "pane_at_left", FORMAT_TABLE_STRING,
+ format_cb_pane_at_left
+ },
+ { "pane_at_right", FORMAT_TABLE_STRING,
+ format_cb_pane_at_right
+ },
+ { "pane_at_top", FORMAT_TABLE_STRING,
+ format_cb_pane_at_top
+ },
+ { "pane_bg", FORMAT_TABLE_STRING,
+ format_cb_pane_bg
+ },
+ { "pane_bottom", FORMAT_TABLE_STRING,
+ format_cb_pane_bottom
+ },
+ { "pane_current_command", FORMAT_TABLE_STRING,
+ format_cb_current_command
+ },
+ { "pane_current_path", FORMAT_TABLE_STRING,
+ format_cb_current_path
+ },
+ { "pane_dead", FORMAT_TABLE_STRING,
+ format_cb_pane_dead
+ },
+ { "pane_dead_status", FORMAT_TABLE_STRING,
+ format_cb_pane_dead_status
+ },
+ { "pane_fg", FORMAT_TABLE_STRING,
+ format_cb_pane_fg
+ },
+ { "pane_format", FORMAT_TABLE_STRING,
+ format_cb_pane_format
+ },
+ { "pane_height", FORMAT_TABLE_STRING,
+ format_cb_pane_height
+ },
+ { "pane_id", FORMAT_TABLE_STRING,
+ format_cb_pane_id
+ },
+ { "pane_in_mode", FORMAT_TABLE_STRING,
+ format_cb_pane_in_mode
+ },
+ { "pane_index", FORMAT_TABLE_STRING,
+ format_cb_pane_index
+ },
+ { "pane_input_off", FORMAT_TABLE_STRING,
+ format_cb_pane_input_off
+ },
+ { "pane_last", FORMAT_TABLE_STRING,
+ format_cb_pane_last
+ },
+ { "pane_left", FORMAT_TABLE_STRING,
+ format_cb_pane_left
+ },
+ { "pane_marked", FORMAT_TABLE_STRING,
+ format_cb_pane_marked
+ },
+ { "pane_marked_set", FORMAT_TABLE_STRING,
+ format_cb_pane_marked_set
+ },
+ { "pane_mode", FORMAT_TABLE_STRING,
+ format_cb_pane_mode
+ },
+ { "pane_path", FORMAT_TABLE_STRING,
+ format_cb_pane_path
+ },
+ { "pane_pid", FORMAT_TABLE_STRING,
+ format_cb_pane_pid
+ },
+ { "pane_pipe", FORMAT_TABLE_STRING,
+ format_cb_pane_pipe
+ },
+ { "pane_right", FORMAT_TABLE_STRING,
+ format_cb_pane_right
+ },
+ { "pane_search_string", FORMAT_TABLE_STRING,
+ format_cb_pane_search_string
+ },
+ { "pane_start_command", FORMAT_TABLE_STRING,
+ format_cb_start_command
+ },
+ { "pane_synchronized", FORMAT_TABLE_STRING,
+ format_cb_pane_synchronized
+ },
+ { "pane_tabs", FORMAT_TABLE_STRING,
+ format_cb_pane_tabs
+ },
+ { "pane_title", FORMAT_TABLE_STRING,
+ format_cb_pane_title
+ },
+ { "pane_top", FORMAT_TABLE_STRING,
+ format_cb_pane_top
+ },
+ { "pane_tty", FORMAT_TABLE_STRING,
+ format_cb_pane_tty
+ },
+ { "pane_width", FORMAT_TABLE_STRING,
+ format_cb_pane_width
+ },
+ { "pid", FORMAT_TABLE_STRING,
+ format_cb_pid
+ },
+ { "scroll_region_lower", FORMAT_TABLE_STRING,
+ format_cb_scroll_region_lower
+ },
+ { "scroll_region_upper", FORMAT_TABLE_STRING,
+ format_cb_scroll_region_upper
+ },
+ { "session_activity", FORMAT_TABLE_TIME,
+ format_cb_session_activity
+ },
+ { "session_alerts", FORMAT_TABLE_STRING,
+ format_cb_session_alerts
+ },
+ { "session_attached", FORMAT_TABLE_STRING,
+ format_cb_session_attached
+ },
+ { "session_attached_list", FORMAT_TABLE_STRING,
+ format_cb_session_attached_list
+ },
+ { "session_created", FORMAT_TABLE_TIME,
+ format_cb_session_created
+ },
+ { "session_format", FORMAT_TABLE_STRING,
+ format_cb_session_format
+ },
+ { "session_group", FORMAT_TABLE_STRING,
+ format_cb_session_group
+ },
+ { "session_group_attached", FORMAT_TABLE_STRING,
+ format_cb_session_group_attached
+ },
+ { "session_group_attached_list", FORMAT_TABLE_STRING,
+ format_cb_session_group_attached_list
+ },
+ { "session_group_list", FORMAT_TABLE_STRING,
+ format_cb_session_group_list
+ },
+ { "session_group_many_attached", FORMAT_TABLE_STRING,
+ format_cb_session_group_many_attached
+ },
+ { "session_group_size", FORMAT_TABLE_STRING,
+ format_cb_session_group_size
+ },
+ { "session_grouped", FORMAT_TABLE_STRING,
+ format_cb_session_grouped
+ },
+ { "session_id", FORMAT_TABLE_STRING,
+ format_cb_session_id
+ },
+ { "session_last_attached", FORMAT_TABLE_TIME,
+ format_cb_session_last_attached
+ },
+ { "session_many_attached", FORMAT_TABLE_STRING,
+ format_cb_session_many_attached
+ },
+ { "session_marked", FORMAT_TABLE_STRING,
+ format_cb_session_marked,
+ },
+ { "session_name", FORMAT_TABLE_STRING,
+ format_cb_session_name
+ },
+ { "session_path", FORMAT_TABLE_STRING,
+ format_cb_session_path
+ },
+ { "session_stack", FORMAT_TABLE_STRING,
+ format_cb_session_stack
+ },
+ { "session_windows", FORMAT_TABLE_STRING,
+ format_cb_session_windows
+ },
+ { "socket_path", FORMAT_TABLE_STRING,
+ format_cb_socket_path
+ },
+ { "start_time", FORMAT_TABLE_TIME,
+ format_cb_start_time
+ },
+ { "tree_mode_format", FORMAT_TABLE_STRING,
+ format_cb_tree_mode_format
+ },
+ { "version", FORMAT_TABLE_STRING,
+ format_cb_version
+ },
+ { "window_active", FORMAT_TABLE_STRING,
+ format_cb_window_active
+ },
+ { "window_active_clients", FORMAT_TABLE_STRING,
+ format_cb_window_active_clients
+ },
+ { "window_active_clients_list", FORMAT_TABLE_STRING,
+ format_cb_window_active_clients_list
+ },
+ { "window_active_sessions", FORMAT_TABLE_STRING,
+ format_cb_window_active_sessions
+ },
+ { "window_active_sessions_list", FORMAT_TABLE_STRING,
+ format_cb_window_active_sessions_list
+ },
+ { "window_activity", FORMAT_TABLE_TIME,
+ format_cb_window_activity
+ },
+ { "window_activity_flag", FORMAT_TABLE_STRING,
+ format_cb_window_activity_flag
+ },
+ { "window_bell_flag", FORMAT_TABLE_STRING,
+ format_cb_window_bell_flag
+ },
+ { "window_bigger", FORMAT_TABLE_STRING,
+ format_cb_window_bigger
+ },
+ { "window_cell_height", FORMAT_TABLE_STRING,
+ format_cb_window_cell_height
+ },
+ { "window_cell_width", FORMAT_TABLE_STRING,
+ format_cb_window_cell_width
+ },
+ { "window_end_flag", FORMAT_TABLE_STRING,
+ format_cb_window_end_flag
+ },
+ { "window_flags", FORMAT_TABLE_STRING,
+ format_cb_window_flags
+ },
+ { "window_format", FORMAT_TABLE_STRING,
+ format_cb_window_format
+ },
+ { "window_height", FORMAT_TABLE_STRING,
+ format_cb_window_height
+ },
+ { "window_id", FORMAT_TABLE_STRING,
+ format_cb_window_id
+ },
+ { "window_index", FORMAT_TABLE_STRING,
+ format_cb_window_index
+ },
+ { "window_last_flag", FORMAT_TABLE_STRING,
+ format_cb_window_last_flag
+ },
+ { "window_layout", FORMAT_TABLE_STRING,
+ format_cb_window_layout
+ },
+ { "window_linked", FORMAT_TABLE_STRING,
+ format_cb_window_linked
+ },
+ { "window_linked_sessions", FORMAT_TABLE_STRING,
+ format_cb_window_linked_sessions
+ },
+ { "window_linked_sessions_list", FORMAT_TABLE_STRING,
+ format_cb_window_linked_sessions_list
+ },
+ { "window_marked_flag", FORMAT_TABLE_STRING,
+ format_cb_window_marked_flag
+ },
+ { "window_name", FORMAT_TABLE_STRING,
+ format_cb_window_name
+ },
+ { "window_offset_x", FORMAT_TABLE_STRING,
+ format_cb_window_offset_x
+ },
+ { "window_offset_y", FORMAT_TABLE_STRING,
+ format_cb_window_offset_y
+ },
+ { "window_panes", FORMAT_TABLE_STRING,
+ format_cb_window_panes
+ },
+ { "window_raw_flags", FORMAT_TABLE_STRING,
+ format_cb_window_raw_flags
+ },
+ { "window_silence_flag", FORMAT_TABLE_STRING,
+ format_cb_window_silence_flag
+ },
+ { "window_stack_index", FORMAT_TABLE_STRING,
+ format_cb_window_stack_index
+ },
+ { "window_start_flag", FORMAT_TABLE_STRING,
+ format_cb_window_start_flag
+ },
+ { "window_visible_layout", FORMAT_TABLE_STRING,
+ format_cb_window_visible_layout
+ },
+ { "window_width", FORMAT_TABLE_STRING,
+ format_cb_window_width
+ },
+ { "window_zoomed_flag", FORMAT_TABLE_STRING,
+ format_cb_window_zoomed_flag
+ },
+ { "wrap_flag", FORMAT_TABLE_STRING,
+ format_cb_wrap_flag
+ }
+};
+
+/* Compare format table entries. */
+static int
+format_table_compare(const void *key0, const void *entry0)
+{
+ const char *key = key0;
+ const struct format_table_entry *entry = entry0;
+
+ return (strcmp(key, entry->key));
+}
+
+/* Get a format callback. */
+static struct format_table_entry *
+format_table_get(const char *key)
+{
+ return (bsearch(key, format_table, nitems(format_table),
+ sizeof *format_table, format_table_compare));
+}
+
/* Merge one format tree into another. */
void
format_merge(struct format_tree *ft, struct format_tree *from)
@@ -1198,20 +3037,8 @@ format_create_add_item(struct format_tree *ft, struct cmdq_item *item)
{
struct key_event *event = cmdq_get_event(item);
struct mouse_event *m = &event->m;
- struct window_pane *wp;
- u_int x, y;
cmdq_merge_formats(item, ft);
-
- if (m->valid && ((wp = cmd_mouse_pane(m, NULL, NULL)) != NULL)) {
- format_add(ft, "mouse_pane", "%%%u", wp->id);
- if (cmd_mouse_at(wp, m, &x, &y, 0) == 0) {
- format_add(ft, "mouse_x", "%u", x);
- format_add(ft, "mouse_y", "%u", y);
- format_add_cb(ft, "mouse_word", format_cb_mouse_word);
- format_add_cb(ft, "mouse_line", format_cb_mouse_line);
- }
- }
memcpy(&ft->m, m, sizeof ft->m);
}
@@ -1219,9 +3046,7 @@ format_create_add_item(struct format_tree *ft, struct cmdq_item *item)
struct format_tree *
format_create(struct client *c, struct cmdq_item *item, int tag, int flags)
{
- struct format_tree *ft;
- const struct window_mode **wm;
- char tmp[64];
+ struct format_tree *ft;
if (!event_initialized(&format_job_event)) {
evtimer_set(&format_job_event, format_job_timer, NULL);
@@ -1240,21 +3065,6 @@ format_create(struct client *c, struct cmdq_item *item, int tag, int flags)
ft->tag = tag;
ft->flags = flags;
- format_add(ft, "version", "%s", getversion());
- format_add_cb(ft, "host", format_cb_host);
- format_add_cb(ft, "host_short", format_cb_host_short);
- format_add_cb(ft, "pid", format_cb_pid);
- format_add(ft, "socket_path", "%s", socket_path);
- format_add_tv(ft, "start_time", &start_time);
-
- for (wm = all_window_modes; *wm != NULL; wm++) {
- if ((*wm)->default_format != NULL) {
- xsnprintf(tmp, sizeof tmp, "%s_format", (*wm)->name);
- tmp[strcspn(tmp, "-")] = '_';
- format_add(ft, tmp, "%s", (*wm)->default_format);
- }
- }
-
if (item != NULL)
format_create_add_item(ft, item);
@@ -1284,9 +3094,28 @@ void
format_each(struct format_tree *ft, void (*cb)(const char *, const char *,
void *), void *arg)
{
- struct format_entry *fe;
- char s[64];
+ const struct format_table_entry *fte;
+ struct format_entry *fe;
+ u_int i;
+ char s[64];
+ void *value;
+ struct timeval *tv;
+
+ for (i = 0; i < nitems(format_table); i++) {
+ fte = &format_table[i];
+ value = fte->cb(ft);
+ if (value == NULL)
+ continue;
+ if (fte->type == FORMAT_TABLE_TIME) {
+ tv = value;
+ xsnprintf(s, sizeof s, "%lld", (long long)tv->tv_sec);
+ cb(fte->key, s, arg);
+ } else {
+ cb(fte->key, value, arg);
+ free(value);
+ }
+ }
RB_FOREACH(fe, format_entry_tree, &ft->tree) {
if (fe->time != 0) {
xsnprintf(s, sizeof s, "%lld", (long long)fe->time);
@@ -1376,9 +3205,9 @@ format_add_cb(struct format_tree *ft, const char *key, format_cb cb)
fe->value = NULL;
}
-/* Quote special characters in string. */
+/* Quote shell special characters in string. */
static char *
-format_quote(const char *s)
+format_quote_shell(const char *s)
{
const char *cp;
char *out, *at;
@@ -1393,6 +3222,23 @@ format_quote(const char *s)
return (out);
}
+/* Quote #s in string. */
+static char *
+format_quote_style(const char *s)
+{
+ const char *cp;
+ char *out, *at;
+
+ at = out = xmalloc(strlen(s) * 2 + 1);
+ for (cp = s; *cp != '\0'; cp++) {
+ if (*cp == '#')
+ *at++ = '#';
+ *at++ = *cp;
+ }
+ *at = '\0';
+ return (out);
+}
+
/* Make a prettier time. */
static char *
format_pretty_time(time_t t)
@@ -1439,14 +3285,16 @@ static char *
format_find(struct format_tree *ft, const char *key, int modifiers,
const char *time_format)
{
- struct format_entry *fe, fe_find;
- struct environ_entry *envent;
- struct options_entry *o;
- int idx;
- char *found = NULL, *saved, s[512];
- const char *errstr;
- time_t t = 0;
- struct tm tm;
+ struct format_table_entry *fte;
+ void *value;
+ struct format_entry *fe, fe_find;
+ struct environ_entry *envent;
+ struct options_entry *o;
+ int idx;
+ char *found = NULL, *saved, s[512];
+ const char *errstr;
+ time_t t = 0;
+ struct tm tm;
o = options_parse_get(global_options, key, &idx, 0);
if (o == NULL && ft->wp != NULL)
@@ -1464,6 +3312,15 @@ format_find(struct format_tree *ft, const char *key, int modifiers,
goto found;
}
+ fte = format_table_get(key);
+ if (fte != NULL) {
+ value = fte->cb(ft);
+ if (fte->type == FORMAT_TABLE_TIME)
+ t = ((struct timeval *)value)->tv_sec;
+ else
+ found = value;
+ goto found;
+ }
fe_find.key = (char *)key;
fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find);
if (fe != NULL) {
@@ -1533,9 +3390,14 @@ found:
found = xstrdup(dirname(saved));
free(saved);
}
- if (modifiers & FORMAT_QUOTE) {
+ if (modifiers & FORMAT_QUOTE_SHELL) {
+ saved = found;
+ found = xstrdup(format_quote_shell(saved));
+ free(saved);
+ }
+ if (modifiers & FORMAT_QUOTE_STYLE) {
saved = found;
- found = xstrdup(format_quote(saved));
+ found = xstrdup(format_quote_style(saved));
free(saved);
}
return (found);
@@ -1671,7 +3533,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
/*
* Modifiers are a ; separated list of the forms:
- * l,m,C,b,d,n,t,q,E,T,S,W,P,<,>
+ * l,m,C,b,d,n,t,w,q,E,T,S,W,P,<,>
* =a
* =/a
* =/a/
@@ -1688,7 +3550,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
cp++;
/* Check single character modifiers with no arguments. */
- if (strchr("lbdnqETSWP<>", cp[0]) != NULL &&
+ if (strchr("lbdnwETSWP<>", cp[0]) != NULL &&
format_is_end(cp[1])) {
format_add_modifier(&list, count, cp, 1, NULL, 0);
cp++;
@@ -1709,7 +3571,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
}
/* Now try single character with arguments. */
- if (strchr("mCst=pe", cp[0]) == NULL)
+ if (strchr("mCNst=peq", cp[0]) == NULL)
break;
c = cp[0];
@@ -1833,6 +3695,24 @@ format_search(struct format_modifier *fm, struct window_pane *wp, const char *s)
return (value);
}
+/* Does session name exist? */
+static char *
+format_session_name(struct format_expand_state *es, const char *fmt)
+{
+ char *name;
+ struct session *s;
+
+ name = format_expand1(es, fmt);
+ RB_FOREACH(s, sessions, &sessions) {
+ if (strcmp(s->name, name) == 0) {
+ free(name);
+ return (xstrdup("1"));
+ }
+ }
+ free(name);
+ return (xstrdup("0"));
+}
+
/* Loop over sessions. */
static char *
format_loop_sessions(struct format_expand_state *es, const char *fmt)
@@ -1852,7 +3732,7 @@ format_loop_sessions(struct format_expand_state *es, const char *fmt)
RB_FOREACH(s, sessions, &sessions) {
format_log(es, "session loop: $%u", s->id);
nft = format_create(c, item, FORMAT_NONE, ft->flags);
- format_defaults(next.ft, ft->c, s, NULL, NULL);
+ format_defaults(nft, ft->c, s, NULL, NULL);
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, fmt);
@@ -1868,6 +3748,30 @@ format_loop_sessions(struct format_expand_state *es, const char *fmt)
return (value);
}
+/* Does window name exist? */
+static char *
+format_window_name(struct format_expand_state *es, const char *fmt)
+{
+ struct format_tree *ft = es->ft;
+ char *name;
+ struct winlink *wl;
+
+ if (ft->s == NULL) {
+ format_log(es, "window name but no session");
+ return (NULL);
+ }
+
+ name = format_expand1(es, fmt);
+ RB_FOREACH(wl, winlinks, &ft->s->windows) {
+ if (strcmp(wl->window->name, name) == 0) {
+ free(name);
+ return (xstrdup("1"));
+ }
+ }
+ free(name);
+ return (xstrdup("0"));
+}
+
/* Loop over windows. */
static char *
format_loop_windows(struct format_expand_state *es, const char *fmt)
@@ -2184,6 +4088,9 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
if (errptr != NULL)
width = 0;
break;
+ case 'w':
+ modifiers |= FORMAT_WIDTH;
+ break;
case 'e':
if (fm->argc < 1 || fm->argc > 3)
break;
@@ -2212,7 +4119,11 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
time_format = format_strip(fm->argv[1]);
break;
case 'q':
- modifiers |= FORMAT_QUOTE;
+ if (fm->argc < 1)
+ modifiers |= FORMAT_QUOTE_SHELL;
+ else if (strchr(fm->argv[0], 'e') != NULL ||
+ strchr(fm->argv[0], 'h') != NULL)
+ modifiers |= FORMAT_QUOTE_STYLE;
break;
case 'E':
modifiers |= FORMAT_EXPAND;
@@ -2220,6 +4131,13 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
case 'T':
modifiers |= FORMAT_EXPANDTIME;
break;
+ case 'N':
+ if (fm->argc < 1 ||
+ strchr(fm->argv[0], 'w') != NULL)
+ modifiers |= FORMAT_WINDOW_NAME;
+ else if (strchr(fm->argv[0], 's') != NULL)
+ modifiers |= FORMAT_SESSION_NAME;
+ break;
case 'S':
modifiers |= FORMAT_SESSIONS;
break;
@@ -2260,6 +4178,14 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
value = format_loop_panes(es, copy);
if (value == NULL)
goto fail;
+ } else if (modifiers & FORMAT_WINDOW_NAME) {
+ value = format_window_name(es, copy);
+ if (value == NULL)
+ goto fail;
+ } else if (modifiers & FORMAT_SESSION_NAME) {
+ value = format_session_name(es, copy);
+ if (value == NULL)
+ goto fail;
} else if (search != NULL) {
/* Search in pane. */
new = format_expand1(es, copy);
@@ -2456,13 +4382,19 @@ done:
format_log(es, "applied padding width %d: %s", width, value);
}
- /* Replace with the length if needed. */
+ /* Replace with the length or width if needed. */
if (modifiers & FORMAT_LENGTH) {
xasprintf(&new, "%zu", strlen(value));
free(value);
value = new;
format_log(es, "replacing with length: %s", new);
}
+ if (modifiers & FORMAT_WIDTH) {
+ xasprintf(&new, "%u", format_width(value));
+ free(value);
+ value = new;
+ format_log(es, "replacing with width: %s", new);
+ }
/* Expand the buffer and copy in the value. */
valuelen = strlen(value);
@@ -2499,7 +4431,6 @@ format_expand1(struct format_expand_state *es, const char *fmt)
const char *ptr, *s;
size_t off, len, n, outlen;
int ch, brackets;
- struct tm *tm;
char expanded[8192];
if (fmt == NULL || *fmt == '\0')
@@ -2511,11 +4442,12 @@ format_expand1(struct format_expand_state *es, const char *fmt)
format_log(es, "expanding format: %s", fmt);
- if (es->flags & FORMAT_EXPAND_TIME) {
- if (es->time == 0)
+ if ((es->flags & FORMAT_EXPAND_TIME) && strchr(fmt, '%') != NULL) {
+ if (es->time == 0) {
es->time = time(NULL);
- tm = localtime(&es->time);
- if (strftime(expanded, sizeof expanded, fmt, tm) == 0) {
+ localtime_r(&es->time, &es->tm);
+ }
+ if (strftime(expanded, sizeof expanded, fmt, &es->tm) == 0) {
format_log(es, "format is too long");
return (xstrdup(""));
}
@@ -2589,8 +4521,30 @@ format_expand1(struct format_expand_state *es, const char *fmt)
break;
fmt += n + 1;
continue;
- case '}':
case '#':
+ /*
+ * If ##[ (with two or more #s), then it is a style and
+ * can be left for format_draw to handle.
+ */
+ ptr = fmt;
+ n = 2;
+ while (*ptr == '#') {
+ ptr++;
+ n++;
+ }
+ if (*ptr == '[') {
+ format_log(es, "found #*%zu[", n);
+ while (len - off < n + 2) {
+ buf = xreallocarray(buf, 2, len);
+ len *= 2;
+ }
+ memcpy(buf + off, fmt - 2, n + 1);
+ off += n + 1;
+ fmt = ptr + 1;
+ continue;
+ }
+ /* FALLTHROUGH */
+ case '}':
case ',':
format_log(es, "found #%c", ch);
while (len - off < 2) {
@@ -2745,9 +4699,14 @@ format_defaults(struct format_tree *ft, struct client *c, struct session *s,
if (c != NULL && s != NULL && c->session != s)
log_debug("%s: session does not match", __func__);
- format_add(ft, "session_format", "%d", s != NULL);
- format_add(ft, "window_format", "%d", wl != NULL);
- format_add(ft, "pane_format", "%d", wp != NULL);
+ if (wp != NULL)
+ ft->type = FORMAT_TYPE_PANE;
+ else if (wl != NULL)
+ ft->type = FORMAT_TYPE_WINDOW;
+ else if (s != NULL)
+ ft->type = FORMAT_TYPE_SESSION;
+ else
+ ft->type = FORMAT_TYPE_UNKNOWN;
if (s == NULL && c != NULL)
s = c->session;
@@ -2774,106 +4733,16 @@ format_defaults(struct format_tree *ft, struct client *c, struct session *s,
static void
format_defaults_session(struct format_tree *ft, struct session *s)
{
- struct session_group *sg;
-
ft->s = s;
-
- format_add(ft, "session_name", "%s", s->name);
- format_add(ft, "session_path", "%s", s->cwd);
- format_add(ft, "session_windows", "%u", winlink_count(&s->windows));
- format_add(ft, "session_id", "$%u", s->id);
-
- sg = session_group_contains(s);
- format_add(ft, "session_grouped", "%d", sg != NULL);
- if (sg != NULL) {
- format_add(ft, "session_group", "%s", sg->name);
- format_add(ft, "session_group_size", "%u",
- session_group_count (sg));
- format_add(ft, "session_group_attached", "%u",
- session_group_attached_count (sg));
- format_add(ft, "session_group_many_attached", "%u",
- session_group_attached_count (sg) > 1);
- format_add_cb(ft, "session_group_list",
- format_cb_session_group_list);
- format_add_cb(ft, "session_group_attached_list",
- format_cb_session_group_attached_list);
- }
-
- format_add_tv(ft, "session_created", &s->creation_time);
- format_add_tv(ft, "session_last_attached", &s->last_attached_time);
- format_add_tv(ft, "session_activity", &s->activity_time);
-
- format_add(ft, "session_attached", "%u", s->attached);
- format_add(ft, "session_many_attached", "%d", s->attached > 1);
- format_add_cb(ft, "session_attached_list",
- format_cb_session_attached_list);
-
- format_add_cb(ft, "session_alerts", format_cb_session_alerts);
- format_add_cb(ft, "session_stack", format_cb_session_stack);
-
- if (server_check_marked() && marked_pane.s == s)
- format_add(ft, "session_marked", "1");
- else
- format_add(ft, "session_marked", "0");
}
/* Set default format keys for a client. */
static void
format_defaults_client(struct format_tree *ft, struct client *c)
{
- struct session *s;
- const char *name;
- struct tty *tty = &c->tty;
-
if (ft->s == NULL)
ft->s = c->session;
ft->c = c;
-
- format_add(ft, "client_name", "%s", c->name);
- format_add(ft, "client_pid", "%ld", (long) c->pid);
- format_add(ft, "client_height", "%u", tty->sy);
- format_add(ft, "client_width", "%u", tty->sx);
- format_add(ft, "client_cell_width", "%u", tty->xpixel);
- format_add(ft, "client_cell_height", "%u", tty->ypixel);
- format_add(ft, "client_tty", "%s", c->ttyname);
- format_add(ft, "client_control_mode", "%d",
- !!(c->flags & CLIENT_CONTROL));
-
- format_add(ft, "client_termname", "%s", c->term_name);
- format_add(ft, "client_termfeatures", "%s",
- tty_get_features(c->term_features));
- if (c->term_type != NULL)
- format_add(ft, "client_termtype", "%s", c->term_type);
-
- format_add_tv(ft, "client_created", &c->creation_time);
- format_add_tv(ft, "client_activity", &c->activity_time);
-
- format_add(ft, "client_written", "%zu", c->written);
- format_add(ft, "client_discarded", "%zu", c->discarded);
-
- name = server_client_get_key_table(c);
- if (strcmp(c->keytable->name, name) == 0)
- format_add(ft, "client_prefix", "%d", 0);
- else
- format_add(ft, "client_prefix", "%d", 1);
- format_add(ft, "client_key_table", "%s", c->keytable->name);
-
- if (c->flags & CLIENT_UTF8)
- format_add(ft, "client_utf8", "%d", 1);
- else
- format_add(ft, "client_utf8", "%d", 0);
- if (c->flags & CLIENT_READONLY)
- format_add(ft, "client_readonly", "%d", 1);
- else
- format_add(ft, "client_readonly", "%d", 0);
- format_add(ft, "client_flags", "%s", server_client_get_flags(c));
-
- s = c->session;
- if (s != NULL)
- format_add(ft, "client_session", "%s", s->name);
- s = c->last_session;
- if (s != NULL && session_alive(s))
- format_add(ft, "client_last_session", "%s", s->name);
}
/* Set default format keys for a window. */
@@ -2881,221 +4750,131 @@ void
format_defaults_window(struct format_tree *ft, struct window *w)
{
ft->w = w;
-
- format_add_tv(ft, "window_activity", &w->activity_time);
- format_add(ft, "window_id", "@%u", w->id);
- format_add(ft, "window_name", "%s", w->name);
- format_add(ft, "window_width", "%u", w->sx);
- format_add(ft, "window_height", "%u", w->sy);
- format_add(ft, "window_cell_width", "%u", w->xpixel);
- format_add(ft, "window_cell_height", "%u", w->ypixel);
- format_add_cb(ft, "window_layout", format_cb_window_layout);
- format_add_cb(ft, "window_visible_layout",
- format_cb_window_visible_layout);
- format_add(ft, "window_panes", "%u", window_count_panes(w));
- format_add(ft, "window_zoomed_flag", "%d",
- !!(w->flags & WINDOW_ZOOMED));
}
/* Set default format keys for a winlink. */
static void
format_defaults_winlink(struct format_tree *ft, struct winlink *wl)
{
- struct client *c = ft->c;
- struct session *s = wl->session;
- struct window *w = wl->window;
- int flag;
- u_int ox, oy, sx, sy;
-
if (ft->w == NULL)
- format_defaults_window(ft, w);
+ format_defaults_window(ft, wl->window);
ft->wl = wl;
-
- if (c != NULL) {
- flag = tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
- format_add(ft, "window_bigger", "%d", flag);
- if (flag) {
- format_add(ft, "window_offset_x", "%u", ox);
- format_add(ft, "window_offset_y", "%u", oy);
- }
- }
-
- format_add(ft, "window_index", "%d", wl->idx);
- format_add_cb(ft, "window_stack_index", format_cb_window_stack_index);
- format_add(ft, "window_flags", "%s", window_printable_flags(wl));
- format_add(ft, "window_active", "%d", wl == s->curw);
- format_add_cb(ft, "window_active_sessions",
- format_cb_window_active_sessions);
- format_add_cb(ft, "window_active_sessions_list",
- format_cb_window_active_sessions_list);
- format_add_cb(ft, "window_active_clients",
- format_cb_window_active_clients);
- format_add_cb(ft, "window_active_clients_list",
- format_cb_window_active_clients_list);
-
- format_add(ft, "window_start_flag", "%d",
- !!(wl == RB_MIN(winlinks, &s->windows)));
- format_add(ft, "window_end_flag", "%d",
- !!(wl == RB_MAX(winlinks, &s->windows)));
-
- if (server_check_marked() && marked_pane.wl == wl)
- format_add(ft, "window_marked_flag", "1");
- else
- format_add(ft, "window_marked_flag", "0");
-
- format_add(ft, "window_bell_flag", "%d",
- !!(wl->flags & WINLINK_BELL));
- format_add(ft, "window_activity_flag", "%d",
- !!(wl->flags & WINLINK_ACTIVITY));
- format_add(ft, "window_silence_flag", "%d",
- !!(wl->flags & WINLINK_SILENCE));
- format_add(ft, "window_last_flag", "%d",
- !!(wl == TAILQ_FIRST(&s->lastw)));
- format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window));
-
- format_add_cb(ft, "window_linked_sessions_list",
- format_cb_window_linked_sessions_list);
- format_add(ft, "window_linked_sessions", "%u",
- wl->window->references);
}
/* Set default format keys for a window pane. */
void
format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
{
- struct window *w = wp->window;
- struct grid *gd = wp->base.grid;
- int status = wp->status;
- u_int idx;
struct window_mode_entry *wme;
if (ft->w == NULL)
- format_defaults_window(ft, w);
+ format_defaults_window(ft, wp->window);
ft->wp = wp;
- format_add(ft, "history_size", "%u", gd->hsize);
- format_add(ft, "history_limit", "%u", gd->hlimit);
- format_add_cb(ft, "history_bytes", format_cb_history_bytes);
- format_add_cb(ft, "history_all_bytes", format_cb_history_all_bytes);
-
- format_add(ft, "pane_written", "%zu", wp->written);
- format_add(ft, "pane_skipped", "%zu", wp->skipped);
-
- if (window_pane_index(wp, &idx) != 0)
- fatalx("index not found");
- format_add(ft, "pane_index", "%u", idx);
-
- format_add(ft, "pane_width", "%u", wp->sx);
- format_add(ft, "pane_height", "%u", wp->sy);
- format_add(ft, "pane_title", "%s", wp->base.title);
- if (wp->base.path != NULL)
- format_add(ft, "pane_path", "%s", wp->base.path);
- format_add(ft, "pane_id", "%%%u", wp->id);
- format_add(ft, "pane_active", "%d", wp == w->active);
- format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF));
- format_add(ft, "pane_pipe", "%d", wp->pipe_fd != -1);
-
- if ((wp->flags & PANE_STATUSREADY) && WIFEXITED(status))
- format_add(ft, "pane_dead_status", "%d", WEXITSTATUS(status));
- if (~wp->flags & PANE_EMPTY)
- 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");
- else
- format_add(ft, "pane_marked", "0");
- format_add(ft, "pane_marked_set", "%d", server_check_marked());
-
- format_add(ft, "pane_left", "%u", wp->xoff);
- format_add(ft, "pane_top", "%u", wp->yoff);
- format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1);
- format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1);
- format_add(ft, "pane_at_left", "%d", wp->xoff == 0);
- format_add_cb(ft, "pane_at_top", format_cb_pane_at_top);
- format_add(ft, "pane_at_right", "%d", wp->xoff + wp->sx == w->sx);
- format_add_cb(ft, "pane_at_bottom", format_cb_pane_at_bottom);
-
wme = TAILQ_FIRST(&wp->modes);
- if (wme != NULL) {
- format_add(ft, "pane_mode", "%s", wme->mode->name);
- if (wme->mode->formats != NULL)
- wme->mode->formats(wme, ft);
- }
- format_add_cb(ft, "pane_in_mode", format_cb_pane_in_mode);
-
- format_add(ft, "pane_synchronized", "%d",
- !!options_get_number(w->options, "synchronize-panes"));
- if (wp->searchstr != NULL)
- format_add(ft, "pane_search_string", "%s", wp->searchstr);
-
- format_add(ft, "pane_tty", "%s", wp->tty);
- format_add(ft, "pane_pid", "%ld", (long) wp->pid);
- format_add_cb(ft, "pane_start_command", format_cb_start_command);
- format_add_cb(ft, "pane_current_command", format_cb_current_command);
- format_add_cb(ft, "pane_current_path", format_cb_current_path);
-
- format_add(ft, "cursor_x", "%u", wp->base.cx);
- format_add(ft, "cursor_y", "%u", wp->base.cy);
- format_add_cb(ft, "cursor_character", format_cb_cursor_character);
-
- format_add(ft, "scroll_region_upper", "%u", wp->base.rupper);
- format_add(ft, "scroll_region_lower", "%u", wp->base.rlower);
-
- format_add(ft, "alternate_on", "%d", wp->base.saved_grid != NULL);
- if (wp->base.saved_cx != UINT_MAX)
- format_add(ft, "alternate_saved_x", "%u", wp->base.saved_cx);
- if (wp->base.saved_cy != UINT_MAX)
- format_add(ft, "alternate_saved_y", "%u", wp->base.saved_cy);
-
- format_add(ft, "cursor_flag", "%d",
- !!(wp->base.mode & MODE_CURSOR));
- format_add(ft, "insert_flag", "%d",
- !!(wp->base.mode & MODE_INSERT));
- format_add(ft, "keypad_cursor_flag", "%d",
- !!(wp->base.mode & MODE_KCURSOR));
- format_add(ft, "keypad_flag", "%d",
- !!(wp->base.mode & MODE_KKEYPAD));
- format_add(ft, "wrap_flag", "%d",
- !!(wp->base.mode & MODE_WRAP));
- format_add(ft, "origin_flag", "%d",
- !!(wp->base.mode & MODE_ORIGIN));
-
- format_add(ft, "mouse_any_flag", "%d",
- !!(wp->base.mode & ALL_MOUSE_MODES));
- format_add(ft, "mouse_standard_flag", "%d",
- !!(wp->base.mode & MODE_MOUSE_STANDARD));
- format_add(ft, "mouse_button_flag", "%d",
- !!(wp->base.mode & MODE_MOUSE_BUTTON));
- format_add(ft, "mouse_all_flag", "%d",
- !!(wp->base.mode & MODE_MOUSE_ALL));
- format_add(ft, "mouse_utf8_flag", "%d",
- !!(wp->base.mode & MODE_MOUSE_UTF8));
- format_add(ft, "mouse_sgr_flag", "%d",
- !!(wp->base.mode & MODE_MOUSE_SGR));
-
- format_add_cb(ft, "pane_tabs", format_cb_pane_tabs);
+ if (wme != NULL && wme->mode->formats != NULL)
+ wme->mode->formats(wme, ft);
}
/* Set default format keys for paste buffer. */
void
format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb)
{
- struct timeval tv;
- size_t size;
- char *s;
+ ft->pb = pb;
+}
+
+/* Return word at given coordinates. Caller frees. */
+char *
+format_grid_word(struct grid *gd, u_int x, u_int y)
+{
+ const struct grid_line *gl;
+ struct grid_cell gc;
+ const char *ws;
+ struct utf8_data *ud = NULL;
+ u_int end;
+ size_t size = 0;
+ int found = 0;
+ char *s = NULL;
- timerclear(&tv);
- tv.tv_sec = paste_buffer_created(pb);
- paste_buffer_data(pb, &size);
+ ws = options_get_string(global_s_options, "word-separators");
- format_add(ft, "buffer_size", "%zu", size);
- format_add(ft, "buffer_name", "%s", paste_buffer_name(pb));
- format_add_tv(ft, "buffer_created", &tv);
+ for (;;) {
+ grid_get_cell(gd, x, y, &gc);
+ if (gc.flags & GRID_FLAG_PADDING)
+ break;
+ if (utf8_cstrhas(ws, &gc.data)) {
+ found = 1;
+ break;
+ }
- s = paste_make_sample(pb);
- format_add(ft, "buffer_sample", "%s", s);
- free(s);
+ if (x == 0) {
+ if (y == 0)
+ break;
+ gl = grid_peek_line(gd, y - 1);
+ if (~gl->flags & GRID_LINE_WRAPPED)
+ break;
+ y--;
+ x = grid_line_length(gd, y);
+ if (x == 0)
+ break;
+ }
+ x--;
+ }
+ for (;;) {
+ if (found) {
+ end = grid_line_length(gd, y);
+ if (end == 0 || x == end - 1) {
+ if (y == gd->hsize + gd->sy - 1)
+ break;
+ gl = grid_peek_line(gd, y);
+ if (~gl->flags & GRID_LINE_WRAPPED)
+ break;
+ y++;
+ x = 0;
+ } else
+ x++;
+ }
+ found = 1;
+
+ grid_get_cell(gd, x, y, &gc);
+ if (gc.flags & GRID_FLAG_PADDING)
+ break;
+ if (utf8_cstrhas(ws, &gc.data))
+ break;
+
+ ud = xreallocarray(ud, size + 2, sizeof *ud);
+ memcpy(&ud[size++], &gc.data, sizeof *ud);
+ }
+ if (size != 0) {
+ ud[size].size = 0;
+ s = utf8_tocstr(ud);
+ free(ud);
+ }
+ return (s);
+}
+
+/* Return line at given coordinates. Caller frees. */
+char *
+format_grid_line(struct grid *gd, u_int y)
+{
+ struct grid_cell gc;
+ struct utf8_data *ud = NULL;
+ u_int x;
+ size_t size = 0;
+ char *s = NULL;
+
+ for (x = 0; x < grid_line_length(gd, y); x++) {
+ grid_get_cell(gd, x, y, &gc);
+ if (gc.flags & GRID_FLAG_PADDING)
+ break;
+
+ ud = xreallocarray(ud, size + 2, sizeof *ud);
+ memcpy(&ud[size++], &gc.data, sizeof *ud);
+ }
+ if (size != 0) {
+ ud[size].size = 0;
+ s = utf8_tocstr(ud);
+ free(ud);
+ }
+ return (s);
}
diff --git a/fuzz/input-fuzzer.c b/fuzz/input-fuzzer.c
new file mode 100644
index 00000000..27f2be3d
--- /dev/null
+++ b/fuzz/input-fuzzer.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2020 Sergey Nizovtsev <snizovtsev@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 <stddef.h>
+#include <assert.h>
+
+#include "tmux.h"
+
+#define FUZZER_MAXLEN 512
+#define PANE_WIDTH 80
+#define PANE_HEIGHT 25
+
+struct event_base *libevent;
+
+int
+LLVMFuzzerTestOneInput(const unsigned char *data, size_t size)
+{
+ struct bufferevent *vpty[2];
+ struct window *w;
+ struct window_pane *wp;
+ int error;
+
+ /*
+ * Since AFL doesn't support -max_len paramenter we have to
+ * discard long inputs manually.
+ */
+ if (size > FUZZER_MAXLEN)
+ return 0;
+
+ w = window_create(PANE_WIDTH, PANE_HEIGHT, 0, 0);
+ wp = window_add_pane(w, NULL, 0, 0);
+ bufferevent_pair_new(libevent, BEV_OPT_CLOSE_ON_FREE, vpty);
+ wp->ictx = input_init(wp, vpty[0]);
+ window_add_ref(w, __func__);
+
+ input_parse_buffer(wp, (u_char*) data, size);
+ while (cmdq_next(NULL) != 0)
+ ;
+ error = event_base_loop(libevent, EVLOOP_NONBLOCK);
+ if (error == -1)
+ errx(1, "event_base_loop failed");
+
+ assert(w->references == 1);
+ window_remove_ref(w, __func__);
+
+ bufferevent_free(vpty[0]);
+ bufferevent_free(vpty[1]);
+
+ return 0;
+}
+
+int
+LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv)
+{
+ const struct options_table_entry *oe;
+
+ global_environ = environ_create();
+ global_options = options_create(NULL);
+ global_s_options = options_create(NULL);
+ global_w_options = options_create(NULL);
+ for (oe = options_table; oe->name != NULL; oe++) {
+ if (oe->scope & OPTIONS_TABLE_SERVER)
+ options_default(global_options, oe);
+ if (oe->scope & OPTIONS_TABLE_SESSION)
+ options_default(global_s_options, oe);
+ if (oe->scope & OPTIONS_TABLE_WINDOW)
+ options_default(global_w_options, oe);
+ }
+ libevent = osdep_event_init();
+
+ options_set_number(global_w_options, "monitor-bell", 0);
+ options_set_number(global_w_options, "allow-rename", 1);
+ options_set_number(global_options, "set-clipboard", 2);
+
+ return 0;
+}
diff --git a/fuzz/input-fuzzer.dict b/fuzz/input-fuzzer.dict
new file mode 100644
index 00000000..2091b970
--- /dev/null
+++ b/fuzz/input-fuzzer.dict
@@ -0,0 +1,8 @@
+"\x1b["
+"1000"
+"2004"
+"1049"
+"38;2"
+"100;"
+"tmux;"
+"rgb:00/00/00"
diff --git a/fuzz/input-fuzzer.options b/fuzz/input-fuzzer.options
new file mode 100644
index 00000000..5d468bc6
--- /dev/null
+++ b/fuzz/input-fuzzer.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 512
diff --git a/grid-reader.c b/grid-reader.c
new file mode 100644
index 00000000..c011ea1d
--- /dev/null
+++ b/grid-reader.c
@@ -0,0 +1,365 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.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 "tmux.h"
+#include <string.h>
+
+/* Initialise virtual cursor. */
+void
+grid_reader_start(struct grid_reader *gr, struct grid *gd, u_int cx, u_int cy)
+{
+ gr->gd = gd;
+ gr->cx = cx;
+ gr->cy = cy;
+}
+
+/* Get cursor position from reader. */
+void
+grid_reader_get_cursor(struct grid_reader *gr, u_int *cx, u_int *cy)
+{
+ *cx = gr->cx;
+ *cy = gr->cy;
+}
+
+/* Get length of line containing the cursor. */
+u_int
+grid_reader_line_length(struct grid_reader *gr)
+{
+ return (grid_line_length(gr->gd, gr->cy));
+}
+
+/* Move cursor forward one position. */
+void
+grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all)
+{
+ u_int px;
+ struct grid_cell gc;
+
+ if (all)
+ px = gr->gd->sx;
+ else
+ px = grid_reader_line_length(gr);
+
+ if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) {
+ grid_reader_cursor_start_of_line(gr, 0);
+ grid_reader_cursor_down(gr);
+ } else if (gr->cx < px) {
+ gr->cx++;
+ while (gr->cx < px) {
+ grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
+ if (~gc.flags & GRID_FLAG_PADDING)
+ break;
+ gr->cx++;
+ }
+ }
+}
+
+/* Move cursor back one position. */
+void
+grid_reader_cursor_left(struct grid_reader *gr)
+{
+ struct grid_cell gc;
+
+ while (gr->cx > 0) {
+ grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
+ if (~gc.flags & GRID_FLAG_PADDING)
+ break;
+ gr->cx--;
+ }
+ if (gr->cx == 0 && gr->cy > 0) {
+ grid_reader_cursor_up(gr);
+ grid_reader_cursor_end_of_line(gr, 0, 0);
+ } else if (gr->cx > 0)
+ gr->cx--;
+}
+
+/* Move cursor down one line. */
+void
+grid_reader_cursor_down(struct grid_reader *gr)
+{
+ struct grid_cell gc;
+
+ if (gr->cy < gr->gd->hsize + gr->gd->sy - 1)
+ gr->cy++;
+ while (gr->cx > 0) {
+ grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
+ if (~gc.flags & GRID_FLAG_PADDING)
+ break;
+ gr->cx--;
+ }
+}
+
+/* Move cursor up one line. */
+void
+grid_reader_cursor_up(struct grid_reader *gr)
+{
+ struct grid_cell gc;
+
+ if (gr->cy > 0)
+ gr->cy--;
+ while (gr->cx > 0) {
+ grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
+ if (~gc.flags & GRID_FLAG_PADDING)
+ break;
+ gr->cx--;
+ }
+}
+
+/* Move cursor to the start of the line. */
+void
+grid_reader_cursor_start_of_line(struct grid_reader *gr, int wrap)
+{
+ if (wrap) {
+ while (gr->cy > 0 &&
+ grid_get_line(gr->gd, gr->cy - 1)->flags &
+ GRID_LINE_WRAPPED)
+ gr->cy--;
+ }
+ gr->cx = 0;
+}
+
+/* Move cursor to the end of the line. */
+void
+grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all)
+{
+ u_int yy;
+
+ if (wrap) {
+ yy = gr->gd->hsize + gr->gd->sy - 1;
+ while (gr->cy < yy && grid_get_line(gr->gd, gr->cy)->flags &
+ GRID_LINE_WRAPPED)
+ gr->cy++;
+ }
+ if (all)
+ gr->cx = gr->gd->sx;
+ else
+ gr->cx = grid_reader_line_length(gr);
+}
+
+/* Check if character under cursor is in set. */
+int
+grid_reader_in_set(struct grid_reader *gr, const char *set)
+{
+ struct grid_cell gc;
+
+ grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
+ if (gc.flags & GRID_FLAG_PADDING)
+ return (0);
+ return (utf8_cstrhas(set, &gc.data));
+}
+
+/* Move cursor to the start of the next word. */
+void
+grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators)
+{
+ u_int xx, yy;
+ int expected = 0;
+
+ /* Do not break up wrapped words. */
+ if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
+ xx = grid_reader_line_length(gr) - 1;
+ else
+ xx = grid_reader_line_length(gr);
+ yy = gr->gd->hsize + gr->gd->sy - 1;
+
+ /*
+ * If we started inside a word, skip over word characters. Then skip
+ * over separators till the next word.
+ *
+ * expected is initially set to 0 for the former and then 1 for the
+ * latter. It is finally set to 0 when the beginning of the next word is
+ * found.
+ */
+ do {
+ while (gr->cx > xx ||
+ grid_reader_in_set(gr, separators) == expected) {
+ /* Move down if we are past the end of the line. */
+ if (gr->cx > xx) {
+ if (gr->cy == yy)
+ return;
+ grid_reader_cursor_start_of_line(gr, 0);
+ grid_reader_cursor_down(gr);
+
+ if (grid_get_line(gr->gd, gr->cy)->flags &
+ GRID_LINE_WRAPPED)
+ xx = grid_reader_line_length(gr) - 1;
+ else
+ xx = grid_reader_line_length(gr);
+ } else
+ gr->cx++;
+ }
+ expected = !expected;
+ } while (expected == 1);
+}
+
+/* Move cursor to the end of the next word. */
+void
+grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators)
+{
+ u_int xx, yy;
+ int expected = 1;
+
+ /* Do not break up wrapped words. */
+ if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
+ xx = grid_reader_line_length(gr) - 1;
+ else
+ xx = grid_reader_line_length(gr);
+ yy = gr->gd->hsize + gr->gd->sy - 1;
+
+ /*
+ * If we started on a separator, skip over separators. Then skip over
+ * word characters till the next separator.
+ *
+ * expected is initially set to 1 for the former and then 1 for the
+ * latter. It is finally set to 1 when the end of the next word is
+ * found.
+ */
+ do {
+ while (gr->cx > xx ||
+ grid_reader_in_set(gr, separators) == expected) {
+ /* Move down if we are past the end of the line. */
+ if (gr->cx > xx) {
+ if (gr->cy == yy)
+ return;
+ grid_reader_cursor_start_of_line(gr, 0);
+ grid_reader_cursor_down(gr);
+
+ if (grid_get_line(gr->gd, gr->cy)->flags &
+ GRID_LINE_WRAPPED)
+ xx = grid_reader_line_length(gr) - 1;
+ else
+ xx = grid_reader_line_length(gr);
+ } else
+ gr->cx++;
+ }
+ expected = !expected;
+ } while (expected == 0);
+}
+
+/* Move to the previous place where a word begins. */
+void
+grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators,
+ int already)
+{
+ int oldx, oldy, r;
+
+ /* Move back to the previous word character. */
+ if (already || grid_reader_in_set(gr, separators)) {
+ for (;;) {
+ if (gr->cx > 0) {
+ gr->cx--;
+ if (!grid_reader_in_set(gr, separators))
+ break;
+ } else {
+ if (gr->cy == 0)
+ return;
+ grid_reader_cursor_up(gr);
+ grid_reader_cursor_end_of_line(gr, 0, 0);
+
+ /* Stop if separator at EOL. */
+ if (gr->cx > 0) {
+ oldx = gr->cx;
+ gr->cx--;
+ r = grid_reader_in_set(gr, separators);
+ gr->cx = oldx;
+ if (r)
+ break;
+ }
+ }
+ }
+ }
+
+ /* Move back to the beginning of this word. */
+ do {
+ oldx = gr->cx;
+ oldy = gr->cy;
+ if (gr->cx == 0) {
+ if (gr->cy == 0 ||
+ ~grid_get_line(gr->gd, gr->cy - 1)->flags &
+ GRID_LINE_WRAPPED)
+ break;
+ grid_reader_cursor_up(gr);
+ grid_reader_cursor_end_of_line(gr, 0, 0);
+ }
+ if (gr->cx > 0)
+ gr->cx--;
+ } while (!grid_reader_in_set(gr, separators));
+ gr->cx = oldx;
+ gr->cy = oldy;
+}
+
+/* Jump forward to character. */
+int
+grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc)
+{
+ struct grid_cell gc;
+ u_int px, py, xx, yy;
+
+ px = gr->cx;
+ yy = gr->gd->hsize + gr->gd->sy - 1;
+
+ for (py = gr->cy; py <= yy; py++) {
+ xx = grid_line_length(gr->gd, py);
+ while (px < xx) {
+ grid_get_cell(gr->gd, px, py, &gc);
+ if (!(gc.flags & GRID_FLAG_PADDING) &&
+ gc.data.size == jc->size &&
+ memcmp(gc.data.data, jc->data, gc.data.size) == 0) {
+ gr->cx = px;
+ gr->cy = py;
+ return 1;
+ }
+ px++;
+ }
+
+ if (py == yy ||
+ !(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED))
+ return 0;
+ px = 0;
+ }
+ return 0;
+}
+
+/* Jump back to character. */
+int
+grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc)
+{
+ struct grid_cell gc;
+ u_int px, py, xx;
+
+ xx = gr->cx + 1;
+
+ for (py = gr->cy + 1; py > 0; py--) {
+ for (px = xx; px > 0; px--) {
+ grid_get_cell(gr->gd, px - 1, py - 1, &gc);
+ if (!(gc.flags & GRID_FLAG_PADDING) &&
+ gc.data.size == jc->size &&
+ memcmp(gc.data.data, jc->data, gc.data.size) == 0) {
+ gr->cx = px - 1;
+ gr->cy = py - 1;
+ return 1;
+ }
+ }
+
+ if (py == 1 ||
+ !(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED))
+ return 0;
+ xx = grid_line_length(gr->gd, py - 2);
+ }
+ return 0;
+}
diff --git a/grid.c b/grid.c
index 90e1a0a5..1822f2b5 100644
--- a/grid.c
+++ b/grid.c
@@ -701,7 +701,6 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
}
-
/* Move a group of cells. */
void
grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
@@ -1049,14 +1048,14 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
srcl->cellsize * sizeof *dstl->celldata);
} else
dstl->celldata = NULL;
-
if (srcl->extdsize != 0) {
dstl->extdsize = srcl->extdsize;
dstl->extddata = xreallocarray(NULL, dstl->extdsize,
sizeof *dstl->extddata);
memcpy(dstl->extddata, srcl->extddata, dstl->extdsize *
sizeof *dstl->extddata);
- }
+ } else
+ dstl->extddata = NULL;
sy++;
dy++;
diff --git a/input.c b/input.c
index 8b7ba08a..f6aeb027 100644
--- a/input.c
+++ b/input.c
@@ -138,6 +138,8 @@ static void input_osc_10(struct input_ctx *, const char *);
static void input_osc_11(struct input_ctx *, const char *);
static void input_osc_52(struct input_ctx *, const char *);
static void input_osc_104(struct input_ctx *, const char *);
+static void input_osc_110(struct input_ctx *, const char *);
+static void input_osc_111(struct input_ctx *, const char *);
/* Transition entry/exit handlers. */
static void input_clear(struct input_ctx *);
@@ -2099,6 +2101,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
gc->attr |= GRID_ATTR_UNDERSCORE;
break;
case 5:
+ case 6:
gc->attr |= GRID_ATTR_BLINK;
break;
case 7:
@@ -2110,6 +2113,10 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
case 9:
gc->attr |= GRID_ATTR_STRIKETHROUGH;
break;
+ case 21:
+ gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE;
+ gc->attr |= GRID_ATTR_UNDERSCORE_2;
+ break;
case 22:
gc->attr &= ~(GRID_ATTR_BRIGHT|GRID_ATTR_DIM);
break;
@@ -2304,6 +2311,12 @@ input_exit_osc(struct input_ctx *ictx)
case 104:
input_osc_104(ictx, p);
break;
+ case 110:
+ input_osc_110(ictx, p);
+ break;
+ case 111:
+ input_osc_111(ictx, p);
+ break;
case 112:
if (*p == '\0') /* no arguments allowed */
screen_set_cursor_colour(sctx->s, "");
@@ -2422,50 +2435,43 @@ input_top_bit_set(struct input_ctx *ictx)
/* Parse colour from OSC. */
static int
-input_osc_parse_colour(const char *p, u_int *r, u_int *g, u_int *b)
+input_osc_parse_colour(const char *p)
{
- u_int rsize, gsize, bsize;
- const char *cp, *s = p;
-
- if (sscanf(p, "rgb:%x/%x/%x", r, g, b) != 3)
- return (0);
- p += 4;
-
- cp = strchr(p, '/');
- rsize = cp - p;
- if (rsize == 1)
- (*r) = (*r) | ((*r) << 4);
- else if (rsize == 3)
- (*r) >>= 4;
- else if (rsize == 4)
- (*r) >>= 8;
- else if (rsize != 2)
- return (0);
-
- p = cp + 1;
- cp = strchr(p, '/');
- gsize = cp - p;
- if (gsize == 1)
- (*g) = (*g) | ((*g) << 4);
- else if (gsize == 3)
- (*g) >>= 4;
- else if (gsize == 4)
- (*g) >>= 8;
- else if (gsize != 2)
- return (0);
-
- bsize = strlen(cp + 1);
- if (bsize == 1)
- (*b) = (*b) | ((*b) << 4);
- else if (bsize == 3)
- (*b) >>= 4;
- else if (bsize == 4)
- (*b) >>= 8;
- else if (bsize != 2)
- return (0);
-
- log_debug("%s: %s = %02x%02x%02x", __func__, s, *r, *g, *b);
- return (1);
+ double c, m, y, k = 0;
+ u_int r, g, b;
+ size_t len = strlen(p);
+ int colour = -1;
+ char *copy;
+
+ if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) ||
+ (len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) ||
+ sscanf(p, "%d,%d,%d", &r, &g, &b) == 3)
+ colour = colour_join_rgb(r, g, b);
+ else if ((len == 18 &&
+ sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) ||
+ (len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3))
+ colour = colour_join_rgb(r >> 8, g >> 8, b >> 8);
+ else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 ||
+ sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) &&
+ c >= 0 && c <= 1 && m >= 0 && m <= 1 &&
+ y >= 0 && y <= 1 && k >= 0 && k <= 1) {
+ colour = colour_join_rgb(
+ (1 - c) * (1 - k) * 255,
+ (1 - m) * (1 - k) * 255,
+ (1 - y) * (1 - k) * 255);
+ } else {
+ while (len != 0 && *p == ' ') {
+ p++;
+ len--;
+ }
+ while (len != 0 && p[len - 1] == ' ')
+ len--;
+ copy = xstrndup(p, len);
+ colour = colour_byname(copy);
+ free(copy);
+ }
+ log_debug("%s: %s = %s", __func__, p, colour_tostring(colour));
+ return (colour);
}
/* Reply to a colour request. */
@@ -2493,7 +2499,7 @@ input_osc_4(struct input_ctx *ictx, const char *p)
struct window_pane *wp = ictx->wp;
char *copy, *s, *next = NULL;
long idx;
- u_int r, g, b;
+ int c;
if (wp == NULL)
return;
@@ -2507,12 +2513,12 @@ input_osc_4(struct input_ctx *ictx, const char *p)
goto bad;
s = strsep(&next, ";");
- if (!input_osc_parse_colour(s, &r, &g, &b)) {
+ if ((c = input_osc_parse_colour(s)) == -1) {
s = next;
continue;
}
- window_pane_set_palette(wp, idx, colour_join_rgb(r, g, b));
+ window_pane_set_palette(wp, idx, c);
s = next;
}
@@ -2530,7 +2536,7 @@ 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;
+ int c;
if (wp == NULL)
return;
@@ -2541,9 +2547,9 @@ input_osc_10(struct input_ctx *ictx, const char *p)
return;
}
- if (!input_osc_parse_colour(p, &r, &g, &b))
+ if ((c = input_osc_parse_colour(p)) == -1)
goto bad;
- wp->fg = colour_join_rgb(r, g, b);
+ wp->fg = c;
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
return;
@@ -2552,13 +2558,29 @@ bad:
log_debug("bad OSC 10: %s", p);
}
+/* Handle the OSC 110 sequence for resetting background colour. */
+static void
+input_osc_110(struct input_ctx *ictx, const char *p)
+{
+ struct window_pane *wp = ictx->wp;
+
+ if (wp == NULL)
+ return;
+
+ if (*p != '\0')
+ return;
+
+ wp->fg = 8;
+ wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
+}
+
/* 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;
+ int c;
if (wp == NULL)
return;
@@ -2569,9 +2591,9 @@ input_osc_11(struct input_ctx *ictx, const char *p)
return;
}
- if (!input_osc_parse_colour(p, &r, &g, &b))
- goto bad;
- wp->bg = colour_join_rgb(r, g, b);
+ if ((c = input_osc_parse_colour(p)) == -1)
+ goto bad;
+ wp->bg = c;
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
return;
@@ -2580,6 +2602,22 @@ bad:
log_debug("bad OSC 11: %s", p);
}
+/* Handle the OSC 111 sequence for resetting background colour. */
+static void
+input_osc_111(struct input_ctx *ictx, const char *p)
+{
+ struct window_pane *wp = ictx->wp;
+
+ if (wp == NULL)
+ return;
+
+ if (*p != '\0')
+ return;
+
+ wp->bg = 8;
+ wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
+}
+
/* Handle the OSC 52 sequence for setting the clipboard. */
static void
input_osc_52(struct input_ctx *ictx, const char *p)
diff --git a/job.c b/job.c
index 6c1a3e09..a1fc0cf5 100644
--- a/job.c
+++ b/job.c
@@ -66,19 +66,20 @@ struct job {
/* All jobs list. */
static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
-/* Start a job running, if it isn't already. */
+/* Start a job running. */
struct job *
-job_run(const char *cmd, struct session *s, const char *cwd,
- job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb,
- void *data, int flags, int sx, int sy)
+job_run(const char *cmd, int argc, char **argv, struct session *s,
+ const char *cwd, job_update_cb updatecb, job_complete_cb completecb,
+ job_free_cb freecb, void *data, int flags, int sx, int sy)
{
- struct job *job;
- struct environ *env;
- pid_t pid;
- int nullfd, out[2], master;
- const char *home;
- sigset_t set, oldset;
- struct winsize ws;
+ struct job *job;
+ struct environ *env;
+ pid_t pid;
+ int nullfd, out[2], master;
+ const char *home;
+ sigset_t set, oldset;
+ struct winsize ws;
+ char **argvp;
/*
* Do not set TERM during .tmux.conf, it is nice to be able to use
@@ -99,7 +100,13 @@ job_run(const char *cmd, struct session *s, const char *cwd,
goto fail;
pid = fork();
}
- log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, cwd == NULL ? "" : cwd);
+ if (cmd == NULL) {
+ cmd_log_argv(argc, argv, "%s:", __func__);
+ log_debug("%s: cwd=%s", __func__, cwd == NULL ? "" : cwd);
+ } else {
+ log_debug("%s: cmd=%s, cwd=%s", __func__, cmd,
+ cwd == NULL ? "" : cwd);
+ }
switch (pid) {
case -1:
@@ -112,10 +119,10 @@ job_run(const char *cmd, struct session *s, const char *cwd,
proc_clear_signals(server_proc, 1);
sigprocmask(SIG_SETMASK, &oldset, NULL);
- if (cwd == NULL || chdir(cwd) != 0) {
- if ((home = find_home()) == NULL || chdir(home) != 0)
- chdir("/");
- }
+ if ((cwd == NULL || chdir(cwd) != 0) &&
+ ((home = find_home()) == NULL || chdir(home) != 0) &&
+ chdir("/") != 0)
+ fatal("chdir failed");
environ_push(env);
environ_free(env);
@@ -139,8 +146,14 @@ job_run(const char *cmd, struct session *s, const char *cwd,
}
closefrom(STDERR_FILENO + 1);
- execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
- fatal("execl failed");
+ if (cmd != NULL) {
+ execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
+ fatal("execl failed");
+ } else {
+ argvp = cmd_copy_argv(argc, argv);
+ execvp(argvp[0], argvp);
+ fatal("execvp failed");
+ }
}
sigprocmask(SIG_SETMASK, &oldset, NULL);
@@ -150,7 +163,10 @@ job_run(const char *cmd, struct session *s, const char *cwd,
job->state = JOB_RUNNING;
job->flags = flags;
- job->cmd = xstrdup(cmd);
+ if (cmd != NULL)
+ job->cmd = xstrdup(cmd);
+ else
+ job->cmd = cmd_stringify_argv(argc, argv);
job->pid = pid;
job->status = 0;
diff --git a/names.c b/names.c
index f437b53e..09b33082 100644
--- a/names.c
+++ b/names.c
@@ -109,6 +109,8 @@ default_window_name(struct window *w)
{
char *cmd, *s;
+ if (w->active == NULL)
+ return (xstrdup(""));
cmd = cmd_stringify_argv(w->active->argc, w->active->argv);
if (cmd != NULL && *cmd != '\0')
s = parse_window_name(cmd);
diff --git a/options-table.c b/options-table.c
index 873f8d67..a6f07cf9 100644
--- a/options-table.c
+++ b/options-table.c
@@ -68,6 +68,12 @@ static const char *options_table_set_clipboard_list[] = {
static const char *options_table_window_size_list[] = {
"largest", "smallest", "manual", "latest", NULL
};
+static const char *options_table_remain_on_exit_list[] = {
+ "off", "on", "failed", NULL
+};
+static const char *options_table_detach_on_destroy_list[] = {
+ "off", "on", "no-detached", NULL
+};
/* Status line format. */
#define OPTIONS_TABLE_STATUS_FORMAT1 \
@@ -401,8 +407,9 @@ const struct options_table_entry options_table[] = {
},
{ .name = "detach-on-destroy",
- .type = OPTIONS_TABLE_FLAG,
+ .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
+ .choices = options_table_detach_on_destroy_list,
.default_num = 1,
.text = "Whether to detach when a session is destroyed, or switch "
"the client to another session if any exist."
@@ -948,16 +955,17 @@ const struct options_table_entry options_table[] = {
},
{ .name = "remain-on-exit",
- .type = OPTIONS_TABLE_FLAG,
+ .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
+ .choices = options_table_remain_on_exit_list,
.default_num = 0,
.text = "Whether panes should remain ('on') or be automatically "
- "killed ('off') when the program inside exits."
+ "killed ('off' or 'failed') when the program inside exits."
},
{ .name = "synchronize-panes",
.type = OPTIONS_TABLE_FLAG,
- .scope = OPTIONS_TABLE_WINDOW,
+ .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_num = 0,
.text = "Whether typing should be sent to all panes simultaneously."
},
diff --git a/options.c b/options.c
index 09850f7e..9bc89db3 100644
--- a/options.c
+++ b/options.c
@@ -157,8 +157,7 @@ options_value_to_string(struct options_entry *o, union options_value *ov,
case OPTIONS_TABLE_CHOICE:
s = xstrdup(o->tableentry->choices[ov->number]);
break;
- case OPTIONS_TABLE_STRING:
- case OPTIONS_TABLE_COMMAND:
+ default:
fatalx("not a number option type");
}
return (s);
@@ -311,6 +310,8 @@ options_default_to_string(const struct options_table_entry *oe)
case OPTIONS_TABLE_CHOICE:
s = xstrdup(oe->choices[oe->default_num]);
break;
+ default:
+ fatalx("unknown option type");
}
return (s);
}
@@ -703,7 +704,7 @@ options_get_number(struct options *oo, const char *name)
if (o == NULL)
fatalx("missing option %s", name);
if (!OPTIONS_IS_NUMBER(o))
- fatalx("option %s is not a number", name);
+ fatalx("option %s is not a number", name);
return (o->value.number);
}
diff --git a/osdep-cygwin.c b/osdep-cygwin.c
index 60630b33..4346373c 100644
--- a/osdep-cygwin.c
+++ b/osdep-cygwin.c
@@ -19,7 +19,6 @@
#include <sys/param.h>
#include <sys/stat.h>
-#include <event.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
diff --git a/osdep-darwin.c b/osdep-darwin.c
index 6b2b1d72..a2b125ad 100644
--- a/osdep-darwin.c
+++ b/osdep-darwin.c
@@ -20,12 +20,13 @@
#include <sys/sysctl.h>
#include <Availability.h>
-#include <event.h>
#include <libproc.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include "compat.h"
+
char *osdep_get_name(int, char *);
char *osdep_get_cwd(int);
struct event_base *osdep_event_init(void);
diff --git a/osdep-dragonfly.c b/osdep-dragonfly.c
index 879034e8..02a4d18e 100644
--- a/osdep-dragonfly.c
+++ b/osdep-dragonfly.c
@@ -23,7 +23,6 @@
#include <err.h>
#include <errno.h>
-#include <event.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
diff --git a/osdep-freebsd.c b/osdep-freebsd.c
index a7d02930..0f347f9a 100644
--- a/osdep-freebsd.c
+++ b/osdep-freebsd.c
@@ -24,7 +24,6 @@
#include <err.h>
#include <errno.h>
-#include <event.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
diff --git a/osdep-haiku.c b/osdep-haiku.c
new file mode 100644
index 00000000..7b1f800a
--- /dev/null
+++ b/osdep-haiku.c
@@ -0,0 +1,52 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2009 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 <kernel/OS.h>
+
+#include "tmux.h"
+
+char *
+osdep_get_name(int fd, __unused char *tty)
+{
+ pid_t tid;
+ team_info tinfo;
+
+ if ((tid = tcgetpgrp(fd)) == -1)
+ return (NULL);
+
+ if (get_team_info(tid, &tinfo) != B_OK)
+ return (NULL);
+
+ /* Up to the first 64 characters. */
+ return (xstrdup(tinfo.args));
+}
+
+char *
+osdep_get_cwd(int fd)
+{
+ return (NULL);
+}
+
+struct event_base *
+osdep_event_init(void)
+{
+ return (event_init());
+}
diff --git a/osdep-hpux.c b/osdep-hpux.c
index 16993b93..64e33784 100644
--- a/osdep-hpux.c
+++ b/osdep-hpux.c
@@ -18,8 +18,6 @@
#include <sys/types.h>
-#include <event.h>
-
#include "tmux.h"
char *
diff --git a/osdep-linux.c b/osdep-linux.c
index 5f0d9352..7dbab1f0 100644
--- a/osdep-linux.c
+++ b/osdep-linux.c
@@ -20,7 +20,6 @@
#include <sys/stat.h>
#include <sys/param.h>
-#include <event.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
diff --git a/osdep-netbsd.c b/osdep-netbsd.c
index 67894175..b473e017 100644
--- a/osdep-netbsd.c
+++ b/osdep-netbsd.c
@@ -22,7 +22,6 @@
#include <sys/sysctl.h>
#include <errno.h>
-#include <event.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
diff --git a/osdep-openbsd.c b/osdep-openbsd.c
index b21a6628..f5c61372 100644
--- a/osdep-openbsd.c
+++ b/osdep-openbsd.c
@@ -23,11 +23,12 @@
#include <sys/stat.h>
#include <errno.h>
-#include <event.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include "compat.h"
+
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
diff --git a/osdep-sunos.c b/osdep-sunos.c
index 07043a9b..138e6bad 100644
--- a/osdep-sunos.c
+++ b/osdep-sunos.c
@@ -19,7 +19,6 @@
#include <sys/param.h>
#include <sys/stat.h>
-#include <event.h>
#include <fcntl.h>
#include <procfs.h>
#include <stdio.h>
diff --git a/osdep-unknown.c b/osdep-unknown.c
index bc59f569..440f619e 100644
--- a/osdep-unknown.c
+++ b/osdep-unknown.c
@@ -18,8 +18,6 @@
#include <sys/types.h>
-#include <event.h>
-
#include "tmux.h"
char *
diff --git a/popup.c b/popup.c
index 0ad20c5f..509d182d 100644
--- a/popup.c
+++ b/popup.c
@@ -31,13 +31,7 @@ struct popup_data {
struct cmdq_item *item;
int flags;
- char **lines;
- u_int nlines;
-
- char *cmd;
- struct cmd_find_state fs;
struct screen s;
-
struct job *job;
struct input_ctx *ictx;
int status;
@@ -104,54 +98,11 @@ popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
ttyctx->arg = pd;
}
-static void
-popup_write_screen(struct client *c, struct popup_data *pd)
-{
- struct cmdq_item *item = pd->item;
- struct screen_write_ctx ctx;
- char *copy, *next, *loop, *tmp;
- struct format_tree *ft;
- u_int i, y;
-
- ft = format_create(c, item, FORMAT_NONE, 0);
- if (cmd_find_valid_state(&pd->fs))
- format_defaults(ft, c, pd->fs.s, pd->fs.wl, pd->fs.wp);
- else
- format_defaults(ft, c, NULL, NULL, NULL);
-
- screen_write_start(&ctx, &pd->s);
- screen_write_clearscreen(&ctx, 8);
-
- y = 0;
- for (i = 0; i < pd->nlines; i++) {
- if (y == pd->sy - 2)
- break;
- copy = next = xstrdup(pd->lines[i]);
- while ((loop = strsep(&next, "\n")) != NULL) {
- if (y == pd->sy - 2)
- break;
- tmp = format_expand(ft, loop);
- screen_write_cursormove(&ctx, 0, y, 0);
- format_draw(&ctx, &grid_default_cell, pd->sx - 2, tmp,
- NULL);
- free(tmp);
- y++;
- }
- free(copy);
- }
-
- format_free(ft);
- screen_write_cursormove(&ctx, 0, y, 0);
- screen_write_stop(&ctx);
-}
-
static struct screen *
popup_mode_cb(struct client *c, u_int *cx, u_int *cy)
{
struct popup_data *pd = c->overlay_data;
- if (pd->ictx == NULL)
- return (0);
*cx = pd->px + 1 + pd->s.cx;
*cy = pd->py + 1 + pd->s.cy;
return (&pd->s);
@@ -199,14 +150,12 @@ popup_free_cb(struct client *c)
{
struct popup_data *pd = c->overlay_data;
struct cmdq_item *item = pd->item;
- u_int i;
if (pd->cb != NULL)
pd->cb(pd->status, pd->arg);
if (item != NULL) {
- if (pd->ictx != NULL &&
- cmdq_get_client(item) != NULL &&
+ if (cmdq_get_client(item) != NULL &&
cmdq_get_client(item)->session == NULL)
cmdq_get_client(item)->retval = pd->status;
cmdq_continue(item);
@@ -215,15 +164,9 @@ popup_free_cb(struct client *c)
if (pd->job != NULL)
job_free(pd->job);
- if (pd->ictx != NULL)
- input_free(pd->ictx);
-
- for (i = 0; i < pd->nlines; i++)
- free(pd->lines[i]);
- free(pd->lines);
+ input_free(pd->ictx);
screen_free(&pd->s);
- free(pd->cmd);
free(pd);
}
@@ -262,9 +205,7 @@ popup_handle_drag(struct client *c, struct popup_data *pd,
pd->sy = m->y - pd->py;
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)
+ if (pd->job != NULL)
job_resize(pd->job, pd->sx - 2, pd->sy - 2);
server_redraw_client(c);
}
@@ -275,13 +216,8 @@ popup_key_cb(struct client *c, struct key_event *event)
{
struct popup_data *pd = c->overlay_data;
struct mouse_event *m = &event->m;
- struct cmd_find_state *fs = &pd->fs;
- struct format_tree *ft;
- const char *cmd, *buf;
+ const char *buf;
size_t len;
- struct cmdq_state *state;
- enum cmd_parse_status status;
- char *error;
if (KEYC_IS_MOUSE(event->key)) {
if (pd->dragging != OFF) {
@@ -313,56 +249,22 @@ popup_key_cb(struct client *c, struct key_event *event)
}
}
- if (pd->ictx != NULL && (pd->flags & POPUP_WRITEKEYS)) {
- if (((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0 ||
- pd->job == NULL) &&
- (event->key == '\033' || event->key == '\003'))
- return (1);
- if (pd->job == NULL)
- return (0);
+ if ((((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0) ||
+ pd->job == NULL) &&
+ (event->key == '\033' || event->key == '\003'))
+ return (1);
+ if (pd->job != NULL) {
if (KEYC_IS_MOUSE(event->key)) {
/* Must be inside, checked already. */
- if (!input_key_get_mouse(&pd->s, m, m->x - pd->px,
- m->y - pd->py, &buf, &len))
+ if (!input_key_get_mouse(&pd->s, m, m->x - pd->px - 1,
+ m->y - pd->py - 1, &buf, &len))
return (0);
bufferevent_write(job_get_event(pd->job), buf, len);
return (0);
}
input_key(&pd->s, job_get_event(pd->job), event->key);
- return (0);
}
-
- if (pd->cmd == NULL)
- return (1);
-
- ft = format_create(NULL, pd->item, FORMAT_NONE, 0);
- if (cmd_find_valid_state(fs))
- format_defaults(ft, c, fs->s, fs->wl, fs->wp);
- else
- format_defaults(ft, c, NULL, NULL, NULL);
- format_add(ft, "popup_key", "%s", key_string_lookup_key(event->key, 0));
- if (KEYC_IS_MOUSE(event->key)) {
- format_add(ft, "popup_mouse", "1");
- format_add(ft, "popup_mouse_x", "%u", m->x - pd->px);
- format_add(ft, "popup_mouse_y", "%u", m->y - pd->py);
- }
- cmd = format_expand(ft, pd->cmd);
- format_free(ft);
-
- if (pd->item != NULL)
- event = cmdq_get_event(pd->item);
- else
- event = NULL;
- state = cmdq_new_state(&pd->fs, event, 0);
-
- status = cmd_parse_and_append(cmd, NULL, c, state, &error);
- if (status == CMD_PARSE_ERROR) {
- cmdq_append(c, cmdq_get_error(error));
- free(error);
- }
- cmdq_free_state(state);
-
- return (1);
+ return (0);
out:
pd->lx = m->x;
@@ -415,62 +317,12 @@ popup_job_complete_cb(struct job *job)
server_client_clear_overlay(pd->c);
}
-u_int
-popup_height(u_int nlines, const char **lines)
-{
- char *copy, *next, *loop;
- u_int i, height = 0;
-
- for (i = 0; i < nlines; i++) {
- copy = next = xstrdup(lines[i]);
- while ((loop = strsep(&next, "\n")) != NULL)
- height++;
- free(copy);
- }
-
- return (height);
-}
-
-u_int
-popup_width(struct cmdq_item *item, u_int nlines, const char **lines,
- struct client *c, struct cmd_find_state *fs)
-{
- char *copy, *next, *loop, *tmp;
- struct format_tree *ft;
- u_int i, width = 0, tmpwidth;
-
- ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
- if (fs != NULL && cmd_find_valid_state(fs))
- format_defaults(ft, c, fs->s, fs->wl, fs->wp);
- else
- format_defaults(ft, c, NULL, NULL, NULL);
-
- for (i = 0; i < nlines; i++) {
- copy = next = xstrdup(lines[i]);
- while ((loop = strsep(&next, "\n")) != NULL) {
- tmp = format_expand(ft, loop);
- tmpwidth = format_width(tmp);
- if (tmpwidth > width)
- width = tmpwidth;
- free(tmp);
- }
- free(copy);
- }
-
- format_free(ft);
- return (width);
-}
-
int
popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx,
- u_int sy, u_int nlines, const char **lines, const char *shellcmd,
- const char *cmd, const char *cwd, struct client *c,
- struct cmd_find_state *fs, popup_close_cb cb, void *arg)
+ u_int sy, const char *shellcmd, int argc, char **argv, const char *cwd,
+ struct client *c, struct session *s, popup_close_cb cb, void *arg)
{
struct popup_data *pd;
- u_int i;
- struct session *s;
- int jobflags;
if (sx < 3 || sy < 3)
return (-1);
@@ -488,39 +340,17 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx,
pd->arg = arg;
pd->status = 128 + SIGHUP;
- if (fs != NULL)
- cmd_find_copy_state(&pd->fs, fs);
screen_init(&pd->s, sx - 2, sy - 2, 0);
- if (cmd != NULL)
- pd->cmd = xstrdup(cmd);
-
pd->px = px;
pd->py = py;
pd->sx = sx;
pd->sy = sy;
- pd->nlines = nlines;
- if (pd->nlines != 0)
- pd->lines = xreallocarray(NULL, pd->nlines, sizeof *pd->lines);
-
- for (i = 0; i < pd->nlines; i++)
- pd->lines[i] = xstrdup(lines[i]);
- popup_write_screen(c, pd);
-
- if (shellcmd != NULL) {
- if (fs != NULL)
- s = fs->s;
- else
- s = NULL;
- jobflags = JOB_NOWAIT|JOB_PTY;
- if (flags & POPUP_WRITEKEYS)
- jobflags |= JOB_KEEPWRITE;
- pd->job = job_run(shellcmd, s, cwd, popup_job_update_cb,
- popup_job_complete_cb, NULL, pd, jobflags, pd->sx - 2,
- pd->sy - 2);
- pd->ictx = input_init(NULL, job_get_event(pd->job));
- }
+ pd->job = job_run(shellcmd, argc, argv, s, cwd,
+ popup_job_update_cb, popup_job_complete_cb, NULL, pd,
+ JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE, pd->sx - 2, pd->sy - 2);
+ pd->ictx = input_init(NULL, job_get_event(pd->job));
server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,
popup_draw_cb, popup_key_cb, popup_free_cb, pd);
@@ -606,9 +436,8 @@ popup_editor(struct client *c, const char *buf, size_t len,
py = (c->tty.sy / 2) - (sy / 2);
xasprintf(&cmd, "%s %s", editor, path);
- if (popup_display(POPUP_WRITEKEYS|POPUP_CLOSEEXIT, NULL, px, py, sx, sy,
- 0, NULL, cmd, NULL, _PATH_TMP, c, NULL, popup_editor_close_cb,
- pe) != 0) {
+ if (popup_display(POPUP_CLOSEEXIT, NULL, px, py, sx, sy, cmd, 0, NULL,
+ _PATH_TMP, c, NULL, popup_editor_close_cb, pe) != 0) {
popup_editor_free(pe);
free(cmd);
return (-1);
diff --git a/proc.c b/proc.c
index 592d8e05..a1a2b36a 100644
--- a/proc.c
+++ b/proc.c
@@ -17,11 +17,12 @@
*/
#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/utsname.h>
#include <errno.h>
-#include <event.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
@@ -47,6 +48,8 @@ struct tmuxproc {
struct event ev_sigusr1;
struct event ev_sigusr2;
struct event ev_sigwinch;
+
+ TAILQ_HEAD(, tmuxpeer) peers;
};
struct tmuxpeer {
@@ -60,6 +63,8 @@ struct tmuxpeer {
void (*dispatchcb)(struct imsg *, void *);
void *arg;
+
+ TAILQ_ENTRY(tmuxpeer) entry;
};
static int peer_check_version(struct tmuxpeer *, struct imsg *);
@@ -203,6 +208,7 @@ proc_start(const char *name)
tp = xcalloc(1, sizeof *tp);
tp->name = xstrdup(name);
+ TAILQ_INIT(&tp->peers);
return (tp);
}
@@ -220,6 +226,10 @@ proc_loop(struct tmuxproc *tp, int (*loopcb)(void))
void
proc_exit(struct tmuxproc *tp)
{
+ struct tmuxpeer *peer;
+
+ TAILQ_FOREACH(peer, &tp->peers, entry)
+ imsg_flush(&peer->ibuf);
tp->exit = 1;
}
@@ -310,6 +320,7 @@ proc_add_peer(struct tmuxproc *tp, int fd,
event_set(&peer->event, fd, EV_READ, proc_event_cb, peer);
log_debug("add peer %p: %d (%p)", peer, fd, arg);
+ TAILQ_INSERT_TAIL(&tp->peers, peer, entry);
proc_update_event(peer);
return (peer);
@@ -318,6 +329,7 @@ proc_add_peer(struct tmuxproc *tp, int fd,
void
proc_remove_peer(struct tmuxpeer *peer)
{
+ TAILQ_REMOVE(&peer->parent->peers, peer, entry);
log_debug("remove peer %p", peer);
event_del(&peer->event);
@@ -338,3 +350,27 @@ proc_toggle_log(struct tmuxproc *tp)
{
log_toggle(tp->name);
}
+
+pid_t
+proc_fork_and_daemon(int *fd)
+{
+ pid_t pid;
+ int pair[2];
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
+ fatal("socketpair failed");
+ switch (pid = fork()) {
+ case -1:
+ fatal("fork failed");
+ case 0:
+ close(pair[0]);
+ *fd = pair[1];
+ if (daemon(1, 0) != 0)
+ fatal("daemon failed");
+ return (0);
+ default:
+ close(pair[1]);
+ *fd = pair[0];
+ return (pid);
+ }
+}
diff --git a/regress/osc-11colours.sh b/regress/osc-11colours.sh
new file mode 100644
index 00000000..a0dd605d
--- /dev/null
+++ b/regress/osc-11colours.sh
@@ -0,0 +1,243 @@
+#!/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
+
+$TMUX -vv new -d
+$TMUX set -g remain-on-exit on
+
+do_test() {
+ $TMUX splitw "printf '$1'"
+ c="$($TMUX display -p '#{pane_bg}')"
+ $TMUX kill-pane
+ [ "$c" != "$2" ] && return 1
+ return 0
+}
+
+do_test '\033]11;rgb:ff/ff/ff\007' '#ffffff' || exit 1
+do_test '\033]11;rgb:ff/ff/ff\007\033]111\007' 'default' || exit 1
+
+do_test '\033]11;cmy:0.9373/0.6941/0.4549\007' '#0f4e8b' || exit 1
+do_test '\033]11;cmyk:0.88/0.44/0.00/0.45\007' '#104e8c' || exit 1
+
+do_test '\033]11;16,78,139\007' '#104e8b' || exit 1
+do_test '\033]11;#104E8B\007' '#104e8b' || exit 1
+do_test '\033]11;#10004E008B00\007' '#104e8b' || exit 1
+do_test '\033]11;DodgerBlue4\007' '#104e8b' || exit 1
+do_test '\033]11;DodgerBlue4 \007' '#104e8b' || exit 1
+do_test '\033]11; DodgerBlue4\007' '#104e8b' || exit 1
+do_test '\033]11;rgb:10/4E/8B\007' '#104e8b' || exit 1
+do_test '\033]11;rgb:1000/4E00/8B00\007' '#104e8b' || exit 1
+
+do_test '\033]11;grey\007' '#bebebe' || exit 1
+do_test '\033]11;grey0\007' '#000000' || exit 1
+do_test '\033]11;grey1\007' '#030303' || exit 1
+do_test '\033]11;grey2\007' '#050505' || exit 1
+do_test '\033]11;grey3\007' '#080808' || exit 1
+do_test '\033]11;grey4\007' '#0a0a0a' || exit 1
+do_test '\033]11;grey5\007' '#0d0d0d' || exit 1
+do_test '\033]11;grey6\007' '#0f0f0f' || exit 1
+do_test '\033]11;grey7\007' '#121212' || exit 1
+do_test '\033]11;grey8\007' '#141414' || exit 1
+do_test '\033]11;grey9\007' '#171717' || exit 1
+do_test '\033]11;grey10\007' '#1a1a1a' || exit 1
+do_test '\033]11;grey11\007' '#1c1c1c' || exit 1
+do_test '\033]11;grey12\007' '#1f1f1f' || exit 1
+do_test '\033]11;grey13\007' '#212121' || exit 1
+do_test '\033]11;grey14\007' '#242424' || exit 1
+do_test '\033]11;grey15\007' '#262626' || exit 1
+do_test '\033]11;grey16\007' '#292929' || exit 1
+do_test '\033]11;grey17\007' '#2b2b2b' || exit 1
+do_test '\033]11;grey18\007' '#2e2e2e' || exit 1
+do_test '\033]11;grey19\007' '#303030' || exit 1
+do_test '\033]11;grey20\007' '#333333' || exit 1
+do_test '\033]11;grey21\007' '#363636' || exit 1
+do_test '\033]11;grey22\007' '#383838' || exit 1
+do_test '\033]11;grey23\007' '#3b3b3b' || exit 1
+do_test '\033]11;grey24\007' '#3d3d3d' || exit 1
+do_test '\033]11;grey25\007' '#404040' || exit 1
+do_test '\033]11;grey26\007' '#424242' || exit 1
+do_test '\033]11;grey27\007' '#454545' || exit 1
+do_test '\033]11;grey28\007' '#474747' || exit 1
+do_test '\033]11;grey29\007' '#4a4a4a' || exit 1
+do_test '\033]11;grey30\007' '#4d4d4d' || exit 1
+do_test '\033]11;grey31\007' '#4f4f4f' || exit 1
+do_test '\033]11;grey32\007' '#525252' || exit 1
+do_test '\033]11;grey33\007' '#545454' || exit 1
+do_test '\033]11;grey34\007' '#575757' || exit 1
+do_test '\033]11;grey35\007' '#595959' || exit 1
+do_test '\033]11;grey36\007' '#5c5c5c' || exit 1
+do_test '\033]11;grey37\007' '#5e5e5e' || exit 1
+do_test '\033]11;grey38\007' '#616161' || exit 1
+do_test '\033]11;grey39\007' '#636363' || exit 1
+do_test '\033]11;grey40\007' '#666666' || exit 1
+do_test '\033]11;grey41\007' '#696969' || exit 1
+do_test '\033]11;grey42\007' '#6b6b6b' || exit 1
+do_test '\033]11;grey43\007' '#6e6e6e' || exit 1
+do_test '\033]11;grey44\007' '#707070' || exit 1
+do_test '\033]11;grey45\007' '#737373' || exit 1
+do_test '\033]11;grey46\007' '#757575' || exit 1
+do_test '\033]11;grey47\007' '#787878' || exit 1
+do_test '\033]11;grey48\007' '#7a7a7a' || exit 1
+do_test '\033]11;grey49\007' '#7d7d7d' || exit 1
+do_test '\033]11;grey50\007' '#7f7f7f' || exit 1
+do_test '\033]11;grey51\007' '#828282' || exit 1
+do_test '\033]11;grey52\007' '#858585' || exit 1
+do_test '\033]11;grey53\007' '#878787' || exit 1
+do_test '\033]11;grey54\007' '#8a8a8a' || exit 1
+do_test '\033]11;grey55\007' '#8c8c8c' || exit 1
+do_test '\033]11;grey56\007' '#8f8f8f' || exit 1
+do_test '\033]11;grey57\007' '#919191' || exit 1
+do_test '\033]11;grey58\007' '#949494' || exit 1
+do_test '\033]11;grey59\007' '#969696' || exit 1
+do_test '\033]11;grey60\007' '#999999' || exit 1
+do_test '\033]11;grey61\007' '#9c9c9c' || exit 1
+do_test '\033]11;grey62\007' '#9e9e9e' || exit 1
+do_test '\033]11;grey63\007' '#a1a1a1' || exit 1
+do_test '\033]11;grey64\007' '#a3a3a3' || exit 1
+do_test '\033]11;grey65\007' '#a6a6a6' || exit 1
+do_test '\033]11;grey66\007' '#a8a8a8' || exit 1
+do_test '\033]11;grey67\007' '#ababab' || exit 1
+do_test '\033]11;grey68\007' '#adadad' || exit 1
+do_test '\033]11;grey69\007' '#b0b0b0' || exit 1
+do_test '\033]11;grey70\007' '#b3b3b3' || exit 1
+do_test '\033]11;grey71\007' '#b5b5b5' || exit 1
+do_test '\033]11;grey72\007' '#b8b8b8' || exit 1
+do_test '\033]11;grey73\007' '#bababa' || exit 1
+do_test '\033]11;grey74\007' '#bdbdbd' || exit 1
+do_test '\033]11;grey75\007' '#bfbfbf' || exit 1
+do_test '\033]11;grey76\007' '#c2c2c2' || exit 1
+do_test '\033]11;grey77\007' '#c4c4c4' || exit 1
+do_test '\033]11;grey78\007' '#c7c7c7' || exit 1
+do_test '\033]11;grey79\007' '#c9c9c9' || exit 1
+do_test '\033]11;grey80\007' '#cccccc' || exit 1
+do_test '\033]11;grey81\007' '#cfcfcf' || exit 1
+do_test '\033]11;grey82\007' '#d1d1d1' || exit 1
+do_test '\033]11;grey83\007' '#d4d4d4' || exit 1
+do_test '\033]11;grey84\007' '#d6d6d6' || exit 1
+do_test '\033]11;grey85\007' '#d9d9d9' || exit 1
+do_test '\033]11;grey86\007' '#dbdbdb' || exit 1
+do_test '\033]11;grey87\007' '#dedede' || exit 1
+do_test '\033]11;grey88\007' '#e0e0e0' || exit 1
+do_test '\033]11;grey89\007' '#e3e3e3' || exit 1
+do_test '\033]11;grey90\007' '#e5e5e5' || exit 1
+do_test '\033]11;grey91\007' '#e8e8e8' || exit 1
+do_test '\033]11;grey92\007' '#ebebeb' || exit 1
+do_test '\033]11;grey93\007' '#ededed' || exit 1
+do_test '\033]11;grey94\007' '#f0f0f0' || exit 1
+do_test '\033]11;grey95\007' '#f2f2f2' || exit 1
+do_test '\033]11;grey96\007' '#f5f5f5' || exit 1
+do_test '\033]11;grey97\007' '#f7f7f7' || exit 1
+do_test '\033]11;grey98\007' '#fafafa' || exit 1
+do_test '\033]11;grey99\007' '#fcfcfc' || exit 1
+do_test '\033]11;grey100\007' '#ffffff' || exit 1
+
+do_test '\033]11;gray\007' '#bebebe' || exit 1
+do_test '\033]11;gray0\007' '#000000' || exit 1
+do_test '\033]11;gray1\007' '#030303' || exit 1
+do_test '\033]11;gray2\007' '#050505' || exit 1
+do_test '\033]11;gray3\007' '#080808' || exit 1
+do_test '\033]11;gray4\007' '#0a0a0a' || exit 1
+do_test '\033]11;gray5\007' '#0d0d0d' || exit 1
+do_test '\033]11;gray6\007' '#0f0f0f' || exit 1
+do_test '\033]11;gray7\007' '#121212' || exit 1
+do_test '\033]11;gray8\007' '#141414' || exit 1
+do_test '\033]11;gray9\007' '#171717' || exit 1
+do_test '\033]11;gray10\007' '#1a1a1a' || exit 1
+do_test '\033]11;gray11\007' '#1c1c1c' || exit 1
+do_test '\033]11;gray12\007' '#1f1f1f' || exit 1
+do_test '\033]11;gray13\007' '#212121' || exit 1
+do_test '\033]11;gray14\007' '#242424' || exit 1
+do_test '\033]11;gray15\007' '#262626' || exit 1
+do_test '\033]11;gray16\007' '#292929' || exit 1
+do_test '\033]11;gray17\007' '#2b2b2b' || exit 1
+do_test '\033]11;gray18\007' '#2e2e2e' || exit 1
+do_test '\033]11;gray19\007' '#303030' || exit 1
+do_test '\033]11;gray20\007' '#333333' || exit 1
+do_test '\033]11;gray21\007' '#363636' || exit 1
+do_test '\033]11;gray22\007' '#383838' || exit 1
+do_test '\033]11;gray23\007' '#3b3b3b' || exit 1
+do_test '\033]11;gray24\007' '#3d3d3d' || exit 1
+do_test '\033]11;gray25\007' '#404040' || exit 1
+do_test '\033]11;gray26\007' '#424242' || exit 1
+do_test '\033]11;gray27\007' '#454545' || exit 1
+do_test '\033]11;gray28\007' '#474747' || exit 1
+do_test '\033]11;gray29\007' '#4a4a4a' || exit 1
+do_test '\033]11;gray30\007' '#4d4d4d' || exit 1
+do_test '\033]11;gray31\007' '#4f4f4f' || exit 1
+do_test '\033]11;gray32\007' '#525252' || exit 1
+do_test '\033]11;gray33\007' '#545454' || exit 1
+do_test '\033]11;gray34\007' '#575757' || exit 1
+do_test '\033]11;gray35\007' '#595959' || exit 1
+do_test '\033]11;gray36\007' '#5c5c5c' || exit 1
+do_test '\033]11;gray37\007' '#5e5e5e' || exit 1
+do_test '\033]11;gray38\007' '#616161' || exit 1
+do_test '\033]11;gray39\007' '#636363' || exit 1
+do_test '\033]11;gray40\007' '#666666' || exit 1
+do_test '\033]11;gray41\007' '#696969' || exit 1
+do_test '\033]11;gray42\007' '#6b6b6b' || exit 1
+do_test '\033]11;gray43\007' '#6e6e6e' || exit 1
+do_test '\033]11;gray44\007' '#707070' || exit 1
+do_test '\033]11;gray45\007' '#737373' || exit 1
+do_test '\033]11;gray46\007' '#757575' || exit 1
+do_test '\033]11;gray47\007' '#787878' || exit 1
+do_test '\033]11;gray48\007' '#7a7a7a' || exit 1
+do_test '\033]11;gray49\007' '#7d7d7d' || exit 1
+do_test '\033]11;gray50\007' '#7f7f7f' || exit 1
+do_test '\033]11;gray51\007' '#828282' || exit 1
+do_test '\033]11;gray52\007' '#858585' || exit 1
+do_test '\033]11;gray53\007' '#878787' || exit 1
+do_test '\033]11;gray54\007' '#8a8a8a' || exit 1
+do_test '\033]11;gray55\007' '#8c8c8c' || exit 1
+do_test '\033]11;gray56\007' '#8f8f8f' || exit 1
+do_test '\033]11;gray57\007' '#919191' || exit 1
+do_test '\033]11;gray58\007' '#949494' || exit 1
+do_test '\033]11;gray59\007' '#969696' || exit 1
+do_test '\033]11;gray60\007' '#999999' || exit 1
+do_test '\033]11;gray61\007' '#9c9c9c' || exit 1
+do_test '\033]11;gray62\007' '#9e9e9e' || exit 1
+do_test '\033]11;gray63\007' '#a1a1a1' || exit 1
+do_test '\033]11;gray64\007' '#a3a3a3' || exit 1
+do_test '\033]11;gray65\007' '#a6a6a6' || exit 1
+do_test '\033]11;gray66\007' '#a8a8a8' || exit 1
+do_test '\033]11;gray67\007' '#ababab' || exit 1
+do_test '\033]11;gray68\007' '#adadad' || exit 1
+do_test '\033]11;gray69\007' '#b0b0b0' || exit 1
+do_test '\033]11;gray70\007' '#b3b3b3' || exit 1
+do_test '\033]11;gray71\007' '#b5b5b5' || exit 1
+do_test '\033]11;gray72\007' '#b8b8b8' || exit 1
+do_test '\033]11;gray73\007' '#bababa' || exit 1
+do_test '\033]11;gray74\007' '#bdbdbd' || exit 1
+do_test '\033]11;gray75\007' '#bfbfbf' || exit 1
+do_test '\033]11;gray76\007' '#c2c2c2' || exit 1
+do_test '\033]11;gray77\007' '#c4c4c4' || exit 1
+do_test '\033]11;gray78\007' '#c7c7c7' || exit 1
+do_test '\033]11;gray79\007' '#c9c9c9' || exit 1
+do_test '\033]11;gray80\007' '#cccccc' || exit 1
+do_test '\033]11;gray81\007' '#cfcfcf' || exit 1
+do_test '\033]11;gray82\007' '#d1d1d1' || exit 1
+do_test '\033]11;gray83\007' '#d4d4d4' || exit 1
+do_test '\033]11;gray84\007' '#d6d6d6' || exit 1
+do_test '\033]11;gray85\007' '#d9d9d9' || exit 1
+do_test '\033]11;gray86\007' '#dbdbdb' || exit 1
+do_test '\033]11;gray87\007' '#dedede' || exit 1
+do_test '\033]11;gray88\007' '#e0e0e0' || exit 1
+do_test '\033]11;gray89\007' '#e3e3e3' || exit 1
+do_test '\033]11;gray90\007' '#e5e5e5' || exit 1
+do_test '\033]11;gray91\007' '#e8e8e8' || exit 1
+do_test '\033]11;gray92\007' '#ebebeb' || exit 1
+do_test '\033]11;gray93\007' '#ededed' || exit 1
+do_test '\033]11;gray94\007' '#f0f0f0' || exit 1
+do_test '\033]11;gray95\007' '#f2f2f2' || exit 1
+do_test '\033]11;gray96\007' '#f5f5f5' || exit 1
+do_test '\033]11;gray97\007' '#f7f7f7' || exit 1
+do_test '\033]11;gray98\007' '#fafafa' || exit 1
+do_test '\033]11;gray99\007' '#fcfcfc' || exit 1
+do_test '\033]11;gray100\007' '#ffffff' || exit 1
+
+$TMUX -f/dev/null kill-server 2>/dev/null
+exit 0
diff --git a/screen-redraw.c b/screen-redraw.c
index 50df2671..6ddabc52 100644
--- a/screen-redraw.c
+++ b/screen-redraw.c
@@ -32,8 +32,8 @@ static void screen_redraw_set_context(struct client *,
struct screen_redraw_ctx *);
#define CELL_INSIDE 0
-#define CELL_LEFTRIGHT 1
-#define CELL_TOPBOTTOM 2
+#define CELL_TOPBOTTOM 1
+#define CELL_LEFTRIGHT 2
#define CELL_TOPLEFT 3
#define CELL_TOPRIGHT 4
#define CELL_BOTTOMLEFT 5
@@ -47,6 +47,9 @@ static void screen_redraw_set_context(struct client *,
#define CELL_BORDERS " xqlkmjwvtun~"
+#define START_ISOLATE "\342\201\246"
+#define END_ISOLATE "\342\201\251"
+
static const struct utf8_data screen_redraw_double_borders[] = {
{ "", 0, 0, 0 },
{ "\342\225\221", 0, 3, 1 }, /* U+2551 */
@@ -299,7 +302,7 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py,
case 13: /* 1101, left right bottom */
return (CELL_TOPJOIN);
case 12: /* 1100, left right */
- return (CELL_TOPBOTTOM);
+ return (CELL_LEFTRIGHT);
case 11: /* 1011, left top bottom */
return (CELL_RIGHTJOIN);
case 10: /* 1010, left top */
@@ -313,7 +316,7 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py,
case 5: /* 0101, right bottom */
return (CELL_TOPLEFT);
case 3: /* 0011, top bottom */
- return (CELL_LEFTRIGHT);
+ return (CELL_TOPBOTTOM);
}
return (CELL_OUTSIDE);
}
@@ -407,7 +410,7 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp,
struct format_tree *ft;
char *expanded;
int pane_status = rctx->pane_status;
- u_int width, i, cell_type, top, px, py;
+ u_int width, i, cell_type, px, py;
struct screen_write_ctx ctx;
struct screen old;
@@ -432,16 +435,12 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp,
screen_write_start(&ctx, &wp->status_screen);
- if (rctx->statustop)
- top = rctx->statuslines;
- else
- top = 0;
for (i = 0; i < width; i++) {
px = wp->xoff + 2 + i;
if (rctx->pane_status == PANE_STATUS_TOP)
- py = top + wp->yoff - 1;
+ py = wp->yoff - 1;
else
- py = top + wp->yoff + wp->sy;
+ py = wp->yoff + wp->sy;
cell_type = screen_redraw_type_of_cell(c, px, py, pane_status);
screen_redraw_border_set(wp, pane_lines, cell_type, &gc);
screen_write_cell(&ctx, &gc);
@@ -684,7 +683,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
struct tty *tty = &c->tty;
struct window_pane *wp;
u_int cell_type, x = ctx->ox + i, y = ctx->oy + j;
- int pane_status = ctx->pane_status;
+ int pane_status = ctx->pane_status, isolates;
struct grid_cell gc;
const struct grid_cell *tmp;
@@ -709,11 +708,22 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
}
screen_redraw_border_set(wp, ctx->pane_lines, cell_type, &gc);
+ if (cell_type == CELL_TOPBOTTOM &&
+ (c->flags & CLIENT_UTF8) &&
+ tty_term_has(tty->term, TTYC_BIDI))
+ isolates = 1;
+ else
+ isolates = 0;
+
if (ctx->statustop)
tty_cursor(tty, i, ctx->statuslines + j);
else
tty_cursor(tty, i, j);
+ if (isolates)
+ tty_puts(tty, END_ISOLATE);
tty_cell(tty, &gc, &grid_default_cell, NULL);
+ if (isolates)
+ tty_puts(tty, START_ISOLATE);
}
/* Draw the borders. */
diff --git a/screen-write.c b/screen-write.c
index c9c61086..8e9ec582 100644
--- a/screen-write.c
+++ b/screen-write.c
@@ -23,13 +23,11 @@
#include "tmux.h"
+static struct screen_write_citem *screen_write_collect_trim(
+ struct screen_write_ctx *, u_int, u_int, u_int, int *);
static void screen_write_collect_clear(struct screen_write_ctx *, u_int,
u_int);
-static void screen_write_collect_clear_end(struct screen_write_ctx *, u_int,
- u_int);
-static void screen_write_collect_clear_start(struct screen_write_ctx *,
- u_int, u_int);
-static void screen_write_collect_scroll(struct screen_write_ctx *);
+static void screen_write_collect_scroll(struct screen_write_ctx *, u_int);
static void screen_write_collect_flush(struct screen_write_ctx *, int,
const char *);
@@ -38,23 +36,44 @@ static int screen_write_overwrite(struct screen_write_ctx *,
static const struct grid_cell *screen_write_combine(struct screen_write_ctx *,
const struct utf8_data *, u_int *);
-struct screen_write_collect_item {
- u_int x;
- int wrapped;
+struct screen_write_citem {
+ u_int x;
+ int wrapped;
- enum { TEXT, CLEAR_END, CLEAR_START } type;
- u_int used;
- u_int bg;
+ enum { TEXT, CLEAR } type;
+ u_int used;
+ u_int bg;
- struct grid_cell gc;
+ struct grid_cell gc;
- TAILQ_ENTRY(screen_write_collect_item) entry;
+ TAILQ_ENTRY(screen_write_citem) entry;
};
-struct screen_write_collect_line {
- u_int bg;
- char *data;
- TAILQ_HEAD(, screen_write_collect_item) items;
+struct screen_write_cline {
+ char *data;
+ TAILQ_HEAD(, screen_write_citem) items;
};
+TAILQ_HEAD(, screen_write_citem) screen_write_citem_freelist =
+ TAILQ_HEAD_INITIALIZER(screen_write_citem_freelist);
+
+static struct screen_write_citem *
+screen_write_get_citem(void)
+{
+ struct screen_write_citem *ci;
+
+ ci = TAILQ_FIRST(&screen_write_citem_freelist);
+ if (ci != NULL) {
+ TAILQ_REMOVE(&screen_write_citem_freelist, ci, entry);
+ memset(ci, 0, sizeof *ci);
+ return (ci);
+ }
+ return (xcalloc(1, sizeof *ci));
+}
+
+static void
+screen_write_free_citem(struct screen_write_citem *ci)
+{
+ TAILQ_INSERT_TAIL(&screen_write_citem_freelist, ci, entry);
+}
static void
screen_write_offset_timer(__unused int fd, __unused short events, void *data)
@@ -125,7 +144,8 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
* Redraw is already deferred to redraw another pane - redraw
* this one also when that happens.
*/
- log_debug("adding %%%u to deferred redraw", wp->id);
+ log_debug("%s: adding %%%u to deferred redraw", __func__,
+ wp->id);
wp->flags |= PANE_REDRAW;
return (-1);
}
@@ -220,7 +240,7 @@ screen_write_init(struct screen_write_ctx *ctx, struct screen *s)
if (ctx->s->write_list == NULL)
screen_write_make_list(ctx->s);
- ctx->item = xcalloc(1, sizeof *ctx->item);
+ ctx->item = screen_write_get_citem();
ctx->scrolled = 0;
ctx->bg = 8;
@@ -259,7 +279,6 @@ screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s,
}
}
-
/* Initialize writing. */
void
screen_write_start(struct screen_write_ctx *ctx, struct screen *s)
@@ -279,14 +298,7 @@ screen_write_stop(struct screen_write_ctx *ctx)
screen_write_collect_end(ctx);
screen_write_collect_flush(ctx, 0, __func__);
- log_debug("%s: %u cells (%u written, %u skipped)", __func__,
- ctx->cells, ctx->written, ctx->skipped);
- if (ctx->wp != NULL) {
- ctx->wp->written += ctx->written;
- ctx->wp->skipped += ctx->skipped;
- }
-
- free(ctx->item);
+ screen_write_free_citem(ctx->item);
}
/* Reset screen state. */
@@ -1095,9 +1107,10 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
void
screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
{
- struct screen *s = ctx->s;
- struct grid_line *gl;
- u_int sx = screen_size_x(s);
+ struct screen *s = ctx->s;
+ struct grid_line *gl;
+ u_int sx = screen_size_x(s);
+ struct screen_write_citem *ci = ctx->item;
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
@@ -1106,18 +1119,22 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
screen_write_collect_clear(ctx, s->cy, 1);
- ctx->s->write_list[s->cy].bg = 1 + bg;
- ctx->item->used = 0;
+ ci->x = 0;
+ ci->used = sx;
+ ci->type = CLEAR;
+ ci->bg = bg;
+ TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
+ ctx->item = screen_write_get_citem();
}
/* Clear to end of line from cursor. */
void
screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
{
- struct screen *s = ctx->s;
- struct grid_line *gl;
- u_int sx = screen_size_x(s);
- struct screen_write_collect_item *ci = ctx->item;
+ struct screen *s = ctx->s;
+ struct grid_line *gl;
+ u_int sx = screen_size_x(s);
+ struct screen_write_citem *ci = ctx->item, *before;
if (s->cx == 0) {
screen_write_clearline(ctx, bg);
@@ -1130,12 +1147,16 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
- screen_write_collect_clear_end(ctx, s->cy, s->cx);
+ before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL);
ci->x = s->cx;
- ci->type = CLEAR_END;
+ ci->used = sx - s->cx;
+ ci->type = CLEAR;
ci->bg = bg;
- TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
- ctx->item = xcalloc(1, sizeof *ctx->item);
+ if (before == NULL)
+ TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
+ else
+ TAILQ_INSERT_BEFORE(before, ci, entry);
+ ctx->item = screen_write_get_citem();
}
/* Clear to start of line from cursor. */
@@ -1143,8 +1164,8 @@ void
screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
{
struct screen *s = ctx->s;
- u_int sx = screen_size_x(s);
- struct screen_write_collect_item *ci = ctx->item;
+ u_int sx = screen_size_x(s);
+ struct screen_write_citem *ci = ctx->item, *before;
if (s->cx >= sx - 1) {
screen_write_clearline(ctx, bg);
@@ -1156,12 +1177,16 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
else
grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
- screen_write_collect_clear_start(ctx, s->cy, s->cx);
- ci->x = s->cx;
- ci->type = CLEAR_START;
+ before = screen_write_collect_trim(ctx, s->cy, 0, s->cx + 1, NULL);
+ ci->x = 0;
+ ci->used = s->cx + 1;
+ ci->type = CLEAR;
ci->bg = bg;
- TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
- ctx->item = xcalloc(1, sizeof *ctx->item);
+ if (before == NULL)
+ TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
+ else
+ TAILQ_INSERT_BEFORE(before, ci, entry);
+ ctx->item = screen_write_get_citem();
}
/* Move cursor to px,py. */
@@ -1183,6 +1208,7 @@ screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py,
if (py != -1 && (u_int)py > screen_size_y(s) - 1)
py = screen_size_y(s) - 1;
+ log_debug("%s: from %u,%u to %u,%u", __func__, s->cx, s->cy, px, py);
screen_write_set_cursor(ctx, px, py);
}
@@ -1240,8 +1266,6 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
gl = grid_get_line(gd, gd->hsize + s->cy);
if (wrapped)
gl->flags |= GRID_LINE_WRAPPED;
- else
- gl->flags &= ~GRID_LINE_WRAPPED;
log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
s->rupper, s->rlower);
@@ -1253,7 +1277,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
if (s->cy == s->rlower) {
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
- screen_write_collect_scroll(ctx);
+ screen_write_collect_scroll(ctx, bg);
ctx->scrolled++;
} else if (s->cy < screen_size_y(s) - 1)
screen_write_set_cursor(ctx, -1, s->cy + 1);
@@ -1279,7 +1303,7 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
for (i = 0; i < lines; i++) {
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
- screen_write_collect_scroll(ctx);
+ screen_write_collect_scroll(ctx, bg);
}
ctx->scrolled += lines;
}
@@ -1393,107 +1417,114 @@ screen_write_clearhistory(struct screen_write_ctx *ctx)
grid_clear_history(ctx->s->grid);
}
-/* Clear to start of a collected line. */
-static void
-screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x)
+/* Trim collected items. */
+static struct screen_write_citem *
+screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x,
+ u_int used, int *wrapped)
{
- struct screen_write_collect_item *ci, *tmp;
- size_t size = 0;
- u_int items = 0;
+ struct screen_write_cline *cl = &ctx->s->write_list[y];
+ struct screen_write_citem *ci, *ci2, *tmp, *before = NULL;
+ u_int sx = x, ex = x + used - 1;
+ u_int csx, cex;
- if (TAILQ_EMPTY(&ctx->s->write_list[y].items))
- return;
- TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) {
- switch (ci->type) {
- case CLEAR_START:
- break;
- case CLEAR_END:
- if (ci->x <= x)
- ci->x = x;
+ if (TAILQ_EMPTY(&cl->items))
+ return (NULL);
+ TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
+ csx = ci->x;
+ cex = ci->x + ci->used - 1;
+
+ /* Item is entirely before. */
+ if (cex < sx) {
+ log_debug("%s: %p %u-%u before %u-%u", __func__, ci,
+ csx, cex, sx, ex);
continue;
- case TEXT:
- if (ci->x > x)
- continue;
+ }
+
+ /* Item is entirely after. */
+ if (csx > ex) {
+ log_debug("%s: %p %u-%u after %u-%u", __func__, ci,
+ csx, cex, sx, ex);
+ before = ci;
break;
}
- items++;
- size += ci->used;
- TAILQ_REMOVE(&ctx->s->write_list[y].items, ci, entry);
- free(ci);
- }
- ctx->skipped += size;
- log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items,
- size, y);
-}
-/* Clear to end of a collected line. */
-static void
-screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x)
-{
- struct screen_write_collect_item *ci, *tmp;
- size_t size = 0;
- u_int items = 0;
+ /* Item is entirely inside. */
+ if (csx >= sx && cex <= ex) {
+ log_debug("%s: %p %u-%u inside %u-%u", __func__, ci,
+ csx, cex, sx, ex);
+ TAILQ_REMOVE(&cl->items, ci, entry);
+ screen_write_free_citem(ci);
+ if (csx == 0 && ci->wrapped && wrapped != NULL)
+ *wrapped = 1;
+ continue;
+ }
- if (TAILQ_EMPTY(&ctx->s->write_list[y].items))
- return;
- TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) {
- switch (ci->type) {
- case CLEAR_START:
- if (ci->x >= x)
- ci->x = x;
+ /* Item under the start. */
+ if (csx < sx && cex >= sx && cex <= ex) {
+ log_debug("%s: %p %u-%u start %u-%u", __func__, ci,
+ csx, cex, sx, ex);
+ ci->used = sx - csx;
+ log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
+ ci->x + ci->used + 1);
continue;
- case CLEAR_END:
- break;
- case TEXT:
- if (ci->x < x)
- continue;
+ }
+
+ /* Item covers the end. */
+ if (cex > ex && csx >= sx && csx <= ex) {
+ log_debug("%s: %p %u-%u end %u-%u", __func__, ci,
+ csx, cex, sx, ex);
+ ci->x = ex + 1;
+ ci->used = cex - ex;
+ log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
+ ci->x + ci->used + 1);
+ before = ci;
break;
}
- items++;
- size += ci->used;
- TAILQ_REMOVE(&ctx->s->write_list[y].items, ci, entry);
- free(ci);
+
+ /* Item must cover both sides. */
+ log_debug("%s: %p %u-%u under %u-%u", __func__, ci,
+ csx, cex, sx, ex);
+ ci2 = screen_write_get_citem();
+ ci2->type = ci->type;
+ ci2->bg = ci->bg;
+ memcpy(&ci2->gc, &ci->gc, sizeof ci2->gc);
+ TAILQ_INSERT_AFTER(&cl->items, ci, ci2, entry);
+
+ ci->used = sx - csx;
+ ci2->x = ex + 1;
+ ci2->used = cex - ex;
+
+ log_debug("%s: %p now %u-%u (%p) and %u-%u (%p)", __func__, ci,
+ ci->x, ci->x + ci->used - 1, ci, ci2->x,
+ ci2->x + ci2->used - 1, ci2);
+ before = ci2;
+ break;
}
- ctx->skipped += size;
- log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items,
- size, y);
+ return (before);
}
/* Clear collected lines. */
static void
screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
{
- struct screen_write_collect_item *ci, *tmp;
- struct screen_write_collect_line *cl;
- u_int i, items;
- size_t size;
+ struct screen_write_cline *cl;
+ u_int i;
for (i = y; i < y + n; i++) {
- if (TAILQ_EMPTY(&ctx->s->write_list[i].items))
- continue;
- items = 0;
- size = 0;
cl = &ctx->s->write_list[i];
- TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
- items++;
- size += ci->used;
- TAILQ_REMOVE(&cl->items, ci, entry);
- free(ci);
- }
- ctx->skipped += size;
- log_debug("%s: dropped %u items (%zu bytes) (line %u)",
- __func__, items, size, y);
+ TAILQ_CONCAT(&screen_write_citem_freelist, &cl->items, entry);
}
}
/* Scroll collected lines up. */
static void
-screen_write_collect_scroll(struct screen_write_ctx *ctx)
+screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg)
{
- struct screen *s = ctx->s;
- struct screen_write_collect_line *cl;
- u_int y;
- char *saved;
+ struct screen *s = ctx->s;
+ struct screen_write_cline *cl;
+ u_int y;
+ char *saved;
+ struct screen_write_citem *ci;
log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
s->rupper, s->rlower);
@@ -1503,11 +1534,16 @@ screen_write_collect_scroll(struct screen_write_ctx *ctx)
for (y = s->rupper; y < s->rlower; y++) {
cl = &ctx->s->write_list[y + 1];
TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry);
- ctx->s->write_list[y].bg = cl->bg;
ctx->s->write_list[y].data = cl->data;
}
- ctx->s->write_list[s->rlower].bg = 1 + 8;
ctx->s->write_list[s->rlower].data = saved;
+
+ ci = screen_write_get_citem();
+ ci->x = 0;
+ ci->used = screen_size_x(s);
+ ci->type = CLEAR;
+ ci->bg = bg;
+ TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry);
}
/* Flush collected lines. */
@@ -1515,12 +1551,11 @@ static void
screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
const char *from)
{
- struct screen *s = ctx->s;
- struct screen_write_collect_item *ci, *tmp;
- struct screen_write_collect_line *cl;
- u_int y, cx, cy, items = 0;
- struct tty_ctx ttyctx;
- size_t written = 0;
+ struct screen *s = ctx->s;
+ struct screen_write_citem *ci, *tmp;
+ struct screen_write_cline *cl;
+ u_int y, cx, cy, last, items = 0;
+ struct tty_ctx ttyctx;
if (ctx->scrolled != 0) {
log_debug("%s: scrolled %u (region %u-%u)", __func__,
@@ -1542,23 +1577,18 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
cx = s->cx; cy = s->cy;
for (y = 0; y < screen_size_y(s); y++) {
cl = &ctx->s->write_list[y];
- if (cl->bg != 0) {
- screen_write_set_cursor(ctx, 0, y);
- screen_write_initctx(ctx, &ttyctx, 1);
- ttyctx.bg = cl->bg - 1;
- tty_write(tty_cmd_clearline, &ttyctx);
- }
+ last = UINT_MAX;
TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
+ if (last != UINT_MAX && ci->x <= last) {
+ fatalx("collect list not in order: %u <= %u",
+ ci->x, last);
+ }
screen_write_set_cursor(ctx, ci->x, y);
- if (ci->type == CLEAR_END) {
- log_debug("XXX %u %u", ci->x, ci->bg);
+ if (ci->type == CLEAR) {
screen_write_initctx(ctx, &ttyctx, 1);
ttyctx.bg = ci->bg;
- tty_write(tty_cmd_clearendofline, &ttyctx);
- } else if (ci->type == CLEAR_START) {
- screen_write_initctx(ctx, &ttyctx, 1);
- ttyctx.bg = ci->bg;
- tty_write(tty_cmd_clearstartofline, &ttyctx);
+ ttyctx.num = ci->used;
+ tty_write(tty_cmd_clearcharacter, &ttyctx);
} else {
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.cell = &ci->gc;
@@ -1567,38 +1597,41 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
ttyctx.num = ci->used;
tty_write(tty_cmd_cells, &ttyctx);
}
-
items++;
- written += ci->used;
TAILQ_REMOVE(&cl->items, ci, entry);
- free(ci);
+ screen_write_free_citem(ci);
+ last = ci->x;
}
- cl->bg = 0;
}
s->cx = cx; s->cy = cy;
- log_debug("%s: flushed %u items (%zu bytes) (%s)", __func__, items,
- written, from);
- ctx->written += written;
+ log_debug("%s: flushed %u items (%s)", __func__, items, from);
}
/* Finish and store collected cells. */
void
screen_write_collect_end(struct screen_write_ctx *ctx)
{
- struct screen *s = ctx->s;
- struct screen_write_collect_item *ci = ctx->item;
- struct screen_write_collect_line *cl = &s->write_list[s->cy];
- struct grid_cell gc;
- u_int xx;
+ struct screen *s = ctx->s;
+ struct screen_write_citem *ci = ctx->item, *before;
+ struct screen_write_cline *cl = &s->write_list[s->cy];
+ struct grid_cell gc;
+ u_int xx;
+ int wrapped = ci->wrapped;
if (ci->used == 0)
return;
+ before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used,
+ &wrapped);
ci->x = s->cx;
- TAILQ_INSERT_TAIL(&cl->items, ci, entry);
- ctx->item = xcalloc(1, sizeof *ctx->item);
+ ci->wrapped = wrapped;
+ if (before == NULL)
+ TAILQ_INSERT_TAIL(&cl->items, ci, entry);
+ else
+ TAILQ_INSERT_BEFORE(before, ci, entry);
+ ctx->item = screen_write_get_citem();
log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used,
(int)ci->used, cl->data + ci->x, s->cx, s->cy);
@@ -1634,10 +1667,10 @@ void
screen_write_collect_add(struct screen_write_ctx *ctx,
const struct grid_cell *gc)
{
- struct screen *s = ctx->s;
- struct screen_write_collect_item *ci;
- u_int sx = screen_size_x(s);
- int collect;
+ struct screen *s = ctx->s;
+ struct screen_write_citem *ci;
+ u_int sx = screen_size_x(s);
+ int collect;
/*
* Don't need to check that the attributes and whatnot are still the
@@ -1662,7 +1695,6 @@ screen_write_collect_add(struct screen_write_ctx *ctx,
screen_write_cell(ctx, gc);
return;
}
- ctx->cells++;
if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx)
screen_write_collect_end(ctx);
@@ -1699,7 +1731,6 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
/* Ignore padding cells. */
if (gc->flags & GRID_FLAG_PADDING)
return;
- ctx->cells++;
/* If the width is zero, combine onto the previous character. */
if (width == 0) {
@@ -1826,9 +1857,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
} else
ttyctx.cell = gc;
tty_write(tty_cmd_cell, &ttyctx);
- ctx->written++;
- } else
- ctx->skipped++;
+ }
}
/* Combine a UTF-8 zero-width character onto the previous. */
@@ -1974,6 +2003,8 @@ screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
return;
+
+ screen_write_collect_flush(ctx, 0, __func__);
screen_alternate_on(ctx->s, gc, cursor);
screen_write_initctx(ctx, &ttyctx, 1);
@@ -1990,6 +2021,8 @@ screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
return;
+
+ screen_write_collect_flush(ctx, 0, __func__);
screen_alternate_off(ctx->s, gc, cursor);
screen_write_initctx(ctx, &ttyctx, 1);
diff --git a/screen.c b/screen.c
index b55c9f87..db2590cc 100644
--- a/screen.c
+++ b/screen.c
@@ -573,7 +573,14 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
void
screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
{
- u_int sx, sy;
+ u_int sx = screen_size_x(s), sy = screen_size_y(s);
+
+ /*
+ * If the current size is different, temporarily resize to the old size
+ * before copying back.
+ */
+ if (s->saved_grid != NULL)
+ screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 1);
/*
* Restore the cursor position and cell. This happens even if not
@@ -581,29 +588,23 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
*/
if (cursor && s->saved_cx != UINT_MAX && s->saved_cy != UINT_MAX) {
s->cx = s->saved_cx;
- if (s->cx > screen_size_x(s) - 1)
- s->cx = screen_size_x(s) - 1;
s->cy = s->saved_cy;
- if (s->cy > screen_size_y(s) - 1)
- s->cy = screen_size_y(s) - 1;
if (gc != NULL)
memcpy(gc, &s->saved_cell, sizeof *gc);
}
- if (s->saved_grid == NULL)
+ /* If not in the alternate screen, do nothing more. */
+ if (s->saved_grid == NULL) {
+ if (s->cx > screen_size_x(s) - 1)
+ s->cx = screen_size_x(s) - 1;
+ if (s->cy > screen_size_y(s) - 1)
+ s->cy = screen_size_y(s) - 1;
return;
- sx = screen_size_x(s);
- sy = screen_size_y(s);
-
- /*
- * If the current size is bigger, temporarily resize to the old size
- * before copying back.
- */
- if (sy > s->saved_grid->sy)
- screen_resize(s, sx, s->saved_grid->sy, 1);
+ }
/* Restore the saved grid. */
- grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0, sy);
+ grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0,
+ s->saved_grid->sy);
/*
* Turn history back on (so resize can use it) and then resize back to
@@ -611,9 +612,13 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
*/
if (s->saved_flags & GRID_HISTORY)
s->grid->flags |= GRID_HISTORY;
- if (sy > s->saved_grid->sy || sx != s->saved_grid->sx)
- screen_resize(s, sx, sy, 1);
+ screen_resize(s, sx, sy, 1);
grid_destroy(s->saved_grid);
s->saved_grid = NULL;
+
+ if (s->cx > screen_size_x(s) - 1)
+ s->cx = screen_size_x(s) - 1;
+ if (s->cy > screen_size_y(s) - 1)
+ s->cy = screen_size_y(s) - 1;
}
diff --git a/server-client.c b/server-client.c
index 66d0d577..96e1b584 100644
--- a/server-client.c
+++ b/server-client.c
@@ -21,7 +21,6 @@
#include <sys/uio.h>
#include <errno.h>
-#include <event.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
@@ -40,6 +39,7 @@ static void server_client_repeat_timer(int, short, void *);
static void server_client_click_timer(int, short, void *);
static void server_client_check_exit(struct client *);
static void server_client_check_redraw(struct client *);
+static void server_client_check_modes(struct client *);
static void server_client_set_title(struct client *);
static void server_client_reset_state(struct client *);
static int server_client_assume_paste(struct session *);
@@ -48,12 +48,6 @@ static void server_client_dispatch(struct imsg *, void *);
static void server_client_dispatch_command(struct client *, struct imsg *);
static void server_client_dispatch_identify(struct client *, struct imsg *);
static void server_client_dispatch_shell(struct client *);
-static void server_client_dispatch_write_ready(struct client *,
- struct imsg *);
-static void server_client_dispatch_read_data(struct client *,
- struct imsg *);
-static void server_client_dispatch_read_done(struct client *,
- struct imsg *);
/* Compare client windows. */
static int
@@ -310,6 +304,7 @@ server_client_lost(struct client *c)
free(c->term_name);
free(c->term_type);
+ tty_term_free_list(c->term_caps, c->term_ncaps);
status_free(c);
@@ -1353,6 +1348,7 @@ server_client_loop(void)
TAILQ_FOREACH(c, &clients, entry) {
server_client_check_exit(c);
if (c->session != NULL) {
+ server_client_check_modes(c);
server_client_check_redraw(c);
server_client_reset_state(c);
}
@@ -1777,11 +1773,11 @@ server_client_check_exit(struct client *c)
switch (c->exit_type) {
case CLIENT_EXIT_RETURN:
- if (c->exit_message != NULL) {
+ if (c->exit_message != NULL)
msize = strlen(c->exit_message) + 1;
- size = (sizeof c->retval) + msize;
- } else
- size = (sizeof c->retval);
+ else
+ msize = 0;
+ size = (sizeof c->retval) + msize;
data = xmalloc(size);
memcpy(data, &c->retval, sizeof c->retval);
if (c->exit_message != NULL)
@@ -1808,6 +1804,28 @@ server_client_redraw_timer(__unused int fd, __unused short events,
log_debug("redraw timer fired");
}
+/*
+ * Check if modes need to be updated. Only modes in the current window are
+ * updated and it is done when the status line is redrawn.
+ */
+static void
+server_client_check_modes(struct client *c)
+{
+ struct window *w = c->session->curw->window;
+ struct window_pane *wp;
+ struct window_mode_entry *wme;
+
+ if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
+ return;
+ if (~c->flags & CLIENT_REDRAWSTATUS)
+ return;
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ wme = TAILQ_FIRST(&wp->modes);
+ if (wme != NULL && wme->mode->update != NULL)
+ wme->mode->update(wme);
+ }
+}
+
/* Check for client redraws. */
static void
server_client_check_redraw(struct client *c)
@@ -1977,16 +1995,17 @@ server_client_dispatch(struct imsg *imsg, void *arg)
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
switch (imsg->hdr.type) {
+ case MSG_IDENTIFY_CLIENTPID:
+ case MSG_IDENTIFY_CWD:
+ case MSG_IDENTIFY_ENVIRON:
case MSG_IDENTIFY_FEATURES:
case MSG_IDENTIFY_FLAGS:
case MSG_IDENTIFY_LONGFLAGS:
- case MSG_IDENTIFY_TERM:
- case MSG_IDENTIFY_TTYNAME:
- case MSG_IDENTIFY_CWD:
case MSG_IDENTIFY_STDIN:
case MSG_IDENTIFY_STDOUT:
- case MSG_IDENTIFY_ENVIRON:
- case MSG_IDENTIFY_CLIENTPID:
+ case MSG_IDENTIFY_TERM:
+ case MSG_IDENTIFY_TERMINFO:
+ case MSG_IDENTIFY_TTYNAME:
case MSG_IDENTIFY_DONE:
server_client_dispatch_identify(c, imsg);
break;
@@ -2044,13 +2063,13 @@ server_client_dispatch(struct imsg *imsg, void *arg)
server_client_dispatch_shell(c);
break;
case MSG_WRITE_READY:
- server_client_dispatch_write_ready(c, imsg);
+ file_write_ready(&c->files, imsg);
break;
case MSG_READ:
- server_client_dispatch_read_data(c, imsg);
+ file_read_data(&c->files, imsg);
break;
case MSG_READ_DONE:
- server_client_dispatch_read_done(c, imsg);
+ file_read_done(&c->files, imsg);
break;
}
}
@@ -2180,6 +2199,14 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
c->term_name = xstrdup(data);
log_debug("client %p IDENTIFY_TERM %s", c, data);
break;
+ case MSG_IDENTIFY_TERMINFO:
+ if (datalen == 0 || data[datalen - 1] != '\0')
+ fatalx("bad MSG_IDENTIFY_TERMINFO string");
+ c->term_caps = xreallocarray(c->term_caps, c->term_ncaps + 1,
+ sizeof *c->term_caps);
+ c->term_caps[c->term_ncaps++] = xstrdup(data);
+ log_debug("client %p IDENTIFY_TERMINFO %s", c, data);
+ break;
case MSG_IDENTIFY_TTYNAME:
if (datalen == 0 || data[datalen - 1] != '\0')
fatalx("bad MSG_IDENTIFY_TTYNAME string");
@@ -2280,71 +2307,6 @@ server_client_dispatch_shell(struct client *c)
proc_kill_peer(c->peer);
}
-/* Handle write ready message. */
-static void
-server_client_dispatch_write_ready(struct client *c, struct imsg *imsg)
-{
- struct msg_write_ready *msg = imsg->data;
- size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
- struct client_file find, *cf;
-
- if (msglen != sizeof *msg)
- fatalx("bad MSG_WRITE_READY size");
- find.stream = msg->stream;
- if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
- return;
- if (msg->error != 0) {
- cf->error = msg->error;
- file_fire_done(cf);
- } else
- file_push(cf);
-}
-
-/* Handle read data message. */
-static void
-server_client_dispatch_read_data(struct client *c, struct imsg *imsg)
-{
- struct msg_read_data *msg = imsg->data;
- size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
- struct client_file find, *cf;
- void *bdata = msg + 1;
- size_t bsize = msglen - sizeof *msg;
-
- if (msglen < sizeof *msg)
- fatalx("bad MSG_READ_DATA size");
- find.stream = msg->stream;
- if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
- return;
-
- log_debug("%s: file %d read %zu bytes", c->name, cf->stream, bsize);
- if (cf->error == 0) {
- if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
- cf->error = ENOMEM;
- file_fire_done(cf);
- } else
- file_fire_read(cf);
- }
-}
-
-/* Handle read done message. */
-static void
-server_client_dispatch_read_done(struct client *c, struct imsg *imsg)
-{
- struct msg_read_done *msg = imsg->data;
- size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
- struct client_file find, *cf;
-
- if (msglen != sizeof *msg)
- fatalx("bad MSG_READ_DONE size");
- find.stream = msg->stream;
- if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
- return;
-
- log_debug("%s: file %d read done", c->name, cf->stream);
- cf->error = msg->error;
- file_fire_done(cf);
-}
-
/* Get client working directory. */
const char *
server_client_get_cwd(struct client *c, struct session *s)
@@ -2432,6 +2394,8 @@ server_client_get_flags(struct client *c)
*s = '\0';
if (c->flags & CLIENT_ATTACHED)
strlcat(s, "attached,", sizeof s);
+ if (c->flags & CLIENT_FOCUSED)
+ strlcat(s, "focused,", sizeof s);
if (c->flags & CLIENT_CONTROL)
strlcat(s, "control-mode,", sizeof s);
if (c->flags & CLIENT_IGNORESIZE)
diff --git a/server-fn.c b/server-fn.c
index e3cc181f..656de08e 100644
--- a/server-fn.c
+++ b/server-fn.c
@@ -312,6 +312,7 @@ server_destroy_pane(struct window_pane *wp, int notify)
struct grid_cell gc;
time_t t;
char tim[26];
+ int remain_on_exit;
if (wp->fd != -1) {
#ifdef HAVE_UTEMPTER
@@ -323,10 +324,17 @@ server_destroy_pane(struct window_pane *wp, int notify)
wp->fd = -1;
}
- if (options_get_number(wp->options, "remain-on-exit")) {
- if (~wp->flags & PANE_STATUSREADY)
- return;
-
+ remain_on_exit = options_get_number(wp->options, "remain-on-exit");
+ if (remain_on_exit != 0 && (~wp->flags & PANE_STATUSREADY))
+ return;
+ switch (remain_on_exit) {
+ case 0:
+ break;
+ case 2:
+ if (WIFEXITED(wp->status) && WEXITSTATUS(wp->status) == 0)
+ break;
+ /* FALLTHROUGH */
+ case 1:
if (wp->flags & PANE_STATUSDRAWN)
return;
wp->flags |= PANE_STATUSDRAWN;
@@ -394,9 +402,8 @@ server_destroy_session_group(struct session *s)
static struct session *
server_next_session(struct session *s)
{
- struct session *s_loop, *s_out;
+ struct session *s_loop, *s_out = NULL;
- s_out = NULL;
RB_FOREACH(s_loop, sessions, &sessions) {
if (s_loop == s)
continue;
@@ -407,17 +414,35 @@ server_next_session(struct session *s)
return (s_out);
}
+static struct session *
+server_next_detached_session(struct session *s)
+{
+ struct session *s_loop, *s_out = NULL;
+
+ RB_FOREACH(s_loop, sessions, &sessions) {
+ if (s_loop == s || s_loop->attached)
+ continue;
+ if (s_out == NULL ||
+ timercmp(&s_loop->activity_time, &s_out->activity_time, <))
+ s_out = s_loop;
+ }
+ return (s_out);
+}
+
void
server_destroy_session(struct session *s)
{
struct client *c;
struct session *s_new;
+ int detach_on_destroy;
- if (!options_get_number(s->options, "detach-on-destroy"))
+ detach_on_destroy = options_get_number(s->options, "detach-on-destroy");
+ if (detach_on_destroy == 0)
s_new = server_next_session(s);
+ else if (detach_on_destroy == 2)
+ s_new = server_next_detached_session(s);
else
s_new = NULL;
-
TAILQ_FOREACH(c, &clients, entry) {
if (c->session != s)
continue;
diff --git a/server.c b/server.c
index 9a1875f4..0260898c 100644
--- a/server.c
+++ b/server.c
@@ -24,7 +24,6 @@
#include <sys/wait.h>
#include <errno.h>
-#include <event.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
@@ -155,35 +154,22 @@ int
server_start(struct tmuxproc *client, int flags, struct event_base *base,
int lockfd, char *lockfile)
{
- int pair[2];
- sigset_t set, oldset;
- struct client *c = NULL;
- char *cause = NULL;
+ int fd;
+ sigset_t set, oldset;
+ struct client *c = NULL;
+ char *cause = NULL;
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
if (~flags & CLIENT_NOFORK) {
- if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
- fatal("socketpair failed");
-
- switch (fork()) {
- case -1:
- fatal("fork failed");
- case 0:
- break;
- default:
+ if (proc_fork_and_daemon(&fd) != 0) {
sigprocmask(SIG_SETMASK, &oldset, NULL);
- close(pair[1]);
- return (pair[0]);
+ return (fd);
}
- close(pair[0]);
- if (daemon(1, 0) != 0)
- fatal("daemon failed");
}
-
- server_client_flags = flags;
proc_clear_signals(client, 0);
+ server_client_flags = flags;
if (event_reinit(base) != 0)
fatalx("event_reinit failed");
@@ -212,7 +198,7 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base,
if (server_fd != -1)
server_update_socket();
if (~flags & CLIENT_NOFORK)
- c = server_client_create(pair[1]);
+ c = server_client_create(fd);
else
options_set_number(global_options, "exit-empty", 0);
diff --git a/spawn.c b/spawn.c
index 7c279eff..c47b11f4 100644
--- a/spawn.c
+++ b/spawn.c
@@ -182,7 +182,7 @@ spawn_window(struct spawn_context *sc, char **cause)
NULL);
options_set_number(w->options, "automatic-rename", 0);
} else
- w->name = xstrdup(default_window_name(w));
+ w->name = default_window_name(w);
}
/* Switch to the new window if required. */
@@ -263,8 +263,9 @@ spawn_pane(struct spawn_context *sc, char **cause)
}
/*
- * Now we have a pane with nothing running in it ready for the new process.
- * Work out the command and arguments and store the working directory.
+ * Now we have a pane with nothing running in it ready for the new
+ * process. Work out the command and arguments and store the working
+ * directory.
*/
if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN)) {
cmd = options_get_string(s->options, "default-command");
@@ -377,10 +378,10 @@ spawn_pane(struct spawn_context *sc, char **cause)
* Child process. Change to the working directory or home if that
* fails.
*/
- if (chdir(new_wp->cwd) != 0) {
- if ((tmp = find_home()) == NULL || chdir(tmp) != 0)
- chdir("/");
- }
+ if (chdir(new_wp->cwd) != 0 &&
+ ((tmp = find_home()) == NULL || chdir(tmp) != 0) &&
+ chdir("/") != 0)
+ fatal("chdir failed");
/*
* Update terminal escape characters from the session if available and
diff --git a/status.c b/status.c
index 668c0a3b..154d9452 100644
--- a/status.c
+++ b/status.c
@@ -543,7 +543,7 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs,
prompt_free_cb freecb, void *data, int flags)
{
struct format_tree *ft;
- char *tmp, *cp;
+ char *tmp;
if (fs != NULL)
ft = format_create_from_state(NULL, c, fs);
@@ -563,7 +563,13 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs,
c->prompt_string = format_expand_time(ft, msg);
- c->prompt_buffer = utf8_fromcstr(tmp);
+ if (flags & PROMPT_INCREMENTAL) {
+ c->prompt_last = xstrdup(tmp);
+ c->prompt_buffer = utf8_fromcstr("");
+ } else {
+ c->prompt_last = NULL;
+ c->prompt_buffer = utf8_fromcstr(tmp);
+ }
c->prompt_index = utf8_strlen(c->prompt_buffer);
c->prompt_inputcb = inputcb;
@@ -579,11 +585,8 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs,
c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
c->flags |= CLIENT_REDRAWSTATUS;
- if ((flags & PROMPT_INCREMENTAL) && *tmp != '\0') {
- xasprintf(&cp, "=%s", tmp);
- c->prompt_inputcb(c, c->prompt_data, cp, 0);
- free(cp);
- }
+ if (flags & PROMPT_INCREMENTAL)
+ c->prompt_inputcb(c, c->prompt_data, "=", 0);
free(tmp);
format_free(ft);
@@ -599,6 +602,9 @@ status_prompt_clear(struct client *c)
if (c->prompt_freecb != NULL && c->prompt_data != NULL)
c->prompt_freecb(c->prompt_data);
+ free(c->prompt_last);
+ c->prompt_last = NULL;
+
free(c->prompt_string);
c->prompt_string = NULL;
@@ -1260,17 +1266,27 @@ process_key:
status_prompt_clear(c);
break;
case '\022': /* C-r */
- if (c->prompt_flags & PROMPT_INCREMENTAL) {
+ if (~c->prompt_flags & PROMPT_INCREMENTAL)
+ break;
+ if (c->prompt_buffer[0].size == 0) {
+ prefix = '=';
+ free (c->prompt_buffer);
+ c->prompt_buffer = utf8_fromcstr(c->prompt_last);
+ c->prompt_index = utf8_strlen(c->prompt_buffer);
+ } else
prefix = '-';
- goto changed;
- }
- break;
+ goto changed;
case '\023': /* C-s */
- if (c->prompt_flags & PROMPT_INCREMENTAL) {
+ if (~c->prompt_flags & PROMPT_INCREMENTAL)
+ break;
+ if (c->prompt_buffer[0].size == 0) {
+ prefix = '=';
+ free (c->prompt_buffer);
+ c->prompt_buffer = utf8_fromcstr(c->prompt_last);
+ c->prompt_index = utf8_strlen(c->prompt_buffer);
+ } else
prefix = '+';
- goto changed;
- }
- break;
+ goto changed;
default:
goto append_key;
}
@@ -1303,12 +1319,14 @@ append_key:
}
if (c->prompt_flags & PROMPT_SINGLE) {
- s = utf8_tocstr(c->prompt_buffer);
- if (strlen(s) != 1)
- status_prompt_clear(c);
- else if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
+ if (utf8_strlen(c->prompt_buffer) != 1)
status_prompt_clear(c);
- free(s);
+ else {
+ s = utf8_tocstr(c->prompt_buffer);
+ if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
+ status_prompt_clear(c);
+ free(s);
+ }
}
changed:
diff --git a/tmux.1 b/tmux.1
index 10d1fa92..4611cc20 100644
--- a/tmux.1
+++ b/tmux.1
@@ -191,6 +191,11 @@ directories are missing).
Behave as a login shell.
This flag currently has no effect and is for compatibility with other shells
when using tmux as a login shell.
+.It Fl N
+Do not start the server even if the command would normally do so (for example
+.Ic new-session
+or
+.Ic start-server ) .
.It Fl S Ar socket-path
Specify a full alternative path to the server socket.
If
@@ -517,6 +522,67 @@ Commands separated by semicolons together form a
- if a command in the sequence encounters an error, no subsequent commands are
executed.
.Pp
+It is recommended that a semicolon used as a command separator should be
+written as an individual token, for example from
+.Xr sh 1 :
+.Bd -literal -offset indent
+$ tmux neww \\; splitw
+.Ed
+.Pp
+Or:
+.Bd -literal -offset indent
+$ tmux neww ';' splitw
+.Ed
+.Pp
+Or from the tmux command prompt:
+.Bd -literal -offset indent
+neww ; splitw
+.Ed
+.Pp
+However, a trailing semicolon is also interpreted as a command separator,
+for example in these
+.Xr sh 1
+commands:
+.Bd -literal -offset indent
+$ tmux neww\e\e; splitw
+.Ed
+.Pp
+Or:
+.Bd -literal -offset indent
+$ tmux 'neww;' splitw
+.Ed
+.Pp
+As in these examples, when running tmux from the shell extra care must be taken
+to properly quote semicolons:
+.Bl -enum -offset Ds
+.It
+Semicolons that should be interpreted as a command separator
+should be escaped according to the shell conventions.
+For
+.Xr sh 1
+this typically means quoted (such as
+.Ql neww ';' splitw )
+or escaped (such as
+.Ql neww \e\e\e\e; splitw ) .
+.It
+Individual semicolons or trailing semicolons that should be interpreted as
+arguments should be escaped twice: once according to the shell conventions and
+a second time for
+.Nm ;
+for example:
+.Bd -literal -offset indent
+$ tmux neww 'foo\e\e;' bar
+$ tmux neww foo\e\e\e\e; bar
+.Ed
+.It
+Semicolons that are not individual tokens or trailing another token should only
+be escaped once according to shell conventions; for example:
+.Bd -literal -offset indent
+$ tmux neww 'foo-;-bar'
+$ tmux neww foo-\e\e;-bar
+.Ed
+.El
+.Pp
Comments are marked by the unquoted # character - any remaining text after a
comment is ignored until the end of the line.
.Pp
@@ -867,12 +933,12 @@ arguments are
commands.
This may be a single argument passed to the shell, for example:
.Bd -literal -offset indent
-new-window 'vi /etc/passwd'
+new-window 'vi ~/.tmux.conf'
.Ed
.Pp
Will run:
.Bd -literal -offset indent
-/bin/sh -c 'vi /etc/passwd'
+/bin/sh -c 'vi ~/.tmux.conf'
.Ed
.Pp
Additionally, the
@@ -889,7 +955,7 @@ to be given as multiple arguments and executed directly (without
This can avoid issues with shell quoting.
For example:
.Bd -literal -offset indent
-$ tmux new-window vi /etc/passwd
+$ tmux new-window vi ~/.tmux.conf
.Ed
.Pp
Will run
@@ -935,7 +1001,7 @@ $ tmux kill-window -t :1
$ tmux new-window \e; split-window -d
-$ tmux new-session -d 'vi /etc/passwd' \e; split-window -d \e; attach
+$ tmux new-session -d 'vi ~/.tmux.conf' \e; split-window -d \e; attach
.Ed
.Sh CLIENTS AND SESSIONS
The
@@ -1357,7 +1423,7 @@ a pane ID such as
.Ql %0 ;
.Ql %*
for all panes in the attached session;
-an window ID such as
+a window ID such as
.Ql @0 ;
or
.Ql @*
@@ -1648,10 +1714,15 @@ The following commands are supported in copy mode:
.It Li "page-down" Ta "C-f" Ta "PageDown"
.It Li "page-down-and-cancel" Ta "" Ta ""
.It Li "page-up" Ta "C-b" Ta "PageUp"
+.It Li "pipe [<command>] [<prefix>]" Ta "" Ta ""
+.It Li "pipe-no-clear [<command>] [<prefix>]" Ta "" Ta ""
+.It Li "pipe-and-cancel [<command>] [<prefix>]" Ta "" Ta ""
.It Li "previous-matching-bracket" Ta "" Ta "M-C-b"
.It Li "previous-paragraph" Ta "{" Ta "M-{"
.It Li "previous-space" Ta "B" Ta ""
.It Li "previous-word" Ta "b" Ta "M-b"
+.It Li "rectangle-on" Ta "" Ta ""
+.It Li "rectangle-off" Ta "" Ta ""
.It Li "rectangle-toggle" Ta "v" Ta "R"
.It Li "refresh-from-pane" Ta "r" Ta "r"
.It Li "scroll-down" Ta "C-e" Ta "C-Down"
@@ -1701,7 +1772,9 @@ so buffers are named
.Ql buffer1
and so on).
Pipe commands take a command argument which is the command to which the
-copied text is piped.
+selected text is piped.
+.Ql copy-pipe
+variants also copy the selection.
The
.Ql -and-cancel
variants of some commands exit copy mode after they have completed (for copy
@@ -2098,7 +2171,7 @@ starts without the option information.
This command works only if at least one client is attached.
.It Xo
.Ic display-panes
-.Op Fl b
+.Op Fl bN
.Op Fl d Ar duration
.Op Fl t Ar target-client
.Op Ar template
@@ -2111,7 +2184,9 @@ See the
and
.Ic display-panes-active-colour
session options.
-The indicator is closed when a key is pressed or
+The indicator is closed when a key is pressed (unless
+.Fl N
+is given) or
.Ar duration
milliseconds have passed.
If
@@ -2339,7 +2414,7 @@ the
.Ic base-index
option.
.It Xo Ic new-window
-.Op Fl abdkP
+.Op Fl abdkPS
.Op Fl c Ar start-directory
.Op Fl e Ar environment
.Op Fl F Ar format
@@ -2368,6 +2443,14 @@ represents the window to be created; if the target already exists an error is
shown, unless the
.Fl k
flag is used, in which case it is destroyed.
+If
+.Fl S
+is given and a window named
+.Ar window-name
+already exists, it is selected (unless
+.Fl d
+is also given in which case the command does nothing).
+.Pp
.Ar shell-command
is the command to execute.
If
@@ -3134,7 +3217,7 @@ abc123
Commands which set options are as follows:
.Bl -tag -width Ds
.It Xo Ic set-option
-.Op Fl aFgopqsuw
+.Op Fl aFgopqsuUw
.Op Fl t Ar target-pane
.Ar option Ar value
.Xc
@@ -3167,6 +3250,11 @@ 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).
+.Fl U
+unsets an option (like
+.Fl u )
+but if the option is a pane option also unsets the option on any panes in the
+window.
.Ar value
depends on the option and may be a number, a string, or a flag (on, off, or
omitted to toggle).
@@ -3559,12 +3647,16 @@ The default is 80x24.
If enabled and the session is no longer attached to any clients, it is
destroyed.
.It Xo Ic detach-on-destroy
-.Op Ic on | off
+.Op Ic off | on | no-detached
.Xc
If on (the default), the client is detached when the session it is attached to
is destroyed.
If off, the client is switched to the most recently active of the remaining
sessions.
+If
+.Ic no-detached ,
+the client is detached only if there are no detached sessions; if detached
+sessions exist, the client is switched to the most recently active.
.It Ic display-panes-active-colour Ar colour
Set the colour used by the
.Ic display-panes
@@ -4058,12 +4150,6 @@ see the
section.
Attributes are ignored.
.Pp
-.It Xo Ic synchronize-panes
-.Op Ic on | off
-.Xc
-Duplicate input to any pane to all other panes in the same window (only
-for panes that are not in any special mode).
-.Pp
.It Ic window-status-activity-style Ar style
Set status line style for windows with an activity alert.
For how to specify
@@ -4178,14 +4264,23 @@ interactive application starts and restores it on exit, so that any output
visible before the application starts reappears unchanged after it exits.
.Pp
.It Xo Ic remain-on-exit
-.Op Ic on | off
+.Op Ic on | off | failed
.Xc
A pane with this flag set is not destroyed when the program running in it
exits.
+If set to
+.Ic failed ,
+then only when the program exit status is not zero.
The pane may be reactivated with the
.Ic respawn-pane
command.
.Pp
+.It Xo Ic synchronize-panes
+.Op Ic on | off
+.Xc
+Duplicate input to all other panes in the same window where this option is also
+on (only for panes that are not in any mode).
+.Pp
.It Ic window-active-style Ar style
Set the pane style when it is the active pane.
For how to specify
@@ -4589,7 +4684,9 @@ pads the string to a given width, for example
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
+expands to the length of the variable and
+.Ql w
+to its width when displayed, for example
.Ql #{n:window_name} .
.Pp
Prefixing a time variable with
@@ -4632,7 +4729,12 @@ of the variable respectively.
.Ql q:\&
will escape
.Xr sh 1
-special characters.
+special characters or with a
+.Ql h
+suffix, escape hash characters (so
+.Ql #
+becomes
+.Ql ## ) .
.Ql E:\&
will expand the format twice, for example
.Ql #{E:status-left}
@@ -4658,6 +4760,17 @@ For example, to get a list of windows formatted like the status line:
#{W:#{E:window-status-format} ,#{E:window-status-current-format} }
.Ed
.Pp
+.Ql N:\&
+checks if a window (without any suffix or with the
+.Ql w
+suffix) or a session (with the
+.Ql s
+suffix) name exists, for example
+.Ql `N/w:foo`
+is replaced with 1 if a window named
+.Ql foo
+exists.
+.Pp
A prefix of the form
.Ql s/foo/bar/:\&
will substitute
@@ -4704,6 +4817,7 @@ will be replaced by
The following variables are available, where appropriate:
.Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX"
.It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with"
+.It Li "active_window_index" Ta "" Ta "Index of active window in session"
.It Li "alternate_on" Ta "" Ta "1 if pane is in alternate screen"
.It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen"
.It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen"
@@ -4726,9 +4840,9 @@ The following variables are available, where appropriate:
.It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed"
.It Li "client_readonly" Ta "" Ta "1 if client is readonly"
.It Li "client_session" Ta "" Ta "Name of the client's session"
+.It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any"
.It Li "client_termname" Ta "" Ta "Terminal name of client"
.It Li "client_termtype" Ta "" Ta "Terminal type of client, if available"
-.It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any"
.It Li "client_tty" Ta "" Ta "Pseudo terminal of client"
.It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8"
.It Li "client_width" Ta "" Ta "Width of client"
@@ -4737,6 +4851,7 @@ The following variables are available, where appropriate:
.It Li "command_list_alias" Ta "" Ta "Command alias if listing commands"
.It Li "command_list_name" Ta "" Ta "Command name if listing commands"
.It Li "command_list_usage" Ta "" Ta "Command usage if listing commands"
+.It Li "config_files" Ta "" Ta "List of configuration files loaded"
.It Li "copy_cursor_line" Ta "" Ta "Line the cursor is on in copy mode"
.It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode"
.It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode"
@@ -4759,6 +4874,7 @@ The following variables are available, where appropriate:
.It Li "insert_flag" Ta "" Ta "Pane insert flag"
.It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag"
.It Li "keypad_flag" Ta "" Ta "Pane keypad flag"
+.It Li "last_window_index" Ta "" Ta "Index of last window in session"
.It Li "line" Ta "" Ta "Line number in the list"
.It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag"
.It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag"
@@ -4776,11 +4892,13 @@ The following variables are available, where appropriate:
.It Li "pane_at_left" Ta "" Ta "1 if pane is at the left of window"
.It Li "pane_at_right" Ta "" Ta "1 if pane is at the right of window"
.It Li "pane_at_top" Ta "" Ta "1 if pane is at the top of window"
+.It Li "pane_bg" Ta "" Ta "Pane background colour"
.It Li "pane_bottom" Ta "" Ta "Bottom of pane"
.It Li "pane_current_command" Ta "" Ta "Current command if available"
.It Li "pane_current_path" Ta "" Ta "Current path if available"
.It Li "pane_dead" Ta "" Ta "1 if pane is dead"
.It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane"
+.It Li "pane_fg" Ta "" Ta "Pane foreground colour"
.It Li "pane_format" Ta "" Ta "1 if format is for a pane"
.It Li "pane_height" Ta "" Ta "Height of pane"
.It Li "pane_id" Ta "#D" Ta "Unique pane ID"
@@ -4797,7 +4915,6 @@ The following variables are available, where appropriate:
.It Li "pane_pipe" Ta "" Ta "1 if pane is being piped"
.It Li "pane_right" Ta "" Ta "Right of pane"
.It Li "pane_search_string" Ta "" Ta "Last search string in copy mode"
-.It Li "pane_skipped" Ta "" Ta "Bytes skipped as not visible in pane"
.It Li "pane_start_command" Ta "" Ta "Command pane started with"
.It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized"
.It Li "pane_tabs" Ta "" Ta "Pane tab positions"
@@ -4805,17 +4922,13 @@ The following variables are available, where appropriate:
.It Li "pane_top" Ta "" Ta "Top of pane"
.It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane"
.It Li "pane_width" Ta "" Ta "Width of pane"
-.It Li "pane_written" Ta "" Ta "Bytes written by pane (aside from redrawing)"
.It Li "pid" Ta "" Ta "Server PID"
-.It Li "popup_key" Ta "" Ta "Key pressed in popup"
-.It Li "popup_mouse_x" Ta "" Ta "Mouse X position in popup"
-.It Li "popup_mouse_y" Ta "" Ta "Mouse Y position in popup"
.It Li "rectangle_toggle" Ta "" Ta "1 if rectangle selection is activated"
.It Li "scroll_position" Ta "" Ta "Scroll position in copy mode"
.It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane"
.It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane"
-.It Li "search_present" Ta "" Ta "1 if search started in copy mode"
.It Li "search_match" Ta "" Ta "Search match if any"
+.It Li "search_present" Ta "" Ta "1 if search started in copy mode"
.It Li "selection_active" Ta "" Ta "1 if selection started and changes with the cursor in copy mode"
.It Li "selection_end_x" Ta "" Ta "X position of the end of the selection"
.It Li "selection_end_y" Ta "" Ta "Y position of the end of the selection"
@@ -4858,7 +4971,8 @@ The following variables are available, where appropriate:
.It Li "window_cell_height" Ta "" Ta "Height of each cell in pixels"
.It Li "window_cell_width" Ta "" Ta "Width of each cell in pixels"
.It Li "window_end_flag" Ta "" Ta "1 if window has the highest index"
-.It Li "window_flags" Ta "#F" Ta "Window flags"
+.It Li "window_flags" Ta "#F" Ta "Window flags with # escaped as ##"
+.It Li "window_raw_flags" Ta "" Ta "Window flags with nothing escaped"
.It Li "window_format" Ta "" Ta "1 if format is for a window"
.It Li "window_height" Ta "" Ta "Height of window"
.It Li "window_id" Ta "" Ta "Unique window ID"
@@ -5386,6 +5500,28 @@ Both may be a row or column number, or one of the following special values:
.It Li "S" Ta Fl y Ta "The line above or below the status line"
.El
.Pp
+Or a format, which is expanded including the following additional variables:
+.Bl -column "XXXXXXXXXXXXXXXXXXXXXXXXXX" -offset indent
+.It Sy "Variable name" Ta Sy "Replaced with"
+.It Li "popup_centre_x" Ta "Centered in the client"
+.It Li "popup_centre_y" Ta "Centered in the client"
+.It Li "popup_height" Ta "Height of menu or popup"
+.It Li "popup_mouse_bottom" Ta "Bottom of at the mouse"
+.It Li "popup_mouse_centre_x" Ta "Horizontal centre at the mouse"
+.It Li "popup_mouse_centre_y" Ta "Vertical centre at the mouse"
+.It Li "popup_mouse_top" Ta "Top at the mouse"
+.It Li "popup_mouse_x" Ta "Mouse X position"
+.It Li "popup_mouse_y" Ta "Mouse Y position"
+.It Li "popup_pane_bottom" Ta "Bottom of the pane"
+.It Li "popup_pane_left" Ta "Left of the pane"
+.It Li "popup_pane_right" Ta "Right of the pane"
+.It Li "popup_pane_top" Ta "Top of the pane"
+.It Li "popup_status_line_y" Ta "Above or below the status line"
+.It Li "popup_width" Ta "Width of menu or popup"
+.It Li "popup_window_status_line_x" Ta "At the window position in status line"
+.It Li "popup_window_status_line_y" Ta "At the status line showing the window"
+.El
+.Pp
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.
@@ -5445,58 +5581,24 @@ lists the format variables and their values.
forwards any input read from stdin to the empty pane given by
.Ar target-pane .
.It Xo Ic display-popup
-.Op Fl CEK
+.Op Fl CE
.Op Fl c Ar target-client
.Op Fl d Ar start-directory
.Op Fl h Ar height
-.Op Fl R Ar shell-command
.Op Fl t Ar target-pane
.Op Fl w Ar width
.Op Fl x Ar position
.Op Fl y Ar position
-.Op Ar command Ar line Ar ...
+.Op Ar shell-command
.Xc
.D1 (alias: Ic popup )
-Display a popup on
+Display a popup running
+.Ar shell-command
+on
.Ar target-client .
A popup is a rectangular box drawn over the top of any panes.
Panes are not updated while a popup is present.
-The popup content may be given in two ways:
-.Bl -enum -offset Ds
-.It
-A set of lines as arguments.
-Each line is a format which is expanded using
-.Ar target-pane
-as the target.
-If a line contains newlines it is split into multiple lines.
-Lines may use styles, see the
-.Sx STYLES
-section.
-.It
-A shell command given by
-.Fl R
-which is run and any output shown in the pane.
-.El
.Pp
-The first argument,
-.Ar command ,
-is a
-.Nm
-command which is run when a key is pressed.
-The key is available in the
-.Ql popup_key
-format.
-After
-.Ar command
-is run, the popup is closed.
-It may be empty to discard any key presses.
-If
-.Fl K
-is given together with
-.Fl R ,
-key presses are instead passed to the
-.Fl R
-shell command.
.Fl E
closes the popup automatically when
.Ar shell-command
@@ -5506,14 +5608,6 @@ Two
closes the popup only if
.Ar shell-command
exited with success.
-With
-.Fl K ,
-.Ql Escape
-and
-.Ql C-c
-close the popup unless
-.Fl E
-is also given.
.Pp
.Fl x
and
@@ -5526,11 +5620,7 @@ and
.Fl h
give the width and height - both may be a percentage (followed by
.Ql % ) .
-If omitted, without
-.Fl R
-they are calculated from the given lines and with
-.Fl R
-they use half the terminal size.
+If omitted, half of the terminal size is used.
.Pp
The
.Fl C
@@ -5789,7 +5879,7 @@ Lock each client individually by running the command specified by the
.Ic lock-command
option.
.It Xo Ic run-shell
-.Op Fl b
+.Op Fl bC
.Op Fl d Ar delay
.Op Fl t Ar target-pane
.Op Ar shell-command
@@ -5797,9 +5887,14 @@ option.
.D1 (alias: Ic run )
Execute
.Ar shell-command
-in the background without creating a window.
-Before being executed, shell-command is expanded using the rules specified in
-the
+or (with
+.Fl C )
+a
+.Nm
+command in the background without creating a window.
+Before being executed,
+.Ar shell-command
+is expanded using the rules specified in the
.Sx FORMATS
section.
With
@@ -5809,11 +5904,13 @@ the command is run in the background.
waits for
.Ar delay
seconds before starting the command.
-After the command finishes, any output to stdout is displayed in view mode (in
-the pane specified by
+If
+.Fl C
+is not given, any output to stdout is displayed in view mode (in the pane
+specified by
.Fl t
-or the current pane if omitted).
-If the command doesn't return success, the exit status is also displayed.
+or the current pane if omitted) after the command finishes.
+If the command fails, the exit status is also displayed.
.It Xo Ic wait-for
.Op Fl L | S | U
.Ar channel
@@ -5876,6 +5973,10 @@ option should be used.
An existing extension that tells
.Nm
the terminal supports default colours.
+.It Em \&Bidi
+Tell
+.Nm
+that the terminal supports the VTE bidirectional text extensions.
.It Em \&Cs , Cr
Set the cursor colour.
The first takes a single string argument and is used to set the colour;
diff --git a/tmux.c b/tmux.c
index 066714df..3a49c803 100644
--- a/tmux.c
+++ b/tmux.c
@@ -21,7 +21,6 @@
#include <sys/utsname.h>
#include <errno.h>
-#include <event.h>
#include <fcntl.h>
#include <langinfo.h>
#include <locale.h>
@@ -54,7 +53,7 @@ static __dead void
usage(void)
{
fprintf(stderr,
- "usage: %s [-2CDluvV] [-c shell-command] [-f file] [-L socket-name]\n"
+ "usage: %s [-2CDlNuvV] [-c shell-command] [-f file] [-L socket-name]\n"
" [-S socket-path] [-T features] [command [flags]]\n",
getprogname());
exit(1);
@@ -139,11 +138,12 @@ expand_path(const char *path, const char *home)
return (xstrdup(path));
}
-void
-expand_paths(const char *s, char ***paths, u_int *n)
+static void
+expand_paths(const char *s, char ***paths, u_int *n, int ignore_errors)
{
const char *home = find_home();
char *copy, *next, *tmp, resolved[PATH_MAX], *expanded;
+ char *path;
u_int i;
*paths = NULL;
@@ -159,20 +159,26 @@ expand_paths(const char *s, char ***paths, u_int *n)
if (realpath(expanded, resolved) == NULL) {
log_debug("%s: realpath(\"%s\") failed: %s", __func__,
expanded, strerror(errno));
+ if (ignore_errors) {
+ free(expanded);
+ continue;
+ }
+ path = expanded;
+ } else {
+ path = xstrdup(resolved);
free(expanded);
- continue;
}
- free(expanded);
for (i = 0; i < *n; i++) {
- if (strcmp(resolved, (*paths)[i]) == 0)
+ if (strcmp(path, (*paths)[i]) == 0)
break;
}
if (i != *n) {
- log_debug("%s: duplicate path: %s", __func__, resolved);
+ log_debug("%s: duplicate path: %s", __func__, path);
+ free(path);
continue;
}
*paths = xreallocarray(*paths, (*n) + 1, sizeof *paths);
- (*paths)[(*n)++] = xstrdup(resolved);
+ (*paths)[(*n)++] = path;
}
free(copy);
}
@@ -190,7 +196,7 @@ make_label(const char *label, char **cause)
label = "default";
uid = getuid();
- expand_paths(TMUX_SOCK, &paths, &n);
+ expand_paths(TMUX_SOCK, &paths, &n, 1);
if (n == 0) {
xasprintf(cause, "no suitable socket path");
return (NULL);
@@ -321,10 +327,11 @@ main(int argc, char **argv)
{
char *path = NULL, *label = NULL;
char *cause, **var;
- const char *s, *shell, *cwd;
+ const char *s, *cwd;
int opt, keys, feat = 0;
uint64_t flags = 0;
const struct options_table_entry *oe;
+ u_int i;
if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL &&
setlocale(LC_CTYPE, "C.UTF-8") == NULL) {
@@ -341,7 +348,14 @@ main(int argc, char **argv)
if (**argv == '-')
flags = CLIENT_LOGIN;
- while ((opt = getopt(argc, argv, "2c:CDdf:lL:qS:T:uUvV")) != -1) {
+ global_environ = environ_create();
+ for (var = environ; *var != NULL; var++)
+ environ_put(global_environ, *var, 0);
+ if ((cwd = find_cwd()) != NULL)
+ environ_set(global_environ, "PWD", 0, "%s", cwd);
+ expand_paths(TMUX_CONF, &cfg_files, &cfg_nfiles, 1);
+
+ while ((opt = getopt(argc, argv, "2c:CDdf:lL:NqS:T:uUvV")) != -1) {
switch (opt) {
case '2':
tty_add_features(&feat, "256", ":,");
@@ -359,7 +373,11 @@ main(int argc, char **argv)
flags |= CLIENT_CONTROL;
break;
case 'f':
- set_cfg_file(optarg);
+ for (i = 0; i < cfg_nfiles; i++)
+ free(cfg_files[i]);
+ free(cfg_files);
+ expand_paths(optarg, &cfg_files, &cfg_nfiles, 0);
+ cfg_quiet = 0;
break;
case 'V':
printf("%s %s\n", getprogname(), getversion());
@@ -371,6 +389,9 @@ main(int argc, char **argv)
free(label);
label = xstrdup(optarg);
break;
+ case 'N':
+ flags |= CLIENT_NOSTARTSERVER;
+ break;
case 'q':
break;
case 'S':
@@ -426,12 +447,6 @@ main(int argc, char **argv)
flags |= CLIENT_UTF8;
}
- global_environ = environ_create();
- for (var = environ; *var != NULL; var++)
- environ_put(global_environ, *var, 0);
- if ((cwd = find_cwd()) != NULL)
- environ_set(global_environ, "PWD", 0, "%s", cwd);
-
global_options = options_create(NULL);
global_s_options = options_create(NULL);
global_w_options = options_create(NULL);
@@ -448,8 +463,8 @@ main(int argc, char **argv)
* The default shell comes from SHELL or from the user's passwd entry
* if available.
*/
- shell = getshell();
- options_set_string(global_s_options, "default-shell", 0, "%s", shell);
+ options_set_string(global_s_options, "default-shell", 0, "%s",
+ getshell());
/* Override keys to vi if VISUAL or EDITOR are set. */
if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) {
diff --git a/tmux.h b/tmux.h
index 44ba53f5..69610940 100644
--- a/tmux.h
+++ b/tmux.h
@@ -22,7 +22,6 @@
#include <sys/time.h>
#include <sys/uio.h>
-#include <event.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
@@ -57,8 +56,8 @@ struct mouse_event;
struct options;
struct options_array_item;
struct options_entry;
-struct screen_write_collect_item;
-struct screen_write_collect_line;
+struct screen_write_citem;
+struct screen_write_cline;
struct screen_write_ctx;
struct session;
struct tty_ctx;
@@ -263,6 +262,7 @@ enum tty_code_code {
TTYC_AX,
TTYC_BCE,
TTYC_BEL,
+ TTYC_BIDI,
TTYC_BLINK,
TTYC_BOLD,
TTYC_CIVIS,
@@ -501,6 +501,7 @@ enum msgtype {
MSG_IDENTIFY_FEATURES,
MSG_IDENTIFY_STDOUT,
MSG_IDENTIFY_LONGFLAGS,
+ MSG_IDENTIFY_TERMINFO,
MSG_COMMAND = 200,
MSG_DETACH,
@@ -728,6 +729,13 @@ struct grid {
struct grid_line *linedata;
};
+/* Virtual cursor in a grid. */
+struct grid_reader {
+ struct grid *gd;
+ u_int cx;
+ u_int cy;
+};
+
/* Style alignment. */
enum style_align {
STYLE_ALIGN_DEFAULT,
@@ -789,55 +797,51 @@ struct style {
struct screen_sel;
struct screen_titles;
struct screen {
- char *title;
- char *path;
- struct screen_titles *titles;
+ char *title;
+ char *path;
+ struct screen_titles *titles;
- struct grid *grid; /* grid data */
+ struct grid *grid; /* grid data */
- u_int cx; /* cursor x */
- u_int cy; /* cursor y */
+ u_int cx; /* cursor x */
+ u_int cy; /* cursor y */
- u_int cstyle; /* cursor style */
- char *ccolour; /* cursor colour string */
+ u_int cstyle; /* cursor style */
+ char *ccolour; /* cursor colour string */
- u_int rupper; /* scroll region top */
- u_int rlower; /* scroll region bottom */
+ u_int rupper; /* scroll region top */
+ u_int rlower; /* scroll region bottom */
- int mode;
+ int mode;
- u_int saved_cx;
- u_int saved_cy;
- struct grid *saved_grid;
- struct grid_cell saved_cell;
- int saved_flags;
+ u_int saved_cx;
+ u_int saved_cy;
+ struct grid *saved_grid;
+ struct grid_cell saved_cell;
+ int saved_flags;
- bitstr_t *tabs;
- struct screen_sel *sel;
+ bitstr_t *tabs;
+ struct screen_sel *sel;
- struct screen_write_collect_line *write_list;
+ struct screen_write_cline *write_list;
};
/* Screen write context. */
typedef void (*screen_write_init_ctx_cb)(struct screen_write_ctx *,
struct tty_ctx *);
struct screen_write_ctx {
- struct window_pane *wp;
- struct screen *s;
+ struct window_pane *wp;
+ struct screen *s;
- int flags;
+ int flags;
#define SCREEN_WRITE_SYNC 0x1
- screen_write_init_ctx_cb init_ctx_cb;
- void *arg;
+ screen_write_init_ctx_cb init_ctx_cb;
+ void *arg;
- struct screen_write_collect_item *item;
- u_int scrolled;
- u_int bg;
-
- u_int cells;
- u_int written;
- u_int skipped;
+ struct screen_write_citem *item;
+ u_int scrolled;
+ u_int bg;
};
/* Screen redraw context. */
@@ -889,6 +893,7 @@ struct window_mode {
struct cmd_find_state *, struct args *);
void (*free)(struct window_mode_entry *);
void (*resize)(struct window_mode_entry *, u_int, u_int);
+ void (*update)(struct window_mode_entry *);
void (*key)(struct window_mode_entry *, struct client *,
struct session *, struct winlink *, key_code,
struct mouse_event *);
@@ -995,9 +1000,6 @@ struct window_pane {
char *searchstr;
int searchregex;
- size_t written;
- size_t skipped;
-
int border_gc_set;
struct grid_cell border_gc;
@@ -1539,6 +1541,8 @@ typedef void (*client_file_cb) (struct client *, const char *, int, int,
struct evbuffer *, void *);
struct client_file {
struct client *c;
+ struct tmuxpeer *peer;
+ struct client_files *tree;
int references;
int stream;
@@ -1601,6 +1605,8 @@ struct client {
char *term_name;
int term_features;
char *term_type;
+ char **term_caps;
+ u_int term_ncaps;
char *ttyname;
struct tty tty;
@@ -1629,7 +1635,7 @@ struct client {
#define CLIENT_DEAD 0x200
#define CLIENT_REDRAWBORDERS 0x400
#define CLIENT_READONLY 0x800
-/* 0x1000 unused */
+#define CLIENT_NOSTARTSERVER 0x1000
#define CLIENT_CONTROL 0x2000
#define CLIENT_CONTROLCONTROL 0x4000
#define CLIENT_FOCUSED 0x8000
@@ -1687,6 +1693,7 @@ struct client {
char *prompt_string;
struct utf8_data *prompt_buffer;
+ char *prompt_last;
size_t prompt_index;
prompt_input_cb prompt_inputcb;
prompt_free_cb prompt_freecb;
@@ -1881,8 +1888,6 @@ const char *sig2name(int);
const char *find_cwd(void);
const char *find_home(void);
const char *getversion(void);
-void expand_paths(const char *, char ***, u_int *);
-
/* proc.c */
struct imsg;
@@ -1897,16 +1902,19 @@ struct tmuxpeer *proc_add_peer(struct tmuxproc *, int,
void proc_remove_peer(struct tmuxpeer *);
void proc_kill_peer(struct tmuxpeer *);
void proc_toggle_log(struct tmuxproc *);
+pid_t proc_fork_and_daemon(int *);
/* cfg.c */
extern int cfg_finished;
extern struct client *cfg_client;
+extern char **cfg_files;
+extern u_int cfg_nfiles;
+extern int cfg_quiet;
void start_cfg(void);
int load_cfg(const char *, struct client *, struct cmdq_item *, int,
struct cmdq_item **);
int load_cfg_from_buffer(const void *, size_t, const char *,
struct client *, struct cmdq_item *, int, struct cmdq_item **);
-void set_cfg_file(const char *);
void printflike(1, 2) cfg_add_cause(const char *, ...);
void cfg_print_causes(struct cmdq_item *);
void cfg_show_causes(struct session *);
@@ -1937,7 +1945,7 @@ char *paste_make_sample(struct paste_buffer *);
#define FORMAT_WINDOW 0x40000000U
struct format_tree;
struct format_modifier;
-typedef char *(*format_cb)(struct format_tree *);
+typedef void *(*format_cb)(struct format_tree *);
const char *format_skip(const char *, const char *);
int format_true(const char *);
struct format_tree *format_create(struct client *, struct cmdq_item *, int,
@@ -2060,9 +2068,9 @@ typedef void (*job_free_cb) (void *);
#define JOB_NOWAIT 0x1
#define JOB_KEEPWRITE 0x2
#define JOB_PTY 0x4
-struct job *job_run(const char *, struct session *, const char *,
- job_update_cb, job_complete_cb, job_free_cb, void *, int,
- int, int);
+struct job *job_run(const char *, int, char **, struct session *,
+ const char *, job_update_cb, job_complete_cb, job_free_cb,
+ void *, int, int, int);
void job_free(struct job *);
void job_resize(struct job *, u_int, u_int);
void job_check_died(pid_t, int);
@@ -2163,8 +2171,12 @@ extern struct tty_terms tty_terms;
u_int tty_term_ncodes(void);
void tty_term_apply(struct tty_term *, const char *, int);
void tty_term_apply_overrides(struct tty_term *);
-struct tty_term *tty_term_create(struct tty *, char *, int *, int, char **);
+struct tty_term *tty_term_create(struct tty *, char *, char **, u_int, int *,
+ char **);
void tty_term_free(struct tty_term *);
+int tty_term_read_list(const char *, int, char ***, u_int *,
+ char **);
+void tty_term_free_list(char **, u_int);
int tty_term_has(struct tty_term *, enum tty_code_code);
const char *tty_term_string(struct tty_term *, enum tty_code_code);
const char *tty_term_string1(struct tty_term *, enum tty_code_code, int);
@@ -2370,7 +2382,10 @@ void alerts_check_session(struct session *);
/* file.c */
int file_cmp(struct client_file *, struct client_file *);
RB_PROTOTYPE(client_files, client_file, entry, file_cmp);
-struct client_file *file_create(struct client *, int, client_file_cb, void *);
+struct client_file *file_create_with_peer(struct tmuxpeer *,
+ struct client_files *, int, client_file_cb, void *);
+struct client_file *file_create_with_client(struct client *, int,
+ client_file_cb, void *);
void file_free(struct client_file *);
void file_fire_done(struct client_file *);
void file_fire_read(struct client_file *);
@@ -2383,6 +2398,16 @@ void file_write(struct client *, const char *, int, const void *, size_t,
client_file_cb, void *);
void file_read(struct client *, const char *, client_file_cb, void *);
void file_push(struct client_file *);
+int file_write_left(struct client_files *);
+void file_write_open(struct client_files *, struct tmuxpeer *,
+ struct imsg *, int, int, client_file_cb, void *);
+void file_write_data(struct client_files *, struct imsg *);
+void file_write_close(struct client_files *, struct imsg *);
+void file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *,
+ int, int, client_file_cb, void *);
+void file_write_ready(struct client_files *, struct imsg *);
+void file_read_data(struct client_files *, struct imsg *);
+void file_read_done(struct client_files *, struct imsg *);
/* server.c */
extern struct tmuxproc *server_proc;
@@ -2509,6 +2534,7 @@ const char *colour_tostring(int);
int colour_fromstring(const char *s);
int colour_256toRGB(int);
int colour_256to16(int);
+int colour_byname(const char *);
/* attributes.c */
const char *attributes_tostring(int);
@@ -2549,6 +2575,26 @@ void grid_wrap_position(struct grid *, u_int, u_int, u_int *, u_int *);
void grid_unwrap_position(struct grid *, u_int *, u_int *, u_int, u_int);
u_int grid_line_length(struct grid *, u_int);
+/* grid-reader.c */
+void grid_reader_start(struct grid_reader *, struct grid *, u_int, u_int);
+void grid_reader_get_cursor(struct grid_reader *, u_int *, u_int *);
+u_int grid_reader_line_length(struct grid_reader *);
+int grid_reader_in_set(struct grid_reader *, const char *);
+void grid_reader_cursor_right(struct grid_reader *, int, int);
+void grid_reader_cursor_left(struct grid_reader *);
+void grid_reader_cursor_down(struct grid_reader *);
+void grid_reader_cursor_up(struct grid_reader *);
+void grid_reader_cursor_start_of_line(struct grid_reader *, int);
+void grid_reader_cursor_end_of_line(struct grid_reader *, int, int);
+void grid_reader_cursor_next_word(struct grid_reader *, const char *);
+void grid_reader_cursor_next_word_end(struct grid_reader *, const char *);
+void grid_reader_cursor_previous_word(struct grid_reader *, const char *,
+ int);
+int grid_reader_cursor_jump(struct grid_reader *,
+ const struct utf8_data *);
+int grid_reader_cursor_jump_back(struct grid_reader *,
+ const struct utf8_data *);
+
/* grid-view.c */
void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
void grid_view_set_cell(struct grid *, u_int, u_int,
@@ -2668,7 +2714,6 @@ void screen_alternate_off(struct screen *, struct grid_cell *, int);
/* window.c */
extern struct windows windows;
extern struct window_pane_tree all_window_panes;
-extern const struct window_mode *all_window_modes[];
int window_cmp(struct window *, struct window *);
RB_PROTOTYPE(windows, window, entry, window_cmp);
int winlink_cmp(struct winlink *, struct winlink *);
@@ -2739,7 +2784,7 @@ int window_pane_key(struct window_pane *, struct client *,
int window_pane_visible(struct window_pane *);
u_int window_pane_search(struct window_pane *, const char *, int,
int);
-const char *window_printable_flags(struct winlink *);
+const char *window_printable_flags(struct winlink *, int);
struct window_pane *window_pane_find_up(struct window_pane *);
struct window_pane *window_pane_find_down(struct window_pane *);
struct window_pane *window_pane_find_left(struct window_pane *);
@@ -2995,18 +3040,13 @@ int menu_display(struct menu *, int, struct cmdq_item *, u_int,
menu_choice_cb, void *);
/* popup.c */
-#define POPUP_WRITEKEYS 0x1
-#define POPUP_CLOSEEXIT 0x2
-#define POPUP_CLOSEEXITZERO 0x4
+#define POPUP_CLOSEEXIT 0x1
+#define POPUP_CLOSEEXITZERO 0x2
typedef void (*popup_close_cb)(int, void *);
typedef void (*popup_finish_edit_cb)(char *, size_t, void *);
-u_int popup_width(struct cmdq_item *, u_int, const char **,
- struct client *, struct cmd_find_state *);
-u_int popup_height(u_int, const char **);
int popup_display(int, struct cmdq_item *, u_int, u_int, u_int,
- u_int, u_int, const char **, const char *, const char *,
- const char *, struct client *, struct cmd_find_state *,
- popup_close_cb, void *);
+ u_int, const char *, int, char **, const char *,
+ struct client *, struct session *, popup_close_cb, void *);
int popup_editor(struct client *, const char *, size_t,
popup_finish_edit_cb, void *);
diff --git a/tty-term.c b/tty-term.c
index ec8302a6..1d9b36da 100644
--- a/tty-term.c
+++ b/tty-term.c
@@ -61,6 +61,7 @@ static const struct tty_term_code_entry tty_term_codes[] = {
[TTYC_AX] = { TTYCODE_FLAG, "AX" },
[TTYC_BCE] = { TTYCODE_FLAG, "bce" },
[TTYC_BEL] = { TTYCODE_STRING, "bel" },
+ [TTYC_BIDI] = { TTYCODE_STRING, "Bidi" },
[TTYC_BLINK] = { TTYCODE_STRING, "blink" },
[TTYC_BOLD] = { TTYCODE_STRING, "bold" },
[TTYC_CIVIS] = { TTYCODE_STRING, "civis" },
@@ -452,7 +453,8 @@ tty_term_apply_overrides(struct tty_term *term)
}
struct tty_term *
-tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause)
+tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps,
+ int *feat, char **cause)
{
struct tty_term *term;
const struct tty_term_code_entry *ent;
@@ -460,10 +462,9 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause)
struct options_entry *o;
struct options_array_item *a;
union options_value *ov;
- u_int i;
- int n, error;
- const char *s, *acs;
- size_t offset;
+ u_int i, j;
+ const char *s, *acs, *value;
+ size_t offset, namelen;
char *first;
log_debug("adding term %s", name);
@@ -474,57 +475,38 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause)
term->codes = xcalloc(tty_term_ncodes(), sizeof *term->codes);
LIST_INSERT_HEAD(&tty_terms, term, entry);
- /* Set up curses terminal. */
- if (setupterm(name, fd, &error) != OK) {
- switch (error) {
- case 1:
- xasprintf(cause, "can't use hardcopy terminal: %s",
- name);
- break;
- case 0:
- xasprintf(cause, "missing or unsuitable terminal: %s",
- name);
- break;
- case -1:
- xasprintf(cause, "can't find terminfo database");
- break;
- default:
- xasprintf(cause, "unknown error");
- break;
- }
- goto error;
- }
-
/* Fill in codes. */
- for (i = 0; i < tty_term_ncodes(); i++) {
- ent = &tty_term_codes[i];
+ for (i = 0; i < ncaps; i++) {
+ namelen = strcspn(caps[i], "=");
+ if (namelen == 0)
+ continue;
+ value = caps[i] + namelen + 1;
- code = &term->codes[i];
- code->type = TTYCODE_NONE;
- switch (ent->type) {
- case TTYCODE_NONE:
- break;
- case TTYCODE_STRING:
- s = tigetstr((char *) ent->name);
- if (s == NULL || s == (char *) -1)
+ for (j = 0; j < tty_term_ncodes(); j++) {
+ ent = &tty_term_codes[j];
+ if (strncmp(ent->name, caps[i], namelen) != 0)
+ continue;
+ if (ent->name[namelen] != '\0')
+ continue;
+
+ code = &term->codes[j];
+ code->type = TTYCODE_NONE;
+ switch (ent->type) {
+ case TTYCODE_NONE:
break;
- code->type = TTYCODE_STRING;
- code->value.string = tty_term_strip(s);
- break;
- case TTYCODE_NUMBER:
- n = tigetnum((char *) ent->name);
- if (n == -1 || n == -2)
+ case TTYCODE_STRING:
+ code->type = TTYCODE_STRING;
+ code->value.string = tty_term_strip(value);
break;
- code->type = TTYCODE_NUMBER;
- code->value.number = n;
- break;
- case TTYCODE_FLAG:
- n = tigetflag((char *) ent->name);
- if (n == -1)
+ case TTYCODE_NUMBER:
+ code->type = TTYCODE_NUMBER;
+ code->value.number = atoi(value);
break;
- code->type = TTYCODE_FLAG;
- code->value.flag = n;
- break;
+ case TTYCODE_FLAG:
+ code->type = TTYCODE_FLAG;
+ code->value.flag = (*value == '1');
+ break;
+ }
}
}
@@ -649,6 +631,88 @@ tty_term_free(struct tty_term *term)
}
int
+tty_term_read_list(const char *name, int fd, char ***caps, u_int *ncaps,
+ char **cause)
+{
+ const struct tty_term_code_entry *ent;
+ int error, n;
+ u_int i;
+ const char *s;
+ char tmp[11];
+
+ if (setupterm(name, fd, &error) != OK) {
+ switch (error) {
+ case 1:
+ xasprintf(cause, "can't use hardcopy terminal: %s",
+ name);
+ break;
+ case 0:
+ xasprintf(cause, "missing or unsuitable terminal: %s",
+ name);
+ break;
+ case -1:
+ xasprintf(cause, "can't find terminfo database");
+ break;
+ default:
+ xasprintf(cause, "unknown error");
+ break;
+ }
+ return (-1);
+ }
+
+ *ncaps = 0;
+ *caps = NULL;
+
+ for (i = 0; i < tty_term_ncodes(); i++) {
+ ent = &tty_term_codes[i];
+ switch (ent->type) {
+ case TTYCODE_NONE:
+ break;
+ case TTYCODE_STRING:
+ s = tigetstr((char *)ent->name);
+ if (s == NULL || s == (char *)-1)
+ continue;
+ break;
+ case TTYCODE_NUMBER:
+ n = tigetnum((char *)ent->name);
+ if (n == -1 || n == -2)
+ continue;
+ xsnprintf(tmp, sizeof tmp, "%d", n);
+ s = tmp;
+ break;
+ case TTYCODE_FLAG:
+ n = tigetflag((char *) ent->name);
+ if (n == -1)
+ continue;
+ if (n)
+ s = "1";
+ else
+ s = "0";
+ break;
+ }
+ *caps = xreallocarray(*caps, (*ncaps) + 1, sizeof **caps);
+ xasprintf(&(*caps)[*ncaps], "%s=%s", ent->name, s);
+ (*ncaps)++;
+ }
+
+#if !defined(NCURSES_VERSION_MAJOR) || NCURSES_VERSION_MAJOR > 5 || \
+ (NCURSES_VERSION_MAJOR == 5 && NCURSES_VERSION_MINOR > 6)
+ del_curterm(cur_term);
+#endif
+ return (0);
+}
+
+void
+tty_term_free_list(char **caps, u_int ncaps)
+{
+ u_int i;
+
+ for (i = 0; i < ncaps; i++)
+ free(caps[i]);
+ free(caps);
+}
+
+int
tty_term_has(struct tty_term *term, enum tty_code_code code)
{
return (term->codes[code].type != TTYCODE_NONE);
diff --git a/tty.c b/tty.c
index bcbccca6..399bbae8 100644
--- a/tty.c
+++ b/tty.c
@@ -249,8 +249,8 @@ tty_open(struct tty *tty, char **cause)
{
struct client *c = tty->client;
- tty->term = tty_term_create(tty, c->term_name, &c->term_features,
- c->fd, cause);
+ tty->term = tty_term_create(tty, c->term_name, c->term_caps,
+ c->term_ncaps, &c->term_features, cause);
if (tty->term == NULL) {
tty_close(tty);
return (-1);
@@ -694,28 +694,26 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s)
}
if ((changed & ALL_MOUSE_MODES) &&
tty_term_has(tty->term, TTYC_KMOUS)) {
- if ((mode & ALL_MOUSE_MODES) == 0)
+ /*
+ * If the mouse modes have changed, clear any that are set and
+ * apply again. There are differences in how terminals track
+ * the various bits.
+ */
+ if (tty->mode & MODE_MOUSE_SGR)
tty_puts(tty, "\033[?1006l");
- if ((changed & MODE_MOUSE_STANDARD) &&
- (~mode & MODE_MOUSE_STANDARD))
+ if (tty->mode & MODE_MOUSE_STANDARD)
tty_puts(tty, "\033[?1000l");
- if ((changed & MODE_MOUSE_BUTTON) &&
- (~mode & MODE_MOUSE_BUTTON))
+ if (tty->mode & MODE_MOUSE_BUTTON)
tty_puts(tty, "\033[?1002l");
- if ((changed & MODE_MOUSE_ALL) &&
- (~mode & MODE_MOUSE_ALL))
+ if (tty->mode & MODE_MOUSE_ALL)
tty_puts(tty, "\033[?1003l");
-
if (mode & ALL_MOUSE_MODES)
tty_puts(tty, "\033[?1006h");
- if ((changed & MODE_MOUSE_STANDARD) &&
- (mode & MODE_MOUSE_STANDARD))
+ if (mode & MODE_MOUSE_STANDARD)
tty_puts(tty, "\033[?1000h");
- if ((changed & MODE_MOUSE_BUTTON) &&
- (mode & MODE_MOUSE_BUTTON))
+ if (mode & MODE_MOUSE_BUTTON)
tty_puts(tty, "\033[?1002h");
- if ((changed & MODE_MOUSE_ALL) &&
- (mode & MODE_MOUSE_ALL))
+ if (mode & MODE_MOUSE_ALL)
tty_puts(tty, "\033[?1003h");
}
if (changed & MODE_BRACKETPASTE) {
@@ -1533,20 +1531,9 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
void
tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx)
{
- if (ctx->bigger) {
- tty_draw_pane(tty, ctx, ctx->ocy);
- return;
- }
-
tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg);
- tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
-
- if (tty_term_has(tty->term, TTYC_ECH) &&
- !tty_fake_bce(tty, &ctx->defaults, 8))
- tty_putcode1(tty, TTYC_ECH, ctx->num);
- else
- tty_repeat_space(tty, ctx->num);
+ tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->num, ctx->bg);
}
void
@@ -2449,7 +2436,7 @@ tty_check_fg(struct tty *tty, int *palette, struct grid_cell *gc)
/* Is this a 256-colour colour? */
if (gc->fg & COLOUR_FLAG_256) {
/* And not a 256 colour mode? */
- if (colours != 256) {
+ if (colours < 256) {
gc->fg = colour_256to16(gc->fg);
if (gc->fg & 8) {
gc->fg &= 7;
@@ -2502,7 +2489,7 @@ tty_check_bg(struct tty *tty, int *palette, struct grid_cell *gc)
* palette. Bold background doesn't exist portably, so just
* discard the bold bit if set.
*/
- if (colours != 256) {
+ if (colours < 256) {
gc->bg = colour_256to16(gc->bg);
if (gc->bg & 8) {
gc->bg &= 7;
diff --git a/utf8.c b/utf8.c
index 458363b8..f43945e6 100644
--- a/utf8.c
+++ b/utf8.c
@@ -216,7 +216,11 @@ utf8_width(struct utf8_data *ud, int *width)
{
wchar_t wc;
+#ifdef HAVE_UTF8PROC
+ switch (utf8proc_mbtowc(&wc, ud->data, ud->size)) {
+#else
switch (mbtowc(&wc, ud->data, ud->size)) {
+#endif
case -1:
log_debug("UTF-8 %.*s, mbtowc() %d", (int)ud->size, ud->data,
errno);
@@ -225,7 +229,11 @@ utf8_width(struct utf8_data *ud, int *width)
case 0:
return (UTF8_ERROR);
}
+#ifdef HAVE_UTF8PROC
+ *width = utf8proc_wcwidth(wc);
+#else
*width = wcwidth(wc);
+#endif
if (*width >= 0 && *width <= 0xff)
return (UTF8_DONE);
log_debug("UTF-8 %.*s, wcwidth() %d", (int)ud->size, ud->data, *width);
diff --git a/window-buffer.c b/window-buffer.c
index 62412cd0..acdcb525 100644
--- a/window-buffer.c
+++ b/window-buffer.c
@@ -31,6 +31,7 @@ static struct screen *window_buffer_init(struct window_mode_entry *,
static void window_buffer_free(struct window_mode_entry *);
static void window_buffer_resize(struct window_mode_entry *, u_int,
u_int);
+static void window_buffer_update(struct window_mode_entry *);
static void window_buffer_key(struct window_mode_entry *,
struct client *, struct session *,
struct winlink *, key_code, struct mouse_event *);
@@ -63,6 +64,7 @@ const struct window_mode window_buffer_mode = {
.init = window_buffer_init,
.free = window_buffer_free,
.resize = window_buffer_resize,
+ .update = window_buffer_update,
.key = window_buffer_key,
};
@@ -336,6 +338,16 @@ window_buffer_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
}
static void
+window_buffer_update(struct window_mode_entry *wme)
+{
+ struct window_buffer_modedata *data = wme->data;
+
+ mode_tree_build(data->data);
+ mode_tree_draw(data->data);
+ data->wp->flags |= PANE_REDRAW;
+}
+
+static void
window_buffer_do_delete(void *modedata, void *itemdata,
__unused struct client *c, __unused key_code key)
{
diff --git a/window-client.c b/window-client.c
index 5e02462b..ec3c646a 100644
--- a/window-client.c
+++ b/window-client.c
@@ -30,6 +30,7 @@ static struct screen *window_client_init(struct window_mode_entry *,
static void window_client_free(struct window_mode_entry *);
static void window_client_resize(struct window_mode_entry *, u_int,
u_int);
+static void window_client_update(struct window_mode_entry *);
static void window_client_key(struct window_mode_entry *,
struct client *, struct session *,
struct winlink *, key_code, struct mouse_event *);
@@ -59,6 +60,7 @@ const struct window_mode window_client_mode = {
.init = window_client_init,
.free = window_client_free,
.resize = window_client_resize,
+ .update = window_client_update,
.key = window_client_key,
};
@@ -312,6 +314,16 @@ window_client_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
}
static void
+window_client_update(struct window_mode_entry *wme)
+{
+ struct window_client_modedata *data = wme->data;
+
+ mode_tree_build(data->data);
+ mode_tree_draw(data->data);
+ data->wp->flags |= PANE_REDRAW;
+}
+
+static void
window_client_do_detach(void *modedata, void *itemdata,
__unused struct client *c, key_code key)
{
diff --git a/window-copy.c b/window-copy.c
index 1dc0c293..4558ed48 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -73,6 +73,8 @@ static int window_copy_search_marks(struct window_mode_entry *,
static void window_copy_clear_marks(struct window_mode_entry *);
static void window_copy_move_left(struct screen *, u_int *, u_int *, int);
static int window_copy_is_lowercase(const char *);
+static void window_copy_search_back_overlap(struct grid *, regex_t *,
+ u_int *, u_int *, u_int *, u_int);
static int window_copy_search_jump(struct window_mode_entry *,
struct grid *, struct grid *, u_int, u_int, u_int, int, int,
int, int, u_int *);
@@ -92,6 +94,8 @@ static void window_copy_synchronize_cursor(struct window_mode_entry *, int);
static void *window_copy_get_selection(struct window_mode_entry *, size_t *);
static void window_copy_copy_buffer(struct window_mode_entry *,
const char *, void *, size_t);
+static void window_copy_pipe(struct window_mode_entry *,
+ struct session *, const char *);
static void window_copy_copy_pipe(struct window_mode_entry *,
struct session *, const char *, const char *);
static void window_copy_copy_selection(struct window_mode_entry *,
@@ -128,11 +132,15 @@ static void window_copy_cursor_previous_word(struct window_mode_entry *,
const char *, int);
static void window_copy_scroll_up(struct window_mode_entry *, u_int);
static void window_copy_scroll_down(struct window_mode_entry *, u_int);
-static void window_copy_rectangle_toggle(struct window_mode_entry *);
+static void window_copy_rectangle_set(struct window_mode_entry *, int);
static void window_copy_move_mouse(struct mouse_event *);
static void window_copy_drag_update(struct client *, struct mouse_event *);
static void window_copy_drag_release(struct client *, struct mouse_event *);
static void window_copy_jump_to_mark(struct window_mode_entry *);
+static void window_copy_acquire_cursor_up(struct window_mode_entry *,
+ u_int, u_int, u_int, u_int, u_int);
+static void window_copy_acquire_cursor_down(struct window_mode_entry *,
+ u_int, u_int, u_int, u_int, u_int, u_int, int);
const struct window_mode window_copy_mode = {
.name = "copy-mode",
@@ -272,7 +280,7 @@ struct window_copy_mode_data {
u_char *searchmark;
int searchcount;
int searchmore;
- int searchthis;
+ int searchall;
int searchx;
int searchy;
int searcho;
@@ -282,8 +290,8 @@ struct window_copy_mode_data {
#define WINDOW_COPY_SEARCH_TIMEOUT 10000
#define WINDOW_COPY_SEARCH_ALL_TIMEOUT 200
- int jumptype;
- char jumpchar;
+ int jumptype;
+ struct utf8_data *jumpchar;
struct event dragtimer;
#define WINDOW_COPY_DRAG_REPEAT_TIME 50000
@@ -396,9 +404,10 @@ window_copy_common_init(struct window_mode_entry *wme)
data->searchstr = NULL;
}
data->searchx = data->searchy = data->searcho = -1;
+ data->searchall = 1;
data->jumptype = WINDOW_COPY_OFF;
- data->jumpchar = '\0';
+ data->jumpchar = NULL;
screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0);
data->modekeys = options_get_number(wp->window->options, "mode-keys");
@@ -479,6 +488,7 @@ window_copy_free(struct window_mode_entry *wme)
free(data->searchmark);
free(data->searchstr);
+ free(data->jumpchar);
screen_free(data->backing);
free(data->backing);
@@ -702,7 +712,7 @@ window_copy_get_line(struct window_pane *wp, u_int y)
return (format_grid_line(gd, gd->hsize + y));
}
-static char *
+static void *
window_copy_cursor_word_cb(struct format_tree *ft)
{
struct window_pane *wp = format_get_pane(ft);
@@ -712,7 +722,7 @@ window_copy_cursor_word_cb(struct format_tree *ft)
return (window_copy_get_word(wp, data->cx, data->cy));
}
-static char *
+static void *
window_copy_cursor_line_cb(struct format_tree *ft)
{
struct window_pane *wp = format_get_pane(ft);
@@ -722,7 +732,7 @@ window_copy_cursor_line_cb(struct format_tree *ft)
return (window_copy_get_line(wp, data->cy));
}
-static char *
+static void *
window_copy_search_match_cb(struct format_tree *ft)
{
struct window_pane *wp = format_get_pane(ft);
@@ -1625,13 +1635,37 @@ window_copy_cmd_previous_word(struct window_copy_cmd_state *cs)
}
static enum window_copy_cmd_action
+window_copy_cmd_rectangle_on(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+
+ data->lineflag = LINE_SEL_NONE;
+ window_copy_rectangle_set(wme, 1);
+
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_rectangle_off(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct window_copy_mode_data *data = wme->data;
+
+ data->lineflag = LINE_SEL_NONE;
+ window_copy_rectangle_set(wme, 0);
+
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs)
{
struct window_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data;
data->lineflag = LINE_SEL_NONE;
- window_copy_rectangle_toggle(wme);
+ window_copy_rectangle_set(wme, !data->rectflag);
return (WINDOW_COPY_CMD_NOTHING);
}
@@ -1850,6 +1884,44 @@ window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs)
}
static enum window_copy_cmd_action
+window_copy_cmd_pipe_no_clear(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+ struct client *c = cs->c;
+ struct session *s = cs->s;
+ struct winlink *wl = cs->wl;
+ struct window_pane *wp = wme->wp;
+ char *command = NULL;
+
+ if (s != NULL && cs->args->argc > 1 && *cs->args->argv[1] != '\0')
+ command = format_single(NULL, cs->args->argv[1], c, s, wl, wp);
+ window_copy_pipe(wme, s, command);
+ free(command);
+
+ return (WINDOW_COPY_CMD_NOTHING);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_pipe(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+
+ window_copy_cmd_pipe_no_clear(cs);
+ window_copy_clear_selection(wme);
+ return (WINDOW_COPY_CMD_REDRAW);
+}
+
+static enum window_copy_cmd_action
+window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state *cs)
+{
+ struct window_mode_entry *wme = cs->wme;
+
+ window_copy_cmd_pipe_no_clear(cs);
+ window_copy_clear_selection(wme);
+ return (WINDOW_COPY_CMD_CANCEL);
+}
+
+static enum window_copy_cmd_action
window_copy_cmd_goto_line(struct window_copy_cmd_state *cs)
{
struct window_mode_entry *wme = cs->wme;
@@ -1870,7 +1942,8 @@ window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs)
if (*argument != '\0') {
data->jumptype = WINDOW_COPY_JUMPBACKWARD;
- data->jumpchar = *argument;
+ free(data->jumpchar);
+ data->jumpchar = utf8_fromcstr(argument);
for (; np != 0; np--)
window_copy_cursor_jump_back(wme);
}
@@ -1887,7 +1960,8 @@ window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs)
if (*argument != '\0') {
data->jumptype = WINDOW_COPY_JUMPFORWARD;
- data->jumpchar = *argument;
+ free(data->jumpchar);
+ data->jumpchar = utf8_fromcstr(argument);
for (; np != 0; np--)
window_copy_cursor_jump(wme);
}
@@ -1904,7 +1978,8 @@ window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs)
if (*argument != '\0') {
data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
- data->jumpchar = *argument;
+ free(data->jumpchar);
+ data->jumpchar = utf8_fromcstr(argument);
for (; np != 0; np--)
window_copy_cursor_jump_to_back(wme);
}
@@ -1921,7 +1996,8 @@ window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs)
if (*argument != '\0') {
data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
- data->jumpchar = *argument;
+ free(data->jumpchar);
+ data->jumpchar = utf8_fromcstr(argument);
for (; np != 0; np--)
window_copy_cursor_jump_to(wme);
}
@@ -2029,6 +2105,8 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
data->timeout = 0;
+ log_debug ("%s: %s", __func__, argument);
+
prefix = *argument++;
if (data->searchx == -1 || data->searchy == -1) {
data->searchx = data->cx;
@@ -2082,6 +2160,8 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
data->timeout = 0;
+ log_debug ("%s: %s", __func__, argument);
+
prefix = *argument++;
if (data->searchx == -1 || data->searchy == -1) {
data->searchx = data->cx;
@@ -2238,6 +2318,12 @@ static const struct {
window_copy_cmd_page_down_and_cancel },
{ "page-up", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
window_copy_cmd_page_up },
+ { "pipe-no-clear", 0, 1, WINDOW_COPY_CMD_CLEAR_NEVER,
+ window_copy_cmd_pipe_no_clear },
+ { "pipe", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS,
+ window_copy_cmd_pipe },
+ { "pipe-and-cancel", 0, 1, WINDOW_COPY_CMD_CLEAR_ALWAYS,
+ window_copy_cmd_pipe_and_cancel },
{ "previous-matching-bracket", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS,
window_copy_cmd_previous_matching_bracket },
{ "previous-paragraph", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
@@ -2246,6 +2332,10 @@ static const struct {
window_copy_cmd_previous_space },
{ "previous-word", 0, 0, WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
window_copy_cmd_previous_word },
+ { "rectangle-on", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS,
+ window_copy_cmd_rectangle_on },
+ { "rectangle-off", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS,
+ window_copy_cmd_rectangle_off },
{ "rectangle-toggle", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS,
window_copy_cmd_rectangle_toggle },
{ "refresh-from-pane", 0, 0, WINDOW_COPY_CMD_CLEAR_ALWAYS,
@@ -2334,9 +2424,6 @@ window_copy_command(struct window_mode_entry *wme, struct client *c,
if (clear != WINDOW_COPY_CMD_CLEAR_NEVER) {
window_copy_clear_marks(wme);
data->searchx = data->searchy = -1;
- } else if (data->searchthis != -1) {
- data->searchthis = -1;
- action = WINDOW_COPY_CMD_REDRAW;
}
if (action == WINDOW_COPY_CMD_NOTHING)
action = WINDOW_COPY_CMD_REDRAW;
@@ -2828,6 +2915,48 @@ window_copy_is_lowercase(const char *ptr)
}
/*
+ * Handle backward wrapped regex searches with overlapping matches. In this case
+ * find the longest overlapping match from previous wrapped lines.
+ */
+static void
+window_copy_search_back_overlap(struct grid *gd, regex_t *preg, u_int *ppx,
+ u_int *psx, u_int *ppy, u_int endline)
+{
+ u_int endx, endy, oldendx, oldendy, px, py, sx;
+ int found = 1;
+
+ oldendx = *ppx + *psx;
+ oldendy = *ppy - 1;
+ while (oldendx > gd->sx - 1) {
+ oldendx -= gd->sx;
+ oldendy++;
+ }
+ endx = oldendx;
+ endy = oldendy;
+ px = *ppx;
+ py = *ppy;
+ while (found && px == 0 && py - 1 > endline &&
+ grid_get_line(gd, py - 2)->flags & GRID_LINE_WRAPPED &&
+ endx == oldendx && endy == oldendy) {
+ py--;
+ found = window_copy_search_rl_regex(gd, &px, &sx, py - 1, 0,
+ gd->sx, preg);
+ if (found) {
+ endx = px + sx;
+ endy = py - 1;
+ while (endx > gd->sx - 1) {
+ endx -= gd->sx;
+ endy++;
+ }
+ if (endx == oldendx && endy == oldendy) {
+ *ppx = px;
+ *ppy = py;
+ }
+ }
+ }
+}
+
+/*
* Search for text stored in sgd starting from position fx,fy up to endline. If
* found, jump to it. If cis then ignore case. The direction is 0 for searching
* up, down otherwise. If wrap then go to begin/end of grid and try again if
@@ -2879,6 +3008,10 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
if (regex) {
found = window_copy_search_rl_regex(gd,
&px, &sx, i - 1, 0, fx + 1, &reg);
+ if (found) {
+ window_copy_search_back_overlap(gd,
+ &reg, &px, &sx, &i, endline);
+ }
} else {
found = window_copy_search_rl(gd, sgd,
&px, i - 1, 0, fx + 1, cis);
@@ -2929,9 +3062,11 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex,
if (data->timeout)
return (0);
- if (wp->searchstr == NULL || wp->searchregex != regex)
+ if (data->searchall || wp->searchstr == NULL ||
+ wp->searchregex != regex) {
visible_only = 0;
- else
+ data->searchall = 0;
+ } else
visible_only = (strcmp(wp->searchstr, str) == 0);
free(wp->searchstr);
wp->searchstr = xstrdup(str);
@@ -2961,6 +3096,12 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex,
if (found) {
window_copy_search_marks(wme, &ss, regex, visible_only);
if (foundlen != 0) {
+ /* Adjust for wrapped lines eating one right. */
+ i = data->cx + foundlen;
+ while (i > gd->sx - 1) {
+ i -= gd->sx;
+ window_copy_cursor_right(wme, 1);
+ }
for (i = 0; i < foundlen; i++)
window_copy_cursor_right(wme, 1);
}
@@ -3009,7 +3150,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp,
struct screen *s = data->backing, ss;
struct screen_write_ctx ctx;
struct grid *gd = s->grid;
- int found, cis, which = -1, stopped = 0;
+ int found, cis, stopped = 0;
int cflags = REG_EXTENDED;
u_int px, py, i, b, nfound = 0, width;
u_int ssize = 1, start, end;
@@ -3072,23 +3213,21 @@ again:
if (!found)
break;
}
-
nfound++;
- if (px == data->cx &&
- py == gd->hsize + data->cy - data->oy)
- which = nfound;
if (window_copy_search_mark_at(data, px, py, &b) == 0) {
if (b + width > gd->sx * gd->sy)
width = (gd->sx * gd->sy) - b;
- for (i = b; i < b + width; i++)
+ for (i = b; i < b + width; i++) {
+ if (data->searchmark[i] != 0)
+ continue;
data->searchmark[i] = data->searchgen;
+ }
if (data->searchgen == UCHAR_MAX)
data->searchgen = 1;
else
data->searchgen++;
}
-
px += width;
}
@@ -3116,7 +3255,6 @@ again:
if (!visible_only) {
if (stopped) {
- data->searchthis = -1;
if (nfound > 1000)
data->searchcount = 1000;
else if (nfound > 100)
@@ -3127,10 +3265,6 @@ again:
data->searchcount = -1;
data->searchmore = 1;
} else {
- if (which != -1)
- data->searchthis = 1 + nfound - which;
- else
- data->searchthis = -1;
data->searchcount = nfound;
data->searchmore = 0;
}
@@ -3366,15 +3500,11 @@ window_copy_write_line(struct window_mode_entry *wme,
if (data->searchcount == -1) {
size = xsnprintf(hdr, sizeof hdr,
"[%u/%u]", data->oy, hsize);
- } else if (data->searchthis == -1) {
+ } else {
size = xsnprintf(hdr, sizeof hdr,
"(%d%s results) [%u/%u]", data->searchcount,
data->searchmore ? "+" : "", data->oy,
hsize);
- } else {
- size = xsnprintf(hdr, sizeof hdr,
- "(%d/%d results) [%u/%u]", data->searchthis,
- data->searchcount, data->oy, hsize);
}
}
if (size > screen_size_x(s))
@@ -3463,10 +3593,10 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin,
struct window_copy_mode_data *data = wme->data;
u_int xx, yy;
+ xx = data->cx;
yy = screen_hsize(data->backing) + data->cy - data->oy;
switch (data->selflag) {
case SEL_WORD:
- xx = data->cx;
if (no_reset)
break;
begin = 0;
@@ -3492,10 +3622,8 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin,
}
break;
case SEL_LINE:
- if (no_reset) {
- xx = data->cx;
+ if (no_reset)
break;
- }
begin = 0;
if (data->dy > yy) {
/* Right to left selection. */
@@ -3515,7 +3643,6 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin,
}
break;
case SEL_CHAR:
- xx = data->cx;
break;
}
if (begin) {
@@ -3825,22 +3952,41 @@ window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix,
paste_add(prefix, buf, len);
}
-static void
-window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s,
- const char *prefix, const char *cmd)
+static void *
+window_copy_pipe_run(struct window_mode_entry *wme, struct session *s,
+ const char *cmd, size_t *len)
{
void *buf;
- size_t len;
struct job *job;
- buf = window_copy_get_selection(wme, &len);
+ buf = window_copy_get_selection(wme, len);
if (cmd == NULL || *cmd == '\0')
cmd = options_get_string(global_options, "copy-command");
if (cmd != NULL && *cmd != '\0') {
- job = job_run(cmd, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT,
- -1, -1);
- bufferevent_write(job_get_event(job), buf, len);
+ job = job_run(cmd, 0, NULL, s, NULL, NULL, NULL, NULL, NULL,
+ JOB_NOWAIT, -1, -1);
+ bufferevent_write(job_get_event(job), buf, *len);
}
+ return (buf);
+}
+
+static void
+window_copy_pipe(struct window_mode_entry *wme, struct session *s,
+ const char *cmd)
+{
+ size_t len;
+
+ window_copy_pipe_run(wme, s, cmd, &len);
+}
+
+static void
+window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s,
+ const char *prefix, const char *cmd)
+{
+ void *buf;
+ size_t len;
+
+ buf = window_copy_pipe_run(wme, s, cmd, &len);
if (buf != NULL)
window_copy_copy_buffer(wme, prefix, buf, len);
}
@@ -3993,20 +4139,18 @@ window_copy_cursor_start_of_line(struct window_mode_entry *wme)
{
struct window_copy_mode_data *data = wme->data;
struct screen *back_s = data->backing;
- struct grid *gd = back_s->grid;
- u_int py;
-
- if (data->cx == 0 && data->lineflag == LINE_SEL_NONE) {
- py = screen_hsize(back_s) + data->cy - data->oy;
- while (py > 0 &&
- grid_get_line(gd, py - 1)->flags & GRID_LINE_WRAPPED) {
- window_copy_cursor_up(wme, 0);
- py = screen_hsize(back_s) + data->cy - data->oy;
- }
- }
- window_copy_update_cursor(wme, 0, data->cy);
- if (window_copy_update_selection(wme, 1, 0))
- window_copy_redraw_lines(wme, data->cy, 1);
+ struct grid_reader gr;
+ u_int px, py, oldy, hsize;
+
+ px = data->cx;
+ hsize = screen_hsize(back_s);
+ py = hsize + data->cy - data->oy;
+ oldy = data->cy;
+
+ grid_reader_start(&gr, back_s->grid, px, py);
+ grid_reader_cursor_start_of_line(&gr, 1);
+ grid_reader_get_cursor(&gr, &px, &py);
+ window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
}
static void
@@ -4037,32 +4181,22 @@ window_copy_cursor_end_of_line(struct window_mode_entry *wme)
{
struct window_copy_mode_data *data = wme->data;
struct screen *back_s = data->backing;
- struct grid *gd = back_s->grid;
- struct grid_line *gl;
- u_int px, py;
-
- py = screen_hsize(back_s) + data->cy - data->oy;
- px = window_copy_find_length(wme, py);
+ struct grid_reader gr;
+ u_int px, py, oldy, hsize;
- if (data->cx == px && data->lineflag == LINE_SEL_NONE) {
- if (data->screen.sel != NULL && data->rectflag)
- px = screen_size_x(back_s);
- gl = grid_get_line(gd, py);
- if (gl->flags & GRID_LINE_WRAPPED) {
- while (py < gd->sy + gd->hsize) {
- gl = grid_get_line(gd, py);
- if (~gl->flags & GRID_LINE_WRAPPED)
- break;
- window_copy_cursor_down(wme, 0);
- py = screen_hsize(back_s) + data->cy - data->oy;
- }
- px = window_copy_find_length(wme, py);
- }
- }
- window_copy_update_cursor(wme, px, data->cy);
+ px = data->cx;
+ hsize = screen_hsize(back_s);
+ py = hsize + data->cy - data->oy;
+ oldy = data->cy;
- if (window_copy_update_selection(wme, 1, 0))
- window_copy_redraw_lines(wme, data->cy, 1);
+ grid_reader_start(&gr, back_s->grid, px, py);
+ if (data->screen.sel != NULL && data->rectflag)
+ grid_reader_cursor_end_of_line(&gr, 1, 1);
+ else
+ grid_reader_cursor_end_of_line(&gr, 1, 0);
+ grid_reader_get_cursor(&gr, &px, &py);
+ window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
+ data->oy, oldy, px, py, 0);
}
static void
@@ -4120,57 +4254,39 @@ static void
window_copy_cursor_left(struct window_mode_entry *wme)
{
struct window_copy_mode_data *data = wme->data;
- u_int py, cx;
- struct grid_cell gc;
+ struct screen *back_s = data->backing;
+ struct grid_reader gr;
+ u_int px, py, oldy, hsize;
- py = screen_hsize(data->backing) + data->cy - data->oy;
- cx = data->cx;
- while (cx > 0) {
- grid_get_cell(data->backing->grid, cx, py, &gc);
- if (~gc.flags & GRID_FLAG_PADDING)
- break;
- cx--;
- }
- if (cx == 0 && py > 0) {
- window_copy_cursor_up(wme, 0);
- window_copy_cursor_end_of_line(wme);
- } else if (cx > 0) {
- window_copy_update_cursor(wme, cx - 1, data->cy);
- if (window_copy_update_selection(wme, 1, 0))
- window_copy_redraw_lines(wme, data->cy, 1);
- }
+ px = data->cx;
+ hsize = screen_hsize(back_s);
+ py = hsize + data->cy - data->oy;
+ oldy = data->cy;
+
+ grid_reader_start(&gr, back_s->grid, px, py);
+ grid_reader_cursor_left(&gr);
+ grid_reader_get_cursor(&gr, &px, &py);
+ window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
}
static void
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;
- struct grid_cell gc;
+ struct screen *back_s = data->backing;
+ struct grid_reader gr;
+ u_int px, py, oldy, hsize;
- py = screen_hsize(data->backing) + data->cy - data->oy;
- yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1;
- if (all || (data->screen.sel != NULL && data->rectflag))
- px = screen_size_x(&data->screen);
- else
- px = window_copy_find_length(wme, py);
+ px = data->cx;
+ hsize = screen_hsize(back_s);
+ py = hsize + data->cy - data->oy;
+ oldy = data->cy;
- if (data->cx >= px && py < yy) {
- window_copy_cursor_start_of_line(wme);
- window_copy_cursor_down(wme, 0);
- } else if (data->cx < px) {
- cx = data->cx + 1;
- cy = screen_hsize(data->backing) + data->cy - data->oy;
- while (cx < px) {
- grid_get_cell(data->backing->grid, cx, cy, &gc);
- if (~gc.flags & GRID_FLAG_PADDING)
- break;
- cx++;
- }
- window_copy_update_cursor(wme, cx, data->cy);
- if (window_copy_update_selection(wme, 1, 0))
- window_copy_redraw_lines(wme, data->cy, 1);
- }
+ grid_reader_start(&gr, back_s->grid, px, py);
+ grid_reader_cursor_right(&gr, 1, all);
+ grid_reader_get_cursor(&gr, &px, &py);
+ window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
+ data->oy, oldy, px, py, 0);
}
static void
@@ -4214,13 +4330,30 @@ window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only)
px = window_copy_find_length(wme, py);
if ((data->cx >= data->lastsx && data->cx != px) ||
data->cx > px)
- window_copy_cursor_end_of_line(wme);
+ {
+ window_copy_update_cursor(wme, px, data->cy);
+ if (window_copy_update_selection(wme, 1, 0))
+ window_copy_redraw_lines(wme, data->cy, 1);
+ }
}
if (data->lineflag == LINE_SEL_LEFT_RIGHT)
- window_copy_cursor_end_of_line(wme);
+ {
+ py = screen_hsize(data->backing) + data->cy - data->oy;
+ if (data->rectflag)
+ px = screen_size_x(data->backing);
+ else
+ px = window_copy_find_length(wme, py);
+ window_copy_update_cursor(wme, px, data->cy);
+ if (window_copy_update_selection(wme, 1, 0))
+ window_copy_redraw_lines(wme, data->cy, 1);
+ }
else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
- window_copy_cursor_start_of_line(wme);
+ {
+ window_copy_update_cursor(wme, 0, data->cy);
+ if (window_copy_update_selection(wme, 1, 0))
+ window_copy_redraw_lines(wme, data->cy, 1);
+ }
}
static void
@@ -4256,13 +4389,30 @@ window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only)
px = window_copy_find_length(wme, py);
if ((data->cx >= data->lastsx && data->cx != px) ||
data->cx > px)
- window_copy_cursor_end_of_line(wme);
+ {
+ window_copy_update_cursor(wme, px, data->cy);
+ if (window_copy_update_selection(wme, 1, 0))
+ window_copy_redraw_lines(wme, data->cy, 1);
+ }
}
if (data->lineflag == LINE_SEL_LEFT_RIGHT)
- window_copy_cursor_end_of_line(wme);
+ {
+ py = screen_hsize(data->backing) + data->cy - data->oy;
+ if (data->rectflag)
+ px = screen_size_x(data->backing);
+ else
+ px = window_copy_find_length(wme, py);
+ window_copy_update_cursor(wme, px, data->cy);
+ if (window_copy_update_selection(wme, 1, 0))
+ window_copy_redraw_lines(wme, data->cy, 1);
+ }
else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
- window_copy_cursor_start_of_line(wme);
+ {
+ window_copy_update_cursor(wme, 0, data->cy);
+ if (window_copy_update_selection(wme, 1, 0))
+ window_copy_redraw_lines(wme, data->cy, 1);
+ }
}
static void
@@ -4270,23 +4420,19 @@ window_copy_cursor_jump(struct window_mode_entry *wme)
{
struct window_copy_mode_data *data = wme->data;
struct screen *back_s = data->backing;
- struct grid_cell gc;
- u_int px, py, xx;
+ struct grid_reader gr;
+ u_int px, py, oldy, hsize;
px = data->cx + 1;
- py = screen_hsize(back_s) + data->cy - data->oy;
- xx = window_copy_find_length(wme, py);
-
- while (px < xx) {
- grid_get_cell(back_s->grid, px, py, &gc);
- if (!(gc.flags & GRID_FLAG_PADDING) &&
- gc.data.size == 1 && *gc.data.data == data->jumpchar) {
- window_copy_update_cursor(wme, px, data->cy);
- if (window_copy_update_selection(wme, 1, 0))
- window_copy_redraw_lines(wme, data->cy, 1);
- return;
- }
- px++;
+ hsize = screen_hsize(back_s);
+ py = hsize + data->cy - data->oy;
+ oldy = data->cy;
+
+ grid_reader_start(&gr, back_s->grid, px, py);
+ if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
+ grid_reader_get_cursor(&gr, &px, &py);
+ window_copy_acquire_cursor_down(wme, hsize,
+ screen_size_y(back_s), data->oy, oldy, px, py, 0);
}
}
@@ -4295,27 +4441,22 @@ window_copy_cursor_jump_back(struct window_mode_entry *wme)
{
struct window_copy_mode_data *data = wme->data;
struct screen *back_s = data->backing;
- struct grid_cell gc;
- u_int px, py;
+ struct grid_reader gr;
+ u_int px, py, oldy, hsize;
px = data->cx;
- py = screen_hsize(back_s) + data->cy - data->oy;
+ hsize = screen_hsize(back_s);
+ py = hsize + data->cy - data->oy;
+ oldy = data->cy;
if (px > 0)
px--;
- for (;;) {
- grid_get_cell(back_s->grid, px, py, &gc);
- if (!(gc.flags & GRID_FLAG_PADDING) &&
- gc.data.size == 1 && *gc.data.data == data->jumpchar) {
- window_copy_update_cursor(wme, px, data->cy);
- if (window_copy_update_selection(wme, 1, 0))
- window_copy_redraw_lines(wme, data->cy, 1);
- return;
- }
- if (px == 0)
- break;
- px--;
+ grid_reader_start(&gr, back_s->grid, px, py);
+ if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
+ grid_reader_get_cursor(&gr, &px, &py);
+ window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
+ py);
}
}
@@ -4324,23 +4465,20 @@ window_copy_cursor_jump_to(struct window_mode_entry *wme)
{
struct window_copy_mode_data *data = wme->data;
struct screen *back_s = data->backing;
- struct grid_cell gc;
- u_int px, py, xx;
+ struct grid_reader gr;
+ u_int px, py, oldy, hsize;
px = data->cx + 2;
- py = screen_hsize(back_s) + data->cy - data->oy;
- xx = window_copy_find_length(wme, py);
-
- while (px < xx) {
- grid_get_cell(back_s->grid, px, py, &gc);
- if (!(gc.flags & GRID_FLAG_PADDING) &&
- gc.data.size == 1 && *gc.data.data == data->jumpchar) {
- window_copy_update_cursor(wme, px - 1, data->cy);
- if (window_copy_update_selection(wme, 1, 0))
- window_copy_redraw_lines(wme, data->cy, 1);
- return;
- }
- px++;
+ hsize = screen_hsize(back_s);
+ py = hsize + data->cy - data->oy;
+ oldy = data->cy;
+
+ grid_reader_start(&gr, back_s->grid, px, py);
+ if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
+ grid_reader_cursor_left(&gr);
+ grid_reader_get_cursor(&gr, &px, &py);
+ window_copy_acquire_cursor_down(wme, hsize,
+ screen_size_y(back_s), data->oy, oldy, px, py, 0);
}
}
@@ -4349,11 +4487,13 @@ window_copy_cursor_jump_to_back(struct window_mode_entry *wme)
{
struct window_copy_mode_data *data = wme->data;
struct screen *back_s = data->backing;
- struct grid_cell gc;
- u_int px, py;
+ struct grid_reader gr;
+ u_int px, py, oldy, hsize;
px = data->cx;
- py = screen_hsize(back_s) + data->cy - data->oy;
+ hsize = screen_hsize(back_s);
+ py = hsize + data->cy - data->oy;
+ oldy = data->cy;
if (px > 0)
px--;
@@ -4361,18 +4501,12 @@ window_copy_cursor_jump_to_back(struct window_mode_entry *wme)
if (px > 0)
px--;
- for (;;) {
- grid_get_cell(back_s->grid, px, py, &gc);
- if (!(gc.flags & GRID_FLAG_PADDING) &&
- gc.data.size == 1 && *gc.data.data == data->jumpchar) {
- window_copy_update_cursor(wme, px + 1, data->cy);
- if (window_copy_update_selection(wme, 1, 0))
- window_copy_redraw_lines(wme, data->cy, 1);
- return;
- }
- if (px == 0)
- break;
- px--;
+ grid_reader_start(&gr, back_s->grid, px, py);
+ if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
+ grid_reader_cursor_right(&gr, 1, 0);
+ grid_reader_get_cursor(&gr, &px, &py);
+ window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
+ py);
}
}
@@ -4382,41 +4516,19 @@ window_copy_cursor_next_word(struct window_mode_entry *wme,
{
struct window_copy_mode_data *data = wme->data;
struct screen *back_s = data->backing;
- u_int px, py, xx, yy;
- int expected = 0;
+ struct grid_reader gr;
+ u_int px, py, oldy, hsize;
px = data->cx;
- py = screen_hsize(back_s) + data->cy - data->oy;
- xx = window_copy_find_length(wme, py);
- yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
+ hsize = screen_hsize(back_s);
+ py = hsize + data->cy - data->oy;
+ oldy = data->cy;
- /*
- * First skip past any nonword characters and then any word characters.
- *
- * expected is initially set to 0 for the former and then 1 for the
- * latter.
- */
- do {
- while (px > xx ||
- window_copy_in_set(wme, px, py, separators) == expected) {
- /* Move down if we're past the end of the line. */
- if (px > xx) {
- if (py == yy)
- return;
- window_copy_cursor_down(wme, 0);
- px = 0;
-
- py = screen_hsize(back_s) + data->cy - data->oy;
- xx = window_copy_find_length(wme, py);
- } else
- px++;
- }
- expected = !expected;
- } while (expected == 1);
-
- window_copy_update_cursor(wme, px, data->cy);
- if (window_copy_update_selection(wme, 1, 0))
- window_copy_redraw_lines(wme, data->cy, 1);
+ grid_reader_start(&gr, back_s->grid, px, py);
+ grid_reader_cursor_next_word(&gr, separators);
+ grid_reader_get_cursor(&gr, &px, &py);
+ window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
+ data->oy, oldy, px, py, 0);
}
static void
@@ -4476,48 +4588,25 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme,
struct window_copy_mode_data *data = wme->data;
struct options *oo = wp->window->options;
struct screen *back_s = data->backing;
- u_int px, py, xx, yy;
- int keys, expected = 1;
+ struct grid_reader gr;
+ u_int px, py, oldy, hsize;
+ int keys;
px = data->cx;
- py = screen_hsize(back_s) + data->cy - data->oy;
- xx = window_copy_find_length(wme, py);
- yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
+ hsize = screen_hsize(back_s);
+ py = hsize + data->cy - data->oy;
+ oldy = data->cy;
+ grid_reader_start(&gr, back_s->grid, px, py);
keys = options_get_number(oo, "mode-keys");
- if (keys == MODEKEY_VI && !window_copy_in_set(wme, px, py, separators))
- px++;
-
- /*
- * First skip past any word characters, then any nonword characters.
- *
- * expected is initially set to 1 for the former and then 0 for the
- * latter.
- */
- do {
- while (px > xx ||
- window_copy_in_set(wme, px, py, separators) == expected) {
- /* Move down if we're past the end of the line. */
- if (px > xx) {
- if (py == yy)
- return;
- window_copy_cursor_down(wme, 0);
- px = 0;
-
- py = screen_hsize(back_s) + data->cy - data->oy;
- xx = window_copy_find_length(wme, py);
- } else
- px++;
- }
- expected = !expected;
- } while (expected == 0);
-
- if (keys == MODEKEY_VI && px != 0)
- px--;
-
- window_copy_update_cursor(wme, px, data->cy);
- if (window_copy_update_selection(wme, 1, no_reset))
- window_copy_redraw_lines(wme, data->cy, 1);
+ if (keys == MODEKEY_VI && !grid_reader_in_set(&gr, separators))
+ grid_reader_cursor_right(&gr, 0, 0);
+ grid_reader_cursor_next_word_end(&gr, separators);
+ if (keys == MODEKEY_VI)
+ grid_reader_cursor_left(&gr);
+ grid_reader_get_cursor(&gr, &px, &py);
+ window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
+ data->oy, oldy, px, py, no_reset);
}
/* Compute the previous place where a word begins. */
@@ -4526,10 +4615,11 @@ window_copy_cursor_previous_word_pos(struct window_mode_entry *wme,
const char *separators, int already, u_int *ppx, u_int *ppy)
{
struct window_copy_mode_data *data = wme->data;
- u_int px, py;
+ u_int px, py, hsize;
+ hsize = screen_hsize(data->backing);
px = data->cx;
- py = screen_hsize(data->backing) + data->cy - data->oy;
+ py = hsize + data->cy - data->oy;
/* Move back to the previous word character. */
if (already || window_copy_in_set(wme, px, py, separators)) {
@@ -4542,9 +4632,7 @@ window_copy_cursor_previous_word_pos(struct window_mode_entry *wme,
} else {
if (py == 0 ||
(data->cy == 0 &&
- (screen_hsize(data->backing) == 0 ||
- data->oy >=
- screen_hsize(data->backing) - 1)))
+ (hsize == 0 || data->oy > hsize - 1)))
goto out;
py--;
@@ -4573,47 +4661,19 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme,
const char *separators, int already)
{
struct window_copy_mode_data *data = wme->data;
- u_int px, py;
+ struct screen *back_s = data->backing;
+ struct grid_reader gr;
+ u_int px, py, oldy, hsize;
px = data->cx;
- py = screen_hsize(data->backing) + data->cy - data->oy;
-
- /* Move back to the previous word character. */
- if (already || window_copy_in_set(wme, px, py, separators)) {
- for (;;) {
- if (px > 0) {
- px--;
- if (!window_copy_in_set(wme, px, py,
- separators))
- break;
- } else {
- if (data->cy == 0 &&
- (screen_hsize(data->backing) == 0 ||
- data->oy >=
- screen_hsize(data->backing) - 1))
- goto out;
- window_copy_cursor_up(wme, 0);
-
- py = screen_hsize(data->backing) + data->cy -
- data->oy;
- px = window_copy_find_length(wme, py);
-
- /* Stop if separator at EOL. */
- if (px > 0 && window_copy_in_set(wme, px - 1,
- py, separators))
- break;
- }
- }
- }
-
- /* Move back to the beginning of this word. */
- while (px > 0 && !window_copy_in_set(wme, px - 1, py, separators))
- px--;
+ hsize = screen_hsize(back_s);
+ py = hsize + data->cy - data->oy;
+ oldy = data->cy;
-out:
- window_copy_update_cursor(wme, px, data->cy);
- if (window_copy_update_selection(wme, 1, 0))
- window_copy_redraw_lines(wme, data->cy, 1);
+ grid_reader_start(&gr, back_s->grid, px, py);
+ grid_reader_cursor_previous_word(&gr, separators, already);
+ grid_reader_get_cursor(&gr, &px, &py);
+ window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
}
static void
@@ -4683,12 +4743,12 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
}
static void
-window_copy_rectangle_toggle(struct window_mode_entry *wme)
+window_copy_rectangle_set(struct window_mode_entry *wme, int rectflag)
{
struct window_copy_mode_data *data = wme->data;
u_int px, py;
- data->rectflag = !data->rectflag;
+ data->rectflag = rectflag;
py = screen_hsize(data->backing) + data->cy - data->oy;
px = window_copy_find_length(wme, py);
@@ -4752,22 +4812,22 @@ window_copy_start_drag(struct client *c, struct mouse_event *m)
if (x < data->selrx || x > data->endselrx || yg != data->selry)
data->selflag = SEL_CHAR;
switch (data->selflag) {
- case SEL_WORD:
- if (data->ws != NULL) {
- window_copy_update_cursor(wme, x, y);
- window_copy_cursor_previous_word_pos(wme,
- data->ws, 0, &x, &y);
- y -= screen_hsize(data->backing) - data->oy;
- }
- window_copy_update_cursor(wme, x, y);
- break;
- case SEL_LINE:
- window_copy_update_cursor(wme, 0, y);
- break;
- case SEL_CHAR:
+ case SEL_WORD:
+ if (data->ws != NULL) {
window_copy_update_cursor(wme, x, y);
- window_copy_start_selection(wme);
- break;
+ window_copy_cursor_previous_word_pos(wme, data->ws, 0,
+ &x, &y);
+ y -= screen_hsize(data->backing) - data->oy;
+ }
+ window_copy_update_cursor(wme, x, y);
+ break;
+ case SEL_LINE:
+ window_copy_update_cursor(wme, 0, y);
+ break;
+ case SEL_CHAR:
+ window_copy_update_cursor(wme, x, y);
+ window_copy_start_selection(wme);
+ break;
}
window_copy_redraw_screen(wme);
@@ -4864,3 +4924,58 @@ window_copy_jump_to_mark(struct window_mode_entry *wme)
window_copy_update_selection(wme, 0, 0);
window_copy_redraw_screen(wme);
}
+
+/* Scroll up if the cursor went off the visible screen. */
+static void
+window_copy_acquire_cursor_up(struct window_mode_entry *wme, u_int hsize,
+ u_int oy, u_int oldy, u_int px, u_int py)
+{
+ u_int cy, yy, ny, nd;
+
+ yy = hsize - oy;
+ if (py < yy) {
+ ny = yy - py;
+ cy = 0;
+ nd = 1;
+ } else {
+ ny = 0;
+ cy = py - yy;
+ nd = oldy - cy + 1;
+ }
+ while (ny > 0) {
+ window_copy_cursor_up(wme, 1);
+ ny--;
+ }
+ window_copy_update_cursor(wme, px, cy);
+ if (window_copy_update_selection(wme, 1, 0))
+ window_copy_redraw_lines(wme, cy, nd);
+}
+
+/* Scroll down if the cursor went off the visible screen. */
+static void
+window_copy_acquire_cursor_down(struct window_mode_entry *wme, u_int hsize,
+ u_int sy, u_int oy, u_int oldy, u_int px, u_int py, int no_reset)
+{
+ u_int cy, yy, ny, nd;
+
+ cy = py - hsize + oy;
+ yy = sy - 1;
+ if (cy > yy) {
+ ny = cy - yy;
+ oldy = yy;
+ nd = 1;
+ } else {
+ ny = 0;
+ nd = cy - oldy + 1;
+ }
+ while (ny > 0) {
+ window_copy_cursor_down(wme, 1);
+ ny--;
+ }
+ if (cy > yy)
+ window_copy_update_cursor(wme, px, yy);
+ else
+ window_copy_update_cursor(wme, px, cy);
+ if (window_copy_update_selection(wme, 1, no_reset))
+ window_copy_redraw_lines(wme, oldy, nd);
+}
diff --git a/window-customize.c b/window-customize.c
index 1dad07cd..a1f191b5 100644
--- a/window-customize.c
+++ b/window-customize.c
@@ -190,13 +190,6 @@ window_customize_scope_text(enum window_customize_scope scope,
u_int idx;
switch (scope) {
- case WINDOW_CUSTOMIZE_NONE:
- case WINDOW_CUSTOMIZE_KEY:
- case WINDOW_CUSTOMIZE_SERVER:
- case WINDOW_CUSTOMIZE_GLOBAL_SESSION:
- case WINDOW_CUSTOMIZE_GLOBAL_WINDOW:
- s = xstrdup("");
- break;
case WINDOW_CUSTOMIZE_PANE:
window_pane_index(fs->wp, &idx);
xasprintf(&s, "pane %u", idx);
@@ -207,6 +200,9 @@ window_customize_scope_text(enum window_customize_scope scope,
case WINDOW_CUSTOMIZE_WINDOW:
xasprintf(&s, "window %u", fs->wl->idx);
break;
+ default:
+ s = xstrdup("");
+ break;
}
return (s);
}
diff --git a/window-tree.c b/window-tree.c
index 154eca89..cc822ac3 100644
--- a/window-tree.c
+++ b/window-tree.c
@@ -29,6 +29,7 @@ static struct screen *window_tree_init(struct window_mode_entry *,
static void window_tree_free(struct window_mode_entry *);
static void window_tree_resize(struct window_mode_entry *, u_int,
u_int);
+static void window_tree_update(struct window_mode_entry *);
static void window_tree_key(struct window_mode_entry *,
struct client *, struct session *,
struct winlink *, key_code, struct mouse_event *);
@@ -79,6 +80,7 @@ const struct window_mode window_tree_mode = {
.init = window_tree_init,
.free = window_tree_free,
.resize = window_tree_resize,
+ .update = window_tree_update,
.key = window_tree_key,
};
@@ -937,6 +939,16 @@ window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
mode_tree_resize(data->data, sx, sy);
}
+static void
+window_tree_update(struct window_mode_entry *wme)
+{
+ struct window_tree_modedata *data = wme->data;
+
+ mode_tree_build(data->data);
+ mode_tree_draw(data->data);
+ data->wp->flags |= PANE_REDRAW;
+}
+
static char *
window_tree_get_target(struct window_tree_itemdata *item,
struct cmd_find_state *fs)
diff --git a/window.c b/window.c
index d5fb5f7d..a41385ce 100644
--- a/window.c
+++ b/window.c
@@ -61,17 +61,6 @@ static u_int next_window_pane_id;
static u_int next_window_id;
static u_int next_active_point;
-/* List of window modes. */
-const struct window_mode *all_window_modes[] = {
- &window_buffer_mode,
- &window_client_mode,
- &window_clock_mode,
- &window_copy_mode,
- &window_tree_mode,
- &window_view_mode,
- NULL
-};
-
struct window_pane_input_data {
struct cmdq_item *item;
u_int wp;
@@ -810,15 +799,18 @@ window_destroy_panes(struct window *w)
}
const char *
-window_printable_flags(struct winlink *wl)
+window_printable_flags(struct winlink *wl, int escape)
{
struct session *s = wl->session;
static char flags[32];
int pos;
pos = 0;
- if (wl->flags & WINLINK_ACTIVITY)
+ if (wl->flags & WINLINK_ACTIVITY) {
flags[pos++] = '#';
+ if (escape)
+ flags[pos++] = '#';
+ }
if (wl->flags & WINLINK_BELL)
flags[pos++] = '!';
if (wl->flags & WINLINK_SILENCE)
@@ -1155,12 +1147,27 @@ window_pane_reset_mode_all(struct window_pane *wp)
window_pane_reset_mode(wp);
}
+static void
+window_pane_copy_key(struct window_pane *wp, key_code key)
+{
+ struct window_pane *loop;
+
+ TAILQ_FOREACH(loop, &wp->window->panes, entry) {
+ if (loop != wp &&
+ TAILQ_EMPTY(&loop->modes) &&
+ loop->fd != -1 &&
+ (~loop->flags & PANE_INPUTOFF) &&
+ window_pane_visible(loop) &&
+ options_get_number(loop->options, "synchronize-panes"))
+ input_key_pane(loop, key, NULL);
+ }
+}
+
int
window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
struct winlink *wl, key_code key, struct mouse_event *m)
{
struct window_mode_entry *wme;
- struct window_pane *wp2;
if (KEYC_IS_MOUSE(key) && m == NULL)
return (-1);
@@ -1182,16 +1189,8 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
if (KEYC_IS_MOUSE(key))
return (0);
- if (options_get_number(wp->window->options, "synchronize-panes")) {
- TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
- if (wp2 != wp &&
- TAILQ_EMPTY(&wp2->modes) &&
- wp2->fd != -1 &&
- (~wp2->flags & PANE_INPUTOFF) &&
- window_pane_visible(wp2))
- input_key_pane(wp2, key, NULL);
- }
- }
+ if (options_get_number(wp->options, "synchronize-panes"))
+ window_pane_copy_key(wp, key);
return (0);
}