mirror of
https://github.com/tmux/tmux.git
synced 2024-12-25 02:48:47 +00:00
Merge branch 'master' into sixel-passthrough
This commit is contained in:
commit
5bb075487f
47
CHANGES
47
CHANGES
@ -1,5 +1,52 @@
|
|||||||
CHANGES FROM 3.0 to X.X
|
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
|
* Do not use bright when emulating 256 colours on an 8 colour terminal because
|
||||||
it is also bold on some terminals.
|
it is also bold on some terminals.
|
||||||
|
|
||||||
|
@ -11,7 +11,8 @@ EXTRA_DIST = \
|
|||||||
dist_EXTRA_tmux_SOURCES = compat/*.[ch]
|
dist_EXTRA_tmux_SOURCES = compat/*.[ch]
|
||||||
|
|
||||||
# Preprocessor flags.
|
# Preprocessor flags.
|
||||||
AM_CPPFLAGS += @XOPEN_DEFINES@ -DTMUX_CONF="\"$(sysconfdir)/tmux.conf\""
|
AM_CPPFLAGS += @XOPEN_DEFINES@
|
||||||
|
AM_CPPFLAGS += -DTMUX_CONF="\"$(sysconfdir)/tmux.conf:~/.tmux.conf:~/.config/tmux/tmux.conf\""
|
||||||
|
|
||||||
# Additional object files.
|
# Additional object files.
|
||||||
LDADD = $(LIBOBJS)
|
LDADD = $(LIBOBJS)
|
||||||
@ -131,6 +132,7 @@ dist_tmux_SOURCES = \
|
|||||||
control-notify.c \
|
control-notify.c \
|
||||||
control.c \
|
control.c \
|
||||||
environ.c \
|
environ.c \
|
||||||
|
file.c \
|
||||||
format.c \
|
format.c \
|
||||||
format-draw.c \
|
format-draw.c \
|
||||||
grid-view.c \
|
grid-view.c \
|
||||||
|
107
cfg.c
107
cfg.c
@ -66,12 +66,45 @@ set_cfg_file(const char *path)
|
|||||||
cfg_file = xstrdup(path);
|
cfg_file = xstrdup(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
expand_cfg_file(const char *path, const char *home)
|
||||||
|
{
|
||||||
|
char *expanded, *name;
|
||||||
|
const char *end;
|
||||||
|
struct environ_entry *value;
|
||||||
|
|
||||||
|
if (strncmp(path, "~/", 2) == 0) {
|
||||||
|
if (home == NULL)
|
||||||
|
return (NULL);
|
||||||
|
xasprintf(&expanded, "%s%s", home, path + 1);
|
||||||
|
return (expanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*path == '$') {
|
||||||
|
end = strchr(path, '/');
|
||||||
|
if (end == NULL)
|
||||||
|
name = xstrdup(path + 1);
|
||||||
|
else
|
||||||
|
name = xstrndup(path + 1, end - path - 1);
|
||||||
|
value = environ_find(global_environ, name);
|
||||||
|
free(name);
|
||||||
|
if (value == NULL)
|
||||||
|
return (NULL);
|
||||||
|
if (end == NULL)
|
||||||
|
end = "";
|
||||||
|
xasprintf(&expanded, "%s%s", value->value, end);
|
||||||
|
return (expanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (xstrdup(path));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
start_cfg(void)
|
start_cfg(void)
|
||||||
{
|
{
|
||||||
const char *home;
|
const char *home = find_home();
|
||||||
int flags = 0;
|
|
||||||
struct client *c;
|
struct client *c;
|
||||||
|
char *path, *copy, *next, *expanded;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Configuration files are loaded without a client, so commands are run
|
* Configuration files are loaded without a client, so commands are run
|
||||||
@ -89,15 +122,21 @@ start_cfg(void)
|
|||||||
cmdq_append(c, cfg_item);
|
cmdq_append(c, cfg_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg_file == NULL)
|
if (cfg_file == NULL) {
|
||||||
load_cfg(TMUX_CONF, c, NULL, CMD_PARSE_QUIET, NULL);
|
path = copy = xstrdup(TMUX_CONF);
|
||||||
|
while ((next = strsep(&path, ":")) != NULL) {
|
||||||
if (cfg_file == NULL && (home = find_home()) != NULL) {
|
expanded = expand_cfg_file(next, home);
|
||||||
xasprintf(&cfg_file, "%s/.tmux.conf", home);
|
if (expanded == NULL) {
|
||||||
flags = CMD_PARSE_QUIET;
|
log_debug("couldn't expand %s", next);
|
||||||
}
|
continue;
|
||||||
if (cfg_file != NULL)
|
}
|
||||||
load_cfg(cfg_file, c, NULL, flags, NULL);
|
log_debug("expanded %s to %s", next, expanded);
|
||||||
|
load_cfg(expanded, c, NULL, CMD_PARSE_QUIET, NULL);
|
||||||
|
free(expanded);
|
||||||
|
}
|
||||||
|
free(copy);
|
||||||
|
} else
|
||||||
|
load_cfg(cfg_file, c, NULL, 0, NULL);
|
||||||
|
|
||||||
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
|
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
|
||||||
}
|
}
|
||||||
@ -155,6 +194,52 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
|
|||||||
return (0);
|
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
|
void
|
||||||
cfg_add_cause(const char *fmt, ...)
|
cfg_add_cause(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
352
client.c
352
client.c
@ -35,7 +35,6 @@
|
|||||||
static struct tmuxproc *client_proc;
|
static struct tmuxproc *client_proc;
|
||||||
static struct tmuxpeer *client_peer;
|
static struct tmuxpeer *client_peer;
|
||||||
static int client_flags;
|
static int client_flags;
|
||||||
static struct event client_stdin;
|
|
||||||
static enum {
|
static enum {
|
||||||
CLIENT_EXIT_NONE,
|
CLIENT_EXIT_NONE,
|
||||||
CLIENT_EXIT_DETACHED,
|
CLIENT_EXIT_DETACHED,
|
||||||
@ -46,19 +45,19 @@ static enum {
|
|||||||
CLIENT_EXIT_EXITED,
|
CLIENT_EXIT_EXITED,
|
||||||
CLIENT_EXIT_SERVER_EXITED,
|
CLIENT_EXIT_SERVER_EXITED,
|
||||||
} client_exitreason = CLIENT_EXIT_NONE;
|
} client_exitreason = CLIENT_EXIT_NONE;
|
||||||
|
static int client_exitflag;
|
||||||
static int client_exitval;
|
static int client_exitval;
|
||||||
static enum msgtype client_exittype;
|
static enum msgtype client_exittype;
|
||||||
static const char *client_exitsession;
|
static const char *client_exitsession;
|
||||||
static const char *client_execshell;
|
static const char *client_execshell;
|
||||||
static const char *client_execcmd;
|
static const char *client_execcmd;
|
||||||
static int client_attached;
|
static int client_attached;
|
||||||
|
static struct client_files client_files = RB_INITIALIZER(&client_files);
|
||||||
|
|
||||||
static __dead void client_exec(const char *,const char *);
|
static __dead void client_exec(const char *,const char *);
|
||||||
static int client_get_lock(char *);
|
static int client_get_lock(char *);
|
||||||
static int client_connect(struct event_base *, const char *, int);
|
static int client_connect(struct event_base *, const char *, int);
|
||||||
static void client_send_identify(const char *, const char *);
|
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_signal(int);
|
||||||
static void client_dispatch(struct imsg *, void *);
|
static void client_dispatch(struct imsg *, void *);
|
||||||
static void client_dispatch_attached(struct imsg *);
|
static void client_dispatch_attached(struct imsg *);
|
||||||
@ -211,13 +210,34 @@ client_exit_message(void)
|
|||||||
return ("unknown reason");
|
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. */
|
/* Client main loop. */
|
||||||
int
|
int
|
||||||
client_main(struct event_base *base, int argc, char **argv, int flags)
|
client_main(struct event_base *base, int argc, char **argv, int flags)
|
||||||
{
|
{
|
||||||
struct cmd_parse_result *pr;
|
struct cmd_parse_result *pr;
|
||||||
struct cmd *cmd;
|
struct cmd *cmd;
|
||||||
struct msg_command_data *data;
|
struct msg_command *data;
|
||||||
int cmdflags, fd, i;
|
int cmdflags, fd, i;
|
||||||
const char *ttynam, *cwd;
|
const char *ttynam, *cwd;
|
||||||
pid_t ppid;
|
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().
|
* "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");
|
fatal("pledge failed");
|
||||||
|
|
||||||
/* Free stuff that is not used in the client. */
|
/* 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);
|
options_free(global_w_options);
|
||||||
environ_free(global_environ);
|
environ_free(global_environ);
|
||||||
|
|
||||||
/* Create stdin handler. */
|
/* Set up control mode. */
|
||||||
setblocking(STDIN_FILENO, 0);
|
|
||||||
event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST,
|
|
||||||
client_stdin_callback, NULL);
|
|
||||||
if (client_flags & CLIENT_CONTROLCONTROL) {
|
if (client_flags & CLIENT_CONTROLCONTROL) {
|
||||||
if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) {
|
if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) {
|
||||||
fprintf(stderr, "tcgetattr failed: %s\n",
|
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);
|
proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Callback for client stdin read events. */
|
/* File write error callback. */
|
||||||
static void
|
static void
|
||||||
client_stdin_callback(__unused int fd, __unused short events,
|
client_write_error_callback(__unused struct bufferevent *bev,
|
||||||
__unused void *arg)
|
__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);
|
log_debug("write error file %d", cf->stream);
|
||||||
if (data.size == -1 && (errno == EINTR || errno == EAGAIN))
|
|
||||||
return;
|
|
||||||
|
|
||||||
proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data);
|
bufferevent_free(cf->event);
|
||||||
if (data.size <= 0)
|
cf->event = NULL;
|
||||||
event_del(&client_stdin);
|
|
||||||
|
close(cf->fd);
|
||||||
|
cf->fd = -1;
|
||||||
|
|
||||||
|
if (client_exitflag)
|
||||||
|
client_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Force write to file descriptor. */
|
/* File write callback. */
|
||||||
static void
|
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);
|
if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
|
||||||
while (size != 0) {
|
bufferevent_free(cf->event);
|
||||||
used = write(fd, data, size);
|
close(cf->fd);
|
||||||
if (used == -1) {
|
RB_REMOVE(client_files, &client_files, cf);
|
||||||
if (errno == EINTR || errno == EAGAIN)
|
file_free(cf);
|
||||||
continue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
data += used;
|
|
||||||
size -= used;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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. */
|
/* Run command in shell; used for -c. */
|
||||||
@ -555,12 +789,10 @@ client_dispatch(struct imsg *imsg, __unused void *arg)
|
|||||||
static void
|
static void
|
||||||
client_dispatch_wait(struct imsg *imsg)
|
client_dispatch_wait(struct imsg *imsg)
|
||||||
{
|
{
|
||||||
char *data;
|
char *data;
|
||||||
ssize_t datalen;
|
ssize_t datalen;
|
||||||
struct msg_stdout_data stdoutdata;
|
int retval;
|
||||||
struct msg_stderr_data stderrdata;
|
static int pledge_applied;
|
||||||
int retval;
|
|
||||||
static int pledge_applied;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "sendfd" is no longer required once all of the identify messages
|
* "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.
|
* get the first message from the server.
|
||||||
*/
|
*/
|
||||||
if (!pledge_applied) {
|
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");
|
fatal("pledge failed");
|
||||||
pledge_applied = 1;
|
pledge_applied = 1;
|
||||||
};
|
}
|
||||||
|
|
||||||
data = imsg->data;
|
data = imsg->data;
|
||||||
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||||
@ -586,38 +820,16 @@ client_dispatch_wait(struct imsg *imsg)
|
|||||||
memcpy(&retval, data, sizeof retval);
|
memcpy(&retval, data, sizeof retval);
|
||||||
client_exitval = retval;
|
client_exitval = retval;
|
||||||
}
|
}
|
||||||
proc_exit(client_proc);
|
client_exitflag = 1;
|
||||||
|
client_exit();
|
||||||
break;
|
break;
|
||||||
case MSG_READY:
|
case MSG_READY:
|
||||||
if (datalen != 0)
|
if (datalen != 0)
|
||||||
fatalx("bad MSG_READY size");
|
fatalx("bad MSG_READY size");
|
||||||
|
|
||||||
event_del(&client_stdin);
|
|
||||||
client_attached = 1;
|
client_attached = 1;
|
||||||
proc_send(client_peer, MSG_RESIZE, -1, NULL, 0);
|
proc_send(client_peer, MSG_RESIZE, -1, NULL, 0);
|
||||||
break;
|
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:
|
case MSG_VERSION:
|
||||||
if (datalen != 0)
|
if (datalen != 0)
|
||||||
fatalx("bad MSG_VERSION size");
|
fatalx("bad MSG_VERSION size");
|
||||||
@ -641,6 +853,18 @@ client_dispatch_wait(struct imsg *imsg)
|
|||||||
case MSG_EXITED:
|
case MSG_EXITED:
|
||||||
proc_exit(client_proc);
|
proc_exit(client_proc);
|
||||||
break;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ static enum cmd_retval
|
|||||||
cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
|
cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||||
{
|
{
|
||||||
struct args *args = self->args;
|
struct args *args = self->args;
|
||||||
struct client *c;
|
struct client *c = item->client;
|
||||||
struct window_pane *wp = item->target.wp;
|
struct window_pane *wp = item->target.wp;
|
||||||
char *buf, *cause;
|
char *buf, *cause;
|
||||||
const char *bufname;
|
const char *bufname;
|
||||||
@ -214,18 +214,15 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
|
|
||||||
if (args_has(args, 'p')) {
|
if (args_has(args, 'p')) {
|
||||||
c = item->client;
|
if (!file_can_print(c)) {
|
||||||
if (c == NULL ||
|
cmdq_error(item, "can't write output to client");
|
||||||
(c->session != NULL && !(c->flags & CLIENT_CONTROL))) {
|
|
||||||
cmdq_error(item, "can't write to stdout");
|
|
||||||
free(buf);
|
free(buf);
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
evbuffer_add(c->stdout_data, buf, len);
|
file_print_buffer(c, buf, len);
|
||||||
free(buf);
|
|
||||||
if (args_has(args, 'P') && len > 0)
|
if (args_has(args, 'P') && len > 0)
|
||||||
evbuffer_add(c->stdout_data, "\n", 1);
|
file_print(c, "\n");
|
||||||
server_client_push_stdout(c);
|
free(buf);
|
||||||
} else {
|
} else {
|
||||||
bufname = NULL;
|
bufname = NULL;
|
||||||
if (args_has(args, 'b'))
|
if (args_has(args, 'b'))
|
||||||
|
@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
static enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmdq_item *);
|
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 = {
|
const struct cmd_entry cmd_load_buffer_entry = {
|
||||||
.name = "load-buffer",
|
.name = "load-buffer",
|
||||||
.alias = "loadb",
|
.alias = "loadb",
|
||||||
@ -48,9 +46,40 @@ const struct cmd_entry cmd_load_buffer_entry = {
|
|||||||
|
|
||||||
struct cmd_load_buffer_data {
|
struct cmd_load_buffer_data {
|
||||||
struct cmdq_item *item;
|
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
|
static enum cmd_retval
|
||||||
cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
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 session *s = item->target.s;
|
||||||
struct winlink *wl = item->target.wl;
|
struct winlink *wl = item->target.wl;
|
||||||
struct window_pane *wp = item->target.wp;
|
struct window_pane *wp = item->target.wp;
|
||||||
FILE *f;
|
const char *bufname = args_get(args, 'b');
|
||||||
const char *bufname;
|
char *path;
|
||||||
char *pdata = NULL, *new_pdata, *cause;
|
|
||||||
char *path, *file;
|
|
||||||
size_t psize;
|
|
||||||
int ch, error;
|
|
||||||
|
|
||||||
bufname = NULL;
|
cdata = xmalloc(sizeof *cdata);
|
||||||
if (args_has(args, 'b'))
|
cdata->item = item;
|
||||||
bufname = args_get(args, 'b');
|
if (bufname != NULL)
|
||||||
|
cdata->name = xstrdup(bufname);
|
||||||
|
else
|
||||||
|
cdata->name = NULL;
|
||||||
|
|
||||||
path = format_single(item, args->argv[0], c, s, wl, wp);
|
path = format_single(item, args->argv[0], c, s, wl, wp);
|
||||||
if (strcmp(path, "-") == 0) {
|
file_read(item->client, path, cmd_load_buffer_done, cdata);
|
||||||
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);
|
|
||||||
free(path);
|
free(path);
|
||||||
|
|
||||||
f = fopen(file, "rb");
|
return (CMD_RETURN_WAIT);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
10
cmd-parse.y
10
cmd-parse.y
@ -745,6 +745,12 @@ cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
|
|||||||
|
|
||||||
struct cmd_parse_result *
|
struct cmd_parse_result *
|
||||||
cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
|
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;
|
static struct cmd_parse_result pr;
|
||||||
struct cmd_parse_input input;
|
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);
|
memset(&pr, 0, sizeof pr);
|
||||||
|
|
||||||
if (*s == '\0') {
|
if (len == 0) {
|
||||||
pr.status = CMD_PARSE_EMPTY;
|
pr.status = CMD_PARSE_EMPTY;
|
||||||
pr.cmdlist = NULL;
|
pr.cmdlist = NULL;
|
||||||
pr.error = NULL;
|
pr.error = NULL;
|
||||||
return (&pr);
|
return (&pr);
|
||||||
}
|
}
|
||||||
|
|
||||||
cmds = cmd_parse_do_buffer(s, strlen(s), pi, &cause);
|
cmds = cmd_parse_do_buffer(buf, len, pi, &cause);
|
||||||
if (cmds == NULL) {
|
if (cmds == NULL) {
|
||||||
pr.status = CMD_PARSE_ERROR;
|
pr.status = CMD_PARSE_ERROR;
|
||||||
pr.error = cause;
|
pr.error = cause;
|
||||||
|
36
cmd-queue.c
36
cmd-queue.c
@ -475,13 +475,11 @@ void
|
|||||||
cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
|
cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
|
||||||
{
|
{
|
||||||
struct client *c = item->client;
|
struct client *c = item->client;
|
||||||
|
long t = item->time;
|
||||||
|
u_int number = item->number;
|
||||||
|
|
||||||
if (c == NULL || !(c->flags & CLIENT_CONTROL))
|
if (c != NULL && (c->flags & CLIENT_CONTROL))
|
||||||
return;
|
file_print(c, "%%%s %ld %u %d\n", guard, t, number, flags);
|
||||||
|
|
||||||
evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
|
|
||||||
(long)item->time, item->number, flags);
|
|
||||||
server_client_push_stdout(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show message from command. */
|
/* Show message from command. */
|
||||||
@ -495,29 +493,29 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
|
|||||||
char *tmp, *msg;
|
char *tmp, *msg;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
|
xvasprintf(&msg, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
log_debug("%s: %s", __func__, msg);
|
||||||
|
|
||||||
if (c == NULL)
|
if (c == NULL)
|
||||||
/* nothing */;
|
/* nothing */;
|
||||||
else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
|
else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
|
||||||
if (~c->flags & CLIENT_UTF8) {
|
if (~c->flags & CLIENT_UTF8) {
|
||||||
xvasprintf(&tmp, fmt, ap);
|
tmp = msg;
|
||||||
msg = utf8_sanitize(tmp);
|
msg = utf8_sanitize(tmp);
|
||||||
free(tmp);
|
free(tmp);
|
||||||
evbuffer_add(c->stdout_data, msg, strlen(msg));
|
}
|
||||||
free(msg);
|
file_print(c, "%s\n", msg);
|
||||||
} else
|
|
||||||
evbuffer_add_vprintf(c->stdout_data, fmt, ap);
|
|
||||||
evbuffer_add(c->stdout_data, "\n", 1);
|
|
||||||
server_client_push_stdout(c);
|
|
||||||
} else {
|
} else {
|
||||||
wp = c->session->curw->window->active;
|
wp = c->session->curw->window->active;
|
||||||
wme = TAILQ_FIRST(&wp->modes);
|
wme = TAILQ_FIRST(&wp->modes);
|
||||||
if (wme == NULL || wme->mode != &window_view_mode)
|
if (wme == NULL || wme->mode != &window_view_mode)
|
||||||
window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
|
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. */
|
/* Show error from command. */
|
||||||
@ -528,11 +526,10 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
|
|||||||
struct cmd *cmd = item->cmd;
|
struct cmd *cmd = item->cmd;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char *msg;
|
char *msg;
|
||||||
size_t msglen;
|
|
||||||
char *tmp;
|
char *tmp;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
msglen = xvasprintf(&msg, fmt, ap);
|
xvasprintf(&msg, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
log_debug("%s: %s", __func__, msg);
|
log_debug("%s: %s", __func__, msg);
|
||||||
@ -544,11 +541,8 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
|
|||||||
tmp = msg;
|
tmp = msg;
|
||||||
msg = utf8_sanitize(tmp);
|
msg = utf8_sanitize(tmp);
|
||||||
free(tmp);
|
free(tmp);
|
||||||
msglen = strlen(msg);
|
|
||||||
}
|
}
|
||||||
evbuffer_add(c->stderr_data, msg, msglen);
|
file_error(c, "%s\n", msg);
|
||||||
evbuffer_add(c->stderr_data, "\n", 1);
|
|
||||||
server_client_push_stderr(c);
|
|
||||||
c->retval = 1;
|
c->retval = 1;
|
||||||
} else {
|
} else {
|
||||||
*msg = toupper((u_char) *msg);
|
*msg = toupper((u_char) *msg);
|
||||||
|
@ -55,6 +55,20 @@ const struct cmd_entry cmd_show_buffer_entry = {
|
|||||||
.exec = cmd_save_buffer_exec
|
.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
|
static enum cmd_retval
|
||||||
cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
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 winlink *wl = item->target.wl;
|
||||||
struct window_pane *wp = item->target.wp;
|
struct window_pane *wp = item->target.wp;
|
||||||
struct paste_buffer *pb;
|
struct paste_buffer *pb;
|
||||||
const char *bufname, *bufdata, *start, *end, *flags;
|
int flags;
|
||||||
char *msg, *path, *file;
|
const char *bufname = args_get(args, 'b'), *bufdata;
|
||||||
size_t size, used, msglen, bufsize;
|
size_t bufsize;
|
||||||
FILE *f;
|
char *path;
|
||||||
|
|
||||||
if (!args_has(args, 'b')) {
|
if (bufname == NULL) {
|
||||||
if ((pb = paste_get_top(NULL)) == NULL) {
|
if ((pb = paste_get_top(NULL)) == NULL) {
|
||||||
cmdq_error(item, "no buffers");
|
cmdq_error(item, "no buffers");
|
||||||
return (CMD_RETURN_ERROR);
|
return (CMD_RETURN_ERROR);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bufname = args_get(args, 'b');
|
|
||||||
pb = paste_get_name(bufname);
|
pb = paste_get_name(bufname);
|
||||||
if (pb == NULL) {
|
if (pb == NULL) {
|
||||||
cmdq_error(item, "no buffer %s", bufname);
|
cmdq_error(item, "no buffer %s", bufname);
|
||||||
@ -88,74 +101,13 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
path = xstrdup("-");
|
path = xstrdup("-");
|
||||||
else
|
else
|
||||||
path = format_single(item, args->argv[0], c, s, wl, wp);
|
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'))
|
if (args_has(self->args, 'a'))
|
||||||
flags = "ab";
|
flags = O_APPEND;
|
||||||
|
else
|
||||||
file = server_client_get_path(item->client, path);
|
flags = 0;
|
||||||
|
file_write(item->client, path, flags, bufdata, bufsize,
|
||||||
|
cmd_save_buffer_done, item);
|
||||||
free(path);
|
free(path);
|
||||||
|
|
||||||
f = fopen(file, flags);
|
return (CMD_RETURN_WAIT);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,6 @@
|
|||||||
|
|
||||||
static enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmdq_item *);
|
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 = {
|
const struct cmd_entry cmd_source_file_entry = {
|
||||||
.name = "source-file",
|
.name = "source-file",
|
||||||
.alias = "source",
|
.alias = "source",
|
||||||
@ -44,40 +42,130 @@ const struct cmd_entry cmd_source_file_entry = {
|
|||||||
.exec = cmd_source_file_exec
|
.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
|
static enum cmd_retval
|
||||||
cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
|
cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
|
||||||
{
|
{
|
||||||
struct args *args = self->args;
|
struct args *args = self->args;
|
||||||
int flags = 0;
|
struct cmd_source_file_data *cdata;
|
||||||
struct client *c = item->client;
|
int flags = 0;
|
||||||
struct cmdq_item *new_item, *after;
|
struct client *c = item->client;
|
||||||
enum cmd_retval retval;
|
enum cmd_retval retval = CMD_RETURN_NORMAL;
|
||||||
char *pattern, *cwd;
|
char *pattern, *cwd;
|
||||||
const char *path, *error;
|
const char *path, *error;
|
||||||
glob_t g;
|
glob_t g;
|
||||||
int i;
|
int i, result;
|
||||||
u_int j;
|
u_int j;
|
||||||
|
|
||||||
|
cdata = xcalloc(1, sizeof *cdata);
|
||||||
|
cdata->item = item;
|
||||||
|
|
||||||
if (args_has(args, 'q'))
|
if (args_has(args, 'q'))
|
||||||
flags |= CMD_PARSE_QUIET;
|
cdata->flags |= CMD_PARSE_QUIET;
|
||||||
if (args_has(args, 'n'))
|
if (args_has(args, 'n'))
|
||||||
flags |= CMD_PARSE_PARSEONLY;
|
cdata->flags |= CMD_PARSE_PARSEONLY;
|
||||||
if (args_has(args, 'v'))
|
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);
|
utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB);
|
||||||
|
|
||||||
retval = CMD_RETURN_NORMAL;
|
|
||||||
for (i = 0; i < args->argc; i++) {
|
for (i = 0; i < args->argc; i++) {
|
||||||
path = args->argv[i];
|
path = args->argv[i];
|
||||||
|
if (strcmp(path, "-") == 0) {
|
||||||
|
cmd_source_file_add(cdata, "-");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (*path == '/')
|
if (*path == '/')
|
||||||
pattern = xstrdup(path);
|
pattern = xstrdup(path);
|
||||||
else
|
else
|
||||||
xasprintf(&pattern, "%s/%s", cwd, path);
|
xasprintf(&pattern, "%s/%s", cwd, path);
|
||||||
log_debug("%s: %s", __func__, pattern);
|
log_debug("%s: %s", __func__, pattern);
|
||||||
|
|
||||||
if (glob(pattern, 0, NULL, &g) != 0) {
|
if ((result = glob(pattern, 0, NULL, &g)) != 0) {
|
||||||
error = strerror(errno);
|
if (result != GLOB_NOMATCH ||
|
||||||
if (errno != ENOENT || (~flags & CMD_PARSE_QUIET)) {
|
(~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);
|
cmdq_error(item, "%s: %s", path, error);
|
||||||
retval = CMD_RETURN_ERROR;
|
retval = CMD_RETURN_ERROR;
|
||||||
}
|
}
|
||||||
@ -86,30 +174,19 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
|
|||||||
}
|
}
|
||||||
free(pattern);
|
free(pattern);
|
||||||
|
|
||||||
after = item;
|
for (j = 0; j < g.gl_pathc; j++)
|
||||||
for (j = 0; j < g.gl_pathc; j++) {
|
cmd_source_file_add(cdata, g.gl_pathv[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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
free(cwd);
|
||||||
return (retval);
|
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);
|
|
||||||
}
|
|
||||||
|
5
compat.h
5
compat.h
@ -21,6 +21,7 @@
|
|||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
|
|
||||||
|
#include <fnmatch.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
@ -169,6 +170,10 @@ void warnx(const char *, ...);
|
|||||||
#define O_DIRECTORY 0
|
#define O_DIRECTORY 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef FNM_CASEFOLD
|
||||||
|
#define FNM_CASEFOLD 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef INFTIM
|
#ifndef INFTIM
|
||||||
#define INFTIM -1
|
#define INFTIM -1
|
||||||
#endif
|
#endif
|
||||||
|
@ -54,7 +54,7 @@ control_notify_input(struct client *c, struct window_pane *wp,
|
|||||||
else
|
else
|
||||||
evbuffer_add_printf(message, "%c", buf[i]);
|
evbuffer_add_printf(message, "%c", buf[i]);
|
||||||
}
|
}
|
||||||
control_write_buffer(c, message);
|
control_write(c, "%s", EVBUFFER_DATA(message));
|
||||||
evbuffer_free(message);
|
evbuffer_free(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
36
control.c
36
control.c
@ -30,23 +30,12 @@
|
|||||||
void
|
void
|
||||||
control_write(struct client *c, const char *fmt, ...)
|
control_write(struct client *c, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
evbuffer_add_vprintf(c->stdout_data, fmt, ap);
|
file_vprint(c, fmt, ap);
|
||||||
|
file_print(c, "\n");
|
||||||
va_end(ap);
|
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. */
|
/* Control error callback. */
|
||||||
@ -65,20 +54,22 @@ control_error(struct cmdq_item *item, void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Control input callback. Read lines and fire commands. */
|
/* Control input callback. Read lines and fire commands. */
|
||||||
void
|
static void
|
||||||
control_callback(struct client *c, int closed, __unused void *data)
|
control_callback(__unused struct client *c, __unused const char *path,
|
||||||
|
int error, int closed, struct evbuffer *buffer, __unused void *data)
|
||||||
{
|
{
|
||||||
char *line;
|
char *line;
|
||||||
struct cmdq_item *item;
|
struct cmdq_item *item;
|
||||||
struct cmd_parse_result *pr;
|
struct cmd_parse_result *pr;
|
||||||
|
|
||||||
if (closed)
|
if (closed || error != 0)
|
||||||
c->flags |= CLIENT_EXIT;
|
c->flags |= CLIENT_EXIT;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
line = evbuffer_readln(c->stdin_data, NULL, EVBUFFER_EOL_LF);
|
line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
|
||||||
if (line == NULL)
|
if (line == NULL)
|
||||||
break;
|
break;
|
||||||
|
log_debug("%s: %s", __func__, line);
|
||||||
if (*line == '\0') { /* empty line exit */
|
if (*line == '\0') { /* empty line exit */
|
||||||
free(line);
|
free(line);
|
||||||
c->flags |= CLIENT_EXIT;
|
c->flags |= CLIENT_EXIT;
|
||||||
@ -104,3 +95,12 @@ control_callback(struct client *c, int closed, __unused void *data)
|
|||||||
free(line);
|
free(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
control_start(struct client *c)
|
||||||
|
{
|
||||||
|
file_read(c, "-", control_callback, c);
|
||||||
|
|
||||||
|
if (c->flags & CLIENT_CONTROLCONTROL)
|
||||||
|
file_print(c, "\033P1000p");
|
||||||
|
}
|
||||||
|
414
file.c
Normal file
414
file.c
Normal file
@ -0,0 +1,414 @@
|
|||||||
|
/* $OpenBSD$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "tmux.h"
|
||||||
|
|
||||||
|
static int file_next_stream = 3;
|
||||||
|
|
||||||
|
RB_GENERATE(client_files, client_file, entry, file_cmp);
|
||||||
|
|
||||||
|
static char *
|
||||||
|
file_get_path(struct client *c, const char *file)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
if (*file == '/')
|
||||||
|
path = xstrdup(file);
|
||||||
|
else
|
||||||
|
xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file);
|
||||||
|
return (path);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
file_cmp(struct client_file *cf1, struct client_file *cf2)
|
||||||
|
{
|
||||||
|
if (cf1->stream < cf2->stream)
|
||||||
|
return (-1);
|
||||||
|
if (cf1->stream > cf2->stream)
|
||||||
|
return (1);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct client_file *
|
||||||
|
file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
|
||||||
|
{
|
||||||
|
struct client_file *cf;
|
||||||
|
|
||||||
|
cf = xcalloc(1, sizeof *cf);
|
||||||
|
cf->c = c;
|
||||||
|
cf->references = 1;
|
||||||
|
cf->stream = stream;
|
||||||
|
|
||||||
|
cf->buffer = evbuffer_new();
|
||||||
|
if (cf->buffer == NULL)
|
||||||
|
fatalx("out of memory");
|
||||||
|
|
||||||
|
cf->cb = cb;
|
||||||
|
cf->data = cbdata;
|
||||||
|
|
||||||
|
if (cf->c != NULL) {
|
||||||
|
RB_INSERT(client_files, &cf->c->files, cf);
|
||||||
|
cf->c->references++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (cf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
file_free(struct client_file *cf)
|
||||||
|
{
|
||||||
|
if (--cf->references != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
evbuffer_free(cf->buffer);
|
||||||
|
free(cf->path);
|
||||||
|
|
||||||
|
if (cf->c != NULL) {
|
||||||
|
RB_REMOVE(client_files, &cf->c->files, cf);
|
||||||
|
server_client_unref(cf->c);
|
||||||
|
}
|
||||||
|
free(cf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
file_fire_done_cb(__unused int fd, __unused short events, void *arg)
|
||||||
|
{
|
||||||
|
struct client_file *cf = arg;
|
||||||
|
struct client *c = cf->c;
|
||||||
|
|
||||||
|
if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD)))
|
||||||
|
cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
|
||||||
|
file_free(cf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
file_fire_done(struct client_file *cf)
|
||||||
|
{
|
||||||
|
event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
file_fire_read(struct client_file *cf)
|
||||||
|
{
|
||||||
|
struct client *c = cf->c;
|
||||||
|
|
||||||
|
if (cf->cb != NULL)
|
||||||
|
cf->cb(c, cf->path, cf->error, 0, cf->buffer, cf->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
file_can_print(struct client *c)
|
||||||
|
{
|
||||||
|
if (c == NULL)
|
||||||
|
return (0);
|
||||||
|
if (c->session != NULL && (~c->flags & CLIENT_CONTROL))
|
||||||
|
return (0);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
file_print(struct client *c, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
file_vprint(c, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
file_vprint(struct client *c, const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
struct client_file find, *cf;
|
||||||
|
struct msg_write_open msg;
|
||||||
|
|
||||||
|
if (!file_can_print(c))
|
||||||
|
return;
|
||||||
|
|
||||||
|
find.stream = 1;
|
||||||
|
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
|
||||||
|
cf = file_create(c, 1, NULL, NULL);
|
||||||
|
cf->path = xstrdup("-");
|
||||||
|
|
||||||
|
evbuffer_add_vprintf(cf->buffer, fmt, ap);
|
||||||
|
|
||||||
|
msg.stream = 1;
|
||||||
|
msg.fd = STDOUT_FILENO;
|
||||||
|
msg.flags = 0;
|
||||||
|
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
|
||||||
|
} else {
|
||||||
|
evbuffer_add_vprintf(cf->buffer, fmt, ap);
|
||||||
|
file_push(cf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
file_print_buffer(struct client *c, void *data, size_t size)
|
||||||
|
{
|
||||||
|
struct client_file find, *cf;
|
||||||
|
struct msg_write_open msg;
|
||||||
|
|
||||||
|
if (!file_can_print(c))
|
||||||
|
return;
|
||||||
|
|
||||||
|
find.stream = 1;
|
||||||
|
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
|
||||||
|
cf = file_create(c, 1, NULL, NULL);
|
||||||
|
cf->path = xstrdup("-");
|
||||||
|
|
||||||
|
evbuffer_add(cf->buffer, data, size);
|
||||||
|
|
||||||
|
msg.stream = 1;
|
||||||
|
msg.fd = STDOUT_FILENO;
|
||||||
|
msg.flags = 0;
|
||||||
|
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
|
||||||
|
} else {
|
||||||
|
evbuffer_add(cf->buffer, data, size);
|
||||||
|
file_push(cf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
file_error(struct client *c, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
struct client_file find, *cf;
|
||||||
|
struct msg_write_open msg;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (!file_can_print(c))
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
|
||||||
|
find.stream = 2;
|
||||||
|
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
|
||||||
|
cf = file_create(c, 2, NULL, NULL);
|
||||||
|
cf->path = xstrdup("-");
|
||||||
|
|
||||||
|
evbuffer_add_vprintf(cf->buffer, fmt, ap);
|
||||||
|
|
||||||
|
msg.stream = 2;
|
||||||
|
msg.fd = STDERR_FILENO;
|
||||||
|
msg.flags = 0;
|
||||||
|
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
|
||||||
|
} else {
|
||||||
|
evbuffer_add_vprintf(cf->buffer, fmt, ap);
|
||||||
|
file_push(cf);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
file_write(struct client *c, const char *path, int flags, const void *bdata,
|
||||||
|
size_t bsize, client_file_cb cb, void *cbdata)
|
||||||
|
{
|
||||||
|
struct client_file *cf;
|
||||||
|
FILE *f;
|
||||||
|
struct msg_write_open *msg;
|
||||||
|
size_t msglen;
|
||||||
|
int fd = -1;
|
||||||
|
const char *mode;
|
||||||
|
|
||||||
|
if (strcmp(path, "-") == 0) {
|
||||||
|
cf = file_create(c, file_next_stream++, cb, cbdata);
|
||||||
|
cf->path = xstrdup("-");
|
||||||
|
|
||||||
|
fd = STDOUT_FILENO;
|
||||||
|
if (c == NULL || c->flags & CLIENT_ATTACHED) {
|
||||||
|
cf->error = EBADF;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
cf = file_create(c, file_next_stream++, cb, cbdata);
|
||||||
|
cf->path = file_get_path(c, path);
|
||||||
|
|
||||||
|
if (c == NULL || c->flags & CLIENT_ATTACHED) {
|
||||||
|
if (flags & O_APPEND)
|
||||||
|
mode = "ab";
|
||||||
|
else
|
||||||
|
mode = "wb";
|
||||||
|
f = fopen(cf->path, mode);
|
||||||
|
if (f == NULL) {
|
||||||
|
cf->error = errno;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (fwrite(bdata, 1, bsize, f) != bsize) {
|
||||||
|
fclose(f);
|
||||||
|
cf->error = EIO;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip:
|
||||||
|
evbuffer_add(cf->buffer, bdata, bsize);
|
||||||
|
|
||||||
|
msglen = strlen(cf->path) + 1 + sizeof *msg;
|
||||||
|
if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
|
||||||
|
cf->error = E2BIG;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
msg = xmalloc(msglen);
|
||||||
|
msg->stream = cf->stream;
|
||||||
|
msg->fd = fd;
|
||||||
|
msg->flags = flags;
|
||||||
|
memcpy(msg + 1, cf->path, msglen - sizeof *msg);
|
||||||
|
if (proc_send(c->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
|
||||||
|
free(msg);
|
||||||
|
cf->error = EINVAL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
free(msg);
|
||||||
|
return;
|
||||||
|
|
||||||
|
done:
|
||||||
|
file_fire_done(cf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
|
||||||
|
{
|
||||||
|
struct client_file *cf;
|
||||||
|
FILE *f;
|
||||||
|
struct msg_read_open *msg;
|
||||||
|
size_t msglen, size;
|
||||||
|
int fd = -1;
|
||||||
|
char buffer[BUFSIZ];
|
||||||
|
|
||||||
|
if (strcmp(path, "-") == 0) {
|
||||||
|
cf = file_create(c, file_next_stream++, cb, cbdata);
|
||||||
|
cf->path = xstrdup("-");
|
||||||
|
|
||||||
|
fd = STDIN_FILENO;
|
||||||
|
if (c == NULL || c->flags & CLIENT_ATTACHED) {
|
||||||
|
cf->error = EBADF;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
cf = file_create(c, file_next_stream++, cb, cbdata);
|
||||||
|
cf->path = file_get_path(c, path);
|
||||||
|
|
||||||
|
if (c == NULL || c->flags & CLIENT_ATTACHED) {
|
||||||
|
f = fopen(cf->path, "rb");
|
||||||
|
if (f == NULL) {
|
||||||
|
cf->error = errno;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
size = fread(buffer, 1, sizeof buffer, f);
|
||||||
|
if (evbuffer_add(cf->buffer, buffer, size) != 0) {
|
||||||
|
cf->error = ENOMEM;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (size != sizeof buffer)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ferror(f)) {
|
||||||
|
cf->error = EIO;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip:
|
||||||
|
msglen = strlen(cf->path) + 1 + sizeof *msg;
|
||||||
|
if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
|
||||||
|
cf->error = E2BIG;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
msg = xmalloc(msglen);
|
||||||
|
msg->stream = cf->stream;
|
||||||
|
msg->fd = fd;
|
||||||
|
memcpy(msg + 1, cf->path, msglen - sizeof *msg);
|
||||||
|
if (proc_send(c->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
|
||||||
|
free(msg);
|
||||||
|
cf->error = EINVAL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
free(msg);
|
||||||
|
return;
|
||||||
|
|
||||||
|
done:
|
||||||
|
file_fire_done(cf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
file_push_cb(__unused int fd, __unused short events, void *arg)
|
||||||
|
{
|
||||||
|
struct client_file *cf = arg;
|
||||||
|
struct client *c = cf->c;
|
||||||
|
|
||||||
|
if (~c->flags & CLIENT_DEAD)
|
||||||
|
file_push(cf);
|
||||||
|
file_free(cf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
file_push(struct client_file *cf)
|
||||||
|
{
|
||||||
|
struct client *c = cf->c;
|
||||||
|
struct msg_write_data *msg;
|
||||||
|
size_t msglen, sent, left;
|
||||||
|
struct msg_write_close close;
|
||||||
|
|
||||||
|
msg = xmalloc(sizeof *msg);
|
||||||
|
left = EVBUFFER_LENGTH(cf->buffer);
|
||||||
|
while (left != 0) {
|
||||||
|
sent = left;
|
||||||
|
if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
|
||||||
|
sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
|
||||||
|
|
||||||
|
msglen = (sizeof *msg) + sent;
|
||||||
|
msg = xrealloc(msg, msglen);
|
||||||
|
msg->stream = cf->stream;
|
||||||
|
memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
|
||||||
|
if (proc_send(c->peer, MSG_WRITE, -1, msg, msglen) != 0)
|
||||||
|
break;
|
||||||
|
evbuffer_drain(cf->buffer, sent);
|
||||||
|
|
||||||
|
left = EVBUFFER_LENGTH(cf->buffer);
|
||||||
|
log_debug("%s: file %d sent %zu, left %zu", c->name, cf->stream,
|
||||||
|
sent, left);
|
||||||
|
}
|
||||||
|
if (left != 0) {
|
||||||
|
cf->references++;
|
||||||
|
event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
|
||||||
|
} else if (cf->stream > 2) {
|
||||||
|
close.stream = cf->stream;
|
||||||
|
proc_send(c->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
|
||||||
|
file_fire_done(cf);
|
||||||
|
}
|
||||||
|
free(msg);
|
||||||
|
}
|
@ -598,6 +598,8 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
|||||||
xasprintf(&text, "%-*s%s%s%s: ", keylen, key, start, mti->name,
|
xasprintf(&text, "%-*s%s%s%s: ", keylen, key, start, mti->name,
|
||||||
tag);
|
tag);
|
||||||
width = utf8_cstrwidth(text);
|
width = utf8_cstrwidth(text);
|
||||||
|
if (width > w)
|
||||||
|
width = w;
|
||||||
free(start);
|
free(start);
|
||||||
|
|
||||||
if (mti->tagged) {
|
if (mti->tagged) {
|
||||||
@ -607,11 +609,11 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
|||||||
|
|
||||||
if (i != mtd->current) {
|
if (i != mtd->current) {
|
||||||
screen_write_clearendofline(&ctx, 8);
|
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);
|
format_draw(&ctx, &gc0, w - width, mti->text, NULL);
|
||||||
} else {
|
} else {
|
||||||
screen_write_clearendofline(&ctx, gc.bg);
|
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);
|
format_draw(&ctx, &gc, w - width, mti->text, NULL);
|
||||||
}
|
}
|
||||||
free(text);
|
free(text);
|
||||||
|
239
server-client.c
239
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_command(struct client *, struct imsg *);
|
||||||
static void server_client_dispatch_identify(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_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. */
|
/* Number of attached clients. */
|
||||||
u_int
|
u_int
|
||||||
@ -195,16 +201,6 @@ server_client_create(int fd)
|
|||||||
|
|
||||||
TAILQ_INIT(&c->queue);
|
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->tty.fd = -1;
|
||||||
c->title = NULL;
|
c->title = NULL;
|
||||||
|
|
||||||
@ -223,6 +219,8 @@ server_client_create(int fd)
|
|||||||
c->prompt_buffer = NULL;
|
c->prompt_buffer = NULL;
|
||||||
c->prompt_index = 0;
|
c->prompt_index = 0;
|
||||||
|
|
||||||
|
RB_INIT(&c->files);
|
||||||
|
|
||||||
c->flags |= CLIENT_FOCUSED;
|
c->flags |= CLIENT_FOCUSED;
|
||||||
|
|
||||||
c->keytable = key_bindings_get_table("root", 1);
|
c->keytable = key_bindings_get_table("root", 1);
|
||||||
@ -264,6 +262,7 @@ void
|
|||||||
server_client_lost(struct client *c)
|
server_client_lost(struct client *c)
|
||||||
{
|
{
|
||||||
struct message_entry *msg, *msg1;
|
struct message_entry *msg, *msg1;
|
||||||
|
struct client_file *cf;
|
||||||
|
|
||||||
c->flags |= CLIENT_DEAD;
|
c->flags |= CLIENT_DEAD;
|
||||||
|
|
||||||
@ -271,8 +270,10 @@ server_client_lost(struct client *c)
|
|||||||
status_prompt_clear(c);
|
status_prompt_clear(c);
|
||||||
status_message_clear(c);
|
status_message_clear(c);
|
||||||
|
|
||||||
if (c->stdin_callback != NULL)
|
RB_FOREACH(cf, client_files, &c->files) {
|
||||||
c->stdin_callback(c, 1, c->stdin_callback_data);
|
cf->error = EINTR;
|
||||||
|
file_fire_done(cf);
|
||||||
|
}
|
||||||
|
|
||||||
TAILQ_REMOVE(&clients, c, entry);
|
TAILQ_REMOVE(&clients, c, entry);
|
||||||
log_debug("lost client %p", c);
|
log_debug("lost client %p", c);
|
||||||
@ -286,11 +287,6 @@ server_client_lost(struct client *c)
|
|||||||
free(c->ttyname);
|
free(c->ttyname);
|
||||||
free(c->term);
|
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);
|
status_free(c);
|
||||||
|
|
||||||
free(c->title);
|
free(c->title);
|
||||||
@ -1558,17 +1554,17 @@ server_client_click_timer(__unused int fd, __unused short events, void *data)
|
|||||||
static void
|
static void
|
||||||
server_client_check_exit(struct client *c)
|
server_client_check_exit(struct client *c)
|
||||||
{
|
{
|
||||||
|
struct client_file *cf;
|
||||||
|
|
||||||
if (~c->flags & CLIENT_EXIT)
|
if (~c->flags & CLIENT_EXIT)
|
||||||
return;
|
return;
|
||||||
if (c->flags & CLIENT_EXITED)
|
if (c->flags & CLIENT_EXITED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (EVBUFFER_LENGTH(c->stdin_data) != 0)
|
RB_FOREACH(cf, client_files, &c->files) {
|
||||||
return;
|
if (EVBUFFER_LENGTH(cf->buffer) != 0)
|
||||||
if (EVBUFFER_LENGTH(c->stdout_data) != 0)
|
return;
|
||||||
return;
|
}
|
||||||
if (EVBUFFER_LENGTH(c->stderr_data) != 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (c->flags & CLIENT_ATTACHED)
|
if (c->flags & CLIENT_ATTACHED)
|
||||||
notify_client("client-detached", c);
|
notify_client("client-detached", c);
|
||||||
@ -1707,11 +1703,10 @@ server_client_set_title(struct client *c)
|
|||||||
static void
|
static void
|
||||||
server_client_dispatch(struct imsg *imsg, void *arg)
|
server_client_dispatch(struct imsg *imsg, void *arg)
|
||||||
{
|
{
|
||||||
struct client *c = arg;
|
struct client *c = arg;
|
||||||
struct msg_stdin_data stdindata;
|
const char *data;
|
||||||
const char *data;
|
ssize_t datalen;
|
||||||
ssize_t datalen;
|
struct session *s;
|
||||||
struct session *s;
|
|
||||||
|
|
||||||
if (c->flags & CLIENT_DEAD)
|
if (c->flags & CLIENT_DEAD)
|
||||||
return;
|
return;
|
||||||
@ -1738,21 +1733,6 @@ server_client_dispatch(struct imsg *imsg, void *arg)
|
|||||||
case MSG_COMMAND:
|
case MSG_COMMAND:
|
||||||
server_client_dispatch_command(c, imsg);
|
server_client_dispatch_command(c, imsg);
|
||||||
break;
|
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:
|
case MSG_RESIZE:
|
||||||
if (datalen != 0)
|
if (datalen != 0)
|
||||||
fatalx("bad MSG_RESIZE size");
|
fatalx("bad MSG_RESIZE size");
|
||||||
@ -1804,6 +1784,15 @@ server_client_dispatch(struct imsg *imsg, void *arg)
|
|||||||
|
|
||||||
server_client_dispatch_shell(c);
|
server_client_dispatch_shell(c);
|
||||||
break;
|
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
|
static void
|
||||||
server_client_dispatch_command(struct client *c, struct imsg *imsg)
|
server_client_dispatch_command(struct client *c, struct imsg *imsg)
|
||||||
{
|
{
|
||||||
struct msg_command_data data;
|
struct msg_command data;
|
||||||
char *buf;
|
char *buf;
|
||||||
size_t len;
|
size_t len;
|
||||||
int argc;
|
int argc;
|
||||||
@ -1966,19 +1955,11 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (c->flags & CLIENT_CONTROL) {
|
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);
|
close(c->fd);
|
||||||
c->fd = -1;
|
c->fd = -1;
|
||||||
|
|
||||||
|
control_start(c);
|
||||||
|
c->tty.fd = -1;
|
||||||
} else if (c->fd != -1) {
|
} else if (c->fd != -1) {
|
||||||
if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
|
if (tty_init(&c->tty, c, c->fd, c->term) != 0) {
|
||||||
close(c->fd);
|
close(c->fd);
|
||||||
@ -2018,93 +1999,71 @@ server_client_dispatch_shell(struct client *c)
|
|||||||
proc_kill_peer(c->peer);
|
proc_kill_peer(c->peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Event callback to push more stdout data if any left. */
|
/* Handle write ready message. */
|
||||||
static void
|
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)
|
if (msglen != sizeof *msg)
|
||||||
server_client_push_stdout(c);
|
fatalx("bad MSG_WRITE_READY size");
|
||||||
server_client_unref(c);
|
find.stream = msg->stream;
|
||||||
}
|
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
|
||||||
|
|
||||||
/* 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);
|
|
||||||
return;
|
return;
|
||||||
}
|
if (msg->error != 0) {
|
||||||
|
cf->error = msg->error;
|
||||||
|
file_fire_done(cf);
|
||||||
|
} else
|
||||||
|
file_push(cf);
|
||||||
|
}
|
||||||
|
|
||||||
left = EVBUFFER_LENGTH(c->stderr_data);
|
/* Handle read data message. */
|
||||||
while (left != 0) {
|
static void
|
||||||
sent = left;
|
server_client_dispatch_read_data(struct client *c, struct imsg *imsg)
|
||||||
if (sent > sizeof data.data)
|
{
|
||||||
sent = sizeof data.data;
|
struct msg_read_data *msg = imsg->data;
|
||||||
memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent);
|
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||||
data.size = sent;
|
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)
|
if (msglen < sizeof *msg)
|
||||||
break;
|
fatalx("bad MSG_READ_DATA size");
|
||||||
evbuffer_drain(c->stderr_data, sent);
|
find.stream = msg->stream;
|
||||||
|
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
left = EVBUFFER_LENGTH(c->stderr_data);
|
log_debug("%s: file %d read %zu bytes", c->name, cf->stream, bsize);
|
||||||
log_debug("%s: client %p, sent %zu, left %zu", __func__, c,
|
if (cf->error == 0) {
|
||||||
sent, left);
|
if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
|
||||||
}
|
cf->error = ENOMEM;
|
||||||
if (left != 0) {
|
file_fire_done(cf);
|
||||||
c->references++;
|
} else
|
||||||
event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL);
|
file_fire_read(cf);
|
||||||
log_debug("%s: client %p, queued", __func__, c);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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. */
|
/* Add to client message log. */
|
||||||
void
|
void
|
||||||
server_client_add_message(struct client *c, const char *fmt, ...)
|
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 (home);
|
||||||
return ("/");
|
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));
|
|
||||||
}
|
|
||||||
|
30
server-fn.c
30
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
|
void
|
||||||
server_unzoom_window(struct window *w)
|
server_unzoom_window(struct window *w)
|
||||||
{
|
{
|
||||||
|
9
spawn.c
9
spawn.c
@ -433,6 +433,15 @@ spawn_pane(struct spawn_context *sc, char **cause)
|
|||||||
_exit(1);
|
_exit(1);
|
||||||
|
|
||||||
complete:
|
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->pipe_off = 0;
|
||||||
new_wp->flags &= ~PANE_EXITED;
|
new_wp->flags &= ~PANE_EXITED;
|
||||||
|
|
||||||
|
115
tmux.h
115
tmux.h
@ -62,9 +62,9 @@ struct winlink;
|
|||||||
/* Client-server protocol version. */
|
/* Client-server protocol version. */
|
||||||
#define PROTOCOL_VERSION 8
|
#define PROTOCOL_VERSION 8
|
||||||
|
|
||||||
/* Default global configuration file. */
|
/* Default configuration files. */
|
||||||
#ifndef TMUX_CONF
|
#ifndef TMUX_CONF
|
||||||
#define TMUX_CONF "/etc/tmux.conf"
|
#define TMUX_CONF "/etc/tmux.conf:~/.tmux.conf"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Minimum layout cell size, NOT including border lines. */
|
/* Minimum layout cell size, NOT including border lines. */
|
||||||
@ -481,13 +481,21 @@ enum msgtype {
|
|||||||
MSG_RESIZE,
|
MSG_RESIZE,
|
||||||
MSG_SHELL,
|
MSG_SHELL,
|
||||||
MSG_SHUTDOWN,
|
MSG_SHUTDOWN,
|
||||||
MSG_STDERR,
|
MSG_OLDSTDERR, /* unused */
|
||||||
MSG_STDIN,
|
MSG_OLDSTDIN, /* unused */
|
||||||
MSG_STDOUT,
|
MSG_OLDSTDOUT, /* unused */
|
||||||
MSG_SUSPEND,
|
MSG_SUSPEND,
|
||||||
MSG_UNLOCK,
|
MSG_UNLOCK,
|
||||||
MSG_WAKEUP,
|
MSG_WAKEUP,
|
||||||
MSG_EXEC,
|
MSG_EXEC,
|
||||||
|
|
||||||
|
MSG_READ_OPEN = 300,
|
||||||
|
MSG_READ,
|
||||||
|
MSG_READ_DONE,
|
||||||
|
MSG_WRITE_OPEN,
|
||||||
|
MSG_WRITE,
|
||||||
|
MSG_WRITE_READY,
|
||||||
|
MSG_WRITE_CLOSE
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -495,23 +503,41 @@ enum msgtype {
|
|||||||
*
|
*
|
||||||
* Don't forget to bump PROTOCOL_VERSION if any of these change!
|
* Don't forget to bump PROTOCOL_VERSION if any of these change!
|
||||||
*/
|
*/
|
||||||
struct msg_command_data {
|
struct msg_command {
|
||||||
int argc;
|
int argc;
|
||||||
}; /* followed by packed argv */
|
}; /* followed by packed argv */
|
||||||
|
|
||||||
struct msg_stdin_data {
|
struct msg_read_open {
|
||||||
ssize_t size;
|
int stream;
|
||||||
char data[BUFSIZ];
|
int fd;
|
||||||
|
}; /* followed by path */
|
||||||
|
|
||||||
|
struct msg_read_data {
|
||||||
|
int stream;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct msg_stdout_data {
|
struct msg_read_done {
|
||||||
ssize_t size;
|
int stream;
|
||||||
char data[BUFSIZ];
|
int error;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct msg_stderr_data {
|
struct msg_write_open {
|
||||||
ssize_t size;
|
int stream;
|
||||||
char data[BUFSIZ];
|
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. */
|
/* Mode keys. */
|
||||||
@ -1480,6 +1506,29 @@ struct status_line {
|
|||||||
struct status_line_entry entries[STATUS_LINES_LIMIT];
|
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. */
|
/* Client connection. */
|
||||||
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
|
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
|
||||||
typedef void (*prompt_free_cb)(void *);
|
typedef void (*prompt_free_cb)(void *);
|
||||||
@ -1513,13 +1562,6 @@ struct client {
|
|||||||
size_t discarded;
|
size_t discarded;
|
||||||
size_t redraw;
|
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 repeat_timer;
|
||||||
|
|
||||||
struct event click_timer;
|
struct event click_timer;
|
||||||
@ -1603,6 +1645,8 @@ struct client {
|
|||||||
void *overlay_data;
|
void *overlay_data;
|
||||||
struct event overlay_timer;
|
struct event overlay_timer;
|
||||||
|
|
||||||
|
struct client_files files;
|
||||||
|
|
||||||
TAILQ_ENTRY(client) entry;
|
TAILQ_ENTRY(client) entry;
|
||||||
};
|
};
|
||||||
TAILQ_HEAD(clients, client);
|
TAILQ_HEAD(clients, client);
|
||||||
@ -1759,6 +1803,8 @@ extern struct client *cfg_client;
|
|||||||
void start_cfg(void);
|
void start_cfg(void);
|
||||||
int load_cfg(const char *, struct client *, struct cmdq_item *, int,
|
int load_cfg(const char *, struct client *, struct cmdq_item *, int,
|
||||||
struct cmdq_item **);
|
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 set_cfg_file(const char *);
|
||||||
void printflike(1, 2) cfg_add_cause(const char *, ...);
|
void printflike(1, 2) cfg_add_cause(const char *, ...);
|
||||||
void cfg_print_causes(struct cmdq_item *);
|
void cfg_print_causes(struct cmdq_item *);
|
||||||
@ -2077,6 +2123,8 @@ void cmd_parse_empty(struct cmd_parse_input *);
|
|||||||
struct cmd_parse_result *cmd_parse_from_file(FILE *, struct cmd_parse_input *);
|
struct cmd_parse_result *cmd_parse_from_file(FILE *, struct cmd_parse_input *);
|
||||||
struct cmd_parse_result *cmd_parse_from_string(const char *,
|
struct cmd_parse_result *cmd_parse_from_string(const char *,
|
||||||
struct cmd_parse_input *);
|
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_result *cmd_parse_from_arguments(int, char **,
|
||||||
struct cmd_parse_input *);
|
struct cmd_parse_input *);
|
||||||
|
|
||||||
@ -2136,6 +2184,23 @@ void alerts_reset_all(void);
|
|||||||
void alerts_queue(struct window *, int);
|
void alerts_queue(struct window *, int);
|
||||||
void alerts_check_session(struct session *);
|
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 */
|
/* server.c */
|
||||||
extern struct tmuxproc *server_proc;
|
extern struct tmuxproc *server_proc;
|
||||||
extern struct clients clients;
|
extern struct clients clients;
|
||||||
@ -2170,7 +2235,6 @@ void server_client_push_stdout(struct client *);
|
|||||||
void server_client_push_stderr(struct client *);
|
void server_client_push_stderr(struct client *);
|
||||||
void printflike(2, 3) server_client_add_message(struct client *, const char *,
|
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 *);
|
const char *server_client_get_cwd(struct client *, struct session *);
|
||||||
|
|
||||||
/* server-fn.c */
|
/* server-fn.c */
|
||||||
@ -2194,8 +2258,6 @@ void server_unlink_window(struct session *, struct winlink *);
|
|||||||
void server_destroy_pane(struct window_pane *, int);
|
void server_destroy_pane(struct window_pane *, int);
|
||||||
void server_destroy_session(struct session *);
|
void server_destroy_session(struct session *);
|
||||||
void server_check_unattached(void);
|
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 *);
|
void server_unzoom_window(struct window *);
|
||||||
|
|
||||||
/* status.c */
|
/* status.c */
|
||||||
@ -2581,9 +2643,8 @@ char *default_window_name(struct window *);
|
|||||||
char *parse_window_name(const char *);
|
char *parse_window_name(const char *);
|
||||||
|
|
||||||
/* control.c */
|
/* 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 printflike(2, 3) control_write(struct client *, const char *, ...);
|
||||||
void control_write_buffer(struct client *, struct evbuffer *);
|
|
||||||
|
|
||||||
/* control-notify.c */
|
/* control-notify.c */
|
||||||
void control_notify_input(struct client *, struct window_pane *,
|
void control_notify_input(struct client *, struct window_pane *,
|
||||||
|
6
tty.c
6
tty.c
@ -340,7 +340,8 @@ tty_start_tty(struct tty *tty)
|
|||||||
tty->flags |= TTY_STARTED;
|
tty->flags |= TTY_STARTED;
|
||||||
tty_invalidate(tty);
|
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_flag = 0;
|
||||||
tty->mouse_drag_update = NULL;
|
tty->mouse_drag_update = NULL;
|
||||||
@ -386,7 +387,8 @@ tty_stop_tty(struct tty *tty)
|
|||||||
}
|
}
|
||||||
if (tty->mode & MODE_BRACKETPASTE)
|
if (tty->mode & MODE_BRACKETPASTE)
|
||||||
tty_raw(tty, "\033[?2004l");
|
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));
|
tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
|
||||||
if (tty_term_has(tty->term, TTYC_KMOUS))
|
if (tty_term_has(tty->term, TTYC_KMOUS))
|
||||||
|
@ -207,9 +207,9 @@ window_buffer_draw(__unused void *modedata, void *itemdata,
|
|||||||
{
|
{
|
||||||
struct window_buffer_itemdata *item = itemdata;
|
struct window_buffer_itemdata *item = itemdata;
|
||||||
struct paste_buffer *pb;
|
struct paste_buffer *pb;
|
||||||
char line[1024];
|
const char *pdata, *start, *end;
|
||||||
const char *pdata, *end, *cp;
|
char *buf = NULL;
|
||||||
size_t psize, at;
|
size_t psize, len;
|
||||||
u_int i, cx = ctx->s->cx, cy = ctx->s->cy;
|
u_int i, cx = ctx->s->cx, cy = ctx->s->cy;
|
||||||
|
|
||||||
pb = paste_get_name(item->name);
|
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);
|
pdata = end = paste_buffer_data(pb, &psize);
|
||||||
for (i = 0; i < sy; i++) {
|
for (i = 0; i < sy; i++) {
|
||||||
at = 0;
|
start = end;
|
||||||
while (end != pdata + psize && *end != '\n') {
|
while (end != pdata + psize && *end != '\n')
|
||||||
if ((sizeof line) - at > 5) {
|
|
||||||
cp = vis(line + at, *end, VIS_OCTAL|VIS_TAB, 0);
|
|
||||||
at = cp - line;
|
|
||||||
}
|
|
||||||
end++;
|
end++;
|
||||||
}
|
buf = xreallocarray(buf, 4, end - start + 1);
|
||||||
if (at > sx)
|
len = utf8_strvis(buf, start, end - start, VIS_OCTAL|VIS_TAB);
|
||||||
at = sx;
|
if (*buf != '\0') {
|
||||||
line[at] = '\0';
|
|
||||||
|
|
||||||
if (*line != '\0') {
|
|
||||||
screen_write_cursormove(ctx, cx, cy + i, 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)
|
if (end == pdata + psize)
|
||||||
break;
|
break;
|
||||||
end++;
|
end++;
|
||||||
}
|
}
|
||||||
|
free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -2082,16 +2082,28 @@ static int
|
|||||||
window_copy_search_lr(struct grid *gd,
|
window_copy_search_lr(struct grid *gd,
|
||||||
struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
|
struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
|
||||||
{
|
{
|
||||||
u_int ax, bx, px;
|
u_int ax, bx, px, pywrap, endline;
|
||||||
int matched;
|
int matched;
|
||||||
|
struct grid_line *gl;
|
||||||
|
|
||||||
|
endline = gd->hsize + gd->sy - 1;
|
||||||
for (ax = first; ax < last; ax++) {
|
for (ax = first; ax < last; ax++) {
|
||||||
if (ax + sgd->sx > gd->sx)
|
|
||||||
break;
|
|
||||||
for (bx = 0; bx < sgd->sx; bx++) {
|
for (bx = 0; bx < sgd->sx; bx++) {
|
||||||
px = ax + bx;
|
px = ax + bx;
|
||||||
matched = window_copy_search_compare(gd, px, py, sgd,
|
pywrap = py;
|
||||||
bx, cis);
|
/* 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)
|
if (!matched)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2107,16 +2119,28 @@ static int
|
|||||||
window_copy_search_rl(struct grid *gd,
|
window_copy_search_rl(struct grid *gd,
|
||||||
struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
|
struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
|
||||||
{
|
{
|
||||||
u_int ax, bx, px;
|
u_int ax, bx, px, pywrap, endline;
|
||||||
int matched;
|
int matched;
|
||||||
|
struct grid_line *gl;
|
||||||
|
|
||||||
for (ax = last + 1; ax > first; ax--) {
|
endline = gd->hsize + gd->sy - 1;
|
||||||
if (gd->sx - (ax - 1) < sgd->sx)
|
for (ax = last; ax > first; ax--) {
|
||||||
continue;
|
|
||||||
for (bx = 0; bx < sgd->sx; bx++) {
|
for (bx = 0; bx < sgd->sx; bx++) {
|
||||||
px = ax - 1 + bx;
|
px = ax - 1 + bx;
|
||||||
matched = window_copy_search_compare(gd, px, py, sgd,
|
pywrap = py;
|
||||||
bx, cis);
|
/* 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)
|
if (!matched)
|
||||||
break;
|
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 (*fy == 0) { /* top */
|
||||||
if (wrapflag) {
|
if (wrapflag) {
|
||||||
*fx = screen_size_x(s) - 1;
|
*fx = screen_size_x(s) - 1;
|
||||||
*fy = screen_hsize(s) + screen_size_y(s);
|
*fy = screen_hsize(s) + screen_size_y(s) - 1;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2149,7 +2173,7 @@ static void
|
|||||||
window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
|
window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
|
||||||
{
|
{
|
||||||
if (*fx == screen_size_x(s) - 1) { /* right */
|
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) {
|
if (wrapflag) {
|
||||||
*fx = 0;
|
*fx = 0;
|
||||||
*fy = 0;
|
*fy = 0;
|
||||||
@ -2199,12 +2223,12 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
|
|||||||
} else {
|
} else {
|
||||||
for (i = fy + 1; endline < i; i--) {
|
for (i = fy + 1; endline < i; i--) {
|
||||||
found = window_copy_search_rl(gd, sgd, &px, i - 1, 0,
|
found = window_copy_search_rl(gd, sgd, &px, i - 1, 0,
|
||||||
fx, cis);
|
fx + 1, cis);
|
||||||
if (found) {
|
if (found) {
|
||||||
i--;
|
i--;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
fx = gd->sx;
|
fx = gd->sx - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
27
window.c
27
window.c
@ -1608,31 +1608,28 @@ winlink_shuffle_up(struct session *s, struct winlink *wl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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_input_data *cdata = data;
|
||||||
struct window_pane *wp;
|
struct window_pane *wp;
|
||||||
struct evbuffer *evb = c->stdin_data;
|
u_char *buf = EVBUFFER_DATA(buffer);
|
||||||
u_char *buf = EVBUFFER_DATA(evb);
|
size_t len = EVBUFFER_LENGTH(buffer);
|
||||||
size_t len = EVBUFFER_LENGTH(evb);
|
|
||||||
|
|
||||||
wp = window_pane_find_by_id(cdata->wp);
|
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)
|
if (wp == NULL)
|
||||||
c->flags |= CLIENT_EXIT;
|
c->flags |= CLIENT_EXIT;
|
||||||
evbuffer_drain(evb, len);
|
|
||||||
|
|
||||||
c->stdin_callback = NULL;
|
|
||||||
server_client_unref(c);
|
|
||||||
|
|
||||||
|
evbuffer_drain(buffer, len);
|
||||||
cmdq_continue(cdata->item);
|
cmdq_continue(cdata->item);
|
||||||
free(cdata);
|
|
||||||
|
|
||||||
|
server_client_unref(c);
|
||||||
|
free(cdata);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
input_parse_buffer(wp, buf, len);
|
input_parse_buffer(wp, buf, len);
|
||||||
evbuffer_drain(evb, len);
|
evbuffer_drain(buffer, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -1651,6 +1648,8 @@ window_pane_start_input(struct window_pane *wp, struct cmdq_item *item,
|
|||||||
cdata->item = item;
|
cdata->item = item;
|
||||||
cdata->wp = wp->id;
|
cdata->wp = wp->id;
|
||||||
|
|
||||||
return (server_set_stdin_callback(c, window_pane_input_callback, cdata,
|
c->references++;
|
||||||
cause));
|
file_read(c, "-", window_pane_input_callback, cdata);
|
||||||
|
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user