diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | client.c | 27 | ||||
-rw-r--r-- | cmd-list.c | 16 | ||||
-rw-r--r-- | control.c | 121 | ||||
-rw-r--r-- | server-client.c | 14 | ||||
-rw-r--r-- | server-fn.c | 2 | ||||
-rw-r--r-- | tmux.c | 8 | ||||
-rw-r--r-- | tmux.h | 6 |
8 files changed, 191 insertions, 5 deletions
@@ -30,7 +30,7 @@ SRCS= arguments.c attributes.c cfg.c client.c clock.c \ cmd-display-message.c cmd-display-panes.c cmd-if-shell.c \ cmd-pipe-pane.c cmd-capture-pane.c cmd.c \ colour.c environ.c grid-view.c grid-utf8.c grid.c input-keys.c \ - input.c key-bindings.c key-string.c format.c \ + input.c key-bindings.c key-string.c format.c control.c \ layout-custom.c layout-set.c layout.c log.c job.c notify.c \ mode-key.c names.c options.c options-table.c paste.c procname.c \ resize.c screen-redraw.c screen-write.c screen.c session.c status.c \ @@ -169,6 +169,7 @@ client_main(int argc, char **argv, int flags) pid_t ppid; enum msgtype msg; char *cause; + struct termios tio, saved_tio; /* Set up the initial command. */ cmdflags = 0; @@ -233,6 +234,23 @@ client_main(int argc, char **argv, int flags) setblocking(STDIN_FILENO, 0); event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, client_stdin_callback, NULL); + if (flags & IDENTIFY_TERMIOS) { + if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { + fprintf(stderr, "tcgetattr failed: %s\n", + strerror(errno)); + return (1); + } + cfmakeraw(&tio); + tio.c_iflag = ICRNL|IXANY; + tio.c_oflag = OPOST|ONLCR; + tio.c_lflag = NOKERNINFO; + tio.c_cflag = CREAD|CS8|HUPCL; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + cfsetispeed(&tio, cfgetispeed(&saved_tio)); + cfsetospeed(&tio, cfgetospeed(&saved_tio)); + tcsetattr(STDIN_FILENO, TCSANOW, &tio); + } /* Establish signal handlers. */ set_signals(client_signal); @@ -273,7 +291,8 @@ client_main(int argc, char **argv, int flags) ppid = getppid(); if (client_exittype == MSG_DETACHKILL && ppid > 1) kill(ppid, SIGHUP); - } + } else if (flags & IDENTIFY_TERMIOS) + tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); setblocking(STDIN_FILENO, 1); return (client_exitval); } @@ -513,6 +532,12 @@ client_dispatch_wait(void *data) shell_exec(shelldata.shell, shellcmd); /* NOTREACHED */ + case MSG_DETACH: + client_write_server(MSG_EXITING, NULL, 0); + break; + case MSG_EXITED: + imsg_free(&imsg); + return (-1); default: fatalx("unexpected message"); } @@ -81,12 +81,24 @@ bad: int cmd_list_exec(struct cmd_list *cmdlist, struct cmd_ctx *ctx) { + struct client *c = ctx->curclient; struct cmd *cmd; - int n, retval; + int n, retval, guards; + + guards = 0; + if (c != NULL && c->session != NULL) + guards = c->flags & CLIENT_CONTROL; retval = 0; TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { - if ((n = cmd_exec(cmd, ctx)) == -1) + if (guards) + ctx->print(ctx, "%%begin"); + n = cmd_exec(cmd, ctx); + if (guards) + ctx->print(ctx, "%%end"); + + /* Return of -1 is an error. */ + if (n == -1) return (-1); /* diff --git a/control.c b/control.c new file mode 100644 index 00000000..52ccb202 --- /dev/null +++ b/control.c @@ -0,0 +1,121 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2012 Nicholas Marriott <nicm@users.sourceforge.net> + * Copyright (c) 2012 George Nachman <tmux@georgester.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 <event.h> +#include <string.h> + +#include "tmux.h" + +void printflike2 control_msg_error(struct cmd_ctx *, const char *, ...); +void printflike2 control_msg_print(struct cmd_ctx *, const char *, ...); +void printflike2 control_msg_info(struct cmd_ctx *, const char *, ...); +void printflike2 control_write(struct client *, const char *, ...); + +/* Command error callback. */ +void printflike2 +control_msg_error(struct cmd_ctx *ctx, const char *fmt, ...) +{ + struct client *c = ctx->curclient; + va_list ap; + + va_start(ap, fmt); + evbuffer_add_vprintf(c->stdout_data, fmt, ap); + va_end(ap); + + evbuffer_add(c->stdout_data, "\n", 1); + server_push_stdout(c); +} + +/* Command print callback. */ +void printflike2 +control_msg_print(struct cmd_ctx *ctx, const char *fmt, ...) +{ + struct client *c = ctx->curclient; + va_list ap; + + va_start(ap, fmt); + evbuffer_add_vprintf(c->stdout_data, fmt, ap); + va_end(ap); + + evbuffer_add(c->stdout_data, "\n", 1); + server_push_stdout(c); +} + +/* Command info callback. */ +void printflike2 +control_msg_info(unused struct cmd_ctx *ctx, unused const char *fmt, ...) +{ +} + +/* Write a line. */ +void printflike2 +control_write(struct client *c, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + evbuffer_add_vprintf(c->stdout_data, fmt, ap); + va_end(ap); + + evbuffer_add(c->stdout_data, "\n", 1); + server_push_stdout(c); +} + +/* Control input callback. Read lines and fire commands. */ +void +control_callback(struct client *c, int closed, unused void *data) +{ + char *line, *cause; + struct cmd_ctx ctx; + struct cmd_list *cmdlist; + + if (closed) + c->flags |= CLIENT_EXIT; + + for (;;) { + line = evbuffer_readln(c->stdin_data, NULL, EVBUFFER_EOL_LF); + if (line == NULL) + break; + if (*line == '\0') { /* empty line exit */ + c->flags |= CLIENT_EXIT; + break; + } + + ctx.msgdata = NULL; + ctx.cmdclient = NULL; + ctx.curclient = c; + + ctx.error = control_msg_error; + ctx.print = control_msg_print; + ctx.info = control_msg_info; + + if (cmd_string_parse(line, &cmdlist, &cause) != 0) { + control_write(c, "%%error in line \"%s\": %s", line, + cause); + xfree(cause); + } else { + cmd_list_exec(cmdlist, &ctx); + cmd_list_free(cmdlist); + } + + xfree(line); + } +} diff --git a/server-client.c b/server-client.c index 34217a61..8b972c90 100644 --- a/server-client.c +++ b/server-client.c @@ -108,6 +108,9 @@ server_client_open(struct client *c, struct session *s, char **cause) struct options *oo = s != NULL ? &s->options : &global_s_options; char *overrides; + if (c->flags & CLIENT_CONTROL) + return (0); + if (!(c->flags & CLIENT_TERMINAL)) { *cause = xstrdup ("not a terminal"); return (-1); @@ -894,6 +897,17 @@ server_client_msg_identify( if (*data->cwd != '\0') c->cwd = xstrdup(data->cwd); + if (data->flags & IDENTIFY_CONTROL) { + c->stdin_callback = control_callback; + c->flags |= (CLIENT_CONTROL|CLIENT_SUSPENDED); + + c->tty.fd = -1; + c->tty.log_fd = -1; + + close(fd); + return; + } + if (!isatty(fd)) return; data->term[(sizeof data->term) - 1] = '\0'; diff --git a/server-fn.c b/server-fn.c index 794205d7..1c55e16b 100644 --- a/server-fn.c +++ b/server-fn.c @@ -49,6 +49,8 @@ server_fill_environ(struct session *s, struct environ *env) void server_write_ready(struct client *c) { + if (c->flags & CLIENT_CONTROL) + return; server_write_client(c, MSG_READY, NULL, 0); } @@ -241,7 +241,7 @@ main(int argc, char **argv) quiet = flags = 0; label = path = NULL; login_shell = (**argv == '-'); - while ((opt = getopt(argc, argv, "28c:df:lL:qS:uUv")) != -1) { + while ((opt = getopt(argc, argv, "28c:Cdf:lL:qS:uUv")) != -1) { switch (opt) { case '2': flags |= IDENTIFY_256COLOURS; @@ -256,6 +256,12 @@ main(int argc, char **argv) xfree(shell_cmd); shell_cmd = xstrdup(optarg); break; + case 'C': + if (flags & IDENTIFY_CONTROL) + flags |= IDENTIFY_TERMIOS; + else + flags |= IDENTIFY_CONTROL; + break; case 'f': if (cfg_file != NULL) xfree(cfg_file); @@ -433,6 +433,8 @@ struct msg_identify_data { #define IDENTIFY_UTF8 0x1 #define IDENTIFY_256COLOURS 0x2 #define IDENTIFY_88COLOURS 0x4 +#define IDENTIFY_CONTROL 0x8 +#define IDENTIFY_TERMIOS 0x10 int flags; }; @@ -1232,6 +1234,7 @@ struct client { #define CLIENT_BORDERS 0x400 #define CLIENT_READONLY 0x800 #define CLIENT_REDRAWWINDOW 0x1000 +#define CLIENT_CONTROL 0x2000 int flags; struct event identify_timer; @@ -2131,6 +2134,9 @@ char *default_window_name(struct window *); void set_signals(void(*)(int, short, void *)); void clear_signals(int); +/* control.c */ +void control_callback(struct client *, int, void*); + /* session.c */ extern struct sessions sessions; extern struct sessions dead_sessions; |