./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
./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
./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
SUBDIR= byacc file flex less \
libarchive libevent mdocml \
-
+ tmux
.if (${MKATF} != "no")
SUBDIR+= atf
--- /dev/null
+# $NetBSD: Makefile,v 1.1 2011/03/10 09:18:00 jmmv Exp $
+
+SUBDIR= share usr.bin
+
+.include <bsd.subdir.mk>
--- /dev/null
+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.
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+ }
+}
--- /dev/null
+/* $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;
+ }
+}
--- /dev/null
+/* $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 */
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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++;
+ }
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+ }
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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 */
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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));
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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]);
+}
--- /dev/null
+/* $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 */
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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 *);
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+ }
+}
--- /dev/null
+# 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
+
+
+
--- /dev/null
+# $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
--- /dev/null
+# $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
--- /dev/null
+# $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
--- /dev/null
+# $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'
+
--- /dev/null
+" 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"
--- /dev/null
+#!/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
--- /dev/null
+# $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
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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));
+}
--- /dev/null
+/* $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++;
+ }
+}
--- /dev/null
+/* $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);
+ }
+ }
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+ }
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+ }
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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());
+}
--- /dev/null
+/* $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());
+}
--- /dev/null
+/* $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());
+}
--- /dev/null
+/* $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());
+}
--- /dev/null
+/* $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());
+}
--- /dev/null
+/* $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());
+}
--- /dev/null
+/* $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());
+}
--- /dev/null
+/* $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());
+}
--- /dev/null
+/* $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());
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+ }
+}
--- /dev/null
+/* $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;
+ }
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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 */
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+ }
+}
--- /dev/null
+/* $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();
+ }
+ }
+}
--- /dev/null
+/* $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);
+ }
+}
--- /dev/null
+/* $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);
+ }
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+.\" $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
--- /dev/null
+/* $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));
+}
--- /dev/null
+/* $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 */
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+#! /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 "${@}"
--- /dev/null
+# $NetBSD: Makefile,v 1.1 2011/03/10 09:18:00 jmmv Exp $
+
+SUBDIR= examples
+
+.include <bsd.subdir.mk>
--- /dev/null
+# $NetBSD: Makefile,v 1.1 2011/03/10 09:18:00 jmmv Exp $
+
+SUBDIR= tmux
+
+.include <bsd.subdir.mk>
--- /dev/null
+# $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>
--- /dev/null
+# $NetBSD: Makefile,v 1.1 2011/03/10 09:18:00 jmmv Exp $
+
+SUBDIR= tmux
+
+.include <bsd.subdir.mk>
--- /dev/null
+# $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>