Merge branch 'master' into sixel-passthrough

This commit is contained in:
Nicholas Marriott 2019-12-18 20:24:42 +00:00
commit 5bb075487f
23 changed files with 1347 additions and 615 deletions

47
CHANGES
View File

@ -1,5 +1,52 @@
CHANGES FROM 3.0 to X.X
* Change file reading and writing to go through the client if necessary. This
fixes commands like "tmux loadb /dev/fd/X". Also modify source-file to
support "-" for standard input, like load-buffer and save-buffer.
* Add ~/.config/tmux/tmux.conf to the default search path for configuration
files.
* Bump the escape sequence timeout to five seconds to allow for longer
legitimate sequences.
* Make a best effort to set xpixel and ypixel for each pane and add formats for
them.
* Add push-default to status-left and status-right in status-format[0].
* Add -F flag to send-keys to expand formats in search-backward and forward
copy mode commands and copy_cursor_word and copy_cursor_line formats for word
and line at cursor in copy mode. Use for default # and * binding with vi(1)
keys.
* Do not clear search marks on cursor movement with vi(1) keys.
* Add p format modifier for padding to width and allow multiple substitutions
in a single format.
* Add -f for full size to join-pane (like split-window).
* Handle OSC 7 (a VTE extension) and put the result in a new format (pane_path).
* Change new-session -A without a session name (that is, no -s option also) to
attach to the best existing session like attach-session rather than creating
a new one.
* Add an option to set the key sent by backspace for those whose system uses ^H
rather than ^?.
* Add formats for cursor and selection position in copy mode.
* Add support for percentage sizes for resize-pane ("-x 10%"). Also change
split-window and join-pane -l to accept similar percentages and no longer
document -p.
* Turn automatic-rename back on if the rename escape sequence is used with an
empty name.
* Make select-pane -P set window-active-style also to match previous behaviour.
* Do not use bright when emulating 256 colours on an 8 colour terminal because
it is also bold on some terminals.

View File

@ -11,7 +11,8 @@ EXTRA_DIST = \
dist_EXTRA_tmux_SOURCES = compat/*.[ch]
# Preprocessor flags.
AM_CPPFLAGS += @XOPEN_DEFINES@ -DTMUX_CONF="\"$(sysconfdir)/tmux.conf\""
AM_CPPFLAGS += @XOPEN_DEFINES@
AM_CPPFLAGS += -DTMUX_CONF="\"$(sysconfdir)/tmux.conf:~/.tmux.conf:~/.config/tmux/tmux.conf\""
# Additional object files.
LDADD = $(LIBOBJS)
@ -131,6 +132,7 @@ dist_tmux_SOURCES = \
control-notify.c \
control.c \
environ.c \
file.c \
format.c \
format-draw.c \
grid-view.c \

107
cfg.c
View File

@ -66,12 +66,45 @@ set_cfg_file(const char *path)
cfg_file = xstrdup(path);
}
static char *
expand_cfg_file(const char *path, const char *home)
{
char *expanded, *name;
const char *end;
struct environ_entry *value;
if (strncmp(path, "~/", 2) == 0) {
if (home == NULL)
return (NULL);
xasprintf(&expanded, "%s%s", home, path + 1);
return (expanded);
}
if (*path == '$') {
end = strchr(path, '/');
if (end == NULL)
name = xstrdup(path + 1);
else
name = xstrndup(path + 1, end - path - 1);
value = environ_find(global_environ, name);
free(name);
if (value == NULL)
return (NULL);
if (end == NULL)
end = "";
xasprintf(&expanded, "%s%s", value->value, end);
return (expanded);
}
return (xstrdup(path));
}
void
start_cfg(void)
{
const char *home;
int flags = 0;
const char *home = find_home();
struct client *c;
char *path, *copy, *next, *expanded;
/*
* Configuration files are loaded without a client, so commands are run
@ -89,15 +122,21 @@ start_cfg(void)
cmdq_append(c, cfg_item);
}
if (cfg_file == NULL)
load_cfg(TMUX_CONF, c, NULL, CMD_PARSE_QUIET, NULL);
if (cfg_file == NULL && (home = find_home()) != NULL) {
xasprintf(&cfg_file, "%s/.tmux.conf", home);
flags = CMD_PARSE_QUIET;
}
if (cfg_file != NULL)
load_cfg(cfg_file, c, NULL, flags, NULL);
if (cfg_file == NULL) {
path = copy = xstrdup(TMUX_CONF);
while ((next = strsep(&path, ":")) != NULL) {
expanded = expand_cfg_file(next, home);
if (expanded == NULL) {
log_debug("couldn't expand %s", next);
continue;
}
log_debug("expanded %s to %s", next, expanded);
load_cfg(expanded, c, NULL, CMD_PARSE_QUIET, NULL);
free(expanded);
}
free(copy);
} else
load_cfg(cfg_file, c, NULL, 0, NULL);
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
}
@ -155,6 +194,52 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
return (0);
}
int
load_cfg_from_buffer(const void *buf, size_t len, const char *path,
struct client *c, struct cmdq_item *item, int flags,
struct cmdq_item **new_item)
{
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
struct cmdq_item *new_item0;
if (new_item != NULL)
*new_item = NULL;
log_debug("loading %s", path);
memset(&pi, 0, sizeof pi);
pi.flags = flags;
pi.file = path;
pi.line = 1;
pi.item = item;
pi.c = c;
pr = cmd_parse_from_buffer(buf, len, &pi);
if (pr->status == CMD_PARSE_EMPTY)
return (0);
if (pr->status == CMD_PARSE_ERROR) {
cfg_add_cause("%s", pr->error);
free(pr->error);
return (-1);
}
if (flags & CMD_PARSE_PARSEONLY) {
cmd_list_free(pr->cmdlist);
return (0);
}
new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
if (item != NULL)
cmdq_insert_after(item, new_item0);
else
cmdq_append(NULL, new_item0);
cmd_list_free(pr->cmdlist);
if (new_item != NULL)
*new_item = new_item0;
return (0);
}
void
cfg_add_cause(const char *fmt, ...)
{

352
client.c
View File

@ -35,7 +35,6 @@
static struct tmuxproc *client_proc;
static struct tmuxpeer *client_peer;
static int client_flags;
static struct event client_stdin;
static enum {
CLIENT_EXIT_NONE,
CLIENT_EXIT_DETACHED,
@ -46,19 +45,19 @@ static enum {
CLIENT_EXIT_EXITED,
CLIENT_EXIT_SERVER_EXITED,
} client_exitreason = CLIENT_EXIT_NONE;
static int client_exitflag;
static int client_exitval;
static enum msgtype client_exittype;
static const char *client_exitsession;
static const char *client_execshell;
static const char *client_execcmd;
static int client_attached;
static struct client_files client_files = RB_INITIALIZER(&client_files);
static __dead void client_exec(const char *,const char *);
static int client_get_lock(char *);
static int client_connect(struct event_base *, const char *, int);
static void client_send_identify(const char *, const char *);
static void client_stdin_callback(int, short, void *);
static void client_write(int, const char *, size_t);
static void client_signal(int);
static void client_dispatch(struct imsg *, void *);
static void client_dispatch_attached(struct imsg *);
@ -211,13 +210,34 @@ client_exit_message(void)
return ("unknown reason");
}
/* Exit if all streams flushed. */
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)
proc_exit(client_proc);
}
/* Client main loop. */
int
client_main(struct event_base *base, int argc, char **argv, int flags)
{
struct cmd_parse_result *pr;
struct cmd *cmd;
struct msg_command_data *data;
struct msg_command *data;
int cmdflags, fd, i;
const char *ttynam, *cwd;
pid_t ppid;
@ -291,7 +311,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
*
* "sendfd" is dropped later in client_dispatch_wait().
*/
if (pledge("stdio unix sendfd proc exec tty", NULL) != 0)
if (pledge(
"stdio rpath wpath cpath unix sendfd proc exec tty",
NULL) != 0)
fatal("pledge failed");
/* Free stuff that is not used in the client. */
@ -302,10 +324,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
options_free(global_w_options);
environ_free(global_environ);
/* Create stdin handler. */
setblocking(STDIN_FILENO, 0);
event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST,
client_stdin_callback, NULL);
/* Set up control mode. */
if (client_flags & CLIENT_CONTROLCONTROL) {
if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) {
fprintf(stderr, "tcgetattr failed: %s\n",
@ -428,39 +447,254 @@ client_send_identify(const char *ttynam, const char *cwd)
proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0);
}
/* Callback for client stdin read events. */
/* File write error callback. */
static void
client_stdin_callback(__unused int fd, __unused short events,
__unused void *arg)
client_write_error_callback(__unused struct bufferevent *bev,
__unused short what, void *arg)
{
struct msg_stdin_data data;
struct client_file *cf = arg;
data.size = read(STDIN_FILENO, data.data, sizeof data.data);
if (data.size == -1 && (errno == EINTR || errno == EAGAIN))
return;
log_debug("write error file %d", cf->stream);
proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data);
if (data.size <= 0)
event_del(&client_stdin);
bufferevent_free(cf->event);
cf->event = NULL;
close(cf->fd);
cf->fd = -1;
if (client_exitflag)
client_exit();
}
/* Force write to file descriptor. */
/* File write callback. */
static void
client_write(int fd, const char *data, size_t size)
client_write_callback(__unused struct bufferevent *bev, void *arg)
{
ssize_t used;
struct client_file *cf = arg;
log_debug("%s: %.*s", __func__, (int)size, data);
while (size != 0) {
used = write(fd, data, size);
if (used == -1) {
if (errno == EINTR || errno == EAGAIN)
continue;
break;
}
data += used;
size -= used;
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 = 0;
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);
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. */
@ -555,12 +789,10 @@ client_dispatch(struct imsg *imsg, __unused void *arg)
static void
client_dispatch_wait(struct imsg *imsg)
{
char *data;
ssize_t datalen;
struct msg_stdout_data stdoutdata;
struct msg_stderr_data stderrdata;
int retval;
static int pledge_applied;
char *data;
ssize_t datalen;
int retval;
static int pledge_applied;
/*
* "sendfd" is no longer required once all of the identify messages
@ -569,10 +801,12 @@ client_dispatch_wait(struct imsg *imsg)
* get the first message from the server.
*/
if (!pledge_applied) {
if (pledge("stdio unix proc exec tty", NULL) != 0)
if (pledge(
"stdio rpath wpath cpath unix proc exec tty",
NULL) != 0)
fatal("pledge failed");
pledge_applied = 1;
};
}
data = imsg->data;
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
@ -586,38 +820,16 @@ client_dispatch_wait(struct imsg *imsg)
memcpy(&retval, data, sizeof retval);
client_exitval = retval;
}
proc_exit(client_proc);
client_exitflag = 1;
client_exit();
break;
case MSG_READY:
if (datalen != 0)
fatalx("bad MSG_READY size");
event_del(&client_stdin);
client_attached = 1;
proc_send(client_peer, MSG_RESIZE, -1, NULL, 0);
break;
case MSG_STDIN:
if (datalen != 0)
fatalx("bad MSG_STDIN size");
event_add(&client_stdin, NULL);
break;
case MSG_STDOUT:
if (datalen != sizeof stdoutdata)
fatalx("bad MSG_STDOUT size");
memcpy(&stdoutdata, data, sizeof stdoutdata);
client_write(STDOUT_FILENO, stdoutdata.data,
stdoutdata.size);
break;
case MSG_STDERR:
if (datalen != sizeof stderrdata)
fatalx("bad MSG_STDERR size");
memcpy(&stderrdata, data, sizeof stderrdata);
client_write(STDERR_FILENO, stderrdata.data,
stderrdata.size);
break;
case MSG_VERSION:
if (datalen != 0)
fatalx("bad MSG_VERSION size");
@ -641,6 +853,18 @@ client_dispatch_wait(struct imsg *imsg)
case MSG_EXITED:
proc_exit(client_proc);
break;
case MSG_READ_OPEN:
client_read_open(data, datalen);
break;
case MSG_WRITE_OPEN:
client_write_open(data, datalen);
break;
case MSG_WRITE:
client_write_data(data, datalen);
break;
case MSG_WRITE_CLOSE:
client_write_close(data, datalen);
break;
}
}

