summaryrefslogtreecommitdiffstats
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)
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;
- }