From 35876eaab991efc7759802f184cdd54663ea8a94 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 1 Jun 2009 22:58:49 +0000 Subject: Import tmux, a terminal multiplexor allowing (among other things) a single terminal to be switched between several different windows and programs displayed on one terminal be detached from one terminal and moved to another. ok deraadt pirofti --- Makefile | 48 ++ arg.c | 194 ++++++ array.h | 119 ++++ attributes.c | 92 +++ buffer-poll.c | 97 +++ buffer.c | 227 +++++++ cfg.c | 132 ++++ client-fn.c | 92 +++ client-msg.c | 159 +++++ client.c | 226 +++++++ clock.c | 161 +++++ cmd-attach-session.c | 80 +++ cmd-bind-key.c | 157 +++++ cmd-break-pane.c | 92 +++ cmd-choose-session.c | 107 +++ cmd-choose-window.c | 106 +++ cmd-clear-history.c | 68 ++ cmd-clock-mode.c | 54 ++ cmd-command-prompt.c | 178 +++++ cmd-confirm-before.c | 141 ++++ cmd-copy-buffer.c | 222 +++++++ cmd-copy-mode.c | 56 ++ cmd-delete-buffer.c | 61 ++ cmd-detach-client.c | 54 ++ cmd-down-pane.c | 61 ++ cmd-find-window.c | 160 +++++ cmd-generic.c | 694 ++++++++++++++++++++ cmd-has-session.c | 51 ++ cmd-kill-pane.c | 72 +++ cmd-kill-server.c | 51 ++ cmd-kill-session.c | 68 ++ cmd-kill-window.c | 69 ++ cmd-last-window.c | 60 ++ cmd-link-window.c | 109 ++++ cmd-list-buffers.c | 91 +++ cmd-list-clients.c | 67 ++ cmd-list-commands.c | 51 ++ cmd-list-keys.c | 59 ++ cmd-list-sessions.c | 67 ++ cmd-list-windows.c | 88 +++ cmd-list.c | 154 +++++ cmd-load-buffer.c | 106 +++ cmd-lock-server.c | 54 ++ cmd-move-window.c | 123 ++++ cmd-new-session.c | 248 +++++++ cmd-new-window.c | 241 +++++++ cmd-next-layout.c | 55 ++ cmd-next-window.c | 78 +++ cmd-paste-buffer.c | 78 +++ cmd-previous-layout.c | 55 ++ cmd-previous-window.c | 78 +++ cmd-refresh-client.c | 54 ++ cmd-rename-session.c | 57 ++ cmd-rename-window.c | 61 ++ cmd-resize-pane.c | 105 +++ cmd-respawn-window.c | 87 +++ cmd-rotate-window.c | 111 ++++ cmd-save-buffer.c | 90 +++ cmd-scroll-mode.c | 72 +++ cmd-select-layout.c | 86 +++ cmd-select-pane.c | 69 ++ cmd-select-prompt.c | 93 +++ cmd-select-window.c | 71 ++ cmd-send-keys.c | 184 ++++++ cmd-send-prefix.c | 57 ++ cmd-server-info.c | 179 +++++ cmd-set-buffer.c | 64 ++ cmd-set-option.c | 169 +++++ cmd-set-password.c | 169 +++++ cmd-set-window-option.c | 170 +++++ cmd-show-buffer.c | 89 +++ cmd-show-options.c | 110 ++++ cmd-show-window-options.c | 110 ++++ cmd-source-file.c | 147 +++++ cmd-split-window.c | 235 +++++++ cmd-start-server.c | 46 ++ cmd-string.c | 309 +++++++++ cmd-suspend-client.c | 64 ++ cmd-swap-pane.c | 285 ++++++++ cmd-swap-window.c | 75 +++ cmd-switch-client.c | 161 +++++ cmd-unbind-key.c | 120 ++++ cmd-unlink-window.c | 74 +++ cmd-up-pane.c | 61 ++ cmd.c | 393 +++++++++++ colour.c | 123 ++++ grid-view.c | 211 ++++++ grid.c | 497 ++++++++++++++ input-keys.c | 227 +++++++ input.c | 1276 ++++++++++++++++++++++++++++++++++++ key-bindings.c | 237 +++++++ key-string.c | 198 ++++++ layout-manual.c | 183 ++++++ layout.c | 373 +++++++++++ log.c | 250 +++++++ mode-key.c | 210 ++++++ names.c | 110 ++++ options-cmd.c | 182 ++++++ options.c | 160 +++++ paste.c | 131 ++++ procname.c | 130 ++++ resize.c | 138 ++++ screen-redraw.c | 171 +++++ screen-write.c | 684 ++++++++++++++++++++ screen.c | 240 +++++++ server-fn.c | 242 +++++++ server-msg.c | 304 +++++++++ server.c | 1105 +++++++++++++++++++++++++++++++ session.c | 378 +++++++++++ status.c | 960 +++++++++++++++++++++++++++ tmux.1 | 1329 ++++++++++++++++++++++++++++++++++++++ tmux.c | 487 ++++++++++++++ tmux.cat1 | 813 +++++++++++++++++++++++ tmux.h | 1576 +++++++++++++++++++++++++++++++++++++++++++++ tty-keys.c | 412 ++++++++++++ tty-term.c | 393 +++++++++++ tty-write.c | 91 +++ tty.c | 1076 +++++++++++++++++++++++++++++++ utf8.c | 317 +++++++++ util.c | 72 +++ window-choose.c | 361 +++++++++++ window-clock.c | 124 ++++ window-copy.c | 968 ++++++++++++++++++++++++++++ window-more.c | 252 ++++++++ window-scroll.c | 296 +++++++++ window.c | 625 ++++++++++++++++++ xmalloc.c | 144 +++++ 127 files changed, 28294 insertions(+) create mode 100644 Makefile create mode 100644 arg.c create mode 100644 array.h create mode 100644 attributes.c create mode 100644 buffer-poll.c create mode 100644 buffer.c create mode 100644 cfg.c create mode 100644 client-fn.c create mode 100644 client-msg.c create mode 100644 client.c create mode 100644 clock.c create mode 100644 cmd-attach-session.c create mode 100644 cmd-bind-key.c create mode 100644 cmd-break-pane.c create mode 100644 cmd-choose-session.c create mode 100644 cmd-choose-window.c create mode 100644 cmd-clear-history.c create mode 100644 cmd-clock-mode.c create mode 100644 cmd-command-prompt.c create mode 100644 cmd-confirm-before.c create mode 100644 cmd-copy-buffer.c create mode 100644 cmd-copy-mode.c create mode 100644 cmd-delete-buffer.c create mode 100644 cmd-detach-client.c create mode 100644 cmd-down-pane.c create mode 100644 cmd-find-window.c create mode 100644 cmd-generic.c create mode 100644 cmd-has-session.c create mode 100644 cmd-kill-pane.c create mode 100644 cmd-kill-server.c create mode 100644 cmd-kill-session.c create mode 100644 cmd-kill-window.c create mode 100644 cmd-last-window.c create mode 100644 cmd-link-window.c create mode 100644 cmd-list-buffers.c create mode 100644 cmd-list-clients.c create mode 100644 cmd-list-commands.c create mode 100644 cmd-list-keys.c create mode 100644 cmd-list-sessions.c create mode 100644 cmd-list-windows.c create mode 100644 cmd-list.c create mode 100644 cmd-load-buffer.c create mode 100644 cmd-lock-server.c create mode 100644 cmd-move-window.c create mode 100644 cmd-new-session.c create mode 100644 cmd-new-window.c create mode 100644 cmd-next-layout.c create mode 100644 cmd-next-window.c create mode 100644 cmd-paste-buffer.c create mode 100644 cmd-previous-layout.c create mode 100644 cmd-previous-window.c create mode 100644 cmd-refresh-client.c create mode 100644 cmd-rename-session.c create mode 100644 cmd-rename-window.c create mode 100644 cmd-resize-pane.c create mode 100644 cmd-respawn-window.c create mode 100644 cmd-rotate-window.c create mode 100644 cmd-save-buffer.c create mode 100644 cmd-scroll-mode.c create mode 100644 cmd-select-layout.c create mode 100644 cmd-select-pane.c create mode 100644 cmd-select-prompt.c create mode 100644 cmd-select-window.c create mode 100644 cmd-send-keys.c create mode 100644 cmd-send-prefix.c create mode 100644 cmd-server-info.c create mode 100644 cmd-set-buffer.c create mode 100644 cmd-set-option.c create mode 100644 cmd-set-password.c create mode 100644 cmd-set-window-option.c create mode 100644 cmd-show-buffer.c create mode 100644 cmd-show-options.c create mode 100644 cmd-show-window-options.c create mode 100644 cmd-source-file.c create mode 100644 cmd-split-window.c create mode 100644 cmd-start-server.c create mode 100644 cmd-string.c create mode 100644 cmd-suspend-client.c create mode 100644 cmd-swap-pane.c create mode 100644 cmd-swap-window.c create mode 100644 cmd-switch-client.c create mode 100644 cmd-unbind-key.c create mode 100644 cmd-unlink-window.c create mode 100644 cmd-up-pane.c create mode 100644 cmd.c create mode 100644 colour.c create mode 100644 grid-view.c create mode 100644 grid.c create mode 100644 input-keys.c create mode 100644 input.c create mode 100644 key-bindings.c create mode 100644 key-string.c create mode 100644 layout-manual.c create mode 100644 layout.c create mode 100644 log.c create mode 100644 mode-key.c create mode 100644 names.c create mode 100644 options-cmd.c create mode 100644 options.c create mode 100644 paste.c create mode 100644 procname.c create mode 100644 resize.c create mode 100644 screen-redraw.c create mode 100644 screen-write.c create mode 100644 screen.c create mode 100644 server-fn.c create mode 100644 server-msg.c create mode 100644 server.c create mode 100644 session.c create mode 100644 status.c create mode 100644 tmux.1 create mode 100644 tmux.c create mode 100644 tmux.cat1 create mode 100644 tmux.h create mode 100644 tty-keys.c create mode 100644 tty-term.c create mode 100644 tty-write.c create mode 100644 tty.c create mode 100644 utf8.c create mode 100644 util.c create mode 100644 window-choose.c create mode 100644 window-clock.c create mode 100644 window-copy.c create mode 100644 window-more.c create mode 100644 window-scroll.c create mode 100644 window.c create mode 100644 xmalloc.c diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..66204957 --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +# $OpenBSD$ + +.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 diff --git a/arg.c b/arg.c new file mode 100644 index 00000000..c02b2663 --- /dev/null +++ b/arg.c @@ -0,0 +1,194 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include + +#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/array.h b/array.h new file mode 100644 index 00000000..46855d77 --- /dev/null +++ b/array.h @@ -0,0 +1,119 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2006 Nicholas Marriott + * + * 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/attributes.c b/attributes.c new file mode 100644 index 00000000..cfe46ecd --- /dev/null +++ b/attributes.c @@ -0,0 +1,92 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Joshua Elsasser + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "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/buffer-poll.c b/buffer-poll.c new file mode 100644 index 00000000..4dd4d234 --- /dev/null +++ b/buffer-poll.c @@ -0,0 +1,97 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* 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/buffer.c b/buffer.c new file mode 100644 index 00000000..eea1fce7 --- /dev/null +++ b/buffer.c @@ -0,0 +1,227 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "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/cfg.c b/cfg.c new file mode 100644 index 00000000..e4278e14 --- /dev/null +++ b/cfg.c @@ -0,0 +1,132 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include + +#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/client-fn.c b/client-fn.c new file mode 100644 index 00000000..3f42a105 --- /dev/null +++ b/client-fn.c @@ -0,0 +1,92 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +void +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/client-msg.c b/client-msg.c new file mode 100644 index 00000000..fd48d403 --- /dev/null +++ b/client-msg.c @@ -0,0 +1,159 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include + +#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/client.c b/client.c new file mode 100644 index 00000000..12041eed --- /dev/null +++ b/client.c @@ -0,0 +1,226 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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/clock.c b/clock.c new file mode 100644 index 00000000..60248f47 --- /dev/null +++ b/clock.c @@ -0,0 +1,161 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +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/cmd-attach-session.c b/cmd-attach-session.c new file mode 100644 index 00000000..1de4eba6 --- /dev/null +++ b/cmd-attach-session.c @@ -0,0 +1,80 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "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/cmd-bind-key.c b/cmd-bind-key.c new file mode 100644 index 00000000..f0988899 --- /dev/null +++ b/cmd-bind-key.c @@ -0,0 +1,157 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "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/cmd-break-pane.c b/cmd-break-pane.c new file mode 100644 index 00000000..f2b51edf --- /dev/null +++ b/cmd-break-pane.c @@ -0,0 +1,92 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "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/cmd-choose-session.c b/cmd-choose-session.c new file mode 100644 index 00000000..d1f6ba23 --- /dev/null +++ b/cmd-choose-session.c @@ -0,0 +1,107 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "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/cmd-choose-window.c b/cmd-choose-window.c new file mode 100644 index 00000000..64eab882 --- /dev/null +++ b/cmd-choose-window.c @@ -0,0 +1,106 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "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