View File

@ -193,7 +193,7 @@ static enum cmd_retval
cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c;
struct client *c = item->client;
struct window_pane *wp = item->target.wp;
char *buf, *cause;
const char *bufname;
@ -214,18 +214,15 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
if (args_has(args, 'p')) {
c = item->client;
if (c == NULL ||
(c->session != NULL && !(c->flags & CLIENT_CONTROL))) {
cmdq_error(item, "can't write to stdout");
if (!file_can_print(c)) {
cmdq_error(item, "can't write output to client");
free(buf);
return (CMD_RETURN_ERROR);
}
evbuffer_add(c->stdout_data, buf, len);
free(buf);
file_print_buffer(c, buf, len);
if (args_has(args, 'P') && len > 0)
evbuffer_add(c->stdout_data, "\n", 1);
server_client_push_stdout(c);
file_print(c, "\n");
free(buf);
} else {
bufname = NULL;
if (args_has(args, 'b'))

View File

@ -33,8 +33,6 @@
static enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmdq_item *);
static void cmd_load_buffer_callback(struct client *, int, void *);
const struct cmd_entry cmd_load_buffer_entry = {
.name = "load-buffer",
.alias = "loadb",
@ -48,9 +46,40 @@ const struct cmd_entry cmd_load_buffer_entry = {
struct cmd_load_buffer_data {
struct cmdq_item *item;
char *bufname;
char *name;
};
static void
cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
int closed, struct evbuffer *buffer, void *data)
{
struct cmd_load_buffer_data *cdata = data;
struct cmdq_item *item = cdata->item;
void *bdata = EVBUFFER_DATA(buffer);
size_t bsize = EVBUFFER_LENGTH(buffer);
void *copy;
char *cause;
if (!closed)
return;
if (error != 0)
cmdq_error(item, "%s: %s", path, strerror(error));
else if (bsize != 0) {
copy = xmalloc(bsize);
memcpy(copy, bdata, bsize);
if (paste_set(copy, bsize, cdata->name, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
free(copy);
}
}
cmdq_continue(item);
free(cdata->name);
free(cdata);
}
static enum cmd_retval
cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
@ -60,124 +89,19 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
FILE *f;
const char *bufname;
char *pdata = NULL, *new_pdata, *cause;
char *path, *file;
size_t psize;
int ch, error;
const char *bufname = args_get(args, 'b');
char *path;
bufname = NULL;
if (args_has(args, 'b'))
bufname = args_get(args, 'b');
cdata = xmalloc(sizeof *cdata);
cdata->item = item;
if (bufname != NULL)
cdata->name = xstrdup(bufname);
else
cdata->name = NULL;
path = format_single(item, args->argv[0], c, s, wl, wp);
if (strcmp(path, "-") == 0) {
free(path);
c = item->client;
cdata = xcalloc(1, sizeof *cdata);
cdata->item = item;
if (bufname != NULL)
cdata->bufname = xstrdup(bufname);
error = server_set_stdin_callback(c, cmd_load_buffer_callback,
cdata, &cause);
if (error != 0) {
cmdq_error(item, "-: %s", cause);
free(cause);
free(cdata);
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_WAIT);
}
file = server_client_get_path(item->client, path);
file_read(item->client, path, cmd_load_buffer_done, cdata);
free(path);
f = fopen(file, "rb");
if (f == NULL) {
cmdq_error(item, "%s: %s", file, strerror(errno));
goto error;
}
pdata = NULL;
psize = 0;
while ((ch = getc(f)) != EOF) {
/* Do not let the server die due to memory exhaustion. */
if ((new_pdata = realloc(pdata, psize + 2)) == NULL) {
cmdq_error(item, "realloc error: %s", strerror(errno));
goto error;
}
pdata = new_pdata;
pdata[psize++] = ch;
}
if (ferror(f)) {
cmdq_error(item, "%s: read error", file);
goto error;
}
if (pdata != NULL)
pdata[psize] = '\0';
fclose(f);
free(file);
if (paste_set(pdata, psize, bufname, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(pdata);
free(cause);
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_NORMAL);
error:
free(pdata);
if (f != NULL)
fclose(f);
free(file);
return (CMD_RETURN_ERROR);
}
static void
cmd_load_buffer_callback(struct client *c, int closed, void *data)
{
struct cmd_load_buffer_data *cdata = data;
char *pdata, *cause, *saved;
size_t psize;
if (!closed)
return;
c->stdin_callback = NULL;
server_client_unref(c);
if (c->flags & CLIENT_DEAD)
goto out;
psize = EVBUFFER_LENGTH(c->stdin_data);
if (psize == 0 || (pdata = malloc(psize + 1)) == NULL)
goto out;
memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize);
pdata[psize] = '\0';
evbuffer_drain(c->stdin_data, psize);
if (paste_set(pdata, psize, cdata->bufname, &cause) != 0) {
/* No context so can't use server_client_msg_error. */
if (~c->flags & CLIENT_UTF8) {
saved = cause;
cause = utf8_sanitize(saved);
free(saved);
}
evbuffer_add_printf(c->stderr_data, "%s", cause);
server_client_push_stderr(c);
free(pdata);
free(cause);
}
out:
cmdq_continue(cdata->item);
free(cdata->bufname);
free(cdata);
return (CMD_RETURN_WAIT);
}

View File

@ -745,6 +745,12 @@ cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
struct cmd_parse_result *
cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
{
return (cmd_parse_from_buffer(s, strlen(s), pi));
}
struct cmd_parse_result *
cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi)
{
static struct cmd_parse_result pr;
struct cmd_parse_input input;
@ -757,14 +763,14 @@ cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
}
memset(&pr, 0, sizeof pr);
if (*s == '\0') {
if (len == 0) {
pr.status = CMD_PARSE_EMPTY;
pr.cmdlist = NULL;
pr.error = NULL;
return (&pr);
}
cmds = cmd_parse_do_buffer(s, strlen(s), pi, &cause);
cmds = cmd_parse_do_buffer(buf, len, pi, &cause);
if (cmds == NULL) {
pr.status = CMD_PARSE_ERROR;
pr.error = cause;

View File

@ -475,13 +475,11 @@ void
cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
{
struct client *c = item->client;
long t = item->time;
u_int number = item->number;
if (c == NULL || !(c->flags & CLIENT_CONTROL))
return;
evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
(long)item->time, item->number, flags);
server_client_push_stdout(c);
if (c != NULL && (c->flags & CLIENT_CONTROL))
file_print(c, "%%%s %ld %u %d\n", guard, t, number, flags);
}
/* Show message from command. */
@ -495,29 +493,29 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
char *tmp, *msg;
va_start(ap, fmt);
xvasprintf(&msg, fmt, ap);
va_end(ap);
log_debug("%s: %s", __func__, msg);
if (c == NULL)
/* nothing */;
else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
if (~c->flags & CLIENT_UTF8) {
xvasprintf(&tmp, fmt, ap);
tmp = msg;
msg = utf8_sanitize(tmp);
free(tmp);
evbuffer_add(c->stdout_data, msg, strlen(msg));
free(msg);
} else
evbuffer_add_vprintf(c->stdout_data, fmt, ap);
evbuffer_add(c->stdout_data, "\n", 1);
server_client_push_stdout(c);
}
file_print(c, "%s\n", msg);
} else {
wp = c->session->curw->window->active;
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
window_copy_vadd(wp, fmt, ap);
window_copy_add(wp, "%s", msg);
}
va_end(ap);
free(msg);
}
/* Show error from command. */
@ -528,11 +526,10 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
struct cmd *cmd = item->cmd;
va_list ap;
char *msg;
size_t msglen;
char *tmp;
va_start(ap, fmt);
msglen = xvasprintf(&msg, fmt, ap);
xvasprintf(&msg, fmt, ap);
va_end(ap);
log_debug("%s: %s", __func__, msg);
@ -544,11 +541,8 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
tmp = msg;
msg = utf8_sanitize(tmp);
free(tmp);
msglen = strlen(msg);
}
evbuffer_add(c->stderr_data, msg, msglen);
evbuffer_add(c->stderr_data, "\n", 1);
server_client_push_stderr(c);
file_error(c, "%s\n", msg);
c->retval = 1;
} else {
*msg = toupper((u_char) *msg);

View File

@ -55,6 +55,20 @@ const struct cmd_entry cmd_show_buffer_entry = {
.exec = cmd_save_buffer_exec
};
static void
cmd_save_buffer_done(__unused struct client *c, const char *path, int error,
__unused int closed, __unused struct evbuffer *buffer, void *data)
{
struct cmdq_item *item = data;
if (!closed)
return;
if (error != 0)
cmdq_error(item, "%s: %s", path, strerror(error));
cmdq_continue(item);
}
static enum cmd_retval
cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
@ -64,18 +78,17 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
struct paste_buffer *pb;
const char *bufname, *bufdata, *start, *end, *flags;
char *msg, *path, *file;
size_t size, used, msglen, bufsize;
FILE *f;
int flags;
const char *bufname = args_get(args, 'b'), *bufdata;
size_t bufsize;
char *path;
if (!args_has(args, 'b')) {
if (bufname == NULL) {
if ((pb = paste_get_top(NULL)) == NULL) {
cmdq_error(item, "no buffers");
return (CMD_RETURN_ERROR);
}
} else {
bufname = args_get(args, 'b');
pb = paste_get_name(bufname);
if (pb == NULL) {
cmdq_error(item, "no buffer %s", bufname);
@ -88,74 +101,13 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
path = xstrdup("-");
else
path = format_single(item, args->argv[0], c, s, wl, wp);
if (strcmp(path, "-") == 0) {
free(path);
c = item->client;
if (c == NULL) {
cmdq_error(item, "can't write to stdout");
return (CMD_RETURN_ERROR);
}
if (c->session == NULL || (c->flags & CLIENT_CONTROL))
goto do_stdout;
goto do_print;
}
flags = "wb";
if (args_has(self->args, 'a'))
flags = "ab";
file = server_client_get_path(item->client, path);
flags = O_APPEND;
else
flags = 0;
file_write(item->client, path, flags, bufdata, bufsize,
cmd_save_buffer_done, item);
free(path);
f = fopen(file, flags);
if (f == NULL) {
cmdq_error(item, "%s: %s", file, strerror(errno));
free(file);
return (CMD_RETURN_ERROR);
}
if (fwrite(bufdata, 1, bufsize, f) != bufsize) {
cmdq_error(item, "%s: write error", file);
fclose(f);
free(file);
return (CMD_RETURN_ERROR);
}
fclose(f);
free(file);
return (CMD_RETURN_NORMAL);
do_stdout:
evbuffer_add(c->stdout_data, bufdata, bufsize);
server_client_push_stdout(c);
return (CMD_RETURN_NORMAL);
do_print:
if (bufsize > (INT_MAX / 4) - 1) {
cmdq_error(item, "buffer too big");
return (CMD_RETURN_ERROR);
}
msg = NULL;
used = 0;
while (used != bufsize) {
start = bufdata + used;
end = memchr(start, '\n', bufsize - used);
if (end != NULL)
size = end - start;
else
size = bufsize - used;
msglen = size * 4 + 1;
msg = xrealloc(msg, msglen);
strvisx(msg, start, size, VIS_OCTAL|VIS_TAB);
cmdq_print(item, "%s", msg);
used += size + (end != NULL);
}
free(msg);
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}

View File

@ -31,8 +31,6 @@
static enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_source_file_done(struct cmdq_item *, void *);
const struct cmd_entry cmd_source_file_entry = {
.name = "source-file",
.alias = "source",
@ -44,40 +42,130 @@ const struct cmd_entry cmd_source_file_entry = {
.exec = cmd_source_file_exec
};
struct cmd_source_file_data {
struct cmdq_item *item;
int flags;
struct cmdq_item *after;
enum cmd_retval retval;
u_int current;
char **files;
u_int nfiles;
};
static enum cmd_retval
cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
{
cfg_print_causes(item);
return (CMD_RETURN_NORMAL);
}
static void
cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata)
{
struct cmdq_item *new_item;
if (cfg_finished) {
if (cdata->retval == CMD_RETURN_ERROR && c->session == NULL)
c->retval = 1;
new_item = cmdq_get_callback(cmd_source_file_complete_cb, NULL);
cmdq_insert_after(cdata->after, new_item);
}
free(cdata->files);
free(cdata);
}
static void
cmd_source_file_done(struct client *c, const char *path, int error,
int closed, struct evbuffer *buffer, void *data)
{
struct cmd_source_file_data *cdata = data;
struct cmdq_item *item = cdata->item;
void *bdata = EVBUFFER_DATA(buffer);
size_t bsize = EVBUFFER_LENGTH(buffer);
u_int n;
struct cmdq_item *new_item;
if (!closed)
return;
if (error != 0)
cmdq_error(item, "%s: %s", path, strerror(error));
else if (bsize != 0) {
if (load_cfg_from_buffer(bdata, bsize, path, c, cdata->after,
cdata->flags, &new_item) < 0)
cdata->retval = CMD_RETURN_ERROR;
else if (new_item != NULL)
cdata->after = new_item;
}
n = ++cdata->current;
if (n < cdata->nfiles)
file_read(c, cdata->files[n], cmd_source_file_done, cdata);
else {
cmd_source_file_complete(c, cdata);
cmdq_continue(item);
}
}
static void
cmd_source_file_add(struct cmd_source_file_data *cdata, const char *path)
{
cdata->files = xreallocarray(cdata->files, cdata->nfiles + 1,
sizeof *cdata->files);
cdata->files[cdata->nfiles++] = xstrdup(path);
}
static enum cmd_retval
cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
int flags = 0;
struct client *c = item->client;
struct cmdq_item *new_item, *after;
enum cmd_retval retval;
char *pattern, *cwd;
const char *path, *error;
glob_t g;
int i;
u_int j;
struct args *args = self->args;
struct cmd_source_file_data *cdata;
int flags = 0;
struct client *c = item->client;
enum cmd_retval retval = CMD_RETURN_NORMAL;
char *pattern, *cwd;
const char *path, *error;
glob_t g;
int i, result;
u_int j;
cdata = xcalloc(1, sizeof *cdata);
cdata->item = item;
if (args_has(args, 'q'))
flags |= CMD_PARSE_QUIET;
cdata->flags |= CMD_PARSE_QUIET;
if (args_has(args, 'n'))
flags |= CMD_PARSE_PARSEONLY;
cdata->flags |= CMD_PARSE_PARSEONLY;
if (args_has(args, 'v'))
flags |= CMD_PARSE_VERBOSE;
cdata->flags |= CMD_PARSE_VERBOSE;
utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB);
retval = CMD_RETURN_NORMAL;
for (i = 0; i < args->argc; i++) {
path = args->argv[i];
if (strcmp(path, "-") == 0) {
cmd_source_file_add(cdata, "-");
continue;
}
if (*path == '/')
pattern = xstrdup(path);
else
xasprintf(&pattern, "%s/%s", cwd, path);
log_debug("%s: %s", __func__, pattern);
if (glob(pattern, 0, NULL, &g) != 0) {
error = strerror(errno);
if (errno != ENOENT || (~flags & CMD_PARSE_QUIET)) {
if ((result = glob(pattern, 0, NULL, &g)) != 0) {
if (result != GLOB_NOMATCH ||
(~flags & CMD_PARSE_QUIET)) {
if (result == GLOB_NOMATCH)
error = strerror(ENOENT);
else if (result == GLOB_NOSPACE)
error = strerror(ENOMEM);
else
error = strerror(EINVAL);
cmdq_error(item, "%s: %s", path, error);
retval = CMD_RETURN_ERROR;
}
@ -86,30 +174,19 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
}
free(pattern);
after = item;
for (j = 0; j < g.gl_pathc; j++) {
path = g.gl_pathv[j];
if (load_cfg(path, c, after, flags, &new_item) < 0)
retval = CMD_RETURN_ERROR;
else if (new_item != NULL)
after = new_item;
}
globfree(&g);
}
if (cfg_finished) {
if (retval == CMD_RETURN_ERROR && c->session == NULL)
c->retval = 1;
new_item = cmdq_get_callback(cmd_source_file_done, NULL);
cmdq_insert_after(item, new_item);
for (j = 0; j < g.gl_pathc; j++)
cmd_source_file_add(cdata, g.gl_pathv[j]);
}
cdata->after = item;
cdata->retval = retval;
if (cdata->nfiles != 0) {
file_read(c, cdata->files[0], cmd_source_file_done, cdata);
retval = CMD_RETURN_WAIT;
} else
cmd_source_file_complete(c, cdata);
free(cwd);
return (retval);
}
static enum cmd_retval
cmd_source_file_done(struct cmdq_item *item, __unused void *data)
{
cfg_print_causes(item);
return (CMD_RETURN_NORMAL);
}

View File

@ -21,6 +21,7 @@
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <fnmatch.h>
#include <limits.h>
#include <stdio.h>
#include <termios.h>
@ -169,6 +170,10 @@ void warnx(const char *, ...);
#define O_DIRECTORY 0
#endif
#ifndef FNM_CASEFOLD
#define FNM_CASEFOLD 0
#endif
#ifndef INFTIM
#define INFTIM -1
#endif

View File

@ -54,7 +54,7 @@ control_notify_input(struct client *c, struct window_pane *wp,
else
evbuffer_add_printf(message, "%c", buf[i]);
}
control_write_buffer(c, message);
control_write(c, "%s", EVBUFFER_DATA(message));
evbuffer_free(message);
}
}

View File

@ -30,23 +30,12 @@
void
control_write(struct client *c, const char *fmt, ...)
{
va_list ap;
va_list ap;
va_start(ap, fmt);
evbuffer_add_vprintf(c->stdout_data, fmt, ap);
file_vprint(c, fmt, ap);
file_print(c, "\n");
va_end(ap);
evbuffer_add(c->stdout_data, "\n", 1);
server_client_push_stdout(c);
}
/* Write a buffer, adding a terminal newline. Empties buffer. */
void
control_write_buffer(struct client *c, struct evbuffer *buffer)
{
evbuffer_add_buffer(c->stdout_data, buffer);
evbuffer_add(c->stdout_data, "\n", 1);
server_client_push_stdout(c);
}
/* Control error callback. */
@ -65,20 +54,22 @@ control_error(struct cmdq_item *item, void *data)
}
/* Control input callback. Read lines and fire commands. */
void
control_callback(struct client *c, int closed, __unused void *data)
static void
control_callback(__unused struct client *c, __unused const char *path,
int error, int closed, struct evbuffer *buffer, __unused void *data)
{
char *line;
struct cmdq_item *item;
struct cmd_parse_result *pr;
if (closed)
if (closed || error != 0)
c->flags |= CLIENT_EXIT;
for (;;) {
line = evbuffer_readln(c->stdin_data, NULL, EVBUFFER_EOL_LF);
line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
if (line == NULL)
break;
log_debug("%s: %s", __func__, line);
if (*line == '\0') { /* empty line exit */
free(line);
c->flags |= CLIENT_EXIT;
@ -104,3 +95,12 @@ control_callback(struct client *c, int closed, __unused void *data)
free(line);
}
}
void
control_start(struct client *c)
{
file_read(c, "-", control_callback, c);
if (c->flags & CLIENT_CONTROLCONTROL)
file_print(c, "\033P1000p");
}

414
file.c Normal file
View File

@ -0,0 +1,414 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
static int file_next_stream = 3;
RB_GENERATE(client_files, client_file, entry, file_cmp);
static char *
file_get_path(struct client *c, const char *file)
{
char *path;
if (*file == '/')
path = xstrdup(file);
else
xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file);
return (path);
}
int
file_cmp(struct client_file *cf1, struct client_file *cf2)
{
if (cf1->stream < cf2->stream)
return (-1);
if (cf1->stream > cf2->stream)
return (1);
return (0);
}
struct client_file *
file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
{
struct client_file *cf;
cf = xcalloc(1, sizeof *cf);
cf->c = c;
cf->references = 1;
cf->stream = stream;
cf->buffer = evbuffer_new();
if (cf->buffer == NULL)
fatalx("out of memory");
cf->cb = cb;
cf->data = cbdata;
if (cf->c != NULL) {
RB_INSERT(client_files, &cf->c->files, cf);
cf->c->references++;
}
return (cf);
}
void
file_free(struct client_file *cf)
{
if (--cf->references != 0)
return;
evbuffer_free(cf->buffer);
free(cf->path);
if (cf->c != NULL) {
RB_REMOVE(client_files, &cf->c->files, cf);
server_client_unref(cf->c);
}
free(cf);
}
static void
file_fire_done_cb(__unused int fd, __unused short events, void *arg)
{
struct client_file *cf = arg;
struct client *c = cf->c;
if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD)))
cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
file_free(cf);
}
void
file_fire_done(struct client_file *cf)
{
event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
}
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);
}
int
file_can_print(struct client *c)
{
if (c == NULL)
return (0);
if (c->session != NULL && (~c->flags & CLIENT_CONTROL))
return (0);
return (1);
}
void
file_print(struct client *c, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
file_vprint(c, fmt, ap);
va_end(ap);
}
void
file_vprint(struct client *c, const char *fmt, va_list ap)
{
struct client_file find, *cf;
struct msg_write_open msg;
if (!file_can_print(c))
return;
find.stream = 1;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
cf = file_create(c, 1, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add_vprintf(cf->buffer, fmt, ap);
msg.stream = 1;
msg.fd = STDOUT_FILENO;
msg.flags = 0;
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
} else {
evbuffer_add_vprintf(cf->buffer, fmt, ap);
file_push(cf);
}
}
void
file_print_buffer(struct client *c, void *data, size_t size)
{
struct client_file find, *cf;
struct msg_write_open msg;
if (!file_can_print(c))
return;
find.stream = 1;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
cf = file_create(c, 1, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add(cf->buffer, data, size);
msg.stream = 1;
msg.fd = STDOUT_FILENO;
msg.flags = 0;
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
} else {
evbuffer_add(cf->buffer, data, size);
file_push(cf);
}
}
void
file_error(struct client *c, const char *fmt, ...)
{
struct client_file find, *cf;
struct msg_write_open msg;
va_list ap;
if (!file_can_print(c))
return;
va_start(ap, fmt);
find.stream = 2;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
cf = file_create(c, 2, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add_vprintf(cf->buffer, fmt, ap);
msg.stream = 2;
msg.fd = STDERR_FILENO;
msg.flags = 0;
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
} else {
evbuffer_add_vprintf(cf->buffer, fmt, ap);
file_push(cf);
}
va_end(ap);
}
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;
const char *mode;
if (strcmp(path, "-") == 0) {
cf = file_create(c, file_next_stream++, cb, cbdata);
cf->path = xstrdup("-");
fd = STDOUT_FILENO;
if (c == NULL || c->flags & CLIENT_ATTACHED) {
cf->error = EBADF;
goto done;
}
goto skip;
}
cf = file_create(c, file_next_stream++, cb, cbdata);
cf->path = file_get_path(c, path);
if (c == NULL || c->flags & CLIENT_ATTACHED) {
if (flags & O_APPEND)
mode = "ab";
else
mode = "wb";
f = fopen(cf->path, mode);
if (f == NULL) {
cf->error = errno;
goto done;
}
if (fwrite(bdata, 1, bsize, f) != bsize) {
fclose(f);
cf->error = EIO;
goto done;
}
fclose(f);
goto done;
}
skip:
evbuffer_add(cf->buffer, bdata, bsize);
msglen = strlen(cf->path) + 1 + sizeof *msg;
if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
cf->error = E2BIG;
goto done;
}
msg = xmalloc(msglen);
msg->stream = cf->stream;
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) {
free(msg);
cf->error = EINVAL;
goto done;
}
free(msg);
return;
done:
file_fire_done(cf);
}
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;
int fd = -1;
char buffer[BUFSIZ];
if (strcmp(path, "-") == 0) {
cf = file_create(c, file_next_stream++, cb, cbdata);
cf->path = xstrdup("-");
fd = STDIN_FILENO;
if (c == NULL || c->flags & CLIENT_ATTACHED) {
cf->error = EBADF;
goto done;
}
goto skip;
}
cf = file_create(c, file_next_stream++, cb, cbdata);
cf->path = file_get_path(c, path);
if (c == NULL || c->flags & CLIENT_ATTACHED) {
f = fopen(cf->path, "rb");
if (f == NULL) {
cf->error = errno;
goto done;
}
for (;;) {
size = fread(buffer, 1, sizeof buffer, f);
if (evbuffer_add(cf->buffer, buffer, size) != 0) {
cf->error = ENOMEM;
goto done;
}
if (size != sizeof buffer)
break;
}
if (ferror(f)) {
cf->error = EIO;
goto done;
}
fclose(f);
goto done;
}
skip:
msglen = strlen(cf->path) + 1 + sizeof *msg;
if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
cf->error = E2BIG;
goto done;
}
msg = xmalloc(msglen);
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) {
free(msg);
cf->error = EINVAL;
goto done;
}
free(msg);
return;
done:
file_fire_done(cf);
}
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)
file_push(cf);
file_free(cf);
}
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;
msg = xmalloc(sizeof *msg);
left = EVBUFFER_LENGTH(cf->buffer);
while (left != 0) {
sent = left;
if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
msglen = (sizeof *msg) + sent;
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)
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);
}
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);
file_fire_done(cf);
}
free(msg);
}

