]> Zhao Yanbai Git Server - minix.git/commitdiff
Import tmux 58/2858/2
authorDavid van Moolenbroek <david@minix3.org>
Wed, 1 Oct 2014 15:59:46 +0000 (15:59 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Fri, 3 Oct 2014 10:01:08 +0000 (10:01 +0000)
We have to use SOCK_SEQPACKET instead of SOCK_STREAM for client/server
communication, because UDS does things with control messages that tmux
does not expect.

Change-Id: I3edb1875d61fb976cf6485c650f4fd4b82fa354c

159 files changed:
distrib/sets/lists/minix/mi
etc/mtree/NetBSD.dist.base
etc/mtree/NetBSD.dist.tests
external/bsd/Makefile
external/bsd/tmux/Makefile [new file with mode: 0644]
external/bsd/tmux/README [new file with mode: 0644]
external/bsd/tmux/dist/arguments.c [new file with mode: 0644]
external/bsd/tmux/dist/array.h [new file with mode: 0644]
external/bsd/tmux/dist/attributes.c [new file with mode: 0644]
external/bsd/tmux/dist/cfg.c [new file with mode: 0644]
external/bsd/tmux/dist/client.c [new file with mode: 0644]
external/bsd/tmux/dist/clock.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-attach-session.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-bind-key.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-break-pane.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-capture-pane.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-choose-buffer.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-choose-client.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-choose-session.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-choose-window.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-clear-history.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-clock-mode.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-command-prompt.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-confirm-before.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-copy-mode.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-delete-buffer.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-detach-client.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-display-message.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-display-panes.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-find-window.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-has-session.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-if-shell.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-join-pane.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-kill-pane.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-kill-server.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-kill-session.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-kill-window.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-link-window.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-list-buffers.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-list-clients.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-list-commands.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-list-keys.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-list-panes.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-list-sessions.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-list-windows.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-list.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-load-buffer.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-lock-server.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-move-window.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-new-session.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-new-window.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-paste-buffer.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-pipe-pane.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-refresh-client.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-rename-session.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-rename-window.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-resize-pane.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-respawn-pane.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-respawn-window.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-rotate-window.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-run-shell.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-save-buffer.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-select-layout.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-select-pane.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-select-window.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-send-keys.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-send-prefix.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-server-info.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-set-buffer.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-set-environment.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-set-option.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-show-buffer.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-show-environment.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-show-messages.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-show-options.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-source-file.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-split-window.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-start-server.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-string.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-suspend-client.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-swap-pane.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-swap-window.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-switch-client.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-unbind-key.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd-unlink-window.c [new file with mode: 0644]
external/bsd/tmux/dist/cmd.c [new file with mode: 0644]
external/bsd/tmux/dist/colour.c [new file with mode: 0644]
external/bsd/tmux/dist/compat.h [new file with mode: 0644]
external/bsd/tmux/dist/compat/imsg-buffer.c [new file with mode: 0644]
external/bsd/tmux/dist/compat/imsg.c [new file with mode: 0644]
external/bsd/tmux/dist/compat/imsg.h [new file with mode: 0644]
external/bsd/tmux/dist/compat/strtonum.c [new file with mode: 0644]
external/bsd/tmux/dist/environ.c [new file with mode: 0644]
external/bsd/tmux/dist/examples/bash_completion_tmux.sh [new file with mode: 0644]
external/bsd/tmux/dist/examples/h-boetes.conf [new file with mode: 0644]
external/bsd/tmux/dist/examples/n-marriott.conf [new file with mode: 0644]
external/bsd/tmux/dist/examples/screen-keys.conf [new file with mode: 0644]
external/bsd/tmux/dist/examples/t-williams.conf [new file with mode: 0644]
external/bsd/tmux/dist/examples/tmux.vim [new file with mode: 0644]
external/bsd/tmux/dist/examples/tmux_backup.sh [new file with mode: 0644]
external/bsd/tmux/dist/examples/vim-keys.conf [new file with mode: 0644]
external/bsd/tmux/dist/grid-utf8.c [new file with mode: 0644]
external/bsd/tmux/dist/grid-view.c [new file with mode: 0644]
external/bsd/tmux/dist/grid.c [new file with mode: 0644]
external/bsd/tmux/dist/input-keys.c [new file with mode: 0644]
external/bsd/tmux/dist/input.c [new file with mode: 0644]
external/bsd/tmux/dist/job.c [new file with mode: 0644]
external/bsd/tmux/dist/key-bindings.c [new file with mode: 0644]
external/bsd/tmux/dist/key-string.c [new file with mode: 0644]
external/bsd/tmux/dist/layout-custom.c [new file with mode: 0644]
external/bsd/tmux/dist/layout-set.c [new file with mode: 0644]
external/bsd/tmux/dist/layout.c [new file with mode: 0644]
external/bsd/tmux/dist/log.c [new file with mode: 0644]
external/bsd/tmux/dist/mode-key.c [new file with mode: 0644]
external/bsd/tmux/dist/names.c [new file with mode: 0644]
external/bsd/tmux/dist/options-table.c [new file with mode: 0644]
external/bsd/tmux/dist/options.c [new file with mode: 0644]
external/bsd/tmux/dist/osdep-aix.c [new file with mode: 0644]
external/bsd/tmux/dist/osdep-darwin.c [new file with mode: 0644]
external/bsd/tmux/dist/osdep-freebsd.c [new file with mode: 0644]
external/bsd/tmux/dist/osdep-hpux.c [new file with mode: 0644]
external/bsd/tmux/dist/osdep-linux.c [new file with mode: 0644]
external/bsd/tmux/dist/osdep-netbsd.c [new file with mode: 0644]
external/bsd/tmux/dist/osdep-openbsd.c [new file with mode: 0644]
external/bsd/tmux/dist/osdep-sunos.c [new file with mode: 0644]
external/bsd/tmux/dist/osdep-unknown.c [new file with mode: 0644]
external/bsd/tmux/dist/paste.c [new file with mode: 0644]
external/bsd/tmux/dist/resize.c [new file with mode: 0644]
external/bsd/tmux/dist/screen-redraw.c [new file with mode: 0644]
external/bsd/tmux/dist/screen-write.c [new file with mode: 0644]
external/bsd/tmux/dist/screen.c [new file with mode: 0644]
external/bsd/tmux/dist/server-client.c [new file with mode: 0644]
external/bsd/tmux/dist/server-fn.c [new file with mode: 0644]
external/bsd/tmux/dist/server-window.c [new file with mode: 0644]
external/bsd/tmux/dist/server.c [new file with mode: 0644]
external/bsd/tmux/dist/session.c [new file with mode: 0644]
external/bsd/tmux/dist/signal.c [new file with mode: 0644]
external/bsd/tmux/dist/status.c [new file with mode: 0644]
external/bsd/tmux/dist/tmux.1 [new file with mode: 0644]
external/bsd/tmux/dist/tmux.c [new file with mode: 0644]
external/bsd/tmux/dist/tmux.h [new file with mode: 0644]
external/bsd/tmux/dist/tty-acs.c [new file with mode: 0644]
external/bsd/tmux/dist/tty-keys.c [new file with mode: 0644]
external/bsd/tmux/dist/tty-term.c [new file with mode: 0644]
external/bsd/tmux/dist/tty.c [new file with mode: 0644]
external/bsd/tmux/dist/utf8.c [new file with mode: 0644]
external/bsd/tmux/dist/utmp.c [new file with mode: 0644]
external/bsd/tmux/dist/window-choose.c [new file with mode: 0644]
external/bsd/tmux/dist/window-clock.c [new file with mode: 0644]
external/bsd/tmux/dist/window-copy.c [new file with mode: 0644]
external/bsd/tmux/dist/window.c [new file with mode: 0644]
external/bsd/tmux/dist/xmalloc.c [new file with mode: 0644]
external/bsd/tmux/dist/xterm-keys.c [new file with mode: 0644]
external/bsd/tmux/prepare-import.sh [new file with mode: 0755]
external/bsd/tmux/share/Makefile [new file with mode: 0644]
external/bsd/tmux/share/examples/Makefile [new file with mode: 0644]
external/bsd/tmux/share/examples/tmux/Makefile [new file with mode: 0644]
external/bsd/tmux/usr.bin/Makefile [new file with mode: 0644]
external/bsd/tmux/usr.bin/tmux/Makefile [new file with mode: 0644]

index 1c10e396a5f400800711a003044718fb9f6535d3..792effd3f383bcd6ef5d691a6dedd5f40e0bfaa5 100644 (file)
 ./usr/bin/tget                         minix-sys
 ./usr/bin/tic                          minix-sys
 ./usr/bin/time                         minix-sys
+./usr/bin/tmux                         minix-sys
 ./usr/bin/top                          minix-sys
 ./usr/bin/toproto                      minix-sys
 ./usr/bin/touch                                minix-sys
 ./usr/man/man1/tget.1                  minix-sys
 ./usr/man/man1/tic.1                   minix-sys
 ./usr/man/man1/time.1                  minix-sys
+./usr/man/man1/tmux.1                  minix-sys
 ./usr/man/man1/touch.1                 minix-sys
 ./usr/man/man1/tput.1                  minix-sys
 ./usr/man/man1/tr.1                    minix-sys
 ./usr/share/examples/lutok/hello.cpp   minix-sys       kyua
 ./usr/share/examples/lutok/interpreter.cpp     minix-sys       kyua
 ./usr/share/examples/lutok/raii.cpp    minix-sys       kyua
+./usr/share/examples/tmux              minix-sys
+./usr/share/examples/tmux/screen-keys.conf     minix-sys
 ./usr/share/games                      minix-sys
 ./usr/share/games/fish.instr                   minix-sys
 ./usr/share/games/fortune                      minix-sys
index 25b39e563763c25f9877f56f9749598b76f6a939..be2d977ffbff50604f7f5e914e0e978e381f8b8e 100644 (file)
 ./usr/share/doc/psd/19.curses
 ./usr/share/doc/usd
 ./usr/share/doc/usd/03.shell
+./usr/share/examples
+./usr/share/examples/tmux
 ./usr/share/info
 ./usr/share/games
 ./usr/share/games/fortune
index c0270cbb1df014d33cbcbc21e351ed2323e465e0..76ce7b7735eb03ca4926b05dc83347d332db621d 100644 (file)
 ./usr/share/doc/kyua-atf-compat
 ./usr/share/doc/kyua-cli
 ./usr/share/doc/kyua-testers
-./usr/share/examples
 ./usr/share/examples/atf
 ./usr/share/examples/kyua-cli
 ./usr/share/examples/lutok
index 34c353c2721658245126ff681de7ae2508de33e6..1b4650723f3afcb6d081556832d1b9fdb8d8818f 100644 (file)
@@ -4,7 +4,7 @@
 
 SUBDIR= byacc file flex less \
        libarchive libevent mdocml \
-       
+       tmux
 
 .if (${MKATF} != "no")
 SUBDIR+= atf
diff --git a/external/bsd/tmux/Makefile b/external/bsd/tmux/Makefile
new file mode 100644 (file)
index 0000000..fb58a82
--- /dev/null
@@ -0,0 +1,5 @@
+# $NetBSD: Makefile,v 1.1 2011/03/10 09:18:00 jmmv Exp $
+
+SUBDIR= share usr.bin
+
+.include <bsd.subdir.mk>
diff --git a/external/bsd/tmux/README b/external/bsd/tmux/README
new file mode 100644 (file)
index 0000000..a14edfe
--- /dev/null
@@ -0,0 +1,15 @@
+To update tmux to a new version:
+
+- Build the package from pkgsrc and write down all -D flags passed to the
+  compiler.  Autoconf is not generating a config.h file, so this is the
+  best we can do to get the build-time settings in place.
+- Use prepare-import.sh to regenerate the dist directory.
+- Update usr.bin/tmux/Makefile to sync the CPPFLAGS to the list of -D flags
+  gathered earlier on.
+- Update the list of source files in usr.bin/tmux/Makefile with the new
+  dist/*.c listing.
+- cvs import the contents of the new dist directory.
+- Fix merge conflicts, if any.
+- Commit the changes to the reachover Makefiles.
+- Update doc/3RDPARTY with the new tmux version.
+- Add a note to doc/CHANGES about the new version.
diff --git a/external/bsd/tmux/dist/arguments.c b/external/bsd/tmux/dist/arguments.c
new file mode 100644 (file)
index 0000000..cf32749
--- /dev/null
@@ -0,0 +1,222 @@
+/* $Id: arguments.c,v 1.1.1.1 2011/08/17 18:40:04 jmmv Exp $ */
+
+/*
+ * Copyright (c) 2010 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"
+
+/* Create an arguments set with no flags. */
+struct args *
+args_create(int argc, ...)
+{
+       struct args     *args;
+       va_list          ap;
+       int              i;
+
+       args = xcalloc(1, sizeof *args);
+       if ((args->flags = bit_alloc(SCHAR_MAX)) == NULL)
+               fatal("bit_alloc failed");
+
+       args->argc = argc;
+       if (argc == 0)
+               args->argv = NULL;
+       else
+               args->argv = xcalloc(argc, sizeof *args->argv);
+
+       va_start(ap, argc);
+       for (i = 0; i < argc; i++)
+               args->argv[i] = xstrdup(va_arg(ap, char *));
+       va_end(ap);
+
+       return (args);
+}
+
+/* Parse an argv and argc into a new argument set. */
+struct args *
+args_parse(const char *template, int argc, char **argv)
+{
+       struct args     *args;
+       char            *ptr;
+       int              opt;
+
+       args = xcalloc(1, sizeof *args);
+       if ((args->flags = bit_alloc(SCHAR_MAX)) == NULL)
+               fatal("bit_alloc failed");
+
+       optreset = 1;
+       optind = 1;
+
+       while ((opt = getopt(argc, argv, template)) != -1) {
+               if (opt < 0 || opt >= SCHAR_MAX)
+                       continue;
+               if (opt == '?' || (ptr = strchr(template, opt)) == NULL) {
+                       xfree(args->flags);
+                       xfree(args);
+                       return (NULL);
+               }
+
+               bit_set(args->flags, opt);
+               if (ptr[1] == ':') {
+                       if (args->values[opt] != NULL)
+                               xfree(args->values[opt]);
+                       args->values[opt] = xstrdup(optarg);
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       args->argc = argc;
+       args->argv = cmd_copy_argv(argc, argv);
+
+       return (args);
+}
+
+/* Free an arguments set. */
+void
+args_free(struct args *args)
+{
+       u_int   i;
+
+       cmd_free_argv(args->argc, args->argv);
+
+       for (i = 0; i < SCHAR_MAX; i++) {
+               if (args->values[i] != NULL)
+                       xfree(args->values[i]);
+       }
+
+       xfree(args->flags);
+       xfree(args);
+}
+
+/* Print a set of arguments. */
+size_t
+args_print(struct args *args, char *buf, size_t len)
+{
+       size_t           off;
+       int              i;
+       const char      *quotes;
+
+       /* There must be at least one byte at the start. */
+       if (len == 0)
+               return (0);
+       off = 0;
+
+       /* Process the flags first. */
+       buf[off++] = '-';
+       for (i = 0; i < SCHAR_MAX; i++) {
+               if (!bit_test(args->flags, i) || args->values[i] != NULL)
+                       continue;
+
+               if (off == len - 1) {
+                       buf[off] = '\0';
+                       return (len);
+               }
+               buf[off++] = i;
+               buf[off] = '\0';
+       }
+       if (off == 1)
+               buf[--off] = '\0';
+
+       /* Then the flags with arguments. */
+       for (i = 0; i < SCHAR_MAX; i++) {
+               if (!bit_test(args->flags, i) || args->values[i] == NULL)
+                       continue;
+
+               if (off >= len) {
+                       /* snprintf will have zero terminated. */
+                       return (len);
+               }
+
+               if (strchr(args->values[i], ' ') != NULL)
+                       quotes = "\"";
+               else
+                       quotes = "";
+               off += xsnprintf(buf + off, len - off, "%s-%c %s%s%s",
+                   off != 0 ? " " : "", i, quotes, args->values[i], quotes);
+       }
+
+       /* And finally the argument vector. */
+       for (i = 0; i < args->argc; i++) {
+               if (off >= len) {
+                       /* snprintf will have zero terminated. */
+                       return (len);
+               }
+
+               if (strchr(args->argv[i], ' ') != NULL)
+                       quotes = "\"";
+               else
+                       quotes = "";
+               off += xsnprintf(buf + off, len - off, "%s%s%s%s",
+                   off != 0 ? " " : "", quotes, args->argv[i], quotes);
+       }
+
+       return (off);
+}
+
+/* Return if an argument is present. */
+int
+args_has(struct args *args, u_char ch)
+{
+       return (bit_test(args->flags, ch));
+}
+
+/* Set argument value. */
+void
+args_set(struct args *args, u_char ch, const char *value)
+{
+       if (args->values[ch] != NULL)
+               xfree(args->values[ch]);
+       if (value != NULL)
+               args->values[ch] = xstrdup(value);
+       else
+               args->values[ch] = NULL;
+       bit_set(args->flags, ch);
+}
+
+/* Get argument value. Will be NULL if it isn't present. */
+const char *
+args_get(struct args *args, u_char ch)
+{
+       return (args->values[ch]);
+}
+
+/* Convert an argument value to a number. */
+long long
+args_strtonum(struct args *args,
+    u_char ch, long long minval, long long maxval, char **cause)
+{
+       const char      *errstr;
+       long long        ll;
+
+       if (!args_has(args, ch)) {
+               *cause = xstrdup("missing");
+               return (0);
+       }
+
+       ll = strtonum(args->values[ch], minval, maxval, &errstr);
+       if (errstr != NULL) {
+               *cause = xstrdup(errstr);
+               return (0);
+       }
+
+       *cause = NULL;
+       return (ll);
+}
diff --git a/external/bsd/tmux/dist/array.h b/external/bsd/tmux/dist/array.h
new file mode 100644 (file)
index 0000000..dc1a4e3
--- /dev/null
@@ -0,0 +1,121 @@
+/* $Id: array.h,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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_INITIALIZER { NULL, 0, 0 }
+
+#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) (((void *) (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/external/bsd/tmux/dist/attributes.c b/external/bsd/tmux/dist/attributes.c
new file mode 100644 (file)
index 0000000..8fbd4fa
--- /dev/null
@@ -0,0 +1,93 @@
+/* $Id: attributes.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 attr)
+{
+       static char     buf[128];
+
+       if (attr == 0)
+               return ("none");
+
+       buf[0] = '\0';
+       if (attr & GRID_ATTR_BRIGHT)
+               strlcat(buf, "bright,", sizeof (buf));
+       if (attr & GRID_ATTR_DIM)
+               strlcat(buf, "dim,", sizeof (buf));
+       if (attr & GRID_ATTR_UNDERSCORE)
+               strlcat(buf, "underscore,", sizeof (buf));
+       if (attr & GRID_ATTR_BLINK)
+               strlcat(buf, "blink,", sizeof (buf));
+       if (attr & GRID_ATTR_REVERSE)
+               strlcat(buf, "reverse,", sizeof (buf));
+       if (attr & GRID_ATTR_HIDDEN)
+               strlcat(buf, "hidden,", sizeof (buf));
+       if (attr & GRID_ATTR_ITALICS)
+               strlcat(buf, "italics,", sizeof (buf));
+       if (*buf != '\0')
+               *(strrchr(buf, ',')) = '\0';
+
+       return (buf);
+}
+
+int
+attributes_fromstring(const char *str)
+{
+       const char      delimiters[] = " ,|";
+       u_char          attr;
+       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 || strcasecmp(str, "none") == 0)
+               return (0);
+
+       attr = 0;
+       do {
+               end = strcspn(str, delimiters);
+               if ((end == 6 && strncasecmp(str, "bright", end) == 0) ||
+                   (end == 4 && strncasecmp(str, "bold", end) == 0))
+                       attr |= GRID_ATTR_BRIGHT;
+               else if (end == 3 && strncasecmp(str, "dim", end) == 0)
+                       attr |= GRID_ATTR_DIM;
+               else if (end == 10 && strncasecmp(str, "underscore", end) == 0)
+                       attr |= GRID_ATTR_UNDERSCORE;
+               else if (end == 5 && strncasecmp(str, "blink", end) == 0)
+                       attr |= GRID_ATTR_BLINK;
+               else if (end == 7 && strncasecmp(str, "reverse", end) == 0)
+                       attr |= GRID_ATTR_REVERSE;
+               else if (end == 6 && strncasecmp(str, "hidden", end) == 0)
+                       attr |= GRID_ATTR_HIDDEN;
+               else if (end == 7 && strncasecmp(str, "italics", end) == 0)
+                       attr |= GRID_ATTR_ITALICS;
+               else
+                       return (-1);
+               str += end + strspn(str + end, delimiters);
+       } while (*str != '\0');
+
+       return (attr);
+}
diff --git a/external/bsd/tmux/dist/cfg.c b/external/bsd/tmux/dist/cfg.c
new file mode 100644 (file)
index 0000000..c14fd53
--- /dev/null
@@ -0,0 +1,143 @@
+/* $Id: cfg.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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.
+ */
+
+void printflike2 cfg_print(struct cmd_ctx *, const char *, ...);
+void printflike2 cfg_error(struct cmd_ctx *, const char *, ...);
+
+char                  *cfg_cause;
+int                    cfg_finished;
+struct causelist       cfg_causes = ARRAY_INITIALIZER;
+
+/* ARGSUSED */
+void printflike2
+cfg_print(unused struct cmd_ctx *ctx, unused const char *fmt, ...)
+{
+}
+
+/* ARGSUSED */
+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);
+}
+
+void printflike2
+cfg_add_cause(struct causelist *causes, const char *fmt, ...)
+{
+       char    *cause;
+       va_list  ap;
+
+       va_start(ap, fmt);
+       xvasprintf(&cause, fmt, ap);
+       va_end(ap);
+
+       ARRAY_ADD(causes, cause);
+}
+
+/*
+ * Load configuration file. Returns -1 for an error with a list of messages in
+ * causes. Note that causes must be initialised by the caller!
+ */
+int
+load_cfg(const char *path, struct cmd_ctx *ctxin, struct causelist *causes)
+{
+       FILE            *f;
+       u_int            n;
+       char            *buf, *line, *cause;
+       size_t           len;
+       struct cmd_list *cmdlist;
+       struct cmd_ctx   ctx;
+       int              retval;
+
+       if ((f = fopen(path, "rb")) == NULL) {
+               cfg_add_cause(causes, "%s: %s", path, strerror(errno));
+               return (-1);
+       }
+       n = 0;
+
+       line = NULL;
+       retval = 0;
+       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;
+                       cfg_add_cause(causes, "%s: %u: %s", path, n, cause);
+                       xfree(cause);
+                       continue;
+               }
+               if (cmdlist == NULL)
+                       continue;
+               cfg_cause = NULL;
+
+               if (ctxin == NULL) {
+                       ctx.msgdata = NULL;
+                       ctx.curclient = NULL;
+                       ctx.cmdclient = NULL;
+               } else {
+                       ctx.msgdata = ctxin->msgdata;
+                       ctx.curclient = ctxin->curclient;
+                       ctx.cmdclient = ctxin->cmdclient;
+               }
+
+               ctx.error = cfg_error;
+               ctx.print = cfg_print;
+               ctx.info = cfg_print;
+
+               cfg_cause = NULL;
+               if (cmd_list_exec(cmdlist, &ctx) == 1)
+                       retval = 1;
+               cmd_list_free(cmdlist);
+               if (cfg_cause != NULL) {
+                       cfg_add_cause(causes, "%s: %d: %s", path, n, cfg_cause);
+                       xfree(cfg_cause);
+               }
+       }
+       if (line != NULL)
+               xfree(line);
+       fclose(f);
+
+       return (retval);
+}
diff --git a/external/bsd/tmux/dist/client.c b/external/bsd/tmux/dist/client.c
new file mode 100644 (file)
index 0000000..a63b811
--- /dev/null
@@ -0,0 +1,512 @@
+/* $Id: client.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+struct imsgbuf client_ibuf;
+struct event   client_event;
+const char     *client_exitmsg;
+int            client_exitval;
+enum msgtype   client_exittype;
+int            client_attached;
+
+int            client_connect(char *, int);
+void           client_send_identify(int);
+void           client_send_environ(void);
+void           client_write_server(enum msgtype, void *, size_t);
+void           client_update_event(void);
+void           client_signal(int, short, void *);
+void           client_callback(int, short, void *);
+int            client_dispatch_attached(void);
+int            client_dispatch_wait(void *);
+
+/* Connect client to server. */
+int
+client_connect(char *path, int start_server)
+{
+       struct sockaddr_un      sa;
+       size_t                  size;
+       int                     fd;
+
+       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;
+               return (-1);
+       }
+
+#ifndef __minix
+       if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+#else
+       if ((fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) == -1)
+#endif
+               fatal("socket failed");
+
+       if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {
+               if (!start_server)
+                       goto failed;
+               switch (errno) {
+               case ECONNREFUSED:
+                       if (unlink(path) != 0)
+                               goto failed;
+                       /* FALLTHROUGH */
+               case ENOENT:
+                       if ((fd = server_start()) == -1)
+                               goto failed;
+                       break;
+               default:
+                       goto failed;
+               }
+       }
+
+       setblocking(fd, 0);
+       return (fd);
+
+failed:
+       close(fd);
+       return (-1);
+}
+
+/* Client main loop. */
+int
+client_main(int argc, char **argv, int flags)
+{
+       struct cmd              *cmd;
+       struct cmd_list         *cmdlist;
+       struct msg_command_data  cmddata;
+       int                      cmdflags, fd;
+       pid_t                    ppid;
+       enum msgtype             msg;
+       char                    *cause;
+
+       /* Set up the initial command. */
+       cmdflags = 0;
+       if (shell_cmd != NULL) {
+               msg = MSG_SHELL;
+               cmdflags = CMD_STARTSERVER;
+       } else if (argc == 0) {
+               msg = MSG_COMMAND;
+               cmdflags = CMD_STARTSERVER|CMD_SENDENVIRON|CMD_CANTNEST;
+       } else {
+               msg = MSG_COMMAND;
+
+               /*
+                * It sucks parsing the command string twice (in client and
+                * later in server) but it is necessary to get the start server
+                * flag.
+                */
+               if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) {
+                       log_warnx("%s", cause);
+                       return (1);
+               }
+               cmdflags &= ~CMD_STARTSERVER;
+               TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
+                       if (cmd->entry->flags & CMD_STARTSERVER)
+                               cmdflags |= CMD_STARTSERVER;
+                       if (cmd->entry->flags & CMD_SENDENVIRON)
+                               cmdflags |= CMD_SENDENVIRON;
+                       if (cmd->entry->flags & CMD_CANTNEST)
+                               cmdflags |= CMD_CANTNEST;
+               }
+               cmd_list_free(cmdlist);
+       }
+
+       /*
+        * Check if this could be a nested session, if the command can't nest:
+        * if the socket path matches $TMUX, this is probably the same server.
+        */
+       if (shell_cmd == NULL && environ_path != NULL &&
+           cmdflags & CMD_CANTNEST && strcmp(socket_path, environ_path) == 0) {
+               log_warnx("sessions should be nested with care. "
+                   "unset $TMUX to force.");
+               return (1);
+       }
+
+       /* Initialise the client socket and start the server. */
+       fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER);
+       if (fd == -1) {
+               log_warn("failed to connect to server");
+               return (1);
+       }
+
+       /* Set process title, log and signals now this is the client. */
+#ifdef HAVE_SETPROCTITLE
+       setproctitle("client (%s)", socket_path);
+#endif
+       logfile("client");
+
+       /* Create imsg. */
+       imsg_init(&client_ibuf, fd);
+       event_set(&client_event, fd, EV_READ, client_callback, shell_cmd);
+
+       /* Establish signal handlers. */
+       set_signals(client_signal);
+
+       /* Send initial environment. */
+       if (cmdflags & CMD_SENDENVIRON)
+               client_send_environ();
+       client_send_identify(flags);
+
+       /* Send first command. */
+       if (msg == MSG_COMMAND) {
+               /* Fill in command line arguments. */
+               cmddata.pid = environ_pid;
+               cmddata.idx = environ_idx;
+
+               /* Prepare command for server. */
+               cmddata.argc = argc;
+               if (cmd_pack_argv(
+                   argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) {
+                       log_warnx("command too long");
+                       return (1);
+               }
+
+               client_write_server(msg, &cmddata, sizeof cmddata);
+       } else if (msg == MSG_SHELL)
+               client_write_server(msg, NULL, 0);
+
+       /* Set the event and dispatch. */
+       client_update_event();
+       event_dispatch();
+
+       /* Print the exit message, if any, and exit. */
+       if (client_attached) {
+               if (client_exitmsg != NULL && !login_shell)
+                       printf("[%s]\n", client_exitmsg);
+
+               ppid = getppid();
+               if (client_exittype == MSG_DETACHKILL && ppid > 1)
+                       kill(ppid, SIGHUP);
+       }
+       return (client_exitval);
+}
+
+/* Send identify message to server with the file descriptors. */
+void
+client_send_identify(int flags)
+{
+       struct msg_identify_data        data;
+       char                           *term;
+       int                             fd;
+
+       data.flags = flags;
+
+       if (getcwd(data.cwd, sizeof data.cwd) == NULL)
+               *data.cwd = '\0';
+
+       term = getenv("TERM");
+       if (term == NULL ||
+           strlcpy(data.term, term, sizeof data.term) >= sizeof data.term)
+               *data.term = '\0';
+
+       if ((fd = dup(STDIN_FILENO)) == -1)
+               fatal("dup failed");
+       imsg_compose(&client_ibuf,
+           MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data);
+
+       if ((fd = dup(STDOUT_FILENO)) == -1)
+               fatal("dup failed");
+       imsg_compose(&client_ibuf,
+           MSG_STDOUT, PROTOCOL_VERSION, -1, fd, NULL, 0);
+
+       if ((fd = dup(STDERR_FILENO)) == -1)
+               fatal("dup failed");
+       imsg_compose(&client_ibuf,
+           MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0);
+}
+
+/* Forward entire environment to server. */
+void
+client_send_environ(void)
+{
+       struct msg_environ_data data;
+       char                  **var;
+
+       for (var = environ; *var != NULL; var++) {
+               if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var)
+                       continue;
+               client_write_server(MSG_ENVIRON, &data, sizeof data);
+       }
+}
+
+/* Write a message to the server without a file descriptor. */
+void
+client_write_server(enum msgtype type, void *buf, size_t len)
+{
+       imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len);
+}
+
+/* Update client event based on whether it needs to read or read and write. */
+void
+client_update_event(void)
+{
+       short   events;
+
+       event_del(&client_event);
+       events = EV_READ;
+       if (client_ibuf.w.queued > 0)
+               events |= EV_WRITE;
+       event_set(
+           &client_event, client_ibuf.fd, events, client_callback, shell_cmd);
+       event_add(&client_event, NULL);
+}
+
+/* Callback to handle signals in the client. */
+/* ARGSUSED */
+void
+client_signal(int sig, unused short events, unused void *data)
+{
+       struct sigaction sigact;
+       int              status;
+
+       if (!client_attached) {
+               switch (sig) {
+               case SIGCHLD:
+                       waitpid(WAIT_ANY, &status, WNOHANG);
+                       break;
+               case SIGTERM:
+                       event_loopexit(NULL);
+                       break;
+               }
+       } else {
+               switch (sig) {
+               case SIGHUP:
+                       client_exitmsg = "lost tty";
+                       client_exitval = 1;
+                       client_write_server(MSG_EXITING, NULL, 0);
+                       break;
+               case SIGTERM:
+                       client_exitmsg = "terminated";
+                       client_exitval = 1;
+                       client_write_server(MSG_EXITING, NULL, 0);
+                       break;
+               case SIGWINCH:
+                       client_write_server(MSG_RESIZE, NULL, 0);
+                       break;
+               case SIGCONT:
+                       memset(&sigact, 0, sizeof sigact);
+                       sigemptyset(&sigact.sa_mask);
+                       sigact.sa_flags = SA_RESTART;
+                       sigact.sa_handler = SIG_IGN;
+                       if (sigaction(SIGTSTP, &sigact, NULL) != 0)
+                               fatal("sigaction failed");
+                       client_write_server(MSG_WAKEUP, NULL, 0);
+                       break;
+               }
+       }
+
+       client_update_event();
+}
+
+/* Callback for client imsg read events. */
+/* ARGSUSED */
+void
+client_callback(unused int fd, short events, void *data)
+{
+       ssize_t n;
+       int     retval;
+
+       if (events & EV_READ) {
+               if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
+                       goto lost_server;
+               if (client_attached)
+                       retval = client_dispatch_attached();
+               else
+                       retval = client_dispatch_wait(data);
+               if (retval != 0) {
+                       event_loopexit(NULL);
+                       return;
+               }
+       }
+
+       if (events & EV_WRITE) {
+               if (msgbuf_write(&client_ibuf.w) < 0)
+                       goto lost_server;
+       }
+
+       client_update_event();
+       return;
+
+lost_server:
+       client_exitmsg = "lost server";
+       client_exitval = 1;
+       event_loopexit(NULL);
+}
+
+/* Dispatch imsgs when in wait state (before MSG_READY). */
+int
+client_dispatch_wait(void *data)
+{
+       struct imsg             imsg;
+       ssize_t                 n, datalen;
+       struct msg_shell_data   shelldata;
+       struct msg_exit_data    exitdata;
+       const char             *shellcmd = data;
+
+       if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
+               fatalx("imsg_read failed");
+
+       for (;;) {
+               if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
+                       fatalx("imsg_get failed");
+               if (n == 0)
+                       return (0);
+               datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+
+               switch (imsg.hdr.type) {
+               case MSG_EXIT:
+               case MSG_SHUTDOWN:
+                       if (datalen != sizeof exitdata) {
+                               if (datalen != 0)
+                                       fatalx("bad MSG_EXIT size");
+                       } else {
+                               memcpy(&exitdata, imsg.data, sizeof exitdata);
+                               client_exitval = exitdata.retcode;
+                       }
+                       imsg_free(&imsg);
+                       return (-1);
+               case MSG_READY:
+                       if (datalen != 0)
+                               fatalx("bad MSG_READY size");
+
+                       client_attached = 1;
+                       break;
+               case MSG_VERSION:
+                       if (datalen != 0)
+                               fatalx("bad MSG_VERSION size");
+
+                       log_warnx("protocol version mismatch (client %u, "
+                           "server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
+                       client_exitval = 1;
+
+                       imsg_free(&imsg);
+                       return (-1);
+               case MSG_SHELL:
+                       if (datalen != sizeof shelldata)
+                               fatalx("bad MSG_SHELL size");
+                       memcpy(&shelldata, imsg.data, sizeof shelldata);
+                       shelldata.shell[(sizeof shelldata.shell) - 1] = '\0';
+
+                       clear_signals(0);
+
+                       shell_exec(shelldata.shell, shellcmd);
+                       /* NOTREACHED */
+               default:
+                       fatalx("unexpected message");
+               }
+
+               imsg_free(&imsg);
+       }
+}
+
+/* Dispatch imsgs in attached state (after MSG_READY). */
+/* ARGSUSED */
+int
+client_dispatch_attached(void)
+{
+       struct imsg             imsg;
+       struct msg_lock_data    lockdata;
+       struct sigaction        sigact;
+       ssize_t                 n, datalen;
+
+       for (;;) {
+               if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
+                       fatalx("imsg_get failed");
+               if (n == 0)
+                       return (0);
+               datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+
+               log_debug("client got %d", imsg.hdr.type);
+               switch (imsg.hdr.type) {
+               case MSG_DETACHKILL:
+               case MSG_DETACH:
+                       if (datalen != 0)
+                               fatalx("bad MSG_DETACH size");
+
+                       client_exittype = imsg.hdr.type;
+                       if (imsg.hdr.type == MSG_DETACHKILL)
+                               client_exitmsg = "detached and SIGHUP";
+                       else
+                               client_exitmsg = "detached";
+                       client_write_server(MSG_EXITING, NULL, 0);
+                       break;
+               case MSG_EXIT:
+                       if (datalen != 0 &&
+                           datalen != sizeof (struct msg_exit_data))
+                               fatalx("bad MSG_EXIT size");
+
+                       client_write_server(MSG_EXITING, NULL, 0);
+                       client_exitmsg = "exited";
+                       break;
+               case MSG_EXITED:
+                       if (datalen != 0)
+                               fatalx("bad MSG_EXITED size");
+
+                       imsg_free(&imsg);
+                       return (-1);
+               case MSG_SHUTDOWN:
+                       if (datalen != 0)
+                               fatalx("bad MSG_SHUTDOWN size");
+
+                       client_write_server(MSG_EXITING, NULL, 0);
+                       client_exitmsg = "server exited";
+                       client_exitval = 1;
+                       break;
+               case MSG_SUSPEND:
+                       if (datalen != 0)
+                               fatalx("bad MSG_SUSPEND size");
+
+                       memset(&sigact, 0, sizeof sigact);
+                       sigemptyset(&sigact.sa_mask);
+                       sigact.sa_flags = SA_RESTART;
+                       sigact.sa_handler = SIG_DFL;
+                       if (sigaction(SIGTSTP, &sigact, NULL) != 0)
+                               fatal("sigaction failed");
+                       kill(getpid(), SIGTSTP);
+                       break;
+               case MSG_LOCK:
+                       if (datalen != sizeof lockdata)
+                               fatalx("bad MSG_LOCK size");
+                       memcpy(&lockdata, imsg.data, sizeof lockdata);
+
+                       lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0';
+                       system(lockdata.cmd);
+                       client_write_server(MSG_UNLOCK, NULL, 0);
+                       break;
+               default:
+                       fatalx("unexpected message");
+               }
+
+               imsg_free(&imsg);
+       }
+}
diff --git a/external/bsd/tmux/dist/clock.c b/external/bsd/tmux/dist/clock.c
new file mode 100644 (file)
index 0000000..64d22f6
--- /dev/null
@@ -0,0 +1,159 @@
+/* $Id: clock.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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, 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);
+
+       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);
+
+                       memcpy(&gc, &grid_default_cell, sizeof gc);
+                       colour_set_fg(&gc, 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;
+
+       memcpy(&gc, &grid_default_cell, sizeof gc);
+       colour_set_bg(&gc, colour);
+       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++) {
+                       for (i = 0; i < 5; i++) {
+                               screen_write_cursormove(ctx, x + i, y + j);
+                               if (clock_table[idx][j][i])
+                                       screen_write_putc(ctx, &gc, ' ');
+                       }
+               }
+               x += 6;
+       }
+}
diff --git a/external/bsd/tmux/dist/cmd-attach-session.c b/external/bsd/tmux/dist/cmd-attach-session.c
new file mode 100644 (file)
index 0000000..1702bbe
--- /dev/null
@@ -0,0 +1,112 @@
+/* $Id: cmd-attach-session.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "drt:", 0, 0,
+       "[-dr] " CMD_TARGET_SESSION_USAGE,
+       CMD_CANTNEST|CMD_STARTSERVER|CMD_SENDENVIRON,
+       NULL,
+       NULL,
+       cmd_attach_session_exec
+};
+
+int
+cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct session  *s;
+       struct client   *c;
+       const char      *update;
+       char            *overrides, *cause;
+       u_int            i;
+
+       if (RB_EMPTY(&sessions)) {
+               ctx->error(ctx, "no sessions");
+               return (-1);
+       }
+
+       if ((s = cmd_find_session(ctx, args_get(args, 't'), 1)) == NULL)
+               return (-1);
+
+       if (ctx->cmdclient == NULL && ctx->curclient == NULL)
+               return (0);
+
+       if (ctx->cmdclient == NULL) {
+               if (args_has(self->args, 'd')) {
+                       /*
+                        * Can't use server_write_session in case attaching to
+                        * the same session as currently attached to.
+                        */
+                       for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+                               c = ARRAY_ITEM(&clients, i);
+                               if (c == NULL || c->session != s)
+                                       continue;
+                               if (c == ctx->curclient)
+                                       continue;
+                               server_write_client(c, MSG_DETACH, NULL, 0);
+                       }
+               }
+
+               ctx->curclient->session = s;
+               session_update_activity(s);
+               server_redraw_client(ctx->curclient);
+       } else {
+               if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) {
+                       ctx->error(ctx, "not a terminal");
+                       return (-1);
+               }
+
+               overrides =
+                   options_get_string(&s->options, "terminal-overrides");
+               if (tty_open(&ctx->cmdclient->tty, overrides, &cause) != 0) {
+                       ctx->error(ctx, "terminal open failed: %s", cause);
+                       xfree(cause);
+                       return (-1);
+               }
+
+               if (args_has(self->args, 'r'))
+                       ctx->cmdclient->flags |= CLIENT_READONLY;
+
+               if (args_has(self->args, 'd'))
+                       server_write_session(s, MSG_DETACH, NULL, 0);
+
+               ctx->cmdclient->session = s;
+               session_update_activity(s);
+               server_write_client(ctx->cmdclient, MSG_READY, NULL, 0);
+
+               update = options_get_string(&s->options, "update-environment");
+               environ_update(update, &ctx->cmdclient->environ, &s->environ);
+
+               server_redraw_client(ctx->cmdclient);
+       }
+       recalculate_sizes();
+       server_update_socket();
+
+       return (1);     /* 1 means don't tell command client to exit */
+}
diff --git a/external/bsd/tmux/dist/cmd-bind-key.c b/external/bsd/tmux/dist/cmd-bind-key.c
new file mode 100644 (file)
index 0000000..b5fc759
--- /dev/null
@@ -0,0 +1,120 @@
+/* $Id: cmd-bind-key.c,v 1.1.1.2 2011/08/17 18:40:03 jmmv 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"
+
+/*
+ * Bind a key to a command, this recurses through cmd_*.
+ */
+
+int    cmd_bind_key_check(struct args *);
+int    cmd_bind_key_exec(struct cmd *, struct cmd_ctx *);
+
+int    cmd_bind_key_table(struct cmd *, struct cmd_ctx *, int);
+
+const struct cmd_entry cmd_bind_key_entry = {
+       "bind-key", "bind",
+       "cnrt:", 1, -1,
+       "[-cnr] [-t key-table] key command [arguments]",
+       0,
+       NULL,
+       cmd_bind_key_check,
+       cmd_bind_key_exec
+};
+
+int
+cmd_bind_key_check(struct args *args)
+{
+       if (args_has(args, 't')) {
+               if (args->argc != 2)
+                       return (-1);
+       } else {
+               if (args->argc < 2)
+                       return (-1);
+       }
+       return (0);
+}
+
+int
+cmd_bind_key_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       char            *cause;
+       struct cmd_list *cmdlist;
+       int              key;
+
+       key = key_string_lookup_string(args->argv[0]);
+       if (key == KEYC_NONE) {
+               ctx->error(ctx, "unknown key: %s", args->argv[0]);
+               return (-1);
+       }
+
+       if (args_has(args, 't'))
+               return (cmd_bind_key_table(self, ctx, key));
+
+       cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, &cause);
+       if (cmdlist == NULL) {
+               ctx->error(ctx, "%s", cause);
+               xfree(cause);
+               return (-1);
+       }
+
+       if (!args_has(args, 'n'))
+           key |= KEYC_PREFIX;
+       key_bindings_add(key, args_has(args, 'r'), cmdlist);
+       return (0);
+}
+
+int
+cmd_bind_key_table(struct cmd *self, struct cmd_ctx *ctx, int key)
+{
+       struct args                     *args = self->args;
+       const char                      *tablename;
+       const struct mode_key_table     *mtab;
+       struct mode_key_binding         *mbind, mtmp;
+       enum mode_key_cmd                cmd;
+
+       tablename = args_get(args, 't');
+       if ((mtab = mode_key_findtable(tablename)) == NULL) {
+               ctx->error(ctx, "unknown key table: %s", tablename);
+               return (-1);
+       }
+
+       cmd = mode_key_fromstring(mtab->cmdstr, args->argv[1]);
+       if (cmd == MODEKEY_NONE) {
+               ctx->error(ctx, "unknown command: %s", args->argv[1]);
+               return (-1);
+       }
+
+       mtmp.key = key;
+       mtmp.mode = !!args_has(args, 'c');
+       if ((mbind = SPLAY_FIND(mode_key_tree, mtab->tree, &mtmp)) != NULL) {
+               mbind->cmd = cmd;
+               return (0);
+       }
+       mbind = xmalloc(sizeof *mbind);
+       mbind->key = mtmp.key;
+       mbind->mode = mtmp.mode;
+       mbind->cmd = cmd;
+       SPLAY_INSERT(mode_key_tree, mtab->tree, mbind);
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-break-pane.c b/external/bsd/tmux/dist/cmd-break-pane.c
new file mode 100644 (file)
index 0000000..ba27d73
--- /dev/null
@@ -0,0 +1,89 @@
+/* $Id: cmd-break-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "dt:", 0, 0,
+       "[-d] " CMD_TARGET_PANE_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_break_pane_exec
+};
+
+int
+cmd_break_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct winlink          *wl;
+       struct session          *s;
+       struct window_pane      *wp;
+       struct window           *w;
+       char                    *cause;
+       int                      base_idx;
+
+       if ((wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp)) == NULL)
+               return (-1);
+
+       if (window_count_panes(wl->window) == 1) {
+               ctx->error(ctx, "can't break with only one pane");
+               return (-1);
+       }
+
+       w = wl->window;
+       TAILQ_REMOVE(&w->panes, wp, entry);
+       if (wp == w->active) {
+               w->active = w->last;
+               w->last = NULL;
+               if (w->active == NULL) {
+                       w->active = TAILQ_PREV(wp, window_panes, entry);
+                       if (w->active == NULL)
+                               w->active = TAILQ_NEXT(wp, entry);
+               }
+       } else if (wp == w->last)
+               w->last = NULL;
+       layout_close_pane(wp);
+
+       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);
+       layout_init(w);
+
+       base_idx = options_get_number(&s->options, "base-index");
+       wl = session_attach(s, w, -1 - base_idx, &cause); /* can't fail */
+       if (!args_has(self->args, 'd'))
+               session_select(s, wl->idx);
+
+       server_redraw_session(s);
+       server_status_session_group(s);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-capture-pane.c b/external/bsd/tmux/dist/cmd-capture-pane.c
new file mode 100644 (file)
index 0000000..c680842
--- /dev/null
@@ -0,0 +1,123 @@
+/* $Id: cmd-capture-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */
+
+/*
+ * Copyright (c) 2009 Jonathan Alvarado <radobobo@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"
+
+/*
+ * Write the entire contents of a pane to a buffer.
+ */
+
+int    cmd_capture_pane_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_capture_pane_entry = {
+       "capture-pane", "capturep",
+       "b:E:S:t:", 0, 0,
+       "[-b buffer-index] [-E end-line] [-S start-line] [-t target-pane]",
+       0,
+       NULL,
+       NULL,
+       cmd_capture_pane_exec
+};
+
+int
+cmd_capture_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct window_pane      *wp;
+       char                    *buf, *line, *cause;
+       struct screen           *s;
+       struct grid             *gd;
+       int                      buffer, n;
+       u_int                    i, limit, top, bottom, tmp;
+       size_t                   len, linelen;
+
+       if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL)
+               return (-1);
+       s = &wp->base;
+       gd = s->grid;
+
+       buf = NULL;
+       len = 0;
+
+       n = args_strtonum(args, 'S', SHRT_MIN, SHRT_MAX, &cause);
+       if (cause != NULL) {
+               top = gd->hsize;
+               xfree(cause);
+       } else if (n < 0 && (u_int) -n > gd->hsize)
+               top = 0;
+       else
+               top = gd->hsize + n;
+       if (top > gd->hsize + gd->sy - 1)
+               top = gd->hsize + gd->sy - 1;
+
+       n = args_strtonum(args, 'E', SHRT_MIN, SHRT_MAX, &cause);
+       if (cause != NULL) {
+               bottom = gd->hsize + gd->sy - 1;
+               xfree(cause);
+       } else if (n < 0 && (u_int) -n > gd->hsize)
+               bottom = 0;
+       else
+               bottom = gd->hsize + n;
+       if (bottom > gd->hsize + gd->sy - 1)
+               bottom = gd->hsize + gd->sy - 1;
+
+       if (bottom < top) {
+               tmp = bottom;
+               bottom = top;
+               top = tmp;
+       }
+
+       for (i = top; i <= bottom; i++) {
+              line = grid_string_cells(s->grid, 0, i, screen_size_x(s));
+              linelen = strlen(line);
+
+              buf = xrealloc(buf, 1, len + linelen + 1);
+              memcpy(buf + len, line, linelen);
+              len += linelen;
+              buf[len++] = '\n';
+
+              xfree(line);
+       }
+
+       limit = options_get_number(&global_options, "buffer-limit");
+
+       if (!args_has(args, 'b')) {
+               paste_add(&global_buffers, buf, len, limit);
+               return (0);
+       }
+
+       buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
+       if (cause != NULL) {
+               ctx->error(ctx, "buffer %s", cause);
+               xfree(cause);
+               return (-1);
+       }
+
+       if (paste_replace(&global_buffers, buffer, buf, len) != 0) {
+               ctx->error(ctx, "no buffer %d", buffer);
+               xfree(buf);
+               return (-1);
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-choose-buffer.c b/external/bsd/tmux/dist/cmd-choose-buffer.c
new file mode 100644 (file)
index 0000000..fd7dbc4
--- /dev/null
@@ -0,0 +1,143 @@
+/* $Id: cmd-choose-buffer.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */
+
+/*
+ * Copyright (c) 2010 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 "tmux.h"
+
+/*
+ * Enter choice mode to choose a buffer.
+ */
+
+int    cmd_choose_buffer_exec(struct cmd *, struct cmd_ctx *);
+
+void   cmd_choose_buffer_callback(void *, int);
+void   cmd_choose_buffer_free(void *);
+
+const struct cmd_entry cmd_choose_buffer_entry = {
+       "choose-buffer", NULL,
+       "t:", 0, 1,
+       CMD_TARGET_WINDOW_USAGE " [template]",
+       0,
+       NULL,
+       NULL,
+       cmd_choose_buffer_exec
+};
+
+struct cmd_choose_buffer_data {
+       struct client   *client;
+       char            *template;
+};
+
+int
+cmd_choose_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args                     *args = self->args;
+       struct cmd_choose_buffer_data   *cdata;
+       struct winlink                  *wl;
+       struct paste_buffer             *pb;
+       u_int                            idx;
+       char                            *tmp;
+
+       if (ctx->curclient == NULL) {
+               ctx->error(ctx, "must be run interactively");
+               return (-1);
+       }
+
+       if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL)
+               return (-1);
+
+       if (paste_get_top(&global_buffers) == NULL)
+               return (0);
+
+       if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
+               return (0);
+
+       idx = 0;
+       while ((pb = paste_walk_stack(&global_buffers, &idx)) != NULL) {
+               tmp = paste_print(pb, 50);
+               window_choose_add(wl->window->active, idx - 1,
+                   "%u: %zu bytes: \"%s\"", idx - 1, pb->size, tmp);
+               xfree(tmp);
+       }
+
+       cdata = xmalloc(sizeof *cdata);
+       if (args->argc != 0)
+               cdata->template = xstrdup(args->argv[0]);
+       else
+               cdata->template = xstrdup("paste-buffer -b '%%'");
+       cdata->client = ctx->curclient;
+       cdata->client->references++;
+
+       window_choose_ready(wl->window->active,
+           0, cmd_choose_buffer_callback, cmd_choose_buffer_free, cdata);
+
+       return (0);
+}
+
+void
+cmd_choose_buffer_callback(void *data, int idx)
+{
+       struct cmd_choose_buffer_data   *cdata = data;
+       struct cmd_list                 *cmdlist;
+       struct cmd_ctx                   ctx;
+       char                            *template, *cause, tmp[16];
+
+       if (idx == -1)
+               return;
+       if (cdata->client->flags & CLIENT_DEAD)
+               return;
+
+       xsnprintf(tmp, sizeof tmp, "%u", idx);
+       template = cmd_template_replace(cdata->template, tmp, 1);
+
+       if (cmd_string_parse(template, &cmdlist, &cause) != 0) {
+               if (cause != NULL) {
+                       *cause = toupper((u_char) *cause);
+                       status_message_set(cdata->client, "%s", cause);
+                       xfree(cause);
+               }
+               xfree(template);
+               return;
+       }
+       xfree(template);
+
+       ctx.msgdata = NULL;
+       ctx.curclient = cdata->client;
+
+       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);
+}
+
+void
+cmd_choose_buffer_free(void *data)
+{
+       struct cmd_choose_buffer_data   *cdata = data;
+
+       cdata->client->references--;
+       xfree(cdata->template);
+       xfree(cdata);
+}
diff --git a/external/bsd/tmux/dist/cmd-choose-client.c b/external/bsd/tmux/dist/cmd-choose-client.c
new file mode 100644 (file)
index 0000000..2a67fe9
--- /dev/null
@@ -0,0 +1,151 @@
+/* $Id: cmd-choose-client.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 "tmux.h"
+
+/*
+ * Enter choice mode to choose a client.
+ */
+
+int    cmd_choose_client_exec(struct cmd *, struct cmd_ctx *);
+
+void   cmd_choose_client_callback(void *, int);
+void   cmd_choose_client_free(void *);
+
+const struct cmd_entry cmd_choose_client_entry = {
+       "choose-client", NULL,
+       "t:", 0, 1,
+       CMD_TARGET_WINDOW_USAGE " [template]",
+       0,
+       NULL,
+       NULL,
+       cmd_choose_client_exec
+};
+
+struct cmd_choose_client_data {
+       struct client   *client;
+       char            *template;
+};
+
+int
+cmd_choose_client_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args                     *args = self->args;
+       struct cmd_choose_client_data   *cdata;
+       struct winlink                  *wl;
+       struct client                   *c;
+       u_int                            i, idx, cur;
+
+       if (ctx->curclient == NULL) {
+               ctx->error(ctx, "must be run interactively");
+               return (-1);
+       }
+
+       if ((wl = cmd_find_window(ctx, args_get(args, 't'), 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(&clients); i++) {
+               c = ARRAY_ITEM(&clients, i);
+               if (c == NULL || c->session == NULL)
+                       continue;
+               if (c == ctx->curclient)
+                       cur = idx;
+               idx++;
+
+               window_choose_add(wl->window->active, i,
+                   "%s: %s [%ux%u %s]%s", c->tty.path,
+                   c->session->name, c->tty.sx, c->tty.sy,
+                   c->tty.termname, c->tty.flags & TTY_UTF8 ? " (utf8)" : "");
+       }
+
+       cdata = xmalloc(sizeof *cdata);
+       if (args->argc != 0)
+               cdata->template = xstrdup(args->argv[0]);
+       else
+               cdata->template = xstrdup("detach-client -t '%%'");
+       cdata->client = ctx->curclient;
+       cdata->client->references++;
+
+       window_choose_ready(wl->window->active,
+           cur, cmd_choose_client_callback, cmd_choose_client_free, cdata);
+
+       return (0);
+}
+
+void
+cmd_choose_client_callback(void *data, int idx)
+{
+       struct cmd_choose_client_data   *cdata = data;
+       struct client                   *c;
+       struct cmd_list                 *cmdlist;
+       struct cmd_ctx                   ctx;
+       char                            *template, *cause;
+
+       if (idx == -1)
+               return;
+       if (cdata->client->flags & CLIENT_DEAD)
+               return;
+
+       if ((u_int) idx > ARRAY_LENGTH(&clients) - 1)
+               return;
+       c = ARRAY_ITEM(&clients, idx);
+       if (c == NULL || c->session == NULL)
+               return;
+       template = cmd_template_replace(cdata->template, c->tty.path, 1);
+
+       if (cmd_string_parse(template, &cmdlist, &cause) != 0) {
+               if (cause != NULL) {
+                       *cause = toupper((u_char) *cause);
+                       status_message_set(c, "%s", cause);
+                       xfree(cause);
+               }
+               xfree(template);
+               return;
+       }
+       xfree(template);
+
+       ctx.msgdata = NULL;
+       ctx.curclient = cdata->client;
+
+       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);
+}
+
+void
+cmd_choose_client_free(void *data)
+{
+       struct cmd_choose_client_data   *cdata = data;
+
+       cdata->client->references--;
+       xfree(cdata->template);
+       xfree(cdata);
+}
diff --git a/external/bsd/tmux/dist/cmd-choose-session.c b/external/bsd/tmux/dist/cmd-choose-session.c
new file mode 100644 (file)
index 0000000..6bd1c34
--- /dev/null
@@ -0,0 +1,156 @@
+/* $Id: cmd-choose-session.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 "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);
+void   cmd_choose_session_free(void *);
+
+const struct cmd_entry cmd_choose_session_entry = {
+       "choose-session", NULL,
+       "t:", 0, 1,
+       CMD_TARGET_WINDOW_USAGE " [template]",
+       0,
+       NULL,
+       NULL,
+       cmd_choose_session_exec
+};
+
+struct cmd_choose_session_data {
+       struct client   *client;
+       char            *template;
+};
+
+int
+cmd_choose_session_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args                     *args = self->args;
+       struct cmd_choose_session_data  *cdata;
+       struct winlink                  *wl;
+       struct session                  *s;
+       struct session_group            *sg;
+       u_int                            idx, sgidx, cur;
+       char                             tmp[64];
+
+       if (ctx->curclient == NULL) {
+               ctx->error(ctx, "must be run interactively");
+               return (-1);
+       }
+
+       if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL)
+               return (-1);
+
+       if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
+               return (0);
+
+       cur = idx = 0;
+       RB_FOREACH(s, sessions, &sessions) {
+               if (s == ctx->curclient->session)
+                       cur = idx;
+               idx++;
+
+               sg = session_group_find(s);
+               if (sg == NULL)
+                       *tmp = '\0';
+               else {
+                       sgidx = session_group_index(sg);
+                       xsnprintf(tmp, sizeof tmp, " (group %u)", sgidx);
+               }
+
+               window_choose_add(wl->window->active, s->idx,
+                   "%s: %u windows [%ux%u]%s%s", s->name,
+                   winlink_count(&s->windows), s->sx, s->sy,
+                   tmp, s->flags & SESSION_UNATTACHED ? "" : " (attached)");
+       }
+
+       cdata = xmalloc(sizeof *cdata);
+       if (args->argc != 0)
+               cdata->template = xstrdup(args->argv[0]);
+       else
+               cdata->template = xstrdup("switch-client -t '%%'");
+       cdata->client = ctx->curclient;
+       cdata->client->references++;
+
+       window_choose_ready(wl->window->active,
+           cur, cmd_choose_session_callback, cmd_choose_session_free, cdata);
+
+       return (0);
+}
+
+void
+cmd_choose_session_callback(void *data, int idx)
+{
+       struct cmd_choose_session_data  *cdata = data;
+       struct session                  *s;
+       struct cmd_list                 *cmdlist;
+       struct cmd_ctx                   ctx;
+       char                            *template, *cause;
+
+       if (idx == -1)
+               return;
+       if (cdata->client->flags & CLIENT_DEAD)
+               return;
+
+       s = session_find_by_index(idx);
+       if (s == NULL)
+               return;
+       template = cmd_template_replace(cdata->template, s->name, 1);
+
+       if (cmd_string_parse(template, &cmdlist, &cause) != 0) {
+               if (cause != NULL) {
+                       *cause = toupper((u_char) *cause);
+                       status_message_set(cdata->client, "%s", cause);
+                       xfree(cause);
+               }
+               xfree(template);
+               return;
+       }
+       xfree(template);
+
+       ctx.msgdata = NULL;
+       ctx.curclient = cdata->client;
+
+       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);
+}
+
+void
+cmd_choose_session_free(void *data)
+{
+       struct cmd_choose_session_data  *cdata = data;
+
+       cdata->client->references--;
+       xfree(cdata->template);
+       xfree(cdata);
+}
diff --git a/external/bsd/tmux/dist/cmd-choose-window.c b/external/bsd/tmux/dist/cmd-choose-window.c
new file mode 100644 (file)
index 0000000..1061446
--- /dev/null
@@ -0,0 +1,169 @@
+/* $Id: cmd-choose-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 "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);
+void   cmd_choose_window_free(void *);
+
+const struct cmd_entry cmd_choose_window_entry = {
+       "choose-window", NULL,
+       "t:", 0, 1,
+       CMD_TARGET_WINDOW_USAGE " [template]",
+       0,
+       NULL,
+       NULL,
+       cmd_choose_window_exec
+};
+
+struct cmd_choose_window_data {
+       struct client   *client;
+       struct session  *session;
+       char            *template;
+};
+
+int
+cmd_choose_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args                     *args = self->args;
+       struct cmd_choose_window_data   *cdata;
+       struct session                  *s;
+       struct winlink                  *wl, *wm;
+       struct window                   *w;
+       u_int                            idx, cur;
+       char                            *flags, *title;
+       const char                      *left, *right;
+
+       if (ctx->curclient == NULL) {
+               ctx->error(ctx, "must be run interactively");
+               return (-1);
+       }
+       s = ctx->curclient->session;
+
+       if ((wl = cmd_find_window(ctx, args_get(args, 't'), 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++;
+
+               flags = window_printable_flags(s, wm);
+               title = w->active->screen->title;
+               if (wm == wl)
+                       title = w->active->base.title;
+               left = " \"";
+               right = "\"";
+               if (*title == '\0')
+                       left = right = "";
+
+               window_choose_add(wl->window->active,
+                   wm->idx, "%3d: %s%s [%ux%u] (%u panes%s)%s%s%s",
+                   wm->idx, w->name, flags, w->sx, w->sy, window_count_panes(w),
+                   w->active->fd == -1 ? ", dead" : "",
+                   left, title, right);
+
+               xfree(flags);
+       }
+
+       cdata = xmalloc(sizeof *cdata);
+       if (args->argc != 0)
+               cdata->template = xstrdup(args->argv[0]);
+       else
+               cdata->template = xstrdup("select-window -t '%%'");
+       cdata->session = s;
+       cdata->session->references++;
+       cdata->client = ctx->curclient;
+       cdata->client->references++;
+
+       window_choose_ready(wl->window->active,
+           cur, cmd_choose_window_callback, cmd_choose_window_free, cdata);
+
+       return (0);
+}
+
+void
+cmd_choose_window_callback(void *data, int idx)
+{
+       struct cmd_choose_window_data   *cdata = data;
+       struct session                  *s = cdata->session;
+       struct cmd_list                 *cmdlist;
+       struct cmd_ctx                   ctx;
+       char                            *target, *template, *cause;
+
+       if (idx == -1)
+               return;
+       if (!session_alive(s))
+               return;
+       if (cdata->client->flags & CLIENT_DEAD)
+               return;
+
+       xasprintf(&target, "%s:%d", s->name, idx);
+       template = cmd_template_replace(cdata->template, target, 1);
+       xfree(target);
+
+       if (cmd_string_parse(template, &cmdlist, &cause) != 0) {
+               if (cause != NULL) {
+                       *cause = toupper((u_char) *cause);
+                       status_message_set(cdata->client, "%s", cause);
+                       xfree(cause);
+               }
+               xfree(template);
+               return;
+       }
+       xfree(template);
+
+       ctx.msgdata = NULL;
+       ctx.curclient = cdata->client;
+
+       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);
+}
+
+void
+cmd_choose_window_free(void *data)
+{
+       struct cmd_choose_window_data   *cdata = data;
+
+       cdata->session->references--;
+       cdata->client->references--;
+       xfree(cdata->template);
+       xfree(cdata);
+}
diff --git a/external/bsd/tmux/dist/cmd-clear-history.c b/external/bsd/tmux/dist/cmd-clear-history.c
new file mode 100644 (file)
index 0000000..533b703
--- /dev/null
@@ -0,0 +1,54 @@
+/* $Id: cmd-clear-history.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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.
+ */
+
+int    cmd_clear_history_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_clear_history_entry = {
+       "clear-history", "clearhist",
+       "t:", 0, 0,
+       CMD_TARGET_PANE_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_clear_history_exec
+};
+
+int
+cmd_clear_history_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct window_pane      *wp;
+       struct grid             *gd;
+
+       if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL)
+               return (-1);
+       gd = wp->base.grid;
+
+       grid_move_lines(gd, 0, gd->hsize, gd->sy);
+       gd->hsize = 0;
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-clock-mode.c b/external/bsd/tmux/dist/cmd-clock-mode.c
new file mode 100644 (file)
index 0000000..a446c62
--- /dev/null
@@ -0,0 +1,51 @@
+/* $Id: cmd-clock-mode.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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,
+       "t:", 0, 0,
+       CMD_TARGET_PANE_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_clock_mode_exec
+};
+
+int
+cmd_clock_mode_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct window_pane      *wp;
+
+       if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL)
+               return (-1);
+
+       window_pane_set_mode(wp, &window_clock_mode);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-command-prompt.c b/external/bsd/tmux/dist/cmd-command-prompt.c
new file mode 100644 (file)
index 0000000..54e957e
--- /dev/null
@@ -0,0 +1,215 @@
+/* $Id: cmd-command-prompt.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 <time.h>
+
+#include "tmux.h"
+
+/*
+ * Prompt for command in client.
+ */
+
+void   cmd_command_prompt_key_binding(struct cmd *, int);
+int    cmd_command_prompt_check(struct args *);
+int    cmd_command_prompt_exec(struct cmd *, struct cmd_ctx *);
+
+int    cmd_command_prompt_callback(void *, const char *);
+void   cmd_command_prompt_free(void *);
+
+const struct cmd_entry cmd_command_prompt_entry = {
+       "command-prompt", NULL,
+       "I:p:t:", 0, 1,
+       "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [template]",
+       0,
+       cmd_command_prompt_key_binding,
+       NULL,
+       cmd_command_prompt_exec
+};
+
+struct cmd_command_prompt_cdata {
+       struct client   *c;
+       char            *inputs;
+       char            *next_input;
+       char            *next_prompt;
+       char            *prompts;
+       char            *template;
+       int              idx;
+};
+
+void
+cmd_command_prompt_key_binding(struct cmd *self, int key)
+{
+       switch (key) {
+       case '$':
+               self->args = args_create(1, "rename-session '%%'");
+               args_set(self->args, 'I', "#S");
+               break;
+       case ',':
+               self->args = args_create(1, "rename-window '%%'");
+               args_set(self->args, 'I', "#W");
+               break;
+       case '.':
+               self->args = args_create(1, "move-window -t '%%'");
+               break;
+       case 'f':
+               self->args = args_create(1, "find-window '%%'");
+               break;
+       case '\'':
+               self->args = args_create(1, "select-window -t ':%%'");
+               args_set(self->args, 'p', "index");
+               break;
+       default:
+               self->args = args_create(0);
+               break;
+       }
+}
+
+int
+cmd_command_prompt_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args                     *args = self->args;
+       const char                      *inputs, *prompts;
+       struct cmd_command_prompt_cdata *cdata;
+       struct client                   *c;
+       char                            *prompt, *ptr, *input = NULL;
+       size_t                           n;
+
+       if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL)
+               return (-1);
+
+       if (c->prompt_string != NULL)
+               return (0);
+
+       cdata = xmalloc(sizeof *cdata);
+       cdata->c = c;
+       cdata->idx = 1;
+       cdata->inputs = NULL;
+       cdata->next_input = NULL;
+       cdata->next_prompt = NULL;
+       cdata->prompts = NULL;
+       cdata->template = NULL;
+
+       if (args->argc != 0)
+               cdata->template = xstrdup(args->argv[0]);
+       else
+               cdata->template = xstrdup("%1");
+
+       if ((prompts = args_get(args, 'p')) != NULL)
+               cdata->prompts = xstrdup(prompts);
+       else if (args->argc != 0) {
+               n = strcspn(cdata->template, " ,");
+               xasprintf(&cdata->prompts, "(%.*s) ", (int) n, cdata->template);
+       } else
+               cdata->prompts = xstrdup(":");
+
+       /* Get first prompt. */
+       cdata->next_prompt = cdata->prompts;
+       ptr = strsep(&cdata->next_prompt, ",");
+       if (prompts == NULL)
+               prompt = xstrdup(ptr);
+       else
+               xasprintf(&prompt, "%s ", ptr);
+
+       /* Get initial prompt input. */
+       if ((inputs = args_get(args, 'I')) != NULL) {
+               cdata->inputs = xstrdup(inputs);
+               cdata->next_input = cdata->inputs;
+               input = strsep(&cdata->next_input, ",");
+       }
+
+       status_prompt_set(c, prompt, input, cmd_command_prompt_callback,
+           cmd_command_prompt_free, cdata, 0);
+       xfree(prompt);
+
+       return (0);
+}
+
+int
+cmd_command_prompt_callback(void *data, const char *s)
+{
+       struct cmd_command_prompt_cdata *cdata = data;
+       struct client                   *c = cdata->c;
+       struct cmd_list                 *cmdlist;
+       struct cmd_ctx                   ctx;
+       char                            *cause, *new_template, *prompt, *ptr;
+       char                            *input = NULL;
+
+       if (s == NULL)
+               return (0);
+
+       new_template = cmd_template_replace(cdata->template, s, cdata->idx);
+       xfree(cdata->template);
+       cdata->template = new_template;
+
+       /*
+        * Check if there are more prompts; if so, get its respective input
+        * and update the prompt data.
+        */
+       if ((ptr = strsep(&cdata->next_prompt, ",")) != NULL) {
+               xasprintf(&prompt, "%s ", ptr);
+               input = strsep(&cdata->next_input, ",");
+               status_prompt_update(c, prompt, input);
+
+               xfree(prompt);
+               cdata->idx++;
+               return (1);
+       }
+
+       if (cmd_string_parse(new_template, &cmdlist, &cause) != 0) {
+               if (cause != NULL) {
+                       *cause = toupper((u_char) *cause);
+                       status_message_set(c, "%s", cause);
+                       xfree(cause);
+               }
+               return (0);
+       }
+
+       ctx.msgdata = NULL;
+       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_callbackfn != (void *) &cmd_command_prompt_callback)
+               return (1);
+       return (0);
+}
+
+void
+cmd_command_prompt_free(void *data)
+{
+       struct cmd_command_prompt_cdata *cdata = data;
+
+       if (cdata->inputs != NULL)
+               xfree(cdata->inputs);
+       if (cdata->prompts != NULL)
+               xfree(cdata->prompts);
+       if (cdata->template != NULL)
+               xfree(cdata->template);
+       xfree(cdata);
+}
diff --git a/external/bsd/tmux/dist/cmd-confirm-before.c b/external/bsd/tmux/dist/cmd-confirm-before.c
new file mode 100644 (file)
index 0000000..26fa0ef
--- /dev/null
@@ -0,0 +1,150 @@
+/* $Id: cmd-confirm-before.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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.
+ */
+
+void   cmd_confirm_before_key_binding(struct cmd *, int);
+int    cmd_confirm_before_exec(struct cmd *, struct cmd_ctx *);
+
+int    cmd_confirm_before_callback(void *, const char *);
+void   cmd_confirm_before_free(void *);
+
+const struct cmd_entry cmd_confirm_before_entry = {
+       "confirm-before", "confirm",
+       "p:t:", 1, 1,
+       "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command",
+       0,
+       cmd_confirm_before_key_binding,
+       NULL,
+       cmd_confirm_before_exec
+};
+
+struct cmd_confirm_before_data {
+       struct client   *c;
+       char            *cmd;
+};
+
+void
+cmd_confirm_before_key_binding(struct cmd *self, int key)
+{
+       switch (key) {
+       case '&':
+               self->args = args_create(1, "kill-window");
+               args_set(self->args, 'p', "kill-window #W? (y/n)");
+               break;
+       case 'x':
+               self->args = args_create(1, "kill-pane");
+               args_set(self->args, 'p', "kill-pane #P? (y/n)");
+               break;
+       default:
+               self->args = args_create(0);
+               break;
+       }
+}
+
+int
+cmd_confirm_before_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args                     *args = self->args;
+       struct cmd_confirm_before_data  *cdata;
+       struct client                   *c;
+       char                            *cmd, *copy, *new_prompt, *ptr;
+       const char                      *prompt;
+
+       if (ctx->curclient == NULL) {
+               ctx->error(ctx, "must be run interactively");
+               return (-1);
+       }
+
+       if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL)
+               return (-1);
+
+       if ((prompt = args_get(args, 'p')) != NULL)
+               xasprintf(&new_prompt, "%s ", prompt);
+       else {
+               ptr = copy = xstrdup(args->argv[0]);
+               cmd = strsep(&ptr, " \t");
+               xasprintf(&new_prompt, "Confirm '%s'? (y/n) ", cmd);
+               xfree(copy);
+       }
+
+       cdata = xmalloc(sizeof *cdata);
+       cdata->cmd = xstrdup(args->argv[0]);
+       cdata->c = c;
+       status_prompt_set(cdata->c, new_prompt, NULL,
+           cmd_confirm_before_callback, cmd_confirm_before_free, cdata,
+           PROMPT_SINGLE);
+
+       xfree(new_prompt);
+       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 || *s == '\0')
+               return (0);
+       if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
+               return (0);
+
+       if (cmd_string_parse(cdata->cmd, &cmdlist, &cause) != 0) {
+               if (cause != NULL) {
+                       *cause = toupper((u_char) *cause);
+                       status_message_set(c, "%s", cause);
+                       xfree(cause);
+               }
+               return (0);
+       }
+
+       ctx.msgdata = NULL;
+       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);
+
+       return (0);
+}
+
+void
+cmd_confirm_before_free(void *data)
+{
+       struct cmd_confirm_before_data  *cdata = data;
+
+       if (cdata->cmd != NULL)
+               xfree(cdata->cmd);
+       xfree(cdata);
+}
diff --git a/external/bsd/tmux/dist/cmd-copy-mode.c b/external/bsd/tmux/dist/cmd-copy-mode.c
new file mode 100644 (file)
index 0000000..8205f8b
--- /dev/null
@@ -0,0 +1,64 @@
+/* $Id: cmd-copy-mode.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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.
+ */
+
+void   cmd_copy_mode_key_binding(struct cmd *, int);
+int    cmd_copy_mode_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_copy_mode_entry = {
+       "copy-mode", NULL,
+       "t:u", 0, 0,
+       "[-u] " CMD_TARGET_PANE_USAGE,
+       0,
+       cmd_copy_mode_key_binding,
+       NULL,
+       cmd_copy_mode_exec
+};
+
+void
+cmd_copy_mode_key_binding(struct cmd *self, int key)
+{
+       self->args = args_create(0);
+       if (key == KEYC_PPAGE)
+               args_set(self->args, 'u', NULL);
+}
+
+int
+cmd_copy_mode_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct window_pane      *wp;
+
+       if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL)
+               return (-1);
+
+       if (window_pane_set_mode(wp, &window_copy_mode) != 0)
+               return (0);
+       window_copy_init_from_pane(wp);
+       if (wp->mode == &window_copy_mode && args_has(self->args, 'u'))
+               window_copy_pageup(wp);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-delete-buffer.c b/external/bsd/tmux/dist/cmd-delete-buffer.c
new file mode 100644 (file)
index 0000000..6ffa36f
--- /dev/null
@@ -0,0 +1,66 @@
+/* $Id: cmd-delete-buffer.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "b:", 0, 0,
+       CMD_BUFFER_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_delete_buffer_exec
+};
+
+int
+cmd_delete_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       char            *cause;
+       int              buffer;
+
+       if (!args_has(args, 'b')) {
+               paste_free_top(&global_buffers);
+               return (0);
+       }
+
+       buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
+       if (cause != NULL) {
+               ctx->error(ctx, "buffer %s", cause);
+               xfree(cause);
+               return (-1);
+       }
+
+       if (paste_free_index(&global_buffers, buffer) != 0) {
+               ctx->error(ctx, "no buffer %d", buffer);
+               return (-1);
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-detach-client.c b/external/bsd/tmux/dist/cmd-detach-client.c
new file mode 100644 (file)
index 0000000..d7ad141
--- /dev/null
@@ -0,0 +1,72 @@
+/* $Id: cmd-detach-client.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "s:t:P", 0, 0,
+       "[-P] [-s target-session] " CMD_TARGET_CLIENT_USAGE,
+       CMD_READONLY,
+       NULL,
+       NULL,
+       cmd_detach_client_exec
+};
+
+int
+cmd_detach_client_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct client   *c;
+       struct session  *s;
+       enum msgtype     msgtype;
+       u_int            i;
+
+       if (args_has(args, 'P'))
+               msgtype = MSG_DETACHKILL;
+       else
+               msgtype = MSG_DETACH;
+
+       if (args_has(args, 's')) {
+               s = cmd_find_session(ctx, args_get(args, 's'), 0);
+               if (s == NULL)
+                       return (-1);
+
+               for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+                       c = ARRAY_ITEM(&clients, i);
+                       if (c != NULL && c->session == s)
+                               server_write_client(c, msgtype, NULL, 0);
+               }
+       } else {
+               c = cmd_find_client(ctx, args_get(args, 't'));
+               if (c == NULL)
+                       return (-1);
+
+               server_write_client(c, msgtype, NULL, 0);
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-display-message.c b/external/bsd/tmux/dist/cmd-display-message.c
new file mode 100644 (file)
index 0000000..4240a7d
--- /dev/null
@@ -0,0 +1,78 @@
+/* $Id: cmd-display-message.c,v 1.2 2011/08/19 09:06:05 christos 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 <time.h>
+
+#include "tmux.h"
+
+/*
+ * Displays a message in the status line.
+ */
+
+int    cmd_display_message_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_display_message_entry = {
+       "display-message", "display",
+       "c:pt:", 0, 1,
+       "[-p] [-c target-client] [-t target-pane] [message]",
+       0,
+       NULL,
+       NULL,
+       cmd_display_message_exec
+};
+
+int
+cmd_display_message_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct client           *c;
+       struct session          *s;
+       struct winlink          *wl;
+       struct window_pane      *wp;
+       const char              *template;
+       char                    *msg;
+
+       if ((c = cmd_find_client(ctx, args_get(args, 'c'))) == NULL)
+               return (-1);
+
+       if (args_has(args, 't') != 0) {
+               wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp);
+               if (wl == NULL)
+                       return (-1);
+       } else {
+               s = NULL;
+               wl = NULL;
+               wp = NULL;
+       }
+
+       if (args->argc == 0)
+               template = "[#S] #I:#W, current pane #P - (%H:%M %d-%b-%Y)";
+       else
+               template = args->argv[0];
+
+       msg = status_replace(c, s, wl, wp, template, time(NULL), 0);
+       if (args_has(self->args, 'p'))
+               ctx->print(ctx, "%s", msg);
+       else
+               status_message_set(c, "%s", msg);
+       xfree(msg);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-display-panes.c b/external/bsd/tmux/dist/cmd-display-panes.c
new file mode 100644 (file)
index 0000000..c476aa3
--- /dev/null
@@ -0,0 +1,51 @@
+/* $Id: cmd-display-panes.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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"
+
+/*
+ * Display panes on a client.
+ */
+
+int    cmd_display_panes_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_display_panes_entry = {
+       "display-panes", "displayp",
+       "t:", 0, 0,
+       CMD_TARGET_CLIENT_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_display_panes_exec
+};
+
+int
+cmd_display_panes_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct client   *c;
+
+       if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL)
+               return (-1);
+
+       server_set_identify(c);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-find-window.c b/external/bsd/tmux/dist/cmd-find-window.c
new file mode 100644 (file)
index 0000000..b665c1a
--- /dev/null
@@ -0,0 +1,176 @@
+/* $Id: cmd-find-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 <fnmatch.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);
+void   cmd_find_window_free(void *);
+
+const struct cmd_entry cmd_find_window_entry = {
+       "find-window", "findw",
+       "t:", 1, 1,
+       CMD_TARGET_WINDOW_USAGE " match-string",
+       0,
+       NULL,
+       NULL,
+       cmd_find_window_exec
+};
+
+struct cmd_find_window_data {
+       struct session  *session;
+};
+
+int
+cmd_find_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args                     *args = self->args;
+       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                            *str, *sres, *sctx, *searchstr;
+       u_int                            i, line;
+
+       if (ctx->curclient == NULL) {
+               ctx->error(ctx, "must be run interactively");
+               return (-1);
+       }
+       s = ctx->curclient->session;
+
+       if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL)
+               return (-1);
+
+       str = args->argv[0];
+
+       ARRAY_INIT(&list_idx);
+       ARRAY_INIT(&list_ctx);
+
+       xasprintf(&searchstr, "*%s*", str);
+       RB_FOREACH(wm, winlinks, &s->windows) {
+               i = 0;
+               TAILQ_FOREACH(wp, &wm->window->panes, entry) {
+                       i++;
+
+                       if (fnmatch(searchstr, wm->window->name, 0) == 0)
+                               sctx = xstrdup("");
+                       else {
+                               sres = window_pane_search(wp, str, &line);
+                               if (sres == NULL &&
+                                   fnmatch(searchstr, wp->base.title, 0) != 0)
+                                       continue;
+
+                               if (sres == NULL) {
+                                       xasprintf(&sctx,
+                                           "pane %u title: \"%s\"", i - 1,
+                                           wp->base.title);
+                               } else {
+                                       xasprintf(&sctx,
+                                           "pane %u line %u: \"%s\"", i - 1,
+                                           line + 1, sres);
+                                       xfree(sres);
+                               }
+                       }
+
+                       ARRAY_ADD(&list_idx, wm->idx);
+                       ARRAY_ADD(&list_ctx, sctx);
+               }
+       }
+       xfree(searchstr);
+
+       if (ARRAY_LENGTH(&list_idx) == 0) {
+               ctx->error(ctx, "no windows matching: %s", str);
+               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);
+       cdata->session = s;
+       cdata->session->references++;
+
+       window_choose_ready(wl->window->active,
+           0, cmd_find_window_callback, cmd_find_window_free, 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 = cdata->session;
+
+       if (idx == -1)
+               return;
+       if (!session_alive(s))
+               return;
+
+       if (session_select(s, idx) == 0) {
+               server_redraw_session(s);
+               recalculate_sizes();
+       }
+}
+
+void
+cmd_find_window_free(void *data)
+{
+       struct cmd_find_window_data     *cdata = data;
+
+       cdata->session->references--;
+       xfree(cdata);
+}
diff --git a/external/bsd/tmux/dist/cmd-has-session.c b/external/bsd/tmux/dist/cmd-has-session.c
new file mode 100644 (file)
index 0000000..848f609
--- /dev/null
@@ -0,0 +1,48 @@
+/* $Id: cmd-has-session.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "t:", 0, 0,
+       CMD_TARGET_SESSION_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_has_session_exec
+};
+
+int
+cmd_has_session_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+
+       if (cmd_find_session(ctx, args_get(args, 't'), 0) == NULL)
+               return (-1);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-if-shell.c b/external/bsd/tmux/dist/cmd-if-shell.c
new file mode 100644 (file)
index 0000000..be65327
--- /dev/null
@@ -0,0 +1,110 @@
+/* $Id: cmd-if-shell.c,v 1.2 2013/10/20 03:00:02 christos Exp $ */
+
+/*
+ * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
+ * Copyright (c) 2009 Nicholas Marriott <nicm@openbsd.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/wait.h>
+
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Executes a tmux command if a shell command returns true.
+ */
+
+int    cmd_if_shell_exec(struct cmd *, struct cmd_ctx *);
+
+void   cmd_if_shell_callback(struct job *);
+void   cmd_if_shell_free(void *);
+
+const struct cmd_entry cmd_if_shell_entry = {
+       "if-shell", "if",
+       "", 2, 2,
+       "shell-command command",
+       0,
+       NULL,
+       NULL,
+       cmd_if_shell_exec
+};
+
+struct cmd_if_shell_data {
+       char            *cmd;
+       struct cmd_ctx   ctx;
+};
+
+int
+cmd_if_shell_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args                     *args = self->args;
+       struct cmd_if_shell_data        *cdata;
+       const char                      *shellcmd = args->argv[0];
+
+       cdata = xmalloc(sizeof *cdata);
+       cdata->cmd = xstrdup(args->argv[1]);
+       memcpy(&cdata->ctx, ctx, sizeof cdata->ctx);
+
+       if (ctx->cmdclient != NULL)
+               ctx->cmdclient->references++;
+       if (ctx->curclient != NULL)
+               ctx->curclient->references++;
+
+       job_run(shellcmd, cmd_if_shell_callback, cmd_if_shell_free, cdata);
+
+       return (1);     /* don't let client exit */
+}
+
+void
+cmd_if_shell_callback(struct job *job)
+{
+       struct cmd_if_shell_data        *cdata = job->data;
+       struct cmd_ctx                  *ctx = &cdata->ctx;
+       struct cmd_list                 *cmdlist;
+       char                            *cause;
+
+       if (!WIFEXITED(job->status) || WEXITSTATUS(job->status) != 0)
+               return;
+
+       if (cmd_string_parse(cdata->cmd, &cmdlist, &cause) != 0) {
+               if (cause != NULL) {
+                       ctx->error(ctx, "%s", cause);
+                       xfree(cause);
+               }
+               return;
+       }
+
+       cmd_list_exec(cmdlist, ctx);
+       cmd_list_free(cmdlist);
+}
+
+void
+cmd_if_shell_free(void *data)
+{
+       struct cmd_if_shell_data        *cdata = data;
+       struct cmd_ctx                  *ctx = &cdata->ctx;
+
+       if (ctx->cmdclient != NULL) {
+               ctx->cmdclient->references--;
+               ctx->cmdclient->flags |= CLIENT_EXIT;
+       }
+       if (ctx->curclient != NULL)
+               ctx->curclient->references--;
+
+       xfree(cdata->cmd);
+       xfree(cdata);
+}
diff --git a/external/bsd/tmux/dist/cmd-join-pane.c b/external/bsd/tmux/dist/cmd-join-pane.c
new file mode 100644 (file)
index 0000000..73a2b9b
--- /dev/null
@@ -0,0 +1,145 @@
+/* $Id: cmd-join-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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"
+
+/*
+ * Join a pane into another (like split/swap/kill).
+ */
+
+void   cmd_join_pane_key_binding(struct cmd *, int);
+int    cmd_join_pane_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_join_pane_entry = {
+       "join-pane", "joinp",
+       "dhvp:l:s:t:", 0, 0,
+       "[-dhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]",
+       0,
+       cmd_join_pane_key_binding,
+       NULL,
+       cmd_join_pane_exec
+};
+
+void
+cmd_join_pane_key_binding(struct cmd *self, int key)
+{
+       switch (key) {
+       case '%':
+               self->args = args_create(0);
+               args_set(self->args, 'h', NULL);
+               break;
+       default:
+               self->args = args_create(0);
+               break;
+       }
+}
+
+int
+cmd_join_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct session          *dst_s;
+       struct winlink          *src_wl, *dst_wl;
+       struct window           *src_w, *dst_w;
+       struct window_pane      *src_wp, *dst_wp;
+       char                    *cause;
+       int                      size, percentage, dst_idx;
+       enum layout_type         type;
+       struct layout_cell      *lc;
+
+       dst_wl = cmd_find_pane(ctx, args_get(args, 't'), &dst_s, &dst_wp);
+       if (dst_wl == NULL)
+               return (-1);
+       dst_w = dst_wl->window;
+       dst_idx = dst_wl->idx;
+
+       src_wl = cmd_find_pane(ctx, args_get(args, 's'), NULL, &src_wp);
+       if (src_wl == NULL)
+               return (-1);
+       src_w = src_wl->window;
+
+       if (src_w == dst_w) {
+               ctx->error(ctx, "can't join a pane to its own window");
+               return (-1);
+       }
+
+       type = LAYOUT_TOPBOTTOM;
+       if (args_has(args, 'h'))
+               type = LAYOUT_LEFTRIGHT;
+
+       size = -1;
+       if (args_has(args, 'l')) {
+               size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
+               if (cause != NULL) {
+                       ctx->error(ctx, "size %s", cause);
+                       xfree(cause);
+                       return (-1);
+               }
+       } else if (args_has(args, 'p')) {
+               percentage = args_strtonum(args, 'p', 0, 100, &cause);
+               if (cause != NULL) {
+                       ctx->error(ctx, "percentage %s", cause);
+                       xfree(cause);
+                       return (-1);
+               }
+               if (type == LAYOUT_TOPBOTTOM)
+                       size = (dst_wp->sy * percentage) / 100;
+               else
+                       size = (dst_wp->sx * percentage) / 100;
+       }
+
+       if ((lc = layout_split_pane(dst_wp, type, size)) == NULL) {
+               ctx->error(ctx, "create pane failed: pane too small");
+               return (-1);
+       }
+
+       layout_close_pane(src_wp);
+
+       if (src_w->active == src_wp) {
+               src_w->active = TAILQ_PREV(src_wp, window_panes, entry);
+               if (src_w->active == NULL)
+                       src_w->active = TAILQ_NEXT(src_wp, entry);
+       }
+       TAILQ_REMOVE(&src_w->panes, src_wp, entry);
+
+       if (window_count_panes(src_w) == 0)
+               server_kill_window(src_w);
+
+       src_wp->window = dst_w;
+       TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
+       layout_assign_pane(lc, src_wp);
+
+       recalculate_sizes();
+
+       server_redraw_window(src_w);
+       server_redraw_window(dst_w);
+
+       if (!args_has(args, 'd')) {
+               window_set_active_pane(dst_w, src_wp);
+               session_select(dst_s, dst_idx);
+               server_redraw_session(dst_s);
+       } else
+               server_status_session(dst_s);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-kill-pane.c b/external/bsd/tmux/dist/cmd-kill-pane.c
new file mode 100644 (file)
index 0000000..eea9377
--- /dev/null
@@ -0,0 +1,75 @@
+/* $Id: cmd-kill-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "at:", 0, 0,
+       "[-a] " CMD_TARGET_PANE_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_kill_pane_exec
+};
+
+int
+cmd_kill_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct winlink          *wl;
+       struct window_pane      *loopwp, *nextwp, *wp;
+
+       if ((wl = cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp)) == NULL)
+               return (-1);
+
+       if (window_count_panes(wl->window) == 1) {
+               /* Only one pane, kill the window. */
+               server_kill_window(wl->window);
+               recalculate_sizes();
+               return (0);
+       }
+
+       if (args_has(self->args, 'a')) {
+               loopwp = TAILQ_FIRST(&wl->window->panes);
+               while (loopwp != NULL) {
+                       nextwp = TAILQ_NEXT(loopwp, entry);
+                       if (loopwp != wp) {
+                               layout_close_pane(loopwp);
+                               window_remove_pane(wl->window, loopwp);
+                       }
+                       loopwp = nextwp;
+               }
+       } else {
+               layout_close_pane(wp);
+               window_remove_pane(wl->window, wp);
+       }
+       server_redraw_window(wl->window);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-kill-server.c b/external/bsd/tmux/dist/cmd-kill-server.c
new file mode 100644 (file)
index 0000000..e323381
--- /dev/null
@@ -0,0 +1,49 @@
+/* $Id: cmd-kill-server.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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, 0,
+       "",
+       0,
+       NULL,
+       NULL,
+       cmd_kill_server_exec
+};
+
+/* ARGSUSED */
+int
+cmd_kill_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx)
+{
+       kill(getpid(), SIGTERM);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-kill-session.c b/external/bsd/tmux/dist/cmd-kill-session.c
new file mode 100644 (file)
index 0000000..6406e40
--- /dev/null
@@ -0,0 +1,55 @@
+/* $Id: cmd-kill-session.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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,
+       "t:", 0, 0,
+       CMD_TARGET_SESSION_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_kill_session_exec
+};
+
+int
+cmd_kill_session_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct session  *s;
+
+       if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL)
+               return (-1);
+
+       server_destroy_session(s);
+       session_destroy(s);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-kill-window.c b/external/bsd/tmux/dist/cmd-kill-window.c
new file mode 100644 (file)
index 0000000..5f35cd9
--- /dev/null
@@ -0,0 +1,52 @@
+/* $Id: cmd-kill-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "t:", 0, 0,
+       CMD_TARGET_WINDOW_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_kill_window_exec
+};
+
+int
+cmd_kill_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct winlink  *wl;
+
+       if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL)
+               return (-1);
+
+       server_kill_window(wl->window);
+       recalculate_sizes();
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-link-window.c b/external/bsd/tmux/dist/cmd-link-window.c
new file mode 100644 (file)
index 0000000..648746c
--- /dev/null
@@ -0,0 +1,65 @@
+/* $Id: cmd-link-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "dks:t:", 0, 0,
+       "[-dk] " CMD_SRCDST_WINDOW_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_link_window_exec
+};
+
+int
+cmd_link_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct session  *src, *dst;
+       struct winlink  *wl;
+       char            *cause;
+       int              idx, kflag, dflag;
+
+       if ((wl = cmd_find_window(ctx, args_get(args, 's'), &src)) == NULL)
+               return (-1);
+       if ((idx = cmd_find_index(ctx, args_get(args, 't'), &dst)) == -2)
+               return (-1);
+
+       kflag = args_has(self->args, 'k');
+       dflag = args_has(self->args, 'd');
+       if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) {
+               ctx->error(ctx, "can't link window: %s", cause);
+               xfree(cause);
+               return (-1);
+       }
+       recalculate_sizes();
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-list-buffers.c b/external/bsd/tmux/dist/cmd-list-buffers.c
new file mode 100644 (file)
index 0000000..b942ccd
--- /dev/null
@@ -0,0 +1,58 @@
+/* $Id: cmd-list-buffers.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "", 0, 0,
+       "",
+       0,
+       NULL,
+       NULL,
+       cmd_list_buffers_exec
+};
+
+/* ARGSUSED */
+int
+cmd_list_buffers_exec(unused struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct paste_buffer     *pb;
+       u_int                    idx;
+       char                    *tmp;
+
+       idx = 0;
+       while ((pb = paste_walk_stack(&global_buffers, &idx)) != NULL) {
+               tmp = paste_print(pb, 50);
+               ctx->print(ctx,
+                   "%u: %zu bytes: \"%s\"", idx - 1, pb->size, tmp);
+               xfree(tmp);
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-list-clients.c b/external/bsd/tmux/dist/cmd-list-clients.c
new file mode 100644 (file)
index 0000000..1e88fe0
--- /dev/null
@@ -0,0 +1,77 @@
+/* $Id: cmd-list-clients.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "t:", 0, 0,
+       CMD_TARGET_SESSION_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_list_clients_exec
+};
+
+/* ARGSUSED */
+int
+cmd_list_clients_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct client   *c;
+       struct session  *s;
+       u_int            i;
+       const char      *s_utf8;
+
+       if (args_has(args, 't')) {
+               s = cmd_find_session(ctx, args_get(args, 't'), 0);
+               if (s == NULL)
+                       return (-1);
+       } else
+               s = NULL;
+
+       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 = "";
+
+               if (s != NULL && s != c->session)
+                       continue;
+               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/external/bsd/tmux/dist/cmd-list-commands.c b/external/bsd/tmux/dist/cmd-list-commands.c
new file mode 100644 (file)
index 0000000..90edd70
--- /dev/null
@@ -0,0 +1,49 @@
+/* $Id: cmd-list-commands.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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, 0,
+       "",
+       0,
+       NULL,
+       NULL,
+       cmd_list_commands_exec
+};
+
+/* ARGSUSED */
+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/external/bsd/tmux/dist/cmd-list-keys.c b/external/bsd/tmux/dist/cmd-list-keys.c
new file mode 100644 (file)
index 0000000..cee6221
--- /dev/null
@@ -0,0 +1,149 @@
+/* $Id: cmd-list-keys.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 key bindings.
+ */
+
+int    cmd_list_keys_exec(struct cmd *, struct cmd_ctx *);
+
+int    cmd_list_keys_table(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_list_keys_entry = {
+       "list-keys", "lsk",
+       "t:", 0, 0,
+       "[-t key-table]",
+       0,
+       NULL,
+       NULL,
+       cmd_list_keys_exec
+};
+
+int
+cmd_list_keys_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct key_binding      *bd;
+       const char              *key;
+       char                     tmp[BUFSIZ], flags[8];
+       size_t                   used;
+       int                      width, keywidth;
+
+       if (args_has(args, 't'))
+               return (cmd_list_keys_table(self, ctx));
+
+       width = 0;
+       *flags = '\0';
+
+       SPLAY_FOREACH(bd, key_bindings, &key_bindings) {
+               key = key_string_lookup_key(bd->key & ~KEYC_PREFIX);
+               if (key == NULL)
+                       continue;
+
+               keywidth = strlen(key);
+               if (!(bd->key & KEYC_PREFIX)) {
+                       if (bd->can_repeat)
+                               keywidth += 4;
+                       else
+                               keywidth += 3;
+               } else if (bd->can_repeat)
+                       keywidth += 3;
+               if (keywidth > width)
+                       width = keywidth;
+       }
+
+       SPLAY_FOREACH(bd, key_bindings, &key_bindings) {
+               key = key_string_lookup_key(bd->key & ~KEYC_PREFIX);
+               if (key == NULL)
+                       continue;
+
+               if (!(bd->key & KEYC_PREFIX)) {
+                       if (bd->can_repeat)
+                               xsnprintf(flags, sizeof flags, "-rn ");
+                       else
+                               xsnprintf(flags, sizeof flags, "-n ");
+               } else if (bd->can_repeat)
+                       xsnprintf(flags, sizeof flags, "-r ");
+
+               used = xsnprintf(tmp, sizeof tmp, "%s%*s ",
+                   flags, (int) (width - strlen(flags)), key);
+               if (used >= sizeof tmp)
+                       continue;
+
+               cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used);
+               ctx->print(ctx, "bind-key %s", tmp);
+       }
+
+       return (0);
+}
+
+int
+cmd_list_keys_table(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args                     *args = self->args;
+       const char                      *tablename;
+       const struct mode_key_table     *mtab;
+       struct mode_key_binding         *mbind;
+       const char                      *key, *cmdstr, *mode;
+       int                              width, keywidth, any_mode;
+
+       tablename = args_get(args, 't');
+       if ((mtab = mode_key_findtable(tablename)) == NULL) {
+               ctx->error(ctx, "unknown key table: %s", tablename);
+               return (-1);
+       }
+
+       width = 0;
+       any_mode = 0;
+       SPLAY_FOREACH(mbind, mode_key_tree, mtab->tree) {
+               key = key_string_lookup_key(mbind->key);
+               if (key == NULL)
+                       continue;
+
+               if (mbind->mode != 0)
+                       any_mode = 1;
+
+               keywidth = strlen(key);
+               if (keywidth > width)
+                       width = keywidth;
+       }
+
+       SPLAY_FOREACH(mbind, mode_key_tree, mtab->tree) {
+               key = key_string_lookup_key(mbind->key);
+               if (key == NULL)
+                       continue;
+
+               mode = "";
+               if (mbind->mode != 0)
+                       mode = "c";
+               cmdstr = mode_key_tostring(mtab->cmdstr, mbind->cmd);
+               if (cmdstr != NULL) {
+                       ctx->print(ctx, "bind-key -%st %s%s %*s %s",
+                           mode, any_mode && *mode == '\0' ? " " : "",
+                           mtab->name, (int) width, key, cmdstr);
+               }
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-list-panes.c b/external/bsd/tmux/dist/cmd-list-panes.c
new file mode 100644 (file)
index 0000000..55e4cb8
--- /dev/null
@@ -0,0 +1,137 @@
+/* $Id: cmd-list-panes.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * List panes on given window.
+ */
+
+int    cmd_list_panes_exec(struct cmd *, struct cmd_ctx *);
+
+void   cmd_list_panes_server(struct cmd_ctx *);
+void   cmd_list_panes_session(struct session *, struct cmd_ctx *, int);
+void   cmd_list_panes_window(
+           struct session *, struct winlink *, struct cmd_ctx *, int);
+
+const struct cmd_entry cmd_list_panes_entry = {
+       "list-panes", "lsp",
+       "ast:", 0, 0,
+       "[-as] [-t target]",
+       0,
+       NULL,
+       NULL,
+       cmd_list_panes_exec
+};
+
+int
+cmd_list_panes_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct session  *s;
+       struct winlink  *wl;
+
+       if (args_has(args, 'a'))
+               cmd_list_panes_server(ctx);
+       else if (args_has(args, 's')) {
+               s = cmd_find_session(ctx, args_get(args, 't'), 0);
+               if (s == NULL)
+                       return (-1);
+               cmd_list_panes_session(s, ctx, 1);
+       } else {
+               wl = cmd_find_window(ctx, args_get(args, 't'), &s);
+               if (wl == NULL)
+                       return (-1);
+               cmd_list_panes_window(s, wl, ctx, 0);
+       }
+
+       return (0);
+}
+
+void
+cmd_list_panes_server(struct cmd_ctx *ctx)
+{
+       struct session  *s;
+
+       RB_FOREACH(s, sessions, &sessions)
+               cmd_list_panes_session(s, ctx, 2);
+}
+
+void
+cmd_list_panes_session(struct session *s, struct cmd_ctx *ctx, int type)
+{
+       struct winlink  *wl;
+
+       RB_FOREACH(wl, winlinks, &s->windows)
+               cmd_list_panes_window(s, wl, ctx, type);
+}
+
+void
+cmd_list_panes_window(
+    struct session *s, struct winlink *wl, struct cmd_ctx *ctx, int type)
+{
+       struct window_pane      *wp;
+       struct grid             *gd;
+       struct grid_line        *gl;
+       u_int                    i, n;
+       unsigned long long       size;
+
+       n = 0;
+       TAILQ_FOREACH(wp, &wl->window->panes, entry) {
+               gd = wp->base.grid;
+
+               size = 0;
+               for (i = 0; i < gd->hsize; i++) {
+                       gl = &gd->linedata[i];
+                       size += gl->cellsize * sizeof *gl->celldata;
+                       size += gl->utf8size * sizeof *gl->utf8data;
+               }
+               size += gd->hsize * sizeof *gd->linedata;
+
+               switch (type) {
+               case 0:
+                       ctx->print(ctx,
+                           "%u: [%ux%u] [history %u/%u, %llu bytes] %%%u%s%s",
+                           n, wp->sx, wp->sy, gd->hsize, gd->hlimit, size,
+                           wp->id, wp == wp->window->active ? " (active)" : "",
+                           wp->fd == -1 ? " (dead)" : "");
+                       break;
+               case 1:
+                       ctx->print(ctx,
+                           "%d.%u: [%ux%u] [history %u/%u, %llu bytes] "
+                           "%%%u%s%s", wl->idx,
+                           n, wp->sx, wp->sy, gd->hsize, gd->hlimit, size,
+                           wp->id, wp == wp->window->active ? " (active)" : "",
+                           wp->fd == -1 ? " (dead)" : "");
+                       break;
+               case 2:
+                       ctx->print(ctx,
+                           "%s:%d.%u: [%ux%u] [history %u/%u, %llu bytes] "
+                           "%%%u%s%s", s->name, wl->idx,
+                           n, wp->sx, wp->sy, gd->hsize, gd->hlimit, size,
+                           wp->id, wp == wp->window->active ? " (active)" : "",
+                           wp->fd == -1 ? " (dead)" : "");
+                       break;
+               }
+               n++;
+       }
+}
diff --git a/external/bsd/tmux/dist/cmd-list-sessions.c b/external/bsd/tmux/dist/cmd-list-sessions.c
new file mode 100644 (file)
index 0000000..7ae7412
--- /dev/null
@@ -0,0 +1,71 @@
+/* $Id: cmd-list-sessions.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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, 0,
+       "",
+       0,
+       NULL,
+       NULL,
+       cmd_list_sessions_exec
+};
+
+/* ARGSUSED */
+int
+cmd_list_sessions_exec(unused struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct session          *s;
+       struct session_group    *sg;
+       char                    *tim, tmp[64];
+       u_int                    idx;
+       time_t                   t;
+
+       RB_FOREACH(s, sessions, &sessions) {
+               sg = session_group_find(s);
+               if (sg == NULL)
+                       *tmp = '\0';
+               else {
+                       idx = session_group_index(sg);
+                       xsnprintf(tmp, sizeof tmp, " (group %u)", idx);
+               }
+
+               t = s->creation_time.tv_sec;
+               tim = ctime(&t);
+               *strchr(tim, '\n') = '\0';
+
+               ctx->print(ctx, "%s: %u windows (created %s) [%ux%u]%s%s",
+                   s->name, winlink_count(&s->windows), tim, s->sx, s->sy,
+                   tmp, s->flags & SESSION_UNATTACHED ? "" : " (attached)");
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-list-windows.c b/external/bsd/tmux/dist/cmd-list-windows.c
new file mode 100644 (file)
index 0000000..d2aa4c2
--- /dev/null
@@ -0,0 +1,92 @@
+/* $Id: cmd-list-windows.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 *);
+
+void   cmd_list_windows_server(struct cmd_ctx *);
+void   cmd_list_windows_session(struct session *, struct cmd_ctx *, int);
+
+const struct cmd_entry cmd_list_windows_entry = {
+       "list-windows", "lsw",
+       "at:", 0, 0,
+       "[-a] " CMD_TARGET_SESSION_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_list_windows_exec
+};
+
+int
+cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct session  *s;
+
+       if (args_has(args, 'a'))
+               cmd_list_windows_server(ctx);
+       else {
+               s = cmd_find_session(ctx, args_get(args, 't'), 0);
+               if (s == NULL)
+                       return (-1);
+               cmd_list_windows_session(s, ctx, 0);
+       }
+
+       return (0);
+}
+
+void
+cmd_list_windows_server(struct cmd_ctx *ctx)
+{
+       struct session  *s;
+
+       RB_FOREACH(s, sessions, &sessions)
+               cmd_list_windows_session(s, ctx, 1);
+}
+
+void
+cmd_list_windows_session(struct session *s, struct cmd_ctx *ctx, int type)
+{
+       struct winlink  *wl;
+       char            *layout;
+
+       RB_FOREACH(wl, winlinks, &s->windows) {
+               layout = layout_dump(wl->window);
+               if (type) {
+                       ctx->print(ctx, "%s:%d: %s [%ux%u] [layout %s]%s",
+                           s->name, wl->idx, wl->window->name, wl->window->sx,
+                           wl->window->sy, layout,
+                           wl == s->curw ? " (active)" : "");
+               } else {
+                       ctx->print(ctx, "%d: %s [%ux%u] [layout %s]%s",
+                           wl->idx, wl->window->name, wl->window->sx,
+                           wl->window->sy, layout,
+                           wl == s->curw ? " (active)" : "");
+               }
+               xfree(layout);
+       }
+}
diff --git a/external/bsd/tmux/dist/cmd-list.c b/external/bsd/tmux/dist/cmd-list.c
new file mode 100644 (file)
index 0000000..06bacee
--- /dev/null
@@ -0,0 +1,150 @@
+/* $Id: cmd-list.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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           **copy_argv, **new_argv;
+
+       copy_argv = cmd_copy_argv(argc, argv);
+
+       cmdlist = xmalloc(sizeof *cmdlist);
+       cmdlist->references = 1;
+       TAILQ_INIT(&cmdlist->list);
+
+       lastsplit = 0;
+       for (i = 0; i < argc; i++) {
+               arglen = strlen(copy_argv[i]);
+               if (arglen == 0 || copy_argv[i][arglen - 1] != ';')
+                       continue;
+               copy_argv[i][arglen - 1] = '\0';
+
+               if (arglen > 1 && copy_argv[i][arglen - 2] == '\\') {
+                       copy_argv[i][arglen - 2] = ';';
+                       continue;
+               }
+
+               new_argc = i - lastsplit;
+               new_argv = copy_argv + lastsplit;
+               if (arglen != 1)
+                       new_argc++;
+
+               cmd = cmd_parse(new_argc, new_argv, cause);
+               if (cmd == NULL)
+                       goto bad;
+               TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
+
+               lastsplit = i + 1;
+       }
+
+       if (lastsplit != argc) {
+               cmd = cmd_parse(argc - lastsplit, copy_argv + lastsplit, cause);
+               if (cmd == NULL)
+                       goto bad;
+               TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
+       }
+
+       cmd_free_argv(argc, copy_argv);
+       return (cmdlist);
+
+bad:
+       cmd_list_free(cmdlist);
+       cmd_free_argv(argc, copy_argv);
+       return (NULL);
+}
+
+int
+cmd_list_exec(struct cmd_list *cmdlist, struct cmd_ctx *ctx)
+{
+       struct cmd      *cmd;
+       int              n, retval;
+
+       retval = 0;
+       TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
+               if ((n = cmd_exec(cmd, ctx)) == -1)
+                       return (-1);
+
+               /*
+                * A 1 return value means the command client is being attached
+                * (sent MSG_READY).
+                */
+               if (n == 1) {
+                       retval = 1;
+
+                       /*
+                        * The command client has been attached, so mangle the
+                        * context to treat any following commands as if they
+                        * were called from inside.
+                        */
+                       if (ctx->curclient == NULL) {
+                               ctx->curclient = ctx->cmdclient;
+                               ctx->cmdclient = NULL;
+
+                               ctx->error = key_bindings_error;
+                               ctx->print = key_bindings_print;
+                               ctx->info = key_bindings_info;
+                       }
+               }
+       }
+       return (retval);
+}
+
+void
+cmd_list_free(struct cmd_list *cmdlist)
+{
+       struct cmd      *cmd;
+
+       if (--cmdlist->references != 0)
+               return;
+
+       while (!TAILQ_EMPTY(&cmdlist->list)) {
+               cmd = TAILQ_FIRST(&cmdlist->list);
+               TAILQ_REMOVE(&cmdlist->list, 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->list, 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/external/bsd/tmux/dist/cmd-load-buffer.c b/external/bsd/tmux/dist/cmd-load-buffer.c
new file mode 100644 (file)
index 0000000..26f032c
--- /dev/null
@@ -0,0 +1,174 @@
+/* $Id: cmd-load-buffer.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 <errno.h>
+#include <stdio.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 *);
+void   cmd_load_buffer_callback(struct client *, void *);
+
+const struct cmd_entry cmd_load_buffer_entry = {
+       "load-buffer", "loadb",
+       "b:", 1, 1,
+       CMD_BUFFER_USAGE " path",
+       0,
+       NULL,
+       NULL,
+       cmd_load_buffer_exec
+};
+
+int
+cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct client   *c = ctx->cmdclient;
+       FILE            *f;
+       const char      *path;
+       char            *pdata, *new_pdata, *cause;
+       size_t           psize;
+       u_int            limit;
+       int              ch, buffer;
+       int             *buffer_ptr;
+
+       if (!args_has(args, 'b'))
+               buffer = -1;
+       else {
+               buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
+               if (cause != NULL) {
+                       ctx->error(ctx, "buffer %s", cause);
+                       xfree(cause);
+                       return (-1);
+               }
+       }
+
+       path = args->argv[0];
+       if (strcmp(path, "-") == 0) {
+               if (c == NULL) {
+                       ctx->error(ctx, "%s: can't read from stdin", path);
+                       return (-1);
+               }
+               if (c->flags & CLIENT_TERMINAL) {
+                       ctx->error(ctx, "%s: stdin is a tty", path);
+                       return (-1);
+               }
+               if (c->stdin_fd == -1) {
+                       ctx->error(ctx, "%s: can't read from stdin", path);
+                       return (-1);
+               }
+
+               buffer_ptr = xmalloc(sizeof *buffer_ptr);
+               *buffer_ptr = buffer;
+
+               c->stdin_data = buffer_ptr;
+               c->stdin_callback = cmd_load_buffer_callback;
+
+               c->references++;
+               bufferevent_enable(c->stdin_event, EV_READ);
+               return (1);
+       }
+
+       if ((f = fopen(path, "rb")) == NULL) {
+               ctx->error(ctx, "%s: %s", path, strerror(errno));
+               return (-1);
+       }
+
+       pdata = NULL;
+       psize = 0;
+       while ((ch = getc(f)) != EOF) {
+               /* Do not let the server die due to memory exhaustion. */
+               if ((new_pdata = realloc(pdata, psize + 2)) == NULL) {
+                       ctx->error(ctx, "realloc error: %s", strerror(errno));
+                       goto error;
+               }
+               pdata = new_pdata;
+               pdata[psize++] = ch;
+       }
+       if (ferror(f)) {
+               ctx->error(ctx, "%s: read error", path);
+               goto error;
+       }
+       if (pdata != NULL)
+               pdata[psize] = '\0';
+
+       fclose(f);
+
+       limit = options_get_number(&global_options, "buffer-limit");
+       if (buffer == -1) {
+               paste_add(&global_buffers, pdata, psize, limit);
+               return (0);
+       }
+       if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) {
+               ctx->error(ctx, "no buffer %d", buffer);
+               return (-1);
+       }
+
+       return (0);
+
+error:
+       if (pdata != NULL)
+               xfree(pdata);
+       if (f != NULL)
+               fclose(f);
+       return (-1);
+}
+
+void
+cmd_load_buffer_callback(struct client *c, void *data)
+{
+       int     *buffer = data;
+       char    *pdata;
+       size_t   psize;
+       u_int    limit;
+
+       /*
+        * Event callback has already checked client is not dead and reduced
+        * its reference count. But tell it to exit.
+        */
+       c->flags |= CLIENT_EXIT;
+
+       psize = EVBUFFER_LENGTH(c->stdin_event->input);
+       if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) {
+               xfree(data);
+               return;
+       }
+       bufferevent_read(c->stdin_event, pdata, psize);
+       pdata[psize] = '\0';
+
+       limit = options_get_number(&global_options, "buffer-limit");
+       if (*buffer == -1)
+               paste_add(&global_buffers, pdata, psize, limit);
+       else if (paste_replace(&global_buffers, *buffer, pdata, psize) != 0) {
+               /* No context so can't use server_client_msg_error. */
+               evbuffer_add_printf(
+                   c->stderr_event->output, "no buffer %d\n", *buffer);
+               bufferevent_enable(c->stderr_event, EV_WRITE);
+       }
+
+       xfree(data);
+}
diff --git a/external/bsd/tmux/dist/cmd-lock-server.c b/external/bsd/tmux/dist/cmd-lock-server.c
new file mode 100644 (file)
index 0000000..3a1275b
--- /dev/null
@@ -0,0 +1,85 @@
+/* $Id: cmd-lock-server.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 commands.
+ */
+
+int    cmd_lock_server_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_lock_server_entry = {
+       "lock-server", "lock",
+       "", 0, 0,
+       "",
+       0,
+       NULL,
+       NULL,
+       cmd_lock_server_exec
+};
+
+const struct cmd_entry cmd_lock_session_entry = {
+       "lock-session", "locks",
+       "t:", 0, 0,
+       CMD_TARGET_SESSION_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_lock_server_exec
+};
+
+const struct cmd_entry cmd_lock_client_entry = {
+       "lock-client", "lockc",
+       "t:", 0, 0,
+       CMD_TARGET_CLIENT_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_lock_server_exec
+};
+
+/* ARGSUSED */
+int
+cmd_lock_server_exec(struct cmd *self, unused struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct client   *c;
+       struct session  *s;
+
+       if (self->entry == &cmd_lock_server_entry)
+               server_lock();
+       else if (self->entry == &cmd_lock_session_entry) {
+               if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL)
+                       return (-1);
+               server_lock_session(s);
+       } else {
+               if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL)
+                       return (-1);
+               server_lock_client(c);
+       }
+       recalculate_sizes();
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-move-window.c b/external/bsd/tmux/dist/cmd-move-window.c
new file mode 100644 (file)
index 0000000..e36ddd7
--- /dev/null
@@ -0,0 +1,66 @@
+/* $Id: cmd-move-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "dks:t:", 0, 0,
+       "[-dk] " CMD_SRCDST_WINDOW_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_move_window_exec
+};
+
+int
+cmd_move_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct session  *src, *dst;
+       struct winlink  *wl;
+       char            *cause;
+       int              idx, kflag, dflag;
+
+       if ((wl = cmd_find_window(ctx, args_get(args, 's'), &src)) == NULL)
+               return (-1);
+       if ((idx = cmd_find_index(ctx, args_get(args, 't'), &dst)) == -2)
+               return (-1);
+
+       kflag = args_has(self->args, 'k');
+       dflag = args_has(self->args, 'd');
+       if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) {
+               ctx->error(ctx, "can't move window: %s", cause);
+               xfree(cause);
+               return (-1);
+       }
+       server_unlink_window(src, wl);
+       recalculate_sizes();
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-new-session.c b/external/bsd/tmux/dist/cmd-new-session.c
new file mode 100644 (file)
index 0000000..e18ba13
--- /dev/null
@@ -0,0 +1,277 @@
+/* $Id: cmd-new-session.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Create a new session and attach to the current terminal unless -d is given.
+ */
+
+int    cmd_new_session_check(struct args *);
+int    cmd_new_session_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_new_session_entry = {
+       "new-session", "new",
+       "dn:s:t:x:y:", 0, 1,
+       "[-d] [-n window-name] [-s session-name] [-t target-session] "
+       "[-x width] [-y height] [command]",
+       CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON,
+       NULL,
+       cmd_new_session_check,
+       cmd_new_session_exec
+};
+
+int
+cmd_new_session_check(struct args *args)
+{
+       if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n')))
+               return (-1);
+       return (0);
+}
+
+int
+cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct session          *s, *old_s, *groupwith;
+       struct window           *w;
+       struct window_pane      *wp;
+       struct environ           env;
+       struct termios           tio, *tiop;
+       struct passwd           *pw;
+       const char              *newname, *target, *update, *cwd, *errstr;
+       char                    *overrides, *cmd, *cause;
+       int                      detached, idx;
+       u_int                    sx, sy, i;
+
+       newname = args_get(args, 's');
+       if (newname != NULL) {
+               if (!session_check_name(newname)) {
+                       ctx->error(ctx, "bad session name: %s", newname);
+                       return (-1);
+               }
+               if (session_find(newname) != NULL) {
+                       ctx->error(ctx, "duplicate session: %s", newname);
+                       return (-1);
+               }
+       }
+
+       target = args_get(args, 't');
+       if (target != NULL) {
+               groupwith = cmd_find_session(ctx, target, 0);
+               if (groupwith == NULL)
+                       return (-1);
+       } else
+               groupwith = NULL;
+
+       /*
+        * There are three cases:
+        *
+        * 1. If cmdclient is non-NULL, new-session has been called from the
+        *    command-line - cmdclient is to become a new attached, interactive
+        *    client. Unless -d is given, the terminal must be opened and then
+        *    the client sent MSG_READY.
+        *
+        * 2. If cmdclient is NULL, new-session has been called from an
+        *    existing client (such as a key binding).
+        *
+        * 3. Both are NULL, the command was in the configuration file. Treat
+        *    this as if -d was given even if it was not.
+        *
+        * In all cases, a new additional session needs to be created and
+        * (unless -d) set as the current session for the client.
+        */
+
+       /* Set -d if no client. */
+       detached = args_has(args, 'd');
+       if (ctx->cmdclient == NULL && ctx->curclient == NULL)
+               detached = 1;
+
+       /*
+        * Save the termios settings, part of which is used for new windows in
+        * this session.
+        *
+        * This is read again with tcgetattr() rather than using tty.tio as if
+        * detached, tty_open won't be called. Because of this, it must be done
+        * before opening the terminal as that calls tcsetattr() to prepare for
+        * tmux taking over.
+        */
+       if (ctx->cmdclient != NULL && ctx->cmdclient->tty.fd != -1) {
+               if (tcgetattr(ctx->cmdclient->tty.fd, &tio) != 0)
+                       fatal("tcgetattr failed");
+               tiop = &tio;
+       } else
+               tiop = NULL;
+
+       /* Open the terminal if necessary. */
+       if (!detached && ctx->cmdclient != NULL) {
+               if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) {
+                       ctx->error(ctx, "not a terminal");
+                       return (-1);
+               }
+
+               overrides =
+                   options_get_string(&global_s_options, "terminal-overrides");
+               if (tty_open(&ctx->cmdclient->tty, overrides, &cause) != 0) {
+                       ctx->error(ctx, "open terminal failed: %s", cause);
+                       xfree(cause);
+                       return (-1);
+               }
+       }
+
+       /* Get the new session working directory. */
+       if (ctx->cmdclient != NULL && ctx->cmdclient->cwd != NULL)
+               cwd = ctx->cmdclient->cwd;
+       else {
+               pw = getpwuid(getuid());
+               if (pw->pw_dir != NULL && *pw->pw_dir != '\0')
+                       cwd = pw->pw_dir;
+               else
+                       cwd = "/";
+       }
+
+       /* Find new session size. */
+       if (detached) {
+               sx = 80;
+               sy = 24;
+               if (args_has(args, 'x')) {
+                       sx = strtonum(
+                           args_get(args, 'x'), 1, USHRT_MAX, &errstr);
+                       if (errstr != NULL) {
+                               ctx->error(ctx, "width %s", errstr);
+                               return (-1);
+                       }
+               }
+               if (args_has(args, 'y')) {
+                       sy = strtonum(
+                           args_get(args, 'y'), 1, USHRT_MAX, &errstr);
+                       if (errstr != NULL) {
+                               ctx->error(ctx, "height %s", errstr);
+                               return (-1);
+                       }
+               }
+       } else if (ctx->cmdclient != NULL) {
+               sx = ctx->cmdclient->tty.sx;
+               sy = ctx->cmdclient->tty.sy;
+       } else {
+               sx = ctx->curclient->tty.sx;
+               sy = ctx->curclient->tty.sy;
+       }
+       if (sy > 0 && options_get_number(&global_s_options, "status"))
+               sy--;
+       if (sx == 0)
+               sx = 1;
+       if (sy == 0)
+               sy = 1;
+
+       /* Figure out the command for the new window. */
+       if (target != NULL)
+               cmd = NULL;
+       else if (args->argc != 0)
+               cmd = args->argv[0];
+       else
+               cmd = options_get_string(&global_s_options, "default-command");
+
+       /* Construct the environment. */
+       environ_init(&env);
+       update = options_get_string(&global_s_options, "update-environment");
+       if (ctx->cmdclient != NULL)
+               environ_update(update, &ctx->cmdclient->environ, &env);
+
+       /* Create the new session. */
+       idx = -1 - options_get_number(&global_s_options, "base-index");
+       s = session_create(newname, cmd, cwd, &env, tiop, idx, sx, sy, &cause);
+       if (s == NULL) {
+               ctx->error(ctx, "create session failed: %s", cause);
+               xfree(cause);
+               return (-1);
+       }
+       environ_free(&env);
+
+       /* Set the initial window name if one given. */
+       if (cmd != NULL && args_has(args, 'n')) {
+               w = s->curw->window;
+
+               xfree(w->name);
+               w->name = xstrdup(args_get(args, 'n'));
+
+               options_set_number(&w->options, "automatic-rename", 0);
+       }
+
+       /*
+        * If a target session is given, this is to be part of a session group,
+        * so add it to the group and synchronize.
+        */
+       if (groupwith != NULL) {
+               session_group_add(groupwith, s);
+               session_group_synchronize_to(s);
+               session_select(s, RB_ROOT(&s->windows)->idx);
+       }
+
+       /*
+        * Set the client to the new session. If a command client exists, it is
+        * taking this session and needs to get MSG_READY and stay around.
+        */
+       if (!detached) {
+               if (ctx->cmdclient != NULL) {
+                       server_write_client(ctx->cmdclient, MSG_READY, NULL, 0);
+
+                       old_s = ctx->cmdclient->session;
+                       if (old_s != NULL)
+                               ctx->cmdclient->last_session = old_s;
+                       ctx->cmdclient->session = s;
+                       session_update_activity(s);
+                       server_redraw_client(ctx->cmdclient);
+               } else {
+                       old_s = ctx->curclient->session;
+                       if (old_s != NULL)
+                               ctx->curclient->last_session = old_s;
+                       ctx->curclient->session = s;
+                       session_update_activity(s);
+                       server_redraw_client(ctx->curclient);
+               }
+       }
+       recalculate_sizes();
+       server_update_socket();
+
+       /*
+        * If there are still configuration file errors to display, put the new
+        * session's current window into more mode and display them now.
+        */
+       if (cfg_finished && !ARRAY_EMPTY(&cfg_causes)) {
+               wp = s->curw->window->active;
+               window_pane_set_mode(wp, &window_copy_mode);
+               window_copy_init_for_output(wp);
+               for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) {
+                       cause = ARRAY_ITEM(&cfg_causes, i);
+                       window_copy_add(wp, "%s", cause);
+                       xfree(cause);
+               }
+               ARRAY_FREE(&cfg_causes);
+       }
+
+       return (!detached);     /* 1 means don't tell command client to exit */
+}
diff --git a/external/bsd/tmux/dist/cmd-new-window.c b/external/bsd/tmux/dist/cmd-new-window.c
new file mode 100644 (file)
index 0000000..3fdc676
--- /dev/null
@@ -0,0 +1,126 @@
+/* $Id: cmd-new-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_new_window_entry = {
+       "new-window", "neww",
+       "adkn:Pt:", 0, 1,
+       "[-adk] [-n window-name] [-t target-window] [command]",
+       0,
+       NULL,
+       NULL,
+       cmd_new_window_exec
+};
+
+int
+cmd_new_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct session  *s;
+       struct winlink  *wl;
+       char            *cmd, *cwd, *cause;
+       int              idx, last, detached;
+
+       if (args_has(args, 'a')) {
+               wl = cmd_find_window(ctx, args_get(args, 't'), &s);
+               if (wl == NULL)
+                       return (-1);
+               idx = wl->idx + 1;
+
+               /* Find the next free index. */
+               for (last = idx; last < INT_MAX; last++) {
+                       if (winlink_find_by_index(&s->windows, last) == NULL)
+                               break;
+               }
+               if (last == INT_MAX) {
+                       ctx->error(ctx, "no free window indexes");
+                       return (-1);
+               }
+
+               /* Move everything from last - 1 to idx up a bit. */
+               for (; last > idx; last--) {
+                       wl = winlink_find_by_index(&s->windows, last - 1);
+                       server_link_window(s, wl, s, last, 0, 0, NULL);
+                       server_unlink_window(s, wl);
+               }
+       } else {
+               if ((idx = cmd_find_index(ctx, args_get(args, 't'), &s)) == -2)
+                       return (-1);
+       }
+       detached = args_has(args, 'd');
+
+       wl = NULL;
+       if (idx != -1)
+               wl = winlink_find_by_index(&s->windows, idx);
+       if (wl != NULL && args_has(args, 'k')) {
+               /*
+                * Can't use session_detach as it will destroy session if this
+                * makes it empty.
+                */
+               wl->flags &= ~WINLINK_ALERTFLAGS;
+               winlink_stack_remove(&s->lastw, wl);
+               winlink_remove(&s->windows, wl);
+
+               /* Force select/redraw if current. */
+               if (wl == s->curw) {
+                       detached = 0;
+                       s->curw = NULL;
+               }
+       }
+
+       if (args->argc == 0)
+               cmd = options_get_string(&s->options, "default-command");
+       else
+               cmd = args->argv[0];
+       cwd = options_get_string(&s->options, "default-path");
+       if (*cwd == '\0') {
+               if (ctx->cmdclient != NULL && ctx->cmdclient->cwd != NULL)
+                       cwd = ctx->cmdclient->cwd;
+               else
+                       cwd = s->cwd;
+       }
+
+       if (idx == -1)
+               idx = -1 - options_get_number(&s->options, "base-index");
+       wl = session_new(s, args_get(args, 'n'), cmd, cwd, idx, &cause);
+       if (wl == NULL) {
+               ctx->error(ctx, "create window failed: %s", cause);
+               xfree(cause);
+               return (-1);
+       }
+       if (!detached) {
+               session_select(s, wl->idx);
+               server_redraw_session_group(s);
+       } else
+               server_status_session_group(s);
+
+       if (args_has(args, 'P'))
+               ctx->print(ctx, "%s:%u", s->name, wl->idx);
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-paste-buffer.c b/external/bsd/tmux/dist/cmd-paste-buffer.c
new file mode 100644 (file)
index 0000000..37fbaf2
--- /dev/null
@@ -0,0 +1,121 @@
+/* $Id: cmd-paste-buffer.c,v 1.3 2011/08/17 18:48:35 jmmv 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"
+
+/*
+ * Paste paste buffer if present.
+ */
+
+int    cmd_paste_buffer_exec(struct cmd *, struct cmd_ctx *);
+
+void   cmd_paste_buffer_filter(
+           struct window_pane *, const char *, size_t, const char *);
+
+const struct cmd_entry cmd_paste_buffer_entry = {
+       "paste-buffer", "pasteb",
+       "db:rs:t:", 0, 0,
+       "[-dr] [-s separator] [-b buffer-index] [-t target-pane]",
+       0,
+       NULL,
+       NULL,
+       cmd_paste_buffer_exec
+};
+
+int
+cmd_paste_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct window_pane      *wp;
+       struct session          *s;
+       struct paste_buffer     *pb;
+       const char              *sepstr;
+       char                    *cause;
+       int                      buffer;
+
+       if (cmd_find_pane(ctx, args_get(args, 't'), &s, &wp) == NULL)
+               return (-1);
+
+       if (!args_has(args, 'b'))
+               buffer = -1;
+       else {
+               buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
+               if (cause != NULL) {
+                       ctx->error(ctx, "buffer %s", cause);
+                       xfree(cause);
+                       return (-1);
+               }
+       }
+
+       if (buffer == -1)
+               pb = paste_get_top(&global_buffers);
+       else {
+               pb = paste_get_index(&global_buffers, buffer);
+               if (pb == NULL) {
+                       ctx->error(ctx, "no buffer %d", buffer);
+                       return (-1);
+               }
+       }
+
+       if (pb != NULL) {
+               sepstr = args_get(args, 's');
+               if (sepstr == NULL) {
+                       if (args_has(args, 'r'))
+                               sepstr = "\n";
+                       else
+                               sepstr = "\r";
+               }
+               cmd_paste_buffer_filter(wp, pb->data, pb->size, sepstr);
+       }
+
+       /* Delete the buffer if -d. */
+       if (args_has(args, 'd')) {
+               if (buffer == -1)
+                       paste_free_top(&global_buffers);
+               else
+                       paste_free_index(&global_buffers, buffer);
+       }
+
+       return (0);
+}
+
+/* Add bytes to a buffer and filter '\n' according to separator. */
+void
+cmd_paste_buffer_filter(
+    struct window_pane *wp, const char *data, size_t size, const char *sep)
+{
+       const char      *end = data + size;
+       const char      *lf;
+       size_t           seplen;
+
+       seplen = strlen(sep);
+       while ((lf = memchr(data, '\n', end - data)) != NULL) {
+               if (lf != data)
+                       bufferevent_write(wp->event, data, lf - data);
+               bufferevent_write(wp->event, sep, seplen);
+               data = lf + 1;
+       }
+
+       if (end != data)
+               bufferevent_write(wp->event, data, end - data);
+}
diff --git a/external/bsd/tmux/dist/cmd-pipe-pane.c b/external/bsd/tmux/dist/cmd-pipe-pane.c
new file mode 100644 (file)
index 0000000..efdc082
--- /dev/null
@@ -0,0 +1,145 @@
+/* $Id: cmd-pipe-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 <sys/socket.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Open pipe to redirect pane output. If already open, close first.
+ */
+
+int    cmd_pipe_pane_exec(struct cmd *, struct cmd_ctx *);
+
+void   cmd_pipe_pane_error_callback(struct bufferevent *, short, void *);
+
+const struct cmd_entry cmd_pipe_pane_entry = {
+       "pipe-pane", "pipep",
+       "ot:", 0, 1,
+       CMD_TARGET_PANE_USAGE "[-o] [command]",
+       0,
+       NULL,
+       NULL,
+       cmd_pipe_pane_exec
+};
+
+int
+cmd_pipe_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct client           *c;
+       struct window_pane      *wp;
+       char                    *command;
+       int                      old_fd, pipe_fd[2], null_fd;
+
+       if ((c = cmd_find_client(ctx, NULL)) == NULL)
+               return (-1);
+
+       if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL)
+               return (-1);
+
+       /* Destroy the old pipe. */
+       old_fd = wp->pipe_fd;
+       if (wp->pipe_fd != -1) {
+               bufferevent_free(wp->pipe_event);
+               close(wp->pipe_fd);
+               wp->pipe_fd = -1;
+       }
+
+       /* If no pipe command, that is enough. */
+       if (args->argc == 0 || *args->argv[0] == '\0')
+               return (0);
+
+       /*
+        * With -o, only open the new pipe if there was no previous one. This
+        * allows a pipe to be toggled with a single key, for example:
+        *
+        *      bind ^p pipep -o 'cat >>~/output'
+        */
+       if (args_has(self->args, 'o') && old_fd != -1)
+               return (0);
+
+       /* Open the new pipe. */
+       if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_fd) != 0) {
+               ctx->error(ctx, "socketpair error: %s", strerror(errno));
+               return (-1);
+       }
+
+       /* Fork the child. */
+       switch (fork()) {
+       case -1:
+               ctx->error(ctx, "fork error: %s", strerror(errno));
+               return (-1);
+       case 0:
+               /* Child process. */
+               close(pipe_fd[0]);
+               clear_signals(1);
+
+               if (dup2(pipe_fd[1], STDIN_FILENO) == -1)
+                       _exit(1);
+               if (pipe_fd[1] != STDIN_FILENO)
+                       close(pipe_fd[1]);
+
+               null_fd = open(_PATH_DEVNULL, O_WRONLY, 0);
+               if (dup2(null_fd, STDOUT_FILENO) == -1)
+                       _exit(1);
+               if (dup2(null_fd, STDERR_FILENO) == -1)
+                       _exit(1);
+               if (null_fd != STDOUT_FILENO && null_fd != STDERR_FILENO)
+                       close(null_fd);
+
+               closefrom(STDERR_FILENO + 1);
+
+               command = status_replace(
+                   c, NULL, NULL, NULL, args->argv[0], time(NULL), 0);
+               execl(_PATH_BSHELL, "sh", "-c", command, (char *) NULL);
+               _exit(1);
+       default:
+               /* Parent process. */
+               close(pipe_fd[1]);
+
+               wp->pipe_fd = pipe_fd[0];
+               wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
+
+               wp->pipe_event = bufferevent_new(wp->pipe_fd,
+                   NULL, NULL, cmd_pipe_pane_error_callback, wp);
+               bufferevent_enable(wp->pipe_event, EV_WRITE);
+
+               setblocking(wp->pipe_fd, 0);
+               return (0);
+       }
+}
+
+/* ARGSUSED */
+void
+cmd_pipe_pane_error_callback(
+    unused struct bufferevent *bufev, unused short what, void *data)
+{
+       struct window_pane      *wp = data;
+
+       bufferevent_free(wp->pipe_event);
+       close(wp->pipe_fd);
+       wp->pipe_fd = -1;
+}
diff --git a/external/bsd/tmux/dist/cmd-refresh-client.c b/external/bsd/tmux/dist/cmd-refresh-client.c
new file mode 100644 (file)
index 0000000..2b84263
--- /dev/null
@@ -0,0 +1,51 @@
+/* $Id: cmd-refresh-client.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "t:", 0, 0,
+       CMD_TARGET_CLIENT_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_refresh_client_exec
+};
+
+int
+cmd_refresh_client_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct client   *c;
+
+       if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL)
+               return (-1);
+
+       server_redraw_client(c);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-rename-session.c b/external/bsd/tmux/dist/cmd-rename-session.c
new file mode 100644 (file)
index 0000000..7c19b65
--- /dev/null
@@ -0,0 +1,69 @@
+/* $Id: cmd-rename-session.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "t:", 1, 1,
+       CMD_TARGET_SESSION_USAGE " new-name",
+       0,
+       NULL,
+       NULL,
+       cmd_rename_session_exec
+};
+
+int
+cmd_rename_session_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct session  *s;
+       const char      *newname;
+
+       newname = args->argv[0];
+       if (!session_check_name(newname)) {
+               ctx->error(ctx, "bad session name: %s", newname);
+               return (-1);
+       }
+       if (session_find(newname) != NULL) {
+               ctx->error(ctx, "duplicate session: %s", newname);
+               return (-1);
+       }
+
+       if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL)
+               return (-1);
+
+       RB_REMOVE(sessions, &sessions, s);
+       xfree(s->name);
+       s->name = xstrdup(newname);
+       RB_INSERT(sessions, &sessions, s);
+
+       server_status_session(s);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-rename-window.c b/external/bsd/tmux/dist/cmd-rename-window.c
new file mode 100644 (file)
index 0000000..d9e6742
--- /dev/null
@@ -0,0 +1,58 @@
+/* $Id: cmd-rename-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "t:", 1, 1,
+       CMD_TARGET_WINDOW_USAGE " new-name",
+       0,
+       NULL,
+       NULL,
+       cmd_rename_window_exec
+};
+
+int
+cmd_rename_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct session  *s;
+       struct winlink  *wl;
+
+       if ((wl = cmd_find_window(ctx, args_get(args, 't'), &s)) == NULL)
+               return (-1);
+
+       xfree(wl->window->name);
+       wl->window->name = xstrdup(args->argv[0]);
+       options_set_number(&wl->window->options, "automatic-rename", 0);
+
+       server_status_window(wl->window);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-resize-pane.c b/external/bsd/tmux/dist/cmd-resize-pane.c
new file mode 100644 (file)
index 0000000..bb6b649
--- /dev/null
@@ -0,0 +1,117 @@
+/* $Id: cmd-resize-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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_key_binding(struct cmd *, int);
+int    cmd_resize_pane_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_resize_pane_entry = {
+       "resize-pane", "resizep",
+       "DLRt:U", 0, 1,
+       "[-DLRU] " CMD_TARGET_PANE_USAGE " [adjustment]",
+       0,
+       cmd_resize_pane_key_binding,
+       NULL,
+       cmd_resize_pane_exec
+};
+
+void
+cmd_resize_pane_key_binding(struct cmd *self, int key)
+{
+       switch (key) {
+       case KEYC_UP | KEYC_CTRL:
+               self->args = args_create(0);
+               args_set(self->args, 'U', NULL);
+               break;
+       case KEYC_DOWN | KEYC_CTRL:
+               self->args = args_create(0);
+               args_set(self->args, 'D', NULL);
+               break;
+       case KEYC_LEFT | KEYC_CTRL:
+               self->args = args_create(0);
+               args_set(self->args, 'L', NULL);
+               break;
+       case KEYC_RIGHT | KEYC_CTRL:
+               self->args = args_create(0);
+               args_set(self->args, 'R', NULL);
+               break;
+       case KEYC_UP | KEYC_ESCAPE:
+               self->args = args_create(1, "5");
+               args_set(self->args, 'U', NULL);
+               break;
+       case KEYC_DOWN | KEYC_ESCAPE:
+               self->args = args_create(1, "5");
+               args_set(self->args, 'D', NULL);
+               break;
+       case KEYC_LEFT | KEYC_ESCAPE:
+               self->args = args_create(1, "5");
+               args_set(self->args, 'L', NULL);
+               break;
+       case KEYC_RIGHT | KEYC_ESCAPE:
+               self->args = args_create(1, "5");
+               args_set(self->args, 'R', NULL);
+               break;
+       default:
+               self->args = args_create(0);
+               break;
+       }
+}
+
+int
+cmd_resize_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct winlink          *wl;
+       const char              *errstr;
+       struct window_pane      *wp;
+       u_int                    adjust;
+
+       if ((wl = cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp)) == NULL)
+               return (-1);
+
+       if (args->argc == 0)
+               adjust = 1;
+       else {
+               adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr);
+               if (errstr != NULL) {
+                       ctx->error(ctx, "adjustment %s", errstr);
+                       return (-1);
+               }
+       }
+
+       if (args_has(self->args, 'L'))
+               layout_resize_pane(wp, LAYOUT_LEFTRIGHT, -adjust);
+       else if (args_has(self->args, 'R'))
+               layout_resize_pane(wp, LAYOUT_LEFTRIGHT, adjust);
+       else if (args_has(self->args, 'U'))
+               layout_resize_pane(wp, LAYOUT_TOPBOTTOM, -adjust);
+       else if (args_has(self->args, 'D'))
+               layout_resize_pane(wp, LAYOUT_TOPBOTTOM, adjust);
+       server_redraw_window(wl->window);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-respawn-pane.c b/external/bsd/tmux/dist/cmd-respawn-pane.c
new file mode 100644 (file)
index 0000000..c76878e
--- /dev/null
@@ -0,0 +1,88 @@
+/* $Id: cmd-respawn-pane.c,v 1.1.1.1 2011/08/17 18:40:04 jmmv Exp $ */
+
+/*
+ * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
+ * Copyright (c) 2011 Marcel P. Partap <mpartap@gmx.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 pane (restart the command). Kill existing if -k given.
+ */
+
+int    cmd_respawn_pane_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_respawn_pane_entry = {
+       "respawn-pane", "respawnp",
+       "kt:", 0, 1,
+       "[-k] " CMD_TARGET_PANE_USAGE " [command]",
+       0,
+       NULL,
+       NULL,
+       cmd_respawn_pane_exec
+};
+
+int
+cmd_respawn_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct winlink          *wl;
+       struct window           *w;
+       struct window_pane      *wp;
+       struct session          *s;
+       struct environ           env;
+       const char              *cmd;
+       char                    *cause;
+
+       if ((wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp)) == NULL)
+               return (-1);
+       w = wl->window;
+
+       if (!args_has(self->args, 'k') && wp->fd != -1) {
+               ctx->error(ctx, "pane still active: %s:%u.%u",
+                   s->name, wl->idx, window_pane_index(w, wp));
+               return (-1);
+       }
+
+       environ_init(&env);
+       environ_copy(&global_environ, &env);
+       environ_copy(&s->environ, &env);
+       server_fill_environ(s, &env);
+
+       window_pane_reset_mode(wp);
+       screen_reinit(&wp->base);
+       input_init(wp);
+
+       if (args->argc != 0)
+               cmd = args->argv[0];
+       else
+               cmd = NULL;
+       if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) {
+               ctx->error(ctx, "respawn pane failed: %s", cause);
+               xfree(cause);
+               environ_free(&env);
+               return (-1);
+       }
+       wp->flags |= PANE_REDRAW;
+       server_status_window(w);
+
+       environ_free(&env);
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-respawn-window.c b/external/bsd/tmux/dist/cmd-respawn-window.c
new file mode 100644 (file)
index 0000000..73aa1e2
--- /dev/null
@@ -0,0 +1,100 @@
+/* $Id: cmd-respawn-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "kt:", 0, 1,
+       "[-k] " CMD_TARGET_WINDOW_USAGE " [command]",
+       0,
+       NULL,
+       NULL,
+       cmd_respawn_window_exec
+};
+
+int
+cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct winlink          *wl;
+       struct window           *w;
+       struct window_pane      *wp;
+       struct session          *s;
+       struct environ           env;
+       const char              *cmd;
+       char                    *cause;
+
+       if ((wl = cmd_find_window(ctx, args_get(args, 't'), &s)) == NULL)
+               return (-1);
+       w = wl->window;
+
+       if (!args_has(self->args, 'k')) {
+               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);
+               }
+       }
+
+       environ_init(&env);
+       environ_copy(&global_environ, &env);
+       environ_copy(&s->environ, &env);
+       server_fill_environ(s, &env);
+
+       wp = TAILQ_FIRST(&w->panes);
+       TAILQ_REMOVE(&w->panes, wp, entry);
+       layout_free(w);
+       window_destroy_panes(w);
+       TAILQ_INSERT_HEAD(&w->panes, wp, entry);
+       window_pane_resize(wp, w->sx, w->sy);
+       if (args->argc != 0)
+               cmd = args->argv[0];
+       else
+               cmd = NULL;
+       if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) {
+               ctx->error(ctx, "respawn window failed: %s", cause);
+               xfree(cause);
+               environ_free(&env);
+               server_destroy_pane(wp);
+               return (-1);
+       }
+       layout_init(w);
+       window_pane_reset_mode(wp);
+       screen_reinit(&wp->base);
+       input_init(wp);
+       window_set_active_pane(w, wp);
+
+       recalculate_sizes();
+       server_redraw_window(w);
+
+       environ_free(&env);
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-rotate-window.c b/external/bsd/tmux/dist/cmd-rotate-window.c
new file mode 100644 (file)
index 0000000..57f5520
--- /dev/null
@@ -0,0 +1,119 @@
+/* $Id: cmd-rotate-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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_key_binding(struct cmd *, int);
+int    cmd_rotate_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_rotate_window_entry = {
+       "rotate-window", "rotatew",
+       "Dt:U", 0, 0,
+       "[-DU] " CMD_TARGET_WINDOW_USAGE,
+       0,
+       cmd_rotate_window_key_binding,
+       NULL,
+       cmd_rotate_window_exec
+};
+
+void
+cmd_rotate_window_key_binding(struct cmd *self, int key)
+{
+       self->args = args_create(0);
+       if (key == ('o' | KEYC_ESCAPE))
+               args_set(self->args, 'D', NULL);
+}
+
+int
+cmd_rotate_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct winlink          *wl;
+       struct window           *w;
+       struct window_pane      *wp, *wp2;
+       struct layout_cell      *lc;
+       u_int                    sx, sy, xoff, yoff;
+
+       if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL)
+               return (-1);
+       w = wl->window;
+
+       if (args_has(self->args, 'D')) {
+               wp = TAILQ_LAST(&w->panes, window_panes);
+               TAILQ_REMOVE(&w->panes, wp, entry);
+               TAILQ_INSERT_HEAD(&w->panes, wp, entry);
+
+               lc = wp->layout_cell;
+               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->layout_cell = wp2->layout_cell;
+                       if (wp->layout_cell != NULL)
+                               wp->layout_cell->wp = wp;
+                       wp->xoff = wp2->xoff; wp->yoff = wp2->yoff;
+                       window_pane_resize(wp, wp2->sx, wp2->sy);
+               }
+               wp->layout_cell = lc;
+               if (wp->layout_cell != NULL)
+                       wp->layout_cell->wp = wp;
+               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);
+               server_redraw_window(w);
+       } else {
+               wp = TAILQ_FIRST(&w->panes);
+               TAILQ_REMOVE(&w->panes, wp, entry);
+               TAILQ_INSERT_TAIL(&w->panes, wp, entry);
+
+               lc = wp->layout_cell;
+               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->layout_cell = wp2->layout_cell;
+                       if (wp->layout_cell != NULL)
+                               wp->layout_cell->wp = wp;
+                       wp->xoff = wp2->xoff; wp->yoff = wp2->yoff;
+                       window_pane_resize(wp, wp2->sx, wp2->sy);
+               }
+               wp->layout_cell = lc;
+               if (wp->layout_cell != NULL)
+                       wp->layout_cell->wp = wp;
+               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);
+               server_redraw_window(w);
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-run-shell.c b/external/bsd/tmux/dist/cmd-run-shell.c
new file mode 100644 (file)
index 0000000..48f9cee
--- /dev/null
@@ -0,0 +1,141 @@
+/* $Id: cmd-run-shell.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */
+
+/*
+ * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
+ * Copyright (c) 2009 Nicholas Marriott <nicm@openbsd.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/wait.h>
+
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Runs a command without a window.
+ */
+
+int    cmd_run_shell_exec(struct cmd *, struct cmd_ctx *);
+
+void   cmd_run_shell_callback(struct job *);
+void   cmd_run_shell_free(void *);
+
+const struct cmd_entry cmd_run_shell_entry = {
+       "run-shell", "run",
+       "", 1, 1,
+       "command",
+       0,
+       NULL,
+       NULL,
+       cmd_run_shell_exec
+};
+
+struct cmd_run_shell_data {
+       char            *cmd;
+       struct cmd_ctx   ctx;
+};
+
+int
+cmd_run_shell_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args                     *args = self->args;
+       struct cmd_run_shell_data       *cdata;
+       const char                      *shellcmd = args->argv[0];
+
+       cdata = xmalloc(sizeof *cdata);
+       cdata->cmd = xstrdup(args->argv[0]);
+       memcpy(&cdata->ctx, ctx, sizeof cdata->ctx);
+
+       if (ctx->cmdclient != NULL)
+               ctx->cmdclient->references++;
+       if (ctx->curclient != NULL)
+               ctx->curclient->references++;
+
+       job_run(shellcmd, cmd_run_shell_callback, cmd_run_shell_free, cdata);
+
+       return (1);     /* don't let client exit */
+}
+
+void
+cmd_run_shell_callback(struct job *job)
+{
+       struct cmd_run_shell_data       *cdata = job->data;
+       struct cmd_ctx                  *ctx = &cdata->ctx;
+       char                            *cmd, *msg, *line;
+       size_t                           size;
+       int                              retcode;
+       u_int                            lines;
+
+       if (ctx->cmdclient != NULL && ctx->cmdclient->flags & CLIENT_DEAD)
+               return;
+       if (ctx->curclient != NULL && ctx->curclient->flags & CLIENT_DEAD)
+               return;
+
+       lines = 0;
+       do {
+               if ((line = evbuffer_readline(job->event->input)) != NULL) {
+                       ctx->print(ctx, "%s", line);
+                       lines++;
+               }
+       } while (line != NULL);
+
+       size = EVBUFFER_LENGTH(job->event->input);
+       if (size != 0) {
+               line = xmalloc(size + 1);
+               memcpy(line, EVBUFFER_DATA(job->event->input), size);
+               line[size] = '\0';
+
+               ctx->print(ctx, "%s", line);
+               lines++;
+
+               xfree(line);
+       }
+
+       cmd = cdata->cmd;
+
+       msg = NULL;
+       if (WIFEXITED(job->status)) {
+               if ((retcode = WEXITSTATUS(job->status)) != 0)
+                       xasprintf(&msg, "'%s' returned %d", cmd, retcode);
+       } else if (WIFSIGNALED(job->status)) {
+               retcode = WTERMSIG(job->status);
+               xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode);
+       }
+       if (msg != NULL) {
+               if (lines != 0)
+                       ctx->print(ctx, "%s", msg);
+               else
+                       ctx->info(ctx, "%s", msg);
+               xfree(msg);
+       }
+}
+
+void
+cmd_run_shell_free(void *data)
+{
+       struct cmd_run_shell_data       *cdata = data;
+       struct cmd_ctx                  *ctx = &cdata->ctx;
+
+       if (ctx->cmdclient != NULL) {
+               ctx->cmdclient->references--;
+               ctx->cmdclient->flags |= CLIENT_EXIT;
+       }
+       if (ctx->curclient != NULL)
+               ctx->curclient->references--;
+
+       xfree(cdata->cmd);
+       xfree(cdata);
+}
diff --git a/external/bsd/tmux/dist/cmd-save-buffer.c b/external/bsd/tmux/dist/cmd-save-buffer.c
new file mode 100644 (file)
index 0000000..c36d2c3
--- /dev/null
@@ -0,0 +1,102 @@
+/* $Id: cmd-save-buffer.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "ab:", 1, 1,
+       "[-a] " CMD_BUFFER_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_save_buffer_exec
+};
+
+int
+cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct client           *c = ctx->cmdclient;
+       struct paste_buffer     *pb;
+       const char              *path;
+       char                    *cause;
+       int                      buffer;
+       mode_t                   mask;
+       FILE                    *f;
+
+       if (!args_has(args, 'b')) {
+               if ((pb = paste_get_top(&global_buffers)) == NULL) {
+                       ctx->error(ctx, "no buffers");
+                       return (-1);
+               }
+       } else {
+               buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
+               if (cause != NULL) {
+                       ctx->error(ctx, "buffer %s", cause);
+                       xfree(cause);
+                       return (-1);
+               }
+
+               pb = paste_get_index(&global_buffers, buffer);
+               if (pb == NULL) {
+                       ctx->error(ctx, "no buffer %d", buffer);
+                       return (-1);
+               }
+       }
+
+       path = args->argv[0];
+       if (strcmp(path, "-") == 0) {
+               if (c == NULL) {
+                       ctx->error(ctx, "%s: can't write to stdout", path);
+                       return (-1);
+               }
+               bufferevent_write(c->stdout_event, pb->data, pb->size);
+       } else {
+               mask = umask(S_IRWXG | S_IRWXO);
+               if (args_has(self->args, 'a'))
+                       f = fopen(path, "ab");
+               else
+                       f = fopen(path, "wb");
+               umask(mask);
+               if (f == NULL) {
+                       ctx->error(ctx, "%s: %s", path, strerror(errno));
+                       return (-1);
+               }
+               if (fwrite(pb->data, 1, pb->size, f) != pb->size) {
+                       ctx->error(ctx, "%s: fwrite error", path);
+                       fclose(f);
+                       return (-1);
+               }
+               fclose(f);
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-select-layout.c b/external/bsd/tmux/dist/cmd-select-layout.c
new file mode 100644 (file)
index 0000000..a474517
--- /dev/null
@@ -0,0 +1,133 @@
+/* $Id: cmd-select-layout.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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_key_binding(struct cmd *, int);
+int    cmd_select_layout_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_select_layout_entry = {
+       "select-layout", "selectl",
+       "npt:", 0, 1,
+       "[-np] " CMD_TARGET_WINDOW_USAGE " [layout-name]",
+       0,
+       cmd_select_layout_key_binding,
+       NULL,
+       cmd_select_layout_exec
+};
+
+const struct cmd_entry cmd_next_layout_entry = {
+       "next-layout", "nextl",
+       "t:", 0, 0,
+       CMD_TARGET_WINDOW_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_select_layout_exec
+};
+
+const struct cmd_entry cmd_previous_layout_entry = {
+       "previous-layout", "prevl",
+       "t:", 0, 0,
+       CMD_TARGET_WINDOW_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_select_layout_exec
+};
+
+void
+cmd_select_layout_key_binding(struct cmd *self, int key)
+{
+       switch (key) {
+       case '1' | KEYC_ESCAPE:
+               self->args = args_create(1, "even-horizontal");
+               break;
+       case '2' | KEYC_ESCAPE:
+               self->args = args_create(1, "even-vertical");
+               break;
+       case '3' | KEYC_ESCAPE:
+               self->args = args_create(1, "main-horizontal");
+               break;
+       case '4' | KEYC_ESCAPE:
+               self->args = args_create(1, "main-vertical");
+               break;
+       case '5' | KEYC_ESCAPE:
+               self->args = args_create(1, "tiled");
+               break;
+       default:
+               self->args = args_create(0);
+               break;
+       }
+}
+
+int
+cmd_select_layout_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct winlink  *wl;
+       const char      *layoutname;
+       int              next, previous, layout;
+
+       if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL)
+               return (-1);
+
+       next = self->entry == &cmd_next_layout_entry;
+       if (args_has(self->args, 'n'))
+               next = 1;
+       previous = self->entry == &cmd_previous_layout_entry;
+       if (args_has(self->args, 'p'))
+               previous = 1;
+
+       if (next || previous) {
+               if (next)
+                       layout = layout_set_next(wl->window);
+               else
+                       layout = layout_set_previous(wl->window);
+               ctx->info(ctx, "arranging in: %s", layout_set_name(layout));
+               return (0);
+       }
+
+       if (args->argc == 0)
+               layout = wl->window->lastlayout;
+       else
+               layout = layout_set_lookup(args->argv[0]);
+       if (layout != -1) {
+               layout = layout_set_select(wl->window, layout);
+               ctx->info(ctx, "arranging in: %s", layout_set_name(layout));
+               return (0);
+       }
+
+       if (args->argc != 0) {
+               layoutname = args->argv[0];
+               if (layout_parse(wl->window, layoutname) == -1) {
+                       ctx->error(ctx, "can't set layout: %s", layoutname);
+                       return (-1);
+               }
+               ctx->info(ctx, "arranging in: %s", layoutname);
+               return (0);
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-select-pane.c b/external/bsd/tmux/dist/cmd-select-pane.c
new file mode 100644 (file)
index 0000000..8836c7f
--- /dev/null
@@ -0,0 +1,116 @@
+/* $Id: cmd-select-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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.
+ */
+
+void   cmd_select_pane_key_binding(struct cmd *, int);
+int    cmd_select_pane_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_select_pane_entry = {
+       "select-pane", "selectp",
+       "lDLRt:U", 0, 0,
+       "[-lDLRU] " CMD_TARGET_PANE_USAGE,
+       0,
+       cmd_select_pane_key_binding,
+       NULL,
+       cmd_select_pane_exec
+};
+
+const struct cmd_entry cmd_last_pane_entry = {
+       "last-pane", "lastp",
+       "t:", 0, 0,
+       CMD_TARGET_WINDOW_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_select_pane_exec
+};
+
+void
+cmd_select_pane_key_binding(struct cmd *self, int key)
+{
+       self->args = args_create(0);
+       if (key == KEYC_UP)
+               args_set(self->args, 'U', NULL);
+       if (key == KEYC_DOWN)
+               args_set(self->args, 'D', NULL);
+       if (key == KEYC_LEFT)
+               args_set(self->args, 'L', NULL);
+       if (key == KEYC_RIGHT)
+               args_set(self->args, 'R', NULL);
+       if (key == 'o')
+               args_set(self->args, 't', ":.+");
+}
+
+int
+cmd_select_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct winlink          *wl;
+       struct window_pane      *wp;
+
+       if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) {
+               wl = cmd_find_window(ctx, args_get(args, 't'), NULL);
+               if (wl == NULL)
+                       return (-1);
+
+               if (wl->window->last == NULL) {
+                       ctx->error(ctx, "no last pane");
+                       return (-1);
+               }
+
+               window_set_active_pane(wl->window, wl->window->last);
+               server_status_window(wl->window);
+               server_redraw_window_borders(wl->window);
+
+               return (0);
+       }
+
+       if ((wl = cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp)) == NULL)
+               return (-1);
+
+       if (!window_pane_visible(wp)) {
+               ctx->error(ctx, "pane not visible");
+               return (-1);
+       }
+
+       if (args_has(self->args, 'L'))
+               wp = window_pane_find_left(wp);
+       else if (args_has(self->args, 'R'))
+               wp = window_pane_find_right(wp);
+       else if (args_has(self->args, 'U'))
+               wp = window_pane_find_up(wp);
+       else if (args_has(self->args, 'D'))
+               wp = window_pane_find_down(wp);
+       if (wp == NULL) {
+               ctx->error(ctx, "pane not found");
+               return (-1);
+       }
+
+       window_set_active_pane(wl->window, wp);
+       server_status_window(wl->window);
+       server_redraw_window_borders(wl->window);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-select-window.c b/external/bsd/tmux/dist/cmd-select-window.c
new file mode 100644 (file)
index 0000000..481766c
--- /dev/null
@@ -0,0 +1,139 @@
+/* $Id: cmd-select-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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_key_binding(struct cmd *, int);
+int    cmd_select_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_select_window_entry = {
+       "select-window", "selectw",
+       "lnpt:", 0, 0,
+       "[-lnp] " CMD_TARGET_WINDOW_USAGE,
+       0,
+       cmd_select_window_key_binding,
+       NULL,
+       cmd_select_window_exec
+};
+
+const struct cmd_entry cmd_next_window_entry = {
+       "next-window", "next",
+       "at:", 0, 0,
+       "[-a] " CMD_TARGET_SESSION_USAGE,
+       0,
+       cmd_select_window_key_binding,
+       NULL,
+       cmd_select_window_exec
+};
+
+const struct cmd_entry cmd_previous_window_entry = {
+       "previous-window", "prev",
+       "at:", 0, 0,
+       "[-a] " CMD_TARGET_SESSION_USAGE,
+       0,
+       cmd_select_window_key_binding,
+       NULL,
+       cmd_select_window_exec
+};
+
+const struct cmd_entry cmd_last_window_entry = {
+       "last-window", "last",
+       "t:", 0, 0,
+       CMD_TARGET_SESSION_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_select_window_exec
+};
+
+void
+cmd_select_window_key_binding(struct cmd *self, int key)
+{
+       char    tmp[16];
+
+       self->args = args_create(0);
+       if (key >= '0' && key <= '9') {
+               xsnprintf(tmp, sizeof tmp, ":%d", key - '0');
+               args_set(self->args, 't', tmp);
+       }
+       if (key == ('n' | KEYC_ESCAPE) || key == ('p' | KEYC_ESCAPE))
+               args_set(self->args, 'a', NULL);
+}
+
+int
+cmd_select_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct winlink  *wl;
+       struct session  *s;
+       int              next, previous, last, activity;
+
+       next = self->entry == &cmd_next_window_entry;
+       if (args_has(self->args, 'n'))
+               next = 1;
+       previous = self->entry == &cmd_previous_window_entry;
+       if (args_has(self->args, 'p'))
+               previous = 1;
+       last = self->entry == &cmd_last_window_entry;
+       if (args_has(self->args, 'l'))
+               last = 1;
+
+       if (next || previous || last) {
+               s = cmd_find_session(ctx, args_get(args, 't'), 0);
+               if (s == NULL)
+                       return (-1);
+
+               activity = args_has(self->args, 'a');
+               if (next) {
+                       if (session_next(s, activity) != 0) {
+                               ctx->error(ctx, "no next window");
+                               return (-1);
+                       }
+               } else if (previous) {
+                       if (session_previous(s, activity) != 0) {
+                               ctx->error(ctx, "no previous window");
+                               return (-1);
+                       }
+               } else {
+                       if (session_last(s) != 0) {
+                               ctx->error(ctx, "no last window");
+                               return (-1);
+                       }
+               }
+
+               server_redraw_session(s);
+       } else {
+               wl = cmd_find_window(ctx, args_get(args, 't'), &s);
+               if (wl == NULL)
+                       return (-1);
+
+               if (session_select(s, wl->idx) == 0)
+                       server_redraw_session(s);
+       }
+       recalculate_sizes();
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-send-keys.c b/external/bsd/tmux/dist/cmd-send-keys.c
new file mode 100644 (file)
index 0000000..5c0833d
--- /dev/null
@@ -0,0 +1,65 @@
+/* $Id: cmd-send-keys.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_send_keys_entry = {
+       "send-keys", "send",
+       "t:", 0, -1,
+       "[-t target-pane] key ...",
+       0,
+       NULL,
+       NULL,
+       cmd_send_keys_exec
+};
+
+int
+cmd_send_keys_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct window_pane      *wp;
+       struct session          *s;
+       const char              *str;
+       int                      i, key;
+
+       if (cmd_find_pane(ctx, args_get(args, 't'), &s, &wp) == NULL)
+               return (-1);
+
+       for (i = 0; i < args->argc; i++) {
+               str = args->argv[i];
+
+               if ((key = key_string_lookup_string(str)) != KEYC_NONE) {
+                           window_pane_key(wp, s, key);
+               } else {
+                       for (; *str != '\0'; str++)
+                           window_pane_key(wp, s, *str);
+               }
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-send-prefix.c b/external/bsd/tmux/dist/cmd-send-prefix.c
new file mode 100644 (file)
index 0000000..ca99249
--- /dev/null
@@ -0,0 +1,54 @@
+/* $Id: cmd-send-prefix.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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,
+       "t:", 0, 0,
+       CMD_TARGET_PANE_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_send_prefix_exec
+};
+
+int
+cmd_send_prefix_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct session          *s;
+       struct window_pane      *wp;
+       struct keylist          *keylist;
+
+       if (cmd_find_pane(ctx, args_get(args, 't'), &s, &wp) == NULL)
+               return (-1);
+
+       keylist = options_get_data(&s->options, "prefix");
+       window_pane_key(wp, s, ARRAY_FIRST(keylist));
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-server-info.c b/external/bsd/tmux/dist/cmd-server-info.c
new file mode 100644 (file)
index 0000000..e0956e5
--- /dev/null
@@ -0,0 +1,185 @@
+/* $Id: cmd-server-info.c,v 1.3 2011/08/17 18:48:35 jmmv 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, 0,
+       "",
+       0,
+       NULL,
+       NULL,
+       cmd_server_info_exec
+};
+
+/* ARGSUSED */
+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;
+       const struct tty_term_code_entry        *ent;
+       struct utsname                           un;
+       struct job                              *job;
+       struct grid                             *gd;
+       struct grid_line                        *gl;
+       u_int                                    i, j, k;
+       char                                    *tim;
+       time_t                                   t;
+       u_int                                    lines, ulines;
+       size_t                                   size, usize;
+
+       tim = ctime(&start_time);
+       *strchr(tim, '\n') = '\0';
+       ctx->print(ctx,
+           "tmux " VERSION ", pid %ld, started %s", (long) getpid(), tim);
+       ctx->print(
+           ctx, "socket path %s, debug level %d", socket_path, debug_level);
+       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, "%s", "");
+
+       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 bs=%hho] "
+                   "[flags=0x%x/0x%x, references=%u]", i, c->tty.path,
+                   c->ibuf.fd, c->tty.fd, c->session->name,
+                   c->tty.sx, c->tty.sy, c->tty.termname,
+                   c->tty.tio.c_cc[VERASE], c->flags,
+                   c->tty.flags, c->references);
+       }
+       ctx->print(ctx, "%s", "");
+
+       ctx->print(ctx, "Sessions: [%zu/%zu]",
+           sizeof (struct grid_cell), sizeof (struct grid_utf8));
+       RB_FOREACH(s, sessions, &sessions) {
+               t = s->creation_time.tv_sec;
+               tim = ctime(&t);
+               *strchr(tim, '\n') = '\0';
+
+               ctx->print(ctx, "%2u: %s: %u windows (created %s) [%ux%u] "
+                   "[flags=0x%x]", s->idx, 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, last layout=%d]", wl->idx, w->name,
+                           w->sx, w->sy, w->flags, w->references,
+                           w->lastlayout);
+                       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++) {
+                                       gl = &gd->linedata[k];
+                                       if (gl->celldata != NULL) {
+                                               lines++;
+                                               size += gl->cellsize *
+                                                   sizeof *gl->celldata;
+                                       }
+                                       if (gl->utf8data != NULL) {
+                                               ulines++;
+                                               usize += gl->utf8size *
+                                                   sizeof *gl->utf8data;
+                                       }
+                               }
+                               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, "%s", "");
+
+       ctx->print(ctx, "Terminals:");
+       LIST_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: {
+                               size_t slen = strlen(code->value.string);
+                               char out[slen * 4 + 1];
+                               strvisx(out, code->value.string, slen,
+                                   VIS_OCTAL|VIS_TAB|VIS_NL);
+                               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, "%s", "");
+
+       ctx->print(ctx, "Jobs:");
+       LIST_FOREACH(job, &all_jobs, lentry) {
+               ctx->print(ctx, "%s [fd=%d, pid=%d, status=%d]",
+                   job->cmd, job->fd, job->pid, job->status);
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-set-buffer.c b/external/bsd/tmux/dist/cmd-set-buffer.c
new file mode 100644 (file)
index 0000000..b60d758
--- /dev/null
@@ -0,0 +1,75 @@
+/* $Id: cmd-set-buffer.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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"
+
+/*
+ * 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",
+       "b:", 1, 1,
+       CMD_BUFFER_USAGE " data",
+       0,
+       NULL,
+       NULL,
+       cmd_set_buffer_exec
+};
+
+int
+cmd_set_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       u_int            limit;
+       char            *pdata, *cause;
+       size_t           psize;
+       int              buffer;
+
+       limit = options_get_number(&global_options, "buffer-limit");
+
+       pdata = xstrdup(args->argv[0]);
+       psize = strlen(pdata);
+
+       if (!args_has(args, 'b')) {
+               paste_add(&global_buffers, pdata, psize, limit);
+               return (0);
+       }
+
+       buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
+       if (cause != NULL) {
+               ctx->error(ctx, "buffer %s", cause);
+               xfree(cause);
+               xfree(pdata);
+               return (-1);
+       }
+
+       if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) {
+               ctx->error(ctx, "no buffer %d", buffer);
+               xfree(pdata);
+               return (-1);
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-set-environment.c b/external/bsd/tmux/dist/cmd-set-environment.c
new file mode 100644 (file)
index 0000000..140a864
--- /dev/null
@@ -0,0 +1,94 @@
+/* $Id: cmd-set-environment.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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"
+
+/*
+ * Set an environment variable.
+ */
+
+int    cmd_set_environment_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_set_environment_entry = {
+       "set-environment", "setenv",
+       "grt:u", 1, 2,
+       "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]",
+       0,
+       NULL,
+       NULL,
+       cmd_set_environment_exec
+};
+
+int
+cmd_set_environment_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct session  *s;
+       struct environ  *env;
+       const char      *name, *value;
+
+       name = args->argv[0];
+       if (*name == '\0') {
+               ctx->error(ctx, "empty variable name");
+               return (-1);
+       }
+       if (strchr(name, '=') != NULL) {
+               ctx->error(ctx, "variable name contains =");
+               return (-1);
+       }
+
+       if (args->argc < 1)
+               value = NULL;
+       else
+               value = args->argv[1];
+
+       if (args_has(self->args, 'g'))
+               env = &global_environ;
+       else {
+               if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL)
+                       return (-1);
+               env = &s->environ;
+       }
+
+       if (args_has(self->args, 'u')) {
+               if (value != NULL) {
+                       ctx->error(ctx, "can't specify a value with -u");
+                       return (-1);
+               }
+               environ_unset(env, name);
+       } else if (args_has(self->args, 'r')) {
+               if (value != NULL) {
+                       ctx->error(ctx, "can't specify a value with -r");
+                       return (-1);
+               }
+               environ_set(env, name, NULL);
+       } else {
+               if (value == NULL) {
+                       ctx->error(ctx, "no value specified");
+                       return (-1);
+               }
+               environ_set(env, name, value);
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-set-option.c b/external/bsd/tmux/dist/cmd-set-option.c
new file mode 100644 (file)
index 0000000..7e1a389
--- /dev/null
@@ -0,0 +1,411 @@
+/* $Id: cmd-set-option.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 *);
+
+int    cmd_set_option_find(const char *, const struct options_table_entry **,
+           const struct options_table_entry **);
+
+int    cmd_set_option_unset(struct cmd *, struct cmd_ctx *,
+           const struct options_table_entry *, struct options *,
+           const char *);
+int    cmd_set_option_set(struct cmd *, struct cmd_ctx *,
+           const struct options_table_entry *, struct options *,
+           const char *);
+
+struct options_entry *cmd_set_option_string(struct cmd *, struct cmd_ctx *,
+           const struct options_table_entry *, struct options *,
+           const char *);
+struct options_entry *cmd_set_option_number(struct cmd *, struct cmd_ctx *,
+           const struct options_table_entry *, struct options *,
+           const char *);
+struct options_entry *cmd_set_option_keys(struct cmd *, struct cmd_ctx *,
+           const struct options_table_entry *, struct options *,
+           const char *);
+struct options_entry *cmd_set_option_colour(struct cmd *, struct cmd_ctx *,
+           const struct options_table_entry *, struct options *,
+           const char *);
+struct options_entry *cmd_set_option_attributes(struct cmd *, struct cmd_ctx *,
+           const struct options_table_entry *, struct options *,
+           const char *);
+struct options_entry *cmd_set_option_flag(struct cmd *, struct cmd_ctx *,
+           const struct options_table_entry *, struct options *,
+           const char *);
+struct options_entry *cmd_set_option_choice(struct cmd *, struct cmd_ctx *,
+           const struct options_table_entry *, struct options *,
+           const char *);
+
+const struct cmd_entry cmd_set_option_entry = {
+       "set-option", "set",
+       "agst:uw", 1, 2,
+       "[-agsuw] [-t target-session|target-window] option [value]",
+       0,
+       NULL,
+       NULL,
+       cmd_set_option_exec
+};
+
+const struct cmd_entry cmd_set_window_option_entry = {
+       "set-window-option", "setw",
+       "agt:u", 1, 2,
+       "[-agu] " CMD_TARGET_WINDOW_USAGE " option [value]",
+       0,
+       NULL,
+       NULL,
+       cmd_set_option_exec
+};
+
+/* Look for an option in all three tables. */
+int
+cmd_set_option_find(
+    const char *optstr, const struct options_table_entry **table,
+    const struct options_table_entry **oe)
+{
+       static const struct options_table_entry *tables[] = {
+               server_options_table,
+               window_options_table,
+               session_options_table
+       };
+       const struct options_table_entry        *oe_loop;
+       u_int                                    i;
+
+       for (i = 0; i < nitems(tables); i++) {
+               for (oe_loop = tables[i]; oe_loop->name != NULL; oe_loop++) {
+                       if (strncmp(oe_loop->name, optstr, strlen(optstr)) != 0)
+                               continue;
+
+                       /* If already found, ambiguous. */
+                       if (*oe != NULL)
+                               return (-1);
+                       *oe = oe_loop;
+                       *table = tables[i];
+
+                       /* Bail now if an exact match. */
+                       if (strcmp((*oe)->name, optstr) == 0)
+                               break;
+               }
+       }
+       return (0);
+}
+
+int
+cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args                             *args = self->args;
+       const struct options_table_entry        *table, *oe;
+       struct session                          *s;
+       struct winlink                          *wl;
+       struct client                           *c;
+       struct options                          *oo;
+       const char                              *optstr, *valstr;
+       u_int                                    i;
+
+       /* Get the option name and value. */
+       optstr = args->argv[0];
+       if (*optstr == '\0') {
+               ctx->error(ctx, "invalid option");
+               return (-1);
+       }
+       if (args->argc < 2)
+               valstr = NULL;
+       else
+               valstr = args->argv[1];
+
+       /* Find the option entry, try each table. */
+       table = oe = NULL;
+       if (cmd_set_option_find(optstr, &table, &oe) != 0) {
+               ctx->error(ctx, "ambiguous option: %s", optstr);
+               return (-1);
+       }
+       if (oe == NULL) {
+               ctx->error(ctx, "unknown option: %s", optstr);
+               return (-1);
+       }
+
+       /* Work out the tree from the table. */
+       if (table == server_options_table)
+               oo = &global_options;
+       else if (table == window_options_table) {
+               if (args_has(self->args, 'g'))
+                       oo = &global_w_options;
+               else {
+                       wl = cmd_find_window(ctx, args_get(args, 't'), NULL);
+                       if (wl == NULL)
+                               return (-1);
+                       oo = &wl->window->options;
+               }
+       } else if (table == session_options_table) {
+               if (args_has(self->args, 'g'))
+                       oo = &global_s_options;
+               else {
+                       s = cmd_find_session(ctx, args_get(args, 't'), 0);
+                       if (s == NULL)
+                               return (-1);
+                       oo = &s->options;
+               }
+       } else {
+               ctx->error(ctx, "unknown table");
+               return (-1);
+       }
+
+       /* Unset or set the option. */
+       if (args_has(args, 'u')) {
+               if (cmd_set_option_unset(self, ctx, oe, oo, valstr) != 0)
+                       return (-1);
+       } else {
+               if (cmd_set_option_set(self, ctx, oe, oo, valstr) != 0)
+                       return (-1);
+       }
+
+       /* Update sizes and redraw. May not need it but meh. */
+       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);
+}
+
+/* Unset an option. */
+int
+cmd_set_option_unset(struct cmd *self, struct cmd_ctx *ctx,
+    const struct options_table_entry *oe, struct options *oo, const char *value)
+{
+       struct args     *args = self->args;
+
+       if (args_has(args, 'g')) {
+               ctx->error(ctx, "can't unset global option: %s", oe->name);
+               return (-1);
+       }
+       if (value != NULL) {
+               ctx->error(ctx, "value passed to unset option: %s", oe->name);
+               return (-1);
+       }
+
+       options_remove(oo, oe->name);
+       ctx->info(ctx, "unset option: %s", oe->name);
+       return (0);
+}
+
+/* Set an option. */
+int
+cmd_set_option_set(struct cmd *self, struct cmd_ctx *ctx,
+    const struct options_table_entry *oe, struct options *oo, const char *value)
+{
+       struct options_entry    *o;
+       const char              *s;
+
+       if (oe->type != OPTIONS_TABLE_FLAG && value == NULL) {
+               ctx->error(ctx, "empty value");
+               return (-1);
+       }
+
+       o = NULL;
+       switch (oe->type) {
+       case OPTIONS_TABLE_STRING:
+               o = cmd_set_option_string(self, ctx, oe, oo, value);
+               break;
+       case OPTIONS_TABLE_NUMBER:
+               o = cmd_set_option_number(self, ctx, oe, oo, value);
+               break;
+       case OPTIONS_TABLE_KEYS:
+               o = cmd_set_option_keys(self, ctx, oe, oo, value);
+               break;
+       case OPTIONS_TABLE_COLOUR:
+               o = cmd_set_option_colour(self, ctx, oe, oo, value);
+               break;
+       case OPTIONS_TABLE_ATTRIBUTES:
+               o = cmd_set_option_attributes(self, ctx, oe, oo, value);
+               break;
+       case OPTIONS_TABLE_FLAG:
+               o = cmd_set_option_flag(self, ctx, oe, oo, value);
+               break;
+       case OPTIONS_TABLE_CHOICE:
+               o = cmd_set_option_choice(self, ctx, oe, oo, value);
+               break;
+       }
+       if (o == NULL)
+               return (-1);
+
+       s = options_table_print_entry(oe, o);
+       ctx->info(ctx, "set option: %s -> %s", oe->name, s);
+       return (0);
+}
+
+/* Set a string option. */
+struct options_entry *
+cmd_set_option_string(struct cmd *self, unused struct cmd_ctx *ctx,
+    const struct options_table_entry *oe, struct options *oo, const char *value)
+{
+       struct args     *args = self->args;
+       struct options_entry    *o;
+       char                    *oldval, *newval;
+
+       if (args_has(args, 'a')) {
+               oldval = options_get_string(oo, oe->name);
+               xasprintf(&newval, "%s%s", oldval, value);
+       } else
+               newval = xstrdup(value);
+
+       o = options_set_string(oo, oe->name, "%s", newval);
+
+       xfree(newval);
+       return (o);
+}
+
+/* Set a number option. */
+struct options_entry *
+cmd_set_option_number(unused struct cmd *self, struct cmd_ctx *ctx,
+    const struct options_table_entry *oe, struct options *oo, const char *value)
+{
+       long long        ll;
+       const char      *errstr;
+
+       ll = strtonum(value, oe->minimum, oe->maximum, &errstr);
+       if (errstr != NULL) {
+               ctx->error(ctx, "value is %s: %s", errstr, value);
+               return (NULL);
+       }
+
+       return (options_set_number(oo, oe->name, ll));
+}
+
+/* Set a keys option. */
+struct options_entry *
+cmd_set_option_keys(unused struct cmd *self, struct cmd_ctx *ctx,
+    const struct options_table_entry *oe, struct options *oo, const char *value)
+{
+       struct keylist  *keylist;
+       char            *copy, *ptr, *s;
+       int              key;
+
+       keylist = xmalloc(sizeof *keylist);
+       ARRAY_INIT(keylist);
+
+       ptr = copy = xstrdup(value);
+       while ((s = strsep(&ptr, ",")) != NULL) {
+               if ((key = key_string_lookup_string(s)) == KEYC_NONE) {
+                       ctx->error(ctx, "unknown key: %s", s);
+                       xfree(copy);
+                       xfree(keylist);
+                       return (NULL);
+               }
+               ARRAY_ADD(keylist, key);
+       }
+       xfree(copy);
+
+       return (options_set_data(oo, oe->name, keylist, xfree));
+}
+
+/* Set a colour option. */
+struct options_entry *
+cmd_set_option_colour(unused struct cmd *self, struct cmd_ctx *ctx,
+    const struct options_table_entry *oe, struct options *oo, const char *value)
+{
+       int     colour;
+
+       if ((colour = colour_fromstring(value)) == -1) {
+               ctx->error(ctx, "bad colour: %s", value);
+               return (NULL);
+       }
+
+       return (options_set_number(oo, oe->name, colour));
+}
+
+/* Set an attributes option. */
+struct options_entry *
+cmd_set_option_attributes(unused struct cmd *self, struct cmd_ctx *ctx,
+    const struct options_table_entry *oe, struct options *oo, const char *value)
+{
+       int     attr;
+
+       if ((attr = attributes_fromstring(value)) == -1) {
+               ctx->error(ctx, "bad attributes: %s", value);
+               return (NULL);
+       }
+
+       return (options_set_number(oo, oe->name, attr));
+}
+
+/* Set a flag option. */
+struct options_entry *
+cmd_set_option_flag(unused struct cmd *self, struct cmd_ctx *ctx,
+    const struct options_table_entry *oe, struct options *oo, const char *value)
+{
+       int     flag;
+
+       if (value == NULL || *value == '\0')
+               flag = !options_get_number(oo, oe->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 (NULL);
+               }
+       }
+
+       return (options_set_number(oo, oe->name, flag));
+}
+
+/* Set a choice option. */
+struct options_entry *
+cmd_set_option_choice(unused struct cmd *self, struct cmd_ctx *ctx,
+    const struct options_table_entry *oe, struct options *oo, const char *value)
+{
+       const char      **choicep;
+       int               n, choice = -1;
+
+       n = 0;
+       for (choicep = oe->choices; *choicep != NULL; choicep++) {
+               n++;
+               if (strncmp(*choicep, value, strlen(value)) != 0)
+                       continue;
+
+               if (choice != -1) {
+                       ctx->error(ctx, "ambiguous value: %s", value);
+                       return (NULL);
+               }
+               choice = n - 1;
+       }
+       if (choice == -1) {
+               ctx->error(ctx, "unknown value: %s", value);
+               return (NULL);
+       }
+
+       return (options_set_number(oo, oe->name, choice));
+}
diff --git a/external/bsd/tmux/dist/cmd-show-buffer.c b/external/bsd/tmux/dist/cmd-show-buffer.c
new file mode 100644 (file)
index 0000000..6a7e38e
--- /dev/null
@@ -0,0 +1,109 @@
+/* $Id: cmd-show-buffer.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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"
+
+/*
+ * 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",
+       "b:", 0, 0,
+       CMD_BUFFER_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_show_buffer_exec
+};
+
+int
+cmd_show_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct session          *s;
+       struct paste_buffer     *pb;
+       int                      buffer;
+       char                    *in, *buf, *ptr, *cause;
+       size_t                   size, len;
+       u_int                    width;
+
+       if ((s = cmd_find_session(ctx, NULL, 0)) == NULL)
+               return (-1);
+
+       if (!args_has(args, 'b')) {
+               if ((pb = paste_get_top(&global_buffers)) == NULL) {
+                       ctx->error(ctx, "no buffers");
+                       return (-1);
+               }
+       } else {
+               buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
+               if (cause != NULL) {
+                       ctx->error(ctx, "buffer %s", cause);
+                       xfree(cause);
+                       return (-1);
+               }
+
+               pb = paste_get_index(&global_buffers, buffer);
+               if (pb == NULL) {
+                       ctx->error(ctx, "no buffer %d", buffer);
+                       return (-1);
+               }
+       }
+
+       size = pb->size;
+       if (size > SIZE_MAX / 4 - 1)
+               size = SIZE_MAX / 4 - 1;
+       in = xmalloc(size * 4 + 1);
+       strvisx(in, pb->data, size, VIS_OCTAL|VIS_TAB);
+
+       width = s->sx;
+       if (ctx->cmdclient != NULL)
+               width = ctx->cmdclient->tty.sx;
+
+       buf = xmalloc(width + 1);
+       len = 0;
+
+       ptr = in;
+       do {
+               buf[len++] = *ptr++;
+
+               if (len == width || buf[len - 1] == '\n') {
+                       if (buf[len - 1] == '\n')
+                               len--;
+                       buf[len] = '\0';
+
+                       ctx->print(ctx, "%s", buf);
+                       len = 0;
+               }
+       } while (*ptr != '\0');
+
+       if (len != 0) {
+               buf[len] = '\0';
+               ctx->print(ctx, "%s", buf);
+       }
+       xfree(buf);
+
+       xfree(in);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-show-environment.c b/external/bsd/tmux/dist/cmd-show-environment.c
new file mode 100644 (file)
index 0000000..30f66d2
--- /dev/null
@@ -0,0 +1,66 @@
+/* $Id: cmd-show-environment.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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"
+
+/*
+ * Show environment.
+ */
+
+int    cmd_show_environment_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_show_environment_entry = {
+       "show-environment", "showenv",
+       "gt:", 0, 0,
+       "[-g] " CMD_TARGET_SESSION_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_show_environment_exec
+};
+
+int
+cmd_show_environment_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct session          *s;
+       struct environ          *env;
+       struct environ_entry    *envent;
+
+       if (args_has(self->args, 'g'))
+               env = &global_environ;
+       else {
+               if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL)
+                       return (-1);
+               env = &s->environ;
+       }
+
+       RB_FOREACH(envent, environ, env) {
+               if (envent->value != NULL)
+                       ctx->print(ctx, "%s=%s", envent->name, envent->value);
+               else
+                       ctx->print(ctx, "-%s", envent->name);
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-show-messages.c b/external/bsd/tmux/dist/cmd-show-messages.c
new file mode 100644 (file)
index 0000000..3500777
--- /dev/null
@@ -0,0 +1,64 @@
+/* $Id: cmd-show-messages.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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"
+
+/*
+ * Show client message log.
+ */
+
+int    cmd_show_messages_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_show_messages_entry = {
+       "show-messages", "showmsgs",
+       "t:", 0, 0,
+       CMD_TARGET_CLIENT_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_show_messages_exec
+};
+
+int
+cmd_show_messages_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct client           *c;
+       struct message_entry    *msg;
+       char                    *tim;
+       u_int                    i;
+
+       if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL)
+               return (-1);
+
+       for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) {
+               msg = &ARRAY_ITEM(&c->message_log, i);
+
+               tim = ctime(&msg->msg_time);
+               *strchr(tim, '\n') = '\0';
+
+               ctx->print(ctx, "%s %s", tim, msg->msg);
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-show-options.c b/external/bsd/tmux/dist/cmd-show-options.c
new file mode 100644 (file)
index 0000000..1d2ea9b
--- /dev/null
@@ -0,0 +1,97 @@
+/* $Id: cmd-show-options.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "gst:w", 0, 0,
+       "[-gsw] [-t target-session|target-window]",
+       0,
+       NULL,
+       NULL,
+       cmd_show_options_exec
+};
+
+const struct cmd_entry cmd_show_window_options_entry = {
+       "show-window-options", "showw",
+       "gt:", 0, 0,
+       "[-g] " CMD_TARGET_WINDOW_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_show_options_exec
+};
+
+int
+cmd_show_options_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args                             *args = self->args;
+       const struct options_table_entry        *table, *oe;
+       struct session                          *s;
+       struct winlink                          *wl;
+       struct options                          *oo;
+       struct options_entry                    *o;
+       const char                              *optval;
+
+       if (args_has(self->args, 's')) {
+               oo = &global_options;
+               table = server_options_table;
+       } else if (args_has(self->args, 'w') ||
+           self->entry == &cmd_show_window_options_entry) {
+               table = window_options_table;
+               if (args_has(self->args, 'g'))
+                       oo = &global_w_options;
+               else {
+                       wl = cmd_find_window(ctx, args_get(args, 't'), NULL);
+                       if (wl == NULL)
+                               return (-1);
+                       oo = &wl->window->options;
+               }
+       } else {
+               table = session_options_table;
+               if (args_has(self->args, 'g'))
+                       oo = &global_s_options;
+               else {
+                       s = cmd_find_session(ctx, args_get(args, 't'), 0);
+                       if (s == NULL)
+                               return (-1);
+                       oo = &s->options;
+               }
+       }
+
+       for (oe = table; oe->name != NULL; oe++) {
+               if ((o = options_find1(oo, oe->name)) == NULL)
+                       continue;
+               optval = options_table_print_entry(oe, o);
+               ctx->print(ctx, "%s %s", oe->name, optval);
+       }
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-source-file.c b/external/bsd/tmux/dist/cmd-source-file.c
new file mode 100644 (file)
index 0000000..ff65718
--- /dev/null
@@ -0,0 +1,74 @@
+/* $Id: cmd-source-file.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_source_file_entry = {
+       "source-file", "source",
+       "", 1, 1,
+       "path",
+       0,
+       NULL,
+       NULL,
+       cmd_source_file_exec
+};
+
+int
+cmd_source_file_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct causelist         causes;
+       char                    *cause;
+       struct window_pane      *wp;
+       int                      retval;
+       u_int                    i;
+
+       ARRAY_INIT(&causes);
+
+       retval = load_cfg(args->argv[0], ctx, &causes);
+       if (ARRAY_EMPTY(&causes))
+               return (retval);
+
+       if (retval == 1 && !RB_EMPTY(&sessions) && ctx->cmdclient != NULL) {
+               wp = RB_MIN(sessions, &sessions)->curw->window->active;
+               window_pane_set_mode(wp, &window_copy_mode);
+               window_copy_init_for_output(wp);
+               for (i = 0; i < ARRAY_LENGTH(&causes); i++) {
+                       cause = ARRAY_ITEM(&causes, i);
+                       window_copy_add(wp, "%s", cause);
+                       xfree(cause);
+               }
+       } else {
+               for (i = 0; i < ARRAY_LENGTH(&causes); i++) {
+                       cause = ARRAY_ITEM(&causes, i);
+                       ctx->print(ctx, "%s", cause);
+                       xfree(cause);
+               }
+       }
+       ARRAY_FREE(&causes);
+
+       return (retval);
+}
diff --git a/external/bsd/tmux/dist/cmd-split-window.c b/external/bsd/tmux/dist/cmd-split-window.c
new file mode 100644 (file)
index 0000000..2ccfab7
--- /dev/null
@@ -0,0 +1,152 @@
+/* $Id: cmd-split-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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).
+ */
+
+void   cmd_split_window_key_binding(struct cmd *, int);
+int    cmd_split_window_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_split_window_entry = {
+       "split-window", "splitw",
+       "dl:hp:Pt:v", 0, 1,
+       "[-dhvP] [-p percentage|-l size] [-t target-pane] [command]",
+       0,
+       cmd_split_window_key_binding,
+       NULL,
+       cmd_split_window_exec
+};
+
+void
+cmd_split_window_key_binding(struct cmd *self, int key)
+{
+       self->args = args_create(0);
+       if (key == '%')
+               args_set(self->args, 'h', NULL);
+}
+
+int
+cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct session          *s;
+       struct winlink          *wl;
+       struct window           *w;
+       struct window_pane      *wp, *new_wp = NULL;
+       struct environ           env;
+       char                    *cmd, *cwd, *cause;
+       const char              *shell;
+       u_int                    hlimit, paneidx;
+       int                      size, percentage;
+       enum layout_type         type;
+       struct layout_cell      *lc;
+
+       if ((wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp)) == NULL)
+               return (-1);
+       w = wl->window;
+
+       environ_init(&env);
+       environ_copy(&global_environ, &env);
+       environ_copy(&s->environ, &env);
+       server_fill_environ(s, &env);
+
+       if (args->argc == 0)
+               cmd = options_get_string(&s->options, "default-command");
+       else
+               cmd = args->argv[0];
+       cwd = options_get_string(&s->options, "default-path");
+       if (*cwd == '\0') {
+               if (ctx->cmdclient != NULL && ctx->cmdclient->cwd != NULL)
+                       cwd = ctx->cmdclient->cwd;
+               else
+                       cwd = s->cwd;
+       }
+
+       type = LAYOUT_TOPBOTTOM;
+       if (args_has(args, 'h'))
+               type = LAYOUT_LEFTRIGHT;
+
+       size = -1;
+       if (args_has(args, 'l')) {
+               size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
+               if (cause != NULL) {
+                       ctx->error(ctx, "size %s", cause);
+                       xfree(cause);
+                       return (-1);
+               }
+       } else if (args_has(args, 'p')) {
+               percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause);
+               if (cause != NULL) {
+                       ctx->error(ctx, "percentage %s", cause);
+                       xfree(cause);
+                       return (-1);
+               }
+               if (type == LAYOUT_TOPBOTTOM)
+                       size = (wp->sy * percentage) / 100;
+               else
+                       size = (wp->sx * percentage) / 100;
+       }
+       hlimit = options_get_number(&s->options, "history-limit");
+
+       shell = options_get_string(&s->options, "default-shell");
+       if (*shell == '\0' || areshell(shell))
+               shell = _PATH_BSHELL;
+
+       if ((lc = layout_split_pane(wp, type, size)) == NULL) {
+               cause = xstrdup("pane too small");
+               goto error;
+       }
+       new_wp = window_add_pane(w, hlimit);
+       if (window_pane_spawn(
+           new_wp, cmd, shell, cwd, &env, s->tio, &cause) != 0)
+               goto error;
+       layout_assign_pane(lc, new_wp);
+
+       server_redraw_window(w);
+
+       if (!args_has(args, 'd')) {
+               window_set_active_pane(w, new_wp);
+               session_select(s, wl->idx);
+               server_redraw_session(s);
+       } else
+               server_status_session(s);
+
+       environ_free(&env);
+
+       if (args_has(args, 'P')) {
+               paneidx = window_pane_index(wl->window, new_wp);
+               ctx->print(ctx, "%s:%u.%u", s->name, wl->idx, paneidx);
+       }
+       return (0);
+
+error:
+       environ_free(&env);
+       if (new_wp != NULL)
+               window_remove_pane(w, new_wp);
+       ctx->error(ctx, "create pane failed: %s", cause);
+       xfree(cause);
+       return (-1);
+}
diff --git a/external/bsd/tmux/dist/cmd-start-server.c b/external/bsd/tmux/dist/cmd-start-server.c
new file mode 100644 (file)
index 0000000..3a7d841
--- /dev/null
@@ -0,0 +1,44 @@
+/* $Id: cmd-start-server.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "", 0, 0,
+       "",
+       CMD_STARTSERVER,
+       NULL,
+       NULL,
+       cmd_start_server_exec
+};
+
+/* ARGSUSED */
+int
+cmd_start_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx)
+{
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-string.c b/external/bsd/tmux/dist/cmd-string.c
new file mode 100644 (file)
index 0000000..a1405c4
--- /dev/null
@@ -0,0 +1,349 @@
+/* $Id: cmd-string.c,v 1.4 2011/10/07 10:38:02 joerg 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 <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Parse a command from a string.
+ */
+
+int    cmd_string_getc(const char *, size_t *);
+void   cmd_string_ungetc(size_t *);
+char   *cmd_string_string(const char *, size_t *, char, int);
+char   *cmd_string_variable(const char *, size_t *);
+char   *cmd_string_expand_tilde(const char *, size_t *);
+
+int
+cmd_string_getc(const char *s, size_t *p)
+{
+       const char      *ucs = s;
+
+       if (ucs[*p] == '\0')
+               return (EOF);
+       return (u_char)(ucs[(*p)++]);
+}
+
+void
+cmd_string_ungetc(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, i, argc, rval;
+       char          **argv, *buf, *t;
+       const char     *whitespace, *equals;
+       size_t          len, len2;
+
+       argv = NULL;
+       argc = 0;
+
+       buf = NULL;
+       len = 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;
+                       len2 = strlen(t);
+                       buf = xrealloc(buf, 1, len + len2 + 1);
+                       memcpy(buf + len, t, len2 + 1);
+                       len += len2;
+                       xfree(t);
+                       break;
+               case '"':
+                       if ((t = cmd_string_string(s, &p, '"', 1)) == NULL)
+                               goto error;
+                       len2 = strlen(t);
+                       buf = xrealloc(buf, 1, len + len2 + 1);
+                       memcpy(buf + len, t, len2 + 1);
+                       len += len2;
+                       xfree(t);
+                       break;
+               case '$':
+                       if ((t = cmd_string_variable(s, &p)) == NULL)
+                               goto error;
+                       len2 = strlen(t);
+                       buf = xrealloc(buf, 1, len + len2 + 1);
+                       strlcpy(buf + len, t, len2 + 1);
+                       len += len2;
+                       xfree(t);
+                       break;
+               case '#':
+                       /* Comment: discard rest of line. */
+                       while ((ch = cmd_string_getc(s, &p)) != EOF)
+                               ;
+                       /* FALLTHROUGH */
+               case EOF:
+               case ' ':
+               case '\t':
+                       if (buf != NULL) {
+                               buf = xrealloc(buf, 1, len + 1);
+                               buf[len] = '\0';
+
+                               argv = xrealloc(argv, argc + 1, sizeof *argv);
+                               argv[argc++] = buf;
+
+                               buf = NULL;
+                               len = 0;
+                       }
+
+                       if (ch != EOF)
+                               break;
+
+                       while (argc != 0) {
+                               equals = strchr(argv[0], '=');
+                               whitespace = argv[0] + strcspn(argv[0], " \t");
+                               if (equals == NULL || equals > whitespace)
+                                       break;
+                               environ_put(&global_environ, argv[0]);
+                               argc--;
+                               memmove(argv, argv + 1, argc * (sizeof *argv));
+                       }
+                       if (argc == 0)
+                               goto out;
+
+                       *cmdlist = cmd_list_parse(argc, argv, cause);
+                       if (*cmdlist == NULL)
+                               goto out;
+
+                       rval = 0;
+                       goto out;
+               case '~':
+                       if (buf == NULL) {
+                               if ((t = cmd_string_expand_tilde(s, &p)) == NULL)
+                                       goto error;
+                               len2 = strlen(t);
+                               buf = xrealloc(buf, 1, len + len2 + 1);
+                               memcpy(buf + len, t, len2 + 1);
+                               len += len2;
+                               xfree(t);
+                               break;
+                       }
+                       /* FALLTHROUGH */
+               default:
+                       if (len >= SIZE_MAX - 2)
+                               goto error;
+
+                       buf = xrealloc(buf, 1, len + 1);
+                       buf[len++] = ch;
+                       break;
+               }
+       }
+
+error:
+       xasprintf(cause, "invalid or unknown command: %s", s);
+
+out:
+       if (buf != NULL)
+               xfree(buf);
+
+       if (argv != NULL) {
+               for (i = 0; i < argc; i++)
+                       xfree(argv[i]);
+               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, len2;
+
+       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 'e':
+                               ch = '\033';
+                               break;
+                       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;
+                       len2 = strlen(t);
+                       buf = xrealloc(buf, 1, len + len2 + 1);
+                       memcpy(buf + len, t, len2 + 1);
+                       len += len2;
+                       xfree(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;
+       struct environ_entry   *envent;
+
+#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(p); /* ch */
+
+       buf = xrealloc(buf, 1, len + 1);
+       buf[len] = '\0';
+
+       envent = environ_find(&global_environ, buf);
+       xfree(buf);
+       if (envent == NULL)
+               return (xstrdup(""));
+       return (xstrdup(envent->value));
+
+error:
+       if (buf != NULL)
+               xfree(buf);
+       return (NULL);
+}
+
+char *
+cmd_string_expand_tilde(const char *s, size_t *p)
+{
+       struct passwd           *pw;
+       struct environ_entry    *envent;
+       char                    *home, *path, *username;
+
+       home = NULL;
+       if (cmd_string_getc(s, p) == '/') {
+               envent = environ_find(&global_environ, "HOME");
+               if (envent != NULL && *envent->value != '\0')
+                       home = envent->value;
+               else if ((pw = getpwuid(getuid())) != NULL)
+                       home = pw->pw_dir;
+       } else {
+               cmd_string_ungetc(p);
+               if ((username = cmd_string_string(s, p, '/', 0)) == NULL)
+                       return (NULL);
+               if ((pw = getpwnam(username)) != NULL)
+                       home = pw->pw_dir;
+               xfree(username);
+       }
+       if (home == NULL)
+               return (NULL);
+
+       xasprintf(&path, "%s/", home);
+       return (path);
+}
diff --git a/external/bsd/tmux/dist/cmd-suspend-client.c b/external/bsd/tmux/dist/cmd-suspend-client.c
new file mode 100644 (file)
index 0000000..b36983a
--- /dev/null
@@ -0,0 +1,56 @@
+/* $Id: cmd-suspend-client.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 *);
+
+const struct cmd_entry cmd_suspend_client_entry = {
+       "suspend-client", "suspendc",
+       "t:", 0, 0,
+       CMD_TARGET_CLIENT_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_suspend_client_exec
+};
+
+int
+cmd_suspend_client_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct client   *c;
+
+       if ((c = cmd_find_client(ctx, args_get(args, 't'))) == 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/external/bsd/tmux/dist/cmd-swap-pane.c b/external/bsd/tmux/dist/cmd-swap-pane.c
new file mode 100644 (file)
index 0000000..79fe495
--- /dev/null
@@ -0,0 +1,142 @@
+/* $Id: cmd-swap-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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.
+ */
+
+void   cmd_swap_pane_key_binding(struct cmd *, int);
+int    cmd_swap_pane_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_swap_pane_entry = {
+       "swap-pane", "swapp",
+       "dDs:t:U", 0, 0,
+       "[-dDU] " CMD_SRCDST_PANE_USAGE,
+       0,
+       cmd_swap_pane_key_binding,
+       NULL,
+       cmd_swap_pane_exec
+};
+
+void
+cmd_swap_pane_key_binding(struct cmd *self, int key)
+{
+       self->args = args_create(0);
+       if (key == '{')
+               args_set(self->args, 'U', NULL);
+       else if (key == '}')
+               args_set(self->args, 'D', NULL);
+}
+
+int
+cmd_swap_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct winlink          *src_wl, *dst_wl;
+       struct window           *src_w, *dst_w;
+       struct window_pane      *tmp_wp, *src_wp, *dst_wp;
+       struct layout_cell      *src_lc, *dst_lc;
+       u_int                    sx, sy, xoff, yoff;
+
+       dst_wl = cmd_find_pane(ctx, args_get(args, 't'), NULL, &dst_wp);
+       if (dst_wl == NULL)
+               return (-1);
+       dst_w = dst_wl->window;
+
+       if (!args_has(args, 's')) {
+               src_w = dst_w;
+               if (args_has(self->args, 'D')) {
+                       src_wp = TAILQ_NEXT(dst_wp, entry);
+                       if (src_wp == NULL)
+                               src_wp = TAILQ_FIRST(&dst_w->panes);
+               } else if (args_has(self->args, 'U')) {
+                       src_wp = TAILQ_PREV(dst_wp, window_panes, entry);
+                       if (src_wp == NULL)
+                               src_wp = TAILQ_LAST(&dst_w->panes, window_panes);
+               } else
+                       return (0);
+       } else {
+               src_wl = cmd_find_pane(ctx, args_get(args, 's'), NULL, &src_wp);
+               if (src_wl == NULL)
+                       return (-1);
+               src_w = src_wl->window;
+       }
+
+       if (src_wp == dst_wp)
+               return (0);
+
+       tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry);
+       TAILQ_REMOVE(&dst_w->panes, dst_wp, entry);
+       TAILQ_REPLACE(&src_w->panes, src_wp, dst_wp, entry);
+       if (tmp_wp == src_wp)
+               tmp_wp = dst_wp;
+       if (tmp_wp == NULL)
+               TAILQ_INSERT_HEAD(&dst_w->panes, src_wp, entry);
+       else
+               TAILQ_INSERT_AFTER(&dst_w->panes, tmp_wp, src_wp, entry);
+
+       src_lc = src_wp->layout_cell;
+       dst_lc = dst_wp->layout_cell;
+       src_lc->wp = dst_wp;
+       dst_wp->layout_cell = src_lc;
+       dst_lc->wp = src_wp;
+       src_wp->layout_cell = dst_lc;
+
+       src_wp->window = dst_w;
+       dst_wp->window = src_w;
+
+       sx = src_wp->sx; sy = src_wp->sy;
+       xoff = src_wp->xoff; yoff = src_wp->yoff;
+       src_wp->xoff = dst_wp->xoff; src_wp->yoff = dst_wp->yoff;
+       window_pane_resize(src_wp, dst_wp->sx, dst_wp->sy);
+       dst_wp->xoff = xoff; dst_wp->yoff = yoff;
+       window_pane_resize(dst_wp, sx, sy);
+
+       if (!args_has(self->args, 'd')) {
+               if (src_w != dst_w) {
+                       window_set_active_pane(src_w, dst_wp);
+                       window_set_active_pane(dst_w, src_wp);
+               } else {
+                       tmp_wp = dst_wp;
+                       if (!window_pane_visible(tmp_wp))
+                               tmp_wp = src_wp;
+                       window_set_active_pane(src_w, tmp_wp);
+               }
+       } else {
+               if (src_w->active == src_wp)
+                       window_set_active_pane(src_w, dst_wp);
+               if (dst_w->active == dst_wp)
+                       window_set_active_pane(dst_w, src_wp);
+       }
+       if (src_w != dst_w) {
+               if (src_w->last == src_wp)
+                       src_w->last = NULL;
+               if (dst_w->last == dst_wp)
+                       dst_w->last = NULL;
+       }
+       server_redraw_window(src_w);
+       server_redraw_window(dst_w);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-swap-window.c b/external/bsd/tmux/dist/cmd-swap-window.c
new file mode 100644 (file)
index 0000000..3241e3a
--- /dev/null
@@ -0,0 +1,87 @@
+/* $Id: cmd-swap-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "ds:t:", 0, 0,
+       "[-d] " CMD_SRCDST_WINDOW_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_swap_window_exec
+};
+
+int
+cmd_swap_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       const char              *target_src, *target_dst;
+       struct session          *src, *dst;
+       struct session_group    *sg_src, *sg_dst;
+       struct winlink          *wl_src, *wl_dst;
+       struct window           *w;
+
+       target_src = args_get(args, 's');
+       if ((wl_src = cmd_find_window(ctx, target_src, &src)) == NULL)
+               return (-1);
+       target_dst = args_get(args, 't');
+       if ((wl_dst = cmd_find_window(ctx, target_dst, &dst)) == NULL)
+               return (-1);
+
+       sg_src = session_group_find(src);
+       sg_dst = session_group_find(dst);
+       if (src != dst &&
+           sg_src != NULL && sg_dst != NULL && sg_src == sg_dst) {
+               ctx->error(ctx, "can't move window, sessions are grouped");
+               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 (!args_has(self->args, 'd')) {
+               session_select(dst, wl_dst->idx);
+               if (src != dst)
+                       session_select(src, wl_src->idx);
+       }
+       session_group_synchronize_from(src);
+       server_redraw_session_group(src);
+       if (src != dst) {
+               session_group_synchronize_from(dst);
+               server_redraw_session_group(dst);
+       }
+       recalculate_sizes();
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-switch-client.c b/external/bsd/tmux/dist/cmd-switch-client.c
new file mode 100644 (file)
index 0000000..6f16278
--- /dev/null
@@ -0,0 +1,103 @@
+/* $Id: cmd-switch-client.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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.
+ */
+
+void   cmd_switch_client_key_binding(struct cmd *, int);
+int    cmd_switch_client_exec(struct cmd *, struct cmd_ctx *);
+
+const struct cmd_entry cmd_switch_client_entry = {
+       "switch-client", "switchc",
+       "lc:npt:", 0, 0,
+       "[-lnp] [-c target-client] [-t target-session]",
+       0,
+       cmd_switch_client_key_binding,
+       NULL,
+       cmd_switch_client_exec
+};
+
+void
+cmd_switch_client_key_binding(struct cmd *self, int key)
+{
+       self->args = args_create(0);
+       switch (key) {
+       case '(':
+               args_set(self->args, 'p', NULL);
+               break;
+       case ')':
+               args_set(self->args, 'n', NULL);
+               break;
+       case 'L':
+               args_set(self->args, 'l', NULL);
+               break;
+       }
+}
+
+int
+cmd_switch_client_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args     *args = self->args;
+       struct client   *c;
+       struct session  *s;
+
+       if ((c = cmd_find_client(ctx, args_get(args, 'c'))) == NULL)
+               return (-1);
+
+       s = NULL;
+       if (args_has(args, 'n')) {
+               if ((s = session_next_session(c->session)) == NULL) {
+                       ctx->error(ctx, "can't find next session");
+                       return (-1);
+               }
+       } else if (args_has(args, 'p')) {
+               if ((s = session_previous_session(c->session)) == NULL) {
+                       ctx->error(ctx, "can't find previous session");
+                       return (-1);
+               }
+       } else if (args_has(args, 'l')) {
+               if (c->last_session != NULL && session_alive(c->last_session))
+                       s = c->last_session;
+               if (s == NULL) {
+                       ctx->error(ctx, "can't find last session");
+                       return (-1);
+               }
+       } else
+               s = cmd_find_session(ctx, args_get(args, 't'), 0);
+       if (s == NULL)
+               return (-1);
+
+       if (c->session != NULL)
+               c->last_session = c->session;
+       c->session = s;
+       session_update_activity(s);
+
+       recalculate_sizes();
+       server_check_unattached();
+       server_redraw_client(c);
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-unbind-key.c b/external/bsd/tmux/dist/cmd-unbind-key.c
new file mode 100644 (file)
index 0000000..87f8736
--- /dev/null
@@ -0,0 +1,105 @@
+/* $Id: cmd-unbind-key.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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_check(struct args *);
+int    cmd_unbind_key_exec(struct cmd *, struct cmd_ctx *);
+
+int    cmd_unbind_key_table(struct cmd *, struct cmd_ctx *, int);
+
+const struct cmd_entry cmd_unbind_key_entry = {
+       "unbind-key", "unbind",
+       "acnt:", 0, 1,
+       "[-acn] [-t key-table] key",
+       0,
+       NULL,
+       cmd_unbind_key_check,
+       cmd_unbind_key_exec
+};
+
+int
+cmd_unbind_key_check(struct args *args)
+{
+       if (args_has(args, 'a') && (args->argc != 0 || args_has(args, 't')))
+           return (-1);
+       if (!args_has(args, 'a') && args->argc != 1)
+               return (-1);
+       return (0);
+}
+
+int
+cmd_unbind_key_exec(struct cmd *self, unused struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct key_binding      *bd;
+       int                      key;
+
+       if (args_has(args, 'a')) {
+               while (!SPLAY_EMPTY(&key_bindings)) {
+                       bd = SPLAY_ROOT(&key_bindings);
+                       SPLAY_REMOVE(key_bindings, &key_bindings, bd);
+                       cmd_list_free(bd->cmdlist);
+                       xfree(bd);
+               }
+               return (0);
+       }
+
+       key = key_string_lookup_string(args->argv[0]);
+       if (key == KEYC_NONE) {
+               ctx->error(ctx, "unknown key: %s", args->argv[0]);
+               return (-1);
+       }
+
+       if (args_has(args, 't'))
+               return (cmd_unbind_key_table(self, ctx, key));
+
+       if (!args_has(args, 'n'))
+               key |= KEYC_PREFIX;
+       key_bindings_remove(key);
+       return (0);
+}
+
+int
+cmd_unbind_key_table(struct cmd *self, struct cmd_ctx *ctx, int key)
+{
+       struct args                     *args = self->args;
+       const char                      *tablename;
+       const struct mode_key_table     *mtab;
+       struct mode_key_binding         *mbind, mtmp;
+
+       tablename = args_get(args, 't');
+       if ((mtab = mode_key_findtable(tablename)) == NULL) {
+               ctx->error(ctx, "unknown key table: %s", tablename);
+               return (-1);
+       }
+
+       mtmp.key = key;
+       mtmp.mode = !!args_has(args, 'c');
+       if ((mbind = SPLAY_FIND(mode_key_tree, mtab->tree, &mtmp)) != NULL) {
+               SPLAY_REMOVE(mode_key_tree, mtab->tree, mbind);
+               xfree(mbind);
+       }
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd-unlink-window.c b/external/bsd/tmux/dist/cmd-unlink-window.c
new file mode 100644 (file)
index 0000000..e3f570c
--- /dev/null
@@ -0,0 +1,70 @@
+/* $Id: cmd-unlink-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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",
+       "kt:", 0, 0,
+       "[-k] " CMD_TARGET_WINDOW_USAGE,
+       0,
+       NULL,
+       NULL,
+       cmd_unlink_window_exec
+};
+
+int
+cmd_unlink_window_exec(struct cmd *self, struct cmd_ctx *ctx)
+{
+       struct args             *args = self->args;
+       struct winlink          *wl;
+       struct window           *w;
+       struct session          *s, *s2;
+       struct session_group    *sg;
+       u_int                    references;
+
+       if ((wl = cmd_find_window(ctx, args_get(args, 't'), &s)) == NULL)
+               return (-1);
+       w = wl->window;
+
+       sg = session_group_find(s);
+       if (sg != NULL) {
+               references = 0;
+               TAILQ_FOREACH(s2, &sg->sessions, gentry)
+                       references++;
+       } else
+               references = 1;
+
+       if (!args_has(self->args, 'k') && w->references == references) {
+               ctx->error(ctx, "window is only linked to one session");
+               return (-1);
+       }
+
+       server_unlink_window(s, wl);
+       recalculate_sizes();
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/cmd.c b/external/bsd/tmux/dist/cmd.c
new file mode 100644 (file)
index 0000000..19b234b
--- /dev/null
@@ -0,0 +1,1214 @@
+/* $Id: cmd.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 <fnmatch.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_capture_pane_entry,
+       &cmd_choose_buffer_entry,
+       &cmd_choose_client_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_mode_entry,
+       &cmd_delete_buffer_entry,
+       &cmd_detach_client_entry,
+       &cmd_display_message_entry,
+       &cmd_display_panes_entry,
+       &cmd_find_window_entry,
+       &cmd_has_session_entry,
+       &cmd_if_shell_entry,
+       &cmd_join_pane_entry,
+       &cmd_kill_pane_entry,
+       &cmd_kill_server_entry,
+       &cmd_kill_session_entry,
+       &cmd_kill_window_entry,
+       &cmd_last_pane_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_panes_entry,
+       &cmd_list_sessions_entry,
+       &cmd_list_windows_entry,
+       &cmd_load_buffer_entry,
+       &cmd_lock_client_entry,
+       &cmd_lock_server_entry,
+       &cmd_lock_session_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_pipe_pane_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_pane_entry,
+       &cmd_respawn_window_entry,
+       &cmd_rotate_window_entry,
+       &cmd_run_shell_entry,
+       &cmd_save_buffer_entry,
+       &cmd_select_layout_entry,
+       &cmd_select_pane_entry,
+       &cmd_select_window_entry,
+       &cmd_send_keys_entry,
+       &cmd_send_prefix_entry,
+       &cmd_server_info_entry,
+       &cmd_set_buffer_entry,
+       &cmd_set_environment_entry,
+       &cmd_set_option_entry,
+       &cmd_set_window_option_entry,
+       &cmd_show_buffer_entry,
+       &cmd_show_environment_entry,
+       &cmd_show_messages_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,
+       NULL
+};
+
+struct session *cmd_choose_session_list(struct sessionslist *);
+struct session *cmd_choose_session(int);
+struct client  *cmd_choose_client(struct clients *);
+struct client  *cmd_lookup_client(const char *);
+struct session *cmd_lookup_session(const char *, int *);
+struct winlink *cmd_lookup_window(struct session *, const char *, int *);
+int             cmd_lookup_index(struct session *, const char *, int *);
+struct window_pane *cmd_lookup_paneid(const char *);
+struct session *cmd_pane_session(struct cmd_ctx *,
+                   struct window_pane *, struct winlink **);
+struct winlink *cmd_find_window_offset(const char *, struct session *, int *);
+int             cmd_find_index_offset(const char *, struct session *, int *);
+struct window_pane *cmd_find_pane_offset(const char *, struct winlink *);
+
+int
+cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
+{
+       size_t  arglen;
+       int     i;
+
+       *buf = '\0';
+       for (i = 0; i < argc; i++) {
+               if (strlcpy(buf, argv[i], len) >= len)
+                       return (-1);
+               arglen = strlen(argv[i]) + 1;
+               buf += arglen;
+               len -= arglen;
+       }
+
+       return (0);
+}
+
+int
+cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
+{
+       int     i;
+       size_t  arglen;
+
+       if (argc == 0)
+               return (0);
+       *argv = xcalloc(argc, sizeof **argv);
+
+       buf[len - 1] = '\0';
+       for (i = 0; i < argc; i++) {
+               if (len == 0) {
+                       cmd_free_argv(argc, *argv);
+                       return (-1);
+               }
+
+               arglen = strlen(buf) + 1;
+               (*argv)[i] = xstrdup(buf);
+               buf += arglen;
+               len -= arglen;
+       }
+
+       return (0);
+}
+
+char **
+cmd_copy_argv(int argc, char *const *argv)
+{
+       char    **new_argv;
+       int       i;
+
+       if (argc == 0)
+               return (NULL);
+       new_argv = xcalloc(argc, sizeof *new_argv);
+       for (i = 0; i < argc; i++) {
+               if (argv[i] != NULL)
+                       new_argv[i] = xstrdup(argv[i]);
+       }
+       return (new_argv);
+}
+
+void
+cmd_free_argv(int argc, char **argv)
+{
+       int     i;
+
+       if (argc == 0)
+               return;
+       for (i = 0; i < argc; i++) {
+               if (argv[i] != NULL)
+                       xfree(argv[i]);
+       }
+       xfree(argv);
+}
+
+struct cmd *
+cmd_parse(int argc, char **argv, char **cause)
+{
+       const struct cmd_entry **entryp, *entry;
+       struct cmd              *cmd;
+       struct args             *args;
+       char                     s[BUFSIZ];
+       int                      ambiguous = 0;
+
+       *cause = NULL;
+       if (argc == 0) {
+               xasprintf(cause, "no command");
+               return (NULL);
+       }
+
+       entry = NULL;
+       for (entryp = cmd_table; *entryp != NULL; entryp++) {
+               if ((*entryp)->alias != NULL &&
+                   strcmp((*entryp)->alias, argv[0]) == 0) {
+                       ambiguous = 0;
+                       entry = *entryp;
+                       break;
+               }
+
+               if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
+                       continue;
+               if (entry != NULL)
+                       ambiguous = 1;
+               entry = *entryp;
+
+               /* Bail now if an exact match. */
+               if (strcmp(entry->name, argv[0]) == 0)
+                       break;
+       }
+       if (ambiguous)
+               goto ambiguous;
+       if (entry == NULL) {
+               xasprintf(cause, "unknown command: %s", argv[0]);
+               return (NULL);
+       }
+
+       args = args_parse(entry->args_template, argc, argv);
+       if (args == NULL)
+               goto usage;
+       if (entry->args_lower != -1 && args->argc < entry->args_lower)
+               goto usage;
+       if (entry->args_upper != -1 && args->argc > entry->args_upper)
+               goto usage;
+       if (entry->check != NULL && entry->check(args) != 0)
+               goto usage;
+
+       cmd = xmalloc(sizeof *cmd);
+       cmd->entry = entry;
+       cmd->args = args;
+       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:
+       if (args != NULL)
+               args_free(args);
+       xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
+       return (NULL);
+}
+
+int
+cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx)
+{
+       return (cmd->entry->exec(cmd, ctx));
+}
+
+void
+cmd_free(struct cmd *cmd)
+{
+       if (cmd->args != NULL)
+               args_free(cmd->args);
+       xfree(cmd);
+}
+
+size_t
+cmd_print(struct cmd *cmd, char *buf, size_t len)
+{
+       size_t  off, used;
+
+       off = xsnprintf(buf, len, "%s ", cmd->entry->name);
+       if (off < len) {
+               used = args_print(cmd->args, buf + off, len - off);
+               if (used == 0)
+                       buf[off - 1] = '\0';
+               else {
+                       off += used;
+                       buf[off] = '\0';
+               }
+       }
+       return (off);
+}
+
+/*
+ * Figure out the current session. Use: 1) the current session, if the command
+ * context has one; 2) the most recently used session containing the pty of the
+ * calling client, if any; 3) the session specified in the TMUX variable from
+ * the environment (as passed from the client); 4) the most recently used
+ * session from all sessions.
+ */
+struct session *
+cmd_current_session(struct cmd_ctx *ctx, int prefer_unattached)
+{
+       struct msg_command_data *data = ctx->msgdata;
+       struct client           *c = ctx->cmdclient;
+       struct session          *s;
+       struct sessionslist      ss;
+       struct winlink          *wl;
+       struct window_pane      *wp;
+       int                      found;
+
+       if (ctx->curclient != NULL && ctx->curclient->session != NULL)
+               return (ctx->curclient->session);
+
+       /*
+        * If the name of the calling client's pty is know, build a list of the
+        * sessions that contain it and if any choose either the first or the
+        * newest.
+        */
+       if (c != NULL && c->tty.path != NULL) {
+               ARRAY_INIT(&ss);
+               RB_FOREACH(s, sessions, &sessions) {
+                       found = 0;
+                       RB_FOREACH(wl, winlinks, &s->windows) {
+                               TAILQ_FOREACH(wp, &wl->window->panes, entry) {
+                                       if (strcmp(wp->tty, c->tty.path) == 0) {
+                                               found = 1;
+                                               break;
+                                       }
+                               }
+                               if (found)
+                                       break;
+                       }
+                       if (found)
+                               ARRAY_ADD(&ss, s);
+               }
+
+               s = cmd_choose_session_list(&ss);
+               ARRAY_FREE(&ss);
+               if (s != NULL)
+                       return (s);
+       }
+
+       /* Use the session from the TMUX environment variable. */
+       if (data != NULL && data->pid == getpid() && data->idx != -1) {
+               s = session_find_by_index(data->idx);
+               if (s != NULL)
+                       return (s);
+       }
+
+       return (cmd_choose_session(prefer_unattached));
+}
+
+/*
+ * Find the most recently used session, preferring unattached if the flag is
+ * set.
+ */
+struct session *
+cmd_choose_session(int prefer_unattached)
+{
+       struct session  *s, *sbest;
+       struct timeval  *tv = NULL;
+
+       sbest = NULL;
+       RB_FOREACH(s, sessions, &sessions) {
+               if (tv == NULL || timercmp(&s->activity_time, tv, >) ||
+                   (prefer_unattached &&
+                   !(sbest->flags & SESSION_UNATTACHED) &&
+                   (s->flags & SESSION_UNATTACHED))) {
+                       sbest = s;
+                       tv = &s->activity_time;
+               }
+       }
+
+       return (sbest);
+}
+
+/* Find the most recently used session from a list. */
+struct session *
+cmd_choose_session_list(struct sessionslist *ss)
+{
+       struct session  *s, *sbest;
+       struct timeval  *tv = NULL;
+       u_int            i;
+
+       sbest = NULL;
+       for (i = 0; i < ARRAY_LENGTH(ss); i++) {
+               if ((s = ARRAY_ITEM(ss, i)) == NULL)
+                       continue;
+
+               if (tv == NULL || timercmp(&s->activity_time, tv, >)) {
+                       sbest = s;
+                       tv = &s->activity_time;
+               }
+       }
+
+       return (sbest);
+}
+
+/*
+ * Find the current client. First try the current client if set, then pick the
+ * most recently used of the clients attached to the current session if any,
+ * then of all clients.
+ */
+struct client *
+cmd_current_client(struct cmd_ctx *ctx)
+{
+       struct session          *s;
+       struct client           *c;
+       struct clients           cc;
+       u_int                    i;
+
+       if (ctx->curclient != NULL)
+               return (ctx->curclient);
+
+       /*
+        * No current client set. Find the current session and return the
+        * newest of its clients.
+        */
+       s = cmd_current_session(ctx, 0);
+       if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
+               ARRAY_INIT(&cc);
+               for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+                       if ((c = ARRAY_ITEM(&clients, i)) == NULL)
+                               continue;
+                       if (s == c->session)
+                               ARRAY_ADD(&cc, c);
+               }
+
+               c = cmd_choose_client(&cc);
+               ARRAY_FREE(&cc);
+               if (c != NULL)
+                       return (c);
+       }
+
+       return (cmd_choose_client(&clients));
+}
+
+/* Choose the most recently used client from a list. */
+struct client *
+cmd_choose_client(struct clients *cc)
+{
+       struct client   *c, *cbest;
+       struct timeval  *tv = NULL;
+       u_int            i;
+
+       cbest = NULL;
+       for (i = 0; i < ARRAY_LENGTH(cc); i++) {
+               if ((c = ARRAY_ITEM(cc, i)) == NULL)
+                       continue;
+               if (c->session == NULL)
+                       continue;
+
+               if (tv == NULL || timercmp(&c->activity_time, tv, >)) {
+                       cbest = c;
+                       tv = &c->activity_time;
+               }
+       }
+
+       return (cbest);
+}
+
+/* Find the target client or report an error and return NULL. */
+struct client *
+cmd_find_client(struct cmd_ctx *ctx, const char *arg)
+{
+       struct client   *c;
+       char            *tmparg;
+       size_t           arglen;
+
+       /* A NULL argument means the current client. */
+       if (arg == NULL)
+               return (cmd_current_client(ctx));
+       tmparg = xstrdup(arg);
+
+       /* Trim a single trailing colon if any. */
+       arglen = strlen(tmparg);
+       if (arglen != 0 && tmparg[arglen - 1] == ':')
+               tmparg[arglen - 1] = '\0';
+
+       /* Find the client, if any. */
+       c = cmd_lookup_client(tmparg);
+
+       /* If no client found, report an error. */
+       if (c == NULL)
+               ctx->error(ctx, "client not found: %s", tmparg);
+
+       xfree(tmparg);
+       return (c);
+}
+
+/*
+ * Lookup a client by device path. Either of a full match and a match without a
+ * leading _PATH_DEV ("/dev/") is accepted.
+ */
+struct client *
+cmd_lookup_client(const char *name)
+{
+       struct client   *c;
+       const char      *path;
+       u_int            i;
+
+       for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+               c = ARRAY_ITEM(&clients, i);
+               if (c == NULL || c->session == NULL)
+                       continue;
+               path = c->tty.path;
+
+               /* Check for exact matches. */
+               if (strcmp(name, path) == 0)
+                       return (c);
+
+               /* Check without leading /dev if present. */
+               if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0)
+                       continue;
+               if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0)
+                       return (c);
+       }
+
+       return (NULL);
+}
+
+/* Lookup a session by name. If no session is found, NULL is returned. */
+struct session *
+cmd_lookup_session(const char *name, int *ambiguous)
+{
+       struct session  *s, *sfound;
+
+       *ambiguous = 0;
+
+       /*
+        * Look for matches. First look for exact matches - session names must
+        * be unique so an exact match can't be ambigious and can just be
+        * returned.
+        */
+       if ((s = session_find(name)) != NULL)
+               return (s);
+
+       /*
+        * Otherwise look for partial matches, returning early if it is found to
+        * be ambiguous.
+        */
+       sfound = NULL;
+       RB_FOREACH(s, sessions, &sessions) {
+               if (strncmp(name, s->name, strlen(name)) == 0 ||
+                   fnmatch(name, s->name, 0) == 0) {
+                       if (sfound != NULL) {
+                               *ambiguous = 1;
+                               return (NULL);
+                       }
+                       sfound = s;
+               }
+       }
+       return (sfound);
+}
+
+/*
+ * Lookup a window or return -1 if not found or ambigious. First try as an
+ * index and if invalid, use fnmatch or leading prefix. Return NULL but fill in
+ * idx if the window index is a valid number but there is no window with that
+ * index.
+ */
+struct winlink *
+cmd_lookup_window(struct session *s, const char *name, int *ambiguous)
+{
+       struct winlink  *wl, *wlfound;
+       const char      *errstr;
+       u_int            idx;
+
+       *ambiguous = 0;
+
+       /* First see if this is a valid window index in this session. */
+       idx = strtonum(name, 0, INT_MAX, &errstr);
+       if (errstr == NULL) {
+               if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL)
+                       return (wl);
+       }
+
+       /* Look for exact matches, error if more than one. */
+       wlfound = NULL;
+       RB_FOREACH(wl, winlinks, &s->windows) {
+               if (strcmp(name, wl->window->name) == 0) {
+                       if (wlfound != NULL) {
+                               *ambiguous = 1;
+                               return (NULL);
+                       }
+                       wlfound = wl;
+               }
+       }
+       if (wlfound != NULL)
+               return (wlfound);
+
+       /* Now look for pattern matches, again error if multiple. */
+       wlfound = NULL;
+       RB_FOREACH(wl, winlinks, &s->windows) {
+               if (strncmp(name, wl->window->name, strlen(name)) == 0 ||
+                   fnmatch(name, wl->window->name, 0) == 0) {
+                       if (wlfound != NULL) {
+                               *ambiguous = 1;
+                               return (NULL);
+                       }
+                       wlfound = wl;
+               }
+       }
+       if (wlfound != NULL)
+               return (wlfound);
+
+       return (NULL);
+}
+
+/*
+ * Find a window index - if the window doesn't exist, check if it is a
+ * potential index and return it anyway.
+ */
+int
+cmd_lookup_index(struct session *s, const char *name, int *ambiguous)
+{
+       struct winlink  *wl;
+       const char      *errstr;
+       u_int            idx;
+
+       if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL)
+               return (wl->idx);
+       if (*ambiguous)
+               return (-1);
+
+       idx = strtonum(name, 0, INT_MAX, &errstr);
+       if (errstr == NULL)
+               return (idx);
+
+       return (-1);
+}
+
+/*
+ * Lookup pane id. An initial % means a pane id. sp must already point to the
+ * current session.
+ */
+struct window_pane *
+cmd_lookup_paneid(const char *arg)
+{
+       const char      *errstr;
+       u_int            paneid;
+
+       if (*arg != '%')
+               return (NULL);
+
+       paneid = strtonum(arg + 1, 0, UINT_MAX, &errstr);
+       if (errstr != NULL)
+               return (NULL);
+       return (window_pane_find_by_id(paneid));
+}
+
+/* Find session and winlink for pane. */
+struct session *
+cmd_pane_session(struct cmd_ctx *ctx, struct window_pane *wp,
+    struct winlink **wlp)
+{
+       struct session          *s;
+       struct sessionslist      ss;
+       struct winlink          *wl;
+
+       /* If this pane is in the current session, return that winlink. */
+       s = cmd_current_session(ctx, 0);
+       if (s != NULL) {
+               wl = winlink_find_by_window(&s->windows, wp->window);
+               if (wl != NULL) {
+                       if (wlp != NULL)
+                               *wlp = wl;
+                       return (s);
+               }
+       }
+
+       /* Otherwise choose from all sessions with this pane. */
+       ARRAY_INIT(&ss);
+       RB_FOREACH(s, sessions, &sessions) {
+               if (winlink_find_by_window(&s->windows, wp->window) != NULL)
+                       ARRAY_ADD(&ss, s);
+       }
+       s = cmd_choose_session_list(&ss);
+       ARRAY_FREE(&ss);
+       if (wlp != NULL)
+               *wlp = winlink_find_by_window(&s->windows, wp->window);
+       return (s);
+}
+
+/* Find the target session or report an error and return NULL. */
+struct session *
+cmd_find_session(struct cmd_ctx *ctx, const char *arg, int prefer_unattached)
+{
+       struct session          *s;
+       struct window_pane      *wp;
+       struct client           *c;
+       char                    *tmparg;
+       size_t                   arglen;
+       int                      ambiguous;
+
+       /* A NULL argument means the current session. */
+       if (arg == NULL)
+               return (cmd_current_session(ctx, prefer_unattached));
+
+       /* Lookup as pane id. */
+       if ((wp = cmd_lookup_paneid(arg)) != NULL)
+               return (cmd_pane_session(ctx, wp, NULL));
+
+       /* Trim a single trailing colon if any. */
+       tmparg = xstrdup(arg);
+       arglen = strlen(tmparg);
+       if (arglen != 0 && tmparg[arglen - 1] == ':')
+               tmparg[arglen - 1] = '\0';
+
+       /* An empty session name is the current session. */
+       if (*tmparg == '\0') {
+               xfree(tmparg);
+               return (cmd_current_session(ctx, prefer_unattached));
+       }
+
+       /* Find the session, if any. */
+       s = cmd_lookup_session(tmparg, &ambiguous);
+
+       /* If it doesn't, try to match it as a client. */
+       if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL)
+               s = c->session;
+
+       /* If no session found, report an error. */
+       if (s == NULL) {
+               if (ambiguous)
+                       ctx->error(ctx, "more than one session: %s", tmparg);
+               else
+                       ctx->error(ctx, "session not found: %s", tmparg);
+       }
+
+       xfree(tmparg);
+       return (s);
+}
+
+/* Find the target session and window or report an error and return NULL. */
+struct winlink *
+cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp)
+{
+       struct session          *s;
+       struct winlink          *wl;
+       struct window_pane      *wp;
+       const char              *winptr;
+       char                    *sessptr = NULL;
+       int                      ambiguous = 0;
+
+       /*
+        * Find the current session. There must always be a current session, if
+        * it can't be found, report an error.
+        */
+       if ((s = cmd_current_session(ctx, 0)) == NULL) {
+               ctx->error(ctx, "can't establish current session");
+               return (NULL);
+       }
+
+       /* A NULL argument means the current session and window. */
+       if (arg == NULL) {
+               if (sp != NULL)
+                       *sp = s;
+               return (s->curw);
+       }
+
+       /* Lookup as pane id. */
+       if ((wp = cmd_lookup_paneid(arg)) != NULL) {
+               s = cmd_pane_session(ctx, wp, &wl);
+               if (sp != NULL)
+                       *sp = s;
+               return (wl);
+       }
+
+       /* Time to look at the argument. If it is empty, that is an error. */
+       if (*arg == '\0')
+               goto not_found;
+
+       /* Find the separating colon and split into window and session. */
+       winptr = strchr(arg, ':');
+       if (winptr == NULL)
+               goto no_colon;
+       winptr++;       /* skip : */
+       sessptr = xstrdup(arg);
+       *strchr(sessptr, ':') = '\0';
+
+       /* Try to lookup the session if present. */
+       if (*sessptr != '\0') {
+               if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
+                       goto no_session;
+       }
+       if (sp != NULL)
+               *sp = s;
+
+       /*
+        * Then work out the window. An empty string is the current window,
+        * otherwise try special cases then to look it up in the session.
+        */
+       if (*winptr == '\0')
+               wl = s->curw;
+       else if (winptr[0] == '!' && winptr[1] == '\0')
+               wl = TAILQ_FIRST(&s->lastw);
+       else if (winptr[0] == '+' || winptr[0] == '-')
+               wl = cmd_find_window_offset(winptr, s, &ambiguous);
+       else
+               wl = cmd_lookup_window(s, winptr, &ambiguous);
+       if (wl == NULL)
+               goto not_found;
+
+       if (sessptr != NULL)
+               xfree(sessptr);
+       return (wl);
+
+no_colon:
+       /*
+        * No colon in the string, first try special cases, then as a window
+        * and lastly as a session.
+        */
+       if (arg[0] == '!' && arg[1] == '\0') {
+               if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
+                       goto not_found;
+       } else if (arg[0] == '+' || arg[0] == '-') {
+               if ((wl = cmd_find_window_offset(arg, s, &ambiguous)) == NULL)
+                       goto lookup_session;
+       } else if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL)
+               goto lookup_session;
+
+       if (sp != NULL)
+               *sp = s;
+
+       return (wl);
+
+lookup_session:
+       if (ambiguous)
+               goto not_found;
+       if (*arg != '\0' && (s = cmd_lookup_session(arg, &ambiguous)) == NULL)
+               goto no_session;
+
+       if (sp != NULL)
+               *sp = s;
+
+       return (s->curw);
+
+no_session:
+       if (ambiguous)
+               ctx->error(ctx, "multiple sessions: %s", arg);
+       else
+               ctx->error(ctx, "session not found: %s", arg);
+       if (sessptr != NULL)
+               xfree(sessptr);
+       return (NULL);
+
+not_found:
+       if (ambiguous)
+               ctx->error(ctx, "multiple windows: %s", arg);
+       else
+               ctx->error(ctx, "window not found: %s", arg);
+       if (sessptr != NULL)
+               xfree(sessptr);
+       return (NULL);
+}
+
+struct winlink *
+cmd_find_window_offset(const char *winptr, struct session *s, int *ambiguous)
+{
+       struct winlink  *wl;
+       int              offset = 1;
+
+       if (winptr[1] != '\0')
+               offset = strtonum(winptr + 1, 1, INT_MAX, NULL);
+       if (offset == 0)
+               wl = cmd_lookup_window(s, winptr, ambiguous);
+       else {
+               if (winptr[0] == '+')
+                       wl = winlink_next_by_number(s->curw, s, offset);
+               else
+                       wl = winlink_previous_by_number(s->curw, s, offset);
+       }
+
+       return (wl);
+}
+
+/*
+ * Find the target session and window index, whether or not it exists in the
+ * session. Return -2 on error or -1 if no window index is specified. This is
+ * used when parsing an argument for a window target that may not exist (for
+ * example if it is going to be created).
+ */
+int
+cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp)
+{
+       struct session  *s;
+       struct winlink  *wl;
+       const char      *winptr;
+       char            *sessptr = NULL;
+       int              idx, ambiguous = 0;
+
+       /*
+        * Find the current session. There must always be a current session, if
+        * it can't be found, report an error.
+        */
+       if ((s = cmd_current_session(ctx, 0)) == NULL) {
+               ctx->error(ctx, "can't establish current session");
+               return (-2);
+       }
+
+       /* A NULL argument means the current session and "no window" (-1). */
+       if (arg == NULL) {
+               if (sp != NULL)
+                       *sp = s;
+               return (-1);
+       }
+
+       /* Time to look at the argument. If it is empty, that is an error. */
+       if (*arg == '\0')
+               goto not_found;
+
+       /* Find the separating colon. If none, assume the current session. */
+       winptr = strchr(arg, ':');
+       if (winptr == NULL)
+               goto no_colon;
+       winptr++;       /* skip : */
+       sessptr = xstrdup(arg);
+       *strchr(sessptr, ':') = '\0';
+
+       /* Try to lookup the session if present. */
+       if (sessptr != NULL && *sessptr != '\0') {
+               if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
+                       goto no_session;
+       }
+       if (sp != NULL)
+               *sp = s;
+
+       /*
+        * Then work out the window. An empty string is a new window otherwise
+        * try to look it up in the session.
+        */
+       if (*winptr == '\0')
+               idx = -1;
+       else if (winptr[0] == '!' && winptr[1] == '\0') {
+               if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
+                       goto not_found;
+               idx = wl->idx;
+       } else if (winptr[0] == '+' || winptr[0] == '-') {
+               if ((idx = cmd_find_index_offset(winptr, s, &ambiguous)) < 0)
+                       goto invalid_index;
+       } else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1)
+               goto invalid_index;
+
+       if (sessptr != NULL)
+               xfree(sessptr);
+       return (idx);
+
+no_colon:
+       /*
+        * No colon in the string, first try special cases, then as a window
+        * and lastly as a session.
+        */
+       if (arg[0] == '!' && arg[1] == '\0') {
+               if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
+                       goto not_found;
+               idx = wl->idx;
+       } else if (arg[0] == '+' || arg[0] == '-') {
+               if ((idx = cmd_find_index_offset(arg, s, &ambiguous)) < 0)
+                       goto lookup_session;
+       } else if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1)
+               goto lookup_session;
+
+       if (sp != NULL)
+               *sp = s;
+
+       return (idx);
+
+lookup_session:
+       if (ambiguous)
+               goto not_found;
+       if (*arg != '\0' && (s = cmd_lookup_session(arg, &ambiguous)) == NULL)
+               goto no_session;
+
+       if (sp != NULL)
+               *sp = s;
+
+       return (-1);
+
+no_session:
+       if (ambiguous)
+               ctx->error(ctx, "multiple sessions: %s", arg);
+       else
+               ctx->error(ctx, "session not found: %s", arg);
+       if (sessptr != NULL)
+               xfree(sessptr);
+       return (-2);
+
+invalid_index:
+       if (ambiguous)
+               goto not_found;
+       ctx->error(ctx, "invalid index: %s", arg);
+
+       if (sessptr != NULL)
+               xfree(sessptr);
+       return (-2);
+
+not_found:
+       if (ambiguous)
+               ctx->error(ctx, "multiple windows: %s", arg);
+       else
+               ctx->error(ctx, "window not found: %s", arg);
+       if (sessptr != NULL)
+               xfree(sessptr);
+       return (-2);
+}
+
+int
+cmd_find_index_offset(const char *winptr, struct session *s, int *ambiguous)
+{
+       int     idx, offset = 1;
+
+       if (winptr[1] != '\0')
+               offset = strtonum(winptr + 1, 1, INT_MAX, NULL);
+       if (offset == 0)
+               idx = cmd_lookup_index(s, winptr, ambiguous);
+       else {
+               if (winptr[0] == '+') {
+                       if (s->curw->idx == INT_MAX)
+                               idx = cmd_lookup_index(s, winptr, ambiguous);
+                       else
+                               idx = s->curw->idx + offset;
+               } else {
+                       if (s->curw->idx == 0)
+                               idx = cmd_lookup_index(s, winptr, ambiguous);
+                       else
+                               idx = s->curw->idx - offset;
+               }
+       }
+
+       return (idx);
+}
+
+/*
+ * Find the target session, window and pane number or report an error and
+ * return NULL. The pane number is separated from the session:window by a .,
+ * such as mysession:mywindow.0.
+ */
+struct winlink *
+cmd_find_pane(struct cmd_ctx *ctx,
+    const char *arg, struct session **sp, struct window_pane **wpp)
+{
+       struct session  *s;
+       struct winlink  *wl;
+       const char      *period, *errstr;
+       char            *winptr, *paneptr;
+       u_int            idx;
+
+       /* Get the current session. */
+       if ((s = cmd_current_session(ctx, 0)) == NULL) {
+               ctx->error(ctx, "can't establish current session");
+               return (NULL);
+       }
+       if (sp != NULL)
+               *sp = s;
+
+       /* A NULL argument means the current session, window and pane. */
+       if (arg == NULL) {
+               *wpp = s->curw->window->active;
+               return (s->curw);
+       }
+
+       /* Lookup as pane id. */
+       if ((*wpp = cmd_lookup_paneid(arg)) != NULL) {
+               s = cmd_pane_session(ctx, *wpp, &wl);
+               if (sp != NULL)
+                       *sp = s;
+               return (wl);
+       }
+
+       /* Look for a separating period. */
+       if ((period = strrchr(arg, '.')) == NULL)
+               goto no_period;
+
+       /* Pull out the window part and parse it. */
+       winptr = xstrdup(arg);
+       winptr[period - arg] = '\0';
+       if (*winptr == '\0')
+               wl = s->curw;
+       else if ((wl = cmd_find_window(ctx, winptr, sp)) == NULL)
+               goto error;
+
+       /* Find the pane section and look it up. */
+       paneptr = winptr + (period - arg) + 1;
+       if (*paneptr == '\0')
+               *wpp = wl->window->active;
+       else if (paneptr[0] == '+' || paneptr[0] == '-')
+               *wpp = cmd_find_pane_offset(paneptr, wl);
+       else {
+               idx = strtonum(paneptr, 0, INT_MAX, &errstr);
+               if (errstr != NULL)
+                       goto lookup_string;
+               *wpp = window_pane_at_index(wl->window, idx);
+               if (*wpp == NULL)
+                       goto lookup_string;
+       }
+
+       xfree(winptr);
+       return (wl);
+
+lookup_string:
+       /* Try pane string description. */
+       if ((*wpp = window_find_string(wl->window, paneptr)) == NULL) {
+               ctx->error(ctx, "can't find pane: %s", paneptr);
+               goto error;
+       }
+
+       xfree(winptr);
+       return (wl);
+
+no_period:
+       /* Try as a pane number alone. */
+       idx = strtonum(arg, 0, INT_MAX, &errstr);
+       if (errstr != NULL)
+               goto lookup_window;
+
+       /* Try index in the current session and window. */
+       if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL)
+               goto lookup_window;
+
+       return (s->curw);
+
+lookup_window:
+       /* Try pane string description. */
+       if ((*wpp = window_find_string(s->curw->window, arg)) != NULL)
+               return (s->curw);
+
+       /* Try as a window and use the active pane. */
+       if ((wl = cmd_find_window(ctx, arg, sp)) != NULL)
+               *wpp = wl->window->active;
+       return (wl);
+
+error:
+       xfree(winptr);
+       return (NULL);
+}
+
+struct window_pane *
+cmd_find_pane_offset(const char *paneptr, struct winlink *wl)
+{
+       struct window           *w = wl->window;
+       struct window_pane      *wp = w->active;
+       u_int                    offset = 1;
+
+       if (paneptr[1] != '\0')
+               offset = strtonum(paneptr + 1, 1, INT_MAX, NULL);
+       if (offset > 0) {
+               if (paneptr[0] == '+')
+                       wp = window_pane_next_by_number(w, wp, offset);
+               else
+                       wp = window_pane_previous_by_number(w, wp, offset);
+       }
+
+       return (wp);
+}
+
+/* Replace the first %% or %idx in template by s. */
+char *
+cmd_template_replace(char *template, const char *s, int idx)
+{
+       char     ch;
+       char    *buf, *ptr;
+       int      replaced;
+       size_t   len;
+
+       if (strstr(template, "%") == NULL)
+               return (xstrdup(template));
+
+       buf = xmalloc(1);
+       *buf = '\0';
+       len = 0;
+       replaced = 0;
+
+       ptr = template;
+       while (*ptr != '\0') {
+               switch (ch = *ptr++) {
+               case '%':
+                       if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
+                               if (*ptr != '%' || replaced)
+                                       break;
+                               replaced = 1;
+                       }
+                       ptr++;
+
+                       len += strlen(s);
+                       buf = xrealloc(buf, 1, len + 1);
+                       strlcat(buf, s, len + 1);
+                       continue;
+               }
+               buf = xrealloc(buf, 1, len + 2);
+               buf[len++] = ch;
+               buf[len] = '\0';
+       }
+
+       return (buf);
+}
diff --git a/external/bsd/tmux/dist/colour.c b/external/bsd/tmux/dist/colour.c
new file mode 100644 (file)
index 0000000..3bb832d
--- /dev/null
@@ -0,0 +1,276 @@
+/* $Id: colour.c,v 1.3 2011/08/22 09:19:51 he 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 <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+/*
+ * Colour to string conversion functions. Bit 8 of the colour means it is one
+ * of the 256 colour palette.
+ */
+
+/* An RGB colour. */
+struct colour_rgb {
+       u_char  r;
+       u_char  g;
+       u_char  b;
+};
+
+/* 256 colour RGB table, generated on first use. */
+struct colour_rgb *colour_rgb_256;
+
+void   colour_rgb_generate256(void);
+u_int  colour_rgb_distance(struct colour_rgb *, struct colour_rgb *);
+int    colour_rgb_find(struct colour_rgb *);
+
+/* Generate 256 colour RGB table. */
+void
+colour_rgb_generate256(void)
+{
+       struct colour_rgb       *rgb;
+       u_int                    i, r, g, b;
+
+       /*
+        * Allocate the table. The first 16 colours are often changed by users
+        * and terminals so don't include them.
+        */
+       colour_rgb_256 = xcalloc(240, sizeof *colour_rgb_256);
+
+       /* Add the colours first. */
+       r = g = b = 0;
+       for (i = 240; i > 24; i--) {
+               rgb = &colour_rgb_256[240 - i];
+
+               if (r != 0)
+                       rgb->r = (r * 40) + 55;
+               if (g != 0)
+                       rgb->g = (g * 40) + 55;
+               if (b != 0)
+                       rgb->b = (b * 40) + 55;
+
+               b++;
+               if (b > 5) {
+                       b = 0;
+                       g++;
+               }
+               if (g > 5) {
+                       g = 0;
+                       r++;
+               }
+       }
+
+       /* Then add the greys. */
+       for (i = 24; i > 0; i--) {
+               rgb = &colour_rgb_256[240 - i];
+
+               rgb->r = 8 + (24 - i) * 10;
+               rgb->g = 8 + (24 - i) * 10;
+               rgb->b = 8 + (24 - i) * 10;
+       }
+}
+
+/* Get a measure of colour RGB distance. */
+u_int
+colour_rgb_distance(struct colour_rgb *rgb1, struct colour_rgb *rgb2)
+{
+       int     r, g, b;
+
+       r = rgb1->r - rgb2->r;
+       g = rgb1->g - rgb2->g;
+       b = rgb1->b - rgb2->b;
+       return (r * r + g * g + b * b);
+}
+
+/* Work out the nearest colour from the 256 colour set. */
+int
+colour_rgb_find(struct colour_rgb *rgb)
+{
+       u_int   distance, lowest;
+       u_int   colour, i;
+
+       if (colour_rgb_256 == NULL)
+               colour_rgb_generate256();
+
+       colour = 16;
+       lowest = UINT_MAX;
+       for (i = 0; i < 240; i++) {
+               distance = colour_rgb_distance(&colour_rgb_256[i], rgb);
+               if (distance < lowest) {
+                       lowest = distance;
+                       colour = 16 + i;
+               }
+       }
+       return (colour);
+}
+
+/* Set grid cell foreground colour. */
+void
+colour_set_fg(struct grid_cell *gc, int c)
+{
+       if (c & 0x100)
+               gc->flags |= GRID_FLAG_FG256;
+       gc->fg = c;
+}
+
+/* Set grid cell background colour. */
+void
+colour_set_bg(struct grid_cell *gc, int c)
+{
+       if (c & 0x100)
+               gc->flags |= GRID_FLAG_BG256;
+       gc->bg = c;
+}
+
+/* Convert colour to a string. */
+const char *
+colour_tostring(int c)
+{
+       static char     s[32];
+
+       if (c & 0x100) {
+               xsnprintf(s, sizeof s, "colour%u", c & ~0x100);
+               return (s);
+       }
+
+       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);
+}
+
+/* Convert colour from string. */
+int
+colour_fromstring(const char *s)
+{
+       const char              *errstr;
+       const char              *cp;
+       struct colour_rgb        rgb;
+       int                      n;
+
+       if (*s == '#' && strlen(s) == 7) {
+               for (cp = s + 1; isxdigit((u_char) *cp); cp++)
+                       ;
+               if (*cp != '\0')
+                       return (-1);
+               n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &rgb.r, &rgb.g, &rgb.b);
+               if (n != 3)
+                       return (-1);
+               return (colour_rgb_find(&rgb) | 0x100);
+       }
+
+       if (strncasecmp(s, "colour", (sizeof "colour") - 1) == 0) {
+               n = strtonum(s + (sizeof "colour") - 1, 0, 255, &errstr);
+               if (errstr != NULL)
+                       return (-1);
+               return (n | 0x100);
+       }
+
+       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);
+}
+
+/* Convert 256 colour palette to 16. */
+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]);
+}
+
+/* Convert 256 colour palette to 88. */
+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/external/bsd/tmux/dist/compat.h b/external/bsd/tmux/dist/compat.h
new file mode 100644 (file)
index 0000000..ac5f513
--- /dev/null
@@ -0,0 +1,240 @@
+/* $Id: compat.h,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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 COMPAT_H
+#define COMPAT_H
+
+#ifndef __GNUC__
+#define __attribute__(a)
+#endif
+
+#ifndef __dead
+#define __dead __attribute__ ((__noreturn__))
+#endif
+#ifndef __packed
+#define __packed __attribute__ ((__packed__))
+#endif
+
+#ifndef HAVE_BSD_TYPES
+typedef uint8_t u_int8_t;
+typedef uint16_t u_int16_t;
+typedef uint32_t u_int32_t;
+typedef uint64_t u_int64_t;
+#endif
+
+#ifndef HAVE_PATHS_H
+#define        _PATH_BSHELL    "/bin/sh"
+#define        _PATH_TMP       "/tmp/"
+#define _PATH_DEVNULL  "/dev/null"
+#define _PATH_TTY      "/dev/tty"
+#define _PATH_DEV      "/dev/"
+#endif
+
+#ifdef HAVE_QUEUE_H
+#include <sys/queue.h>
+#else
+#include "compat/queue.h"
+#endif
+
+#ifdef HAVE_TREE_H
+#include <sys/tree.h>
+#else
+#include "compat/tree.h"
+#endif
+
+#ifdef HAVE_BITSTRING_H
+#include <bitstring.h>
+#else
+#include "compat/bitstring.h"
+#endif
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifdef HAVE_FORKPTY
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#ifdef HAVE_PTY_H
+#include <pty.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#endif
+
+#ifdef HAVE_VIS
+#include <vis.h>
+#else
+#include "compat/vis.h"
+#endif
+
+#ifdef HAVE_IMSG
+#include <imsg.h>
+#else
+#include "compat/imsg.h"
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#else
+#include <inttypes.h>
+#endif
+
+#ifdef BROKEN_CMSG_FIRSTHDR
+#undef CMSG_FIRSTHDR
+#define CMSG_FIRSTHDR(mhdr) \
+       ((mhdr)->msg_controllen >= sizeof(struct cmsghdr) ? \
+           (struct cmsghdr *)(mhdr)->msg_control :         \
+           (struct cmsghdr *)NULL)
+#endif
+
+#ifndef CMSG_ALIGN
+#ifdef _CMSG_DATA_ALIGN
+#define CMSG_ALIGN _CMSG_DATA_ALIGN
+#else
+#define CMSG_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1))
+#endif
+#endif
+
+#ifndef CMSG_SPACE
+#define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(len))
+#endif
+
+#ifndef CMSG_LEN
+#define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))
+#endif
+
+#ifndef INFTIM
+#define INFTIM -1
+#endif
+
+#ifndef WAIT_ANY
+#define WAIT_ANY -1
+#endif
+
+#ifndef SUN_LEN
+#define SUN_LEN(sun) (sizeof (sun)->sun_path)
+#endif
+
+#ifndef timercmp
+#define        timercmp(tvp, uvp, cmp)                                         \
+       (((tvp)->tv_sec == (uvp)->tv_sec) ?                             \
+           ((tvp)->tv_usec cmp (uvp)->tv_usec) :                       \
+           ((tvp)->tv_sec cmp (uvp)->tv_sec))
+#endif
+
+#ifndef timeradd
+#define        timeradd(tvp, uvp, vvp)                                         \
+       do {                                                            \
+               (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec;          \
+               (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec;       \
+               if ((vvp)->tv_usec >= 1000000) {                        \
+                       (vvp)->tv_sec++;                                \
+                       (vvp)->tv_usec -= 1000000;                      \
+               }                                                       \
+       } while (0)
+#endif
+
+#ifndef TTY_NAME_MAX
+#define TTY_NAME_MAX 32
+#endif
+
+#ifndef HAVE_BZERO
+#undef bzero
+#define bzero(buf, len) memset(buf, 0, len);
+#endif
+
+#ifndef HAVE_CLOSEFROM
+/* closefrom.c */
+void   closefrom(int);
+#endif
+
+#ifndef HAVE_STRCASESTR
+/* strcasestr.c */
+char           *strcasestr(const char *, const char *);
+#endif
+
+#ifndef HAVE_STRSEP
+/* strsep.c */
+char           *strsep(char **, const char *);
+#endif
+
+#ifndef HAVE_STRTONUM
+/* strtonum.c */
+long long       strtonum(const char *, long long, long long, const char **);
+#endif
+
+#ifndef HAVE_STRLCPY
+/* strlcpy.c */
+size_t          strlcpy(char *, const char *, size_t);
+#endif
+
+#ifndef HAVE_STRLCAT
+/* strlcat.c */
+size_t          strlcat(char *, const char *, size_t);
+#endif
+
+#ifndef HAVE_DAEMON
+/* daemon.c */
+int             daemon(int, int);
+#endif
+
+#ifndef HAVE_FORKPTY
+/* forkpty.c */
+#include <sys/ioctl.h>
+pid_t           forkpty(int *, char *, struct termios *, struct winsize *);
+#endif
+
+#ifndef HAVE_ASPRINTF
+/* asprintf.c */
+int             asprintf(char **, const char *, ...);
+int             vasprintf(char **, const char *, va_list);
+#endif
+
+#ifndef HAVE_FGETLN
+/* fgetln.c */
+char           *fgetln(FILE *, size_t *);
+#endif
+
+#ifndef HAVE_SETENV
+/* setenv.c */
+int             setenv(const char *, const char *, int);
+int             unsetenv(const char *);
+#endif
+
+#ifdef HAVE_GETOPT
+#include <getopt.h>
+#else
+/* getopt.c */
+extern int     BSDopterr;
+extern int     BSDoptind;
+extern int     BSDoptopt;
+extern int     BSDoptreset;
+extern char    *BSDoptarg;
+int    BSDgetopt(int, char *const *, const char *);
+#define getopt(ac, av, o)  BSDgetopt(ac, av, o)
+#define opterr             BSDopterr
+#define optind             BSDoptind
+#define optopt             BSDoptopt
+#define optreset           BSDoptreset
+#define optarg             BSDoptarg
+#endif
+
+#endif /* COMPAT_H */
diff --git a/external/bsd/tmux/dist/compat/imsg-buffer.c b/external/bsd/tmux/dist/compat/imsg-buffer.c
new file mode 100644 (file)
index 0000000..08c4d1d
--- /dev/null
@@ -0,0 +1,303 @@
+/* $Id: imsg-buffer.c,v 1.5 2012/01/20 14:08:04 joerg Exp $ */
+/*     $OpenBSD: imsg-buffer.c,v 1.3 2010/05/26 13:56:07 nicm Exp $    */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.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 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/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+int    ibuf_realloc(struct ibuf *, size_t);
+void   ibuf_enqueue(struct msgbuf *, struct ibuf *);
+void   ibuf_dequeue(struct msgbuf *, struct ibuf *);
+
+struct ibuf *
+ibuf_open(size_t len)
+{
+       struct ibuf     *buf;
+
+       if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
+               return (NULL);
+       if ((buf->buf = malloc(len)) == NULL) {
+               free(buf);
+               return (NULL);
+       }
+       buf->size = buf->max = len;
+       buf->fd = -1;
+
+       return (buf);
+}
+
+struct ibuf *
+ibuf_dynamic(size_t len, size_t max)
+{
+       struct ibuf     *buf;
+
+       if (max < len)
+               return (NULL);
+
+       if ((buf = ibuf_open(len)) == NULL)
+               return (NULL);
+
+       if (max > 0)
+               buf->max = max;
+
+       return (buf);
+}
+
+int
+ibuf_realloc(struct ibuf *buf, size_t len)
+{
+       u_char  *b;
+
+       /* on static buffers max is eq size and so the following fails */
+       if (buf->wpos + len > buf->max) {
+               errno = ENOMEM;
+               return (-1);
+       }
+
+       b = realloc(buf->buf, buf->wpos + len);
+       if (b == NULL)
+               return (-1);
+       buf->buf = b;
+       buf->size = buf->wpos + len;
+
+       return (0);
+}
+
+int
+ibuf_add(struct ibuf *buf, const void *data, size_t len)
+{
+       if (buf->wpos + len > buf->size)
+               if (ibuf_realloc(buf, len) == -1)
+                       return (-1);
+
+       memcpy(buf->buf + buf->wpos, data, len);
+       buf->wpos += len;
+       return (0);
+}
+
+void *
+ibuf_reserve(struct ibuf *buf, size_t len)
+{
+       void    *b;
+
+       if (buf->wpos + len > buf->size)
+               if (ibuf_realloc(buf, len) == -1)
+                       return (NULL);
+
+       b = buf->buf + buf->wpos;
+       buf->wpos += len;
+       return (b);
+}
+
+void *
+ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
+{
+       /* only allowed to seek in already written parts */
+       if (pos + len > buf->wpos)
+               return (NULL);
+
+       return (buf->buf + pos);
+}
+
+size_t
+ibuf_size(struct ibuf *buf)
+{
+       return (buf->wpos);
+}
+
+size_t
+ibuf_left(struct ibuf *buf)
+{
+       return (buf->max - buf->wpos);
+}
+
+void
+ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+       ibuf_enqueue(msgbuf, buf);
+}
+
+int
+ibuf_write(struct msgbuf *msgbuf)
+{
+       struct iovec     iov[IOV_MAX];
+       struct ibuf     *buf;
+       unsigned int     i = 0;
+       ssize_t n;
+
+       bzero(&iov, sizeof(iov));
+       TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+               if (i >= IOV_MAX)
+                       break;
+               iov[i].iov_base = buf->buf + buf->rpos;
+               iov[i].iov_len = buf->wpos - buf->rpos;
+               i++;
+       }
+
+       if ((n = writev(msgbuf->fd, iov, i)) == -1) {
+               if (errno == EAGAIN || errno == ENOBUFS ||
+                   errno == EINTR)     /* try later */
+                       return (0);
+               else
+                       return (-1);
+       }
+
+       if (n == 0) {                   /* connection closed */
+               errno = 0;
+               return (-2);
+       }
+
+       msgbuf_drain(msgbuf, n);
+
+       return (0);
+}
+
+void
+ibuf_free(struct ibuf *buf)
+{
+       free(buf->buf);
+       free(buf);
+}
+
+void
+msgbuf_init(struct msgbuf *msgbuf)
+{
+       msgbuf->queued = 0;
+       msgbuf->fd = -1;
+       TAILQ_INIT(&msgbuf->bufs);
+}
+
+void
+msgbuf_drain(struct msgbuf *msgbuf, size_t n)
+{
+       struct ibuf     *buf, *next;
+
+       for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
+           buf = next) {
+               next = TAILQ_NEXT(buf, entry);
+               if (buf->rpos + n >= buf->wpos) {
+                       n -= buf->wpos - buf->rpos;
+                       ibuf_dequeue(msgbuf, buf);
+               } else {
+                       buf->rpos += n;
+                       n = 0;
+               }
+       }
+}
+
+void
+msgbuf_clear(struct msgbuf *msgbuf)
+{
+       struct ibuf     *buf;
+
+       while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
+               ibuf_dequeue(msgbuf, buf);
+}
+
+int
+msgbuf_write(struct msgbuf *msgbuf)
+{
+       struct iovec     iov[IOV_MAX];
+       struct ibuf     *buf;
+       unsigned int     i = 0;
+       ssize_t          n;
+       struct msghdr    msg;
+       struct cmsghdr  *cmsg;
+       union {
+               struct cmsghdr  hdr;
+               char            buf[CMSG_SPACE(sizeof(int))];
+       } cmsgbuf;
+
+       bzero(&iov, sizeof(iov));
+       bzero(&msg, sizeof(msg));
+       TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+               if (i >= IOV_MAX)
+                       break;
+               iov[i].iov_base = buf->buf + buf->rpos;
+               iov[i].iov_len = buf->wpos - buf->rpos;
+               i++;
+               if (buf->fd != -1)
+                       break;
+       }
+
+       msg.msg_iov = iov;
+       msg.msg_iovlen = i;
+
+       if (buf != NULL && buf->fd != -1) {
+               msg.msg_control = (caddr_t)&cmsgbuf.buf;
+               msg.msg_controllen = CMSG_SPACE(sizeof(int));
+               cmsg = CMSG_FIRSTHDR(&msg);
+               cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+               cmsg->cmsg_level = SOL_SOCKET;
+               cmsg->cmsg_type = SCM_RIGHTS;
+               *(int *)CMSG_DATA(cmsg) = buf->fd;
+       }
+
+       if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
+               if (errno == EAGAIN || errno == ENOBUFS ||
+                   errno == EINTR)     /* try later */
+                       return (0);
+               else
+                       return (-1);
+       }
+
+       if (n == 0) {                   /* connection closed */
+               errno = 0;
+               return (-2);
+       }
+
+       /*
+        * assumption: fd got sent if sendmsg sent anything
+        * this works because fds are passed one at a time
+        */
+       if (buf != NULL && buf->fd != -1) {
+               close(buf->fd);
+               buf->fd = -1;
+       }
+
+       msgbuf_drain(msgbuf, n);
+
+       return (0);
+}
+
+void
+ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+       TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
+       msgbuf->queued++;
+}
+
+void
+ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+       TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
+
+       if (buf->fd != -1)
+               close(buf->fd);
+
+       msgbuf->queued--;
+       ibuf_free(buf);
+}
diff --git a/external/bsd/tmux/dist/compat/imsg.c b/external/bsd/tmux/dist/compat/imsg.c
new file mode 100644 (file)
index 0000000..4cf36f3
--- /dev/null
@@ -0,0 +1,271 @@
+/* $Id: imsg.c,v 1.5 2012/01/20 14:08:04 joerg Exp $ */
+/*     $OpenBSD: imsg.c,v 1.3 2010/05/26 13:56:07 nicm Exp $   */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.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 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/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+int     imsg_get_fd(struct imsgbuf *);
+
+void
+imsg_init(struct imsgbuf *ibuf, int fd)
+{
+       msgbuf_init(&ibuf->w);
+       bzero(&ibuf->r, sizeof(ibuf->r));
+       ibuf->fd = fd;
+       ibuf->w.fd = fd;
+       ibuf->pid = getpid();
+       TAILQ_INIT(&ibuf->fds);
+}
+
+ssize_t
+imsg_read(struct imsgbuf *ibuf)
+{
+       struct msghdr            msg;
+       struct cmsghdr          *cmsg;
+       union {
+               struct cmsghdr hdr;
+               char    buf[CMSG_SPACE(sizeof(int) * 16)];
+       } cmsgbuf;
+       struct iovec             iov;
+       ssize_t                  n;
+       int                      fd;
+       struct imsg_fd          *ifd;
+
+       bzero(&msg, sizeof(msg));
+
+       iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
+       iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+       msg.msg_control = &cmsgbuf.buf;
+       msg.msg_controllen = CMSG_SPACE(sizeof(int) * 16);
+
+       if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
+               if (errno != EINTR && errno != EAGAIN) {
+                       return (-1);
+               }
+               return (-2);
+       }
+
+       ibuf->r.wpos += n;
+
+       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+           cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+               if (cmsg->cmsg_level == SOL_SOCKET &&
+                   cmsg->cmsg_type == SCM_RIGHTS) {
+                       fd = (*(int *)CMSG_DATA(cmsg));
+                       if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) {
+                               close(fd);
+                               return (-1);
+                       }
+                       ifd->fd = fd;
+                       TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry);
+               }
+               /* we do not handle other ctl data level */
+       }
+
+       return (n);
+}
+
+ssize_t
+imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
+{
+       size_t                   av, left, datalen;
+
+       av = ibuf->r.wpos;
+
+       if (IMSG_HEADER_SIZE > av)
+               return (0);
+
+       memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
+       if (imsg->hdr.len < IMSG_HEADER_SIZE ||
+           imsg->hdr.len > MAX_IMSGSIZE) {
+               errno = ERANGE;
+               return (-1);
+       }
+       if (imsg->hdr.len > av)
+               return (0);
+       datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+       ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
+       if ((imsg->data = malloc(datalen)) == NULL && datalen != 0)
+               return (-1);
+
+       if (imsg->hdr.flags & IMSGF_HASFD)
+               imsg->fd = imsg_get_fd(ibuf);
+       else
+               imsg->fd = -1;
+
+       memcpy(imsg->data, ibuf->r.rptr, datalen);
+
+       if (imsg->hdr.len < av) {
+               left = av - imsg->hdr.len;
+               memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
+               ibuf->r.wpos = left;
+       } else
+               ibuf->r.wpos = 0;
+
+       return (datalen + IMSG_HEADER_SIZE);
+}
+
+int
+imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
+    pid_t pid, int fd, void *data, u_int16_t datalen)
+{
+       struct ibuf     *wbuf;
+
+       if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+               return (-1);
+
+       if (imsg_add(wbuf, data, datalen) == -1)
+               return (-1);
+
+       wbuf->fd = fd;
+
+       imsg_close(ibuf, wbuf);
+
+       return (1);
+}
+
+int
+imsg_composev(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
+    pid_t pid, int fd, const struct iovec *iov, int iovcnt)
+{
+       struct ibuf     *wbuf;
+       int              i, datalen = 0;
+
+       for (i = 0; i < iovcnt; i++)
+               datalen += iov[i].iov_len;
+
+       if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+               return (-1);
+
+       for (i = 0; i < iovcnt; i++)
+               if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
+                       return (-1);
+
+       wbuf->fd = fd;
+
+       imsg_close(ibuf, wbuf);
+
+       return (1);
+}
+
+/* ARGSUSED */
+struct ibuf *
+imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
+    pid_t pid, u_int16_t datalen)
+{
+       struct ibuf     *wbuf;
+       struct imsg_hdr  hdr;
+
+       datalen += IMSG_HEADER_SIZE;
+       if (datalen > MAX_IMSGSIZE) {
+               errno = ERANGE;
+               return (NULL);
+       }
+
+       hdr.type = type;
+       hdr.flags = 0;
+       hdr.peerid = peerid;
+       if ((hdr.pid = pid) == 0)
+               hdr.pid = ibuf->pid;
+       if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
+               return (NULL);
+       }
+       if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
+               return (NULL);
+
+       return (wbuf);
+}
+
+int
+imsg_add(struct ibuf *msg, void *data, u_int16_t datalen)
+{
+       if (datalen)
+               if (ibuf_add(msg, data, datalen) == -1) {
+                       ibuf_free(msg);
+                       return (-1);
+               }
+       return (datalen);
+}
+
+void
+imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
+{
+       struct imsg_hdr *hdr;
+
+       hdr = (struct imsg_hdr *)msg->buf;
+
+       hdr->flags &= ~IMSGF_HASFD;
+       if (msg->fd != -1)
+               hdr->flags |= IMSGF_HASFD;
+
+       hdr->len = (u_int16_t)msg->wpos;
+
+       ibuf_close(&ibuf->w, msg);
+}
+
+void
+imsg_free(struct imsg *imsg)
+{
+       free(imsg->data);
+}
+
+int
+imsg_get_fd(struct imsgbuf *ibuf)
+{
+       int              fd;
+       struct imsg_fd  *ifd;
+
+       if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
+               return (-1);
+
+       fd = ifd->fd;
+       TAILQ_REMOVE(&ibuf->fds, ifd, entry);
+       free(ifd);
+
+       return (fd);
+}
+
+int
+imsg_flush(struct imsgbuf *ibuf)
+{
+       while (ibuf->w.queued)
+               if (msgbuf_write(&ibuf->w) < 0)
+                       return (-1);
+       return (0);
+}
+
+void
+imsg_clear(struct imsgbuf *ibuf)
+{
+       int     fd;
+
+       msgbuf_clear(&ibuf->w);
+       while ((fd = imsg_get_fd(ibuf)) != -1)
+               close(fd);
+}
diff --git a/external/bsd/tmux/dist/compat/imsg.h b/external/bsd/tmux/dist/compat/imsg.h
new file mode 100644 (file)
index 0000000..f5bb79e
--- /dev/null
@@ -0,0 +1,110 @@
+/* $Id: imsg.h,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ */
+/*     $OpenBSD: imsg.h,v 1.4 2010/05/26 13:56:07 nicm Exp $   */
+
+/*
+ * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
+ * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.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 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 "tmux.h"
+
+#define IBUF_READ_SIZE         65535
+#define IMSG_HEADER_SIZE       sizeof(struct imsg_hdr)
+#define MAX_IMSGSIZE           16384
+
+struct ibuf {
+       TAILQ_ENTRY(ibuf)        entry;
+       u_char                  *buf;
+       size_t                   size;
+       size_t                   max;
+       size_t                   wpos;
+       size_t                   rpos;
+       int                      fd;
+};
+
+struct msgbuf {
+       TAILQ_HEAD(, ibuf)       bufs;
+       u_int32_t                queued;
+       int                      fd;
+};
+
+struct ibuf_read {
+       u_char                   buf[IBUF_READ_SIZE];
+       u_char                  *rptr;
+       size_t                   wpos;
+};
+
+struct imsg_fd {
+       TAILQ_ENTRY(imsg_fd)    entry;
+       int                     fd;
+};
+
+struct imsgbuf {
+       TAILQ_HEAD(, imsg_fd)    fds;
+       struct ibuf_read         r;
+       struct msgbuf            w;
+       int                      fd;
+       pid_t                    pid;
+};
+
+#define IMSGF_HASFD    1
+
+struct imsg_hdr {
+       u_int32_t        type;
+       u_int16_t        len;
+       u_int16_t        flags;
+       u_int32_t        peerid;
+       u_int32_t        pid;
+};
+
+struct imsg {
+       struct imsg_hdr  hdr;
+       int              fd;
+       void            *data;
+};
+
+
+/* buffer.c */
+struct ibuf    *ibuf_open(size_t);
+struct ibuf    *ibuf_dynamic(size_t, size_t);
+int             ibuf_add(struct ibuf *, const void *, size_t);
+void           *ibuf_reserve(struct ibuf *, size_t);
+void           *ibuf_seek(struct ibuf *, size_t, size_t);
+size_t          ibuf_size(struct ibuf *);
+size_t          ibuf_left(struct ibuf *);
+void            ibuf_close(struct msgbuf *, struct ibuf *);
+int             ibuf_write(struct msgbuf *);
+void            ibuf_free(struct ibuf *);
+void            msgbuf_init(struct msgbuf *);
+void            msgbuf_clear(struct msgbuf *);
+int             msgbuf_write(struct msgbuf *);
+void            msgbuf_drain(struct msgbuf *, size_t);
+
+/* imsg.c */
+void    imsg_init(struct imsgbuf *, int);
+ssize_t         imsg_read(struct imsgbuf *);
+ssize_t         imsg_get(struct imsgbuf *, struct imsg *);
+int     imsg_compose(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
+           int, void *, u_int16_t);
+int     imsg_composev(struct imsgbuf *, u_int32_t, u_int32_t,  pid_t,
+           int, const struct iovec *, int);
+struct ibuf *imsg_create(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
+           u_int16_t);
+int     imsg_add(struct ibuf *, void *, u_int16_t);
+void    imsg_close(struct imsgbuf *, struct ibuf *);
+void    imsg_free(struct imsg *);
+int     imsg_flush(struct imsgbuf *);
+void    imsg_clear(struct imsgbuf *);
diff --git a/external/bsd/tmux/dist/compat/strtonum.c b/external/bsd/tmux/dist/compat/strtonum.c
new file mode 100644 (file)
index 0000000..c605d87
--- /dev/null
@@ -0,0 +1,68 @@
+/* $Id: strtonum.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ */
+/*     $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $    */
+
+/*
+ * Copyright (c) 2004 Ted Unangst and Todd Miller
+ * All rights reserved.
+ *
+ * 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 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 <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "tmux.h"
+
+#define INVALID        1
+#define TOOSMALL       2
+#define TOOLARGE       3
+
+long long
+strtonum(const char *numstr, long long minval, long long maxval,
+    const char **errstrp)
+{
+       long long ll = 0;
+       char *ep;
+       int error = 0;
+       struct errval {
+               const char *errstr;
+               int err;
+       } ev[4] = {
+               { NULL,         0 },
+               { "invalid",    EINVAL },
+               { "too small",  ERANGE },
+               { "too large",  ERANGE },
+       };
+
+       ev[0].err = errno;
+       errno = 0;
+       if (minval > maxval)
+               error = INVALID;
+       else {
+               ll = strtoll(numstr, &ep, 10);
+               if (numstr == ep || *ep != '\0')
+                       error = INVALID;
+               else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
+                       error = TOOSMALL;
+               else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
+                       error = TOOLARGE;
+       }
+       if (errstrp != NULL)
+               *errstrp = ev[error].errstr;
+       errno = ev[error].err;
+       if (error)
+               ll = 0;
+
+       return (ll);
+}
diff --git a/external/bsd/tmux/dist/environ.c b/external/bsd/tmux/dist/environ.c
new file mode 100644 (file)
index 0000000..e46253f
--- /dev/null
@@ -0,0 +1,181 @@
+/* $Id: environ.c,v 1.3 2011/08/17 18:48:36 jmmv 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"
+
+/*
+ * Environment - manipulate a set of environment variables.
+ */
+
+RB_GENERATE(environ, environ_entry, entry, environ_cmp);
+
+int
+environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2)
+{
+       return (strcmp(envent1->name, envent2->name));
+}
+
+/* Initialise the environment. */
+void
+environ_init(struct environ *env)
+{
+       RB_INIT(env);
+}
+
+/* Free an environment. */
+void
+environ_free(struct environ *env)
+{
+       struct environ_entry    *envent;
+
+       while (!RB_EMPTY(env)) {
+               envent = RB_ROOT(env);
+               RB_REMOVE(environ, env, envent);
+               xfree(envent->name);
+               if (envent->value != NULL)
+                       xfree(envent->value);
+               xfree(envent);
+       }
+}
+
+/* Copy one environment into another. */
+void
+environ_copy(struct environ *srcenv, struct environ *dstenv)
+{
+       struct environ_entry    *envent;
+
+       RB_FOREACH(envent, environ, srcenv)
+               environ_set(dstenv, envent->name, envent->value);
+}
+
+/* Find an environment variable. */
+struct environ_entry *
+environ_find(struct environ *env, const char *name)
+{
+       struct environ_entry    envent;
+
+       envent.name = __UNCONST(name);
+       return (RB_FIND(environ, env, &envent));
+}
+
+/* Set an environment variable. */
+void
+environ_set(struct environ *env, const char *name, const char *value)
+{
+       struct environ_entry    *envent;
+
+       if ((envent = environ_find(env, name)) != NULL) {
+               if (envent->value != NULL)
+                       xfree(envent->value);
+               if (value != NULL)
+                       envent->value = xstrdup(value);
+               else
+                       envent->value = NULL;
+       } else {
+               envent = xmalloc(sizeof *envent);
+               envent->name = xstrdup(name);
+               if (value != NULL)
+                       envent->value = xstrdup(value);
+               else
+                       envent->value = NULL;
+               RB_INSERT(environ, env, envent);
+       }
+}
+
+/* Set an environment variable from a NAME=VALUE string. */
+void
+environ_put(struct environ *env, const char *var)
+{
+       char    *name, *value;
+
+       value = strchr(var, '=');
+       if (value == NULL)
+               return;
+       value++;
+
+       name = xstrdup(var);
+       name[strcspn(name, "=")] = '\0';
+
+       environ_set(env, name, value);
+       xfree(name);
+}
+
+/* Unset an environment variable. */
+void
+environ_unset(struct environ *env, const char *name)
+{
+       struct environ_entry    *envent;
+
+       if ((envent = environ_find(env, name)) == NULL)
+               return;
+       RB_REMOVE(environ, env, envent);
+       xfree(envent->name);
+       if (envent->value != NULL)
+               xfree(envent->value);
+       xfree(envent);
+}
+
+/*
+ * Copy a space-separated list of variables from a destination into a source
+ * environment.
+ */
+void
+environ_update(const char *vars, struct environ *srcenv, struct environ *dstenv)
+{
+       struct environ_entry    *envent;
+       char                    *copyvars, *var, *next;
+
+       copyvars = next = xstrdup(vars);
+       while ((var = strsep(&next, " ")) != NULL) {
+               if ((envent = environ_find(srcenv, var)) == NULL)
+                       environ_set(dstenv, var, NULL);
+               else
+                       environ_set(dstenv, envent->name, envent->value);
+       }
+       xfree(copyvars);
+}
+
+/* Push environment into the real environment - use after fork(). */
+void
+environ_push(struct environ *env)
+{
+       ARRAY_DECL(, char *)    varlist;
+       struct environ_entry   *envent;
+       char                  **varp, *var;
+       u_int                   i;
+
+       ARRAY_INIT(&varlist);
+       for (varp = environ; *varp != NULL; varp++) {
+               var = xstrdup(*varp);
+               var[strcspn(var, "=")] = '\0';
+               ARRAY_ADD(&varlist, var);
+       }
+       for (i = 0; i < ARRAY_LENGTH(&varlist); i++)
+               unsetenv(ARRAY_ITEM(&varlist, i));
+       ARRAY_FREE(&varlist);
+
+       RB_FOREACH(envent, environ, env) {
+               if (envent->value != NULL)
+                       setenv(envent->name, envent->value, 1);
+       }
+}
diff --git a/external/bsd/tmux/dist/examples/bash_completion_tmux.sh b/external/bsd/tmux/dist/examples/bash_completion_tmux.sh
new file mode 100644 (file)
index 0000000..74728b9
--- /dev/null
@@ -0,0 +1,105 @@
+# START tmux completion
+# This file is in the public domain
+# See: http://www.debian-administration.org/articles/317 for how to write more.
+# Usage: Put "source bash_completion_tmux.sh" into your .bashrc
+_tmux() 
+{
+    local cur prev opts
+    COMPREPLY=()
+    cur="${COMP_WORDS[COMP_CWORD]}"
+    prev="${COMP_WORDS[COMP_CWORD-1]}"
+    
+    opts=" \
+    attach-session \
+    bind-key \
+    break-pane \
+    capture-pane \
+    choose-client \
+    choose-session \
+    choose-window \
+    clear-history \
+    clock-mode \
+    command-prompt \
+    confirm-before \
+    copy-buffer \
+    copy-mode \
+    delete-buffer \
+    detach-client \
+    display-message \
+    display-panes \
+    down-pane \
+    find-window \
+    has-session \
+    if-shell \
+    join-pane \
+    kill-pane \
+    kill-server \
+    kill-session \
+    kill-window \
+    last-window \
+    link-window \
+    list-buffers \
+    list-clients \
+    list-commands \
+    list-keys \
+    list-panes \
+    list-sessions \
+    list-windows \
+    load-buffer \
+    lock-client \
+    lock-server \
+    lock-session \
+    move-window \
+    new-session \
+    new-window \
+    next-layout \
+    next-window \
+    paste-buffer \
+    pipe-pane \
+    previous-layout \
+    previous-window \
+    refresh-client \
+    rename-session \
+    rename-window \
+    resize-pane \
+    respawn-window \
+    rotate-window \
+    run-shell \
+    save-buffer \
+    select-layout \
+    select-pane \
+    select-prompt \
+    select-window \
+    send-keys \
+    send-prefix \
+    server-info \
+    set-buffer \
+    set-environment \
+    set-option \
+    set-window-option \
+    show-buffer \
+    show-environment \
+    show-messages \
+    show-options \
+    show-window-options \
+    source-file \
+    split-window \
+    start-server \
+    suspend-client \
+    swap-pane \
+    swap-window \
+    switch-client \
+    unbind-key \
+    unlink-window \
+    up-pane"
+
+    COMPREPLY=($(compgen -W "${opts}" -- ${cur}))  
+    return 0
+
+}
+complete -F _tmux tmux
+
+# END tmux completion
+
+
+                
diff --git a/external/bsd/tmux/dist/examples/h-boetes.conf b/external/bsd/tmux/dist/examples/h-boetes.conf
new file mode 100644 (file)
index 0000000..c443941
--- /dev/null
@@ -0,0 +1,42 @@
+# $Id: h-boetes.conf,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $
+#
+# From Han Boetes.
+
+set -g default-command zsh
+set -g status-right "#(uptime|awk '{print $11}') #(date)"
+
+# Statusbar properties.
+set -g display-time 3000
+set -g status-bg black
+set -g status-fg cyan
+set-window-option -g window-status-current-attr bright,reverse
+set-window-option -g window-status-current-bg cyan
+set-window-option -g window-status-current-fg black
+
+# Use c-t instead of c-b as the prefix
+unbind C-b
+set -g prefix C-t
+bind C-t send-prefix
+bind t send-prefix
+
+# Bind function keys.
+bind -n F1 select-window -t 1
+bind -n F2 select-window -t 2
+bind -n F3 select-window -t 3
+bind -n F4 select-window -t 4
+bind -n F5 select-window -t 5
+bind -n F6 select-window -t 6
+bind -n F7 select-window -t 7
+bind -n F8 select-window -t 8
+
+# All new windows started at startup.
+new emacs
+neww irssi
+neww mutt
+neww
+neww
+neww
+neww
+neww
+
+select-window -t 1
diff --git a/external/bsd/tmux/dist/examples/n-marriott.conf b/external/bsd/tmux/dist/examples/n-marriott.conf
new file mode 100644 (file)
index 0000000..5d74586
--- /dev/null
@@ -0,0 +1,110 @@
+# $Id: n-marriott.conf,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $
+#
+# By Nicholas Marriott. Public domain.
+
+# Default global options.
+set -g status-bg green
+set -g status-right "%H:%M" # %d-%b-%y
+set -g bell-action none
+set -g lock-after-time 1800
+
+# Default global window options.
+setw -g remain-on-exit on
+setw -g window-status-current-attr "underscore"
+#setw -g xterm-keys on
+
+# Prefix key.
+set -g prefix C-a
+unbind C-b
+bind C-a send-prefix
+
+# Keys to switch session.
+bind Q switchc -t0
+bind W switchc -t1
+bind E switchc -t2
+
+# Other key bindings.
+bind F1 selectw -t:10
+bind F2 selectw -t:11
+bind F3 selectw -t:12
+bind F4 selectw -t:13
+bind F5 selectw -t:14
+bind F6 selectw -t:15
+bind F7 selectw -t:16
+bind F8 selectw -t:17
+bind F9 selectw -t:18
+bind F10 selectw -t:19
+bind F11 selectw -t:20
+bind F12 selectw -t:21
+
+bind m setw monitor-activity
+
+bind y setw force-width 81
+bind u setw force-width 0
+
+bind -n F1 run-shell 'mpc toggle >/dev/null 2>&1'
+bind -n F2 run-shell 'mpc'
+bind -n F3 run-shell 'mpc prev >/dev/null 2>&1'
+bind -n F4 run-shell 'mpc next >/dev/null 2>&1'
+bind -n F5 run-shell 'mpc volume -5 >/dev/null 2>&1'
+bind -n F6 run-shell 'mpc volume +5 >/dev/null 2>&1'
+
+# Hide and show window name from status line
+bind '-' setw window-status-format '#I'\; setw window-status-current-format '#I'
+bind '+' setw window-status-format '#I:#W#F'\; setw window-status-current-format '#I:#W#F'
+
+# First session.
+new -d -s0 -nirssi 'exec ssh -t natalya exec sh ~/bin/tmux-start'
+setw -t0:0 monitor-activity on
+setw -t0:0 aggressive-resize on
+set -t0 status-bg green
+neww -d -ntodo 'exec emacs ~/TODO'
+setw -t0:1 aggressive-resize on
+neww -d -ntodo2 'exec emacs ~/TODO2'
+setw -t0:2 aggressive-resize on
+neww -d -nncmpc 'exec ncmpc -f ~/.ncmpc.conf'
+setw -t0:3 aggressive-resize on
+neww -d -nmutt 'exec mutt'
+setw -t0:4 aggressive-resize on
+neww -d
+neww -d
+neww -d
+neww -d
+neww -d
+neww -d
+neww -d
+neww -d
+neww -d
+neww -d
+neww -d
+neww -d
+
+# Second session.
+new -d -s1
+set -t1 status-bg cyan
+linkw -dk -t0 -s0:0
+linkw -dk -t1 -s0:1
+linkw -dk -t2 -s0:2
+linkw -dk -t3 -s0:3
+linkw -dk -t4 -s0:4
+neww -d
+neww -d
+neww -d
+neww -d
+neww -d
+neww -d
+
+# Third session.
+new -d -s2
+set -t2 status-bg yellow
+linkw -dk -t0 -s0:0
+linkw -dk -t1 -s0:1
+linkw -dk -t2 -s0:2
+linkw -dk -t3 -s0:3
+linkw -dk -t4 -s0:4
+neww -d
+neww -d
+neww -d
+neww -d
+neww -d
+neww -d
diff --git a/external/bsd/tmux/dist/examples/screen-keys.conf b/external/bsd/tmux/dist/examples/screen-keys.conf
new file mode 100644 (file)
index 0000000..da2ce41
--- /dev/null
@@ -0,0 +1,102 @@
+# $Id: screen-keys.conf,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $
+#
+# By Nicholas Marriott. Public domain.
+#
+# This configuration file binds many of the common GNU screen key bindings to
+# appropriate tmux key bindings. Note that for some key bindings there is no
+# tmux analogue and also that this set omits binding some commands available in
+# tmux but not in screen.
+#
+# Note this is only a selection of key bindings and they are in addition to the
+# normal tmux key bindings. This is intended as an example not as to be used
+# as-is.
+
+# Set the prefix to ^A.
+unbind C-b
+set -g prefix ^A
+bind a send-prefix
+
+# Bind appropriate commands similar to screen.
+# lockscreen ^X x 
+unbind ^X
+bind ^X lock-server
+unbind x
+bind x lock-server
+
+# screen ^C c 
+unbind ^C
+bind ^C new-window
+bind c
+bind c new-window
+
+# detach ^D d
+unbind ^D
+bind ^D detach
+
+# displays * 
+unbind *
+bind * list-clients
+
+# next ^@ ^N sp n 
+unbind ^@
+bind ^@ next-window
+unbind ^N
+bind ^N next-window
+unbind " "
+bind " " next-window
+unbind n
+bind n next-window
+
+# title A
+unbind A
+bind A command-prompt "rename-window %%"
+
+# other ^A
+unbind ^A
+bind ^A last-window
+
+# prev ^H ^P p ^? 
+unbind ^H
+bind ^H previous-window
+unbind ^P
+bind ^P previous-window
+unbind p
+bind p previous-window
+unbind BSpace
+bind BSpace previous-window
+
+# windows ^W w 
+unbind ^W
+bind ^W list-windows
+unbind w
+bind w list-windows
+
+# quit \ 
+unbind \
+bind \ confirm-before "kill-server"
+
+# kill K k 
+unbind K
+bind K confirm-before "kill-window"
+unbind k
+bind k confirm-before "kill-window"
+
+# redisplay ^L l 
+unbind ^L
+bind ^L refresh-client
+unbind l
+bind l refresh-client
+
+# split -v |
+unbind |
+bind | split-window
+
+# :kB: focus up
+unbind Tab
+bind Tab select-pane -t:.+
+unbind BTab
+bind BTab select-pane -t:.-
+
+# " windowlist -b
+unbind '"'
+bind '"' choose-window
diff --git a/external/bsd/tmux/dist/examples/t-williams.conf b/external/bsd/tmux/dist/examples/t-williams.conf
new file mode 100644 (file)
index 0000000..fb593d4
--- /dev/null
@@ -0,0 +1,104 @@
+# $Id: t-williams.conf,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $
+#
+# ~/.tmux.conf - tmux terminal multiplexer config
+# Thayer Williams (http://cinderwick.ca)
+# "Feel free to do whatever you like with it."
+
+# I typically start tmux from ~/.xinitrc with the following:
+#
+#   urxvt -e bash -c "tmux attach -d -t mysession" &
+#
+# and recall it any time thereafter with xbindkeys (Mod4+s):
+#
+#   "urxvt -e bash -c 'tmux attach -d -t mysession'"
+#     m:0x50 + c:39
+
+
+# set prefix key to ctrl+a until I have time to adapt
+unbind C-b
+set -g prefix C-a
+
+# send the prefix to client inside window (ala nested sessions)
+bind-key a send-prefix
+
+# toggle last window like screen
+bind-key C-a last-window
+
+# confirm before killing a window or the server
+bind-key k confirm kill-window
+bind-key K confirm kill-server
+
+# toggle statusbar
+bind-key b set-option status
+
+# ctrl+left/right cycles thru windows
+bind-key -n C-right next
+bind-key -n C-left prev
+
+# open a man page in new window
+bind / command-prompt "split-window 'exec man %%'"
+
+# quick view of processes
+bind '~' split-window "exec htop"
+
+# scrollback buffer n lines
+set -g history-limit 5000
+
+# listen for activity on all windows
+set -g bell-action any
+
+# on-screen time for display-panes in ms
+set -g display-panes-time 2000
+
+# start window indexing at one instead of zero
+set -g base-index 1
+
+# enable wm window titles
+set -g set-titles on
+
+# wm window title string (uses statusbar variables)
+set -g set-titles-string "tmux.#I.#W"
+
+# session initialization
+new -s mysession mutt
+neww -t 2
+neww -d -t 3
+neww -d -t 5 mocp
+neww -d -t 6 rtorrent
+selectw -t 1
+
+# statusbar --------------------------------------------------------------
+
+set -g display-time 2000
+
+# default statusbar colors
+set -g status-fg white
+set -g status-bg default
+set -g status-attr default
+
+# default window title colors
+set-window-option -g window-status-fg cyan
+set-window-option -g window-status-bg default
+set-window-option -g window-status-attr dim
+
+# active window title colors
+set-window-option -g window-status-current-fg white
+set-window-option -g window-status-current-bg default
+set-window-option -g window-status-current-attr bright
+
+# command/message line colors
+set -g message-fg white
+set -g message-bg black
+set -g message-attr bright
+
+# center align the window list
+set -g status-justify centre
+
+# show some useful stats but only when tmux is started 
+# outside of Xorg, otherwise dwm statusbar shows these already
+set -g status-right ""
+set -g status-left ""
+if '[ -z "$DISPLAY" ]' 'set -g status-left "[#[fg=green] #H #[default]]"'
+if '[ -z "$DISPLAY" ]' 'set -g status-right "[ #[fg=magenta]#(cat /proc/loadavg | cut -d \" \" -f 1,2,3)#[default] ][ #[fg=cyan,bright]%a %Y-%m-%d %H:%M #[default]]"'
+if '[ -z "$DISPLAY" ]' 'set -g status-right-length 50'
+
diff --git a/external/bsd/tmux/dist/examples/tmux.vim b/external/bsd/tmux/dist/examples/tmux.vim
new file mode 100644 (file)
index 0000000..dce95e0
--- /dev/null
@@ -0,0 +1,104 @@
+" Vim syntax file
+" Language: tmux(1) configuration file
+" Maintainer: Tiago Cunha <me@tiagocunha.org>
+" Last Change: $Date: 2011/08/17 18:40:06 $
+" License: This file is placed in the public domain.
+
+if version < 600
+       syntax clear
+elseif exists("b:current_syntax")
+       finish
+endif
+
+setlocal iskeyword+=-
+syntax case match
+
+syn keyword tmuxAction any current none
+syn keyword tmuxBoolean        off on
+
+syn keyword tmuxCmds detach[-client] ls list-sessions neww new-window
+syn keyword tmuxCmds bind[-key] unbind[-key] prev[ious-window] last[-window]
+syn keyword tmuxCmds lsk list-keys set[-option] renamew rename-window selectw
+syn keyword tmuxCmds select-window lsw list-windows attach[-session]
+syn keyword tmuxCmds send-prefix refresh[-client] killw kill-window lsc
+syn keyword tmuxCmds list-clients linkw link-window unlinkw unlink-window
+syn keyword tmuxCmds next[-window] send[-keys] swapw swap-window
+syn keyword tmuxCmds rename[-session] kill-session switchc switch-client
+syn keyword tmuxCmds has[-session] copy-mode pasteb paste-buffer
+syn keyword tmuxCmds new[-session] start[-server] kill-server setw
+syn keyword tmuxCmds set-window-option show[-options] showw show-window-options
+syn keyword tmuxCmds command-prompt setb set-buffer showb show-buffer lsb
+syn keyword tmuxCmds list-buffers deleteb delete-buffer lscm list-commands
+syn keyword tmuxCmds movew move-window respawnw respawn-window
+syn keyword tmuxCmds source[-file] info server-info clock-mode lock[-server]
+syn keyword tmuxCmds saveb save-buffer killp
+syn keyword tmuxCmds kill-pane resizep resize-pane selectp select-pane swapp
+syn keyword tmuxCmds swap-pane splitw split-window choose-session
+syn keyword tmuxCmds choose-window loadb load-buffer copyb copy-buffer suspendc
+syn keyword tmuxCmds suspend-client findw find-window breakp break-pane nextl
+syn keyword tmuxCmds next-layout rotatew rotate-window confirm[-before]
+syn keyword tmuxCmds clearhist clear-history selectl select-layout if[-shell]
+syn keyword tmuxCmds display[-message] setenv set-environment showenv
+syn keyword tmuxCmds show-environment choose-client displayp display-panes
+syn keyword tmuxCmds run[-shell] lockc lock-client locks lock-session lsp
+syn keyword tmuxCmds list-panes pipep pipe-pane showmsgs show-messages capturep
+syn keyword tmuxCmds capture-pane joinp join-pane choose-buffer
+
+syn keyword tmuxOptsSet prefix status status-fg status-bg bell-action
+syn keyword tmuxOptsSet default-command history-limit status-left status-right
+syn keyword tmuxOptsSet status-interval set-titles display-time buffer-limit
+syn keyword tmuxOptsSet status-left-length status-right-length message-fg
+syn keyword tmuxOptsSet message-bg lock-after-time default-path repeat-time
+syn keyword tmuxOptsSet message-attr status-attr status-keys set-remain-on-exit
+syn keyword tmuxOptsSet status-utf8 default-terminal visual-activity
+syn keyword tmuxOptsSet visual-bell visual-content status-justify
+syn keyword tmuxOptsSet terminal-overrides status-left-attr status-left-bg
+syn keyword tmuxOptsSet status-left-fg status-right-attr status-right-bg
+syn keyword tmuxOptsSet status-right-fg update-environment base-index
+syn keyword tmuxOptsSet display-panes-colour display-panes-time default-shell
+syn keyword tmuxOptsSet set-titles-string lock-command lock-server
+syn keyword tmuxOptsSet mouse-select-pane message-limit quiet escape-time
+syn keyword tmuxOptsSet pane-active-border-bg pane-active-border-fg
+syn keyword tmuxOptsSet pane-border-bg pane-border-fg
+syn keyword tmuxOptsSet display-panes-active-colour alternate-screen
+syn keyword tmuxOptsSet detach-on-destroy
+
+syn keyword tmuxOptsSetw monitor-activity aggressive-resize force-width
+syn keyword tmuxOptsSetw force-height remain-on-exit uft8 mode-fg mode-bg
+syn keyword tmuxOptsSetw mode-keys clock-mode-colour clock-mode-style
+syn keyword tmuxOptsSetw xterm-keys mode-attr window-status-attr
+syn keyword tmuxOptsSetw window-status-bg window-status-fg automatic-rename
+syn keyword tmuxOptsSetw main-pane-width main-pane-height monitor-content
+syn keyword tmuxOptsSetw window-status-current-attr window-status-current-bg
+syn keyword tmuxOptsSetw window-status-current-fg mode-mouse synchronize-panes
+syn keyword tmuxOptsSetw window-status-format window-status-current-format
+syn keyword tmuxOptsSetw word-separators window-status-alert-alert
+syn keyword tmuxOptsSetw window-status-alert-bg window-status-alert-fg
+
+syn keyword tmuxTodo FIXME NOTE TODO XXX contained
+
+syn match tmuxKey              /\(C-\|M-\|\^\)\p/      display
+syn match tmuxNumber           /\d\+/                  display
+syn match tmuxOptions          /\s-\a\+/               display
+syn match tmuxVariable         /\w\+=/                 display
+syn match tmuxVariableExpansion        /\${\=\w\+}\=/          display
+
+syn region tmuxComment start=/#/ end=/$/ contains=tmuxTodo display oneline
+syn region tmuxString  start=/"/ end=/"/ display oneline
+syn region tmuxString  start=/'/ end=/'/ display oneline
+
+hi def link tmuxAction                 Boolean
+hi def link tmuxBoolean                        Boolean
+hi def link tmuxCmds                   Keyword
+hi def link tmuxComment                        Comment
+hi def link tmuxKey                    Special
+hi def link tmuxNumber                 Number
+hi def link tmuxOptions                        Identifier
+hi def link tmuxOptsSet                        Function
+hi def link tmuxOptsSetw               Function
+hi def link tmuxString                 String
+hi def link tmuxTodo                   Todo
+hi def link tmuxVariable               Constant
+hi def link tmuxVariableExpansion      Constant
+
+let b:current_syntax = "tmux"
diff --git a/external/bsd/tmux/dist/examples/tmux_backup.sh b/external/bsd/tmux/dist/examples/tmux_backup.sh
new file mode 100644 (file)
index 0000000..bc0bf37
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/bash
+#
+# By Victor Orlikowski. Public domain.
+#
+# This script maintains snapshots of each pane's
+# history buffer, for each tmux session you are running.
+# 
+# It is intended to be run by cron, on whatever interval works
+# for you.
+
+# Maximum number of snapshots to keep.
+max_backups=12
+# Names of sessions you may wish to exclude from snapshotting,
+# space separated.
+ignore_sessions=""
+# The directory into which you want your snapshots placed.
+# The default is probably "good enough."
+backup_dir=~/.tmux_backup/snapshot
+
+########################################################################
+
+# Rotate previous backups.
+i=${max_backups}
+while [[ ${i} != 0 ]] ; do
+if [ -d ${backup_dir}.${i} ] ; then
+  if [[ ${i} = ${max_backups} ]] ; then
+    rm -r ${backup_dir}.${i}
+  else
+    mv ${backup_dir}.${i} ${backup_dir}.$((${i}+1))
+  fi
+fi
+i=$((${i}-1))
+done
+
+if [ -d ${backup_dir} ] ; then
+  mv ${backup_dir} ${backup_dir}.1
+fi
+
+## Dump hardcopy from all windows in all available tmux sessions.
+unset TMUX
+for session in $(tmux list-sessions | cut -d' ' -f1 | sed -e 's/:$//') ; do
+  for ignore_session in ${ignore_sessions} ; do
+    if [ ${session} = ${ignore_session} ] ; then
+      continue 2
+    fi
+  done
+
+  # Session name can contain the colon character (":").
+  # This can screw up addressing of windows within tmux, since
+  # target windows are specified as target-session:target-window.
+  #
+  # We use uuidgen to create a "safe" temporary session name,
+  # which we then use to create a "detached" session that "links"
+  # to the "real" session that we want to back up.
+  tmpsession=$(uuidgen)
+  tmux new-session -d -s "$tmpsession" -t "$session"
+  HISTSIZE=$(tmux show-options -g -t "$tmpsession" | grep "history-limit" | awk '{print $2}')
+  for win in $(tmux list-windows -t "$tmpsession" | grep -v "^\s" | cut -d' ' -f1 | sed -e 's/:$//'); do
+    session_dir=$(echo "$session" | sed -e 's/ /_/g' | sed -e 's%/%|%g')
+    win_spec="$tmpsession":"$win"
+
+    if [ ! -d ${backup_dir}/${session_dir}/${win} ] ; then
+      mkdir -p ${backup_dir}/${session_dir}/${win}
+    fi
+
+    for pane in $(tmux list-panes -t "$win_spec" | cut -d' ' -f1 | sed -e 's/:$//'); do
+      pane_path=${backup_dir}/${session_dir}/${win}/${pane}
+      pane_spec="$win_spec"."$pane"
+
+      tmux capture-pane -t "$pane_spec" -S -${HISTSIZE}
+      tmux save-buffer ${pane_path}
+
+      if [ ! -s ${pane_path} ] ; then
+        sleep 1
+        rm ${pane_path}
+      fi
+    done
+  done
+  tmux kill-session -t "$tmpsession"
+
+done
diff --git a/external/bsd/tmux/dist/examples/vim-keys.conf b/external/bsd/tmux/dist/examples/vim-keys.conf
new file mode 100644 (file)
index 0000000..7c381ee
--- /dev/null
@@ -0,0 +1,36 @@
+# $Id: vim-keys.conf,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $
+#
+# vim-keys.conf, v1.2 2010/09/12
+#
+# By Daniel Thau.  Public domain.
+#
+# This configuration file binds many vi- and vim-like bindings to the
+# appropriate tmux key bindings.  Note that for many key bindings there is no
+# tmux analogue.  This is intended for tmux 1.3, which handles pane selection
+# differently from the previous versions
+
+# split windows like vim
+# vim's definition of a horizontal/vertical split is reversed from tmux's
+bind s split-window -v
+bind v split-window -h
+
+# move around panes with hjkl, as one would in vim after pressing ctrl-w
+bind h select-pane -L
+bind j select-pane -D
+bind k select-pane -U
+bind l select-pane -R
+
+# resize panes like vim
+# feel free to change the "1" to however many lines you want to resize by, only
+# one at a time can be slow
+bind < resize-pane -L 1
+bind > resize-pane -R 1
+bind - resize-pane -D 1
+bind + resize-pane -U 1
+
+# bind : to command-prompt like vim
+# this is the default in tmux already
+bind : command-prompt
+
+# vi-style controls for copy mode
+setw -g mode-keys vi
diff --git a/external/bsd/tmux/dist/grid-utf8.c b/external/bsd/tmux/dist/grid-utf8.c
new file mode 100644 (file)
index 0000000..397e416
--- /dev/null
@@ -0,0 +1,96 @@
+/* $Id: grid-utf8.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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"
+
+/*
+ * Grid UTF-8 utility functions.
+ */
+
+/* Calculate UTF-8 grid cell size. Data is terminated by 0xff. */
+size_t
+grid_utf8_size(const struct grid_utf8 *gu)
+{
+       size_t  size;
+
+       for (size = 0; size < sizeof gu->data; size++) {
+               if (gu->data[size] == 0xff)
+                       break;
+       }
+       return (size);
+}
+
+/* Copy UTF-8 out into a buffer. */
+size_t
+grid_utf8_copy(const struct grid_utf8 *gu, char *buf, size_t len)
+{
+       size_t  size;
+
+       size = grid_utf8_size(gu);
+       if (size > len)
+               fatalx("UTF-8 copy overflow");
+       memcpy(buf, gu->data, size);
+       return (size);
+}
+
+/* Set UTF-8 grid data from input UTF-8. */
+void
+grid_utf8_set(struct grid_utf8 *gu, const struct utf8_data *utf8data)
+{
+       if (utf8data->size == 0)
+               fatalx("UTF-8 data empty");
+       if (utf8data->size > sizeof gu->data)
+               fatalx("UTF-8 data too long");
+       memcpy(gu->data, utf8data->data, utf8data->size);
+       if (utf8data->size != sizeof gu->data)
+               gu->data[utf8data->size] = 0xff;
+       gu->width = utf8data->width;
+}
+
+/* Append UTF-8 character onto the cell data (for combined characters). */
+int
+grid_utf8_append(struct grid_utf8 *gu, const struct utf8_data *utf8data)
+{
+       size_t  old_size;
+
+       old_size = grid_utf8_size(gu);
+       if (old_size + utf8data->size > sizeof gu->data)
+               return (-1);
+       memcpy(gu->data + old_size, utf8data->data, utf8data->size);
+       if (old_size + utf8data->size != sizeof gu->data)
+               gu->data[old_size + utf8data->size] = 0xff;
+       return (0);
+}
+
+/* Compare two UTF-8 cells. */
+int
+grid_utf8_compare(const struct grid_utf8 *gu1, const struct grid_utf8 *gu2)
+{
+       size_t  size;
+
+       size = grid_utf8_size(gu1);
+       if (size != grid_utf8_size(gu2))
+               return (0);
+       if (memcmp(gu1->data, gu2->data, size) != 0)
+               return (0);
+       return (1);
+}
diff --git a/external/bsd/tmux/dist/grid-view.c b/external/bsd/tmux/dist/grid-view.c
new file mode 100644 (file)
index 0000000..b404147
--- /dev/null
@@ -0,0 +1,258 @@
+/* $Id: grid-view.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 into history. */
+void
+grid_view_clear_history(struct grid *gd)
+{
+       struct grid_line        *gl;
+       u_int                    yy, last;
+
+       GRID_DEBUG(gd, "");
+
+       /* Find the last used line. */
+       last = 0;
+       for (yy = 0; yy < gd->sy; yy++) {
+               gl = &gd->linedata[grid_view_y(gd, yy)];
+               if (gl->cellsize != 0 || gl->utf8size != 0)
+                       last = yy + 1;
+       }
+       if (last == 0)
+               return;
+
+       /* Scroll the lines into the history. */
+       for (yy = 0; yy < last; yy++)
+               grid_scroll_history(gd);
+}
+
+/* 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 (gd->flags & GRID_HISTORY) {
+               grid_collect_history(gd);
+               if (rupper == 0 && rlower == gd->sy - 1)
+                       grid_scroll_history(gd);
+               else {
+                       rupper = grid_view_y(gd, rupper);
+                       rlower = grid_view_y(gd, rlower);
+                       grid_scroll_history_region(gd, rupper, rlower);
+               }
+       } else {
+               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, u_int rlower, u_int py, u_int ny)
+{
+       u_int   ny2;
+
+       GRID_DEBUG(gd, "rlower=%u, py=%u, ny=%u", rlower, py, ny);
+
+       rlower = grid_view_y(gd, rlower);
+
+       py = grid_view_y(gd, py);
+
+       ny2 = rlower + 1 - py - ny;
+       grid_move_lines(gd, rlower + 1 - ny2, py, ny2);
+       grid_clear(gd, 0, py + ny2, gd->sx, ny - ny2);
+}
+
+/* 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);
+       grid_clear(gd, 0, sy - ny, gd->sx, py + ny - (sy - ny));
+}
+
+/* Delete lines inside scroll region. */
+void
+grid_view_delete_lines_region(struct grid *gd, u_int rlower, u_int py, u_int ny)
+{
+       u_int   ny2;
+
+       GRID_DEBUG(gd, "rlower=%u, py=%u, ny=%u", rlower, py, ny);
+
+       rlower = grid_view_y(gd, rlower);
+
+       py = grid_view_y(gd, py);
+
+       ny2 = rlower + 1 - py - ny;
+       grid_move_lines(gd, py, py + ny, ny2);
+       grid_clear(gd, 0, py + ny2, gd->sx, ny - ny2);
+}
+
+/* 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 - 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 - px - nx);
+       grid_clear(gd, sx - nx, py, px + nx - (sx - nx), 1);
+}
+
+/* Convert cells into a string. */
+char *
+grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx)
+{
+       GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx);
+
+       px = grid_view_x(gd, px);
+       py = grid_view_y(gd, py);
+
+       return (grid_string_cells(gd, px, py, nx));
+}
diff --git a/external/bsd/tmux/dist/grid.c b/external/bsd/tmux/dist/grid.c
new file mode 100644 (file)
index 0000000..16f8ed4
--- /dev/null
@@ -0,0 +1,545 @@
+/* $Id: grid.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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->linedata[py].celldata[px],                  \
+           gc, sizeof gd->linedata[py].celldata[px]);          \
+} while (0)
+#define grid_put_utf8(gd, px, py, gc) do {                     \
+       memcpy(&gd->linedata[py].utf8data[px],                  \
+           gc, sizeof gd->linedata[py].utf8data[px]);          \
+} while (0)
+
+int    grid_check_y(struct grid *, u_int);
+
+#ifdef DEBUG
+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_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->flags = GRID_HISTORY;
+
+       gd->hsize = 0;
+       gd->hlimit = hlimit;
+
+       gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
+
+       return (gd);
+}
+
+/* Destroy grid. */
+void
+grid_destroy(struct grid *gd)
+{
+       struct grid_line        *gl;
+       u_int                    yy;
+
+       for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
+               gl = &gd->linedata[yy];
+               if (gl->celldata != NULL)
+                       xfree(gl->celldata);
+               if (gl->utf8data != NULL)
+                       xfree(gl->utf8data);
+       }
+
+       xfree(gd->linedata);
+
+       xfree(gd);
+}
+
+/* Compare grids. */
+int
+grid_compare(struct grid *ga, struct grid *gb)
+{
+       struct grid_line        *gla, *glb;
+       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++) {
+               gla = &ga->linedata[yy];
+               glb = &gb->linedata[yy];
+               if (gla->cellsize != glb->cellsize)
+                       return (1);
+               for (xx = 0; xx < ga->sx; xx++) {
+                       gca = &gla->celldata[xx];
+                       gcb = &glb->celldata[xx];
+                       if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0)
+                               return (1);
+                       if (!(gca->flags & GRID_FLAG_UTF8))
+                               continue;
+                       gua = &gla->utf8data[xx];
+                       gub = &glb->utf8data[xx];
+                       if (memcmp(gua, gub, sizeof (struct grid_utf8)) != 0)
+                               return (1);
+               }
+       }
+
+       return (0);
+}
+
+/*
+ * Collect lines from the history if at the limit. Free the top (oldest) 10%
+ * and shift up.
+ */
+void
+grid_collect_history(struct grid *gd)
+{
+       u_int   yy;
+
+       GRID_DEBUG(gd, "");
+
+       if (gd->hsize < gd->hlimit)
+               return;
+
+       yy = gd->hlimit / 10;
+       if (yy < 1)
+               yy = 1;
+
+       grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy);
+       gd->hsize -= yy;
+}
+
+/*
+ * Scroll the entire visible screen, moving one line into the history. Just
+ * allocate a new line at the bottom and move the history size indicator.
+ */
+void
+grid_scroll_history(struct grid *gd)
+{
+       u_int   yy;
+
+       GRID_DEBUG(gd, "");
+
+       yy = gd->hsize + gd->sy;
+       gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata);
+       memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]);
+
+       gd->hsize++;
+}
+
+/* Scroll a region up, moving the top line into the history. */
+void
+grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower)
+{
+       struct grid_line        *gl_history, *gl_upper, *gl_lower;
+       u_int                    yy;
+
+       GRID_DEBUG(gd, "upper=%u, lower=%u", upper, lower);
+
+       /* Create a space for a new line. */
+       yy = gd->hsize + gd->sy;
+       gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata);
+
+       /* Move the entire screen down to free a space for this line. */
+       gl_history = &gd->linedata[gd->hsize];
+       memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history);
+
+       /* Adjust the region and find its start and end. */
+       upper++;
+       gl_upper = &gd->linedata[upper];
+       lower++;
+       gl_lower = &gd->linedata[lower];
+
+       /* Move the line into the history. */
+       memcpy(gl_history, gl_upper, sizeof *gl_history);
+
+       /* Then move the region up and clear the bottom line. */
+       memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
+       memset(gl_lower, 0, sizeof *gl_lower);
+
+       /* Move the history offset down over the line. */
+       gd->hsize++;
+}
+
+/* Expand line to fit to cell. */
+void
+grid_expand_line(struct grid *gd, u_int py, u_int sx)
+{
+       struct grid_line        *gl;
+       u_int                    xx;
+
+       gl = &gd->linedata[py];
+       if (sx <= gl->cellsize)
+               return;
+
+       gl->celldata = xrealloc(gl->celldata, sx, sizeof *gl->celldata);
+       for (xx = gl->cellsize; xx < sx; xx++)
+               grid_put_cell(gd, xx, py, &grid_default_cell);
+       gl->cellsize = sx;
+}
+
+/* Expand line to fit to cell for UTF-8. */
+void
+grid_expand_line_utf8(struct grid *gd, u_int py, u_int sx)
+{
+       struct grid_line        *gl;
+
+       gl = &gd->linedata[py];
+       if (sx <= gl->utf8size)
+               return;
+
+       gl->utf8data = xrealloc(gl->utf8data, sx, sizeof *gl->utf8data);
+       gl->utf8size = sx;
+}
+
+/* Get cell for reading. */
+const struct grid_cell *
+grid_peek_cell(struct grid *gd, u_int px, u_int py)
+{
+       if (grid_check_y(gd, py) != 0)
+               return (&grid_default_cell);
+
+       if (px >= gd->linedata[py].cellsize)
+               return (&grid_default_cell);
+       return (&gd->linedata[py].celldata[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_y(gd, py) != 0)
+               return (NULL);
+
+       grid_expand_line(gd, py, px + 1);
+       return (&gd->linedata[py].celldata[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_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_y(gd, py) != 0)
+               return (NULL);
+
+       if (px >= gd->linedata[py].utf8size)
+               return (NULL);
+       return (&gd->linedata[py].utf8data[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_y(gd, py) != 0)
+               return (NULL);
+
+       grid_expand_line_utf8(gd, py, px + 1);
+       return (&gd->linedata[py].utf8data[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_y(gd, py) != 0)
+               return;
+
+       grid_expand_line_utf8(gd, py, px + 1);
+       grid_put_utf8(gd, px, py, gc);
+}
+
+/* Clear area. */
+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_y(gd, py) != 0)
+               return;
+       if (grid_check_y(gd, py + ny - 1) != 0)
+               return;
+
+       for (yy = py; yy < py + ny; yy++) {
+               if (px >= gd->linedata[yy].cellsize)
+                       continue;
+               if (px + nx >= gd->linedata[yy].cellsize) {
+                       gd->linedata[yy].cellsize = px;
+                       continue;
+               }
+               for (xx = px; xx < px + nx; xx++) {
+                       if (xx >= gd->linedata[yy].cellsize)
+                               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)
+{
+       struct grid_line        *gl;
+       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++) {
+               gl = &gd->linedata[yy];
+               if (gl->celldata != NULL)
+                       xfree(gl->celldata);
+               if (gl->utf8data != NULL)
+                       xfree(gl->utf8data);
+               memset(gl, 0, sizeof *gl);
+       }
+}
+
+/* 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->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata));
+
+       /* Wipe any lines that have been moved (without freeing them). */
+       for (yy = py; yy < py + ny; yy++) {
+               if (yy >= dy && yy < dy + ny)
+                       continue;
+               memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]);
+       }
+}
+
+/* Move a group of cells. */
+void
+grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx)
+{
+       struct grid_line        *gl;
+       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_y(gd, py) != 0)
+               return;
+       gl = &gd->linedata[py];
+
+       grid_expand_line(gd, py, px + nx);
+       grid_expand_line(gd, py, dx + nx);
+       memmove(
+           &gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata);
+
+       if (gl->utf8data != NULL) {
+               grid_expand_line_utf8(gd, py, px + nx);
+               grid_expand_line_utf8(gd, py, dx + nx);
+               memmove(&gl->utf8data[dx],
+                   &gl->utf8data[px], nx * sizeof *gl->utf8data);
+       }
+
+       /* 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);
+       }
+}
+
+/* Convert cells into a string. */
+char *
+grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx)
+{
+       const struct grid_cell  *gc;
+       const struct grid_utf8  *gu;
+       char                    *buf;
+       size_t                   len, off, size;
+       u_int                    xx;
+
+       GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx);
+
+       len = 128;
+       buf = xmalloc(len);
+       off = 0;
+
+       for (xx = px; xx < px + nx; xx++) {
+               gc = grid_peek_cell(gd, xx, py);
+               if (gc->flags & GRID_FLAG_PADDING)
+                       continue;
+
+               if (gc->flags & GRID_FLAG_UTF8) {
+                       gu = grid_peek_utf8(gd, xx, py);
+
+                       size = grid_utf8_size(gu);
+                       while (len < off + size + 1) {
+                               buf = xrealloc(buf, 2, len);
+                               len *= 2;
+                       }
+
+                       off += grid_utf8_copy(gu, buf + off, len - off);
+               } else {
+                       while (len < off + 2) {
+                               buf = xrealloc(buf, 2, len);
+                               len *= 2;
+                       }
+
+                       buf[off++] = gc->data;
+               }
+       }
+
+       while (off > 0 && buf[off - 1] == ' ')
+               off--;
+       buf[off] = '\0';
+       return (buf);
+}
+
+/*
+ * Duplicate a set of lines between two grids. If there aren't enough lines in
+ * either source or destination, the number of lines is limited to the number
+ * available.
+ */
+void
+grid_duplicate_lines(
+    struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny)
+{
+       struct grid_line        *dstl, *srcl;
+       u_int                    yy;
+
+       GRID_DEBUG(src, "dy=%u, sy=%u, ny=%u", dy, sy, ny);
+
+       if (dy + ny > dst->hsize + dst->sy)
+               ny = dst->hsize + dst->sy - dy;
+       if (sy + ny > src->hsize + src->sy)
+               ny = src->hsize + src->sy - sy;
+       grid_clear_lines(dst, dy, ny);
+
+       for (yy = 0; yy < ny; yy++) {
+               srcl = &src->linedata[sy];
+               dstl = &dst->linedata[dy];
+
+               memcpy(dstl, srcl, sizeof *dstl);
+               if (srcl->cellsize != 0) {
+                       dstl->celldata = xcalloc(
+                           srcl->cellsize, sizeof *dstl->celldata);
+                       memcpy(dstl->celldata, srcl->celldata,
+                           srcl->cellsize * sizeof *dstl->celldata);
+               }
+               if (srcl->utf8size != 0) {
+                       dstl->utf8data = xcalloc(
+                           srcl->utf8size, sizeof *dstl->utf8data);
+                       memcpy(dstl->utf8data, srcl->utf8data,
+                           srcl->utf8size * sizeof *dstl->utf8data);
+               }
+
+               sy++;
+               dy++;
+       }
+}
diff --git a/external/bsd/tmux/dist/input-keys.c b/external/bsd/tmux/dist/input-keys.c
new file mode 100644 (file)
index 0000000..77893a7
--- /dev/null
@@ -0,0 +1,233 @@
+/* $Id: input-keys.c,v 1.2 2011/08/17 19:28:36 jmmv 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"
+
+/*
+ * This file is rather misleadingly named, it contains the code which takes a
+ * key code and translates it into something suitable to be sent to the
+ * application running in a pane (similar to input.c does in the other
+ * direction with output).
+ */
+
+struct input_key_ent {
+       int              key;
+       const char      *data;
+
+       int              flags;
+#define INPUTKEY_KEYPAD 0x1    /* keypad key */
+#define INPUTKEY_CURSOR 0x2    /* cursor key */
+};
+
+const struct input_key_ent input_keys[] = {
+       /* Backspace key. */
+       { KEYC_BSPACE,          "\177",         0 },
+
+       /* Function keys. */
+       { KEYC_F1,              "\033OP",       0 },
+       { KEYC_F2,              "\033OQ",       0 },
+       { KEYC_F3,              "\033OR",       0 },
+       { KEYC_F4,              "\033OS",       0 },
+       { KEYC_F5,              "\033[15~",     0 },
+       { KEYC_F6,              "\033[17~",     0 },
+       { KEYC_F7,              "\033[18~",     0 },
+       { KEYC_F8,              "\033[19~",     0 },
+       { KEYC_F9,              "\033[20~",     0 },
+       { KEYC_F10,             "\033[21~",     0 },
+       { KEYC_F11,             "\033[23~",     0 },
+       { KEYC_F12,             "\033[24~",     0 },
+       { KEYC_F13,             "\033[25~",     0 },
+       { KEYC_F14,             "\033[26~",     0 },
+       { KEYC_F15,             "\033[28~",     0 },
+       { KEYC_F16,             "\033[29~",     0 },
+       { KEYC_F17,             "\033[31~",     0 },
+       { KEYC_F18,             "\033[32~",     0 },
+       { KEYC_F19,             "\033[33~",     0 },
+       { KEYC_F20,             "\033[34~",     0 },
+       { KEYC_IC,              "\033[2~",      0 },
+       { KEYC_DC,              "\033[3~",      0 },
+       { KEYC_HOME,            "\033[1~",      0 },
+       { KEYC_END,             "\033[4~",      0 },
+       { KEYC_NPAGE,           "\033[6~",      0 },
+       { KEYC_PPAGE,           "\033[5~",      0 },
+       { KEYC_BTAB,            "\033[Z",       0 },
+
+       /*
+        * Arrow keys. Cursor versions must come first. The codes are toggled
+        * between CSI and SS3 versions when ctrl is pressed.
+        */
+       { KEYC_UP|KEYC_CTRL,    "\033[A",       INPUTKEY_CURSOR },
+       { KEYC_DOWN|KEYC_CTRL,  "\033[B",       INPUTKEY_CURSOR },
+       { KEYC_RIGHT|KEYC_CTRL, "\033[C",       INPUTKEY_CURSOR },
+       { KEYC_LEFT|KEYC_CTRL,  "\033[D",       INPUTKEY_CURSOR },
+
+       { KEYC_UP,              "\033OA",       INPUTKEY_CURSOR },
+       { KEYC_DOWN,            "\033OB",       INPUTKEY_CURSOR },
+       { KEYC_RIGHT,           "\033OC",       INPUTKEY_CURSOR },
+       { KEYC_LEFT,            "\033OD",       INPUTKEY_CURSOR },
+
+       { KEYC_UP|KEYC_CTRL,    "\033OA",       0 },
+       { KEYC_DOWN|KEYC_CTRL,  "\033OB",       0 },
+       { KEYC_RIGHT|KEYC_CTRL, "\033OC",       0 },
+       { KEYC_LEFT|KEYC_CTRL,  "\033OD",       0 },
+
+       { 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_KP_SLASH,        "\033Oo",       INPUTKEY_KEYPAD },
+       { KEYC_KP_STAR,         "\033Oj",       INPUTKEY_KEYPAD },
+       { KEYC_KP_MINUS,        "\033Om",       INPUTKEY_KEYPAD },
+       { KEYC_KP_SEVEN,        "\033Ow",       INPUTKEY_KEYPAD },
+       { KEYC_KP_EIGHT,        "\033Ox",       INPUTKEY_KEYPAD },
+       { KEYC_KP_NINE,         "\033Oy",       INPUTKEY_KEYPAD },
+       { KEYC_KP_PLUS,         "\033Ok",       INPUTKEY_KEYPAD },
+       { KEYC_KP_FOUR,         "\033Ot",       INPUTKEY_KEYPAD },
+       { KEYC_KP_FIVE,         "\033Ou",       INPUTKEY_KEYPAD },
+       { KEYC_KP_SIX,          "\033Ov",       INPUTKEY_KEYPAD },
+       { KEYC_KP_ONE,          "\033Oq",       INPUTKEY_KEYPAD },
+       { KEYC_KP_TWO,          "\033Or",       INPUTKEY_KEYPAD },
+       { KEYC_KP_THREE,        "\033Os",       INPUTKEY_KEYPAD },
+       { KEYC_KP_ENTER,        "\033OM",       INPUTKEY_KEYPAD },
+       { KEYC_KP_ZERO,         "\033Op",       INPUTKEY_KEYPAD },
+       { KEYC_KP_PERIOD,       "\033On",       INPUTKEY_KEYPAD },
+
+       { KEYC_KP_SLASH,        "/",            0 },
+       { KEYC_KP_STAR,         "*",            0 },
+       { KEYC_KP_MINUS,        "-",            0 },
+       { KEYC_KP_SEVEN,        "7",            0 },
+       { KEYC_KP_EIGHT,        "8",            0 },
+       { KEYC_KP_NINE,         "9",            0 },
+       { KEYC_KP_PLUS,         "+",            0 },
+       { KEYC_KP_FOUR,         "4",            0 },
+       { KEYC_KP_FIVE,         "5",            0 },
+       { KEYC_KP_SIX,          "6",            0 },
+       { KEYC_KP_ONE,          "1",            0 },
+       { KEYC_KP_TWO,          "2",            0 },
+       { KEYC_KP_THREE,        "3",            0 },
+       { KEYC_KP_ENTER,        "\n",           0 },
+       { KEYC_KP_ZERO,         "0",            0 },
+       { KEYC_KP_PERIOD,       ".",            0 },
+};
+
+/* Translate a key code into an output key sequence. */
+void
+input_key(struct window_pane *wp, int key)
+{
+       const struct input_key_ent     *ike;
+       u_int                           i;
+       size_t                          dlen;
+       char                           *out;
+       u_char                          ch;
+
+       log_debug2("writing key 0x%x", key);
+
+       /*
+        * If this is a normal 7-bit key, just send it, with a leading escape
+        * if necessary.
+        */
+       if (key != KEYC_NONE && (key & ~KEYC_ESCAPE) < 0x100) {
+               if (key & KEYC_ESCAPE)
+                       bufferevent_write(wp->event, "\033", 1);
+               ch = key & ~KEYC_ESCAPE;
+               bufferevent_write(wp->event, &ch, 1);
+               return;
+       }
+
+       /*
+        * Then try to look this up as an xterm key, if the flag to output them
+        * is set.
+        */
+       if (options_get_number(&wp->window->options, "xterm-keys")) {
+               if ((out = xterm_keys_lookup(key)) != NULL) {
+                       bufferevent_write(wp->event, out, strlen(out));
+                       xfree(out);
+                       return;
+               }
+       }
+
+       /* Otherwise look the key up in the table. */
+       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 ((key & KEYC_ESCAPE) && (ike->key | KEYC_ESCAPE) == key)
+                       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);
+
+       /* Prefix a \033 for escape. */
+       if (key & KEYC_ESCAPE)
+               bufferevent_write(wp->event, "\033", 1);
+       bufferevent_write(wp->event, ike->data, dlen);
+}
+
+/* Translate mouse and output. */
+void
+input_mouse(struct window_pane *wp, struct mouse_event *m)
+{
+       char    buf[10];
+       size_t  len;
+
+       if (wp->screen->mode & ALL_MOUSE_MODES) {
+               if (wp->screen->mode & MODE_MOUSE_UTF8) {
+                       len = xsnprintf(buf, sizeof buf, "\033[M");
+                       len += utf8_split2(m->b + 32,
+                           (unsigned char *)&buf[len]);
+                       len += utf8_split2(m->x + 33,
+                           (unsigned char *)&buf[len]);
+                       len += utf8_split2(m->y + 33,
+                           (unsigned char *)&buf[len]);
+               } else {
+                       if (m->b > 223 || m->x >= 222 || m->y > 222)
+                               return;
+                       len = xsnprintf(buf, sizeof buf, "\033[M");
+                       buf[len++] = m->b + 32;
+                       buf[len++] = m->x + 33;
+                       buf[len++] = m->y + 33;
+               }
+               bufferevent_write(wp->event, buf, len);
+       } else if ((m->b & MOUSE_BUTTON) != MOUSE_2) {
+               if (options_get_number(&wp->window->options, "mode-mouse") &&
+                   window_pane_set_mode(wp, &window_copy_mode) == 0) {
+                       window_copy_init_from_pane(wp);
+                       if (wp->mode->mouse != NULL)
+                               wp->mode->mouse(wp, NULL, m);
+               }
+       }
+}
diff --git a/external/bsd/tmux/dist/input.c b/external/bsd/tmux/dist/input.c
new file mode 100644 (file)
index 0000000..3a1bbe2
--- /dev/null
@@ -0,0 +1,1572 @@
+/* $Id: input.c,v 1.4 2011/08/17 19:28:36 jmmv 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"
+
+/*
+ * Based on the description by Paul Williams at:
+ *
+ * http://vt100.net/emu/dec_ansi_parser
+ *
+ * With the following changes:
+ *
+ * - 7-bit only.
+ *
+ * - Support for UTF-8.
+ *
+ * - OSC (but not APC) may be terminated by \007 as well as ST.
+ *
+ * - A state for APC similar to OSC. Some terminals appear to use this to set
+ *   the title.
+ *
+ * - A state for the screen \033k...\033\\ sequence to rename a window. This is
+ *   pretty stupid but not supporting it is more trouble than it is worth.
+ *
+ * - Special handling for ESC inside a DCS to allow arbitrary byte sequences to
+ *   be passed to the underlying teminal(s).
+ */
+
+/* Helper functions. */
+int    input_split(struct input_ctx *);
+int    input_get(struct input_ctx *, u_int, int, int);
+void   input_reply(struct input_ctx *, const char *, ...);
+
+/* Transition entry/exit handlers. */
+void   input_clear(struct input_ctx *);
+void   input_enter_osc(struct input_ctx *);
+void   input_exit_osc(struct input_ctx *);
+void   input_enter_apc(struct input_ctx *);
+void   input_exit_apc(struct input_ctx *);
+void   input_enter_rename(struct input_ctx *);
+void   input_exit_rename(struct input_ctx *);
+
+/* Input state handlers. */
+int    input_print(struct input_ctx *);
+int    input_intermediate(struct input_ctx *);
+int    input_parameter(struct input_ctx *);
+int    input_input(struct input_ctx *);
+int    input_c0_dispatch(struct input_ctx *);
+int    input_esc_dispatch(struct input_ctx *);
+int    input_csi_dispatch(struct input_ctx *);
+void   input_csi_dispatch_sgr(struct input_ctx *);
+int    input_dcs_dispatch(struct input_ctx *);
+int    input_utf8_open(struct input_ctx *);
+int    input_utf8_add(struct input_ctx *);
+int    input_utf8_close(struct input_ctx *);
+
+/* Command table comparison function. */
+int    input_table_compare(const void *, const void *);
+
+/* Command table entry. */
+struct input_table_entry {
+       int             ch;
+       const char     *interm;
+       int             type;
+};
+
+/* Escape commands. */
+enum input_esc_type {
+       INPUT_ESC_DECALN,
+       INPUT_ESC_DECKPAM,
+       INPUT_ESC_DECKPNM,
+       INPUT_ESC_DECRC,
+       INPUT_ESC_DECSC,
+       INPUT_ESC_HTS,
+       INPUT_ESC_IND,
+       INPUT_ESC_NEL,
+       INPUT_ESC_RI,
+       INPUT_ESC_RIS,
+       INPUT_ESC_SCSOFF_G0,
+       INPUT_ESC_SCSON_G0,
+};
+
+/* Escape command table. */
+const struct input_table_entry input_esc_table[] = {
+       { '0', "(", INPUT_ESC_SCSOFF_G0 },
+       { '7', "",  INPUT_ESC_DECSC },
+       { '8', "",  INPUT_ESC_DECRC },
+       { '8', "#", INPUT_ESC_DECALN },
+       { '=', "",  INPUT_ESC_DECKPAM },
+       { '>', "",  INPUT_ESC_DECKPNM },
+       { 'B', "(", INPUT_ESC_SCSON_G0 },
+       { 'D', "",  INPUT_ESC_IND },
+       { 'E', "",  INPUT_ESC_NEL },
+       { 'H', "",  INPUT_ESC_HTS },
+       { 'M', "",  INPUT_ESC_RI },
+       { 'c', "",  INPUT_ESC_RIS },
+};
+
+/* Control (CSI) commands. */
+enum input_csi_type {
+       INPUT_CSI_CBT,
+       INPUT_CSI_CUB,
+       INPUT_CSI_CUD,
+       INPUT_CSI_CUF,
+       INPUT_CSI_CUP,
+       INPUT_CSI_CUU,
+       INPUT_CSI_DA,
+       INPUT_CSI_DCH,
+       INPUT_CSI_DECSCUSR,
+       INPUT_CSI_DECSTBM,
+       INPUT_CSI_DL,
+       INPUT_CSI_DSR,
+       INPUT_CSI_ED,
+       INPUT_CSI_EL,
+       INPUT_CSI_HPA,
+       INPUT_CSI_ICH,
+       INPUT_CSI_IL,
+       INPUT_CSI_RM,
+       INPUT_CSI_RM_PRIVATE,
+       INPUT_CSI_SGR,
+       INPUT_CSI_SM,
+       INPUT_CSI_SM_PRIVATE,
+       INPUT_CSI_TBC,
+       INPUT_CSI_VPA,
+};
+
+/* Control (CSI) command table. */
+const struct input_table_entry input_csi_table[] = {
+       { '@', "",  INPUT_CSI_ICH },
+       { 'A', "",  INPUT_CSI_CUU },
+       { 'B', "",  INPUT_CSI_CUD },
+       { 'C', "",  INPUT_CSI_CUF },
+       { 'D', "",  INPUT_CSI_CUB },
+       { 'G', "",  INPUT_CSI_HPA },
+       { 'H', "",  INPUT_CSI_CUP },
+       { 'J', "",  INPUT_CSI_ED },
+       { 'K', "",  INPUT_CSI_EL },
+       { 'L', "",  INPUT_CSI_IL },
+       { 'M', "",  INPUT_CSI_DL },
+       { 'P', "",  INPUT_CSI_DCH },
+       { 'Z', "",  INPUT_CSI_CBT },
+       { 'c', "",  INPUT_CSI_DA },
+       { 'd', "",  INPUT_CSI_VPA },
+       { 'f', "",  INPUT_CSI_CUP },
+       { 'g', "",  INPUT_CSI_TBC },
+       { 'h', "",  INPUT_CSI_SM },
+       { 'h', "?", INPUT_CSI_SM_PRIVATE },
+       { 'l', "",  INPUT_CSI_RM },
+       { 'l', "?", INPUT_CSI_RM_PRIVATE },
+       { 'm', "",  INPUT_CSI_SGR },
+       { 'n', "",  INPUT_CSI_DSR },
+       { 'q', " ", INPUT_CSI_DECSCUSR },
+       { 'r', "",  INPUT_CSI_DECSTBM },
+};
+
+/* Input transition. */
+struct input_transition {
+       int                             first;
+       int                             last;
+
+       int                             (*handler)(struct input_ctx *);
+       const struct input_state       *state;
+};
+
+/* Input state. */
+struct input_state {
+       const char                      *name;
+       void                            (*enter)(struct input_ctx *);
+       void                            (*exit)(struct input_ctx *);
+       const struct input_transition   *transitions;
+};
+
+/* State transitions available from all states. */
+#define INPUT_STATE_ANYWHERE \
+       { 0x18, 0x18, input_c0_dispatch, &input_state_ground }, \
+       { 0x1a, 0x1a, input_c0_dispatch, &input_state_ground }, \
+       { 0x1b, 0x1b, NULL,              &input_state_esc_enter }
+
+/* Forward declarations of state tables. */
+const struct input_transition input_state_ground_table[];
+const struct input_transition input_state_esc_enter_table[];
+const struct input_transition input_state_esc_intermediate_table[];
+const struct input_transition input_state_csi_enter_table[];
+const struct input_transition input_state_csi_parameter_table[];
+const struct input_transition input_state_csi_intermediate_table[];
+const struct input_transition input_state_csi_ignore_table[];
+const struct input_transition input_state_dcs_enter_table[];
+const struct input_transition input_state_dcs_parameter_table[];
+const struct input_transition input_state_dcs_intermediate_table[];
+const struct input_transition input_state_dcs_handler_table[];
+const struct input_transition input_state_dcs_escape_table[];
+const struct input_transition input_state_dcs_ignore_table[];
+const struct input_transition input_state_osc_string_table[];
+const struct input_transition input_state_apc_string_table[];
+const struct input_transition input_state_rename_string_table[];
+const struct input_transition input_state_consume_st_table[];
+const struct input_transition input_state_utf8_three_table[];
+const struct input_transition input_state_utf8_two_table[];
+const struct input_transition input_state_utf8_one_table[];
+
+/* ground state definition. */
+const struct input_state input_state_ground = {
+       "ground",
+       NULL, NULL,
+       input_state_ground_table
+};
+
+/* esc_enter state definition. */
+const struct input_state input_state_esc_enter = {
+       "esc_enter",
+       input_clear, NULL,
+       input_state_esc_enter_table
+};
+
+/* esc_intermediate state definition. */
+const struct input_state input_state_esc_intermediate = {
+       "esc_intermediate",
+       NULL, NULL,
+       input_state_esc_intermediate_table
+};
+
+/* csi_enter state definition. */
+const struct input_state input_state_csi_enter = {
+       "csi_enter",
+       input_clear, NULL,
+       input_state_csi_enter_table
+};
+
+/* csi_parameter state definition. */
+const struct input_state input_state_csi_parameter = {
+       "csi_parameter",
+       NULL, NULL,
+       input_state_csi_parameter_table
+};
+
+/* csi_intermediate state definition. */
+const struct input_state input_state_csi_intermediate = {
+       "csi_intermediate",
+       NULL, NULL,
+       input_state_csi_intermediate_table
+};
+
+/* csi_ignore state definition. */
+const struct input_state input_state_csi_ignore = {
+       "csi_ignore",
+       NULL, NULL,
+       input_state_csi_ignore_table
+};
+
+/* dcs_enter state definition. */
+const struct input_state input_state_dcs_enter = {
+       "dcs_enter",
+       input_clear, NULL,
+       input_state_dcs_enter_table
+};
+
+/* dcs_parameter state definition. */
+const struct input_state input_state_dcs_parameter = {
+       "dcs_parameter",
+       NULL, NULL,
+       input_state_dcs_parameter_table
+};
+
+/* dcs_intermediate state definition. */
+const struct input_state input_state_dcs_intermediate = {
+       "dcs_intermediate",
+       NULL, NULL,
+       input_state_dcs_intermediate_table
+};
+
+/* dcs_handler state definition. */
+const struct input_state input_state_dcs_handler = {
+       "dcs_handler",
+       NULL, NULL,
+       input_state_dcs_handler_table
+};
+
+/* dcs_escape state definition. */
+const struct input_state input_state_dcs_escape = {
+       "dcs_escape",
+       NULL, NULL,
+       input_state_dcs_escape_table
+};
+
+/* dcs_ignore state definition. */
+const struct input_state input_state_dcs_ignore = {
+       "dcs_ignore",
+       NULL, NULL,
+       input_state_dcs_ignore_table
+};
+
+/* osc_string state definition. */
+const struct input_state input_state_osc_string = {
+       "osc_string",
+       input_enter_osc, input_exit_osc,
+       input_state_osc_string_table
+};
+
+/* apc_string state definition. */
+const struct input_state input_state_apc_string = {
+       "apc_string",
+       input_enter_apc, input_exit_apc,
+       input_state_apc_string_table
+};
+
+/* rename_string state definition. */
+const struct input_state input_state_rename_string = {
+       "rename_string",
+       input_enter_rename, input_exit_rename,
+       input_state_rename_string_table
+};
+
+/* consume_st state definition. */
+const struct input_state input_state_consume_st = {
+       "consume_st",
+       NULL, NULL,
+       input_state_consume_st_table
+};
+
+/* utf8_three state definition. */
+const struct input_state input_state_utf8_three = {
+       "utf8_three",
+       NULL, NULL,
+       input_state_utf8_three_table
+};
+
+/* utf8_two state definition. */
+const struct input_state input_state_utf8_two = {
+       "utf8_two",
+       NULL, NULL,
+       input_state_utf8_two_table
+};
+
+/* utf8_one state definition. */
+const struct input_state input_state_utf8_one = {
+       "utf8_one",
+       NULL, NULL,
+       input_state_utf8_one_table
+};
+
+/* ground state table. */
+const struct input_transition input_state_ground_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x17, input_c0_dispatch, NULL },
+       { 0x19, 0x19, input_c0_dispatch, NULL },
+       { 0x1c, 0x1f, input_c0_dispatch, NULL },
+       { 0x20, 0x7e, input_print,       NULL },
+       { 0x7f, 0x7f, NULL,              NULL },
+       { 0x80, 0xc1, input_print,       NULL },
+       { 0xc2, 0xdf, input_utf8_open,   &input_state_utf8_one },
+       { 0xe0, 0xef, input_utf8_open,   &input_state_utf8_two },
+       { 0xf0, 0xf4, input_utf8_open,   &input_state_utf8_three },
+       { 0xf5, 0xff, input_print,       NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* esc_enter state table. */
+const struct input_transition input_state_esc_enter_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x17, input_c0_dispatch,  NULL },
+       { 0x19, 0x19, input_c0_dispatch,  NULL },
+       { 0x1c, 0x1f, input_c0_dispatch,  NULL },
+       { 0x20, 0x2f, input_intermediate, &input_state_esc_intermediate },
+       { 0x30, 0x4f, input_esc_dispatch, &input_state_ground },
+       { 0x50, 0x50, NULL,               &input_state_dcs_enter },
+       { 0x51, 0x57, input_esc_dispatch, &input_state_ground },
+       { 0x58, 0x58, NULL,               &input_state_consume_st },
+       { 0x59, 0x59, input_esc_dispatch, &input_state_ground },
+       { 0x5a, 0x5a, input_esc_dispatch, &input_state_ground },
+       { 0x5b, 0x5b, NULL,               &input_state_csi_enter },
+       { 0x5c, 0x5c, input_esc_dispatch, &input_state_ground },
+       { 0x5d, 0x5d, NULL,               &input_state_osc_string },
+       { 0x5e, 0x5e, NULL,               &input_state_consume_st },
+       { 0x5f, 0x5f, NULL,               &input_state_apc_string },
+       { 0x60, 0x6a, input_esc_dispatch, &input_state_ground },
+       { 0x6b, 0x6b, NULL,               &input_state_rename_string },
+       { 0x6c, 0x7e, input_esc_dispatch, &input_state_ground },
+       { 0x7f, 0xff, NULL,               NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* esc_interm state table. */
+const struct input_transition input_state_esc_intermediate_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x17, input_c0_dispatch,  NULL },
+       { 0x19, 0x19, input_c0_dispatch,  NULL },
+       { 0x1c, 0x1f, input_c0_dispatch,  NULL },
+       { 0x20, 0x2f, input_intermediate, NULL },
+       { 0x30, 0x7e, input_esc_dispatch, &input_state_ground },
+       { 0x7f, 0xff, NULL,               NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* csi_enter state table. */
+const struct input_transition input_state_csi_enter_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x17, input_c0_dispatch,  NULL },
+       { 0x19, 0x19, input_c0_dispatch,  NULL },
+       { 0x1c, 0x1f, input_c0_dispatch,  NULL },
+       { 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate },
+       { 0x30, 0x39, input_parameter,    &input_state_csi_parameter },
+       { 0x3a, 0x3a, NULL,               &input_state_csi_ignore },
+       { 0x3b, 0x3b, input_parameter,    &input_state_csi_parameter },
+       { 0x3c, 0x3f, input_intermediate, &input_state_csi_parameter },
+       { 0x40, 0x7e, input_csi_dispatch, &input_state_ground },
+       { 0x7f, 0xff, NULL,               NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* csi_parameter state table. */
+const struct input_transition input_state_csi_parameter_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x17, input_c0_dispatch,  NULL },
+       { 0x19, 0x19, input_c0_dispatch,  NULL },
+       { 0x1c, 0x1f, input_c0_dispatch,  NULL },
+       { 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate },
+       { 0x30, 0x39, input_parameter,    NULL },
+       { 0x3a, 0x3a, NULL,               &input_state_csi_ignore },
+       { 0x3b, 0x3b, input_parameter,    NULL },
+       { 0x3c, 0x3f, NULL,               &input_state_csi_ignore },
+       { 0x40, 0x7e, input_csi_dispatch, &input_state_ground },
+       { 0x7f, 0xff, NULL,               NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* csi_intermediate state table. */
+const struct input_transition input_state_csi_intermediate_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x17, input_c0_dispatch,  NULL },
+       { 0x19, 0x19, input_c0_dispatch,  NULL },
+       { 0x1c, 0x1f, input_c0_dispatch,  NULL },
+       { 0x20, 0x2f, input_intermediate, NULL },
+       { 0x30, 0x3f, NULL,               &input_state_csi_ignore },
+       { 0x40, 0x7e, input_csi_dispatch, &input_state_ground },
+       { 0x7f, 0xff, NULL,               NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* csi_ignore state table. */
+const struct input_transition input_state_csi_ignore_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x17, input_c0_dispatch, NULL },
+       { 0x19, 0x19, input_c0_dispatch, NULL },
+       { 0x1c, 0x1f, input_c0_dispatch, NULL },
+       { 0x20, 0x3f, NULL,              NULL },
+       { 0x40, 0x7e, NULL,              &input_state_ground },
+       { 0x7f, 0xff, NULL,              NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* dcs_enter state table. */
+const struct input_transition input_state_dcs_enter_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x17, NULL,               NULL },
+       { 0x19, 0x19, NULL,               NULL },
+       { 0x1c, 0x1f, NULL,               NULL },
+       { 0x20, 0x2f, input_intermediate, &input_state_dcs_intermediate },
+       { 0x30, 0x39, input_parameter,    &input_state_dcs_parameter },
+       { 0x3a, 0x3a, NULL,               &input_state_dcs_ignore },
+       { 0x3b, 0x3b, input_parameter,    &input_state_dcs_parameter },
+       { 0x3c, 0x3f, input_intermediate, &input_state_dcs_parameter },
+       { 0x40, 0x7e, input_input,        &input_state_dcs_handler },
+       { 0x7f, 0xff, NULL,               NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* dcs_parameter state table. */
+const struct input_transition input_state_dcs_parameter_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x17, NULL,               NULL },
+       { 0x19, 0x19, NULL,               NULL },
+       { 0x1c, 0x1f, NULL,               NULL },
+       { 0x20, 0x2f, input_intermediate, &input_state_dcs_intermediate },
+       { 0x30, 0x39, input_parameter,    NULL },
+       { 0x3a, 0x3a, NULL,               &input_state_dcs_ignore },
+       { 0x3b, 0x3b, input_parameter,    NULL },
+       { 0x3c, 0x3f, NULL,               &input_state_dcs_ignore },
+       { 0x40, 0x7e, input_input,        &input_state_dcs_handler },
+       { 0x7f, 0xff, NULL,               NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* dcs_interm state table. */
+const struct input_transition input_state_dcs_intermediate_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x17, NULL,               NULL },
+       { 0x19, 0x19, NULL,               NULL },
+       { 0x1c, 0x1f, NULL,               NULL },
+       { 0x20, 0x2f, input_intermediate, NULL },
+       { 0x30, 0x3f, NULL,               &input_state_dcs_ignore },
+       { 0x40, 0x7e, input_input,        &input_state_dcs_handler },
+       { 0x7f, 0xff, NULL,               NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* dcs_handler state table. */
+const struct input_transition input_state_dcs_handler_table[] = {
+       /* No INPUT_STATE_ANYWHERE */
+
+       { 0x00, 0x1a, input_input,  NULL },
+       { 0x1b, 0x1b, NULL,         &input_state_dcs_escape },
+       { 0x1c, 0xff, input_input,  NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* dcs_escape state table. */
+const struct input_transition input_state_dcs_escape_table[] = {
+       /* No INPUT_STATE_ANYWHERE */
+
+       { 0x00, 0x5b, input_input,        &input_state_dcs_handler },
+       { 0x5c, 0x5c, input_dcs_dispatch, &input_state_ground },
+       { 0x5d, 0xff, input_input,        &input_state_dcs_handler },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* dcs_ignore state table. */
+const struct input_transition input_state_dcs_ignore_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x17, NULL,         NULL },
+       { 0x19, 0x19, NULL,         NULL },
+       { 0x1c, 0x1f, NULL,         NULL },
+       { 0x20, 0xff, NULL,         NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* osc_string state table. */
+const struct input_transition input_state_osc_string_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x06, NULL,         NULL },
+       { 0x07, 0x07, NULL,         &input_state_ground },
+       { 0x08, 0x17, NULL,         NULL },
+       { 0x19, 0x19, NULL,         NULL },
+       { 0x1c, 0x1f, NULL,         NULL },
+       { 0x20, 0xff, input_input,  NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* apc_string state table. */
+const struct input_transition input_state_apc_string_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x17, NULL,         NULL },
+       { 0x19, 0x19, NULL,         NULL },
+       { 0x1c, 0x1f, NULL,         NULL },
+       { 0x20, 0xff, input_input,  NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* rename_string state table. */
+const struct input_transition input_state_rename_string_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x17, NULL,         NULL },
+       { 0x19, 0x19, NULL,         NULL },
+       { 0x1c, 0x1f, NULL,         NULL },
+       { 0x20, 0xff, input_input,  NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* consume_st state table. */
+const struct input_transition input_state_consume_st_table[] = {
+       INPUT_STATE_ANYWHERE,
+
+       { 0x00, 0x17, NULL,         NULL },
+       { 0x19, 0x19, NULL,         NULL },
+       { 0x1c, 0x1f, NULL,         NULL },
+       { 0x20, 0xff, NULL,         NULL },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* utf8_three state table. */
+const struct input_transition input_state_utf8_three_table[] = {
+       /* No INPUT_STATE_ANYWHERE */
+
+       { 0x00, 0x7f, NULL,             &input_state_ground },
+       { 0x80, 0xbf, input_utf8_add,   &input_state_utf8_two },
+       { 0xc0, 0xff, NULL,             &input_state_ground },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* utf8_two state table. */
+const struct input_transition input_state_utf8_two_table[] = {
+       /* No INPUT_STATE_ANYWHERE */
+
+       { 0x00, 0x7f, NULL,           &input_state_ground },
+       { 0x80, 0xbf, input_utf8_add, &input_state_utf8_one },
+       { 0xc0, 0xff, NULL,           &input_state_ground },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* utf8_one state table. */
+const struct input_transition input_state_utf8_one_table[] = {
+       /* No INPUT_STATE_ANYWHERE */
+
+       { 0x00, 0x7f, NULL,             &input_state_ground },
+       { 0x80, 0xbf, input_utf8_close, &input_state_ground },
+       { 0xc0, 0xff, NULL,             &input_state_ground },
+
+       { -1, -1, NULL, NULL }
+};
+
+/* Input table compare. */
+int
+input_table_compare(const void *key, const void *value)
+{
+       const struct input_ctx          *ictx = key;
+       const struct input_table_entry  *entry = value;
+
+       if (ictx->ch != entry->ch)
+               return (ictx->ch - entry->ch);
+       return (strcmp((const char *)ictx->interm_buf, entry->interm));
+}
+
+/* Initialise input parser. */
+void
+input_init(struct window_pane *wp)
+{
+       struct input_ctx        *ictx = &wp->ictx;
+
+       memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell);
+
+       memcpy(&ictx->old_cell, &grid_default_cell, sizeof ictx->old_cell);
+       ictx->old_cx = 0;
+       ictx->old_cy = 0;
+
+       *ictx->interm_buf = '\0';
+       ictx->interm_len = 0;
+
+       *ictx->param_buf = '\0';
+       ictx->param_len = 0;
+
+       ictx->state = &input_state_ground;
+       ictx->flags = 0;
+}
+
+/* Destroy input parser. */
+void
+input_free(unused struct window_pane *wp)
+{
+}
+
+/* Parse input. */
+void
+input_parse(struct window_pane *wp)
+{
+       struct input_ctx                *ictx = &wp->ictx;
+       const struct input_transition   *itr;
+       struct evbuffer                 *evb = wp->event->input;
+       u_char                          *buf;
+       size_t                           len, off;
+
+       if (EVBUFFER_LENGTH(evb) == 0)
+               return;
+
+       wp->window->flags |= WINDOW_ACTIVITY;
+       wp->window->flags &= ~WINDOW_SILENCE;
+
+       /*
+        * Open the screen. Use NULL wp if there is a mode set as don't want to
+        * update the tty.
+        */
+       if (wp->mode == NULL)
+               screen_write_start(&ictx->ctx, wp, &wp->base);
+       else
+               screen_write_start(&ictx->ctx, NULL, &wp->base);
+       ictx->wp = wp;
+
+       buf = EVBUFFER_DATA(evb);
+       len = EVBUFFER_LENGTH(evb);
+       off = 0;
+
+       /* Parse the input. */
+       while (off < len) {
+               ictx->ch = buf[off++];
+               log_debug("%s: '%c' %s", __func__, ictx->ch, ictx->state->name);
+
+               /* Find the transition. */
+               itr = ictx->state->transitions;
+               while (itr->first != -1 && itr->last != -1) {
+                       if (ictx->ch >= itr->first && ictx->ch <= itr->last)
+                               break;
+                       itr++;
+               }
+               if (itr->first == -1 || itr->last == -1) {
+                       /* No transition? Eh? */
+                       fatalx("No transition from state!");
+               }
+
+               /*
+                * Execute the handler, if any. Don't switch state if it
+                * returns non-zero.
+                */
+               if (itr->handler != NULL && itr->handler(ictx) != 0)
+                       continue;
+
+               /* And switch state, if necessary. */
+               if (itr->state != NULL) {
+                       if (ictx->state->exit != NULL)
+                               ictx->state->exit(ictx);
+                       ictx->state = itr->state;
+                       if (ictx->state->enter != NULL)
+                               ictx->state->enter(ictx);
+               }
+       }
+
+       /* Close the screen. */
+       screen_write_stop(&ictx->ctx);
+
+       evbuffer_drain(evb, len);
+}
+
+/* Split the parameter list (if any). */
+int
+input_split(struct input_ctx *ictx)
+
+{
+       const char      *errstr;
+       char            *ptr, *out;
+       int              n;
+
+       ictx->param_list_len = 0;
+       if (ictx->param_len == 0)
+               return (0);
+
+       ptr = (char *)ictx->param_buf;
+       while ((out = strsep(&ptr, ";")) != NULL) {
+               if (*out == '\0')
+                       n = -1;
+               else {
+                       n = strtonum(out, 0, INT_MAX, &errstr);
+                       if (errstr != NULL)
+                               return (-1);
+               }
+
+               ictx->param_list[ictx->param_list_len++] = n;
+               if (ictx->param_list_len == nitems(ictx->param_list))
+                       return (-1);
+       }
+
+       return (0);
+}
+
+/* Get an argument or return default value. */
+int
+input_get(struct input_ctx *ictx, u_int validx, int minval, int defval)
+{
+       int     retval;
+
+       if (validx >= ictx->param_list_len)
+           return (defval);
+
+       retval = ictx->param_list[validx];
+       if (retval == -1)
+               return (defval);
+       if (retval < minval)
+               return (minval);
+       return (retval);
+}
+
+/* Reply to terminal query. */
+void
+input_reply(struct input_ctx *ictx, const char *fmt, ...)
+{
+       va_list ap;
+       char   *reply;
+
+       va_start(ap, fmt);
+       vasprintf(&reply, fmt, ap);
+       va_end(ap);
+
+       bufferevent_write(ictx->wp->event, reply, strlen(reply));
+       xfree(reply);
+}
+
+/* Clear saved state. */
+void
+input_clear(struct input_ctx *ictx)
+{
+       *ictx->interm_buf = '\0';
+       ictx->interm_len = 0;
+
+       *ictx->param_buf = '\0';
+       ictx->param_len = 0;
+
+       *ictx->input_buf = '\0';
+       ictx->input_len = 0;
+
+       ictx->flags &= ~INPUT_DISCARD;
+}
+
+/* Output this character to the screen. */
+int
+input_print(struct input_ctx *ictx)
+{
+       ictx->cell.data = ictx->ch;
+       screen_write_cell(&ictx->ctx, &ictx->cell, NULL);
+
+       return (0);
+}
+
+/* Collect intermediate string. */
+int
+input_intermediate(struct input_ctx *ictx)
+{
+       if (ictx->interm_len == (sizeof ictx->interm_buf) - 1)
+               ictx->flags |= INPUT_DISCARD;
+       else {
+               ictx->interm_buf[ictx->interm_len++] = ictx->ch;
+               ictx->interm_buf[ictx->interm_len] = '\0';
+       }
+
+       return (0);
+}
+
+/* Collect parameter string. */
+int
+input_parameter(struct input_ctx *ictx)
+{
+       if (ictx->param_len == (sizeof ictx->param_buf) - 1)
+               ictx->flags |= INPUT_DISCARD;
+       else {
+               ictx->param_buf[ictx->param_len++] = ictx->ch;
+               ictx->param_buf[ictx->param_len] = '\0';
+       }
+
+       return (0);
+}
+
+/* Collect input string. */
+int
+input_input(struct input_ctx *ictx)
+{
+       if (ictx->input_len == (sizeof ictx->input_buf) - 1)
+               ictx->flags |= INPUT_DISCARD;
+       else {
+               ictx->input_buf[ictx->input_len++] = ictx->ch;
+               ictx->input_buf[ictx->input_len] = '\0';
+       }
+
+       return (0);
+}
+
+/* Execute C0 control sequence. */
+int
+input_c0_dispatch(struct input_ctx *ictx)
+{
+       struct screen_write_ctx *sctx = &ictx->ctx;
+       struct window_pane      *wp = ictx->wp;
+       struct screen           *s = sctx->s;
+
+       log_debug("%s: '%c", __func__, ictx->ch);
+
+       switch (ictx->ch) {
+       case '\000':    /* NUL */
+               break;
+       case '\007':    /* BEL */
+               wp->window->flags |= WINDOW_BELL;
+               break;
+       case '\010':    /* BS */
+               screen_write_backspace(sctx);
+               break;
+       case '\011':    /* HT */
+               /* Don't tab beyond the end of the line. */
+               if (s->cx >= screen_size_x(s) - 1)
+                       break;
+
+               /* Find the next tab point, or use the last column if none. */
+               do {
+                       s->cx++;
+                       if (bit_test(s->tabs, s->cx))
+                               break;
+               } while (s->cx < screen_size_x(s) - 1);
+               break;
+       case '\012':    /* LF */
+       case '\013':    /* VT */
+       case '\014':    /* FF */
+               screen_write_linefeed(sctx, 0);
+               break;
+       case '\015':    /* CR */
+               screen_write_carriagereturn(sctx);
+               break;
+       case '\016':    /* SO */
+               ictx->cell.attr |= GRID_ATTR_CHARSET;
+               break;
+       case '\017':    /* SI */
+               ictx->cell.attr &= ~GRID_ATTR_CHARSET;
+               break;
+       default:
+               log_debug("%s: unknown '%c'", __func__, ictx->ch);
+               break;
+       }
+
+       return (0);
+}
+
+/* Execute escape sequence. */
+int
+input_esc_dispatch(struct input_ctx *ictx)
+{
+       struct screen_write_ctx         *sctx = &ictx->ctx;
+       struct screen                   *s = sctx->s;
+       struct input_table_entry        *entry;
+
+       if (ictx->flags & INPUT_DISCARD)
+               return (0);
+       log_debug("%s: '%c', %s", __func__, ictx->ch, ictx->interm_buf);
+
+       entry = bsearch(ictx, input_esc_table, nitems(input_esc_table),
+           sizeof input_esc_table[0], input_table_compare);
+       if (entry == NULL) {
+               log_debug("%s: unknown '%c'", __func__, ictx->ch);
+               return (0);
+       }
+
+       switch (entry->type) {
+       case INPUT_ESC_RIS:
+               memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell);
+               memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell);
+               ictx->old_cx = 0;
+               ictx->old_cy = 0;
+
+               screen_reset_tabs(sctx->s);
+
+               screen_write_scrollregion(sctx, 0, screen_size_y(sctx->s) - 1);
+
+               screen_write_insertmode(sctx, 0);
+               screen_write_kcursormode(sctx, 0);
+               screen_write_kkeypadmode(sctx, 0);
+               screen_write_mousemode_off(sctx);
+
+               screen_write_clearscreen(sctx);
+               screen_write_cursormove(sctx, 0, 0);
+               break;
+       case INPUT_ESC_IND:
+               screen_write_linefeed(sctx, 0);
+               break;
+       case INPUT_ESC_NEL:
+               screen_write_carriagereturn(sctx);
+               screen_write_linefeed(sctx, 0);
+               break;
+       case INPUT_ESC_HTS:
+               if (s->cx < screen_size_x(s))
+                       bit_set(s->tabs, s->cx);
+               break;
+       case INPUT_ESC_RI:
+               screen_write_reverseindex(sctx);
+               break;
+       case INPUT_ESC_DECKPAM:
+               screen_write_kkeypadmode(sctx, 1);
+               break;
+       case INPUT_ESC_DECKPNM:
+               screen_write_kkeypadmode(sctx, 0);
+               break;
+       case INPUT_ESC_DECSC:
+               memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell);
+               ictx->old_cx = s->cx;
+               ictx->old_cy = s->cy;
+               break;
+       case INPUT_ESC_DECRC:
+               memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell);
+               screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy);
+               break;
+       case INPUT_ESC_DECALN:
+               screen_write_alignmenttest(sctx);
+               break;
+       case INPUT_ESC_SCSON_G0:
+               /*
+                * 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).
+                */
+               ictx->cell.attr &= ~GRID_ATTR_CHARSET;
+               break;
+       case INPUT_ESC_SCSOFF_G0:
+               ictx->cell.attr |= GRID_ATTR_CHARSET;
+               break;
+       }
+
+       return (0);
+}
+
+/* Execute control sequence. */
+int
+input_csi_dispatch(struct input_ctx *ictx)
+{
+       struct screen_write_ctx        *sctx = &ictx->ctx;
+       struct window_pane             *wp = ictx->wp;
+       struct screen                  *s = sctx->s;
+       struct input_table_entry       *entry;
+       int                             n, m;
+
+       if (ictx->flags & INPUT_DISCARD)
+               return (0);
+       if (input_split(ictx) != 0)
+               return (0);
+       log_debug("%s: '%c' \"%s\" \"%s\"",
+           __func__, ictx->ch, ictx->interm_buf, ictx->param_buf);
+
+       entry = bsearch(ictx, input_csi_table, nitems(input_csi_table),
+           sizeof input_csi_table[0], input_table_compare);
+       if (entry == NULL) {
+               log_debug("%s: unknown '%c'", __func__, ictx->ch);
+               return (0);
+       }
+
+       switch (entry->type) {
+       case INPUT_CSI_CBT:
+               /* Find the previous tab point, n times. */
+               n = input_get(ictx, 0, 1, 1);
+               while (s->cx > 0 && n-- > 0) {
+                       do
+                               s->cx--;
+                       while (s->cx > 0 && !bit_test(s->tabs, s->cx));
+               }
+               break;
+       case INPUT_CSI_CUB:
+               screen_write_cursorleft(sctx, input_get(ictx, 0, 1, 1));
+               break;
+       case INPUT_CSI_CUD:
+               screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1));
+               break;
+       case INPUT_CSI_CUF:
+               screen_write_cursorright(sctx, input_get(ictx, 0, 1, 1));
+               break;
+       case INPUT_CSI_CUP:
+               n = input_get(ictx, 0, 1, 1);
+               m = input_get(ictx, 1, 1, 1);
+               screen_write_cursormove(sctx, m - 1, n - 1);
+               break;
+       case INPUT_CSI_CUU:
+               screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1));
+               break;
+       case INPUT_CSI_DA:
+               switch (input_get(ictx, 0, 0, 0)) {
+               case 0:
+                       input_reply(ictx, "\033[?1;2c");
+                       break;
+               default:
+                       log_debug("%s: unknown '%c'", __func__, ictx->ch);
+                       break;
+               }
+               break;
+       case INPUT_CSI_DCH:
+               screen_write_deletecharacter(sctx, input_get(ictx, 0, 1, 1));
+               break;
+       case INPUT_CSI_DECSTBM:
+               n = input_get(ictx, 0, 1, 1);
+               m = input_get(ictx, 1, 1, screen_size_y(s));
+               screen_write_scrollregion(sctx, n - 1, m - 1);
+               break;
+       case INPUT_CSI_DL:
+               screen_write_deleteline(sctx, input_get(ictx, 0, 1, 1));
+               break;
+       case INPUT_CSI_DSR:
+               switch (input_get(ictx, 0, 0, 0)) {
+               case 5:
+                       input_reply(ictx, "\033[0n");
+                       break;
+               case 6:
+                       input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1);
+                       break;
+               default:
+                       log_debug("%s: unknown '%c'", __func__, ictx->ch);
+                       break;
+               }
+               break;
+       case INPUT_CSI_ED:
+               switch (input_get(ictx, 0, 0, 0)) {
+               case 0:
+                       screen_write_clearendofscreen(sctx);
+                       break;
+               case 1:
+                       screen_write_clearstartofscreen(sctx);
+                       break;
+               case 2:
+                       screen_write_clearscreen(sctx);
+                       break;
+               default:
+                       log_debug("%s: unknown '%c'", __func__, ictx->ch);
+                       break;
+               }
+               break;
+       case INPUT_CSI_EL:
+               switch (input_get(ictx, 0, 0, 0)) {
+               case 0:
+                       screen_write_clearendofline(sctx);
+                       break;
+               case 1:
+                       screen_write_clearstartofline(sctx);
+                       break;
+               case 2:
+                       screen_write_clearline(sctx);
+                       break;
+               default:
+                       log_debug("%s: unknown '%c'", __func__, ictx->ch);
+                       break;
+               }
+               break;
+       case INPUT_CSI_HPA:
+               n = input_get(ictx, 0, 1, 1);
+               screen_write_cursormove(sctx, n - 1, s->cy);
+               break;
+       case INPUT_CSI_ICH:
+               screen_write_insertcharacter(sctx, input_get(ictx, 0, 1, 1));
+               break;
+       case INPUT_CSI_IL:
+               screen_write_insertline(sctx, input_get(ictx, 0, 1, 1));
+               break;
+       case INPUT_CSI_RM:
+               switch (input_get(ictx, 0, 0, -1)) {
+               case 4:         /* IRM */
+                       screen_write_insertmode(&ictx->ctx, 0);
+                       break;
+               default:
+                       log_debug("%s: unknown '%c'", __func__, ictx->ch);
+                       break;
+               }
+               break;
+       case INPUT_CSI_RM_PRIVATE:
+               switch (input_get(ictx, 0, 0, -1)) {
+               case 1:         /* GATM */
+                       screen_write_kcursormode(&ictx->ctx, 0);
+                       break;
+               case 3:         /* DECCOLM */
+                       screen_write_cursormove(&ictx->ctx, 0, 0);
+                       screen_write_clearscreen(&ictx->ctx);
+                       break;
+               case 25:        /* TCEM */
+                       screen_write_cursormode(&ictx->ctx, 0);
+                       break;
+               case 1000:
+               case 1001:
+               case 1002:
+               case 1003:
+                       screen_write_mousemode_off(&ictx->ctx);
+                       break;
+               case 1005:
+                       screen_write_utf8mousemode(&ictx->ctx, 0);
+                       break;
+               case 1049:
+                       window_pane_alternate_off(wp, &ictx->cell);
+                       break;
+               default:
+                       log_debug("%s: unknown '%c'", __func__, ictx->ch);
+                       break;
+               }
+               break;
+       case INPUT_CSI_SGR:
+               input_csi_dispatch_sgr(ictx);
+               break;
+       case INPUT_CSI_SM:
+               switch (input_get(ictx, 0, 0, -1)) {
+               case 4:         /* IRM */
+                       screen_write_insertmode(&ictx->ctx, 1);
+                       break;
+               default:
+                       log_debug("%s: unknown '%c'", __func__, ictx->ch);
+                       break;
+               }
+               break;
+       case INPUT_CSI_SM_PRIVATE:
+               switch (input_get(ictx, 0, 0, -1)) {
+               case 1:         /* GATM */
+                       screen_write_kcursormode(&ictx->ctx, 1);
+                       break;
+               case 3:         /* DECCOLM */
+                       screen_write_cursormove(&ictx->ctx, 0, 0);
+                       screen_write_clearscreen(&ictx->ctx);
+                       break;
+               case 25:        /* TCEM */
+                       screen_write_cursormode(&ictx->ctx, 1);
+                       break;
+               case 1000:
+                       screen_write_mousemode_on(
+                           &ictx->ctx, MODE_MOUSE_STANDARD);
+                       break;
+               case 1002:
+                       screen_write_mousemode_on(
+                           &ictx->ctx, MODE_MOUSE_BUTTON);
+                       break;
+               case 1003:
+                       screen_write_mousemode_on(&ictx->ctx, MODE_MOUSE_ANY);
+                       break;
+               case 1005:
+                       screen_write_utf8mousemode(&ictx->ctx, 1);
+                       break;
+               case 1049:
+                       window_pane_alternate_on(wp, &ictx->cell);
+                       break;
+               default:
+                       log_debug("%s: unknown '%c'", __func__, ictx->ch);
+                       break;
+               }
+               break;
+       case INPUT_CSI_TBC:
+               switch (input_get(ictx, 0, 0, 0)) {
+               case 0:
+                       if (s->cx < screen_size_x(s))
+                               bit_clear(s->tabs, s->cx);
+                       break;
+               case 3:
+                       bit_nclear(s->tabs, 0, screen_size_x(s) - 1);
+                       break;
+               default:
+                       log_debug("%s: unknown '%c'", __func__, ictx->ch);
+                       break;
+               }
+               break;
+       case INPUT_CSI_VPA:
+               n = input_get(ictx, 0, 1, 1);
+               screen_write_cursormove(sctx, s->cx, n - 1);
+               break;
+       case INPUT_CSI_DECSCUSR:
+               n = input_get(ictx, 0, 0, 0);
+               screen_set_cursor_style(s, n);
+               break;
+       }
+
+       return (0);
+}
+
+/* Handle CSI SGR. */
+void
+input_csi_dispatch_sgr(struct input_ctx *ictx)
+{
+       struct grid_cell        *gc = &ictx->cell;
+       u_int                    i;
+       int                      n, m;
+       u_char                   attr;
+
+       if (ictx->param_list_len == 0) {
+               attr = gc->attr;
+               memcpy(gc, &grid_default_cell, sizeof *gc);
+               gc->attr |= (attr & GRID_ATTR_CHARSET);
+               return;
+       }
+
+       for (i = 0; i < ictx->param_list_len; i++) {
+               n = input_get(ictx, i, 0, 0);
+
+               if (n == 38 || n == 48) {
+                       i++;
+                       if (input_get(ictx, i, 0, -1) != 5)
+                               continue;
+
+                       i++;
+                       m = input_get(ictx, i, 0, -1);
+                       if (m == -1) {
+                               if (n == 38) {
+                                       gc->flags &= ~GRID_FLAG_FG256;
+                                       gc->fg = 8;
+                               } else if (n == 48) {
+                                       gc->flags &= ~GRID_FLAG_BG256;
+                                       gc->bg = 8;
+                               }
+
+                       } else {
+                               if (n == 38) {
+                                       gc->flags |= GRID_FLAG_FG256;
+                                       gc->fg = m;
+                               } else if (n == 48) {
+                                       gc->flags |= GRID_FLAG_BG256;
+                                       gc->bg = m;
+                               }
+                       }
+                       continue;
+               }
+
+               switch (n) {
+               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 = n - 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 = n - 40;
+                       break;
+               case 49:
+                       gc->flags &= ~GRID_FLAG_BG256;
+                       gc->bg = 8;
+                       break;
+               case 90:
+               case 91:
+               case 92:
+               case 93:
+               case 94:
+               case 95:
+               case 96:
+               case 97:
+                       gc->flags &= ~GRID_FLAG_FG256;
+                       gc->fg = n;
+                       break;
+               case 100:
+               case 101:
+               case 102:
+               case 103:
+               case 104:
+               case 105:
+               case 106:
+               case 107:
+                       gc->flags &= ~GRID_FLAG_BG256;
+                       gc->bg = n;
+                       break;
+               }
+       }
+}
+
+/* DCS terminator (ST) received. */
+int
+input_dcs_dispatch(struct input_ctx *ictx)
+{
+       const char      prefix[] = "tmux;";
+       const u_int     prefix_len = (sizeof prefix) - 1;
+
+       if (ictx->flags & INPUT_DISCARD)
+               return (0);
+
+       log_debug("%s: \"%s\"", __func__, ictx->input_buf);
+
+       /* Check for tmux prefix. */
+       if (ictx->input_len >= prefix_len &&
+           strncmp((const char *)ictx->input_buf, prefix, prefix_len) == 0) {
+               screen_write_rawstring(&ictx->ctx,
+                   ictx->input_buf + prefix_len, ictx->input_len - prefix_len);
+       }
+
+       return (0);
+}
+
+/* OSC string started. */
+void
+input_enter_osc(struct input_ctx *ictx)
+{
+       log_debug("%s", __func__);
+
+       input_clear(ictx);
+}
+
+/* OSC terminator (ST) received. */
+void
+input_exit_osc(struct input_ctx *ictx)
+{
+       u_char *p = ictx->input_buf;
+       int     option;
+
+       if (ictx->flags & INPUT_DISCARD)
+               return;
+       if (ictx->input_len < 1 || *p < '0' || *p > '9')
+               return;
+
+       log_debug("%s: \"%s\"", __func__, p);
+
+       option = 0;
+       while (*p >= '0' && *p <= '9')
+               option = option * 10 + *p++ - '0';
+       if (*p == ';')
+               p++;
+
+       switch (option) {
+       case 0:
+       case 2:
+               screen_set_title(ictx->ctx.s, (const char *)p);
+               server_status_window(ictx->wp->window);
+               break;
+       case 12:
+               screen_set_cursor_colour(ictx->ctx.s, (const char *)p);
+               break;
+       case 112:
+               if (*p == '\0') /* No arguments allowed. */
+                       screen_set_cursor_colour(ictx->ctx.s, "");
+               break;
+       default:
+               log_debug("%s: unknown '%u'", __func__, option);
+               break;
+       }
+}
+
+/* APC string started. */
+void
+input_enter_apc(struct input_ctx *ictx)
+{
+       log_debug("%s", __func__);
+
+       input_clear(ictx);
+}
+
+/* APC terminator (ST) received. */
+void
+input_exit_apc(struct input_ctx *ictx)
+{
+       if (ictx->flags & INPUT_DISCARD)
+               return;
+       log_debug("%s: \"%s\"", __func__, ictx->input_buf);
+
+       screen_set_title(ictx->ctx.s, (const char *)ictx->input_buf);
+       server_status_window(ictx->wp->window);
+}
+
+/* Rename string started. */
+void
+input_enter_rename(struct input_ctx *ictx)
+{
+       log_debug("%s", __func__);
+
+       input_clear(ictx);
+}
+
+/* Rename terminator (ST) received. */
+void
+input_exit_rename(struct input_ctx *ictx)
+{
+       if (ictx->flags & INPUT_DISCARD)
+               return;
+       log_debug("%s: \"%s\"", __func__, ictx->input_buf);
+
+       xfree(ictx->wp->window->name);
+       ictx->wp->window->name = xstrdup((const char *)ictx->input_buf);
+       options_set_number(&ictx->wp->window->options, "automatic-rename", 0);
+
+       server_status_window(ictx->wp->window);
+}
+
+/* Open UTF-8 character. */
+int
+input_utf8_open(struct input_ctx *ictx)
+{
+       if (!options_get_number(&ictx->wp->window->options, "utf8")) {
+               /* Print, and do not switch state. */
+               input_print(ictx);
+               return (-1);
+       }
+       log_debug("%s", __func__);
+
+       utf8_open(&ictx->utf8data, ictx->ch);
+       return (0);
+}
+
+/* Append to UTF-8 character. */
+int
+input_utf8_add(struct input_ctx *ictx)
+{
+       log_debug("%s", __func__);
+
+       utf8_append(&ictx->utf8data, ictx->ch);
+       return (0);
+}
+
+/* Close UTF-8 string. */
+int
+input_utf8_close(struct input_ctx *ictx)
+{
+       log_debug("%s", __func__);
+
+       utf8_append(&ictx->utf8data, ictx->ch);
+
+       ictx->cell.flags |= GRID_FLAG_UTF8;
+       screen_write_cell(&ictx->ctx, &ictx->cell, &ictx->utf8data);
+       ictx->cell.flags &= ~GRID_FLAG_UTF8;
+
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/job.c b/external/bsd/tmux/dist/job.c
new file mode 100644 (file)
index 0000000..897d84d
--- /dev/null
@@ -0,0 +1,168 @@
+/* $Id: job.c,v 1.3 2011/08/17 18:48:36 jmmv 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 <sys/socket.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Job scheduling. Run queued commands in the background and record their
+ * output.
+ */
+
+void   job_callback(struct bufferevent *, short, void *);
+
+/* All jobs list. */
+struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
+
+/* Start a job running, if it isn't already. */
+struct job *
+job_run(const char *cmd,
+    void (*callbackfn)(struct job *), void (*freefn)(void *), void *data)
+{
+       struct job      *job;
+       struct environ   env;
+       pid_t            pid;
+       int              nullfd, out[2];
+
+       if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0)
+               return (NULL);
+
+       environ_init(&env);
+       environ_copy(&global_environ, &env);
+       server_fill_environ(NULL, &env);
+
+       switch (pid = fork()) {
+       case -1:
+               environ_free(&env);
+               return (NULL);
+       case 0:         /* child */
+               clear_signals(1);
+
+               environ_push(&env);
+               environ_free(&env);
+
+               if (dup2(out[1], STDOUT_FILENO) == -1)
+                       fatal("dup2 failed");
+               if (out[1] != STDOUT_FILENO)
+                       close(out[1]);
+               close(out[0]);
+
+               nullfd = open(_PATH_DEVNULL, O_RDWR, 0);
+               if (nullfd < 0)
+                       fatal("open failed");
+               if (dup2(nullfd, STDIN_FILENO) == -1)
+                       fatal("dup2 failed");
+               if (dup2(nullfd, STDERR_FILENO) == -1)
+                       fatal("dup2 failed");
+               if (nullfd != STDIN_FILENO && nullfd != STDERR_FILENO)
+                       close(nullfd);
+
+               closefrom(STDERR_FILENO + 1);
+
+               execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
+               fatal("execl failed");
+       }
+
+       /* parent */
+       environ_free(&env);
+       close(out[1]);
+
+       job = xmalloc(sizeof *job);
+       job->cmd = xstrdup(cmd);
+       job->pid = pid;
+       job->status = 0;
+
+       LIST_INSERT_HEAD(&all_jobs, job, lentry);
+
+       job->callbackfn = callbackfn;
+       job->freefn = freefn;
+       job->data = data;
+
+       job->fd = out[0];
+       setblocking(job->fd, 0);
+
+       job->event = bufferevent_new(job->fd, NULL, NULL, job_callback, job);
+       bufferevent_enable(job->event, EV_READ);
+
+       log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid);
+       return (job);
+}
+
+/* Kill and free an individual job. */
+void
+job_free(struct job *job)
+{
+       log_debug("free job %p: %s", job, job->cmd);
+
+       LIST_REMOVE(job, lentry);
+       xfree(job->cmd);
+
+       if (job->freefn != NULL && job->data != NULL)
+               job->freefn(job->data);
+
+       if (job->pid != -1)
+               kill(job->pid, SIGTERM);
+       if (job->fd != -1)
+               close(job->fd);
+       if (job->event != NULL)
+               bufferevent_free(job->event);
+
+       xfree(job);
+}
+
+/* Job buffer error callback. */
+/* ARGSUSED */
+void
+job_callback(unused struct bufferevent *bufev, unused short events, void *data)
+{
+       struct job      *job = data;
+
+       log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid);
+
+       if (job->pid == -1) {
+               if (job->callbackfn != NULL)
+                       job->callbackfn(job);
+               job_free(job);
+       } else {
+               bufferevent_disable(job->event, EV_READ);
+               close(job->fd);
+               job->fd = -1;
+       }
+}
+
+/* Job died (waitpid() returned its pid). */
+void
+job_died(struct job *job, int status)
+{
+       log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid);
+
+       job->status = status;
+
+       if (job->fd == -1) {
+               if (job->callbackfn != NULL)
+                       job->callbackfn(job);
+               job_free(job);
+       } else
+               job->pid = -1;
+}
diff --git a/external/bsd/tmux/dist/key-bindings.c b/external/bsd/tmux/dist/key-bindings.c
new file mode 100644 (file)
index 0000000..ba1fbbe
--- /dev/null
@@ -0,0 +1,279 @@
+/* $Id: key-bindings.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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;
+struct key_bindings    dead_key_bindings;
+
+int
+key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2)
+{
+       int     key1, key2;
+
+       key1 = bd1->key & ~KEYC_PREFIX;
+       key2 = bd2->key & ~KEYC_PREFIX;
+       if (key1 != key2)
+               return (key1 - key2);
+
+       if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX))
+               return (-1);
+       if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX))
+               return (1);
+       return (0);
+}
+
+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;
+
+       key_bindings_remove(key);
+
+       bd = xmalloc(sizeof *bd);
+       bd->key = key;
+       SPLAY_INSERT(key_bindings, &key_bindings, bd);
+
+       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);
+       SPLAY_INSERT(key_bindings, &dead_key_bindings, bd);
+}
+
+void
+key_bindings_clean(void)
+{
+       struct key_binding      *bd;
+
+       while (!SPLAY_EMPTY(&dead_key_bindings)) {
+               bd = SPLAY_ROOT(&dead_key_bindings);
+               SPLAY_REMOVE(key_bindings, &dead_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_command_prompt_entry },
+               { '%',                    0, &cmd_split_window_entry },
+               { '&',                    0, &cmd_confirm_before_entry },
+               { '(',                    0, &cmd_switch_client_entry },
+               { ')',                    0, &cmd_switch_client_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_last_pane_entry },
+               { '=',                    0, &cmd_choose_buffer_entry },
+               { '?',                    0, &cmd_list_keys_entry },
+               { 'D',                    0, &cmd_choose_client_entry },
+               { 'L',                    0, &cmd_switch_client_entry },
+               { '[',                    0, &cmd_copy_mode_entry },
+               { '\'',                   0, &cmd_command_prompt_entry },
+               { '\002', /* C-b */       0, &cmd_send_prefix_entry },
+               { '\017', /* C-o */       0, &cmd_rotate_window_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 },
+               { 'i',                    0, &cmd_display_message_entry },
+               { 'l',                    0, &cmd_last_window_entry },
+               { 'n',                    0, &cmd_next_window_entry },
+               { 'o',                    0, &cmd_select_pane_entry },
+               { 'p',                    0, &cmd_previous_window_entry },
+               { 'q',                    0, &cmd_display_panes_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 },
+               { '~',                    0, &cmd_show_messages_entry },
+               { '1' | KEYC_ESCAPE,      0, &cmd_select_layout_entry },
+               { '2' | KEYC_ESCAPE,      0, &cmd_select_layout_entry },
+               { '3' | KEYC_ESCAPE,      0, &cmd_select_layout_entry },
+               { '4' | KEYC_ESCAPE,      0, &cmd_select_layout_entry },
+               { '5' | KEYC_ESCAPE,      0, &cmd_select_layout_entry },
+               { KEYC_PPAGE,             0, &cmd_copy_mode_entry },
+               { 'n' | KEYC_ESCAPE,      0, &cmd_next_window_entry },
+               { 'o' | KEYC_ESCAPE,      0, &cmd_rotate_window_entry },
+               { 'p' | KEYC_ESCAPE,      0, &cmd_previous_window_entry },
+               { KEYC_UP,                1, &cmd_select_pane_entry },
+               { KEYC_DOWN,              1, &cmd_select_pane_entry },
+               { KEYC_LEFT,              1, &cmd_select_pane_entry },
+               { KEYC_RIGHT,             1, &cmd_select_pane_entry },
+               { KEYC_UP | KEYC_ESCAPE,  1, &cmd_resize_pane_entry },
+               { KEYC_DOWN | KEYC_ESCAPE,  1, &cmd_resize_pane_entry },
+               { KEYC_LEFT | KEYC_ESCAPE,  1, &cmd_resize_pane_entry },
+               { KEYC_RIGHT | KEYC_ESCAPE, 1, &cmd_resize_pane_entry },
+               { KEYC_UP | KEYC_CTRL,    1, &cmd_resize_pane_entry },
+               { KEYC_DOWN | KEYC_CTRL,  1, &cmd_resize_pane_entry },
+               { KEYC_LEFT | KEYC_CTRL,  1, &cmd_resize_pane_entry },
+               { KEYC_RIGHT | KEYC_CTRL, 1, &cmd_resize_pane_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->list);
+               cmdlist->references = 1;
+
+               cmd = xmalloc(sizeof *cmd);
+               cmd->entry = table[i].entry;
+               if (cmd->entry->key_binding != NULL)
+                       cmd->entry->key_binding(cmd, table[i].key);
+               else
+                       cmd->args = args_create(0);
+               TAILQ_INSERT_HEAD(&cmdlist->list, cmd, qentry);
+
+               key_bindings_add(
+                   table[i].key | KEYC_PREFIX, table[i].can_repeat, cmdlist);
+       }
+}
+
+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, "%s", msg);
+       xfree(msg);
+}
+
+void printflike2
+key_bindings_print(struct cmd_ctx *ctx, const char *fmt, ...)
+{
+       struct winlink  *wl = ctx->curclient->session->curw;
+       va_list          ap;
+
+       if (wl->window->active->mode != &window_copy_mode) {
+               window_pane_reset_mode(wl->window->active);
+               window_pane_set_mode(wl->window->active, &window_copy_mode);
+               window_copy_init_for_output(wl->window->active);
+       }
+
+       va_start(ap, fmt);
+       window_copy_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 (options_get_number(&global_options, "quiet"))
+               return;
+
+       va_start(ap, fmt);
+       xvasprintf(&msg, fmt, ap);
+       va_end(ap);
+
+       *msg = toupper((u_char) *msg);
+       status_message_set(ctx->curclient, "%s", msg);
+       xfree(msg);
+}
+
+void
+key_bindings_dispatch(struct key_binding *bd, struct client *c)
+{
+       struct cmd_ctx   ctx;
+       struct cmd      *cmd;
+       int              readonly;
+
+       ctx.msgdata = NULL;
+       ctx.curclient = c;
+
+       ctx.error = key_bindings_error;
+       ctx.print = key_bindings_print;
+       ctx.info = key_bindings_info;
+
+       ctx.cmdclient = NULL;
+
+       readonly = 1;
+       TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) {
+               if (!(cmd->entry->flags & CMD_READONLY))
+                       readonly = 0;
+       }
+       if (!readonly && c->flags & CLIENT_READONLY) {
+               key_bindings_info(&ctx, "Client is read-only");
+               return;
+       }
+
+       cmd_list_exec(bd->cmdlist, &ctx);
+}
diff --git a/external/bsd/tmux/dist/key-string.c b/external/bsd/tmux/dist/key-string.c
new file mode 100644 (file)
index 0000000..8b40ce0
--- /dev/null
@@ -0,0 +1,233 @@
+/* $Id: key-string.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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 *);
+int    key_string_get_modifiers(const char **);
+
+const 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 },
+       { "Space",      ' ' },
+       { "BSpace",     KEYC_BSPACE },
+       { "Enter",      '\r' },
+       { "Escape",     '\033' },
+
+       /* Arrow keys. */
+       { "Up",         KEYC_UP },
+       { "Down",       KEYC_DOWN },
+       { "Left",       KEYC_LEFT },
+       { "Right",      KEYC_RIGHT },
+
+       /* Numeric keypad. */
+       { "KP/",        KEYC_KP_SLASH },
+       { "KP*",        KEYC_KP_STAR },
+       { "KP-",        KEYC_KP_MINUS },
+       { "KP7",        KEYC_KP_SEVEN },
+       { "KP8",        KEYC_KP_EIGHT },
+       { "KP9",        KEYC_KP_NINE },
+       { "KP+",        KEYC_KP_PLUS },
+       { "KP4",        KEYC_KP_FOUR },
+       { "KP5",        KEYC_KP_FIVE },
+       { "KP6",        KEYC_KP_SIX },
+       { "KP1",        KEYC_KP_ONE },
+       { "KP2",        KEYC_KP_TWO },
+       { "KP3",        KEYC_KP_THREE },
+       { "KPEnter",    KEYC_KP_ENTER },
+       { "KP0",        KEYC_KP_ZERO },
+       { "KP.",        KEYC_KP_PERIOD },
+};
+
+/* Find key string in table. */
+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);
+}
+
+/* Find modifiers. */
+int
+key_string_get_modifiers(const char **string)
+{
+       int     modifiers;
+
+       modifiers = 0;
+       while (((*string)[0] != '\0') && (*string)[1] == '-') {
+               switch ((*string)[0]) {
+               case 'C':
+               case 'c':
+                       modifiers |= KEYC_CTRL;
+                       break;
+               case 'M':
+               case 'm':
+                       modifiers |= KEYC_ESCAPE;
+                       break;
+               case 'S':
+               case 's':
+                       modifiers |= KEYC_SHIFT;
+                       break;
+               }
+               *string += 2;
+       }
+       return (modifiers);
+}
+
+/* Lookup a string and convert to a key value. */
+int
+key_string_lookup_string(const char *string)
+{
+       int     key, modifiers;
+
+       /* Check for modifiers. */
+       modifiers = 0;
+       if (string[0] == '^' && string[1] != '\0') {
+               modifiers |= KEYC_CTRL;
+               string++;
+       }
+       modifiers |= key_string_get_modifiers(&string);
+       if (string[0] == '\0')
+               return (KEYC_NONE);
+
+       /* Is this a standard ASCII key? */
+       if (string[1] == '\0') {
+               key = (u_char) string[0];
+               if (key < 32 || key == 127 || key > 255)
+                       return (KEYC_NONE);
+       } else {
+               /* Otherwise look the key up in the table. */
+               key = key_string_search_table(string);
+               if (key == KEYC_NONE)
+                       return (KEYC_NONE);
+       }
+
+       /* Convert the standard control keys. */
+       if (key < KEYC_BASE && (modifiers & KEYC_CTRL)) {
+               if (key >= 97 && key <= 122)
+                       key -= 96;
+               else if (key >= 64 && key <= 95)
+                       key -= 64;
+               else if (key == 32)
+                       key = 0;
+               else if (key == 63)
+                       key = KEYC_BSPACE;
+               else
+                       return (KEYC_NONE);
+               modifiers &= ~KEYC_CTRL;
+       }
+
+       return (key | modifiers);
+}
+
+/* Convert a key code into string format, with prefix if necessary. */
+const char *
+key_string_lookup_key(int key)
+{
+       static char     out[24];
+       char            tmp[8];
+       u_int           i;
+
+       *out = '\0';
+
+       /*
+        * Special case: display C-@ as C-Space. Could do this below in
+        * the (key >= 0 && key <= 32), but this way we let it be found
+        * in key_string_table, for the unlikely chance that we might
+        * change its name.
+        */
+       if ((key & KEYC_MASK_KEY) == 0)
+           key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD);
+
+       /* Fill in the modifiers. */
+       if (key & KEYC_CTRL)
+               strlcat(out, "C-", sizeof out);
+       if (key & KEYC_ESCAPE)
+               strlcat(out, "M-", sizeof out);
+       if (key & KEYC_SHIFT)
+               strlcat(out, "S-", sizeof out);
+       key &= KEYC_MASK_KEY;
+
+       /* Try the key against the string table. */
+       for (i = 0; i < nitems(key_string_table); i++) {
+               if (key == key_string_table[i].key)
+                       break;
+       }
+       if (i != nitems(key_string_table)) {
+               strlcat(out, key_string_table[i].string, sizeof out);
+               return (out);
+       }
+
+       /* Invalid keys are errors. */
+       if (key == 127 || key > 255)
+               return (NULL);
+
+       /* Check for standard or control key. */
+       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);
+       } else if (key >= 32 && key <= 126) {
+               tmp[0] = key;
+               tmp[1] = '\0';
+       } else if (key >= 128)
+               xsnprintf(tmp, sizeof tmp, "\\%o", key);
+
+       strlcat(out, tmp, sizeof out);
+       return (out);
+}
diff --git a/external/bsd/tmux/dist/layout-custom.c b/external/bsd/tmux/dist/layout-custom.c
new file mode 100644 (file)
index 0000000..5fb320e
--- /dev/null
@@ -0,0 +1,275 @@
+/* $Id: layout-custom.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */
+
+/*
+ * Copyright (c) 2010 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"
+
+struct layout_cell     *layout_find_bottomright(struct layout_cell *);
+u_short                        layout_checksum(const char *);
+int                    layout_append(struct layout_cell *, char *, size_t);
+struct layout_cell     *layout_construct(struct layout_cell *, const char **);
+void                   layout_assign(struct window_pane **, struct layout_cell *);
+
+/* Find the bottom-right cell. */
+struct layout_cell *
+layout_find_bottomright(struct layout_cell *lc)
+{
+       if (lc->type == LAYOUT_WINDOWPANE)
+               return (lc);
+       lc = TAILQ_LAST(&lc->cells, layout_cells);
+       return (layout_find_bottomright(lc));
+}
+
+/* Calculate layout checksum. */
+u_short
+layout_checksum(const char *layout)
+{
+       u_short csum;
+
+       csum = 0;
+       for (; *layout != '\0'; layout++) {
+               csum = (csum >> 1) + ((csum & 1) << 15);
+               csum += *layout;
+       }
+       return (csum);
+}
+
+/* Dump layout as a string. */
+char *
+layout_dump(struct window *w)
+{
+       char    layout[BUFSIZ], *out;
+
+       *layout = '\0';
+       if (layout_append(w->layout_root, layout, sizeof layout) != 0)
+               return (NULL);
+
+       xasprintf(&out, "%4x,%s", layout_checksum(layout), layout);
+       return (out);
+}
+
+/* Append information for a single cell. */
+int
+layout_append(struct layout_cell *lc, char *buf, size_t len)
+{
+       struct layout_cell     *lcchild;
+       char                    tmp[64];
+       size_t                  tmplen;
+       const char             *brackets = "][";
+
+       if (len == 0)
+               return (-1);
+
+       tmplen = xsnprintf(tmp, sizeof tmp,
+           "%ux%u,%u,%u", lc->sx, lc->sy, lc->xoff, lc->yoff);
+       if (tmplen > (sizeof tmp) - 1)
+               return (-1);
+       if (strlcat(buf, tmp, len) >= len)
+               return (-1);
+
+       switch (lc->type) {
+       case LAYOUT_LEFTRIGHT:
+               brackets = "}{";
+               /* FALLTHROUGH */
+       case LAYOUT_TOPBOTTOM:
+               if (strlcat(buf, &brackets[1], len) >= len)
+                       return (-1);
+               TAILQ_FOREACH(lcchild, &lc->cells, entry) {
+                       if (layout_append(lcchild, buf, len) != 0)
+                               return (-1);
+                       if (strlcat(buf, ",", len) >= len)
+                               return (-1);
+               }
+               buf[strlen(buf) - 1] = brackets[0];
+               break;
+       case LAYOUT_WINDOWPANE:
+               break;
+       }
+
+       return (0);
+}
+
+/* Parse a layout string and arrange window as layout. */
+int
+layout_parse(struct window *w, const char *layout)
+{
+       struct layout_cell      *lc, *lcchild;
+       struct window_pane      *wp;
+       u_int                    npanes, ncells, sx, sy;
+       u_short                  csum;
+
+       /* Check validity. */
+       if (sscanf(layout, "%hx,", &csum) != 1)
+               return (-1);
+       layout += 5;
+       if (csum != layout_checksum(layout))
+               return (-1);
+
+       /* Build the layout. */
+       lc = layout_construct(NULL, &layout);
+       if (lc == NULL)
+               return (-1);
+       if (*layout != '\0')
+               goto fail;
+
+       /* Check this window will fit into the layout. */
+       for (;;) {
+               npanes = window_count_panes(w);
+               ncells = layout_count_cells(lc);
+               if (npanes > ncells)
+                       goto fail;
+               if (npanes == ncells)
+                       break;
+
+               /* Fewer panes than cells - close the bottom right. */
+               lcchild = layout_find_bottomright(lc);
+               layout_destroy_cell(lcchild, &lc);
+       }
+
+       /* Save the old window size and resize to the layout size. */
+       sx = w->sx; sy = w->sy;
+       window_resize(w, lc->sx, lc->sy);
+
+       /* Destroy the old layout and swap to the new. */
+       layout_free_cell(w->layout_root);
+       w->layout_root = lc;
+
+       /* Assign the panes into the cells. */
+       wp = TAILQ_FIRST(&w->panes);
+       layout_assign(&wp, lc);
+
+       /* Update pane offsets and sizes. */
+       layout_fix_offsets(lc);
+       layout_fix_panes(w, lc->sx, lc->sy);
+
+       /* Then resize the layout back to the original window size. */
+       layout_resize(w, sx, sy);
+       window_resize(w, sx, sy);
+
+       layout_print_cell(lc, __func__, 0);
+
+       return (0);
+
+fail:
+       layout_free_cell(lc);
+       return (-1);
+}
+
+/* Assign panes into cells. */
+void
+layout_assign(struct window_pane **wp, struct layout_cell *lc)
+{
+       struct layout_cell      *lcchild;
+
+       switch (lc->type) {
+       case LAYOUT_WINDOWPANE:
+               layout_make_leaf(lc, *wp);
+               *wp = TAILQ_NEXT(*wp, entry);
+               return;
+       case LAYOUT_LEFTRIGHT:
+       case LAYOUT_TOPBOTTOM:
+               TAILQ_FOREACH(lcchild, &lc->cells, entry)
+                       layout_assign(wp, lcchild);
+               return;
+       }
+}
+
+/* Construct a cell from all or part of a layout tree. */
+struct layout_cell *
+layout_construct(struct layout_cell *lcparent, const char **layout)
+{
+       struct layout_cell     *lc, *lcchild;
+       u_int                   sx, sy, xoff, yoff;
+
+       if (!isdigit((u_char) **layout))
+               return (NULL);
+       if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4)
+               return (NULL);
+
+       while (isdigit((u_char) **layout))
+               (*layout)++;
+       if (**layout != 'x')
+               return (NULL);
+       (*layout)++;
+       while (isdigit((u_char) **layout))
+               (*layout)++;
+       if (**layout != ',')
+               return (NULL);
+       (*layout)++;
+       while (isdigit((u_char) **layout))
+               (*layout)++;
+       if (**layout != ',')
+               return (NULL);
+       (*layout)++;
+       while (isdigit((u_char) **layout))
+               (*layout)++;
+
+       lc = layout_create_cell(lcparent);
+       lc->sx = sx;
+       lc->sy = sy;
+       lc->xoff = xoff;
+       lc->yoff = yoff;
+
+       switch (**layout) {
+       case ',':
+       case '}':
+       case ']':
+       case '\0':
+               return (lc);
+       case '{':
+               lc->type = LAYOUT_LEFTRIGHT;
+               break;
+       case '[':
+               lc->type = LAYOUT_TOPBOTTOM;
+               break;
+       default:
+               goto fail;
+       }
+
+       do {
+               (*layout)++;
+               lcchild = layout_construct(lc, layout);
+               if (lcchild == NULL)
+                       goto fail;
+               TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry);
+       } while (**layout == ',');
+
+       switch (lc->type) {
+       case LAYOUT_LEFTRIGHT:
+               if (**layout != '}')
+                       goto fail;
+               break;
+       case LAYOUT_TOPBOTTOM:
+               if (**layout != ']')
+                       goto fail;
+               break;
+       default:
+               goto fail;
+       }
+       (*layout)++;
+
+       return (lc);
+
+fail:
+       layout_free_cell(lc);
+       return (NULL);
+}
diff --git a/external/bsd/tmux/dist/layout-set.c b/external/bsd/tmux/dist/layout-set.c
new file mode 100644 (file)
index 0000000..e9562ca
--- /dev/null
@@ -0,0 +1,569 @@
+/* $Id: layout-set.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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"
+
+/*
+ * Set window layouts - predefined methods to arrange windows. These are one-off
+ * and generate a layout tree.
+ */
+
+void   layout_set_even_h(struct window *);
+void   layout_set_even_v(struct window *);
+void   layout_set_main_h(struct window *);
+void   layout_set_main_v(struct window *);
+void   layout_set_tiled(struct window *);
+
+const struct {
+       const char      *name;
+       void            (*arrange)(struct window *);
+} layout_sets[] = {
+       { "even-horizontal", layout_set_even_h },
+       { "even-vertical", layout_set_even_v },
+       { "main-horizontal", layout_set_main_h },
+       { "main-vertical", layout_set_main_v },
+       { "tiled", layout_set_tiled },
+};
+
+const char *
+layout_set_name(u_int layout)
+{
+       return (layout_sets[layout].name);
+}
+
+int
+layout_set_lookup(const char *name)
+{
+       u_int   i;
+       int     matched = -1;
+
+       for (i = 0; i < nitems(layout_sets); i++) {
+               if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) {
+                       if (matched != -1)      /* ambiguous */
+                               return (-1);
+                       matched = i;
+               }
+       }
+
+       return (matched);
+}
+
+u_int
+layout_set_select(struct window *w, u_int layout)
+{
+       if (layout > nitems(layout_sets) - 1)
+               layout = nitems(layout_sets) - 1;
+
+       if (layout_sets[layout].arrange != NULL)
+               layout_sets[layout].arrange(w);
+
+       w->lastlayout = layout;
+       return (layout);
+}
+
+u_int
+layout_set_next(struct window *w)
+{
+       u_int   layout;
+
+       if (w->lastlayout == -1)
+               layout = 0;
+       else {
+               layout = w->lastlayout + 1;
+               if (layout > nitems(layout_sets) - 1)
+                       layout = 0;
+       }
+
+       if (layout_sets[layout].arrange != NULL)
+               layout_sets[layout].arrange(w);
+       w->lastlayout = layout;
+       return (layout);
+}
+
+u_int
+layout_set_previous(struct window *w)
+{
+       u_int   layout;
+
+       if (w->lastlayout == -1)
+               layout = nitems(layout_sets) - 1;
+       else {
+               layout = w->lastlayout;
+               if (layout == 0)
+                       layout = nitems(layout_sets) - 1;
+               else
+                       layout--;
+       }
+
+       if (layout_sets[layout].arrange != NULL)
+               layout_sets[layout].arrange(w);
+       w->lastlayout = layout;
+       return (layout);
+}
+
+void
+layout_set_even_h(struct window *w)
+{
+       struct window_pane      *wp;
+       struct layout_cell      *lc, *lcnew;
+       u_int                    i, n, width, xoff;
+
+       layout_print_cell(w->layout_root, __func__, 1);
+
+       /* Get number of panes. */
+       n = window_count_panes(w);
+       if (n <= 1)
+               return;
+
+       /* How many can we fit? */
+       width = (w->sx - (n - 1)) / n;
+       if (width < PANE_MINIMUM)
+               width = PANE_MINIMUM;
+
+       /* Free the old root and construct a new. */
+       layout_free(w);
+       lc = w->layout_root = layout_create_cell(NULL);
+       layout_set_size(lc, w->sx, w->sy, 0, 0);
+       layout_make_node(lc, LAYOUT_LEFTRIGHT);
+
+       /* Build new leaf cells. */
+       i = xoff = 0;
+       TAILQ_FOREACH(wp, &w->panes, entry) {
+               /* Create child cell. */
+               lcnew = layout_create_cell(lc);
+               layout_set_size(lcnew, width, w->sy, xoff, 0);
+               layout_make_leaf(lcnew, wp);
+               TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
+
+               i++;
+               xoff += width + 1;
+       }
+
+       /* Allocate any remaining space. */
+       if (w->sx > xoff - 1) {
+               lc = TAILQ_LAST(&lc->cells, layout_cells);
+               layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, w->sx - (xoff - 1));
+       }
+
+       /* Fix cell offsets. */
+       layout_fix_offsets(lc);
+       layout_fix_panes(w, w->sx, w->sy);
+
+       layout_print_cell(w->layout_root, __func__, 1);
+
+       server_redraw_window(w);
+}
+
+void
+layout_set_even_v(struct window *w)
+{
+       struct window_pane      *wp;
+       struct layout_cell      *lc, *lcnew;
+       u_int                    i, n, height, yoff;
+
+       layout_print_cell(w->layout_root, __func__, 1);
+
+       /* Get number of panes. */
+       n = window_count_panes(w);
+       if (n <= 1)
+               return;
+
+       /* How many can we fit? */
+       height = (w->sy - (n - 1)) / n;
+       if (height < PANE_MINIMUM)
+               height = PANE_MINIMUM;
+
+       /* Free the old root and construct a new. */
+       layout_free(w);
+       lc = w->layout_root = layout_create_cell(NULL);
+       layout_set_size(lc, w->sx, w->sy, 0, 0);
+       layout_make_node(lc, LAYOUT_TOPBOTTOM);
+
+       /* Build new leaf cells. */
+       i = yoff = 0;
+       TAILQ_FOREACH(wp, &w->panes, entry) {
+               /* Create child cell. */
+               lcnew = layout_create_cell(lc);
+               layout_set_size(lcnew, w->sx, height, 0, yoff);
+               layout_make_leaf(lcnew, wp);
+               TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
+
+               i++;
+               yoff += height + 1;
+       }
+
+       /* Allocate any remaining space. */
+       if (w->sy > yoff - 1) {
+               lc = TAILQ_LAST(&lc->cells, layout_cells);
+               layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, w->sy - (yoff - 1));
+       }
+
+       /* Fix cell offsets. */
+       layout_fix_offsets(lc);
+       layout_fix_panes(w, w->sx, w->sy);
+
+       layout_print_cell(w->layout_root, __func__, 1);
+
+       server_redraw_window(w);
+}
+
+void
+layout_set_main_h(struct window *w)
+{
+       struct window_pane      *wp;
+       struct layout_cell      *lc, *lcmain, *lcrow, *lcchild;
+       u_int                    n, mainheight, otherheight, width, height;
+       u_int                    used, i, j, columns, rows, totalrows;
+
+       layout_print_cell(w->layout_root, __func__, 1);
+
+       /* Get number of panes. */
+       n = window_count_panes(w);
+       if (n <= 1)
+               return;
+       n--;    /* take off main pane */
+
+       /* How many rows and columns will be needed, not counting main? */
+       columns = (w->sx + 1) / (PANE_MINIMUM + 1);     /* maximum columns */
+       if (columns == 0)
+               columns = 1;
+       rows = 1 + (n - 1) / columns;
+       columns = 1 + (n - 1) / rows;
+       width = (w->sx - (n - 1)) / columns;
+
+       /* Get the main pane height and add one for separator line. */
+       mainheight = options_get_number(&w->options, "main-pane-height") + 1;
+
+       /* Get the optional other pane height and add one for separator line. */
+       otherheight = options_get_number(&w->options, "other-pane-height") + 1;
+
+       /*
+        * If an other pane height was specified, honour it so long as it
+        * doesn't shrink the main height to less than the main-pane-height
+        */
+       if (otherheight > 1 && w->sx - otherheight > mainheight)
+               mainheight = w->sx - otherheight;
+       if (mainheight < PANE_MINIMUM + 1)
+               mainheight = PANE_MINIMUM + 1;
+
+       /* Try and make everything fit. */
+       totalrows = rows * (PANE_MINIMUM + 1) - 1;
+       if (mainheight + totalrows > w->sy) {
+               if (totalrows + PANE_MINIMUM + 1 > w->sy)
+                       mainheight = PANE_MINIMUM + 2;
+               else
+                       mainheight = w->sy - totalrows;
+               height = PANE_MINIMUM;
+       } else
+               height = (w->sy - mainheight - (rows - 1)) / rows;
+
+       /* Free old tree and create a new root. */
+       layout_free(w);
+       lc = w->layout_root = layout_create_cell(NULL);
+       layout_set_size(lc, w->sx, mainheight + rows * (height + 1) - 1, 0, 0);
+       layout_make_node(lc, LAYOUT_TOPBOTTOM);
+
+       /* Create the main pane. */
+       lcmain = layout_create_cell(lc);
+       layout_set_size(lcmain, w->sx, mainheight - 1, 0, 0);
+       layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
+       TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
+
+       /* Create a grid of the remaining cells. */
+       wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
+       for (j = 0; j < rows; j++) {
+               /* If this is the last cell, all done. */
+               if (wp == NULL)
+                       break;
+
+               /* Create the new row. */
+               lcrow = layout_create_cell(lc);
+               layout_set_size(lcrow, w->sx, height, 0, 0);
+               TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
+
+               /* If only one column, just use the row directly. */
+               if (columns == 1) {
+                       layout_make_leaf(lcrow, wp);
+                       wp = TAILQ_NEXT(wp, entry);
+                       continue;
+               }
+
+               /* Add in the columns. */
+               layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
+               for (i = 0; i < columns; i++) {
+                       /* Create and add a pane cell. */
+                       lcchild = layout_create_cell(lcrow);
+                       layout_set_size(lcchild, width, height, 0, 0);
+                       layout_make_leaf(lcchild, wp);
+                       TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
+
+                       /* Move to the next cell. */
+                       if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
+                               break;
+               }
+
+               /* Adjust the row to fit the full width if necessary. */
+               if (i == columns)
+                       i--;
+               used = ((i + 1) * (width + 1)) - 1;
+               if (w->sx <= used)
+                       continue;
+               lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
+               layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used);
+       }
+
+       /* Adjust the last row height to fit if necessary. */
+       used = mainheight + (rows * height) + rows - 1;
+       if (w->sy > used) {
+               lcrow = TAILQ_LAST(&lc->cells, layout_cells);
+               layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used);
+       }
+
+       /* Fix cell offsets. */
+       layout_fix_offsets(lc);
+       layout_fix_panes(w, w->sx, w->sy);
+
+       layout_print_cell(w->layout_root, __func__, 1);
+
+       server_redraw_window(w);
+}
+
+void
+layout_set_main_v(struct window *w)
+{
+       struct window_pane      *wp;
+       struct layout_cell      *lc, *lcmain, *lccolumn, *lcchild;
+       u_int                    n, mainwidth, otherwidth, width, height;
+       u_int                    used, i, j, columns, rows, totalcolumns;
+
+       layout_print_cell(w->layout_root, __func__, 1);
+
+       /* Get number of panes. */
+       n = window_count_panes(w);
+       if (n <= 1)
+               return;
+       n--;    /* take off main pane */
+
+       /* How many rows and columns will be needed, not counting main? */
+       rows = (w->sy + 1) / (PANE_MINIMUM + 1);        /* maximum rows */
+       if (rows == 0)
+               rows = 1;
+       columns = 1 + (n - 1) / rows;
+       rows = 1 + (n - 1) / columns;
+       height = (w->sy - (n - 1)) / rows;
+
+       /* Get the main pane width and add one for separator line. */
+       mainwidth = options_get_number(&w->options, "main-pane-width") + 1;
+
+       /* Get the optional other pane width and add one for separator line. */
+       otherwidth = options_get_number(&w->options, "other-pane-width") + 1;
+
+       /*
+        * If an other pane width was specified, honour it so long as it
+        * doesn't shrink the main width to less than the main-pane-width
+        */
+       if (otherwidth > 1 && w->sx - otherwidth > mainwidth)
+               mainwidth = w->sx - otherwidth;
+       if (mainwidth < PANE_MINIMUM + 1)
+               mainwidth = PANE_MINIMUM + 1;
+
+       /* Try and make everything fit. */
+       totalcolumns = columns * (PANE_MINIMUM + 1) - 1;
+       if (mainwidth + totalcolumns > w->sx) {
+               if (totalcolumns + PANE_MINIMUM + 1 > w->sx)
+                       mainwidth = PANE_MINIMUM + 2;
+               else
+                       mainwidth = w->sx - totalcolumns;
+               width = PANE_MINIMUM;
+       } else
+               width = (w->sx - mainwidth - (columns - 1)) / columns;
+
+       /* Free old tree and create a new root. */
+       layout_free(w);
+       lc = w->layout_root = layout_create_cell(NULL);
+       layout_set_size(lc, mainwidth + columns * (width + 1) - 1, w->sy, 0, 0);
+       layout_make_node(lc, LAYOUT_LEFTRIGHT);
+
+       /* Create the main pane. */
+       lcmain = layout_create_cell(lc);
+       layout_set_size(lcmain, mainwidth - 1, w->sy, 0, 0);
+       layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
+       TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
+
+       /* Create a grid of the remaining cells. */
+       wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
+       for (j = 0; j < columns; j++) {
+               /* If this is the last cell, all done. */
+               if (wp == NULL)
+                       break;
+
+               /* Create the new column. */
+               lccolumn = layout_create_cell(lc);
+               layout_set_size(lccolumn, width, w->sy, 0, 0);
+               TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry);
+
+               /* If only one row, just use the row directly. */
+               if (rows == 1) {
+                       layout_make_leaf(lccolumn, wp);
+                       wp = TAILQ_NEXT(wp, entry);
+                       continue;
+               }
+
+               /* Add in the rows. */
+               layout_make_node(lccolumn, LAYOUT_TOPBOTTOM);
+               for (i = 0; i < rows; i++) {
+                       /* Create and add a pane cell. */
+                       lcchild = layout_create_cell(lccolumn);
+                       layout_set_size(lcchild, width, height, 0, 0);
+                       layout_make_leaf(lcchild, wp);
+                       TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry);
+
+                       /* Move to the next cell. */
+                       if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
+                               break;
+               }
+
+               /* Adjust the column to fit the full height if necessary. */
+               if (i == rows)
+                       i--;
+               used = ((i + 1) * (height + 1)) - 1;
+               if (w->sy <= used)
+                       continue;
+               lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells);
+               layout_resize_adjust(lcchild, LAYOUT_TOPBOTTOM, w->sy - used);
+       }
+
+       /* Adjust the last column width to fit if necessary. */
+       used = mainwidth + (columns * width) + columns - 1;
+       if (w->sx > used) {
+               lccolumn = TAILQ_LAST(&lc->cells, layout_cells);
+               layout_resize_adjust(lccolumn, LAYOUT_LEFTRIGHT, w->sx - used);
+       }
+
+       /* Fix cell offsets. */
+       layout_fix_offsets(lc);
+       layout_fix_panes(w, w->sx, w->sy);
+
+       layout_print_cell(w->layout_root, __func__, 1);
+
+       server_redraw_window(w);
+}
+
+void
+layout_set_tiled(struct window *w)
+{
+       struct window_pane      *wp;
+       struct layout_cell      *lc, *lcrow, *lcchild;
+       u_int                    n, width, height, used;
+       u_int                    i, j, columns, rows;
+
+       layout_print_cell(w->layout_root, __func__, 1);
+
+       /* Get number of panes. */
+       n = window_count_panes(w);
+       if (n <= 1)
+               return;
+
+       /* How many rows and columns are wanted? */
+       rows = columns = 1;
+       while (rows * columns < n) {
+               rows++;
+               if (rows * columns < n)
+                       columns++;
+       }
+
+       /* What width and height should they be? */
+       width = (w->sx - (columns - 1)) / columns;
+       if (width < PANE_MINIMUM)
+               width = PANE_MINIMUM;
+       height = (w->sy - (rows - 1)) / rows;
+       if (height < PANE_MINIMUM)
+               height = PANE_MINIMUM;
+
+       /* Free old tree and create a new root. */
+       layout_free(w);
+       lc = w->layout_root = layout_create_cell(NULL);
+       layout_set_size(lc, (width + 1) * columns - 1,
+           (height + 1) * rows - 1, 0, 0);
+       layout_make_node(lc, LAYOUT_TOPBOTTOM);
+
+       /* Create a grid of the cells. */
+       wp = TAILQ_FIRST(&w->panes);
+       for (j = 0; j < rows; j++) {
+               /* If this is the last cell, all done. */
+               if (wp == NULL)
+                       break;
+
+               /* Create the new row. */
+               lcrow = layout_create_cell(lc);
+               layout_set_size(lcrow, w->sx, height, 0, 0);
+               TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
+
+               /* If only one column, just use the row directly. */
+               if (n - (j * columns) == 1 || columns == 1) {
+                       layout_make_leaf(lcrow, wp);
+                       wp = TAILQ_NEXT(wp, entry);
+                       continue;
+               }
+
+               /* Add in the columns. */
+               layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
+               for (i = 0; i < columns; i++) {
+                       /* Create and add a pane cell. */
+                       lcchild = layout_create_cell(lcrow);
+                       layout_set_size(lcchild, width, height, 0, 0);
+                       layout_make_leaf(lcchild, wp);
+                       TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
+
+                       /* Move to the next cell. */
+                       if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
+                               break;
+               }
+
+               /*
+                * Adjust the row and columns to fit the full width if
+                * necessary.
+                */
+               if (i == columns)
+                       i--;
+               used = ((i + 1) * (width + 1)) - 1;
+               if (w->sx <= used)
+                       continue;
+               lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
+               layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used);
+       }
+
+       /* Adjust the last row height to fit if necessary. */
+       used = (rows * height) + rows - 1;
+       if (w->sy > used) {
+               lcrow = TAILQ_LAST(&lc->cells, layout_cells);
+               layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used);
+       }
+
+       /* Fix cell offsets. */
+       layout_fix_offsets(lc);
+       layout_fix_panes(w, w->sx, w->sy);
+
+       layout_print_cell(w->layout_root, __func__, 1);
+
+       server_redraw_window(w);
+}
diff --git a/external/bsd/tmux/dist/layout.c b/external/bsd/tmux/dist/layout.c
new file mode 100644 (file)
index 0000000..7176527
--- /dev/null
@@ -0,0 +1,730 @@
+/* $Id: layout.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv 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"
+
+/*
+ * The window layout is a tree of cells each of which can be one of: a
+ * left-right container for a list of cells, a top-bottom container for a list
+ * of cells, or a container for a window pane.
+ *
+ * Each window has a pointer to the root of its layout tree (containing its
+ * panes), every pane has a pointer back to the cell containing it, and each
+ * cell a pointer to its parent cell.
+ */
+
+int    layout_resize_pane_grow(struct layout_cell *, enum layout_type, int);
+int    layout_resize_pane_shrink(struct layout_cell *, enum layout_type, int);
+
+struct layout_cell *
+layout_create_cell(struct layout_cell *lcparent)
+{
+       struct layout_cell      *lc;
+
+       lc = xmalloc(sizeof *lc);
+       lc->type = LAYOUT_WINDOWPANE;
+       lc->parent = lcparent;
+
+       TAILQ_INIT(&lc->cells);
+
+       lc->sx = UINT_MAX;
+       lc->sy = UINT_MAX;
+
+       lc->xoff = UINT_MAX;
+       lc->yoff = UINT_MAX;
+
+       lc->wp = NULL;
+
+       return (lc);
+}
+
+void
+layout_free_cell(struct layout_cell *lc)
+{
+       struct layout_cell      *lcchild;
+
+       switch (lc->type) {
+       case LAYOUT_LEFTRIGHT:
+       case LAYOUT_TOPBOTTOM:
+               while (!TAILQ_EMPTY(&lc->cells)) {
+                       lcchild = TAILQ_FIRST(&lc->cells);
+                       TAILQ_REMOVE(&lc->cells, lcchild, entry);
+                       layout_free_cell(lcchild);
+               }
+               break;
+       case LAYOUT_WINDOWPANE:
+               if (lc->wp != NULL)
+                       lc->wp->layout_cell = NULL;
+               break;
+       }
+
+       xfree(lc);
+}
+
+void
+layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
+{
+       struct layout_cell      *lcchild;
+
+       log_debug(
+           "%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, " ", lc,
+           lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, lc->sy);
+       switch (lc->type) {
+       case LAYOUT_LEFTRIGHT:
+       case LAYOUT_TOPBOTTOM:
+               TAILQ_FOREACH(lcchild, &lc->cells, entry)
+                       layout_print_cell(lcchild, hdr, n + 1);
+               break;
+       case LAYOUT_WINDOWPANE:
+               break;
+       }
+}
+
+void
+layout_set_size(
+    struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, u_int yoff)
+{
+       lc->sx = sx;
+       lc->sy = sy;
+
+       lc->xoff = xoff;
+       lc->yoff = yoff;
+}
+
+void
+layout_make_leaf(struct layout_cell *lc, struct window_pane *wp)
+{
+       lc->type = LAYOUT_WINDOWPANE;
+
+       TAILQ_INIT(&lc->cells);
+
+       wp->layout_cell = lc;
+       lc->wp = wp;
+}
+
+void
+layout_make_node(struct layout_cell *lc, enum layout_type type)
+{
+       if (type == LAYOUT_WINDOWPANE)
+               fatalx("bad layout type");
+       lc->type = type;
+
+       TAILQ_INIT(&lc->cells);
+
+       if (lc->wp != NULL)
+               lc->wp->layout_cell = NULL;
+       lc->wp = NULL;
+}
+
+/* Fix cell offsets based on their sizes. */
+void
+layout_fix_offsets(struct layout_cell *lc)
+{
+       struct layout_cell      *lcchild;
+       u_int                    xoff, yoff;
+
+       if (lc->type == LAYOUT_LEFTRIGHT) {
+               xoff = lc->xoff;
+               TAILQ_FOREACH(lcchild, &lc->cells, entry) {
+                       lcchild->xoff = xoff;
+                       lcchild->yoff = lc->yoff;
+                       if (lcchild->type != LAYOUT_WINDOWPANE)
+                               layout_fix_offsets(lcchild);
+                       xoff += lcchild->sx + 1;
+               }
+       } else {
+               yoff = lc->yoff;
+               TAILQ_FOREACH(lcchild, &lc->cells, entry) {
+                       lcchild->xoff = lc->xoff;
+                       lcchild->yoff = yoff;
+                       if (lcchild->type != LAYOUT_WINDOWPANE)
+                               layout_fix_offsets(lcchild);
+                       yoff += lcchild->sy + 1;
+               }
+       }
+}
+
+/* Update pane offsets and sizes based on their cells. */
+void
+layout_fix_panes(struct window *w, u_int wsx, u_int wsy)
+{
+       struct window_pane      *wp;
+       struct layout_cell      *lc;
+       u_int                    sx, sy;
+
+       TAILQ_FOREACH(wp, &w->panes, entry) {
+               if ((lc = wp->layout_cell) == NULL)
+                       continue;
+               wp->xoff = lc->xoff;
+               wp->yoff = lc->yoff;
+
+               /*
+                * Layout cells are limited by the smallest size of other cells
+                * within the same row or column; if this isn't the case
+                * resizing becomes difficult.
+                *
+                * However, panes do not have to take up their entire cell, so
+                * they can be cropped to the window edge if the layout
+                * overflows and they are partly visible.
+                *
+                * This stops cells being hidden unnecessarily.
+                */
+
+               /*
+                * Work out the horizontal size. If the pane is actually
+                * outside the window or the entire pane is already visible,
+                * don't crop.
+                */
+               if (lc->xoff >= wsx || lc->xoff + lc->sx < wsx)
+                       sx = lc->sx;
+               else {
+                       sx = wsx - lc->xoff;
+                       if (sx < 1)
+                               sx = lc->sx;
+               }
+
+               /*
+                * Similarly for the vertical size; the minimum vertical size
+                * is two because scroll regions cannot be one line.
+                */
+               if (lc->yoff >= wsy || lc->yoff + lc->sy < wsy)
+                       sy = lc->sy;
+               else {
+                       sy = wsy - lc->yoff;
+                       if (sy < 2)
+                               sy = lc->sy;
+               }
+
+               window_pane_resize(wp, sx, sy);
+       }
+}
+
+/* Count the number of available cells in a layout. */
+u_int
+layout_count_cells(struct layout_cell *lc)
+{
+       struct layout_cell      *lcchild;
+       u_int                    n;
+
+       switch (lc->type) {
+       case LAYOUT_WINDOWPANE:
+               return (1);
+       case LAYOUT_LEFTRIGHT:
+       case LAYOUT_TOPBOTTOM:
+               n = 0;
+               TAILQ_FOREACH(lcchild, &lc->cells, entry)
+                       n += layout_count_cells(lcchild);
+               return (n);
+       default:
+               fatalx("bad layout type");
+       }
+}
+
+/* Calculate how much size is available to be removed from a cell. */
+u_int
+layout_resize_check(struct layout_cell *lc, enum layout_type type)
+{
+       struct layout_cell      *lcchild;
+       u_int                    available, minimum;
+
+       if (lc->type == LAYOUT_WINDOWPANE) {
+               /* Space available in this cell only. */
+               if (type == LAYOUT_LEFTRIGHT)
+                       available = lc->sx;
+               else
+                       available = lc->sy;
+
+               if (available > PANE_MINIMUM)
+                       available -= PANE_MINIMUM;
+               else
+                       available = 0;
+       } else if (lc->type == type) {
+               /* Same type: total of available space in all child cells. */
+               available = 0;
+               TAILQ_FOREACH(lcchild, &lc->cells, entry)
+                       available += layout_resize_check(lcchild, type);
+       } else {
+               /* Different type: minimum of available space in child cells. */
+               minimum = UINT_MAX;
+               TAILQ_FOREACH(lcchild, &lc->cells, entry) {
+                       available = layout_resize_check(lcchild, type);
+                       if (available < minimum)
+                               minimum = available;
+               }
+               available = minimum;
+       }
+
+       return (available);
+}
+
+/*
+ * Adjust cell size evenly, including altering its children. This function
+ * expects the change to have already been bounded to the space available.
+ */
+void
+layout_resize_adjust(struct layout_cell *lc, enum layout_type type, int change)
+{
+       struct layout_cell      *lcchild;
+
+       /* Adjust the cell size. */
+       if (type == LAYOUT_LEFTRIGHT)
+               lc->sx += change;
+       else
+               lc->sy += change;
+
+       /* If this is a leaf cell, that is all that is necessary. */
+       if (type == LAYOUT_WINDOWPANE)
+               return;
+
+       /* Child cell runs in a different direction. */
+       if (lc->type != type) {
+               TAILQ_FOREACH(lcchild, &lc->cells, entry)
+                       layout_resize_adjust(lcchild, type, change);
+               return;
+       }
+
+       /*
+        * Child cell runs in the same direction. Adjust each child equally
+        * until no further change is possible.
+        */
+       while (change != 0) {
+               TAILQ_FOREACH(lcchild, &lc->cells, entry) {
+                       if (change == 0)
+                               break;
+                       if (change > 0) {
+                               layout_resize_adjust(lcchild, type, 1);
+                               change--;
+                               continue;
+                       }
+                       if (layout_resize_check(lcchild, type) > 0) {
+                               layout_resize_adjust(lcchild, type, -1);
+                               change++;
+                       }
+               }
+       }
+}
+
+/* Destroy a cell and redistribute the space. */
+void
+layout_destroy_cell(struct layout_cell *lc, struct layout_cell **lcroot)
+{
+       struct layout_cell     *lcother, *lcparent;
+
+       /*
+        * If no parent, this is the last pane so window close is imminent and
+        * there is no need to resize anything.
+        */
+       lcparent = lc->parent;
+       if (lcparent == NULL) {
+               layout_free_cell(lc);
+               *lcroot = NULL;
+               return;
+       }
+
+       /* Merge the space into the previous or next cell. */
+       if (lc == TAILQ_FIRST(&lcparent->cells))
+               lcother = TAILQ_NEXT(lc, entry);
+       else
+               lcother = TAILQ_PREV(lc, layout_cells, entry);
+       if (lcparent->type == LAYOUT_LEFTRIGHT)
+               layout_resize_adjust(lcother, lcparent->type, lc->sx + 1);
+       else
+               layout_resize_adjust(lcother, lcparent->type, lc->sy + 1);
+
+       /* Remove this from the parent's list. */
+       TAILQ_REMOVE(&lcparent->cells, lc, entry);
+       layout_free_cell(lc);
+
+       /*
+        * If the parent now has one cell, remove the parent from the tree and
+        * replace it by that cell.
+        */
+       lc = TAILQ_FIRST(&lcparent->cells);
+       if (TAILQ_NEXT(lc, entry) == NULL) {
+               TAILQ_REMOVE(&lcparent->cells, lc, entry);
+
+               lc->parent = lcparent->parent;
+               if (lc->parent == NULL) {
+                       lc->xoff = 0; lc->yoff = 0;
+                       *lcroot = lc;
+               } else
+                       TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
+
+               layout_free_cell(lcparent);
+       }
+}
+
+void
+layout_init(struct window *w)
+{
+       struct layout_cell      *lc;
+
+       lc = w->layout_root = layout_create_cell(NULL);
+       layout_set_size(lc, w->sx, w->sy, 0, 0);
+       layout_make_leaf(lc, TAILQ_FIRST(&w->panes));
+
+       layout_fix_panes(w, w->sx, w->sy);
+}
+
+void
+layout_free(struct window *w)
+{
+       layout_free_cell(w->layout_root);
+}
+
+/* Resize the entire layout after window resize. */
+void
+layout_resize(struct window *w, u_int sx, u_int sy)
+{
+       struct layout_cell      *lc = w->layout_root;
+       int                      xlimit, ylimit, xchange, ychange;
+
+       /*
+        * Adjust horizontally. Do not attempt to reduce the layout lower than
+        * the minimum (more than the amount returned by layout_resize_check).
+        *
+        * This can mean that the window size is smaller than the total layout
+        * size: redrawing this is handled at a higher level, but it does leave
+        * a problem with growing the window size here: if the current size is
+        * < the minimum, growing proportionately by adding to each pane is
+        * wrong as it would keep the layout size larger than the window size.
+        * Instead, spread the difference between the minimum and the new size
+        * out proportionately - this should leave the layout fitting the new
+        * window size.
+        */
+       xchange = sx - w->sx;
+       xlimit = layout_resize_check(lc, LAYOUT_LEFTRIGHT);
+       if (xchange < 0 && xchange < -xlimit)
+               xchange = -xlimit;
+       if (xlimit == 0) {
+               if (sx <= lc->sx)       /* lc->sx is minimum possible */
+                       xchange = 0;
+               else
+                       xchange = sx - lc->sx;
+       }
+       if (xchange != 0)
+               layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, xchange);
+
+       /* Adjust vertically in a similar fashion. */
+       ychange = sy - w->sy;
+       ylimit = layout_resize_check(lc, LAYOUT_TOPBOTTOM);
+       if (ychange < 0 && ychange < -ylimit)
+               ychange = -ylimit;
+       if (ylimit == 0) {
+               if (sy <= lc->sy)       /* lc->sy is minimum possible */
+                       ychange = 0;
+               else
+                       ychange = sy - lc->sy;
+       }
+       if (ychange != 0)
+               layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, ychange);
+
+       /* Fix cell offsets. */
+       layout_fix_offsets(lc);
+       layout_fix_panes(w, sx, sy);
+}
+
+/* Resize a single pane within the layout. */
+void
+layout_resize_pane(struct window_pane *wp, enum layout_type type, int change)
+{
+       struct layout_cell     *lc, *lcparent;
+       int                     needed, size;
+
+       lc = wp->layout_cell;
+
+       /* Find next parent of the same type. */
+       lcparent = lc->parent;
+       while (lcparent != NULL && lcparent->type != type) {
+               lc = lcparent;
+               lcparent = lc->parent;
+       }
+       if (lcparent == NULL)
+               return;
+
+       /* If this is the last cell, move back one. */
+       if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
+               lc = TAILQ_PREV(lc, layout_cells, entry);
+
+       /* Grow or shrink the cell. */
+       needed = change;
+       while (needed != 0) {
+               if (change > 0) {
+                       size = layout_resize_pane_grow(lc, type, needed);
+                       needed -= size;
+               } else {
+                       size = layout_resize_pane_shrink(lc, type, needed);
+                       needed += size;
+               }
+
+               if (size == 0)  /* no more change possible */
+                       break;
+       }
+
+       /* Fix cell offsets. */
+       layout_fix_offsets(wp->window->layout_root);
+       layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
+}
+
+void
+layout_resize_pane_mouse(struct client *c, struct mouse_event *mouse)
+{
+       struct window           *w;
+       struct window_pane      *wp;
+       int                      pane_border;
+
+       w = c->session->curw->window;
+
+       pane_border = 0;
+       if ((c->last_mouse.b & MOUSE_BUTTON) != MOUSE_UP &&
+               (c->last_mouse.b & MOUSE_RESIZE_PANE)) {
+               TAILQ_FOREACH(wp, &w->panes, entry) {
+                       if (wp->xoff + wp->sx == c->last_mouse.x &&
+                               wp->yoff <= 1 + c->last_mouse.y &&
+                               wp->yoff + wp->sy >= c->last_mouse.y) {
+                               layout_resize_pane(wp, LAYOUT_LEFTRIGHT,
+                                                                  mouse->x - c->last_mouse.x);
+                               pane_border = 1;
+                       }
+                       if (wp->yoff + wp->sy == c->last_mouse.y &&
+                               wp->xoff <= 1 + c->last_mouse.x &&
+                               wp->xoff + wp->sx >= c->last_mouse.x) {
+                               layout_resize_pane(wp, LAYOUT_TOPBOTTOM,
+                                                                  mouse->y - c->last_mouse.y);
+                               pane_border = 1;
+                       }
+               }
+               if (pane_border)
+                       server_redraw_window(w);
+       } else if (mouse->b != MOUSE_UP &&
+                          mouse->b == (mouse->b & MOUSE_BUTTON)) {
+               TAILQ_FOREACH(wp, &w->panes, entry) {
+                       if ((wp->xoff + wp->sx == mouse->x &&
+                                wp->yoff <= 1 + mouse->y &&
+                                wp->yoff + wp->sy >= mouse->y) ||
+                               (wp->yoff + wp->sy == mouse->y &&
+                                wp->xoff <= 1 + mouse->x &&
+                                wp->xoff + wp->sx >= mouse->x)) {
+                               pane_border = 1;
+                       }
+               }
+       }
+       if (pane_border)
+               mouse->b |= MOUSE_RESIZE_PANE;
+}
+
+int
+layout_resize_pane_grow(
+    struct layout_cell *lc, enum layout_type type, int needed)
+{
+       struct layout_cell      *lcadd, *lcremove;
+       u_int                    size;
+
+       /* Growing. Always add to the current cell. */
+       lcadd = lc;
+
+       /* Look towards the tail for a suitable cell for reduction. */
+       lcremove = TAILQ_NEXT(lc, entry);
+       while (lcremove != NULL) {
+               size = layout_resize_check(lcremove, type);
+               if (size > 0)
+                       break;
+               lcremove = TAILQ_NEXT(lcremove, entry);
+       }
+
+       /* If none found, look towards the head. */
+       if (lcremove == NULL) {
+               lcremove = TAILQ_PREV(lc, layout_cells, entry);
+               while (lcremove != NULL) {
+                       size = layout_resize_check(lcremove, type);
+                       if (size > 0)
+                               break;
+                       lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
+               }
+               if (lcremove == NULL)
+                       return (0);
+       }
+
+       /* Change the cells. */
+       if (size > (u_int) needed)
+               size = needed;
+       layout_resize_adjust(lcadd, type, size);
+       layout_resize_adjust(lcremove, type, -size);
+       return (size);
+}
+
+int
+layout_resize_pane_shrink(
+    struct layout_cell *lc, enum layout_type type, int needed)
+{
+       struct layout_cell      *lcadd, *lcremove;
+       u_int                    size;
+
+       /* Shrinking. Find cell to remove from by walking towards head. */
+       lcremove = lc;
+       do {
+               size = layout_resize_check(lcremove, type);
+               if (size != 0)
+                       break;
+               lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
+       } while (lcremove != NULL);
+       if (lcremove == NULL)
+               return (0);
+
+       /* And add onto the next cell (from the original cell). */
+       lcadd = TAILQ_NEXT(lc, entry);
+       if (lcadd == NULL)
+               return (0);
+
+       /* Change the cells. */
+       if (size > (u_int) -needed)
+               size = -needed;
+       layout_resize_adjust(lcadd, type, size);
+       layout_resize_adjust(lcremove, type, -size);
+       return (size);
+}
+
+/* Assign window pane to newly split cell. */
+void
+layout_assign_pane(struct layout_cell *lc, struct window_pane *wp)
+{
+       layout_make_leaf(lc, wp);
+       layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
+}
+
+/*
+ * Split a pane into two. size is a hint, or -1 for default half/half
+ * split. This must be followed by layout_assign_pane before much else happens!
+ **/
+struct layout_cell *
+layout_split_pane(struct window_pane *wp, enum layout_type type, int size)
+{
+       struct layout_cell     *lc, *lcparent, *lcnew;
+       u_int                   sx, sy, xoff, yoff, size1, size2;
+
+       lc = wp->layout_cell;
+
+       /* Copy the old cell size. */
+       sx = lc->sx;
+       sy = lc->sy;
+       xoff = lc->xoff;
+       yoff = lc->yoff;
+
+       /* Check there is enough space for the two new panes. */
+       switch (type) {
+       case LAYOUT_LEFTRIGHT:
+               if (sx < PANE_MINIMUM * 2 + 1)
+                       return (NULL);
+               break;
+       case LAYOUT_TOPBOTTOM:
+               if (sy < PANE_MINIMUM * 2 + 1)
+                       return (NULL);
+               break;
+       default:
+               fatalx("bad layout type");
+       }
+
+       if (lc->parent != NULL && lc->parent->type == type) {
+               /*
+                * If the parent exists and is of the same type as the split,
+                * create a new cell and insert it after this one.
+                */
+
+               /* Create the new child cell. */
+               lcnew = layout_create_cell(lc->parent);
+               TAILQ_INSERT_AFTER(&lc->parent->cells, lc, lcnew, entry);
+       } else {
+               /*
+                * Otherwise create a new parent and insert it.
+                */
+
+               /* Create and insert the replacement parent. */
+               lcparent = layout_create_cell(lc->parent);
+               layout_make_node(lcparent, type);
+               layout_set_size(lcparent, sx, sy, xoff, yoff);
+               if (lc->parent == NULL)
+                       wp->window->layout_root = lcparent;
+               else
+                       TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
+
+               /* Insert the old cell. */
+               lc->parent = lcparent;
+               TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
+
+               /* Create the new child cell. */
+               lcnew = layout_create_cell(lcparent);
+               TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
+       }
+
+       /* Set new cell sizes.  size is the target size or -1 for middle split,
+        * size1 is the size of the top/left and size2 the bottom/right.
+        */
+       switch (type) {
+       case LAYOUT_LEFTRIGHT:
+               if (size < 0)
+                       size2 = ((sx + 1) / 2) - 1;
+               else
+                       size2 = size;
+               if (size2 < PANE_MINIMUM)
+                       size2 = PANE_MINIMUM;
+               else if (size2 > sx - 2)
+                       size2 = sx - 2;
+               size1 = sx - 1 - size2;
+               layout_set_size(lc, size1, sy, xoff, yoff);
+               layout_set_size(lcnew, size2, sy, xoff + lc->sx + 1, yoff);
+               break;
+       case LAYOUT_TOPBOTTOM:
+               if (size < 0)
+                       size2 = ((sy + 1) / 2) - 1;
+               else
+                       size2 = size;
+               if (size2 < PANE_MINIMUM)
+                       size2 = PANE_MINIMUM;
+               else if (size2 > sy - 2)
+                       size2 = sy - 2;
+               size1 = sy - 1 - size2;
+               layout_set_size(lc, sx, size1, xoff, yoff);
+               layout_set_size(lcnew, sx, size2, xoff, yoff + lc->sy + 1);
+               break;
+       default:
+               fatalx("bad layout type");
+       }
+
+       /* Assign the panes. */
+       layout_make_leaf(lc, wp);
+
+       return (lcnew);
+}
+
+/* Destroy the cell associated with a pane. */
+void
+layout_close_pane(struct window_pane *wp)
+{
+       /* Remove the cell. */
+       layout_destroy_cell(wp->layout_cell, &wp->window->layout_root);
+
+       /* Fix pane offsets and sizes. */
+       if (wp->window->layout_root != NULL) {
+               layout_fix_offsets(wp->window->layout_root);
+               layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
+       }
+}
diff --git a/external/bsd/tmux/dist/log.c b/external/bsd/tmux/dist/log.c
new file mode 100644 (file)
index 0000000..f689d69
--- /dev/null
@@ -0,0 +1,211 @@
+/* $Id: log.c,v 1.2 2011/08/25 16:41:51 joerg 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_TTY 1
+#define LOG_TYPE_FILE 2
+int    log_type = LOG_TYPE_OFF;
+
+/* Log file, if needed. */
+FILE   *log_file;
+
+/* Debug level. */
+int    log_level;
+
+void            log_vwrite(int, const char *, va_list);
+__dead void     log_vfatal(const char *, va_list);
+
+/* 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_vwrite(int pri, const char *msg, va_list ap)
+{
+       FILE    *f = log_file;
+
+       switch (log_type) {
+       case LOG_TYPE_TTY:
+               if (pri == LOG_INFO)
+                       f = stdout;
+               else
+                       f = stderr;
+               /* FALLTHROUGH */
+       case LOG_TYPE_FILE:
+               if (vfprintf(f, msg, ap) == -1)
+                       exit(1);
+               if (putc('\n', f) == -1)
+                       exit(1);
+               fflush(f);
+               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 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/external/bsd/tmux/dist/mode-key.c b/external/bsd/tmux/dist/mode-key.c
new file mode 100644 (file)
index 0000000..eec0c04
--- /dev/null
@@ -0,0 +1,471 @@
+/* $Id: mode-key.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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"
+
+/*
+ * Mode keys. These are the key bindings used when editing (status prompt), and
+ * in the modes. They are split into two sets of three tables, one set of three
+ * for vi and the other for emacs key bindings. The three tables are for
+ * editing, for menu-like modes (choice, more), and for copy modes (copy,
+ * scroll).
+ *
+ * The fixed tables of struct mode_key_entry below are the defaults: they are
+ * built into a tree of struct mode_key_binding by mode_key_init_trees, which
+ * can then be modified.
+ *
+ * vi command mode is handled by having a mode flag in the struct which allows
+ * two sets of bindings to be swapped between. A couple of editing commands
+ * (MODEKEYEDIT_SWITCHMODE and MODEKEYEDIT_SWITCHMODEAPPEND) are special-cased
+ * to do this.
+ */
+
+/* Edit keys command strings. */
+const struct mode_key_cmdstr mode_key_cmdstr_edit[] = {
+       { MODEKEYEDIT_BACKSPACE, "backspace" },
+       { MODEKEYEDIT_CANCEL, "cancel" },
+       { MODEKEYEDIT_COMPLETE, "complete" },
+       { MODEKEYEDIT_CURSORLEFT, "cursor-left" },
+       { MODEKEYEDIT_CURSORRIGHT, "cursor-right" },
+       { MODEKEYEDIT_DELETE, "delete" },
+       { MODEKEYEDIT_DELETELINE, "delete-line" },
+       { MODEKEYEDIT_DELETETOENDOFLINE, "delete-end-of-line" },
+       { MODEKEYEDIT_ENDOFLINE, "end-of-line" },
+       { MODEKEYEDIT_ENTER, "enter" },
+       { MODEKEYEDIT_HISTORYDOWN, "history-down" },
+       { MODEKEYEDIT_HISTORYUP, "history-up" },
+       { MODEKEYEDIT_PASTE, "paste" },
+       { MODEKEYEDIT_STARTOFLINE, "start-of-line" },
+       { MODEKEYEDIT_SWITCHMODE, "switch-mode" },
+       { MODEKEYEDIT_SWITCHMODEAPPEND, "switch-mode-append" },
+       { MODEKEYEDIT_TRANSPOSECHARS, "transpose-chars" },
+
+       { 0, NULL }
+};
+
+/* Choice keys command strings. */
+const struct mode_key_cmdstr mode_key_cmdstr_choice[] = {
+       { MODEKEYCHOICE_CANCEL, "cancel" },
+       { MODEKEYCHOICE_CHOOSE, "choose" },
+       { MODEKEYCHOICE_DOWN, "down" },
+       { MODEKEYCHOICE_PAGEDOWN, "page-down" },
+       { MODEKEYCHOICE_PAGEUP, "page-up" },
+       { MODEKEYCHOICE_SCROLLDOWN, "scroll-down" },
+       { MODEKEYCHOICE_SCROLLUP, "scroll-up" },
+       { MODEKEYCHOICE_UP, "up" },
+
+       { 0, NULL }
+};
+
+/* Copy keys command strings. */
+const struct mode_key_cmdstr mode_key_cmdstr_copy[] = {
+       { MODEKEYCOPY_BACKTOINDENTATION, "back-to-indentation" },
+       { MODEKEYCOPY_BOTTOMLINE, "bottom-line" },
+       { MODEKEYCOPY_CANCEL, "cancel" },
+       { MODEKEYCOPY_CLEARSELECTION, "clear-selection" },
+       { MODEKEYCOPY_COPYLINE, "copy-line" },
+       { MODEKEYCOPY_COPYENDOFLINE, "copy-end-of-line" },
+       { MODEKEYCOPY_COPYSELECTION, "copy-selection" },
+       { MODEKEYCOPY_DOWN, "cursor-down" },
+       { MODEKEYCOPY_ENDOFLINE, "end-of-line" },
+       { MODEKEYCOPY_GOTOLINE, "goto-line" },
+       { MODEKEYCOPY_HISTORYBOTTOM, "history-bottom" },
+       { MODEKEYCOPY_HISTORYTOP, "history-top" },
+       { MODEKEYCOPY_JUMP, "jump-forward" },
+       { MODEKEYCOPY_JUMPAGAIN, "jump-again" },
+       { MODEKEYCOPY_JUMPREVERSE, "jump-reverse" },
+       { MODEKEYCOPY_JUMPBACK, "jump-backward" },
+       { MODEKEYCOPY_LEFT, "cursor-left" },
+       { MODEKEYCOPY_RECTANGLETOGGLE, "rectangle-toggle" },
+       { MODEKEYCOPY_MIDDLELINE, "middle-line" },
+       { MODEKEYCOPY_NEXTPAGE, "page-down" },
+       { MODEKEYCOPY_NEXTSPACE, "next-space" },
+       { MODEKEYCOPY_NEXTSPACEEND, "next-space-end" },
+       { MODEKEYCOPY_NEXTWORD, "next-word" },
+       { MODEKEYCOPY_NEXTWORDEND, "next-word-end" },
+       { MODEKEYCOPY_PREVIOUSPAGE, "page-up" },
+       { MODEKEYCOPY_PREVIOUSSPACE, "previous-space" },
+       { MODEKEYCOPY_PREVIOUSWORD, "previous-word" },
+       { MODEKEYCOPY_RIGHT, "cursor-right" },
+       { MODEKEYCOPY_SCROLLDOWN, "scroll-down" },
+       { MODEKEYCOPY_SCROLLUP, "scroll-up" },
+       { MODEKEYCOPY_SEARCHAGAIN, "search-again" },
+       { MODEKEYCOPY_SEARCHDOWN, "search-forward" },
+       { MODEKEYCOPY_SEARCHREVERSE, "search-reverse" },
+       { MODEKEYCOPY_SEARCHUP, "search-backward" },
+       { MODEKEYCOPY_SELECTLINE, "select-line" },
+       { MODEKEYCOPY_STARTNUMBERPREFIX, "start-number-prefix" },
+       { MODEKEYCOPY_STARTOFLINE, "start-of-line" },
+       { MODEKEYCOPY_STARTSELECTION, "begin-selection" },
+       { MODEKEYCOPY_TOPLINE, "top-line" },
+       { MODEKEYCOPY_UP, "cursor-up" },
+
+       { 0, NULL }
+};
+
+/* vi editing keys. */
+const struct mode_key_entry mode_key_vi_edit[] = {
+       { '\003' /* C-c */,     0, MODEKEYEDIT_CANCEL },
+       { '\010' /* C-h */,     0, MODEKEYEDIT_BACKSPACE },
+       { '\025' /* C-u */,     0, MODEKEYEDIT_DELETELINE },
+       { '\011' /* Tab */,     0, MODEKEYEDIT_COMPLETE },
+       { '\033' /* Escape */,  0, MODEKEYEDIT_SWITCHMODE },
+       { '\r',                 0, MODEKEYEDIT_ENTER },
+       { KEYC_BSPACE,          0, MODEKEYEDIT_BACKSPACE },
+       { KEYC_DC,              0, MODEKEYEDIT_DELETE },
+
+       { '$',                  1, MODEKEYEDIT_ENDOFLINE },
+       { '0',                  1, MODEKEYEDIT_STARTOFLINE },
+       { 'D',                  1, MODEKEYEDIT_DELETETOENDOFLINE },
+       { '\003' /* C-c */,     1, MODEKEYEDIT_CANCEL },
+       { '\010' /* C-h */,     1, MODEKEYEDIT_BACKSPACE },
+       { '\r',                 1, MODEKEYEDIT_ENTER },
+       { '^',                  1, MODEKEYEDIT_STARTOFLINE },
+       { 'a',                  1, MODEKEYEDIT_SWITCHMODEAPPEND },
+       { 'd',                  1, MODEKEYEDIT_DELETELINE },
+       { 'h',                  1, MODEKEYEDIT_CURSORLEFT },
+       { 'i',                  1, MODEKEYEDIT_SWITCHMODE },
+       { 'j',                  1, MODEKEYEDIT_HISTORYDOWN },
+       { 'k',                  1, MODEKEYEDIT_HISTORYUP },
+       { 'l',                  1, MODEKEYEDIT_CURSORRIGHT },
+       { 'p',                  1, MODEKEYEDIT_PASTE },
+       { KEYC_BSPACE,          1, MODEKEYEDIT_BACKSPACE },
+       { KEYC_DC,              1, MODEKEYEDIT_DELETE },
+       { KEYC_DOWN,            1, MODEKEYEDIT_HISTORYDOWN },
+       { KEYC_LEFT,            1, MODEKEYEDIT_CURSORLEFT },
+       { KEYC_RIGHT,           1, MODEKEYEDIT_CURSORRIGHT },
+       { KEYC_UP,              1, MODEKEYEDIT_HISTORYUP },
+
+       { 0,                   -1, 0 }
+};
+struct mode_key_tree mode_key_tree_vi_edit;
+
+/* vi choice selection keys. */
+const struct mode_key_entry mode_key_vi_choice[] = {
+       { '\002' /* C-b */,     0, MODEKEYCHOICE_PAGEUP },
+       { '\003' /* C-c */,     0, MODEKEYCHOICE_CANCEL },
+       { '\005' /* C-e */,     0, MODEKEYCHOICE_SCROLLDOWN },
+       { '\006' /* C-f */,     0, MODEKEYCHOICE_PAGEDOWN },
+       { '\031' /* C-y */,     0, MODEKEYCHOICE_SCROLLUP },
+       { '\r',                 0, MODEKEYCHOICE_CHOOSE },
+       { 'j',                  0, MODEKEYCHOICE_DOWN },
+       { 'k',                  0, MODEKEYCHOICE_UP },
+       { 'q',                  0, MODEKEYCHOICE_CANCEL },
+       { KEYC_DOWN | KEYC_CTRL,0, MODEKEYCHOICE_SCROLLDOWN },
+       { KEYC_DOWN,            0, MODEKEYCHOICE_DOWN },
+       { KEYC_NPAGE,           0, MODEKEYCHOICE_PAGEDOWN },
+       { KEYC_PPAGE,           0, MODEKEYCHOICE_PAGEUP },
+       { KEYC_UP | KEYC_CTRL,  0, MODEKEYCHOICE_SCROLLUP },
+       { KEYC_UP,              0, MODEKEYCHOICE_UP },
+
+       { 0,                    -1, 0 }
+};
+struct mode_key_tree mode_key_tree_vi_choice;
+
+/* vi copy mode keys. */
+const struct mode_key_entry mode_key_vi_copy[] = {
+       { ' ',                  0, MODEKEYCOPY_STARTSELECTION },
+       { '$',                  0, MODEKEYCOPY_ENDOFLINE },
+       { ',',                  0, MODEKEYCOPY_JUMPREVERSE },
+       { ';',                  0, MODEKEYCOPY_JUMPAGAIN },
+       { '/',                  0, MODEKEYCOPY_SEARCHDOWN },
+       { '0',                  0, MODEKEYCOPY_STARTOFLINE },
+       { '1',                  0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '2',                  0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '3',                  0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '4',                  0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '5',                  0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '6',                  0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '7',                  0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '8',                  0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '9',                  0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { ':',                  0, MODEKEYCOPY_GOTOLINE },
+       { '?',                  0, MODEKEYCOPY_SEARCHUP },
+       { 'B',                  0, MODEKEYCOPY_PREVIOUSSPACE },
+       { 'D',                  0, MODEKEYCOPY_COPYENDOFLINE },
+       { 'E',                  0, MODEKEYCOPY_NEXTSPACEEND },
+       { 'F',                  0, MODEKEYCOPY_JUMPBACK },
+       { 'G',                  0, MODEKEYCOPY_HISTORYBOTTOM },
+       { 'H',                  0, MODEKEYCOPY_TOPLINE },
+       { 'J',                  0, MODEKEYCOPY_SCROLLDOWN },
+       { 'K',                  0, MODEKEYCOPY_SCROLLUP },
+       { 'L',                  0, MODEKEYCOPY_BOTTOMLINE },
+       { 'M',                  0, MODEKEYCOPY_MIDDLELINE },
+       { 'N',                  0, MODEKEYCOPY_SEARCHREVERSE },
+       { 'W',                  0, MODEKEYCOPY_NEXTSPACE },
+       { '\002' /* C-b */,     0, MODEKEYCOPY_PREVIOUSPAGE },
+       { '\003' /* C-c */,     0, MODEKEYCOPY_CANCEL },
+       { '\004' /* C-d */,     0, MODEKEYCOPY_HALFPAGEDOWN },
+       { '\005' /* C-e */,     0, MODEKEYCOPY_SCROLLDOWN },
+       { '\006' /* C-f */,     0, MODEKEYCOPY_NEXTPAGE },
+       { '\010' /* C-h */,     0, MODEKEYCOPY_LEFT },
+       { '\025' /* C-u */,     0, MODEKEYCOPY_HALFPAGEUP },
+       { '\031' /* C-y */,     0, MODEKEYCOPY_SCROLLUP },
+       { '\033' /* Escape */,  0, MODEKEYCOPY_CLEARSELECTION },
+       { '\r',                 0, MODEKEYCOPY_COPYSELECTION },
+       { '^',                  0, MODEKEYCOPY_BACKTOINDENTATION },
+       { 'b',                  0, MODEKEYCOPY_PREVIOUSWORD },
+       { 'e',                  0, MODEKEYCOPY_NEXTWORDEND },
+       { 'f',                  0, MODEKEYCOPY_JUMP },
+       { 'g',                  0, MODEKEYCOPY_HISTORYTOP },
+       { 'h',                  0, MODEKEYCOPY_LEFT },
+       { 'j',                  0, MODEKEYCOPY_DOWN },
+       { 'k',                  0, MODEKEYCOPY_UP },
+       { 'l',                  0, MODEKEYCOPY_RIGHT },
+       { 'n',                  0, MODEKEYCOPY_SEARCHAGAIN },
+       { 'q',                  0, MODEKEYCOPY_CANCEL },
+       { 'v',                  0, MODEKEYCOPY_RECTANGLETOGGLE },
+       { 'w',                  0, MODEKEYCOPY_NEXTWORD },
+       { KEYC_BSPACE,          0, MODEKEYCOPY_LEFT },
+       { KEYC_DOWN | KEYC_CTRL,0, MODEKEYCOPY_SCROLLDOWN },
+       { KEYC_DOWN,            0, MODEKEYCOPY_DOWN },
+       { KEYC_LEFT,            0, MODEKEYCOPY_LEFT },
+       { KEYC_NPAGE,           0, MODEKEYCOPY_NEXTPAGE },
+       { KEYC_PPAGE,           0, MODEKEYCOPY_PREVIOUSPAGE },
+       { KEYC_RIGHT,           0, MODEKEYCOPY_RIGHT },
+       { KEYC_UP | KEYC_CTRL,  0, MODEKEYCOPY_SCROLLUP },
+       { KEYC_UP,              0, MODEKEYCOPY_UP },
+
+       { 0,                    -1, 0 }
+};
+struct mode_key_tree mode_key_tree_vi_copy;
+
+/* emacs editing keys. */
+const struct mode_key_entry mode_key_emacs_edit[] = {
+       { '\001' /* C-a */,     0, MODEKEYEDIT_STARTOFLINE },
+       { '\002' /* C-b */,     0, MODEKEYEDIT_CURSORLEFT },
+       { '\003' /* C-c */,     0, MODEKEYEDIT_CANCEL },
+       { '\004' /* C-d */,     0, MODEKEYEDIT_DELETE },
+       { '\005' /* C-e */,     0, MODEKEYEDIT_ENDOFLINE },
+       { '\006' /* C-f */,     0, MODEKEYEDIT_CURSORRIGHT },
+       { '\010' /* C-H */,     0, MODEKEYEDIT_BACKSPACE },
+       { '\011' /* Tab */,     0, MODEKEYEDIT_COMPLETE },
+       { '\013' /* C-k */,     0, MODEKEYEDIT_DELETETOENDOFLINE },
+       { '\016' /* C-n */,     0, MODEKEYEDIT_HISTORYDOWN },
+       { '\020' /* C-p */,     0, MODEKEYEDIT_HISTORYUP },
+       { '\024' /* C-t */,     0, MODEKEYEDIT_TRANSPOSECHARS },
+       { '\025' /* C-u */,     0, MODEKEYEDIT_DELETELINE },
+       { '\031' /* C-y */,     0, MODEKEYEDIT_PASTE },
+       { '\033' /* Escape */,  0, MODEKEYEDIT_CANCEL },
+       { '\r',                 0, MODEKEYEDIT_ENTER },
+       { 'm' | KEYC_ESCAPE,    0, MODEKEYEDIT_STARTOFLINE },
+       { KEYC_BSPACE,          0, MODEKEYEDIT_BACKSPACE },
+       { KEYC_DC,              0, MODEKEYEDIT_DELETE },
+       { KEYC_DOWN,            0, MODEKEYEDIT_HISTORYDOWN },
+       { KEYC_LEFT,            0, MODEKEYEDIT_CURSORLEFT },
+       { KEYC_RIGHT,           0, MODEKEYEDIT_CURSORRIGHT },
+       { KEYC_UP,              0, MODEKEYEDIT_HISTORYUP },
+
+       { 0,                   -1, 0 }
+};
+struct mode_key_tree mode_key_tree_emacs_edit;
+
+/* emacs choice selection keys. */
+const struct mode_key_entry mode_key_emacs_choice[] = {
+       { '\003' /* C-c */,     0, MODEKEYCHOICE_CANCEL },
+       { '\016' /* C-n */,     0, MODEKEYCHOICE_DOWN },
+       { '\020' /* C-p */,     0, MODEKEYCHOICE_UP },
+       { '\026' /* C-v */,     0, MODEKEYCHOICE_PAGEDOWN },
+       { '\033' /* Escape */,  0, MODEKEYCHOICE_CANCEL },
+       { '\r',                 0, MODEKEYCHOICE_CHOOSE },
+       { 'q',                  0, MODEKEYCHOICE_CANCEL },
+       { 'v' | KEYC_ESCAPE,    0, MODEKEYCHOICE_PAGEUP },
+       { KEYC_DOWN | KEYC_CTRL,0, MODEKEYCHOICE_SCROLLDOWN },
+       { KEYC_DOWN,            0, MODEKEYCHOICE_DOWN },
+       { KEYC_NPAGE,           0, MODEKEYCHOICE_PAGEDOWN },
+       { KEYC_PPAGE,           0, MODEKEYCHOICE_PAGEUP },
+       { KEYC_UP | KEYC_CTRL,  0, MODEKEYCHOICE_SCROLLUP },
+       { KEYC_UP,              0, MODEKEYCHOICE_UP },
+
+       { 0,                    -1, 0 }
+};
+struct mode_key_tree mode_key_tree_emacs_choice;
+
+/* emacs copy mode keys. */
+const struct mode_key_entry mode_key_emacs_copy[] = {
+       { ' ',                  0, MODEKEYCOPY_NEXTPAGE },
+       { ',',                  0, MODEKEYCOPY_JUMPREVERSE },
+       { ';',                  0, MODEKEYCOPY_JUMPAGAIN },
+       { '1' | KEYC_ESCAPE,    0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '2' | KEYC_ESCAPE,    0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '3' | KEYC_ESCAPE,    0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '4' | KEYC_ESCAPE,    0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '5' | KEYC_ESCAPE,    0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '6' | KEYC_ESCAPE,    0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '7' | KEYC_ESCAPE,    0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '8' | KEYC_ESCAPE,    0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '9' | KEYC_ESCAPE,    0, MODEKEYCOPY_STARTNUMBERPREFIX },
+       { '<' | KEYC_ESCAPE,    0, MODEKEYCOPY_HISTORYTOP },
+       { '>' | KEYC_ESCAPE,    0, MODEKEYCOPY_HISTORYBOTTOM },
+       { 'F',                  0, MODEKEYCOPY_JUMPBACK },
+       { 'N',                  0, MODEKEYCOPY_SEARCHREVERSE },
+       { 'R' | KEYC_ESCAPE,    0, MODEKEYCOPY_TOPLINE },
+       { 'R',                  0, MODEKEYCOPY_RECTANGLETOGGLE },
+       { '\000' /* C-Space */, 0, MODEKEYCOPY_STARTSELECTION },
+       { '\001' /* C-a */,     0, MODEKEYCOPY_STARTOFLINE },
+       { '\002' /* C-b */,     0, MODEKEYCOPY_LEFT },
+       { '\003' /* C-c */,     0, MODEKEYCOPY_CANCEL },
+       { '\005' /* C-e */,     0, MODEKEYCOPY_ENDOFLINE },
+       { '\006' /* C-f */,     0, MODEKEYCOPY_RIGHT },
+       { '\007' /* C-g */,     0, MODEKEYCOPY_CLEARSELECTION },
+       { '\013' /* C-k */,     0, MODEKEYCOPY_COPYENDOFLINE },
+       { '\016' /* C-n */,     0, MODEKEYCOPY_DOWN },
+       { '\020' /* C-p */,     0, MODEKEYCOPY_UP },
+       { '\022' /* C-r */,     0, MODEKEYCOPY_SEARCHUP },
+       { '\023' /* C-s */,     0, MODEKEYCOPY_SEARCHDOWN },
+       { '\026' /* C-v */,     0, MODEKEYCOPY_NEXTPAGE },
+       { '\027' /* C-w */,     0, MODEKEYCOPY_COPYSELECTION },
+       { '\033' /* Escape */,  0, MODEKEYCOPY_CANCEL },
+       { 'N',                  0, MODEKEYCOPY_SEARCHREVERSE },
+       { 'b' | KEYC_ESCAPE,    0, MODEKEYCOPY_PREVIOUSWORD },
+       { 'f',                  0, MODEKEYCOPY_JUMP },
+       { 'f' | KEYC_ESCAPE,    0, MODEKEYCOPY_NEXTWORDEND },
+       { 'g',                  0, MODEKEYCOPY_GOTOLINE },
+       { 'm' | KEYC_ESCAPE,    0, MODEKEYCOPY_BACKTOINDENTATION },
+       { 'n',                  0, MODEKEYCOPY_SEARCHAGAIN },
+       { 'q',                  0, MODEKEYCOPY_CANCEL },
+       { 'r' | KEYC_ESCAPE,    0, MODEKEYCOPY_MIDDLELINE },
+       { 'v' | KEYC_ESCAPE,    0, MODEKEYCOPY_PREVIOUSPAGE },
+       { 'w' | KEYC_ESCAPE,    0, MODEKEYCOPY_COPYSELECTION },
+       { KEYC_DOWN | KEYC_CTRL,0, MODEKEYCOPY_SCROLLDOWN },
+       { KEYC_DOWN | KEYC_ESCAPE, 0, MODEKEYCOPY_HALFPAGEDOWN },
+       { KEYC_DOWN,            0, MODEKEYCOPY_DOWN },
+       { KEYC_LEFT,            0, MODEKEYCOPY_LEFT },
+       { KEYC_NPAGE,           0, MODEKEYCOPY_NEXTPAGE },
+       { KEYC_PPAGE,           0, MODEKEYCOPY_PREVIOUSPAGE },
+       { KEYC_RIGHT,           0, MODEKEYCOPY_RIGHT },
+       { KEYC_UP | KEYC_CTRL,  0, MODEKEYCOPY_SCROLLUP },
+       { KEYC_UP | KEYC_ESCAPE, 0, MODEKEYCOPY_HALFPAGEUP },
+       { KEYC_UP,              0, MODEKEYCOPY_UP },
+
+       { 0,                    -1, 0 }
+};
+struct mode_key_tree mode_key_tree_emacs_copy;
+
+/* Table mapping key table names to default settings and trees. */
+const struct mode_key_table mode_key_tables[] = {
+       { "vi-edit", mode_key_cmdstr_edit,
+         &mode_key_tree_vi_edit, mode_key_vi_edit },
+       { "vi-choice", mode_key_cmdstr_choice,
+         &mode_key_tree_vi_choice, mode_key_vi_choice },
+       { "vi-copy", mode_key_cmdstr_copy,
+         &mode_key_tree_vi_copy, mode_key_vi_copy },
+       { "emacs-edit", mode_key_cmdstr_edit,
+         &mode_key_tree_emacs_edit, mode_key_emacs_edit },
+       { "emacs-choice", mode_key_cmdstr_choice,
+         &mode_key_tree_emacs_choice, mode_key_emacs_choice },
+       { "emacs-copy", mode_key_cmdstr_copy,
+         &mode_key_tree_emacs_copy, mode_key_emacs_copy },
+
+       { NULL, NULL, NULL, NULL }
+};
+
+SPLAY_GENERATE(mode_key_tree, mode_key_binding, entry, mode_key_cmp);
+
+int
+mode_key_cmp(struct mode_key_binding *mbind1, struct mode_key_binding *mbind2)
+{
+       if (mbind1->mode != mbind2->mode)
+               return (mbind1->mode - mbind2->mode);
+       return (mbind1->key - mbind2->key);
+}
+
+const char *
+mode_key_tostring(const struct mode_key_cmdstr *cmdstr, enum mode_key_cmd cmd)
+{
+       for (; cmdstr->name != NULL; cmdstr++) {
+               if (cmdstr->cmd == cmd)
+                       return (cmdstr->name);
+       }
+       return (NULL);
+}
+
+enum mode_key_cmd
+mode_key_fromstring(const struct mode_key_cmdstr *cmdstr, const char *name)
+{
+       for (; cmdstr->name != NULL; cmdstr++) {
+               if (strcasecmp(cmdstr->name, name) == 0)
+                       return (cmdstr->cmd);
+       }
+       return (MODEKEY_NONE);
+}
+
+const struct mode_key_table *
+mode_key_findtable(const char *name)
+{
+       const struct mode_key_table     *mtab;
+
+       for (mtab = mode_key_tables; mtab->name != NULL; mtab++) {
+               if (strcasecmp(name, mtab->name) == 0)
+                       return (mtab);
+       }
+       return (NULL);
+}
+
+void
+mode_key_init_trees(void)
+{
+       const struct mode_key_table     *mtab;
+       const struct mode_key_entry     *ment;
+       struct mode_key_binding         *mbind;
+
+       for (mtab = mode_key_tables; mtab->name != NULL; mtab++) {
+               SPLAY_INIT(mtab->tree);
+               for (ment = mtab->table; ment->mode != -1; ment++) {
+                       mbind = xmalloc(sizeof *mbind);
+                       mbind->key = ment->key;
+                       mbind->mode = ment->mode;
+                       mbind->cmd = ment->cmd;
+                       SPLAY_INSERT(mode_key_tree, mtab->tree, mbind);
+               }
+       }
+}
+
+void
+mode_key_init(struct mode_key_data *mdata, struct mode_key_tree *mtree)
+{
+       mdata->tree = mtree;
+       mdata->mode = 0;
+}
+
+enum mode_key_cmd
+mode_key_lookup(struct mode_key_data *mdata, int key)
+{
+       struct mode_key_binding *mbind, mtmp;
+
+       mtmp.key = key;
+       mtmp.mode = mdata->mode;
+       if ((mbind = SPLAY_FIND(mode_key_tree, mdata->tree, &mtmp)) == NULL) {
+               if (mdata->mode != 0)
+                       return (MODEKEY_NONE);
+               return (MODEKEY_OTHER);
+       }
+
+       switch (mbind->cmd) {
+       case MODEKEYEDIT_SWITCHMODE:
+       case MODEKEYEDIT_SWITCHMODEAPPEND:
+               mdata->mode = 1 - mdata->mode;
+               /* FALLTHROUGH */
+       default:
+               return (mbind->cmd);
+       }
+}
diff --git a/external/bsd/tmux/dist/names.c b/external/bsd/tmux/dist/names.c
new file mode 100644 (file)
index 0000000..ef2ec88
--- /dev/null
@@ -0,0 +1,125 @@
+/* $Id: names.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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"
+
+void    window_name_callback(unused int, unused short, void *);
+char   *parse_window_name(const char *);
+
+void
+queue_window_name(struct window *w)
+{
+       struct timeval  tv;
+
+       tv.tv_sec = 0;
+       tv.tv_usec = NAME_INTERVAL * 1000L;
+
+       evtimer_del(&w->name_timer);
+       evtimer_set(&w->name_timer, window_name_callback, w);
+       evtimer_add(&w->name_timer, &tv);
+}
+
+/* ARGSUSED */
+void
+window_name_callback(unused int fd, unused short events, void *data)
+{
+       struct window   *w = data;
+       char            *name, *wname;
+
+       queue_window_name(w);   /* XXX even if the option is off? */
+       if (!options_get_number(&w->options, "automatic-rename"))
+               return;
+
+       if (w->active->screen != &w->active->base)
+               name = NULL;
+       else
+               name = osdep_get_name(w->active->fd, w->active->tty);
+       if (name == NULL)
+               wname = default_window_name(w);
+       else {
+               /*
+                * If tmux is using the default command, it will be a login
+                * shell and argv[0] may have a - prefix. Remove this if it is
+                * present. Ick.
+                */
+               if (w->active->cmd != NULL && *w->active->cmd == '\0' &&
+                   name != NULL && name[0] == '-' && name[1] != '\0')
+                       wname = parse_window_name(name + 1);
+               else
+                               wname = parse_window_name(name);
+               xfree(name);
+       }
+
+       if (w->active->fd == -1) {
+               xasprintf(&name, "%s[dead]", wname);
+               xfree(wname);
+               wname = 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]"));
+       if (w->active->cmd != NULL && *w->active->cmd != '\0')
+               return (parse_window_name(w->active->cmd));
+       return (parse_window_name(w->active->shell));
+}
+
+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((u_char)*ptr))
+                       *ptr-- = '\0';
+       }
+
+       if (*name == '/')
+               name = basename(name);
+       name = xstrdup(name);
+       xfree(copy);
+       return (name);
+}
diff --git a/external/bsd/tmux/dist/options-table.c b/external/bsd/tmux/dist/options-table.c
new file mode 100644 (file)
index 0000000..50c620e
--- /dev/null
@@ -0,0 +1,666 @@
+/* $Id: options-table.c,v 1.1.1.1 2011/08/17 18:40:05 jmmv Exp $ */
+
+/*
+ * Copyright (c) 2011 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"
+
+/*
+ * This file has a tables with all the server, session and window
+ * options. These tables are the master copy of the options with their real
+ * (user-visible) types, range limits and default values. At start these are
+ * copied into the runtime global options trees (which only has number and
+ * string types). These tables are then used to loop up the real type when
+ * the user sets an option or its value needs to be shown.
+ */
+
+/* Choice option type lists. */
+const char *options_table_mode_keys_list[] = {
+       "emacs", "vi", NULL
+};
+const char *options_table_clock_mode_style_list[] = {
+       "12", "24", NULL
+};
+const char *options_table_status_keys_list[] = {
+       "emacs", "vi", NULL
+};
+const char *options_table_status_justify_list[] = {
+       "left", "centre", "right", NULL
+};
+const char *options_table_bell_action_list[] = {
+       "none", "any", "current", NULL
+};
+
+/* Server options. */
+const struct options_table_entry server_options_table[] = {
+       { .name = "buffer-limit",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 1,
+         .maximum = INT_MAX,
+         .default_num = 20
+       },
+
+       { .name = "escape-time",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 0,
+         .maximum = INT_MAX,
+         .default_num = 500
+       },
+
+       { .name = "exit-unattached",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "quiet",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0 /* overridden in main() */
+       },
+
+       { .name = "set-clipboard",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 1
+       },
+
+       { .name = NULL }
+};
+
+/* Session options. */
+const struct options_table_entry session_options_table[] = {
+       { .name = "base-index",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 0,
+         .maximum = INT_MAX,
+         .default_num = 0
+       },
+
+       { .name = "bell-action",
+         .type = OPTIONS_TABLE_CHOICE,
+         .choices = options_table_bell_action_list,
+         .default_num = BELL_ANY
+       },
+
+       { .name = "bell-on-alert",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "default-command",
+         .type = OPTIONS_TABLE_STRING,
+         .default_str = ""
+       },
+
+       { .name = "default-path",
+         .type = OPTIONS_TABLE_STRING,
+         .default_str = ""
+       },
+
+       { .name = "default-shell",
+         .type = OPTIONS_TABLE_STRING,
+         .default_str = _PATH_BSHELL
+       },
+
+       { .name = "default-terminal",
+         .type = OPTIONS_TABLE_STRING,
+         .default_str = "screen"
+       },
+
+       { .name = "destroy-unattached",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "detach-on-destroy",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 1
+       },
+
+       { .name = "display-panes-active-colour",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 1
+       },
+
+       { .name = "display-panes-colour",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 4
+       },
+
+       { .name = "display-panes-time",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 1,
+         .maximum = INT_MAX,
+         .default_num = 1000
+       },
+
+       { .name = "display-time",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 1,
+         .maximum = INT_MAX,
+         .default_num = 750
+       },
+
+       { .name = "history-limit",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 0,
+         .maximum = INT_MAX,
+         .default_num = 2000
+       },
+
+       { .name = "lock-after-time",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 0,
+         .maximum = INT_MAX,
+         .default_num = 0
+       },
+
+       { .name = "lock-command",
+         .type = OPTIONS_TABLE_STRING,
+         .default_str = "lock -np"
+       },
+
+       { .name = "lock-server",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 1
+       },
+
+       { .name = "message-attr",
+         .type = OPTIONS_TABLE_ATTRIBUTES,
+         .default_num = 0
+       },
+
+       { .name = "message-bg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 3
+       },
+
+       { .name = "message-fg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 0
+       },
+
+       { .name = "message-limit",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 0,
+         .maximum = INT_MAX,
+         .default_num = 20
+       },
+
+       { .name = "mouse-resize-pane",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "mouse-select-pane",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "mouse-select-window",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "mouse-utf8",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "pane-active-border-bg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 8
+       },
+
+       { .name = "pane-active-border-fg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 2
+       },
+
+       { .name = "pane-border-bg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 8
+       },
+
+       { .name = "pane-border-fg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 8
+       },
+
+       { .name = "prefix",
+         .type = OPTIONS_TABLE_KEYS,
+         /* set in main() */
+       },
+
+       { .name = "repeat-time",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 0,
+         .maximum = SHRT_MAX,
+         .default_num = 500
+       },
+
+       { .name = "set-remain-on-exit",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "set-titles",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "set-titles-string",
+         .type = OPTIONS_TABLE_STRING,
+         .default_str = "#S:#I:#W - \"#T\""
+       },
+
+       { .name = "status",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 1
+       },
+
+       { .name = "status-attr",
+         .type = OPTIONS_TABLE_ATTRIBUTES,
+         .default_num = 0
+       },
+
+       { .name = "status-bg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 2
+       },
+
+       { .name = "status-fg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 0
+       },
+
+       { .name = "status-interval",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 0,
+         .maximum = INT_MAX,
+         .default_num = 15
+       },
+
+       { .name = "status-justify",
+         .type = OPTIONS_TABLE_CHOICE,
+         .choices = options_table_status_justify_list,
+         .default_num = 0
+       },
+
+       { .name = "status-keys",
+         .type = OPTIONS_TABLE_CHOICE,
+         .choices = options_table_status_keys_list,
+         .default_num = MODEKEY_EMACS
+       },
+
+       { .name = "status-left",
+         .type = OPTIONS_TABLE_STRING,
+         .default_str = "[#S]"
+       },
+
+       { .name = "status-left-attr",
+         .type = OPTIONS_TABLE_ATTRIBUTES,
+         .default_num = 0
+       },
+
+       { .name = "status-left-bg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 8
+       },
+
+       { .name = "status-left-fg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 8
+       },
+
+       { .name = "status-left-length",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 0,
+         .maximum = SHRT_MAX,
+         .default_num = 10
+       },
+
+       { .name = "status-right",
+         .type = OPTIONS_TABLE_STRING,
+         .default_str = "\"#22T\" %H:%M %d-%b-%y"
+       },
+
+       { .name = "status-right-attr",
+         .type = OPTIONS_TABLE_ATTRIBUTES,
+         .default_num = 0
+       },
+
+       { .name = "status-right-bg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 8
+       },
+
+       { .name = "status-right-fg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 8
+       },
+
+       { .name = "status-right-length",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 0,
+         .maximum = SHRT_MAX,
+         .default_num = 40
+       },
+
+       { .name = "status-utf8",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0 /* overridden in main() */
+       },
+
+       { .name = "terminal-overrides",
+         .type = OPTIONS_TABLE_STRING,
+         .default_str = "*88col*:colors=88,*256col*:colors=256"
+                        ",xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007"
+                        ":Cc=\\E]12;%p1%s\\007:Cr=\\E]112\\007"
+                        ":Cs=\\E[%p1%d q:Csr=\\E[2 q"
+       },
+
+       { .name = "update-environment",
+         .type = OPTIONS_TABLE_STRING,
+         .default_str = "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID "
+                        "SSH_CONNECTION WINDOWID XAUTHORITY"
+
+       },
+
+       { .name = "visual-activity",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "visual-bell",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "visual-content",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "visual-silence",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = NULL }
+};
+
+/* Window options. */
+const struct options_table_entry window_options_table[] = {
+       { .name = "aggressive-resize",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "alternate-screen",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 1
+       },
+
+       { .name = "automatic-rename",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 1
+       },
+
+       { .name = "clock-mode-colour",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 4
+       },
+
+       { .name = "clock-mode-style",
+         .type = OPTIONS_TABLE_CHOICE,
+         .choices = options_table_clock_mode_style_list,
+         .default_num = 1
+       },
+
+       { .name = "force-height",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 0,
+         .maximum = INT_MAX,
+         .default_num = 0
+       },
+
+       { .name = "force-width",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 0,
+         .maximum = INT_MAX,
+         .default_num = 0
+       },
+
+       { .name = "main-pane-height",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 1,
+         .maximum = INT_MAX,
+         .default_num = 24
+       },
+
+       { .name = "main-pane-width",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 1,
+         .maximum = INT_MAX,
+         .default_num = 80
+       },
+
+       { .name = "mode-attr",
+         .type = OPTIONS_TABLE_ATTRIBUTES,
+         .default_num = 0
+       },
+
+       { .name = "mode-bg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 3
+       },
+
+       { .name = "mode-fg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 0
+       },
+
+       { .name = "mode-keys",
+         .type = OPTIONS_TABLE_CHOICE,
+         .choices = options_table_mode_keys_list,
+         .default_num = MODEKEY_EMACS
+       },
+
+       { .name = "mode-mouse",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "monitor-activity",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "monitor-content",
+         .type = OPTIONS_TABLE_STRING,
+         .default_str = ""
+       },
+
+       { .name = "monitor-silence",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 0,
+         .maximum = INT_MAX,
+         .default_num = 0
+       },
+
+       { .name = "other-pane-height",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 0,
+         .maximum = INT_MAX,
+         .default_num = 0
+       },
+
+       { .name = "other-pane-width",
+         .type = OPTIONS_TABLE_NUMBER,
+         .minimum = 0,
+         .maximum = INT_MAX,
+         .default_num = 0
+       },
+
+       { .name = "remain-on-exit",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "synchronize-panes",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = "utf8",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0 /* overridden in main() */
+       },
+
+       { .name = "window-status-alert-attr",
+         .type = OPTIONS_TABLE_ATTRIBUTES,
+         .default_num = GRID_ATTR_REVERSE
+       },
+
+       { .name = "window-status-alert-bg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 8
+       },
+
+       { .name = "window-status-alert-fg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 8
+       },
+
+       { .name = "window-status-attr",
+         .type = OPTIONS_TABLE_ATTRIBUTES,
+         .default_num = 0
+       },
+
+       { .name = "window-status-bg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 8
+       },
+
+       { .name = "window-status-current-attr",
+         .type = OPTIONS_TABLE_ATTRIBUTES,
+         .default_num = 0
+       },
+
+       { .name = "window-status-current-bg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 8
+       },
+
+       { .name = "window-status-current-fg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 8
+       },
+
+       { .name = "window-status-current-format",
+         .type = OPTIONS_TABLE_STRING,
+         .default_str = "#I:#W#F"
+       },
+
+       { .name = "window-status-fg",
+         .type = OPTIONS_TABLE_COLOUR,
+         .default_num = 8
+       },
+
+       { .name = "window-status-format",
+         .type = OPTIONS_TABLE_STRING,
+         .default_str = "#I:#W#F"
+       },
+
+       { .name = "word-separators",
+         .type = OPTIONS_TABLE_STRING,
+         .default_str = " -_@"
+       },
+
+       { .name = "xterm-keys",
+         .type = OPTIONS_TABLE_FLAG,
+         .default_num = 0
+       },
+
+       { .name = NULL }
+};
+
+/* Populate an options tree from a table. */
+void
+options_table_populate_tree(
+    const struct options_table_entry *table, struct options *oo)
+{
+       const struct options_table_entry        *oe;
+
+       for (oe = table; oe->name != NULL; oe++) {
+               if (oe->default_str != NULL)
+                       options_set_string(oo, oe->name, "%s", oe->default_str);
+               else
+                       options_set_number(oo, oe->name, oe->default_num);
+       }
+}
+
+/* Print an option using its type from the table. */
+const char *
+options_table_print_entry(
+    const struct options_table_entry *oe, struct options_entry *o)
+{
+       static char                              out[BUFSIZ];
+       const char                              *s;
+       struct keylist                          *keylist;
+       u_int                                    i;
+
+       *out = '\0';
+       switch (oe->type) {
+       case OPTIONS_TABLE_STRING:
+               xsnprintf(out, sizeof out, "\"%s\"", o->str);
+               break;
+       case OPTIONS_TABLE_NUMBER:
+               xsnprintf(out, sizeof out, "%lld", o->num);
+               break;
+       case OPTIONS_TABLE_KEYS:
+               keylist = o->data;
+               for (i = 0; i < ARRAY_LENGTH(keylist); i++) {
+                       s = key_string_lookup_key(ARRAY_ITEM(keylist, i));
+                       strlcat(out, s, sizeof out);
+                       if (i != ARRAY_LENGTH(keylist) - 1)
+                               strlcat(out, ",", sizeof out);
+               }
+               break;
+       case OPTIONS_TABLE_COLOUR:
+               s = colour_tostring(o->num);
+               xsnprintf(out, sizeof out, "%s", s);
+               break;
+       case OPTIONS_TABLE_ATTRIBUTES:
+               s = attributes_tostring(o->num);
+               xsnprintf(out, sizeof out, "%s", s);
+               break;
+       case OPTIONS_TABLE_FLAG:
+               if (o->num)
+                       strlcpy(out, "on", sizeof out);
+               else
+                       strlcpy(out, "off", sizeof out);
+               break;
+       case OPTIONS_TABLE_CHOICE:
+               s = oe->choices[o->num];
+               xsnprintf(out, sizeof out, "%s", s);
+               break;
+       }
+       return (out);
+}
diff --git a/external/bsd/tmux/dist/options.c b/external/bsd/tmux/dist/options.c
new file mode 100644 (file)
index 0000000..ae8662d
--- /dev/null
@@ -0,0 +1,201 @@
+/* $Id: options.c,v 1.3 2011/08/17 18:48:36 jmmv 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->str);
+               else if (o->type == OPTIONS_DATA)
+                       o->freefn(o->data);
+               xfree(o);
+       }
+}
+
+struct options_entry *
+options_find1(struct options *oo, const char *name)
+{
+       struct options_entry    p;
+
+       p.name = __UNCONST(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 = __UNCONST(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);
+}
+
+void
+options_remove(struct options *oo, const char *name)
+{
+       struct options_entry    *o;
+
+       if ((o = options_find1(oo, name)) == NULL)
+               return;
+
+       SPLAY_REMOVE(options_tree, &oo->tree, o);
+       xfree(o->name);
+       if (o->type == OPTIONS_STRING)
+               xfree(o->str);
+       else if (o->type == OPTIONS_DATA)
+               o->freefn(o->data);
+       xfree(o);
+}
+
+struct options_entry *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->str);
+       else if (o->type == OPTIONS_DATA)
+               o->freefn(o->data);
+
+       va_start(ap, fmt);
+       o->type = OPTIONS_STRING;
+       xvasprintf(&o->str, fmt, ap);
+       va_end(ap);
+       return (o);
+}
+
+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->str);
+}
+
+struct options_entry *
+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->str);
+       else if (o->type == OPTIONS_DATA)
+               o->freefn(o->data);
+
+       o->type = OPTIONS_NUMBER;
+       o->num = value;
+       return (o);
+}
+
+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->num);
+}
+
+struct options_entry *
+options_set_data(
+    struct options *oo, const char *name, void *value, void (*freefn)(void *))
+{
+       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->str);
+       else if (o->type == OPTIONS_DATA)
+               o->freefn(o->data);
+
+       o->type = OPTIONS_DATA;
+       o->data = value;
+       o->freefn = freefn;
+       return (o);
+}
+
+void *
+options_get_data(struct options *oo, const char *name)
+{
+       struct options_entry    *o;
+
+       if ((o = options_find(oo, name)) == NULL)
+               fatalx("missing option");
+       if (o->type != OPTIONS_DATA)
+               fatalx("option not data");
+       return (o->data);
+}
diff --git a/external/bsd/tmux/dist/osdep-aix.c b/external/bsd/tmux/dist/osdep-aix.c
new file mode 100644 (file)
index 0000000..039733f
--- /dev/null
@@ -0,0 +1,35 @@
+/* $Id: osdep-aix.c,v 1.1.1.1 2011/08/17 18:40:05 jmmv Exp $ */
+
+/*
+ * Copyright (c) 2011 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 <event.h>
+
+#include "tmux.h"
+
+char *
+osdep_get_name(unused int fd, unused char *tty)
+{
+       return (NULL);
+}
+
+struct event_base *
+osdep_event_init(void)
+{
+       return (event_init());
+}
diff --git a/external/bsd/tmux/dist/osdep-darwin.c b/external/bsd/tmux/dist/osdep-darwin.c
new file mode 100644 (file)
index 0000000..d622776
--- /dev/null
@@ -0,0 +1,61 @@
+/* $Id: osdep-darwin.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv 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 <sys/sysctl.h>
+
+#include <event.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+char                   *osdep_get_name(int, char *);
+struct event_base      *osdep_event_init(void);
+
+#define unused __attribute__ ((unused))
+
+char *
+osdep_get_name(int fd, unused char *tty)
+{
+       int     mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 };
+       size_t  size;
+       struct kinfo_proc kp;
+
+       if ((mib[3] = tcgetpgrp(fd)) == -1)
+               return (NULL);
+
+       size = sizeof kp;
+       if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1)
+               return (NULL);
+       if (*kp.kp_proc.p_comm == '\0')
+               return (NULL);
+
+       return (strdup(kp.kp_proc.p_comm));
+}
+
+struct event_base *
+osdep_event_init(void)
+{
+       /*
+        * On OS X, kqueue and poll are both completely broken and don't
+        * work on anything except socket file descriptors (yes, really).
+        */
+       setenv("EVENT_NOKQUEUE", "1", 1);
+       setenv("EVENT_NOPOLL", "1", 1);
+       return (event_init());
+}
diff --git a/external/bsd/tmux/dist/osdep-freebsd.c b/external/bsd/tmux/dist/osdep-freebsd.c
new file mode 100644 (file)
index 0000000..2462452
--- /dev/null
@@ -0,0 +1,142 @@
+/* $Id: osdep-freebsd.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv 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/proc.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct kinfo_proc      *cmp_procs(struct kinfo_proc *, struct kinfo_proc *);
+char                   *osdep_get_name(int, char *);
+struct event_base      *osdep_event_init(void);
+
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+#define is_runnable(p) \
+       ((p)->ki_stat == SRUN || (p)->ki_stat == SIDL)
+#define is_stopped(p) \
+       ((p)->ki_stat == SSTOP || (p)->ki_stat == SZOMB)
+
+struct kinfo_proc *
+cmp_procs(struct kinfo_proc *p1, struct kinfo_proc *p2)
+{
+       if (is_runnable(p1) && !is_runnable(p2))
+               return (p1);
+       if (!is_runnable(p1) && is_runnable(p2))
+               return (p2);
+
+       if (is_stopped(p1) && !is_stopped(p2))
+               return (p1);
+       if (!is_stopped(p1) && is_stopped(p2))
+               return (p2);
+
+       if (p1->ki_estcpu > p2->ki_estcpu)
+               return (p1);
+       if (p1->ki_estcpu < p2->ki_estcpu)
+               return (p2);
+
+       if (p1->ki_slptime < p2->ki_slptime)
+               return (p1);
+       if (p1->ki_slptime > p2->ki_slptime)
+               return (p2);
+
+       if (strcmp(p1->ki_comm, p2->ki_comm) < 0)
+               return (p1);
+       if (strcmp(p1->ki_comm, p2->ki_comm) > 0)
+               return (p2);
+
+       if (p1->ki_pid > p2->ki_pid)
+               return (p1);
+       return (p2);
+}
+
+char *
+osdep_get_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, *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)
+               goto error;
+       buf = newbuf;
+
+       if (sysctl(mib, nitems(mib), buf, &len, NULL, 0) == -1) {
+               if (errno == ENOMEM)
+                       goto retry;
+               goto error;
+       }
+
+       bestp = NULL;
+       for (i = 0; i < len / sizeof (struct kinfo_proc); i++) {
+               if (buf[i].ki_tdev != sb.st_rdev)
+                       continue;
+               if (bestp == NULL)
+                       bestp = &buf[i];
+               else
+                       bestp = cmp_procs(&buf[i], bestp);
+       }
+
+       name = NULL;
+       if (bestp != NULL)
+               name = strdup(bestp->ki_comm);
+
+       free(buf);
+       return (name);
+
+error:
+       free(buf);
+       return (NULL);
+}
+
+struct event_base *
+osdep_event_init(void)
+{
+       /*
+        * On some versions of FreeBSD, kqueue doesn't work properly on tty
+        * file descriptors. This is fixed in recent FreeBSD versions.
+        */
+       setenv("EVENT_NOKQUEUE", "1", 1);
+       return (event_init());
+}
diff --git a/external/bsd/tmux/dist/osdep-hpux.c b/external/bsd/tmux/dist/osdep-hpux.c
new file mode 100644 (file)
index 0000000..03bc979
--- /dev/null
@@ -0,0 +1,35 @@
+/* $Id: osdep-hpux.c,v 1.1.1.1 2011/08/17 18:40:06 jmmv 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 <event.h>
+
+#include "tmux.h"
+
+char *
+osdep_get_name(unused int fd, unused char *tty)
+{
+       return (NULL);
+}
+
+struct event_base *
+osdep_event_init(void)
+{
+       return (event_init());
+}
diff --git a/external/bsd/tmux/dist/osdep-linux.c b/external/bsd/tmux/dist/osdep-linux.c
new file mode 100644 (file)
index 0000000..9821000
--- /dev/null
@@ -0,0 +1,76 @@
+/* $Id: osdep-linux.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv 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 <sys/stat.h>
+
+#include <event.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+char *
+osdep_get_name(int fd, unused char *tty)
+{
+       FILE    *f;
+       char    *path, *buf;
+       size_t   len;
+       int      ch;
+       pid_t    pgrp;
+
+       if ((pgrp = tcgetpgrp(fd)) == -1)
+               return (NULL);
+
+       xasprintf(&path, "/proc/%lld/cmdline", (long long) pgrp);
+       if ((f = fopen(path, "r")) == NULL) {
+               xfree(path);
+               return (NULL);
+       }
+       xfree(path);
+
+       len = 0;
+       buf = NULL;
+       while ((ch = fgetc(f)) != EOF) {
+               if (ch == '\0')
+                       break;
+               buf = xrealloc(buf, 1, len + 2);
+               buf[len++] = ch;
+       }
+       if (buf != NULL)
+               buf[len] = '\0';
+
+       fclose(f);
+       return (buf);
+}
+
+struct event_base *
+osdep_event_init(void)
+{
+       /*
+        * On Linux, epoll doesn't work on /dev/null (yes, really).
+        *
+        * This has been commented because libevent versions up until the very
+        * latest (1.4 git or 2.0.10) do not handle signals properly when using
+        * poll or select, causing hangs.
+        * 
+        */
+       /* setenv("EVENT_NOEPOLL", "1", 1); */
+       return (event_init());
+}
diff --git a/external/bsd/tmux/dist/osdep-netbsd.c b/external/bsd/tmux/dist/osdep-netbsd.c
new file mode 100644 (file)
index 0000000..161bb6b
--- /dev/null
@@ -0,0 +1,130 @@
+/* $Id: osdep-netbsd.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv 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/proc.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#include <errno.h>
+#include <event.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define is_runnable(p) \
+        ((p)->p_stat == LSRUN || (p)->p_stat == SIDL)
+#define is_stopped(p) \
+        ((p)->p_stat == SSTOP || (p)->p_stat == SZOMB)
+
+struct kinfo_proc2     *cmp_procs(struct kinfo_proc2 *, struct kinfo_proc2 *);
+char                   *osdep_get_name(int, char *);
+struct event_base      *osdep_event_init(void);
+
+struct kinfo_proc2 *
+cmp_procs(struct kinfo_proc2 *p1, struct kinfo_proc2 *p2)
+{
+       if (is_runnable(p1) && !is_runnable(p2))
+               return (p1);
+       if (!is_runnable(p1) && is_runnable(p2))
+               return (p2);
+
+       if (is_stopped(p1) && !is_stopped(p2))
+               return (p1);
+       if (!is_stopped(p1) && is_stopped(p2))
+               return (p2);
+
+       if (p1->p_estcpu > p2->p_estcpu)
+               return (p1);
+       if (p1->p_estcpu < p2->p_estcpu)
+               return (p2);
+
+       if (p1->p_slptime < p2->p_slptime)
+               return (p1);
+       if (p1->p_slptime > p2->p_slptime)
+               return (p2);
+
+       if (p1->p_pid > p2->p_pid)
+               return (p1);
+       return (p2);
+}
+
+char *
+osdep_get_name(int fd, __unused char *tty)
+{
+       int              mib[6];
+       struct stat      sb;
+       size_t           len, i;
+       struct kinfo_proc2 *buf, *newbuf, *bestp;
+       char            *name;
+
+       if (stat(tty, &sb) == -1)
+               return (NULL);
+       if ((mib[3] = tcgetpgrp(fd)) == -1)
+               return (NULL);
+
+       buf = NULL;
+       len = sizeof(bestp);
+       mib[0] = CTL_KERN;
+       mib[1] = KERN_PROC2;
+       mib[2] = KERN_PROC_PGRP;
+       mib[4] = sizeof (*buf);
+       mib[5] = 0;
+
+retry:
+       if (sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0) == -1)
+               return (NULL);
+
+       if ((newbuf = realloc(buf, len * sizeof (*buf))) == NULL)
+               goto error;
+       buf = newbuf;
+
+       mib[5] = len / sizeof(*buf);
+       if (sysctl(mib, __arraycount(mib), buf, &len, NULL, 0) == -1) {
+               if (errno == ENOMEM)
+                       goto retry; /* possible infinite loop? */
+               goto error;
+       }
+
+       bestp = NULL;
+       for (i = 0; i < len / sizeof (*buf); i++) {
+               if (buf[i].p_tdev != sb.st_rdev)
+                       continue;
+               if (bestp == NULL)
+                       bestp = &buf[i];
+               else
+                       bestp = cmp_procs(&buf[i], bestp);
+       }
+
+       name = NULL;
+       if (bestp != NULL)
+               name = strdup(bestp->p_comm);
+
+       free(buf);
+       return (name);
+
+error:
+       free(buf);
+       return (NULL);
+}
+
+struct event_base *
+osdep_event_init(void)
+{
+       return (event_init());
+}
diff --git a/external/bsd/tmux/dist/osdep-openbsd.c b/external/bsd/tmux/dist/osdep-openbsd.c
new file mode 100644 (file)
index 0000000..c94da31
--- /dev/null
@@ -0,0 +1,140 @@
+/* $Id: osdep-openbsd.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv 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 <event.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+#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)
+
+struct kinfo_proc      *cmp_procs(struct kinfo_proc *, struct kinfo_proc *);
+char                   *osdep_get_name(int, char *);
+struct event_base      *osdep_event_init(void);
+
+struct kinfo_proc *
+cmp_procs(struct kinfo_proc *p1, struct kinfo_proc *p2)
+{
+       if (is_runnable(p1) && !is_runnable(p2))
+               return (p1);
+       if (!is_runnable(p1) && is_runnable(p2))
+               return (p2);
+
+       if (is_stopped(p1) && !is_stopped(p2))
+               return (p1);
+       if (!is_stopped(p1) && is_stopped(p2))
+               return (p2);
+
+       if (p1->p_estcpu > p2->p_estcpu)
+               return (p1);
+       if (p1->p_estcpu < p2->p_estcpu)
+               return (p2);
+
+       if (p1->p_slptime < p2->p_slptime)
+               return (p1);
+       if (p1->p_slptime > p2->p_slptime)
+               return (p2);
+
+       if ((p1->p_flag & P_SINTR) && !(p2->p_flag & P_SINTR))
+               return (p1);
+       if (!(p1->p_flag & P_SINTR) && (p2->p_flag & P_SINTR))
+               return (p2);
+
+       if (strcmp(p1->p_comm, p2->p_comm) < 0)
+               return (p1);
+       if (strcmp(p1->p_comm, p2->p_comm) > 0)
+               return (p2);
+
+       if (p1->p_pid > p2->p_pid)
+               return (p1);
+       return (p2);
+}
+
+char *
+osdep_get_name(int fd, char *tty)
+{
+       int              mib[6] = { CTL_KERN, KERN_PROC, KERN_PROC_PGRP, 0,
+                                   sizeof(struct kinfo_proc), 0 };
+       struct stat      sb;
+       size_t           len;
+       struct kinfo_proc *buf, *newbuf, *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)
+               goto error;
+       buf = newbuf;
+
+       mib[5] = (int)(len / sizeof(struct kinfo_proc));
+       if (sysctl(mib, nitems(mib), buf, &len, NULL, 0) == -1) {
+               if (errno == ENOMEM)
+                       goto retry;
+               goto error;
+       }
+
+       bestp = NULL;
+       for (i = 0; i < len / sizeof (struct kinfo_proc); i++) {
+               if ((dev_t)buf[i].p_tdev != sb.st_rdev)
+                       continue;
+               if (bestp == NULL)
+                       bestp = &buf[i];
+               else
+                       bestp = cmp_procs(&buf[i], bestp);
+       }
+
+       name = NULL;
+       if (bestp != NULL)
+               name = strdup(bestp->p_comm);
+
+       free(buf);
+       return (name);
+
+error:
+       free(buf);
+       return (NULL);
+}
+
+struct event_base *
+osdep_event_init(void)
+{
+       return (event_init());
+}
diff --git a/external/bsd/tmux/dist/osdep-sunos.c b/external/bsd/tmux/dist/osdep-sunos.c
new file mode 100644 (file)
index 0000000..298e911
--- /dev/null
@@ -0,0 +1,72 @@
+/* $Id: osdep-sunos.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ */
+
+/*
+ * Copyright (c) 2009 Todd Carson <toc@daybefore.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 <event.h>
+#include <fcntl.h>
+#include <procfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+char *
+osdep_get_name(int fd, char *tty)
+{
+       struct psinfo    p;
+       struct stat      st;
+       char            *path;
+       ssize_t          bytes;
+       int              f;
+       pid_t            pgrp;
+
+       if ((f = open(tty, O_RDONLY)) < 0)
+               return (NULL);
+
+       if ((fstat(f, &st) != 0) ||
+           (ioctl(f, TIOCGPGRP, &pgrp) != 0)) {
+               close(f);
+               return (NULL);
+       }
+       close(f);
+
+       xasprintf(&path, "/proc/%hu/psinfo", pgrp);
+       f = open(path, O_RDONLY);
+       xfree(path);
+       if (f < 0)
+               return (NULL);
+
+       bytes = read(f, &p, sizeof(p));
+       close(f);
+       if (bytes != sizeof(p))
+               return (NULL);
+
+       if (p.pr_ttydev != st.st_rdev)
+               return (NULL);
+
+       return (xstrdup(p.pr_fname));
+}
+
+struct event_base *
+osdep_event_init(void)
+{
+       return (event_init());
+}
diff --git a/external/bsd/tmux/dist/osdep-unknown.c b/external/bsd/tmux/dist/osdep-unknown.c
new file mode 100644 (file)
index 0000000..9903ee6
--- /dev/null
@@ -0,0 +1,35 @@
+/* $Id: osdep-unknown.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv 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 <event.h>
+
+#include "tmux.h"
+
+char *
+osdep_get_name(unused int fd, unused char *tty)
+{
+       return (NULL);
+}
+
+struct event_base *
+osdep_event_init(void)
+{
+       return (event_init());
+}
diff --git a/external/bsd/tmux/dist/paste.c b/external/bsd/tmux/dist/paste.c
new file mode 100644 (file)
index 0000000..8f3d635
--- /dev/null
@@ -0,0 +1,169 @@
+/* $Id: paste.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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"
+
+/*
+ * Stack of paste buffers. Note that paste buffer data is not necessarily a C
+ * string!
+ */
+
+/* Return each item of the stack in turn. */
+struct paste_buffer *
+paste_walk_stack(struct paste_stack *ps, u_int *idx)
+{
+       struct paste_buffer     *pb;
+
+       pb = paste_get_index(ps, *idx);
+       (*idx)++;
+       return (pb);
+}
+
+/* Get the top item on the stack. */
+struct paste_buffer *
+paste_get_top(struct paste_stack *ps)
+{
+       if (ARRAY_LENGTH(ps) == 0)
+               return (NULL);
+       return (ARRAY_FIRST(ps));
+}
+
+/* Get an item by its index. */
+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));
+}
+
+/* Free the top item on the stack. */
+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);
+}
+
+/* Free an item by index. */
+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);
+}
+
+/*
+ * Add an item onto the top of the stack, freeing the bottom if at limit. Note
+ * that the caller is responsible for allocating data.
+ */
+void
+paste_add(struct paste_stack *ps, char *data, size_t size, u_int limit)
+{
+       struct paste_buffer     *pb;
+
+       if (size == 0)
+               return;
+
+       while (ARRAY_LENGTH(ps) >= limit) {
+               pb = ARRAY_LAST(ps);
+               xfree(pb->data);
+               xfree(pb);
+               ARRAY_TRUNC(ps, 1);
+       }
+
+       pb = xmalloc(sizeof *pb);
+       ARRAY_INSERT(ps, 0, pb);
+
+       pb->data = data;
+       pb->size = size;
+}
+
+
+/*
+ * Replace an item on the stack. Note that the caller is responsible for
+ * allocating data.
+ */
+int
+paste_replace(struct paste_stack *ps, u_int idx, char *data, size_t size)
+{
+       struct paste_buffer     *pb;
+
+       if (size == 0)
+               return (0);
+
+       if (idx >= ARRAY_LENGTH(ps))
+               return (-1);
+
+       pb = ARRAY_ITEM(ps, idx);
+       xfree(pb->data);
+
+       pb->data = data;
+       pb->size = size;
+
+       return (0);
+}
+
+/* Convert a buffer into a visible string. */
+char *
+paste_print(struct paste_buffer *pb, size_t width)
+{
+       char    *buf;
+       size_t   len, used;
+
+       if (width < 3)
+               width = 3;
+       buf = xmalloc(width * 4 + 1);
+
+       len = pb->size;
+       if (len > width)
+               len = width;
+
+       used = strvisx(buf, pb->data, len, VIS_OCTAL|VIS_TAB|VIS_NL);
+       if (pb->size > width || used > width) {
+               buf[width - 3] = '\0';
+               strlcat(buf, "...", width);
+       }
+
+       return (buf);
+}
diff --git a/external/bsd/tmux/dist/resize.c b/external/bsd/tmux/dist/resize.c
new file mode 100644 (file)
index 0000000..b213047
--- /dev/null
@@ -0,0 +1,145 @@
+/* $Id: resize.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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;
+       struct window_pane      *wp;
+       u_int                    i, j, ssx, ssy, has, limit;
+       int                      flag;
+
+       RB_FOREACH(s, sessions, &sessions) {
+               ssx = ssy = UINT_MAX;
+               for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
+                       c = ARRAY_ITEM(&clients, j);
+                       if (c == NULL || c->flags & CLIENT_SUSPENDED)
+                               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;
+               RB_FOREACH(s, sessions, &sessions) {
+                       if (s->flags & SESSION_UNATTACHED)
+                               continue;
+                       if (flag)
+                               has = s->curw->window == w;
+                       else
+                               has = session_has(s, w) != NULL;
+                       if (has) {
+                               if (s->sx < ssx)
+                                       ssx = s->sx;
+                               if (s->sy < ssy)
+                                       ssy = s->sy;
+                       }
+               }
+               if (ssx == UINT_MAX || ssy == UINT_MAX)
+                       continue;
+
+               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);
+
+               layout_resize(w, ssx, ssy);
+               window_resize(w, ssx, ssy);
+
+               /*
+                * If the current pane is now not visible, move to the next
+                * that is.
+                */
+               wp = w->active;
+               while (!window_pane_visible(w->active)) {
+                       w->active = TAILQ_PREV(w->active, window_panes, entry);
+                       if (w->active == NULL)
+                               w->active = TAILQ_LAST(&w->panes, window_panes);
+                       if (w->active == wp)
+                              break;
+               }
+
+               server_redraw_window(w);
+       }
+}
diff --git a/external/bsd/tmux/dist/screen-redraw.c b/external/bsd/tmux/dist/screen-redraw.c
new file mode 100644 (file)
index 0000000..1feef3b
--- /dev/null
@@ -0,0 +1,323 @@
+/* $Id: screen-redraw.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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_cell_border1(struct window_pane *, u_int, u_int);
+int    screen_redraw_cell_border(struct client *, u_int, u_int);
+int    screen_redraw_check_cell(struct client *, u_int, u_int);
+void   screen_redraw_draw_number(struct client *, struct window_pane *);
+
+#define CELL_INSIDE 0
+#define CELL_LEFTRIGHT 1
+#define CELL_TOPBOTTOM 2
+#define CELL_TOPLEFT 3
+#define CELL_TOPRIGHT 4
+#define CELL_BOTTOMLEFT 5
+#define CELL_BOTTOMRIGHT 6
+#define CELL_TOPJOIN 7
+#define CELL_BOTTOMJOIN 8
+#define CELL_LEFTJOIN 9
+#define CELL_RIGHTJOIN 10
+#define CELL_JOIN 11
+#define CELL_OUTSIDE 12
+
+#define CELL_BORDERS " xqlkmjwvtun~"
+
+/* Check if cell is on the border of a particular pane. */
+int
+screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py)
+{
+       /* Inside pane. */
+       if (px >= wp->xoff && px < wp->xoff + wp->sx &&
+           py >= wp->yoff && py < wp->yoff + wp->sy)
+               return (0);
+
+       /* Left/right borders. */
+       if ((wp->yoff == 0 || py >= wp->yoff - 1) && 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 ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= wp->xoff + wp->sx) {
+               if (wp->yoff != 0 && py == wp->yoff - 1)
+                       return (1);
+               if (py == wp->yoff + wp->sy)
+                       return (1);
+       }
+
+       /* Outside pane. */
+       return (-1);
+}
+
+/* Check if a cell is on the pane border. */
+int
+screen_redraw_cell_border(struct client *c, u_int px, u_int py)
+{
+       struct window           *w = c->session->curw->window;
+       struct window_pane      *wp;
+       int                      retval;
+
+       /* Check all the panes. */
+       TAILQ_FOREACH(wp, &w->panes, entry) {
+               if (!window_pane_visible(wp))
+                       continue;
+               if ((retval = screen_redraw_cell_border1(wp, px, py)) != -1)
+                       return (retval);
+       }
+
+       return (0);
+}
+
+/* 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;
+       int                      borders;
+
+       if (px > w->sx || py > w->sy)
+               return (CELL_OUTSIDE);
+
+       TAILQ_FOREACH(wp, &w->panes, entry) {
+               if (!window_pane_visible(wp))
+                       continue;
+
+               /* If outside the pane and its border, skip it. */
+               if ((wp->xoff != 0 && px < wp->xoff - 1) ||
+                   px > wp->xoff + wp->sx ||
+                   (wp->yoff != 0 && py < wp->yoff - 1) ||
+                   py > wp->yoff + wp->sy)
+                       continue;
+
+               /* If definitely inside, return so. */
+               if (!screen_redraw_cell_border(c, px, py))
+                       return (CELL_INSIDE);
+
+               /*
+                * Construct a bitmask of whether the cells to the left (bit
+                * 4), right, top, and bottom (bit 1) of this cell are borders.
+                */
+               borders = 0;
+               if (px == 0 || screen_redraw_cell_border(c, px - 1, py))
+                       borders |= 8;
+               if (px <= w->sx && screen_redraw_cell_border(c, px + 1, py))
+                       borders |= 4;
+               if (py == 0 || screen_redraw_cell_border(c, px, py - 1))
+                       borders |= 2;
+               if (py <= w->sy && screen_redraw_cell_border(c, px, py + 1))
+                       borders |= 1;
+
+               /*
+                * Figure out what kind of border this cell is. Only one bit
+                * set doesn't make sense (can't have a border cell with no
+                * others connected).
+                */
+               switch (borders) {
+               case 15:        /* 1111, left right top bottom */
+                       return (CELL_JOIN);
+               case 14:        /* 1110, left right top */
+                       return (CELL_BOTTOMJOIN);
+               case 13:        /* 1101, left right bottom */
+                       return (CELL_TOPJOIN);
+               case 12:        /* 1100, left right */
+                       return (CELL_TOPBOTTOM);
+               case 11:        /* 1011, left top bottom */
+                       return (CELL_RIGHTJOIN);
+               case 10:        /* 1010, left top */
+                       return (CELL_BOTTOMRIGHT);
+               case 9:         /* 1001, left bottom */
+                       return (CELL_TOPRIGHT);
+               case 7:         /* 0111, right top bottom */
+                       return (CELL_LEFTJOIN);
+               case 6:         /* 0110, right top */
+                       return (CELL_BOTTOMLEFT);
+               case 5:         /* 0101, right bottom */
+                       return (CELL_TOPLEFT);
+               case 3:         /* 0011, top bottom */
+                       return (CELL_LEFTRIGHT);
+               }
+       }
+
+       return (CELL_OUTSIDE);
+}
+
+/* Redraw entire screen. */
+void
+screen_redraw_screen(struct client *c, int status_only, int borders_only)
+{
+       struct window           *w = c->session->curw->window;
+       struct tty              *tty = &c->tty;
+       struct window_pane      *wp;
+       struct grid_cell         active_gc, other_gc;
+       u_int                    i, j, type;
+       int                      status, fg, bg;
+
+       /* Suspended clients should not be updated. */
+       if (c->flags & CLIENT_SUSPENDED)
+               return;
+
+       /* Get status line, er, status. */
+       if (c->message_string != NULL || c->prompt_string != NULL)
+               status = 1;
+       else
+               status = options_get_number(&c->session->options, "status");
+
+       /* If only drawing status and it is present, don't need the rest. */
+       if (status_only && status) {
+               tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1);
+               tty_reset(tty);
+               return;
+       }
+
+       /* Set up pane border attributes. */
+       memcpy(&other_gc, &grid_default_cell, sizeof other_gc);
+       memcpy(&active_gc, &grid_default_cell, sizeof active_gc);
+       active_gc.data = other_gc.data = 'x'; /* not space */
+       active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET;
+       fg = options_get_number(&c->session->options, "pane-border-fg");
+       colour_set_fg(&other_gc, fg);
+       bg = options_get_number(&c->session->options, "pane-border-bg");
+       colour_set_bg(&other_gc, bg);
+       fg = options_get_number(&c->session->options, "pane-active-border-fg");
+       colour_set_fg(&active_gc, fg);
+       bg = options_get_number(&c->session->options, "pane-active-border-bg");
+       colour_set_bg(&active_gc, bg);
+
+       /* Draw background and borders. */
+       for (j = 0; j < tty->sy - status; j++) {
+               if (status_only && j != tty->sy - 1)
+                       continue;
+               for (i = 0; i < tty->sx; i++) {
+                       type = screen_redraw_check_cell(c, i, j);
+                       if (type == CELL_INSIDE)
+                               continue;
+                       if (screen_redraw_cell_border1(w->active, i, j) == 1)
+                               tty_attributes(tty, &active_gc);
+                       else
+                               tty_attributes(tty, &other_gc);
+                       tty_cursor(tty, i, j);
+                       tty_putc(tty, CELL_BORDERS[type]);
+               }
+       }
+
+       /* If only drawing borders, that's it. */
+       if (borders_only)
+               return;
+
+       /* Draw the panes, if necessary. */
+       TAILQ_FOREACH(wp, &w->panes, entry) {
+               if (!window_pane_visible(wp))
+                       continue;
+               for (i = 0; i < wp->sy; i++) {
+                       if (status_only && wp->yoff + i != tty->sy - 1)
+                               continue;
+                       tty_draw_line(tty, wp->screen, i, wp->xoff, wp->yoff);
+               }
+               if (c->flags & CLIENT_IDENTIFY)
+                       screen_redraw_draw_number(c, wp);
+       }
+
+       /* Draw the status line. */
+       if (status)
+               tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1);
+       tty_reset(tty);
+}
+
+/* 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);
+       tty_reset(&c->tty);
+}
+
+/* Draw number on a pane. */
+void
+screen_redraw_draw_number(struct client *c, struct window_pane *wp)
+{
+       struct tty              *tty = &c->tty;
+       struct session          *s = c->session;
+       struct options          *oo = &s->options;
+       struct window           *w = wp->window;
+       struct grid_cell         gc;
+       u_int                    idx, px, py, i, j, xoff, yoff;
+       int                      colour, active_colour;
+       char                     buf[16], *ptr;
+       size_t                   len;
+
+       idx = window_pane_index(w, wp);
+       len = xsnprintf(buf, sizeof buf, "%u", idx);
+
+       if (wp->sx < len)
+               return;
+       colour = options_get_number(oo, "display-panes-colour");
+       active_colour = options_get_number(oo, "display-panes-active-colour");
+
+       px = wp->sx / 2; py = wp->sy / 2;
+       xoff = wp->xoff; yoff = wp->yoff;
+
+       if (wp->sx < len * 6 || wp->sy < 5) {
+               tty_cursor(tty, xoff + px - len / 2, yoff + py);
+               memcpy(&gc, &grid_default_cell, sizeof gc);
+               gc.data = '_'; /* not space */
+               if (w->active == wp)
+                       colour_set_fg(&gc, active_colour);
+               else
+                       colour_set_fg(&gc, colour);
+               tty_attributes(tty, &gc);
+               tty_puts(tty, buf);
+               return;
+       }
+
+       px -= len * 3;
+       py -= 2;
+
+       memcpy(&gc, &grid_default_cell, sizeof gc);
+       gc.data = '_'; /* not space */
+       if (w->active == wp)
+               colour_set_bg(&gc, active_colour);
+       else
+               colour_set_bg(&gc, colour);
+       tty_attributes(tty, &gc);
+       for (ptr = buf; *ptr != '\0'; ptr++) {
+               if (*ptr < '0' || *ptr > '9')
+                       continue;
+               idx = *ptr - '0';
+
+               for (j = 0; j < 5; j++) {
+                       for (i = px; i < px + 5; i++) {
+                               tty_cursor(tty, xoff + i, yoff + py + j);
+                               if (clock_table[idx][j][i - px])
+                                       tty_putc(tty, ' ');
+                       }
+               }
+               px += 6;
+       }
+}
diff --git a/external/bsd/tmux/dist/screen-write.c b/external/bsd/tmux/dist/screen-write.c
new file mode 100644 (file)
index 0000000..2fdc361
--- /dev/null
@@ -0,0 +1,1220 @@
+/* $Id: screen-write.c,v 1.3 2011/08/17 18:48:36 jmmv 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_initctx(struct screen_write_ctx *, struct tty_ctx *, int);
+void   screen_write_overwrite(struct screen_write_ctx *, u_int);
+int    screen_write_combine(
+           struct screen_write_ctx *, const struct utf8_data *);
+
+/* 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. */
+/* ARGSUSED */
+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);
+}
+
+/* Calculate string length, with embedded formatting. */
+size_t printflike2
+screen_write_cstrlen(int utf8flag, const char *fmt, ...)
+{
+       va_list ap;
+       char   *msg, *msg2, *ptr, *ptr2;
+       size_t  size;
+
+       va_start(ap, fmt);
+       xvasprintf(&msg, fmt, ap);
+       va_end(ap);
+       msg2 = xmalloc(strlen(msg) + 1);
+
+       ptr = msg;
+       ptr2 = msg2;
+       while (*ptr != '\0') {
+               if (ptr[0] == '#' && ptr[1] == '[') {
+                       while (*ptr != ']' && *ptr != '\0')
+                               ptr++;
+                       if (*ptr == ']')
+                               ptr++;
+                       continue;
+               }
+               *ptr2++ = *ptr++;
+       }
+       *ptr2 = '\0';
+
+       size = screen_write_strlen(utf8flag, "%s", msg2);
+
+       xfree(msg);
+       xfree(msg2);
+
+       return (size);
+}
+
+/* Calculate string length. */
+size_t printflike2
+screen_write_strlen(int utf8flag, const char *fmt, ...)
+{
+       va_list                 ap;
+       char                   *msg;
+       struct utf8_data        utf8data;
+       char                   *ptr;
+       size_t                  left, size = 0;
+
+       va_start(ap, fmt);
+       xvasprintf(&msg, fmt, ap);
+       va_end(ap);
+
+       ptr = msg;
+       while (*ptr != '\0') {
+               if (utf8flag && *ptr & 0x80 && utf8_open(&utf8data, (u_char)*ptr)) {
+                       ptr++;
+
+                       left = strlen(ptr);
+                       if (left < utf8data.size - 1)
+                               break;
+                       while (utf8_append(&utf8data, (u_char)*ptr))
+                               ptr++;
+                       ptr++;
+
+                       size += utf8data.width;
+               } else {
+                       size++;
+                       ptr++;
+               }
+       }
+
+       xfree(msg);
+       return (size);
+}
+
+/* Write simple string (no UTF-8 or maximum length). */
+void printflike3
+screen_write_puts(
+    struct screen_write_ctx *ctx, struct grid_cell *gc, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       screen_write_vnputs(ctx, -1, gc, 0, fmt, ap);
+       va_end(ap);
+}
+
+/* Write string with length limit (-1 for unlimited). */
+void printflike5
+screen_write_nputs(struct screen_write_ctx *ctx,
+    ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       screen_write_vnputs(ctx, maxlen, gc, utf8flag, fmt, ap);
+       va_end(ap);
+}
+
+void
+screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
+    struct grid_cell *gc, int utf8flag, const char *fmt, va_list ap)
+{
+       char                   *msg;
+       struct utf8_data        utf8data;
+       char                   *ptr;
+       size_t                  left, size = 0;
+
+       xvasprintf(&msg, fmt, ap);
+
+       ptr = msg;
+       while (*ptr != '\0') {
+               if (utf8flag && *ptr & 0x80 && utf8_open(&utf8data, (u_char)*ptr)) {
+                       ptr++;
+
+                       left = strlen(ptr);
+                       if (left < utf8data.size - 1)
+                               break;
+                       while (utf8_append(&utf8data, (u_char)*ptr))
+                               ptr++;
+                       ptr++;
+
+                       if (maxlen > 0 &&
+                           size + utf8data.width > (size_t) maxlen) {
+                               while (size < (size_t) maxlen) {
+                                       screen_write_putc(ctx, gc, ' ');
+                                       size++;
+                               }
+                               break;
+                       }
+                       size += utf8data.width;
+
+                       gc->flags |= GRID_FLAG_UTF8;
+                       screen_write_cell(ctx, gc, &utf8data);
+                       gc->flags &= ~GRID_FLAG_UTF8;
+               } else {
+                       if (maxlen > 0 && size + 1 > (size_t) maxlen)
+                               break;
+
+                       size++;
+                       screen_write_putc(ctx, gc, (u_char)*ptr);
+                       ptr++;
+               }
+       }
+
+       xfree(msg);
+}
+
+/* Write string, similar to nputs, but with embedded formatting (#[]). */
+void printflike5
+screen_write_cnputs(struct screen_write_ctx *ctx,
+    ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...)
+{
+       struct grid_cell         lgc;
+       struct utf8_data         utf8data;
+       va_list                  ap;
+       char                    *msg;
+       char                    *ptr, *last;
+       size_t                   left, size = 0;
+
+       va_start(ap, fmt);
+       xvasprintf(&msg, fmt, ap);
+       va_end(ap);
+
+       memcpy(&lgc, gc, sizeof lgc);
+
+       ptr = msg;
+       while (*ptr != '\0') {
+               if (ptr[0] == '#' && ptr[1] == '[') {
+                       ptr += 2;
+                       last = ptr + strcspn(ptr, "]");
+                       if (*last == '\0') {
+                               /* No ]. Not much point in doing anything. */
+                               break;
+                       }
+                       *last = '\0';
+
+                       screen_write_parsestyle(gc, &lgc, ptr);
+                       ptr = last + 1;
+                       continue;
+               }
+
+               if (utf8flag && *ptr & 0x80 && utf8_open(&utf8data, (u_char)*ptr)) {
+                       ptr++;
+
+                       left = strlen(ptr);
+                       if (left < utf8data.size - 1)
+                               break;
+                       while (utf8_append(&utf8data, (u_char)*ptr))
+                               ptr++;
+                       ptr++;
+
+                       if (maxlen > 0 &&
+                           size + utf8data.width > (size_t) maxlen) {
+                               while (size < (size_t) maxlen) {
+                                       screen_write_putc(ctx, gc, ' ');
+                                       size++;
+                               }
+                               break;
+                       }
+                       size += utf8data.width;
+
+                       lgc.flags |= GRID_FLAG_UTF8;
+                       screen_write_cell(ctx, &lgc, &utf8data);
+                       lgc.flags &= ~GRID_FLAG_UTF8;
+               } else {
+                       if (maxlen > 0 && size + 1 > (size_t) maxlen)
+                               break;
+
+                       size++;
+                       screen_write_putc(ctx, &lgc, (u_char)*ptr);
+                       ptr++;
+               }
+       }
+
+       xfree(msg);
+}
+
+/* Parse an embedded style of the form "fg=colour,bg=colour,bright,...". */
+void
+screen_write_parsestyle(
+    struct grid_cell *defgc, struct grid_cell *gc, const char *in)
+{
+       const char      delimiters[] = " ,";
+       char            tmp[32];
+       int             val;
+       size_t          end;
+       u_char          fg, bg, attr, flags;
+
+       if (*in == '\0')
+               return;
+       if (strchr(delimiters, in[strlen(in) - 1]) != NULL)
+               return;
+
+       fg = gc->fg;
+       bg = gc->bg;
+       attr = gc->attr;
+       flags = gc->flags;
+       do {
+               end = strcspn(in, delimiters);
+               if (end > (sizeof tmp) - 1)
+                       return;
+               memcpy(tmp, in, end);
+               tmp[end] = '\0';
+
+               if (strcasecmp(tmp, "default") == 0) {
+                       fg = defgc->fg;
+                       bg = defgc->bg;
+                       attr = defgc->attr;
+               } else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) {
+                       if ((val = colour_fromstring(tmp + 3)) == -1)
+                               return;
+                       if (*in == 'f' || *in == 'F') {
+                               if (val != 8) {
+                                       if (val & 0x100) {
+                                               flags |= GRID_FLAG_FG256;
+                                               val &= ~0x100;
+                                       } else
+                                               flags &= ~GRID_FLAG_FG256;
+                                       fg = val;
+                               } else
+                                       fg = defgc->fg;
+                       } else if (*in == 'b' || *in == 'B') {
+                               if (val != 8) {
+                                       if (val & 0x100) {
+                                               flags |= GRID_FLAG_BG256;
+                                               val &= ~0x100;
+                                       } else
+                                               flags &= ~GRID_FLAG_BG256;
+                                       bg = val;
+                               } else
+                                       bg = defgc->bg;
+                       } else
+                               return;
+               } else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) {
+                       if ((val = attributes_fromstring(tmp + 2)) == -1)
+                               return;
+                       attr &= ~val;
+               } else {
+                       if ((val = attributes_fromstring(tmp)) == -1)
+                               return;
+                       attr |= val;
+               }
+
+               in += end + strspn(in + end, delimiters);
+       } while (*in != '\0');
+       gc->fg = fg;
+       gc->bg = bg;
+       gc->attr = attr;
+       gc->flags = flags;
+}
+
+/* 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;
+       struct grid_line        *gl;
+       const struct grid_cell  *gc;
+       const struct grid_utf8  *gu;
+       struct utf8_data         utf8data;
+       u_int                    xx, yy, cx, cy, ax, bx;
+
+       cx = s->cx;
+       cy = s->cy;
+       for (yy = py; yy < py + ny; yy++) {
+               gl = &gd->linedata[yy];
+               if (yy < gd->hsize + gd->sy) {
+                       /*
+                        * Find start and end position and copy between
+                        * them. Limit to the real end of the line then use a
+                        * clear EOL only if copying to the end, otherwise
+                        * could overwrite whatever is there already.
+                        */
+                       if (px > gl->cellsize)
+                               ax = gl->cellsize;
+                       else
+                               ax = px;
+                       if (px + nx == gd->sx && px + nx > gl->cellsize)
+                               bx = gl->cellsize;
+                       else
+                               bx = px + nx;
+
+                       for (xx = ax; xx < bx; xx++) {
+                               if (xx >= gl->cellsize)
+                                       gc = &grid_default_cell;
+                               else
+                                       gc = &gl->celldata[xx];
+                               if (!(gc->flags & GRID_FLAG_UTF8)) {
+                                       screen_write_cell(ctx, gc, NULL);
+                                       continue;
+                               }
+                               /* Reinject the UTF-8 sequence. */
+                               gu = &gl->utf8data[xx];
+                               utf8data.size = grid_utf8_copy(gu,
+                                   (char *)utf8data.data,
+                                   sizeof utf8data.data);
+                               utf8data.width = gu->width;
+                               screen_write_cell(ctx, gc, &utf8data);
+                       }
+                       if (px + nx == gd->sx && px + nx > gl->cellsize)
+                               screen_write_clearendofline(ctx);
+               } else
+                       screen_write_clearline(ctx);
+               cy++;
+               screen_write_cursormove(ctx, cx, cy);
+       }
+}
+
+/* Set up context for TTY command. */
+void
+screen_write_initctx(
+    struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, int save_last)
+{
+       struct screen           *s = ctx->s;
+       struct grid             *gd = s->grid;
+       const struct grid_cell  *gc;
+       const struct grid_utf8  *gu;
+       u_int                    xx;
+
+       ttyctx->wp = ctx->wp;
+
+       ttyctx->ocx = s->cx;
+       ttyctx->ocy = s->cy;
+
+       ttyctx->orlower = s->rlower;
+       ttyctx->orupper = s->rupper;
+
+       if (!save_last)
+               return;
+
+       /* Save the last cell on the screen. */
+       gc = &grid_default_cell;
+       for (xx = 1; xx <= screen_size_x(s); xx++) {
+               gc = grid_view_peek_cell(gd, screen_size_x(s) - xx, s->cy);
+               if (!(gc->flags & GRID_FLAG_PADDING))
+                       break;
+       }
+       ttyctx->last_width = xx;
+       memcpy(&ttyctx->last_cell, gc, sizeof ttyctx->last_cell);
+       if (gc->flags & GRID_FLAG_UTF8) {
+               gu = grid_view_peek_utf8(gd, screen_size_x(s) - xx, s->cy);
+               memcpy(&ttyctx->last_utf8, gu, sizeof ttyctx->last_utf8);
+       }
+}
+
+/* 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 (s->cy < s->rupper) {
+               /* Above region. */
+               if (ny > s->cy)
+                       ny = s->cy;
+       } else {
+               /* Below region. */
+               if (ny > s->cy - s->rupper)
+                       ny = s->cy - s->rupper;
+       }
+       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 (s->cy > s->rlower) {
+               /* Below region. */
+               if (ny > screen_size_y(s) - 1 - s->cy)
+                       ny = screen_size_y(s) - 1 - s->cy;
+       } else {
+               /* Above region. */
+               if (ny > s->rlower - s->cy)
+                       ny = s->rlower - 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;
+}
+
+/* Backspace; cursor left unless at start of wrapped line when can move up. */
+void
+screen_write_backspace(struct screen_write_ctx *ctx)
+{
+       struct screen           *s = ctx->s;
+       struct grid_line        *gl;
+
+       if (s->cx == 0) {
+               if (s->cy == 0)
+                       return;
+               gl = &s->grid->linedata[s->grid->hsize + s->cy - 1];
+               if (gl->flags & GRID_LINE_WRAPPED) {
+                       s->cy--;
+                       s->cx = screen_size_x(s) - 1;
+               }
+       } else
+               s->cx--;
+}
+
+/* VT100 alignment test. */
+void
+screen_write_alignmenttest(struct screen_write_ctx *ctx)
+{
+       struct screen           *s = ctx->s;
+       struct tty_ctx           ttyctx;
+       struct grid_cell         gc;
+       u_int                    xx, yy;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+
+       memcpy(&gc, &grid_default_cell, sizeof gc);
+       gc.data = 'E';
+
+       for (yy = 0; yy < screen_size_y(s); yy++) {
+               for (xx = 0; xx < screen_size_x(s); xx++)
+                       grid_view_set_cell(s->grid, xx, yy, &gc);
+       }
+
+       s->cx = 0;
+       s->cy = 0;
+
+       s->rupper = 0;
+
+       s->rlower = screen_size_y(s) - 1;
+
+       tty_write(tty_cmd_alignmenttest, &ttyctx);
+}
+
+/* Insert nx characters. */
+void
+screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx)
+{
+       struct screen   *s = ctx->s;
+       struct tty_ctx   ttyctx;
+
+       if (nx == 0)
+               nx = 1;
+
+       if (nx > screen_size_x(s) - s->cx)
+               nx = screen_size_x(s) - s->cx;
+       if (nx == 0)
+               return;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+
+       if (s->cx <= screen_size_x(s) - 1)
+               grid_view_insert_cells(s->grid, s->cx, s->cy, nx);
+
+       ttyctx.num = nx;
+       tty_write(tty_cmd_insertcharacter, &ttyctx);
+}
+
+/* Delete nx characters. */
+void
+screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx)
+{
+       struct screen   *s = ctx->s;
+       struct tty_ctx   ttyctx;
+
+       if (nx == 0)
+               nx = 1;
+
+       if (nx > screen_size_x(s) - s->cx)
+               nx = screen_size_x(s) - s->cx;
+       if (nx == 0)
+               return;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+
+       if (s->cx <= screen_size_x(s) - 1)
+               grid_view_delete_cells(s->grid, s->cx, s->cy, nx);
+
+       ttyctx.num = nx;
+       tty_write(tty_cmd_deletecharacter, &ttyctx);
+}
+
+/* Insert ny lines. */
+void
+screen_write_insertline(struct screen_write_ctx *ctx, u_int ny)
+{
+       struct screen   *s = ctx->s;
+       struct tty_ctx   ttyctx;
+
+       if (ny == 0)
+               ny = 1;
+
+       if (s->cy < s->rupper || s->cy > s->rlower) {
+               if (ny > screen_size_y(s) - s->cy)
+                       ny = screen_size_y(s) - s->cy;
+               if (ny == 0)
+                       return;
+
+               screen_write_initctx(ctx, &ttyctx, 0);
+
+               grid_view_insert_lines(s->grid, s->cy, ny);
+
+               ttyctx.num = ny;
+               tty_write(tty_cmd_insertline, &ttyctx);
+               return;
+       }
+
+       if (ny > s->rlower + 1 - s->cy)
+               ny = s->rlower + 1 - s->cy;
+       if (ny == 0)
+               return;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+
+       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->rlower, s->cy, ny);
+
+       ttyctx.num = ny;
+       tty_write(tty_cmd_insertline, &ttyctx);
+}
+
+/* Delete ny lines. */
+void
+screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny)
+{
+       struct screen   *s = ctx->s;
+       struct tty_ctx   ttyctx;
+
+       if (ny == 0)
+               ny = 1;
+
+       if (s->cy < s->rupper || s->cy > s->rlower) {
+               if (ny > screen_size_y(s) - s->cy)
+                       ny = screen_size_y(s) - s->cy;
+               if (ny == 0)
+                       return;
+
+               screen_write_initctx(ctx, &ttyctx, 0);
+
+               grid_view_delete_lines(s->grid, s->cy, ny);
+
+               ttyctx.num = ny;
+               tty_write(tty_cmd_deleteline, &ttyctx);
+               return;
+       }
+
+       if (ny > s->rlower + 1 - s->cy)
+               ny = s->rlower + 1 - s->cy;
+       if (ny == 0)
+               return;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+
+       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->rlower, s->cy, ny);
+
+       ttyctx.num = ny;
+       tty_write(tty_cmd_deleteline, &ttyctx);
+}
+
+/* Clear line at cursor. */
+void
+screen_write_clearline(struct screen_write_ctx *ctx)
+{
+       struct screen   *s = ctx->s;
+       struct tty_ctx   ttyctx;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+
+       grid_view_clear(s->grid, 0, s->cy, screen_size_x(s), 1);
+
+       tty_write(tty_cmd_clearline, &ttyctx);
+}
+
+/* Clear to end of line from cursor. */
+void
+screen_write_clearendofline(struct screen_write_ctx *ctx)
+{
+       struct screen   *s = ctx->s;
+       struct tty_ctx   ttyctx;
+       u_int            sx;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+
+       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(tty_cmd_clearendofline, &ttyctx);
+}
+
+/* Clear to start of line from cursor. */
+void
+screen_write_clearstartofline(struct screen_write_ctx *ctx)
+{
+       struct screen   *s = ctx->s;
+       struct tty_ctx   ttyctx;
+       u_int            sx;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+
+       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(tty_cmd_clearstartofline, &ttyctx);
+}
+
+/* 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;
+       struct tty_ctx   ttyctx;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+
+       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(tty_cmd_reverseindex, &ttyctx);
+}
+
+/* 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)   /* cannot be one line */
+               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 UTF-8 mouse mode.  */
+void
+screen_write_utf8mousemode(struct screen_write_ctx *ctx, int state)
+{
+       struct screen   *s = ctx->s;
+
+       if (state)
+               s->mode |= MODE_MOUSE_UTF8;
+       else
+               s->mode &= ~MODE_MOUSE_UTF8;
+}
+
+/* Set mouse mode off. */
+void
+screen_write_mousemode_off(struct screen_write_ctx *ctx)
+{
+       struct screen   *s = ctx->s;
+
+       s->mode &= ~ALL_MOUSE_MODES;
+}
+
+/* Set mouse mode on. */
+void
+screen_write_mousemode_on(struct screen_write_ctx *ctx, int mode)
+{
+       struct screen   *s = ctx->s;
+
+       s->mode &= ~ALL_MOUSE_MODES;
+       s->mode |= mode;
+}
+
+/* Line feed. */
+void
+screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped)
+{
+       struct screen           *s = ctx->s;
+       struct grid_line        *gl;
+       struct tty_ctx           ttyctx;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+
+       gl = &s->grid->linedata[s->grid->hsize + s->cy];
+       if (wrapped)
+               gl->flags |= GRID_LINE_WRAPPED;
+       else
+               gl->flags &= ~GRID_LINE_WRAPPED;
+
+       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++;
+
+       ttyctx.num = wrapped;
+       tty_write(tty_cmd_linefeed, &ttyctx);
+}
+
+/* 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;
+       struct tty_ctx   ttyctx;
+       u_int            sx, sy;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+
+       sx = screen_size_x(s);
+       sy = screen_size_y(s);
+
+       /* Scroll into history if it is enabled and clearing entire screen. */
+       if (s->cy == 0 && s->grid->flags & GRID_HISTORY)
+               grid_view_clear_history(s->grid);
+       else {
+               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(tty_cmd_clearendofscreen, &ttyctx);
+}
+
+/* Clear to start of screen. */
+void
+screen_write_clearstartofscreen(struct screen_write_ctx *ctx)
+{
+       struct screen   *s = ctx->s;
+       struct tty_ctx   ttyctx;
+       u_int            sx;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+
+       sx = screen_size_x(s);
+
+       if (s->cy > 0)
+               grid_view_clear(s->grid, 0, 0, sx, s->cy);
+       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(tty_cmd_clearstartofscreen, &ttyctx);
+}
+
+/* Clear entire screen. */
+void
+screen_write_clearscreen(struct screen_write_ctx *ctx)
+{
+       struct screen   *s = ctx->s;
+       struct tty_ctx   ttyctx;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+
+       /* Scroll into history if it is enabled. */
+       if (s->grid->flags & GRID_HISTORY)
+               grid_view_clear_history(s->grid);
+       else {
+               grid_view_clear(
+                   s->grid, 0, 0, screen_size_x(s), screen_size_y(s));
+       }
+
+       tty_write(tty_cmd_clearscreen, &ttyctx);
+}
+
+/* Write cell data. */
+void
+screen_write_cell(struct screen_write_ctx *ctx,
+    const struct grid_cell *gc, const struct utf8_data *utf8data)
+{
+       struct screen           *s = ctx->s;
+       struct grid             *gd = s->grid;
+       struct tty_ctx           ttyctx;
+       struct grid_utf8         gu;
+       u_int                    width, xx;
+       struct grid_cell         tmp_gc, *tmp_gcp;
+       int                      insert = 0;
+
+       /* Ignore padding. */
+       if (gc->flags & GRID_FLAG_PADDING)
+               return;
+
+       /* Find character width. */
+       if (gc->flags & GRID_FLAG_UTF8)
+               width = utf8data->width;
+       else
+               width = 1;
+
+       /*
+        * If this is a wide character and there is no room on the screen, for
+        * the entire character, don't print it.
+        */
+       if (!(s->mode & MODE_WRAP)
+           && (width > 1 && (width > screen_size_x(s) ||
+               (s->cx != screen_size_x(s)
+                && s->cx > screen_size_x(s) - width))))
+               return;
+
+       /*
+        * If the width is zero, combine onto the previous character, if
+        * there is space.
+        */
+       if (width == 0) {
+               if (screen_write_combine(ctx, utf8data) == 0) {
+                       screen_write_initctx(ctx, &ttyctx, 0);
+                       tty_write(tty_cmd_utf8character, &ttyctx);
+               }
+               return;
+       }
+
+       /* Initialise the redraw context, saving the last cell. */
+       screen_write_initctx(ctx, &ttyctx, 1);
+
+       /* If in insert mode, make space for the cells. */
+       if (s->mode & MODE_INSERT && s->cx <= screen_size_x(s) - width) {
+               xx = screen_size_x(s) - s->cx - width;
+               grid_move_cells(s->grid, s->cx + width, s->cx, s->cy, xx);
+               insert = 1;
+       }
+
+       /* Check this will fit on the current line and wrap if not. */
+       if ((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width) {
+               screen_write_linefeed(ctx, 1);
+               s->cx = 0;      /* carriage return */
+       }
+
+       /* Sanity checks. */
+       if (((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width)
+           || s->cy > screen_size_y(s) - 1)
+               return;
+
+       /* Handle overwriting of UTF-8 characters. */
+       screen_write_overwrite(ctx, width);
+
+       /*
+        * 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_gcp = grid_view_get_cell(gd, xx, s->cy);
+               if (tmp_gcp != NULL)
+                       tmp_gcp->flags |= GRID_FLAG_PADDING;
+       }
+
+       /* Set the cell. */
+       grid_view_set_cell(gd, s->cx, s->cy, gc);
+       if (gc->flags & GRID_FLAG_UTF8) {
+               /* Construct UTF-8 and write it. */
+               grid_utf8_set(&gu, utf8data);
+               grid_view_set_utf8(gd, s->cx, s->cy, &gu);
+       }
+
+       /* Move the cursor. */
+       s->cx += width;
+
+       /* Draw to the screen if necessary. */
+       if (insert) {
+               ttyctx.num = width;
+               tty_write(tty_cmd_insertcharacter, &ttyctx);
+       }
+       ttyctx.utf8 = &gu;
+       if (screen_check_selection(s, s->cx - width, s->cy)) {
+               memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc);
+               tmp_gc.data = gc->data;
+               tmp_gc.flags = gc->flags &
+                   ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
+               tmp_gc.flags |= s->sel.cell.flags &
+                   (GRID_FLAG_FG256|GRID_FLAG_BG256);
+               ttyctx.cell = &tmp_gc;
+               tty_write(tty_cmd_cell, &ttyctx);
+       } else {
+               ttyctx.cell = gc;
+               tty_write(tty_cmd_cell, &ttyctx);
+       }
+}
+
+/* Combine a UTF-8 zero-width character onto the previous. */
+int
+screen_write_combine(
+    struct screen_write_ctx *ctx, const struct utf8_data *utf8data)
+{
+       struct screen           *s = ctx->s;
+       struct grid             *gd = s->grid;
+       struct grid_cell        *gc;
+       struct grid_utf8        *gu, tmp_gu;
+       u_int                    i;
+
+       /* Can't combine if at 0. */
+       if (s->cx == 0)
+               return (-1);
+
+       /* Empty utf8data is out. */
+       if (utf8data->size == 0)
+               fatalx("UTF-8 data empty");
+
+       /* Retrieve the previous cell and convert to UTF-8 if not already. */
+       gc = grid_view_get_cell(gd, s->cx - 1, s->cy);
+       if (!(gc->flags & GRID_FLAG_UTF8)) {
+               tmp_gu.data[0] = gc->data;
+               tmp_gu.data[1] = 0xff;
+               tmp_gu.width = 1;
+
+               grid_view_set_utf8(gd, s->cx - 1, s->cy, &tmp_gu);
+               gc->flags |= GRID_FLAG_UTF8;
+       }
+
+       /* Append the current cell. */
+       gu = grid_view_get_utf8(gd, s->cx - 1, s->cy);
+       if (grid_utf8_append(gu, utf8data) != 0) {
+               /* Failed: scrap this character and replace with underscores. */
+               if (gu->width == 1) {
+                       gc->data = '_';
+                       gc->flags &= ~GRID_FLAG_UTF8;
+               } else {
+                       for (i = 0; i < gu->width && i != sizeof gu->data; i++)
+                               gu->data[i] = '_';
+                       if (i != sizeof gu->data)
+                               gu->data[i] = 0xff;
+                       gu->width = i;
+               }
+       }
+
+       return (0);
+}
+
+/*
+ * 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, u_int width)
+{
+       struct screen           *s = ctx->s;
+       struct grid             *gd = s->grid;
+       const struct grid_cell  *gc;
+       u_int                    xx;
+
+       gc = grid_view_peek_cell(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 any padding cells that belong to a UTF-8 character
+        * we'll be overwriting with the current character.
+        */
+       xx = s->cx + width - 1;
+       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);
+       }
+}
+
+void
+screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len)
+{
+       struct tty_ctx  ttyctx;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+       ttyctx.ptr = str;
+       ttyctx.num = len;
+
+       tty_write(tty_cmd_setselection, &ttyctx);
+}
+
+void
+screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len)
+{
+       struct tty_ctx           ttyctx;
+
+       screen_write_initctx(ctx, &ttyctx, 0);
+       ttyctx.ptr = str;
+       ttyctx.num = len;
+
+       tty_write(tty_cmd_rawstring, &ttyctx);
+}
diff --git a/external/bsd/tmux/dist/screen.c b/external/bsd/tmux/dist/screen.c
new file mode 100644 (file)
index 0000000..d0b7ac4
--- /dev/null
@@ -0,0 +1,362 @@
+/* $Id: screen.c,v 1.3 2011/08/17 18:48:36 jmmv 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 <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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)
+{
+       char hn[MAXHOSTNAMELEN];
+
+       s->grid = grid_create(sx, sy, hlimit);
+
+       if (gethostname(hn, MAXHOSTNAMELEN) == 0)
+               s->title = xstrdup(hn);
+       else
+               s->title = xstrdup("");
+
+       s->cstyle = 0;
+       s->ccolour = xstrdup("");
+       s->tabs = NULL;
+
+       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 | MODE_WRAP;
+
+       screen_reset_tabs(s);
+
+       grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy);
+
+       screen_clear_selection(s);
+}
+
+/* Destroy a screen. */
+void
+screen_free(struct screen *s)
+{
+       if (s->tabs != NULL)
+               xfree(s->tabs);
+       xfree(s->title);
+       xfree(s->ccolour);
+       grid_destroy(s->grid);
+}
+
+/* Reset tabs to default, eight spaces apart. */
+void
+screen_reset_tabs(struct screen *s)
+{
+       u_int   i;
+
+       if (s->tabs != NULL)
+               xfree(s->tabs);
+
+       if ((s->tabs = bit_alloc(screen_size_x(s))) == NULL)
+               fatal("bit_alloc failed");
+       for (i = 8; i < screen_size_x(s); i += 8)
+               bit_set(s->tabs, i);
+}
+
+/* Set screen cursor style. */
+void
+screen_set_cursor_style(struct screen *s, u_int style)
+{
+       if (style <= 4)
+               s->cstyle = style;
+}
+
+/* Set screen cursor colour. */
+void
+screen_set_cursor_colour(struct screen *s, const char *colour_string)
+{
+       xfree(s->ccolour);
+       s->ccolour = xstrdup(colour_string);
+}
+
+/* Set screen title. */
+void
+screen_set_title(struct screen *s, const char *title)
+{
+       size_t  slen = strlen(title);
+       char    tmp[slen * 4 + 1];
+
+       strvisx(tmp, title, slen, VIS_OCTAL|VIS_TAB|VIS_NL);
+
+       xfree(s->title);
+       s->title = xstrdup(tmp);
+}
+
+/* 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);
+
+               /*
+                * It is unclear what should happen to tabs on resize. xterm
+                * seems to try and maintain them, rxvt resets them. Resetting
+                * is simpler and more reliable so let's do that.
+                */
+               screen_reset_tabs(s);
+       }
+
+       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;
+
+       if (sx == 0)
+               fatalx("zero size");
+
+       /*
+        * Treat resizing horizontally simply: just ensure the cursor is
+        * on-screen and change the size. Don't bother to truncate any lines -
+        * then the data should be accessible if the size is then incrased.
+        *
+        * The only potential wrinkle is if UTF-8 double-width characters are
+        * left in the last column, but UTF-8 terminals should deal with this
+        * sanely.
+        */
+       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            needed, available, oldy, i;
+
+       if (sy == 0)
+               fatalx("zero size");
+       oldy = screen_size_y(s);
+
+       /*
+        * When resizing:
+        *
+        * If the height is decreasing, delete lines from the bottom until
+        * hitting the cursor, then push lines from the top into the history.
+        *
+        * When increasing, pull as many lines as possible from the history to
+        * the top, then fill the remaining with blanks at the bottom.
+        */
+
+       /* Size decreasing. */
+       if (sy < oldy) {
+               needed = oldy - sy;
+
+               /* Delete as many lines as possible from the bottom. */
+               available = oldy - 1 - s->cy;
+               if (available > 0) {
+                       if (available > needed)
+                               available = needed;
+                       grid_view_delete_lines(gd, oldy - available, available);
+               }
+               needed -= available;
+
+               /*
+                * Now just increase the history size, if possible, to take
+                * over the lines which are left. If history is off, delete
+                * lines from the top.
+                *
+                * XXX Should apply history limit?
+                */
+               available = s->cy;
+               if (gd->flags & GRID_HISTORY)
+                       gd->hsize += needed;
+               else if (needed > 0 && available > 0) {
+                       if (available > needed)
+                               available = needed;
+                       grid_view_delete_lines(gd, 0, available);
+               }
+               s->cy -= needed;
+       }
+
+       /* Resize line arrays. */
+       gd->linedata = xrealloc(
+           gd->linedata, gd->hsize + sy, sizeof *gd->linedata);
+
+       /* Size increasing. */
+       if (sy > oldy) {
+               needed = sy - oldy;
+
+               /*
+                * Try to pull as much as possible out of the history, if is
+                * is enabled.
+                */
+               available = gd->hsize;
+               if (gd->flags & GRID_HISTORY && available > 0) {
+                       if (available > needed)
+                               available = needed;
+                       gd->hsize -= available;
+                       s->cy += available;
+               } else
+                       available = 0;
+               needed -= available;
+
+               /* Then fill the rest in with blanks. */
+               for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++)
+                       memset(&gd->linedata[i], 0, sizeof gd->linedata[i]);
+       }
+
+       /* Set the new size, and reset the scroll region. */
+       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, u_int rectflag, struct grid_cell *gc)
+{
+       struct screen_sel       *sel = &s->sel;
+
+       memcpy(&sel->cell, gc, sizeof sel->cell);
+       sel->flag = 1;
+       sel->rectflag = rectflag;
+
+       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)
+               return (0);
+
+       if (sel->rectflag) {
+               if (sel->sy < sel->ey) {
+                       /* start line < end line -- downward selection. */
+                       if (py < sel->sy || py > sel->ey)
+                               return (0);
+               } else if (sel->sy > sel->ey) {
+                       /* start line > end line -- upward selection. */
+                       if (py > sel->sy || py < sel->ey)
+                               return (0);
+               } else {
+                       /* starting line == ending line. */
+                       if (py != sel->sy)
+                               return (0);
+               }
+
+               /*
+                * Need to include the selection start row, but not the cursor
+                * row, which means the selection changes depending on which
+                * one is on the left.
+                */
+               if (sel->ex < sel->sx) {
+                       /* Cursor (ex) is on the left. */
+                       if (px < sel->ex)
+                               return (0);
+
+                       if (px > sel->sx)
+                               return (0);
+               } else {
+                       /* Selection start (sx) is on the left. */
+                       if (px < sel->sx)
+                               return (0);
+
+                       if (px > sel->ex)
+                               return (0);
+               }
+       } else {
+               /*
+                * Like emacs, keep the top-left-most character, and drop the
+                * bottom-right-most, regardless of copy direction.
+                */
+               if (sel->sy < sel->ey) {
+                       /* starting line < ending line -- downward selection. */
+                       if (py < sel->sy || py > sel->ey)
+                               return (0);
+
+                       if ((py == sel->sy && px < sel->sx)
+                           || (py == sel->ey && px > sel->ex))
+                               return (0);
+               } else if (sel->sy > sel->ey) {
+                       /* starting line > ending line -- upward selection. */
+                       if (py > sel->sy || py < sel->ey)
+                               return (0);
+
+                       if ((py == sel->sy && px >= sel->sx)
+                           || (py == sel->ey && px < sel->ex))
+                               return (0);
+               } else {
+                       /* starting line == ending line. */
+                       if (py != sel->sy)
+                               return (0);
+
+                       if (sel->ex < sel->sx) {
+                               /* cursor (ex) is on the left */
+                               if (px > sel->sx || px < sel->ex)
+                                       return (0);
+                       } else {
+                               /* selection start (sx) is on the left */
+                               if (px < sel->sx || px > sel->ex)
+                                       return (0);
+                       }
+               }
+       }
+
+       return (1);
+}
diff --git a/external/bsd/tmux/dist/server-client.c b/external/bsd/tmux/dist/server-client.c
new file mode 100644 (file)
index 0000000..e3a62e3
--- /dev/null
@@ -0,0 +1,1004 @@
+/* $Id: server-client.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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 <event.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+void   server_client_handle_key(int, struct mouse_event *, void *);
+void   server_client_repeat_timer(int, short, void *);
+void   server_client_check_exit(struct client *);
+void   server_client_check_backoff(struct client *);
+void   server_client_check_redraw(struct client *);
+void   server_client_set_title(struct client *);
+void   server_client_reset_state(struct client *);
+void   server_client_in_callback(struct bufferevent *, short, void *);
+void   server_client_out_callback(struct bufferevent *, short, void *);
+void   server_client_err_callback(struct bufferevent *, short, void *);
+
+int    server_client_msg_dispatch(struct client *);
+void   server_client_msg_command(struct client *, struct msg_command_data *);
+void   server_client_msg_identify(
+           struct client *, struct msg_identify_data *, int);
+void   server_client_msg_shell(struct client *);
+
+void printflike2 server_client_msg_error(struct cmd_ctx *, const char *, ...);
+void printflike2 server_client_msg_print(struct cmd_ctx *, const char *, ...);
+void printflike2 server_client_msg_info(struct cmd_ctx *, const char *, ...);
+
+/* Create a new client. */
+void
+server_client_create(int fd)
+{
+       struct client   *c;
+       u_int            i;
+
+       setblocking(fd, 0);
+
+       c = xcalloc(1, sizeof *c);
+       c->references = 0;
+       imsg_init(&c->ibuf, fd);
+       server_update_event(c);
+
+       if (gettimeofday(&c->creation_time, NULL) != 0)
+               fatal("gettimeofday failed");
+       memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);
+
+       c->stdin_event = NULL;
+       c->stdout_event = NULL;
+       c->stderr_event = NULL;
+
+       c->tty.fd = -1;
+       c->title = NULL;
+
+       c->session = NULL;
+       c->last_session = NULL;
+       c->tty.sx = 80;
+       c->tty.sy = 24;
+
+       screen_init(&c->status, c->tty.sx, 1, 0);
+       RB_INIT(&c->status_new);
+       RB_INIT(&c->status_old);
+
+       c->message_string = NULL;
+       ARRAY_INIT(&c->message_log);
+
+       c->prompt_string = NULL;
+       c->prompt_buffer = NULL;
+       c->prompt_index = 0;
+
+       c->last_mouse.b = MOUSE_UP;
+       c->last_mouse.x = c->last_mouse.y = -1;
+
+       evtimer_set(&c->repeat_timer, server_client_repeat_timer, c);
+
+       for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+               if (ARRAY_ITEM(&clients, i) == NULL) {
+                       ARRAY_SET(&clients, i, c);
+                       return;
+               }
+       }
+       ARRAY_ADD(&clients, c);
+       log_debug("new client %d", fd);
+}
+
+/* Lost a client. */
+void
+server_client_lost(struct client *c)
+{
+       struct message_entry    *msg;
+       u_int                    i;
+
+       for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+               if (ARRAY_ITEM(&clients, i) == c)
+                       ARRAY_SET(&clients, i, NULL);
+       }
+       log_debug("lost client %d", c->ibuf.fd);
+
+       /*
+        * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called
+        * and tty_free might close an unrelated fd.
+        */
+       if (c->flags & CLIENT_TERMINAL)
+               tty_free(&c->tty);
+
+       if (c->stdin_fd != -1) {
+               setblocking(c->stdin_fd, 1);
+               close(c->stdin_fd);
+       }
+       if (c->stdin_event != NULL)
+               bufferevent_free(c->stdin_event);
+       if (c->stdout_fd != -1) {
+               setblocking(c->stdout_fd, 1);
+               close(c->stdout_fd);
+       }
+       if (c->stdout_event != NULL)
+               bufferevent_free(c->stdout_event);
+       if (c->stderr_fd != -1) {
+               setblocking(c->stderr_fd, 1);
+               close(c->stderr_fd);
+       }
+       if (c->stderr_event != NULL)
+               bufferevent_free(c->stderr_event);
+
+       status_free_jobs(&c->status_new);
+       status_free_jobs(&c->status_old);
+       screen_free(&c->status);
+
+       if (c->title != NULL)
+               xfree(c->title);
+
+       evtimer_del(&c->repeat_timer);
+
+       evtimer_del(&c->identify_timer);
+
+       if (c->message_string != NULL)
+               xfree(c->message_string);
+       evtimer_del(&c->message_timer);
+       for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) {
+               msg = &ARRAY_ITEM(&c->message_log, i);
+               xfree(msg->msg);
+       }
+       ARRAY_FREE(&c->message_log);
+
+       if (c->prompt_string != NULL)
+               xfree(c->prompt_string);
+       if (c->prompt_buffer != NULL)
+               xfree(c->prompt_buffer);
+
+       if (c->cwd != NULL)
+               xfree(c->cwd);
+
+       close(c->ibuf.fd);
+       imsg_clear(&c->ibuf);
+       event_del(&c->event);
+
+       for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
+               if (ARRAY_ITEM(&dead_clients, i) == NULL) {
+                       ARRAY_SET(&dead_clients, i, c);
+                       break;
+               }
+       }
+       if (i == ARRAY_LENGTH(&dead_clients))
+               ARRAY_ADD(&dead_clients, c);
+       c->flags |= CLIENT_DEAD;
+
+       recalculate_sizes();
+       server_check_unattached();
+       server_update_socket();
+}
+
+/* Process a single client event. */
+void
+server_client_callback(int fd, short events, void *data)
+{
+       struct client   *c = data;
+
+       if (c->flags & CLIENT_DEAD)
+               return;
+
+       if (fd == c->ibuf.fd) {
+               if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0)
+                       goto client_lost;
+
+               if (c->flags & CLIENT_BAD) {
+                       if (c->ibuf.w.queued == 0)
+                               goto client_lost;
+                       return;
+               }
+
+               if (events & EV_READ && server_client_msg_dispatch(c) != 0)
+                       goto client_lost;
+       }
+
+       server_update_event(c);
+       return;
+
+client_lost:
+       server_client_lost(c);
+}
+
+/* Handle client status timer. */
+void
+server_client_status_timer(void)
+{
+       struct client   *c;
+       struct session  *s;
+       struct timeval   tv;
+       u_int            i;
+       int              interval;
+       time_t           difference;
+
+       if (gettimeofday(&tv, NULL) != 0)
+               fatal("gettimeofday failed");
+
+       for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+               c = ARRAY_ITEM(&clients, i);
+               if (c == NULL || c->session == NULL)
+                       continue;
+               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.
+                        */
+                       continue;
+               }
+               s = c->session;
+
+               if (!options_get_number(&s->options, "status"))
+                       continue;
+               interval = options_get_number(&s->options, "status-interval");
+
+               difference = tv.tv_sec - c->status_timer.tv_sec;
+               if (difference >= interval) {
+                       status_update_jobs(c);
+                       c->flags |= CLIENT_STATUS;
+               }
+       }
+}
+
+/* Handle data key input from client. */
+void
+server_client_handle_key(int key, struct mouse_event *mouse, void *data)
+{
+       struct client           *c = data;
+       struct session          *s;
+       struct window           *w;
+       struct window_pane      *wp;
+       struct options          *oo;
+       struct timeval           tv;
+       struct key_binding      *bd;
+       struct keylist          *keylist;
+       int                      xtimeout, isprefix;
+       u_int                    i;
+
+       /* Check the client is good to accept input. */
+       if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
+               return;
+       if (c->session == NULL)
+               return;
+       s = c->session;
+
+       /* Update the activity timer. */
+       if (gettimeofday(&c->activity_time, NULL) != 0)
+               fatal("gettimeofday failed");
+       memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time);
+
+       w = c->session->curw->window;
+       wp = w->active;
+       oo = &c->session->options;
+
+       /* Special case: number keys jump to pane in identify mode. */
+       if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') {
+               if (c->flags & CLIENT_READONLY)
+                       return;
+               wp = window_pane_at_index(w, key - '0');
+               if (wp != NULL && window_pane_visible(wp))
+                       window_set_active_pane(w, wp);
+               server_clear_identify(c);
+               return;
+       }
+
+       /* Handle status line. */
+       if (!(c->flags & CLIENT_READONLY)) {
+               status_message_clear(c);
+               server_clear_identify(c);
+       }
+       if (c->prompt_string != NULL) {
+               if (!(c->flags & CLIENT_READONLY))
+                       status_prompt_key(c, key);
+               return;
+       }
+
+       /* Check for mouse keys. */
+       if (key == KEYC_MOUSE) {
+               if (c->flags & CLIENT_READONLY)
+                       return;
+               if (options_get_number(oo, "mouse-select-pane") &&
+                   ((!(mouse->b & MOUSE_DRAG) && mouse->b != MOUSE_UP) ||
+                   wp->mode != &window_copy_mode)) {
+                       /*
+                        * Allow pane switching in copy mode only by mouse down
+                        * (click).
+                        */
+                       window_set_active_at(w, mouse->x, mouse->y);
+                       server_redraw_window_borders(w);
+                       wp = w->active;
+               }
+               if (mouse->y + 1 == c->tty.sy &&
+                   options_get_number(oo, "mouse-select-window") &&
+                   options_get_number(oo, "status")) {
+                       if (mouse->b == MOUSE_UP &&
+                           c->last_mouse.b != MOUSE_UP) {
+                               status_set_window_at(c, mouse->x);
+                               return;
+                       }
+                       if (mouse->b & MOUSE_45) {
+                               if ((mouse->b & MOUSE_BUTTON) == MOUSE_1) {
+                                       session_previous(c->session, 0);
+                                       server_redraw_session(s);
+                               }
+                               if ((mouse->b & MOUSE_BUTTON) == MOUSE_2) {
+                                       session_next(c->session, 0);
+                                       server_redraw_session(s);
+                               }
+                               return;
+                       }
+               }
+               if (options_get_number(oo, "mouse-resize-pane"))
+                       layout_resize_pane_mouse(c, mouse);
+               memcpy(&c->last_mouse, mouse, sizeof c->last_mouse);
+               window_pane_mouse(wp, c->session, mouse);
+               return;
+       }
+
+       /* Is this a prefix key? */
+       keylist = options_get_data(&c->session->options, "prefix");
+       isprefix = 0;
+       for (i = 0; i < ARRAY_LENGTH(keylist); i++) {
+               if (key == ARRAY_ITEM(keylist, i)) {
+                       isprefix = 1;
+                       break;
+               }
+       }
+
+       /* No previous prefix key. */
+       if (!(c->flags & CLIENT_PREFIX)) {
+               if (isprefix)
+                       c->flags |= CLIENT_PREFIX;
+               else {
+                       /* Try as a non-prefix key binding. */
+                       if ((bd = key_bindings_lookup(key)) == NULL) {
+                               if (!(c->flags & CLIENT_READONLY))
+                                       window_pane_key(wp, c->session, key);
+                       } else
+                               key_bindings_dispatch(bd, c);
+               }
+               return;
+       }
+
+       /* Prefix key already pressed. Reset prefix and lookup key. */
+       c->flags &= ~CLIENT_PREFIX;
+       if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) {
+               /* If repeating, treat this as a key, else ignore. */
+               if (c->flags & CLIENT_REPEAT) {
+                       c->flags &= ~CLIENT_REPEAT;
+                       if (isprefix)
+                               c->flags |= CLIENT_PREFIX;
+                       else if (!(c->flags & CLIENT_READONLY))
+                               window_pane_key(wp, c->session, key);
+               }
+               return;
+       }
+
+       /* If already repeating, but this key can't repeat, skip it. */
+       if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
+               c->flags &= ~CLIENT_REPEAT;
+               if (isprefix)
+                       c->flags |= CLIENT_PREFIX;
+               else if (!(c->flags & CLIENT_READONLY))
+                       window_pane_key(wp, c->session, key);
+               return;
+       }
+
+       /* If this key can repeat, reset the repeat flags and timer. */
+       xtimeout = options_get_number(&c->session->options, "repeat-time");
+       if (xtimeout != 0 && bd->can_repeat) {
+               c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
+
+               tv.tv_sec = xtimeout / 1000;
+               tv.tv_usec = (xtimeout % 1000) * 1000L;
+               evtimer_del(&c->repeat_timer);
+               evtimer_add(&c->repeat_timer, &tv);
+       }
+
+       /* Dispatch the command. */
+       key_bindings_dispatch(bd, c);
+}
+
+/* Client functions that need to happen every loop. */
+void
+server_client_loop(void)
+{
+       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);
+               if (c == NULL)
+                       continue;
+
+               server_client_check_exit(c);
+               if (c->session != NULL) {
+                       server_client_check_redraw(c);
+                       server_client_reset_state(c);
+               }
+       }
+
+       /*
+        * Any windows will have been redrawn as part of clients, so clear
+        * their flags now.
+        */
+       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;
+       }
+}
+
+/*
+ * Update cursor position and mode settings. The scroll region and attributes
+ * are cleared when idle (waiting for an event) as this is the most likely time
+ * a user may interrupt tmux, for example with ~^Z in ssh(1). This is a
+ * compromise between excessive resets and likelihood of an interrupt.
+ *
+ * tty_region/tty_reset/tty_update_mode already take care of not resetting
+ * things that are already in their default state.
+ */
+void
+server_client_reset_state(struct client *c)
+{
+       struct window           *w = c->session->curw->window;
+       struct window_pane      *wp = w->active;
+       struct screen           *s = wp->screen;
+       struct options          *oo = &c->session->options;
+       struct options          *wo = &w->options;
+       int                      status, mode;
+
+       if (c->flags & CLIENT_SUSPENDED)
+               return;
+
+       tty_region(&c->tty, 0, c->tty.sy - 1);
+
+       status = options_get_number(oo, "status");
+       if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
+               tty_cursor(&c->tty, 0, 0);
+       else
+               tty_cursor(&c->tty, wp->xoff + s->cx, wp->yoff + s->cy);
+
+       /*
+        * Resizing panes with the mouse requires at least button mode to give
+        * a smooth appearance.
+        */
+       mode = s->mode;
+       if ((c->last_mouse.b & MOUSE_RESIZE_PANE) &&
+           !(mode & (MODE_MOUSE_BUTTON|MODE_MOUSE_ANY)))
+               mode |= MODE_MOUSE_BUTTON;
+
+       /*
+        * Any mode will do for mouse-select-pane, but set standard mode if
+        * none.
+        */
+       if ((mode & ALL_MOUSE_MODES) == 0) {
+               if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL &&
+                   options_get_number(oo, "mouse-select-pane"))
+                       mode |= MODE_MOUSE_STANDARD;
+               else if (options_get_number(oo, "mouse-resize-pane"))
+                       mode |= MODE_MOUSE_STANDARD;
+               else if (options_get_number(oo, "mouse-select-window"))
+                       mode |= MODE_MOUSE_STANDARD;
+               else if (options_get_number(wo, "mode-mouse"))
+                       mode |= MODE_MOUSE_STANDARD;
+       }
+
+       /*
+        * Set UTF-8 mouse input if required. If the terminal is UTF-8, the
+        * user has set mouse-utf8 and any mouse mode is in effect, turn on
+        * UTF-8 mouse input. If the receiving terminal hasn't requested it
+        * (that is, it isn't in s->mode), then it'll be converted in
+        * input_mouse.
+        */
+       if ((c->tty.flags & TTY_UTF8) &&
+           (mode & ALL_MOUSE_MODES) && options_get_number(oo, "mouse-utf8"))
+               mode |= MODE_MOUSE_UTF8;
+       else
+               mode &= ~MODE_MOUSE_UTF8;
+
+       /* Set the terminal mode and reset attributes. */
+       tty_update_mode(&c->tty, mode, s);
+       tty_reset(&c->tty);
+}
+
+/* Repeat time callback. */
+/* ARGSUSED */
+void
+server_client_repeat_timer(unused int fd, unused short events, void *data)
+{
+       struct client   *c = data;
+
+       if (c->flags & CLIENT_REPEAT)
+               c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
+}
+
+/* Check if client should be exited. */
+void
+server_client_check_exit(struct client *c)
+{
+       struct msg_exit_data    exitdata;
+
+       if (!(c->flags & CLIENT_EXIT))
+               return;
+
+       if (c->stdout_fd != -1 && c->stdout_event != NULL &&
+           EVBUFFER_LENGTH(c->stdout_event->output) != 0)
+               return;
+       if (c->stderr_fd != -1 && c->stderr_event != NULL &&
+           EVBUFFER_LENGTH(c->stderr_event->output) != 0)
+               return;
+
+       exitdata.retcode = c->retcode;
+       server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata);
+
+       c->flags &= ~CLIENT_EXIT;
+}
+
+/*
+ * Check if the client should backoff. During backoff, data from external
+ * programs is not written to the terminal. When the existing data drains, the
+ * client is redrawn.
+ *
+ * There are two backoff phases - both the tty and client have backoff flags -
+ * the first to allow existing data to drain and the latter to ensure backoff
+ * is disabled until the redraw has finished and prevent the redraw triggering
+ * another backoff.
+ */
+void
+server_client_check_backoff(struct client *c)
+{
+       struct tty      *tty = &c->tty;
+       size_t           used;
+
+       used = EVBUFFER_LENGTH(tty->event->output);
+
+       /*
+        * If in the second backoff phase (redrawing), don't check backoff
+        * until the redraw has completed (or enough of it to drop below the
+        * backoff threshold).
+        */
+       if (c->flags & CLIENT_BACKOFF) {
+               if (used > BACKOFF_THRESHOLD)
+                       return;
+               c->flags &= ~CLIENT_BACKOFF;
+               return;
+       }
+
+       /* Once drained, allow data through again and schedule redraw. */
+       if (tty->flags & TTY_BACKOFF) {
+               if (used != 0)
+                       return;
+               tty->flags &= ~TTY_BACKOFF;
+               c->flags |= (CLIENT_BACKOFF|CLIENT_REDRAWWINDOW|CLIENT_STATUS);
+               return;
+       }
+
+       /* If too much data, start backoff. */
+       if (used > BACKOFF_THRESHOLD)
+               tty->flags |= TTY_BACKOFF;
+}
+
+/* Check for client redraws. */
+void
+server_client_check_redraw(struct client *c)
+{
+       struct session          *s = c->session;
+       struct window_pane      *wp;
+       int                      flags, redraw;
+
+       flags = c->tty.flags & TTY_FREEZE;
+       c->tty.flags &= ~TTY_FREEZE;
+
+       if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
+               if (options_get_number(&s->options, "set-titles"))
+                       server_client_set_title(c);
+
+               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) {
+               screen_redraw_screen(c, 0, 0);
+               c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS);
+       } else if (c->flags & CLIENT_REDRAWWINDOW) {
+               TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry)
+                       screen_redraw_pane(c, wp);
+               c->flags &= ~CLIENT_REDRAWWINDOW;
+       } else {
+               TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
+                       if (wp->flags & PANE_REDRAW)
+                               screen_redraw_pane(c, wp);
+               }
+       }
+
+       if (c->flags & CLIENT_BORDERS)
+               screen_redraw_screen(c, 0, 1);
+
+       if (c->flags & CLIENT_STATUS)
+               screen_redraw_screen(c, 1, 0);
+
+       c->tty.flags |= flags;
+
+       c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS|CLIENT_BORDERS);
+}
+
+/* Set client title. */
+void
+server_client_set_title(struct client *c)
+{
+       struct session  *s = c->session;
+       const char      *template;
+       char            *title;
+
+       template = options_get_string(&s->options, "set-titles-string");
+
+       title = status_replace(c, NULL, NULL, NULL, template, time(NULL), 1);
+       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);
+       }
+       xfree(title);
+}
+
+/*
+ * Error callback for client stdin. Caller must increase reference count when
+ * enabling event!
+ */
+void
+server_client_in_callback(
+    unused struct bufferevent *bufev, unused short what, void *data)
+{
+       struct client   *c = data;
+
+       c->references--;
+       if (c->flags & CLIENT_DEAD)
+               return;
+
+       bufferevent_disable(c->stdin_event, EV_READ|EV_WRITE);
+       setblocking(c->stdin_fd, 1);
+       close(c->stdin_fd);
+       c->stdin_fd = -1;
+
+       if (c->stdin_callback != NULL)
+               c->stdin_callback(c, c->stdin_data);
+}
+
+/* Error callback for client stdout. */
+void
+server_client_out_callback(
+    unused struct bufferevent *bufev, unused short what, unused void *data)
+{
+       struct client   *c = data;
+
+       bufferevent_disable(c->stdout_event, EV_READ|EV_WRITE);
+       setblocking(c->stdout_fd, 1);
+       close(c->stdout_fd);
+       c->stdout_fd = -1;
+}
+
+/* Error callback for client stderr. */
+void
+server_client_err_callback(
+    unused struct bufferevent *bufev, unused short what, unused void *data)
+{
+       struct client   *c = data;
+
+       bufferevent_disable(c->stderr_event, EV_READ|EV_WRITE);
+       setblocking(c->stderr_fd, 1);
+       close(c->stderr_fd);
+       c->stderr_fd = -1;
+}
+
+/* Dispatch message from client. */
+int
+server_client_msg_dispatch(struct client *c)
+{
+       struct imsg              imsg;
+       struct msg_command_data  commanddata;
+       struct msg_identify_data identifydata;
+       struct msg_environ_data  environdata;
+       ssize_t                  n, datalen;
+
+       if ((n = imsg_read(&c->ibuf)) == -1 || n == 0)
+               return (-1);
+
+       for (;;) {
+               if ((n = imsg_get(&c->ibuf, &imsg)) == -1)
+                       return (-1);
+               if (n == 0)
+                       return (0);
+               datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+
+               if (imsg.hdr.peerid != PROTOCOL_VERSION) {
+                       server_write_client(c, MSG_VERSION, NULL, 0);
+                       c->flags |= CLIENT_BAD;
+                       imsg_free(&imsg);
+                       continue;
+               }
+
+               log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd);
+               switch (imsg.hdr.type) {
+               case MSG_COMMAND:
+                       if (datalen != sizeof commanddata)
+                               fatalx("bad MSG_COMMAND size");
+                       memcpy(&commanddata, imsg.data, sizeof commanddata);
+
+                       server_client_msg_command(c, &commanddata);
+                       break;
+               case MSG_IDENTIFY:
+                       if (datalen != sizeof identifydata)
+                               fatalx("bad MSG_IDENTIFY size");
+                       if (imsg.fd == -1)
+                               fatalx("MSG_IDENTIFY missing fd");
+                       memcpy(&identifydata, imsg.data, sizeof identifydata);
+
+                       c->stdin_fd = imsg.fd;
+                       c->stdin_event = bufferevent_new(c->stdin_fd,
+                           NULL, NULL, server_client_in_callback, c);
+                       if (c->stdin_event == NULL)
+                               fatalx("failed to create stdin event");
+                       setblocking(c->stdin_fd, 0);
+
+                       server_client_msg_identify(c, &identifydata, imsg.fd);
+                       break;
+               case MSG_STDOUT:
+                       if (datalen != 0)
+                               fatalx("bad MSG_STDOUT size");
+                       if (imsg.fd == -1)
+                               fatalx("MSG_STDOUT missing fd");
+
+                       c->stdout_fd = imsg.fd;
+                       c->stdout_event = bufferevent_new(c->stdout_fd,
+                           NULL, NULL, server_client_out_callback, c);
+                       if (c->stdout_event == NULL)
+                               fatalx("failed to create stdout event");
+                       setblocking(c->stdout_fd, 0);
+
+                       break;
+               case MSG_STDERR:
+                       if (datalen != 0)
+                               fatalx("bad MSG_STDERR size");
+                       if (imsg.fd == -1)
+                               fatalx("MSG_STDERR missing fd");
+
+                       c->stderr_fd = imsg.fd;
+                       c->stderr_event = bufferevent_new(c->stderr_fd,
+                           NULL, NULL, server_client_err_callback, c);
+                       if (c->stderr_event == NULL)
+                               fatalx("failed to create stderr event");
+                       setblocking(c->stderr_fd, 0);
+
+                       break;
+               case MSG_RESIZE:
+                       if (datalen != 0)
+                               fatalx("bad MSG_RESIZE size");
+
+                       if (tty_resize(&c->tty)) {
+                               recalculate_sizes();
+                               server_redraw_client(c);
+                       }
+                       break;
+               case MSG_EXITING:
+                       if (datalen != 0)
+                               fatalx("bad MSG_EXITING size");
+
+                       c->session = NULL;
+                       tty_close(&c->tty);
+                       server_write_client(c, MSG_EXITED, NULL, 0);
+                       break;
+               case MSG_WAKEUP:
+               case MSG_UNLOCK:
+                       if (datalen != 0)
+                               fatalx("bad MSG_WAKEUP size");
+
+                       if (!(c->flags & CLIENT_SUSPENDED))
+                               break;
+                       c->flags &= ~CLIENT_SUSPENDED;
+
+                       if (gettimeofday(&c->activity_time, NULL) != 0)
+                               fatal("gettimeofday");
+                       if (c->session != NULL)
+                               session_update_activity(c->session);
+
+                       tty_start_tty(&c->tty);
+                       server_redraw_client(c);
+                       recalculate_sizes();
+                       break;
+               case MSG_ENVIRON:
+                       if (datalen != sizeof environdata)
+                               fatalx("bad MSG_ENVIRON size");
+                       memcpy(&environdata, imsg.data, sizeof environdata);
+
+                       environdata.var[(sizeof environdata.var) - 1] = '\0';
+                       if (strchr(environdata.var, '=') != NULL)
+                               environ_put(&c->environ, environdata.var);
+                       break;
+               case MSG_SHELL:
+                       if (datalen != 0)
+                               fatalx("bad MSG_SHELL size");
+
+                       server_client_msg_shell(c);
+                       break;
+               default:
+                       fatalx("unexpected message");
+               }
+
+               imsg_free(&imsg);
+       }
+}
+
+/* Callback to send error message to client. */
+void printflike2
+server_client_msg_error(struct cmd_ctx *ctx, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       evbuffer_add_vprintf(ctx->cmdclient->stderr_event->output, fmt, ap);
+       va_end(ap);
+
+       bufferevent_write(ctx->cmdclient->stderr_event, "\n", 1);
+       ctx->cmdclient->retcode = 1;
+}
+
+/* Callback to send print message to client. */
+void printflike2
+server_client_msg_print(struct cmd_ctx *ctx, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       evbuffer_add_vprintf(ctx->cmdclient->stdout_event->output, fmt, ap);
+       va_end(ap);
+
+       bufferevent_write(ctx->cmdclient->stdout_event, "\n", 1);
+}
+
+/* Callback to send print message to client, if not quiet. */
+void printflike2
+server_client_msg_info(struct cmd_ctx *ctx, const char *fmt, ...)
+{
+       va_list ap;
+
+       if (options_get_number(&global_options, "quiet"))
+               return;
+
+       va_start(ap, fmt);
+       evbuffer_add_vprintf(ctx->cmdclient->stdout_event->output, fmt, ap);
+       va_end(ap);
+
+       bufferevent_write(ctx->cmdclient->stdout_event, "\n", 1);
+}
+
+/* Handle command message. */
+void
+server_client_msg_command(struct client *c, struct msg_command_data *data)
+{
+       struct cmd_ctx   ctx;
+       struct cmd_list *cmdlist = NULL;
+       int              argc;
+       char           **argv, *cause;
+
+       ctx.error = server_client_msg_error;
+       ctx.print = server_client_msg_print;
+       ctx.info = server_client_msg_info;
+
+       ctx.msgdata = data;
+       ctx.curclient = NULL;
+
+       ctx.cmdclient = c;
+
+       argc = data->argc;
+       data->argv[(sizeof data->argv) - 1] = '\0';
+       if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) {
+               server_client_msg_error(&ctx, "command too long");
+               goto error;
+       }
+
+       if (argc == 0) {
+               argc = 1;
+               argv = xcalloc(1, sizeof *argv);
+               *argv = xstrdup("new-session");
+       }
+
+       if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) {
+               server_client_msg_error(&ctx, "%s", cause);
+               cmd_free_argv(argc, argv);
+               goto error;
+       }
+       cmd_free_argv(argc, argv);
+
+       if (cmd_list_exec(cmdlist, &ctx) != 1)
+               c->flags |= CLIENT_EXIT;
+       cmd_list_free(cmdlist);
+       return;
+
+error:
+       if (cmdlist != NULL)
+               cmd_list_free(cmdlist);
+       c->flags |= CLIENT_EXIT;
+}
+
+/* Handle identify message. */
+void
+server_client_msg_identify(
+    struct client *c, struct msg_identify_data *data, int fd)
+{
+       int     tty_fd;
+
+       c->cwd = NULL;
+       data->cwd[(sizeof data->cwd) - 1] = '\0';
+       if (*data->cwd != '\0')
+               c->cwd = xstrdup(data->cwd);
+
+       if (!isatty(fd))
+           return;
+       if ((tty_fd = dup(fd)) == -1)
+               fatal("dup failed");
+       data->term[(sizeof data->term) - 1] = '\0';
+       tty_init(&c->tty, tty_fd, data->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;
+       c->tty.key_callback = server_client_handle_key;
+       c->tty.key_data = c;
+
+       tty_resize(&c->tty);
+
+       c->flags |= CLIENT_TERMINAL;
+}
+
+/* Handle shell message. */
+void
+server_client_msg_shell(struct client *c)
+{
+       struct msg_shell_data    data;
+       const char              *shell;
+
+       shell = options_get_string(&global_s_options, "default-shell");
+
+       if (*shell == '\0' || areshell(shell))
+               shell = _PATH_BSHELL;
+       if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell)
+               strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell);
+
+       server_write_client(c, MSG_SHELL, &data, sizeof data);
+       c->flags |= CLIENT_BAD; /* it will die after exec */
+}
diff --git a/external/bsd/tmux/dist/server-fn.c b/external/bsd/tmux/dist/server-fn.c
new file mode 100644 (file)
index 0000000..82b196d
--- /dev/null
@@ -0,0 +1,480 @@
+/* $Id: server-fn.c,v 1.3 2011/08/17 18:48:36 jmmv 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 <unistd.h>
+
+#include "tmux.h"
+
+struct session *server_next_session(struct session *);
+void           server_callback_identify(int, short, void *);
+
+void
+server_fill_environ(struct session *s, struct environ *env)
+{
+       char    var[MAXPATHLEN], *term;
+       u_int   idx;
+       long    pid;
+
+       if (s != NULL) {
+               term = options_get_string(&s->options, "default-terminal");
+               environ_set(env, "TERM", term);
+
+               idx = s->idx;
+       } else
+               idx = -1;
+       pid = getpid();
+       xsnprintf(var, sizeof var, "%s,%ld,%d", socket_path, pid, idx);
+       environ_set(env, "TMUX", var);
+}
+
+void
+server_write_client(
+    struct client *c, enum msgtype type, const void *buf, size_t len)
+{
+       struct imsgbuf  *ibuf = &c->ibuf;
+
+       if (c->flags & CLIENT_BAD)
+               return;
+       log_debug("writing %d to client %d", type, c->ibuf.fd);
+       imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, __UNCONST(buf), len);
+       server_update_event(c);
+}
+
+void
+server_write_session(
+    struct session *s, enum msgtype 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_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_redraw_session_group(struct session *s)
+{
+       struct session_group    *sg;
+
+       if ((sg = session_group_find(s)) == NULL)
+               server_redraw_session(s);
+       else {
+               TAILQ_FOREACH(s, &sg->sessions, gentry)
+                       server_redraw_session(s);
+       }
+}
+
+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_status_session_group(struct session *s)
+{
+       struct session_group    *sg;
+
+       if ((sg = session_group_find(s)) == NULL)
+               server_status_session(s);
+       else {
+               TAILQ_FOREACH(s, &sg->sessions, gentry)
+                       server_status_session(s);
+       }
+}
+
+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_redraw_window_borders(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)
+                       c->flags |= CLIENT_BORDERS;
+       }
+}
+
+void
+server_status_window(struct window *w)
+{
+       struct session  *s;
+
+       /*
+        * 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.
+        */
+
+       RB_FOREACH(s, sessions, &sessions) {
+               if (session_has(s, w) != NULL)
+                       server_status_session(s);
+       }
+}
+
+void
+server_lock(void)
+{
+       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;
+               server_lock_client(c);
+       }
+}
+
+void
+server_lock_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 || c->session != s)
+                       continue;
+               server_lock_client(c);
+       }
+}
+
+void
+server_lock_client(struct client *c)
+{
+       const char              *cmd;
+       size_t                   cmdlen;
+       struct msg_lock_data     lockdata;
+
+       if (c->flags & CLIENT_SUSPENDED)
+               return;
+
+       cmd = options_get_string(&c->session->options, "lock-command");
+       cmdlen = strlcpy(lockdata.cmd, cmd, sizeof lockdata.cmd);
+       if (cmdlen >= sizeof lockdata.cmd)
+               return;
+
+       tty_stop_tty(&c->tty);
+       tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP));
+       tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR));
+
+       c->flags |= CLIENT_SUSPENDED;
+       server_write_client(c, MSG_LOCK, &lockdata, sizeof lockdata);
+}
+
+void
+server_kill_window(struct window *w)
+{
+       struct session  *s, *next_s;
+       struct winlink  *wl;
+
+       next_s = RB_MIN(sessions, &sessions);
+       while (next_s != NULL) {
+               s = next_s;
+               next_s = RB_NEXT(sessions, &sessions, s);
+
+               if (session_has(s, w) == NULL)
+                       continue;
+               while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
+                       if (session_detach(s, wl)) {
+                               server_destroy_session_group(s);
+                               break;
+                       } else
+                               server_redraw_session_group(s);
+               }
+       }
+}
+
+int
+server_link_window(struct session *src, struct winlink *srcwl,
+    struct session *dst, int dstidx, int killflag, int selectflag, char **cause)
+{
+       struct winlink          *dstwl;
+       struct session_group    *srcsg, *dstsg;
+
+       srcsg = session_group_find(src);
+       dstsg = session_group_find(dst);
+       if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) {
+               xasprintf(cause, "sessions are grouped");
+               return (-1);
+       }
+
+       dstwl = NULL;
+       if (dstidx != -1)
+               dstwl = winlink_find_by_index(&dst->windows, dstidx);
+       if (dstwl != NULL) {
+               if (dstwl->window == srcwl->window) {
+                       xasprintf(cause, "same index: %d", dstidx);
+                       return (-1);
+               }
+               if (killflag) {
+                       /*
+                        * Can't use session_detach as it will destroy session
+                        * if this makes it empty.
+                        */
+                       dstwl->flags &= ~WINLINK_ALERTFLAGS;
+                       winlink_stack_remove(&dst->lastw, dstwl);
+                       winlink_remove(&dst->windows, dstwl);
+
+                       /* Force select/redraw if current. */
+                       if (dstwl == dst->curw) {
+                               selectflag = 1;
+                               dst->curw = NULL;
+                       }
+               }
+       }
+
+       if (dstidx == -1)
+               dstidx = -1 - options_get_number(&dst->options, "base-index");
+       dstwl = session_attach(dst, srcwl->window, dstidx, cause);
+       if (dstwl == NULL)
+               return (-1);
+
+       if (selectflag)
+               session_select(dst, dstwl->idx);
+       server_redraw_session_group(dst);
+
+       return (0);
+}
+
+void
+server_unlink_window(struct session *s, struct winlink *wl)
+{
+       if (session_detach(s, wl))
+               server_destroy_session_group(s);
+       else
+               server_redraw_session_group(s);
+}
+
+void
+server_destroy_pane(struct window_pane *wp)
+{
+       struct window   *w = wp->window;
+
+       if (wp->fd != -1) {
+               close(wp->fd);
+               bufferevent_free(wp->event);
+               wp->fd = -1;
+       }
+
+       if (options_get_number(&w->options, "remain-on-exit"))
+               return;
+
+       layout_close_pane(wp);
+       window_remove_pane(w, wp);
+
+       if (TAILQ_EMPTY(&w->panes))
+               server_kill_window(w);
+       else
+               server_redraw_window(w);
+}
+
+void
+server_destroy_session_group(struct session *s)
+{
+       struct session_group    *sg;
+
+       if ((sg = session_group_find(s)) == NULL)
+               server_destroy_session(s);
+       else {
+               TAILQ_FOREACH(s, &sg->sessions, gentry)
+                       server_destroy_session(s);
+               TAILQ_REMOVE(&session_groups, sg, entry);
+               xfree(sg);
+       }
+}
+
+struct session *
+server_next_session(struct session *s)
+{
+       struct session *s_loop, *s_out;
+
+       s_out = NULL;
+       RB_FOREACH(s_loop, sessions, &sessions) {
+               if (s_loop == s)
+                       continue;
+               if (s_out == NULL ||
+                   timercmp(&s_loop->activity_time, &s_out->activity_time, <))
+                       s_out = s_loop;
+       }
+       return (s_out);
+}
+
+void
+server_destroy_session(struct session *s)
+{
+       struct client   *c;
+       struct session  *s_new;
+       u_int            i;
+
+       if (!options_get_number(&s->options, "detach-on-destroy"))
+               s_new = server_next_session(s);
+       else
+               s_new = NULL;
+
+       for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+               c = ARRAY_ITEM(&clients, i);
+               if (c == NULL || c->session != s)
+                       continue;
+               if (s_new == NULL) {
+                       c->session = NULL;
+                       c->flags |= CLIENT_EXIT;
+               } else {
+                       c->last_session = NULL;
+                       c->session = s_new;
+                       session_update_activity(s_new);
+                       server_redraw_client(c);
+               }
+       }
+       recalculate_sizes();
+}
+
+void
+server_check_unattached (void)
+{
+       struct session  *s;
+
+       /*
+        * If any sessions are no longer attached and have destroy-unattached
+        * set, collect them.
+        */
+       RB_FOREACH(s, sessions, &sessions) {
+               if (!(s->flags & SESSION_UNATTACHED))
+                       continue;
+               if (options_get_number (&s->options, "destroy-unattached"))
+                       session_destroy(s);
+       }
+}
+
+void
+server_set_identify(struct client *c)
+{
+       struct timeval  tv;
+       int             delay;
+
+       delay = options_get_number(&c->session->options, "display-panes-time");
+       tv.tv_sec = delay / 1000;
+       tv.tv_usec = (delay % 1000) * 1000L;
+
+       evtimer_del(&c->identify_timer);
+       evtimer_set(&c->identify_timer, server_callback_identify, c);
+       evtimer_add(&c->identify_timer, &tv);
+
+       c->flags |= CLIENT_IDENTIFY;
+       c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR);
+       server_redraw_client(c);
+}
+
+void
+server_clear_identify(struct client *c)
+{
+       if (c->flags & CLIENT_IDENTIFY) {
+               c->flags &= ~CLIENT_IDENTIFY;
+               c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
+               server_redraw_client(c);
+       }
+}
+
+/* ARGSUSED */
+void
+server_callback_identify(unused int fd, unused short events, void *data)
+{
+       struct client   *c = data;
+
+       server_clear_identify(c);
+}
+
+void
+server_update_event(struct client *c)
+{
+       short   events;
+
+       events = 0;
+       if (!(c->flags & CLIENT_BAD))
+               events |= EV_READ;
+       if (c->ibuf.w.queued > 0)
+               events |= EV_WRITE;
+       event_del(&c->event);
+       event_set(&c->event, c->ibuf.fd, events, server_client_callback, c);
+       event_add(&c->event, NULL);
+}
diff --git a/external/bsd/tmux/dist/server-window.c b/external/bsd/tmux/dist/server-window.c
new file mode 100644 (file)
index 0000000..1084ed2
--- /dev/null
@@ -0,0 +1,259 @@
+/* $Id: server-window.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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 <event.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+int    server_window_check_bell(struct session *, struct winlink *);
+int    server_window_check_activity(struct session *, struct winlink *);
+int    server_window_check_silence(struct session *, struct winlink *);
+int    server_window_check_content(
+           struct session *, struct winlink *, struct window_pane *);
+void   ring_bell(struct session *);
+
+/* Window functions that need to happen every loop. */
+void
+server_window_loop(void)
+{
+       struct window           *w;
+       struct winlink          *wl;
+       struct window_pane      *wp;
+       struct session          *s;
+       u_int                    i;
+
+       for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+               w = ARRAY_ITEM(&windows, i);
+               if (w == NULL)
+                       continue;
+
+               RB_FOREACH(s, sessions, &sessions) {
+                       wl = session_has(s, w);
+                       if (wl == NULL)
+                               continue;
+
+                       if (server_window_check_bell(s, wl) ||
+                           server_window_check_activity(s, wl) ||
+                           server_window_check_silence(s, wl))
+                               server_status_session(s);
+                       TAILQ_FOREACH(wp, &w->panes, entry)
+                               server_window_check_content(s, wl, wp);
+               }
+               w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY);
+       }
+}
+
+/* Check for bell in window. */
+int
+server_window_check_bell(struct session *s, struct winlink *wl)
+{
+       struct client   *c;
+       struct window   *w = wl->window;
+       u_int            i;
+       int              action, visual;
+
+       if (!(w->flags & WINDOW_BELL) || wl->flags & WINLINK_BELL)
+               return (0);
+       if (s->curw != wl)
+               wl->flags |= WINLINK_BELL;
+
+       action = options_get_number(&s->options, "bell-action");
+       switch (action) {
+       case BELL_ANY:
+               if (s->flags & SESSION_UNATTACHED)
+                       break;
+               visual = options_get_number(&s->options, "visual-bell");
+               for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+                       c = ARRAY_ITEM(&clients, i);
+                       if (c == NULL || c->session != s)
+                               continue;
+                       if (!visual) {
+                               tty_putcode(&c->tty, TTYC_BEL);
+                               continue;
+                       }
+                       if (c->session->curw->window == w) {
+                               status_message_set(c, "Bell in current window");
+                               continue;
+                       }
+                       status_message_set(c, "Bell in window %u",
+                           winlink_find_by_window(&s->windows, w)->idx);
+               }
+               break;
+       case BELL_CURRENT:
+               if (s->flags & SESSION_UNATTACHED)
+                       break;
+               visual = options_get_number(&s->options, "visual-bell");
+               for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+                       c = ARRAY_ITEM(&clients, i);
+                       if (c == NULL || c->session != s)
+                               continue;
+                       if (c->session->curw->window != w)
+                               continue;
+                       if (!visual) {
+                               tty_putcode(&c->tty, TTYC_BEL);
+                               continue;
+                       }
+                       status_message_set(c, "Bell in current window");
+               }
+               break;
+       }
+
+       return (1);
+}
+
+/* Check for activity in window. */
+int
+server_window_check_activity(struct session *s, struct winlink *wl)
+{
+       struct client   *c;
+       struct window   *w = wl->window;
+       u_int            i;
+
+       if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_ACTIVITY)
+               return (0);
+       if (s->curw == wl)
+               return (0);
+
+       if (!options_get_number(&w->options, "monitor-activity"))
+               return (0);
+
+       if (options_get_number(&s->options, "bell-on-alert"))
+               ring_bell(s);
+       wl->flags |= WINLINK_ACTIVITY;
+
+       if (options_get_number(&s->options, "visual-activity")) {
+               for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+                       c = ARRAY_ITEM(&clients, i);
+                       if (c == NULL || c->session != s)
+                               continue;
+                       status_message_set(c, "Activity in window %u",
+                           winlink_find_by_window(&s->windows, w)->idx);
+               }
+       }
+
+       return (1);
+}
+
+/* Check for silence in window. */
+int
+server_window_check_silence(struct session *s, struct winlink *wl)
+{
+       struct client   *c;
+       struct window   *w = wl->window;
+       struct timeval   timer;
+       u_int            i;
+       int              silence_interval, timer_difference;
+
+       if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE)
+               return (0);
+
+       if (s->curw == wl) {
+               /*
+                * Reset the timer for this window if we've focused it.  We
+                * don't want the timer tripping as soon as we've switched away
+                * from this window.
+                */
+               if (gettimeofday(&w->silence_timer, NULL) != 0)
+                       fatal("gettimeofday failed.");
+
+               return (0);
+       }
+
+       silence_interval = options_get_number(&w->options, "monitor-silence");
+       if (silence_interval == 0)
+               return (0);
+
+       if (gettimeofday(&timer, NULL) != 0)
+               fatal("gettimeofday");
+       timer_difference = timer.tv_sec - w->silence_timer.tv_sec;
+       if (timer_difference <= silence_interval)
+               return (0);
+
+       if (options_get_number(&s->options, "bell-on-alert"))
+               ring_bell(s);
+       wl->flags |= WINLINK_SILENCE;
+
+       if (options_get_number(&s->options, "visual-silence")) {
+               for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+                       c = ARRAY_ITEM(&clients, i);
+                       if (c == NULL || c->session != s)
+                               continue;
+                       status_message_set(c, "Silence in window %u",
+                           winlink_find_by_window(&s->windows, w)->idx);
+               }
+       }
+
+       return (1);
+}
+
+/* Check for content change in window. */
+int
+server_window_check_content(
+    struct session *s, struct winlink *wl, struct window_pane *wp)
+{
+       struct client   *c;
+       struct window   *w = wl->window;
+       u_int            i;
+       char            *found, *ptr;
+
+       /* Activity flag must be set for new content. */
+       if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_CONTENT)
+               return (0);
+       if (s->curw == wl)
+               return (0);
+
+       ptr = options_get_string(&w->options, "monitor-content");
+       if (ptr == NULL || *ptr == '\0')
+               return (0);
+       if ((found = window_pane_search(wp, ptr, NULL)) == NULL)
+               return (0);
+       xfree(found);
+
+       if (options_get_number(&s->options, "bell-on-alert"))
+               ring_bell(s);
+       wl->flags |= WINLINK_CONTENT;
+
+       if (options_get_number(&s->options, "visual-content")) {
+               for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+                       c = ARRAY_ITEM(&clients, i);
+                       if (c == NULL || c->session != s)
+                               continue;
+                       status_message_set(c, "Content in window %u",
+                           winlink_find_by_window(&s->windows, w)->idx);
+               }
+       }
+
+       return (1);
+}
+
+/* Ring terminal bell. */
+void
+ring_bell(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 == s)
+                       tty_putcode(&c->tty, TTYC_BEL);
+       }
+}
diff --git a/external/bsd/tmux/dist/server.c b/external/bsd/tmux/dist/server.c
new file mode 100644 (file)
index 0000000..cbcd520
--- /dev/null
@@ -0,0 +1,532 @@
+/* $Id: server.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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 <event.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;
+struct clients  dead_clients;
+
+int             server_fd;
+int             server_shutdown;
+struct event    server_ev_accept;
+struct event    server_ev_second;
+
+struct paste_stack global_buffers;
+
+int             server_create_socket(void);
+void            server_loop(void);
+int             server_should_shutdown(void);
+void            server_send_shutdown(void);
+void            server_clean_dead(void);
+void            server_accept_callback(int, short, void *);
+void            server_signal_callback(int, short, void *);
+void            server_child_signal(void);
+void            server_child_exited(pid_t, int);
+void            server_child_stopped(pid_t, int);
+void            server_second_callback(int, short, void *);
+void            server_lock_server(void);
+void            server_lock_sessions(void);
+
+/* Create server socket. */
+int
+server_create_socket(void)
+{
+       struct sockaddr_un      sa;
+       size_t                  size;
+       mode_t                  mask;
+       int                     fd;
+
+       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);
+
+#ifndef __minix
+       if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+#else
+       if ((fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) == -1)
+#endif
+               fatal("socket failed");
+
+       mask = umask(S_IXUSR|S_IXGRP|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");
+       setblocking(fd, 0);
+
+       server_update_socket();
+
+       return (fd);
+}
+
+/* Fork new server. */
+int
+server_start(void)
+{
+       struct window_pane      *wp;
+       int                      pair[2];
+       char                    *cause;
+       struct timeval           tv;
+       u_int                    i;
+
+       /* The first client is special and gets a socketpair; create it. */
+#ifndef __minix
+       if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
+#else
+       if (socketpair(AF_UNIX, SOCK_SEQPACKET, PF_UNSPEC, pair) != 0)
+#endif
+               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, 0) != 0)
+               fatal("daemon failed");
+
+       /* event_init() was called in our parent, need to reinit. */
+       if (event_reinit(ev_base) != 0)
+               fatal("event_reinit failed");
+       clear_signals(0);
+
+       logfile("server");
+       log_debug("server started, pid %ld", (long) getpid());
+
+       ARRAY_INIT(&windows);
+       RB_INIT(&all_window_panes);
+       ARRAY_INIT(&clients);
+       ARRAY_INIT(&dead_clients);
+       RB_INIT(&sessions);
+       RB_INIT(&dead_sessions);
+       TAILQ_INIT(&session_groups);
+       ARRAY_INIT(&global_buffers);
+       mode_key_init_trees();
+       key_bindings_init();
+       utf8_build();
+
+       start_time = time(NULL);
+       log_debug("socket path %s", socket_path);
+#ifdef HAVE_SETPROCTITLE
+       setproctitle("server (%s)", socket_path);
+#endif
+
+       server_fd = server_create_socket();
+       server_client_create(pair[1]);
+
+       if (access(SYSTEM_CFG, R_OK) == 0)
+               load_cfg(SYSTEM_CFG, NULL, &cfg_causes);
+       else if (errno != ENOENT) {
+               cfg_add_cause(
+                   &cfg_causes, "%s: %s", strerror(errno), SYSTEM_CFG);
+       }
+       if (cfg_file != NULL)
+               load_cfg(cfg_file, NULL, &cfg_causes);
+
+       /*
+        * If there is a session already, put the current window and pane into
+        * more mode.
+        */
+       if (!RB_EMPTY(&sessions) && !ARRAY_EMPTY(&cfg_causes)) {
+               wp = RB_MIN(sessions, &sessions)->curw->window->active;
+               window_pane_set_mode(wp, &window_copy_mode);
+               window_copy_init_for_output(wp);
+               for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) {
+                       cause = ARRAY_ITEM(&cfg_causes, i);
+                       window_copy_add(wp, "%s", cause);
+                       xfree(cause);
+               }
+               ARRAY_FREE(&cfg_causes);
+       }
+       cfg_finished = 1;
+
+       event_set(&server_ev_accept,
+           server_fd, EV_READ|EV_PERSIST, server_accept_callback, NULL);
+       event_add(&server_ev_accept, NULL);
+
+       memset(&tv, 0, sizeof tv);
+       tv.tv_sec = 1;
+       evtimer_set(&server_ev_second, server_second_callback, NULL);
+       evtimer_add(&server_ev_second, &tv);
+
+       set_signals(server_signal_callback);
+       server_loop();
+       exit(0);
+}
+
+/* Main server loop. */
+void
+server_loop(void)
+{
+       while (!server_should_shutdown()) {
+               event_loop(EVLOOP_ONCE);
+
+               server_window_loop();
+               server_client_loop();
+
+               key_bindings_clean();
+               server_clean_dead();
+       }
+}
+
+/* Check if the server should be shutting down (no more clients or sessions). */
+int
+server_should_shutdown(void)
+{
+       u_int   i;
+
+       if (!options_get_number(&global_options, "exit-unattached")) {
+               if (!RB_EMPTY(&sessions))
+                       return (0);
+       }
+       for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+               if (ARRAY_ITEM(&clients, i) != NULL)
+                       return (0);
+       }
+       return (1);
+}
+
+/* Shutdown the server by killing all clients and windows. */
+void
+server_send_shutdown(void)
+{
+       struct client   *c;
+       struct session  *s, *next_s;
+       u_int            i;
+
+       for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
+               c = ARRAY_ITEM(&clients, i);
+               if (c != NULL) {
+                       if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED))
+                               server_client_lost(c);
+                       else
+                               server_write_client(c, MSG_SHUTDOWN, NULL, 0);
+                       c->session = NULL;
+               }
+       }
+
+       s = RB_MIN(sessions, &sessions);
+       while (s != NULL) {
+               next_s = RB_NEXT(sessions, &sessions, s);
+               session_destroy(s);
+               s = next_s;
+       }
+}
+
+/* Free dead, unreferenced clients and sessions. */
+void
+server_clean_dead(void)
+{
+       struct session  *s, *next_s;
+       struct client   *c;
+       u_int            i;
+
+       s = RB_MIN(sessions, &dead_sessions);
+       while (s != NULL) {
+               next_s = RB_NEXT(sessions, &dead_sessions, s);
+               if (s->references == 0) {
+                       RB_REMOVE(sessions, &dead_sessions, s);
+                       xfree(s->name);
+                       xfree(s);
+               }
+               s = next_s;
+       }
+
+       for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
+               c = ARRAY_ITEM(&dead_clients, i);
+               if (c == NULL || c->references != 0)
+                       continue;
+               ARRAY_SET(&dead_clients, i, NULL);
+               xfree(c);
+       }
+}
+
+/* Update socket execute permissions based on whether sessions are attached. */
+void
+server_update_socket(void)
+{
+       struct session  *s;
+       static int       last = -1;
+       int              n, mode;
+       struct stat      sb;
+
+       n = 0;
+       RB_FOREACH(s, sessions, &sessions) {
+               if (!(s->flags & SESSION_UNATTACHED)) {
+                       n++;
+                       break;
+               }
+       }
+
+       if (n != last) {
+               last = n;
+
+               if (stat(socket_path, &sb) != 0)
+                       return;
+               mode = sb.st_mode;
+               if (n != 0) {
+                       if (mode & S_IRUSR)
+                               mode |= S_IXUSR;
+                       if (mode & S_IRGRP)
+                               mode |= S_IXGRP;
+                       if (mode & S_IROTH)
+                               mode |= S_IXOTH;
+               } else
+                       mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
+               chmod(socket_path, mode);
+       }
+}
+
+/* Callback for server socket. */
+/* ARGSUSED */
+void
+server_accept_callback(int fd, short events, unused void *data)
+{
+       struct sockaddr_storage sa;
+       socklen_t               slen = sizeof sa;
+       int                     newfd;
+
+       if (!(events & EV_READ))
+               return;
+
+       newfd = accept(fd, (struct sockaddr *) &sa, &slen);
+       if (newfd == -1) {
+               if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
+                       return;
+               fatal("accept failed");
+       }
+       if (server_shutdown) {
+               close(newfd);
+               return;
+       }
+       server_client_create(newfd);
+}
+
+/* Signal handler. */
+/* ARGSUSED */
+void
+server_signal_callback(int sig, unused short events, unused void *data)
+{
+       switch (sig) {
+       case SIGTERM:
+               server_shutdown = 1;
+               server_send_shutdown();
+               break;
+       case SIGCHLD:
+               server_child_signal();
+               break;
+       case SIGUSR1:
+               event_del(&server_ev_accept);
+               close(server_fd);
+               server_fd = server_create_socket();
+               event_set(&server_ev_accept, server_fd,
+                   EV_READ|EV_PERSIST, server_accept_callback, NULL);
+               event_add(&server_ev_accept, NULL);
+               break;
+       }
+}
+
+/* Handle SIGCHLD. */
+void
+server_child_signal(void)
+{
+       int      status;
+       pid_t    pid;
+
+       for (;;) {
+               switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
+               case -1:
+                       if (errno == ECHILD)
+                               return;
+                       fatal("waitpid failed");
+               case 0:
+                       return;
+               }
+               if (WIFSTOPPED(status))
+                       server_child_stopped(pid, status);
+               else if (WIFEXITED(status) || WIFSIGNALED(status))
+                       server_child_exited(pid, status);
+       }
+}
+
+/* Handle exited children. */
+void
+server_child_exited(pid_t pid, int status)
+{
+       struct window           *w;
+       struct window_pane      *wp;
+       struct job              *job;
+       u_int                    i;
+
+       for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+               if ((w = ARRAY_ITEM(&windows, i)) == NULL)
+                       continue;
+               TAILQ_FOREACH(wp, &w->panes, entry) {
+                       if (wp->pid == pid) {
+                               server_destroy_pane(wp);
+                               break;
+                       }
+               }
+       }
+
+       LIST_FOREACH(job, &all_jobs, lentry) {
+               if (pid == job->pid) {
+                       job_died(job, status);  /* might free job */
+                       break;
+               }
+       }
+}
+
+/* Handle stopped children. */
+void
+server_child_stopped(pid_t pid, int status)
+{
+       struct window           *w;
+       struct window_pane      *wp;
+       u_int                    i;
+
+       if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
+               return;
+
+       for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+               if ((w = ARRAY_ITEM(&windows, i)) == NULL)
+                       continue;
+               TAILQ_FOREACH(wp, &w->panes, entry) {
+                       if (wp->pid == pid) {
+                               if (killpg(pid, SIGCONT) != 0)
+                                       kill(pid, SIGCONT);
+                       }
+               }
+       }
+}
+
+/* Handle once-per-second timer events. */
+/* ARGSUSED */
+void
+server_second_callback(unused int fd, unused short events, unused void *arg)
+{
+       struct window           *w;
+       struct window_pane      *wp;
+       struct timeval           tv;
+       u_int                    i;
+
+       if (options_get_number(&global_s_options, "lock-server"))
+               server_lock_server();
+       else
+               server_lock_sessions();
+
+       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);
+               }
+       }
+
+       server_client_status_timer();
+
+       evtimer_del(&server_ev_second);
+       memset(&tv, 0, sizeof tv);
+       tv.tv_sec = 1;
+       evtimer_add(&server_ev_second, &tv);
+}
+
+/* Lock the server if ALL sessions have hit the time limit. */
+void
+server_lock_server(void)
+{
+       struct session  *s;
+       int              timeout;
+       time_t           t;
+
+       t = time(NULL);
+       RB_FOREACH(s, sessions, &sessions) {
+               if (s->flags & SESSION_UNATTACHED)
+                       continue;
+               timeout = options_get_number(&s->options, "lock-after-time");
+               if (timeout <= 0 || t <= s->activity_time.tv_sec + timeout)
+                       return; /* not timed out */
+       }
+
+       server_lock();
+       recalculate_sizes();
+}
+
+/* Lock any sessions which have timed out. */
+void
+server_lock_sessions(void)
+{
+       struct session  *s;
+       int              timeout;
+       time_t           t;
+
+       t = time(NULL);
+       RB_FOREACH(s, sessions, &sessions) {
+               if (s->flags & SESSION_UNATTACHED)
+                       continue;
+               timeout = options_get_number(&s->options, "lock-after-time");
+               if (timeout > 0 && t > s->activity_time.tv_sec + timeout) {
+                       server_lock_session(s);
+                       recalculate_sizes();
+               }
+       }
+}
diff --git a/external/bsd/tmux/dist/session.c b/external/bsd/tmux/dist/session.c
new file mode 100644 (file)
index 0000000..d9780a4
--- /dev/null
@@ -0,0 +1,573 @@
+/* $Id: session.c,v 1.3 2011/08/17 18:48:36 jmmv 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 <time.h>
+
+#include "tmux.h"
+
+/* Global session list. */
+struct sessions        sessions;
+struct sessions dead_sessions;
+u_int          next_session;
+struct session_groups session_groups;
+
+struct winlink *session_next_alert(struct winlink *);
+struct winlink *session_previous_alert(struct winlink *);
+
+RB_GENERATE(sessions, session, entry, session_cmp);
+
+int
+session_cmp(struct session *s1, struct session *s2)
+{
+       return (strcmp(s1->name, s2->name));
+}
+
+/*
+ * Find if session is still alive. This is true if it is still on the global
+ * sessions list.
+ */
+int
+session_alive(struct session *s)
+{
+       struct session *s_loop;
+
+       RB_FOREACH(s_loop, sessions, &sessions) {
+               if (s_loop == s)
+                       return (1);
+       }
+       return (0);
+}
+
+/* Find session by name. */
+struct session *
+session_find(const char *name)
+{
+       struct session  s;
+
+       s.name = __UNCONST(name);
+       return (RB_FIND(sessions, &sessions, &s));
+}
+
+/* Find session by index. */
+struct session *
+session_find_by_index(u_int idx)
+{
+       struct session  *s;
+
+       RB_FOREACH(s, sessions, &sessions) {
+               if (s->idx == idx)
+                       return (s);
+       }
+       return (NULL);
+}
+
+/* Create a new session. */
+struct session *
+session_create(const char *name, const char *cmd, const char *cwd,
+    struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy,
+    char **cause)
+{
+       struct session  *s;
+
+       s = xmalloc(sizeof *s);
+       s->references = 0;
+       s->flags = 0;
+
+       if (gettimeofday(&s->creation_time, NULL) != 0)
+               fatal("gettimeofday failed");
+       session_update_activity(s);
+
+       s->cwd = xstrdup(cwd);
+
+       s->curw = NULL;
+       TAILQ_INIT(&s->lastw);
+       RB_INIT(&s->windows);
+
+       options_init(&s->options, &global_s_options);
+       environ_init(&s->environ);
+       if (env != NULL)
+               environ_copy(env, &s->environ);
+
+       s->tio = NULL;
+       if (tio != NULL) {
+               s->tio = xmalloc(sizeof *s->tio);
+               memcpy(s->tio, tio, sizeof *s->tio);
+       }
+
+       s->sx = sx;
+       s->sy = sy;
+
+       s->idx = next_session++;
+       if (name != NULL)
+               s->name = xstrdup(name);
+       else
+               xasprintf(&s->name, "%u", s->idx);
+       RB_INSERT(sessions, &sessions, s);
+
+       if (cmd != NULL) {
+               if (session_new(s, NULL, cmd, cwd, idx, cause) == NULL) {
+                       session_destroy(s);
+                       return (NULL);
+               }
+               session_select(s, RB_ROOT(&s->windows)->idx);
+       }
+
+       log_debug("session %s created", s->name);
+
+       return (s);
+}
+
+/* Destroy a session. */
+void
+session_destroy(struct session *s)
+{
+       log_debug("session %s destroyed", s->name);
+
+       RB_REMOVE(sessions, &sessions, s);
+
+       if (s->tio != NULL)
+               xfree(s->tio);
+
+       session_group_remove(s);
+       environ_free(&s->environ);
+       options_free(&s->options);
+
+       while (!TAILQ_EMPTY(&s->lastw))
+               winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw));
+       while (!RB_EMPTY(&s->windows))
+               winlink_remove(&s->windows, RB_ROOT(&s->windows));
+
+       xfree(s->cwd);
+
+       RB_INSERT(sessions, &dead_sessions, s);
+}
+
+/* Check a session name is valid: not empty and no colons. */
+int
+session_check_name(const char *name)
+{
+       return (*name != '\0' && strchr(name, ':') == NULL);
+}
+
+/* Update session active time. */
+void
+session_update_activity(struct session *s)
+{
+       if (gettimeofday(&s->activity_time, NULL) != 0)
+               fatal("gettimeofday");
+}
+
+/* Find the next usable session. */
+struct session *
+session_next_session(struct session *s)
+{
+       struct session *s2;
+
+       if (RB_EMPTY(&sessions) || !session_alive(s))
+               return (NULL);
+
+       s2 = RB_NEXT(sessions, &sessions, s);
+       if (s2 == NULL)
+               s2 = RB_MIN(sessions, &sessions);
+       if (s2 == s)
+               return (NULL);
+       return (s2);
+}
+
+/* Find the previous usable session. */
+struct session *
+session_previous_session(struct session *s)
+{
+       struct session *s2;
+
+       if (RB_EMPTY(&sessions) || !session_alive(s))
+               return (NULL);
+
+       s2 = RB_PREV(sessions, &sessions, s);
+       if (s2 == NULL)
+               s2 = RB_MAX(sessions, &sessions);
+       if (s2 == s)
+               return (NULL);
+       return (s2);
+}
+
+/* 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;
+       struct winlink  *wl;
+       struct environ   env;
+       const char      *shell;
+       u_int            hlimit;
+
+       if ((wl = winlink_add(&s->windows, idx)) == NULL) {
+               xasprintf(cause, "index in use: %d", idx);
+               return (NULL);
+       }
+
+       environ_init(&env);
+       environ_copy(&global_environ, &env);
+       environ_copy(&s->environ, &env);
+       server_fill_environ(s, &env);
+
+       shell = options_get_string(&s->options, "default-shell");
+       if (*shell == '\0' || areshell(shell))
+               shell = _PATH_BSHELL;
+
+       hlimit = options_get_number(&s->options, "history-limit");
+       w = window_create(
+           name, cmd, shell, cwd, &env, s->tio, s->sx, s->sy, hlimit, cause);
+       if (w == NULL) {
+               winlink_remove(&s->windows, wl);
+               environ_free(&env);
+               return (NULL);
+       }
+       winlink_set_window(wl, w);
+       environ_free(&env);
+
+       if (options_get_number(&s->options, "set-remain-on-exit"))
+               options_set_number(&w->options, "remain-on-exit", 1);
+
+       session_group_synchronize_from(s);
+       return (wl);
+}
+
+/* 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, idx)) == NULL) {
+               xasprintf(cause, "index in use: %d", idx);
+               return (NULL);
+       }
+       winlink_set_window(wl, w);
+
+       session_group_synchronize_from(s);
+       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);
+
+       wl->flags &= ~WINLINK_ALERTFLAGS;
+       winlink_stack_remove(&s->lastw, wl);
+       winlink_remove(&s->windows, wl);
+       session_group_synchronize_from(s);
+       if (RB_EMPTY(&s->windows)) {
+               session_destroy(s);
+               return (1);
+       }
+       return (0);
+}
+
+/* Return if session has window. */
+struct winlink *
+session_has(struct session *s, struct window *w)
+{
+       struct winlink  *wl;
+
+       RB_FOREACH(wl, winlinks, &s->windows) {
+               if (wl->window == w)
+                       return (wl);
+       }
+       return (NULL);
+}
+
+struct winlink *
+session_next_alert(struct winlink *wl)
+{
+       while (wl != NULL) {
+               if (wl->flags & WINLINK_ALERTFLAGS)
+                       break;
+               wl = winlink_next(wl);
+       }
+       return (wl);
+}
+
+/* Move session to next window. */
+int
+session_next(struct session *s, int alert)
+{
+       struct winlink  *wl;
+
+       if (s->curw == NULL)
+               return (-1);
+
+       wl = winlink_next(s->curw);
+       if (alert)
+               wl = session_next_alert(wl);
+       if (wl == NULL) {
+               wl = RB_MIN(winlinks, &s->windows);
+               if (alert && ((wl = session_next_alert(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;
+       wl->flags &= ~WINLINK_ALERTFLAGS;
+       return (0);
+}
+
+struct winlink *
+session_previous_alert(struct winlink *wl)
+{
+       while (wl != NULL) {
+               if (wl->flags & WINLINK_ALERTFLAGS)
+                       break;
+               wl = winlink_previous(wl);
+       }
+       return (wl);
+}
+
+/* Move session to previous window. */
+int
+session_previous(struct session *s, int alert)
+{
+       struct winlink  *wl;
+
+       if (s->curw == NULL)
+               return (-1);
+
+       wl = winlink_previous(s->curw);
+       if (alert)
+               wl = session_previous_alert(wl);
+       if (wl == NULL) {
+               wl = RB_MAX(winlinks, &s->windows);
+               if (alert && (wl = session_previous_alert(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;
+       wl->flags &= ~WINLINK_ALERTFLAGS;
+       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;
+       wl->flags &= ~WINLINK_ALERTFLAGS;
+       return (0);
+}
+
+/* Move session to last used window. */
+int
+session_last(struct session *s)
+{
+       struct winlink  *wl;
+
+       wl = TAILQ_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;
+       wl->flags &= ~WINLINK_ALERTFLAGS;
+       return (0);
+}
+
+/* Find the session group containing a session. */
+struct session_group *
+session_group_find(struct session *target)
+{
+       struct session_group    *sg;
+       struct session          *s;
+
+       TAILQ_FOREACH(sg, &session_groups, entry) {
+               TAILQ_FOREACH(s, &sg->sessions, gentry) {
+                       if (s == target)
+                               return (sg);
+               }
+       }
+       return (NULL);
+}
+
+/* Find session group index. */
+u_int
+session_group_index(struct session_group *sg)
+{
+       struct session_group   *sg2;
+       u_int                   i;
+
+       i = 0;
+       TAILQ_FOREACH(sg2, &session_groups, entry) {
+               if (sg == sg2)
+                       return (i);
+               i++;
+       }
+
+       fatalx("session group not found");
+}
+
+/*
+ * Add a session to the session group containing target, creating it if
+ * necessary.
+ */
+void
+session_group_add(struct session *target, struct session *s)
+{
+       struct session_group    *sg;
+
+       if ((sg = session_group_find(target)) == NULL) {
+               sg = xmalloc(sizeof *sg);
+               TAILQ_INSERT_TAIL(&session_groups, sg, entry);
+               TAILQ_INIT(&sg->sessions);
+               TAILQ_INSERT_TAIL(&sg->sessions, target, gentry);
+       }
+       TAILQ_INSERT_TAIL(&sg->sessions, s, gentry);
+}
+
+/* Remove a session from its group and destroy the group if empty. */
+void
+session_group_remove(struct session *s)
+{
+       struct session_group    *sg;
+
+       if ((sg = session_group_find(s)) == NULL)
+               return;
+       TAILQ_REMOVE(&sg->sessions, s, gentry);
+       if (TAILQ_NEXT(TAILQ_FIRST(&sg->sessions), gentry) == NULL)
+               TAILQ_REMOVE(&sg->sessions, TAILQ_FIRST(&sg->sessions), gentry);
+       if (TAILQ_EMPTY(&sg->sessions)) {
+               TAILQ_REMOVE(&session_groups, sg, entry);
+               xfree(sg);
+       }
+}
+
+/* Synchronize a session to its session group. */
+void
+session_group_synchronize_to(struct session *s)
+{
+       struct session_group    *sg;
+       struct session          *target;
+
+       if ((sg = session_group_find(s)) == NULL)
+               return;
+
+       target = NULL;
+       TAILQ_FOREACH(target, &sg->sessions, gentry) {
+               if (target != s)
+                       break;
+       }
+       session_group_synchronize1(target, s);
+}
+
+/* Synchronize a session group to a session. */
+void
+session_group_synchronize_from(struct session *target)
+{
+       struct session_group    *sg;
+       struct session          *s;
+
+       if ((sg = session_group_find(target)) == NULL)
+               return;
+
+       TAILQ_FOREACH(s, &sg->sessions, gentry) {
+               if (s != target)
+                       session_group_synchronize1(target, s);
+       }
+}
+
+/*
+ * Synchronize a session with a target session. This means destroying all
+ * winlinks then recreating them, then updating the current window, last window
+ * stack and alerts.
+ */
+void
+session_group_synchronize1(struct session *target, struct session *s)
+{
+       struct winlinks          old_windows, *ww;
+       struct winlink_stack     old_lastw;
+       struct winlink          *wl, *wl2;
+
+       /* Don't do anything if the session is empty (it'll be destroyed). */
+       ww = &target->windows;
+       if (RB_EMPTY(ww))
+               return;
+
+       /* If the current window has vanished, move to the next now. */
+       if (s->curw != NULL &&
+           winlink_find_by_index(ww, s->curw->idx) == NULL &&
+           session_last(s) != 0 && session_previous(s, 0) != 0)
+               session_next(s, 0);
+
+       /* Save the old pointer and reset it. */
+       memcpy(&old_windows, &s->windows, sizeof old_windows);
+       RB_INIT(&s->windows);
+
+       /* Link all the windows from the target. */
+       RB_FOREACH(wl, winlinks, ww) {
+               wl2 = winlink_add(&s->windows, wl->idx);
+               winlink_set_window(wl2, wl->window);
+               wl2->flags |= wl->flags & WINLINK_ALERTFLAGS;
+       }
+
+       /* Fix up the current window. */
+       if (s->curw != NULL)
+               s->curw = winlink_find_by_index(&s->windows, s->curw->idx);
+       else
+               s->curw = winlink_find_by_index(&s->windows, target->curw->idx);
+
+       /* Fix up the last window stack. */
+       memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
+       TAILQ_INIT(&s->lastw);
+       TAILQ_FOREACH(wl, &old_lastw, sentry) {
+               wl2 = winlink_find_by_index(&s->windows, wl->idx);
+               if (wl2 != NULL)
+                       TAILQ_INSERT_TAIL(&s->lastw, wl2, sentry);
+       }
+
+       /* Then free the old winlinks list. */
+       while (!RB_EMPTY(&old_windows)) {
+               wl = RB_ROOT(&old_windows);
+               winlink_remove(&old_windows, wl);
+       }
+}
diff --git a/external/bsd/tmux/dist/signal.c b/external/bsd/tmux/dist/signal.c
new file mode 100644 (file)
index 0000000..d3e1d0f
--- /dev/null
@@ -0,0 +1,103 @@
+/* $Id: signal.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
+ * Copyright (c) 2010 Romain Francoise <rfrancoise@debian.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 <string.h>
+#include <signal.h>
+
+#include "tmux.h"
+
+struct event   ev_sighup;
+struct event   ev_sigchld;
+struct event   ev_sigcont;
+struct event   ev_sigterm;
+struct event   ev_sigusr1;
+struct event   ev_sigwinch;
+
+void
+set_signals(void(*handler)(int, short, unused void *))
+{
+       struct sigaction        sigact;
+
+       memset(&sigact, 0, sizeof sigact);
+       sigemptyset(&sigact.sa_mask);
+       sigact.sa_flags = SA_RESTART;
+       sigact.sa_handler = SIG_IGN;
+       if (sigaction(SIGINT, &sigact, NULL) != 0)
+               fatal("sigaction failed");
+       if (sigaction(SIGPIPE, &sigact, NULL) != 0)
+               fatal("sigaction failed");
+       if (sigaction(SIGUSR2, &sigact, NULL) != 0)
+               fatal("sigaction failed");
+       if (sigaction(SIGTSTP, &sigact, NULL) != 0)
+               fatal("sigaction failed");
+
+       signal_set(&ev_sighup, SIGHUP, handler, NULL);
+       signal_add(&ev_sighup, NULL);
+       signal_set(&ev_sigchld, SIGCHLD, handler, NULL);
+       signal_add(&ev_sigchld, NULL);
+       signal_set(&ev_sigcont, SIGCONT, handler, NULL);
+       signal_add(&ev_sigcont, NULL);
+       signal_set(&ev_sigterm, SIGTERM, handler, NULL);
+       signal_add(&ev_sigterm, NULL);
+       signal_set(&ev_sigusr1, SIGUSR1, handler, NULL);
+       signal_add(&ev_sigusr1, NULL);
+       signal_set(&ev_sigwinch, SIGWINCH, handler, NULL);
+       signal_add(&ev_sigwinch, NULL);
+}
+
+void
+clear_signals(int after_fork)
+{
+       struct sigaction        sigact;
+
+       memset(&sigact, 0, sizeof sigact);
+       sigemptyset(&sigact.sa_mask);
+       sigact.sa_flags = SA_RESTART;
+       sigact.sa_handler = SIG_DFL;
+       if (sigaction(SIGINT, &sigact, NULL) != 0)
+               fatal("sigaction failed");
+       if (sigaction(SIGPIPE, &sigact, NULL) != 0)
+               fatal("sigaction failed");
+       if (sigaction(SIGUSR2, &sigact, NULL) != 0)
+               fatal("sigaction failed");
+       if (sigaction(SIGTSTP, &sigact, NULL) != 0)
+               fatal("sigaction failed");
+
+       if (after_fork) {
+               if (sigaction(SIGHUP, &sigact, NULL) != 0)
+                       fatal("sigaction failed");
+               if (sigaction(SIGCHLD, &sigact, NULL) != 0)
+                       fatal("sigaction failed");
+               if (sigaction(SIGCONT, &sigact, NULL) != 0)
+                       fatal("sigaction failed");
+               if (sigaction(SIGTERM, &sigact, NULL) != 0)
+                       fatal("sigaction failed");
+               if (sigaction(SIGUSR1, &sigact, NULL) != 0)
+                       fatal("sigaction failed");
+               if (sigaction(SIGWINCH, &sigact, NULL) != 0)
+                       fatal("sigaction failed");
+       } else {
+               event_del(&ev_sighup);
+               event_del(&ev_sigchld);
+               event_del(&ev_sigcont);
+               event_del(&ev_sigterm);
+               event_del(&ev_sigusr1);
+               event_del(&ev_sigwinch);
+       }
+}
diff --git a/external/bsd/tmux/dist/status.c b/external/bsd/tmux/dist/status.c
new file mode 100644 (file)
index 0000000..8bd009f
--- /dev/null
@@ -0,0 +1,1284 @@
+/* $Id: status.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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_redraw_get_left(
+           struct client *, time_t, int, struct grid_cell *, size_t *);
+char   *status_redraw_get_right(
+           struct client *, time_t, int, struct grid_cell *, size_t *);
+char   *status_find_job(struct client *, char **);
+void   status_job_free(void *);
+void   status_job_callback(struct job *);
+char   *status_print(
+           struct client *, struct winlink *, time_t, struct grid_cell *);
+void   status_replace1(struct client *, struct session *, struct winlink *,
+           struct window_pane *, char **, char **, char *, size_t, int);
+void   status_message_callback(int, short, void *);
+
+const char *status_prompt_up_history(u_int *);
+const char *status_prompt_down_history(u_int *);
+void   status_prompt_add_history(const char *);
+char   *status_prompt_complete(const char *);
+
+/* Status prompt history. */
+ARRAY_DECL(, char *) status_prompt_history = ARRAY_INITIALIZER;
+
+/* Status output tree. */
+RB_GENERATE(status_out_tree, status_out, entry, status_out_cmp);
+
+/* Output tree comparison function. */
+int
+status_out_cmp(struct status_out *so1, struct status_out *so2)
+{
+       return (strcmp(so1->cmd, so2->cmd));
+}
+
+/* Retrieve options for left string. */
+char *
+status_redraw_get_left(struct client *c,
+    time_t t, int utf8flag, struct grid_cell *gc, size_t *size)
+{
+       struct session  *s = c->session;
+       char            *left;
+       u_char           fg, bg, attr;
+       size_t           leftlen;
+
+       fg = options_get_number(&s->options, "status-left-fg");
+       if (fg != 8)
+               colour_set_fg(gc, fg);
+       bg = options_get_number(&s->options, "status-left-bg");
+       if (bg != 8)
+               colour_set_bg(gc, bg);
+       attr = options_get_number(&s->options, "status-left-attr");
+       if (attr != 0)
+               gc->attr = attr;
+
+       left = status_replace(c, NULL,
+           NULL, NULL, options_get_string(&s->options, "status-left"), t, 1);
+
+       *size = options_get_number(&s->options, "status-left-length");
+       leftlen = screen_write_cstrlen(utf8flag, "%s", left);
+       if (leftlen < *size)
+               *size = leftlen;
+       return (left);
+}
+
+/* Retrieve options for right string. */
+char *
+status_redraw_get_right(struct client *c,
+    time_t t, int utf8flag, struct grid_cell *gc, size_t *size)
+{
+       struct session  *s = c->session;
+       char            *right;
+       u_char           fg, bg, attr;
+       size_t           rightlen;
+
+       fg = options_get_number(&s->options, "status-right-fg");
+       if (fg != 8)
+               colour_set_fg(gc, fg);
+       bg = options_get_number(&s->options, "status-right-bg");
+       if (bg != 8)
+               colour_set_bg(gc, bg);
+       attr = options_get_number(&s->options, "status-right-attr");
+       if (attr != 0)
+               gc->attr = attr;
+
+       right = status_replace(c, NULL,
+           NULL, NULL, options_get_string(&s->options, "status-right"), t, 1);
+
+       *size = options_get_number(&s->options, "status-right-length");
+       rightlen = screen_write_cstrlen(utf8flag, "%s", right);
+       if (rightlen < *size)
+               *size = rightlen;
+       return (right);
+}
+
+/* Set window at window list position. */
+void
+status_set_window_at(struct client *c, u_int x)
+{
+       struct session  *s = c->session;
+       struct winlink  *wl;
+
+       x += s->wlmouse;
+       RB_FOREACH(wl, winlinks, &s->windows) {
+               if (x < wl->status_width &&
+                       session_select(s, wl->idx) == 0) {
+                       server_redraw_session(s);
+               }
+               x -= wl->status_width + 1;
+       }
+}
+
+/* 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 screen           old_status, window_list;
+       struct grid_cell        stdgc, lgc, rgc, gc;
+       time_t                  t;
+       char                   *left, *right;
+       u_int                   offset, needed;
+       u_int                   wlstart, wlwidth, wlavailable, wloffset, wlsize;
+       size_t                  llen, rlen;
+       int                     larrow, rarrow, utf8flag;
+
+       /* No status line? */
+       if (c->tty.sy == 0 || !options_get_number(&s->options, "status"))
+               return (1);
+       left = right = NULL;
+       larrow = rarrow = 0;
+
+       /* Update status timer. */
+       if (gettimeofday(&c->status_timer, NULL) != 0)
+               fatal("gettimeofday failed");
+       t = c->status_timer.tv_sec;
+
+       /* Set up default colour. */
+       memcpy(&stdgc, &grid_default_cell, sizeof gc);
+       colour_set_fg(&stdgc, options_get_number(&s->options, "status-fg"));
+       colour_set_bg(&stdgc, options_get_number(&s->options, "status-bg"));
+       stdgc.attr |= options_get_number(&s->options, "status-attr");
+
+       /* Create the target screen. */
+       memcpy(&old_status, &c->status, sizeof old_status);
+       screen_init(&c->status, c->tty.sx, 1, 0);
+       screen_write_start(&ctx, NULL, &c->status);
+       for (offset = 0; offset < c->tty.sx; offset++)
+               screen_write_putc(&ctx, &stdgc, ' ');
+       screen_write_stop(&ctx);
+
+       /* If the height is one line, blank status line. */
+       if (c->tty.sy <= 1)
+               goto out;
+
+       /* Get UTF-8 flag. */
+       utf8flag = options_get_number(&s->options, "status-utf8");
+
+       /* Work out left and right strings. */
+       memcpy(&lgc, &stdgc, sizeof lgc);
+       left = status_redraw_get_left(c, t, utf8flag, &lgc, &llen);
+       memcpy(&rgc, &stdgc, sizeof rgc);
+       right = status_redraw_get_right(c, t, utf8flag, &rgc, &rlen);
+
+       /*
+        * Figure out how much space we have for the window list. If there
+        * isn't enough space, just show a blank status line.
+        */
+       needed = 0;
+       if (llen != 0)
+               needed += llen + 1;
+       if (rlen != 0)
+               needed += rlen + 1;
+       if (c->tty.sx == 0 || c->tty.sx <= needed)
+               goto out;
+       wlavailable = c->tty.sx - needed;
+
+       /* Calculate the total size needed for the window list. */
+       wlstart = wloffset = wlwidth = 0;
+       RB_FOREACH(wl, winlinks, &s->windows) {
+               if (wl->status_text != NULL)
+                       xfree(wl->status_text);
+               memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell);
+               wl->status_text = status_print(c, wl, t, &wl->status_cell);
+               wl->status_width =
+                   screen_write_cstrlen(utf8flag, "%s", wl->status_text);
+
+               if (wl == s->curw)
+                       wloffset = wlwidth;
+               wlwidth += wl->status_width + 1;
+       }
+
+       /* Create a new screen for the window list. */
+       screen_init(&window_list, wlwidth, 1, 0);
+
+       /* And draw the window list into it. */
+       screen_write_start(&ctx, NULL, &window_list);
+       RB_FOREACH(wl, winlinks, &s->windows) {
+               screen_write_cnputs(&ctx,
+                   -1, &wl->status_cell, utf8flag, "%s", wl->status_text);
+               screen_write_putc(&ctx, &stdgc, ' ');
+       }
+       screen_write_stop(&ctx);
+
+       /* If there is enough space for the total width, skip to draw now. */
+       if (wlwidth <= wlavailable)
+               goto draw;
+
+       /* Find size of current window text. */
+       wlsize = s->curw->status_width;
+
+       /*
+        * If the current window is already on screen, good to draw from the
+        * start and just leave off the end.
+        */
+       if (wloffset + wlsize < wlavailable) {
+               if (wlavailable > 0) {
+                       rarrow = 1;
+                       wlavailable--;
+               }
+               wlwidth = wlavailable;
+       } else {
+               /*
+                * Work out how many characters we need to omit from the
+                * start. There are wlavailable characters to fill, and
+                * wloffset + wlsize must be the last. So, the start character
+                * is wloffset + wlsize - wlavailable.
+                */
+               if (wlavailable > 0) {
+                       larrow = 1;
+                       wlavailable--;
+               }
+
+               wlstart = wloffset + wlsize - wlavailable;
+               if (wlavailable > 0 && wlwidth > wlstart + wlavailable + 1) {
+                       rarrow = 1;
+                       wlstart++;
+                       wlavailable--;
+               }
+               wlwidth = wlavailable;
+       }
+
+       /* Bail if anything is now too small too. */
+       if (wlwidth == 0 || wlavailable == 0) {
+               screen_free(&window_list);
+               goto out;
+       }
+
+       /*
+        * Now the start position is known, work out the state of the left and
+        * right arrows.
+        */
+       offset = 0;
+       RB_FOREACH(wl, winlinks, &s->windows) {
+               if (wl->flags & WINLINK_ALERTFLAGS &&
+                   larrow == 1 && offset < wlstart)
+                       larrow = -1;
+
+               offset += wl->status_width;
+
+               if (wl->flags & WINLINK_ALERTFLAGS &&
+                   rarrow == 1 && offset > wlstart + wlwidth)
+                       rarrow = -1;
+       }
+
+draw:
+       /* Begin drawing. */
+       screen_write_start(&ctx, NULL, &c->status);
+
+       /* Draw the left string and arrow. */
+       screen_write_cursormove(&ctx, 0, 0);
+       if (llen != 0) {
+               screen_write_cnputs(&ctx, llen, &lgc, utf8flag, "%s", left);
+               screen_write_putc(&ctx, &stdgc, ' ');
+       }
+       if (larrow != 0) {
+               memcpy(&gc, &stdgc, sizeof gc);
+               if (larrow == -1)
+                       gc.attr ^= GRID_ATTR_REVERSE;
+               screen_write_putc(&ctx, &gc, '<');
+       }
+
+       /* Draw the right string and arrow. */
+       if (rarrow != 0) {
+               screen_write_cursormove(&ctx, c->tty.sx - rlen - 2, 0);
+               memcpy(&gc, &stdgc, sizeof gc);
+               if (rarrow == -1)
+                       gc.attr ^= GRID_ATTR_REVERSE;
+               screen_write_putc(&ctx, &gc, '>');
+       } else
+               screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0);
+       if (rlen != 0) {
+               screen_write_putc(&ctx, &stdgc, ' ');
+               screen_write_cnputs(&ctx, rlen, &rgc, utf8flag, "%s", right);
+       }
+
+       /* Figure out the offset for the window list. */
+       if (llen != 0)
+               wloffset = llen + 1;
+       else
+               wloffset = 0;
+       if (wlwidth < wlavailable) {
+               switch (options_get_number(&s->options, "status-justify")) {
+               case 1: /* centered */
+                       wloffset += (wlavailable - wlwidth) / 2;
+                       break;
+               case 2: /* right */
+                       wloffset += (wlavailable - wlwidth);
+                       break;
+               }
+       }
+       if (larrow != 0)
+               wloffset++;
+
+       /* Copy the window list. */
+       s->wlmouse = -wloffset + wlstart;
+       screen_write_cursormove(&ctx, wloffset, 0);
+       screen_write_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1);
+       screen_free(&window_list);
+
+       screen_write_stop(&ctx);
+
+out:
+       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);
+}
+
+/* Replace a single special sequence (prefixed by #). */
+void
+status_replace1(struct client *c, struct session *s, struct winlink *wl,
+    struct window_pane *wp, char **iptr, char **optr, char *out,
+    size_t outsize, int jobsflag)
+{
+       char    ch, tmp[256], *ptr, *endptr, *freeptr;
+       size_t  ptrlen;
+       long    limit;
+
+       if (s == NULL)
+               s = c->session;
+       if (wl == NULL)
+               wl = s->curw;
+       if (wp == NULL)
+               wp = wl->window->active;
+
+       errno = 0;
+       limit = strtol(*iptr, &endptr, 10);
+       if ((limit == 0 && errno != EINVAL) ||
+           (limit == LONG_MIN && errno != ERANGE) ||
+           (limit == LONG_MAX && errno != ERANGE) ||
+           limit != 0)
+               *iptr = endptr;
+       if (limit <= 0)
+               limit = LONG_MAX;
+
+       freeptr = NULL;
+
+       switch (*(*iptr)++) {
+       case '(':
+               if (!jobsflag) {
+                       ch = ')';
+                       goto skip_to;
+               }
+               if ((ptr = status_find_job(c, iptr)) == NULL)
+                       return;
+               goto do_replace;
+       case 'D':
+               xsnprintf(tmp, sizeof tmp, "%%%u", wp->id);
+               ptr = tmp;
+               goto do_replace;
+       case 'H':
+               if (gethostname(tmp, sizeof tmp) != 0)
+                       fatal("gethostname failed");
+               ptr = tmp;
+               goto do_replace;
+       case 'h':
+               if (gethostname(tmp, sizeof tmp) != 0)
+                       fatal("gethostname failed");
+               if ((ptr = strchr(tmp, '.')) != NULL)
+                       *ptr = '\0';
+               ptr = tmp;
+               goto do_replace;
+       case 'I':
+               xsnprintf(tmp, sizeof tmp, "%d", wl->idx);
+               ptr = tmp;
+               goto do_replace;
+       case 'P':
+               xsnprintf(
+                   tmp, sizeof tmp, "%u", window_pane_index(wl->window, wp));
+               ptr = tmp;
+               goto do_replace;
+       case 'S':
+               ptr = s->name;
+               goto do_replace;
+       case 'T':
+               ptr = wp->base.title;
+               goto do_replace;
+       case 'W':
+               ptr = wl->window->name;
+               goto do_replace;
+       case 'F':
+               ptr = window_printable_flags(s, wl);
+               freeptr = ptr;
+               goto do_replace;
+       case '[':
+               /*
+                * Embedded style, handled at display time. Leave present and
+                * skip input until ].
+                */
+               ch = ']';
+               goto skip_to;
+       case '#':
+               *(*optr)++ = '#';
+               break;
+       }
+
+       return;
+
+do_replace:
+       ptrlen = strlen(ptr);
+       if ((size_t) limit < ptrlen)
+               ptrlen = limit;
+
+       if (*optr + ptrlen >= out + outsize - 1)
+               return;
+       while (ptrlen > 0 && *ptr != '\0') {
+               *(*optr)++ = *ptr++;
+               ptrlen--;
+       }
+
+       if (freeptr != NULL)
+               xfree(freeptr);
+       return;
+
+skip_to:
+       *(*optr)++ = '#';
+
+       (*iptr)--;      /* include ch */
+       while (**iptr != ch && **iptr != '\0') {
+               if (*optr >=  out + outsize - 1)
+                       break;
+               *(*optr)++ = *(*iptr)++;
+       }
+}
+
+/* Replace special sequences in fmt. */
+char *
+status_replace(struct client *c, struct session *s, struct winlink *wl,
+    struct window_pane *wp, const char *fmt, time_t t, int jobsflag)
+{
+       static char     out[BUFSIZ];
+       char            in[BUFSIZ], ch, *iptr, *optr;
+
+       strftime(in, sizeof in, fmt, localtime(&t));
+       in[(sizeof in) - 1] = '\0';
+
+       iptr = in;
+       optr = out;
+
+       while (*iptr != '\0') {
+               if (optr >= out + (sizeof out) - 1)
+                       break;
+               ch = *iptr++;
+
+               if (ch != '#' || *iptr == '\0') {
+                       *optr++ = ch;
+                       continue;
+               }
+               status_replace1(
+                   c, s, wl, wp, &iptr, &optr, out, sizeof out, jobsflag);
+       }
+       *optr = '\0';
+
+       return (xstrdup(out));
+}
+
+/* Figure out job name and get its result, starting it off if necessary. */
+char *
+status_find_job(struct client *c, char **iptr)
+{
+       struct status_out       *so, so_find;
+       char                    *cmd;
+       int                      lastesc;
+       size_t                   len;
+
+       if (**iptr == '\0')
+               return (NULL);
+       if (**iptr == ')') {            /* no command given */
+               (*iptr)++;
+               return (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 ) */ {
+               xfree(cmd);
+               return (NULL);
+       }
+       (*iptr)++;                      /* skip final ) */
+       cmd[len] = '\0';
+
+       /* First try in the new tree. */
+       so_find.cmd = cmd;
+       so = RB_FIND(status_out_tree, &c->status_new, &so_find);
+       if (so != NULL && so->out != NULL)
+               return (so->out);
+
+       /* If not found at all, start the job and add to the tree. */
+       if (so == NULL) {
+               job_run(cmd, status_job_callback, status_job_free, c);
+               c->references++;
+
+               so = xmalloc(sizeof *so);
+               so->cmd = xstrdup(cmd);
+               so->out = NULL;
+               RB_INSERT(status_out_tree, &c->status_new, so);
+       }
+
+       /* Lookup in the old tree. */
+       so_find.cmd = cmd;
+       so = RB_FIND(status_out_tree, &c->status_old, &so_find);
+       xfree(cmd);
+       if (so != NULL)
+               return (so->out);
+       return (NULL);
+}
+
+/* Free job tree. */
+void
+status_free_jobs(struct status_out_tree *sotree)
+{
+       struct status_out       *so, *so_next;
+
+       so_next = RB_MIN(status_out_tree, sotree);
+       while (so_next != NULL) {
+               so = so_next;
+               so_next = RB_NEXT(status_out_tree, sotree, so);
+
+               RB_REMOVE(status_out_tree, sotree, so);
+               if (so->out != NULL)
+                       xfree(so->out);
+               xfree(so->cmd);
+               xfree(so);
+       }
+}
+
+/* Update jobs on status interval. */
+void
+status_update_jobs(struct client *c)
+{
+       /* Free the old tree. */
+       status_free_jobs(&c->status_old);
+
+       /* Move the new to old. */
+       memcpy(&c->status_old, &c->status_new, sizeof c->status_old);
+       RB_INIT(&c->status_new);
+}
+
+/* Free status job. */
+void
+status_job_free(void *data)
+{
+       struct client   *c = data;
+
+       c->references--;
+}
+
+/* Job has finished: save its result. */
+void
+status_job_callback(struct job *job)
+{
+       struct client           *c = job->data;
+       struct status_out       *so, so_find;
+       char                    *line, *buf;
+       size_t                   len;
+
+       if (c->flags & CLIENT_DEAD)
+               return;
+
+       so_find.cmd = job->cmd;
+       so = RB_FIND(status_out_tree, &c->status_new, &so_find);
+       if (so == NULL || so->out != NULL)
+               return;
+
+       buf = NULL;
+       if ((line = evbuffer_readline(job->event->input)) == NULL) {
+               len = EVBUFFER_LENGTH(job->event->input);
+               buf = xmalloc(len + 1);
+               if (len != 0)
+                       memcpy(buf, EVBUFFER_DATA(job->event->input), len);
+               buf[len] = '\0';
+       } else
+               buf = xstrdup(line);
+
+       so->out = buf;
+       server_status_client(c);
+}
+
+/* Return winlink status line entry and adjust gc as necessary. */
+char *
+status_print(
+    struct client *c, struct winlink *wl, time_t t, struct grid_cell *gc)
+{
+       struct options  *oo = &wl->window->options;
+       struct session  *s = c->session;
+       const char      *fmt;
+       char            *text;
+       u_char           fg, bg, attr;
+
+       fg = options_get_number(oo, "window-status-fg");
+       if (fg != 8)
+               colour_set_fg(gc, fg);
+       bg = options_get_number(oo, "window-status-bg");
+       if (bg != 8)
+               colour_set_bg(gc, bg);
+       attr = options_get_number(oo, "window-status-attr");
+       if (attr != 0)
+               gc->attr = attr;
+       fmt = options_get_string(oo, "window-status-format");
+       if (wl == s->curw) {
+               fg = options_get_number(oo, "window-status-current-fg");
+               if (fg != 8)
+                       colour_set_fg(gc, fg);
+               bg = options_get_number(oo, "window-status-current-bg");
+               if (bg != 8)
+                       colour_set_bg(gc, bg);
+               attr = options_get_number(oo, "window-status-current-attr");
+               if (attr != 0)
+                       gc->attr = attr;
+               fmt = options_get_string(oo, "window-status-current-format");
+       }
+
+       if (wl->flags & WINLINK_ALERTFLAGS) {
+               fg = options_get_number(oo, "window-status-alert-fg");
+               if (fg != 8)
+                       colour_set_fg(gc, fg);
+               bg = options_get_number(oo, "window-status-alert-bg");
+               if (bg != 8)
+                       colour_set_bg(gc, bg);
+               attr = options_get_number(oo, "window-status-alert-attr");
+               if (attr != 0)
+                       gc->attr = attr;
+       }
+
+       text = status_replace(c, NULL, wl, NULL, fmt, t, 1);
+       return (text);
+}
+
+/* Set a status line message. */
+void printflike2
+status_message_set(struct client *c, const char *fmt, ...)
+{
+       struct timeval           tv;
+       struct session          *s = c->session;
+       struct message_entry    *msg;
+       va_list                  ap;
+       int                      delay;
+       u_int                    i, limit;
+
+       status_prompt_clear(c);
+       status_message_clear(c);
+
+       va_start(ap, fmt);
+       xvasprintf(&c->message_string, fmt, ap);
+       va_end(ap);
+
+       ARRAY_EXPAND(&c->message_log, 1);
+       msg = &ARRAY_LAST(&c->message_log);
+       msg->msg_time = time(NULL);
+       msg->msg = xstrdup(c->message_string);
+
+       if (s == NULL)
+               limit = 0;
+       else
+               limit = options_get_number(&s->options, "message-limit");
+       if (ARRAY_LENGTH(&c->message_log) > limit) {
+               limit = ARRAY_LENGTH(&c->message_log) - limit;
+               for (i = 0; i < limit; i++) {
+                       msg = &ARRAY_FIRST(&c->message_log);
+                       xfree(msg->msg);
+                       ARRAY_REMOVE(&c->message_log, 0);
+               }
+       }
+
+       delay = options_get_number(&c->session->options, "display-time");
+       tv.tv_sec = delay / 1000;
+       tv.tv_usec = (delay % 1000) * 1000L;
+
+       evtimer_del(&c->message_timer);
+       evtimer_set(&c->message_timer, status_message_callback, c);
+       evtimer_add(&c->message_timer, &tv);
+
+       c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
+       c->flags |= CLIENT_STATUS;
+}
+
+/* Clear status line message. */
+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; /* screen was frozen and may have changed */
+
+       screen_reinit(&c->status);
+}
+
+/* Clear status line message after timer expires. */
+/* ARGSUSED */
+void
+status_message_callback(unused int fd, unused short event, void *data)
+{
+       struct client   *c = data;
+
+       status_message_clear(c);
+}
+
+/* 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;
+       int                             utf8flag;
+
+       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);
+
+       utf8flag = options_get_number(&s->options, "status-utf8");
+
+       len = screen_write_strlen(utf8flag, "%s", c->message_string);
+       if (len > c->tty.sx)
+               len = c->tty.sx;
+
+       memcpy(&gc, &grid_default_cell, sizeof gc);
+       colour_set_fg(&gc, options_get_number(&s->options, "message-fg"));
+       colour_set_bg(&gc, 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_nputs(&ctx, len, &gc, utf8flag, "%s", 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);
+}
+
+/* Enable status line prompt. */
+void
+status_prompt_set(struct client *c, const char *msg, const char *input,
+    int (*callbackfn)(void *, const char *), void (*freefn)(void *),
+    void *data, int flags)
+{
+       int     keys;
+
+       status_message_clear(c);
+       status_prompt_clear(c);
+
+       c->prompt_string = status_replace(c, NULL, NULL, NULL, msg,
+           time(NULL), 0);
+
+       if (input == NULL)
+               input = "";
+       c->prompt_buffer = status_replace(c, NULL, NULL, NULL, input,
+           time(NULL), 0);
+       c->prompt_index = strlen(c->prompt_buffer);
+
+       c->prompt_callbackfn = callbackfn;
+       c->prompt_freefn = freefn;
+       c->prompt_data = data;
+
+       c->prompt_hindex = 0;
+
+       c->prompt_flags = flags;
+
+       keys = options_get_number(&c->session->options, "status-keys");
+       if (keys == MODEKEY_EMACS)
+               mode_key_init(&c->prompt_mdata, &mode_key_tree_emacs_edit);
+       else
+               mode_key_init(&c->prompt_mdata, &mode_key_tree_vi_edit);
+
+       c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
+       c->flags |= CLIENT_STATUS;
+}
+
+/* Remove status line prompt. */
+void
+status_prompt_clear(struct client *c)
+{
+       if (c->prompt_string == NULL)
+               return;
+
+       if (c->prompt_freefn != NULL && c->prompt_data != NULL)
+               c->prompt_freefn(c->prompt_data);
+
+       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; /* screen was frozen and may have changed */
+
+       screen_reinit(&c->status);
+}
+
+/* Update status line prompt with a new prompt string. */
+void
+status_prompt_update(struct client *c, const char *msg, const char *input)
+{
+       xfree(c->prompt_string);
+       c->prompt_string = status_replace(c, NULL, NULL, NULL, msg,
+           time(NULL), 0);
+
+       xfree(c->prompt_buffer);
+       if (input == NULL)
+               input = "";
+       c->prompt_buffer = status_replace(c, NULL, NULL, NULL, input,
+           time(NULL), 0);
+       c->prompt_index = strlen(c->prompt_buffer);
+
+       c->prompt_hindex = 0;
+
+       c->flags |= CLIENT_STATUS;
+}
+
+/* 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, off;
+       struct grid_cell                gc, *gcp;
+       int                             utf8flag;
+
+       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);
+
+       utf8flag = options_get_number(&s->options, "status-utf8");
+
+       len = screen_write_strlen(utf8flag, "%s", c->prompt_string);
+       if (len > c->tty.sx)
+               len = c->tty.sx;
+       off = 0;
+
+       memcpy(&gc, &grid_default_cell, sizeof gc);
+       colour_set_fg(&gc, options_get_number(&s->options, "message-fg"));
+       colour_set_bg(&gc, 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_nputs(&ctx, len, &gc, utf8flag, "%s", c->prompt_string);
+
+       left = c->tty.sx - len;
+       if (left != 0) {
+               size = screen_write_strlen(utf8flag, "%s", c->prompt_buffer);
+               if (c->prompt_index >= left) {
+                       off = c->prompt_index - left + 1;
+                       if (c->prompt_index == size)
+                               left--;
+                       size = left;
+               }
+               screen_write_nputs(
+                   &ctx, left, &gc, utf8flag, "%s", c->prompt_buffer + off);
+
+               for (i = len + size; i < c->tty.sx; i++)
+                       screen_write_putc(&ctx, &gc, ' ');
+       }
+
+       screen_write_stop(&ctx);
+
+       /* Apply fake cursor. */
+       off = len + c->prompt_index - off;
+       gcp = grid_view_get_cell(c->status.grid, off, 0);
+       gcp->attr ^= GRID_ATTR_REVERSE;
+
+       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], swapc;
+       const char              *histstr;
+       u_char                   ch;
+       size_t                   size, n, off, idx;
+
+       size = strlen(c->prompt_buffer);
+       switch (mode_key_lookup(&c->prompt_mdata, key)) {
+       case MODEKEYEDIT_CURSORLEFT:
+               if (c->prompt_index > 0) {
+                       c->prompt_index--;
+                       c->flags |= CLIENT_STATUS;
+               }
+               break;
+       case MODEKEYEDIT_SWITCHMODEAPPEND:
+       case MODEKEYEDIT_CURSORRIGHT:
+               if (c->prompt_index < size) {
+                       c->prompt_index++;
+                       c->flags |= CLIENT_STATUS;
+               }
+               break;
+       case MODEKEYEDIT_STARTOFLINE:
+               if (c->prompt_index != 0) {
+                       c->prompt_index = 0;
+                       c->flags |= CLIENT_STATUS;
+               }
+               break;
+       case MODEKEYEDIT_ENDOFLINE:
+               if (c->prompt_index != size) {
+                       c->prompt_index = size;
+                       c->flags |= CLIENT_STATUS;
+               }
+               break;
+       case MODEKEYEDIT_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);
+               xfree(s);
+
+               c->flags |= CLIENT_STATUS;
+               break;
+       case MODEKEYEDIT_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 MODEKEYEDIT_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 MODEKEYEDIT_DELETELINE:
+               *c->prompt_buffer = '\0';
+               c->prompt_index = 0;
+               c->flags |= CLIENT_STATUS;
+               break;
+       case MODEKEYEDIT_DELETETOENDOFLINE:
+               if (c->prompt_index < size) {
+                       c->prompt_buffer[c->prompt_index] = '\0';
+                       c->flags |= CLIENT_STATUS;
+               }
+               break;
+       case MODEKEYEDIT_HISTORYUP:
+               histstr = status_prompt_up_history(&c->prompt_hindex);
+               if (histstr == NULL)
+                       break;
+               xfree(c->prompt_buffer);
+               c->prompt_buffer = xstrdup(histstr);
+               c->prompt_index = strlen(c->prompt_buffer);
+               c->flags |= CLIENT_STATUS;
+               break;
+       case MODEKEYEDIT_HISTORYDOWN:
+               histstr = status_prompt_down_history(&c->prompt_hindex);
+               if (histstr == NULL)
+                       break;
+               xfree(c->prompt_buffer);
+               c->prompt_buffer = xstrdup(histstr);
+               c->prompt_index = strlen(c->prompt_buffer);
+               c->flags |= CLIENT_STATUS;
+               break;
+       case MODEKEYEDIT_PASTE:
+               if ((pb = paste_get_top(&global_buffers)) == NULL)
+                       break;
+               for (n = 0; n < pb->size; n++) {
+                       ch = (u_char) pb->data[n];
+                       if (ch < 32 || ch == 127)
+                               break;
+               }
+
+               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 MODEKEYEDIT_TRANSPOSECHARS:
+               idx = c->prompt_index;
+               if (idx < size)
+                       idx++;
+               if (idx >= 2) {
+                       swapc = c->prompt_buffer[idx - 2];
+                       c->prompt_buffer[idx - 2] = c->prompt_buffer[idx - 1];
+                       c->prompt_buffer[idx - 1] = swapc;
+                       c->prompt_index = idx;
+                       c->flags |= CLIENT_STATUS;
+               }
+               break;
+       case MODEKEYEDIT_ENTER:
+               if (*c->prompt_buffer != '\0')
+                       status_prompt_add_history(c->prompt_buffer);
+               if (c->prompt_callbackfn(c->prompt_data, c->prompt_buffer) == 0)
+                       status_prompt_clear(c);
+               break;
+       case MODEKEYEDIT_CANCEL:
+               if (c->prompt_callbackfn(c->prompt_data, NULL) == 0)
+                       status_prompt_clear(c);
+               break;
+       case MODEKEY_OTHER:
+               if ((key & 0xff00) != 0 || key < 32 || key == 127)
+                       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_callbackfn(
+                           c->prompt_data, c->prompt_buffer) == 0)
+                               status_prompt_clear(c);
+               }
+
+               c->flags |= CLIENT_STATUS;
+               break;
+       default:
+               break;
+       }
+}
+
+/* Get previous line from the history. */
+const char *
+status_prompt_up_history(u_int *idx)
+{
+       u_int size;
+
+       /*
+        * History runs from 0 to size - 1.
+        *
+        * Index is from 0 to size. Zero is empty.
+        */
+
+       size = ARRAY_LENGTH(&status_prompt_history);
+       if (size == 0 || *idx == size)
+               return (NULL);
+       (*idx)++;
+       return (ARRAY_ITEM(&status_prompt_history, size - *idx));
+}
+
+/* Get next line from the history. */
+const char *
+status_prompt_down_history(u_int *idx)
+{
+       u_int size;
+
+       size = ARRAY_LENGTH(&status_prompt_history);
+       if (size == 0 || *idx == 0)
+               return ("");
+       (*idx)--;
+       if (*idx == 0)
+               return ("");
+       return (ARRAY_ITEM(&status_prompt_history, size - *idx));
+}
+
+/* Add line to the history. */
+void
+status_prompt_add_history(const char *line)
+{
+       u_int size;
+
+       size = ARRAY_LENGTH(&status_prompt_history);
+       if (size > 0 && strcmp(ARRAY_LAST(&status_prompt_history), line) == 0)
+               return;
+
+       if (size == PROMPT_HISTORY) {
+               xfree(ARRAY_FIRST(&status_prompt_history));
+               ARRAY_REMOVE(&status_prompt_history, 0);
+       }
+
+       ARRAY_ADD(&status_prompt_history, xstrdup(line));
+}
+
+/* Complete word. */
+char *
+status_prompt_complete(const char *s)
+{
+       const struct cmd_entry                 **cmdent;
+       const struct options_table_entry        *oe;
+       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 (oe = server_options_table; oe->name != NULL; oe++) {
+               if (strncmp(oe->name, s, strlen(s)) == 0)
+                       ARRAY_ADD(&list, oe->name);
+       }
+       for (oe = session_options_table; oe->name != NULL; oe++) {
+               if (strncmp(oe->name, s, strlen(s)) == 0)
+                       ARRAY_ADD(&list, oe->name);
+       }
+       for (oe = window_options_table; oe->name != NULL; oe++) {
+               if (strncmp(oe->name, s, strlen(s)) == 0)
+                       ARRAY_ADD(&list, oe->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/external/bsd/tmux/dist/tmux.1 b/external/bsd/tmux/dist/tmux.1
new file mode 100644 (file)
index 0000000..22528b3
--- /dev/null
@@ -0,0 +1,3011 @@
+.\" $Id: tmux.1,v 1.2 2013/07/20 21:40:04 wiz 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 $Mdocdate: July 8 2011 $
+.Dt TMUX 1
+.Os
+.Sh NAME
+.Nm tmux
+.Nd terminal multiplexer
+.Sh SYNOPSIS
+.Nm tmux
+.Bk -words
+.Op Fl 28lquvV
+.Op Fl c Ar shell-command
+.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 created, accessed, and
+controlled from a single screen.
+.Nm
+may be detached from a screen
+and continue running in the background,
+then later reattached.
+.Pp
+When
+.Nm
+is started it creates a new
+.Em session
+with a single
+.Em window
+and displays it on screen.
+A status line at the bottom of the screen
+shows information on the current session
+and is used to enter interactive commands.
+.Pp
+A session is a single collection of
+.Em pseudo terminals
+under the management of
+.Nm .
+Each session has one or more
+windows linked to it.
+A window occupies the entire screen
+and may be split into rectangular panes,
+each of which is a separate pseudo terminal
+(the
+.Xr pty 4
+manual page documents the technical details of pseudo terminals).
+Any number of
+.Nm
+instances may connect to the same session,
+and any number of windows may be present in the same session.
+Once all sessions are killed,
+.Nm
+exits.
+.Pp
+Each session is persistent and will survive accidental disconnection
+(such as
+.Xr ssh 1
+connection timeout) or intentional detaching (with the
+.Ql C-b d
+key strokes).
+.Nm
+may be reattached using:
+.Pp
+.Dl $ tmux attach
+.Pp
+In
+.Nm ,
+a session is displayed on screen by a
+.Em client
+and all sessions are managed by a single
+.Em server .
+The server and each client are separate processes which communicate through a
+socket 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 ,
+but indicates that the terminal supports 88 colours.
+.It Fl c Ar shell-command
+Execute
+.Ar shell-command
+using the default shell.
+If necessary, the
+.Nm
+server will be started to retrieve the
+.Ic default-shell
+option.
+This option is for compatibility with
+.Xr sh 1
+when
+.Nm
+is used as a login shell.
+.It Fl f Ar file
+Specify an alternative configuration file.
+By default,
+.Nm
+loads the system configuration file from
+.Pa /etc/tmux.conf ,
+if present, then looks for a user configuration 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.
+.Pp
+If a command in the configuration file fails,
+.Nm
+will report an error and exit without executing further commands.
+.It Fl L Ar socket-name
+.Nm
+stores the server socket in a directory under
+.Pa /tmp
+(or
+.Ev TMPDIR
+if set);
+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.
+.Pp
+If the socket is accidentally removed, the
+.Dv SIGUSR1
+signal may be sent to the
+.Nm
+server process to recreate it.
+.It Fl l
+Behave as a login shell.
+This flag currently has no effect and is for compatibility with other shells
+when using tmux as a login shell.
+.It Fl q
+Set the
+.Ic quiet
+server option to prevent the server sending various informational messages.
+.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
+.Nm
+attempts to guess if the terminal is likely to support UTF-8 by checking the
+first of the
+.Ev LC_ALL ,
+.Ev LC_CTYPE
+and
+.Ev LANG
+environment variables to be set for the string "UTF-8".
+This is not always correct: the
+.Fl u
+flag explicitly informs
+.Nm
+that UTF-8 is supported.
+.Pp
+If the server is started from a client passed
+.Fl u
+or where UTF-8 is detected, the
+.Ic utf8
+and
+.Ic status-utf8
+options are enabled in the global window and session options respectively.
+.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 Fl V
+Report the
+.Nm
+version.
+.It Ar command Op Ar flags
+This specifies one of a set of commands used to control
+.Nm ,
+as described in the following sections.
+If no commands are specified, the
+.Ic new-session
+command is assumed.
+.El
+.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
+The default command key bindings are:
+.Pp
+.Bl -tag -width "XXXXXXXXXX" -offset indent -compact
+.It C-b
+Send the prefix key (C-b) through to the application.
+.It C-o
+Rotate the panes in the current window forwards.
+.It C-z
+Suspend the
+.Nm
+client.
+.It !
+Break the current pane out of the window.
+.It \&"
+Split the current pane into two, top and bottom.
+.It #
+List all paste buffers.
+.It $
+Rename the current session.
+.It %
+Split the current pane into two, left and right.
+.It &
+Kill the current window.
+.It '
+Prompt for a window index to select.
+.It ,
+Rename the current window.
+.It -
+Delete the most recently copied buffer of text.
+.It .
+Prompt for an index to move the current window.
+.It 0 to 9
+Select windows 0 to 9.
+.It :
+Enter the
+.Nm
+command prompt.
+.It ;
+Move to the previously active pane.
+.It =
+Choose which buffer to paste interactively from a list.
+.It \&?
+List all key bindings.
+.It D
+Choose a client to detach.
+.It \&[
+Enter copy mode to copy text or view the history.
+.It \&]
+Paste the most recently copied buffer of text.
+.It c
+Create a new window.
+.It d
+Detach the current client.
+.It f
+Prompt to search for text in open windows.
+.It i
+Display some information about the current window.
+.It l
+Move to the previously selected window.
+.It n
+Change to the next window.
+.It o
+Select the next pane in the current window.
+.It p
+Change to the previous window.
+.It q
+Briefly display pane indexes.
+.It r
+Force redraw of the attached client.
+.It s
+Select a new session for the attached client interactively.
+.It L
+Switch the attached client back to the last session.
+.It t
+Show the time.
+.It w
+Choose the current window interactively.
+.It x
+Kill the current pane.
+.It {
+Swap the current pane with the previous pane.
+.It }
+Swap the current pane with the next pane.
+.It ~
+Show previous messages from
+.Nm ,
+if any.
+.It Page Up
+Enter copy mode and scroll one page up.
+.It Up, Down
+.It Left, Right
+Change to the pane above, below, to the left, or to the right of the current
+pane.
+.It M-1 to M-5
+Arrange panes in one of the five preset layouts: even-horizontal,
+even-vertical, main-horizontal, main-vertical, or tiled.
+.It M-n
+Move to the next window with a bell or activity marker.
+.It M-o
+Rotate the panes in the current window backwards.
+.It M-p
+Move to the previous window with a bell or activity marker.
+.It C-Up, C-Down
+.It C-Left, C-Right
+Resize the current pane in steps of one cell.
+.It M-Up, M-Down
+.It M-Left, M-Right
+Resize the current pane in steps of five cells.
+.El
+.Pp
+Key bindings may be changed with the
+.Ic bind-key
+and
+.Ic unbind-key
+commands.
+.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
+.Ar target-window ,
+or
+.Ar target-pane .
+These specify the client, session, window or pane 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 either of
+.Pa /dev/ttyp1
+or
+.Pa ttyp1
+for the client attached to
+.Pa /dev/ttyp1 .
+If no client is specified, the current client is chosen, if possible, or an
+error is reported.
+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 with the same syntax as
+.Ar target-client ,
+in which case the session attached to the client is used.
+When looking for the session name,
+.Nm
+initially searches for an exact match; if none is found, the session names
+are checked for any for which
+.Ar target-session
+is a prefix or for which it matches as an
+.Xr fnmatch 3
+pattern.
+If a single match is found, it is used as the target session; multiple matches
+produce an error.
+If a session is omitted, the current session is used if available; if no
+current session is available, the most recently used is chosen.
+.Pp
+.Ar target-window
+specifies a window in the form
+.Em session Ns \&: Ns Em window .
+.Em session
+follows the same rules as for
+.Ar target-session ,
+and
+.Em window
+is looked for in order: as a window index, for example mysession:1; as an exact
+window name, such as mysession:mywindow; then as an
+.Xr fnmatch 3
+pattern or the start of a window name, such as mysession:mywin* or
+mysession:mywin.
+An empty window name specifies the next unused index if appropriate (for
+example the
+.Ic new-window
+and
+.Ic link-window
+commands)
+otherwise the current window in
+.Em session
+is chosen.
+The special character
+.Ql \&!
+uses the last (previously current) window, or
+.Ql +
+and
+.Ql -
+are the next window or the previous window by number.
+When the argument does not contain a colon,
+.Nm
+first attempts to parse it as window; if that fails, an attempt is made to
+match a session.
+.Pp
+.Ar target-pane
+takes a similar form to
+.Ar target-window
+but with the optional addition of a period followed by a pane index, for
+example: mysession:mywindow.1.
+If the pane index is omitted, the currently active pane in the specified
+window is used.
+If neither a colon nor period appears,
+.Nm
+first attempts to use the argument as a pane index; if that fails, it is looked
+up as for
+.Ar target-window .
+A
+.Ql +
+or
+.Ql -
+indicate the next or previous pane index, respectively.
+One of the strings
+.Em top ,
+.Em bottom ,
+.Em left ,
+.Em right ,
+.Em top-left ,
+.Em top-right ,
+.Em bottom-left
+or
+.Em bottom-right
+may be used instead of a pane index.
+.Pp
+The special characters
+.Ql +
+and
+.Ql -
+may be followed by an offset, for example:
+.Bd -literal -offset indent
+select-window -t:+2
+.Ed
+.Pp
+When dealing with a session that doesn't contain sequential window indexes,
+they will be correctly skipped.
+.Pp
+.Nm
+also gives each pane created in a server an identifier consisting of a
+.Ql %
+and a number, starting from zero.
+A pane's identifier is unique for the life of the
+.Nm
+server and is passed to the child process of the pane in the
+.Ev TMUX_PANE
+environment variable.
+It may be used alone to target a pane or the window containing it.
+.Pp
+.Ar shell-command
+arguments are
+.Xr sh 1
+commands.
+These must be passed as a single item, which typically means quoting them, for
+example:
+.Bd -literal -offset indent
+new-window 'vi /etc/passwd'
+.Ed
+.Pp
+.Ar command
+.Op Ar arguments
+refers to a
+.Nm
+command, passed with the command and arguments separately, for example:
+.Bd -literal -offset indent
+bind-key F1 set-window-option force-width 81
+.Ed
+.Pp
+Or if using
+.Xr sh 1 :
+.Bd -literal -offset indent
+$ tmux bind-key F1 set-window-option force-width 81
+.Ed
+.Pp
+Multiple commands may be specified together as part of a
+.Em command sequence .
+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
+.Ic bind-key ) .
+.Pp
+Example
+.Nm
+commands include:
+.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
+.Ed
+.Pp
+Or from
+.Xr sh 1 :
+.Bd -literal -offset indent
+$ tmux kill-window -t :1
+
+$ tmux new-window \e; split-window -d
+
+$ tmux new-session -d 'vi /etc/passwd' \e; split-window -d \e; attach
+.Ed
+.Sh CLIENTS AND SESSIONS
+The
+.Nm
+server manages clients, sessions, windows and panes.
+Clients are attached to sessions to interact with them, either
+when they are created with the
+.Ic new-session
+command, or later with the
+.Ic attach-session
+command.
+Each session has one or more windows
+.Em linked
+into it.
+Windows may be linked to multiple sessions and are made up of one or
+more panes,
+each of which contains a pseudo terminal.
+Commands for creating, linking and otherwise manipulating windows
+are covered
+in the
+.Sx WINDOWS AND PANES
+section.
+.Pp
+The following commands are available to manage clients and sessions:
+.Bl -tag -width Ds
+.It Xo Ic attach-session
+.Op Fl dr
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic attach )
+If run from outside
+.Nm ,
+create a new client in the current terminal and attach it to
+.Ar target-session .
+If used from inside, switch the current client.
+If
+.Fl d
+is specified, any other clients attached to the session are detached.
+.Fl r
+signifies the client is read-only (only keys bound to the
+.Ic detach-client
+command have any effect)
+.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.
+.Pp
+The
+.Ar target-session
+rules for
+.Ic attach-session
+are slightly adjusted: if
+.Nm
+needs to select the most recently used session, it will prefer the most
+recently used
+.Em unattached
+session.
+.It Xo Ic detach-client
+.Op Fl P
+.Op Fl s Ar target-session
+.Op Fl t Ar target-client
+.Xc
+.D1 (alias: Ic detach )
+Detach the current client if bound to a key, the client specified with
+.Fl t ,
+or all clients currently attached to to the session specified by
+.Fl s .
+If
+.Fl P
+is given, send SIGHUP to the parent process of the client, typically causing it
+to exit.
+.It Ic has-session Op Fl t Ar target-session
+.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 Ic kill-server
+Kill the
+.Nm
+server and clients and destroy all sessions.
+.It Ic kill-session Op Fl t Ar target-session
+Destroy the given session, closing any windows linked to it and no other
+sessions, and detaching all clients attached to it.
+.It Ic list-clients Op Fl t Ar target-session
+.D1 (alias: Ic lsc )
+List all clients attached to the server.
+If
+.Ar target-session
+is specified, list only clients connected to that session.
+.It Ic list-commands
+.D1 (alias: Ic lscm )
+List the syntax of all commands supported by
+.Nm .
+.It Ic list-sessions
+.D1 (alias: Ic ls )
+List all sessions managed by the server.
+.It Ic lock-client Op Fl t Ar target-client
+.D1 (alias: Ic lockc )
+Lock
+.Ar target-client ,
+see the
+.Ic lock-server
+command.
+.It Ic lock-session Op Fl t Ar target-session
+.D1 (alias: Ic locks )
+Lock all clients attached to
+.Ar target-session .
+.It Xo Ic new-session
+.Op Fl d
+.Op Fl n Ar window-name
+.Op Fl s Ar session-name
+.Op Fl t Ar target-session
+.Op Fl x Ar width
+.Op Fl y Ar height
+.Op Ar shell-command
+.Xc
+.D1 (alias: Ic new )
+Create a new session with name
+.Ar session-name .
+.Pp
+The new session is attached to the current terminal unless
+.Fl d
+is given.
+.Ar window-name
+and
+.Ar shell-command
+are the name of and shell command to execute in the initial window.
+If
+.Fl d
+is used,
+.Fl x
+and
+.Fl y
+specify the size of the initial window (80 by 24 if not given).
+.Pp
+If run from a terminal, any
+.Xr termios 4
+special characters are saved and used for new windows in the new session.
+.Pp
+If
+.Fl t
+is given, the new session is
+.Em grouped
+with
+.Ar target-session .
+This means they share the same set of windows - all windows from
+.Ar target-session
+are linked to the new session and any subsequent new windows or windows being
+closed are applied to both sessions.
+The current and previous window and any session options remain independent and
+either session may be killed without affecting the other.
+Giving
+.Fl n
+or
+.Ar shell-command
+are invalid if
+.Fl t
+is used.
+.It Ic refresh-client Op Fl t Ar target-client
+.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 show-messages
+.Op Fl t Ar target-client
+.Xc
+.D1 (alias: Ic showmsgs )
+Any messages displayed on the status line are saved in a per-client message
+log, up to a maximum of the limit set by the
+.Ar message-limit
+session option for the session attached to that client.
+This command displays the log for
+.Ar target-client .
+.It Ic source-file Ar path
+.D1 (alias: Ic source )
+Execute commands from
+.Ar path .
+.It Ic start-server
+.D1 (alias: Ic start )
+Start the
+.Nm
+server, if not already running, without creating any sessions.
+.It Xo Ic suspend-client
+.Op Fl t Ar target-client
+.Xc
+.D1 (alias: Ic suspendc )
+Suspend a client by sending
+.Dv SIGTSTP
+(tty stop).
+.It Xo Ic switch-client
+.Op Fl lnp
+.Op Fl c Ar target-client
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic switchc )
+Switch the current session for client
+.Ar target-client
+to
+.Ar target-session .
+If
+.Fl l ,
+.Fl n
+or
+.Fl p
+is used, the client is moved to the last, next or previous session
+respectively.
+.El
+.Sh WINDOWS AND PANES
+A
+.Nm
+window may be in one of several modes.
+The default permits direct access to the terminal attached to the window.
+The other is copy mode, which 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.
+It is also entered when a command that produces output, such as
+.Ic list-keys ,
+is executed from a key binding.
+.Pp
+The keys available depend on whether emacs or vi mode is selected
+(see the
+.Ic mode-keys
+option).
+The following keys are supported as appropriate for the mode:
+.Bl -column "FunctionXXXXXXXXXXXXXXXXX" "viXXXXXXXXXX" "emacs" -offset indent
+.It Sy "Function" Ta Sy "vi" Ta Sy "emacs"
+.It Li "Back to indentation" Ta "^" Ta "M-m"
+.It Li "Bottom of history" Ta "G" Ta "M-<"
+.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 "Cursor left" Ta "h" Ta "Left"
+.It Li "Cursor right" Ta "l" Ta "Right"
+.It Li "Cursor to bottom line" Ta "L" Ta ""
+.It Li "Cursor to middle line" Ta "M" Ta "M-r"
+.It Li "Cursor to top line" Ta "H" Ta "M-R"
+.It Li "Cursor up" Ta "k" Ta "Up"
+.It Li "Delete entire line" Ta "d" Ta "C-u"
+.It Li "Delete/Copy to end of line" Ta "D" Ta "C-k"
+.It Li "End of line" Ta "$" Ta "C-e"
+.It Li "Go to line" Ta ":" Ta "g"
+.It Li "Half page down" Ta "C-d" Ta "M-Down"
+.It Li "Half page up" Ta "C-u" Ta "M-Up"
+.It Li "Jump forward" Ta "f" Ta "f"
+.It Li "Jump backward" Ta "F" Ta "F"
+.It Li "Jump again" Ta ";" Ta ";"
+.It Li "Jump again in reverse" Ta "," Ta ","
+.It Li "Next page" Ta "C-f" Ta "Page down"
+.It Li "Next space" Ta "W" Ta ""
+.It Li "Next space, end of word" Ta "E" Ta ""
+.It Li "Next word" Ta "w" Ta ""
+.It Li "Next word end" Ta "e" Ta "M-f"
+.It Li "Paste buffer" Ta "p" Ta "C-y"
+.It Li "Previous page" Ta "C-b" Ta "Page up"
+.It Li "Previous word" Ta "b" Ta "M-b"
+.It Li "Previous space" Ta "B" Ta ""
+.It Li "Quit mode" Ta "q" Ta "Escape"
+.It Li "Rectangle toggle" Ta "v" Ta "R"
+.It Li "Scroll down" Ta "C-Down or C-e" Ta "C-Down"
+.It Li "Scroll up" Ta "C-Up or C-y" Ta "C-Up"
+.It Li "Search again" Ta "n" Ta "n"
+.It Li "Search again in reverse" Ta "N" Ta "N"
+.It Li "Search backward" Ta "?" Ta "C-r"
+.It Li "Search forward" Ta "/" Ta "C-s"
+.It Li "Start of line" Ta "0" Ta "C-a"
+.It Li "Start selection" Ta "Space" Ta "C-Space"
+.It Li "Top of history" Ta "g" Ta "M->"
+.It Li "Transpose chars" Ta "" Ta "C-t"
+.El
+.Pp
+The next and previous word keys use space and the
+.Ql - ,
+.Ql _
+and
+.Ql @
+characters as word delimiters by default, but this can be adjusted by
+setting the
+.Em word-separators
+window option.
+Next word moves to the start of the next word, next word end to the end of the
+next word and previous word to the start of the previous word.
+The three next and previous space keys work similarly but use a space alone as
+the word separator.
+.Pp
+The jump commands enable quick movement within a line.
+For instance, typing
+.Ql f
+followed by
+.Ql /
+will move the cursor to the next
+.Ql /
+character on the current line.
+A
+.Ql \&;
+will then jump to the next occurrence.
+.Pp
+Commands in copy mode may be prefaced by an optional repeat count.
+With vi key bindings, a prefix is entered using the number keys; with
+emacs, the Alt (meta) key and a number begins prefix entry.
+For example, to move the cursor forward by ten words, use
+.Ql M-1 0 M-f
+in emacs mode, and
+.Ql 10w
+in vi.
+.Pp
+Mode key bindings are defined in a set of named tables:
+.Em vi-edit
+and
+.Em emacs-edit
+for keys used when line editing at the command prompt;
+.Em vi-choice
+and
+.Em emacs-choice
+for keys used when choosing from lists (such as produced by the
+.Ic choose-window
+command); and
+.Em vi-copy
+and
+.Em emacs-copy
+used in copy mode.
+The tables may be viewed with the
+.Ic list-keys
+command and keys modified or removed with
+.Ic bind-key
+and
+.Ic unbind-key .
+.Pp
+The paste buffer key pastes the first line from the top paste buffer on the
+stack.
+.Pp
+The synopsis for the
+.Ic copy-mode
+command is:
+.Bl -tag -width Ds
+.It Xo Ic copy-mode
+.Op Fl u
+.Op Fl t Ar target-pane
+.Xc
+Enter copy mode.
+The
+.Fl u
+option scrolls one page up.
+.El
+.Pp
+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.
+Windows may be split horizontally (with the
+.Fl h
+flag) or vertically.
+Panes may be resized with the
+.Ic resize-pane
+command (bound to
+.Ql C-up ,
+.Ql C-down
+.Ql C-left
+and
+.Ql C-right
+by default), the current pane may be changed with the
+.Ic select-pane
+command and the
+.Ic rotate-window
+and
+.Ic swap-pane
+commands may be used to swap panes without changing their position.
+Panes are numbered beginning from zero in the order they are created.
+.Pp
+A number of preset
+.Em layouts
+are available.
+These may be selected with the
+.Ic select-layout
+command or cycled with
+.Ic next-layout
+(bound to
+.Ql Space
+by default); once a layout is chosen, panes within it may be moved and resized
+as normal.
+.Pp
+The following layouts are supported:
+.Bl -tag -width Ds
+.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-horizontal
+A large (main) pane is shown at the top of the window and the remaining panes
+are spread from left to right in the leftover space at the bottom.
+Use the
+.Em main-pane-height
+window option to specify the height of the top pane.
+.It Ic main-vertical
+Similar to
+.Ic main-horizontal
+but the large pane is placed on the left and the others spread from top to
+bottom along the right.
+See the
+.Em main-pane-width
+window option.
+.It Ic tiled
+Panes are spread out as evenly as possible over the window in both rows and
+columns.
+.El
+.Pp
+In addition,
+.Ic select-layout
+may be used to apply a previously used layout - the
+.Ic list-windows
+command displays the layout of each window in a form suitable for use with
+.Ic select-layout .
+For example:
+.Bd -literal -offset indent
+$ tmux list-windows
+0: ksh [159x48]
+    layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0}
+$ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0}
+.Ed
+.Pp
+.Nm
+automatically adjusts the size of the layout for the current window size.
+Note that a layout cannot be applied to a window with more panes than that
+from which the layout was originally defined.
+.Pp
+Commands related to windows and panes are as follows:
+.Bl -tag -width Ds
+.It Xo Ic break-pane
+.Op Fl d
+.Op Fl t Ar target-pane
+.Xc
+.D1 (alias: Ic breakp )
+Break
+.Ar target-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 capture-pane
+.Op Fl b Ar buffer-index
+.Op Fl E Ar end-line
+.Op Fl S Ar start-line
+.Op Fl t Ar target-pane
+.Xc
+.D1 (alias: Ic capturep )
+Capture the contents of a pane to the specified buffer, or a new buffer if none
+is specified.
+.Pp
+.Fl S
+and
+.Fl E
+specify the starting and ending line numbers, zero is the first line of the
+visible pane and negative numbers are lines in the history.
+The default is to capture only the visible contents of the pane.
+.It Xo
+.Ic choose-client
+.Op Fl t Ar target-window
+.Op Ar template
+.Xc
+Put a window into client choice mode, allowing a client to be selected
+interactively from a list.
+After a client is chosen,
+.Ql %%
+is replaced by the client
+.Xr pty 4
+path in
+.Ar template
+and the result executed as a command.
+If
+.Ar template
+is not given, "detach-client -t '%%'" is used.
+This command works only from inside
+.Nm .
+.It Xo
+.Ic choose-session
+.Op Fl t Ar target-window
+.Op Ar template
+.Xc
+Put a window into session choice mode, where a session may be selected
+interactively from a list.
+When one is chosen,
+.Ql %%
+is replaced by the session name in
+.Ar template
+and the result executed as a command.
+If
+.Ar template
+is not given, "switch-client -t '%%'" is used.
+This command works only from inside
+.Nm .
+.It Xo
+.Ic choose-window
+.Op Fl t Ar target-window
+.Op Ar template
+.Xc
+Put a window into window choice mode, where a window may be chosen
+interactively from a list.
+After a window is selected,
+.Ql %%
+is replaced by the session name and window index in
+.Ar template
+and the result executed as a command.
+If
+.Ar template
+is not given, "select-window -t '%%'" is used.
+This command works only from inside
+.Nm .
+.It Ic display-panes Op Fl t Ar target-client
+.D1 (alias: Ic displayp)
+Display a visible indicator of each pane shown by
+.Ar target-client .
+See the
+.Ic display-panes-time ,
+.Ic display-panes-colour ,
+and
+.Ic display-panes-active-colour
+session options.
+While the indicator is on screen, a pane may be selected with the
+.Ql 0
+to
+.Ql 9
+keys.
+.It Xo Ic find-window
+.Op Fl t Ar target-window
+.Ar match-string
+.Xc
+.D1 (alias: Ic findw )
+Search for the
+.Xr fnmatch 3
+pattern
+.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 join-pane
+.Op Fl dhv
+.Oo Fl l
+.Ar size |
+.Fl p Ar percentage Oc
+.Op Fl s Ar src-pane
+.Op Fl t Ar dst-pane
+.Xc
+.D1 (alias: Ic joinp )
+Like
+.Ic split-window ,
+but instead of splitting
+.Ar dst-pane
+and creating a new pane, split it and move
+.Ar src-pane
+into the space.
+This can be used to reverse
+.Ic break-pane .
+.It Xo Ic kill-pane
+.Op Fl a
+.Op Fl t Ar target-pane
+.Xc
+.D1 (alias: Ic killp )
+Destroy the given pane.
+If no panes remain in the containing window, it is also destroyed.
+The
+.Fl a
+option kills all but the pane given with
+.Fl t .
+.It Ic kill-window Op Fl t Ar target-window
+.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 Ic last-pane Op Fl t Ar target-window
+.D1 (alias: Ic lastp )
+Select the last (previously selected) pane.
+.It Ic last-window Op Fl t Ar target-session
+.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-panes
+.Op Fl as
+.Op Fl t Ar target
+.Xc
+.D1 (alias: Ic lsp )
+If
+.Fl a
+is given,
+.Ar target
+is ignored and all panes on the server are listed.
+If
+.Fl s
+is given,
+.Ar target
+is a session (or the current session).
+If neither is given,
+.Ar target
+is a window (or the current window).
+.It Xo Ic list-windows
+.Op Fl a
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic lsw )
+If
+.Fl a
+is given, list all windows on the server.
+Otherwise, list windows in the current session or in
+.Ar target-session .
+.It Xo Ic move-window
+.Op Fl dk
+.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-window
+.Op Fl adkP
+.Op Fl n Ar window-name
+.Op Fl t Ar target-window
+.Op Ar shell-command
+.Xc
+.D1 (alias: Ic neww )
+Create a new window.
+With
+.Fl a ,
+the new window is inserted at the next index up from the specified
+.Ar target-window ,
+moving windows up if necessary,
+otherwise
+.Ar target-window
+is the new window location.
+.Pp
+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; if the target already exists an error is
+shown, unless the
+.Fl k
+flag is used, in which case it is destroyed.
+.Ar shell-command
+is the command to execute.
+If
+.Ar shell-command
+is not specified, the value of the
+.Ic default-command
+option is used.
+.Pp
+When the shell command completes, the window closes.
+See the
+.Ic remain-on-exit
+option to change this behaviour.
+.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.
+.Pp
+The
+.Fl P
+option prints the location of the new window after it has been created.
+.It Ic next-layout Op Fl t Ar target-window
+.D1 (alias: Ic nextl )
+Move a window to the next layout and rearrange the panes to fit.
+.It Xo Ic next-window
+.Op Fl a
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic next )
+Move to the next window in the session.
+If
+.Fl a
+is used, move to the next window with a bell, activity or content alert.
+.It Xo Ic pipe-pane
+.Op Fl o
+.Op Fl t Ar target-pane
+.Op Ar shell-command
+.Xc
+.D1 (alias: Ic pipep )
+Pipe any output sent by the program in
+.Ar target-pane
+to a shell command.
+A pane may only be piped to one command at a time, any existing pipe is
+closed before
+.Ar shell-command
+is executed.
+The
+.Ar shell-command
+string may contain the special character sequences supported by the
+.Ic status-left
+option.
+If no
+.Ar shell-command
+is given, the current pipe (if any) is closed.
+.Pp
+The
+.Fl o
+option only opens a new pipe if no previous pipe exists, allowing a pipe to
+be toggled with a single key, for example:
+.Bd -literal -offset indent
+bind-key C-p pipe-pane -o 'cat >>~/output.#I-#P'
+.Ed
+.It Xo Ic previous-layout
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic prevl )
+Move to the previous layout in the session.
+.It Xo Ic previous-window
+.Op Fl a
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic prev )
+Move to the previous window in the session.
+With
+.Fl a ,
+move to the previous window with a bell, activity or content alert.
+.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 DLRU
+.Op Fl t Ar target-pane
+.Op Ar adjustment
+.Xc
+.D1 (alias: Ic resizep )
+Resize a pane, upward with
+.Fl U
+(the default), downward with
+.Fl D ,
+to the left with
+.Fl L
+and to the right with
+.Fl R .
+The
+.Ar adjustment
+is given in lines or cells (the default is 1).
+.It Xo Ic respawn-pane
+.Op Fl k
+.Op Fl t Ar target-pane
+.Op Ar shell-command
+.Xc
+.D1 (alias: Ic respawnp )
+Reactivate a pane in which the command has exited (see the
+.Ic remain-on-exit
+window option).
+If
+.Ar shell-command
+is not given, the command used when the pane was created is executed.
+The pane must be already inactive, unless
+.Fl k
+is given, in which case any existing command is killed.
+.It Xo Ic respawn-window
+.Op Fl k
+.Op Fl t Ar target-window
+.Op Ar shell-command
+.Xc
+.D1 (alias: Ic respawnw )
+Reactivate a window in which the command has exited (see the
+.Ic remain-on-exit
+window option).
+If
+.Ar shell-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 select-layout
+.Op Fl np
+.Op Fl t Ar target-window
+.Op Ar layout-name
+.Xc
+.D1 (alias: Ic selectl )
+Choose a specific layout for a window.
+If
+.Ar layout-name
+is not given, the last preset layout used (if any) is reapplied.
+.Fl n
+and
+.Fl p
+are equivalent to the
+.Ic next-layout
+and
+.Ic previous-layout
+commands.
+.It Xo Ic select-pane
+.Op Fl lDLRU
+.Op Fl t Ar target-pane
+.Xc
+.D1 (alias: Ic selectp )
+Make pane
+.Ar target-pane
+the active pane in window
+.Ar target-window .
+If one of
+.Fl D ,
+.Fl L ,
+.Fl R ,
+or
+.Fl U
+is used, respectively the pane below, to the left, to the right, or above the
+target pane is used.
+.Fl l
+is the same as using the
+.Ic last-pane
+command.
+.It Xo Ic select-window
+.Op Fl lnp
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic selectw )
+Select the window at
+.Ar target-window .
+.Fl l ,
+.Fl n
+and
+.Fl p
+are equivalent to the
+.Ic last-window ,
+.Ic next-window
+and
+.Ic previous-window
+commands.
+.It Xo Ic split-window
+.Op Fl dhvP
+.Oo Fl l
+.Ar size |
+.Fl p Ar percentage Oc
+.Op Fl t Ar target-pane
+.Op Ar shell-command
+.Xc
+.D1 (alias: Ic splitw )
+Create a new pane by splitting
+.Ar target-pane :
+.Fl h
+does a horizontal split and
+.Fl v
+a vertical split; if neither is specified,
+.Fl v
+is assumed.
+The
+.Fl l
+and
+.Fl p
+options specify the size of the new pane in lines (for vertical split) or in
+cells (for horizontal split), or as a percentage, respectively.
+All other options have the same meaning as for the
+.Ic new-window
+command.
+.It Xo Ic swap-pane
+.Op Fl dDU
+.Op Fl s Ar src-pane
+.Op Fl t Ar dst-pane
+.Xc
+.D1 (alias: Ic swapp )
+Swap two panes.
+If
+.Fl U
+is used and no source pane is specified with
+.Fl s ,
+.Ar dst-pane
+is swapped with the previous pane (before it numerically);
+.Fl D
+swaps with the next pane (after it numerically).
+.Fl d
+instructs
+.Nm
+not to change the active 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 unlink-window
+.Op Fl k
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic unlinkw )
+Unlink
+.Ar target-window .
+Unless
+.Fl k
+is given, a window may be unlinked only if it is linked to multiple sessions -
+windows may not be linked to no sessions;
+if
+.Fl k
+is specified and the window is linked to only one session, it is unlinked and
+destroyed.
+.El
+.Sh KEY BINDINGS
+.Nm
+allows a command to be bound to most keys, with or without a prefix key.
+When specifying keys, most represent themselves (for example
+.Ql A
+to
+.Ql Z ) .
+Ctrl keys may be prefixed with
+.Ql C-
+or
+.Ql ^ ,
+and Alt (meta) with
+.Ql M- .
+In addition, the following special key names are accepted:
+.Em Up ,
+.Em Down ,
+.Em Left ,
+.Em Right ,
+.Em BSpace ,
+.Em BTab ,
+.Em DC
+(Delete),
+.Em End ,
+.Em Enter ,
+.Em Escape ,
+.Em F1
+to
+.Em F20 ,
+.Em Home ,
+.Em IC
+(Insert),
+.Em NPage
+(Page Up),
+.Em PPage
+(Page Down),
+.Em Space ,
+and
+.Em Tab .
+Note that to bind the
+.Ql \&"
+or
+.Ql '
+keys, quotation marks are necessary, for example:
+.Bd -literal -offset indent
+bind-key '"' split-window
+bind-key "'" new-window
+.Ed
+.Pp
+Commands related to key bindings are as follows:
+.Bl -tag -width Ds
+.It Xo Ic bind-key
+.Op Fl cnr
+.Op Fl t Ar key-table
+.Ar key Ar command Op Ar arguments
+.Xc
+.D1 (alias: Ic bind )
+Bind key
+.Ar key
+to
+.Ar command .
+By default (without
+.Fl t )
+the primary key bindings are modified (those normally activated with the prefix
+key); in this case, if
+.Fl n
+is specified, it is not necessary to use the prefix key,
+.Ar command
+is bound to
+.Ar key
+alone.
+The
+.Fl r
+flag indicates this key may repeat, see the
+.Ic repeat-time
+option.
+.Pp
+If
+.Fl t
+is present,
+.Ar key
+is bound in
+.Ar key-table :
+the binding for command mode with
+.Fl c
+or for normal mode without.
+To view the default bindings and possible commands, see the
+.Ic list-keys
+command.
+.It Ic list-keys Op Fl t Ar key-table
+.D1 (alias: Ic lsk )
+List all key bindings.
+Without
+.Fl t
+the primary key bindings - those executed when preceded by the prefix key -
+are printed.
+Keys bound without the prefix key (see
+.Ic bind-key
+.Fl n )
+are marked with
+.Ql (no prefix) .
+.Pp
+With
+.Fl t ,
+the key bindings in
+.Ar key-table
+are listed; this may be one of:
+.Em vi-edit ,
+.Em emacs-edit ,
+.Em vi-choice ,
+.Em emacs-choice ,
+.Em vi-copy
+or
+.Em emacs-copy .
+.It Xo Ic send-keys
+.Op Fl t Ar target-pane
+.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 Ic send-prefix Op Fl t Ar target-pane
+Send the prefix key to a window as if it was pressed.
+If multiple prefix keys are configured, only the first is sent.
+.It Xo Ic unbind-key
+.Op Fl acn
+.Op Fl t Ar key-table
+.Ar key
+.Xc
+.D1 (alias: Ic unbind )
+Unbind the command bound to
+.Ar key .
+Without
+.Fl t
+the primary key bindings are modified; in this case, if
+.Fl n
+is specified, the command bound to
+.Ar key
+without a prefix (if any) is removed.
+If
+.Fl a
+is present, all key bindings are removed.
+.Pp
+If
+.Fl t
+is present,
+.Ar key
+in
+.Ar key-table
+is unbound: the binding for command mode with
+.Fl c
+or for normal mode without.
+.El
+.Sh OPTIONS
+The appearance and behaviour of
+.Nm
+may be modified by changing the value of various options.
+There are three types of option:
+.Em server options ,
+.Em session options
+and
+.Em window options .
+.Pp
+The
+.Nm
+server has a set of global options which do not apply to any particular
+window or session.
+These are altered with the
+.Ic set-option
+.Fl s
+command, or displayed with the
+.Ic show-options
+.Fl s
+command.
+.Pp
+In addition, each individual session may have a set of session options, and
+there is a separate set of global session options.
+Sessions which do not have a particular option configured inherit the value
+from the global session options.
+Session options are set or unset with the
+.Ic set-option
+command and may be listed with the
+.Ic show-options
+command.
+The available server and session options are listed under the
+.Ic set-option
+command.
+.Pp
+Similarly, a set of window options is attached to each window, and there is
+a set of global window options from which any unset options are inherited.
+Window options are altered with the
+.Ic set-window-option
+command and can be listed with the
+.Ic show-window-options
+command.
+All window options are documented with the
+.Ic set-window-option
+command.
+.Pp
+Commands which set options are as follows:
+.Bl -tag -width Ds
+.It Xo Ic set-option
+.Op Fl agsuw
+.Op Fl t Ar target-session | Ar target-window
+.Ar option Ar value
+.Xc
+.D1 (alias: Ic set )
+Set a window option with
+.Fl w
+(equivalent to the
+.Ic set-window-option
+command),
+a server option with
+.Fl s ,
+otherwise a session option.
+.Pp
+If
+.Fl g
+is specified, the global session or window option is set.
+With
+.Fl a ,
+and if the option expects a string,
+.Ar value
+is appended to the existing setting.
+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
+Available window options are listed under
+.Ic set-window-option .
+.Pp
+Available server options are:
+.Bl -tag -width Ds
+.It Ic buffer-limit Ar number
+Set the number of buffers; 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 Xo Ic set-clipboard
+.Op Ic on | off
+.Xc
+Attempt to set the terminal clipboard content using the
+\ee]52;...\e007
+.Xr xterm 1
+escape sequences.
+This option is on by default if there is an
+.Em \&Ms
+entry in the
+.Xr terminfo 5
+description for the client terminal.
+Note that this feature needs to be enabled in
+.Xr xterm 1
+by setting the resource:
+.Bd -literal -offset indent
+disallowedWindowOps: 20,21,SetXprop
+.Ed
+.Pp
+Or changing this property from the
+.Xr xterm 1
+interactive menu when required.
+.It Ic escape-time Ar time
+Set the time in milliseconds for which
+.Nm
+waits after an escape is input to determine if it is part of a function or meta
+key sequences.
+The default is 500 milliseconds.
+.It Xo Ic exit-unattached
+.Op Ic on | off
+.Xc
+If enabled, the server will exit when there are no attached clients.
+.It Xo Ic quiet
+.Op Ic on | off
+.Xc
+Enable or disable the display of various informational messages (see also the
+.Fl q
+command line flag).
+.El
+.Pp
+Available session options are:
+.Bl -tag -width Ds
+.It Ic base-index Ar index
+Set the base index from which an unused index should be searched when a new
+window is created.
+The default is zero.
+.It Xo Ic bell-action
+.Op Ic any | none | 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 Xo Ic bell-on-alert
+.Op Ic on | off
+.Xc
+If on, ring the terminal bell when an activity, content or silence alert
+occurs.
+.It Ic default-command Ar shell-command
+Set the command used for new windows (if not specified when the window is
+created) to
+.Ar shell-command ,
+which may be any
+.Xr sh 1
+command.
+The default is an empty string, which instructs
+.Nm
+to create a login shell using the value of the
+.Ic default-shell
+option.
+.It Ic default-path Ar path
+Set the default working directory for processes created from keys, or
+interactively from the prompt.
+The default is empty, which means to use the working directory of the shell
+from which the server was started if it is available or the user's home if not.
+.It Ic default-shell Ar path
+Specify the default shell.
+This is used as the login shell for new windows when the
+.Ic default-command
+option is set to empty, and must be the full path of the executable.
+When started
+.Nm
+tries to set a default value from the first suitable of the
+.Ev SHELL
+environment variable, the shell returned by
+.Xr getpwuid 3 ,
+or
+.Pa /bin/sh .
+This option should be configured when
+.Nm
+is used as a login shell.
+.It Ic default-terminal Ar terminal
+Set the default terminal for new windows created in this session - the
+default value of the
+.Ev TERM
+environment variable.
+For
+.Nm
+to work correctly, this
+.Em must
+be set to
+.Ql screen
+or a derivative of it.
+.It Xo Ic destroy-unattached
+.Op Ic on | off
+.Xc
+If enabled and the session is no longer attached to any clients, it is
+destroyed.
+.It Xo Ic detach-on-destroy
+.Op Ic on | off
+.Xc
+If on (the default), the client is detached when the session it is attached to
+is destroyed.
+If off, the client is switched to the most recently active of the remaining
+sessions.
+.It Ic display-panes-active-colour Ar colour
+Set the colour used by the
+.Ic display-panes
+command to show the indicator for the active pane.
+.It Ic display-panes-colour Ar colour
+Set the colour used by the
+.Ic display-panes
+command to show the indicators for inactive panes.
+.It Ic display-panes-time Ar time
+Set the time in milliseconds for which the indicators shown by the
+.Ic display-panes
+command appear.
+.It Ic display-time Ar time
+Set the amount of time for which status line messages and other on-screen
+indicators are displayed.
+.Ar time
+is in milliseconds.
+.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 session (like the
+.Ic lock-session
+command) after
+.Ar number
+seconds of inactivity, or the entire server (all sessions) if the
+.Ic lock-server
+option is set.
+The default is not to lock (set to 0).
+.It Ic lock-command Ar shell-command
+Command to run when locking each client.
+The default is to run
+.Xr lock 1
+with
+.Fl np .
+.It Xo Ic lock-server
+.Op Ic on | off
+.Xc
+If this option is
+.Ic on
+(the default),
+instead of each session locking individually as each has been
+idle for
+.Ic lock-after-time ,
+the entire server will lock after
+.Em all
+sessions would have locked.
+This has no effect as a session option; it must be set as a global option.
+.It Ic message-attr Ar attributes
+Set status line message attributes, where
+.Ar attributes
+is either
+.Ic none
+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 ,
+.Ic colour0
+to
+.Ic colour255
+from the 256-colour set,
+.Ic default ,
+or a hexadecimal RGB string such as
+.Ql #ffffff ,
+which chooses the closest match from the default 256-colour set.
+.It Ic message-fg Ar colour
+Set status line message foreground colour.
+.It Ic message-limit Ar number
+Set the number of error or information messages to save in the message log for
+each client.
+The default is 20.
+.It Xo Ic mouse-resize-pane
+.Op Ic on | off
+.Xc
+If on,
+.Nm
+captures the mouse and allows panes to be resized by dragging on their borders.
+.It Xo Ic mouse-select-pane
+.Op Ic on | off
+.Xc
+If on,
+.Nm
+captures the mouse and when a window is split into multiple panes the mouse may
+be used to select the current pane.
+The mouse click is also passed through to the application as normal.
+.It Xo Ic mouse-select-window
+.Op Ic on | off
+.Xc
+If on, clicking the mouse on a window name in the status line will select that
+window.
+.It Ic pane-active-border-bg Ar colour
+.It Ic pane-active-border-fg Ar colour
+Set the pane border colour for the currently active pane.
+.It Ic pane-border-bg Ar colour
+.It Ic pane-border-fg Ar colour
+Set the pane border colour for panes aside from the active pane.
+.It Ic prefix Ar keys
+Set the keys accepted as a prefix key.
+.Ar keys
+is a comma-separated list of key names, each of which individually behave as
+the prefix key.
+.It Ic repeat-time Ar time
+Allow multiple commands to be entered without pressing the prefix-key again
+in the specified
+.Ar time
+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 bound to the
+.Ic resize-pane
+command.
+.It Xo Ic mouse-utf8
+.Op Ic on | off
+.Xc
+If enabled, request mouse input as UTF-8 on UTF-8 terminals.
+.It Xo Ic set-remain-on-exit
+.Op Ic on | off
+.Xc
+Set the
+.Ic remain-on-exit
+window option for any windows first created in this session.
+When this option is true, windows in which the running program has
+exited do not close, instead remaining open but inactivate.
+Use the
+.Ic respawn-window
+command to reactivate such a window, or the
+.Ic kill-window
+command to destroy it.
+.It Xo Ic set-titles
+.Op Ic on | off
+.Xc
+Attempt to set the window title using the \ee]2;...\e007 xterm code if
+the terminal appears to be an xterm.
+This option is off by default.
+Note that elinks
+will only attempt to set the window title if the STY environment
+variable is set.
+.It Ic set-titles-string Ar string
+String used to set the window title if
+.Ic set-titles
+is on.
+Character sequences are replaced as for the
+.Ic status-left
+option.
+.It Xo Ic status
+.Op Ic on | 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-justify
+.Op Ic left | centre | right
+.Xc
+Set the position of the window list component of the status line: left, centre
+or right justified.
+.It Xo Ic status-keys
+.Op Ic vi | emacs
+.Xc
+Use vi or emacs-style
+key bindings in the status line, for example at the command prompt.
+The default is emacs, unless the
+.Ev VISUAL
+or
+.Ev EDITOR
+environment variables are set and contain the string
+.Ql vi .
+.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 sequences:
+.Bl -column "Character pair" "Replaced with" -offset indent
+.It Sy "Character pair" Ta Sy "Replaced with"
+.It Li "#(shell-command)" Ta "First line of the command's output"
+.It Li "#[attributes]" Ta "Colour or attribute change"
+.It Li "#H" Ta "Hostname of local host"
+.It Li "#h" Ta "Hostname of local host without the domain name"
+.It Li "#F" Ta "Current window flag"
+.It Li "#I" Ta "Current window index"
+.It Li "#P" Ta "Current pane index"
+.It Li "#S" Ta "Session name"
+.It Li "#T" Ta "Current window title"
+.It Li "#W" Ta "Current window name"
+.It Li "##" Ta "A literal" Ql #
+.El
+.Pp
+The #(shell-command) form executes
+.Ql shell-command
+and inserts the first line of its output.
+Note that shell commands are only executed once at the interval specified by
+the
+.Ic status-interval
+option: if the status line is redrawn in the meantime, the previous result is
+used.
+Shell commands are executed with the
+.Nm
+global environment set (see the
+.Sx ENVIRONMENT
+section).
+.Pp
+The window title (#T) is the title set by the program running within the window
+using the OSC title setting sequence, for example:
+.Bd -literal -offset indent
+$ printf '\e033]2;My Title\e033\e\e'
+.Ed
+.Pp
+When a window is first created, its title is the hostname.
+.Pp
+#[attributes] allows a comma-separated list of attributes to be specified,
+these may be
+.Ql fg=colour
+to set the foreground colour,
+.Ql bg=colour
+to set the background colour, the name of one of the attributes (listed under
+the
+.Ic message-attr
+option) to turn an attribute on, or an attribute prefixed with
+.Ql no
+to turn one off, for example
+.Ic nobright .
+Examples are:
+.Bd -literal -offset indent
+#(sysctl vm.loadavg)
+#[fg=yellow,bold]#(apm -l)%%#[default] [#S]
+.Ed
+.Pp
+Where appropriate, special character sequences may be prefixed with a number to
+specify the maximum length, for example
+.Ql #24T .
+.Pp
+By default, UTF-8 in
+.Ar string
+is not interpreted, to enable UTF-8, use the
+.Ic status-utf8
+option.
+.It Ic status-left-attr Ar attributes
+Set the attribute of the left part of the status line.
+.It Ic status-left-bg Ar colour
+Set the background colour of the left part of the status line.
+.It Ic status-left-fg Ar colour
+Set the foreground colour of the left part of the status line.
+.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 current window title in double quotes, the date and the time
+are shown.
+As with
+.Ic status-left ,
+.Ar string
+will be passed to
+.Xr strftime 3 ,
+character pairs are replaced, and UTF-8 is dependent on the
+.Ic status-utf8
+option.
+.It Ic status-right-attr Ar attributes
+Set the attribute of the right part of the status line.
+.It Ic status-right-bg Ar colour
+Set the background colour of the right part of the status line.
+.It Ic status-right-fg Ar colour
+Set the foreground colour of the right part of the status line.
+.It Ic status-right-length Ar length
+Set the maximum
+.Ar length
+of the right component of the status bar.
+The default is 40.
+.It Xo Ic status-utf8
+.Op Ic on | off
+.Xc
+Instruct
+.Nm
+to treat top-bit-set characters in the
+.Ic status-left
+and
+.Ic status-right
+strings as UTF-8; notably, this is important for wide characters.
+This option defaults to off.
+.It Ic terminal-overrides Ar string
+Contains a list of entries which override terminal descriptions read using
+.Xr terminfo 5 .
+.Ar string
+is a comma-separated list of items each a colon-separated string made up of a
+terminal type pattern (matched using
+.Xr fnmatch 3 )
+and a set of
+.Em name=value
+entries.
+.Pp
+For example, to set the
+.Ql clear
+.Xr terminfo 5
+entry to
+.Ql \ee[H\ee[2J
+for all terminal types and the
+.Ql dch1
+entry to
+.Ql \ee[P
+for the
+.Ql rxvt
+terminal type, the option could be set to the string:
+.Bd -literal -offset indent
+"*:clear=\ee[H\ee[2J,rxvt:dch1=\ee[P"
+.Ed
+.Pp
+The terminal entry value is passed through
+.Xr strunvis 3
+before interpretation.
+The default value forcibly corrects the
+.Ql colors
+entry for terminals which support 88 or 256 colours:
+.Bd -literal -offset indent
+"*88col*:colors=88,*256col*:colors=256,xterm*:XT"
+.Ed
+.It Ic update-environment Ar variables
+Set a space-separated string containing a list of environment variables to be
+copied into the session environment when a new session is created or an
+existing session is attached.
+Any variables that do not exist in the source environment are set to be
+removed from the session environment (as if
+.Fl r
+was given to the
+.Ic set-environment
+command).
+The default is
+"DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID
+XAUTHORITY".
+.It Xo Ic visual-activity
+.Op Ic on | off
+.Xc
+If on, display a status line message when activity occurs in a window
+for which the
+.Ic monitor-activity
+window option is enabled.
+.It Xo Ic visual-bell
+.Op Ic on | off
+.Xc
+If this option is on, a message is shown on a bell instead of it being passed
+through to the terminal (which normally makes a sound).
+Also see the
+.Ic bell-action
+option.
+.It Xo Ic visual-content
+.Op Ic on | off
+.Xc
+Like
+.Ic visual-activity ,
+display a message when content is present in a window
+for which the
+.Ic monitor-content
+window option is enabled.
+.It Xo Ic visual-silence
+.Op Ic on | off
+.Xc
+If
+.Ic monitor-silence
+is enabled, prints a message after the interval has expired on a given window.
+.El
+.It Xo Ic set-window-option
+.Op Fl agu
+.Op Fl t Ar target-window
+.Ar option Ar value
+.Xc
+.D1 (alias: Ic setw )
+Set a window option.
+The
+.Fl a ,
+.Fl g
+and
+.Fl u
+flags work similarly to the
+.Ic set-option
+command.
+.Pp
+Supported window options are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Xo Ic aggressive-resize
+.Op Ic on | 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
+.Dv SIGWINCH
+and poor for interactive programs such as shells.
+.Pp
+.It Xo Ic alternate-screen
+.Op Ic on | off
+.Xc
+This option configures whether programs running inside
+.Nm
+may use the terminal alternate screen feature, which allows the
+.Em smcup
+and
+.Em rmcup
+.Xr terminfo 5
+capabilities.
+The alternate screen feature preserves the contents of the window when an
+interactive application starts and restores it on exit, so that any output
+visible before the application starts reappears unchanged after it exits.
+The default is on.
+.Pp
+.It Xo Ic automatic-rename
+.Op Ic on | 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
+.Pp
+.It Ic clock-mode-colour Ar colour
+Set clock colour.
+.Pp
+.It Xo Ic clock-mode-style
+.Op Ic 12 | 24
+.Xc
+Set clock hour format.
+.Pp
+.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.
+.Pp
+.It Ic main-pane-height Ar height
+.It Ic main-pane-width Ar width
+Set the width or height of the main (left or top) pane in the
+.Ic main-horizontal
+or
+.Ic main-vertical
+layouts.
+.Pp
+.It Ic mode-attr Ar attributes
+Set window modes attributes.
+.Pp
+.It Ic mode-bg Ar colour
+Set window modes background colour.
+.Pp
+.It Ic mode-fg Ar colour
+Set window modes foreground colour.
+.Pp
+.It Xo Ic mode-keys
+.Op Ic vi | emacs
+.Xc
+Use vi or emacs-style key bindings in copy and choice modes.
+As with the
+.Ic status-keys
+option, the default is emacs, unless
+.Ev VISUAL
+or
+.Ev EDITOR
+contains
+.Ql vi .
+.Pp
+.It Xo Ic mode-mouse
+.Op Ic on | off
+.Xc
+Mouse state in modes.
+If on, the mouse may be used to enter copy mode and copy a selection by
+dragging, to enter copy mode and scroll with the mouse wheel, or to select an
+option in choice mode.
+.Pp
+.It Xo Ic monitor-activity
+.Op Ic on | off
+.Xc
+Monitor for activity in the window.
+Windows with activity are highlighted in the status line.
+.Pp
+.It Ic monitor-content Ar match-string
+Monitor content in the window.
+When
+.Xr fnmatch 3
+pattern
+.Ar match-string
+appears in the window, it is highlighted in the status line.
+.Pp
+.It Xo Ic monitor-silence
+.Op Ic interval
+.Xc
+Monitor for silence (no activity) in the window within
+.Ic interval
+seconds.
+Windows that have been silent for the interval are highlighted in the
+status line.
+An interval of zero disables the monitoring.
+.Pp
+.It Ic other-pane-height Ar height
+Set the height of the other panes (not the main pane) in the
+.Ic main-horizontal
+layout.
+If this option is set to 0 (the default), it will have no effect.
+If both the
+.Ic main-pane-height
+and
+.Ic other-pane-height
+options are set, the main pane will grow taller to make the other panes the
+specified height, but will never shrink to do so.
+.Pp
+.It Ic other-pane-width Ar width
+Like
+.Ic other-pane-height ,
+but set the width of other panes in the
+.Ic main-vertical
+layout.
+.Pp
+.It Xo Ic remain-on-exit
+.Op Ic on | 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.
+.Pp
+.It Xo Ic synchronize-panes
+.Op Ic on | off
+.Xc
+Duplicate input to any pane to all other panes in the same window (only
+for panes that are not in any special mode).
+.Pp
+.It Xo Ic utf8
+.Op Ic on | off
+.Xc
+Instructs
+.Nm
+to expect UTF-8 sequences to appear in this window.
+.Pp
+.It Ic window-status-attr Ar attributes
+Set status line attributes for a single window.
+.Pp
+.It Ic window-status-bg Ar colour
+Set status line background colour for a single window.
+.Pp
+.It Ic window-status-fg Ar colour
+Set status line foreground colour for a single window.
+.Pp
+.It Ic window-status-format Ar string
+Set the format in which the window is displayed in the status line window list.
+See the
+.Ar status-left
+option for details of special character sequences available.
+The default is
+.Ql #I:#W#F .
+.Pp
+.It Ic window-status-alert-attr Ar attributes
+Set status line attributes for windows which have an alert (bell, activity
+or content).
+.Pp
+.It Ic window-status-alert-bg Ar colour
+Set status line background colour for windows with an alert.
+.Pp
+.It Ic window-status-alert-fg Ar colour
+Set status line foreground colour for windows with an alert.
+.Pp
+.It Ic window-status-current-attr Ar attributes
+Set status line attributes for the currently active window.
+.Pp
+.It Ic window-status-current-bg Ar colour
+Set status line background colour for the currently active window.
+.Pp
+.It Ic window-status-current-fg Ar colour
+Set status line foreground colour for the currently active window.
+.Pp
+.It Ic window-status-current-format Ar string
+Like
+.Ar window-status-format ,
+but is the format used when the window is the current window.
+.Pp
+.It Ic word-separators Ar string
+Sets the window's conception of what characters are considered word
+separators, for the purposes of the next and previous word commands in
+copy mode.
+The default is
+.Ql \ -_@ .
+.Pp
+.It Xo Ic xterm-keys
+.Op Ic on | 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, Alt or Ctrl.
+The default is off.
+.El
+.It Xo Ic show-options
+.Op Fl gsw
+.Op Fl t Ar target-session | Ar target-window
+.Xc
+.D1 (alias: Ic show )
+Show the window options with
+.Fl w
+(equivalent to
+.Ic show-window-options ) ,
+the server options with
+.Fl s ,
+otherwise the session options for
+.Ar target session .
+Global session or window options are listed if
+.Fl g
+is used.
+.It Xo Ic show-window-options
+.Op Fl g
+.Op Fl t Ar target-window
+.Xc
+.D1 (alias: Ic showw )
+List the window options for
+.Ar target-window ,
+or the global window options if
+.Fl g
+is used.
+.El
+.Sh ENVIRONMENT
+When the server is started,
+.Nm
+copies the environment into the
+.Em global environment ;
+in addition, each session has a
+.Em session environment .
+When a window is created, the session and global environments are merged.
+If a variable exists in both, the value from the session environment is used.
+The result is the initial environment passed to the new process.
+.Pp
+The
+.Ic update-environment
+session option may be used to update the session environment from the client
+when a new session is created or an old reattached.
+.Nm
+also initialises the
+.Ev TMUX
+variable with some internal information to allow commands to be executed
+from inside, and the
+.Ev TERM
+variable with the correct terminal setting of
+.Ql screen .
+.Pp
+Commands to alter and view the environment are:
+.Bl -tag -width Ds
+.It Xo Ic set-environment
+.Op Fl gru
+.Op Fl t Ar target-session
+.Ar name Op Ar value
+.Xc
+.D1 (alias: Ic setenv )
+Set or unset an environment variable.
+If
+.Fl g
+is used, the change is made in the global environment; otherwise, it is applied
+to the session environment for
+.Ar target-session .
+The
+.Fl u
+flag unsets a variable.
+.Fl r
+indicates the variable is to be removed from the environment before starting a
+new process.
+.It Xo Ic show-environment
+.Op Fl g
+.Op Fl t Ar target-session
+.Xc
+.D1 (alias: Ic showenv )
+Display the environment for
+.Ar target-session
+or the global environment with
+.Fl g .
+Variables removed from the environment are prefixed with
+.Ql - .
+.El
+.Sh STATUS LINE
+.Nm
+includes an optional status line which is displayed in the bottom line of each
+terminal.
+By default, the status line is enabled (it may be disabled with the
+.Ic status
+session option) and contains, from left-to-right: the name of the current
+session in square brackets; the window list; the current window title in double
+quotes; and the time and date.
+.Pp
+The status line is made of three parts: configurable left and right sections
+(which may contain dynamic content such as the time or output from a shell
+command, see the
+.Ic status-left ,
+.Ic status-left-length ,
+.Ic status-right ,
+and
+.Ic status-right-length
+options below), and a central window list.
+By default, the window list shows the index, name and (if any) flag of the
+windows present in the current session in ascending numerical order.
+It may be customised with the
+.Ar window-status-format
+and
+.Ar window-status-current-format
+options.
+The flag is one of the following symbols appended to the window name:
+.Bl -column "Symbol" "Meaning" -offset indent
+.It Sy "Symbol" Ta Sy "Meaning"
+.It Li "*" Ta "Denotes the current window."
+.It Li "-" Ta "Marks the last window (previously selected)."
+.It Li "#" Ta "Window is monitored and activity has been detected."
+.It Li "!" Ta "A bell has occurred in the window."
+.It Li "+" Ta "Window is monitored for content and it has appeared."
+.It Li "~" Ta "The window has been silent for the monitor-silence interval."
+.El
+.Pp
+The # symbol relates to the
+.Ic monitor-activity
+and + to the
+.Ic monitor-content
+window options.
+The window name is printed in inverted colours if an alert (bell, activity or
+content) is present.
+.Pp
+The colour and attributes of the status line may be configured, the entire
+status line using the
+.Ic status-attr ,
+.Ic status-fg
+and
+.Ic status-bg
+session options and individual windows using the
+.Ic window-status-attr ,
+.Ic window-status-fg
+and
+.Ic window-status-bg
+window options.
+.Pp
+The status line is automatically refreshed at interval if it has changed, the
+interval may be controlled with the
+.Ic status-interval
+session option.
+.Pp
+Commands related to the status line are as follows:
+.Bl -tag -width Ds
+.It Xo Ic command-prompt
+.Op Fl I Ar inputs
+.Op Fl p Ar prompts
+.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.
+.Pp
+If
+.Ar template
+is specified, it is used as the command.
+If present,
+.Fl I
+is a comma-separated list of the initial text for each prompt.
+If
+.Fl p
+is given,
+.Ar prompts
+is a comma-separated list of prompts which are displayed in order; otherwise
+a single prompt is displayed, constructed from
+.Ar template
+if it is present, or
+.Ql \&:
+if not.
+.Pp
+Both
+.Ar inputs
+and
+.Ar prompts
+may contain the special character sequences supported by the
+.Ic status-left
+option.
+.Pp
+Before the command is executed, the first occurrence of the string
+.Ql %%
+and all occurrences of
+.Ql %1
+are replaced by the response to the first prompt, the second
+.Ql %%
+and all
+.Ql %2
+are replaced with the response to the second prompt, and so on for further
+prompts.
+Up to nine prompt responses may be replaced
+.Po
+.Ql %1
+to
+.Ql %9
+.Pc .
+.It Xo Ic confirm-before
+.Op Fl p Ar prompt
+.Op Fl t Ar target-client
+.Ar command
+.Xc
+.D1 (alias: Ic confirm )
+Ask for confirmation before executing
+.Ar command .
+If
+.Fl p
+is given,
+.Ar prompt
+is the prompt to display; otherwise a prompt is constructed from
+.Ar command .
+It may contain the special character sequences supported by the
+.Ic status-left
+option.
+.Pp
+This command works only from inside
+.Nm .
+.It Xo Ic display-message
+.Op Fl p
+.Op Fl c Ar target-client
+.Op Fl t Ar target-pane
+.Op Ar message
+.Xc
+.D1 (alias: Ic display )
+Display a message.
+If
+.Fl p
+is given, the output is printed to stdout, otherwise it is displayed in the
+.Ar target-client
+status line.
+The format of
+.Ar message
+is as for
+.Ic status-left ,
+with the exception that #() are not handled; information is taken from
+.Ar target-pane
+if
+.Fl t
+is given, otherwise the active pane for the session attached to
+.Ar target-client .
+.El
+.Sh BUFFERS
+.Nm
+maintains a stack of
+.Em paste buffers .
+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.
+.Pp
+A configurable history buffer is also maintained 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 above).
+.Pp
+The buffer commands are as follows:
+.Bl -tag -width Ds
+.It Xo
+.Ic choose-buffer
+.Op Fl t Ar target-window
+.Op Ar template
+.Xc
+Put a window into buffer choice mode, where a buffer may be chosen
+interactively from a list.
+After a buffer is selected,
+.Ql %%
+is replaced by the buffer index in
+.Ar template
+and the result executed as a command.
+If
+.Ar template
+is not given, "paste-buffer -b '%%'" is used.
+This command works only from inside
+.Nm .
+.It Ic clear-history Op Fl t Ar target-pane
+.D1 (alias: Ic clearhist )
+Remove and free the history for the specified pane.
+.It Ic delete-buffer Op Fl b Ar buffer-index
+.D1 (alias: Ic deleteb )
+Delete the buffer at
+.Ar buffer-index ,
+or the top buffer if not specified.
+.It Ic list-buffers
+.D1 (alias: Ic lsb )
+List the global buffers.
+.It Xo Ic load-buffer
+.Op Fl b Ar buffer-index
+.Ar path
+.Xc
+.D1 (alias: Ic loadb )
+Load the contents of the specified paste buffer from
+.Ar path .
+.It Xo Ic paste-buffer
+.Op Fl dr
+.Op Fl b Ar buffer-index
+.Op Fl s Ar separator
+.Op Fl t Ar target-pane
+.Xc
+.D1 (alias: Ic pasteb )
+Insert the contents of a paste buffer into the specified pane.
+If not specified, paste into the current one.
+With
+.Fl d ,
+also delete the paste buffer from the stack.
+When output, any linefeed (LF) characters in the paste buffer are replaced with
+a separator, by default carriage return (CR).
+A custom separator may be specified using the
+.Fl s
+flag.
+The
+.Fl r
+flag means to do no replacement (equivalent to a separator of LF).
+.It Xo Ic save-buffer
+.Op Fl a
+.Op Fl b Ar buffer-index
+.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 set-buffer
+.Op Fl b Ar buffer-index
+.Ar data
+.Xc
+.D1 (alias: Ic setb )
+Set the contents of the specified buffer to
+.Ar data .
+.It Xo Ic show-buffer
+.Op Fl b Ar buffer-index
+.Xc
+.D1 (alias: Ic showb )
+Display the contents of the specified buffer.
+.El
+.Sh MISCELLANEOUS
+Miscellaneous commands are as follows:
+.Bl -tag -width Ds
+.It Ic clock-mode Op Fl t Ar target-pane
+Display a large clock.
+.It Ic if-shell Ar shell-command command
+.D1 (alias: Ic if )
+Execute
+.Ar command
+if
+.Ar shell-command
+returns success.
+.It Ic lock-server
+.D1 (alias: Ic lock )
+Lock each client individually by running the command specified by the
+.Ic lock-command
+option.
+.It Ic run-shell Ar shell-command
+.D1 (alias: Ic run )
+Execute
+.Ar shell-command
+in the background without creating a window.
+After it finishes, any output to stdout is displayed in copy mode.
+If the command doesn't return success, the exit status is also displayed.
+.It Ic server-info
+.D1 (alias: Ic info )
+Show server information and terminal details.
+.El
+.Sh TERMINFO EXTENSIONS
+.Nm
+understands some extensions to
+.Xr terminfo 5 :
+.Bl -tag -width Ds
+.It Em Cc , Cr
+Set the cursor colour.
+The first takes a single string argument and is used to set the colour;
+the second takes no arguments and restores the default cursor colour.
+If set, a sequence such as this may be used
+to change the cursor colour from inside
+.Nm :
+.Bd -literal -offset indent
+$ printf '\e033]12;red\e033\e\e'
+.Ed
+.It Em Cs , Csr
+Change the cursor style.
+If set, a sequence such as this may be used
+to change the cursor to an underline:
+.Bd -literal -offset indent
+$ printf '\e033[4 q'
+.Ed
+.Pp
+If
+.Em Csr
+is set, it will be used to reset the cursor style instead
+of
+.Em Cs .
+.It Em \&Ms
+This sequence can be used by
+.Nm
+to store the current buffer in the host terminal's selection (clipboard).
+See the
+.Em set-clipboard
+option above and the
+.Xr xterm 1
+man page.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/tmux.confXXX" -compact
+.It Pa ~/.tmux.conf
+Default
+.Nm
+configuration file.
+.It Pa /etc/tmux.conf
+System-wide configuration file.
+.El
+.Sh EXAMPLES
+To create a new
+.Nm
+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 c
+(Ctrl
+followed by the
+.Ql b
+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
+(or by an external event such as
+.Xr ssh 1
+disconnection) 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.
+.Pp
+Commands to be run when the
+.Nm
+server is started may be placed in the
+.Pa ~/.tmux.conf
+configuration file.
+Common examples include:
+.Pp
+Changing the default prefix key:
+.Bd -literal -offset indent
+set-option -g prefix C-a
+unbind-key C-b
+bind-key C-a send-prefix
+.Ed
+.Pp
+Turning the status line off, or changing its colour:
+.Bd -literal -offset indent
+set-option -g status off
+set-option -g status-bg blue
+.Ed
+.Pp
+Setting other options, such as the default command,
+or locking after 30 minutes of inactivity:
+.Bd -literal -offset indent
+set-option -g default-command "exec /bin/ksh"
+set-option -g lock-after-time 1800
+.Ed
+.Pp
+Creating new key bindings:
+.Bd -literal -offset indent
+bind-key b set-option status
+bind-key / command-prompt "split-window 'exec man %%'"
+bind-key S command-prompt "new-window -n %1 'ssh %1'"
+.Ed
+.Sh SEE ALSO
+.Xr pty 4
+.Sh AUTHORS
+.An Nicholas Marriott Aq Mt nicm@users.sourceforge.net
diff --git a/external/bsd/tmux/dist/tmux.c b/external/bsd/tmux/dist/tmux.c
new file mode 100644 (file)
index 0000000..5ba7ed9
--- /dev/null
@@ -0,0 +1,413 @@
+/* $Id: tmux.c,v 1.2 2012/05/31 19:14:56 martin 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 <event.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+#if defined(DEBUG) && defined(__OpenBSD__)
+extern char    *malloc_options;
+#endif
+
+struct options  global_options;        /* server options */
+struct options  global_s_options;      /* session options */
+struct options  global_w_options;      /* window options */
+struct environ  global_environ;
+
+struct event_base *ev_base;
+
+char           *cfg_file;
+char           *shell_cmd;
+int             debug_level;
+time_t          start_time;
+char            socket_path[MAXPATHLEN];
+int             login_shell;
+char           *environ_path;
+pid_t           environ_pid = -1;
+int             environ_idx = -1;
+
+__dead void     usage(void);
+void            parseenvironment(void);
+char           *makesocketpath(const char *);
+
+#ifndef HAVE___PROGNAME
+char      *__progname = (char *) "tmux";
+#endif
+
+__dead void
+usage(void)
+{
+       fprintf(stderr,
+           "usage: %s [-28lquvV] [-c shell-command] [-f file] [-L socket-name]\n"
+           "            [-S socket-path] [command [flags]]\n",
+           __progname);
+       exit(1);
+}
+
+void
+logfile(const char *name)
+{
+       char    *path;
+
+       log_close();
+       if (debug_level > 0) {
+               xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid());
+               log_open_file(debug_level, path);
+               xfree(path);
+       }
+}
+
+const char *
+getshell(void)
+{
+       struct passwd   *pw;
+       const char      *shell;
+
+       shell = getenv("SHELL");
+       if (checkshell(shell))
+               return (shell);
+
+       pw = getpwuid(getuid());
+       if (pw != NULL && checkshell(pw->pw_shell))
+               return (pw->pw_shell);
+
+       return (_PATH_BSHELL);
+}
+
+int
+checkshell(const char *shell)
+{
+       if (shell == NULL || *shell == '\0' || areshell(shell))
+               return (0);
+       if (access(shell, X_OK) != 0)
+               return (0);
+       return (1);
+}
+
+int
+areshell(const char *shell)
+{
+       const char      *progname, *ptr;
+
+       if ((ptr = strrchr(shell, '/')) != NULL)
+               ptr++;
+       else
+               ptr = shell;
+       progname = __progname;
+       if (*progname == '-')
+               progname++;
+       if (strcmp(ptr, progname) == 0)
+               return (1);
+       return (0);
+}
+
+void
+parseenvironment(void)
+{
+       char    *env, path[256];
+       long     pid;
+       int      idx;
+
+       if ((env = getenv("TMUX")) == NULL)
+               return;
+
+       if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &idx) != 3)
+               return;
+       environ_path = xstrdup(path);
+       environ_pid = pid;
+       environ_idx = idx;
+}
+
+char *
+makesocketpath(const char *label)
+{
+       char            base[MAXPATHLEN], *path, *s;
+       struct stat     sb;
+       u_int           uid;
+
+       uid = getuid();
+       if ((s = getenv("TMPDIR")) == NULL || *s == '\0')
+               xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid);
+       else
+               xsnprintf(base, sizeof base, "%s/tmux-%u", s, 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);
+}
+
+void
+setblocking(int fd, int state)
+{
+       int mode;
+
+       if ((mode = fcntl(fd, F_GETFL)) != -1) {
+               if (!state)
+                       mode |= O_NONBLOCK;
+               else
+                       mode &= ~O_NONBLOCK;
+               fcntl(fd, F_SETFL, mode);
+       }
+}
+
+__dead void
+shell_exec(const char *shell, const char *shellcmd)
+{
+       const char      *shellname, *ptr;
+       char            *argv0;
+
+       ptr = strrchr(shell, '/');
+       if (ptr != NULL && *(ptr + 1) != '\0')
+               shellname = ptr + 1;
+       else
+               shellname = shell;
+       if (login_shell)
+               xasprintf(&argv0, "-%s", shellname);
+       else
+               xasprintf(&argv0, "%s", shellname);
+       setenv("SHELL", shell, 1);
+
+       setblocking(STDIN_FILENO, 1);
+       setblocking(STDOUT_FILENO, 1);
+       setblocking(STDERR_FILENO, 1);
+       closefrom(STDERR_FILENO + 1);
+
+       execl(shell, argv0, "-c", shellcmd, (char *) NULL);
+       fatal("execl failed");
+}
+
+static void
+init_std_fds(void)
+{
+       int fd;
+
+       /*
+        * Make sure the standard file descriptors are populated, so we
+        * don't end up forwarding (for example) the event descriptor
+        * instead of stdin to the server.
+        */
+
+       while ((fd = open("/dev/null", O_RDWR)) <= 2)
+               ;
+       close(fd);       
+}
+
+int
+main(int argc, char **argv)
+{
+       struct passwd   *pw;
+       struct keylist  *keylist;
+       char            *s, *path, *label, *home, **var;
+       int              opt, flags, quiet, keys;
+
+#if defined(DEBUG) && defined(__OpenBSD__)
+       malloc_options = (char *) "AFGJPX";
+#endif
+
+       quiet = flags = 0;
+       label = path = NULL;
+       login_shell = (**argv == '-');
+       while ((opt = getopt(argc, argv, "28c:df:lL:qS:uUvV")) != -1) {
+               switch (opt) {
+               case '2':
+                       flags |= IDENTIFY_256COLOURS;
+                       flags &= ~IDENTIFY_88COLOURS;
+                       break;
+               case '8':
+                       flags |= IDENTIFY_88COLOURS;
+                       flags &= ~IDENTIFY_256COLOURS;
+                       break;
+               case 'c':
+                       if (shell_cmd != NULL)
+                               xfree(shell_cmd);
+                       shell_cmd = xstrdup(optarg);
+                       break;
+               case 'V':
+                       printf("%s %s\n", __progname, VERSION);
+                       exit(0);
+               case 'f':
+                       if (cfg_file != NULL)
+                               xfree(cfg_file);
+                       cfg_file = xstrdup(optarg);
+                       break;
+               case 'l':
+                       login_shell = 1;
+                       break;
+               case 'L':
+                       if (label != NULL)
+                               xfree(label);
+                       label = xstrdup(optarg);
+                       break;
+               case 'q':
+                       quiet = 1;
+                       break;
+               case 'S':
+                       if (path != NULL)
+                               xfree(path);
+                       path = xstrdup(optarg);
+                       break;
+               case 'u':
+                       flags |= IDENTIFY_UTF8;
+                       break;
+               case 'v':
+                       debug_level++;
+                       break;
+               default:
+                       usage();
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (shell_cmd != NULL && argc != 0)
+               usage();
+
+       init_std_fds();
+       log_open_tty(debug_level);
+
+       if (!(flags & IDENTIFY_UTF8)) {
+               /*
+                * If the user has set whichever of LC_ALL, LC_CTYPE or LANG
+                * exist (in that order) 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("LC_ALL")) == NULL) {
+                       if ((s = getenv("LC_CTYPE")) == NULL)
+                               s = getenv("LANG");
+               }
+               if (s != NULL && (strcasestr(s, "UTF-8") != NULL ||
+                   strcasestr(s, "UTF8") != NULL))
+                       flags |= IDENTIFY_UTF8;
+       }
+
+       environ_init(&global_environ);
+       for (var = environ; *var != NULL; var++)
+               environ_put(&global_environ, *var);
+
+       options_init(&global_options, NULL);
+       options_table_populate_tree(server_options_table, &global_options);
+       options_set_number(&global_options, "quiet", quiet);
+
+       options_init(&global_s_options, NULL);
+       options_table_populate_tree(session_options_table, &global_s_options);
+       options_set_string(&global_s_options, "default-shell", "%s", getshell());
+
+       options_init(&global_w_options, NULL);
+       options_table_populate_tree(window_options_table, &global_w_options);
+
+       /* Set the prefix option (its a list, so not in the table). */
+       keylist = xmalloc(sizeof *keylist);
+       ARRAY_INIT(keylist);
+       ARRAY_ADD(keylist, '\002');
+       options_set_data(&global_s_options, "prefix", keylist, xfree);
+
+       /* Enable UTF-8 if the first client is on UTF-8 terminal. */
+       if (flags & IDENTIFY_UTF8) {
+               options_set_number(&global_s_options, "status-utf8", 1);
+               options_set_number(&global_s_options, "mouse-utf8", 1);
+               options_set_number(&global_w_options, "utf8", 1);
+       }
+
+       /* Override keys to vi if VISUAL or EDITOR are set. */
+       if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) {
+               if (strrchr(s, '/') != NULL)
+                       s = strrchr(s, '/') + 1;
+               if (strstr(s, "vi") != NULL)
+                       keys = MODEKEY_VI;
+               else
+                       keys = MODEKEY_EMACS;
+               options_set_number(&global_s_options, "status-keys", keys);
+               options_set_number(&global_w_options, "mode-keys", keys);
+       }
+
+       /* Locate the configuration file. */
+       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 && errno == ENOENT) {
+                       xfree(cfg_file);
+                       cfg_file = NULL;
+               }
+       }
+
+       /*
+        * Figure out the socket path. If specified on the command-line with -S
+        * or -L, use it, otherwise try $TMUX or assume -L default.
+        */
+       parseenvironment();
+       if (path == NULL) {
+               /* If no -L, use the environment. */
+               if (label == NULL) {
+                       if (environ_path != NULL)
+                               path = xstrdup(environ_path);
+                       else
+                               label = xstrdup("default");
+               }
+
+               /* -L or default set. */
+               if (label != NULL) {
+                       if ((path = makesocketpath(label)) == NULL) {
+                               log_warn("can't create socket");
+                               exit(1);
+                       }
+               }
+       }
+       if (label != NULL)
+               xfree(label);
+       if (realpath(path, socket_path) == NULL)
+               strlcpy(socket_path, path, sizeof socket_path);
+       xfree(path);
+
+#ifdef HAVE_SETPROCTITLE
+       /* Set process title. */
+       setproctitle("%s (%s)", __progname, socket_path);
+#endif
+
+       /* Pass control to the client. */
+       ev_base = osdep_event_init();
+       exit(client_main(argc, argv, flags));
+}
diff --git a/external/bsd/tmux/dist/tmux.h b/external/bsd/tmux/dist/tmux.h
new file mode 100644 (file)
index 0000000..e9b7aba
--- /dev/null
@@ -0,0 +1,2077 @@
+/* $Id: tmux.h,v 1.3 2011/09/17 01:50:08 christos 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 6
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+
+#include <event.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "array.h"
+
+#include "compat.h"
+
+extern char    *__progname;
+extern char   **environ;
+
+/* Default configuration files. */
+#define DEFAULT_CFG ".tmux.conf"
+#define SYSTEM_CFG "/etc/tmux.conf"
+
+/* Default prompt history length. */
+#define PROMPT_HISTORY 100
+
+/*
+ * Minimum layout cell size, NOT including separator line. The scroll region
+ * cannot be one line in height so this must be at least two.
+ */
+#define PANE_MINIMUM 2
+
+/* Automatic name refresh interval, in milliseconds. */
+#define NAME_INTERVAL 500
+
+/* Maximum data to buffer for output before suspending writing to a tty. */
+#define BACKOFF_THRESHOLD 16384
+
+/*
+ * Maximum sizes of strings in message data. Don't forget to bump
+ * PROTOCOL_VERSION if any of these change!
+ */
+#define COMMAND_LENGTH 2048    /* packed argv size */
+#define TERMINAL_LENGTH 128    /* length of TERM environment variable */
+#define ENVIRON_LENGTH 1024    /* environment variable length */
+
+/*
+ * UTF-8 data size. This must be big enough to hold combined characters as well
+ * as single.
+ */
+#define UTF8_SIZE 9
+
+/* 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)))
+#define printflike5 __attribute__ ((format (printf, 5, 6)))
+
+/* Number of items in array. */
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+/* Bell option values. */
+#define BELL_NONE 0
+#define BELL_ANY 1
+#define BELL_CURRENT 2
+
+/* Special key codes. */
+#define KEYC_NONE 0xfff
+#define KEYC_BASE 0x1000
+
+/* Key modifier bits. */
+#define KEYC_ESCAPE 0x2000
+#define KEYC_CTRL 0x4000
+#define KEYC_SHIFT 0x8000
+#define KEYC_PREFIX 0x10000
+
+/* Mask to obtain key w/o modifiers. */
+#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_PREFIX)
+#define KEYC_MASK_KEY (~KEYC_MASK_MOD)
+
+/* Other key codes. */
+enum key_code {
+       /* Mouse key. */
+       KEYC_MOUSE = KEYC_BASE,
+
+       /* Backspace key. */
+       KEYC_BSPACE,
+
+       /* Function keys. */
+       KEYC_F1,
+       KEYC_F2,
+       KEYC_F3,
+       KEYC_F4,
+       KEYC_F5,
+       KEYC_F6,
+       KEYC_F7,
+       KEYC_F8,
+       KEYC_F9,
+       KEYC_F10,
+       KEYC_F11,
+       KEYC_F12,
+       KEYC_F13,
+       KEYC_F14,
+       KEYC_F15,
+       KEYC_F16,
+       KEYC_F17,
+       KEYC_F18,
+       KEYC_F19,
+       KEYC_F20,
+       KEYC_IC,
+       KEYC_DC,
+       KEYC_HOME,
+       KEYC_END,
+       KEYC_NPAGE,
+       KEYC_PPAGE,
+       KEYC_BTAB,
+
+       /* Arrow keys. */
+       KEYC_UP,
+       KEYC_DOWN,
+       KEYC_LEFT,
+       KEYC_RIGHT,
+
+       /* Numeric keypad. */
+       KEYC_KP_SLASH,
+       KEYC_KP_STAR,
+       KEYC_KP_MINUS,
+       KEYC_KP_SEVEN,
+       KEYC_KP_EIGHT,
+       KEYC_KP_NINE,
+       KEYC_KP_PLUS,
+       KEYC_KP_FOUR,
+       KEYC_KP_FIVE,
+       KEYC_KP_SIX,
+       KEYC_KP_ONE,
+       KEYC_KP_TWO,
+       KEYC_KP_THREE,
+       KEYC_KP_ENTER,
+       KEYC_KP_ZERO,
+       KEYC_KP_PERIOD,
+};
+
+/* 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_CC,        /* set colour cursor, Cc */
+       TTYC_CIVIS,     /* cursor_invisible, vi */
+       TTYC_CLEAR,     /* clear_screen, cl */
+       TTYC_CNORM,     /* cursor_normal, ve */
+       TTYC_COLORS,    /* max_colors, Co */
+       TTYC_CR,        /* restore cursor colour, Cr */
+       TTYC_CS1,       /* set cursor style, Cs */
+       TTYC_CSR,       /* change_scroll_region, cs */
+       TTYC_CSR1,      /* reset cursor style, Csr */
+       TTYC_CUB,       /* parm_left_cursor, LE */
+       TTYC_CUB1,      /* cursor_left, le */
+       TTYC_CUD,       /* parm_down_cursor, DO */
+       TTYC_CUD1,      /* cursor_down, do */
+       TTYC_CUF,       /* parm_right_cursor, RI */
+       TTYC_CUF1,      /* cursor_right, nd */
+       TTYC_CUP,       /* cursor_address, cm */
+       TTYC_CUU,       /* parm_up_cursor, UP */
+       TTYC_CUU1,      /* cursor_up, up */
+       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_FSL,       /* from_status_line, fsl */
+       TTYC_HOME,      /* cursor_home, ho */
+       TTYC_HPA,       /* column_address, ch */
+       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_KDC2,
+       TTYC_KDC3,
+       TTYC_KDC4,
+       TTYC_KDC5,
+       TTYC_KDC6,
+       TTYC_KDC7,
+       TTYC_KDCH1,     /* key_dc, kD */
+       TTYC_KDN2,
+       TTYC_KDN3,
+       TTYC_KDN4,
+       TTYC_KDN5,
+       TTYC_KDN6,
+       TTYC_KDN7,
+       TTYC_KEND,      /* key_end, ke */
+       TTYC_KEND2,
+       TTYC_KEND3,
+       TTYC_KEND4,
+       TTYC_KEND5,
+       TTYC_KEND6,
+       TTYC_KEND7,
+       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_KF2,       /* key_f2, k2 */
+       TTYC_KF20,      /* key_f20, F10 */
+       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_KHOM2,
+       TTYC_KHOM3,
+       TTYC_KHOM4,
+       TTYC_KHOM5,
+       TTYC_KHOM6,
+       TTYC_KHOM7,
+       TTYC_KHOME,     /* key_home, kh */
+       TTYC_KIC2,
+       TTYC_KIC3,
+       TTYC_KIC4,
+       TTYC_KIC5,
+       TTYC_KIC6,
+       TTYC_KIC7,
+       TTYC_KICH1,     /* key_ic, kI */
+       TTYC_KLFT2,
+       TTYC_KLFT3,
+       TTYC_KLFT4,
+       TTYC_KLFT5,
+       TTYC_KLFT6,
+       TTYC_KLFT7,
+       TTYC_KMOUS,     /* key_mouse, Km */
+       TTYC_KNP,       /* key_npage, kN */
+       TTYC_KNXT2,
+       TTYC_KNXT3,
+       TTYC_KNXT4,
+       TTYC_KNXT5,
+       TTYC_KNXT6,
+       TTYC_KNXT7,
+       TTYC_KPP,       /* key_ppage, kP */
+       TTYC_KPRV2,
+       TTYC_KPRV3,
+       TTYC_KPRV4,
+       TTYC_KPRV5,
+       TTYC_KPRV6,
+       TTYC_KPRV7,
+       TTYC_KRIT2,
+       TTYC_KRIT3,
+       TTYC_KRIT4,
+       TTYC_KRIT5,
+       TTYC_KRIT6,
+       TTYC_KRIT7,
+       TTYC_KUP2,
+       TTYC_KUP3,
+       TTYC_KUP4,
+       TTYC_KUP5,
+       TTYC_KUP6,
+       TTYC_KUP7,
+       TTYC_MS,        /* modify xterm(1) selection */
+       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_SITM,      /* enter_italics_mode, it */
+       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_TSL,       /* to_status_line, tsl */
+       TTYC_VPA,       /* row_address, cv */
+       TTYC_XENL,      /* eat_newline_glitch, xn */
+       TTYC_XT,        /* xterm(1)-compatible title, XT */
+};
+#define NTTYCODE (TTYC_XT + 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;
+};
+
+/* Message codes. */
+enum msgtype {
+       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_VERSION,
+       MSG_WAKEUP,
+       MSG_ENVIRON,
+       MSG_UNLOCK,
+       MSG_LOCK,
+       MSG_SHELL,
+       MSG_STDERR,
+       MSG_STDOUT,
+       MSG_DETACHKILL
+};
+
+/*
+ * Message data.
+ *
+ * Don't forget to bump PROTOCOL_VERSION if any of these change!
+ */
+struct msg_command_data {
+       pid_t           pid;    /* PID from $TMUX or -1 */
+       int             idx;    /* index from $TMUX or -1 */
+
+       int             argc;
+       char            argv[COMMAND_LENGTH];
+};
+
+struct msg_identify_data {
+       char            cwd[MAXPATHLEN];
+
+       char            term[TERMINAL_LENGTH];
+
+#define IDENTIFY_UTF8 0x1
+#define IDENTIFY_256COLOURS 0x2
+#define IDENTIFY_88COLOURS 0x4
+       int             flags;
+};
+
+struct msg_lock_data {
+       char            cmd[COMMAND_LENGTH];
+};
+
+struct msg_environ_data {
+       char            var[ENVIRON_LENGTH];
+};
+
+struct msg_shell_data {
+       char            shell[MAXPATHLEN];
+};
+
+struct msg_exit_data {
+       int             retcode;
+};
+
+/* Mode key commands. */
+enum mode_key_cmd {
+       MODEKEY_NONE,
+       MODEKEY_OTHER,
+
+       /* Editing keys. */
+       MODEKEYEDIT_BACKSPACE,
+       MODEKEYEDIT_CANCEL,
+       MODEKEYEDIT_COMPLETE,
+       MODEKEYEDIT_CURSORLEFT,
+       MODEKEYEDIT_CURSORRIGHT,
+       MODEKEYEDIT_DELETE,
+       MODEKEYEDIT_DELETELINE,
+       MODEKEYEDIT_DELETETOENDOFLINE,
+       MODEKEYEDIT_ENDOFLINE,
+       MODEKEYEDIT_ENTER,
+       MODEKEYEDIT_HISTORYDOWN,
+       MODEKEYEDIT_HISTORYUP,
+       MODEKEYEDIT_PASTE,
+       MODEKEYEDIT_STARTOFLINE,
+       MODEKEYEDIT_SWITCHMODE,
+       MODEKEYEDIT_SWITCHMODEAPPEND,
+       MODEKEYEDIT_TRANSPOSECHARS,
+
+       /* Menu (choice) keys. */
+       MODEKEYCHOICE_CANCEL,
+       MODEKEYCHOICE_CHOOSE,
+       MODEKEYCHOICE_DOWN,
+       MODEKEYCHOICE_PAGEDOWN,
+       MODEKEYCHOICE_PAGEUP,
+       MODEKEYCHOICE_SCROLLDOWN,
+       MODEKEYCHOICE_SCROLLUP,
+       MODEKEYCHOICE_UP,
+
+       /* Copy keys. */
+       MODEKEYCOPY_BACKTOINDENTATION,
+       MODEKEYCOPY_BOTTOMLINE,
+       MODEKEYCOPY_CANCEL,
+       MODEKEYCOPY_CLEARSELECTION,
+       MODEKEYCOPY_COPYLINE,
+       MODEKEYCOPY_COPYENDOFLINE,
+       MODEKEYCOPY_COPYSELECTION,
+       MODEKEYCOPY_DOWN,
+       MODEKEYCOPY_ENDOFLINE,
+       MODEKEYCOPY_GOTOLINE,
+       MODEKEYCOPY_HALFPAGEDOWN,
+       MODEKEYCOPY_HALFPAGEUP,
+       MODEKEYCOPY_HISTORYBOTTOM,
+       MODEKEYCOPY_HISTORYTOP,
+       MODEKEYCOPY_JUMP,
+       MODEKEYCOPY_JUMPAGAIN,
+       MODEKEYCOPY_JUMPREVERSE,
+       MODEKEYCOPY_JUMPBACK,
+       MODEKEYCOPY_LEFT,
+       MODEKEYCOPY_MIDDLELINE,
+       MODEKEYCOPY_NEXTPAGE,
+       MODEKEYCOPY_NEXTSPACE,
+       MODEKEYCOPY_NEXTSPACEEND,
+       MODEKEYCOPY_NEXTWORD,
+       MODEKEYCOPY_NEXTWORDEND,
+       MODEKEYCOPY_PREVIOUSPAGE,
+       MODEKEYCOPY_PREVIOUSSPACE,
+       MODEKEYCOPY_PREVIOUSWORD,
+       MODEKEYCOPY_RECTANGLETOGGLE,
+       MODEKEYCOPY_RIGHT,
+       MODEKEYCOPY_SCROLLDOWN,
+       MODEKEYCOPY_SCROLLUP,
+       MODEKEYCOPY_SEARCHAGAIN,
+       MODEKEYCOPY_SEARCHDOWN,
+       MODEKEYCOPY_SEARCHREVERSE,
+       MODEKEYCOPY_SEARCHUP,
+       MODEKEYCOPY_SELECTLINE,
+       MODEKEYCOPY_STARTNUMBERPREFIX,
+       MODEKEYCOPY_STARTOFLINE,
+       MODEKEYCOPY_STARTSELECTION,
+       MODEKEYCOPY_TOPLINE,
+       MODEKEYCOPY_UP,
+};
+
+/* Entry in the default mode key tables. */
+struct mode_key_entry {
+       int                     key;
+
+       /*
+        * Editing mode for vi: 0 is edit mode, keys not in the table are
+        * returned as MODEKEY_OTHER; 1 is command mode, keys not in the table
+        * are returned as MODEKEY_NONE. This is also matched on, allowing some
+        * keys to be bound in edit mode.
+        */
+       int                     mode;
+       enum mode_key_cmd       cmd;
+};
+
+/* Data required while mode keys are in use. */
+struct mode_key_data {
+       struct mode_key_tree   *tree;
+       int                     mode;
+};
+#define MODEKEY_EMACS 0
+#define MODEKEY_VI 1
+
+/* Binding between a key and a command. */
+struct mode_key_binding {
+       int                     key;
+
+       int                     mode;
+       enum mode_key_cmd       cmd;
+
+       SPLAY_ENTRY(mode_key_binding) entry;
+};
+SPLAY_HEAD(mode_key_tree, mode_key_binding);
+
+/* Command to string mapping. */
+struct mode_key_cmdstr {
+       enum mode_key_cmd        cmd;
+       const char              *name;
+};
+
+/* Named mode key table description. */
+struct mode_key_table {
+       const char                      *name;
+       const struct mode_key_cmdstr    *cmdstr;
+       struct mode_key_tree            *tree;
+       const struct mode_key_entry     *table; /* default entries */
+};
+
+/* Modes. */
+#define MODE_CURSOR 0x1
+#define MODE_INSERT 0x2
+#define MODE_KCURSOR 0x4
+#define MODE_KKEYPAD 0x8       /* set = application, clear = number */
+#define MODE_WRAP 0x10         /* whether lines wrap */
+#define MODE_MOUSE_STANDARD 0x20
+#define MODE_MOUSE_BUTTON 0x40
+#define MODE_MOUSE_ANY 0x80
+#define MODE_MOUSE_UTF8 0x100
+
+#define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ANY)
+
+/*
+ * A single UTF-8 character.
+ *
+ * The data member in this must be UTF8_SIZE to allow screen_write_copy to
+ * reinject stored UTF-8 data back into screen_write_cell after combining (ugh
+ * XXX XXX).
+ */
+struct utf8_data {
+       u_char  data[UTF8_SIZE];
+
+       size_t  have;
+       size_t  size;
+
+       u_int   width;
+};
+
+/* Grid output. */
+#if defined(DEBUG) && \
+    ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \
+     (defined(__GNUC__) && __GNUC__ >= 3))
+#define GRID_DEBUG(gd, fmt, ...) log_debug2("%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 line flags. */
+#define GRID_LINE_WRAPPED 0x1
+
+/* 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. */
+struct grid_utf8 {
+       u_char  width;
+       u_char  data[UTF8_SIZE];
+} __packed;
+
+/* Grid line. */
+struct grid_line {
+       u_int   cellsize;
+       struct grid_cell *celldata;
+
+       u_int   utf8size;
+       struct grid_utf8 *utf8data;
+
+       int     flags;
+} __packed;
+
+/* Entire grid of cells. */
+struct grid {
+       int     flags;
+#define GRID_HISTORY 0x1       /* scroll lines into history */
+
+       u_int   sx;
+       u_int   sy;
+
+       u_int   hsize;
+       u_int   hlimit;
+
+       struct grid_line *linedata;
+};
+
+/* Option data structures. */
+struct options_entry {
+       char            *name;
+
+       enum {
+               OPTIONS_STRING,
+               OPTIONS_NUMBER,
+               OPTIONS_DATA,
+       } type;
+
+       char            *str;
+       long long        num;
+       void            *data;
+
+       void             (*freefn)(void *);
+
+       SPLAY_ENTRY(options_entry) entry;
+};
+
+struct options {
+       SPLAY_HEAD(options_tree, options_entry) tree;
+       struct options  *parent;
+};
+
+/* Key list for prefix option. */
+ARRAY_DECL(keylist, int);
+
+/* Scheduled job. */
+struct job {
+       char            *cmd;
+       pid_t            pid;
+       int              status;
+
+       int              fd;
+       struct bufferevent *event;
+
+       void            (*callbackfn)(struct job *);
+       void            (*freefn)(void *);
+       void            *data;
+
+       LIST_ENTRY(job)  lentry;
+};
+LIST_HEAD(joblist, job);
+
+/* Screen selection. */
+struct screen_sel {
+       int              flag;
+       int              rectflag;
+
+       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            cstyle;        /* cursor style */
+       char            *ccolour;       /* cursor colour string */
+
+       u_int            rupper;        /* scroll region top */
+       u_int            rlower;        /* scroll region bottom */
+
+       int              mode;
+
+       bitstr_t        *tabs;
+
+       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 context. */
+struct input_ctx {
+       struct window_pane     *wp;
+       struct screen_write_ctx ctx;
+
+       struct grid_cell        cell;
+
+       struct grid_cell        old_cell;
+       u_int                   old_cx;
+       u_int                   old_cy;
+
+       u_char                  interm_buf[4];
+       size_t                  interm_len;
+
+       u_char                  param_buf[64];
+       size_t                  param_len;
+
+       u_char                  input_buf[256];
+       size_t                  input_len;
+
+       int                     param_list[24]; /* -1 not present */
+       u_int                   param_list_len;
+
+       struct utf8_data        utf8data;
+
+       int                     ch;
+       int                     flags;
+#define INPUT_DISCARD 0x1
+
+       const struct input_state *state;
+};
+
+/*
+ * Window mode. Windows can be in several modes and this is used to call the
+ * right function to handle input and output.
+ */
+struct session;
+struct window;
+struct mouse_event;
+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 session *, int);
+       void    (*mouse)(struct window_pane *,
+                   struct session *, struct mouse_event *);
+       void    (*timer)(struct window_pane *);
+};
+
+/* Child window structure. */
+struct window_utmp;
+struct window_pane {
+       u_int            id;
+
+       struct window   *window;
+       struct layout_cell *layout_cell;
+
+       u_int            sx;
+       u_int            sy;
+
+       u_int            xoff;
+       u_int            yoff;
+
+       int              flags;
+#define PANE_REDRAW 0x1
+
+       char            *cmd;
+       char            *shell;
+       char            *cwd;
+
+       pid_t            pid;
+       char             tty[TTY_NAME_MAX];
+
+       int              fd;
+       struct bufferevent *event;
+
+       struct input_ctx ictx;
+
+       int              pipe_fd;
+       struct bufferevent *pipe_event;
+       size_t           pipe_off;
+
+       struct screen   *screen;
+       struct screen    base;
+
+       /* Saved in alternative screen mode. */
+       u_int            saved_cx;
+       u_int            saved_cy;
+       struct grid     *saved_grid;
+       struct grid_cell saved_cell;
+
+       const struct window_mode *mode;
+       void            *modedata;
+
+       struct window_utmp *utmp;
+
+       TAILQ_ENTRY(window_pane) entry;
+       RB_ENTRY(window_pane) tree_entry;
+};
+TAILQ_HEAD(window_panes, window_pane);
+RB_HEAD(window_pane_tree, window_pane);
+
+/* Window structure. */
+struct window {
+       char            *name;
+       struct event     name_timer;
+       struct timeval   silence_timer;
+
+       struct window_pane *active;
+       struct window_pane *last;
+       struct window_panes panes;
+
+       int              lastlayout;
+       struct layout_cell *layout_root;
+
+       u_int            sx;
+       u_int            sy;
+
+       int              flags;
+#define WINDOW_BELL 0x1
+#define WINDOW_ACTIVITY 0x2
+#define WINDOW_REDRAW 0x4
+#define WINDOW_SILENCE 0x8
+
+       struct options   options;
+
+       u_int            references;
+};
+ARRAY_DECL(windows, struct window *);
+
+/* Entry on local window list. */
+struct winlink {
+       int              idx;
+       struct window   *window;
+
+       size_t           status_width;
+       struct grid_cell status_cell;
+       char            *status_text;
+
+       int              flags;
+#define WINLINK_BELL 0x1
+#define WINLINK_ACTIVITY 0x2
+#define WINLINK_CONTENT 0x4
+#define WINLINK_SILENCE 0x8
+#define WINLINK_ALERTFLAGS \
+    (WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_CONTENT|WINLINK_SILENCE)
+
+       RB_ENTRY(winlink) entry;
+       TAILQ_ENTRY(winlink) sentry;
+};
+RB_HEAD(winlinks, winlink);
+TAILQ_HEAD(winlink_stack, winlink);
+
+/* Layout direction. */
+enum layout_type {
+       LAYOUT_LEFTRIGHT,
+       LAYOUT_TOPBOTTOM,
+       LAYOUT_WINDOWPANE
+};
+
+/* Layout cells queue. */
+TAILQ_HEAD(layout_cells, layout_cell);
+
+/* Layout cell. */
+struct layout_cell {
+       enum layout_type type;
+
+       struct layout_cell *parent;
+
+       u_int            sx;
+       u_int            sy;
+
+       u_int            xoff;
+       u_int            yoff;
+
+       struct window_pane *wp;
+       struct layout_cells cells;
+
+       TAILQ_ENTRY(layout_cell) entry;
+};
+
+/* Paste buffer. */
+struct paste_buffer {
+       char            *data;
+       size_t           size;
+};
+ARRAY_DECL(paste_stack, struct paste_buffer *);
+
+/* Environment variable. */
+struct environ_entry {
+       char            *name;
+       char            *value;
+
+       RB_ENTRY(environ_entry) entry;
+};
+RB_HEAD(environ, environ_entry);
+
+/* Client session. */
+struct session_group {
+       TAILQ_HEAD(, session) sessions;
+
+       TAILQ_ENTRY(session_group) entry;
+};
+TAILQ_HEAD(session_groups, session_group);
+
+struct session {
+       u_int            idx;
+
+       char            *name;
+       char            *cwd;
+
+       struct timeval   creation_time;
+       struct timeval   activity_time;
+
+       u_int            sx;
+       u_int            sy;
+
+       struct winlink  *curw;
+       struct winlink_stack lastw;
+       struct winlinks  windows;
+
+       struct options   options;
+
+#define SESSION_UNATTACHED 0x1 /* not attached to any clients */
+       int              flags;
+
+       struct termios  *tio;
+
+       struct environ   environ;
+
+       int              wlmouse;
+
+       int              references;
+
+       TAILQ_ENTRY(session) gentry;
+       RB_ENTRY(session)    entry;
+};
+RB_HEAD(sessions, session);
+ARRAY_DECL(sessionslist, struct session *);
+
+/* TTY information. */
+struct tty_key {
+       char             ch;
+       int              key;
+
+       struct tty_key  *left;
+       struct tty_key  *right;
+
+       struct tty_key  *next;
+};
+
+struct tty_term {
+       char            *name;
+       u_int            references;
+
+       char             acs[UCHAR_MAX + 1][2];
+
+       struct tty_code  codes[NTTYCODE];
+
+#define TERM_256COLOURS 0x1
+#define TERM_88COLOURS 0x2
+#define TERM_EARLYWRAP 0x4
+       int              flags;
+
+       LIST_ENTRY(tty_term) entry;
+};
+LIST_HEAD(tty_terms, tty_term);
+
+struct tty {
+       char            *path;
+
+       u_int            sx;
+       u_int            sy;
+
+       u_int            cx;
+       u_int            cy;
+       u_int            cstyle;
+       char            *ccolour;
+
+       int              mode;
+
+       u_int            rlower;
+       u_int            rupper;
+
+       char            *termname;
+       struct tty_term *term;
+
+       int              fd;
+       struct bufferevent *event;
+
+       int              log_fd;
+
+       struct termios   tio;
+
+       struct grid_cell cell;
+
+#define TTY_NOCURSOR 0x1
+#define TTY_FREEZE 0x2
+#define TTY_ESCAPE 0x4
+#define TTY_UTF8 0x8
+#define TTY_STARTED 0x10
+#define TTY_OPENED 0x20
+#define TTY_BACKOFF 0x40
+       int              flags;
+
+       int              term_flags;
+
+       void             (*key_callback)(int, struct mouse_event *, void *);
+       void            *key_data;
+       struct event     key_timer;
+       struct tty_key  *key_tree;
+};
+
+/* TTY command context and function pointer. */
+struct tty_ctx {
+       struct window_pane *wp;
+
+       const struct grid_cell *cell;
+       const struct grid_utf8 *utf8;
+
+       u_int            num;
+       void            *ptr;
+
+       /*
+        * Cursor and region position before the screen was updated - this is
+        * where the command should be applied; the values in the screen have
+        * already been updated.
+        */
+       u_int            ocx;
+       u_int            ocy;
+
+       u_int            orupper;
+       u_int            orlower;
+
+       /* Saved last cell on line. */
+       struct grid_cell last_cell;
+       struct grid_utf8 last_utf8;
+       u_int            last_width;
+};
+
+/*
+ * xterm mouse mode is fairly silly. Buttons are in the bottom two
+ * bits: 0 button 1; 1 button 2; 2 button 3; 3 buttons released.
+ *
+ * Bit 3 is shift; bit 4 is meta; bit 5 control.
+ *
+ * Bit 6 is added for mouse buttons 4 and 5.
+ */
+/* Mouse input. */
+struct mouse_event {
+       u_int   b;
+#define MOUSE_1 0
+#define MOUSE_2 1
+#define MOUSE_3 2
+#define MOUSE_UP 3
+#define MOUSE_BUTTON 3
+#define MOUSE_DRAG 32
+#define MOUSE_45 64
+#define MOUSE_RESIZE_PANE 128 /* marker for resizing */
+       u_int   x;
+       u_int   y;
+};
+
+/* Saved message entry. */
+struct message_entry {
+       char   *msg;
+       time_t  msg_time;
+};
+
+/* Status output data from a job. */
+struct status_out {
+       char   *cmd;
+       char   *out;
+
+       RB_ENTRY(status_out) entry;
+};
+RB_HEAD(status_out_tree, status_out);
+
+/* Client connection. */
+struct client {
+       struct imsgbuf   ibuf;
+       struct event     event;
+       int              retcode;
+
+       struct timeval   creation_time;
+       struct timeval   activity_time;
+
+       struct environ   environ;
+
+       char            *title;
+       char            *cwd;
+
+       struct tty       tty;
+
+       int              stdin_fd;
+       void            *stdin_data;
+       void            (*stdin_callback)(struct client *, void *);
+       struct bufferevent *stdin_event;
+
+       int              stdout_fd;
+       struct bufferevent *stdout_event;
+
+       int              stderr_fd;
+       struct bufferevent *stderr_event;
+
+       struct event     repeat_timer;
+
+       struct status_out_tree status_old;
+       struct status_out_tree status_new;
+       struct timeval   status_timer;
+       struct screen    status;
+
+#define CLIENT_TERMINAL 0x1
+#define CLIENT_PREFIX 0x2
+#define CLIENT_EXIT 0x4
+#define CLIENT_REDRAW 0x8
+#define CLIENT_STATUS 0x10
+#define CLIENT_REPEAT 0x20     /* allow command to repeat within repeat time */
+#define CLIENT_SUSPENDED 0x40
+#define CLIENT_BAD 0x80
+#define CLIENT_IDENTIFY 0x100
+#define CLIENT_DEAD 0x200
+#define CLIENT_BORDERS 0x400
+#define CLIENT_READONLY 0x800
+#define CLIENT_BACKOFF 0x1000
+#define CLIENT_REDRAWWINDOW 0x2000
+       int              flags;
+
+       struct event     identify_timer;
+
+       char            *message_string;
+       struct event     message_timer;
+       ARRAY_DECL(, struct message_entry) message_log;
+
+       char            *prompt_string;
+       char            *prompt_buffer;
+       size_t           prompt_index;
+       int              (*prompt_callbackfn)(void *, const char *);
+       void             (*prompt_freefn)(void *);
+       void            *prompt_data;
+       u_int            prompt_hindex;
+
+#define PROMPT_SINGLE 0x1
+       int              prompt_flags;
+
+       struct mode_key_data prompt_mdata;
+
+       struct session  *session;
+       struct session  *last_session;
+
+       struct mouse_event last_mouse;
+
+       int              references;
+};
+ARRAY_DECL(clients, struct client *);
+
+/* Parsed arguments. */
+struct args {
+       bitstr_t        *flags;
+       char            *values[SCHAR_MAX]; /* XXX This is awfully big. */
+
+       int              argc;
+       char           **argv;
+};
+
+/* Key/command line command. */
+struct cmd_ctx {
+       /*
+        * curclient is the client where this command was executed if inside
+        * tmux. This is NULL if the command came from the command-line.
+        *
+        * cmdclient is the client which sent the MSG_COMMAND to the server, if
+        * any. This is NULL unless the command came from the command-line.
+        *
+        * cmdclient and curclient may both be NULL if the command is in the
+        * configuration file.
+        */
+       struct client  *curclient;
+       struct client  *cmdclient;
+
+       struct msg_command_data *msgdata;
+
+       /* gcc2 doesn't understand attributes on function pointers... */
+#if defined(__GNUC__) && __GNUC__ >= 3
+       void printflike2 (*print)(struct cmd_ctx *, const char *, ...);
+       void printflike2 (*info)(struct cmd_ctx *, const char *, ...);
+       void printflike2 (*error)(struct cmd_ctx *, const char *, ...);
+#else
+       void (*print)(struct cmd_ctx *, const char *, ...);
+       void (*info)(struct cmd_ctx *, const char *, ...);
+       void (*error)(struct cmd_ctx *, const char *, ...);
+#endif
+};
+
+struct cmd {
+       const struct cmd_entry  *entry;
+       struct args             *args;
+
+       TAILQ_ENTRY(cmd)         qentry;
+};
+struct cmd_list {
+       int                      references;
+       TAILQ_HEAD(, cmd)        list;
+};
+
+struct cmd_entry {
+       const char      *name;
+       const char      *alias;
+
+       const char      *args_template;
+       int              args_lower;
+       int              args_upper;
+
+       const char      *usage;
+
+#define CMD_STARTSERVER 0x1
+#define CMD_CANTNEST 0x2
+#define CMD_SENDENVIRON 0x4
+#define CMD_READONLY 0x8
+       int              flags;
+
+       void             (*key_binding)(struct cmd *, int);
+       int              (*check)(struct args *);
+       int              (*exec)(struct cmd *, struct cmd_ctx *);
+};
+
+/* 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);
+
+/*
+ * Option table entries. The option table is the user-visible part of the
+ * option, as opposed to the internal options (struct option) which are just
+ * number or string.
+ */
+enum options_table_type {
+       OPTIONS_TABLE_STRING,
+       OPTIONS_TABLE_NUMBER,
+       OPTIONS_TABLE_KEYS,
+       OPTIONS_TABLE_COLOUR,
+       OPTIONS_TABLE_ATTRIBUTES,
+       OPTIONS_TABLE_FLAG,
+       OPTIONS_TABLE_CHOICE
+};
+
+struct options_table_entry {
+       const char             *name;
+       enum options_table_type type;
+
+       u_int                   minimum;
+       u_int                   maximum;
+       const char            **choices;
+
+       const char             *default_str;
+       long long               default_num;
+};
+
+/* List of configuration causes. */
+ARRAY_DECL(causelist, char *);
+
+/* Common command usages. */
+#define CMD_TARGET_PANE_USAGE "[-t target-pane]"
+#define CMD_TARGET_WINDOW_USAGE "[-t target-window]"
+#define CMD_TARGET_SESSION_USAGE "[-t target-session]"
+#define CMD_TARGET_CLIENT_USAGE "[-t target-client]"
+#define CMD_SRCDST_PANE_USAGE "[-s src-pane] [-t dst-pane]"
+#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]"
+#define CMD_BUFFER_USAGE "[-b buffer-index]"
+
+/* tmux.c */
+extern struct options global_options;
+extern struct options global_s_options;
+extern struct options global_w_options;
+extern struct environ global_environ;
+extern struct event_base *ev_base;
+extern char    *cfg_file;
+extern char    *shell_cmd;
+extern int      debug_level;
+extern time_t   start_time;
+extern char     socket_path[MAXPATHLEN];
+extern int      login_shell;
+extern char    *environ_path;
+extern pid_t    environ_pid;
+extern int      environ_idx;
+void            logfile(const char *);
+const char     *getshell(void);
+int             checkshell(const char *);
+int             areshell(const char *);
+void            setblocking(int, int);
+__dead void     shell_exec(const char *, const char *);
+
+/* cfg.c */
+extern int       cfg_finished;
+extern struct causelist cfg_causes;
+void printflike2 cfg_add_cause(struct causelist *, const char *, ...);
+int             load_cfg(const char *, struct cmd_ctx *, struct causelist *);
+
+/* mode-key.c */
+extern const struct mode_key_table mode_key_tables[];
+extern struct mode_key_tree mode_key_tree_vi_edit;
+extern struct mode_key_tree mode_key_tree_vi_choice;
+extern struct mode_key_tree mode_key_tree_vi_copy;
+extern struct mode_key_tree mode_key_tree_emacs_edit;
+extern struct mode_key_tree mode_key_tree_emacs_choice;
+extern struct mode_key_tree mode_key_tree_emacs_copy;
+int    mode_key_cmp(struct mode_key_binding *, struct mode_key_binding *);
+SPLAY_PROTOTYPE(mode_key_tree, mode_key_binding, entry, mode_key_cmp);
+const char *mode_key_tostring(const struct mode_key_cmdstr *,
+           enum mode_key_cmd);
+enum mode_key_cmd mode_key_fromstring(const struct mode_key_cmdstr *,
+           const char *);
+const struct mode_key_table *mode_key_findtable(const char *);
+void   mode_key_init_trees(void);
+void   mode_key_init(struct mode_key_data *, struct mode_key_tree *);
+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 *);
+void   options_remove(struct options *, const char *);
+struct options_entry *printflike3 options_set_string(
+           struct options *, const char *, const char *, ...);
+char   *options_get_string(struct options *, const char *);
+struct options_entry *options_set_number(
+           struct options *, const char *, long long);
+long long options_get_number(struct options *, const char *);
+struct options_entry *options_set_data(
+           struct options *, const char *, void *, void (*)(void *));
+void   *options_get_data(struct options *, const char *);
+
+/* options-table.c */
+extern const struct options_table_entry server_options_table[];
+extern const struct options_table_entry session_options_table[];
+extern const struct options_table_entry window_options_table[];
+void   options_table_populate_tree(
+           const struct options_table_entry *, struct options *);
+const char *options_table_print_entry(
+           const struct options_table_entry *, struct options_entry *);
+
+/* job.c */
+extern struct joblist all_jobs;
+struct job *job_run(
+           const char *, void (*)(struct job *), void (*)(void *), void *);
+void   job_free(struct job *);
+void   job_died(struct job *, int);
+
+/* environ.c */
+int    environ_cmp(struct environ_entry *, struct environ_entry *);
+RB_PROTOTYPE(environ, environ_entry, entry, environ_cmp);
+void   environ_init(struct environ *);
+void   environ_free(struct environ *);
+void   environ_copy(struct environ *, struct environ *);
+struct environ_entry *environ_find(struct environ *, const char *);
+void   environ_set(struct environ *, const char *, const char *);
+void   environ_put(struct environ *, const char *);
+void   environ_unset(struct environ *, const char *);
+void   environ_update(const char *, struct environ *, struct environ *);
+void   environ_push(struct environ *);
+
+/* tty.c */
+void   tty_raw(struct tty *, const char *);
+void   tty_attributes(struct tty *, const struct grid_cell *);
+void   tty_reset(struct tty *);
+void   tty_region_pane(struct tty *, const struct tty_ctx *, u_int, u_int);
+void   tty_region(struct tty *, u_int, u_int);
+void   tty_cursor_pane(struct tty *, const struct tty_ctx *, u_int, u_int);
+void   tty_cursor(struct tty *, u_int, u_int);
+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_putcode_ptr1(struct tty *, enum tty_code_code, const void *);
+void   tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, const void *);
+void   tty_puts(struct tty *, const char *);
+void   tty_putc(struct tty *, u_char);
+void   tty_pututf8(struct tty *, const struct grid_utf8 *);
+void   tty_init(struct tty *, int, char *);
+int    tty_resize(struct tty *);
+void   tty_start_tty(struct tty *);
+void   tty_stop_tty(struct tty *);
+void   tty_set_title(struct tty *, const char *);
+void   tty_update_mode(struct tty *, int, struct screen *);
+void   tty_force_cursor_colour(struct tty *, const char *);
+void   tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int);
+int    tty_open(struct tty *, const char *, char **);
+void   tty_close(struct tty *);
+void   tty_free(struct tty *);
+void   tty_write(void (*)(
+           struct tty *, const struct tty_ctx *), const struct tty_ctx *);
+void   tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *);
+void   tty_cmd_cell(struct tty *, const struct tty_ctx *);
+void   tty_cmd_clearendofline(struct tty *, const struct tty_ctx *);
+void   tty_cmd_clearendofscreen(struct tty *, const struct tty_ctx *);
+void   tty_cmd_clearline(struct tty *, const struct tty_ctx *);
+void   tty_cmd_clearscreen(struct tty *, const struct tty_ctx *);
+void   tty_cmd_clearstartofline(struct tty *, const struct tty_ctx *);
+void   tty_cmd_clearstartofscreen(struct tty *, const struct tty_ctx *);
+void   tty_cmd_deletecharacter(struct tty *, const struct tty_ctx *);
+void   tty_cmd_deleteline(struct tty *, const struct tty_ctx *);
+void   tty_cmd_erasecharacter(struct tty *, const struct tty_ctx *);
+void   tty_cmd_insertcharacter(struct tty *, const struct tty_ctx *);
+void   tty_cmd_insertline(struct tty *, const struct tty_ctx *);
+void   tty_cmd_linefeed(struct tty *, const struct tty_ctx *);
+void   tty_cmd_utf8character(struct tty *, const struct tty_ctx *);
+void   tty_cmd_reverseindex(struct tty *, const struct tty_ctx *);
+void   tty_cmd_setselection(struct tty *, const struct tty_ctx *);
+void   tty_cmd_rawstring(struct tty *, const struct tty_ctx *);
+
+/* tty-term.c */
+extern struct tty_terms tty_terms;
+extern const struct tty_term_code_entry tty_term_codes[NTTYCODE];
+struct tty_term *tty_term_find(char *, int, const char *, 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);
+const char     *tty_term_ptr1(
+                    struct tty_term *, enum tty_code_code, const void *);
+const char     *tty_term_ptr2(
+                    struct tty_term *, enum tty_code_code, const void *, const void *);
+int             tty_term_number(struct tty_term *, enum tty_code_code);
+int             tty_term_flag(struct tty_term *, enum tty_code_code);
+
+/* tty-acs.c */
+const char     *tty_acs_get(struct tty *, u_char);
+
+/* tty-keys.c */
+void   tty_keys_init(struct tty *);
+void   tty_keys_free(struct tty *);
+int    tty_keys_next(struct tty *);
+
+/* paste.c */
+struct paste_buffer *paste_walk_stack(struct paste_stack *, u_int *);
+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 *, size_t, u_int);
+int             paste_replace(struct paste_stack *, u_int, char *, size_t);
+char           *paste_print(struct paste_buffer *, size_t);
+
+/* clock.c */
+extern const char clock_table[14][5][5];
+void            clock_draw(struct screen_write_ctx *, int, int);
+
+/* arguments.c */
+struct args    *args_create(int, ...);
+struct args    *args_parse(const char *, int, char **);
+void            args_free(struct args *);
+size_t          args_print(struct args *, char *, size_t);
+int             args_has(struct args *, u_char);
+void            args_set(struct args *, u_char, const char *);
+const char     *args_get(struct args *, u_char);
+long long       args_strtonum(
+                   struct args *, u_char, long long, long long, char **);
+
+/* cmd.c */
+int             cmd_pack_argv(int, char **, char *, size_t);
+int             cmd_unpack_argv(char *, size_t, int, char ***);
+char          **cmd_copy_argv(int, char *const *);
+void            cmd_free_argv(int, char **);
+struct cmd     *cmd_parse(int, char **, char **);
+int             cmd_exec(struct cmd *, struct cmd_ctx *);
+void            cmd_free(struct cmd *);
+size_t          cmd_print(struct cmd *, char *, size_t);
+struct session *cmd_current_session(struct cmd_ctx *, int);
+struct client  *cmd_current_client(struct cmd_ctx *);
+struct client  *cmd_find_client(struct cmd_ctx *, const char *);
+struct session *cmd_find_session(struct cmd_ctx *, const char *, int);
+struct winlink *cmd_find_window(
+                    struct cmd_ctx *, const char *, struct session **);
+int             cmd_find_index(
+                    struct cmd_ctx *, const char *, struct session **);
+struct winlink *cmd_find_pane(struct cmd_ctx *,
+                    const char *, struct session **, struct window_pane **);
+char           *cmd_template_replace(char *, const char *, int);
+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_capture_pane_entry;
+extern const struct cmd_entry cmd_choose_buffer_entry;
+extern const struct cmd_entry cmd_choose_client_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_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_display_message_entry;
+extern const struct cmd_entry cmd_display_panes_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_if_shell_entry;
+extern const struct cmd_entry cmd_join_pane_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_pane_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_panes_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_client_entry;
+extern const struct cmd_entry cmd_lock_server_entry;
+extern const struct cmd_entry cmd_lock_session_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_pipe_pane_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_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_run_shell_entry;
+extern const struct cmd_entry cmd_save_buffer_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_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_environment_entry;
+extern const struct cmd_entry cmd_set_option_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_environment_entry;
+extern const struct cmd_entry cmd_show_messages_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_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 **);
+
+/* client.c */
+int    client_main(int, char **, int);
+
+/* 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_clean(void);
+void    key_bindings_init(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;
+extern struct clients dead_clients;
+extern struct paste_stack global_buffers;
+int     server_start(void);
+void    server_update_socket(void);
+
+/* server-client.c */
+void    server_client_create(int);
+void    server_client_lost(struct client *);
+void    server_client_callback(int, short, void *);
+void    server_client_status_timer(void);
+void    server_client_loop(void);
+
+/* server-window.c */
+void    server_window_loop(void);
+
+/* server-fn.c */
+void    server_fill_environ(struct session *, struct environ *);
+void    server_write_client(
+            struct client *, enum msgtype, const void *, size_t);
+void    server_write_session(
+            struct session *, enum msgtype, const void *, size_t);
+void    server_redraw_client(struct client *);
+void    server_status_client(struct client *);
+void    server_redraw_session(struct session *);
+void    server_redraw_session_group(struct session *);
+void    server_status_session(struct session *);
+void    server_status_session_group(struct session *);
+void    server_redraw_window(struct window *);
+void    server_redraw_window_borders(struct window *);
+void    server_status_window(struct window *);
+void    server_lock(void);
+void    server_lock_session(struct session *);
+void    server_lock_client(struct client *);
+int     server_unlock(const char *);
+void    server_kill_window(struct window *);
+int     server_link_window(struct session *,
+            struct winlink *, struct session *, int, int, int, char **);
+void    server_unlink_window(struct session *, struct winlink *);
+void    server_destroy_pane(struct window_pane *);
+void    server_destroy_session_group(struct session *);
+void    server_destroy_session(struct session *);
+void    server_check_unattached (void);
+void    server_set_identify(struct client *);
+void    server_clear_identify(struct client *);
+void    server_update_event(struct client *);
+
+/* status.c */
+int     status_out_cmp(struct status_out *, struct status_out *);
+RB_PROTOTYPE(status_out_tree, status_out, entry, status_out_cmp);
+void    status_free_jobs(struct status_out_tree *);
+void    status_update_jobs(struct client *);
+void    status_set_window_at(struct client *, u_int);
+int     status_redraw(struct client *);
+char   *status_replace(struct client *, struct session *,
+            struct winlink *, struct window_pane *, const char *, time_t, int)
+    __attribute__((__format__(__strftime__, 5, 0)));
+void printflike2 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 *, const char *,
+            int (*)(void *, const char *), void (*)(void *), void *, int);
+void    status_prompt_clear(struct client *);
+int     status_prompt_redraw(struct client *);
+void    status_prompt_key(struct client *, int);
+void    status_prompt_update(struct client *, const char *, const char *);
+
+/* 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 *, struct mouse_event *);
+
+/* xterm-keys.c */
+char   *xterm_keys_lookup(int);
+int     xterm_keys_find(const char *, size_t, size_t *, int *);
+
+/* colour.c */
+void    colour_set_fg(struct grid_cell *, int);
+void    colour_set_bg(struct grid_cell *, int);
+const char *colour_tostring(int);
+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_collect_history(struct grid *);
+void    grid_scroll_history(struct grid *);
+void    grid_scroll_history_region(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);
+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_move_cells(struct grid *, u_int, u_int, u_int, u_int);
+char   *grid_string_cells(struct grid *, u_int, u_int, u_int);
+void    grid_duplicate_lines(
+            struct grid *, u_int, struct grid *, u_int, u_int);
+
+/* grid-utf8.c */
+size_t  grid_utf8_size(const struct grid_utf8 *);
+size_t  grid_utf8_copy(const struct grid_utf8 *, char *, size_t);
+void    grid_utf8_set(struct grid_utf8 *, const struct utf8_data *);
+int     grid_utf8_append(struct grid_utf8 *, const struct utf8_data *);
+int     grid_utf8_compare(const struct grid_utf8 *, const struct grid_utf8 *);
+
+/* 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_history(struct grid *);
+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);
+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);
+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);
+char   *grid_view_string_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 *);
+size_t printflike2 screen_write_cstrlen(int, const char *, ...);
+void printflike5 screen_write_cnputs(struct screen_write_ctx *,
+            ssize_t, struct grid_cell *, int, const char *, ...);
+size_t printflike2 screen_write_strlen(int, const char *, ...);
+void printflike3 screen_write_puts(struct screen_write_ctx *,
+            struct grid_cell *, const char *, ...);
+void printflike5 screen_write_nputs(struct screen_write_ctx *,
+            ssize_t, struct grid_cell *, int, const char *, ...);
+void    screen_write_vnputs(struct screen_write_ctx *,
+            ssize_t, struct grid_cell *, int, const char *, va_list);
+void    screen_write_parsestyle(
+            struct grid_cell *, 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_backspace(struct screen_write_ctx *);
+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_alignmenttest(struct screen_write_ctx *);
+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_utf8mousemode(struct screen_write_ctx *, int);
+void    screen_write_mousemode_on(struct screen_write_ctx *, int);
+void    screen_write_mousemode_off(struct screen_write_ctx *);
+void    screen_write_linefeed(struct screen_write_ctx *, int);
+void    screen_write_linefeedscreen(struct screen_write_ctx *, int);
+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 *, const struct utf8_data *);
+void    screen_write_setselection(struct screen_write_ctx *, u_char *, u_int);
+void    screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int);
+
+/* screen-redraw.c */
+void    screen_redraw_screen(struct client *, int, int);
+void    screen_redraw_pane(struct client *, struct window_pane *);
+
+/* 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_reset_tabs(struct screen *);
+void    screen_set_cursor_style(struct screen *, u_int);
+void    screen_set_cursor_colour(struct screen *, const char *);
+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, u_int, struct grid_cell *);
+void    screen_clear_selection(struct screen *);
+int     screen_check_selection(struct screen *, u_int, u_int);
+
+/* window.c */
+extern struct windows windows;
+extern struct window_pane_tree all_window_panes;
+int             winlink_cmp(struct winlink *, struct winlink *);
+RB_PROTOTYPE(winlinks, winlink, entry, winlink_cmp);
+int             window_pane_cmp(struct window_pane *, struct window_pane *);
+RB_PROTOTYPE(window_pane_tree, window_pane, tree_entry, window_pane_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 *, int);
+u_int           winlink_count(struct winlinks *);
+struct winlink *winlink_add(struct winlinks *, int);
+void            winlink_set_window(struct winlink *, struct window *);
+void            winlink_remove(struct winlinks *, struct winlink *);
+struct winlink *winlink_next(struct winlink *);
+struct winlink *winlink_previous(struct winlink *);
+struct winlink *winlink_next_by_number(struct winlink *, struct session *,
+                    int);
+struct winlink *winlink_previous_by_number(struct winlink *, struct session *,
+                    int);
+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 *, struct environ *, struct termios *,
+                    u_int, u_int, u_int, char **);
+void            window_destroy(struct window *);
+struct window_pane *window_get_active_at(struct window *, u_int, u_int);
+void            window_set_active_at(struct window *, u_int, u_int);
+struct window_pane *window_find_string(struct window *, const char *);
+void            window_set_active_pane(struct window *, struct window_pane *);
+struct window_pane *window_add_pane(struct window *, u_int);
+void            window_resize(struct window *, u_int, u_int);
+void            window_remove_pane(struct window *, struct window_pane *);
+struct window_pane *window_pane_at_index(struct window *, u_int);
+struct window_pane *window_pane_next_by_number(struct window *,
+                       struct window_pane *, u_int);
+struct window_pane *window_pane_previous_by_number(struct window *,
+                       struct window_pane *, u_int);
+u_int           window_pane_index(struct window *, struct window_pane *);
+u_int           window_count_panes(struct window *);
+void            window_destroy_panes(struct window *);
+struct window_pane *window_pane_find_by_id(u_int);
+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 *, struct environ *,
+                    struct termios *, char **);
+void            window_pane_resize(struct window_pane *, u_int, u_int);
+void            window_pane_alternate_on(
+                    struct window_pane *, struct grid_cell *);
+void            window_pane_alternate_off(
+                    struct window_pane *, struct grid_cell *);
+int             window_pane_set_mode(
+                    struct window_pane *, const struct window_mode *);
+void            window_pane_reset_mode(struct window_pane *);
+void            window_pane_key(struct window_pane *, struct session *, int);
+void            window_pane_mouse(struct window_pane *,
+                    struct session *, struct mouse_event *);
+int             window_pane_visible(struct window_pane *);
+char           *window_pane_search(
+                    struct window_pane *, const char *, u_int *);
+char           *window_printable_flags(struct session *, struct winlink *);
+
+struct window_pane *window_pane_find_up(struct window_pane *);
+struct window_pane *window_pane_find_down(struct window_pane *);
+struct window_pane *window_pane_find_left(struct window_pane *);
+struct window_pane *window_pane_find_right(struct window_pane *);
+
+/* layout.c */
+u_int           layout_count_cells(struct layout_cell *);
+struct layout_cell *layout_create_cell(struct layout_cell *);
+void            layout_free_cell(struct layout_cell *);
+void            layout_print_cell(struct layout_cell *, const char *, u_int);
+void            layout_destroy_cell(struct layout_cell *, struct layout_cell **);
+void            layout_set_size(
+                    struct layout_cell *, u_int, u_int, u_int, u_int);
+void            layout_make_leaf(
+                    struct layout_cell *, struct window_pane *);
+void            layout_make_node(struct layout_cell *, enum layout_type);
+void            layout_fix_offsets(struct layout_cell *);
+void            layout_fix_panes(struct window *, u_int, u_int);
+u_int           layout_resize_check(struct layout_cell *, enum layout_type);
+void            layout_resize_adjust(
+                    struct layout_cell *, enum layout_type, int);
+void            layout_init(struct window *);
+void            layout_free(struct window *);
+void            layout_resize(struct window *, u_int, u_int);
+void            layout_resize_pane(
+                    struct window_pane *, enum layout_type, int);
+void            layout_resize_pane_mouse(
+                    struct client *c, struct mouse_event *mouse);
+void            layout_assign_pane(struct layout_cell *, struct window_pane *);
+struct layout_cell *layout_split_pane(
+                    struct window_pane *, enum layout_type, int);
+void            layout_close_pane(struct window_pane *);
+
+/* layout-custom.c */
+char           *layout_dump(struct window *);
+int             layout_parse(struct window *, const char *);
+
+/* layout-set.c */
+const char     *layout_set_name(u_int);
+int             layout_set_lookup(const char *);
+u_int           layout_set_select(struct window *, u_int);
+u_int           layout_set_next(struct window *);
+u_int           layout_set_previous(struct window *);
+void            layout_set_active_changed(struct window *);
+
+/* 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_init_from_pane(struct window_pane *);
+void            window_copy_init_for_output(struct window_pane *);
+void            window_copy_add(struct window_pane *, const char *, ...);
+void            window_copy_vadd(struct window_pane *, const char *, va_list);
+void            window_copy_pageup(struct window_pane *);
+
+/* 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 (*)(void *), void *);
+
+/* names.c */
+void            queue_window_name(struct window *);
+char           *default_window_name(struct window *);
+
+/* signal.c */
+void set_signals(void(*)(int, short, void *));
+void clear_signals(int);
+
+/* session.c */
+extern struct sessions sessions;
+extern struct sessions dead_sessions;
+extern struct session_groups session_groups;
+int    session_cmp(struct session *, struct session *);
+RB_PROTOTYPE(sessions, session, entry, session_cmp);
+int             session_alive(struct session *);
+struct session *session_find(const char *);
+struct session *session_find_by_index(u_int);
+struct session *session_create(const char *, const char *, const char *,
+                    struct environ *, struct termios *, int, u_int, u_int,
+                    char **);
+void            session_destroy(struct session *);
+int             session_check_name(const char *);
+void            session_update_activity(struct session *);
+struct session *session_next_session(struct session *);
+struct session *session_previous_session(struct session *);
+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 *);
+struct winlink*         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 *);
+struct session_group *session_group_find(struct session *);
+u_int           session_group_index(struct session_group *);
+void            session_group_add(struct session *, struct session *);
+void            session_group_remove(struct session *);
+void            session_group_synchronize_to(struct session *);
+void            session_group_synchronize_from(struct session *);
+void            session_group_synchronize1(struct session *, struct session *);
+
+/* utf8.c */
+void   utf8_build(void);
+int    utf8_open(struct utf8_data *, u_char);
+int    utf8_append(struct utf8_data *, u_char);
+u_int  utf8_combine(const struct utf8_data *);
+u_int  utf8_split2(u_int, u_char *);
+
+/* osdep-*.c */
+char           *osdep_get_name(int, char *);
+struct event_base *osdep_event_init(void);
+
+/* log.c */
+void            log_open_tty(int);
+void            log_open_file(int, const char *);
+void            log_close(void);
+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 *, ...);
+__dead void printflike1 log_fatal(const char *, ...);
+__dead void printflike1 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);
+
+/* utmp.c */
+struct window_utmp *utmp_create(const char *);
+void            utmp_destroy(struct window_utmp *);
+
+#endif /* TMUX_H */
diff --git a/external/bsd/tmux/dist/tty-acs.c b/external/bsd/tmux/dist/tty-acs.c
new file mode 100644 (file)
index 0000000..48e9940
--- /dev/null
@@ -0,0 +1,97 @@
+/* $Id: tty-acs.c,v 1.3 2011/08/17 18:48:36 jmmv Exp $ */
+
+/*
+ * Copyright (c) 2010 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"
+
+int    tty_acs_cmp(const void *, const void *);
+
+/* Table mapping ACS entries to UTF-8. */
+struct tty_acs_entry {
+       u_char           key;
+       const char      *string;
+};
+const struct tty_acs_entry tty_acs_table[] = {
+       { '+', "\342\206\222" },
+       { ',', "\342\206\220" },
+       { '-', "\342\206\221" },
+       { '.', "\342\206\223" },
+       { '0', "\342\226\256" },
+       { '`', "\342\227\206" },
+       { 'a', "\342\226\222" },
+       { 'f', "\302\260" },
+       { 'g', "\302\261" },
+       { 'h', "\342\226\222" },
+       { 'i', "\342\230\203" },
+       { 'j', "\342\224\230" },
+       { 'k', "\342\224\220" },
+       { 'l', "\342\224\214" },
+       { 'm', "\342\224\224" },
+       { 'n', "\342\224\274" },
+       { 'o', "\342\216\272" },
+       { 'p', "\342\216\273" },
+       { 'q', "\342\224\200" },
+       { 'r', "\342\216\274" },
+       { 's', "\342\216\275" },
+       { 't', "\342\224\234" },
+       { 'u', "\342\224\244" },
+       { 'v', "\342\224\264" },
+       { 'w', "\342\224\254" },
+       { 'x', "\342\224\202" },
+       { 'y', "\342\211\244" },
+       { 'z', "\342\211\245" },
+       { '{', "\317\200" },
+       { '|', "\342\211\240" },
+       { '}', "\302\243" },
+       { '~', "\302\267" }
+};
+
+int
+tty_acs_cmp(const void *key, const void *value)
+{
+       const struct tty_acs_entry      *entry = value;
+       u_char                           ch;
+
+       ch = *(const u_char *) key;
+       return (ch - entry->key);
+}
+
+/* Retrieve ACS to output as a string. */
+const char *
+tty_acs_get(struct tty *tty, u_char ch)
+{
+       struct tty_acs_entry *entry;
+
+       /* If not a UTF-8 terminal, use the ACS set. */
+       if (!(tty->flags & TTY_UTF8)) {
+               if (tty->term->acs[ch][0] == '\0')
+                       return (NULL);
+               return (&tty->term->acs[ch][0]);
+       }
+
+       /* Otherwise look up the UTF-8 translation. */
+       entry = bsearch(&ch,
+           tty_acs_table, nitems(tty_acs_table), sizeof tty_acs_table[0],
+           tty_acs_cmp);
+       if (entry == NULL)
+               return (NULL);
+       return (entry->string);
+}
diff --git a/external/bsd/tmux/dist/tty-keys.c b/external/bsd/tmux/dist/tty-keys.c
new file mode 100644 (file)
index 0000000..90101fe
--- /dev/null
@@ -0,0 +1,657 @@
+/* $Id: tty-keys.c,v 1.4 2011/08/17 19:28:36 jmmv 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 <termios.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Handle keys input from the outside terminal. tty_keys[] is a base table of
+ * supported keys which are looked up in terminfo(5) and translated into a
+ * ternary tree (a binary tree of binary trees).
+ */
+
+void           tty_keys_add1(struct tty_key **, const char *, int);
+void           tty_keys_add(struct tty *, const char *, int);
+void           tty_keys_free1(struct tty_key *);
+struct tty_key *tty_keys_find1(
+                   struct tty_key *, const char *, size_t, size_t *);
+struct tty_key *tty_keys_find(struct tty *, const char *, size_t, size_t *);
+void           tty_keys_callback(int, short, void *);
+int            tty_keys_mouse(struct tty *,
+                   const char *, size_t, size_t *, struct mouse_event *);
+
+struct tty_key_ent {
+       enum tty_code_code      code;
+       const char             *string;
+
+       int                     key;
+       int                     flags;
+#define TTYKEY_RAW 0x1
+};
+
+/*
+ * Default key tables. Those flagged with TTYKEY_RAW are inserted directly,
+ * otherwise they are looked up in terminfo(5).
+ */
+const struct tty_key_ent tty_keys[] = {
+       /*
+        * Numeric keypad. Just use 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_KP_SLASH,          TTYKEY_RAW },
+       { 0,    "\033Oj",       KEYC_KP_STAR,           TTYKEY_RAW },
+       { 0,    "\033Om",       KEYC_KP_MINUS,          TTYKEY_RAW },
+       { 0,    "\033Ow",       KEYC_KP_SEVEN,          TTYKEY_RAW },
+       { 0,    "\033Ox",       KEYC_KP_EIGHT,          TTYKEY_RAW },
+       { 0,    "\033Oy",       KEYC_KP_NINE,           TTYKEY_RAW },
+       { 0,    "\033Ok",       KEYC_KP_PLUS,           TTYKEY_RAW },
+       { 0,    "\033Ot",       KEYC_KP_FOUR,           TTYKEY_RAW },
+       { 0,    "\033Ou",       KEYC_KP_FIVE,           TTYKEY_RAW },
+       { 0,    "\033Ov",       KEYC_KP_SIX,            TTYKEY_RAW },
+       { 0,    "\033Oq",       KEYC_KP_ONE,            TTYKEY_RAW },
+       { 0,    "\033Or",       KEYC_KP_TWO,            TTYKEY_RAW },
+       { 0,    "\033Os",       KEYC_KP_THREE,          TTYKEY_RAW },
+       { 0,    "\033OM",       KEYC_KP_ENTER,          TTYKEY_RAW },
+       { 0,    "\033Op",       KEYC_KP_ZERO,           TTYKEY_RAW },
+       { 0,    "\033On",       KEYC_KP_PERIOD,         TTYKEY_RAW },
+
+       /* 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 },
+
+       /* rxvt-style arrow + modifier keys. */
+       { 0,    "\033Oa",       KEYC_UP|KEYC_CTRL,      TTYKEY_RAW },
+       { 0,    "\033Ob",       KEYC_DOWN|KEYC_CTRL,    TTYKEY_RAW },
+       { 0,    "\033Oc",       KEYC_RIGHT|KEYC_CTRL,   TTYKEY_RAW },
+       { 0,    "\033Od",       KEYC_LEFT|KEYC_CTRL,    TTYKEY_RAW },
+
+       { 0,    "\033[a",       KEYC_UP|KEYC_SHIFT,     TTYKEY_RAW },
+       { 0,    "\033[b",       KEYC_DOWN|KEYC_SHIFT,   TTYKEY_RAW },
+       { 0,    "\033[c",       KEYC_RIGHT|KEYC_SHIFT,  TTYKEY_RAW },
+       { 0,    "\033[d",       KEYC_LEFT|KEYC_SHIFT,   TTYKEY_RAW },
+
+       /*
+        * rxvt-style function + modifier keys:
+        *              Ctrl = ^, Shift = $, Ctrl+Shift = @
+        */
+       { 0,    "\033[11^",     KEYC_F1|KEYC_CTRL,      TTYKEY_RAW },
+       { 0,    "\033[12^",     KEYC_F2|KEYC_CTRL,      TTYKEY_RAW },
+       { 0,    "\033[13^",     KEYC_F3|KEYC_CTRL,      TTYKEY_RAW },
+       { 0,    "\033[14^",     KEYC_F4|KEYC_CTRL,      TTYKEY_RAW },
+       { 0,    "\033[15^",     KEYC_F5|KEYC_CTRL,      TTYKEY_RAW },
+       { 0,    "\033[17^",     KEYC_F6|KEYC_CTRL,      TTYKEY_RAW },
+       { 0,    "\033[18^",     KEYC_F7|KEYC_CTRL,      TTYKEY_RAW },
+       { 0,    "\033[19^",     KEYC_F8|KEYC_CTRL,      TTYKEY_RAW },
+       { 0,    "\033[20^",     KEYC_F9|KEYC_CTRL,      TTYKEY_RAW },
+       { 0,    "\033[21^",     KEYC_F10|KEYC_CTRL,     TTYKEY_RAW },
+       { 0,    "\033[23^",     KEYC_F11|KEYC_CTRL,     TTYKEY_RAW },
+       { 0,    "\033[24^",     KEYC_F12|KEYC_CTRL,     TTYKEY_RAW },
+       { 0,    "\033[25^",     KEYC_F13|KEYC_CTRL,     TTYKEY_RAW },
+       { 0,    "\033[26^",     KEYC_F14|KEYC_CTRL,     TTYKEY_RAW },
+       { 0,    "\033[28^",     KEYC_F15|KEYC_CTRL,     TTYKEY_RAW },
+       { 0,    "\033[29^",     KEYC_F16|KEYC_CTRL,     TTYKEY_RAW },
+       { 0,    "\033[31^",     KEYC_F17|KEYC_CTRL,     TTYKEY_RAW },
+       { 0,    "\033[32^",     KEYC_F18|KEYC_CTRL,     TTYKEY_RAW },
+       { 0,    "\033[33^",     KEYC_F19|KEYC_CTRL,     TTYKEY_RAW },
+       { 0,    "\033[34^",     KEYC_F20|KEYC_CTRL,     TTYKEY_RAW },
+       { 0,    "\033[2^",      KEYC_IC|KEYC_CTRL,      TTYKEY_RAW },
+       { 0,    "\033[3^",      KEYC_DC|KEYC_CTRL,      TTYKEY_RAW },
+       { 0,    "\033[7^",      KEYC_HOME|KEYC_CTRL,    TTYKEY_RAW },
+       { 0,    "\033[8^",      KEYC_END|KEYC_CTRL,     TTYKEY_RAW },
+       { 0,    "\033[6^",      KEYC_NPAGE|KEYC_CTRL,   TTYKEY_RAW },
+       { 0,    "\033[5^",      KEYC_PPAGE|KEYC_CTRL,   TTYKEY_RAW },
+
+       { 0,    "\033[11$",     KEYC_F1|KEYC_SHIFT,     TTYKEY_RAW },
+       { 0,    "\033[12$",     KEYC_F2|KEYC_SHIFT,     TTYKEY_RAW },
+       { 0,    "\033[13$",     KEYC_F3|KEYC_SHIFT,     TTYKEY_RAW },
+       { 0,    "\033[14$",     KEYC_F4|KEYC_SHIFT,     TTYKEY_RAW },
+       { 0,    "\033[15$",     KEYC_F5|KEYC_SHIFT,     TTYKEY_RAW },
+       { 0,    "\033[17$",     KEYC_F6|KEYC_SHIFT,     TTYKEY_RAW },
+       { 0,    "\033[18$",     KEYC_F7|KEYC_SHIFT,     TTYKEY_RAW },
+       { 0,    "\033[19$",     KEYC_F8|KEYC_SHIFT,     TTYKEY_RAW },
+       { 0,    "\033[20$",     KEYC_F9|KEYC_SHIFT,     TTYKEY_RAW },
+       { 0,    "\033[21$",     KEYC_F10|KEYC_SHIFT,    TTYKEY_RAW },
+       { 0,    "\033[23$",     KEYC_F11|KEYC_SHIFT,    TTYKEY_RAW },
+       { 0,    "\033[24$",     KEYC_F12|KEYC_SHIFT,    TTYKEY_RAW },
+       { 0,    "\033[25$",     KEYC_F13|KEYC_SHIFT,    TTYKEY_RAW },
+       { 0,    "\033[26$",     KEYC_F14|KEYC_SHIFT,    TTYKEY_RAW },
+       { 0,    "\033[28$",     KEYC_F15|KEYC_SHIFT,    TTYKEY_RAW },
+       { 0,    "\033[29$",     KEYC_F16|KEYC_SHIFT,    TTYKEY_RAW },
+       { 0,    "\033[31$",     KEYC_F17|KEYC_SHIFT,    TTYKEY_RAW },
+       { 0,    "\033[32$",     KEYC_F18|KEYC_SHIFT,    TTYKEY_RAW },
+       { 0,    "\033[33$",     KEYC_F19|KEYC_SHIFT,    TTYKEY_RAW },
+       { 0,    "\033[34$",     KEYC_F20|KEYC_SHIFT,    TTYKEY_RAW },
+       { 0,    "\033[2$",      KEYC_IC|KEYC_SHIFT,     TTYKEY_RAW },
+       { 0,    "\033[3$",      KEYC_DC|KEYC_SHIFT,     TTYKEY_RAW },
+       { 0,    "\033[7$",      KEYC_HOME|KEYC_SHIFT,   TTYKEY_RAW },
+       { 0,    "\033[8$",      KEYC_END|KEYC_SHIFT,    TTYKEY_RAW },
+       { 0,    "\033[6$",      KEYC_NPAGE|KEYC_SHIFT,  TTYKEY_RAW },
+       { 0,    "\033[5$",      KEYC_PPAGE|KEYC_SHIFT,  TTYKEY_RAW },
+
+       { 0,    "\033[11@",     KEYC_F1|KEYC_CTRL|KEYC_SHIFT,   TTYKEY_RAW },
+       { 0,    "\033[12@",     KEYC_F2|KEYC_CTRL|KEYC_SHIFT,   TTYKEY_RAW },
+       { 0,    "\033[13@",     KEYC_F3|KEYC_CTRL|KEYC_SHIFT,   TTYKEY_RAW },
+       { 0,    "\033[14@",     KEYC_F4|KEYC_CTRL|KEYC_SHIFT,   TTYKEY_RAW },
+       { 0,    "\033[15@",     KEYC_F5|KEYC_CTRL|KEYC_SHIFT,   TTYKEY_RAW },
+       { 0,    "\033[17@",     KEYC_F6|KEYC_CTRL|KEYC_SHIFT,   TTYKEY_RAW },
+       { 0,    "\033[18@",     KEYC_F7|KEYC_CTRL|KEYC_SHIFT,   TTYKEY_RAW },
+       { 0,    "\033[19@",     KEYC_F8|KEYC_CTRL|KEYC_SHIFT,   TTYKEY_RAW },
+       { 0,    "\033[20@",     KEYC_F9|KEYC_CTRL|KEYC_SHIFT,   TTYKEY_RAW },
+       { 0,    "\033[21@",     KEYC_F10|KEYC_CTRL|KEYC_SHIFT,  TTYKEY_RAW },
+       { 0,    "\033[23@",     KEYC_F11|KEYC_CTRL|KEYC_SHIFT,  TTYKEY_RAW },
+       { 0,    "\033[24@",     KEYC_F12|KEYC_CTRL|KEYC_SHIFT,  TTYKEY_RAW },
+       { 0,    "\033[25@",     KEYC_F13|KEYC_CTRL|KEYC_SHIFT,  TTYKEY_RAW },
+       { 0,    "\033[26@",     KEYC_F14|KEYC_CTRL|KEYC_SHIFT,  TTYKEY_RAW },
+       { 0,    "\033[28@",     KEYC_F15|KEYC_CTRL|KEYC_SHIFT,  TTYKEY_RAW },
+       { 0,    "\033[29@",     KEYC_F16|KEYC_CTRL|KEYC_SHIFT,  TTYKEY_RAW },
+       { 0,    "\033[31@",     KEYC_F17|KEYC_CTRL|KEYC_SHIFT,  TTYKEY_RAW },
+       { 0,    "\033[32@",     KEYC_F18|KEYC_CTRL|KEYC_SHIFT,  TTYKEY_RAW },
+       { 0,    "\033[33@",     KEYC_F19|KEYC_CTRL|KEYC_SHIFT,  TTYKEY_RAW },
+       { 0,    "\033[34@",     KEYC_F20|KEYC_CTRL|KEYC_SHIFT,  TTYKEY_RAW },
+       { 0,    "\033[2@",      KEYC_IC|KEYC_CTRL|KEYC_SHIFT,   TTYKEY_RAW },
+       { 0,    "\033[3@",      KEYC_DC|KEYC_CTRL|KEYC_SHIFT,   TTYKEY_RAW },
+       { 0,    "\033[7@",      KEYC_HOME|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW },
+       { 0,    "\033[8@",      KEYC_END|KEYC_CTRL|KEYC_SHIFT,  TTYKEY_RAW },
+       { 0,    "\033[6@",      KEYC_NPAGE|KEYC_CTRL|KEYC_SHIFT,TTYKEY_RAW },
+       { 0,    "\033[5@",      KEYC_PPAGE|KEYC_CTRL|KEYC_SHIFT,TTYKEY_RAW },
+
+       /* terminfo lookups below this line so they can override raw keys. */
+
+       /* Function keys. */
+       { TTYC_KF1,     NULL,           KEYC_F1,                0 },
+       { TTYC_KF2,     NULL,           KEYC_F2,                0 },
+       { TTYC_KF3,     NULL,           KEYC_F3,                0 },
+       { TTYC_KF4,     NULL,           KEYC_F4,                0 },
+       { TTYC_KF5,     NULL,           KEYC_F5,                0 },
+       { TTYC_KF6,     NULL,           KEYC_F6,                0 },
+       { TTYC_KF7,     NULL,           KEYC_F7,                0 },
+       { TTYC_KF8,     NULL,           KEYC_F8,                0 },
+       { TTYC_KF9,     NULL,           KEYC_F9,                0 },
+       { TTYC_KF10,    NULL,           KEYC_F10,               0 },
+       { TTYC_KF11,    NULL,           KEYC_F11,               0 },
+       { TTYC_KF12,    NULL,           KEYC_F12,               0 },
+       { TTYC_KF13,    NULL,           KEYC_F13,               0 },
+       { TTYC_KF14,    NULL,           KEYC_F14,               0 },
+       { TTYC_KF15,    NULL,           KEYC_F15,               0 },
+       { TTYC_KF16,    NULL,           KEYC_F16,               0 },
+       { TTYC_KF17,    NULL,           KEYC_F17,               0 },
+       { TTYC_KF18,    NULL,           KEYC_F18,               0 },
+       { TTYC_KF19,    NULL,           KEYC_F19,               0 },
+       { TTYC_KF20,    NULL,           KEYC_F20,               0 },
+       { TTYC_KICH1,   NULL,           KEYC_IC,                0 },
+       { TTYC_KDCH1,   NULL,           KEYC_DC,                0 },
+       { TTYC_KHOME,   NULL,           KEYC_HOME,              0 },
+       { TTYC_KEND,    NULL,           KEYC_END,               0 },
+       { TTYC_KNP,     NULL,           KEYC_NPAGE,             0 },
+       { TTYC_KPP,     NULL,           KEYC_PPAGE,             0 },
+       { TTYC_KCBT,    NULL,           KEYC_BTAB,              0 },
+
+       /* Arrow keys from terminfo. */
+       { TTYC_KCUU1,   NULL,           KEYC_UP,                0 },
+       { TTYC_KCUD1,   NULL,           KEYC_DOWN,              0 },
+       { TTYC_KCUB1,   NULL,           KEYC_LEFT,              0 },
+       { TTYC_KCUF1,   NULL,           KEYC_RIGHT,             0 },
+
+       /* Key and modifier capabilities. */
+       { TTYC_KDC2,    NULL,           KEYC_DC|KEYC_SHIFT,     0 },
+       { TTYC_KDC3,    NULL,           KEYC_DC|KEYC_ESCAPE,    0 },
+       { TTYC_KDC4,    NULL,           KEYC_DC|KEYC_SHIFT|KEYC_ESCAPE, 0 },
+       { TTYC_KDC5,    NULL,           KEYC_DC|KEYC_CTRL,      0 },
+       { TTYC_KDC6,    NULL,           KEYC_DC|KEYC_SHIFT|KEYC_CTRL, 0 },
+       { TTYC_KDC7,    NULL,           KEYC_DC|KEYC_ESCAPE|KEYC_CTRL, 0 },
+       { TTYC_KDN2,    NULL,           KEYC_DOWN|KEYC_SHIFT,   0 },
+       { TTYC_KDN3,    NULL,           KEYC_DOWN|KEYC_ESCAPE,  0 },
+       { TTYC_KDN4,    NULL,           KEYC_DOWN|KEYC_SHIFT|KEYC_ESCAPE, 0 },
+       { TTYC_KDN5,    NULL,           KEYC_DOWN|KEYC_CTRL,    0 },
+       { TTYC_KDN6,    NULL,           KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL, 0 },
+       { TTYC_KDN7,    NULL,           KEYC_DOWN|KEYC_ESCAPE|KEYC_CTRL, 0 },
+       { TTYC_KEND2,   NULL,           KEYC_END|KEYC_SHIFT,    0 },
+       { TTYC_KEND3,   NULL,           KEYC_END|KEYC_ESCAPE,   0 },
+       { TTYC_KEND4,   NULL,           KEYC_END|KEYC_SHIFT|KEYC_ESCAPE, 0 },
+       { TTYC_KEND5,   NULL,           KEYC_END|KEYC_CTRL,     0 },
+       { TTYC_KEND6,   NULL,           KEYC_END|KEYC_SHIFT|KEYC_CTRL, 0 },
+       { TTYC_KEND7,   NULL,           KEYC_END|KEYC_ESCAPE|KEYC_CTRL, 0 },
+       { TTYC_KHOM2,   NULL,           KEYC_HOME|KEYC_SHIFT,   0 },
+       { TTYC_KHOM3,   NULL,           KEYC_HOME|KEYC_ESCAPE,  0 },
+       { TTYC_KHOM4,   NULL,           KEYC_HOME|KEYC_SHIFT|KEYC_ESCAPE, 0 },
+       { TTYC_KHOM5,   NULL,           KEYC_HOME|KEYC_CTRL,    0 },
+       { TTYC_KHOM6,   NULL,           KEYC_HOME|KEYC_SHIFT|KEYC_CTRL, 0 },
+       { TTYC_KHOM7,   NULL,           KEYC_HOME|KEYC_ESCAPE|KEYC_CTRL, 0 },
+       { TTYC_KIC2,    NULL,           KEYC_IC|KEYC_SHIFT,     0 },
+       { TTYC_KIC3,    NULL,           KEYC_IC|KEYC_ESCAPE,    0 },
+       { TTYC_KIC4,    NULL,           KEYC_IC|KEYC_SHIFT|KEYC_ESCAPE, 0 },
+       { TTYC_KIC5,    NULL,           KEYC_IC|KEYC_CTRL,      0 },
+       { TTYC_KIC6,    NULL,           KEYC_IC|KEYC_SHIFT|KEYC_CTRL, 0 },
+       { TTYC_KIC7,    NULL,           KEYC_IC|KEYC_ESCAPE|KEYC_CTRL, 0 },
+       { TTYC_KLFT2,   NULL,           KEYC_LEFT|KEYC_SHIFT,   0 },
+       { TTYC_KLFT3,   NULL,           KEYC_LEFT|KEYC_ESCAPE,  0 },
+       { TTYC_KLFT4,   NULL,           KEYC_LEFT|KEYC_SHIFT|KEYC_ESCAPE, 0 },
+       { TTYC_KLFT5,   NULL,           KEYC_LEFT|KEYC_CTRL,    0 },
+       { TTYC_KLFT6,   NULL,           KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL, 0 },
+       { TTYC_KLFT7,   NULL,           KEYC_LEFT|KEYC_ESCAPE|KEYC_CTRL, 0 },
+       { TTYC_KNXT2,   NULL,           KEYC_NPAGE|KEYC_SHIFT,  0 },
+       { TTYC_KNXT3,   NULL,           KEYC_NPAGE|KEYC_ESCAPE, 0 },
+       { TTYC_KNXT4,   NULL,           KEYC_NPAGE|KEYC_SHIFT|KEYC_ESCAPE, 0 },
+       { TTYC_KNXT5,   NULL,           KEYC_NPAGE|KEYC_CTRL,   0 },
+       { TTYC_KNXT6,   NULL,           KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL, 0 },
+       { TTYC_KNXT7,   NULL,           KEYC_NPAGE|KEYC_ESCAPE|KEYC_CTRL, 0 },
+       { TTYC_KPRV2,   NULL,           KEYC_PPAGE|KEYC_SHIFT,  0 },
+       { TTYC_KPRV3,   NULL,           KEYC_PPAGE|KEYC_ESCAPE, 0 },
+       { TTYC_KPRV4,   NULL,           KEYC_PPAGE|KEYC_SHIFT|KEYC_ESCAPE, 0 },
+       { TTYC_KPRV5,   NULL,           KEYC_PPAGE|KEYC_CTRL,   0 },
+       { TTYC_KPRV6,   NULL,           KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL, 0 },
+       { TTYC_KPRV7,   NULL,           KEYC_PPAGE|KEYC_ESCAPE|KEYC_CTRL, 0 },
+       { TTYC_KRIT2,   NULL,           KEYC_RIGHT|KEYC_SHIFT,  0 },
+       { TTYC_KRIT3,   NULL,           KEYC_RIGHT|KEYC_ESCAPE, 0 },
+       { TTYC_KRIT4,   NULL,           KEYC_RIGHT|KEYC_SHIFT|KEYC_ESCAPE, 0 },
+       { TTYC_KRIT5,   NULL,           KEYC_RIGHT|KEYC_CTRL,   0 },
+       { TTYC_KRIT6,   NULL,           KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL, 0 },
+       { TTYC_KRIT7,   NULL,           KEYC_RIGHT|KEYC_ESCAPE|KEYC_CTRL, 0 },
+       { TTYC_KUP2,    NULL,           KEYC_UP|KEYC_SHIFT,     0 },
+       { TTYC_KUP3,    NULL,           KEYC_UP|KEYC_ESCAPE,    0 },
+       { TTYC_KUP4,    NULL,           KEYC_UP|KEYC_SHIFT|KEYC_ESCAPE, 0 },
+       { TTYC_KUP5,    NULL,           KEYC_UP|KEYC_CTRL,      0 },
+       { TTYC_KUP6,    NULL,           KEYC_UP|KEYC_SHIFT|KEYC_CTRL, 0 },
+       { TTYC_KUP7,    NULL,           KEYC_UP|KEYC_ESCAPE|KEYC_CTRL, 0 },
+};
+
+void
+tty_keys_add(struct tty *tty, const char *s, int key)
+{
+       struct tty_key  *tk;
+       size_t           size;
+       const char      *keystr;
+
+       keystr = key_string_lookup_key(key);
+       if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) {
+               log_debug("new key %s: 0x%x (%s)", s, key, keystr);
+               tty_keys_add1(&tty->key_tree, s, key);
+       } else {
+               log_debug("replacing key %s: 0x%x (%s)", s, key, keystr);
+               tk->key = key;
+       }
+}
+
+/* Add next node to the tree. */
+void
+tty_keys_add1(struct tty_key **tkp, const char *s, int key)
+{
+       struct tty_key  *tk;
+
+       /* Allocate a tree entry if there isn't one already. */
+       tk = *tkp;
+       if (tk == NULL) {
+               tk = *tkp = xcalloc(1, sizeof *tk);
+               tk->ch = *s;
+               tk->key = KEYC_NONE;
+       }
+
+       /* Find the next entry. */
+       if (*s == tk->ch) {
+               /* Move forward in string. */
+               s++;
+
+               /* If this is the end of the string, no more is necessary. */
+               if (*s == '\0') {
+                       tk->key = key;
+                       return;
+               }
+
+               /* Use the child tree for the next character. */
+               tkp = &tk->next;
+       } else {
+               if (*s < tk->ch)
+                       tkp = &tk->left;
+               else if (*s > tk->ch)
+                       tkp = &tk->right;
+       }
+
+       /* And recurse to add it. */
+       tty_keys_add1(tkp, s, key);
+}
+
+/* Initialise a key tree from the table. */
+void
+tty_keys_init(struct tty *tty)
+{
+       const struct tty_key_ent        *tke;
+       u_int                            i;
+       const char                      *s;
+
+       tty->key_tree = NULL;
+       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);
+       }
+}
+
+/* Free the entire key tree. */
+void
+tty_keys_free(struct tty *tty)
+{
+       tty_keys_free1(tty->key_tree);
+}
+
+/* Free a single key. */
+void
+tty_keys_free1(struct tty_key *tk)
+{
+       if (tk->next != NULL)
+               tty_keys_free1(tk->next);
+       if (tk->left != NULL)
+               tty_keys_free1(tk->left);
+       if (tk->right != NULL)
+               tty_keys_free1(tk->right);
+       xfree(tk);
+}
+
+/* Lookup a key in the tree. */
+struct tty_key *
+tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size)
+{
+       *size = 0;
+       return (tty_keys_find1(tty->key_tree, buf, len, size));
+}
+
+/* Find the next node. */
+struct tty_key *
+tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size)
+{
+       /* If the node is NULL, this is the end of the tree. No match. */
+       if (tk == NULL)
+               return (NULL);
+
+       /* Pick the next in the sequence. */
+       if (tk->ch == *buf) {
+               /* Move forward in the string. */
+               buf++; len--;
+               (*size)++;
+
+               /* At the end of the string, return the current node. */
+               if (len == 0 || (tk->next == NULL && tk->key != KEYC_NONE))
+                       return (tk);
+
+               /* Move into the next tree for the following character. */
+               tk = tk->next;
+       } else {
+               if (*buf < tk->ch)
+                       tk = tk->left;
+               else if (*buf > tk->ch)
+                       tk = tk->right;
+       }
+
+       /* Move to the next in the tree. */
+       return (tty_keys_find1(tk, buf, len, size));
+}
+
+/*
+ * Process at least one key in the buffer and invoke tty->key_callback. Return
+ * 0 if there are no further keys, or 1 if there could be more in the buffer.
+ */
+int
+tty_keys_next(struct tty *tty)
+{
+       struct tty_key          *tk;
+       struct timeval           tv;
+       struct mouse_event       mouse;
+       const u_char            *buf;
+       size_t                   len, size;
+       cc_t                     bspace;
+       int                      key, delay;
+
+       buf = EVBUFFER_DATA(tty->event->input);
+       len = EVBUFFER_LENGTH(tty->event->input);
+       if (len == 0)
+               return (0);
+       log_debug("keys are %zu (%.*s)", len, (int) len, buf);
+
+       /* If a normal key, return it. */
+       if (*buf != '\033') {
+               key = *buf;
+               evbuffer_drain(tty->event->input, 1);
+
+               /*
+                * Check for backspace key using termios VERASE - the terminfo
+                * kbs entry is extremely unreliable, so cannot be safely
+                * used. termios should have a better idea.
+                */
+               bspace = tty->tio.c_cc[VERASE];
+               if (bspace != _POSIX_VDISABLE && key == bspace)
+                       key = KEYC_BSPACE;
+               goto handle_key;
+       }
+
+       /* Is this a mouse key press? */
+       switch (tty_keys_mouse(tty, (const char *)buf, len, &size, &mouse)) {
+       case 0:         /* yes */
+               evbuffer_drain(tty->event->input, size);
+               key = KEYC_MOUSE;
+               goto handle_key;
+       case -1:        /* no, or not valid */
+               break;
+       case 1:         /* partial */
+               goto partial_key;
+       }
+
+       /* Try to parse a key with an xterm-style modifier. */
+       switch (xterm_keys_find((const char *)buf, len, &size, &key)) {
+       case 0:         /* found */
+               evbuffer_drain(tty->event->input, size);
+               goto handle_key;
+       case -1:        /* not found */
+               break;
+       case 1:
+               goto partial_key;
+       }
+
+       /* Look for matching key string and return if found. */
+       tk = tty_keys_find(tty, (const char *)buf + 1, len - 1, &size);
+       if (tk != NULL) {
+               key = tk->key;
+               goto found_key;
+       }
+
+       /* Skip the escape. */
+       buf++;
+       len--;
+
+       /* Is there a normal key following? */
+       if (len != 0 && *buf != '\033') {
+               key = *buf | KEYC_ESCAPE;
+               evbuffer_drain(tty->event->input, 2);
+               goto handle_key;
+       }
+
+       /* Or a key string? */
+       if (len > 1) {
+               tk = tty_keys_find(tty, (const char *)buf + 1, len - 1, &size);
+               if (tk != NULL) {
+                       key = tk->key | KEYC_ESCAPE;
+                       size++; /* include escape */
+                       goto found_key;
+               }
+       }
+
+       /* Escape and then nothing useful - fall through. */
+
+partial_key:
+       /*
+        * Escape but no key string. If have already seen an escape, then the
+        * timer must have expired, so give up waiting and send the escape.
+        */
+       if (tty->flags & TTY_ESCAPE) {
+               evbuffer_drain(tty->event->input, 1);
+               key = '\033';
+               goto handle_key;
+       }
+
+       /* Fall through to start the timer. */
+
+start_timer:
+       /* Start the timer and wait for expiry or more data. */
+       delay = options_get_number(&global_options, "escape-time");
+       tv.tv_sec = delay / 1000;
+       tv.tv_usec = (delay % 1000) * 1000L;
+
+       evtimer_del(&tty->key_timer);
+       evtimer_set(&tty->key_timer, tty_keys_callback, tty);
+       evtimer_add(&tty->key_timer, &tv);
+
+       tty->flags |= TTY_ESCAPE;
+       return (0);
+
+found_key:
+       if (tk->next != NULL) {
+               /* Partial key. Start the timer if not already expired. */
+               if (!(tty->flags & TTY_ESCAPE))
+                       goto start_timer;
+
+               /* Otherwise, if no key, send the escape alone. */
+               if (tk->key == KEYC_NONE)
+                       goto partial_key;
+
+               /* Or fall through to send the partial key found. */
+       }
+       evbuffer_drain(tty->event->input, size + 1);
+
+       goto handle_key;
+
+handle_key:
+       evtimer_del(&tty->key_timer);
+
+       tty->key_callback(key, &mouse, tty->key_data);
+
+       tty->flags &= ~TTY_ESCAPE;
+       return (1);
+}
+
+/* Key timer callback. */
+/* ARGSUSED */
+void
+tty_keys_callback(unused int fd, unused short events, void *data)
+{
+       struct tty      *tty = data;
+
+       if (!(tty->flags & TTY_ESCAPE))
+               return;
+
+       while (tty_keys_next(tty))
+               ;
+}
+
+/*
+ * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial
+ * (probably a mouse sequence but need more data).
+ */
+int
+tty_keys_mouse(struct tty *tty,
+    const char *buf, size_t len, size_t *size, struct mouse_event *m)
+{
+       struct utf8_data        utf8data;
+       u_int                   i, value;
+
+       /*
+        * Standard mouse sequences are \033[M followed by three characters
+        * indicating buttons, X and Y, all based at 32 with 1,1 top-left.
+        *
+        * UTF-8 mouse sequences are similar but the three are expressed as
+        * UTF-8 characters.
+        */
+
+       *size = 0;
+
+       /* First three bytes are always \033[M. */
+       if (buf[0] != '\033')
+               return (-1);
+       if (len == 1)
+               return (1);
+       if (buf[1] != '[')
+               return (-1);
+       if (len == 2)
+               return (1);
+       if (buf[2] != 'M')
+               return (-1);
+       if (len == 3)
+               return (1);
+
+       /* Read the three inputs. */
+       *size = 3;
+       for (i = 0; i < 3; i++) {
+               if (len < *size)
+                       return (1);
+
+               if (tty->mode & MODE_MOUSE_UTF8) {
+                       if (utf8_open(&utf8data, buf[*size])) {
+                               if (utf8data.size != 2)
+                                       return (-1);
+                               (*size)++;
+                               if (len < *size)
+                                       return (1);
+                               utf8_append(&utf8data, buf[*size]);
+                               value = utf8_combine(&utf8data);
+                       } else
+                               value = (unsigned char)buf[*size];
+                       (*size)++;
+               } else {
+                       value = (unsigned char)buf[*size];
+                       (*size)++;
+               }
+
+               if (i == 0)
+                       m->b = value;
+               else if (i == 1)
+                       m->x = value;
+               else
+                       m->y = value;
+       }
+       log_debug("mouse input: %.*s", (int) *size, buf);
+
+       /* Check and return the mouse input. */
+       if (m->b < 32 || m->x < 33 || m->y < 33)
+               return (-1);
+       m->b -= 32;
+       m->x -= 33;
+       m->y -= 33;
+       log_debug("mouse position: x=%u y=%u b=%u", m->x, m->y, m->b);
+       return (0);
+}
diff --git a/external/bsd/tmux/dist/tty-term.c b/external/bsd/tmux/dist/tty-term.c
new file mode 100644 (file)
index 0000000..e5f789d
--- /dev/null
@@ -0,0 +1,535 @@
+/* $Id: tty-term.c,v 1.4 2011/08/17 19:28:36 jmmv 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>
+
+#ifdef HAVE_CURSES_H
+#include <curses.h>
+#else
+#include <ncurses.h>
+#endif
+#include <fnmatch.h>
+#include <stdlib.h>
+#include <string.h>
+#include <term.h>
+
+#include "tmux.h"
+
+void    tty_term_override(struct tty_term *, const char *);
+char   *tty_term_strip(const char *);
+
+struct tty_terms tty_terms = LIST_HEAD_INITIALIZER(tty_terms);
+
+const struct tty_term_code_entry tty_term_codes[NTTYCODE] = {
+       { TTYC_ACSC, TTYCODE_STRING, "acsc" },
+       { TTYC_AX, TTYCODE_FLAG, "AX" },
+       { TTYC_BEL, TTYCODE_STRING, "bel" },
+       { TTYC_BLINK, TTYCODE_STRING, "blink" },
+       { TTYC_BOLD, TTYCODE_STRING, "bold" },
+       { TTYC_CC, TTYCODE_STRING, "Cc" },
+       { TTYC_CIVIS, TTYCODE_STRING, "civis" },
+       { TTYC_CLEAR, TTYCODE_STRING, "clear" },
+       { TTYC_CNORM, TTYCODE_STRING, "cnorm" },
+       { TTYC_COLORS, TTYCODE_NUMBER, "colors" },
+       { TTYC_CR, TTYCODE_STRING, "Cr" },
+       { TTYC_CS1, TTYCODE_STRING, "Cs" },
+       { TTYC_CSR, TTYCODE_STRING, "csr" },
+       { TTYC_CSR1, TTYCODE_STRING, "Csr" },
+       { TTYC_CUB, TTYCODE_STRING, "cub" },
+       { TTYC_CUB1, TTYCODE_STRING, "cub1" },
+       { TTYC_CUD, TTYCODE_STRING, "cud" },
+       { TTYC_CUD1, TTYCODE_STRING, "cud1" },
+       { TTYC_CUF, TTYCODE_STRING, "cuf" },
+       { TTYC_CUF1, TTYCODE_STRING, "cuf1" },
+       { TTYC_CUP, TTYCODE_STRING, "cup" },
+       { TTYC_CUU, TTYCODE_STRING, "cuu" },
+       { TTYC_CUU1, TTYCODE_STRING, "cuu1" },
+       { 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_FSL, TTYCODE_STRING, "fsl" },
+       { TTYC_HOME, TTYCODE_STRING, "home" },
+       { TTYC_HPA, TTYCODE_STRING, "hpa" },
+       { 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_KDC2, TTYCODE_STRING, "kDC" },
+       { TTYC_KDC3, TTYCODE_STRING, "kDC3" },
+       { TTYC_KDC4, TTYCODE_STRING, "kDC4" },
+       { TTYC_KDC5, TTYCODE_STRING, "kDC5" },
+       { TTYC_KDC6, TTYCODE_STRING, "kDC6" },
+       { TTYC_KDC7, TTYCODE_STRING, "kDC7" },
+       { TTYC_KDCH1, TTYCODE_STRING, "kdch1" },
+       { TTYC_KDN2, TTYCODE_STRING, "kDN" },
+       { TTYC_KDN3, TTYCODE_STRING, "kDN3" },
+       { TTYC_KDN4, TTYCODE_STRING, "kDN4" },
+       { TTYC_KDN5, TTYCODE_STRING, "kDN5" },
+       { TTYC_KDN6, TTYCODE_STRING, "kDN6" },
+       { TTYC_KDN7, TTYCODE_STRING, "kDN7" },
+       { TTYC_KEND, TTYCODE_STRING, "kend" },
+       { TTYC_KEND2, TTYCODE_STRING, "kEND" },
+       { TTYC_KEND3, TTYCODE_STRING, "kEND3" },
+       { TTYC_KEND4, TTYCODE_STRING, "kEND4" },
+       { TTYC_KEND5, TTYCODE_STRING, "kEND5" },
+       { TTYC_KEND6, TTYCODE_STRING, "kEND6" },
+       { TTYC_KEND7, TTYCODE_STRING, "kEND7" },
+       { 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_KF2, TTYCODE_STRING, "kf2" },
+       { TTYC_KF20, TTYCODE_STRING, "kf20" },
+       { 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_KHOM2, TTYCODE_STRING, "kHOM" },
+       { TTYC_KHOM3, TTYCODE_STRING, "kHOM3" },
+       { TTYC_KHOM4, TTYCODE_STRING, "kHOM4" },
+       { TTYC_KHOM5, TTYCODE_STRING, "kHOM5" },
+       { TTYC_KHOM6, TTYCODE_STRING, "kHOM6" },
+       { TTYC_KHOM7, TTYCODE_STRING, "kHOM7" },
+       { TTYC_KHOME, TTYCODE_STRING, "khome" },
+       { TTYC_KIC2, TTYCODE_STRING, "kIC" },
+       { TTYC_KIC3, TTYCODE_STRING, "kIC3" },
+       { TTYC_KIC4, TTYCODE_STRING, "kIC4" },
+       { TTYC_KIC5, TTYCODE_STRING, "kIC5" },
+       { TTYC_KIC6, TTYCODE_STRING, "kIC6" },
+       { TTYC_KIC7, TTYCODE_STRING, "kIC7" },
+       { TTYC_KICH1, TTYCODE_STRING, "kich1" },
+       { TTYC_KLFT2, TTYCODE_STRING, "kLFT" },
+       { TTYC_KLFT3, TTYCODE_STRING, "kLFT3" },
+       { TTYC_KLFT4, TTYCODE_STRING, "kLFT4" },
+       { TTYC_KLFT5, TTYCODE_STRING, "kLFT5" },
+       { TTYC_KLFT6, TTYCODE_STRING, "kLFT6" },
+       { TTYC_KLFT7, TTYCODE_STRING, "kLFT7" },
+       { TTYC_KMOUS, TTYCODE_STRING, "kmous" },
+       { TTYC_KNP, TTYCODE_STRING, "knp" },
+       { TTYC_KNXT2, TTYCODE_STRING, "kNXT" },
+       { TTYC_KNXT3, TTYCODE_STRING, "kNXT3" },
+       { TTYC_KNXT4, TTYCODE_STRING, "kNXT4" },
+       { TTYC_KNXT5, TTYCODE_STRING, "kNXT5" },
+       { TTYC_KNXT6, TTYCODE_STRING, "kNXT6" },
+       { TTYC_KNXT7, TTYCODE_STRING, "kNXT7" },
+       { TTYC_KPP, TTYCODE_STRING, "kpp" },
+       { TTYC_KPRV2, TTYCODE_STRING, "kPRV" },
+       { TTYC_KPRV3, TTYCODE_STRING, "kPRV3" },
+       { TTYC_KPRV4, TTYCODE_STRING, "kPRV4" },
+       { TTYC_KPRV5, TTYCODE_STRING, "kPRV5" },
+       { TTYC_KPRV6, TTYCODE_STRING, "kPRV6" },
+       { TTYC_KPRV7, TTYCODE_STRING, "kPRV7" },
+       { TTYC_KRIT2, TTYCODE_STRING, "kRIT" },
+       { TTYC_KRIT3, TTYCODE_STRING, "kRIT3" },
+       { TTYC_KRIT4, TTYCODE_STRING, "kRIT4" },
+       { TTYC_KRIT5, TTYCODE_STRING, "kRIT5" },
+       { TTYC_KRIT6, TTYCODE_STRING, "kRIT6" },
+       { TTYC_KRIT7, TTYCODE_STRING, "kRIT7" },
+       { TTYC_KUP2, TTYCODE_STRING, "kUP" },
+       { TTYC_KUP3, TTYCODE_STRING, "kUP3" },
+       { TTYC_KUP4, TTYCODE_STRING, "kUP4" },
+       { TTYC_KUP5, TTYCODE_STRING, "kUP5" },
+       { TTYC_KUP6, TTYCODE_STRING, "kUP6" },
+       { TTYC_KUP7, TTYCODE_STRING, "kUP7" },
+       { TTYC_MS, TTYCODE_STRING, "Ms" },
+       { 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_SITM, TTYCODE_STRING, "sitm" },
+       { 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_TSL, TTYCODE_STRING, "tsl" },
+       { TTYC_VPA, TTYCODE_STRING, "vpa" },
+       { TTYC_XENL, TTYCODE_FLAG, "xenl" },
+       { TTYC_XT, TTYCODE_FLAG, "XT" },
+};
+
+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_override(struct tty_term *term, const char *overrides)
+{
+       const struct tty_term_code_entry        *ent;
+       struct tty_code                         *code;
+       char                                    *termnext, *termstr;
+       char                                    *entnext, *entstr;
+       char                                    *s, *ptr, *val;
+       const char                              *errstr;
+       u_int                                    i;
+       int                                      n, removeflag;
+
+       s = xstrdup(overrides);
+
+       termnext = s;
+       while ((termstr = strsep(&termnext, ",")) != NULL) {
+               entnext = termstr;
+
+               entstr = strsep(&entnext, ":");
+               if (entstr == NULL || entnext == NULL)
+                       continue;
+               if (fnmatch(entstr, term->name, 0) != 0)
+                       continue;
+               while ((entstr = strsep(&entnext, ":")) != NULL) {
+                       if (*entstr == '\0')
+                               continue;
+
+                       val = NULL;
+                       removeflag = 0;
+                       if ((ptr = strchr(entstr, '=')) != NULL) {
+                               *ptr++ = '\0';
+                               val = xstrdup(ptr);
+                               if (strunvis(val, ptr) == -1) {
+                                       xfree(val);
+                                       val = xstrdup(ptr);
+                               }
+                       } else if (entstr[strlen(entstr) - 1] == '@') {
+                               entstr[strlen(entstr) - 1] = '\0';
+                               removeflag = 1;
+                       } else
+                               val = xstrdup("");
+
+                       for (i = 0; i < NTTYCODE; i++) {
+                               ent = &tty_term_codes[i];
+                               if (strcmp(entstr, ent->name) != 0)
+                                       continue;
+                               code = &term->codes[ent->code];
+
+                               if (removeflag) {
+                                       code->type = TTYCODE_NONE;
+                                       continue;
+                               }
+                               switch (ent->type) {
+                               case TTYCODE_NONE:
+                                       break;
+                               case TTYCODE_STRING:
+                                       if (code->type == TTYCODE_STRING)
+                                               xfree(code->value.string);
+                                       code->value.string = xstrdup(val);
+                                       code->type = ent->type;
+                                       break;
+                               case TTYCODE_NUMBER:
+                                       n = strtonum(val, 0, INT_MAX, &errstr);
+                                       if (errstr != NULL)
+                                               break;
+                                       code->value.number = n;
+                                       code->type = ent->type;
+                                       break;
+                               case TTYCODE_FLAG:
+                                       code->value.flag = 1;
+                                       code->type = ent->type;
+                                       break;
+                               }
+                       }
+
+                       if (val != NULL)
+                               xfree(val);
+               }
+       }
+
+       xfree(s);
+}
+
+struct tty_term *
+tty_term_find(char *name, int fd, const char *overrides, char **cause)
+{
+       struct tty_term                         *term;
+       const struct tty_term_code_entry        *ent;
+       struct tty_code                         *code;
+       u_int                                    i;
+       int                                      n, error;
+       char                                    *s;
+       const char                              *acs;
+
+       LIST_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;
+       memset(term->codes, 0, sizeof term->codes);
+       LIST_INSERT_HEAD(&tty_terms, term, entry);
+
+       /* Set up curses terminal. */
+       if (setupterm(name, fd, &error) != OK) {
+               switch (error) {
+               case 1:
+                       xasprintf(
+                           cause, "can't use hardcopy terminal: %s", name);
+                       break;
+               case 0:
+                       xasprintf(
+                           cause, "missing or unsuitable terminal: %s", name);
+                       break;
+               case -1:
+                       xasprintf(cause, "can't find terminfo database");
+                       break;
+               default:
+                       xasprintf(cause, "unknown error");
+                       break;
+               }
+               goto error;
+       }
+
+       /* Fill in 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(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(ent->name);
+                       if (n == -1 || n == -2)
+                               break;
+                       code->type = TTYCODE_NUMBER;
+                       code->value.number = n;
+                       break;
+               case TTYCODE_FLAG:
+                       n = tigetflag(ent->name);
+                       if (n == -1)
+                               break;
+                       code->type = TTYCODE_FLAG;
+                       code->value.number = n;
+                       break;
+               }
+       }
+       tty_term_override(term, overrides);
+
+       /* Delete curses data. */
+#if !defined(__FreeBSD_version) || __FreeBSD_version >= 700000
+       del_curterm(cur_term);
+#endif
+
+       /* 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_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;
+       }
+
+       /* Figure out if we have 256 or 88 colours. */
+       if (tty_term_number(term, TTYC_COLORS) == 256)
+               term->flags |= TERM_256COLOURS;
+       if (tty_term_number(term, TTYC_COLORS) == 88)
+               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;
+
+       /* Generate ACS table. If none is present, use nearest ASCII. */
+       memset(term->acs, 0, sizeof term->acs);
+       if (tty_term_has(term, TTYC_ACSC))
+               acs = tty_term_string(term, TTYC_ACSC);
+       else
+               acs = "a#j+k+l+m+n+o-p-q-r-s-t+u+v+w+x|y<z>~.";
+       for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2)
+               term->acs[(u_char) acs[0]][0] = acs[1];
+
+       /* On terminals with xterm titles (XT), fill in tsl and fsl. */
+       if (tty_term_flag(term, TTYC_XT) &&
+           !tty_term_has(term, TTYC_TSL) &&
+           !tty_term_has(term, TTYC_FSL)) {
+               code = &term->codes[TTYC_TSL];
+               code->value.string = xstrdup("\033]0;");
+               code->type = TTYCODE_STRING;
+               code = &term->codes[TTYC_FSL];
+               code->value.string = xstrdup("\007");
+               code->type = TTYCODE_STRING;
+       }
+
+       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;
+
+       LIST_REMOVE(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 curses. */
+const char *
+tty_term_string1(struct tty_term *term, enum tty_code_code code, int a)
+{
+       return (tparm(tty_term_string(term, code), a, 0, 0, 0, 0, 0, 0, 0, 0));
+}
+
+const char *
+tty_term_string2(struct tty_term *term, enum tty_code_code code, int a, int b)
+{
+       return (tparm(tty_term_string(term, code), a, b, 0, 0, 0, 0, 0, 0, 0));
+}
+
+const char *
+tty_term_ptr1(struct tty_term *term, enum tty_code_code code, const void *a)
+{
+       return (tparm(tty_term_string(term, code), (long)a, 0, 0, 0, 0, 0, 0, 0, 0));
+}
+
+const char *
+tty_term_ptr2(struct tty_term *term, enum tty_code_code code, const void *a, const void *b)
+{
+       return (tparm(tty_term_string(term, code), (long)a, (long)b, 0, 0, 0, 0, 0, 0, 0));
+}
+
+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/external/bsd/tmux/dist/tty.c b/external/bsd/tmux/dist/tty.c
new file mode 100644 (file)
index 0000000..885daaf
--- /dev/null
@@ -0,0 +1,1548 @@
+/* $Id: tty.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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 <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+void   tty_read_callback(struct bufferevent *, void *);
+void   tty_error_callback(struct bufferevent *, short, void *);
+
+int    tty_try_256(struct tty *, u_char, const char *);
+int    tty_try_88(struct tty *, u_char, const char *);
+
+void   tty_colours(struct tty *, const struct grid_cell *);
+void   tty_check_fg(struct tty *, struct grid_cell *);
+void   tty_check_bg(struct tty *, struct grid_cell *);
+void   tty_colours_fg(struct tty *, const struct grid_cell *);
+void   tty_colours_bg(struct tty *, const struct grid_cell *);
+
+void   tty_redraw_region(struct tty *, const struct tty_ctx *);
+void   tty_emulate_repeat(
+           struct tty *, enum tty_code_code, enum tty_code_code, u_int);
+void   tty_cell(struct tty *,
+           const struct grid_cell *, const struct grid_utf8 *);
+
+#define tty_use_acs(tty) \
+       (tty_term_has(tty->term, TTYC_ACSC) && !((tty)->flags & TTY_UTF8))
+
+void
+tty_init(struct tty *tty, int fd, char *term)
+{
+       char    *path;
+
+       memset(tty, 0, sizeof *tty);
+       tty->log_fd = -1;
+
+       if (term == NULL || *term == '\0')
+               tty->termname = xstrdup("unknown");
+       else
+               tty->termname = xstrdup(term);
+       tty->fd = fd;
+
+       if ((path = ttyname(fd)) == NULL)
+               fatalx("ttyname failed");
+       tty->path = xstrdup(path);
+       tty->cstyle = 0;
+       tty->ccolour = xstrdup("");
+
+       tty->flags = 0;
+       tty->term_flags = 0;
+}
+
+int
+tty_resize(struct tty *tty)
+{
+       struct winsize  ws;
+       u_int           sx, sy;
+
+       if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) {
+               sx = ws.ws_col;
+               if (sx == 0)
+                       sx = 80;
+               sy = ws.ws_row;
+               if (sy == 0)
+                       sy = 24;
+       } else {
+               sx = 80;
+               sy = 24;
+       }
+       if (sx == tty->sx && sy == tty->sy)
+               return (0);
+       tty->sx = sx;
+       tty->sy = sy;
+
+       tty->cx = UINT_MAX;
+       tty->cy = UINT_MAX;
+
+       tty->rupper = UINT_MAX;
+       tty->rlower = UINT_MAX;
+
+       /*
+        * If the terminal has been started, reset the actual scroll region and
+        * cursor position, as this may not have happened.
+        */
+       if (tty->flags & TTY_STARTED) {
+               tty_cursor(tty, 0, 0);
+               tty_region(tty, 0, tty->sy - 1);
+       }
+
+       return (1);
+}
+
+int
+tty_open(struct tty *tty, const char *overrides, char **cause)
+{
+       char    out[64];
+       int     fd;
+
+       if (debug_level > 3) {
+               xsnprintf(out, sizeof out, "tmux-out-%ld.log", (long) getpid());
+               fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+               if (fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
+                       fatal("fcntl failed");
+               tty->log_fd = fd;
+       }
+
+       tty->term = tty_term_find(tty->termname, tty->fd, overrides, cause);
+       if (tty->term == NULL) {
+               tty_close(tty);
+               return (-1);
+       }
+       tty->flags |= TTY_OPENED;
+
+       tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_ESCAPE);
+
+       tty->event = bufferevent_new(
+           tty->fd, tty_read_callback, NULL, tty_error_callback, tty);
+
+       tty_start_tty(tty);
+
+       tty_keys_init(tty);
+
+       return (0);
+}
+
+/* ARGSUSED */
+void
+tty_read_callback(unused struct bufferevent *bufev, void *data)
+{
+       struct tty      *tty = data;
+
+       while (tty_keys_next(tty))
+               ;
+}
+
+/* ARGSUSED */
+void
+tty_error_callback(
+    unused struct bufferevent *bufev, unused short what, unused void *data)
+{
+}
+
+void
+tty_start_tty(struct tty *tty)
+{
+       struct termios   tio;
+
+       if (tty->fd == -1 || tcgetattr(tty->fd, &tty->tio) != 0)
+               return;
+
+       setblocking(tty->fd, 0);
+
+       bufferevent_enable(tty->event, EV_READ|EV_WRITE);
+
+       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)
+               tcflush(tty->fd, TCIOFLUSH);
+
+       tty_putcode(tty, TTYC_SMCUP);
+
+       tty_putcode(tty, TTYC_SGR0);
+       memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell);
+
+       tty_putcode(tty, TTYC_RMKX);
+       if (tty_use_acs(tty))
+               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");
+
+       tty->cx = UINT_MAX;
+       tty->cy = UINT_MAX;
+
+       tty->rlower = UINT_MAX;
+       tty->rupper = UINT_MAX;
+
+       tty->mode = MODE_CURSOR;
+
+       tty->flags |= TTY_STARTED;
+
+       tty_force_cursor_colour(tty, "");
+}
+
+void
+tty_stop_tty(struct tty *tty)
+{
+       struct winsize  ws;
+
+       if (!(tty->flags & TTY_STARTED))
+               return;
+       tty->flags &= ~TTY_STARTED;
+
+       bufferevent_disable(tty->event, EV_READ|EV_WRITE);
+
+       /*
+        * 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;
+
+       setblocking(tty->fd, 1);
+
+       tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
+       if (tty_use_acs(tty))
+               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_CLEAR));
+       if (tty_term_has(tty->term, TTYC_CS1) && tty->cstyle != 0) {
+               if (tty_term_has(tty->term, TTYC_CSR1))
+                       tty_raw(tty, tty_term_string(tty->term, TTYC_CSR1));
+               else
+                       tty_raw(tty, tty_term_string1(tty->term, TTYC_CS1, 0));
+       }
+       tty_raw(tty, tty_term_string(tty->term, TTYC_CR));
+
+       tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
+       if (tty_term_has(tty->term, TTYC_KMOUS))
+               tty_raw(tty, "\033[?1000l");
+
+       tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
+}
+
+void
+tty_close(struct tty *tty)
+{
+       if (tty->log_fd != -1) {
+               close(tty->log_fd);
+               tty->log_fd = -1;
+       }
+
+       evtimer_del(&tty->key_timer);
+       tty_stop_tty(tty);
+
+       if (tty->flags & TTY_OPENED) {
+               bufferevent_free(tty->event);
+
+               tty_term_free(tty->term);
+               tty_keys_free(tty);
+
+               tty->flags &= ~TTY_OPENED;
+       }
+
+       if (tty->fd != -1) {
+               close(tty->fd);
+               tty->fd = -1;
+       }
+}
+
+void
+tty_free(struct tty *tty)
+{
+       tty_close(tty);
+
+       xfree(tty->ccolour);
+       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_putcode_ptr1(struct tty *tty, enum tty_code_code code, const void *a)
+{
+       if (a != NULL)
+               tty_puts(tty, tty_term_ptr1(tty->term, code, a));
+}
+
+void
+tty_putcode_ptr2(struct tty *tty, enum tty_code_code code, const void *a, const void *b)
+{
+       if (a != NULL && b != NULL)
+               tty_puts(tty, tty_term_ptr2(tty->term, code, a, b));
+}
+
+void
+tty_puts(struct tty *tty, const char *s)
+{
+       if (*s == '\0')
+               return;
+       bufferevent_write(tty->event, s, strlen(s));
+
+       if (tty->log_fd != -1)
+               write(tty->log_fd, s, strlen(s));
+}
+
+void
+tty_putc(struct tty *tty, u_char ch)
+{
+       const char      *acs;
+       u_int            sx;
+
+       if (tty->cell.attr & GRID_ATTR_CHARSET) {
+               acs = tty_acs_get(tty, ch);
+               if (acs != NULL)
+                       bufferevent_write(tty->event, acs, strlen(acs));
+               else
+                       bufferevent_write(tty->event, &ch, 1);
+       } else
+               bufferevent_write(tty->event, &ch, 1);
+
+       if (ch >= 0x20 && ch != 0x7f) {
+               sx = tty->sx;
+               if (tty->term->flags & TERM_EARLYWRAP)
+                       sx--;
+
+               if (tty->cx >= sx) {
+                       tty->cx = 1;
+                       if (tty->cy != tty->rlower)
+                               tty->cy++;
+               } else
+                       tty->cx++;
+       }
+
+       if (tty->log_fd != -1)
+               write(tty->log_fd, &ch, 1);
+}
+
+void
+tty_pututf8(struct tty *tty, const struct grid_utf8 *gu)
+{
+       size_t  size;
+
+       size = grid_utf8_size(gu);
+       bufferevent_write(tty->event, gu->data, size);
+       if (tty->log_fd != -1)
+               write(tty->log_fd, gu->data, size);
+       tty->cx += gu->width;
+}
+
+void
+tty_set_title(struct tty *tty, const char *title)
+{
+       if (!tty_term_has(tty->term, TTYC_TSL) ||
+           !tty_term_has(tty->term, TTYC_FSL))
+               return;
+
+       tty_putcode(tty, TTYC_TSL);
+       tty_puts(tty, title);
+       tty_putcode(tty, TTYC_FSL);
+}
+
+void
+tty_force_cursor_colour(struct tty *tty, const char *ccolour)
+{
+       if (*ccolour == '\0')
+               tty_putcode(tty, TTYC_CR);
+       else
+               tty_putcode_ptr1(tty, TTYC_CC, ccolour);
+       xfree(tty->ccolour);
+       tty->ccolour = xstrdup(ccolour);
+}
+
+void
+tty_update_mode(struct tty *tty, int mode, struct screen *s)
+{
+       int     changed;
+
+       if (strcmp(s->ccolour, tty->ccolour))
+               tty_force_cursor_colour(tty, s->ccolour);
+
+       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 (tty->cstyle != s->cstyle) {
+               if (tty_term_has(tty->term, TTYC_CS1)) {
+                       if (s->cstyle == 0 &&
+                           tty_term_has(tty->term, TTYC_CSR1))
+                               tty_putcode(tty, TTYC_CSR1);
+                       else
+                               tty_putcode1(tty, TTYC_CS1, s->cstyle);
+               }
+               tty->cstyle = s->cstyle;
+       }
+       if (changed & ALL_MOUSE_MODES) {
+               if (mode & ALL_MOUSE_MODES) {
+                       if (mode & MODE_MOUSE_UTF8)
+                               tty_puts(tty, "\033[?1005h");
+                       if (mode & MODE_MOUSE_ANY)
+                               tty_puts(tty, "\033[?1003h");
+                       else if (mode & MODE_MOUSE_BUTTON)
+                               tty_puts(tty, "\033[?1002h");
+                       else if (mode & MODE_MOUSE_STANDARD)
+                               tty_puts(tty, "\033[?1000h");
+               } else {
+                       if (tty->mode & MODE_MOUSE_ANY)
+                               tty_puts(tty, "\033[?1003l");
+                       else if (tty->mode & MODE_MOUSE_BUTTON)
+                               tty_puts(tty, "\033[?1002l");
+                       else if (tty->mode & MODE_MOUSE_STANDARD)
+                               tty_puts(tty, "\033[?1000l");
+                       if (tty->mode & MODE_MOUSE_UTF8)
+                               tty_puts(tty, "\033[?1005l");
+               }
+       }
+       if (changed & MODE_KKEYPAD) {
+               if (mode & MODE_KKEYPAD)
+                       tty_putcode(tty, TTYC_SMKX);
+               else
+                       tty_putcode(tty, TTYC_RMKX);
+       }
+       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, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->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 (ctx->orlower - ctx->orupper >= screen_size_y(s) / 2) {
+               wp->flags |= PANE_REDRAW;
+               return;
+       }
+
+       if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) {
+               for (i = ctx->ocy; i < screen_size_y(s); i++)
+                       tty_draw_line(tty, s, i, wp->xoff, wp->yoff);
+       } else {
+               for (i = ctx->orupper; i <= ctx->orlower; 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;
+       struct grid_line        *gl;
+       struct grid_cell         tmpgc;
+       const struct grid_utf8  *gu;
+       u_int                    i, sx;
+
+       tty_update_mode(tty, tty->mode & ~MODE_CURSOR, s);
+
+       sx = screen_size_x(s);
+       if (sx > s->grid->linedata[s->grid->hsize + py].cellsize)
+               sx = s->grid->linedata[s->grid->hsize + py].cellsize;
+       if (sx > tty->sx)
+               sx = tty->sx;
+
+       /*
+        * Don't move the cursor to the start permission if it will wrap there
+        * itself.
+        */
+       gl = NULL;
+       if (py != 0)
+               gl = &s->grid->linedata[s->grid->hsize + py - 1];
+       if (oy + py == 0 || gl == NULL || !(gl->flags & GRID_LINE_WRAPPED) ||
+           tty->cx < tty->sx || ox != 0 ||
+           (oy + py != tty->cy + 1 && tty->cy != s->rlower + oy))
+               tty_cursor(tty, ox, oy + py);
+
+       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)) {
+                       memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc);
+                       tmpgc.data = gc->data;
+                       tmpgc.flags = gc->flags &
+                           ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
+                       tmpgc.flags |= s->sel.cell.flags &
+                           (GRID_FLAG_FG256|GRID_FLAG_BG256);
+                       tty_cell(tty, &tmpgc, gu);
+               } else
+                       tty_cell(tty, gc, gu);
+       }
+
+       if (sx >= tty->sx) {
+               tty_update_mode(tty, tty->mode, s);
+               return;
+       }
+       tty_reset(tty);
+
+       tty_cursor(tty, ox + sx, oy + py);
+       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, ' ');
+       }
+       tty_update_mode(tty, tty->mode, s);
+}
+
+void
+tty_write(void (*cmdfn)(
+    struct tty *, const struct tty_ctx *), const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       struct client           *c;
+       u_int                    i;
+
+       /* wp can be NULL if updating the screen but not the terminal. */
+       if (wp == NULL)
+               return;
+
+       if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW)
+               return;
+       if (!window_pane_visible(wp))
+               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) {
+                       if (c->tty.term == NULL)
+                               continue;
+                       if (c->tty.flags & (TTY_FREEZE|TTY_BACKOFF))
+                               continue;
+                       cmdfn(&c->tty, ctx);
+               }
+       }
+}
+
+void
+tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       struct screen           *s = wp->screen;
+       u_int                    i;
+
+       if (wp->xoff != 0 || screen_size_x(s) < tty->sx) {
+               tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
+               return;
+       }
+
+       tty_reset(tty);
+
+       tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
+
+       if (tty_term_has(tty->term, TTYC_ICH) ||
+           tty_term_has(tty->term, TTYC_ICH1))
+               tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num);
+       else if (tty_term_has(tty->term, TTYC_SMIR) &&
+           tty_term_has(tty->term, TTYC_RMIR)) {
+               tty_putcode(tty, TTYC_SMIR);
+               for (i = 0; i < ctx->num; i++)
+                       tty_putc(tty, ' ');
+               tty_putcode(tty, TTYC_RMIR);
+       } else
+               tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
+}
+
+void
+tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       struct screen           *s = wp->screen;
+
+       if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
+           (!tty_term_has(tty->term, TTYC_DCH) &&
+           !tty_term_has(tty->term, TTYC_DCH1))) {
+               tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
+               return;
+       }
+
+       tty_reset(tty);
+
+       tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
+
+       if (tty_term_has(tty->term, TTYC_DCH) ||
+           tty_term_has(tty->term, TTYC_DCH1))
+               tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num);
+}
+
+void
+tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       struct screen           *s = wp->screen;
+
+       if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
+           !tty_term_has(tty->term, TTYC_CSR) ||
+           !tty_term_has(tty->term, TTYC_IL1)) {
+               tty_redraw_region(tty, ctx);
+               return;
+       }
+
+       tty_reset(tty);
+
+       tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
+       tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
+
+       tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num);
+}
+
+void
+tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       struct screen           *s = wp->screen;
+
+       if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
+           !tty_term_has(tty->term, TTYC_CSR) ||
+           !tty_term_has(tty->term, TTYC_DL1)) {
+               tty_redraw_region(tty, ctx);
+               return;
+       }
+
+       tty_reset(tty);
+
+       tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
+       tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
+
+       tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num);
+}
+
+void
+tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       struct screen           *s = wp->screen;
+       u_int                    i;
+
+       tty_reset(tty);
+
+       tty_cursor_pane(tty, ctx, 0, ctx->ocy);
+
+       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, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       struct screen           *s = wp->screen;
+       u_int                    i;
+
+       tty_reset(tty);
+
+       tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
+
+       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 = ctx->ocx; i < screen_size_x(s); i++)
+                       tty_putc(tty, ' ');
+       }
+}
+
+void
+tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       u_int                    i;
+
+       tty_reset(tty);
+
+       if (wp->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) {
+               tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
+               tty_putcode(tty, TTYC_EL1);
+       } else {
+               tty_cursor_pane(tty, ctx, 0, ctx->ocy);
+               for (i = 0; i < ctx->ocx + 1; i++)
+                       tty_putc(tty, ' ');
+       }
+}
+
+void
+tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       struct screen           *s = wp->screen;
+
+       if (ctx->ocy != ctx->orupper)
+               return;
+
+       if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
+           !tty_term_has(tty->term, TTYC_CSR) ||
+           !tty_term_has(tty->term, TTYC_RI)) {
+               tty_redraw_region(tty, ctx);
+               return;
+       }
+
+       tty_reset(tty);
+
+       tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
+       tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
+
+       tty_putcode(tty, TTYC_RI);
+}
+
+void
+tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       struct screen           *s = wp->screen;
+
+       if (ctx->ocy != ctx->orlower)
+               return;
+
+       if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
+           !tty_term_has(tty->term, TTYC_CSR)) {
+               tty_redraw_region(tty, ctx);
+               return;
+       }
+
+       /*
+        * If this line wrapped naturally (ctx->num is nonzero), don't do
+        * anything - the cursor can just be moved to the last cell and wrap
+        * naturally.
+        */
+       if (ctx->num && !(tty->term->flags & TERM_EARLYWRAP))
+               return;
+
+       tty_reset(tty);
+
+       tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
+       tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
+
+       tty_putc(tty, '\n');
+}
+
+void
+tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       struct screen           *s = wp->screen;
+       u_int                    i, j;
+
+       tty_reset(tty);
+
+       tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
+       tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
+
+       if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
+           tty_term_has(tty->term, TTYC_EL)) {
+               tty_putcode(tty, TTYC_EL);
+               if (ctx->ocy != screen_size_y(s) - 1) {
+                       tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
+                       for (i = ctx->ocy + 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 = ctx->ocx; i < screen_size_x(s); i++)
+                       tty_putc(tty, ' ');
+               for (j = ctx->ocy + 1; j < screen_size_y(s); j++) {
+                       tty_cursor_pane(tty, ctx, 0, j);
+                       for (i = 0; i < screen_size_x(s); i++)
+                               tty_putc(tty, ' ');
+               }
+       }
+}
+
+void
+tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       struct screen           *s = wp->screen;
+       u_int                    i, j;
+
+       tty_reset(tty);
+
+       tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
+       tty_cursor_pane(tty, ctx, 0, 0);
+
+       if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
+           tty_term_has(tty->term, TTYC_EL)) {
+               for (i = 0; i < ctx->ocy; i++) {
+                       tty_putcode(tty, TTYC_EL);
+                       tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
+                       tty->cy++;
+               }
+       } else {
+               for (j = 0; j < ctx->ocy; j++) {
+                       tty_cursor_pane(tty, ctx, 0, j);
+                       for (i = 0; i < screen_size_x(s); i++)
+                               tty_putc(tty, ' ');
+               }
+       }
+       for (i = 0; i <= ctx->ocx; i++)
+               tty_putc(tty, ' ');
+}
+
+void
+tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       struct screen           *s = wp->screen;
+       u_int                    i, j;
+
+       tty_reset(tty);
+
+       tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
+       tty_cursor_pane(tty, ctx, 0, 0);
+
+       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_pane(tty, ctx, 0, j);
+                       for (i = 0; i < screen_size_x(s); i++)
+                               tty_putc(tty, ' ');
+               }
+       }
+}
+
+void
+tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       struct screen           *s = wp->screen;
+       u_int                    i, j;
+
+       tty_reset(tty);
+
+       tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
+
+       for (j = 0; j < screen_size_y(s); j++) {
+               tty_cursor_pane(tty, ctx, 0, j);
+               for (i = 0; i < screen_size_x(s); i++)
+                       tty_putc(tty, 'E');
+       }
+}
+
+void
+tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+       struct screen           *s = wp->screen;
+       u_int                    cx;
+       u_int                    width;
+       const struct grid_cell  *gc = ctx->cell;
+       const struct grid_utf8  *gu = ctx->utf8;
+
+       if (gc->flags & GRID_FLAG_UTF8)
+               width = gu->width;
+       else
+               width = 1;
+
+       tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
+
+       /* Is the cursor in the very last position? */
+       if (ctx->ocx > wp->sx - width) {
+               if (wp->xoff != 0 || wp->sx != tty->sx) {
+                       /*
+                        * The pane doesn't fill the entire line, the linefeed
+                        * will already have happened, so just move the cursor.
+                        */
+                       tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
+               } else if (tty->cx < tty->sx) {
+                       /*
+                        * The cursor isn't in the last position already, so
+                        * move as far left as possible and redraw the last
+                        * cell to move into the last position.
+                        */
+                       cx = screen_size_x(s) - width;
+                       tty_cursor_pane(tty, ctx, cx, ctx->ocy);
+                       tty_cell(tty, &ctx->last_cell, &ctx->last_utf8);
+               }
+       } else
+               tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
+
+       tty_cell(tty, ctx->cell, ctx->utf8);
+}
+
+void
+tty_cmd_utf8character(struct tty *tty, const struct tty_ctx *ctx)
+{
+       struct window_pane      *wp = ctx->wp;
+
+       /*
+        * Cannot rely on not being a partial character, so just redraw the
+        * whole line.
+        */
+       tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
+}
+
+void
+tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx)
+{
+       char    *buf;
+       size_t   off;
+
+       if (!tty_term_has(tty->term, TTYC_MS))
+               return;
+
+       off = 4 * ((ctx->num + 2) / 3) + 1; /* storage for base64 */
+       buf = xmalloc(off);
+
+       b64_ntop(ctx->ptr, ctx->num, buf, off);
+       tty_putcode_ptr2(tty, TTYC_MS, "", buf);
+
+       xfree(buf);
+}
+
+void
+tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
+{
+       u_int    i;
+       u_char  *str = ctx->ptr;
+
+       for (i = 0; i < ctx->num; i++)
+               tty_putc(tty, str[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. */
+       tty_pututf8(tty, gu);
+}
+
+void
+tty_reset(struct tty *tty)
+{
+       struct grid_cell        *gc = &tty->cell;
+
+       if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0)
+               return;
+
+       if ((gc->attr & GRID_ATTR_CHARSET) && tty_use_acs(tty))
+               tty_putcode(tty, TTYC_RMACS);
+       tty_putcode(tty, TTYC_SGR0);
+       memcpy(gc, &grid_default_cell, sizeof *gc);
+}
+
+/* Set region inside pane. */
+void
+tty_region_pane(
+    struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower)
+{
+       struct window_pane      *wp = ctx->wp;
+
+       tty_region(tty, wp->yoff + rupper, wp->yoff + rlower);
+}
+
+/* Set region at absolute position. */
+void
+tty_region(struct tty *tty, u_int rupper, u_int rlower)
+{
+       if (tty->rlower == rlower && tty->rupper == rupper)
+               return;
+       if (!tty_term_has(tty->term, TTYC_CSR))
+               return;
+
+       tty->rupper = rupper;
+       tty->rlower = rlower;
+
+       /*
+        * Some terminals (such as PuTTY) do not correctly reset the cursor to
+        * 0,0 if it is beyond the last column (they do not reset their wrap
+        * flag so further output causes a line feed). As a workaround, do an
+        * explicit move to 0 first.
+        */
+       if (tty->cx >= tty->sx)
+               tty_cursor(tty, 0, tty->cy);
+
+       tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower);
+       tty_cursor(tty, 0, 0);
+}
+
+/* Move cursor inside pane. */
+void
+tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy)
+{
+       struct window_pane      *wp = ctx->wp;
+
+       tty_cursor(tty, wp->xoff + cx, wp->yoff + cy);
+}
+
+/* Move cursor to absolute position. */
+void
+tty_cursor(struct tty *tty, u_int cx, u_int cy)
+{
+       struct tty_term *term = tty->term;
+       u_int            thisx, thisy;
+       int              change;
+
+       if (cx > tty->sx - 1)
+               cx = tty->sx - 1;
+
+       thisx = tty->cx;
+       thisy = tty->cy;
+
+       /* No change. */
+       if (cx == thisx && cy == thisy)
+               return;
+
+       /* Very end of the line, just use absolute movement. */
+       if (thisx > tty->sx - 1)
+               goto absolute;
+
+       /* Move to home position (0, 0). */
+       if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) {
+               tty_putcode(tty, TTYC_HOME);
+               goto out;
+       }
+
+       /* Zero on the next line. */
+       if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower) {
+               tty_putc(tty, '\r');
+               tty_putc(tty, '\n');
+               goto out;
+       }
+
+       /* Moving column or row. */
+       if (cy == thisy) {
+               /*
+                * Moving column only, row staying the same.
+                */
+
+               /* To left edge. */
+               if (cx == 0)    {
+                       tty_putc(tty, '\r');
+                       goto out;
+               }
+
+               /* One to the left. */
+               if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) {
+                       tty_putcode(tty, TTYC_CUB1);
+                       goto out;
+               }
+
+               /* One to the right. */
+               if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) {
+                       tty_putcode(tty, TTYC_CUF1);
+                       goto out;
+               }
+
+               /* Calculate difference. */
+               change = thisx - cx;    /* +ve left, -ve right */
+
+               /*
+                * Use HPA if change is larger than absolute, otherwise move
+                * the cursor with CUB/CUF.
+                */
+               if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) {
+                       tty_putcode1(tty, TTYC_HPA, cx);
+                       goto out;
+               } else if (change > 0 && tty_term_has(term, TTYC_CUB)) {
+                       tty_putcode1(tty, TTYC_CUB, change);
+                       goto out;
+               } else if (change < 0 && tty_term_has(term, TTYC_CUF)) {
+                       tty_putcode1(tty, TTYC_CUF, -change);
+                       goto out;
+               }
+       } else if (cx == thisx) {
+               /*
+                * Moving row only, column staying the same.
+                */
+
+               /* One above. */
+               if (thisy != tty->rupper &&
+                   cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) {
+                       tty_putcode(tty, TTYC_CUU1);
+                       goto out;
+               }
+
+               /* One below. */
+               if (thisy != tty->rlower &&
+                   cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) {
+                       tty_putcode(tty, TTYC_CUD1);
+                       goto out;
+               }
+
+               /* Calculate difference. */
+               change = thisy - cy;    /* +ve up, -ve down */
+
+               /*
+                * Try to use VPA if change is larger than absolute or if this
+                * change would cross the scroll region, otherwise use CUU/CUD.
+                */
+               if ((u_int) abs(change) > cy ||
+                   (change < 0 && cy - change > tty->rlower) ||
+                   (change > 0 && cy - change < tty->rupper)) {
+                           if (tty_term_has(term, TTYC_VPA)) {
+                                   tty_putcode1(tty, TTYC_VPA, cy);
+                                   goto out;
+                           }
+               } else if (change > 0 && tty_term_has(term, TTYC_CUU)) {
+                       tty_putcode1(tty, TTYC_CUU, change);
+                       goto out;
+               } else if (change < 0 && tty_term_has(term, TTYC_CUD)) {
+                       tty_putcode1(tty, TTYC_CUD, -change);
+                       goto out;
+               }
+       }
+
+absolute:
+       /* Absolute movement. */
+       tty_putcode2(tty, TTYC_CUP, cy, cx);
+
+out:
+       tty->cx = cx;
+       tty->cy = cy;
+}
+
+void
+tty_attributes(struct tty *tty, const struct grid_cell *gc)
+{
+       struct grid_cell        *tc = &tty->cell, gc2;
+       u_char                   changed;
+
+       memcpy(&gc2, gc, sizeof gc2);
+
+       /*
+        * If no setab, try to use the reverse attribute as a best-effort for a
+        * non-default background. This is a bit of a hack but it doesn't do
+        * any serious harm and makes a couple of applications happier.
+        */
+       if (!tty_term_has(tty->term, TTYC_SETAB)) {
+               if (gc2.attr & GRID_ATTR_REVERSE) {
+                       if (gc2.fg != 7 && gc2.fg != 8)
+                               gc2.attr &= ~GRID_ATTR_REVERSE;
+               } else {
+                       if (gc2.bg != 0 && gc2.bg != 8)
+                               gc2.attr |= GRID_ATTR_REVERSE;
+               }
+       }
+
+       /* Fix up the colours if necessary. */
+       tty_check_fg(tty, &gc2);
+       tty_check_bg(tty, &gc2);
+
+       /* If any bits are being cleared, reset everything. */
+       if (tc->attr & ~gc2.attr)
+               tty_reset(tty);
+
+       /*
+        * Set the colours. This may call tty_reset() (so it comes next) and
+        * may add to (NOT remove) the desired attributes by changing new_attr.
+        */
+       tty_colours(tty, &gc2);
+
+       /* Filter out attribute bits already set. */
+       changed = gc2.attr & ~tc->attr;
+       tc->attr = gc2.attr;
+
+       /* Set the attributes. */
+       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)
+       {
+               if (tty_term_has(tty->term, TTYC_SITM))
+                       tty_putcode(tty, TTYC_SITM);
+               else
+                       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_use_acs(tty))
+               tty_putcode(tty, TTYC_SMACS);
+}
+
+void
+tty_colours(struct tty *tty, const struct grid_cell *gc)
+{
+       struct grid_cell        *tc = &tty->cell;
+       u_char                   fg = gc->fg, bg = gc->bg, flags = gc->flags;
+       int                      have_ax, fg_default, bg_default;
+
+       /* No changes? Nothing is necessary. */
+       if (fg == tc->fg && bg == tc->bg &&
+           ((flags ^ tc->flags) & (GRID_FLAG_FG256|GRID_FLAG_BG256)) == 0)
+               return;
+
+       /*
+        * Is either the default colour? This is handled specially because the
+        * best solution might be to reset both colours to default, in which
+        * case if only one is default need to fall onward to set the other
+        * colour.
+        */
+       fg_default = (fg == 8 && !(flags & GRID_FLAG_FG256));
+       bg_default = (bg == 8 && !(flags & GRID_FLAG_BG256));
+       if (fg_default || bg_default) {
+               /*
+                * If don't have AX but do have op, send sgr0 (op can't
+                * actually be used because it is sometimes the same as sgr0
+                * and sometimes isn't). This resets both colours to default.
+                *
+                * Otherwise, try to set the default colour only as needed.
+                */
+               have_ax = tty_term_has(tty->term, TTYC_AX);
+               if (!have_ax && tty_term_has(tty->term, TTYC_OP))
+                       tty_reset(tty);
+               else {
+                       if (fg_default &&
+                           (tc->fg != 8 || tc->flags & GRID_FLAG_FG256)) {
+                               if (have_ax)
+                                       tty_puts(tty, "\033[39m");
+                               else if (tc->fg != 7 ||
+                                   tc->flags & GRID_FLAG_FG256)
+                                       tty_putcode1(tty, TTYC_SETAF, 7);
+                               tc->fg = 8;
+                               tc->flags &= ~GRID_FLAG_FG256;
+                       }
+                       if (bg_default &&
+                           (tc->bg != 8 || tc->flags & GRID_FLAG_BG256)) {
+                               if (have_ax)
+                                       tty_puts(tty, "\033[49m");
+                               else if (tc->bg != 0 ||
+                                   tc->flags & GRID_FLAG_BG256)
+                                       tty_putcode1(tty, TTYC_SETAB, 0);
+                               tc->bg = 8;
+                               tc->flags &= ~GRID_FLAG_BG256;
+                       }
+               }
+       }
+
+       /* Set the foreground colour. */
+       if (!fg_default && (fg != tc->fg ||
+           ((flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256))))
+               tty_colours_fg(tty, gc);
+
+       /*
+        * Set the background colour. This must come after the foreground as
+        * tty_colour_fg() can call tty_reset().
+        */
+       if (!bg_default && (bg != tc->bg ||
+           ((flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256))))
+               tty_colours_bg(tty, gc);
+}
+
+void
+tty_check_fg(struct tty *tty, struct grid_cell *gc)
+{
+       u_int   colours;
+
+       /* Is this a 256-colour colour? */
+       if (gc->flags & GRID_FLAG_FG256) {
+               /* And not a 256 colour mode? */
+               if (!(tty->term->flags & TERM_88COLOURS) &&
+                   !(tty->term_flags & TERM_88COLOURS) &&
+                   !(tty->term->flags & TERM_256COLOURS) &&
+                   !(tty->term_flags & TERM_256COLOURS)) {
+                       gc->fg = colour_256to16(gc->fg);
+                       if (gc->fg & 8) {
+                               gc->fg &= 7;
+                               gc->attr |= GRID_ATTR_BRIGHT;
+                       } else
+                               gc->attr &= ~GRID_ATTR_BRIGHT;
+                       gc->flags &= ~GRID_FLAG_FG256;
+               }
+               return;
+       }
+
+       /* Is this an aixterm colour? */
+       colours = tty_term_number(tty->term, TTYC_COLORS);
+       if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) {
+               gc->fg -= 90;
+               gc->attr |= GRID_ATTR_BRIGHT;
+       }
+}
+
+void
+tty_check_bg(struct tty *tty, struct grid_cell *gc)
+{
+       u_int   colours;
+
+       /* Is this a 256-colour colour? */
+       if (gc->flags & GRID_FLAG_BG256) {
+               /*
+                * And not a 256 colour mode? Translate to 16-colour
+                * palette. Bold background doesn't exist portably, so just
+                * discard the bold bit if set.
+                */
+               if (!(tty->term->flags & TERM_88COLOURS) &&
+                   !(tty->term_flags & TERM_88COLOURS) &&
+                   !(tty->term->flags & TERM_256COLOURS) &&
+                   !(tty->term_flags & TERM_256COLOURS)) {
+                       gc->bg = colour_256to16(gc->bg);
+                       if (gc->bg & 8)
+                               gc->bg &= 7;
+                       gc->attr &= ~GRID_ATTR_BRIGHT;
+                       gc->flags &= ~GRID_FLAG_BG256;
+               }
+               return;
+       }
+
+       /* Is this an aixterm colour? */
+       colours = tty_term_number(tty->term, TTYC_COLORS);
+       if (gc->bg >= 100 && gc->bg <= 107 && colours < 16) {
+               gc->bg -= 90;
+               gc->attr |= GRID_ATTR_BRIGHT;
+       }
+}
+
+void
+tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
+{
+       struct grid_cell        *tc = &tty->cell;
+       u_char                   fg = gc->fg;
+       char                     s[32];
+
+       /* Is this a 256-colour colour? */
+       if (gc->flags & GRID_FLAG_FG256) {
+               /* Try as 256 colours or translating to 88. */
+               if (tty_try_256(tty, fg, "38") == 0)
+                       goto save_fg;
+               if (tty_try_88(tty, fg, "38") == 0)
+                       goto save_fg;
+               /* Else already handled by tty_check_fg. */
+               return;
+       }
+
+       /* Is this an aixterm bright colour? */
+       if (fg >= 90 && fg <= 97) {
+               xsnprintf(s, sizeof s, "\033[%dm", fg);
+               tty_puts(tty, s);
+               goto save_fg;
+       }
+
+       /* Otherwise set the foreground colour. */
+       tty_putcode1(tty, TTYC_SETAF, fg);
+
+save_fg:
+       /* Save the new values in the terminal current cell. */
+       tc->fg = fg;
+       tc->flags &= ~GRID_FLAG_FG256;
+       tc->flags |= gc->flags & GRID_FLAG_FG256;
+}
+
+void
+tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
+{
+       struct grid_cell        *tc = &tty->cell;
+       u_char                   bg = gc->bg;
+       char                     s[32];
+
+       /* Is this a 256-colour colour? */
+       if (gc->flags & GRID_FLAG_BG256) {
+               /* Try as 256 colours or translating to 88. */
+               if (tty_try_256(tty, bg, "48") == 0)
+                       goto save_bg;
+               if (tty_try_88(tty, bg, "48") == 0)
+                       goto save_bg;
+               /* Else already handled by tty_check_bg. */
+               return;
+       }
+
+       /* Is this an aixterm bright colour? */
+       if (bg >= 100 && bg <= 107) {
+               /* 16 colour terminals or above only. */
+               if (tty_term_number(tty->term, TTYC_COLORS) >= 16) {
+                       xsnprintf(s, sizeof s, "\033[%dm", bg);
+                       tty_puts(tty, s);
+                       goto save_bg;
+               }
+               bg -= 100;
+               /* no such thing as a bold background */
+       }
+
+       /* Otherwise set the background colour. */
+       tty_putcode1(tty, TTYC_SETAB, bg);
+
+save_bg:
+       /* Save the new values in the terminal current cell. */
+       tc->bg = bg;
+       tc->flags &= ~GRID_FLAG_BG256;
+       tc->flags |= gc->flags & GRID_FLAG_BG256;
+}
+
+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);
+}
diff --git a/external/bsd/tmux/dist/utf8.c b/external/bsd/tmux/dist/utf8.c
new file mode 100644 (file)
index 0000000..5f13299
--- /dev/null
@@ -0,0 +1,353 @@
+/* $Id: utf8.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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 *);
+u_int  utf8_combine(const struct utf8_data *);
+u_int  utf8_width(const struct utf8_data *);
+
+/*
+ * Open 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
+ *
+ * Returns 1 if more UTF-8 to come, 0 if not UTF-8.
+ */
+int
+utf8_open(struct utf8_data *utf8data, u_char ch)
+{
+       memset(utf8data, 0, sizeof *utf8data);
+       if (ch >= 0xc2 && ch <= 0xdf)
+               utf8data->size = 2;
+       else if (ch >= 0xe0 && ch <= 0xef)
+               utf8data->size = 3;
+       else if (ch >= 0xf0 && ch <= 0xf4)
+               utf8data->size = 4;
+       else
+               return (0);
+       utf8_append(utf8data, ch);
+       return (1);
+}
+
+/*
+ * Append character to UTF-8, closing if finished.
+ *
+ * Returns 1 if more UTF-8 data to come, 0 if finished.
+ */
+int
+utf8_append(struct utf8_data *utf8data, u_char ch)
+{
+       if (utf8data->have >= utf8data->size)
+               fatalx("UTF-8 character overflow");
+       if (utf8data->size > sizeof utf8data->data)
+               fatalx("UTF-8 character size too large");
+
+       utf8data->data[utf8data->have++] = ch;
+       if (utf8data->have != utf8data->size)
+               return (1);
+
+       utf8data->width = utf8_width(utf8data);
+       return (0);
+}
+
+/* Check if two width tree entries overlap. */
+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);
+}
+
+/* Build UTF-8 width tree. */
+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;
+       }
+}
+
+/* Combine UTF-8 into 32-bit Unicode. */
+u_int
+utf8_combine(const struct utf8_data *utf8data)
+{
+       u_int   value;
+
+       value = 0xff;
+       switch (utf8data->size) {
+       case 1:
+               value = utf8data->data[0];
+               break;
+       case 2:
+               value = utf8data->data[1] & 0x3f;
+               value |= (utf8data->data[0] & 0x1f) << 6;
+               break;
+       case 3:
+               value = utf8data->data[2] & 0x3f;
+               value |= (utf8data->data[1] & 0x3f) << 6;
+               value |= (utf8data->data[0] & 0x0f) << 12;
+               break;
+       case 4:
+               value = utf8data->data[3] & 0x3f;
+               value |= (utf8data->data[2] & 0x3f) << 6;
+               value |= (utf8data->data[1] & 0x3f) << 12;
+               value |= (utf8data->data[0] & 0x3f) << 18;
+               break;
+       }
+       return (value);
+}
+
+/* Split a two-byte UTF-8 character. */
+u_int
+utf8_split2(u_int uc, u_char *ptr)
+{
+       if (uc > 0x7f) {
+               ptr[0] = (uc >> 6) | 0xc0;
+               ptr[1] = (uc & 0x3f) | 0x80;
+               return (2);
+       }
+       ptr[0] = uc;
+       return (1);
+}
+
+/* Lookup width of UTF-8 data in tree. */
+u_int
+utf8_width(const struct utf8_data *utf8data)
+{
+       struct utf8_width_entry *item;
+       u_int                    value;
+
+       value = utf8_combine(utf8data);
+
+       item = utf8_width_root;
+       while (item != NULL) {
+               if (value < item->first)
+                       item = item->left;
+               else if (value > item->last)
+                       item = item->right;
+               else
+                       return (item->width);
+       }
+       return (1);
+}
diff --git a/external/bsd/tmux/dist/utmp.c b/external/bsd/tmux/dist/utmp.c
new file mode 100644 (file)
index 0000000..94a0b62
--- /dev/null
@@ -0,0 +1,167 @@
+/*     $NetBSD: utmp.c,v 1.1 2011/09/17 01:50:08 christos Exp $        */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: utmp.c,v 1.1 2011/09/17 01:50:08 christos Exp $");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <syslog.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <util.h>
+#include <paths.h>
+
+#ifdef SUPPORT_UTMP
+#include <utmp.h>
+#endif
+#ifdef SUPPORT_UTMPX
+#include <utmpx.h>
+#endif
+
+#include "tmux.h"
+
+struct window_utmp {
+#ifdef SUPPORT_UTMP
+       struct utmp ut;
+#endif
+#ifdef SUPPORT_UTMPX
+       struct utmpx utx;
+#endif
+};
+
+#ifdef SUPPORT_UTMPX
+static void
+login_utmpx(struct utmpx *utmpx, const char *username, const char *hostname,
+       const char *tty, const struct timeval *now)
+{
+       const char *t;
+
+       (void)memset(utmpx, 0, sizeof(*utmpx));
+       utmpx->ut_tv = *now;
+       (void)strncpy(utmpx->ut_name, username, sizeof(utmpx->ut_name));
+       if (hostname)
+               (void)strncpy(utmpx->ut_host, hostname, sizeof(utmpx->ut_host));
+       (void)strncpy(utmpx->ut_line, tty, sizeof(utmpx->ut_line));
+       utmpx->ut_type = USER_PROCESS;
+       utmpx->ut_pid = getpid();
+       t = tty + strlen(tty);
+       if ((size_t)(t - tty) >= sizeof(utmpx->ut_id)) {
+           (void)strncpy(utmpx->ut_id, t - sizeof(utmpx->ut_id),
+               sizeof(utmpx->ut_id));
+       } else {
+           (void)strncpy(utmpx->ut_id, tty, sizeof(utmpx->ut_id));
+       }
+       (void)pututxline(utmpx);
+       endutxent();
+}
+
+static void
+logout_utmpx(struct utmpx *utmpx, const struct timeval *now)
+{
+       utmpx->ut_type = DEAD_PROCESS;
+       utmpx->ut_tv = *now;
+       utmpx->ut_pid = 0;
+       (void)pututxline(utmpx);
+       endutxent();
+}
+#endif
+
+#ifdef SUPPORT_UTMP
+static void
+login_utmp(struct utmp *utmp, const char *username, const char *hostname,
+    const char *tty, const struct timeval *now)
+{
+       (void)memset(utmp, 0, sizeof(*utmp));
+       utmp->ut_time = now->tv_sec;
+       (void)strncpy(utmp->ut_name, username, sizeof(utmp->ut_name));
+       if (hostname)
+               (void)strncpy(utmp->ut_host, hostname, sizeof(utmp->ut_host));
+       (void)strncpy(utmp->ut_line, tty, sizeof(utmp->ut_line));
+       login(utmp);
+}
+
+static void
+logout_utmp(struct utmp *utmp, const struct timeval *now)
+{
+       logout(utmp->ut_line);
+}
+#endif
+
+struct window_utmp *
+utmp_create(const char *tty)
+{
+       struct window_utmp *wu;
+       struct timeval tv;
+       char username[LOGIN_NAME_MAX];
+
+       if (getlogin_r(username, sizeof(username)) == -1)
+               return NULL;
+
+       if ((wu = malloc(sizeof(*wu))) == NULL)
+               return NULL;
+
+       tty += sizeof(_PATH_DEV) - 1;
+
+       (void)gettimeofday(&tv, NULL);
+#ifdef SUPPORT_UTMPX
+       login_utmpx(&wu->utx, username, "tmux", tty, &tv);
+#endif
+#ifdef SUPPORT_UTMP
+       login_utmp(&wu->ut, username, "tmux", tty, &tv);
+#endif
+       return wu;
+}
+
+void
+utmp_destroy(struct window_utmp *wu)
+{
+       struct timeval tv;
+
+       if (wu == NULL)
+               return;
+
+       (void)gettimeofday(&tv, NULL);
+#ifdef SUPPORT_UTMPX
+       logout_utmpx(&wu->utx, &tv);
+#endif
+#ifdef SUPPORT_UTMP
+       logout_utmp(&wu->ut, &tv);
+#endif
+}
diff --git a/external/bsd/tmux/dist/window-choose.c b/external/bsd/tmux/dist/window-choose.c
new file mode 100644 (file)
index 0000000..f55ae6b
--- /dev/null
@@ -0,0 +1,457 @@
+/* $Id: window-choose.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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 session *, int);
+void   window_choose_mouse(
+           struct window_pane *, struct session *, struct mouse_event *);
+
+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                    (*callbackfn)(void *, int);
+       void                    (*freefn)(void *);
+       void                   *data;
+};
+
+int    window_choose_key_index(struct window_choose_mode_data *, u_int);
+int    window_choose_index_key(struct window_choose_mode_data *, int);
+
+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 (*callbackfn)(void *, int), void (*freefn)(void *), 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->callbackfn = callbackfn;
+       data->freefn = freefn;
+       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;
+       int                              keys;
+
+       wp->modedata = data = xmalloc(sizeof *data);
+
+       data->callbackfn = NULL;
+       data->freefn = NULL;
+       data->data = 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;
+       if (options_get_number(&wp->window->options, "mode-mouse"))
+               s->mode |= MODE_MOUSE_STANDARD;
+
+       keys = options_get_number(&wp->window->options, "mode-keys");
+       if (keys == MODEKEY_EMACS)
+               mode_key_init(&data->mdata, &mode_key_tree_emacs_choice);
+       else
+               mode_key_init(&data->mdata, &mode_key_tree_vi_choice);
+
+       return (s);
+}
+
+void
+window_choose_free(struct window_pane *wp)
+{
+       struct window_choose_mode_data  *data = wp->modedata;
+       u_int                            i;
+
+       if (data->freefn != NULL && data->data != NULL)
+               data->freefn(data->data);
+
+       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);
+}
+
+/* ARGSUSED */
+void
+window_choose_key(struct window_pane *wp, unused struct session *sess, 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;
+       int                              idx;
+
+       items = ARRAY_LENGTH(&data->list);
+
+       switch (mode_key_lookup(&data->mdata, key)) {
+       case MODEKEYCHOICE_CANCEL:
+               data->callbackfn(data->data, -1);
+               window_pane_reset_mode(wp);
+               break;
+       case MODEKEYCHOICE_CHOOSE:
+               item = &ARRAY_ITEM(&data->list, data->selected);
+               data->callbackfn(data->data, item->idx);
+               window_pane_reset_mode(wp);
+               break;
+       case MODEKEYCHOICE_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 MODEKEYCHOICE_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(s)) {
+                       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);
+               } else
+                       window_choose_scroll_down(wp);
+               break;
+       case MODEKEYCHOICE_SCROLLUP:
+               if (items == 0 || data->top == 0)
+                       break;
+               if (data->selected == data->top + screen_size_y(s) - 1) {
+                       data->selected--;
+                       window_choose_scroll_up(wp);
+                       screen_write_start(&ctx, wp, NULL);
+                       window_choose_write_line(
+                           wp, &ctx, screen_size_y(s) - 1);
+                       screen_write_stop(&ctx);
+               } else
+                       window_choose_scroll_up(wp);
+               break;
+       case MODEKEYCHOICE_SCROLLDOWN:
+               if (items == 0 ||
+                   data->top + screen_size_y(&data->screen) >= items)
+                       break;
+               if (data->selected == data->top) {
+                       data->selected++;
+                       window_choose_scroll_down(wp);
+                       screen_write_start(&ctx, wp, NULL);
+                       window_choose_write_line(wp, &ctx, 0);
+                       screen_write_stop(&ctx);
+               } else
+                       window_choose_scroll_down(wp);
+               break;
+       case MODEKEYCHOICE_PAGEUP:
+               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 MODEKEYCHOICE_PAGEDOWN:
+               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:
+               idx = window_choose_index_key(data, key);
+               if (idx < 0 || (u_int) idx >= ARRAY_LENGTH(&data->list))
+                       break;
+               data->selected = idx;
+
+               item = &ARRAY_ITEM(&data->list, data->selected);
+               data->callbackfn(data->data, item->idx);
+               window_pane_reset_mode(wp);
+               break;
+       }
+}
+
+/* ARGSUSED */
+void
+window_choose_mouse(
+    struct window_pane *wp, unused struct session *sess, struct mouse_event *m)
+{
+       struct window_choose_mode_data  *data = wp->modedata;
+       struct screen                   *s = &data->screen;
+       struct window_choose_mode_item  *item;
+       u_int                            idx;
+
+       if ((m->b & 3) == 3)
+               return;
+       if (m->x >= screen_size_x(s))
+               return;
+       if (m->y >= screen_size_y(s))
+               return;
+
+       idx = data->top + m->y;
+       if (idx >= ARRAY_LENGTH(&data->list))
+               return;
+       data->selected = idx;
+
+       item = &ARRAY_ITEM(&data->list, data->selected);
+       data->callbackfn(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 options                  *oo = &wp->window->options;
+       struct screen                   *s = &data->screen;
+       struct grid_cell                 gc;
+       int                              utf8flag, key;
+
+       if (data->callbackfn == NULL)
+               fatalx("called before callback assigned");
+
+       utf8flag = options_get_number(&wp->window->options, "utf8");
+       memcpy(&gc, &grid_default_cell, sizeof gc);
+       if (data->selected == data->top + py) {
+               colour_set_fg(&gc, options_get_number(oo, "mode-fg"));
+               colour_set_bg(&gc, options_get_number(oo, "mode-bg"));
+               gc.attr |= options_get_number(oo, "mode-attr");
+       }
+
+       screen_write_cursormove(ctx, 0, py);
+       if (data->top + py  < ARRAY_LENGTH(&data->list)) {
+               item = &ARRAY_ITEM(&data->list, data->top + py);
+               key = window_choose_key_index(data, data->top + py);
+               if (key != -1) {
+                       screen_write_nputs(ctx, screen_size_x(s) - 1,
+                           &gc, utf8flag, "(%c) %s", key, item->name);
+               } else {
+                       screen_write_nputs(ctx, screen_size_x(s) - 1,
+                           &gc, utf8flag, "    %s", item->name);
+               }
+
+       }
+       while (s->cx < screen_size_x(s))
+               screen_write_putc(ctx, &gc, ' ');
+}
+
+int
+window_choose_key_index(struct window_choose_mode_data *data, u_int idx)
+{
+       static const char       keys[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+       const char             *ptr;
+       int                     mkey;
+
+       for (ptr = keys; *ptr != '\0'; ptr++) {
+               mkey = mode_key_lookup(&data->mdata, *ptr);
+               if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER)
+                       continue;
+               if (idx-- == 0)
+                       return (*ptr);
+       }
+       return (-1);
+}
+
+int
+window_choose_index_key(struct window_choose_mode_data *data, int key)
+{
+       static const char       keys[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+       const char             *ptr;
+       int                     mkey;
+       u_int                   idx = 0;
+
+       for (ptr = keys; *ptr != '\0'; ptr++) {
+               mkey = mode_key_lookup(&data->mdata, *ptr);
+               if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER)
+                       continue;
+               if (key == *ptr)
+                       return (idx);
+               idx++;
+       }
+       return (-1);
+}
+
+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/external/bsd/tmux/dist/window-clock.c b/external/bsd/tmux/dist/window-clock.c
new file mode 100644 (file)
index 0000000..7f9725c
--- /dev/null
@@ -0,0 +1,124 @@
+/* $Id: window-clock.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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 session *, 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);
+}
+
+/* ARGSUSED */
+void
+window_clock_key(
+    struct window_pane *wp, unused struct session *sess, 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);
+       gmtime_r(&t, &now);
+       gmtime_r(&data->tim, &then);
+       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;
+       int                              colour, 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/external/bsd/tmux/dist/window-copy.c b/external/bsd/tmux/dist/window-copy.c
new file mode 100644 (file)
index 0000000..86a0034
--- /dev/null
@@ -0,0 +1,1910 @@
+/* $Id: window-copy.c,v 1.2 2011/08/17 19:28:36 jmmv 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"
+
+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 session *, int);
+int    window_copy_key_input(struct window_pane *, int);
+int    window_copy_key_numeric_prefix(struct window_pane *, int);
+void   window_copy_mouse(
+           struct window_pane *, struct session *, struct mouse_event *);
+
+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_scroll_to(struct window_pane *, u_int, u_int);
+int    window_copy_search_compare(
+           struct grid *, u_int, u_int, struct grid *, u_int);
+int    window_copy_search_lr(
+           struct grid *, struct grid *, u_int *, u_int, u_int, u_int);
+int    window_copy_search_rl(
+           struct grid *, struct grid *, u_int *, u_int, u_int, u_int);
+void   window_copy_search_up(struct window_pane *, const char *);
+void   window_copy_search_down(struct window_pane *, const char *);
+void   window_copy_goto_line(struct window_pane *, const char *);
+void   window_copy_update_cursor(struct window_pane *, u_int, u_int);
+void   window_copy_start_selection(struct window_pane *);
+int    window_copy_update_selection(struct window_pane *);
+void   window_copy_copy_selection(struct window_pane *);
+void   window_copy_clear_selection(struct window_pane *);
+void   window_copy_copy_line(
+           struct window_pane *, char **, size_t *, u_int, u_int, u_int);
+int    window_copy_in_set(struct window_pane *, u_int, u_int, const char *);
+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_back_to_indentation(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 *, int);
+void   window_copy_cursor_down(struct window_pane *, int);
+void   window_copy_cursor_jump(struct window_pane *);
+void   window_copy_cursor_jump_back(struct window_pane *);
+void   window_copy_cursor_next_word(struct window_pane *, const char *);
+void   window_copy_cursor_next_word_end(struct window_pane *, const char *);
+void   window_copy_cursor_previous_word(struct window_pane *, const char *);
+void   window_copy_scroll_up(struct window_pane *, u_int);
+void   window_copy_scroll_down(struct window_pane *, u_int);
+void   window_copy_rectangle_toggle(struct window_pane *);
+
+const struct window_mode window_copy_mode = {
+       window_copy_init,
+       window_copy_free,
+       window_copy_resize,
+       window_copy_key,
+       window_copy_mouse,
+       NULL,
+};
+
+enum window_copy_input_type {
+       WINDOW_COPY_OFF,
+       WINDOW_COPY_NUMERICPREFIX,
+       WINDOW_COPY_SEARCHUP,
+       WINDOW_COPY_SEARCHDOWN,
+       WINDOW_COPY_JUMPFORWARD,
+       WINDOW_COPY_JUMPBACK,
+       WINDOW_COPY_GOTOLINE,
+};
+
+/*
+ * Copy-mode's visible screen (the "screen" field) is filled from one of
+ * two sources: the original contents of the pane (used when we
+ * actually enter via the "copy-mode" command, to copy the contents of
+ * the current pane), or else a series of lines containing the output
+ * from an output-writing tmux command (such as any of the "show-*" or
+ * "list-*" commands).
+ *
+ * In either case, the full content of the copy-mode grid is pointed at
+ * by the "backing" field, and is copied into "screen" as needed (that
+ * is, when scrolling occurs). When copy-mode is backed by a pane,
+ * backing points directly at that pane's screen structure (&wp->base);
+ * when backed by a list of output-lines from a command, it points at
+ * a newly-allocated screen structure (which is deallocated when the
+ * mode ends).
+ */
+struct window_copy_mode_data {
+       struct screen   screen;
+
+       struct screen  *backing;
+       int             backing_written; /* backing display has started */
+
+       struct mode_key_data mdata;
+
+       u_int           oy;
+
+       u_int           selx;
+       u_int           sely;
+
+       u_int           rectflag; /* are we in rectangle copy mode? */
+
+       u_int           cx;
+       u_int           cy;
+
+       u_int           lastcx; /* position in last line with content */
+       u_int           lastsx; /* size of last line with content */
+
+       enum window_copy_input_type inputtype;
+       const char     *inputprompt;
+       char           *inputstr;
+
+       u_int           numprefix;
+
+       enum window_copy_input_type searchtype;
+       char           *searchstr;
+
+       enum window_copy_input_type jumptype;
+       char            jumpchar;
+};
+
+struct screen *
+window_copy_init(struct window_pane *wp)
+{
+       struct window_copy_mode_data    *data;
+       struct screen                   *s;
+       int                              keys;
+
+       wp->modedata = data = xmalloc(sizeof *data);
+       data->oy = 0;
+       data->cx = 0;
+       data->cy = 0;
+
+       data->lastcx = 0;
+       data->lastsx = 0;
+
+       data->backing_written = 0;
+
+       data->rectflag = 0;
+
+       data->inputtype = WINDOW_COPY_OFF;
+       data->inputprompt = NULL;
+       data->inputstr = xstrdup("");
+       data->numprefix = 0;
+
+       data->searchtype = WINDOW_COPY_OFF;
+       data->searchstr = NULL;
+
+       if (wp->fd != -1)
+               bufferevent_disable(wp->event, EV_READ|EV_WRITE);
+
+       data->jumptype = WINDOW_COPY_OFF;
+       data->jumpchar = '\0';
+
+       s = &data->screen;
+       screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
+       if (options_get_number(&wp->window->options, "mode-mouse"))
+               s->mode |= MODE_MOUSE_STANDARD;
+
+       keys = options_get_number(&wp->window->options, "mode-keys");
+       if (keys == MODEKEY_EMACS)
+               mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
+       else
+               mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
+
+       data->backing = NULL;
+
+       return (s);
+}
+
+void
+window_copy_init_from_pane(struct window_pane *wp)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *s = &data->screen;
+       struct screen_write_ctx          ctx;
+       u_int                            i;
+
+       if (wp->mode != &window_copy_mode)
+               fatalx("not in copy mode");
+
+       data->backing = &wp->base;
+       data->cx = data->backing->cx;
+       data->cy = data->backing->cy;
+
+       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);
+}
+
+void
+window_copy_init_for_output(struct window_pane *wp)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+
+       data->backing = xmalloc(sizeof *data->backing);
+       screen_init(data->backing, screen_size_x(&wp->base),
+           screen_size_y(&wp->base), UINT_MAX);
+       data->backing->mode &= ~MODE_WRAP;
+}
+
+void
+window_copy_free(struct window_pane *wp)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+
+       if (wp->fd != -1)
+               bufferevent_enable(wp->event, EV_READ|EV_WRITE);
+
+       if (data->searchstr != NULL)
+               xfree(data->searchstr);
+       xfree(data->inputstr);
+
+       if (data->backing != &wp->base) {
+               screen_free(data->backing);
+               xfree(data->backing);
+       }
+       screen_free(&data->screen);
+
+       xfree(data);
+}
+
+void
+window_copy_add(struct window_pane *wp, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       window_copy_vadd(wp, fmt, ap);
+       va_end(ap);
+}
+
+void
+window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *backing = data->backing;
+       struct screen_write_ctx          back_ctx, ctx;
+       struct grid_cell                 gc;
+       int                              utf8flag;
+       u_int                            old_hsize;
+
+       if (backing == &wp->base)
+               return;
+
+       utf8flag = options_get_number(&wp->window->options, "utf8");
+       memcpy(&gc, &grid_default_cell, sizeof gc);
+
+       old_hsize = screen_hsize(data->backing);
+       screen_write_start(&back_ctx, NULL, backing);
+       if (data->backing_written) {
+               /*
+                * On the second or later line, do a CRLF before writing
+                * (so it's on a new line).
+                */
+               screen_write_carriagereturn(&back_ctx);
+               screen_write_linefeed(&back_ctx, 0);
+       } else
+               data->backing_written = 1;
+       screen_write_vnputs(&back_ctx, 0, &gc, utf8flag, fmt, ap);
+       screen_write_stop(&back_ctx);
+
+       data->oy += screen_hsize(data->backing) - old_hsize;
+
+       screen_write_start(&ctx, wp, &data->screen);
+
+       /*
+        * If the history has changed, draw the top line.
+        * (If there's any history at all, it has changed.)
+        */
+       if (screen_hsize(data->backing))
+               window_copy_redraw_lines(wp, 0, 1);
+
+       /* Write the line, if it's visible. */
+       if (backing->cy + data->oy < screen_size_y(backing))
+               window_copy_redraw_lines(wp, backing->cy, 1);
+
+       screen_write_stop(&ctx);
+}
+
+void
+window_copy_pageup(struct window_pane *wp)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *s = &data->screen;
+       u_int                            n;
+
+       n = 1;
+       if (screen_size_y(s) > 2)
+               n = screen_size_y(s) - 2;
+       if (data->oy + n > screen_hsize(data->backing))
+               data->oy = screen_hsize(data->backing);
+       else
+               data->oy += n;
+       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);
+       if (data->backing != &wp->base)
+               screen_resize(data->backing, sx, sy);
+
+       if (data->cy > sy - 1)
+               data->cy = sy - 1;
+       if (data->cx > sx)
+               data->cx = sx;
+       if (data->oy > screen_hsize(data->backing))
+               data->oy = screen_hsize(data->backing);
+
+       window_copy_clear_selection(wp);
+
+       screen_write_start(&ctx, NULL, s);
+       window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1);
+       screen_write_stop(&ctx);
+
+       window_copy_redraw_screen(wp);
+}
+
+void
+window_copy_key(struct window_pane *wp, struct session *sess, int key)
+{
+       const char                      *word_separators;
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *s = &data->screen;
+       u_int                            n, np;
+       int                              keys;
+       enum mode_key_cmd                cmd;
+
+       np = data->numprefix;
+       if (np == 0)
+               np = 1;
+
+       if (data->inputtype == WINDOW_COPY_JUMPFORWARD ||
+           data->inputtype == WINDOW_COPY_JUMPBACK) {
+               /* Ignore keys with modifiers. */
+               if ((key & KEYC_MASK_MOD) == 0) {
+                       data->jumpchar = key;
+                       if (data->inputtype == WINDOW_COPY_JUMPFORWARD) {
+                               for (; np != 0; np--)
+                                       window_copy_cursor_jump(wp);
+                       }  else {
+                               for (; np != 0; np--)
+                                       window_copy_cursor_jump_back(wp);
+                       }
+               }
+               data->jumptype = data->inputtype;
+               data->inputtype = WINDOW_COPY_OFF;
+               window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
+               return;
+       } else if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) {
+               if (window_copy_key_numeric_prefix(wp, key) == 0)
+                       return;
+               data->inputtype = WINDOW_COPY_OFF;
+               window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
+       } else if (data->inputtype != WINDOW_COPY_OFF) {
+               if (window_copy_key_input(wp, key) != 0)
+                       goto input_off;
+               return;
+       }
+
+       cmd = mode_key_lookup(&data->mdata, key);
+       switch (cmd) {
+       case MODEKEYCOPY_CANCEL:
+               window_pane_reset_mode(wp);
+               return;
+       case MODEKEYCOPY_LEFT:
+               for (; np != 0; np--)
+                       window_copy_cursor_left(wp);
+               break;
+       case MODEKEYCOPY_RIGHT:
+               for (; np != 0; np--)
+                       window_copy_cursor_right(wp);
+               break;
+       case MODEKEYCOPY_UP:
+               for (; np != 0; np--)
+                       window_copy_cursor_up(wp, 0);
+               break;
+       case MODEKEYCOPY_DOWN:
+               for (; np != 0; np--)
+                       window_copy_cursor_down(wp, 0);
+               break;
+       case MODEKEYCOPY_SCROLLUP:
+               for (; np != 0; np--)
+                       window_copy_cursor_up(wp, 1);
+               break;
+       case MODEKEYCOPY_SCROLLDOWN:
+               for (; np != 0; np--)
+                       window_copy_cursor_down(wp, 1);
+               break;
+       case MODEKEYCOPY_PREVIOUSPAGE:
+               for (; np != 0; np--)
+                       window_copy_pageup(wp);
+               break;
+       case MODEKEYCOPY_NEXTPAGE:
+               n = 1;
+               if (screen_size_y(s) > 2)
+                       n = screen_size_y(s) - 2;
+               for (; np != 0; np--) {
+                       if (data->oy < n)
+                               data->oy = 0;
+                       else
+                               data->oy -= n;
+               }
+               window_copy_update_selection(wp);
+               window_copy_redraw_screen(wp);
+               break;
+       case MODEKEYCOPY_HALFPAGEUP:
+               n = screen_size_y(s) / 2;
+               for (; np != 0; np--) {
+                       if (data->oy + n > screen_hsize(data->backing))
+                               data->oy = screen_hsize(data->backing);
+                       else
+                               data->oy += n;
+               }
+               window_copy_update_selection(wp);
+               window_copy_redraw_screen(wp);
+               break;
+       case MODEKEYCOPY_HALFPAGEDOWN:
+               n = screen_size_y(s) / 2;
+               for (; np != 0; np--) {
+                       if (data->oy < n)
+                               data->oy = 0;
+                       else
+                               data->oy -= n;
+               }
+               window_copy_update_selection(wp);
+               window_copy_redraw_screen(wp);
+               break;
+       case MODEKEYCOPY_TOPLINE:
+               data->cx = 0;
+               data->cy = 0;
+               window_copy_update_selection(wp);
+               window_copy_redraw_screen(wp);
+               break;
+       case MODEKEYCOPY_MIDDLELINE:
+               data->cx = 0;
+               data->cy = (screen_size_y(s) - 1) / 2;
+               window_copy_update_selection(wp);
+               window_copy_redraw_screen(wp);
+               break;
+       case MODEKEYCOPY_BOTTOMLINE:
+               data->cx = 0;
+               data->cy = screen_size_y(s) - 1;
+               window_copy_update_selection(wp);
+               window_copy_redraw_screen(wp);
+               break;
+       case MODEKEYCOPY_HISTORYTOP:
+               data->cx = 0;
+               data->cy = 0;
+               data->oy = screen_hsize(data->backing);
+               window_copy_update_selection(wp);
+               window_copy_redraw_screen(wp);
+               break;
+       case MODEKEYCOPY_HISTORYBOTTOM:
+               data->cx = 0;
+               data->cy = screen_size_y(s) - 1;
+               data->oy = 0;
+               window_copy_update_selection(wp);
+               window_copy_redraw_screen(wp);
+               break;
+       case MODEKEYCOPY_STARTSELECTION:
+               window_copy_start_selection(wp);
+               window_copy_redraw_screen(wp);
+               break;
+       case MODEKEYCOPY_COPYLINE:
+       case MODEKEYCOPY_SELECTLINE:
+               window_copy_cursor_start_of_line(wp);
+               /* FALLTHROUGH */
+       case MODEKEYCOPY_COPYENDOFLINE:
+               window_copy_start_selection(wp);
+               for (; np > 1; np--)
+                       window_copy_cursor_down(wp, 0);
+               window_copy_cursor_end_of_line(wp);
+               window_copy_redraw_screen(wp);
+
+               /* If a copy command then copy the selection and exit. */
+               if (sess != NULL &&
+                   (cmd == MODEKEYCOPY_COPYLINE ||
+                   cmd == MODEKEYCOPY_COPYENDOFLINE)) {
+                       window_copy_copy_selection(wp);
+                       window_pane_reset_mode(wp);
+                       return;
+               }
+               break;
+       case MODEKEYCOPY_CLEARSELECTION:
+               window_copy_clear_selection(wp);
+               window_copy_redraw_screen(wp);
+               break;
+       case MODEKEYCOPY_COPYSELECTION:
+               if (sess != NULL) {
+                       window_copy_copy_selection(wp);
+                       window_pane_reset_mode(wp);
+                       return;
+               }
+               break;
+       case MODEKEYCOPY_STARTOFLINE:
+               window_copy_cursor_start_of_line(wp);
+               break;
+       case MODEKEYCOPY_BACKTOINDENTATION:
+               window_copy_cursor_back_to_indentation(wp);
+               break;
+       case MODEKEYCOPY_ENDOFLINE:
+               window_copy_cursor_end_of_line(wp);
+               break;
+       case MODEKEYCOPY_NEXTSPACE:
+               for (; np != 0; np--)
+                       window_copy_cursor_next_word(wp, " ");
+               break;
+       case MODEKEYCOPY_NEXTSPACEEND:
+               for (; np != 0; np--)
+                       window_copy_cursor_next_word_end(wp, " ");
+               break;
+       case MODEKEYCOPY_NEXTWORD:
+               word_separators =
+                   options_get_string(&wp->window->options, "word-separators");
+               for (; np != 0; np--)
+                       window_copy_cursor_next_word(wp, word_separators);
+               break;
+       case MODEKEYCOPY_NEXTWORDEND:
+               word_separators =
+                   options_get_string(&wp->window->options, "word-separators");
+               for (; np != 0; np--)
+                       window_copy_cursor_next_word_end(wp, word_separators);
+               break;
+       case MODEKEYCOPY_PREVIOUSSPACE:
+               for (; np != 0; np--)
+                       window_copy_cursor_previous_word(wp, " ");
+               break;
+       case MODEKEYCOPY_PREVIOUSWORD:
+               word_separators =
+                   options_get_string(&wp->window->options, "word-separators");
+               for (; np != 0; np--)
+                       window_copy_cursor_previous_word(wp, word_separators);
+               break;
+       case MODEKEYCOPY_JUMP:
+               data->inputtype = WINDOW_COPY_JUMPFORWARD;
+               data->inputprompt = "Jump Forward";
+               *data->inputstr = '\0';
+               window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
+               return; /* skip numprefix reset */
+       case MODEKEYCOPY_JUMPAGAIN:
+               if (data->jumptype == WINDOW_COPY_JUMPFORWARD) {
+                       for (; np != 0; np--)
+                               window_copy_cursor_jump(wp);
+               } else if (data->jumptype == WINDOW_COPY_JUMPBACK) {
+                       for (; np != 0; np--)
+                               window_copy_cursor_jump_back(wp);
+               }
+               break;
+       case MODEKEYCOPY_JUMPREVERSE:
+               if (data->jumptype == WINDOW_COPY_JUMPFORWARD) {
+                       for (; np != 0; np--)
+                               window_copy_cursor_jump_back(wp);
+               } else if (data->jumptype == WINDOW_COPY_JUMPBACK) {
+                       for (; np != 0; np--)
+                               window_copy_cursor_jump(wp);
+               }
+               break;
+       case MODEKEYCOPY_JUMPBACK:
+               data->inputtype = WINDOW_COPY_JUMPBACK;
+               data->inputprompt = "Jump Back";
+               *data->inputstr = '\0';
+               window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
+               return; /* skip numprefix reset */
+       case MODEKEYCOPY_SEARCHUP:
+               data->inputtype = WINDOW_COPY_SEARCHUP;
+               data->inputprompt = "Search Up";
+               goto input_on;
+       case MODEKEYCOPY_SEARCHDOWN:
+               data->inputtype = WINDOW_COPY_SEARCHDOWN;
+               data->inputprompt = "Search Down";
+               goto input_on;
+       case MODEKEYCOPY_SEARCHAGAIN:
+       case MODEKEYCOPY_SEARCHREVERSE:
+               switch (data->searchtype) {
+               case WINDOW_COPY_OFF:
+               case WINDOW_COPY_GOTOLINE:
+               case WINDOW_COPY_JUMPFORWARD:
+               case WINDOW_COPY_JUMPBACK:
+               case WINDOW_COPY_NUMERICPREFIX:
+                       break;
+               case WINDOW_COPY_SEARCHUP:
+                       if (cmd == MODEKEYCOPY_SEARCHAGAIN) {
+                               for (; np != 0; np--) {
+                                       window_copy_search_up(
+                                           wp, data->searchstr);
+                               }
+                       } else {
+                               for (; np != 0; np--) {
+                                       window_copy_search_down(
+                                           wp, data->searchstr);
+                               }
+                       }
+                       break;
+               case WINDOW_COPY_SEARCHDOWN:
+                       if (cmd == MODEKEYCOPY_SEARCHAGAIN) {
+                               for (; np != 0; np--) {
+                                       window_copy_search_down(
+                                           wp, data->searchstr);
+                               }
+                       } else {
+                               for (; np != 0; np--) {
+                                       window_copy_search_up(
+                                           wp, data->searchstr);
+                               }
+                       }
+                       break;
+               }
+               break;
+       case MODEKEYCOPY_GOTOLINE:
+               data->inputtype = WINDOW_COPY_GOTOLINE;
+               data->inputprompt = "Goto Line";
+               *data->inputstr = '\0';
+               goto input_on;
+       case MODEKEYCOPY_STARTNUMBERPREFIX:
+               key &= KEYC_MASK_KEY;
+               if (key >= '0' && key <= '9') {
+                       data->inputtype = WINDOW_COPY_NUMERICPREFIX;
+                       data->numprefix = 0;
+                       window_copy_key_numeric_prefix(wp, key);
+                       return;
+               }
+               break;
+       case MODEKEYCOPY_RECTANGLETOGGLE:
+               window_copy_rectangle_toggle(wp);
+               break;
+       default:
+               break;
+       }
+
+       data->numprefix = 0;
+       return;
+
+input_on:
+       keys = options_get_number(&wp->window->options, "mode-keys");
+       if (keys == MODEKEY_EMACS)
+               mode_key_init(&data->mdata, &mode_key_tree_emacs_edit);
+       else
+               mode_key_init(&data->mdata, &mode_key_tree_vi_edit);
+
+       window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
+       return;
+
+input_off:
+       keys = options_get_number(&wp->window->options, "mode-keys");
+       if (keys == MODEKEY_EMACS)
+               mode_key_init(&data->mdata, &mode_key_tree_emacs_copy);
+       else
+               mode_key_init(&data->mdata, &mode_key_tree_vi_copy);
+
+       data->inputtype = WINDOW_COPY_OFF;
+       data->inputprompt = NULL;
+
+       window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
+}
+
+int
+window_copy_key_input(struct window_pane *wp, int key)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *s = &data->screen;
+       size_t                           inputlen;
+       u_int                            np;
+
+       switch (mode_key_lookup(&data->mdata, key)) {
+       case MODEKEYEDIT_CANCEL:
+               data->numprefix = 0;
+               return (-1);
+       case MODEKEYEDIT_BACKSPACE:
+               inputlen = strlen(data->inputstr);
+               if (inputlen > 0)
+                       data->inputstr[inputlen - 1] = '\0';
+               break;
+       case MODEKEYEDIT_DELETELINE:
+               *data->inputstr = '\0';
+               break;
+       case MODEKEYEDIT_ENTER:
+               np = data->numprefix;
+               if (np == 0)
+                       np = 1;
+
+               switch (data->inputtype) {
+               case WINDOW_COPY_OFF:
+               case WINDOW_COPY_JUMPFORWARD:
+               case WINDOW_COPY_JUMPBACK:
+               case WINDOW_COPY_NUMERICPREFIX:
+                       break;
+               case WINDOW_COPY_SEARCHUP:
+                       for (; np != 0; np--)
+                               window_copy_search_up(wp, data->inputstr);
+                       data->searchtype = data->inputtype;
+                       data->searchstr = xstrdup(data->inputstr);
+                       break;
+               case WINDOW_COPY_SEARCHDOWN:
+                       for (; np != 0; np--)
+                               window_copy_search_down(wp, data->inputstr);
+                       data->searchtype = data->inputtype;
+                       data->searchstr = xstrdup(data->inputstr);
+                       break;
+               case WINDOW_COPY_GOTOLINE:
+                       window_copy_goto_line(wp, data->inputstr);
+                       *data->inputstr = '\0';
+                       break;
+               }
+               data->numprefix = 0;
+               return (1);
+       case MODEKEY_OTHER:
+               if (key < 32 || key > 126)
+                       break;
+               inputlen = strlen(data->inputstr) + 2;
+
+               data->inputstr = xrealloc(data->inputstr, 1, inputlen);
+               data->inputstr[inputlen - 2] = key;
+               data->inputstr[inputlen - 1] = '\0';
+               break;
+       default:
+               break;
+       }
+
+       window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
+       return (0);
+}
+
+int
+window_copy_key_numeric_prefix(struct window_pane *wp, int key)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *s = &data->screen;
+
+       key &= KEYC_MASK_KEY;
+       if (key < '0' || key > '9')
+               return 1;
+
+       if (data->numprefix >= 100)     /* no more than three digits */
+               return 0;
+       data->numprefix = data->numprefix * 10 + key - '0';
+
+       window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
+       return 0;
+}
+
+/* ARGSUSED */
+void
+window_copy_mouse(
+    struct window_pane *wp, struct session *sess, struct mouse_event *m)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *s = &data->screen;
+       u_int                            i, old_cy;
+
+       if (m->x >= screen_size_x(s))
+               return;
+       if (m->y >= screen_size_y(s))
+               return;
+
+       /* If mouse wheel (buttons 4 and 5), scroll. */
+       if ((m->b & MOUSE_45)) {
+               if ((m->b & MOUSE_BUTTON) == MOUSE_1) {
+                       for (i = 0; i < 5; i++)
+                               window_copy_cursor_up(wp, 0);
+               } else if ((m->b & MOUSE_BUTTON) == MOUSE_2) {
+                       old_cy = data->cy;
+                       for (i = 0; i < 5; i++)
+                               window_copy_cursor_down(wp, 0);
+                       if (old_cy == data->cy)
+                               goto reset_mode;
+               }
+               return;
+       }
+
+       /*
+        * If already reading motion, move the cursor while buttons are still
+        * pressed, or stop the selection on their release.
+        */
+       if (s->mode & MODE_MOUSE_BUTTON) {
+               if ((m->b & MOUSE_BUTTON) != MOUSE_UP) {
+                       window_copy_update_cursor(wp, m->x, m->y);
+                       if (window_copy_update_selection(wp))
+                               window_copy_redraw_screen(wp);
+                       return;
+               }
+               goto reset_mode;
+       }
+
+       /* Otherwise if other buttons pressed, start selection and motion. */
+       if ((m->b & MOUSE_BUTTON) != MOUSE_UP) {
+               s->mode &= ~MODE_MOUSE_STANDARD;
+               s->mode |= MODE_MOUSE_BUTTON;
+
+               window_copy_update_cursor(wp, m->x, m->y);
+               window_copy_start_selection(wp);
+               window_copy_redraw_screen(wp);
+       }
+
+       return;
+
+reset_mode:
+       s->mode &= ~MODE_MOUSE_BUTTON;
+       s->mode |= MODE_MOUSE_STANDARD;
+       if (sess != NULL) {
+               window_copy_copy_selection(wp);
+               window_pane_reset_mode(wp);
+       }
+}
+
+void
+window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct grid                     *gd = data->backing->grid;
+       u_int                            offset, gap;
+
+       data->cx = px;
+
+       gap = gd->sy / 4;
+       if (py < gd->sy) {
+               offset = 0;
+               data->cy = py;
+       } else if (py > gd->hsize + gd->sy - gap) {
+               offset = gd->hsize;
+               data->cy = py - gd->hsize;
+       } else {
+               offset = py + gap - gd->sy;
+               data->cy = py - offset;
+       }
+       data->oy = gd->hsize - offset;
+
+       window_copy_update_selection(wp);
+       window_copy_redraw_screen(wp);
+}
+
+int
+window_copy_search_compare(
+    struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx)
+{
+       const struct grid_cell  *gc, *sgc;
+       const struct grid_utf8  *gu, *sgu;
+
+       gc = grid_peek_cell(gd, px, py);
+       sgc = grid_peek_cell(sgd, spx, 0);
+
+       if ((gc->flags & GRID_FLAG_UTF8) != (sgc->flags & GRID_FLAG_UTF8))
+               return (0);
+
+       if (gc->flags & GRID_FLAG_UTF8) {
+               gu = grid_peek_utf8(gd, px, py);
+               sgu = grid_peek_utf8(sgd, spx, 0);
+               if (grid_utf8_compare(gu, sgu))
+                       return (1);
+       } else {
+               if (gc->data == sgc->data)
+                       return (1);
+       }
+       return (0);
+}
+
+int
+window_copy_search_lr(struct grid *gd,
+    struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last)
+{
+       u_int   ax, bx, px;
+
+       for (ax = first; ax < last; ax++) {
+               if (ax + sgd->sx >= gd->sx)
+                       break;
+               for (bx = 0; bx < sgd->sx; bx++) {
+                       px = ax + bx;
+                       if (!window_copy_search_compare(gd, px, py, sgd, bx))
+                               break;
+               }
+               if (bx == sgd->sx) {
+                       *ppx = ax;
+                       return (1);
+               }
+       }
+       return (0);
+}
+
+int
+window_copy_search_rl(struct grid *gd,
+    struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last)
+{
+       u_int   ax, bx, px;
+
+       for (ax = last + 1; ax > first; ax--) {
+               if (gd->sx - (ax - 1) < sgd->sx)
+                       continue;
+               for (bx = 0; bx < sgd->sx; bx++) {
+                       px = ax - 1 + bx;
+                       if (!window_copy_search_compare(gd, px, py, sgd, bx))
+                               break;
+               }
+               if (bx == sgd->sx) {
+                       *ppx = ax - 1;
+                       return (1);
+               }
+       }
+       return (0);
+}
+
+void
+window_copy_search_up(struct window_pane *wp, const char *searchstr)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *s = data->backing, ss;
+       struct screen_write_ctx          ctx;
+       struct grid                     *gd = s->grid, *sgd;
+       struct grid_cell                 gc;
+       size_t                           searchlen;
+       u_int                            i, last, fx, fy, px;
+       int                              utf8flag, n, wrapped;
+
+       if (*searchstr == '\0')
+               return;
+       utf8flag = options_get_number(&wp->window->options, "utf8");
+       searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
+
+       screen_init(&ss, searchlen, 1, 0);
+       screen_write_start(&ctx, NULL, &ss);
+       memcpy(&gc, &grid_default_cell, sizeof gc);
+       screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
+       screen_write_stop(&ctx);
+
+       fx = data->cx;
+       fy = gd->hsize - data->oy + data->cy;
+
+       if (fx == 0) {
+               if (fy == 0)
+                       return;
+               fx = gd->sx - 1;
+               fy--;
+       } else
+               fx--;
+       n = wrapped = 0;
+
+retry:
+       sgd = ss.grid;
+       for (i = fy + 1; i > 0; i--) {
+               last = screen_size_x(s);
+               if (i == fy + 1)
+                       last = fx;
+               n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last);
+               if (n) {
+                       window_copy_scroll_to(wp, px, i - 1);
+                       break;
+               }
+       }
+       if (!n && !wrapped) {
+               fx = gd->sx - 1;
+               fy = gd->hsize + gd->sy - 1;
+               wrapped = 1;
+               goto retry;
+       }
+
+       screen_free(&ss);
+}
+
+void
+window_copy_search_down(struct window_pane *wp, const char *searchstr)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *s = data->backing, ss;
+       struct screen_write_ctx          ctx;
+       struct grid                     *gd = s->grid, *sgd;
+       struct grid_cell                 gc;
+       size_t                           searchlen;
+       u_int                            i, first, fx, fy, px;
+       int                              utf8flag, n, wrapped;
+
+       if (*searchstr == '\0')
+               return;
+       utf8flag = options_get_number(&wp->window->options, "utf8");
+       searchlen = screen_write_strlen(utf8flag, "%s", searchstr);
+
+       screen_init(&ss, searchlen, 1, 0);
+       screen_write_start(&ctx, NULL, &ss);
+       memcpy(&gc, &grid_default_cell, sizeof gc);
+       screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr);
+       screen_write_stop(&ctx);
+
+       fx = data->cx;
+       fy = gd->hsize - data->oy + data->cy;
+
+       if (fx == gd->sx - 1) {
+               if (fy == gd->hsize + gd->sy)
+                       return;
+               fx = 0;
+               fy++;
+       } else
+               fx++;
+       n = wrapped = 0;
+
+retry:
+       sgd = ss.grid;
+       for (i = fy + 1; i < gd->hsize + gd->sy; i++) {
+               first = 0;
+               if (i == fy + 1)
+                       first = fx;
+               n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx);
+               if (n) {
+                       window_copy_scroll_to(wp, px, i - 1);
+                       break;
+               }
+       }
+       if (!n && !wrapped) {
+               fx = 0;
+               fy = 0;
+               wrapped = 1;
+               goto retry;
+       }
+
+       screen_free(&ss);
+}
+
+void
+window_copy_goto_line(struct window_pane *wp, const char *linestr)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       const char                      *errstr;
+       u_int                            lineno;
+
+       lineno = strtonum(linestr, 0, screen_hsize(data->backing), &errstr);
+       if (errstr != NULL)
+               return;
+
+       data->oy = lineno;
+       window_copy_update_selection(wp);
+       window_copy_redraw_screen(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 options                  *oo = &wp->window->options;
+       struct grid_cell                 gc;
+       char                             hdr[32];
+       size_t                           last, xoff = 0, size = 0;
+
+       memcpy(&gc, &grid_default_cell, sizeof gc);
+       colour_set_fg(&gc, options_get_number(oo, "mode-fg"));
+       colour_set_bg(&gc, options_get_number(oo, "mode-bg"));
+       gc.attr |= options_get_number(oo, "mode-attr");
+
+       last = screen_size_y(s) - 1;
+       if (py == 0) {
+               size = xsnprintf(hdr, sizeof hdr,
+                   "[%u/%u]", data->oy, screen_hsize(data->backing));
+               if (size > screen_size_x(s))
+                       size = screen_size_x(s);
+               screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
+               screen_write_puts(ctx, &gc, "%s", hdr);
+       } else if (py == last && data->inputtype != WINDOW_COPY_OFF) {
+               if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) {
+                       xoff = size = xsnprintf(hdr, sizeof hdr,
+                           "Repeat: %u", data->numprefix);
+               } else {
+                       xoff = size = xsnprintf(hdr, sizeof hdr,
+                           "%s: %s", data->inputprompt, data->inputstr);
+               }
+               screen_write_cursormove(ctx, 0, last);
+               screen_write_puts(ctx, &gc, "%s", hdr);
+       } else
+               size = 0;
+
+       screen_write_cursormove(ctx, xoff, py);
+       screen_write_copy(ctx, data->backing, xoff,
+           (screen_hsize(data->backing) - data->oy) + py,
+           screen_size_x(s) - size, 1);
+
+       if (py == data->cy && data->cx == screen_size_x(s)) {
+               memcpy(&gc, &grid_default_cell, sizeof gc);
+               screen_write_cursormove(ctx, screen_size_x(s) - 1, py);
+               screen_write_putc(ctx, &gc, '$');
+       }
+}
+
+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_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, u_int cx, u_int cy)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *s = &data->screen;
+       struct screen_write_ctx          ctx;
+       u_int                            old_cx, old_cy;
+
+       old_cx = data->cx; old_cy = data->cy;
+       data->cx = cx; data->cy = cy;
+       if (old_cx == screen_size_x(s))
+               window_copy_redraw_lines(wp, old_cy, 1);
+       if (data->cx == screen_size_x(s))
+               window_copy_redraw_lines(wp, data->cy, 1);
+       else {
+               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->sely = screen_hsize(data->backing) + 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 options                  *oo = &wp->window->options;
+       struct grid_cell                 gc;
+       u_int                            sx, sy, ty, cy;
+
+       if (!s->sel.flag)
+               return (0);
+
+       /* Set colours. */
+       memcpy(&gc, &grid_default_cell, sizeof gc);
+       colour_set_fg(&gc, options_get_number(oo, "mode-fg"));
+       colour_set_bg(&gc, options_get_number(oo, "mode-bg"));
+       gc.attr |= options_get_number(oo, "mode-attr");
+
+       /* Find top of screen. */
+       ty = screen_hsize(data->backing) - data->oy;
+
+       /* Adjust the selection. */
+       sx = data->selx;
+       sy = data->sely;
+       if (sy < ty) {                                  /* above screen */
+               if (!data->rectflag)
+                       sx = 0;
+               sy = 0;
+       } else if (sy > ty + screen_size_y(s) - 1) {    /* below screen */
+               if (!data->rectflag)
+                       sx = screen_size_x(s) - 1;
+               sy = screen_size_y(s) - 1;
+       } else
+               sy -= ty;
+       sy = screen_hsize(s) + sy;
+
+       screen_set_selection(s,
+           sx, sy, data->cx, screen_hsize(s) + data->cy, data->rectflag, &gc);
+
+       if (data->rectflag) {
+               /*
+                * Can't rely on the caller to redraw the right lines for
+                * rectangle selection - find the highest line and the number
+                * of lines, and redraw just past that in both directions
+                */
+               cy = data->cy;
+               if (sy < cy)
+                       window_copy_redraw_lines(wp, sy, cy - sy + 1);
+               else
+                       window_copy_redraw_lines(wp, cy, sy - cy + 1);
+       }
+
+       return (1);
+}
+
+void
+window_copy_copy_selection(struct window_pane *wp)
+{
+       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;
+       u_int                            firstsx, lastex, restex, restsx;
+       int                              keys;
+
+       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;
+       yy = screen_hsize(data->backing) + data->cy - data->oy;
+       if (yy < data->sely || (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;
+
+       /*
+        * Deal with rectangle-copy if necessary; four situations: start of
+        * first line (firstsx), end of last line (lastex), start (restsx) and
+        * end (restex) of all other lines.
+        */
+       xx = screen_size_x(s);
+
+       /*
+        * Behave according to mode-keys. If it is emacs, copy like emacs,
+        * keeping the top-left-most character, and dropping the
+        * bottom-right-most, regardless of copy direction. If it is vi, also
+        * keep bottom-right-most character.
+        */
+       keys = options_get_number(&wp->window->options, "mode-keys");
+       if (data->rectflag) {
+               /*
+                * Need to ignore the column with the cursor in it, which for
+                * rectangular copy means knowing which side the cursor is on.
+                */
+               if (data->selx < data->cx) {
+                       /* Selection start is on the left. */
+                       if (keys == MODEKEY_EMACS) {
+                               lastex = data->cx;
+                               restex = data->cx;
+                       }
+                       else {
+                               lastex = data->cx + 1;
+                               restex = data->cx + 1;
+                       }
+                       firstsx = data->selx;
+                       restsx = data->selx;
+               } else {
+                       /* Cursor is on the left. */
+                       lastex = data->selx + 1;
+                       restex = data->selx + 1;
+                       firstsx = data->cx;
+                       restsx = data->cx;
+               }
+       } else {
+               if (keys == MODEKEY_EMACS)
+                       lastex = ex;
+               else    
+                       lastex = ex + 1;
+               restex = xx;
+               firstsx = sx;
+               restsx = 0;
+       }
+
+       /* Copy the lines. */
+       if (sy == ey)
+               window_copy_copy_line(wp, &buf, &off, sy, firstsx, lastex);
+       else {
+               window_copy_copy_line(wp, &buf, &off, sy, firstsx, restex);
+               if (ey - sy > 1) {
+                       for (i = sy + 1; i < ey; i++) {
+                               window_copy_copy_line(
+                                   wp, &buf, &off, i, restsx, restex);
+                       }
+               }
+               window_copy_copy_line(wp, &buf, &off, ey, restsx, lastex);
+       }
+
+       /* Don't bother if no data. */
+       if (off == 0) {
+               xfree(buf);
+               return;
+       }
+       off--;  /* remove final \n */
+
+       if (options_get_number(&global_options, "set-clipboard"))
+               screen_write_setselection(&wp->ictx.ctx, (u_char *)buf, off);
+
+       /* Add the buffer to the stack. */
+       limit = options_get_number(&global_options, "buffer-limit");
+       paste_add(&global_buffers, buf, off, limit);
+}
+
+void
+window_copy_copy_line(struct window_pane *wp,
+    char **buf, size_t *off, u_int sy, u_int sx, u_int ex)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct grid                     *gd = data->backing->grid;
+       const struct grid_cell          *gc;
+       const struct grid_utf8          *gu;
+       struct grid_line                *gl;
+       u_int                            i, xx, wrapped = 0;
+       size_t                           size;
+
+       if (sx > ex)
+               return;
+
+       /*
+        * Work out if the line was wrapped at the screen edge and all of it is
+        * on screen.
+        */
+       gl = &gd->linedata[sy];
+       if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
+               wrapped = 1;
+
+       /* If the line was wrapped, don't strip spaces (use the full length). */
+       if (wrapped)
+               xx = gl->cellsize;
+       else
+               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(gd, 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(gd, i, sy);
+                               size = grid_utf8_size(gu);
+                               *buf = xrealloc(*buf, 1, (*off) + size);
+                               *off += grid_utf8_copy(gu, *buf + *off, size);
+                       }
+               }
+       }
+
+       /* Only add a newline if the line wasn't wrapped. */
+       if (!wrapped || ex != xx) {
+               *buf = xrealloc(*buf, 1, (*off) + 1);
+               (*buf)[(*off)++] = '\n';
+       }
+}
+
+void
+window_copy_clear_selection(struct window_pane *wp)
+{
+       struct window_copy_mode_data   *data = wp->modedata;
+       u_int                           px, py;
+
+       screen_clear_selection(&data->screen);
+
+       py = screen_hsize(data->backing) + data->cy - data->oy;
+       px = window_copy_find_length(wp, py);
+       if (data->cx > px)
+               window_copy_update_cursor(wp, px, data->cy);
+}
+
+int
+window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       const struct grid_cell          *gc;
+
+       gc = grid_peek_cell(data->backing->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(set, gc->data) != NULL);
+}
+
+u_int
+window_copy_find_length(struct window_pane *wp, u_int py)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *s = data->backing;
+       const struct grid_cell          *gc;
+       u_int                            px;
+
+       /*
+        * If the pane has been resized, its grid can contain old overlong
+        * lines. grid_peek_cell does not allow accessing cells beyond the
+        * width of the grid, and screen_write_copy treats them as spaces, so
+        * ignore them here too.
+        */
+       px = s->grid->linedata[py].cellsize;
+       if (px > screen_size_x(s))
+               px = screen_size_x(s);
+       while (px > 0) {
+               gc = grid_peek_cell(s->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;
+       struct screen                   *back_s = data->backing;
+       struct grid                     *gd = back_s->grid;
+       u_int                            py;
+
+       if (data->cx == 0) {
+               py = screen_hsize(back_s) + data->cy - data->oy;
+               while (py > 0 && gd->linedata[py-1].flags & GRID_LINE_WRAPPED) {
+                       window_copy_cursor_up(wp, 0);
+                       py = screen_hsize(back_s) + data->cy - data->oy;
+               }
+       }
+       window_copy_update_cursor(wp, 0, data->cy);
+       if (window_copy_update_selection(wp))
+               window_copy_redraw_lines(wp, data->cy, 1);
+}
+
+void
+window_copy_cursor_back_to_indentation(struct window_pane *wp)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       u_int                            px, py, xx;
+       const struct grid_cell          *gc;
+
+       px = 0;
+       py = screen_hsize(data->backing) + data->cy - data->oy;
+       xx = window_copy_find_length(wp, py);
+
+       while (px < xx) {
+               gc = grid_peek_cell(data->backing->grid, px, py);
+               if (gc->flags & GRID_FLAG_UTF8)
+                       break;
+               if (gc->data != ' ')
+                       break;
+               px++;
+       }
+
+       window_copy_update_cursor(wp, px, data->cy);
+       if (window_copy_update_selection(wp))
+               window_copy_redraw_lines(wp, data->cy, 1);
+}
+
+void
+window_copy_cursor_end_of_line(struct window_pane *wp)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *back_s = data->backing;
+       struct grid                     *gd = back_s->grid;
+       u_int                            px, py;
+
+       py = screen_hsize(back_s) + data->cy - data->oy;
+       px = window_copy_find_length(wp, py);
+
+       if (data->cx == px) {
+               if (data->screen.sel.flag && data->rectflag)
+                       px = screen_size_x(back_s);
+               if (gd->linedata[py].flags & GRID_LINE_WRAPPED) {
+                       while (py < gd->sy + gd->hsize &&
+                           gd->linedata[py].flags & GRID_LINE_WRAPPED) {
+                               window_copy_cursor_down(wp, 0);
+                               py = screen_hsize(back_s)
+                                    + data->cy - data->oy;
+                       }
+                       px = window_copy_find_length(wp, py);
+               }
+       }
+       window_copy_update_cursor(wp, px, data->cy);
+
+       if (window_copy_update_selection(wp))
+               window_copy_redraw_lines(wp, data->cy, 1);
+}
+
+void
+window_copy_cursor_left(struct window_pane *wp)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+
+       if (data->cx == 0) {
+               window_copy_cursor_up(wp, 0);
+               window_copy_cursor_end_of_line(wp);
+       } else {
+               window_copy_update_cursor(wp, data->cx - 1, data->cy);
+               if (window_copy_update_selection(wp))
+                       window_copy_redraw_lines(wp, data->cy, 1);
+       }
+}
+
+void
+window_copy_cursor_right(struct window_pane *wp)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       u_int                            px, py;
+
+       if (data->screen.sel.flag && data->rectflag)
+               px = screen_size_x(&data->screen);
+       else {
+               py = screen_hsize(data->backing) + 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, 0);
+       } else {
+               window_copy_update_cursor(wp, data->cx + 1, data->cy);
+               if (window_copy_update_selection(wp))
+                       window_copy_redraw_lines(wp, data->cy, 1);
+       }
+}
+
+void
+window_copy_cursor_up(struct window_pane *wp, int scroll_only)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *s = &data->screen;
+       u_int                            ox, oy, px, py;
+
+       oy = screen_hsize(data->backing) + data->cy - data->oy;
+       ox = window_copy_find_length(wp, oy);
+       if (ox != 0) {
+               data->lastcx = data->cx;
+               data->lastsx = ox;
+       }
+
+       data->cx = data->lastcx;
+       if (scroll_only || data->cy == 0) {
+               window_copy_scroll_down(wp, 1);
+               if (scroll_only) {
+                       if (data->cy == screen_size_y(s) - 1)
+                               window_copy_redraw_lines(wp, data->cy, 1);
+                       else
+                               window_copy_redraw_lines(wp, data->cy, 2);
+               }
+       } else {
+               window_copy_update_cursor(wp, data->cx, data->cy - 1);
+               if (window_copy_update_selection(wp)) {
+                       if (data->cy == screen_size_y(s) - 1)
+                               window_copy_redraw_lines(wp, data->cy, 1);
+                       else
+                               window_copy_redraw_lines(wp, data->cy, 2);
+               }
+       }
+
+       if (!data->screen.sel.flag || !data->rectflag) {
+               py = screen_hsize(data->backing) + data->cy - data->oy;
+               px = window_copy_find_length(wp, py);
+               if ((data->cx >= data->lastsx && data->cx != px) ||
+                   data->cx > px)
+                       window_copy_cursor_end_of_line(wp);
+       }
+}
+
+void
+window_copy_cursor_down(struct window_pane *wp, int scroll_only)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *s = &data->screen;
+       u_int                            ox, oy, px, py;
+
+       oy = screen_hsize(data->backing) + data->cy - data->oy;
+       ox = window_copy_find_length(wp, oy);
+       if (ox != 0) {
+               data->lastcx = data->cx;
+               data->lastsx = ox;
+       }
+
+       data->cx = data->lastcx;
+       if (scroll_only || data->cy == screen_size_y(s) - 1) {
+               window_copy_scroll_up(wp, 1);
+               if (scroll_only && data->cy > 0)
+                       window_copy_redraw_lines(wp, data->cy - 1, 2);
+       } else {
+               window_copy_update_cursor(wp, data->cx, data->cy + 1);
+               if (window_copy_update_selection(wp))
+                       window_copy_redraw_lines(wp, data->cy - 1, 2);
+       }
+
+       if (!data->screen.sel.flag || !data->rectflag) {
+               py = screen_hsize(data->backing) + data->cy - data->oy;
+               px = window_copy_find_length(wp, py);
+               if ((data->cx >= data->lastsx && data->cx != px) ||
+                   data->cx > px)
+                       window_copy_cursor_end_of_line(wp);
+       }
+}
+
+void
+window_copy_cursor_jump(struct window_pane *wp)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *back_s = data->backing;
+       const struct grid_cell          *gc;
+       u_int                            px, py, xx;
+
+       px = data->cx + 1;
+       py = screen_hsize(back_s) + data->cy - data->oy;
+       xx = window_copy_find_length(wp, py);
+
+       while (px < xx) {
+               gc = grid_peek_cell(back_s->grid, px, py);
+               if ((gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) == 0
+                   && gc->data == data->jumpchar) {
+
+                       window_copy_update_cursor(wp, px, data->cy);
+                       if (window_copy_update_selection(wp))
+                               window_copy_redraw_lines(wp, data->cy, 1);
+                       return;
+               }
+               px++;
+       }
+}
+
+void
+window_copy_cursor_jump_back(struct window_pane *wp)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *back_s = data->backing;
+       const struct grid_cell          *gc;
+       u_int                            px, py;
+
+       px = data->cx;
+       py = screen_hsize(back_s) + data->cy - data->oy;
+
+       if (px > 0)
+               px--;
+
+       for (;;) {
+               gc = grid_peek_cell(back_s->grid, px, py);
+               if ((gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) == 0
+                   && gc->data == data->jumpchar) {
+
+                       window_copy_update_cursor(wp, px, data->cy);
+                       if (window_copy_update_selection(wp))
+                               window_copy_redraw_lines(wp, data->cy, 1);
+                       return;
+               }
+               if (px == 0)
+                       break;
+               px--;
+       }
+}
+
+void
+window_copy_cursor_next_word(struct window_pane *wp, const char *separators)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *back_s = data->backing;
+       u_int                            px, py, xx, yy;
+       int                              expected = 0;
+
+       px = data->cx;
+       py = screen_hsize(back_s) + data->cy - data->oy;
+       xx = window_copy_find_length(wp, py);
+       yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
+
+       /*
+        * First skip past any nonword characters and then any word characters.
+        *
+        * expected is initially set to 0 for the former and then 1 for the
+        * latter.
+        */
+       do {
+               while (px > xx ||
+                   window_copy_in_set(wp, px, py, separators) == expected) {
+                       /* Move down if we're past the end of the line. */
+                       if (px > xx) {
+                               if (py == yy)
+                                       return;
+                               window_copy_cursor_down(wp, 0);
+                               px = 0;
+
+                               py = screen_hsize(back_s) + data->cy - data->oy;
+                               xx = window_copy_find_length(wp, py);
+                       } else
+                               px++;
+               }
+               expected = !expected;
+       } while (expected == 1);
+
+       window_copy_update_cursor(wp, px, data->cy);
+       if (window_copy_update_selection(wp))
+               window_copy_redraw_lines(wp, data->cy, 1);
+}
+
+void
+window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       struct screen                   *back_s = data->backing;
+       u_int                            px, py, xx, yy;
+       int                              expected = 1;
+
+       px = data->cx;
+       py = screen_hsize(back_s) + data->cy - data->oy;
+       xx = window_copy_find_length(wp, py);
+       yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
+
+       /*
+        * First skip past any word characters, then any nonword characters.
+        *
+        * expected is initially set to 1 for the former and then 0 for the
+        * latter.
+        */
+       do {
+               while (px > xx ||
+                   window_copy_in_set(wp, px, py, separators) == expected) {
+                       /* Move down if we're past the end of the line. */
+                       if (px > xx) {
+                               if (py == yy)
+                                       return;
+                               window_copy_cursor_down(wp, 0);
+                               px = 0;
+
+                               py = screen_hsize(back_s) + data->cy - data->oy;
+                               xx = window_copy_find_length(wp, py);
+                       } else
+                               px++;
+               }
+               expected = !expected;
+       } while (expected == 0);
+
+       window_copy_update_cursor(wp, px, data->cy);
+       if (window_copy_update_selection(wp))
+               window_copy_redraw_lines(wp, data->cy, 1);
+}
+
+/* Move to the previous place where a word begins. */
+void
+window_copy_cursor_previous_word(struct window_pane *wp, const char *separators)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       u_int                            px, py;
+
+       px = data->cx;
+       py = screen_hsize(data->backing) + data->cy - data->oy;
+
+       /* Move back to the previous word character. */
+       for (;;) {
+               if (px > 0) {
+                       px--;
+                       if (!window_copy_in_set(wp, px, py, separators))
+                               break;
+               } else {
+                       if (data->cy == 0 &&
+                           (screen_hsize(data->backing) == 0 ||
+                           data->oy >= screen_hsize(data->backing) - 1))
+                               goto out;
+                       window_copy_cursor_up(wp, 0);
+
+                       py = screen_hsize(data->backing) + data->cy - data->oy;
+                       px = window_copy_find_length(wp, py);
+               }
+       }
+
+       /* Move back to the beginning of this word. */
+       while (px > 0 && !window_copy_in_set(wp, px - 1, py, separators))
+               px--;
+
+out:
+       window_copy_update_cursor(wp, px, data->cy);
+       if (window_copy_update_selection(wp))
+               window_copy_redraw_lines(wp, data->cy, 1);
+}
+
+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;
+
+       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);
+       if (screen_size_y(s) > 1)
+               window_copy_write_line(wp, &ctx, 1);
+       if (screen_size_y(s) > 3)
+               window_copy_write_line(wp, &ctx, screen_size_y(s) - 2);
+       if (s->sel.flag && screen_size_y(s) > ny) {
+               window_copy_update_selection(wp);
+               window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
+       }
+       screen_write_cursormove(&ctx, data->cx, data->cy);
+       window_copy_update_selection(wp);
+       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(data->backing))
+               return;
+
+       if (data->oy > screen_hsize(data->backing) - ny)
+               ny = screen_hsize(data->backing) - data->oy;
+       if (ny == 0)
+               return;
+       data->oy += ny;
+
+       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_update_selection(wp);
+               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);
+       window_copy_update_selection(wp);
+       screen_write_stop(&ctx);
+}
+
+void
+window_copy_rectangle_toggle(struct window_pane *wp)
+{
+       struct window_copy_mode_data    *data = wp->modedata;
+       u_int                            px, py;
+
+       data->rectflag = !data->rectflag;
+
+       py = screen_hsize(data->backing) + data->cy - data->oy;
+       px = window_copy_find_length(wp, py);
+       if (data->cx > px)
+               window_copy_update_cursor(wp, px, data->cy);
+
+       window_copy_update_selection(wp);
+       window_copy_redraw_screen(wp);
+}
diff --git a/external/bsd/tmux/dist/window.c b/external/bsd/tmux/dist/window.c
new file mode 100644 (file)
index 0000000..36b4f86
--- /dev/null
@@ -0,0 +1,1092 @@
+/* $Id: window.c,v 1.4 2011/09/17 01:50:09 christos 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 <fnmatch.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "tmux.h"
+
+/*
+ * Each window is attached to a number of 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;
+
+/* Global panes tree. */
+struct window_pane_tree all_window_panes;
+u_int  next_window_pane;
+
+void   window_pane_read_callback(struct bufferevent *, void *);
+void   window_pane_error_callback(struct bufferevent *, short, void *);
+
+RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
+
+int
+winlink_cmp(struct winlink *wl1, struct winlink *wl2)
+{
+       return (wl1->idx - wl2->idx);
+}
+
+RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp);
+
+int
+window_pane_cmp(struct window_pane *wp1, struct window_pane *wp2)
+{
+       return (wp1->id - wp2->id);
+}
+
+struct winlink *
+winlink_find_by_window(struct winlinks *wwl, struct window *w)
+{
+       struct winlink  *wl;
+
+       RB_FOREACH(wl, winlinks, wwl) {
+               if (wl->window == w)
+                       return (wl);
+       }
+
+       return (NULL);
+}
+
+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, int idx)
+{
+       int     i;
+
+       i = idx;
+       do {
+               if (winlink_find_by_index(wwl, i) == NULL)
+                       return (i);
+               if (i == INT_MAX)
+                       i = 0;
+               else
+                       i++;
+       } while (i != idx);
+       return (-1);
+}
+
+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, int idx)
+{
+       struct winlink  *wl;
+
+       if (idx < 0) {
+               if ((idx = winlink_next_index(wwl, -idx - 1)) == -1)
+                       return (NULL);
+       } else if (winlink_find_by_index(wwl, idx) != NULL)
+               return (NULL);
+
+       wl = xcalloc(1, sizeof *wl);
+       wl->idx = idx;
+       RB_INSERT(winlinks, wwl, wl);
+
+       return (wl);
+}
+
+void
+winlink_set_window(struct winlink *wl, struct window *w)
+{
+       wl->window = w;
+       w->references++;
+}
+
+void
+winlink_remove(struct winlinks *wwl, struct winlink *wl)
+{
+       struct window   *w = wl->window;
+
+       RB_REMOVE(winlinks, wwl, wl);
+       if (wl->status_text != NULL)
+               xfree(wl->status_text);
+       xfree(wl);
+
+       if (w != NULL) {
+               if (w->references == 0)
+                       fatal("bad reference count");
+               w->references--;
+               if (w->references == 0)
+                       window_destroy(w);
+       }
+}
+
+struct winlink *
+winlink_next(struct winlink *wl)
+{
+       return (RB_NEXT(winlinks, wwl, wl));
+}
+
+struct winlink *
+winlink_previous(struct winlink *wl)
+{
+       return (RB_PREV(winlinks, wwl, wl));
+}
+
+struct winlink *
+winlink_next_by_number(struct winlink *wl, struct session *s, int n)
+{
+       for (; n > 0; n--) {
+               if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL)
+                       wl = RB_MIN(winlinks, &s->windows);
+       }
+
+       return (wl);
+}
+
+struct winlink *
+winlink_previous_by_number(struct winlink *wl, struct session *s, int n)
+{
+       for (; n > 0; n--) {
+               if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL)
+                       wl = RB_MAX(winlinks, &s->windows);
+       }
+
+       return (wl);
+}
+
+void
+winlink_stack_push(struct winlink_stack *stack, struct winlink *wl)
+{
+       if (wl == NULL)
+               return;
+
+       winlink_stack_remove(stack, wl);
+       TAILQ_INSERT_HEAD(stack, wl, sentry);
+}
+
+void
+winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl)
+{
+       struct winlink  *wl2;
+
+       if (wl == NULL)
+               return;
+
+       TAILQ_FOREACH(wl2, stack, sentry) {
+               if (wl2 == wl) {
+                       TAILQ_REMOVE(stack, wl, 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 = xcalloc(1, sizeof *w);
+       w->name = NULL;
+       w->flags = 0;
+
+       TAILQ_INIT(&w->panes);
+       w->active = NULL;
+
+       w->lastlayout = -1;
+       w->layout_root = NULL;
+
+       w->sx = sx;
+       w->sy = sy;
+
+       queue_window_name(w);
+
+       options_init(&w->options, &global_w_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 *shell,
+    const char *cwd, struct environ *env, struct termios *tio,
+    u_int sx, u_int sy, u_int hlimit,char **cause)
+{
+       struct window           *w;
+       struct window_pane      *wp;
+
+       w = window_create1(sx, sy);
+       wp = window_add_pane(w, hlimit);
+       layout_init(w);
+       if (window_pane_spawn(wp, cmd, shell, cwd, env, tio, cause) != 0) {
+               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);
+
+       if (w->layout_root != NULL)
+               layout_free(w);
+
+       evtimer_del(&w->name_timer);
+
+       options_free(&w->options);
+
+       window_destroy_panes(w);
+
+       if (w->name != NULL)
+               xfree(w->name);
+       xfree(w);
+}
+
+void
+window_resize(struct window *w, u_int sx, u_int sy)
+{
+       w->sx = sx;
+       w->sy = sy;
+}
+
+void
+window_set_active_pane(struct window *w, struct window_pane *wp)
+{
+       if (wp == w->active)
+               return;
+       w->last = w->active;
+       w->active = wp;
+       while (!window_pane_visible(w->active)) {
+               w->active = TAILQ_PREV(w->active, window_panes, entry);
+               if (w->active == NULL)
+                       w->active = TAILQ_LAST(&w->panes, window_panes);
+               if (w->active == wp)
+                       return;
+       }
+}
+
+struct window_pane *
+window_get_active_at(struct window *w, u_int x, u_int y)
+{
+       struct window_pane      *wp;
+
+       TAILQ_FOREACH(wp, &w->panes, entry) {
+               if (!window_pane_visible(wp))
+                       continue;
+               if (x < wp->xoff || x > wp->xoff + wp->sx)
+                       continue;
+               if (y < wp->yoff || y > wp->yoff + wp->sy)
+                       continue;
+               return (wp);
+       }
+       return (NULL);
+}
+
+void
+window_set_active_at(struct window *w, u_int x, u_int y)
+{
+       struct window_pane      *wp;
+
+       wp = window_get_active_at(w, x, y);
+       if (wp != NULL && wp != w->active)
+               window_set_active_pane(w, wp);
+}
+
+struct window_pane *
+window_find_string(struct window *w, const char *s)
+{
+       u_int   x, y;
+
+       x = w->sx / 2;
+       y = w->sy / 2;
+
+       if (strcasecmp(s, "top") == 0)
+               y = 0;
+       else if (strcasecmp(s, "bottom") == 0)
+               y = w->sy - 1;
+       else if (strcasecmp(s, "left") == 0)
+               x = 0;
+       else if (strcasecmp(s, "right") == 0)
+               x = w->sx - 1;
+       else if (strcasecmp(s, "top-left") == 0) {
+               x = 0;
+               y = 0;
+       } else if (strcasecmp(s, "top-right") == 0) {
+               x = w->sx - 1;
+               y = 0;
+       } else if (strcasecmp(s, "bottom-left") == 0) {
+               x = 0;
+               y = w->sy - 1;
+       } else if (strcasecmp(s, "bottom-right") == 0) {
+               x = w->sx - 1;
+               y = w->sy - 1;
+       } else
+               return (NULL);
+
+       return (window_get_active_at(w, x, y));
+}
+
+struct window_pane *
+window_add_pane(struct window *w, u_int hlimit)
+{
+       struct window_pane      *wp;
+
+       wp = window_pane_create(w, w->sx, w->sy, hlimit);
+       if (TAILQ_EMPTY(&w->panes))
+               TAILQ_INSERT_HEAD(&w->panes, wp, entry);
+       else
+               TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry);
+       return (wp);
+}
+
+void
+window_remove_pane(struct window *w, struct window_pane *wp)
+{
+       if (wp == w->active) {
+               w->active = w->last;
+               w->last = NULL;
+               if (w->active == NULL) {
+                       w->active = TAILQ_PREV(wp, window_panes, entry);
+                       if (w->active == NULL)
+                               w->active = TAILQ_NEXT(wp, entry);
+               }
+       } else if (wp == w->last)
+               w->last = NULL;
+
+       TAILQ_REMOVE(&w->panes, wp, entry);
+       window_pane_destroy(wp);
+}
+
+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);
+}
+
+struct window_pane *
+window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n)
+{
+       for (; n > 0; n--) {
+               if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
+                       wp = TAILQ_FIRST(&w->panes);
+       }
+
+       return (wp);
+}
+
+struct window_pane *
+window_pane_previous_by_number(struct window *w, struct window_pane *wp,
+    u_int n)
+{
+       for (; n > 0; n--) {
+               if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL)
+                       wp = TAILQ_LAST(&w->panes, window_panes);
+       }
+
+       return (wp);
+}
+
+u_int
+window_pane_index(struct window *w, struct window_pane *wp)
+{
+       struct window_pane      *wq;
+       u_int                    n;
+
+       n = 0;
+       TAILQ_FOREACH(wq, &w->panes, entry) {
+               if (wp == wq)
+                       break;
+               n++;
+       }
+       return (n);
+}
+
+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);
+       }
+}
+
+/* Return list of printable window flag symbols. No flags is just a space. */
+char *
+window_printable_flags(struct session *s, struct winlink *wl)
+{
+       char    flags[BUFSIZ];
+       int     pos;
+
+       pos = 0;
+       if (wl->flags & WINLINK_ACTIVITY)
+               flags[pos++] = '#';
+       if (wl->flags & WINLINK_BELL)
+               flags[pos++] = '!';
+       if (wl->flags & WINLINK_CONTENT)
+               flags[pos++] = '+';
+       if (wl->flags & WINLINK_SILENCE)
+               flags[pos++] = '~';
+       if (wl == s->curw)
+               flags[pos++] = '*';
+       if (wl == TAILQ_FIRST(&s->lastw))
+               flags[pos++] = '-';
+       if (pos == 0)
+               flags[pos++] = ' ';
+       flags[pos] = '\0';
+       return (xstrdup(flags));
+}
+
+/* Find pane in global tree by id. */
+struct window_pane *
+window_pane_find_by_id(u_int id)
+{
+       struct window_pane      wp;
+
+       wp.id = id;
+       return (RB_FIND(window_pane_tree, &all_window_panes, &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->id = next_window_pane++;
+       RB_INSERT(window_pane_tree, &all_window_panes, wp);
+
+       wp->cmd = NULL;
+       wp->shell = NULL;
+       wp->cwd = NULL;
+
+       wp->fd = -1;
+       wp->event = NULL;
+
+       wp->mode = NULL;
+
+       wp->layout_cell = NULL;
+
+       wp->xoff = 0;
+       wp->yoff = 0;
+
+       wp->sx = sx;
+       wp->sy = sy;
+
+       wp->pipe_fd = -1;
+       wp->pipe_off = 0;
+       wp->pipe_event = NULL;
+
+       wp->saved_grid = NULL;
+
+       screen_init(&wp->base, sx, sy, hlimit);
+       wp->screen = &wp->base;
+
+       input_init(wp);
+
+       return (wp);
+}
+
+void
+window_pane_destroy(struct window_pane *wp)
+{
+       window_pane_reset_mode(wp);
+
+       if (wp->fd != -1) {
+               close(wp->fd);
+               bufferevent_free(wp->event);
+       }
+
+       input_free(wp);
+
+       screen_free(&wp->base);
+       if (wp->saved_grid != NULL)
+               grid_destroy(wp->saved_grid);
+
+       if (wp->pipe_fd != -1) {
+               close(wp->pipe_fd);
+               bufferevent_free(wp->pipe_event);
+       }
+
+       RB_REMOVE(window_pane_tree, &all_window_panes, wp);
+
+       if (wp->cwd != NULL)
+               xfree(wp->cwd);
+       if (wp->shell != NULL)
+               xfree(wp->shell);
+       if (wp->cmd != NULL)
+               xfree(wp->cmd);
+       utmp_destroy(wp->utmp);
+       xfree(wp);
+}
+
+int
+window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell,
+    const char *cwd, struct environ *env, struct termios *tio, char **cause)
+{
+       struct winsize   ws;
+       char            *argv0, paneid[16];
+       const char      *ptr;
+       struct termios   tio2;
+
+       if (wp->fd != -1) {
+               close(wp->fd);
+               bufferevent_free(wp->event);
+       }
+       if (cmd != NULL) {
+               if (wp->cmd != NULL)
+                       xfree(wp->cmd);
+               wp->cmd = xstrdup(cmd);
+       }
+       if (shell != NULL) {
+               if (wp->shell != NULL)
+                       xfree(wp->shell);
+               wp->shell = xstrdup(shell);
+       }
+       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);
+
+       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("/");
+
+               if (tcgetattr(STDIN_FILENO, &tio2) != 0)
+                       fatal("tcgetattr failed");
+               if (tio != NULL)
+                       memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc);
+               tio2.c_cc[VERASE] = '\177';
+               if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0)
+                       fatal("tcgetattr failed");
+
+               closefrom(STDERR_FILENO + 1);
+
+               xsnprintf(paneid, sizeof paneid, "%%%u", wp->id);
+               environ_set(env, "TMUX_PANE", paneid);
+               environ_push(env);
+
+               clear_signals(1);
+               log_close();
+
+               if (*wp->cmd != '\0') {
+                       /* Set SHELL but only if it is currently not useful. */
+                       shell = getenv("SHELL");
+                       if (shell == NULL || *shell == '\0' || areshell(shell))
+                               setenv("SHELL", wp->shell, 1);
+
+                       execl(_PATH_BSHELL, "sh", "-c", wp->cmd, (char *) NULL);
+                       fatal("execl failed");
+               }
+
+               /* No command; fork a login shell. */
+               ptr = strrchr(wp->shell, '/');
+               if (ptr != NULL && *(ptr + 1) != '\0')
+                       xasprintf(&argv0, "-%s", ptr + 1);
+               else
+                       xasprintf(&argv0, "-%s", wp->shell);
+               setenv("SHELL", wp->shell, 1);
+               execl(wp->shell, argv0, (char *) NULL);
+               fatal("execl failed");
+       }
+
+       setblocking(wp->fd, 0);
+
+       wp->event = bufferevent_new(wp->fd,
+           window_pane_read_callback, NULL, window_pane_error_callback, wp);
+       bufferevent_enable(wp->event, EV_READ|EV_WRITE);
+
+       wp->utmp = utmp_create(wp->tty);
+
+       return (0);
+}
+
+/* ARGSUSED */
+void
+window_pane_read_callback(unused struct bufferevent *bufev, void *data)
+{
+       struct window_pane     *wp = data;
+       char                   *new_data;
+       size_t                  new_size;
+
+       new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off;
+       if (wp->pipe_fd != -1 && new_size > 0) {
+               new_data = (char *)EVBUFFER_DATA(wp->event->input);
+               bufferevent_write(wp->pipe_event, new_data, new_size);
+       }
+
+       input_parse(wp);
+
+       wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
+
+       /*
+        * If we get here, we're not outputting anymore, so set the silence
+        * flag on the window.
+        */
+       wp->window->flags |= WINDOW_SILENCE;
+       if (gettimeofday(&wp->window->silence_timer, NULL) != 0)
+               fatal("gettimeofday failed.");
+}
+
+/* ARGSUSED */
+void
+window_pane_error_callback(
+    unused struct bufferevent *bufev, unused short what, void *data)
+{
+       struct window_pane *wp = data;
+
+       server_destroy_pane(wp);
+}
+
+void
+window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
+{
+       struct winsize  ws;
+
+       if (sx == wp->sx && sy == wp->sy)
+               return;
+       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)
+#ifdef __sun
+               /*
+                * Some versions of Solaris apparently can return an error when
+                * resizing; don't know why this happens, can't reproduce on
+                * other platforms and ignoring it doesn't seem to cause any
+                * issues.
+                */
+               if (errno != EINVAL)
+#endif
+               fatal("ioctl failed");
+}
+
+/*
+ * Enter alternative screen mode. A copy of the visible screen is saved and the
+ * history is not updated
+ */
+void
+window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc)
+{
+       struct screen   *s = &wp->base;
+       u_int            sx, sy;
+
+       if (wp->saved_grid != NULL)
+               return;
+       if (!options_get_number(&wp->window->options, "alternate-screen"))
+               return;
+       sx = screen_size_x(s);
+       sy = screen_size_y(s);
+
+       wp->saved_grid = grid_create(sx, sy, 0);
+       grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy);
+       wp->saved_cx = s->cx;
+       wp->saved_cy = s->cy;
+       memcpy(&wp->saved_cell, gc, sizeof wp->saved_cell);
+
+       grid_view_clear(s->grid, 0, 0, sx, sy);
+
+       wp->base.grid->flags &= ~GRID_HISTORY;
+
+       wp->flags |= PANE_REDRAW;
+}
+
+/* Exit alternate screen mode and restore the copied grid. */
+void
+window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc)
+{
+       struct screen   *s = &wp->base;
+       u_int            sx, sy;
+
+       if (wp->saved_grid == NULL)
+               return;
+       if (!options_get_number(&wp->window->options, "alternate-screen"))
+               return;
+       sx = screen_size_x(s);
+       sy = screen_size_y(s);
+
+       /*
+        * If the current size is bigger, temporarily resize to the old size
+        * before copying back.
+        */
+       if (sy > wp->saved_grid->sy)
+               screen_resize(s, sx, wp->saved_grid->sy);
+
+       /* Restore the grid, cursor position and cell. */
+       grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy);
+       s->cx = wp->saved_cx;
+       if (s->cx > screen_size_x(s) - 1)
+               s->cx = screen_size_x(s) - 1;
+       s->cy = wp->saved_cy;
+       if (s->cy > screen_size_y(s) - 1)
+               s->cy = screen_size_y(s) - 1;
+       memcpy(gc, &wp->saved_cell, sizeof *gc);
+
+       /*
+        * Turn history back on (so resize can use it) and then resize back to
+        * the current size.
+        */
+       wp->base.grid->flags |= GRID_HISTORY;
+       if (sy > wp->saved_grid->sy)
+               screen_resize(s, sx, sy);
+
+       grid_destroy(wp->saved_grid);
+       wp->saved_grid = NULL;
+
+       wp->flags |= PANE_REDRAW;
+}
+
+int
+window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode)
+{
+       struct screen   *s;
+
+       if (wp->mode != NULL)
+               return (1);
+       wp->mode = mode;
+
+       if ((s = wp->mode->init(wp)) != NULL)
+               wp->screen = s;
+       wp->flags |= PANE_REDRAW;
+       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;
+       wp->flags |= PANE_REDRAW;
+}
+
+void
+window_pane_key(struct window_pane *wp, struct session *sess, int key)
+{
+       struct window_pane      *wp2;
+
+       if (!window_pane_visible(wp))
+               return;
+
+       if (wp->mode != NULL) {
+               if (wp->mode->key != NULL)
+                       wp->mode->key(wp, sess, key);
+               return;
+       }
+
+       if (wp->fd == -1)
+               return;
+       input_key(wp, key);
+       if (options_get_number(&wp->window->options, "synchronize-panes")) {
+               TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
+                       if (wp2 == wp || wp2->mode != NULL)
+                               continue;
+                       if (wp2->fd != -1 && window_pane_visible(wp2))
+                               input_key(wp2, key);
+               }
+       }
+}
+
+void
+window_pane_mouse(
+    struct window_pane *wp, struct session *sess, struct mouse_event *m)
+{
+       if (!window_pane_visible(wp))
+               return;
+
+       if (m->x < wp->xoff || m->x >= wp->xoff + wp->sx)
+               return;
+       if (m->y < wp->yoff || m->y >= wp->yoff + wp->sy)
+               return;
+       m->x -= wp->xoff;
+       m->y -= wp->yoff;
+
+       if (wp->mode != NULL) {
+               if (wp->mode->mouse != NULL &&
+                   options_get_number(&wp->window->options, "mode-mouse"))
+                       wp->mode->mouse(wp, sess, m);
+       } else if (wp->fd != -1)
+               input_mouse(wp, m);
+}
+
+int
+window_pane_visible(struct window_pane *wp)
+{
+       struct window   *w = wp->window;
+
+       if (wp->xoff >= w->sx || wp->yoff >= w->sy)
+               return (0);
+       if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy)
+               return (0);
+       return (1);
+}
+
+char *
+window_pane_search(struct window_pane *wp, const char *searchstr, u_int *lineno)
+{
+       struct screen   *s = &wp->base;
+       char            *newsearchstr, *line, *msg;
+       u_int            i;
+
+       msg = NULL;
+       xasprintf(&newsearchstr, "*%s*", searchstr);
+
+       for (i = 0; i < screen_size_y(s); i++) {
+               line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s));
+               if (fnmatch(newsearchstr, line, 0) == 0) {
+                       msg = line;
+                       if (lineno != NULL)
+                               *lineno = i;
+                       break;
+               }
+               xfree(line);
+       }
+
+       xfree(newsearchstr);
+       return (msg);
+}
+
+/* Find the pane directly above another. */
+struct window_pane *
+window_pane_find_up(struct window_pane *wp)
+{
+       struct window_pane     *wp2;
+       u_int                   left, top;
+
+       if (wp == NULL || !window_pane_visible(wp))
+               return (NULL);
+
+       top = wp->yoff;
+       if (top == 0)
+               top = wp->window->sy + 1;
+       left = wp->xoff;
+
+       TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
+               if (!window_pane_visible(wp2))
+                       continue;
+               if (wp2->yoff + wp2->sy + 1 != top)
+                       continue;
+               if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx)
+                       return (wp2);
+       }
+       return (NULL);
+}
+
+/* Find the pane directly below another. */
+struct window_pane *
+window_pane_find_down(struct window_pane *wp)
+{
+       struct window_pane     *wp2;
+       u_int                   left, bottom;
+
+       if (wp == NULL || !window_pane_visible(wp))
+               return (NULL);
+
+       bottom = wp->yoff + wp->sy + 1;
+       if (bottom >= wp->window->sy)
+               bottom = 0;
+       left = wp->xoff;
+
+       TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
+               if (!window_pane_visible(wp2))
+                       continue;
+               if (wp2->yoff != bottom)
+                       continue;
+               if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx)
+                       return (wp2);
+       }
+       return (NULL);
+}
+
+/*
+ * Find the pane directly to the left of another, adjacent to the left side and
+ * containing the top edge.
+ */
+struct window_pane *
+window_pane_find_left(struct window_pane *wp)
+{
+       struct window_pane     *wp2;
+       u_int                   left, top;
+
+       if (wp == NULL || !window_pane_visible(wp))
+               return (NULL);
+
+       left = wp->xoff;
+       if (left == 0)
+               left = wp->window->sx + 1;
+       top = wp->yoff;
+
+       TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
+               if (!window_pane_visible(wp2))
+                       continue;
+               if (wp2->xoff + wp2->sx + 1 != left)
+                       continue;
+               if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy)
+                       return (wp2);
+       }
+       return (NULL);
+}
+
+/*
+ * Find the pane directly to the right of another, that is adjacent to the
+ * right edge and including the top edge.
+ */
+struct window_pane *
+window_pane_find_right(struct window_pane *wp)
+{
+       struct window_pane     *wp2;
+       u_int                   right, top;
+
+       if (wp == NULL || !window_pane_visible(wp))
+               return (NULL);
+
+       right = wp->xoff + wp->sx + 1;
+       if (right >= wp->window->sx)
+               right = 0;
+       top = wp->yoff;
+
+       TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
+               if (!window_pane_visible(wp2))
+                       continue;
+               if (wp2->xoff != right)
+                       continue;
+               if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy)
+                       return (wp2);
+       }
+       return (NULL);
+}
diff --git a/external/bsd/tmux/dist/xmalloc.c b/external/bsd/tmux/dist/xmalloc.c
new file mode 100644 (file)
index 0000000..fae5ebd
--- /dev/null
@@ -0,0 +1,144 @@
+/* $Id: xmalloc.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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 <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+char *
+xstrdup(const char *s)
+{
+       char    *ptr;
+       size_t   len;
+
+       len = strlen(s) + 1;
+       ptr = xmalloc(len);
+
+       strlcpy(ptr, s, len);
+       return (ptr);
+}
+
+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);
+}
diff --git a/external/bsd/tmux/dist/xterm-keys.c b/external/bsd/tmux/dist/xterm-keys.c
new file mode 100644 (file)
index 0000000..f42a776
--- /dev/null
@@ -0,0 +1,203 @@
+/* $Id: xterm-keys.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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"
+
+/*
+ * xterm-style function keys append one of the following values before the last
+ * character:
+ *
+ * 2 Shift
+ * 3 Alt
+ * 4 Shift + Alt
+ * 5 Ctrl
+ * 6 Shift + Ctrl
+ * 7 Alt + Ctrl
+ * 8 Shift + Alt + Ctrl
+ *
+ * Rather than parsing them, just match against a table.
+ *
+ * There are three forms for F1-F4 (\\033O_P and \\033O1;_P and \\033[1;_P).
+ * We accept any but always output the latter (it comes first in the table).
+ */
+
+int    xterm_keys_match(const char *, const char *, size_t);
+int    xterm_keys_modifiers(const char *, const char *, size_t);
+
+struct xterm_keys_entry {
+       int              key;
+       const char      *template;
+};
+
+const struct xterm_keys_entry xterm_keys_table[] = {
+       { KEYC_F1,      "\033[1;_P" },
+       { KEYC_F1,      "\033O1;_P" },
+       { KEYC_F1,      "\033O_P" },
+       { KEYC_F2,      "\033[1;_Q" },
+       { KEYC_F2,      "\033O1;_Q" },
+       { KEYC_F2,      "\033O_Q" },
+       { KEYC_F3,      "\033[1;_R" },
+       { KEYC_F3,      "\033O1;_R" },
+       { KEYC_F3,      "\033O_R" },
+       { KEYC_F4,      "\033[1;_S" },
+       { KEYC_F4,      "\033O1;_S" },
+       { KEYC_F4,      "\033O_S" },
+       { KEYC_F5,      "\033[15;_~" },
+       { KEYC_F6,      "\033[17;_~" },
+       { KEYC_F7,      "\033[18;_~" },
+       { KEYC_F8,      "\033[19;_~" },
+       { KEYC_F9,      "\033[20;_~" },
+       { KEYC_F10,     "\033[21;_~" },
+       { KEYC_F11,     "\033[23;_~" },
+       { KEYC_F12,     "\033[24;_~" },
+       { KEYC_F13,     "\033[25;_~" },
+       { KEYC_F14,     "\033[26;_~" },
+       { KEYC_F15,     "\033[28;_~" },
+       { KEYC_F16,     "\033[29;_~" },
+       { KEYC_F17,     "\033[31;_~" },
+       { KEYC_F18,     "\033[32;_~" },
+       { KEYC_F19,     "\033[33;_~" },
+       { KEYC_F20,     "\033[34;_~" },
+       { KEYC_UP,      "\033[1;_A" },
+       { KEYC_DOWN,    "\033[1;_B" },
+       { KEYC_RIGHT,   "\033[1;_C" },
+       { KEYC_LEFT,    "\033[1;_D" },
+       { KEYC_HOME,    "\033[1;_H" },
+       { KEYC_END,     "\033[1;_F" },
+       { KEYC_PPAGE,   "\033[5;_~" },
+       { KEYC_NPAGE,   "\033[6;_~" },
+       { KEYC_IC,      "\033[2;_~" },
+       { KEYC_DC,      "\033[3;_~" },
+};
+
+/*
+ * Match key against buffer, treating _ as a wildcard. Return -1 for no match,
+ * 0 for match, 1 if the end of the buffer is reached (need more data).
+ */
+int
+xterm_keys_match(const char *template, const char *buf, size_t len)
+{
+       size_t  pos;
+
+       if (len == 0)
+               return (0);
+
+       pos = 0;
+       do {
+               if (*template != '_' && buf[pos] != *template)
+                       return (-1);
+       } while (pos++ != len && *++template != '\0');
+
+       if (*template != '\0')  /* partial */
+               return (1);
+
+       return (0);
+}
+
+/* Find modifiers based on template. */
+int
+xterm_keys_modifiers(const char *template, const char *buf, size_t len)
+{
+       size_t  idx;
+       int     param, modifiers;
+
+       idx = strcspn(template, "_");
+       if (idx >= len)
+               return (0);
+       param = buf[idx] - '1';
+
+       modifiers = 0;
+       if (param & 1)
+               modifiers |= KEYC_SHIFT;
+       if (param & 2)
+               modifiers |= KEYC_ESCAPE;
+       if (param & 4)
+               modifiers |= KEYC_CTRL;
+       if (param & 8)
+               modifiers |= KEYC_ESCAPE;
+       return (modifiers);
+}
+
+/*
+ * Lookup key from a buffer against the table. Returns 0 for found (and the
+ * key), -1 for not found, 1 for partial match.
+ */
+int
+xterm_keys_find(const char *buf, size_t len, size_t *size, int *key)
+{
+       const struct xterm_keys_entry   *entry;
+       u_int                            i;
+
+       for (i = 0; i < nitems(xterm_keys_table); i++) {
+               entry = &xterm_keys_table[i];
+               switch (xterm_keys_match(entry->template, buf, len)) {
+               case 0:
+                       *size = strlen(entry->template);
+                       *key = entry->key;
+                       *key |= xterm_keys_modifiers(entry->template, buf, len);
+                       return (0);
+               case 1:
+                       return (1);
+               }
+       }
+       return (-1);
+}
+
+/* Lookup a key number from the table. */
+char *
+xterm_keys_lookup(int key)
+{
+       const struct xterm_keys_entry   *entry;
+       u_int                            i;
+       int                              modifiers;
+       char                            *out;
+
+       modifiers = 1;
+       if (key & KEYC_SHIFT)
+               modifiers += 1;
+       if (key & KEYC_ESCAPE)
+               modifiers += 2;
+       if (key & KEYC_CTRL)
+               modifiers += 4;
+
+       /*
+        * If the key has no modifiers, return NULL and let it fall through to
+        * the normal lookup.
+        */
+       if (modifiers == 1)
+               return (NULL);
+
+       /* Otherwise, find the key in the table. */
+       key &= ~(KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL);
+       for (i = 0; i < nitems(xterm_keys_table); i++) {
+               entry = &xterm_keys_table[i];
+               if (key == entry->key)
+                       break;
+       }
+       if (i == nitems(xterm_keys_table))
+               return (NULL);
+
+       /* Copy the template and replace the modifier. */
+       out = xstrdup(entry->template);
+       out[strcspn(out, "_")] = '0' + modifiers;
+       return (out);
+}
diff --git a/external/bsd/tmux/prepare-import.sh b/external/bsd/tmux/prepare-import.sh
new file mode 100755 (executable)
index 0000000..17cf0ff
--- /dev/null
@@ -0,0 +1,105 @@
+#! /bin/sh
+# $NetBSD: prepare-import.sh,v 1.2 2011/08/17 18:37:59 jmmv Exp $
+#
+# Use this script to recreate the 'dist' subdirectory from a newly released
+# distfile.  The script takes care of unpacking the distfile, removing any
+# files that are not relevant to NetBSD and checking if there are any new
+# files in the new release that need to be addressed.
+#
+# See the README file for general instructions.
+#
+
+set -e
+
+ProgName=${0##*/}
+
+CLEAN_PATTERNS=
+CLEAN_PATTERNS="${CLEAN_PATTERNS} [A-Z]*"
+CLEAN_PATTERNS="${CLEAN_PATTERNS} aclocal.m4"
+CLEAN_PATTERNS="${CLEAN_PATTERNS} etc"
+CLEAN_PATTERNS="${CLEAN_PATTERNS} configure*"
+
+err() {
+       echo "${ProgName}:" "${@}" 1>&2
+       exit 1
+}
+
+log() {
+       echo "${ProgName}:" "${@}"
+}
+
+backup_dist() {
+       if [ -d dist.old ]; then
+               log "Removing dist; dist.old exists"
+               rm -rf dist
+       else
+               log "Backing up dist as dist.old"
+               mv dist dist.old
+       fi
+}
+
+extract_distfile() {
+       local distfile="${1}"; shift
+       local distname="${1}"; shift
+
+       log "Extracting ${distfile}"
+       tar -xzf "${distfile}"
+       [ -d "${distname}" ] || err "Distfile did not create ${distname}"
+       log "Renaming ${distname} to dist"
+       mv "${distname}" dist
+}
+
+get_distname() {
+       local distfile="${1}"; shift
+       basename "${distfile}" | sed -e 's,\.tar.*,,'
+}
+
+cleanup_dist() {
+       log "Removing unnecessary files from dist"
+       ( cd dist && rm -rf ${CLEAN_PATTERNS} )
+}
+
+diff_dirs() {
+       local old_dir="${1}"; shift
+       local new_dir="${1}"; shift
+
+       local old_list=$(mktemp -t tmux-import.XXXXXX)
+       local new_list=$(mktemp -t tmux-import.XXXXXX)
+       local diff=$(mktemp -t tmux-import.XXXXXX)
+       trap "rm -f '${old_list}' '${new_list}' '${diff}'; exit 1" \
+           HUP INT QUIT TERM
+
+       ( cd "${old_dir}" && find . | sort >>"${old_list}" )
+       ( cd "${new_dir}" && find . | sort >>"${new_list}" )
+
+       diff -u "${old_list}" "${new_list}" | grep '^+\.' >>"${diff}" || true
+       if [ -s "${diff}" ]; then
+               log "New files found"
+               diff -u "${old_list}" "${new_list}" | grep '^+\.'
+               log "Check if any files have to be cleaned up and update" \
+                   "the prepare-import.sh script accordingly"
+       else
+               log "No new files; all good!"
+       fi
+
+       rm -f "${old_list}" "${new_list}" "${diff}"
+}
+
+main() {
+       [ ${#} -eq 1 ] || err "Must provide a distfile name"
+       local distfile="${1}"; shift
+
+       [ -f Makefile -a -f prepare-import.sh ] || \
+           err "Must be run from the src/external/bsd/tmux subdirectory"
+
+       local distname="$(get_distname ${distfile})"
+
+       backup_dist
+       extract_distfile "${distfile}" "${distname}"
+       cleanup_dist
+       diff_dirs dist.old dist
+       log "Don't forget to update the -D flags in usr.bin/tmux/Makefile" \
+           "and to update the version in doc/3RDPARTY"
+}
+
+main "${@}"
diff --git a/external/bsd/tmux/share/Makefile b/external/bsd/tmux/share/Makefile
new file mode 100644 (file)
index 0000000..bff5f28
--- /dev/null
@@ -0,0 +1,5 @@
+# $NetBSD: Makefile,v 1.1 2011/03/10 09:18:00 jmmv Exp $
+
+SUBDIR= examples
+
+.include <bsd.subdir.mk>
diff --git a/external/bsd/tmux/share/examples/Makefile b/external/bsd/tmux/share/examples/Makefile
new file mode 100644 (file)
index 0000000..4ad9266
--- /dev/null
@@ -0,0 +1,5 @@
+# $NetBSD: Makefile,v 1.1 2011/03/10 09:18:00 jmmv Exp $
+
+SUBDIR= tmux
+
+.include <bsd.subdir.mk>
diff --git a/external/bsd/tmux/share/examples/tmux/Makefile b/external/bsd/tmux/share/examples/tmux/Makefile
new file mode 100644 (file)
index 0000000..2176cb9
--- /dev/null
@@ -0,0 +1,14 @@
+# $NetBSD: Makefile,v 1.1 2011/03/10 09:18:00 jmmv Exp $
+
+.include <bsd.own.mk>
+
+.if ${MKSHARE} != "no"
+SRCDIR=                ${NETBSDSRCDIR}/external/bsd/tmux/dist
+.PATH:         ${SRCDIR}/examples
+
+FILESDIR=      /usr/share/examples/tmux
+FILESMODE=     444
+FILES=         screen-keys.conf
+.endif
+
+.include <bsd.prog.mk>
diff --git a/external/bsd/tmux/usr.bin/Makefile b/external/bsd/tmux/usr.bin/Makefile
new file mode 100644 (file)
index 0000000..4ad9266
--- /dev/null
@@ -0,0 +1,5 @@
+# $NetBSD: Makefile,v 1.1 2011/03/10 09:18:00 jmmv Exp $
+
+SUBDIR= tmux
+
+.include <bsd.subdir.mk>
diff --git a/external/bsd/tmux/usr.bin/tmux/Makefile b/external/bsd/tmux/usr.bin/tmux/Makefile
new file mode 100644 (file)
index 0000000..d73f0d9
--- /dev/null
@@ -0,0 +1,221 @@
+# $NetBSD: Makefile,v 1.10 2013/11/26 01:27:20 christos Exp $
+
+.include <bsd.own.mk>
+
+SRCDIR=                ${NETBSDSRCDIR}/external/bsd/tmux/dist
+.PATH:         ${SRCDIR}
+.PATH:         ${SRCDIR}/compat
+
+BINDIR=                /usr/bin
+PROG=          tmux
+MAN=           tmux.1
+
+WARNS?=        4
+COPTS.cmd-server-info.c+=      -Wno-stack-protector
+COPTS.imsg-buffer.c+=          -Wno-stack-protector
+COPTS.imsg.c+=                 -Wno-stack-protector
+COPTS.screen.c+=               -Wno-stack-protector
+
+SRCS=          arguments.c
+SRCS+=         attributes.c
+SRCS+=         cfg.c
+SRCS+=         client.c
+SRCS+=         clock.c
+SRCS+=         cmd-attach-session.c
+SRCS+=         cmd-bind-key.c
+SRCS+=         cmd-break-pane.c
+SRCS+=         cmd-capture-pane.c
+SRCS+=         cmd-choose-buffer.c
+SRCS+=         cmd-choose-client.c
+SRCS+=         cmd-choose-session.c
+SRCS+=         cmd-choose-window.c
+SRCS+=         cmd-clear-history.c
+SRCS+=         cmd-clock-mode.c
+SRCS+=         cmd-command-prompt.c
+SRCS+=         cmd-confirm-before.c
+SRCS+=         cmd-copy-mode.c
+SRCS+=         cmd-delete-buffer.c
+SRCS+=         cmd-detach-client.c
+SRCS+=         cmd-display-message.c
+SRCS+=         cmd-display-panes.c
+SRCS+=         cmd-find-window.c
+SRCS+=         cmd-has-session.c
+SRCS+=         cmd-if-shell.c
+SRCS+=         cmd-join-pane.c
+SRCS+=         cmd-kill-pane.c
+SRCS+=         cmd-kill-server.c
+SRCS+=         cmd-kill-session.c
+SRCS+=         cmd-kill-window.c
+SRCS+=         cmd-link-window.c
+SRCS+=         cmd-list-buffers.c
+SRCS+=         cmd-list-clients.c
+SRCS+=         cmd-list-commands.c
+SRCS+=         cmd-list-keys.c
+SRCS+=         cmd-list-panes.c
+SRCS+=         cmd-list-sessions.c
+SRCS+=         cmd-list-windows.c
+SRCS+=         cmd-list.c
+SRCS+=         cmd-load-buffer.c
+SRCS+=         cmd-lock-server.c
+SRCS+=         cmd-move-window.c
+SRCS+=         cmd-new-session.c
+SRCS+=         cmd-new-window.c
+SRCS+=         cmd-paste-buffer.c
+SRCS+=         cmd-pipe-pane.c
+SRCS+=         cmd-refresh-client.c
+SRCS+=         cmd-rename-session.c
+SRCS+=         cmd-rename-window.c
+SRCS+=         cmd-resize-pane.c
+SRCS+=         cmd-respawn-pane.c
+SRCS+=         cmd-respawn-window.c
+SRCS+=         cmd-rotate-window.c
+SRCS+=         cmd-run-shell.c
+SRCS+=         cmd-save-buffer.c
+SRCS+=         cmd-select-layout.c
+SRCS+=         cmd-select-pane.c
+SRCS+=         cmd-select-window.c
+SRCS+=         cmd-send-keys.c
+SRCS+=         cmd-send-prefix.c
+SRCS+=         cmd-server-info.c
+SRCS+=         cmd-set-buffer.c
+SRCS+=         cmd-set-environment.c
+SRCS+=         cmd-set-option.c
+SRCS+=         cmd-show-buffer.c
+SRCS+=         cmd-show-environment.c
+SRCS+=         cmd-show-messages.c
+SRCS+=         cmd-show-options.c
+SRCS+=         cmd-source-file.c
+SRCS+=         cmd-split-window.c
+SRCS+=         cmd-start-server.c
+SRCS+=         cmd-string.c
+SRCS+=         cmd-suspend-client.c
+SRCS+=         cmd-swap-pane.c
+SRCS+=         cmd-swap-window.c
+SRCS+=         cmd-switch-client.c
+SRCS+=         cmd-unbind-key.c
+SRCS+=         cmd-unlink-window.c
+SRCS+=         cmd.c
+SRCS+=         colour.c
+SRCS+=         environ.c
+SRCS+=         grid-utf8.c
+SRCS+=         grid-view.c
+SRCS+=         grid.c
+SRCS+=         input-keys.c
+SRCS+=         input.c
+SRCS+=         job.c
+SRCS+=         key-bindings.c
+SRCS+=         key-string.c
+SRCS+=         layout-custom.c
+SRCS+=         layout-set.c
+SRCS+=         layout.c
+SRCS+=         log.c
+SRCS+=         mode-key.c
+SRCS+=         names.c
+SRCS+=         options.c
+SRCS+=         options-table.c
+SRCS+=         osdep-netbsd.c
+SRCS+=         paste.c
+SRCS+=         resize.c
+SRCS+=         screen-redraw.c
+SRCS+=         screen-write.c
+SRCS+=         screen.c
+SRCS+=         server-client.c
+SRCS+=         server-fn.c
+SRCS+=         server-window.c
+SRCS+=         server.c
+SRCS+=         session.c
+SRCS+=         signal.c
+SRCS+=         status.c
+SRCS+=         tmux.c
+SRCS+=         tty-acs.c
+SRCS+=         tty-keys.c
+SRCS+=         tty-term.c
+SRCS+=         tty.c
+SRCS+=         utf8.c
+SRCS+=         window-choose.c
+SRCS+=         window-clock.c
+SRCS+=         window-copy.c
+SRCS+=         window.c
+SRCS+=         xmalloc.c
+SRCS+=         xterm-keys.c
+SRCS+=         utmp.c
+
+# Files in compat/
+#SRCS+=                forkpty-hpux.c
+SRCS+=         imsg-buffer.c
+SRCS+=         imsg.c
+SRCS+=         strtonum.c
+#SRCS+=                unvis.c
+#SRCS+=                vis.c
+
+CPPFLAGS+=     -I${SRCDIR} -I${.CURDIR}
+
+# The following flags have been extracted from the compiler command-line
+# generated by Automake and Autoconf when building tmux under NetBSD.
+# Would be nicer to stick this in a config.h file, but the upstream code
+# does not use one at this moment.
+CPPFLAGS+=     -DPACKAGE_TARNAME=\"tmux\"
+CPPFLAGS+=     -DPACKAGE_VERSION=\"1.5\"
+CPPFLAGS+=     -DPACKAGE_STRING=\"tmux\ 1.5\"
+CPPFLAGS+=     -DPACKAGE_BUGREPORT=\"\"
+CPPFLAGS+=     -DPACKAGE_URL=\"\"
+CPPFLAGS+=     -DPACKAGE=\"tmux\"
+CPPFLAGS+=     -DVERSION=\"1.5\"
+CPPFLAGS+=     -DSTDC_HEADERS=1
+CPPFLAGS+=     -DHAVE_SYS_TYPES_H=1
+CPPFLAGS+=     -DHAVE_SYS_STAT_H=1
+CPPFLAGS+=     -DHAVE_STDLIB_H=1
+CPPFLAGS+=     -DHAVE_STRING_H=1
+CPPFLAGS+=     -DHAVE_MEMORY_H=1
+CPPFLAGS+=     -DHAVE_STRINGS_H=1
+CPPFLAGS+=     -DHAVE_INTTYPES_H=1
+CPPFLAGS+=     -DHAVE_STDINT_H=1
+CPPFLAGS+=     -DHAVE_UNISTD_H=1
+CPPFLAGS+=     -DHAVE_BITSTRING_H=1
+CPPFLAGS+=     -DHAVE_CURSES_H=1
+CPPFLAGS+=     -DHAVE_DIRENT_H=1
+CPPFLAGS+=     -DHAVE_FCNTL_H=1
+CPPFLAGS+=     -DHAVE_INTTYPES_H=1
+CPPFLAGS+=     -DHAVE_PATHS_H=1
+CPPFLAGS+=     -DHAVE_STDINT_H=1
+CPPFLAGS+=     -DHAVE_SYS_DIR_H=1
+CPPFLAGS+=     -DHAVE_QUEUE_H=1
+CPPFLAGS+=     -DHAVE_TREE_H=1
+CPPFLAGS+=     -DHAVE_TERM_H=1
+CPPFLAGS+=     -DHAVE_UTIL_H=1
+CPPFLAGS+=     -DHAVE_LIBRT=1
+CPPFLAGS+=     -DHAVE_FORKPTY=1
+CPPFLAGS+=     -DHAVE_CLOSEFROM=1
+CPPFLAGS+=     -DHAVE_DAEMON=1
+CPPFLAGS+=     -DHAVE_SETENV=1
+CPPFLAGS+=     -DHAVE_STRLCPY=1
+CPPFLAGS+=     -DHAVE_STRLCAT=1
+CPPFLAGS+=     -DHAVE_ASPRINTF=1
+CPPFLAGS+=     -DHAVE_FGETLN=1
+CPPFLAGS+=     -DHAVE_STRCASESTR=1
+CPPFLAGS+=     -DHAVE_STRSEP=1
+CPPFLAGS+=     -DHAVE_VIS=1
+CPPFLAGS+=     -DHAVE_DECL_OPTARG=1
+CPPFLAGS+=     -DHAVE_DECL_OPTIND=1
+CPPFLAGS+=     -DHAVE_DECL_OPTRESET=1
+CPPFLAGS+=     -DHAVE_GETOPT=1
+CPPFLAGS+=     -DHAVE_BZERO=1
+CPPFLAGS+=     -DHAVE_SETPROCTITLE=1
+CPPFLAGS+=     -DHAVE_SYSCONF=1
+CPPFLAGS+=     -DHAVE_BSD_TYPES=1
+CPPFLAGS+=     -DHAVE___PROGNAME=1
+CPPFLAGS+=     -DHAVE_FCNTL_CLOSEM=1
+CPPFLAGS+=     -DHAVE_PROC_PID=1
+CPPFLAGS+=     -DHAVE_DIRFD=1
+CPPFLAGS+=     -DSUPPORT_UTMP
+CPPFLAGS+=     -DSUPPORT_UTMPX
+
+LDADD+=                -levent -lterminfo -lutil -lm
+DPADD+=                ${LIBEVENT} ${LIBTERMINFO} ${LIBUTIL}
+
+COPTS.cmd-display-message.c += -Wno-format-nonliteral
+COPTS.cmd-pipe-pane.c += -Wno-format-nonliteral
+COPTS.server-client.c += -Wno-format-nonliteral
+COPTS.status.c += -Wno-format-nonliteral
+
+.include <bsd.prog.mk>