diff --git a/client.c b/client.c index 0083bbe8..3478b999 100644 --- a/client.c +++ b/client.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; @@ -235,6 +236,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); @@ -275,7 +293,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); } @@ -515,6 +534,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"); } diff --git a/cmd-list.c b/cmd-list.c index 5a234d6d..5ff41687 100644 --- a/cmd-list.c +++ b/cmd-list.c @@ -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..90ab4729 --- /dev/null +++ b/control.c @@ -0,0 +1,121 @@ +/* $Id$ */ + +/* + * Copyright (c) 2012 Nicholas Marriott + * Copyright (c) 2012 George Nachman + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "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 d1f72397..a678a122 100644 --- a/server-client.c +++ b/server-client.c @@ -107,6 +107,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); @@ -893,6 +896,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 d95c498c..ddf7415d 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); } diff --git a/tmux.c b/tmux.c index 3f5e6cdf..5d8bbb09 100644 --- a/tmux.c +++ b/tmux.c @@ -244,7 +244,7 @@ main(int argc, char **argv) quiet = flags = 0; label = path = NULL; login_shell = (**argv == '-'); - while ((opt = getopt(argc, argv, "28c:df:lL:qS:uUvV")) != -1) { + while ((opt = getopt(argc, argv, "28c:Cdf:lL:qS:uUvV")) != -1) { switch (opt) { case '2': flags |= IDENTIFY_256COLOURS; @@ -259,6 +259,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 'V': printf("%s %s\n", __progname, VERSION); exit(0); diff --git a/tmux.h b/tmux.h index d0dc1223..2281f9fb 100644 --- a/tmux.h +++ b/tmux.h @@ -429,6 +429,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; }; @@ -1228,6 +1230,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; @@ -2127,6 +2130,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;