View File

@ -598,6 +598,8 @@ mode_tree_draw(struct mode_tree_data *mtd)
xasprintf(&text, "%-*s%s%s%s: ", keylen, key, start, mti->name,
tag);
width = utf8_cstrwidth(text);
if (width > w)
width = w;
free(start);
if (mti->tagged) {
@ -607,11 +609,11 @@ mode_tree_draw(struct mode_tree_data *mtd)
if (i != mtd->current) {
screen_write_clearendofline(&ctx, 8);
screen_write_puts(&ctx, &gc0, "%s", text);
screen_write_nputs(&ctx, w, &gc0, "%s", text);
format_draw(&ctx, &gc0, w - width, mti->text, NULL);
} else {
screen_write_clearendofline(&ctx, gc.bg);
screen_write_puts(&ctx, &gc, "%s", text);
screen_write_nputs(&ctx, w, &gc, "%s", text);
format_draw(&ctx, &gc, w - width, mti->text, NULL);
}
free(text);

View File

@ -48,6 +48,12 @@ 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 *);
/* Number of attached clients. */
u_int
@ -195,16 +201,6 @@ server_client_create(int fd)
TAILQ_INIT(&c->queue);
c->stdin_data = evbuffer_new();
if (c->stdin_data == NULL)
fatalx("out of memory");
c->stdout_data = evbuffer_new();
if (c->stdout_data == NULL)
fatalx("out of memory");
c->stderr_data = evbuffer_new();
if (c->stderr_data == NULL)
fatalx("out of memory");
c->tty.fd = -1;
c->title = NULL;
@ -223,6 +219,8 @@ server_client_create(int fd)
c->prompt_buffer = NULL;
c->prompt_index = 0;
RB_INIT(&c->files);
c->flags |= CLIENT_FOCUSED;
c->keytable = key_bindings_get_table("root", 1);
@ -264,6 +262,7 @@ void
server_client_lost(struct client *c)
{
struct message_entry *msg, *msg1;
struct client_file *cf;
c->flags |= CLIENT_DEAD;
@ -271,8 +270,10 @@ server_client_lost(struct client *c)
status_prompt_clear(c);
status_message_clear(c);
if (c->stdin_callback != NULL)
c->stdin_callback(c, 1, c->stdin_callback_data);
RB_FOREACH(cf, client_files, &c->files) {
cf->error = EINTR;
file_fire_done(cf);
}
TAILQ_REMOVE(&clients, c, entry);
log_debug("lost client %p", c);
@ -286,11 +287,6 @@ server_client_lost(struct client *c)
free(c->ttyname);
free(c->term);
evbuffer_free(c->stdin_data);
evbuffer_free(c->stdout_data);
if (c->stderr_data != c->stdout_data)
evbuffer_free(c->stderr_data);
status_free(c);
free(c->title);
@ -1558,17 +1554,17 @@ server_client_click_timer(__unused int fd, __unused short events, void *data)
static void
server_client_check_exit(struct client *c)
{
struct client_file *cf;
if (~c->flags & CLIENT_EXIT)
return;
if (c->flags & CLIENT_EXITED)
return;
if (EVBUFFER_LENGTH(c->stdin_data) != 0)
return;
if (EVBUFFER_LENGTH(c->stdout_data) != 0)
return;
if (EVBUFFER_LENGTH(c->stderr_data) != 0)
return;
RB_FOREACH(cf, client_files, &c->files) {
if (EVBUFFER_LENGTH(cf->buffer) != 0)
return;
}
if (c->flags & CLIENT_ATTACHED)
notify_client("client-detached", c);
@ -1707,11 +1703,10 @@ server_client_set_title(struct client *c)
static void
server_client_dispatch(struct imsg *imsg, void *arg)
{
struct client *c = arg;
struct msg_stdin_data stdindata;
const char *data;
ssize_t datalen;
struct session *s;
struct client *c = arg;
const char *data;
ssize_t datalen;
struct session *s;
if (c->flags & CLIENT_DEAD)
return;
@ -1738,21 +1733,6 @@ server_client_dispatch(struct imsg *imsg, void *arg)
case MSG_COMMAND:
server_client_dispatch_command(c, imsg);
break;
case MSG_STDIN:
if (datalen != sizeof stdindata)
fatalx("bad MSG_STDIN size");
memcpy(&stdindata, data, sizeof stdindata);
if (c->stdin_callback == NULL)
break;
if (stdindata.size <= 0)
c->stdin_closed = 1;
else {
evbuffer_add(c->stdin_data, stdindata.data,
stdindata.size);
}
c->stdin_callback(c, c->stdin_closed, c->stdin_callback_data);
break;
case MSG_RESIZE:
if (datalen != 0)
fatalx("bad MSG_RESIZE size");
@ -1804,6 +1784,15 @@ 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);
break;
case MSG_READ:
server_client_dispatch_read_data(c, imsg);
break;
case MSG_READ_DONE:
server_client_dispatch_read_done(c, imsg);
break;
}
}
@ -1822,7 +1811,7 @@ server_client_command_done(struct cmdq_item *item, __unused void *data)
static void
server_client_dispatch_command(struct client *c, struct imsg *imsg)
{
struct msg_command_data data;
struct msg_command data;
char *buf;
size_t len;
int argc;
@ -1966,19 +1955,11 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
#endif
if (c->flags & CLIENT_CONTROL) {
c->stdin_callback = control_callback;
evbuffer_free(c->stderr_data);
c->stderr_data = c->stdout_data;
if (c->flags & CLIENT_CONTROLCONTROL)
evbuffer_add_printf(c->stdout_data, "\033P1000p");
proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
c->tty.fd = -1;
close(c->fd);
c->fd = -1;
control_start(c);
c->tty.fd = -1;
} else if (c->fd != -1) {
if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
close(c->fd);
@ -2018,93 +1999,71 @@ server_client_dispatch_shell(struct client *c)
proc_kill_peer(c->peer);
}
/* Event callback to push more stdout data if any left. */
/* Handle write ready message. */
static void
server_client_stdout_cb(__unused int fd, __unused short events, void *arg)
server_client_dispatch_write_ready(struct client *c, struct imsg *imsg)
{
struct client *c = arg;
struct msg_write_ready *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
struct client_file find, *cf;
if (~c->flags & CLIENT_DEAD)
server_client_push_stdout(c);
server_client_unref(c);
}
/* Push stdout to client if possible. */
void
server_client_push_stdout(struct client *c)
{
struct msg_stdout_data data;
size_t sent, left;
left = EVBUFFER_LENGTH(c->stdout_data);
while (left != 0) {
sent = left;
if (sent > sizeof data.data)
sent = sizeof data.data;
memcpy(data.data, EVBUFFER_DATA(c->stdout_data), sent);
data.size = sent;
if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) != 0)
break;
evbuffer_drain(c->stdout_data, sent);
left = EVBUFFER_LENGTH(c->stdout_data);
log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
sent, left);
}
if (left != 0) {
c->references++;
event_once(-1, EV_TIMEOUT, server_client_stdout_cb, c, NULL);
log_debug("%s: client %p, queued", __func__, c);
}
}
/* Event callback to push more stderr data if any left. */
static void
server_client_stderr_cb(__unused int fd, __unused short events, void *arg)
{
struct client *c = arg;
if (~c->flags & CLIENT_DEAD)
server_client_push_stderr(c);
server_client_unref(c);
}
/* Push stderr to client if possible. */
void
server_client_push_stderr(struct client *c)
{
struct msg_stderr_data data;
size_t sent, left;
if (c->stderr_data == c->stdout_data) {
server_client_push_stdout(c);
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);
}
left = EVBUFFER_LENGTH(c->stderr_data);
while (left != 0) {
sent = left;
if (sent > sizeof data.data)
sent = sizeof data.data;
memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent);
data.size = sent;
/* 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 (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0)
break;
evbuffer_drain(c->stderr_data, sent);
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;
left = EVBUFFER_LENGTH(c->stderr_data);
log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
sent, left);
}
if (left != 0) {
c->references++;
event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL);
log_debug("%s: client %p, queued", __func__, c);
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);
}
/* Add to client message log. */
void
server_client_add_message(struct client *c, const char *fmt, ...)
@ -2154,19 +2113,3 @@ server_client_get_cwd(struct client *c, struct session *s)
return (home);
return ("/");
}
/* Resolve an absolute path or relative to client working directory. */
char *
server_client_get_path(struct client *c, const char *file)
{
char *path, resolved[PATH_MAX];
if (*file == '/')
path = xstrdup(file);
else
xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file);
if (realpath(path, resolved) == NULL)
return (path);
free(path);
return (xstrdup(resolved));
}

