diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | cfg.c | 46 | ||||
-rw-r--r-- | client.c | 330 | ||||
-rw-r--r-- | cmd-capture-pane.c | 15 | ||||
-rw-r--r-- | cmd-load-buffer.c | 160 | ||||
-rw-r--r-- | cmd-queue.c | 34 | ||||
-rw-r--r-- | cmd-save-buffer.c | 98 | ||||
-rw-r--r-- | cmd-source-file.c | 147 | ||||
-rw-r--r-- | control-notify.c | 2 | ||||
-rw-r--r-- | control.c | 36 | ||||
-rw-r--r-- | file.c | 390 | ||||
-rw-r--r-- | server-client.c | 213 | ||||
-rw-r--r-- | server-fn.c | 30 | ||||
-rw-r--r-- | tmux.h | 112 | ||||
-rw-r--r-- | window.c | 27 |
15 files changed, 1108 insertions, 533 deletions
diff --git a/Makefile.am b/Makefile.am index dc281383..6902477e 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 \ @@ -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, ...) { @@ -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, @@ -52,13 +51,12 @@ 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 *); @@ -217,7 +215,7 @@ 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 +289,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 +302,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 +425,234 @@ 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; + + close(cf->fd); + cf->fd = -1; +} + +/* 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); + } +} + +/* Open write file. */ +static void +client_write_open(void *data, size_t datalen) +{ + struct msg_write_open *msg = data; + 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"); + log_debug("open write file %d %s", msg->stream, msg->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(msg->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; + } - proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data); - if (data.size <= 0) - event_del(&client_stdin); + 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; + + 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", msg->size, cf->stream); + + if (cf->event != NULL) + bufferevent_write(cf->event, msg->data, msg->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); + } } -/* Force write to file descriptor. */ +/* File read callback. */ static void -client_write(int fd, const char *data, size_t size) +client_read_callback(__unused struct bufferevent *bev, void *arg) { - 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 client_file *cf = arg; + void *bdata; + size_t bsize; + struct msg_read_data msg; + + for (;;) { + bdata = EVBUFFER_DATA(cf->event->input); + bsize = EVBUFFER_LENGTH(cf->event->input); + + if (bsize == 0) break; + if (bsize > sizeof msg.data) + bsize = sizeof msg.data; + log_debug("read %zu from file %d", bsize, cf->stream); + + memcpy(msg.data, bdata, bsize); + msg.size = bsize; + + msg.stream = cf->stream; + proc_send(client_peer, MSG_READ, -1, &msg, sizeof msg); + + evbuffer_drain(cf->event->input, bsize); + } +} + +/* 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; + 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"); + log_debug("open read file %d %s", msg->stream, msg->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(msg->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. */ @@ -534,6 +726,29 @@ client_signal(int sig) } } +/* Exit if all streams flushed. */ +static void +client_exit(__unused int fd, __unused short events, __unused void *arg) +{ + 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); + else + event_once(-1, EV_TIMEOUT, client_exit, NULL, NULL); +} + /* Callback for client read events. */ static void client_dispatch(struct imsg *imsg, __unused void *arg) @@ -555,12 +770,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 +782,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 +801,15 @@ client_dispatch_wait(struct imsg *imsg) memcpy(&retval, data, sizeof retval); client_exitval = retval; } - proc_exit(client_proc); + event_once(-1, EV_TIMEOUT, client_exit, NULL, NULL); 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 +833,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-queue.c b/cmd-queue.c index fa6999e8..6019ab51 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,20 +493,20 @@ 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); @@ -517,7 +515,7 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...) window_copy_vadd(wp, fmt, ap); } - 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..0f1f79de 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,31 +42,115 @@ 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; + 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 @@ -86,30 +168,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); |