Merge branch 'master' into 3.2-rc

This commit is contained in:
Nicholas Marriott 2021-03-02 12:08:34 +00:00
commit c01251d023
75 changed files with 6577 additions and 2275 deletions

View File

@ -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)

2
.gitignore vendored
View File

@ -19,3 +19,5 @@ configure
tmux.1.*
*.dSYM
cmd-parse.c
fuzz/*-fuzzer
.dirstamp

70
CHANGES
View File

@ -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

View File

@ -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 \

View File

@ -18,7 +18,6 @@
#include <sys/types.h>
#include <event.h>
#include <stdlib.h>
#include "tmux.h"

30
cfg.c
View File

@ -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)
{
@ -59,19 +62,11 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
return (CMD_RETURN_NORMAL);
}
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));
}

343
client.c
View File

@ -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:

View File

@ -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;
char *p;
int top;
u_int line, ox, oy, sx, sy, lines, position;
long n;
struct format_tree *ft;
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;
/*
* 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;
/* 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
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
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)
*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;
else
*py = 0;
} else if (strcmp(yp, "M") == 0) {
if (event->m.valid)
*py = event->m.y + 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;
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));
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;
}
value = args_get(args, 'R');
if (value != NULL)
shellcmd = format_single_from_target(item, value);
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);
}

View File

@ -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);

View File

@ -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) {

View File

@ -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();

View File

@ -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}] "

View File

@ -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);

View File

@ -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 != '#')

View File

@ -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. */

View File

@ -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);

View File

@ -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);

View File

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

View File

@ -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) {

View File

@ -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)

611
colour.c
View File

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

View File

@ -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;

37
compat/clock_gettime.c Normal file
View File

@ -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;
}

View File

@ -50,6 +50,8 @@
# include <libproc.h>
#endif
#include "compat.h"
#ifndef OPEN_MAX
# define OPEN_MAX 256
#endif

82
compat/forkpty-haiku.c Normal file
View File

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

View File

@ -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

View File

@ -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)

View File

@ -19,7 +19,6 @@
#include <sys/types.h>
#include <event.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

454
file.c
View File

@ -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(struct client *c, int stream, client_file_cb cb, void *cbdata)
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_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);
}

View File

@ -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;

2731
format.c

File diff suppressed because it is too large Load Diff

89
fuzz/input-fuzzer.c Normal file
View File

@ -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;
}

8
fuzz/input-fuzzer.dict Normal file
View File

@ -0,0 +1,8 @@
"\x1b["
"1000"
"2004"
"1049"
"38;2"
"100;"
"tmux;"
"rgb:00/00/00"

View File

@ -0,0 +1,2 @@
[libfuzzer]
max_len = 512

365
grid-reader.c Normal file
View File

@ -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;
}

5
grid.c
View File

@ -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++;

142
input.c
View File

@ -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;
double c, m, y, k = 0;
u_int r, g, b;
size_t len = strlen(p);
int colour = -1;
char *copy;
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);
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)

54
job.c
View File

@ -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;

View File

@ -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);

View File

@ -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."
},

View File

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

View File

@ -19,7 +19,6 @@
#include <sys/param.h>
#include <sys/stat.h>
#include <event.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

View File

@ -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);

View File

@ -23,7 +23,6 @@
#include <err.h>
#include <errno.h>
#include <event.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

View File

@ -24,7 +24,6 @@
#include <err.h>
#include <errno.h>
#include <event.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

52
osdep-haiku.c Normal file
View File

@ -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());
}

View File

@ -18,8 +18,6 @@
#include <sys/types.h>
#include <event.h>
#include "tmux.h"
char *

View File

@ -20,7 +20,6 @@
#include <sys/stat.h>
#include <sys/param.h>
#include <event.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

View File

@ -22,7 +22,6 @@
#include <sys/sysctl.h>
#include <errno.h>
#include <event.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>

View File

@ -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

View File

@ -19,7 +19,6 @@
#include <sys/param.h>
#include <sys/stat.h>
#include <event.h>
#include <fcntl.h>
#include <procfs.h>
#include <stdio.h>

View File

@ -18,8 +18,6 @@
#include <sys/types.h>
#include <event.h>
#include "tmux.h"
char *

211
popup.c
View File

@ -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);

38
proc.c
View File

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

243
regress/osc-11colours.sh Normal file
View File

@ -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

View File

@ -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. */

View File

@ -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;
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 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;
case CLEAR_END:
break;
case TEXT:
if (ci->x < x)
continue;
}
/* 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;
}
/* 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);

View File

@ -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;
}

View File

@ -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)

View File

@ -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;

View File

@ -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);

15
spawn.c
View File

@ -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

View File

@ -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)
if (utf8_strlen(c->prompt_buffer) != 1)
status_prompt_clear(c);
else if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
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:

277
tmux.1
View File

@ -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;

57
tmux.c
View File

@ -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) {

156
tmux.h
View File

@ -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 *);

View File

@ -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;
}
}
}
@ -648,6 +630,88 @@ tty_term_free(struct tty_term *term)
free(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)
{

47
tty.c
View File

@ -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;

8
utf8.c
View File

@ -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);

View File

@ -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,
};
@ -335,6 +337,16 @@ window_buffer_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
mode_tree_resize(data->data, sx, 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)

View File

@ -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,
};
@ -311,6 +313,16 @@ window_client_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
mode_tree_resize(data->data, sx, 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)

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -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)

View File

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