View File

@ -440,36 +440,6 @@ server_check_unattached(void)
}
}
int
server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
void *), void *cb_data, char **cause)
{
if (c == NULL || c->session != NULL) {
*cause = xstrdup("no client with stdin");
return (-1);
}
if (c->flags & CLIENT_TERMINAL) {
*cause = xstrdup("stdin is a tty");
return (-1);
}
if (c->stdin_callback != NULL) {
*cause = xstrdup("stdin is in use");
return (-1);
}
c->stdin_callback_data = cb_data;
c->stdin_callback = cb;
c->references++;
if (c->stdin_closed)
c->stdin_callback(c, 1, c->stdin_callback_data);
proc_send(c->peer, MSG_STDIN, -1, NULL, 0);
return (0);
}
void
server_unzoom_window(struct window *w)
{

View File

@ -433,6 +433,15 @@ spawn_pane(struct spawn_context *sc, char **cause)
_exit(1);
complete:
#ifdef HAVE_UTEMPTER
if (~new_wp->flags & PANE_EMPTY) {
xasprintf(&cp, "tmux(%lu).%%%u", (long)getpid(), new_wp->id);
utempter_add_record(new_wp->fd, cp);
kill(getpid(), SIGCHLD);
free(cp);
}
#endif
new_wp->pipe_off = 0;
new_wp->flags &= ~PANE_EXITED;

115
tmux.h
View File

@ -62,9 +62,9 @@ struct winlink;
/* Client-server protocol version. */
#define PROTOCOL_VERSION 8
/* Default global configuration file. */
/* Default configuration files. */
#ifndef TMUX_CONF
#define TMUX_CONF "/etc/tmux.conf"
#define TMUX_CONF "/etc/tmux.conf:~/.tmux.conf"
#endif
/* Minimum layout cell size, NOT including border lines. */
@ -481,13 +481,21 @@ enum msgtype {
MSG_RESIZE,
MSG_SHELL,
MSG_SHUTDOWN,
MSG_STDERR,
MSG_STDIN,
MSG_STDOUT,
MSG_OLDSTDERR, /* unused */
MSG_OLDSTDIN, /* unused */
MSG_OLDSTDOUT, /* unused */
MSG_SUSPEND,
MSG_UNLOCK,
MSG_WAKEUP,
MSG_EXEC,
MSG_READ_OPEN = 300,
MSG_READ,
MSG_READ_DONE,
MSG_WRITE_OPEN,
MSG_WRITE,
MSG_WRITE_READY,
MSG_WRITE_CLOSE
};
/*
@ -495,23 +503,41 @@ enum msgtype {
*
* Don't forget to bump PROTOCOL_VERSION if any of these change!
*/
struct msg_command_data {
struct msg_command {
int argc;
}; /* followed by packed argv */
struct msg_stdin_data {
ssize_t size;
char data[BUFSIZ];
struct msg_read_open {
int stream;
int fd;
}; /* followed by path */
struct msg_read_data {
int stream;
};
struct msg_stdout_data {
ssize_t size;
char data[BUFSIZ];
struct msg_read_done {
int stream;
int error;
};
struct msg_stderr_data {
ssize_t size;
char data[BUFSIZ];
struct msg_write_open {
int stream;
int fd;
int flags;
}; /* followed by path */
struct msg_write_data {
int stream;
}; /* followed by data */
struct msg_write_ready {
int stream;
int error;
};
struct msg_write_close {
int stream;
};
/* Mode keys. */
@ -1480,6 +1506,29 @@ struct status_line {
struct status_line_entry entries[STATUS_LINES_LIMIT];
};
/* File in client. */
typedef void (*client_file_cb) (struct client *, const char *, int, int,
struct evbuffer *, void *);
struct client_file {
struct client *c;
int references;
int stream;
char *path;
struct evbuffer *buffer;
struct bufferevent *event;
int fd;
int error;
int closed;
client_file_cb cb;
void *data;
RB_ENTRY (client_file) entry;
};
RB_HEAD(client_files, client_file);
/* Client connection. */
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
typedef void (*prompt_free_cb)(void *);
@ -1513,13 +1562,6 @@ struct client {
size_t discarded;
size_t redraw;
void (*stdin_callback)(struct client *, int, void *);
void *stdin_callback_data;
struct evbuffer *stdin_data;
int stdin_closed;
struct evbuffer *stdout_data;
struct evbuffer *stderr_data;
struct event repeat_timer;
struct event click_timer;
@ -1603,6 +1645,8 @@ struct client {
void *overlay_data;
struct event overlay_timer;
struct client_files files;
TAILQ_ENTRY(client) entry;
};
TAILQ_HEAD(clients, client);
@ -1759,6 +1803,8 @@ extern struct client *cfg_client;
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 *);
@ -2077,6 +2123,8 @@ void cmd_parse_empty(struct cmd_parse_input *);
struct cmd_parse_result *cmd_parse_from_file(FILE *, struct cmd_parse_input *);
struct cmd_parse_result *cmd_parse_from_string(const char *,
struct cmd_parse_input *);
struct cmd_parse_result *cmd_parse_from_buffer(const void *, size_t,
struct cmd_parse_input *);
struct cmd_parse_result *cmd_parse_from_arguments(int, char **,
struct cmd_parse_input *);
@ -2136,6 +2184,23 @@ void alerts_reset_all(void);
void alerts_queue(struct window *, int);
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 *);
void file_free(struct client_file *);
void file_fire_done(struct client_file *);
void file_fire_read(struct client_file *);
int file_can_print(struct client *);
void printflike(2, 3) file_print(struct client *, const char *, ...);
void file_vprint(struct client *, const char *, va_list);
void file_print_buffer(struct client *, void *, size_t);
void printflike(2, 3) file_error(struct client *, const char *, ...);
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 *);
/* server.c */
extern struct tmuxproc *server_proc;
extern struct clients clients;
@ -2170,7 +2235,6 @@ void server_client_push_stdout(struct client *);
void server_client_push_stderr(struct client *);
void printflike(2, 3) server_client_add_message(struct client *, const char *,
...);
char *server_client_get_path(struct client *, const char *);
const char *server_client_get_cwd(struct client *, struct session *);
/* server-fn.c */
@ -2194,8 +2258,6 @@ void server_unlink_window(struct session *, struct winlink *);
void server_destroy_pane(struct window_pane *, int);
void server_destroy_session(struct session *);
void server_check_unattached(void);
int server_set_stdin_callback(struct client *, void (*)(struct client *,
int, void *), void *, char **);
void server_unzoom_window(struct window *);
/* status.c */
@ -2581,9 +2643,8 @@ char *default_window_name(struct window *);
char *parse_window_name(const char *);
/* control.c */
void control_callback(struct client *, int, void *);
void control_start(struct client *);
void printflike(2, 3) control_write(struct client *, const char *, ...);
void control_write_buffer(struct client *, struct evbuffer *);
/* control-notify.c */
void control_notify_input(struct client *, struct window_pane *,

6
tty.c
View File

@ -340,7 +340,8 @@ tty_start_tty(struct tty *tty)
tty->flags |= TTY_STARTED;
tty_invalidate(tty);
tty_force_cursor_colour(tty, "");
if (*tty->ccolour != '\0')
tty_force_cursor_colour(tty, "");
tty->mouse_drag_flag = 0;
tty->mouse_drag_update = NULL;
@ -386,7 +387,8 @@ tty_stop_tty(struct tty *tty)
}
if (tty->mode & MODE_BRACKETPASTE)
tty_raw(tty, "\033[?2004l");
tty_raw(tty, tty_term_string(tty->term, TTYC_CR));
if (*tty->ccolour != '\0')
tty_raw(tty, tty_term_string(tty->term, TTYC_CR));
tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
if (tty_term_has(tty->term, TTYC_KMOUS))

View File

@ -207,9 +207,9 @@ window_buffer_draw(__unused void *modedata, void *itemdata,
{
struct window_buffer_itemdata *item = itemdata;
struct paste_buffer *pb;
char line[1024];
const char *pdata, *end, *cp;
size_t psize, at;
const char *pdata, *start, *end;
char *buf = NULL;
size_t psize, len;
u_int i, cx = ctx->s->cx, cy = ctx->s->cy;
pb = paste_get_name(item->name);
@ -218,27 +218,22 @@ window_buffer_draw(__unused void *modedata, void *itemdata,
pdata = end = paste_buffer_data(pb, &psize);
for (i = 0; i < sy; i++) {
at = 0;
while (end != pdata + psize && *end != '\n') {
if ((sizeof line) - at > 5) {
cp = vis(line + at, *end, VIS_OCTAL|VIS_TAB, 0);
at = cp - line;
}
start = end;
while (end != pdata + psize && *end != '\n')
end++;
}
if (at > sx)
at = sx;
line[at] = '\0';
if (*line != '\0') {
buf = xreallocarray(buf, 4, end - start + 1);
len = utf8_strvis(buf, start, end - start, VIS_OCTAL|VIS_TAB);
if (*buf != '\0') {
screen_write_cursormove(ctx, cx, cy + i, 0);
screen_write_puts(ctx, &grid_default_cell, "%s", line);
screen_write_nputs(ctx, sx, &grid_default_cell, "%s",
buf);
}
if (end == pdata + psize)
break;
end++;
}
free(buf);
}
static int

View File

@ -2082,16 +2082,28 @@ static int
window_copy_search_lr(struct grid *gd,
struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
{
u_int ax, bx, px;
int matched;
u_int ax, bx, px, pywrap, endline;
int matched;
struct grid_line *gl;
endline = gd->hsize + gd->sy - 1;
for (ax = first; ax < last; ax++) {
if (ax + sgd->sx > gd->sx)
break;
for (bx = 0; bx < sgd->sx; bx++) {
px = ax + bx;
matched = window_copy_search_compare(gd, px, py, sgd,
bx, cis);
pywrap = py;
/* Wrap line. */
while (px >= gd->sx && pywrap < endline) {
gl = grid_get_line(gd, pywrap);
if (~gl->flags & GRID_LINE_WRAPPED)
break;
px -= gd->sx;
pywrap++;
}
/* We have run off the end of the grid. */
if (px >= gd->sx)
break;
matched = window_copy_search_compare(gd, px, pywrap,
sgd, bx, cis);
if (!matched)
break;
}
@ -2107,16 +2119,28 @@ static int
window_copy_search_rl(struct grid *gd,
struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
{
u_int ax, bx, px;
int matched;
u_int ax, bx, px, pywrap, endline;
int matched;
struct grid_line *gl;
for (ax = last + 1; ax > first; ax--) {
if (gd->sx - (ax - 1) < sgd->sx)
continue;
endline = gd->hsize + gd->sy - 1;
for (ax = last; ax > first; ax--) {
for (bx = 0; bx < sgd->sx; bx++) {
px = ax - 1 + bx;
matched = window_copy_search_compare(gd, px, py, sgd,
bx, cis);
pywrap = py;
/* Wrap line. */
while (px >= gd->sx && pywrap < endline) {
gl = grid_get_line(gd, pywrap);
if (~gl->flags & GRID_LINE_WRAPPED)
break;
px -= gd->sx;
pywrap++;
}
/* We have run off the end of the grid. */
if (px >= gd->sx)
break;
matched = window_copy_search_compare(gd, px, pywrap,
sgd, bx, cis);
if (!matched)
break;
}
@ -2135,7 +2159,7 @@ window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
if (*fy == 0) { /* top */
if (wrapflag) {
*fx = screen_size_x(s) - 1;
*fy = screen_hsize(s) + screen_size_y(s);
*fy = screen_hsize(s) + screen_size_y(s) - 1;
}
return;
}
@ -2149,7 +2173,7 @@ static void
window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
{
if (*fx == screen_size_x(s) - 1) { /* right */
if (*fy == screen_hsize(s) + screen_size_y(s)) { /* bottom */
if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */
if (wrapflag) {
*fx = 0;
*fy = 0;
@ -2199,12 +2223,12 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
} else {
for (i = fy + 1; endline < i; i--) {
found = window_copy_search_rl(gd, sgd, &px, i - 1, 0,
fx, cis);
fx + 1, cis);
if (found) {
i--;
break;
}
fx = gd->sx;
fx = gd->sx - 1;
}
}

View File

@ -1608,31 +1608,28 @@ winlink_shuffle_up(struct session *s, struct winlink *wl)
}
static void
window_pane_input_callback(struct client *c, int closed, void *data)
window_pane_input_callback(struct client *c, __unused const char *path,
int error, int closed, struct evbuffer *buffer, void *data)
{
struct window_pane_input_data *cdata = data;
struct window_pane *wp;
struct evbuffer *evb = c->stdin_data;
u_char *buf = EVBUFFER_DATA(evb);
size_t len = EVBUFFER_LENGTH(evb);
u_char *buf = EVBUFFER_DATA(buffer);
size_t len = EVBUFFER_LENGTH(buffer);
wp = window_pane_find_by_id(cdata->wp);
if (wp == NULL || closed || c->flags & CLIENT_DEAD) {
if (wp == NULL || closed || error != 0 || c->flags & CLIENT_DEAD) {
if (wp == NULL)
c->flags |= CLIENT_EXIT;
evbuffer_drain(evb, len);
c->stdin_callback = NULL;
server_client_unref(c);
evbuffer_drain(buffer, len);
cmdq_continue(cdata->item);
free(cdata);
server_client_unref(c);
free(cdata);
return;
}
input_parse_buffer(wp, buf, len);
evbuffer_drain(evb, len);
evbuffer_drain(buffer, len);
}
int
@ -1651,6 +1648,8 @@ window_pane_start_input(struct window_pane *wp, struct cmdq_item *item,
cdata->item = item;
cdata->wp = wp->id;
return (server_set_stdin_callback(c, window_pane_input_callback, cdata,
cause));
c->references++;
file_read(c, "-", window_pane_input_callback, cdata);
return (0);
}