aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--client.c27
-rw-r--r--cmd-list.c16
-rw-r--r--control.c121
-rw-r--r--server-client.c14
-rw-r--r--server-fn.c2
-rw-r--r--tmux.c8
-rw-r--r--tmux.h6
8 files changed, 191 insertions, 5 deletions
diff --git a/Makefile b/Makefile
index 2e23b71a..2b9dde1f 100644
--- a/Makefile
+++ b/Makefile
@@ -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 \
diff --git a/client.c b/client.c
index 1a318d3f..911a639f 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;
@@ -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");
}
diff --git a/cmd-list.c b/cmd-list.c
index b8a8c115..dc102dcc 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..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);
}
diff --git a/tmux.c b/tmux.c
index ad632be0..9a305c02 100644
--- a/tmux.c
+++ b/tmux.c
@@ -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);
diff --git a/tmux.h b/tmux.h
index 6971fd0f..891b8e3c 100644
--- a/tmux.h
+++ b/tmux.h
@@ -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;