summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicholas Marriott <nicholas.marriott@gmail.com>2019-12-18 20:24:26 +0000
committerNicholas Marriott <nicholas.marriott@gmail.com>2019-12-18 20:24:26 +0000
commitdd3c72f132c911b0ba61b56a56f46510704d3392 (patch)
treeb17e5ae8df99a9374f9642a2cc18be4f1caa7308
parent1a0e5fe933e89932f2f658936c52eb50644fbef4 (diff)
parent54efe337993b5571728a09b247c7f39d493659a8 (diff)
Merge branch 'master' into sixel
-rw-r--r--CHANGES47
-rw-r--r--Makefile.am1
-rw-r--r--cfg.c46
-rw-r--r--client.c350
-rw-r--r--cmd-capture-pane.c15
-rw-r--r--cmd-load-buffer.c160
-rw-r--r--cmd-parse.y10
-rw-r--r--cmd-queue.c36
-rw-r--r--cmd-save-buffer.c98
-rw-r--r--cmd-source-file.c159
-rw-r--r--compat.h5
-rw-r--r--control-notify.c2
-rw-r--r--control.c36
-rw-r--r--file.c414
-rw-r--r--mode-tree.c6
-rw-r--r--server-client.c229
-rw-r--r--server-fn.c30
-rw-r--r--spawn.c9
-rw-r--r--tmux.h111
-rw-r--r--tty.c6
-rw-r--r--window-buffer.c27
-rw-r--r--window-copy.c58
-rw-r--r--window.c27
23 files changed, 1287 insertions, 595 deletions
diff --git a/CHANGES b/CHANGES
index 051c8e05..cd3a1b8a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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.
diff --git a/Makefile.am b/Makefile.am
index 40aa8705..9ee8b737 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -132,6 +132,7 @@ dist_tmux_SOURCES = \
control-notify.c \
control.c \
environ.c \
+ file.c \
format.c \
format-draw.c \
grid-view.c \
diff --git a/cfg.c b/cfg.c
index 8dbb6233..ddc112a2 100644
--- a/cfg.c
+++ b/cfg.c
@@ -194,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, ...)
{
diff --git a/client.c b/client.c
index 204a431b..26c392b1 100644
--- a/client.c
+++ b/client.c
@@ -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);
+
+ bufferevent_free(cf->event);
+ cf->event = NULL;
- proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data);
- if (data.size <= 0)
- event_del(&client_stdin);
+ 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();
}
-/* Force write to file descriptor. */
+/* Open write file. */
static void
-client_write(int fd, const char *data, size_t size)
+client_write_open(void *data, size_t datalen)
{
- ssize_t used;
-
- 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;
+ 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 */
}
- data += used;
- size -= used;
}
+ 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;
}
}
diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c
index fc6c26e4..18be3f77 100644
--- a/cmd-capture-pane.c
+++ b/cmd-capture-pane.c
@@ -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'))
diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c
index cdf44bf7..5e930126 100644
--- a/cmd-load-buffer.c
+++ b/cmd-load-buffer.c
@@ -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);
}
diff --git a/cmd-parse.y b/cmd-parse.y
index 2c924010..97b50f57 100644
--- a/cmd-parse.y
+++ b/cmd-parse.y
@@ -746,6 +746,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;
struct cmd_parse_commands *cmds;
@@ -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;
diff --git a/cmd-queue.c b/cmd-queue.c
index fa6999e8..69e4f6b2 100644
--- a/cmd-queue.c
+++ b/cmd-queue.c
@@ -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);
diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c
index 3395612f..84e50242 100644
--- a/cmd-save-buffer.c
+++ b/cmd-save-buffer.c
@@ -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);