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); - 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; } } 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 @@ -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; 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); - 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); } diff --git a/cmd-source-file.c b/cmd-source-file.c index 2e01ae67..bdcdd222 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -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); -} diff --git a/compat.h b/compat.h index df1ac03c..794ac67d 100644 --- a/compat.h +++ b/compat.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -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 diff --git a/control-notify.c b/control-notify.c index a1e2b7bf..babfcf2d 100644 --- a/control-notify.c +++ b/control-notify.c @@ -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); } } diff --git a/control.c b/control.c index c4cf5338..b8a16e73 100644 --- a/control.c +++ b/control.c @@ -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"); +} diff --git a/file.c b/file.c new file mode 100644 index 00000000..2c06765c --- /dev/null +++ b/file.c @@ -0,0 +1,414 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2019 Nicholas Marriott + * + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/mode-tree.c b/mode-tree.c index 054989fb..b9fa5f65 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -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); diff --git a/server-client.c b/server-client.c index 9a24ca39..27d80cbd 100644 --- a/server-client.c +++ b/server-client.c @@ -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)); -} diff --git a/server-fn.c b/server-fn.c index cfafef21..2247f1c5 100644 --- a/server-fn.c +++ b/server-fn.c @@ -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) { diff --git a/spawn.c b/spawn.c index 54021817..51e4ae78 100644 --- a/spawn.c +++ b/spawn.c @@ -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; diff --git a/tmux.h b/tmux.h index 288db687..64923c90 100644 --- a/tmux.h +++ b/tmux.h @@ -482,13 +482,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 }; /* @@ -496,23 +504,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. */ @@ -1495,6 +1521,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 *); @@ -1528,13 +1577,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; @@ -1618,6 +1660,8 @@ struct client { void *overlay_data; struct event overlay_timer; + struct client_files files; + TAILQ_ENTRY(client) entry; }; TAILQ_HEAD(clients, client); @@ -1774,6 +1818,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 *); @@ -2093,6 +2139,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 *); @@ -2152,6 +2200,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; @@ -2186,7 +2251,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 */ @@ -2210,8 +2274,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 */ @@ -2598,9 +2660,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 *, diff --git a/tty.c b/tty.c index e69d9c38..3b18bbaa 100644 --- a/tty.c +++ b/tty.c @@ -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)) diff --git a/window-buffer.c b/window-buffer.c index 0412dc97..39d98608 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -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 diff --git a/window-copy.c b/window-copy.c index 16708d04..4787fb27 100644 --- a/window-copy.c +++ b/window-copy.c @@ -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; } } diff --git a/window.c b/window.c index 242ad48d..96632058 100644 --- a/window.c +++ b/window.c @@ -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); }