summaryrefslogtreecommitdiff
path: root/usr.bin/tmux
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/tmux')
-rw-r--r--usr.bin/tmux/Makefile48
-rw-r--r--usr.bin/tmux/arg.c194
-rw-r--r--usr.bin/tmux/array.h119
-rw-r--r--usr.bin/tmux/attributes.c92
-rw-r--r--usr.bin/tmux/buffer-poll.c97
-rw-r--r--usr.bin/tmux/buffer.c227
-rw-r--r--usr.bin/tmux/cfg.c132
-rw-r--r--usr.bin/tmux/client-fn.c92
-rw-r--r--usr.bin/tmux/client-msg.c159
-rw-r--r--usr.bin/tmux/client.c226
-rw-r--r--usr.bin/tmux/clock.c161
-rw-r--r--usr.bin/tmux/cmd-attach-session.c80
-rw-r--r--usr.bin/tmux/cmd-bind-key.c157
-rw-r--r--usr.bin/tmux/cmd-break-pane.c92
-rw-r--r--usr.bin/tmux/cmd-choose-session.c107
-rw-r--r--usr.bin/tmux/cmd-choose-window.c106
-rw-r--r--usr.bin/tmux/cmd-clear-history.c68
-rw-r--r--usr.bin/tmux/cmd-clock-mode.c54
-rw-r--r--usr.bin/tmux/cmd-command-prompt.c178
-rw-r--r--usr.bin/tmux/cmd-confirm-before.c141
-rw-r--r--usr.bin/tmux/cmd-copy-buffer.c222
-rw-r--r--usr.bin/tmux/cmd-copy-mode.c56
-rw-r--r--usr.bin/tmux/cmd-delete-buffer.c61
-rw-r--r--usr.bin/tmux/cmd-detach-client.c54
-rw-r--r--usr.bin/tmux/cmd-down-pane.c61
-rw-r--r--usr.bin/tmux/cmd-find-window.c160
-rw-r--r--usr.bin/tmux/cmd-generic.c694
-rw-r--r--usr.bin/tmux/cmd-has-session.c51
-rw-r--r--usr.bin/tmux/cmd-kill-pane.c72
-rw-r--r--usr.bin/tmux/cmd-kill-server.c51
-rw-r--r--usr.bin/tmux/cmd-kill-session.c68
-rw-r--r--usr.bin/tmux/cmd-kill-window.c69
-rw-r--r--usr.bin/tmux/cmd-last-window.c60
-rw-r--r--usr.bin/tmux/cmd-link-window.c109
-rw-r--r--usr.bin/tmux/cmd-list-buffers.c91
-rw-r--r--usr.bin/tmux/cmd-list-clients.c67
-rw-r--r--usr.bin/tmux/cmd-list-commands.c51
-rw-r--r--usr.bin/tmux/cmd-list-keys.c59
-rw-r--r--usr.bin/tmux/cmd-list-sessions.c67
-rw-r--r--usr.bin/tmux/cmd-list-windows.c88
-rw-r--r--usr.bin/tmux/cmd-list.c154
-rw-r--r--usr.bin/tmux/cmd-load-buffer.c106
-rw-r--r--usr.bin/tmux/cmd-lock-server.c54
-rw-r--r--usr.bin/tmux/cmd-move-window.c123
-rw-r--r--usr.bin/tmux/cmd-new-session.c248
-rw-r--r--usr.bin/tmux/cmd-new-window.c241
-rw-r--r--usr.bin/tmux/cmd-next-layout.c55
-rw-r--r--usr.bin/tmux/cmd-next-window.c78
-rw-r--r--usr.bin/tmux/cmd-paste-buffer.c78
-rw-r--r--usr.bin/tmux/cmd-previous-layout.c55
-rw-r--r--usr.bin/tmux/cmd-previous-window.c78
-rw-r--r--usr.bin/tmux/cmd-refresh-client.c54
-rw-r--r--usr.bin/tmux/cmd-rename-session.c57
-rw-r--r--usr.bin/tmux/cmd-rename-window.c61
-rw-r--r--usr.bin/tmux/cmd-resize-pane.c105
-rw-r--r--usr.bin/tmux/cmd-respawn-window.c87
-rw-r--r--usr.bin/tmux/cmd-rotate-window.c111
-rw-r--r--usr.bin/tmux/cmd-save-buffer.c90
-rw-r--r--usr.bin/tmux/cmd-scroll-mode.c72
-rw-r--r--usr.bin/tmux/cmd-select-layout.c86
-rw-r--r--usr.bin/tmux/cmd-select-pane.c69
-rw-r--r--usr.bin/tmux/cmd-select-prompt.c93
-rw-r--r--usr.bin/tmux/cmd-select-window.c71
-rw-r--r--usr.bin/tmux/cmd-send-keys.c184
-rw-r--r--usr.bin/tmux/cmd-send-prefix.c57
-rw-r--r--usr.bin/tmux/cmd-server-info.c179
-rw-r--r--usr.bin/tmux/cmd-set-buffer.c64
-rw-r--r--usr.bin/tmux/cmd-set-option.c169
-rw-r--r--usr.bin/tmux/cmd-set-password.c169
-rw-r--r--usr.bin/tmux/cmd-set-window-option.c170
-rw-r--r--usr.bin/tmux/cmd-show-buffer.c89
-rw-r--r--usr.bin/tmux/cmd-show-options.c110
-rw-r--r--usr.bin/tmux/cmd-show-window-options.c110
-rw-r--r--usr.bin/tmux/cmd-source-file.c147
-rw-r--r--usr.bin/tmux/cmd-split-window.c235
-rw-r--r--usr.bin/tmux/cmd-start-server.c46
-rw-r--r--usr.bin/tmux/cmd-string.c309
-rw-r--r--usr.bin/tmux/cmd-suspend-client.c64
-rw-r--r--usr.bin/tmux/cmd-swap-pane.c285
-rw-r--r--usr.bin/tmux/cmd-swap-window.c75
-rw-r--r--usr.bin/tmux/cmd-switch-client.c161
-rw-r--r--usr.bin/tmux/cmd-unbind-key.c120
-rw-r--r--usr.bin/tmux/cmd-unlink-window.c74
-rw-r--r--usr.bin/tmux/cmd-up-pane.c61
-rw-r--r--usr.bin/tmux/cmd.c393
-rw-r--r--usr.bin/tmux/colour.c123
-rw-r--r--usr.bin/tmux/grid-view.c211
-rw-r--r--usr.bin/tmux/grid.c497
-rw-r--r--usr.bin/tmux/input-keys.c227
-rw-r--r--usr.bin/tmux/input.c1276
-rw-r--r--usr.bin/tmux/key-bindings.c237
-rw-r--r--usr.bin/tmux/key-string.c198
-rw-r--r--usr.bin/tmux/layout-manual.c183
-rw-r--r--usr.bin/tmux/layout.c373
-rw-r--r--usr.bin/tmux/log.c250
-rw-r--r--usr.bin/tmux/mode-key.c210
-rw-r--r--usr.bin/tmux/names.c110
-rw-r--r--usr.bin/tmux/options-cmd.c182
-rw-r--r--usr.bin/tmux/options.c160
-rw-r--r--usr.bin/tmux/paste.c131
-rw-r--r--usr.bin/tmux/procname.c130
-rw-r--r--usr.bin/tmux/resize.c138
-rw-r--r--usr.bin/tmux/screen-redraw.c171
-rw-r--r--usr.bin/tmux/screen-write.c684
-rw-r--r--usr.bin/tmux/screen.c240
-rw-r--r--usr.bin/tmux/server-fn.c242
-rw-r--r--usr.bin/tmux/server-msg.c304
-rw-r--r--usr.bin/tmux/server.c1105
-rw-r--r--usr.bin/tmux/session.c378
-rw-r--r--usr.bin/tmux/status.c960
-rw-r--r--usr.bin/tmux/tmux.11329
-rw-r--r--usr.bin/tmux/tmux.c487
-rw-r--r--usr.bin/tmux/tmux.cat1813
-rw-r--r--usr.bin/tmux/tmux.h1576
-rw-r--r--usr.bin/tmux/tty-keys.c412
-rw-r--r--usr.bin/tmux/tty-term.c393
-rw-r--r--usr.bin/tmux/tty-write.c91
-rw-r--r--usr.bin/tmux/tty.c1076
-rw-r--r--usr.bin/tmux/utf8.c317
-rw-r--r--usr.bin/tmux/util.c72
-rw-r--r--usr.bin/tmux/window-choose.c361
-rw-r--r--usr.bin/tmux/window-clock.c124
-rw-r--r--usr.bin/tmux/window-copy.c968
-rw-r--r--usr.bin/tmux/window-more.c252
-rw-r--r--usr.bin/tmux/window-scroll.c296
-rw-r--r--usr.bin/tmux/window.c625
-rw-r--r--usr.bin/tmux/xmalloc.c144
127 files changed, 28294 insertions, 0 deletions
diff --git a/usr.bin/tmux/Makefile b/usr.bin/tmux/Makefile
new file mode 100644
index 00000000000..9200a8cbfaa
--- /dev/null
+++ b/usr.bin/tmux/Makefile
@@ -0,0 +1,48 @@
+# $OpenBSD: Makefile,v 1.1 2009/06/01 22:58:49 nicm Exp $
+
+.PATH: ${.CURDIR}/..
+
+PROG= tmux
+SRCS= arg.c attributes.c buffer-poll.c buffer.c cfg.c client-fn.c \
+ client-msg.c client.c clock.c cmd-attach-session.c cmd-bind-key.c \
+ cmd-break-pane.c cmd-choose-session.c cmd-choose-window.c \
+ cmd-clear-history.c cmd-clock-mode.c cmd-command-prompt.c \
+ cmd-confirm-before.c cmd-copy-buffer.c cmd-copy-mode.c \
+ cmd-delete-buffer.c cmd-detach-client.c cmd-down-pane.c \
+ cmd-find-window.c cmd-generic.c cmd-has-session.c cmd-kill-pane.c \
+ cmd-kill-server.c cmd-kill-session.c cmd-kill-window.c \
+ cmd-last-window.c cmd-link-window.c cmd-list-buffers.c \
+ cmd-list-clients.c cmd-list-commands.c cmd-list-keys.c \
+ cmd-list-sessions.c cmd-list-windows.c cmd-list.c cmd-load-buffer.c \
+ cmd-lock-server.c cmd-move-window.c cmd-new-session.c cmd-new-window.c \
+ cmd-next-layout.c cmd-next-window.c cmd-paste-buffer.c \
+ cmd-previous-layout.c cmd-previous-window.c cmd-refresh-client.c \
+ cmd-rename-session.c cmd-rename-window.c cmd-resize-pane.c \
+ cmd-respawn-window.c cmd-rotate-window.c cmd-save-buffer.c \
+ cmd-scroll-mode.c cmd-select-layout.c cmd-select-pane.c \
+ cmd-select-prompt.c cmd-select-window.c cmd-send-keys.c \
+ cmd-send-prefix.c cmd-server-info.c cmd-set-buffer.c cmd-set-option.c \
+ cmd-set-password.c cmd-set-window-option.c cmd-show-buffer.c \
+ cmd-show-options.c cmd-show-window-options.c cmd-source-file.c \
+ cmd-split-window.c cmd-start-server.c cmd-string.c \
+ cmd-suspend-client.c cmd-swap-pane.c cmd-swap-window.c \
+ cmd-switch-client.c cmd-unbind-key.c cmd-unlink-window.c \
+ cmd-up-pane.c cmd.c colour.c grid-view.c grid.c input-keys.c \
+ input.c key-bindings.c key-string.c layout-manual.c layout.c log.c \
+ mode-key.c names.c options-cmd.c options.c paste.c procname.c \
+ resize.c screen-redraw.c screen-write.c screen.c server-fn.c \
+ server-msg.c server.c session.c status.c tmux.c tty-keys.c tty-term.c \
+ tty-write.c tty.c utf8.c util.c window-choose.c window-clock.c \
+ window-copy.c window-more.c window-scroll.c window.c xmalloc.c
+
+CFLAGS+= -Wno-long-long -Wall -W -Wnested-externs -Wformat=2
+CFLAGS+= -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
+CFLAGS+= -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare
+CFLAGS+= -Wundef -Wbad-function-cast -Winline -Wcast-align
+
+LDADD= -lutil -lncurses
+DPADD= ${LIBUTIL}
+
+MAN= tmux.1
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tmux/arg.c b/usr.bin/tmux/arg.c
new file mode 100644
index 00000000000..47603b3a405
--- /dev/null
+++ b/usr.bin/tmux/arg.c
@@ -0,0 +1,194 @@
+/* $OpenBSD: arg.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <fnmatch.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+struct client *arg_lookup_client(const char *);
+struct session *arg_lookup_session(const char *);
+
+struct client *
+arg_lookup_client(const char *name)
+{
+ struct client *c;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c != NULL && strcmp(name, c->tty.path) == 0)
+ return (c);
+ }
+
+ return (NULL);
+}
+
+struct session *
+arg_lookup_session(const char *name)
+{
+ struct session *s, *newest = NULL;
+ struct timeval *tv;
+ u_int i;
+
+ tv = NULL;
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ s = ARRAY_ITEM(&sessions, i);
+ if (s == NULL || fnmatch(name, s->name, 0) != 0)
+ continue;
+
+ if (tv == NULL || timercmp(&s->tv, tv, >)) {
+ newest = s;
+ tv = &s->tv;
+ }
+ }
+
+ return (newest);
+}
+
+struct client *
+arg_parse_client(const char *arg)
+{
+ struct client *c;
+ char *arg2;
+ size_t n;
+
+ if (arg != NULL && (arg[0] != ':' || arg[1] != '\0')) {
+ arg2 = xstrdup(arg);
+
+ /* Trim a trailing : if any from the argument. */
+ n = strlen(arg2);
+ if (arg2[n - 1] == ':')
+ arg2[n - 1] = '\0';
+
+ /* Try and look up the client name. */
+ c = arg_lookup_client(arg2);
+ xfree(arg2);
+ return (c);
+ }
+
+ return (NULL);
+}
+
+struct session *
+arg_parse_session(const char *arg)
+{
+ struct session *s;
+ struct client *c;
+ char *arg2;
+ size_t n;
+
+ if (arg != NULL && (arg[0] != ':' || arg[1] != '\0')) {
+ arg2 = xstrdup(arg);
+
+ /* Trim a trailing : if any from the argument. */
+ n = strlen(arg2);
+ if (arg2[n - 1] == ':')
+ arg2[n - 1] = '\0';
+
+ /* See if the argument matches a session. */
+ if ((s = arg_lookup_session(arg2)) != NULL) {
+ xfree(arg2);
+ return (s);
+ }
+
+ /* If not try a client. */
+ if ((c = arg_lookup_client(arg2)) != NULL) {
+ xfree(arg2);
+ return (c->session);
+ }
+
+ xfree(arg2);
+ }
+
+ return (NULL);
+}
+
+int
+arg_parse_window(const char *arg, struct session **s, int *idx)
+{
+ char *arg2, *ptr;
+ const char *errstr;
+
+ *idx = -1;
+
+ /* Handle no argument or a single :. */
+ if (arg == NULL || (arg[0] == ':' && arg[1] == '\0')) {
+ *s = arg_parse_session(NULL);
+ return (0);
+ }
+
+ /* Find the separator if any. */
+ arg2 = xstrdup(arg);
+ ptr = strrchr(arg2, ':');
+
+ /*
+ * If it is first, this means no session name, so use current session
+ * and try to convert the rest as index.
+ */
+ if (ptr == arg2) {
+ *idx = strtonum(ptr + 1, 0, INT_MAX, &errstr);
+ if (errstr != NULL) {
+ xfree(arg2);
+ return (1);
+ }
+
+ xfree(arg2);
+ *s = arg_parse_session(NULL);
+ return (0);
+ }
+
+ /* If missing, try as an index, else look up immediately. */
+ if (ptr == NULL) {
+ *idx = strtonum(arg2, 0, INT_MAX, &errstr);
+ if (errstr == NULL) {
+ /* This is good as an index; use current session. */
+ xfree(arg2);
+ *s = arg_parse_session(NULL);
+ return (0);
+ }
+
+ *idx = -1;
+ goto lookup;
+ }
+
+ /* If last, strip it and look up as a session. */
+ if (ptr[1] == '\0') {
+ *ptr = '\0';
+ goto lookup;
+ }
+
+ /* Present but not first and not last. Break and convert both. */
+ *ptr = '\0';
+ *idx = strtonum(ptr + 1, 0, INT_MAX, &errstr);
+ if (errstr != NULL) {
+ xfree(arg2);
+ return (1);
+ }
+
+lookup:
+ /* Look up as session. */
+ *s = arg_parse_session(arg2);
+ xfree(arg2);
+ if (*s == NULL)
+ return (1);
+ return (0);
+}
diff --git a/usr.bin/tmux/array.h b/usr.bin/tmux/array.h
new file mode 100644
index 00000000000..c3603c69894
--- /dev/null
+++ b/usr.bin/tmux/array.h
@@ -0,0 +1,119 @@
+/* $OpenBSD: array.h,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2006 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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.
+ */
+
+#ifndef ARRAY_H
+#define ARRAY_H
+
+#define ARRAY_DECL(n, c) \
+ struct n { \
+ c *list; \
+ u_int num; \
+ size_t space; \
+ }
+
+#define ARRAY_ITEM(a, i) ((a)->list[i])
+#define ARRAY_ITEMSIZE(a) (sizeof *(a)->list)
+#define ARRAY_INITIALSPACE(a) (10 * ARRAY_ITEMSIZE(a))
+
+#define ARRAY_ENSURE(a, n) do { \
+ if (UINT_MAX - (n) < (a)->num) \
+ fatalx("number too big"); \
+ if (SIZE_MAX / ((a)->num + (n)) < ARRAY_ITEMSIZE(a)) \
+ fatalx("size too big"); \
+ if ((a)->space == 0) { \
+ (a)->space = ARRAY_INITIALSPACE(a); \
+ (a)->list = xrealloc((a)->list, 1, (a)->space); \
+ } \
+ while ((a)->space <= ((a)->num + (n)) * ARRAY_ITEMSIZE(a)) { \
+ (a)->list = xrealloc((a)->list, 2, (a)->space); \
+ (a)->space *= 2; \
+ } \
+} while (0)
+
+#define ARRAY_EMPTY(a) ((a) == NULL || (a)->num == 0)
+#define ARRAY_LENGTH(a) ((a)->num)
+#define ARRAY_DATA(a) ((a)->list)
+
+#define ARRAY_FIRST(a) ARRAY_ITEM(a, 0)
+#define ARRAY_LAST(a) ARRAY_ITEM(a, (a)->num - 1)
+
+#define ARRAY_INIT(a) do { \
+ (a)->num = 0; \
+ (a)->list = NULL; \
+ (a)->space = 0; \
+} while (0)
+#define ARRAY_CLEAR(a) do { \
+ (a)->num = 0; \
+} while (0)
+
+#define ARRAY_SET(a, i, s) do { \
+ (a)->list[i] = s; \
+} while (0)
+
+#define ARRAY_ADD(a, s) do { \
+ ARRAY_ENSURE(a, 1); \
+ (a)->list[(a)->num] = s; \
+ (a)->num++; \
+} while (0)
+#define ARRAY_INSERT(a, i, s) do { \
+ ARRAY_ENSURE(a, 1); \
+ if ((i) < (a)->num) { \
+ memmove((a)->list + (i) + 1, (a)->list + (i), \
+ ARRAY_ITEMSIZE(a) * ((a)->num - (i))); \
+ } \
+ (a)->list[i] = s; \
+ (a)->num++; \
+} while (0)
+#define ARRAY_REMOVE(a, i) do { \
+ if ((i) < (a)->num - 1) { \
+ memmove((a)->list + (i), (a)->list + (i) + 1, \
+ ARRAY_ITEMSIZE(a) * ((a)->num - (i) - 1)); \
+ } \
+ (a)->num--; \
+ if ((a)->num == 0) \
+ ARRAY_FREE(a); \
+} while (0)
+
+#define ARRAY_EXPAND(a, n) do { \
+ ARRAY_ENSURE(a, n); \
+ (a)->num += n; \
+} while (0)
+#define ARRAY_TRUNC(a, n) do { \
+ if ((a)->num > n) \
+ (a)->num -= n; \
+ else \
+ ARRAY_FREE(a); \
+} while (0)
+
+#define ARRAY_CONCAT(a, b) do { \
+ ARRAY_ENSURE(a, (b)->num); \
+ memcpy((a)->list + (a)->num, (b)->list, (b)->num * ARRAY_ITEMSIZE(a)) \
+ (a)->num += (b)->num; \
+} while (0)
+
+#define ARRAY_FREE(a) do { \
+ if ((a)->list != NULL) \
+ xfree((a)->list); \
+ ARRAY_INIT(a); \
+} while (0)
+#define ARRAY_FREEALL(a) do { \
+ ARRAY_FREE(a); \
+ xfree(a); \
+} while (0)
+
+#endif
diff --git a/usr.bin/tmux/attributes.c b/usr.bin/tmux/attributes.c
new file mode 100644
index 00000000000..7248b99f34a
--- /dev/null
+++ b/usr.bin/tmux/attributes.c
@@ -0,0 +1,92 @@
+/* $OpenBSD: attributes.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Joshua Elsasser <josh@elsasser.org>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+const char *
+attributes_tostring(u_char ch)
+{
+ static char buf[128];
+
+ if (ch == 0)
+ return ("default");
+
+ buf[0] = '\0';
+ if (ch & GRID_ATTR_BRIGHT)
+ strlcat(buf, "bright,", sizeof (buf));
+ if (ch & GRID_ATTR_DIM)
+ strlcat(buf, "dim,", sizeof (buf));
+ if (ch & GRID_ATTR_UNDERSCORE)
+ strlcat(buf, "underscore,", sizeof (buf));
+ if (ch & GRID_ATTR_BLINK)
+ strlcat(buf, "blink,", sizeof (buf));
+ if (ch & GRID_ATTR_REVERSE)
+ strlcat(buf, "reverse,", sizeof (buf));
+ if (ch & GRID_ATTR_HIDDEN)
+ strlcat(buf, "hidden,", sizeof (buf));
+ if (ch & GRID_ATTR_ITALICS)
+ strlcat(buf, "italics,", sizeof (buf));
+ *(strrchr(buf, ',')) = '\0';
+
+ return (buf);
+}
+
+int
+attributes_fromstring(const char *str)
+{
+ const char delimiters[] = " ,|";
+ u_char ch;
+ size_t end;
+
+ if (*str == '\0' || strcspn(str, delimiters) == 0)
+ return (-1);
+ if (strchr(delimiters, str[strlen(str) - 1]) != NULL)
+ return (-1);
+
+ if (strcasecmp(str, "default") == 0)
+ return (0);
+
+ ch = 0;
+ do {
+ end = strcspn(str, delimiters);
+ if ((end == 6 && strncasecmp(str, "bright", end) == 0) ||
+ (end == 4 && strncasecmp(str, "bold", end) == 0))
+ ch |= GRID_ATTR_BRIGHT;
+ else if (end == 3 && strncasecmp(str, "dim", end) == 0)
+ ch |= GRID_ATTR_DIM;
+ else if (end == 10 && strncasecmp(str, "underscore", end) == 0)
+ ch |= GRID_ATTR_UNDERSCORE;
+ else if (end == 5 && strncasecmp(str, "blink", end) == 0)
+ ch |= GRID_ATTR_BLINK;
+ else if (end == 7 && strncasecmp(str, "reverse", end) == 0)
+ ch |= GRID_ATTR_REVERSE;
+ else if (end == 6 && strncasecmp(str, "hidden", end) == 0)
+ ch |= GRID_ATTR_HIDDEN;
+ else if (end == 7 && strncasecmp(str, "italics", end) == 0)
+ ch |= GRID_ATTR_ITALICS;
+ else
+ return (-1);
+ str += end + strspn(str + end, delimiters);
+ } while (*str != '\0');
+
+ return (ch);
+}
diff --git a/usr.bin/tmux/buffer-poll.c b/usr.bin/tmux/buffer-poll.c
new file mode 100644
index 00000000000..dc6507a5e38
--- /dev/null
+++ b/usr.bin/tmux/buffer-poll.c
@@ -0,0 +1,97 @@
+/* $OpenBSD: buffer-poll.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <errno.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/* Set up pollfd for buffers. */
+void
+buffer_set(
+ struct pollfd *pfd, int fd, unused struct buffer *in, struct buffer *out)
+{
+ pfd->fd = fd;
+ pfd->events = POLLIN;
+ if (BUFFER_USED(out) > 0)
+ pfd->events |= POLLOUT;
+}
+
+/* Fill buffers from socket based on poll results. */
+int
+buffer_poll(struct pollfd *pfd, struct buffer *in, struct buffer *out)
+{
+ ssize_t n;
+
+#if 0
+ log_debug("buffer_poll (%ld): fd=%d, revents=%d; out=%zu in=%zu",
+ (long) getpid(),
+ pfd->fd, pfd->revents, BUFFER_USED(out), BUFFER_USED(in));
+#endif
+
+ if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP))
+ return (-1);
+ if (pfd->revents & POLLIN) {
+ buffer_ensure(in, BUFSIZ);
+ n = read(pfd->fd, BUFFER_IN(in), BUFFER_FREE(in));
+#if 0
+ log_debug("buffer_poll: fd=%d, read=%zd", pfd->fd, n);
+#endif
+ if (n == 0)
+ return (-1);
+ if (n == -1) {
+ if (errno != EINTR && errno != EAGAIN)
+ return (-1);
+ } else
+ buffer_add(in, n);
+ }
+ if (BUFFER_USED(out) > 0 && pfd->revents & POLLOUT) {
+ n = write(pfd->fd, BUFFER_OUT(out), BUFFER_USED(out));
+#if 0
+ log_debug("buffer_poll: fd=%d, write=%zd", pfd->fd, n);
+#endif
+ if (n == -1) {
+ if (errno != EINTR && errno != EAGAIN)
+ return (-1);
+ } else
+ buffer_remove(out, n);
+ }
+ return (0);
+}
+
+/* Flush buffer output to socket. */
+void
+buffer_flush(int fd, struct buffer *in, struct buffer *out)
+{
+ struct pollfd pfd;
+
+ while (BUFFER_USED(out) > 0) {
+ buffer_set(&pfd, fd, in, out);
+
+ if (poll(&pfd, 1, INFTIM) == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ fatal("poll failed");
+ }
+
+ if (buffer_poll(&pfd, in, out) != 0)
+ break;
+ }
+}
diff --git a/usr.bin/tmux/buffer.c b/usr.bin/tmux/buffer.c
new file mode 100644
index 00000000000..7ff1b10efc0
--- /dev/null
+++ b/usr.bin/tmux/buffer.c
@@ -0,0 +1,227 @@
+/* $OpenBSD: buffer.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+/* Create a buffer. */
+struct buffer *
+buffer_create(size_t size)
+{
+ struct buffer *b;
+
+ if (size == 0)
+ fatalx("zero size");
+
+ b = xcalloc(1, sizeof *b);
+
+ b->base = xmalloc(size);
+ b->space = size;
+
+ return (b);
+}
+
+/* Destroy a buffer. */
+void
+buffer_destroy(struct buffer *b)
+{
+ xfree(b->base);
+ xfree(b);
+}
+
+/* Empty a buffer. */
+void
+buffer_clear(struct buffer *b)
+{
+ b->size = 0;
+ b->off = 0;
+}
+
+/* Ensure free space for size in buffer. */
+void
+buffer_ensure(struct buffer *b, size_t size)
+{
+ if (size == 0)
+ fatalx("zero size");
+
+ if (BUFFER_FREE(b) >= size)
+ return;
+
+ if (b->off > 0) {
+ if (b->size > 0)
+ memmove(b->base, b->base + b->off, b->size);
+ b->off = 0;
+ }
+
+ if (SIZE_MAX - b->size < size)
+ fatalx("size too big");
+ while (b->space < b->size + size) {
+ b->base = xrealloc(b->base, 2, b->space);
+ b->space *= 2;
+ }
+}
+
+/* Adjust buffer after data appended. */
+void
+buffer_add(struct buffer *b, size_t size)
+{
+ if (size == 0)
+ fatalx("zero size");
+ if (size > b->space - b->size)
+ fatalx("overflow");
+
+ b->size += size;
+}
+
+/* Reverse buffer add. */
+void
+buffer_reverse_add(struct buffer *b, size_t size)
+{
+ if (size == 0)
+ fatalx("zero size");
+ if (size > b->size)
+ fatalx("underflow");
+
+ b->size -= size;
+}
+
+/* Adjust buffer after data removed. */
+void
+buffer_remove(struct buffer *b, size_t size)
+{
+ if (size == 0)
+ fatalx("zero size");
+ if (size > b->size)
+ fatalx("underflow");
+
+ b->size -= size;
+ b->off += size;
+}
+
+/* Reverse buffer remove. */
+void
+buffer_reverse_remove(struct buffer *b, size_t size)
+{
+ if (size == 0)
+ fatalx("zero size");
+ if (size > b->off)
+ fatalx("overflow");
+
+ b->size += size;
+ b->off -= size;
+}
+
+/* Insert a section into the buffer. */
+void
+buffer_insert_range(struct buffer *b, size_t base, size_t size)
+{
+ if (size == 0)
+ fatalx("zero size");
+ if (base > b->size)
+ fatalx("range outside buffer");
+
+ buffer_ensure(b, size);
+ memmove(b->base + b->off + base + size,
+ b->base + b->off + base, b->size - base);
+ b->size += size;
+}
+
+/* Delete a section from the buffer. */
+void
+buffer_delete_range(struct buffer *b, size_t base, size_t size)
+{
+ if (size == 0)
+ fatalx("zero size");
+ if (size > b->size)
+ fatalx("size too big");
+ if (base + size > b->size)
+ fatalx("range outside buffer");
+
+ memmove(b->base + b->off + base,
+ b->base + b->off + base + size, b->size - base - size);
+ b->size -= size;
+}
+
+/* Copy data into a buffer. */
+void
+buffer_write(struct buffer *b, const void *data, size_t size)
+{
+ if (size == 0)
+ fatalx("zero size");
+
+ buffer_ensure(b, size);
+ memcpy(BUFFER_IN(b), data, size);
+ buffer_add(b, size);
+}
+
+/* Copy data out of a buffer. */
+void
+buffer_read(struct buffer *b, void *data, size_t size)
+{
+ if (size == 0)
+ fatalx("zero size");
+ if (size > b->size)
+ fatalx("underflow");
+
+ memcpy(data, BUFFER_OUT(b), size);
+ buffer_remove(b, size);
+}
+
+/* Store an 8-bit value. */
+void
+buffer_write8(struct buffer *b, uint8_t n)
+{
+ buffer_ensure(b, 1);
+ BUFFER_IN(b)[0] = n;
+ buffer_add(b, 1);
+}
+
+/* Store a 16-bit value. */
+void
+buffer_write16(struct buffer *b, uint16_t n)
+{
+ buffer_ensure(b, 2);
+ BUFFER_IN(b)[0] = n & 0xff;
+ BUFFER_IN(b)[1] = n >> 8;
+ buffer_add(b, 2);
+}
+
+/* Extract an 8-bit value. */
+uint8_t
+buffer_read8(struct buffer *b)
+{
+ uint8_t n;
+
+ n = BUFFER_OUT(b)[0];
+ buffer_remove(b, 1);
+ return (n);
+}
+
+/* Extract a 16-bit value. */
+uint16_t
+buffer_read16(struct buffer *b)
+{
+ uint16_t n;
+
+ n = BUFFER_OUT(b)[0] | (BUFFER_OUT(b)[1] << 8);
+ buffer_remove(b, 2);
+ return (n);
+}
diff --git a/usr.bin/tmux/cfg.c b/usr.bin/tmux/cfg.c
new file mode 100644
index 00000000000..2eb035c679b
--- /dev/null
+++ b/usr.bin/tmux/cfg.c
@@ -0,0 +1,132 @@
+/* $OpenBSD: cfg.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/stat.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Config file parser. Pretty quick and simple, each line is parsed into a
+ * argv array and executed as a command.
+ */
+
+char *cfg_string(FILE *, char, int);
+void printflike2 cfg_print(struct cmd_ctx *, const char *, ...);
+void printflike2 cfg_error(struct cmd_ctx *, const char *, ...);
+
+char *cfg_cause;
+
+void printflike2
+cfg_print(unused struct cmd_ctx *ctx, unused const char *fmt, ...)
+{
+}
+
+void printflike2
+cfg_error(unused struct cmd_ctx *ctx, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ xvasprintf(&cfg_cause, fmt, ap);
+ va_end(ap);
+}
+
+int
+load_cfg(const char *path, char **cause)
+{
+ FILE *f;
+ u_int n;
+ struct stat sb;
+ char *buf, *line, *ptr;
+ size_t len;
+ struct cmd_list *cmdlist;
+ struct cmd_ctx ctx;
+
+ if (stat(path, &sb) != 0) {
+ xasprintf(cause, "%s: %s", path, strerror(errno));
+ return (-1);
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ xasprintf(cause, "%s: not a regular file", path);
+ return (-1);
+ }
+
+ if ((f = fopen(path, "rb")) == NULL) {
+ xasprintf(cause, "%s: %s", path, strerror(errno));
+ return (1);
+ }
+ n = 0;
+
+ line = NULL;
+ while ((buf = fgetln(f, &len))) {
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ else {
+ line = xrealloc(line, 1, len + 1);
+ memcpy(line, buf, len);
+ line[len] = '\0';
+ buf = line;
+ }
+ n++;
+
+ if (cmd_string_parse(buf, &cmdlist, cause) != 0) {
+ if (*cause == NULL)
+ continue;
+ goto error;
+ }
+ if (cmdlist == NULL)
+ continue;
+ cfg_cause = NULL;
+
+ ctx.msgdata = NULL;
+ ctx.cursession = NULL;
+ ctx.curclient = NULL;
+
+ ctx.error = cfg_error;
+ ctx.print = cfg_print;
+ ctx.info = cfg_print;
+
+ ctx.cmdclient = NULL;
+
+ cfg_cause = NULL;
+ cmd_list_exec(cmdlist, &ctx);
+ cmd_list_free(cmdlist);
+ if (cfg_cause != NULL) {
+ *cause = cfg_cause;
+ goto error;
+ }
+ }
+ if (line != NULL)
+ xfree(line);
+ fclose(f);
+
+ return (0);
+
+error:
+ fclose(f);
+
+ xasprintf(&ptr, "%s: %s at line %u", path, *cause, n);
+ xfree(*cause);
+ *cause = ptr;
+ return (1);
+}
diff --git a/usr.bin/tmux/client-fn.c b/usr.bin/tmux/client-fn.c
new file mode 100644
index 00000000000..8a0e9c8627f
--- /dev/null
+++ b/usr.bin/tmux/client-fn.c
@@ -0,0 +1,92 @@
+/* $OpenBSD: client-fn.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+void
+client_fill_session(struct msg_command_data *data)
+{
+ char *env, *ptr1, *ptr2, buf[256];
+ size_t len;
+ const char *errstr;
+ long long ll;
+
+ data->pid = -1;
+ if ((env = getenv("TMUX")) == NULL)
+ return;
+
+ if ((ptr2 = strrchr(env, ',')) == NULL || ptr2 == env)
+ return;
+ for (ptr1 = ptr2 - 1; ptr1 > env && *ptr1 != ','; ptr1--)
+ ;
+ if (*ptr1 != ',')
+ return;
+ ptr1++;
+ ptr2++;
+
+ len = ptr2 - ptr1 - 1;
+ if (len > (sizeof buf) - 1)
+ return;
+ memcpy(buf, ptr1, len);
+ buf[len] = '\0';
+
+ ll = strtonum(buf, 0, LONG_MAX, &errstr);
+ if (errstr != NULL)
+ return;
+ data->pid = ll;
+
+ ll = strtonum(ptr2, 0, UINT_MAX, &errstr);
+ if (errstr != NULL)
+ return;
+ data->idx = ll;
+}
+
+void
+client_write_server(
+ struct client_ctx *cctx, enum hdrtype type, void *buf, size_t len)
+{
+ struct hdr hdr;
+
+ hdr.type = type;
+ hdr.size = len;
+ buffer_write(cctx->srv_out, &hdr, sizeof hdr);
+
+ if (buf != NULL && len > 0)
+ buffer_write(cctx->srv_out, buf, len);
+}
+
+void
+client_write_server2(struct client_ctx *cctx,
+ enum hdrtype type, void *buf1, size_t len1, void *buf2, size_t len2)
+{
+ struct hdr hdr;
+
+ hdr.type = type;
+ hdr.size = len1 + len2;
+ buffer_write(cctx->srv_out, &hdr, sizeof hdr);
+
+ if (buf1 != NULL && len1 > 0)
+ buffer_write(cctx->srv_out, buf1, len1);
+ if (buf2 != NULL && len2 > 0)
+ buffer_write(cctx->srv_out, buf2, len2);
+}
diff --git a/usr.bin/tmux/client-msg.c b/usr.bin/tmux/client-msg.c
new file mode 100644
index 00000000000..bafbb1d9a45
--- /dev/null
+++ b/usr.bin/tmux/client-msg.c
@@ -0,0 +1,159 @@
+/* $OpenBSD: client-msg.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+int client_msg_fn_detach(struct hdr *, struct client_ctx *, char **);
+int client_msg_fn_error(struct hdr *, struct client_ctx *, char **);
+int client_msg_fn_shutdown(struct hdr *, struct client_ctx *, char **);
+int client_msg_fn_exit(struct hdr *, struct client_ctx *, char **);
+int client_msg_fn_exited(struct hdr *, struct client_ctx *, char **);
+int client_msg_fn_suspend(struct hdr *, struct client_ctx *, char **);
+
+struct client_msg {
+ enum hdrtype type;
+ int (*fn)(struct hdr *, struct client_ctx *, char **);
+};
+struct client_msg client_msg_table[] = {
+ { MSG_DETACH, client_msg_fn_detach },
+ { MSG_ERROR, client_msg_fn_error },
+ { MSG_EXIT, client_msg_fn_exit },
+ { MSG_EXITED, client_msg_fn_exited },
+ { MSG_SHUTDOWN, client_msg_fn_shutdown },
+ { MSG_SUSPEND, client_msg_fn_suspend },
+};
+
+int
+client_msg_dispatch(struct client_ctx *cctx, char **error)
+{
+ struct hdr hdr;
+ struct client_msg *msg;
+ u_int i;
+
+ if (BUFFER_USED(cctx->srv_in) < sizeof hdr)
+ return (1);
+ memcpy(&hdr, BUFFER_OUT(cctx->srv_in), sizeof hdr);
+ if (BUFFER_USED(cctx->srv_in) < (sizeof hdr) + hdr.size)
+ return (1);
+ buffer_remove(cctx->srv_in, sizeof hdr);
+
+ for (i = 0; i < nitems(client_msg_table); i++) {
+ msg = client_msg_table + i;
+ if (msg->type == hdr.type) {
+ if (msg->fn(&hdr, cctx, error) != 0)
+ return (-1);
+ return (0);
+ }
+ }
+ fatalx("unexpected message");
+}
+
+int
+client_msg_fn_error(struct hdr *hdr, struct client_ctx *cctx, char **error)
+{
+ if (hdr->size > SIZE_MAX - 1)
+ fatalx("bad MSG_ERROR size");
+
+ *error = xmalloc(hdr->size + 1);
+ buffer_read(cctx->srv_in, *error, hdr->size);
+ (*error)[hdr->size] = '\0';
+
+ return (-1);
+}
+
+int
+client_msg_fn_detach(
+ struct hdr *hdr, unused struct client_ctx *cctx, unused char **error)
+{
+ if (hdr->size != 0)
+ fatalx("bad MSG_DETACH size");
+
+ client_write_server(cctx, MSG_EXITING, NULL, 0);
+ cctx->flags |= CCTX_DETACH;
+
+ return (0);
+}
+
+int
+client_msg_fn_shutdown(
+ struct hdr *hdr, unused struct client_ctx *cctx, unused char **error)
+{
+ if (hdr->size != 0)
+ fatalx("bad MSG_SHUTDOWN size");
+
+ client_write_server(cctx, MSG_EXITING, NULL, 0);
+ cctx->flags |= CCTX_SHUTDOWN;
+
+ return (0);
+}
+
+int
+client_msg_fn_exit(
+ struct hdr *hdr, unused struct client_ctx *cctx, unused char **error)
+{
+ if (hdr->size != 0)
+ fatalx("bad MSG_EXIT size");
+
+ client_write_server(cctx, MSG_EXITING, NULL, 0);
+ cctx->flags |= CCTX_EXIT;
+
+ return (0);
+}
+
+int
+client_msg_fn_exited(
+ struct hdr *hdr, unused struct client_ctx *cctx, unused char **error)
+{
+ if (hdr->size != 0)
+ fatalx("bad MSG_EXITED size");
+
+ return (-1);
+}
+
+int
+client_msg_fn_suspend(
+ struct hdr *hdr, unused struct client_ctx *cctx, unused char **error)
+{
+ struct sigaction act;
+
+ if (hdr->size != 0)
+ fatalx("bad MSG_SUSPEND size");
+
+ memset(&act, 0, sizeof act);
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+
+ act.sa_handler = SIG_DFL;
+ if (sigaction(SIGTSTP, &act, NULL) != 0)
+ fatal("sigaction failed");
+
+ act.sa_handler = sighandler;
+ if (sigaction(SIGCONT, &act, NULL) != 0)
+ fatal("sigaction failed");
+
+ kill(getpid(), SIGTSTP);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/client.c b/usr.bin/tmux/client.c
new file mode 100644
index 00000000000..2068484ce66
--- /dev/null
+++ b/usr.bin/tmux/client.c
@@ -0,0 +1,226 @@
+/* $OpenBSD: client.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+void client_handle_winch(struct client_ctx *);
+
+int
+client_init(char *path, struct client_ctx *cctx, int start_server, int flags)
+{
+ struct sockaddr_un sa;
+ struct stat sb;
+ struct msg_identify_data data;
+ struct winsize ws;
+ size_t size;
+ int mode;
+ struct buffer *b;
+ char *name;
+
+ if (lstat(path, &sb) != 0) {
+ if (start_server && errno == ENOENT) {
+ if ((cctx->srv_fd = server_start(path)) == -1)
+ goto start_failed;
+ goto server_started;
+ }
+ goto not_found;
+ }
+ if (!S_ISSOCK(sb.st_mode)) {
+ errno = ENOTSOCK;
+ goto not_found;
+ }
+
+ memset(&sa, 0, sizeof sa);
+ sa.sun_family = AF_UNIX;
+ size = strlcpy(sa.sun_path, path, sizeof sa.sun_path);
+ if (size >= sizeof sa.sun_path) {
+ errno = ENAMETOOLONG;
+ goto not_found;
+ }
+
+ if ((cctx->srv_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ fatal("socket");
+
+ if (connect(
+ cctx->srv_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {
+ if (errno == ECONNREFUSED) {
+ if (unlink(path) != 0 || !start_server)
+ goto not_found;
+ if ((cctx->srv_fd = server_start(path)) == -1)
+ goto start_failed;
+ goto server_started;
+ }
+ goto not_found;
+ }
+
+server_started:
+ if ((mode = fcntl(cctx->srv_fd, F_GETFL)) == -1)
+ fatal("fcntl failed");
+ if (fcntl(cctx->srv_fd, F_SETFL, mode|O_NONBLOCK) == -1)
+ fatal("fcntl failed");
+ cctx->srv_in = buffer_create(BUFSIZ);
+ cctx->srv_out = buffer_create(BUFSIZ);
+
+ if (isatty(STDIN_FILENO)) {
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1)
+ fatal("ioctl(TIOCGWINSZ)");
+ data.version = PROTOCOL_VERSION;
+ data.flags = flags;
+ data.sx = ws.ws_col;
+ data.sy = ws.ws_row;
+ *data.tty = '\0';
+ if (getcwd(data.cwd, sizeof data.cwd) == NULL)
+ *data.cwd = '\0';
+
+ if ((name = ttyname(STDIN_FILENO)) == NULL)
+ fatal("ttyname failed");
+ if (strlcpy(data.tty, name, sizeof data.tty) >= sizeof data.tty)
+ fatalx("ttyname failed");
+
+ b = buffer_create(BUFSIZ);
+ cmd_send_string(b, getenv("TERM"));
+ client_write_server2(cctx, MSG_IDENTIFY,
+ &data, sizeof data, BUFFER_OUT(b), BUFFER_USED(b));
+ buffer_destroy(b);
+ }
+
+ return (0);
+
+start_failed:
+ log_warnx("server failed to start");
+ return (1);
+
+not_found:
+ log_warn("server not found");
+ return (1);
+}
+
+int
+client_main(struct client_ctx *cctx)
+{
+ struct pollfd pfd;
+ char *error;
+ int xtimeout; /* Yay for ncurses namespace! */
+
+ siginit();
+
+ logfile("client");
+ setproctitle("client");
+
+ error = NULL;
+ xtimeout = INFTIM;
+ while (!sigterm) {
+ if (sigchld) {
+ waitpid(WAIT_ANY, NULL, WNOHANG);
+ sigchld = 0;
+ }
+ if (sigwinch)
+ client_handle_winch(cctx);
+ if (sigcont) {
+ siginit();
+ client_write_server(cctx, MSG_WAKEUP, NULL, 0);
+ sigcont = 0;
+ }
+
+ switch (client_msg_dispatch(cctx, &error)) {
+ case -1:
+ goto out;
+ case 0:
+ /* May be more in buffer, don't let poll block. */
+ xtimeout = 0;
+ break;
+ default:
+ /* Out of data, poll may block. */
+ xtimeout = INFTIM;
+ break;
+ }
+
+ pfd.fd = cctx->srv_fd;
+ pfd.events = POLLIN;
+ if (BUFFER_USED(cctx->srv_out) > 0)
+ pfd.events |= POLLOUT;
+
+ if (poll(&pfd, 1, xtimeout) == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ fatal("poll failed");
+ }
+
+ if (buffer_poll(&pfd, cctx->srv_in, cctx->srv_out) != 0)
+ goto server_dead;
+ }
+
+out:
+ if (sigterm) {
+ printf("[terminated]\n");
+ return (1);
+ }
+
+ if (cctx->flags & CCTX_SHUTDOWN) {
+ printf("[server exited]\n");
+ return (0);
+ }
+
+ if (cctx->flags & CCTX_EXIT) {
+ printf("[exited]\n");
+ return (0);
+ }
+
+ if (cctx->flags & CCTX_DETACH) {
+ printf("[detached]\n");
+ return (0);
+ }
+
+ printf("[error: %s]\n", error);
+ return (1);
+
+server_dead:
+ printf("[lost server]\n");
+ return (0);
+}
+
+void
+client_handle_winch(struct client_ctx *cctx)
+{
+ struct msg_resize_data data;
+ struct winsize ws;
+
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1)
+ fatal("ioctl failed");
+
+ data.sx = ws.ws_col;
+ data.sy = ws.ws_row;
+ client_write_server(cctx, MSG_RESIZE, &data, sizeof data);
+
+ sigwinch = 0;
+}
diff --git a/usr.bin/tmux/clock.c b/usr.bin/tmux/clock.c
new file mode 100644
index 00000000000..9f13d42bb19
--- /dev/null
+++ b/usr.bin/tmux/clock.c
@@ -0,0 +1,161 @@
+/* $OpenBSD: clock.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+#include <time.h>
+
+#include "tmux.h"
+
+const char clock_table[14][5][5] = {
+ { { 1,1,1,1,1 }, /* 0 */
+ { 1,0,0,0,1 },
+ { 1,0,0,0,1 },
+ { 1,0,0,0,1 },
+ { 1,1,1,1,1 } },
+ { { 0,0,0,0,1 }, /* 1 */
+ { 0,0,0,0,1 },
+ { 0,0,0,0,1 },
+ { 0,0,0,0,1 },
+ { 0,0,0,0,1 } },
+ { { 1,1,1,1,1 }, /* 2 */
+ { 0,0,0,0,1 },
+ { 1,1,1,1,1 },
+ { 1,0,0,0,0 },
+ { 1,1,1,1,1 } },
+ { { 1,1,1,1,1 }, /* 3 */
+ { 0,0,0,0,1 },
+ { 1,1,1,1,1 },
+ { 0,0,0,0,1 },
+ { 1,1,1,1,1 } },
+ { { 1,0,0,0,1 }, /* 4 */
+ { 1,0,0,0,1 },
+ { 1,1,1,1,1 },
+ { 0,0,0,0,1 },
+ { 0,0,0,0,1 } },
+ { { 1,1,1,1,1 }, /* 5 */
+ { 1,0,0,0,0 },
+ { 1,1,1,1,1 },
+ { 0,0,0,0,1 },
+ { 1,1,1,1,1 } },
+ { { 1,1,1,1,1 }, /* 6 */
+ { 1,0,0,0,0 },
+ { 1,1,1,1,1 },
+ { 1,0,0,0,1 },
+ { 1,1,1,1,1 } },
+ { { 1,1,1,1,1 }, /* 7 */
+ { 0,0,0,0,1 },
+ { 0,0,0,0,1 },
+ { 0,0,0,0,1 },
+ { 0,0,0,0,1 } },
+ { { 1,1,1,1,1 }, /* 8 */
+ { 1,0,0,0,1 },
+ { 1,1,1,1,1 },
+ { 1,0,0,0,1 },
+ { 1,1,1,1,1 } },
+ { { 1,1,1,1,1 }, /* 9 */
+ { 1,0,0,0,1 },
+ { 1,1,1,1,1 },
+ { 0,0,0,0,1 },
+ { 1,1,1,1,1 } },
+ { { 0,0,0,0,0 }, /* : */
+ { 0,0,1,0,0 },
+ { 0,0,0,0,0 },
+ { 0,0,1,0,0 },
+ { 0,0,0,0,0 } },
+ { { 1,1,1,1,1 }, /* A */
+ { 1,0,0,0,1 },
+ { 1,1,1,1,1 },
+ { 1,0,0,0,1 },
+ { 1,0,0,0,1 } },
+ { { 1,1,1,1,1 }, /* P */
+ { 1,0,0,0,1 },
+ { 1,1,1,1,1 },
+ { 1,0,0,0,0 },
+ { 1,0,0,0,0 } },
+ { { 1,0,0,0,1 }, /* M */
+ { 1,1,0,1,1 },
+ { 1,0,1,0,1 },
+ { 1,0,0,0,1 },
+ { 1,0,0,0,1 } },
+};
+
+void
+clock_draw(struct screen_write_ctx *ctx, u_int colour, int style)
+{
+ struct screen *s = ctx->s;
+ struct grid_cell gc;
+ char tim[64], *ptr;
+ time_t t;
+ u_int i, j, x, y, idx;
+
+ t = time(NULL);
+ if (style == 0)
+ strftime(tim, sizeof tim, "%l:%M %p", localtime(&t));
+ else
+ strftime(tim, sizeof tim, "%H:%M", localtime(&t));
+
+ screen_write_clearscreen(ctx);
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ gc.fg = colour;
+
+ if (screen_size_x(s) < 6 * strlen(tim) || screen_size_y(s) < 6) {
+ if (screen_size_x(s) >= strlen(tim) && screen_size_y(s) != 0) {
+ x = (screen_size_x(s) / 2) - (strlen(tim) / 2);
+ y = screen_size_y(s) / 2;
+ screen_write_cursormove(ctx, x, y);
+
+ gc.fg = colour;
+ screen_write_puts(ctx, &gc, "%s", tim);
+ }
+ return;
+ }
+
+ x = (screen_size_x(s) / 2) - 3 * strlen(tim);
+ y = (screen_size_y(s) / 2) - 3;
+
+ for (ptr = tim; *ptr != '\0'; ptr++) {
+ if (*ptr >= '0' && *ptr <= '9')
+ idx = *ptr - '0';
+ else if (*ptr == ':')
+ idx = 10;
+ else if (*ptr == 'A')
+ idx = 11;
+ else if (*ptr == 'P')
+ idx = 12;
+ else if (*ptr == 'M')
+ idx = 13;
+ else {
+ x += 6;
+ continue;
+ }
+
+ for (j = 0; j < 5; j++) {
+ screen_write_cursormove(ctx, x, y + j);
+ for (i = 0; i < 5; i++) {
+ if (clock_table[idx][j][i])
+ gc.attr |= GRID_ATTR_REVERSE;
+ else
+ gc.attr &= ~GRID_ATTR_REVERSE;
+ screen_write_putc(ctx, &gc, ' ');
+ }
+ }
+ x += 6;
+ }
+}
diff --git a/usr.bin/tmux/cmd-attach-session.c b/usr.bin/tmux/cmd-attach-session.c
new file mode 100644
index 00000000000..4fdaae04cfb
--- /dev/null
+++ b/usr.bin/tmux/cmd-attach-session.c
@@ -0,0 +1,80 @@
+/* $OpenBSD: cmd-attach-session.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Attach existing session to the current terminal.
+ */
+
+int cmd_attach_session_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_attach_session_entry = {
+ "attach-session", "attach",
+ "[-d] " CMD_TARGET_SESSION_USAGE,
+ CMD_DFLAG|CMD_CANTNEST|CMD_STARTSERVER,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_attach_session_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct session *s;
+ char *cause;
+
+ if (ctx->curclient != NULL)
+ return (0);
+
+ if (ARRAY_LENGTH(&sessions) == 0) {
+ ctx->error(ctx, "no sessions");
+ return (-1);
+ }
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
+ if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) {
+ ctx->error(ctx, "not a terminal");
+ return (-1);
+ }
+
+ if (tty_open(&ctx->cmdclient->tty, &cause) != 0) {
+ ctx->error(ctx, "terminal open failed: %s", cause);
+ xfree(cause);
+ return (-1);
+ }
+
+ if (data->flags & CMD_DFLAG)
+ server_write_session(s, MSG_DETACH, NULL, 0);
+ ctx->cmdclient->session = s;
+
+ server_write_client(ctx->cmdclient, MSG_READY, NULL, 0);
+ recalculate_sizes();
+ server_redraw_client(ctx->cmdclient);
+
+ return (1);
+}
+
diff --git a/usr.bin/tmux/cmd-bind-key.c b/usr.bin/tmux/cmd-bind-key.c
new file mode 100644
index 00000000000..7db271c5bfc
--- /dev/null
+++ b/usr.bin/tmux/cmd-bind-key.c
@@ -0,0 +1,157 @@
+/* $OpenBSD: cmd-bind-key.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Bind a key to a command, this recurses through cmd_*.
+ */
+
+int cmd_bind_key_parse(struct cmd *, int, char **, char **);
+int cmd_bind_key_exec(struct cmd *, struct cmd_ctx *);
+void cmd_bind_key_send(struct cmd *, struct buffer *);
+void cmd_bind_key_recv(struct cmd *, struct buffer *);
+void cmd_bind_key_free(struct cmd *);
+size_t cmd_bind_key_print(struct cmd *, char *, size_t);
+
+struct cmd_bind_key_data {
+ int key;
+ int can_repeat;
+ struct cmd_list *cmdlist;
+};
+
+const struct cmd_entry cmd_bind_key_entry = {
+ "bind-key", "bind",
+ "[-r] key command [arguments]",
+ 0,
+ NULL,
+ cmd_bind_key_parse,
+ cmd_bind_key_exec,
+ cmd_bind_key_send,
+ cmd_bind_key_recv,
+ cmd_bind_key_free,
+ cmd_bind_key_print
+};
+
+int
+cmd_bind_key_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_bind_key_data *data;
+ int opt;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->can_repeat = 0;
+ data->cmdlist = NULL;
+
+ while ((opt = getopt(argc, argv, "r")) != -1) {
+ switch (opt) {
+ case 'r':
+ data->can_repeat = 1;
+ break;
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 1)
+ goto usage;
+
+ if ((data->key = key_string_lookup_string(argv[0])) == KEYC_NONE) {
+ xasprintf(cause, "unknown key: %s", argv[0]);
+ goto error;
+ }
+
+ argc--;
+ argv++;
+ if ((data->cmdlist = cmd_list_parse(argc, argv, cause)) == NULL)
+ goto error;
+
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+error:
+ self->entry->free(self);
+ return (-1);
+}
+
+int
+cmd_bind_key_exec(struct cmd *self, unused struct cmd_ctx *ctx)
+{
+ struct cmd_bind_key_data *data = self->data;
+
+ if (data == NULL)
+ return (0);
+
+ key_bindings_add(data->key, data->can_repeat, data->cmdlist);
+ data->cmdlist = NULL; /* avoid free */
+
+ return (0);
+}
+
+void
+cmd_bind_key_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_bind_key_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_list_send(data->cmdlist, b);
+}
+
+void
+cmd_bind_key_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_bind_key_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->cmdlist = cmd_list_recv(b);
+}
+
+void
+cmd_bind_key_free(struct cmd *self)
+{
+ struct cmd_bind_key_data *data = self->data;
+
+ if (data->cmdlist != NULL)
+ cmd_list_free(data->cmdlist);
+ xfree(data);
+}
+
+size_t
+cmd_bind_key_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_bind_key_data *data = self->data;
+ size_t off = 0;
+ const char *skey;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ if (off < len) {
+ skey = key_string_lookup_key(data->key);
+ off += xsnprintf(buf + off, len - off, " %s ", skey);
+ }
+ if (off < len)
+ off += cmd_list_print(data->cmdlist, buf + off, len - off);
+ return (off);
+}
diff --git a/usr.bin/tmux/cmd-break-pane.c b/usr.bin/tmux/cmd-break-pane.c
new file mode 100644
index 00000000000..d66a8ce372f
--- /dev/null
+++ b/usr.bin/tmux/cmd-break-pane.c
@@ -0,0 +1,92 @@
+/* $OpenBSD: cmd-break-pane.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Break pane off into a window.
+ */
+
+int cmd_break_pane_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_break_pane_entry = {
+ "break-pane", "breakp",
+ CMD_PANE_WINDOW_USAGE " [-d]",
+ CMD_DFLAG,
+ cmd_pane_init,
+ cmd_pane_parse,
+ cmd_break_pane_exec,
+ cmd_pane_send,
+ cmd_pane_recv,
+ cmd_pane_free,
+ cmd_pane_print
+};
+
+int
+cmd_break_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_pane_data *data = self->data;
+ struct winlink *wl;
+ struct session *s;
+ struct window_pane *wp;
+ struct window *w;
+ char *cause;
+
+ if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL)
+ return (-1);
+ if (data->pane == -1)
+ wp = wl->window->active;
+ else {
+ wp = window_pane_at_index(wl->window, data->pane);
+ if (wp == NULL) {
+ ctx->error(ctx, "no pane: %d", data->pane);
+ return (-1);
+ }
+ }
+
+ if (window_count_panes(wl->window) == 1) {
+ ctx->error(ctx, "can't break pane: %d", data->pane);
+ return (-1);
+ }
+
+ TAILQ_REMOVE(&wl->window->panes, wp, entry);
+ if (wl->window->active == wp) {
+ wl->window->active = TAILQ_PREV(wp, window_panes, entry);
+ if (wl->window->active == NULL)
+ wl->window->active = TAILQ_NEXT(wp, entry);
+ }
+ layout_refresh(wl->window, 0);
+
+ w = wp->window = window_create1(s->sx, s->sy);
+ TAILQ_INSERT_HEAD(&w->panes, wp, entry);
+ w->active = wp;
+ w->name = default_window_name(w);
+
+ wl = session_attach(s, w, -1, &cause); /* can't fail */
+ if (!(data->flags & CMD_DFLAG))
+ session_select(s, wl->idx);
+ layout_refresh(w, 0);
+
+ server_redraw_session(s);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-choose-session.c b/usr.bin/tmux/cmd-choose-session.c
new file mode 100644
index 00000000000..c827ddb207e
--- /dev/null
+++ b/usr.bin/tmux/cmd-choose-session.c
@@ -0,0 +1,107 @@
+/* $OpenBSD: cmd-choose-session.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Enter choice mode to choose a session.
+ */
+
+int cmd_choose_session_exec(struct cmd *, struct cmd_ctx *);
+
+void cmd_choose_session_callback(void *, int);
+
+const struct cmd_entry cmd_choose_session_entry = {
+ "choose-session", NULL,
+ CMD_TARGET_WINDOW_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_choose_session_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+struct cmd_choose_session_data {
+ u_int client;
+};
+
+int
+cmd_choose_session_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct cmd_choose_session_data *cdata;
+ struct winlink *wl;
+ struct session *s;
+ u_int i, idx, cur;
+
+ if (ctx->curclient == NULL) {
+ ctx->error(ctx, "must be run interactively");
+ return (-1);
+ }
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+
+ if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
+ return (0);
+
+ cur = idx = 0;
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ s = ARRAY_ITEM(&sessions, i);
+ if (s == NULL)
+ continue;
+ if (s == ctx->curclient->session)
+ cur = idx;
+ idx++;
+
+ window_choose_add(wl->window->active, i,
+ "%s: %u windows [%ux%u]%s", s->name,
+ winlink_count(&s->windows), s->sx, s->sy,
+ s->flags & SESSION_UNATTACHED ? "" : " (attached)");
+ }
+
+ cdata = xmalloc(sizeof *cdata);
+ cdata->client = server_client_index(ctx->curclient);
+
+ window_choose_ready(
+ wl->window->active, cur, cmd_choose_session_callback, cdata);
+
+ return (0);
+}
+
+void
+cmd_choose_session_callback(void *data, int idx)
+{
+ struct cmd_choose_session_data *cdata = data;
+ struct client *c;
+
+ if (idx != -1 && cdata->client <= ARRAY_LENGTH(&clients) - 1) {
+ c = ARRAY_ITEM(&clients, cdata->client);
+ if (c != NULL && (u_int) idx <= ARRAY_LENGTH(&sessions) - 1) {
+ c->session = ARRAY_ITEM(&sessions, idx);
+ recalculate_sizes();
+ server_redraw_client(c);
+ }
+ }
+ xfree(cdata);
+}
diff --git a/usr.bin/tmux/cmd-choose-window.c b/usr.bin/tmux/cmd-choose-window.c
new file mode 100644
index 00000000000..100d0b57253
--- /dev/null
+++ b/usr.bin/tmux/cmd-choose-window.c
@@ -0,0 +1,106 @@
+/* $OpenBSD: cmd-choose-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Enter choice mode to choose a window.
+ */
+
+int cmd_choose_window_exec(struct cmd *, struct cmd_ctx *);
+
+void cmd_choose_window_callback(void *, int);
+
+const struct cmd_entry cmd_choose_window_entry = {
+ "choose-window", NULL,
+ CMD_TARGET_WINDOW_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_choose_window_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+struct cmd_choose_window_data {
+ u_int session;
+};
+
+int
+cmd_choose_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct cmd_choose_window_data *cdata;
+ struct session *s;
+ struct winlink *wl, *wm;
+ struct window *w;
+ u_int idx, cur;
+
+ if (ctx->curclient == NULL) {
+ ctx->error(ctx, "must be run interactively");
+ return (-1);
+ }
+ s = ctx->curclient->session;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+
+ if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
+ return (0);
+
+ cur = idx = 0;
+ RB_FOREACH(wm, winlinks, &s->windows) {
+ w = wm->window;
+
+ if (wm == s->curw)
+ cur = idx;
+ idx++;
+
+ window_choose_add(wl->window->active,
+ wm->idx, "%3d: %s [%ux%u %s] (%u panes)", wm->idx, w->name,
+ w->sx, w->sy, layout_name(w), window_count_panes(w));
+ }
+
+ cdata = xmalloc(sizeof *cdata);
+ if (session_index(s, &cdata->session) != 0)
+ fatalx("session not found");
+
+ window_choose_ready(
+ wl->window->active, cur, cmd_choose_window_callback, cdata);
+
+ return (0);
+}
+
+void
+cmd_choose_window_callback(void *data, int idx)
+{
+ struct cmd_choose_window_data *cdata = data;
+ struct session *s;
+
+ if (idx != -1 && cdata->session <= ARRAY_LENGTH(&sessions) - 1) {
+ s = ARRAY_ITEM(&sessions, cdata->session);
+ if (s != NULL && session_select(s, idx) == 0)
+ server_redraw_session(s);
+ recalculate_sizes();
+ }
+ xfree(cdata);
+}
diff --git a/usr.bin/tmux/cmd-clear-history.c b/usr.bin/tmux/cmd-clear-history.c
new file mode 100644
index 00000000000..a15afa8388d
--- /dev/null
+++ b/usr.bin/tmux/cmd-clear-history.c
@@ -0,0 +1,68 @@
+/* $OpenBSD: cmd-clear-history.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Clear pane history.
+ */
+
+void cmd_clear_history_init(struct cmd *, int);
+int cmd_clear_history_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_clear_history_entry = {
+ "clear-history", "clearhist",
+ CMD_PANE_WINDOW_USAGE,
+ 0,
+ cmd_pane_init,
+ cmd_pane_parse,
+ cmd_clear_history_exec,
+ cmd_pane_send,
+ cmd_pane_recv,
+ cmd_pane_free,
+ cmd_pane_print
+};
+
+int
+cmd_clear_history_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_pane_data *data = self->data;
+ struct winlink *wl;
+ struct window_pane *wp;
+ struct grid *gd;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+ if (data->pane == -1)
+ wp = wl->window->active;
+ else {
+ wp = window_pane_at_index(wl->window, data->pane);
+ if (wp == NULL) {
+ ctx->error(ctx, "no pane: %d", data->pane);
+ return (-1);
+ }
+ }
+ gd = wp->base.grid;
+
+ grid_move_lines(gd, 0, gd->hsize, gd->sy);
+ gd->hsize = 0;
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-clock-mode.c b/usr.bin/tmux/cmd-clock-mode.c
new file mode 100644
index 00000000000..331ec464d82
--- /dev/null
+++ b/usr.bin/tmux/cmd-clock-mode.c
@@ -0,0 +1,54 @@
+/* $OpenBSD: cmd-clock-mode.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Enter clock mode.
+ */
+
+int cmd_clock_mode_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_clock_mode_entry = {
+ "clock-mode", NULL,
+ CMD_TARGET_WINDOW_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_clock_mode_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_clock_mode_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct winlink *wl;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+
+ window_pane_set_mode(wl->window->active, &window_clock_mode);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-command-prompt.c b/usr.bin/tmux/cmd-command-prompt.c
new file mode 100644
index 00000000000..82354ef463a
--- /dev/null
+++ b/usr.bin/tmux/cmd-command-prompt.c
@@ -0,0 +1,178 @@
+/* $OpenBSD: cmd-command-prompt.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <ctype.h>
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Prompt for command in client.
+ */
+
+void cmd_command_prompt_init(struct cmd *, int);
+int cmd_command_prompt_exec(struct cmd *, struct cmd_ctx *);
+
+int cmd_command_prompt_callback(void *, const char *);
+
+const struct cmd_entry cmd_command_prompt_entry = {
+ "command-prompt", NULL,
+ CMD_TARGET_CLIENT_USAGE " [template]",
+ CMD_ARG01,
+ cmd_command_prompt_init,
+ cmd_target_parse,
+ cmd_command_prompt_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+struct cmd_command_prompt_data {
+ struct client *c;
+ char *template;
+};
+
+void
+cmd_command_prompt_init(struct cmd *self, int key)
+{
+ struct cmd_target_data *data;
+
+ cmd_target_init(self, key);
+ data = self->data;
+
+ switch (key) {
+ case ',':
+ data->arg = xstrdup("rename-window '%%'");
+ break;
+ case '.':
+ data->arg = xstrdup("move-window -t '%%'");
+ break;
+ case 'f':
+ data->arg = xstrdup("find-window '%%'");
+ break;
+ }
+}
+
+int
+cmd_command_prompt_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct cmd_command_prompt_data *cdata;
+ struct client *c;
+ char *hdr, *ptr;
+
+ if ((c = cmd_find_client(ctx, data->target)) == NULL)
+ return (-1);
+
+ if (c->prompt_string != NULL)
+ return (0);
+
+ cdata = xmalloc(sizeof *cdata);
+ cdata->c = c;
+ if (data->arg != NULL) {
+ cdata->template = xstrdup(data->arg);
+ if ((ptr = strchr(data->arg, ' ')) == NULL)
+ ptr = strchr(data->arg, '\0');
+ xasprintf(&hdr, "(%.*s) ", (int) (ptr - data->arg), data->arg);
+ } else {
+ cdata->template = NULL;
+ hdr = xstrdup(":");
+ }
+ status_prompt_set(c, hdr, cmd_command_prompt_callback, cdata, 0);
+ xfree(hdr);
+
+ return (0);
+}
+
+int
+cmd_command_prompt_callback(void *data, const char *s)
+{
+ struct cmd_command_prompt_data *cdata = data;
+ struct client *c = cdata->c;
+ struct cmd_list *cmdlist;
+ struct cmd_ctx ctx;
+ char *cause, *ptr, *buf, ch;
+ size_t len, slen;
+
+ if (s == NULL) {
+ xfree(cdata);
+ return (0);
+ }
+ slen = strlen(s);
+
+ len = 0;
+ buf = NULL;
+ if (cdata->template != NULL) {
+ ptr = cdata->template;
+ while (*ptr != '\0') {
+ switch (ch = *ptr++) {
+ case '%':
+ if (*ptr != '%')
+ break;
+ ptr++;
+
+ buf = xrealloc(buf, 1, len + slen + 1);
+ memcpy(buf + len, s, slen);
+ len += slen;
+ break;
+ default:
+ buf = xrealloc(buf, 1, len + 2);
+ buf[len++] = ch;
+ break;
+ }
+ }
+ xfree(cdata->template);
+
+ buf[len] = '\0';
+ s = buf;
+ }
+ xfree(cdata);
+
+ if (cmd_string_parse(s, &cmdlist, &cause) != 0) {
+ if (cause == NULL)
+ return (0);
+ *cause = toupper((u_char) *cause);
+ status_message_set(c, cause);
+ xfree(cause);
+ cmdlist = NULL;
+ }
+ if (buf != NULL)
+ xfree(buf);
+ if (cmdlist == NULL)
+ return (0);
+
+ ctx.msgdata = NULL;
+ ctx.cursession = c->session;
+ ctx.curclient = c;
+
+ ctx.error = key_bindings_error;
+ ctx.print = key_bindings_print;
+ ctx.info = key_bindings_info;
+
+ ctx.cmdclient = NULL;
+
+ cmd_list_exec(cmdlist, &ctx);
+ cmd_list_free(cmdlist);
+
+ if (c->prompt_callback != (void *) &cmd_command_prompt_callback)
+ return (1);
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-confirm-before.c b/usr.bin/tmux/cmd-confirm-before.c
new file mode 100644
index 00000000000..4b3b8cab3a2
--- /dev/null
+++ b/usr.bin/tmux/cmd-confirm-before.c
@@ -0,0 +1,141 @@
+/* $OpenBSD: cmd-confirm-before.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
+ *
+ * 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 <ctype.h>
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Asks for confirmation before executing a command.
+ */
+
+int cmd_confirm_before_exec(struct cmd *, struct cmd_ctx *);
+void cmd_confirm_before_init(struct cmd *, int);
+
+int cmd_confirm_before_callback(void *, const char *);
+
+struct cmd_confirm_before_data {
+ struct client *c;
+ char *cmd;
+};
+
+const struct cmd_entry cmd_confirm_before_entry = {
+ "confirm-before", "confirm",
+ CMD_TARGET_CLIENT_USAGE " command",
+ CMD_ARG1,
+ cmd_confirm_before_init,
+ cmd_target_parse,
+ cmd_confirm_before_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+void
+cmd_confirm_before_init(struct cmd *self, int key)
+{
+ struct cmd_target_data *data;
+
+ cmd_target_init(self, key);
+ data = self->data;
+
+ switch (key) {
+ case '&':
+ data->arg = xstrdup("kill-window");
+ break;
+ case 'x':
+ data->arg = xstrdup("kill-pane");
+ break;
+ }
+}
+
+int
+cmd_confirm_before_exec(unused struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct cmd_confirm_before_data *cdata;
+ struct client *c;
+ char *buf, *cmd, *ptr;
+
+ if (ctx->curclient == NULL) {
+ ctx->error(ctx, "must be run interactively");
+ return (-1);
+ }
+
+ if ((c = cmd_find_client(ctx, data->target)) == NULL)
+ return (-1);
+
+ ptr = xstrdup(data->arg);
+ if ((cmd = strtok(ptr, " \t")) == NULL)
+ cmd = ptr;
+ xasprintf(&buf, "Confirm '%s'? (y/n) ", cmd);
+ xfree(ptr);
+
+ cdata = xmalloc(sizeof *cdata);
+ cdata->cmd = xstrdup(data->arg);
+ cdata->c = c;
+ status_prompt_set(
+ cdata->c, buf, cmd_confirm_before_callback, cdata, PROMPT_SINGLE);
+
+ xfree(buf);
+ return (1);
+}
+
+int
+cmd_confirm_before_callback(void *data, const char *s)
+{
+ struct cmd_confirm_before_data *cdata = data;
+ struct client *c = cdata->c;
+ struct cmd_list *cmdlist;
+ struct cmd_ctx ctx;
+ char *cause;
+
+ if (s == NULL || tolower((u_char) s[0]) != 'y' || s[1] != '\0')
+ goto out;
+
+ if (cmd_string_parse(cdata->cmd, &cmdlist, &cause) != 0) {
+ if (cause != NULL) {
+ *cause = toupper((u_char) *cause);
+ status_message_set(c, cause);
+ xfree(cause);
+ }
+ goto out;
+ }
+
+ ctx.msgdata = NULL;
+ ctx.cursession = c->session;
+ ctx.curclient = c;
+
+ ctx.error = key_bindings_error;
+ ctx.print = key_bindings_print;
+ ctx.info = key_bindings_info;
+
+ ctx.cmdclient = NULL;
+
+ cmd_list_exec(cmdlist, &ctx);
+ cmd_list_free(cmdlist);
+
+out:
+ if (cdata->cmd != NULL)
+ xfree(cdata->cmd);
+ xfree(cdata);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-copy-buffer.c b/usr.bin/tmux/cmd-copy-buffer.c
new file mode 100644
index 00000000000..f8381fc9396
--- /dev/null
+++ b/usr.bin/tmux/cmd-copy-buffer.c
@@ -0,0 +1,222 @@
+/* $OpenBSD: cmd-copy-buffer.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Copies a session paste buffer to another session.
+ */
+
+int cmd_copy_buffer_parse(struct cmd *, int, char **, char **);
+int cmd_copy_buffer_exec(struct cmd *, struct cmd_ctx *);
+void cmd_copy_buffer_send(struct cmd *, struct buffer *);
+void cmd_copy_buffer_recv(struct cmd *, struct buffer *);
+void cmd_copy_buffer_free(struct cmd *);
+void cmd_copy_buffer_init(struct cmd *, int);
+size_t cmd_copy_buffer_print(struct cmd *, char *, size_t);
+
+struct cmd_copy_buffer_data {
+ char *dst_session;
+ char *src_session;
+ int dst_idx;
+ int src_idx;
+};
+
+const struct cmd_entry cmd_copy_buffer_entry = {
+ "copy-buffer", "copyb",
+ "[-a src-index] [-b dst-index] [-s src-session] [-t dst-session]",
+ 0,
+ cmd_copy_buffer_init,
+ cmd_copy_buffer_parse,
+ cmd_copy_buffer_exec,
+ cmd_copy_buffer_send,
+ cmd_copy_buffer_recv,
+ cmd_copy_buffer_free,
+ cmd_copy_buffer_print
+};
+
+void
+cmd_copy_buffer_init(struct cmd *self, unused int arg)
+{
+ struct cmd_copy_buffer_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->dst_session = NULL;
+ data->src_session = NULL;
+ data->dst_idx = -1;
+ data->src_idx = -1;
+}
+
+int
+cmd_copy_buffer_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_copy_buffer_data *data;
+ const char *errstr;
+ int n, opt;
+
+ self->entry->init(self, 0);
+ data = self->data;
+
+ while ((opt = getopt(argc, argv, "a:b:s:t:")) != -1) {
+ switch (opt) {
+ case 'a':
+ if (data->src_idx == -1) {
+ n = strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr != NULL) {
+ xasprintf(cause, "buffer %s", errstr);
+ goto error;
+ }
+ data->src_idx = n;
+ }
+ break;
+ case 'b':
+ if (data->dst_idx == -1) {
+ n = strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr != NULL) {
+ xasprintf(cause, "buffer %s", errstr);
+ goto error;
+ }
+ data->dst_idx = n;
+ }
+ break;
+ case 's':
+ if (data->src_session == NULL)
+ data->src_session = xstrdup(optarg);
+ break;
+ case 't':
+ if (data->dst_session == NULL)
+ data->dst_session = xstrdup(optarg);
+ break;
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+error:
+ self->entry->free(self);
+ return (-1);
+}
+
+int
+cmd_copy_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_copy_buffer_data *data = self->data;
+ struct paste_buffer *pb;
+ struct session *dst_session, *src_session;
+ u_int limit;
+
+ if ((dst_session = cmd_find_session(ctx, data->dst_session)) == NULL ||
+ (src_session = cmd_find_session(ctx, data->src_session)) == NULL)
+ return (-1);
+
+ if (data->src_idx == -1) {
+ if ((pb = paste_get_top(&src_session->buffers)) == NULL) {
+ ctx->error(ctx, "no buffers");
+ return (-1);
+ }
+ } else {
+ if ((pb = paste_get_index(&src_session->buffers,
+ data->src_idx)) == NULL) {
+ ctx->error(ctx, "no buffer %d", data->src_idx);
+ return (-1);
+ }
+ }
+
+ limit = options_get_number(&dst_session->options, "buffer-limit");
+ if (data->dst_idx == -1) {
+ paste_add(&dst_session->buffers, xstrdup(pb->data), limit);
+ return (0);
+ }
+ if (paste_replace(&dst_session->buffers, data->dst_idx,
+ xstrdup(pb->data)) != 0) {
+ ctx->error(ctx, "no buffer %d", data->dst_idx);
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+cmd_copy_buffer_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_copy_buffer_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_send_string(b, data->dst_session);
+ cmd_send_string(b, data->src_session);
+}
+
+void
+cmd_copy_buffer_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_copy_buffer_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->dst_session = cmd_recv_string(b);
+ data->src_session = cmd_recv_string(b);
+}
+
+void
+cmd_copy_buffer_free(struct cmd *self)
+{
+ struct cmd_copy_buffer_data *data = self->data;
+
+ if (data->dst_session != NULL)
+ xfree(data->dst_session);
+ if (data->src_session != NULL)
+ xfree(data->src_session);
+ xfree(data);
+}
+
+size_t
+cmd_copy_buffer_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_copy_buffer_data *data = self->data;
+ size_t off = 0;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ if (off < len && data->src_idx != -1) {
+ off += xsnprintf(buf + off, len - off, " -a %d",
+ data->src_idx);
+ }
+ if (off < len && data->dst_idx != -1) {
+ off += xsnprintf(buf + off, len - off, " -b %d",
+ data->dst_idx);
+ }
+ if (off < len && data->src_session != NULL) {
+ off += cmd_prarg(buf + off, len - off, " -s ",
+ data->src_session);
+ }
+ if (off < len && data->dst_session != NULL) {
+ off += cmd_prarg(buf + off, len - off, " -t ",
+ data->dst_session);
+ }
+ return (off);
+}
diff --git a/usr.bin/tmux/cmd-copy-mode.c b/usr.bin/tmux/cmd-copy-mode.c
new file mode 100644
index 00000000000..f1121d5ac86
--- /dev/null
+++ b/usr.bin/tmux/cmd-copy-mode.c
@@ -0,0 +1,56 @@
+/* $OpenBSD: cmd-copy-mode.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Enter copy mode.
+ */
+
+int cmd_copy_mode_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_copy_mode_entry = {
+ "copy-mode", NULL,
+ CMD_TARGET_WINDOW_USAGE,
+ CMD_UFLAG,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_copy_mode_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ NULL
+};
+
+int
+cmd_copy_mode_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct winlink *wl;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+
+ window_pane_set_mode(wl->window->active, &window_copy_mode);
+ if (data->flags & CMD_UFLAG)
+ window_copy_pageup(wl->window->active);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-delete-buffer.c b/usr.bin/tmux/cmd-delete-buffer.c
new file mode 100644
index 00000000000..223c5e42d8b
--- /dev/null
+++ b/usr.bin/tmux/cmd-delete-buffer.c
@@ -0,0 +1,61 @@
+/* $OpenBSD: cmd-delete-buffer.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Delete a paste buffer.
+ */
+
+int cmd_delete_buffer_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_delete_buffer_entry = {
+ "delete-buffer", "deleteb",
+ CMD_BUFFER_SESSION_USAGE,
+ 0,
+ cmd_buffer_init,
+ cmd_buffer_parse,
+ cmd_delete_buffer_exec,
+ cmd_buffer_send,
+ cmd_buffer_recv,
+ cmd_buffer_free,
+ cmd_buffer_print
+};
+
+int
+cmd_delete_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_buffer_data *data = self->data;
+ struct session *s;
+
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
+ if (data->buffer == -1)
+ paste_free_top(&s->buffers);
+ else if (paste_free_index(&s->buffers, data->buffer) != 0) {
+ ctx->error(ctx, "no buffer %d", data->buffer);
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-detach-client.c b/usr.bin/tmux/cmd-detach-client.c
new file mode 100644
index 00000000000..6344671f863
--- /dev/null
+++ b/usr.bin/tmux/cmd-detach-client.c
@@ -0,0 +1,54 @@
+/* $OpenBSD: cmd-detach-client.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Detach a client.
+ */
+
+int cmd_detach_client_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_detach_client_entry = {
+ "detach-client", "detach",
+ CMD_TARGET_CLIENT_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_detach_client_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_detach_client_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct client *c;
+
+ if ((c = cmd_find_client(ctx, data->target)) == NULL)
+ return (-1);
+
+ server_write_client(c, MSG_DETACH, NULL, 0);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-down-pane.c b/usr.bin/tmux/cmd-down-pane.c
new file mode 100644
index 00000000000..86bd41eb949
--- /dev/null
+++ b/usr.bin/tmux/cmd-down-pane.c
@@ -0,0 +1,61 @@
+/* $OpenBSD: cmd-down-pane.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Move down a pane.
+ */
+
+int cmd_down_pane_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_down_pane_entry = {
+ "down-pane", "downp",
+ CMD_TARGET_WINDOW_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_down_pane_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_down_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct winlink *wl;
+ struct window *w;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+ w = wl->window;
+
+ do {
+ w->active = TAILQ_NEXT(w->active, entry);
+ if (w->active == NULL)
+ w->active = TAILQ_FIRST(&w->panes);
+ layout_refresh(w, 1);
+ } while (w->active->flags & PANE_HIDDEN);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-find-window.c b/usr.bin/tmux/cmd-find-window.c
new file mode 100644
index 00000000000..772892c114d
--- /dev/null
+++ b/usr.bin/tmux/cmd-find-window.c
@@ -0,0 +1,160 @@
+/* $OpenBSD: cmd-find-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+/*
+ * Find window containing text.
+ */
+
+int cmd_find_window_exec(struct cmd *, struct cmd_ctx *);
+
+void cmd_find_window_callback(void *, int);
+
+const struct cmd_entry cmd_find_window_entry = {
+ "find-window", "findw",
+ CMD_TARGET_WINDOW_USAGE " match-string",
+ CMD_ARG1,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_find_window_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+struct cmd_find_window_data {
+ u_int session;
+};
+
+int
+cmd_find_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct cmd_find_window_data *cdata;
+ struct session *s;
+ struct winlink *wl, *wm;
+ struct window *w;
+ struct window_pane *wp;
+ ARRAY_DECL(, u_int) list_idx;
+ ARRAY_DECL(, char *) list_ctx;
+ char *sres, *sctx;
+ u_int i;
+
+ if (ctx->curclient == NULL) {
+ ctx->error(ctx, "must be run interactively");
+ return (-1);
+ }
+ s = ctx->curclient->session;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+
+ ARRAY_INIT(&list_idx);
+ ARRAY_INIT(&list_ctx);
+
+ RB_FOREACH(wm, winlinks, &s->windows) {
+ i = 0;
+ TAILQ_FOREACH(wp, &wm->window->panes, entry) {
+ i++;
+
+ if (strstr(wm->window->name, data->arg) != NULL)
+ sctx = xstrdup("");
+ else {
+ sres = window_pane_search(wp, data->arg);
+ if (sres == NULL &&
+ strstr(wp->base.title, data->arg) == NULL)
+ continue;
+
+ if (sres == NULL) {
+ xasprintf(&sctx,
+ "pane %u title: \"%s\"", i - 1,
+ wp->base.title);
+ } else {
+ xasprintf(&sctx, "\"%s\"", sres);
+ xfree(sres);
+ }
+ }
+
+ ARRAY_ADD(&list_idx, wm->idx);
+ ARRAY_ADD(&list_ctx, sctx);
+ }
+ }
+
+ if (ARRAY_LENGTH(&list_idx) == 0) {
+ ctx->error(ctx, "no windows matching: %s", data->arg);
+ ARRAY_FREE(&list_idx);
+ ARRAY_FREE(&list_ctx);
+ return (-1);
+ }
+
+ if (ARRAY_LENGTH(&list_idx) == 1) {
+ if (session_select(s, ARRAY_FIRST(&list_idx)) == 0)
+ server_redraw_session(s);
+ recalculate_sizes();
+ goto out;
+ }
+
+ if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
+ goto out;
+
+ for (i = 0; i < ARRAY_LENGTH(&list_idx); i++) {
+ wm = winlink_find_by_index(
+ &s->windows, ARRAY_ITEM(&list_idx, i));
+ w = wm->window;
+
+ sctx = ARRAY_ITEM(&list_ctx, i);
+ window_choose_add(wl->window->active,
+ wm->idx, "%3d: %s [%ux%u] (%u panes) %s", wm->idx, w->name,
+ w->sx, w->sy, window_count_panes(w), sctx);
+ xfree(sctx);
+ }
+
+ cdata = xmalloc(sizeof *cdata);
+ if (session_index(s, &cdata->session) != 0)
+ fatalx("session not found");
+
+ window_choose_ready(
+ wl->window->active, 0, cmd_find_window_callback, cdata);
+
+out:
+ ARRAY_FREE(&list_idx);
+ ARRAY_FREE(&list_ctx);
+
+ return (0);
+}
+
+void
+cmd_find_window_callback(void *data, int idx)
+{
+ struct cmd_find_window_data *cdata = data;
+ struct session *s;
+
+ if (idx != -1 && cdata->session <= ARRAY_LENGTH(&sessions) - 1) {
+ s = ARRAY_ITEM(&sessions, cdata->session);
+ if (s != NULL && session_select(s, idx) == 0)
+ server_redraw_session(s);
+ recalculate_sizes();
+ }
+ xfree(cdata);
+}
diff --git a/usr.bin/tmux/cmd-generic.c b/usr.bin/tmux/cmd-generic.c
new file mode 100644
index 00000000000..5518588b0e4
--- /dev/null
+++ b/usr.bin/tmux/cmd-generic.c
@@ -0,0 +1,694 @@
+/* $OpenBSD: cmd-generic.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+#define CMD_FLAGS "adDgkuU"
+#define CMD_FLAGMASK (CMD_AFLAG|CMD_DFLAG|CMD_BIGDFLAG|CMD_GFLAG|CMD_KFLAG| \
+ CMD_UFLAG|CMD_BIGUFLAG)
+
+int cmd_do_flags(int, int, int *);
+size_t cmd_print_flags(char *, size_t, size_t, int);
+int cmd_fill_argument(int, char **, int, char **);
+
+size_t
+cmd_prarg(char *buf, size_t len, const char *prefix, char *arg)
+{
+ if (strchr(arg, ' ') != NULL)
+ return (xsnprintf(buf, len, "%s\"%s\"", prefix, arg));
+ return (xsnprintf(buf, len, "%s%s", prefix, arg));
+}
+
+int
+cmd_do_flags(int opt, int iflags, int *oflags)
+{
+ switch (opt) {
+ case 'a':
+ if (iflags & CMD_AFLAG) {
+ (*oflags) |= CMD_AFLAG;
+ return (0);
+ }
+ return (-1);
+ case 'd':
+ if (iflags & CMD_DFLAG) {
+ (*oflags) |= CMD_DFLAG;
+ return (0);
+ }
+ return (-1);
+ case 'D':
+ if (iflags & CMD_BIGDFLAG) {
+ (*oflags) |= CMD_BIGDFLAG;
+ return (0);
+ }
+ return (-1);
+ case 'g':
+ if (iflags & CMD_GFLAG) {
+ (*oflags) |= CMD_GFLAG;
+ return (0);
+ }
+ return (-1);
+ case 'k':
+ if (iflags & CMD_KFLAG) {
+ (*oflags) |= CMD_KFLAG;
+ return (0);
+ }
+ return (-1);
+ case 'u':
+ if (iflags & CMD_UFLAG) {
+ (*oflags) |= CMD_UFLAG;
+ return (0);
+ }
+ return (-1);
+ case 'U':
+ if (iflags & CMD_BIGUFLAG) {
+ (*oflags) |= CMD_BIGUFLAG;
+ return (0);
+ }
+ return (-1);
+ }
+ return (1);
+}
+
+size_t
+cmd_print_flags(char *buf, size_t len, size_t off, int flags)
+{
+ size_t boff = off;
+
+ if ((flags & CMD_FLAGMASK) == 0)
+ return (0);
+ off += xsnprintf(buf + off, len - off, " -");
+ if (off < len && flags & CMD_AFLAG)
+ off += xsnprintf(buf + off, len - off, "a");
+ if (off < len && flags & CMD_BIGDFLAG)
+ off += xsnprintf(buf + off, len - off, "D");
+ if (off < len && flags & CMD_DFLAG)
+ off += xsnprintf(buf + off, len - off, "d");
+ if (off < len && flags & CMD_GFLAG)
+ off += xsnprintf(buf + off, len - off, "g");
+ if (off < len && flags & CMD_KFLAG)
+ off += xsnprintf(buf + off, len - off, "k");
+ if (off < len && flags & CMD_UFLAG)
+ off += xsnprintf(buf + off, len - off, "u");
+ if (off < len && flags & CMD_BIGUFLAG)
+ off += xsnprintf(buf + off, len - off, "U");
+ return (off - boff);
+}
+
+int
+cmd_fill_argument(int flags, char **arg, int argc, char **argv)
+{
+ *arg = NULL;
+
+ if (flags & CMD_ARG1) {
+ if (argc != 1)
+ return (-1);
+ *arg = xstrdup(argv[0]);
+ return (0);
+ }
+
+ if (flags & CMD_ARG01) {
+ if (argc != 0 && argc != 1)
+ return (-1);
+ if (argc == 1)
+ *arg = xstrdup(argv[0]);
+ return (0);
+ }
+
+ if (argc != 0)
+ return (-1);
+ return (0);
+}
+
+void
+cmd_target_init(struct cmd *self, unused int key)
+{
+ struct cmd_target_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->flags = 0;
+ data->target = NULL;
+ data->arg = NULL;
+}
+
+int
+cmd_target_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_target_data *data;
+ int opt;
+
+ /* Don't use the entry version since it may be dependent on key. */
+ cmd_target_init(self, 0);
+ data = self->data;
+
+ while ((opt = getopt(argc, argv, CMD_FLAGS "t:")) != -1) {
+ switch (cmd_do_flags(opt, self->entry->flags, &data->flags)) {
+ case -1:
+ goto usage;
+ case 0:
+ continue;
+ }
+ switch (opt) {
+ case 't':
+ if (data->target == NULL)
+ data->target = xstrdup(optarg);
+ break;
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (cmd_fill_argument(self->entry->flags, &data->arg, argc, argv) != 0)
+ goto usage;
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+ self->entry->free(self);
+ return (-1);
+}
+
+void
+cmd_target_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_target_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_send_string(b, data->target);
+ cmd_send_string(b, data->arg);
+}
+
+void
+cmd_target_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_target_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->target = cmd_recv_string(b);
+ data->arg = cmd_recv_string(b);
+}
+
+void
+cmd_target_free(struct cmd *self)
+{
+ struct cmd_target_data *data = self->data;
+
+ if (data->target != NULL)
+ xfree(data->target);
+ if (data->arg != NULL)
+ xfree(data->arg);
+ xfree(data);
+}
+
+size_t
+cmd_target_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_target_data *data = self->data;
+ size_t off = 0;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ off += cmd_print_flags(buf, len, off, data->flags);
+ if (off < len && data->target != NULL)
+ off += cmd_prarg(buf + off, len - off, " -t ", data->target);
+ if (off < len && data->arg != NULL)
+ off += cmd_prarg(buf + off, len - off, " ", data->arg);
+ return (off);
+}
+
+void
+cmd_srcdst_init(struct cmd *self, unused int key)
+{
+ struct cmd_srcdst_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->flags = 0;
+ data->src = NULL;
+ data->dst = NULL;
+ data->arg = NULL;
+}
+
+int
+cmd_srcdst_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_srcdst_data *data;
+ int opt;
+
+ cmd_srcdst_init(self, 0);
+ data = self->data;
+
+ while ((opt = getopt(argc, argv, CMD_FLAGS "s:t:")) != -1) {
+ switch (cmd_do_flags(opt, self->entry->flags, &data->flags)) {
+ case -1:
+ goto usage;
+ case 0:
+ continue;
+ }
+ switch (opt) {
+ case 's':
+ if (data->src == NULL)
+ data->src = xstrdup(optarg);
+ break;
+ case 't':
+ if (data->dst == NULL)
+ data->dst = xstrdup(optarg);
+ break;
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (cmd_fill_argument(self->entry->flags, &data->arg, argc, argv) != 0)
+ goto usage;
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+ self->entry->free(self);
+ return (-1);
+}
+
+void
+cmd_srcdst_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_srcdst_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_send_string(b, data->src);
+ cmd_send_string(b, data->dst);
+ cmd_send_string(b, data->arg);
+}
+
+void
+cmd_srcdst_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_srcdst_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->src = cmd_recv_string(b);
+ data->dst = cmd_recv_string(b);
+ data->arg = cmd_recv_string(b);
+}
+
+void
+cmd_srcdst_free(struct cmd *self)
+{
+ struct cmd_srcdst_data *data = self->data;
+
+ if (data->src != NULL)
+ xfree(data->src);
+ if (data->dst != NULL)
+ xfree(data->dst);
+ if (data->arg != NULL)
+ xfree(data->arg);
+ xfree(data);
+}
+
+size_t
+cmd_srcdst_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_srcdst_data *data = self->data;
+ size_t off = 0;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ off += cmd_print_flags(buf, len, off, data->flags);
+ if (off < len && data->src != NULL)
+ off += xsnprintf(buf + off, len - off, " -s %s", data->src);
+ if (off < len && data->dst != NULL)
+ off += xsnprintf(buf + off, len - off, " -t %s", data->dst);
+ if (off < len && data->arg != NULL)
+ off += cmd_prarg(buf + off, len - off, " ", data->arg);
+ return (off);
+}
+
+void
+cmd_buffer_init(struct cmd *self, unused int key)
+{
+ struct cmd_buffer_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->flags = 0;
+ data->target = NULL;
+ data->buffer = -1;
+ data->arg = NULL;
+}
+
+int
+cmd_buffer_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_buffer_data *data;
+ int opt, n;
+ const char *errstr;
+
+ cmd_buffer_init(self, 0);
+ data = self->data;
+
+ while ((opt = getopt(argc, argv, CMD_FLAGS "b:t:")) != -1) {
+ switch (cmd_do_flags(opt, self->entry->flags, &data->flags)) {
+ case -1:
+ goto usage;
+ case 0:
+ continue;
+ }
+ switch (opt) {
+ case 'b':
+ if (data->buffer == -1) {
+ n = strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr != NULL) {
+ xasprintf(cause, "buffer %s", errstr);
+ goto error;
+ }
+ data->buffer = n;
+ }
+ break;
+ case 't':
+ if (data->target == NULL)
+ data->target = xstrdup(optarg);
+ break;
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (cmd_fill_argument(self->entry->flags, &data->arg, argc, argv) != 0)
+ goto usage;
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+error:
+ self->entry->free(self);
+ return (-1);
+}
+
+void
+cmd_buffer_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_buffer_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_send_string(b, data->target);
+ cmd_send_string(b, data->arg);
+}
+
+void
+cmd_buffer_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_buffer_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->target = cmd_recv_string(b);
+ data->arg = cmd_recv_string(b);
+}
+
+void
+cmd_buffer_free(struct cmd *self)
+{
+ struct cmd_buffer_data *data = self->data;
+
+ if (data->target != NULL)
+ xfree(data->target);
+ if (data->arg != NULL)
+ xfree(data->arg);
+ xfree(data);
+}
+
+size_t
+cmd_buffer_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_buffer_data *data = self->data;
+ size_t off = 0;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ off += cmd_print_flags(buf, len, off, data->flags);
+ if (off < len && data->buffer != -1)
+ off += xsnprintf(buf + off, len - off, " -b %d", data->buffer);
+ if (off < len && data->target != NULL)
+ off += cmd_prarg(buf + off, len - off, " -t ", data->target);
+ if (off < len && data->arg != NULL)
+ off += cmd_prarg(buf + off, len - off, " ", data->arg);
+ return (off);
+}
+
+void
+cmd_option_init(struct cmd *self, unused int key)
+{
+ struct cmd_option_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->flags = 0;
+ data->target = NULL;
+ data->option = NULL;
+ data->value = NULL;
+}
+
+int
+cmd_option_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_option_data *data;
+ int opt;
+
+ /* Don't use the entry version since it may be dependent on key. */
+ cmd_option_init(self, 0);
+ data = self->data;
+
+ while ((opt = getopt(argc, argv, CMD_FLAGS "t:")) != -1) {
+ switch (cmd_do_flags(opt, self->entry->flags, &data->flags)) {
+ case -1:
+ goto usage;
+ case 0:
+ continue;
+ }
+ switch (opt) {
+ case 't':
+ if (data->target == NULL)
+ data->target = xstrdup(optarg);
+ break;
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 2) {
+ data->option = xstrdup(argv[0]);
+ data->value = xstrdup(argv[1]);
+ } else if (argc == 1)
+ data->option = xstrdup(argv[0]);
+ else
+ goto usage;
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+ self->entry->free(self);
+ return (-1);
+}
+
+void
+cmd_option_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_option_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_send_string(b, data->target);
+ cmd_send_string(b, data->option);
+ cmd_send_string(b, data->value);
+}
+
+void
+cmd_option_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_option_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->target = cmd_recv_string(b);
+ data->option = cmd_recv_string(b);
+ data->value = cmd_recv_string(b);
+}
+
+void
+cmd_option_free(struct cmd *self)
+{
+ struct cmd_option_data *data = self->data;
+
+ if (data->target != NULL)
+ xfree(data->target);
+ if (data->option != NULL)
+ xfree(data->option);
+ if (data->value != NULL)
+ xfree(data->value);
+ xfree(data);
+}
+
+size_t
+cmd_option_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_option_data *data = self->data;
+ size_t off = 0;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ off += cmd_print_flags(buf, len, off, data->flags);
+ if (off < len && data->target != NULL)
+ off += cmd_prarg(buf + off, len - off, " -t ", data->target);
+ if (off < len && data->option != NULL)
+ off += xsnprintf(buf + off, len - off, " %s", data->option);
+ if (off < len && data->value != NULL)
+ off += xsnprintf(buf + off, len - off, " %s", data->value);
+ return (off);
+}
+
+void
+cmd_pane_init(struct cmd *self, unused int key)
+{
+ struct cmd_pane_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->flags = 0;
+ data->target = NULL;
+ data->arg = NULL;
+ data->pane = -1;
+}
+
+int
+cmd_pane_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_pane_data *data;
+ int opt, n;
+ const char *errstr;
+
+ /* Don't use the entry version since it may be dependent on key. */
+ cmd_pane_init(self, 0);
+ data = self->data;
+
+ while ((opt = getopt(argc, argv, CMD_FLAGS "p:t:")) != -1) {
+ switch (cmd_do_flags(opt, self->entry->flags, &data->flags)) {
+ case -1:
+ goto usage;
+ case 0:
+ continue;
+ }
+ switch (opt) {
+ case 'p':
+ if (data->pane == -1) {
+ n = strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr != NULL) {
+ xasprintf(cause, "pane %s", errstr);
+ goto error;
+ }
+ data->pane = n;
+ }
+ break;
+ case 't':
+ if (data->target == NULL)
+ data->target = xstrdup(optarg);
+ break;
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (cmd_fill_argument(self->entry->flags, &data->arg, argc, argv) != 0)
+ goto usage;
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+error:
+ self->entry->free(self);
+ return (-1);
+}
+
+void
+cmd_pane_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_pane_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_send_string(b, data->target);
+ cmd_send_string(b, data->arg);
+}
+
+void
+cmd_pane_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_pane_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->target = cmd_recv_string(b);
+ data->arg = cmd_recv_string(b);
+}
+
+void
+cmd_pane_free(struct cmd *self)
+{
+ struct cmd_pane_data *data = self->data;
+
+ if (data->target != NULL)
+ xfree(data->target);
+ if (data->arg != NULL)
+ xfree(data->arg);
+ xfree(data);
+}
+
+size_t
+cmd_pane_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_pane_data *data = self->data;
+ size_t off = 0;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ off += cmd_print_flags(buf, len, off, data->flags);
+ if (off < len && data->target != NULL)
+ off += cmd_prarg(buf + off, len - off, " -t ", data->target);
+ if (off < len && data->arg != NULL)
+ off += cmd_prarg(buf + off, len - off, " ", data->arg);
+ return (off);
+}
diff --git a/usr.bin/tmux/cmd-has-session.c b/usr.bin/tmux/cmd-has-session.c
new file mode 100644
index 00000000000..ba46d768ebe
--- /dev/null
+++ b/usr.bin/tmux/cmd-has-session.c
@@ -0,0 +1,51 @@
+/* $OpenBSD: cmd-has-session.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Cause client to report an error and exit with 1 if session doesn't exist.
+ */
+
+int cmd_has_session_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_has_session_entry = {
+ "has-session", "has",
+ CMD_TARGET_SESSION_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_has_session_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_has_session_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+
+ if (cmd_find_session(ctx, data->target) == NULL)
+ return (-1);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-kill-pane.c b/usr.bin/tmux/cmd-kill-pane.c
new file mode 100644
index 00000000000..c6ecea5aa7f
--- /dev/null
+++ b/usr.bin/tmux/cmd-kill-pane.c
@@ -0,0 +1,72 @@
+/* $OpenBSD: cmd-kill-pane.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Kill pane.
+ */
+
+int cmd_kill_pane_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_kill_pane_entry = {
+ "kill-pane", "killp",
+ CMD_PANE_WINDOW_USAGE,
+ 0,
+ cmd_pane_init,
+ cmd_pane_parse,
+ cmd_kill_pane_exec,
+ cmd_pane_send,
+ cmd_pane_recv,
+ cmd_pane_free,
+ cmd_pane_print
+};
+
+int
+cmd_kill_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_pane_data *data = self->data;
+ struct winlink *wl;
+ struct window_pane *wp;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+ if (data->pane == -1)
+ wp = wl->window->active;
+ else {
+ wp = window_pane_at_index(wl->window, data->pane);
+ if (wp == NULL) {
+ ctx->error(ctx, "no pane: %d", data->pane);
+ return (-1);
+ }
+ }
+
+ if (window_count_panes(wl->window) == 1) {
+ ctx->error(ctx, "can't kill pane: %d", data->pane);
+ return (-1);
+ }
+
+ window_remove_pane(wl->window, wp);
+ server_redraw_window(wl->window);
+ layout_refresh(wl->window, 0);
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-kill-server.c b/usr.bin/tmux/cmd-kill-server.c
new file mode 100644
index 00000000000..e3d2e1ce82e
--- /dev/null
+++ b/usr.bin/tmux/cmd-kill-server.c
@@ -0,0 +1,51 @@
+/* $OpenBSD: cmd-kill-server.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <signal.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Kill the server and do nothing else.
+ */
+
+int cmd_kill_server_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_kill_server_entry = {
+ "kill-server", NULL,
+ "",
+ 0,
+ NULL,
+ NULL,
+ cmd_kill_server_exec,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+int
+cmd_kill_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx)
+{
+ sigterm = 1;
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-kill-session.c b/usr.bin/tmux/cmd-kill-session.c
new file mode 100644
index 00000000000..7d63daea66e
--- /dev/null
+++ b/usr.bin/tmux/cmd-kill-session.c
@@ -0,0 +1,68 @@
+/* $OpenBSD: cmd-kill-session.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Destroy session, detaching all clients attached to it and destroying any
+ * windows linked only to this session.
+ *
+ * Note this deliberately has no alias to make it hard to hit by accident.
+ */
+
+int cmd_kill_session_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_kill_session_entry = {
+ "kill-session", NULL,
+ CMD_TARGET_SESSION_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_kill_session_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_kill_session_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct session *s;
+ struct client *c;
+ u_int i;
+
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c->session == s) {
+ c->session = NULL;
+ server_write_client(c, MSG_EXIT, NULL, 0);
+ }
+ }
+ recalculate_sizes();
+
+ session_destroy(s);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-kill-window.c b/usr.bin/tmux/cmd-kill-window.c
new file mode 100644
index 00000000000..d74d8eebbf6
--- /dev/null
+++ b/usr.bin/tmux/cmd-kill-window.c
@@ -0,0 +1,69 @@
+/* $OpenBSD: cmd-kill-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Destroy window.
+ */
+
+int cmd_kill_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_kill_window_entry = {
+ "kill-window", "killw",
+ CMD_TARGET_WINDOW_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_kill_window_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_kill_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct winlink *wl;
+ struct session *s;
+ struct client *c;
+ u_int i;
+ int destroyed;
+
+ if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL)
+ return (-1);
+
+ destroyed = session_detach(s, wl);
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session != s)
+ continue;
+ if (destroyed) {
+ c->session = NULL;
+ server_write_client(c, MSG_EXIT, NULL, 0);
+ } else
+ server_redraw_client(c);
+ }
+ recalculate_sizes();
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-last-window.c b/usr.bin/tmux/cmd-last-window.c
new file mode 100644
index 00000000000..6c77b4d5a92
--- /dev/null
+++ b/usr.bin/tmux/cmd-last-window.c
@@ -0,0 +1,60 @@
+/* $OpenBSD: cmd-last-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Move to last window.
+ */
+
+int cmd_last_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_last_window_entry = {
+ "last-window", "last",
+ CMD_TARGET_SESSION_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_last_window_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_last_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct session *s;
+
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
+ if (session_last(s) == 0)
+ server_redraw_session(s);
+ else {
+ ctx->error(ctx, "no last window");
+ return (-1);
+ }
+ recalculate_sizes();
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-link-window.c b/usr.bin/tmux/cmd-link-window.c
new file mode 100644
index 00000000000..32144859fcf
--- /dev/null
+++ b/usr.bin/tmux/cmd-link-window.c
@@ -0,0 +1,109 @@
+/* $OpenBSD: cmd-link-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Link a window into another session.
+ */
+
+int cmd_link_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_link_window_entry = {
+ "link-window", "linkw",
+ "[-dk] " CMD_SRCDST_WINDOW_USAGE,
+ CMD_DFLAG|CMD_KFLAG,
+ cmd_srcdst_init,
+ cmd_srcdst_parse,
+ cmd_link_window_exec,
+ cmd_srcdst_send,
+ cmd_srcdst_recv,
+ cmd_srcdst_free,
+ cmd_srcdst_print
+};
+
+int
+cmd_link_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_srcdst_data *data = self->data;
+ struct session *dst;
+ struct winlink *wl_src, *wl_dst;
+ char *cause;
+ int idx;
+
+ if ((wl_src = cmd_find_window(ctx, data->src, NULL)) == NULL)
+ return (-1);
+
+ if (arg_parse_window(data->dst, &dst, &idx) != 0) {
+ ctx->error(ctx, "bad window: %s", data->dst);
+ return (-1);
+ }
+ if (dst == NULL)
+ dst = ctx->cursession;
+ if (dst == NULL)
+ dst = cmd_current_session(ctx);
+ if (dst == NULL) {
+ ctx->error(ctx, "session not found: %s", data->dst);
+ return (-1);
+ }
+
+ wl_dst = NULL;
+ if (idx != -1)
+ wl_dst = winlink_find_by_index(&dst->windows, idx);
+ if (wl_dst != NULL) {
+ if (wl_dst->window == wl_src->window)
+ return (0);
+
+ if (data->flags & CMD_KFLAG) {
+ /*
+ * Can't use session_detach as it will destroy session
+ * if this makes it empty.
+ */
+ session_alert_cancel(dst, wl_dst);
+ winlink_stack_remove(&dst->lastw, wl_dst);
+ winlink_remove(&dst->windows, wl_dst);
+
+ /* Force select/redraw if current. */
+ if (wl_dst == dst->curw) {
+ data->flags &= ~CMD_DFLAG;
+ dst->curw = NULL;
+ }
+ }
+ }
+
+ wl_dst = session_attach(dst, wl_src->window, idx, &cause);
+ if (wl_dst == NULL) {
+ ctx->error(ctx, "create session failed: %s", cause);
+ xfree(cause);
+ return (-1);
+ }
+
+ if (data->flags & CMD_DFLAG)
+ server_status_session(dst);
+ else {
+ session_select(dst, wl_dst->idx);
+ server_redraw_session(dst);
+ }
+ recalculate_sizes();
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-list-buffers.c b/usr.bin/tmux/cmd-list-buffers.c
new file mode 100644
index 00000000000..50a0fa858c5
--- /dev/null
+++ b/usr.bin/tmux/cmd-list-buffers.c
@@ -0,0 +1,91 @@
+/* $OpenBSD: cmd-list-buffers.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+/*
+ * List paste buffers.
+ */
+
+int cmd_list_buffers_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_list_buffers_entry = {
+ "list-buffers", "lsb",
+ CMD_TARGET_SESSION_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_list_buffers_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_list_buffers_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct session *s;
+ struct paste_buffer *pb;
+ u_int idx;
+ char *tmp;
+ size_t size, in, out;
+
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
+ if (s->sx > 35) { /* leave three for ... */
+ size = s->sx - 32;
+ tmp = xmalloc(size + 1);
+ } else {
+ size = 0;
+ tmp = NULL;
+ }
+
+ idx = 0;
+ while ((pb = paste_walk_stack(&s->buffers, &idx)) != NULL) {
+ if (tmp != NULL) {
+ in = out = 0;
+ while (out < size && pb->data[in] != '\0') {
+ if (pb->data[in] > 31 && pb->data[in] != 127)
+ tmp[out++] = pb->data[in];
+ in++;
+ }
+ tmp[out] = '\0';
+ if (out == size) {
+ tmp[out - 1] = '.';
+ tmp[out - 2] = '.';
+ tmp[out - 3] = '.';
+ }
+
+ ctx->print(ctx, "%d: %zu bytes: \"%s\"",
+ idx - 1, strlen(pb->data), tmp);
+ } else
+ ctx->print(ctx, "%d: %zu bytes", idx, strlen(pb->data));
+ }
+
+ if (tmp != NULL)
+ xfree(tmp);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-list-clients.c b/usr.bin/tmux/cmd-list-clients.c
new file mode 100644
index 00000000000..e16bf6d007a
--- /dev/null
+++ b/usr.bin/tmux/cmd-list-clients.c
@@ -0,0 +1,67 @@
+/* $OpenBSD: cmd-list-clients.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+#include <time.h>
+
+#include "tmux.h"
+
+/*
+ * List all clients.
+ */
+
+int cmd_list_clients_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_list_clients_entry = {
+ "list-clients", "lsc",
+ "",
+ 0,
+ NULL,
+ NULL,
+ cmd_list_clients_exec,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+int
+cmd_list_clients_exec(unused struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct client *c;
+ u_int i;
+ const char *s_utf8;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session == NULL)
+ continue;
+
+ if (c->tty.flags & TTY_UTF8)
+ s_utf8 = " (utf8)";
+ else
+ s_utf8 = "";
+ ctx->print(ctx, "%s: %s [%ux%u %s]%s", c->tty.path,
+ c->session->name, c->tty.sx, c->tty.sy,
+ c->tty.termname, s_utf8);
+ }
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-list-commands.c b/usr.bin/tmux/cmd-list-commands.c
new file mode 100644
index 00000000000..300df4d174a
--- /dev/null
+++ b/usr.bin/tmux/cmd-list-commands.c
@@ -0,0 +1,51 @@
+/* $OpenBSD: cmd-list-commands.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * List all commands with usages.
+ */
+
+int cmd_list_commands_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_list_commands_entry = {
+ "list-commands", "lscm",
+ "",
+ 0,
+ NULL,
+ NULL,
+ cmd_list_commands_exec,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+int
+cmd_list_commands_exec(unused struct cmd *self, struct cmd_ctx *ctx)
+{
+ const struct cmd_entry **entryp;
+
+ for (entryp = cmd_table; *entryp != NULL; entryp++)
+ ctx->print(ctx, "%s %s", (*entryp)->name, (*entryp)->usage);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-list-keys.c b/usr.bin/tmux/cmd-list-keys.c
new file mode 100644
index 00000000000..fa756582319
--- /dev/null
+++ b/usr.bin/tmux/cmd-list-keys.c
@@ -0,0 +1,59 @@
+/* $OpenBSD: cmd-list-keys.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * List key bindings.
+ */
+
+int cmd_list_keys_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_list_keys_entry = {
+ "list-keys", "lsk",
+ "",
+ 0,
+ NULL,
+ NULL,
+ cmd_list_keys_exec,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+int
+cmd_list_keys_exec(unused struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct key_binding *bd;
+ const char *key;
+ char tmp[BUFSIZ];
+
+ SPLAY_FOREACH(bd, key_bindings, &key_bindings) {
+ if ((key = key_string_lookup_key(bd->key)) == NULL)
+ continue;
+
+ *tmp = '\0';
+ cmd_list_print(bd->cmdlist, tmp, sizeof tmp);
+ ctx->print(ctx, "%11s: %s", key, tmp);
+ }
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-list-sessions.c b/usr.bin/tmux/cmd-list-sessions.c
new file mode 100644
index 00000000000..4cd4f2f7a35
--- /dev/null
+++ b/usr.bin/tmux/cmd-list-sessions.c
@@ -0,0 +1,67 @@
+/* $OpenBSD: cmd-list-sessions.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+#include <time.h>
+
+#include "tmux.h"
+
+/*
+ * List all sessions.
+ */
+
+int cmd_list_sessions_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_list_sessions_entry = {
+ "list-sessions", "ls", "",
+ 0,
+ NULL,
+ NULL,
+ cmd_list_sessions_exec,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+int
+cmd_list_sessions_exec(unused struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct session *s;
+ char *tim;
+ u_int i;
+ time_t t;
+
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ s = ARRAY_ITEM(&sessions, i);
+ if (s == NULL)
+ continue;
+
+ t = s->tv.tv_sec;
+ tim = ctime(&t);
+ *strchr(tim, '\n') = '\0';
+
+ ctx->print(ctx, "%s: %u windows (created %s) [%ux%u]%s",
+ s->name, winlink_count(&s->windows), tim, s->sx, s->sy,
+ s->flags & SESSION_UNATTACHED ? "" : " (attached)");
+ }
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-list-windows.c b/usr.bin/tmux/cmd-list-windows.c
new file mode 100644
index 00000000000..b5686635241
--- /dev/null
+++ b/usr.bin/tmux/cmd-list-windows.c
@@ -0,0 +1,88 @@
+/* $OpenBSD: cmd-list-windows.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * List windows on given session.
+ */
+
+int cmd_list_windows_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_list_windows_entry = {
+ "list-windows", "lsw",
+ CMD_TARGET_SESSION_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_list_windows_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct session *s;
+ struct winlink *wl;
+ struct window *w;
+ struct window_pane *wp;
+ struct grid *gd;
+ u_int i;
+ unsigned long long size;
+ const char *name;
+
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
+ RB_FOREACH(wl, winlinks, &s->windows) {
+ w = wl->window;
+ ctx->print(ctx,
+ "%3d: %s [%ux%u]", wl->idx, w->name, w->sx, w->sy);
+
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ gd = wp->base.grid;
+
+ size = 0;
+ for (i = 0; i < gd->hsize; i++) {
+ size += gd->size[i] * sizeof **gd->data;
+ size += gd->usize[i] * sizeof **gd->udata;
+ }
+ size += gd->hsize * (sizeof *gd->data);
+ size += gd->hsize * (sizeof *gd->size);
+
+ if (wp->fd != -1)
+ name = ttyname(wp->fd);
+ else
+ name = "unknown";
+ ctx->print(ctx,
+ " %s [%ux%u %s] [history %u/%u, %llu bytes]",
+ name, wp->sx, wp->sy, layout_name(w), gd->hsize,
+ gd->hlimit, size);
+ }
+ }
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-list.c b/usr.bin/tmux/cmd-list.c
new file mode 100644
index 00000000000..2d778e25e31
--- /dev/null
+++ b/usr.bin/tmux/cmd-list.c
@@ -0,0 +1,154 @@
+/* $OpenBSD: cmd-list.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+struct cmd_list *
+cmd_list_parse(int argc, char **argv, char **cause)
+{
+ struct cmd_list *cmdlist;
+ struct cmd *cmd;
+ int i, lastsplit;
+ size_t arglen, new_argc;
+ char **new_argv;
+
+ cmdlist = xmalloc(sizeof *cmdlist);
+ TAILQ_INIT(cmdlist);
+
+ lastsplit = 0;
+ for (i = 0; i < argc; i++) {
+ arglen = strlen(argv[i]);
+ if (arglen == 0 || argv[i][arglen - 1] != ';')
+ continue;
+ argv[i][arglen - 1] = '\0';
+
+ if (arglen > 1 && argv[i][arglen - 2] == '\\') {
+ argv[i][arglen - 2] = ';';
+ continue;
+ }
+
+ new_argc = i - lastsplit;
+ new_argv = argv + lastsplit;
+ if (arglen != 1)
+ new_argc++;
+
+ cmd = cmd_parse(new_argc, new_argv, cause);
+ if (cmd == NULL)
+ goto bad;
+ TAILQ_INSERT_TAIL(cmdlist, cmd, qentry);
+
+ lastsplit = i + 1;
+ }
+
+ if (lastsplit != argc) {
+ cmd = cmd_parse(argc - lastsplit, argv + lastsplit, cause);
+ if (cmd == NULL)
+ goto bad;
+ TAILQ_INSERT_TAIL(cmdlist, cmd, qentry);
+ }
+
+ return (cmdlist);
+
+bad:
+ cmd_list_free(cmdlist);
+ return (NULL);
+}
+
+int
+cmd_list_exec(struct cmd_list *cmdlist, struct cmd_ctx *ctx)
+{
+ struct cmd *cmd;
+ int n;
+
+ TAILQ_FOREACH(cmd, cmdlist, qentry) {
+ if ((n = cmd_exec(cmd, ctx)) != 0)
+ return (n);
+ }
+ return (0);
+}
+
+void
+cmd_list_send(struct cmd_list *cmdlist, struct buffer *b)
+{
+ struct cmd *cmd;
+ u_int n;
+
+ n = 0;
+ TAILQ_FOREACH(cmd, cmdlist, qentry)
+ n++;
+
+ buffer_write(b, &n, sizeof n);
+ TAILQ_FOREACH(cmd, cmdlist, qentry)
+ cmd_send(cmd, b);
+}
+
+struct cmd_list *
+cmd_list_recv(struct buffer *b)
+{
+ struct cmd_list *cmdlist;
+ struct cmd *cmd;
+ u_int n;
+
+ buffer_read(b, &n, sizeof n);
+
+ cmdlist = xmalloc(sizeof *cmdlist);
+ TAILQ_INIT(cmdlist);
+
+ while (n-- > 0) {
+ cmd = cmd_recv(b);
+ TAILQ_INSERT_TAIL(cmdlist, cmd, qentry);
+ }
+
+ return (cmdlist);
+}
+
+void
+cmd_list_free(struct cmd_list *cmdlist)
+{
+ struct cmd *cmd;
+
+ while (!TAILQ_EMPTY(cmdlist)) {
+ cmd = TAILQ_FIRST(cmdlist);
+ TAILQ_REMOVE(cmdlist, cmd, qentry);
+ cmd_free(cmd);
+ }
+ xfree(cmdlist);
+}
+
+size_t
+cmd_list_print(struct cmd_list *cmdlist, char *buf, size_t len)
+{
+ struct cmd *cmd;
+ size_t off;
+
+ off = 0;
+ TAILQ_FOREACH(cmd, cmdlist, qentry) {
+ if (off >= len)
+ break;
+ off += cmd_print(cmd, buf + off, len - off);
+ if (off >= len)
+ break;
+ if (TAILQ_NEXT(cmd, qentry) != NULL)
+ off += xsnprintf(buf + off, len - off, " ; ");
+ }
+ return (off);
+}
diff --git a/usr.bin/tmux/cmd-load-buffer.c b/usr.bin/tmux/cmd-load-buffer.c
new file mode 100644
index 00000000000..5ac0246cd13
--- /dev/null
+++ b/usr.bin/tmux/cmd-load-buffer.c
@@ -0,0 +1,106 @@
+/* $OpenBSD: cmd-load-buffer.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
+ *
+ * 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/stat.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Loads a session paste buffer from a file.
+ */
+
+int cmd_load_buffer_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_load_buffer_entry = {
+ "load-buffer", "loadb",
+ CMD_BUFFER_SESSION_USAGE " path",
+ CMD_ARG1,
+ cmd_buffer_init,
+ cmd_buffer_parse,
+ cmd_load_buffer_exec,
+ cmd_buffer_send,
+ cmd_buffer_recv,
+ cmd_buffer_free,
+ cmd_buffer_print
+};
+
+int
+cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_buffer_data *data = self->data;
+ struct session *s;
+ struct stat statbuf;
+ FILE *f;
+ char *buf;
+ u_int limit;
+
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
+ if (stat(data->arg, &statbuf) < 0) {
+ ctx->error(ctx, "%s: %s", data->arg, strerror(errno));
+ return (-1);
+ }
+ if (!S_ISREG(statbuf.st_mode)) {
+ ctx->error(ctx, "%s: not a regular file", data->arg);
+ return (-1);
+ }
+
+ if ((f = fopen(data->arg, "rb")) == NULL) {
+ ctx->error(ctx, "%s: %s", data->arg, strerror(errno));
+ return (-1);
+ }
+
+ /*
+ * We don't want to die due to memory exhaustion, hence xmalloc can't
+ * be used here.
+ */
+ if ((buf = malloc(statbuf.st_size + 1)) == NULL) {
+ ctx->error(ctx, "malloc error: %s", strerror(errno));
+ return (-1);
+ }
+
+ if (fread(buf, 1, statbuf.st_size, f) != (size_t) statbuf.st_size) {
+ ctx->error(ctx, "%s: fread error", data->arg);
+ xfree(buf);
+ fclose(f);
+ return (-1);
+ }
+
+ buf[statbuf.st_size] = '\0';
+ fclose(f);
+
+ limit = options_get_number(&s->options, "buffer-limit");
+ if (data->buffer == -1) {
+ paste_add(&s->buffers, buf, limit);
+ return (0);
+ }
+ if (paste_replace(&s->buffers, data->buffer, buf) != 0) {
+ ctx->error(ctx, "no buffer %d", data->buffer);
+ xfree(buf);
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-lock-server.c b/usr.bin/tmux/cmd-lock-server.c
new file mode 100644
index 00000000000..e0c3ab60655
--- /dev/null
+++ b/usr.bin/tmux/cmd-lock-server.c
@@ -0,0 +1,54 @@
+/* $OpenBSD: cmd-lock-server.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <pwd.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Lock server.
+ */
+
+int cmd_lock_server_exec(struct cmd *, struct cmd_ctx *);
+
+int cmd_lock_server_callback(void *, const char *);
+
+const struct cmd_entry cmd_lock_server_entry = {
+ "lock-server", "lock",
+ "",
+ 0,
+ NULL,
+ NULL,
+ cmd_lock_server_exec,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+int
+cmd_lock_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx)
+{
+ server_lock();
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-move-window.c b/usr.bin/tmux/cmd-move-window.c
new file mode 100644
index 00000000000..bde3ed61380
--- /dev/null
+++ b/usr.bin/tmux/cmd-move-window.c
@@ -0,0 +1,123 @@
+/* $OpenBSD: cmd-move-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Move a window.
+ */
+
+int cmd_move_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_move_window_entry = {
+ "move-window", "movew",
+ "[-dk] " CMD_SRCDST_WINDOW_USAGE,
+ CMD_DFLAG|CMD_KFLAG,
+ cmd_srcdst_init,
+ cmd_srcdst_parse,
+ cmd_move_window_exec,
+ cmd_srcdst_send,
+ cmd_srcdst_recv,
+ cmd_srcdst_free,
+ cmd_srcdst_print
+};
+
+int
+cmd_move_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_srcdst_data *data = self->data;
+ struct session *src, *dst;
+ struct winlink *wl_src, *wl_dst;
+ struct client *c;
+ u_int i;
+ int destroyed, idx;
+ char *cause;
+
+ if ((wl_src = cmd_find_window(ctx, data->src, &src)) == NULL)
+ return (-1);
+
+ if (arg_parse_window(data->dst, &dst, &idx) != 0) {
+ ctx->error(ctx, "bad window: %s", data->dst);
+ return (-1);
+ }
+ if (dst == NULL)
+ dst = ctx->cursession;
+ if (dst == NULL)
+ dst = cmd_current_session(ctx);
+ if (dst == NULL) {
+ ctx->error(ctx, "session not found: %s", data->dst);
+ return (-1);
+ }
+
+ wl_dst = NULL;
+ if (idx != -1)
+ wl_dst = winlink_find_by_index(&dst->windows, idx);
+ if (wl_dst != NULL) {
+ if (wl_dst->window == wl_src->window)
+ return (0);
+
+ if (data->flags & CMD_KFLAG) {
+ /*
+ * Can't use session_detach as it will destroy session
+ * if this makes it empty.
+ */
+ session_alert_cancel(dst, wl_dst);
+ winlink_stack_remove(&dst->lastw, wl_dst);
+ winlink_remove(&dst->windows, wl_dst);
+
+ /* Force select/redraw if current. */
+ if (wl_dst == dst->curw) {
+ data->flags &= ~CMD_DFLAG;
+ dst->curw = NULL;
+ }
+ }
+ }
+
+ wl_dst = session_attach(dst, wl_src->window, idx, &cause);
+ if (wl_dst == NULL) {
+ ctx->error(ctx, "attach window failed: %s", cause);
+ xfree(cause);
+ return (-1);
+ }
+
+ destroyed = session_detach(src, wl_src);
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session != src)
+ continue;
+ if (destroyed) {
+ c->session = NULL;
+ server_write_client(c, MSG_EXIT, NULL, 0);
+ } else
+ server_redraw_client(c);
+ }
+
+ if (data->flags & CMD_DFLAG)
+ server_status_session(dst);
+ else {
+ session_select(dst, wl_dst->idx);
+ server_redraw_session(dst);
+ }
+ recalculate_sizes();
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-new-session.c b/usr.bin/tmux/cmd-new-session.c
new file mode 100644
index 00000000000..fbf8c286074
--- /dev/null
+++ b/usr.bin/tmux/cmd-new-session.c
@@ -0,0 +1,248 @@
+/* $OpenBSD: cmd-new-session.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Create a new session and attach to the current terminal unless -d is given.
+ */
+
+int cmd_new_session_parse(struct cmd *, int, char **, char **);
+int cmd_new_session_exec(struct cmd *, struct cmd_ctx *);
+void cmd_new_session_send(struct cmd *, struct buffer *);
+void cmd_new_session_recv(struct cmd *, struct buffer *);
+void cmd_new_session_free(struct cmd *);
+void cmd_new_session_init(struct cmd *, int);
+size_t cmd_new_session_print(struct cmd *, char *, size_t);
+
+struct cmd_new_session_data {
+ char *newname;
+ char *winname;
+ char *cmd;
+ int flag_detached;
+};
+
+const struct cmd_entry cmd_new_session_entry = {
+ "new-session", "new",
+ "[-d] [-n window-name] [-s session-name] [command]",
+ CMD_STARTSERVER|CMD_CANTNEST,
+ cmd_new_session_init,
+ cmd_new_session_parse,
+ cmd_new_session_exec,
+ cmd_new_session_send,
+ cmd_new_session_recv,
+ cmd_new_session_free,
+ cmd_new_session_print
+};
+
+void
+cmd_new_session_init(struct cmd *self, unused int arg)
+{
+ struct cmd_new_session_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->flag_detached = 0;
+ data->newname = NULL;
+ data->winname = NULL;
+ data->cmd = NULL;
+}
+
+int
+cmd_new_session_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_new_session_data *data;
+ int opt;
+
+ self->entry->init(self, 0);
+ data = self->data;
+
+ while ((opt = getopt(argc, argv, "ds:n:")) != -1) {
+ switch (opt) {
+ case 'd':
+ data->flag_detached = 1;
+ break;
+ case 's':
+ if (data->newname == NULL)
+ data->newname = xstrdup(optarg);
+ break;
+ case 'n':
+ if (data->winname == NULL)
+ data->winname = xstrdup(optarg);
+ break;
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 0 && argc != 1)
+ goto usage;
+
+ if (argc == 1)
+ data->cmd = xstrdup(argv[0]);
+
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+ self->entry->free(self);
+ return (-1);
+}
+
+int
+cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_new_session_data *data = self->data;
+ struct client *c = ctx->cmdclient;
+ struct session *s;
+ char *cmd, *cwd, *cause;
+ u_int sx, sy;
+
+ if (ctx->curclient != NULL)
+ return (0);
+
+ if (!data->flag_detached) {
+ if (c == NULL) {
+ ctx->error(ctx, "no client to attach to");
+ return (-1);
+ }
+ if (!(c->flags & CLIENT_TERMINAL)) {
+ ctx->error(ctx, "not a terminal");
+ return (-1);
+ }
+ }
+
+ if (data->newname != NULL && session_find(data->newname) != NULL) {
+ ctx->error(ctx, "duplicate session: %s", data->newname);
+ return (-1);
+ }
+
+ cmd = data->cmd;
+ if (cmd == NULL)
+ cmd = options_get_string(&global_options, "default-command");
+ if (c == NULL || c->cwd == NULL)
+ cwd = options_get_string(&global_options, "default-path");
+ else
+ cwd = c->cwd;
+
+ sx = 80;
+ sy = 25;
+ if (!data->flag_detached) {
+ sx = c->tty.sx;
+ sy = c->tty.sy;
+ }
+
+ if (options_get_number(&global_options, "status")) {
+ if (sy == 0)
+ sy = 1;
+ else
+ sy--;
+ }
+
+ if (!data->flag_detached && tty_open(&c->tty, &cause) != 0) {
+ ctx->error(ctx, "open terminal failed: %s", cause);
+ xfree(cause);
+ return (-1);
+ }
+
+
+ s = session_create(data->newname, cmd, cwd, sx, sy, &cause);
+ if (s == NULL) {
+ ctx->error(ctx, "create session failed: %s", cause);
+ xfree(cause);
+ return (-1);
+ }
+ if (data->winname != NULL) {
+ xfree(s->curw->window->name);
+ s->curw->window->name = xstrdup(data->winname);
+ options_set_number(
+ &s->curw->window->options, "automatic-rename", 0);
+ }
+
+ if (data->flag_detached) {
+ if (c != NULL)
+ server_write_client(c, MSG_EXIT, NULL, 0);
+ } else {
+ c->session = s;
+ server_write_client(c, MSG_READY, NULL, 0);
+ server_redraw_client(c);
+ }
+ recalculate_sizes();
+
+ return (1);
+}
+
+void
+cmd_new_session_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_new_session_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_send_string(b, data->newname);
+ cmd_send_string(b, data->winname);
+ cmd_send_string(b, data->cmd);
+}
+
+void
+cmd_new_session_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_new_session_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->newname = cmd_recv_string(b);
+ data->winname = cmd_recv_string(b);
+ data->cmd = cmd_recv_string(b);
+}
+
+void
+cmd_new_session_free(struct cmd *self)
+{
+ struct cmd_new_session_data *data = self->data;
+
+ if (data->newname != NULL)
+ xfree(data->newname);
+ if (data->winname != NULL)
+ xfree(data->winname);
+ if (data->cmd != NULL)
+ xfree(data->cmd);
+ xfree(data);
+}
+
+size_t
+cmd_new_session_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_new_session_data *data = self->data;
+ size_t off = 0;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ if (off < len && data->flag_detached)
+ off += xsnprintf(buf + off, len - off, " -d");
+ if (off < len && data->newname != NULL)
+ off += cmd_prarg(buf + off, len - off, " -s ", data->newname);
+ if (off < len && data->winname != NULL)
+ off += cmd_prarg(buf + off, len - off, " -n ", data->winname);
+ if (off < len && data->cmd != NULL)
+ off += cmd_prarg(buf + off, len - off, " ", data->cmd);
+ return (off);
+}
diff --git a/usr.bin/tmux/cmd-new-window.c b/usr.bin/tmux/cmd-new-window.c
new file mode 100644
index 00000000000..3711ff65b46
--- /dev/null
+++ b/usr.bin/tmux/cmd-new-window.c
@@ -0,0 +1,241 @@
+/* $OpenBSD: cmd-new-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Create a new window.
+ */
+
+int cmd_new_window_parse(struct cmd *, int, char **, char **);
+int cmd_new_window_exec(struct cmd *, struct cmd_ctx *);
+void cmd_new_window_send(struct cmd *, struct buffer *);
+void cmd_new_window_recv(struct cmd *, struct buffer *);
+void cmd_new_window_free(struct cmd *);
+void cmd_new_window_init(struct cmd *, int);
+size_t cmd_new_window_print(struct cmd *, char *, size_t);
+
+struct cmd_new_window_data {
+ char *target;
+ char *name;
+ char *cmd;
+ int flag_detached;
+ int flag_kill;
+};
+
+const struct cmd_entry cmd_new_window_entry = {
+ "new-window", "neww",
+ "[-dk] [-n window-name] [-t target-window] [command]",
+ 0,
+ cmd_new_window_init,
+ cmd_new_window_parse,
+ cmd_new_window_exec,
+ cmd_new_window_send,
+ cmd_new_window_recv,
+ cmd_new_window_free,
+ cmd_new_window_print
+};
+
+void
+cmd_new_window_init(struct cmd *self, unused int arg)
+{
+ struct cmd_new_window_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->target = NULL;
+ data->name = NULL;
+ data->cmd = NULL;
+ data->flag_detached = 0;
+ data->flag_kill = 0;
+}
+
+int
+cmd_new_window_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_new_window_data *data;
+ int opt;
+
+ self->entry->init(self, 0);
+ data = self->data;
+
+ while ((opt = getopt(argc, argv, "dkt:n:")) != -1) {
+ switch (opt) {
+ case 'd':
+ data->flag_detached = 1;
+ break;
+ case 'k':
+ data->flag_kill = 1;
+ break;
+ case 't':
+ if (data->target == NULL)
+ data->target = xstrdup(optarg);
+ break;
+ case 'n':
+ if (data->name == NULL)
+ data->name = xstrdup(optarg);
+ break;
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 0 && argc != 1)
+ goto usage;
+
+ if (argc == 1)
+ data->cmd = xstrdup(argv[0]);
+
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+ self->entry->free(self);
+ return (-1);
+}
+
+int
+cmd_new_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_new_window_data *data = self->data;
+ struct session *s;
+ struct winlink *wl;
+ char *cmd, *cwd, *cause;
+ int idx;
+
+ if (data == NULL)
+ return (0);
+
+ if (arg_parse_window(data->target, &s, &idx) != 0) {
+ ctx->error(ctx, "bad window: %s", data->target);
+ return (-1);
+ }
+ if (s == NULL)
+ s = ctx->cursession;
+ if (s == NULL)
+ s = cmd_current_session(ctx);
+ if (s == NULL) {
+ ctx->error(ctx, "session not found: %s", data->target);
+ return (-1);
+ }
+
+ wl = NULL;
+ if (idx != -1)
+ wl = winlink_find_by_index(&s->windows, idx);
+ if (wl != NULL) {
+ if (data->flag_kill) {
+ /*
+ * Can't use session_detach as it will destroy session
+ * if this makes it empty.
+ */
+ session_alert_cancel(s, wl);
+ winlink_stack_remove(&s->lastw, wl);
+ winlink_remove(&s->windows, wl);
+
+ /* Force select/redraw if current. */
+ if (wl == s->curw) {
+ data->flag_detached = 0;
+ s->curw = NULL;
+ }
+ }
+ }
+
+ cmd = data->cmd;
+ if (cmd == NULL)
+ cmd = options_get_string(&s->options, "default-command");
+ if (ctx->cmdclient == NULL || ctx->cmdclient->cwd == NULL)
+ cwd = options_get_string(&global_options, "default-path");
+ else
+ cwd = ctx->cmdclient->cwd;
+
+ wl = session_new(s, data->name, cmd, cwd, idx, &cause);
+ if (wl == NULL) {
+ ctx->error(ctx, "create window failed: %s", cause);
+ xfree(cause);
+ return (-1);
+ }
+ if (!data->flag_detached) {
+ session_select(s, wl->idx);
+ server_redraw_session(s);
+ } else
+ server_status_session(s);
+
+ return (0);
+}
+
+void
+cmd_new_window_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_new_window_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_send_string(b, data->target);
+ cmd_send_string(b, data->name);
+ cmd_send_string(b, data->cmd);
+}
+
+void
+cmd_new_window_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_new_window_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->target = cmd_recv_string(b);
+ data->name = cmd_recv_string(b);
+ data->cmd = cmd_recv_string(b);
+}
+
+void
+cmd_new_window_free(struct cmd *self)
+{
+ struct cmd_new_window_data *data = self->data;
+
+ if (data->target != NULL)
+ xfree(data->target);
+ if (data->name != NULL)
+ xfree(data->name);
+ if (data->cmd != NULL)
+ xfree(data->cmd);
+ xfree(data);
+}
+
+size_t
+cmd_new_window_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_new_window_data *data = self->data;
+ size_t off = 0;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ if (off < len && data->flag_detached)
+ off += xsnprintf(buf + off, len - off, " -d");
+ if (off < len && data->target != NULL)
+ off += cmd_prarg(buf + off, len - off, " -t ", data->target);
+ if (off < len && data->name != NULL)
+ off += cmd_prarg(buf + off, len - off, " -n ", data->name);
+ if (off < len && data->cmd != NULL)
+ off += cmd_prarg(buf + off, len - off, " ", data->cmd);
+ return (off);
+}
diff --git a/usr.bin/tmux/cmd-next-layout.c b/usr.bin/tmux/cmd-next-layout.c
new file mode 100644
index 00000000000..c4bbfcc0f25
--- /dev/null
+++ b/usr.bin/tmux/cmd-next-layout.c
@@ -0,0 +1,55 @@
+/* $OpenBSD: cmd-next-layout.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Switch window to next layout.
+ */
+
+int cmd_next_layout_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_next_layout_entry = {
+ "next-layout", "nextl",
+ CMD_TARGET_WINDOW_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_next_layout_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_next_layout_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct winlink *wl;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+
+ layout_next(wl->window);
+ ctx->info(ctx, "layout now: %s", layout_name(wl->window));
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-next-window.c b/usr.bin/tmux/cmd-next-window.c
new file mode 100644
index 00000000000..c938a8f7cff
--- /dev/null
+++ b/usr.bin/tmux/cmd-next-window.c
@@ -0,0 +1,78 @@
+/* $OpenBSD: cmd-next-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Move to next window.
+ */
+
+void cmd_next_window_init(struct cmd *, int);
+int cmd_next_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_next_window_entry = {
+ "next-window", "next",
+ CMD_TARGET_SESSION_USAGE,
+ CMD_AFLAG,
+ cmd_next_window_init,
+ cmd_target_parse,
+ cmd_next_window_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+void
+cmd_next_window_init(struct cmd *self, int key)
+{
+ struct cmd_target_data *data;
+
+ cmd_target_init(self, key);
+ data = self->data;
+
+ if (key == KEYC_ADDESC('n'))
+ data->flags |= CMD_AFLAG;
+}
+
+int
+cmd_next_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct session *s;
+ int activity;
+
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
+ activity = 0;
+ if (data->flags & CMD_AFLAG)
+ activity = 1;
+
+ if (session_next(s, activity) == 0)
+ server_redraw_session(s);
+ else {
+ ctx->error(ctx, "no next window");
+ return (-1);
+ }
+ recalculate_sizes();
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-paste-buffer.c b/usr.bin/tmux/cmd-paste-buffer.c
new file mode 100644
index 00000000000..fc01461bc7b
--- /dev/null
+++ b/usr.bin/tmux/cmd-paste-buffer.c
@@ -0,0 +1,78 @@
+/* $OpenBSD: cmd-paste-buffer.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+/*
+ * Paste paste buffer if present.
+ */
+
+int cmd_paste_buffer_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_paste_buffer_entry = {
+ "paste-buffer", "pasteb",
+ "[-d] " CMD_BUFFER_WINDOW_USAGE,
+ CMD_DFLAG,
+ cmd_buffer_init,
+ cmd_buffer_parse,
+ cmd_paste_buffer_exec,
+ cmd_buffer_send,
+ cmd_buffer_recv,
+ cmd_buffer_free,
+ cmd_buffer_print
+};
+
+int
+cmd_paste_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_buffer_data *data = self->data;
+ struct winlink *wl;
+ struct window *w;
+ struct session *s;
+ struct paste_buffer *pb;
+
+ if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL)
+ return (-1);
+ w = wl->window;
+
+ if (data->buffer == -1)
+ pb = paste_get_top(&s->buffers);
+ else {
+ if ((pb = paste_get_index(&s->buffers, data->buffer)) == NULL) {
+ ctx->error(ctx, "no buffer %d", data->buffer);
+ return (-1);
+ }
+ }
+
+ if (pb != NULL)
+ buffer_write(w->active->out, pb->data, strlen(pb->data));
+
+ /* Delete the buffer if -d. */
+ if (data->flags & CMD_DFLAG) {
+ if (data->buffer == -1)
+ paste_free_top(&s->buffers);
+ else
+ paste_free_index(&s->buffers, data->buffer);
+ }
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-previous-layout.c b/usr.bin/tmux/cmd-previous-layout.c
new file mode 100644
index 00000000000..6a2730432a2
--- /dev/null
+++ b/usr.bin/tmux/cmd-previous-layout.c
@@ -0,0 +1,55 @@
+/* $OpenBSD: cmd-previous-layout.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Switch window to previous layout.
+ */
+
+int cmd_previous_layout_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_previous_layout_entry = {
+ "previous-layout", "prevl",
+ CMD_TARGET_WINDOW_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_previous_layout_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_previous_layout_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct winlink *wl;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+
+ layout_previous(wl->window);
+ ctx->info(ctx, "layout now: %s", layout_name(wl->window));
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-previous-window.c b/usr.bin/tmux/cmd-previous-window.c
new file mode 100644
index 00000000000..b477b140deb
--- /dev/null
+++ b/usr.bin/tmux/cmd-previous-window.c
@@ -0,0 +1,78 @@
+/* $OpenBSD: cmd-previous-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Move to previous window.
+ */
+
+void cmd_previous_window_init(struct cmd *, int);
+int cmd_previous_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_previous_window_entry = {
+ "previous-window", "prev",
+ CMD_TARGET_SESSION_USAGE,
+ CMD_AFLAG,
+ cmd_previous_window_init,
+ cmd_target_parse,
+ cmd_previous_window_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+void
+cmd_previous_window_init(struct cmd *self, int key)
+{
+ struct cmd_target_data *data;
+
+ cmd_target_init(self, key);
+ data = self->data;
+
+ if (key == KEYC_ADDESC('p'))
+ data->flags |= CMD_AFLAG;
+}
+
+int
+cmd_previous_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct session *s;
+ int activity;
+
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
+ activity = 0;
+ if (data->flags & CMD_AFLAG)
+ activity = 1;
+
+ if (session_previous(s, activity) == 0)
+ server_redraw_session(s);
+ else {
+ ctx->error(ctx, "no previous window");
+ return (-1);
+ }
+ recalculate_sizes();
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-refresh-client.c b/usr.bin/tmux/cmd-refresh-client.c
new file mode 100644
index 00000000000..75e2fa74d38
--- /dev/null
+++ b/usr.bin/tmux/cmd-refresh-client.c
@@ -0,0 +1,54 @@
+/* $OpenBSD: cmd-refresh-client.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Refresh client.
+ */
+
+int cmd_refresh_client_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_refresh_client_entry = {
+ "refresh-client", "refresh",
+ CMD_TARGET_CLIENT_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_refresh_client_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_refresh_client_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct client *c;
+
+ if ((c = cmd_find_client(ctx, data->target)) == NULL)
+ return (-1);
+
+ server_redraw_client(c);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-rename-session.c b/usr.bin/tmux/cmd-rename-session.c
new file mode 100644
index 00000000000..dfeba64d8ff
--- /dev/null
+++ b/usr.bin/tmux/cmd-rename-session.c
@@ -0,0 +1,57 @@
+/* $OpenBSD: cmd-rename-session.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Change session name.
+ */
+
+int cmd_rename_session_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_rename_session_entry = {
+ "rename-session", "rename",
+ CMD_TARGET_SESSION_USAGE " new-name",
+ CMD_ARG1,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_rename_session_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_rename_session_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct session *s;
+
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
+ xfree(s->name);
+ s->name = xstrdup(data->arg);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-rename-window.c b/usr.bin/tmux/cmd-rename-window.c
new file mode 100644
index 00000000000..d702cb3b847
--- /dev/null
+++ b/usr.bin/tmux/cmd-rename-window.c
@@ -0,0 +1,61 @@
+/* $OpenBSD: cmd-rename-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Rename a window.
+ */
+
+int cmd_rename_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_rename_window_entry = {
+ "rename-window", "renamew",
+ CMD_TARGET_WINDOW_USAGE " new-name",
+ CMD_ARG1,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_rename_window_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_rename_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct session *s;
+ struct winlink *wl;
+
+ if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL)
+ return (-1);
+
+ xfree(wl->window->name);
+ wl->window->name = xstrdup(data->arg);
+ options_set_number(&wl->window->options, "automatic-rename", 0);
+
+ server_status_session(s);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-resize-pane.c b/usr.bin/tmux/cmd-resize-pane.c
new file mode 100644
index 00000000000..c9c77e800c7
--- /dev/null
+++ b/usr.bin/tmux/cmd-resize-pane.c
@@ -0,0 +1,105 @@
+/* $OpenBSD: cmd-resize-pane.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Increase or decrease pane size.
+ */
+
+void cmd_resize_pane_init(struct cmd *, int);
+int cmd_resize_pane_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_resize_pane_entry = {
+ "resize-pane", "resizep",
+ CMD_PANE_WINDOW_USAGE "[-DU] [adjustment]",
+ CMD_ARG01|CMD_BIGUFLAG|CMD_BIGDFLAG,
+ cmd_resize_pane_init,
+ cmd_pane_parse,
+ cmd_resize_pane_exec,
+ cmd_pane_send,
+ cmd_pane_recv,
+ cmd_pane_free,
+ cmd_pane_print
+};
+
+void
+cmd_resize_pane_init(struct cmd *self, int key)
+{
+ struct cmd_pane_data *data;
+
+ cmd_pane_init(self, key);
+ data = self->data;
+
+ if (key == KEYC_ADDCTL(KEYC_DOWN))
+ data->flags |= CMD_BIGDFLAG;
+
+ if (key == KEYC_ADDESC(KEYC_UP))
+ data->arg = xstrdup("5");
+ if (key == KEYC_ADDESC(KEYC_DOWN)) {
+ data->flags |= CMD_BIGDFLAG;
+ data->arg = xstrdup("5");
+ }
+}
+
+int
+cmd_resize_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_pane_data *data = self->data;
+ struct winlink *wl;
+ const char *errstr;
+ struct window_pane *wp;
+ u_int adjust;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+ if (data->pane == -1)
+ wp = wl->window->active;
+ else {
+ wp = window_pane_at_index(wl->window, data->pane);
+ if (wp == NULL) {
+ ctx->error(ctx, "no pane: %d", data->pane);
+ return (-1);
+ }
+ }
+
+ if (data->arg == NULL)
+ adjust = 1;
+ else {
+ adjust = strtonum(data->arg, 1, INT_MAX, &errstr);
+ if (errstr != NULL) {
+ ctx->error(ctx, "adjustment %s: %s", errstr, data->arg);
+ return (-1);
+ }
+ }
+
+ if (!(data->flags & CMD_BIGDFLAG))
+ adjust = -adjust;
+ if (layout_resize(wp, adjust) != 0) {
+ ctx->error(ctx, "layout %s "
+ "does not support resizing", layout_name(wp->window));
+ return (-1);
+ }
+ server_redraw_window(wl->window);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-respawn-window.c b/usr.bin/tmux/cmd-respawn-window.c
new file mode 100644
index 00000000000..1693f868860
--- /dev/null
+++ b/usr.bin/tmux/cmd-respawn-window.c
@@ -0,0 +1,87 @@
+/* $OpenBSD: cmd-respawn-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Respawn a window (restart the command). Kill existing if -k given.
+ */
+
+int cmd_respawn_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_respawn_window_entry = {
+ "respawn-window", "respawnw",
+ "[-k] " CMD_TARGET_WINDOW_USAGE " [command]",
+ CMD_ARG01|CMD_KFLAG,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_respawn_window_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct winlink *wl;
+ struct window *w;
+ struct window_pane *wp;
+ struct session *s;
+ const char **env;
+ char *cause;
+
+ if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL)
+ return (-1);
+ w = wl->window;
+
+ if (!(data->flags & CMD_KFLAG)) {
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp->fd == -1)
+ continue;
+ ctx->error(ctx,
+ "window still active: %s:%d", s->name, wl->idx);
+ return (-1);
+ }
+ }
+
+ env = server_fill_environ(s);
+
+ wp = TAILQ_FIRST(&w->panes);
+ TAILQ_REMOVE(&w->panes, wp, entry);
+ window_destroy_panes(w);
+ TAILQ_INSERT_HEAD(&w->panes, wp, entry);
+ window_pane_resize(wp, w->sx, w->sy);
+ if (window_pane_spawn(wp, data->arg, NULL, env, &cause) != 0) {
+ ctx->error(ctx, "respawn window failed: %s", cause);
+ xfree(cause);
+ return (-1);
+ }
+ screen_reinit(&wp->base);
+
+ recalculate_sizes();
+ server_redraw_window(w);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-rotate-window.c b/usr.bin/tmux/cmd-rotate-window.c
new file mode 100644
index 00000000000..abe78fdd62b
--- /dev/null
+++ b/usr.bin/tmux/cmd-rotate-window.c
@@ -0,0 +1,111 @@
+/* $OpenBSD: cmd-rotate-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Rotate the panes in a window.
+ */
+
+void cmd_rotate_window_init(struct cmd *, int);
+int cmd_rotate_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_rotate_window_entry = {
+ "rotate-window", "rotatew",
+ "[-DU] " CMD_TARGET_WINDOW_USAGE,
+ CMD_BIGUFLAG|CMD_BIGDFLAG,
+ cmd_rotate_window_init,
+ cmd_target_parse,
+ cmd_rotate_window_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+void
+cmd_rotate_window_init(struct cmd *self, int key)
+{
+ struct cmd_target_data *data;
+
+ cmd_target_init(self, key);
+ data = self->data;
+
+ if (key == KEYC_ADDESC('o'))
+ data->flags |= CMD_BIGDFLAG;
+}
+
+int
+cmd_rotate_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct winlink *wl;
+ struct window *w;
+ struct window_pane *wp, *wp2;
+ u_int sx, sy, xoff, yoff;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+ w = wl->window;
+
+ if (data->flags & CMD_BIGDFLAG) {
+ wp = TAILQ_LAST(&w->panes, window_panes);
+ TAILQ_REMOVE(&w->panes, wp, entry);
+ TAILQ_INSERT_HEAD(&w->panes, wp, entry);
+
+ xoff = wp->xoff; yoff = wp->yoff;
+ sx = wp->sx; sy = wp->sy;
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if ((wp2 = TAILQ_NEXT(wp, entry)) == NULL)
+ break;
+ wp->xoff = wp2->xoff; wp->yoff = wp2->yoff;
+ window_pane_resize(wp, wp2->sx, wp2->sy);
+ }
+ wp->xoff = xoff; wp->yoff = yoff;
+ window_pane_resize(wp, sx, sy);
+
+ if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL)
+ wp = TAILQ_LAST(&w->panes, window_panes);
+ window_set_active_pane(w, wp);
+ } else {
+ wp = TAILQ_FIRST(&w->panes);
+ TAILQ_REMOVE(&w->panes, wp, entry);
+ TAILQ_INSERT_TAIL(&w->panes, wp, entry);
+
+ xoff = wp->xoff; yoff = wp->yoff;
+ sx = wp->sx; sy = wp->sy;
+ TAILQ_FOREACH_REVERSE(wp, &w->panes, window_panes, entry) {
+ if ((wp2 = TAILQ_PREV(wp, window_panes, entry)) == NULL)
+ break;
+ wp->xoff = wp2->xoff; wp->yoff = wp2->yoff;
+ window_pane_resize(wp, wp2->sx, wp2->sy);
+ }
+ wp->xoff = xoff; wp->yoff = yoff;
+ window_pane_resize(wp, sx, sy);
+
+ if ((wp = TAILQ_NEXT(w->active, entry)) == NULL)
+ wp = TAILQ_FIRST(&w->panes);
+ window_set_active_pane(w, wp);
+ }
+
+ layout_refresh(w, 0);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-save-buffer.c b/usr.bin/tmux/cmd-save-buffer.c
new file mode 100644
index 00000000000..012e4031b37
--- /dev/null
+++ b/usr.bin/tmux/cmd-save-buffer.c
@@ -0,0 +1,90 @@
+/* $OpenBSD: cmd-save-buffer.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
+ *
+ * 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/stat.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Saves a session paste buffer to a file.
+ */
+
+int cmd_save_buffer_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_save_buffer_entry = {
+ "save-buffer", "saveb",
+ "[-a] " CMD_BUFFER_SESSION_USAGE " path",
+ CMD_AFLAG|CMD_ARG1,
+ cmd_buffer_init,
+ cmd_buffer_parse,
+ cmd_save_buffer_exec,
+ cmd_buffer_send,
+ cmd_buffer_recv,
+ cmd_buffer_free,
+ cmd_buffer_print
+};
+
+int
+cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_buffer_data *data = self->data;
+ struct session *s;
+ struct paste_buffer *pb;
+ mode_t mask;
+ FILE *f;
+
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
+ if (data->buffer == -1) {
+ if ((pb = paste_get_top(&s->buffers)) == NULL) {
+ ctx->error(ctx, "no buffers");
+ return (-1);
+ }
+ } else {
+ if ((pb = paste_get_index(&s->buffers, data->buffer)) == NULL) {
+ ctx->error(ctx, "no buffer %d", data->buffer);
+ return (-1);
+ }
+ }
+
+ mask = umask(S_IRWXG | S_IRWXO);
+ if (data->flags & CMD_AFLAG)
+ f = fopen(data->arg, "ab");
+ else
+ f = fopen(data->arg, "wb");
+ if (f == NULL) {
+ ctx->error(ctx, "%s: %s", data->arg, strerror(errno));
+ return (-1);
+ }
+
+ if (fwrite(pb->data, 1, strlen(pb->data), f) != strlen(pb->data)) {
+ ctx->error(ctx, "%s: fwrite error", data->arg);
+ fclose(f);
+ return (-1);
+ }
+
+ fclose(f);
+ umask(mask);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-scroll-mode.c b/usr.bin/tmux/cmd-scroll-mode.c
new file mode 100644
index 00000000000..2bbf6e864d0
--- /dev/null
+++ b/usr.bin/tmux/cmd-scroll-mode.c
@@ -0,0 +1,72 @@
+/* $OpenBSD: cmd-scroll-mode.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Enter scroll mode.
+ */
+
+void cmd_scroll_mode_init(struct cmd *, int);
+int cmd_scroll_mode_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_scroll_mode_entry = {
+ "scroll-mode", NULL,
+ CMD_TARGET_WINDOW_USAGE,
+ CMD_UFLAG,
+ cmd_scroll_mode_init,
+ cmd_target_parse,
+ cmd_scroll_mode_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+void
+cmd_scroll_mode_init(struct cmd *self, int key)
+{
+ struct cmd_target_data *data;
+
+ cmd_target_init(self, key);
+ data = self->data;
+
+ switch (key) {
+ case KEYC_PPAGE:
+ data->flags |= CMD_UFLAG;
+ break;
+ }
+}
+
+int
+cmd_scroll_mode_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct winlink *wl;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+
+ window_pane_set_mode(wl->window->active, &window_scroll_mode);
+ if (data->flags & CMD_UFLAG)
+ window_scroll_pageup(wl->window->active);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-select-layout.c b/usr.bin/tmux/cmd-select-layout.c
new file mode 100644
index 00000000000..213e7fd4cb4
--- /dev/null
+++ b/usr.bin/tmux/cmd-select-layout.c
@@ -0,0 +1,86 @@
+/* $OpenBSD: cmd-select-layout.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Switch window to selected layout.
+ */
+
+void cmd_select_layout_init(struct cmd *, int);
+int cmd_select_layout_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_select_layout_entry = {
+ "select-layout", "selectl",
+ CMD_TARGET_WINDOW_USAGE " layout-name",
+ CMD_ARG1,
+ cmd_select_layout_init,
+ cmd_target_parse,
+ cmd_select_layout_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+void
+cmd_select_layout_init(struct cmd *self, int key)
+{
+ struct cmd_target_data *data;
+
+ cmd_target_init(self, key);
+ data = self->data;
+
+ switch (key) {
+ case KEYC_ADDESC('0'):
+ data->arg = xstrdup("manual-vertical");
+ break;
+ case KEYC_ADDESC('1'):
+ data->arg = xstrdup("even-horizontal");
+ break;
+ case KEYC_ADDESC('2'):
+ data->arg = xstrdup("even-vertical");
+ break;
+ case KEYC_ADDESC('9'):
+ data->arg = xstrdup("active-only");
+ break;
+ }
+}
+
+int
+cmd_select_layout_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct winlink *wl;
+ int layout;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+
+ if ((layout = layout_lookup(data->arg)) == -1) {
+ ctx->error(ctx, "unknown or ambiguous layout: %s", data->arg);
+ return (-1);
+ }
+
+ if (layout_select(wl->window, layout) == 0)
+ ctx->info(ctx, "layout now: %s", layout_name(wl->window));
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-select-pane.c b/usr.bin/tmux/cmd-select-pane.c
new file mode 100644
index 00000000000..a153ad3b134
--- /dev/null
+++ b/usr.bin/tmux/cmd-select-pane.c
@@ -0,0 +1,69 @@
+/* $OpenBSD: cmd-select-pane.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Select pane.
+ */
+
+int cmd_select_pane_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_select_pane_entry = {
+ "select-pane", "selectp",
+ CMD_PANE_WINDOW_USAGE,
+ 0,
+ cmd_pane_init,
+ cmd_pane_parse,
+ cmd_select_pane_exec,
+ cmd_pane_send,
+ cmd_pane_recv,
+ cmd_pane_free,
+ cmd_pane_print
+};
+
+int
+cmd_select_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_pane_data *data = self->data;
+ struct winlink *wl;
+ struct window_pane *wp;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+ if (data->pane == -1)
+ wp = wl->window->active;
+ else {
+ wp = window_pane_at_index(wl->window, data->pane);
+ if (wp == NULL) {
+ ctx->error(ctx, "no pane: %d", data->pane);
+ return (-1);
+ }
+ }
+
+ if (wp->flags & PANE_HIDDEN) {
+ ctx->error(ctx, "pane %d is hidden", data->pane);
+ return (-1);
+ }
+ window_set_active_pane(wl->window, wp);
+ layout_refresh(wl->window, 1);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-select-prompt.c b/usr.bin/tmux/cmd-select-prompt.c
new file mode 100644
index 00000000000..ff28c5f36b2
--- /dev/null
+++ b/usr.bin/tmux/cmd-select-prompt.c
@@ -0,0 +1,93 @@
+/* $OpenBSD: cmd-select-prompt.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Prompt for window index and select it.
+ */
+
+int cmd_select_prompt_exec(struct cmd *, struct cmd_ctx *);
+
+int cmd_select_prompt_callback(void *, const char *);
+
+const struct cmd_entry cmd_select_prompt_entry = {
+ "select-prompt", NULL,
+ CMD_TARGET_CLIENT_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_select_prompt_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_select_prompt_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct client *c;
+
+ if ((c = cmd_find_client(ctx, data->target)) == NULL)
+ return (-1);
+
+ if (c->prompt_string != NULL)
+ return (0);
+
+ status_prompt_set(c, "index ", cmd_select_prompt_callback, c, 0);
+
+ return (0);
+}
+
+int
+cmd_select_prompt_callback(void *data, const char *s)
+{
+ struct client *c = data;
+ const char *errstr;
+ char msg[128];
+ u_int idx;
+
+ if (s == NULL)
+ return (0);
+
+ idx = strtonum(s, 0, UINT_MAX, &errstr);
+ if (errstr != NULL) {
+ xsnprintf(msg, sizeof msg, "Index %s: %s", errstr, s);
+ status_message_set(c, msg);
+ return (0);
+ }
+
+ if (winlink_find_by_index(&c->session->windows, idx) == NULL) {
+ xsnprintf(msg, sizeof msg,
+ "Window not found: %s:%d", c->session->name, idx);
+ status_message_set(c, msg);
+ return (0);
+ }
+
+ if (session_select(c->session, idx) == 0)
+ server_redraw_session(c->session);
+ recalculate_sizes();
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-select-window.c b/usr.bin/tmux/cmd-select-window.c
new file mode 100644
index 00000000000..8b7da8fe535
--- /dev/null
+++ b/usr.bin/tmux/cmd-select-window.c
@@ -0,0 +1,71 @@
+/* $OpenBSD: cmd-select-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Select window by index.
+ */
+
+void cmd_select_window_init(struct cmd *, int);
+int cmd_select_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_select_window_entry = {
+ "select-window", "selectw",
+ CMD_TARGET_WINDOW_USAGE,
+ 0,
+ cmd_select_window_init,
+ cmd_target_parse,
+ cmd_select_window_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+void
+cmd_select_window_init(struct cmd *self, int key)
+{
+ struct cmd_target_data *data;
+
+ cmd_target_init(self, key);
+ data = self->data;
+
+ xasprintf(&data->target, ":%d", key - '0');
+}
+
+int
+cmd_select_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct winlink *wl;
+ struct session *s;
+
+ if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL)
+ return (-1);
+
+ if (session_select(s, wl->idx) == 0)
+ server_redraw_session(s);
+ recalculate_sizes();
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-send-keys.c b/usr.bin/tmux/cmd-send-keys.c
new file mode 100644
index 00000000000..44a51b2839f
--- /dev/null
+++ b/usr.bin/tmux/cmd-send-keys.c
@@ -0,0 +1,184 @@
+/* $OpenBSD: cmd-send-keys.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Send keys to client.
+ */
+
+int cmd_send_keys_parse(struct cmd *, int, char **, char **);
+int cmd_send_keys_exec(struct cmd *, struct cmd_ctx *);
+void cmd_send_keys_send(struct cmd *, struct buffer *);
+void cmd_send_keys_recv(struct cmd *, struct buffer *);
+void cmd_send_keys_free(struct cmd *);
+size_t cmd_send_keys_print(struct cmd *, char *, size_t);
+
+struct cmd_send_keys_data {
+ char *target;
+ int idx;
+ u_int nkeys;
+ int *keys;
+};
+
+const struct cmd_entry cmd_send_keys_entry = {
+ "send-keys", "send",
+ "[-t target-window] key ...",
+ 0,
+ NULL,
+ cmd_send_keys_parse,
+ cmd_send_keys_exec,
+ cmd_send_keys_send,
+ cmd_send_keys_recv,
+ cmd_send_keys_free,
+ cmd_send_keys_print
+};
+
+int
+cmd_send_keys_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_send_keys_data *data;
+ int opt, key;
+ char *s;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->target = NULL;
+ data->idx = -1;
+ data->nkeys = 0;
+ data->keys = NULL;
+
+ while ((opt = getopt(argc, argv, "t:")) != -1) {
+ switch (opt) {
+ case 't':
+ if (data->target == NULL)
+ data->target = xstrdup(optarg);
+ break;
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc == 0)
+ goto usage;
+
+ while (argc-- != 0) {
+ if ((key = key_string_lookup_string(*argv)) != KEYC_NONE) {
+ data->keys = xrealloc(
+ data->keys, data->nkeys + 1, sizeof *data->keys);
+ data->keys[data->nkeys++] = key;
+ } else {
+ for (s = *argv; *s != '\0'; s++) {
+ data->keys = xrealloc(data->keys,
+ data->nkeys + 1, sizeof *data->keys);
+ data->keys[data->nkeys++] = *s;
+ }
+ }
+
+ argv++;
+ }
+
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+ self->entry->free(self);
+ return (-1);
+}
+
+int
+cmd_send_keys_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_send_keys_data *data = self->data;
+ struct winlink *wl;
+ u_int i;
+
+ if (data == NULL)
+ return (-1);
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+
+ for (i = 0; i < data->nkeys; i++) {
+ window_pane_key(
+ wl->window->active, ctx->curclient, data->keys[i]);
+ }
+
+ return (0);
+}
+
+void
+cmd_send_keys_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_send_keys_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_send_string(b, data->target);
+ buffer_write(b, data->keys, data->nkeys * sizeof *data->keys);
+}
+
+void
+cmd_send_keys_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_send_keys_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->target = cmd_recv_string(b);
+ data->keys = xcalloc(data->nkeys, sizeof *data->keys);
+ buffer_read(b, data->keys, data->nkeys * sizeof *data->keys);
+}
+
+void
+cmd_send_keys_free(struct cmd *self)
+{
+ struct cmd_send_keys_data *data = self->data;
+
+ if (data->target != NULL)
+ xfree(data->target);
+ xfree(data);
+}
+
+size_t
+cmd_send_keys_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_send_keys_data *data = self->data;
+ size_t off = 0;
+ u_int i;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ if (off < len && data->target != NULL)
+ off += cmd_prarg(buf + off, len - off, " -t ", data->target);
+ if (off < len && data->idx != -1)
+ off += xsnprintf(buf + off, len - off, " -i %d", data->idx);
+
+ for (i = 0; i < data->nkeys; i++) {
+ if (off >= len)
+ break;
+ off += xsnprintf(buf + off,
+ len - off, " %s", key_string_lookup_key(data->keys[i]));
+ }
+ return (off);
+}
diff --git a/usr.bin/tmux/cmd-send-prefix.c b/usr.bin/tmux/cmd-send-prefix.c
new file mode 100644
index 00000000000..dcb61247338
--- /dev/null
+++ b/usr.bin/tmux/cmd-send-prefix.c
@@ -0,0 +1,57 @@
+/* $OpenBSD: cmd-send-prefix.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Send prefix key as a key.
+ */
+
+int cmd_send_prefix_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_send_prefix_entry = {
+ "send-prefix", NULL,
+ CMD_TARGET_WINDOW_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_send_prefix_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_send_prefix_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct session *s;
+ struct winlink *wl;
+ int key;
+
+ if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL)
+ return (-1);
+
+ key = options_get_number(&s->options, "prefix");
+ window_pane_key(wl->window->active, ctx->curclient, key);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-server-info.c b/usr.bin/tmux/cmd-server-info.c
new file mode 100644
index 00000000000..653ff3533f3
--- /dev/null
+++ b/usr.bin/tmux/cmd-server-info.c
@@ -0,0 +1,179 @@
+/* $OpenBSD: cmd-server-info.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/utsname.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Show various information about server.
+ */
+
+int cmd_server_info_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_server_info_entry = {
+ "server-info", "info",
+ "",
+ 0,
+ NULL,
+ NULL,
+ cmd_server_info_exec,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+int
+cmd_server_info_exec(unused struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct tty_term *term;
+ struct client *c;
+ struct session *s;
+ struct winlink *wl;
+ struct window *w;
+ struct window_pane *wp;
+ struct tty_code *code;
+ struct tty_term_code_entry *ent;
+ struct utsname un;
+ struct grid *gd;
+ u_int i, j, k;
+ char out[80];
+ char *tim;
+ time_t t;
+ u_int lines, ulines;
+ size_t size, usize;
+
+ tim = ctime(&start_time);
+ *strchr(tim, '\n') = '\0';
+ ctx->print(ctx, "pid %ld, started %s", (long) getpid(), tim);
+ ctx->print(ctx, "socket path %s, debug level %d%s",
+ socket_path, debug_level, be_quiet ? ", quiet" : "");
+ if (uname(&un) == 0) {
+ ctx->print(ctx, "system is %s %s %s %s",
+ un.sysname, un.release, un.version, un.machine);
+ }
+ if (cfg_file != NULL)
+ ctx->print(ctx, "configuration file is %s", cfg_file);
+ else
+ ctx->print(ctx, "configuration file not specified");
+ ctx->print(ctx, "protocol version is %d", PROTOCOL_VERSION);
+ ctx->print(ctx, "%u clients, %u sessions",
+ ARRAY_LENGTH(&clients), ARRAY_LENGTH(&sessions));
+ ctx->print(ctx, "");
+
+ ctx->print(ctx, "Clients:");
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session == NULL)
+ continue;
+
+ ctx->print(ctx, "%2d: %s (%d, %d): %s [%ux%u %s] "
+ "[flags=0x%x/0x%x]", i, c->tty.path, c->fd, c->tty.fd,
+ c->session->name, c->tty.sx, c->tty.sy, c->tty.termname,
+ c->flags, c->tty.flags);
+ }
+ ctx->print(ctx, "");
+
+ ctx->print(ctx, "Sessions: [%zu/%zu]",
+ sizeof (struct grid_cell), sizeof (struct grid_utf8));
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ s = ARRAY_ITEM(&sessions, i);
+ if (s == NULL)
+ continue;
+
+ t = s->tv.tv_sec;
+ tim = ctime(&t);
+ *strchr(tim, '\n') = '\0';
+
+ ctx->print(ctx, "%2u: %s: %u windows (created %s) [%ux%u] "
+ "[flags=0x%x]", i, s->name, winlink_count(&s->windows),
+ tim, s->sx, s->sy, s->flags);
+ RB_FOREACH(wl, winlinks, &s->windows) {
+ w = wl->window;
+ ctx->print(ctx, "%4u: %s [%ux%u] [flags=0x%x, "
+ "references=%u, layout=%u]", wl->idx, w->name,
+ w->sx, w->sy, w->flags, w->references,
+ w->layout);
+ j = 0;
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ lines = ulines = size = usize = 0;
+ gd = wp->base.grid;
+ for (k = 0; k < gd->hsize + gd->sy; k++) {
+ if (gd->data[k] != NULL) {
+ lines++;
+ size += gd->size[k] *
+ sizeof (**gd->data);
+ }
+ if (gd->udata[k] != NULL) {
+ ulines++;
+ usize += gd->usize[k] *
+ sizeof (**gd->udata);
+ }
+ }
+ ctx->print(ctx, "%6u: %s %lu %d %u/%u, %zu "
+ "bytes; UTF-8 %u/%u, %zu bytes", j,
+ wp->tty, (u_long) wp->pid, wp->fd, lines,
+ gd->hsize + gd->sy, size, ulines,
+ gd->hsize + gd->sy, usize);
+ j++;
+ }
+ }
+ }
+ ctx->print(ctx, "");
+
+ ctx->print(ctx, "Terminals:");
+ SLIST_FOREACH(term, &tty_terms, entry) {
+ ctx->print(ctx, "%s [references=%u, flags=0x%x]:",
+ term->name, term->references, term->flags);
+ for (i = 0; i < NTTYCODE; i++) {
+ ent = &tty_term_codes[i];
+ code = &term->codes[ent->code];
+ switch (code->type) {
+ case TTYCODE_NONE:
+ ctx->print(ctx, "%2u: %s: [missing]",
+ ent->code, ent->name);
+ break;
+ case TTYCODE_STRING:
+ clean_string(
+ code->value.string, out, sizeof out);
+ ctx->print(ctx, "%2u: %s: (string) %s",
+ ent->code, ent->name, out);
+ break;
+ case TTYCODE_NUMBER:
+ ctx->print(ctx, "%2u: %s: (number) %d",
+ ent->code, ent->name, code->value.number);
+ break;
+ case TTYCODE_FLAG:
+ ctx->print(ctx, "%2u: %s: (flag) %s",
+ ent->code, ent->name,
+ code->value.flag ? "true" : "false");
+ break;
+ }
+ }
+ }
+ ctx->print(ctx, "");
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-set-buffer.c b/usr.bin/tmux/cmd-set-buffer.c
new file mode 100644
index 00000000000..bd9e1124c27
--- /dev/null
+++ b/usr.bin/tmux/cmd-set-buffer.c
@@ -0,0 +1,64 @@
+/* $OpenBSD: cmd-set-buffer.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Add or set a session paste buffer.
+ */
+
+int cmd_set_buffer_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_set_buffer_entry = {
+ "set-buffer", "setb",
+ CMD_BUFFER_SESSION_USAGE " data",
+ CMD_ARG1,
+ cmd_buffer_init,
+ cmd_buffer_parse,
+ cmd_set_buffer_exec,
+ cmd_buffer_send,
+ cmd_buffer_recv,
+ cmd_buffer_free,
+ cmd_buffer_print
+};
+
+int
+cmd_set_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_buffer_data *data = self->data;
+ struct session *s;
+ u_int limit;
+
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
+ limit = options_get_number(&s->options, "buffer-limit");
+ if (data->buffer == -1) {
+ paste_add(&s->buffers, xstrdup(data->arg), limit);
+ return (0);
+ }
+ if (paste_replace(&s->buffers, data->buffer, xstrdup(data->arg)) != 0) {
+ ctx->error(ctx, "no buffer %d", data->buffer);
+ return (-1);
+ }
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-set-option.c b/usr.bin/tmux/cmd-set-option.c
new file mode 100644
index 00000000000..da5cd4ef09c
--- /dev/null
+++ b/usr.bin/tmux/cmd-set-option.c
@@ -0,0 +1,169 @@
+/* $OpenBSD: cmd-set-option.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Set an option.
+ */
+
+int cmd_set_option_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_set_option_entry = {
+ "set-option", "set",
+ CMD_OPTION_SESSION_USAGE,
+ CMD_GFLAG|CMD_UFLAG,
+ NULL,
+ cmd_option_parse,
+ cmd_set_option_exec,
+ cmd_option_send,
+ cmd_option_recv,
+ cmd_option_free,
+ cmd_option_print
+};
+
+const char *set_option_status_keys_list[] = {
+ "emacs", "vi", NULL
+};
+const char *set_option_bell_action_list[] = {
+ "none", "any", "current", NULL
+};
+const struct set_option_entry set_option_table[NSETOPTION] = {
+ { "bell-action", SET_OPTION_CHOICE, 0, 0, set_option_bell_action_list },
+ { "buffer-limit", SET_OPTION_NUMBER, 1, INT_MAX, NULL },
+ { "default-command", SET_OPTION_STRING, 0, 0, NULL },
+ { "default-path", SET_OPTION_STRING, 0, 0, NULL },
+ { "display-time", SET_OPTION_NUMBER, 1, INT_MAX, NULL },
+ { "history-limit", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
+ { "lock-after-time", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
+ { "message-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL },
+ { "message-bg", SET_OPTION_COLOUR, 0, 0, NULL },
+ { "message-fg", SET_OPTION_COLOUR, 0, 0, NULL },
+ { "prefix", SET_OPTION_KEY, 0, 0, NULL },
+ { "repeat-time", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL },
+ { "set-remain-on-exit", SET_OPTION_FLAG, 0, 0, NULL },
+ { "set-titles", SET_OPTION_FLAG, 0, 0, NULL },
+ { "status", SET_OPTION_FLAG, 0, 0, NULL },
+ { "status-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL },
+ { "status-bg", SET_OPTION_COLOUR, 0, 0, NULL },
+ { "status-fg", SET_OPTION_COLOUR, 0, 0, NULL },
+ { "status-interval", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
+ { "status-keys", SET_OPTION_CHOICE, 0, 0, set_option_status_keys_list },
+ { "status-left", SET_OPTION_STRING, 0, 0, NULL },
+ { "status-left-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL },
+ { "status-right", SET_OPTION_STRING, 0, 0, NULL },
+ { "status-right-length", SET_OPTION_NUMBER, 0, SHRT_MAX, NULL },
+};
+
+int
+cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_option_data *data = self->data;
+ struct session *s;
+ struct client *c;
+ struct options *oo;
+ const struct set_option_entry *entry;
+ u_int i;
+
+ if (data->flags & CMD_GFLAG)
+ oo = &global_options;
+ else {
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+ oo = &s->options;
+ }
+
+ if (*data->option == '\0') {
+ ctx->error(ctx, "invalid option");
+ return (-1);
+ }
+
+ entry = NULL;
+ for (i = 0; i < NSETOPTION; i++) {
+ if (strncmp(set_option_table[i].name,
+ data->option, strlen(data->option)) != 0)
+ continue;
+ if (entry != NULL) {
+ ctx->error(ctx, "ambiguous option: %s", data->option);
+ return (-1);
+ }
+ entry = &set_option_table[i];
+
+ /* Bail now if an exact match. */
+ if (strcmp(entry->name, data->option) == 0)
+ break;
+ }
+ if (entry == NULL) {
+ ctx->error(ctx, "unknown option: %s", data->option);
+ return (-1);
+ }
+
+ if (data->flags & CMD_UFLAG) {
+ if (data->flags & CMD_GFLAG) {
+ ctx->error(ctx,
+ "can't unset global option: %s", entry->name);
+ return (-1);
+ }
+ if (data->value != NULL) {
+ ctx->error(ctx,
+ "value passed to unset option: %s", entry->name);
+ return (-1);
+ }
+
+ options_remove(oo, entry->name);
+ ctx->info(ctx, "unset option: %s", entry->name);
+ } else {
+ switch (entry->type) {
+ case SET_OPTION_STRING:
+ set_option_string(ctx, oo, entry, data->value);
+ break;
+ case SET_OPTION_NUMBER:
+ set_option_number(ctx, oo, entry, data->value);
+ break;
+ case SET_OPTION_KEY:
+ set_option_key(ctx, oo, entry, data->value);
+ break;
+ case SET_OPTION_COLOUR:
+ set_option_colour(ctx, oo, entry, data->value);
+ break;
+ case SET_OPTION_ATTRIBUTES:
+ set_option_attributes(ctx, oo, entry, data->value);
+ break;
+ case SET_OPTION_FLAG:
+ set_option_flag(ctx, oo, entry, data->value);
+ break;
+ case SET_OPTION_CHOICE:
+ set_option_choice(ctx, oo, entry, data->value);
+ break;
+ }
+ }
+
+ recalculate_sizes();
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c != NULL && c->session != NULL)
+ server_redraw_client(c);
+ }
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-set-password.c b/usr.bin/tmux/cmd-set-password.c
new file mode 100644
index 00000000000..262ff7be794
--- /dev/null
+++ b/usr.bin/tmux/cmd-set-password.c
@@ -0,0 +1,169 @@
+/* $OpenBSD: cmd-set-password.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <pwd.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Set server password.
+ */
+
+int cmd_set_password_parse(struct cmd *, int, char **, char **);
+int cmd_set_password_exec(struct cmd *, struct cmd_ctx *);
+void cmd_set_password_send(struct cmd *, struct buffer *);
+void cmd_set_password_recv(struct cmd *, struct buffer *);
+void cmd_set_password_free(struct cmd *);
+void cmd_set_password_init(struct cmd *, int);
+size_t cmd_set_password_print(struct cmd *, char *, size_t);
+
+struct cmd_set_password_data {
+ char *password;
+ int flag_encrypted;
+};
+
+const struct cmd_entry cmd_set_password_entry = {
+ "set-password", "pass",
+ "[-c] password",
+ 0,
+ cmd_set_password_init,
+ cmd_set_password_parse,
+ cmd_set_password_exec,
+ cmd_set_password_send,
+ cmd_set_password_recv,
+ cmd_set_password_free,
+ cmd_set_password_print
+};
+
+void
+cmd_set_password_init(struct cmd *self, unused int arg)
+{
+ struct cmd_set_password_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->password = NULL;
+ data->flag_encrypted = 0;
+}
+
+int
+cmd_set_password_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_set_password_data *data;
+ int opt;
+ char *out;
+
+ self->entry->init(self, 0);
+ data = self->data;
+
+ while ((opt = getopt(argc, argv, "c")) != -1) {
+ switch (opt) {
+ case 'c':
+ data->flag_encrypted = 1;
+ break;
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ goto usage;
+
+ if (!data->flag_encrypted) {
+ if ((out = crypt(argv[0], "$1")) != NULL)
+ data->password = xstrdup(out);
+ } else
+ data->password = xstrdup(argv[0]);
+
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+ self->entry->free(self);
+ return (-1);
+}
+
+int
+cmd_set_password_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_set_password_data *data = self->data;
+
+ if (data->password == NULL) {
+ ctx->error(ctx, "failed to encrypt password");
+ return (-1);
+ }
+
+ if (server_password != NULL)
+ xfree(server_password);
+ if (*data->password == '\0')
+ server_password = NULL;
+ else
+ server_password = xstrdup(data->password);
+ log_debug("pw now %s", server_password);
+
+ return (0);
+}
+
+void
+cmd_set_password_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_set_password_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_send_string(b, data->password);
+}
+
+void
+cmd_set_password_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_set_password_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->password = cmd_recv_string(b);
+}
+
+void
+cmd_set_password_free(struct cmd *self)
+{
+ struct cmd_set_password_data *data = self->data;
+
+ if (data->password != NULL)
+ xfree(data->password);
+ xfree(data);
+}
+
+size_t
+cmd_set_password_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_set_password_data *data = self->data;
+ size_t off = 0;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ if (off < len && data->flag_encrypted)
+ off += xsnprintf(buf + off, len - off, " -c");
+ if (off < len && data->password != NULL)
+ off += xsnprintf(buf + off, len - off, " password");
+ return (off);
+}
diff --git a/usr.bin/tmux/cmd-set-window-option.c b/usr.bin/tmux/cmd-set-window-option.c
new file mode 100644
index 00000000000..9995fd3c2cf
--- /dev/null
+++ b/usr.bin/tmux/cmd-set-window-option.c
@@ -0,0 +1,170 @@
+/* $OpenBSD: cmd-set-window-option.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Set a window option.
+ */
+
+int cmd_set_window_option_parse(struct cmd *, int, char **, char **);
+int cmd_set_window_option_exec(struct cmd *, struct cmd_ctx *);
+void cmd_set_window_option_send(struct cmd *, struct buffer *);
+void cmd_set_window_option_recv(struct cmd *, struct buffer *);
+void cmd_set_window_option_free(struct cmd *);
+size_t cmd_set_window_option_print(struct cmd *, char *, size_t);
+
+const struct cmd_entry cmd_set_window_option_entry = {
+ "set-window-option", "setw",
+ CMD_OPTION_WINDOW_USAGE,
+ CMD_GFLAG|CMD_UFLAG,
+ NULL,
+ cmd_option_parse,
+ cmd_set_window_option_exec,
+ cmd_option_send,
+ cmd_option_recv,
+ cmd_option_free,
+ cmd_option_print
+};
+
+const char *set_option_mode_keys_list[] = {
+ "emacs", "vi", NULL
+};
+const char *set_option_clock_mode_style_list[] = {
+ "12", "24", NULL
+};
+const struct set_option_entry set_window_option_table[NSETWINDOWOPTION] = {
+ { "aggressive-resize", SET_OPTION_FLAG, 0, 0, NULL },
+ { "automatic-rename", SET_OPTION_FLAG, 0, 0, NULL },
+ { "clock-mode-colour", SET_OPTION_COLOUR, 0, 0, NULL },
+ { "clock-mode-style",
+ SET_OPTION_CHOICE, 0, 0, set_option_clock_mode_style_list },
+ { "force-height", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
+ { "force-width", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
+ { "main-pane-width", SET_OPTION_NUMBER, 1, INT_MAX, NULL },
+ { "mode-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL },
+ { "mode-bg", SET_OPTION_COLOUR, 0, 0, NULL },
+ { "mode-fg", SET_OPTION_COLOUR, 0, 0, NULL },
+ { "mode-keys", SET_OPTION_CHOICE, 0, 0, set_option_mode_keys_list },
+ { "monitor-activity", SET_OPTION_FLAG, 0, 0, NULL },
+ { "monitor-content", SET_OPTION_STRING, 0, 0, NULL },
+ { "remain-on-exit", SET_OPTION_FLAG, 0, 0, NULL },
+ { "utf8", SET_OPTION_FLAG, 0, 0, NULL },
+ { "window-status-attr", SET_OPTION_ATTRIBUTES, 0, 0, NULL },
+ { "window-status-bg", SET_OPTION_COLOUR, 0, 0, NULL },
+ { "window-status-fg", SET_OPTION_COLOUR, 0, 0, NULL },
+ { "xterm-keys", SET_OPTION_FLAG, 0, 0, NULL },
+};
+
+int
+cmd_set_window_option_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_option_data *data = self->data;
+ struct winlink *wl;
+ struct client *c;
+ struct options *oo;
+ const struct set_option_entry *entry;
+ u_int i;
+
+ if (data->flags & CMD_GFLAG)
+ oo = &global_window_options;
+ else {
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+ oo = &wl->window->options;
+ }
+
+ if (*data->option == '\0') {
+ ctx->error(ctx, "invalid option");
+ return (-1);
+ }
+
+ entry = NULL;
+ for (i = 0; i < NSETWINDOWOPTION; i++) {
+ if (strncmp(set_window_option_table[i].name,
+ data->option, strlen(data->option)) != 0)
+ continue;
+ if (entry != NULL) {
+ ctx->error(ctx, "ambiguous option: %s", data->option);
+ return (-1);
+ }
+ entry = &set_window_option_table[i];
+
+ /* Bail now if an exact match. */
+ if (strcmp(entry->name, data->option) == 0)
+ break;
+ }
+ if (entry == NULL) {
+ ctx->error(ctx, "unknown option: %s", data->option);
+ return (-1);
+ }
+
+ if (data->flags & CMD_UFLAG) {
+ if (data->flags & CMD_GFLAG) {
+ ctx->error(ctx,
+ "can't unset global option: %s", entry->name);
+ return (-1);
+ }
+ if (data->value != NULL) {
+ ctx->error(ctx,
+ "value passed to unset option: %s", entry->name);
+ return (-1);
+ }
+
+ options_remove(oo, entry->name);
+ ctx->info(ctx, "unset option: %s", entry->name);
+ } else {
+ switch (entry->type) {
+ case SET_OPTION_STRING:
+ set_option_string(ctx, oo, entry, data->value);
+ break;
+ case SET_OPTION_NUMBER:
+ set_option_number(ctx, oo, entry, data->value);
+ break;
+ case SET_OPTION_KEY:
+ set_option_key(ctx, oo, entry, data->value);
+ break;
+ case SET_OPTION_COLOUR:
+ set_option_colour(ctx, oo, entry, data->value);
+ break;
+ case SET_OPTION_ATTRIBUTES:
+ set_option_attributes(ctx, oo, entry, data->value);
+ break;
+ case SET_OPTION_FLAG:
+ set_option_flag(ctx, oo, entry, data->value);
+ break;
+ case SET_OPTION_CHOICE:
+ set_option_choice(ctx, oo, entry, data->value);
+ break;
+ }
+ }
+
+ recalculate_sizes();
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c != NULL && c->session != NULL)
+ server_redraw_client(c);
+ }
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-show-buffer.c b/usr.bin/tmux/cmd-show-buffer.c
new file mode 100644
index 00000000000..30c28eafd86
--- /dev/null
+++ b/usr.bin/tmux/cmd-show-buffer.c
@@ -0,0 +1,89 @@
+/* $OpenBSD: cmd-show-buffer.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Show a session paste buffer.
+ */
+
+int cmd_show_buffer_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_show_buffer_entry = {
+ "show-buffer", "showb",
+ CMD_BUFFER_SESSION_USAGE,
+ 0,
+ cmd_buffer_init,
+ cmd_buffer_parse,
+ cmd_show_buffer_exec,
+ cmd_buffer_send,
+ cmd_buffer_recv,
+ cmd_buffer_free,
+ cmd_buffer_print
+};
+
+int
+cmd_show_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_buffer_data *data = self->data;
+ struct session *s;
+ struct paste_buffer *pb;
+ u_int size;
+ char *buf, *ptr;
+ size_t len;
+
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
+ if (data->buffer == -1) {
+ if ((pb = paste_get_top(&s->buffers)) == NULL) {
+ ctx->error(ctx, "no buffers");
+ return (-1);
+ }
+ } else if ((pb = paste_get_index(&s->buffers, data->buffer)) == NULL) {
+ ctx->error(ctx, "no buffer %d", data->buffer);
+ return (-1);
+ }
+
+ if (pb != NULL) {
+ size = s->sx;
+
+ buf = xmalloc(size + 1);
+ len = 0;
+
+ ptr = pb->data;
+ do {
+ buf[len++] = *ptr++;
+
+ if (len == size) {
+ buf[len] = '\0';
+ ctx->print(ctx, buf);
+
+ len = 0;
+ }
+ } while (*ptr != '\0');
+ buf[len] = '\0';
+ ctx->print(ctx, buf);
+ }
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-show-options.c b/usr.bin/tmux/cmd-show-options.c
new file mode 100644
index 00000000000..862e666bbfd
--- /dev/null
+++ b/usr.bin/tmux/cmd-show-options.c
@@ -0,0 +1,110 @@
+/* $OpenBSD: cmd-show-options.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Show options.
+ */
+
+int cmd_show_options_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_show_options_entry = {
+ "show-options", "show",
+ "[-g] " CMD_TARGET_SESSION_USAGE,
+ CMD_GFLAG,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_show_options_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_show_options_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct session *s;
+ struct options *oo;
+ const struct set_option_entry *entry;
+ u_int i;
+ char *vs;
+ long long vn;
+
+ if (data->flags & CMD_GFLAG)
+ oo = &global_options;
+ else {
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+ oo = &s->options;
+ }
+
+ for (i = 0; i < NSETOPTION; i++) {
+ entry = &set_option_table[i];
+
+ if (options_find1(oo, entry->name) == NULL)
+ continue;
+
+ switch (entry->type) {
+ case SET_OPTION_STRING:
+ vs = options_get_string(oo, entry->name);
+ ctx->print(ctx, "%s \"%s\"", entry->name, vs);
+ break;
+ case SET_OPTION_NUMBER:
+ vn = options_get_number(oo, entry->name);
+ ctx->print(ctx, "%s %lld", entry->name, vn);
+ break;
+ case SET_OPTION_KEY:
+ vn = options_get_number(oo, entry->name);
+ ctx->print(ctx, "%s %s",
+ entry->name, key_string_lookup_key(vn));
+ break;
+ case SET_OPTION_COLOUR:
+ vn = options_get_number(oo, entry->name);
+ ctx->print(ctx, "%s %s",
+ entry->name, colour_tostring(vn));
+ break;
+ case SET_OPTION_ATTRIBUTES:
+ vn = options_get_number(oo, entry->name);
+ ctx->print(ctx, "%s %s",
+ entry->name, attributes_tostring(vn));
+ break;
+ case SET_OPTION_FLAG:
+ vn = options_get_number(oo, entry->name);
+ if (vn)
+ ctx->print(ctx, "%s on", entry->name);
+ else
+ ctx->print(ctx, "%s off", entry->name);
+ break;
+ case SET_OPTION_CHOICE:
+ vn = options_get_number(oo, entry->name);
+ ctx->print(ctx, "%s %s",
+ entry->name, entry->choices[vn]);
+ break;
+ }
+ }
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-show-window-options.c b/usr.bin/tmux/cmd-show-window-options.c
new file mode 100644
index 00000000000..02aa4472343
--- /dev/null
+++ b/usr.bin/tmux/cmd-show-window-options.c
@@ -0,0 +1,110 @@
+/* $OpenBSD: cmd-show-window-options.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Show window options.
+ */
+
+int cmd_show_window_options_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_show_window_options_entry = {
+ "show-window-options", "showw",
+ "[-g] " CMD_TARGET_WINDOW_USAGE,
+ CMD_GFLAG,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_show_window_options_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_show_window_options_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct winlink *wl;
+ struct options *oo;
+ const struct set_option_entry *entry;
+ u_int i;
+ char *vs;
+ long long vn;
+
+ if (data->flags & CMD_GFLAG)
+ oo = &global_window_options;
+ else {
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+ oo = &wl->window->options;
+ }
+
+ for (i = 0; i < NSETWINDOWOPTION; i++) {
+ entry = &set_window_option_table[i];
+
+ if (options_find1(oo, entry->name) == NULL)
+ continue;
+
+ switch (entry->type) {
+ case SET_OPTION_STRING:
+ vs = options_get_string(oo, entry->name);
+ ctx->print(ctx, "%s \"%s\"", entry->name, vs);
+ break;
+ case SET_OPTION_NUMBER:
+ vn = options_get_number(oo, entry->name);
+ ctx->print(ctx, "%s %lld", entry->name, vn);
+ break;
+ case SET_OPTION_KEY:
+ vn = options_get_number(oo, entry->name);
+ ctx->print(ctx, "%s %s",
+ entry->name, key_string_lookup_key(vn));
+ break;
+ case SET_OPTION_COLOUR:
+ vn = options_get_number(oo, entry->name);
+ ctx->print(ctx, "%s %s",
+ entry->name, colour_tostring(vn));
+ break;
+ case SET_OPTION_ATTRIBUTES:
+ vn = options_get_number(oo, entry->name);
+ ctx->print(ctx, "%s %s",
+ entry->name, attributes_tostring(vn));
+ break;
+ case SET_OPTION_FLAG:
+ vn = options_get_number(oo, entry->name);
+ if (vn)
+ ctx->print(ctx, "%s on", entry->name);
+ else
+ ctx->print(ctx, "%s off", entry->name);
+ break;
+ case SET_OPTION_CHOICE:
+ vn = options_get_number(oo, entry->name);
+ ctx->print(ctx, "%s %s",
+ entry->name, entry->choices[vn]);
+ break;
+ }
+ }
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-source-file.c b/usr.bin/tmux/cmd-source-file.c
new file mode 100644
index 00000000000..d26d5213418
--- /dev/null
+++ b/usr.bin/tmux/cmd-source-file.c
@@ -0,0 +1,147 @@
+/* $OpenBSD: cmd-source-file.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Tiago Cunha <me@tiagocunha.org>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Sources a configuration file.
+ */
+
+int cmd_source_file_parse(struct cmd *, int, char **, char **);
+int cmd_source_file_exec(struct cmd *, struct cmd_ctx *);
+void cmd_source_file_send(struct cmd *, struct buffer *);
+void cmd_source_file_recv(struct cmd *, struct buffer *);
+void cmd_source_file_free(struct cmd *);
+void cmd_source_file_init(struct cmd *, int);
+size_t cmd_source_file_print(struct cmd *, char *, size_t);
+
+struct cmd_source_file_data {
+ char *path;
+};
+
+const struct cmd_entry cmd_source_file_entry = {
+ "source-file", "source",
+ "path",
+ 0,
+ cmd_source_file_init,
+ cmd_source_file_parse,
+ cmd_source_file_exec,
+ cmd_source_file_send,
+ cmd_source_file_recv,
+ cmd_source_file_free,
+ cmd_source_file_print
+};
+
+void
+cmd_source_file_init(struct cmd *self, unused int arg)
+{
+ struct cmd_source_file_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->path = NULL;
+}
+
+int
+cmd_source_file_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_source_file_data *data;
+ int opt;
+
+ self->entry->init(self, 0);
+ data = self->data;
+
+ while ((opt = getopt(argc, argv, "")) != -1) {
+ switch (opt) {
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ goto usage;
+
+ data->path = xstrdup(argv[0]);
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+ self->entry->free(self);
+ return (-1);
+}
+
+int
+cmd_source_file_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_source_file_data *data = self->data;
+ char *cause;
+
+ if (load_cfg(data->path, &cause) != 0) {
+ ctx->error(ctx, "%s", cause);
+ xfree(cause);
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+cmd_source_file_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_source_file_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_send_string(b, data->path);
+}
+
+void
+cmd_source_file_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_source_file_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->path = cmd_recv_string(b);
+}
+
+void
+cmd_source_file_free(struct cmd *self)
+{
+ struct cmd_source_file_data *data = self->data;
+
+ if (data->path != NULL)
+ xfree(data->path);
+ xfree(data);
+}
+
+size_t
+cmd_source_file_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_source_file_data *data = self->data;
+ size_t off = 0;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ if (off < len && data->path != NULL)
+ off += cmd_prarg(buf + off, len - off, " ", data->path);
+ return (off);
+}
diff --git a/usr.bin/tmux/cmd-split-window.c b/usr.bin/tmux/cmd-split-window.c
new file mode 100644
index 00000000000..fd41034f279
--- /dev/null
+++ b/usr.bin/tmux/cmd-split-window.c
@@ -0,0 +1,235 @@
+/* $OpenBSD: cmd-split-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Split a window (add a new pane).
+ */
+
+int cmd_split_window_parse(struct cmd *, int, char **, char **);
+int cmd_split_window_exec(struct cmd *, struct cmd_ctx *);
+void cmd_split_window_send(struct cmd *, struct buffer *);
+void cmd_split_window_recv(struct cmd *, struct buffer *);
+void cmd_split_window_free(struct cmd *);
+void cmd_split_window_init(struct cmd *, int);
+size_t cmd_split_window_print(struct cmd *, char *, size_t);
+
+struct cmd_split_window_data {
+ char *target;
+ char *cmd;
+ int flag_detached;
+ int percentage;
+ int lines;
+};
+
+const struct cmd_entry cmd_split_window_entry = {
+ "split-window", "splitw",
+ "[-d] [-p percentage|-l lines] [-t target-window] [command]",
+ 0,
+ cmd_split_window_init,
+ cmd_split_window_parse,
+ cmd_split_window_exec,
+ cmd_split_window_send,
+ cmd_split_window_recv,
+ cmd_split_window_free,
+ cmd_split_window_print
+};
+
+void
+cmd_split_window_init(struct cmd *self, unused int arg)
+{
+ struct cmd_split_window_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->target = NULL;
+ data->cmd = NULL;
+ data->flag_detached = 0;
+ data->percentage = -1;
+ data->lines = -1;
+}
+
+int
+cmd_split_window_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_split_window_data *data;
+ int opt, n;
+ const char *errstr;
+
+ self->entry->init(self, 0);
+ data = self->data;
+
+ while ((opt = getopt(argc, argv, "dl:p:t:")) != -1) {
+ switch (opt) {
+ case 'd':
+ data->flag_detached = 1;
+ break;
+ case 't':
+ if (data->target == NULL)
+ data->target = xstrdup(optarg);
+ break;
+ case 'l':
+ if (data->percentage == -1 && data->lines == -1) {
+ n = strtonum(optarg, 1, INT_MAX, &errstr);
+ if (errstr != NULL) {
+ xasprintf(cause, "lines %s", errstr);
+ goto error;
+ }
+ data->lines = n;
+ }
+ break;
+ case 'p':
+ if (data->lines == -1 && data->percentage == -1) {
+ n = strtonum(optarg, 1, 100, &errstr);
+ if (errstr != NULL) {
+ xasprintf(
+ cause, "percentage %s", errstr);
+ goto error;
+ }
+ data->percentage = n;
+ }
+ break;
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 0 && argc != 1)
+ goto usage;
+
+ if (argc == 1)
+ data->cmd = xstrdup(argv[0]);
+
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+error:
+ self->entry->free(self);
+ return (-1);
+}
+
+int
+cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_split_window_data *data = self->data;
+ struct session *s;
+ struct winlink *wl;
+ struct window *w;
+ struct window_pane *wp;
+ const char **env;
+ char *cmd, *cwd, *cause;
+ u_int hlimit, lines;
+
+ if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL)
+ return (-1);
+ w = wl->window;
+
+ env = server_fill_environ(s);
+
+ cmd = data->cmd;
+ if (cmd == NULL)
+ cmd = options_get_string(&s->options, "default-command");
+ if (ctx->cmdclient == NULL || ctx->cmdclient->cwd == NULL)
+ cwd = options_get_string(&global_options, "default-path");
+ else
+ cwd = ctx->cmdclient->cwd;
+
+ lines = -1;
+ if (data->lines != -1)
+ lines = data->lines;
+ else if (data->percentage != -1)
+ lines = (w->active->sy * data->percentage) / 100;
+
+ hlimit = options_get_number(&s->options, "history-limit");
+ wp = window_add_pane(w, lines, cmd, cwd, env, hlimit, &cause);
+ if (wp == NULL) {
+ ctx->error(ctx, "create pane failed: %s", cause);
+ xfree(cause);
+ return (-1);
+ }
+ server_redraw_window(w);
+
+ if (!data->flag_detached) {
+ window_set_active_pane(w, wp);
+ session_select(s, wl->idx);
+ server_redraw_session(s);
+ } else
+ server_status_session(s);
+ layout_refresh(w, 0);
+
+ return (0);
+}
+
+void
+cmd_split_window_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_split_window_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_send_string(b, data->target);
+ cmd_send_string(b, data->cmd);
+}
+
+void
+cmd_split_window_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_split_window_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->target = cmd_recv_string(b);
+ data->cmd = cmd_recv_string(b);
+}
+
+void
+cmd_split_window_free(struct cmd *self)
+{
+ struct cmd_split_window_data *data = self->data;
+
+ if (data->target != NULL)
+ xfree(data->target);
+ if (data->cmd != NULL)
+ xfree(data->cmd);
+ xfree(data);
+}
+
+size_t
+cmd_split_window_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_split_window_data *data = self->data;
+ size_t off = 0;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ if (off < len && data->flag_detached)
+ off += xsnprintf(buf + off, len - off, " -d");
+ if (off < len && data->target != NULL)
+ off += cmd_prarg(buf + off, len - off, " -t ", data->target);
+ if (off < len && data->cmd != NULL)
+ off += cmd_prarg(buf + off, len - off, " ", data->cmd);
+ return (off);
+}
diff --git a/usr.bin/tmux/cmd-start-server.c b/usr.bin/tmux/cmd-start-server.c
new file mode 100644
index 00000000000..57dbd8cd376
--- /dev/null
+++ b/usr.bin/tmux/cmd-start-server.c
@@ -0,0 +1,46 @@
+/* $OpenBSD: cmd-start-server.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Start the server and do nothing else.
+ */
+
+int cmd_start_server_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_start_server_entry = {
+ "start-server", "start",
+ "",
+ CMD_STARTSERVER,
+ NULL,
+ NULL,
+ cmd_start_server_exec,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+int
+cmd_start_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx)
+{
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-string.c b/usr.bin/tmux/cmd-string.c
new file mode 100644
index 00000000000..d151a397bd8
--- /dev/null
+++ b/usr.bin/tmux/cmd-string.c
@@ -0,0 +1,309 @@
+/* $OpenBSD: cmd-string.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Parse a command from a string.
+ */
+
+int cmd_string_getc(const char *, size_t *);
+void cmd_string_ungetc(const char *, size_t *);
+char *cmd_string_string(const char *, size_t *, char, int);
+char *cmd_string_variable(const char *, size_t *);
+
+int
+cmd_string_getc(const char *s, size_t *p)
+{
+ if (s[*p] == '\0')
+ return (EOF);
+ return (s[(*p)++]);
+}
+
+void
+cmd_string_ungetc(unused const char *s, size_t *p)
+{
+ (*p)--;
+}
+
+/*
+ * Parse command string. Returns -1 on error. If returning -1, cause is error
+ * string, or NULL for empty command.
+ */
+int
+cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause)
+{
+ size_t p;
+ int ch, argc, rval, have_arg;
+ char **argv, *buf, *t, *u;
+ size_t len;
+
+ if ((t = strchr(s, ' ')) == NULL && (t = strchr(s, '\t')) == NULL)
+ t = strchr(s, '\0');
+ if ((u = strchr(s, '=')) != NULL && u < t) {
+ if (putenv((char *) s) != 0) {
+ xasprintf(cause, "assignment failed: %s", s);
+ return (-1);
+ }
+ *cmdlist = NULL;
+ return (0);
+ }
+
+ argv = NULL;
+ argc = 0;
+
+ buf = NULL;
+ len = 0;
+
+ have_arg = 0;
+
+ *cause = NULL;
+
+ *cmdlist = NULL;
+ rval = -1;
+
+ p = 0;
+ for (;;) {
+ ch = cmd_string_getc(s, &p);
+ switch (ch) {
+ case '\'':
+ if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL)
+ goto error;
+ buf = xrealloc(buf, 1, len + strlen(t) + 1);
+ strlcpy(buf + len, t, strlen(t) + 1);
+ len += strlen(t);
+ xfree(t);
+
+ have_arg = 1;
+ break;
+ case '"':
+ if ((t = cmd_string_string(s, &p, '"', 1)) == NULL)
+ goto error;
+ buf = xrealloc(buf, 1, len + strlen(t) + 1);
+ strlcpy(buf + len, t, strlen(t) + 1);
+ len += strlen(t);
+ xfree(t);
+
+ have_arg = 1;
+ break;
+ case '$':
+ if ((t = cmd_string_variable(s, &p)) == NULL)
+ goto error;
+ buf = xrealloc(buf, 1, len + strlen(t) + 1);
+ strlcpy(buf + len, t, strlen(t) + 1);
+ len += strlen(t);
+
+ have_arg = 1;
+ break;
+ case '#':
+ /* Comment: discard rest of line. */
+ while ((ch = cmd_string_getc(s, &p)) != EOF)
+ ;
+ /* FALLTHROUGH */
+ case EOF:
+ case ' ':
+ case '\t':
+ if (have_arg) {
+ buf = xrealloc(buf, 1, len + 1);
+ buf[len] = '\0';
+
+ argv = xrealloc(argv, argc + 1, sizeof *argv);
+ argv[argc++] = buf;
+
+ buf = NULL;
+ len = 0;
+
+ have_arg = 0;
+ }
+
+ if (ch != EOF)
+ break;
+ if (argc == 0)
+ goto out;
+
+ *cmdlist = cmd_list_parse(argc, argv, cause);
+ if (*cmdlist == NULL)
+ goto out;
+
+ do
+ xfree(argv[argc - 1]);
+ while (--argc > 0);
+
+ rval = 0;
+ goto out;
+ default:
+ if (len >= SIZE_MAX - 2)
+ goto error;
+
+ buf = xrealloc(buf, 1, len + 1);
+ buf[len++] = ch;
+
+ have_arg = 1;
+ break;
+ }
+ }
+
+error:
+ xasprintf(cause, "invalid or unknown command: %s", s);
+
+out:
+ if (buf != NULL)
+ xfree(buf);
+
+ while (--argc >= 0)
+ xfree(argv[argc]);
+ if (argv != NULL)
+ xfree(argv);
+
+ return (rval);
+}
+
+char *
+cmd_string_string(const char *s, size_t *p, char endch, int esc)
+{
+ int ch;
+ char *buf, *t;
+ size_t len;
+
+ buf = NULL;
+ len = 0;
+
+ while ((ch = cmd_string_getc(s, p)) != endch) {
+ switch (ch) {
+ case EOF:
+ goto error;
+ case '\\':
+ if (!esc)
+ break;
+ switch (ch = cmd_string_getc(s, p)) {
+ case EOF:
+ goto error;
+ case 'r':
+ ch = '\r';
+ break;
+ case 'n':
+ ch = '\n';
+ break;
+ case 't':
+ ch = '\t';
+ break;
+ }
+ break;
+ case '$':
+ if (!esc)
+ break;
+ if ((t = cmd_string_variable(s, p)) == NULL)
+ goto error;
+ buf = xrealloc(buf, 1, len + strlen(t) + 1);
+ strlcpy(buf + len, t, strlen(t) + 1);
+ len += strlen(t);
+ continue;
+ }
+
+ if (len >= SIZE_MAX - 2)
+ goto error;
+ buf = xrealloc(buf, 1, len + 1);
+ buf[len++] = ch;
+ }
+
+ buf = xrealloc(buf, 1, len + 1);
+ buf[len] = '\0';
+ return (buf);
+
+error:
+ if (buf != NULL)
+ xfree(buf);
+ return (NULL);
+}
+
+char *
+cmd_string_variable(const char *s, size_t *p)
+{
+ int ch, fch;
+ char *buf, *t;
+ size_t len;
+
+#define cmd_string_first(ch) ((ch) == '_' || \
+ ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
+#define cmd_string_other(ch) ((ch) == '_' || \
+ ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
+ ((ch) >= '0' && (ch) <= '9'))
+
+ buf = NULL;
+ len = 0;
+
+ fch = EOF;
+ switch (ch = cmd_string_getc(s, p)) {
+ case EOF:
+ goto error;
+ case '{':
+ fch = '{';
+
+ ch = cmd_string_getc(s, p);
+ if (!cmd_string_first(ch))
+ goto error;
+ /* FALLTHROUGH */
+ default:
+ if (!cmd_string_first(ch)) {
+ xasprintf(&t, "$%c", ch);
+ return (t);
+ }
+
+ buf = xrealloc(buf, 1, len + 1);
+ buf[len++] = ch;
+
+ for (;;) {
+ ch = cmd_string_getc(s, p);
+ if (ch == EOF || !cmd_string_other(ch))
+ break;
+ else {
+ if (len >= SIZE_MAX - 3)
+ goto error;
+ buf = xrealloc(buf, 1, len + 1);
+ buf[len++] = ch;
+ }
+ }
+ }
+
+ if (fch == '{' && ch != '}')
+ goto error;
+ if (ch != EOF && fch != '{')
+ cmd_string_ungetc(s, p); /* ch */
+
+ buf = xrealloc(buf, 1, len + 1);
+ buf[len] = '\0';
+
+ if ((t = getenv(buf)) == NULL) {
+ xfree(buf);
+ return (xstrdup(""));
+ }
+ xfree(buf);
+ return (xstrdup(t));
+
+error:
+ if (buf != NULL)
+ xfree(buf);
+ return (NULL);
+}
diff --git a/usr.bin/tmux/cmd-suspend-client.c b/usr.bin/tmux/cmd-suspend-client.c
new file mode 100644
index 00000000000..4b4926f0f25
--- /dev/null
+++ b/usr.bin/tmux/cmd-suspend-client.c
@@ -0,0 +1,64 @@
+/* $OpenBSD: cmd-suspend-client.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Suspend client with SIGTSTP.
+ */
+
+int cmd_suspend_client_exec(struct cmd *, struct cmd_ctx *);
+
+struct cmd_suspend_client_data {
+ char *name;
+ char *target;
+};
+
+const struct cmd_entry cmd_suspend_client_entry = {
+ "suspend-client", "suspendc",
+ "[-c target-client]",
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_suspend_client_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_suspend_client_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct client *c;
+
+ if ((c = cmd_find_client(ctx, data->target)) == NULL)
+ return (-1);
+
+ tty_stop_tty(&c->tty);
+ c->flags |= CLIENT_SUSPENDED;
+ server_write_client(c, MSG_SUSPEND, NULL, 0);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-swap-pane.c b/usr.bin/tmux/cmd-swap-pane.c
new file mode 100644
index 00000000000..62852a198ed
--- /dev/null
+++ b/usr.bin/tmux/cmd-swap-pane.c
@@ -0,0 +1,285 @@
+/* $OpenBSD: cmd-swap-pane.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Swap two panes.
+ */
+
+int cmd_swap_pane_parse(struct cmd *, int, char **, char **);
+int cmd_swap_pane_exec(struct cmd *, struct cmd_ctx *);
+void cmd_swap_pane_send(struct cmd *, struct buffer *);
+void cmd_swap_pane_recv(struct cmd *, struct buffer *);
+void cmd_swap_pane_free(struct cmd *);
+void cmd_swap_pane_init(struct cmd *, int);
+size_t cmd_swap_pane_print(struct cmd *, char *, size_t);
+
+struct cmd_swap_pane_data {
+ char *target;
+ int src;
+ int dst;
+ int flag_detached;
+ int flag_up;
+ int flag_down;
+};
+
+const struct cmd_entry cmd_swap_pane_entry = {
+ "swap-pane", "swapp",
+ "[-dDU] [-t target-window] [-p src-index] [-q dst-index]",
+ 0,
+ cmd_swap_pane_init,
+ cmd_swap_pane_parse,
+ cmd_swap_pane_exec,
+ cmd_swap_pane_send,
+ cmd_swap_pane_recv,
+ cmd_swap_pane_free,
+ cmd_swap_pane_print
+};
+
+void
+cmd_swap_pane_init(struct cmd *self, int key)
+{
+ struct cmd_swap_pane_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->target = NULL;
+ data->src = -1;
+ data->dst = -1;
+ data->flag_detached = 0;
+ data->flag_up = 0;
+ data->flag_down = 0;
+
+ switch (key) {
+ case '{':
+ data->flag_up = 1;
+ break;
+ case '}':
+ data->flag_down = 1;
+ break;
+ }
+}
+
+int
+cmd_swap_pane_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_swap_pane_data *data;
+ int opt, n;
+ const char *errstr;
+
+ self->entry->init(self, 0);
+ data = self->data;
+
+ while ((opt = getopt(argc, argv, "dDt:p:q:U")) != -1) {
+ switch (opt) {
+ case 'd':
+ data->flag_detached = 1;
+ break;
+ case 'D':
+ data->flag_up = 0;
+ data->flag_down = 1;
+ data->dst = -1;
+ break;
+ case 't':
+ if (data->target == NULL)
+ data->target = xstrdup(optarg);
+ break;
+ case 'p':
+ if (data->src == -1) {
+ n = strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr != NULL) {
+ xasprintf(cause, "src %s", errstr);
+ goto error;
+ }
+ data->src = n;
+ }
+ break;
+ case 'q':
+ if (data->dst == -1) {
+ n = strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr != NULL) {
+ xasprintf(cause, "dst %s", errstr);
+ goto error;
+ }
+ data->dst = n;
+ }
+ data->flag_up = 0;
+ data->flag_down = 0;
+ break;
+ case 'U':
+ data->flag_up = 1;
+ data->flag_down = 0;
+ data->dst = -1;
+ break;
+
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 0)
+ goto usage;
+
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+error:
+ self->entry->free(self);
+ return (-1);
+}
+
+int
+cmd_swap_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_swap_pane_data *data = self->data;
+ struct winlink *wl;
+ struct window *w;
+ struct window_pane *tmp_wp, *src_wp, *dst_wp;
+ u_int xx, yy;
+
+ if (data == NULL)
+ return (0);
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+ w = wl->window;
+
+ if (data->src == -1)
+ src_wp = w->active;
+ else {
+ src_wp = window_pane_at_index(w, data->src);
+ if (src_wp == NULL) {
+ ctx->error(ctx, "no pane: %d", data->src);
+ return (-1);
+ }
+ }
+ if (data->dst == -1)
+ dst_wp = w->active;
+ else {
+ dst_wp = window_pane_at_index(w, data->dst);
+ if (dst_wp == NULL) {
+ ctx->error(ctx, "no pane: %d", data->dst);
+ return (-1);
+ }
+ }
+
+ if (data->dst == -1 && data->flag_up) {
+ if ((dst_wp = TAILQ_PREV(src_wp, window_panes, entry)) == NULL)
+ dst_wp = TAILQ_LAST(&w->panes, window_panes);
+ }
+ if (data->dst == -1 && data->flag_down) {
+ if ((dst_wp = TAILQ_NEXT(src_wp, entry)) == NULL)
+ dst_wp = TAILQ_FIRST(&w->panes);
+ }
+
+ if (src_wp == dst_wp)
+ return (0);
+
+ tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry);
+ TAILQ_REMOVE(&w->panes, dst_wp, entry);
+ TAILQ_REPLACE(&w->panes, src_wp, dst_wp, entry);
+ if (tmp_wp == src_wp)
+ tmp_wp = dst_wp;
+ if (tmp_wp == NULL)
+ TAILQ_INSERT_HEAD(&w->panes, src_wp, entry);
+ else
+ TAILQ_INSERT_AFTER(&w->panes, tmp_wp, src_wp, entry);
+
+ xx = src_wp->xoff;
+ yy = src_wp->yoff;
+ src_wp->xoff = dst_wp->xoff;
+ src_wp->yoff = dst_wp->yoff;
+ dst_wp->xoff = xx;
+ dst_wp->yoff = yy;
+
+ xx = src_wp->sx;
+ yy = src_wp->sy;
+ window_pane_resize(src_wp, dst_wp->sx, dst_wp->sy);
+ window_pane_resize(dst_wp, xx, yy);
+
+ if (!data->flag_detached) {
+ window_set_active_pane(w, dst_wp);
+ layout_refresh(w, 0);
+ }
+
+ return (0);
+}
+
+void
+cmd_swap_pane_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_swap_pane_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_send_string(b, data->target);
+}
+
+void
+cmd_swap_pane_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_swap_pane_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->target = cmd_recv_string(b);
+}
+
+void
+cmd_swap_pane_free(struct cmd *self)
+{
+ struct cmd_swap_pane_data *data = self->data;
+
+ if (data->target != NULL)
+ xfree(data->target);
+ xfree(data);
+}
+
+size_t
+cmd_swap_pane_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_swap_pane_data *data = self->data;
+ size_t off = 0;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ if (off < len &&
+ (data->flag_down || data->flag_up || data->flag_detached)) {
+ off += xsnprintf(buf + off, len - off, " -");
+ if (off < len && data->flag_detached)
+ off += xsnprintf(buf + off, len - off, "d");
+ if (off < len && data->flag_up)
+ off += xsnprintf(buf + off, len - off, "D");
+ if (off < len && data->flag_down)
+ off += xsnprintf(buf + off, len - off, "U");
+ }
+ if (off < len && data->target != NULL)
+ off += cmd_prarg(buf + off, len - off, " -t ", data->target);
+ if (off < len && data->src != -1)
+ off += xsnprintf(buf + off, len - off, " -p %d", data->src);
+ if (off < len && data->dst != -1)
+ off += xsnprintf(buf + off, len - off, " -q %d", data->dst);
+ return (off);
+}
diff --git a/usr.bin/tmux/cmd-swap-window.c b/usr.bin/tmux/cmd-swap-window.c
new file mode 100644
index 00000000000..64c0238fc9a
--- /dev/null
+++ b/usr.bin/tmux/cmd-swap-window.c
@@ -0,0 +1,75 @@
+/* $OpenBSD: cmd-swap-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+
+#include "tmux.h"
+
+/*
+ * Swap one window with another.
+ */
+
+int cmd_swap_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_swap_window_entry = {
+ "swap-window", "swapw",
+ "[-d] " CMD_SRCDST_WINDOW_USAGE,
+ CMD_DFLAG,
+ cmd_srcdst_init,
+ cmd_srcdst_parse,
+ cmd_swap_window_exec,
+ cmd_srcdst_send,
+ cmd_srcdst_recv,
+ cmd_srcdst_free,
+ cmd_srcdst_print
+};
+
+int
+cmd_swap_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_srcdst_data *data = self->data;
+ struct session *src, *dst;
+ struct winlink *wl_src, *wl_dst;
+ struct window *w;
+
+ if ((wl_src = cmd_find_window(ctx, data->src, &src)) == NULL)
+ return (-1);
+ if ((wl_dst = cmd_find_window(ctx, data->dst, &dst)) == NULL)
+ return (-1);
+
+ if (wl_dst->window == wl_src->window)
+ return (0);
+
+ w = wl_dst->window;
+ wl_dst->window = wl_src->window;
+ wl_src->window = w;
+
+ if (!(data->flags & CMD_DFLAG)) {
+ session_select(dst, wl_dst->idx);
+ if (src != dst)
+ session_select(src, wl_src->idx);
+ }
+ server_redraw_session(src);
+ if (src != dst)
+ server_redraw_session(dst);
+ recalculate_sizes();
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-switch-client.c b/usr.bin/tmux/cmd-switch-client.c
new file mode 100644
index 00000000000..627e5b8b873
--- /dev/null
+++ b/usr.bin/tmux/cmd-switch-client.c
@@ -0,0 +1,161 @@
+/* $OpenBSD: cmd-switch-client.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Switch client to a different session.
+ */
+
+int cmd_switch_client_parse(struct cmd *, int, char **, char **);
+int cmd_switch_client_exec(struct cmd *, struct cmd_ctx *);
+void cmd_switch_client_send(struct cmd *, struct buffer *);
+void cmd_switch_client_recv(struct cmd *, struct buffer *);
+void cmd_switch_client_free(struct cmd *);
+size_t cmd_switch_client_print(struct cmd *, char *, size_t);
+
+struct cmd_switch_client_data {
+ char *name;
+ char *target;
+};
+
+const struct cmd_entry cmd_switch_client_entry = {
+ "switch-client", "switchc",
+ "[-c target-client] [-t target-session]",
+ 0,
+ NULL,
+ cmd_switch_client_parse,
+ cmd_switch_client_exec,
+ cmd_switch_client_send,
+ cmd_switch_client_recv,
+ cmd_switch_client_free,
+ cmd_switch_client_print
+};
+
+int
+cmd_switch_client_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_switch_client_data *data;
+ int opt;
+
+ self->data = data = xmalloc(sizeof *data);
+ data->name = NULL;
+ data->target = NULL;
+
+ while ((opt = getopt(argc, argv, "c:t:")) != -1) {
+ switch (opt) {
+ case 'c':
+ data->name = xstrdup(optarg);
+ break;
+ case 't':
+ data->target = xstrdup(optarg);
+ break;
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 0)
+ goto usage;
+
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+ self->entry->free(self);
+ return (-1);
+}
+
+int
+cmd_switch_client_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_switch_client_data *data = self->data;
+ struct client *c;
+ struct session *s;
+
+ if (data == NULL)
+ return (0);
+
+ if ((c = cmd_find_client(ctx, data->name)) == NULL)
+ return (-1);
+ if ((s = cmd_find_session(ctx, data->target)) == NULL)
+ return (-1);
+
+ c->session = s;
+
+ recalculate_sizes();
+ server_redraw_client(c);
+
+ return (0);
+}
+
+void
+cmd_switch_client_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_switch_client_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+ cmd_send_string(b, data->name);
+ cmd_send_string(b, data->target);
+}
+
+void
+cmd_switch_client_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_switch_client_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+ data->name = cmd_recv_string(b);
+ data->target = cmd_recv_string(b);
+}
+
+void
+cmd_switch_client_free(struct cmd *self)
+{
+ struct cmd_switch_client_data *data = self->data;
+
+ if (data->name != NULL)
+ xfree(data->name);
+ if (data->target != NULL)
+ xfree(data->target);
+ xfree(data);
+}
+
+size_t
+cmd_switch_client_print(struct cmd *self, char *buf, size_t len)
+{
+ struct cmd_switch_client_data *data = self->data;
+ size_t off = 0;
+
+ off += xsnprintf(buf, len, "%s", self->entry->name);
+ if (data == NULL)
+ return (off);
+ if (off < len && data->name != NULL)
+ off += cmd_prarg(buf + off, len - off, " -c ", data->name);
+ if (off < len && data->target != NULL)
+ off += cmd_prarg(buf + off, len - off, " -t ", data->target);
+ return (off);
+}
diff --git a/usr.bin/tmux/cmd-unbind-key.c b/usr.bin/tmux/cmd-unbind-key.c
new file mode 100644
index 00000000000..d69a6f59bab
--- /dev/null
+++ b/usr.bin/tmux/cmd-unbind-key.c
@@ -0,0 +1,120 @@
+/* $OpenBSD: cmd-unbind-key.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Unbind key from command.
+ */
+
+int cmd_unbind_key_parse(struct cmd *, int, char **, char **);
+int cmd_unbind_key_exec(struct cmd *, struct cmd_ctx *);
+void cmd_unbind_key_send(struct cmd *, struct buffer *);
+void cmd_unbind_key_recv(struct cmd *, struct buffer *);
+void cmd_unbind_key_free(struct cmd *);
+
+struct cmd_unbind_key_data {
+ int key;
+};
+
+const struct cmd_entry cmd_unbind_key_entry = {
+ "unbind-key", "unbind",
+ "key",
+ 0,
+ NULL,
+ cmd_unbind_key_parse,
+ cmd_unbind_key_exec,
+ cmd_unbind_key_send,
+ cmd_unbind_key_recv,
+ cmd_unbind_key_free,
+ NULL
+};
+
+int
+cmd_unbind_key_parse(struct cmd *self, int argc, char **argv, char **cause)
+{
+ struct cmd_unbind_key_data *data;
+ int opt;
+
+ self->data = data = xmalloc(sizeof *data);
+
+ while ((opt = getopt(argc, argv, "")) != -1) {
+ switch (opt) {
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ goto usage;
+
+ if ((data->key = key_string_lookup_string(argv[0])) == KEYC_NONE) {
+ xasprintf(cause, "unknown key: %s", argv[0]);
+ goto error;
+ }
+
+ return (0);
+
+usage:
+ xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
+
+error:
+ xfree(data);
+ return (-1);
+}
+
+int
+cmd_unbind_key_exec(struct cmd *self, unused struct cmd_ctx *ctx)
+{
+ struct cmd_unbind_key_data *data = self->data;
+
+ if (data == NULL)
+ return (0);
+
+ key_bindings_remove(data->key);
+
+ return (0);
+}
+
+void
+cmd_unbind_key_send(struct cmd *self, struct buffer *b)
+{
+ struct cmd_unbind_key_data *data = self->data;
+
+ buffer_write(b, data, sizeof *data);
+}
+
+void
+cmd_unbind_key_recv(struct cmd *self, struct buffer *b)
+{
+ struct cmd_unbind_key_data *data;
+
+ self->data = data = xmalloc(sizeof *data);
+ buffer_read(b, data, sizeof *data);
+}
+
+void
+cmd_unbind_key_free(struct cmd *self)
+{
+ struct cmd_unbind_key_data *data = self->data;
+
+ xfree(data);
+}
diff --git a/usr.bin/tmux/cmd-unlink-window.c b/usr.bin/tmux/cmd-unlink-window.c
new file mode 100644
index 00000000000..5caebfa02e1
--- /dev/null
+++ b/usr.bin/tmux/cmd-unlink-window.c
@@ -0,0 +1,74 @@
+/* $OpenBSD: cmd-unlink-window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Unlink a window, unless it would be destroyed by doing so (only one link).
+ */
+
+int cmd_unlink_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_unlink_window_entry = {
+ "unlink-window", "unlinkw",
+ CMD_TARGET_WINDOW_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_unlink_window_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_unlink_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct winlink *wl;
+ struct session *s;
+ struct client *c;
+ u_int i;
+ int destroyed;
+
+ if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL)
+ return (-1);
+
+ if (wl->window->references == 1) {
+ ctx->error(ctx, "window is only linked to one session");
+ return (-1);
+ }
+
+ destroyed = session_detach(s, wl);
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session != s)
+ continue;
+ if (destroyed) {
+ c->session = NULL;
+ server_write_client(c, MSG_EXIT, NULL, 0);
+ } else
+ server_redraw_client(c);
+ }
+ recalculate_sizes();
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd-up-pane.c b/usr.bin/tmux/cmd-up-pane.c
new file mode 100644
index 00000000000..5d5dad5b978
--- /dev/null
+++ b/usr.bin/tmux/cmd-up-pane.c
@@ -0,0 +1,61 @@
+/* $OpenBSD: cmd-up-pane.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/*
+ * Move up a pane.
+ */
+
+int cmd_up_pane_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_up_pane_entry = {
+ "up-pane", "upp",
+ CMD_TARGET_WINDOW_USAGE,
+ 0,
+ cmd_target_init,
+ cmd_target_parse,
+ cmd_up_pane_exec,
+ cmd_target_send,
+ cmd_target_recv,
+ cmd_target_free,
+ cmd_target_print
+};
+
+int
+cmd_up_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+ struct cmd_target_data *data = self->data;
+ struct winlink *wl;
+ struct window *w;
+
+ if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
+ return (-1);
+ w = wl->window;
+
+ do {
+ w->active = TAILQ_PREV(w->active, window_panes, entry);
+ if (w->active == NULL)
+ w->active = TAILQ_LAST(&w->panes, window_panes);
+ layout_refresh(w, 1);
+ } while (w->active->flags & PANE_HIDDEN);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/cmd.c b/usr.bin/tmux/cmd.c
new file mode 100644
index 00000000000..ebceb5486d5
--- /dev/null
+++ b/usr.bin/tmux/cmd.c
@@ -0,0 +1,393 @@
+/* $OpenBSD: cmd.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/time.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+const struct cmd_entry *cmd_table[] = {
+ &cmd_attach_session_entry,
+ &cmd_bind_key_entry,
+ &cmd_break_pane_entry,
+ &cmd_choose_session_entry,
+ &cmd_choose_window_entry,
+ &cmd_clear_history_entry,
+ &cmd_clock_mode_entry,
+ &cmd_command_prompt_entry,
+ &cmd_confirm_before_entry,
+ &cmd_copy_buffer_entry,
+ &cmd_copy_mode_entry,
+ &cmd_delete_buffer_entry,
+ &cmd_detach_client_entry,
+ &cmd_down_pane_entry,
+ &cmd_find_window_entry,
+ &cmd_has_session_entry,
+ &cmd_kill_pane_entry,
+ &cmd_kill_server_entry,
+ &cmd_kill_session_entry,
+ &cmd_kill_window_entry,
+ &cmd_last_window_entry,
+ &cmd_link_window_entry,
+ &cmd_list_buffers_entry,
+ &cmd_list_clients_entry,
+ &cmd_list_commands_entry,
+ &cmd_list_keys_entry,
+ &cmd_list_sessions_entry,
+ &cmd_list_windows_entry,
+ &cmd_load_buffer_entry,
+ &cmd_lock_server_entry,
+ &cmd_move_window_entry,
+ &cmd_new_session_entry,
+ &cmd_new_window_entry,
+ &cmd_next_layout_entry,
+ &cmd_next_window_entry,
+ &cmd_paste_buffer_entry,
+ &cmd_previous_layout_entry,
+ &cmd_previous_window_entry,
+ &cmd_refresh_client_entry,
+ &cmd_rename_session_entry,
+ &cmd_rename_window_entry,
+ &cmd_resize_pane_entry,
+ &cmd_respawn_window_entry,
+ &cmd_rotate_window_entry,
+ &cmd_save_buffer_entry,
+ &cmd_scroll_mode_entry,
+ &cmd_select_layout_entry,
+ &cmd_select_pane_entry,
+ &cmd_select_prompt_entry,
+ &cmd_select_window_entry,
+ &cmd_send_keys_entry,
+ &cmd_send_prefix_entry,
+ &cmd_server_info_entry,
+ &cmd_set_buffer_entry,
+ &cmd_set_option_entry,
+ &cmd_set_password_entry,
+ &cmd_set_window_option_entry,
+ &cmd_show_buffer_entry,
+ &cmd_show_options_entry,
+ &cmd_show_window_options_entry,
+ &cmd_source_file_entry,
+ &cmd_split_window_entry,
+ &cmd_start_server_entry,
+ &cmd_suspend_client_entry,
+ &cmd_swap_pane_entry,
+ &cmd_swap_window_entry,
+ &cmd_switch_client_entry,
+ &cmd_unbind_key_entry,
+ &cmd_unlink_window_entry,
+ &cmd_up_pane_entry,
+ NULL
+};
+
+struct cmd *
+cmd_parse(int argc, char **argv, char **cause)
+{
+ const struct cmd_entry **entryp, *entry;
+ struct cmd *cmd;
+ char s[BUFSIZ];
+ int opt;
+
+ *cause = NULL;
+ if (argc == 0)
+ return (NULL);
+
+ entry = NULL;
+ for (entryp = cmd_table; *entryp != NULL; entryp++) {
+ if ((*entryp)->alias != NULL &&
+ strcmp((*entryp)->alias, argv[0]) == 0) {
+ entry = *entryp;
+ break;
+ }
+
+ if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
+ continue;
+ if (entry != NULL)
+ goto ambiguous;
+ entry = *entryp;
+
+ /* Bail now if an exact match. */
+ if (strcmp(entry->name, argv[0]) == 0)
+ break;
+ }
+ if (entry == NULL) {
+ xasprintf(cause, "unknown command: %s", argv[0]);
+ return (NULL);
+ }
+
+ optreset = 1;
+ optind = 1;
+ if (entry->parse == NULL) {
+ while ((opt = getopt(argc, argv, "")) != -1) {
+ switch (opt) {
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 0)
+ goto usage;
+ }
+
+ cmd = xmalloc(sizeof *cmd);
+ cmd->entry = entry;
+ cmd->data = NULL;
+ if (entry->parse != NULL) {
+ if (entry->parse(cmd, argc, argv, cause) != 0) {
+ xfree(cmd);
+ return (NULL);
+ }
+ }
+ return (cmd);
+
+ambiguous:
+ *s = '\0';
+ for (entryp = cmd_table; *entryp != NULL; entryp++) {
+ if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
+ continue;
+ if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s)
+ break;
+ if (strlcat(s, ", ", sizeof s) >= sizeof s)
+ break;
+ }
+ s[strlen(s) - 2] = '\0';
+ xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s);
+ return (NULL);
+
+usage:
+ xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
+ return (NULL);
+}
+
+int
+cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx)
+{
+ if (server_locked) {
+ ctx->error(ctx, "server is locked");
+ return (-1);
+ }
+ return (cmd->entry->exec(cmd, ctx));
+}
+
+void
+cmd_send(struct cmd *cmd, struct buffer *b)
+{
+ const struct cmd_entry **entryp;
+ u_int n;
+
+ n = 0;
+ for (entryp = cmd_table; *entryp != NULL; entryp++) {
+ if (*entryp == cmd->entry)
+ break;
+ n++;
+ }
+ if (*entryp == NULL)
+ fatalx("command not found");
+
+ buffer_write(b, &n, sizeof n);
+
+ if (cmd->entry->send != NULL)
+ cmd->entry->send(cmd, b);
+}
+
+struct cmd *
+cmd_recv(struct buffer *b)
+{
+ const struct cmd_entry **entryp;
+ struct cmd *cmd;
+ u_int m, n;
+
+ buffer_read(b, &m, sizeof m);
+
+ n = 0;
+ for (entryp = cmd_table; *entryp != NULL; entryp++) {
+ if (n == m)
+ break;
+ n++;
+ }
+ if (*entryp == NULL)
+ fatalx("command not found");
+
+ cmd = xmalloc(sizeof *cmd);
+ cmd->entry = *entryp;
+
+ if (cmd->entry->recv != NULL)
+ cmd->entry->recv(cmd, b);
+ return (cmd);
+}
+
+void
+cmd_free(struct cmd *cmd)
+{
+ if (cmd->data != NULL && cmd->entry->free != NULL)
+ cmd->entry->free(cmd);
+ xfree(cmd);
+}
+
+size_t
+cmd_print(struct cmd *cmd, char *buf, size_t len)
+{
+ if (cmd->entry->print == NULL) {
+ return (xsnprintf(buf, len, "%s", cmd->entry->name));
+ }
+ return (cmd->entry->print(cmd, buf, len));
+}
+
+void
+cmd_send_string(struct buffer *b, const char *s)
+{
+ size_t n;
+
+ if (s == NULL) {
+ n = 0;
+ buffer_write(b, &n, sizeof n);
+ return;
+ }
+
+ n = strlen(s) + 1;
+ buffer_write(b, &n, sizeof n);
+
+ buffer_write(b, s, n);
+}
+
+char *
+cmd_recv_string(struct buffer *b)
+{
+ char *s;
+ size_t n;
+
+ buffer_read(b, &n, sizeof n);
+
+ if (n == 0)
+ return (NULL);
+
+ s = xmalloc(n);
+ buffer_read(b, s, n);
+ s[n - 1] = '\0';
+
+ return (s);
+}
+
+struct session *
+cmd_current_session(struct cmd_ctx *ctx)
+{
+ struct msg_command_data *data = ctx->msgdata;
+ struct timeval *tv;
+ struct session *s, *newest = NULL;
+ u_int i;
+
+ if (ctx->cursession != NULL)
+ return (ctx->cursession);
+
+ if (data != NULL && data->pid != -1) {
+ if (data->pid != getpid()) {
+ ctx->error(ctx, "wrong server: %ld", (long) data->pid);
+ return (NULL);
+ }
+ if (data->idx > ARRAY_LENGTH(&sessions)) {
+ ctx->error(ctx, "index out of range: %d", data->idx);
+ return (NULL);
+ }
+ if ((s = ARRAY_ITEM(&sessions, data->idx)) == NULL) {
+ ctx->error(ctx, "session doesn't exist: %u", data->idx);
+ return (NULL);
+ }
+ return (s);
+ }
+
+ tv = NULL;
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ s = ARRAY_ITEM(&sessions, i);
+ if (s != NULL && (tv == NULL || timercmp(&s->tv, tv, >))) {
+ newest = ARRAY_ITEM(&sessions, i);
+ tv = &s->tv;
+ }
+ }
+ return (newest);
+}
+
+struct client *
+cmd_find_client(struct cmd_ctx *ctx, const char *arg)
+{
+ struct client *c;
+
+ if (arg == NULL)
+ c = ctx->curclient;
+ else {
+ if ((c = arg_parse_client(arg)) == NULL) {
+ if (arg != NULL)
+ ctx->error(ctx, "client not found: %s", arg);
+ else
+ ctx->error(ctx, "no client found");
+ }
+ }
+ return (c);
+}
+
+struct session *
+cmd_find_session(struct cmd_ctx *ctx, const char *arg)
+{
+ struct session *s;
+
+ if (arg == NULL)
+ s = cmd_current_session(ctx);
+ else {
+ if ((s = arg_parse_session(arg)) == NULL) {
+ if (arg != NULL)
+ ctx->error(ctx, "session not found: %s", arg);
+ else
+ ctx->error(ctx, "no session found");
+ }
+ }
+ return (s);
+}
+
+struct winlink *
+cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp)
+{
+ struct session *s;
+ struct winlink *wl;
+ int idx;
+
+ wl = NULL;
+ if (arg_parse_window(arg, &s, &idx) != 0) {
+ ctx->error(ctx, "bad window: %s", arg);
+ return (NULL);
+ }
+ if (s == NULL)
+ s = ctx->cursession;
+ if (s == NULL)
+ s = cmd_current_session(ctx);
+ if (s == NULL)
+ return (NULL);
+ if (sp != NULL)
+ *sp = s;
+
+ if (idx == -1)
+ wl = s->curw;
+ else
+ wl = winlink_find_by_index(&s->windows, idx);
+ if (wl == NULL)
+ ctx->error(ctx, "window not found: %s:%d", s->name, idx);
+ return (wl);
+}
diff --git a/usr.bin/tmux/colour.c b/usr.bin/tmux/colour.c
new file mode 100644
index 00000000000..5b13db98760
--- /dev/null
+++ b/usr.bin/tmux/colour.c
@@ -0,0 +1,123 @@
+/* $OpenBSD: colour.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+const char *
+colour_tostring(u_char c)
+{
+ switch (c) {
+ case 0:
+ return ("black");
+ case 1:
+ return ("red");
+ case 2:
+ return ("green");
+ case 3:
+ return ("yellow");
+ case 4:
+ return ("blue");
+ case 5:
+ return ("magenta");
+ case 6:
+ return ("cyan");
+ case 7:
+ return ("white");
+ case 8:
+ return ("default");
+ }
+ return (NULL);
+}
+
+int
+colour_fromstring(const char *s)
+{
+ if (strcasecmp(s, "black") == 0 || (s[0] == '0' && s[1] == '\0'))
+ return (0);
+ if (strcasecmp(s, "red") == 0 || (s[0] == '1' && s[1] == '\0'))
+ return (1);
+ if (strcasecmp(s, "green") == 0 || (s[0] == '2' && s[1] == '\0'))
+ return (2);
+ if (strcasecmp(s, "yellow") == 0 || (s[0] == '3' && s[1] == '\0'))
+ return (3);
+ if (strcasecmp(s, "blue") == 0 || (s[0] == '4' && s[1] == '\0'))
+ return (4);
+ if (strcasecmp(s, "magenta") == 0 || (s[0] == '5' && s[1] == '\0'))
+ return (5);
+ if (strcasecmp(s, "cyan") == 0 || (s[0] == '6' && s[1] == '\0'))
+ return (6);
+ if (strcasecmp(s, "white") == 0 || (s[0] == '7' && s[1] == '\0'))
+ return (7);
+ if (strcasecmp(s, "default") == 0 || (s[0] == '8' && s[1] == '\0'))
+ return (8);
+ return (-1);
+}
+
+u_char
+colour_256to16(u_char c)
+{
+ static const u_char table[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4,
+ 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
+ 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12,
+ 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10,
+ 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1,
+ 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12,
+ 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5,
+ 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3,
+ 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,
+ 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9,
+ 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10,
+ 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13,
+ 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9,
+ 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8,
+ 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15
+ };
+
+ return (table[c]);
+}
+
+u_char
+colour_256to88(u_char c)
+{
+ static const u_char table[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 17, 18, 18, 19, 20, 21, 21, 22, 22, 23, 20, 21, 21, 22,
+ 22, 23, 24, 25, 25, 26, 26, 27, 24, 25, 25, 26, 26, 27, 28, 29,
+ 29, 30, 30, 31, 32, 33, 33, 34, 34, 35, 36, 37, 37, 38, 38, 39,
+ 36, 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 43, 40, 41, 41, 42,
+ 42, 43, 44, 45, 45, 46, 46, 47, 32, 33, 33, 34, 34, 35, 36, 37,
+ 37, 38, 38, 39, 36, 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 43,
+ 40, 41, 41, 42, 42, 43, 44, 45, 45, 46, 46, 47, 48, 49, 49, 50,
+ 50, 51, 52, 53, 53, 54, 54, 55, 52, 53, 53, 54, 54, 55, 56, 57,
+ 57, 58, 58, 59, 56, 57, 57, 58, 58, 59, 60, 61, 61, 62, 62, 63,
+ 48, 49, 49, 50, 50, 51, 52, 53, 53, 54, 54, 55, 52, 53, 53, 54,
+ 54, 55, 56, 57, 57, 58, 58, 59, 56, 57, 57, 58, 58, 59, 60, 61,
+ 61, 62, 62, 63, 64, 65, 65, 66, 66, 67, 68, 69, 69, 70, 70, 71,
+ 68, 69, 69, 70, 70, 71, 72, 73, 73, 74, 74, 75, 72, 73, 73, 74,
+ 74, 75, 76, 77, 77, 78, 78, 79, 0, 0, 80, 80, 80, 81, 81, 81,
+ 82, 82, 82, 83, 83, 83, 84, 84, 84, 85, 85, 85, 86, 86, 86, 87
+ };
+
+ return (table[c]);
+}
diff --git a/usr.bin/tmux/grid-view.c b/usr.bin/tmux/grid-view.c
new file mode 100644
index 00000000000..7e4920ac7f8
--- /dev/null
+++ b/usr.bin/tmux/grid-view.c
@@ -0,0 +1,211 @@
+/* $OpenBSD: grid-view.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+/*
+ * Grid view functions. These work using coordinates relative to the visible
+ * screen area.
+ */
+
+#define grid_view_x(gd, x) (x)
+#define grid_view_y(gd, y) ((gd)->hsize + (y))
+
+/* Get cell for reading. */
+const struct grid_cell *
+grid_view_peek_cell(struct grid *gd, u_int px, u_int py)
+{
+ return (grid_peek_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py)));
+}
+
+/* Get cell for writing. */
+struct grid_cell *
+grid_view_get_cell(struct grid *gd, u_int px, u_int py)
+{
+ return (grid_get_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py)));
+}
+
+/* Set cell. */
+void
+grid_view_set_cell(
+ struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
+{
+ grid_set_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc);
+}
+
+/* Get UTF-8 for reading. */
+const struct grid_utf8 *
+grid_view_peek_utf8(struct grid *gd, u_int px, u_int py)
+{
+ return (grid_peek_utf8(gd, grid_view_x(gd, px), grid_view_y(gd, py)));
+}
+
+/* Get UTF-8 for writing. */
+struct grid_utf8 *
+grid_view_get_utf8(struct grid *gd, u_int px, u_int py)
+{
+ return (grid_get_utf8(gd, grid_view_x(gd, px), grid_view_y(gd, py)));
+}
+
+/* Set UTF-8. */
+void
+grid_view_set_utf8(
+ struct grid *gd, u_int px, u_int py, const struct grid_utf8 *gu)
+{
+ grid_set_utf8(gd, grid_view_x(gd, px), grid_view_y(gd, py), gu);
+}
+
+/* Clear area. */
+void
+grid_view_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny)
+{
+ GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny);
+
+ px = grid_view_x(gd, px);
+ py = grid_view_y(gd, py);
+
+ grid_clear(gd, px, py, nx, ny);
+}
+
+/* Scroll region up. */
+void
+grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower)
+{
+ GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower);
+
+ if (rupper == 0 && rlower == gd->sy - 1) {
+ grid_scroll_line(gd);
+ return;
+ }
+
+ rupper = grid_view_y(gd, rupper);
+ rlower = grid_view_y(gd, rlower);
+
+ grid_move_lines(gd, rupper, rupper + 1, rlower - rupper);
+}
+
+/* Scroll region down. */
+void
+grid_view_scroll_region_down(struct grid *gd, u_int rupper, u_int rlower)
+{
+ GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower);
+
+ rupper = grid_view_y(gd, rupper);
+ rlower = grid_view_y(gd, rlower);
+
+ grid_move_lines(gd, rupper + 1, rupper, rlower - rupper);
+}
+
+/* Insert lines. */
+void
+grid_view_insert_lines(struct grid *gd, u_int py, u_int ny)
+{
+ u_int sy;
+
+ GRID_DEBUG(gd, "py=%u, ny=%u", py, ny);
+
+ py = grid_view_y(gd, py);
+
+ sy = grid_view_y(gd, gd->sy);
+
+ grid_move_lines(gd, py + ny, py, sy - py - ny);
+}
+
+/* Insert lines in region. */
+void
+grid_view_insert_lines_region(
+ struct grid *gd, unused u_int rupper, u_int rlower, u_int py, u_int ny)
+{
+ GRID_DEBUG(
+ gd, "rupper=%u, rlower=%u, py=%u, ny=%u", rupper, rlower, py, ny);
+
+ rlower = grid_view_y(gd, rlower);
+
+ py = grid_view_y(gd, py);
+
+ grid_move_lines(gd, py + ny, py, (rlower + 1) - py - ny);
+}
+
+/* Delete lines. */
+void
+grid_view_delete_lines(struct grid *gd, u_int py, u_int ny)
+{
+ u_int sy;
+
+ GRID_DEBUG(gd, "py=%u, ny=%u", py, ny);
+
+ py = grid_view_y(gd, py);
+
+ sy = grid_view_y(gd, gd->sy);
+
+ grid_move_lines(gd, py, py + ny, sy - py - ny);
+}
+
+/* Delete lines inside scroll region. */
+void
+grid_view_delete_lines_region(
+ struct grid *gd, unused u_int rupper, u_int rlower, u_int py, u_int ny)
+{
+ GRID_DEBUG(
+ gd, "rupper=%u, rlower=%u, py=%u, ny=%u", rupper, rlower, py, ny);
+
+ rlower = grid_view_y(gd, rlower);
+
+ py = grid_view_y(gd, py);
+
+ grid_move_lines(gd, py, py + ny, (rlower + 1) - py - ny);
+}
+
+/* Insert characters. */
+void
+grid_view_insert_cells(struct grid *gd, u_int px, u_int py, u_int nx)
+{
+ u_int sx;
+
+ GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx);
+
+ px = grid_view_x(gd, px);
+ py = grid_view_y(gd, py);
+
+ sx = grid_view_x(gd, gd->sx);
+
+ if (px == sx - 1)
+ grid_clear(gd, px, py, 1, 1);
+ else
+ grid_move_cells(gd, px + nx, px, py, (sx - 1) - (px + nx));
+}
+
+/* Delete characters. */
+void
+grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx)
+{
+ u_int sx;
+
+ GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx);
+
+ px = grid_view_x(gd, px);
+ py = grid_view_y(gd, py);
+
+ sx = grid_view_x(gd, gd->sx);
+
+ grid_move_cells(gd, px, px + nx, py, (sx - 1) - (px + nx));
+}
diff --git a/usr.bin/tmux/grid.c b/usr.bin/tmux/grid.c
new file mode 100644
index 00000000000..0e90df3441f
--- /dev/null
+++ b/usr.bin/tmux/grid.c
@@ -0,0 +1,497 @@
+/* $OpenBSD: grid.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+/*
+ * Grid data. This is the basic data structure that represents what is shown on
+ * screen.
+ *
+ * A grid is a grid of cells (struct grid_cell). Lines are not allocated until
+ * cells in that line are written to. The grid is split into history and
+ * viewable data with the history starting at row (line) 0 and extending to
+ * (hsize - 1); from hsize to hsize + (sy - 1) is the viewable data. All
+ * functions in this file work on absolute coordinates, grid-view.c has
+ * functions which work on the screen data.
+ */
+
+/* Default grid cell data. */
+const struct grid_cell grid_default_cell = { 0, 0, 8, 8, ' ' };
+
+#define grid_put_cell(gd, px, py, gc) do { \
+ memcpy(&gd->data[py][px], gc, sizeof gd->data[py][px]); \
+} while (0)
+#define grid_put_utf8(gd, px, py, gc) do { \
+ memcpy(&gd->udata[py][px], gc, sizeof gd->udata[py][px]); \
+} while (0)
+
+int grid_check_x(struct grid *, u_int);
+int grid_check_y(struct grid *, u_int);
+
+#ifdef DEBUG
+int
+grid_check_x(struct grid *gd, u_int px)
+{
+ if ((px) >= (gd)->sx)
+ log_fatalx("x out of range: %u", px);
+ return (0);
+}
+
+int
+grid_check_y(struct grid *gd, u_int py)
+{
+ if ((py) >= (gd)->hsize + (gd)->sy)
+ log_fatalx("y out of range: %u", py);
+ return (0);
+}
+#else
+int
+grid_check_x(struct grid *gd, u_int px)
+{
+ if ((px) >= (gd)->sx) {
+ log_debug("x out of range: %u", px);
+ return (-1);
+ }
+ return (0);
+}
+
+int
+grid_check_y(struct grid *gd, u_int py)
+{
+ if ((py) >= (gd)->hsize + (gd)->sy) {
+ log_debug("y out of range: %u", py);
+ return (-1);
+ }
+ return (0);
+}
+#endif
+
+/* Create a new grid. */
+struct grid *
+grid_create(u_int sx, u_int sy, u_int hlimit)
+{
+ struct grid *gd;
+
+ gd = xmalloc(sizeof *gd);
+ gd->sx = sx;
+ gd->sy = sy;
+
+ gd->hsize = 0;
+ gd->hlimit = hlimit;
+
+ gd->size = xcalloc(gd->sy, sizeof *gd->size);
+ gd->data = xcalloc(gd->sy, sizeof *gd->data);
+
+ gd->usize = xcalloc(gd->sy, sizeof *gd->usize);
+ gd->udata = xcalloc(gd->sy, sizeof *gd->udata);
+
+ return (gd);
+}
+
+/* Destroy grid. */
+void
+grid_destroy(struct grid *gd)
+{
+ u_int yy;
+
+ for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
+ if (gd->udata[yy] != NULL)
+ xfree(gd->udata[yy]);
+ if (gd->data[yy] != NULL)
+ xfree(gd->data[yy]);
+ }
+
+ if (gd->udata != NULL)
+ xfree(gd->udata);
+ if (gd->usize != NULL)
+ xfree(gd->usize);
+
+ if (gd->data != NULL)
+ xfree(gd->data);
+ if (gd->size != NULL)
+ xfree(gd->size);
+
+ xfree(gd);
+}
+
+/* Compare grids. */
+int
+grid_compare(struct grid *ga, struct grid *gb)
+{
+ struct grid_cell *gca, *gcb;
+ struct grid_utf8 *gua, *gub;
+ u_int xx, yy;
+
+ if (ga->sx != gb->sx || ga->sy != ga->sy)
+ return (1);
+
+ for (yy = 0; yy < ga->sy; yy++) {
+ if (ga->size[yy] != gb->size[yy])
+ return (1);
+ for (xx = 0; xx < ga->sx; xx++) {
+ gca = &ga->data[yy][xx];
+ gcb = &gb->data[yy][xx];
+ if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0)
+ return (1);
+ if (!(gca->flags & GRID_FLAG_UTF8))
+ continue;
+ gua = &ga->udata[yy][xx];
+ gub = &gb->udata[yy][xx];
+ if (memcmp(gua, gub, sizeof (struct grid_utf8)) != 0)
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+/* Scroll a line into the history. */
+void
+grid_scroll_line(struct grid *gd)
+{
+ u_int yy;
+
+ GRID_DEBUG(gd, "");
+
+ if (gd->hsize >= gd->hlimit - 1) {
+ /* If the limit is hit, free the bottom 10% and shift up. */
+ yy = gd->hlimit / 10;
+ if (yy < 1)
+ yy = 1;
+
+ grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy);
+ gd->hsize -= yy;
+ }
+
+ yy = gd->hsize + gd->sy;
+
+ gd->size = xrealloc(gd->size, yy + 1, sizeof *gd->size);
+ gd->size[yy] = 0;
+ gd->data = xrealloc(gd->data, yy + 1, sizeof *gd->data);
+ gd->data[yy] = NULL;
+
+ gd->usize = xrealloc(gd->usize, yy + 1, sizeof *gd->usize);
+ gd->usize[yy] = 0;
+ gd->udata = xrealloc(gd->udata, yy + 1, sizeof *gd->udata);
+ gd->udata[yy] = NULL;
+
+ gd->hsize++;
+}
+
+/* Reduce line to fit to cell. */
+void
+grid_reduce_line(struct grid *gd, u_int py, u_int sx)
+{
+ if (sx < gd->size[py]) {
+ gd->data[py] = xrealloc(gd->data[py], sx, sizeof **gd->data);
+ gd->size[py] = sx;
+ }
+ if (sx < gd->usize[py]) {
+ gd->udata[py] = xrealloc(gd->udata[py], sx, sizeof **gd->udata);
+ gd->usize[py] = sx;
+ }
+}
+
+/* Expand line to fit to cell. */
+void
+grid_expand_line(struct grid *gd, u_int py, u_int sx)
+{
+ u_int xx;
+
+ if (sx <= gd->size[py])
+ return;
+
+ gd->data[py] = xrealloc(gd->data[py], sx, sizeof **gd->data);
+ for (xx = gd->size[py]; xx < sx; xx++)
+ grid_put_cell(gd, xx, py, &grid_default_cell);
+ gd->size[py] = sx;
+}
+
+/* Expand line to fit to cell for UTF-8. */
+void
+grid_expand_line_utf8(struct grid *gd, u_int py, u_int sx)
+{
+ if (sx <= gd->usize[py])
+ return;
+
+ gd->udata[py] = xrealloc(gd->udata[py], sx, sizeof **gd->udata);
+ gd->usize[py] = sx;
+}
+
+/* Get cell for reading. */
+const struct grid_cell *
+grid_peek_cell(struct grid *gd, u_int px, u_int py)
+{
+ if (grid_check_x(gd, px) != 0)
+ return (&grid_default_cell);
+ if (grid_check_y(gd, py) != 0)
+ return (&grid_default_cell);
+
+ if (px >= gd->size[py])
+ return (&grid_default_cell);
+ return (&gd->data[py][px]);
+}
+
+/* Get cell at relative position (for writing). */
+struct grid_cell *
+grid_get_cell(struct grid *gd, u_int px, u_int py)
+{
+ if (grid_check_x(gd, px) != 0)
+ return (NULL);
+ if (grid_check_y(gd, py) != 0)
+ return (NULL);
+
+ grid_expand_line(gd, py, px + 1);
+ return (&gd->data[py][px]);
+}
+
+/* Set cell at relative position. */
+void
+grid_set_cell(
+ struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
+{
+ if (grid_check_x(gd, px) != 0)
+ return;
+ if (grid_check_y(gd, py) != 0)
+ return;
+
+ grid_expand_line(gd, py, px + 1);
+ grid_put_cell(gd, px, py, gc);
+}
+
+/* Get UTF-8 for reading. */
+const struct grid_utf8 *
+grid_peek_utf8(struct grid *gd, u_int px, u_int py)
+{
+ if (grid_check_x(gd, px) != 0)
+ return (NULL);
+ if (grid_check_y(gd, py) != 0)
+ return (NULL);
+
+ if (px >= gd->usize[py])
+ return (NULL);
+ return (&gd->udata[py][px]);
+}
+
+/* Get utf8 at relative position (for writing). */
+struct grid_utf8 *
+grid_get_utf8(struct grid *gd, u_int px, u_int py)
+{
+ if (grid_check_x(gd, px) != 0)
+ return (NULL);
+ if (grid_check_y(gd, py) != 0)
+ return (NULL);
+
+ grid_expand_line_utf8(gd, py, px + 1);
+ return (&gd->udata[py][px]);
+}
+
+/* Set utf8 at relative position. */
+void
+grid_set_utf8(
+ struct grid *gd, u_int px, u_int py, const struct grid_utf8 *gc)
+{
+ if (grid_check_x(gd, px) != 0)
+ return;
+ if (grid_check_y(gd, py) != 0)
+ return;
+
+ grid_expand_line_utf8(gd, py, px + 1);
+ grid_put_utf8(gd, px, py, gc);
+}
+
+/*
+ * Clear area. Note this is different from a fill as it just omits unallocated
+ * cells.
+ */
+void
+grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny)
+{
+ u_int xx, yy;
+
+ GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny);
+
+ if (nx == 0 || ny == 0)
+ return;
+
+ if (px == 0 && nx == gd->sx) {
+ grid_clear_lines(gd, py, ny);
+ return;
+ }
+
+ if (grid_check_x(gd, px) != 0)
+ return;
+ if (grid_check_x(gd, px + nx - 1) != 0)
+ return;
+ if (grid_check_y(gd, py) != 0)
+ return;
+ if (grid_check_y(gd, py + ny - 1) != 0)
+ return;
+
+ for (yy = py; yy < py + ny; yy++) {
+ for (xx = px; xx < px + nx; xx++) {
+ if (xx >= gd->size[yy])
+ break;
+ grid_put_cell(gd, xx, yy, &grid_default_cell);
+ }
+ }
+}
+
+/* Clear lines. This just frees and truncates the lines. */
+void
+grid_clear_lines(struct grid *gd, u_int py, u_int ny)
+{
+ u_int yy;
+
+ GRID_DEBUG(gd, "py=%u, ny=%u", py, ny);
+
+ if (ny == 0)
+ return;
+
+ if (grid_check_y(gd, py) != 0)
+ return;
+ if (grid_check_y(gd, py + ny - 1) != 0)
+ return;
+
+ for (yy = py; yy < py + ny; yy++) {
+ if (gd->data[yy] != NULL) {
+ xfree(gd->data[yy]);
+ gd->data[yy] = NULL;
+ gd->size[yy] = 0;
+ }
+ if (gd->udata[yy] != NULL) {
+ xfree(gd->udata[yy]);
+ gd->udata[yy] = NULL;
+ gd->usize[yy] = 0;
+ }
+ }
+}
+
+/* Move a group of lines. */
+void
+grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny)
+{
+ u_int yy;
+
+ GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny);
+
+ if (ny == 0 || py == dy)
+ return;
+
+ if (grid_check_y(gd, py) != 0)
+ return;
+ if (grid_check_y(gd, py + ny - 1) != 0)
+ return;
+ if (grid_check_y(gd, dy) != 0)
+ return;
+ if (grid_check_y(gd, dy + ny - 1) != 0)
+ return;
+
+ /* Free any lines which are being replaced. */
+ for (yy = dy; yy < dy + ny; yy++) {
+ if (yy >= py && yy < py + ny)
+ continue;
+ grid_clear_lines(gd, yy, 1);
+ }
+
+ memmove(&gd->data[dy], &gd->data[py], ny * (sizeof *gd->data));
+ memmove(&gd->size[dy], &gd->size[py], ny * (sizeof *gd->size));
+
+ memmove(&gd->udata[dy], &gd->udata[py], ny * (sizeof *gd->udata));
+ memmove(&gd->usize[dy], &gd->usize[py], ny * (sizeof *gd->usize));
+
+ /* Wipe any lines that have been moved (without freeing them). */
+ for (yy = py; yy < py + ny; yy++) {
+ if (yy >= dy && yy < dy + ny)
+ continue;
+ gd->data[yy] = NULL;
+ gd->size[yy] = 0;
+ gd->udata[yy] = NULL;
+ gd->usize[yy] = 0;
+ }
+}
+
+/* Clear a group of cells. */
+void
+grid_clear_cells(struct grid *gd, u_int px, u_int py, u_int nx)
+{
+ u_int xx;
+
+ GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx);
+
+ if (nx == 0)
+ return;
+
+ if (grid_check_x(gd, px) != 0)
+ return;
+ if (grid_check_x(gd, px + nx - 1) != 0)
+ return;
+ if (grid_check_y(gd, py) != 0)
+ return;
+
+ for (xx = px; xx < px + nx; xx++) {
+ if (xx >= gd->size[py])
+ break;
+ grid_put_cell(gd, xx, py, &grid_default_cell);
+ }
+}
+
+/* Move a group of cells. */
+void
+grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx)
+{
+ u_int xx;
+
+ GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx);
+
+ if (nx == 0 || px == dx)
+ return;
+
+ if (grid_check_x(gd, px) != 0)
+ return;
+ if (grid_check_x(gd, px + nx - 1) != 0)
+ return;
+ if (grid_check_x(gd, dx + nx - 1) != 0)
+ return;
+ if (grid_check_y(gd, py) != 0)
+ return;
+
+ grid_expand_line(gd, py, px + nx);
+ grid_expand_line(gd, py, dx + nx);
+ memmove(&gd->data[py][dx], &gd->data[py][px], nx * (sizeof **gd->data));
+
+ if (gd->udata[py] != NULL) {
+ grid_expand_line_utf8(gd, py, px + nx);
+ grid_expand_line_utf8(gd, py, dx + nx);
+ memmove(&gd->udata[py][dx],
+ &gd->udata[py][px], nx * (sizeof **gd->udata));
+ }
+
+ /* Wipe any cells that have been moved. */
+ for (xx = px; xx < px + nx; xx++) {
+ if (xx >= dx && xx < dx + nx)
+ continue;
+ grid_put_cell(gd, xx, py, &grid_default_cell);
+ }
+}
+
+
diff --git a/usr.bin/tmux/input-keys.c b/usr.bin/tmux/input-keys.c
new file mode 100644
index 00000000000..dc49f709d73
--- /dev/null
+++ b/usr.bin/tmux/input-keys.c
@@ -0,0 +1,227 @@
+/* $OpenBSD: input-keys.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+struct input_key_ent {
+ int key;
+ const char *data;
+
+ int flags;
+#define INPUTKEY_KEYPAD 0x1 /* keypad key */
+#define INPUTKEY_CURSOR 0x2 /* cursor key */
+#define INPUTKEY_CTRL 0x4 /* may be modified with ctrl */
+#define INPUTKEY_XTERM 0x4 /* may have xterm argument appended */
+};
+
+struct input_key_ent input_keys[] = {
+ /* Function keys. */
+ { KEYC_F1, "\033OP", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F2, "\033OQ", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F3, "\033OR", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F4, "\033OS", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F5, "\033[15~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F6, "\033[17~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F7, "\033[18~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F8, "\033[19~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F9, "\033[20~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F10, "\033[21~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F11, "\033[23~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F12, "\033[24~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F13, "\033[25~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F14, "\033[26~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F15, "\033[28~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F16, "\033[29~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F17, "\033[31~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F18, "\033[32~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F19, "\033[33~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_F20, "\033[34~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_IC, "\033[2~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_DC, "\033[3~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_HOME, "\033[1~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_END, "\033[4~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_NPAGE, "\033[6~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_PPAGE, "\033[5~", INPUTKEY_CTRL|INPUTKEY_XTERM },
+ { KEYC_BTAB, "\033[Z", INPUTKEY_CTRL },
+
+ /* Arrow keys. Cursor versions must come first. */
+ { KEYC_ADDCTL(KEYC_UP), "\033Oa", 0 },
+ { KEYC_ADDCTL(KEYC_DOWN), "\033Ob", 0 },
+ { KEYC_ADDCTL(KEYC_RIGHT), "\033Oc", 0 },
+ { KEYC_ADDCTL(KEYC_LEFT), "\033Od", 0 },
+
+ { KEYC_ADDSFT(KEYC_UP), "\033[a", 0 },
+ { KEYC_ADDSFT(KEYC_DOWN), "\033[b", 0 },
+ { KEYC_ADDSFT(KEYC_RIGHT), "\033[c", 0 },
+ { KEYC_ADDSFT(KEYC_LEFT), "\033[d", 0 },
+
+ { KEYC_UP, "\033OA", INPUTKEY_CURSOR },
+ { KEYC_DOWN, "\033OB", INPUTKEY_CURSOR },
+ { KEYC_RIGHT, "\033OC", INPUTKEY_CURSOR },
+ { KEYC_LEFT, "\033OD", INPUTKEY_CURSOR },
+
+ { KEYC_UP, "\033[A", 0 },
+ { KEYC_DOWN, "\033[B", 0 },
+ { KEYC_RIGHT, "\033[C", 0 },
+ { KEYC_LEFT, "\033[D", 0 },
+
+ /* Keypad keys. Keypad versions must come first. */
+ { KEYC_KP0_1, "/", INPUTKEY_KEYPAD },
+ { KEYC_KP0_2, "*", INPUTKEY_KEYPAD },
+ { KEYC_KP0_3, "-", INPUTKEY_KEYPAD },
+ { KEYC_KP1_0, "7", INPUTKEY_KEYPAD },
+ { KEYC_KP1_1, "8", INPUTKEY_KEYPAD },
+ { KEYC_KP1_2, "9", INPUTKEY_KEYPAD },
+ { KEYC_KP1_3, "+", INPUTKEY_KEYPAD },
+ { KEYC_KP2_0, "4", INPUTKEY_KEYPAD },
+ { KEYC_KP2_1, "5", INPUTKEY_KEYPAD },
+ { KEYC_KP2_2, "6", INPUTKEY_KEYPAD },
+ { KEYC_KP3_0, "1", INPUTKEY_KEYPAD },
+ { KEYC_KP3_1, "2", INPUTKEY_KEYPAD },
+ { KEYC_KP3_2, "3", INPUTKEY_KEYPAD },
+ { KEYC_KP3_3, "\n", INPUTKEY_KEYPAD }, /* this can be CRLF too? */
+ { KEYC_KP4_0, "0", INPUTKEY_KEYPAD },
+ { KEYC_KP4_2, ".", INPUTKEY_KEYPAD },
+ { KEYC_KP0_1, "\033Oo", 0 },
+ { KEYC_KP0_2, "\033Oj", 0 },
+ { KEYC_KP0_3, "\033Om", 0 },
+ { KEYC_KP1_0, "\033Ow", 0 },
+ { KEYC_KP1_1, "\033Ox", 0 },
+ { KEYC_KP1_2, "\033Oy", 0 },
+ { KEYC_KP1_3, "\033Ok", 0 },
+ { KEYC_KP2_0, "\033Ot", 0 },
+ { KEYC_KP2_1, "\033Ou", 0 },
+ { KEYC_KP2_2, "\033Ov", 0 },
+ { KEYC_KP3_0, "\033Oq", 0 },
+ { KEYC_KP3_1, "\033Or", 0 },
+ { KEYC_KP3_2, "\033Os", 0 },
+ { KEYC_KP3_3, "\033OM", 0 },
+ { KEYC_KP4_0, "\033Op", 0 },
+ { KEYC_KP4_2, "\033On", 0 },
+};
+
+/* Translate a key code from client into an output key sequence. */
+void
+input_key(struct window_pane *wp, int key)
+{
+ struct input_key_ent *ike;
+ u_int i;
+ char ch;
+ size_t dlen;
+ int xterm_keys;
+
+ log_debug2("writing key 0x%x", key);
+
+ if (key != KEYC_NONE && KEYC_REMOVEESC(key) < KEYC_OFFSET) {
+ if (KEYC_ISESC(key))
+ buffer_write8(wp->out, '\033');
+ buffer_write8(wp->out, (uint8_t) KEYC_REMOVEESC(key));
+ return;
+ }
+
+ for (i = 0; i < nitems(input_keys); i++) {
+ ike = &input_keys[i];
+
+ if ((ike->flags & INPUTKEY_KEYPAD) &&
+ !(wp->screen->mode & MODE_KKEYPAD))
+ continue;
+ if ((ike->flags & INPUTKEY_CURSOR) &&
+ !(wp->screen->mode & MODE_KCURSOR))
+ continue;
+
+ if (KEYC_ISESC(key) && KEYC_ADDESC(ike->key) == key)
+ break;
+ if (KEYC_ISSFT(key) && KEYC_ADDSFT(ike->key) == key)
+ break;
+ if (KEYC_ISCTL(key) && KEYC_ADDCTL(ike->key) == key) {
+ if (ike->flags & INPUTKEY_CTRL)
+ break;
+ }
+ if (ike->key == key)
+ break;
+ }
+ if (i == nitems(input_keys)) {
+ log_debug2("key 0x%x missing", key);
+ return;
+ }
+ dlen = strlen(ike->data);
+
+ log_debug2("found key 0x%x: \"%s\"", key, ike->data);
+
+ /*
+ * If in xterm keys mode, work out and append the modifier as an
+ * argument.
+ */
+ xterm_keys = options_get_number(&wp->window->options, "xterm-keys");
+ if (xterm_keys && ike->flags & INPUTKEY_XTERM) {
+ ch = '\0';
+ if (KEYC_ISSFT(key) && KEYC_ISESC(key) && KEYC_ISCTL(key))
+ ch = '8';
+ else if (KEYC_ISESC(key) && KEYC_ISCTL(key))
+ ch = '7';
+ else if (KEYC_ISSFT(key) && KEYC_ISCTL(key))
+ ch = '6';
+ else if (KEYC_ISCTL(key))
+ ch = '5';
+ else if (KEYC_ISSFT(key) && KEYC_ISESC(key))
+ ch = '4';
+ else if (KEYC_ISESC(key))
+ ch = '3';
+ else if (KEYC_ISSFT(key))
+ ch = '2';
+ if (ch != '\0') {
+ buffer_write(wp->out, ike->data, dlen - 1);
+ buffer_write8(wp->out, ';');
+ buffer_write8(wp->out, ch);
+ buffer_write8(wp->out, ike->data[dlen - 1]);
+ } else
+ buffer_write(wp->out, ike->data, dlen);
+ return;
+ }
+
+ /*
+ * Not in xterm mode. Prefix a \033 for escape, and set bit 5 of the
+ * last byte for ctrl.
+ */
+ if (KEYC_ISESC(key))
+ buffer_write8(wp->out, '\033');
+ if (KEYC_ISCTL(key) && ike->flags & INPUTKEY_CTRL) {
+ buffer_write(wp->out, ike->data, dlen - 1);
+ buffer_write8(wp->out, ike->data[dlen - 1] ^ 0x20);
+ return;
+ }
+ buffer_write(wp->out, ike->data, dlen);
+}
+
+/* Handle input mouse. */
+void
+input_mouse(struct window_pane *wp, u_char b, u_char x, u_char y)
+{
+ if (wp->screen->mode & MODE_MOUSE) {
+ buffer_write(wp->out, "\033[M", 3);
+ buffer_write8(wp->out, b + 32);
+ buffer_write8(wp->out, x + 33);
+ buffer_write8(wp->out, y + 33);
+ }
+}
diff --git a/usr.bin/tmux/input.c b/usr.bin/tmux/input.c
new file mode 100644
index 00000000000..2f7105fb7af
--- /dev/null
+++ b/usr.bin/tmux/input.c
@@ -0,0 +1,1276 @@
+/* $OpenBSD: input.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+#define INPUT_C0CONTROL(ch) (ch <= 0x1f)
+#define INPUT_INTERMEDIATE(ch) (ch == 0xa0 || (ch >= 0x20 && ch <= 0x2f))
+#define INPUT_PARAMETER(ch) (ch >= 0x30 && ch <= 0x3f)
+#define INPUT_UPPERCASE(ch) (ch >= 0x40 && ch <= 0x5f)
+#define INPUT_LOWERCASE(ch) (ch >= 0x60 && ch <= 0x7e)
+#define INPUT_DELETE(ch) (ch == 0x7f)
+#define INPUT_C1CONTROL(ch) (ch >= 0x80 && ch <= 0x9f)
+#define INPUT_G1DISPLAYABLE(ch) (ch >= 0xa1 && ch <= 0xfe)
+#define INPUT_SPECIAL(ch) (ch == 0xff)
+
+int input_get_argument(struct input_ctx *, u_int, uint16_t *, uint16_t);
+int input_new_argument(struct input_ctx *);
+int input_add_argument(struct input_ctx *, u_char);
+
+void input_start_string(struct input_ctx *, int);
+void input_abort_string(struct input_ctx *);
+int input_add_string(struct input_ctx *, u_char);
+char *input_get_string(struct input_ctx *);
+
+void input_state(struct input_ctx *, void *);
+
+void input_state_first(u_char, struct input_ctx *);
+void input_state_escape(u_char, struct input_ctx *);
+void input_state_intermediate(u_char, struct input_ctx *);
+void input_state_sequence_first(u_char, struct input_ctx *);
+void input_state_sequence_next(u_char, struct input_ctx *);
+void input_state_sequence_intermediate(u_char, struct input_ctx *);
+void input_state_string_next(u_char, struct input_ctx *);
+void input_state_string_escape(u_char, struct input_ctx *);
+void input_state_utf8(u_char, struct input_ctx *);
+
+void input_handle_character(u_char, struct input_ctx *);
+void input_handle_c0_control(u_char, struct input_ctx *);
+void input_handle_c1_control(u_char, struct input_ctx *);
+void input_handle_private_two(u_char, struct input_ctx *);
+void input_handle_standard_two(u_char, struct input_ctx *);
+void input_handle_sequence(u_char, struct input_ctx *);
+
+void input_handle_sequence_cuu(struct input_ctx *);
+void input_handle_sequence_cud(struct input_ctx *);
+void input_handle_sequence_cuf(struct input_ctx *);
+void input_handle_sequence_cub(struct input_ctx *);
+void input_handle_sequence_dch(struct input_ctx *);
+void input_handle_sequence_dl(struct input_ctx *);
+void input_handle_sequence_ich(struct input_ctx *);
+void input_handle_sequence_il(struct input_ctx *);
+void input_handle_sequence_vpa(struct input_ctx *);
+void input_handle_sequence_hpa(struct input_ctx *);
+void input_handle_sequence_cup(struct input_ctx *);
+void input_handle_sequence_cup(struct input_ctx *);
+void input_handle_sequence_ed(struct input_ctx *);
+void input_handle_sequence_el(struct input_ctx *);
+void input_handle_sequence_sm(struct input_ctx *);
+void input_handle_sequence_rm(struct input_ctx *);
+void input_handle_sequence_decstbm(struct input_ctx *);
+void input_handle_sequence_sgr(struct input_ctx *);
+void input_handle_sequence_dsr(struct input_ctx *);
+
+int input_sequence_cmp(const void *, const void *);
+
+struct input_sequence_entry {
+ u_char ch;
+ void (*fn)(struct input_ctx *);
+};
+const struct input_sequence_entry input_sequence_table[] = {
+ { '@', input_handle_sequence_ich },
+ { 'A', input_handle_sequence_cuu },
+ { 'B', input_handle_sequence_cud },
+ { 'C', input_handle_sequence_cuf },
+ { 'D', input_handle_sequence_cub },
+ { 'G', input_handle_sequence_hpa },
+ { 'H', input_handle_sequence_cup },
+ { 'J', input_handle_sequence_ed },
+ { 'K', input_handle_sequence_el },
+ { 'L', input_handle_sequence_il },
+ { 'M', input_handle_sequence_dl },
+ { 'P', input_handle_sequence_dch },
+ { 'd', input_handle_sequence_vpa },
+ { 'f', input_handle_sequence_cup },
+ { 'h', input_handle_sequence_sm },
+ { 'l', input_handle_sequence_rm },
+ { 'm', input_handle_sequence_sgr },
+ { 'n', input_handle_sequence_dsr },
+ { 'r', input_handle_sequence_decstbm },
+};
+
+int
+input_sequence_cmp(const void *a, const void *b)
+{
+ int ai = ((const struct input_sequence_entry *) a)->ch;
+ int bi = ((const struct input_sequence_entry *) b)->ch;
+
+ return (ai - bi);
+}
+
+int
+input_new_argument(struct input_ctx *ictx)
+{
+ struct input_arg *arg;
+
+ ARRAY_EXPAND(&ictx->args, 1);
+
+ arg = &ARRAY_LAST(&ictx->args);
+ arg->used = 0;
+
+ return (0);
+}
+
+int
+input_add_argument(struct input_ctx *ictx, u_char ch)
+{
+ struct input_arg *arg;
+
+ if (ARRAY_LENGTH(&ictx->args) == 0)
+ return (0);
+
+ arg = &ARRAY_LAST(&ictx->args);
+ if (arg->used > (sizeof arg->data) - 1)
+ return (-1);
+ arg->data[arg->used++] = ch;
+
+ return (0);
+}
+
+int
+input_get_argument(struct input_ctx *ictx, u_int i, uint16_t *n, uint16_t d)
+{
+ struct input_arg *arg;
+ const char *errstr;
+
+ *n = d;
+ if (i >= ARRAY_LENGTH(&ictx->args))
+ return (0);
+
+ arg = &ARRAY_ITEM(&ictx->args, i);
+ if (*arg->data == '\0')
+ return (0);
+
+ *n = strtonum(arg->data, 0, UINT16_MAX, &errstr);
+ if (errstr != NULL)
+ return (-1);
+ return (0);
+}
+
+void
+input_start_string(struct input_ctx *ictx, int type)
+{
+ ictx->string_type = type;
+ ictx->string_len = 0;
+}
+
+void
+input_abort_string(struct input_ctx *ictx)
+{
+ if (ictx->string_buf != NULL)
+ xfree(ictx->string_buf);
+ ictx->string_buf = NULL;
+}
+
+int
+input_add_string(struct input_ctx *ictx, u_char ch)
+{
+ ictx->string_buf = xrealloc(ictx->string_buf, 1, ictx->string_len + 1);
+ ictx->string_buf[ictx->string_len++] = ch;
+
+ if (ictx->string_len >= MAXSTRINGLEN) {
+ input_abort_string(ictx);
+ return (1);
+ }
+
+ return (0);
+}
+
+char *
+input_get_string(struct input_ctx *ictx)
+{
+ char *s;
+
+ if (ictx->string_buf == NULL || input_add_string(ictx, '\0') != 0)
+ return (xstrdup(""));
+
+ s = ictx->string_buf;
+ ictx->string_buf = NULL;
+ return (s);
+}
+
+void
+input_state(struct input_ctx *ictx, void *state)
+{
+ ictx->state = state;
+}
+
+void
+input_init(struct window_pane *wp)
+{
+ struct input_ctx *ictx = &wp->ictx;
+
+ ARRAY_INIT(&ictx->args);
+
+ ictx->string_len = 0;
+ ictx->string_buf = NULL;
+
+ memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell);
+
+ memcpy(&ictx->saved_cell, &grid_default_cell, sizeof ictx->saved_cell);
+ ictx->saved_cx = 0;
+ ictx->saved_cy = 0;
+
+ input_state(ictx, input_state_first);
+}
+
+void
+input_free(struct window_pane *wp)
+{
+ if (wp->ictx.string_buf != NULL)
+ xfree(wp->ictx.string_buf);
+
+ ARRAY_FREE(&wp->ictx.args);
+}
+
+void
+input_parse(struct window_pane *wp)
+{
+ struct input_ctx *ictx = &wp->ictx;
+ u_char ch;
+
+ if (BUFFER_USED(wp->in) == 0)
+ return;
+
+ ictx->buf = BUFFER_OUT(wp->in);
+ ictx->len = BUFFER_USED(wp->in);
+ ictx->off = 0;
+
+ ictx->wp = wp;
+
+ log_debug2("entry; buffer=%zu", ictx->len);
+
+ if (wp->mode == NULL)
+ screen_write_start(&ictx->ctx, wp, &wp->base);
+ else
+ screen_write_start(&ictx->ctx, NULL, &wp->base);
+
+ if (ictx->off != ictx->len)
+ wp->window->flags |= WINDOW_ACTIVITY;
+ while (ictx->off < ictx->len) {
+ ch = ictx->buf[ictx->off++];
+ ictx->state(ch, ictx);
+ }
+
+ screen_write_stop(&ictx->ctx);
+
+ buffer_remove(wp->in, ictx->len);
+}
+
+void
+input_state_first(u_char ch, struct input_ctx *ictx)
+{
+ ictx->intermediate = '\0';
+
+ if (INPUT_C0CONTROL(ch)) {
+ if (ch == 0x1b)
+ input_state(ictx, input_state_escape);
+ else
+ input_handle_c0_control(ch, ictx);
+ return;
+ }
+
+#if 0
+ if (INPUT_C1CONTROL(ch)) {
+ ch -= 0x40;
+ if (ch == '[')
+ input_state(ictx, input_state_sequence_first);
+ else if (ch == ']') {
+ input_start_string(ictx, STRING_SYSTEM);
+ input_state(ictx, input_state_string_next);
+ } else if (ch == '_') {
+ input_start_string(ictx, STRING_APPLICATION);
+ input_state(ictx, input_state_string_next);
+ } else
+ input_handle_c1_control(ch, ictx);
+ return;
+ }
+#endif
+
+ if (INPUT_DELETE(ch))
+ return;
+
+ input_handle_character(ch, ictx);
+}
+
+void
+input_state_escape(u_char ch, struct input_ctx *ictx)
+{
+ /* Treat C1 control and G1 displayable as 7-bit equivalent. */
+ if (INPUT_C1CONTROL(ch) || INPUT_G1DISPLAYABLE(ch))
+ ch &= 0x7f;
+
+ if (INPUT_C0CONTROL(ch)) {
+ input_handle_c0_control(ch, ictx);
+ return;
+ }
+
+ if (INPUT_INTERMEDIATE(ch)) {
+ log_debug2(":: in1 %zu: %hhu (%c)", ictx->off, ch, ch);
+ ictx->intermediate = ch;
+ input_state(ictx, input_state_intermediate);
+ return;
+ }
+
+ if (INPUT_PARAMETER(ch)) {
+ input_state(ictx, input_state_first);
+ input_handle_private_two(ch, ictx);
+ return;
+ }
+
+ if (INPUT_UPPERCASE(ch)) {
+ if (ch == '[')
+ input_state(ictx, input_state_sequence_first);
+ else if (ch == ']') {
+ input_start_string(ictx, STRING_SYSTEM);
+ input_state(ictx, input_state_string_next);
+ } else if (ch == '_') {
+ input_start_string(ictx, STRING_APPLICATION);
+ input_state(ictx, input_state_string_next);
+ } else {
+ input_state(ictx, input_state_first);
+ input_handle_c1_control(ch, ictx);
+ }
+ return;
+ }
+
+ if (INPUT_LOWERCASE(ch)) {
+ input_state(ictx, input_state_first);
+ input_handle_standard_two(ch, ictx);
+ return;
+ }
+
+ input_state(ictx, input_state_first);
+}
+
+void
+input_state_intermediate(u_char ch, struct input_ctx *ictx)
+{
+ if (INPUT_INTERMEDIATE(ch)) {
+ /* Multiple intermediates currently ignored. */
+ log_debug2(":: in2 %zu: %hhu (%c)", ictx->off, ch, ch);
+ return;
+ }
+
+ if (INPUT_PARAMETER(ch)) {
+ input_state(ictx, input_state_first);
+ input_handle_private_two(ch, ictx);
+ return;
+ }
+
+ if (INPUT_UPPERCASE(ch) || INPUT_LOWERCASE(ch)) {
+ input_state(ictx, input_state_first);
+ input_handle_standard_two(ch, ictx);
+ return;
+ }
+
+ input_state(ictx, input_state_first);
+}
+
+void
+input_state_sequence_first(u_char ch, struct input_ctx *ictx)
+{
+ ictx->private = '\0';
+ ARRAY_CLEAR(&ictx->args);
+
+ input_state(ictx, input_state_sequence_next);
+
+ if (INPUT_PARAMETER(ch)) {
+ input_new_argument(ictx);
+ if (ch >= 0x3c && ch <= 0x3f) {
+ /* Private control sequence. */
+ ictx->private = ch;
+ return;
+ }
+ }
+
+ /* Pass character on directly. */
+ input_state_sequence_next(ch, ictx);
+}
+
+void
+input_state_sequence_next(u_char ch, struct input_ctx *ictx)
+{
+ if (INPUT_INTERMEDIATE(ch)) {
+ if (input_add_argument(ictx, '\0') != 0)
+ input_state(ictx, input_state_first);
+ else {
+ log_debug2(":: si1 %zu: %hhu (%c)", ictx->off, ch, ch);
+ input_state(ictx, input_state_sequence_intermediate);
+ }
+ return;
+ }
+
+ if (INPUT_PARAMETER(ch)) {
+ if (ch == ';') {
+ if (input_add_argument(ictx, '\0') != 0)
+ input_state(ictx, input_state_first);
+ else
+ input_new_argument(ictx);
+ } else if (input_add_argument(ictx, ch) != 0)
+ input_state(ictx, input_state_first);
+ return;
+ }
+
+ if (INPUT_UPPERCASE(ch) || INPUT_LOWERCASE(ch)) {
+ if (input_add_argument(ictx, '\0') != 0)
+ input_state(ictx, input_state_first);
+ else {
+ input_state(ictx, input_state_first);
+ input_handle_sequence(ch, ictx);
+ }
+ return;
+ }
+
+ input_state(ictx, input_state_first);
+}
+
+void
+input_state_sequence_intermediate(u_char ch, struct input_ctx *ictx)
+{
+ if (INPUT_INTERMEDIATE(ch)) {
+ log_debug2(":: si2 %zu: %hhu (%c)", ictx->off, ch, ch);
+ return;
+ }
+
+ if (INPUT_UPPERCASE(ch) || INPUT_LOWERCASE(ch)) {
+ input_state(ictx, input_state_first);
+ input_handle_sequence(ch, ictx);
+ return;
+ }
+
+ input_state(ictx, input_state_first);
+}
+
+void
+input_state_string_next(u_char ch, struct input_ctx *ictx)
+{
+ if (ch == 0x1b) {
+ input_state(ictx, input_state_string_escape);
+ return;
+ }
+ if (ch == 0x07) {
+ input_state_string_escape(ch, ictx);
+ return;
+ }
+
+ if (ch >= 0x20 && ch != 0x7f) {
+ if (input_add_string(ictx, ch) != 0)
+ input_state(ictx, input_state_first);
+ return;
+ }
+}
+
+void
+input_state_string_escape(u_char ch, struct input_ctx *ictx)
+{
+ char *s;
+
+ if (ch == '\007' || ch == '\\') {
+ input_state(ictx, input_state_first);
+ switch (ictx->string_type) {
+ case STRING_SYSTEM:
+ if (ch != '\007')
+ return;
+ s = input_get_string(ictx);
+ if ((s[0] != '0' && s[0] != '2') || s[1] != ';') {
+ xfree(s);
+ return;
+ }
+ screen_set_title(ictx->ctx.s, s + 2);
+ server_status_window(ictx->wp->window);
+ xfree(s);
+ break;
+ case STRING_APPLICATION:
+ if (ch != '\\')
+ return;
+ s = input_get_string(ictx);
+ screen_set_title(ictx->ctx.s, s);
+ server_status_window(ictx->wp->window);
+ xfree(s);
+ break;
+ case STRING_NAME:
+ if (ch != '\\')
+ return;
+ xfree(ictx->wp->window->name);
+ ictx->wp->window->name = input_get_string(ictx);
+ server_status_window(ictx->wp->window);
+ break;
+ }
+ return;
+ }
+
+ input_state(ictx, input_state_string_next);
+ input_state_string_next(ch, ictx);
+}
+
+void
+input_state_utf8(u_char ch, struct input_ctx *ictx)
+{
+ log_debug2("-- un %zu: %hhu (%c)", ictx->off, ch, ch);
+
+ ictx->utf8_buf[ictx->utf8_off++] = ch;
+ if (--ictx->utf8_len != 0)
+ return;
+ input_state(ictx, input_state_first);
+
+ ictx->cell.flags |= GRID_FLAG_UTF8;
+ screen_write_cell(&ictx->ctx, &ictx->cell, ictx->utf8_buf);
+ ictx->cell.flags &= ~GRID_FLAG_UTF8;
+}
+
+void
+input_handle_character(u_char ch, struct input_ctx *ictx)
+{
+ struct window_pane *wp = ictx->wp;
+
+ if (ch > 0x7f && options_get_number(&wp->window->options, "utf8")) {
+ /*
+ * UTF-8 sequence.
+ *
+ * 11000010-11011111 C2-DF start of 2-byte sequence
+ * 11100000-11101111 E0-EF start of 3-byte sequence
+ * 11110000-11110100 F0-F4 start of 4-byte sequence
+ */
+ memset(ictx->utf8_buf, 0xff, sizeof ictx->utf8_buf);
+ ictx->utf8_buf[0] = ch;
+ ictx->utf8_off = 1;
+
+ if (ch >= 0xc2 && ch <= 0xdf) {
+ log_debug2("-- u2 %zu: %hhu (%c)", ictx->off, ch, ch);
+ input_state(ictx, input_state_utf8);
+ ictx->utf8_len = 1;
+ return;
+ }
+ if (ch >= 0xe0 && ch <= 0xef) {
+ log_debug2("-- u3 %zu: %hhu (%c)", ictx->off, ch, ch);
+ input_state(ictx, input_state_utf8);
+ ictx->utf8_len = 2;
+ return;
+ }
+ if (ch >= 0xf0 && ch <= 0xf4) {
+ log_debug2("-- u4 %zu: %hhu (%c)", ictx->off, ch, ch);
+ input_state(ictx, input_state_utf8);
+ ictx->utf8_len = 3;
+ return;
+ }
+ }
+ log_debug2("-- ch %zu: %hhu (%c)", ictx->off, ch, ch);
+
+ ictx->cell.data = ch;
+ screen_write_cell(&ictx->ctx, &ictx->cell, ictx->utf8_buf);
+}
+
+void
+input_handle_c0_control(u_char ch, struct input_ctx *ictx)
+{
+ struct screen *s = ictx->ctx.s;
+
+ log_debug2("-- c0 %zu: %hhu", ictx->off, ch);
+
+ switch (ch) {
+ case '\0': /* NUL */
+ break;
+ case '\n': /* LF */
+ screen_write_linefeed(&ictx->ctx);
+ break;
+ case '\r': /* CR */
+ screen_write_carriagereturn(&ictx->ctx);
+ break;
+ case '\007': /* BELL */
+ ictx->wp->window->flags |= WINDOW_BELL;
+ break;
+ case '\010': /* BS */
+ screen_write_cursorleft(&ictx->ctx, 1);
+ break;
+ case '\011': /* TAB */
+ s->cx = ((s->cx / 8) * 8) + 8;
+ if (s->cx > screen_size_x(s) - 1) {
+ s->cx = 0;
+ screen_write_cursordown(&ictx->ctx, 1);
+ }
+ screen_write_cursormove(&ictx->ctx, s->cx, s->cy);
+ break;
+ case '\016': /* SO */
+ ictx->cell.attr |= GRID_ATTR_CHARSET;
+ break;
+ case '\017': /* SI */
+ ictx->cell.attr &= ~GRID_ATTR_CHARSET;
+ break;
+ default:
+ log_debug("unknown c0: %hhu", ch);
+ break;
+ }
+}
+
+void
+input_handle_c1_control(u_char ch, struct input_ctx *ictx)
+{
+ log_debug2("-- c1 %zu: %hhu (%c)", ictx->off, ch, ch);
+
+ switch (ch) {
+ case 'E': /* NEL */
+ screen_write_carriagereturn(&ictx->ctx);
+ screen_write_linefeed(&ictx->ctx);
+ break;
+ case 'M': /* RI */
+ screen_write_reverseindex(&ictx->ctx);
+ break;
+ default:
+ log_debug("unknown c1: %hhu", ch);
+ break;
+ }
+}
+
+void
+input_handle_private_two(u_char ch, struct input_ctx *ictx)
+{
+ struct screen *s = ictx->ctx.s;
+
+ log_debug2(
+ "-- p2 %zu: %hhu (%c) %hhu", ictx->off, ch, ch, ictx->intermediate);
+
+ switch (ch) {
+ case '0': /* Dscs (graphics) */
+ /*
+ * Not really supported, but fake it up enough for those that
+ * use it to switch character sets (by redefining G0 to
+ * graphics set, rather than switching to G1).
+ */
+ switch (ictx->intermediate) {
+ case '(': /* G0 */
+ ictx->cell.attr |= GRID_ATTR_CHARSET;
+ break;
+ }
+ break;
+ case '=': /* DECKPAM */
+ screen_write_kkeypadmode(&ictx->ctx, 1);
+ log_debug("kkeypad on (application mode)");
+ break;
+ case '>': /* DECKPNM */
+ screen_write_kkeypadmode(&ictx->ctx, 0);
+ log_debug("kkeypad off (number mode)");
+ break;
+ case '7': /* DECSC */
+ memcpy(&ictx->saved_cell, &ictx->cell, sizeof ictx->saved_cell);
+ ictx->saved_cx = s->cx;
+ ictx->saved_cy = s->cy;
+ break;
+ case '8': /* DECRC */
+ memcpy(&ictx->cell, &ictx->saved_cell, sizeof ictx->cell);
+ screen_write_cursormove(
+ &ictx->ctx, ictx->saved_cx, ictx->saved_cy);
+ break;
+ default:
+ log_debug("unknown p2: %hhu", ch);
+ break;
+ }
+}
+
+void
+input_handle_standard_two(u_char ch, struct input_ctx *ictx)
+{
+ log_debug2(
+ "-- s2 %zu: %hhu (%c) %hhu", ictx->off, ch, ch, ictx->intermediate);
+
+ switch (ch) {
+ case 'B': /* Dscs (ASCII) */
+ /*
+ * Not really supported, but fake it up enough for those that
+ * use it to switch character sets (by redefining G0 to
+ * graphics set, rather than switching to G1).
+ */
+ switch (ictx->intermediate) {
+ case '(': /* G0 */
+ ictx->cell.attr &= ~GRID_ATTR_CHARSET;
+ break;
+ }
+ break;
+ case 'c': /* RIS */
+ memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell);
+
+ memcpy(&ictx->saved_cell, &ictx->cell, sizeof ictx->saved_cell);
+ ictx->saved_cx = 0;
+ ictx->saved_cy = 0;
+
+ screen_write_scrollregion(
+ &ictx->ctx, 0, screen_size_y(ictx->ctx.s) - 1);
+
+ screen_write_insertmode(&ictx->ctx, 0);
+ screen_write_kcursormode(&ictx->ctx, 0);
+ screen_write_kkeypadmode(&ictx->ctx, 0);
+ screen_write_mousemode(&ictx->ctx, 0);
+
+ screen_write_clearscreen(&ictx->ctx);
+ screen_write_cursormove(&ictx->ctx, 0, 0);
+ break;
+ case 'k':
+ input_start_string(ictx, STRING_NAME);
+ input_state(ictx, input_state_string_next);
+ break;
+ default:
+ log_debug("unknown s2: %hhu", ch);
+ break;
+ }
+}
+
+void
+input_handle_sequence(u_char ch, struct input_ctx *ictx)
+{
+ struct input_sequence_entry *entry, find;
+ struct screen *s = ictx->ctx.s;
+ u_int i;
+ struct input_arg *iarg;
+
+ log_debug2("-- sq %zu: %hhu (%c): %u [sx=%u, sy=%u, cx=%u, cy=%u, "
+ "ru=%u, rl=%u]", ictx->off, ch, ch, ARRAY_LENGTH(&ictx->args),
+ screen_size_x(s), screen_size_y(s), s->cx, s->cy, s->rupper,
+ s->rlower);
+ for (i = 0; i < ARRAY_LENGTH(&ictx->args); i++) {
+ iarg = &ARRAY_ITEM(&ictx->args, i);
+ if (*iarg->data != '\0')
+ log_debug2(" ++ %u: %s", i, iarg->data);
+ }
+
+ find.ch = ch;
+ entry = bsearch(&find,
+ input_sequence_table, nitems(input_sequence_table),
+ sizeof input_sequence_table[0], input_sequence_cmp);
+ if (entry != NULL)
+ entry->fn(ictx);
+ else
+ log_debug("unknown sq: %c (%hhu %hhu)", ch, ch, ictx->private);
+}
+
+void
+input_handle_sequence_cuu(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+ if (n == 0)
+ n = 1;
+
+ screen_write_cursorup(&ictx->ctx, n);
+}
+
+void
+input_handle_sequence_cud(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+ if (n == 0)
+ n = 1;
+
+ screen_write_cursordown(&ictx->ctx, n);
+}
+
+void
+input_handle_sequence_cuf(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+ if (n == 0)
+ n = 1;
+
+ screen_write_cursorright(&ictx->ctx, n);
+}
+
+void
+input_handle_sequence_cub(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+ if (n == 0)
+ n = 1;
+
+ screen_write_cursorleft(&ictx->ctx, n);
+}
+
+void
+input_handle_sequence_dch(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+ if (n == 0)
+ n = 1;
+
+ screen_write_deletecharacter(&ictx->ctx, n);
+}
+
+void
+input_handle_sequence_dl(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+ if (n == 0)
+ n = 1;
+
+ screen_write_deleteline(&ictx->ctx, n);
+}
+
+void
+input_handle_sequence_ich(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+ if (n == 0)
+ n = 1;
+
+ screen_write_insertcharacter(&ictx->ctx, n);
+}
+
+void
+input_handle_sequence_il(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+ if (n == 0)
+ n = 1;
+
+ screen_write_insertline(&ictx->ctx, n);
+}
+
+void
+input_handle_sequence_vpa(struct input_ctx *ictx)
+{
+ struct screen *s = ictx->ctx.s;
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+ if (n == 0)
+ n = 1;
+
+ screen_write_cursormove(&ictx->ctx, s->cx, n - 1);
+}
+
+void
+input_handle_sequence_hpa(struct input_ctx *ictx)
+{
+ struct screen *s = ictx->ctx.s;
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+ if (n == 0)
+ n = 1;
+
+ screen_write_cursormove(&ictx->ctx, n - 1, s->cy);
+}
+
+void
+input_handle_sequence_cup(struct input_ctx *ictx)
+{
+ uint16_t n, m;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 2)
+ return;
+ if (input_get_argument(ictx, 0, &n, 1) != 0)
+ return;
+ if (input_get_argument(ictx, 1, &m, 1) != 0)
+ return;
+ if (n == 0)
+ n = 1;
+ if (m == 0)
+ m = 1;
+
+ screen_write_cursormove(&ictx->ctx, m - 1, n - 1);
+}
+
+void
+input_handle_sequence_ed(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 0) != 0)
+ return;
+ if (n > 2)
+ return;
+
+ switch (n) {
+ case 0:
+ screen_write_clearendofscreen(&ictx->ctx);
+ break;
+ case 1:
+ screen_write_clearstartofscreen(&ictx->ctx);
+ break;
+ case 2:
+ screen_write_clearscreen(&ictx->ctx);
+ break;
+ }
+}
+
+void
+input_handle_sequence_el(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 0) != 0)
+ return;
+ if (n > 2)
+ return;
+
+ switch (n) {
+ case 0:
+ screen_write_clearendofline(&ictx->ctx);
+ break;
+ case 1:
+ screen_write_clearstartofline(&ictx->ctx);
+ break;
+ case 2:
+ screen_write_clearline(&ictx->ctx);
+ break;
+ }
+}
+
+void
+input_handle_sequence_sm(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 0) != 0)
+ return;
+
+ if (ictx->private == '?') {
+ switch (n) {
+ case 1: /* GATM */
+ screen_write_kcursormode(&ictx->ctx, 1);
+ log_debug("kcursor on");
+ break;
+ case 25: /* TCEM */
+ screen_write_cursormode(&ictx->ctx, 1);
+ log_debug("cursor on");
+ break;
+ case 1000:
+ screen_write_mousemode(&ictx->ctx, 1);
+ log_debug("mouse on");
+ break;
+ default:
+ log_debug("unknown SM [%hhu]: %u", ictx->private, n);
+ break;
+ }
+ } else {
+ switch (n) {
+ case 4: /* IRM */
+ screen_write_insertmode(&ictx->ctx, 1);
+ log_debug("insert on");
+ break;
+ case 34:
+ /* Cursor high visibility not supported. */
+ break;
+ default:
+ log_debug("unknown SM [%hhu]: %u", ictx->private, n);
+ break;
+ }
+ }
+}
+
+void
+input_handle_sequence_rm(struct input_ctx *ictx)
+{
+ uint16_t n;
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 0) != 0)
+ return;
+
+ if (ictx->private == '?') {
+ switch (n) {
+ case 1: /* GATM */
+ screen_write_kcursormode(&ictx->ctx, 0);
+ log_debug("kcursor off");
+ break;
+ case 25: /* TCEM */
+ screen_write_cursormode(&ictx->ctx, 0);
+ log_debug("cursor off");
+ break;
+ case 1000:
+ screen_write_mousemode(&ictx->ctx, 0);
+ log_debug("mouse off");
+ break;
+ default:
+ log_debug("unknown RM [%hhu]: %u", ictx->private, n);
+ break;
+ }
+ } else if (ictx->private == '\0') {
+ switch (n) {
+ case 4: /* IRM */
+ screen_write_insertmode(&ictx->ctx, 0);
+ log_debug("insert off");
+ break;
+ case 34:
+ /* Cursor high visibility not supported. */
+ break;
+ default:
+ log_debug("unknown RM [%hhu]: %u", ictx->private, n);
+ break;
+ }
+ }
+}
+
+void
+input_handle_sequence_dsr(struct input_ctx *ictx)
+{
+ struct screen *s = ictx->ctx.s;
+ uint16_t n;
+ char reply[32];
+
+ if (ARRAY_LENGTH(&ictx->args) > 1)
+ return;
+ if (input_get_argument(ictx, 0, &n, 0) != 0)
+ return;
+
+ if (ictx->private == '\0') {
+ switch (n) {
+ case 6: /* cursor position */
+ xsnprintf(reply, sizeof reply,
+ "\033[%u;%uR", s->cy + 1, s->cx + 1);
+ log_debug("cursor request, reply: %s", reply);
+ buffer_write(ictx->wp->out, reply, strlen(reply));
+ break;
+ }
+ }
+
+}
+
+void
+input_handle_sequence_decstbm(struct input_ctx *ictx)
+{
+ struct screen *s = ictx->ctx.s;
+ uint16_t n, m;
+
+ if (ictx->private != '\0')
+ return;
+
+ if (ARRAY_LENGTH(&ictx->args) > 2)
+ return;
+ if (input_get_argument(ictx, 0, &n, 0) != 0)
+ return;
+ if (input_get_argument(ictx, 1, &m, 0) != 0)
+ return;
+ if (n == 0)
+ n = 1;
+ if (m == 0)
+ m = screen_size_y(s);
+
+ screen_write_scrollregion(&ictx->ctx, n - 1, m - 1);
+}
+
+void
+input_handle_sequence_sgr(struct input_ctx *ictx)
+{
+ struct grid_cell *gc = &ictx->cell;
+ u_int i;
+ uint16_t m, o;
+ u_char attr;
+
+ if (ARRAY_LENGTH(&ictx->args) == 0) {
+ attr = gc->attr;
+ memcpy(gc, &grid_default_cell, sizeof *gc);
+ gc->attr |= (attr & GRID_ATTR_CHARSET);
+ return;
+ }
+
+ for (i = 0; i < ARRAY_LENGTH(&ictx->args); i++) {
+ if (input_get_argument(ictx, i, &m, 0) != 0)
+ return;
+
+ if (m == 38 || m == 48) {
+ i++;
+ if (input_get_argument(ictx, i, &o, 0) != 0)
+ return;
+ if (o != 5)
+ continue;
+
+ i++;
+ if (input_get_argument(ictx, i, &o, 0) != 0)
+ return;
+ if (m == 38) {
+ gc->flags |= GRID_FLAG_FG256;
+ gc->fg = o;
+ } else if (m == 48) {
+ gc->flags |= GRID_FLAG_BG256;
+ gc->bg = o;
+ }
+ continue;
+ }
+
+ switch (m) {
+ case 0:
+ case 10:
+ attr = gc->attr;
+ memcpy(gc, &grid_default_cell, sizeof *gc);
+ gc->attr |= (attr & GRID_ATTR_CHARSET);
+ break;
+ case 1:
+ gc->attr |= GRID_ATTR_BRIGHT;
+ break;
+ case 2:
+ gc->attr |= GRID_ATTR_DIM;
+ break;
+ case 3:
+ gc->attr |= GRID_ATTR_ITALICS;
+ break;
+ case 4:
+ gc->attr |= GRID_ATTR_UNDERSCORE;
+ break;
+ case 5:
+ gc->attr |= GRID_ATTR_BLINK;
+ break;
+ case 7:
+ gc->attr |= GRID_ATTR_REVERSE;
+ break;
+ case 8:
+ gc->attr |= GRID_ATTR_HIDDEN;
+ break;
+ case 22:
+ gc->attr &= ~(GRID_ATTR_BRIGHT|GRID_ATTR_DIM);
+ break;
+ case 23:
+ gc->attr &= ~GRID_ATTR_ITALICS;
+ break;
+ case 24:
+ gc->attr &= ~GRID_ATTR_UNDERSCORE;
+ break;
+ case 25:
+ gc->attr &= ~GRID_ATTR_BLINK;
+ break;
+ case 27:
+ gc->attr &= ~GRID_ATTR_REVERSE;
+ break;
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ gc->flags &= ~GRID_FLAG_FG256;
+ gc->fg = m - 30;
+ break;
+ case 39:
+ gc->flags &= ~GRID_FLAG_FG256;
+ gc->fg = 8;
+ break;
+ case 40:
+ case 41:
+ case 42:
+ case 43:
+ case 44:
+ case 45:
+ case 46:
+ case 47:
+ gc->flags &= ~GRID_FLAG_BG256;
+ gc->bg = m - 40;
+ break;
+ case 49:
+ gc->flags &= ~GRID_FLAG_BG256;
+ gc->bg = 8;
+ break;
+ }
+ }
+}
diff --git a/usr.bin/tmux/key-bindings.c b/usr.bin/tmux/key-bindings.c
new file mode 100644
index 00000000000..743be1ee736
--- /dev/null
+++ b/usr.bin/tmux/key-bindings.c
@@ -0,0 +1,237 @@
+/* $OpenBSD: key-bindings.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+SPLAY_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp);
+
+struct key_bindings key_bindings;
+
+int
+key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2)
+{
+ return (bd1->key - bd2->key);
+}
+
+struct key_binding *
+key_bindings_lookup(int key)
+{
+ struct key_binding bd;
+
+ bd.key = key;
+ return (SPLAY_FIND(key_bindings, &key_bindings, &bd));
+}
+
+void
+key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist)
+{
+ struct key_binding *bd;
+
+ if ((bd = key_bindings_lookup(key)) == NULL) {
+ bd = xmalloc(sizeof *bd);
+ bd->key = key;
+ SPLAY_INSERT(key_bindings, &key_bindings, bd);
+ } else
+ cmd_list_free(bd->cmdlist);
+ bd->can_repeat = can_repeat;
+ bd->cmdlist = cmdlist;
+}
+
+void
+key_bindings_remove(int key)
+{
+ struct key_binding *bd;
+
+ if ((bd = key_bindings_lookup(key)) == NULL)
+ return;
+ SPLAY_REMOVE(key_bindings, &key_bindings, bd);
+
+ cmd_list_free(bd->cmdlist);
+ xfree(bd);
+}
+
+void
+key_bindings_init(void)
+{
+ static const struct {
+ int key;
+ int can_repeat;
+ const struct cmd_entry *entry;
+ } table[] = {
+ { ' ', 0, &cmd_next_layout_entry },
+ { '!', 0, &cmd_break_pane_entry },
+ { '"', 0, &cmd_split_window_entry },
+ { '#', 0, &cmd_list_buffers_entry },
+ { '&', 0, &cmd_confirm_before_entry },
+ { ',', 0, &cmd_command_prompt_entry },
+ { '-', 0, &cmd_delete_buffer_entry },
+ { '.', 0, &cmd_command_prompt_entry },
+ { '0', 0, &cmd_select_window_entry },
+ { '1', 0, &cmd_select_window_entry },
+ { '2', 0, &cmd_select_window_entry },
+ { '3', 0, &cmd_select_window_entry },
+ { '4', 0, &cmd_select_window_entry },
+ { '5', 0, &cmd_select_window_entry },
+ { '6', 0, &cmd_select_window_entry },
+ { '7', 0, &cmd_select_window_entry },
+ { '8', 0, &cmd_select_window_entry },
+ { '9', 0, &cmd_select_window_entry },
+ { ':', 0, &cmd_command_prompt_entry },
+ { '=', 0, &cmd_scroll_mode_entry },
+ { '?', 0, &cmd_list_keys_entry },
+ { '[', 0, &cmd_copy_mode_entry },
+ { '\'', 0, &cmd_select_prompt_entry },
+ { '\032', /* C-z */ 0, &cmd_suspend_client_entry },
+ { ']', 0, &cmd_paste_buffer_entry },
+ { 'c', 0, &cmd_new_window_entry },
+ { 'd', 0, &cmd_detach_client_entry },
+ { 'f', 0, &cmd_command_prompt_entry },
+ { 'l', 0, &cmd_last_window_entry },
+ { 'n', 0, &cmd_next_window_entry },
+ { 'o', 0, &cmd_down_pane_entry },
+ { 'p', 0, &cmd_previous_window_entry },
+ { 'r', 0, &cmd_refresh_client_entry },
+ { 's', 0, &cmd_choose_session_entry },
+ { 't', 0, &cmd_clock_mode_entry },
+ { 'w', 0, &cmd_choose_window_entry },
+ { 'x', 0, &cmd_confirm_before_entry },
+ { '{', 0, &cmd_swap_pane_entry },
+ { '}', 0, &cmd_swap_pane_entry },
+ { '\002', 0, &cmd_send_prefix_entry },
+ { KEYC_ADDESC('0'), 0, &cmd_select_layout_entry },
+ { KEYC_ADDESC('1'), 0, &cmd_select_layout_entry },
+ { KEYC_ADDESC('2'), 0, &cmd_select_layout_entry },
+ { KEYC_ADDESC('9'), 0, &cmd_select_layout_entry },
+ { KEYC_ADDCTL(KEYC_DOWN), 1, &cmd_resize_pane_entry },
+ { KEYC_PPAGE, 0, &cmd_scroll_mode_entry },
+ { KEYC_ADDESC('n'), 0, &cmd_next_window_entry },
+ { KEYC_ADDESC('p'), 0, &cmd_previous_window_entry },
+ { KEYC_UP, 1, &cmd_up_pane_entry },
+ { KEYC_DOWN, 1, &cmd_down_pane_entry },
+ { KEYC_ADDESC(KEYC_UP), 1, &cmd_resize_pane_entry },
+ { KEYC_ADDESC(KEYC_DOWN), 1, &cmd_resize_pane_entry },
+ { KEYC_ADDCTL(KEYC_UP), 1, &cmd_resize_pane_entry },
+ { KEYC_ADDCTL(KEYC_DOWN), 1, &cmd_resize_pane_entry },
+ { KEYC_ADDESC('o'), 0, &cmd_rotate_window_entry },
+ { '\017', 0, &cmd_rotate_window_entry },
+ };
+ u_int i;
+ struct cmd *cmd;
+ struct cmd_list *cmdlist;
+
+ SPLAY_INIT(&key_bindings);
+
+ for (i = 0; i < nitems(table); i++) {
+ cmdlist = xmalloc(sizeof *cmdlist);
+ TAILQ_INIT(cmdlist);
+
+ cmd = xmalloc(sizeof *cmd);
+ cmd->entry = table[i].entry;
+ cmd->data = NULL;
+ if (cmd->entry->init != NULL)
+ cmd->entry->init(cmd, table[i].key);
+ TAILQ_INSERT_HEAD(cmdlist, cmd, qentry);
+
+ key_bindings_add(table[i].key, table[i].can_repeat, cmdlist);
+ }
+}
+
+void
+key_bindings_free(void)
+{
+ struct key_binding *bd;
+
+ while (!SPLAY_EMPTY(&key_bindings)) {
+ bd = SPLAY_ROOT(&key_bindings);
+ SPLAY_REMOVE(key_bindings, &key_bindings, bd);
+ cmd_list_free(bd->cmdlist);
+ xfree(bd);
+ }
+}
+
+void printflike2
+key_bindings_error(struct cmd_ctx *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+
+ va_start(ap, fmt);
+ xvasprintf(&msg, fmt, ap);
+ va_end(ap);
+
+ *msg = toupper((u_char) *msg);
+ status_message_set(ctx->curclient, msg);
+ xfree(msg);
+}
+
+void printflike2
+key_bindings_print(struct cmd_ctx *ctx, const char *fmt, ...)
+{
+ struct winlink *wl = ctx->cursession->curw;
+ va_list ap;
+
+ if (wl->window->active->mode != &window_more_mode)
+ window_pane_reset_mode(wl->window->active);
+ window_pane_set_mode(wl->window->active, &window_more_mode);
+
+ va_start(ap, fmt);
+ window_more_vadd(wl->window->active, fmt, ap);
+ va_end(ap);
+}
+
+void printflike2
+key_bindings_info(struct cmd_ctx *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+
+ if (be_quiet)
+ return;
+
+ va_start(ap, fmt);
+ xvasprintf(&msg, fmt, ap);
+ va_end(ap);
+
+ *msg = toupper((u_char) *msg);
+ status_message_set(ctx->curclient, msg);
+ xfree(msg);
+}
+
+void
+key_bindings_dispatch(struct key_binding *bd, struct client *c)
+{
+ struct cmd_ctx ctx;
+
+ ctx.msgdata = NULL;
+ ctx.cursession = c->session;
+ ctx.curclient = c;
+
+ ctx.error = key_bindings_error;
+ ctx.print = key_bindings_print;
+ ctx.info = key_bindings_info;
+
+ ctx.cmdclient = NULL;
+
+ cmd_list_exec(bd->cmdlist, &ctx);
+}
diff --git a/usr.bin/tmux/key-string.c b/usr.bin/tmux/key-string.c
new file mode 100644
index 00000000000..6280a42fe49
--- /dev/null
+++ b/usr.bin/tmux/key-string.c
@@ -0,0 +1,198 @@
+/* $OpenBSD: key-string.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+int key_string_search_table(const char *);
+
+struct {
+ const char *string;
+ int key;
+} key_string_table[] = {
+ /* Function keys. */
+ { "F1", KEYC_F1 },
+ { "F2", KEYC_F2 },
+ { "F3", KEYC_F3 },
+ { "F4", KEYC_F4 },
+ { "F5", KEYC_F5 },
+ { "F6", KEYC_F6 },
+ { "F7", KEYC_F7 },
+ { "F8", KEYC_F8 },
+ { "F9", KEYC_F9 },
+ { "F10", KEYC_F10 },
+ { "F11", KEYC_F11 },
+ { "F12", KEYC_F12 },
+ { "F13", KEYC_F13 },
+ { "F14", KEYC_F14 },
+ { "F15", KEYC_F15 },
+ { "F16", KEYC_F16 },
+ { "F17", KEYC_F17 },
+ { "F18", KEYC_F18 },
+ { "F19", KEYC_F19 },
+ { "F20", KEYC_F20 },
+ { "IC", KEYC_IC },
+ { "DC", KEYC_DC },
+ { "Home", KEYC_HOME },
+ { "End", KEYC_END },
+ { "NPage", KEYC_NPAGE },
+ { "PPage", KEYC_PPAGE },
+ { "Tab", '\011' },
+ { "BTab", KEYC_BTAB },
+
+ /* Arrow keys. */
+ { "Up", KEYC_UP },
+ { "Down", KEYC_DOWN },
+ { "Left", KEYC_LEFT },
+ { "Right", KEYC_RIGHT },
+
+ /* Numeric keypad. */
+ { "KP/", KEYC_KP0_1 },
+ { "KP*", KEYC_KP0_2 },
+ { "KP-", KEYC_KP0_3 },
+ { "KP7", KEYC_KP1_0 },
+ { "KP8", KEYC_KP1_1 },
+ { "KP9", KEYC_KP1_2 },
+ { "KP+", KEYC_KP1_3 },
+ { "KP4", KEYC_KP2_0 },
+ { "KP5", KEYC_KP2_1 },
+ { "KP6", KEYC_KP2_2 },
+ { "KP1", KEYC_KP3_0 },
+ { "KP2", KEYC_KP3_1 },
+ { "KP3", KEYC_KP3_2 },
+ { "KPEnter", KEYC_KP3_3 },
+ { "KP0", KEYC_KP4_0 },
+ { "KP.", KEYC_KP4_2 },
+};
+
+int
+key_string_search_table(const char *string)
+{
+ u_int i;
+
+ for (i = 0; i < nitems(key_string_table); i++) {
+ if (strcasecmp(string, key_string_table[i].string) == 0)
+ return (key_string_table[i].key);
+ }
+ return (KEYC_NONE);
+}
+
+int
+key_string_lookup_string(const char *string)
+{
+ int key;
+ const u_char *ptr;
+
+ if (string[0] == '\0')
+ return (KEYC_NONE);
+ if (string[1] == '\0')
+ return (string[0]);
+
+ ptr = NULL;
+ if (string[0] == 'C' && string[1] == '-')
+ ptr = string + 2;
+ else if (string[0] == '^')
+ ptr = string + 1;
+ if (ptr != NULL) {
+ if (ptr[0] == '\0')
+ return (KEYC_NONE);
+ if (ptr[1] == '\0') {
+ if (ptr[0] == 32)
+ return (0);
+ if (ptr[0] >= 64 && ptr[0] <= 95)
+ return (ptr[0] - 64);
+ if (ptr[0] >= 97 && ptr[0] <= 122)
+ return (ptr[0] - 96);
+ return (KEYC_NONE);
+ }
+ key = key_string_search_table(ptr);
+ if (key != KEYC_NONE)
+ return (KEYC_ADDCTL(key));
+ return (KEYC_NONE);
+ }
+
+ if (string[0] == 'M' && string[1] == '-') {
+ ptr = string + 2;
+ if (ptr[0] == '\0')
+ return (KEYC_NONE);
+ if (ptr[1] == '\0') {
+ if (ptr[0] < 32 || ptr[0] > 127)
+ return (KEYC_NONE);
+ return (KEYC_ADDESC(ptr[0]));
+ }
+ key = key_string_lookup_string(ptr);
+ if (key != KEYC_NONE)
+ return (KEYC_ADDESC(key));
+ return (KEYC_NONE);
+ }
+
+ return (key_string_search_table(string));
+}
+
+const char *
+key_string_lookup_key(int key)
+{
+ static char tmp[24], tmp2[24];
+ const char *s;
+ u_int i;
+
+ if (key == 127)
+ return (NULL);
+
+ if (KEYC_ISESC(key)) {
+ if ((s = key_string_lookup_key(KEYC_REMOVEESC(key))) == NULL)
+ return (NULL);
+ xsnprintf(tmp2, sizeof tmp2, "M-%s", s);
+ return (tmp2);
+ }
+ if (KEYC_ISCTL(key)) {
+ if ((s = key_string_lookup_key(KEYC_REMOVECTL(key))) == NULL)
+ return (NULL);
+ xsnprintf(tmp2, sizeof tmp2, "C-%s", s);
+ return (tmp2);
+ }
+ if (KEYC_ISSFT(key)) {
+ if ((s = key_string_lookup_key(KEYC_REMOVESFT(key))) == NULL)
+ return (NULL);
+ xsnprintf(tmp2, sizeof tmp2, "S-%s", s);
+ return (tmp2);
+ }
+
+ if (key >= 32 && key <= 255) {
+ tmp[0] = key;
+ tmp[1] = '\0';
+ return (tmp);
+ }
+
+ if (key >= 0 && key <= 32) {
+ if (key == 0 || key > 26)
+ xsnprintf(tmp, sizeof tmp, "C-%c", 64 + key);
+ else
+ xsnprintf(tmp, sizeof tmp, "C-%c", 96 + key);
+ return (tmp);
+ }
+
+ for (i = 0; i < nitems(key_string_table); i++) {
+ if (key == key_string_table[i].key)
+ return (key_string_table[i].string);
+ }
+ return (NULL);
+}
diff --git a/usr.bin/tmux/layout-manual.c b/usr.bin/tmux/layout-manual.c
new file mode 100644
index 00000000000..66d472d620b
--- /dev/null
+++ b/usr.bin/tmux/layout-manual.c
@@ -0,0 +1,183 @@
+/* $OpenBSD: layout-manual.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+void layout_manual_v_update_offsets(struct window *);
+
+void
+layout_manual_v_refresh(struct window *w, unused int active_only)
+{
+ struct window_pane *wp;
+ u_int npanes, canfit, total;
+ int left;
+
+ if (active_only)
+ return;
+
+ if (TAILQ_EMPTY(&w->panes))
+ return;
+
+ /* Clear hidden flags. */
+ TAILQ_FOREACH(wp, &w->panes, entry)
+ wp->flags &= ~PANE_HIDDEN;
+
+ /* Check the new size. */
+ npanes = window_count_panes(w);
+ if (w->sy <= PANE_MINIMUM * npanes) {
+ /* How many can we fit? */
+ canfit = w->sy / PANE_MINIMUM;
+ if (canfit == 0) {
+ /* None. Just use this size for the first. */
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp == TAILQ_FIRST(&w->panes))
+ wp->sy = w->sy;
+ else
+ wp->flags |= PANE_HIDDEN;
+ }
+ } else {
+ /* >=1, set minimum for them all. */
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (canfit-- > 0)
+ wp->sy = PANE_MINIMUM - 1;
+ else
+ wp->flags |= PANE_HIDDEN;
+ }
+ /* And increase the first by the rest. */
+ TAILQ_FIRST(&w->panes)->sy += 1 + w->sy % PANE_MINIMUM;
+ }
+ } else {
+ /* In theory they will all fit. Find the current total. */
+ total = 0;
+ TAILQ_FOREACH(wp, &w->panes, entry)
+ total += wp->sy;
+ total += npanes - 1;
+
+ /* Growing or shrinking? */
+ left = w->sy - total;
+ if (left > 0) {
+ /* Growing. Expand evenly. */
+ while (left > 0) {
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ wp->sy++;
+ if (--left == 0)
+ break;
+ }
+ }
+ } else {
+ /* Shrinking. Reduce evenly down to minimum. */
+ while (left < 0) {
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp->sy <= PANE_MINIMUM - 1)
+ continue;
+ wp->sy--;
+ if (++left == 0)
+ break;
+ }
+ }
+ }
+ }
+
+ /* Now do the resize. */
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ wp->sy--;
+ window_pane_resize(wp, w->sx, wp->sy + 1);
+ }
+
+ /* Fill in the offsets. */
+ layout_manual_v_update_offsets(w);
+
+ /* Switch the active window if necessary. */
+ window_set_active_pane(w, w->active);
+}
+
+void
+layout_manual_v_resize(struct window_pane *wp, int adjust)
+{
+ struct window *w = wp->window;
+ struct window_pane *wq;
+
+ if (adjust > 0) {
+ /*
+ * If this is not the last pane, keep trying to increase size
+ * and remove it from the next panes. If it is the last, do
+ * so on the previous pane.
+ */
+ if (TAILQ_NEXT(wp, entry) == NULL) {
+ if (wp == TAILQ_FIRST(&w->panes)) {
+ /* Only one pane. */
+ return;
+ }
+ wp = TAILQ_PREV(wp, window_panes, entry);
+ }
+ while (adjust-- > 0) {
+ wq = wp;
+ while ((wq = TAILQ_NEXT(wq, entry)) != NULL) {
+ if (wq->sy <= PANE_MINIMUM)
+ continue;
+ window_pane_resize(wq, wq->sx, wq->sy - 1);
+ break;
+ }
+ if (wq == NULL)
+ break;
+ window_pane_resize(wp, wp->sx, wp->sy + 1);
+ }
+ } else {
+ adjust = -adjust;
+ /*
+ * If this is not the last pane, keep trying to reduce size
+ * and add to the following pane. If it is the last, do so on
+ * the previous pane.
+ */
+ wq = TAILQ_NEXT(wp, entry);
+ if (wq == NULL) {
+ if (wp == TAILQ_FIRST(&w->panes)) {
+ /* Only one pane. */
+ return;
+ }
+ wq = wp;
+ wp = TAILQ_PREV(wq, window_panes, entry);
+ }
+ while (adjust-- > 0) {
+ if (wp->sy <= PANE_MINIMUM)
+ break;
+ window_pane_resize(wq, wq->sx, wq->sy + 1);
+ window_pane_resize(wp, wp->sx, wp->sy - 1);
+ }
+ }
+
+ layout_manual_v_update_offsets(w);
+}
+
+void
+layout_manual_v_update_offsets(struct window *w)
+{
+ struct window_pane *wp;
+ u_int yoff;
+
+ yoff = 0;
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp->flags & PANE_HIDDEN)
+ continue;
+ wp->xoff = 0;
+ wp->yoff = yoff;
+ yoff += wp->sy + 1;
+ }
+}
diff --git a/usr.bin/tmux/layout.c b/usr.bin/tmux/layout.c
new file mode 100644
index 00000000000..62657a4bab7
--- /dev/null
+++ b/usr.bin/tmux/layout.c
@@ -0,0 +1,373 @@
+/* $OpenBSD: layout.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+/*
+ * Each layout has two functions, _refresh to relayout the panes and _resize to
+ * resize a single pane.
+ *
+ * Second argument (int) to _refresh is 1 if the only change has been that the
+ * active pane has changed. If 0 then panes, active pane or both may have
+ * changed.
+ */
+
+void layout_active_only_refresh(struct window *, int);
+void layout_even_h_refresh(struct window *, int);
+void layout_even_v_refresh(struct window *, int);
+void layout_main_h_refresh(struct window *, int);
+void layout_main_v_refresh(struct window *, int);
+
+const struct {
+ const char *name;
+ void (*refresh)(struct window *, int);
+ void (*resize)(struct window_pane *, int);
+} layouts[] = {
+ { "manual-vertical", layout_manual_v_refresh, layout_manual_v_resize },
+ { "active-only", layout_active_only_refresh, NULL },
+ { "even-horizontal", layout_even_h_refresh, NULL },
+ { "even-vertical", layout_even_v_refresh, NULL },
+ { "main-horizontal", layout_main_h_refresh, NULL },
+ { "main-vertical", layout_main_v_refresh, NULL },
+};
+
+const char *
+layout_name(struct window *w)
+{
+ return (layouts[w->layout].name);
+}
+
+int
+layout_lookup(const char *name)
+{
+ u_int i;
+ int matched = -1;
+
+ for (i = 0; i < nitems(layouts); i++) {
+ if (strncmp(layouts[i].name, name, strlen(name)) == 0) {
+ if (matched != -1) /* ambiguous */
+ return (-1);
+ matched = i;
+ }
+ }
+
+ return (matched);
+}
+
+int
+layout_select(struct window *w, u_int layout)
+{
+ if (layout > nitems(layouts) - 1 || layout == w->layout)
+ return (-1);
+ w->layout = layout;
+
+ layout_refresh(w, 0);
+ return (0);
+}
+
+void
+layout_next(struct window *w)
+{
+ w->layout++;
+ if (w->layout > nitems(layouts) - 1)
+ w->layout = 0;
+ layout_refresh(w, 0);
+}
+
+void
+layout_previous(struct window *w)
+{
+ if (w->layout == 0)
+ w->layout = nitems(layouts) - 1;
+ else
+ w->layout--;
+ layout_refresh(w, 0);
+}
+
+void
+layout_refresh(struct window *w, int active_only)
+{
+ layouts[w->layout].refresh(w, active_only);
+ server_redraw_window(w);
+}
+
+int
+layout_resize(struct window_pane *wp, int adjust)
+{
+ struct window *w = wp->window;
+
+ if (layouts[w->layout].resize == NULL)
+ return (-1);
+ layouts[w->layout].resize(wp, adjust);
+ return (0);
+}
+
+void
+layout_active_only_refresh(struct window *w, unused int active_only)
+{
+ struct window_pane *wp;
+
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp == w->active) {
+ wp->flags &= ~PANE_HIDDEN;
+ wp->xoff = wp->yoff = 0;
+ window_pane_resize(wp, w->sx, w->sy);
+ } else
+ wp->flags |= PANE_HIDDEN;
+ }
+}
+
+void
+layout_even_h_refresh(struct window *w, int active_only)
+{
+ struct window_pane *wp;
+ u_int i, n, width, xoff;
+
+ if (active_only)
+ return;
+
+ /* Get number of panes. */
+ n = window_count_panes(w);
+ if (n == 0)
+ return;
+
+ /* How many can we fit? */
+ if (w->sx / n < PANE_MINIMUM) {
+ width = PANE_MINIMUM;
+ n = w->sx / PANE_MINIMUM;
+ } else
+ width = w->sx / n;
+
+ /* Fit the panes. */
+ i = xoff = 0;
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (i > n) {
+ wp->flags |= PANE_HIDDEN;
+ continue;
+ }
+ wp->flags &= ~PANE_HIDDEN;
+
+ wp->xoff = xoff;
+ wp->yoff = 0;
+ if (i != n - 1)
+ window_pane_resize(wp, width - 1, w->sy);
+ else
+ window_pane_resize(wp, width, w->sy);
+
+ i++;
+ xoff += width;
+ }
+
+ /* Any space left? */
+ while (xoff++ < w->sx) {
+ wp = TAILQ_LAST(&w->panes, window_panes);
+ window_pane_resize(wp, wp->sx + 1, wp->sy);
+ }
+}
+
+void
+layout_even_v_refresh(struct window *w, int active_only)
+{
+ struct window_pane *wp;
+ u_int i, n, height, yoff;
+
+ if (active_only)
+ return;
+
+ /* Get number of panes. */
+ n = window_count_panes(w);
+ if (n == 0)
+ return;
+
+ /* How many can we fit? */
+ if (w->sy / n < PANE_MINIMUM) {
+ height = PANE_MINIMUM;
+ n = w->sy / PANE_MINIMUM;
+ } else
+ height = w->sy / n;
+
+ /* Fit the panes. */
+ i = yoff = 0;
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (i > n) {
+ wp->flags |= PANE_HIDDEN;
+ continue;
+ }
+ wp->flags &= ~PANE_HIDDEN;
+
+ wp->xoff = 0;
+ wp->yoff = yoff;
+ if (i != n - 1)
+ window_pane_resize(wp, w->sx, height - 1);
+ else
+ window_pane_resize(wp, w->sx, height);
+
+ i++;
+ yoff += height;
+ }
+
+ /* Any space left? */
+ while (yoff++ < w->sy) {
+ wp = TAILQ_LAST(&w->panes, window_panes);
+ window_pane_resize(wp, wp->sx, wp->sy + 1);
+ }
+}
+
+void
+layout_main_v_refresh(struct window *w, int active_only)
+{
+ struct window_pane *wp;
+ u_int i, n, mainwidth, height, yoff;
+
+ if (active_only)
+ return;
+
+ /* Get number of panes. */
+ n = window_count_panes(w);
+ if (n == 0)
+ return;
+
+ /* Get the main pane width and add one for separator line. */
+ mainwidth = options_get_number(&w->options, "main-pane-width") + 1;
+
+ /* Need >1 pane and minimum columns; if fewer, display active only. */
+ if (n == 1 || w->sx < mainwidth + PANE_MINIMUM) {
+ layout_active_only_refresh(w, active_only);
+ return;
+ }
+ n--;
+
+ /* How many can we fit, not including first? */
+ if (w->sy / n < PANE_MINIMUM) {
+ height = PANE_MINIMUM;
+ n = w->sy / PANE_MINIMUM;
+ } else
+ height = w->sy / n;
+
+ /* Fit the panes. */
+ i = yoff = 0;
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp == TAILQ_FIRST(&w->panes)) {
+ wp->xoff = 0;
+ wp->yoff = 0;
+ window_pane_resize(wp, mainwidth - 1, w->sy);
+ wp->flags &= ~PANE_HIDDEN;
+ continue;
+ }
+
+ if (i > n) {
+ wp->flags |= PANE_HIDDEN;
+ continue;
+ }
+ wp->flags &= ~PANE_HIDDEN;
+
+ wp->xoff = mainwidth;
+ wp->yoff = yoff;
+ if (i != n - 1)
+ window_pane_resize(wp, w->sx - mainwidth, height - 1);
+ else
+ window_pane_resize(wp, w->sx - mainwidth, height);
+
+ i++;
+ yoff += height;
+ }
+
+ /* Any space left? */
+ while (yoff++ < w->sy) {
+ wp = TAILQ_LAST(&w->panes, window_panes);
+ while (wp != NULL && wp == TAILQ_FIRST(&w->panes))
+ wp = TAILQ_PREV(wp, window_panes, entry);
+ if (wp == NULL)
+ break;
+ window_pane_resize(wp, wp->sx, wp->sy + 1);
+ }
+}
+
+void
+layout_main_h_refresh(struct window *w, int active_only)
+{
+ struct window_pane *wp;
+ u_int i, n, mainheight, width, xoff;
+
+ if (active_only)
+ return;
+
+ /* Get number of panes. */
+ n = window_count_panes(w);
+ if (n == 0)
+ return;
+
+ /* Get the main pane height and add one for separator line. */
+ mainheight = options_get_number(&w->options, "main-pane-height") + 1;
+
+ /* Need >1 pane and minimum rows; if fewer, display active only. */
+ if (n == 1 || w->sy < mainheight + PANE_MINIMUM) {
+ layout_active_only_refresh(w, active_only);
+ return;
+ }
+ n--;
+
+ /* How many can we fit, not including first? */
+ if (w->sx / n < PANE_MINIMUM) {
+ width = PANE_MINIMUM;
+ n = w->sx / PANE_MINIMUM;
+ } else
+ width = w->sx / n;
+
+ /* Fit the panes. */
+ i = xoff = 0;
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp == TAILQ_FIRST(&w->panes)) {
+ wp->xoff = 0;
+ wp->yoff = 0;
+ window_pane_resize(wp, w->sx, mainheight - 1);
+ wp->flags &= ~PANE_HIDDEN;
+ continue;
+ }
+
+ if (i > n) {
+ wp->flags |= PANE_HIDDEN;
+ continue;
+ }
+ wp->flags &= ~PANE_HIDDEN;
+
+ wp->xoff = xoff;
+ wp->yoff = mainheight;
+ if (i != n - 1)
+ window_pane_resize(wp, width - 1, w->sy - mainheight);
+ else
+ window_pane_resize(wp, width - 1, w->sy - mainheight);
+
+ i++;
+ xoff += width;
+ }
+
+ /* Any space left? */
+ while (xoff++ < w->sx + 1) {
+ wp = TAILQ_LAST(&w->panes, window_panes);
+ while (wp != NULL && wp == TAILQ_FIRST(&w->panes))
+ wp = TAILQ_PREV(wp, window_panes, entry);
+ if (wp == NULL)
+ break;
+ window_pane_resize(wp, wp->sx + 1, wp->sy);
+ }
+}
diff --git a/usr.bin/tmux/log.c b/usr.bin/tmux/log.c
new file mode 100644
index 00000000000..c0105e4d53c
--- /dev/null
+++ b/usr.bin/tmux/log.c
@@ -0,0 +1,250 @@
+/* $OpenBSD: log.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+
+#include "tmux.h"
+
+/* Logging type. */
+#define LOG_TYPE_OFF 0
+#define LOG_TYPE_SYSLOG 1
+#define LOG_TYPE_TTY 2
+#define LOG_TYPE_FILE 3
+int log_type = LOG_TYPE_OFF;
+
+/* Log file, if needed. */
+FILE *log_file;
+
+/* Debug level. */
+int log_level;
+
+/* Open logging to syslog. */
+void
+log_open_syslog(int level)
+{
+ log_type = LOG_TYPE_SYSLOG;
+ log_level = level;
+
+ openlog(__progname, LOG_PID|LOG_NDELAY, LOG_FACILITY);
+
+ tzset();
+}
+
+/* Open logging to tty. */
+void
+log_open_tty(int level)
+{
+ log_type = LOG_TYPE_TTY;
+ log_level = level;
+
+ setlinebuf(stderr);
+ setlinebuf(stdout);
+
+ tzset();
+}
+
+/* Open logging to file. */
+void
+log_open_file(int level, const char *path)
+{
+ log_file = fopen(path, "w");
+ if (log_file == NULL)
+ return;
+
+ log_type = LOG_TYPE_FILE;
+ log_level = level;
+
+ setlinebuf(log_file);
+
+ tzset();
+}
+
+/* Close logging. */
+void
+log_close(void)
+{
+ if (log_type == LOG_TYPE_FILE)
+ fclose(log_file);
+
+ log_type = LOG_TYPE_OFF;
+}
+
+/* Write a log message. */
+void
+log_write(int pri, const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ log_vwrite(pri, msg, ap);
+ va_end(ap);
+}
+
+/* Write a log message. */
+void
+log_vwrite(int pri, const char *msg, va_list ap)
+{
+ char *fmt;
+ FILE *f = log_file;
+
+ switch (log_type) {
+ case LOG_TYPE_SYSLOG:
+ vsyslog(pri, msg, ap);
+ break;
+ case LOG_TYPE_TTY:
+ if (pri == LOG_INFO)
+ f = stdout;
+ else
+ f = stderr;
+ /* FALLTHROUGH */
+ case LOG_TYPE_FILE:
+ if (asprintf(&fmt, "%s\n", msg) == -1)
+ exit(1);
+ if (vfprintf(f, fmt, ap) == -1)
+ exit(1);
+ fflush(f);
+ free(fmt);
+ break;
+ }
+}
+
+/* Log a warning with error string. */
+void printflike1
+log_warn(const char *msg, ...)
+{
+ va_list ap;
+ char *fmt;
+
+ va_start(ap, msg);
+ if (asprintf(&fmt, "%s: %s", msg, strerror(errno)) == -1)
+ exit(1);
+ log_vwrite(LOG_CRIT, fmt, ap);
+ free(fmt);
+ va_end(ap);
+}
+
+/* Log a warning. */
+void printflike1
+log_warnx(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ log_vwrite(LOG_CRIT, msg, ap);
+ va_end(ap);
+}
+
+/* Log an informational message. */
+void printflike1
+log_info(const char *msg, ...)
+{
+ va_list ap;
+
+ if (log_level > -1) {
+ va_start(ap, msg);
+ log_vwrite(LOG_INFO, msg, ap);
+ va_end(ap);
+ }
+}
+
+/* Log a debug message. */
+void printflike1
+log_debug(const char *msg, ...)
+{
+ va_list ap;
+
+ if (log_level > 0) {
+ va_start(ap, msg);
+ log_vwrite(LOG_DEBUG, msg, ap);
+ va_end(ap);
+ }
+}
+
+/* Log a debug message at level 2. */
+void printflike1
+log_debug2(const char *msg, ...)
+{
+ va_list ap;
+
+ if (log_level > 1) {
+ va_start(ap, msg);
+ log_vwrite(LOG_DEBUG, msg, ap);
+ va_end(ap);
+ }
+}
+
+/* Log a debug message at level 3. */
+void printflike1
+log_debug3(const char *msg, ...)
+{
+ va_list ap;
+
+ if (log_level > 2) {
+ va_start(ap, msg);
+ log_vwrite(LOG_DEBUG, msg, ap);
+ va_end(ap);
+ }
+}
+
+/* Log a critical error, with error string if necessary, and die. */
+__dead void
+log_vfatal(const char *msg, va_list ap)
+{
+ char *fmt;
+
+ if (errno != 0) {
+ if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1)
+ exit(1);
+ log_vwrite(LOG_CRIT, fmt, ap);
+ } else {
+ if (asprintf(&fmt, "fatal: %s", msg) == -1)
+ exit(1);
+ log_vwrite(LOG_CRIT, fmt, ap);
+ }
+ free(fmt);
+
+ exit(1);
+}
+
+/* Log a critical error, with error string, and die. */
+__dead void printflike1
+log_fatal(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ log_vfatal(msg, ap);
+}
+
+/* Log a critical error and die. */
+__dead void printflike1
+log_fatalx(const char *msg, ...)
+{
+ va_list ap;
+
+ errno = 0;
+ va_start(ap, msg);
+ log_vfatal(msg, ap);
+}
diff --git a/usr.bin/tmux/mode-key.c b/usr.bin/tmux/mode-key.c
new file mode 100644
index 00000000000..e89e52d5572
--- /dev/null
+++ b/usr.bin/tmux/mode-key.c
@@ -0,0 +1,210 @@
+/* $OpenBSD: mode-key.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+enum mode_key_cmd mode_key_lookup_vi(struct mode_key_data *, int);
+enum mode_key_cmd mode_key_lookup_emacs(struct mode_key_data *, int);
+
+void
+mode_key_init(struct mode_key_data *mdata, int type, int flags)
+{
+ mdata->type = type;
+
+ if (flags & MODEKEY_CANEDIT)
+ flags |= MODEKEY_EDITMODE;
+ mdata->flags = flags;
+}
+
+void
+mode_key_free(unused struct mode_key_data *mdata)
+{
+}
+
+enum mode_key_cmd
+mode_key_lookup(struct mode_key_data *mdata, int key)
+{
+ switch (mdata->type) {
+ case MODEKEY_VI:
+ return (mode_key_lookup_vi(mdata, key));
+ case MODEKEY_EMACS:
+ return (mode_key_lookup_emacs(mdata, key));
+ default:
+ fatalx("unknown mode key type");
+ }
+}
+
+enum mode_key_cmd
+mode_key_lookup_vi(struct mode_key_data *mdata, int key)
+{
+ if (KEYC_ISESC(key)) {
+ key = KEYC_REMOVEESC(key);
+ if (mdata->flags & MODEKEY_CANEDIT)
+ mdata->flags ^= MODEKEY_EDITMODE;
+ }
+
+
+ if (mdata->flags & MODEKEY_EDITMODE) {
+ switch (key) {
+ case '\003':
+ return (MODEKEYCMD_QUIT);
+ case '\033':
+ if (mdata->flags & MODEKEY_CANEDIT)
+ mdata->flags &= ~MODEKEY_EDITMODE;
+ return (MODEKEYCMD_NONE);
+ case '\010':
+ case '\177':
+ return (MODEKEYCMD_BACKSPACE);
+ case '\011':
+ return (MODEKEYCMD_COMPLETE);
+ case KEYC_DC:
+ return (MODEKEYCMD_DELETE);
+ case '\r':
+ return (MODEKEYCMD_CHOOSE);
+ }
+ return (MODEKEYCMD_OTHERKEY);
+ }
+
+ switch (key) {
+ case '\010':
+ case '\177':
+ return (MODEKEYCMD_LEFT);
+ case KEYC_DC:
+ return (MODEKEYCMD_DELETE);
+ case '\011':
+ return (MODEKEYCMD_COMPLETE);
+ case 'i':
+ if (mdata->flags & MODEKEY_CANEDIT)
+ mdata->flags |= MODEKEY_EDITMODE;
+ break;
+ case 'a':
+ if (mdata->flags & MODEKEY_CANEDIT) {
+ mdata->flags |= MODEKEY_EDITMODE;
+ return (MODEKEYCMD_RIGHT);
+ }
+ break;
+ case '\r':
+ if (mdata->flags & (MODEKEY_CANEDIT|MODEKEY_CHOOSEMODE))
+ return (MODEKEYCMD_CHOOSE);
+ return (MODEKEYCMD_COPYSELECTION);
+ case '0':
+ case '^':
+ return (MODEKEYCMD_STARTOFLINE);
+ case '\033':
+ return (MODEKEYCMD_CLEARSELECTION);
+ case 'j':
+ case KEYC_DOWN:
+ return (MODEKEYCMD_DOWN);
+ case '$':
+ return (MODEKEYCMD_ENDOFLINE);
+ case 'h':
+ case KEYC_LEFT:
+ return (MODEKEYCMD_LEFT);
+ case '\006':
+ case KEYC_NPAGE:
+ return (MODEKEYCMD_NEXTPAGE);
+ case 'w':
+ return (MODEKEYCMD_NEXTWORD);
+ case '\025':
+ case KEYC_PPAGE:
+ return (MODEKEYCMD_PREVIOUSPAGE);
+ case 'b':
+ return (MODEKEYCMD_PREVIOUSWORD);
+ case 'q':
+ case '\003':
+ return (MODEKEYCMD_QUIT);
+ case 'l':
+ case KEYC_RIGHT:
+ return (MODEKEYCMD_RIGHT);
+ case ' ':
+ return (MODEKEYCMD_STARTSELECTION);
+ case 'k':
+ case KEYC_UP:
+ return (MODEKEYCMD_UP);
+ case 'p':
+ return (MODEKEYCMD_PASTE);
+ }
+
+ return (MODEKEYCMD_NONE);
+}
+
+enum mode_key_cmd
+mode_key_lookup_emacs(struct mode_key_data *mdata, int key)
+{
+ switch (key) {
+ case '\010':
+ case '\177':
+ return (MODEKEYCMD_BACKSPACE);
+ case KEYC_DC:
+ return (MODEKEYCMD_DELETE);
+ case '\011':
+ return (MODEKEYCMD_COMPLETE);
+ case '\r':
+ return (MODEKEYCMD_CHOOSE);
+ case '\001':
+ return (MODEKEYCMD_STARTOFLINE);
+ case '\007':
+ return (MODEKEYCMD_CLEARSELECTION);
+ case '\027':
+ case KEYC_ADDESC('w'):
+ return (MODEKEYCMD_COPYSELECTION);
+ case '\016':
+ case KEYC_DOWN:
+ return (MODEKEYCMD_DOWN);
+ case '\005':
+ return (MODEKEYCMD_ENDOFLINE);
+ case '\002':
+ case KEYC_LEFT:
+ return (MODEKEYCMD_LEFT);
+ case ' ':
+ if (mdata->flags & MODEKEY_CANEDIT)
+ break;
+ /* FALLTHROUGH */
+ case '\026':
+ case KEYC_NPAGE:
+ return (MODEKEYCMD_NEXTPAGE);
+ case KEYC_ADDESC('f'):
+ return (MODEKEYCMD_NEXTWORD);
+ case '\031':
+ return (MODEKEYCMD_PASTE);
+ case KEYC_ADDESC('v'):
+ case KEYC_PPAGE:
+ return (MODEKEYCMD_PREVIOUSPAGE);
+ case KEYC_ADDESC('b'):
+ return (MODEKEYCMD_PREVIOUSWORD);
+ case '\006':
+ case KEYC_RIGHT:
+ return (MODEKEYCMD_RIGHT);
+ case '\000':
+ return (MODEKEYCMD_STARTSELECTION);
+ case '\020':
+ case KEYC_UP:
+ return (MODEKEYCMD_UP);
+ case 'q':
+ if (mdata->flags & MODEKEY_CANEDIT)
+ break;
+ /* FALLTHROUGH */
+ case '\003':
+ case '\033':
+ return (MODEKEYCMD_QUIT);
+ }
+
+ return (MODEKEYCMD_OTHERKEY);
+}
diff --git a/usr.bin/tmux/names.c b/usr.bin/tmux/names.c
new file mode 100644
index 00000000000..0e181629647
--- /dev/null
+++ b/usr.bin/tmux/names.c
@@ -0,0 +1,110 @@
+/* $OpenBSD: names.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <ctype.h>
+#include <libgen.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+char *parse_window_name(const char *);
+
+void
+set_window_names(void)
+{
+ struct window *w;
+ u_int i;
+ char *name, *wname;
+ struct timeval tv, tv2;
+
+ if (gettimeofday(&tv, NULL) != 0)
+ fatal("gettimeofday");
+
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ w = ARRAY_ITEM(&windows, i);
+ if (w == NULL || w->active == NULL)
+ continue;
+ if (!options_get_number(&w->options, "automatic-rename"))
+ continue;
+
+ if (timercmp(&tv, &w->name_timer, <))
+ continue;
+ memcpy(&w->name_timer, &tv, sizeof w->name_timer);
+ tv2.tv_sec = 0;
+ tv2.tv_usec = NAME_INTERVAL * 1000L;
+ timeradd(&w->name_timer, &tv2, &w->name_timer);
+
+ if (w->active->screen != &w->active->base)
+ name = NULL;
+ else
+ name = get_proc_name(w->active->fd, w->active->tty);
+ if (name == NULL)
+ wname = default_window_name(w);
+ else {
+ wname = parse_window_name(name);
+ xfree(name);
+ }
+
+ if (strcmp(wname, w->name) == 0)
+ xfree(wname);
+ else {
+ xfree(w->name);
+ w->name = wname;
+ server_status_window(w);
+ }
+ }
+}
+
+char *
+default_window_name(struct window *w)
+{
+ if (w->active->screen != &w->active->base)
+ return (xstrdup("[tmux]"));
+ return (parse_window_name(w->active->cmd));
+}
+
+char *
+parse_window_name(const char *in)
+{
+ char *copy, *name, *ptr;
+
+ name = copy = xstrdup(in);
+ if (strncmp(name, "exec ", (sizeof "exec ") - 1) == 0)
+ name = name + (sizeof "exec ") - 1;
+
+ while (*name == ' ')
+ name++;
+ if ((ptr = strchr(name, ' ')) != NULL)
+ *ptr = '\0';
+
+ if (*name != '\0') {
+ ptr = name + strlen(name) - 1;
+ while (ptr > name && !isalnum(*ptr))
+ *ptr-- = '\0';
+ }
+
+ if (*name == '/')
+ name = basename(name);
+ name = xstrdup(name);
+ xfree(copy);
+ return (name);
+}
+
diff --git a/usr.bin/tmux/options-cmd.c b/usr.bin/tmux/options-cmd.c
new file mode 100644
index 00000000000..4bc727316ab
--- /dev/null
+++ b/usr.bin/tmux/options-cmd.c
@@ -0,0 +1,182 @@
+/* $OpenBSD: options-cmd.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+void
+set_option_string(struct cmd_ctx *ctx, struct options *oo,
+ const struct set_option_entry *entry, char *value)
+{
+ if (value == NULL) {
+ ctx->error(ctx, "empty value");
+ return;
+ }
+
+ options_set_string(oo, entry->name, "%s", value);
+ ctx->info(ctx, "set option: %s -> %s", entry->name, value);
+}
+
+void
+set_option_number(struct cmd_ctx *ctx, struct options *oo,
+ const struct set_option_entry *entry, char *value)
+{
+ long long number;
+ const char *errstr;
+
+ if (value == NULL) {
+ ctx->error(ctx, "empty value");
+ return;
+ }
+
+ number = strtonum(value, entry->minimum, entry->maximum, &errstr);
+ if (errstr != NULL) {
+ ctx->error(ctx, "value is %s: %s", errstr, value);
+ return;
+ }
+ options_set_number(oo, entry->name, number);
+ ctx->info(ctx, "set option: %s -> %lld", entry->name, number);
+}
+
+void
+set_option_key(struct cmd_ctx *ctx, struct options *oo,
+ const struct set_option_entry *entry, char *value)
+{
+ int key;
+
+ if (value == NULL) {
+ ctx->error(ctx, "empty value");
+ return;
+ }
+
+ if ((key = key_string_lookup_string(value)) == KEYC_NONE) {
+ ctx->error(ctx, "unknown key: %s", value);
+ return;
+ }
+ options_set_number(oo, entry->name, key);
+ ctx->info(ctx,
+ "set option: %s -> %s", entry->name, key_string_lookup_key(key));
+}
+
+void
+set_option_colour(struct cmd_ctx *ctx, struct options *oo,
+ const struct set_option_entry *entry, char *value)
+{
+ int colour;
+
+ if (value == NULL) {
+ ctx->error(ctx, "empty value");
+ return;
+ }
+
+ if ((colour = colour_fromstring(value)) == -1) {
+ ctx->error(ctx, "bad colour: %s", value);
+ return;
+ }
+
+ options_set_number(oo, entry->name, colour);
+ ctx->info(ctx,
+ "set option: %s -> %s", entry->name, colour_tostring(colour));
+}
+
+void
+set_option_attributes(struct cmd_ctx *ctx, struct options *oo,
+ const struct set_option_entry *entry, char *value)
+{
+ int attr;
+
+ if (value == NULL) {
+ ctx->error(ctx, "empty value");
+ return;
+ }
+
+ if ((attr = attributes_fromstring(value)) == -1) {
+ ctx->error(ctx, "bad attributes: %s", value);
+ return;
+ }
+
+ options_set_number(oo, entry->name, attr);
+ ctx->info(ctx,
+ "set option: %s -> %s", entry->name, attributes_tostring(attr));
+}
+
+void
+set_option_flag(struct cmd_ctx *ctx, struct options *oo,
+ const struct set_option_entry *entry, char *value)
+{
+ int flag;
+
+ if (value == NULL || *value == '\0')
+ flag = !options_get_number(oo, entry->name);
+ else {
+ if ((value[0] == '1' && value[1] == '\0') ||
+ strcasecmp(value, "on") == 0 ||
+ strcasecmp(value, "yes") == 0)
+ flag = 1;
+ else if ((value[0] == '0' && value[1] == '\0') ||
+ strcasecmp(value, "off") == 0 ||
+ strcasecmp(value, "no") == 0)
+ flag = 0;
+ else {
+ ctx->error(ctx, "bad value: %s", value);
+ return;
+ }
+ }
+
+ options_set_number(oo, entry->name, flag);
+ ctx->info(ctx,
+ "set option: %s -> %s", entry->name, flag ? "on" : "off");
+}
+
+void
+set_option_choice(struct cmd_ctx *ctx, struct options *oo,
+ const struct set_option_entry *entry, char *value)
+{
+ const char **choicep;
+ int n, choice = -1;
+
+ if (value == NULL) {
+ ctx->error(ctx, "empty value");
+ return;
+ }
+
+ n = 0;
+ for (choicep = entry->choices; *choicep != NULL; choicep++) {
+ n++;
+ if (strncmp(*choicep, value, strlen(value)) != 0)
+ continue;
+
+ if (choice != -1) {
+ ctx->error(ctx, "ambiguous option: %s", value);
+ return;
+ }
+ choice = n - 1;
+ }
+ if (choice == -1) {
+ ctx->error(ctx, "unknown option: %s", value);
+ return;
+ }
+
+ options_set_number(oo, entry->name, choice);
+ ctx->info(ctx,
+ "set option: %s -> %s", entry->name, entry->choices[choice]);
+}
diff --git a/usr.bin/tmux/options.c b/usr.bin/tmux/options.c
new file mode 100644
index 00000000000..11f653481aa
--- /dev/null
+++ b/usr.bin/tmux/options.c
@@ -0,0 +1,160 @@
+/* $OpenBSD: options.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <stdarg.h>
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Option handling; each option has a name, type and value and is stored in
+ * a splay tree.
+ */
+
+SPLAY_GENERATE(options_tree, options_entry, entry, options_cmp);
+
+int
+options_cmp(struct options_entry *o1, struct options_entry *o2)
+{
+ return (strcmp(o1->name, o2->name));
+}
+
+void
+options_init(struct options *oo, struct options *parent)
+{
+ SPLAY_INIT(&oo->tree);
+ oo->parent = parent;
+}
+
+void
+options_free(struct options *oo)
+{
+ struct options_entry *o;
+
+ while (!SPLAY_EMPTY(&oo->tree)) {
+ o = SPLAY_ROOT(&oo->tree);
+ SPLAY_REMOVE(options_tree, &oo->tree, o);
+ xfree(o->name);
+ if (o->type == OPTIONS_STRING)
+ xfree(o->value.string);
+ xfree(o);
+ }
+}
+
+struct options_entry *
+options_find1(struct options *oo, const char *name)
+{
+ struct options_entry p;
+
+ p.name = (char *) name;
+ return (SPLAY_FIND(options_tree, &oo->tree, &p));
+}
+
+struct options_entry *
+options_find(struct options *oo, const char *name)
+{
+ struct options_entry *o, p;
+
+ p.name = (char *) name;
+ o = SPLAY_FIND(options_tree, &oo->tree, &p);
+ while (o == NULL) {
+ oo = oo->parent;
+ if (oo == NULL)
+ break;
+ o = SPLAY_FIND(options_tree, &oo->tree, &p);
+ }
+ return (o);
+}
+
+int
+options_remove(struct options *oo, const char *name)
+{
+ struct options_entry *o;
+
+ if ((o = options_find1(oo, name)) == NULL)
+ return (-1);
+
+ SPLAY_REMOVE(options_tree, &oo->tree, o);
+ xfree(o->name);
+ if (o->type == OPTIONS_STRING)
+ xfree(o->value.string);
+ xfree(o);
+ return (0);
+}
+
+void printflike3
+options_set_string(struct options *oo, const char *name, const char *fmt, ...)
+{
+ struct options_entry *o;
+ va_list ap;
+
+ if ((o = options_find1(oo, name)) == NULL) {
+ o = xmalloc(sizeof *o);
+ o->name = xstrdup(name);
+ SPLAY_INSERT(options_tree, &oo->tree, o);
+ } else if (o->type == OPTIONS_STRING)
+ xfree(o->value.string);
+
+ va_start(ap, fmt);
+ o->type = OPTIONS_STRING;
+ xvasprintf(&o->value.string, fmt, ap);
+ va_end(ap);
+}
+
+char *
+options_get_string(struct options *oo, const char *name)
+{
+ struct options_entry *o;
+
+ if ((o = options_find(oo, name)) == NULL)
+ fatalx("missing option");
+ if (o->type != OPTIONS_STRING)
+ fatalx("option not a string");
+ return (o->value.string);
+}
+
+void
+options_set_number(struct options *oo, const char *name, long long value)
+{
+ struct options_entry *o;
+
+ if ((o = options_find1(oo, name)) == NULL) {
+ o = xmalloc(sizeof *o);
+ o->name = xstrdup(name);
+ SPLAY_INSERT(options_tree, &oo->tree, o);
+ } else if (o->type == OPTIONS_STRING)
+ xfree(o->value.string);
+
+ o->type = OPTIONS_NUMBER;
+ o->value.number = value;
+
+}
+
+long long
+options_get_number(struct options *oo, const char *name)
+{
+ struct options_entry *o;
+
+ if ((o = options_find(oo, name)) == NULL)
+ fatalx("missing option");
+ if (o->type != OPTIONS_NUMBER)
+ fatalx("option not a number");
+ return (o->value.number);
+}
diff --git a/usr.bin/tmux/paste.c b/usr.bin/tmux/paste.c
new file mode 100644
index 00000000000..2f091406e4c
--- /dev/null
+++ b/usr.bin/tmux/paste.c
@@ -0,0 +1,131 @@
+/* $OpenBSD: paste.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/time.h>
+
+#include <string.h>
+
+#include "tmux.h"
+
+void
+paste_init_stack(struct paste_stack *ps)
+{
+ ARRAY_INIT(ps);
+}
+
+void
+paste_free_stack(struct paste_stack *ps)
+{
+ while (paste_free_top(ps) == 0)
+ ;
+}
+
+struct paste_buffer *
+paste_walk_stack(struct paste_stack *ps, uint *idx)
+{
+ struct paste_buffer *pb;
+
+ pb = paste_get_index(ps, *idx);
+ (*idx)++;
+ return (pb);
+}
+
+struct paste_buffer *
+paste_get_top(struct paste_stack *ps)
+{
+ if (ARRAY_LENGTH(ps) == 0)
+ return (NULL);
+ return (ARRAY_FIRST(ps));
+}
+
+struct paste_buffer *
+paste_get_index(struct paste_stack *ps, u_int idx)
+{
+ if (idx >= ARRAY_LENGTH(ps))
+ return (NULL);
+ return (ARRAY_ITEM(ps, idx));
+}
+
+int
+paste_free_top(struct paste_stack *ps)
+{
+ struct paste_buffer *pb;
+
+ if (ARRAY_LENGTH(ps) == 0)
+ return (-1);
+
+ pb = ARRAY_FIRST(ps);
+ ARRAY_REMOVE(ps, 0);
+
+ xfree(pb->data);
+ xfree(pb);
+
+ return (0);
+}
+
+int
+paste_free_index(struct paste_stack *ps, u_int idx)
+{
+ struct paste_buffer *pb;
+
+ if (idx >= ARRAY_LENGTH(ps))
+ return (-1);
+
+ pb = ARRAY_ITEM(ps, idx);
+ ARRAY_REMOVE(ps, idx);
+
+ xfree(pb->data);
+ xfree(pb);
+
+ return (0);
+}
+
+void
+paste_add(struct paste_stack *ps, char *data, u_int limit)
+{
+ struct paste_buffer *pb;
+
+ while (ARRAY_LENGTH(ps) >= limit)
+ ARRAY_TRUNC(ps, 1);
+
+ pb = xmalloc(sizeof *pb);
+ ARRAY_INSERT(ps, 0, pb);
+
+ pb->data = data;
+ if (gettimeofday(&pb->tv, NULL) != 0)
+ fatal("gettimeofday");
+}
+
+int
+paste_replace(struct paste_stack *ps, u_int idx, char *data)
+{
+ struct paste_buffer *pb;
+
+ if (idx >= ARRAY_LENGTH(ps))
+ return (-1);
+
+ pb = ARRAY_ITEM(ps, idx);
+ xfree(pb->data);
+
+ pb->data = data;
+ if (gettimeofday(&pb->tv, NULL) != 0)
+ fatal("gettimeofday");
+
+ return (0);
+}
diff --git a/usr.bin/tmux/procname.c b/usr.bin/tmux/procname.c
new file mode 100644
index 00000000000..0eb398ee75d
--- /dev/null
+++ b/usr.bin/tmux/procname.c
@@ -0,0 +1,130 @@
+/* $OpenBSD: procname.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/param.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+
+#define is_runnable(p) \
+ ((p)->p_stat == SRUN || (p)->p_stat == SIDL || (p)->p_stat == SONPROC)
+#define is_stopped(p) \
+ ((p)->p_stat == SSTOP || (p)->p_stat == SZOMB || (p)->p_stat == SDEAD)
+
+char *get_proc_name(int, char *);
+
+char *
+get_proc_name(int fd, char *tty)
+{
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PGRP, 0 };
+ struct stat sb;
+ size_t len;
+ struct kinfo_proc *buf, *newbuf;
+ struct proc *p, *bestp;
+ u_int i;
+ char *name;
+
+ buf = NULL;
+
+ if (stat(tty, &sb) == -1)
+ return (NULL);
+ if ((mib[3] = tcgetpgrp(fd)) == -1)
+ return (NULL);
+
+retry:
+ if (sysctl(mib, nitems(mib), NULL, &len, NULL, 0) == -1)
+ return (NULL);
+ len = (len * 5) / 4;
+
+ if ((newbuf = realloc(buf, len)) == NULL) {
+ free(buf);
+ return (NULL);
+ }
+ buf = newbuf;
+
+ if (sysctl(mib, nitems(mib), buf, &len, NULL, 0) == -1) {
+ if (errno == ENOMEM)
+ goto retry;
+ free(buf);
+ return (NULL);
+ }
+
+ bestp = NULL;
+ for (i = 0; i < len / sizeof (struct kinfo_proc); i++) {
+ if (buf[i].kp_eproc.e_tdev != sb.st_rdev)
+ continue;
+ p = &buf[i].kp_proc;
+ if (bestp == NULL) {
+ bestp = p;
+ continue;
+ }
+
+ if (is_runnable(p) && !is_runnable(bestp))
+ bestp = p;
+ else if (!is_runnable(p) && is_runnable(bestp))
+ continue;
+
+ if (!is_stopped(p) && is_stopped(bestp))
+ bestp = p;
+ else if (is_stopped(p) && !is_stopped(bestp))
+ continue;
+
+ if (p->p_estcpu > bestp->p_estcpu)
+ bestp = p;
+ else if (p->p_estcpu < bestp->p_estcpu)
+ continue;
+
+ if (p->p_slptime < bestp->p_slptime)
+ bestp = p;
+ else if (p->p_slptime > bestp->p_slptime)
+ continue;
+
+ if (p->p_flag & P_SINTR && !(bestp->p_flag & P_SINTR))
+ bestp = p;
+ else if (!(p->p_flag & P_SINTR) && bestp->p_flag & P_SINTR)
+ continue;
+
+ if (LIST_FIRST(&p->p_children) == NULL &&
+ LIST_FIRST(&bestp->p_children) != NULL) /* XXX ugh */
+ bestp = p;
+ else if (LIST_FIRST(&p->p_children) != NULL &&
+ LIST_FIRST(&bestp->p_children) == NULL)
+ continue;
+
+ if (strcmp(p->p_comm, p->p_comm) < 0)
+ bestp = p;
+ else if (strcmp(p->p_comm, p->p_comm) > 0)
+ continue;
+
+ if (p->p_pid > bestp->p_pid)
+ bestp = p;
+ }
+
+ name = NULL;
+ if (bestp != NULL)
+ name = strdup(bestp->p_comm);
+
+ free(buf);
+ return (name);
+}
diff --git a/usr.bin/tmux/resize.c b/usr.bin/tmux/resize.c
new file mode 100644
index 00000000000..e272f0fab28
--- /dev/null
+++ b/usr.bin/tmux/resize.c
@@ -0,0 +1,138 @@
+/* $OpenBSD: resize.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+/*
+ * Recalculate window and session sizes.
+ *
+ * Every session has the size of the smallest client it is attached to and
+ * every window the size of the smallest session it is attached to.
+ *
+ * So, when a client is resized or a session attached to or detached from a
+ * client, the window sizes must be recalculated. For each session, find the
+ * smallest client it is attached to, and resize it to that size. Then for
+ * every window, find the smallest session it is attached to, resize it to that
+ * size and clear and redraw every client with it as the current window.
+ *
+ * This is quite inefficient - better/additional data structures are needed
+ * to make it better.
+ *
+ * As a side effect, this function updates the SESSION_UNATTACHED flag. This
+ * flag is necessary to make sure unattached sessions do not limit the size of
+ * windows that are attached both to them and to other (attached) sessions.
+ */
+
+void
+recalculate_sizes(void)
+{
+ struct session *s;
+ struct client *c;
+ struct window *w;
+ u_int i, j, ssx, ssy, has, limit;
+ int flag;
+
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ s = ARRAY_ITEM(&sessions, i);
+ if (s == NULL)
+ continue;
+
+ ssx = ssy = UINT_MAX;
+ for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
+ c = ARRAY_ITEM(&clients, j);
+ if (c == NULL)
+ continue;
+ if (c->session == s) {
+ if (c->tty.sx < ssx)
+ ssx = c->tty.sx;
+ if (c->tty.sy < ssy)
+ ssy = c->tty.sy;
+ }
+ }
+ if (ssx == UINT_MAX || ssy == UINT_MAX) {
+ s->flags |= SESSION_UNATTACHED;
+ continue;
+ }
+ s->flags &= ~SESSION_UNATTACHED;
+
+ if (options_get_number(&s->options, "status")) {
+ if (ssy == 0)
+ ssy = 1;
+ else
+ ssy--;
+ }
+ if (s->sx == ssx && s->sy == ssy)
+ continue;
+
+ log_debug(
+ "session size %u,%u (was %u,%u)", ssx, ssy, s->sx, s->sy);
+
+ s->sx = ssx;
+ s->sy = ssy;
+ }
+
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ w = ARRAY_ITEM(&windows, i);
+ if (w == NULL)
+ continue;
+ flag = options_get_number(&w->options, "aggressive-resize");
+
+ ssx = ssy = UINT_MAX;
+ for (j = 0; j < ARRAY_LENGTH(&sessions); j++) {
+ s = ARRAY_ITEM(&sessions, j);
+ if (s == NULL || s->flags & SESSION_UNATTACHED)
+ continue;
+ if (flag)
+ has = s->curw->window == w;
+ else
+ has = session_has(s, w);
+ if (has) {
+ if (s->sx < ssx)
+ ssx = s->sx;
+ if (s->sy < ssy)
+ ssy = s->sy;
+ }
+ }
+ if (ssx == UINT_MAX || ssy == UINT_MAX) {
+ w->flags |= WINDOW_HIDDEN;
+ continue;
+ }
+ w->flags &= ~WINDOW_HIDDEN;
+
+ limit = options_get_number(&w->options, "force-width");
+ if (limit != 0 && ssx > limit)
+ ssx = limit;
+ limit = options_get_number(&w->options, "force-height");
+ if (limit != 0 && ssy > limit)
+ ssy = limit;
+
+ if (w->sx == ssx && w->sy == ssy)
+ continue;
+
+ log_debug(
+ "window size %u,%u (was %u,%u)", ssx, ssy, w->sx, w->sy);
+
+ window_resize(w, ssx, ssy);
+ server_redraw_window(w);
+ layout_refresh(w, 0);
+ }
+}
diff --git a/usr.bin/tmux/screen-redraw.c b/usr.bin/tmux/screen-redraw.c
new file mode 100644
index 00000000000..4f6ee46a3d5
--- /dev/null
+++ b/usr.bin/tmux/screen-redraw.c
@@ -0,0 +1,171 @@
+/* $OpenBSD: screen-redraw.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+int screen_redraw_check_cell(struct client *, u_int, u_int);
+
+/* Check if cell inside a pane. */
+int
+screen_redraw_check_cell(struct client *c, u_int px, u_int py)
+{
+ struct window *w = c->session->curw->window;
+ struct window_pane *wp;
+
+ if (px > w->sx || py > w->sy)
+ return (0);
+
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ /* Inside pane. */
+ if (px >= wp->xoff && px < wp->xoff + wp->sx &&
+ py >= wp->yoff && py < wp->yoff + wp->sy)
+ return (1);
+
+ /* Left/right borders. */
+ if (py >= wp->yoff && py < wp->yoff + wp->sy) {
+ if (wp->xoff != 0 && px == wp->xoff - 1)
+ return (1);
+ if (px == wp->xoff + wp->sx)
+ return (1);
+ }
+
+ /* Top/bottom borders. */
+ if (px >= wp->xoff && px < wp->xoff + wp->sx) {
+ if (wp->yoff != 0 && py == wp->yoff - 1)
+ return (1);
+ if (py == wp->yoff + wp->sy)
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+/* Redraw entire screen.. */
+void
+screen_redraw_screen(struct client *c)
+{
+ struct window *w = c->session->curw->window;
+ struct tty *tty = &c->tty;
+ struct window_pane *wp;
+ struct screen *s;
+ u_int i, j, sx, sy;
+ int status, has_acs;
+ u_char choriz, cvert, cbackg;
+
+ /* Get status line, er, status. */
+ status = options_get_number(&c->session->options, "status");
+
+ /* Work out ACS characters. */
+ if (tty_term_has(tty->term, TTYC_ACSC)) {
+ has_acs = 1;
+ choriz = tty_get_acs(tty, 'q');
+ cvert = tty_get_acs(tty, 'x');
+ cbackg = tty_get_acs(tty, '~');
+ } else {
+ has_acs = 0;
+ choriz = '-';
+ cvert = '|';
+ cbackg = '.';
+ }
+
+ /* Clear the screen. */
+ tty_reset(tty);
+ if (has_acs)
+ tty_putcode(tty, TTYC_SMACS);
+ for (j = 0; j < tty->sy - status; j++) {
+ for (i = 0; i < tty->sx; i++) {
+ if (!screen_redraw_check_cell(c, i, j)) {
+ tty_cursor(tty, i, j, 0, 0);
+ tty_putc(tty, cbackg);
+ }
+ }
+ }
+ if (has_acs)
+ tty_putcode(tty, TTYC_RMACS);
+
+ /* Draw the panes. */
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp->flags & PANE_HIDDEN)
+ continue;
+
+ tty_reset(tty);
+
+ s = wp->screen;
+ sx = wp->sx;
+ sy = wp->sy;
+
+ /* Draw left and right borders. */
+ if (has_acs)
+ tty_putcode(tty, TTYC_SMACS);
+ if (wp->xoff > 0) {
+ for (i = wp->yoff; i < wp->yoff + sy; i++) {
+ tty_cursor(tty, wp->xoff - 1, i, 0, 0);
+ tty_putc(tty, cvert);
+ }
+ }
+ if (wp->xoff + sx < tty->sx) {
+ for (i = wp->yoff; i < wp->yoff + sy; i++) {
+ tty_cursor(tty, wp->xoff + sx, i, 0, 0);
+ tty_putc(&c->tty, cvert);
+ }
+ }
+
+ /* Draw top and bottom borders. */
+ if (wp->yoff > 0) {
+ tty_cursor(tty, wp->xoff, wp->yoff - 1, 0, 0);
+ for (i = 0; i < sx; i++)
+ tty_putc(tty, choriz);
+ }
+ if (wp->yoff + sy < tty->sy - status) {
+ tty_cursor(tty, wp->xoff, wp->yoff + sy, 0, 0);
+ for (i = 0; i < sx; i++)
+ tty_putc(tty, choriz);
+ }
+ if (has_acs)
+ tty_putcode(tty, TTYC_RMACS);
+
+ /* Draw the pane. */
+ screen_redraw_pane(c, wp);
+ }
+
+ /* Draw the status line. */
+ screen_redraw_status(c);
+}
+
+/* Draw a single pane. */
+void
+screen_redraw_pane(struct client *c, struct window_pane *wp)
+{
+ u_int i;
+
+ for (i = 0; i < wp->sy; i++)
+ tty_draw_line(&c->tty, wp->screen, i, wp->xoff, wp->yoff);
+}
+
+
+/* Draw the status line. */
+void
+screen_redraw_status(struct client *c)
+{
+ tty_draw_line(&c->tty, &c->status, 0, 0, c->tty.sy - 1);
+}
diff --git a/usr.bin/tmux/screen-write.c b/usr.bin/tmux/screen-write.c
new file mode 100644
index 00000000000..30a024272b6
--- /dev/null
+++ b/usr.bin/tmux/screen-write.c
@@ -0,0 +1,684 @@
+/* $OpenBSD: screen-write.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+void screen_write_save(struct screen_write_ctx *);
+void screen_write_overwrite(struct screen_write_ctx *);
+
+/* Initialise writing with a window. */
+void
+screen_write_start(
+ struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s)
+{
+ ctx->wp = wp;
+ if (wp != NULL && s == NULL)
+ ctx->s = wp->screen;
+ else
+ ctx->s = s;
+}
+
+/* Finish writing. */
+void
+screen_write_stop(unused struct screen_write_ctx *ctx)
+{
+}
+
+/* Write character. */
+void
+screen_write_putc(
+ struct screen_write_ctx *ctx, struct grid_cell *gc, u_char ch)
+{
+ gc->data = ch;
+ screen_write_cell(ctx, gc, NULL);
+}
+
+/* Write string. */
+void printflike3
+screen_write_puts(
+ struct screen_write_ctx *ctx, struct grid_cell *gc, const char *fmt, ...)
+{
+ va_list ap;
+ char *msg, *ptr;
+
+ va_start(ap, fmt);
+ xvasprintf(&msg, fmt, ap);
+ va_end(ap);
+
+ for (ptr = msg; *ptr != '\0'; ptr++)
+ screen_write_putc(ctx, gc, (u_char) *ptr);
+
+ xfree(msg);
+}
+
+/* Copy from another screen. */
+void
+screen_write_copy(struct screen_write_ctx *ctx,
+ struct screen *src, u_int px, u_int py, u_int nx, u_int ny)
+{
+ struct screen *s = ctx->s;
+ struct grid *gd = src->grid;
+ const struct grid_cell *gc;
+ struct grid_utf8 *gu;
+ u_char *udata;
+ u_int xx, yy, cx, cy;
+
+ cx = s->cx;
+ cy = s->cy;
+ for (yy = py; yy < py + ny; yy++) {
+ for (xx = px; xx < px + nx; xx++) {
+ if (xx >= gd->sx || yy >= gd->hsize + gd->sy)
+ gc = &grid_default_cell;
+ else
+ gc = grid_peek_cell(gd, xx, yy);
+
+ udata = NULL;
+ if (gc->flags & GRID_FLAG_UTF8) {
+ gu = grid_get_utf8(gd, xx, yy);
+ udata = gu->data;
+ }
+
+ screen_write_cell(ctx, gc, udata);
+ }
+ cy++;
+ screen_write_cursormove(ctx, cx, cy);
+ }
+}
+
+/* Save cursor and region positions. */
+void
+screen_write_save(struct screen_write_ctx *ctx)
+{
+ struct screen *s = ctx->s;
+
+ s->old_cx = s->cx;
+ s->old_cy = s->cy;
+
+ s->old_rlower = s->rlower;
+ s->old_rupper = s->rupper;
+}
+
+/* Cursor up by ny. */
+void
+screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
+{
+ struct screen *s = ctx->s;
+
+ if (ny == 0)
+ ny = 1;
+
+ if (ny > s->cy)
+ ny = s->cy;
+ if (ny == 0)
+ return;
+
+ s->cy -= ny;
+}
+
+/* Cursor down by ny. */
+void
+screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
+{
+ struct screen *s = ctx->s;
+
+ if (ny == 0)
+ ny = 1;
+
+ if (ny > screen_size_y(s) - 1 - s->cy)
+ ny = screen_size_y(s) - 1 - s->cy;
+ if (ny == 0)
+ return;
+
+ s->cy += ny;
+}
+
+/* Cursor right by nx. */
+void
+screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
+{
+ struct screen *s = ctx->s;
+
+ if (nx == 0)
+ nx = 1;
+
+ if (nx > screen_size_x(s) - 1 - s->cx)
+ nx = screen_size_x(s) - 1 - s->cx;
+ if (nx == 0)
+ return;
+
+ s->cx += nx;
+}
+
+/* Cursor left by nx. */
+void
+screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx)
+{
+ struct screen *s = ctx->s;
+
+ if (nx == 0)
+ nx = 1;
+
+ if (nx > s->cx)
+ nx = s->cx;
+ if (nx == 0)
+ return;
+
+ s->cx -= nx;
+}
+
+/* Insert nx characters. */
+void
+screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx)
+{
+ struct screen *s = ctx->s;
+
+ if (nx == 0)
+ nx = 1;
+
+ if (nx > screen_size_x(s) - 1 - s->cx)
+ nx = screen_size_x(s) - 1 - s->cx;
+ if (nx == 0)
+ return;
+
+ screen_write_save(ctx);
+
+ if (s->cx <= screen_size_x(s) - 1)
+ grid_view_insert_cells(s->grid, s->cx, s->cy, nx);
+
+ tty_write_cmd(ctx->wp, TTY_INSERTCHARACTER, nx);
+}
+
+/* Delete nx characters. */
+void
+screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx)
+{
+ struct screen *s = ctx->s;
+
+ if (nx == 0)
+ nx = 1;
+
+ if (nx > screen_size_x(s) - 1 - s->cx)
+ nx = screen_size_x(s) - 1 - s->cx;
+ if (nx == 0)
+ return;
+
+ screen_write_save(ctx);
+
+ if (s->cx <= screen_size_x(s) - 1)
+ grid_view_delete_cells(s->grid, s->cx, s->cy, nx);
+
+ tty_write_cmd(ctx->wp, TTY_DELETECHARACTER, nx);
+}
+
+/* Insert ny lines. */
+void
+screen_write_insertline(struct screen_write_ctx *ctx, u_int ny)
+{
+ struct screen *s = ctx->s;
+
+ if (ny == 0)
+ ny = 1;
+
+ if (ny > screen_size_y(s) - 1 - s->cy)
+ ny = screen_size_y(s) - 1 - s->cy;
+ if (ny == 0)
+ return;
+
+ screen_write_save(ctx);
+
+ if (s->cy < s->rupper || s->cy > s->rlower)
+ grid_view_insert_lines(s->grid, s->cy, ny);
+ else {
+ grid_view_insert_lines_region(
+ s->grid, s->rupper, s->rlower, s->cy, ny);
+ }
+
+ tty_write_cmd(ctx->wp, TTY_INSERTLINE, ny);
+}
+
+/* Delete ny lines. */
+void
+screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny)
+{
+ struct screen *s = ctx->s;
+
+ if (ny == 0)
+ ny = 1;
+
+ if (ny > screen_size_y(s) - 1 - s->cy)
+ ny = screen_size_y(s) - 1 - s->cy;
+ if (ny == 0)
+ return;
+
+ screen_write_save(ctx);
+
+ if (s->cy < s->rupper || s->cy > s->rlower)
+ grid_view_delete_lines(s->grid, s->cy, ny);
+ else {
+ grid_view_delete_lines_region(
+ s->grid, s->rupper, s->rlower, s->cy, ny);
+ }
+
+ tty_write_cmd(ctx->wp, TTY_DELETELINE, ny);
+}
+
+/* Clear line at cursor. */
+void
+screen_write_clearline(struct screen_write_ctx *ctx)
+{
+ struct screen *s = ctx->s;
+
+ screen_write_save(ctx);
+
+ grid_view_clear(s->grid, 0, s->cy, screen_size_x(s), 1);
+
+ tty_write_cmd(ctx->wp, TTY_CLEARLINE);
+}
+
+/* Clear to end of line from cursor. */
+void
+screen_write_clearendofline(struct screen_write_ctx *ctx)
+{
+ struct screen *s = ctx->s;
+ u_int sx;
+
+ screen_write_save(ctx);
+
+ sx = screen_size_x(s);
+
+ if (s->cx <= sx - 1)
+ grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1);
+
+ tty_write_cmd(ctx->wp, TTY_CLEARENDOFLINE);
+}
+
+/* Clear to start of line from cursor. */
+void
+screen_write_clearstartofline(struct screen_write_ctx *ctx)
+{
+ struct screen *s = ctx->s;
+ u_int sx;
+
+ screen_write_save(ctx);
+
+ sx = screen_size_x(s);
+
+ if (s->cx > sx - 1)
+ grid_view_clear(s->grid, 0, s->cy, sx, 1);
+ else
+ grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1);
+
+ tty_write_cmd(ctx->wp, TTY_CLEARSTARTOFLINE);
+}
+
+/* Move cursor to px,py. */
+void
+screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py)
+{
+ struct screen *s = ctx->s;
+
+ if (px > screen_size_x(s) - 1)
+ px = screen_size_x(s) - 1;
+ if (py > screen_size_y(s) - 1)
+ py = screen_size_y(s) - 1;
+
+ s->cx = px;
+ s->cy = py;
+}
+
+/* Set cursor mode. */
+void
+screen_write_cursormode(struct screen_write_ctx *ctx, int state)
+{
+ struct screen *s = ctx->s;
+
+ if (state)
+ s->mode |= MODE_CURSOR;
+ else
+ s->mode &= ~MODE_CURSOR;
+}
+
+/* Reverse index (up with scroll). */
+void
+screen_write_reverseindex(struct screen_write_ctx *ctx)
+{
+ struct screen *s = ctx->s;
+
+ screen_write_save(ctx);
+
+ if (s->cy == s->rupper)
+ grid_view_scroll_region_down(s->grid, s->rupper, s->rlower);
+ else if (s->cy > 0)
+ s->cy--;
+
+ tty_write_cmd(ctx->wp, TTY_REVERSEINDEX);
+}
+
+/* Set scroll region. */
+void
+screen_write_scrollregion(
+ struct screen_write_ctx *ctx, u_int rupper, u_int rlower)
+{
+ struct screen *s = ctx->s;
+
+ if (rupper > screen_size_y(s) - 1)
+ rupper = screen_size_y(s) - 1;
+ if (rlower > screen_size_y(s) - 1)
+ rlower = screen_size_y(s) - 1;
+ if (rupper > rlower)
+ return;
+
+ /* Cursor moves to top-left. */
+ s->cx = 0;
+ s->cy = 0;
+
+ s->rupper = rupper;
+ s->rlower = rlower;
+}
+
+/* Set insert mode. */
+void
+screen_write_insertmode(struct screen_write_ctx *ctx, int state)
+{
+ struct screen *s = ctx->s;
+
+ if (state)
+ s->mode |= MODE_INSERT;
+ else
+ s->mode &= ~MODE_INSERT;
+}
+
+/* Set mouse mode. */
+void
+screen_write_mousemode(struct screen_write_ctx *ctx, int state)
+{
+ struct screen *s = ctx->s;
+
+ if (state)
+ s->mode |= MODE_MOUSE;
+ else
+ s->mode &= ~MODE_MOUSE;
+}
+
+/* Line feed (down with scroll). */
+void
+screen_write_linefeed(struct screen_write_ctx *ctx)
+{
+ struct screen *s = ctx->s;
+
+ screen_write_save(ctx);
+
+ if (s->cy == s->rlower)
+ grid_view_scroll_region_up(s->grid, s->rupper, s->rlower);
+ else if (s->cy < screen_size_y(s) - 1)
+ s->cy++;
+
+ tty_write_cmd(ctx->wp, TTY_LINEFEED);
+}
+
+/* Carriage return (cursor to start of line). */
+void
+screen_write_carriagereturn(struct screen_write_ctx *ctx)
+{
+ struct screen *s = ctx->s;
+
+ s->cx = 0;
+}
+
+/* Set keypad cursor keys mode. */
+void
+screen_write_kcursormode(struct screen_write_ctx *ctx, int state)
+{
+ struct screen *s = ctx->s;
+
+ if (state)
+ s->mode |= MODE_KCURSOR;
+ else
+ s->mode &= ~MODE_KCURSOR;
+}
+
+/* Set keypad number keys mode. */
+void
+screen_write_kkeypadmode(struct screen_write_ctx *ctx, int state)
+{
+ struct screen *s = ctx->s;
+
+ if (state)
+ s->mode |= MODE_KKEYPAD;
+ else
+ s->mode &= ~MODE_KKEYPAD;
+}
+
+/* Clear to end of screen from cursor. */
+void
+screen_write_clearendofscreen(struct screen_write_ctx *ctx)
+{
+ struct screen *s = ctx->s;
+ u_int sx, sy;
+
+ screen_write_save(ctx);
+
+ sx = screen_size_x(s);
+ sy = screen_size_y(s);
+
+ if (s->cx <= sx - 1)
+ grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1);
+ grid_view_clear(s->grid, 0, s->cy + 1, sx, sy - (s->cy + 1));
+
+ tty_write_cmd(ctx->wp, TTY_CLEARENDOFSCREEN);
+}
+
+/* Clear to start of screen. */
+void
+screen_write_clearstartofscreen(struct screen_write_ctx *ctx)
+{
+ struct screen *s = ctx->s;
+ u_int sx;
+
+ screen_write_save(ctx);
+
+ sx = screen_size_x(s);
+
+ if (s->cy > 0)
+ grid_view_clear(s->grid, 0, 0, sx, s->cy - 1);
+ if (s->cx > sx - 1)
+ grid_view_clear(s->grid, 0, s->cy, sx, 1);
+ else
+ grid_view_clear(s->grid, 0, s->cy, s->cx, 1);
+
+ tty_write_cmd(ctx->wp, TTY_CLEARSTARTOFSCREEN);
+}
+
+/* Clear entire screen. */
+void
+screen_write_clearscreen(struct screen_write_ctx *ctx)
+{
+ struct screen *s = ctx->s;
+
+ screen_write_save(ctx);
+
+ grid_view_clear(s->grid, 0, 0, screen_size_x(s), screen_size_y(s));
+
+ tty_write_cmd(ctx->wp, TTY_CLEARSCREEN);
+}
+
+/* Write cell data. */
+void
+screen_write_cell(
+ struct screen_write_ctx *ctx, const struct grid_cell *gc, u_char *udata)
+{
+ struct screen *s = ctx->s;
+ struct grid *gd = s->grid;
+ struct grid_utf8 gu, *tmp_gu;
+ u_int width, xx, i;
+ struct grid_cell tmp_gc, *tmp_gc2;
+ size_t size;
+
+ /* Ignore padding. */
+ if (gc->flags & GRID_FLAG_PADDING)
+ return;
+
+ /* Find character width. */
+ if (gc->flags & GRID_FLAG_UTF8) {
+ width = utf8_width(udata);
+
+ gu.width = width;
+ memcpy(&gu.data, udata, sizeof gu.data);
+ } else
+ width = 1;
+
+ /* If the width is zero, combine onto the previous character. */
+ if (width == 0) {
+ if (s->cx == 0)
+ return;
+ tmp_gc2 = grid_view_get_cell(gd, s->cx - 1, s->cy);
+ if (!(tmp_gc2->flags & GRID_FLAG_UTF8)) {
+ tmp_gc2->flags |= GRID_FLAG_UTF8;
+ memset(&gu.data, 0xff, sizeof gu.data);
+ *gu.data = tmp_gc2->data;
+ gu.width = 1;
+ grid_view_set_utf8(gd, s->cx - 1, s->cy, &gu);
+ }
+ tmp_gu = grid_view_get_utf8(gd, s->cx - 1, s->cy);
+
+ for (i = 0; i < UTF8_SIZE; i++) {
+ if (tmp_gu->data[i] == 0xff)
+ break;
+ }
+ memcpy(tmp_gu->data + i, udata, UTF8_SIZE - i);
+
+ /* Assume the previous character has just been input. */
+ for (size = 0; size < UTF8_SIZE; size++) {
+ if (udata[size] == 0xff)
+ break;
+ }
+ tty_write_cmd(ctx->wp, TTY_RAW, udata, size);
+ return;
+ }
+
+ /* If the character is wider than the screen, don't print it. */
+ if (width > screen_size_x(s)) {
+ memcpy(&tmp_gc, gc, sizeof tmp_gc);
+ tmp_gc.data = '_';
+ width = 1;
+ gc = &tmp_gc;
+ }
+
+ /* Check this will fit on the current line; scroll if not. */
+ if (s->cx > screen_size_x(s) - width) {
+ screen_write_carriagereturn(ctx);
+ screen_write_linefeed(ctx);
+ }
+
+ /* Sanity checks. */
+ if (s->cx > screen_size_x(s) - 1 || s->cy > screen_size_y(s) - 1)
+ return;
+
+ /* Handle overwriting of UTF-8 characters. */
+ screen_write_overwrite(ctx);
+
+ /*
+ * If the new character is UTF-8 wide, fill in padding cells. Have
+ * already ensured there is enough room.
+ */
+ for (xx = s->cx + 1; xx < s->cx + width; xx++) {
+ tmp_gc2 = grid_view_get_cell(gd, xx, s->cy);
+ if (tmp_gc2 != NULL)
+ tmp_gc2->flags |= GRID_FLAG_PADDING;
+ }
+
+ /* Set the cell. */
+ grid_view_set_cell(gd, s->cx, s->cy, gc);
+ if (gc->flags & GRID_FLAG_UTF8)
+ grid_view_set_utf8(gd, s->cx, s->cy, &gu);
+
+ /* Move the cursor. */
+ screen_write_save(ctx);
+ s->cx += width;
+
+ /* Draw to the screen if necessary. */
+ if (screen_check_selection(s, s->cx - width, s->cy)) {
+ s->sel.cell.data = gc->data;
+ tty_write_cmd(ctx->wp, TTY_CELL, &s->sel.cell, &gu);
+ } else
+ tty_write_cmd(ctx->wp, TTY_CELL, gc, &gu);
+}
+
+/*
+ * UTF-8 wide characters are a bit of an annoyance. They take up more than one
+ * cell on the screen, so following cells must not be drawn by marking them as
+ * padding.
+ *
+ * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8
+ * character, it is necessary to also overwrite any other cells which covered
+ * by the same character.
+ */
+void
+screen_write_overwrite(struct screen_write_ctx *ctx)
+{
+ struct screen *s = ctx->s;
+ struct grid *gd = s->grid;
+ const struct grid_cell *gc;
+ const struct grid_utf8 *gu;
+ u_int xx;
+
+ gc = grid_view_peek_cell(gd, s->cx, s->cy);
+ gu = grid_view_peek_utf8(gd, s->cx, s->cy);
+
+ if (gc->flags & GRID_FLAG_PADDING) {
+ /*
+ * A padding cell, so clear any following and leading padding
+ * cells back to the character. Don't overwrite the current
+ * cell as that happens later anyway.
+ */
+ xx = s->cx + 1;
+ while (--xx > 0) {
+ gc = grid_view_peek_cell(gd, xx, s->cy);
+ if (!(gc->flags & GRID_FLAG_PADDING))
+ break;
+ grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
+ }
+
+ /* Overwrite the character at the start of this padding. */
+ grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
+
+ /* Overwrite following padding cells. */
+ xx = s->cx;
+ while (++xx < screen_size_x(s)) {
+ gc = grid_view_peek_cell(gd, xx, s->cy);
+ if (!(gc->flags & GRID_FLAG_PADDING))
+ break;
+ grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
+ }
+ } else if (gc->flags & GRID_FLAG_UTF8 && gu->width > 1) {
+ /*
+ * An UTF-8 wide cell; overwrite following padding cells only.
+ */
+ xx = s->cx;
+ while (++xx < screen_size_x(s)) {
+ gc = grid_view_peek_cell(gd, xx, s->cy);
+ if (!(gc->flags & GRID_FLAG_PADDING))
+ break;
+ grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
+ }
+ }
+}
diff --git a/usr.bin/tmux/screen.c b/usr.bin/tmux/screen.c
new file mode 100644
index 00000000000..028653b56c4
--- /dev/null
+++ b/usr.bin/tmux/screen.c
@@ -0,0 +1,240 @@
+/* $OpenBSD: screen.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+void screen_resize_x(struct screen *, u_int);
+void screen_resize_y(struct screen *, u_int);
+
+/* Create a new screen. */
+void
+screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
+{
+ s->grid = grid_create(sx, sy, hlimit);
+
+ s->title = xstrdup("");
+
+ screen_reinit(s);
+}
+
+/* Reinitialise screen. */
+void
+screen_reinit(struct screen *s)
+{
+ s->cx = 0;
+ s->cy = 0;
+
+ s->rupper = 0;
+ s->rlower = screen_size_y(s) - 1;
+
+ s->mode = MODE_CURSOR;
+
+ grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy - 1);
+
+ screen_clear_selection(s);
+}
+
+/* Destroy a screen. */
+void
+screen_free(struct screen *s)
+{
+ xfree(s->title);
+ grid_destroy(s->grid);
+}
+
+/* Set screen title. */
+void
+screen_set_title(struct screen *s, const char *title)
+{
+ xfree(s->title);
+ s->title = xstrdup(title);
+}
+
+/* Resize screen. */
+void
+screen_resize(struct screen *s, u_int sx, u_int sy)
+{
+ if (sx < 1)
+ sx = 1;
+ if (sy < 1)
+ sy = 1;
+
+ if (sx != screen_size_x(s))
+ screen_resize_x(s, sx);
+ if (sy != screen_size_y(s))
+ screen_resize_y(s, sy);
+}
+
+void
+screen_resize_x(struct screen *s, u_int sx)
+{
+ struct grid *gd = s->grid;
+ const struct grid_cell *gc;
+ const struct grid_utf8 *gu;
+ u_int xx, yy;
+
+ if (sx == 0)
+ fatalx("zero size");
+
+ /* If getting larger, not much to do. */
+ if (sx > screen_size_x(s)) {
+ gd->sx = sx;
+ return;
+ }
+
+ /* If getting smaller, nuke any data in lines over the new size. */
+ for (yy = gd->hsize; yy < gd->hsize + screen_size_y(s); yy++) {
+ /*
+ * If the character after the last is wide or padding, remove
+ * it and any leading padding.
+ */
+ gc = &grid_default_cell;
+ for (xx = sx; xx > 0; xx--) {
+ gc = grid_peek_cell(gd, xx - 1, yy);
+ if (!(gc->flags & GRID_FLAG_PADDING))
+ break;
+ grid_set_cell(gd, xx - 1, yy, &grid_default_cell);
+ }
+ if (xx > 0 && xx != sx && gc->flags & GRID_FLAG_UTF8) {
+ gu = grid_peek_utf8(gd, xx - 1, yy);
+ if (gu->width > 1) {
+ grid_set_cell(
+ gd, xx - 1, yy, &grid_default_cell);
+ }
+ }
+
+ /* Reduce the line size. */
+ grid_reduce_line(gd, yy, sx);
+ }
+
+ if (s->cx >= sx)
+ s->cx = sx - 1;
+ gd->sx = sx;
+}
+
+void
+screen_resize_y(struct screen *s, u_int sy)
+{
+ struct grid *gd = s->grid;
+ u_int oy, yy, ny;
+
+ if (sy == 0)
+ fatalx("zero size");
+
+ /* Size decreasing. */
+ if (sy < screen_size_y(s)) {
+ oy = screen_size_y(s);
+
+ if (s->cy != 0) {
+ /*
+ * The cursor is not at the start. Try to remove as
+ * many lines as possible from the top. (Up to the
+ * cursor line.)
+ */
+ ny = s->cy;
+ if (ny > oy - sy)
+ ny = oy - sy;
+
+ grid_view_delete_lines(gd, 0, ny);
+
+ s->cy -= ny;
+ oy -= ny;
+ }
+
+ if (sy < oy) {
+ /* Remove any remaining lines from the bottom. */
+ grid_view_delete_lines(gd, sy, oy - sy);
+ if (s->cy >= sy)
+ s->cy = sy - 1;
+ }
+ }
+
+ /* Resize line arrays. */
+ gd->size = xrealloc(gd->size, gd->hsize + sy, sizeof *gd->size);
+ gd->data = xrealloc(gd->data, gd->hsize + sy, sizeof *gd->data);
+ gd->usize = xrealloc(gd->usize, gd->hsize + sy, sizeof *gd->usize);
+ gd->udata = xrealloc(gd->udata, gd->hsize + sy, sizeof *gd->udata);
+
+ /* Size increasing. */
+ if (sy > screen_size_y(s)) {
+ oy = screen_size_y(s);
+ for (yy = gd->hsize + oy; yy < gd->hsize + sy; yy++) {
+ gd->size[yy] = 0;
+ gd->data[yy] = NULL;
+ gd->usize[yy] = 0;
+ gd->udata[yy] = NULL;
+ }
+ }
+
+ gd->sy = sy;
+
+ s->rupper = 0;
+ s->rlower = screen_size_y(s) - 1;
+}
+
+/* Set selection. */
+void
+screen_set_selection(struct screen *s,
+ u_int sx, u_int sy, u_int ex, u_int ey, struct grid_cell *gc)
+{
+ struct screen_sel *sel = &s->sel;
+
+ memcpy(&sel->cell, gc, sizeof sel->cell);
+
+ sel->flag = 1;
+ if (ey < sy || (sy == ey && ex < sx)) {
+ sel->sx = ex; sel->sy = ey;
+ sel->ex = sx; sel->ey = sy;
+ } else {
+ sel->sx = sx; sel->sy = sy;
+ sel->ex = ex; sel->ey = ey;
+ }
+}
+
+/* Clear selection. */
+void
+screen_clear_selection(struct screen *s)
+{
+ struct screen_sel *sel = &s->sel;
+
+ sel->flag = 0;
+}
+
+/* Check if cell in selection. */
+int
+screen_check_selection(struct screen *s, u_int px, u_int py)
+{
+ struct screen_sel *sel = &s->sel;
+
+ if (!sel->flag || py < sel->sy || py > sel->ey)
+ return (0);
+
+ if (py == sel->sy && py == sel->ey) {
+ if (px < sel->sx || px > sel->ex)
+ return (0);
+ return (1);
+ }
+
+ if ((py == sel->sy && px < sel->sx) || (py == sel->ey && px > sel->ex))
+ return (0);
+ return (1);
+}
diff --git a/usr.bin/tmux/server-fn.c b/usr.bin/tmux/server-fn.c
new file mode 100644
index 00000000000..2841930bf13
--- /dev/null
+++ b/usr.bin/tmux/server-fn.c
@@ -0,0 +1,242 @@
+/* $OpenBSD: server-fn.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/time.h>
+
+#include <string.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+int server_lock_callback(void *, const char *);
+
+const char **
+server_fill_environ(struct session *s)
+{
+ static const char *env[] = { NULL /* TMUX= */, "TERM=screen", NULL };
+ static char tmuxvar[MAXPATHLEN + 256];
+ u_int idx;
+
+ if (session_index(s, &idx) != 0)
+ fatalx("session not found");
+
+ xsnprintf(tmuxvar, sizeof tmuxvar,
+ "TMUX=%s,%ld,%u", socket_path, (long) getpid(), idx);
+ env[0] = tmuxvar;
+
+ return (env);
+}
+
+void
+server_write_client(
+ struct client *c, enum hdrtype type, const void *buf, size_t len)
+{
+ struct hdr hdr;
+
+ log_debug("writing %d to client %d", type, c->fd);
+
+ hdr.type = type;
+ hdr.size = len;
+
+ buffer_write(c->out, &hdr, sizeof hdr);
+ if (buf != NULL && len > 0)
+ buffer_write(c->out, buf, len);
+}
+
+void
+server_write_session(
+ struct session *s, enum hdrtype type, const void *buf, size_t len)
+{
+ struct client *c;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session == NULL)
+ continue;
+ if (c->session == s)
+ server_write_client(c, type, buf, len);
+ }
+}
+
+void
+server_write_window(
+ struct window *w, enum hdrtype type, const void *buf, size_t len)
+{
+ struct client *c;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session == NULL)
+ continue;
+ if (c->session->curw->window == w)
+ server_write_client(c, type, buf, len);
+ }
+}
+
+void
+server_redraw_client(struct client *c)
+{
+ c->flags |= CLIENT_REDRAW;
+}
+
+void
+server_status_client(struct client *c)
+{
+ c->flags |= CLIENT_STATUS;
+}
+
+void
+server_redraw_session(struct session *s)
+{
+ struct client *c;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session == NULL)
+ continue;
+ if (c->session == s)
+ server_redraw_client(c);
+ }
+}
+
+void
+server_status_session(struct session *s)
+{
+ struct client *c;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session == NULL)
+ continue;
+ if (c->session == s)
+ server_status_client(c);
+ }
+}
+
+void
+server_redraw_window(struct window *w)
+{
+ struct client *c;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session == NULL)
+ continue;
+ if (c->session->curw->window == w)
+ server_redraw_client(c);
+ }
+ w->flags |= WINDOW_REDRAW;
+}
+
+void
+server_status_window(struct window *w)
+{
+ struct session *s;
+ u_int i;
+
+ /*
+ * This is slightly different. We want to redraw the status line of any
+ * clients containing this window rather than any where it is the
+ * current window.
+ */
+
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ s = ARRAY_ITEM(&sessions, i);
+ if (s != NULL && session_has(s, w))
+ server_status_session(s);
+ }
+}
+
+void
+server_lock(void)
+{
+ struct client *c;
+ u_int i;
+
+ if (server_locked)
+ return;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session == NULL)
+ continue;
+
+ status_prompt_clear(c);
+ status_prompt_set(
+ c, "Password: ", server_lock_callback, c, PROMPT_HIDDEN);
+ server_redraw_client(c);
+ }
+ server_locked = 1;
+}
+
+int
+server_lock_callback(unused void *data, const char *s)
+{
+ return (server_unlock(s));
+}
+
+int
+server_unlock(const char *s)
+{
+ struct client *c;
+ u_int i;
+ char *out;
+
+ if (!server_locked)
+ return (0);
+ server_activity = time(NULL);
+
+ if (server_password != NULL) {
+ if (s == NULL)
+ return (-1);
+ out = crypt(s, server_password);
+ if (strcmp(out, server_password) != 0)
+ goto wrong;
+ }
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL)
+ continue;
+
+ status_prompt_clear(c);
+ server_redraw_client(c);
+ }
+
+ server_locked = 0;
+ return (0);
+
+wrong:
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL)
+ continue;
+
+ *c->prompt_buffer = '\0';
+ c->prompt_index = 0;
+ server_status_client(c);
+ }
+
+ return (-1);
+}
diff --git a/usr.bin/tmux/server-msg.c b/usr.bin/tmux/server-msg.c
new file mode 100644
index 00000000000..05f7e8567d9
--- /dev/null
+++ b/usr.bin/tmux/server-msg.c
@@ -0,0 +1,304 @@
+/* $OpenBSD: server-msg.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+int server_msg_fn_command(struct hdr *, struct client *);
+int server_msg_fn_identify(struct hdr *, struct client *);
+int server_msg_fn_resize(struct hdr *, struct client *);
+int server_msg_fn_exiting(struct hdr *, struct client *);
+int server_msg_fn_unlock(struct hdr *, struct client *);
+int server_msg_fn_wakeup(struct hdr *, struct client *);
+
+void printflike2 server_msg_fn_command_error(
+ struct cmd_ctx *, const char *, ...);
+void printflike2 server_msg_fn_command_print(
+ struct cmd_ctx *, const char *, ...);
+void printflike2 server_msg_fn_command_info(
+ struct cmd_ctx *, const char *, ...);
+
+struct server_msg {
+ enum hdrtype type;
+ int (*fn)(struct hdr *, struct client *);
+};
+const struct server_msg server_msg_table[] = {
+ { MSG_IDENTIFY, server_msg_fn_identify },
+ { MSG_COMMAND, server_msg_fn_command },
+ { MSG_RESIZE, server_msg_fn_resize },
+ { MSG_EXITING, server_msg_fn_exiting },
+ { MSG_UNLOCK, server_msg_fn_unlock },
+ { MSG_WAKEUP, server_msg_fn_wakeup },
+};
+
+int
+server_msg_dispatch(struct client *c)
+{
+ struct hdr hdr;
+ const struct server_msg *msg;
+ u_int i;
+ int n;
+
+ for (;;) {
+ if (BUFFER_USED(c->in) < sizeof hdr)
+ return (0);
+ memcpy(&hdr, BUFFER_OUT(c->in), sizeof hdr);
+ if (BUFFER_USED(c->in) < (sizeof hdr) + hdr.size)
+ return (0);
+ buffer_remove(c->in, sizeof hdr);
+
+ for (i = 0; i < nitems(server_msg_table); i++) {
+ msg = server_msg_table + i;
+ if (msg->type == hdr.type) {
+ if ((n = msg->fn(&hdr, c)) != 0)
+ return (n);
+ break;
+ }
+ }
+ if (i == nitems(server_msg_table))
+ fatalx("unexpected message");
+ }
+}
+
+void printflike2
+server_msg_fn_command_error(struct cmd_ctx *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+
+ va_start(ap, fmt);
+ xvasprintf(&msg, fmt, ap);
+ va_end(ap);
+
+ server_write_client(ctx->cmdclient, MSG_ERROR, msg, strlen(msg));
+ xfree(msg);
+}
+
+void printflike2
+server_msg_fn_command_print(struct cmd_ctx *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+
+ va_start(ap, fmt);
+ xvasprintf(&msg, fmt, ap);
+ va_end(ap);
+
+ server_write_client(ctx->cmdclient, MSG_PRINT, msg, strlen(msg));
+ xfree(msg);
+}
+
+void printflike2
+server_msg_fn_command_info(struct cmd_ctx *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+
+ if (be_quiet)
+ return;
+
+ va_start(ap, fmt);
+ xvasprintf(&msg, fmt, ap);
+ va_end(ap);
+
+ server_write_client(ctx->cmdclient, MSG_PRINT, msg, strlen(msg));
+ xfree(msg);
+}
+
+int
+server_msg_fn_command(struct hdr *hdr, struct client *c)
+{
+ struct msg_command_data data;
+ struct cmd_ctx ctx;
+ struct cmd_list *cmdlist;
+ struct cmd *cmd;
+
+ if (hdr->size < sizeof data)
+ fatalx("bad MSG_COMMAND size");
+ buffer_read(c->in, &data, sizeof data);
+
+ cmdlist = cmd_list_recv(c->in);
+ server_activity = time(NULL);
+
+ ctx.error = server_msg_fn_command_error;
+ ctx.print = server_msg_fn_command_print;
+ ctx.info = server_msg_fn_command_info;
+
+ ctx.msgdata = &data;
+ ctx.curclient = NULL;
+ ctx.cursession = NULL;
+
+ ctx.cmdclient = c;
+
+ if (data.pid != -1) {
+ TAILQ_FOREACH(cmd, cmdlist, qentry) {
+ if (cmd->entry->flags & CMD_CANTNEST) {
+ server_msg_fn_command_error(&ctx,
+ "sessions should be nested with care. "
+ "unset $TMUX to force");
+ cmd_list_free(cmdlist);
+ server_write_client(c, MSG_EXIT, NULL, 0);
+ return (0);
+ }
+ }
+ }
+
+ if (cmd_list_exec(cmdlist, &ctx) != 1)
+ server_write_client(c, MSG_EXIT, NULL, 0);
+ cmd_list_free(cmdlist);
+ return (0);
+}
+
+int
+server_msg_fn_identify(struct hdr *hdr, struct client *c)
+{
+ struct msg_identify_data data;
+ char *term;
+
+ if (hdr->size < sizeof data)
+ fatalx("bad MSG_IDENTIFY size");
+ buffer_read(c->in, &data, sizeof data);
+ term = cmd_recv_string(c->in);
+
+ log_debug("identify msg from client: %u,%u (%d)",
+ data.sx, data.sy, data.version);
+
+ if (data.version != PROTOCOL_VERSION) {
+#define MSG "protocol version mismatch"
+ server_write_client(c, MSG_ERROR, MSG, (sizeof MSG) - 1);
+#undef MSG
+ return (0);
+ }
+
+ c->tty.sx = data.sx;
+ c->tty.sy = data.sy;
+
+ c->cwd = NULL;
+ if (*data.cwd != '\0')
+ c->cwd = xstrdup(data.cwd);
+
+ data.tty[(sizeof data.tty) - 1] = '\0';
+ tty_init(&c->tty, data.tty, term);
+ if (data.flags & IDENTIFY_UTF8)
+ c->tty.flags |= TTY_UTF8;
+ if (data.flags & IDENTIFY_256COLOURS)
+ c->tty.term_flags |= TERM_256COLOURS;
+ else if (data.flags & IDENTIFY_88COLOURS)
+ c->tty.term_flags |= TERM_88COLOURS;
+ if (data.flags & IDENTIFY_HASDEFAULTS)
+ c->tty.term_flags |= TERM_HASDEFAULTS;
+ xfree(term);
+
+ c->flags |= CLIENT_TERMINAL;
+
+ return (0);
+}
+
+int
+server_msg_fn_resize(struct hdr *hdr, struct client *c)
+{
+ struct msg_resize_data data;
+
+ if (hdr->size != sizeof data)
+ fatalx("bad MSG_RESIZE size");
+ buffer_read(c->in, &data, sizeof data);
+
+ log_debug("resize msg from client: %u,%u", data.sx, data.sy);
+
+ c->tty.sx = data.sx;
+ if (c->tty.sx == 0)
+ c->tty.sx = 80;
+ c->tty.sy = data.sy;
+ if (c->tty.sy == 0)
+ c->tty.sy = 25;
+
+ c->tty.cx = UINT_MAX;
+ c->tty.cy = UINT_MAX;
+ c->tty.rupper = UINT_MAX;
+ c->tty.rlower = UINT_MAX;
+
+ recalculate_sizes();
+
+ /* Always redraw this client. */
+ server_redraw_client(c);
+
+ return (0);
+}
+
+int
+server_msg_fn_exiting(struct hdr *hdr, struct client *c)
+{
+ if (hdr->size != 0)
+ fatalx("bad MSG_EXITING size");
+
+ log_debug("exiting msg from client");
+
+ c->session = NULL;
+
+ tty_close(&c->tty, c->flags & CLIENT_SUSPENDED);
+
+ server_write_client(c, MSG_EXITED, NULL, 0);
+
+ return (0);
+}
+
+int
+server_msg_fn_unlock(struct hdr *hdr, struct client *c)
+{
+ char *pass;
+
+ if (hdr->size == 0)
+ fatalx("bad MSG_UNLOCK size");
+ pass = cmd_recv_string(c->in);
+
+ log_debug("unlock msg from client");
+
+ if (server_unlock(pass) != 0) {
+#define MSG "bad password"
+ server_write_client(c, MSG_ERROR, MSG, (sizeof MSG) - 1);
+ server_write_client(c, MSG_EXIT, NULL, 0);
+ return (0);
+#undef MSG
+ }
+
+ server_write_client(c, MSG_EXIT, NULL, 0);
+
+ return (0);
+}
+
+int
+server_msg_fn_wakeup(struct hdr *hdr, struct client *c)
+{
+ if (hdr->size != 0)
+ fatalx("bad MSG_WAKEUP size");
+
+ log_debug("wakeup msg from client");
+
+ c->flags &= ~CLIENT_SUSPENDED;
+ tty_start_tty(&c->tty);
+ server_redraw_client(c);
+
+ return (0);
+}
diff --git a/usr.bin/tmux/server.c b/usr.bin/tmux/server.c
new file mode 100644
index 00000000000..642f38c842c
--- /dev/null
+++ b/usr.bin/tmux/server.c
@@ -0,0 +1,1105 @@
+/* $OpenBSD: server.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Main server functions.
+ */
+
+/* Client list. */
+struct clients clients;
+
+int server_create_socket(void);
+int server_main(int);
+void server_shutdown(void);
+void server_child_signal(void);
+void server_fill_windows(struct pollfd **);
+void server_handle_windows(struct pollfd **);
+void server_fill_clients(struct pollfd **);
+void server_handle_clients(struct pollfd **);
+struct client *server_accept_client(int);
+void server_handle_client(struct client *);
+void server_handle_window(struct window *, struct window_pane *);
+int server_check_window_bell(struct session *, struct window *,
+ struct window_pane *);
+int server_check_window_activity(struct session *,
+ struct window *);
+int server_check_window_content(struct session *, struct window *,
+ struct window_pane *);
+void server_lost_client(struct client *);
+void server_check_window(struct window *);
+void server_check_redraw(struct client *);
+void server_redraw_locked(struct client *);
+void server_check_timers(struct client *);
+void server_second_timers(void);
+int server_update_socket(void);
+
+/* Create a new client. */
+struct client *
+server_create_client(int fd)
+{
+ struct client *c;
+ int mode;
+ u_int i;
+
+ if ((mode = fcntl(fd, F_GETFL)) == -1)
+ fatal("fcntl failed");
+ if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
+ fatal("fcntl failed");
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
+ fatal("fcntl failed");
+
+ c = xcalloc(1, sizeof *c);
+ c->fd = fd;
+ c->in = buffer_create(BUFSIZ);
+ c->out = buffer_create(BUFSIZ);
+
+ ARRAY_INIT(&c->prompt_hdata);
+
+ c->tty.fd = -1;
+ c->title = NULL;
+
+ c->session = NULL;
+ c->tty.sx = 80;
+ c->tty.sy = 25;
+ screen_init(&c->status, c->tty.sx, 1, 0);
+
+ c->message_string = NULL;
+
+ c->prompt_string = NULL;
+ c->prompt_buffer = NULL;
+ c->prompt_index = 0;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ if (ARRAY_ITEM(&clients, i) == NULL) {
+ ARRAY_SET(&clients, i, c);
+ return (c);
+ }
+ }
+ ARRAY_ADD(&clients, c);
+ return (c);
+}
+
+/* Find client index. */
+int
+server_client_index(struct client *c)
+{
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ if (c == ARRAY_ITEM(&clients, i))
+ return (i);
+ }
+ return (-1);
+}
+
+/* Fork new server. */
+int
+server_start(char *path)
+{
+ int pair[2], srv_fd;
+ char *cause;
+ char rpathbuf[MAXPATHLEN];
+
+ /* The first client is special and gets a socketpair; create it. */
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
+ fatal("socketpair failed");
+
+ switch (fork()) {
+ case -1:
+ fatal("fork failed");
+ case 0:
+ break;
+ default:
+ close(pair[1]);
+ return (pair[0]);
+ }
+ close(pair[0]);
+
+ /*
+ * Must daemonise before loading configuration as the PID changes so
+ * $TMUX would be wrong for sessions created in the config file.
+ */
+ if (daemon(1, 1) != 0)
+ fatal("daemon failed");
+
+ ARRAY_INIT(&windows);
+ ARRAY_INIT(&clients);
+ ARRAY_INIT(&sessions);
+ key_bindings_init();
+ utf8_build();
+
+ server_locked = 0;
+ server_password = NULL;
+ server_activity = time(NULL);
+
+ start_time = time(NULL);
+ socket_path = path;
+
+ if (cfg_file != NULL && load_cfg(cfg_file, &cause) != 0) {
+ log_warnx("%s", cause);
+ exit(1);
+ }
+ logfile("server");
+
+ log_debug("server started, pid %ld", (long) getpid());
+ log_debug("socket path %s", socket_path);
+
+ if (realpath(socket_path, rpathbuf) == NULL)
+ strlcpy(rpathbuf, socket_path, sizeof rpathbuf);
+ setproctitle("server (%s)", rpathbuf);
+
+ srv_fd = server_create_socket();
+ server_create_client(pair[1]);
+
+ exit(server_main(srv_fd));
+}
+
+/* Create server socket. */
+int
+server_create_socket(void)
+{
+ struct sockaddr_un sa;
+ size_t size;
+ mode_t mask;
+ int fd, mode;
+
+ memset(&sa, 0, sizeof sa);
+ sa.sun_family = AF_UNIX;
+ size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
+ if (size >= sizeof sa.sun_path) {
+ errno = ENAMETOOLONG;
+ fatal("socket failed");
+ }
+ unlink(sa.sun_path);
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ fatal("socket failed");
+
+ mask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
+ if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1)
+ fatal("bind failed");
+ umask(mask);
+
+ if (listen(fd, 16) == -1)
+ fatal("listen failed");
+
+ if ((mode = fcntl(fd, F_GETFL)) == -1)
+ fatal("fcntl failed");
+ if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
+ fatal("fcntl failed");
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
+ fatal("fcntl failed");
+
+ return (fd);
+}
+
+/* Main server loop. */
+int
+server_main(int srv_fd)
+{
+ struct window *w;
+ struct pollfd *pfds, *pfd;
+ int nfds, xtimeout;
+ u_int i, n;
+ time_t now, last;
+
+ siginit();
+
+ last = time(NULL);
+
+ pfds = NULL;
+ for (;;) {
+ /* If sigterm, kill all windows and clients. */
+ if (sigterm)
+ server_shutdown();
+
+ /* Handle child exit. */
+ if (sigchld) {
+ server_child_signal();
+ sigchld = 0;
+ }
+
+ /* Recreate socket on SIGUSR1. */
+ if (sigusr1) {
+ close(srv_fd);
+ srv_fd = server_create_socket();
+ sigusr1 = 0;
+ }
+
+ /* Initialise pollfd array. */
+ nfds = 1;
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ w = ARRAY_ITEM(&windows, i);
+ if (w != NULL)
+ nfds += window_count_panes(w);
+ }
+ nfds += ARRAY_LENGTH(&clients) * 2;
+ pfds = xrealloc(pfds, nfds, sizeof *pfds);
+ memset(pfds, 0, nfds * sizeof *pfds);
+ pfd = pfds;
+
+ /* Fill server socket. */
+ pfd->fd = srv_fd;
+ pfd->events = POLLIN;
+ pfd++;
+
+ /* Fill window and client sockets. */
+ server_fill_windows(&pfd);
+ server_fill_clients(&pfd);
+
+ /* Update socket permissions. */
+ xtimeout = INFTIM;
+ if (sigterm || server_update_socket() != 0)
+ xtimeout = POLL_TIMEOUT;
+
+ /* Do the poll. */
+ if ((nfds = poll(pfds, nfds, xtimeout)) == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ fatal("poll failed");
+ }
+ pfd = pfds;
+
+ /* Handle server socket. */
+ if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP))
+ fatalx("lost server socket");
+ if (pfd->revents & POLLIN) {
+ server_accept_client(srv_fd);
+ continue;
+ }
+ pfd++;
+
+ /* Call second-based timers. */
+ now = time(NULL);
+ if (now != last) {
+ last = now;
+ server_second_timers();
+ }
+
+ /* Set window names. */
+ set_window_names();
+
+ /*
+ * Handle window and client sockets. Clients can create
+ * windows, so windows must come first to avoid messing up by
+ * increasing the array size.
+ */
+ server_handle_windows(&pfd);
+ server_handle_clients(&pfd);
+
+ /*
+ * If we have no sessions and clients left, let's get out
+ * of here...
+ */
+ n = 0;
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ if (ARRAY_ITEM(&sessions, i) != NULL)
+ n++;
+ }
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ if (ARRAY_ITEM(&clients, i) != NULL)
+ n++;
+ }
+ if (n == 0)
+ break;
+ }
+ if (pfds != NULL)
+ xfree(pfds);
+
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ if (ARRAY_ITEM(&sessions, i) != NULL)
+ session_destroy(ARRAY_ITEM(&sessions, i));
+ }
+ ARRAY_FREE(&sessions);
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ if (ARRAY_ITEM(&clients, i) != NULL)
+ server_lost_client(ARRAY_ITEM(&clients, i));
+ }
+ ARRAY_FREE(&clients);
+
+ key_bindings_free();
+
+ close(srv_fd);
+
+ unlink(socket_path);
+ xfree(socket_path);
+
+ options_free(&global_options);
+ options_free(&global_window_options);
+ if (server_password != NULL)
+ xfree(server_password);
+
+ return (0);
+}
+
+/* Kill all clients. */
+void
+server_shutdown(void)
+{
+ struct session *s;
+ struct client *c;
+ u_int i, j;
+
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ s = ARRAY_ITEM(&sessions, i);
+ for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
+ c = ARRAY_ITEM(&clients, j);
+ if (c != NULL && c->session == s) {
+ s = NULL;
+ break;
+ }
+ }
+ if (s != NULL)
+ session_destroy(s);
+ }
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c != NULL)
+ server_write_client(c, MSG_SHUTDOWN, NULL, 0);
+ }
+}
+
+/* Handle SIGCHLD. */
+void
+server_child_signal(void)
+{
+ struct window *w;
+ struct window_pane *wp;
+ int status;
+ pid_t pid;
+ u_int i;
+
+ for (;;) {
+ switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
+ case -1:
+ if (errno == ECHILD)
+ return;
+ fatal("waitpid");
+ case 0:
+ return;
+ }
+ if (!WIFSTOPPED(status))
+ continue;
+ if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
+ continue;
+
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ w = ARRAY_ITEM(&windows, i);
+ if (w == NULL)
+ continue;
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp->pid == pid) {
+ if (killpg(pid, SIGCONT) != 0)
+ kill(pid, SIGCONT);
+ }
+ }
+ }
+ }
+}
+
+/* Fill window pollfds. */
+void
+server_fill_windows(struct pollfd **pfd)
+{
+ struct window *w;
+ struct window_pane *wp;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ w = ARRAY_ITEM(&windows, i);
+ if (w == NULL)
+ continue;
+
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ (*pfd)->fd = wp->fd;
+ if (wp->fd != -1) {
+ (*pfd)->events = POLLIN;
+ if (BUFFER_USED(wp->out) > 0)
+ (*pfd)->events |= POLLOUT;
+ }
+ (*pfd)++;
+ }
+ }
+}
+
+/* Handle window pollfds. */
+void
+server_handle_windows(struct pollfd **pfd)
+{
+ struct window *w;
+ struct window_pane *wp;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ w = ARRAY_ITEM(&windows, i);
+ if (w == NULL)
+ continue;
+
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp->fd != -1) {
+ if (buffer_poll(*pfd, wp->in, wp->out) != 0) {
+ close(wp->fd);
+ wp->fd = -1;
+ } else
+ server_handle_window(w, wp);
+ }
+ (*pfd)++;
+ }
+
+ server_check_window(w);
+ }
+}
+
+/* Check for general redraw on client. */
+void
+server_check_redraw(struct client *c)
+{
+ struct session *s;
+ struct window_pane *wp;
+ char title[512];
+ int flags, redraw;
+
+ if (c == NULL || c->session == NULL)
+ return;
+ s = c->session;
+
+ flags = c->tty.flags & TTY_FREEZE;
+ c->tty.flags &= ~TTY_FREEZE;
+
+ if (options_get_number(&s->options, "set-titles")) {
+ xsnprintf(title, sizeof title, "%s:%u:%s - \"%s\"",
+ s->name, s->curw->idx, s->curw->window->name,
+ s->curw->window->active->screen->title);
+ if (c->title == NULL || strcmp(title, c->title) != 0) {
+ if (c->title != NULL)
+ xfree(c->title);
+ c->title = xstrdup(title);
+ tty_set_title(&c->tty, c->title);
+ }
+ }
+
+ if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
+ if (c->message_string != NULL)
+ redraw = status_message_redraw(c);
+ else if (c->prompt_string != NULL)
+ redraw = status_prompt_redraw(c);
+ else
+ redraw = status_redraw(c);
+ if (!redraw)
+ c->flags &= ~CLIENT_STATUS;
+ }
+
+ if (c->flags & CLIENT_REDRAW) {
+ if (server_locked)
+ server_redraw_locked(c);
+ else
+ screen_redraw_screen(c);
+ c->flags &= ~CLIENT_STATUS;
+ } else {
+ TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
+ if (wp->flags & PANE_REDRAW)
+ screen_redraw_pane(c, wp);
+ }
+ }
+
+ if (c->flags & CLIENT_STATUS)
+ screen_redraw_status(c);
+
+ c->tty.flags |= flags;
+
+ c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS);
+}
+
+/* Redraw client when locked. */
+void
+server_redraw_locked(struct client *c)
+{
+ struct screen_write_ctx ctx;
+ struct screen screen;
+ u_int colour, xx, yy, i;
+ int style;
+
+ xx = c->tty.sx;
+ yy = c->tty.sy - 1;
+ if (xx == 0 || yy == 0)
+ return;
+ colour = options_get_number(
+ &global_window_options, "clock-mode-colour");
+ style = options_get_number(
+ &global_window_options, "clock-mode-style");
+
+ screen_init(&screen, xx, yy, 0);
+
+ screen_write_start(&ctx, NULL, &screen);
+ clock_draw(&ctx, colour, style);
+ screen_write_stop(&ctx);
+
+ for (i = 0; i < screen_size_y(&screen); i++)
+ tty_draw_line(&c->tty, &screen, i, 0, 0);
+ screen_redraw_status(c);
+
+ screen_free(&screen);
+}
+
+/* Check for timers on client. */
+void
+server_check_timers(struct client *c)
+{
+ struct session *s;
+ struct timeval tv;
+ u_int interval;
+
+ if (c == NULL || c->session == NULL)
+ return;
+ s = c->session;
+
+ if (gettimeofday(&tv, NULL) != 0)
+ fatal("gettimeofday");
+
+ if (c->message_string != NULL && timercmp(&tv, &c->message_timer, >))
+ status_message_clear(c);
+
+ if (c->message_string != NULL || c->prompt_string != NULL) {
+ /*
+ * Don't need timed redraw for messages/prompts so bail now.
+ * The status timer isn't reset when they are redrawn anyway.
+ */
+ return;
+ }
+ if (!options_get_number(&s->options, "status"))
+ return;
+
+ /* Check timer; resolution is only a second so don't be too clever. */
+ interval = options_get_number(&s->options, "status-interval");
+ if (interval == 0)
+ return;
+ if (tv.tv_sec < c->status_timer.tv_sec ||
+ ((u_int) tv.tv_sec) - c->status_timer.tv_sec >= interval)
+ c->flags |= CLIENT_STATUS;
+}
+
+/* Fill client pollfds. */
+void
+server_fill_clients(struct pollfd **pfd)
+{
+ struct client *c;
+ struct window *w;
+ struct window_pane *wp;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+
+ server_check_timers(c);
+ server_check_redraw(c);
+
+ if (c == NULL)
+ (*pfd)->fd = -1;
+ else {
+ (*pfd)->fd = c->fd;
+ (*pfd)->events = POLLIN;
+ if (BUFFER_USED(c->out) > 0)
+ (*pfd)->events |= POLLOUT;
+ }
+ (*pfd)++;
+
+ if (c == NULL || c->flags & CLIENT_SUSPENDED ||
+ c->tty.fd == -1 || c->session == NULL)
+ (*pfd)->fd = -1;
+ else {
+ (*pfd)->fd = c->tty.fd;
+ (*pfd)->events = POLLIN;
+ if (BUFFER_USED(c->tty.out) > 0)
+ (*pfd)->events |= POLLOUT;
+ }
+ (*pfd)++;
+ }
+
+ /*
+ * Clear any window redraw flags (will have been redrawn as part of
+ * client).
+ */
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ w = ARRAY_ITEM(&windows, i);
+ if (w == NULL)
+ continue;
+
+ w->flags &= ~WINDOW_REDRAW;
+ TAILQ_FOREACH(wp, &w->panes, entry)
+ wp->flags &= ~PANE_REDRAW;
+ }
+}
+
+/* Handle client pollfds. */
+void
+server_handle_clients(struct pollfd **pfd)
+{
+ struct client *c;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+
+ if (c != NULL) {
+ if (buffer_poll(*pfd, c->in, c->out) != 0) {
+ server_lost_client(c);
+ (*pfd) += 2;
+ continue;
+ } else
+ server_msg_dispatch(c);
+ }
+ (*pfd)++;
+
+ if (c != NULL && !(c->flags & CLIENT_SUSPENDED) &&
+ c->tty.fd != -1 && c->session != NULL) {
+ if (buffer_poll(*pfd, c->tty.in, c->tty.out) != 0)
+ server_lost_client(c);
+ else
+ server_handle_client(c);
+ }
+ (*pfd)++;
+ }
+}
+
+/* accept(2) and create new client. */
+struct client *
+server_accept_client(int srv_fd)
+{
+ struct sockaddr_storage sa;
+ socklen_t slen = sizeof sa;
+ int fd;
+
+ fd = accept(srv_fd, (struct sockaddr *) &sa, &slen);
+ if (fd == -1) {
+ if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
+ return (NULL);
+ fatal("accept failed");
+ }
+ if (sigterm) {
+ close(fd);
+ return (NULL);
+ }
+ return (server_create_client(fd));
+}
+
+/* Input data from client. */
+void
+server_handle_client(struct client *c)
+{
+ struct window_pane *wp;
+ struct screen *s;
+ struct timeval tv;
+ struct key_binding *bd;
+ int key, prefix, status, xtimeout;
+ int mode;
+ u_char mouse[3];
+
+ xtimeout = options_get_number(&c->session->options, "repeat-time");
+ if (xtimeout != 0 && c->flags & CLIENT_REPEAT) {
+ if (gettimeofday(&tv, NULL) != 0)
+ fatal("gettimeofday");
+ if (timercmp(&tv, &c->repeat_timer, >))
+ c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
+ }
+
+ /* Process keys. */
+ prefix = options_get_number(&c->session->options, "prefix");
+ while (tty_keys_next(&c->tty, &key, mouse) == 0) {
+ server_activity = time(NULL);
+
+ if (c->session == NULL)
+ return;
+ wp = c->session->curw->window->active; /* could die */
+
+ status_message_clear(c);
+ if (c->prompt_string != NULL) {
+ status_prompt_key(c, key);
+ continue;
+ }
+ if (server_locked)
+ continue;
+
+ /* Check for mouse keys. */
+ if (key == KEYC_MOUSE) {
+ window_pane_mouse(wp, c, mouse[0], mouse[1], mouse[2]);
+ continue;
+ }
+
+ /* No previous prefix key. */
+ if (!(c->flags & CLIENT_PREFIX)) {
+ if (key == prefix)
+ c->flags |= CLIENT_PREFIX;
+ else
+ window_pane_key(wp, c, key);
+ continue;
+ }
+
+ /* Prefix key already pressed. Reset prefix and lookup key. */
+ c->flags &= ~CLIENT_PREFIX;
+ if ((bd = key_bindings_lookup(key)) == NULL) {
+ /* If repeating, treat this as a key, else ignore. */
+ if (c->flags & CLIENT_REPEAT) {
+ c->flags &= ~CLIENT_REPEAT;
+ if (key == prefix)
+ c->flags |= CLIENT_PREFIX;
+ else
+ window_pane_key(wp, c, key);
+ }
+ continue;
+ }
+
+ /* If already repeating, but this key can't repeat, skip it. */
+ if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
+ c->flags &= ~CLIENT_REPEAT;
+ if (key == prefix)
+ c->flags |= CLIENT_PREFIX;
+ else
+ window_pane_key(wp, c, key);
+ continue;
+ }
+
+ /* If this key can repeat, reset the repeat flags and timer. */
+ if (xtimeout != 0 && bd->can_repeat) {
+ c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
+
+ tv.tv_sec = xtimeout / 1000;
+ tv.tv_usec = (xtimeout % 1000) * 1000L;
+ if (gettimeofday(&c->repeat_timer, NULL) != 0)
+ fatal("gettimeofday");
+ timeradd(&c->repeat_timer, &tv, &c->repeat_timer);
+ }
+
+ /* Dispatch the command. */
+ key_bindings_dispatch(bd, c);
+ }
+ if (c->session == NULL)
+ return;
+ wp = c->session->curw->window->active; /* could die - do each loop */
+ s = wp->screen;
+
+ /* Ensure cursor position and mode settings. */
+ status = options_get_number(&c->session->options, "status");
+ if (wp->yoff + s->cy < c->tty.sy - status)
+ tty_cursor(&c->tty, s->cx, s->cy, wp->xoff, wp->yoff);
+
+ mode = s->mode;
+ if (server_locked)
+ mode &= ~TTY_NOCURSOR;
+ tty_update_mode(&c->tty, mode);
+}
+
+/* Lost a client. */
+void
+server_lost_client(struct client *c)
+{
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ if (ARRAY_ITEM(&clients, i) == c)
+ ARRAY_SET(&clients, i, NULL);
+ }
+
+ tty_free(&c->tty, c->flags & CLIENT_SUSPENDED);
+
+ screen_free(&c->status);
+
+ if (c->title != NULL)
+ xfree(c->title);
+
+ if (c->message_string != NULL)
+ xfree(c->message_string);
+
+ if (c->prompt_string != NULL)
+ xfree(c->prompt_string);
+ if (c->prompt_buffer != NULL)
+ xfree(c->prompt_buffer);
+ for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++)
+ xfree(ARRAY_ITEM(&c->prompt_hdata, i));
+ ARRAY_FREE(&c->prompt_hdata);
+
+ if (c->cwd != NULL)
+ xfree(c->cwd);
+
+ close(c->fd);
+ buffer_destroy(c->in);
+ buffer_destroy(c->out);
+ xfree(c);
+
+ recalculate_sizes();
+}
+
+/* Handle window data. */
+void
+server_handle_window(struct window *w, struct window_pane *wp)
+{
+ struct session *s;
+ u_int i;
+ int update;
+
+ window_pane_parse(wp);
+
+ if ((w->flags & (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT)) == 0)
+ return;
+
+ update = 0;
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ s = ARRAY_ITEM(&sessions, i);
+ if (s == NULL || !session_has(s, w))
+ continue;
+
+ update += server_check_window_bell(s, w, wp);
+ update += server_check_window_activity(s, w);
+ update += server_check_window_content(s, w, wp);
+ }
+ if (update)
+ server_status_window(w);
+
+ w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT);
+}
+
+int
+server_check_window_bell(
+ struct session *s, struct window *w, struct window_pane *wp)
+{
+ struct client *c;
+ u_int i;
+ int action;
+
+ if (!(w->flags & WINDOW_BELL))
+ return (0);
+ if (session_alert_has_window(s, w, WINDOW_BELL))
+ return (0);
+ session_alert_add(s, w, WINDOW_BELL);
+
+ action = options_get_number(&s->options, "bell-action");
+ switch (action) {
+ case BELL_ANY:
+ if (s->flags & SESSION_UNATTACHED)
+ break;
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c != NULL && c->session == s)
+ tty_putcode(&c->tty, TTYC_BEL);
+ }
+ break;
+ case BELL_CURRENT:
+ if (w->active != wp)
+ break;
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c != NULL && c->session == s)
+ tty_putcode(&c->tty, TTYC_BEL);
+ }
+ break;
+ }
+ return (1);
+}
+
+int
+server_check_window_activity(struct session *s, struct window *w)
+{
+ if (!(w->flags & WINDOW_ACTIVITY))
+ return (0);
+ if (!options_get_number(&w->options, "monitor-activity"))
+ return (0);
+ if (session_alert_has_window(s, w, WINDOW_ACTIVITY))
+ return (0);
+ session_alert_add(s, w, WINDOW_ACTIVITY);
+ return (1);
+}
+
+int
+server_check_window_content(
+ struct session *s, struct window *w, struct window_pane *wp)
+{
+ char *found, *ptr;
+
+ if (!(w->flags & WINDOW_CONTENT))
+ return (0);
+ if ((ptr = options_get_string(&w->options, "monitor-content")) == NULL)
+ return (0);
+ if (*ptr == '\0')
+ return (0);
+ if (session_alert_has_window(s, w, WINDOW_CONTENT))
+ return (0);
+ if ((found = window_pane_search(wp, ptr)) == NULL)
+ return (0);
+ session_alert_add(s, w, WINDOW_CONTENT);
+ xfree(found);
+ return (1);
+}
+
+/* Check if window still exists.. */
+void
+server_check_window(struct window *w)
+{
+ struct window_pane *wp, *wq;
+ struct client *c;
+ struct session *s;
+ struct winlink *wl;
+ u_int i, j;
+ int destroyed, flag;
+
+ flag = options_get_number(&w->options, "remain-on-exit");
+
+ destroyed = 1;
+
+ wp = TAILQ_FIRST(&w->panes);
+ while (wp != NULL) {
+ wq = TAILQ_NEXT(wp, entry);
+ if (wp->fd != -1)
+ destroyed = 0;
+ else if (!flag) {
+ window_remove_pane(w, wp);
+ server_redraw_window(w);
+ layout_refresh(w, 0);
+ }
+ wp = wq;
+ }
+
+ if (!destroyed)
+ return;
+
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ s = ARRAY_ITEM(&sessions, i);
+ if (s == NULL)
+ continue;
+ if (!session_has(s, w))
+ continue;
+
+ restart:
+ /* Detach window and either redraw or kill clients. */
+ RB_FOREACH(wl, winlinks, &s->windows) {
+ if (wl->window != w)
+ continue;
+ destroyed = session_detach(s, wl);
+ for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
+ c = ARRAY_ITEM(&clients, j);
+ if (c == NULL || c->session != s)
+ continue;
+ if (!destroyed) {
+ server_redraw_client(c);
+ continue;
+ }
+ c->session = NULL;
+ server_write_client(c, MSG_EXIT, NULL, 0);
+ }
+ /* If the session was destroyed, bail now. */
+ if (destroyed)
+ break;
+ goto restart;
+ }
+ }
+
+ recalculate_sizes();
+}
+
+/* Call any once-per-second timers. */
+void
+server_second_timers(void)
+{
+ struct window *w;
+ struct window_pane *wp;
+ u_int i;
+ int xtimeout;
+ struct tm now, then;
+ static time_t last_t = 0;
+ time_t t;
+
+ t = time(NULL);
+ xtimeout = options_get_number(&global_options, "lock-after-time");
+ if (xtimeout > 0 && t > server_activity + xtimeout)
+ server_lock();
+
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ w = ARRAY_ITEM(&windows, i);
+ if (w == NULL)
+ continue;
+
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp->mode != NULL && wp->mode->timer != NULL)
+ wp->mode->timer(wp);
+ }
+ }
+
+ /* Check for a minute having passed. */
+ gmtime_r(&t, &now);
+ gmtime_r(&last_t, &then);
+ if (now.tm_min == then.tm_min)
+ return;
+ last_t = t;
+
+ /* If locked, redraw all clients. */
+ if (server_locked) {
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ if (ARRAY_ITEM(&clients, i) != NULL)
+ server_redraw_client(ARRAY_ITEM(&clients, i));
+ }
+ }
+}
+
+/* Update socket execute permissions based on whether sessions are attached. */
+int
+server_update_socket(void)
+{
+ struct session *s;
+ u_int i;
+ static int last = -1;
+ int n;
+
+ n = 0;
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ s = ARRAY_ITEM(&sessions, i);
+ if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
+ n++;
+ break;
+ }
+ }
+
+ if (n != last) {
+ last = n;
+ if (n != 0)
+ chmod(socket_path, S_IRWXU);
+ else
+ chmod(socket_path, S_IRUSR|S_IWUSR);
+ }
+
+ return (n);
+}
diff --git a/usr.bin/tmux/session.c b/usr.bin/tmux/session.c
new file mode 100644
index 00000000000..8ae08a772ab
--- /dev/null
+++ b/usr.bin/tmux/session.c
@@ -0,0 +1,378 @@
+/* $OpenBSD: session.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/time.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/* Global session list. */
+struct sessions sessions;
+
+struct winlink *session_next_activity(struct session *, struct winlink *);
+struct winlink *session_previous_activity(struct session *, struct winlink *);
+
+void
+session_alert_cancel(struct session *s, struct winlink *wl)
+{
+ struct session_alert *sa, *sb;
+
+ sa = SLIST_FIRST(&s->alerts);
+ while (sa != NULL) {
+ sb = sa;
+ sa = SLIST_NEXT(sa, entry);
+
+ if (wl == NULL || sb->wl == wl) {
+ SLIST_REMOVE(&s->alerts, sb, session_alert, entry);
+ xfree(sb);
+ }
+ }
+}
+
+void
+session_alert_add(struct session *s, struct window *w, int type)
+{
+ struct session_alert *sa;
+ struct winlink *wl;
+
+ RB_FOREACH(wl, winlinks, &s->windows) {
+ if (wl == s->curw)
+ continue;
+
+ if (wl->window == w &&
+ !session_alert_has(s, wl, type)) {
+ sa = xmalloc(sizeof *sa);
+ sa->wl = wl;
+ sa->type = type;
+ SLIST_INSERT_HEAD(&s->alerts, sa, entry);
+ }
+ }
+}
+
+int
+session_alert_has(struct session *s, struct winlink *wl, int type)
+{
+ struct session_alert *sa;
+
+ SLIST_FOREACH(sa, &s->alerts, entry) {
+ if (sa->wl == wl && sa->type == type)
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+session_alert_has_window(struct session *s, struct window *w, int type)
+{
+ struct session_alert *sa;
+
+ SLIST_FOREACH(sa, &s->alerts, entry) {
+ if (sa->wl->window == w && sa->type == type)
+ return (1);
+ }
+
+ return (0);
+}
+
+/* Find session by name. */
+struct session *
+session_find(const char *name)
+{
+ struct session *s;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ s = ARRAY_ITEM(&sessions, i);
+ if (s != NULL && strcmp(s->name, name) == 0)
+ return (s);
+ }
+
+ return (NULL);
+}
+
+/* Create a new session. */
+struct session *
+session_create(const char *name,
+ const char *cmd, const char *cwd, u_int sx, u_int sy, char **cause)
+{
+ struct session *s;
+ u_int i;
+
+ s = xmalloc(sizeof *s);
+ s->flags = 0;
+ if (gettimeofday(&s->tv, NULL) != 0)
+ fatal("gettimeofday");
+ s->curw = NULL;
+ SLIST_INIT(&s->lastw);
+ RB_INIT(&s->windows);
+ SLIST_INIT(&s->alerts);
+ paste_init_stack(&s->buffers);
+ options_init(&s->options, &global_options);
+
+ s->sx = sx;
+ s->sy = sy;
+
+ for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
+ if (ARRAY_ITEM(&sessions, i) == NULL) {
+ ARRAY_SET(&sessions, i, s);
+ break;
+ }
+ }
+ if (i == ARRAY_LENGTH(&sessions))
+ ARRAY_ADD(&sessions, s);
+
+ if (name != NULL)
+ s->name = xstrdup(name);
+ else
+ xasprintf(&s->name, "%u", i);
+ if (session_new(s, NULL, cmd, cwd, -1, cause) == NULL) {
+ session_destroy(s);
+ return (NULL);
+ }
+ session_select(s, 0);
+
+ log_debug("session %s created", s->name);
+
+ return (s);
+}
+
+/* Destroy a session. */
+void
+session_destroy(struct session *s)
+{
+ u_int i;
+
+ log_debug("session %s destroyed", s->name);
+
+ if (session_index(s, &i) != 0)
+ fatalx("session not found");
+ ARRAY_SET(&sessions, i, NULL);
+ while (!ARRAY_EMPTY(&sessions) && ARRAY_LAST(&sessions) == NULL)
+ ARRAY_TRUNC(&sessions, 1);
+
+ session_alert_cancel(s, NULL);
+ options_free(&s->options);
+ paste_free_stack(&s->buffers);
+
+ while (!SLIST_EMPTY(&s->lastw))
+ winlink_stack_remove(&s->lastw, SLIST_FIRST(&s->lastw));
+ while (!RB_EMPTY(&s->windows))
+ winlink_remove(&s->windows, RB_ROOT(&s->windows));
+
+ xfree(s->name);
+ xfree(s);
+}
+
+/* Find session index. */
+int
+session_index(struct session *s, u_int *i)
+{
+ for (*i = 0; *i < ARRAY_LENGTH(&sessions); (*i)++) {
+ if (s == ARRAY_ITEM(&sessions, *i))
+ return (0);
+ }
+ return (-1);
+}
+
+/* Create a new window on a session. */
+struct winlink *
+session_new(struct session *s,
+ const char *name, const char *cmd, const char *cwd, int idx, char **cause)
+{
+ struct window *w;
+ const char **env;
+ u_int hlimit;
+
+ env = server_fill_environ(s);
+
+ hlimit = options_get_number(&s->options, "history-limit");
+ w = window_create(name, cmd, cwd, env, s->sx, s->sy, hlimit, cause);
+ if (w == NULL)
+ return (NULL);
+
+ if (options_get_number(&s->options, "set-remain-on-exit"))
+ options_set_number(&w->options, "remain-on-exit", 1);
+
+ return (session_attach(s, w, idx, cause));
+}
+
+/* Attach a window to a session. */
+struct winlink *
+session_attach(struct session *s, struct window *w, int idx, char **cause)
+{
+ struct winlink *wl;
+
+ if ((wl = winlink_add(&s->windows, w, idx)) == NULL)
+ xasprintf(cause, "index in use: %d", idx);
+ return (wl);
+}
+
+/* Detach a window from a session. */
+int
+session_detach(struct session *s, struct winlink *wl)
+{
+ if (s->curw == wl &&
+ session_last(s) != 0 && session_previous(s, 0) != 0)
+ session_next(s, 0);
+
+ session_alert_cancel(s, wl);
+ winlink_stack_remove(&s->lastw, wl);
+ winlink_remove(&s->windows, wl);
+ if (RB_EMPTY(&s->windows)) {
+ session_destroy(s);
+ return (1);
+ }
+ return (0);
+}
+
+/* Return if session has window. */
+int
+session_has(struct session *s, struct window *w)
+{
+ struct winlink *wl;
+
+ RB_FOREACH(wl, winlinks, &s->windows) {
+ if (wl->window == w)
+ return (1);
+ }
+ return (0);
+}
+
+struct winlink *
+session_next_activity(struct session *s, struct winlink *wl)
+{
+ while (wl != NULL) {
+ if (session_alert_has(s, wl, WINDOW_BELL))
+ break;
+ if (session_alert_has(s, wl, WINDOW_ACTIVITY))
+ break;
+ if (session_alert_has(s, wl, WINDOW_CONTENT))
+ break;
+ wl = winlink_next(&s->windows, wl);
+ }
+ return (wl);
+}
+
+/* Move session to next window. */
+int
+session_next(struct session *s, int activity)
+{
+ struct winlink *wl;
+
+ if (s->curw == NULL)
+ return (-1);
+
+ wl = winlink_next(&s->windows, s->curw);
+ if (activity)
+ wl = session_next_activity(s, wl);
+ if (wl == NULL) {
+ wl = RB_MIN(winlinks, &s->windows);
+ if (activity && ((wl = session_next_activity(s, wl)) == NULL))
+ return (-1);
+ }
+ if (wl == s->curw)
+ return (1);
+ winlink_stack_remove(&s->lastw, wl);
+ winlink_stack_push(&s->lastw, s->curw);
+ s->curw = wl;
+ session_alert_cancel(s, wl);
+ return (0);
+}
+
+struct winlink *
+session_previous_activity(struct session *s, struct winlink *wl)
+{
+ while (wl != NULL) {
+ if (session_alert_has(s, wl, WINDOW_BELL))
+ break;
+ if (session_alert_has(s, wl, WINDOW_ACTIVITY))
+ break;
+ if (session_alert_has(s, wl, WINDOW_CONTENT))
+ break;
+ wl = winlink_previous(&s->windows, wl);
+ }
+ return (wl);
+}
+
+/* Move session to previous window. */
+int
+session_previous(struct session *s, int activity)
+{
+ struct winlink *wl;
+
+ if (s->curw == NULL)
+ return (-1);
+
+ wl = winlink_previous(&s->windows, s->curw);
+ if (activity)
+ wl = session_previous_activity(s, wl);
+ if (wl == NULL) {
+ wl = RB_MAX(winlinks, &s->windows);
+ if (activity && (wl = session_previous_activity(s, wl)) == NULL)
+ return (-1);
+ }
+ if (wl == s->curw)
+ return (1);
+ winlink_stack_remove(&s->lastw, wl);
+ winlink_stack_push(&s->lastw, s->curw);
+ s->curw = wl;
+ session_alert_cancel(s, wl);
+ return (0);
+}
+
+/* Move session to specific window. */
+int
+session_select(struct session *s, int idx)
+{
+ struct winlink *wl;
+
+ wl = winlink_find_by_index(&s->windows, idx);
+ if (wl == NULL)
+ return (-1);
+ if (wl == s->curw)
+ return (1);
+ winlink_stack_remove(&s->lastw, wl);
+ winlink_stack_push(&s->lastw, s->curw);
+ s->curw = wl;
+ session_alert_cancel(s, wl);
+ return (0);
+}
+
+/* Move session to last used window. */
+int
+session_last(struct session *s)
+{
+ struct winlink *wl;
+
+ wl = SLIST_FIRST(&s->lastw);
+ if (wl == NULL)
+ return (-1);
+ if (wl == s->curw)
+ return (1);
+
+ winlink_stack_remove(&s->lastw, wl);
+ winlink_stack_push(&s->lastw, s->curw);
+ s->curw = wl;
+ session_alert_cancel(s, wl);
+ return (0);
+}
diff --git a/usr.bin/tmux/status.c b/usr.bin/tmux/status.c
new file mode 100644
index 00000000000..d7f09acdb28
--- /dev/null
+++ b/usr.bin/tmux/status.c
@@ -0,0 +1,960 @@
+/* $OpenBSD: status.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/time.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+char *status_replace(struct session *, char *, time_t);
+char *status_replace_popen(char **);
+size_t status_width(struct winlink *);
+char *status_print(struct session *, struct winlink *, struct grid_cell *);
+
+void status_prompt_add_history(struct client *);
+char *status_prompt_complete(const char *);
+
+/* Draw status for client on the last lines of given context. */
+int
+status_redraw(struct client *c)
+{
+ struct screen_write_ctx ctx;
+ struct session *s = c->session;
+ struct winlink *wl;
+ struct window_pane *wp;
+ struct screen *sc = NULL, old_status;
+ char *left, *right, *text, *ptr;
+ size_t llen, rlen, offset, xx, yy, sy;
+ size_t size, start, width;
+ struct grid_cell stdgc, gc;
+ int larrow, rarrow;
+
+ left = right = NULL;
+
+ /* Create the target screen. */
+ memcpy(&old_status, &c->status, sizeof old_status);
+ screen_init(&c->status, c->tty.sx, 1, 0);
+
+ /* No status line? */
+ if (c->tty.sy == 0 || !options_get_number(&s->options, "status"))
+ goto off;
+ larrow = rarrow = 0;
+
+ if (gettimeofday(&c->status_timer, NULL) != 0)
+ fatal("gettimeofday");
+ memcpy(&stdgc, &grid_default_cell, sizeof gc);
+ stdgc.bg = options_get_number(&s->options, "status-fg");
+ stdgc.fg = options_get_number(&s->options, "status-bg");
+ stdgc.attr |= options_get_number(&s->options, "status-attr");
+
+ yy = c->tty.sy - 1;
+ if (yy == 0)
+ goto blank;
+
+ /* Work out the left and right strings. */
+ left = status_replace(s, options_get_string(
+ &s->options, "status-left"), c->status_timer.tv_sec);
+ llen = options_get_number(&s->options, "status-left-length");
+ if (strlen(left) < llen)
+ llen = strlen(left);
+ left[llen] = '\0';
+
+ right = status_replace(s, options_get_string(
+ &s->options, "status-right"), c->status_timer.tv_sec);
+ rlen = options_get_number(&s->options, "status-right-length");
+ if (strlen(right) < rlen)
+ rlen = strlen(right);
+ right[rlen] = '\0';
+
+ /*
+ * Figure out how much space we have for the window list. If there isn't
+ * enough space, just wimp out.
+ */
+ xx = 0;
+ if (llen != 0)
+ xx += llen + 1;
+ if (rlen != 0)
+ xx += rlen + 1;
+ if (c->tty.sx == 0 || c->tty.sx <= xx)
+ goto blank;
+ xx = c->tty.sx - xx;
+
+ /*
+ * Right. We have xx characters to fill. Find out how much is to go in
+ * them and the offset of the current window (it must be on screen).
+ */
+ width = offset = 0;
+ RB_FOREACH(wl, winlinks, &s->windows) {
+ size = status_width(wl) + 1;
+ if (wl == s->curw)
+ offset = width;
+ width += size;
+ }
+ start = 0;
+
+ /* If there is enough space for the total width, all is gravy. */
+ if (width <= xx)
+ goto draw;
+
+ /* Find size of current window text. */
+ size = status_width(s->curw);
+
+ /*
+ * If the offset is already on screen, we're good to draw from the
+ * start and just leave off the end.
+ */
+ if (offset + size < xx) {
+ if (xx > 0) {
+ rarrow = 1;
+ xx--;
+ }
+
+ width = xx;
+ goto draw;
+ }
+
+ /*
+ * Work out how many characters we need to omit from the start. There
+ * are xx characters to fill, and offset + size must be the last. So,
+ * the start character is offset + size - xx.
+ */
+ if (xx > 0) {
+ larrow = 1;
+ xx--;
+ }
+
+ start = offset + size - xx;
+ if (xx > 0 && width > start + xx + 1) { /* + 1, eh? */
+ rarrow = 1;
+ start++;
+ xx--;
+ }
+ width = xx;
+
+draw:
+ /* Bail here if anything is too small too. XXX. */
+ if (width == 0 || xx == 0)
+ goto blank;
+
+ /* Begin drawing and move to the starting position. */
+ screen_write_start(&ctx, NULL, &c->status);
+ if (llen != 0) {
+ screen_write_cursormove(&ctx, 0, yy);
+ screen_write_puts(&ctx, &stdgc, "%s ", left);
+ if (larrow)
+ screen_write_putc(&ctx, &stdgc, ' ');
+ } else {
+ if (larrow)
+ screen_write_cursormove(&ctx, 1, yy);
+ else
+ screen_write_cursormove(&ctx, 0, yy);
+ }
+
+ /* Draw each character in succession. */
+ offset = 0;
+ RB_FOREACH(wl, winlinks, &s->windows) {
+ memcpy(&gc, &stdgc, sizeof gc);
+ text = status_print(s, wl, &gc);
+
+ if (larrow == 1 && offset < start) {
+ if (session_alert_has(s, wl, WINDOW_ACTIVITY))
+ larrow = -1;
+ else if (session_alert_has(s, wl, WINDOW_BELL))
+ larrow = -1;
+ else if (session_alert_has(s, wl, WINDOW_CONTENT))
+ larrow = -1;
+ }
+
+ for (ptr = text; *ptr != '\0'; ptr++) {
+ if (offset >= start && offset < start + width)
+ screen_write_putc(&ctx, &gc, *ptr);
+ offset++;
+ }
+
+ if (rarrow == 1 && offset > start + width) {
+ if (session_alert_has(s, wl, WINDOW_ACTIVITY))
+ rarrow = -1;
+ else if (session_alert_has(s, wl, WINDOW_BELL))
+ rarrow = -1;
+ else if (session_alert_has(s, wl, WINDOW_CONTENT))
+ rarrow = -1;
+ }
+
+ if (offset < start + width) {
+ if (offset >= start) {
+ screen_write_putc(&ctx, &stdgc, ' ');
+ }
+ offset++;
+ }
+
+ xfree(text);
+ }
+
+ /* Fill the remaining space if any. */
+ while (offset++ < xx)
+ screen_write_putc(&ctx, &stdgc, ' ');
+
+ /* Draw the last item. */
+ if (rlen != 0) {
+ screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, yy);
+ screen_write_puts(&ctx, &stdgc, " %s", right);
+ }
+
+ /* Draw the arrows. */
+ if (larrow != 0) {
+ memcpy(&gc, &stdgc, sizeof gc);
+ if (larrow == -1)
+ gc.attr ^= GRID_ATTR_REVERSE;
+ if (llen != 0)
+ screen_write_cursormove(&ctx, llen + 1, yy);
+ else
+ screen_write_cursormove(&ctx, 0, yy);
+ screen_write_putc(&ctx, &gc, '<');
+ }
+ if (rarrow != 0) {
+ memcpy(&gc, &stdgc, sizeof gc);
+ if (rarrow == -1)
+ gc.attr ^= GRID_ATTR_REVERSE;
+ if (rlen != 0)
+ screen_write_cursormove(&ctx, c->tty.sx - rlen - 2, yy);
+ else
+ screen_write_cursormove(&ctx, c->tty.sx - 1, yy);
+ screen_write_putc(&ctx, &gc, '>');
+ }
+
+ goto out;
+
+blank:
+ /* Just draw the whole line as blank. */
+ screen_write_start(&ctx, NULL, &c->status);
+ screen_write_cursormove(&ctx, 0, yy);
+ for (offset = 0; offset < c->tty.sx; offset++)
+ screen_write_putc(&ctx, &stdgc, ' ');
+
+ goto out;
+
+off:
+ /*
+ * Draw the real window last line. Necessary to wipe over message if
+ * status is off. Not sure this is the right place for this.
+ */
+ memcpy(&stdgc, &grid_default_cell, sizeof stdgc);
+ screen_write_start(&ctx, NULL, &c->status);
+
+ sy = 0;
+ TAILQ_FOREACH(wp, &s->curw->window->panes, entry) {
+ sy += wp->sy + 1;
+ sc = wp->screen;
+ }
+
+ screen_write_cursormove(&ctx, 0, 0);
+ if (sy < c->tty.sy) {
+ /* If the screen is too small, use blank. */
+ for (offset = 0; offset < c->tty.sx; offset++)
+ screen_write_putc(&ctx, &stdgc, ' ');
+ } else {
+ screen_write_copy(&ctx,
+ sc, 0, sc->grid->hsize + screen_size_y(sc) - 1, c->tty.sx, 1);
+ }
+
+out:
+ screen_write_stop(&ctx);
+
+ if (left != NULL)
+ xfree(left);
+ if (right != NULL)
+ xfree(right);
+
+ if (grid_compare(c->status.grid, old_status.grid) == 0) {
+ screen_free(&old_status);
+ return (0);
+ }
+ screen_free(&old_status);
+ return (1);
+}
+
+char *
+status_replace(struct session *s, char *fmt, time_t t)
+{
+ struct winlink *wl = s->curw;
+ static char out[BUFSIZ];
+ char in[BUFSIZ], tmp[256], ch, *iptr, *optr, *ptr, *endptr;
+ char *savedptr;
+ size_t len;
+ long n;
+
+ strftime(in, sizeof in, fmt, localtime(&t));
+ in[(sizeof in) - 1] = '\0';
+
+ iptr = in;
+ optr = out;
+ savedptr = NULL;
+
+ while (*iptr != '\0') {
+ if (optr >= out + (sizeof out) - 1)
+ break;
+ switch (ch = *iptr++) {
+ case '#':
+ errno = 0;
+ n = strtol(iptr, &endptr, 10);
+ if ((n == 0 && errno != EINVAL) ||
+ (n == LONG_MIN && errno != ERANGE) ||
+ (n == LONG_MAX && errno != ERANGE) ||
+ n != 0)
+ iptr = endptr;
+ if (n <= 0)
+ n = LONG_MAX;
+
+ ptr = NULL;
+ switch (*iptr++) {
+ case '(':
+ if (ptr == NULL) {
+ ptr = status_replace_popen(&iptr);
+ if (ptr == NULL)
+ break;
+ savedptr = ptr;
+ }
+ /* FALLTHROUGH */
+ case 'H':
+ if (ptr == NULL) {
+ if (gethostname(tmp, sizeof tmp) != 0)
+ fatal("gethostname");
+ ptr = tmp;
+ }
+ /* FALLTHROUGH */
+ case 'S':
+ if (ptr == NULL)
+ ptr = s->name;
+ /* FALLTHROUGH */
+ case 'T':
+ if (ptr == NULL)
+ ptr = wl->window->active->base.title;
+ len = strlen(ptr);
+ if ((size_t) n < len)
+ len = n;
+ if (optr + len >= out + (sizeof out) - 1)
+ break;
+ while (len > 0 && *ptr != '\0') {
+ *optr++ = *ptr++;
+ len--;
+ }
+ break;
+ case '#':
+ *optr++ = '#';
+ break;
+ }
+ if (savedptr != NULL) {
+ xfree(savedptr);
+ savedptr = NULL;
+ }
+ break;
+ default:
+ *optr++ = ch;
+ break;
+ }
+ }
+ *optr = '\0';
+
+ return (xstrdup(out));
+}
+
+char *
+status_replace_popen(char **iptr)
+{
+ FILE *f;
+ char *buf, *cmd, *ptr;
+ int lastesc;
+ size_t len;
+
+ if (**iptr == '\0')
+ return (NULL);
+ if (**iptr == ')') { /* no command given */
+ (*iptr)++;
+ return (NULL);
+ }
+
+ buf = NULL;
+
+ cmd = xmalloc(strlen(*iptr) + 1);
+ len = 0;
+
+ lastesc = 0;
+ for (; **iptr != '\0'; (*iptr)++) {
+ if (!lastesc && **iptr == ')')
+ break; /* unescaped ) is the end */
+ if (!lastesc && **iptr == '\\') {
+ lastesc = 1;
+ continue; /* skip \ if not escaped */
+ }
+ lastesc = 0;
+ cmd[len++] = **iptr;
+ }
+ if (**iptr == '\0') /* no terminating ) */
+ goto out;
+ (*iptr)++; /* skip final ) */
+ cmd[len] = '\0';
+
+ if ((f = popen(cmd, "r")) == NULL)
+ goto out;
+
+ if ((buf = fgetln(f, &len)) == NULL) {
+ pclose(f);
+ goto out;
+ }
+ if (buf[len - 1] == '\n') {
+ buf[len - 1] = '\0';
+ buf = xstrdup(buf);
+ } else {
+ ptr = xmalloc(len + 1);
+ memcpy(ptr, buf, len);
+ ptr[len] = '\0';
+ buf = ptr;
+ }
+ pclose(f);
+
+out:
+ xfree(cmd);
+ return (buf);
+}
+
+size_t
+status_width(struct winlink *wl)
+{
+ return (xsnprintf(NULL, 0, "%d:%s ", wl->idx, wl->window->name));
+}
+
+char *
+status_print(struct session *s, struct winlink *wl, struct grid_cell *gc)
+{
+ char *text, flag;
+ u_char fg, bg, attr;
+
+ fg = options_get_number(&wl->window->options, "window-status-fg");
+ if (fg != 8)
+ gc->fg = fg;
+ bg = options_get_number(&wl->window->options, "window-status-bg");
+ if (bg != 8)
+ gc->bg = bg;
+ attr = options_get_number(&wl->window->options, "window-status-attr");
+ if (attr != 0)
+ gc->attr = attr;
+
+ flag = ' ';
+ if (wl == SLIST_FIRST(&s->lastw))
+ flag = '-';
+ if (wl == s->curw)
+ flag = '*';
+
+ if (session_alert_has(s, wl, WINDOW_ACTIVITY)) {
+ flag = '#';
+ gc->attr ^= GRID_ATTR_REVERSE;
+ } else if (session_alert_has(s, wl, WINDOW_BELL)) {
+ flag = '!';
+ gc->attr ^= GRID_ATTR_REVERSE;
+ } else if (session_alert_has(s, wl, WINDOW_CONTENT)) {
+ flag = '+';
+ gc->attr ^= GRID_ATTR_REVERSE;
+ }
+
+ xasprintf(&text, "%d:%s%c", wl->idx, wl->window->name, flag);
+ return (text);
+}
+
+void
+status_message_set(struct client *c, const char *msg)
+{
+ struct timeval tv;
+ int delay;
+
+ delay = options_get_number(&c->session->options, "display-time");
+ tv.tv_sec = delay / 1000;
+ tv.tv_usec = (delay % 1000) * 1000L;
+
+ c->message_string = xstrdup(msg);
+ if (gettimeofday(&c->message_timer, NULL) != 0)
+ fatal("gettimeofday");
+ timeradd(&c->message_timer, &tv, &c->message_timer);
+
+ c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
+ c->flags |= CLIENT_STATUS;
+}
+
+void
+status_message_clear(struct client *c)
+{
+ if (c->message_string == NULL)
+ return;
+
+ xfree(c->message_string);
+ c->message_string = NULL;
+
+ c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
+ c->flags |= CLIENT_REDRAW;
+}
+
+/* Draw client message on status line of present else on last line. */
+int
+status_message_redraw(struct client *c)
+{
+ struct screen_write_ctx ctx;
+ struct session *s = c->session;
+ struct screen old_status;
+ size_t len;
+ struct grid_cell gc;
+
+ if (c->tty.sx == 0 || c->tty.sy == 0)
+ return (0);
+ memcpy(&old_status, &c->status, sizeof old_status);
+ screen_init(&c->status, c->tty.sx, 1, 0);
+
+ len = strlen(c->message_string);
+ if (len > c->tty.sx)
+ len = c->tty.sx;
+
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ gc.bg = options_get_number(&s->options, "message-fg");
+ gc.fg = options_get_number(&s->options, "message-bg");
+ gc.attr |= options_get_number(&s->options, "message-attr");
+
+ screen_write_start(&ctx, NULL, &c->status);
+
+ screen_write_cursormove(&ctx, 0, 0);
+ screen_write_puts(&ctx, &gc, "%.*s", (int) len, c->message_string);
+ for (; len < c->tty.sx; len++)
+ screen_write_putc(&ctx, &gc, ' ');
+
+ screen_write_stop(&ctx);
+
+ if (grid_compare(c->status.grid, old_status.grid) == 0) {
+ screen_free(&old_status);
+ return (0);
+ }
+ screen_free(&old_status);
+ return (1);
+}
+
+void
+status_prompt_set(struct client *c,
+ const char *msg, int (*fn)(void *, const char *), void *data, int flags)
+{
+ c->prompt_string = xstrdup(msg);
+
+ c->prompt_buffer = xstrdup("");
+ c->prompt_index = 0;
+
+ c->prompt_callback = fn;
+ c->prompt_data = data;
+
+ c->prompt_hindex = 0;
+
+ c->prompt_flags = flags;
+
+ mode_key_init(&c->prompt_mdata,
+ options_get_number(&c->session->options, "status-keys"),
+ MODEKEY_CANEDIT);
+
+ c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
+ c->flags |= CLIENT_STATUS;
+}
+
+void
+status_prompt_clear(struct client *c)
+{
+ if (c->prompt_string == NULL)
+ return;
+
+ mode_key_free(&c->prompt_mdata);
+
+ xfree(c->prompt_string);
+ c->prompt_string = NULL;
+
+ xfree(c->prompt_buffer);
+ c->prompt_buffer = NULL;
+
+ c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
+ c->flags |= CLIENT_REDRAW;
+}
+
+/* Draw client prompt on status line of present else on last line. */
+int
+status_prompt_redraw(struct client *c)
+{
+ struct screen_write_ctx ctx;
+ struct session *s = c->session;
+ struct screen old_status;
+ size_t i, size, left, len, offset, n;
+ char ch;
+ struct grid_cell gc;
+
+ if (c->tty.sx == 0 || c->tty.sy == 0)
+ return (0);
+ memcpy(&old_status, &c->status, sizeof old_status);
+ screen_init(&c->status, c->tty.sx, 1, 0);
+ offset = 0;
+
+ len = strlen(c->prompt_string);
+ if (len > c->tty.sx)
+ len = c->tty.sx;
+
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ gc.bg = options_get_number(&s->options, "message-fg");
+ gc.fg = options_get_number(&s->options, "message-bg");
+ gc.attr |= options_get_number(&s->options, "message-attr");
+
+ screen_write_start(&ctx, NULL, &c->status);
+
+ screen_write_cursormove(&ctx, 0, 0);
+ screen_write_puts(&ctx, &gc, "%.*s", (int) len, c->prompt_string);
+
+ left = c->tty.sx - len;
+ if (left != 0) {
+ if (c->prompt_index < left)
+ size = strlen(c->prompt_buffer);
+ else {
+ offset = c->prompt_index - left - 1;
+ if (c->prompt_index == strlen(c->prompt_buffer))
+ left--;
+ size = left;
+ }
+ if (c->prompt_flags & PROMPT_HIDDEN) {
+ n = strlen(c->prompt_buffer);
+ if (n > left)
+ n = left;
+ for (i = 0; i < n; i++)
+ screen_write_putc(&ctx, &gc, '*');
+ } else {
+ screen_write_puts(&ctx, &gc,
+ "%.*s", (int) left, c->prompt_buffer + offset);
+ }
+
+ for (i = len + size; i < c->tty.sx; i++)
+ screen_write_putc(&ctx, &gc, ' ');
+ }
+
+ /* Draw a fake cursor. */
+ screen_write_cursormove(&ctx, len + c->prompt_index - offset, 0);
+ if (c->prompt_index == strlen(c->prompt_buffer))
+ ch = ' ';
+ else {
+ if (c->prompt_flags & PROMPT_HIDDEN)
+ ch = '*';
+ else
+ ch = c->prompt_buffer[c->prompt_index];
+ }
+ if (ch == '\0')
+ ch = ' ';
+ gc.attr ^= GRID_ATTR_REVERSE;
+ screen_write_putc(&ctx, &gc, ch);
+
+ screen_write_stop(&ctx);
+
+ if (grid_compare(c->status.grid, old_status.grid) == 0) {
+ screen_free(&old_status);
+ return (0);
+ }
+ screen_free(&old_status);
+ return (1);
+}
+
+/* Handle keys in prompt. */
+void
+status_prompt_key(struct client *c, int key)
+{
+ struct paste_buffer *pb;
+ char *s, *first, *last, word[64];
+ size_t size, n, off, idx;
+
+ size = strlen(c->prompt_buffer);
+ switch (mode_key_lookup(&c->prompt_mdata, key)) {
+ case MODEKEYCMD_LEFT:
+ if (c->prompt_index > 0) {
+ c->prompt_index--;
+ c->flags |= CLIENT_STATUS;
+ }
+ break;
+ case MODEKEYCMD_RIGHT:
+ if (c->prompt_index < size) {
+ c->prompt_index++;
+ c->flags |= CLIENT_STATUS;
+ }
+ break;
+ case MODEKEYCMD_STARTOFLINE:
+ if (c->prompt_index != 0) {
+ c->prompt_index = 0;
+ c->flags |= CLIENT_STATUS;
+ }
+ break;
+ case MODEKEYCMD_ENDOFLINE:
+ if (c->prompt_index != size) {
+ c->prompt_index = size;
+ c->flags |= CLIENT_STATUS;
+ }
+ break;
+ case MODEKEYCMD_COMPLETE:
+ if (*c->prompt_buffer == '\0')
+ break;
+
+ idx = c->prompt_index;
+ if (idx != 0)
+ idx--;
+
+ /* Find the word we are in. */
+ first = c->prompt_buffer + idx;
+ while (first > c->prompt_buffer && *first != ' ')
+ first--;
+ while (*first == ' ')
+ first++;
+ last = c->prompt_buffer + idx;
+ while (*last != '\0' && *last != ' ')
+ last++;
+ while (*last == ' ')
+ last--;
+ if (*last != '\0')
+ last++;
+ if (last <= first ||
+ ((size_t) (last - first)) > (sizeof word) - 1)
+ break;
+ memcpy(word, first, last - first);
+ word[last - first] = '\0';
+
+ /* And try to complete it. */
+ if ((s = status_prompt_complete(word)) == NULL)
+ break;
+
+ /* Trim out word. */
+ n = size - (last - c->prompt_buffer) + 1; /* with \0 */
+ memmove(first, last, n);
+ size -= last - first;
+
+ /* Insert the new word. */
+ size += strlen(s);
+ off = first - c->prompt_buffer;
+ c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 1);
+ first = c->prompt_buffer + off;
+ memmove(first + strlen(s), first, n);
+ memcpy(first, s, strlen(s));
+
+ c->prompt_index = (first - c->prompt_buffer) + strlen(s);
+
+ c->flags |= CLIENT_STATUS;
+ break;
+ case MODEKEYCMD_BACKSPACE:
+ if (c->prompt_index != 0) {
+ if (c->prompt_index == size)
+ c->prompt_buffer[--c->prompt_index] = '\0';
+ else {
+ memmove(c->prompt_buffer + c->prompt_index - 1,
+ c->prompt_buffer + c->prompt_index,
+ size + 1 - c->prompt_index);
+ c->prompt_index--;
+ }
+ c->flags |= CLIENT_STATUS;
+ }
+ break;
+ case MODEKEYCMD_DELETE:
+ if (c->prompt_index != size) {
+ memmove(c->prompt_buffer + c->prompt_index,
+ c->prompt_buffer + c->prompt_index + 1,
+ size + 1 - c->prompt_index);
+ c->flags |= CLIENT_STATUS;
+ }
+ break;
+ case MODEKEYCMD_UP:
+ if (server_locked)
+ break;
+
+ if (ARRAY_LENGTH(&c->prompt_hdata) == 0)
+ break;
+ xfree(c->prompt_buffer);
+
+ c->prompt_buffer = xstrdup(ARRAY_ITEM(&c->prompt_hdata,
+ ARRAY_LENGTH(&c->prompt_hdata) - 1 - c->prompt_hindex));
+ if (c->prompt_hindex != ARRAY_LENGTH(&c->prompt_hdata) - 1)
+ c->prompt_hindex++;
+
+ c->prompt_index = strlen(c->prompt_buffer);
+ c->flags |= CLIENT_STATUS;
+ break;
+ case MODEKEYCMD_DOWN:
+ if (server_locked)
+ break;
+
+ xfree(c->prompt_buffer);
+
+ if (c->prompt_hindex != 0) {
+ c->prompt_hindex--;
+ c->prompt_buffer = xstrdup(ARRAY_ITEM(
+ &c->prompt_hdata, ARRAY_LENGTH(
+ &c->prompt_hdata) - 1 - c->prompt_hindex));
+ } else
+ c->prompt_buffer = xstrdup("");
+
+ c->prompt_index = strlen(c->prompt_buffer);
+ c->flags |= CLIENT_STATUS;
+ break;
+ case MODEKEYCMD_PASTE:
+ if ((pb = paste_get_top(&c->session->buffers)) == NULL)
+ break;
+ if ((last = strchr(pb->data, '\n')) == NULL)
+ last = strchr(pb->data, '\0');
+ n = last - pb->data;
+
+ c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + n + 1);
+ if (c->prompt_index == size) {
+ memcpy(c->prompt_buffer + c->prompt_index, pb->data, n);
+ c->prompt_index += n;
+ c->prompt_buffer[c->prompt_index] = '\0';
+ } else {
+ memmove(c->prompt_buffer + c->prompt_index + n,
+ c->prompt_buffer + c->prompt_index,
+ size + 1 - c->prompt_index);
+ memcpy(c->prompt_buffer + c->prompt_index, pb->data, n);
+ c->prompt_index += n;
+ }
+
+ c->flags |= CLIENT_STATUS;
+ break;
+ case MODEKEYCMD_CHOOSE:
+ if (*c->prompt_buffer != '\0') {
+ status_prompt_add_history(c);
+ if (c->prompt_callback(
+ c->prompt_data, c->prompt_buffer) == 0)
+ status_prompt_clear(c);
+ break;
+ }
+ /* FALLTHROUGH */
+ case MODEKEYCMD_QUIT:
+ if (c->prompt_callback(c->prompt_data, NULL) == 0)
+ status_prompt_clear(c);
+ break;
+ case MODEKEYCMD_OTHERKEY:
+ if (key < 32 || key > 126)
+ break;
+ c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 2);
+
+ if (c->prompt_index == size) {
+ c->prompt_buffer[c->prompt_index++] = key;
+ c->prompt_buffer[c->prompt_index] = '\0';
+ } else {
+ memmove(c->prompt_buffer + c->prompt_index + 1,
+ c->prompt_buffer + c->prompt_index,
+ size + 1 - c->prompt_index);
+ c->prompt_buffer[c->prompt_index++] = key;
+ }
+
+ if (c->prompt_flags & PROMPT_SINGLE) {
+ if (c->prompt_callback(
+ c->prompt_data, c->prompt_buffer) == 0)
+ status_prompt_clear(c);
+ }
+
+ c->flags |= CLIENT_STATUS;
+ break;
+ default:
+ break;
+ }
+}
+
+/* Add line to the history. */
+void
+status_prompt_add_history(struct client *c)
+{
+ if (server_locked)
+ return;
+
+ if (ARRAY_LENGTH(&c->prompt_hdata) > 0 &&
+ strcmp(ARRAY_LAST(&c->prompt_hdata), c->prompt_buffer) == 0)
+ return;
+
+ if (ARRAY_LENGTH(&c->prompt_hdata) == PROMPT_HISTORY) {
+ xfree(ARRAY_FIRST(&c->prompt_hdata));
+ ARRAY_REMOVE(&c->prompt_hdata, 0);
+ }
+
+ ARRAY_ADD(&c->prompt_hdata, xstrdup(c->prompt_buffer));
+}
+
+/* Complete word. */
+char *
+status_prompt_complete(const char *s)
+{
+ const struct cmd_entry **cmdent;
+ const struct set_option_entry *optent;
+ ARRAY_DECL(, const char *) list;
+ char *prefix, *s2;
+ u_int i;
+ size_t j;
+
+ if (*s == '\0')
+ return (NULL);
+
+ /* First, build a list of all the possible matches. */
+ ARRAY_INIT(&list);
+ for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
+ if (strncmp((*cmdent)->name, s, strlen(s)) == 0)
+ ARRAY_ADD(&list, (*cmdent)->name);
+ }
+ for (i = 0; i < NSETOPTION; i++) {
+ optent = &set_option_table[i];
+ if (strncmp(optent->name, s, strlen(s)) == 0)
+ ARRAY_ADD(&list, optent->name);
+ }
+ for (i = 0; i < NSETWINDOWOPTION; i++) {
+ optent = &set_window_option_table[i];
+ if (strncmp(optent->name, s, strlen(s)) == 0)
+ ARRAY_ADD(&list, optent->name);
+ }
+
+ /* If none, bail now. */
+ if (ARRAY_LENGTH(&list) == 0) {
+ ARRAY_FREE(&list);
+ return (NULL);
+ }
+
+ /* If an exact match, return it, with a trailing space. */
+ if (ARRAY_LENGTH(&list) == 1) {
+ xasprintf(&s2, "%s ", ARRAY_FIRST(&list));
+ ARRAY_FREE(&list);
+ return (s2);
+ }
+
+ /* Now loop through the list and find the longest common prefix. */
+ prefix = xstrdup(ARRAY_FIRST(&list));
+ for (i = 1; i < ARRAY_LENGTH(&list); i++) {
+ s = ARRAY_ITEM(&list, i);
+
+ j = strlen(s);
+ if (j > strlen(prefix))
+ j = strlen(prefix);
+ for (; j > 0; j--) {
+ if (prefix[j - 1] != s[j - 1])
+ prefix[j - 1] = '\0';
+ }
+ }
+
+ ARRAY_FREE(&list);
+ return (prefix);
+}
diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1
new file mode 100644
index 00000000000..5fd91427a07
--- /dev/null
+++ b/usr.bin/tmux/tmux.1
@@ -0,0 +1,1329 @@
+.\" $OpenBSD: tmux.1,v 1.1 2009/06/01 22:58:49 nicm Exp $
+.\"
+.\" Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+.\"
+.\" 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.
+.\"
+.Dd April 20, 2009
+.Dt TMUX 1
+.Os
+.Sh NAME
+.Nm tmux
+.Nd "terminal multiplexer"
+.Sh SYNOPSIS
+.Nm tmux
+.Bk -words
+.Op Fl 28dqUuv
+.Op Fl f Ar file
+.Op Fl L Ar socket-name
+.Op Fl S Ar socket-path
+.Op Ar command Op Ar flags
+.Ek
+.Sh DESCRIPTION
+.Nm
+is a terminal multiplexer; it enables a number of terminals to be accessed and
+controlled from a single terminal.
+.Pp
+.Nm
+runs as a server-client system.
+A server is created automatically when necessary and holds a number of
+.Em sessions ,
+each of which may have a number of
+.Em windows
+linked to it.
+A window may be split on screen into one or more
+.Em panes ,
+each of which is a separate terminal.
+Any number of
+.Em clients
+may connect to a session, or the server
+may be controlled by issuing commands with
+.Nm .
+Communication takes place through a socket, by default placed in
+.Pa /tmp .
+.Pp
+The options are as follows:
+.Bl -tag -width "XXXXXXXXXXXX"
+.It Fl 2
+Force
+.Nm
+to assume the terminal supports 256 colours.
+.It Fl 8
+Like
+.Fl 2 ,
+indicates the terminal supports 88 colours.
+.It Fl d
+Force
+.Nm
+to assume the terminal supports default colours.
+.It Fl f Ar file
+Specify an alternative configuration file.
+By default,
+.Nm
+will look for a config file at
+.Pa ~/.tmux.conf .
+The configuration file is a set of
+.Nm
+commands which are executed in sequence when the server is first started.
+.It Fl q
+Prevent the server sending various information messages, for example when
+window flags are altered.
+.It Fl L Ar socket-name
+.Nm
+stores the server socket in a directory under
+.Pa /tmp ;
+the default socket is named
+.Em default .
+This option allows a different socket name to be specified, allowing several
+independent
+.Nm
+servers to be run.
+Unlike
+.Fl S
+a full path is not necessary: the sockets are all created in the same
+directory.
+.It Fl S Ar socket-path
+Specify a full alternative path to the server socket.
+If
+.Fl S
+is specified, the default socket directory is not used and any
+.Fl L
+flag is ignored.
+.It Fl U
+Unlock the server.
+.It Fl u
+Instruct
+.Nm
+that the terminal support UTF-8.
+.It Fl v
+Request verbose logging.
+This option may be specified multiple times for increasing verbosity.
+Log messages will be saved into
+.Pa tmux-client-PID.log
+and
+.Pa tmux-server-PID.log
+files in the current directory, where
+.Em PID
+is the pid of the server or client process.
+.It Ar command Op Ar flags
+This specifies one of a set of commands used to control
+.Nm ,
+and described in the following sections.
+If no command and flags is specified, the
+.Ic new-session
+command is assumed.
+.Pp
+.El
+.Sh QUICK START
+To create a new tmux session running
+.Xr vi 1 :
+.Pp
+.Dl $ tmux new-session vi
+.Pp
+Most commands have a shorter form, known as an alias.
+For new-session, this is
+.Ic new :
+.Pp
+.Dl $ tmux new vi
+.Pp
+Alternatively, the shortest unambiguous form of a command is accepted.
+If there are several options, they are listed:
+.Bd -literal -offset indent
+$ tmux n
+ambiguous command: n, could be: new-session, new-window, next-window
+$
+.Ed
+.Pp
+Within an active session, a new window may be created by typing
+.Ql C-b
+(ctrl-b, known as the prefix key)
+followed by the
+.Ql c
+key.
+.Pp
+Windows may be navigated with:
+.Ql C-b 0
+(to select window 0),
+.Ql C-b 1
+(to select window 1), and so on;
+.Ql C-b n
+to select the next window; and
+.Ql C-b p
+to select the previous window.
+.Pp
+A session may be detached using
+.Ql C-b d
+and reattached with:
+.Pp
+.Dl $ tmux attach-session
+.Pp
+Typing
+.Ql C-b \&?
+lists the current key bindings in the current window; up and down may be used
+to navigate the list or
+.Ql Q
+to exit from it.
+.Sh KEY BINDINGS
+.Nm
+may be controlled from an attached client by using a key combination of a
+prefix key,
+.Ql C-b
+(ctrl-b) by default, followed by a command key.
+.Pp
+Some of the default key bindings include:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ql d
+Detach current client.
+.It Ql c
+Create new window.
+.It Ql n
+Change to next window in the current session.
+.It Ql p
+Change to previous window in the current session.
+.It Ql l
+Move to last (previously selected) window in the current session.
+.It Ql t
+Display a large clock.
+.It Ql \&?
+List current key bindings.
+.El
+.Pp
+A complete list may be obtained with the
+.Ic list-keys
+command (bound to
+.Ql \&?
+by default).
+Key bindings may be changed with the
+.Ic bind-key
+and
+.Ic unbind-key
+commands.
+.Sh HISTORY
+.Nm
+maintains a configurable history buffer for each window.
+By default, up to 2000 lines are kept, this can be altered with the
+.Ic history-limit
+option (see the
+.Ic set-option
+command below).
+.Sh MODES
+A
+.Nm
+window may be in one of several modes.
+The default permits direct access to the terminal attached to the window.
+The others are:
+.Bl -tag -width Ds
+.It Em output mode
+This is entered when a command which produces output, such as
+.Ic list-keys ,
+is executed from a key binding.
+.It Em scroll mode
+This is entered with the
+.Ic scroll-mode
+command (bound to
+.Ql =
+by default) and permits the window history buffer to be inspected.
+.It Em copy mode
+This permits a section of a window or its history to be copied to a
+.Em paste buffer
+for later insertion into another window.
+This mode is entered with the
+.Ic copy-mode
+command, bound to
+.Ql [
+by default.
+.El
+.Pp
+The keys available depend on whether
+.Xr emacs 1
+or
+.Xr vi 1
+mode is selected (see the
+.Ic mode-keys
+option).
+The following keys are supported as appropriate for the mode:
+.Bl -column "FunctionXXXXXXXXXXXX" "viXXXXXX" "emacs" -offset indent
+.It Sy "Function" Ta Sy "vi" Ta Sy "emacs"
+.It Li "Start of line" Ta "0 or ^" Ta "C-a"
+.It Li "Clear selection" Ta "Escape" Ta "C-g"
+.It Li "Copy selection" Ta "Enter" Ta "M-w"
+.It Li "Cursor down" Ta "j" Ta "Down"
+.It Li "End of line" Ta "$" Ta "C-e"
+.It Li "Cursor left" Ta "h" Ta "Left"
+.It Li "Next page" Ta "C-f" Ta "Page down"
+.It Li "Next word" Ta "w" Ta "M-f"
+.It Li "Previous page" Ta "C-u" Ta "Page up"
+.It Li "Previous word" Ta "b" Ta "M-b"
+.It Li "Quit mode" Ta "q" Ta "Escape"
+.It Li "Cursor right" Ta "l" Ta "Right"
+.It Li "Start selection" Ta "Space" Ta "C-Space"
+.It Li "Cursor up" Ta "k" Ta "Up"
+.El
+.Pp
+.Sh BUFFERS
+.Nm
+maintains a stack of
+.Em paste buffers
+for each session.
+Up to the value of the
+.Ic buffer-limit
+option are kept; when a new buffer is added, the buffer at the bottom of the
+stack is removed.
+Buffers may be added using
+.Ic copy-mode
+or the
+.Ic set-buffer
+command, and pasted into a window using the
+.Ic paste-buffer
+command.
+.Sh PANES AND LAYOUTS
+Each window displayed by
+.Nm
+may be split into one or more
+.Em panes ;
+each pane takes up a certain area of the display and is a separate terminal.
+A window may be split into panes using the
+.Ic split-window
+command.
+.Pp
+Panes are numbered beginning from zero; in horizontal layouts zero is the
+leftmost pane and in vertical the topmost.
+.Pp
+Panes may be arranged using several layouts.
+The layout may be cycled with the
+.Ic next-layout
+command (bound to
+.Ql C-space
+by default), the current pane may be changed with the
+.Ic up-pane
+and
+.Ic down-pane
+commands and the
+.Ic rotate-window
+and
+.Ic swap-pane
+commands may be used to swap panes without changing the window layout.
+.Pp
+The following layouts are supported:
+.Bl -tag -width Ds
+.It Ic manual
+Manual layout splits windows vertically (running across); only with this layout
+may panes be resized using the
+.Ic resize-pane
+command.
+.It Ic active-only
+Only the active pane is shown - all other panes are hidden.
+.It Ic even-horizontal
+Panes are spread out evenly from left to right across the window.
+.It Ic even-vertical
+Panes are spread evenly from top to bottom.
+.It Ic main-vertical
+A large (81 column) pane is shown on the left of the window and the remaining
+panes are spread from top to bottom in the leftover space to the right.
+.El
+.Sh COMMANDS
+This section contains a list of the commands supported by
+.Nm .
+Most commands accept the optional
+.Fl t
+argument with one of
+.Ar target-client ,
+.Ar target-session
+or
+.Ar target-window .
+These specify the client, session or window which a command should affect.
+.Ar target-client
+is the name of the
+.Xr pty 4
+file to which the client is connected, for example
+.Pa /dev/ttyp1 .
+Clients may be listed with the
+.Ic list-clients
+command.
+.Pp
+.Ar target-session
+is either the name of a session (as listed by the
+.Ic list-sessions
+command); or the name of a client as for
+.Ar target-client ,
+in this case, the session attached to the client is used.
+An
+.Xr fnmatch 3
+pattern may be used to match the session name.
+If a session is omitted when required,
+.Nm tmux
+attempts to use the current session; if no current session is available, the
+most recently created is chosen.
+If no client is specified, the current client is chosen, if possible, or an
+error is reported.
+.Pp
+.Ar target-window
+specifies a window in the form
+.Em session Ns \&: Ns Em index ,
+for example mysession:1.
+The session is in the same form as for
+.Ar target-session .
+.Em session ,
+.Em index
+or both may be omitted.
+If
+.Em session
+is omitted, the same rules as for
+.Ar target-session
+are followed; if
+.Em index
+is not present, the current window for the given session is used.
+When the argument does not contain a colon (:),
+.Nm
+first attempts to parse it as window index; if that fails, an attempt is made
+to match a session or client name.
+.Pp
+Multiple commands may be specified together as part of a
+.Em command sequence .
+Each command should be separated by spaces and a semicolon
+.Eo ( Ql \& \&; \& Ec ) ;
+commands are executed sequentially from left to right.
+A literal semicolon may be included by escaping it with a backslash (for
+example, when specifying a command sequence to
+.Ic bind-key ) .
+.Pp
+Examples include:
+.Pp
+.Bd -literal -offset indent
+refresh-client -t/dev/ttyp2
+
+rename-session -tfirst newname
+
+set-window-option -t:0 monitor-activity on
+
+new-window ; split-window -d
+
+bind-key D detach-client \e\; lock-server
+.Ed
+.Pp
+The following commands are available:
+.Bl -tag -width Ds
+.It Xo Ic attach-session
+.Op Fl d
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic attach )
+Create a new client in the current terminal and attach it to a session.
+If
+.Fl d
+is specified, any other clients attached to the session are detached.
+.Pp
+If no server is started,
+.Ic attach-session
+will attempt to start it; this will fail unless sessions are created in the
+configuration file.
+.It Xo Ic bind-key
+.Op Fl r
+.Ar key Ar command Op Ar arguments
+.Xc
+.D1 (alias: Ic bind )
+Bind key
+.Ar key
+to
+.Ar command .
+Keys may be specified prefixed with
+.Ql C-
+or
+.Ql ^
+for ctrl keys, or
+.Ql M-
+for alt (meta) keys.
+The
+.Fl r
+flag indicates this key may repeat, see the
+.Ic repeat-time
+option.
+.It Xo Ic break-pane
+.Op Fl d
+.Op Fl p Ar pane-index
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic breakp)
+Break the current pane off from its containing window to make it the only pane
+in a new window.
+If
+.Fl d
+is given, the new window does not become the current window.
+.It Xo Ic choose-session
+.Op Fl t Ar target-window
+.Xc
+Put a window into session choice mode, where the session for the current
+client may be selected interactively from a list.
+This command works only from inside
+.Nm .
+.It Xo Ic choose-window
+.Op Fl t Ar target-window
+.Xc
+Put a window into window choice mode, where the window for the session
+attached to the current client may be selected interactively from a list.
+This command works only from inside
+.Nm .
+.It Xo Ic clock-mode
+.Op Fl t Ar target-window
+.Xc
+Display a large clock.
+.It Xo Ic command-prompt
+.Op Fl t Ar target-client
+.Op Ar template
+.Xc
+Open the command prompt in a client.
+This may be used from inside
+.Nm
+to execute commands interactively.
+If
+.Ar template
+is specified, it is used as the command; any %% in the template will be
+replaced by what is entered at the prompt.
+.It Xo Ic confirm-before
+.Op Fl t Ar target-client
+.Ar command
+.Xc
+.D1 (alias: Ic confirm)
+Ask for confirmation before executing
+.Ar command .
+This command works only from inside
+.Nm .
+.It Xo Ic copy-buffer
+.Op Fl a Ar src-index
+.Op Fl b Ar dst-index
+.Op Fl s Ar src-session
+.Op Fl t Ar dst-session
+.Xc
+.D1 (alias: Ic copyb)
+Copy a session paste buffer to another session.
+If no sessions are specified, the current one is used instead.
+.It Xo Ic copy-mode
+.Op Fl u
+.Op Fl t Ar target-window
+.Xc
+Enter copy mode.
+The
+.Fl u
+option scrolls one page up.
+.It Xo Ic delete-buffer
+.Op Fl b Ar buffer-index
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic deleteb )
+Delete the buffer at
+.Ar buffer-index ,
+or the top buffer if not specified.
+.It Xo Ic detach-client
+.Op Fl t Ar target-client
+.Xc
+.D1 (alias: Ic detach )
+Detach the current client if bound to a key, or the specified client with
+.Fl t .
+.It Xo Ic down-pane
+.Op Fl p Ar pane-index
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic downp )
+Move down a pane.
+.It Xo Ic find-window
+.Op Fl t Ar target-window
+.Ar match-string
+.Xc
+.D1 (alias: Ic findw )
+Search for
+.Ar match-string
+in window names, titles, and visible content (but not history).
+If only one window is matched, it'll be automatically selected, otherwise a
+choice list is shown.
+This command only works from inside
+.Nm .
+.It Xo Ic has-session
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic has )
+Report an error and exit with 1 if the specified session does not exist.
+If it does exist, exit with 0.
+.It Xo Ic kill-pane
+.Op Fl p Ar pane-index
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic killp )
+Destroy the given pane.
+.It Xo Ic kill-server
+.Xc
+Kill the
+.Nm
+server and clients and destroy all sessions.
+.It Xo Ic kill-session
+.Op Fl t Ar target-session
+.Xc
+Destroy the given session, closing any windows linked to it and no other
+sessions, and detaching all clients attached to it.
+.It Xo Ic kill-window
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic killw )
+Kill the current window or the window at
+.Ar target-window ,
+removing it from any sessions to which it is linked.
+.It Xo Ic last-window
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic last )
+Select the last (previously selected) window.
+If no
+.Ar target-session
+is specified, select the last window of the current session.
+.It Xo Ic link-window
+.Op Fl dk
+.Op Fl s Ar src-window
+.Op Fl t Ar dst-window
+.Xc
+.D1 (alias: Ic linkw )
+Link the window at
+.Ar src-window
+to the specified
+.Ar dst-window .
+If
+.Ar dst-window
+is specified and no such window exists, the
+.Ar src-window
+is linked there.
+If
+.Fl k
+is given and
+.Ar dst-window
+exists, it is killed, otherwise an error is generated.
+If
+.Fl d
+is given, the newly linked window is not selected.
+.It Xo Ic list-buffers
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic lsb )
+List the buffers in the given session.
+.It Xo Ic list-clients
+.Xc
+.D1 (alias: Ic lsc )
+List all clients attached to the server.
+.It Xo Ic list-commands
+.Xc
+.D1 (alias: Ic lscm )
+List the syntax of all commands supported by
+.Nm .
+.It Xo Ic list-keys
+.Xc
+.D1 (alias: Ic lsk )
+List all key bindings.
+.It Xo Ic list-sessions
+.Xc
+.D1 (alias: Ic ls )
+List all sessions managed by the server.
+.It Xo Ic list-windows
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic lsw )
+List windows in the current session or in
+.Ar target-session .
+.It Xo Ic load-buffer
+.Op Fl b Ar buffer-index
+.Op Fl t Ar target-session
+.Ar path
+.Xc
+.D1 (alias: Ic loadb )
+Load the contents of the specified paste buffer from
+.Ar path .
+.It Xo Ic lock-server
+.Xc
+.D1 (alias: Ic lock )
+Lock the server until a password is entered.
+.It Xo Ic move-window
+.Op Fl d
+.Op Fl s Ar src-window
+.Op Fl t Ar dst-window
+.Xc
+.D1 (alias: Ic movew )
+This is similar to
+.Ic link-window ,
+except the window at
+.Ar src-window
+is moved to
+.Ar dst-window .
+.It Xo Ic new-session
+.Op Fl d
+.Op Fl n Ar window-name
+.Op Fl s Ar session-name
+.Op Ar command
+.Xc
+.D1 (alias: Ic new )
+Create a new session with name
+.Ar session-name .
+The new session is attached to the current terminal unless
+.Fl d
+is given.
+.Ar window-name
+and
+.Ar command
+are the name of and command to execute in the initial window.
+.It Xo Ic new-window
+.Op Fl d
+.Op Fl n Ar window-name
+.Op Fl t Ar target-window
+.Op Ar command
+.Xc
+.D1 (alias: Ic neww )
+Create a new window.
+If
+.Fl d
+is given, the session does not make the new window the current window.
+.Ar target-window
+represents the window to be created.
+.Ar command
+is the command to execute.
+If
+.Ar command
+is not specified, the default command is used.
+.Pp
+The
+.Ev TERM
+environment variable must be set to
+.Dq screen
+for all programs running
+.Em inside
+.Nm .
+New windows will automatically have
+.Dq TERM=screen
+added to their environment, but care must be taken not to reset this in shell
+start-up files.
+.It Xo Ic next-layout
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic nextl )
+Move a window to the next layout and rearrange the panes to fit.
+.It Xo Ic next-window
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic next )
+Move to the next window in the session.
+.It Xo Ic paste-buffer
+.Op Fl d
+.Op Fl b Ar buffer-index
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic pasteb )
+Insert the contents of a paste buffer into the current window.
+.It Xo Ic previous-window
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic prev )
+Move to the previous window in the session.
+.It Xo Ic refresh-client
+.Op Fl t Ar target-client
+.Xc
+.D1 (alias: Ic refresh )
+Refresh the current client if bound to a key, or a single client if one is given
+with
+.Fl t .
+.It Xo Ic rename-session
+.Op Fl t Ar target-session
+.Ar new-name
+.Xc
+.D1 (alias: Ic rename )
+Rename the session to
+.Ar new-name .
+.It Xo Ic rename-window
+.Op Fl t Ar target-window
+.Ar new-name
+.Xc
+.D1 (alias: Ic renamew )
+Rename the current window, or the window at
+.Ar target-window
+if specified, to
+.Ar new-name .
+.It Xo Ic resize-pane
+.Op Fl DU
+.Op Fl p Ar pane-index
+.Op Fl t Ar target-window
+.Op Ar adjustment
+.Xc
+.D1 (alias: Ic resizep )
+Resize a pane, upward with
+.Fl U
+(the default) or downward with
+.Fl D .
+The
+.Ar adjustment
+is given in lines (the default is 1).
+.It Xo Ic respawn-window
+.Op Fl k
+.Op Fl t Ar target-window
+.Op Ar command
+.Xc
+.D1 (alias: Ic respawnw )
+Reactive a window in which the command has exited (see the
+.Ic remain-on-exit
+window option).
+If
+.Ar command
+is not given, the command used when the window was created is executed.
+The window must be already inactive, unless
+.Fl k
+is given, in which case any existing command is killed.
+.It Xo Ic rotate-window
+.Op Fl DU
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic rotatew )
+Rotate the positions of the panes within a window, either upward (numerically
+lower) with
+.Fl U
+or downward (numerically higher).
+.It Xo Ic save-buffer
+.Op Fl a
+.Op Fl b Ar buffer-index
+.Op Fl t Ar target-session
+.Ar path
+.Xc
+.D1 (alias: Ic saveb )
+Save the contents of the specified paste buffer to
+.Ar path .
+The
+.Fl a
+option appends to rather than overwriting the file.
+.It Xo Ic scroll-mode
+.Op Fl u
+.Op Fl t Ar target-window
+.Xc
+Enter scroll mode.
+The
+.Fl u
+has the same meaning as in the
+.Ic copy-mode
+command.
+.It Xo Ic select-pane
+.Op Fl p Ar pane-index
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic selectp )
+Make pane
+.Ar pane-index
+the active pane in window
+.Ar target-window .
+.It Xo Ic select-prompt
+.Op Fl t Ar target-client
+.Xc
+Open a prompt inside
+.Ar target-client
+allowing a window index to be entered interactively.
+.It Xo Ic select-window
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic selectw )
+Select the window at
+.Ar target-window .
+.It Xo Ic send-keys
+.Op Fl t Ar target-window
+.Ar key Ar ...
+.Xc
+.D1 (alias: Ic send )
+Send a key or keys to a window.
+Each argument
+.Ar key
+is the name of the key (such as
+.Ql C-a
+or
+.Ql npage
+) to send; if the string is not recognised as a key, it is sent as a series of
+characters.
+All arguments are sent sequentially from first to last.
+.It Xo Ic send-prefix
+.Op Fl t Ar target-window
+.Xc
+Send the prefix key to a window as if it was pressed.
+.It Xo Ic server-info
+.Xc
+.D1 (alias: Ic info )
+Show server information and terminal details.
+.It Xo Ic set-buffer
+.Op Fl b Ar buffer-index
+.Op Fl t Ar target-session
+.Ar data
+.Xc
+.D1 (alias: Ic setb )
+Set the contents of the specified buffer to
+.Ar data .
+.It Xo Ic set-option
+.Op Fl gu
+.Op Fl t Ar target-session
+.Ar option Ar value
+.Xc
+.D1 (alias: Ic set )
+Set an option.
+If
+.Fl g
+is specified, the option is set as a global option.
+Global options apply to all sessions which don't have the option explicitly
+set.
+If
+.Fl g
+is not used, the option applies only to
+.Ar target-session .
+The
+.Fl u
+flag unsets an option, so a session inherits the option from the global
+options - it is not possible to unset a global option.
+.Pp
+Possible options are:
+.Bl -tag -width Ds
+.It Xo Ic bell-action
+.Op Ic any | Ic none | Ic current
+.Xc
+Set action on window bell.
+.Ic any
+means a bell in any window linked to a session causes a bell in the current
+window of that session,
+.Ic none
+means all bells are ignored and
+.Ic current
+means only bell in windows other than the current window are ignored.
+.It Ic buffer-limit Ar number
+Set the number of buffers kept for each session; as new buffers are added to
+the top of the stack, old ones are removed from the bottom if necessary to
+maintain this maximum length.
+.It Ic default-command Ar command
+Set the command used for new windows (if not specified when the window is
+created) to
+.Ar command .
+The default is
+.Dq exec $SHELL .
+.It Ic default-path Ar path
+Set the default working directory for processes created from keys, or
+interactively from the prompt.
+The default is the current working directory when the server is started.
+.It Ic history-limit Ar lines
+Set the maximum number of lines held in window history.
+This setting applies only to new windows - existing window histories are not
+resized and retain the limit at the point they were created.
+.It Ic lock-after-time Ar number
+Lock the server after
+.Ar number
+seconds of inactivity.
+The default is off (set to 0).
+This has no effect as a session option; it must be set as a global option using
+.Fl g .
+.It Ic message-attr Ar attributes
+Set status line message attributes, where
+.Ar attributes
+is either
+.Ic default
+or a comma-delimited list of one or more of:
+.Ic bright
+(or
+.Ic bold ) ,
+.Ic dim ,
+.Ic underscore ,
+.Ic blink ,
+.Ic reverse ,
+.Ic hidden ,
+or
+.Ic italics .
+.It Ic message-bg Ar colour
+Set status line message background colour, where
+.Ar colour
+is one of:
+.Ic black ,
+.Ic red ,
+.Ic green ,
+.Ic yellow ,
+.Ic blue ,
+.Ic magenta ,
+.Ic cyan ,
+.Ic white
+or
+.Ic default .
+.It Ic message-fg Ar colour
+Set status line message foreground colour.
+.It Ic prefix Ar key
+Set the current prefix key.
+.It Ic repeat-time Ar number
+Allow multiple commands to be entered without pressing the prefix-key again
+in the specified
+.Ar number
+milliseconds (the default is 500).
+Whether a key repeats may be set when it is bound using the
+.Fl r
+flag to
+.Ic bind-key .
+Repeat is enabled for the default keys of the
+.Ic up-pane ,
+.Ic down-pane ,
+.Ic resize-pane-up ,
+and
+.Ic resize-pane-down
+commands.
+.It Xo Ic set-remain-on-exit
+.Op Ic on | Ic off
+.Xc
+Set the
+.Ic remain-on-exit
+window option for any windows first created in this session.
+.It Xo Ic set-titles
+.Op Ic on | Ic off
+.Xc
+Attempt to set the window title using the \ee]2;...\e007 xterm code and
+the terminal appears to be an xterm.
+This option is enabled by default.
+Note that
+.Xr elinks 1
+will only attempt to set the window title if the STY environment
+variable is set.
+.It Xo Ic status
+.Op Ic on | Ic off
+.Xc
+Show or hide the status line.
+.It Ic status-attr Ar attributes
+Set status line attributes.
+.It Ic status-bg Ar colour
+Set status line background colour.
+.It Ic status-fg Ar colour
+Set status line foreground colour.
+.It Ic status-interval Ar interval
+Update the status bar every
+.Ar interval
+seconds.
+By default, updates will occur every 15 seconds.
+A setting of zero disables redrawing at interval.
+.It Xo Ic status-keys
+.Op Ic vi | Ic emacs
+.Xc
+Use
+.Xr vi 1 -
+or
+.Xr emacs 1 -style
+key bindings in the status line, for example at the command prompt.
+Defaults to emacs.
+.It Ic status-left Ar string
+Display
+.Ar string
+to the left of the status bar.
+.Ar string
+will be passed through
+.Xr strftime 3
+before being used.
+By default, the session name is shown.
+.Ar string
+may contain any of the following special character pairs:
+.Bl -column "Character pair" "Replaced with" -offset indent
+.It Sy "Character pair" Ta Sy "Replaced with"
+.It Li "#(command)" Ta "First line of command's output"
+.It Li "#H" Ta "Hostname of local host"
+.It Li "#S" Ta "Session name"
+.It Li "#T" Ta "Current window title"
+.It Li "##" Ta "A literal" Ql #
+.El
+.Pp
+Where appropriate, these may be prefixed with a number to specify the maximum
+length, for example
+.Ql #24T .
+.It Ic status-left-length Ar length
+Set the maximum
+.Ar length
+of the left component of the status bar.
+The default is 10.
+.It Ic status-right Ar string
+Display
+.Ar string
+to the right of the status bar.
+By default, the date and time will be shown.
+As with
+.Ic status-left ,
+.Ar string
+will be passed to
+.Xr strftime 3
+and character pairs are replaced.
+.It Ic status-right-length Ar length
+Set the maximum
+.Ar length
+of the right component of the status bar.
+The default is 40.
+.El
+.It Xo Ic set-password
+.Op Fl c
+.Ar password
+.Xc
+.D1 (alias: Ic pass )
+Set the server password.
+If the
+.Fl c
+option is given, a pre-encrypted password may be specified.
+By default, the password is blank, thus any entered password will be accepted
+when unlocking the server (see the
+.Ic lock-server
+command).
+To prevent variable expansion when an encrypted password is read from a
+configuration file, enclose it in single quotes (').
+.It Xo Ic set-window-option
+.Op Fl gu
+.Op Fl t Ar target-window
+.Ar option Ar value
+.Xc
+.D1 (alias: Ic setw )
+Set a window-specific option.
+The
+.Fl g
+and
+.Fl u
+flags work similarly to the
+.Ic set-option
+command.
+.Pp
+Supported options are:
+.Bl -tag -width Ds
+.It Xo Ic aggressive-resize
+.Op Ic on | Ic off
+.Xc
+Aggressively resize the chosen window.
+This means that
+.Nm
+will resize the window to the size of the smallest session for which it is the
+current window, rather than the smallest session to which it is attached.
+The window may resize when the current window is changed on another sessions;
+this option is good for full-screen programs which support SIGWINCH and poor for
+interactive programs such as shells.
+.It Xo Ic automatic-rename
+.Op Ic on | Ic off
+.Xc
+Control automatic window renaming.
+When this setting is enabled,
+.Nm
+will attempt - on supported platforms - to rename the window to reflect the
+command currently running in it.
+This flag is automatically disabled for an individual window when a name
+is specified at creation with
+.Ic new-window or
+.Ic new-session ,
+or later with
+.Ic rename-window .
+It may be switched off globally with:
+.Bd -literal -offset indent
+set-window-option -g automatic-rename off
+.Ed
+.It Ic clock-mode-colour Ar colour
+Set clock colour.
+.It Xo Ic clock-mode-style
+.Op Ic 12 | Ic 24
+.Xc
+Set clock hour format.
+.It Ic force-height Ar height
+.It Ic force-width Ar width
+Prevent
+.Nm
+from resizing a window to greater than
+.Ar width
+or
+.Ar height .
+A value of zero restores the default unlimited setting.
+.It Ic mode-attr Ar attributes
+Set window modes attributes.
+.It Ic mode-bg Ar colour
+Set window modes background colour.
+.It Ic mode-fg Ar colour
+Set window modes foreground colour.
+.It Xo Ic mode-keys
+.Op Ic vi | Ic emacs
+.Xc
+Use
+.Xr vi 1 -
+or
+.Xr emacs 1 -style
+key bindings in scroll and copy modes.
+Key bindings default to emacs.
+.It Xo Ic monitor-activity
+.Op Ic on | Ic off
+.Xc
+Monitor for activity in the window.
+Windows with activity are highlighted in the status line.
+.It Xo Ic monitor-content Ar match-string
+.Xc
+Monitor content in the window. When
+.Ar match-string
+appears in the window, it is highlighted in the status line.
+.It Xo Ic remain-on-exit
+.Op Ic on | Ic off
+.Xc
+A window with this flag set is not destroyed when the program running in it
+exits.
+The window may be reactivated with the
+.Ic respawn-window
+command.
+.It Xo Ic utf8
+.Op Ic on | Ic off
+.Xc
+Instructs
+.Nm
+to expect UTF-8 sequences to appear in this window.
+.It Ic window-status-attr Ar attributes
+Set status line attributes for a single window.
+.It Ic window-status-bg Ar colour
+Set status line background colour for a single window.
+.It Ic window-status-fg Ar colour
+Set status line foreground colour for a single window.
+.It Xo Ic xterm-keys
+.Op Ic on | Ic off
+.Xc
+If this option is set,
+.Nm
+will generate
+.Xr xterm 1 -style
+function key sequences; these have a number included to indicate modifiers such
+as shift, meta or ctrl.
+.El
+.It Xo Ic show-buffer
+.Op Fl b Ar buffer-index
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic showb )
+Display the contents of the specified buffer.
+.It Xo Ic show-options
+.Op Fl t Ar target-session
+.Ar option Ar value
+.Xc
+.D1 (alias: Ic show )
+Show the currently set options.
+If a
+.Ar target-session
+is specified, the options for that session are shown; otherwise, the global
+options are listed.
+.It Xo Ic show-window-options
+.Op Fl t Ar target-window
+.Ar option Ar value
+.Xc
+.D1 (alias: Ic showw )
+List the current options for the given window.
+.It Xo Ic source-file
+.Ar path
+.Xc
+.D1 (alias: Ic source )
+Execute commands from
+.Ar path .
+.It Xo Ic split-window
+.Op Fl d
+.Oo Fl l
+.Ar lines |
+.Fl p Ar percentage Oc
+.Op Fl t Ar target-window
+.Op Ar command
+.Xc
+.D1 (alias: splitw )
+Creates a new window by splitting it vertically.
+The
+.Fl l
+and
+.Fl p
+options specify the size of the new window in lines, or as a percentage,
+respectively.
+All other options have the same meaning as in the
+.Ic new-window
+command.
+.Pp
+A few notes with regard to panes:
+.Bl -enum -compact
+.It
+If attempting to split a window with less than eight lines, an error will be
+shown.
+.It
+If the window is resized, as many panes are shown as can fit without reducing
+them below four lines.
+.It
+The minimum pane size is four lines (including the separator line).
+.It
+The panes are indexed from top (0) to bottom, with no numbers skipped.
+.El
+.It Xo Ic start-server
+.Xc
+.D1 (alias: Ic start )
+Start the
+.Nm
+server, if not already running, without creating any sessions.
+.It Xo Ic suspend-client
+.Op Fl c target-client
+.Xc
+.D1 (alias: Ic suspendc )
+Suspend a client by sending SIGTSTP (tty stop).
+.It Xo Ic swap-pane
+.Op Fl dDU
+.Op Fl p Ar src-index
+.Op Fl t Ar target-window
+.Op Fl q Ar dst-index
+.Xc
+.D1 (alias: Ic swapp )
+Swap two panes within a window.
+If
+.Fl U
+is used, the pane is swapped with the pane above (before it numerically);
+.Fl D
+swaps with the pane below (the next numerically); or
+.Ar dst-index
+may be give to swap with a specific pane.
+.It Xo Ic swap-window
+.Op Fl d
+.Op Fl s Ar src-window
+.Op Fl t Ar dst-window
+.Xc
+.D1 (alias: Ic swapw )
+This is similar to
+.Ic link-window ,
+except the source and destination windows are swapped.
+It is an error if no window exists at
+.Ar src-window .
+.It Xo Ic switch-client
+.Op Fl c Ar target-client Fl t Ar target-session
+.Xc
+.D1 (alias: Ic switchc )
+Switch the current session for client
+.Ar target-client
+to
+.Ar target-session .
+.It Xo Ic unbind-key
+.Ar key
+.Xc
+.D1 (alias: Ic unbind )
+Unbind the key bound to
+.Ar key .
+.It Xo Ic unlink-window
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic unlinkw )
+Unlink
+.Ar target-window .
+A window may be unlinked only if it is linked to multiple sessions - windows may
+not be linked to no sessions.
+.It Xo Ic up-pane
+.Op Fl p Ar pane-index
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic upp )
+Move up a pane.
+.El
+.Sh FILES
+.Bl -tag -width Ds -compact
+.It Pa ~/.tmux.conf
+default
+.Nm
+configuration file
+.El
+.Sh SEE ALSO
+.Xr pty 4
+.Sh AUTHORS
+.An Nicholas Marriott Aq nicm@users.sourceforge.net
diff --git a/usr.bin/tmux/tmux.c b/usr.bin/tmux/tmux.c
new file mode 100644
index 00000000000..aa470d955da
--- /dev/null
+++ b/usr.bin/tmux/tmux.c
@@ -0,0 +1,487 @@
+/* $OpenBSD: tmux.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/stat.h>
+
+#include <errno.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+#ifdef DEBUG
+const char *malloc_options = "AFGJPX";
+#endif
+
+volatile sig_atomic_t sigwinch;
+volatile sig_atomic_t sigterm;
+volatile sig_atomic_t sigcont;
+volatile sig_atomic_t sigchld;
+volatile sig_atomic_t sigusr1;
+volatile sig_atomic_t sigusr2;
+
+char *cfg_file;
+struct options global_options;
+struct options global_window_options;
+
+int server_locked;
+char *server_password;
+time_t server_activity;
+
+int debug_level;
+int be_quiet;
+time_t start_time;
+char *socket_path;
+
+__dead void usage(void);
+char *makesockpath(const char *);
+
+__dead void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-28dqUuVv] [-f file] "
+ "[-L socket-name] [-S socket-path] [command [flags]]\n",
+ __progname);
+ exit(1);
+}
+
+void
+logfile(const char *name)
+{
+ char *path;
+
+ log_close();
+ if (debug_level > 0) {
+ xasprintf(
+ &path, "%s-%s-%ld.log", __progname, name, (long) getpid());
+ log_open_file(debug_level, path);
+ xfree(path);
+ }
+}
+
+void
+sighandler(int sig)
+{
+ int saved_errno;
+
+ saved_errno = errno;
+ switch (sig) {
+ case SIGWINCH:
+ sigwinch = 1;
+ break;
+ case SIGTERM:
+ sigterm = 1;
+ break;
+ case SIGCHLD:
+ sigchld = 1;
+ break;
+ case SIGCONT:
+ sigcont = 1;
+ break;
+ case SIGUSR1:
+ sigusr1 = 1;
+ break;
+ case SIGUSR2:
+ sigusr2 = 1;
+ break;
+ }
+ errno = saved_errno;
+}
+
+void
+siginit(void)
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof act);
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+
+ act.sa_handler = SIG_IGN;
+ if (sigaction(SIGPIPE, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGINT, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGTSTP, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGQUIT, &act, NULL) != 0)
+ fatal("sigaction failed");
+
+ act.sa_handler = sighandler;
+ if (sigaction(SIGWINCH, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGTERM, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGCHLD, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGUSR1, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGUSR2, &act, NULL) != 0)
+ fatal("sigaction failed");
+}
+
+void
+sigreset(void)
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof act);
+ sigemptyset(&act.sa_mask);
+
+ act.sa_handler = SIG_DFL;
+ if (sigaction(SIGPIPE, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGUSR1, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGUSR2, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGINT, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGTSTP, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGQUIT, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGWINCH, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGTERM, &act, NULL) != 0)
+ fatal("sigaction failed");
+ if (sigaction(SIGCHLD, &act, NULL) != 0)
+ fatal("sigaction failed");
+}
+
+char *
+makesockpath(const char *label)
+{
+ char base[MAXPATHLEN], *path;
+ struct stat sb;
+ u_int uid;
+
+ uid = getuid();
+ xsnprintf(base, MAXPATHLEN, "%s/%s-%d", _PATH_TMP, __progname, uid);
+
+ if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST)
+ return (NULL);
+
+ if (lstat(base, &sb) != 0)
+ return (NULL);
+ if (!S_ISDIR(sb.st_mode)) {
+ errno = ENOTDIR;
+ return (NULL);
+ }
+ if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) {
+ errno = EACCES;
+ return (NULL);
+ }
+
+ xasprintf(&path, "%s/%s", base, label);
+ return (path);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct client_ctx cctx;
+ struct msg_command_data cmddata;
+ struct buffer *b;
+ struct cmd_list *cmdlist;
+ struct cmd *cmd;
+ struct pollfd pfd;
+ struct hdr hdr;
+ const char *shell;
+ struct passwd *pw;
+ char *s, *path, *label, *cause, *home, *pass = NULL;
+ char cwd[MAXPATHLEN];
+ int retcode, opt, flags, unlock, start_server;
+
+ unlock = flags = 0;
+ label = path = NULL;
+ while ((opt = getopt(argc, argv, "28df:L:qS:uUv")) != -1) {
+ switch (opt) {
+ case '2':
+ flags |= IDENTIFY_256COLOURS;
+ flags &= ~IDENTIFY_88COLOURS;
+ break;
+ case '8':
+ flags |= IDENTIFY_88COLOURS;
+ flags &= ~IDENTIFY_256COLOURS;
+ break;
+ case 'f':
+ cfg_file = xstrdup(optarg);
+ break;
+ case 'L':
+ if (path != NULL) {
+ log_warnx("-L and -S cannot be used together");
+ exit(1);
+ }
+ if (label != NULL)
+ xfree(label);
+ label = xstrdup(optarg);
+ break;
+ case 'S':
+ if (label != NULL) {
+ log_warnx("-L and -S cannot be used together");
+ exit(1);
+ }
+ if (path != NULL)
+ xfree(path);
+ path = xstrdup(optarg);
+ break;
+ case 'q':
+ be_quiet = 1;
+ break;
+ case 'u':
+ flags |= IDENTIFY_UTF8;
+ break;
+ case 'U':
+ unlock = 1;
+ break;
+ case 'd':
+ flags |= IDENTIFY_HASDEFAULTS;
+ break;
+ case 'v':
+ debug_level++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ log_open_tty(debug_level);
+ siginit();
+
+ options_init(&global_options, NULL);
+ options_set_number(&global_options, "bell-action", BELL_ANY);
+ options_set_number(&global_options, "buffer-limit", 9);
+ options_set_number(&global_options, "display-time", 750);
+ options_set_number(&global_options, "history-limit", 2000);
+ options_set_number(&global_options, "lock-after-time", 0);
+ options_set_number(&global_options, "message-attr", GRID_ATTR_REVERSE);
+ options_set_number(&global_options, "message-bg", 3);
+ options_set_number(&global_options, "message-fg", 0);
+ options_set_number(&global_options, "prefix", '\002');
+ options_set_number(&global_options, "repeat-time", 500);
+ options_set_number(&global_options, "set-remain-on-exit", 0);
+ options_set_number(&global_options, "set-titles", 1);
+ options_set_number(&global_options, "status", 1);
+ options_set_number(&global_options, "status-attr", GRID_ATTR_REVERSE);
+ options_set_number(&global_options, "status-bg", 2);
+ options_set_number(&global_options, "status-fg", 0);
+ options_set_number(&global_options, "status-interval", 15);
+ options_set_number(&global_options, "status-keys", MODEKEY_EMACS);
+ options_set_number(&global_options, "status-left-length", 10);
+ options_set_number(&global_options, "status-right-length", 40);
+ options_set_string(&global_options, "status-left", "[#S]");
+ options_set_string(
+ &global_options, "status-right", "\"#24T\" %%H:%%M %%d-%%b-%%y");
+
+ options_init(&global_window_options, NULL);
+ options_set_number(&global_window_options, "aggressive-resize", 0);
+ options_set_number(&global_window_options, "automatic-rename", 1);
+ options_set_number(&global_window_options, "clock-mode-colour", 4);
+ options_set_number(&global_window_options, "clock-mode-style", 1);
+ options_set_number(&global_window_options, "force-height", 0);
+ options_set_number(&global_window_options, "force-width", 0);
+ options_set_number(
+ &global_window_options, "mode-attr", GRID_ATTR_REVERSE);
+ options_set_number(&global_window_options, "main-pane-width", 81);
+ options_set_number(&global_window_options, "main-pane-height", 24);
+ options_set_number(&global_window_options, "mode-bg", 3);
+ options_set_number(&global_window_options, "mode-fg", 0);
+ options_set_number(&global_window_options, "mode-keys", MODEKEY_EMACS);
+ options_set_number(&global_window_options, "monitor-activity", 0);
+ options_set_string(&global_window_options, "monitor-content", "%s", "");
+ options_set_number(&global_window_options, "utf8", 0);
+ options_set_number(&global_window_options, "window-status-attr", 0);
+ options_set_number(&global_window_options, "window-status-bg", 8);
+ options_set_number(&global_window_options, "window-status-fg", 8);
+ options_set_number(&global_window_options, "xterm-keys", 0);
+ options_set_number(&global_window_options, "remain-on-exit", 0);
+
+ if (!(flags & IDENTIFY_UTF8)) {
+ /*
+ * If the user has set LANG to contain UTF-8, it is a safe
+ * assumption that either they are using a UTF-8 terminal, or
+ * if not they know that output from UTF-8-capable programs may
+ * be wrong.
+ */
+ if ((s = getenv("LANG")) != NULL && strstr(s, "UTF-8") != NULL)
+ flags |= IDENTIFY_UTF8;
+ }
+
+ if (cfg_file == NULL) {
+ home = getenv("HOME");
+ if (home == NULL || *home == '\0') {
+ pw = getpwuid(getuid());
+ if (pw != NULL)
+ home = pw->pw_dir;
+ }
+ xasprintf(&cfg_file, "%s/%s", home, DEFAULT_CFG);
+ if (access(cfg_file, R_OK) != 0) {
+ xfree(cfg_file);
+ cfg_file = NULL;
+ }
+ } else {
+ if (access(cfg_file, R_OK) != 0) {
+ log_warn("%s", cfg_file);
+ exit(1);
+ }
+ }
+
+ if (label == NULL)
+ label = xstrdup("default");
+ if (path == NULL && (path = makesockpath(label)) == NULL) {
+ log_warn("can't create socket");
+ exit(1);
+ }
+ xfree(label);
+
+ shell = getenv("SHELL");
+ if (shell == NULL || *shell == '\0') {
+ pw = getpwuid(getuid());
+ if (pw != NULL)
+ shell = pw->pw_shell;
+ if (shell == NULL || *shell == '\0')
+ shell = _PATH_BSHELL;
+ }
+ options_set_string(
+ &global_options, "default-command", "exec %s", shell);
+
+ if (getcwd(cwd, sizeof cwd) == NULL) {
+ log_warn("getcwd");
+ exit(1);
+ }
+ options_set_string(&global_options, "default-path", "%s", cwd);
+
+ if (unlock) {
+ if (argc != 0) {
+ log_warnx("can't specify a command when unlocking");
+ exit(1);
+ }
+ cmdlist = NULL;
+ if ((pass = getpass("Password: ")) == NULL)
+ exit(1);
+ start_server = 0;
+ } else {
+ if (argc == 0) {
+ cmd = xmalloc(sizeof *cmd);
+ cmd->entry = &cmd_new_session_entry;
+ cmd->entry->init(cmd, 0);
+
+ cmdlist = xmalloc(sizeof *cmdlist);
+ TAILQ_INIT(cmdlist);
+ TAILQ_INSERT_HEAD(cmdlist, cmd, qentry);
+ } else {
+ cmdlist = cmd_list_parse(argc, argv, &cause);
+ if (cmdlist == NULL) {
+ log_warnx("%s", cause);
+ exit(1);
+ }
+ }
+ start_server = 0;
+ TAILQ_FOREACH(cmd, cmdlist, qentry) {
+ if (cmd->entry->flags & CMD_STARTSERVER) {
+ start_server = 1;
+ break;
+ }
+ }
+ }
+
+ memset(&cctx, 0, sizeof cctx);
+ if (client_init(path, &cctx, start_server, flags) != 0)
+ exit(1);
+ xfree(path);
+
+ b = buffer_create(BUFSIZ);
+ if (unlock) {
+ cmd_send_string(b, pass);
+ client_write_server(
+ &cctx, MSG_UNLOCK, BUFFER_OUT(b), BUFFER_USED(b));
+ } else {
+ cmd_list_send(cmdlist, b);
+ cmd_list_free(cmdlist);
+ client_fill_session(&cmddata);
+ client_write_server2(&cctx, MSG_COMMAND,
+ &cmddata, sizeof cmddata, BUFFER_OUT(b), BUFFER_USED(b));
+ }
+ buffer_destroy(b);
+
+ retcode = 0;
+ for (;;) {
+ pfd.fd = cctx.srv_fd;
+ pfd.events = POLLIN;
+ if (BUFFER_USED(cctx.srv_out) > 0)
+ pfd.events |= POLLOUT;
+
+ if (poll(&pfd, 1, INFTIM) == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ fatal("poll failed");
+ }
+
+ if (buffer_poll(&pfd, cctx.srv_in, cctx.srv_out) != 0)
+ goto out;
+
+ restart:
+ if (BUFFER_USED(cctx.srv_in) < sizeof hdr)
+ continue;
+ memcpy(&hdr, BUFFER_OUT(cctx.srv_in), sizeof hdr);
+ if (BUFFER_USED(cctx.srv_in) < (sizeof hdr) + hdr.size)
+ continue;
+ buffer_remove(cctx.srv_in, sizeof hdr);
+
+ switch (hdr.type) {
+ case MSG_EXIT:
+ case MSG_SHUTDOWN:
+ goto out;
+ case MSG_ERROR:
+ retcode = 1;
+ /* FALLTHROUGH */
+ case MSG_PRINT:
+ if (hdr.size > INT_MAX - 1)
+ fatalx("bad MSG_PRINT size");
+ log_info("%.*s",
+ (int) hdr.size, BUFFER_OUT(cctx.srv_in));
+ if (hdr.size != 0)
+ buffer_remove(cctx.srv_in, hdr.size);
+ goto restart;
+ case MSG_READY:
+ retcode = client_main(&cctx);
+ goto out;
+ default:
+ fatalx("unexpected command");
+ }
+ }
+
+out:
+ options_free(&global_options);
+ options_free(&global_window_options);
+
+ close(cctx.srv_fd);
+ buffer_destroy(cctx.srv_in);
+ buffer_destroy(cctx.srv_out);
+
+ return (retcode);
+}
diff --git a/usr.bin/tmux/tmux.cat1 b/usr.bin/tmux/tmux.cat1
new file mode 100644
index 00000000000..fe815603ab1
--- /dev/null
+++ b/usr.bin/tmux/tmux.cat1
@@ -0,0 +1,813 @@
+TMUX(1) OpenBSD Reference Manual TMUX(1)
+
+NNAAMMEE
+ ttmmuuxx - terminal multiplexer
+
+SSYYNNOOPPSSIISS
+ ttmmuuxx [--2288ddqqUUuuvv] [--ff _f_i_l_e] [--LL _s_o_c_k_e_t_-_n_a_m_e] [--SS _s_o_c_k_e_t_-_p_a_t_h]
+ [_c_o_m_m_a_n_d [_f_l_a_g_s]]
+
+DDEESSCCRRIIPPTTIIOONN
+ ttmmuuxx is a terminal multiplexer; it enables a number of terminals to be
+ accessed and controlled from a single terminal.
+
+ ttmmuuxx runs as a server-client system. A server is created automatically
+ when necessary and holds a number of _s_e_s_s_i_o_n_s, each of which may have a
+ number of _w_i_n_d_o_w_s linked to it. A window may be split on screen into one
+ or more _p_a_n_e_s, each of which is a separate terminal. Any number of
+ _c_l_i_e_n_t_s may connect to a session, or the server may be controlled by is-
+ suing commands with ttmmuuxx. Communication takes place through a socket, by
+ default placed in _/_t_m_p.
+
+ The options are as follows:
+
+ --22 Force ttmmuuxx to assume the terminal supports 256 colours.
+
+ --88 Like --22, indicates the terminal supports 88 colours.
+
+ --dd Force ttmmuuxx to assume the terminal supports default colours.
+
+ --ff _f_i_l_e Specify an alternative configuration file. By default,
+ ttmmuuxx will look for a config file at _~_/_._t_m_u_x_._c_o_n_f. The con-
+ figuration file is a set of ttmmuuxx commands which are execut-
+ ed in sequence when the server is first started.
+
+ --qq Prevent the server sending various information messages,
+ for example when window flags are altered.
+
+ --LL _s_o_c_k_e_t_-_n_a_m_e
+ ttmmuuxx stores the server socket in a directory under _/_t_m_p;
+ the default socket is named _d_e_f_a_u_l_t. This option allows a
+ different socket name to be specified, allowing several in-
+ dependent ttmmuuxx servers to be run. Unlike --SS a full path is
+ not necessary: the sockets are all created in the same di-
+ rectory.
+
+ --SS _s_o_c_k_e_t_-_p_a_t_h
+ Specify a full alternative path to the server socket. If
+ --SS is specified, the default socket directory is not used
+ and any --LL flag is ignored.
+
+ --UU Unlock the server.
+
+ --uu Instruct ttmmuuxx that the terminal support UTF-8.
+
+ --vv Request verbose logging. This option may be specified mul-
+ tiple times for increasing verbosity. Log messages will be
+ saved into _t_m_u_x_-_c_l_i_e_n_t_-_P_I_D_._l_o_g and _t_m_u_x_-_s_e_r_v_e_r_-_P_I_D_._l_o_g
+ files in the current directory, where _P_I_D is the pid of the
+ server or client process.
+
+ _c_o_m_m_a_n_d [_f_l_a_g_s]
+ This specifies one of a set of commands used to control
+ ttmmuuxx, and described in the following sections. If no com-
+ mand and flags is specified, the nneeww--sseessssiioonn command is as-
+ sumed.
+
+QQUUIICCKK SSTTAARRTT
+ To create a new tmux session running vi(1):
+
+ $ tmux new-session vi
+
+ Most commands have a shorter form, known as an alias. For new-session,
+ this is nneeww:
+
+ $ tmux new vi
+
+ Alternatively, the shortest unambiguous form of a command is accepted.
+ If there are several options, they are listed:
+
+ $ tmux n
+ ambiguous command: n, could be: new-session, new-window, next-window
+ $
+
+ Within an active session, a new window may be created by typing `C-b'
+ (ctrl-b, known as the prefix key) followed by the `c' key.
+
+ Windows may be navigated with: `C-b 0' (to select window 0), `C-b 1' (to
+ select window 1), and so on; `C-b n' to select the next window; and `C-b
+ p' to select the previous window.
+
+ A session may be detached using `C-b d' and reattached with:
+
+ $ tmux attach-session
+
+ Typing `C-b ?' lists the current key bindings in the current window; up
+ and down may be used to navigate the list or `Q' to exit from it.
+
+KKEEYY BBIINNDDIINNGGSS
+ ttmmuuxx may be controlled from an attached client by using a key combination
+ of a prefix key, `C-b' (ctrl-b) by default, followed by a command key.
+
+ Some of the default key bindings include:
+
+ `d' Detach current client.
+ `c' Create new window.
+ `n' Change to next window in the current session.
+ `p' Change to previous window in the current session.
+ `l' Move to last (previously selected) window in the current session.
+ `t' Display a large clock.
+ `?' List current key bindings.
+
+ A complete list may be obtained with the lliisstt--kkeeyyss command (bound to `?'
+ by default). Key bindings may be changed with the bbiinndd--kkeeyy and uunnbbiinndd--
+ kkeeyy commands.
+
+HHIISSTTOORRYY
+ ttmmuuxx maintains a configurable history buffer for each window. By de-
+ fault, up to 2000 lines are kept, this can be altered with the hhiissttoorryy--
+ lliimmiitt option (see the sseett--ooppttiioonn command below).
+
+MMOODDEESS
+ A ttmmuuxx window may be in one of several modes. The default permits direct
+ access to the terminal attached to the window. The others are:
+
+ _o_u_t_p_u_t _m_o_d_e
+ This is entered when a command which produces output, such as
+ lliisstt--kkeeyyss, is executed from a key binding.
+
+ _s_c_r_o_l_l _m_o_d_e
+ This is entered with the ssccrroollll--mmooddee command (bound to `=' by de-
+ fault) and permits the window history buffer to be inspected.
+
+ _c_o_p_y _m_o_d_e
+ This permits a section of a window or its history to be copied to
+ a _p_a_s_t_e _b_u_f_f_e_r for later insertion into another window. This
+ mode is entered with the ccooppyy--mmooddee command, bound to `[' by de-
+ fault.
+
+ The keys available depend on whether emacs(1) or vi(1) mode is selected
+ (see the mmooddee--kkeeyyss option). The following keys are supported as appro-
+ priate for the mode:
+
+ FFuunnccttiioonn vvii eemmaaccss
+ Start of line 0 or ^ C-a
+ Clear selection Escape C-g
+ Copy selection Enter M-w
+ Cursor down j Down
+ End of line $ C-e
+ Cursor left h Left
+ Next page C-f Page down
+ Next word w M-f
+ Previous page C-u Page up
+ Previous word b M-b
+ Quit mode q Escape
+ Cursor right l Right
+ Start selection Space C-Space
+ Cursor up k Up
+
+BBUUFFFFEERRSS
+ ttmmuuxx maintains a stack of _p_a_s_t_e _b_u_f_f_e_r_s for each session. Up to the val-
+ ue of the bbuuffffeerr--lliimmiitt option are kept; when a new buffer is added, the
+ buffer at the bottom of the stack is removed. Buffers may be added using
+ ccooppyy--mmooddee or the sseett--bbuuffffeerr command, and pasted into a window using the
+ ppaassttee--bbuuffffeerr command.
+
+PPAANNEESS AANNDD LLAAYYOOUUTTSS
+ Each window displayed by ttmmuuxx may be split into one or more _p_a_n_e_s; each
+ pane takes up a certain area of the display and is a separate terminal.
+ A window may be split into panes using the sspplliitt--wwiinnddooww command.
+
+ Panes are numbered beginning from zero; in horizontal layouts zero is the
+ leftmost pane and in vertical the topmost.
+
+ Panes may be arranged using several layouts. The layout may be cycled
+ with the nneexxtt--llaayyoouutt command (bound to `C-space' by default), the current
+ pane may be changed with the uupp--ppaannee and ddoowwnn--ppaannee commands and the
+ rroottaattee--wwiinnddooww and sswwaapp--ppaannee commands may be used to swap panes without
+ changing the window layout.
+
+ The following layouts are supported:
+
+ mmaannuuaall Manual layout splits windows vertically (running across); only
+ with this layout may panes be resized using the rreessiizzee--ppaannee com-
+ mand.
+
+ aaccttiivvee--oonnllyy
+ Only the active pane is shown - all other panes are hidden.
+
+ eevveenn--hhoorriizzoonnttaall
+ Panes are spread out evenly from left to right across the window.
+
+ eevveenn--vveerrttiiccaall
+ Panes are spread evenly from top to bottom.
+
+ mmaaiinn--vveerrttiiccaall
+ A large (81 column) pane is shown on the left of the window and
+ the remaining panes are spread from top to bottom in the leftover
+ space to the right.
+
+CCOOMMMMAANNDDSS
+ This section contains a list of the commands supported by ttmmuuxx. Most
+ commands accept the optional --tt argument with one of _t_a_r_g_e_t_-_c_l_i_e_n_t,
+ _t_a_r_g_e_t_-_s_e_s_s_i_o_n or _t_a_r_g_e_t_-_w_i_n_d_o_w. These specify the client, session or
+ window which a command should affect. _t_a_r_g_e_t_-_c_l_i_e_n_t is the name of the
+ pty(4) file to which the client is connected, for example _/_d_e_v_/_t_t_y_p_1.
+ Clients may be listed with the lliisstt--cclliieennttss command.
+
+ _t_a_r_g_e_t_-_s_e_s_s_i_o_n is either the name of a session (as listed by the lliisstt--
+ sseessssiioonnss command); or the name of a client as for _t_a_r_g_e_t_-_c_l_i_e_n_t, in this
+ case, the session attached to the client is used. An fnmatch(3) pattern
+ may be used to match the session name. If a session is omitted when re-
+ quired, ttmmuuxx attempts to use the current session; if no current session
+ is available, the most recently created is chosen. If no client is spec-
+ ified, the current client is chosen, if possible, or an error is report-
+ ed.
+
+ _t_a_r_g_e_t_-_w_i_n_d_o_w specifies a window in the form _s_e_s_s_i_o_n:_i_n_d_e_x, for example
+ mysession:1. The session is in the same form as for _t_a_r_g_e_t_-_s_e_s_s_i_o_n.
+ _s_e_s_s_i_o_n, _i_n_d_e_x or both may be omitted. If _s_e_s_s_i_o_n is omitted, the same
+ rules as for _t_a_r_g_e_t_-_s_e_s_s_i_o_n are followed; if _i_n_d_e_x is not present, the
+ current window for the given session is used. When the argument does not
+ contain a colon (:), ttmmuuxx first attempts to parse it as window index; if
+ that fails, an attempt is made to match a session or client name.
+
+ Multiple commands may be specified together as part of a _c_o_m_m_a_n_d
+ _s_e_q_u_e_n_c_e. Each command should be separated by spaces and a semicolon (`
+ ; '); commands are executed sequentially from left to right. A literal
+ semicolon may be included by escaping it with a backslash (for example,
+ when specifying a command sequence to bbiinndd--kkeeyy).
+
+ Examples include:
+
+ refresh-client -t/dev/ttyp2
+
+ rename-session -tfirst newname
+
+ set-window-option -t:0 monitor-activity on
+
+ new-window ; split-window -d
+
+ bind-key D detach-client \; lock-server
+
+ The following commands are available:
+
+ aattttaacchh--sseessssiioonn [--dd] [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n]
+ (alias: aattttaacchh)
+ Create a new client in the current terminal and attach it to a
+ session. If --dd is specified, any other clients attached to the
+ session are detached.
+
+ If no server is started, aattttaacchh--sseessssiioonn will attempt to start it;
+ this will fail unless sessions are created in the configuration
+ file.
+
+ bbiinndd--kkeeyy [--rr] _k_e_y _c_o_m_m_a_n_d [_a_r_g_u_m_e_n_t_s]
+ (alias: bbiinndd)
+ Bind key _k_e_y to _c_o_m_m_a_n_d. Keys may be specified prefixed with
+ `C-' or `^' for ctrl keys, or `M-' for alt (meta) keys. The --rr
+ flag indicates this key may repeat, see the rreeppeeaatt--ttiimmee option.
+
+ bbrreeaakk--ppaannee [--dd] [--pp _p_a_n_e_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ (alias: bbrreeaakkpp))
+ Break the current pane off from its containing window to make it
+ the only pane in a new window. If --dd is given, the new window
+ does not become the current window.
+
+ cchhoooossee--sseessssiioonn [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ Put a window into session choice mode, where the session for the
+ current client may be selected interactively from a list. This
+ command works only from inside ttmmuuxx.
+
+ cchhoooossee--wwiinnddooww [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ Put a window into window choice mode, where the window for the
+ session attached to the current client may be selected interac-
+ tively from a list. This command works only from inside ttmmuuxx.
+
+ cclloocckk--mmooddee [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ Display a large clock.
+
+ ccoommmmaanndd--pprroommpptt [--tt _t_a_r_g_e_t_-_c_l_i_e_n_t] [_t_e_m_p_l_a_t_e]
+ Open the command prompt in a client. This may be used from in-
+ side ttmmuuxx to execute commands interactively. If _t_e_m_p_l_a_t_e is
+ specified, it is used as the command; any %% in the template will
+ be replaced by what is entered at the prompt.
+
+ ccoonnffiirrmm--bbeeffoorree [--tt _t_a_r_g_e_t_-_c_l_i_e_n_t] _c_o_m_m_a_n_d
+ (alias: ccoonnffiirrmm))
+ Ask for confirmation before executing _c_o_m_m_a_n_d. This command
+ works only from inside ttmmuuxx.
+
+ ccooppyy--bbuuffffeerr [--aa _s_r_c_-_i_n_d_e_x] [--bb _d_s_t_-_i_n_d_e_x] [--ss _s_r_c_-_s_e_s_s_i_o_n] [--tt
+ _d_s_t_-_s_e_s_s_i_o_n]
+ (alias: ccooppyybb))
+ Copy a session paste buffer to another session. If no sessions
+ are specified, the current one is used instead.
+
+ ccooppyy--mmooddee [--uu] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ Enter copy mode. The --uu option scrolls one page up.
+
+ ddeelleettee--bbuuffffeerr [--bb _b_u_f_f_e_r_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n]
+ (alias: ddeelleetteebb)
+ Delete the buffer at _b_u_f_f_e_r_-_i_n_d_e_x, or the top buffer if not spec-
+ ified.
+
+ ddeettaacchh--cclliieenntt [--tt _t_a_r_g_e_t_-_c_l_i_e_n_t]
+ (alias: ddeettaacchh)
+ Detach the current client if bound to a key, or the specified
+ client with --tt.
+
+ ddoowwnn--ppaannee [--pp _p_a_n_e_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ (alias: ddoowwnnpp)
+ Move down a pane.
+
+ ffiinndd--wwiinnddooww [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] _m_a_t_c_h_-_s_t_r_i_n_g
+ (alias: ffiinnddww)
+ Search for _m_a_t_c_h_-_s_t_r_i_n_g in window names, titles, and visible con-
+ tent (but not history). If only one window is matched, it'll be
+ automatically selected, otherwise a choice list is shown. This
+ command only works from inside ttmmuuxx.
+
+ hhaass--sseessssiioonn [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n]
+ (alias: hhaass)
+ Report an error and exit with 1 if the specified session does not
+ exist. If it does exist, exit with 0.
+
+ kkiillll--ppaannee [--pp _p_a_n_e_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ (alias: kkiillllpp)
+ Destroy the given pane.
+
+ kkiillll--sseerrvveerr
+ Kill the ttmmuuxx server and clients and destroy all sessions.
+
+ kkiillll--sseessssiioonn [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n]
+ Destroy the given session, closing any windows linked to it and
+ no other sessions, and detaching all clients attached to it.
+
+ kkiillll--wwiinnddooww [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ (alias: kkiillllww)
+ Kill the current window or the window at _t_a_r_g_e_t_-_w_i_n_d_o_w, removing
+ it from any sessions to which it is linked.
+
+ llaasstt--wwiinnddooww [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n]
+ (alias: llaasstt)
+ Select the last (previously selected) window. If no _t_a_r_g_e_t_-
+ _s_e_s_s_i_o_n is specified, select the last window of the current ses-
+ sion.
+
+ lliinnkk--wwiinnddooww [--ddkk] [--ss _s_r_c_-_w_i_n_d_o_w] [--tt _d_s_t_-_w_i_n_d_o_w]
+ (alias: lliinnkkww)
+ Link the window at _s_r_c_-_w_i_n_d_o_w to the specified _d_s_t_-_w_i_n_d_o_w. If
+ _d_s_t_-_w_i_n_d_o_w is specified and no such window exists, the _s_r_c_-_w_i_n_d_o_w
+ is linked there. If --kk is given and _d_s_t_-_w_i_n_d_o_w exists, it is
+ killed, otherwise an error is generated. If --dd is given, the
+ newly linked window is not selected.
+
+ lliisstt--bbuuffffeerrss [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n]
+ (alias: llssbb)
+ List the buffers in the given session.
+
+ lliisstt--cclliieennttss
+ (alias: llsscc)
+ List all clients attached to the server.
+
+ lliisstt--ccoommmmaannddss
+ (alias: llssccmm)
+ List the syntax of all commands supported by ttmmuuxx.
+
+ lliisstt--kkeeyyss
+ (alias: llsskk)
+ List all key bindings.
+
+ lliisstt--sseessssiioonnss
+ (alias: llss)
+ List all sessions managed by the server.
+
+ lliisstt--wwiinnddoowwss [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n]
+ (alias: llssww)
+ List windows in the current session or in _t_a_r_g_e_t_-_s_e_s_s_i_o_n.
+
+ llooaadd--bbuuffffeerr [--bb _b_u_f_f_e_r_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] _p_a_t_h
+ (alias: llooaaddbb)
+ Load the contents of the specified paste buffer from _p_a_t_h.
+
+ lloocckk--sseerrvveerr
+ (alias: lloocckk)
+ Lock the server until a password is entered.
+
+ mmoovvee--wwiinnddooww [--dd] [--ss _s_r_c_-_w_i_n_d_o_w] [--tt _d_s_t_-_w_i_n_d_o_w]
+ (alias: mmoovveeww)
+ This is similar to lliinnkk--wwiinnddooww, except the window at _s_r_c_-_w_i_n_d_o_w
+ is moved to _d_s_t_-_w_i_n_d_o_w.
+
+ nneeww--sseessssiioonn [--dd] [--nn _w_i_n_d_o_w_-_n_a_m_e] [--ss _s_e_s_s_i_o_n_-_n_a_m_e] [_c_o_m_m_a_n_d]
+ (alias: nneeww)
+ Create a new session with name _s_e_s_s_i_o_n_-_n_a_m_e. The new session is
+ attached to the current terminal unless --dd is given. _w_i_n_d_o_w_-_n_a_m_e
+ and _c_o_m_m_a_n_d are the name of and command to execute in the initial
+ window.
+
+ nneeww--wwiinnddooww [--dd] [--nn _w_i_n_d_o_w_-_n_a_m_e] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] [_c_o_m_m_a_n_d]
+ (alias: nneewwww)
+ Create a new window. If --dd is given, the session does not make
+ the new window the current window. _t_a_r_g_e_t_-_w_i_n_d_o_w represents the
+ window to be created. _c_o_m_m_a_n_d is the command to execute. If
+ _c_o_m_m_a_n_d is not specified, the default command is used.
+
+ The TERM environment variable must be set to ``screen'' for all
+ programs running _i_n_s_i_d_e ttmmuuxx. New windows will automatically
+ have ``TERM=screen'' added to their environment, but care must be
+ taken not to reset this in shell start-up files.
+
+ nneexxtt--llaayyoouutt [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ (alias: nneexxttll)
+ Move a window to the next layout and rearrange the panes to fit.
+
+ nneexxtt--wwiinnddooww [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n]
+ (alias: nneexxtt)
+ Move to the next window in the session.
+
+ ppaassttee--bbuuffffeerr [--dd] [--bb _b_u_f_f_e_r_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ (alias: ppaasstteebb)
+ Insert the contents of a paste buffer into the current window.
+
+ pprreevviioouuss--wwiinnddooww [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n]
+ (alias: pprreevv)
+ Move to the previous window in the session.
+
+ rreeffrreesshh--cclliieenntt [--tt _t_a_r_g_e_t_-_c_l_i_e_n_t]
+ (alias: rreeffrreesshh)
+ Refresh the current client if bound to a key, or a single client
+ if one is given with --tt.
+
+ rreennaammee--sseessssiioonn [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] _n_e_w_-_n_a_m_e
+ (alias: rreennaammee)
+ Rename the session to _n_e_w_-_n_a_m_e.
+
+ rreennaammee--wwiinnddooww [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] _n_e_w_-_n_a_m_e
+ (alias: rreennaammeeww)
+ Rename the current window, or the window at _t_a_r_g_e_t_-_w_i_n_d_o_w if
+ specified, to _n_e_w_-_n_a_m_e.
+
+ rreessiizzee--ppaannee [--DDUU] [--pp _p_a_n_e_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] [_a_d_j_u_s_t_m_e_n_t]
+ (alias: rreessiizzeepp)
+ Resize a pane, upward with --UU (the default) or downward with --DD.
+ The _a_d_j_u_s_t_m_e_n_t is given in lines (the default is 1).
+
+ rreessppaawwnn--wwiinnddooww [--kk] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] [_c_o_m_m_a_n_d]
+ (alias: rreessppaawwnnww)
+ Reactive a window in which the command has exited (see the
+ rreemmaaiinn--oonn--eexxiitt window option). If _c_o_m_m_a_n_d is not given, the com-
+ mand used when the window was created is executed. The window
+ must be already inactive, unless --kk is given, in which case any
+ existing command is killed.
+
+ rroottaattee--wwiinnddooww [--DDUU] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ (alias: rroottaatteeww)
+ Rotate the positions of the panes within a window, either upward
+ (numerically lower) with --UU or downward (numerically higher).
+
+ ssaavvee--bbuuffffeerr [--aa] [--bb _b_u_f_f_e_r_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] _p_a_t_h
+ (alias: ssaavveebb)
+ Save the contents of the specified paste buffer to _p_a_t_h. The --aa
+ option appends to rather than overwriting the file.
+
+ ssccrroollll--mmooddee [--uu] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ Enter scroll mode. The --uu has the same meaning as in the ccooppyy--
+ mmooddee command.
+
+ sseelleecctt--ppaannee [--pp _p_a_n_e_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ (alias: sseelleeccttpp)
+ Make pane _p_a_n_e_-_i_n_d_e_x the active pane in window _t_a_r_g_e_t_-_w_i_n_d_o_w.
+
+ sseelleecctt--pprroommpptt [--tt _t_a_r_g_e_t_-_c_l_i_e_n_t]
+ Open a prompt inside _t_a_r_g_e_t_-_c_l_i_e_n_t allowing a window index to be
+ entered interactively.
+
+ sseelleecctt--wwiinnddooww [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ (alias: sseelleeccttww)
+ Select the window at _t_a_r_g_e_t_-_w_i_n_d_o_w.
+
+ sseenndd--kkeeyyss [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] _k_e_y _._._.
+ (alias: sseenndd)
+ Send a key or keys to a window. Each argument _k_e_y is the name of
+ the key (such as `C-a' or `npage' ) to send; if the string is not
+ recognised as a key, it is sent as a series of characters. All
+ arguments are sent sequentially from first to last.
+
+ sseenndd--pprreeffiixx [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ Send the prefix key to a window as if it was pressed.
+
+ sseerrvveerr--iinnffoo
+ (alias: iinnffoo)
+ Show server information and terminal details.
+
+ sseett--bbuuffffeerr [--bb _b_u_f_f_e_r_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] _d_a_t_a
+ (alias: sseettbb)
+ Set the contents of the specified buffer to _d_a_t_a.
+
+ sseett--ooppttiioonn [--gguu] [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] _o_p_t_i_o_n _v_a_l_u_e
+ (alias: sseett)
+ Set an option. If --gg is specified, the option is set as a global
+ option. Global options apply to all sessions which don't have
+ the option explicitly set. If --gg is not used, the option applies
+ only to _t_a_r_g_e_t_-_s_e_s_s_i_o_n. The --uu flag unsets an option, so a ses-
+ sion inherits the option from the global options - it is not pos-
+ sible to unset a global option.
+
+ Possible options are:
+
+ bbeellll--aaccttiioonn [aannyy | nnoonnee | ccuurrrreenntt]
+ Set action on window bell. aannyy means a bell in any win-
+ dow linked to a session causes a bell in the current win-
+ dow of that session, nnoonnee means all bells are ignored and
+ ccuurrrreenntt means only bell in windows other than the current
+ window are ignored.
+
+ bbuuffffeerr--lliimmiitt _n_u_m_b_e_r
+ Set the number of buffers kept for each session; as new
+ buffers are added to the top of the stack, old ones are
+ removed from the bottom if necessary to maintain this
+ maximum length.
+
+ ddeeffaauulltt--ccoommmmaanndd _c_o_m_m_a_n_d
+ Set the command used for new windows (if not specified
+ when the window is created) to _c_o_m_m_a_n_d. The default is
+ ``exec $SHELL''.
+
+ ddeeffaauulltt--ppaatthh _p_a_t_h
+ Set the default working directory for processes created
+ from keys, or interactively from the prompt. The default
+ is the current working directory when the server is
+ started.
+
+ hhiissttoorryy--lliimmiitt _l_i_n_e_s
+ Set the maximum number of lines held in window history.
+ This setting applies only to new windows - existing win-
+ dow histories are not resized and retain the limit at the
+ point they were created.
+
+ lloocckk--aafftteerr--ttiimmee _n_u_m_b_e_r
+ Lock the server after _n_u_m_b_e_r seconds of inactivity. The
+ default is off (set to 0). This has no effect as a ses-
+ sion option; it must be set as a global option using --gg.
+
+ mmeessssaaggee--aattttrr _a_t_t_r_i_b_u_t_e_s
+ Set status line message attributes, where _a_t_t_r_i_b_u_t_e_s is
+ either ddeeffaauulltt or a comma-delimited list of one or more
+ of: bbrriigghhtt (or bboolldd), ddiimm, uunnddeerrssccoorree, bblliinnkk, rreevveerrssee,
+ hhiiddddeenn, or iittaalliiccss.
+
+ mmeessssaaggee--bbgg _c_o_l_o_u_r
+ Set status line message background colour, where _c_o_l_o_u_r
+ is one of: bbllaacckk, rreedd, ggrreeeenn, yyeellllooww, bblluuee, mmaaggeennttaa,
+ ccyyaann, wwhhiittee or ddeeffaauulltt.
+
+ mmeessssaaggee--ffgg _c_o_l_o_u_r
+ Set status line message foreground colour.
+
+ pprreeffiixx _k_e_y
+ Set the current prefix key.
+
+ rreeppeeaatt--ttiimmee _n_u_m_b_e_r
+ Allow multiple commands to be entered without pressing
+ the prefix-key again in the specified _n_u_m_b_e_r milliseconds
+ (the default is 500). Whether a key repeats may be set
+ when it is bound using the --rr flag to bbiinndd--kkeeyy. Repeat
+ is enabled for the default keys of the uupp--ppaannee, ddoowwnn--
+ ppaannee, rreessiizzee--ppaannee--uupp, and rreessiizzee--ppaannee--ddoowwnn commands.
+
+ sseett--rreemmaaiinn--oonn--eexxiitt [oonn | ooffff]
+ Set the rreemmaaiinn--oonn--eexxiitt window option for any windows
+ first created in this session.
+
+ sseett--ttiittlleess [oonn | ooffff]
+ Attempt to set the window title using the \e]2;...\007
+ xterm code and the terminal appears to be an xterm. This
+ option is enabled by default. Note that elinks(1) will
+ only attempt to set the window title if the STY environ-
+ ment variable is set.
+
+ ssttaattuuss [oonn | ooffff]
+ Show or hide the status line.
+
+ ssttaattuuss--aattttrr _a_t_t_r_i_b_u_t_e_s
+ Set status line attributes.
+
+ ssttaattuuss--bbgg _c_o_l_o_u_r
+ Set status line background colour.
+
+ ssttaattuuss--ffgg _c_o_l_o_u_r
+ Set status line foreground colour.
+
+ ssttaattuuss--iinntteerrvvaall _i_n_t_e_r_v_a_l
+ Update the status bar every _i_n_t_e_r_v_a_l seconds. By de-
+ fault, updates will occur every 15 seconds. A setting of
+ zero disables redrawing at interval.
+
+ ssttaattuuss--kkeeyyss [vvii | eemmaaccss]
+ Use vi(1)- or emacs(1)-style key bindings in the status
+ line, for example at the command prompt. Defaults to
+ emacs.
+
+ ssttaattuuss--lleefftt _s_t_r_i_n_g
+ Display _s_t_r_i_n_g to the left of the status bar. _s_t_r_i_n_g
+ will be passed through strftime(3) before being used. By
+ default, the session name is shown. _s_t_r_i_n_g may contain
+ any of the following special character pairs:
+
+ CChhaarraacctteerr ppaaiirr RReeppllaacceedd wwiitthh
+ #(command) First line of command's output
+ #H Hostname of local host
+ #S Session name
+ #T Current window title
+ ## A literal `#'
+
+ Where appropriate, these may be prefixed with a number to
+ specify the maximum length, for example `#24T'.
+
+ ssttaattuuss--lleefftt--lleennggtthh _l_e_n_g_t_h
+ Set the maximum _l_e_n_g_t_h of the left component of the sta-
+ tus bar. The default is 10.
+
+ ssttaattuuss--rriigghhtt _s_t_r_i_n_g
+ Display _s_t_r_i_n_g to the right of the status bar. By de-
+ fault, the date and time will be shown. As with ssttaattuuss--
+ lleefftt, _s_t_r_i_n_g will be passed to strftime(3) and character
+ pairs are replaced.
+
+ ssttaattuuss--rriigghhtt--lleennggtthh _l_e_n_g_t_h
+ Set the maximum _l_e_n_g_t_h of the right component of the sta-
+ tus bar. The default is 40.
+
+ sseett--ppaasssswwoorrdd [--cc] _p_a_s_s_w_o_r_d
+ (alias: ppaassss)
+ Set the server password. If the --cc option is given, a pre-en-
+ crypted password may be specified. By default, the password is
+ blank, thus any entered password will be accepted when unlocking
+ the server (see the lloocckk--sseerrvveerr command). To prevent variable
+ expansion when an encrypted password is read from a configuration
+ file, enclose it in single quotes (').
+
+ sseett--wwiinnddooww--ooppttiioonn [--gguu] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] _o_p_t_i_o_n _v_a_l_u_e
+ (alias: sseettww)
+ Set a window-specific option. The --gg and --uu flags work similarly
+ to the sseett--ooppttiioonn command.
+
+ Supported options are:
+
+ aaggggrreessssiivvee--rreessiizzee [oonn | ooffff]
+ Aggressively resize the chosen window. This means that
+ ttmmuuxx will resize the window to the size of the smallest
+ session for which it is the current window, rather than
+ the smallest session to which it is attached. The window
+ may resize when the current window is changed on another
+ sessions; this option is good for full-screen programs
+ which support SIGWINCH and poor for interactive programs
+ such as shells.
+
+ aauuttoommaattiicc--rreennaammee [oonn | ooffff]
+ Control automatic window renaming. When this setting is
+ enabled, ttmmuuxx will attempt - on supported platforms - to
+ rename the window to reflect the command currently run-
+ ning in it. This flag is automatically disabled for an
+ individual window when a name is specified at creation
+ with nneeww--wwiinnddooww oorr nneeww--sseessssiioonn, or later with rreennaammee--
+ wwiinnddooww. It may be switched off globally with:
+
+ set-window-option -g automatic-rename off
+
+ cclloocckk--mmooddee--ccoolloouurr _c_o_l_o_u_r
+ Set clock colour.
+
+ cclloocckk--mmooddee--ssttyyllee [1122 | 2244]
+ Set clock hour format.
+
+ ffoorrccee--hheeiigghhtt _h_e_i_g_h_t
+
+ ffoorrccee--wwiiddtthh _w_i_d_t_h
+ Prevent ttmmuuxx from resizing a window to greater than _w_i_d_t_h
+ or _h_e_i_g_h_t. A value of zero restores the default unlimit-
+ ed setting.
+
+ mmooddee--aattttrr _a_t_t_r_i_b_u_t_e_s
+ Set window modes attributes.
+
+ mmooddee--bbgg _c_o_l_o_u_r
+ Set window modes background colour.
+
+ mmooddee--ffgg _c_o_l_o_u_r
+ Set window modes foreground colour.
+
+ mmooddee--kkeeyyss [vvii | eemmaaccss]
+ Use vi(1)- or emacs(1)-style key bindings in scroll and
+ copy modes. Key bindings default to emacs.
+
+ mmoonniittoorr--aaccttiivviittyy [oonn | ooffff]
+ Monitor for activity in the window. Windows with activi-
+ ty are highlighted in the status line.
+
+ mmoonniittoorr--ccoonntteenntt _m_a_t_c_h_-_s_t_r_i_n_g
+ Monitor content in the window. When _m_a_t_c_h_-_s_t_r_i_n_g appears
+ in the window, it is highlighted in the status line.
+
+ rreemmaaiinn--oonn--eexxiitt [oonn | ooffff]
+ A window with this flag set is not destroyed when the
+ program running in it exits. The window may be reacti-
+ vated with the rreessppaawwnn--wwiinnddooww command.
+
+ uuttff88 [oonn | ooffff]
+ Instructs ttmmuuxx to expect UTF-8 sequences to appear in
+ this window.
+
+ wwiinnddooww--ssttaattuuss--aattttrr _a_t_t_r_i_b_u_t_e_s
+ Set status line attributes for a single window.
+
+ wwiinnddooww--ssttaattuuss--bbgg _c_o_l_o_u_r
+ Set status line background colour for a single window.
+
+ wwiinnddooww--ssttaattuuss--ffgg _c_o_l_o_u_r
+ Set status line foreground colour for a single window.
+
+ xxtteerrmm--kkeeyyss [oonn | ooffff]
+ If this option is set, ttmmuuxx will generate xterm(1)-style
+ function key sequences; these have a number included to
+ indicate modifiers such as shift, meta or ctrl.
+
+ sshhooww--bbuuffffeerr [--bb _b_u_f_f_e_r_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n]
+ (alias: sshhoowwbb)
+ Display the contents of the specified buffer.
+
+ sshhooww--ooppttiioonnss [--tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n] _o_p_t_i_o_n _v_a_l_u_e
+ (alias: sshhooww)
+ Show the currently set options. If a _t_a_r_g_e_t_-_s_e_s_s_i_o_n is speci-
+ fied, the options for that session are shown; otherwise, the
+ global options are listed.
+
+ sshhooww--wwiinnddooww--ooppttiioonnss [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] _o_p_t_i_o_n _v_a_l_u_e
+ (alias: sshhoowwww)
+ List the current options for the given window.
+
+ ssoouurrccee--ffiillee _p_a_t_h
+ (alias: ssoouurrccee)
+ Execute commands from _p_a_t_h.
+
+ sspplliitt--wwiinnddooww [--dd] [--ll _l_i_n_e_s | --pp _p_e_r_c_e_n_t_a_g_e] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] [_c_o_m_m_a_n_d]
+ (alias: splitw)
+ Creates a new window by splitting it vertically. The --ll and --pp
+ options specify the size of the new window in lines, or as a per-
+ centage, respectively. All other options have the same meaning
+ as in the nneeww--wwiinnddooww command.
+
+ A few notes with regard to panes:
+ 1. If attempting to split a window with less than eight lines,
+ an error will be shown.
+ 2. If the window is resized, as many panes are shown as can fit
+ without reducing them below four lines.
+ 3. The minimum pane size is four lines (including the separator
+ line).
+ 4. The panes are indexed from top (0) to bottom, with no num-
+ bers skipped.
+
+ ssttaarrtt--sseerrvveerr
+ (alias: ssttaarrtt)
+ Start the ttmmuuxx server, if not already running, without creating
+ any sessions.
+
+ ssuussppeenndd--cclliieenntt [--cc --ttaarrggeett--cclliieenntt]
+ (alias: ssuussppeennddcc)
+ Suspend a client by sending SIGTSTP (tty stop).
+
+ sswwaapp--ppaannee [--ddDDUU] [--pp _s_r_c_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w] [--qq _d_s_t_-_i_n_d_e_x]
+ (alias: sswwaapppp)
+ Swap two panes within a window. If --UU is used, the pane is
+ swapped with the pane above (before it numerically); --DD swaps
+ with the pane below (the next numerically); or _d_s_t_-_i_n_d_e_x may be
+ give to swap with a specific pane.
+
+ sswwaapp--wwiinnddooww [--dd] [--ss _s_r_c_-_w_i_n_d_o_w] [--tt _d_s_t_-_w_i_n_d_o_w]
+ (alias: sswwaappww)
+ This is similar to lliinnkk--wwiinnddooww, except the source and destination
+ windows are swapped. It is an error if no window exists at _s_r_c_-
+ _w_i_n_d_o_w.
+
+ sswwiittcchh--cclliieenntt [--cc _t_a_r_g_e_t_-_c_l_i_e_n_t --tt _t_a_r_g_e_t_-_s_e_s_s_i_o_n]
+ (alias: sswwiittcchhcc)
+ Switch the current session for client _t_a_r_g_e_t_-_c_l_i_e_n_t to _t_a_r_g_e_t_-
+ _s_e_s_s_i_o_n.
+
+ uunnbbiinndd--kkeeyy _k_e_y
+ (alias: uunnbbiinndd)
+ Unbind the key bound to _k_e_y.
+
+ uunnlliinnkk--wwiinnddooww [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ (alias: uunnlliinnkkww)
+ Unlink _t_a_r_g_e_t_-_w_i_n_d_o_w. A window may be unlinked only if it is
+ linked to multiple sessions - windows may not be linked to no
+ sessions.
+
+ uupp--ppaannee [--pp _p_a_n_e_-_i_n_d_e_x] [--tt _t_a_r_g_e_t_-_w_i_n_d_o_w]
+ (alias: uupppp)
+ Move up a pane.
+
+FFIILLEESS
+ ~/.tmux.conf
+ default ttmmuuxx configuration file
+
+SSEEEE AALLSSOO
+ pty(4)
+
+AAUUTTHHOORRSS
+ Nicholas Marriott <nicm@users.sourceforge.net>
+
+OpenBSD 4.5 April 20, 2009 13
diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h
new file mode 100644
index 00000000000..f39f70f601c
--- /dev/null
+++ b/usr.bin/tmux/tmux.h
@@ -0,0 +1,1576 @@
+/* $OpenBSD: tmux.h,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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.
+ */
+
+#ifndef TMUX_H
+#define TMUX_H
+
+#define PROTOCOL_VERSION -13
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+
+#include <getopt.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "array.h"
+
+extern const char *__progname;
+
+/* Default configuration file. */
+#define DEFAULT_CFG ".tmux.conf"
+
+/* Default prompt history length. */
+#define PROMPT_HISTORY 100
+
+/* Minimum pane size. */
+#define PANE_MINIMUM 4 /* includes separator line */
+
+/* Automatic name refresh interval, in milliseconds. */
+#define NAME_INTERVAL 500
+
+/* Escape timer period, in milliseconds. */
+#define ESCAPE_PERIOD 250
+
+/* Maximum poll timeout (when attached). */
+#define POLL_TIMEOUT 50
+
+/* Fatal errors. */
+#define fatal(msg) log_fatal("%s: %s", __func__, msg);
+#define fatalx(msg) log_fatalx("%s: %s", __func__, msg);
+
+/* Definition to shut gcc up about unused arguments. */
+#define unused __attribute__ ((unused))
+
+/* Attribute to make gcc check printf-like arguments. */
+#define printflike1 __attribute__ ((format (printf, 1, 2)))
+#define printflike2 __attribute__ ((format (printf, 2, 3)))
+#define printflike3 __attribute__ ((format (printf, 3, 4)))
+#define printflike4 __attribute__ ((format (printf, 4, 5)))
+
+/* Number of items in array. */
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+
+/* Buffer macros. */
+#define BUFFER_USED(b) ((b)->size)
+#define BUFFER_FREE(b) ((b)->space - (b)->off - (b)->size)
+#define BUFFER_IN(b) ((b)->base + (b)->off + (b)->size)
+#define BUFFER_OUT(b) ((b)->base + (b)->off)
+
+/* Buffer structure. */
+struct buffer {
+ u_char *base; /* buffer start */
+ size_t space; /* total size of buffer */
+
+ size_t size; /* size of data in buffer */
+ size_t off; /* offset of data in buffer */
+};
+
+/* Bell option values. */
+#define BELL_NONE 0
+#define BELL_ANY 1
+#define BELL_CURRENT 2
+
+/* Key codes. ncurses defines KEY_*. Grrr. */
+#define KEYC_NONE 0x00ffff
+#define KEYC_OFFSET 0x010000
+#define KEYC_ESCAPE 0x020000
+#define KEYC_CONTROL 0x080000
+#define KEYC_SHIFT 0x100000
+
+#define KEYC_ADDESC(k) ((k) | KEYC_ESCAPE)
+#define KEYC_REMOVEESC(k) ((k) & ~KEYC_ESCAPE)
+#define KEYC_ISESC(k) ((k) != KEYC_NONE && ((k) & KEYC_ESCAPE))
+
+#define KEYC_ADDCTL(k) ((k) | KEYC_CONTROL)
+#define KEYC_REMOVECTL(k) ((k) & ~KEYC_CONTROL)
+#define KEYC_ISCTL(k) ((k) != KEYC_NONE && ((k) & KEYC_CONTROL))
+
+#define KEYC_ADDSFT(k) ((k) | KEYC_SHIFT)
+#define KEYC_REMOVESFT(k) ((k) & ~KEYC_SHIFT)
+#define KEYC_ISSFT(k) ((k) != KEYC_NONE && ((k) & KEYC_SHIFT))
+
+/* Mouse key. */
+#define KEYC_MOUSE (KEYC_OFFSET + 0x00)
+
+/* Function keys. */
+#define KEYC_F1 (KEYC_OFFSET + 0x01)
+#define KEYC_F2 (KEYC_OFFSET + 0x02)
+#define KEYC_F3 (KEYC_OFFSET + 0x03)
+#define KEYC_F4 (KEYC_OFFSET + 0x04)
+#define KEYC_F5 (KEYC_OFFSET + 0x05)
+#define KEYC_F6 (KEYC_OFFSET + 0x06)
+#define KEYC_F7 (KEYC_OFFSET + 0x07)
+#define KEYC_F8 (KEYC_OFFSET + 0x08)
+#define KEYC_F9 (KEYC_OFFSET + 0x09)
+#define KEYC_F10 (KEYC_OFFSET + 0x10)
+#define KEYC_F11 (KEYC_OFFSET + 0x11)
+#define KEYC_F12 (KEYC_OFFSET + 0x12)
+#define KEYC_F13 (KEYC_OFFSET + 0x13)
+#define KEYC_F14 (KEYC_OFFSET + 0x14)
+#define KEYC_F15 (KEYC_OFFSET + 0x15)
+#define KEYC_F16 (KEYC_OFFSET + 0x16)
+#define KEYC_F17 (KEYC_OFFSET + 0x17)
+#define KEYC_F18 (KEYC_OFFSET + 0x18)
+#define KEYC_F19 (KEYC_OFFSET + 0x19)
+#define KEYC_F20 (KEYC_OFFSET + 0x1a)
+#define KEYC_IC (KEYC_OFFSET + 0x1b)
+#define KEYC_DC (KEYC_OFFSET + 0x1c)
+#define KEYC_HOME (KEYC_OFFSET + 0x1d)
+#define KEYC_END (KEYC_OFFSET + 0x1e)
+#define KEYC_NPAGE (KEYC_OFFSET + 0x1f)
+#define KEYC_PPAGE (KEYC_OFFSET + 0x20)
+#define KEYC_BTAB (KEYC_OFFSET + 0x21)
+
+/* Arrow keys. */
+#define KEYC_UP (KEYC_OFFSET + 0x50)
+#define KEYC_DOWN (KEYC_OFFSET + 0x51)
+#define KEYC_LEFT (KEYC_OFFSET + 0x52)
+#define KEYC_RIGHT (KEYC_OFFSET + 0x53)
+
+/* Numeric keypad. Numbered from top-left, KPY_X. */
+#define KEYC_KP0_1 (KEYC_OFFSET + 0x100)
+#define KEYC_KP0_2 (KEYC_OFFSET + 0x101)
+#define KEYC_KP0_3 (KEYC_OFFSET + 0x102)
+#define KEYC_KP1_0 (KEYC_OFFSET + 0x103)
+#define KEYC_KP1_1 (KEYC_OFFSET + 0x104)
+#define KEYC_KP1_2 (KEYC_OFFSET + 0x105)
+#define KEYC_KP1_3 (KEYC_OFFSET + 0x106)
+#define KEYC_KP2_0 (KEYC_OFFSET + 0x107)
+#define KEYC_KP2_1 (KEYC_OFFSET + 0x108)
+#define KEYC_KP2_2 (KEYC_OFFSET + 0x109)
+#define KEYC_KP3_0 (KEYC_OFFSET + 0x10a)
+#define KEYC_KP3_1 (KEYC_OFFSET + 0x10b)
+#define KEYC_KP3_2 (KEYC_OFFSET + 0x10c)
+#define KEYC_KP3_3 (KEYC_OFFSET + 0x10d)
+#define KEYC_KP4_0 (KEYC_OFFSET + 0x10e)
+#define KEYC_KP4_2 (KEYC_OFFSET + 0x10f)
+
+/* Termcap codes. */
+enum tty_code_code {
+ TTYC_AX = 0,
+ TTYC_ACSC, /* acs_chars, ac */
+ TTYC_BEL, /* bell, bl */
+ TTYC_BLINK, /* enter_blink_mode, mb */
+ TTYC_BOLD, /* enter_bold_mode, md */
+ TTYC_CIVIS, /* cursor_invisible, vi */
+ TTYC_CLEAR, /* clear_screen, cl */
+ TTYC_CNORM, /* cursor_normal, ve */
+ TTYC_COLORS, /* max_colors, Co */
+ TTYC_CSR, /* change_scroll_region, cs */
+ TTYC_CUD, /* parm_down_cursor, DO */
+ TTYC_CUD1, /* cursor_down, do */
+ TTYC_CUP, /* cursor_address, cm */
+ TTYC_DCH, /* parm_dch, DC */
+ TTYC_DCH1, /* delete_character, dc */
+ TTYC_DIM, /* enter_dim_mode, mh */
+ TTYC_DL, /* parm_delete_line, DL */
+ TTYC_DL1, /* delete_line, dl */
+ TTYC_EL, /* clr_eol, ce */
+ TTYC_EL1, /* clr_bol, cb */
+ TTYC_ENACS, /* ena_acs, eA */
+ TTYC_ICH, /* parm_ich, IC */
+ TTYC_ICH1, /* insert_character, ic */
+ TTYC_IL, /* parm_insert_line, IL */
+ TTYC_IL1, /* insert_line, il */
+ TTYC_INVIS, /* enter_secure_mode, mk */
+ TTYC_IS1, /* init_1string, i1 */
+ TTYC_IS2, /* init_2string, i2 */
+ TTYC_IS3, /* init_3string, i3 */
+ TTYC_KCBT, /* key_btab, kB */
+ TTYC_KCUB1, /* key_left, kl */
+ TTYC_KCUD1, /* key_down, kd */
+ TTYC_KCUF1, /* key_right, kr */
+ TTYC_KCUU1, /* key_up, ku */
+ TTYC_KDCH1, /* key_dc, kD */
+ TTYC_KEND, /* key_end, ke */
+ TTYC_KF1, /* key_f1, k1 */
+ TTYC_KF10, /* key_f10, k; */
+ TTYC_KF11, /* key_f11, F1 */
+ TTYC_KF12, /* key_f12, F2 */
+ TTYC_KF13, /* key_f13, F3 */
+ TTYC_KF14, /* key_f14, F4 */
+ TTYC_KF15, /* key_f15, F5 */
+ TTYC_KF16, /* key_f16, F6 */
+ TTYC_KF17, /* key_f17, F7 */
+ TTYC_KF18, /* key_f18, F8 */
+ TTYC_KF19, /* key_f19, F9 */
+ TTYC_KF20, /* key_f20, F10 */
+ TTYC_KF2, /* key_f2, k2 */
+ TTYC_KF3, /* key_f3, k3 */
+ TTYC_KF4, /* key_f4, k4 */
+ TTYC_KF5, /* key_f5, k5 */
+ TTYC_KF6, /* key_f6, k6 */
+ TTYC_KF7, /* key_f7, k7 */
+ TTYC_KF8, /* key_f8, k8 */
+ TTYC_KF9, /* key_f9, k9 */
+ TTYC_KHOME, /* key_home, kh */
+ TTYC_KICH1, /* key_ic, kI */
+ TTYC_KMOUS, /* key_mouse, Km */
+ TTYC_KNP, /* key_npage, kN */
+ TTYC_KPP, /* key_ppage, kP */
+ TTYC_OP, /* orig_pair, op */
+ TTYC_REV, /* enter_reverse_mode, mr */
+ TTYC_RI, /* scroll_reverse, sr */
+ TTYC_RMACS, /* exit_alt_charset_mode */
+ TTYC_RMCUP, /* exit_ca_mode, te */
+ TTYC_RMIR, /* exit_insert_mode, ei */
+ TTYC_RMKX, /* keypad_local, ke */
+ TTYC_SETAB, /* set_a_background, AB */
+ TTYC_SETAF, /* set_a_foreground, AF */
+ TTYC_SGR0, /* exit_attribute_mode, me */
+ TTYC_SMACS, /* enter_alt_charset_mode, as */
+ TTYC_SMCUP, /* enter_ca_mode, ti */
+ TTYC_SMIR, /* enter_insert_mode, im */
+ TTYC_SMKX, /* keypad_xmit, ks */
+ TTYC_SMSO, /* enter_standout_mode, so */
+ TTYC_SMUL, /* enter_underline_mode, us */
+ TTYC_XENL, /* eat_newline_glitch, xn */
+};
+#define NTTYCODE (TTYC_XENL + 1)
+
+/* Termcap types. */
+enum tty_code_type {
+ TTYCODE_NONE = 0,
+ TTYCODE_STRING,
+ TTYCODE_NUMBER,
+ TTYCODE_FLAG,
+};
+
+/* Termcap code. */
+struct tty_code {
+ enum tty_code_type type;
+ union {
+ char *string;
+ int number;
+ int flag;
+ } value;
+};
+
+/* Entry in terminal code table. */
+struct tty_term_code_entry {
+ enum tty_code_code code;
+ enum tty_code_type type;
+ const char *name;
+};
+
+/* Output commands. */
+enum tty_cmd {
+ TTY_CELL,
+ TTY_CLEARENDOFLINE,
+ TTY_CLEARENDOFSCREEN,
+ TTY_CLEARLINE,
+ TTY_CLEARSCREEN,
+ TTY_CLEARSTARTOFLINE,
+ TTY_CLEARSTARTOFSCREEN,
+ TTY_DELETECHARACTER,
+ TTY_DELETELINE,
+ TTY_INSERTCHARACTER,
+ TTY_INSERTLINE,
+ TTY_LINEFEED,
+ TTY_RAW,
+ TTY_REVERSEINDEX,
+};
+
+/* Message codes. */
+enum hdrtype {
+ MSG_COMMAND,
+ MSG_DETACH,
+ MSG_ERROR,
+ MSG_EXIT,
+ MSG_EXITED,
+ MSG_EXITING,
+ MSG_IDENTIFY,
+ MSG_PRINT,
+ MSG_READY,
+ MSG_RESIZE,
+ MSG_SHUTDOWN,
+ MSG_SUSPEND,
+ MSG_UNLOCK,
+ MSG_WAKEUP,
+};
+
+/* Message header structure. */
+struct hdr {
+ enum hdrtype type;
+ size_t size;
+};
+
+struct msg_command_data {
+ pid_t pid; /* pid from $TMUX or -1 */
+ u_int idx; /* index from $TMUX */
+
+ size_t namelen;
+};
+
+struct msg_identify_data {
+ char tty[TTY_NAME_MAX];
+ int version;
+
+ char cwd[MAXPATHLEN];
+
+#define IDENTIFY_UTF8 0x1
+#define IDENTIFY_256COLOURS 0x2
+#define IDENTIFY_88COLOURS 0x4
+#define IDENTIFY_HASDEFAULTS 0x8
+ int flags;
+
+ u_int sx;
+ u_int sy;
+
+ size_t termlen;
+};
+
+struct msg_resize_data {
+ u_int sx;
+ u_int sy;
+};
+
+/* Editing keys. */
+enum mode_key_cmd {
+ MODEKEYCMD_BACKSPACE = 0x1000,
+ MODEKEYCMD_CHOOSE,
+ MODEKEYCMD_CLEARSELECTION,
+ MODEKEYCMD_COMPLETE,
+ MODEKEYCMD_COPYSELECTION,
+ MODEKEYCMD_DELETE,
+ MODEKEYCMD_DOWN,
+ MODEKEYCMD_ENDOFLINE,
+ MODEKEYCMD_LEFT,
+ MODEKEYCMD_NEXTPAGE,
+ MODEKEYCMD_NEXTWORD,
+ MODEKEYCMD_NONE,
+ MODEKEYCMD_OTHERKEY,
+ MODEKEYCMD_PASTE,
+ MODEKEYCMD_PREVIOUSPAGE,
+ MODEKEYCMD_PREVIOUSWORD,
+ MODEKEYCMD_QUIT,
+ MODEKEYCMD_RIGHT,
+ MODEKEYCMD_STARTOFLINE,
+ MODEKEYCMD_STARTSELECTION,
+ MODEKEYCMD_UP,
+};
+
+struct mode_key_data {
+ int type;
+
+ int flags;
+#define MODEKEY_EDITMODE 0x1
+#define MODEKEY_CANEDIT 0x2
+#define MODEKEY_CHOOSEMODE 0x4
+};
+
+#define MODEKEY_EMACS 0
+#define MODEKEY_VI 1
+
+/* Modes. */
+#define MODE_CURSOR 0x1
+#define MODE_INSERT 0x2
+#define MODE_KCURSOR 0x4
+#define MODE_KKEYPAD 0x8
+#define MODE_MOUSE 0x10
+
+/* Grid output. */
+#if defined(DEBUG) && \
+ ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \
+ (defined(__GNUC__) && __GNUC__ >= 3))
+#define GRID_DEBUG(gd, fmt, ...) log_debug3("%s: (sx=%u, sy=%u, hsize=%u) " \
+ fmt, __func__, (gd)->sx, (gd)->sy, (gd)->hsize, ## __VA_ARGS__)
+#else
+#define GRID_DEBUG(...)
+#endif
+
+/* Grid attributes. */
+#define GRID_ATTR_BRIGHT 0x1
+#define GRID_ATTR_DIM 0x2
+#define GRID_ATTR_UNDERSCORE 0x4
+#define GRID_ATTR_BLINK 0x8
+#define GRID_ATTR_REVERSE 0x10
+#define GRID_ATTR_HIDDEN 0x20
+#define GRID_ATTR_ITALICS 0x40
+#define GRID_ATTR_CHARSET 0x80 /* alternative character set */
+
+/* Grid flags. */
+#define GRID_FLAG_FG256 0x1
+#define GRID_FLAG_BG256 0x2
+#define GRID_FLAG_PADDING 0x4
+#define GRID_FLAG_UTF8 0x8
+
+/* Grid cell data. */
+struct grid_cell {
+ u_char attr;
+ u_char flags;
+ u_char fg;
+ u_char bg;
+ u_char data;
+} __packed;
+
+/* Grid cell UTF-8 data. Used instead of data in grid_cell for UTF-8 cells. */
+#define UTF8_SIZE 8
+struct grid_utf8 {
+ u_char width;
+ u_char data[UTF8_SIZE];
+} __packed;
+
+/* Entire grid of cells. */
+struct grid {
+ u_int sx;
+ u_int sy;
+
+ u_int hsize;
+ u_int hlimit;
+
+ u_int *size;
+ struct grid_cell **data;
+
+ u_int *usize;
+ struct grid_utf8 **udata;
+};
+
+/* Option data structures. */
+struct options_entry {
+ char *name;
+
+ enum {
+ OPTIONS_STRING,
+ OPTIONS_NUMBER,
+ OPTIONS_KEY,
+ } type;
+ union {
+ char *string;
+ long long number;
+ int key;
+ } value;
+
+ SPLAY_ENTRY(options_entry) entry;
+};
+
+struct options {
+ SPLAY_HEAD(options_tree, options_entry) tree;
+ struct options *parent;
+};
+
+/* Screen selection. */
+struct screen_sel {
+ int flag;
+
+ u_int sx;
+ u_int sy;
+
+ u_int ex;
+ u_int ey;
+
+ struct grid_cell cell;
+};
+
+/* Virtual screen. */
+struct screen {
+ char *title;
+
+ struct grid *grid; /* grid data */
+
+ u_int cx; /* cursor x */
+ u_int cy; /* cursor y */
+
+ u_int old_cx;
+ u_int old_cy;
+
+ u_int rupper; /* scroll region top */
+ u_int rlower; /* scroll region bottom */
+
+ u_int old_rupper;
+ u_int old_rlower;
+
+ int mode;
+
+ struct screen_sel sel;
+};
+
+/* Screen write context. */
+struct screen_write_ctx {
+ struct window_pane *wp;
+ struct screen *s;
+};
+
+/* Screen size. */
+#define screen_size_x(s) ((s)->grid->sx)
+#define screen_size_y(s) ((s)->grid->sy)
+#define screen_hsize(s) ((s)->grid->hsize)
+#define screen_hlimit(s) ((s)->grid->hlimit)
+
+/* Input parser sequence argument. */
+struct input_arg {
+ u_char data[64];
+ size_t used;
+};
+
+/* Input parser context. */
+struct input_ctx {
+ struct window_pane *wp;
+ struct screen_write_ctx ctx;
+
+ u_char *buf;
+ size_t len;
+ size_t off;
+
+ struct grid_cell cell;
+
+
+ struct grid_cell saved_cell;
+ u_int saved_cx;
+ u_int saved_cy;
+
+#define MAXSTRINGLEN 1024
+ u_char *string_buf;
+ size_t string_len;
+ int string_type;
+#define STRING_SYSTEM 0
+#define STRING_APPLICATION 1
+#define STRING_NAME 2
+
+ u_char utf8_buf[4];
+ u_int utf8_len;
+ u_int utf8_off;
+
+ u_char intermediate;
+ void *(*state)(u_char, struct input_ctx *);
+
+ u_char private;
+ ARRAY_DECL(, struct input_arg) args;
+};
+
+/*
+ * Window mode. Windows can be in several modes and this is used to call the
+ * right function to handle input and output.
+ */
+struct client;
+struct window;
+struct window_mode {
+ struct screen *(*init)(struct window_pane *);
+ void (*free)(struct window_pane *);
+ void (*resize)(struct window_pane *, u_int, u_int);
+ void (*key)(struct window_pane *, struct client *, int);
+ void (*mouse)(struct window_pane *,
+ struct client *, u_char, u_char, u_char);
+ void (*timer)(struct window_pane *);
+};
+
+/* Child window structure. */
+struct window_pane {
+ struct window *window;
+
+ u_int sx;
+ u_int sy;
+
+ u_int xoff;
+ u_int yoff;
+
+ int flags;
+#define PANE_HIDDEN 0x1
+#define PANE_RESTART 0x2
+#define PANE_REDRAW 0x4
+
+ char *cmd;
+ char *cwd;
+
+ pid_t pid;
+ int fd;
+ char tty[TTY_NAME_MAX];
+ struct buffer *in;
+ struct buffer *out;
+
+ struct input_ctx ictx;
+
+ struct screen *screen;
+ struct screen base;
+
+ const struct window_mode *mode;
+ void *modedata;
+
+ TAILQ_ENTRY(window_pane) entry;
+};
+TAILQ_HEAD(window_panes, window_pane);
+
+/* Window structure. */
+struct window {
+ char *name;
+ struct timeval name_timer;
+
+ struct window_pane *active;
+ struct window_panes panes;
+ u_int layout;
+
+ u_int sx;
+ u_int sy;
+
+ int flags;
+#define WINDOW_BELL 0x1
+#define WINDOW_HIDDEN 0x2
+#define WINDOW_ACTIVITY 0x4
+#define WINDOW_CONTENT 0x6
+#define WINDOW_REDRAW 0x8
+
+ struct options options;
+
+ u_int references;
+};
+ARRAY_DECL(windows, struct window *);
+
+/* Entry on local window list. */
+struct winlink {
+ int idx;
+ struct window *window;
+
+ RB_ENTRY(winlink) entry;
+ SLIST_ENTRY(winlink) sentry;
+};
+RB_HEAD(winlinks, winlink);
+SLIST_HEAD(winlink_stack, winlink);
+
+/* Paste buffer. */
+struct paste_buffer {
+ char *data;
+ struct timeval tv;
+};
+ARRAY_DECL(paste_stack, struct paste_buffer *);
+
+/* Client session. */
+struct session_alert {
+ struct winlink *wl;
+ int type;
+
+ SLIST_ENTRY(session_alert) entry;
+};
+
+struct session {
+ char *name;
+ struct timeval tv;
+
+ u_int sx;
+ u_int sy;
+
+ struct winlink *curw;
+ struct winlink_stack lastw;
+ struct winlinks windows;
+
+ struct options options;
+
+ struct paste_stack buffers;
+
+ SLIST_HEAD(, session_alert) alerts;
+
+#define SESSION_UNATTACHED 0x1 /* not attached to any clients */
+ int flags;
+};
+ARRAY_DECL(sessions, struct session *);
+
+/* TTY information. */
+struct tty_key {
+ int key;
+ char *string;
+
+ int flags;
+#define TTYKEY_CTRL 0x1
+#define TTYKEY_RAW 0x2
+
+ RB_ENTRY(tty_key) entry;
+};
+
+struct tty_term {
+ char *name;
+ u_int references;
+
+ struct tty_code codes[NTTYCODE];
+
+#define TERM_HASDEFAULTS 0x1
+#define TERM_256COLOURS 0x2
+#define TERM_88COLOURS 0x4
+#define TERM_EARLYWRAP 0x8
+ int flags;
+
+ SLIST_ENTRY(tty_term) entry;
+};
+SLIST_HEAD(tty_terms, tty_term);
+
+struct tty {
+ char *path;
+
+ u_int sx;
+ u_int sy;
+
+ u_int cx;
+ u_int cy;
+
+ int mode;
+
+ u_int rlower;
+ u_int rupper;
+
+ char *termname;
+ struct tty_term *term;
+
+ int fd;
+ struct buffer *in;
+ struct buffer *out;
+
+ int log_fd;
+
+ struct termios tio;
+
+ struct grid_cell cell;
+
+ u_char acs[UCHAR_MAX + 1];
+
+#define TTY_NOCURSOR 0x1
+#define TTY_FREEZE 0x2
+#define TTY_ESCAPE 0x4
+#define TTY_UTF8 0x8
+ int flags;
+
+ int term_flags;
+
+ struct timeval key_timer;
+
+ size_t ksize; /* maximum key size */
+ RB_HEAD(tty_keys, tty_key) ktree;
+};
+
+/* Client connection. */
+struct client {
+ int fd;
+ struct buffer *in;
+ struct buffer *out;
+
+ char *title;
+ char *cwd;
+
+ struct tty tty;
+ struct timeval status_timer;
+ struct timeval repeat_timer;
+
+ struct screen status;
+
+#define CLIENT_TERMINAL 0x1
+#define CLIENT_PREFIX 0x2
+#define CLIENT_MOUSE 0x4
+#define CLIENT_REDRAW 0x8
+#define CLIENT_STATUS 0x10
+#define CLIENT_REPEAT 0x20 /* allow command to repeat within repeat time */
+#define CLIENT_SUSPENDED 0x40
+ int flags;
+
+ char *message_string;
+ struct timeval message_timer;
+
+ char *prompt_string;
+ char *prompt_buffer;
+ size_t prompt_index;
+ int (*prompt_callback)(void *, const char *);
+ void *prompt_data;
+
+#define PROMPT_HIDDEN 0x1
+#define PROMPT_SINGLE 0x2
+ int prompt_flags;
+
+ u_int prompt_hindex;
+ ARRAY_DECL(, char *) prompt_hdata;
+
+ struct mode_key_data prompt_mdata;
+
+ struct session *session;
+};
+ARRAY_DECL(clients, struct client *);
+
+/* Client context. */
+struct client_ctx {
+ int srv_fd;
+ struct buffer *srv_in;
+ struct buffer *srv_out;
+
+#define CCTX_DETACH 0x1
+#define CCTX_EXIT 0x2
+#define CCTX_SHUTDOWN 0x4
+ int flags;
+};
+
+/* Key/command line command. */
+struct cmd_ctx {
+ struct client *cmdclient;
+
+ struct client *curclient;
+ struct session *cursession;
+ struct msg_command_data *msgdata;
+
+ void (*print)(struct cmd_ctx *, const char *, ...);
+ void (*info)(struct cmd_ctx *, const char *, ...);
+ void (*error)(struct cmd_ctx *, const char *, ...);
+};
+
+struct cmd {
+ const struct cmd_entry *entry;
+ void *data;
+
+ TAILQ_ENTRY(cmd) qentry;
+};
+TAILQ_HEAD(cmd_list, cmd);
+
+struct cmd_entry {
+ const char *name;
+ const char *alias;
+ const char *usage;
+
+#define CMD_STARTSERVER 0x1
+#define CMD_CANTNEST 0x2
+#define CMD_ARG1 0x4
+#define CMD_ARG01 0x8
+#define CMD_AFLAG 0x10
+#define CMD_DFLAG 0x20
+#define CMD_GFLAG 0x40
+#define CMD_KFLAG 0x80
+#define CMD_UFLAG 0x100
+#define CMD_BIGDFLAG 0x200
+#define CMD_BIGUFLAG 0x400
+
+ int flags;
+
+ void (*init)(struct cmd *, int);
+ int (*parse)(struct cmd *, int, char **, char **);
+ int (*exec)(struct cmd *, struct cmd_ctx *);
+ void (*send)(struct cmd *, struct buffer *);
+ void (*recv)(struct cmd *, struct buffer *);
+ void (*free)(struct cmd *);
+ size_t (*print)(struct cmd *, char *, size_t);
+};
+
+/* Generic command data. */
+struct cmd_target_data {
+ int flags;
+ char *target;
+ char *arg;
+};
+
+struct cmd_srcdst_data {
+ int flags;
+ char *src;
+ char *dst;
+ char *arg;
+};
+
+struct cmd_buffer_data {
+ int flags;
+ char *target;
+ int buffer;
+ char *arg;
+};
+
+struct cmd_option_data {
+ int flags;
+ char *target;
+ char *option;
+ char *value;
+};
+
+struct cmd_pane_data {
+ int flags;
+ char *target;
+ char *arg;
+ int pane;
+};
+
+/* Key binding. */
+struct key_binding {
+ int key;
+ struct cmd_list *cmdlist;
+ int can_repeat;
+
+ SPLAY_ENTRY(key_binding) entry;
+};
+SPLAY_HEAD(key_bindings, key_binding);
+
+/* Set/display option data. */
+struct set_option_entry {
+ const char *name;
+ enum {
+ SET_OPTION_STRING,
+ SET_OPTION_NUMBER,
+ SET_OPTION_KEY,
+ SET_OPTION_COLOUR,
+ SET_OPTION_ATTRIBUTES,
+ SET_OPTION_FLAG,
+ SET_OPTION_CHOICE
+ } type;
+
+ u_int minimum;
+ u_int maximum;
+
+ const char **choices;
+};
+extern const struct set_option_entry set_option_table[];
+extern const struct set_option_entry set_window_option_table[];
+#define NSETOPTION 24
+#define NSETWINDOWOPTION 19
+
+/* tmux.c */
+extern volatile sig_atomic_t sigwinch;
+extern volatile sig_atomic_t sigterm;
+extern volatile sig_atomic_t sigcont;
+extern volatile sig_atomic_t sigchld;
+extern volatile sig_atomic_t sigusr1;
+extern volatile sig_atomic_t sigusr2;
+extern struct options global_options;
+extern struct options global_window_options;
+extern char *cfg_file;
+extern int server_locked;
+extern char *server_password;
+extern time_t server_activity;
+extern int debug_level;
+extern int be_quiet;
+extern time_t start_time;
+extern char *socket_path;
+void logfile(const char *);
+void siginit(void);
+void sigreset(void);
+void sighandler(int);
+
+/* cfg.c */
+int load_cfg(const char *, char **x);
+
+/* mode-key.c */
+void mode_key_init(struct mode_key_data *, int, int);
+void mode_key_free(struct mode_key_data *);
+enum mode_key_cmd mode_key_lookup(struct mode_key_data *, int);
+
+/* options.c */
+int options_cmp(struct options_entry *, struct options_entry *);
+SPLAY_PROTOTYPE(options_tree, options_entry, entry, options_cmp);
+void options_init(struct options *, struct options *);
+void options_free(struct options *);
+struct options_entry *options_find1(struct options *, const char *);
+struct options_entry *options_find(struct options *, const char *);
+int options_remove(struct options *, const char *);
+void printflike3 options_set_string(
+ struct options *, const char *, const char *, ...);
+char *options_get_string(struct options *, const char *);
+void options_set_number(struct options *, const char *, long long);
+long long options_get_number(struct options *, const char *);
+
+/* tty.c */
+u_char tty_get_acs(struct tty *, u_char);
+void tty_emulate_repeat(struct tty *,
+ enum tty_code_code, enum tty_code_code, u_int);
+void tty_reset(struct tty *);
+void tty_region(struct tty *, u_int, u_int, u_int);
+void tty_cursor(struct tty *, u_int, u_int, u_int, u_int);
+void tty_cell(struct tty *,
+ const struct grid_cell *, const struct grid_utf8 *);
+void tty_putcode(struct tty *, enum tty_code_code);
+void tty_putcode1(struct tty *, enum tty_code_code, int);
+void tty_putcode2(struct tty *, enum tty_code_code, int, int);
+void tty_puts(struct tty *, const char *);
+void tty_putc(struct tty *, u_char);
+void tty_init(struct tty *, char *, char *);
+void tty_start_tty(struct tty *);
+void tty_stop_tty(struct tty *);
+void tty_detect_utf8(struct tty *);
+void tty_set_title(struct tty *, const char *);
+void tty_update_mode(struct tty *, int);
+void tty_draw_line(
+ struct tty *, struct screen *, u_int, u_int, u_int);
+void tty_redraw_region(struct tty *, struct window_pane *);
+int tty_open(struct tty *, char **);
+void tty_close(struct tty *, int);
+void tty_free(struct tty *, int);
+void tty_write(
+ struct tty *, struct window_pane *, enum tty_cmd, ...);
+void tty_vwrite(
+ struct tty *, struct window_pane *, enum tty_cmd, va_list);
+
+/* tty-term.c */
+extern struct tty_terms tty_terms;
+extern struct tty_term_code_entry tty_term_codes[NTTYCODE];
+struct tty_term *tty_term_find(char *, int, char **);
+void tty_term_free(struct tty_term *);
+int tty_term_has(struct tty_term *, enum tty_code_code);
+const char *tty_term_string(struct tty_term *, enum tty_code_code);
+const char *tty_term_string1(struct tty_term *, enum tty_code_code, int);
+const char *tty_term_string2(
+ struct tty_term *, enum tty_code_code, int, int);
+int tty_term_number(struct tty_term *, enum tty_code_code);
+int tty_term_flag(struct tty_term *, enum tty_code_code);
+
+/* tty-keys.c */
+int tty_keys_cmp(struct tty_key *, struct tty_key *);
+RB_PROTOTYPE(tty_keys, tty_key, entry, tty_keys_cmp);
+void tty_keys_init(struct tty *);
+void tty_keys_free(struct tty *);
+int tty_keys_next(struct tty *, int *, u_char *);
+
+/* tty-write.c */
+void tty_write_cmd(struct window_pane *, enum tty_cmd, ...);
+void tty_write_mode(struct window_pane *, int);
+
+/* options-cmd.c */
+void set_option_string(struct cmd_ctx *,
+ struct options *, const struct set_option_entry *, char *);
+void set_option_number(struct cmd_ctx *,
+ struct options *, const struct set_option_entry *, char *);
+void set_option_key(struct cmd_ctx *,
+ struct options *, const struct set_option_entry *, char *);
+void set_option_colour(struct cmd_ctx *,
+ struct options *, const struct set_option_entry *, char *);
+void set_option_attributes(struct cmd_ctx *,
+ struct options *, const struct set_option_entry *, char *);
+void set_option_flag(struct cmd_ctx *,
+ struct options *, const struct set_option_entry *, char *);
+void set_option_choice(struct cmd_ctx *,
+ struct options *, const struct set_option_entry *, char *);
+
+/* paste.c */
+void paste_init_stack(struct paste_stack *);
+void paste_free_stack(struct paste_stack *);
+struct paste_buffer *paste_walk_stack(struct paste_stack *, uint *);
+struct paste_buffer *paste_get_top(struct paste_stack *);
+struct paste_buffer *paste_get_index(struct paste_stack *, u_int);
+int paste_free_top(struct paste_stack *);
+int paste_free_index(struct paste_stack *, u_int);
+void paste_add(struct paste_stack *, char *, u_int);
+int paste_replace(struct paste_stack *, u_int, char *);
+
+/* clock.c */
+void clock_draw(struct screen_write_ctx *, u_int, int);
+
+/* arg.c */
+struct client *arg_parse_client(const char *);
+struct session *arg_parse_session(const char *);
+int arg_parse_window(const char *, struct session **, int *);
+
+/* cmd.c */
+struct cmd *cmd_parse(int, char **, char **);
+int cmd_exec(struct cmd *, struct cmd_ctx *);
+void cmd_send(struct cmd *, struct buffer *);
+struct cmd *cmd_recv(struct buffer *);
+void cmd_free(struct cmd *);
+size_t cmd_print(struct cmd *, char *, size_t);
+void cmd_send_string(struct buffer *, const char *);
+char *cmd_recv_string(struct buffer *);
+struct session *cmd_current_session(struct cmd_ctx *);
+struct client *cmd_find_client(struct cmd_ctx *, const char *);
+struct session *cmd_find_session(struct cmd_ctx *, const char *);
+struct winlink *cmd_find_window(
+ struct cmd_ctx *, const char *, struct session **);
+extern const struct cmd_entry *cmd_table[];
+extern const struct cmd_entry cmd_attach_session_entry;
+extern const struct cmd_entry cmd_bind_key_entry;
+extern const struct cmd_entry cmd_break_pane_entry;
+extern const struct cmd_entry cmd_choose_session_entry;
+extern const struct cmd_entry cmd_choose_window_entry;
+extern const struct cmd_entry cmd_clear_history_entry;
+extern const struct cmd_entry cmd_clock_mode_entry;
+extern const struct cmd_entry cmd_command_prompt_entry;
+extern const struct cmd_entry cmd_confirm_before_entry;
+extern const struct cmd_entry cmd_copy_buffer_entry;
+extern const struct cmd_entry cmd_copy_mode_entry;
+extern const struct cmd_entry cmd_delete_buffer_entry;
+extern const struct cmd_entry cmd_detach_client_entry;
+extern const struct cmd_entry cmd_down_pane_entry;
+extern const struct cmd_entry cmd_find_window_entry;
+extern const struct cmd_entry cmd_has_session_entry;
+extern const struct cmd_entry cmd_kill_pane_entry;
+extern const struct cmd_entry cmd_kill_server_entry;
+extern const struct cmd_entry cmd_kill_session_entry;
+extern const struct cmd_entry cmd_kill_window_entry;
+extern const struct cmd_entry cmd_last_window_entry;
+extern const struct cmd_entry cmd_link_window_entry;
+extern const struct cmd_entry cmd_list_buffers_entry;
+extern const struct cmd_entry cmd_list_clients_entry;
+extern const struct cmd_entry cmd_list_commands_entry;
+extern const struct cmd_entry cmd_list_keys_entry;
+extern const struct cmd_entry cmd_list_sessions_entry;
+extern const struct cmd_entry cmd_list_windows_entry;
+extern const struct cmd_entry cmd_load_buffer_entry;
+extern const struct cmd_entry cmd_lock_server_entry;
+extern const struct cmd_entry cmd_move_window_entry;
+extern const struct cmd_entry cmd_new_session_entry;
+extern const struct cmd_entry cmd_new_window_entry;
+extern const struct cmd_entry cmd_next_layout_entry;
+extern const struct cmd_entry cmd_next_window_entry;
+extern const struct cmd_entry cmd_paste_buffer_entry;
+extern const struct cmd_entry cmd_previous_layout_entry;
+extern const struct cmd_entry cmd_previous_window_entry;
+extern const struct cmd_entry cmd_refresh_client_entry;
+extern const struct cmd_entry cmd_rename_session_entry;
+extern const struct cmd_entry cmd_rename_window_entry;
+extern const struct cmd_entry cmd_resize_pane_entry;
+extern const struct cmd_entry cmd_respawn_window_entry;
+extern const struct cmd_entry cmd_rotate_window_entry;
+extern const struct cmd_entry cmd_save_buffer_entry;
+extern const struct cmd_entry cmd_scroll_mode_entry;
+extern const struct cmd_entry cmd_select_layout_entry;
+extern const struct cmd_entry cmd_select_pane_entry;
+extern const struct cmd_entry cmd_select_prompt_entry;
+extern const struct cmd_entry cmd_select_window_entry;
+extern const struct cmd_entry cmd_send_keys_entry;
+extern const struct cmd_entry cmd_send_prefix_entry;
+extern const struct cmd_entry cmd_server_info_entry;
+extern const struct cmd_entry cmd_set_buffer_entry;
+extern const struct cmd_entry cmd_set_option_entry;
+extern const struct cmd_entry cmd_set_password_entry;
+extern const struct cmd_entry cmd_set_window_option_entry;
+extern const struct cmd_entry cmd_show_buffer_entry;
+extern const struct cmd_entry cmd_show_options_entry;
+extern const struct cmd_entry cmd_show_window_options_entry;
+extern const struct cmd_entry cmd_source_file_entry;
+extern const struct cmd_entry cmd_split_window_entry;
+extern const struct cmd_entry cmd_start_server_entry;
+extern const struct cmd_entry cmd_suspend_client_entry;
+extern const struct cmd_entry cmd_swap_pane_entry;
+extern const struct cmd_entry cmd_swap_window_entry;
+extern const struct cmd_entry cmd_switch_client_entry;
+extern const struct cmd_entry cmd_unbind_key_entry;
+extern const struct cmd_entry cmd_unlink_window_entry;
+extern const struct cmd_entry cmd_up_pane_entry;
+
+/* cmd-list.c */
+struct cmd_list *cmd_list_parse(int, char **, char **);
+int cmd_list_exec(struct cmd_list *, struct cmd_ctx *);
+void cmd_list_send(struct cmd_list *, struct buffer *);
+struct cmd_list *cmd_list_recv(struct buffer *);
+void cmd_list_free(struct cmd_list *);
+size_t cmd_list_print(struct cmd_list *, char *, size_t);
+
+/* cmd-string.c */
+int cmd_string_parse(const char *, struct cmd_list **, char **);
+
+/* cmd-generic.c */
+size_t cmd_prarg(char *, size_t, const char *, char *);
+#define CMD_TARGET_WINDOW_USAGE "[-t target-window]"
+#define CMD_TARGET_SESSION_USAGE "[-t target-session]"
+#define CMD_TARGET_CLIENT_USAGE "[-t target-client]"
+void cmd_target_init(struct cmd *, int);
+int cmd_target_parse(struct cmd *, int, char **, char **);
+void cmd_target_send(struct cmd *, struct buffer *);
+void cmd_target_recv(struct cmd *, struct buffer *);
+void cmd_target_free(struct cmd *);
+size_t cmd_target_print(struct cmd *, char *, size_t);
+#define CMD_SRCDST_WINDOW_USAGE "[-s src-window] [-t dst-window]"
+#define CMD_SRCDST_SESSION_USAGE "[-s src-session] [-t dst-session]"
+#define CMD_SRCDST_CLIENT_USAGE "[-s src-client] [-t dst-client]"
+void cmd_srcdst_init(struct cmd *, int);
+int cmd_srcdst_parse(struct cmd *, int, char **, char **);
+void cmd_srcdst_send(struct cmd *, struct buffer *);
+void cmd_srcdst_recv(struct cmd *, struct buffer *);
+void cmd_srcdst_free(struct cmd *);
+size_t cmd_srcdst_print(struct cmd *, char *, size_t);
+#define CMD_BUFFER_WINDOW_USAGE "[-b buffer-index] [-t target-window]"
+#define CMD_BUFFER_SESSION_USAGE "[-b buffer-index] [-t target-session]"
+#define CMD_BUFFER_CLIENT_USAGE "[-b buffer-index] [-t target-client]"
+void cmd_buffer_init(struct cmd *, int);
+int cmd_buffer_parse(struct cmd *, int, char **, char **);
+void cmd_buffer_send(struct cmd *, struct buffer *);
+void cmd_buffer_recv(struct cmd *, struct buffer *);
+void cmd_buffer_free(struct cmd *);
+size_t cmd_buffer_print(struct cmd *, char *, size_t);
+#define CMD_OPTION_WINDOW_USAGE "[-gu] [-t target-window] option [value]"
+#define CMD_OPTION_SESSION_USAGE "[-gu] [-t target-session] option [value]"
+#define CMD_OPTION_CLIENT_USAGE "[-gu] [-t target-client] option [value]"
+void cmd_option_init(struct cmd *, int);
+int cmd_option_parse(struct cmd *, int, char **, char **);
+void cmd_option_send(struct cmd *, struct buffer *);
+void cmd_option_recv(struct cmd *, struct buffer *);
+void cmd_option_free(struct cmd *);
+size_t cmd_option_print(struct cmd *, char *, size_t);
+#define CMD_PANE_WINDOW_USAGE "[-t target-window] [-p pane-index]"
+#define CMD_PANE_SESSION_USAGE "[-t target-session] [-p pane-index]"
+#define CMD_PANE_CLIENT_USAGE "[-t target-client] [-p pane-index]"
+void cmd_pane_init(struct cmd *, int);
+int cmd_pane_parse(struct cmd *, int, char **, char **);
+void cmd_pane_send(struct cmd *, struct buffer *);
+void cmd_pane_recv(struct cmd *, struct buffer *);
+void cmd_pane_free(struct cmd *);
+size_t cmd_pane_print(struct cmd *, char *, size_t);
+
+/* client.c */
+int client_init(char *, struct client_ctx *, int, int);
+int client_flush(struct client_ctx *);
+int client_main(struct client_ctx *);
+
+/* client-msg.c */
+int client_msg_dispatch(struct client_ctx *, char **);
+
+/* client-fn.c */
+void client_write_server(struct client_ctx *, enum hdrtype, void *, size_t);
+void client_write_server2(
+ struct client_ctx *, enum hdrtype, void *, size_t, void *, size_t);
+void client_fill_session(struct msg_command_data *);
+
+/* key-bindings.c */
+extern struct key_bindings key_bindings;
+int key_bindings_cmp(struct key_binding *, struct key_binding *);
+SPLAY_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp);
+struct key_binding *key_bindings_lookup(int);
+void key_bindings_add(int, int, struct cmd_list *);
+void key_bindings_remove(int);
+void key_bindings_init(void);
+void key_bindings_free(void);
+void key_bindings_dispatch(struct key_binding *, struct client *);
+void printflike2 key_bindings_error(struct cmd_ctx *, const char *, ...);
+void printflike2 key_bindings_print(struct cmd_ctx *, const char *, ...);
+void printflike2 key_bindings_info(struct cmd_ctx *, const char *, ...);
+
+/* key-string.c */
+int key_string_lookup_string(const char *);
+const char *key_string_lookup_key(int);
+
+/* server.c */
+extern struct clients clients;
+struct client *server_create_client(int);
+int server_client_index(struct client *);
+int server_start(char *);
+
+/* server-msg.c */
+int server_msg_dispatch(struct client *);
+
+/* server-fn.c */
+const char **server_fill_environ(struct session *);
+void server_write_client(
+ struct client *, enum hdrtype, const void *, size_t);
+void server_write_session(
+ struct session *, enum hdrtype, const void *, size_t);
+void server_write_window(
+ struct window *, enum hdrtype, const void *, size_t);
+void server_redraw_client(struct client *);
+void server_status_client(struct client *);
+void server_redraw_session(struct session *);
+void server_status_session(struct session *);
+void server_redraw_window(struct window *);
+void server_status_window(struct window *);
+void server_lock(void);
+int server_unlock(const char *);
+
+/* status.c */
+int status_redraw(struct client *);
+void status_message_set(struct client *, const char *);
+void status_message_clear(struct client *);
+int status_message_redraw(struct client *);
+void status_prompt_set(struct client *,
+ const char *, int (*)(void *, const char *), void *, int);
+void status_prompt_clear(struct client *);
+int status_prompt_redraw(struct client *);
+void status_prompt_key(struct client *, int);
+
+/* resize.c */
+void recalculate_sizes(void);
+
+/* input.c */
+void input_init(struct window_pane *);
+void input_free(struct window_pane *);
+void input_parse(struct window_pane *);
+
+/* input-key.c */
+void input_key(struct window_pane *, int);
+void input_mouse(struct window_pane *, u_char, u_char, u_char);
+
+/* colour.c */
+const char *colour_tostring(u_char);
+int colour_fromstring(const char *);
+u_char colour_256to16(u_char);
+u_char colour_256to88(u_char);
+
+/* attributes.c */
+const char *attributes_tostring(u_char);
+int attributes_fromstring(const char *);
+
+/* grid.c */
+extern const struct grid_cell grid_default_cell;
+struct grid *grid_create(u_int, u_int, u_int);
+void grid_destroy(struct grid *);
+int grid_compare(struct grid *, struct grid *);
+void grid_reduce_line(struct grid *, u_int, u_int);
+void grid_expand_line(struct grid *, u_int, u_int);
+void grid_expand_line_utf8(struct grid *, u_int, u_int);
+void grid_scroll_line(struct grid *);
+const struct grid_cell *grid_peek_cell(struct grid *, u_int, u_int);
+struct grid_cell *grid_get_cell(struct grid *, u_int, u_int);
+void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *);
+const struct grid_utf8 *grid_peek_utf8(struct grid *, u_int, u_int);
+struct grid_utf8 *grid_get_utf8(struct grid *, u_int, u_int);
+void grid_set_utf8(struct grid *, u_int, u_int, const struct grid_utf8 *);
+void grid_clear(struct grid *, u_int, u_int, u_int, u_int);
+void grid_clear_lines(struct grid *, u_int, u_int);
+void grid_move_lines(struct grid *, u_int, u_int, u_int);
+void grid_clear_cells(struct grid *, u_int, u_int, u_int);
+void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int);
+
+/* grid-view.c */
+const struct grid_cell *grid_view_peek_cell(struct grid *, u_int, u_int);
+struct grid_cell *grid_view_get_cell(struct grid *, u_int, u_int);
+void grid_view_set_cell(
+ struct grid *, u_int, u_int, const struct grid_cell *);
+const struct grid_utf8 *grid_view_peek_utf8(struct grid *, u_int, u_int);
+struct grid_utf8 *grid_view_get_utf8(struct grid *, u_int, u_int);
+void grid_view_set_utf8(
+ struct grid *, u_int, u_int, const struct grid_utf8 *);
+void grid_view_clear(struct grid *, u_int, u_int, u_int, u_int);
+void grid_view_scroll_region_up(struct grid *, u_int, u_int);
+void grid_view_scroll_region_down(struct grid *, u_int, u_int);
+void grid_view_insert_lines(struct grid *, u_int, u_int);
+void grid_view_insert_lines_region(
+ struct grid *, u_int, u_int, u_int, u_int);
+void grid_view_delete_lines(struct grid *, u_int, u_int);
+void grid_view_delete_lines_region(
+ struct grid *, u_int, u_int, u_int, u_int);
+void grid_view_insert_cells(struct grid *, u_int, u_int, u_int);
+void grid_view_delete_cells(struct grid *, u_int, u_int, u_int);
+
+/* screen-write.c */
+void screen_write_start(
+ struct screen_write_ctx *, struct window_pane *, struct screen *);
+void screen_write_stop(struct screen_write_ctx *);
+void printflike3 screen_write_puts(
+ struct screen_write_ctx *, struct grid_cell *, const char *, ...);
+void screen_write_putc(
+ struct screen_write_ctx *, struct grid_cell *, u_char);
+void screen_write_copy(struct screen_write_ctx *,
+ struct screen *, u_int, u_int, u_int, u_int);
+void screen_write_cursorup(struct screen_write_ctx *, u_int);
+void screen_write_cursordown(struct screen_write_ctx *, u_int);
+void screen_write_cursorright(struct screen_write_ctx *, u_int);
+void screen_write_cursorleft(struct screen_write_ctx *, u_int);
+void screen_write_insertcharacter(struct screen_write_ctx *, u_int);
+void screen_write_deletecharacter(struct screen_write_ctx *, u_int);
+void screen_write_insertline(struct screen_write_ctx *, u_int);
+void screen_write_deleteline(struct screen_write_ctx *, u_int);
+void screen_write_clearline(struct screen_write_ctx *);
+void screen_write_clearendofline(struct screen_write_ctx *);
+void screen_write_clearstartofline(struct screen_write_ctx *);
+void screen_write_cursormove(struct screen_write_ctx *, u_int, u_int);
+void screen_write_cursormode(struct screen_write_ctx *, int);
+void screen_write_reverseindex(struct screen_write_ctx *);
+void screen_write_scrollregion(struct screen_write_ctx *, u_int, u_int);
+void screen_write_insertmode(struct screen_write_ctx *, int);
+void screen_write_mousemode(struct screen_write_ctx *, int);
+void screen_write_linefeed(struct screen_write_ctx *);
+void screen_write_carriagereturn(struct screen_write_ctx *);
+void screen_write_kcursormode(struct screen_write_ctx *, int);
+void screen_write_kkeypadmode(struct screen_write_ctx *, int);
+void screen_write_clearendofscreen(struct screen_write_ctx *);
+void screen_write_clearstartofscreen(struct screen_write_ctx *);
+void screen_write_clearscreen(struct screen_write_ctx *);
+void screen_write_cell(
+ struct screen_write_ctx *, const struct grid_cell *, u_char *);
+
+/* screen-redraw.c */
+void screen_redraw_screen(struct client *);
+void screen_redraw_pane(struct client *, struct window_pane *);
+void screen_redraw_status(struct client *);
+
+/* screen.c */
+void screen_init(struct screen *, u_int, u_int, u_int);
+void screen_reinit(struct screen *);
+void screen_free(struct screen *);
+void screen_set_title(struct screen *, const char *);
+void screen_resize(struct screen *, u_int, u_int);
+void screen_set_selection(
+ struct screen *, u_int, u_int, u_int, u_int, struct grid_cell *);
+void screen_clear_selection(struct screen *);
+int screen_check_selection(struct screen *, u_int, u_int);
+void screen_display_copy_area(struct screen *, struct screen *,
+ u_int, u_int, u_int, u_int, u_int, u_int);
+
+/* window.c */
+extern struct windows windows;
+int window_cmp(struct window *, struct window *);
+int winlink_cmp(struct winlink *, struct winlink *);
+RB_PROTOTYPE(windows, window, entry, window_cmp);
+RB_PROTOTYPE(winlinks, winlink, entry, winlink_cmp);
+struct winlink *winlink_find_by_index(struct winlinks *, int);
+struct winlink *winlink_find_by_window(struct winlinks *, struct window *);
+int winlink_next_index(struct winlinks *);
+u_int winlink_count(struct winlinks *);
+struct winlink *winlink_add(struct winlinks *, struct window *, int);
+void winlink_remove(struct winlinks *, struct winlink *);
+struct winlink *winlink_next(struct winlinks *, struct winlink *);
+struct winlink *winlink_previous(struct winlinks *, struct winlink *);
+void winlink_stack_push(struct winlink_stack *, struct winlink *);
+void winlink_stack_remove(struct winlink_stack *, struct winlink *);
+int window_index(struct window *, u_int *);
+struct window *window_create1(u_int, u_int);
+struct window *window_create(const char *, const char *,
+ const char *, const char **, u_int, u_int, u_int, char **);
+void window_destroy(struct window *);
+int window_resize(struct window *, u_int, u_int);
+void window_set_active_pane(struct window *, struct window_pane *);
+struct window_pane *window_add_pane(struct window *, int,
+ const char *, const char *, const char **, u_int, char **);
+void window_remove_pane(struct window *, struct window_pane *);
+u_int window_index_of_pane(struct window *, struct window_pane *);
+struct window_pane *window_pane_at_index(struct window *, u_int);
+u_int window_count_panes(struct window *);
+void window_destroy_panes(struct window *);
+struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int);
+void window_pane_destroy(struct window_pane *);
+int window_pane_spawn(struct window_pane *,
+ const char *, const char *, const char **, char **);
+int window_pane_resize(struct window_pane *, u_int, u_int);
+void window_calculate_sizes(struct window *);
+int window_pane_set_mode(
+ struct window_pane *, const struct window_mode *);
+void window_pane_reset_mode(struct window_pane *);
+void window_pane_parse(struct window_pane *);
+void window_pane_key(struct window_pane *, struct client *, int);
+void window_pane_mouse(struct window_pane *,
+ struct client *, u_char, u_char, u_char);
+char *window_pane_search(struct window_pane *, const char *);
+
+/* layout.c */
+const char * layout_name(struct window *);
+int layout_lookup(const char *);
+void layout_refresh(struct window *, int);
+int layout_resize(struct window_pane *, int);
+int layout_select(struct window *, u_int);
+void layout_next(struct window *);
+void layout_previous(struct window *);
+
+/* layout-manual.c */
+void layout_manual_v_refresh(struct window *, int);
+void layout_manual_v_resize(struct window_pane *, int);
+
+/* window-clock.c */
+extern const struct window_mode window_clock_mode;
+
+/* window-copy.c */
+extern const struct window_mode window_copy_mode;
+void window_copy_pageup(struct window_pane *);
+
+/* window-scroll.c */
+extern const struct window_mode window_scroll_mode;
+void window_scroll_pageup(struct window_pane *);
+
+/* window-more.c */
+extern const struct window_mode window_more_mode;
+void window_more_vadd(struct window_pane *, const char *, va_list);
+void printflike2 window_more_add(struct window_pane *, const char *, ...);
+
+/* window-choose.c */
+extern const struct window_mode window_choose_mode;
+void window_choose_vadd(
+ struct window_pane *, int, const char *, va_list);
+void printflike3 window_choose_add(
+ struct window_pane *, int, const char *, ...);
+void window_choose_ready(struct window_pane *,
+ u_int, void (*)(void *, int), void *);
+
+/* names.c */
+void set_window_names(void);
+char *default_window_name(struct window *);
+
+/* session.c */
+extern struct sessions sessions;
+void session_alert_add(struct session *, struct window *, int);
+void session_alert_cancel(struct session *, struct winlink *);
+int session_alert_has(struct session *, struct winlink *, int);
+int session_alert_has_window(struct session *, struct window *, int);
+struct session *session_find(const char *);
+struct session *session_create(const char *, const char *,
+ const char *, u_int, u_int, char **);
+void session_destroy(struct session *);
+int session_index(struct session *, u_int *);
+struct winlink *session_new(struct session *,
+ const char *, const char *, const char *, int, char **);
+struct winlink *session_attach(
+ struct session *, struct window *, int, char **);
+int session_detach(struct session *, struct winlink *);
+int session_has(struct session *, struct window *);
+int session_next(struct session *, int);
+int session_previous(struct session *, int);
+int session_select(struct session *, int);
+int session_last(struct session *);
+
+/* utf8.c */
+void utf8_build(void);
+int utf8_width(u_char *);
+
+/* util.c */
+char *section_string(char *, size_t, size_t, size_t);
+void clean_string(const char *, char *, size_t);
+
+/* procname.c */
+char *get_proc_name(int, char *);
+
+/* buffer.c */
+struct buffer *buffer_create(size_t);
+void buffer_destroy(struct buffer *);
+void buffer_clear(struct buffer *);
+void buffer_ensure(struct buffer *, size_t);
+void buffer_add(struct buffer *, size_t);
+void buffer_reverse_add(struct buffer *, size_t);
+void buffer_remove(struct buffer *, size_t);
+void buffer_reverse_remove(struct buffer *, size_t);
+void buffer_insert_range(struct buffer *, size_t, size_t);
+void buffer_delete_range(struct buffer *, size_t, size_t);
+void buffer_write(struct buffer *, const void *, size_t);
+void buffer_read(struct buffer *, void *, size_t);
+void buffer_write8(struct buffer *, uint8_t);
+void buffer_write16(struct buffer *, uint16_t);
+uint8_t buffer_read8(struct buffer *);
+uint16_t buffer_read16(struct buffer *);
+
+/* buffer-poll.c */
+void buffer_set(
+ struct pollfd *, int, struct buffer *, struct buffer *);
+int buffer_poll(struct pollfd *, struct buffer *, struct buffer *);
+void buffer_flush(int, struct buffer *n, struct buffer *);
+
+/* log.c */
+#define LOG_FACILITY LOG_DAEMON
+void log_open_syslog(int);
+void log_open_tty(int);
+void log_open_file(int, const char *);
+void log_close(void);
+void log_vwrite(int, const char *, va_list);
+void log_write(int, const char *, ...);
+void printflike1 log_warn(const char *, ...);
+void printflike1 log_warnx(const char *, ...);
+void printflike1 log_info(const char *, ...);
+void printflike1 log_debug(const char *, ...);
+void printflike1 log_debug2(const char *, ...);
+void printflike1 log_debug3(const char *, ...);
+__dead void log_vfatal(const char *, va_list);
+__dead void log_fatal(const char *, ...);
+__dead void log_fatalx(const char *, ...);
+
+/* xmalloc.c */
+char *xstrdup(const char *);
+void *xcalloc(size_t, size_t);
+void *xmalloc(size_t);
+void *xrealloc(void *, size_t, size_t);
+void xfree(void *);
+int printflike2 xasprintf(char **, const char *, ...);
+int xvasprintf(char **, const char *, va_list);
+int printflike3 xsnprintf(char *, size_t, const char *, ...);
+int xvsnprintf(char *, size_t, const char *, va_list);
+int printflike3 printpath(char *, size_t, const char *, ...);
+
+#endif /* TMUX_H */
diff --git a/usr.bin/tmux/tty-keys.c b/usr.bin/tmux/tty-keys.c
new file mode 100644
index 00000000000..524da45bcf7
--- /dev/null
+++ b/usr.bin/tmux/tty-keys.c
@@ -0,0 +1,412 @@
+/* $OpenBSD: tty-keys.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/time.h>
+
+#include <string.h>
+
+#include "tmux.h"
+
+void tty_keys_add(struct tty *, const char *, int, int);
+int tty_keys_parse_xterm(struct tty *, char *, size_t, size_t *);
+int tty_keys_parse_mouse(struct tty *, char *, size_t, size_t *, u_char *);
+
+struct tty_key_ent {
+ enum tty_code_code code;
+ const char *string;
+
+ int key;
+ int flags;
+};
+
+struct tty_key_ent tty_keys[] = {
+ /* Function keys. */
+ { TTYC_KF1, NULL, KEYC_F1, TTYKEY_CTRL },
+ { TTYC_KF2, NULL, KEYC_F2, TTYKEY_CTRL },
+ { TTYC_KF3, NULL, KEYC_F3, TTYKEY_CTRL },
+ { TTYC_KF4, NULL, KEYC_F4, TTYKEY_CTRL },
+ { TTYC_KF5, NULL, KEYC_F5, TTYKEY_CTRL },
+ { TTYC_KF6, NULL, KEYC_F6, TTYKEY_CTRL },
+ { TTYC_KF7, NULL, KEYC_F7, TTYKEY_CTRL },
+ { TTYC_KF8, NULL, KEYC_F8, TTYKEY_CTRL },
+ { TTYC_KF9, NULL, KEYC_F9, TTYKEY_CTRL },
+ { TTYC_KF10, NULL, KEYC_F10, TTYKEY_CTRL },
+ { TTYC_KF11, NULL, KEYC_F11, TTYKEY_CTRL },
+ { TTYC_KF12, NULL, KEYC_F12, TTYKEY_CTRL },
+ { TTYC_KF13, NULL, KEYC_F13, TTYKEY_CTRL },
+ { TTYC_KF14, NULL, KEYC_F14, TTYKEY_CTRL },
+ { TTYC_KF15, NULL, KEYC_F15, TTYKEY_CTRL },
+ { TTYC_KF16, NULL, KEYC_F16, TTYKEY_CTRL },
+ { TTYC_KF17, NULL, KEYC_F17, TTYKEY_CTRL },
+ { TTYC_KF18, NULL, KEYC_F18, TTYKEY_CTRL },
+ { TTYC_KF19, NULL, KEYC_F19, TTYKEY_CTRL },
+ { TTYC_KF20, NULL, KEYC_F20, TTYKEY_CTRL },
+ { TTYC_KICH1, NULL, KEYC_IC, TTYKEY_CTRL },
+ { TTYC_KDCH1, NULL, KEYC_DC, TTYKEY_CTRL },
+ { TTYC_KHOME, NULL, KEYC_HOME, TTYKEY_CTRL },
+ { TTYC_KEND, NULL, KEYC_END, TTYKEY_CTRL },
+ { TTYC_KNP, NULL, KEYC_NPAGE, TTYKEY_CTRL },
+ { TTYC_KPP, NULL, KEYC_PPAGE, TTYKEY_CTRL },
+ { TTYC_KCBT, NULL, KEYC_BTAB, TTYKEY_CTRL },
+
+ /* Arrow keys. */
+ { 0, "\033OA", KEYC_UP, TTYKEY_RAW },
+ { 0, "\033OB", KEYC_DOWN, TTYKEY_RAW },
+ { 0, "\033OC", KEYC_RIGHT, TTYKEY_RAW },
+ { 0, "\033OD", KEYC_LEFT, TTYKEY_RAW },
+
+ { 0, "\033[A", KEYC_UP, TTYKEY_RAW },
+ { 0, "\033[B", KEYC_DOWN, TTYKEY_RAW },
+ { 0, "\033[C", KEYC_RIGHT, TTYKEY_RAW },
+ { 0, "\033[D", KEYC_LEFT, TTYKEY_RAW },
+
+ { 0, "\033Oa", KEYC_ADDCTL(KEYC_UP), TTYKEY_RAW },
+ { 0, "\033Ob", KEYC_ADDCTL(KEYC_DOWN), TTYKEY_RAW },
+ { 0, "\033Oc", KEYC_ADDCTL(KEYC_RIGHT), TTYKEY_RAW },
+ { 0, "\033Od", KEYC_ADDCTL(KEYC_LEFT), TTYKEY_RAW },
+ { 0, "\033[a", KEYC_ADDSFT(KEYC_UP), TTYKEY_RAW },
+ { 0, "\033[b", KEYC_ADDSFT(KEYC_DOWN), TTYKEY_RAW },
+ { 0, "\033[c", KEYC_ADDSFT(KEYC_RIGHT), TTYKEY_RAW },
+ { 0, "\033[d", KEYC_ADDSFT(KEYC_LEFT), TTYKEY_RAW },
+
+ { TTYC_KCUU1, NULL, KEYC_UP, TTYKEY_CTRL },
+ { TTYC_KCUD1, NULL, KEYC_DOWN, TTYKEY_CTRL },
+ { TTYC_KCUB1, NULL, KEYC_LEFT, TTYKEY_CTRL },
+ { TTYC_KCUF1, NULL, KEYC_RIGHT, TTYKEY_CTRL },
+
+ /*
+ * Numeric keypad. termcap and terminfo are totally confusing for this.
+ * There are definitions for some keypad keys and for function keys,
+ * but these seem to now be used for the real function keys rather than
+ * for the keypad keys in application mode (which is different from
+ * what it says in the termcap file). So, we just hardcode the vt100
+ * escape sequences here and always put the terminal into keypad_xmit
+ * mode. Translation of numbers mode/applications mode is done in
+ * input-keys.c.
+ */
+ { 0, "\033Oo", KEYC_KP0_1, TTYKEY_RAW },
+ { 0, "\033Oj", KEYC_KP0_2, TTYKEY_RAW },
+ { 0, "\033Om", KEYC_KP0_3, TTYKEY_RAW },
+ { 0, "\033Ow", KEYC_KP1_0, TTYKEY_RAW },
+ { 0, "\033Ox", KEYC_KP1_1, TTYKEY_RAW },
+ { 0, "\033Oy", KEYC_KP1_2, TTYKEY_RAW },
+ { 0, "\033Ok", KEYC_KP1_3, TTYKEY_RAW },
+ { 0, "\033Ot", KEYC_KP2_0, TTYKEY_RAW },
+ { 0, "\033Ou", KEYC_KP2_1, TTYKEY_RAW },
+ { 0, "\033Ov", KEYC_KP2_2, TTYKEY_RAW },
+ { 0, "\033Oq", KEYC_KP3_0, TTYKEY_RAW },
+ { 0, "\033Or", KEYC_KP3_1, TTYKEY_RAW },
+ { 0, "\033Os", KEYC_KP3_2, TTYKEY_RAW },
+ { 0, "\033OM", KEYC_KP3_3, TTYKEY_RAW },
+ { 0, "\033Op", KEYC_KP4_0, TTYKEY_RAW },
+ { 0, "\033On", KEYC_KP4_2, TTYKEY_RAW },
+};
+
+RB_GENERATE(tty_keys, tty_key, entry, tty_keys_cmp);
+
+struct tty_key *tty_keys_find(struct tty *, char *, size_t, size_t *);
+
+int
+tty_keys_cmp(struct tty_key *k1, struct tty_key *k2)
+{
+ return (strcmp(k1->string, k2->string));
+}
+
+void
+tty_keys_add(struct tty *tty, const char *s, int key, int flags)
+{
+ struct tty_key *tk, *tl;
+
+ tk = xmalloc(sizeof *tk);
+ tk->string = xstrdup(s);
+ tk->key = key;
+ tk->flags = flags;
+
+ if ((tl = RB_INSERT(tty_keys, &tty->ktree, tk)) != NULL) {
+ xfree(tk->string);
+ xfree(tk);
+ log_debug("key exists: %s (old %x, new %x)", s, tl->key, key);
+ return;
+ }
+
+ if (strlen(tk->string) > tty->ksize)
+ tty->ksize = strlen(tk->string);
+ log_debug("new key %x: size now %zu (%s)", key, tty->ksize, tk->string);
+}
+
+void
+tty_keys_init(struct tty *tty)
+{
+ struct tty_key_ent *tke;
+ u_int i;
+ const char *s;
+ char tmp[64];
+
+ RB_INIT(&tty->ktree);
+
+ tty->ksize = 0;
+ for (i = 0; i < nitems(tty_keys); i++) {
+ tke = &tty_keys[i];
+
+ if (tke->flags & TTYKEY_RAW)
+ s = tke->string;
+ else {
+ if (!tty_term_has(tty->term, tke->code))
+ continue;
+ s = tty_term_string(tty->term, tke->code);
+ if (s[0] != '\033' || s[1] == '\0')
+ continue;
+ }
+
+ tty_keys_add(tty, s + 1, tke->key, tke->flags);
+ if (tke->flags & TTYKEY_CTRL) {
+ if (strlcpy(tmp, s, sizeof tmp) >= sizeof tmp)
+ continue;
+ tmp[strlen(tmp) - 1] ^= 0x20;
+ tty_keys_add(tty, tmp + 1, KEYC_ADDCTL(tke->key), 0);
+ }
+ }
+}
+
+void
+tty_keys_free(struct tty *tty)
+{
+ struct tty_key *tk;
+
+ while (!RB_EMPTY(&tty->ktree)) {
+ tk = RB_ROOT(&tty->ktree);
+ RB_REMOVE(tty_keys, &tty->ktree, tk);
+ xfree(tk->string);
+ xfree(tk);
+ }
+}
+
+struct tty_key *
+tty_keys_find(struct tty *tty, char *buf, size_t len, size_t *size)
+{
+ struct tty_key *tk, tl;
+ char *s;
+
+ if (len == 0)
+ return (NULL);
+
+ s = xmalloc(tty->ksize + 1);
+ for (*size = tty->ksize; (*size) > 0; (*size)--) {
+ if ((*size) > len)
+ continue;
+ memcpy(s, buf, *size);
+ s[*size] = '\0';
+
+ log_debug2("looking for key: %s", s);
+
+ tl.string = s;
+ tk = RB_FIND(tty_keys, &tty->ktree, &tl);
+ if (tk != NULL) {
+ log_debug2("got key: 0x%x", tk->key);
+ xfree(s);
+ return (tk);
+ }
+ }
+ xfree(s);
+
+ return (NULL);
+}
+
+int
+tty_keys_next(struct tty *tty, int *key, u_char *mouse)
+{
+ struct tty_key *tk;
+ struct timeval tv;
+ char *buf;
+ size_t len, size;
+
+ buf = BUFFER_OUT(tty->in);
+ len = BUFFER_USED(tty->in);
+ if (len == 0)
+ return (1);
+ log_debug("keys are %zu (%.*s)", len, (int) len, buf);
+
+ /* If a normal key, return it. */
+ if (*buf != '\033') {
+ *key = buffer_read8(tty->in);
+ goto found;
+ }
+
+ /* Look for matching key string and return if found. */
+ tk = tty_keys_find(tty, buf + 1, len - 1, &size);
+ if (tk != NULL) {
+ buffer_remove(tty->in, size + 1);
+ *key = tk->key;
+ goto found;
+ }
+
+ /* Not found. Is this a mouse key press? */
+ *key = tty_keys_parse_mouse(tty, buf, len, &size, mouse);
+ if (*key != KEYC_NONE) {
+ buffer_remove(tty->in, size);
+ goto found;
+ }
+
+ /* Not found. Try to parse xterm-type arguments. */
+ *key = tty_keys_parse_xterm(tty, buf, len, &size);
+ if (*key != KEYC_NONE) {
+ buffer_remove(tty->in, size);
+ goto found;
+ }
+
+ /* Escape but no key string. If the timer isn't started, start it. */
+ if (!(tty->flags & TTY_ESCAPE)) {
+ tv.tv_sec = 0;
+ tv.tv_usec = ESCAPE_PERIOD * 1000L;
+ if (gettimeofday(&tty->key_timer, NULL) != 0)
+ fatal("gettimeofday");
+ timeradd(&tty->key_timer, &tv, &tty->key_timer);
+
+ tty->flags |= TTY_ESCAPE;
+ return (1);
+ }
+
+ /* Skip the escape. */
+ buf++;
+ len--;
+
+ /* Is there a normal key following? */
+ if (len != 0 && *buf != '\033') {
+ buffer_remove(tty->in, 1);
+ *key = KEYC_ADDESC(buffer_read8(tty->in));
+ goto found;
+ }
+
+ /* Or a key string? */
+ if (len > 1) {
+ tk = tty_keys_find(tty, buf + 1, len - 1, &size);
+ if (tk != NULL) {
+ buffer_remove(tty->in, size + 2);
+ *key = KEYC_ADDESC(tk->key);
+ goto found;
+ }
+ }
+
+ /* If the timer hasn't expired, keep waiting. */
+ if (gettimeofday(&tv, NULL) != 0)
+ fatal("gettimeofday");
+ if (timercmp(&tty->key_timer, &tv, >))
+ return (1);
+
+ /* Give up and return the escape. */
+ buffer_remove(tty->in, 1);
+ *key = '\033';
+
+found:
+ tty->flags &= ~TTY_ESCAPE;
+ return (0);
+}
+
+int
+tty_keys_parse_mouse(
+ unused struct tty *tty, char *buf, size_t len, size_t *size, u_char *mouse)
+{
+ /*
+ * Mouse sequences are \033[M followed by three characters indicating
+ * buttons, X and Y, all based at 32 with 1,1 top-left.
+ */
+
+ log_debug("mouse input is: %.*s", (int) len, buf);
+ if (len != 6 || memcmp(buf, "\033[M", 3) != 0)
+ return (KEYC_NONE);
+ *size = 6;
+
+ if (buf[3] < 32 || buf[4] < 33 || buf[5] < 33)
+ return (KEYC_NONE);
+
+ mouse[0] = buf[3] - 32;
+ mouse[1] = buf[4] - 33;
+ mouse[2] = buf[5] - 33;
+ return (KEYC_MOUSE);
+}
+
+int
+tty_keys_parse_xterm(struct tty *tty, char *buf, size_t len, size_t *size)
+{
+ struct tty_key *tk;
+ char tmp[5];
+ size_t tmplen;
+ int key;
+
+ /*
+ * xterm sequences with modifier keys are of the form:
+ *
+ * ^[[1;xD becomes ^[[D
+ * ^[[5;x~ becomes ^[[5~
+ *
+ * This function is a bit of a hack. Need to figure out what exact
+ * format and meaning xterm outputs and fix it. XXX
+ */
+
+ log_debug("xterm input is: %.*s", (int) len, buf);
+ if (len != 6 || memcmp(buf, "\033[1;", 4) != 0)
+ return (KEYC_NONE);
+ *size = 6;
+
+ tmplen = 0;
+ tmp[tmplen++] = '[';
+ if (buf[5] == '~') {
+ tmp[tmplen++] = buf[2];
+ tmp[tmplen++] = '~';
+ } else
+ tmp[tmplen++] = buf[5];
+ log_debug("xterm output is: %.*s", (int) tmplen, tmp);
+
+ tk = tty_keys_find(tty, tmp, tmplen, size);
+ if (tk == NULL)
+ return (KEYC_NONE);
+ key = tk->key;
+
+ switch (buf[4]) {
+ case '8':
+ key = KEYC_ADDSFT(key);
+ key = KEYC_ADDESC(key);
+ key = KEYC_ADDCTL(key);
+ break;
+ case '7':
+ key = KEYC_ADDESC(key);
+ key = KEYC_ADDCTL(key);
+ break;
+ case '6':
+ key = KEYC_ADDSFT(key);
+ key = KEYC_ADDCTL(key);
+ break;
+ case '5':
+ key = KEYC_ADDCTL(key);
+ break;
+ case '4':
+ key = KEYC_ADDSFT(key);
+ key = KEYC_ADDESC(key);
+ break;
+ case '3':
+ key = KEYC_ADDESC(key);
+ break;
+ case '2':
+ key = KEYC_ADDSFT(key);
+ break;
+ }
+
+ *size = 6;
+ return (key);
+}
diff --git a/usr.bin/tmux/tty-term.c b/usr.bin/tmux/tty-term.c
new file mode 100644
index 00000000000..990fbc552ae
--- /dev/null
+++ b/usr.bin/tmux/tty-term.c
@@ -0,0 +1,393 @@
+/* $OpenBSD: tty-term.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <ncurses.h>
+#include <string.h>
+#include <term.h>
+
+#include "tmux.h"
+
+void tty_term_quirks(struct tty_term *);
+char *tty_term_strip(const char *);
+
+struct tty_terms tty_terms = SLIST_HEAD_INITIALIZER(tty_terms);
+
+struct tty_term_code_entry tty_term_codes[NTTYCODE] = {
+ { TTYC_AX, TTYCODE_FLAG, "AX" },
+ { TTYC_ACSC, TTYCODE_STRING, "acsc" },
+ { TTYC_BEL, TTYCODE_STRING, "bel" },
+ { TTYC_BLINK, TTYCODE_STRING, "blink" },
+ { TTYC_BOLD, TTYCODE_STRING, "bold" },
+ { TTYC_CIVIS, TTYCODE_STRING, "civis" },
+ { TTYC_CLEAR, TTYCODE_STRING, "clear" },
+ { TTYC_CNORM, TTYCODE_STRING, "cnorm" },
+ { TTYC_COLORS, TTYCODE_NUMBER, "colors" },
+ { TTYC_CSR, TTYCODE_STRING, "csr" },
+ { TTYC_CUD, TTYCODE_STRING, "cud" },
+ { TTYC_CUD1, TTYCODE_STRING, "cud1" },
+ { TTYC_CUP, TTYCODE_STRING, "cup" },
+ { TTYC_DCH, TTYCODE_STRING, "dch" },
+ { TTYC_DCH1, TTYCODE_STRING, "dch1" },
+ { TTYC_DIM, TTYCODE_STRING, "dim" },
+ { TTYC_DL, TTYCODE_STRING, "dl" },
+ { TTYC_DL1, TTYCODE_STRING, "dl1" },
+ { TTYC_EL, TTYCODE_STRING, "el" },
+ { TTYC_EL1, TTYCODE_STRING, "el1" },
+ { TTYC_ENACS, TTYCODE_STRING, "enacs" },
+ { TTYC_ICH, TTYCODE_STRING, "ich" },
+ { TTYC_ICH1, TTYCODE_STRING, "ich1" },
+ { TTYC_IL, TTYCODE_STRING, "il" },
+ { TTYC_IL1, TTYCODE_STRING, "il1" },
+ { TTYC_INVIS, TTYCODE_STRING, "invis" },
+ { TTYC_IS1, TTYCODE_STRING, "is1" },
+ { TTYC_IS2, TTYCODE_STRING, "is2" },
+ { TTYC_IS3, TTYCODE_STRING, "is3" },
+ { TTYC_KCBT, TTYCODE_STRING, "kcbt" },
+ { TTYC_KCUB1, TTYCODE_STRING, "kcub1" },
+ { TTYC_KCUD1, TTYCODE_STRING, "kcud1" },
+ { TTYC_KCUF1, TTYCODE_STRING, "kcuf1" },
+ { TTYC_KCUU1, TTYCODE_STRING, "kcuu1" },
+ { TTYC_KDCH1, TTYCODE_STRING, "kdch1" },
+ { TTYC_KEND, TTYCODE_STRING, "kend" },
+ { TTYC_KF1, TTYCODE_STRING, "kf1" },
+ { TTYC_KF10, TTYCODE_STRING, "kf10" },
+ { TTYC_KF11, TTYCODE_STRING, "kf11" },
+ { TTYC_KF12, TTYCODE_STRING, "kf12" },
+ { TTYC_KF13, TTYCODE_STRING, "kf13" },
+ { TTYC_KF14, TTYCODE_STRING, "kf14" },
+ { TTYC_KF15, TTYCODE_STRING, "kf15" },
+ { TTYC_KF16, TTYCODE_STRING, "kf16" },
+ { TTYC_KF17, TTYCODE_STRING, "kf17" },
+ { TTYC_KF18, TTYCODE_STRING, "kf18" },
+ { TTYC_KF19, TTYCODE_STRING, "kf19" },
+ { TTYC_KF20, TTYCODE_STRING, "kf20" },
+ { TTYC_KF2, TTYCODE_STRING, "kf2" },
+ { TTYC_KF3, TTYCODE_STRING, "kf3" },
+ { TTYC_KF4, TTYCODE_STRING, "kf4" },
+ { TTYC_KF5, TTYCODE_STRING, "kf5" },
+ { TTYC_KF6, TTYCODE_STRING, "kf6" },
+ { TTYC_KF7, TTYCODE_STRING, "kf7" },
+ { TTYC_KF8, TTYCODE_STRING, "kf8" },
+ { TTYC_KF9, TTYCODE_STRING, "kf9" },
+ { TTYC_KHOME, TTYCODE_STRING, "khome" },
+ { TTYC_KICH1, TTYCODE_STRING, "kich1" },
+ { TTYC_KMOUS, TTYCODE_STRING, "kmous" },
+ { TTYC_KNP, TTYCODE_STRING, "knp" },
+ { TTYC_KPP, TTYCODE_STRING, "kpp" },
+ { TTYC_OP, TTYCODE_STRING, "op" },
+ { TTYC_REV, TTYCODE_STRING, "rev" },
+ { TTYC_RI, TTYCODE_STRING, "ri" },
+ { TTYC_RMACS, TTYCODE_STRING, "rmacs" },
+ { TTYC_RMCUP, TTYCODE_STRING, "rmcup" },
+ { TTYC_RMIR, TTYCODE_STRING, "rmir" },
+ { TTYC_RMKX, TTYCODE_STRING, "rmkx" },
+ { TTYC_SETAB, TTYCODE_STRING, "setab" },
+ { TTYC_SETAF, TTYCODE_STRING, "setaf" },
+ { TTYC_SGR0, TTYCODE_STRING, "sgr0" },
+ { TTYC_SMACS, TTYCODE_STRING, "smacs" },
+ { TTYC_SMCUP, TTYCODE_STRING, "smcup" },
+ { TTYC_SMIR, TTYCODE_STRING, "smir" },
+ { TTYC_SMKX, TTYCODE_STRING, "smkx" },
+ { TTYC_SMSO, TTYCODE_STRING, "smso" },
+ { TTYC_SMUL, TTYCODE_STRING, "smul" },
+ { TTYC_XENL, TTYCODE_FLAG, "xenl" },
+};
+
+char *
+tty_term_strip(const char *s)
+{
+ const char *ptr;
+ static char buf[BUFSIZ];
+ size_t len;
+
+ /* Ignore strings with no padding. */
+ if (strchr(s, '$') == NULL)
+ return (xstrdup(s));
+
+ len = 0;
+ for (ptr = s; *ptr != '\0'; ptr++) {
+ if (*ptr == '$' && *(ptr + 1) == '<') {
+ while (*ptr != '\0' && *ptr != '>')
+ ptr++;
+ if (*ptr == '>')
+ ptr++;
+ }
+
+ buf[len++] = *ptr;
+ if (len == (sizeof buf) - 1)
+ break;
+ }
+ buf[len] = '\0';
+
+ return (xstrdup(buf));
+}
+
+void
+tty_term_quirks(struct tty_term *term)
+{
+ if (strncmp(term->name, "rxvt", 4) == 0) {
+ /* rxvt supports dch1 but some termcap files do not have it. */
+ if (!tty_term_has(term, TTYC_DCH1)) {
+ term->codes[TTYC_DCH1].type = TTYCODE_STRING;
+ term->codes[TTYC_DCH1].value.string = xstrdup("\033[P");
+ }
+ }
+
+ if (strncmp(term->name, "xterm", 5) == 0) {
+ /* xterm supports ich1 but some termcaps omit it. */
+ if (!tty_term_has(term, TTYC_ICH1)) {
+ term->codes[TTYC_ICH1].type = TTYCODE_STRING;
+ term->codes[TTYC_ICH1].value.string = xstrdup("\033[@");
+ }
+ }
+}
+
+struct tty_term *
+tty_term_find(char *name, int fd, char **cause)
+{
+ struct tty_term *term;
+ struct tty_term_code_entry *ent;
+ struct tty_code *code;
+ u_int i;
+ int n, error;
+ char *s;
+
+ SLIST_FOREACH(term, &tty_terms, entry) {
+ if (strcmp(term->name, name) == 0) {
+ term->references++;
+ return (term);
+ }
+ }
+
+ log_debug("new term: %s", name);
+ term = xmalloc(sizeof *term);
+ term->name = xstrdup(name);
+ term->references = 1;
+ term->flags = 0;
+ SLIST_INSERT_HEAD(&tty_terms, term, entry);
+
+ /* Set up ncurses terminal. */
+ if (setupterm(name, fd, &error) != OK) {
+ switch (error) {
+ case 0:
+ xasprintf(cause, "can't use hardcopy terminal");
+ break;
+ case 1:
+ xasprintf(cause, "missing or unsuitable terminal");
+ break;
+ case 2:
+ xasprintf(cause, "can't find terminfo database");
+ break;
+ default:
+ xasprintf(cause, "unknown error");
+ break;
+ }
+ goto error;
+ }
+
+ /* Fill in codes. */
+ memset(&term->codes, 0, sizeof term->codes);
+ for (i = 0; i < NTTYCODE; i++) {
+ ent = &tty_term_codes[i];
+
+ code = &term->codes[ent->code];
+ code->type = TTYCODE_NONE;
+ switch (ent->type) {
+ case TTYCODE_NONE:
+ break;
+ case TTYCODE_STRING:
+ s = tigetstr((char *) ent->name);
+ if (s == NULL || s == (char *) -1)
+ break;
+ code->type = TTYCODE_STRING;
+ code->value.string = tty_term_strip(s);
+ break;
+ case TTYCODE_NUMBER:
+ n = tigetnum((char *) ent->name);
+ if (n == -1 || n == -2)
+ break;
+ code->type = TTYCODE_NUMBER;
+ code->value.number = n;
+ break;
+ case TTYCODE_FLAG:
+ n = tigetflag((char *) ent->name);
+ if (n == -1)
+ break;
+ code->type = TTYCODE_FLAG;
+ code->value.number = n;
+ break;
+ }
+ }
+ tty_term_quirks(term);
+
+ /* Delete ncurses data. */
+ del_curterm(cur_term);
+
+ /* These are always required. */
+ if (!tty_term_has(term, TTYC_CLEAR)) {
+ xasprintf(cause, "terminal does not support clear");
+ goto error;
+ }
+ if (!tty_term_has(term, TTYC_RI)) {
+ xasprintf(cause, "terminal does not support ri");
+ goto error;
+ }
+ if (!tty_term_has(term, TTYC_CUP)) {
+ xasprintf(cause, "terminal does not support cup");
+ goto error;
+ }
+
+ /* These can be emulated so one of the two is required. */
+ if (!tty_term_has(term, TTYC_CUD1) && !tty_term_has(term, TTYC_CUD)) {
+ xasprintf(cause, "terminal does not support cud1 or cud");
+ goto error;
+ }
+ if (!tty_term_has(term, TTYC_IL1) && !tty_term_has(term, TTYC_IL)) {
+ xasprintf(cause, "terminal does not support il1 or il");
+ goto error;
+ }
+ if (!tty_term_has(term, TTYC_DL1) && !tty_term_has(term, TTYC_DL)) {
+ xasprintf(cause, "terminal does not support dl1 or dl");
+ goto error;
+ }
+ if (!tty_term_has(term, TTYC_ICH1) &&
+ !tty_term_has(term, TTYC_ICH) && (!tty_term_has(term, TTYC_SMIR) ||
+ !tty_term_has(term, TTYC_RMIR))) {
+ xasprintf(cause,
+ "terminal does not support ich1 or ich or smir and rmir");
+ goto error;
+ }
+ if (!tty_term_has(term, TTYC_DCH1) && !tty_term_has(term, TTYC_DCH)) {
+ xasprintf(cause, "terminal does not support dch1 or dch");
+ goto error;
+ }
+
+ /*
+ * Figure out if terminal support default colours. AX is a screen
+ * extension which indicates this. Also check if op (orig_pair) uses
+ * the default colours - if it does, this is a good indication the
+ * terminal supports them.
+ */
+ if (tty_term_flag(term, TTYC_AX))
+ term->flags |= TERM_HASDEFAULTS;
+ if (strcmp(tty_term_string(term, TTYC_OP), "\033[39;49m") == 0)
+ term->flags |= TERM_HASDEFAULTS;
+
+ /*
+ * Try to figure out if we have 256 or 88 colours. The standard xterm
+ * definitions are broken (well, or the way they are parsed is: in any
+ * case they end up returning 8). So also do a hack.
+ */
+ if (tty_term_number(term, TTYC_COLORS) == 256)
+ term->flags |= TERM_256COLOURS;
+ if (strstr(name, "256col") != NULL) /* XXX HACK */
+ term->flags |= TERM_256COLOURS;
+ if (tty_term_number(term, TTYC_COLORS) == 88)
+ term->flags |= TERM_88COLOURS;
+ if (strstr(name, "88col") != NULL) /* XXX HACK */
+ term->flags |= TERM_88COLOURS;
+
+ /*
+ * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1
+ * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1).
+ *
+ * This is irritating, most notably because it is impossible to write
+ * to the very bottom-right of the screen without scrolling.
+ *
+ * Flag the terminal here and apply some workarounds in other places to
+ * do the best possible.
+ */
+ if (!tty_term_flag(term, TTYC_XENL))
+ term->flags |= TERM_EARLYWRAP;
+
+ return (term);
+
+error:
+ tty_term_free(term);
+ return (NULL);
+}
+
+void
+tty_term_free(struct tty_term *term)
+{
+ u_int i;
+
+ if (--term->references != 0)
+ return;
+
+ SLIST_REMOVE(&tty_terms, term, tty_term, entry);
+
+ for (i = 0; i < NTTYCODE; i++) {
+ if (term->codes[i].type == TTYCODE_STRING)
+ xfree(term->codes[i].value.string);
+ }
+ xfree(term->name);
+ xfree(term);
+}
+
+int
+tty_term_has(struct tty_term *term, enum tty_code_code code)
+{
+ return (term->codes[code].type != TTYCODE_NONE);
+}
+
+const char *
+tty_term_string(struct tty_term *term, enum tty_code_code code)
+{
+ if (!tty_term_has(term, code))
+ return ("");
+ if (term->codes[code].type != TTYCODE_STRING)
+ log_fatalx("not a string: %d", code);
+ return (term->codes[code].value.string);
+}
+
+/* No vtparm. Fucking ncurses. */
+const char *
+tty_term_string1(struct tty_term *term, enum tty_code_code code, int a)
+{
+ return (tparm((char *) tty_term_string(term, code), a));
+}
+
+const char *
+tty_term_string2(struct tty_term *term, enum tty_code_code code, int a, int b)
+{
+ return (tparm((char *) tty_term_string(term, code), a, b));
+}
+
+int
+tty_term_number(struct tty_term *term, enum tty_code_code code)
+{
+ if (!tty_term_has(term, code))
+ return (0);
+ if (term->codes[code].type != TTYCODE_NUMBER)
+ log_fatalx("not a number: %d", code);
+ return (term->codes[code].value.number);
+}
+
+int
+tty_term_flag(struct tty_term *term, enum tty_code_code code)
+{
+ if (!tty_term_has(term, code))
+ return (0);
+ if (term->codes[code].type != TTYCODE_FLAG)
+ log_fatalx("not a flag: %d", code);
+ return (term->codes[code].value.flag);
+}
+
diff --git a/usr.bin/tmux/tty-write.c b/usr.bin/tmux/tty-write.c
new file mode 100644
index 00000000000..7ab56a86bea
--- /dev/null
+++ b/usr.bin/tmux/tty-write.c
@@ -0,0 +1,91 @@
+/* $OpenBSD: tty-write.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+void tty_vwrite_cmd(struct window_pane *, enum tty_cmd, va_list);
+
+void
+tty_write_cmd(struct window_pane *wp, enum tty_cmd cmd, ...)
+{
+ va_list ap;
+
+ va_start(ap, cmd);
+ tty_vwrite_cmd(wp, cmd, ap);
+ va_end(ap);
+}
+
+void
+tty_vwrite_cmd(struct window_pane *wp, enum tty_cmd cmd, va_list ap)
+{
+ struct client *c;
+ va_list aq;
+ u_int i;
+
+ if (wp == NULL)
+ return;
+
+ if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW)
+ return;
+ if (wp->window->flags & WINDOW_HIDDEN || wp->flags & PANE_HIDDEN)
+ return;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session == NULL)
+ continue;
+ if (c->flags & CLIENT_SUSPENDED)
+ continue;
+
+ if (c->session->curw->window == wp->window) {
+ tty_update_mode(&c->tty, c->tty.mode & ~MODE_CURSOR);
+
+ va_copy(aq, ap);
+ tty_vwrite(&c->tty, wp, cmd, aq);
+ va_end(aq);
+ }
+ }
+}
+
+void
+tty_write_mode(struct window_pane *wp, int mode)
+{
+ struct client *c;
+ u_int i;
+
+ if (wp == NULL)
+ return;
+
+ if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW)
+ return;
+ if (wp->window->flags & WINDOW_HIDDEN || wp->flags & PANE_HIDDEN)
+ return;
+
+ for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+ c = ARRAY_ITEM(&clients, i);
+ if (c == NULL || c->session == NULL)
+ continue;
+ if (c->flags & CLIENT_SUSPENDED)
+ continue;
+
+ tty_update_mode(&c->tty, mode);
+ }
+}
+
diff --git a/usr.bin/tmux/tty.c b/usr.bin/tmux/tty.c
new file mode 100644
index 00000000000..c8a61f96d7b
--- /dev/null
+++ b/usr.bin/tmux/tty.c
@@ -0,0 +1,1076 @@
+/* $OpenBSD: tty.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/ioctl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+void tty_fill_acs(struct tty *);
+
+void tty_raw(struct tty *, const char *);
+
+int tty_try_256(struct tty *, u_char, const char *);
+int tty_try_88(struct tty *, u_char, const char *);
+
+void tty_attributes(struct tty *, const struct grid_cell *);
+void tty_attributes_fg(struct tty *, const struct grid_cell *);
+void tty_attributes_bg(struct tty *, const struct grid_cell *);
+
+void tty_cmd_cell(struct tty *, struct window_pane *, va_list);
+void tty_cmd_clearendofline(struct tty *, struct window_pane *, va_list);
+void tty_cmd_clearendofscreen(struct tty *, struct window_pane *, va_list);
+void tty_cmd_clearline(struct tty *, struct window_pane *, va_list);
+void tty_cmd_clearscreen(struct tty *, struct window_pane *, va_list);
+void tty_cmd_clearstartofline(struct tty *, struct window_pane *, va_list);
+void tty_cmd_clearstartofscreen(struct tty *, struct window_pane *, va_list);
+void tty_cmd_deletecharacter(struct tty *, struct window_pane *, va_list);
+void tty_cmd_deleteline(struct tty *, struct window_pane *, va_list);
+void tty_cmd_insertcharacter(struct tty *, struct window_pane *, va_list);
+void tty_cmd_insertline(struct tty *, struct window_pane *, va_list);
+void tty_cmd_linefeed(struct tty *, struct window_pane *, va_list);
+void tty_cmd_raw(struct tty *, struct window_pane *, va_list);
+void tty_cmd_reverseindex(struct tty *, struct window_pane *, va_list);
+
+void (*tty_cmds[])(struct tty *, struct window_pane *, va_list) = {
+ tty_cmd_cell,
+ tty_cmd_clearendofline,
+ tty_cmd_clearendofscreen,
+ tty_cmd_clearline,
+ tty_cmd_clearscreen,
+ tty_cmd_clearstartofline,
+ tty_cmd_clearstartofscreen,
+ tty_cmd_deletecharacter,
+ tty_cmd_deleteline,
+ tty_cmd_insertcharacter,
+ tty_cmd_insertline,
+ tty_cmd_linefeed,
+ tty_cmd_raw,
+ tty_cmd_reverseindex,
+};
+
+void
+tty_init(struct tty *tty, char *path, char *term)
+{
+ tty->path = xstrdup(path);
+ if (term == NULL)
+ tty->termname = xstrdup("unknown");
+ else
+ tty->termname = xstrdup(term);
+ tty->flags = 0;
+ tty->term_flags = 0;
+}
+
+int
+tty_open(struct tty *tty, char **cause)
+{
+ int mode;
+
+ tty->fd = open(tty->path, O_RDWR|O_NONBLOCK);
+ if (tty->fd == -1) {
+ xasprintf(cause, "%s: %s", tty->path, strerror(errno));
+ return (-1);
+ }
+
+ if ((mode = fcntl(tty->fd, F_GETFL)) == -1)
+ fatal("fcntl failed");
+ if (fcntl(tty->fd, F_SETFL, mode|O_NONBLOCK) == -1)
+ fatal("fcntl failedo");
+ if (fcntl(tty->fd, F_SETFD, FD_CLOEXEC) == -1)
+ fatal("fcntl failed");
+
+ if (debug_level > 3)
+ tty->log_fd = open("tmux.out", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ else
+ tty->log_fd = -1;
+
+ if ((tty->term = tty_term_find(tty->termname, tty->fd, cause)) == NULL)
+ goto error;
+
+ tty->in = buffer_create(BUFSIZ);
+ tty->out = buffer_create(BUFSIZ);
+
+ tty->flags &= TTY_UTF8;
+
+ tty_start_tty(tty);
+
+ tty_keys_init(tty);
+
+ tty_fill_acs(tty);
+
+ return (0);
+
+error:
+ close(tty->fd);
+ tty->fd = -1;
+
+ return (-1);
+}
+
+void
+tty_start_tty(struct tty *tty)
+{
+ struct termios tio;
+ int what;
+
+ tty_detect_utf8(tty);
+
+ if (tcgetattr(tty->fd, &tty->tio) != 0)
+ fatal("tcgetattr failed");
+ memcpy(&tio, &tty->tio, sizeof tio);
+ tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP);
+ tio.c_iflag |= IGNBRK;
+ tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET);
+ tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL|
+ ECHOPRT|ECHOKE|ECHOCTL|ISIG);
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
+ if (tcsetattr(tty->fd, TCSANOW, &tio) != 0)
+ fatal("tcsetattr failed");
+
+ what = 0;
+ if (ioctl(tty->fd, TIOCFLUSH, &what) != 0)
+ fatal("ioctl(TIOCFLUSH)");
+
+ tty_putcode(tty, TTYC_IS1);
+ tty_putcode(tty, TTYC_IS2);
+ tty_putcode(tty, TTYC_IS3);
+
+ tty_putcode(tty, TTYC_SMCUP);
+ tty_putcode(tty, TTYC_SMKX);
+ tty_putcode(tty, TTYC_ENACS);
+ tty_putcode(tty, TTYC_CLEAR);
+
+ tty_putcode(tty, TTYC_CNORM);
+ if (tty_term_has(tty->term, TTYC_KMOUS))
+ tty_puts(tty, "\033[?1000l");
+
+ memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell);
+
+ tty->cx = UINT_MAX;
+ tty->cy = UINT_MAX;
+
+ tty->rlower = UINT_MAX;
+ tty->rupper = UINT_MAX;
+
+ tty->mode = MODE_CURSOR;
+}
+
+void
+tty_stop_tty(struct tty *tty)
+{
+ struct winsize ws;
+
+ /*
+ * Be flexible about error handling and try not kill the server just
+ * because the fd is invalid. Things like ssh -t can easily leave us
+ * with a dead tty.
+ */
+ if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1)
+ return;
+ if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1)
+ return;
+
+ tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
+ tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
+ tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
+ tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
+ tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
+ tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
+
+ tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
+ if (tty_term_has(tty->term, TTYC_KMOUS))
+ tty_raw(tty, "\033[?1000l");
+}
+
+void
+tty_detect_utf8(struct tty *tty)
+{
+ struct pollfd pfd;
+ char buf[7];
+ size_t len;
+ ssize_t n;
+ int nfds;
+ struct termios tio, old_tio;
+ int what;
+
+ if (tty->flags & TTY_UTF8)
+ return;
+
+ /*
+ * If the terminal looks reasonably likely to support this, try to
+ * write a three-byte UTF-8 wide character to the terminal, then read
+ * the cursor position.
+ *
+ * XXX This entire function is a hack.
+ */
+
+ /* Check if the terminal looks sort of vt100. */
+ if (strstr(tty_term_string(tty->term, TTYC_CLEAR), "[2J") == NULL ||
+ strstr(tty_term_string(tty->term, TTYC_CUP), "H") == NULL)
+ return;
+
+ if (tcgetattr(tty->fd, &old_tio) != 0)
+ fatal("tcgetattr failed");
+ cfmakeraw(&tio);
+ if (tcsetattr(tty->fd, TCSANOW, &tio) != 0)
+ fatal("tcsetattr failed");
+
+ what = 0;
+ if (ioctl(tty->fd, TIOCFLUSH, &what) != 0)
+ fatal("ioctl(TIOCFLUSH)");
+
+#define UTF8_TEST_DATA "\033[H\357\277\246\033[6n"
+ if (write(tty->fd, UTF8_TEST_DATA, (sizeof UTF8_TEST_DATA) - 1) == -1)
+ fatal("write failed");
+#undef UTF8_TEST_DATA
+
+ len = 0;
+ for (;;) {
+ pfd.fd = tty->fd;
+ pfd.events = POLLIN;
+
+ nfds = poll(&pfd, 1, 500);
+ if (nfds == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ fatal("poll failed");
+ }
+ if (nfds == 0)
+ break;
+ if (pfd.revents & (POLLERR|POLLNVAL|POLLHUP))
+ break;
+ if (!(pfd.revents & POLLIN))
+ continue;
+
+ if ((n = read(tty->fd, buf + len, 1)) != 1)
+ break;
+ buf[++len] = '\0';
+
+ if (len == (sizeof buf) - 1) {
+ if (strcmp(buf, "\033[1;3R") == 0)
+ tty->flags |= TTY_UTF8;
+ break;
+ }
+ }
+
+ if (tcsetattr(tty->fd, TCSANOW, &old_tio) != 0)
+ fatal("tcsetattr failed");
+}
+
+void
+tty_fill_acs(struct tty *tty)
+{
+ const char *ptr;
+
+ memset(tty->acs, 0, sizeof tty->acs);
+ if (!tty_term_has(tty->term, TTYC_ACSC))
+ return;
+
+ ptr = tty_term_string(tty->term, TTYC_ACSC);
+ if (strlen(ptr) % 2 != 0)
+ return;
+ for (; *ptr != '\0'; ptr += 2)
+ tty->acs[(u_char) ptr[0]] = ptr[1];
+}
+
+u_char
+tty_get_acs(struct tty *tty, u_char ch)
+{
+ if (tty->acs[ch] != '\0')
+ return (tty->acs[ch]);
+ return (ch);
+}
+
+void
+tty_close(struct tty *tty, int no_stop)
+{
+ if (tty->fd == -1)
+ return;
+
+ if (tty->log_fd != -1) {
+ close(tty->log_fd);
+ tty->log_fd = -1;
+ }
+
+ if (!no_stop)
+ tty_stop_tty(tty);
+
+ tty_term_free(tty->term);
+ tty_keys_free(tty);
+
+ close(tty->fd);
+ tty->fd = -1;
+
+ buffer_destroy(tty->in);
+ buffer_destroy(tty->out);
+}
+
+void
+tty_free(struct tty *tty, int no_stop)
+{
+ tty_close(tty, no_stop);
+
+ if (tty->path != NULL)
+ xfree(tty->path);
+ if (tty->termname != NULL)
+ xfree(tty->termname);
+}
+
+void
+tty_raw(struct tty *tty, const char *s)
+{
+ write(tty->fd, s, strlen(s));
+}
+
+void
+tty_putcode(struct tty *tty, enum tty_code_code code)
+{
+ tty_puts(tty, tty_term_string(tty->term, code));
+}
+
+void
+tty_putcode1(struct tty *tty, enum tty_code_code code, int a)
+{
+ if (a < 0)
+ return;
+ tty_puts(tty, tty_term_string1(tty->term, code, a));
+}
+
+void
+tty_putcode2(struct tty *tty, enum tty_code_code code, int a, int b)
+{
+ if (a < 0 || b < 0)
+ return;
+ tty_puts(tty, tty_term_string2(tty->term, code, a, b));
+}
+
+void
+tty_puts(struct tty *tty, const char *s)
+{
+ if (*s == '\0')
+ return;
+ buffer_write(tty->out, s, strlen(s));
+
+ if (tty->log_fd != -1)
+ write(tty->log_fd, s, strlen(s));
+}
+
+void
+tty_putc(struct tty *tty, u_char ch)
+{
+ u_int sx;
+
+ if (tty->cell.attr & GRID_ATTR_CHARSET)
+ ch = tty_get_acs(tty, ch);
+ buffer_write8(tty->out, ch);
+
+ if (ch >= 0x20 && ch != 0x7f) {
+ sx = tty->sx;
+ if (tty->term->flags & TERM_EARLYWRAP)
+ sx--;
+
+ if (tty->cx == sx) {
+ tty->cx = 0;
+ tty->cy++;
+ } else
+ tty->cx++;
+ }
+
+ if (tty->log_fd != -1)
+ write(tty->log_fd, &ch, 1);
+}
+
+void
+tty_set_title(struct tty *tty, const char *title)
+{
+ if (strstr(tty->termname, "xterm") == NULL &&
+ strstr(tty->termname, "rxvt") == NULL &&
+ strcmp(tty->termname, "screen") != 0)
+ return;
+
+ tty_puts(tty, "\033]0;");
+ tty_puts(tty, title);
+ tty_putc(tty, '\007');
+}
+
+void
+tty_update_mode(struct tty *tty, int mode)
+{
+ int changed;
+
+ if (tty->flags & TTY_NOCURSOR)
+ mode &= ~MODE_CURSOR;
+
+ changed = mode ^ tty->mode;
+ if (changed & MODE_CURSOR) {
+ if (mode & MODE_CURSOR)
+ tty_putcode(tty, TTYC_CNORM);
+ else
+ tty_putcode(tty, TTYC_CIVIS);
+ }
+ if (changed & MODE_MOUSE) {
+ if (mode & MODE_MOUSE)
+ tty_puts(tty, "\033[?1000h");
+ else
+ tty_puts(tty, "\033[?1000l");
+ }
+ tty->mode = mode;
+}
+
+void
+tty_emulate_repeat(
+ struct tty *tty, enum tty_code_code code, enum tty_code_code code1, u_int n)
+{
+ if (tty_term_has(tty->term, code))
+ tty_putcode1(tty, code, n);
+ else {
+ while (n-- > 0)
+ tty_putcode(tty, code1);
+ }
+}
+
+/*
+ * Redraw scroll region using data from screen (already updated). Used when
+ * CSR not supported, or window is a pane that doesn't take up the full
+ * width of the terminal.
+ */
+void
+tty_redraw_region(struct tty *tty, struct window_pane *wp)
+{
+ struct screen *s = wp->screen;
+ u_int i;
+
+ /*
+ * If region is >= 50% of the screen, just schedule a window redraw. In
+ * most cases, this is likely to be followed by some more scrolling -
+ * without this, the entire pane ends up being redrawn many times which
+ * can be much more data.
+ */
+ if (s->old_rupper - s->old_rlower >= screen_size_y(s) / 2) {
+ wp->flags |= PANE_REDRAW;
+ return;
+ }
+
+ if (s->old_cy < s->old_rupper || s->old_cy > s->old_rlower) {
+ for (i = s->old_cy; i < screen_size_y(s); i++)
+ tty_draw_line(tty, s, i, wp->xoff, wp->yoff);
+ } else {
+ for (i = s->old_rupper; i <= s->old_rlower; i++)
+ tty_draw_line(tty, s, i, wp->xoff, wp->yoff);
+ }
+}
+
+void
+tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy)
+{
+ const struct grid_cell *gc;
+ const struct grid_utf8 *gu;
+ u_int i, sx;
+
+ sx = screen_size_x(s);
+ if (sx > s->grid->size[s->grid->hsize + py])
+ sx = s->grid->size[s->grid->hsize + py];
+ if (sx > tty->sx)
+ sx = tty->sx;
+
+ tty_cursor(tty, 0, py, ox, oy);
+ for (i = 0; i < sx; i++) {
+ gc = grid_view_peek_cell(s->grid, i, py);
+
+ gu = NULL;
+ if (gc->flags & GRID_FLAG_UTF8)
+ gu = grid_view_peek_utf8(s->grid, i, py);
+
+ if (screen_check_selection(s, i, py)) {
+ s->sel.cell.data = gc->data;
+ tty_cell(tty, &s->sel.cell, gu);
+ } else
+ tty_cell(tty, gc, gu);
+ }
+
+ if (sx >= tty->sx)
+ return;
+ tty_reset(tty);
+
+ tty_cursor(tty, sx, py, ox, oy);
+ if (screen_size_x(s) >= tty->sx && tty_term_has(tty->term, TTYC_EL))
+ tty_putcode(tty, TTYC_EL);
+ else {
+ for (i = sx; i < screen_size_x(s); i++)
+ tty_putc(tty, ' ');
+ }
+}
+
+void
+tty_write(struct tty *tty, struct window_pane *wp, enum tty_cmd cmd, ...)
+{
+ va_list ap;
+
+ va_start(ap, cmd);
+ tty_vwrite(tty, wp, cmd, ap);
+ va_end(ap);
+}
+
+void
+tty_vwrite(
+ struct tty *tty, struct window_pane *wp, enum tty_cmd cmd, va_list ap)
+{
+ if (tty->flags & TTY_FREEZE || tty->term == NULL)
+ return;
+ if (tty_cmds[cmd] != NULL)
+ tty_cmds[cmd](tty, wp, ap);
+}
+
+void
+tty_cmd_insertcharacter(struct tty *tty, struct window_pane *wp, va_list ap)
+{
+ struct screen *s = wp->screen;
+ u_int ua;
+
+ if (wp->xoff != 0 || screen_size_x(s) < tty->sx) {
+ tty_draw_line(tty, wp->screen, s->old_cy, wp->xoff, wp->yoff);
+ return;
+ }
+
+ ua = va_arg(ap, u_int);
+
+ tty_reset(tty);
+
+ tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff);
+ if (tty_term_has(tty->term, TTYC_ICH) ||
+ tty_term_has(tty->term, TTYC_ICH1))
+ tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ua);
+ else {
+ tty_putcode(tty, TTYC_SMIR);
+ while (ua-- > 0)
+ tty_putc(tty, ' ');
+ tty_putcode(tty, TTYC_RMIR);
+ }
+}
+
+void
+tty_cmd_deletecharacter(struct tty *tty, struct window_pane *wp, va_list ap)
+{
+ struct screen *s = wp->screen;
+ u_int ua;
+
+ if (wp->xoff != 0 || screen_size_x(s) < tty->sx) {
+ tty_draw_line(tty, wp->screen, s->old_cy, wp->xoff, wp->yoff);
+ return;
+ }
+
+ ua = va_arg(ap, u_int);
+
+ tty_reset(tty);
+
+ tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff);
+ tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ua);
+}
+
+void
+tty_cmd_insertline(struct tty *tty, struct window_pane *wp, va_list ap)
+{
+ struct screen *s = wp->screen;
+ u_int ua;
+
+ if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
+ !tty_term_has(tty->term, TTYC_CSR)) {
+ tty_redraw_region(tty, wp);
+ return;
+ }
+
+ ua = va_arg(ap, u_int);
+
+ tty_reset(tty);
+
+ tty_region(tty, s->old_rupper, s->old_rlower, wp->yoff);
+
+ tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff);
+ tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ua);
+}
+
+void
+tty_cmd_deleteline(struct tty *tty, struct window_pane *wp, va_list ap)
+{
+ struct screen *s = wp->screen;
+ u_int ua;
+
+ if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
+ !tty_term_has(tty->term, TTYC_CSR)) {
+ tty_redraw_region(tty, wp);
+ return;
+ }
+
+ ua = va_arg(ap, u_int);
+
+ tty_reset(tty);
+
+ tty_region(tty, s->old_rupper, s->old_rlower, wp->yoff);
+
+ tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff);
+ tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ua);
+}
+
+void
+tty_cmd_clearline(struct tty *tty, struct window_pane *wp, unused va_list ap)
+{
+ struct screen *s = wp->screen;
+ u_int i;
+
+ tty_reset(tty);
+
+ tty_cursor(tty, 0, s->old_cy, wp->xoff, wp->yoff);
+ if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
+ tty_term_has(tty->term, TTYC_EL)) {
+ tty_putcode(tty, TTYC_EL);
+ } else {
+ for (i = 0; i < screen_size_x(s); i++)
+ tty_putc(tty, ' ');
+ }
+}
+
+void
+tty_cmd_clearendofline(
+ struct tty *tty, struct window_pane *wp, unused va_list ap)
+{
+ struct screen *s = wp->screen;
+ u_int i;
+
+ tty_reset(tty);
+
+ tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff);
+ if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
+ tty_term_has(tty->term, TTYC_EL))
+ tty_putcode(tty, TTYC_EL);
+ else {
+ for (i = s->old_cx; i < screen_size_x(s); i++)
+ tty_putc(tty, ' ');
+ }
+}
+
+void
+tty_cmd_clearstartofline(
+ struct tty *tty, struct window_pane *wp, unused va_list ap)
+{
+ struct screen *s = wp->screen;
+ u_int i;
+
+ tty_reset(tty);
+
+ if (wp->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) {
+ tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff);
+ tty_putcode(tty, TTYC_EL1);
+ } else {
+ tty_cursor(tty, 0, s->old_cy, wp->xoff, wp->yoff);
+ for (i = 0; i < s->old_cx + 1; i++)
+ tty_putc(tty, ' ');
+ }
+}
+
+void
+tty_cmd_reverseindex(struct tty *tty, struct window_pane *wp, unused va_list ap)
+{
+ struct screen *s = wp->screen;
+
+ if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
+ !tty_term_has(tty->term, TTYC_CSR)) {
+ tty_redraw_region(tty, wp);
+ return;
+ }
+
+ tty_reset(tty);
+
+ tty_region(tty, s->old_rupper, s->old_rlower, wp->yoff);
+
+ if (s->old_cy == s->old_rupper) {
+ tty_cursor(tty, s->old_cx, s->old_rupper, wp->xoff, wp->yoff);
+ tty_putcode(tty, TTYC_RI);
+ }
+}
+
+void
+tty_cmd_linefeed(struct tty *tty, struct window_pane *wp, unused va_list ap)
+{
+ struct screen *s = wp->screen;
+
+ if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
+ !tty_term_has(tty->term, TTYC_CSR)) {
+ tty_redraw_region(tty, wp);
+ return;
+ }
+
+ tty_reset(tty);
+
+ tty_region(tty, s->old_rupper, s->old_rlower, wp->yoff);
+
+ if (s->old_cy == s->old_rlower) {
+ tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff);
+ tty_putc(tty, '\n');
+ }
+}
+
+void
+tty_cmd_clearendofscreen(
+ struct tty *tty, struct window_pane *wp, unused va_list ap)
+{
+ struct screen *s = wp->screen;
+ u_int i, j, oy;
+
+ oy = wp->yoff;
+
+ tty_reset(tty);
+
+ tty_region(tty, 0, screen_size_y(s) - 1, wp->yoff);
+ tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff);
+ if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
+ tty_term_has(tty->term, TTYC_EL)) {
+ tty_putcode(tty, TTYC_EL);
+ if (s->old_cy != screen_size_y(s) - 1) {
+ tty_cursor(tty, 0, s->old_cy + 1, wp->xoff, wp->yoff);
+ for (i = s->old_cy + 1; i < screen_size_y(s); i++) {
+ tty_putcode(tty, TTYC_EL);
+ if (i == screen_size_y(s) - 1)
+ continue;
+ tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
+ tty->cy++;
+ }
+ }
+ } else {
+ for (i = s->old_cx; i < screen_size_x(s); i++)
+ tty_putc(tty, ' ');
+ for (j = s->old_cy; j < screen_size_y(s); j++) {
+ tty_cursor(tty, 0, j, wp->xoff, wp->yoff);
+ for (i = 0; i < screen_size_x(s); i++)
+ tty_putc(tty, ' ');
+ }
+ }
+}
+
+void
+tty_cmd_clearstartofscreen(
+ struct tty *tty, struct window_pane *wp, unused va_list ap)
+{
+ struct screen *s = wp->screen;
+ u_int i, j;
+
+ tty_reset(tty);
+
+ tty_region(tty, 0, screen_size_y(s) - 1, wp->yoff);
+ tty_cursor(tty, 0, 0, wp->xoff, wp->yoff);
+ if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
+ tty_term_has(tty->term, TTYC_EL)) {
+ for (i = 0; i < s->old_cy; i++) {
+ tty_putcode(tty, TTYC_EL);
+ tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
+ tty->cy++;
+ }
+ } else {
+ for (j = 0; j < s->old_cy; j++) {
+ tty_cursor(tty, 0, j, wp->xoff, wp->yoff);
+ for (i = 0; i < screen_size_x(s); i++)
+ tty_putc(tty, ' ');
+ }
+ }
+ for (i = 0; i < s->old_cx; i++)
+ tty_putc(tty, ' ');
+}
+
+void
+tty_cmd_clearscreen(
+ struct tty *tty, struct window_pane *wp, unused va_list ap)
+{
+ struct screen *s = wp->screen;
+ u_int i, j;
+
+ tty_reset(tty);
+
+ tty_region(tty, 0, screen_size_y(s) - 1, wp->yoff);
+ tty_cursor(tty, 0, 0, wp->xoff, wp->yoff);
+ if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
+ tty_term_has(tty->term, TTYC_EL)) {
+ for (i = 0; i < screen_size_y(s); i++) {
+ tty_putcode(tty, TTYC_EL);
+ if (i != screen_size_y(s) - 1) {
+ tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
+ tty->cy++;
+ }
+ }
+ } else {
+ for (j = 0; j < screen_size_y(s); j++) {
+ tty_cursor(tty, 0, j, wp->xoff, wp->yoff);
+ for (i = 0; i < screen_size_x(s); i++)
+ tty_putc(tty, ' ');
+ }
+ }
+}
+
+void
+tty_cmd_cell(struct tty *tty, struct window_pane *wp, va_list ap)
+{
+ struct screen *s = wp->screen;
+ struct grid_cell *gc;
+ struct grid_utf8 *gu;
+
+ gc = va_arg(ap, struct grid_cell *);
+ gu = va_arg(ap, struct grid_utf8 *);
+
+ tty_cursor(tty, s->old_cx, s->old_cy, wp->xoff, wp->yoff);
+
+ tty_cell(tty, gc, gu);
+}
+
+void
+tty_cmd_raw(struct tty *tty, unused struct window_pane *wp, va_list ap)
+{
+ u_char *buf;
+ size_t i, len;
+
+ buf = va_arg(ap, u_char *);
+ len = va_arg(ap, size_t);
+
+ for (i = 0; i < len; i++)
+ tty_putc(tty, buf[i]);
+}
+
+void
+tty_cell(
+ struct tty *tty, const struct grid_cell *gc, const struct grid_utf8 *gu)
+{
+ u_int i;
+
+ /* Skip last character if terminal is stupid. */
+ if (tty->term->flags & TERM_EARLYWRAP &&
+ tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1)
+ return;
+
+ /* If this is a padding character, do nothing. */
+ if (gc->flags & GRID_FLAG_PADDING)
+ return;
+
+ /* Set the attributes. */
+ tty_attributes(tty, gc);
+
+ /* If not UTF-8, write directly. */
+ if (!(gc->flags & GRID_FLAG_UTF8)) {
+ if (gc->data < 0x20 || gc->data == 0x7f)
+ return;
+ tty_putc(tty, gc->data);
+ return;
+ }
+
+ /* If the terminal doesn't support UTF-8, write underscores. */
+ if (!(tty->flags & TTY_UTF8)) {
+ for (i = 0; i < gu->width; i++)
+ tty_putc(tty, '_');
+ return;
+ }
+
+ /* Otherwise, write UTF-8. */
+ for (i = 0; i < UTF8_SIZE; i++) {
+ if (gu->data[i] == 0xff)
+ break;
+ tty_putc(tty, gu->data[i]);
+ }
+}
+
+void
+tty_reset(struct tty *tty)
+{
+ struct grid_cell *gc = &tty->cell;
+
+ if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0)
+ return;
+
+ if (tty_term_has(tty->term, TTYC_RMACS) && gc->attr & GRID_ATTR_CHARSET)
+ tty_putcode(tty, TTYC_RMACS);
+ tty_putcode(tty, TTYC_SGR0);
+ memcpy(gc, &grid_default_cell, sizeof *gc);
+}
+
+void
+tty_region(struct tty *tty, u_int rupper, u_int rlower, u_int oy)
+{
+ if (!tty_term_has(tty->term, TTYC_CSR))
+ return;
+ if (tty->rlower != oy + rlower || tty->rupper != oy + rupper) {
+ tty->rlower = oy + rlower;
+ tty->rupper = oy + rupper;
+ tty->cx = 0;
+ tty->cy = 0;
+ tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower);
+ }
+}
+
+void
+tty_cursor(struct tty *tty, u_int cx, u_int cy, u_int ox, u_int oy)
+{
+ if (ox + cx == 0 && tty->cx != 0 && tty->cy == oy + cy) {
+ tty->cx = 0;
+ tty_putc(tty, '\r');
+ } else if (tty->cx != ox + cx || tty->cy != oy + cy) {
+ tty->cx = ox + cx;
+ tty->cy = oy + cy;
+ tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx);
+ }
+}
+
+void
+tty_attributes(struct tty *tty, const struct grid_cell *gc)
+{
+ struct grid_cell *tc = &tty->cell;
+ u_char changed;
+ u_int fg, bg;
+
+ /* If any bits are being cleared, reset everything. */
+ if (tc->attr & ~gc->attr)
+ tty_reset(tty);
+
+ /* Filter out attribute bits already set. */
+ changed = gc->attr & ~tc->attr;
+ tc->attr = gc->attr;
+
+ /* Set the attributes. */
+ fg = gc->fg;
+ bg = gc->bg;
+ if (changed & GRID_ATTR_BRIGHT)
+ tty_putcode(tty, TTYC_BOLD);
+ if (changed & GRID_ATTR_DIM)
+ tty_putcode(tty, TTYC_DIM);
+ if (changed & GRID_ATTR_ITALICS)
+ tty_putcode(tty, TTYC_SMSO);
+ if (changed & GRID_ATTR_UNDERSCORE)
+ tty_putcode(tty, TTYC_SMUL);
+ if (changed & GRID_ATTR_BLINK)
+ tty_putcode(tty, TTYC_BLINK);
+ if (changed & GRID_ATTR_REVERSE) {
+ if (tty_term_has(tty->term, TTYC_REV))
+ tty_putcode(tty, TTYC_REV);
+ else if (tty_term_has(tty->term, TTYC_SMSO))
+ tty_putcode(tty, TTYC_SMSO);
+ }
+ if (changed & GRID_ATTR_HIDDEN)
+ tty_putcode(tty, TTYC_INVIS);
+ if (changed & GRID_ATTR_CHARSET)
+ tty_putcode(tty, TTYC_SMACS);
+
+ /* Set foreground colour. */
+ if (fg != tc->fg ||
+ (gc->flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256)) {
+ tty_attributes_fg(tty, gc);
+ tc->fg = fg;
+ }
+
+ /* Set background colour. */
+ if (bg != tc->bg ||
+ (gc->flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256)) {
+ tty_attributes_bg(tty, gc);
+ tc->bg = bg;
+ }
+}
+
+int
+tty_try_256(struct tty *tty, u_char colour, const char *type)
+{
+ char s[32];
+
+ if (!(tty->term->flags & TERM_256COLOURS) &&
+ !(tty->term_flags & TERM_256COLOURS))
+ return (-1);
+
+ xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
+ tty_puts(tty, s);
+ return (0);
+}
+
+int
+tty_try_88(struct tty *tty, u_char colour, const char *type)
+{
+ char s[32];
+
+ if (!(tty->term->flags & TERM_88COLOURS) &&
+ !(tty->term_flags & TERM_88COLOURS))
+ return (-1);
+ colour = colour_256to88(colour);
+
+ xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
+ tty_puts(tty, s);
+ return (0);
+}
+
+void
+tty_attributes_fg(struct tty *tty, const struct grid_cell *gc)
+{
+ u_char fg;
+
+ fg = gc->fg;
+ if (gc->flags & GRID_FLAG_FG256) {
+ if (tty_try_256(tty, fg, "38") == 0)
+ return;
+ if (tty_try_88(tty, fg, "38") == 0)
+ return;
+ fg = colour_256to16(fg);
+ if (fg & 8) {
+ fg &= 7;
+ tty_putcode(tty, TTYC_BOLD);
+ tty->cell.attr |= GRID_ATTR_BRIGHT;
+ } else if (tty->cell.attr & GRID_ATTR_BRIGHT)
+ tty_reset(tty);
+ }
+
+ if (fg == 8 &&
+ !(tty->term->flags & TERM_HASDEFAULTS) &&
+ !(tty->term_flags & TERM_HASDEFAULTS))
+ fg = 7;
+ if (fg == 8)
+ tty_puts(tty, "\033[39m");
+ else
+ tty_putcode1(tty, TTYC_SETAF, fg);
+}
+
+void
+tty_attributes_bg(struct tty *tty, const struct grid_cell *gc)
+{
+ u_char bg;
+
+ bg = gc->bg;
+ if (gc->flags & GRID_FLAG_BG256) {
+ if (tty_try_256(tty, bg, "48") == 0)
+ return;
+ if (tty_try_88(tty, bg, "48") == 0)
+ return;
+ bg = colour_256to16(bg);
+ if (bg & 8)
+ bg &= 7;
+ }
+
+ if (bg == 8 &&
+ !(tty->term->flags & TERM_HASDEFAULTS) &&
+ !(tty->term_flags & TERM_HASDEFAULTS))
+ bg = 0;
+ if (bg == 8)
+ tty_puts(tty, "\033[49m");
+ else
+ tty_putcode1(tty, TTYC_SETAB, bg);
+}
diff --git a/usr.bin/tmux/utf8.c b/usr.bin/tmux/utf8.c
new file mode 100644
index 00000000000..63660e8c135
--- /dev/null
+++ b/usr.bin/tmux/utf8.c
@@ -0,0 +1,317 @@
+/* $OpenBSD: utf8.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+struct utf8_width_entry {
+ u_int first;
+ u_int last;
+
+ int width;
+
+ struct utf8_width_entry *left;
+ struct utf8_width_entry *right;
+};
+
+/* Random order. Not optimal but it'll do for now... */
+struct utf8_width_entry utf8_width_table[] = {
+ { 0x00951, 0x00954, 0, NULL, NULL },
+ { 0x00ccc, 0x00ccd, 0, NULL, NULL },
+ { 0x0fff9, 0x0fffb, 0, NULL, NULL },
+ { 0x20000, 0x2fffd, 2, NULL, NULL },
+ { 0x00ebb, 0x00ebc, 0, NULL, NULL },
+ { 0x01932, 0x01932, 0, NULL, NULL },
+ { 0x0070f, 0x0070f, 0, NULL, NULL },
+ { 0x00a70, 0x00a71, 0, NULL, NULL },
+ { 0x02329, 0x02329, 2, NULL, NULL },
+ { 0x00acd, 0x00acd, 0, NULL, NULL },
+ { 0x00ac7, 0x00ac8, 0, NULL, NULL },
+ { 0x00a3c, 0x00a3c, 0, NULL, NULL },
+ { 0x009cd, 0x009cd, 0, NULL, NULL },
+ { 0x00591, 0x005bd, 0, NULL, NULL },
+ { 0x01058, 0x01059, 0, NULL, NULL },
+ { 0x0ffe0, 0x0ffe6, 2, NULL, NULL },
+ { 0x01100, 0x0115f, 2, NULL, NULL },
+ { 0x0fe20, 0x0fe23, 0, NULL, NULL },
+ { 0x0302a, 0x0302f, 0, NULL, NULL },
+ { 0x01772, 0x01773, 0, NULL, NULL },
+ { 0x005bf, 0x005bf, 0, NULL, NULL },
+ { 0x006ea, 0x006ed, 0, NULL, NULL },
+ { 0x00bc0, 0x00bc0, 0, NULL, NULL },
+ { 0x00962, 0x00963, 0, NULL, NULL },
+ { 0x01732, 0x01734, 0, NULL, NULL },
+ { 0x00d41, 0x00d43, 0, NULL, NULL },
+ { 0x01b42, 0x01b42, 0, NULL, NULL },
+ { 0x00a41, 0x00a42, 0, NULL, NULL },
+ { 0x00eb4, 0x00eb9, 0, NULL, NULL },
+ { 0x00b01, 0x00b01, 0, NULL, NULL },
+ { 0x00e34, 0x00e3a, 0, NULL, NULL },
+ { 0x03040, 0x03098, 2, NULL, NULL },
+ { 0x0093c, 0x0093c, 0, NULL, NULL },
+ { 0x00c4a, 0x00c4d, 0, NULL, NULL },
+ { 0x01032, 0x01032, 0, NULL, NULL },
+ { 0x00f37, 0x00f37, 0, NULL, NULL },
+ { 0x00901, 0x00902, 0, NULL, NULL },
+ { 0x00cbf, 0x00cbf, 0, NULL, NULL },
+ { 0x0a806, 0x0a806, 0, NULL, NULL },
+ { 0x00dd2, 0x00dd4, 0, NULL, NULL },
+ { 0x00f71, 0x00f7e, 0, NULL, NULL },
+ { 0x01752, 0x01753, 0, NULL, NULL },
+ { 0x1d242, 0x1d244, 0, NULL, NULL },
+ { 0x005c1, 0x005c2, 0, NULL, NULL },
+ { 0x0309b, 0x0a4cf, 2, NULL, NULL },
+ { 0xe0100, 0xe01ef, 0, NULL, NULL },
+ { 0x017dd, 0x017dd, 0, NULL, NULL },
+ { 0x00600, 0x00603, 0, NULL, NULL },
+ { 0x009e2, 0x009e3, 0, NULL, NULL },
+ { 0x00cc6, 0x00cc6, 0, NULL, NULL },
+ { 0x0a80b, 0x0a80b, 0, NULL, NULL },
+ { 0x01712, 0x01714, 0, NULL, NULL },
+ { 0x00b3c, 0x00b3c, 0, NULL, NULL },
+ { 0x01b00, 0x01b03, 0, NULL, NULL },
+ { 0x007eb, 0x007f3, 0, NULL, NULL },
+ { 0xe0001, 0xe0001, 0, NULL, NULL },
+ { 0x1d185, 0x1d18b, 0, NULL, NULL },
+ { 0x0feff, 0x0feff, 0, NULL, NULL },
+ { 0x01b36, 0x01b3a, 0, NULL, NULL },
+ { 0x01920, 0x01922, 0, NULL, NULL },
+ { 0x00670, 0x00670, 0, NULL, NULL },
+ { 0x00f90, 0x00f97, 0, NULL, NULL },
+ { 0x01927, 0x01928, 0, NULL, NULL },
+ { 0x0200b, 0x0200f, 0, NULL, NULL },
+ { 0x0ff00, 0x0ff60, 2, NULL, NULL },
+ { 0x0f900, 0x0faff, 2, NULL, NULL },
+ { 0x0fb1e, 0x0fb1e, 0, NULL, NULL },
+ { 0x00cbc, 0x00cbc, 0, NULL, NULL },
+ { 0x00eb1, 0x00eb1, 0, NULL, NULL },
+ { 0x10a38, 0x10a3a, 0, NULL, NULL },
+ { 0x007a6, 0x007b0, 0, NULL, NULL },
+ { 0x00f80, 0x00f84, 0, NULL, NULL },
+ { 0x005c4, 0x005c5, 0, NULL, NULL },
+ { 0x0ac00, 0x0d7a3, 2, NULL, NULL },
+ { 0x017c9, 0x017d3, 0, NULL, NULL },
+ { 0x00d4d, 0x00d4d, 0, NULL, NULL },
+ { 0x1d167, 0x1d169, 0, NULL, NULL },
+ { 0x01036, 0x01037, 0, NULL, NULL },
+ { 0xe0020, 0xe007f, 0, NULL, NULL },
+ { 0x00f35, 0x00f35, 0, NULL, NULL },
+ { 0x017b4, 0x017b5, 0, NULL, NULL },
+ { 0x0206a, 0x0206f, 0, NULL, NULL },
+ { 0x00c46, 0x00c48, 0, NULL, NULL },
+ { 0x01939, 0x0193b, 0, NULL, NULL },
+ { 0x01dc0, 0x01dca, 0, NULL, NULL },
+ { 0x10a0c, 0x10a0f, 0, NULL, NULL },
+ { 0x0102d, 0x01030, 0, NULL, NULL },
+ { 0x017c6, 0x017c6, 0, NULL, NULL },
+ { 0x00ec8, 0x00ecd, 0, NULL, NULL },
+ { 0x00b41, 0x00b43, 0, NULL, NULL },
+ { 0x017b7, 0x017bd, 0, NULL, NULL },
+ { 0x1d173, 0x1d182, 0, NULL, NULL },
+ { 0x00a47, 0x00a48, 0, NULL, NULL },
+ { 0x0232a, 0x0232a, 2, NULL, NULL },
+ { 0x01b3c, 0x01b3c, 0, NULL, NULL },
+ { 0x10a01, 0x10a03, 0, NULL, NULL },
+ { 0x00ae2, 0x00ae3, 0, NULL, NULL },
+ { 0x00483, 0x00486, 0, NULL, NULL },
+ { 0x0135f, 0x0135f, 0, NULL, NULL },
+ { 0x01a17, 0x01a18, 0, NULL, NULL },
+ { 0x006e7, 0x006e8, 0, NULL, NULL },
+ { 0x03099, 0x0309a, 0, NULL, NULL },
+ { 0x00b4d, 0x00b4d, 0, NULL, NULL },
+ { 0x00ce2, 0x00ce3, 0, NULL, NULL },
+ { 0x00bcd, 0x00bcd, 0, NULL, NULL },
+ { 0x00610, 0x00615, 0, NULL, NULL },
+ { 0x00f99, 0x00fbc, 0, NULL, NULL },
+ { 0x009c1, 0x009c4, 0, NULL, NULL },
+ { 0x00730, 0x0074a, 0, NULL, NULL },
+ { 0x00300, 0x0036f, 0, NULL, NULL },
+ { 0x03030, 0x0303e, 2, NULL, NULL },
+ { 0x01b34, 0x01b34, 0, NULL, NULL },
+ { 0x1d1aa, 0x1d1ad, 0, NULL, NULL },
+ { 0x00dca, 0x00dca, 0, NULL, NULL },
+ { 0x006d6, 0x006e4, 0, NULL, NULL },
+ { 0x00f86, 0x00f87, 0, NULL, NULL },
+ { 0x00b3f, 0x00b3f, 0, NULL, NULL },
+ { 0x0fe30, 0x0fe6f, 2, NULL, NULL },
+ { 0x01039, 0x01039, 0, NULL, NULL },
+ { 0x0094d, 0x0094d, 0, NULL, NULL },
+ { 0x00c55, 0x00c56, 0, NULL, NULL },
+ { 0x00488, 0x00489, 0, NULL, NULL },
+ { 0x00e47, 0x00e4e, 0, NULL, NULL },
+ { 0x00a81, 0x00a82, 0, NULL, NULL },
+ { 0x00ac1, 0x00ac5, 0, NULL, NULL },
+ { 0x0202a, 0x0202e, 0, NULL, NULL },
+ { 0x00dd6, 0x00dd6, 0, NULL, NULL },
+ { 0x018a9, 0x018a9, 0, NULL, NULL },
+ { 0x0064b, 0x0065e, 0, NULL, NULL },
+ { 0x00abc, 0x00abc, 0, NULL, NULL },
+ { 0x00b82, 0x00b82, 0, NULL, NULL },
+ { 0x00f39, 0x00f39, 0, NULL, NULL },
+ { 0x020d0, 0x020ef, 0, NULL, NULL },
+ { 0x01dfe, 0x01dff, 0, NULL, NULL },
+ { 0x30000, 0x3fffd, 2, NULL, NULL },
+ { 0x00711, 0x00711, 0, NULL, NULL },
+ { 0x0fe00, 0x0fe0f, 0, NULL, NULL },
+ { 0x01160, 0x011ff, 0, NULL, NULL },
+ { 0x0180b, 0x0180d, 0, NULL, NULL },
+ { 0x10a3f, 0x10a3f, 0, NULL, NULL },
+ { 0x00981, 0x00981, 0, NULL, NULL },
+ { 0x0a825, 0x0a826, 0, NULL, NULL },
+ { 0x00941, 0x00948, 0, NULL, NULL },
+ { 0x01b6b, 0x01b73, 0, NULL, NULL },
+ { 0x00e31, 0x00e31, 0, NULL, NULL },
+ { 0x0fe10, 0x0fe19, 2, NULL, NULL },
+ { 0x00a01, 0x00a02, 0, NULL, NULL },
+ { 0x00a4b, 0x00a4d, 0, NULL, NULL },
+ { 0x00f18, 0x00f19, 0, NULL, NULL },
+ { 0x00fc6, 0x00fc6, 0, NULL, NULL },
+ { 0x02e80, 0x03029, 2, NULL, NULL },
+ { 0x00b56, 0x00b56, 0, NULL, NULL },
+ { 0x009bc, 0x009bc, 0, NULL, NULL },
+ { 0x005c7, 0x005c7, 0, NULL, NULL },
+ { 0x02060, 0x02063, 0, NULL, NULL },
+ { 0x00c3e, 0x00c40, 0, NULL, NULL },
+ { 0x10a05, 0x10a06, 0, NULL, NULL },
+};
+
+struct utf8_width_entry *utf8_width_root = NULL;
+
+int utf8_overlap(struct utf8_width_entry *, struct utf8_width_entry *);
+void utf8_print(struct utf8_width_entry *, int);
+u_int utf8_combine(const u_char *);
+void utf8_split(u_int, u_char *);
+
+int
+utf8_overlap(
+ struct utf8_width_entry *item1, struct utf8_width_entry *item2)
+{
+ if (item1->first >= item2->first && item1->first <= item2->last)
+ return (1);
+ if (item1->last >= item2->first && item1->last <= item2->last)
+ return (1);
+ if (item2->first >= item1->first && item2->first <= item1->last)
+ return (1);
+ if (item2->last >= item1->first && item2->last <= item1->last)
+ return (1);
+ return (0);
+}
+
+void
+utf8_build(void)
+{
+ struct utf8_width_entry **ptr, *item, *node;
+ u_int i, j;
+
+ for (i = 0; i < nitems(utf8_width_table); i++) {
+ item = &utf8_width_table[i];
+
+ for (j = 0; j < nitems(utf8_width_table); j++) {
+ if (i != j && utf8_overlap(item, &utf8_width_table[j]))
+ log_fatalx("utf8 overlap: %u %u", i, j);
+ }
+
+ ptr = &utf8_width_root;
+ while (*ptr != NULL) {
+ node = *ptr;
+ if (item->last < node->first)
+ ptr = &(node->left);
+ else if (item->first > node->last)
+ ptr = &(node->right);
+ }
+ *ptr = item;
+ }
+}
+
+void
+utf8_print(struct utf8_width_entry *node, int n)
+{
+ log_debug("%*s%04x -> %04x", n, " ", node->first, node->last);
+ if (node->left != NULL)
+ utf8_print(node->left, n + 1);
+ if (node->right != NULL)
+ utf8_print(node->right, n + 1);
+}
+
+u_int
+utf8_combine(const u_char *data)
+{
+ u_int uvalue;
+
+ if (data[1] == 0xff)
+ uvalue = data[0];
+ else if (data[2] == 0xff) {
+ uvalue = data[1] & 0x3f;
+ uvalue |= (data[0] & 0x1f) << 6;
+ } else if (data[3] == 0xff) {
+ uvalue = data[2] & 0x3f;
+ uvalue |= (data[1] & 0x3f) << 6;
+ uvalue |= (data[0] & 0x0f) << 12;
+ } else {
+ uvalue = data[3] & 0x3f;
+ uvalue |= (data[2] & 0x3f) << 6;
+ uvalue |= (data[1] & 0x3f) << 12;
+ uvalue |= (data[0] & 0x3f) << 18;
+ }
+ return (uvalue);
+}
+
+void
+utf8_split(u_int uvalue, u_char *data)
+{
+ memset(data, 0xff, 4);
+
+ if (uvalue <= 0x7f)
+ data[0] = uvalue;
+ else if (uvalue > 0x7f && uvalue <= 0x7ff) {
+ data[0] = (uvalue >> 6) | 0xc0;
+ data[1] = (uvalue & 0x3f) | 0x80;
+ } else if (uvalue > 0x7ff && uvalue <= 0xffff) {
+ data[0] = (uvalue >> 12) | 0xe0;
+ data[1] = ((uvalue >> 6) & 0x3f) | 0x80;
+ data[2] = (uvalue & 0x3f) | 0x80;
+ } else if (uvalue > 0xffff && uvalue <= 0x10ffff) {
+ data[0] = (uvalue >> 18) | 0xf0;
+ data[1] = ((uvalue >> 12) & 0x3f) | 0x80;
+ data[2] = ((uvalue >> 6) & 0x3f) | 0x80;
+ data[3] = (uvalue & 0x3f) | 0x80;
+ }
+}
+
+int
+utf8_width(u_char *udata)
+{
+ struct utf8_width_entry *item;
+ u_int uvalue;
+
+ uvalue = utf8_combine(udata);
+
+ item = utf8_width_root;
+ while (item != NULL) {
+ if (uvalue < item->first)
+ item = item->left;
+ else if (uvalue > item->last)
+ item = item->right;
+ else
+ return (item->width);
+ }
+ return (1);
+}
diff --git a/usr.bin/tmux/util.c b/usr.bin/tmux/util.c
new file mode 100644
index 00000000000..da0a50cfd06
--- /dev/null
+++ b/usr.bin/tmux/util.c
@@ -0,0 +1,72 @@
+/* $OpenBSD: util.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 "tmux.h"
+
+/* Return a section of a string around a point. */
+char *
+section_string(char *buf, size_t len, size_t sectoff, size_t sectlen)
+{
+ char *s;
+ size_t first, last;
+
+ if (len <= sectlen) {
+ first = 0;
+ last = len;
+ } else if (sectoff < sectlen / 2) {
+ first = 0;
+ last = sectlen;
+ } else if (sectoff + sectlen / 2 > len) {
+ last = len;
+ first = last - sectlen;
+ } else {
+ first = sectoff - sectlen / 2;
+ last = first + sectlen;
+ }
+
+ if (last - first > 3 && first != 0)
+ first += 3;
+ if (last - first > 3 && last != len)
+ last -= 3;
+
+ xasprintf(&s, "%s%.*s%s", first == 0 ? "" : "...",
+ (int) (last - first), buf + first, last == len ? "" : "...");
+ return (s);
+}
+
+/* Clean string of invisible characters. */
+void
+clean_string(const char *in, char *buf, size_t len)
+{
+ const u_char *cp;
+ size_t off;
+
+ off = 0;
+ for (cp = in; *cp != '\0'; cp++) {
+ if (off >= len)
+ break;
+ if (*cp >= 0x20 && *cp <= 0x7f)
+ buf[off++] = *cp;
+ else
+ off += xsnprintf(buf + off, len - off, "\\%03hho", *cp);
+ }
+ if (off < len)
+ buf[off] = '\0';
+}
diff --git a/usr.bin/tmux/window-choose.c b/usr.bin/tmux/window-choose.c
new file mode 100644
index 00000000000..21c43b27160
--- /dev/null
+++ b/usr.bin/tmux/window-choose.c
@@ -0,0 +1,361 @@
+/* $OpenBSD: window-choose.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+struct screen *window_choose_init(struct window_pane *);
+void window_choose_free(struct window_pane *);
+void window_choose_resize(struct window_pane *, u_int, u_int);
+void window_choose_key(struct window_pane *, struct client *, int);
+void window_choose_mouse(
+ struct window_pane *, struct client *, u_char, u_char, u_char);
+
+void window_choose_redraw_screen(struct window_pane *);
+void window_choose_write_line(
+ struct window_pane *, struct screen_write_ctx *, u_int);
+
+void window_choose_scroll_up(struct window_pane *);
+void window_choose_scroll_down(struct window_pane *);
+
+const struct window_mode window_choose_mode = {
+ window_choose_init,
+ window_choose_free,
+ window_choose_resize,
+ window_choose_key,
+ window_choose_mouse,
+ NULL,
+};
+
+struct window_choose_mode_item {
+ char *name;
+ int idx;
+};
+
+struct window_choose_mode_data {
+ struct screen screen;
+
+ struct mode_key_data mdata;
+
+ ARRAY_DECL(, struct window_choose_mode_item) list;
+ u_int top;
+ u_int selected;
+
+ void (*callback)(void *, int);
+ void *data;
+};
+
+void
+window_choose_vadd(struct window_pane *wp, int idx, const char *fmt, va_list ap)
+{
+ struct window_choose_mode_data *data = wp->modedata;
+ struct window_choose_mode_item *item;
+
+ ARRAY_EXPAND(&data->list, 1);
+ item = &ARRAY_LAST(&data->list);
+ xvasprintf(&item->name, fmt, ap);
+ item->idx = idx;
+}
+
+void printflike3
+window_choose_add(struct window_pane *wp, int idx, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ window_choose_vadd(wp, idx, fmt, ap);
+ va_end(ap);
+}
+
+void
+window_choose_ready(struct window_pane *wp,
+ u_int cur, void (*callback)(void *, int), void *cdata)
+{
+ struct window_choose_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+
+ data->selected = cur;
+ if (data->selected > screen_size_y(s) - 1)
+ data->top = ARRAY_LENGTH(&data->list) - screen_size_y(s);
+
+ data->callback = callback;
+ data->data = cdata;
+
+ window_choose_redraw_screen(wp);
+}
+
+struct screen *
+window_choose_init(struct window_pane *wp)
+{
+ struct window_choose_mode_data *data;
+ struct screen *s;
+
+ wp->modedata = data = xmalloc(sizeof *data);
+ data->callback = NULL;
+ ARRAY_INIT(&data->list);
+ data->top = 0;
+
+ s = &data->screen;
+ screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
+ s->mode &= ~MODE_CURSOR;
+ s->mode |= MODE_MOUSE;
+
+ mode_key_init(&data->mdata,
+ options_get_number(&wp->window->options, "mode-keys"),
+ MODEKEY_CHOOSEMODE);
+
+ return (s);
+}
+
+void
+window_choose_free(struct window_pane *wp)
+{
+ struct window_choose_mode_data *data = wp->modedata;
+ u_int i;
+
+ mode_key_free(&data->mdata);
+
+ for (i = 0; i < ARRAY_LENGTH(&data->list); i++)
+ xfree(ARRAY_ITEM(&data->list, i).name);
+ ARRAY_FREE(&data->list);
+
+ screen_free(&data->screen);
+ xfree(data);
+}
+
+void
+window_choose_resize(struct window_pane *wp, u_int sx, u_int sy)
+{
+ struct window_choose_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+
+ data->top = 0;
+ if (data->selected > sy - 1)
+ data->top = data->selected - (sy - 1);
+
+ screen_resize(s, sx, sy);
+ window_choose_redraw_screen(wp);
+}
+
+void
+window_choose_key(struct window_pane *wp, unused struct client *c, int key)
+{
+ struct window_choose_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+ struct window_choose_mode_item *item;
+ u_int items;
+
+ items = ARRAY_LENGTH(&data->list);
+
+ switch (mode_key_lookup(&data->mdata, key)) {
+ case MODEKEYCMD_QUIT:
+ data->callback(data->data, -1);
+ window_pane_reset_mode(wp);
+ break;
+ case MODEKEYCMD_CHOOSE:
+ item = &ARRAY_ITEM(&data->list, data->selected);
+ data->callback(data->data, item->idx);
+ window_pane_reset_mode(wp);
+ break;
+ case MODEKEYCMD_UP:
+ if (items == 0)
+ break;
+ if (data->selected == 0) {
+ data->selected = items - 1;
+ if (data->selected > screen_size_y(s) - 1)
+ data->top = items - screen_size_y(s);
+ window_choose_redraw_screen(wp);
+ break;
+ }
+ data->selected--;
+ if (data->selected < data->top)
+ window_choose_scroll_up(wp);
+ else {
+ screen_write_start(&ctx, wp, NULL);
+ window_choose_write_line(
+ wp, &ctx, data->selected - data->top);
+ window_choose_write_line(
+ wp, &ctx, data->selected + 1 - data->top);
+ screen_write_stop(&ctx);
+ }
+ break;
+ case MODEKEYCMD_DOWN:
+ if (items == 0)
+ break;
+ if (data->selected == items - 1) {
+ data->selected = 0;
+ data->top = 0;
+ window_choose_redraw_screen(wp);
+ break;
+ }
+ data->selected++;
+ if (data->selected >= data->top + screen_size_y(&data->screen))
+ window_choose_scroll_down(wp);
+ else {
+ screen_write_start(&ctx, wp, NULL);
+ window_choose_write_line(
+ wp, &ctx, data->selected - data->top);
+ window_choose_write_line(
+ wp, &ctx, data->selected - 1 - data->top);
+ screen_write_stop(&ctx);
+ }
+ break;
+ case MODEKEYCMD_PREVIOUSPAGE:
+ if (data->selected < screen_size_y(s)) {
+ data->selected = 0;
+ data->top = 0;
+ } else {
+ data->selected -= screen_size_y(s);
+ if (data->top < screen_size_y(s))
+ data->top = 0;
+ else
+ data->top -= screen_size_y(s);
+ }
+ window_choose_redraw_screen(wp);
+ break;
+ case MODEKEYCMD_NEXTPAGE:
+ data->selected += screen_size_y(s);
+ if (data->selected > items - 1)
+ data->selected = items - 1;
+ data->top += screen_size_y(s);
+ if (screen_size_y(s) < items) {
+ if (data->top + screen_size_y(s) > items)
+ data->top = items - screen_size_y(s);
+ } else
+ data->top = 0;
+ if (data->selected < data->top)
+ data->top = data->selected;
+ window_choose_redraw_screen(wp);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+window_choose_mouse(struct window_pane *wp,
+ unused struct client *c, u_char b, u_char x, u_char y)
+{
+ struct window_choose_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct window_choose_mode_item *item;
+ u_int idx;
+
+ if ((b & 3) == 3)
+ return;
+ if (x >= screen_size_x(s))
+ return;
+ if (y >= screen_size_y(s))
+ return;
+
+ idx = data->top + y;
+ if (idx >= ARRAY_LENGTH(&data->list))
+ return;
+ data->selected = idx;
+
+ item = &ARRAY_ITEM(&data->list, data->selected);
+ data->callback(data->data, item->idx);
+ window_pane_reset_mode(wp);
+}
+
+void
+window_choose_write_line(
+ struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
+{
+ struct window_choose_mode_data *data = wp->modedata;
+ struct window_choose_mode_item *item;
+ struct screen *s = &data->screen;
+ struct grid_cell gc;
+
+ if (data->callback == NULL)
+ fatalx("called before callback assigned");
+
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ if (data->selected == data->top + py) {
+ gc.fg = options_get_number(&wp->window->options, "mode-bg");
+ gc.bg = options_get_number(&wp->window->options, "mode-fg");
+ gc.attr |= options_get_number(&wp->window->options, "mode-attr");
+ }
+
+ screen_write_cursormove(ctx, 0, py);
+ if (data->top + py < ARRAY_LENGTH(&data->list)) {
+ item = &ARRAY_ITEM(&data->list, data->top + py);
+ screen_write_puts(
+ ctx, &gc, "%.*s", (int) screen_size_x(s), item->name);
+ }
+ while (s->cx < screen_size_x(s))
+ screen_write_putc(ctx, &gc, ' ');
+
+}
+
+void
+window_choose_redraw_screen(struct window_pane *wp)
+{
+ struct window_choose_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+ u_int i;
+
+ screen_write_start(&ctx, wp, NULL);
+ for (i = 0; i < screen_size_y(s); i++)
+ window_choose_write_line(wp, &ctx, i);
+ screen_write_stop(&ctx);
+}
+
+void
+window_choose_scroll_up(struct window_pane *wp)
+{
+ struct window_choose_mode_data *data = wp->modedata;
+ struct screen_write_ctx ctx;
+
+ if (data->top == 0)
+ return;
+ data->top--;
+
+ screen_write_start(&ctx, wp, NULL);
+ screen_write_cursormove(&ctx, 0, 0);
+ screen_write_insertline(&ctx, 1);
+ window_choose_write_line(wp, &ctx, 0);
+ if (screen_size_y(&data->screen) > 1)
+ window_choose_write_line(wp, &ctx, 1);
+ screen_write_stop(&ctx);
+}
+
+void
+window_choose_scroll_down(struct window_pane *wp)
+{
+ struct window_choose_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+
+ if (data->top >= ARRAY_LENGTH(&data->list))
+ return;
+ data->top++;
+
+ screen_write_start(&ctx, wp, NULL);
+ screen_write_cursormove(&ctx, 0, 0);
+ screen_write_deleteline(&ctx, 1);
+ window_choose_write_line(wp, &ctx, screen_size_y(s) - 1);
+ if (screen_size_y(&data->screen) > 1)
+ window_choose_write_line(wp, &ctx, screen_size_y(s) - 2);
+ screen_write_stop(&ctx);
+}
diff --git a/usr.bin/tmux/window-clock.c b/usr.bin/tmux/window-clock.c
new file mode 100644
index 00000000000..1cc748e44d4
--- /dev/null
+++ b/usr.bin/tmux/window-clock.c
@@ -0,0 +1,124 @@
+/* $OpenBSD: window-clock.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+#include <time.h>
+
+#include "tmux.h"
+
+struct screen *window_clock_init(struct window_pane *);
+void window_clock_free(struct window_pane *);
+void window_clock_resize(struct window_pane *, u_int, u_int);
+void window_clock_key(struct window_pane *, struct client *, int);
+void window_clock_timer(struct window_pane *);
+
+void window_clock_draw_screen(struct window_pane *);
+
+const struct window_mode window_clock_mode = {
+ window_clock_init,
+ window_clock_free,
+ window_clock_resize,
+ window_clock_key,
+ NULL,
+ window_clock_timer,
+};
+
+struct window_clock_mode_data {
+ struct screen screen;
+ time_t tim;
+};
+
+struct screen *
+window_clock_init(struct window_pane *wp)
+{
+ struct window_clock_mode_data *data;
+ struct screen *s;
+
+ wp->modedata = data = xmalloc(sizeof *data);
+ data->tim = time(NULL);
+
+ s = &data->screen;
+ screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
+ s->mode &= ~MODE_CURSOR;
+
+ window_clock_draw_screen(wp);
+
+ return (s);
+}
+
+void
+window_clock_free(struct window_pane *wp)
+{
+ struct window_clock_mode_data *data = wp->modedata;
+
+ screen_free(&data->screen);
+ xfree(data);
+}
+
+void
+window_clock_resize(struct window_pane *wp, u_int sx, u_int sy)
+{
+ struct window_clock_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+
+ screen_resize(s, sx, sy);
+ window_clock_draw_screen(wp);
+}
+
+void
+window_clock_key(
+ struct window_pane *wp, unused struct client *c, unused int key)
+{
+ window_pane_reset_mode(wp);
+}
+
+void
+window_clock_timer(struct window_pane *wp)
+{
+ struct window_clock_mode_data *data = wp->modedata;
+ struct tm *now, *then;
+ time_t t;
+
+ t = time(NULL);
+ now = gmtime(&t);
+ then = gmtime(&data->tim);
+ if (now->tm_min == then->tm_min)
+ return;
+ data->tim = t;
+
+ window_clock_draw_screen(wp);
+ server_redraw_window(wp->window);
+}
+
+void
+window_clock_draw_screen(struct window_pane *wp)
+{
+ struct window_clock_mode_data *data = wp->modedata;
+ struct screen_write_ctx ctx;
+ u_int colour;
+ int style;
+
+ colour = options_get_number(&wp->window->options, "clock-mode-colour");
+ style = options_get_number(&wp->window->options, "clock-mode-style");
+
+ screen_write_start(&ctx, NULL, &data->screen);
+ clock_draw(&ctx, colour, style);
+ screen_write_stop(&ctx);
+}
diff --git a/usr.bin/tmux/window-copy.c b/usr.bin/tmux/window-copy.c
new file mode 100644
index 00000000000..8a291bfc11c
--- /dev/null
+++ b/usr.bin/tmux/window-copy.c
@@ -0,0 +1,968 @@
+/* $OpenBSD: window-copy.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+struct screen *window_copy_init(struct window_pane *);
+void window_copy_free(struct window_pane *);
+void window_copy_resize(struct window_pane *, u_int, u_int);
+void window_copy_key(struct window_pane *, struct client *, int);
+void window_copy_mouse(
+ struct window_pane *, struct client *, u_char, u_char, u_char);
+
+void window_copy_redraw_lines(struct window_pane *, u_int, u_int);
+void window_copy_redraw_screen(struct window_pane *);
+void window_copy_write_line(
+ struct window_pane *, struct screen_write_ctx *, u_int);
+void window_copy_write_lines(
+ struct window_pane *, struct screen_write_ctx *, u_int, u_int);
+void window_copy_write_column(
+ struct window_pane *, struct screen_write_ctx *, u_int);
+void window_copy_write_columns(
+ struct window_pane *, struct screen_write_ctx *, u_int, u_int);
+
+void window_copy_update_cursor(struct window_pane *);
+void window_copy_start_selection(struct window_pane *);
+int window_copy_update_selection(struct window_pane *);
+void window_copy_copy_selection(struct window_pane *, struct client *);
+void window_copy_copy_line(
+ struct window_pane *, char **, size_t *, u_int, u_int, u_int);
+int window_copy_is_space(struct window_pane *, u_int, u_int);
+u_int window_copy_find_length(struct window_pane *, u_int);
+void window_copy_cursor_start_of_line(struct window_pane *);
+void window_copy_cursor_end_of_line(struct window_pane *);
+void window_copy_cursor_left(struct window_pane *);
+void window_copy_cursor_right(struct window_pane *);
+void window_copy_cursor_up(struct window_pane *);
+void window_copy_cursor_down(struct window_pane *);
+void window_copy_cursor_next_word(struct window_pane *);
+void window_copy_cursor_previous_word(struct window_pane *);
+void window_copy_scroll_left(struct window_pane *, u_int);
+void window_copy_scroll_right(struct window_pane *, u_int);
+void window_copy_scroll_up(struct window_pane *, u_int);
+void window_copy_scroll_down(struct window_pane *, u_int);
+
+const struct window_mode window_copy_mode = {
+ window_copy_init,
+ window_copy_free,
+ window_copy_resize,
+ window_copy_key,
+ window_copy_mouse,
+ NULL,
+};
+
+struct window_copy_mode_data {
+ struct screen screen;
+
+ struct mode_key_data mdata;
+
+ u_int ox;
+ u_int oy;
+
+ u_int selx;
+ u_int sely;
+
+ u_int cx;
+ u_int cy;
+};
+
+struct screen *
+window_copy_init(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data;
+ struct screen *s;
+ struct screen_write_ctx ctx;
+ u_int i;
+
+ wp->modedata = data = xmalloc(sizeof *data);
+ data->ox = 0;
+ data->oy = 0;
+ data->cx = wp->base.cx;
+ data->cy = wp->base.cy;
+
+ s = &data->screen;
+ screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
+ s->mode |= MODE_MOUSE;
+
+ mode_key_init(&data->mdata,
+ options_get_number(&wp->window->options, "mode-keys"), 0);
+
+ s->cx = data->cx;
+ s->cy = data->cy;
+
+ screen_write_start(&ctx, NULL, s);
+ for (i = 0; i < screen_size_y(s); i++)
+ window_copy_write_line(wp, &ctx, i);
+ screen_write_cursormove(&ctx, data->cx, data->cy);
+ screen_write_stop(&ctx);
+
+ return (s);
+}
+
+void
+window_copy_free(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+
+ mode_key_free(&data->mdata);
+
+ screen_free(&data->screen);
+ xfree(data);
+}
+
+void
+window_copy_pageup(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+
+ if (data->oy + screen_size_y(s) > screen_hsize(&wp->base))
+ data->oy = screen_hsize(&wp->base);
+ else
+ data->oy += screen_size_y(s);
+ window_copy_update_selection(wp);
+ window_copy_redraw_screen(wp);
+}
+
+void
+window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+
+ screen_resize(s, sx, sy);
+ screen_write_start(&ctx, NULL, s);
+ window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1);
+ screen_write_stop(&ctx);
+ window_copy_update_selection(wp);
+}
+
+void
+window_copy_key(struct window_pane *wp, struct client *c, int key)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+
+ switch (mode_key_lookup(&data->mdata, key)) {
+ case MODEKEYCMD_QUIT:
+ window_pane_reset_mode(wp);
+ break;
+ case MODEKEYCMD_LEFT:
+ window_copy_cursor_left(wp);
+ return;
+ case MODEKEYCMD_RIGHT:
+ window_copy_cursor_right(wp);
+ return;
+ case MODEKEYCMD_UP:
+ window_copy_cursor_up(wp);
+ return;
+ case MODEKEYCMD_DOWN:
+ window_copy_cursor_down(wp);
+ return;
+ case MODEKEYCMD_PREVIOUSPAGE:
+ window_copy_pageup(wp);
+ break;
+ case MODEKEYCMD_NEXTPAGE:
+ if (data->oy < screen_size_y(s))
+ data->oy = 0;
+ else
+ data->oy -= screen_size_y(s);
+ window_copy_update_selection(wp);
+ window_copy_redraw_screen(wp);
+ break;
+ case MODEKEYCMD_STARTSELECTION:
+ window_copy_start_selection(wp);
+ break;
+ case MODEKEYCMD_CLEARSELECTION:
+ screen_clear_selection(&data->screen);
+ window_copy_redraw_screen(wp);
+ break;
+ case MODEKEYCMD_COPYSELECTION:
+ if (c != NULL && c->session != NULL) {
+ window_copy_copy_selection(wp, c);
+ window_pane_reset_mode(wp);
+ }
+ break;
+ case MODEKEYCMD_STARTOFLINE:
+ window_copy_cursor_start_of_line(wp);
+ break;
+ case MODEKEYCMD_ENDOFLINE:
+ window_copy_cursor_end_of_line(wp);
+ break;
+ case MODEKEYCMD_NEXTWORD:
+ window_copy_cursor_next_word(wp);
+ break;
+ case MODEKEYCMD_PREVIOUSWORD:
+ window_copy_cursor_previous_word(wp);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+window_copy_mouse(struct window_pane *wp,
+ unused struct client *c, u_char b, u_char x, u_char y)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+
+ if ((b & 3) == 3)
+ return;
+ if (x >= screen_size_x(s))
+ return;
+ if (y >= screen_size_y(s))
+ return;
+
+ data->cx = x;
+ data->cy = y;
+
+ if (window_copy_update_selection(wp))
+ window_copy_redraw_screen(wp);
+ window_copy_update_cursor(wp);
+}
+
+void
+window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct grid_cell gc;
+ char hdr[32];
+ size_t size;
+
+ if (py == 0) {
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ size = xsnprintf(hdr, sizeof hdr,
+ "[%u,%u/%u]", data->ox, data->oy, screen_hsize(&wp->base));
+ gc.bg = options_get_number(&wp->window->options, "mode-fg");
+ gc.fg = options_get_number(&wp->window->options, "mode-bg");
+ gc.attr |= options_get_number(&wp->window->options, "mode-attr");
+ screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
+ screen_write_puts(ctx, &gc, "%s", hdr);
+ } else
+ size = 0;
+
+ screen_write_cursormove(ctx, 0, py);
+ screen_write_copy(ctx, &wp->base, data->ox, (screen_hsize(&wp->base) -
+ data->oy) + py, screen_size_x(s) - size, 1);
+}
+
+void
+window_copy_write_lines(
+ struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny)
+{
+ u_int yy;
+
+ for (yy = py; yy < py + ny; yy++)
+ window_copy_write_line(wp, ctx, py);
+}
+
+void
+window_copy_write_column(
+ struct window_pane *wp, struct screen_write_ctx *ctx, u_int px)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+
+ screen_write_cursormove(ctx, px, 0);
+ screen_write_copy(ctx, &wp->base,
+ data->ox + px, screen_hsize(&wp->base) - data->oy, 1, screen_size_y(s));
+}
+
+void
+window_copy_write_columns(
+ struct window_pane *wp, struct screen_write_ctx *ctx, u_int px, u_int nx)
+{
+ u_int xx;
+
+ for (xx = px; xx < px + nx; xx++)
+ window_copy_write_column(wp, ctx, xx);
+}
+
+void
+window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen_write_ctx ctx;
+ u_int i;
+
+ screen_write_start(&ctx, wp, NULL);
+ for (i = py; i < py + ny; i++)
+ window_copy_write_line(wp, &ctx, i);
+ screen_write_cursormove(&ctx, data->cx, data->cy);
+ screen_write_stop(&ctx);
+}
+
+void
+window_copy_redraw_screen(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+
+ window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
+}
+
+void
+window_copy_update_cursor(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen_write_ctx ctx;
+
+ screen_write_start(&ctx, wp, NULL);
+ screen_write_cursormove(&ctx, data->cx, data->cy);
+ screen_write_stop(&ctx);
+}
+
+void
+window_copy_start_selection(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+
+ data->selx = data->cx + data->ox;
+ data->sely = screen_hsize(&wp->base) + data->cy - data->oy;
+
+ s->sel.flag = 1;
+ window_copy_update_selection(wp);
+}
+
+int
+window_copy_update_selection(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct grid_cell gc;
+ u_int sx, sy, tx, ty;
+
+ if (!s->sel.flag)
+ return (0);
+
+ /* Set colours. */
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ gc.bg = options_get_number(&wp->window->options, "mode-fg");
+ gc.fg = options_get_number(&wp->window->options, "mode-bg");
+ gc.attr |= options_get_number(&wp->window->options, "mode-attr");
+
+ /* Find top-left of screen. */
+ tx = data->ox;
+ ty = screen_hsize(&wp->base) - data->oy;
+
+ /* Adjust the selection. */
+ sx = data->selx;
+ sy = data->sely;
+ if (sy < ty) {
+ /* Above it. */
+ sx = 0;
+ sy = 0;
+ } else if (sy > ty + screen_size_y(s) - 1) {
+ /* Below it. */
+ sx = screen_size_x(s) - 1;
+ sy = screen_size_y(s) - 1;
+ } else if (sx < tx) {
+ /* To the left. */
+ sx = 0;
+ } else if (sx > tx + screen_size_x(s) - 1) {
+ /* To the right. */
+ sx = 0;
+ sy++;
+ if (sy > screen_size_y(s) - 1)
+ sy = screen_size_y(s) - 1;
+ } else {
+ sx -= tx;
+ sy -= ty;
+ }
+ sy = screen_hsize(s) + sy;
+
+ screen_set_selection(
+ s, sx, sy, data->cx, screen_hsize(s) + data->cy, &gc);
+ return (1);
+}
+
+void
+window_copy_copy_selection(struct window_pane *wp, struct client *c)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ char *buf;
+ size_t off;
+ u_int i, xx, yy, sx, sy, ex, ey, limit;
+
+ if (!s->sel.flag)
+ return;
+
+ buf = xmalloc(1);
+ off = 0;
+
+ *buf = '\0';
+
+ /*
+ * The selection extends from selx,sely to (adjusted) cx,cy on
+ * the base screen.
+ */
+
+ /* Find start and end. */
+ xx = data->cx + data->ox;
+ yy = screen_hsize(&wp->base) + data->cy - data->oy;
+ if (xx < data->selx || (yy == data->sely && xx < data->selx)) {
+ sx = xx; sy = yy;
+ ex = data->selx; ey = data->sely;
+ } else {
+ sx = data->selx; sy = data->sely;
+ ex = xx; ey = yy;
+ }
+
+ /* Trim ex to end of line. */
+ xx = window_copy_find_length(wp, ey);
+ if (ex > xx)
+ ex = xx;
+
+ /* Copy the lines. */
+ if (sy == ey)
+ window_copy_copy_line(wp, &buf, &off, sy, sx, ex);
+ else {
+ xx = window_copy_find_length(wp, sy);
+ window_copy_copy_line(wp, &buf, &off, sy, sx, xx);
+ if (ey - sy > 1) {
+ for (i = sy + 1; i < ey - 1; i++) {
+ xx = window_copy_find_length(wp, i);
+ window_copy_copy_line(wp, &buf, &off, i, 0, xx);
+ }
+ }
+ window_copy_copy_line(wp, &buf, &off, ey, 0, ex);
+ }
+
+ /* Terminate buffer, overwriting final \n. */
+ if (off != 0)
+ buf[off - 1] = '\0';
+
+ /* Add the buffer to the stack. */
+ limit = options_get_number(&c->session->options, "buffer-limit");
+ paste_add(&c->session->buffers, buf, limit);
+}
+
+void
+window_copy_copy_line(struct window_pane *wp,
+ char **buf, size_t *off, u_int sy, u_int sx, u_int ex)
+{
+ const struct grid_cell *gc;
+ const struct grid_utf8 *gu;
+ u_int i, j, xx;
+
+ if (sx > ex)
+ return;
+
+ xx = window_copy_find_length(wp, sy);
+ if (ex > xx)
+ ex = xx;
+ if (sx > xx)
+ sx = xx;
+
+ if (sx < ex) {
+ for (i = sx; i < ex; i++) {
+ gc = grid_peek_cell(wp->base.grid, i, sy);
+ if (gc->flags & GRID_FLAG_PADDING)
+ continue;
+ if (!(gc->flags & GRID_FLAG_UTF8)) {
+ *buf = xrealloc(*buf, 1, (*off) + 1);
+ (*buf)[(*off)++] = gc->data;
+ } else {
+ gu = grid_peek_utf8(wp->base.grid, i, sy);
+ *buf = xrealloc(*buf, 1, (*off) + UTF8_SIZE);
+ for (j = 0; j < UTF8_SIZE; j++) {
+ if (gu->data[j] == 0xff)
+ break;
+ (*buf)[(*off)++] = gu->data[j];
+ }
+ }
+ }
+ }
+
+ *buf = xrealloc(*buf, 1, (*off) + 1);
+ (*buf)[*off] = '\n';
+ (*off)++;
+}
+
+int
+window_copy_is_space(struct window_pane *wp, u_int px, u_int py)
+{
+ const struct grid_cell *gc;
+ const char *spaces = " -_@";
+
+ gc = grid_peek_cell(wp->base.grid, px, py);
+ if (gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8))
+ return (0);
+ if (gc->data == 0x00 || gc->data == 0x7f)
+ return (0);
+ return (strchr(spaces, gc->data) != NULL);
+}
+
+u_int
+window_copy_find_length(struct window_pane *wp, u_int py)
+{
+ const struct grid_cell *gc;
+ u_int px;
+
+ px = wp->base.grid->size[py];
+ while (px > 0) {
+ gc = grid_peek_cell(wp->base.grid, px - 1, py);
+ if (gc->flags & GRID_FLAG_UTF8)
+ break;
+ if (gc->data != ' ')
+ break;
+ px--;
+ }
+ return (px);
+}
+
+void
+window_copy_cursor_start_of_line(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+
+ if (data->ox != 0)
+ window_copy_scroll_right(wp, data->ox);
+ data->cx = 0;
+
+ if (window_copy_update_selection(wp))
+ window_copy_redraw_lines(wp, data->cy, 1);
+ else
+ window_copy_update_cursor(wp);
+}
+
+void
+window_copy_cursor_end_of_line(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ u_int px, py;
+
+ py = screen_hsize(&wp->base) + data->cy - data->oy;
+ px = window_copy_find_length(wp, py);
+
+ /* On screen. */
+ if (px > data->ox && px <= data->ox + screen_size_x(s) - 1)
+ data->cx = px - data->ox;
+
+ /* Off right of screen. */
+ if (px > data->ox + screen_size_x(s) - 1) {
+ /* Move cursor to last and scroll screen. */
+ window_copy_scroll_left(
+ wp, px - data->ox - (screen_size_x(s) - 1));
+ data->cx = screen_size_x(s) - 1;
+ }
+
+ /* Off left of screen. */
+ if (px <= data->ox) {
+ if (px < screen_size_x(s) - 1) {
+ /* Short enough to fit on screen. */
+ window_copy_scroll_right(wp, data->ox);
+ data->cx = px;
+ } else {
+ /* Too long to fit on screen. */
+ window_copy_scroll_right(
+ wp, data->ox - (px - (screen_size_x(s) - 1)));
+ data->cx = screen_size_x(s) - 1;
+ }
+ }
+
+ if (window_copy_update_selection(wp))
+ window_copy_redraw_lines(wp, data->cy, 1);
+ else
+ window_copy_update_cursor(wp);
+}
+
+void
+window_copy_cursor_left(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+
+ if (data->cx == 0) {
+ if (data->ox > 0)
+ window_copy_scroll_right(wp, 1);
+ else {
+ window_copy_cursor_up(wp);
+ window_copy_cursor_end_of_line(wp);
+ }
+ } else {
+ data->cx--;
+ if (window_copy_update_selection(wp))
+ window_copy_redraw_lines(wp, data->cy, 1);
+ else
+ window_copy_update_cursor(wp);
+ }
+}
+
+void
+window_copy_cursor_right(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ u_int px, py;
+
+ py = screen_hsize(&wp->base) + data->cy - data->oy;
+ px = window_copy_find_length(wp, py);
+
+ if (data->cx >= px) {
+ window_copy_cursor_start_of_line(wp);
+ window_copy_cursor_down(wp);
+ } else {
+ data->cx++;
+ if (window_copy_update_selection(wp))
+ window_copy_redraw_lines(wp, data->cy, 1);
+ else
+ window_copy_update_cursor(wp);
+ }
+}
+
+void
+window_copy_cursor_up(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ u_int ox, oy, px, py;
+
+ oy = screen_hsize(&wp->base) + data->cy - data->oy;
+ ox = window_copy_find_length(wp, oy);
+
+ if (data->cy == 0)
+ window_copy_scroll_down(wp, 1);
+ else {
+ data->cy--;
+ if (window_copy_update_selection(wp))
+ window_copy_redraw_lines(wp, data->cy, 2);
+ else
+ window_copy_update_cursor(wp);
+ }
+
+ py = screen_hsize(&wp->base) + data->cy - data->oy;
+ px = window_copy_find_length(wp, py);
+
+ if (data->cx + data->ox >= px || data->cx + data->ox >= ox)
+ window_copy_cursor_end_of_line(wp);
+}
+
+void
+window_copy_cursor_down(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ u_int ox, oy, px, py;
+
+ oy = screen_hsize(&wp->base) + data->cy - data->oy;
+ ox = window_copy_find_length(wp, oy);
+
+ if (data->cy == screen_size_y(s) - 1)
+ window_copy_scroll_up(wp, 1);
+ else {
+ data->cy++;
+ if (window_copy_update_selection(wp))
+ window_copy_redraw_lines(wp, data->cy - 1, 2);
+ else
+ window_copy_update_cursor(wp);
+ }
+
+ py = screen_hsize(&wp->base) + data->cy - data->oy;
+ px = window_copy_find_length(wp, py);
+
+ if (data->cx + data->ox >= px || data->cx + data->ox >= ox)
+ window_copy_cursor_end_of_line(wp);
+}
+
+void
+window_copy_cursor_next_word(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ u_int px, py, xx, skip;
+
+ px = data->ox + data->cx;
+ py = screen_hsize(&wp->base) + data->cy - data->oy;
+ xx = window_copy_find_length(wp, py);
+
+ skip = 1;
+ if (px < xx) {
+ /* If currently on a space, skip space. */
+ if (window_copy_is_space(wp, px, py))
+ skip = 0;
+ } else
+ skip = 0;
+ for (;;) {
+ if (px >= xx) {
+ if (skip) {
+ px = xx;
+ break;
+ }
+
+ while (px >= xx) {
+ if (data->cy == screen_size_y(s) - 1) {
+ if (data->oy == 0)
+ goto out;
+ }
+
+ px = 0;
+ window_copy_cursor_down(wp);
+
+ py =screen_hsize(
+ &wp->base) + data->cy - data->oy;
+ xx = window_copy_find_length(wp, py);
+ }
+ }
+
+ if (skip) {
+ /* Currently skipping non-space (until space). */
+ if (window_copy_is_space(wp, px, py))
+ break;
+ } else {
+ /* Currently skipping space (until non-space). */
+ if (!window_copy_is_space(wp, px, py))
+ skip = 1;
+ }
+
+ px++;
+ }
+out:
+
+ /* On screen. */
+ if (px > data->ox && px <= data->ox + screen_size_x(s) - 1)
+ data->cx = px - data->ox;
+
+ /* Off right of screen. */
+ if (px > data->ox + screen_size_x(s) - 1) {
+ /* Move cursor to last and scroll screen. */
+ window_copy_scroll_left(
+ wp, px - data->ox - (screen_size_x(s) - 1));
+ data->cx = screen_size_x(s) - 1;
+ }
+
+ /* Off left of screen. */
+ if (px <= data->ox) {
+ if (px < screen_size_x(s) - 1) {
+ /* Short enough to fit on screen. */
+ window_copy_scroll_right(wp, data->ox);
+ data->cx = px;
+ } else {
+ /* Too long to fit on screen. */
+ window_copy_scroll_right(
+ wp, data->ox - (px - (screen_size_x(s) - 1)));
+ data->cx = screen_size_x(s) - 1;
+ }
+ }
+
+ if (window_copy_update_selection(wp))
+ window_copy_redraw_lines(wp, data->cy, 1);
+ else
+ window_copy_update_cursor(wp);
+}
+
+void
+window_copy_cursor_previous_word(struct window_pane *wp)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ u_int ox, px, py, skip;
+
+ ox = px = data->ox + data->cx;
+ py = screen_hsize(&wp->base) + data->cy - data->oy;
+
+ skip = 1;
+ if (px != 0) {
+ /* If currently on a space, skip space. */
+ if (window_copy_is_space(wp, px - 1, py))
+ skip = 0;
+ }
+ for (;;) {
+ if (px == 0) {
+ if (ox != 0)
+ break;
+
+ while (px == 0) {
+ if (data->cy == 0 &&
+ (screen_hsize(&wp->base) == 0 ||
+ data->oy >= screen_hsize(&wp->base) - 1))
+ goto out;
+
+ window_copy_cursor_up(wp);
+
+ py = screen_hsize(
+ &wp->base) + data->cy - data->oy;
+ px = window_copy_find_length(wp, py);
+ }
+ goto out;
+ }
+
+ if (skip) {
+ /* Currently skipping non-space (until space). */
+ if (window_copy_is_space(wp, px - 1, py))
+ skip = 0;
+ } else {
+ /* Currently skipping space (until non-space). */
+ if (!window_copy_is_space(wp, px - 1, py))
+ break;
+ }
+
+ px--;
+ }
+out:
+
+ /* On screen. */
+ if (px > data->ox && px <= data->ox + screen_size_x(s) - 1)
+ data->cx = px - data->ox;
+
+ /* Off right of screen. */
+ if (px > data->ox + screen_size_x(s) - 1) {
+ /* Move cursor to last and scroll screen. */
+ window_copy_scroll_left(
+ wp, px - data->ox - (screen_size_x(s) - 1));
+ data->cx = screen_size_x(s) - 1;
+ }
+
+ /* Off left of screen. */
+ if (px <= data->ox) {
+ if (px < screen_size_x(s) - 1) {
+ /* Short enough to fit on screen. */
+ window_copy_scroll_right(wp, data->ox);
+ data->cx = px;
+ } else {
+ /* Too long to fit on screen. */
+ window_copy_scroll_right(
+ wp, data->ox - (px - (screen_size_x(s) - 1)));
+ data->cx = screen_size_x(s) - 1;
+ }
+ }
+
+ if (window_copy_update_selection(wp))
+ window_copy_redraw_lines(wp, data->cy, 1);
+ else
+ window_copy_update_cursor(wp);
+}
+
+void
+window_copy_scroll_left(struct window_pane *wp, u_int nx)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+ u_int i;
+
+ if (data->ox > SHRT_MAX - nx)
+ nx = SHRT_MAX - data->ox;
+ if (nx == 0)
+ return;
+ data->ox += nx;
+ window_copy_update_selection(wp);
+
+ screen_write_start(&ctx, wp, NULL);
+ for (i = 1; i < screen_size_y(s); i++) {
+ screen_write_cursormove(&ctx, 0, i);
+ screen_write_deletecharacter(&ctx, nx);
+ }
+ window_copy_write_columns(wp, &ctx, screen_size_x(s) - nx, nx);
+ window_copy_write_line(wp, &ctx, 0);
+ if (s->sel.flag) {
+ window_copy_update_selection(wp);
+ window_copy_write_lines(wp, &ctx, data->cy, 1);
+ }
+ screen_write_cursormove(&ctx, data->cx, data->cy);
+ screen_write_stop(&ctx);
+}
+
+void
+window_copy_scroll_right(struct window_pane *wp, u_int nx)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+ u_int i;
+
+ if (data->ox < nx)
+ nx = data->ox;
+ if (nx == 0)
+ return;
+ data->ox -= nx;
+ window_copy_update_selection(wp);
+
+ screen_write_start(&ctx, wp, NULL);
+ for (i = 1; i < screen_size_y(s); i++) {
+ screen_write_cursormove(&ctx, 0, i);
+ screen_write_insertcharacter(&ctx, nx);
+ }
+ window_copy_write_columns(wp, &ctx, 0, nx);
+ window_copy_write_line(wp, &ctx, 0);
+ if (s->sel.flag)
+ window_copy_write_line(wp, &ctx, data->cy);
+ screen_write_cursormove(&ctx, data->cx, data->cy);
+ screen_write_stop(&ctx);
+}
+
+void
+window_copy_scroll_up(struct window_pane *wp, u_int ny)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+
+ if (data->oy < ny)
+ ny = data->oy;
+ if (ny == 0)
+ return;
+ data->oy -= ny;
+ window_copy_update_selection(wp);
+
+ screen_write_start(&ctx, wp, NULL);
+ screen_write_cursormove(&ctx, 0, 0);
+ screen_write_deleteline(&ctx, ny);
+ window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny);
+ window_copy_write_line(wp, &ctx, 0);
+ window_copy_write_line(wp, &ctx, 1);
+ if (s->sel.flag && screen_size_y(s) > ny)
+ window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
+ screen_write_cursormove(&ctx, data->cx, data->cy);
+ screen_write_stop(&ctx);
+}
+
+void
+window_copy_scroll_down(struct window_pane *wp, u_int ny)
+{
+ struct window_copy_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+
+ if (ny > screen_hsize(&wp->base))
+ return;
+
+ if (data->oy > screen_hsize(&wp->base) - ny)
+ ny = screen_hsize(&wp->base) - data->oy;
+ if (ny == 0)
+ return;
+ data->oy += ny;
+ window_copy_update_selection(wp);
+
+ screen_write_start(&ctx, wp, NULL);
+ screen_write_cursormove(&ctx, 0, 0);
+ screen_write_insertline(&ctx, ny);
+ window_copy_write_lines(wp, &ctx, 0, ny);
+ if (s->sel.flag && screen_size_y(s) > ny)
+ window_copy_write_line(wp, &ctx, ny);
+ else if (ny == 1) /* nuke position */
+ window_copy_write_line(wp, &ctx, 1);
+ screen_write_cursormove(&ctx, data->cx, data->cy);
+ screen_write_stop(&ctx);
+}
diff --git a/usr.bin/tmux/window-more.c b/usr.bin/tmux/window-more.c
new file mode 100644
index 00000000000..3f75f278252
--- /dev/null
+++ b/usr.bin/tmux/window-more.c
@@ -0,0 +1,252 @@
+/* $OpenBSD: window-more.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+struct screen *window_more_init(struct window_pane *);
+void window_more_free(struct window_pane *);
+void window_more_resize(struct window_pane *, u_int, u_int);
+void window_more_key(struct window_pane *, struct client *, int);
+
+void window_more_redraw_screen(struct window_pane *);
+void window_more_write_line(
+ struct window_pane *, struct screen_write_ctx *, u_int);
+
+void window_more_scroll_up(struct window_pane *);
+void window_more_scroll_down(struct window_pane *);
+
+const struct window_mode window_more_mode = {
+ window_more_init,
+ window_more_free,
+ window_more_resize,
+ window_more_key,
+ NULL,
+ NULL,
+};
+
+struct window_more_mode_data {
+ struct screen screen;
+
+ struct mode_key_data mdata;
+
+ ARRAY_DECL(, char *) list;
+ u_int top;
+};
+
+void
+window_more_vadd(struct window_pane *wp, const char *fmt, va_list ap)
+{
+ struct window_more_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+ char *msg;
+ u_int size;
+
+ xvasprintf(&msg, fmt, ap);
+ ARRAY_ADD(&data->list, msg);
+
+ screen_write_start(&ctx, wp, NULL);
+ size = ARRAY_LENGTH(&data->list) - 1;
+ if (size >= data->top && size <= data->top + screen_size_y(s) - 1) {
+ window_more_write_line(wp, &ctx, size - data->top);
+ if (size != data->top)
+ window_more_write_line(wp, &ctx, 0);
+ } else
+ window_more_write_line(wp, &ctx, 0);
+ screen_write_stop(&ctx);
+}
+
+void printflike2
+window_more_add(struct window_pane *wp, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ window_more_vadd(wp, fmt, ap);
+ va_end(ap);
+}
+
+struct screen *
+window_more_init(struct window_pane *wp)
+{
+ struct window_more_mode_data *data;
+ struct screen *s;
+
+ wp->modedata = data = xmalloc(sizeof *data);
+ ARRAY_INIT(&data->list);
+ data->top = 0;
+
+ s = &data->screen;
+ screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
+ s->mode &= ~MODE_CURSOR;
+
+ mode_key_init(&data->mdata,
+ options_get_number(&wp->window->options, "mode-keys"), 0);
+
+ return (s);
+}
+
+void
+window_more_free(struct window_pane *wp)
+{
+ struct window_more_mode_data *data = wp->modedata;
+ u_int i;
+
+ mode_key_free(&data->mdata);
+
+ for (i = 0; i < ARRAY_LENGTH(&data->list); i++)
+ xfree(ARRAY_ITEM(&data->list, i));
+ ARRAY_FREE(&data->list);
+
+ screen_free(&data->screen);
+ xfree(data);
+}
+
+void
+window_more_resize(struct window_pane *wp, u_int sx, u_int sy)
+{
+ struct window_more_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+
+ screen_resize(s, sx, sy);
+ window_more_redraw_screen(wp);
+}
+
+void
+window_more_key(struct window_pane *wp, unused struct client *c, int key)
+{
+ struct window_more_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+
+ switch (mode_key_lookup(&data->mdata, key)) {
+ case MODEKEYCMD_QUIT:
+ window_pane_reset_mode(wp);
+ break;
+ case MODEKEYCMD_UP:
+ window_more_scroll_up(wp);
+ break;
+ case MODEKEYCMD_DOWN:
+ window_more_scroll_down(wp);
+ break;
+ case MODEKEYCMD_PREVIOUSPAGE:
+ if (data->top < screen_size_y(s))
+ data->top = 0;
+ else
+ data->top -= screen_size_y(s);
+ window_more_redraw_screen(wp);
+ break;
+ case MODEKEYCMD_NEXTPAGE:
+ if (data->top + screen_size_y(s) > ARRAY_LENGTH(&data->list))
+ data->top = ARRAY_LENGTH(&data->list);
+ else
+ data->top += screen_size_y(s);
+ window_more_redraw_screen(wp);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+window_more_write_line(
+ struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
+{
+ struct window_more_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct grid_cell gc;
+ char *msg, hdr[32];
+ size_t size;
+
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+
+ if (py == 0) {
+ size = xsnprintf(hdr, sizeof hdr,
+ "[%u/%u]", data->top, ARRAY_LENGTH(&data->list));
+ screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
+ gc.bg = options_get_number(&wp->window->options, "mode-fg");
+ gc.fg = options_get_number(&wp->window->options, "mode-bg");
+ gc.attr |= options_get_number(&wp->window->options, "mode-attr");
+ screen_write_puts(ctx, &gc, "%s", hdr);
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ } else
+ size = 0;
+
+ screen_write_cursormove(ctx, 0, py);
+ if (data->top + py < ARRAY_LENGTH(&data->list)) {
+ msg = ARRAY_ITEM(&data->list, data->top + py);
+ screen_write_puts(
+ ctx, &gc, "%.*s", (int) (screen_size_x(s) - size), msg);
+ }
+ while (s->cx < screen_size_x(s) - size)
+ screen_write_putc(ctx, &gc, ' ');
+}
+
+void
+window_more_redraw_screen(struct window_pane *wp)
+{
+ struct window_more_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+ u_int i;
+
+ screen_write_start(&ctx, wp, NULL);
+ for (i = 0; i < screen_size_y(s); i++)
+ window_more_write_line(wp, &ctx, i);
+ screen_write_stop(&ctx);
+}
+
+void
+window_more_scroll_up(struct window_pane *wp)
+{
+ struct window_more_mode_data *data = wp->modedata;
+ struct screen_write_ctx ctx;
+
+ if (data->top == 0)
+ return;
+ data->top--;
+
+ screen_write_start(&ctx, wp, NULL);
+ screen_write_cursormove(&ctx, 0, 0);
+ screen_write_insertline(&ctx, 1);
+ window_more_write_line(wp, &ctx, 0);
+ window_more_write_line(wp, &ctx, 1);
+ screen_write_stop(&ctx);
+}
+
+void
+window_more_scroll_down(struct window_pane *wp)
+{
+ struct window_more_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+
+ if (data->top >= ARRAY_LENGTH(&data->list))
+ return;
+ data->top++;
+
+ screen_write_start(&ctx, wp, NULL);
+ screen_write_cursormove(&ctx, 0, 0);
+ screen_write_deleteline(&ctx, 1);
+ window_more_write_line(wp, &ctx, screen_size_y(s) - 1);
+ window_more_write_line(wp, &ctx, 0);
+ screen_write_stop(&ctx);
+}
diff --git a/usr.bin/tmux/window-scroll.c b/usr.bin/tmux/window-scroll.c
new file mode 100644
index 00000000000..14d01e3637b
--- /dev/null
+++ b/usr.bin/tmux/window-scroll.c
@@ -0,0 +1,296 @@
+/* $OpenBSD: window-scroll.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include "tmux.h"
+
+struct screen *window_scroll_init(struct window_pane *);
+void window_scroll_free(struct window_pane *);
+void window_scroll_resize(struct window_pane *, u_int, u_int);
+void window_scroll_key(struct window_pane *, struct client *, int);
+
+void window_scroll_redraw_screen(struct window_pane *);
+void window_scroll_write_line(
+ struct window_pane *, struct screen_write_ctx *, u_int);
+void window_scroll_write_column(
+ struct window_pane *, struct screen_write_ctx *, u_int);
+
+void window_scroll_scroll_up(struct window_pane *);
+void window_scroll_scroll_down(struct window_pane *);
+void window_scroll_scroll_left(struct window_pane *);
+void window_scroll_scroll_right(struct window_pane *);
+
+const struct window_mode window_scroll_mode = {
+ window_scroll_init,
+ window_scroll_free,
+ window_scroll_resize,
+ window_scroll_key,
+ NULL,
+ NULL,
+};
+
+struct window_scroll_mode_data {
+ struct screen screen;
+
+ struct mode_key_data mdata;
+
+ u_int ox;
+ u_int oy;
+};
+
+struct screen *
+window_scroll_init(struct window_pane *wp)
+{
+ struct window_scroll_mode_data *data;
+ struct screen *s;
+ struct screen_write_ctx ctx;
+ u_int i;
+
+ wp->modedata = data = xmalloc(sizeof *data);
+ data->ox = 0;
+ data->oy = 0;
+
+ s = &data->screen;
+ screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
+ s->mode &= ~MODE_CURSOR;
+
+ mode_key_init(&data->mdata,
+ options_get_number(&wp->window->options, "mode-keys"), 0);
+
+ screen_write_start(&ctx, NULL, s);
+ for (i = 0; i < screen_size_y(s); i++)
+ window_scroll_write_line(wp, &ctx, i);
+ screen_write_stop(&ctx);
+
+ return (s);
+}
+
+void
+window_scroll_free(struct window_pane *wp)
+{
+ struct window_scroll_mode_data *data = wp->modedata;
+
+ mode_key_free(&data->mdata);
+
+ screen_free(&data->screen);
+ xfree(data);
+}
+
+void
+window_scroll_pageup(struct window_pane *wp)
+{
+ struct window_scroll_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+
+ if (data->oy + screen_size_y(s) > screen_hsize(&wp->base))
+ data->oy = screen_hsize(&wp->base);
+ else
+ data->oy += screen_size_y(s);
+
+ window_scroll_redraw_screen(wp);
+}
+
+void
+window_scroll_resize(struct window_pane *wp, u_int sx, u_int sy)
+{
+ struct window_scroll_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+ u_int i;
+
+ screen_resize(s, sx, sy);
+ screen_write_start(&ctx, NULL, s);
+ for (i = 0; i < screen_size_y(s); i++)
+ window_scroll_write_line(wp, &ctx, i);
+ screen_write_stop(&ctx);
+}
+
+void
+window_scroll_key(struct window_pane *wp, unused struct client *c, int key)
+{
+ struct window_scroll_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+
+ switch (mode_key_lookup(&data->mdata, key)) {
+ case MODEKEYCMD_QUIT:
+ window_pane_reset_mode(wp);
+ break;
+ case MODEKEYCMD_LEFT:
+ window_scroll_scroll_left(wp);
+ break;
+ case MODEKEYCMD_RIGHT:
+ window_scroll_scroll_right(wp);
+ break;
+ case MODEKEYCMD_UP:
+ window_scroll_scroll_up(wp);
+ break;
+ case MODEKEYCMD_DOWN:
+ window_scroll_scroll_down(wp);
+ break;
+ case MODEKEYCMD_PREVIOUSPAGE:
+ window_scroll_pageup(wp);
+ break;
+ case MODEKEYCMD_NEXTPAGE:
+ if (data->oy < screen_size_y(s))
+ data->oy = 0;
+ else
+ data->oy -= screen_size_y(s);
+ window_scroll_redraw_screen(wp);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+window_scroll_write_line(
+ struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
+{
+ struct window_scroll_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct grid_cell gc;
+ char hdr[32];
+ size_t size;
+
+ if (py == 0) {
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ size = xsnprintf(hdr, sizeof hdr,
+ "[%u,%u/%u]", data->ox, data->oy, screen_hsize(&wp->base));
+ gc.bg = options_get_number(&wp->window->options, "mode-fg");
+ gc.fg = options_get_number(&wp->window->options, "mode-bg");
+ gc.attr |= options_get_number(&wp->window->options, "mode-attr");
+ screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
+ screen_write_puts(ctx, &gc, "%s", hdr);
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ } else
+ size = 0;
+
+ screen_write_cursormove(ctx, 0, py);
+ screen_write_copy(ctx, &wp->base, data->ox, (screen_hsize(&wp->base) -
+ data->oy) + py, screen_size_x(s) - size, 1);
+}
+
+void
+window_scroll_write_column(
+ struct window_pane *wp, struct screen_write_ctx *ctx, u_int px)
+{
+ struct window_scroll_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+
+ screen_write_cursormove(ctx, px, 0);
+ screen_write_copy(ctx, &wp->base, data->ox + px,
+ screen_hsize(&wp->base) - data->oy, 1, screen_size_y(s));
+}
+
+void
+window_scroll_redraw_screen(struct window_pane *wp)
+{
+ struct window_scroll_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+ u_int i;
+
+ screen_write_start(&ctx, wp, NULL);
+ for (i = 0; i < screen_size_y(s); i++)
+ window_scroll_write_line(wp, &ctx, i);
+ screen_write_stop(&ctx);
+}
+
+void
+window_scroll_scroll_up(struct window_pane *wp)
+{
+ struct window_scroll_mode_data *data = wp->modedata;
+ struct screen_write_ctx ctx;
+
+ if (data->oy >= screen_hsize(&wp->base))
+ return;
+ data->oy++;
+
+ screen_write_start(&ctx, wp, NULL);
+ screen_write_cursormove(&ctx, 0, 0);
+ screen_write_insertline(&ctx, 1);
+ window_scroll_write_line(wp, &ctx, 0);
+ window_scroll_write_line(wp, &ctx, 1);
+ screen_write_stop(&ctx);
+}
+
+void
+window_scroll_scroll_down(struct window_pane *wp)
+{
+ struct window_scroll_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+
+ if (data->oy == 0)
+ return;
+ data->oy--;
+
+ screen_write_start(&ctx, wp, NULL);
+ screen_write_cursormove(&ctx, 0, 0);
+ screen_write_deleteline(&ctx, 1);
+ window_scroll_write_line(wp, &ctx, screen_size_y(s) - 1);
+ window_scroll_write_line(wp, &ctx, 0);
+ screen_write_stop(&ctx);
+}
+
+void
+window_scroll_scroll_right(struct window_pane *wp)
+{
+ struct window_scroll_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+ u_int i;
+
+ if (data->ox >= SHRT_MAX)
+ return;
+ data->ox++;
+
+ screen_write_start(&ctx, wp, NULL);
+ for (i = 1; i < screen_size_y(s); i++) {
+ screen_write_cursormove(&ctx, 0, i);
+ screen_write_deletecharacter(&ctx, 1);
+ }
+ window_scroll_write_column(wp, &ctx, screen_size_x(s) - 1);
+ window_scroll_write_line(wp, &ctx, 0);
+ screen_write_stop(&ctx);
+}
+
+void
+window_scroll_scroll_left(struct window_pane *wp)
+{
+ struct window_scroll_mode_data *data = wp->modedata;
+ struct screen *s = &data->screen;
+ struct screen_write_ctx ctx;
+ u_int i;
+
+ if (data->ox == 0)
+ return;
+ data->ox--;
+
+ screen_write_start(&ctx, wp, NULL);
+ for (i = 1; i < screen_size_y(s); i++) {
+ screen_write_cursormove(&ctx, 0, i);
+ screen_write_insertcharacter(&ctx, 1);
+ }
+ window_scroll_write_column(wp, &ctx, 0);
+ window_scroll_write_line(wp, &ctx, 0);
+ screen_write_stop(&ctx);
+}
diff --git a/usr.bin/tmux/window.c b/usr.bin/tmux/window.c
new file mode 100644
index 00000000000..0f328c62b1e
--- /dev/null
+++ b/usr.bin/tmux/window.c
@@ -0,0 +1,625 @@
+/* $OpenBSD: window.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/ioctl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "tmux.h"
+
+/*
+ * Each window is attached to one or two panes, each of which is a pty. This
+ * file contains code to handle them.
+ *
+ * A pane has two buffers attached, these are filled and emptied by the main
+ * server poll loop. Output data is received from pty's in screen format,
+ * translated and returned as a series of escape sequences and strings via
+ * input_parse (in input.c). Input data is received as key codes and written
+ * directly via input_key.
+ *
+ * Each pane also has a "virtual" screen (screen.c) which contains the current
+ * state and is redisplayed when the window is reattached to a client.
+ *
+ * Windows are stored directly on a global array and wrapped in any number of
+ * winlink structs to be linked onto local session RB trees. A reference count
+ * is maintained and a window removed from the global list and destroyed when
+ * it reaches zero.
+ */
+
+/* Global window list. */
+struct windows windows;
+
+RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
+
+int
+winlink_cmp(struct winlink *wl1, struct winlink *wl2)
+{
+ return (wl1->idx - wl2->idx);
+}
+
+struct winlink *
+winlink_find_by_index(struct winlinks *wwl, int idx)
+{
+ struct winlink wl;
+
+ if (idx < 0)
+ fatalx("bad index");
+
+ wl.idx = idx;
+ return (RB_FIND(winlinks, wwl, &wl));
+}
+
+int
+winlink_next_index(struct winlinks *wwl)
+{
+ u_int i;
+
+ for (i = 0; i < INT_MAX; i++) {
+ if (winlink_find_by_index(wwl, i) == NULL)
+ return (i);
+ }
+
+ fatalx("no free indexes");
+}
+
+u_int
+winlink_count(struct winlinks *wwl)
+{
+ struct winlink *wl;
+ u_int n;
+
+ n = 0;
+ RB_FOREACH(wl, winlinks, wwl)
+ n++;
+
+ return (n);
+}
+
+struct winlink *
+winlink_add(struct winlinks *wwl, struct window *w, int idx)
+{
+ struct winlink *wl;
+
+ if (idx == -1)
+ idx = winlink_next_index(wwl);
+ else if (winlink_find_by_index(wwl, idx) != NULL)
+ return (NULL);
+
+ if (idx < 0)
+ fatalx("bad index");
+
+ wl = xcalloc(1, sizeof *wl);
+ wl->idx = idx;
+ wl->window = w;
+ RB_INSERT(winlinks, wwl, wl);
+
+ w->references++;
+
+ return (wl);
+}
+
+void
+winlink_remove(struct winlinks *wwl, struct winlink *wl)
+{
+ struct window *w = wl->window;
+
+ RB_REMOVE(winlinks, wwl, wl);
+ xfree(wl);
+
+ if (w->references == 0)
+ fatal("bad reference count");
+ w->references--;
+ if (w->references == 0)
+ window_destroy(w);
+}
+
+struct winlink *
+winlink_next(unused struct winlinks *wwl, struct winlink *wl)
+{
+ return (RB_NEXT(winlinks, wwl, wl));
+}
+
+struct winlink *
+winlink_previous(unused struct winlinks *wwl, struct winlink *wl)
+{
+ return (RB_PREV(winlinks, wwl, wl));
+}
+
+void
+winlink_stack_push(struct winlink_stack *stack, struct winlink *wl)
+{
+ if (wl == NULL)
+ return;
+
+ winlink_stack_remove(stack, wl);
+ SLIST_INSERT_HEAD(stack, wl, sentry);
+}
+
+void
+winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl)
+{
+ struct winlink *wl2;
+
+ if (wl == NULL)
+ return;
+
+ SLIST_FOREACH(wl2, stack, sentry) {
+ if (wl2 == wl) {
+ SLIST_REMOVE(stack, wl, winlink, sentry);
+ return;
+ }
+ }
+}
+
+int
+window_index(struct window *s, u_int *i)
+{
+ for (*i = 0; *i < ARRAY_LENGTH(&windows); (*i)++) {
+ if (s == ARRAY_ITEM(&windows, *i))
+ return (0);
+ }
+ return (-1);
+}
+
+struct window *
+window_create1(u_int sx, u_int sy)
+{
+ struct window *w;
+ u_int i;
+
+ w = xmalloc(sizeof *w);
+ w->name = NULL;
+ w->flags = 0;
+
+ TAILQ_INIT(&w->panes);
+ w->active = NULL;
+ w->layout = 0;
+
+ w->sx = sx;
+ w->sy = sy;
+
+ options_init(&w->options, &global_window_options);
+
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ if (ARRAY_ITEM(&windows, i) == NULL) {
+ ARRAY_SET(&windows, i, w);
+ break;
+ }
+ }
+ if (i == ARRAY_LENGTH(&windows))
+ ARRAY_ADD(&windows, w);
+ w->references = 0;
+
+ return (w);
+}
+
+struct window *
+window_create(const char *name, const char *cmd, const char *cwd,
+ const char **envp, u_int sx, u_int sy, u_int hlimit, char **cause)
+{
+ struct window *w;
+
+ w = window_create1(sx, sy);
+ if (window_add_pane(w, -1, cmd, cwd, envp, hlimit, cause) == NULL) {
+ window_destroy(w);
+ return (NULL);
+ }
+ w->active = TAILQ_FIRST(&w->panes);
+
+ if (name != NULL) {
+ w->name = xstrdup(name);
+ options_set_number(&w->options, "automatic-rename", 0);
+ } else
+ w->name = default_window_name(w);
+ return (w);
+}
+
+void
+window_destroy(struct window *w)
+{
+ u_int i;
+
+ if (window_index(w, &i) != 0)
+ fatalx("index not found");
+ ARRAY_SET(&windows, i, NULL);
+ while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL)
+ ARRAY_TRUNC(&windows, 1);
+
+ options_free(&w->options);
+
+ window_destroy_panes(w);
+
+ if (w->name != NULL)
+ xfree(w->name);
+ xfree(w);
+}
+
+int
+window_resize(struct window *w, u_int sx, u_int sy)
+{
+ w->sx = sx;
+ w->sy = sy;
+
+ return (0);
+}
+
+void
+window_set_active_pane(struct window *w, struct window_pane *wp)
+{
+ w->active = wp;
+ while (w->active->flags & PANE_HIDDEN)
+ w->active = TAILQ_PREV(w->active, window_panes, entry);
+}
+
+struct window_pane *
+window_add_pane(struct window *w, int wanty, const char *cmd,
+ const char *cwd, const char **envp, u_int hlimit, char **cause)
+{
+ struct window_pane *wp;
+ u_int sizey;
+
+ if (TAILQ_EMPTY(&w->panes))
+ wanty = w->sy;
+ else {
+ sizey = w->active->sy - 1; /* for separator */
+ if (sizey < PANE_MINIMUM * 2) {
+ *cause = xstrdup("pane too small");
+ return (NULL);
+ }
+
+ if (wanty == -1)
+ wanty = sizey / 2;
+
+ if (wanty < PANE_MINIMUM)
+ wanty = PANE_MINIMUM;
+ if ((u_int) wanty > sizey - PANE_MINIMUM)
+ wanty = sizey - PANE_MINIMUM;
+
+ window_pane_resize(w->active, w->sx, sizey - wanty);
+ }
+
+ wp = window_pane_create(w, w->sx, wanty, hlimit);
+ if (TAILQ_EMPTY(&w->panes))
+ TAILQ_INSERT_HEAD(&w->panes, wp, entry);
+ else
+ TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry);
+ if (window_pane_spawn(wp, cmd, cwd, envp, cause) != 0) {
+ window_remove_pane(w, wp);
+ return (NULL);
+ }
+ return (wp);
+}
+
+void
+window_remove_pane(struct window *w, struct window_pane *wp)
+{
+ w->active = TAILQ_PREV(wp, window_panes, entry);
+ if (w->active == NULL)
+ w->active = TAILQ_NEXT(wp, entry);
+
+ TAILQ_REMOVE(&w->panes, wp, entry);
+ window_pane_destroy(wp);
+}
+
+u_int
+window_index_of_pane(struct window *w, struct window_pane *find)
+{
+ struct window_pane *wp;
+ u_int n;
+
+ n = 0;
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp == find)
+ return (n);
+ n++;
+ }
+ fatalx("unknown pane");
+}
+
+struct window_pane *
+window_pane_at_index(struct window *w, u_int idx)
+{
+ struct window_pane *wp;
+ u_int n;
+
+ n = 0;
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (n == idx)
+ return (wp);
+ n++;
+ }
+ return (NULL);
+}
+
+u_int
+window_count_panes(struct window *w)
+{
+ struct window_pane *wp;
+ u_int n;
+
+ n = 0;
+ TAILQ_FOREACH(wp, &w->panes, entry)
+ n++;
+ return (n);
+}
+
+void
+window_destroy_panes(struct window *w)
+{
+ struct window_pane *wp;
+
+ while (!TAILQ_EMPTY(&w->panes)) {
+ wp = TAILQ_FIRST(&w->panes);
+ TAILQ_REMOVE(&w->panes, wp, entry);
+ window_pane_destroy(wp);
+ }
+}
+
+struct window_pane *
+window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
+{
+ struct window_pane *wp;
+
+ wp = xcalloc(1, sizeof *wp);
+ wp->window = w;
+
+ wp->cmd = NULL;
+ wp->cwd = NULL;
+
+ wp->fd = -1;
+ wp->in = buffer_create(BUFSIZ);
+ wp->out = buffer_create(BUFSIZ);
+
+ wp->mode = NULL;
+
+ wp->xoff = 0;
+ wp->yoff = 0;
+
+ wp->sx = sx;
+ wp->sy = sy;
+
+ screen_init(&wp->base, sx, sy, hlimit);
+ wp->screen = &wp->base;
+
+ input_init(wp);
+
+ return (wp);
+}
+
+void
+window_pane_destroy(struct window_pane *wp)
+{
+ if (wp->fd != -1)
+ close(wp->fd);
+
+ input_free(wp);
+
+ window_pane_reset_mode(wp);
+ screen_free(&wp->base);
+
+ buffer_destroy(wp->in);
+ buffer_destroy(wp->out);
+
+ if (wp->cwd != NULL)
+ xfree(wp->cwd);
+ if (wp->cmd != NULL)
+ xfree(wp->cmd);
+ xfree(wp);
+}
+
+int
+window_pane_spawn(struct window_pane *wp,
+ const char *cmd, const char *cwd, const char **envp, char **cause)
+{
+ struct winsize ws;
+ int mode;
+ const char **envq;
+ struct timeval tv;
+
+ if (wp->fd != -1)
+ close(wp->fd);
+ if (cmd != NULL) {
+ if (wp->cmd != NULL)
+ xfree(wp->cmd);
+ wp->cmd = xstrdup(cmd);
+ }
+ if (cwd != NULL) {
+ if (wp->cwd != NULL)
+ xfree(wp->cwd);
+ wp->cwd = xstrdup(cwd);
+ }
+
+ memset(&ws, 0, sizeof ws);
+ ws.ws_col = screen_size_x(&wp->base);
+ ws.ws_row = screen_size_y(&wp->base);
+
+ if (gettimeofday(&wp->window->name_timer, NULL) != 0)
+ fatal("gettimeofday");
+ tv.tv_sec = 0;
+ tv.tv_usec = NAME_INTERVAL * 1000L;
+ timeradd(&wp->window->name_timer, &tv, &wp->window->name_timer);
+
+ switch (wp->pid = forkpty(&wp->fd, wp->tty, NULL, &ws)) {
+ case -1:
+ wp->fd = -1;
+ xasprintf(cause, "%s: %s", cmd, strerror(errno));
+ return (-1);
+ case 0:
+ if (chdir(wp->cwd) != 0)
+ chdir("/");
+ for (envq = envp; *envq != NULL; envq++) {
+ if (putenv((char *) *envq) != 0)
+ fatal("putenv failed");
+ }
+ sigreset();
+ log_close();
+
+ execl(_PATH_BSHELL, "sh", "-c", wp->cmd, (char *) NULL);
+ fatal("execl failed");
+ }
+
+ if ((mode = fcntl(wp->fd, F_GETFL)) == -1)
+ fatal("fcntl failed");
+ if (fcntl(wp->fd, F_SETFL, mode|O_NONBLOCK) == -1)
+ fatal("fcntl failed");
+ if (fcntl(wp->fd, F_SETFD, FD_CLOEXEC) == -1)
+ fatal("fcntl failed");
+
+ return (0);
+}
+
+int
+window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
+{
+ struct winsize ws;
+
+ if (sx == wp->sx && sy == wp->sy)
+ return (-1);
+ wp->sx = sx;
+ wp->sy = sy;
+
+ memset(&ws, 0, sizeof ws);
+ ws.ws_col = sx;
+ ws.ws_row = sy;
+
+ screen_resize(&wp->base, sx, sy);
+ if (wp->mode != NULL)
+ wp->mode->resize(wp, sx, sy);
+
+ if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
+ fatal("ioctl failed");
+ return (0);
+}
+
+int
+window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode)
+{
+ struct screen *s;
+
+ if (wp->mode != NULL || wp->mode == mode)
+ return (1);
+
+ wp->mode = mode;
+
+ if ((s = wp->mode->init(wp)) != NULL)
+ wp->screen = s;
+ server_redraw_window(wp->window);
+ return (0);
+}
+
+void
+window_pane_reset_mode(struct window_pane *wp)
+{
+ if (wp->mode == NULL)
+ return;
+
+ wp->mode->free(wp);
+ wp->mode = NULL;
+
+ wp->screen = &wp->base;
+ server_redraw_window(wp->window);
+}
+
+void
+window_pane_parse(struct window_pane *wp)
+{
+ input_parse(wp);
+}
+
+void
+window_pane_key(struct window_pane *wp, struct client *c, int key)
+{
+ if (wp->mode != NULL) {
+ if (wp->mode->key != NULL)
+ wp->mode->key(wp, c, key);
+ } else
+ input_key(wp, key);
+}
+
+void
+window_pane_mouse(
+ struct window_pane *wp, struct client *c, u_char b, u_char x, u_char y)
+{
+ /* XXX convert from 1-based? */
+
+ if (x < wp->xoff || x >= wp->xoff + wp->sx)
+ return;
+ if (y < wp->yoff || y >= wp->yoff + wp->sy)
+ return;
+ x -= wp->xoff;
+ y -= wp->yoff;
+
+ if (wp->mode != NULL) {
+ if (wp->mode->mouse != NULL)
+ wp->mode->mouse(wp, c, b, x, y);
+ } else
+ input_mouse(wp, b, x, y);
+}
+
+char *
+window_pane_search(struct window_pane *wp, const char *searchstr)
+{
+ const struct grid_cell *gc;
+ const struct grid_utf8 *gu;
+ char *buf, *s;
+ size_t off;
+ u_int i, j, k;
+
+ buf = xmalloc(1);
+
+ for (j = 0; j < screen_size_y(&wp->base); j++) {
+ off = 0;
+ for (i = 0; i < screen_size_x(&wp->base); i++) {
+ gc = grid_view_peek_cell(wp->base.grid, i, j);
+ if (gc->flags & GRID_FLAG_UTF8) {
+ gu = grid_view_peek_utf8(wp->base.grid, i, j);
+ buf = xrealloc(buf, 1, off + 8);
+ for (k = 0; k < UTF8_SIZE; k++) {
+ if (gu->data[k] == 0xff)
+ break;
+ buf[off++] = gu->data[k];
+ }
+ } else {
+ buf = xrealloc(buf, 1, off + 1);
+ buf[off++] = gc->data;
+ }
+ }
+ while (off > 0 && buf[off - 1] == ' ')
+ off--;
+ buf[off] = '\0';
+
+ if ((s = strstr(buf, searchstr)) != NULL) {
+ s = section_string(buf, off, s - buf, 40);
+ xfree(buf);
+ return (s);
+ }
+ }
+
+ xfree(buf);
+ return (NULL);
+}
diff --git a/usr.bin/tmux/xmalloc.c b/usr.bin/tmux/xmalloc.c
new file mode 100644
index 00000000000..6756a14d435
--- /dev/null
+++ b/usr.bin/tmux/xmalloc.c
@@ -0,0 +1,144 @@
+/* $OpenBSD: xmalloc.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+
+/*
+ * Copyright (c) 2004 Nicholas Marriott <nicm@users.sourceforge.net>
+ *
+ * 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/param.h>
+
+#include <errno.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+char *
+xstrdup(const char *s)
+{
+ void *ptr;
+ size_t len;
+
+ len = strlen(s) + 1;
+ ptr = xmalloc(len);
+
+ return (strncpy(ptr, s, len));
+}
+
+void *
+xcalloc(size_t nmemb, size_t size)
+{
+ void *ptr;
+
+ if (size == 0 || nmemb == 0)
+ fatalx("zero size");
+ if (SIZE_MAX / nmemb < size)
+ fatalx("nmemb * size > SIZE_MAX");
+ if ((ptr = calloc(nmemb, size)) == NULL)
+ fatal("xcalloc failed");
+
+ return (ptr);
+}
+
+void *
+xmalloc(size_t size)
+{
+ void *ptr;
+
+ if (size == 0)
+ fatalx("zero size");
+ if ((ptr = malloc(size)) == NULL)
+ fatal("xmalloc failed");
+
+ return (ptr);
+}
+
+void *
+xrealloc(void *oldptr, size_t nmemb, size_t size)
+{
+ size_t newsize = nmemb * size;
+ void *newptr;
+
+ if (newsize == 0)
+ fatalx("zero size");
+ if (SIZE_MAX / nmemb < size)
+ fatalx("nmemb * size > SIZE_MAX");
+ if ((newptr = realloc(oldptr, newsize)) == NULL)
+ fatal("xrealloc failed");
+
+ return (newptr);
+}
+
+void
+xfree(void *ptr)
+{
+ if (ptr == NULL)
+ fatalx("null pointer");
+ free(ptr);
+}
+
+int printflike2
+xasprintf(char **ret, const char *fmt, ...)
+{
+ va_list ap;
+ int i;
+
+ va_start(ap, fmt);
+ i = xvasprintf(ret, fmt, ap);
+ va_end(ap);
+
+ return (i);
+}
+
+int
+xvasprintf(char **ret, const char *fmt, va_list ap)
+{
+ int i;
+
+ i = vasprintf(ret, fmt, ap);
+ if (i < 0 || *ret == NULL)
+ fatal("xvasprintf failed");
+
+ return (i);
+}
+
+int printflike3
+xsnprintf(char *buf, size_t len, const char *fmt, ...)
+{
+ va_list ap;
+ int i;
+
+ va_start(ap, fmt);
+ i = xvsnprintf(buf, len, fmt, ap);
+ va_end(ap);
+
+ return (i);
+}
+
+int
+xvsnprintf(char *buf, size_t len, const char *fmt, va_list ap)
+{
+ int i;
+
+ if (len > INT_MAX)
+ fatalx("len > INT_MAX");
+
+ i = vsnprintf(buf, len, fmt, ap);
+ if (i < 0)
+ fatal("vsnprintf failed");
+
+ return (i);
+}